From bfecad40becce58741ac74e76a5936937ebef8ce Mon Sep 17 00:00:00 2001 From: deepfriedwaffles Date: Tue, 24 Mar 2026 19:07:55 -0400 Subject: [PATCH] restore include paths for cross-platform builds because i messed absolutely every #include up. dont worry that everything is modified, all i did was normalize line endings because CLRF snuck into the repo and this prevents fake diffs --- CMakeLists.txt | 806 +- data/lang/en_US.lang | 1984 +-- project/android/.classpath | 16 +- project/android/.project | 66 +- project/android/ManifestAddons.xml | 36 +- project/android/assets/lang/en_US.lang | 1938 +-- project/android/jni/Android.mk | 562 +- project/android/proguard.cfg | 136 +- project/android/res/drawable/bgtiled.xml | 6 +- .../android/res/drawable/btn_nw_cancel.xml | 10 +- .../android/res/drawable/btn_nw_create.xml | 10 +- project/android/res/drawable/btngamemode.xml | 10 +- .../android/res/layout/mainmenu_options.xml | 38 +- .../android/res/layout/rename_mp_world.xml | 58 +- project/android/res/values/strings.xml | 78 +- project/android/res/xml/preferences.xml | 208 +- .../src/com/mojang/android/EditTextAscii.java | 116 +- .../src/com/mojang/android/StringValue.java | 10 +- .../android/licensing/LicenseCodes.java | 58 +- .../android/preferences/SliderPreference.java | 466 +- .../mojang/minecraftpe/GameModeButton.java | 154 +- .../minecraftpe/MainMenuOptionsActivity.java | 392 +- .../mojang/minecraftpe/Minecraft_Market.java | 28 +- .../minecraftpe/Minecraft_Market_Demo.java | 34 +- .../mojang/minecraftpe/Minecraft_Verizon.java | 178 +- project/android_java/.classpath | 18 +- project/android_java/.project | 80 +- project/android_java/ManifestAddons.xml | 24 +- project/android_java/assets/lang/en_US.lang | 1896 +-- project/android_java/jni/Android.mk | 500 +- project/android_java/proguard.cfg | 132 +- project/android_java/res/drawable/bgtiled.xml | 6 +- .../res/drawable/btn_nw_cancel.xml | 10 +- .../res/drawable/btn_nw_create.xml | 10 +- .../android_java/res/drawable/btngamemode.xml | 10 +- .../res/layout/create_new_world.xml | 352 +- .../res/layout/mainmenu_options.xml | 34 +- .../res/layout/rename_mp_world.xml | 56 +- project/android_java/res/values/strings.xml | 78 +- project/android_java/res/xml/preferences.xml | 208 +- .../mojang/minecraftpe/sound/SoundPlayer.java | 442 +- project/lib_projects/raknet/jni/Android.mk | 28 +- .../lib_projects/raknet/jni/Application.mk | 6 +- .../RaknetSources/AutopatcherPatchContext.h | 46 +- .../AutopatcherRepositoryInterface.h | 144 +- .../raknet/jni/RaknetSources/BitStream.cpp | 2344 +-- .../raknet/jni/RaknetSources/BitStream.h | 4064 +++--- .../RaknetSources/CCRakNetSlidingWindow.cpp | 664 +- .../jni/RaknetSources/CCRakNetSlidingWindow.h | 418 +- .../raknet/jni/RaknetSources/CCRakNetUDT.cpp | 1596 +-- .../raknet/jni/RaknetSources/CCRakNetUDT.h | 788 +- .../raknet/jni/RaknetSources/CheckSum.cpp | 194 +- .../raknet/jni/RaknetSources/CheckSum.h | 106 +- .../raknet/jni/RaknetSources/CloudClient.cpp | 484 +- .../raknet/jni/RaknetSources/CloudClient.h | 326 +- .../raknet/jni/RaknetSources/CloudCommon.cpp | 318 +- .../raknet/jni/RaknetSources/CloudCommon.h | 282 +- .../raknet/jni/RaknetSources/CloudServer.cpp | 3346 ++--- .../raknet/jni/RaknetSources/CloudServer.h | 750 +- .../RaknetSources/CommandParserInterface.cpp | 322 +- .../RaknetSources/CommandParserInterface.h | 280 +- .../jni/RaknetSources/ConnectionGraph2.cpp | 598 +- .../jni/RaknetSources/ConnectionGraph2.h | 236 +- .../jni/RaknetSources/ConsoleServer.cpp | 622 +- .../raknet/jni/RaknetSources/ConsoleServer.h | 154 +- .../raknet/jni/RaknetSources/DS_BPlusTree.h | 2292 +-- .../jni/RaknetSources/DS_BinarySearchTree.h | 2268 +-- .../raknet/jni/RaknetSources/DS_BytePool.cpp | 298 +- .../raknet/jni/RaknetSources/DS_BytePool.h | 92 +- .../raknet/jni/RaknetSources/DS_ByteQueue.cpp | 254 +- .../raknet/jni/RaknetSources/DS_ByteQueue.h | 80 +- .../raknet/jni/RaknetSources/DS_Hash.h | 688 +- .../raknet/jni/RaknetSources/DS_Heap.h | 594 +- .../RaknetSources/DS_HuffmanEncodingTree.cpp | 598 +- .../RaknetSources/DS_HuffmanEncodingTree.h | 134 +- .../DS_HuffmanEncodingTreeFactory.h | 114 +- .../DS_HuffmanEncodingTreeNode.h | 42 +- .../raknet/jni/RaknetSources/DS_LinkedList.h | 2504 ++-- .../raknet/jni/RaknetSources/DS_List.h | 1036 +- .../raknet/jni/RaknetSources/DS_Map.h | 642 +- .../raknet/jni/RaknetSources/DS_MemoryPool.h | 588 +- .../raknet/jni/RaknetSources/DS_Multilist.h | 3288 ++--- .../jni/RaknetSources/DS_OrderedChannelHeap.h | 488 +- .../raknet/jni/RaknetSources/DS_OrderedList.h | 526 +- .../raknet/jni/RaknetSources/DS_Queue.h | 870 +- .../jni/RaknetSources/DS_QueueLinkedList.h | 206 +- .../raknet/jni/RaknetSources/DS_RangeList.h | 472 +- .../raknet/jni/RaknetSources/DS_Table.cpp | 2236 +-- .../raknet/jni/RaknetSources/DS_Table.h | 686 +- .../DS_ThreadsafeAllocatingQueue.h | 286 +- .../raknet/jni/RaknetSources/DS_Tree.h | 196 +- .../jni/RaknetSources/DS_WeightedGraph.h | 1074 +- .../jni/RaknetSources/DataCompressor.cpp | 126 +- .../raknet/jni/RaknetSources/DataCompressor.h | 66 +- .../RaknetSources/DirectoryDeltaTransfer.cpp | 484 +- .../RaknetSources/DirectoryDeltaTransfer.h | 328 +- .../raknet/jni/RaknetSources/DynDNS.cpp | 472 +- .../raknet/jni/RaknetSources/DynDNS.h | 200 +- .../raknet/jni/RaknetSources/EmailSender.cpp | 724 +- .../raknet/jni/RaknetSources/EmailSender.h | 116 +- .../jni/RaknetSources/EncodeClassName.cpp | 2 +- .../jni/RaknetSources/EpochTimeToString.cpp | 70 +- .../jni/RaknetSources/EpochTimeToString.h | 30 +- .../raknet/jni/RaknetSources/Export.h | 26 +- .../raknet/jni/RaknetSources/FileList.cpp | 1588 +- .../raknet/jni/RaknetSources/FileList.h | 516 +- .../jni/RaknetSources/FileListNodeContext.h | 78 +- .../jni/RaknetSources/FileListTransfer.cpp | 2172 +-- .../jni/RaknetSources/FileListTransfer.h | 346 +- .../FileListTransferCBInterface.h | 308 +- .../jni/RaknetSources/FileOperations.cpp | 346 +- .../raknet/jni/RaknetSources/FileOperations.h | 48 +- .../raknet/jni/RaknetSources/FormatString.cpp | 60 +- .../raknet/jni/RaknetSources/FormatString.h | 44 +- .../jni/RaknetSources/FullyConnectedMesh2.cpp | 1104 +- .../jni/RaknetSources/FullyConnectedMesh2.h | 506 +- .../raknet/jni/RaknetSources/GetTime.cpp | 440 +- .../raknet/jni/RaknetSources/GetTime.h | 70 +- .../raknet/jni/RaknetSources/Getche.cpp | 54 +- .../raknet/jni/RaknetSources/Getche.h | 22 +- .../raknet/jni/RaknetSources/Gets.cpp | 50 +- .../raknet/jni/RaknetSources/Gets.h | 28 +- .../jni/RaknetSources/GridSectorizer.cpp | 382 +- .../raknet/jni/RaknetSources/GridSectorizer.h | 136 +- .../jni/RaknetSources/HTTPConnection.cpp | 626 +- .../raknet/jni/RaknetSources/HTTPConnection.h | 352 +- .../IncrementalReadInterface.cpp | 30 +- .../RaknetSources/IncrementalReadInterface.h | 56 +- .../raknet/jni/RaknetSources/InternalPacket.h | 262 +- .../raknet/jni/RaknetSources/Itoa.cpp | 104 +- .../raknet/jni/RaknetSources/Itoa.h | 30 +- .../raknet/jni/RaknetSources/Kbhit.h | 168 +- .../raknet/jni/RaknetSources/LinuxStrings.cpp | 52 +- .../raknet/jni/RaknetSources/LinuxStrings.h | 52 +- .../jni/RaknetSources/LocklessTypes.cpp | 84 +- .../raknet/jni/RaknetSources/LocklessTypes.h | 80 +- .../jni/RaknetSources/LogCommandParser.cpp | 558 +- .../jni/RaknetSources/LogCommandParser.h | 240 +- .../raknet/jni/RaknetSources/MTUSize.h | 62 +- .../jni/RaknetSources/MessageFilter.cpp | 816 +- .../raknet/jni/RaknetSources/MessageFilter.h | 382 +- .../jni/RaknetSources/MessageIdentifiers.h | 806 +- .../RaknetSources/NatPunchthroughClient.cpp | 1940 +-- .../jni/RaknetSources/NatPunchthroughClient.h | 530 +- .../RaknetSources/NatPunchthroughServer.cpp | 1126 +- .../jni/RaknetSources/NatPunchthroughServer.h | 290 +- .../RaknetSources/NatTypeDetectionClient.cpp | 340 +- .../RaknetSources/NatTypeDetectionClient.h | 162 +- .../RaknetSources/NatTypeDetectionCommon.cpp | 232 +- .../RaknetSources/NatTypeDetectionCommon.h | 112 +- .../RaknetSources/NatTypeDetectionServer.cpp | 526 +- .../RaknetSources/NatTypeDetectionServer.h | 240 +- .../jni/RaknetSources/NativeFeatureIncludes.h | 350 +- .../NativeFeatureIncludesOverrides.h | 18 +- .../raknet/jni/RaknetSources/NativeTypes.h | 46 +- .../jni/RaknetSources/NetworkIDManager.cpp | 216 +- .../jni/RaknetSources/NetworkIDManager.h | 140 +- .../jni/RaknetSources/NetworkIDObject.cpp | 124 +- .../jni/RaknetSources/NetworkIDObject.h | 162 +- .../raknet/jni/RaknetSources/PS3Includes.h | 142 +- .../jni/RaknetSources/PacketConsoleLogger.cpp | 52 +- .../jni/RaknetSources/PacketConsoleLogger.h | 76 +- .../jni/RaknetSources/PacketFileLogger.cpp | 90 +- .../jni/RaknetSources/PacketFileLogger.h | 74 +- .../raknet/jni/RaknetSources/PacketLogger.cpp | 912 +- .../raknet/jni/RaknetSources/PacketLogger.h | 202 +- .../PacketOutputWindowLogger.cpp | 78 +- .../RaknetSources/PacketOutputWindowLogger.h | 68 +- .../raknet/jni/RaknetSources/PacketPriority.h | 158 +- .../jni/RaknetSources/PacketizedTCP.cpp | 816 +- .../raknet/jni/RaknetSources/PacketizedTCP.h | 186 +- .../jni/RaknetSources/PluginInterface2.cpp | 178 +- .../jni/RaknetSources/PluginInterface2.h | 400 +- .../raknet/jni/RaknetSources/RPC4Plugin.cpp | 1188 +- .../raknet/jni/RaknetSources/RPC4Plugin.h | 472 +- .../raknet/jni/RaknetSources/Rackspace.cpp | 1310 +- .../raknet/jni/RaknetSources/Rackspace.h | 810 +- .../raknet/jni/RaknetSources/RakAlloca.h | 32 +- .../raknet/jni/RaknetSources/RakAssert.h | 4 +- .../jni/RaknetSources/RakMemoryOverride.cpp | 588 +- .../jni/RaknetSources/RakMemoryOverride.h | 472 +- .../raknet/jni/RaknetSources/RakNet.vcproj | 1690 +-- .../jni/RaknetSources/RakNetCommandParser.cpp | 604 +- .../jni/RaknetSources/RakNetCommandParser.h | 126 +- .../raknet/jni/RaknetSources/RakNetDefines.h | 338 +- .../RaknetSources/RakNetDefinesOverrides.h | 4 +- .../raknet/jni/RaknetSources/RakNetSmartPtr.h | 346 +- .../raknet/jni/RaknetSources/RakNetSocket.cpp | 50 +- .../raknet/jni/RaknetSources/RakNetSocket.h | 76 +- .../jni/RaknetSources/RakNetStatistics.cpp | 296 +- .../jni/RaknetSources/RakNetStatistics.h | 252 +- .../raknet/jni/RaknetSources/RakNetTime.h | 46 +- .../jni/RaknetSources/RakNetTransport2.cpp | 256 +- .../jni/RaknetSources/RakNetTransport2.h | 204 +- .../raknet/jni/RaknetSources/RakNetTypes.cpp | 1450 +- .../raknet/jni/RaknetSources/RakNetTypes.h | 964 +- .../raknet/jni/RaknetSources/RakNetVersion.h | 16 +- .../jni/RaknetSources/RakNet_vc8.vcproj | 1698 +-- .../jni/RaknetSources/RakNet_vc9.vcproj | 1698 +-- .../raknet/jni/RaknetSources/RakPeer.cpp | 11928 ++++++++-------- .../raknet/jni/RaknetSources/RakPeer.h | 1968 +-- .../jni/RaknetSources/RakPeerInterface.h | 1142 +- .../raknet/jni/RaknetSources/RakSleep.cpp | 106 +- .../raknet/jni/RaknetSources/RakSleep.h | 16 +- .../raknet/jni/RaknetSources/RakString.cpp | 2844 ++-- .../raknet/jni/RaknetSources/RakString.h | 614 +- .../raknet/jni/RaknetSources/RakThread.cpp | 326 +- .../raknet/jni/RaknetSources/RakThread.h | 176 +- .../raknet/jni/RaknetSources/RakWString.cpp | 690 +- .../raknet/jni/RaknetSources/RakWString.h | 226 +- .../raknet/jni/RaknetSources/Rand.cpp | 548 +- .../raknet/jni/RaknetSources/Rand.h | 120 +- .../raknet/jni/RaknetSources/ReadyEvent.cpp | 1128 +- .../raknet/jni/RaknetSources/ReadyEvent.h | 468 +- .../raknet/jni/RaknetSources/RefCountedObj.h | 50 +- .../jni/RaknetSources/ReliabilityLayer.cpp | 7654 +++++----- .../jni/RaknetSources/ReliabilityLayer.h | 1194 +- .../raknet/jni/RaknetSources/ReplicaEnums.h | 86 +- .../jni/RaknetSources/ReplicaManager3.cpp | 4500 +++--- .../jni/RaknetSources/ReplicaManager3.h | 2080 +-- .../raknet/jni/RaknetSources/Router2.cpp | 2658 ++-- .../raknet/jni/RaknetSources/Router2.h | 390 +- .../raknet/jni/RaknetSources/SHA1.cpp | 652 +- .../raknet/jni/RaknetSources/SHA1.h | 174 +- .../jni/RaknetSources/SecureHandshake.cpp | 108 +- .../jni/RaknetSources/SecureHandshake.h | 50 +- .../raknet/jni/RaknetSources/SendToThread.cpp | 154 +- .../raknet/jni/RaknetSources/SendToThread.h | 94 +- .../jni/RaknetSources/SignaledEvent.cpp | 490 +- .../raknet/jni/RaknetSources/SignaledEvent.h | 118 +- .../raknet/jni/RaknetSources/SimpleMutex.cpp | 362 +- .../raknet/jni/RaknetSources/SimpleMutex.h | 140 +- .../RaknetSources/SingleProducerConsumer.h | 518 +- .../raknet/jni/RaknetSources/SocketDefines.h | 138 +- .../raknet/jni/RaknetSources/SocketIncludes.h | 62 +- .../raknet/jni/RaknetSources/SocketLayer.cpp | 3702 ++--- .../raknet/jni/RaknetSources/SocketLayer.h | 328 +- .../jni/RaknetSources/StringCompressor.cpp | 998 +- .../jni/RaknetSources/StringCompressor.h | 210 +- .../raknet/jni/RaknetSources/StringTable.cpp | 280 +- .../raknet/jni/RaknetSources/StringTable.h | 192 +- .../jni/RaknetSources/SuperFastHash.cpp | 238 +- .../raknet/jni/RaknetSources/SuperFastHash.h | 34 +- .../raknet/jni/RaknetSources/TCPInterface.cpp | 2436 ++-- .../raknet/jni/RaknetSources/TCPInterface.h | 480 +- .../jni/RaknetSources/TableSerializer.cpp | 640 +- .../jni/RaknetSources/TableSerializer.h | 416 +- .../raknet/jni/RaknetSources/TeamBalancer.cpp | 1748 +-- .../raknet/jni/RaknetSources/TeamBalancer.h | 388 +- .../jni/RaknetSources/TelnetTransport.cpp | 728 +- .../jni/RaknetSources/TelnetTransport.h | 144 +- .../raknet/jni/RaknetSources/ThreadPool.h | 1240 +- .../RaknetSources/ThreadsafePacketLogger.cpp | 76 +- .../RaknetSources/ThreadsafePacketLogger.h | 80 +- .../jni/RaknetSources/TransportInterface.h | 166 +- .../RaknetSources/TwoWayAuthentication.cpp | 862 +- .../jni/RaknetSources/TwoWayAuthentication.h | 292 +- .../raknet/jni/RaknetSources/UDPForwarder.cpp | 1632 +-- .../raknet/jni/RaknetSources/UDPForwarder.h | 354 +- .../jni/RaknetSources/UDPProxyClient.cpp | 602 +- .../raknet/jni/RaknetSources/UDPProxyClient.h | 356 +- .../raknet/jni/RaknetSources/UDPProxyCommon.h | 114 +- .../jni/RaknetSources/UDPProxyCoordinator.cpp | 1086 +- .../jni/RaknetSources/UDPProxyCoordinator.h | 230 +- .../jni/RaknetSources/UDPProxyServer.cpp | 334 +- .../raknet/jni/RaknetSources/UDPProxyServer.h | 236 +- .../RaknetSources/VariableDeltaSerializer.cpp | 550 +- .../RaknetSources/VariableDeltaSerializer.h | 514 +- .../VariableListDeltaTracker.cpp | 80 +- .../RaknetSources/VariableListDeltaTracker.h | 262 +- .../jni/RaknetSources/VariadicSQLParser.cpp | 300 +- .../jni/RaknetSources/VariadicSQLParser.h | 48 +- .../raknet/jni/RaknetSources/VitaIncludes.cpp | 326 +- .../raknet/jni/RaknetSources/VitaIncludes.h | 114 +- .../jni/RaknetSources/WSAStartupSingleton.cpp | 150 +- .../jni/RaknetSources/WSAStartupSingleton.h | 32 +- .../jni/RaknetSources/WindowsIncludes.h | 34 +- .../jni/RaknetSources/XBox360Includes.h | 110 +- .../raknet/jni/RaknetSources/_FindFirst.cpp | 318 +- .../raknet/jni/RaknetSources/_FindFirst.h | 112 +- .../raknet/jni/RaknetSources/gettimeofday.cpp | 110 +- .../raknet/jni/RaknetSources/gettimeofday.h | 112 +- .../jni/RaknetSources/rdlmalloc-options.h | 54 +- .../raknet/jni/RaknetSources/rdlmalloc.cpp | 8322 +++++------ .../raknet/jni/RaknetSources/rdlmalloc.h | 4520 +++--- project/raspberry/py/api.py | 198 +- project/raspberry/py/apiutil.py | 26 +- project/raspberry/py/event.py | 38 +- project/raspberry/py/game_snake.py | 398 +- project/raspberry/py/image.py | 76 +- project/raspberry/py/kbhelp.py | 120 +- project/raspberry/py/vec3.py | 160 +- project/win32/MinecraftWin32.sln | 64 +- project/win32/MinecraftWin32.vcxproj | 2250 +-- project/win32/MinecraftWin32.vcxproj.filters | 6380 ++++----- project/win32/MinecraftWin32.vcxproj.user | 10 +- project/win32_gl/MinecraftWin32_GL.sln | 40 +- project/win32_gl/MinecraftWin32_GL.vcxproj | 1048 +- .../MinecraftWin32_GL.vcxproj.filters | 2656 ++-- .../win32_gl/MinecraftWin32_GL.vcxproj.user | 4 +- src/App.h | 174 +- src/AppConstants.h | 20 +- src/AppPlatform.h | 286 +- src/AppPlatform_android.h | 1308 +- src/AppPlatform_android23.h | 98 +- src/AppPlatform_glfw.cpp | 22 +- src/AppPlatform_glfw.h | 310 +- src/AppPlatform_iOS.h | 130 +- src/AppPlatform_iOS.mm | 562 +- src/AppPlatform_win32.cpp | 32 +- src/AppPlatform_win32.h | 284 +- src/ErrorCodes.h | 26 +- src/LicenseCodes.h | 80 +- src/NinecraftApp.cpp | 856 +- src/NinecraftApp.h | 104 +- src/Performance.cpp | 8 +- src/Performance.h | 24 +- src/SharedConstants.cpp | 32 +- src/SharedConstants.h | 68 +- src/client/IConfigListener.cpp | 32 +- src/client/IConfigListener.h | 96 +- src/client/KeyMapping.h | 42 +- src/client/Minecraft.cpp | 3173 ++-- src/client/Minecraft.h | 460 +- src/client/MouseHandler.cpp | 120 +- src/client/MouseHandler.h | 56 +- src/client/Options.cpp | 604 +- src/client/Options.h | 341 +- src/client/OptionsFile.cpp | 196 +- src/client/OptionsFile.h | 42 +- src/client/Timer.h | 248 +- src/client/gamemode/GameMode.h | 126 +- src/client/gamemode/SurvivalMode.cpp | 196 +- src/client/gamemode/SurvivalMode.h | 62 +- src/client/gui/Font.cpp | 744 +- src/client/gui/Font.h | 126 +- src/client/gui/Gui.cpp | 4 +- src/client/gui/Gui.h | 268 +- src/client/gui/GuiComponent.cpp | 312 +- src/client/gui/GuiComponent.h | 68 +- src/client/gui/Screen.cpp | 582 +- src/client/gui/Screen.h | 166 +- src/client/gui/TweenData.h | 22 +- src/client/gui/components/Button.cpp | 438 +- src/client/gui/components/Button.h | 160 +- src/client/gui/components/GButton.cpp | 2 +- src/client/gui/components/GButton.h | 110 +- src/client/gui/components/GuiElement.cpp | 40 +- src/client/gui/components/GuiElement.h | 68 +- .../gui/components/GuiElementContainer.cpp | 130 +- .../gui/components/GuiElementContainer.h | 56 +- src/client/gui/components/ImageButton.cpp | 270 +- src/client/gui/components/ImageButton.h | 194 +- src/client/gui/components/InventoryPane.cpp | 412 +- src/client/gui/components/InventoryPane.h | 124 +- src/client/gui/components/ItemPane.cpp | 296 +- src/client/gui/components/ItemPane.h | 190 +- src/client/gui/components/KeyOption.h | 4 +- .../gui/components/LargeImageButton.cpp | 208 +- src/client/gui/components/LargeImageButton.h | 42 +- src/client/gui/components/NinePatch.cpp | 282 +- src/client/gui/components/NinePatch.h | 156 +- src/client/gui/components/OptionsGroup.cpp | 228 +- src/client/gui/components/OptionsGroup.h | 64 +- src/client/gui/components/OptionsItem.cpp | 82 +- src/client/gui/components/OptionsItem.h | 54 +- src/client/gui/components/OptionsPane.cpp | 126 +- src/client/gui/components/OptionsPane.h | 60 +- .../gui/components/RolledSelectionListH.cpp | 598 +- .../gui/components/RolledSelectionListH.h | 164 +- .../gui/components/RolledSelectionListV.cpp | 704 +- .../gui/components/RolledSelectionListV.h | 188 +- .../gui/components/ScrolledSelectionList.cpp | 592 +- .../gui/components/ScrolledSelectionList.h | 138 +- src/client/gui/components/ScrollingPane.cpp | 1610 +-- src/client/gui/components/ScrollingPane.h | 406 +- src/client/gui/components/Slider.cpp | 186 +- src/client/gui/components/Slider.h | 94 +- src/client/gui/components/SmallButton.cpp | 48 +- src/client/gui/components/SmallButton.h | 46 +- src/client/gui/components/TextBox.cpp | 204 +- src/client/gui/components/TextBox.h | 88 +- src/client/gui/components/TextOption.h | 4 +- src/client/gui/screens/ArmorScreen.cpp | 740 +- src/client/gui/screens/ArmorScreen.h | 158 +- src/client/gui/screens/BaseContainerScreen.h | 100 +- src/client/gui/screens/BuyGameScreen.h | 52 +- src/client/gui/screens/ChatScreen.cpp | 48 +- src/client/gui/screens/ChatScreen.h | 42 +- src/client/gui/screens/ChestScreen.cpp | 948 +- src/client/gui/screens/ChestScreen.h | 144 +- src/client/gui/screens/ChooseLevelScreen.cpp | 52 +- src/client/gui/screens/ChooseLevelScreen.h | 42 +- src/client/gui/screens/ConfirmScreen.cpp | 168 +- src/client/gui/screens/ConfirmScreen.h | 80 +- src/client/gui/screens/DeathScreen.cpp | 166 +- src/client/gui/screens/DeathScreen.h | 60 +- src/client/gui/screens/DialogDefinitions.h | 26 +- src/client/gui/screens/DisconnectionScreen.h | 114 +- src/client/gui/screens/FurnaceScreen.cpp | 1110 +- src/client/gui/screens/FurnaceScreen.h | 168 +- src/client/gui/screens/InBedScreen.cpp | 98 +- src/client/gui/screens/InBedScreen.h | 52 +- .../screens/IngameBlockSelectionScreen.cpp | 682 +- .../gui/screens/IngameBlockSelectionScreen.h | 118 +- src/client/gui/screens/InvalidLicenseScreen.h | 214 +- src/client/gui/screens/JoinGameScreen.cpp | 334 +- src/client/gui/screens/JoinGameScreen.h | 140 +- src/client/gui/screens/OptionsScreen.cpp | 540 +- src/client/gui/screens/OptionsScreen.h | 96 +- src/client/gui/screens/PauseScreen.cpp | 402 +- src/client/gui/screens/PauseScreen.h | 90 +- src/client/gui/screens/PrerenderTilesScreen.h | 324 +- src/client/gui/screens/ProgressScreen.cpp | 198 +- src/client/gui/screens/ProgressScreen.h | 42 +- .../gui/screens/RenameMPLevelScreen.cpp | 110 +- src/client/gui/screens/RenameMPLevelScreen.h | 34 +- src/client/gui/screens/ScreenChooser.cpp | 126 +- src/client/gui/screens/ScreenChooser.h | 64 +- src/client/gui/screens/SelectWorldScreen.cpp | 936 +- src/client/gui/screens/SelectWorldScreen.h | 224 +- .../gui/screens/SimpleChooseLevelScreen.cpp | 520 +- .../gui/screens/SimpleChooseLevelScreen.h | 88 +- src/client/gui/screens/StartMenuScreen.cpp | 432 +- src/client/gui/screens/StartMenuScreen.h | 80 +- src/client/gui/screens/TextEditScreen.cpp | 292 +- src/client/gui/screens/TextEditScreen.h | 70 +- src/client/gui/screens/UploadPhotoScreen.cpp | 354 +- src/client/gui/screens/UploadPhotoScreen.h | 72 +- .../gui/screens/crafting/CraftingFilters.cpp | 62 +- .../gui/screens/crafting/CraftingFilters.h | 20 +- .../screens/crafting/PaneCraftingScreen.cpp | 1112 +- .../gui/screens/crafting/PaneCraftingScreen.h | 212 +- .../screens/crafting/StonecutterScreen.cpp | 34 +- .../gui/screens/crafting/StonecutterScreen.h | 34 +- .../gui/screens/crafting/WorkbenchScreen.cpp | 30 +- .../gui/screens/crafting/WorkbenchScreen.h | 34 +- .../touch/TouchIngameBlockSelectionScreen.h | 146 +- .../gui/screens/touch/TouchJoinGameScreen.cpp | 486 +- .../gui/screens/touch/TouchJoinGameScreen.h | 148 +- .../screens/touch/TouchSelectWorldScreen.cpp | 1210 +- .../screens/touch/TouchSelectWorldScreen.h | 244 +- .../screens/touch/TouchStartMenuScreen.cpp | 460 +- .../gui/screens/touch/TouchStartMenuScreen.h | 86 +- src/client/model/ChestModel.h | 104 +- src/client/model/ChickenModel.cpp | 202 +- src/client/model/ChickenModel.h | 46 +- src/client/model/CowModel.cpp | 60 +- src/client/model/CowModel.h | 30 +- src/client/model/CreeperModel.h | 164 +- src/client/model/HumanoidModel.cpp | 584 +- src/client/model/HumanoidModel.h | 56 +- src/client/model/Model.h | 96 +- src/client/model/ModelInclude.h | 34 +- src/client/model/PigModel.cpp | 16 +- src/client/model/PigModel.h | 30 +- src/client/model/QuadrupedModel.cpp | 244 +- src/client/model/QuadrupedModel.h | 56 +- src/client/model/SheepFurModel.cpp | 98 +- src/client/model/SheepFurModel.h | 48 +- src/client/model/SheepModel.cpp | 64 +- src/client/model/SheepModel.h | 46 +- src/client/model/SignModel.h | 64 +- src/client/model/SkeletonModel.h | 92 +- src/client/model/SpiderModel.h | 336 +- src/client/model/ZombieModel.h | 74 +- src/client/model/geom/Cube.cpp | 148 +- src/client/model/geom/Cube.h | 58 +- src/client/model/geom/ModelPart.cpp | 606 +- src/client/model/geom/ModelPart.h | 148 +- src/client/model/geom/Polygon.cpp | 110 +- src/client/model/geom/Polygon.h | 58 +- src/client/model/geom/Vertex.h | 80 +- src/client/multiplayer/MultiPlayerLevel.h | 530 +- src/client/particle/BreakingItemParticle.h | 132 +- src/client/particle/BubbleParticle.h | 102 +- src/client/particle/CritParticle2.h | 148 +- src/client/particle/ExplodeParticle.h | 102 +- src/client/particle/FlameParticle.h | 146 +- src/client/particle/HugeExplosionParticle.h | 144 +- .../particle/HugeExplosionSeedParticle.h | 80 +- src/client/particle/LavaParticle.h | 126 +- src/client/particle/Particle.cpp | 198 +- src/client/particle/Particle.h | 92 +- src/client/particle/ParticleEngine.cpp | 314 +- src/client/particle/ParticleEngine.h | 108 +- src/client/particle/ParticleInclude.h | 34 +- src/client/particle/RedDustParticle.h | 146 +- src/client/particle/SmokeParticle.h | 138 +- src/client/particle/SplashParticle.h | 52 +- src/client/particle/TakeAnimationParticle.h | 164 +- src/client/particle/TerrainParticle.h | 126 +- src/client/player/LocalPlayer.cpp | 1712 +-- src/client/player/LocalPlayer.h | 220 +- src/client/player/RemotePlayer.cpp | 70 +- src/client/player/RemotePlayer.h | 42 +- src/client/player/input/ControllerTurnInput.h | 138 +- src/client/player/input/IBuildInput.h | 90 +- src/client/player/input/IInputHolder.h | 150 +- src/client/player/input/IMoveInput.h | 94 +- src/client/player/input/ITurnInput.h | 104 +- src/client/player/input/KeyboardInput.cpp | 122 +- src/client/player/input/KeyboardInput.h | 72 +- src/client/player/input/MouseBuildInput.h | 72 +- src/client/player/input/MouseTurnInput.h | 86 +- src/client/player/input/XperiaPlayInput.h | 104 +- .../input/touchscreen/ITouchScreenModel.h | 28 +- .../player/input/touchscreen/TouchAreaModel.h | 678 +- .../input/touchscreen/TouchInputHolder.h | 942 +- .../player/input/touchscreen/TouchTurnInput.h | 176 +- .../input/touchscreen/TouchscreenInput.cpp | 1048 +- .../input/touchscreen/TouchscreenInput.h | 160 +- src/client/renderer/Chunk.cpp | 524 +- src/client/renderer/Chunk.h | 148 +- src/client/renderer/ChunkRenderer.h | 44 +- src/client/renderer/Color4.h | 106 +- src/client/renderer/DirtyChunkSorter.h | 70 +- src/client/renderer/DistanceChunkSorter.h | 54 +- src/client/renderer/EntityTileRenderer.cpp | 16 +- src/client/renderer/EntityTileRenderer.h | 42 +- src/client/renderer/GameRenderer.cpp | 1914 +-- src/client/renderer/GameRenderer.h | 234 +- src/client/renderer/ItemInHandRenderer.cpp | 1066 +- src/client/renderer/ItemInHandRenderer.h | 114 +- src/client/renderer/LevelRenderer.cpp | 2598 ++-- src/client/renderer/LevelRenderer.h | 260 +- src/client/renderer/RenderChunk.cpp | 34 +- src/client/renderer/RenderChunk.h | 48 +- src/client/renderer/RenderChunkHandler.h | 46 +- src/client/renderer/RenderList.cpp | 192 +- src/client/renderer/RenderList.h | 82 +- src/client/renderer/Tesselator.cpp | 874 +- src/client/renderer/Tesselator.h | 260 +- src/client/renderer/TextureData.h | 74 +- src/client/renderer/Textures.cpp | 722 +- src/client/renderer/Textures.h | 154 +- src/client/renderer/TileRenderer.cpp | 5628 ++++---- src/client/renderer/TileRenderer.h | 170 +- src/client/renderer/VertecDecl.h | 42 +- src/client/renderer/culling/AllowAllCuller.h | 58 +- src/client/renderer/culling/Culler.h | 44 +- src/client/renderer/culling/Frustum.cpp | 6 +- src/client/renderer/culling/Frustum.h | 326 +- src/client/renderer/culling/FrustumCuller.h | 78 +- src/client/renderer/culling/FrustumData.h | 194 +- src/client/renderer/culling/tmp/Frustum.h | 344 +- .../renderer/culling/tmp/FrustumCuller.h | 330 +- src/client/renderer/culling/tmp/FrustumData.h | 204 +- .../renderer/culling/tmp/_FrustumCuller.h | 74 +- src/client/renderer/entity/ArrowRenderer.cpp | 124 +- src/client/renderer/entity/ArrowRenderer.h | 16 +- .../renderer/entity/ChickenRenderer.cpp | 46 +- src/client/renderer/entity/ChickenRenderer.h | 44 +- src/client/renderer/entity/CreeperRenderer.h | 188 +- .../entity/EntityRenderDispatcher.cpp | 388 +- .../renderer/entity/EntityRenderDispatcher.h | 152 +- src/client/renderer/entity/EntityRenderer.cpp | 498 +- src/client/renderer/entity/EntityRenderer.h | 80 +- .../renderer/entity/FallingTileRenderer.cpp | 64 +- .../renderer/entity/FallingTileRenderer.h | 44 +- .../renderer/entity/HumanoidMobRenderer.cpp | 446 +- .../renderer/entity/HumanoidMobRenderer.h | 62 +- src/client/renderer/entity/ItemRenderer.cpp | 642 +- src/client/renderer/entity/ItemRenderer.h | 76 +- .../renderer/entity/ItemSpriteRenderer.cpp | 82 +- .../renderer/entity/ItemSpriteRenderer.h | 36 +- src/client/renderer/entity/MobRenderer.cpp | 482 +- src/client/renderer/entity/MobRenderer.h | 102 +- .../renderer/entity/PaintingRenderer.cpp | 196 +- src/client/renderer/entity/PaintingRenderer.h | 22 +- src/client/renderer/entity/PigRenderer.h | 58 +- src/client/renderer/entity/PlayerRenderer.cpp | 248 +- src/client/renderer/entity/PlayerRenderer.h | 58 +- src/client/renderer/entity/SheepRenderer.cpp | 58 +- src/client/renderer/entity/SheepRenderer.h | 40 +- src/client/renderer/entity/SpiderRenderer.h | 92 +- src/client/renderer/entity/TntRenderer.cpp | 94 +- src/client/renderer/entity/TntRenderer.h | 36 +- .../renderer/entity/TripodCameraRenderer.cpp | 150 +- .../renderer/entity/TripodCameraRenderer.h | 58 +- src/client/renderer/gles.cpp | 772 +- src/client/renderer/gles.h | 296 +- .../renderer/ptexture/DynamicTexture.cpp | 416 +- src/client/renderer/ptexture/DynamicTexture.h | 118 +- .../renderer/tileentity/ChestRenderer.cpp | 152 +- .../renderer/tileentity/ChestRenderer.h | 42 +- .../renderer/tileentity/SignRenderer.cpp | 130 +- src/client/renderer/tileentity/SignRenderer.h | 42 +- .../tileentity/TileEntityRenderDispatcher.cpp | 272 +- .../tileentity/TileEntityRenderDispatcher.h | 122 +- .../tileentity/TileEntityRenderer.cpp | 64 +- .../renderer/tileentity/TileEntityRenderer.h | 76 +- src/client/sound/Sound.cpp | 452 +- src/client/sound/Sound.h | 360 +- src/client/sound/SoundEngine.cpp | 514 +- src/client/sound/SoundEngine.mm | 770 +- src/client/sound/SoundRepository.h | 118 +- src/locale/I18n.cpp | 242 +- src/locale/I18n.h | 62 +- src/main.cpp | 92 +- src/main_android.cpp | 448 +- src/main_android.h | 1178 +- src/main_android_java.cpp | 542 +- src/main_android_java.h | 348 +- src/main_glfw.h | 446 +- src/main_win32.h | 662 +- src/nbt/ByteArrayTag.h | 158 +- src/nbt/ByteTag.h | 114 +- src/nbt/CompoundTag.h | 484 +- src/nbt/DoubleTag.h | 114 +- src/nbt/EndTag.h | 82 +- src/nbt/FloatTag.h | 118 +- src/nbt/IntTag.h | 116 +- src/nbt/ListTag.h | 342 +- src/nbt/LongTag.h | 112 +- src/nbt/NbtIo.h | 52 +- src/nbt/ShortTag.h | 118 +- src/nbt/StringTag.h | 124 +- src/nbt/Tag.cpp | 278 +- src/nbt/Tag.h | 130 +- src/network/ClientSideNetworkHandler.cpp | 1852 +-- src/network/ClientSideNetworkHandler.h | 214 +- src/network/NATPunchHandler.cpp | 66 +- src/network/NATPunchHandler.h | 48 +- src/network/NetEventCallback.cpp | 118 +- src/network/NetEventCallback.h | 268 +- src/network/PHPDirectoryServer2.h | 274 +- src/network/Packet.cpp | 424 +- src/network/Packet.h | 346 +- src/network/RakNetInstance.cpp | 1480 +- src/network/RakNetInstance.h | 236 +- src/network/ServerSideNetworkHandler.cpp | 1430 +- src/network/ServerSideNetworkHandler.h | 168 +- src/network/packet/AddEntityPacket.h | 186 +- src/network/packet/AddItemEntityPacket.h | 156 +- src/network/packet/AddMobPacket.h | 156 +- src/network/packet/AddPaintingPacket.h | 94 +- src/network/packet/AddPlayerPacket.h | 186 +- src/network/packet/AdventureSettingsPacket.h | 134 +- src/network/packet/AnimatePacket.h | 92 +- src/network/packet/ChatPacket.h | 80 +- src/network/packet/ChunkDataPacket.h | 154 +- src/network/packet/ContainerAckPacket.h | 86 +- src/network/packet/ContainerClosePacket.h | 70 +- src/network/packet/ContainerOpenPacket.h | 94 +- .../packet/ContainerSetContentPacket.h | 88 +- src/network/packet/ContainerSetDataPacket.h | 86 +- src/network/packet/ContainerSetSlotPacket.h | 128 +- src/network/packet/DropItemPacket.h | 90 +- src/network/packet/EntityEventPacket.h | 80 +- src/network/packet/ExplodePacket.h | 152 +- src/network/packet/HurtArmorPacket.h | 64 +- src/network/packet/InteractPacket.h | 94 +- src/network/packet/LevelEventPacket.h | 98 +- src/network/packet/LoginPacket.h | 102 +- src/network/packet/LoginStatusPacket.h | 80 +- src/network/packet/MessagePacket.h | 78 +- src/network/packet/MoveEntityPacket.h | 156 +- src/network/packet/MovePlayerPacket.h | 108 +- src/network/packet/PacketInclude.h | 108 +- src/network/packet/PlaceBlockPacket.h | 120 +- src/network/packet/PlayerActionPacket.h | 124 +- .../packet/PlayerArmorEquipmentPacket.h | 156 +- src/network/packet/PlayerEquipmentPacket.h | 92 +- src/network/packet/ReadyPacket.h | 84 +- src/network/packet/RemoveBlockPacket.h | 104 +- src/network/packet/RemoveEntityPacket.h | 78 +- src/network/packet/RemovePlayerPacket.h | 82 +- src/network/packet/RequestChunkPacket.h | 84 +- src/network/packet/RespawnPacket.h | 94 +- src/network/packet/SendInventoryPacket.h | 138 +- src/network/packet/SetEntityDataPacket.h | 128 +- src/network/packet/SetEntityMotionPacket.h | 136 +- src/network/packet/SetHealthPacket.h | 82 +- src/network/packet/SetSpawnPositionPacket.h | 104 +- src/network/packet/SetTimePacket.h | 74 +- src/network/packet/SignUpdatePacket.h | 136 +- src/network/packet/StartGamePacket.h | 124 +- src/network/packet/TakeItemEntityPacket.h | 84 +- src/network/packet/TeleportEntityPacket.h | 154 +- src/network/packet/TileEventPacket.h | 96 +- src/network/packet/UpdateArmorPacket.h | 56 +- src/network/packet/UpdateBlockPacket.h | 110 +- src/network/packet/UseItemPacket.h | 172 +- src/platform/CThread.cpp | 146 +- src/platform/CThread.h | 110 +- src/platform/audio/SoundSystem.h | 58 +- src/platform/audio/SoundSystemAL.cpp | 514 +- src/platform/audio/SoundSystemAL.h | 158 +- src/platform/audio/SoundSystemSL.cpp | 486 +- src/platform/audio/SoundSystemSL.h | 158 +- src/platform/file.h | 136 +- src/platform/input/Controller.cpp | 150 +- src/platform/input/Controller.h | 66 +- src/platform/input/Keyboard.cpp | 116 +- src/platform/input/Keyboard.h | 284 +- src/platform/input/Mouse.cpp | 338 +- src/platform/input/Mouse.h | 224 +- src/platform/input/Multitouch.cpp | 40 +- src/platform/input/Multitouch.h | 352 +- src/platform/log.h | 94 +- src/platform/time.cpp | 448 +- src/platform/time.h | 222 +- src/raknet/AutopatcherPatchContext.h | 46 +- src/raknet/AutopatcherRepositoryInterface.h | 144 +- src/raknet/BitStream.cpp | 2344 +-- src/raknet/BitStream.h | 4064 +++--- src/raknet/CCRakNetSlidingWindow.cpp | 664 +- src/raknet/CCRakNetSlidingWindow.h | 418 +- src/raknet/CCRakNetUDT.cpp | 1596 +-- src/raknet/CCRakNetUDT.h | 788 +- src/raknet/CMakeLists.txt | 2 +- src/raknet/CheckSum.cpp | 194 +- src/raknet/CheckSum.h | 106 +- src/raknet/CloudClient.cpp | 484 +- src/raknet/CloudClient.h | 326 +- src/raknet/CloudCommon.cpp | 318 +- src/raknet/CloudCommon.h | 282 +- src/raknet/CloudServer.cpp | 3346 ++--- src/raknet/CloudServer.h | 750 +- src/raknet/CommandParserInterface.cpp | 322 +- src/raknet/CommandParserInterface.h | 280 +- src/raknet/ConnectionGraph2.cpp | 598 +- src/raknet/ConnectionGraph2.h | 236 +- src/raknet/ConsoleServer.cpp | 622 +- src/raknet/ConsoleServer.h | 154 +- src/raknet/DS_BPlusTree.h | 2292 +-- src/raknet/DS_BinarySearchTree.h | 2268 +-- src/raknet/DS_BytePool.cpp | 298 +- src/raknet/DS_BytePool.h | 92 +- src/raknet/DS_ByteQueue.cpp | 254 +- src/raknet/DS_ByteQueue.h | 80 +- src/raknet/DS_Hash.h | 688 +- src/raknet/DS_Heap.h | 594 +- src/raknet/DS_HuffmanEncodingTree.cpp | 598 +- src/raknet/DS_HuffmanEncodingTree.h | 134 +- src/raknet/DS_HuffmanEncodingTreeFactory.h | 114 +- src/raknet/DS_HuffmanEncodingTreeNode.h | 42 +- src/raknet/DS_LinkedList.h | 2504 ++-- src/raknet/DS_List.h | 1036 +- src/raknet/DS_Map.h | 642 +- src/raknet/DS_MemoryPool.h | 588 +- src/raknet/DS_Multilist.h | 3288 ++--- src/raknet/DS_OrderedChannelHeap.h | 488 +- src/raknet/DS_OrderedList.h | 526 +- src/raknet/DS_Queue.h | 870 +- src/raknet/DS_QueueLinkedList.h | 206 +- src/raknet/DS_RangeList.h | 472 +- src/raknet/DS_Table.cpp | 2236 +-- src/raknet/DS_Table.h | 686 +- src/raknet/DS_ThreadsafeAllocatingQueue.h | 286 +- src/raknet/DS_Tree.h | 196 +- src/raknet/DS_WeightedGraph.h | 1074 +- src/raknet/DataCompressor.cpp | 126 +- src/raknet/DataCompressor.h | 66 +- src/raknet/DirectoryDeltaTransfer.cpp | 484 +- src/raknet/DirectoryDeltaTransfer.h | 328 +- src/raknet/DynDNS.cpp | 472 +- src/raknet/DynDNS.h | 200 +- src/raknet/EmailSender.cpp | 724 +- src/raknet/EmailSender.h | 116 +- src/raknet/EncodeClassName.cpp | 2 +- src/raknet/EpochTimeToString.cpp | 70 +- src/raknet/EpochTimeToString.h | 30 +- src/raknet/Export.h | 26 +- src/raknet/FileList.cpp | 1588 +- src/raknet/FileList.h | 516 +- src/raknet/FileListNodeContext.h | 78 +- src/raknet/FileListTransfer.cpp | 2172 +-- src/raknet/FileListTransfer.h | 346 +- src/raknet/FileListTransferCBInterface.h | 308 +- src/raknet/FileOperations.cpp | 346 +- src/raknet/FileOperations.h | 48 +- src/raknet/FormatString.cpp | 60 +- src/raknet/FormatString.h | 44 +- src/raknet/FullyConnectedMesh2.cpp | 1104 +- src/raknet/FullyConnectedMesh2.h | 506 +- src/raknet/GetTime.cpp | 444 +- src/raknet/GetTime.h | 70 +- src/raknet/Getche.cpp | 54 +- src/raknet/Getche.h | 22 +- src/raknet/Gets.cpp | 50 +- src/raknet/Gets.h | 28 +- src/raknet/GridSectorizer.cpp | 382 +- src/raknet/GridSectorizer.h | 136 +- src/raknet/HTTPConnection.cpp | 626 +- src/raknet/HTTPConnection.h | 352 +- src/raknet/IncrementalReadInterface.cpp | 30 +- src/raknet/IncrementalReadInterface.h | 56 +- src/raknet/InternalPacket.h | 262 +- src/raknet/Itoa.cpp | 104 +- src/raknet/Itoa.h | 30 +- src/raknet/Kbhit.h | 168 +- src/raknet/LinuxStrings.cpp | 52 +- src/raknet/LinuxStrings.h | 52 +- src/raknet/LocklessTypes.cpp | 84 +- src/raknet/LocklessTypes.h | 80 +- src/raknet/LogCommandParser.cpp | 558 +- src/raknet/LogCommandParser.h | 240 +- src/raknet/MTUSize.h | 62 +- src/raknet/MessageFilter.cpp | 816 +- src/raknet/MessageFilter.h | 382 +- src/raknet/MessageIdentifiers.h | 806 +- src/raknet/NatPunchthroughClient.cpp | 1940 +-- src/raknet/NatPunchthroughClient.h | 530 +- src/raknet/NatPunchthroughServer.cpp | 1126 +- src/raknet/NatPunchthroughServer.h | 290 +- src/raknet/NatTypeDetectionClient.cpp | 340 +- src/raknet/NatTypeDetectionClient.h | 162 +- src/raknet/NatTypeDetectionCommon.cpp | 232 +- src/raknet/NatTypeDetectionCommon.h | 112 +- src/raknet/NatTypeDetectionServer.cpp | 526 +- src/raknet/NatTypeDetectionServer.h | 240 +- src/raknet/NativeFeatureIncludes.h | 350 +- src/raknet/NativeFeatureIncludesOverrides.h | 18 +- src/raknet/NativeTypes.h | 46 +- src/raknet/NetworkIDManager.cpp | 216 +- src/raknet/NetworkIDManager.h | 140 +- src/raknet/NetworkIDObject.cpp | 124 +- src/raknet/NetworkIDObject.h | 162 +- src/raknet/PS3Includes.h | 142 +- src/raknet/PacketConsoleLogger.cpp | 52 +- src/raknet/PacketConsoleLogger.h | 76 +- src/raknet/PacketFileLogger.cpp | 90 +- src/raknet/PacketFileLogger.h | 74 +- src/raknet/PacketLogger.cpp | 912 +- src/raknet/PacketLogger.h | 202 +- src/raknet/PacketOutputWindowLogger.cpp | 78 +- src/raknet/PacketOutputWindowLogger.h | 68 +- src/raknet/PacketPriority.h | 158 +- src/raknet/PacketizedTCP.cpp | 816 +- src/raknet/PacketizedTCP.h | 186 +- src/raknet/PluginInterface2.cpp | 178 +- src/raknet/PluginInterface2.h | 400 +- src/raknet/RPC4Plugin.cpp | 1188 +- src/raknet/RPC4Plugin.h | 472 +- src/raknet/Rackspace.cpp | 1310 +- src/raknet/Rackspace.h | 810 +- src/raknet/RakAlloca.h | 32 +- src/raknet/RakAssert.h | 4 +- src/raknet/RakMemoryOverride.cpp | 588 +- src/raknet/RakMemoryOverride.h | 472 +- src/raknet/RakNet.vcproj | 1690 +-- src/raknet/RakNetCommandParser.cpp | 604 +- src/raknet/RakNetCommandParser.h | 126 +- src/raknet/RakNetDefines.h | 338 +- src/raknet/RakNetDefinesOverrides.h | 4 +- src/raknet/RakNetSmartPtr.h | 346 +- src/raknet/RakNetSocket.cpp | 50 +- src/raknet/RakNetSocket.h | 76 +- src/raknet/RakNetStatistics.cpp | 296 +- src/raknet/RakNetStatistics.h | 252 +- src/raknet/RakNetTime.h | 46 +- src/raknet/RakNetTransport2.cpp | 256 +- src/raknet/RakNetTransport2.h | 204 +- src/raknet/RakNetTypes.cpp | 1450 +- src/raknet/RakNetTypes.h | 964 +- src/raknet/RakNetVersion.h | 16 +- src/raknet/RakNet_vc8.vcproj | 1698 +-- src/raknet/RakNet_vc9.vcproj | 1698 +-- src/raknet/RakPeer.cpp | 11928 ++++++++-------- src/raknet/RakPeer.h | 1968 +-- src/raknet/RakPeerInterface.h | 1142 +- src/raknet/RakSleep.cpp | 106 +- src/raknet/RakSleep.h | 16 +- src/raknet/RakString.cpp | 2844 ++-- src/raknet/RakString.h | 614 +- src/raknet/RakThread.cpp | 326 +- src/raknet/RakThread.h | 176 +- src/raknet/RakWString.cpp | 690 +- src/raknet/RakWString.h | 226 +- src/raknet/Rand.cpp | 548 +- src/raknet/Rand.h | 120 +- src/raknet/ReadyEvent.cpp | 1128 +- src/raknet/ReadyEvent.h | 468 +- src/raknet/RefCountedObj.h | 50 +- src/raknet/ReliabilityLayer.cpp | 7654 +++++----- src/raknet/ReliabilityLayer.h | 1194 +- src/raknet/ReplicaEnums.h | 86 +- src/raknet/ReplicaManager3.cpp | 4500 +++--- src/raknet/ReplicaManager3.h | 2080 +-- src/raknet/Router2.cpp | 2658 ++-- src/raknet/Router2.h | 390 +- src/raknet/SHA1.cpp | 652 +- src/raknet/SHA1.h | 174 +- src/raknet/SecureHandshake.cpp | 108 +- src/raknet/SecureHandshake.h | 50 +- src/raknet/SendToThread.cpp | 154 +- src/raknet/SendToThread.h | 94 +- src/raknet/SignaledEvent.cpp | 490 +- src/raknet/SignaledEvent.h | 118 +- src/raknet/SimpleMutex.cpp | 362 +- src/raknet/SimpleMutex.h | 140 +- src/raknet/SingleProducerConsumer.h | 518 +- src/raknet/SocketDefines.h | 138 +- src/raknet/SocketIncludes.h | 62 +- src/raknet/SocketLayer.cpp | 3702 ++--- src/raknet/SocketLayer.h | 328 +- src/raknet/StringCompressor.cpp | 998 +- src/raknet/StringCompressor.h | 210 +- src/raknet/StringTable.cpp | 280 +- src/raknet/StringTable.h | 192 +- src/raknet/SuperFastHash.cpp | 238 +- src/raknet/SuperFastHash.h | 34 +- src/raknet/TCPInterface.cpp | 2436 ++-- src/raknet/TCPInterface.h | 480 +- src/raknet/TableSerializer.cpp | 640 +- src/raknet/TableSerializer.h | 416 +- src/raknet/TeamBalancer.cpp | 1748 +-- src/raknet/TeamBalancer.h | 388 +- src/raknet/TelnetTransport.cpp | 728 +- src/raknet/TelnetTransport.h | 144 +- src/raknet/ThreadPool.h | 1240 +- src/raknet/ThreadsafePacketLogger.cpp | 76 +- src/raknet/ThreadsafePacketLogger.h | 80 +- src/raknet/TransportInterface.h | 166 +- src/raknet/TwoWayAuthentication.cpp | 862 +- src/raknet/TwoWayAuthentication.h | 292 +- src/raknet/UDPForwarder.cpp | 1632 +-- src/raknet/UDPForwarder.h | 354 +- src/raknet/UDPProxyClient.cpp | 602 +- src/raknet/UDPProxyClient.h | 356 +- src/raknet/UDPProxyCommon.h | 114 +- src/raknet/UDPProxyCoordinator.cpp | 1086 +- src/raknet/UDPProxyCoordinator.h | 230 +- src/raknet/UDPProxyServer.cpp | 334 +- src/raknet/UDPProxyServer.h | 236 +- src/raknet/VariableDeltaSerializer.cpp | 550 +- src/raknet/VariableDeltaSerializer.h | 514 +- src/raknet/VariableListDeltaTracker.cpp | 80 +- src/raknet/VariableListDeltaTracker.h | 262 +- src/raknet/VariadicSQLParser.cpp | 300 +- src/raknet/VariadicSQLParser.h | 48 +- src/raknet/VitaIncludes.cpp | 326 +- src/raknet/VitaIncludes.h | 114 +- src/raknet/WSAStartupSingleton.cpp | 150 +- src/raknet/WSAStartupSingleton.h | 32 +- src/raknet/WindowsIncludes.h | 34 +- src/raknet/XBox360Includes.h | 110 +- src/raknet/_FindFirst.cpp | 318 +- src/raknet/_FindFirst.h | 112 +- src/raknet/gettimeofday.cpp | 110 +- src/raknet/gettimeofday.h | 112 +- src/raknet/rdlmalloc-options.h | 54 +- src/raknet/rdlmalloc.cpp | 8322 +++++------ src/raknet/rdlmalloc.h | 4520 +++--- src/server/CreatorLevel.h | 30 +- src/server/ServerLevel.cpp | 134 +- src/server/ServerLevel.h | 40 +- src/server/ServerPlayer.cpp | 330 +- src/server/ServerPlayer.h | 110 +- src/util/CollectionUtils.h | 34 +- src/util/DataIO.cpp | 58 +- src/util/DataIO.h | 640 +- src/util/IntHashMap.h | 452 +- src/util/MemUtils.h | 70 +- src/util/Mth.cpp | 258 +- src/util/Mth.h | 194 +- src/util/PerfRenderer.cpp | 454 +- src/util/PerfTimer.cpp | 246 +- src/util/PerfTimer.h | 136 +- src/util/RakDataIO.h | 98 +- src/util/Random.h | 508 +- src/util/SmoothFloat.h | 74 +- src/util/StringUtils.cpp | 146 +- src/util/StringUtils.h | 54 +- src/util/WeighedRandom.h | 206 +- src/world/Container.h | 124 +- src/world/Difficulty.h | 30 +- src/world/Direction.cpp | 142 +- src/world/Direction.h | 56 +- src/world/Facing.h | 68 +- src/world/Pos.h | 386 +- src/world/PosTranslator.h | 14 +- src/world/entity/AgableMob.cpp | 106 +- src/world/entity/AgableMob.h | 60 +- src/world/entity/Creature.h | 18 +- src/world/entity/Entity.cpp | 1926 +-- src/world/entity/Entity.h | 452 +- src/world/entity/EntityEvent.h | 46 +- src/world/entity/EntityFactory.cpp | 114 +- src/world/entity/EntityFactory.h | 30 +- src/world/entity/EntityPos.h | 168 +- src/world/entity/EntityRendererId.h | 56 +- src/world/entity/EntityTypes.h | 258 +- src/world/entity/FlyingMob.cpp | 142 +- src/world/entity/FlyingMob.h | 48 +- src/world/entity/HangingEntity.cpp | 468 +- src/world/entity/HangingEntity.h | 70 +- src/world/entity/Mob.cpp | 2370 +-- src/world/entity/Mob.h | 540 +- src/world/entity/MobCategory.cpp | 100 +- src/world/entity/MobCategory.h | 120 +- src/world/entity/MobFactory.h | 180 +- src/world/entity/Motive.cpp | 168 +- src/world/entity/Motive.h | 104 +- src/world/entity/Painting.cpp | 154 +- src/world/entity/Painting.h | 52 +- src/world/entity/PathfinderMob.cpp | 496 +- src/world/entity/PathfinderMob.h | 130 +- src/world/entity/SharedFlags.h | 32 +- src/world/entity/SynchedEntityData.cpp | 500 +- src/world/entity/SynchedEntityData.h | 502 +- src/world/entity/ai/PathNavigation.h | 692 +- src/world/entity/ai/Sensing.h | 76 +- src/world/entity/ai/control/Control.h | 26 +- src/world/entity/ai/control/JumpControl.h | 62 +- src/world/entity/ai/control/MoveControl.cpp | 140 +- src/world/entity/ai/control/MoveControl.h | 76 +- src/world/entity/ai/goal/BreakDoorGoal.h | 114 +- src/world/entity/ai/goal/DoorInteractGoal.h | 158 +- src/world/entity/ai/goal/Goal.h | 88 +- src/world/entity/ai/goal/GoalSelector.h | 208 +- src/world/entity/ai/goal/MeleeAttackGoal.h | 188 +- src/world/entity/ai/goal/RandomStrollGoal.h | 108 +- .../entity/ai/goal/target/HurtByTargetGoal.h | 82 +- .../goal/target/NearestAttackableTargetGoal.h | 84 +- src/world/entity/ai/goal/target/TargetGoal.h | 114 +- src/world/entity/ai/util/RandomPos.h | 142 +- src/world/entity/animal/Animal.cpp | 184 +- src/world/entity/animal/Animal.h | 78 +- src/world/entity/animal/AnimalInclude.h | 20 +- src/world/entity/animal/Chicken.cpp | 226 +- src/world/entity/animal/Chicken.h | 98 +- src/world/entity/animal/Cow.cpp | 158 +- src/world/entity/animal/Cow.h | 84 +- src/world/entity/animal/Pig.cpp | 140 +- src/world/entity/animal/Pig.h | 96 +- src/world/entity/animal/Sheep.cpp | 504 +- src/world/entity/animal/Sheep.h | 182 +- src/world/entity/animal/WaterAnimal.cpp | 96 +- src/world/entity/animal/WaterAnimal.h | 92 +- src/world/entity/item/FallingTile.cpp | 234 +- src/world/entity/item/FallingTile.h | 80 +- src/world/entity/item/ItemEntity.h | 98 +- src/world/entity/item/PrimedTnt.cpp | 182 +- src/world/entity/item/PrimedTnt.h | 72 +- src/world/entity/item/TripodCamera.cpp | 176 +- src/world/entity/item/TripodCamera.h | 64 +- src/world/entity/monster/Creeper.cpp | 228 +- src/world/entity/monster/Creeper.h | 92 +- src/world/entity/monster/Monster.cpp | 288 +- src/world/entity/monster/Monster.h | 114 +- src/world/entity/monster/MonsterInclude.h | 20 +- src/world/entity/monster/PigZombie.cpp | 276 +- src/world/entity/monster/PigZombie.h | 74 +- src/world/entity/monster/Skeleton.cpp | 214 +- src/world/entity/monster/Skeleton.h | 86 +- src/world/entity/monster/Spider.cpp | 274 +- src/world/entity/monster/Spider.h | 124 +- src/world/entity/monster/Zombie.cpp | 258 +- src/world/entity/monster/Zombie.h | 88 +- src/world/entity/player/Abilities.h | 94 +- src/world/entity/player/Inventory.cpp | 718 +- src/world/entity/player/Inventory.h | 114 +- .../entity/player/InventorySlotManager.h | 18 +- src/world/entity/player/OldInventory.h | 88 +- src/world/entity/player/Player.cpp | 1848 +-- src/world/entity/player/Player.h | 402 +- src/world/entity/projectile/Arrow.cpp | 692 +- src/world/entity/projectile/Arrow.h | 114 +- src/world/entity/projectile/Snowball.h | 100 +- src/world/entity/projectile/Throwable.cpp | 520 +- src/world/entity/projectile/Throwable.h | 126 +- src/world/entity/projectile/ThrownEgg.h | 126 +- src/world/food/FoodConstants.h | 32 +- src/world/food/SimpleFoodData.cpp | 66 +- src/world/food/SimpleFoodData.h | 44 +- src/world/inventory/BaseContainerMenu.cpp | 82 +- src/world/inventory/BaseContainerMenu.h | 94 +- src/world/inventory/ContainerMenu.cpp | 70 +- src/world/inventory/ContainerMenu.h | 60 +- src/world/inventory/CraftingContainer.h | 184 +- src/world/inventory/FillingContainer.cpp | 1248 +- src/world/inventory/FillingContainer.h | 214 +- src/world/inventory/FurnaceMenu.cpp | 146 +- src/world/inventory/FurnaceMenu.h | 76 +- src/world/item/ArmorItem.cpp | 154 +- src/world/item/ArmorItem.h | 116 +- src/world/item/AuxDataTileItem.h | 68 +- src/world/item/BedItem.cpp | 70 +- src/world/item/BedItem.h | 26 +- src/world/item/BowItem.h | 128 +- src/world/item/BowlFoodItem.h | 50 +- src/world/item/CameraItem.h | 52 +- src/world/item/ClothTileItem.h | 74 +- src/world/item/CoalItem.h | 58 +- src/world/item/DiggerItem.h | 170 +- src/world/item/DoorItem.h | 146 +- src/world/item/DyePowderItem.cpp | 244 +- src/world/item/DyePowderItem.h | 106 +- src/world/item/EggItem.h | 64 +- src/world/item/FlintAndSteelItem.h | 86 +- src/world/item/FoodItem.h | 190 +- src/world/item/HangingEntityItem.cpp | 86 +- src/world/item/HangingEntityItem.h | 30 +- src/world/item/HatchetItem.cpp | 54 +- src/world/item/HatchetItem.h | 46 +- src/world/item/HoeItem.cpp | 80 +- src/world/item/HoeItem.h | 32 +- src/world/item/Item.cpp | 616 +- src/world/item/Item.h | 816 +- src/world/item/ItemCategory.h | 54 +- src/world/item/ItemInclude.h | 72 +- src/world/item/ItemInstance.cpp | 584 +- src/world/item/ItemInstance.h | 232 +- src/world/item/LeafTileItem.h | 80 +- src/world/item/PickaxeItem.cpp | 106 +- src/world/item/PickaxeItem.h | 46 +- src/world/item/SaplingTileItem.h | 58 +- src/world/item/SeedItem.h | 62 +- src/world/item/ShearsItem.h | 98 +- src/world/item/ShovelItem.cpp | 48 +- src/world/item/ShovelItem.h | 42 +- src/world/item/SignItem.h | 100 +- src/world/item/SnowballItem.h | 60 +- src/world/item/StoneSlabTileItem.h | 130 +- src/world/item/TileItem.h | 172 +- src/world/item/TilePlanterItem.h | 106 +- src/world/item/TreeTileItem.h | 60 +- src/world/item/UseAnim.h | 28 +- src/world/item/WeaponItem.h | 164 +- src/world/item/crafting/ArmorRecipes.cpp | 106 +- src/world/item/crafting/ArmorRecipes.h | 28 +- src/world/item/crafting/ClothDyeRecipes.h | 146 +- src/world/item/crafting/FoodRecipes.h | 108 +- src/world/item/crafting/FurnaceRecipes.cpp | 132 +- src/world/item/crafting/FurnaceRecipes.h | 66 +- src/world/item/crafting/OreRecipes.cpp | 90 +- src/world/item/crafting/OreRecipes.h | 28 +- src/world/item/crafting/Recipe.cpp | 218 +- src/world/item/crafting/Recipe.h | 122 +- src/world/item/crafting/RecipeCategory.h | 24 +- src/world/item/crafting/Recipes.cpp | 1284 +- src/world/item/crafting/Recipes.h | 196 +- src/world/item/crafting/ShapedRecipe.h | 210 +- src/world/item/crafting/ShapelessRecipe.h | 166 +- src/world/item/crafting/StructureRecipes.h | 200 +- src/world/item/crafting/ToolRecipes.cpp | 118 +- src/world/item/crafting/ToolRecipes.h | 28 +- src/world/item/crafting/WeaponRecipes.cpp | 98 +- src/world/item/crafting/WeaponRecipes.h | 30 +- src/world/level/ChunkPos.h | 98 +- src/world/level/Explosion.cpp | 328 +- src/world/level/Explosion.h | 76 +- src/world/level/FoliageColor.h | 72 +- src/world/level/Level.cpp | 4482 +++--- src/world/level/Level.h | 686 +- src/world/level/LevelConstants.h | 28 +- src/world/level/LevelListener.h | 86 +- src/world/level/LevelSettings.h | 128 +- src/world/level/LevelSource.h | 72 +- src/world/level/LightLayer.cpp | 8 +- src/world/level/LightLayer.h | 40 +- src/world/level/LightUpdate.cpp | 386 +- src/world/level/LightUpdate.h | 62 +- src/world/level/MobSpawner.cpp | 568 +- src/world/level/MobSpawner.h | 82 +- src/world/level/Region.cpp | 280 +- src/world/level/Region.h | 78 +- src/world/level/TickNextTickData.cpp | 58 +- src/world/level/TickNextTickData.h | 50 +- src/world/level/TilePos.h | 88 +- src/world/level/biome/Biome.cpp | 458 +- src/world/level/biome/Biome.h | 190 +- src/world/level/biome/BiomeInclude.h | 22 +- src/world/level/biome/BiomeSource.cpp | 356 +- src/world/level/biome/BiomeSource.h | 124 +- src/world/level/biome/FlatBiome.h | 22 +- src/world/level/biome/ForestBiome.h | 48 +- src/world/level/biome/RainforestBiome.h | 42 +- src/world/level/biome/SwampBiome.h | 22 +- src/world/level/biome/TaigaBiome.h | 44 +- src/world/level/chunk/ChunkCache.h | 528 +- src/world/level/chunk/ChunkSource.h | 92 +- src/world/level/chunk/DataLayer.h | 150 +- src/world/level/chunk/EmptyLevelChunk.h | 392 +- src/world/level/chunk/LevelChunk.cpp | 1426 +- src/world/level/chunk/LevelChunk.h | 284 +- src/world/level/chunk/storage/ChunkStorage.h | 60 +- .../level/chunk/storage/MemoryChunkStorage.h | 20 +- src/world/level/dimension/Dimension.cpp | 280 +- src/world/level/dimension/Dimension.h | 128 +- .../level/dimension/NormalDayCycleDimension.h | 82 +- src/world/level/levelgen/CanyonFeature.cpp | 336 +- src/world/level/levelgen/CanyonFeature.h | 38 +- src/world/level/levelgen/DungeonFeature.cpp | 352 +- src/world/level/levelgen/DungeonFeature.h | 46 +- src/world/level/levelgen/LargeCaveFeature.h | 360 +- src/world/level/levelgen/LargeFeature.cpp | 54 +- src/world/level/levelgen/LargeFeature.h | 54 +- .../level/levelgen/RandomLevelSource.cpp | 1458 +- src/world/level/levelgen/RandomLevelSource.h | 250 +- src/world/level/levelgen/TownFeature.h | 26 +- .../level/levelgen/feature/BirchFeature.h | 150 +- .../level/levelgen/feature/ClayFeature.h | 126 +- src/world/level/levelgen/feature/Feature.cpp | 52 +- src/world/level/levelgen/feature/Feature.h | 46 +- .../level/levelgen/feature/FeatureInclude.h | 28 +- .../level/levelgen/feature/FlowerFeature.h | 70 +- .../level/levelgen/feature/LakeFeature.h | 254 +- src/world/level/levelgen/feature/OreFeature.h | 154 +- .../level/levelgen/feature/PineFeature.h | 184 +- .../level/levelgen/feature/ReedsFeature.h | 80 +- .../level/levelgen/feature/SpringFeature.h | 100 +- .../level/levelgen/feature/SpruceFeature.h | 202 +- .../level/levelgen/feature/TallgrassFeature.h | 80 +- .../level/levelgen/feature/TreeFeature.h | 148 +- .../level/levelgen/synth/ImprovedNoise.cpp | 410 +- .../level/levelgen/synth/ImprovedNoise.h | 76 +- .../level/levelgen/synth/PerlinNoise.cpp | 176 +- src/world/level/levelgen/synth/PerlinNoise.h | 74 +- src/world/level/levelgen/synth/Synth.cpp | 42 +- src/world/level/levelgen/synth/Synth.h | 36 +- src/world/level/material/DecorationMaterial.h | 46 +- src/world/level/material/GasMaterial.h | 54 +- src/world/level/material/LiquidMaterial.h | 54 +- src/world/level/material/Material.cpp | 198 +- src/world/level/material/Material.h | 232 +- src/world/level/material/WebMaterial.h | 38 +- src/world/level/pathfinder/BinaryHeap.h | 402 +- src/world/level/pathfinder/Node.h | 158 +- src/world/level/pathfinder/Path.cpp | 268 +- src/world/level/pathfinder/Path.h | 106 +- src/world/level/pathfinder/PathFinder.h | 788 +- .../storage/ExternalFileLevelStorage.cpp | 1268 +- .../level/storage/ExternalFileLevelStorage.h | 176 +- .../ExternalFileLevelStorageSource.cpp | 386 +- .../storage/ExternalFileLevelStorageSource.h | 90 +- src/world/level/storage/FolderMethods.cpp | 154 +- src/world/level/storage/FolderMethods.h | 44 +- src/world/level/storage/LevelData.cpp | 754 +- src/world/level/storage/LevelData.h | 202 +- src/world/level/storage/LevelStorage.h | 78 +- .../level/storage/LevelStorageSource.cpp | 6 +- src/world/level/storage/LevelStorageSource.h | 120 +- src/world/level/storage/MemoryLevelStorage.h | 106 +- .../level/storage/MemoryLevelStorageSource.h | 116 +- src/world/level/storage/PlayerData.h | 132 +- src/world/level/storage/RegionFile.cpp | 432 +- src/world/level/storage/RegionFile.h | 62 +- src/world/level/tile/BedTile.cpp | 426 +- src/world/level/tile/BedTile.h | 80 +- src/world/level/tile/BookshelfTile.h | 66 +- src/world/level/tile/Bush.h | 164 +- src/world/level/tile/CarriedTile.h | 80 +- src/world/level/tile/ChestTile.cpp | 642 +- src/world/level/tile/ChestTile.h | 102 +- src/world/level/tile/ClayTile.h | 52 +- src/world/level/tile/ClothTile.h | 90 +- src/world/level/tile/CropTile.cpp | 226 +- src/world/level/tile/CropTile.h | 56 +- src/world/level/tile/DirectionalTile.h | 32 +- src/world/level/tile/DirtTile.h | 40 +- src/world/level/tile/DoorTile.cpp | 484 +- src/world/level/tile/DoorTile.h | 128 +- src/world/level/tile/EntityTile.cpp | 76 +- src/world/level/tile/EntityTile.h | 56 +- src/world/level/tile/FarmTile.h | 220 +- src/world/level/tile/FenceGateTile.h | 196 +- src/world/level/tile/FenceTile.h | 234 +- src/world/level/tile/FireTile.h | 618 +- src/world/level/tile/FurnaceTile.cpp | 350 +- src/world/level/tile/FurnaceTile.h | 88 +- src/world/level/tile/GlassTile.h | 54 +- src/world/level/tile/GrassTile.cpp | 108 +- src/world/level/tile/GrassTile.h | 58 +- src/world/level/tile/GravelTile.h | 52 +- src/world/level/tile/HalfTransparentTile.h | 78 +- src/world/level/tile/HeavyTile.cpp | 144 +- src/world/level/tile/HeavyTile.h | 64 +- src/world/level/tile/IceTile.h | 82 +- src/world/level/tile/InvisibleTile.h | 42 +- src/world/level/tile/LadderTile.h | 224 +- src/world/level/tile/LeafTile.h | 416 +- src/world/level/tile/LevelEvent.h | 54 +- src/world/level/tile/LightGemTile.cpp | 30 +- src/world/level/tile/LightGemTile.h | 22 +- src/world/level/tile/LiquidTile.h | 464 +- src/world/level/tile/LiquidTileDynamic.h | 460 +- src/world/level/tile/LiquidTileStatic.h | 132 +- src/world/level/tile/MelonTile.cpp | 54 +- src/world/level/tile/MelonTile.h | 32 +- src/world/level/tile/MetalTile.h | 46 +- src/world/level/tile/MultiTextureTile.h | 84 +- src/world/level/tile/Mushroom.cpp | 112 +- src/world/level/tile/Mushroom.h | 38 +- src/world/level/tile/NetherReactor.cpp | 148 +- src/world/level/tile/NetherReactor.h | 38 +- src/world/level/tile/NetherReactorPattern.cpp | 88 +- src/world/level/tile/NetherReactorPattern.h | 28 +- src/world/level/tile/ObsidianTile.h | 120 +- src/world/level/tile/OreTile.h | 82 +- src/world/level/tile/QuartzBlockTile.h | 128 +- src/world/level/tile/RedStoneOreTile.h | 186 +- src/world/level/tile/ReedTile.h | 196 +- src/world/level/tile/SandStoneTile.h | 74 +- src/world/level/tile/SandTile.cpp | 52 +- src/world/level/tile/SandTile.h | 114 +- src/world/level/tile/Sapling.h | 264 +- src/world/level/tile/SignTile.h | 226 +- src/world/level/tile/SnowTile.h | 76 +- src/world/level/tile/StairTile.cpp | 474 +- src/world/level/tile/StairTile.h | 486 +- src/world/level/tile/StemTile.cpp | 306 +- src/world/level/tile/StemTile.h | 54 +- src/world/level/tile/StoneSlabTile.cpp | 310 +- src/world/level/tile/StoneSlabTile.h | 112 +- src/world/level/tile/StoneTile.h | 48 +- src/world/level/tile/StonecutterTile.h | 64 +- src/world/level/tile/TallGrass.cpp | 110 +- src/world/level/tile/TallGrass.h | 46 +- src/world/level/tile/ThinFenceTile.h | 266 +- src/world/level/tile/Tile.cpp | 1494 +- src/world/level/tile/Tile.h | 778 +- src/world/level/tile/TileInclude.h | 124 +- src/world/level/tile/TntTile.h | 138 +- src/world/level/tile/TopSnowTile.h | 194 +- src/world/level/tile/TorchTile.h | 354 +- src/world/level/tile/TransparentTile.h | 80 +- src/world/level/tile/TrapDoorTile.cpp | 308 +- src/world/level/tile/TrapDoorTile.h | 70 +- src/world/level/tile/TreeTile.h | 134 +- src/world/level/tile/WebTile.h | 118 +- src/world/level/tile/WorkbenchTile.h | 68 +- .../level/tile/entity/ChestTileEntity.cpp | 478 +- src/world/level/tile/entity/ChestTileEntity.h | 132 +- .../level/tile/entity/FurnaceTileEntity.cpp | 502 +- .../level/tile/entity/FurnaceTileEntity.h | 148 +- .../tile/entity/NetherReactorTileEntity.cpp | 804 +- .../tile/entity/NetherReactorTileEntity.h | 110 +- .../level/tile/entity/SignTileEntity.cpp | 116 +- src/world/level/tile/entity/SignTileEntity.h | 78 +- src/world/level/tile/entity/TileEntity.cpp | 442 +- src/world/level/tile/entity/TileEntity.h | 196 +- .../level/tile/entity/TileEntityRendererId.h | 20 +- src/world/phys/AABB.h | 498 +- src/world/phys/HitResult.cpp | 98 +- src/world/phys/HitResult.h | 68 +- src/world/phys/Vec3.h | 462 +- 1340 files changed, 264990 insertions(+), 264996 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1711d1c..5f16a21 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,404 +1,404 @@ -cmake_minimum_required(VERSION 3.21) -project(MinecraftPE) - -include(cmake/CPM.cmake) - -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_POLICY_VERSION_MINIMUM 3.10) - -include(cmake/EnumOption.cmake) - -if(EMSCRIPTEN) - # When configuring web builds with "emcmake cmake -B build -S .", set PLATFORM to Web by default - set(PLATFORM Web CACHE STRING "Platform to build for.") -endif() - -enum_option(PLATFORM "Desktop;Web" "Platform to build for.") - -find_package(Threads REQUIRED) -find_package(OpenSSL) - -if (${PLATFORM} STREQUAL "Desktop") - set(PLATFORM_CPP "PLATFORM_DESKTOP") - - if (MINGW) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") - endif() - - if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++11-narrowing -Wno-narrowing -Wno-invalid-source-encoding -Wno-reserved-user-defined-literal") - endif() - - if (WIN32) - add_definitions(-D_CRT_SECURE_NO_WARNINGS) - include_directories(misc/windows) - set(EXTRA_LIBS ws2_32 winhttp) - elseif(UNIX) - find_library(pthread NAMES pthread) - set(EXTRA_LIBS pthread m) - endif() - -elseif (${PLATFORM} STREQUAL "Web") - set(PLATFORM_CPP "PLATFORM_WEB") - set(EXTRA_LIBS "idbfs.js") -endif() - -# I totally shocked -if(${PLATFORM} MATCHES "Web") - set(PNG_LIB png) - set(AL_LIBTYPE "STATIC") - - add_library(zlib INTERFACE IMPORTED) - set_target_properties(zlib PROPERTIES - INTERFACE_LINK_OPTIONS "-sUSE_ZLIB=1" - ) - - add_library(png INTERFACE IMPORTED) - set_target_properties(png PROPERTIES - INTERFACE_COMPILE_OPTIONS "-sUSE_LIBPNG=1" - INTERFACE_LINK_OPTIONS "-sUSE_LIBPNG=1" - ) - - add_library(glfw INTERFACE IMPORTED) - set_target_properties(glfw PROPERTIES - INTERFACE_LINK_OPTIONS "-sUSE_GLFW=3" - ) -else() - set(PNG_LIB png_shared) - set(AL_LIBTYPE "SHARED") - - CPMAddPackage( - NAME "zlib" - GIT_REPOSITORY "https://github.com/madler/zlib" - GIT_TAG "v1.3.2" - ) - - CPMAddPackage( - NAME "libpng" - GIT_REPOSITORY "https://github.com/pnggroup/libpng.git" - GIT_TAG "v1.6.55" - OPTIONS - "ZLIB_ROOT ${zlib_SOURCE_DIR}" - "ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR}" - "PNG_TOOLS OFF" - "PNG_TESTS OFF" - ) - - CPMAddPackage( - NAME "glfw" - GIT_REPOSITORY "https://github.com/glfw/glfw.git" - GIT_TAG "3.4" - EXCLUDE_FROM_ALL TRUE - OPTIONS - "GLFW_BUILD_EXAMPLES OFF" - "GLFW_BUILD_TESTS OFF" - "GLFW_BUILD_DOCS OFF" - ) -endif() - -CPMAddPackage( - NAME "openal" - GIT_REPOSITORY "https://github.com/kcat/openal-soft.git" - GIT_TAG "1.25.1" - OPTIONS - "ALSOFT_EXAMPLES OFF" - "ALSOFT_TESTS OFF" - "ALSOFT_UTILS OFF" - "LIBTYPE ${AL_LIBTYPE}" - "ALSOFT_ENABLE_MODULES OFF" - "ALSOFT_STATIC_STDCXX ON" - "ALSOFT_STATIC_LIBGCC ON" -) - -# TODO: Clear this paths with * -file(GLOB SERVER_SOURCES - "project/lib_projects/raknet/jni/RaknetSources/*.cpp" - "src/NinecraftApp.cpp" - "src/Performance.cpp" - "src/SharedConstants.cpp" - - "src/client/IConfigListener.cpp" - "src/client/Minecraft.cpp" - "src/client/OptionStrings.cpp" - "src/client/Option.cpp" - "src/client/Options.cpp" - "src/client/OptionsFile.cpp" - "src/client/ServerProfiler.cpp" - - "src/client/gamemode/CreativeMode.cpp" - "src/client/gamemode/GameMode.cpp" - "src/client/gamemode/SurvivalMode.cpp" - - "src/client/player/LocalPlayer.cpp" - "src/client/player/RemotePlayer.cpp" - "src/client/player/input/KeyboardInput.cpp" - - "src/locale/I18n.cpp" - - "src/main.cpp" - "src/main_dedicated.cpp" - - "src/nbt/Tag.cpp" - - "src/network/ClientSideNetworkHandler.cpp" - "src/network/NetEventCallback.cpp" - "src/network/Packet.cpp" - "src/network/RakNetInstance.cpp" - "src/network/ServerSideNetworkHandler.cpp" - "src/network/command/CommandServer.cpp" - - "src/platform/CThread.cpp" - "src/platform/HttpClient.cpp" - "src/platform/PngLoader.cpp" - "src/platform/time.cpp" - - "src/platform/input/Controller.cpp" - "src/platform/input/Keyboard.cpp" - "src/platform/input/Mouse.cpp" - "src/platform/input/Multitouch.cpp" - - "src/server/ArgumentsSettings.cpp" - "src/server/ServerLevel.cpp" - "src/server/ServerPlayer.cpp" - - "src/util/DataIO.cpp" - "src/util/Mth.cpp" - "src/util/PerfTimer.cpp" - "src/util/StringUtils.cpp" - - "src/world/Direction.cpp" - - "src/world/entity/*.cpp" - - "src/world/entity/ai/control/MoveControl.cpp" - - "src/world/entity/animal/*.cpp" - - "src/world/entity/item/*.cpp" - - "src/world/entity/monster/*.cpp" - - "src/world/entity/player/Inventory.cpp" - "src/world/entity/player/Player.cpp" - - "src/world/entity/projectile/Arrow.cpp" - "src/world/entity/projectile/Throwable.cpp" - - "src/world/food/SimpleFoodData.cpp" - - "src/world/inventory/*.cpp" - "src/world/item/*.cpp" - "src/world/item/crafting/*.cpp" - "src/world/level/*.cpp" - - "src/world/level/biome/Biome.cpp" - "src/world/level/biome/BiomeSource.cpp" - - "src/world/level/chunk/LevelChunk.cpp" - - "src/world/level/dimension/Dimension.cpp" - - "src/world/level/levelgen/*.cpp" - "src/world/level/levelgen/feature/Feature.cpp" - "src/world/level/levelgen/synth/*.cpp" - - "src/world/level/material/Material.cpp" - - "src/world/level/pathfinder/Path.cpp" - - "src/world/level/storage/*.cpp" - "src/world/level/tile/*.cpp" - "src/world/level/tile/entity/*.cpp" - - "src/world/phys/HitResult.cpp" -) - -file(GLOB CLIENT_SOURCES - "src/client/*.cpp" - - "src/client/gamemode/*.cpp" - "src/client/gui/*.cpp" - "src/client/gui/components/*.cpp" - "src/client/gui/screens/*.cpp" - "src/client/gui/screens/crafting/*.cpp" - "src/client/gui/screens/touch/*.cpp" - - "src/client/model/*.cpp" - "src/client/model/geom/*.cpp" - - "src/client/particle/*.cpp" - - "src/client/player/*.cpp" - "src/client/player/input/*.cpp" - "src/client/player/input/touchscreen/*.cpp" - - "src/client/renderer/*.cpp" - "src/client/renderer/culling/*.cpp" - "src/client/renderer/entity/*.cpp" - "src/client/renderer/ptexture/*.cpp" - "src/client/renderer/tileentity/*.cpp" - - "src/client/sound/*.cpp" - - "src/locale/*.cpp" - - "src/nbt/*.cpp" - - "src/network/*.cpp" - "src/network/command/*.cpp" - - "src/platform/*.cpp" - "src/platform/audio/SoundSystemAL.cpp" - "src/platform/input/*.cpp" - - "src/raknet/**.cpp" - - "src/server/**.cpp" - - "src/util/**.cpp" - - "src/world/*.cpp" - "src/world/phys/*.cpp" - "src/world/entity/*.cpp" - "src/world/entity/ai/control/*.cpp" - "src/world/entity/animal/*.cpp" - "src/world/entity/item/*.cpp" - "src/world/entity/monster/*.cpp" - "src/world/entity/player/*.cpp" - "src/world/entity/projectile/*.cpp" - - "src/world/food/*.cpp" - "src/world/inventory/*.cpp" - - "src/world/item/*.cpp" - "src/world/item/crafting/*.cpp" - - "src/world/level/*.cpp" - "src/world/level/biome/*.cpp" - "src/world/level/chunk/*.cpp" - "src/world/level/dimension/*.cpp" - "src/world/level/levelgen/*.cpp" - "src/world/level/levelgen/feature/*.cpp" - "src/world/level/levelgen/synth/*.cpp" - "src/world/level/material/*.cpp" - "src/world/level/pathfinder/*.cpp" - "src/world/level/storage/*.cpp" - "src/world/level/tile/*.cpp" - "src/world/level/tile/entity/*.cpp" - - "src/SharedConstants.cpp" - "src/main.cpp" - "src/NinecraftApp.cpp" - - "src/AppPlatform_glfw.cpp" - "src/main.cpp" -) - -if (${PLATFORM} STREQUAL "Desktop") - list(APPEND CLIENT_SOURCES glad/src/glad.c) -endif() - -# Server -if(UNIX) - add_executable("${PROJECT_NAME}-server" ${SERVER_SOURCES}) - - target_compile_definitions("${PROJECT_NAME}-server" PUBLIC "STANDALONE_SERVER" "SERVER_PROFILER") - - target_include_directories("${PROJECT_NAME}-server" PUBLIC - "${CMAKE_SOURCE_DIR}/src/" - "project/lib_projects/raknet/jni/RaknetSources" - ) - - target_link_libraries("${PROJECT_NAME}-server" ${CMAKE_THREAD_LIBS_INIT}) -endif() - -add_executable(${PROJECT_NAME} ${CLIENT_SOURCES}) - -target_compile_definitions(${PROJECT_NAME} PUBLIC ${PLATFORM_CPP}) - -target_include_directories(${PROJECT_NAME} PUBLIC - "${CMAKE_SOURCE_DIR}/glad/include/" - "${CMAKE_SOURCE_DIR}/src" - "${openal_SOURCE_DIR}/include" - "${glfw_SOURCE_DIR}/include" - "lib/include" -) - -if(${PLATFORM} MATCHES "Web") - set(CMAKE_CXX_STANDARD 11) - # uuuh i hate it - set(EM_FLAGS "-pthread -sUSE_PTHREADS=1 -sUSE_LIBPNG=1 -sSHARED_MEMORY=1") - - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EM_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EM_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EM_FLAGS} --preload-file ${CMAKE_SOURCE_DIR}/data@/data") - - target_compile_options(${PROJECT_NAME} PUBLIC - "-Os" - "-Wno-invalid-source-encoding" - "-Wno-narrowing" - "-Wno-deprecated-register" - "-Wno-reserved-user-defined-literal" - ) - - target_link_options(${PROJECT_NAME} PUBLIC - "-Os" - "-sALLOW_MEMORY_GROWTH=1" - "-sFORCE_FILESYSTEM=1" - "-sLEGACY_GL_EMULATION=1" - "-sGL_UNSAFE_OPTS=0" - "-sEMULATE_FUNCTION_POINTER_CASTS=1" - "-sALLOW_TABLE_GROWTH=1" - "-sEXPORTED_RUNTIME_METHODS=['FS','stringToUTF8','UTF8ToString','cwrap','ccall','HEAP8','HEAPU8','HEAP32','HEAPU32']" - "-sEXPORTED_FUNCTIONS=['_main']" - ) - - if (CMAKE_BUILD_TYPE STREQUAL "Debug") - message("DEBUG MODE") - - target_link_options(${PROJECT_NAME} PUBLIC - "-sASSERTIONS=2" - "-sSTACK_OVERFLOW_CHECK=2" - "-sSTACK_SIZE=5242880" - "-sGL_DEBUG=1" - ) - endif() - - target_compile_definitions(${PROJECT_NAME} PUBLIC "__EMSCRIPTEN__" "NO_SOUND" "NO_NETWORK") -endif() - -# Client -target_compile_definitions(${PROJECT_NAME} PUBLIC "OPENGL_ES" "NO_EGL" ${PLATFORM}) -target_link_libraries(${PROJECT_NAME} zlib ${PNG_LIB} OpenAL::OpenAL glfw ${EXTRA_LIBS}) - -if (OpenSSL_FOUND) - target_link_libraries(${PROJECT_NAME} OpenSSL::SSL OpenSSL::Crypto) - target_compile_definitions(${PROJECT_NAME} PUBLIC HTTPCLIENT_USE_OPENSSL) -endif() - -if (NOT UNIX) - add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ - COMMAND_EXPAND_LISTS - ) -endif() - -if(NOT ${PLATFORM} MATCHES "Web") - add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/data" $/data - ) -else() - add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/misc/web/index.html" $ - ) -endif() - -message(STATUS "Compiling with the flags:") +cmake_minimum_required(VERSION 3.21) +project(MinecraftPE) + +include(cmake/CPM.cmake) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_POLICY_VERSION_MINIMUM 3.10) + +include(cmake/EnumOption.cmake) + +if(EMSCRIPTEN) + # When configuring web builds with "emcmake cmake -B build -S .", set PLATFORM to Web by default + set(PLATFORM Web CACHE STRING "Platform to build for.") +endif() + +enum_option(PLATFORM "Desktop;Web" "Platform to build for.") + +find_package(Threads REQUIRED) +find_package(OpenSSL) + +if (${PLATFORM} STREQUAL "Desktop") + set(PLATFORM_CPP "PLATFORM_DESKTOP") + + if (MINGW) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") + endif() + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++11-narrowing -Wno-narrowing -Wno-invalid-source-encoding -Wno-reserved-user-defined-literal") + endif() + + if (WIN32) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + include_directories(misc/windows) + set(EXTRA_LIBS ws2_32 winhttp) + elseif(UNIX) + find_library(pthread NAMES pthread) + set(EXTRA_LIBS pthread m) + endif() + +elseif (${PLATFORM} STREQUAL "Web") + set(PLATFORM_CPP "PLATFORM_WEB") + set(EXTRA_LIBS "idbfs.js") +endif() + +# I totally shocked +if(${PLATFORM} MATCHES "Web") + set(PNG_LIB png) + set(AL_LIBTYPE "STATIC") + + add_library(zlib INTERFACE IMPORTED) + set_target_properties(zlib PROPERTIES + INTERFACE_LINK_OPTIONS "-sUSE_ZLIB=1" + ) + + add_library(png INTERFACE IMPORTED) + set_target_properties(png PROPERTIES + INTERFACE_COMPILE_OPTIONS "-sUSE_LIBPNG=1" + INTERFACE_LINK_OPTIONS "-sUSE_LIBPNG=1" + ) + + add_library(glfw INTERFACE IMPORTED) + set_target_properties(glfw PROPERTIES + INTERFACE_LINK_OPTIONS "-sUSE_GLFW=3" + ) +else() + set(PNG_LIB png_shared) + set(AL_LIBTYPE "SHARED") + + CPMAddPackage( + NAME "zlib" + GIT_REPOSITORY "https://github.com/madler/zlib" + GIT_TAG "v1.3.2" + ) + + CPMAddPackage( + NAME "libpng" + GIT_REPOSITORY "https://github.com/pnggroup/libpng.git" + GIT_TAG "v1.6.55" + OPTIONS + "ZLIB_ROOT ${zlib_SOURCE_DIR}" + "ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR}" + "PNG_TOOLS OFF" + "PNG_TESTS OFF" + ) + + CPMAddPackage( + NAME "glfw" + GIT_REPOSITORY "https://github.com/glfw/glfw.git" + GIT_TAG "3.4" + EXCLUDE_FROM_ALL TRUE + OPTIONS + "GLFW_BUILD_EXAMPLES OFF" + "GLFW_BUILD_TESTS OFF" + "GLFW_BUILD_DOCS OFF" + ) +endif() + +CPMAddPackage( + NAME "openal" + GIT_REPOSITORY "https://github.com/kcat/openal-soft.git" + GIT_TAG "1.25.1" + OPTIONS + "ALSOFT_EXAMPLES OFF" + "ALSOFT_TESTS OFF" + "ALSOFT_UTILS OFF" + "LIBTYPE ${AL_LIBTYPE}" + "ALSOFT_ENABLE_MODULES OFF" + "ALSOFT_STATIC_STDCXX ON" + "ALSOFT_STATIC_LIBGCC ON" +) + +# TODO: Clear this paths with * +file(GLOB SERVER_SOURCES + "project/lib_projects/raknet/jni/RaknetSources/*.cpp" + "src/NinecraftApp.cpp" + "src/Performance.cpp" + "src/SharedConstants.cpp" + + "src/client/IConfigListener.cpp" + "src/client/Minecraft.cpp" + "src/client/OptionStrings.cpp" + "src/client/Option.cpp" + "src/client/Options.cpp" + "src/client/OptionsFile.cpp" + "src/client/ServerProfiler.cpp" + + "src/client/gamemode/CreativeMode.cpp" + "src/client/gamemode/GameMode.cpp" + "src/client/gamemode/SurvivalMode.cpp" + + "src/client/player/LocalPlayer.cpp" + "src/client/player/RemotePlayer.cpp" + "src/client/player/input/KeyboardInput.cpp" + + "src/locale/I18n.cpp" + + "src/main.cpp" + "src/main_dedicated.cpp" + + "src/nbt/Tag.cpp" + + "src/network/ClientSideNetworkHandler.cpp" + "src/network/NetEventCallback.cpp" + "src/network/Packet.cpp" + "src/network/RakNetInstance.cpp" + "src/network/ServerSideNetworkHandler.cpp" + "src/network/command/CommandServer.cpp" + + "src/platform/CThread.cpp" + "src/platform/HttpClient.cpp" + "src/platform/PngLoader.cpp" + "src/platform/time.cpp" + + "src/platform/input/Controller.cpp" + "src/platform/input/Keyboard.cpp" + "src/platform/input/Mouse.cpp" + "src/platform/input/Multitouch.cpp" + + "src/server/ArgumentsSettings.cpp" + "src/server/ServerLevel.cpp" + "src/server/ServerPlayer.cpp" + + "src/util/DataIO.cpp" + "src/util/Mth.cpp" + "src/util/PerfTimer.cpp" + "src/util/StringUtils.cpp" + + "src/world/Direction.cpp" + + "src/world/entity/*.cpp" + + "src/world/entity/ai/control/MoveControl.cpp" + + "src/world/entity/animal/*.cpp" + + "src/world/entity/item/*.cpp" + + "src/world/entity/monster/*.cpp" + + "src/world/entity/player/Inventory.cpp" + "src/world/entity/player/Player.cpp" + + "src/world/entity/projectile/Arrow.cpp" + "src/world/entity/projectile/Throwable.cpp" + + "src/world/food/SimpleFoodData.cpp" + + "src/world/inventory/*.cpp" + "src/world/item/*.cpp" + "src/world/item/crafting/*.cpp" + "src/world/level/*.cpp" + + "src/world/level/biome/Biome.cpp" + "src/world/level/biome/BiomeSource.cpp" + + "src/world/level/chunk/LevelChunk.cpp" + + "src/world/level/dimension/Dimension.cpp" + + "src/world/level/levelgen/*.cpp" + "src/world/level/levelgen/feature/Feature.cpp" + "src/world/level/levelgen/synth/*.cpp" + + "src/world/level/material/Material.cpp" + + "src/world/level/pathfinder/Path.cpp" + + "src/world/level/storage/*.cpp" + "src/world/level/tile/*.cpp" + "src/world/level/tile/entity/*.cpp" + + "src/world/phys/HitResult.cpp" +) + +file(GLOB CLIENT_SOURCES + "src/client/*.cpp" + + "src/client/gamemode/*.cpp" + "src/client/gui/*.cpp" + "src/client/gui/components/*.cpp" + "src/client/gui/screens/*.cpp" + "src/client/gui/screens/crafting/*.cpp" + "src/client/gui/screens/touch/*.cpp" + + "src/client/model/*.cpp" + "src/client/model/geom/*.cpp" + + "src/client/particle/*.cpp" + + "src/client/player/*.cpp" + "src/client/player/input/*.cpp" + "src/client/player/input/touchscreen/*.cpp" + + "src/client/renderer/*.cpp" + "src/client/renderer/culling/*.cpp" + "src/client/renderer/entity/*.cpp" + "src/client/renderer/ptexture/*.cpp" + "src/client/renderer/tileentity/*.cpp" + + "src/client/sound/*.cpp" + + "src/locale/*.cpp" + + "src/nbt/*.cpp" + + "src/network/*.cpp" + "src/network/command/*.cpp" + + "src/platform/*.cpp" + "src/platform/audio/SoundSystemAL.cpp" + "src/platform/input/*.cpp" + + "src/raknet/**.cpp" + + "src/server/**.cpp" + + "src/util/**.cpp" + + "src/world/*.cpp" + "src/world/phys/*.cpp" + "src/world/entity/*.cpp" + "src/world/entity/ai/control/*.cpp" + "src/world/entity/animal/*.cpp" + "src/world/entity/item/*.cpp" + "src/world/entity/monster/*.cpp" + "src/world/entity/player/*.cpp" + "src/world/entity/projectile/*.cpp" + + "src/world/food/*.cpp" + "src/world/inventory/*.cpp" + + "src/world/item/*.cpp" + "src/world/item/crafting/*.cpp" + + "src/world/level/*.cpp" + "src/world/level/biome/*.cpp" + "src/world/level/chunk/*.cpp" + "src/world/level/dimension/*.cpp" + "src/world/level/levelgen/*.cpp" + "src/world/level/levelgen/feature/*.cpp" + "src/world/level/levelgen/synth/*.cpp" + "src/world/level/material/*.cpp" + "src/world/level/pathfinder/*.cpp" + "src/world/level/storage/*.cpp" + "src/world/level/tile/*.cpp" + "src/world/level/tile/entity/*.cpp" + + "src/SharedConstants.cpp" + "src/main.cpp" + "src/NinecraftApp.cpp" + + "src/AppPlatform_glfw.cpp" + "src/main.cpp" +) + +if (${PLATFORM} STREQUAL "Desktop") + list(APPEND CLIENT_SOURCES glad/src/glad.c) +endif() + +# Server +if(UNIX) + add_executable("${PROJECT_NAME}-server" ${SERVER_SOURCES}) + + target_compile_definitions("${PROJECT_NAME}-server" PUBLIC "STANDALONE_SERVER" "SERVER_PROFILER") + + target_include_directories("${PROJECT_NAME}-server" PUBLIC + "${CMAKE_SOURCE_DIR}/src/" + "project/lib_projects/raknet/jni/RaknetSources" + ) + + target_link_libraries("${PROJECT_NAME}-server" ${CMAKE_THREAD_LIBS_INIT}) +endif() + +add_executable(${PROJECT_NAME} ${CLIENT_SOURCES}) + +target_compile_definitions(${PROJECT_NAME} PUBLIC ${PLATFORM_CPP}) + +target_include_directories(${PROJECT_NAME} PUBLIC + "${CMAKE_SOURCE_DIR}/glad/include/" + "${CMAKE_SOURCE_DIR}/src" + "${openal_SOURCE_DIR}/include" + "${glfw_SOURCE_DIR}/include" + "lib/include" +) + +if(${PLATFORM} MATCHES "Web") + set(CMAKE_CXX_STANDARD 11) + # uuuh i hate it + set(EM_FLAGS "-pthread -sUSE_PTHREADS=1 -sUSE_LIBPNG=1 -sSHARED_MEMORY=1") + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EM_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EM_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EM_FLAGS} --preload-file ${CMAKE_SOURCE_DIR}/data@/data") + + target_compile_options(${PROJECT_NAME} PUBLIC + "-Os" + "-Wno-invalid-source-encoding" + "-Wno-narrowing" + "-Wno-deprecated-register" + "-Wno-reserved-user-defined-literal" + ) + + target_link_options(${PROJECT_NAME} PUBLIC + "-Os" + "-sALLOW_MEMORY_GROWTH=1" + "-sFORCE_FILESYSTEM=1" + "-sLEGACY_GL_EMULATION=1" + "-sGL_UNSAFE_OPTS=0" + "-sEMULATE_FUNCTION_POINTER_CASTS=1" + "-sALLOW_TABLE_GROWTH=1" + "-sEXPORTED_RUNTIME_METHODS=['FS','stringToUTF8','UTF8ToString','cwrap','ccall','HEAP8','HEAPU8','HEAP32','HEAPU32']" + "-sEXPORTED_FUNCTIONS=['_main']" + ) + + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + message("DEBUG MODE") + + target_link_options(${PROJECT_NAME} PUBLIC + "-sASSERTIONS=2" + "-sSTACK_OVERFLOW_CHECK=2" + "-sSTACK_SIZE=5242880" + "-sGL_DEBUG=1" + ) + endif() + + target_compile_definitions(${PROJECT_NAME} PUBLIC "__EMSCRIPTEN__" "NO_SOUND" "NO_NETWORK") +endif() + +# Client +target_compile_definitions(${PROJECT_NAME} PUBLIC "OPENGL_ES" "NO_EGL" ${PLATFORM}) +target_link_libraries(${PROJECT_NAME} zlib ${PNG_LIB} OpenAL::OpenAL glfw ${EXTRA_LIBS}) + +if (OpenSSL_FOUND) + target_link_libraries(${PROJECT_NAME} OpenSSL::SSL OpenSSL::Crypto) + target_compile_definitions(${PROJECT_NAME} PUBLIC HTTPCLIENT_USE_OPENSSL) +endif() + +if (NOT UNIX) + add_custom_command( + TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + COMMAND_EXPAND_LISTS + ) +endif() + +if(NOT ${PLATFORM} MATCHES "Web") + add_custom_command( + TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/data" $/data + ) +else() + add_custom_command( + TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/misc/web/index.html" $ + ) +endif() + +message(STATUS "Compiling with the flags:") message(STATUS " PLATFORM=" ${PLATFORM_CPP}) \ No newline at end of file diff --git a/data/lang/en_US.lang b/data/lang/en_US.lang index 4bb15e2..baba952 100755 --- a/data/lang/en_US.lang +++ b/data/lang/en_US.lang @@ -1,992 +1,992 @@ - -language.name=English -language.region=US -language.code=en_US - -gui.done=Done -gui.cancel=Cancel -gui.toMenu=Back to title screen -gui.up=Up -gui.down=Down -gui.yes=Yes -gui.no=No - -menu.singleplayer=Singleplayer -menu.multiplayer=Multiplayer -menu.mods=Texture Packs -menu.options=Options... -menu.quit=Quit Game -menu.returnToMenu=Save and Quit to Title -menu.disconnect=Disconnect -menu.returnToGame=Back to Game -menu.switchingLevel=Switching level -menu.generatingLevel=Generating level -menu.loadingLevel=Loading level -menu.generatingTerrain=Building terrain -menu.simulating=Simulating the world for a bit -menu.respawning=Respawning - -selectWorld.title=Select World -selectWorld.empty=empty -selectWorld.world=World -selectWorld.select=Play Selected World -selectWorld.create=Create New World -selectWorld.createDemo=Play New Demo World -selectWorld.delete=Delete -selectWorld.rename=Rename -selectWorld.deleteQuestion=Are you sure you want to delete this world? -selectWorld.deleteWarning=will be lost forever! (A long time!) -selectWorld.deleteButton=Delete -selectWorld.renameButton=Rename -selectWorld.renameTitle=Rename World -selectWorld.conversion=Must be converted! -selectWorld.newWorld=New World -selectWorld.enterName=World Name -selectWorld.resultFolder=Will be saved in: -selectWorld.enterSeed=Seed for the World Generator -selectWorld.seedInfo=Leave blank for a random seed - -gameMode.survival=Survival Mode -gameMode.creative=Creative Mode -gameMode.hardcore=Hardcore Mode! -gameMode.changed=Your game mode has been changed - -selectWorld.gameMode=Game Mode: -selectWorld.gameMode.survival=Survival -selectWorld.gameMode.survival.line1=Search for resources, crafting, gain -selectWorld.gameMode.survival.line2=levels, health and hunger -selectWorld.gameMode.creative=Creative -selectWorld.gameMode.creative.line1=Unlimited resources, free flying and -selectWorld.gameMode.creative.line2=destroy blocks instantly -selectWorld.gameMode.hardcore=Hardcore -selectWorld.gameMode.hardcore.line1=Same as survival mode, locked at hardest -selectWorld.gameMode.hardcore.line2=difficulty, and one life only -selectWorld.moreWorldOptions=More World Options... -selectWorld.mapFeatures=Generate Structures: -selectWorld.mapFeatures.info=Villages, dungeons etc -selectWorld.mapType=World Type: -selectWorld.mapType.normal=Normal -selectWorld.hardcoreMode=Hardcore: -selectWorld.hardcoreMode.info=World is deleted upon death - -generator.default=Default -generator.flat=Superflat - -selectServer.title=Select Server -selectServer.empty=empty -selectServer.select=Join Server -selectServer.direct=Direct Connect -selectServer.edit=Edit -selectServer.delete=Delete -selectServer.add=Add server -selectServer.defaultName=Minecraft Server -selectServer.deleteQuestion=Are you sure you want to remove this server? -selectServer.deleteWarning=will be lost forever! (A long time!) -selectServer.deleteButton=Delete -selectServer.refresh=Refresh -addServer.title=Edit Server Info -addServer.enterName=Server Name -addServer.enterIp=Server Address -addServer.add=Done - -multiplayer.title=Play Multiplayer -multiplayer.connect=Connect -multiplayer.info1=Minecraft Multiplayer is currently not finished, but there -multiplayer.info2=is some buggy early testing going on. -multiplayer.ipinfo=Enter the IP of a server to connect to it: - -multiplayer.downloadingTerrain=Downloading terrain - -multiplayer.stopSleeping=Leave Bed - -demo.day.1=This demo will last five game days, do your best! -demo.day.2=Day Two -demo.day.3=Day Three -demo.day.4=Day Four -demo.day.5=This is your last day! -demo.day.warning=Your time is almost up! -demo.day.6=You have passed your fifth day, use F2 to save a screenshot of your creation -demo.reminder=The demo time has expired, buy the game to continue or start a new world! -demo.help.movement=Use %1$s, %2$s, %3$s, %4$s and the mouse to move around -demo.help.jump=Jump by pressing %1$s -demo.help.inventory=Use %1$s to open your inventory - -connect.connecting=Connecting to the server... -connect.authorizing=Logging in... -connect.failed=Failed to connect to the server - -disconnect.genericReason=%s -disconnect.disconnected=Disconnected by Server -disconnect.lost=Connection Lost -disconnect.kicked=Was kicked from the game -disconnect.timeout=Timed out -disconnect.closed=Connection closed -disconnect.loginFailed=Failed to login -disconnect.loginFailedInfo=Failed to login: %s -disconnect.quitting=Quitting -disconnect.endOfStream=End of stream -disconnect.overflow=Buffer overflow - -options.off=OFF -options.on=ON -options.title=Options -options.controls=Controls... -options.video=Video Settings... -options.language=Language... -options.languageWarning=Language translations may not be 100% accurate -options.videoTitle=Video Settings -options.music=Music -options.sound=Sound -options.invertMouse=Invert Mouse -options.fov=FOV -options.fov.min=Normal -options.fov.max=Quake Pro -options.gamma=Brightness -options.gamma.min=Moody -options.gamma.max=Bright -options.group.mojang=Login -options.group.general=General -options.group.game=Game -options.group.controls=Controls -options.group.graphics=Graphics -options.group.tweaks=Tweaks -options.allowSprint=Allow sprint -options.barOnTop=HUD above inventory -options.rpiCursor=Show Raspberry PI cursor -options.autoJump=Auto Jump -options.thirdperson=Third Person -options.servervisible=Server Visible -options.sensitivity=Sensitivity -options.sensitivity.min=*yawn* -options.sensitivity.max=HYPERSPEED!!! -options.renderDistance=Render Distance -options.renderDistance.tiny=Tiny -options.renderDistance.short=Short -options.renderDistance.normal=Normal -options.renderDistance.far=Far -options.viewBobbing=View Bobbing -options.ao=Smooth Lighting -options.anaglyph=3D Anaglyph -options.framerateLimit=Performance -options.limitFramerate=Limit Framerate -options.vsync=VSync -options.difficulty=Difficulty -options.difficulty.peaceful=Peaceful -options.difficulty.easy=Easy -options.difficulty.normal=Normal -options.difficulty.hard=Hard -options.difficulty.hardcore=Hardcore -options.graphics=Graphics -options.graphics.fancy=Fancy -options.graphics.fast=Fast -options.guiScale=GUI Scale -options.guiScale.auto=Auto -options.guiScale.small=Small -options.guiScale.medium=Medium -options.guiScale.large=Large -options.guiScale.larger=Larger -options.guiScale.largest=Largest -options.advancedOpengl=Advanced OpenGL -options.renderClouds=Clouds -options.farWarning1=A 64 bit Java installation is recommended -options.farWarning2=for 'Far' render distance (you have 32 bit) -options.particles=Particles -options.particles.all=All -options.particles.decreased=Decreased -options.particles.minimal=Minimal -options.username=Username -options.smoothCamera=Smooth camera -options.destroyVibration=Destroy vibration -options.isLeftHanded=Left handed -options.useTouchscreen=Use touchscreen -options.fancyGraphics=Fancy graphics -options.renderDebug=Debug render -options.anaglyph3d=3D anaglyph - - -performance.max=Max FPS -performance.balanced=Balanced -performance.powersaver=Power saver - -controls.title=Controls - -options.key.forward=Forward -options.key.left=Left -options.key.back=Back -options.key.right=Right -options.key.jump=Jump -options.key.inventory=Inventory -options.key.drop=Drop -options.key.chat=Chat -options.key.fog=Toggle Fog -options.key.sneak=Sneak -options.key.playerlist=List Players -options.key.attack=Attack -options.key.use=Use Item -options.key.pickItem=Pick Block -options.key.mouseButton=Button %1$s - -texturePack.openFolder=Open texture pack folder -texturePack.title=Select Texture Pack -texturePack.folderInfo=(Place texture pack files here) - -tile.stone.name=Stone -tile.grass.name=Grass Block -tile.dirt.name=Dirt -tile.stonebrick.name=Cobblestone -tile.wood.name=Wooden Planks -tile.sapling.name=Sapling -tile.deadbush.name=Dead Bush -tile.bedrock.name=Bedrock -tile.water.name=Water -tile.lava.name=Lava -tile.sand.name=Sand -tile.sandStone.name=Sandstone -tile.gravel.name=Gravel -tile.oreGold.name=Gold Ore -tile.oreIron.name=Iron Ore -tile.oreCoal.name=Coal Ore -tile.log.name=Wood -tile.leaves.name=Leaves -tile.tallgrass.name=Grass -tile.tallgrass.shrub.name=Shrub -tile.tallgrass.grass.name=Grass -tile.tallgrass.fern.name=Fern -tile.sponge.name=Sponge -tile.glass.name=Glass -tile.thinGlass.name=Glass Pane -tile.cloth.name=Wool -tile.flower.name=Flower -tile.rose.name=Rose -tile.mushroom.name=Mushroom -tile.blockGold.name=Block of Gold -tile.blockIron.name=Block of Iron -tile.stoneSlab.stone.name=Stone Slab -tile.stoneSlab.sand.name=Sandstone Slab -tile.stoneSlab.wood.name=Wooden Slab -tile.stoneSlab.cobble.name=Cobblestone Slab -tile.stoneSlab.brick.name=Bricks Slab -tile.stoneSlab.smoothStoneBrick.name=Stone Bricks Slab -tile.brick.name=Bricks -tile.tnt.name=TNT -tile.bookshelf.name=Bookshelf -tile.stoneMoss.name=Moss Stone -tile.obsidian.name=Obsidian -tile.torch.name=Torch -tile.fire.name=Fire -tile.mobSpawner.name=Monster Spawner -tile.stairsWood.name=Wooden Stairs -tile.chest.name=Chest -tile.redstoneDust.name=Redstone Dust -tile.oreDiamond.name=Diamond Ore -tile.blockDiamond.name=Block of Diamond -tile.workbench.name=Crafting Table -tile.crops.name=Crops -tile.farmland.name=Farmland -tile.furnace.name=Furnace -tile.sign.name=Sign -tile.doorWood.name=Wooden Door -tile.ladder.name=Ladder -tile.rail.name=Rail -tile.goldenRail.name=Powered Rail -tile.detectorRail.name=Detector Rail -tile.stairsStone.name=Stone Stairs -tile.lever.name=Lever -tile.pressurePlate.name=Pressure Plate -tile.doorIron.name=Iron Door -tile.oreRedstone.name=Redstone Ore -tile.notGate.name=Redstone Torch -tile.button.name=Button -tile.snow.name=Snow -tile.ice.name=Ice -tile.cactus.name=Cactus -tile.clay.name=Clay -tile.reeds.name=Sugar cane -tile.jukebox.name=Jukebox -tile.fence.name=Fence -tile.fenceGate.name=Fence Gate -tile.pumpkin.name=Pumpkin -tile.litpumpkin.name=Jack 'o' Lantern -tile.hellrock.name=Netherrack -tile.hellsand.name=Soul Sand -tile.lightgem.name=Glowstone -tile.portal.name=Portal -tile.cloth.black.name=Black Wool -tile.cloth.red.name=Red Wool -tile.cloth.green.name=Green Wool -tile.cloth.brown.name=Brown Wool -tile.cloth.blue.name=Blue Wool -tile.cloth.purple.name=Purple Wool -tile.cloth.cyan.name=Cyan Wool -tile.cloth.silver.name=Light Gray Wool -tile.cloth.gray.name=Gray Wool -tile.cloth.pink.name=Pink Wool -tile.cloth.lime.name=Lime Wool -tile.cloth.yellow.name=Yellow Wool -tile.cloth.lightBlue.name=Blue Light Wool -tile.cloth.magenta.name=Magenta Wool -tile.cloth.orange.name=Orange Wool -tile.cloth.white.name=Wool -tile.oreLapis.name=Lapis Lazuli Ore -tile.blockLapis.name=Lapis Lazuli Block -tile.dispenser.name=Dispenser -tile.musicBlock.name=Note Block -tile.cake.name=Cake -tile.bed.name=Bed -tile.bed.occupied=This bed is occupied -tile.bed.noSleep=You can only sleep at night -tile.bed.notSafe=You may not rest now, there are monsters nearby -tile.bed.notValid=Your home bed was missing or obstructed -tile.lockedchest.name=Locked chest -tile.trapdoor.name=Trapdoor -tile.web.name=Cobweb -tile.stonebricksmooth.name=Stone Bricks -tile.pistonBase.name=Piston -tile.pistonStickyBase.name=Sticky Piston -tile.fenceIron.name=Iron Bars -tile.melon.name=Melon -tile.stairsBrick.name=Brick Stairs -tile.stairsStoneBrickSmooth.name=Stone Brick Stairs -tile.vine.name=Vines -tile.netherBrick.name=Nether Brick -tile.netherFence.name=Nether Brick Fence -tile.stairsNetherBrick.name=Nether Brick Stairs -tile.netherStalk.name=Nether Wart -tile.cauldron.name=Cauldron -tile.enchantmentTable.name=Enchantment Table -tile.whiteStone.name=End Stone -tile.mycel.name=Mycelium -tile.waterlily.name=Lily Pad -tile.dragonEgg.name=Dragon Egg -tile.redstoneLight.name=Redstone Lamp -tile.glowingobsidian.name=Glowing Obsidian -tile.netherreactor.name=Nether Reactor Core -tile.quartzBlock.name=Block of Quartz -tile.stairsQuartz.name=Quartz Stairs -tile.stairsSandStone.name=Sand Stone Stairs -tile.stonecutter.name=Stonecutter - -item.shovelIron.name=Iron Shovel -item.pickaxeIron.name=Iron Pickaxe -item.hatchetIron.name=Iron Axe -item.flintAndSteel.name=Flint and Steel -item.apple.name=Apple -item.cookie.name=Cookie -item.bow.name=Bow -item.arrow.name=Arrow -item.coal.name=Coal -item.charcoal.name=Charcoal -item.emerald.name=Diamond -item.ingotIron.name=Iron Ingot -item.ingotGold.name=Gold Ingot -item.swordIron.name=Iron Sword -item.swordWood.name=Wooden Sword -item.shovelWood.name=Wooden Shovel -item.pickaxeWood.name=Wooden Pickaxe -item.hatchetWood.name=Wooden Axe -item.swordStone.name=Stone Sword -item.shovelStone.name=Stone Shovel -item.pickaxeStone.name=Stone Pickaxe -item.hatchetStone.name=Stone Axe -item.swordDiamond.name=Diamond Sword -item.shovelDiamond.name=Diamond Shovel -item.pickaxeDiamond.name=Diamond Pickaxe -item.hatchetDiamond.name=Diamond Axe -item.stick.name=Stick -item.bowl.name=Bowl -item.mushroomStew.name=Mushroom Stew -item.swordGold.name=Golden Sword -item.shovelGold.name=Golden Shovel -item.pickaxeGold.name=Golden Pickaxe -item.hatchetGold.name=Golden Axe -item.string.name=String -item.feather.name=Feather -item.sulphur.name=Gunpowder -item.hoeWood.name=Wooden Hoe -item.hoeStone.name=Stone Hoe -item.hoeIron.name=Iron Hoe -item.hoeDiamond.name=Diamond Hoe -item.hoeGold.name=Golden Hoe -item.seeds.name=Seeds -item.seeds_pumpkin.name=Pumpkin Seeds -item.seeds_melon.name=Melon Seeds -item.melon.name=Melon -item.wheat.name=Wheat -item.bread.name=Bread -item.helmetCloth.name=Leather Cap -item.chestplateCloth.name=Leather Tunic -item.leggingsCloth.name=Leather Pants -item.bootsCloth.name=Leather Boots -item.helmetChain.name=Chain Helmet -item.chestplateChain.name=Chain Chestplate -item.leggingsChain.name=Chain Leggings -item.bootsChain.name=Chain Boots -item.helmetIron.name=Iron Helmet -item.chestplateIron.name=Iron Chestplate -item.leggingsIron.name=Iron Leggings -item.bootsIron.name=Iron Boots -item.helmetDiamond.name=Diamond Helmet -item.chestplateDiamond.name=Diamond Chestplate -item.leggingsDiamond.name=Diamond Leggings -item.bootsDiamond.name=Diamond Boots -item.helmetGold.name=Golden Helmet -item.chestplateGold.name=Golden Chestplate -item.leggingsGold.name=Golden Leggings -item.bootsGold.name=Golden Boots -item.flint.name=Flint -item.porkchopRaw.name=Raw Porkchop -item.porkchopCooked.name=Cooked Porkchop -item.chickenRaw.name=Raw Chicken -item.chickenCooked.name=Cooked Chicken -item.beefRaw.name=Raw Beef -item.beefCooked.name=Steak -item.painting.name=Painting -item.appleGold.name=Golden Apple -item.sign.name=Sign -item.doorWood.name=Wooden Door -item.bucket.name=Bucket -item.bucketWater.name=Water Bucket -item.bucketLava.name=Lava Bucket -item.minecart.name=Minecart -item.saddle.name=Saddle -item.doorIron.name=Iron Door -item.redstone.name=Redstone -item.snowball.name=Snowball -item.boat.name=Boat -item.leather.name=Leather -item.milk.name=Milk -item.brick.name=Brick -item.clay.name=Clay -item.reeds.name=Sugar Canes -item.paper.name=Paper -item.book.name=Book -item.slimeball.name=Slimeball -item.minecartChest.name=Minecart with Chest -item.minecartFurnace.name=Minecart with Furnace -item.egg.name=Egg -item.compass.name=Compass -item.fishingRod.name=Fishing Rod -item.clock.name=Clock -item.yellowDust.name=Glowstone Dust -item.fishRaw.name=Raw Fish -item.fishCooked.name=Cooked Fish -item.record.name=Music Disc -item.bone.name=Bone -item.dyePowder.black.name=Ink Sac -item.dyePowder.red.name=Rose Red -item.dyePowder.green.name=Cactus Green -item.dyePowder.brown.name=Cocoa Beans -item.dyePowder.blue.name=Lapis Lazuli -item.dyePowder.purple.name=Purple Dye -item.dyePowder.cyan.name=Cyan Dye -item.dyePowder.silver.name=Light Gray Dye -item.dyePowder.gray.name=Gray Dye -item.dyePowder.pink.name=Pink Dye -item.dyePowder.lime.name=Lime Dye -item.dyePowder.yellow.name=Dandelion Yellow -item.dyePowder.lightBlue.name=Light Blue Dye -item.dyePowder.magenta.name=Magenta Dye -item.dyePowder.orange.name=Orange Dye -item.dyePowder.white.name=Bone Meal -item.sugar.name=Sugar -item.cake.name=Cake -item.bed.name=Bed -item.diode.name=Redstone Repeater -item.map.name=Map -item.leaves.name=Leaves -item.shears.name=Shears -item.rottenFlesh.name=Rotten Flesh -item.enderPearl.name=Ender Pearl -item.blazeRod.name=Blaze Rod -item.ghastTear.name=Ghast Tear -item.netherStalkSeeds.name=Nether Wart -item.potion.name=Potion -item.emptyPotion.name=Water Bottle -item.goldNugget.name=Gold Nugget -item.glassBottle.name=Glass Bottle -item.spiderEye.name=Spider Eye -item.fermentedSpiderEye.name=Fermented Spider Eye -item.blazePowder.name=Blaze Powder -item.magmaCream.name=Magma Cream -item.cauldron.name=Cauldron -item.brewingStand.name=Brewing Stand -item.eyeOfEnder.name=Eye of Ender -item.speckledMelon.name=Glistering Melon -item.monsterPlacer.name=Spawn -item.expBottle.name=Bottle o' Enchanting -item.fireball.name=Fire Charge -item.netherbrickItem.name=Nether Brick -item.netherquartz.name=Nether Quartz - -container.inventory=Inventory -container.crafting=Crafting -container.dispenser=Dispenser -container.furnace=Furnace -container.enchant=Enchant -container.creative=Item Selection -container.brewing=Brewing Stand -container.chest=Chest -container.minecart=Minecart - -entity.Item.name=Item -entity.XPOrb.name=Experience Orb - -entity.Arrow.name=Arrow -entity.Snowball.name=Snowball -entity.Painting.name=Painting - -entity.Mob.name=Mob -entity.Monster.name=Monster - -entity.Creeper.name=Creeper -entity.Skeleton.name=Skeleton -entity.Spider.name=Spider -entity.Giant.name=Giant -entity.Zombie.name=Zombie -entity.Slime.name=Slime -entity.Ghast.name=Ghast -entity.PigZombie.name=Zombie Pigman -entity.Enderman.name=Enderman -entity.Silverfish.name=Silverfish -entity.CaveSpider.name=Cave Spider -entity.Blaze.name=Blaze -entity.LavaSlime.name=Magma Cube -entity.MushroomCow.name=Mooshroom -entity.Villager.name=Villager -entity.VillagerGolem.name=Iron Golem -entity.SnowMan.name=Snow Golem -entity.EnderDragon.name=Ender Dragon - -entity.Pig.name=Pig -entity.Sheep.name=Sheep -entity.Cow.name=Cow -entity.Chicken.name=Chicken -entity.Squid.name=Squid -entity.Wolf.name=Wolf -entity.Ozelot.name=Ocelot -entity.Cat.name=Cat - -entity.PrimedTnt.name=Block of TNT -entity.FallingSand.name=Falling Block - -entity.Minecart.name=Minecart -entity.Boat.name=Boat - -entity.Arrow.name=arrow -entity.generic.name=unknown - -death.inFire=%1$s went up in flames -death.onFire=%1$s burned to death -death.lava=%1$s tried to swim in lava -death.inWall=%1$s suffocated in a wall -death.drown=%1$s drowned -death.starve=%1$s starved to death -death.cactus=%1$s was pricked to death -death.fall=%1$s hit the ground too hard -death.outOfWorld=%1$s fell out of the world -death.generic=%1$s died -death.explosion=%1$s blew up -death.magic=%1$s was killed by magic -death.mob=%1$s was slain by %2$s -death.player=%1$s was slain by %2$s -death.arrow=%1$s was shot by %2$s -death.fireball=%1$s was fireballed by %2$s -death.thrown=%1$s was pummeled by %2$s -death.indirectMagic=%1$s was killed by %2$s - -deathScreen.respawn=Respawn -deathScreen.deleteWorld=Delete world -deathScreen.titleScreen=Title screen -deathScreen.score=Score -deathScreen.title.hardcore=Game over! -deathScreen.hardcoreInfo=You cannot respawn in hardcore mode! -deathScreen.title=You died! - -potion.empty=No Effects -potion.moveSpeed=Speed -potion.moveSlowdown=Slowness -potion.digSpeed=Haste -potion.digSlowDown=Mining Fatigue -potion.damageBoost=Strength -potion.weakness=Weakness -potion.heal=Instant Health -potion.harm=Instant Damage -potion.jump=Jump Boost -potion.confusion=Nausea -potion.regeneration=Regeneration -potion.resistance=Resistance -potion.fireResistance=Fire Resistance -potion.waterBreathing=Water Breathing -potion.invisibility=Invisibility -potion.blindness=Blindness -potion.nightVision=Night Vision -potion.hunger=Hunger -potion.poison=Poison - -potion.moveSpeed.postfix=Potion of Swiftness -potion.moveSlowdown.postfix=Potion of Slowness -potion.digSpeed.postfix=Potion of Haste -potion.digSlowDown.postfix=Potion of Dullness -potion.damageBoost.postfix=Potion of Strength -potion.weakness.postfix=Potion of Weakness -potion.heal.postfix=Potion of Healing -potion.harm.postfix=Potion of Harming -potion.jump.postfix=Potion of Leaping -potion.confusion.postfix=Potion of Nausea -potion.regeneration.postfix=Potion of Regeneration -potion.resistance.postfix=Potion of Resistance -potion.fireResistance.postfix=Potion of Fire Resistance -potion.waterBreathing.postfix=Potion of Water Breathing -potion.invisibility.postfix=Potion of Invisibility -potion.blindness.postfix=Potion of Blindness -potion.nightVision.postfix=Potion of Night Vision -potion.hunger.postfix=Potion of Hunger -potion.poison.postfix=Potion of Poison - -potion.potency.0= -potion.potency.1=II -potion.potency.2=III -potion.potency.3=IV - -potion.prefix.grenade=Splash -potion.prefix.mundane=Mundane -potion.prefix.uninteresting=Uninteresting -potion.prefix.bland=Bland -potion.prefix.clear=Clear -potion.prefix.milky=Milky -potion.prefix.diffuse=Diffuse -potion.prefix.artless=Artless -potion.prefix.thin=Thin -potion.prefix.awkward=Awkward -potion.prefix.flat=Flat -potion.prefix.bulky=Bulky -potion.prefix.bungling=Bungling -potion.prefix.buttered=Buttered -potion.prefix.smooth=Smooth -potion.prefix.suave=Suave -potion.prefix.debonair=Debonair -potion.prefix.thick=Thick -potion.prefix.elegant=Elegant -potion.prefix.fancy=Fancy -potion.prefix.charming=Charming -potion.prefix.dashing=Dashing -potion.prefix.refined=Refined -potion.prefix.cordial=Cordial -potion.prefix.sparkling=Sparkling -potion.prefix.potent=Potent -potion.prefix.foul=Foul -potion.prefix.odorless=Odorless -potion.prefix.rank=Rank -potion.prefix.harsh=Harsh -potion.prefix.acrid=Acrid -potion.prefix.gross=Gross -potion.prefix.stinky=Stinky - -enchantment.damage.all=Sharpness -enchantment.damage.undead=Smite -enchantment.damage.arthropods=Bane of Arthropods -enchantment.knockback=Knockback -enchantment.fire=Fire Aspect -enchantment.protect.all=Protection -enchantment.protect.fire=Fire Protection -enchantment.protect.fall=Feather Falling -enchantment.protect.explosion=Blast Protection -enchantment.protect.projectile=Projectile Protection -enchantment.oxygen=Respiration -enchantment.waterWorker=Aqua Affinity -enchantment.digging=Efficiency -enchantment.untouching=Silk Touch -enchantment.durability=Unbreaking -enchantment.lootBonus=Looting -enchantment.lootBonusDigger=Fortune -enchantment.arrowDamage=Power -enchantment.arrowFire=Flame -enchantment.arrowKnockback=Punch -enchantment.arrowInfinite=Infinity - -enchantment.level.1=I -enchantment.level.2=II -enchantment.level.3=III -enchantment.level.4=IV -enchantment.level.5=V -enchantment.level.6=VI -enchantment.level.7=VII -enchantment.level.8=VIII -enchantment.level.9=IX -enchantment.level.10=X - -gui.achievements=Achievements -gui.stats=Statistics - -stat.generalButton=General -stat.blocksButton=Blocks -stat.itemsButton=Items - -stat.used=Times Used -stat.mined=Times Mined -stat.depleted=Times Depleted -stat.crafted=Times Crafted - -stat.startGame=Times played -stat.createWorld=Worlds played -stat.loadWorld=Saves loaded -stat.joinMultiplayer=Multiplayer joins -stat.leaveGame=Games quit - -stat.playOneMinute=Minutes Played - -stat.walkOneCm=Distance Walked -stat.fallOneCm=Distance Fallen -stat.swimOneCm=Distance Swum -stat.flyOneCm=Distance Flown -stat.climbOneCm=Distance Climbed -stat.diveOneCm=Distance Dove -stat.minecartOneCm=Distance by Minecart -stat.boatOneCm=Distance by Boat -stat.pigOneCm=Distance by Pig -stat.jump=Jumps -stat.drop=Items Dropped - -stat.damageDealt=Damage Dealt -stat.damageTaken=Damage Taken -stat.deaths=Number of Deaths -stat.mobKills=Mob Kills -stat.playerKills=Player Kills -stat.fishCaught=Fish Caught - -stat.mineBlock=%1$s Mined -stat.craftItem=%1$s Crafted -stat.useItem=%1$s Used -stat.breakItem=%1$s Depleted - -achievement.get=Achievement get! - -achievement.taken=Taken! - -achievement.requires=Requires '%1$s' -achievement.openInventory=Taking Inventory -achievement.openInventory.desc=Press '%1$s' to open your inventory. -achievement.mineWood=Getting Wood -achievement.mineWood.desc=Attack a tree until a block of wood pops out -achievement.buildWorkBench=Benchmarking -achievement.buildWorkBench.desc=Craft a workbench with four blocks of planks -achievement.buildPickaxe=Time to Mine! -achievement.buildPickaxe.desc=Use planks and sticks to make a pickaxe -achievement.buildFurnace=Hot Topic -achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks -achievement.acquireIron=Acquire Hardware -achievement.acquireIron.desc=Smelt an iron ingot -achievement.buildHoe=Time to Farm! -achievement.buildHoe.desc=Use planks and sticks to make a hoe -achievement.makeBread=Bake Bread -achievement.makeBread.desc=Turn wheat into bread -achievement.bakeCake=The Lie -achievement.bakeCake.desc=Wheat, sugar, milk and eggs! -achievement.buildBetterPickaxe=Getting an Upgrade -achievement.buildBetterPickaxe.desc=Construct a better pickaxe -achievement.cookFish=Delicious Fish -achievement.cookFish.desc=Catch and cook fish! -achievement.onARail=On A Rail -achievement.onARail.desc=Travel by minecart at least 1 km from where you started -achievement.buildSword=Time to Strike! -achievement.buildSword.desc=Use planks and sticks to make a sword -achievement.killEnemy=Monster Hunter -achievement.killEnemy.desc=Attack and destroy a monster -achievement.killCow=Cow Tipper -achievement.killCow.desc=Harvest some leather -achievement.flyPig=When Pigs Fly -achievement.flyPig.desc=Fly a pig off a cliff -achievement.snipeSkeleton=Sniper Duel -achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters -achievement.diamonds=DIAMONDS! -achievement.diamonds.desc=Acquire diamonds with your iron tools -achievement.portal=We Need to Go Deeper -achievement.portal.desc=Build a portal to the Nether -achievement.ghast=Return to Sender -achievement.ghast.desc=Destroy a Ghast with a fireball -achievement.blazeRod=Into Fire -achievement.blazeRod.desc=Relieve a Blaze of its rod -achievement.potion=Local Brewery -achievement.potion.desc=Brew a potion -achievement.theEnd=The End? -achievement.theEnd.desc=Locate the End -achievement.theEnd2=The End. -achievement.theEnd2.desc=Defeat the Ender Dragon -achievement.enchantments=Enchanter -achievement.enchantments.desc=Use a book, obsidian and diamonds to construct an enchantment table -achievement.overkill=Overkill -achievement.overkill.desc=Deal eight hearts of damage in a single hit -achievement.bookcase=Librarian -achievement.bookcase.desc=Build some bookshelves to improve your enchantment table - -desc.sword=Deals more damage than by hand. -desc.shovel=Used to dig dirt, grass, sand, gravel and snow faster than by hand. -desc.pickaxe=Required to mine stone-related blocks and ore. -desc.hatchet=Used to chop wood-related blocks faster than by hand. -desc.hoe=Used to till dirt and grass blocks to prepare for crops. -desc.doorwood=Wooden doors are activated by using, hitting them or with Redstone. -desc.dooriron=Iron doors can only be opened by Redstone, buttons or switches. -desc.helmet=Gives the user 1.5 Armor when worn. -desc.chestplate=Gives the user 4 Armor when worn. -desc.leggings=Gives the user 3 Armor when worn. -desc.boots=Gives the user 1.5 Armor when worn. -desc.ingot=A shiny ingot which can be used to craft tools made from this material. -desc.block=Used as an expensive building block or compact storage of the ore. -desc.pressureplate=Used to send an electrical charge when stepped on by a player, an animal, or a monster. Wooden Pressure Plates can also be activated by dropping something on them. -desc.stairs=Used for compact staircases. -desc.stairsstonebricksmooth=Used for compact staircases. -desc.stairsnetherbrick=Used for compact staircases. -desc.stairssandstone=Used for compact staircases. -desc.stairsquartzblock=Used for compact staircases. -desc.slab=Used for making long staircases. -desc.halfslab=Used for compact staircases. -desc.torch=Used to create light. Torches also melt snow and ice. -desc.wood=Used as a building material and can be crafted into many things. Can be crafted from any wood. -desc.sandstone=Used as a building material. -desc.sandstone.addon=Is not influenced by gravity like normal Sand. -desc.structblock=Used as a building material. -desc.stick=Used to craft torches, arrows, signs, ladders, fences and as handles for tools and weapons. -desc.bed=Used to sleep until dawn. Changes your spawn point to the bed's position. -desc.craftingtable=Allows you to craft a more varied selection of items than the normal crafting. -desc.furnace=Allows you to smelt ore, create charcoal and glass, and cook fish and porkchops. -desc.chest=Stores blocks and items inside. -desc.fence=Used as a barrier that cannot be jumped over. -desc.ladder=Used to climb vertically. -desc.trapdoor=Work like normal doors, but are a one by one block and lay flat on the ground. -desc.sign=Shows text entered by you or other players. -desc.glowstone=Used to create brighter light than torches. Melts snow/ice and can be used underwater. -desc.tnt=Used to cause explosions. Activated after placing by hitting, or with an electrical charge. -desc.bowl=Used to hold mushroom stew. You keep the bowl when the stew has been eaten. -desc.bucket=Used to hold and transport water, lava and milk. -desc.flintandsteel=Used to detonate TNT. -desc.fishingrod=Used to catch fish. -desc.clock=Displays positions of the Sun and Moon. -desc.compass=Points to your start point. -desc.map=Will create an image of an area explored while held. This can be used for path-finding. -desc.bow=Allows for ranged attacks by using arrows. -desc.arrow=Used as ammunition for bows. -desc.mushroomstew=Restores 5 hearts. -desc.cookie=Restores 0.5 Hearts. Can be stacked up to 8 times. -desc.cake=Restores 1.5 Hearts. Can be used 6 times. -desc.sugar=Used in the cake recipe. -desc.bread=Restores 2.5 Hearts. -desc.goldenapple=Restores 10 Hearts. -desc.lever=Used to send an electrical charge by being turned on or off. Stays in the on or off state until pressed again. -desc.redstonetorch=Constantly sends an electrical charge, or can be used as a receiver/transmitter when connected to the side of a block. -Can also be used for low-level lighting. -desc.redstonerepeater=Used in Redstone circuits as repeater, a delayer, and/or a diode -desc.button=Used to send an electrical charge by being pressed. Stays activated for approximately a second before shutting off again. -desc.dispenser=Used to hold and shoot out items in a random order when given a Redstone charge. -desc.noteblock=Plays a note when triggered. Hit it to change the pitch of the note. Placing this on top of different blocks will change the type of instrument. -desc.rail=Used to guide minecarts. -desc.poweredrail=When powered, accelerates minecarts that pass over it. When unpowered, causes minecarts to stop on it. -desc.detectorrail=Functions like a Pressure Plate (sends a Redstone signal when powered) but can only be activated by a minecart. -desc.minecart=Used to transport you, an animal, or a monster along rails. -desc.minecartwithchest=Used to transport goods along rails. -desc.minecartwithfurnace=Will move along rails and can push other minecarts when coal is put in it. -desc.boat=Used to travel in water more quickly than swimming. -desc.wool=Collected from sheep, and can be colored with dyes. -desc.woolstring=Used as a building material and can be colored with dyes. Can be easily obtained from Sheep. -desc.dyepowder.black=Used as a dye to create black wool. -desc.dyepowder.green=Used as a dye to create green wool. -desc.dyepowder.brown=Used as a dye to create brown wool. -desc.dyepowder.silver=Used as a dye to create silver wool. -desc.dyepowder.yellow=Used as a dye to create yellow wool. -desc.dyepowder.red=Used as a dye to create red wool. -desc.dyepowder.white=Used to instantly grow crops and flowers. Can be used in dye recipes. -desc.dyepowder.pink=Used as a dye to create pink wool. -desc.dyepowder.orange=Used as a dye to create orange wool. -desc.dyepowder.lime=Used as a dye to create lime wool. -desc.dyepowder.gray=Used as a dye to create gray wool. -desc.dyepowder.lightgray=Used as a dye to create light gray wool. -(Note: light gray dye can also be made by combining gray dye with bone meal, letting you make four light gray dyes from every ink sac instead of three.) -desc.dyepowder.lightblue=Used as a dye to create light blue wool. -desc.dyepowder.cyan=Used as a dye to create cyan wool. -desc.dyepowder.purple=Used as a dye to create purple wool. -desc.dyepowder.magenta=Used as a dye to create magenta wool. -desc.dyepowder.blue=Used as dye to create Blue Wool. -desc.jukebox=Plays Music Discs. -desc.diamonds=Use these to create very strong tools, weapons or armor -desc.emerald=Use these to create very strong tools, weapons or armor -desc.jackolantern=Used to create brighter light than torches. Melts snow/ice and can be used underwater. -desc.paper=Used to create books and maps. -desc.book=Used to create a bookshelf. -desc.bookshelf=Used as decoration. -desc.picture=Used as decoration. -desc.ore_gold=Can be mined with an iron pickaxe or better, then smelted in a furnace to produce gold ingots. -desc.ore_iron=Can be mined with a stone pickaxe or better, then smelted in a furnace to produce iron ingots. -desc.ore_coal=Can be mined with a pickaxe to collect coal. -desc.ore_lapis=Can be mined with a stone pickaxe or better to collect lapis lazuli. -desc.ore_diamond=Can be mined with an iron pickaxe or better to collect diamonds. -desc.ore_redstone=Can be mined with an iron pickaxe or better to collect redstone dust. -desc.stone=Can be mined with a pickaxe to collect cobblestone. -desc.dirt=Collected using a shovel. Can be used for construction. -desc.sapling=Can be planted and it will eventually grow into a tree. -desc.bedrock=This cannot be broken. -desc.lava=Sets fire to anything that touches it. Can be collected in a bucket. -desc.sand=Collected using a shovel. Can be smelted into glass using the furnace. Is affected by gravity if there is no other tile underneath it. -desc.gravel=Collected using a shovel. Sometimes produces flint when dug up. Is affected by gravity if there is no other tile underneath it. -desc.log=Chopped using an axe, and can be crafted into planks or used as a fuel. -desc.glass=Created in a furnace by smelting sand. Will break if you try to mine it. -desc.thinglass=Will break if you try to mine it. -desc.stone_brick=Mined from stone using a pickaxe. Can be used to construct a furnace or stone tools. -desc.brick=Baked from clay in a furnace. -desc.clay=Can be baked into bricks in a furnace. -desc.snow=A compact way to store snowballs. -desc.top_snow=Can be dug with a shovel to create snowballs. -desc.tall_grass=Sometimes produces seeds when broken. -desc.flower=Can be crafted into a dye. -desc.mushroom=Can be crafted with a bowl to make stew. -desc.obsidian=Can only be mined with a diamond pickaxe. Is produced by the meeting of water and still lava, and is used to build a portal. -desc.mob_spawner=Spawns monsters into the world. -desc.redstone_dust=Is placed on the ground to carry an electrical charge. -desc.crops=When fully grown, crops can be harvested to collect wheat. -desc.farmland=Ground that has been prepared ready to plant seeds. -desc.cactus=Can be crafted to create a dye. -desc.reeds=Can be crafted to create sugar. -desc.pumpkin=Can be worn as a helmet or crafted with a torch to create a Jack-O-Lantern. -desc.hell_rock=Burns forever if set alight. -desc.hell_sand=Slows the movement of anything walking over it. -desc.portal=Standing in the portal allows you to pass between the overworld and the nether. -desc.apple=Restores health, and can be crafted into a golden apple. -desc.melon=Can be broken into melon slices. -desc.coal=Used as a fuel in a furnace, or crafted to make a torch. -desc.string=Collected by killing a spider, and can be crafted into a bow. -desc.feather=Collected by killing a chicken, and can be crafted into an arrow. -desc.sulphur=Collected by killing a creeper, and can be crafted into TNT. -desc.shears=Used to obtain wool from sheep and to harvest placeable Leaf blocks. -desc.seeds=Can be planted in farmland to grow crops. -desc.wheat=Harvested from crops, and can be used to craft food items. -desc.flint=Collected by digging gravel, and can be used to craft a flint and steel. -desc.porkchopraw=Collected by killing a pig, and can be cooked in a furnace. Restores health. -desc.porkchopcooked=Created by cooking a porkchop in a furnace. Restores health. -desc.beefraw=Collected by killing a cow, and can be cooked in a furnace. Restores health. -desc.beefcooked=Created by cooking a beef in a furnace. Restores health. -desc.chickenraw=Collected by killing a chicken, and can be cooked in a furnace. Restores health. -desc.chickencooked=Created by cooking a chicken in a furnace. Restores health. -desc.saddle=When used on a pig it allows you to ride the pig. -desc.snowball=Collected by digging snow, and can be thrown. -desc.leather=Collected by killing a cow, and can be crafted into armor. -desc.slimeball=Collected by killing a slime. -desc.egg=Dropped randomly by chickens, and can be crafted into food items. -desc.yellow_dust=Collected by mining Glowstone, and can be crafted to make Glowstone blocks again. -desc.fish_raw=Can be eaten to restore health, or cooked in a furnace. -desc.fish_cooked=Created by cooking a raw fish in a furnace. Can be eaten to restore health. -desc.bone=Collected by killing a skeleton. Can be crafted into bone meal. -desc.record=Collected by getting a skeleton to kill a creeper. Can be played in a jukebox. -desc.water=Extinguishes fire and helps crops grow. Can be collected in a bucket. -desc.leaves=When broken sometimes drops a sapling which can then be replanted to grow into a tree. -desc.moss_stone=Can be used for construction and decoration. -desc.stonebricksmooth=Used as building material. -desc.netherreactor=Core of the Nether Reactor -desc.glowingobsidian=Obsidion activated by external source. -desc.lightgem=Used for illumination. -desc.painting=Used as decoration. -desc.hellrock=Is a stone material from the Nether. -desc.netherbrick=Construction block made from Nether bricks. -desc.netherbrickitem=Used to form blocks of Nether bricks. -desc.netherquartz=Quartz from the Nether, used to create Blocks of Quartz. -desc.quartzblock=Decorative block, used to create other kinds of Quartz blocks. -desc.stonecutter=For crafting stoneblocks. -desc.seedsmelon=Allows for more melons. -desc.tile.melon=Can be cut up and eaten. + +language.name=English +language.region=US +language.code=en_US + +gui.done=Done +gui.cancel=Cancel +gui.toMenu=Back to title screen +gui.up=Up +gui.down=Down +gui.yes=Yes +gui.no=No + +menu.singleplayer=Singleplayer +menu.multiplayer=Multiplayer +menu.mods=Texture Packs +menu.options=Options... +menu.quit=Quit Game +menu.returnToMenu=Save and Quit to Title +menu.disconnect=Disconnect +menu.returnToGame=Back to Game +menu.switchingLevel=Switching level +menu.generatingLevel=Generating level +menu.loadingLevel=Loading level +menu.generatingTerrain=Building terrain +menu.simulating=Simulating the world for a bit +menu.respawning=Respawning + +selectWorld.title=Select World +selectWorld.empty=empty +selectWorld.world=World +selectWorld.select=Play Selected World +selectWorld.create=Create New World +selectWorld.createDemo=Play New Demo World +selectWorld.delete=Delete +selectWorld.rename=Rename +selectWorld.deleteQuestion=Are you sure you want to delete this world? +selectWorld.deleteWarning=will be lost forever! (A long time!) +selectWorld.deleteButton=Delete +selectWorld.renameButton=Rename +selectWorld.renameTitle=Rename World +selectWorld.conversion=Must be converted! +selectWorld.newWorld=New World +selectWorld.enterName=World Name +selectWorld.resultFolder=Will be saved in: +selectWorld.enterSeed=Seed for the World Generator +selectWorld.seedInfo=Leave blank for a random seed + +gameMode.survival=Survival Mode +gameMode.creative=Creative Mode +gameMode.hardcore=Hardcore Mode! +gameMode.changed=Your game mode has been changed + +selectWorld.gameMode=Game Mode: +selectWorld.gameMode.survival=Survival +selectWorld.gameMode.survival.line1=Search for resources, crafting, gain +selectWorld.gameMode.survival.line2=levels, health and hunger +selectWorld.gameMode.creative=Creative +selectWorld.gameMode.creative.line1=Unlimited resources, free flying and +selectWorld.gameMode.creative.line2=destroy blocks instantly +selectWorld.gameMode.hardcore=Hardcore +selectWorld.gameMode.hardcore.line1=Same as survival mode, locked at hardest +selectWorld.gameMode.hardcore.line2=difficulty, and one life only +selectWorld.moreWorldOptions=More World Options... +selectWorld.mapFeatures=Generate Structures: +selectWorld.mapFeatures.info=Villages, dungeons etc +selectWorld.mapType=World Type: +selectWorld.mapType.normal=Normal +selectWorld.hardcoreMode=Hardcore: +selectWorld.hardcoreMode.info=World is deleted upon death + +generator.default=Default +generator.flat=Superflat + +selectServer.title=Select Server +selectServer.empty=empty +selectServer.select=Join Server +selectServer.direct=Direct Connect +selectServer.edit=Edit +selectServer.delete=Delete +selectServer.add=Add server +selectServer.defaultName=Minecraft Server +selectServer.deleteQuestion=Are you sure you want to remove this server? +selectServer.deleteWarning=will be lost forever! (A long time!) +selectServer.deleteButton=Delete +selectServer.refresh=Refresh +addServer.title=Edit Server Info +addServer.enterName=Server Name +addServer.enterIp=Server Address +addServer.add=Done + +multiplayer.title=Play Multiplayer +multiplayer.connect=Connect +multiplayer.info1=Minecraft Multiplayer is currently not finished, but there +multiplayer.info2=is some buggy early testing going on. +multiplayer.ipinfo=Enter the IP of a server to connect to it: + +multiplayer.downloadingTerrain=Downloading terrain + +multiplayer.stopSleeping=Leave Bed + +demo.day.1=This demo will last five game days, do your best! +demo.day.2=Day Two +demo.day.3=Day Three +demo.day.4=Day Four +demo.day.5=This is your last day! +demo.day.warning=Your time is almost up! +demo.day.6=You have passed your fifth day, use F2 to save a screenshot of your creation +demo.reminder=The demo time has expired, buy the game to continue or start a new world! +demo.help.movement=Use %1$s, %2$s, %3$s, %4$s and the mouse to move around +demo.help.jump=Jump by pressing %1$s +demo.help.inventory=Use %1$s to open your inventory + +connect.connecting=Connecting to the server... +connect.authorizing=Logging in... +connect.failed=Failed to connect to the server + +disconnect.genericReason=%s +disconnect.disconnected=Disconnected by Server +disconnect.lost=Connection Lost +disconnect.kicked=Was kicked from the game +disconnect.timeout=Timed out +disconnect.closed=Connection closed +disconnect.loginFailed=Failed to login +disconnect.loginFailedInfo=Failed to login: %s +disconnect.quitting=Quitting +disconnect.endOfStream=End of stream +disconnect.overflow=Buffer overflow + +options.off=OFF +options.on=ON +options.title=Options +options.controls=Controls... +options.video=Video Settings... +options.language=Language... +options.languageWarning=Language translations may not be 100% accurate +options.videoTitle=Video Settings +options.music=Music +options.sound=Sound +options.invertMouse=Invert Mouse +options.fov=FOV +options.fov.min=Normal +options.fov.max=Quake Pro +options.gamma=Brightness +options.gamma.min=Moody +options.gamma.max=Bright +options.group.mojang=Login +options.group.general=General +options.group.game=Game +options.group.controls=Controls +options.group.graphics=Graphics +options.group.tweaks=Tweaks +options.allowSprint=Allow sprint +options.barOnTop=HUD above inventory +options.rpiCursor=Show Raspberry PI cursor +options.autoJump=Auto Jump +options.thirdperson=Third Person +options.servervisible=Server Visible +options.sensitivity=Sensitivity +options.sensitivity.min=*yawn* +options.sensitivity.max=HYPERSPEED!!! +options.renderDistance=Render Distance +options.renderDistance.tiny=Tiny +options.renderDistance.short=Short +options.renderDistance.normal=Normal +options.renderDistance.far=Far +options.viewBobbing=View Bobbing +options.ao=Smooth Lighting +options.anaglyph=3D Anaglyph +options.framerateLimit=Performance +options.limitFramerate=Limit Framerate +options.vsync=VSync +options.difficulty=Difficulty +options.difficulty.peaceful=Peaceful +options.difficulty.easy=Easy +options.difficulty.normal=Normal +options.difficulty.hard=Hard +options.difficulty.hardcore=Hardcore +options.graphics=Graphics +options.graphics.fancy=Fancy +options.graphics.fast=Fast +options.guiScale=GUI Scale +options.guiScale.auto=Auto +options.guiScale.small=Small +options.guiScale.medium=Medium +options.guiScale.large=Large +options.guiScale.larger=Larger +options.guiScale.largest=Largest +options.advancedOpengl=Advanced OpenGL +options.renderClouds=Clouds +options.farWarning1=A 64 bit Java installation is recommended +options.farWarning2=for 'Far' render distance (you have 32 bit) +options.particles=Particles +options.particles.all=All +options.particles.decreased=Decreased +options.particles.minimal=Minimal +options.username=Username +options.smoothCamera=Smooth camera +options.destroyVibration=Destroy vibration +options.isLeftHanded=Left handed +options.useTouchscreen=Use touchscreen +options.fancyGraphics=Fancy graphics +options.renderDebug=Debug render +options.anaglyph3d=3D anaglyph + + +performance.max=Max FPS +performance.balanced=Balanced +performance.powersaver=Power saver + +controls.title=Controls + +options.key.forward=Forward +options.key.left=Left +options.key.back=Back +options.key.right=Right +options.key.jump=Jump +options.key.inventory=Inventory +options.key.drop=Drop +options.key.chat=Chat +options.key.fog=Toggle Fog +options.key.sneak=Sneak +options.key.playerlist=List Players +options.key.attack=Attack +options.key.use=Use Item +options.key.pickItem=Pick Block +options.key.mouseButton=Button %1$s + +texturePack.openFolder=Open texture pack folder +texturePack.title=Select Texture Pack +texturePack.folderInfo=(Place texture pack files here) + +tile.stone.name=Stone +tile.grass.name=Grass Block +tile.dirt.name=Dirt +tile.stonebrick.name=Cobblestone +tile.wood.name=Wooden Planks +tile.sapling.name=Sapling +tile.deadbush.name=Dead Bush +tile.bedrock.name=Bedrock +tile.water.name=Water +tile.lava.name=Lava +tile.sand.name=Sand +tile.sandStone.name=Sandstone +tile.gravel.name=Gravel +tile.oreGold.name=Gold Ore +tile.oreIron.name=Iron Ore +tile.oreCoal.name=Coal Ore +tile.log.name=Wood +tile.leaves.name=Leaves +tile.tallgrass.name=Grass +tile.tallgrass.shrub.name=Shrub +tile.tallgrass.grass.name=Grass +tile.tallgrass.fern.name=Fern +tile.sponge.name=Sponge +tile.glass.name=Glass +tile.thinGlass.name=Glass Pane +tile.cloth.name=Wool +tile.flower.name=Flower +tile.rose.name=Rose +tile.mushroom.name=Mushroom +tile.blockGold.name=Block of Gold +tile.blockIron.name=Block of Iron +tile.stoneSlab.stone.name=Stone Slab +tile.stoneSlab.sand.name=Sandstone Slab +tile.stoneSlab.wood.name=Wooden Slab +tile.stoneSlab.cobble.name=Cobblestone Slab +tile.stoneSlab.brick.name=Bricks Slab +tile.stoneSlab.smoothStoneBrick.name=Stone Bricks Slab +tile.brick.name=Bricks +tile.tnt.name=TNT +tile.bookshelf.name=Bookshelf +tile.stoneMoss.name=Moss Stone +tile.obsidian.name=Obsidian +tile.torch.name=Torch +tile.fire.name=Fire +tile.mobSpawner.name=Monster Spawner +tile.stairsWood.name=Wooden Stairs +tile.chest.name=Chest +tile.redstoneDust.name=Redstone Dust +tile.oreDiamond.name=Diamond Ore +tile.blockDiamond.name=Block of Diamond +tile.workbench.name=Crafting Table +tile.crops.name=Crops +tile.farmland.name=Farmland +tile.furnace.name=Furnace +tile.sign.name=Sign +tile.doorWood.name=Wooden Door +tile.ladder.name=Ladder +tile.rail.name=Rail +tile.goldenRail.name=Powered Rail +tile.detectorRail.name=Detector Rail +tile.stairsStone.name=Stone Stairs +tile.lever.name=Lever +tile.pressurePlate.name=Pressure Plate +tile.doorIron.name=Iron Door +tile.oreRedstone.name=Redstone Ore +tile.notGate.name=Redstone Torch +tile.button.name=Button +tile.snow.name=Snow +tile.ice.name=Ice +tile.cactus.name=Cactus +tile.clay.name=Clay +tile.reeds.name=Sugar cane +tile.jukebox.name=Jukebox +tile.fence.name=Fence +tile.fenceGate.name=Fence Gate +tile.pumpkin.name=Pumpkin +tile.litpumpkin.name=Jack 'o' Lantern +tile.hellrock.name=Netherrack +tile.hellsand.name=Soul Sand +tile.lightgem.name=Glowstone +tile.portal.name=Portal +tile.cloth.black.name=Black Wool +tile.cloth.red.name=Red Wool +tile.cloth.green.name=Green Wool +tile.cloth.brown.name=Brown Wool +tile.cloth.blue.name=Blue Wool +tile.cloth.purple.name=Purple Wool +tile.cloth.cyan.name=Cyan Wool +tile.cloth.silver.name=Light Gray Wool +tile.cloth.gray.name=Gray Wool +tile.cloth.pink.name=Pink Wool +tile.cloth.lime.name=Lime Wool +tile.cloth.yellow.name=Yellow Wool +tile.cloth.lightBlue.name=Blue Light Wool +tile.cloth.magenta.name=Magenta Wool +tile.cloth.orange.name=Orange Wool +tile.cloth.white.name=Wool +tile.oreLapis.name=Lapis Lazuli Ore +tile.blockLapis.name=Lapis Lazuli Block +tile.dispenser.name=Dispenser +tile.musicBlock.name=Note Block +tile.cake.name=Cake +tile.bed.name=Bed +tile.bed.occupied=This bed is occupied +tile.bed.noSleep=You can only sleep at night +tile.bed.notSafe=You may not rest now, there are monsters nearby +tile.bed.notValid=Your home bed was missing or obstructed +tile.lockedchest.name=Locked chest +tile.trapdoor.name=Trapdoor +tile.web.name=Cobweb +tile.stonebricksmooth.name=Stone Bricks +tile.pistonBase.name=Piston +tile.pistonStickyBase.name=Sticky Piston +tile.fenceIron.name=Iron Bars +tile.melon.name=Melon +tile.stairsBrick.name=Brick Stairs +tile.stairsStoneBrickSmooth.name=Stone Brick Stairs +tile.vine.name=Vines +tile.netherBrick.name=Nether Brick +tile.netherFence.name=Nether Brick Fence +tile.stairsNetherBrick.name=Nether Brick Stairs +tile.netherStalk.name=Nether Wart +tile.cauldron.name=Cauldron +tile.enchantmentTable.name=Enchantment Table +tile.whiteStone.name=End Stone +tile.mycel.name=Mycelium +tile.waterlily.name=Lily Pad +tile.dragonEgg.name=Dragon Egg +tile.redstoneLight.name=Redstone Lamp +tile.glowingobsidian.name=Glowing Obsidian +tile.netherreactor.name=Nether Reactor Core +tile.quartzBlock.name=Block of Quartz +tile.stairsQuartz.name=Quartz Stairs +tile.stairsSandStone.name=Sand Stone Stairs +tile.stonecutter.name=Stonecutter + +item.shovelIron.name=Iron Shovel +item.pickaxeIron.name=Iron Pickaxe +item.hatchetIron.name=Iron Axe +item.flintAndSteel.name=Flint and Steel +item.apple.name=Apple +item.cookie.name=Cookie +item.bow.name=Bow +item.arrow.name=Arrow +item.coal.name=Coal +item.charcoal.name=Charcoal +item.emerald.name=Diamond +item.ingotIron.name=Iron Ingot +item.ingotGold.name=Gold Ingot +item.swordIron.name=Iron Sword +item.swordWood.name=Wooden Sword +item.shovelWood.name=Wooden Shovel +item.pickaxeWood.name=Wooden Pickaxe +item.hatchetWood.name=Wooden Axe +item.swordStone.name=Stone Sword +item.shovelStone.name=Stone Shovel +item.pickaxeStone.name=Stone Pickaxe +item.hatchetStone.name=Stone Axe +item.swordDiamond.name=Diamond Sword +item.shovelDiamond.name=Diamond Shovel +item.pickaxeDiamond.name=Diamond Pickaxe +item.hatchetDiamond.name=Diamond Axe +item.stick.name=Stick +item.bowl.name=Bowl +item.mushroomStew.name=Mushroom Stew +item.swordGold.name=Golden Sword +item.shovelGold.name=Golden Shovel +item.pickaxeGold.name=Golden Pickaxe +item.hatchetGold.name=Golden Axe +item.string.name=String +item.feather.name=Feather +item.sulphur.name=Gunpowder +item.hoeWood.name=Wooden Hoe +item.hoeStone.name=Stone Hoe +item.hoeIron.name=Iron Hoe +item.hoeDiamond.name=Diamond Hoe +item.hoeGold.name=Golden Hoe +item.seeds.name=Seeds +item.seeds_pumpkin.name=Pumpkin Seeds +item.seeds_melon.name=Melon Seeds +item.melon.name=Melon +item.wheat.name=Wheat +item.bread.name=Bread +item.helmetCloth.name=Leather Cap +item.chestplateCloth.name=Leather Tunic +item.leggingsCloth.name=Leather Pants +item.bootsCloth.name=Leather Boots +item.helmetChain.name=Chain Helmet +item.chestplateChain.name=Chain Chestplate +item.leggingsChain.name=Chain Leggings +item.bootsChain.name=Chain Boots +item.helmetIron.name=Iron Helmet +item.chestplateIron.name=Iron Chestplate +item.leggingsIron.name=Iron Leggings +item.bootsIron.name=Iron Boots +item.helmetDiamond.name=Diamond Helmet +item.chestplateDiamond.name=Diamond Chestplate +item.leggingsDiamond.name=Diamond Leggings +item.bootsDiamond.name=Diamond Boots +item.helmetGold.name=Golden Helmet +item.chestplateGold.name=Golden Chestplate +item.leggingsGold.name=Golden Leggings +item.bootsGold.name=Golden Boots +item.flint.name=Flint +item.porkchopRaw.name=Raw Porkchop +item.porkchopCooked.name=Cooked Porkchop +item.chickenRaw.name=Raw Chicken +item.chickenCooked.name=Cooked Chicken +item.beefRaw.name=Raw Beef +item.beefCooked.name=Steak +item.painting.name=Painting +item.appleGold.name=Golden Apple +item.sign.name=Sign +item.doorWood.name=Wooden Door +item.bucket.name=Bucket +item.bucketWater.name=Water Bucket +item.bucketLava.name=Lava Bucket +item.minecart.name=Minecart +item.saddle.name=Saddle +item.doorIron.name=Iron Door +item.redstone.name=Redstone +item.snowball.name=Snowball +item.boat.name=Boat +item.leather.name=Leather +item.milk.name=Milk +item.brick.name=Brick +item.clay.name=Clay +item.reeds.name=Sugar Canes +item.paper.name=Paper +item.book.name=Book +item.slimeball.name=Slimeball +item.minecartChest.name=Minecart with Chest +item.minecartFurnace.name=Minecart with Furnace +item.egg.name=Egg +item.compass.name=Compass +item.fishingRod.name=Fishing Rod +item.clock.name=Clock +item.yellowDust.name=Glowstone Dust +item.fishRaw.name=Raw Fish +item.fishCooked.name=Cooked Fish +item.record.name=Music Disc +item.bone.name=Bone +item.dyePowder.black.name=Ink Sac +item.dyePowder.red.name=Rose Red +item.dyePowder.green.name=Cactus Green +item.dyePowder.brown.name=Cocoa Beans +item.dyePowder.blue.name=Lapis Lazuli +item.dyePowder.purple.name=Purple Dye +item.dyePowder.cyan.name=Cyan Dye +item.dyePowder.silver.name=Light Gray Dye +item.dyePowder.gray.name=Gray Dye +item.dyePowder.pink.name=Pink Dye +item.dyePowder.lime.name=Lime Dye +item.dyePowder.yellow.name=Dandelion Yellow +item.dyePowder.lightBlue.name=Light Blue Dye +item.dyePowder.magenta.name=Magenta Dye +item.dyePowder.orange.name=Orange Dye +item.dyePowder.white.name=Bone Meal +item.sugar.name=Sugar +item.cake.name=Cake +item.bed.name=Bed +item.diode.name=Redstone Repeater +item.map.name=Map +item.leaves.name=Leaves +item.shears.name=Shears +item.rottenFlesh.name=Rotten Flesh +item.enderPearl.name=Ender Pearl +item.blazeRod.name=Blaze Rod +item.ghastTear.name=Ghast Tear +item.netherStalkSeeds.name=Nether Wart +item.potion.name=Potion +item.emptyPotion.name=Water Bottle +item.goldNugget.name=Gold Nugget +item.glassBottle.name=Glass Bottle +item.spiderEye.name=Spider Eye +item.fermentedSpiderEye.name=Fermented Spider Eye +item.blazePowder.name=Blaze Powder +item.magmaCream.name=Magma Cream +item.cauldron.name=Cauldron +item.brewingStand.name=Brewing Stand +item.eyeOfEnder.name=Eye of Ender +item.speckledMelon.name=Glistering Melon +item.monsterPlacer.name=Spawn +item.expBottle.name=Bottle o' Enchanting +item.fireball.name=Fire Charge +item.netherbrickItem.name=Nether Brick +item.netherquartz.name=Nether Quartz + +container.inventory=Inventory +container.crafting=Crafting +container.dispenser=Dispenser +container.furnace=Furnace +container.enchant=Enchant +container.creative=Item Selection +container.brewing=Brewing Stand +container.chest=Chest +container.minecart=Minecart + +entity.Item.name=Item +entity.XPOrb.name=Experience Orb + +entity.Arrow.name=Arrow +entity.Snowball.name=Snowball +entity.Painting.name=Painting + +entity.Mob.name=Mob +entity.Monster.name=Monster + +entity.Creeper.name=Creeper +entity.Skeleton.name=Skeleton +entity.Spider.name=Spider +entity.Giant.name=Giant +entity.Zombie.name=Zombie +entity.Slime.name=Slime +entity.Ghast.name=Ghast +entity.PigZombie.name=Zombie Pigman +entity.Enderman.name=Enderman +entity.Silverfish.name=Silverfish +entity.CaveSpider.name=Cave Spider +entity.Blaze.name=Blaze +entity.LavaSlime.name=Magma Cube +entity.MushroomCow.name=Mooshroom +entity.Villager.name=Villager +entity.VillagerGolem.name=Iron Golem +entity.SnowMan.name=Snow Golem +entity.EnderDragon.name=Ender Dragon + +entity.Pig.name=Pig +entity.Sheep.name=Sheep +entity.Cow.name=Cow +entity.Chicken.name=Chicken +entity.Squid.name=Squid +entity.Wolf.name=Wolf +entity.Ozelot.name=Ocelot +entity.Cat.name=Cat + +entity.PrimedTnt.name=Block of TNT +entity.FallingSand.name=Falling Block + +entity.Minecart.name=Minecart +entity.Boat.name=Boat + +entity.Arrow.name=arrow +entity.generic.name=unknown + +death.inFire=%1$s went up in flames +death.onFire=%1$s burned to death +death.lava=%1$s tried to swim in lava +death.inWall=%1$s suffocated in a wall +death.drown=%1$s drowned +death.starve=%1$s starved to death +death.cactus=%1$s was pricked to death +death.fall=%1$s hit the ground too hard +death.outOfWorld=%1$s fell out of the world +death.generic=%1$s died +death.explosion=%1$s blew up +death.magic=%1$s was killed by magic +death.mob=%1$s was slain by %2$s +death.player=%1$s was slain by %2$s +death.arrow=%1$s was shot by %2$s +death.fireball=%1$s was fireballed by %2$s +death.thrown=%1$s was pummeled by %2$s +death.indirectMagic=%1$s was killed by %2$s + +deathScreen.respawn=Respawn +deathScreen.deleteWorld=Delete world +deathScreen.titleScreen=Title screen +deathScreen.score=Score +deathScreen.title.hardcore=Game over! +deathScreen.hardcoreInfo=You cannot respawn in hardcore mode! +deathScreen.title=You died! + +potion.empty=No Effects +potion.moveSpeed=Speed +potion.moveSlowdown=Slowness +potion.digSpeed=Haste +potion.digSlowDown=Mining Fatigue +potion.damageBoost=Strength +potion.weakness=Weakness +potion.heal=Instant Health +potion.harm=Instant Damage +potion.jump=Jump Boost +potion.confusion=Nausea +potion.regeneration=Regeneration +potion.resistance=Resistance +potion.fireResistance=Fire Resistance +potion.waterBreathing=Water Breathing +potion.invisibility=Invisibility +potion.blindness=Blindness +potion.nightVision=Night Vision +potion.hunger=Hunger +potion.poison=Poison + +potion.moveSpeed.postfix=Potion of Swiftness +potion.moveSlowdown.postfix=Potion of Slowness +potion.digSpeed.postfix=Potion of Haste +potion.digSlowDown.postfix=Potion of Dullness +potion.damageBoost.postfix=Potion of Strength +potion.weakness.postfix=Potion of Weakness +potion.heal.postfix=Potion of Healing +potion.harm.postfix=Potion of Harming +potion.jump.postfix=Potion of Leaping +potion.confusion.postfix=Potion of Nausea +potion.regeneration.postfix=Potion of Regeneration +potion.resistance.postfix=Potion of Resistance +potion.fireResistance.postfix=Potion of Fire Resistance +potion.waterBreathing.postfix=Potion of Water Breathing +potion.invisibility.postfix=Potion of Invisibility +potion.blindness.postfix=Potion of Blindness +potion.nightVision.postfix=Potion of Night Vision +potion.hunger.postfix=Potion of Hunger +potion.poison.postfix=Potion of Poison + +potion.potency.0= +potion.potency.1=II +potion.potency.2=III +potion.potency.3=IV + +potion.prefix.grenade=Splash +potion.prefix.mundane=Mundane +potion.prefix.uninteresting=Uninteresting +potion.prefix.bland=Bland +potion.prefix.clear=Clear +potion.prefix.milky=Milky +potion.prefix.diffuse=Diffuse +potion.prefix.artless=Artless +potion.prefix.thin=Thin +potion.prefix.awkward=Awkward +potion.prefix.flat=Flat +potion.prefix.bulky=Bulky +potion.prefix.bungling=Bungling +potion.prefix.buttered=Buttered +potion.prefix.smooth=Smooth +potion.prefix.suave=Suave +potion.prefix.debonair=Debonair +potion.prefix.thick=Thick +potion.prefix.elegant=Elegant +potion.prefix.fancy=Fancy +potion.prefix.charming=Charming +potion.prefix.dashing=Dashing +potion.prefix.refined=Refined +potion.prefix.cordial=Cordial +potion.prefix.sparkling=Sparkling +potion.prefix.potent=Potent +potion.prefix.foul=Foul +potion.prefix.odorless=Odorless +potion.prefix.rank=Rank +potion.prefix.harsh=Harsh +potion.prefix.acrid=Acrid +potion.prefix.gross=Gross +potion.prefix.stinky=Stinky + +enchantment.damage.all=Sharpness +enchantment.damage.undead=Smite +enchantment.damage.arthropods=Bane of Arthropods +enchantment.knockback=Knockback +enchantment.fire=Fire Aspect +enchantment.protect.all=Protection +enchantment.protect.fire=Fire Protection +enchantment.protect.fall=Feather Falling +enchantment.protect.explosion=Blast Protection +enchantment.protect.projectile=Projectile Protection +enchantment.oxygen=Respiration +enchantment.waterWorker=Aqua Affinity +enchantment.digging=Efficiency +enchantment.untouching=Silk Touch +enchantment.durability=Unbreaking +enchantment.lootBonus=Looting +enchantment.lootBonusDigger=Fortune +enchantment.arrowDamage=Power +enchantment.arrowFire=Flame +enchantment.arrowKnockback=Punch +enchantment.arrowInfinite=Infinity + +enchantment.level.1=I +enchantment.level.2=II +enchantment.level.3=III +enchantment.level.4=IV +enchantment.level.5=V +enchantment.level.6=VI +enchantment.level.7=VII +enchantment.level.8=VIII +enchantment.level.9=IX +enchantment.level.10=X + +gui.achievements=Achievements +gui.stats=Statistics + +stat.generalButton=General +stat.blocksButton=Blocks +stat.itemsButton=Items + +stat.used=Times Used +stat.mined=Times Mined +stat.depleted=Times Depleted +stat.crafted=Times Crafted + +stat.startGame=Times played +stat.createWorld=Worlds played +stat.loadWorld=Saves loaded +stat.joinMultiplayer=Multiplayer joins +stat.leaveGame=Games quit + +stat.playOneMinute=Minutes Played + +stat.walkOneCm=Distance Walked +stat.fallOneCm=Distance Fallen +stat.swimOneCm=Distance Swum +stat.flyOneCm=Distance Flown +stat.climbOneCm=Distance Climbed +stat.diveOneCm=Distance Dove +stat.minecartOneCm=Distance by Minecart +stat.boatOneCm=Distance by Boat +stat.pigOneCm=Distance by Pig +stat.jump=Jumps +stat.drop=Items Dropped + +stat.damageDealt=Damage Dealt +stat.damageTaken=Damage Taken +stat.deaths=Number of Deaths +stat.mobKills=Mob Kills +stat.playerKills=Player Kills +stat.fishCaught=Fish Caught + +stat.mineBlock=%1$s Mined +stat.craftItem=%1$s Crafted +stat.useItem=%1$s Used +stat.breakItem=%1$s Depleted + +achievement.get=Achievement get! + +achievement.taken=Taken! + +achievement.requires=Requires '%1$s' +achievement.openInventory=Taking Inventory +achievement.openInventory.desc=Press '%1$s' to open your inventory. +achievement.mineWood=Getting Wood +achievement.mineWood.desc=Attack a tree until a block of wood pops out +achievement.buildWorkBench=Benchmarking +achievement.buildWorkBench.desc=Craft a workbench with four blocks of planks +achievement.buildPickaxe=Time to Mine! +achievement.buildPickaxe.desc=Use planks and sticks to make a pickaxe +achievement.buildFurnace=Hot Topic +achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks +achievement.acquireIron=Acquire Hardware +achievement.acquireIron.desc=Smelt an iron ingot +achievement.buildHoe=Time to Farm! +achievement.buildHoe.desc=Use planks and sticks to make a hoe +achievement.makeBread=Bake Bread +achievement.makeBread.desc=Turn wheat into bread +achievement.bakeCake=The Lie +achievement.bakeCake.desc=Wheat, sugar, milk and eggs! +achievement.buildBetterPickaxe=Getting an Upgrade +achievement.buildBetterPickaxe.desc=Construct a better pickaxe +achievement.cookFish=Delicious Fish +achievement.cookFish.desc=Catch and cook fish! +achievement.onARail=On A Rail +achievement.onARail.desc=Travel by minecart at least 1 km from where you started +achievement.buildSword=Time to Strike! +achievement.buildSword.desc=Use planks and sticks to make a sword +achievement.killEnemy=Monster Hunter +achievement.killEnemy.desc=Attack and destroy a monster +achievement.killCow=Cow Tipper +achievement.killCow.desc=Harvest some leather +achievement.flyPig=When Pigs Fly +achievement.flyPig.desc=Fly a pig off a cliff +achievement.snipeSkeleton=Sniper Duel +achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters +achievement.diamonds=DIAMONDS! +achievement.diamonds.desc=Acquire diamonds with your iron tools +achievement.portal=We Need to Go Deeper +achievement.portal.desc=Build a portal to the Nether +achievement.ghast=Return to Sender +achievement.ghast.desc=Destroy a Ghast with a fireball +achievement.blazeRod=Into Fire +achievement.blazeRod.desc=Relieve a Blaze of its rod +achievement.potion=Local Brewery +achievement.potion.desc=Brew a potion +achievement.theEnd=The End? +achievement.theEnd.desc=Locate the End +achievement.theEnd2=The End. +achievement.theEnd2.desc=Defeat the Ender Dragon +achievement.enchantments=Enchanter +achievement.enchantments.desc=Use a book, obsidian and diamonds to construct an enchantment table +achievement.overkill=Overkill +achievement.overkill.desc=Deal eight hearts of damage in a single hit +achievement.bookcase=Librarian +achievement.bookcase.desc=Build some bookshelves to improve your enchantment table + +desc.sword=Deals more damage than by hand. +desc.shovel=Used to dig dirt, grass, sand, gravel and snow faster than by hand. +desc.pickaxe=Required to mine stone-related blocks and ore. +desc.hatchet=Used to chop wood-related blocks faster than by hand. +desc.hoe=Used to till dirt and grass blocks to prepare for crops. +desc.doorwood=Wooden doors are activated by using, hitting them or with Redstone. +desc.dooriron=Iron doors can only be opened by Redstone, buttons or switches. +desc.helmet=Gives the user 1.5 Armor when worn. +desc.chestplate=Gives the user 4 Armor when worn. +desc.leggings=Gives the user 3 Armor when worn. +desc.boots=Gives the user 1.5 Armor when worn. +desc.ingot=A shiny ingot which can be used to craft tools made from this material. +desc.block=Used as an expensive building block or compact storage of the ore. +desc.pressureplate=Used to send an electrical charge when stepped on by a player, an animal, or a monster. Wooden Pressure Plates can also be activated by dropping something on them. +desc.stairs=Used for compact staircases. +desc.stairsstonebricksmooth=Used for compact staircases. +desc.stairsnetherbrick=Used for compact staircases. +desc.stairssandstone=Used for compact staircases. +desc.stairsquartzblock=Used for compact staircases. +desc.slab=Used for making long staircases. +desc.halfslab=Used for compact staircases. +desc.torch=Used to create light. Torches also melt snow and ice. +desc.wood=Used as a building material and can be crafted into many things. Can be crafted from any wood. +desc.sandstone=Used as a building material. +desc.sandstone.addon=Is not influenced by gravity like normal Sand. +desc.structblock=Used as a building material. +desc.stick=Used to craft torches, arrows, signs, ladders, fences and as handles for tools and weapons. +desc.bed=Used to sleep until dawn. Changes your spawn point to the bed's position. +desc.craftingtable=Allows you to craft a more varied selection of items than the normal crafting. +desc.furnace=Allows you to smelt ore, create charcoal and glass, and cook fish and porkchops. +desc.chest=Stores blocks and items inside. +desc.fence=Used as a barrier that cannot be jumped over. +desc.ladder=Used to climb vertically. +desc.trapdoor=Work like normal doors, but are a one by one block and lay flat on the ground. +desc.sign=Shows text entered by you or other players. +desc.glowstone=Used to create brighter light than torches. Melts snow/ice and can be used underwater. +desc.tnt=Used to cause explosions. Activated after placing by hitting, or with an electrical charge. +desc.bowl=Used to hold mushroom stew. You keep the bowl when the stew has been eaten. +desc.bucket=Used to hold and transport water, lava and milk. +desc.flintandsteel=Used to detonate TNT. +desc.fishingrod=Used to catch fish. +desc.clock=Displays positions of the Sun and Moon. +desc.compass=Points to your start point. +desc.map=Will create an image of an area explored while held. This can be used for path-finding. +desc.bow=Allows for ranged attacks by using arrows. +desc.arrow=Used as ammunition for bows. +desc.mushroomstew=Restores 5 hearts. +desc.cookie=Restores 0.5 Hearts. Can be stacked up to 8 times. +desc.cake=Restores 1.5 Hearts. Can be used 6 times. +desc.sugar=Used in the cake recipe. +desc.bread=Restores 2.5 Hearts. +desc.goldenapple=Restores 10 Hearts. +desc.lever=Used to send an electrical charge by being turned on or off. Stays in the on or off state until pressed again. +desc.redstonetorch=Constantly sends an electrical charge, or can be used as a receiver/transmitter when connected to the side of a block. +Can also be used for low-level lighting. +desc.redstonerepeater=Used in Redstone circuits as repeater, a delayer, and/or a diode +desc.button=Used to send an electrical charge by being pressed. Stays activated for approximately a second before shutting off again. +desc.dispenser=Used to hold and shoot out items in a random order when given a Redstone charge. +desc.noteblock=Plays a note when triggered. Hit it to change the pitch of the note. Placing this on top of different blocks will change the type of instrument. +desc.rail=Used to guide minecarts. +desc.poweredrail=When powered, accelerates minecarts that pass over it. When unpowered, causes minecarts to stop on it. +desc.detectorrail=Functions like a Pressure Plate (sends a Redstone signal when powered) but can only be activated by a minecart. +desc.minecart=Used to transport you, an animal, or a monster along rails. +desc.minecartwithchest=Used to transport goods along rails. +desc.minecartwithfurnace=Will move along rails and can push other minecarts when coal is put in it. +desc.boat=Used to travel in water more quickly than swimming. +desc.wool=Collected from sheep, and can be colored with dyes. +desc.woolstring=Used as a building material and can be colored with dyes. Can be easily obtained from Sheep. +desc.dyepowder.black=Used as a dye to create black wool. +desc.dyepowder.green=Used as a dye to create green wool. +desc.dyepowder.brown=Used as a dye to create brown wool. +desc.dyepowder.silver=Used as a dye to create silver wool. +desc.dyepowder.yellow=Used as a dye to create yellow wool. +desc.dyepowder.red=Used as a dye to create red wool. +desc.dyepowder.white=Used to instantly grow crops and flowers. Can be used in dye recipes. +desc.dyepowder.pink=Used as a dye to create pink wool. +desc.dyepowder.orange=Used as a dye to create orange wool. +desc.dyepowder.lime=Used as a dye to create lime wool. +desc.dyepowder.gray=Used as a dye to create gray wool. +desc.dyepowder.lightgray=Used as a dye to create light gray wool. +(Note: light gray dye can also be made by combining gray dye with bone meal, letting you make four light gray dyes from every ink sac instead of three.) +desc.dyepowder.lightblue=Used as a dye to create light blue wool. +desc.dyepowder.cyan=Used as a dye to create cyan wool. +desc.dyepowder.purple=Used as a dye to create purple wool. +desc.dyepowder.magenta=Used as a dye to create magenta wool. +desc.dyepowder.blue=Used as dye to create Blue Wool. +desc.jukebox=Plays Music Discs. +desc.diamonds=Use these to create very strong tools, weapons or armor +desc.emerald=Use these to create very strong tools, weapons or armor +desc.jackolantern=Used to create brighter light than torches. Melts snow/ice and can be used underwater. +desc.paper=Used to create books and maps. +desc.book=Used to create a bookshelf. +desc.bookshelf=Used as decoration. +desc.picture=Used as decoration. +desc.ore_gold=Can be mined with an iron pickaxe or better, then smelted in a furnace to produce gold ingots. +desc.ore_iron=Can be mined with a stone pickaxe or better, then smelted in a furnace to produce iron ingots. +desc.ore_coal=Can be mined with a pickaxe to collect coal. +desc.ore_lapis=Can be mined with a stone pickaxe or better to collect lapis lazuli. +desc.ore_diamond=Can be mined with an iron pickaxe or better to collect diamonds. +desc.ore_redstone=Can be mined with an iron pickaxe or better to collect redstone dust. +desc.stone=Can be mined with a pickaxe to collect cobblestone. +desc.dirt=Collected using a shovel. Can be used for construction. +desc.sapling=Can be planted and it will eventually grow into a tree. +desc.bedrock=This cannot be broken. +desc.lava=Sets fire to anything that touches it. Can be collected in a bucket. +desc.sand=Collected using a shovel. Can be smelted into glass using the furnace. Is affected by gravity if there is no other tile underneath it. +desc.gravel=Collected using a shovel. Sometimes produces flint when dug up. Is affected by gravity if there is no other tile underneath it. +desc.log=Chopped using an axe, and can be crafted into planks or used as a fuel. +desc.glass=Created in a furnace by smelting sand. Will break if you try to mine it. +desc.thinglass=Will break if you try to mine it. +desc.stone_brick=Mined from stone using a pickaxe. Can be used to construct a furnace or stone tools. +desc.brick=Baked from clay in a furnace. +desc.clay=Can be baked into bricks in a furnace. +desc.snow=A compact way to store snowballs. +desc.top_snow=Can be dug with a shovel to create snowballs. +desc.tall_grass=Sometimes produces seeds when broken. +desc.flower=Can be crafted into a dye. +desc.mushroom=Can be crafted with a bowl to make stew. +desc.obsidian=Can only be mined with a diamond pickaxe. Is produced by the meeting of water and still lava, and is used to build a portal. +desc.mob_spawner=Spawns monsters into the world. +desc.redstone_dust=Is placed on the ground to carry an electrical charge. +desc.crops=When fully grown, crops can be harvested to collect wheat. +desc.farmland=Ground that has been prepared ready to plant seeds. +desc.cactus=Can be crafted to create a dye. +desc.reeds=Can be crafted to create sugar. +desc.pumpkin=Can be worn as a helmet or crafted with a torch to create a Jack-O-Lantern. +desc.hell_rock=Burns forever if set alight. +desc.hell_sand=Slows the movement of anything walking over it. +desc.portal=Standing in the portal allows you to pass between the overworld and the nether. +desc.apple=Restores health, and can be crafted into a golden apple. +desc.melon=Can be broken into melon slices. +desc.coal=Used as a fuel in a furnace, or crafted to make a torch. +desc.string=Collected by killing a spider, and can be crafted into a bow. +desc.feather=Collected by killing a chicken, and can be crafted into an arrow. +desc.sulphur=Collected by killing a creeper, and can be crafted into TNT. +desc.shears=Used to obtain wool from sheep and to harvest placeable Leaf blocks. +desc.seeds=Can be planted in farmland to grow crops. +desc.wheat=Harvested from crops, and can be used to craft food items. +desc.flint=Collected by digging gravel, and can be used to craft a flint and steel. +desc.porkchopraw=Collected by killing a pig, and can be cooked in a furnace. Restores health. +desc.porkchopcooked=Created by cooking a porkchop in a furnace. Restores health. +desc.beefraw=Collected by killing a cow, and can be cooked in a furnace. Restores health. +desc.beefcooked=Created by cooking a beef in a furnace. Restores health. +desc.chickenraw=Collected by killing a chicken, and can be cooked in a furnace. Restores health. +desc.chickencooked=Created by cooking a chicken in a furnace. Restores health. +desc.saddle=When used on a pig it allows you to ride the pig. +desc.snowball=Collected by digging snow, and can be thrown. +desc.leather=Collected by killing a cow, and can be crafted into armor. +desc.slimeball=Collected by killing a slime. +desc.egg=Dropped randomly by chickens, and can be crafted into food items. +desc.yellow_dust=Collected by mining Glowstone, and can be crafted to make Glowstone blocks again. +desc.fish_raw=Can be eaten to restore health, or cooked in a furnace. +desc.fish_cooked=Created by cooking a raw fish in a furnace. Can be eaten to restore health. +desc.bone=Collected by killing a skeleton. Can be crafted into bone meal. +desc.record=Collected by getting a skeleton to kill a creeper. Can be played in a jukebox. +desc.water=Extinguishes fire and helps crops grow. Can be collected in a bucket. +desc.leaves=When broken sometimes drops a sapling which can then be replanted to grow into a tree. +desc.moss_stone=Can be used for construction and decoration. +desc.stonebricksmooth=Used as building material. +desc.netherreactor=Core of the Nether Reactor +desc.glowingobsidian=Obsidion activated by external source. +desc.lightgem=Used for illumination. +desc.painting=Used as decoration. +desc.hellrock=Is a stone material from the Nether. +desc.netherbrick=Construction block made from Nether bricks. +desc.netherbrickitem=Used to form blocks of Nether bricks. +desc.netherquartz=Quartz from the Nether, used to create Blocks of Quartz. +desc.quartzblock=Decorative block, used to create other kinds of Quartz blocks. +desc.stonecutter=For crafting stoneblocks. +desc.seedsmelon=Allows for more melons. +desc.tile.melon=Can be cut up and eaten. diff --git a/project/android/.classpath b/project/android/.classpath index bf30c06..78dc9b3 100755 --- a/project/android/.classpath +++ b/project/android/.classpath @@ -1,8 +1,8 @@ - - - - - - - - + + + + + + + + diff --git a/project/android/.project b/project/android/.project index b27db94..3bca2bc 100755 --- a/project/android/.project +++ b/project/android/.project @@ -1,33 +1,33 @@ - - - MinecraftPocketEdition - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - + + + MinecraftPocketEdition + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/project/android/ManifestAddons.xml b/project/android/ManifestAddons.xml index af7bf58..2bc3ba6 100755 --- a/project/android/ManifestAddons.xml +++ b/project/android/ManifestAddons.xml @@ -1,19 +1,19 @@ - - - - - - android:debuggable="true" - - - - - - - - - - - - + + + + + + android:debuggable="true" + + + + + + + + + + + + \ No newline at end of file diff --git a/project/android/assets/lang/en_US.lang b/project/android/assets/lang/en_US.lang index fedae1c..cca8914 100755 --- a/project/android/assets/lang/en_US.lang +++ b/project/android/assets/lang/en_US.lang @@ -1,969 +1,969 @@ - -language.name=English -language.region=US -language.code=en_US - -gui.done=Done -gui.cancel=Cancel -gui.toMenu=Back to title screen -gui.up=Up -gui.down=Down -gui.yes=Yes -gui.no=No - -menu.singleplayer=Singleplayer -menu.multiplayer=Multiplayer -menu.mods=Texture Packs -menu.options=Options... -menu.quit=Quit Game -menu.returnToMenu=Save and Quit to Title -menu.disconnect=Disconnect -menu.returnToGame=Back to Game -menu.switchingLevel=Switching level -menu.generatingLevel=Generating level -menu.loadingLevel=Loading level -menu.generatingTerrain=Building terrain -menu.simulating=Simulating the world for a bit -menu.respawning=Respawning - -selectWorld.title=Select World -selectWorld.empty=empty -selectWorld.world=World -selectWorld.select=Play Selected World -selectWorld.create=Create New World -selectWorld.createDemo=Play New Demo World -selectWorld.delete=Delete -selectWorld.rename=Rename -selectWorld.deleteQuestion=Are you sure you want to delete this world? -selectWorld.deleteWarning=will be lost forever! (A long time!) -selectWorld.deleteButton=Delete -selectWorld.renameButton=Rename -selectWorld.renameTitle=Rename World -selectWorld.conversion=Must be converted! -selectWorld.newWorld=New World -selectWorld.enterName=World Name -selectWorld.resultFolder=Will be saved in: -selectWorld.enterSeed=Seed for the World Generator -selectWorld.seedInfo=Leave blank for a random seed - -gameMode.survival=Survival Mode -gameMode.creative=Creative Mode -gameMode.hardcore=Hardcore Mode! -gameMode.changed=Your game mode has been changed - -selectWorld.gameMode=Game Mode: -selectWorld.gameMode.survival=Survival -selectWorld.gameMode.survival.line1=Search for resources, crafting, gain -selectWorld.gameMode.survival.line2=levels, health and hunger -selectWorld.gameMode.creative=Creative -selectWorld.gameMode.creative.line1=Unlimited resources, free flying and -selectWorld.gameMode.creative.line2=destroy blocks instantly -selectWorld.gameMode.hardcore=Hardcore -selectWorld.gameMode.hardcore.line1=Same as survival mode, locked at hardest -selectWorld.gameMode.hardcore.line2=difficulty, and one life only -selectWorld.moreWorldOptions=More World Options... -selectWorld.mapFeatures=Generate Structures: -selectWorld.mapFeatures.info=Villages, dungeons etc -selectWorld.mapType=World Type: -selectWorld.mapType.normal=Normal -selectWorld.hardcoreMode=Hardcore: -selectWorld.hardcoreMode.info=World is deleted upon death - -generator.default=Default -generator.flat=Superflat - -selectServer.title=Select Server -selectServer.empty=empty -selectServer.select=Join Server -selectServer.direct=Direct Connect -selectServer.edit=Edit -selectServer.delete=Delete -selectServer.add=Add server -selectServer.defaultName=Minecraft Server -selectServer.deleteQuestion=Are you sure you want to remove this server? -selectServer.deleteWarning=will be lost forever! (A long time!) -selectServer.deleteButton=Delete -selectServer.refresh=Refresh -addServer.title=Edit Server Info -addServer.enterName=Server Name -addServer.enterIp=Server Address -addServer.add=Done - -multiplayer.title=Play Multiplayer -multiplayer.connect=Connect -multiplayer.info1=Minecraft Multiplayer is currently not finished, but there -multiplayer.info2=is some buggy early testing going on. -multiplayer.ipinfo=Enter the IP of a server to connect to it: - -multiplayer.downloadingTerrain=Downloading terrain - -multiplayer.stopSleeping=Leave Bed - -demo.day.1=This demo will last five game days, do your best! -demo.day.2=Day Two -demo.day.3=Day Three -demo.day.4=Day Four -demo.day.5=This is your last day! -demo.day.warning=Your time is almost up! -demo.day.6=You have passed your fifth day, use F2 to save a screenshot of your creation -demo.reminder=The demo time has expired, buy the game to continue or start a new world! -demo.help.movement=Use %1$s, %2$s, %3$s, %4$s and the mouse to move around -demo.help.jump=Jump by pressing %1$s -demo.help.inventory=Use %1$s to open your inventory - -connect.connecting=Connecting to the server... -connect.authorizing=Logging in... -connect.failed=Failed to connect to the server - -disconnect.genericReason=%s -disconnect.disconnected=Disconnected by Server -disconnect.lost=Connection Lost -disconnect.kicked=Was kicked from the game -disconnect.timeout=Timed out -disconnect.closed=Connection closed -disconnect.loginFailed=Failed to login -disconnect.loginFailedInfo=Failed to login: %s -disconnect.quitting=Quitting -disconnect.endOfStream=End of stream -disconnect.overflow=Buffer overflow - -options.off=OFF -options.on=ON -options.title=Options -options.controls=Controls... -options.video=Video Settings... -options.language=Language... -options.languageWarning=Language translations may not be 100% accurate -options.videoTitle=Video Settings -options.music=Music -options.sound=Sound -options.invertMouse=Invert Mouse -options.fov=FOV -options.fov.min=Normal -options.fov.max=Quake Pro -options.gamma=Brightness -options.gamma.min=Moody -options.gamma.max=Bright -options.sensitivity=Sensitivity -options.sensitivity.min=*yawn* -options.sensitivity.max=HYPERSPEED!!! -options.renderDistance=Render Distance -options.renderDistance.tiny=Tiny -options.renderDistance.short=Short -options.renderDistance.normal=Normal -options.renderDistance.far=Far -options.viewBobbing=View Bobbing -options.ao=Smooth Lighting -options.anaglyph=3D Anaglyph -options.framerateLimit=Performance -options.difficulty=Difficulty -options.difficulty.peaceful=Peaceful -options.difficulty.easy=Easy -options.difficulty.normal=Normal -options.difficulty.hard=Hard -options.difficulty.hardcore=Hardcore -options.graphics=Graphics -options.graphics.fancy=Fancy -options.graphics.fast=Fast -options.guiScale=GUI Scale -options.guiScale.auto=Auto -options.guiScale.small=Small -options.guiScale.medium=Medium -options.guiScale.large=Large -options.guiScale.larger=Larger -options.guiScale.largest=Largest -options.advancedOpengl=Advanced OpenGL -options.renderClouds=Clouds -options.farWarning1=A 64 bit Java installation is recommended -options.farWarning2=for 'Far' render distance (you have 32 bit) -options.particles=Particles -options.particles.all=All -options.particles.decreased=Decreased -options.particles.minimal=Minimal - -performance.max=Max FPS -performance.balanced=Balanced -performance.powersaver=Power saver - -controls.title=Controls - -key.forward=Forward -key.left=Left -key.back=Back -key.right=Right -key.jump=Jump -key.inventory=Inventory -key.drop=Drop -key.chat=Chat -key.fog=Toggle Fog -key.sneak=Sneak -key.playerlist=List Players -key.attack=Attack -key.use=Use Item -key.pickItem=Pick Block -key.mouseButton=Button %1$s - -texturePack.openFolder=Open texture pack folder -texturePack.title=Select Texture Pack -texturePack.folderInfo=(Place texture pack files here) - -tile.stone.name=Stone -tile.grass.name=Grass Block -tile.dirt.name=Dirt -tile.stonebrick.name=Cobblestone -tile.wood.name=Wooden Planks -tile.sapling.name=Sapling -tile.deadbush.name=Dead Bush -tile.bedrock.name=Bedrock -tile.water.name=Water -tile.lava.name=Lava -tile.sand.name=Sand -tile.sandStone.name=Sandstone -tile.gravel.name=Gravel -tile.oreGold.name=Gold Ore -tile.oreIron.name=Iron Ore -tile.oreCoal.name=Coal Ore -tile.log.name=Wood -tile.leaves.name=Leaves -tile.tallgrass.name=Grass -tile.tallgrass.shrub.name=Shrub -tile.tallgrass.grass.name=Grass -tile.tallgrass.fern.name=Fern -tile.sponge.name=Sponge -tile.glass.name=Glass -tile.thinGlass.name=Glass Pane -tile.cloth.name=Wool -tile.flower.name=Flower -tile.rose.name=Rose -tile.mushroom.name=Mushroom -tile.blockGold.name=Block of Gold -tile.blockIron.name=Block of Iron -tile.stoneSlab.stone.name=Stone Slab -tile.stoneSlab.sand.name=Sandstone Slab -tile.stoneSlab.wood.name=Wooden Slab -tile.stoneSlab.cobble.name=Cobblestone Slab -tile.stoneSlab.brick.name=Bricks Slab -tile.stoneSlab.smoothStoneBrick.name=Stone Bricks Slab -tile.brick.name=Bricks -tile.tnt.name=TNT -tile.bookshelf.name=Bookshelf -tile.stoneMoss.name=Moss Stone -tile.obsidian.name=Obsidian -tile.torch.name=Torch -tile.fire.name=Fire -tile.mobSpawner.name=Monster Spawner -tile.stairsWood.name=Wooden Stairs -tile.chest.name=Chest -tile.redstoneDust.name=Redstone Dust -tile.oreDiamond.name=Diamond Ore -tile.blockDiamond.name=Block of Diamond -tile.workbench.name=Crafting Table -tile.crops.name=Crops -tile.farmland.name=Farmland -tile.furnace.name=Furnace -tile.sign.name=Sign -tile.doorWood.name=Wooden Door -tile.ladder.name=Ladder -tile.rail.name=Rail -tile.goldenRail.name=Powered Rail -tile.detectorRail.name=Detector Rail -tile.stairsStone.name=Stone Stairs -tile.lever.name=Lever -tile.pressurePlate.name=Pressure Plate -tile.doorIron.name=Iron Door -tile.oreRedstone.name=Redstone Ore -tile.notGate.name=Redstone Torch -tile.button.name=Button -tile.snow.name=Snow -tile.ice.name=Ice -tile.cactus.name=Cactus -tile.clay.name=Clay -tile.reeds.name=Sugar cane -tile.jukebox.name=Jukebox -tile.fence.name=Fence -tile.fenceGate.name=Fence Gate -tile.pumpkin.name=Pumpkin -tile.litpumpkin.name=Jack 'o' Lantern -tile.hellrock.name=Netherrack -tile.hellsand.name=Soul Sand -tile.lightgem.name=Glowstone -tile.portal.name=Portal -tile.cloth.black.name=Black Wool -tile.cloth.red.name=Red Wool -tile.cloth.green.name=Green Wool -tile.cloth.brown.name=Brown Wool -tile.cloth.blue.name=Blue Wool -tile.cloth.purple.name=Purple Wool -tile.cloth.cyan.name=Cyan Wool -tile.cloth.silver.name=Light Gray Wool -tile.cloth.gray.name=Gray Wool -tile.cloth.pink.name=Pink Wool -tile.cloth.lime.name=Lime Wool -tile.cloth.yellow.name=Yellow Wool -tile.cloth.lightBlue.name=Blue Light Wool -tile.cloth.magenta.name=Magenta Wool -tile.cloth.orange.name=Orange Wool -tile.cloth.white.name=Wool -tile.oreLapis.name=Lapis Lazuli Ore -tile.blockLapis.name=Lapis Lazuli Block -tile.dispenser.name=Dispenser -tile.musicBlock.name=Note Block -tile.cake.name=Cake -tile.bed.name=Bed -tile.bed.occupied=This bed is occupied -tile.bed.noSleep=You can only sleep at night -tile.bed.notSafe=You may not rest now, there are monsters nearby -tile.bed.notValid=Your home bed was missing or obstructed -tile.lockedchest.name=Locked chest -tile.trapdoor.name=Trapdoor -tile.web.name=Cobweb -tile.stonebricksmooth.name=Stone Bricks -tile.pistonBase.name=Piston -tile.pistonStickyBase.name=Sticky Piston -tile.fenceIron.name=Iron Bars -tile.melon.name=Melon -tile.stairsBrick.name=Brick Stairs -tile.stairsStoneBrickSmooth.name=Stone Brick Stairs -tile.vine.name=Vines -tile.netherBrick.name=Nether Brick -tile.netherFence.name=Nether Brick Fence -tile.stairsNetherBrick.name=Nether Brick Stairs -tile.netherStalk.name=Nether Wart -tile.cauldron.name=Cauldron -tile.enchantmentTable.name=Enchantment Table -tile.whiteStone.name=End Stone -tile.mycel.name=Mycelium -tile.waterlily.name=Lily Pad -tile.dragonEgg.name=Dragon Egg -tile.redstoneLight.name=Redstone Lamp -tile.glowingobsidian.name=Glowing Obsidian -tile.netherreactor.name=Nether Reactor Core -tile.quartzBlock.name=Block of Quartz -tile.stairsQuartz.name=Quartz Stairs -tile.stairsSandStone.name=Sand Stone Stairs -tile.stonecutter.name=Stonecutter - -item.shovelIron.name=Iron Shovel -item.pickaxeIron.name=Iron Pickaxe -item.hatchetIron.name=Iron Axe -item.flintAndSteel.name=Flint and Steel -item.apple.name=Apple -item.cookie.name=Cookie -item.bow.name=Bow -item.arrow.name=Arrow -item.coal.name=Coal -item.charcoal.name=Charcoal -item.emerald.name=Diamond -item.ingotIron.name=Iron Ingot -item.ingotGold.name=Gold Ingot -item.swordIron.name=Iron Sword -item.swordWood.name=Wooden Sword -item.shovelWood.name=Wooden Shovel -item.pickaxeWood.name=Wooden Pickaxe -item.hatchetWood.name=Wooden Axe -item.swordStone.name=Stone Sword -item.shovelStone.name=Stone Shovel -item.pickaxeStone.name=Stone Pickaxe -item.hatchetStone.name=Stone Axe -item.swordDiamond.name=Diamond Sword -item.shovelDiamond.name=Diamond Shovel -item.pickaxeDiamond.name=Diamond Pickaxe -item.hatchetDiamond.name=Diamond Axe -item.stick.name=Stick -item.bowl.name=Bowl -item.mushroomStew.name=Mushroom Stew -item.swordGold.name=Golden Sword -item.shovelGold.name=Golden Shovel -item.pickaxeGold.name=Golden Pickaxe -item.hatchetGold.name=Golden Axe -item.string.name=String -item.feather.name=Feather -item.sulphur.name=Gunpowder -item.hoeWood.name=Wooden Hoe -item.hoeStone.name=Stone Hoe -item.hoeIron.name=Iron Hoe -item.hoeDiamond.name=Diamond Hoe -item.hoeGold.name=Golden Hoe -item.seeds.name=Seeds -item.seeds_pumpkin.name=Pumpkin Seeds -item.seeds_melon.name=Melon Seeds -item.melon.name=Melon -item.wheat.name=Wheat -item.bread.name=Bread -item.helmetCloth.name=Leather Cap -item.chestplateCloth.name=Leather Tunic -item.leggingsCloth.name=Leather Pants -item.bootsCloth.name=Leather Boots -item.helmetChain.name=Chain Helmet -item.chestplateChain.name=Chain Chestplate -item.leggingsChain.name=Chain Leggings -item.bootsChain.name=Chain Boots -item.helmetIron.name=Iron Helmet -item.chestplateIron.name=Iron Chestplate -item.leggingsIron.name=Iron Leggings -item.bootsIron.name=Iron Boots -item.helmetDiamond.name=Diamond Helmet -item.chestplateDiamond.name=Diamond Chestplate -item.leggingsDiamond.name=Diamond Leggings -item.bootsDiamond.name=Diamond Boots -item.helmetGold.name=Golden Helmet -item.chestplateGold.name=Golden Chestplate -item.leggingsGold.name=Golden Leggings -item.bootsGold.name=Golden Boots -item.flint.name=Flint -item.porkchopRaw.name=Raw Porkchop -item.porkchopCooked.name=Cooked Porkchop -item.chickenRaw.name=Raw Chicken -item.chickenCooked.name=Cooked Chicken -item.beefRaw.name=Raw Beef -item.beefCooked.name=Steak -item.painting.name=Painting -item.appleGold.name=Golden Apple -item.sign.name=Sign -item.doorWood.name=Wooden Door -item.bucket.name=Bucket -item.bucketWater.name=Water Bucket -item.bucketLava.name=Lava Bucket -item.minecart.name=Minecart -item.saddle.name=Saddle -item.doorIron.name=Iron Door -item.redstone.name=Redstone -item.snowball.name=Snowball -item.boat.name=Boat -item.leather.name=Leather -item.milk.name=Milk -item.brick.name=Brick -item.clay.name=Clay -item.reeds.name=Sugar Canes -item.paper.name=Paper -item.book.name=Book -item.slimeball.name=Slimeball -item.minecartChest.name=Minecart with Chest -item.minecartFurnace.name=Minecart with Furnace -item.egg.name=Egg -item.compass.name=Compass -item.fishingRod.name=Fishing Rod -item.clock.name=Clock -item.yellowDust.name=Glowstone Dust -item.fishRaw.name=Raw Fish -item.fishCooked.name=Cooked Fish -item.record.name=Music Disc -item.bone.name=Bone -item.dyePowder.black.name=Ink Sac -item.dyePowder.red.name=Rose Red -item.dyePowder.green.name=Cactus Green -item.dyePowder.brown.name=Cocoa Beans -item.dyePowder.blue.name=Lapis Lazuli -item.dyePowder.purple.name=Purple Dye -item.dyePowder.cyan.name=Cyan Dye -item.dyePowder.silver.name=Light Gray Dye -item.dyePowder.gray.name=Gray Dye -item.dyePowder.pink.name=Pink Dye -item.dyePowder.lime.name=Lime Dye -item.dyePowder.yellow.name=Dandelion Yellow -item.dyePowder.lightBlue.name=Light Blue Dye -item.dyePowder.magenta.name=Magenta Dye -item.dyePowder.orange.name=Orange Dye -item.dyePowder.white.name=Bone Meal -item.sugar.name=Sugar -item.cake.name=Cake -item.bed.name=Bed -item.diode.name=Redstone Repeater -item.map.name=Map -item.leaves.name=Leaves -item.shears.name=Shears -item.rottenFlesh.name=Rotten Flesh -item.enderPearl.name=Ender Pearl -item.blazeRod.name=Blaze Rod -item.ghastTear.name=Ghast Tear -item.netherStalkSeeds.name=Nether Wart -item.potion.name=Potion -item.emptyPotion.name=Water Bottle -item.goldNugget.name=Gold Nugget -item.glassBottle.name=Glass Bottle -item.spiderEye.name=Spider Eye -item.fermentedSpiderEye.name=Fermented Spider Eye -item.blazePowder.name=Blaze Powder -item.magmaCream.name=Magma Cream -item.cauldron.name=Cauldron -item.brewingStand.name=Brewing Stand -item.eyeOfEnder.name=Eye of Ender -item.speckledMelon.name=Glistering Melon -item.monsterPlacer.name=Spawn -item.expBottle.name=Bottle o' Enchanting -item.fireball.name=Fire Charge -item.netherbrickItem.name=Nether Brick -item.netherquartz.name=Nether Quartz - -container.inventory=Inventory -container.crafting=Crafting -container.dispenser=Dispenser -container.furnace=Furnace -container.enchant=Enchant -container.creative=Item Selection -container.brewing=Brewing Stand -container.chest=Chest -container.minecart=Minecart - -entity.Item.name=Item -entity.XPOrb.name=Experience Orb - -entity.Arrow.name=Arrow -entity.Snowball.name=Snowball -entity.Painting.name=Painting - -entity.Mob.name=Mob -entity.Monster.name=Monster - -entity.Creeper.name=Creeper -entity.Skeleton.name=Skeleton -entity.Spider.name=Spider -entity.Giant.name=Giant -entity.Zombie.name=Zombie -entity.Slime.name=Slime -entity.Ghast.name=Ghast -entity.PigZombie.name=Zombie Pigman -entity.Enderman.name=Enderman -entity.Silverfish.name=Silverfish -entity.CaveSpider.name=Cave Spider -entity.Blaze.name=Blaze -entity.LavaSlime.name=Magma Cube -entity.MushroomCow.name=Mooshroom -entity.Villager.name=Villager -entity.VillagerGolem.name=Iron Golem -entity.SnowMan.name=Snow Golem -entity.EnderDragon.name=Ender Dragon - -entity.Pig.name=Pig -entity.Sheep.name=Sheep -entity.Cow.name=Cow -entity.Chicken.name=Chicken -entity.Squid.name=Squid -entity.Wolf.name=Wolf -entity.Ozelot.name=Ocelot -entity.Cat.name=Cat - -entity.PrimedTnt.name=Block of TNT -entity.FallingSand.name=Falling Block - -entity.Minecart.name=Minecart -entity.Boat.name=Boat - -entity.Arrow.name=arrow -entity.generic.name=unknown - -death.inFire=%1$s went up in flames -death.onFire=%1$s burned to death -death.lava=%1$s tried to swim in lava -death.inWall=%1$s suffocated in a wall -death.drown=%1$s drowned -death.starve=%1$s starved to death -death.cactus=%1$s was pricked to death -death.fall=%1$s hit the ground too hard -death.outOfWorld=%1$s fell out of the world -death.generic=%1$s died -death.explosion=%1$s blew up -death.magic=%1$s was killed by magic -death.mob=%1$s was slain by %2$s -death.player=%1$s was slain by %2$s -death.arrow=%1$s was shot by %2$s -death.fireball=%1$s was fireballed by %2$s -death.thrown=%1$s was pummeled by %2$s -death.indirectMagic=%1$s was killed by %2$s - -deathScreen.respawn=Respawn -deathScreen.deleteWorld=Delete world -deathScreen.titleScreen=Title screen -deathScreen.score=Score -deathScreen.title.hardcore=Game over! -deathScreen.hardcoreInfo=You cannot respawn in hardcore mode! -deathScreen.title=You died! - -potion.empty=No Effects -potion.moveSpeed=Speed -potion.moveSlowdown=Slowness -potion.digSpeed=Haste -potion.digSlowDown=Mining Fatigue -potion.damageBoost=Strength -potion.weakness=Weakness -potion.heal=Instant Health -potion.harm=Instant Damage -potion.jump=Jump Boost -potion.confusion=Nausea -potion.regeneration=Regeneration -potion.resistance=Resistance -potion.fireResistance=Fire Resistance -potion.waterBreathing=Water Breathing -potion.invisibility=Invisibility -potion.blindness=Blindness -potion.nightVision=Night Vision -potion.hunger=Hunger -potion.poison=Poison - -potion.moveSpeed.postfix=Potion of Swiftness -potion.moveSlowdown.postfix=Potion of Slowness -potion.digSpeed.postfix=Potion of Haste -potion.digSlowDown.postfix=Potion of Dullness -potion.damageBoost.postfix=Potion of Strength -potion.weakness.postfix=Potion of Weakness -potion.heal.postfix=Potion of Healing -potion.harm.postfix=Potion of Harming -potion.jump.postfix=Potion of Leaping -potion.confusion.postfix=Potion of Nausea -potion.regeneration.postfix=Potion of Regeneration -potion.resistance.postfix=Potion of Resistance -potion.fireResistance.postfix=Potion of Fire Resistance -potion.waterBreathing.postfix=Potion of Water Breathing -potion.invisibility.postfix=Potion of Invisibility -potion.blindness.postfix=Potion of Blindness -potion.nightVision.postfix=Potion of Night Vision -potion.hunger.postfix=Potion of Hunger -potion.poison.postfix=Potion of Poison - -potion.potency.0= -potion.potency.1=II -potion.potency.2=III -potion.potency.3=IV - -potion.prefix.grenade=Splash -potion.prefix.mundane=Mundane -potion.prefix.uninteresting=Uninteresting -potion.prefix.bland=Bland -potion.prefix.clear=Clear -potion.prefix.milky=Milky -potion.prefix.diffuse=Diffuse -potion.prefix.artless=Artless -potion.prefix.thin=Thin -potion.prefix.awkward=Awkward -potion.prefix.flat=Flat -potion.prefix.bulky=Bulky -potion.prefix.bungling=Bungling -potion.prefix.buttered=Buttered -potion.prefix.smooth=Smooth -potion.prefix.suave=Suave -potion.prefix.debonair=Debonair -potion.prefix.thick=Thick -potion.prefix.elegant=Elegant -potion.prefix.fancy=Fancy -potion.prefix.charming=Charming -potion.prefix.dashing=Dashing -potion.prefix.refined=Refined -potion.prefix.cordial=Cordial -potion.prefix.sparkling=Sparkling -potion.prefix.potent=Potent -potion.prefix.foul=Foul -potion.prefix.odorless=Odorless -potion.prefix.rank=Rank -potion.prefix.harsh=Harsh -potion.prefix.acrid=Acrid -potion.prefix.gross=Gross -potion.prefix.stinky=Stinky - -enchantment.damage.all=Sharpness -enchantment.damage.undead=Smite -enchantment.damage.arthropods=Bane of Arthropods -enchantment.knockback=Knockback -enchantment.fire=Fire Aspect -enchantment.protect.all=Protection -enchantment.protect.fire=Fire Protection -enchantment.protect.fall=Feather Falling -enchantment.protect.explosion=Blast Protection -enchantment.protect.projectile=Projectile Protection -enchantment.oxygen=Respiration -enchantment.waterWorker=Aqua Affinity -enchantment.digging=Efficiency -enchantment.untouching=Silk Touch -enchantment.durability=Unbreaking -enchantment.lootBonus=Looting -enchantment.lootBonusDigger=Fortune -enchantment.arrowDamage=Power -enchantment.arrowFire=Flame -enchantment.arrowKnockback=Punch -enchantment.arrowInfinite=Infinity - -enchantment.level.1=I -enchantment.level.2=II -enchantment.level.3=III -enchantment.level.4=IV -enchantment.level.5=V -enchantment.level.6=VI -enchantment.level.7=VII -enchantment.level.8=VIII -enchantment.level.9=IX -enchantment.level.10=X - -gui.achievements=Achievements -gui.stats=Statistics - -stat.generalButton=General -stat.blocksButton=Blocks -stat.itemsButton=Items - -stat.used=Times Used -stat.mined=Times Mined -stat.depleted=Times Depleted -stat.crafted=Times Crafted - -stat.startGame=Times played -stat.createWorld=Worlds played -stat.loadWorld=Saves loaded -stat.joinMultiplayer=Multiplayer joins -stat.leaveGame=Games quit - -stat.playOneMinute=Minutes Played - -stat.walkOneCm=Distance Walked -stat.fallOneCm=Distance Fallen -stat.swimOneCm=Distance Swum -stat.flyOneCm=Distance Flown -stat.climbOneCm=Distance Climbed -stat.diveOneCm=Distance Dove -stat.minecartOneCm=Distance by Minecart -stat.boatOneCm=Distance by Boat -stat.pigOneCm=Distance by Pig -stat.jump=Jumps -stat.drop=Items Dropped - -stat.damageDealt=Damage Dealt -stat.damageTaken=Damage Taken -stat.deaths=Number of Deaths -stat.mobKills=Mob Kills -stat.playerKills=Player Kills -stat.fishCaught=Fish Caught - -stat.mineBlock=%1$s Mined -stat.craftItem=%1$s Crafted -stat.useItem=%1$s Used -stat.breakItem=%1$s Depleted - -achievement.get=Achievement get! - -achievement.taken=Taken! - -achievement.requires=Requires '%1$s' -achievement.openInventory=Taking Inventory -achievement.openInventory.desc=Press '%1$s' to open your inventory. -achievement.mineWood=Getting Wood -achievement.mineWood.desc=Attack a tree until a block of wood pops out -achievement.buildWorkBench=Benchmarking -achievement.buildWorkBench.desc=Craft a workbench with four blocks of planks -achievement.buildPickaxe=Time to Mine! -achievement.buildPickaxe.desc=Use planks and sticks to make a pickaxe -achievement.buildFurnace=Hot Topic -achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks -achievement.acquireIron=Acquire Hardware -achievement.acquireIron.desc=Smelt an iron ingot -achievement.buildHoe=Time to Farm! -achievement.buildHoe.desc=Use planks and sticks to make a hoe -achievement.makeBread=Bake Bread -achievement.makeBread.desc=Turn wheat into bread -achievement.bakeCake=The Lie -achievement.bakeCake.desc=Wheat, sugar, milk and eggs! -achievement.buildBetterPickaxe=Getting an Upgrade -achievement.buildBetterPickaxe.desc=Construct a better pickaxe -achievement.cookFish=Delicious Fish -achievement.cookFish.desc=Catch and cook fish! -achievement.onARail=On A Rail -achievement.onARail.desc=Travel by minecart at least 1 km from where you started -achievement.buildSword=Time to Strike! -achievement.buildSword.desc=Use planks and sticks to make a sword -achievement.killEnemy=Monster Hunter -achievement.killEnemy.desc=Attack and destroy a monster -achievement.killCow=Cow Tipper -achievement.killCow.desc=Harvest some leather -achievement.flyPig=When Pigs Fly -achievement.flyPig.desc=Fly a pig off a cliff -achievement.snipeSkeleton=Sniper Duel -achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters -achievement.diamonds=DIAMONDS! -achievement.diamonds.desc=Acquire diamonds with your iron tools -achievement.portal=We Need to Go Deeper -achievement.portal.desc=Build a portal to the Nether -achievement.ghast=Return to Sender -achievement.ghast.desc=Destroy a Ghast with a fireball -achievement.blazeRod=Into Fire -achievement.blazeRod.desc=Relieve a Blaze of its rod -achievement.potion=Local Brewery -achievement.potion.desc=Brew a potion -achievement.theEnd=The End? -achievement.theEnd.desc=Locate the End -achievement.theEnd2=The End. -achievement.theEnd2.desc=Defeat the Ender Dragon -achievement.enchantments=Enchanter -achievement.enchantments.desc=Use a book, obsidian and diamonds to construct an enchantment table -achievement.overkill=Overkill -achievement.overkill.desc=Deal eight hearts of damage in a single hit -achievement.bookcase=Librarian -achievement.bookcase.desc=Build some bookshelves to improve your enchantment table - -desc.sword=Deals more damage than by hand. -desc.shovel=Used to dig dirt, grass, sand, gravel and snow faster than by hand. -desc.pickaxe=Required to mine stone-related blocks and ore. -desc.hatchet=Used to chop wood-related blocks faster than by hand. -desc.hoe=Used to till dirt and grass blocks to prepare for crops. -desc.doorwood=Wooden doors are activated by using, hitting them or with Redstone. -desc.dooriron=Iron doors can only be opened by Redstone, buttons or switches. -desc.helmet=Gives the user 1.5 Armor when worn. -desc.chestplate=Gives the user 4 Armor when worn. -desc.leggings=Gives the user 3 Armor when worn. -desc.boots=Gives the user 1.5 Armor when worn. -desc.ingot=A shiny ingot which can be used to craft tools made from this material. -desc.block=Used as an expensive building block or compact storage of the ore. -desc.pressureplate=Used to send an electrical charge when stepped on by a player, an animal, or a monster. Wooden Pressure Plates can also be activated by dropping something on them. -desc.stairs=Used for compact staircases. -desc.stairsstonebricksmooth=Used for compact staircases. -desc.stairsnetherbrick=Used for compact staircases. -desc.stairssandstone=Used for compact staircases. -desc.stairsquartzblock=Used for compact staircases. -desc.slab=Used for making long staircases. -desc.halfslab=Used for compact staircases. -desc.torch=Used to create light. Torches also melt snow and ice. -desc.wood=Used as a building material and can be crafted into many things. Can be crafted from any wood. -desc.sandstone=Used as a building material. -desc.sandstone.addon=Is not influenced by gravity like normal Sand. -desc.structblock=Used as a building material. -desc.stick=Used to craft torches, arrows, signs, ladders, fences and as handles for tools and weapons. -desc.bed=Used to sleep until dawn. Changes your spawn point to the bed's position. -desc.craftingtable=Allows you to craft a more varied selection of items than the normal crafting. -desc.furnace=Allows you to smelt ore, create charcoal and glass, and cook fish and porkchops. -desc.chest=Stores blocks and items inside. -desc.fence=Used as a barrier that cannot be jumped over. -desc.ladder=Used to climb vertically. -desc.trapdoor=Work like normal doors, but are a one by one block and lay flat on the ground. -desc.sign=Shows text entered by you or other players. -desc.glowstone=Used to create brighter light than torches. Melts snow/ice and can be used underwater. -desc.tnt=Used to cause explosions. Activated after placing by hitting, or with an electrical charge. -desc.bowl=Used to hold mushroom stew. You keep the bowl when the stew has been eaten. -desc.bucket=Used to hold and transport water, lava and milk. -desc.flintandsteel=Used to detonate TNT. -desc.fishingrod=Used to catch fish. -desc.clock=Displays positions of the Sun and Moon. -desc.compass=Points to your start point. -desc.map=Will create an image of an area explored while held. This can be used for path-finding. -desc.bow=Allows for ranged attacks by using arrows. -desc.arrow=Used as ammunition for bows. -desc.mushroomstew=Restores 5 hearts. -desc.cookie=Restores 0.5 Hearts. Can be stacked up to 8 times. -desc.cake=Restores 1.5 Hearts. Can be used 6 times. -desc.sugar=Used in the cake recipe. -desc.bread=Restores 2.5 Hearts. -desc.goldenapple=Restores 10 Hearts. -desc.lever=Used to send an electrical charge by being turned on or off. Stays in the on or off state until pressed again. -desc.redstonetorch=Constantly sends an electrical charge, or can be used as a receiver/transmitter when connected to the side of a block. -Can also be used for low-level lighting. -desc.redstonerepeater=Used in Redstone circuits as repeater, a delayer, and/or a diode -desc.button=Used to send an electrical charge by being pressed. Stays activated for approximately a second before shutting off again. -desc.dispenser=Used to hold and shoot out items in a random order when given a Redstone charge. -desc.noteblock=Plays a note when triggered. Hit it to change the pitch of the note. Placing this on top of different blocks will change the type of instrument. -desc.rail=Used to guide minecarts. -desc.poweredrail=When powered, accelerates minecarts that pass over it. When unpowered, causes minecarts to stop on it. -desc.detectorrail=Functions like a Pressure Plate (sends a Redstone signal when powered) but can only be activated by a minecart. -desc.minecart=Used to transport you, an animal, or a monster along rails. -desc.minecartwithchest=Used to transport goods along rails. -desc.minecartwithfurnace=Will move along rails and can push other minecarts when coal is put in it. -desc.boat=Used to travel in water more quickly than swimming. -desc.wool=Collected from sheep, and can be colored with dyes. -desc.woolstring=Used as a building material and can be colored with dyes. Can be easily obtained from Sheep. -desc.dyepowder.black=Used as a dye to create black wool. -desc.dyepowder.green=Used as a dye to create green wool. -desc.dyepowder.brown=Used as a dye to create brown wool. -desc.dyepowder.silver=Used as a dye to create silver wool. -desc.dyepowder.yellow=Used as a dye to create yellow wool. -desc.dyepowder.red=Used as a dye to create red wool. -desc.dyepowder.white=Used to instantly grow crops and flowers. Can be used in dye recipes. -desc.dyepowder.pink=Used as a dye to create pink wool. -desc.dyepowder.orange=Used as a dye to create orange wool. -desc.dyepowder.lime=Used as a dye to create lime wool. -desc.dyepowder.gray=Used as a dye to create gray wool. -desc.dyepowder.lightgray=Used as a dye to create light gray wool. -(Note: light gray dye can also be made by combining gray dye with bone meal, letting you make four light gray dyes from every ink sac instead of three.) -desc.dyepowder.lightblue=Used as a dye to create light blue wool. -desc.dyepowder.cyan=Used as a dye to create cyan wool. -desc.dyepowder.purple=Used as a dye to create purple wool. -desc.dyepowder.magenta=Used as a dye to create magenta wool. -desc.dyepowder.blue=Used as dye to create Blue Wool. -desc.jukebox=Plays Music Discs. -desc.diamonds=Use these to create very strong tools, weapons or armor -desc.emerald=Use these to create very strong tools, weapons or armor -desc.jackolantern=Used to create brighter light than torches. Melts snow/ice and can be used underwater. -desc.paper=Used to create books and maps. -desc.book=Used to create a bookshelf. -desc.bookshelf=Used as decoration. -desc.picture=Used as decoration. -desc.ore_gold=Can be mined with an iron pickaxe or better, then smelted in a furnace to produce gold ingots. -desc.ore_iron=Can be mined with a stone pickaxe or better, then smelted in a furnace to produce iron ingots. -desc.ore_coal=Can be mined with a pickaxe to collect coal. -desc.ore_lapis=Can be mined with a stone pickaxe or better to collect lapis lazuli. -desc.ore_diamond=Can be mined with an iron pickaxe or better to collect diamonds. -desc.ore_redstone=Can be mined with an iron pickaxe or better to collect redstone dust. -desc.stone=Can be mined with a pickaxe to collect cobblestone. -desc.dirt=Collected using a shovel. Can be used for construction. -desc.sapling=Can be planted and it will eventually grow into a tree. -desc.bedrock=This cannot be broken. -desc.lava=Sets fire to anything that touches it. Can be collected in a bucket. -desc.sand=Collected using a shovel. Can be smelted into glass using the furnace. Is affected by gravity if there is no other tile underneath it. -desc.gravel=Collected using a shovel. Sometimes produces flint when dug up. Is affected by gravity if there is no other tile underneath it. -desc.log=Chopped using an axe, and can be crafted into planks or used as a fuel. -desc.glass=Created in a furnace by smelting sand. Will break if you try to mine it. -desc.thinglass=Will break if you try to mine it. -desc.stone_brick=Mined from stone using a pickaxe. Can be used to construct a furnace or stone tools. -desc.brick=Baked from clay in a furnace. -desc.clay=Can be baked into bricks in a furnace. -desc.snow=A compact way to store snowballs. -desc.top_snow=Can be dug with a shovel to create snowballs. -desc.tall_grass=Sometimes produces seeds when broken. -desc.flower=Can be crafted into a dye. -desc.mushroom=Can be crafted with a bowl to make stew. -desc.obsidian=Can only be mined with a diamond pickaxe. Is produced by the meeting of water and still lava, and is used to build a portal. -desc.mob_spawner=Spawns monsters into the world. -desc.redstone_dust=Is placed on the ground to carry an electrical charge. -desc.crops=When fully grown, crops can be harvested to collect wheat. -desc.farmland=Ground that has been prepared ready to plant seeds. -desc.cactus=Can be crafted to create a dye. -desc.reeds=Can be crafted to create sugar. -desc.pumpkin=Can be worn as a helmet or crafted with a torch to create a Jack-O-Lantern. -desc.hell_rock=Burns forever if set alight. -desc.hell_sand=Slows the movement of anything walking over it. -desc.portal=Standing in the portal allows you to pass between the overworld and the nether. -desc.apple=Restores health, and can be crafted into a golden apple. -desc.melon=Can be broken into melon slices. -desc.coal=Used as a fuel in a furnace, or crafted to make a torch. -desc.string=Collected by killing a spider, and can be crafted into a bow. -desc.feather=Collected by killing a chicken, and can be crafted into an arrow. -desc.sulphur=Collected by killing a creeper, and can be crafted into TNT. -desc.shears=Used to obtain wool from sheep and to harvest placeable Leaf blocks. -desc.seeds=Can be planted in farmland to grow crops. -desc.wheat=Harvested from crops, and can be used to craft food items. -desc.flint=Collected by digging gravel, and can be used to craft a flint and steel. -desc.porkchopraw=Collected by killing a pig, and can be cooked in a furnace. Restores health. -desc.porkchopcooked=Created by cooking a porkchop in a furnace. Restores health. -desc.beefraw=Collected by killing a cow, and can be cooked in a furnace. Restores health. -desc.beefcooked=Created by cooking a beef in a furnace. Restores health. -desc.chickenraw=Collected by killing a chicken, and can be cooked in a furnace. Restores health. -desc.chickencooked=Created by cooking a chicken in a furnace. Restores health. -desc.saddle=When used on a pig it allows you to ride the pig. -desc.snowball=Collected by digging snow, and can be thrown. -desc.leather=Collected by killing a cow, and can be crafted into armor. -desc.slimeball=Collected by killing a slime. -desc.egg=Dropped randomly by chickens, and can be crafted into food items. -desc.yellow_dust=Collected by mining Glowstone, and can be crafted to make Glowstone blocks again. -desc.fish_raw=Can be eaten to restore health, or cooked in a furnace. -desc.fish_cooked=Created by cooking a raw fish in a furnace. Can be eaten to restore health. -desc.bone=Collected by killing a skeleton. Can be crafted into bone meal. -desc.record=Collected by getting a skeleton to kill a creeper. Can be played in a jukebox. -desc.water=Extinguishes fire and helps crops grow. Can be collected in a bucket. -desc.leaves=When broken sometimes drops a sapling which can then be replanted to grow into a tree. -desc.moss_stone=Can be used for construction and decoration. -desc.stonebricksmooth=Used as building material. -desc.netherreactor=Core of the Nether Reactor -desc.glowingobsidian=Obsidion activated by external source. -desc.lightgem=Used for illumination. -desc.painting=Used as decoration. -desc.hellrock=Is a stone material from the Nether. -desc.netherbrick=Construction block made from Nether bricks. -desc.netherbrickitem=Used to form blocks of Nether bricks. -desc.netherquartz=Quartz from the Nether, used to create Blocks of Quartz. -desc.quartzblock=Decorative block, used to create other kinds of Quartz blocks. -desc.stonecutter=For crafting stoneblocks. -desc.seedsmelon=Allows for more melons. -desc.tile.melon=Can be cut up and eaten. + +language.name=English +language.region=US +language.code=en_US + +gui.done=Done +gui.cancel=Cancel +gui.toMenu=Back to title screen +gui.up=Up +gui.down=Down +gui.yes=Yes +gui.no=No + +menu.singleplayer=Singleplayer +menu.multiplayer=Multiplayer +menu.mods=Texture Packs +menu.options=Options... +menu.quit=Quit Game +menu.returnToMenu=Save and Quit to Title +menu.disconnect=Disconnect +menu.returnToGame=Back to Game +menu.switchingLevel=Switching level +menu.generatingLevel=Generating level +menu.loadingLevel=Loading level +menu.generatingTerrain=Building terrain +menu.simulating=Simulating the world for a bit +menu.respawning=Respawning + +selectWorld.title=Select World +selectWorld.empty=empty +selectWorld.world=World +selectWorld.select=Play Selected World +selectWorld.create=Create New World +selectWorld.createDemo=Play New Demo World +selectWorld.delete=Delete +selectWorld.rename=Rename +selectWorld.deleteQuestion=Are you sure you want to delete this world? +selectWorld.deleteWarning=will be lost forever! (A long time!) +selectWorld.deleteButton=Delete +selectWorld.renameButton=Rename +selectWorld.renameTitle=Rename World +selectWorld.conversion=Must be converted! +selectWorld.newWorld=New World +selectWorld.enterName=World Name +selectWorld.resultFolder=Will be saved in: +selectWorld.enterSeed=Seed for the World Generator +selectWorld.seedInfo=Leave blank for a random seed + +gameMode.survival=Survival Mode +gameMode.creative=Creative Mode +gameMode.hardcore=Hardcore Mode! +gameMode.changed=Your game mode has been changed + +selectWorld.gameMode=Game Mode: +selectWorld.gameMode.survival=Survival +selectWorld.gameMode.survival.line1=Search for resources, crafting, gain +selectWorld.gameMode.survival.line2=levels, health and hunger +selectWorld.gameMode.creative=Creative +selectWorld.gameMode.creative.line1=Unlimited resources, free flying and +selectWorld.gameMode.creative.line2=destroy blocks instantly +selectWorld.gameMode.hardcore=Hardcore +selectWorld.gameMode.hardcore.line1=Same as survival mode, locked at hardest +selectWorld.gameMode.hardcore.line2=difficulty, and one life only +selectWorld.moreWorldOptions=More World Options... +selectWorld.mapFeatures=Generate Structures: +selectWorld.mapFeatures.info=Villages, dungeons etc +selectWorld.mapType=World Type: +selectWorld.mapType.normal=Normal +selectWorld.hardcoreMode=Hardcore: +selectWorld.hardcoreMode.info=World is deleted upon death + +generator.default=Default +generator.flat=Superflat + +selectServer.title=Select Server +selectServer.empty=empty +selectServer.select=Join Server +selectServer.direct=Direct Connect +selectServer.edit=Edit +selectServer.delete=Delete +selectServer.add=Add server +selectServer.defaultName=Minecraft Server +selectServer.deleteQuestion=Are you sure you want to remove this server? +selectServer.deleteWarning=will be lost forever! (A long time!) +selectServer.deleteButton=Delete +selectServer.refresh=Refresh +addServer.title=Edit Server Info +addServer.enterName=Server Name +addServer.enterIp=Server Address +addServer.add=Done + +multiplayer.title=Play Multiplayer +multiplayer.connect=Connect +multiplayer.info1=Minecraft Multiplayer is currently not finished, but there +multiplayer.info2=is some buggy early testing going on. +multiplayer.ipinfo=Enter the IP of a server to connect to it: + +multiplayer.downloadingTerrain=Downloading terrain + +multiplayer.stopSleeping=Leave Bed + +demo.day.1=This demo will last five game days, do your best! +demo.day.2=Day Two +demo.day.3=Day Three +demo.day.4=Day Four +demo.day.5=This is your last day! +demo.day.warning=Your time is almost up! +demo.day.6=You have passed your fifth day, use F2 to save a screenshot of your creation +demo.reminder=The demo time has expired, buy the game to continue or start a new world! +demo.help.movement=Use %1$s, %2$s, %3$s, %4$s and the mouse to move around +demo.help.jump=Jump by pressing %1$s +demo.help.inventory=Use %1$s to open your inventory + +connect.connecting=Connecting to the server... +connect.authorizing=Logging in... +connect.failed=Failed to connect to the server + +disconnect.genericReason=%s +disconnect.disconnected=Disconnected by Server +disconnect.lost=Connection Lost +disconnect.kicked=Was kicked from the game +disconnect.timeout=Timed out +disconnect.closed=Connection closed +disconnect.loginFailed=Failed to login +disconnect.loginFailedInfo=Failed to login: %s +disconnect.quitting=Quitting +disconnect.endOfStream=End of stream +disconnect.overflow=Buffer overflow + +options.off=OFF +options.on=ON +options.title=Options +options.controls=Controls... +options.video=Video Settings... +options.language=Language... +options.languageWarning=Language translations may not be 100% accurate +options.videoTitle=Video Settings +options.music=Music +options.sound=Sound +options.invertMouse=Invert Mouse +options.fov=FOV +options.fov.min=Normal +options.fov.max=Quake Pro +options.gamma=Brightness +options.gamma.min=Moody +options.gamma.max=Bright +options.sensitivity=Sensitivity +options.sensitivity.min=*yawn* +options.sensitivity.max=HYPERSPEED!!! +options.renderDistance=Render Distance +options.renderDistance.tiny=Tiny +options.renderDistance.short=Short +options.renderDistance.normal=Normal +options.renderDistance.far=Far +options.viewBobbing=View Bobbing +options.ao=Smooth Lighting +options.anaglyph=3D Anaglyph +options.framerateLimit=Performance +options.difficulty=Difficulty +options.difficulty.peaceful=Peaceful +options.difficulty.easy=Easy +options.difficulty.normal=Normal +options.difficulty.hard=Hard +options.difficulty.hardcore=Hardcore +options.graphics=Graphics +options.graphics.fancy=Fancy +options.graphics.fast=Fast +options.guiScale=GUI Scale +options.guiScale.auto=Auto +options.guiScale.small=Small +options.guiScale.medium=Medium +options.guiScale.large=Large +options.guiScale.larger=Larger +options.guiScale.largest=Largest +options.advancedOpengl=Advanced OpenGL +options.renderClouds=Clouds +options.farWarning1=A 64 bit Java installation is recommended +options.farWarning2=for 'Far' render distance (you have 32 bit) +options.particles=Particles +options.particles.all=All +options.particles.decreased=Decreased +options.particles.minimal=Minimal + +performance.max=Max FPS +performance.balanced=Balanced +performance.powersaver=Power saver + +controls.title=Controls + +key.forward=Forward +key.left=Left +key.back=Back +key.right=Right +key.jump=Jump +key.inventory=Inventory +key.drop=Drop +key.chat=Chat +key.fog=Toggle Fog +key.sneak=Sneak +key.playerlist=List Players +key.attack=Attack +key.use=Use Item +key.pickItem=Pick Block +key.mouseButton=Button %1$s + +texturePack.openFolder=Open texture pack folder +texturePack.title=Select Texture Pack +texturePack.folderInfo=(Place texture pack files here) + +tile.stone.name=Stone +tile.grass.name=Grass Block +tile.dirt.name=Dirt +tile.stonebrick.name=Cobblestone +tile.wood.name=Wooden Planks +tile.sapling.name=Sapling +tile.deadbush.name=Dead Bush +tile.bedrock.name=Bedrock +tile.water.name=Water +tile.lava.name=Lava +tile.sand.name=Sand +tile.sandStone.name=Sandstone +tile.gravel.name=Gravel +tile.oreGold.name=Gold Ore +tile.oreIron.name=Iron Ore +tile.oreCoal.name=Coal Ore +tile.log.name=Wood +tile.leaves.name=Leaves +tile.tallgrass.name=Grass +tile.tallgrass.shrub.name=Shrub +tile.tallgrass.grass.name=Grass +tile.tallgrass.fern.name=Fern +tile.sponge.name=Sponge +tile.glass.name=Glass +tile.thinGlass.name=Glass Pane +tile.cloth.name=Wool +tile.flower.name=Flower +tile.rose.name=Rose +tile.mushroom.name=Mushroom +tile.blockGold.name=Block of Gold +tile.blockIron.name=Block of Iron +tile.stoneSlab.stone.name=Stone Slab +tile.stoneSlab.sand.name=Sandstone Slab +tile.stoneSlab.wood.name=Wooden Slab +tile.stoneSlab.cobble.name=Cobblestone Slab +tile.stoneSlab.brick.name=Bricks Slab +tile.stoneSlab.smoothStoneBrick.name=Stone Bricks Slab +tile.brick.name=Bricks +tile.tnt.name=TNT +tile.bookshelf.name=Bookshelf +tile.stoneMoss.name=Moss Stone +tile.obsidian.name=Obsidian +tile.torch.name=Torch +tile.fire.name=Fire +tile.mobSpawner.name=Monster Spawner +tile.stairsWood.name=Wooden Stairs +tile.chest.name=Chest +tile.redstoneDust.name=Redstone Dust +tile.oreDiamond.name=Diamond Ore +tile.blockDiamond.name=Block of Diamond +tile.workbench.name=Crafting Table +tile.crops.name=Crops +tile.farmland.name=Farmland +tile.furnace.name=Furnace +tile.sign.name=Sign +tile.doorWood.name=Wooden Door +tile.ladder.name=Ladder +tile.rail.name=Rail +tile.goldenRail.name=Powered Rail +tile.detectorRail.name=Detector Rail +tile.stairsStone.name=Stone Stairs +tile.lever.name=Lever +tile.pressurePlate.name=Pressure Plate +tile.doorIron.name=Iron Door +tile.oreRedstone.name=Redstone Ore +tile.notGate.name=Redstone Torch +tile.button.name=Button +tile.snow.name=Snow +tile.ice.name=Ice +tile.cactus.name=Cactus +tile.clay.name=Clay +tile.reeds.name=Sugar cane +tile.jukebox.name=Jukebox +tile.fence.name=Fence +tile.fenceGate.name=Fence Gate +tile.pumpkin.name=Pumpkin +tile.litpumpkin.name=Jack 'o' Lantern +tile.hellrock.name=Netherrack +tile.hellsand.name=Soul Sand +tile.lightgem.name=Glowstone +tile.portal.name=Portal +tile.cloth.black.name=Black Wool +tile.cloth.red.name=Red Wool +tile.cloth.green.name=Green Wool +tile.cloth.brown.name=Brown Wool +tile.cloth.blue.name=Blue Wool +tile.cloth.purple.name=Purple Wool +tile.cloth.cyan.name=Cyan Wool +tile.cloth.silver.name=Light Gray Wool +tile.cloth.gray.name=Gray Wool +tile.cloth.pink.name=Pink Wool +tile.cloth.lime.name=Lime Wool +tile.cloth.yellow.name=Yellow Wool +tile.cloth.lightBlue.name=Blue Light Wool +tile.cloth.magenta.name=Magenta Wool +tile.cloth.orange.name=Orange Wool +tile.cloth.white.name=Wool +tile.oreLapis.name=Lapis Lazuli Ore +tile.blockLapis.name=Lapis Lazuli Block +tile.dispenser.name=Dispenser +tile.musicBlock.name=Note Block +tile.cake.name=Cake +tile.bed.name=Bed +tile.bed.occupied=This bed is occupied +tile.bed.noSleep=You can only sleep at night +tile.bed.notSafe=You may not rest now, there are monsters nearby +tile.bed.notValid=Your home bed was missing or obstructed +tile.lockedchest.name=Locked chest +tile.trapdoor.name=Trapdoor +tile.web.name=Cobweb +tile.stonebricksmooth.name=Stone Bricks +tile.pistonBase.name=Piston +tile.pistonStickyBase.name=Sticky Piston +tile.fenceIron.name=Iron Bars +tile.melon.name=Melon +tile.stairsBrick.name=Brick Stairs +tile.stairsStoneBrickSmooth.name=Stone Brick Stairs +tile.vine.name=Vines +tile.netherBrick.name=Nether Brick +tile.netherFence.name=Nether Brick Fence +tile.stairsNetherBrick.name=Nether Brick Stairs +tile.netherStalk.name=Nether Wart +tile.cauldron.name=Cauldron +tile.enchantmentTable.name=Enchantment Table +tile.whiteStone.name=End Stone +tile.mycel.name=Mycelium +tile.waterlily.name=Lily Pad +tile.dragonEgg.name=Dragon Egg +tile.redstoneLight.name=Redstone Lamp +tile.glowingobsidian.name=Glowing Obsidian +tile.netherreactor.name=Nether Reactor Core +tile.quartzBlock.name=Block of Quartz +tile.stairsQuartz.name=Quartz Stairs +tile.stairsSandStone.name=Sand Stone Stairs +tile.stonecutter.name=Stonecutter + +item.shovelIron.name=Iron Shovel +item.pickaxeIron.name=Iron Pickaxe +item.hatchetIron.name=Iron Axe +item.flintAndSteel.name=Flint and Steel +item.apple.name=Apple +item.cookie.name=Cookie +item.bow.name=Bow +item.arrow.name=Arrow +item.coal.name=Coal +item.charcoal.name=Charcoal +item.emerald.name=Diamond +item.ingotIron.name=Iron Ingot +item.ingotGold.name=Gold Ingot +item.swordIron.name=Iron Sword +item.swordWood.name=Wooden Sword +item.shovelWood.name=Wooden Shovel +item.pickaxeWood.name=Wooden Pickaxe +item.hatchetWood.name=Wooden Axe +item.swordStone.name=Stone Sword +item.shovelStone.name=Stone Shovel +item.pickaxeStone.name=Stone Pickaxe +item.hatchetStone.name=Stone Axe +item.swordDiamond.name=Diamond Sword +item.shovelDiamond.name=Diamond Shovel +item.pickaxeDiamond.name=Diamond Pickaxe +item.hatchetDiamond.name=Diamond Axe +item.stick.name=Stick +item.bowl.name=Bowl +item.mushroomStew.name=Mushroom Stew +item.swordGold.name=Golden Sword +item.shovelGold.name=Golden Shovel +item.pickaxeGold.name=Golden Pickaxe +item.hatchetGold.name=Golden Axe +item.string.name=String +item.feather.name=Feather +item.sulphur.name=Gunpowder +item.hoeWood.name=Wooden Hoe +item.hoeStone.name=Stone Hoe +item.hoeIron.name=Iron Hoe +item.hoeDiamond.name=Diamond Hoe +item.hoeGold.name=Golden Hoe +item.seeds.name=Seeds +item.seeds_pumpkin.name=Pumpkin Seeds +item.seeds_melon.name=Melon Seeds +item.melon.name=Melon +item.wheat.name=Wheat +item.bread.name=Bread +item.helmetCloth.name=Leather Cap +item.chestplateCloth.name=Leather Tunic +item.leggingsCloth.name=Leather Pants +item.bootsCloth.name=Leather Boots +item.helmetChain.name=Chain Helmet +item.chestplateChain.name=Chain Chestplate +item.leggingsChain.name=Chain Leggings +item.bootsChain.name=Chain Boots +item.helmetIron.name=Iron Helmet +item.chestplateIron.name=Iron Chestplate +item.leggingsIron.name=Iron Leggings +item.bootsIron.name=Iron Boots +item.helmetDiamond.name=Diamond Helmet +item.chestplateDiamond.name=Diamond Chestplate +item.leggingsDiamond.name=Diamond Leggings +item.bootsDiamond.name=Diamond Boots +item.helmetGold.name=Golden Helmet +item.chestplateGold.name=Golden Chestplate +item.leggingsGold.name=Golden Leggings +item.bootsGold.name=Golden Boots +item.flint.name=Flint +item.porkchopRaw.name=Raw Porkchop +item.porkchopCooked.name=Cooked Porkchop +item.chickenRaw.name=Raw Chicken +item.chickenCooked.name=Cooked Chicken +item.beefRaw.name=Raw Beef +item.beefCooked.name=Steak +item.painting.name=Painting +item.appleGold.name=Golden Apple +item.sign.name=Sign +item.doorWood.name=Wooden Door +item.bucket.name=Bucket +item.bucketWater.name=Water Bucket +item.bucketLava.name=Lava Bucket +item.minecart.name=Minecart +item.saddle.name=Saddle +item.doorIron.name=Iron Door +item.redstone.name=Redstone +item.snowball.name=Snowball +item.boat.name=Boat +item.leather.name=Leather +item.milk.name=Milk +item.brick.name=Brick +item.clay.name=Clay +item.reeds.name=Sugar Canes +item.paper.name=Paper +item.book.name=Book +item.slimeball.name=Slimeball +item.minecartChest.name=Minecart with Chest +item.minecartFurnace.name=Minecart with Furnace +item.egg.name=Egg +item.compass.name=Compass +item.fishingRod.name=Fishing Rod +item.clock.name=Clock +item.yellowDust.name=Glowstone Dust +item.fishRaw.name=Raw Fish +item.fishCooked.name=Cooked Fish +item.record.name=Music Disc +item.bone.name=Bone +item.dyePowder.black.name=Ink Sac +item.dyePowder.red.name=Rose Red +item.dyePowder.green.name=Cactus Green +item.dyePowder.brown.name=Cocoa Beans +item.dyePowder.blue.name=Lapis Lazuli +item.dyePowder.purple.name=Purple Dye +item.dyePowder.cyan.name=Cyan Dye +item.dyePowder.silver.name=Light Gray Dye +item.dyePowder.gray.name=Gray Dye +item.dyePowder.pink.name=Pink Dye +item.dyePowder.lime.name=Lime Dye +item.dyePowder.yellow.name=Dandelion Yellow +item.dyePowder.lightBlue.name=Light Blue Dye +item.dyePowder.magenta.name=Magenta Dye +item.dyePowder.orange.name=Orange Dye +item.dyePowder.white.name=Bone Meal +item.sugar.name=Sugar +item.cake.name=Cake +item.bed.name=Bed +item.diode.name=Redstone Repeater +item.map.name=Map +item.leaves.name=Leaves +item.shears.name=Shears +item.rottenFlesh.name=Rotten Flesh +item.enderPearl.name=Ender Pearl +item.blazeRod.name=Blaze Rod +item.ghastTear.name=Ghast Tear +item.netherStalkSeeds.name=Nether Wart +item.potion.name=Potion +item.emptyPotion.name=Water Bottle +item.goldNugget.name=Gold Nugget +item.glassBottle.name=Glass Bottle +item.spiderEye.name=Spider Eye +item.fermentedSpiderEye.name=Fermented Spider Eye +item.blazePowder.name=Blaze Powder +item.magmaCream.name=Magma Cream +item.cauldron.name=Cauldron +item.brewingStand.name=Brewing Stand +item.eyeOfEnder.name=Eye of Ender +item.speckledMelon.name=Glistering Melon +item.monsterPlacer.name=Spawn +item.expBottle.name=Bottle o' Enchanting +item.fireball.name=Fire Charge +item.netherbrickItem.name=Nether Brick +item.netherquartz.name=Nether Quartz + +container.inventory=Inventory +container.crafting=Crafting +container.dispenser=Dispenser +container.furnace=Furnace +container.enchant=Enchant +container.creative=Item Selection +container.brewing=Brewing Stand +container.chest=Chest +container.minecart=Minecart + +entity.Item.name=Item +entity.XPOrb.name=Experience Orb + +entity.Arrow.name=Arrow +entity.Snowball.name=Snowball +entity.Painting.name=Painting + +entity.Mob.name=Mob +entity.Monster.name=Monster + +entity.Creeper.name=Creeper +entity.Skeleton.name=Skeleton +entity.Spider.name=Spider +entity.Giant.name=Giant +entity.Zombie.name=Zombie +entity.Slime.name=Slime +entity.Ghast.name=Ghast +entity.PigZombie.name=Zombie Pigman +entity.Enderman.name=Enderman +entity.Silverfish.name=Silverfish +entity.CaveSpider.name=Cave Spider +entity.Blaze.name=Blaze +entity.LavaSlime.name=Magma Cube +entity.MushroomCow.name=Mooshroom +entity.Villager.name=Villager +entity.VillagerGolem.name=Iron Golem +entity.SnowMan.name=Snow Golem +entity.EnderDragon.name=Ender Dragon + +entity.Pig.name=Pig +entity.Sheep.name=Sheep +entity.Cow.name=Cow +entity.Chicken.name=Chicken +entity.Squid.name=Squid +entity.Wolf.name=Wolf +entity.Ozelot.name=Ocelot +entity.Cat.name=Cat + +entity.PrimedTnt.name=Block of TNT +entity.FallingSand.name=Falling Block + +entity.Minecart.name=Minecart +entity.Boat.name=Boat + +entity.Arrow.name=arrow +entity.generic.name=unknown + +death.inFire=%1$s went up in flames +death.onFire=%1$s burned to death +death.lava=%1$s tried to swim in lava +death.inWall=%1$s suffocated in a wall +death.drown=%1$s drowned +death.starve=%1$s starved to death +death.cactus=%1$s was pricked to death +death.fall=%1$s hit the ground too hard +death.outOfWorld=%1$s fell out of the world +death.generic=%1$s died +death.explosion=%1$s blew up +death.magic=%1$s was killed by magic +death.mob=%1$s was slain by %2$s +death.player=%1$s was slain by %2$s +death.arrow=%1$s was shot by %2$s +death.fireball=%1$s was fireballed by %2$s +death.thrown=%1$s was pummeled by %2$s +death.indirectMagic=%1$s was killed by %2$s + +deathScreen.respawn=Respawn +deathScreen.deleteWorld=Delete world +deathScreen.titleScreen=Title screen +deathScreen.score=Score +deathScreen.title.hardcore=Game over! +deathScreen.hardcoreInfo=You cannot respawn in hardcore mode! +deathScreen.title=You died! + +potion.empty=No Effects +potion.moveSpeed=Speed +potion.moveSlowdown=Slowness +potion.digSpeed=Haste +potion.digSlowDown=Mining Fatigue +potion.damageBoost=Strength +potion.weakness=Weakness +potion.heal=Instant Health +potion.harm=Instant Damage +potion.jump=Jump Boost +potion.confusion=Nausea +potion.regeneration=Regeneration +potion.resistance=Resistance +potion.fireResistance=Fire Resistance +potion.waterBreathing=Water Breathing +potion.invisibility=Invisibility +potion.blindness=Blindness +potion.nightVision=Night Vision +potion.hunger=Hunger +potion.poison=Poison + +potion.moveSpeed.postfix=Potion of Swiftness +potion.moveSlowdown.postfix=Potion of Slowness +potion.digSpeed.postfix=Potion of Haste +potion.digSlowDown.postfix=Potion of Dullness +potion.damageBoost.postfix=Potion of Strength +potion.weakness.postfix=Potion of Weakness +potion.heal.postfix=Potion of Healing +potion.harm.postfix=Potion of Harming +potion.jump.postfix=Potion of Leaping +potion.confusion.postfix=Potion of Nausea +potion.regeneration.postfix=Potion of Regeneration +potion.resistance.postfix=Potion of Resistance +potion.fireResistance.postfix=Potion of Fire Resistance +potion.waterBreathing.postfix=Potion of Water Breathing +potion.invisibility.postfix=Potion of Invisibility +potion.blindness.postfix=Potion of Blindness +potion.nightVision.postfix=Potion of Night Vision +potion.hunger.postfix=Potion of Hunger +potion.poison.postfix=Potion of Poison + +potion.potency.0= +potion.potency.1=II +potion.potency.2=III +potion.potency.3=IV + +potion.prefix.grenade=Splash +potion.prefix.mundane=Mundane +potion.prefix.uninteresting=Uninteresting +potion.prefix.bland=Bland +potion.prefix.clear=Clear +potion.prefix.milky=Milky +potion.prefix.diffuse=Diffuse +potion.prefix.artless=Artless +potion.prefix.thin=Thin +potion.prefix.awkward=Awkward +potion.prefix.flat=Flat +potion.prefix.bulky=Bulky +potion.prefix.bungling=Bungling +potion.prefix.buttered=Buttered +potion.prefix.smooth=Smooth +potion.prefix.suave=Suave +potion.prefix.debonair=Debonair +potion.prefix.thick=Thick +potion.prefix.elegant=Elegant +potion.prefix.fancy=Fancy +potion.prefix.charming=Charming +potion.prefix.dashing=Dashing +potion.prefix.refined=Refined +potion.prefix.cordial=Cordial +potion.prefix.sparkling=Sparkling +potion.prefix.potent=Potent +potion.prefix.foul=Foul +potion.prefix.odorless=Odorless +potion.prefix.rank=Rank +potion.prefix.harsh=Harsh +potion.prefix.acrid=Acrid +potion.prefix.gross=Gross +potion.prefix.stinky=Stinky + +enchantment.damage.all=Sharpness +enchantment.damage.undead=Smite +enchantment.damage.arthropods=Bane of Arthropods +enchantment.knockback=Knockback +enchantment.fire=Fire Aspect +enchantment.protect.all=Protection +enchantment.protect.fire=Fire Protection +enchantment.protect.fall=Feather Falling +enchantment.protect.explosion=Blast Protection +enchantment.protect.projectile=Projectile Protection +enchantment.oxygen=Respiration +enchantment.waterWorker=Aqua Affinity +enchantment.digging=Efficiency +enchantment.untouching=Silk Touch +enchantment.durability=Unbreaking +enchantment.lootBonus=Looting +enchantment.lootBonusDigger=Fortune +enchantment.arrowDamage=Power +enchantment.arrowFire=Flame +enchantment.arrowKnockback=Punch +enchantment.arrowInfinite=Infinity + +enchantment.level.1=I +enchantment.level.2=II +enchantment.level.3=III +enchantment.level.4=IV +enchantment.level.5=V +enchantment.level.6=VI +enchantment.level.7=VII +enchantment.level.8=VIII +enchantment.level.9=IX +enchantment.level.10=X + +gui.achievements=Achievements +gui.stats=Statistics + +stat.generalButton=General +stat.blocksButton=Blocks +stat.itemsButton=Items + +stat.used=Times Used +stat.mined=Times Mined +stat.depleted=Times Depleted +stat.crafted=Times Crafted + +stat.startGame=Times played +stat.createWorld=Worlds played +stat.loadWorld=Saves loaded +stat.joinMultiplayer=Multiplayer joins +stat.leaveGame=Games quit + +stat.playOneMinute=Minutes Played + +stat.walkOneCm=Distance Walked +stat.fallOneCm=Distance Fallen +stat.swimOneCm=Distance Swum +stat.flyOneCm=Distance Flown +stat.climbOneCm=Distance Climbed +stat.diveOneCm=Distance Dove +stat.minecartOneCm=Distance by Minecart +stat.boatOneCm=Distance by Boat +stat.pigOneCm=Distance by Pig +stat.jump=Jumps +stat.drop=Items Dropped + +stat.damageDealt=Damage Dealt +stat.damageTaken=Damage Taken +stat.deaths=Number of Deaths +stat.mobKills=Mob Kills +stat.playerKills=Player Kills +stat.fishCaught=Fish Caught + +stat.mineBlock=%1$s Mined +stat.craftItem=%1$s Crafted +stat.useItem=%1$s Used +stat.breakItem=%1$s Depleted + +achievement.get=Achievement get! + +achievement.taken=Taken! + +achievement.requires=Requires '%1$s' +achievement.openInventory=Taking Inventory +achievement.openInventory.desc=Press '%1$s' to open your inventory. +achievement.mineWood=Getting Wood +achievement.mineWood.desc=Attack a tree until a block of wood pops out +achievement.buildWorkBench=Benchmarking +achievement.buildWorkBench.desc=Craft a workbench with four blocks of planks +achievement.buildPickaxe=Time to Mine! +achievement.buildPickaxe.desc=Use planks and sticks to make a pickaxe +achievement.buildFurnace=Hot Topic +achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks +achievement.acquireIron=Acquire Hardware +achievement.acquireIron.desc=Smelt an iron ingot +achievement.buildHoe=Time to Farm! +achievement.buildHoe.desc=Use planks and sticks to make a hoe +achievement.makeBread=Bake Bread +achievement.makeBread.desc=Turn wheat into bread +achievement.bakeCake=The Lie +achievement.bakeCake.desc=Wheat, sugar, milk and eggs! +achievement.buildBetterPickaxe=Getting an Upgrade +achievement.buildBetterPickaxe.desc=Construct a better pickaxe +achievement.cookFish=Delicious Fish +achievement.cookFish.desc=Catch and cook fish! +achievement.onARail=On A Rail +achievement.onARail.desc=Travel by minecart at least 1 km from where you started +achievement.buildSword=Time to Strike! +achievement.buildSword.desc=Use planks and sticks to make a sword +achievement.killEnemy=Monster Hunter +achievement.killEnemy.desc=Attack and destroy a monster +achievement.killCow=Cow Tipper +achievement.killCow.desc=Harvest some leather +achievement.flyPig=When Pigs Fly +achievement.flyPig.desc=Fly a pig off a cliff +achievement.snipeSkeleton=Sniper Duel +achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters +achievement.diamonds=DIAMONDS! +achievement.diamonds.desc=Acquire diamonds with your iron tools +achievement.portal=We Need to Go Deeper +achievement.portal.desc=Build a portal to the Nether +achievement.ghast=Return to Sender +achievement.ghast.desc=Destroy a Ghast with a fireball +achievement.blazeRod=Into Fire +achievement.blazeRod.desc=Relieve a Blaze of its rod +achievement.potion=Local Brewery +achievement.potion.desc=Brew a potion +achievement.theEnd=The End? +achievement.theEnd.desc=Locate the End +achievement.theEnd2=The End. +achievement.theEnd2.desc=Defeat the Ender Dragon +achievement.enchantments=Enchanter +achievement.enchantments.desc=Use a book, obsidian and diamonds to construct an enchantment table +achievement.overkill=Overkill +achievement.overkill.desc=Deal eight hearts of damage in a single hit +achievement.bookcase=Librarian +achievement.bookcase.desc=Build some bookshelves to improve your enchantment table + +desc.sword=Deals more damage than by hand. +desc.shovel=Used to dig dirt, grass, sand, gravel and snow faster than by hand. +desc.pickaxe=Required to mine stone-related blocks and ore. +desc.hatchet=Used to chop wood-related blocks faster than by hand. +desc.hoe=Used to till dirt and grass blocks to prepare for crops. +desc.doorwood=Wooden doors are activated by using, hitting them or with Redstone. +desc.dooriron=Iron doors can only be opened by Redstone, buttons or switches. +desc.helmet=Gives the user 1.5 Armor when worn. +desc.chestplate=Gives the user 4 Armor when worn. +desc.leggings=Gives the user 3 Armor when worn. +desc.boots=Gives the user 1.5 Armor when worn. +desc.ingot=A shiny ingot which can be used to craft tools made from this material. +desc.block=Used as an expensive building block or compact storage of the ore. +desc.pressureplate=Used to send an electrical charge when stepped on by a player, an animal, or a monster. Wooden Pressure Plates can also be activated by dropping something on them. +desc.stairs=Used for compact staircases. +desc.stairsstonebricksmooth=Used for compact staircases. +desc.stairsnetherbrick=Used for compact staircases. +desc.stairssandstone=Used for compact staircases. +desc.stairsquartzblock=Used for compact staircases. +desc.slab=Used for making long staircases. +desc.halfslab=Used for compact staircases. +desc.torch=Used to create light. Torches also melt snow and ice. +desc.wood=Used as a building material and can be crafted into many things. Can be crafted from any wood. +desc.sandstone=Used as a building material. +desc.sandstone.addon=Is not influenced by gravity like normal Sand. +desc.structblock=Used as a building material. +desc.stick=Used to craft torches, arrows, signs, ladders, fences and as handles for tools and weapons. +desc.bed=Used to sleep until dawn. Changes your spawn point to the bed's position. +desc.craftingtable=Allows you to craft a more varied selection of items than the normal crafting. +desc.furnace=Allows you to smelt ore, create charcoal and glass, and cook fish and porkchops. +desc.chest=Stores blocks and items inside. +desc.fence=Used as a barrier that cannot be jumped over. +desc.ladder=Used to climb vertically. +desc.trapdoor=Work like normal doors, but are a one by one block and lay flat on the ground. +desc.sign=Shows text entered by you or other players. +desc.glowstone=Used to create brighter light than torches. Melts snow/ice and can be used underwater. +desc.tnt=Used to cause explosions. Activated after placing by hitting, or with an electrical charge. +desc.bowl=Used to hold mushroom stew. You keep the bowl when the stew has been eaten. +desc.bucket=Used to hold and transport water, lava and milk. +desc.flintandsteel=Used to detonate TNT. +desc.fishingrod=Used to catch fish. +desc.clock=Displays positions of the Sun and Moon. +desc.compass=Points to your start point. +desc.map=Will create an image of an area explored while held. This can be used for path-finding. +desc.bow=Allows for ranged attacks by using arrows. +desc.arrow=Used as ammunition for bows. +desc.mushroomstew=Restores 5 hearts. +desc.cookie=Restores 0.5 Hearts. Can be stacked up to 8 times. +desc.cake=Restores 1.5 Hearts. Can be used 6 times. +desc.sugar=Used in the cake recipe. +desc.bread=Restores 2.5 Hearts. +desc.goldenapple=Restores 10 Hearts. +desc.lever=Used to send an electrical charge by being turned on or off. Stays in the on or off state until pressed again. +desc.redstonetorch=Constantly sends an electrical charge, or can be used as a receiver/transmitter when connected to the side of a block. +Can also be used for low-level lighting. +desc.redstonerepeater=Used in Redstone circuits as repeater, a delayer, and/or a diode +desc.button=Used to send an electrical charge by being pressed. Stays activated for approximately a second before shutting off again. +desc.dispenser=Used to hold and shoot out items in a random order when given a Redstone charge. +desc.noteblock=Plays a note when triggered. Hit it to change the pitch of the note. Placing this on top of different blocks will change the type of instrument. +desc.rail=Used to guide minecarts. +desc.poweredrail=When powered, accelerates minecarts that pass over it. When unpowered, causes minecarts to stop on it. +desc.detectorrail=Functions like a Pressure Plate (sends a Redstone signal when powered) but can only be activated by a minecart. +desc.minecart=Used to transport you, an animal, or a monster along rails. +desc.minecartwithchest=Used to transport goods along rails. +desc.minecartwithfurnace=Will move along rails and can push other minecarts when coal is put in it. +desc.boat=Used to travel in water more quickly than swimming. +desc.wool=Collected from sheep, and can be colored with dyes. +desc.woolstring=Used as a building material and can be colored with dyes. Can be easily obtained from Sheep. +desc.dyepowder.black=Used as a dye to create black wool. +desc.dyepowder.green=Used as a dye to create green wool. +desc.dyepowder.brown=Used as a dye to create brown wool. +desc.dyepowder.silver=Used as a dye to create silver wool. +desc.dyepowder.yellow=Used as a dye to create yellow wool. +desc.dyepowder.red=Used as a dye to create red wool. +desc.dyepowder.white=Used to instantly grow crops and flowers. Can be used in dye recipes. +desc.dyepowder.pink=Used as a dye to create pink wool. +desc.dyepowder.orange=Used as a dye to create orange wool. +desc.dyepowder.lime=Used as a dye to create lime wool. +desc.dyepowder.gray=Used as a dye to create gray wool. +desc.dyepowder.lightgray=Used as a dye to create light gray wool. +(Note: light gray dye can also be made by combining gray dye with bone meal, letting you make four light gray dyes from every ink sac instead of three.) +desc.dyepowder.lightblue=Used as a dye to create light blue wool. +desc.dyepowder.cyan=Used as a dye to create cyan wool. +desc.dyepowder.purple=Used as a dye to create purple wool. +desc.dyepowder.magenta=Used as a dye to create magenta wool. +desc.dyepowder.blue=Used as dye to create Blue Wool. +desc.jukebox=Plays Music Discs. +desc.diamonds=Use these to create very strong tools, weapons or armor +desc.emerald=Use these to create very strong tools, weapons or armor +desc.jackolantern=Used to create brighter light than torches. Melts snow/ice and can be used underwater. +desc.paper=Used to create books and maps. +desc.book=Used to create a bookshelf. +desc.bookshelf=Used as decoration. +desc.picture=Used as decoration. +desc.ore_gold=Can be mined with an iron pickaxe or better, then smelted in a furnace to produce gold ingots. +desc.ore_iron=Can be mined with a stone pickaxe or better, then smelted in a furnace to produce iron ingots. +desc.ore_coal=Can be mined with a pickaxe to collect coal. +desc.ore_lapis=Can be mined with a stone pickaxe or better to collect lapis lazuli. +desc.ore_diamond=Can be mined with an iron pickaxe or better to collect diamonds. +desc.ore_redstone=Can be mined with an iron pickaxe or better to collect redstone dust. +desc.stone=Can be mined with a pickaxe to collect cobblestone. +desc.dirt=Collected using a shovel. Can be used for construction. +desc.sapling=Can be planted and it will eventually grow into a tree. +desc.bedrock=This cannot be broken. +desc.lava=Sets fire to anything that touches it. Can be collected in a bucket. +desc.sand=Collected using a shovel. Can be smelted into glass using the furnace. Is affected by gravity if there is no other tile underneath it. +desc.gravel=Collected using a shovel. Sometimes produces flint when dug up. Is affected by gravity if there is no other tile underneath it. +desc.log=Chopped using an axe, and can be crafted into planks or used as a fuel. +desc.glass=Created in a furnace by smelting sand. Will break if you try to mine it. +desc.thinglass=Will break if you try to mine it. +desc.stone_brick=Mined from stone using a pickaxe. Can be used to construct a furnace or stone tools. +desc.brick=Baked from clay in a furnace. +desc.clay=Can be baked into bricks in a furnace. +desc.snow=A compact way to store snowballs. +desc.top_snow=Can be dug with a shovel to create snowballs. +desc.tall_grass=Sometimes produces seeds when broken. +desc.flower=Can be crafted into a dye. +desc.mushroom=Can be crafted with a bowl to make stew. +desc.obsidian=Can only be mined with a diamond pickaxe. Is produced by the meeting of water and still lava, and is used to build a portal. +desc.mob_spawner=Spawns monsters into the world. +desc.redstone_dust=Is placed on the ground to carry an electrical charge. +desc.crops=When fully grown, crops can be harvested to collect wheat. +desc.farmland=Ground that has been prepared ready to plant seeds. +desc.cactus=Can be crafted to create a dye. +desc.reeds=Can be crafted to create sugar. +desc.pumpkin=Can be worn as a helmet or crafted with a torch to create a Jack-O-Lantern. +desc.hell_rock=Burns forever if set alight. +desc.hell_sand=Slows the movement of anything walking over it. +desc.portal=Standing in the portal allows you to pass between the overworld and the nether. +desc.apple=Restores health, and can be crafted into a golden apple. +desc.melon=Can be broken into melon slices. +desc.coal=Used as a fuel in a furnace, or crafted to make a torch. +desc.string=Collected by killing a spider, and can be crafted into a bow. +desc.feather=Collected by killing a chicken, and can be crafted into an arrow. +desc.sulphur=Collected by killing a creeper, and can be crafted into TNT. +desc.shears=Used to obtain wool from sheep and to harvest placeable Leaf blocks. +desc.seeds=Can be planted in farmland to grow crops. +desc.wheat=Harvested from crops, and can be used to craft food items. +desc.flint=Collected by digging gravel, and can be used to craft a flint and steel. +desc.porkchopraw=Collected by killing a pig, and can be cooked in a furnace. Restores health. +desc.porkchopcooked=Created by cooking a porkchop in a furnace. Restores health. +desc.beefraw=Collected by killing a cow, and can be cooked in a furnace. Restores health. +desc.beefcooked=Created by cooking a beef in a furnace. Restores health. +desc.chickenraw=Collected by killing a chicken, and can be cooked in a furnace. Restores health. +desc.chickencooked=Created by cooking a chicken in a furnace. Restores health. +desc.saddle=When used on a pig it allows you to ride the pig. +desc.snowball=Collected by digging snow, and can be thrown. +desc.leather=Collected by killing a cow, and can be crafted into armor. +desc.slimeball=Collected by killing a slime. +desc.egg=Dropped randomly by chickens, and can be crafted into food items. +desc.yellow_dust=Collected by mining Glowstone, and can be crafted to make Glowstone blocks again. +desc.fish_raw=Can be eaten to restore health, or cooked in a furnace. +desc.fish_cooked=Created by cooking a raw fish in a furnace. Can be eaten to restore health. +desc.bone=Collected by killing a skeleton. Can be crafted into bone meal. +desc.record=Collected by getting a skeleton to kill a creeper. Can be played in a jukebox. +desc.water=Extinguishes fire and helps crops grow. Can be collected in a bucket. +desc.leaves=When broken sometimes drops a sapling which can then be replanted to grow into a tree. +desc.moss_stone=Can be used for construction and decoration. +desc.stonebricksmooth=Used as building material. +desc.netherreactor=Core of the Nether Reactor +desc.glowingobsidian=Obsidion activated by external source. +desc.lightgem=Used for illumination. +desc.painting=Used as decoration. +desc.hellrock=Is a stone material from the Nether. +desc.netherbrick=Construction block made from Nether bricks. +desc.netherbrickitem=Used to form blocks of Nether bricks. +desc.netherquartz=Quartz from the Nether, used to create Blocks of Quartz. +desc.quartzblock=Decorative block, used to create other kinds of Quartz blocks. +desc.stonecutter=For crafting stoneblocks. +desc.seedsmelon=Allows for more melons. +desc.tile.melon=Can be cut up and eaten. diff --git a/project/android/jni/Android.mk b/project/android/jni/Android.mk index 9bff609..b1fd323 100755 --- a/project/android/jni/Android.mk +++ b/project/android/jni/Android.mk @@ -1,281 +1,281 @@ -LOCAL_PATH := $(call my-dir) -# Convert Windows backslashes to forward slashes so NDK toolchain doesn’t treat them as escapes. -LOCAL_PATH := $(subst \,/,$(LOCAL_PATH)) - -include $(CLEAR_VARS) - -LOCAL_MODULE := minecraftpe -LOCAL_SRC_FILES := ../../../src/main.cpp \ - ../../../src/main_android_java.cpp \ - ../../../src/platform/audio/SoundSystemSL.cpp \ - ../../../src/platform/input/Controller.cpp \ - ../../../src/platform/input/Keyboard.cpp \ - ../../../src/platform/input/Mouse.cpp \ - ../../../src/platform/input/Multitouch.cpp \ - ../../../src/platform/time.cpp \ - ../../../src/platform/CThread.cpp \ - ../../../src/platform/HttpClient.cpp \ -../../../src/NinecraftApp.cpp \ -../../../src/Performance.cpp \ -../../../src/SharedConstants.cpp \ -../../../src/client/IConfigListener.cpp \ -../../../src/client/Minecraft.cpp \ -../../../src/client/MouseHandler.cpp \ -../../../src/client/Options.cpp \ -../../../src/client/OptionsFile.cpp \ -../../../src/client/OptionStrings.cpp \ -../../../src/client/Option.cpp \ -../../../src/client/gamemode/GameMode.cpp \ -../../../src/client/gamemode/CreativeMode.cpp \ -../../../src/client/gamemode/SurvivalMode.cpp \ -../../../src/client/gui/components/Button.cpp \ -../../../src/client/gui/components/GuiElement.cpp \ -../../../src/client/gui/components/GuiElementContainer.cpp \ -../../../src/client/gui/components/ImageButton.cpp \ -../../../src/client/gui/components/ItemPane.cpp \ -../../../src/client/gui/components/InventoryPane.cpp \ -../../../src/client/gui/components/LargeImageButton.cpp \ -../../../src/client/gui/components/NinePatch.cpp \ -../../../src/client/gui/components/OptionsGroup.cpp \ -../../../src/client/gui/components/OptionsItem.cpp \ -../../../src/client/gui/components/KeyOption.cpp \ -../../../src/client/gui/components/TextOption.cpp \ -../../../src/client/gui/components/RolledSelectionListH.cpp \ -../../../src/client/gui/components/RolledSelectionListV.cpp \ -../../../src/client/gui/components/ScrolledSelectionList.cpp \ -../../../src/client/gui/components/ScrollingPane.cpp \ -../../../src/client/gui/components/Slider.cpp \ -../../../src/client/gui/components/TextBox.cpp \ -../../../src/client/gui/Font.cpp \ -../../../src/client/gui/Gui.cpp \ -../../../src/client/gui/GuiComponent.cpp \ -../../../src/client/gui/Screen.cpp \ -../../../src/client/gui/screens/ScreenChooser.cpp \ -../../../src/client/gui/screens/ArmorScreen.cpp \ -../../../src/client/gui/screens/ChatScreen.cpp \ -../../../src/client/gui/screens/ChooseLevelScreen.cpp \ -../../../src/client/gui/screens/SimpleChooseLevelScreen.cpp \ -../../../src/client/gui/screens/ConsoleScreen.cpp \ -../../../src/client/gui/screens/UsernameScreen.cpp \ -../../../src/client/gui/screens/ConfirmScreen.cpp \ -../../../src/client/gui/screens/ChestScreen.cpp \ -../../../src/client/gui/screens/DeathScreen.cpp \ -../../../src/client/gui/screens/FurnaceScreen.cpp \ -../../../src/client/gui/screens/InBedScreen.cpp \ -../../../src/client/gui/screens/IngameBlockSelectionScreen.cpp \ -../../../src/client/gui/screens/JoinGameScreen.cpp \ -../../../src/client/gui/screens/OptionsScreen.cpp \ - ../../../src/client/gui/screens/CreditsScreen.cpp \ - ../../../src/client/gui/screens/PauseScreen.cpp \ -../../../src/client/gui/screens/ProgressScreen.cpp \ -../../../src/client/gui/screens/RenameMPLevelScreen.cpp \ -../../../src/client/gui/screens/SelectWorldScreen.cpp \ -../../../src/client/gui/screens/StartMenuScreen.cpp \ -../../../src/client/gui/screens/TextEditScreen.cpp \ -../../../src/client/gui/screens/JoinByIPScreen.cpp \ -../../../src/client/gui/screens/touch/TouchIngameBlockSelectionScreen.cpp \ -../../../src/client/gui/screens/touch/TouchJoinGameScreen.cpp \ -../../../src/client/gui/screens/touch/TouchSelectWorldScreen.cpp \ -../../../src/client/gui/screens/touch/TouchStartMenuScreen.cpp \ -../../../src/client/gui/screens/UploadPhotoScreen.cpp \ -../../../src/client/gui/screens/crafting/CraftingFilters.cpp \ -../../../src/client/gui/screens/crafting/PaneCraftingScreen.cpp \ -../../../src/client/gui/screens/crafting/StonecutterScreen.cpp \ -../../../src/client/gui/screens/crafting/WorkbenchScreen.cpp \ -../../../src/client/model/ChickenModel.cpp \ -../../../src/client/model/CowModel.cpp \ -../../../src/client/model/HumanoidModel.cpp \ -../../../src/client/model/PigModel.cpp \ -../../../src/client/model/SheepFurModel.cpp \ -../../../src/client/model/SheepModel.cpp \ -../../../src/client/model/QuadrupedModel.cpp \ -../../../src/client/model/geom/Cube.cpp \ -../../../src/client/model/geom/ModelPart.cpp \ -../../../src/client/model/geom/Polygon.cpp \ -../../../src/client/particle/Particle.cpp \ -../../../src/client/particle/ParticleEngine.cpp \ -../../../src/client/player/LocalPlayer.cpp \ -../../../src/client/player/RemotePlayer.cpp \ -../../../src/client/player/input/KeyboardInput.cpp \ -../../../src/client/player/input/touchscreen/TouchscreenInput.cpp \ -../../../src/client/renderer/Chunk.cpp \ -../../../src/client/renderer/EntityTileRenderer.cpp \ -../../../src/client/renderer/GameRenderer.cpp \ -../../../src/client/renderer/ItemInHandRenderer.cpp \ -../../../src/client/renderer/LevelRenderer.cpp \ -../../../src/client/renderer/RenderChunk.cpp \ -../../../src/client/renderer/RenderList.cpp \ -../../../src/client/renderer/Tesselator.cpp \ -../../../src/client/renderer/Textures.cpp \ -../../../src/client/renderer/TileRenderer.cpp \ -../../../src/client/renderer/gles.cpp \ -../../../src/client/renderer/culling/Frustum.cpp \ -../../../src/client/renderer/entity/ArrowRenderer.cpp \ -../../../src/client/renderer/entity/ChickenRenderer.cpp \ -../../../src/client/renderer/entity/EntityRenderDispatcher.cpp \ -../../../src/client/renderer/entity/EntityRenderer.cpp \ -../../../src/client/renderer/entity/FallingTileRenderer.cpp \ -../../../src/client/renderer/entity/HumanoidMobRenderer.cpp \ -../../../src/client/renderer/entity/ItemRenderer.cpp \ -../../../src/client/renderer/entity/ItemSpriteRenderer.cpp \ -../../../src/client/renderer/entity/MobRenderer.cpp \ -../../../src/client/renderer/entity/PaintingRenderer.cpp \ -../../../src/client/renderer/entity/PlayerRenderer.cpp \ -../../../src/client/renderer/entity/SheepRenderer.cpp \ -../../../src/client/renderer/entity/TntRenderer.cpp \ -../../../src/client/renderer/entity/TripodCameraRenderer.cpp \ -../../../src/client/renderer/ptexture/DynamicTexture.cpp \ -../../../src/client/renderer/tileentity/ChestRenderer.cpp \ -../../../src/client/renderer/tileentity/SignRenderer.cpp \ -../../../src/client/renderer/tileentity/TileEntityRenderDispatcher.cpp \ -../../../src/client/renderer/tileentity/TileEntityRenderer.cpp \ -../../../src/client/sound/Sound.cpp \ -../../../src/client/sound/SoundEngine.cpp \ -../../../src/locale/I18n.cpp \ -../../../src/nbt/Tag.cpp \ -../../../src/network/command/CommandServer.cpp \ -../../../src/network/ClientSideNetworkHandler.cpp \ -../../../src/network/NetEventCallback.cpp \ -../../../src/network/Packet.cpp \ -../../../src/network/RakNetInstance.cpp \ -../../../src/network/ServerSideNetworkHandler.cpp \ -../../../src/server/ServerLevel.cpp \ -../../../src/server/ServerPlayer.cpp \ -../../../src/util/DataIO.cpp \ -../../../src/util/Mth.cpp \ -../../../src/util/StringUtils.cpp \ -../../../src/util/PerfTimer.cpp \ -../../../src/util/PerfRenderer.cpp \ -../../../src/world/Direction.cpp \ -../../../src/world/entity/AgableMob.cpp \ -../../../src/world/entity/Entity.cpp \ -../../../src/world/entity/EntityFactory.cpp \ -../../../src/world/entity/FlyingMob.cpp \ -../../../src/world/entity/HangingEntity.cpp \ -../../../src/world/entity/Mob.cpp \ -../../../src/world/entity/MobCategory.cpp \ -../../../src/world/entity/Motive.cpp \ -../../../src/world/entity/Painting.cpp \ -../../../src/world/entity/PathfinderMob.cpp \ -../../../src/world/entity/SynchedEntityData.cpp \ -../../../src/world/entity/ai/control/MoveControl.cpp \ -../../../src/world/entity/animal/Animal.cpp \ -../../../src/world/entity/animal/Chicken.cpp \ -../../../src/world/entity/animal/Cow.cpp \ -../../../src/world/entity/animal/Pig.cpp \ -../../../src/world/entity/animal/Sheep.cpp \ -../../../src/world/entity/animal/WaterAnimal.cpp \ -../../../src/world/entity/item/FallingTile.cpp \ -../../../src/world/entity/item/ItemEntity.cpp \ -../../../src/world/entity/item/PrimedTnt.cpp \ -../../../src/world/entity/item/TripodCamera.cpp \ -../../../src/world/entity/monster/Creeper.cpp \ -../../../src/world/entity/monster/Monster.cpp \ -../../../src/world/entity/monster/PigZombie.cpp \ -../../../src/world/entity/monster/Skeleton.cpp \ -../../../src/world/entity/monster/Spider.cpp \ -../../../src/world/entity/monster/Zombie.cpp \ -../../../src/world/entity/projectile/Arrow.cpp \ -../../../src/world/entity/projectile/Throwable.cpp \ -../../../src/world/entity/player/Inventory.cpp \ -../../../src/world/entity/player/Player.cpp \ -../../../src/world/food/SimpleFoodData.cpp \ -../../../src/world/inventory/BaseContainerMenu.cpp \ -../../../src/world/inventory/ContainerMenu.cpp \ -../../../src/world/inventory/FillingContainer.cpp \ -../../../src/world/inventory/FurnaceMenu.cpp \ -../../../src/world/item/ArmorItem.cpp \ -../../../src/world/item/BedItem.cpp \ -../../../src/world/item/DyePowderItem.cpp \ -../../../src/world/item/Item.cpp \ -../../../src/world/item/ItemInstance.cpp \ -../../../src/world/item/HangingEntityItem.cpp \ -../../../src/world/item/HatchetItem.cpp \ -../../../src/world/item/HoeItem.cpp \ -../../../src/world/item/PickaxeItem.cpp \ -../../../src/world/item/ShovelItem.cpp \ -../../../src/world/item/crafting/ArmorRecipes.cpp \ -../../../src/world/item/crafting/Recipe.cpp \ -../../../src/world/item/crafting/Recipes.cpp \ -../../../src/world/item/crafting/FurnaceRecipes.cpp \ -../../../src/world/item/crafting/OreRecipes.cpp \ -../../../src/world/item/crafting/StructureRecipes.cpp \ -../../../src/world/item/crafting/ToolRecipes.cpp \ -../../../src/world/item/crafting/WeaponRecipes.cpp \ -../../../src/world/level/Explosion.cpp \ -../../../src/world/level/Level.cpp \ -../../../src/world/level/LightLayer.cpp \ -../../../src/world/level/LightUpdate.cpp \ -../../../src/world/level/MobSpawner.cpp \ -../../../src/world/level/Region.cpp \ -../../../src/world/level/TickNextTickData.cpp \ -../../../src/world/level/biome/Biome.cpp \ -../../../src/world/level/biome/BiomeSource.cpp \ -../../../src/world/level/chunk/LevelChunk.cpp \ -../../../src/world/level/dimension/Dimension.cpp \ -../../../src/world/level/levelgen/CanyonFeature.cpp \ -../../../src/world/level/levelgen/DungeonFeature.cpp \ -../../../src/world/level/levelgen/LargeCaveFeature.cpp \ -../../../src/world/level/levelgen/LargeFeature.cpp \ -../../../src/world/level/levelgen/RandomLevelSource.cpp \ -../../../src/world/level/levelgen/feature/Feature.cpp \ -../../../src/world/level/levelgen/synth/ImprovedNoise.cpp \ -../../../src/world/level/levelgen/synth/PerlinNoise.cpp \ -../../../src/world/level/levelgen/synth/Synth.cpp \ -../../../src/world/level/material/Material.cpp \ -../../../src/world/level/pathfinder/Path.cpp \ -../../../src/world/level/storage/ExternalFileLevelStorage.cpp \ -../../../src/world/level/storage/ExternalFileLevelStorageSource.cpp \ -../../../src/world/level/storage/FolderMethods.cpp \ -../../../src/world/level/storage/LevelData.cpp \ -../../../src/world/level/storage/LevelStorageSource.cpp \ -../../../src/world/level/storage/RegionFile.cpp \ -../../../src/world/level/tile/BedTile.cpp \ -../../../src/world/level/tile/ChestTile.cpp \ -../../../src/world/level/tile/CropTile.cpp \ -../../../src/world/level/tile/DoorTile.cpp \ -../../../src/world/level/tile/EntityTile.cpp \ -../../../src/world/level/tile/FurnaceTile.cpp \ -../../../src/world/level/tile/GrassTile.cpp \ -../../../src/world/level/tile/HeavyTile.cpp \ -../../../src/world/level/tile/LightGemTile.cpp \ -../../../src/world/level/tile/MelonTile.cpp \ -../../../src/world/level/tile/Mushroom.cpp \ -../../../src/world/level/tile/NetherReactor.cpp \ -../../../src/world/level/tile/NetherReactorPattern.cpp \ -../../../src/world/level/tile/StairTile.cpp \ -../../../src/world/level/tile/StemTile.cpp \ -../../../src/world/level/tile/StoneSlabTile.cpp \ -../../../src/world/level/tile/TallGrass.cpp \ -../../../src/world/level/tile/Tile.cpp \ -../../../src/world/level/tile/TrapDoorTile.cpp \ -../../../src/world/level/tile/entity/ChestTileEntity.cpp \ -../../../src/world/level/tile/entity/NetherReactorTileEntity.cpp \ -../../../src/world/level/tile/entity/SignTileEntity.cpp \ -../../../src/world/level/tile/entity/TileEntity.cpp \ -../../../src/world/level/tile/entity/FurnaceTileEntity.cpp \ -../../../src/world/phys/HitResult.cpp - -LOCAL_CFLAGS := -DPLATFORM_ANDROID -DPRE_ANDROID23 -Wno-narrowing $(LOCAL_CFLAGS) -LOCAL_CPPFLAGS := -std=c++14 -frtti -LOCAL_CPPFLAGS += -frtti -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../src - -#LOCAL_CFLAGS := -DANDROID_PUBLISH -DDEMO_MODE $(LOCAL_CFLAGS) -#LOCAL_CFLAGS := -DANDROID_PUBLISH $(LOCAL_CFLAGS) - -#LOCAL_CFLAGS := -DDEMO_MODE -DGLDEBUG $(LOCAL_CFLAGS) -#LOCAL_CFLAGS := -DGLDEBUG $(LOCAL_CFLAGS) - -LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM -lOpenSLES -LOCAL_STATIC_LIBRARIES := android_native_app_glue RakNet - -#LOCAL_CPP_FEATURES := exceptions - -TARGET_ARCH_ABI := armeabi-v7a - -include $(BUILD_SHARED_LIBRARY) - -# NOTE: environment var NDK_MODULE_PATH needs to point to lib_projects folder -$(call import-module,android/native_app_glue) -$(call import-module, raknet/jni) +LOCAL_PATH := $(call my-dir) +# Convert Windows backslashes to forward slashes so NDK toolchain doesn’t treat them as escapes. +LOCAL_PATH := $(subst \,/,$(LOCAL_PATH)) + +include $(CLEAR_VARS) + +LOCAL_MODULE := minecraftpe +LOCAL_SRC_FILES := ../../../src/main.cpp \ + ../../../src/main_android_java.cpp \ + ../../../src/platform/audio/SoundSystemSL.cpp \ + ../../../src/platform/input/Controller.cpp \ + ../../../src/platform/input/Keyboard.cpp \ + ../../../src/platform/input/Mouse.cpp \ + ../../../src/platform/input/Multitouch.cpp \ + ../../../src/platform/time.cpp \ + ../../../src/platform/CThread.cpp \ + ../../../src/platform/HttpClient.cpp \ +../../../src/NinecraftApp.cpp \ +../../../src/Performance.cpp \ +../../../src/SharedConstants.cpp \ +../../../src/client/IConfigListener.cpp \ +../../../src/client/Minecraft.cpp \ +../../../src/client/MouseHandler.cpp \ +../../../src/client/Options.cpp \ +../../../src/client/OptionsFile.cpp \ +../../../src/client/OptionStrings.cpp \ +../../../src/client/Option.cpp \ +../../../src/client/gamemode/GameMode.cpp \ +../../../src/client/gamemode/CreativeMode.cpp \ +../../../src/client/gamemode/SurvivalMode.cpp \ +../../../src/client/gui/components/Button.cpp \ +../../../src/client/gui/components/GuiElement.cpp \ +../../../src/client/gui/components/GuiElementContainer.cpp \ +../../../src/client/gui/components/ImageButton.cpp \ +../../../src/client/gui/components/ItemPane.cpp \ +../../../src/client/gui/components/InventoryPane.cpp \ +../../../src/client/gui/components/LargeImageButton.cpp \ +../../../src/client/gui/components/NinePatch.cpp \ +../../../src/client/gui/components/OptionsGroup.cpp \ +../../../src/client/gui/components/OptionsItem.cpp \ +../../../src/client/gui/components/KeyOption.cpp \ +../../../src/client/gui/components/TextOption.cpp \ +../../../src/client/gui/components/RolledSelectionListH.cpp \ +../../../src/client/gui/components/RolledSelectionListV.cpp \ +../../../src/client/gui/components/ScrolledSelectionList.cpp \ +../../../src/client/gui/components/ScrollingPane.cpp \ +../../../src/client/gui/components/Slider.cpp \ +../../../src/client/gui/components/TextBox.cpp \ +../../../src/client/gui/Font.cpp \ +../../../src/client/gui/Gui.cpp \ +../../../src/client/gui/GuiComponent.cpp \ +../../../src/client/gui/Screen.cpp \ +../../../src/client/gui/screens/ScreenChooser.cpp \ +../../../src/client/gui/screens/ArmorScreen.cpp \ +../../../src/client/gui/screens/ChatScreen.cpp \ +../../../src/client/gui/screens/ChooseLevelScreen.cpp \ +../../../src/client/gui/screens/SimpleChooseLevelScreen.cpp \ +../../../src/client/gui/screens/ConsoleScreen.cpp \ +../../../src/client/gui/screens/UsernameScreen.cpp \ +../../../src/client/gui/screens/ConfirmScreen.cpp \ +../../../src/client/gui/screens/ChestScreen.cpp \ +../../../src/client/gui/screens/DeathScreen.cpp \ +../../../src/client/gui/screens/FurnaceScreen.cpp \ +../../../src/client/gui/screens/InBedScreen.cpp \ +../../../src/client/gui/screens/IngameBlockSelectionScreen.cpp \ +../../../src/client/gui/screens/JoinGameScreen.cpp \ +../../../src/client/gui/screens/OptionsScreen.cpp \ + ../../../src/client/gui/screens/CreditsScreen.cpp \ + ../../../src/client/gui/screens/PauseScreen.cpp \ +../../../src/client/gui/screens/ProgressScreen.cpp \ +../../../src/client/gui/screens/RenameMPLevelScreen.cpp \ +../../../src/client/gui/screens/SelectWorldScreen.cpp \ +../../../src/client/gui/screens/StartMenuScreen.cpp \ +../../../src/client/gui/screens/TextEditScreen.cpp \ +../../../src/client/gui/screens/JoinByIPScreen.cpp \ +../../../src/client/gui/screens/touch/TouchIngameBlockSelectionScreen.cpp \ +../../../src/client/gui/screens/touch/TouchJoinGameScreen.cpp \ +../../../src/client/gui/screens/touch/TouchSelectWorldScreen.cpp \ +../../../src/client/gui/screens/touch/TouchStartMenuScreen.cpp \ +../../../src/client/gui/screens/UploadPhotoScreen.cpp \ +../../../src/client/gui/screens/crafting/CraftingFilters.cpp \ +../../../src/client/gui/screens/crafting/PaneCraftingScreen.cpp \ +../../../src/client/gui/screens/crafting/StonecutterScreen.cpp \ +../../../src/client/gui/screens/crafting/WorkbenchScreen.cpp \ +../../../src/client/model/ChickenModel.cpp \ +../../../src/client/model/CowModel.cpp \ +../../../src/client/model/HumanoidModel.cpp \ +../../../src/client/model/PigModel.cpp \ +../../../src/client/model/SheepFurModel.cpp \ +../../../src/client/model/SheepModel.cpp \ +../../../src/client/model/QuadrupedModel.cpp \ +../../../src/client/model/geom/Cube.cpp \ +../../../src/client/model/geom/ModelPart.cpp \ +../../../src/client/model/geom/Polygon.cpp \ +../../../src/client/particle/Particle.cpp \ +../../../src/client/particle/ParticleEngine.cpp \ +../../../src/client/player/LocalPlayer.cpp \ +../../../src/client/player/RemotePlayer.cpp \ +../../../src/client/player/input/KeyboardInput.cpp \ +../../../src/client/player/input/touchscreen/TouchscreenInput.cpp \ +../../../src/client/renderer/Chunk.cpp \ +../../../src/client/renderer/EntityTileRenderer.cpp \ +../../../src/client/renderer/GameRenderer.cpp \ +../../../src/client/renderer/ItemInHandRenderer.cpp \ +../../../src/client/renderer/LevelRenderer.cpp \ +../../../src/client/renderer/RenderChunk.cpp \ +../../../src/client/renderer/RenderList.cpp \ +../../../src/client/renderer/Tesselator.cpp \ +../../../src/client/renderer/Textures.cpp \ +../../../src/client/renderer/TileRenderer.cpp \ +../../../src/client/renderer/gles.cpp \ +../../../src/client/renderer/culling/Frustum.cpp \ +../../../src/client/renderer/entity/ArrowRenderer.cpp \ +../../../src/client/renderer/entity/ChickenRenderer.cpp \ +../../../src/client/renderer/entity/EntityRenderDispatcher.cpp \ +../../../src/client/renderer/entity/EntityRenderer.cpp \ +../../../src/client/renderer/entity/FallingTileRenderer.cpp \ +../../../src/client/renderer/entity/HumanoidMobRenderer.cpp \ +../../../src/client/renderer/entity/ItemRenderer.cpp \ +../../../src/client/renderer/entity/ItemSpriteRenderer.cpp \ +../../../src/client/renderer/entity/MobRenderer.cpp \ +../../../src/client/renderer/entity/PaintingRenderer.cpp \ +../../../src/client/renderer/entity/PlayerRenderer.cpp \ +../../../src/client/renderer/entity/SheepRenderer.cpp \ +../../../src/client/renderer/entity/TntRenderer.cpp \ +../../../src/client/renderer/entity/TripodCameraRenderer.cpp \ +../../../src/client/renderer/ptexture/DynamicTexture.cpp \ +../../../src/client/renderer/tileentity/ChestRenderer.cpp \ +../../../src/client/renderer/tileentity/SignRenderer.cpp \ +../../../src/client/renderer/tileentity/TileEntityRenderDispatcher.cpp \ +../../../src/client/renderer/tileentity/TileEntityRenderer.cpp \ +../../../src/client/sound/Sound.cpp \ +../../../src/client/sound/SoundEngine.cpp \ +../../../src/locale/I18n.cpp \ +../../../src/nbt/Tag.cpp \ +../../../src/network/command/CommandServer.cpp \ +../../../src/network/ClientSideNetworkHandler.cpp \ +../../../src/network/NetEventCallback.cpp \ +../../../src/network/Packet.cpp \ +../../../src/network/RakNetInstance.cpp \ +../../../src/network/ServerSideNetworkHandler.cpp \ +../../../src/server/ServerLevel.cpp \ +../../../src/server/ServerPlayer.cpp \ +../../../src/util/DataIO.cpp \ +../../../src/util/Mth.cpp \ +../../../src/util/StringUtils.cpp \ +../../../src/util/PerfTimer.cpp \ +../../../src/util/PerfRenderer.cpp \ +../../../src/world/Direction.cpp \ +../../../src/world/entity/AgableMob.cpp \ +../../../src/world/entity/Entity.cpp \ +../../../src/world/entity/EntityFactory.cpp \ +../../../src/world/entity/FlyingMob.cpp \ +../../../src/world/entity/HangingEntity.cpp \ +../../../src/world/entity/Mob.cpp \ +../../../src/world/entity/MobCategory.cpp \ +../../../src/world/entity/Motive.cpp \ +../../../src/world/entity/Painting.cpp \ +../../../src/world/entity/PathfinderMob.cpp \ +../../../src/world/entity/SynchedEntityData.cpp \ +../../../src/world/entity/ai/control/MoveControl.cpp \ +../../../src/world/entity/animal/Animal.cpp \ +../../../src/world/entity/animal/Chicken.cpp \ +../../../src/world/entity/animal/Cow.cpp \ +../../../src/world/entity/animal/Pig.cpp \ +../../../src/world/entity/animal/Sheep.cpp \ +../../../src/world/entity/animal/WaterAnimal.cpp \ +../../../src/world/entity/item/FallingTile.cpp \ +../../../src/world/entity/item/ItemEntity.cpp \ +../../../src/world/entity/item/PrimedTnt.cpp \ +../../../src/world/entity/item/TripodCamera.cpp \ +../../../src/world/entity/monster/Creeper.cpp \ +../../../src/world/entity/monster/Monster.cpp \ +../../../src/world/entity/monster/PigZombie.cpp \ +../../../src/world/entity/monster/Skeleton.cpp \ +../../../src/world/entity/monster/Spider.cpp \ +../../../src/world/entity/monster/Zombie.cpp \ +../../../src/world/entity/projectile/Arrow.cpp \ +../../../src/world/entity/projectile/Throwable.cpp \ +../../../src/world/entity/player/Inventory.cpp \ +../../../src/world/entity/player/Player.cpp \ +../../../src/world/food/SimpleFoodData.cpp \ +../../../src/world/inventory/BaseContainerMenu.cpp \ +../../../src/world/inventory/ContainerMenu.cpp \ +../../../src/world/inventory/FillingContainer.cpp \ +../../../src/world/inventory/FurnaceMenu.cpp \ +../../../src/world/item/ArmorItem.cpp \ +../../../src/world/item/BedItem.cpp \ +../../../src/world/item/DyePowderItem.cpp \ +../../../src/world/item/Item.cpp \ +../../../src/world/item/ItemInstance.cpp \ +../../../src/world/item/HangingEntityItem.cpp \ +../../../src/world/item/HatchetItem.cpp \ +../../../src/world/item/HoeItem.cpp \ +../../../src/world/item/PickaxeItem.cpp \ +../../../src/world/item/ShovelItem.cpp \ +../../../src/world/item/crafting/ArmorRecipes.cpp \ +../../../src/world/item/crafting/Recipe.cpp \ +../../../src/world/item/crafting/Recipes.cpp \ +../../../src/world/item/crafting/FurnaceRecipes.cpp \ +../../../src/world/item/crafting/OreRecipes.cpp \ +../../../src/world/item/crafting/StructureRecipes.cpp \ +../../../src/world/item/crafting/ToolRecipes.cpp \ +../../../src/world/item/crafting/WeaponRecipes.cpp \ +../../../src/world/level/Explosion.cpp \ +../../../src/world/level/Level.cpp \ +../../../src/world/level/LightLayer.cpp \ +../../../src/world/level/LightUpdate.cpp \ +../../../src/world/level/MobSpawner.cpp \ +../../../src/world/level/Region.cpp \ +../../../src/world/level/TickNextTickData.cpp \ +../../../src/world/level/biome/Biome.cpp \ +../../../src/world/level/biome/BiomeSource.cpp \ +../../../src/world/level/chunk/LevelChunk.cpp \ +../../../src/world/level/dimension/Dimension.cpp \ +../../../src/world/level/levelgen/CanyonFeature.cpp \ +../../../src/world/level/levelgen/DungeonFeature.cpp \ +../../../src/world/level/levelgen/LargeCaveFeature.cpp \ +../../../src/world/level/levelgen/LargeFeature.cpp \ +../../../src/world/level/levelgen/RandomLevelSource.cpp \ +../../../src/world/level/levelgen/feature/Feature.cpp \ +../../../src/world/level/levelgen/synth/ImprovedNoise.cpp \ +../../../src/world/level/levelgen/synth/PerlinNoise.cpp \ +../../../src/world/level/levelgen/synth/Synth.cpp \ +../../../src/world/level/material/Material.cpp \ +../../../src/world/level/pathfinder/Path.cpp \ +../../../src/world/level/storage/ExternalFileLevelStorage.cpp \ +../../../src/world/level/storage/ExternalFileLevelStorageSource.cpp \ +../../../src/world/level/storage/FolderMethods.cpp \ +../../../src/world/level/storage/LevelData.cpp \ +../../../src/world/level/storage/LevelStorageSource.cpp \ +../../../src/world/level/storage/RegionFile.cpp \ +../../../src/world/level/tile/BedTile.cpp \ +../../../src/world/level/tile/ChestTile.cpp \ +../../../src/world/level/tile/CropTile.cpp \ +../../../src/world/level/tile/DoorTile.cpp \ +../../../src/world/level/tile/EntityTile.cpp \ +../../../src/world/level/tile/FurnaceTile.cpp \ +../../../src/world/level/tile/GrassTile.cpp \ +../../../src/world/level/tile/HeavyTile.cpp \ +../../../src/world/level/tile/LightGemTile.cpp \ +../../../src/world/level/tile/MelonTile.cpp \ +../../../src/world/level/tile/Mushroom.cpp \ +../../../src/world/level/tile/NetherReactor.cpp \ +../../../src/world/level/tile/NetherReactorPattern.cpp \ +../../../src/world/level/tile/StairTile.cpp \ +../../../src/world/level/tile/StemTile.cpp \ +../../../src/world/level/tile/StoneSlabTile.cpp \ +../../../src/world/level/tile/TallGrass.cpp \ +../../../src/world/level/tile/Tile.cpp \ +../../../src/world/level/tile/TrapDoorTile.cpp \ +../../../src/world/level/tile/entity/ChestTileEntity.cpp \ +../../../src/world/level/tile/entity/NetherReactorTileEntity.cpp \ +../../../src/world/level/tile/entity/SignTileEntity.cpp \ +../../../src/world/level/tile/entity/TileEntity.cpp \ +../../../src/world/level/tile/entity/FurnaceTileEntity.cpp \ +../../../src/world/phys/HitResult.cpp + +LOCAL_CFLAGS := -DPLATFORM_ANDROID -DPRE_ANDROID23 -Wno-narrowing $(LOCAL_CFLAGS) +LOCAL_CPPFLAGS := -std=c++14 -frtti +LOCAL_CPPFLAGS += -frtti +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../src + +#LOCAL_CFLAGS := -DANDROID_PUBLISH -DDEMO_MODE $(LOCAL_CFLAGS) +#LOCAL_CFLAGS := -DANDROID_PUBLISH $(LOCAL_CFLAGS) + +#LOCAL_CFLAGS := -DDEMO_MODE -DGLDEBUG $(LOCAL_CFLAGS) +#LOCAL_CFLAGS := -DGLDEBUG $(LOCAL_CFLAGS) + +LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM -lOpenSLES +LOCAL_STATIC_LIBRARIES := android_native_app_glue RakNet + +#LOCAL_CPP_FEATURES := exceptions + +TARGET_ARCH_ABI := armeabi-v7a + +include $(BUILD_SHARED_LIBRARY) + +# NOTE: environment var NDK_MODULE_PATH needs to point to lib_projects folder +$(call import-module,android/native_app_glue) +$(call import-module, raknet/jni) diff --git a/project/android/proguard.cfg b/project/android/proguard.cfg index af9a84e..2dddb62 100755 --- a/project/android/proguard.cfg +++ b/project/android/proguard.cfg @@ -1,68 +1,68 @@ --optimizationpasses 5 --dontusemixedcaseclassnames --dontskipnonpubliclibraryclasses --dontpreverify --printseeds --verbose --optimizations !code/simplification/arithmetic,!field/*,!class/merging/* - --keep public class com.mojang.minecraftpe.GameModeButton --keep public class com.mojang.android.StringValue --keep public class * extends android.app.Activity --keep public class * extends com.mojang.minecraftpe.MainActivity --keep public class * extends MainActivity --keep public class * extends android.app.Application --keep public class * extends android.app.Service --keep public class * extends android.content.BroadcastReceiver --keep public class * extends android.content.ContentProvider --keep public class com.android.vending.licensing.ILicensingService - --keep,allowobfuscation class com.mojang.** { *** *(...); } - --keepclasseswithmembernames class * { - native ; -} - --keepclasseswithmembernames class * { - public (android.content.Context, android.util.AttributeSet); -} - --keepclasseswithmembernames class * { - public (android.content.Context, android.util.AttributeSet, int); -} - --keepclassmembers enum * { - public static **[] values(); - public static ** valueOf(java.lang.String); -} - --keepclassmembers class * extends android.app.Activity { - static public void saveScreenshot(java.lang.String, int, int, int[]); - public int[] getImageData(java.lang.String); - public byte[] getFileDataBytes(java.lang.String); - public java.lang.String getPlatformStringVar(int); - public int getScreenWidth(); - public int getScreenHeight(); - public float getPixelsPerMillimeter(); - public int checkLicense(); - public boolean isNetworkEnabled(boolean); - public java.lang.String getDateString(int); - public boolean hasBuyButtonWhenInvalidLicense(); - public void postScreenshotToFacebook(java.lang.String, int, int, int[]); - public int getKeyFromKeyCode(int, int, int); - public void quit(); - public void displayDialog(int); - public void tick(); - public java.lang.String[] getOptionStrings(); - public void buyGame(); - public boolean isTouchscreen(); - public void setIsPowerVR(boolean); - public void initiateUserInput(int); - public int getUserInputStatus(); - public java.lang.String[] getUserInputString(); - public void vibrate(int); -} - --keep class * implements android.os.Parcelable { - public static final android.os.Parcelable$Creator *; -} +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-printseeds +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class com.mojang.minecraftpe.GameModeButton +-keep public class com.mojang.android.StringValue +-keep public class * extends android.app.Activity +-keep public class * extends com.mojang.minecraftpe.MainActivity +-keep public class * extends MainActivity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class com.android.vending.licensing.ILicensingService + +-keep,allowobfuscation class com.mojang.** { *** *(...); } + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keepclassmembers class * extends android.app.Activity { + static public void saveScreenshot(java.lang.String, int, int, int[]); + public int[] getImageData(java.lang.String); + public byte[] getFileDataBytes(java.lang.String); + public java.lang.String getPlatformStringVar(int); + public int getScreenWidth(); + public int getScreenHeight(); + public float getPixelsPerMillimeter(); + public int checkLicense(); + public boolean isNetworkEnabled(boolean); + public java.lang.String getDateString(int); + public boolean hasBuyButtonWhenInvalidLicense(); + public void postScreenshotToFacebook(java.lang.String, int, int, int[]); + public int getKeyFromKeyCode(int, int, int); + public void quit(); + public void displayDialog(int); + public void tick(); + public java.lang.String[] getOptionStrings(); + public void buyGame(); + public boolean isTouchscreen(); + public void setIsPowerVR(boolean); + public void initiateUserInput(int); + public int getUserInputStatus(); + public java.lang.String[] getUserInputString(); + public void vibrate(int); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/project/android/res/drawable/bgtiled.xml b/project/android/res/drawable/bgtiled.xml index 006dcf2..4935bb1 100755 --- a/project/android/res/drawable/bgtiled.xml +++ b/project/android/res/drawable/bgtiled.xml @@ -1,4 +1,4 @@ - \ No newline at end of file diff --git a/project/android/res/drawable/btn_nw_cancel.xml b/project/android/res/drawable/btn_nw_cancel.xml index a37a22a..e7d53fa 100755 --- a/project/android/res/drawable/btn_nw_cancel.xml +++ b/project/android/res/drawable/btn_nw_cancel.xml @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/project/android/res/drawable/btn_nw_create.xml b/project/android/res/drawable/btn_nw_create.xml index 022379d..492930a 100755 --- a/project/android/res/drawable/btn_nw_create.xml +++ b/project/android/res/drawable/btn_nw_create.xml @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/project/android/res/drawable/btngamemode.xml b/project/android/res/drawable/btngamemode.xml index a19299b..eea1c73 100755 --- a/project/android/res/drawable/btngamemode.xml +++ b/project/android/res/drawable/btngamemode.xml @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/project/android/res/layout/mainmenu_options.xml b/project/android/res/layout/mainmenu_options.xml index 9cb5b81..77e4217 100755 --- a/project/android/res/layout/mainmenu_options.xml +++ b/project/android/res/layout/mainmenu_options.xml @@ -1,19 +1,19 @@ - - - - - - + + + + + + diff --git a/project/android/res/layout/rename_mp_world.xml b/project/android/res/layout/rename_mp_world.xml index b6a0780..7ab00ba 100755 --- a/project/android/res/layout/rename_mp_world.xml +++ b/project/android/res/layout/rename_mp_world.xml @@ -1,29 +1,29 @@ - - - - - - + + + + + + diff --git a/project/android/res/values/strings.xml b/project/android/res/values/strings.xml index 7e66a1f..e0b8cd2 100755 --- a/project/android/res/values/strings.xml +++ b/project/android/res/values/strings.xml @@ -1,40 +1,40 @@ - - - Minecraft - Pocket Edition - Minecraft - Pocket Edition Demo + + + Minecraft - Pocket Edition + Minecraft - Pocket Edition Demo true - Minecraft PE - Minecraft PE Demo - - - World name - Unnamed world - World Generator seed. Leave blank for random. - Leave blank for random seed - Mobs, health and gather resources - Unlimited resources and flying - - - Save world as - Saved World - - - Username - Enter your username - Server visible by default - Sensitivity - Invert Y-axis - Lefty - Use touch screen - Split touch controls - Vibration - Slight vibration when blocks are destroyed - Fancy graphics - - - Lower graphics quality - Shorter view distance and disables fancy rendering - Peaceful mode - No hostile mobs, heal automatically - - + Minecraft PE + Minecraft PE Demo + + + World name + Unnamed world + World Generator seed. Leave blank for random. + Leave blank for random seed + Mobs, health and gather resources + Unlimited resources and flying + + + Save world as + Saved World + + + Username + Enter your username + Server visible by default + Sensitivity + Invert Y-axis + Lefty + Use touch screen + Split touch controls + Vibration + Slight vibration when blocks are destroyed + Fancy graphics + + + Lower graphics quality + Shorter view distance and disables fancy rendering + Peaceful mode + No hostile mobs, heal automatically + + diff --git a/project/android/res/xml/preferences.xml b/project/android/res/xml/preferences.xml index 8c13363..9a531b1 100755 --- a/project/android/res/xml/preferences.xml +++ b/project/android/res/xml/preferences.xml @@ -1,105 +1,105 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/project/android/src/com/mojang/android/EditTextAscii.java b/project/android/src/com/mojang/android/EditTextAscii.java index 3b0c9bb..5640c91 100755 --- a/project/android/src/com/mojang/android/EditTextAscii.java +++ b/project/android/src/com/mojang/android/EditTextAscii.java @@ -1,58 +1,58 @@ -package com.mojang.android; - -import android.content.Context; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.AttributeSet; -import android.widget.EditText; - -public class EditTextAscii extends EditText - implements TextWatcher { - - public EditTextAscii(Context context) { - super(context); - _init(); - } - - public EditTextAscii(Context context, AttributeSet attrs) { - super(context, attrs); - _init(); - } - - public EditTextAscii(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - _init(); - } - - private void _init() { - this.addTextChangedListener(this); - } - - // - // TextWatcher - // - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} - //@Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { } - //@Override - public void afterTextChanged(Editable e) { - String s = e.toString(); - String sanitized = sanitize(s); - - if (!s.equals(sanitized)) { - e.replace(0, e.length(), sanitized); - } - } - - static public String sanitize(String s) { - StringBuilder sb = new StringBuilder(s.length()); - - for (int i = 0; i < s.length(); ++i) { - char ch = s.charAt(i); - if (ch < 128) - sb.append(ch); - } - return sb.toString(); - } -} +package com.mojang.android; + +import android.content.Context; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.widget.EditText; + +public class EditTextAscii extends EditText + implements TextWatcher { + + public EditTextAscii(Context context) { + super(context); + _init(); + } + + public EditTextAscii(Context context, AttributeSet attrs) { + super(context, attrs); + _init(); + } + + public EditTextAscii(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + _init(); + } + + private void _init() { + this.addTextChangedListener(this); + } + + // + // TextWatcher + // + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + //@Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { } + //@Override + public void afterTextChanged(Editable e) { + String s = e.toString(); + String sanitized = sanitize(s); + + if (!s.equals(sanitized)) { + e.replace(0, e.length(), sanitized); + } + } + + static public String sanitize(String s) { + StringBuilder sb = new StringBuilder(s.length()); + + for (int i = 0; i < s.length(); ++i) { + char ch = s.charAt(i); + if (ch < 128) + sb.append(ch); + } + return sb.toString(); + } +} diff --git a/project/android/src/com/mojang/android/StringValue.java b/project/android/src/com/mojang/android/StringValue.java index f568c05..4f3627e 100755 --- a/project/android/src/com/mojang/android/StringValue.java +++ b/project/android/src/com/mojang/android/StringValue.java @@ -1,5 +1,5 @@ -package com.mojang.android; - -public interface StringValue { - public String getStringValue(); -} +package com.mojang.android; + +public interface StringValue { + public String getStringValue(); +} diff --git a/project/android/src/com/mojang/android/licensing/LicenseCodes.java b/project/android/src/com/mojang/android/licensing/LicenseCodes.java index 63638c7..b6b9ad6 100755 --- a/project/android/src/com/mojang/android/licensing/LicenseCodes.java +++ b/project/android/src/com/mojang/android/licensing/LicenseCodes.java @@ -1,29 +1,29 @@ -package com.mojang.android.licensing; - -///see LicenseResult.h in C++ project -public class LicenseCodes { - - // Something's not ready, call again later - public static final int WAIT_PLATFORM_NOT_READY = -2; // Note used from java - public static final int WAIT_SERVER_NOT_READY = -1; - - // License is ok - public static final int LICENSE_OK = 0; - public static final int LICENSE_TRIAL_OK = 1; - - // License is not working in one way or another - public static final int LICENSE_VALIDATION_FAILED = 50; - public static final int ITEM_NOT_FOUND = 51; - public static final int LICENSE_NOT_FOUND = 52; - public static final int ERROR_CONTENT_HANDLER = 100; - public static final int ERROR_ILLEGAL_ARGUMENT = 101; - public static final int ERROR_SECURITY = 102; - public static final int ERROR_INPUT_OUTPUT = 103; - public static final int ERROR_ILLEGAL_STATE = 104; - public static final int ERROR_NULL_POINTER = 105; - public static final int ERROR_GENERAL = 106; - public static final int ERROR_UNABLE_TO_CONNECT_TO_CDS = 107; - - // The call went wrong so we didn't get a license value at all - public static final int LICENSE_CHECK_EXCEPTION = 200; -} +package com.mojang.android.licensing; + +///see LicenseResult.h in C++ project +public class LicenseCodes { + + // Something's not ready, call again later + public static final int WAIT_PLATFORM_NOT_READY = -2; // Note used from java + public static final int WAIT_SERVER_NOT_READY = -1; + + // License is ok + public static final int LICENSE_OK = 0; + public static final int LICENSE_TRIAL_OK = 1; + + // License is not working in one way or another + public static final int LICENSE_VALIDATION_FAILED = 50; + public static final int ITEM_NOT_FOUND = 51; + public static final int LICENSE_NOT_FOUND = 52; + public static final int ERROR_CONTENT_HANDLER = 100; + public static final int ERROR_ILLEGAL_ARGUMENT = 101; + public static final int ERROR_SECURITY = 102; + public static final int ERROR_INPUT_OUTPUT = 103; + public static final int ERROR_ILLEGAL_STATE = 104; + public static final int ERROR_NULL_POINTER = 105; + public static final int ERROR_GENERAL = 106; + public static final int ERROR_UNABLE_TO_CONNECT_TO_CDS = 107; + + // The call went wrong so we didn't get a license value at all + public static final int LICENSE_CHECK_EXCEPTION = 200; +} diff --git a/project/android/src/com/mojang/android/preferences/SliderPreference.java b/project/android/src/com/mojang/android/preferences/SliderPreference.java index 2d40586..cfcc2c2 100755 --- a/project/android/src/com/mojang/android/preferences/SliderPreference.java +++ b/project/android/src/com/mojang/android/preferences/SliderPreference.java @@ -1,233 +1,233 @@ -package com.mojang.android.preferences; - -import android.content.Context; -import android.content.res.Resources; -import android.preference.DialogPreference; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.widget.LinearLayout; -import android.widget.SeekBar; -import android.widget.TextView; - -public class SliderPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener { - - private static final String androidns = "http://schemas.android.com/apk/res/android"; - - private Context _context; - - //private TextView _minValueTextView; - private TextView _valueTextView; - //private TextView _maxValueTextView; - private SeekBar _seekBar; - - private String _valueSuffix; - - private int _defaultValue; // Stores the default preference value when none has been set - private int _maxValue; // Stores the upper preference value bound - private int _value; // Stores the value of the preference - private int _minValue; // Stores the minimum preference value bound - - public SliderPreference(Context context, AttributeSet attrs) { - super(context, attrs); - - _context = context; - - _valueSuffix = getResourceValueFromAttribute(attrs, androidns, "text", ""); - - _defaultValue = getResourceValueFromAttribute(attrs, androidns, "defaultValue", 0); - _maxValue = getResourceValueFromAttribute(attrs, androidns, "max", 100); - _minValue = getResourceValueFromAttribute(attrs, null, "min", 0); - - // Set the default value of the preference to the default value found in attribute - setDefaultValue((Integer) _defaultValue); - } - //public SliderPreference(Context context, AttributeSet attrs, int) - - @Override - protected View onCreateDialogView() { - LinearLayout.LayoutParams params; - LinearLayout layout = new LinearLayout(getContext()); - layout.setOrientation(LinearLayout.VERTICAL); - layout.setPadding(6,6,6,6); - -// mSplashText = new TextView(_context); -// if (mDialogMessage != null) -// mSplashText.setText(mDialogMessage); -// layout.addView(mSplashText); - - _valueTextView = new TextView(_context); - _valueTextView.setGravity(Gravity.CENTER_HORIZONTAL); - _valueTextView.setTextSize(32); - params = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.FILL_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT); - layout.addView(_valueTextView, params); - - _seekBar = new SeekBar(_context); - _seekBar.setOnSeekBarChangeListener(this); - layout.addView(_seekBar, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); - - if (shouldPersist()) - _value = getPersistedInt(_defaultValue); - - _seekBar.setMax(_maxValue); - _seekBar.setProgress(_value); - return layout; - } - - @Override - protected void onSetInitialValue(boolean restore, Object defaultValue) { - super.onSetInitialValue(restore, defaultValue); - if (restore) - _value = shouldPersist() ? getPersistedInt(_defaultValue) : 0; - else - _value = (Integer) defaultValue; - } - - -/* - @Override - protected void onBindView(View view) { - super.onBindView(view); - // Bind _seekBar to the layout's internal view - _seekBar = (SeekBar) view.findViewById(R.id.slider_preference_seekbar); - - // Setup it's listeners and parameters - _seekBar.setMax(translateValueToSeekBar(_maxValue)); - _seekBar.setProgress(translateValueToSeekBar(_value)); - _seekBar.setOnSeekBarChangeListener(this); - - // Bind mTextView to the layout's internal view - _valueTextView = (TextView) view.findViewById(R.id.slider_preference_value); - - // Setup it's parameters - _valueTextView.setText(getValueText(_value)); - - // Setup min and max value text views - _minValueTextView = (TextView) view.findViewById(R.id.slider_preference_min_value); - _minValueTextView.setText(getValueText(_minValue)); - _maxValueTextView = (TextView) view.findViewById(R.id.slider_preference_max_value); - _maxValueTextView.setText(getValueText(_maxValue)); - } -*/ - - //@Override - public void onProgressChanged(SeekBar seekBar, int value, boolean fromUser) { - // Change mTextView and _value to the current seekbar value - _value = translateValueFro_seekBar(value); - _valueTextView.setText(getValueText(_value)); - if (shouldPersist()) - persistInt(translateValueFro_seekBar(value)); - callChangeListener(new Integer(_value)); - } - - //@Override - public void onStartTrackingTouch(SeekBar seekBar) { - // Not used - } - - //@Override - public void onStopTrackingTouch(SeekBar seekBar) { - // Not used - } - - private String getValueText(int value) { - String t = String.valueOf(value); - return t.concat(_valueSuffix); - } - - /** - * Retrieves a value from the resources of this slider preference's context - * based on an attribute pointing to that resource. - * - * @param attrs - * The attribute set to search - * - * @param namespace - * The namespace of the attribute - * - * @param name - * The name of the attribute - * - * @param defaultValue - * The default value returned if no resource is found - * - * @return - * The resource value - */ - private int getResourceValueFromAttribute(AttributeSet attrs, String namespace, String name, int defaultValue) { - Resources res = getContext().getResources(); - int valueID = attrs.getAttributeResourceValue(namespace, name, 0); - if (valueID != 0) { - return res.getInteger(valueID); - } - else { - return attrs.getAttributeIntValue(namespace, name, defaultValue); - } - } - - /** - * Retrieves a value from the resources of this slider preference's context - * based on an attribute pointing to that resource. - * - * @param attrs - * The attribute set to search - * - * @param namespace - * The namespace of the attribute - * - * @param name - * The name of the attribute - * - * @param defaultValue - * The default value returned if no resource is found - * - * @return - * The resource value - */ - private String getResourceValueFromAttribute(AttributeSet attrs, String namespace, String name, String defaultValue) { - Resources res = getContext().getResources(); - int valueID = attrs.getAttributeResourceValue(namespace, name, 0); - if (valueID != 0) { - return res.getString(valueID); - } - else { - String value = attrs.getAttributeValue(namespace, name); - if (value != null) { - return value; - } - else { - return defaultValue; - } - } - } - - /** - * Translates a value from this slider preference's seekbar by - * adjusting it for a set perceived minimum value. - * - * @param value - * The value to be translated from the seekbar - * - * @return - * A value equal to the value passed plus the currently set minimum value. - */ - private int translateValueFro_seekBar(int value) { - return value + _minValue; - } - - /** - * Translates a value for when setting this slider preference's seekbar by - * adjusting it for a set perceived minimum value. - * - * @param value - * The value to be translated for use - * - * @return - * A value equal to the value passed minus the currently set minimum value. - */ - private int translateValueToSeekBar(int value) { - return value - _minValue; - } -} +package com.mojang.android.preferences; + +import android.content.Context; +import android.content.res.Resources; +import android.preference.DialogPreference; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.SeekBar; +import android.widget.TextView; + +public class SliderPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener { + + private static final String androidns = "http://schemas.android.com/apk/res/android"; + + private Context _context; + + //private TextView _minValueTextView; + private TextView _valueTextView; + //private TextView _maxValueTextView; + private SeekBar _seekBar; + + private String _valueSuffix; + + private int _defaultValue; // Stores the default preference value when none has been set + private int _maxValue; // Stores the upper preference value bound + private int _value; // Stores the value of the preference + private int _minValue; // Stores the minimum preference value bound + + public SliderPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + _context = context; + + _valueSuffix = getResourceValueFromAttribute(attrs, androidns, "text", ""); + + _defaultValue = getResourceValueFromAttribute(attrs, androidns, "defaultValue", 0); + _maxValue = getResourceValueFromAttribute(attrs, androidns, "max", 100); + _minValue = getResourceValueFromAttribute(attrs, null, "min", 0); + + // Set the default value of the preference to the default value found in attribute + setDefaultValue((Integer) _defaultValue); + } + //public SliderPreference(Context context, AttributeSet attrs, int) + + @Override + protected View onCreateDialogView() { + LinearLayout.LayoutParams params; + LinearLayout layout = new LinearLayout(getContext()); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setPadding(6,6,6,6); + +// mSplashText = new TextView(_context); +// if (mDialogMessage != null) +// mSplashText.setText(mDialogMessage); +// layout.addView(mSplashText); + + _valueTextView = new TextView(_context); + _valueTextView.setGravity(Gravity.CENTER_HORIZONTAL); + _valueTextView.setTextSize(32); + params = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.FILL_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT); + layout.addView(_valueTextView, params); + + _seekBar = new SeekBar(_context); + _seekBar.setOnSeekBarChangeListener(this); + layout.addView(_seekBar, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + + if (shouldPersist()) + _value = getPersistedInt(_defaultValue); + + _seekBar.setMax(_maxValue); + _seekBar.setProgress(_value); + return layout; + } + + @Override + protected void onSetInitialValue(boolean restore, Object defaultValue) { + super.onSetInitialValue(restore, defaultValue); + if (restore) + _value = shouldPersist() ? getPersistedInt(_defaultValue) : 0; + else + _value = (Integer) defaultValue; + } + + +/* + @Override + protected void onBindView(View view) { + super.onBindView(view); + // Bind _seekBar to the layout's internal view + _seekBar = (SeekBar) view.findViewById(R.id.slider_preference_seekbar); + + // Setup it's listeners and parameters + _seekBar.setMax(translateValueToSeekBar(_maxValue)); + _seekBar.setProgress(translateValueToSeekBar(_value)); + _seekBar.setOnSeekBarChangeListener(this); + + // Bind mTextView to the layout's internal view + _valueTextView = (TextView) view.findViewById(R.id.slider_preference_value); + + // Setup it's parameters + _valueTextView.setText(getValueText(_value)); + + // Setup min and max value text views + _minValueTextView = (TextView) view.findViewById(R.id.slider_preference_min_value); + _minValueTextView.setText(getValueText(_minValue)); + _maxValueTextView = (TextView) view.findViewById(R.id.slider_preference_max_value); + _maxValueTextView.setText(getValueText(_maxValue)); + } +*/ + + //@Override + public void onProgressChanged(SeekBar seekBar, int value, boolean fromUser) { + // Change mTextView and _value to the current seekbar value + _value = translateValueFro_seekBar(value); + _valueTextView.setText(getValueText(_value)); + if (shouldPersist()) + persistInt(translateValueFro_seekBar(value)); + callChangeListener(new Integer(_value)); + } + + //@Override + public void onStartTrackingTouch(SeekBar seekBar) { + // Not used + } + + //@Override + public void onStopTrackingTouch(SeekBar seekBar) { + // Not used + } + + private String getValueText(int value) { + String t = String.valueOf(value); + return t.concat(_valueSuffix); + } + + /** + * Retrieves a value from the resources of this slider preference's context + * based on an attribute pointing to that resource. + * + * @param attrs + * The attribute set to search + * + * @param namespace + * The namespace of the attribute + * + * @param name + * The name of the attribute + * + * @param defaultValue + * The default value returned if no resource is found + * + * @return + * The resource value + */ + private int getResourceValueFromAttribute(AttributeSet attrs, String namespace, String name, int defaultValue) { + Resources res = getContext().getResources(); + int valueID = attrs.getAttributeResourceValue(namespace, name, 0); + if (valueID != 0) { + return res.getInteger(valueID); + } + else { + return attrs.getAttributeIntValue(namespace, name, defaultValue); + } + } + + /** + * Retrieves a value from the resources of this slider preference's context + * based on an attribute pointing to that resource. + * + * @param attrs + * The attribute set to search + * + * @param namespace + * The namespace of the attribute + * + * @param name + * The name of the attribute + * + * @param defaultValue + * The default value returned if no resource is found + * + * @return + * The resource value + */ + private String getResourceValueFromAttribute(AttributeSet attrs, String namespace, String name, String defaultValue) { + Resources res = getContext().getResources(); + int valueID = attrs.getAttributeResourceValue(namespace, name, 0); + if (valueID != 0) { + return res.getString(valueID); + } + else { + String value = attrs.getAttributeValue(namespace, name); + if (value != null) { + return value; + } + else { + return defaultValue; + } + } + } + + /** + * Translates a value from this slider preference's seekbar by + * adjusting it for a set perceived minimum value. + * + * @param value + * The value to be translated from the seekbar + * + * @return + * A value equal to the value passed plus the currently set minimum value. + */ + private int translateValueFro_seekBar(int value) { + return value + _minValue; + } + + /** + * Translates a value for when setting this slider preference's seekbar by + * adjusting it for a set perceived minimum value. + * + * @param value + * The value to be translated for use + * + * @return + * A value equal to the value passed minus the currently set minimum value. + */ + private int translateValueToSeekBar(int value) { + return value - _minValue; + } +} diff --git a/project/android/src/com/mojang/minecraftpe/GameModeButton.java b/project/android/src/com/mojang/minecraftpe/GameModeButton.java index be079b9..df32f0c 100755 --- a/project/android/src/com/mojang/minecraftpe/GameModeButton.java +++ b/project/android/src/com/mojang/minecraftpe/GameModeButton.java @@ -1,77 +1,77 @@ -package com.mojang.minecraftpe; - -import com.mojang.android.StringValue; -import com.mojang.minecraftpe.R; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.TextView; -import android.widget.ToggleButton; - -public class GameModeButton extends ToggleButton implements OnClickListener, StringValue { - public GameModeButton(Context context, AttributeSet attrs) { - super(context, attrs); - _init(); - } - - //@Override - public void onClick(View v) { - _update(); - } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - _update(); - } - @Override - protected void onAttachedToWindow() { - if (!_attached) { - _update(); - _attached = true; - } - } - private boolean _attached = false; - - private void _init() { - setOnClickListener(this); - } - private void _update() { - _setGameType(isChecked()?Survival:Creative); - } - private void _setGameType(int i) { - _type = _clamp(i); - - int id = R.string.gamemode_creative_summary; - if (_type == Survival) - id = R.string.gamemode_survival_summary; - String desc = getContext().getString(id); - - View v = getRootView().findViewById(R.id.labelGameModeDesc); - System.out.println("Mode: " + _type + ", view? " + (v!=null)); - if (desc != null && v != null && v instanceof TextView) { - ((TextView)v).setText(desc); - } - } - - static private int _clamp(int i) { - if (i > Survival) return Survival; - if (i < Creative) return Creative; - return i; - } - - public String getStringValue() { - return getStringForType(_type); - } - static public String getStringForType(int i) { - return new String[] { - "creative", - "survival" - } [_clamp(i)]; - } - - private int _type = 0; - static final int Creative = 0; - static final int Survival = 1; -} +package com.mojang.minecraftpe; + +import com.mojang.android.StringValue; +import com.mojang.minecraftpe.R; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.TextView; +import android.widget.ToggleButton; + +public class GameModeButton extends ToggleButton implements OnClickListener, StringValue { + public GameModeButton(Context context, AttributeSet attrs) { + super(context, attrs); + _init(); + } + + //@Override + public void onClick(View v) { + _update(); + } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + _update(); + } + @Override + protected void onAttachedToWindow() { + if (!_attached) { + _update(); + _attached = true; + } + } + private boolean _attached = false; + + private void _init() { + setOnClickListener(this); + } + private void _update() { + _setGameType(isChecked()?Survival:Creative); + } + private void _setGameType(int i) { + _type = _clamp(i); + + int id = R.string.gamemode_creative_summary; + if (_type == Survival) + id = R.string.gamemode_survival_summary; + String desc = getContext().getString(id); + + View v = getRootView().findViewById(R.id.labelGameModeDesc); + System.out.println("Mode: " + _type + ", view? " + (v!=null)); + if (desc != null && v != null && v instanceof TextView) { + ((TextView)v).setText(desc); + } + } + + static private int _clamp(int i) { + if (i > Survival) return Survival; + if (i < Creative) return Creative; + return i; + } + + public String getStringValue() { + return getStringForType(_type); + } + static public String getStringForType(int i) { + return new String[] { + "creative", + "survival" + } [_clamp(i)]; + } + + private int _type = 0; + static final int Creative = 0; + static final int Survival = 1; +} diff --git a/project/android/src/com/mojang/minecraftpe/MainMenuOptionsActivity.java b/project/android/src/com/mojang/minecraftpe/MainMenuOptionsActivity.java index 1c0294b..5b9301b 100755 --- a/project/android/src/com/mojang/minecraftpe/MainMenuOptionsActivity.java +++ b/project/android/src/com/mojang/minecraftpe/MainMenuOptionsActivity.java @@ -1,197 +1,197 @@ -package com.mojang.minecraftpe; - -import java.util.ArrayList; - -import com.mojang.android.EditTextAscii; -//import com.mojang.minecraftpe.R; - -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceCategory; -import android.preference.PreferenceGroup; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; - -public class MainMenuOptionsActivity extends PreferenceActivity implements - SharedPreferences.OnSharedPreferenceChangeListener -{ - static public final String Multiplayer_Username = "mp_username"; - static public final String Multiplayer_ServerVisible = "mp_server_visible_default"; - static public final String Graphics_Fancy = "gfx_fancygraphics"; - static public final String Graphics_LowQuality = "gfx_lowquality"; - static public final String Controls_InvertMouse = "ctrl_invertmouse"; - static public final String Controls_Sensitivity = "ctrl_sensitivity"; - static public final String Controls_UseTouchscreen = "ctrl_usetouchscreen"; - static public final String Controls_UseTouchJoypad = "ctrl_usetouchjoypad"; - static public final String Controls_FeedbackVibration = "feedback_vibration"; - static public final String Game_DifficultyLevel = "game_difficulty"; - static public final String Internal_Game_DifficultyPeaceful = "game_difficultypeaceful"; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Bundle extras = getIntent().getExtras(); - addPreferencesFromResource(extras.getInt("preferenceId"));//R.xml.preferences); - - //getPreferenceManager().setSharedPreferencesMode(MODE_PRIVATE); - - PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); - PreferenceScreen s = getPreferenceScreen(); - - if (PreferenceManager.getDefaultSharedPreferences(this).contains(Multiplayer_Username)) { - previousName = PreferenceManager.getDefaultSharedPreferences(this).getString(Multiplayer_Username, null); - } - - _validator = new PreferenceValidator(this); - readBackAll(s); - _validator.commit(); - } - - private void readBackAll(PreferenceGroup g) { - traversePreferences(g, new PreferenceTraverser() { - void onPreference(Preference p) { readBack(p); _validator.validate(p); } - }); - } - private void traversePreferences(PreferenceGroup g, PreferenceTraverser pt) { - int size = g.getPreferenceCount(); - for (int i = 0; i < size; ++i) { - Preference p = g.getPreference(i); - if (p instanceof PreferenceGroup) { - PreferenceGroup pg = (PreferenceGroup)p; - pt.onPreferenceGroup(pg); - traversePreferences(pg, pt); - } - else - pt.onPreference(p); - } - } - private void readBack(Preference p) { - if (p == null) - return; - - //System.out.println("pref: " + p.toString()); - - if (p instanceof EditTextPreference) { - EditTextPreference e = (EditTextPreference) p; - p.setSummary("'" + e.getText() + "'"); - } - } - - //@Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - Preference p = findPreference(key); - _validator.validate(sharedPreferences, key); - - if (p instanceof EditTextPreference) { - EditTextPreference e = (EditTextPreference) p; - Editor editor = sharedPreferences.edit(); - - String s = e.getText(); - String sanitized = EditTextAscii.sanitize(s).trim(); - - if (key.equals(Multiplayer_Username) && sanitized == null || sanitized.length() == 0) { - sanitized = previousName; - if (sanitized == null || sanitized.equals("")) { - sanitized = "Steve"; - previousName = sanitized; - } - } - - if (!s.equals(sanitized)) { - editor.putString(key, sanitized); - editor.commit(); - e.setText(sanitized); - } - } - - readBack(p); - } - - String previousName; - PreferenceValidator _validator; -} - -class PreferenceValidator { - static private class Pref { - Pref(PreferenceGroup g, Preference p) { - this.g = g; - this.p = p; - } - PreferenceGroup g; - Preference p; - } - private PreferenceActivity _prefs; - private ArrayList _arrayList = new ArrayList(); - - public PreferenceValidator(PreferenceActivity prefs) { - _prefs = prefs; - } - - public void commit() { - //System.err.println("ERR: " + _arrayList.size()); - for (int i = 0; i < _arrayList.size(); ++i) { - PreferenceGroup g = _arrayList.get(i).g; - Preference p = _arrayList.get(i).p; - g.removePreference(p); - } - } - - public void validate(Preference p) { - validate(PreferenceManager.getDefaultSharedPreferences(_prefs), p.getKey()); - } - public void validate(SharedPreferences preferences, String key) { - Preference p = findPreference(key); - - if (p instanceof CheckBoxPreference) { - //CheckBoxPreference e = (CheckBoxPreference) p; - if (key.equals(MainMenuOptionsActivity.Graphics_LowQuality)) { - boolean isShort = preferences.getBoolean(MainMenuOptionsActivity.Graphics_LowQuality, false); - CheckBoxPreference fancyPref = (CheckBoxPreference)findPreference(MainMenuOptionsActivity.Graphics_Fancy); - if (fancyPref != null) { - fancyPref.setEnabled(isShort == false); - if (isShort) - fancyPref.setChecked(false); - } - } - if (key.equals(MainMenuOptionsActivity.Graphics_Fancy)) { - CheckBoxPreference fancyPref = (CheckBoxPreference) p; - //System.err.println("Is PowerVR? : " + MainActivity.isPowerVR()); - if (MainActivity.isPowerVR()) { - fancyPref.setSummary("Experimental on this device!"); - } - } - - if (p.getKey().equals(MainMenuOptionsActivity.Controls_UseTouchscreen)) { - boolean hasOtherPrimaryControls = MainActivity.isXperiaPlay(); - if (!hasOtherPrimaryControls) { - PreferenceCategory mCategory = (PreferenceCategory) findPreference("category_graphics"); - _arrayList.add(new Pref(mCategory, p)); - } - p.setEnabled(hasOtherPrimaryControls); - p.setDefaultValue( !hasOtherPrimaryControls ); - - if (hasOtherPrimaryControls) { - CheckBoxPreference pp = (CheckBoxPreference) p; - CheckBoxPreference j = (CheckBoxPreference)findPreference(MainMenuOptionsActivity.Controls_UseTouchJoypad); - j.setEnabled(pp.isChecked()); - } - } - } - } - - private Preference findPreference(String key) { - return _prefs.findPreference(key); - } -} - -abstract class PreferenceTraverser { - void onPreference(Preference p) {} - void onPreferenceGroup(PreferenceGroup p) {} - } +package com.mojang.minecraftpe; + +import java.util.ArrayList; + +import com.mojang.android.EditTextAscii; +//import com.mojang.minecraftpe.R; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.EditTextPreference; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceCategory; +import android.preference.PreferenceGroup; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; + +public class MainMenuOptionsActivity extends PreferenceActivity implements + SharedPreferences.OnSharedPreferenceChangeListener +{ + static public final String Multiplayer_Username = "mp_username"; + static public final String Multiplayer_ServerVisible = "mp_server_visible_default"; + static public final String Graphics_Fancy = "gfx_fancygraphics"; + static public final String Graphics_LowQuality = "gfx_lowquality"; + static public final String Controls_InvertMouse = "ctrl_invertmouse"; + static public final String Controls_Sensitivity = "ctrl_sensitivity"; + static public final String Controls_UseTouchscreen = "ctrl_usetouchscreen"; + static public final String Controls_UseTouchJoypad = "ctrl_usetouchjoypad"; + static public final String Controls_FeedbackVibration = "feedback_vibration"; + static public final String Game_DifficultyLevel = "game_difficulty"; + static public final String Internal_Game_DifficultyPeaceful = "game_difficultypeaceful"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle extras = getIntent().getExtras(); + addPreferencesFromResource(extras.getInt("preferenceId"));//R.xml.preferences); + + //getPreferenceManager().setSharedPreferencesMode(MODE_PRIVATE); + + PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); + PreferenceScreen s = getPreferenceScreen(); + + if (PreferenceManager.getDefaultSharedPreferences(this).contains(Multiplayer_Username)) { + previousName = PreferenceManager.getDefaultSharedPreferences(this).getString(Multiplayer_Username, null); + } + + _validator = new PreferenceValidator(this); + readBackAll(s); + _validator.commit(); + } + + private void readBackAll(PreferenceGroup g) { + traversePreferences(g, new PreferenceTraverser() { + void onPreference(Preference p) { readBack(p); _validator.validate(p); } + }); + } + private void traversePreferences(PreferenceGroup g, PreferenceTraverser pt) { + int size = g.getPreferenceCount(); + for (int i = 0; i < size; ++i) { + Preference p = g.getPreference(i); + if (p instanceof PreferenceGroup) { + PreferenceGroup pg = (PreferenceGroup)p; + pt.onPreferenceGroup(pg); + traversePreferences(pg, pt); + } + else + pt.onPreference(p); + } + } + private void readBack(Preference p) { + if (p == null) + return; + + //System.out.println("pref: " + p.toString()); + + if (p instanceof EditTextPreference) { + EditTextPreference e = (EditTextPreference) p; + p.setSummary("'" + e.getText() + "'"); + } + } + + //@Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + Preference p = findPreference(key); + _validator.validate(sharedPreferences, key); + + if (p instanceof EditTextPreference) { + EditTextPreference e = (EditTextPreference) p; + Editor editor = sharedPreferences.edit(); + + String s = e.getText(); + String sanitized = EditTextAscii.sanitize(s).trim(); + + if (key.equals(Multiplayer_Username) && sanitized == null || sanitized.length() == 0) { + sanitized = previousName; + if (sanitized == null || sanitized.equals("")) { + sanitized = "Steve"; + previousName = sanitized; + } + } + + if (!s.equals(sanitized)) { + editor.putString(key, sanitized); + editor.commit(); + e.setText(sanitized); + } + } + + readBack(p); + } + + String previousName; + PreferenceValidator _validator; +} + +class PreferenceValidator { + static private class Pref { + Pref(PreferenceGroup g, Preference p) { + this.g = g; + this.p = p; + } + PreferenceGroup g; + Preference p; + } + private PreferenceActivity _prefs; + private ArrayList _arrayList = new ArrayList(); + + public PreferenceValidator(PreferenceActivity prefs) { + _prefs = prefs; + } + + public void commit() { + //System.err.println("ERR: " + _arrayList.size()); + for (int i = 0; i < _arrayList.size(); ++i) { + PreferenceGroup g = _arrayList.get(i).g; + Preference p = _arrayList.get(i).p; + g.removePreference(p); + } + } + + public void validate(Preference p) { + validate(PreferenceManager.getDefaultSharedPreferences(_prefs), p.getKey()); + } + public void validate(SharedPreferences preferences, String key) { + Preference p = findPreference(key); + + if (p instanceof CheckBoxPreference) { + //CheckBoxPreference e = (CheckBoxPreference) p; + if (key.equals(MainMenuOptionsActivity.Graphics_LowQuality)) { + boolean isShort = preferences.getBoolean(MainMenuOptionsActivity.Graphics_LowQuality, false); + CheckBoxPreference fancyPref = (CheckBoxPreference)findPreference(MainMenuOptionsActivity.Graphics_Fancy); + if (fancyPref != null) { + fancyPref.setEnabled(isShort == false); + if (isShort) + fancyPref.setChecked(false); + } + } + if (key.equals(MainMenuOptionsActivity.Graphics_Fancy)) { + CheckBoxPreference fancyPref = (CheckBoxPreference) p; + //System.err.println("Is PowerVR? : " + MainActivity.isPowerVR()); + if (MainActivity.isPowerVR()) { + fancyPref.setSummary("Experimental on this device!"); + } + } + + if (p.getKey().equals(MainMenuOptionsActivity.Controls_UseTouchscreen)) { + boolean hasOtherPrimaryControls = MainActivity.isXperiaPlay(); + if (!hasOtherPrimaryControls) { + PreferenceCategory mCategory = (PreferenceCategory) findPreference("category_graphics"); + _arrayList.add(new Pref(mCategory, p)); + } + p.setEnabled(hasOtherPrimaryControls); + p.setDefaultValue( !hasOtherPrimaryControls ); + + if (hasOtherPrimaryControls) { + CheckBoxPreference pp = (CheckBoxPreference) p; + CheckBoxPreference j = (CheckBoxPreference)findPreference(MainMenuOptionsActivity.Controls_UseTouchJoypad); + j.setEnabled(pp.isChecked()); + } + } + } + } + + private Preference findPreference(String key) { + return _prefs.findPreference(key); + } +} + +abstract class PreferenceTraverser { + void onPreference(Preference p) {} + void onPreferenceGroup(PreferenceGroup p) {} + } \ No newline at end of file diff --git a/project/android/src/com/mojang/minecraftpe/Minecraft_Market.java b/project/android/src/com/mojang/minecraftpe/Minecraft_Market.java index cacc2ad..aae5015 100755 --- a/project/android/src/com/mojang/minecraftpe/Minecraft_Market.java +++ b/project/android/src/com/mojang/minecraftpe/Minecraft_Market.java @@ -1,14 +1,14 @@ -package com.mojang.minecraftpe; - -import android.content.Intent; -import android.net.Uri; -import com.mojang.minecraftpe.MainActivity; - -public class Minecraft_Market extends MainActivity { - @Override - public void buyGame() { - final Uri buyLink = Uri.parse("market://details?id=com.mojang.minecraftpe"); - Intent marketIntent = new Intent( Intent.ACTION_VIEW, buyLink ); - startActivity(marketIntent); - } -} +package com.mojang.minecraftpe; + +import android.content.Intent; +import android.net.Uri; +import com.mojang.minecraftpe.MainActivity; + +public class Minecraft_Market extends MainActivity { + @Override + public void buyGame() { + final Uri buyLink = Uri.parse("market://details?id=com.mojang.minecraftpe"); + Intent marketIntent = new Intent( Intent.ACTION_VIEW, buyLink ); + startActivity(marketIntent); + } +} diff --git a/project/android/src/com/mojang/minecraftpe/Minecraft_Market_Demo.java b/project/android/src/com/mojang/minecraftpe/Minecraft_Market_Demo.java index d5a09ab..082c2d4 100755 --- a/project/android/src/com/mojang/minecraftpe/Minecraft_Market_Demo.java +++ b/project/android/src/com/mojang/minecraftpe/Minecraft_Market_Demo.java @@ -1,17 +1,17 @@ -package com.mojang.minecraftpe; - -import android.content.Intent; -import android.net.Uri; -import com.mojang.android.licensing.LicenseCodes; - -public class Minecraft_Market_Demo extends MainActivity { - @Override - public void buyGame() { - final Uri buyLink = Uri.parse("market://details?id=com.mojang.minecraftpe"); - Intent marketIntent = new Intent( Intent.ACTION_VIEW, buyLink ); - startActivity(marketIntent); - } - - @Override - protected boolean isDemo() { return true; } -} +package com.mojang.minecraftpe; + +import android.content.Intent; +import android.net.Uri; +import com.mojang.android.licensing.LicenseCodes; + +public class Minecraft_Market_Demo extends MainActivity { + @Override + public void buyGame() { + final Uri buyLink = Uri.parse("market://details?id=com.mojang.minecraftpe"); + Intent marketIntent = new Intent( Intent.ACTION_VIEW, buyLink ); + startActivity(marketIntent); + } + + @Override + protected boolean isDemo() { return true; } +} diff --git a/project/android/src/com/mojang/minecraftpe/Minecraft_Verizon.java b/project/android/src/com/mojang/minecraftpe/Minecraft_Verizon.java index e4ea527..01d2f02 100755 --- a/project/android/src/com/mojang/minecraftpe/Minecraft_Verizon.java +++ b/project/android/src/com/mojang/minecraftpe/Minecraft_Verizon.java @@ -1,89 +1,89 @@ -package com.mojang.minecraftpe; - -import android.os.Bundle; - -import com.mojang.android.licensing.LicenseCodes; -import com.verizon.vcast.apps.LicenseAuthenticator; - - -public class Minecraft_Verizon extends MainActivity { - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - _licenseLib = new LicenseAuthenticator(this); - _verizonThread = new VerizonLicenseThread(_licenseLib, VCastMarketKeyword, false); - _verizonThread.start(); - } - - @Override - public int checkLicense() { - if (_verizonThread == null) - return _licenseCode; - - if (!_verizonThread.hasCode) - return -1; - - _licenseCode = _verizonThread.returnCode; - _verizonThread = null; - return _licenseCode; - } - @Override - public boolean hasBuyButtonWhenInvalidLicense() { return false; } - - private LicenseAuthenticator _licenseLib; - private VerizonLicenseThread _verizonThread; - private int _licenseCode; - static private final String VCastMarketKeyword = "Minecraft"; -} - -// -// Requests license code from the Verizon VCAST application on the phone -// -class VerizonLicenseThread extends Thread -{ - public VerizonLicenseThread(LicenseAuthenticator licenseLib, String keyword, boolean isTest) { - _keyword = keyword; - _isTest = isTest; - _licenseLib = licenseLib; - } - - public void run() { - if (_isTest) - validateTestLicense(); - else - validateLicense(); - } - - void validateTestLicense() { - try { - //final int status = LicenseAuthenticator.LICENSE_NOT_FOUND; - final int status = LicenseAuthenticator.LICENSE_OK; - returnCode = _licenseLib.checkTestLicense( _keyword, status ); - } - catch (Throwable e) { - returnCode = LicenseCodes.LICENSE_CHECK_EXCEPTION; - } - hasCode = true; - } - - void validateLicense() { - try { - returnCode = _licenseLib.checkLicense( _keyword ); - } - catch (Throwable e) { - returnCode = LicenseCodes.LICENSE_CHECK_EXCEPTION; - //e.printStackTrace(); - } - hasCode = true; - } - - public volatile boolean hasCode = false; - public volatile int returnCode = -1; - - private String _keyword; - private boolean _isTest; - private LicenseAuthenticator _licenseLib; -} +package com.mojang.minecraftpe; + +import android.os.Bundle; + +import com.mojang.android.licensing.LicenseCodes; +import com.verizon.vcast.apps.LicenseAuthenticator; + + +public class Minecraft_Verizon extends MainActivity { + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + _licenseLib = new LicenseAuthenticator(this); + _verizonThread = new VerizonLicenseThread(_licenseLib, VCastMarketKeyword, false); + _verizonThread.start(); + } + + @Override + public int checkLicense() { + if (_verizonThread == null) + return _licenseCode; + + if (!_verizonThread.hasCode) + return -1; + + _licenseCode = _verizonThread.returnCode; + _verizonThread = null; + return _licenseCode; + } + @Override + public boolean hasBuyButtonWhenInvalidLicense() { return false; } + + private LicenseAuthenticator _licenseLib; + private VerizonLicenseThread _verizonThread; + private int _licenseCode; + static private final String VCastMarketKeyword = "Minecraft"; +} + +// +// Requests license code from the Verizon VCAST application on the phone +// +class VerizonLicenseThread extends Thread +{ + public VerizonLicenseThread(LicenseAuthenticator licenseLib, String keyword, boolean isTest) { + _keyword = keyword; + _isTest = isTest; + _licenseLib = licenseLib; + } + + public void run() { + if (_isTest) + validateTestLicense(); + else + validateLicense(); + } + + void validateTestLicense() { + try { + //final int status = LicenseAuthenticator.LICENSE_NOT_FOUND; + final int status = LicenseAuthenticator.LICENSE_OK; + returnCode = _licenseLib.checkTestLicense( _keyword, status ); + } + catch (Throwable e) { + returnCode = LicenseCodes.LICENSE_CHECK_EXCEPTION; + } + hasCode = true; + } + + void validateLicense() { + try { + returnCode = _licenseLib.checkLicense( _keyword ); + } + catch (Throwable e) { + returnCode = LicenseCodes.LICENSE_CHECK_EXCEPTION; + //e.printStackTrace(); + } + hasCode = true; + } + + public volatile boolean hasCode = false; + public volatile int returnCode = -1; + + private String _keyword; + private boolean _isTest; + private LicenseAuthenticator _licenseLib; +} diff --git a/project/android_java/.classpath b/project/android_java/.classpath index 47d3f92..5681cad 100755 --- a/project/android_java/.classpath +++ b/project/android_java/.classpath @@ -1,9 +1,9 @@ - - - - - - - - - + + + + + + + + + diff --git a/project/android_java/.project b/project/android_java/.project index 54b88f3..636e887 100755 --- a/project/android_java/.project +++ b/project/android_java/.project @@ -1,40 +1,40 @@ - - - MinecraftPocketEdition_java - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - - - src_linked - 2 - C:/dev/subversion/mojang/minecraftcpp/trunk/handheld/project/android/src - - - + + + MinecraftPocketEdition_java + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + + + src_linked + 2 + C:/dev/subversion/mojang/minecraftcpp/trunk/handheld/project/android/src + + + diff --git a/project/android_java/ManifestAddons.xml b/project/android_java/ManifestAddons.xml index 0a19b0e..c617173 100755 --- a/project/android_java/ManifestAddons.xml +++ b/project/android_java/ManifestAddons.xml @@ -1,13 +1,13 @@ - - - - - - android:debuggable="true" - - - - - - + + + + + + android:debuggable="true" + + + + + + \ No newline at end of file diff --git a/project/android_java/assets/lang/en_US.lang b/project/android_java/assets/lang/en_US.lang index 8713541..6642290 100755 --- a/project/android_java/assets/lang/en_US.lang +++ b/project/android_java/assets/lang/en_US.lang @@ -1,949 +1,949 @@ - -language.name=English -language.region=US -language.code=en_US - -gui.done=Done -gui.cancel=Cancel -gui.toMenu=Back to title screen -gui.up=Up -gui.down=Down -gui.yes=Yes -gui.no=No - -menu.singleplayer=Singleplayer -menu.multiplayer=Multiplayer -menu.mods=Texture Packs -menu.options=Options... -menu.quit=Quit Game -menu.returnToMenu=Save and Quit to Title -menu.disconnect=Disconnect -menu.returnToGame=Back to Game -menu.switchingLevel=Switching level -menu.generatingLevel=Generating level -menu.loadingLevel=Loading level -menu.generatingTerrain=Building terrain -menu.simulating=Simulating the world for a bit -menu.respawning=Respawning - -selectWorld.title=Select World -selectWorld.empty=empty -selectWorld.world=World -selectWorld.select=Play Selected World -selectWorld.create=Create New World -selectWorld.createDemo=Play New Demo World -selectWorld.delete=Delete -selectWorld.rename=Rename -selectWorld.deleteQuestion=Are you sure you want to delete this world? -selectWorld.deleteWarning=will be lost forever! (A long time!) -selectWorld.deleteButton=Delete -selectWorld.renameButton=Rename -selectWorld.renameTitle=Rename World -selectWorld.conversion=Must be converted! -selectWorld.newWorld=New World -selectWorld.enterName=World Name -selectWorld.resultFolder=Will be saved in: -selectWorld.enterSeed=Seed for the World Generator -selectWorld.seedInfo=Leave blank for a random seed - -gameMode.survival=Survival Mode -gameMode.creative=Creative Mode -gameMode.hardcore=Hardcore Mode! -gameMode.changed=Your game mode has been changed - -selectWorld.gameMode=Game Mode: -selectWorld.gameMode.survival=Survival -selectWorld.gameMode.survival.line1=Search for resources, crafting, gain -selectWorld.gameMode.survival.line2=levels, health and hunger -selectWorld.gameMode.creative=Creative -selectWorld.gameMode.creative.line1=Unlimited resources, free flying and -selectWorld.gameMode.creative.line2=destroy blocks instantly -selectWorld.gameMode.hardcore=Hardcore -selectWorld.gameMode.hardcore.line1=Same as survival mode, locked at hardest -selectWorld.gameMode.hardcore.line2=difficulty, and one life only -selectWorld.moreWorldOptions=More World Options... -selectWorld.mapFeatures=Generate Structures: -selectWorld.mapFeatures.info=Villages, dungeons etc -selectWorld.mapType=World Type: -selectWorld.mapType.normal=Normal -selectWorld.hardcoreMode=Hardcore: -selectWorld.hardcoreMode.info=World is deleted upon death - -generator.default=Default -generator.flat=Superflat - -selectServer.title=Select Server -selectServer.empty=empty -selectServer.select=Join Server -selectServer.direct=Direct Connect -selectServer.edit=Edit -selectServer.delete=Delete -selectServer.add=Add server -selectServer.defaultName=Minecraft Server -selectServer.deleteQuestion=Are you sure you want to remove this server? -selectServer.deleteWarning=will be lost forever! (A long time!) -selectServer.deleteButton=Delete -selectServer.refresh=Refresh -addServer.title=Edit Server Info -addServer.enterName=Server Name -addServer.enterIp=Server Address -addServer.add=Done - -multiplayer.title=Play Multiplayer -multiplayer.connect=Connect -multiplayer.info1=Minecraft Multiplayer is currently not finished, but there -multiplayer.info2=is some buggy early testing going on. -multiplayer.ipinfo=Enter the IP of a server to connect to it: - -multiplayer.downloadingTerrain=Downloading terrain - -multiplayer.stopSleeping=Leave Bed - -demo.day.1=This demo will last five game days, do your best! -demo.day.2=Day Two -demo.day.3=Day Three -demo.day.4=Day Four -demo.day.5=This is your last day! -demo.day.warning=Your time is almost up! -demo.day.6=You have passed your fifth day, use F2 to save a screenshot of your creation -demo.reminder=The demo time has expired, buy the game to continue or start a new world! -demo.help.movement=Use %1$s, %2$s, %3$s, %4$s and the mouse to move around -demo.help.jump=Jump by pressing %1$s -demo.help.inventory=Use %1$s to open your inventory - -connect.connecting=Connecting to the server... -connect.authorizing=Logging in... -connect.failed=Failed to connect to the server - -disconnect.genericReason=%s -disconnect.disconnected=Disconnected by Server -disconnect.lost=Connection Lost -disconnect.kicked=Was kicked from the game -disconnect.timeout=Timed out -disconnect.closed=Connection closed -disconnect.loginFailed=Failed to login -disconnect.loginFailedInfo=Failed to login: %s -disconnect.quitting=Quitting -disconnect.endOfStream=End of stream -disconnect.overflow=Buffer overflow - -options.off=OFF -options.on=ON -options.title=Options -options.controls=Controls... -options.video=Video Settings... -options.language=Language... -options.languageWarning=Language translations may not be 100% accurate -options.videoTitle=Video Settings -options.music=Music -options.sound=Sound -options.invertMouse=Invert Mouse -options.fov=FOV -options.fov.min=Normal -options.fov.max=Quake Pro -options.gamma=Brightness -options.gamma.min=Moody -options.gamma.max=Bright -options.sensitivity=Sensitivity -options.sensitivity.min=*yawn* -options.sensitivity.max=HYPERSPEED!!! -options.renderDistance=Render Distance -options.renderDistance.tiny=Tiny -options.renderDistance.short=Short -options.renderDistance.normal=Normal -options.renderDistance.far=Far -options.viewBobbing=View Bobbing -options.ao=Smooth Lighting -options.anaglyph=3D Anaglyph -options.framerateLimit=Performance -options.difficulty=Difficulty -options.difficulty.peaceful=Peaceful -options.difficulty.easy=Easy -options.difficulty.normal=Normal -options.difficulty.hard=Hard -options.difficulty.hardcore=Hardcore -options.graphics=Graphics -options.graphics.fancy=Fancy -options.graphics.fast=Fast -options.guiScale=GUI Scale -options.guiScale.auto=Auto -options.guiScale.small=Small -options.guiScale.medium=Medium -options.guiScale.large=Large -options.guiScale.larger=Larger -options.guiScale.largest=Largest -options.advancedOpengl=Advanced OpenGL -options.renderClouds=Clouds -options.farWarning1=A 64 bit Java installation is recommended -options.farWarning2=for 'Far' render distance (you have 32 bit) -options.particles=Particles -options.particles.all=All -options.particles.decreased=Decreased -options.particles.minimal=Minimal - -performance.max=Max FPS -performance.balanced=Balanced -performance.powersaver=Power saver - -controls.title=Controls - -key.forward=Forward -key.left=Left -key.back=Back -key.right=Right -key.jump=Jump -key.inventory=Inventory -key.drop=Drop -key.chat=Chat -key.fog=Toggle Fog -key.sneak=Sneak -key.playerlist=List Players -key.attack=Attack -key.use=Use Item -key.pickItem=Pick Block -key.mouseButton=Button %1$s - -texturePack.openFolder=Open texture pack folder -texturePack.title=Select Texture Pack -texturePack.folderInfo=(Place texture pack files here) - -tile.stone.name=Stone -tile.grass.name=Grass Block -tile.dirt.name=Dirt -tile.stonebrick.name=Cobblestone -tile.wood.name=Wooden Planks -tile.sapling.name=Sapling -tile.deadbush.name=Dead Bush -tile.bedrock.name=Bedrock -tile.water.name=Water -tile.lava.name=Lava -tile.sand.name=Sand -tile.sandStone.name=Sandstone -tile.gravel.name=Gravel -tile.oreGold.name=Gold Ore -tile.oreIron.name=Iron Ore -tile.oreCoal.name=Coal Ore -tile.log.name=Wood -tile.leaves.name=Leaves -tile.tallgrass.name=Grass -tile.tallgrass.shrub.name=Shrub -tile.tallgrass.grass.name=Grass -tile.tallgrass.fern.name=Fern -tile.sponge.name=Sponge -tile.glass.name=Glass -tile.thinGlass.name=Glass Pane -tile.cloth.name=Wool -tile.flower.name=Flower -tile.rose.name=Rose -tile.mushroom.name=Mushroom -tile.blockGold.name=Block of Gold -tile.blockIron.name=Block of Iron -tile.stoneSlab.stone.name=Stone Slab -tile.stoneSlab.sand.name=Sandstone Slab -tile.stoneSlab.wood.name=Wooden Slab -tile.stoneSlab.cobble.name=Cobblestone Slab -tile.stoneSlab.brick.name=Bricks Slab -tile.stoneSlab.smoothStoneBrick.name=Stone Bricks Slab -tile.brick.name=Bricks -tile.tnt.name=TNT -tile.bookshelf.name=Bookshelf -tile.stoneMoss.name=Moss Stone -tile.obsidian.name=Obsidian -tile.torch.name=Torch -tile.fire.name=Fire -tile.mobSpawner.name=Monster Spawner -tile.stairsWood.name=Wooden Stairs -tile.chest.name=Chest -tile.redstoneDust.name=Redstone Dust -tile.oreDiamond.name=Diamond Ore -tile.blockDiamond.name=Block of Diamond -tile.workbench.name=Crafting Table -tile.crops.name=Crops -tile.farmland.name=Farmland -tile.furnace.name=Furnace -tile.sign.name=Sign -tile.doorWood.name=Wooden Door -tile.ladder.name=Ladder -tile.rail.name=Rail -tile.goldenRail.name=Powered Rail -tile.detectorRail.name=Detector Rail -tile.stairsStone.name=Stone Stairs -tile.lever.name=Lever -tile.pressurePlate.name=Pressure Plate -tile.doorIron.name=Iron Door -tile.oreRedstone.name=Redstone Ore -tile.notGate.name=Redstone Torch -tile.button.name=Button -tile.snow.name=Snow -tile.ice.name=Ice -tile.cactus.name=Cactus -tile.clay.name=Clay -tile.reeds.name=Sugar cane -tile.jukebox.name=Jukebox -tile.fence.name=Fence -tile.fenceGate.name=Fence Gate -tile.pumpkin.name=Pumpkin -tile.litpumpkin.name=Jack 'o' Lantern -tile.hellrock.name=Netherrack -tile.hellsand.name=Soul Sand -tile.lightgem.name=Glowstone -tile.portal.name=Portal -tile.cloth.black.name=Black Wool -tile.cloth.red.name=Red Wool -tile.cloth.green.name=Green Wool -tile.cloth.brown.name=Brown Wool -tile.cloth.blue.name=Blue Wool -tile.cloth.purple.name=Purple Wool -tile.cloth.cyan.name=Cyan Wool -tile.cloth.silver.name=Light Gray Wool -tile.cloth.gray.name=Gray Wool -tile.cloth.pink.name=Pink Wool -tile.cloth.lime.name=Lime Wool -tile.cloth.yellow.name=Yellow Wool -tile.cloth.lightBlue.name=Blue Light Wool -tile.cloth.magenta.name=Magenta Wool -tile.cloth.orange.name=Orange Wool -tile.cloth.white.name=Wool -tile.oreLapis.name=Lapis Lazuli Ore -tile.blockLapis.name=Lapis Lazuli Block -tile.dispenser.name=Dispenser -tile.musicBlock.name=Note Block -tile.cake.name=Cake -tile.bed.name=Bed -tile.bed.occupied=This bed is occupied -tile.bed.noSleep=You can only sleep at night -tile.bed.notSafe=You may not rest now, there are monsters nearby -tile.bed.notValid=Your home bed was missing or obstructed -tile.lockedchest.name=Locked chest -tile.trapdoor.name=Trapdoor -tile.web.name=Cobweb -tile.stonebricksmooth.name=Stone Bricks -tile.pistonBase.name=Piston -tile.pistonStickyBase.name=Sticky Piston -tile.fenceIron.name=Iron Bars -tile.melon.name=Melon -tile.stairsBrick.name=Brick Stairs -tile.stairsStoneBrickSmooth.name=Stone Brick Stairs -tile.vine.name=Vines -tile.netherBrick.name=Nether Brick -tile.netherFence.name=Nether Brick Fence -tile.stairsNetherBrick.name=Nether Brick Stairs -tile.netherStalk.name=Nether Wart -tile.cauldron.name=Cauldron -tile.enchantmentTable.name=Enchantment Table -tile.whiteStone.name=End Stone -tile.mycel.name=Mycelium -tile.waterlily.name=Lily Pad -tile.dragonEgg.name=Dragon Egg -tile.redstoneLight.name=Redstone Lamp -tile.glowingobsidian.name=Glowing Obsidian -tile.netherreactor.name=Nether Reactor Core - -item.shovelIron.name=Iron Shovel -item.pickaxeIron.name=Iron Pickaxe -item.hatchetIron.name=Iron Axe -item.flintAndSteel.name=Flint and Steel -item.apple.name=Apple -item.cookie.name=Cookie -item.bow.name=Bow -item.arrow.name=Arrow -item.coal.name=Coal -item.charcoal.name=Charcoal -item.emerald.name=Diamond -item.ingotIron.name=Iron Ingot -item.ingotGold.name=Gold Ingot -item.swordIron.name=Iron Sword -item.swordWood.name=Wooden Sword -item.shovelWood.name=Wooden Shovel -item.pickaxeWood.name=Wooden Pickaxe -item.hatchetWood.name=Wooden Axe -item.swordStone.name=Stone Sword -item.shovelStone.name=Stone Shovel -item.pickaxeStone.name=Stone Pickaxe -item.hatchetStone.name=Stone Axe -item.swordDiamond.name=Diamond Sword -item.shovelDiamond.name=Diamond Shovel -item.pickaxeDiamond.name=Diamond Pickaxe -item.hatchetDiamond.name=Diamond Axe -item.stick.name=Stick -item.bowl.name=Bowl -item.mushroomStew.name=Mushroom Stew -item.swordGold.name=Golden Sword -item.shovelGold.name=Golden Shovel -item.pickaxeGold.name=Golden Pickaxe -item.hatchetGold.name=Golden Axe -item.string.name=String -item.feather.name=Feather -item.sulphur.name=Gunpowder -item.hoeWood.name=Wooden Hoe -item.hoeStone.name=Stone Hoe -item.hoeIron.name=Iron Hoe -item.hoeDiamond.name=Diamond Hoe -item.hoeGold.name=Golden Hoe -item.seeds.name=Seeds -item.seeds_pumpkin.name=Pumpkin Seeds -item.seeds_melon.name=Melon Seeds -item.melon.name=Melon -item.wheat.name=Wheat -item.bread.name=Bread -item.helmetCloth.name=Leather Cap -item.chestplateCloth.name=Leather Tunic -item.leggingsCloth.name=Leather Pants -item.bootsCloth.name=Leather Boots -item.helmetChain.name=Chain Helmet -item.chestplateChain.name=Chain Chestplate -item.leggingsChain.name=Chain Leggings -item.bootsChain.name=Chain Boots -item.helmetIron.name=Iron Helmet -item.chestplateIron.name=Iron Chestplate -item.leggingsIron.name=Iron Leggings -item.bootsIron.name=Iron Boots -item.helmetDiamond.name=Diamond Helmet -item.chestplateDiamond.name=Diamond Chestplate -item.leggingsDiamond.name=Diamond Leggings -item.bootsDiamond.name=Diamond Boots -item.helmetGold.name=Golden Helmet -item.chestplateGold.name=Golden Chestplate -item.leggingsGold.name=Golden Leggings -item.bootsGold.name=Golden Boots -item.flint.name=Flint -item.porkchopRaw.name=Raw Porkchop -item.porkchopCooked.name=Cooked Porkchop -item.chickenRaw.name=Raw Chicken -item.chickenCooked.name=Cooked Chicken -item.beefRaw.name=Raw Beef -item.beefCooked.name=Steak -item.painting.name=Painting -item.appleGold.name=Golden Apple -item.sign.name=Sign -item.doorWood.name=Wooden Door -item.bucket.name=Bucket -item.bucketWater.name=Water Bucket -item.bucketLava.name=Lava Bucket -item.minecart.name=Minecart -item.saddle.name=Saddle -item.doorIron.name=Iron Door -item.redstone.name=Redstone -item.snowball.name=Snowball -item.boat.name=Boat -item.leather.name=Leather -item.milk.name=Milk -item.brick.name=Brick -item.clay.name=Clay -item.reeds.name=Sugar Canes -item.paper.name=Paper -item.book.name=Book -item.slimeball.name=Slimeball -item.minecartChest.name=Minecart with Chest -item.minecartFurnace.name=Minecart with Furnace -item.egg.name=Egg -item.compass.name=Compass -item.fishingRod.name=Fishing Rod -item.clock.name=Clock -item.yellowDust.name=Glowstone Dust -item.fishRaw.name=Raw Fish -item.fishCooked.name=Cooked Fish -item.record.name=Music Disc -item.bone.name=Bone -item.dyePowder.black.name=Ink Sac -item.dyePowder.red.name=Rose Red -item.dyePowder.green.name=Cactus Green -item.dyePowder.brown.name=Cocoa Beans -item.dyePowder.blue.name=Lapis Lazuli -item.dyePowder.purple.name=Purple Dye -item.dyePowder.cyan.name=Cyan Dye -item.dyePowder.silver.name=Light Gray Dye -item.dyePowder.gray.name=Gray Dye -item.dyePowder.pink.name=Pink Dye -item.dyePowder.lime.name=Lime Dye -item.dyePowder.yellow.name=Dandelion Yellow -item.dyePowder.lightBlue.name=Light Blue Dye -item.dyePowder.magenta.name=Magenta Dye -item.dyePowder.orange.name=Orange Dye -item.dyePowder.white.name=Bone Meal -item.sugar.name=Sugar -item.cake.name=Cake -item.bed.name=Bed -item.diode.name=Redstone Repeater -item.map.name=Map -item.leaves.name=Leaves -item.shears.name=Shears -item.rottenFlesh.name=Rotten Flesh -item.enderPearl.name=Ender Pearl -item.blazeRod.name=Blaze Rod -item.ghastTear.name=Ghast Tear -item.netherStalkSeeds.name=Nether Wart -item.potion.name=Potion -item.emptyPotion.name=Water Bottle -item.goldNugget.name=Gold Nugget -item.glassBottle.name=Glass Bottle -item.spiderEye.name=Spider Eye -item.fermentedSpiderEye.name=Fermented Spider Eye -item.blazePowder.name=Blaze Powder -item.magmaCream.name=Magma Cream -item.cauldron.name=Cauldron -item.brewingStand.name=Brewing Stand -item.eyeOfEnder.name=Eye of Ender -item.speckledMelon.name=Glistering Melon -item.monsterPlacer.name=Spawn -item.expBottle.name=Bottle o' Enchanting -item.fireball.name=Fire Charge - -container.inventory=Inventory -container.crafting=Crafting -container.dispenser=Dispenser -container.furnace=Furnace -container.enchant=Enchant -container.creative=Item Selection -container.brewing=Brewing Stand -container.chest=Chest -container.minecart=Minecart - -entity.Item.name=Item -entity.XPOrb.name=Experience Orb - -entity.Arrow.name=Arrow -entity.Snowball.name=Snowball -entity.Painting.name=Painting - -entity.Mob.name=Mob -entity.Monster.name=Monster - -entity.Creeper.name=Creeper -entity.Skeleton.name=Skeleton -entity.Spider.name=Spider -entity.Giant.name=Giant -entity.Zombie.name=Zombie -entity.Slime.name=Slime -entity.Ghast.name=Ghast -entity.PigZombie.name=Zombie Pigman -entity.Enderman.name=Enderman -entity.Silverfish.name=Silverfish -entity.CaveSpider.name=Cave Spider -entity.Blaze.name=Blaze -entity.LavaSlime.name=Magma Cube -entity.MushroomCow.name=Mooshroom -entity.Villager.name=Villager -entity.VillagerGolem.name=Iron Golem -entity.SnowMan.name=Snow Golem -entity.EnderDragon.name=Ender Dragon - -entity.Pig.name=Pig -entity.Sheep.name=Sheep -entity.Cow.name=Cow -entity.Chicken.name=Chicken -entity.Squid.name=Squid -entity.Wolf.name=Wolf -entity.Ozelot.name=Ocelot -entity.Cat.name=Cat - -entity.PrimedTnt.name=Block of TNT -entity.FallingSand.name=Falling Block - -entity.Minecart.name=Minecart -entity.Boat.name=Boat - -entity.Arrow.name=arrow -entity.generic.name=unknown - -death.inFire=%1$s went up in flames -death.onFire=%1$s burned to death -death.lava=%1$s tried to swim in lava -death.inWall=%1$s suffocated in a wall -death.drown=%1$s drowned -death.starve=%1$s starved to death -death.cactus=%1$s was pricked to death -death.fall=%1$s hit the ground too hard -death.outOfWorld=%1$s fell out of the world -death.generic=%1$s died -death.explosion=%1$s blew up -death.magic=%1$s was killed by magic -death.mob=%1$s was slain by %2$s -death.player=%1$s was slain by %2$s -death.arrow=%1$s was shot by %2$s -death.fireball=%1$s was fireballed by %2$s -death.thrown=%1$s was pummeled by %2$s -death.indirectMagic=%1$s was killed by %2$s - -deathScreen.respawn=Respawn -deathScreen.deleteWorld=Delete world -deathScreen.titleScreen=Title screen -deathScreen.score=Score -deathScreen.title.hardcore=Game over! -deathScreen.hardcoreInfo=You cannot respawn in hardcore mode! -deathScreen.title=You died! - -potion.empty=No Effects -potion.moveSpeed=Speed -potion.moveSlowdown=Slowness -potion.digSpeed=Haste -potion.digSlowDown=Mining Fatigue -potion.damageBoost=Strength -potion.weakness=Weakness -potion.heal=Instant Health -potion.harm=Instant Damage -potion.jump=Jump Boost -potion.confusion=Nausea -potion.regeneration=Regeneration -potion.resistance=Resistance -potion.fireResistance=Fire Resistance -potion.waterBreathing=Water Breathing -potion.invisibility=Invisibility -potion.blindness=Blindness -potion.nightVision=Night Vision -potion.hunger=Hunger -potion.poison=Poison - -potion.moveSpeed.postfix=Potion of Swiftness -potion.moveSlowdown.postfix=Potion of Slowness -potion.digSpeed.postfix=Potion of Haste -potion.digSlowDown.postfix=Potion of Dullness -potion.damageBoost.postfix=Potion of Strength -potion.weakness.postfix=Potion of Weakness -potion.heal.postfix=Potion of Healing -potion.harm.postfix=Potion of Harming -potion.jump.postfix=Potion of Leaping -potion.confusion.postfix=Potion of Nausea -potion.regeneration.postfix=Potion of Regeneration -potion.resistance.postfix=Potion of Resistance -potion.fireResistance.postfix=Potion of Fire Resistance -potion.waterBreathing.postfix=Potion of Water Breathing -potion.invisibility.postfix=Potion of Invisibility -potion.blindness.postfix=Potion of Blindness -potion.nightVision.postfix=Potion of Night Vision -potion.hunger.postfix=Potion of Hunger -potion.poison.postfix=Potion of Poison - -potion.potency.0= -potion.potency.1=II -potion.potency.2=III -potion.potency.3=IV - -potion.prefix.grenade=Splash -potion.prefix.mundane=Mundane -potion.prefix.uninteresting=Uninteresting -potion.prefix.bland=Bland -potion.prefix.clear=Clear -potion.prefix.milky=Milky -potion.prefix.diffuse=Diffuse -potion.prefix.artless=Artless -potion.prefix.thin=Thin -potion.prefix.awkward=Awkward -potion.prefix.flat=Flat -potion.prefix.bulky=Bulky -potion.prefix.bungling=Bungling -potion.prefix.buttered=Buttered -potion.prefix.smooth=Smooth -potion.prefix.suave=Suave -potion.prefix.debonair=Debonair -potion.prefix.thick=Thick -potion.prefix.elegant=Elegant -potion.prefix.fancy=Fancy -potion.prefix.charming=Charming -potion.prefix.dashing=Dashing -potion.prefix.refined=Refined -potion.prefix.cordial=Cordial -potion.prefix.sparkling=Sparkling -potion.prefix.potent=Potent -potion.prefix.foul=Foul -potion.prefix.odorless=Odorless -potion.prefix.rank=Rank -potion.prefix.harsh=Harsh -potion.prefix.acrid=Acrid -potion.prefix.gross=Gross -potion.prefix.stinky=Stinky - -enchantment.damage.all=Sharpness -enchantment.damage.undead=Smite -enchantment.damage.arthropods=Bane of Arthropods -enchantment.knockback=Knockback -enchantment.fire=Fire Aspect -enchantment.protect.all=Protection -enchantment.protect.fire=Fire Protection -enchantment.protect.fall=Feather Falling -enchantment.protect.explosion=Blast Protection -enchantment.protect.projectile=Projectile Protection -enchantment.oxygen=Respiration -enchantment.waterWorker=Aqua Affinity -enchantment.digging=Efficiency -enchantment.untouching=Silk Touch -enchantment.durability=Unbreaking -enchantment.lootBonus=Looting -enchantment.lootBonusDigger=Fortune -enchantment.arrowDamage=Power -enchantment.arrowFire=Flame -enchantment.arrowKnockback=Punch -enchantment.arrowInfinite=Infinity - -enchantment.level.1=I -enchantment.level.2=II -enchantment.level.3=III -enchantment.level.4=IV -enchantment.level.5=V -enchantment.level.6=VI -enchantment.level.7=VII -enchantment.level.8=VIII -enchantment.level.9=IX -enchantment.level.10=X - -gui.achievements=Achievements -gui.stats=Statistics - -stat.generalButton=General -stat.blocksButton=Blocks -stat.itemsButton=Items - -stat.used=Times Used -stat.mined=Times Mined -stat.depleted=Times Depleted -stat.crafted=Times Crafted - -stat.startGame=Times played -stat.createWorld=Worlds played -stat.loadWorld=Saves loaded -stat.joinMultiplayer=Multiplayer joins -stat.leaveGame=Games quit - -stat.playOneMinute=Minutes Played - -stat.walkOneCm=Distance Walked -stat.fallOneCm=Distance Fallen -stat.swimOneCm=Distance Swum -stat.flyOneCm=Distance Flown -stat.climbOneCm=Distance Climbed -stat.diveOneCm=Distance Dove -stat.minecartOneCm=Distance by Minecart -stat.boatOneCm=Distance by Boat -stat.pigOneCm=Distance by Pig -stat.jump=Jumps -stat.drop=Items Dropped - -stat.damageDealt=Damage Dealt -stat.damageTaken=Damage Taken -stat.deaths=Number of Deaths -stat.mobKills=Mob Kills -stat.playerKills=Player Kills -stat.fishCaught=Fish Caught - -stat.mineBlock=%1$s Mined -stat.craftItem=%1$s Crafted -stat.useItem=%1$s Used -stat.breakItem=%1$s Depleted - -achievement.get=Achievement get! - -achievement.taken=Taken! - -achievement.requires=Requires '%1$s' -achievement.openInventory=Taking Inventory -achievement.openInventory.desc=Press '%1$s' to open your inventory. -achievement.mineWood=Getting Wood -achievement.mineWood.desc=Attack a tree until a block of wood pops out -achievement.buildWorkBench=Benchmarking -achievement.buildWorkBench.desc=Craft a workbench with four blocks of planks -achievement.buildPickaxe=Time to Mine! -achievement.buildPickaxe.desc=Use planks and sticks to make a pickaxe -achievement.buildFurnace=Hot Topic -achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks -achievement.acquireIron=Acquire Hardware -achievement.acquireIron.desc=Smelt an iron ingot -achievement.buildHoe=Time to Farm! -achievement.buildHoe.desc=Use planks and sticks to make a hoe -achievement.makeBread=Bake Bread -achievement.makeBread.desc=Turn wheat into bread -achievement.bakeCake=The Lie -achievement.bakeCake.desc=Wheat, sugar, milk and eggs! -achievement.buildBetterPickaxe=Getting an Upgrade -achievement.buildBetterPickaxe.desc=Construct a better pickaxe -achievement.cookFish=Delicious Fish -achievement.cookFish.desc=Catch and cook fish! -achievement.onARail=On A Rail -achievement.onARail.desc=Travel by minecart at least 1 km from where you started -achievement.buildSword=Time to Strike! -achievement.buildSword.desc=Use planks and sticks to make a sword -achievement.killEnemy=Monster Hunter -achievement.killEnemy.desc=Attack and destroy a monster -achievement.killCow=Cow Tipper -achievement.killCow.desc=Harvest some leather -achievement.flyPig=When Pigs Fly -achievement.flyPig.desc=Fly a pig off a cliff -achievement.snipeSkeleton=Sniper Duel -achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters -achievement.diamonds=DIAMONDS! -achievement.diamonds.desc=Acquire diamonds with your iron tools -achievement.portal=We Need to Go Deeper -achievement.portal.desc=Build a portal to the Nether -achievement.ghast=Return to Sender -achievement.ghast.desc=Destroy a Ghast with a fireball -achievement.blazeRod=Into Fire -achievement.blazeRod.desc=Relieve a Blaze of its rod -achievement.potion=Local Brewery -achievement.potion.desc=Brew a potion -achievement.theEnd=The End? -achievement.theEnd.desc=Locate the End -achievement.theEnd2=The End. -achievement.theEnd2.desc=Defeat the Ender Dragon -achievement.enchantments=Enchanter -achievement.enchantments.desc=Use a book, obsidian and diamonds to construct an enchantment table -achievement.overkill=Overkill -achievement.overkill.desc=Deal eight hearts of damage in a single hit -achievement.bookcase=Librarian -achievement.bookcase.desc=Build some bookshelves to improve your enchantment table - -desc.sword=Deals more damage than by hand. -desc.shovel=Used to dig dirt, grass, sand, gravel and snow faster than by hand. -desc.pickaxe=Required to mine stone-related blocks and ore. -desc.hatchet=Used to chop wood-related blocks faster than by hand. -desc.hoe=Used to till dirt and grass blocks to prepare for crops. -desc.doorwood=Wooden doors are activated by using, hitting them or with Redstone. -desc.dooriron=Iron doors can only be opened by Redstone, buttons or switches. -desc.helmet=Gives the user 1.5 Armor when worn. -desc.chestplate=Gives the user 4 Armor when worn. -desc.leggings=Gives the user 3 Armor when worn. -desc.boots=Gives the user 1.5 Armor when worn. -desc.ingot=A shiny ingot which can be used to craft tools made from this material. -desc.block=Allows ingots, gems, or dyes to be crafted into placeable blocks. Can be used as an expensive building block or compact storage of the ore. -desc.pressureplate=Used to send an electrical charge when stepped on by a player, an animal, or a monster. Wooden Pressure Plates can also be activated by dropping something on them. -desc.stairs=Used for compact staircases. -desc.slab=Used for making long staircases. -desc.halfslab=Used for compact staircases. -desc.torch=Used to create light. Torches also melt snow and ice. -desc.wood=Used as a building material and can be crafted into many things. Can be crafted from any wood. -desc.sandstone=Used as a building material. -desc.sandstone.addon=Is not influenced by gravity like normal Sand. -desc.structblock=Used as a building material. -desc.stick=Used to craft torches, arrows, signs, ladders, fences and as handles for tools and weapons. -desc.bed=Used to skip to dawn, if all players are sleeping. Changes your spawn point to the bed's position. -The colors of the bed are always the same, regardless of the colors of wool used. -desc.craftingtable=Allows you to craft a more varied selection of items than the normal crafting. -desc.furnace=Allows you to smelt ore, create charcoal and glass, and cook fish and porkchops. -desc.chest=Stores blocks and items inside. -desc.fence=Used as a barrier that cannot be jumped over. -desc.ladder=Used to climb vertically. -desc.trapdoor=Activated by using, hitting them or with redstone. They function as normal doors, but are a one by one block and lay flat on the ground. -desc.sign=Shows text entered by you or other players. -desc.glowstone=Used to create brighter light than torches. Melts snow/ice and can be used underwater. -desc.tnt=Used to cause explosions. Activated after placing by hitting, or with an electrical charge. -desc.bowl=Used to hold mushroom stew. You keep the bowl when the stew has been eaten. -desc.bucket=Used to hold and transport water, lava and milk. -desc.flintandsteel=Used to detonate TNT. -desc.fishingrod=Used to catch fish. -desc.clock=Displays positions of the Sun and Moon. -desc.compass=Points to your start point. -desc.map=Will create an image of an area explored while held. This can be used for path-finding. -desc.bow=Allows for ranged attacks by using arrows. -desc.arrow=Used as ammunition for bows. -desc.mushroomstew=Restores 5 hearts. -desc.cookie=Restores 0.5 Hearts. Can be stacked up to 8 times. -desc.cake=Restores 1.5 Hearts. Can be used 6 times. -desc.sugar=Used in the cake recipe. -desc.bread=Restores 2.5 Hearts. -desc.goldenapple=Restores 10 Hearts. -desc.lever=Used to send an electrical charge by being turned on or off. Stays in the on or off state until pressed again. -desc.redstonetorch=Constantly sends an electrical charge, or can be used as a receiver/transmitter when connected to the side of a block. -Can also be used for low-level lighting. -desc.redstonerepeater=Used in Redstone circuits as repeater, a delayer, and/or a diode -desc.button=Used to send an electrical charge by being pressed. Stays activated for approximately a second before shutting off again. -desc.dispenser=Used to hold and shoot out items in a random order when given a Redstone charge. -desc.noteblock=Plays a note when triggered. Hit it to change the pitch of the note. Placing this on top of different blocks will change the type of instrument. -desc.rail=Used to guide minecarts. -desc.poweredrail=When powered, accelerates minecarts that pass over it. When unpowered, causes minecarts to stop on it. -desc.detectorrail=Functions like a Pressure Plate (sends a Redstone signal when powered) but can only be activated by a minecart. -desc.minecart=Used to transport you, an animal, or a monster along rails. -desc.minecartwithchest=Used to transport goods along rails. -desc.minecartwithfurnace=Will move along rails and can push other minecarts when coal is put in it. -desc.boat=Used to travel in water more quickly than swimming. -desc.wool=Collected from sheep, and can be colored with dyes. -desc.woolstring=Used as a building material and can be colored with dyes. Can be easily obtained from Sheep. -desc.dyepowder.black=Used as a dye to create black wool. -desc.dyepowder.green=Used as a dye to create green wool. -desc.dyepowder.brown=Used as a dye to create brown wool. -desc.dyepowder.silver=Used as a dye to create silver wool. -desc.dyepowder.yellow=Used as a dye to create yellow wool. -desc.dyepowder.red=Used as a dye to create red wool. -desc.dyepowder.white=Used to instantly grow crops, trees, tall grass, huge mushrooms and flowers, and can be used in dye recipes. -desc.dyepowder.pink=Used as a dye to create pink wool. -desc.dyepowder.orange=Used as a dye to create orange wool. -desc.dyepowder.lime=Used as a dye to create lime wool. -desc.dyepowder.gray=Used as a dye to create gray wool. -desc.dyepowder.lightgray=Used as a dye to create light gray wool. -(Note: light gray dye can also be made by combining gray dye with bone meal, letting you make four light gray dyes from every ink sac instead of three.) -desc.dyepowder.lightblue=Used as a dye to create light blue wool. -desc.dyepowder.cyan=Used as a dye to create cyan wool. -desc.dyepowder.purple=Used as a dye to create purple wool. -desc.dyepowder.magenta=Used as a dye to create magenta wool. -desc.dyepowder.blue=Used as dye to create Blue Wool. -desc.jukebox=Plays Music Discs. -desc.diamonds=Use these to create very strong tools, weapons or armor -desc.emerald=Use these to create very strong tools, weapons or armor -desc.jackolantern=Used to create brighter light than torches. Melts snow/ice and can be used underwater. -desc.paper=Used to create books and maps. -desc.book=Used to create a bookshelf. -desc.bookshelf=Used as decoration. -desc.picture=Used as decoration. -desc.ore_gold=Can be mined with an iron pickaxe or better, then smelted in a furnace to produce gold ingots. -desc.ore_iron=Can be mined with a stone pickaxe or better, then smelted in a furnace to produce iron ingots. -desc.ore_coal=Can be mined with a pickaxe to collect coal. -desc.ore_lapis=Can be mined with a stone pickaxe or better to collect lapis lazuli. -desc.ore_diamond=Can be mined with an iron pickaxe or better to collect diamonds. -desc.ore_redstone=Can be mined with an iron pickaxe or better to collect redstone dust. -desc.stone=Can be mined with a pickaxe to collect cobblestone. -desc.dirt=Collected using a shovel. Can be used for construction. -desc.sapling=Can be planted and it will eventually grow into a tree. -desc.bedrock=This cannot be broken. -desc.lava=Sets fire to anything that touches it. Can be collected in a bucket. -desc.sand=Collected using a shovel. Can be smelted into glass using the furnace. Is affected by gravity if there is no other tile underneath it. -desc.gravel=Collected using a shovel. Sometimes produces flint when dug up. Is affected by gravity if there is no other tile underneath it. -desc.log=Chopped using an axe, and can be crafted into planks or used as a fuel. -desc.glass=Created in a furnace by smelting sand. Will break if you try to mine it. -desc.thinglass=Will break if you try to mine it. -desc.stone_brick=Mined from stone using a pickaxe. Can be used to construct a furnace or stone tools. -desc.brick=Baked from clay in a furnace. -desc.clay=Can be baked into bricks in a furnace. -desc.snow=A compact way to store snowballs. -desc.top_snow=Can be dug with a shovel to create snowballs. -desc.tall_grass=Sometimes produces seeds when broken. -desc.flower=Can be crafted into a dye. -desc.mushroom=Can be crafted with a bowl to make stew. -desc.obsidian=Can only be mined with a diamond pickaxe. Is produced by the meeting of water and still lava, and is used to build a portal. -desc.mob_spawner=Spawns monsters into the world. -desc.redstone_dust=Is placed on the ground to carry an electrical charge. -desc.crops=When fully grown, crops can be harvested to collect wheat. -desc.farmland=Ground that has been prepared ready to plant seeds. -desc.cactus=Can be crafted to create a dye. -desc.reeds=Can be crafted to create sugar. -desc.pumpkin=Can be worn as a helmet or crafted with a torch to create a Jack-O-Lantern. -desc.hell_rock=Burns forever if set alight. -desc.hell_sand=Slows the movement of anything walking over it. -desc.portal=Standing in the portal allows you to pass between the overworld and the nether. -desc.apple=Restores health, and can be crafted into a golden apple. -desc.coal=Used as a fuel in a furnace, or crafted to make a torch. -desc.string=Collected by killing a spider, and can be crafted into a bow. -desc.feather=Collected by killing a chicken, and can be crafted into an arrow. -desc.sulphur=Collected by killing a creeper, and can be crafted into TNT. -desc.shears=Used to obtain wool from sheep and to harvest placeable Leaf blocks. -desc.seeds=Can be planted in farmland to grow crops. -desc.wheat=Harvested from crops, and can be used to craft food items. -desc.flint=Collected by digging gravel, and can be used to craft a flint and steel. -desc.porkchopraw=Collected by killing a pig, and can be cooked in a furnace. Restores health. -desc.porkchopcooked=Created by cooking a porkchop in a furnace. Restores health. -desc.beefraw=Collected by killing a cow, and can be cooked in a furnace. Restores health. -desc.beefcooked=Created by cooking a beef in a furnace. Restores health. -desc.chickenraw=Collected by killing a chicken, and can be cooked in a furnace. Restores health. -desc.chickencooked=Created by cooking a chicken in a furnace. Restores health. -desc.saddle=When used on a pig it allows you to ride the pig. -desc.snowball=Collected by digging snow, and can be thrown. -desc.leather=Collected by killing a cow, and can be crafted into armor. -desc.slimeball=Collected by killing a slime. -desc.egg=Dropped randomly by chickens, and can be crafted into food items. -desc.yellow_dust=Collected by mining Glowstone, and can be crafted to make Glowstone blocks again. -desc.fish_raw=Can be eaten to restore health, or cooked in a furnace. -desc.fish_cooked=Created by cooking a raw fish in a furnace. Can be eaten to restore health. -desc.bone=Collected by killing a skeleton. Can be crafted into bone meal. -desc.record=Collected by getting a skeleton to kill a creeper. Can be played in a jukebox. -desc.water=Extinguishes fire and helps crops grow. Can be collected in a bucket. -desc.leaves=When broken sometimes drops a sapling which can then be replanted to grow into a tree. -desc.moss_stone=Can be used for construction and decoration. -desc.stonebricksmooth=Used as building material. -desc.netherreactor=Core of the Nether Reactor + +language.name=English +language.region=US +language.code=en_US + +gui.done=Done +gui.cancel=Cancel +gui.toMenu=Back to title screen +gui.up=Up +gui.down=Down +gui.yes=Yes +gui.no=No + +menu.singleplayer=Singleplayer +menu.multiplayer=Multiplayer +menu.mods=Texture Packs +menu.options=Options... +menu.quit=Quit Game +menu.returnToMenu=Save and Quit to Title +menu.disconnect=Disconnect +menu.returnToGame=Back to Game +menu.switchingLevel=Switching level +menu.generatingLevel=Generating level +menu.loadingLevel=Loading level +menu.generatingTerrain=Building terrain +menu.simulating=Simulating the world for a bit +menu.respawning=Respawning + +selectWorld.title=Select World +selectWorld.empty=empty +selectWorld.world=World +selectWorld.select=Play Selected World +selectWorld.create=Create New World +selectWorld.createDemo=Play New Demo World +selectWorld.delete=Delete +selectWorld.rename=Rename +selectWorld.deleteQuestion=Are you sure you want to delete this world? +selectWorld.deleteWarning=will be lost forever! (A long time!) +selectWorld.deleteButton=Delete +selectWorld.renameButton=Rename +selectWorld.renameTitle=Rename World +selectWorld.conversion=Must be converted! +selectWorld.newWorld=New World +selectWorld.enterName=World Name +selectWorld.resultFolder=Will be saved in: +selectWorld.enterSeed=Seed for the World Generator +selectWorld.seedInfo=Leave blank for a random seed + +gameMode.survival=Survival Mode +gameMode.creative=Creative Mode +gameMode.hardcore=Hardcore Mode! +gameMode.changed=Your game mode has been changed + +selectWorld.gameMode=Game Mode: +selectWorld.gameMode.survival=Survival +selectWorld.gameMode.survival.line1=Search for resources, crafting, gain +selectWorld.gameMode.survival.line2=levels, health and hunger +selectWorld.gameMode.creative=Creative +selectWorld.gameMode.creative.line1=Unlimited resources, free flying and +selectWorld.gameMode.creative.line2=destroy blocks instantly +selectWorld.gameMode.hardcore=Hardcore +selectWorld.gameMode.hardcore.line1=Same as survival mode, locked at hardest +selectWorld.gameMode.hardcore.line2=difficulty, and one life only +selectWorld.moreWorldOptions=More World Options... +selectWorld.mapFeatures=Generate Structures: +selectWorld.mapFeatures.info=Villages, dungeons etc +selectWorld.mapType=World Type: +selectWorld.mapType.normal=Normal +selectWorld.hardcoreMode=Hardcore: +selectWorld.hardcoreMode.info=World is deleted upon death + +generator.default=Default +generator.flat=Superflat + +selectServer.title=Select Server +selectServer.empty=empty +selectServer.select=Join Server +selectServer.direct=Direct Connect +selectServer.edit=Edit +selectServer.delete=Delete +selectServer.add=Add server +selectServer.defaultName=Minecraft Server +selectServer.deleteQuestion=Are you sure you want to remove this server? +selectServer.deleteWarning=will be lost forever! (A long time!) +selectServer.deleteButton=Delete +selectServer.refresh=Refresh +addServer.title=Edit Server Info +addServer.enterName=Server Name +addServer.enterIp=Server Address +addServer.add=Done + +multiplayer.title=Play Multiplayer +multiplayer.connect=Connect +multiplayer.info1=Minecraft Multiplayer is currently not finished, but there +multiplayer.info2=is some buggy early testing going on. +multiplayer.ipinfo=Enter the IP of a server to connect to it: + +multiplayer.downloadingTerrain=Downloading terrain + +multiplayer.stopSleeping=Leave Bed + +demo.day.1=This demo will last five game days, do your best! +demo.day.2=Day Two +demo.day.3=Day Three +demo.day.4=Day Four +demo.day.5=This is your last day! +demo.day.warning=Your time is almost up! +demo.day.6=You have passed your fifth day, use F2 to save a screenshot of your creation +demo.reminder=The demo time has expired, buy the game to continue or start a new world! +demo.help.movement=Use %1$s, %2$s, %3$s, %4$s and the mouse to move around +demo.help.jump=Jump by pressing %1$s +demo.help.inventory=Use %1$s to open your inventory + +connect.connecting=Connecting to the server... +connect.authorizing=Logging in... +connect.failed=Failed to connect to the server + +disconnect.genericReason=%s +disconnect.disconnected=Disconnected by Server +disconnect.lost=Connection Lost +disconnect.kicked=Was kicked from the game +disconnect.timeout=Timed out +disconnect.closed=Connection closed +disconnect.loginFailed=Failed to login +disconnect.loginFailedInfo=Failed to login: %s +disconnect.quitting=Quitting +disconnect.endOfStream=End of stream +disconnect.overflow=Buffer overflow + +options.off=OFF +options.on=ON +options.title=Options +options.controls=Controls... +options.video=Video Settings... +options.language=Language... +options.languageWarning=Language translations may not be 100% accurate +options.videoTitle=Video Settings +options.music=Music +options.sound=Sound +options.invertMouse=Invert Mouse +options.fov=FOV +options.fov.min=Normal +options.fov.max=Quake Pro +options.gamma=Brightness +options.gamma.min=Moody +options.gamma.max=Bright +options.sensitivity=Sensitivity +options.sensitivity.min=*yawn* +options.sensitivity.max=HYPERSPEED!!! +options.renderDistance=Render Distance +options.renderDistance.tiny=Tiny +options.renderDistance.short=Short +options.renderDistance.normal=Normal +options.renderDistance.far=Far +options.viewBobbing=View Bobbing +options.ao=Smooth Lighting +options.anaglyph=3D Anaglyph +options.framerateLimit=Performance +options.difficulty=Difficulty +options.difficulty.peaceful=Peaceful +options.difficulty.easy=Easy +options.difficulty.normal=Normal +options.difficulty.hard=Hard +options.difficulty.hardcore=Hardcore +options.graphics=Graphics +options.graphics.fancy=Fancy +options.graphics.fast=Fast +options.guiScale=GUI Scale +options.guiScale.auto=Auto +options.guiScale.small=Small +options.guiScale.medium=Medium +options.guiScale.large=Large +options.guiScale.larger=Larger +options.guiScale.largest=Largest +options.advancedOpengl=Advanced OpenGL +options.renderClouds=Clouds +options.farWarning1=A 64 bit Java installation is recommended +options.farWarning2=for 'Far' render distance (you have 32 bit) +options.particles=Particles +options.particles.all=All +options.particles.decreased=Decreased +options.particles.minimal=Minimal + +performance.max=Max FPS +performance.balanced=Balanced +performance.powersaver=Power saver + +controls.title=Controls + +key.forward=Forward +key.left=Left +key.back=Back +key.right=Right +key.jump=Jump +key.inventory=Inventory +key.drop=Drop +key.chat=Chat +key.fog=Toggle Fog +key.sneak=Sneak +key.playerlist=List Players +key.attack=Attack +key.use=Use Item +key.pickItem=Pick Block +key.mouseButton=Button %1$s + +texturePack.openFolder=Open texture pack folder +texturePack.title=Select Texture Pack +texturePack.folderInfo=(Place texture pack files here) + +tile.stone.name=Stone +tile.grass.name=Grass Block +tile.dirt.name=Dirt +tile.stonebrick.name=Cobblestone +tile.wood.name=Wooden Planks +tile.sapling.name=Sapling +tile.deadbush.name=Dead Bush +tile.bedrock.name=Bedrock +tile.water.name=Water +tile.lava.name=Lava +tile.sand.name=Sand +tile.sandStone.name=Sandstone +tile.gravel.name=Gravel +tile.oreGold.name=Gold Ore +tile.oreIron.name=Iron Ore +tile.oreCoal.name=Coal Ore +tile.log.name=Wood +tile.leaves.name=Leaves +tile.tallgrass.name=Grass +tile.tallgrass.shrub.name=Shrub +tile.tallgrass.grass.name=Grass +tile.tallgrass.fern.name=Fern +tile.sponge.name=Sponge +tile.glass.name=Glass +tile.thinGlass.name=Glass Pane +tile.cloth.name=Wool +tile.flower.name=Flower +tile.rose.name=Rose +tile.mushroom.name=Mushroom +tile.blockGold.name=Block of Gold +tile.blockIron.name=Block of Iron +tile.stoneSlab.stone.name=Stone Slab +tile.stoneSlab.sand.name=Sandstone Slab +tile.stoneSlab.wood.name=Wooden Slab +tile.stoneSlab.cobble.name=Cobblestone Slab +tile.stoneSlab.brick.name=Bricks Slab +tile.stoneSlab.smoothStoneBrick.name=Stone Bricks Slab +tile.brick.name=Bricks +tile.tnt.name=TNT +tile.bookshelf.name=Bookshelf +tile.stoneMoss.name=Moss Stone +tile.obsidian.name=Obsidian +tile.torch.name=Torch +tile.fire.name=Fire +tile.mobSpawner.name=Monster Spawner +tile.stairsWood.name=Wooden Stairs +tile.chest.name=Chest +tile.redstoneDust.name=Redstone Dust +tile.oreDiamond.name=Diamond Ore +tile.blockDiamond.name=Block of Diamond +tile.workbench.name=Crafting Table +tile.crops.name=Crops +tile.farmland.name=Farmland +tile.furnace.name=Furnace +tile.sign.name=Sign +tile.doorWood.name=Wooden Door +tile.ladder.name=Ladder +tile.rail.name=Rail +tile.goldenRail.name=Powered Rail +tile.detectorRail.name=Detector Rail +tile.stairsStone.name=Stone Stairs +tile.lever.name=Lever +tile.pressurePlate.name=Pressure Plate +tile.doorIron.name=Iron Door +tile.oreRedstone.name=Redstone Ore +tile.notGate.name=Redstone Torch +tile.button.name=Button +tile.snow.name=Snow +tile.ice.name=Ice +tile.cactus.name=Cactus +tile.clay.name=Clay +tile.reeds.name=Sugar cane +tile.jukebox.name=Jukebox +tile.fence.name=Fence +tile.fenceGate.name=Fence Gate +tile.pumpkin.name=Pumpkin +tile.litpumpkin.name=Jack 'o' Lantern +tile.hellrock.name=Netherrack +tile.hellsand.name=Soul Sand +tile.lightgem.name=Glowstone +tile.portal.name=Portal +tile.cloth.black.name=Black Wool +tile.cloth.red.name=Red Wool +tile.cloth.green.name=Green Wool +tile.cloth.brown.name=Brown Wool +tile.cloth.blue.name=Blue Wool +tile.cloth.purple.name=Purple Wool +tile.cloth.cyan.name=Cyan Wool +tile.cloth.silver.name=Light Gray Wool +tile.cloth.gray.name=Gray Wool +tile.cloth.pink.name=Pink Wool +tile.cloth.lime.name=Lime Wool +tile.cloth.yellow.name=Yellow Wool +tile.cloth.lightBlue.name=Blue Light Wool +tile.cloth.magenta.name=Magenta Wool +tile.cloth.orange.name=Orange Wool +tile.cloth.white.name=Wool +tile.oreLapis.name=Lapis Lazuli Ore +tile.blockLapis.name=Lapis Lazuli Block +tile.dispenser.name=Dispenser +tile.musicBlock.name=Note Block +tile.cake.name=Cake +tile.bed.name=Bed +tile.bed.occupied=This bed is occupied +tile.bed.noSleep=You can only sleep at night +tile.bed.notSafe=You may not rest now, there are monsters nearby +tile.bed.notValid=Your home bed was missing or obstructed +tile.lockedchest.name=Locked chest +tile.trapdoor.name=Trapdoor +tile.web.name=Cobweb +tile.stonebricksmooth.name=Stone Bricks +tile.pistonBase.name=Piston +tile.pistonStickyBase.name=Sticky Piston +tile.fenceIron.name=Iron Bars +tile.melon.name=Melon +tile.stairsBrick.name=Brick Stairs +tile.stairsStoneBrickSmooth.name=Stone Brick Stairs +tile.vine.name=Vines +tile.netherBrick.name=Nether Brick +tile.netherFence.name=Nether Brick Fence +tile.stairsNetherBrick.name=Nether Brick Stairs +tile.netherStalk.name=Nether Wart +tile.cauldron.name=Cauldron +tile.enchantmentTable.name=Enchantment Table +tile.whiteStone.name=End Stone +tile.mycel.name=Mycelium +tile.waterlily.name=Lily Pad +tile.dragonEgg.name=Dragon Egg +tile.redstoneLight.name=Redstone Lamp +tile.glowingobsidian.name=Glowing Obsidian +tile.netherreactor.name=Nether Reactor Core + +item.shovelIron.name=Iron Shovel +item.pickaxeIron.name=Iron Pickaxe +item.hatchetIron.name=Iron Axe +item.flintAndSteel.name=Flint and Steel +item.apple.name=Apple +item.cookie.name=Cookie +item.bow.name=Bow +item.arrow.name=Arrow +item.coal.name=Coal +item.charcoal.name=Charcoal +item.emerald.name=Diamond +item.ingotIron.name=Iron Ingot +item.ingotGold.name=Gold Ingot +item.swordIron.name=Iron Sword +item.swordWood.name=Wooden Sword +item.shovelWood.name=Wooden Shovel +item.pickaxeWood.name=Wooden Pickaxe +item.hatchetWood.name=Wooden Axe +item.swordStone.name=Stone Sword +item.shovelStone.name=Stone Shovel +item.pickaxeStone.name=Stone Pickaxe +item.hatchetStone.name=Stone Axe +item.swordDiamond.name=Diamond Sword +item.shovelDiamond.name=Diamond Shovel +item.pickaxeDiamond.name=Diamond Pickaxe +item.hatchetDiamond.name=Diamond Axe +item.stick.name=Stick +item.bowl.name=Bowl +item.mushroomStew.name=Mushroom Stew +item.swordGold.name=Golden Sword +item.shovelGold.name=Golden Shovel +item.pickaxeGold.name=Golden Pickaxe +item.hatchetGold.name=Golden Axe +item.string.name=String +item.feather.name=Feather +item.sulphur.name=Gunpowder +item.hoeWood.name=Wooden Hoe +item.hoeStone.name=Stone Hoe +item.hoeIron.name=Iron Hoe +item.hoeDiamond.name=Diamond Hoe +item.hoeGold.name=Golden Hoe +item.seeds.name=Seeds +item.seeds_pumpkin.name=Pumpkin Seeds +item.seeds_melon.name=Melon Seeds +item.melon.name=Melon +item.wheat.name=Wheat +item.bread.name=Bread +item.helmetCloth.name=Leather Cap +item.chestplateCloth.name=Leather Tunic +item.leggingsCloth.name=Leather Pants +item.bootsCloth.name=Leather Boots +item.helmetChain.name=Chain Helmet +item.chestplateChain.name=Chain Chestplate +item.leggingsChain.name=Chain Leggings +item.bootsChain.name=Chain Boots +item.helmetIron.name=Iron Helmet +item.chestplateIron.name=Iron Chestplate +item.leggingsIron.name=Iron Leggings +item.bootsIron.name=Iron Boots +item.helmetDiamond.name=Diamond Helmet +item.chestplateDiamond.name=Diamond Chestplate +item.leggingsDiamond.name=Diamond Leggings +item.bootsDiamond.name=Diamond Boots +item.helmetGold.name=Golden Helmet +item.chestplateGold.name=Golden Chestplate +item.leggingsGold.name=Golden Leggings +item.bootsGold.name=Golden Boots +item.flint.name=Flint +item.porkchopRaw.name=Raw Porkchop +item.porkchopCooked.name=Cooked Porkchop +item.chickenRaw.name=Raw Chicken +item.chickenCooked.name=Cooked Chicken +item.beefRaw.name=Raw Beef +item.beefCooked.name=Steak +item.painting.name=Painting +item.appleGold.name=Golden Apple +item.sign.name=Sign +item.doorWood.name=Wooden Door +item.bucket.name=Bucket +item.bucketWater.name=Water Bucket +item.bucketLava.name=Lava Bucket +item.minecart.name=Minecart +item.saddle.name=Saddle +item.doorIron.name=Iron Door +item.redstone.name=Redstone +item.snowball.name=Snowball +item.boat.name=Boat +item.leather.name=Leather +item.milk.name=Milk +item.brick.name=Brick +item.clay.name=Clay +item.reeds.name=Sugar Canes +item.paper.name=Paper +item.book.name=Book +item.slimeball.name=Slimeball +item.minecartChest.name=Minecart with Chest +item.minecartFurnace.name=Minecart with Furnace +item.egg.name=Egg +item.compass.name=Compass +item.fishingRod.name=Fishing Rod +item.clock.name=Clock +item.yellowDust.name=Glowstone Dust +item.fishRaw.name=Raw Fish +item.fishCooked.name=Cooked Fish +item.record.name=Music Disc +item.bone.name=Bone +item.dyePowder.black.name=Ink Sac +item.dyePowder.red.name=Rose Red +item.dyePowder.green.name=Cactus Green +item.dyePowder.brown.name=Cocoa Beans +item.dyePowder.blue.name=Lapis Lazuli +item.dyePowder.purple.name=Purple Dye +item.dyePowder.cyan.name=Cyan Dye +item.dyePowder.silver.name=Light Gray Dye +item.dyePowder.gray.name=Gray Dye +item.dyePowder.pink.name=Pink Dye +item.dyePowder.lime.name=Lime Dye +item.dyePowder.yellow.name=Dandelion Yellow +item.dyePowder.lightBlue.name=Light Blue Dye +item.dyePowder.magenta.name=Magenta Dye +item.dyePowder.orange.name=Orange Dye +item.dyePowder.white.name=Bone Meal +item.sugar.name=Sugar +item.cake.name=Cake +item.bed.name=Bed +item.diode.name=Redstone Repeater +item.map.name=Map +item.leaves.name=Leaves +item.shears.name=Shears +item.rottenFlesh.name=Rotten Flesh +item.enderPearl.name=Ender Pearl +item.blazeRod.name=Blaze Rod +item.ghastTear.name=Ghast Tear +item.netherStalkSeeds.name=Nether Wart +item.potion.name=Potion +item.emptyPotion.name=Water Bottle +item.goldNugget.name=Gold Nugget +item.glassBottle.name=Glass Bottle +item.spiderEye.name=Spider Eye +item.fermentedSpiderEye.name=Fermented Spider Eye +item.blazePowder.name=Blaze Powder +item.magmaCream.name=Magma Cream +item.cauldron.name=Cauldron +item.brewingStand.name=Brewing Stand +item.eyeOfEnder.name=Eye of Ender +item.speckledMelon.name=Glistering Melon +item.monsterPlacer.name=Spawn +item.expBottle.name=Bottle o' Enchanting +item.fireball.name=Fire Charge + +container.inventory=Inventory +container.crafting=Crafting +container.dispenser=Dispenser +container.furnace=Furnace +container.enchant=Enchant +container.creative=Item Selection +container.brewing=Brewing Stand +container.chest=Chest +container.minecart=Minecart + +entity.Item.name=Item +entity.XPOrb.name=Experience Orb + +entity.Arrow.name=Arrow +entity.Snowball.name=Snowball +entity.Painting.name=Painting + +entity.Mob.name=Mob +entity.Monster.name=Monster + +entity.Creeper.name=Creeper +entity.Skeleton.name=Skeleton +entity.Spider.name=Spider +entity.Giant.name=Giant +entity.Zombie.name=Zombie +entity.Slime.name=Slime +entity.Ghast.name=Ghast +entity.PigZombie.name=Zombie Pigman +entity.Enderman.name=Enderman +entity.Silverfish.name=Silverfish +entity.CaveSpider.name=Cave Spider +entity.Blaze.name=Blaze +entity.LavaSlime.name=Magma Cube +entity.MushroomCow.name=Mooshroom +entity.Villager.name=Villager +entity.VillagerGolem.name=Iron Golem +entity.SnowMan.name=Snow Golem +entity.EnderDragon.name=Ender Dragon + +entity.Pig.name=Pig +entity.Sheep.name=Sheep +entity.Cow.name=Cow +entity.Chicken.name=Chicken +entity.Squid.name=Squid +entity.Wolf.name=Wolf +entity.Ozelot.name=Ocelot +entity.Cat.name=Cat + +entity.PrimedTnt.name=Block of TNT +entity.FallingSand.name=Falling Block + +entity.Minecart.name=Minecart +entity.Boat.name=Boat + +entity.Arrow.name=arrow +entity.generic.name=unknown + +death.inFire=%1$s went up in flames +death.onFire=%1$s burned to death +death.lava=%1$s tried to swim in lava +death.inWall=%1$s suffocated in a wall +death.drown=%1$s drowned +death.starve=%1$s starved to death +death.cactus=%1$s was pricked to death +death.fall=%1$s hit the ground too hard +death.outOfWorld=%1$s fell out of the world +death.generic=%1$s died +death.explosion=%1$s blew up +death.magic=%1$s was killed by magic +death.mob=%1$s was slain by %2$s +death.player=%1$s was slain by %2$s +death.arrow=%1$s was shot by %2$s +death.fireball=%1$s was fireballed by %2$s +death.thrown=%1$s was pummeled by %2$s +death.indirectMagic=%1$s was killed by %2$s + +deathScreen.respawn=Respawn +deathScreen.deleteWorld=Delete world +deathScreen.titleScreen=Title screen +deathScreen.score=Score +deathScreen.title.hardcore=Game over! +deathScreen.hardcoreInfo=You cannot respawn in hardcore mode! +deathScreen.title=You died! + +potion.empty=No Effects +potion.moveSpeed=Speed +potion.moveSlowdown=Slowness +potion.digSpeed=Haste +potion.digSlowDown=Mining Fatigue +potion.damageBoost=Strength +potion.weakness=Weakness +potion.heal=Instant Health +potion.harm=Instant Damage +potion.jump=Jump Boost +potion.confusion=Nausea +potion.regeneration=Regeneration +potion.resistance=Resistance +potion.fireResistance=Fire Resistance +potion.waterBreathing=Water Breathing +potion.invisibility=Invisibility +potion.blindness=Blindness +potion.nightVision=Night Vision +potion.hunger=Hunger +potion.poison=Poison + +potion.moveSpeed.postfix=Potion of Swiftness +potion.moveSlowdown.postfix=Potion of Slowness +potion.digSpeed.postfix=Potion of Haste +potion.digSlowDown.postfix=Potion of Dullness +potion.damageBoost.postfix=Potion of Strength +potion.weakness.postfix=Potion of Weakness +potion.heal.postfix=Potion of Healing +potion.harm.postfix=Potion of Harming +potion.jump.postfix=Potion of Leaping +potion.confusion.postfix=Potion of Nausea +potion.regeneration.postfix=Potion of Regeneration +potion.resistance.postfix=Potion of Resistance +potion.fireResistance.postfix=Potion of Fire Resistance +potion.waterBreathing.postfix=Potion of Water Breathing +potion.invisibility.postfix=Potion of Invisibility +potion.blindness.postfix=Potion of Blindness +potion.nightVision.postfix=Potion of Night Vision +potion.hunger.postfix=Potion of Hunger +potion.poison.postfix=Potion of Poison + +potion.potency.0= +potion.potency.1=II +potion.potency.2=III +potion.potency.3=IV + +potion.prefix.grenade=Splash +potion.prefix.mundane=Mundane +potion.prefix.uninteresting=Uninteresting +potion.prefix.bland=Bland +potion.prefix.clear=Clear +potion.prefix.milky=Milky +potion.prefix.diffuse=Diffuse +potion.prefix.artless=Artless +potion.prefix.thin=Thin +potion.prefix.awkward=Awkward +potion.prefix.flat=Flat +potion.prefix.bulky=Bulky +potion.prefix.bungling=Bungling +potion.prefix.buttered=Buttered +potion.prefix.smooth=Smooth +potion.prefix.suave=Suave +potion.prefix.debonair=Debonair +potion.prefix.thick=Thick +potion.prefix.elegant=Elegant +potion.prefix.fancy=Fancy +potion.prefix.charming=Charming +potion.prefix.dashing=Dashing +potion.prefix.refined=Refined +potion.prefix.cordial=Cordial +potion.prefix.sparkling=Sparkling +potion.prefix.potent=Potent +potion.prefix.foul=Foul +potion.prefix.odorless=Odorless +potion.prefix.rank=Rank +potion.prefix.harsh=Harsh +potion.prefix.acrid=Acrid +potion.prefix.gross=Gross +potion.prefix.stinky=Stinky + +enchantment.damage.all=Sharpness +enchantment.damage.undead=Smite +enchantment.damage.arthropods=Bane of Arthropods +enchantment.knockback=Knockback +enchantment.fire=Fire Aspect +enchantment.protect.all=Protection +enchantment.protect.fire=Fire Protection +enchantment.protect.fall=Feather Falling +enchantment.protect.explosion=Blast Protection +enchantment.protect.projectile=Projectile Protection +enchantment.oxygen=Respiration +enchantment.waterWorker=Aqua Affinity +enchantment.digging=Efficiency +enchantment.untouching=Silk Touch +enchantment.durability=Unbreaking +enchantment.lootBonus=Looting +enchantment.lootBonusDigger=Fortune +enchantment.arrowDamage=Power +enchantment.arrowFire=Flame +enchantment.arrowKnockback=Punch +enchantment.arrowInfinite=Infinity + +enchantment.level.1=I +enchantment.level.2=II +enchantment.level.3=III +enchantment.level.4=IV +enchantment.level.5=V +enchantment.level.6=VI +enchantment.level.7=VII +enchantment.level.8=VIII +enchantment.level.9=IX +enchantment.level.10=X + +gui.achievements=Achievements +gui.stats=Statistics + +stat.generalButton=General +stat.blocksButton=Blocks +stat.itemsButton=Items + +stat.used=Times Used +stat.mined=Times Mined +stat.depleted=Times Depleted +stat.crafted=Times Crafted + +stat.startGame=Times played +stat.createWorld=Worlds played +stat.loadWorld=Saves loaded +stat.joinMultiplayer=Multiplayer joins +stat.leaveGame=Games quit + +stat.playOneMinute=Minutes Played + +stat.walkOneCm=Distance Walked +stat.fallOneCm=Distance Fallen +stat.swimOneCm=Distance Swum +stat.flyOneCm=Distance Flown +stat.climbOneCm=Distance Climbed +stat.diveOneCm=Distance Dove +stat.minecartOneCm=Distance by Minecart +stat.boatOneCm=Distance by Boat +stat.pigOneCm=Distance by Pig +stat.jump=Jumps +stat.drop=Items Dropped + +stat.damageDealt=Damage Dealt +stat.damageTaken=Damage Taken +stat.deaths=Number of Deaths +stat.mobKills=Mob Kills +stat.playerKills=Player Kills +stat.fishCaught=Fish Caught + +stat.mineBlock=%1$s Mined +stat.craftItem=%1$s Crafted +stat.useItem=%1$s Used +stat.breakItem=%1$s Depleted + +achievement.get=Achievement get! + +achievement.taken=Taken! + +achievement.requires=Requires '%1$s' +achievement.openInventory=Taking Inventory +achievement.openInventory.desc=Press '%1$s' to open your inventory. +achievement.mineWood=Getting Wood +achievement.mineWood.desc=Attack a tree until a block of wood pops out +achievement.buildWorkBench=Benchmarking +achievement.buildWorkBench.desc=Craft a workbench with four blocks of planks +achievement.buildPickaxe=Time to Mine! +achievement.buildPickaxe.desc=Use planks and sticks to make a pickaxe +achievement.buildFurnace=Hot Topic +achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks +achievement.acquireIron=Acquire Hardware +achievement.acquireIron.desc=Smelt an iron ingot +achievement.buildHoe=Time to Farm! +achievement.buildHoe.desc=Use planks and sticks to make a hoe +achievement.makeBread=Bake Bread +achievement.makeBread.desc=Turn wheat into bread +achievement.bakeCake=The Lie +achievement.bakeCake.desc=Wheat, sugar, milk and eggs! +achievement.buildBetterPickaxe=Getting an Upgrade +achievement.buildBetterPickaxe.desc=Construct a better pickaxe +achievement.cookFish=Delicious Fish +achievement.cookFish.desc=Catch and cook fish! +achievement.onARail=On A Rail +achievement.onARail.desc=Travel by minecart at least 1 km from where you started +achievement.buildSword=Time to Strike! +achievement.buildSword.desc=Use planks and sticks to make a sword +achievement.killEnemy=Monster Hunter +achievement.killEnemy.desc=Attack and destroy a monster +achievement.killCow=Cow Tipper +achievement.killCow.desc=Harvest some leather +achievement.flyPig=When Pigs Fly +achievement.flyPig.desc=Fly a pig off a cliff +achievement.snipeSkeleton=Sniper Duel +achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters +achievement.diamonds=DIAMONDS! +achievement.diamonds.desc=Acquire diamonds with your iron tools +achievement.portal=We Need to Go Deeper +achievement.portal.desc=Build a portal to the Nether +achievement.ghast=Return to Sender +achievement.ghast.desc=Destroy a Ghast with a fireball +achievement.blazeRod=Into Fire +achievement.blazeRod.desc=Relieve a Blaze of its rod +achievement.potion=Local Brewery +achievement.potion.desc=Brew a potion +achievement.theEnd=The End? +achievement.theEnd.desc=Locate the End +achievement.theEnd2=The End. +achievement.theEnd2.desc=Defeat the Ender Dragon +achievement.enchantments=Enchanter +achievement.enchantments.desc=Use a book, obsidian and diamonds to construct an enchantment table +achievement.overkill=Overkill +achievement.overkill.desc=Deal eight hearts of damage in a single hit +achievement.bookcase=Librarian +achievement.bookcase.desc=Build some bookshelves to improve your enchantment table + +desc.sword=Deals more damage than by hand. +desc.shovel=Used to dig dirt, grass, sand, gravel and snow faster than by hand. +desc.pickaxe=Required to mine stone-related blocks and ore. +desc.hatchet=Used to chop wood-related blocks faster than by hand. +desc.hoe=Used to till dirt and grass blocks to prepare for crops. +desc.doorwood=Wooden doors are activated by using, hitting them or with Redstone. +desc.dooriron=Iron doors can only be opened by Redstone, buttons or switches. +desc.helmet=Gives the user 1.5 Armor when worn. +desc.chestplate=Gives the user 4 Armor when worn. +desc.leggings=Gives the user 3 Armor when worn. +desc.boots=Gives the user 1.5 Armor when worn. +desc.ingot=A shiny ingot which can be used to craft tools made from this material. +desc.block=Allows ingots, gems, or dyes to be crafted into placeable blocks. Can be used as an expensive building block or compact storage of the ore. +desc.pressureplate=Used to send an electrical charge when stepped on by a player, an animal, or a monster. Wooden Pressure Plates can also be activated by dropping something on them. +desc.stairs=Used for compact staircases. +desc.slab=Used for making long staircases. +desc.halfslab=Used for compact staircases. +desc.torch=Used to create light. Torches also melt snow and ice. +desc.wood=Used as a building material and can be crafted into many things. Can be crafted from any wood. +desc.sandstone=Used as a building material. +desc.sandstone.addon=Is not influenced by gravity like normal Sand. +desc.structblock=Used as a building material. +desc.stick=Used to craft torches, arrows, signs, ladders, fences and as handles for tools and weapons. +desc.bed=Used to skip to dawn, if all players are sleeping. Changes your spawn point to the bed's position. +The colors of the bed are always the same, regardless of the colors of wool used. +desc.craftingtable=Allows you to craft a more varied selection of items than the normal crafting. +desc.furnace=Allows you to smelt ore, create charcoal and glass, and cook fish and porkchops. +desc.chest=Stores blocks and items inside. +desc.fence=Used as a barrier that cannot be jumped over. +desc.ladder=Used to climb vertically. +desc.trapdoor=Activated by using, hitting them or with redstone. They function as normal doors, but are a one by one block and lay flat on the ground. +desc.sign=Shows text entered by you or other players. +desc.glowstone=Used to create brighter light than torches. Melts snow/ice and can be used underwater. +desc.tnt=Used to cause explosions. Activated after placing by hitting, or with an electrical charge. +desc.bowl=Used to hold mushroom stew. You keep the bowl when the stew has been eaten. +desc.bucket=Used to hold and transport water, lava and milk. +desc.flintandsteel=Used to detonate TNT. +desc.fishingrod=Used to catch fish. +desc.clock=Displays positions of the Sun and Moon. +desc.compass=Points to your start point. +desc.map=Will create an image of an area explored while held. This can be used for path-finding. +desc.bow=Allows for ranged attacks by using arrows. +desc.arrow=Used as ammunition for bows. +desc.mushroomstew=Restores 5 hearts. +desc.cookie=Restores 0.5 Hearts. Can be stacked up to 8 times. +desc.cake=Restores 1.5 Hearts. Can be used 6 times. +desc.sugar=Used in the cake recipe. +desc.bread=Restores 2.5 Hearts. +desc.goldenapple=Restores 10 Hearts. +desc.lever=Used to send an electrical charge by being turned on or off. Stays in the on or off state until pressed again. +desc.redstonetorch=Constantly sends an electrical charge, or can be used as a receiver/transmitter when connected to the side of a block. +Can also be used for low-level lighting. +desc.redstonerepeater=Used in Redstone circuits as repeater, a delayer, and/or a diode +desc.button=Used to send an electrical charge by being pressed. Stays activated for approximately a second before shutting off again. +desc.dispenser=Used to hold and shoot out items in a random order when given a Redstone charge. +desc.noteblock=Plays a note when triggered. Hit it to change the pitch of the note. Placing this on top of different blocks will change the type of instrument. +desc.rail=Used to guide minecarts. +desc.poweredrail=When powered, accelerates minecarts that pass over it. When unpowered, causes minecarts to stop on it. +desc.detectorrail=Functions like a Pressure Plate (sends a Redstone signal when powered) but can only be activated by a minecart. +desc.minecart=Used to transport you, an animal, or a monster along rails. +desc.minecartwithchest=Used to transport goods along rails. +desc.minecartwithfurnace=Will move along rails and can push other minecarts when coal is put in it. +desc.boat=Used to travel in water more quickly than swimming. +desc.wool=Collected from sheep, and can be colored with dyes. +desc.woolstring=Used as a building material and can be colored with dyes. Can be easily obtained from Sheep. +desc.dyepowder.black=Used as a dye to create black wool. +desc.dyepowder.green=Used as a dye to create green wool. +desc.dyepowder.brown=Used as a dye to create brown wool. +desc.dyepowder.silver=Used as a dye to create silver wool. +desc.dyepowder.yellow=Used as a dye to create yellow wool. +desc.dyepowder.red=Used as a dye to create red wool. +desc.dyepowder.white=Used to instantly grow crops, trees, tall grass, huge mushrooms and flowers, and can be used in dye recipes. +desc.dyepowder.pink=Used as a dye to create pink wool. +desc.dyepowder.orange=Used as a dye to create orange wool. +desc.dyepowder.lime=Used as a dye to create lime wool. +desc.dyepowder.gray=Used as a dye to create gray wool. +desc.dyepowder.lightgray=Used as a dye to create light gray wool. +(Note: light gray dye can also be made by combining gray dye with bone meal, letting you make four light gray dyes from every ink sac instead of three.) +desc.dyepowder.lightblue=Used as a dye to create light blue wool. +desc.dyepowder.cyan=Used as a dye to create cyan wool. +desc.dyepowder.purple=Used as a dye to create purple wool. +desc.dyepowder.magenta=Used as a dye to create magenta wool. +desc.dyepowder.blue=Used as dye to create Blue Wool. +desc.jukebox=Plays Music Discs. +desc.diamonds=Use these to create very strong tools, weapons or armor +desc.emerald=Use these to create very strong tools, weapons or armor +desc.jackolantern=Used to create brighter light than torches. Melts snow/ice and can be used underwater. +desc.paper=Used to create books and maps. +desc.book=Used to create a bookshelf. +desc.bookshelf=Used as decoration. +desc.picture=Used as decoration. +desc.ore_gold=Can be mined with an iron pickaxe or better, then smelted in a furnace to produce gold ingots. +desc.ore_iron=Can be mined with a stone pickaxe or better, then smelted in a furnace to produce iron ingots. +desc.ore_coal=Can be mined with a pickaxe to collect coal. +desc.ore_lapis=Can be mined with a stone pickaxe or better to collect lapis lazuli. +desc.ore_diamond=Can be mined with an iron pickaxe or better to collect diamonds. +desc.ore_redstone=Can be mined with an iron pickaxe or better to collect redstone dust. +desc.stone=Can be mined with a pickaxe to collect cobblestone. +desc.dirt=Collected using a shovel. Can be used for construction. +desc.sapling=Can be planted and it will eventually grow into a tree. +desc.bedrock=This cannot be broken. +desc.lava=Sets fire to anything that touches it. Can be collected in a bucket. +desc.sand=Collected using a shovel. Can be smelted into glass using the furnace. Is affected by gravity if there is no other tile underneath it. +desc.gravel=Collected using a shovel. Sometimes produces flint when dug up. Is affected by gravity if there is no other tile underneath it. +desc.log=Chopped using an axe, and can be crafted into planks or used as a fuel. +desc.glass=Created in a furnace by smelting sand. Will break if you try to mine it. +desc.thinglass=Will break if you try to mine it. +desc.stone_brick=Mined from stone using a pickaxe. Can be used to construct a furnace or stone tools. +desc.brick=Baked from clay in a furnace. +desc.clay=Can be baked into bricks in a furnace. +desc.snow=A compact way to store snowballs. +desc.top_snow=Can be dug with a shovel to create snowballs. +desc.tall_grass=Sometimes produces seeds when broken. +desc.flower=Can be crafted into a dye. +desc.mushroom=Can be crafted with a bowl to make stew. +desc.obsidian=Can only be mined with a diamond pickaxe. Is produced by the meeting of water and still lava, and is used to build a portal. +desc.mob_spawner=Spawns monsters into the world. +desc.redstone_dust=Is placed on the ground to carry an electrical charge. +desc.crops=When fully grown, crops can be harvested to collect wheat. +desc.farmland=Ground that has been prepared ready to plant seeds. +desc.cactus=Can be crafted to create a dye. +desc.reeds=Can be crafted to create sugar. +desc.pumpkin=Can be worn as a helmet or crafted with a torch to create a Jack-O-Lantern. +desc.hell_rock=Burns forever if set alight. +desc.hell_sand=Slows the movement of anything walking over it. +desc.portal=Standing in the portal allows you to pass between the overworld and the nether. +desc.apple=Restores health, and can be crafted into a golden apple. +desc.coal=Used as a fuel in a furnace, or crafted to make a torch. +desc.string=Collected by killing a spider, and can be crafted into a bow. +desc.feather=Collected by killing a chicken, and can be crafted into an arrow. +desc.sulphur=Collected by killing a creeper, and can be crafted into TNT. +desc.shears=Used to obtain wool from sheep and to harvest placeable Leaf blocks. +desc.seeds=Can be planted in farmland to grow crops. +desc.wheat=Harvested from crops, and can be used to craft food items. +desc.flint=Collected by digging gravel, and can be used to craft a flint and steel. +desc.porkchopraw=Collected by killing a pig, and can be cooked in a furnace. Restores health. +desc.porkchopcooked=Created by cooking a porkchop in a furnace. Restores health. +desc.beefraw=Collected by killing a cow, and can be cooked in a furnace. Restores health. +desc.beefcooked=Created by cooking a beef in a furnace. Restores health. +desc.chickenraw=Collected by killing a chicken, and can be cooked in a furnace. Restores health. +desc.chickencooked=Created by cooking a chicken in a furnace. Restores health. +desc.saddle=When used on a pig it allows you to ride the pig. +desc.snowball=Collected by digging snow, and can be thrown. +desc.leather=Collected by killing a cow, and can be crafted into armor. +desc.slimeball=Collected by killing a slime. +desc.egg=Dropped randomly by chickens, and can be crafted into food items. +desc.yellow_dust=Collected by mining Glowstone, and can be crafted to make Glowstone blocks again. +desc.fish_raw=Can be eaten to restore health, or cooked in a furnace. +desc.fish_cooked=Created by cooking a raw fish in a furnace. Can be eaten to restore health. +desc.bone=Collected by killing a skeleton. Can be crafted into bone meal. +desc.record=Collected by getting a skeleton to kill a creeper. Can be played in a jukebox. +desc.water=Extinguishes fire and helps crops grow. Can be collected in a bucket. +desc.leaves=When broken sometimes drops a sapling which can then be replanted to grow into a tree. +desc.moss_stone=Can be used for construction and decoration. +desc.stonebricksmooth=Used as building material. +desc.netherreactor=Core of the Nether Reactor desc.glowingobsidian=Obsidion activated by external source. \ No newline at end of file diff --git a/project/android_java/jni/Android.mk b/project/android_java/jni/Android.mk index 04720a6..17b587f 100755 --- a/project/android_java/jni/Android.mk +++ b/project/android_java/jni/Android.mk @@ -1,250 +1,250 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := minecraftpe -LOCAL_SRC_FILES := ../../../src/main.cpp \ - ../../../src/main_android_java.cpp \ - ../../../src/platform/input/Controller.cpp \ - ../../../src/platform/input/Keyboard.cpp \ - ../../../src/platform/input/Mouse.cpp \ - ../../../src/platform/input/Multitouch.cpp \ - ../../../src/platform/time.cpp \ - ../../../src/platform/CThread.cpp \ -../../../src/NinecraftApp.cpp \ -../../../src/Performance.cpp \ -../../../src/SharedConstants.cpp \ -../../../src/client/IConfigListener.cpp \ -../../../src/client/Minecraft.cpp \ -../../../src/client/MouseHandler.cpp \ -../../../src/client/Options.cpp \ -../../../src/client/OptionStrings.cpp \ -../../../src/client/gamemode/GameMode.cpp \ -../../../src/client/gamemode/CreativeMode.cpp \ -../../../src/client/gamemode/SurvivalMode.cpp \ -../../../src/client/gui/components/Button.cpp \ -../../../src/client/gui/components/ImageButton.cpp \ -../../../src/client/gui/components/ItemPane.cpp \ -../../../src/client/gui/components/InventoryPane.cpp \ -../../../src/client/gui/components/LargeImageButton.cpp \ -../../../src/client/gui/components/RolledSelectionListH.cpp \ -../../../src/client/gui/components/RolledSelectionListV.cpp \ -../../../src/client/gui/components/ScrolledSelectionList.cpp \ -../../../src/client/gui/components/ScrollingPane.cpp \ -../../../src/client/gui/components/SmallButton.cpp \ -../../../src/client/gui/Font.cpp \ -../../../src/client/gui/Gui.cpp \ -../../../src/client/gui/GuiComponent.cpp \ -../../../src/client/gui/Screen.cpp \ -../../../src/client/gui/screens/ScreenChooser.cpp \ -../../../src/client/gui/screens/ChatScreen.cpp \ -../../../src/client/gui/screens/ChestScreen.cpp \ -../../../src/client/gui/screens/ConfirmScreen.cpp \ -../../../src/client/gui/screens/DeathScreen.cpp \ -../../../src/client/gui/screens/FurnaceScreen.cpp \ -../../../src/client/gui/screens/InBedScreen.cpp \ -../../../src/client/gui/screens/IngameBlockSelectionScreen.cpp \ -../../../src/client/gui/screens/JoinGameScreen.cpp \ -../../../src/client/gui/screens/OptionsScreen.cpp \ -../../../src/client/gui/screens/PauseScreen.cpp \ -../../../src/client/gui/screens/ProgressScreen.cpp \ -../../../src/client/gui/screens/RenameMPLevelScreen.cpp \ -../../../src/client/gui/screens/SelectWorldScreen.cpp \ -../../../src/client/gui/screens/StartMenuScreen.cpp \ -../../../src/client/gui/screens/TextEditScreen.cpp \ -../../../src/client/gui/screens/touch/TouchIngameBlockSelectionScreen.cpp \ -../../../src/client/gui/screens/touch/TouchJoinGameScreen.cpp \ -../../../src/client/gui/screens/touch/TouchSelectWorldScreen.cpp \ -../../../src/client/gui/screens/touch/TouchStartMenuScreen.cpp \ -../../../src/client/gui/screens/UploadPhotoScreen.cpp \ -../../../src/client/gui/screens/crafting/PaneCraftingScreen.cpp \ -../../../src/client/gui/screens/crafting/WorkbenchScreen.cpp \ -../../../src/client/model/ChickenModel.cpp \ -../../../src/client/model/CowModel.cpp \ -../../../src/client/model/HumanoidModel.cpp \ -../../../src/client/model/PigModel.cpp \ -../../../src/client/model/SheepFurModel.cpp \ -../../../src/client/model/SheepModel.cpp \ -../../../src/client/model/QuadrupedModel.cpp \ -../../../src/client/model/geom/Cube.cpp \ -../../../src/client/model/geom/ModelPart.cpp \ -../../../src/client/model/geom/Polygon.cpp \ -../../../src/client/particle/Particle.cpp \ -../../../src/client/particle/ParticleEngine.cpp \ -../../../src/client/player/LocalPlayer.cpp \ -../../../src/client/player/RemotePlayer.cpp \ -../../../src/client/player/input/KeyboardInput.cpp \ -../../../src/client/player/input/touchscreen/TouchscreenInput.cpp \ -../../../src/client/renderer/Chunk.cpp \ -../../../src/client/renderer/EntityTileRenderer.cpp \ -../../../src/client/renderer/GameRenderer.cpp \ -../../../src/client/renderer/ItemInHandRenderer.cpp \ -../../../src/client/renderer/LevelRenderer.cpp \ -../../../src/client/renderer/RenderChunk.cpp \ -../../../src/client/renderer/RenderList.cpp \ -../../../src/client/renderer/Tesselator.cpp \ -../../../src/client/renderer/Textures.cpp \ -../../../src/client/renderer/TileRenderer.cpp \ -../../../src/client/renderer/gles.cpp \ -../../../src/client/renderer/culling/Frustum.cpp \ -../../../src/client/renderer/entity/ArrowRenderer.cpp \ -../../../src/client/renderer/entity/ChickenRenderer.cpp \ -../../../src/client/renderer/entity/EntityRenderDispatcher.cpp \ -../../../src/client/renderer/entity/EntityRenderer.cpp \ -../../../src/client/renderer/entity/HumanoidMobRenderer.cpp \ -../../../src/client/renderer/entity/ItemRenderer.cpp \ -../../../src/client/renderer/entity/ItemSpriteRenderer.cpp \ -../../../src/client/renderer/entity/MobRenderer.cpp \ -../../../src/client/renderer/entity/PaintingRenderer.cpp \ -../../../src/client/renderer/entity/PlayerRenderer.cpp \ -../../../src/client/renderer/entity/SheepRenderer.cpp \ -../../../src/client/renderer/entity/TntRenderer.cpp \ -../../../src/client/renderer/entity/TripodCameraRenderer.cpp \ -../../../src/client/renderer/ptexture/DynamicTexture.cpp \ -../../../src/client/renderer/tileentity/ChestRenderer.cpp \ -../../../src/client/renderer/tileentity/SignRenderer.cpp \ -../../../src/client/renderer/tileentity/TileEntityRenderDispatcher.cpp \ -../../../src/client/renderer/tileentity/TileEntityRenderer.cpp \ -../../../src/client/sound/Sound.cpp \ -../../../src/client/sound/SoundEngine.cpp \ -../../../src/locale/I18n.cpp \ -../../../src/nbt/Tag.cpp \ -../../../src/network/command/CommandServer.cpp \ -../../../src/network/ClientSideNetworkHandler.cpp \ -../../../src/network/NetEventCallback.cpp \ -../../../src/network/Packet.cpp \ -../../../src/network/RakNetInstance.cpp \ -../../../src/network/ServerSideNetworkHandler.cpp \ -../../../src/server/ServerLevel.cpp \ -../../../src/server/ServerPlayer.cpp \ -../../../src/util/DataIO.cpp \ -../../../src/util/Mth.cpp \ -../../../src/util/StringUtils.cpp \ -../../../src/util/PerfTimer.cpp \ -../../../src/util/PerfRenderer.cpp \ -../../../src/world/Direction.cpp \ -../../../src/world/entity/AgableMob.cpp \ -../../../src/world/entity/Entity.cpp \ -../../../src/world/entity/EntityFactory.cpp \ -../../../src/world/entity/FlyingMob.cpp \ -../../../src/world/entity/HangingEntity.cpp \ -../../../src/world/entity/Mob.cpp \ -../../../src/world/entity/MobCategory.cpp \ -../../../src/world/entity/Motive.cpp \ -../../../src/world/entity/Painting.cpp \ -../../../src/world/entity/PathfinderMob.cpp \ -../../../src/world/entity/SynchedEntityData.cpp \ -../../../src/world/entity/ai/control/MoveControl.cpp \ -../../../src/world/entity/animal/Animal.cpp \ -../../../src/world/entity/animal/Chicken.cpp \ -../../../src/world/entity/animal/Cow.cpp \ -../../../src/world/entity/animal/Pig.cpp \ -../../../src/world/entity/animal/Sheep.cpp \ -../../../src/world/entity/animal/WaterAnimal.cpp \ -../../../src/world/entity/item/FallingTile.cpp \ -../../../src/world/entity/item/ItemEntity.cpp \ -../../../src/world/entity/item/PrimedTnt.cpp \ -../../../src/world/entity/item/TripodCamera.cpp \ -../../../src/world/entity/monster/Creeper.cpp \ -../../../src/world/entity/monster/Monster.cpp \ -../../../src/world/entity/monster/PigZombie.cpp \ -../../../src/world/entity/monster/Skeleton.cpp \ -../../../src/world/entity/monster/Spider.cpp \ -../../../src/world/entity/monster/Zombie.cpp \ -../../../src/world/entity/projectile/Arrow.cpp \ -../../../src/world/entity/projectile/Throwable.cpp \ -../../../src/world/entity/player/Inventory.cpp \ -../../../src/world/entity/player/Player.cpp \ -../../../src/world/food/SimpleFoodData.cpp \ -../../../src/world/inventory/BaseContainerMenu.cpp \ -../../../src/world/inventory/ContainerMenu.cpp \ -../../../src/world/inventory/FillingContainer.cpp \ -../../../src/world/inventory/FurnaceMenu.cpp \ -../../../src/world/item/BedItem.cpp \ -../../../src/world/item/DyePowderItem.cpp \ -../../../src/world/item/Item.cpp \ -../../../src/world/item/ItemInstance.cpp \ -../../../src/world/item/HangingEntityItem.cpp \ -../../../src/world/item/HatchetItem.cpp \ -../../../src/world/item/HoeItem.cpp \ -../../../src/world/item/PickaxeItem.cpp \ -../../../src/world/item/ShovelItem.cpp \ -../../../src/world/item/crafting/Recipe.cpp \ -../../../src/world/item/crafting/Recipes.cpp \ -../../../src/world/item/crafting/FurnaceRecipes.cpp \ -../../../src/world/item/crafting/OreRecipes.cpp \ -../../../src/world/item/crafting/StructureRecipes.cpp \ -../../../src/world/item/crafting/ToolRecipes.cpp \ -../../../src/world/item/crafting/WeaponRecipes.cpp \ -../../../src/world/level/Explosion.cpp \ -../../../src/world/level/Level.cpp \ -../../../src/world/level/LightLayer.cpp \ -../../../src/world/level/LightUpdate.cpp \ -../../../src/world/level/MobSpawner.cpp \ -../../../src/world/level/Region.cpp \ -../../../src/world/level/TickNextTickData.cpp \ -../../../src/world/level/biome/Biome.cpp \ -../../../src/world/level/biome/BiomeSource.cpp \ -../../../src/world/level/chunk/LevelChunk.cpp \ -../../../src/world/level/dimension/Dimension.cpp \ -../../../src/world/level/levelgen/CanyonFeature.cpp \ -../../../src/world/level/levelgen/DungeonFeature.cpp \ -../../../src/world/level/levelgen/LargeCaveFeature.cpp \ -../../../src/world/level/levelgen/LargeFeature.cpp \ -../../../src/world/level/levelgen/RandomLevelSource.cpp \ -../../../src/world/level/levelgen/feature/Feature.cpp \ -../../../src/world/level/levelgen/synth/ImprovedNoise.cpp \ -../../../src/world/level/levelgen/synth/PerlinNoise.cpp \ -../../../src/world/level/levelgen/synth/Synth.cpp \ -../../../src/world/level/material/Material.cpp \ -../../../src/world/level/pathfinder/Path.cpp \ -../../../src/world/level/storage/ExternalFileLevelStorage.cpp \ -../../../src/world/level/storage/ExternalFileLevelStorageSource.cpp \ -../../../src/world/level/storage/FolderMethods.cpp \ -../../../src/world/level/storage/LevelData.cpp \ -../../../src/world/level/storage/LevelStorageSource.cpp \ -../../../src/world/level/storage/RegionFile.cpp \ -../../../src/world/level/tile/BedTile.cpp \ -../../../src/world/level/tile/ChestTile.cpp \ -../../../src/world/level/tile/CropTile.cpp \ -../../../src/world/level/tile/DoorTile.cpp \ -../../../src/world/level/tile/EntityTile.cpp \ -../../../src/world/level/tile/FurnaceTile.cpp \ -../../../src/world/level/tile/GrassTile.cpp \ -../../../src/world/level/tile/LightGemTile.cpp \ -../../../src/world/level/tile/MelonTile.cpp \ -../../../src/world/level/tile/Mushroom.cpp \ -../../../src/world/level/tile/NetherReactor.cpp \ -../../../src/world/level/tile/NetherReactorPattern.cpp \ -../../../src/world/level/tile/SandTile.cpp \ -../../../src/world/level/tile/StemTile.cpp \ -../../../src/world/level/tile/StoneSlabTile.cpp \ -../../../src/world/level/tile/TallGrass.cpp \ -../../../src/world/level/tile/Tile.cpp \ -../../../src/world/level/tile/TrapDoorTile.cpp \ -../../../src/world/level/tile/entity/ChestTileEntity.cpp \ -../../../src/world/level/tile/entity/NetherReactorTileEntity.cpp \ -../../../src/world/level/tile/entity/SignTileEntity.cpp \ -../../../src/world/level/tile/entity/TileEntity.cpp \ -../../../src/world/level/tile/entity/FurnaceTileEntity.cpp \ -../../../src/world/phys/HitResult.cpp - -LOCAL_CFLAGS := -Wno-psabi $(LOCAL_CFLAGS) - -LOCAL_CFLAGS := -DPRE_ANDROID23 -DANDROID_PUBLISH $(LOCAL_CFLAGS) -#LOCAL_CFLAGS := -DPRE_ANDROID23 -DANDROID_PUBLISH -DDEMO_MODE $(LOCAL_CFLAGS) - -#LOCAL_CFLAGS := -DPRE_ANDROID23 -DDEMO_MODE $(LOCAL_CFLAGS) -#LOCAL_CFLAGS := -DPRE_ANDROID23 $(LOCAL_CFLAGS) - -LOCAL_LDLIBS := -llog -lEGL -lGLESv1_CM - -LOCAL_STATIC_LIBRARIES := RakNet - -TARGET_ARCH_ABI := armeabi-v7a - -include $(BUILD_SHARED_LIBRARY) - -# NOTE: environment var NDK_MODULE_PATH needs to point to lib_projects folder -$(call import-module, raknet/jni) - +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := minecraftpe +LOCAL_SRC_FILES := ../../../src/main.cpp \ + ../../../src/main_android_java.cpp \ + ../../../src/platform/input/Controller.cpp \ + ../../../src/platform/input/Keyboard.cpp \ + ../../../src/platform/input/Mouse.cpp \ + ../../../src/platform/input/Multitouch.cpp \ + ../../../src/platform/time.cpp \ + ../../../src/platform/CThread.cpp \ +../../../src/NinecraftApp.cpp \ +../../../src/Performance.cpp \ +../../../src/SharedConstants.cpp \ +../../../src/client/IConfigListener.cpp \ +../../../src/client/Minecraft.cpp \ +../../../src/client/MouseHandler.cpp \ +../../../src/client/Options.cpp \ +../../../src/client/OptionStrings.cpp \ +../../../src/client/gamemode/GameMode.cpp \ +../../../src/client/gamemode/CreativeMode.cpp \ +../../../src/client/gamemode/SurvivalMode.cpp \ +../../../src/client/gui/components/Button.cpp \ +../../../src/client/gui/components/ImageButton.cpp \ +../../../src/client/gui/components/ItemPane.cpp \ +../../../src/client/gui/components/InventoryPane.cpp \ +../../../src/client/gui/components/LargeImageButton.cpp \ +../../../src/client/gui/components/RolledSelectionListH.cpp \ +../../../src/client/gui/components/RolledSelectionListV.cpp \ +../../../src/client/gui/components/ScrolledSelectionList.cpp \ +../../../src/client/gui/components/ScrollingPane.cpp \ +../../../src/client/gui/components/SmallButton.cpp \ +../../../src/client/gui/Font.cpp \ +../../../src/client/gui/Gui.cpp \ +../../../src/client/gui/GuiComponent.cpp \ +../../../src/client/gui/Screen.cpp \ +../../../src/client/gui/screens/ScreenChooser.cpp \ +../../../src/client/gui/screens/ChatScreen.cpp \ +../../../src/client/gui/screens/ChestScreen.cpp \ +../../../src/client/gui/screens/ConfirmScreen.cpp \ +../../../src/client/gui/screens/DeathScreen.cpp \ +../../../src/client/gui/screens/FurnaceScreen.cpp \ +../../../src/client/gui/screens/InBedScreen.cpp \ +../../../src/client/gui/screens/IngameBlockSelectionScreen.cpp \ +../../../src/client/gui/screens/JoinGameScreen.cpp \ +../../../src/client/gui/screens/OptionsScreen.cpp \ +../../../src/client/gui/screens/PauseScreen.cpp \ +../../../src/client/gui/screens/ProgressScreen.cpp \ +../../../src/client/gui/screens/RenameMPLevelScreen.cpp \ +../../../src/client/gui/screens/SelectWorldScreen.cpp \ +../../../src/client/gui/screens/StartMenuScreen.cpp \ +../../../src/client/gui/screens/TextEditScreen.cpp \ +../../../src/client/gui/screens/touch/TouchIngameBlockSelectionScreen.cpp \ +../../../src/client/gui/screens/touch/TouchJoinGameScreen.cpp \ +../../../src/client/gui/screens/touch/TouchSelectWorldScreen.cpp \ +../../../src/client/gui/screens/touch/TouchStartMenuScreen.cpp \ +../../../src/client/gui/screens/UploadPhotoScreen.cpp \ +../../../src/client/gui/screens/crafting/PaneCraftingScreen.cpp \ +../../../src/client/gui/screens/crafting/WorkbenchScreen.cpp \ +../../../src/client/model/ChickenModel.cpp \ +../../../src/client/model/CowModel.cpp \ +../../../src/client/model/HumanoidModel.cpp \ +../../../src/client/model/PigModel.cpp \ +../../../src/client/model/SheepFurModel.cpp \ +../../../src/client/model/SheepModel.cpp \ +../../../src/client/model/QuadrupedModel.cpp \ +../../../src/client/model/geom/Cube.cpp \ +../../../src/client/model/geom/ModelPart.cpp \ +../../../src/client/model/geom/Polygon.cpp \ +../../../src/client/particle/Particle.cpp \ +../../../src/client/particle/ParticleEngine.cpp \ +../../../src/client/player/LocalPlayer.cpp \ +../../../src/client/player/RemotePlayer.cpp \ +../../../src/client/player/input/KeyboardInput.cpp \ +../../../src/client/player/input/touchscreen/TouchscreenInput.cpp \ +../../../src/client/renderer/Chunk.cpp \ +../../../src/client/renderer/EntityTileRenderer.cpp \ +../../../src/client/renderer/GameRenderer.cpp \ +../../../src/client/renderer/ItemInHandRenderer.cpp \ +../../../src/client/renderer/LevelRenderer.cpp \ +../../../src/client/renderer/RenderChunk.cpp \ +../../../src/client/renderer/RenderList.cpp \ +../../../src/client/renderer/Tesselator.cpp \ +../../../src/client/renderer/Textures.cpp \ +../../../src/client/renderer/TileRenderer.cpp \ +../../../src/client/renderer/gles.cpp \ +../../../src/client/renderer/culling/Frustum.cpp \ +../../../src/client/renderer/entity/ArrowRenderer.cpp \ +../../../src/client/renderer/entity/ChickenRenderer.cpp \ +../../../src/client/renderer/entity/EntityRenderDispatcher.cpp \ +../../../src/client/renderer/entity/EntityRenderer.cpp \ +../../../src/client/renderer/entity/HumanoidMobRenderer.cpp \ +../../../src/client/renderer/entity/ItemRenderer.cpp \ +../../../src/client/renderer/entity/ItemSpriteRenderer.cpp \ +../../../src/client/renderer/entity/MobRenderer.cpp \ +../../../src/client/renderer/entity/PaintingRenderer.cpp \ +../../../src/client/renderer/entity/PlayerRenderer.cpp \ +../../../src/client/renderer/entity/SheepRenderer.cpp \ +../../../src/client/renderer/entity/TntRenderer.cpp \ +../../../src/client/renderer/entity/TripodCameraRenderer.cpp \ +../../../src/client/renderer/ptexture/DynamicTexture.cpp \ +../../../src/client/renderer/tileentity/ChestRenderer.cpp \ +../../../src/client/renderer/tileentity/SignRenderer.cpp \ +../../../src/client/renderer/tileentity/TileEntityRenderDispatcher.cpp \ +../../../src/client/renderer/tileentity/TileEntityRenderer.cpp \ +../../../src/client/sound/Sound.cpp \ +../../../src/client/sound/SoundEngine.cpp \ +../../../src/locale/I18n.cpp \ +../../../src/nbt/Tag.cpp \ +../../../src/network/command/CommandServer.cpp \ +../../../src/network/ClientSideNetworkHandler.cpp \ +../../../src/network/NetEventCallback.cpp \ +../../../src/network/Packet.cpp \ +../../../src/network/RakNetInstance.cpp \ +../../../src/network/ServerSideNetworkHandler.cpp \ +../../../src/server/ServerLevel.cpp \ +../../../src/server/ServerPlayer.cpp \ +../../../src/util/DataIO.cpp \ +../../../src/util/Mth.cpp \ +../../../src/util/StringUtils.cpp \ +../../../src/util/PerfTimer.cpp \ +../../../src/util/PerfRenderer.cpp \ +../../../src/world/Direction.cpp \ +../../../src/world/entity/AgableMob.cpp \ +../../../src/world/entity/Entity.cpp \ +../../../src/world/entity/EntityFactory.cpp \ +../../../src/world/entity/FlyingMob.cpp \ +../../../src/world/entity/HangingEntity.cpp \ +../../../src/world/entity/Mob.cpp \ +../../../src/world/entity/MobCategory.cpp \ +../../../src/world/entity/Motive.cpp \ +../../../src/world/entity/Painting.cpp \ +../../../src/world/entity/PathfinderMob.cpp \ +../../../src/world/entity/SynchedEntityData.cpp \ +../../../src/world/entity/ai/control/MoveControl.cpp \ +../../../src/world/entity/animal/Animal.cpp \ +../../../src/world/entity/animal/Chicken.cpp \ +../../../src/world/entity/animal/Cow.cpp \ +../../../src/world/entity/animal/Pig.cpp \ +../../../src/world/entity/animal/Sheep.cpp \ +../../../src/world/entity/animal/WaterAnimal.cpp \ +../../../src/world/entity/item/FallingTile.cpp \ +../../../src/world/entity/item/ItemEntity.cpp \ +../../../src/world/entity/item/PrimedTnt.cpp \ +../../../src/world/entity/item/TripodCamera.cpp \ +../../../src/world/entity/monster/Creeper.cpp \ +../../../src/world/entity/monster/Monster.cpp \ +../../../src/world/entity/monster/PigZombie.cpp \ +../../../src/world/entity/monster/Skeleton.cpp \ +../../../src/world/entity/monster/Spider.cpp \ +../../../src/world/entity/monster/Zombie.cpp \ +../../../src/world/entity/projectile/Arrow.cpp \ +../../../src/world/entity/projectile/Throwable.cpp \ +../../../src/world/entity/player/Inventory.cpp \ +../../../src/world/entity/player/Player.cpp \ +../../../src/world/food/SimpleFoodData.cpp \ +../../../src/world/inventory/BaseContainerMenu.cpp \ +../../../src/world/inventory/ContainerMenu.cpp \ +../../../src/world/inventory/FillingContainer.cpp \ +../../../src/world/inventory/FurnaceMenu.cpp \ +../../../src/world/item/BedItem.cpp \ +../../../src/world/item/DyePowderItem.cpp \ +../../../src/world/item/Item.cpp \ +../../../src/world/item/ItemInstance.cpp \ +../../../src/world/item/HangingEntityItem.cpp \ +../../../src/world/item/HatchetItem.cpp \ +../../../src/world/item/HoeItem.cpp \ +../../../src/world/item/PickaxeItem.cpp \ +../../../src/world/item/ShovelItem.cpp \ +../../../src/world/item/crafting/Recipe.cpp \ +../../../src/world/item/crafting/Recipes.cpp \ +../../../src/world/item/crafting/FurnaceRecipes.cpp \ +../../../src/world/item/crafting/OreRecipes.cpp \ +../../../src/world/item/crafting/StructureRecipes.cpp \ +../../../src/world/item/crafting/ToolRecipes.cpp \ +../../../src/world/item/crafting/WeaponRecipes.cpp \ +../../../src/world/level/Explosion.cpp \ +../../../src/world/level/Level.cpp \ +../../../src/world/level/LightLayer.cpp \ +../../../src/world/level/LightUpdate.cpp \ +../../../src/world/level/MobSpawner.cpp \ +../../../src/world/level/Region.cpp \ +../../../src/world/level/TickNextTickData.cpp \ +../../../src/world/level/biome/Biome.cpp \ +../../../src/world/level/biome/BiomeSource.cpp \ +../../../src/world/level/chunk/LevelChunk.cpp \ +../../../src/world/level/dimension/Dimension.cpp \ +../../../src/world/level/levelgen/CanyonFeature.cpp \ +../../../src/world/level/levelgen/DungeonFeature.cpp \ +../../../src/world/level/levelgen/LargeCaveFeature.cpp \ +../../../src/world/level/levelgen/LargeFeature.cpp \ +../../../src/world/level/levelgen/RandomLevelSource.cpp \ +../../../src/world/level/levelgen/feature/Feature.cpp \ +../../../src/world/level/levelgen/synth/ImprovedNoise.cpp \ +../../../src/world/level/levelgen/synth/PerlinNoise.cpp \ +../../../src/world/level/levelgen/synth/Synth.cpp \ +../../../src/world/level/material/Material.cpp \ +../../../src/world/level/pathfinder/Path.cpp \ +../../../src/world/level/storage/ExternalFileLevelStorage.cpp \ +../../../src/world/level/storage/ExternalFileLevelStorageSource.cpp \ +../../../src/world/level/storage/FolderMethods.cpp \ +../../../src/world/level/storage/LevelData.cpp \ +../../../src/world/level/storage/LevelStorageSource.cpp \ +../../../src/world/level/storage/RegionFile.cpp \ +../../../src/world/level/tile/BedTile.cpp \ +../../../src/world/level/tile/ChestTile.cpp \ +../../../src/world/level/tile/CropTile.cpp \ +../../../src/world/level/tile/DoorTile.cpp \ +../../../src/world/level/tile/EntityTile.cpp \ +../../../src/world/level/tile/FurnaceTile.cpp \ +../../../src/world/level/tile/GrassTile.cpp \ +../../../src/world/level/tile/LightGemTile.cpp \ +../../../src/world/level/tile/MelonTile.cpp \ +../../../src/world/level/tile/Mushroom.cpp \ +../../../src/world/level/tile/NetherReactor.cpp \ +../../../src/world/level/tile/NetherReactorPattern.cpp \ +../../../src/world/level/tile/SandTile.cpp \ +../../../src/world/level/tile/StemTile.cpp \ +../../../src/world/level/tile/StoneSlabTile.cpp \ +../../../src/world/level/tile/TallGrass.cpp \ +../../../src/world/level/tile/Tile.cpp \ +../../../src/world/level/tile/TrapDoorTile.cpp \ +../../../src/world/level/tile/entity/ChestTileEntity.cpp \ +../../../src/world/level/tile/entity/NetherReactorTileEntity.cpp \ +../../../src/world/level/tile/entity/SignTileEntity.cpp \ +../../../src/world/level/tile/entity/TileEntity.cpp \ +../../../src/world/level/tile/entity/FurnaceTileEntity.cpp \ +../../../src/world/phys/HitResult.cpp + +LOCAL_CFLAGS := -Wno-psabi $(LOCAL_CFLAGS) + +LOCAL_CFLAGS := -DPRE_ANDROID23 -DANDROID_PUBLISH $(LOCAL_CFLAGS) +#LOCAL_CFLAGS := -DPRE_ANDROID23 -DANDROID_PUBLISH -DDEMO_MODE $(LOCAL_CFLAGS) + +#LOCAL_CFLAGS := -DPRE_ANDROID23 -DDEMO_MODE $(LOCAL_CFLAGS) +#LOCAL_CFLAGS := -DPRE_ANDROID23 $(LOCAL_CFLAGS) + +LOCAL_LDLIBS := -llog -lEGL -lGLESv1_CM + +LOCAL_STATIC_LIBRARIES := RakNet + +TARGET_ARCH_ABI := armeabi-v7a + +include $(BUILD_SHARED_LIBRARY) + +# NOTE: environment var NDK_MODULE_PATH needs to point to lib_projects folder +$(call import-module, raknet/jni) + diff --git a/project/android_java/proguard.cfg b/project/android_java/proguard.cfg index 6a15b3c..f960f08 100755 --- a/project/android_java/proguard.cfg +++ b/project/android_java/proguard.cfg @@ -1,66 +1,66 @@ --optimizationpasses 5 --dontusemixedcaseclassnames --dontskipnonpubliclibraryclasses --dontpreverify --verbose --optimizations !code/simplification/arithmetic,!field/*,!class/merging/* - --keep public class com.mojang.minecraftpe.GameModeButton --keep public class com.mojang.android.StringValue --keep public class * extends android.app.Activity --keep public class * extends com.mojang.minecraftpe.MainActivity --keep public class * extends MainActivity --keep public class * extends android.app.Application --keep public class * extends android.app.Service --keep public class * extends android.content.BroadcastReceiver --keep public class * extends android.content.ContentProvider --keep public class com.android.vending.licensing.ILicensingService --keep,allowobfuscation class com.mojang.** { *** *(...); } - --keepclasseswithmembernames class * { - native ; -} - --keepclasseswithmembernames class * { - public (android.content.Context, android.util.AttributeSet); -} - --keepclasseswithmembernames class * { - public (android.content.Context, android.util.AttributeSet, int); -} - --keepclassmembers enum * { - public static **[] values(); - public static ** valueOf(java.lang.String); -} - --keepclassmembers class * extends android.app.Activity { - static public void saveScreenshot(java.lang.String, int, int, int[]); - public int[] getImageData(java.lang.String); - public byte[] getFileDataBytes(java.lang.String); - public java.lang.String getPlatformStringVar(int); - public int getScreenWidth(); - public int getScreenHeight(); - public float getPixelsPerMillimeter(); - public int checkLicense(); - public boolean isNetworkEnabled(boolean); - public java.lang.String getDateString(int); - public boolean hasBuyButtonWhenInvalidLicense(); - public void postScreenshotToFacebook(java.lang.String, int, int, int[]); - public void quit(); - public void displayDialog(int); - public void tick(); - public java.lang.String[] getOptionStrings(); - public void buyGame(); - public void playSound(java.lang.String, float, float); - public boolean isTouchscreen(); - public void setIsPowerVR(boolean); - public void initiateUserInput(int); - public int getUserInputStatus(); - public java.lang.String[] getUserInputString(); - public void vibrate(int); -} - --keep class * implements android.os.Parcelable { - public static final android.os.Parcelable$Creator *; -} +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class com.mojang.minecraftpe.GameModeButton +-keep public class com.mojang.android.StringValue +-keep public class * extends android.app.Activity +-keep public class * extends com.mojang.minecraftpe.MainActivity +-keep public class * extends MainActivity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class com.android.vending.licensing.ILicensingService +-keep,allowobfuscation class com.mojang.** { *** *(...); } + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keepclassmembers class * extends android.app.Activity { + static public void saveScreenshot(java.lang.String, int, int, int[]); + public int[] getImageData(java.lang.String); + public byte[] getFileDataBytes(java.lang.String); + public java.lang.String getPlatformStringVar(int); + public int getScreenWidth(); + public int getScreenHeight(); + public float getPixelsPerMillimeter(); + public int checkLicense(); + public boolean isNetworkEnabled(boolean); + public java.lang.String getDateString(int); + public boolean hasBuyButtonWhenInvalidLicense(); + public void postScreenshotToFacebook(java.lang.String, int, int, int[]); + public void quit(); + public void displayDialog(int); + public void tick(); + public java.lang.String[] getOptionStrings(); + public void buyGame(); + public void playSound(java.lang.String, float, float); + public boolean isTouchscreen(); + public void setIsPowerVR(boolean); + public void initiateUserInput(int); + public int getUserInputStatus(); + public java.lang.String[] getUserInputString(); + public void vibrate(int); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/project/android_java/res/drawable/bgtiled.xml b/project/android_java/res/drawable/bgtiled.xml index 006dcf2..4935bb1 100755 --- a/project/android_java/res/drawable/bgtiled.xml +++ b/project/android_java/res/drawable/bgtiled.xml @@ -1,4 +1,4 @@ - \ No newline at end of file diff --git a/project/android_java/res/drawable/btn_nw_cancel.xml b/project/android_java/res/drawable/btn_nw_cancel.xml index a297fcd..5b43e9e 100755 --- a/project/android_java/res/drawable/btn_nw_cancel.xml +++ b/project/android_java/res/drawable/btn_nw_cancel.xml @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/project/android_java/res/drawable/btn_nw_create.xml b/project/android_java/res/drawable/btn_nw_create.xml index c9e68f0..8790435 100755 --- a/project/android_java/res/drawable/btn_nw_create.xml +++ b/project/android_java/res/drawable/btn_nw_create.xml @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/project/android_java/res/drawable/btngamemode.xml b/project/android_java/res/drawable/btngamemode.xml index 4a6e21c..b8b7f4f 100755 --- a/project/android_java/res/drawable/btngamemode.xml +++ b/project/android_java/res/drawable/btngamemode.xml @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/project/android_java/res/layout/create_new_world.xml b/project/android_java/res/layout/create_new_world.xml index e2ab23e..795adf8 100755 --- a/project/android_java/res/layout/create_new_world.xml +++ b/project/android_java/res/layout/create_new_world.xml @@ -1,177 +1,177 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/project/android_java/res/layout/mainmenu_options.xml b/project/android_java/res/layout/mainmenu_options.xml index 13cfdf4..3ae67c6 100755 --- a/project/android_java/res/layout/mainmenu_options.xml +++ b/project/android_java/res/layout/mainmenu_options.xml @@ -1,17 +1,17 @@ - - - - - - + + + + + + diff --git a/project/android_java/res/layout/rename_mp_world.xml b/project/android_java/res/layout/rename_mp_world.xml index 2c7376e..375d529 100755 --- a/project/android_java/res/layout/rename_mp_world.xml +++ b/project/android_java/res/layout/rename_mp_world.xml @@ -1,28 +1,28 @@ - - - - - - + + + + + + diff --git a/project/android_java/res/values/strings.xml b/project/android_java/res/values/strings.xml index 356de7b..ed64e6b 100755 --- a/project/android_java/res/values/strings.xml +++ b/project/android_java/res/values/strings.xml @@ -1,40 +1,40 @@ - - - Minecraft - Pocket Edition - Minecraft - Pocket Edition Demo + + + Minecraft - Pocket Edition + Minecraft - Pocket Edition Demo true - Minecraft PE - Minecraft PE Demo - - - World name - Unnamed world - Seed for the World Generator - Leave blank for random seed - Mobs, health and gather resources - Unlimited resources and flying - - - Save world as - Saved World - - - Username - Enter your username - Server visible by default - Sensitivity - Invert Y-axis - Lefty - Use touch screen - Split touch controls - Vibration - Slight vibration when blocks are destroyed - Fancy graphics - - - Lower graphics quality - Shorter view distance and disables fancy rendering - Peaceful mode - No hostile mobs, heal automatically - - + Minecraft PE + Minecraft PE Demo + + + World name + Unnamed world + Seed for the World Generator + Leave blank for random seed + Mobs, health and gather resources + Unlimited resources and flying + + + Save world as + Saved World + + + Username + Enter your username + Server visible by default + Sensitivity + Invert Y-axis + Lefty + Use touch screen + Split touch controls + Vibration + Slight vibration when blocks are destroyed + Fancy graphics + + + Lower graphics quality + Shorter view distance and disables fancy rendering + Peaceful mode + No hostile mobs, heal automatically + + diff --git a/project/android_java/res/xml/preferences.xml b/project/android_java/res/xml/preferences.xml index fde2552..b4dfe42 100755 --- a/project/android_java/res/xml/preferences.xml +++ b/project/android_java/res/xml/preferences.xml @@ -1,105 +1,105 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/project/android_java/src/com/mojang/minecraftpe/sound/SoundPlayer.java b/project/android_java/src/com/mojang/minecraftpe/sound/SoundPlayer.java index f3686c7..0a9f5d3 100755 --- a/project/android_java/src/com/mojang/minecraftpe/sound/SoundPlayer.java +++ b/project/android_java/src/com/mojang/minecraftpe/sound/SoundPlayer.java @@ -1,221 +1,221 @@ -package com.mojang.minecraftpe.sound; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import android.content.Context; -import android.media.SoundPool; - -import com.mojang.minecraftpe.R; - -public class SoundPlayer -{ - public SoundPlayer(Context c, int streamType) { - _context = c; - _stream = streamType; - - _random = new Random(); - _pool = new SoundPool(4, _stream, 0); - - _init(); - } - - private void _init() { - // Sounds that needs to be preloaded (although asynchronous :p) - SoundId[] preLoaded = { - new SoundId(R.raw.click, "random.click"), - new SoundId(R.raw.explode, "random.explode"), - new SoundId(R.raw.splash, "random.splash"), - new SoundId(R.raw.hurt, "random.hurt"), - new SoundId(R.raw.pop, "random.pop"), - - new SoundId(R.raw.door_open, "random.door_open"), - new SoundId(R.raw.door_close, "random.door_close"), - - new SoundId(R.raw.cloth1, "step.cloth"), - new SoundId(R.raw.cloth2, "step.cloth"), - new SoundId(R.raw.cloth3, "step.cloth"), - new SoundId(R.raw.cloth4, "step.cloth"), - - new SoundId(R.raw.glass1, "random.glass"), - new SoundId(R.raw.glass2, "random.glass"), - new SoundId(R.raw.glass3, "random.glass"), - - new SoundId(R.raw.grass1, "step.grass"), - new SoundId(R.raw.grass2, "step.grass"), - new SoundId(R.raw.grass3, "step.grass"), - new SoundId(R.raw.grass4, "step.grass"), - - //new SoundId(R.raw.gravel1, "step.gravel"), - new SoundId(R.raw.gravel2, "step.gravel"), - new SoundId(R.raw.gravel3, "step.gravel"), - new SoundId(R.raw.gravel4, "step.gravel"), - - new SoundId(R.raw.sand1, "step.sand"), - new SoundId(R.raw.sand2, "step.sand"), - new SoundId(R.raw.sand3, "step.sand"), - new SoundId(R.raw.sand4, "step.sand"), - - new SoundId(R.raw.stone1, "step.stone"), - new SoundId(R.raw.stone2, "step.stone"), - new SoundId(R.raw.stone3, "step.stone"), - new SoundId(R.raw.stone4, "step.stone"), - - new SoundId(R.raw.wood1, "step.wood"), - new SoundId(R.raw.wood2, "step.wood"), - new SoundId(R.raw.wood3, "step.wood"), - new SoundId(R.raw.wood4, "step.wood"), - - new SoundId(R.raw.sheep1, "mob.sheep"), - new SoundId(R.raw.sheep2, "mob.sheep"), - new SoundId(R.raw.sheep3, "mob.sheep"), - - new SoundId(R.raw.chicken2, "mob.chicken"), - new SoundId(R.raw.chicken3, "mob.chicken"), - new SoundId(R.raw.chickenhurt1, "mob.chickenhurt"), - new SoundId(R.raw.chickenhurt2, "mob.chickenhurt"), - - new SoundId(R.raw.cow1, "mob.cow"), - new SoundId(R.raw.cow2, "mob.cow"), - new SoundId(R.raw.cow3, "mob.cow"), - new SoundId(R.raw.cow4, "mob.cow"), - new SoundId(R.raw.cowhurt1, "mob.cowhurt"), - new SoundId(R.raw.cowhurt2, "mob.cowhurt"), - - new SoundId(R.raw.pig1, "mob.pig"), - new SoundId(R.raw.pig2, "mob.pig"), - new SoundId(R.raw.pig3, "mob.pig"), - new SoundId(R.raw.pigdeath, "mob.pigdeath"), - - new SoundId(R.raw.zombie1, "mob.zombie"), - new SoundId(R.raw.zombie2, "mob.zombie"), - new SoundId(R.raw.zombie3, "mob.zombie"), - new SoundId(R.raw.zombiedeath, "mob.zombiedeath"), - new SoundId(R.raw.zombiehurt1, "mob.zombiehurt"), - new SoundId(R.raw.zombiehurt2, "mob.zombiehurt"), - - new SoundId(R.raw.skeleton1, "mob.skeleton"), - new SoundId(R.raw.skeleton2, "mob.skeleton"), - new SoundId(R.raw.skeleton3, "mob.skeleton"), - new SoundId(R.raw.skeletonhurt1, "mob.skeletonhurt"), - new SoundId(R.raw.skeletonhurt2, "mob.skeletonhurt"), - new SoundId(R.raw.skeletonhurt3, "mob.skeletonhurt"), - new SoundId(R.raw.skeletonhurt4, "mob.skeletonhurt"), - - new SoundId(R.raw.spider1, "mob.spider"), - new SoundId(R.raw.spider2, "mob.spider"), - new SoundId(R.raw.spider3, "mob.spider"), - new SoundId(R.raw.spider4, "mob.spider"), - new SoundId(R.raw.spiderdeath, "mob.spiderdeath"), - - new SoundId(R.raw.fallbig1, "damage.fallbig"), - new SoundId(R.raw.fallbig2, "damage.fallbig"), - new SoundId(R.raw.fallsmall, "damage.fallsmall"), - - new SoundId(R.raw.bow, "random.bow"), - new SoundId(R.raw.bowhit1, "random.bowhit"), - new SoundId(R.raw.bowhit2, "random.bowhit"), - new SoundId(R.raw.bowhit3, "random.bowhit"), - new SoundId(R.raw.bowhit4, "random.bowhit"), - - new SoundId(R.raw.creeper1, "mob.creeper"), - new SoundId(R.raw.creeper2, "mob.creeper"), - new SoundId(R.raw.creeper3, "mob.creeper"), - new SoundId(R.raw.creeper4, "mob.creeper"), - new SoundId(R.raw.creeperdeath, "mob.creeperdeath"), - new SoundId(R.raw.eat1, "random.eat"), - new SoundId(R.raw.eat2, "random.eat"), - new SoundId(R.raw.eat3, "random.eat"), - new SoundId(R.raw.fuse, "random.fuse"), - - new SoundId(R.raw.zpig1, "mob.zombiepig.zpig"), - new SoundId(R.raw.zpig2, "mob.zombiepig.zpig"), - new SoundId(R.raw.zpig3, "mob.zombiepig.zpig"), - new SoundId(R.raw.zpig4, "mob.zombiepig.zpig"), - new SoundId(R.raw.zpigangry1, "mob.zombiepig.zpigangry"), - new SoundId(R.raw.zpigangry2, "mob.zombiepig.zpigangry"), - new SoundId(R.raw.zpigangry3, "mob.zombiepig.zpigangry"), - new SoundId(R.raw.zpigangry4, "mob.zombiepig.zpigangry"), - new SoundId(R.raw.zpigdeath, "mob.zombiepig.zpigdeath"), - new SoundId(R.raw.zpighurt1, "mob.zombiepig.zpighurt"), - new SoundId(R.raw.zpighurt2, "mob.zombiepig.zpighurt"), - }; - - for (SoundId s: preLoaded) { - load(s.name, s.soundId); - } - // Sounds that are loaded in a separate thread - } - -// public void loadWithAlias(String filename, String alias) { -// int id = loadRaw(filename); -// addRaw(new SoundId(id, alias)); -// } - - public void play(String s, float volume, float pitch) { - SoundId sound = get(s);//load(s); - if (sound != null) { - volume *= getCurrentStreamVolume(); - //System.out.println("playing sound id: " + sound.soundId); - if (s.equals("step.sand") || s.equals("step.gravel")) volume *= 0.5f; - _pool.play(sound.soundId, volume, volume, 0, 0, pitch); - } - } - - private SoundId get(String s) { - List sounds = _sounds.get( s ); - if (sounds == null) return null; - - return sounds.get(_random.nextInt(sounds.size())); - } - - public float getCurrentStreamVolume() { - return 2.5f; - //AudioManager mgr = (AudioManager)_context.getSystemService(Context.AUDIO_SERVICE); - //return ((float)mgr.getStreamVolume(_stream)) / mgr.getStreamMaxVolume(_stream); - } - - synchronized public SoundId load(String id, int resId) { - if (id == null) - return null; - - List sounds = _sounds.get(id); - SoundId soundId = null; - - if (sounds == null) { - sounds = new ArrayList(); - _sounds.put(id, sounds); - } - - for (SoundId sid : sounds) - if (sid.soundId == resId) - return sid; - - int snd = _pool.load(_context, resId, DefaultPriority); - soundId = new SoundId(snd, id); - sounds.add(soundId); - - return soundId; - } - - private Context _context; - private Random _random; - private int _stream; - private SoundPool _pool; - - private Map> _sounds = new HashMap>(); - - static private final int DefaultPriority = 1; - - public class SoundId { - SoundId(int soundId, String name) { - this.soundId = soundId; - this.name = name; - } - int soundId; - String name; - } -} +package com.mojang.minecraftpe.sound; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import android.content.Context; +import android.media.SoundPool; + +import com.mojang.minecraftpe.R; + +public class SoundPlayer +{ + public SoundPlayer(Context c, int streamType) { + _context = c; + _stream = streamType; + + _random = new Random(); + _pool = new SoundPool(4, _stream, 0); + + _init(); + } + + private void _init() { + // Sounds that needs to be preloaded (although asynchronous :p) + SoundId[] preLoaded = { + new SoundId(R.raw.click, "random.click"), + new SoundId(R.raw.explode, "random.explode"), + new SoundId(R.raw.splash, "random.splash"), + new SoundId(R.raw.hurt, "random.hurt"), + new SoundId(R.raw.pop, "random.pop"), + + new SoundId(R.raw.door_open, "random.door_open"), + new SoundId(R.raw.door_close, "random.door_close"), + + new SoundId(R.raw.cloth1, "step.cloth"), + new SoundId(R.raw.cloth2, "step.cloth"), + new SoundId(R.raw.cloth3, "step.cloth"), + new SoundId(R.raw.cloth4, "step.cloth"), + + new SoundId(R.raw.glass1, "random.glass"), + new SoundId(R.raw.glass2, "random.glass"), + new SoundId(R.raw.glass3, "random.glass"), + + new SoundId(R.raw.grass1, "step.grass"), + new SoundId(R.raw.grass2, "step.grass"), + new SoundId(R.raw.grass3, "step.grass"), + new SoundId(R.raw.grass4, "step.grass"), + + //new SoundId(R.raw.gravel1, "step.gravel"), + new SoundId(R.raw.gravel2, "step.gravel"), + new SoundId(R.raw.gravel3, "step.gravel"), + new SoundId(R.raw.gravel4, "step.gravel"), + + new SoundId(R.raw.sand1, "step.sand"), + new SoundId(R.raw.sand2, "step.sand"), + new SoundId(R.raw.sand3, "step.sand"), + new SoundId(R.raw.sand4, "step.sand"), + + new SoundId(R.raw.stone1, "step.stone"), + new SoundId(R.raw.stone2, "step.stone"), + new SoundId(R.raw.stone3, "step.stone"), + new SoundId(R.raw.stone4, "step.stone"), + + new SoundId(R.raw.wood1, "step.wood"), + new SoundId(R.raw.wood2, "step.wood"), + new SoundId(R.raw.wood3, "step.wood"), + new SoundId(R.raw.wood4, "step.wood"), + + new SoundId(R.raw.sheep1, "mob.sheep"), + new SoundId(R.raw.sheep2, "mob.sheep"), + new SoundId(R.raw.sheep3, "mob.sheep"), + + new SoundId(R.raw.chicken2, "mob.chicken"), + new SoundId(R.raw.chicken3, "mob.chicken"), + new SoundId(R.raw.chickenhurt1, "mob.chickenhurt"), + new SoundId(R.raw.chickenhurt2, "mob.chickenhurt"), + + new SoundId(R.raw.cow1, "mob.cow"), + new SoundId(R.raw.cow2, "mob.cow"), + new SoundId(R.raw.cow3, "mob.cow"), + new SoundId(R.raw.cow4, "mob.cow"), + new SoundId(R.raw.cowhurt1, "mob.cowhurt"), + new SoundId(R.raw.cowhurt2, "mob.cowhurt"), + + new SoundId(R.raw.pig1, "mob.pig"), + new SoundId(R.raw.pig2, "mob.pig"), + new SoundId(R.raw.pig3, "mob.pig"), + new SoundId(R.raw.pigdeath, "mob.pigdeath"), + + new SoundId(R.raw.zombie1, "mob.zombie"), + new SoundId(R.raw.zombie2, "mob.zombie"), + new SoundId(R.raw.zombie3, "mob.zombie"), + new SoundId(R.raw.zombiedeath, "mob.zombiedeath"), + new SoundId(R.raw.zombiehurt1, "mob.zombiehurt"), + new SoundId(R.raw.zombiehurt2, "mob.zombiehurt"), + + new SoundId(R.raw.skeleton1, "mob.skeleton"), + new SoundId(R.raw.skeleton2, "mob.skeleton"), + new SoundId(R.raw.skeleton3, "mob.skeleton"), + new SoundId(R.raw.skeletonhurt1, "mob.skeletonhurt"), + new SoundId(R.raw.skeletonhurt2, "mob.skeletonhurt"), + new SoundId(R.raw.skeletonhurt3, "mob.skeletonhurt"), + new SoundId(R.raw.skeletonhurt4, "mob.skeletonhurt"), + + new SoundId(R.raw.spider1, "mob.spider"), + new SoundId(R.raw.spider2, "mob.spider"), + new SoundId(R.raw.spider3, "mob.spider"), + new SoundId(R.raw.spider4, "mob.spider"), + new SoundId(R.raw.spiderdeath, "mob.spiderdeath"), + + new SoundId(R.raw.fallbig1, "damage.fallbig"), + new SoundId(R.raw.fallbig2, "damage.fallbig"), + new SoundId(R.raw.fallsmall, "damage.fallsmall"), + + new SoundId(R.raw.bow, "random.bow"), + new SoundId(R.raw.bowhit1, "random.bowhit"), + new SoundId(R.raw.bowhit2, "random.bowhit"), + new SoundId(R.raw.bowhit3, "random.bowhit"), + new SoundId(R.raw.bowhit4, "random.bowhit"), + + new SoundId(R.raw.creeper1, "mob.creeper"), + new SoundId(R.raw.creeper2, "mob.creeper"), + new SoundId(R.raw.creeper3, "mob.creeper"), + new SoundId(R.raw.creeper4, "mob.creeper"), + new SoundId(R.raw.creeperdeath, "mob.creeperdeath"), + new SoundId(R.raw.eat1, "random.eat"), + new SoundId(R.raw.eat2, "random.eat"), + new SoundId(R.raw.eat3, "random.eat"), + new SoundId(R.raw.fuse, "random.fuse"), + + new SoundId(R.raw.zpig1, "mob.zombiepig.zpig"), + new SoundId(R.raw.zpig2, "mob.zombiepig.zpig"), + new SoundId(R.raw.zpig3, "mob.zombiepig.zpig"), + new SoundId(R.raw.zpig4, "mob.zombiepig.zpig"), + new SoundId(R.raw.zpigangry1, "mob.zombiepig.zpigangry"), + new SoundId(R.raw.zpigangry2, "mob.zombiepig.zpigangry"), + new SoundId(R.raw.zpigangry3, "mob.zombiepig.zpigangry"), + new SoundId(R.raw.zpigangry4, "mob.zombiepig.zpigangry"), + new SoundId(R.raw.zpigdeath, "mob.zombiepig.zpigdeath"), + new SoundId(R.raw.zpighurt1, "mob.zombiepig.zpighurt"), + new SoundId(R.raw.zpighurt2, "mob.zombiepig.zpighurt"), + }; + + for (SoundId s: preLoaded) { + load(s.name, s.soundId); + } + // Sounds that are loaded in a separate thread + } + +// public void loadWithAlias(String filename, String alias) { +// int id = loadRaw(filename); +// addRaw(new SoundId(id, alias)); +// } + + public void play(String s, float volume, float pitch) { + SoundId sound = get(s);//load(s); + if (sound != null) { + volume *= getCurrentStreamVolume(); + //System.out.println("playing sound id: " + sound.soundId); + if (s.equals("step.sand") || s.equals("step.gravel")) volume *= 0.5f; + _pool.play(sound.soundId, volume, volume, 0, 0, pitch); + } + } + + private SoundId get(String s) { + List sounds = _sounds.get( s ); + if (sounds == null) return null; + + return sounds.get(_random.nextInt(sounds.size())); + } + + public float getCurrentStreamVolume() { + return 2.5f; + //AudioManager mgr = (AudioManager)_context.getSystemService(Context.AUDIO_SERVICE); + //return ((float)mgr.getStreamVolume(_stream)) / mgr.getStreamMaxVolume(_stream); + } + + synchronized public SoundId load(String id, int resId) { + if (id == null) + return null; + + List sounds = _sounds.get(id); + SoundId soundId = null; + + if (sounds == null) { + sounds = new ArrayList(); + _sounds.put(id, sounds); + } + + for (SoundId sid : sounds) + if (sid.soundId == resId) + return sid; + + int snd = _pool.load(_context, resId, DefaultPriority); + soundId = new SoundId(snd, id); + sounds.add(soundId); + + return soundId; + } + + private Context _context; + private Random _random; + private int _stream; + private SoundPool _pool; + + private Map> _sounds = new HashMap>(); + + static private final int DefaultPriority = 1; + + public class SoundId { + SoundId(int soundId, String name) { + this.soundId = soundId; + this.name = name; + } + int soundId; + String name; + } +} diff --git a/project/lib_projects/raknet/jni/Android.mk b/project/lib_projects/raknet/jni/Android.mk index f50b99a..e347b1a 100755 --- a/project/lib_projects/raknet/jni/Android.mk +++ b/project/lib_projects/raknet/jni/Android.mk @@ -1,14 +1,14 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := RakNet - -MY_PREFIX := $(LOCAL_PATH)/RaknetSources/ -MY_SOURCES := $(wildcard $(MY_PREFIX)*.cpp) -LOCAL_SRC_FILES += $(MY_SOURCES:$(MY_PREFIX)%=RaknetSources/%) - -LOCAL_CFLAGS := -Wno-psabi $(LOCAL_CFLAGS) -LOCAL_CPPFLAGS += -frtti - -include $(BUILD_STATIC_LIBRARY) +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := RakNet + +MY_PREFIX := $(LOCAL_PATH)/RaknetSources/ +MY_SOURCES := $(wildcard $(MY_PREFIX)*.cpp) +LOCAL_SRC_FILES += $(MY_SOURCES:$(MY_PREFIX)%=RaknetSources/%) + +LOCAL_CFLAGS := -Wno-psabi $(LOCAL_CFLAGS) +LOCAL_CPPFLAGS += -frtti + +include $(BUILD_STATIC_LIBRARY) diff --git a/project/lib_projects/raknet/jni/Application.mk b/project/lib_projects/raknet/jni/Application.mk index ec8b9b3..d276a6f 100755 --- a/project/lib_projects/raknet/jni/Application.mk +++ b/project/lib_projects/raknet/jni/Application.mk @@ -1,4 +1,4 @@ -APP_PLATFORM := android-8 -APP_STL := stlport_static -APP_OPTIM := release +APP_PLATFORM := android-8 +APP_STL := stlport_static +APP_OPTIM := release APP_ABI := armeabi-v7a \ No newline at end of file diff --git a/project/lib_projects/raknet/jni/RaknetSources/AutopatcherPatchContext.h b/project/lib_projects/raknet/jni/RaknetSources/AutopatcherPatchContext.h index 4fae91a..ab5d86b 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/AutopatcherPatchContext.h +++ b/project/lib_projects/raknet/jni/RaknetSources/AutopatcherPatchContext.h @@ -1,23 +1,23 @@ -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// Usage of RakNet is subject to the appropriate license agreement. -/// - -#ifndef __AUTOPATCHER_PATCH_CONTEXT_H -#define __AUTOPATCHER_PATCH_CONTEXT_H - -enum PatchContext -{ - PC_HASH_1_WITH_PATCH, - PC_HASH_2_WITH_PATCH, - PC_WRITE_FILE, - PC_ERROR_FILE_WRITE_FAILURE, - PC_ERROR_PATCH_TARGET_MISSING, - PC_ERROR_PATCH_APPLICATION_FAILURE, - PC_ERROR_PATCH_RESULT_CHECKSUM_FAILURE, - PC_NOTICE_WILL_COPY_ON_RESTART, - PC_NOTICE_FILE_DOWNLOADED, - PC_NOTICE_FILE_DOWNLOADED_PATCH, -}; - -#endif +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// Usage of RakNet is subject to the appropriate license agreement. +/// + +#ifndef __AUTOPATCHER_PATCH_CONTEXT_H +#define __AUTOPATCHER_PATCH_CONTEXT_H + +enum PatchContext +{ + PC_HASH_1_WITH_PATCH, + PC_HASH_2_WITH_PATCH, + PC_WRITE_FILE, + PC_ERROR_FILE_WRITE_FAILURE, + PC_ERROR_PATCH_TARGET_MISSING, + PC_ERROR_PATCH_APPLICATION_FAILURE, + PC_ERROR_PATCH_RESULT_CHECKSUM_FAILURE, + PC_NOTICE_WILL_COPY_ON_RESTART, + PC_NOTICE_FILE_DOWNLOADED, + PC_NOTICE_FILE_DOWNLOADED_PATCH, +}; + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/AutopatcherRepositoryInterface.h b/project/lib_projects/raknet/jni/RaknetSources/AutopatcherRepositoryInterface.h index 303d321..d1ba5a3 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/AutopatcherRepositoryInterface.h +++ b/project/lib_projects/raknet/jni/RaknetSources/AutopatcherRepositoryInterface.h @@ -1,72 +1,72 @@ -/// -/// \file AutopatcherRepositoryInterface.h -/// \brief An interface used by AutopatcherServer to get the data necessary to run an autopatcher. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// Usage of RakNet is subject to the appropriate license agreement. -/// - -#ifndef __AUTOPATCHER_REPOSITORY_INTERFACE_H -#define __AUTOPATCHER_REPOSITORY_INTERFACE_H - -#include "IncrementalReadInterface.h" -#include "SimpleMutex.h" - -namespace RakNet -{ -/// Forward declarations -class FileList; -class BitStream; - -/// An interface used by AutopatcherServer to get the data necessary to run an autopatcher. This is up to you to implement for custom repository solutions. -class AutopatcherRepositoryInterface : public IncrementalReadInterface -{ -public: - /// Get list of files added and deleted since a certain date. This is used by AutopatcherServer and not usually explicitly called. - /// \param[in] applicationName A null terminated string identifying the application - /// \param[out] addedFiles A list of the current versions of filenames with hashes as their data that were created after \a sinceData - /// \param[out] deletedFiles A list of the current versions of filenames that were deleted after \a sinceData - /// \param[in] An input date, in whatever format your repository uses - /// \param[out] currentDate The current server date, in whatever format your repository uses - /// \return True on success, false on failure. - virtual bool GetChangelistSinceDate(const char *applicationName, FileList *addedFiles, FileList *deletedFiles, double sinceDate)=0; - - /// Get patches (or files) for every file in input, assuming that input has a hash for each of those files. - /// \param[in] applicationName A null terminated string identifying the application - /// \param[in] input A list of files with SHA1_LENGTH byte hashes to get from the database. - /// \param[out] patchList You should return list of files with either the filedata or the patch. This is a subset of \a input. The context data for each file will be either PC_WRITE_FILE (to just write the file) or PC_HASH_WITH_PATCH (to patch). If PC_HASH_WITH_PATCH, then the file contains a SHA1_LENGTH byte patch followed by the hash. The datalength is patchlength + SHA1_LENGTH - /// \param[out] currentDate The current server date, in whatever format your repository uses - /// \return True on success, false on failure. - virtual bool GetPatches(const char *applicationName, FileList *input, FileList *patchList)=0; - - /// For the most recent update, return files that were patched, added, or deleted. For files that were patched, return both the patch in \a patchedFiles and the current version in \a updatedFiles - /// The cache will be used if the client last patched between \a priorRowPatchTime and \a mostRecentRowPatchTime - /// No files changed will be returned to the client if the client last patched after mostRecentRowPatchTime - /// \param[in,out] applicationName Name of the application to get patches for. If empty, uses the most recently updated application, and the string will be updated to reflect this name. - /// \param[out] patchedFiles Given the most recent update, if a file was patched, add it to this list. The context data for each file will be PC_HASH_WITH_PATCH. The first 4 bytes of data should be a hash of the file being patched. The second 4 bytes should be the hash of the file after the patch. The remaining bytes should be the patch itself. - /// \param[out] updatedFiles The current value of the file. List should have the same length and order as \a patchedFiles - /// \param[out] updatedFileHashes The hash of the current value of the file. List should have the same length and order as \a patchedFiles - /// \param[out] deletedFiles Files that were deleted in the last patch. - /// \param[out] priorRowPatchTime When the patch before the most recent patch took place. 0 means never. - /// \param[out] mostRecentRowPatchTime When the most recent patch took place. 0 means never. - /// \return true on success, false on failure - virtual bool GetMostRecentChangelistWithPatches( - RakNet::RakString &applicationName, - FileList *patchedFiles, - FileList *updatedFiles, - FileList *updatedFileHashes, - FileList *deletedFiles, - double *priorRowPatchTime, - double *mostRecentRowPatchTime)=0; - - /// \return Whatever this function returns is sent from the AutopatcherServer to the AutopatcherClient when one of the above functions returns false. - virtual const char *GetLastError(void) const=0; - - /// \return Passed to FileListTransfer::Send() as the _chunkSize parameter. - virtual const int GetIncrementalReadChunkSize(void) const=0; -}; - -} // namespace RakNet - -#endif - +/// +/// \file AutopatcherRepositoryInterface.h +/// \brief An interface used by AutopatcherServer to get the data necessary to run an autopatcher. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// Usage of RakNet is subject to the appropriate license agreement. +/// + +#ifndef __AUTOPATCHER_REPOSITORY_INTERFACE_H +#define __AUTOPATCHER_REPOSITORY_INTERFACE_H + +#include "IncrementalReadInterface.h" +#include "SimpleMutex.h" + +namespace RakNet +{ +/// Forward declarations +class FileList; +class BitStream; + +/// An interface used by AutopatcherServer to get the data necessary to run an autopatcher. This is up to you to implement for custom repository solutions. +class AutopatcherRepositoryInterface : public IncrementalReadInterface +{ +public: + /// Get list of files added and deleted since a certain date. This is used by AutopatcherServer and not usually explicitly called. + /// \param[in] applicationName A null terminated string identifying the application + /// \param[out] addedFiles A list of the current versions of filenames with hashes as their data that were created after \a sinceData + /// \param[out] deletedFiles A list of the current versions of filenames that were deleted after \a sinceData + /// \param[in] An input date, in whatever format your repository uses + /// \param[out] currentDate The current server date, in whatever format your repository uses + /// \return True on success, false on failure. + virtual bool GetChangelistSinceDate(const char *applicationName, FileList *addedFiles, FileList *deletedFiles, double sinceDate)=0; + + /// Get patches (or files) for every file in input, assuming that input has a hash for each of those files. + /// \param[in] applicationName A null terminated string identifying the application + /// \param[in] input A list of files with SHA1_LENGTH byte hashes to get from the database. + /// \param[out] patchList You should return list of files with either the filedata or the patch. This is a subset of \a input. The context data for each file will be either PC_WRITE_FILE (to just write the file) or PC_HASH_WITH_PATCH (to patch). If PC_HASH_WITH_PATCH, then the file contains a SHA1_LENGTH byte patch followed by the hash. The datalength is patchlength + SHA1_LENGTH + /// \param[out] currentDate The current server date, in whatever format your repository uses + /// \return True on success, false on failure. + virtual bool GetPatches(const char *applicationName, FileList *input, FileList *patchList)=0; + + /// For the most recent update, return files that were patched, added, or deleted. For files that were patched, return both the patch in \a patchedFiles and the current version in \a updatedFiles + /// The cache will be used if the client last patched between \a priorRowPatchTime and \a mostRecentRowPatchTime + /// No files changed will be returned to the client if the client last patched after mostRecentRowPatchTime + /// \param[in,out] applicationName Name of the application to get patches for. If empty, uses the most recently updated application, and the string will be updated to reflect this name. + /// \param[out] patchedFiles Given the most recent update, if a file was patched, add it to this list. The context data for each file will be PC_HASH_WITH_PATCH. The first 4 bytes of data should be a hash of the file being patched. The second 4 bytes should be the hash of the file after the patch. The remaining bytes should be the patch itself. + /// \param[out] updatedFiles The current value of the file. List should have the same length and order as \a patchedFiles + /// \param[out] updatedFileHashes The hash of the current value of the file. List should have the same length and order as \a patchedFiles + /// \param[out] deletedFiles Files that were deleted in the last patch. + /// \param[out] priorRowPatchTime When the patch before the most recent patch took place. 0 means never. + /// \param[out] mostRecentRowPatchTime When the most recent patch took place. 0 means never. + /// \return true on success, false on failure + virtual bool GetMostRecentChangelistWithPatches( + RakNet::RakString &applicationName, + FileList *patchedFiles, + FileList *updatedFiles, + FileList *updatedFileHashes, + FileList *deletedFiles, + double *priorRowPatchTime, + double *mostRecentRowPatchTime)=0; + + /// \return Whatever this function returns is sent from the AutopatcherServer to the AutopatcherClient when one of the above functions returns false. + virtual const char *GetLastError(void) const=0; + + /// \return Passed to FileListTransfer::Send() as the _chunkSize parameter. + virtual const int GetIncrementalReadChunkSize(void) const=0; +}; + +} // namespace RakNet + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/BitStream.cpp b/project/lib_projects/raknet/jni/RaknetSources/BitStream.cpp index 7a0a14f..cdf88ac 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/BitStream.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/BitStream.cpp @@ -1,1172 +1,1172 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization -#include "BitStream_NoTemplate.cpp" -#else - -#include "BitStream.h" -#include -#include -#include - -#include "SocketIncludes.h" -#include "RakNetDefines.h" - - - -#if defined(_WIN32) -#include // htonl -#include -#include -#include - - -#else -#include -#include -#if defined(ANDROID) -#include -#else -#include -#endif -#include -#endif - -// MSWin uses _copysign, others use copysign... -#ifndef _WIN32 -#define _copysign copysign -#endif - -using namespace RakNet; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -STATIC_FACTORY_DEFINITIONS(BitStream,BitStream) - -BitStream::BitStream() -{ - numberOfBitsUsed = 0; - //numberOfBitsAllocated = 32 * 8; - numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; - readOffset = 0; - //data = ( unsigned char* ) rakMalloc_Ex( 32, _FILE_AND_LINE_ ); - data = ( unsigned char* ) stackData; - -#ifdef _DEBUG - // RakAssert( data ); -#endif - //memset(data, 0, 32); - copyData = true; -} - -BitStream::BitStream( const unsigned int initialBytesToAllocate ) -{ - numberOfBitsUsed = 0; - readOffset = 0; - if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE) - { - data = ( unsigned char* ) stackData; - numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; - } - else - { - data = ( unsigned char* ) rakMalloc_Ex( (size_t) initialBytesToAllocate, _FILE_AND_LINE_ ); - numberOfBitsAllocated = initialBytesToAllocate << 3; - } -#ifdef _DEBUG - RakAssert( data ); -#endif - // memset(data, 0, initialBytesToAllocate); - copyData = true; -} - -BitStream::BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData ) -{ - numberOfBitsUsed = lengthInBytes << 3; - readOffset = 0; - copyData = _copyData; - numberOfBitsAllocated = lengthInBytes << 3; - - if ( copyData ) - { - if ( lengthInBytes > 0 ) - { - if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE) - { - data = ( unsigned char* ) stackData; - numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << 3; - } - else - { - data = ( unsigned char* ) rakMalloc_Ex( (size_t) lengthInBytes, _FILE_AND_LINE_ ); - } -#ifdef _DEBUG - RakAssert( data ); -#endif - memcpy( data, _data, (size_t) lengthInBytes ); - } - else - data = 0; - } - else - data = ( unsigned char* ) _data; -} - -// Use this if you pass a pointer copy to the constructor (_copyData==false) and want to overallocate to prevent reallocation -void BitStream::SetNumberOfBitsAllocated( const BitSize_t lengthInBits ) -{ -#ifdef _DEBUG - RakAssert( lengthInBits >= ( BitSize_t ) numberOfBitsAllocated ); -#endif - numberOfBitsAllocated = lengthInBits; -} - -BitStream::~BitStream() -{ - if ( copyData && numberOfBitsAllocated > (BITSTREAM_STACK_ALLOCATION_SIZE << 3)) - rakFree_Ex( data , _FILE_AND_LINE_ ); // Use realloc and free so we are more efficient than delete and new for resizing -} - -void BitStream::Reset( void ) -{ - // Note: Do NOT reallocate memory because BitStream is used - // in places to serialize/deserialize a buffer. Reallocation - // is a dangerous operation (may result in leaks). - - if ( numberOfBitsUsed > 0 ) - { - // memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed)); - } - - // Don't free memory here for speed efficiency - //free(data); // Use realloc and free so we are more efficient than delete and new for resizing - numberOfBitsUsed = 0; - - //numberOfBitsAllocated=8; - readOffset = 0; - - //data=(unsigned char*)rakMalloc_Ex(1, _FILE_AND_LINE_); - // if (numberOfBitsAllocated>0) - // memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated)); -} - -// Write an array or casted stream -void BitStream::Write( const char* inputByteArray, const unsigned int numberOfBytes ) -{ - if (numberOfBytes==0) - return; - - // Optimization: - if ((numberOfBitsUsed & 7) == 0) - { - AddBitsAndReallocate( BYTES_TO_BITS(numberOfBytes) ); - memcpy(data+BITS_TO_BYTES(numberOfBitsUsed), inputByteArray, (size_t) numberOfBytes); - numberOfBitsUsed+=BYTES_TO_BITS(numberOfBytes); - } - else - { - WriteBits( ( unsigned char* ) inputByteArray, numberOfBytes * 8, true ); - } - -} -void BitStream::Write( BitStream *bitStream) -{ - Write(bitStream, bitStream->GetNumberOfBitsUsed()-bitStream->GetReadOffset()); -} -void BitStream::Write( BitStream *bitStream, BitSize_t numberOfBits ) -{ - AddBitsAndReallocate( numberOfBits ); - BitSize_t numberOfBitsMod8; - - if ((bitStream->GetReadOffset()&7)==0 && (numberOfBitsUsed&7)==0) - { - int readOffsetBytes=bitStream->GetReadOffset()/8; - int numBytes=numberOfBits/8; - memcpy(data + (numberOfBitsUsed >> 3), bitStream->GetData()+readOffsetBytes, numBytes); - numberOfBits-=BYTES_TO_BITS(numBytes); - bitStream->SetReadOffset(BYTES_TO_BITS(numBytes+readOffsetBytes)); - numberOfBitsUsed+=BYTES_TO_BITS(numBytes); - } - - while (numberOfBits-->0 && bitStream->readOffset + 1 <= bitStream->numberOfBitsUsed) - { - numberOfBitsMod8 = numberOfBitsUsed & 7; - if ( numberOfBitsMod8 == 0 ) - { - // New byte - if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) ) - { - // Write 1 - data[ numberOfBitsUsed >> 3 ] = 0x80; - } - else - { - // Write 0 - data[ numberOfBitsUsed >> 3 ] = 0; - } - - } - else - { - // Existing byte - if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) ) - data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 - // else 0, do nothing - } - - bitStream->readOffset++; - numberOfBitsUsed++; - } -} -void BitStream::Write( BitStream &bitStream, BitSize_t numberOfBits ) -{ - Write(&bitStream, numberOfBits); -} -void BitStream::Write( BitStream &bitStream ) -{ - Write(&bitStream); -} -bool BitStream::Read( BitStream *bitStream, BitSize_t numberOfBits ) -{ - if (GetNumberOfUnreadBits() < numberOfBits) - return false; - bitStream->Write(this, numberOfBits); - return true; -} -bool BitStream::Read( BitStream *bitStream ) -{ - bitStream->Write(this); - return true; -} -bool BitStream::Read( BitStream &bitStream, BitSize_t numberOfBits ) -{ - if (GetNumberOfUnreadBits() < numberOfBits) - return false; - bitStream.Write(this, numberOfBits); - return true; -} -bool BitStream::Read( BitStream &bitStream ) -{ - bitStream.Write(this); - return true; -} - -// Read an array or casted stream -bool BitStream::Read( char* outByteArray, const unsigned int numberOfBytes ) -{ - // Optimization: - if ((readOffset & 7) == 0) - { - if ( readOffset + ( numberOfBytes << 3 ) > numberOfBitsUsed ) - return false; - - // Write the data - memcpy( outByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytes ); - - readOffset += numberOfBytes << 3; - return true; - } - else - { - return ReadBits( ( unsigned char* ) outByteArray, numberOfBytes * 8 ); - } -} - -// Sets the read pointer back to the beginning of your data. -void BitStream::ResetReadPointer( void ) -{ - readOffset = 0; -} - -// Sets the write pointer back to the beginning of your data. -void BitStream::ResetWritePointer( void ) -{ - numberOfBitsUsed = 0; -} - -// Write a 0 -void BitStream::Write0( void ) -{ - AddBitsAndReallocate( 1 ); - - // New bytes need to be zeroed - if ( ( numberOfBitsUsed & 7 ) == 0 ) - data[ numberOfBitsUsed >> 3 ] = 0; - - numberOfBitsUsed++; -} - -// Write a 1 -void BitStream::Write1( void ) -{ - AddBitsAndReallocate( 1 ); - - BitSize_t numberOfBitsMod8 = numberOfBitsUsed & 7; - - if ( numberOfBitsMod8 == 0 ) - data[ numberOfBitsUsed >> 3 ] = 0x80; - else - data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 - - numberOfBitsUsed++; -} - -// Returns true if the next data read is a 1, false if it is a 0 -bool BitStream::ReadBit( void ) -{ - bool result = ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0; - readOffset++; - return result; -} - -// Align the bitstream to the byte boundary and then write the specified number of bits. -// This is faster than WriteBits but wastes the bits to do the alignment and requires you to call -// SetReadToByteAlignment at the corresponding read position -void BitStream::WriteAlignedBytes( const unsigned char* inByteArray, const unsigned int numberOfBytesToWrite ) -{ - AlignWriteToByteBoundary(); - Write((const char*) inByteArray, numberOfBytesToWrite); -} -void BitStream::EndianSwapBytes( int byteOffset, int length ) -{ - if (DoEndianSwap()) - { - ReverseBytesInPlace(data+byteOffset, length); - } -} -/// Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite -void BitStream::WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite ) -{ - if (inByteArray==0 || inputLength==0) - { - WriteCompressed((unsigned int)0); - return; - } - WriteCompressed(inputLength); - WriteAlignedBytes((const unsigned char*) inByteArray, inputLength < maxBytesToWrite ? inputLength : maxBytesToWrite); -} - -// Read bits, starting at the next aligned bits. Note that the modulus 8 starting offset of the -// sequence must be the same as was used with WriteBits. This will be a problem with packet coalescence -// unless you byte align the coalesced packets. -bool BitStream::ReadAlignedBytes( unsigned char* inOutByteArray, const unsigned int numberOfBytesToRead ) -{ -#ifdef _DEBUG - RakAssert( numberOfBytesToRead > 0 ); -#endif - - if ( numberOfBytesToRead <= 0 ) - return false; - - // Byte align - AlignReadToByteBoundary(); - - if ( readOffset + ( numberOfBytesToRead << 3 ) > numberOfBitsUsed ) - return false; - - // Write the data - memcpy( inOutByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytesToRead ); - - readOffset += numberOfBytesToRead << 3; - - return true; -} -bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead ) -{ - return ReadAlignedBytesSafe(inOutByteArray,(unsigned int&) inputLength,(unsigned int)maxBytesToRead); -} -bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ) -{ - if (ReadCompressed(inputLength)==false) - return false; - if (inputLength > maxBytesToRead) - inputLength=maxBytesToRead; - if (inputLength==0) - return true; - return ReadAlignedBytes((unsigned char*) inOutByteArray, inputLength); -} -bool BitStream::ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead ) -{ - return ReadAlignedBytesSafeAlloc(outByteArray,(unsigned int&) inputLength, maxBytesToRead); -} -bool BitStream::ReadAlignedBytesSafeAlloc( char ** outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ) -{ - rakFree_Ex(*outByteArray, _FILE_AND_LINE_ ); - *outByteArray=0; - if (ReadCompressed(inputLength)==false) - return false; - if (inputLength > maxBytesToRead) - inputLength=maxBytesToRead; - if (inputLength==0) - return true; - *outByteArray = (char*) rakMalloc_Ex( (size_t) inputLength, _FILE_AND_LINE_ ); - return ReadAlignedBytes((unsigned char*) *outByteArray, inputLength); -} - -// Write numberToWrite bits from the input source -void BitStream::WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits ) -{ -// if (numberOfBitsToWrite<=0) -// return; - - AddBitsAndReallocate( numberOfBitsToWrite ); - - const BitSize_t numberOfBitsUsedMod8 = numberOfBitsUsed & 7; - - // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed - if (numberOfBitsUsedMod8==0 && (numberOfBitsToWrite&7)==0) - { - memcpy( data + ( numberOfBitsUsed >> 3 ), inByteArray, numberOfBitsToWrite>>3); - numberOfBitsUsed+=numberOfBitsToWrite; - return; - } - - unsigned char dataByte; - const unsigned char* inputPtr=inByteArray; - - // Faster to put the while at the top surprisingly enough - while ( numberOfBitsToWrite > 0 ) - //do - { - dataByte = *( inputPtr++ ); - - if ( numberOfBitsToWrite < 8 && rightAlignedBits ) // rightAlignedBits means in the case of a partial byte, the bits are aligned from the right (bit 0) rather than the left (as in the normal internal representation) - dataByte <<= 8 - numberOfBitsToWrite; // shift left to get the bits on the left, as in our internal representation - - // Writing to a new byte each time - if ( numberOfBitsUsedMod8 == 0 ) - * ( data + ( numberOfBitsUsed >> 3 ) ) = dataByte; - else - { - // Copy over the new data. - *( data + ( numberOfBitsUsed >> 3 ) ) |= dataByte >> ( numberOfBitsUsedMod8 ); // First half - - if ( 8 - ( numberOfBitsUsedMod8 ) < 8 && 8 - ( numberOfBitsUsedMod8 ) < numberOfBitsToWrite ) // If we didn't write it all out in the first half (8 - (numberOfBitsUsed%8) is the number we wrote in the first half) - { - *( data + ( numberOfBitsUsed >> 3 ) + 1 ) = (unsigned char) ( dataByte << ( 8 - ( numberOfBitsUsedMod8 ) ) ); // Second half (overlaps byte boundary) - } - } - - if ( numberOfBitsToWrite >= 8 ) - { - numberOfBitsUsed += 8; - numberOfBitsToWrite -= 8; - } - else - { - numberOfBitsUsed += numberOfBitsToWrite; - numberOfBitsToWrite=0; - } - } - // } while(numberOfBitsToWrite>0); -} - -// Set the stream to some initial data. For internal use -void BitStream::SetData( unsigned char *inByteArray ) -{ - data=inByteArray; - copyData=false; -} - -// Assume the input source points to a native type, compress and write it -void BitStream::WriteCompressed( const unsigned char* inByteArray, - const unsigned int size, const bool unsignedData ) -{ - BitSize_t currentByte = ( size >> 3 ) - 1; // PCs - - unsigned char byteMatch; - - if ( unsignedData ) - { - byteMatch = 0; - } - - else - { - byteMatch = 0xFF; - } - - // Write upper bytes with a single 1 - // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes - while ( currentByte > 0 ) - { - if ( inByteArray[ currentByte ] == byteMatch ) // If high byte is byteMatch (0 of 0xff) then it would have the same value shifted - { - bool b = true; - Write( b ); - } - else - { - // Write the remainder of the data after writing 0 - bool b = false; - Write( b ); - - WriteBits( inByteArray, ( currentByte + 1 ) << 3, true ); - // currentByte--; - - - return ; - } - - currentByte--; - } - - // If the upper half of the last byte is a 0 (positive) or 16 (negative) then write a 1 and the remaining 4 bits. Otherwise write a 0 and the 8 bites. - if ( ( unsignedData && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0x00 ) || - ( unsignedData == false && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0xF0 ) ) - { - bool b = true; - Write( b ); - WriteBits( inByteArray + currentByte, 4, true ); - } - - else - { - bool b = false; - Write( b ); - WriteBits( inByteArray + currentByte, 8, true ); - } -} - -// Read numberOfBitsToRead bits to the output source -// alignBitsToRight should be set to true to convert internal bitstream data to userdata -// It should be false if you used WriteBits with rightAlignedBits false -bool BitStream::ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight ) -{ -#ifdef _DEBUG - // RakAssert( numberOfBitsToRead > 0 ); -#endif - if (numberOfBitsToRead<=0) - return false; - - if ( readOffset + numberOfBitsToRead > numberOfBitsUsed ) - return false; - - - const BitSize_t readOffsetMod8 = readOffset & 7; - - // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed - if (readOffsetMod8==0 && (numberOfBitsToRead&7)==0) - { - memcpy( inOutByteArray, data + ( readOffset >> 3 ), numberOfBitsToRead>>3); - readOffset+=numberOfBitsToRead; - return true; - } - - - - BitSize_t offset = 0; - - memset( inOutByteArray, 0, (size_t) BITS_TO_BYTES( numberOfBitsToRead ) ); - - while ( numberOfBitsToRead > 0 ) - { - *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) ) << ( readOffsetMod8 ); // First half - - if ( readOffsetMod8 > 0 && numberOfBitsToRead > 8 - ( readOffsetMod8 ) ) // If we have a second half, we didn't read enough bytes in the first half - *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) + 1 ) >> ( 8 - ( readOffsetMod8 ) ); // Second half (overlaps byte boundary) - - if (numberOfBitsToRead>=8) - { - numberOfBitsToRead -= 8; - readOffset += 8; - offset++; - } - else - { - int neg = (int) numberOfBitsToRead - 8; - - if ( neg < 0 ) // Reading a partial byte for the last byte, shift right so the data is aligned on the right - { - - if ( alignBitsToRight ) - * ( inOutByteArray + offset ) >>= -neg; - - readOffset += 8 + neg; - } - else - readOffset += 8; - - offset++; - - numberOfBitsToRead=0; - } - } - - return true; -} - -// Assume the input source points to a compressed native type. Decompress and read it -bool BitStream::ReadCompressed( unsigned char* inOutByteArray, - const unsigned int size, const bool unsignedData ) -{ - unsigned int currentByte = ( size >> 3 ) - 1; - - - unsigned char byteMatch, halfByteMatch; - - if ( unsignedData ) - { - byteMatch = 0; - halfByteMatch = 0; - } - - else - { - byteMatch = 0xFF; - halfByteMatch = 0xF0; - } - - // Upper bytes are specified with a single 1 if they match byteMatch - // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes - while ( currentByte > 0 ) - { - // If we read a 1 then the data is byteMatch. - - bool b; - - if ( Read( b ) == false ) - return false; - - if ( b ) // Check that bit - { - inOutByteArray[ currentByte ] = byteMatch; - currentByte--; - } - else - { - // Read the rest of the bytes - - if ( ReadBits( inOutByteArray, ( currentByte + 1 ) << 3 ) == false ) - return false; - - return true; - } - } - - // All but the first bytes are byteMatch. If the upper half of the last byte is a 0 (positive) or 16 (negative) then what we read will be a 1 and the remaining 4 bits. - // Otherwise we read a 0 and the 8 bytes - //RakAssert(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from - if ( readOffset + 1 > numberOfBitsUsed ) - return false; - - bool b=false; - - if ( Read( b ) == false ) - return false; - - if ( b ) // Check that bit - { - - if ( ReadBits( inOutByteArray + currentByte, 4 ) == false ) - return false; - - inOutByteArray[ currentByte ] |= halfByteMatch; // We have to set the high 4 bits since these are set to 0 by ReadBits - } - else - { - if ( ReadBits( inOutByteArray + currentByte, 8 ) == false ) - return false; - } - - return true; -} - -// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite -void BitStream::AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite ) -{ - BitSize_t newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed; - - if ( numberOfBitsToWrite + numberOfBitsUsed > 0 && ( ( numberOfBitsAllocated - 1 ) >> 3 ) < ( ( newNumberOfBitsAllocated - 1 ) >> 3 ) ) // If we need to allocate 1 or more new bytes - { -#ifdef _DEBUG - // If this assert hits then we need to specify true for the third parameter in the constructor - // It needs to reallocate to hold all the data and can't do it unless we allocated to begin with - // Often hits if you call Write or Serialize on a read-only bitstream - RakAssert( copyData == true ); -#endif - - // Less memory efficient but saves on news and deletes - /// Cap to 1 meg buffer to save on huge allocations - newNumberOfBitsAllocated = ( numberOfBitsToWrite + numberOfBitsUsed ) * 2; - if (newNumberOfBitsAllocated - ( numberOfBitsToWrite + numberOfBitsUsed ) > 1048576 ) - newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed + 1048576; - - // BitSize_t newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated ); - // Use realloc and free so we are more efficient than delete and new for resizing - BitSize_t amountToAllocate = BITS_TO_BYTES( newNumberOfBitsAllocated ); - if (data==(unsigned char*)stackData) - { - if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE) - { - data = ( unsigned char* ) rakMalloc_Ex( (size_t) amountToAllocate, _FILE_AND_LINE_ ); - RakAssert(data); - - // need to copy the stack data over to our new memory area too - memcpy ((void *)data, (void *)stackData, (size_t) BITS_TO_BYTES( numberOfBitsAllocated )); - } - } - else - { - data = ( unsigned char* ) rakRealloc_Ex( data, (size_t) amountToAllocate, _FILE_AND_LINE_ ); - } - -#ifdef _DEBUG - RakAssert( data ); // Make sure realloc succeeded -#endif - // memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0 - } - - if ( newNumberOfBitsAllocated > numberOfBitsAllocated ) - numberOfBitsAllocated = newNumberOfBitsAllocated; -} -BitSize_t BitStream::GetNumberOfBitsAllocated(void) const -{ - return numberOfBitsAllocated; -} -void BitStream::PadWithZeroToByteLength( unsigned int bytes ) -{ - if (GetNumberOfBytesUsed() < bytes) - { - AlignWriteToByteBoundary(); - unsigned int numToWrite = bytes - GetNumberOfBytesUsed(); - AddBitsAndReallocate( BYTES_TO_BITS(numToWrite) ); - memset(data+BITS_TO_BYTES(numberOfBitsUsed), 0, (size_t) numToWrite); - numberOfBitsUsed+=BYTES_TO_BITS(numToWrite); - } -} - -/* -// Julius Goryavsky's version of Harley's algorithm. -// 17 elementary ops plus an indexed load, if the machine -// has "and not." - -int nlz10b(unsigned x) { - - static char table[64] = - {32,20,19, u, u,18, u, 7, 10,17, u, u,14, u, 6, u, - u, 9, u,16, u, u, 1,26, u,13, u, u,24, 5, u, u, - u,21, u, 8,11, u,15, u, u, u, u, 2,27, 0,25, u, - 22, u,12, u, u, 3,28, u, 23, u, 4,29, u, u,30,31}; - - x = x | (x >> 1); // Propagate leftmost - x = x | (x >> 2); // 1-bit to the right. - x = x | (x >> 4); - x = x | (x >> 8); - x = x & ~(x >> 16); - x = x*0xFD7049FF; // Activate this line or the following 3. -// x = (x << 9) - x; // Multiply by 511. -// x = (x << 11) - x; // Multiply by 2047. -// x = (x << 14) - x; // Multiply by 16383. - return table[x >> 26]; -} -*/ -int BitStream::NumberOfLeadingZeroes( int8_t x ) {return NumberOfLeadingZeroes((uint8_t)x);} -int BitStream::NumberOfLeadingZeroes( uint8_t x ) -{ - uint8_t y; - int n; - - n = 8; - y = x >> 4; if (y != 0) {n = n - 4; x = y;} - y = x >> 2; if (y != 0) {n = n - 2; x = y;} - y = x >> 1; if (y != 0) return n - 2; - return (int)(n - x); -} -int BitStream::NumberOfLeadingZeroes( int16_t x ) {return NumberOfLeadingZeroes((uint16_t)x);} -int BitStream::NumberOfLeadingZeroes( uint16_t x ) -{ - uint16_t y; - int n; - - n = 16; - y = x >> 8; if (y != 0) {n = n - 8; x = y;} - y = x >> 4; if (y != 0) {n = n - 4; x = y;} - y = x >> 2; if (y != 0) {n = n - 2; x = y;} - y = x >> 1; if (y != 0) return n - 2; - return (int)(n - x); -} -int BitStream::NumberOfLeadingZeroes( int32_t x ) {return NumberOfLeadingZeroes((uint32_t)x);} -int BitStream::NumberOfLeadingZeroes( uint32_t x ) -{ - uint32_t y; - int n; - - n = 32; - y = x >>16; if (y != 0) {n = n -16; x = y;} - y = x >> 8; if (y != 0) {n = n - 8; x = y;} - y = x >> 4; if (y != 0) {n = n - 4; x = y;} - y = x >> 2; if (y != 0) {n = n - 2; x = y;} - y = x >> 1; if (y != 0) return n - 2; - return (int)(n - x); -} -int BitStream::NumberOfLeadingZeroes( int64_t x ) {return NumberOfLeadingZeroes((uint64_t)x);} -int BitStream::NumberOfLeadingZeroes( uint64_t x ) -{ - uint64_t y; - int n; - - n = 64; - y = x >>32; if (y != 0) {n = n -32; x = y;} - y = x >>16; if (y != 0) {n = n -16; x = y;} - y = x >> 8; if (y != 0) {n = n - 8; x = y;} - y = x >> 4; if (y != 0) {n = n - 4; x = y;} - y = x >> 2; if (y != 0) {n = n - 2; x = y;} - y = x >> 1; if (y != 0) return n - 2; - return (int)(n - x); -} - -// Should hit if reads didn't match writes -void BitStream::AssertStreamEmpty( void ) -{ - RakAssert( readOffset == numberOfBitsUsed ); -} -void BitStream::PrintBits( char *out ) const -{ - if ( numberOfBitsUsed <= 0 ) - { - strcpy(out, "No bits\n" ); - return; - } - - unsigned int strIndex=0; - for ( BitSize_t counter = 0; counter < BITS_TO_BYTES( numberOfBitsUsed ) && strIndex < 2000 ; counter++ ) - { - BitSize_t stop; - - if ( counter == ( numberOfBitsUsed - 1 ) >> 3 ) - stop = 8 - ( ( ( numberOfBitsUsed - 1 ) & 7 ) + 1 ); - else - stop = 0; - - for ( BitSize_t counter2 = 7; counter2 >= stop; counter2-- ) - { - if ( ( data[ counter ] >> counter2 ) & 1 ) - out[strIndex++]='1'; - else - out[strIndex++]='0'; - - if (counter2==0) - break; - } - - out[strIndex++]=' '; - } - - out[strIndex++]='\n'; - - out[strIndex++]=0; -} -void BitStream::PrintBits( void ) const -{ - char out[2048]; - PrintBits(out); - RAKNET_DEBUG_PRINTF("%s", out); -} -void BitStream::PrintHex( char *out ) const -{ - BitSize_t i; - for ( i=0; i < GetNumberOfBytesUsed(); i++) - { - sprintf(out+i*3, "%02x ", data[i]); - } -} -void BitStream::PrintHex( void ) const -{ - char out[2048]; - PrintHex(out); - RAKNET_DEBUG_PRINTF("%s", out); -} - -// Exposes the data for you to look at, like PrintBits does. -// Data will point to the stream. Returns the length in bits of the stream. -BitSize_t BitStream::CopyData( unsigned char** _data ) const -{ -#ifdef _DEBUG - RakAssert( numberOfBitsUsed > 0 ); -#endif - - *_data = (unsigned char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES( numberOfBitsUsed ), _FILE_AND_LINE_ ); - memcpy( *_data, data, sizeof(unsigned char) * (size_t) ( BITS_TO_BYTES( numberOfBitsUsed ) ) ); - return numberOfBitsUsed; -} - -// Ignore data we don't intend to read -void BitStream::IgnoreBits( const BitSize_t numberOfBits ) -{ - readOffset += numberOfBits; -} - -void BitStream::IgnoreBytes( const unsigned int numberOfBytes ) -{ - IgnoreBits(BYTES_TO_BITS(numberOfBytes)); -} - -// Move the write pointer to a position on the array. Dangerous if you don't know what you are doing! -// Doesn't work with non-aligned data! -void BitStream::SetWriteOffset( const BitSize_t offset ) -{ - numberOfBitsUsed = offset; -} - -/* -BitSize_t BitStream::GetWriteOffset( void ) const -{ -return numberOfBitsUsed; -} - -// Returns the length in bits of the stream -BitSize_t BitStream::GetNumberOfBitsUsed( void ) const -{ -return GetWriteOffset(); -} - -// Returns the length in bytes of the stream -BitSize_t BitStream::GetNumberOfBytesUsed( void ) const -{ -return BITS_TO_BYTES( numberOfBitsUsed ); -} - -// Returns the number of bits into the stream that we have read -BitSize_t BitStream::GetReadOffset( void ) const -{ -return readOffset; -} - - -// Sets the read bit index -void BitStream::SetReadOffset( const BitSize_t newReadOffset ) -{ -readOffset=newReadOffset; -} - -// Returns the number of bits left in the stream that haven't been read -BitSize_t BitStream::GetNumberOfUnreadBits( void ) const -{ -return numberOfBitsUsed - readOffset; -} -// Exposes the internal data -unsigned char* BitStream::GetData( void ) const -{ -return data; -} - -*/ -// If we used the constructor version with copy data off, this makes sure it is set to on and the data pointed to is copied. -void BitStream::AssertCopyData( void ) -{ - if ( copyData == false ) - { - copyData = true; - - if ( numberOfBitsAllocated > 0 ) - { - unsigned char * newdata = ( unsigned char* ) rakMalloc_Ex( (size_t) BITS_TO_BYTES( numberOfBitsAllocated ), _FILE_AND_LINE_ ); -#ifdef _DEBUG - - RakAssert( data ); -#endif - - memcpy( newdata, data, (size_t) BITS_TO_BYTES( numberOfBitsAllocated ) ); - data = newdata; - } - - else - data = 0; - } -} -bool BitStream::IsNetworkOrderInternal(void) -{ - - - - - - static const bool isNetworkOrder=(htonl(12345) == 12345); - return isNetworkOrder; - -} -void BitStream::ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length) -{ - for (BitSize_t i=0; i < length; i++) - inOutByteArray[i]=inByteArray[length-i-1]; -} -void BitStream::ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length) -{ - unsigned char temp; - BitSize_t i; - for (i=0; i < (length>>1); i++) - { - temp = inOutData[i]; - inOutData[i]=inOutData[length-i-1]; - inOutData[length-i-1]=temp; - } -} - -bool BitStream::Read(char *varString) -{ - return RakString::Deserialize(varString,this); -} -bool BitStream::Read(unsigned char *varString) -{ - return RakString::Deserialize((char*) varString,this); -} -void BitStream::WriteAlignedVar8(const char *inByteArray) -{ - RakAssert((numberOfBitsUsed&7)==0); - AddBitsAndReallocate(1*8); - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; - numberOfBitsUsed+=1*8; -} -bool BitStream::ReadAlignedVar8(char *inOutByteArray) -{ - RakAssert((readOffset&7)==0); - if ( readOffset + 1*8 > numberOfBitsUsed ) - return false; - - inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; - readOffset+=1*8; - return true; -} -void BitStream::WriteAlignedVar16(const char *inByteArray) -{ - RakAssert((numberOfBitsUsed&7)==0); - AddBitsAndReallocate(2*8); -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[1]; - data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[0]; - } - else -#endif - { - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; - data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1]; - } - - numberOfBitsUsed+=2*8; -} -bool BitStream::ReadAlignedVar16(char *inOutByteArray) -{ - RakAssert((readOffset&7)==0); - if ( readOffset + 2*8 > numberOfBitsUsed ) - return false; -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - inOutByteArray[0] = data[( readOffset >> 3 ) + 1]; - inOutByteArray[1] = data[( readOffset >> 3 ) + 0]; - } - else -#endif - { - inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; - inOutByteArray[1] = data[( readOffset >> 3 ) + 1]; - } - - readOffset+=2*8; - return true; -} -void BitStream::WriteAlignedVar32(const char *inByteArray) -{ - RakAssert((numberOfBitsUsed&7)==0); - AddBitsAndReallocate(4*8); -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[3]; - data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[2]; - data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[1]; - data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[0]; - } - else -#endif - { - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; - data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1]; - data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[2]; - data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[3]; - } - - numberOfBitsUsed+=4*8; -} -bool BitStream::ReadAlignedVar32(char *inOutByteArray) -{ - RakAssert((readOffset&7)==0); - if ( readOffset + 4*8 > numberOfBitsUsed ) - return false; -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - inOutByteArray[0] = data[( readOffset >> 3 ) + 3]; - inOutByteArray[1] = data[( readOffset >> 3 ) + 2]; - inOutByteArray[2] = data[( readOffset >> 3 ) + 1]; - inOutByteArray[3] = data[( readOffset >> 3 ) + 0]; - } - else -#endif - { - inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; - inOutByteArray[1] = data[( readOffset >> 3 ) + 1]; - inOutByteArray[2] = data[( readOffset >> 3 ) + 2]; - inOutByteArray[3] = data[( readOffset >> 3 ) + 3]; - } - - readOffset+=4*8; - return true; -} -bool BitStream::ReadFloat16( float &outFloat, float floatMin, float floatMax ) -{ - unsigned short percentile; - if (Read(percentile)) - { - RakAssert(floatMax>floatMin); - outFloat = floatMin + ((float) percentile / 65535.0f) * (floatMax-floatMin); - if (outFloatfloatMax) - outFloat=floatMax; - return true; - } - return false; -} -bool BitStream::SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax) -{ - if (writeToBitstream) - WriteFloat16(inOutFloat, floatMin, floatMax); - else - return ReadFloat16(inOutFloat, floatMin, floatMax); - return true; -} -void BitStream::WriteFloat16( float inOutFloat, float floatMin, float floatMax ) -{ - RakAssert(floatMax>floatMin); - if (inOutFloat>floatMax+.001) - { - RakAssert(inOutFloat<=floatMax+.001); - } - if (inOutFloat=floatMin-.001); - } - float percentile=65535.0f * (inOutFloat-floatMin)/(floatMax-floatMin); - if (percentile<0.0) - percentile=0.0; - if (percentile>65535.0f) - percentile=65535.0f; - Write((unsigned short)percentile); -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // #if _MSC_VER < 1299 +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization +#include "BitStream_NoTemplate.cpp" +#else + +#include "BitStream.h" +#include +#include +#include + +#include "SocketIncludes.h" +#include "RakNetDefines.h" + + + +#if defined(_WIN32) +#include // htonl +#include +#include +#include + + +#else +#include +#include +#if defined(ANDROID) +#include +#else +#include +#endif +#include +#endif + +// MSWin uses _copysign, others use copysign... +#ifndef _WIN32 +#define _copysign copysign +#endif + +using namespace RakNet; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +STATIC_FACTORY_DEFINITIONS(BitStream,BitStream) + +BitStream::BitStream() +{ + numberOfBitsUsed = 0; + //numberOfBitsAllocated = 32 * 8; + numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; + readOffset = 0; + //data = ( unsigned char* ) rakMalloc_Ex( 32, _FILE_AND_LINE_ ); + data = ( unsigned char* ) stackData; + +#ifdef _DEBUG + // RakAssert( data ); +#endif + //memset(data, 0, 32); + copyData = true; +} + +BitStream::BitStream( const unsigned int initialBytesToAllocate ) +{ + numberOfBitsUsed = 0; + readOffset = 0; + if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE) + { + data = ( unsigned char* ) stackData; + numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; + } + else + { + data = ( unsigned char* ) rakMalloc_Ex( (size_t) initialBytesToAllocate, _FILE_AND_LINE_ ); + numberOfBitsAllocated = initialBytesToAllocate << 3; + } +#ifdef _DEBUG + RakAssert( data ); +#endif + // memset(data, 0, initialBytesToAllocate); + copyData = true; +} + +BitStream::BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData ) +{ + numberOfBitsUsed = lengthInBytes << 3; + readOffset = 0; + copyData = _copyData; + numberOfBitsAllocated = lengthInBytes << 3; + + if ( copyData ) + { + if ( lengthInBytes > 0 ) + { + if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE) + { + data = ( unsigned char* ) stackData; + numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << 3; + } + else + { + data = ( unsigned char* ) rakMalloc_Ex( (size_t) lengthInBytes, _FILE_AND_LINE_ ); + } +#ifdef _DEBUG + RakAssert( data ); +#endif + memcpy( data, _data, (size_t) lengthInBytes ); + } + else + data = 0; + } + else + data = ( unsigned char* ) _data; +} + +// Use this if you pass a pointer copy to the constructor (_copyData==false) and want to overallocate to prevent reallocation +void BitStream::SetNumberOfBitsAllocated( const BitSize_t lengthInBits ) +{ +#ifdef _DEBUG + RakAssert( lengthInBits >= ( BitSize_t ) numberOfBitsAllocated ); +#endif + numberOfBitsAllocated = lengthInBits; +} + +BitStream::~BitStream() +{ + if ( copyData && numberOfBitsAllocated > (BITSTREAM_STACK_ALLOCATION_SIZE << 3)) + rakFree_Ex( data , _FILE_AND_LINE_ ); // Use realloc and free so we are more efficient than delete and new for resizing +} + +void BitStream::Reset( void ) +{ + // Note: Do NOT reallocate memory because BitStream is used + // in places to serialize/deserialize a buffer. Reallocation + // is a dangerous operation (may result in leaks). + + if ( numberOfBitsUsed > 0 ) + { + // memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed)); + } + + // Don't free memory here for speed efficiency + //free(data); // Use realloc and free so we are more efficient than delete and new for resizing + numberOfBitsUsed = 0; + + //numberOfBitsAllocated=8; + readOffset = 0; + + //data=(unsigned char*)rakMalloc_Ex(1, _FILE_AND_LINE_); + // if (numberOfBitsAllocated>0) + // memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated)); +} + +// Write an array or casted stream +void BitStream::Write( const char* inputByteArray, const unsigned int numberOfBytes ) +{ + if (numberOfBytes==0) + return; + + // Optimization: + if ((numberOfBitsUsed & 7) == 0) + { + AddBitsAndReallocate( BYTES_TO_BITS(numberOfBytes) ); + memcpy(data+BITS_TO_BYTES(numberOfBitsUsed), inputByteArray, (size_t) numberOfBytes); + numberOfBitsUsed+=BYTES_TO_BITS(numberOfBytes); + } + else + { + WriteBits( ( unsigned char* ) inputByteArray, numberOfBytes * 8, true ); + } + +} +void BitStream::Write( BitStream *bitStream) +{ + Write(bitStream, bitStream->GetNumberOfBitsUsed()-bitStream->GetReadOffset()); +} +void BitStream::Write( BitStream *bitStream, BitSize_t numberOfBits ) +{ + AddBitsAndReallocate( numberOfBits ); + BitSize_t numberOfBitsMod8; + + if ((bitStream->GetReadOffset()&7)==0 && (numberOfBitsUsed&7)==0) + { + int readOffsetBytes=bitStream->GetReadOffset()/8; + int numBytes=numberOfBits/8; + memcpy(data + (numberOfBitsUsed >> 3), bitStream->GetData()+readOffsetBytes, numBytes); + numberOfBits-=BYTES_TO_BITS(numBytes); + bitStream->SetReadOffset(BYTES_TO_BITS(numBytes+readOffsetBytes)); + numberOfBitsUsed+=BYTES_TO_BITS(numBytes); + } + + while (numberOfBits-->0 && bitStream->readOffset + 1 <= bitStream->numberOfBitsUsed) + { + numberOfBitsMod8 = numberOfBitsUsed & 7; + if ( numberOfBitsMod8 == 0 ) + { + // New byte + if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) ) + { + // Write 1 + data[ numberOfBitsUsed >> 3 ] = 0x80; + } + else + { + // Write 0 + data[ numberOfBitsUsed >> 3 ] = 0; + } + + } + else + { + // Existing byte + if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) ) + data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 + // else 0, do nothing + } + + bitStream->readOffset++; + numberOfBitsUsed++; + } +} +void BitStream::Write( BitStream &bitStream, BitSize_t numberOfBits ) +{ + Write(&bitStream, numberOfBits); +} +void BitStream::Write( BitStream &bitStream ) +{ + Write(&bitStream); +} +bool BitStream::Read( BitStream *bitStream, BitSize_t numberOfBits ) +{ + if (GetNumberOfUnreadBits() < numberOfBits) + return false; + bitStream->Write(this, numberOfBits); + return true; +} +bool BitStream::Read( BitStream *bitStream ) +{ + bitStream->Write(this); + return true; +} +bool BitStream::Read( BitStream &bitStream, BitSize_t numberOfBits ) +{ + if (GetNumberOfUnreadBits() < numberOfBits) + return false; + bitStream.Write(this, numberOfBits); + return true; +} +bool BitStream::Read( BitStream &bitStream ) +{ + bitStream.Write(this); + return true; +} + +// Read an array or casted stream +bool BitStream::Read( char* outByteArray, const unsigned int numberOfBytes ) +{ + // Optimization: + if ((readOffset & 7) == 0) + { + if ( readOffset + ( numberOfBytes << 3 ) > numberOfBitsUsed ) + return false; + + // Write the data + memcpy( outByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytes ); + + readOffset += numberOfBytes << 3; + return true; + } + else + { + return ReadBits( ( unsigned char* ) outByteArray, numberOfBytes * 8 ); + } +} + +// Sets the read pointer back to the beginning of your data. +void BitStream::ResetReadPointer( void ) +{ + readOffset = 0; +} + +// Sets the write pointer back to the beginning of your data. +void BitStream::ResetWritePointer( void ) +{ + numberOfBitsUsed = 0; +} + +// Write a 0 +void BitStream::Write0( void ) +{ + AddBitsAndReallocate( 1 ); + + // New bytes need to be zeroed + if ( ( numberOfBitsUsed & 7 ) == 0 ) + data[ numberOfBitsUsed >> 3 ] = 0; + + numberOfBitsUsed++; +} + +// Write a 1 +void BitStream::Write1( void ) +{ + AddBitsAndReallocate( 1 ); + + BitSize_t numberOfBitsMod8 = numberOfBitsUsed & 7; + + if ( numberOfBitsMod8 == 0 ) + data[ numberOfBitsUsed >> 3 ] = 0x80; + else + data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 + + numberOfBitsUsed++; +} + +// Returns true if the next data read is a 1, false if it is a 0 +bool BitStream::ReadBit( void ) +{ + bool result = ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0; + readOffset++; + return result; +} + +// Align the bitstream to the byte boundary and then write the specified number of bits. +// This is faster than WriteBits but wastes the bits to do the alignment and requires you to call +// SetReadToByteAlignment at the corresponding read position +void BitStream::WriteAlignedBytes( const unsigned char* inByteArray, const unsigned int numberOfBytesToWrite ) +{ + AlignWriteToByteBoundary(); + Write((const char*) inByteArray, numberOfBytesToWrite); +} +void BitStream::EndianSwapBytes( int byteOffset, int length ) +{ + if (DoEndianSwap()) + { + ReverseBytesInPlace(data+byteOffset, length); + } +} +/// Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite +void BitStream::WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite ) +{ + if (inByteArray==0 || inputLength==0) + { + WriteCompressed((unsigned int)0); + return; + } + WriteCompressed(inputLength); + WriteAlignedBytes((const unsigned char*) inByteArray, inputLength < maxBytesToWrite ? inputLength : maxBytesToWrite); +} + +// Read bits, starting at the next aligned bits. Note that the modulus 8 starting offset of the +// sequence must be the same as was used with WriteBits. This will be a problem with packet coalescence +// unless you byte align the coalesced packets. +bool BitStream::ReadAlignedBytes( unsigned char* inOutByteArray, const unsigned int numberOfBytesToRead ) +{ +#ifdef _DEBUG + RakAssert( numberOfBytesToRead > 0 ); +#endif + + if ( numberOfBytesToRead <= 0 ) + return false; + + // Byte align + AlignReadToByteBoundary(); + + if ( readOffset + ( numberOfBytesToRead << 3 ) > numberOfBitsUsed ) + return false; + + // Write the data + memcpy( inOutByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytesToRead ); + + readOffset += numberOfBytesToRead << 3; + + return true; +} +bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead ) +{ + return ReadAlignedBytesSafe(inOutByteArray,(unsigned int&) inputLength,(unsigned int)maxBytesToRead); +} +bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ) +{ + if (ReadCompressed(inputLength)==false) + return false; + if (inputLength > maxBytesToRead) + inputLength=maxBytesToRead; + if (inputLength==0) + return true; + return ReadAlignedBytes((unsigned char*) inOutByteArray, inputLength); +} +bool BitStream::ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead ) +{ + return ReadAlignedBytesSafeAlloc(outByteArray,(unsigned int&) inputLength, maxBytesToRead); +} +bool BitStream::ReadAlignedBytesSafeAlloc( char ** outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ) +{ + rakFree_Ex(*outByteArray, _FILE_AND_LINE_ ); + *outByteArray=0; + if (ReadCompressed(inputLength)==false) + return false; + if (inputLength > maxBytesToRead) + inputLength=maxBytesToRead; + if (inputLength==0) + return true; + *outByteArray = (char*) rakMalloc_Ex( (size_t) inputLength, _FILE_AND_LINE_ ); + return ReadAlignedBytes((unsigned char*) *outByteArray, inputLength); +} + +// Write numberToWrite bits from the input source +void BitStream::WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits ) +{ +// if (numberOfBitsToWrite<=0) +// return; + + AddBitsAndReallocate( numberOfBitsToWrite ); + + const BitSize_t numberOfBitsUsedMod8 = numberOfBitsUsed & 7; + + // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed + if (numberOfBitsUsedMod8==0 && (numberOfBitsToWrite&7)==0) + { + memcpy( data + ( numberOfBitsUsed >> 3 ), inByteArray, numberOfBitsToWrite>>3); + numberOfBitsUsed+=numberOfBitsToWrite; + return; + } + + unsigned char dataByte; + const unsigned char* inputPtr=inByteArray; + + // Faster to put the while at the top surprisingly enough + while ( numberOfBitsToWrite > 0 ) + //do + { + dataByte = *( inputPtr++ ); + + if ( numberOfBitsToWrite < 8 && rightAlignedBits ) // rightAlignedBits means in the case of a partial byte, the bits are aligned from the right (bit 0) rather than the left (as in the normal internal representation) + dataByte <<= 8 - numberOfBitsToWrite; // shift left to get the bits on the left, as in our internal representation + + // Writing to a new byte each time + if ( numberOfBitsUsedMod8 == 0 ) + * ( data + ( numberOfBitsUsed >> 3 ) ) = dataByte; + else + { + // Copy over the new data. + *( data + ( numberOfBitsUsed >> 3 ) ) |= dataByte >> ( numberOfBitsUsedMod8 ); // First half + + if ( 8 - ( numberOfBitsUsedMod8 ) < 8 && 8 - ( numberOfBitsUsedMod8 ) < numberOfBitsToWrite ) // If we didn't write it all out in the first half (8 - (numberOfBitsUsed%8) is the number we wrote in the first half) + { + *( data + ( numberOfBitsUsed >> 3 ) + 1 ) = (unsigned char) ( dataByte << ( 8 - ( numberOfBitsUsedMod8 ) ) ); // Second half (overlaps byte boundary) + } + } + + if ( numberOfBitsToWrite >= 8 ) + { + numberOfBitsUsed += 8; + numberOfBitsToWrite -= 8; + } + else + { + numberOfBitsUsed += numberOfBitsToWrite; + numberOfBitsToWrite=0; + } + } + // } while(numberOfBitsToWrite>0); +} + +// Set the stream to some initial data. For internal use +void BitStream::SetData( unsigned char *inByteArray ) +{ + data=inByteArray; + copyData=false; +} + +// Assume the input source points to a native type, compress and write it +void BitStream::WriteCompressed( const unsigned char* inByteArray, + const unsigned int size, const bool unsignedData ) +{ + BitSize_t currentByte = ( size >> 3 ) - 1; // PCs + + unsigned char byteMatch; + + if ( unsignedData ) + { + byteMatch = 0; + } + + else + { + byteMatch = 0xFF; + } + + // Write upper bytes with a single 1 + // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes + while ( currentByte > 0 ) + { + if ( inByteArray[ currentByte ] == byteMatch ) // If high byte is byteMatch (0 of 0xff) then it would have the same value shifted + { + bool b = true; + Write( b ); + } + else + { + // Write the remainder of the data after writing 0 + bool b = false; + Write( b ); + + WriteBits( inByteArray, ( currentByte + 1 ) << 3, true ); + // currentByte--; + + + return ; + } + + currentByte--; + } + + // If the upper half of the last byte is a 0 (positive) or 16 (negative) then write a 1 and the remaining 4 bits. Otherwise write a 0 and the 8 bites. + if ( ( unsignedData && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0x00 ) || + ( unsignedData == false && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0xF0 ) ) + { + bool b = true; + Write( b ); + WriteBits( inByteArray + currentByte, 4, true ); + } + + else + { + bool b = false; + Write( b ); + WriteBits( inByteArray + currentByte, 8, true ); + } +} + +// Read numberOfBitsToRead bits to the output source +// alignBitsToRight should be set to true to convert internal bitstream data to userdata +// It should be false if you used WriteBits with rightAlignedBits false +bool BitStream::ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight ) +{ +#ifdef _DEBUG + // RakAssert( numberOfBitsToRead > 0 ); +#endif + if (numberOfBitsToRead<=0) + return false; + + if ( readOffset + numberOfBitsToRead > numberOfBitsUsed ) + return false; + + + const BitSize_t readOffsetMod8 = readOffset & 7; + + // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed + if (readOffsetMod8==0 && (numberOfBitsToRead&7)==0) + { + memcpy( inOutByteArray, data + ( readOffset >> 3 ), numberOfBitsToRead>>3); + readOffset+=numberOfBitsToRead; + return true; + } + + + + BitSize_t offset = 0; + + memset( inOutByteArray, 0, (size_t) BITS_TO_BYTES( numberOfBitsToRead ) ); + + while ( numberOfBitsToRead > 0 ) + { + *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) ) << ( readOffsetMod8 ); // First half + + if ( readOffsetMod8 > 0 && numberOfBitsToRead > 8 - ( readOffsetMod8 ) ) // If we have a second half, we didn't read enough bytes in the first half + *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) + 1 ) >> ( 8 - ( readOffsetMod8 ) ); // Second half (overlaps byte boundary) + + if (numberOfBitsToRead>=8) + { + numberOfBitsToRead -= 8; + readOffset += 8; + offset++; + } + else + { + int neg = (int) numberOfBitsToRead - 8; + + if ( neg < 0 ) // Reading a partial byte for the last byte, shift right so the data is aligned on the right + { + + if ( alignBitsToRight ) + * ( inOutByteArray + offset ) >>= -neg; + + readOffset += 8 + neg; + } + else + readOffset += 8; + + offset++; + + numberOfBitsToRead=0; + } + } + + return true; +} + +// Assume the input source points to a compressed native type. Decompress and read it +bool BitStream::ReadCompressed( unsigned char* inOutByteArray, + const unsigned int size, const bool unsignedData ) +{ + unsigned int currentByte = ( size >> 3 ) - 1; + + + unsigned char byteMatch, halfByteMatch; + + if ( unsignedData ) + { + byteMatch = 0; + halfByteMatch = 0; + } + + else + { + byteMatch = 0xFF; + halfByteMatch = 0xF0; + } + + // Upper bytes are specified with a single 1 if they match byteMatch + // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes + while ( currentByte > 0 ) + { + // If we read a 1 then the data is byteMatch. + + bool b; + + if ( Read( b ) == false ) + return false; + + if ( b ) // Check that bit + { + inOutByteArray[ currentByte ] = byteMatch; + currentByte--; + } + else + { + // Read the rest of the bytes + + if ( ReadBits( inOutByteArray, ( currentByte + 1 ) << 3 ) == false ) + return false; + + return true; + } + } + + // All but the first bytes are byteMatch. If the upper half of the last byte is a 0 (positive) or 16 (negative) then what we read will be a 1 and the remaining 4 bits. + // Otherwise we read a 0 and the 8 bytes + //RakAssert(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from + if ( readOffset + 1 > numberOfBitsUsed ) + return false; + + bool b=false; + + if ( Read( b ) == false ) + return false; + + if ( b ) // Check that bit + { + + if ( ReadBits( inOutByteArray + currentByte, 4 ) == false ) + return false; + + inOutByteArray[ currentByte ] |= halfByteMatch; // We have to set the high 4 bits since these are set to 0 by ReadBits + } + else + { + if ( ReadBits( inOutByteArray + currentByte, 8 ) == false ) + return false; + } + + return true; +} + +// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite +void BitStream::AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite ) +{ + BitSize_t newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed; + + if ( numberOfBitsToWrite + numberOfBitsUsed > 0 && ( ( numberOfBitsAllocated - 1 ) >> 3 ) < ( ( newNumberOfBitsAllocated - 1 ) >> 3 ) ) // If we need to allocate 1 or more new bytes + { +#ifdef _DEBUG + // If this assert hits then we need to specify true for the third parameter in the constructor + // It needs to reallocate to hold all the data and can't do it unless we allocated to begin with + // Often hits if you call Write or Serialize on a read-only bitstream + RakAssert( copyData == true ); +#endif + + // Less memory efficient but saves on news and deletes + /// Cap to 1 meg buffer to save on huge allocations + newNumberOfBitsAllocated = ( numberOfBitsToWrite + numberOfBitsUsed ) * 2; + if (newNumberOfBitsAllocated - ( numberOfBitsToWrite + numberOfBitsUsed ) > 1048576 ) + newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed + 1048576; + + // BitSize_t newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated ); + // Use realloc and free so we are more efficient than delete and new for resizing + BitSize_t amountToAllocate = BITS_TO_BYTES( newNumberOfBitsAllocated ); + if (data==(unsigned char*)stackData) + { + if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE) + { + data = ( unsigned char* ) rakMalloc_Ex( (size_t) amountToAllocate, _FILE_AND_LINE_ ); + RakAssert(data); + + // need to copy the stack data over to our new memory area too + memcpy ((void *)data, (void *)stackData, (size_t) BITS_TO_BYTES( numberOfBitsAllocated )); + } + } + else + { + data = ( unsigned char* ) rakRealloc_Ex( data, (size_t) amountToAllocate, _FILE_AND_LINE_ ); + } + +#ifdef _DEBUG + RakAssert( data ); // Make sure realloc succeeded +#endif + // memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0 + } + + if ( newNumberOfBitsAllocated > numberOfBitsAllocated ) + numberOfBitsAllocated = newNumberOfBitsAllocated; +} +BitSize_t BitStream::GetNumberOfBitsAllocated(void) const +{ + return numberOfBitsAllocated; +} +void BitStream::PadWithZeroToByteLength( unsigned int bytes ) +{ + if (GetNumberOfBytesUsed() < bytes) + { + AlignWriteToByteBoundary(); + unsigned int numToWrite = bytes - GetNumberOfBytesUsed(); + AddBitsAndReallocate( BYTES_TO_BITS(numToWrite) ); + memset(data+BITS_TO_BYTES(numberOfBitsUsed), 0, (size_t) numToWrite); + numberOfBitsUsed+=BYTES_TO_BITS(numToWrite); + } +} + +/* +// Julius Goryavsky's version of Harley's algorithm. +// 17 elementary ops plus an indexed load, if the machine +// has "and not." + +int nlz10b(unsigned x) { + + static char table[64] = + {32,20,19, u, u,18, u, 7, 10,17, u, u,14, u, 6, u, + u, 9, u,16, u, u, 1,26, u,13, u, u,24, 5, u, u, + u,21, u, 8,11, u,15, u, u, u, u, 2,27, 0,25, u, + 22, u,12, u, u, 3,28, u, 23, u, 4,29, u, u,30,31}; + + x = x | (x >> 1); // Propagate leftmost + x = x | (x >> 2); // 1-bit to the right. + x = x | (x >> 4); + x = x | (x >> 8); + x = x & ~(x >> 16); + x = x*0xFD7049FF; // Activate this line or the following 3. +// x = (x << 9) - x; // Multiply by 511. +// x = (x << 11) - x; // Multiply by 2047. +// x = (x << 14) - x; // Multiply by 16383. + return table[x >> 26]; +} +*/ +int BitStream::NumberOfLeadingZeroes( int8_t x ) {return NumberOfLeadingZeroes((uint8_t)x);} +int BitStream::NumberOfLeadingZeroes( uint8_t x ) +{ + uint8_t y; + int n; + + n = 8; + y = x >> 4; if (y != 0) {n = n - 4; x = y;} + y = x >> 2; if (y != 0) {n = n - 2; x = y;} + y = x >> 1; if (y != 0) return n - 2; + return (int)(n - x); +} +int BitStream::NumberOfLeadingZeroes( int16_t x ) {return NumberOfLeadingZeroes((uint16_t)x);} +int BitStream::NumberOfLeadingZeroes( uint16_t x ) +{ + uint16_t y; + int n; + + n = 16; + y = x >> 8; if (y != 0) {n = n - 8; x = y;} + y = x >> 4; if (y != 0) {n = n - 4; x = y;} + y = x >> 2; if (y != 0) {n = n - 2; x = y;} + y = x >> 1; if (y != 0) return n - 2; + return (int)(n - x); +} +int BitStream::NumberOfLeadingZeroes( int32_t x ) {return NumberOfLeadingZeroes((uint32_t)x);} +int BitStream::NumberOfLeadingZeroes( uint32_t x ) +{ + uint32_t y; + int n; + + n = 32; + y = x >>16; if (y != 0) {n = n -16; x = y;} + y = x >> 8; if (y != 0) {n = n - 8; x = y;} + y = x >> 4; if (y != 0) {n = n - 4; x = y;} + y = x >> 2; if (y != 0) {n = n - 2; x = y;} + y = x >> 1; if (y != 0) return n - 2; + return (int)(n - x); +} +int BitStream::NumberOfLeadingZeroes( int64_t x ) {return NumberOfLeadingZeroes((uint64_t)x);} +int BitStream::NumberOfLeadingZeroes( uint64_t x ) +{ + uint64_t y; + int n; + + n = 64; + y = x >>32; if (y != 0) {n = n -32; x = y;} + y = x >>16; if (y != 0) {n = n -16; x = y;} + y = x >> 8; if (y != 0) {n = n - 8; x = y;} + y = x >> 4; if (y != 0) {n = n - 4; x = y;} + y = x >> 2; if (y != 0) {n = n - 2; x = y;} + y = x >> 1; if (y != 0) return n - 2; + return (int)(n - x); +} + +// Should hit if reads didn't match writes +void BitStream::AssertStreamEmpty( void ) +{ + RakAssert( readOffset == numberOfBitsUsed ); +} +void BitStream::PrintBits( char *out ) const +{ + if ( numberOfBitsUsed <= 0 ) + { + strcpy(out, "No bits\n" ); + return; + } + + unsigned int strIndex=0; + for ( BitSize_t counter = 0; counter < BITS_TO_BYTES( numberOfBitsUsed ) && strIndex < 2000 ; counter++ ) + { + BitSize_t stop; + + if ( counter == ( numberOfBitsUsed - 1 ) >> 3 ) + stop = 8 - ( ( ( numberOfBitsUsed - 1 ) & 7 ) + 1 ); + else + stop = 0; + + for ( BitSize_t counter2 = 7; counter2 >= stop; counter2-- ) + { + if ( ( data[ counter ] >> counter2 ) & 1 ) + out[strIndex++]='1'; + else + out[strIndex++]='0'; + + if (counter2==0) + break; + } + + out[strIndex++]=' '; + } + + out[strIndex++]='\n'; + + out[strIndex++]=0; +} +void BitStream::PrintBits( void ) const +{ + char out[2048]; + PrintBits(out); + RAKNET_DEBUG_PRINTF("%s", out); +} +void BitStream::PrintHex( char *out ) const +{ + BitSize_t i; + for ( i=0; i < GetNumberOfBytesUsed(); i++) + { + sprintf(out+i*3, "%02x ", data[i]); + } +} +void BitStream::PrintHex( void ) const +{ + char out[2048]; + PrintHex(out); + RAKNET_DEBUG_PRINTF("%s", out); +} + +// Exposes the data for you to look at, like PrintBits does. +// Data will point to the stream. Returns the length in bits of the stream. +BitSize_t BitStream::CopyData( unsigned char** _data ) const +{ +#ifdef _DEBUG + RakAssert( numberOfBitsUsed > 0 ); +#endif + + *_data = (unsigned char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES( numberOfBitsUsed ), _FILE_AND_LINE_ ); + memcpy( *_data, data, sizeof(unsigned char) * (size_t) ( BITS_TO_BYTES( numberOfBitsUsed ) ) ); + return numberOfBitsUsed; +} + +// Ignore data we don't intend to read +void BitStream::IgnoreBits( const BitSize_t numberOfBits ) +{ + readOffset += numberOfBits; +} + +void BitStream::IgnoreBytes( const unsigned int numberOfBytes ) +{ + IgnoreBits(BYTES_TO_BITS(numberOfBytes)); +} + +// Move the write pointer to a position on the array. Dangerous if you don't know what you are doing! +// Doesn't work with non-aligned data! +void BitStream::SetWriteOffset( const BitSize_t offset ) +{ + numberOfBitsUsed = offset; +} + +/* +BitSize_t BitStream::GetWriteOffset( void ) const +{ +return numberOfBitsUsed; +} + +// Returns the length in bits of the stream +BitSize_t BitStream::GetNumberOfBitsUsed( void ) const +{ +return GetWriteOffset(); +} + +// Returns the length in bytes of the stream +BitSize_t BitStream::GetNumberOfBytesUsed( void ) const +{ +return BITS_TO_BYTES( numberOfBitsUsed ); +} + +// Returns the number of bits into the stream that we have read +BitSize_t BitStream::GetReadOffset( void ) const +{ +return readOffset; +} + + +// Sets the read bit index +void BitStream::SetReadOffset( const BitSize_t newReadOffset ) +{ +readOffset=newReadOffset; +} + +// Returns the number of bits left in the stream that haven't been read +BitSize_t BitStream::GetNumberOfUnreadBits( void ) const +{ +return numberOfBitsUsed - readOffset; +} +// Exposes the internal data +unsigned char* BitStream::GetData( void ) const +{ +return data; +} + +*/ +// If we used the constructor version with copy data off, this makes sure it is set to on and the data pointed to is copied. +void BitStream::AssertCopyData( void ) +{ + if ( copyData == false ) + { + copyData = true; + + if ( numberOfBitsAllocated > 0 ) + { + unsigned char * newdata = ( unsigned char* ) rakMalloc_Ex( (size_t) BITS_TO_BYTES( numberOfBitsAllocated ), _FILE_AND_LINE_ ); +#ifdef _DEBUG + + RakAssert( data ); +#endif + + memcpy( newdata, data, (size_t) BITS_TO_BYTES( numberOfBitsAllocated ) ); + data = newdata; + } + + else + data = 0; + } +} +bool BitStream::IsNetworkOrderInternal(void) +{ + + + + + + static const bool isNetworkOrder=(htonl(12345) == 12345); + return isNetworkOrder; + +} +void BitStream::ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length) +{ + for (BitSize_t i=0; i < length; i++) + inOutByteArray[i]=inByteArray[length-i-1]; +} +void BitStream::ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length) +{ + unsigned char temp; + BitSize_t i; + for (i=0; i < (length>>1); i++) + { + temp = inOutData[i]; + inOutData[i]=inOutData[length-i-1]; + inOutData[length-i-1]=temp; + } +} + +bool BitStream::Read(char *varString) +{ + return RakString::Deserialize(varString,this); +} +bool BitStream::Read(unsigned char *varString) +{ + return RakString::Deserialize((char*) varString,this); +} +void BitStream::WriteAlignedVar8(const char *inByteArray) +{ + RakAssert((numberOfBitsUsed&7)==0); + AddBitsAndReallocate(1*8); + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; + numberOfBitsUsed+=1*8; +} +bool BitStream::ReadAlignedVar8(char *inOutByteArray) +{ + RakAssert((readOffset&7)==0); + if ( readOffset + 1*8 > numberOfBitsUsed ) + return false; + + inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; + readOffset+=1*8; + return true; +} +void BitStream::WriteAlignedVar16(const char *inByteArray) +{ + RakAssert((numberOfBitsUsed&7)==0); + AddBitsAndReallocate(2*8); +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[1]; + data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[0]; + } + else +#endif + { + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; + data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1]; + } + + numberOfBitsUsed+=2*8; +} +bool BitStream::ReadAlignedVar16(char *inOutByteArray) +{ + RakAssert((readOffset&7)==0); + if ( readOffset + 2*8 > numberOfBitsUsed ) + return false; +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + inOutByteArray[0] = data[( readOffset >> 3 ) + 1]; + inOutByteArray[1] = data[( readOffset >> 3 ) + 0]; + } + else +#endif + { + inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; + inOutByteArray[1] = data[( readOffset >> 3 ) + 1]; + } + + readOffset+=2*8; + return true; +} +void BitStream::WriteAlignedVar32(const char *inByteArray) +{ + RakAssert((numberOfBitsUsed&7)==0); + AddBitsAndReallocate(4*8); +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[3]; + data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[2]; + data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[1]; + data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[0]; + } + else +#endif + { + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; + data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1]; + data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[2]; + data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[3]; + } + + numberOfBitsUsed+=4*8; +} +bool BitStream::ReadAlignedVar32(char *inOutByteArray) +{ + RakAssert((readOffset&7)==0); + if ( readOffset + 4*8 > numberOfBitsUsed ) + return false; +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + inOutByteArray[0] = data[( readOffset >> 3 ) + 3]; + inOutByteArray[1] = data[( readOffset >> 3 ) + 2]; + inOutByteArray[2] = data[( readOffset >> 3 ) + 1]; + inOutByteArray[3] = data[( readOffset >> 3 ) + 0]; + } + else +#endif + { + inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; + inOutByteArray[1] = data[( readOffset >> 3 ) + 1]; + inOutByteArray[2] = data[( readOffset >> 3 ) + 2]; + inOutByteArray[3] = data[( readOffset >> 3 ) + 3]; + } + + readOffset+=4*8; + return true; +} +bool BitStream::ReadFloat16( float &outFloat, float floatMin, float floatMax ) +{ + unsigned short percentile; + if (Read(percentile)) + { + RakAssert(floatMax>floatMin); + outFloat = floatMin + ((float) percentile / 65535.0f) * (floatMax-floatMin); + if (outFloatfloatMax) + outFloat=floatMax; + return true; + } + return false; +} +bool BitStream::SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax) +{ + if (writeToBitstream) + WriteFloat16(inOutFloat, floatMin, floatMax); + else + return ReadFloat16(inOutFloat, floatMin, floatMax); + return true; +} +void BitStream::WriteFloat16( float inOutFloat, float floatMin, float floatMax ) +{ + RakAssert(floatMax>floatMin); + if (inOutFloat>floatMax+.001) + { + RakAssert(inOutFloat<=floatMax+.001); + } + if (inOutFloat=floatMin-.001); + } + float percentile=65535.0f * (inOutFloat-floatMin)/(floatMax-floatMin); + if (percentile<0.0) + percentile=0.0; + if (percentile>65535.0f) + percentile=65535.0f; + Write((unsigned short)percentile); +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // #if _MSC_VER < 1299 diff --git a/project/lib_projects/raknet/jni/RaknetSources/BitStream.h b/project/lib_projects/raknet/jni/RaknetSources/BitStream.h index 4a76f6a..0614a2c 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/BitStream.h +++ b/project/lib_projects/raknet/jni/RaknetSources/BitStream.h @@ -1,2032 +1,2032 @@ -/// \file BitStream.h -/// \brief This class allows you to write and read native types as a string of bits. -/// \details BitStream is used extensively throughout RakNet and is designed to be used by users as well. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// - -#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization -#include "BitStream_NoTemplate.h" -#else - -#ifndef __BITSTREAM_H -#define __BITSTREAM_H - -#include "RakMemoryOverride.h" -#include "RakNetDefines.h" -#include "Export.h" -#include "RakNetTypes.h" -#include "RakString.h" -#include "RakWString.h" -#include "RakAssert.h" -#include -#include - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -// MSWin uses _copysign, others use copysign... -#ifndef _WIN32 -#define _copysign copysign -#endif - -namespace RakNet -{ - /// This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. - /// \sa BitStreamSample.txt - class RAK_DLL_EXPORT BitStream - { - - public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(BitStream) - - /// Default Constructor - BitStream(); - - /// \brief Create the bitstream, with some number of bytes to immediately allocate. - /// \details There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE. - /// In that case all it does is save you one or more realloc calls. - /// \param[in] initialBytesToAllocate the number of bytes to pre-allocate. - BitStream( const unsigned int initialBytesToAllocate ); - - /// \brief Initialize the BitStream, immediately setting the data it contains to a predefined pointer. - /// \details Set \a _copyData to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data. - /// You shouldn't call Write functions with \a _copyData as false, as this will write to unallocated memory - /// 99% of the time you will use this function to cast Packet::data to a bitstream for reading, in which case you should write something as follows: - /// \code - /// RakNet::BitStream bs(packet->data, packet->length, false); - /// \endcode - /// \param[in] _data An array of bytes. - /// \param[in] lengthInBytes Size of the \a _data. - /// \param[in] _copyData true or false to make a copy of \a _data or not. - BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData ); - - // Destructor - ~BitStream(); - - /// Resets the bitstream for reuse. - void Reset( void ); - - /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutTemplateVar The value to write - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool Serialize(bool writeToBitstream, templateType &inOutTemplateVar); - - /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutCurrentValue The current value to write - /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue); - - /// \brief Bidirectional version of SerializeDelta when you don't know what the last value is, or there is no last value. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutCurrentValue The current value to write - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue); - - /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutTemplateVar The value to write - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar); - - /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutCurrentValue The current value to write - /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue); - - /// \brief Save as SerializeCompressedDelta(templateType ¤tValue, const templateType &lastValue) when we have an unknown second parameter - /// \return true on data read. False on insufficient data in bitstream - template - bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutTemplateVar); - - /// \brief Bidirectional serialize/deserialize an array or casted stream or raw data. This does NOT do endian swapping. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutByteArray a byte buffer - /// \param[in] numberOfBytes the size of \a input in bytes - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - bool Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes ); - - /// \brief Serialize a float into 2 bytes, spanning the range between \a floatMin and \a floatMax - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutFloat The float to write - /// \param[in] floatMin Predetermined minimum value of f - /// \param[in] floatMax Predetermined maximum value of f - bool SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax); - - /// Serialize one type casted to another (smaller) type, to save bandwidth - /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t - /// Example: int num=53; SerializeCasted(true, num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes) - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] value The value to serialize - template - bool SerializeCasted( bool writeToBitstream, sourceType &value ); - - /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range - /// Then serialize only those bits - /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum - /// \param[in] minimum Minimum value of \a value - /// \param[in] maximum Maximum value of \a value - /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates - template - bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); - /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum - template - bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); - - /// \brief Bidirectional serialize/deserialize a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. - /// \details Will further compress y or z axis aligned vectors. - /// Accurate to 1/32767.5. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template // templateType for this function must be a float or double - bool SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ); - - /// \brief Bidirectional serialize/deserialize a vector, using 10 bytes instead of 12. - /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template // templateType for this function must be a float or double - bool SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ); - - /// \brief Bidirectional serialize/deserialize a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] w w - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template // templateType for this function must be a float or double - bool SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z); - - /// \brief Bidirectional serialize/deserialize an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each. - /// \details Use 6 bytes instead of 36 - /// Lossy, although the result is renormalized - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool SerializeOrthMatrix( - bool writeToBitstream, - templateType &m00, templateType &m01, templateType &m02, - templateType &m10, templateType &m11, templateType &m12, - templateType &m20, templateType &m21, templateType &m22 ); - - /// \brief Bidirectional serialize/deserialize numberToSerialize bits to/from the input. - /// \details Right aligned data means in the case of a partial byte, the bits are aligned - /// from the right (bit 0) rather than the left (as in the normal - /// internal representation) You would set this to true when - /// writing user data, and false when copying bitstream data, such - /// as writing one bitstream to another - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutByteArray The data - /// \param[in] numberOfBitsToSerialize The number of bits to write - /// \param[in] rightAlignedBits if true data will be right aligned - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - bool SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits = true ); - - /// \brief Write any integral type to a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] inTemplateVar The value to write - template - void Write(const templateType &inTemplateVar); - - /// \brief Write the dereferenced pointer to any integral type to a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] inTemplateVar The value to write - template - void WritePtr(templateType *inTemplateVar); - - /// \brief Write any integral type to a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template - void WriteDelta(const templateType ¤tValue, const templateType &lastValue); - - /// \brief WriteDelta when you don't know what the last value is, or there is no last value. - /// \param[in] currentValue The current value to write - template - void WriteDelta(const templateType ¤tValue); - - /// \brief Write any integral type to a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// \param[in] inTemplateVar The value to write - template - void WriteCompressed(const templateType &inTemplateVar); - - /// \brief Write any integral type to a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template - void WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue); - - /// \brief Save as WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) when we have an unknown second parameter - template - void WriteCompressedDelta(const templateType ¤tValue); - - /// \brief Read any integral type from a bitstream. - /// \details Define __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] outTemplateVar The value to read - /// \return true on success, false on failure. - template - bool Read(templateType &outTemplateVar); - - /// \brief Read any integral type from a bitstream. - /// \details If the written value differed from the value compared against in the write function, - /// var will be updated. Otherwise it will retain the current value. - /// ReadDelta is only valid from a previous call to WriteDelta - /// \param[in] outTemplateVar The value to read - /// \return true on success, false on failure. - template - bool ReadDelta(templateType &outTemplateVar); - - /// \brief Read any integral type from a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] outTemplateVar The value to read - /// \return true on success, false on failure. - template - bool ReadCompressed(templateType &outTemplateVar); - - /// \brief Read any integral type from a bitstream. - /// \details If the written value differed from the value compared against in the write function, - /// var will be updated. Otherwise it will retain the current value. - /// the current value will be updated. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// ReadCompressedDelta is only valid from a previous call to WriteDelta - /// \param[in] outTemplateVar The value to read - /// \return true on success, false on failure. - template - bool ReadCompressedDelta(templateType &outTemplateVar); - - /// \brief Read one bitstream to another. - /// \param[in] numberOfBits bits to read - /// \param bitStream the bitstream to read into from - /// \return true on success, false on failure. - bool Read( BitStream *bitStream, BitSize_t numberOfBits ); - bool Read( BitStream *bitStream ); - bool Read( BitStream &bitStream, BitSize_t numberOfBits ); - bool Read( BitStream &bitStream ); - - /// \brief Write an array or casted stream or raw data. This does NOT do endian swapping. - /// \param[in] inputByteArray a byte buffer - /// \param[in] numberOfBytes the size of \a input in bytes - void Write( const char* inputByteArray, const unsigned int numberOfBytes ); - - /// \brief Write one bitstream to another. - /// \param[in] numberOfBits bits to write - /// \param bitStream the bitstream to copy from - void Write( BitStream *bitStream, BitSize_t numberOfBits ); - void Write( BitStream *bitStream ); - void Write( BitStream &bitStream, BitSize_t numberOfBits ); - void Write( BitStream &bitStream );\ - - /// \brief Write a float into 2 bytes, spanning the range between \a floatMin and \a floatMax - /// \param[in] x The float to write - /// \param[in] floatMin Predetermined minimum value of f - /// \param[in] floatMax Predetermined maximum value of f - void WriteFloat16( float x, float floatMin, float floatMax ); - - /// Write one type serialized as another (smaller) type, to save bandwidth - /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t - /// Example: int num=53; WriteCasted(num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes) - /// \param[in] value The value to write - template - void WriteCasted( const sourceType &value ); - - /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range - /// Then write only those bits - /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program - /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum - /// \param[in] minimum Minimum value of \a value - /// \param[in] maximum Maximum value of \a value - /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Read(). - template - void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); - /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum - template - void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); - - /// \brief Write a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. - /// \details Will further compress y or z axis aligned vectors. - /// Accurate to 1/32767.5. - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - template // templateType for this function must be a float or double - void WriteNormVector( templateType x, templateType y, templateType z ); - - /// \brief Write a vector, using 10 bytes instead of 12. - /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, - /// so only use if accuracy is not important. - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - template // templateType for this function must be a float or double - void WriteVector( templateType x, templateType y, templateType z ); - - /// \brief Write a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. - /// \param[in] w w - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - template // templateType for this function must be a float or double - void WriteNormQuat( templateType w, templateType x, templateType y, templateType z); - - /// \brief Write an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each. - /// \details Use 6 bytes instead of 36 - /// Lossy, although the result is renormalized - template // templateType for this function must be a float or double - void WriteOrthMatrix( - templateType m00, templateType m01, templateType m02, - templateType m10, templateType m11, templateType m12, - templateType m20, templateType m21, templateType m22 ); - - /// \brief Read an array or casted stream of byte. - /// \details The array is raw data. There is no automatic endian conversion with this function - /// \param[in] output The result byte array. It should be larger than @em numberOfBytes. - /// \param[in] numberOfBytes The number of byte to read - /// \return true on success false if there is some missing bytes. - bool Read( char* output, const unsigned int numberOfBytes ); - - /// \brief Read a float into 2 bytes, spanning the range between \a floatMin and \a floatMax - /// \param[in] outFloat The float to read - /// \param[in] floatMin Predetermined minimum value of f - /// \param[in] floatMax Predetermined maximum value of f - bool ReadFloat16( float &outFloat, float floatMin, float floatMax ); - - /// Read one type serialized to another (smaller) type, to save bandwidth - /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t - /// Example: int num; ReadCasted(num); would read 1 bytefrom the stream, and put the value in an integer - /// \param[in] value The value to write - template - bool ReadCasted( sourceType &value ); - - /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range - /// Then read only those bits - /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program - /// \param[in] value Integer value to read, which should be between \a minimum and \a maximum - /// \param[in] minimum Minimum value of \a value - /// \param[in] maximum Maximum value of \a value - /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Write(). - template - bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); - /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum - template - bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); - - /// \brief Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. - /// \details Will further compress y or z axis aligned vectors. - /// Accurate to 1/32767.5. - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool ReadNormVector( templateType &x, templateType &y, templateType &z ); - - /// \brief Read 3 floats or doubles, using 10 bytes, where those float or doubles comprise a vector. - /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, - /// so only use if accuracy is not important. - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool ReadVector( templateType &x, templateType &y, templateType &z ); - - /// \brief Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. - /// \param[in] w w - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z); - - /// \brief Read an orthogonal matrix from a quaternion, reading 3 components of the quaternion in 2 bytes each and extrapolatig the 4th. - /// \details Use 6 bytes instead of 36 - /// Lossy, although the result is renormalized - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool ReadOrthMatrix( - templateType &m00, templateType &m01, templateType &m02, - templateType &m10, templateType &m11, templateType &m12, - templateType &m20, templateType &m21, templateType &m22 ); - - /// \brief Sets the read pointer back to the beginning of your data. - void ResetReadPointer( void ); - - /// \brief Sets the write pointer back to the beginning of your data. - void ResetWritePointer( void ); - - /// \brief This is good to call when you are done with the stream to make - /// sure you didn't leave any data left over void - void AssertStreamEmpty( void ); - - /// \brief RAKNET_DEBUG_PRINTF the bits in the stream. Great for debugging. - void PrintBits( char *out ) const; - void PrintBits( void ) const; - void PrintHex( char *out ) const; - void PrintHex( void ) const; - - /// \brief Ignore data we don't intend to read - /// \param[in] numberOfBits The number of bits to ignore - void IgnoreBits( const BitSize_t numberOfBits ); - - /// \brief Ignore data we don't intend to read - /// \param[in] numberOfBits The number of bytes to ignore - void IgnoreBytes( const unsigned int numberOfBytes ); - - /// \brief Move the write pointer to a position on the array. - /// \param[in] offset the offset from the start of the array. - /// \attention - /// \details Dangerous if you don't know what you are doing! - /// For efficiency reasons you can only write mid-stream if your data is byte aligned. - void SetWriteOffset( const BitSize_t offset ); - - /// \brief Returns the length in bits of the stream - inline BitSize_t GetNumberOfBitsUsed( void ) const {return GetWriteOffset();} - inline BitSize_t GetWriteOffset( void ) const {return numberOfBitsUsed;} - - /// \brief Returns the length in bytes of the stream - inline BitSize_t GetNumberOfBytesUsed( void ) const {return BITS_TO_BYTES( numberOfBitsUsed );} - - /// \brief Returns the number of bits into the stream that we have read - inline BitSize_t GetReadOffset( void ) const {return readOffset;} - - /// \brief Sets the read bit index - void SetReadOffset( const BitSize_t newReadOffset ) {readOffset=newReadOffset;} - - /// \brief Returns the number of bits left in the stream that haven't been read - inline BitSize_t GetNumberOfUnreadBits( void ) const {return numberOfBitsUsed - readOffset;} - - /// \brief Makes a copy of the internal data for you \a _data will point to - /// the stream. Partial bytes are left aligned. - /// \param[out] _data The allocated copy of GetData() - /// \return The length in bits of the stream. - BitSize_t CopyData( unsigned char** _data ) const; - - /// \internal - /// Set the stream to some initial data. - void SetData( unsigned char *inByteArray ); - - /// Gets the data that BitStream is writing to / reading from. - /// Partial bytes are left aligned. - /// \return A pointer to the internal state - inline unsigned char* GetData( void ) const {return data;} - - /// \brief Write numberToWrite bits from the input source. - /// \details Right aligned data means in the case of a partial byte, the bits are aligned - /// from the right (bit 0) rather than the left (as in the normal - /// internal representation) You would set this to true when - /// writing user data, and false when copying bitstream data, such - /// as writing one bitstream to another. - /// \param[in] inByteArray The data - /// \param[in] numberOfBitsToWrite The number of bits to write - /// \param[in] rightAlignedBits if true data will be right aligned - void WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits = true ); - - /// \brief Align the bitstream to the byte boundary and then write the - /// specified number of bits. - /// \details This is faster than WriteBits but - /// wastes the bits to do the alignment and requires you to call - /// ReadAlignedBits at the corresponding read position. - /// \param[in] inByteArray The data - /// \param[in] numberOfBytesToWrite The size of input. - void WriteAlignedBytes( const unsigned char *inByteArray, const unsigned int numberOfBytesToWrite ); - - // Endian swap bytes already in the bitstream - void EndianSwapBytes( int byteOffset, int length ); - - /// \brief Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite - /// \param[in] inByteArray The data - /// \param[in] inputLength The size of input. - /// \param[in] maxBytesToWrite Max bytes to write - void WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite ); - - /// \brief Read bits, starting at the next aligned bits. - /// \details Note that the modulus 8 starting offset of the sequence must be the same as - /// was used with WriteBits. This will be a problem with packet - /// coalescence unless you byte align the coalesced packets. - /// \param[in] inOutByteArray The byte array larger than @em numberOfBytesToRead - /// \param[in] numberOfBytesToRead The number of byte to read from the internal state - /// \return true if there is enough byte. - bool ReadAlignedBytes( unsigned char *inOutByteArray, const unsigned int numberOfBytesToRead ); - - /// \brief Reads what was written by WriteAlignedBytesSafe. - /// \param[in] inOutByteArray The data - /// \param[in] maxBytesToRead Maximum number of bytes to read - /// \return true on success, false on failure. - bool ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead ); - bool ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ); - - /// \brief Same as ReadAlignedBytesSafe() but allocates the memory for you using new, rather than assuming it is safe to write to - /// \param[in] outByteArray outByteArray will be deleted if it is not a pointer to 0 - /// \return true on success, false on failure. - bool ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead ); - bool ReadAlignedBytesSafeAlloc( char **outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ); - - /// \brief Align the next write and/or read to a byte boundary. - /// \details This can be used to 'waste' bits to byte align for efficiency reasons It - /// can also be used to force coalesced bitstreams to start on byte - /// boundaries so so WriteAlignedBits and ReadAlignedBits both - /// calculate the same offset when aligning. - inline void AlignWriteToByteBoundary( void ) {numberOfBitsUsed += 8 - ( (( numberOfBitsUsed - 1 ) & 7) + 1 );} - - /// \brief Align the next write and/or read to a byte boundary. - /// \details This can be used to 'waste' bits to byte align for efficiency reasons It - /// can also be used to force coalesced bitstreams to start on byte - /// boundaries so so WriteAlignedBits and ReadAlignedBits both - /// calculate the same offset when aligning. - inline void AlignReadToByteBoundary( void ) {readOffset += 8 - ( (( readOffset - 1 ) & 7 ) + 1 );} - - /// \brief Read \a numberOfBitsToRead bits to the output source. - /// \details alignBitsToRight should be set to true to convert internal - /// bitstream data to userdata. It should be false if you used - /// WriteBits with rightAlignedBits false - /// \param[in] inOutByteArray The resulting bits array - /// \param[in] numberOfBitsToRead The number of bits to read - /// \param[in] alignBitsToRight if true bits will be right aligned. - /// \return true if there is enough bits to read - bool ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight = true ); - - /// \brief Write a 0 - void Write0( void ); - - /// \brief Write a 1 - void Write1( void ); - - /// \brief Reads 1 bit and returns true if that bit is 1 and false if it is 0. - bool ReadBit( void ); - - /// \brief If we used the constructor version with copy data off, this - /// *makes sure it is set to on and the data pointed to is copied. - void AssertCopyData( void ); - - /// \brief Use this if you pass a pointer copy to the constructor - /// *(_copyData==false) and want to overallocate to prevent - /// reallocation. - void SetNumberOfBitsAllocated( const BitSize_t lengthInBits ); - - /// \brief Reallocates (if necessary) in preparation of writing numberOfBitsToWrite - void AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite ); - - /// \internal - /// \return How many bits have been allocated internally - BitSize_t GetNumberOfBitsAllocated(void) const; - - /// \brief Read strings, non reference. - bool Read(char *varString); - bool Read(unsigned char *varString); - - /// Write zeros until the bitstream is filled up to \a bytes - void PadWithZeroToByteLength( unsigned int bytes ); - - /// Get the number of leading zeros for a number - /// \param[in] x Number to test - static int NumberOfLeadingZeroes( uint8_t x ); - static int NumberOfLeadingZeroes( uint16_t x ); - static int NumberOfLeadingZeroes( uint32_t x ); - static int NumberOfLeadingZeroes( uint64_t x ); - static int NumberOfLeadingZeroes( int8_t x ); - static int NumberOfLeadingZeroes( int16_t x ); - static int NumberOfLeadingZeroes( int32_t x ); - static int NumberOfLeadingZeroes( int64_t x ); - - /// \internal Unrolled inner loop, for when performance is critical - void WriteAlignedVar8(const char *inByteArray); - /// \internal Unrolled inner loop, for when performance is critical - bool ReadAlignedVar8(char *inOutByteArray); - /// \internal Unrolled inner loop, for when performance is critical - void WriteAlignedVar16(const char *inByteArray); - /// \internal Unrolled inner loop, for when performance is critical - bool ReadAlignedVar16(char *inOutByteArray); - /// \internal Unrolled inner loop, for when performance is critical - void WriteAlignedVar32(const char *inByteArray); - /// \internal Unrolled inner loop, for when performance is critical - bool ReadAlignedVar32(char *inOutByteArray); - - inline void Write(const char * const inStringVar) - { - RakString::Serialize(inStringVar, this); - } - inline void Write(const wchar_t * const inStringVar) - { - RakWString::Serialize(inStringVar, this); - } - inline void Write(const unsigned char * const inTemplateVar) - { - Write((const char*)inTemplateVar); - } - inline void Write(char * const inTemplateVar) - { - Write((const char*)inTemplateVar); - } - inline void Write(unsigned char * const inTemplateVar) - { - Write((const char*)inTemplateVar); - } - inline void WriteCompressed(const char * const inStringVar) - { - RakString::SerializeCompressed(inStringVar,this,0,false); - } - inline void WriteCompressed(const wchar_t * const inStringVar) - { - RakWString::Serialize(inStringVar,this); - } - inline void WriteCompressed(const unsigned char * const inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - inline void WriteCompressed(char * const inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - inline void WriteCompressed(unsigned char * const inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - - /// ---- Member function template specialization declarations ---- - // Used for VC7 -#if defined(_MSC_VER) && _MSC_VER == 1300 - /// Write a bool to a bitstream. - /// \param[in] var The value to write - template <> - void Write(const bool &var); - - /// Write a systemAddress to a bitstream - /// \param[in] var The value to write - template <> - void Write(const SystemAddress &var); - - /// Write a uint24_t to a bitstream - /// \param[in] var The value to write - template <> - void Write(const uint24_t &var); - - /// Write a RakNetGUID to a bitsteam - /// \param[in] var The value to write - template <> - void Write(const RakNetGuid &var); - - /// Write a string to a bitstream - /// \param[in] var The value to write - template <> - void Write(const char* const &var); - template <> - void Write(const unsigned char* const &var); - template <> - void Write(char* const &var); - template <> - void Write(unsigned char* const &var); - template <> - void Write(const RakString &var); - template <> - void Write(const RakWString &var); - - /// \brief Write a systemAddress. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - void WriteDelta(const SystemAddress ¤tValue, const SystemAddress &lastValue); - - template <> - void WriteDelta(const uint24_t ¤tValue, const uint24_t &lastValue); - - template <> - void WriteDelta(const RakNetGUID ¤tValue, const RakNetGUID &lastValue); - - /// \brief Write a bool delta. - /// \details Same thing as just calling Write - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - void WriteDelta(const bool ¤tValue, const bool &lastValue); - - template <> - void WriteCompressed(const SystemAddress &var); - - template <> - void WriteCompressed(const uint24_t &var); - - template <> - void WriteCompressed(const RakNetGUID &var); - - template <> - void WriteCompressed(const bool &var); - - /// For values between -1 and 1 - template <> - void WriteCompressed(const float &var); - - /// For values between -1 and 1 - template <> - void WriteCompressed(const double &var); - - /// Compressed string - template <> - void WriteCompressed(const char* var); - template <> - void WriteCompressed(const unsigned char* var); - template <> - void WriteCompressed(char* var); - template <> - void WriteCompressed(unsigned char* var); - template <> - void WriteCompressed(const RakString &var); - template <> - void WriteCompressed(const RakWString &var); - - /// \brief Write a bool delta. - /// \details Same thing as just calling Write - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - void WriteCompressedDelta(const bool ¤tValue, const bool &lastValue); - - /// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue) - /// when we have an unknown second bool - template <> - void WriteCompressedDelta(const bool ¤tValue); - - /// \brief Read a bool from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool Read(bool &var); - - /// \brief Read a systemAddress from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool Read(SystemAddress &var); - - template <> - bool Read(uint24_t &var); - - template <> - bool Read(RakNetGUID &var); - - /// \brief Read a String from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool Read(char *&var); - template <> - bool Read(wchar_t *&var); - template <> - bool Read(unsigned char *&var); - template <> - bool Read(RakString &var); - template <> - bool Read(RakWString &var); - - /// \brief Read a bool from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool ReadDelta(bool &var); - - template <> - bool ReadCompressed(SystemAddress &var); - - template <> - bool ReadCompressed(uint24_t &var); - - template <> - bool ReadCompressed(RakNetGUID &var); - - template <> - bool ReadCompressed(bool &var); - - template <> - bool ReadCompressed(float &var); - - /// For values between -1 and 1 - /// \return true on success, false on failure. - template <> - bool ReadCompressed(double &var); - - template <> - bool ReadCompressed(char* &var); - template <> - bool ReadCompressed(wchar_t* &var); - template <> - bool ReadCompressed(unsigned char *&var); - template <> - bool ReadCompressed(RakString &var); - template <> - bool ReadCompressed(RakWString &var); - - /// \brief Read a bool from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool ReadCompressedDelta(bool &var); -#endif - - inline static bool DoEndianSwap(void) { -#ifndef __BITSTREAM_NATIVE_END - return IsNetworkOrder()==false; -#else - return false; -#endif - } - inline static bool IsBigEndian(void) - { - return IsNetworkOrder(); - } - inline static bool IsNetworkOrder(void) {static const bool r = IsNetworkOrderInternal(); return r;} - // Not inline, won't compile on PC due to winsock include errors - static bool IsNetworkOrderInternal(void); - static void ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length); - static void ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length); - - private: - - BitStream( const BitStream &invalid) { - (void) invalid; - RakAssert(0); - } - - /// \brief Assume the input source points to a native type, compress and write it. - void WriteCompressed( const unsigned char* inByteArray, const unsigned int size, const bool unsignedData ); - - /// \brief Assume the input source points to a compressed native type. Decompress and read it. - bool ReadCompressed( unsigned char* inOutByteArray, const unsigned int size, const bool unsignedData ); - - - BitSize_t numberOfBitsUsed; - - BitSize_t numberOfBitsAllocated; - - BitSize_t readOffset; - - unsigned char *data; - - /// true if the internal buffer is copy of the data passed to the constructor - bool copyData; - - /// BitStreams that use less than BITSTREAM_STACK_ALLOCATION_SIZE use the stack, rather than the heap to store data. It switches over if BITSTREAM_STACK_ALLOCATION_SIZE is exceeded - unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE]; - }; - - template - inline bool BitStream::Serialize(bool writeToBitstream, templateType &inOutTemplateVar) - { - if (writeToBitstream) - Write(inOutTemplateVar); - else - return Read(inOutTemplateVar); - return true; - } - - template - inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue) - { - if (writeToBitstream) - WriteDelta(inOutCurrentValue, lastValue); - else - return ReadDelta(inOutCurrentValue); - return true; - } - - template - inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue) - { - if (writeToBitstream) - WriteDelta(inOutCurrentValue); - else - return ReadDelta(inOutCurrentValue); - return true; - } - - template - inline bool BitStream::SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar) - { - if (writeToBitstream) - WriteCompressed(inOutTemplateVar); - else - return ReadCompressed(inOutTemplateVar); - return true; - } - - template - inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue) - { - if (writeToBitstream) - WriteCompressedDelta(inOutCurrentValue,lastValue); - else - return ReadCompressedDelta(inOutCurrentValue); - return true; - } -//Stoppedhere - template - inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue) - { - if (writeToBitstream) - WriteCompressedDelta(inOutCurrentValue); - else - return ReadCompressedDelta(inOutCurrentValue); - return true; - } - - inline bool BitStream::Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes ) - { - if (writeToBitstream) - Write(inOutByteArray, numberOfBytes); - else - return Read(inOutByteArray, numberOfBytes); - return true; - } - - template - bool BitStream::SerializeCasted( bool writeToBitstream, sourceType &value ) - { - if (writeToBitstream) WriteCasted(value); - else return ReadCasted(value); - return true; - } - - template - bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange ) - { - int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); - return SerializeBitsFromIntegerRange(writeToBitstream,value,minimum,maximum,requiredBits,allowOutsideRange); - } - template - bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange ) - { - if (writeToBitstream) WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); - else return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); - return true; - } - - template - inline bool BitStream::SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ) - { - if (writeToBitstream) - WriteNormVector(x,y,z); - else - return ReadNormVector(x,y,z); - return true; - } - - template - inline bool BitStream::SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ) - { - if (writeToBitstream) - WriteVector(x,y,z); - else - return ReadVector(x,y,z); - return true; - } - - template - inline bool BitStream::SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z) - { - if (writeToBitstream) - WriteNormQuat(w,x,y,z); - else - return ReadNormQuat(w,x,y,z); - return true; - } - - template - inline bool BitStream::SerializeOrthMatrix( - bool writeToBitstream, - templateType &m00, templateType &m01, templateType &m02, - templateType &m10, templateType &m11, templateType &m12, - templateType &m20, templateType &m21, templateType &m22 ) - { - if (writeToBitstream) - WriteOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); - else - return ReadOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); - return true; - } - - inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits ) - { - if (writeToBitstream) - WriteBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits); - else - return ReadBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits); - return true; - } - - template - inline void BitStream::Write(const templateType &inTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(inTemplateVar)==1) - WriteBits( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType)); - WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ); - } - else -#endif - WriteBits( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true ); - } - } - - template - inline void BitStream::WritePtr(templateType *inTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(templateType)==1) - WriteBits( ( unsigned char* ) inTemplateVar, sizeof( templateType ) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - ReverseBytes((unsigned char*) inTemplateVar, output, sizeof(templateType)); - WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ); - } - else -#endif - WriteBits( ( unsigned char* ) inTemplateVar, sizeof(templateType) * 8, true ); - } - } - - /// \brief Write a bool to a bitstream. - /// \param[in] inTemplateVar The value to write - template <> - inline void BitStream::Write(const bool &inTemplateVar) - { - if ( inTemplateVar ) - Write1(); - else - Write0(); - } - - - /// \brief Write a systemAddress to a bitstream. - /// \param[in] inTemplateVar The value to write - template <> - inline void BitStream::Write(const SystemAddress &inTemplateVar) - { - Write(inTemplateVar.GetIPVersion()); - if (inTemplateVar.GetIPVersion()==4) - { - // Hide the address so routers don't modify it - SystemAddress var2=inTemplateVar; - uint32_t binaryAddress=~inTemplateVar.address.addr4.sin_addr.s_addr; - // Don't endian swap the address or port - WriteBits((unsigned char*)&binaryAddress, sizeof(binaryAddress)*8, true); - unsigned short p = var2.GetPortNetworkOrder(); - WriteBits((unsigned char*)&p, sizeof(unsigned short)*8, true); - } - else - { -#if RAKNET_SUPPORT_IPV6==1 - // Don't endian swap - WriteBits((const unsigned char*) &inTemplateVar.address.addr6, sizeof(inTemplateVar.address.addr6)*8, true); -#endif - } - } - - template <> - inline void BitStream::Write(const uint24_t &inTemplateVar) - { - AlignWriteToByteBoundary(); - AddBitsAndReallocate(3*8); - - if (IsBigEndian()==false) - { - data[( numberOfBitsUsed >> 3 ) + 0] = ((char *)&inTemplateVar.val)[0]; - data[( numberOfBitsUsed >> 3 ) + 1] = ((char *)&inTemplateVar.val)[1]; - data[( numberOfBitsUsed >> 3 ) + 2] = ((char *)&inTemplateVar.val)[2]; - } - else - { - data[( numberOfBitsUsed >> 3 ) + 0] = ((char *)&inTemplateVar.val)[3]; - data[( numberOfBitsUsed >> 3 ) + 1] = ((char *)&inTemplateVar.val)[2]; - data[( numberOfBitsUsed >> 3 ) + 2] = ((char *)&inTemplateVar.val)[1]; - } - - numberOfBitsUsed+=3*8; - } - - template <> - inline void BitStream::Write(const RakNetGUID &inTemplateVar) - { - Write(inTemplateVar.g); - } - - /// \brief Write a string to a bitstream. - /// \param[in] var The value to write - template <> - inline void BitStream::Write(const RakString &inTemplateVar) - { - inTemplateVar.Serialize(this); - } - template <> - inline void BitStream::Write(const RakWString &inTemplateVar) - { - inTemplateVar.Serialize(this); - } - template <> - inline void BitStream::Write(const char * const &inStringVar) - { - RakString::Serialize(inStringVar, this); - } - template <> - inline void BitStream::Write(const wchar_t * const &inStringVar) - { - RakWString::Serialize(inStringVar, this); - } - template <> - inline void BitStream::Write(const unsigned char * const &inTemplateVar) - { - Write((const char*)inTemplateVar); - } - template <> - inline void BitStream::Write(char * const &inTemplateVar) - { - Write((const char*)inTemplateVar); - } - template <> - inline void BitStream::Write(unsigned char * const &inTemplateVar) - { - Write((const char*)inTemplateVar); - } - - /// \brief Write any integral type to a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template - inline void BitStream::WriteDelta(const templateType ¤tValue, const templateType &lastValue) - { - if (currentValue==lastValue) - { - Write(false); - } - else - { - Write(true); - Write(currentValue); - } - } - - /// \brief Write a bool delta. Same thing as just calling Write - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - inline void BitStream::WriteDelta(const bool ¤tValue, const bool &lastValue) - { - (void) lastValue; - - Write(currentValue); - } - - /// \brief WriteDelta when you don't know what the last value is, or there is no last value. - /// \param[in] currentValue The current value to write - template - inline void BitStream::WriteDelta(const templateType ¤tValue) - { - Write(true); - Write(currentValue); - } - - /// \brief Write any integral type to a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] inTemplateVar The value to write - template - inline void BitStream::WriteCompressed(const templateType &inTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(inTemplateVar)==1) - WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END -#ifdef _MSC_VER -#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data -#endif - - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType)); - WriteCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true ); - } - else -#endif - WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true ); - } - } - - template <> - inline void BitStream::WriteCompressed(const SystemAddress &inTemplateVar) - { - Write(inTemplateVar); - } - - template <> - inline void BitStream::WriteCompressed(const RakNetGUID &inTemplateVar) - { - Write(inTemplateVar); - } - - template <> - inline void BitStream::WriteCompressed(const uint24_t &var) - { - Write(var); - } - - template <> - inline void BitStream::WriteCompressed(const bool &inTemplateVar) - { - Write(inTemplateVar); - } - - /// For values between -1 and 1 - template <> - inline void BitStream::WriteCompressed(const float &inTemplateVar) - { - RakAssert(inTemplateVar > -1.01f && inTemplateVar < 1.01f); - float varCopy=inTemplateVar; - if (varCopy < -1.0f) - varCopy=-1.0f; - if (varCopy > 1.0f) - varCopy=1.0f; - Write((unsigned short)((varCopy+1.0f)*32767.5f)); - } - - /// For values between -1 and 1 - template <> - inline void BitStream::WriteCompressed(const double &inTemplateVar) - { - RakAssert(inTemplateVar > -1.01 && inTemplateVar < 1.01); - double varCopy=inTemplateVar; - if (varCopy < -1.0f) - varCopy=-1.0f; - if (varCopy > 1.0f) - varCopy=1.0f; - Write((uint32_t)((varCopy+1.0)*2147483648.0)); - } - - /// Compress the string - template <> - inline void BitStream::WriteCompressed(const RakString &inTemplateVar) - { - inTemplateVar.SerializeCompressed(this,0,false); - } - template <> - inline void BitStream::WriteCompressed(const RakWString &inTemplateVar) - { - inTemplateVar.Serialize(this); - } - template <> - inline void BitStream::WriteCompressed(const char * const &inStringVar) - { - RakString::SerializeCompressed(inStringVar,this,0,false); - } - template <> - inline void BitStream::WriteCompressed(const wchar_t * const &inStringVar) - { - RakWString::Serialize(inStringVar,this); - } - template <> - inline void BitStream::WriteCompressed(const unsigned char * const &inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - template <> - inline void BitStream::WriteCompressed(char * const &inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - template <> - inline void BitStream::WriteCompressed(unsigned char * const &inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - - - /// \brief Write any integral type to a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template - inline void BitStream::WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) - { - if (currentValue==lastValue) - { - Write(false); - } - else - { - Write(true); - WriteCompressed(currentValue); - } - } - - /// \brief Write a bool delta. Same thing as just calling Write - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - inline void BitStream::WriteCompressedDelta(const bool ¤tValue, const bool &lastValue) - { - (void) lastValue; - - Write(currentValue); - } - - /// \brief Save as WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) - /// when we have an unknown second parameter - template - inline void BitStream::WriteCompressedDelta(const templateType ¤tValue) - { - Write(true); - WriteCompressed(currentValue); - } - - /// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue) - /// when we have an unknown second bool - template <> - inline void BitStream::WriteCompressedDelta(const bool ¤tValue) - { - Write(currentValue); - } - - /// \brief Read any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] outTemplateVar The value to read - template - inline bool BitStream::Read(templateType &outTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(outTemplateVar)==1) - return ReadBits( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END -#ifdef _MSC_VER -#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data -#endif - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - if (ReadBits( ( unsigned char* ) output, sizeof(templateType) * 8, true )) - { - ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType)); - return true; - } - return false; - } - else -#endif - return ReadBits( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true ); - } - } - - /// \brief Read a bool from a bitstream. - /// \param[in] outTemplateVar The value to read - template <> - inline bool BitStream::Read(bool &outTemplateVar) - { - if ( readOffset + 1 > numberOfBitsUsed ) - return false; - - if ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) // Is it faster to just write it out here? - outTemplateVar = true; - else - outTemplateVar = false; - - // Has to be on a different line for Mac - readOffset++; - - return true; - } - - /// \brief Read a systemAddress from a bitstream. - /// \param[in] outTemplateVar The value to read - template <> - inline bool BitStream::Read(SystemAddress &outTemplateVar) - { - unsigned char ipVersion; - Read(ipVersion); - if (ipVersion==4) - { - outTemplateVar.address.addr4.sin_family=AF_INET; - // Read(var.binaryAddress); - // Don't endian swap the address or port - uint32_t binaryAddress; - ReadBits( ( unsigned char* ) & binaryAddress, sizeof(binaryAddress) * 8, true ); - // Unhide the IP address, done to prevent routers from changing it - outTemplateVar.address.addr4.sin_addr.s_addr=~binaryAddress; - bool b = ReadBits(( unsigned char* ) & outTemplateVar.address.addr4.sin_port, sizeof(outTemplateVar.address.addr4.sin_port) * 8, true); - outTemplateVar.debugPort=ntohs(outTemplateVar.address.addr4.sin_port); - return b; - } - else - { -#if RAKNET_SUPPORT_IPV6==1 - bool b = ReadBits((unsigned char*) &outTemplateVar.address.addr6, sizeof(outTemplateVar.address.addr6)*8, true); - outTemplateVar.debugPort=ntohs(outTemplateVar.address.addr6.sin6_port); - return b; -#else - return false; -#endif - } - } - - template <> - inline bool BitStream::Read(uint24_t &outTemplateVar) - { - AlignReadToByteBoundary(); - if ( readOffset + 3*8 > numberOfBitsUsed ) - return false; - - if (IsBigEndian()==false) - { - ((char *)&outTemplateVar.val)[0]=data[ (readOffset >> 3) + 0]; - ((char *)&outTemplateVar.val)[1]=data[ (readOffset >> 3) + 1]; - ((char *)&outTemplateVar.val)[2]=data[ (readOffset >> 3) + 2]; - ((char *)&outTemplateVar.val)[3]=0; - } - else - { - - ((char *)&outTemplateVar.val)[3]=data[ (readOffset >> 3) + 0]; - ((char *)&outTemplateVar.val)[2]=data[ (readOffset >> 3) + 1]; - ((char *)&outTemplateVar.val)[1]=data[ (readOffset >> 3) + 2]; - ((char *)&outTemplateVar.val)[0]=0; - } - - readOffset+=3*8; - return true; - } - - template <> - inline bool BitStream::Read(RakNetGUID &outTemplateVar) - { - return Read(outTemplateVar.g); - } - - - template <> - inline bool BitStream::Read(RakString &outTemplateVar) - { - return outTemplateVar.Deserialize(this); - } - template <> - inline bool BitStream::Read(RakWString &outTemplateVar) - { - return outTemplateVar.Deserialize(this); - } - template <> - inline bool BitStream::Read(char *&varString) - { - return RakString::Deserialize(varString,this); - } - template <> - inline bool BitStream::Read(wchar_t *&varString) - { - return RakWString::Deserialize(varString,this); - } - template <> - inline bool BitStream::Read(unsigned char *&varString) - { - return RakString::Deserialize((char*) varString,this); - } - - /// \brief Read any integral type from a bitstream. - /// \details If the written value differed from the value compared against in the write function, - /// var will be updated. Otherwise it will retain the current value. - /// ReadDelta is only valid from a previous call to WriteDelta - /// \param[in] outTemplateVar The value to read - template - inline bool BitStream::ReadDelta(templateType &outTemplateVar) - { - bool dataWritten; - bool success; - success=Read(dataWritten); - if (dataWritten) - success=Read(outTemplateVar); - return success; - } - - /// \brief Read a bool from a bitstream. - /// \param[in] outTemplateVar The value to read - template <> - inline bool BitStream::ReadDelta(bool &outTemplateVar) - { - return Read(outTemplateVar); - } - - /// \brief Read any integral type from a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] outTemplateVar The value to read - template - inline bool BitStream::ReadCompressed(templateType &outTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(outTemplateVar)==1) - return ReadCompressed( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - if (ReadCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true )) - { - ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType)); - return true; - } - return false; - } - else -#endif - return ReadCompressed( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true ); - } - } - - template <> - inline bool BitStream::ReadCompressed(SystemAddress &outTemplateVar) - { - return Read(outTemplateVar); - } - - template <> - inline bool BitStream::ReadCompressed(uint24_t &outTemplateVar) - { - return Read(outTemplateVar); - } - - template <> - inline bool BitStream::ReadCompressed(RakNetGUID &outTemplateVar) - { - return Read(outTemplateVar); - } - - template <> - inline bool BitStream::ReadCompressed(bool &outTemplateVar) - { - return Read(outTemplateVar); - } - - /// For values between -1 and 1 - template <> - inline bool BitStream::ReadCompressed(float &outTemplateVar) - { - unsigned short compressedFloat; - if (Read(compressedFloat)) - { - outTemplateVar = ((float)compressedFloat / 32767.5f - 1.0f); - return true; - } - return false; - } - - /// For values between -1 and 1 - template <> - inline bool BitStream::ReadCompressed(double &outTemplateVar) - { - uint32_t compressedFloat; - if (Read(compressedFloat)) - { - outTemplateVar = ((double)compressedFloat / 2147483648.0 - 1.0); - return true; - } - return false; - } - - /// For strings - template <> - inline bool BitStream::ReadCompressed(RakString &outTemplateVar) - { - return outTemplateVar.DeserializeCompressed(this,false); - } - template <> - inline bool BitStream::ReadCompressed(RakWString &outTemplateVar) - { - return outTemplateVar.Deserialize(this); - } - template <> - inline bool BitStream::ReadCompressed(char *&outTemplateVar) - { - return RakString::DeserializeCompressed(outTemplateVar,this,false); - } - template <> - inline bool BitStream::ReadCompressed(wchar_t *&outTemplateVar) - { - return RakWString::Deserialize(outTemplateVar,this); - } - template <> - inline bool BitStream::ReadCompressed(unsigned char *&outTemplateVar) - { - return RakString::DeserializeCompressed((char*) outTemplateVar,this,false); - } - - /// \brief Read any integral type from a bitstream. - /// \details If the written value differed from the value compared against in the write function, - /// var will be updated. Otherwise it will retain the current value. - /// the current value will be updated. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// ReadCompressedDelta is only valid from a previous call to WriteDelta - /// \param[in] outTemplateVar The value to read - template - inline bool BitStream::ReadCompressedDelta(templateType &outTemplateVar) - { - bool dataWritten; - bool success; - success=Read(dataWritten); - if (dataWritten) - success=ReadCompressed(outTemplateVar); - return success; - } - - /// \brief Read a bool from a bitstream. - /// \param[in] outTemplateVar The value to read - template <> - inline bool BitStream::ReadCompressedDelta(bool &outTemplateVar) - { - return Read(outTemplateVar); - } - - template - void BitStream::WriteCasted( const sourceType &value ) - { - destinationType val = (destinationType) value; - Write(val); - } - - template - void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, bool allowOutsideRange ) - { - int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); - WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); - } - template - void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, const int requiredBits, bool allowOutsideRange ) - { - RakAssert(maximum>=minimum); - RakAssert(allowOutsideRange==true || (value>=minimum && value<=maximum)); - if (allowOutsideRange) - { - if (valuemaximum) - { - Write(true); - Write(value); - return; - } - Write(false); - } - templateType valueOffMin=value-minimum; - if (IsBigEndian()==true) - { - unsigned char output[sizeof(templateType)]; - ReverseBytes((unsigned char*)&valueOffMin, output, sizeof(templateType)); - WriteBits(output,requiredBits); - } - else - { - WriteBits((unsigned char*) &valueOffMin,requiredBits); - } - } - - template // templateType for this function must be a float or double - void BitStream::WriteNormVector( templateType x, templateType y, templateType z ) - { -#ifdef _DEBUG - RakAssert(x <= 1.01 && y <= 1.01 && z <= 1.01 && x >= -1.01 && y >= -1.01 && z >= -1.01); -#endif - - WriteFloat16((float)x,-1.0f,1.0f); - WriteFloat16((float)y,-1.0f,1.0f); - WriteFloat16((float)z,-1.0f,1.0f); - } - - template // templateType for this function must be a float or double - void BitStream::WriteVector( templateType x, templateType y, templateType z ) - { - templateType magnitude = sqrt(x * x + y * y + z * z); - Write((float)magnitude); - if (magnitude > 0.00001f) - { - WriteCompressed((float)(x/magnitude)); - WriteCompressed((float)(y/magnitude)); - WriteCompressed((float)(z/magnitude)); - // Write((unsigned short)((x/magnitude+1.0f)*32767.5f)); - // Write((unsigned short)((y/magnitude+1.0f)*32767.5f)); - // Write((unsigned short)((z/magnitude+1.0f)*32767.5f)); - } - } - - template // templateType for this function must be a float or double - void BitStream::WriteNormQuat( templateType w, templateType x, templateType y, templateType z) - { - Write((bool)(w<0.0)); - Write((bool)(x<0.0)); - Write((bool)(y<0.0)); - Write((bool)(z<0.0)); - Write((unsigned short)(fabs(x)*65535.0)); - Write((unsigned short)(fabs(y)*65535.0)); - Write((unsigned short)(fabs(z)*65535.0)); - // Leave out w and calculate it on the target - } - - template // templateType for this function must be a float or double - void BitStream::WriteOrthMatrix( - templateType m00, templateType m01, templateType m02, - templateType m10, templateType m11, templateType m12, - templateType m20, templateType m21, templateType m22 ) - { - - double qw; - double qx; - double qy; - double qz; - - // Convert matrix to quat - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ - float sum; - sum = 1 + m00 + m11 + m22; - if (sum < 0.0f) sum=0.0f; - qw = sqrt( sum ) / 2; - sum = 1 + m00 - m11 - m22; - if (sum < 0.0f) sum=0.0f; - qx = sqrt( sum ) / 2; - sum = 1 - m00 + m11 - m22; - if (sum < 0.0f) sum=0.0f; - qy = sqrt( sum ) / 2; - sum = 1 - m00 - m11 + m22; - if (sum < 0.0f) sum=0.0f; - qz = sqrt( sum ) / 2; - if (qw < 0.0) qw=0.0; - if (qx < 0.0) qx=0.0; - if (qy < 0.0) qy=0.0; - if (qz < 0.0) qz=0.0; - qx = _copysign( (double) qx, (double) (m21 - m12) ); - qy = _copysign( (double) qy, (double) (m02 - m20) ); - qz = _copysign( (double) qz, (double) (m10 - m01) ); - - WriteNormQuat(qw,qx,qy,qz); - } - - template - bool BitStream::ReadCasted( sourceType &value ) - { - serializationType val; - bool success = Read(val); - value=(sourceType) val; - return success; - } - - template - bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange ) - { - int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); - return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); - } - template - bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange ) - { - RakAssert(maximum>=minimum); - if (allowOutsideRange) - { - bool isOutsideRange; - Read(isOutsideRange); - if (isOutsideRange) - return Read(value); - } - unsigned char output[sizeof(templateType)]; - memset(output,0,sizeof(output)); - bool success = ReadBits(output,requiredBits); - if (success) - { - if (IsBigEndian()==true) - ReverseBytesInPlace(output,sizeof(output)); - memcpy(&value,output,sizeof(output)); - - value+=minimum; - } - - return success; - } - - template // templateType for this function must be a float or double - bool BitStream::ReadNormVector( templateType &x, templateType &y, templateType &z ) - { - float xIn,yIn,zIn; - ReadFloat16(xIn,-1.0f,1.0f); - ReadFloat16(yIn,-1.0f,1.0f); - ReadFloat16(zIn,-1.0f,1.0f); - x=xIn; - y=yIn; - z=zIn; - return true; - } - - template // templateType for this function must be a float or double - bool BitStream::ReadVector( templateType &x, templateType &y, templateType &z ) - { - float magnitude; - //unsigned short sx,sy,sz; - if (!Read(magnitude)) - return false; - if (magnitude>0.00001f) - { - // Read(sx); - // Read(sy); - // if (!Read(sz)) - // return false; - // x=((float)sx / 32767.5f - 1.0f) * magnitude; - // y=((float)sy / 32767.5f - 1.0f) * magnitude; - // z=((float)sz / 32767.5f - 1.0f) * magnitude; - float cx,cy,cz; - ReadCompressed(cx); - ReadCompressed(cy); - if (!ReadCompressed(cz)) - return false; - x=cx; - y=cy; - z=cz; - x*=magnitude; - y*=magnitude; - z*=magnitude; - } - else - { - x=0.0; - y=0.0; - z=0.0; - } - return true; - } - - template // templateType for this function must be a float or double - bool BitStream::ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z) - { - bool cwNeg=false, cxNeg=false, cyNeg=false, czNeg=false; - unsigned short cx,cy,cz; - Read(cwNeg); - Read(cxNeg); - Read(cyNeg); - Read(czNeg); - Read(cx); - Read(cy); - if (!Read(cz)) - return false; - - // Calculate w from x,y,z - x=(templateType)(cx/65535.0); - y=(templateType)(cy/65535.0); - z=(templateType)(cz/65535.0); - if (cxNeg) x=-x; - if (cyNeg) y=-y; - if (czNeg) z=-z; - float difference = 1.0f - x*x - y*y - z*z; - if (difference < 0.0f) - difference=0.0f; - w = (templateType)(sqrt(difference)); - if (cwNeg) - w=-w; - - return true; - } - - template // templateType for this function must be a float or double - bool BitStream::ReadOrthMatrix( - templateType &m00, templateType &m01, templateType &m02, - templateType &m10, templateType &m11, templateType &m12, - templateType &m20, templateType &m21, templateType &m22 ) - { - float qw,qx,qy,qz; - if (!ReadNormQuat(qw,qx,qy,qz)) - return false; - - // Quat to orthogonal rotation matrix - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm - double sqw = (double)qw*(double)qw; - double sqx = (double)qx*(double)qx; - double sqy = (double)qy*(double)qy; - double sqz = (double)qz*(double)qz; - m00 = (templateType)(sqx - sqy - sqz + sqw); // since sqw + sqx + sqy + sqz =1 - m11 = (templateType)(-sqx + sqy - sqz + sqw); - m22 = (templateType)(-sqx - sqy + sqz + sqw); - - double tmp1 = (double)qx*(double)qy; - double tmp2 = (double)qz*(double)qw; - m10 = (templateType)(2.0 * (tmp1 + tmp2)); - m01 = (templateType)(2.0 * (tmp1 - tmp2)); - - tmp1 = (double)qx*(double)qz; - tmp2 = (double)qy*(double)qw; - m20 =(templateType)(2.0 * (tmp1 - tmp2)); - m02 = (templateType)(2.0 * (tmp1 + tmp2)); - tmp1 = (double)qy*(double)qz; - tmp2 = (double)qx*(double)qw; - m21 = (templateType)(2.0 * (tmp1 + tmp2)); - m12 = (templateType)(2.0 * (tmp1 - tmp2)); - - return true; - } - - template - BitStream& operator<<(BitStream& out, templateType& c) - { - out.Write(c); - return out; - } - template - BitStream& operator>>(BitStream& in, templateType& c) - { - bool success = in.Read(c); - RakAssert(success); - return in; - } - -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - -#endif // VC6 +/// \file BitStream.h +/// \brief This class allows you to write and read native types as a string of bits. +/// \details BitStream is used extensively throughout RakNet and is designed to be used by users as well. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// + +#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization +#include "BitStream_NoTemplate.h" +#else + +#ifndef __BITSTREAM_H +#define __BITSTREAM_H + +#include "RakMemoryOverride.h" +#include "RakNetDefines.h" +#include "Export.h" +#include "RakNetTypes.h" +#include "RakString.h" +#include "RakWString.h" +#include "RakAssert.h" +#include +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +// MSWin uses _copysign, others use copysign... +#ifndef _WIN32 +#define _copysign copysign +#endif + +namespace RakNet +{ + /// This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. + /// \sa BitStreamSample.txt + class RAK_DLL_EXPORT BitStream + { + + public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(BitStream) + + /// Default Constructor + BitStream(); + + /// \brief Create the bitstream, with some number of bytes to immediately allocate. + /// \details There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE. + /// In that case all it does is save you one or more realloc calls. + /// \param[in] initialBytesToAllocate the number of bytes to pre-allocate. + BitStream( const unsigned int initialBytesToAllocate ); + + /// \brief Initialize the BitStream, immediately setting the data it contains to a predefined pointer. + /// \details Set \a _copyData to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data. + /// You shouldn't call Write functions with \a _copyData as false, as this will write to unallocated memory + /// 99% of the time you will use this function to cast Packet::data to a bitstream for reading, in which case you should write something as follows: + /// \code + /// RakNet::BitStream bs(packet->data, packet->length, false); + /// \endcode + /// \param[in] _data An array of bytes. + /// \param[in] lengthInBytes Size of the \a _data. + /// \param[in] _copyData true or false to make a copy of \a _data or not. + BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData ); + + // Destructor + ~BitStream(); + + /// Resets the bitstream for reuse. + void Reset( void ); + + /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutTemplateVar The value to write + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool Serialize(bool writeToBitstream, templateType &inOutTemplateVar); + + /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutCurrentValue The current value to write + /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue); + + /// \brief Bidirectional version of SerializeDelta when you don't know what the last value is, or there is no last value. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutCurrentValue The current value to write + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue); + + /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutTemplateVar The value to write + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar); + + /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutCurrentValue The current value to write + /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue); + + /// \brief Save as SerializeCompressedDelta(templateType ¤tValue, const templateType &lastValue) when we have an unknown second parameter + /// \return true on data read. False on insufficient data in bitstream + template + bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutTemplateVar); + + /// \brief Bidirectional serialize/deserialize an array or casted stream or raw data. This does NOT do endian swapping. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutByteArray a byte buffer + /// \param[in] numberOfBytes the size of \a input in bytes + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes ); + + /// \brief Serialize a float into 2 bytes, spanning the range between \a floatMin and \a floatMax + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutFloat The float to write + /// \param[in] floatMin Predetermined minimum value of f + /// \param[in] floatMax Predetermined maximum value of f + bool SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax); + + /// Serialize one type casted to another (smaller) type, to save bandwidth + /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t + /// Example: int num=53; SerializeCasted(true, num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes) + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] value The value to serialize + template + bool SerializeCasted( bool writeToBitstream, sourceType &value ); + + /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range + /// Then serialize only those bits + /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum + /// \param[in] minimum Minimum value of \a value + /// \param[in] maximum Maximum value of \a value + /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates + template + bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); + /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum + template + bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); + + /// \brief Bidirectional serialize/deserialize a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. + /// \details Will further compress y or z axis aligned vectors. + /// Accurate to 1/32767.5. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template // templateType for this function must be a float or double + bool SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ); + + /// \brief Bidirectional serialize/deserialize a vector, using 10 bytes instead of 12. + /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template // templateType for this function must be a float or double + bool SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ); + + /// \brief Bidirectional serialize/deserialize a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] w w + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template // templateType for this function must be a float or double + bool SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z); + + /// \brief Bidirectional serialize/deserialize an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each. + /// \details Use 6 bytes instead of 36 + /// Lossy, although the result is renormalized + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool SerializeOrthMatrix( + bool writeToBitstream, + templateType &m00, templateType &m01, templateType &m02, + templateType &m10, templateType &m11, templateType &m12, + templateType &m20, templateType &m21, templateType &m22 ); + + /// \brief Bidirectional serialize/deserialize numberToSerialize bits to/from the input. + /// \details Right aligned data means in the case of a partial byte, the bits are aligned + /// from the right (bit 0) rather than the left (as in the normal + /// internal representation) You would set this to true when + /// writing user data, and false when copying bitstream data, such + /// as writing one bitstream to another + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutByteArray The data + /// \param[in] numberOfBitsToSerialize The number of bits to write + /// \param[in] rightAlignedBits if true data will be right aligned + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits = true ); + + /// \brief Write any integral type to a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] inTemplateVar The value to write + template + void Write(const templateType &inTemplateVar); + + /// \brief Write the dereferenced pointer to any integral type to a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] inTemplateVar The value to write + template + void WritePtr(templateType *inTemplateVar); + + /// \brief Write any integral type to a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template + void WriteDelta(const templateType ¤tValue, const templateType &lastValue); + + /// \brief WriteDelta when you don't know what the last value is, or there is no last value. + /// \param[in] currentValue The current value to write + template + void WriteDelta(const templateType ¤tValue); + + /// \brief Write any integral type to a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// \param[in] inTemplateVar The value to write + template + void WriteCompressed(const templateType &inTemplateVar); + + /// \brief Write any integral type to a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template + void WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue); + + /// \brief Save as WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) when we have an unknown second parameter + template + void WriteCompressedDelta(const templateType ¤tValue); + + /// \brief Read any integral type from a bitstream. + /// \details Define __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] outTemplateVar The value to read + /// \return true on success, false on failure. + template + bool Read(templateType &outTemplateVar); + + /// \brief Read any integral type from a bitstream. + /// \details If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// ReadDelta is only valid from a previous call to WriteDelta + /// \param[in] outTemplateVar The value to read + /// \return true on success, false on failure. + template + bool ReadDelta(templateType &outTemplateVar); + + /// \brief Read any integral type from a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] outTemplateVar The value to read + /// \return true on success, false on failure. + template + bool ReadCompressed(templateType &outTemplateVar); + + /// \brief Read any integral type from a bitstream. + /// \details If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// the current value will be updated. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// ReadCompressedDelta is only valid from a previous call to WriteDelta + /// \param[in] outTemplateVar The value to read + /// \return true on success, false on failure. + template + bool ReadCompressedDelta(templateType &outTemplateVar); + + /// \brief Read one bitstream to another. + /// \param[in] numberOfBits bits to read + /// \param bitStream the bitstream to read into from + /// \return true on success, false on failure. + bool Read( BitStream *bitStream, BitSize_t numberOfBits ); + bool Read( BitStream *bitStream ); + bool Read( BitStream &bitStream, BitSize_t numberOfBits ); + bool Read( BitStream &bitStream ); + + /// \brief Write an array or casted stream or raw data. This does NOT do endian swapping. + /// \param[in] inputByteArray a byte buffer + /// \param[in] numberOfBytes the size of \a input in bytes + void Write( const char* inputByteArray, const unsigned int numberOfBytes ); + + /// \brief Write one bitstream to another. + /// \param[in] numberOfBits bits to write + /// \param bitStream the bitstream to copy from + void Write( BitStream *bitStream, BitSize_t numberOfBits ); + void Write( BitStream *bitStream ); + void Write( BitStream &bitStream, BitSize_t numberOfBits ); + void Write( BitStream &bitStream );\ + + /// \brief Write a float into 2 bytes, spanning the range between \a floatMin and \a floatMax + /// \param[in] x The float to write + /// \param[in] floatMin Predetermined minimum value of f + /// \param[in] floatMax Predetermined maximum value of f + void WriteFloat16( float x, float floatMin, float floatMax ); + + /// Write one type serialized as another (smaller) type, to save bandwidth + /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t + /// Example: int num=53; WriteCasted(num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes) + /// \param[in] value The value to write + template + void WriteCasted( const sourceType &value ); + + /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range + /// Then write only those bits + /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program + /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum + /// \param[in] minimum Minimum value of \a value + /// \param[in] maximum Maximum value of \a value + /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Read(). + template + void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); + /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum + template + void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); + + /// \brief Write a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. + /// \details Will further compress y or z axis aligned vectors. + /// Accurate to 1/32767.5. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + template // templateType for this function must be a float or double + void WriteNormVector( templateType x, templateType y, templateType z ); + + /// \brief Write a vector, using 10 bytes instead of 12. + /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, + /// so only use if accuracy is not important. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + template // templateType for this function must be a float or double + void WriteVector( templateType x, templateType y, templateType z ); + + /// \brief Write a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. + /// \param[in] w w + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + template // templateType for this function must be a float or double + void WriteNormQuat( templateType w, templateType x, templateType y, templateType z); + + /// \brief Write an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each. + /// \details Use 6 bytes instead of 36 + /// Lossy, although the result is renormalized + template // templateType for this function must be a float or double + void WriteOrthMatrix( + templateType m00, templateType m01, templateType m02, + templateType m10, templateType m11, templateType m12, + templateType m20, templateType m21, templateType m22 ); + + /// \brief Read an array or casted stream of byte. + /// \details The array is raw data. There is no automatic endian conversion with this function + /// \param[in] output The result byte array. It should be larger than @em numberOfBytes. + /// \param[in] numberOfBytes The number of byte to read + /// \return true on success false if there is some missing bytes. + bool Read( char* output, const unsigned int numberOfBytes ); + + /// \brief Read a float into 2 bytes, spanning the range between \a floatMin and \a floatMax + /// \param[in] outFloat The float to read + /// \param[in] floatMin Predetermined minimum value of f + /// \param[in] floatMax Predetermined maximum value of f + bool ReadFloat16( float &outFloat, float floatMin, float floatMax ); + + /// Read one type serialized to another (smaller) type, to save bandwidth + /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t + /// Example: int num; ReadCasted(num); would read 1 bytefrom the stream, and put the value in an integer + /// \param[in] value The value to write + template + bool ReadCasted( sourceType &value ); + + /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range + /// Then read only those bits + /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program + /// \param[in] value Integer value to read, which should be between \a minimum and \a maximum + /// \param[in] minimum Minimum value of \a value + /// \param[in] maximum Maximum value of \a value + /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Write(). + template + bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); + /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum + template + bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); + + /// \brief Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. + /// \details Will further compress y or z axis aligned vectors. + /// Accurate to 1/32767.5. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool ReadNormVector( templateType &x, templateType &y, templateType &z ); + + /// \brief Read 3 floats or doubles, using 10 bytes, where those float or doubles comprise a vector. + /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, + /// so only use if accuracy is not important. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool ReadVector( templateType &x, templateType &y, templateType &z ); + + /// \brief Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. + /// \param[in] w w + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z); + + /// \brief Read an orthogonal matrix from a quaternion, reading 3 components of the quaternion in 2 bytes each and extrapolatig the 4th. + /// \details Use 6 bytes instead of 36 + /// Lossy, although the result is renormalized + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool ReadOrthMatrix( + templateType &m00, templateType &m01, templateType &m02, + templateType &m10, templateType &m11, templateType &m12, + templateType &m20, templateType &m21, templateType &m22 ); + + /// \brief Sets the read pointer back to the beginning of your data. + void ResetReadPointer( void ); + + /// \brief Sets the write pointer back to the beginning of your data. + void ResetWritePointer( void ); + + /// \brief This is good to call when you are done with the stream to make + /// sure you didn't leave any data left over void + void AssertStreamEmpty( void ); + + /// \brief RAKNET_DEBUG_PRINTF the bits in the stream. Great for debugging. + void PrintBits( char *out ) const; + void PrintBits( void ) const; + void PrintHex( char *out ) const; + void PrintHex( void ) const; + + /// \brief Ignore data we don't intend to read + /// \param[in] numberOfBits The number of bits to ignore + void IgnoreBits( const BitSize_t numberOfBits ); + + /// \brief Ignore data we don't intend to read + /// \param[in] numberOfBits The number of bytes to ignore + void IgnoreBytes( const unsigned int numberOfBytes ); + + /// \brief Move the write pointer to a position on the array. + /// \param[in] offset the offset from the start of the array. + /// \attention + /// \details Dangerous if you don't know what you are doing! + /// For efficiency reasons you can only write mid-stream if your data is byte aligned. + void SetWriteOffset( const BitSize_t offset ); + + /// \brief Returns the length in bits of the stream + inline BitSize_t GetNumberOfBitsUsed( void ) const {return GetWriteOffset();} + inline BitSize_t GetWriteOffset( void ) const {return numberOfBitsUsed;} + + /// \brief Returns the length in bytes of the stream + inline BitSize_t GetNumberOfBytesUsed( void ) const {return BITS_TO_BYTES( numberOfBitsUsed );} + + /// \brief Returns the number of bits into the stream that we have read + inline BitSize_t GetReadOffset( void ) const {return readOffset;} + + /// \brief Sets the read bit index + void SetReadOffset( const BitSize_t newReadOffset ) {readOffset=newReadOffset;} + + /// \brief Returns the number of bits left in the stream that haven't been read + inline BitSize_t GetNumberOfUnreadBits( void ) const {return numberOfBitsUsed - readOffset;} + + /// \brief Makes a copy of the internal data for you \a _data will point to + /// the stream. Partial bytes are left aligned. + /// \param[out] _data The allocated copy of GetData() + /// \return The length in bits of the stream. + BitSize_t CopyData( unsigned char** _data ) const; + + /// \internal + /// Set the stream to some initial data. + void SetData( unsigned char *inByteArray ); + + /// Gets the data that BitStream is writing to / reading from. + /// Partial bytes are left aligned. + /// \return A pointer to the internal state + inline unsigned char* GetData( void ) const {return data;} + + /// \brief Write numberToWrite bits from the input source. + /// \details Right aligned data means in the case of a partial byte, the bits are aligned + /// from the right (bit 0) rather than the left (as in the normal + /// internal representation) You would set this to true when + /// writing user data, and false when copying bitstream data, such + /// as writing one bitstream to another. + /// \param[in] inByteArray The data + /// \param[in] numberOfBitsToWrite The number of bits to write + /// \param[in] rightAlignedBits if true data will be right aligned + void WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits = true ); + + /// \brief Align the bitstream to the byte boundary and then write the + /// specified number of bits. + /// \details This is faster than WriteBits but + /// wastes the bits to do the alignment and requires you to call + /// ReadAlignedBits at the corresponding read position. + /// \param[in] inByteArray The data + /// \param[in] numberOfBytesToWrite The size of input. + void WriteAlignedBytes( const unsigned char *inByteArray, const unsigned int numberOfBytesToWrite ); + + // Endian swap bytes already in the bitstream + void EndianSwapBytes( int byteOffset, int length ); + + /// \brief Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite + /// \param[in] inByteArray The data + /// \param[in] inputLength The size of input. + /// \param[in] maxBytesToWrite Max bytes to write + void WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite ); + + /// \brief Read bits, starting at the next aligned bits. + /// \details Note that the modulus 8 starting offset of the sequence must be the same as + /// was used with WriteBits. This will be a problem with packet + /// coalescence unless you byte align the coalesced packets. + /// \param[in] inOutByteArray The byte array larger than @em numberOfBytesToRead + /// \param[in] numberOfBytesToRead The number of byte to read from the internal state + /// \return true if there is enough byte. + bool ReadAlignedBytes( unsigned char *inOutByteArray, const unsigned int numberOfBytesToRead ); + + /// \brief Reads what was written by WriteAlignedBytesSafe. + /// \param[in] inOutByteArray The data + /// \param[in] maxBytesToRead Maximum number of bytes to read + /// \return true on success, false on failure. + bool ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead ); + bool ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ); + + /// \brief Same as ReadAlignedBytesSafe() but allocates the memory for you using new, rather than assuming it is safe to write to + /// \param[in] outByteArray outByteArray will be deleted if it is not a pointer to 0 + /// \return true on success, false on failure. + bool ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead ); + bool ReadAlignedBytesSafeAlloc( char **outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ); + + /// \brief Align the next write and/or read to a byte boundary. + /// \details This can be used to 'waste' bits to byte align for efficiency reasons It + /// can also be used to force coalesced bitstreams to start on byte + /// boundaries so so WriteAlignedBits and ReadAlignedBits both + /// calculate the same offset when aligning. + inline void AlignWriteToByteBoundary( void ) {numberOfBitsUsed += 8 - ( (( numberOfBitsUsed - 1 ) & 7) + 1 );} + + /// \brief Align the next write and/or read to a byte boundary. + /// \details This can be used to 'waste' bits to byte align for efficiency reasons It + /// can also be used to force coalesced bitstreams to start on byte + /// boundaries so so WriteAlignedBits and ReadAlignedBits both + /// calculate the same offset when aligning. + inline void AlignReadToByteBoundary( void ) {readOffset += 8 - ( (( readOffset - 1 ) & 7 ) + 1 );} + + /// \brief Read \a numberOfBitsToRead bits to the output source. + /// \details alignBitsToRight should be set to true to convert internal + /// bitstream data to userdata. It should be false if you used + /// WriteBits with rightAlignedBits false + /// \param[in] inOutByteArray The resulting bits array + /// \param[in] numberOfBitsToRead The number of bits to read + /// \param[in] alignBitsToRight if true bits will be right aligned. + /// \return true if there is enough bits to read + bool ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight = true ); + + /// \brief Write a 0 + void Write0( void ); + + /// \brief Write a 1 + void Write1( void ); + + /// \brief Reads 1 bit and returns true if that bit is 1 and false if it is 0. + bool ReadBit( void ); + + /// \brief If we used the constructor version with copy data off, this + /// *makes sure it is set to on and the data pointed to is copied. + void AssertCopyData( void ); + + /// \brief Use this if you pass a pointer copy to the constructor + /// *(_copyData==false) and want to overallocate to prevent + /// reallocation. + void SetNumberOfBitsAllocated( const BitSize_t lengthInBits ); + + /// \brief Reallocates (if necessary) in preparation of writing numberOfBitsToWrite + void AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite ); + + /// \internal + /// \return How many bits have been allocated internally + BitSize_t GetNumberOfBitsAllocated(void) const; + + /// \brief Read strings, non reference. + bool Read(char *varString); + bool Read(unsigned char *varString); + + /// Write zeros until the bitstream is filled up to \a bytes + void PadWithZeroToByteLength( unsigned int bytes ); + + /// Get the number of leading zeros for a number + /// \param[in] x Number to test + static int NumberOfLeadingZeroes( uint8_t x ); + static int NumberOfLeadingZeroes( uint16_t x ); + static int NumberOfLeadingZeroes( uint32_t x ); + static int NumberOfLeadingZeroes( uint64_t x ); + static int NumberOfLeadingZeroes( int8_t x ); + static int NumberOfLeadingZeroes( int16_t x ); + static int NumberOfLeadingZeroes( int32_t x ); + static int NumberOfLeadingZeroes( int64_t x ); + + /// \internal Unrolled inner loop, for when performance is critical + void WriteAlignedVar8(const char *inByteArray); + /// \internal Unrolled inner loop, for when performance is critical + bool ReadAlignedVar8(char *inOutByteArray); + /// \internal Unrolled inner loop, for when performance is critical + void WriteAlignedVar16(const char *inByteArray); + /// \internal Unrolled inner loop, for when performance is critical + bool ReadAlignedVar16(char *inOutByteArray); + /// \internal Unrolled inner loop, for when performance is critical + void WriteAlignedVar32(const char *inByteArray); + /// \internal Unrolled inner loop, for when performance is critical + bool ReadAlignedVar32(char *inOutByteArray); + + inline void Write(const char * const inStringVar) + { + RakString::Serialize(inStringVar, this); + } + inline void Write(const wchar_t * const inStringVar) + { + RakWString::Serialize(inStringVar, this); + } + inline void Write(const unsigned char * const inTemplateVar) + { + Write((const char*)inTemplateVar); + } + inline void Write(char * const inTemplateVar) + { + Write((const char*)inTemplateVar); + } + inline void Write(unsigned char * const inTemplateVar) + { + Write((const char*)inTemplateVar); + } + inline void WriteCompressed(const char * const inStringVar) + { + RakString::SerializeCompressed(inStringVar,this,0,false); + } + inline void WriteCompressed(const wchar_t * const inStringVar) + { + RakWString::Serialize(inStringVar,this); + } + inline void WriteCompressed(const unsigned char * const inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + inline void WriteCompressed(char * const inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + inline void WriteCompressed(unsigned char * const inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + + /// ---- Member function template specialization declarations ---- + // Used for VC7 +#if defined(_MSC_VER) && _MSC_VER == 1300 + /// Write a bool to a bitstream. + /// \param[in] var The value to write + template <> + void Write(const bool &var); + + /// Write a systemAddress to a bitstream + /// \param[in] var The value to write + template <> + void Write(const SystemAddress &var); + + /// Write a uint24_t to a bitstream + /// \param[in] var The value to write + template <> + void Write(const uint24_t &var); + + /// Write a RakNetGUID to a bitsteam + /// \param[in] var The value to write + template <> + void Write(const RakNetGuid &var); + + /// Write a string to a bitstream + /// \param[in] var The value to write + template <> + void Write(const char* const &var); + template <> + void Write(const unsigned char* const &var); + template <> + void Write(char* const &var); + template <> + void Write(unsigned char* const &var); + template <> + void Write(const RakString &var); + template <> + void Write(const RakWString &var); + + /// \brief Write a systemAddress. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + void WriteDelta(const SystemAddress ¤tValue, const SystemAddress &lastValue); + + template <> + void WriteDelta(const uint24_t ¤tValue, const uint24_t &lastValue); + + template <> + void WriteDelta(const RakNetGUID ¤tValue, const RakNetGUID &lastValue); + + /// \brief Write a bool delta. + /// \details Same thing as just calling Write + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + void WriteDelta(const bool ¤tValue, const bool &lastValue); + + template <> + void WriteCompressed(const SystemAddress &var); + + template <> + void WriteCompressed(const uint24_t &var); + + template <> + void WriteCompressed(const RakNetGUID &var); + + template <> + void WriteCompressed(const bool &var); + + /// For values between -1 and 1 + template <> + void WriteCompressed(const float &var); + + /// For values between -1 and 1 + template <> + void WriteCompressed(const double &var); + + /// Compressed string + template <> + void WriteCompressed(const char* var); + template <> + void WriteCompressed(const unsigned char* var); + template <> + void WriteCompressed(char* var); + template <> + void WriteCompressed(unsigned char* var); + template <> + void WriteCompressed(const RakString &var); + template <> + void WriteCompressed(const RakWString &var); + + /// \brief Write a bool delta. + /// \details Same thing as just calling Write + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + void WriteCompressedDelta(const bool ¤tValue, const bool &lastValue); + + /// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue) + /// when we have an unknown second bool + template <> + void WriteCompressedDelta(const bool ¤tValue); + + /// \brief Read a bool from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool Read(bool &var); + + /// \brief Read a systemAddress from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool Read(SystemAddress &var); + + template <> + bool Read(uint24_t &var); + + template <> + bool Read(RakNetGUID &var); + + /// \brief Read a String from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool Read(char *&var); + template <> + bool Read(wchar_t *&var); + template <> + bool Read(unsigned char *&var); + template <> + bool Read(RakString &var); + template <> + bool Read(RakWString &var); + + /// \brief Read a bool from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool ReadDelta(bool &var); + + template <> + bool ReadCompressed(SystemAddress &var); + + template <> + bool ReadCompressed(uint24_t &var); + + template <> + bool ReadCompressed(RakNetGUID &var); + + template <> + bool ReadCompressed(bool &var); + + template <> + bool ReadCompressed(float &var); + + /// For values between -1 and 1 + /// \return true on success, false on failure. + template <> + bool ReadCompressed(double &var); + + template <> + bool ReadCompressed(char* &var); + template <> + bool ReadCompressed(wchar_t* &var); + template <> + bool ReadCompressed(unsigned char *&var); + template <> + bool ReadCompressed(RakString &var); + template <> + bool ReadCompressed(RakWString &var); + + /// \brief Read a bool from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool ReadCompressedDelta(bool &var); +#endif + + inline static bool DoEndianSwap(void) { +#ifndef __BITSTREAM_NATIVE_END + return IsNetworkOrder()==false; +#else + return false; +#endif + } + inline static bool IsBigEndian(void) + { + return IsNetworkOrder(); + } + inline static bool IsNetworkOrder(void) {static const bool r = IsNetworkOrderInternal(); return r;} + // Not inline, won't compile on PC due to winsock include errors + static bool IsNetworkOrderInternal(void); + static void ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length); + static void ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length); + + private: + + BitStream( const BitStream &invalid) { + (void) invalid; + RakAssert(0); + } + + /// \brief Assume the input source points to a native type, compress and write it. + void WriteCompressed( const unsigned char* inByteArray, const unsigned int size, const bool unsignedData ); + + /// \brief Assume the input source points to a compressed native type. Decompress and read it. + bool ReadCompressed( unsigned char* inOutByteArray, const unsigned int size, const bool unsignedData ); + + + BitSize_t numberOfBitsUsed; + + BitSize_t numberOfBitsAllocated; + + BitSize_t readOffset; + + unsigned char *data; + + /// true if the internal buffer is copy of the data passed to the constructor + bool copyData; + + /// BitStreams that use less than BITSTREAM_STACK_ALLOCATION_SIZE use the stack, rather than the heap to store data. It switches over if BITSTREAM_STACK_ALLOCATION_SIZE is exceeded + unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE]; + }; + + template + inline bool BitStream::Serialize(bool writeToBitstream, templateType &inOutTemplateVar) + { + if (writeToBitstream) + Write(inOutTemplateVar); + else + return Read(inOutTemplateVar); + return true; + } + + template + inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue) + { + if (writeToBitstream) + WriteDelta(inOutCurrentValue, lastValue); + else + return ReadDelta(inOutCurrentValue); + return true; + } + + template + inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue) + { + if (writeToBitstream) + WriteDelta(inOutCurrentValue); + else + return ReadDelta(inOutCurrentValue); + return true; + } + + template + inline bool BitStream::SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar) + { + if (writeToBitstream) + WriteCompressed(inOutTemplateVar); + else + return ReadCompressed(inOutTemplateVar); + return true; + } + + template + inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue) + { + if (writeToBitstream) + WriteCompressedDelta(inOutCurrentValue,lastValue); + else + return ReadCompressedDelta(inOutCurrentValue); + return true; + } +//Stoppedhere + template + inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue) + { + if (writeToBitstream) + WriteCompressedDelta(inOutCurrentValue); + else + return ReadCompressedDelta(inOutCurrentValue); + return true; + } + + inline bool BitStream::Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes ) + { + if (writeToBitstream) + Write(inOutByteArray, numberOfBytes); + else + return Read(inOutByteArray, numberOfBytes); + return true; + } + + template + bool BitStream::SerializeCasted( bool writeToBitstream, sourceType &value ) + { + if (writeToBitstream) WriteCasted(value); + else return ReadCasted(value); + return true; + } + + template + bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange ) + { + int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); + return SerializeBitsFromIntegerRange(writeToBitstream,value,minimum,maximum,requiredBits,allowOutsideRange); + } + template + bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange ) + { + if (writeToBitstream) WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); + else return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); + return true; + } + + template + inline bool BitStream::SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ) + { + if (writeToBitstream) + WriteNormVector(x,y,z); + else + return ReadNormVector(x,y,z); + return true; + } + + template + inline bool BitStream::SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ) + { + if (writeToBitstream) + WriteVector(x,y,z); + else + return ReadVector(x,y,z); + return true; + } + + template + inline bool BitStream::SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z) + { + if (writeToBitstream) + WriteNormQuat(w,x,y,z); + else + return ReadNormQuat(w,x,y,z); + return true; + } + + template + inline bool BitStream::SerializeOrthMatrix( + bool writeToBitstream, + templateType &m00, templateType &m01, templateType &m02, + templateType &m10, templateType &m11, templateType &m12, + templateType &m20, templateType &m21, templateType &m22 ) + { + if (writeToBitstream) + WriteOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); + else + return ReadOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); + return true; + } + + inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits ) + { + if (writeToBitstream) + WriteBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits); + else + return ReadBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits); + return true; + } + + template + inline void BitStream::Write(const templateType &inTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(inTemplateVar)==1) + WriteBits( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType)); + WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ); + } + else +#endif + WriteBits( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true ); + } + } + + template + inline void BitStream::WritePtr(templateType *inTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(templateType)==1) + WriteBits( ( unsigned char* ) inTemplateVar, sizeof( templateType ) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + ReverseBytes((unsigned char*) inTemplateVar, output, sizeof(templateType)); + WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ); + } + else +#endif + WriteBits( ( unsigned char* ) inTemplateVar, sizeof(templateType) * 8, true ); + } + } + + /// \brief Write a bool to a bitstream. + /// \param[in] inTemplateVar The value to write + template <> + inline void BitStream::Write(const bool &inTemplateVar) + { + if ( inTemplateVar ) + Write1(); + else + Write0(); + } + + + /// \brief Write a systemAddress to a bitstream. + /// \param[in] inTemplateVar The value to write + template <> + inline void BitStream::Write(const SystemAddress &inTemplateVar) + { + Write(inTemplateVar.GetIPVersion()); + if (inTemplateVar.GetIPVersion()==4) + { + // Hide the address so routers don't modify it + SystemAddress var2=inTemplateVar; + uint32_t binaryAddress=~inTemplateVar.address.addr4.sin_addr.s_addr; + // Don't endian swap the address or port + WriteBits((unsigned char*)&binaryAddress, sizeof(binaryAddress)*8, true); + unsigned short p = var2.GetPortNetworkOrder(); + WriteBits((unsigned char*)&p, sizeof(unsigned short)*8, true); + } + else + { +#if RAKNET_SUPPORT_IPV6==1 + // Don't endian swap + WriteBits((const unsigned char*) &inTemplateVar.address.addr6, sizeof(inTemplateVar.address.addr6)*8, true); +#endif + } + } + + template <> + inline void BitStream::Write(const uint24_t &inTemplateVar) + { + AlignWriteToByteBoundary(); + AddBitsAndReallocate(3*8); + + if (IsBigEndian()==false) + { + data[( numberOfBitsUsed >> 3 ) + 0] = ((char *)&inTemplateVar.val)[0]; + data[( numberOfBitsUsed >> 3 ) + 1] = ((char *)&inTemplateVar.val)[1]; + data[( numberOfBitsUsed >> 3 ) + 2] = ((char *)&inTemplateVar.val)[2]; + } + else + { + data[( numberOfBitsUsed >> 3 ) + 0] = ((char *)&inTemplateVar.val)[3]; + data[( numberOfBitsUsed >> 3 ) + 1] = ((char *)&inTemplateVar.val)[2]; + data[( numberOfBitsUsed >> 3 ) + 2] = ((char *)&inTemplateVar.val)[1]; + } + + numberOfBitsUsed+=3*8; + } + + template <> + inline void BitStream::Write(const RakNetGUID &inTemplateVar) + { + Write(inTemplateVar.g); + } + + /// \brief Write a string to a bitstream. + /// \param[in] var The value to write + template <> + inline void BitStream::Write(const RakString &inTemplateVar) + { + inTemplateVar.Serialize(this); + } + template <> + inline void BitStream::Write(const RakWString &inTemplateVar) + { + inTemplateVar.Serialize(this); + } + template <> + inline void BitStream::Write(const char * const &inStringVar) + { + RakString::Serialize(inStringVar, this); + } + template <> + inline void BitStream::Write(const wchar_t * const &inStringVar) + { + RakWString::Serialize(inStringVar, this); + } + template <> + inline void BitStream::Write(const unsigned char * const &inTemplateVar) + { + Write((const char*)inTemplateVar); + } + template <> + inline void BitStream::Write(char * const &inTemplateVar) + { + Write((const char*)inTemplateVar); + } + template <> + inline void BitStream::Write(unsigned char * const &inTemplateVar) + { + Write((const char*)inTemplateVar); + } + + /// \brief Write any integral type to a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template + inline void BitStream::WriteDelta(const templateType ¤tValue, const templateType &lastValue) + { + if (currentValue==lastValue) + { + Write(false); + } + else + { + Write(true); + Write(currentValue); + } + } + + /// \brief Write a bool delta. Same thing as just calling Write + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + inline void BitStream::WriteDelta(const bool ¤tValue, const bool &lastValue) + { + (void) lastValue; + + Write(currentValue); + } + + /// \brief WriteDelta when you don't know what the last value is, or there is no last value. + /// \param[in] currentValue The current value to write + template + inline void BitStream::WriteDelta(const templateType ¤tValue) + { + Write(true); + Write(currentValue); + } + + /// \brief Write any integral type to a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] inTemplateVar The value to write + template + inline void BitStream::WriteCompressed(const templateType &inTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(inTemplateVar)==1) + WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END +#ifdef _MSC_VER +#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data +#endif + + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType)); + WriteCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true ); + } + else +#endif + WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true ); + } + } + + template <> + inline void BitStream::WriteCompressed(const SystemAddress &inTemplateVar) + { + Write(inTemplateVar); + } + + template <> + inline void BitStream::WriteCompressed(const RakNetGUID &inTemplateVar) + { + Write(inTemplateVar); + } + + template <> + inline void BitStream::WriteCompressed(const uint24_t &var) + { + Write(var); + } + + template <> + inline void BitStream::WriteCompressed(const bool &inTemplateVar) + { + Write(inTemplateVar); + } + + /// For values between -1 and 1 + template <> + inline void BitStream::WriteCompressed(const float &inTemplateVar) + { + RakAssert(inTemplateVar > -1.01f && inTemplateVar < 1.01f); + float varCopy=inTemplateVar; + if (varCopy < -1.0f) + varCopy=-1.0f; + if (varCopy > 1.0f) + varCopy=1.0f; + Write((unsigned short)((varCopy+1.0f)*32767.5f)); + } + + /// For values between -1 and 1 + template <> + inline void BitStream::WriteCompressed(const double &inTemplateVar) + { + RakAssert(inTemplateVar > -1.01 && inTemplateVar < 1.01); + double varCopy=inTemplateVar; + if (varCopy < -1.0f) + varCopy=-1.0f; + if (varCopy > 1.0f) + varCopy=1.0f; + Write((uint32_t)((varCopy+1.0)*2147483648.0)); + } + + /// Compress the string + template <> + inline void BitStream::WriteCompressed(const RakString &inTemplateVar) + { + inTemplateVar.SerializeCompressed(this,0,false); + } + template <> + inline void BitStream::WriteCompressed(const RakWString &inTemplateVar) + { + inTemplateVar.Serialize(this); + } + template <> + inline void BitStream::WriteCompressed(const char * const &inStringVar) + { + RakString::SerializeCompressed(inStringVar,this,0,false); + } + template <> + inline void BitStream::WriteCompressed(const wchar_t * const &inStringVar) + { + RakWString::Serialize(inStringVar,this); + } + template <> + inline void BitStream::WriteCompressed(const unsigned char * const &inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + template <> + inline void BitStream::WriteCompressed(char * const &inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + template <> + inline void BitStream::WriteCompressed(unsigned char * const &inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + + + /// \brief Write any integral type to a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template + inline void BitStream::WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) + { + if (currentValue==lastValue) + { + Write(false); + } + else + { + Write(true); + WriteCompressed(currentValue); + } + } + + /// \brief Write a bool delta. Same thing as just calling Write + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + inline void BitStream::WriteCompressedDelta(const bool ¤tValue, const bool &lastValue) + { + (void) lastValue; + + Write(currentValue); + } + + /// \brief Save as WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) + /// when we have an unknown second parameter + template + inline void BitStream::WriteCompressedDelta(const templateType ¤tValue) + { + Write(true); + WriteCompressed(currentValue); + } + + /// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue) + /// when we have an unknown second bool + template <> + inline void BitStream::WriteCompressedDelta(const bool ¤tValue) + { + Write(currentValue); + } + + /// \brief Read any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] outTemplateVar The value to read + template + inline bool BitStream::Read(templateType &outTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(outTemplateVar)==1) + return ReadBits( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END +#ifdef _MSC_VER +#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data +#endif + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + if (ReadBits( ( unsigned char* ) output, sizeof(templateType) * 8, true )) + { + ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType)); + return true; + } + return false; + } + else +#endif + return ReadBits( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true ); + } + } + + /// \brief Read a bool from a bitstream. + /// \param[in] outTemplateVar The value to read + template <> + inline bool BitStream::Read(bool &outTemplateVar) + { + if ( readOffset + 1 > numberOfBitsUsed ) + return false; + + if ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) // Is it faster to just write it out here? + outTemplateVar = true; + else + outTemplateVar = false; + + // Has to be on a different line for Mac + readOffset++; + + return true; + } + + /// \brief Read a systemAddress from a bitstream. + /// \param[in] outTemplateVar The value to read + template <> + inline bool BitStream::Read(SystemAddress &outTemplateVar) + { + unsigned char ipVersion; + Read(ipVersion); + if (ipVersion==4) + { + outTemplateVar.address.addr4.sin_family=AF_INET; + // Read(var.binaryAddress); + // Don't endian swap the address or port + uint32_t binaryAddress; + ReadBits( ( unsigned char* ) & binaryAddress, sizeof(binaryAddress) * 8, true ); + // Unhide the IP address, done to prevent routers from changing it + outTemplateVar.address.addr4.sin_addr.s_addr=~binaryAddress; + bool b = ReadBits(( unsigned char* ) & outTemplateVar.address.addr4.sin_port, sizeof(outTemplateVar.address.addr4.sin_port) * 8, true); + outTemplateVar.debugPort=ntohs(outTemplateVar.address.addr4.sin_port); + return b; + } + else + { +#if RAKNET_SUPPORT_IPV6==1 + bool b = ReadBits((unsigned char*) &outTemplateVar.address.addr6, sizeof(outTemplateVar.address.addr6)*8, true); + outTemplateVar.debugPort=ntohs(outTemplateVar.address.addr6.sin6_port); + return b; +#else + return false; +#endif + } + } + + template <> + inline bool BitStream::Read(uint24_t &outTemplateVar) + { + AlignReadToByteBoundary(); + if ( readOffset + 3*8 > numberOfBitsUsed ) + return false; + + if (IsBigEndian()==false) + { + ((char *)&outTemplateVar.val)[0]=data[ (readOffset >> 3) + 0]; + ((char *)&outTemplateVar.val)[1]=data[ (readOffset >> 3) + 1]; + ((char *)&outTemplateVar.val)[2]=data[ (readOffset >> 3) + 2]; + ((char *)&outTemplateVar.val)[3]=0; + } + else + { + + ((char *)&outTemplateVar.val)[3]=data[ (readOffset >> 3) + 0]; + ((char *)&outTemplateVar.val)[2]=data[ (readOffset >> 3) + 1]; + ((char *)&outTemplateVar.val)[1]=data[ (readOffset >> 3) + 2]; + ((char *)&outTemplateVar.val)[0]=0; + } + + readOffset+=3*8; + return true; + } + + template <> + inline bool BitStream::Read(RakNetGUID &outTemplateVar) + { + return Read(outTemplateVar.g); + } + + + template <> + inline bool BitStream::Read(RakString &outTemplateVar) + { + return outTemplateVar.Deserialize(this); + } + template <> + inline bool BitStream::Read(RakWString &outTemplateVar) + { + return outTemplateVar.Deserialize(this); + } + template <> + inline bool BitStream::Read(char *&varString) + { + return RakString::Deserialize(varString,this); + } + template <> + inline bool BitStream::Read(wchar_t *&varString) + { + return RakWString::Deserialize(varString,this); + } + template <> + inline bool BitStream::Read(unsigned char *&varString) + { + return RakString::Deserialize((char*) varString,this); + } + + /// \brief Read any integral type from a bitstream. + /// \details If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// ReadDelta is only valid from a previous call to WriteDelta + /// \param[in] outTemplateVar The value to read + template + inline bool BitStream::ReadDelta(templateType &outTemplateVar) + { + bool dataWritten; + bool success; + success=Read(dataWritten); + if (dataWritten) + success=Read(outTemplateVar); + return success; + } + + /// \brief Read a bool from a bitstream. + /// \param[in] outTemplateVar The value to read + template <> + inline bool BitStream::ReadDelta(bool &outTemplateVar) + { + return Read(outTemplateVar); + } + + /// \brief Read any integral type from a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] outTemplateVar The value to read + template + inline bool BitStream::ReadCompressed(templateType &outTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(outTemplateVar)==1) + return ReadCompressed( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + if (ReadCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true )) + { + ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType)); + return true; + } + return false; + } + else +#endif + return ReadCompressed( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true ); + } + } + + template <> + inline bool BitStream::ReadCompressed(SystemAddress &outTemplateVar) + { + return Read(outTemplateVar); + } + + template <> + inline bool BitStream::ReadCompressed(uint24_t &outTemplateVar) + { + return Read(outTemplateVar); + } + + template <> + inline bool BitStream::ReadCompressed(RakNetGUID &outTemplateVar) + { + return Read(outTemplateVar); + } + + template <> + inline bool BitStream::ReadCompressed(bool &outTemplateVar) + { + return Read(outTemplateVar); + } + + /// For values between -1 and 1 + template <> + inline bool BitStream::ReadCompressed(float &outTemplateVar) + { + unsigned short compressedFloat; + if (Read(compressedFloat)) + { + outTemplateVar = ((float)compressedFloat / 32767.5f - 1.0f); + return true; + } + return false; + } + + /// For values between -1 and 1 + template <> + inline bool BitStream::ReadCompressed(double &outTemplateVar) + { + uint32_t compressedFloat; + if (Read(compressedFloat)) + { + outTemplateVar = ((double)compressedFloat / 2147483648.0 - 1.0); + return true; + } + return false; + } + + /// For strings + template <> + inline bool BitStream::ReadCompressed(RakString &outTemplateVar) + { + return outTemplateVar.DeserializeCompressed(this,false); + } + template <> + inline bool BitStream::ReadCompressed(RakWString &outTemplateVar) + { + return outTemplateVar.Deserialize(this); + } + template <> + inline bool BitStream::ReadCompressed(char *&outTemplateVar) + { + return RakString::DeserializeCompressed(outTemplateVar,this,false); + } + template <> + inline bool BitStream::ReadCompressed(wchar_t *&outTemplateVar) + { + return RakWString::Deserialize(outTemplateVar,this); + } + template <> + inline bool BitStream::ReadCompressed(unsigned char *&outTemplateVar) + { + return RakString::DeserializeCompressed((char*) outTemplateVar,this,false); + } + + /// \brief Read any integral type from a bitstream. + /// \details If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// the current value will be updated. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// ReadCompressedDelta is only valid from a previous call to WriteDelta + /// \param[in] outTemplateVar The value to read + template + inline bool BitStream::ReadCompressedDelta(templateType &outTemplateVar) + { + bool dataWritten; + bool success; + success=Read(dataWritten); + if (dataWritten) + success=ReadCompressed(outTemplateVar); + return success; + } + + /// \brief Read a bool from a bitstream. + /// \param[in] outTemplateVar The value to read + template <> + inline bool BitStream::ReadCompressedDelta(bool &outTemplateVar) + { + return Read(outTemplateVar); + } + + template + void BitStream::WriteCasted( const sourceType &value ) + { + destinationType val = (destinationType) value; + Write(val); + } + + template + void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, bool allowOutsideRange ) + { + int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); + WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); + } + template + void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, const int requiredBits, bool allowOutsideRange ) + { + RakAssert(maximum>=minimum); + RakAssert(allowOutsideRange==true || (value>=minimum && value<=maximum)); + if (allowOutsideRange) + { + if (valuemaximum) + { + Write(true); + Write(value); + return; + } + Write(false); + } + templateType valueOffMin=value-minimum; + if (IsBigEndian()==true) + { + unsigned char output[sizeof(templateType)]; + ReverseBytes((unsigned char*)&valueOffMin, output, sizeof(templateType)); + WriteBits(output,requiredBits); + } + else + { + WriteBits((unsigned char*) &valueOffMin,requiredBits); + } + } + + template // templateType for this function must be a float or double + void BitStream::WriteNormVector( templateType x, templateType y, templateType z ) + { +#ifdef _DEBUG + RakAssert(x <= 1.01 && y <= 1.01 && z <= 1.01 && x >= -1.01 && y >= -1.01 && z >= -1.01); +#endif + + WriteFloat16((float)x,-1.0f,1.0f); + WriteFloat16((float)y,-1.0f,1.0f); + WriteFloat16((float)z,-1.0f,1.0f); + } + + template // templateType for this function must be a float or double + void BitStream::WriteVector( templateType x, templateType y, templateType z ) + { + templateType magnitude = sqrt(x * x + y * y + z * z); + Write((float)magnitude); + if (magnitude > 0.00001f) + { + WriteCompressed((float)(x/magnitude)); + WriteCompressed((float)(y/magnitude)); + WriteCompressed((float)(z/magnitude)); + // Write((unsigned short)((x/magnitude+1.0f)*32767.5f)); + // Write((unsigned short)((y/magnitude+1.0f)*32767.5f)); + // Write((unsigned short)((z/magnitude+1.0f)*32767.5f)); + } + } + + template // templateType for this function must be a float or double + void BitStream::WriteNormQuat( templateType w, templateType x, templateType y, templateType z) + { + Write((bool)(w<0.0)); + Write((bool)(x<0.0)); + Write((bool)(y<0.0)); + Write((bool)(z<0.0)); + Write((unsigned short)(fabs(x)*65535.0)); + Write((unsigned short)(fabs(y)*65535.0)); + Write((unsigned short)(fabs(z)*65535.0)); + // Leave out w and calculate it on the target + } + + template // templateType for this function must be a float or double + void BitStream::WriteOrthMatrix( + templateType m00, templateType m01, templateType m02, + templateType m10, templateType m11, templateType m12, + templateType m20, templateType m21, templateType m22 ) + { + + double qw; + double qx; + double qy; + double qz; + + // Convert matrix to quat + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ + float sum; + sum = 1 + m00 + m11 + m22; + if (sum < 0.0f) sum=0.0f; + qw = sqrt( sum ) / 2; + sum = 1 + m00 - m11 - m22; + if (sum < 0.0f) sum=0.0f; + qx = sqrt( sum ) / 2; + sum = 1 - m00 + m11 - m22; + if (sum < 0.0f) sum=0.0f; + qy = sqrt( sum ) / 2; + sum = 1 - m00 - m11 + m22; + if (sum < 0.0f) sum=0.0f; + qz = sqrt( sum ) / 2; + if (qw < 0.0) qw=0.0; + if (qx < 0.0) qx=0.0; + if (qy < 0.0) qy=0.0; + if (qz < 0.0) qz=0.0; + qx = _copysign( (double) qx, (double) (m21 - m12) ); + qy = _copysign( (double) qy, (double) (m02 - m20) ); + qz = _copysign( (double) qz, (double) (m10 - m01) ); + + WriteNormQuat(qw,qx,qy,qz); + } + + template + bool BitStream::ReadCasted( sourceType &value ) + { + serializationType val; + bool success = Read(val); + value=(sourceType) val; + return success; + } + + template + bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange ) + { + int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); + return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); + } + template + bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange ) + { + RakAssert(maximum>=minimum); + if (allowOutsideRange) + { + bool isOutsideRange; + Read(isOutsideRange); + if (isOutsideRange) + return Read(value); + } + unsigned char output[sizeof(templateType)]; + memset(output,0,sizeof(output)); + bool success = ReadBits(output,requiredBits); + if (success) + { + if (IsBigEndian()==true) + ReverseBytesInPlace(output,sizeof(output)); + memcpy(&value,output,sizeof(output)); + + value+=minimum; + } + + return success; + } + + template // templateType for this function must be a float or double + bool BitStream::ReadNormVector( templateType &x, templateType &y, templateType &z ) + { + float xIn,yIn,zIn; + ReadFloat16(xIn,-1.0f,1.0f); + ReadFloat16(yIn,-1.0f,1.0f); + ReadFloat16(zIn,-1.0f,1.0f); + x=xIn; + y=yIn; + z=zIn; + return true; + } + + template // templateType for this function must be a float or double + bool BitStream::ReadVector( templateType &x, templateType &y, templateType &z ) + { + float magnitude; + //unsigned short sx,sy,sz; + if (!Read(magnitude)) + return false; + if (magnitude>0.00001f) + { + // Read(sx); + // Read(sy); + // if (!Read(sz)) + // return false; + // x=((float)sx / 32767.5f - 1.0f) * magnitude; + // y=((float)sy / 32767.5f - 1.0f) * magnitude; + // z=((float)sz / 32767.5f - 1.0f) * magnitude; + float cx,cy,cz; + ReadCompressed(cx); + ReadCompressed(cy); + if (!ReadCompressed(cz)) + return false; + x=cx; + y=cy; + z=cz; + x*=magnitude; + y*=magnitude; + z*=magnitude; + } + else + { + x=0.0; + y=0.0; + z=0.0; + } + return true; + } + + template // templateType for this function must be a float or double + bool BitStream::ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z) + { + bool cwNeg=false, cxNeg=false, cyNeg=false, czNeg=false; + unsigned short cx,cy,cz; + Read(cwNeg); + Read(cxNeg); + Read(cyNeg); + Read(czNeg); + Read(cx); + Read(cy); + if (!Read(cz)) + return false; + + // Calculate w from x,y,z + x=(templateType)(cx/65535.0); + y=(templateType)(cy/65535.0); + z=(templateType)(cz/65535.0); + if (cxNeg) x=-x; + if (cyNeg) y=-y; + if (czNeg) z=-z; + float difference = 1.0f - x*x - y*y - z*z; + if (difference < 0.0f) + difference=0.0f; + w = (templateType)(sqrt(difference)); + if (cwNeg) + w=-w; + + return true; + } + + template // templateType for this function must be a float or double + bool BitStream::ReadOrthMatrix( + templateType &m00, templateType &m01, templateType &m02, + templateType &m10, templateType &m11, templateType &m12, + templateType &m20, templateType &m21, templateType &m22 ) + { + float qw,qx,qy,qz; + if (!ReadNormQuat(qw,qx,qy,qz)) + return false; + + // Quat to orthogonal rotation matrix + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm + double sqw = (double)qw*(double)qw; + double sqx = (double)qx*(double)qx; + double sqy = (double)qy*(double)qy; + double sqz = (double)qz*(double)qz; + m00 = (templateType)(sqx - sqy - sqz + sqw); // since sqw + sqx + sqy + sqz =1 + m11 = (templateType)(-sqx + sqy - sqz + sqw); + m22 = (templateType)(-sqx - sqy + sqz + sqw); + + double tmp1 = (double)qx*(double)qy; + double tmp2 = (double)qz*(double)qw; + m10 = (templateType)(2.0 * (tmp1 + tmp2)); + m01 = (templateType)(2.0 * (tmp1 - tmp2)); + + tmp1 = (double)qx*(double)qz; + tmp2 = (double)qy*(double)qw; + m20 =(templateType)(2.0 * (tmp1 - tmp2)); + m02 = (templateType)(2.0 * (tmp1 + tmp2)); + tmp1 = (double)qy*(double)qz; + tmp2 = (double)qx*(double)qw; + m21 = (templateType)(2.0 * (tmp1 + tmp2)); + m12 = (templateType)(2.0 * (tmp1 - tmp2)); + + return true; + } + + template + BitStream& operator<<(BitStream& out, templateType& c) + { + out.Write(c); + return out; + } + template + BitStream& operator>>(BitStream& in, templateType& c) + { + bool success = in.Read(c); + RakAssert(success); + return in; + } + +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + +#endif // VC6 diff --git a/project/lib_projects/raknet/jni/RaknetSources/CCRakNetSlidingWindow.cpp b/project/lib_projects/raknet/jni/RaknetSources/CCRakNetSlidingWindow.cpp index ccc9757..cf32151 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CCRakNetSlidingWindow.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/CCRakNetSlidingWindow.cpp @@ -1,332 +1,332 @@ -#include "CCRakNetSlidingWindow.h" - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1 - -static const double UNSET_TIME_US=-1; - -#if CC_TIME_TYPE_BYTES==4 -static const CCTimeType SYN=10; -#else -static const CCTimeType SYN=10000; -#endif - -#include "MTUSize.h" -#include -#include -#include -#include "RakAssert.h" -#include "RakAlloca.h" - -using namespace RakNet; - -// ****************************************************** PUBLIC METHODS ****************************************************** - -CCRakNetSlidingWindow::CCRakNetSlidingWindow() -{ -} -// ---------------------------------------------------------------------------------------------------------------------------- -CCRakNetSlidingWindow::~CCRakNetSlidingWindow() -{ - -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::Init(CCTimeType curTime, uint32_t maxDatagramPayload) -{ - (void) curTime; - - RTT=UNSET_TIME_US; - MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload; - cwnd=maxDatagramPayload; - ssThresh=0.0; - oldestUnsentAck=0; - nextDatagramSequenceNumber=0; - nextCongestionControlBlock=0; - backoffThisBlock=speedUpThisBlock=false; - expectedNextSequenceNumber=0; - _isContinuousSend=false; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::Update(CCTimeType curTime, bool hasDataToSendOrResend) -{ - (void) curTime; - (void) hasDataToSendOrResend; -} -// ---------------------------------------------------------------------------------------------------------------------------- -int CCRakNetSlidingWindow::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) -{ - (void) curTime; - (void) isContinuousSend; - (void) timeSinceLastTick; - - return unacknowledgedBytes; -} -// ---------------------------------------------------------------------------------------------------------------------------- -int CCRakNetSlidingWindow::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) -{ - (void) curTime; - (void) timeSinceLastTick; - - _isContinuousSend=isContinuousSend; - - if (unacknowledgedBytes<=cwnd) - return (int) (cwnd-unacknowledgedBytes); - else - return 0; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetSlidingWindow::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick) -{ - CCTimeType rto = GetSenderRTOForACK(); - (void) estimatedTimeToNextTick; - - // iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 - if (rto==(CCTimeType) UNSET_TIME_US) - { - // Unknown how long until the remote system will retransmit, so better send right away - return true; - } - - return curTime >= oldestUnsentAck + SYN; -} -// ---------------------------------------------------------------------------------------------------------------------------- -DatagramSequenceNumberType CCRakNetSlidingWindow::GetNextDatagramSequenceNumber(void) -{ - return nextDatagramSequenceNumber; -} -// ---------------------------------------------------------------------------------------------------------------------------- -DatagramSequenceNumberType CCRakNetSlidingWindow::GetAndIncrementNextDatagramSequenceNumber(void) -{ - DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber; - nextDatagramSequenceNumber++; - return dsnt; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnSendBytes(CCTimeType curTime, uint32_t numBytes) -{ - (void) curTime; - (void) numBytes; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime) -{ - (void) curTime; - (void) sizeInBytes; - (void) datagramSequenceNumber; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetSlidingWindow::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount) -{ - (void) curTime; - (void) sizeInBytes; - (void) isContinuousSend; - - if (oldestUnsentAck==0) - oldestUnsentAck=curTime; - - if (datagramSequenceNumber==expectedNextSequenceNumber) - { - *skippedMessageCount=0; - expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; - } - else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber)) - { - *skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber; - // Sanity check, just use timeout resend if this was really valid - if (*skippedMessageCount>1000) - { - // During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice - if (*skippedMessageCount>(uint32_t)50000) - return false; - *skippedMessageCount=1000; - } - expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; - } - else - { - *skippedMessageCount=0; - } - - return true; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnResend(CCTimeType curTime) -{ - (void) curTime; - - if (_isContinuousSend && backoffThisBlock==false && cwnd>MAXIMUM_MTU_INCLUDING_UDP_HEADER*2) - { - ssThresh=cwnd/2; - if (ssThreshssThresh && ssThresh!=0) - { - cwnd=ssThresh; - cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd; - } - } - } - } - else - { - if (isNewCongestionControlPeriod) - cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd; - } -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ) -{ - (void) sequenceNumber; - - OnResend(curTime); -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS) -{ - (void) curTime; - (void) _B; - (void) _AS; - - *hasBAndAS=false; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnSendAck(CCTimeType curTime, uint32_t numBytes) -{ - (void) curTime; - (void) numBytes; - - oldestUnsentAck=0; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnSendNACK(CCTimeType curTime, uint32_t numBytes) -{ - (void) curTime; - (void) numBytes; - -} -// ---------------------------------------------------------------------------------------------------------------------------- -CCTimeType CCRakNetSlidingWindow::GetRTOForRetransmission(void) const -{ -#if CC_TIME_TYPE_BYTES==4 - const CCTimeType maxThreshold=2000; - const CCTimeType minThreshold=100; -#else - const CCTimeType maxThreshold=2000000; - const CCTimeType minThreshold=100000; -#endif - - if (RTT==UNSET_TIME_US) - { - return maxThreshold; - } - - if (RTT * 3 > maxThreshold) - return maxThreshold; - if (RTT * 3 < minThreshold) - return minThreshold; - return (CCTimeType) RTT * 3; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::SetMTU(uint32_t bytes) -{ - MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes; -} -// ---------------------------------------------------------------------------------------------------------------------------- -uint32_t CCRakNetSlidingWindow::GetMTU(void) const -{ - return MAXIMUM_MTU_INCLUDING_UDP_HEADER; -} -// ---------------------------------------------------------------------------------------------------------------------------- -BytesPerMicrosecond CCRakNetSlidingWindow::GetLocalReceiveRate(CCTimeType currentTime) const -{ - (void) currentTime; - - return 0; // TODO -} -// ---------------------------------------------------------------------------------------------------------------------------- -double CCRakNetSlidingWindow::GetRTT(void) const -{ - if (RTT==UNSET_TIME_US) - return 0.0; - return RTT; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetSlidingWindow::GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) -{ - // a > b? - const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2); - return b!=a && b-a>halfSpan; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetSlidingWindow::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) -{ - // a < b? - const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2; - return b!=a && b-a +#include +#include +#include "RakAssert.h" +#include "RakAlloca.h" + +using namespace RakNet; + +// ****************************************************** PUBLIC METHODS ****************************************************** + +CCRakNetSlidingWindow::CCRakNetSlidingWindow() +{ +} +// ---------------------------------------------------------------------------------------------------------------------------- +CCRakNetSlidingWindow::~CCRakNetSlidingWindow() +{ + +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::Init(CCTimeType curTime, uint32_t maxDatagramPayload) +{ + (void) curTime; + + RTT=UNSET_TIME_US; + MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload; + cwnd=maxDatagramPayload; + ssThresh=0.0; + oldestUnsentAck=0; + nextDatagramSequenceNumber=0; + nextCongestionControlBlock=0; + backoffThisBlock=speedUpThisBlock=false; + expectedNextSequenceNumber=0; + _isContinuousSend=false; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::Update(CCTimeType curTime, bool hasDataToSendOrResend) +{ + (void) curTime; + (void) hasDataToSendOrResend; +} +// ---------------------------------------------------------------------------------------------------------------------------- +int CCRakNetSlidingWindow::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) +{ + (void) curTime; + (void) isContinuousSend; + (void) timeSinceLastTick; + + return unacknowledgedBytes; +} +// ---------------------------------------------------------------------------------------------------------------------------- +int CCRakNetSlidingWindow::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) +{ + (void) curTime; + (void) timeSinceLastTick; + + _isContinuousSend=isContinuousSend; + + if (unacknowledgedBytes<=cwnd) + return (int) (cwnd-unacknowledgedBytes); + else + return 0; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetSlidingWindow::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick) +{ + CCTimeType rto = GetSenderRTOForACK(); + (void) estimatedTimeToNextTick; + + // iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 + if (rto==(CCTimeType) UNSET_TIME_US) + { + // Unknown how long until the remote system will retransmit, so better send right away + return true; + } + + return curTime >= oldestUnsentAck + SYN; +} +// ---------------------------------------------------------------------------------------------------------------------------- +DatagramSequenceNumberType CCRakNetSlidingWindow::GetNextDatagramSequenceNumber(void) +{ + return nextDatagramSequenceNumber; +} +// ---------------------------------------------------------------------------------------------------------------------------- +DatagramSequenceNumberType CCRakNetSlidingWindow::GetAndIncrementNextDatagramSequenceNumber(void) +{ + DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber; + nextDatagramSequenceNumber++; + return dsnt; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnSendBytes(CCTimeType curTime, uint32_t numBytes) +{ + (void) curTime; + (void) numBytes; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime) +{ + (void) curTime; + (void) sizeInBytes; + (void) datagramSequenceNumber; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetSlidingWindow::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount) +{ + (void) curTime; + (void) sizeInBytes; + (void) isContinuousSend; + + if (oldestUnsentAck==0) + oldestUnsentAck=curTime; + + if (datagramSequenceNumber==expectedNextSequenceNumber) + { + *skippedMessageCount=0; + expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; + } + else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber)) + { + *skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber; + // Sanity check, just use timeout resend if this was really valid + if (*skippedMessageCount>1000) + { + // During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice + if (*skippedMessageCount>(uint32_t)50000) + return false; + *skippedMessageCount=1000; + } + expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; + } + else + { + *skippedMessageCount=0; + } + + return true; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnResend(CCTimeType curTime) +{ + (void) curTime; + + if (_isContinuousSend && backoffThisBlock==false && cwnd>MAXIMUM_MTU_INCLUDING_UDP_HEADER*2) + { + ssThresh=cwnd/2; + if (ssThreshssThresh && ssThresh!=0) + { + cwnd=ssThresh; + cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd; + } + } + } + } + else + { + if (isNewCongestionControlPeriod) + cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd; + } +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ) +{ + (void) sequenceNumber; + + OnResend(curTime); +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS) +{ + (void) curTime; + (void) _B; + (void) _AS; + + *hasBAndAS=false; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnSendAck(CCTimeType curTime, uint32_t numBytes) +{ + (void) curTime; + (void) numBytes; + + oldestUnsentAck=0; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnSendNACK(CCTimeType curTime, uint32_t numBytes) +{ + (void) curTime; + (void) numBytes; + +} +// ---------------------------------------------------------------------------------------------------------------------------- +CCTimeType CCRakNetSlidingWindow::GetRTOForRetransmission(void) const +{ +#if CC_TIME_TYPE_BYTES==4 + const CCTimeType maxThreshold=2000; + const CCTimeType minThreshold=100; +#else + const CCTimeType maxThreshold=2000000; + const CCTimeType minThreshold=100000; +#endif + + if (RTT==UNSET_TIME_US) + { + return maxThreshold; + } + + if (RTT * 3 > maxThreshold) + return maxThreshold; + if (RTT * 3 < minThreshold) + return minThreshold; + return (CCTimeType) RTT * 3; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::SetMTU(uint32_t bytes) +{ + MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes; +} +// ---------------------------------------------------------------------------------------------------------------------------- +uint32_t CCRakNetSlidingWindow::GetMTU(void) const +{ + return MAXIMUM_MTU_INCLUDING_UDP_HEADER; +} +// ---------------------------------------------------------------------------------------------------------------------------- +BytesPerMicrosecond CCRakNetSlidingWindow::GetLocalReceiveRate(CCTimeType currentTime) const +{ + (void) currentTime; + + return 0; // TODO +} +// ---------------------------------------------------------------------------------------------------------------------------- +double CCRakNetSlidingWindow::GetRTT(void) const +{ + if (RTT==UNSET_TIME_US) + return 0.0; + return RTT; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetSlidingWindow::GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) +{ + // a > b? + const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2); + return b!=a && b-a>halfSpan; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetSlidingWindow::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) +{ + // a < b? + const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2; + return b!=a && b-a b, accounting for variable overflow? - static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); - /// Is a < b, accounting for variable overflow? - static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); -// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond); - uint64_t GetBytesPerSecondLimitByCongestionControl(void) const; - - protected: - - // Maximum amount of bytes that the user can send, e.g. the size of one full datagram - uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER; - - double RTT; - - double cwnd; // max bytes on wire - double ssThresh; // Threshhold between slow start and congestion avoidance - - /// When we get an ack, if oldestUnsentAck==0, set it to the current time - /// When we send out acks, set oldestUnsentAck to 0 - CCTimeType oldestUnsentAck; - - CCTimeType GetSenderRTOForACK(void) const; - - /// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment - DatagramSequenceNumberType nextDatagramSequenceNumber; - DatagramSequenceNumberType nextCongestionControlBlock; - bool backoffThisBlock, speedUpThisBlock; - /// Track which datagram sequence numbers have arrived. - /// If a sequence number is skipped, send a NAK for all skipped messages - DatagramSequenceNumberType expectedNextSequenceNumber; - - bool _isContinuousSend; - - bool IsInSlowStart(void) const; -}; - -} - -#endif - -#endif +/* +http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html + +cwnd=max bytes allowed on wire at once + +Start: +cwnd=mtu +ssthresh=unlimited + +Slow start: +On ack cwnd*=2 + +congestion avoidance: +On ack during new period +cwnd+=mtu*mtu/cwnd + +on loss or duplicate ack during period: +sshtresh=cwnd/2 +cwnd=MTU +This reenters slow start + +If cwnd < ssthresh, then use slow start +else use congestion avoidance + + +*/ + +#include "RakNetDefines.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1 + +#ifndef __CONGESTION_CONTROL_SLIDING_WINDOW_H +#define __CONGESTION_CONTROL_SLIDING_WINDOW_H + +#include "NativeTypes.h" +#include "RakNetTime.h" +#include "RakNetTypes.h" +#include "DS_Queue.h" + +/// Sizeof an UDP header in byte +#define UDP_HEADER_SIZE 28 + +#define CC_DEBUG_PRINTF_1(x) +#define CC_DEBUG_PRINTF_2(x,y) +#define CC_DEBUG_PRINTF_3(x,y,z) +#define CC_DEBUG_PRINTF_4(x,y,z,a) +#define CC_DEBUG_PRINTF_5(x,y,z,a,b) +//#define CC_DEBUG_PRINTF_1(x) printf(x) +//#define CC_DEBUG_PRINTF_2(x,y) printf(x,y) +//#define CC_DEBUG_PRINTF_3(x,y,z) printf(x,y,z) +//#define CC_DEBUG_PRINTF_4(x,y,z,a) printf(x,y,z,a) +//#define CC_DEBUG_PRINTF_5(x,y,z,a,b) printf(x,y,z,a,b) + +/// Set to 4 if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 +#define CC_TIME_TYPE_BYTES 8 + + +typedef RakNet::TimeUS CCTimeType; + + + + +typedef RakNet::uint24_t DatagramSequenceNumberType; +typedef double BytesPerMicrosecond; +typedef double BytesPerSecond; +typedef double MicrosecondsPerByte; + +namespace RakNet +{ + +class CCRakNetSlidingWindow +{ + public: + + CCRakNetSlidingWindow(); + ~CCRakNetSlidingWindow(); + + /// Reset all variables to their initial states, for a new connection + void Init(CCTimeType curTime, uint32_t maxDatagramPayload); + + /// Update over time + void Update(CCTimeType curTime, bool hasDataToSendOrResend); + + int GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); + int GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); + + /// Acks do not have to be sent immediately. Instead, they can be buffered up such that groups of acks are sent at a time + /// This reduces overall bandwidth usage + /// How long they can be buffered depends on the retransmit time of the sender + /// Should call once per update tick, and send if needed + bool ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick); + + /// Every data packet sent must contain a sequence number + /// Call this function to get it. The sequence number is passed into OnGotPacketPair() + DatagramSequenceNumberType GetAndIncrementNextDatagramSequenceNumber(void); + DatagramSequenceNumberType GetNextDatagramSequenceNumber(void); + + /// Call this when you send packets + /// Every 15th and 16th packets should be sent as a packet pair if possible + /// When packets marked as a packet pair arrive, pass to OnGotPacketPair() + /// When any packets arrive, (additionally) pass to OnGotPacket + /// Packets should contain our system time, so we can pass rtt to OnNonDuplicateAck() + void OnSendBytes(CCTimeType curTime, uint32_t numBytes); + + /// Call this when you get a packet pair + void OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime); + + /// Call this when you get a packet (including packet pairs) + /// If the DatagramSequenceNumberType is out of order, skippedMessageCount will be non-zero + /// In that case, send a NAK for every sequence number up to that count + bool OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount); + + /// Call when you get a NAK, with the sequence number of the lost message + /// Affects the congestion control + void OnResend(CCTimeType curTime); + void OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber); + + /// Call this when an ACK arrives. + /// hasBAndAS are possibly written with the ack, see OnSendAck() + /// B and AS are used in the calculations in UpdateWindowSizeAndAckOnAckPerSyn + /// B and AS are updated at most once per SYN + void OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ); + void OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ); + + /// Call when you send an ack, to see if the ack should have the B and AS parameters transmitted + /// Call before calling OnSendAck() + void OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS); + + /// Call when we send an ack, to write B and AS if needed + /// B and AS are only written once per SYN, to prevent slow calculations + /// Also updates SND, the period between sends, since data is written out + /// Be sure to call OnSendAckGetBAndAS() before calling OnSendAck(), since whether you write it or not affects \a numBytes + void OnSendAck(CCTimeType curTime, uint32_t numBytes); + + /// Call when we send a NACK + /// Also updates SND, the period between sends, since data is written out + void OnSendNACK(CCTimeType curTime, uint32_t numBytes); + + /// Retransmission time out for the sender + /// If the time difference between when a message was last transmitted, and the current time is greater than RTO then packet is eligible for retransmission, pending congestion control + /// RTO = (RTT + 4 * RTTVar) + SYN + /// If we have been continuously sending for the last RTO, and no ACK or NAK at all, SND*=2; + /// This is per message, which is different from UDT, but RakNet supports packetloss with continuing data where UDT is only RELIABLE_ORDERED + /// Minimum value is 100 milliseconds + CCTimeType GetRTOForRetransmission(void) const; + + /// Set the maximum amount of data that can be sent in one datagram + /// Default to MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE + void SetMTU(uint32_t bytes); + + /// Return what was set by SetMTU() + uint32_t GetMTU(void) const; + + /// Query for statistics + BytesPerMicrosecond GetLocalSendRate(void) const {return 0;} + BytesPerMicrosecond GetLocalReceiveRate(CCTimeType currentTime) const; + BytesPerMicrosecond GetRemoveReceiveRate(void) const {return 0;} + //BytesPerMicrosecond GetEstimatedBandwidth(void) const {return B;} + BytesPerMicrosecond GetEstimatedBandwidth(void) const {return GetLinkCapacityBytesPerSecond()*1000000.0;} + double GetLinkCapacityBytesPerSecond(void) const {return 0;} + + /// Query for statistics + double GetRTT(void) const; + + bool GetIsInSlowStart(void) const {return IsInSlowStart();} + uint32_t GetCWNDLimit(void) const {return (uint32_t) 0;} + + + /// Is a > b, accounting for variable overflow? + static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); + /// Is a < b, accounting for variable overflow? + static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); +// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond); + uint64_t GetBytesPerSecondLimitByCongestionControl(void) const; + + protected: + + // Maximum amount of bytes that the user can send, e.g. the size of one full datagram + uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER; + + double RTT; + + double cwnd; // max bytes on wire + double ssThresh; // Threshhold between slow start and congestion avoidance + + /// When we get an ack, if oldestUnsentAck==0, set it to the current time + /// When we send out acks, set oldestUnsentAck to 0 + CCTimeType oldestUnsentAck; + + CCTimeType GetSenderRTOForACK(void) const; + + /// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment + DatagramSequenceNumberType nextDatagramSequenceNumber; + DatagramSequenceNumberType nextCongestionControlBlock; + bool backoffThisBlock, speedUpThisBlock; + /// Track which datagram sequence numbers have arrived. + /// If a sequence number is skipped, send a NAK for all skipped messages + DatagramSequenceNumberType expectedNextSequenceNumber; + + bool _isContinuousSend; + + bool IsInSlowStart(void) const; +}; + +} + +#endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/CCRakNetUDT.cpp b/project/lib_projects/raknet/jni/RaknetSources/CCRakNetUDT.cpp index dcf942e..c6a8c79 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CCRakNetUDT.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/CCRakNetUDT.cpp @@ -1,798 +1,798 @@ -#include "CCRakNetUDT.h" - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 - -#include "Rand.h" -#include "MTUSize.h" -#include -#include -#include -//#include -#include "RakAssert.h" -#include "RakAlloca.h" - -using namespace RakNet; - -static const double UNSET_TIME_US=-1; -static const double CWND_MIN_THRESHOLD=2.0; -static const double UNDEFINED_TRANSFER_RATE=0.0; -/// Interval at which to update aspects of the system -/// 1. send acks -/// 2. update time interval between outgoing packets -/// 3, Yodate retransmit timeout -#if CC_TIME_TYPE_BYTES==4 -static const CCTimeType SYN=10; -#else -static const CCTimeType SYN=10000; -#endif - -#if CC_TIME_TYPE_BYTES==4 -#define MAX_RTT 1000 -#define RTT_TOLERANCE 30 -#else -#define MAX_RTT 1000000 -#define RTT_TOLERANCE 30000 -#endif - - -double RTTVarMultiple=4.0; - - -// ****************************************************** PUBLIC METHODS ****************************************************** - -CCRakNetUDT::CCRakNetUDT() -{ -} - -// ---------------------------------------------------------------------------------------------------------------------------- - -CCRakNetUDT::~CCRakNetUDT() -{ -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::Init(CCTimeType curTime, uint32_t maxDatagramPayload) -{ - (void) curTime; - - nextSYNUpdate=0; - packetPairRecieptHistoryWriteIndex=0; - packetArrivalHistoryWriteIndex=0; - packetArrivalHistoryWriteCount=0; - RTT=UNSET_TIME_US; - // RTTVar=UNSET_TIME_US; - isInSlowStart=true; - NAKCount=1000; - AvgNAKNum=1; - DecInterval=1; - DecCount=0; - nextDatagramSequenceNumber=0; - lastPacketPairPacketArrivalTime=0; - lastPacketPairSequenceNumber=(DatagramSequenceNumberType)(const uint32_t)-1; - lastPacketArrivalTime=0; - CWND=CWND_MIN_THRESHOLD; - lastUpdateWindowSizeAndAck=0; - lastTransmitOfBAndAS=0; - ExpCount=1.0; - totalUserDataBytesSent=0; - oldestUnsentAck=0; - MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload; - CWND_MAX_THRESHOLD=RESEND_BUFFER_ARRAY_LENGTH; -#if CC_TIME_TYPE_BYTES==4 - const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) 3.6; -#else - const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) .0036; -#endif - -#if CC_TIME_TYPE_BYTES==4 - lastRttOnIncreaseSendRate=1000; -#else - lastRttOnIncreaseSendRate=1000000; -#endif - nextCongestionControlBlock=0; - lastRtt=0; - - // B=DEFAULT_TRANSFER_RATE; - AS=UNDEFINED_TRANSFER_RATE; - const MicrosecondsPerByte DEFAULT_BYTE_INTERVAL=(MicrosecondsPerByte) (1.0/DEFAULT_TRANSFER_RATE); - SND=DEFAULT_BYTE_INTERVAL; - expectedNextSequenceNumber=0; - sendBAndASCount=0; - packetArrivalHistoryContinuousGapsIndex=0; - //packetPairRecipetHistoryGapsIndex=0; - hasWrittenToPacketPairReceiptHistory=false; - InitPacketArrivalHistory(); - - estimatedLinkCapacityBytesPerSecond=0; - bytesCanSendThisTick=0; - hadPacketlossThisBlock=false; - pingsLastInterval.Clear(__FILE__,__LINE__); -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::SetMTU(uint32_t bytes) -{ - MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes; -} -// ---------------------------------------------------------------------------------------------------------------------------- -uint32_t CCRakNetUDT::GetMTU(void) const -{ - return MAXIMUM_MTU_INCLUDING_UDP_HEADER; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::Update(CCTimeType curTime, bool hasDataToSendOrResend) -{ - (void) hasDataToSendOrResend; - (void) curTime; - - return; - - // I suspect this is causing major lag - - /* - if (hasDataToSendOrResend==false) - halveSNDOnNoDataTime=0; - else if (halveSNDOnNoDataTime==0) - { - UpdateHalveSNDOnNoDataTime(curTime); - ExpCount=1.0; - } - - // If you send, and get no data at all from that time to RTO, then halve send rate7 - if (HasHalveSNDOnNoDataTimeElapsed(curTime)) - { - /// 2000 bytes per second - /// 0.0005 seconds per byte - /// 0.5 milliseconds per byte - /// 500 microseconds per byte - // printf("No incoming data, halving send rate\n"); - SND*=2.0; - CapMinSnd(_FILE_AND_LINE_); - ExpCount+=1.0; - if (ExpCount>8.0) - ExpCount=8.0; - - UpdateHalveSNDOnNoDataTime(curTime); - } - */ -} -// ---------------------------------------------------------------------------------------------------------------------------- -int CCRakNetUDT::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) -{ - (void) curTime; - - if (isInSlowStart) - { - uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER); - return CWNDLimit; - } - return GetTransmissionBandwidth(curTime,timeSinceLastTick,unacknowledgedBytes,isContinuousSend); -} -// ---------------------------------------------------------------------------------------------------------------------------- -int CCRakNetUDT::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) -{ - (void) curTime; - - if (isInSlowStart) - { - uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER-unacknowledgedBytes); - return CWNDLimit; - } - if (bytesCanSendThisTick>0) - bytesCanSendThisTick=0; - -#if CC_TIME_TYPE_BYTES==4 - if (isContinuousSend==false && timeSinceLastTick>100) - timeSinceLastTick=100; -#else - if (isContinuousSend==false && timeSinceLastTick>100000) - timeSinceLastTick=100000; -#endif - - bytesCanSendThisTick=(int)((double)timeSinceLastTick*((double)1.0/SND)+(double)bytesCanSendThisTick); - if (bytesCanSendThisTick>0) - return bytesCanSendThisTick; - return 0; -} -uint64_t CCRakNetUDT::GetBytesPerSecondLimitByCongestionControl(void) const -{ - if (isInSlowStart) - return 0; -#if CC_TIME_TYPE_BYTES==4 - return (uint64_t) ((double)1.0/(SND*1000.0)); -#else - return (uint64_t) ((double)1.0/(SND*1000000.0)); -#endif -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetUDT::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick) -{ - CCTimeType rto = GetSenderRTOForACK(); - - // iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 - if (rto==(CCTimeType) UNSET_TIME_US) - { - // Unknown how long until the remote system will retransmit, so better send right away - return true; - } - - - // CCTimeType remoteRetransmitTime=oldestUnsentAck+rto-RTT*.5; - // CCTimeType ackArrivalTimeIfWeDelay=RTT*.5+estimatedTimeToNextTick+curTime; - // return ackArrivalTimeIfWeDelay= oldestUnsentAck + SYN || - estimatedTimeToNextTick+curTime < oldestUnsentAck+rto-RTT; -} -// ---------------------------------------------------------------------------------------------------------------------------- -DatagramSequenceNumberType CCRakNetUDT::GetNextDatagramSequenceNumber(void) -{ - return nextDatagramSequenceNumber; -} -// ---------------------------------------------------------------------------------------------------------------------------- -DatagramSequenceNumberType CCRakNetUDT::GetAndIncrementNextDatagramSequenceNumber(void) -{ - DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber; - nextDatagramSequenceNumber++; - return dsnt; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnSendBytes(CCTimeType curTime, uint32_t numBytes) -{ - (void) curTime; - - totalUserDataBytesSent+=numBytes; - if (isInSlowStart==false) - bytesCanSendThisTick-=numBytes; -} - -// ****************************************************** PROTECTED METHODS ****************************************************** - -void CCRakNetUDT::SetNextSYNUpdate(CCTimeType currentTime) -{ - nextSYNUpdate+=SYN; - if (nextSYNUpdate < currentTime) - nextSYNUpdate=currentTime+SYN; -} -// ---------------------------------------------------------------------------------------------------------------------------- -BytesPerMicrosecond CCRakNetUDT::ReceiverCalculateDataArrivalRate(CCTimeType curTime) const -{ - (void) curTime; - // Not an instantaneous measurement - /* - if (continuousBytesReceivedStartTime!=0 && curTime>continuousBytesReceivedStartTime) - { - #if CC_TIME_TYPE_BYTES==4 - const CCTimeType threshold=100; - #else - const CCTimeType threshold=100000; - #endif - if (curTime-continuousBytesReceivedStartTime>threshold) - return (BytesPerMicrosecond) continuousBytesReceived/(BytesPerMicrosecond) (curTime-continuousBytesReceivedStartTime); - } - - return UNDEFINED_TRANSFER_RATE; - */ - - - if (packetArrivalHistoryWriteCount=oneEighthMedian && - packetArrivalHistory[i] b? - const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2); - return b!=a && b-a>halfSpan; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetUDT::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) -{ - // a < b? - const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2; - return b!=a && b-amaxThreshold) - return maxThreshold; - return ret; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnResend(CCTimeType curTime) -{ - (void) curTime; - - if (isInSlowStart) - { - if (AS!=UNDEFINED_TRANSFER_RATE) - EndSlowStart(); - return; - } - - if (hadPacketlossThisBlock==false) - { - // Logging - // printf("Sending SLOWER due to Resend, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt ); - - IncreaseTimeBetweenSends(); - hadPacketlossThisBlock=true; - } -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber) -{ - (void) nakSequenceNumber; - (void) curTime; - - if (isInSlowStart) - { - if (AS!=UNDEFINED_TRANSFER_RATE) - EndSlowStart(); - return; - } - - if (hadPacketlossThisBlock==false) - { - // Logging - //printf("Sending SLOWER due to NAK, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt ); - if (pingsLastInterval.Size()>10) - { - for (int i=0; i < 10; i++) - printf("%i, ", pingsLastInterval[pingsLastInterval.Size()-1-i]/1000); - } - printf("\n"); - IncreaseTimeBetweenSends(); - - hadPacketlossThisBlock=true; - } -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::EndSlowStart(void) -{ - RakAssert(isInSlowStart==true); - RakAssert(AS!=UNDEFINED_TRANSFER_RATE); - - // This overestimates - estimatedLinkCapacityBytesPerSecond=AS * 1000000.0; - - isInSlowStart=false; - SND=1.0/AS; - CapMinSnd(_FILE_AND_LINE_); - - // printf("ENDING SLOW START\n"); -#if CC_TIME_TYPE_BYTES==4 - // printf("Initial SND=%f Kilobytes per second\n", 1.0/SND); -#else - // printf("Initial SND=%f Megabytes per second\n", 1.0/SND); -#endif - if (SND > .1) - PrintLowBandwidthWarning(); -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime) -{ - (void) datagramSequenceNumber; - (void) sizeInBytes; - (void) curTime; - -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetUDT::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount) -{ - CC_DEBUG_PRINTF_2("R%i ",datagramSequenceNumber.val); - - if (datagramSequenceNumber==expectedNextSequenceNumber) - { - *skippedMessageCount=0; - expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; - } - else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber)) - { - *skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber; - // Sanity check, just use timeout resend if this was really valid - if (*skippedMessageCount>1000) - { - // During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice - if (*skippedMessageCount>(uint32_t)50000) - return false; - *skippedMessageCount=1000; - } - expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; - } - else - { - *skippedMessageCount=0; - } - - if (curTime>lastPacketArrivalTime) - { - CCTimeType interval = curTime-lastPacketArrivalTime; - - // printf("Packet arrival gap is %I64u\n", (interval)); - - if (isContinuousSend) - { - continuousBytesReceived+=sizeInBytes; - if (continuousBytesReceivedStartTime==0) - continuousBytesReceivedStartTime=lastPacketArrivalTime; - - - mostRecentPacketArrivalHistory=(BytesPerMicrosecond)sizeInBytes/(BytesPerMicrosecond)interval; - - // if (mostRecentPacketArrivalHistory < (BytesPerMicrosecond)0.0035) - // { - // printf("%s:%i LIKELY BUG: Calculated packetArrivalHistory is below 28.8 Kbps modem\nReport to rakkar@jenkinssoftware.com with file and line number\n", _FILE_AND_LINE_); - // } - - packetArrivalHistoryContinuousGaps[packetArrivalHistoryContinuousGapsIndex++]=(int) interval; - packetArrivalHistoryContinuousGapsIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1); - - packetArrivalHistoryWriteCount++; - packetArrivalHistory[packetArrivalHistoryWriteIndex++]=mostRecentPacketArrivalHistory; - // Wrap to 0 at the end of the range - // Assumes power of 2 for CC_RAKNET_UDT_PACKET_HISTORY_LENGTH - packetArrivalHistoryWriteIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1); - } - else - { - continuousBytesReceivedStartTime=0; - continuousBytesReceived=0; - } - - lastPacketArrivalTime=curTime; - } - return true; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ) -{ -#if CC_TIME_TYPE_BYTES==4 - RakAssert(rtt < 10000); -#else - RakAssert(rtt < 10000000); -#endif - (void) _B; - - if (hasBAndAS) - { - /// RakAssert(_B!=UNDEFINED_TRANSFER_RATE && _AS!=UNDEFINED_TRANSFER_RATE); - // B=B * .875 + _B * .125; - // AS is packet arrival rate - RakAssert(_AS!=UNDEFINED_TRANSFER_RATE); - AS=_AS; - CC_DEBUG_PRINTF_4("ArrivalRate=%f linkCap=%f incomingLinkCap=%f\n", _AS,B,_B); - } - - if (oldestUnsentAck==0) - oldestUnsentAck=curTime; - - if (isInSlowStart==true) - { - nextCongestionControlBlock=nextDatagramSequenceNumber; - lastRttOnIncreaseSendRate=rtt; - UpdateWindowSizeAndAckOnAckPreSlowStart(totalUserDataBytesAcked); - } - else - { - UpdateWindowSizeAndAckOnAckPerSyn(curTime, rtt, isContinuousSend, sequenceNumber); - } - - lastUpdateWindowSizeAndAck=curTime; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS) -{ - if (curTime>lastTransmitOfBAndAS+SYN) - { - *_B=0; - *_AS=ReceiverCalculateDataArrivalRate(curTime); - - if (*_AS==UNDEFINED_TRANSFER_RATE) - { - *hasBAndAS=false; - } - else - { - *hasBAndAS=true; - } - } - else - { - *hasBAndAS=false; - } -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnSendAck(CCTimeType curTime, uint32_t numBytes) -{ - (void) numBytes; - (void) curTime; - - // This is not accounted for on the remote system, and thus causes bandwidth to be underutilized - //UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE); - - oldestUnsentAck=0; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnSendNACK(CCTimeType curTime, uint32_t numBytes) -{ - (void) numBytes; - (void) curTime; - - // This is not accounted for on the remote system, and thus causes bandwidth to be underutilized - // UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE); -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked) -{ - // During slow start, max window size is the number of full packets that have been sent out - // CWND=(double) ((double)totalUserDataBytesSent/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER); - CC_DEBUG_PRINTF_3("CWND increasing from %f to %f\n", CWND, (double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER)); - CWND=(double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER); - if (CWND>=CWND_MAX_THRESHOLD) - { - CWND=CWND_MAX_THRESHOLD; - - if (AS!=UNDEFINED_TRANSFER_RATE) - EndSlowStart(); - } - if (CWNDintervalSize) - pingsLastInterval.Pop(); - if (GreaterThan(sequenceNumber, nextCongestionControlBlock) && - sequenceNumber-nextCongestionControlBlock>=intervalSize && - pingsLastInterval.Size()==intervalSize) - { - double slopeSum=0.0; - double average=(double) pingsLastInterval[0]; - int sampleSize=pingsLastInterval.Size(); - for (int i=1; i < sampleSize; i++) - { - slopeSum+=(double)pingsLastInterval[i]-(double)pingsLastInterval[i-1]; - average+=pingsLastInterval[i]; - } - average/=sampleSize; - - if (hadPacketlossThisBlock==true) - { - } - else if (slopeSum < -.10*average) - { - // Logging - //printf("Ping dropping. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); - } - else if (slopeSum > .10*average) - { - // Logging - //printf("Ping rising. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); - IncreaseTimeBetweenSends(); - } - else - { - // Logging - //printf("Ping stable. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); - - // No packetloss over time threshhold, and rtt decreased, so send faster - lastRttOnIncreaseSendRate=rtt; - DecreaseTimeBetweenSends(); - } - - pingsLastInterval.Clear(__FILE__,__LINE__); - hadPacketlossThisBlock=false; - nextCongestionControlBlock=nextDatagramSequenceNumber; - } - - lastRtt=rtt; -} - -// ---------------------------------------------------------------------------------------------------------------------------- -double CCRakNetUDT::BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in) -{ -#if CC_TIME_TYPE_BYTES==4 - const BytesPerMicrosecond factor = 1.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER; -#else - const BytesPerMicrosecond factor = 1000.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER; -#endif - return in * factor; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::InitPacketArrivalHistory(void) -{ - unsigned int i; - for (i=0; i < CC_RAKNET_UDT_PACKET_HISTORY_LENGTH; i++) - { - packetArrivalHistory[i]=UNDEFINED_TRANSFER_RATE; - packetArrivalHistoryContinuousGaps[i]=0; - } - - packetArrivalHistoryWriteCount=0; - continuousBytesReceived=0; - continuousBytesReceivedStartTime=0; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::PrintLowBandwidthWarning(void) -{ - - /* - printf("\n-------LOW BANDWIDTH -----\n"); - if (isInSlowStart==false) - printf("SND=%f Megabytes per second\n", 1.0/SND); - printf("Window size=%f\n", CWND); - printf("Pipe from packet pair = %f megabytes per second\n", B); - printf("RTT=%f milliseconds\n", RTT/1000.0); - printf("RTT Variance=%f milliseconds\n", RTTVar/1000.0); - printf("Retransmission=%i milliseconds\n", GetRTOForRetransmission()/1000); - printf("Packet arrival rate on the remote system=%f megabytes per second\n", AS); - printf("Packet arrival rate on our system=%f megabytes per second\n", ReceiverCalculateDataArrivalRate()); - printf("isInSlowStart=%i\n", isInSlowStart); - printf("---------------\n"); - */ -} -BytesPerMicrosecond CCRakNetUDT::GetLocalReceiveRate(CCTimeType currentTime) const -{ - return ReceiverCalculateDataArrivalRate(currentTime); -} -double CCRakNetUDT::GetRTT(void) const -{ - if (RTT==UNSET_TIME_US) - return 0.0; - return RTT; -} -void CCRakNetUDT::CapMinSnd(const char *file, int line) -{ - (void) file; - (void) line; - - if (SND > 500) - { - SND=500; - CC_DEBUG_PRINTF_3("%s:%i LIKELY BUG: SND has gotten above 500 microseconds between messages (28.8 modem)\nReport to rakkar@jenkinssoftware.com with file and line number\n", file, line); - } -} -void CCRakNetUDT::IncreaseTimeBetweenSends(void) -{ - // In order to converge, bigger numbers have to increase slower and decrease faster - // SND==500 then increment is .02 - // SND==0 then increment is near 0 - // (SND+1.0) brings it to the range of 1 to 501 - // Square the number, which is the range of 1 to 251001 - // Divide by 251001, which is the range of 1/251001 to 1 - - double increment; - increment = .02 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ; - // SND=500 then increment=.02 - // SND=0 then increment=near 0 - SND*=(1.02 - increment); - - // SND=0 then fast increase, slow decrease - // SND=500 then slow increase, fast decrease - CapMinSnd(__FILE__,__LINE__); -} -void CCRakNetUDT::DecreaseTimeBetweenSends(void) -{ - double increment; - increment = .01 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ; - // SND=500 then increment=.01 - // SND=0 then increment=near 0 - SND*=(.99 - increment); -} -/* -void CCRakNetUDT::SetTimeBetweenSendsLimit(unsigned int bitsPerSecond) -{ -// bitsPerSecond / 1000000 = bitsPerMicrosecond -// bitsPerMicrosecond / 8 = BytesPerMicrosecond -// 1 / BytesPerMicrosecond = MicrosecondsPerByte -// 1 / ( (bitsPerSecond / 1000000) / 8 ) = -// 1 / (bitsPerSecond / 8000000) = -// 8000000 / bitsPerSecond - -#if CC_TIME_TYPE_BYTES==4 - MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000 / (MicrosecondsPerByte)bitsPerSecond; -#else - MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000000 / (MicrosecondsPerByte)bitsPerSecond; -#endif - if (limit > SND) - SND=limit; -} -*/ - -#endif +#include "CCRakNetUDT.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 + +#include "Rand.h" +#include "MTUSize.h" +#include +#include +#include +//#include +#include "RakAssert.h" +#include "RakAlloca.h" + +using namespace RakNet; + +static const double UNSET_TIME_US=-1; +static const double CWND_MIN_THRESHOLD=2.0; +static const double UNDEFINED_TRANSFER_RATE=0.0; +/// Interval at which to update aspects of the system +/// 1. send acks +/// 2. update time interval between outgoing packets +/// 3, Yodate retransmit timeout +#if CC_TIME_TYPE_BYTES==4 +static const CCTimeType SYN=10; +#else +static const CCTimeType SYN=10000; +#endif + +#if CC_TIME_TYPE_BYTES==4 +#define MAX_RTT 1000 +#define RTT_TOLERANCE 30 +#else +#define MAX_RTT 1000000 +#define RTT_TOLERANCE 30000 +#endif + + +double RTTVarMultiple=4.0; + + +// ****************************************************** PUBLIC METHODS ****************************************************** + +CCRakNetUDT::CCRakNetUDT() +{ +} + +// ---------------------------------------------------------------------------------------------------------------------------- + +CCRakNetUDT::~CCRakNetUDT() +{ +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::Init(CCTimeType curTime, uint32_t maxDatagramPayload) +{ + (void) curTime; + + nextSYNUpdate=0; + packetPairRecieptHistoryWriteIndex=0; + packetArrivalHistoryWriteIndex=0; + packetArrivalHistoryWriteCount=0; + RTT=UNSET_TIME_US; + // RTTVar=UNSET_TIME_US; + isInSlowStart=true; + NAKCount=1000; + AvgNAKNum=1; + DecInterval=1; + DecCount=0; + nextDatagramSequenceNumber=0; + lastPacketPairPacketArrivalTime=0; + lastPacketPairSequenceNumber=(DatagramSequenceNumberType)(const uint32_t)-1; + lastPacketArrivalTime=0; + CWND=CWND_MIN_THRESHOLD; + lastUpdateWindowSizeAndAck=0; + lastTransmitOfBAndAS=0; + ExpCount=1.0; + totalUserDataBytesSent=0; + oldestUnsentAck=0; + MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload; + CWND_MAX_THRESHOLD=RESEND_BUFFER_ARRAY_LENGTH; +#if CC_TIME_TYPE_BYTES==4 + const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) 3.6; +#else + const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) .0036; +#endif + +#if CC_TIME_TYPE_BYTES==4 + lastRttOnIncreaseSendRate=1000; +#else + lastRttOnIncreaseSendRate=1000000; +#endif + nextCongestionControlBlock=0; + lastRtt=0; + + // B=DEFAULT_TRANSFER_RATE; + AS=UNDEFINED_TRANSFER_RATE; + const MicrosecondsPerByte DEFAULT_BYTE_INTERVAL=(MicrosecondsPerByte) (1.0/DEFAULT_TRANSFER_RATE); + SND=DEFAULT_BYTE_INTERVAL; + expectedNextSequenceNumber=0; + sendBAndASCount=0; + packetArrivalHistoryContinuousGapsIndex=0; + //packetPairRecipetHistoryGapsIndex=0; + hasWrittenToPacketPairReceiptHistory=false; + InitPacketArrivalHistory(); + + estimatedLinkCapacityBytesPerSecond=0; + bytesCanSendThisTick=0; + hadPacketlossThisBlock=false; + pingsLastInterval.Clear(__FILE__,__LINE__); +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::SetMTU(uint32_t bytes) +{ + MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes; +} +// ---------------------------------------------------------------------------------------------------------------------------- +uint32_t CCRakNetUDT::GetMTU(void) const +{ + return MAXIMUM_MTU_INCLUDING_UDP_HEADER; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::Update(CCTimeType curTime, bool hasDataToSendOrResend) +{ + (void) hasDataToSendOrResend; + (void) curTime; + + return; + + // I suspect this is causing major lag + + /* + if (hasDataToSendOrResend==false) + halveSNDOnNoDataTime=0; + else if (halveSNDOnNoDataTime==0) + { + UpdateHalveSNDOnNoDataTime(curTime); + ExpCount=1.0; + } + + // If you send, and get no data at all from that time to RTO, then halve send rate7 + if (HasHalveSNDOnNoDataTimeElapsed(curTime)) + { + /// 2000 bytes per second + /// 0.0005 seconds per byte + /// 0.5 milliseconds per byte + /// 500 microseconds per byte + // printf("No incoming data, halving send rate\n"); + SND*=2.0; + CapMinSnd(_FILE_AND_LINE_); + ExpCount+=1.0; + if (ExpCount>8.0) + ExpCount=8.0; + + UpdateHalveSNDOnNoDataTime(curTime); + } + */ +} +// ---------------------------------------------------------------------------------------------------------------------------- +int CCRakNetUDT::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) +{ + (void) curTime; + + if (isInSlowStart) + { + uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER); + return CWNDLimit; + } + return GetTransmissionBandwidth(curTime,timeSinceLastTick,unacknowledgedBytes,isContinuousSend); +} +// ---------------------------------------------------------------------------------------------------------------------------- +int CCRakNetUDT::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) +{ + (void) curTime; + + if (isInSlowStart) + { + uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER-unacknowledgedBytes); + return CWNDLimit; + } + if (bytesCanSendThisTick>0) + bytesCanSendThisTick=0; + +#if CC_TIME_TYPE_BYTES==4 + if (isContinuousSend==false && timeSinceLastTick>100) + timeSinceLastTick=100; +#else + if (isContinuousSend==false && timeSinceLastTick>100000) + timeSinceLastTick=100000; +#endif + + bytesCanSendThisTick=(int)((double)timeSinceLastTick*((double)1.0/SND)+(double)bytesCanSendThisTick); + if (bytesCanSendThisTick>0) + return bytesCanSendThisTick; + return 0; +} +uint64_t CCRakNetUDT::GetBytesPerSecondLimitByCongestionControl(void) const +{ + if (isInSlowStart) + return 0; +#if CC_TIME_TYPE_BYTES==4 + return (uint64_t) ((double)1.0/(SND*1000.0)); +#else + return (uint64_t) ((double)1.0/(SND*1000000.0)); +#endif +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetUDT::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick) +{ + CCTimeType rto = GetSenderRTOForACK(); + + // iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 + if (rto==(CCTimeType) UNSET_TIME_US) + { + // Unknown how long until the remote system will retransmit, so better send right away + return true; + } + + + // CCTimeType remoteRetransmitTime=oldestUnsentAck+rto-RTT*.5; + // CCTimeType ackArrivalTimeIfWeDelay=RTT*.5+estimatedTimeToNextTick+curTime; + // return ackArrivalTimeIfWeDelay= oldestUnsentAck + SYN || + estimatedTimeToNextTick+curTime < oldestUnsentAck+rto-RTT; +} +// ---------------------------------------------------------------------------------------------------------------------------- +DatagramSequenceNumberType CCRakNetUDT::GetNextDatagramSequenceNumber(void) +{ + return nextDatagramSequenceNumber; +} +// ---------------------------------------------------------------------------------------------------------------------------- +DatagramSequenceNumberType CCRakNetUDT::GetAndIncrementNextDatagramSequenceNumber(void) +{ + DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber; + nextDatagramSequenceNumber++; + return dsnt; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnSendBytes(CCTimeType curTime, uint32_t numBytes) +{ + (void) curTime; + + totalUserDataBytesSent+=numBytes; + if (isInSlowStart==false) + bytesCanSendThisTick-=numBytes; +} + +// ****************************************************** PROTECTED METHODS ****************************************************** + +void CCRakNetUDT::SetNextSYNUpdate(CCTimeType currentTime) +{ + nextSYNUpdate+=SYN; + if (nextSYNUpdate < currentTime) + nextSYNUpdate=currentTime+SYN; +} +// ---------------------------------------------------------------------------------------------------------------------------- +BytesPerMicrosecond CCRakNetUDT::ReceiverCalculateDataArrivalRate(CCTimeType curTime) const +{ + (void) curTime; + // Not an instantaneous measurement + /* + if (continuousBytesReceivedStartTime!=0 && curTime>continuousBytesReceivedStartTime) + { + #if CC_TIME_TYPE_BYTES==4 + const CCTimeType threshold=100; + #else + const CCTimeType threshold=100000; + #endif + if (curTime-continuousBytesReceivedStartTime>threshold) + return (BytesPerMicrosecond) continuousBytesReceived/(BytesPerMicrosecond) (curTime-continuousBytesReceivedStartTime); + } + + return UNDEFINED_TRANSFER_RATE; + */ + + + if (packetArrivalHistoryWriteCount=oneEighthMedian && + packetArrivalHistory[i] b? + const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2); + return b!=a && b-a>halfSpan; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetUDT::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) +{ + // a < b? + const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2; + return b!=a && b-amaxThreshold) + return maxThreshold; + return ret; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnResend(CCTimeType curTime) +{ + (void) curTime; + + if (isInSlowStart) + { + if (AS!=UNDEFINED_TRANSFER_RATE) + EndSlowStart(); + return; + } + + if (hadPacketlossThisBlock==false) + { + // Logging + // printf("Sending SLOWER due to Resend, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt ); + + IncreaseTimeBetweenSends(); + hadPacketlossThisBlock=true; + } +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber) +{ + (void) nakSequenceNumber; + (void) curTime; + + if (isInSlowStart) + { + if (AS!=UNDEFINED_TRANSFER_RATE) + EndSlowStart(); + return; + } + + if (hadPacketlossThisBlock==false) + { + // Logging + //printf("Sending SLOWER due to NAK, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt ); + if (pingsLastInterval.Size()>10) + { + for (int i=0; i < 10; i++) + printf("%i, ", pingsLastInterval[pingsLastInterval.Size()-1-i]/1000); + } + printf("\n"); + IncreaseTimeBetweenSends(); + + hadPacketlossThisBlock=true; + } +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::EndSlowStart(void) +{ + RakAssert(isInSlowStart==true); + RakAssert(AS!=UNDEFINED_TRANSFER_RATE); + + // This overestimates + estimatedLinkCapacityBytesPerSecond=AS * 1000000.0; + + isInSlowStart=false; + SND=1.0/AS; + CapMinSnd(_FILE_AND_LINE_); + + // printf("ENDING SLOW START\n"); +#if CC_TIME_TYPE_BYTES==4 + // printf("Initial SND=%f Kilobytes per second\n", 1.0/SND); +#else + // printf("Initial SND=%f Megabytes per second\n", 1.0/SND); +#endif + if (SND > .1) + PrintLowBandwidthWarning(); +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime) +{ + (void) datagramSequenceNumber; + (void) sizeInBytes; + (void) curTime; + +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetUDT::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount) +{ + CC_DEBUG_PRINTF_2("R%i ",datagramSequenceNumber.val); + + if (datagramSequenceNumber==expectedNextSequenceNumber) + { + *skippedMessageCount=0; + expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; + } + else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber)) + { + *skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber; + // Sanity check, just use timeout resend if this was really valid + if (*skippedMessageCount>1000) + { + // During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice + if (*skippedMessageCount>(uint32_t)50000) + return false; + *skippedMessageCount=1000; + } + expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; + } + else + { + *skippedMessageCount=0; + } + + if (curTime>lastPacketArrivalTime) + { + CCTimeType interval = curTime-lastPacketArrivalTime; + + // printf("Packet arrival gap is %I64u\n", (interval)); + + if (isContinuousSend) + { + continuousBytesReceived+=sizeInBytes; + if (continuousBytesReceivedStartTime==0) + continuousBytesReceivedStartTime=lastPacketArrivalTime; + + + mostRecentPacketArrivalHistory=(BytesPerMicrosecond)sizeInBytes/(BytesPerMicrosecond)interval; + + // if (mostRecentPacketArrivalHistory < (BytesPerMicrosecond)0.0035) + // { + // printf("%s:%i LIKELY BUG: Calculated packetArrivalHistory is below 28.8 Kbps modem\nReport to rakkar@jenkinssoftware.com with file and line number\n", _FILE_AND_LINE_); + // } + + packetArrivalHistoryContinuousGaps[packetArrivalHistoryContinuousGapsIndex++]=(int) interval; + packetArrivalHistoryContinuousGapsIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1); + + packetArrivalHistoryWriteCount++; + packetArrivalHistory[packetArrivalHistoryWriteIndex++]=mostRecentPacketArrivalHistory; + // Wrap to 0 at the end of the range + // Assumes power of 2 for CC_RAKNET_UDT_PACKET_HISTORY_LENGTH + packetArrivalHistoryWriteIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1); + } + else + { + continuousBytesReceivedStartTime=0; + continuousBytesReceived=0; + } + + lastPacketArrivalTime=curTime; + } + return true; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ) +{ +#if CC_TIME_TYPE_BYTES==4 + RakAssert(rtt < 10000); +#else + RakAssert(rtt < 10000000); +#endif + (void) _B; + + if (hasBAndAS) + { + /// RakAssert(_B!=UNDEFINED_TRANSFER_RATE && _AS!=UNDEFINED_TRANSFER_RATE); + // B=B * .875 + _B * .125; + // AS is packet arrival rate + RakAssert(_AS!=UNDEFINED_TRANSFER_RATE); + AS=_AS; + CC_DEBUG_PRINTF_4("ArrivalRate=%f linkCap=%f incomingLinkCap=%f\n", _AS,B,_B); + } + + if (oldestUnsentAck==0) + oldestUnsentAck=curTime; + + if (isInSlowStart==true) + { + nextCongestionControlBlock=nextDatagramSequenceNumber; + lastRttOnIncreaseSendRate=rtt; + UpdateWindowSizeAndAckOnAckPreSlowStart(totalUserDataBytesAcked); + } + else + { + UpdateWindowSizeAndAckOnAckPerSyn(curTime, rtt, isContinuousSend, sequenceNumber); + } + + lastUpdateWindowSizeAndAck=curTime; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS) +{ + if (curTime>lastTransmitOfBAndAS+SYN) + { + *_B=0; + *_AS=ReceiverCalculateDataArrivalRate(curTime); + + if (*_AS==UNDEFINED_TRANSFER_RATE) + { + *hasBAndAS=false; + } + else + { + *hasBAndAS=true; + } + } + else + { + *hasBAndAS=false; + } +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnSendAck(CCTimeType curTime, uint32_t numBytes) +{ + (void) numBytes; + (void) curTime; + + // This is not accounted for on the remote system, and thus causes bandwidth to be underutilized + //UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE); + + oldestUnsentAck=0; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnSendNACK(CCTimeType curTime, uint32_t numBytes) +{ + (void) numBytes; + (void) curTime; + + // This is not accounted for on the remote system, and thus causes bandwidth to be underutilized + // UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE); +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked) +{ + // During slow start, max window size is the number of full packets that have been sent out + // CWND=(double) ((double)totalUserDataBytesSent/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER); + CC_DEBUG_PRINTF_3("CWND increasing from %f to %f\n", CWND, (double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER)); + CWND=(double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER); + if (CWND>=CWND_MAX_THRESHOLD) + { + CWND=CWND_MAX_THRESHOLD; + + if (AS!=UNDEFINED_TRANSFER_RATE) + EndSlowStart(); + } + if (CWNDintervalSize) + pingsLastInterval.Pop(); + if (GreaterThan(sequenceNumber, nextCongestionControlBlock) && + sequenceNumber-nextCongestionControlBlock>=intervalSize && + pingsLastInterval.Size()==intervalSize) + { + double slopeSum=0.0; + double average=(double) pingsLastInterval[0]; + int sampleSize=pingsLastInterval.Size(); + for (int i=1; i < sampleSize; i++) + { + slopeSum+=(double)pingsLastInterval[i]-(double)pingsLastInterval[i-1]; + average+=pingsLastInterval[i]; + } + average/=sampleSize; + + if (hadPacketlossThisBlock==true) + { + } + else if (slopeSum < -.10*average) + { + // Logging + //printf("Ping dropping. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); + } + else if (slopeSum > .10*average) + { + // Logging + //printf("Ping rising. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); + IncreaseTimeBetweenSends(); + } + else + { + // Logging + //printf("Ping stable. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); + + // No packetloss over time threshhold, and rtt decreased, so send faster + lastRttOnIncreaseSendRate=rtt; + DecreaseTimeBetweenSends(); + } + + pingsLastInterval.Clear(__FILE__,__LINE__); + hadPacketlossThisBlock=false; + nextCongestionControlBlock=nextDatagramSequenceNumber; + } + + lastRtt=rtt; +} + +// ---------------------------------------------------------------------------------------------------------------------------- +double CCRakNetUDT::BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in) +{ +#if CC_TIME_TYPE_BYTES==4 + const BytesPerMicrosecond factor = 1.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER; +#else + const BytesPerMicrosecond factor = 1000.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER; +#endif + return in * factor; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::InitPacketArrivalHistory(void) +{ + unsigned int i; + for (i=0; i < CC_RAKNET_UDT_PACKET_HISTORY_LENGTH; i++) + { + packetArrivalHistory[i]=UNDEFINED_TRANSFER_RATE; + packetArrivalHistoryContinuousGaps[i]=0; + } + + packetArrivalHistoryWriteCount=0; + continuousBytesReceived=0; + continuousBytesReceivedStartTime=0; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::PrintLowBandwidthWarning(void) +{ + + /* + printf("\n-------LOW BANDWIDTH -----\n"); + if (isInSlowStart==false) + printf("SND=%f Megabytes per second\n", 1.0/SND); + printf("Window size=%f\n", CWND); + printf("Pipe from packet pair = %f megabytes per second\n", B); + printf("RTT=%f milliseconds\n", RTT/1000.0); + printf("RTT Variance=%f milliseconds\n", RTTVar/1000.0); + printf("Retransmission=%i milliseconds\n", GetRTOForRetransmission()/1000); + printf("Packet arrival rate on the remote system=%f megabytes per second\n", AS); + printf("Packet arrival rate on our system=%f megabytes per second\n", ReceiverCalculateDataArrivalRate()); + printf("isInSlowStart=%i\n", isInSlowStart); + printf("---------------\n"); + */ +} +BytesPerMicrosecond CCRakNetUDT::GetLocalReceiveRate(CCTimeType currentTime) const +{ + return ReceiverCalculateDataArrivalRate(currentTime); +} +double CCRakNetUDT::GetRTT(void) const +{ + if (RTT==UNSET_TIME_US) + return 0.0; + return RTT; +} +void CCRakNetUDT::CapMinSnd(const char *file, int line) +{ + (void) file; + (void) line; + + if (SND > 500) + { + SND=500; + CC_DEBUG_PRINTF_3("%s:%i LIKELY BUG: SND has gotten above 500 microseconds between messages (28.8 modem)\nReport to rakkar@jenkinssoftware.com with file and line number\n", file, line); + } +} +void CCRakNetUDT::IncreaseTimeBetweenSends(void) +{ + // In order to converge, bigger numbers have to increase slower and decrease faster + // SND==500 then increment is .02 + // SND==0 then increment is near 0 + // (SND+1.0) brings it to the range of 1 to 501 + // Square the number, which is the range of 1 to 251001 + // Divide by 251001, which is the range of 1/251001 to 1 + + double increment; + increment = .02 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ; + // SND=500 then increment=.02 + // SND=0 then increment=near 0 + SND*=(1.02 - increment); + + // SND=0 then fast increase, slow decrease + // SND=500 then slow increase, fast decrease + CapMinSnd(__FILE__,__LINE__); +} +void CCRakNetUDT::DecreaseTimeBetweenSends(void) +{ + double increment; + increment = .01 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ; + // SND=500 then increment=.01 + // SND=0 then increment=near 0 + SND*=(.99 - increment); +} +/* +void CCRakNetUDT::SetTimeBetweenSendsLimit(unsigned int bitsPerSecond) +{ +// bitsPerSecond / 1000000 = bitsPerMicrosecond +// bitsPerMicrosecond / 8 = BytesPerMicrosecond +// 1 / BytesPerMicrosecond = MicrosecondsPerByte +// 1 / ( (bitsPerSecond / 1000000) / 8 ) = +// 1 / (bitsPerSecond / 8000000) = +// 8000000 / bitsPerSecond + +#if CC_TIME_TYPE_BYTES==4 + MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000 / (MicrosecondsPerByte)bitsPerSecond; +#else + MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000000 / (MicrosecondsPerByte)bitsPerSecond; +#endif + if (limit > SND) + SND=limit; +} +*/ + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/CCRakNetUDT.h b/project/lib_projects/raknet/jni/RaknetSources/CCRakNetUDT.h index ca8a1c2..3400dde 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CCRakNetUDT.h +++ b/project/lib_projects/raknet/jni/RaknetSources/CCRakNetUDT.h @@ -1,394 +1,394 @@ -#include "RakNetDefines.h" - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 - -#ifndef __CONGESTION_CONTROL_UDT_H -#define __CONGESTION_CONTROL_UDT_H - -#include "NativeTypes.h" -#include "RakNetTime.h" -#include "RakNetTypes.h" -#include "DS_Queue.h" - -/// Set to 4 if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 -#define CC_TIME_TYPE_BYTES 8 - -namespace RakNet -{ - - -typedef uint64_t CCTimeType; - - - - -typedef uint24_t DatagramSequenceNumberType; -typedef double BytesPerMicrosecond; -typedef double BytesPerSecond; -typedef double MicrosecondsPerByte; - -/// CC_RAKNET_UDT_PACKET_HISTORY_LENGTH should be a power of 2 for the writeIndex variables to wrap properly -#define CC_RAKNET_UDT_PACKET_HISTORY_LENGTH 64 -#define RTT_HISTORY_LENGTH 64 - -/// Sizeof an UDP header in byte -#define UDP_HEADER_SIZE 28 - -#define CC_DEBUG_PRINTF_1(x) -#define CC_DEBUG_PRINTF_2(x,y) -#define CC_DEBUG_PRINTF_3(x,y,z) -#define CC_DEBUG_PRINTF_4(x,y,z,a) -#define CC_DEBUG_PRINTF_5(x,y,z,a,b) -//#define CC_DEBUG_PRINTF_1(x) printf(x) -//#define CC_DEBUG_PRINTF_2(x,y) printf(x,y) -//#define CC_DEBUG_PRINTF_3(x,y,z) printf(x,y,z) -//#define CC_DEBUG_PRINTF_4(x,y,z,a) printf(x,y,z,a) -//#define CC_DEBUG_PRINTF_5(x,y,z,a,b) printf(x,y,z,a,b) - -/// \brief Encapsulates UDT congestion control, as used by RakNet -/// Requirements: -///
    -///
  1. Each datagram is no more than MAXIMUM_MTU_SIZE, after accounting for the UDP header -///
  2. Each datagram containing a user message has a sequence number which is set after calling OnSendBytes(). Set it by calling GetAndIncrementNextDatagramSequenceNumber() -///
  3. System is designed to be used from a single thread. -///
  4. Each packet should have a timeout time based on GetSenderRTOForACK(). If this time elapses, add the packet to the head of the send list for retransmission. -///
-/// -/// Recommended: -///
    -///
  1. Call sendto in its own thread. This takes a significant amount of time in high speed networks. -///
-/// -/// Algorithm: -///
    -///
  1. On a new connection, call Init() -///
  2. On a periodic interval (SYN time is the best) call Update(). Also call ShouldSendACKs(), and send buffered ACKS if it returns true. -///
  3. Call OnSendAck() when sending acks. -///
  4. When you want to send or resend data, call GetNumberOfBytesToSend(). It will return you enough bytes to keep you busy for \a estimatedTimeToNextTick. You can send more than this to fill out a datagram, or to send packet pairs -///
  5. Call OnSendBytes() when sending datagrams. -///
  6. When data arrives, record the sequence number and buffer an ACK for it, to be sent from Update() if ShouldSendACKs() returns true -///
  7. Every 16 packets that you send, send two of them back to back (a packet pair) as long as both packets are the same size. If you don't have two packets the same size, it is fine to defer this until you do. -///
  8. When you get a packet, call OnGotPacket(). If the packet is also either of a packet pair, call OnGotPacketPair() -///
  9. If you get a packet, and the sequence number is not 1 + the last sequence number, send a NAK. On the remote system, call OnNAK() and resend that message. -///
  10. If you get an ACK, remove that message from retransmission. Call OnNonDuplicateAck(). -///
  11. If a message is not ACKed for GetRTOForRetransmission(), resend it. -///
-class CCRakNetUDT -{ - public: - - CCRakNetUDT(); - ~CCRakNetUDT(); - - /// Reset all variables to their initial states, for a new connection - void Init(CCTimeType curTime, uint32_t maxDatagramPayload); - - /// Update over time - void Update(CCTimeType curTime, bool hasDataToSendOrResend); - - int GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); - int GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); - - /// Acks do not have to be sent immediately. Instead, they can be buffered up such that groups of acks are sent at a time - /// This reduces overall bandwidth usage - /// How long they can be buffered depends on the retransmit time of the sender - /// Should call once per update tick, and send if needed - bool ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick); - - /// Every data packet sent must contain a sequence number - /// Call this function to get it. The sequence number is passed into OnGotPacketPair() - DatagramSequenceNumberType GetAndIncrementNextDatagramSequenceNumber(void); - DatagramSequenceNumberType GetNextDatagramSequenceNumber(void); - - /// Call this when you send packets - /// Every 15th and 16th packets should be sent as a packet pair if possible - /// When packets marked as a packet pair arrive, pass to OnGotPacketPair() - /// When any packets arrive, (additionally) pass to OnGotPacket - /// Packets should contain our system time, so we can pass rtt to OnNonDuplicateAck() - void OnSendBytes(CCTimeType curTime, uint32_t numBytes); - - /// Call this when you get a packet pair - void OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime); - - /// Call this when you get a packet (including packet pairs) - /// If the DatagramSequenceNumberType is out of order, skippedMessageCount will be non-zero - /// In that case, send a NAK for every sequence number up to that count - bool OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount); - - /// Call when you get a NAK, with the sequence number of the lost message - /// Affects the congestion control - void OnResend(CCTimeType curTime); - void OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber); - - /// Call this when an ACK arrives. - /// hasBAndAS are possibly written with the ack, see OnSendAck() - /// B and AS are used in the calculations in UpdateWindowSizeAndAckOnAckPerSyn - /// B and AS are updated at most once per SYN - void OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ); - void OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ) {} - - /// Call when you send an ack, to see if the ack should have the B and AS parameters transmitted - /// Call before calling OnSendAck() - void OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS); - - /// Call when we send an ack, to write B and AS if needed - /// B and AS are only written once per SYN, to prevent slow calculations - /// Also updates SND, the period between sends, since data is written out - /// Be sure to call OnSendAckGetBAndAS() before calling OnSendAck(), since whether you write it or not affects \a numBytes - void OnSendAck(CCTimeType curTime, uint32_t numBytes); - - /// Call when we send a NACK - /// Also updates SND, the period between sends, since data is written out - void OnSendNACK(CCTimeType curTime, uint32_t numBytes); - - /// Retransmission time out for the sender - /// If the time difference between when a message was last transmitted, and the current time is greater than RTO then packet is eligible for retransmission, pending congestion control - /// RTO = (RTT + 4 * RTTVar) + SYN - /// If we have been continuously sending for the last RTO, and no ACK or NAK at all, SND*=2; - /// This is per message, which is different from UDT, but RakNet supports packetloss with continuing data where UDT is only RELIABLE_ORDERED - /// Minimum value is 100 milliseconds - CCTimeType GetRTOForRetransmission(void) const; - - /// Set the maximum amount of data that can be sent in one datagram - /// Default to MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE - void SetMTU(uint32_t bytes); - - /// Return what was set by SetMTU() - uint32_t GetMTU(void) const; - - /// Query for statistics - BytesPerMicrosecond GetLocalSendRate(void) const {return 1.0 / SND;} - BytesPerMicrosecond GetLocalReceiveRate(CCTimeType currentTime) const; - BytesPerMicrosecond GetRemoveReceiveRate(void) const {return AS;} - //BytesPerMicrosecond GetEstimatedBandwidth(void) const {return B;} - BytesPerMicrosecond GetEstimatedBandwidth(void) const {return GetLinkCapacityBytesPerSecond()*1000000.0;} - double GetLinkCapacityBytesPerSecond(void) const {return estimatedLinkCapacityBytesPerSecond;}; - - /// Query for statistics - double GetRTT(void) const; - - bool GetIsInSlowStart(void) const {return isInSlowStart;} - uint32_t GetCWNDLimit(void) const {return (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER);} - - - /// Is a > b, accounting for variable overflow? - static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); - /// Is a < b, accounting for variable overflow? - static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); -// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond); - uint64_t GetBytesPerSecondLimitByCongestionControl(void) const; - - protected: - // --------------------------- PROTECTED VARIABLES --------------------------- - /// time interval between bytes, in microseconds. - /// Only used when slowStart==false - /// Increased over time as we continually get messages - /// Decreased on NAK and timeout - /// Starts at 0 (invalid) - MicrosecondsPerByte SND; - - /// Supportive window mechanism, controlling the maximum number of in-flight packets - /// Used both during and after slow-start, but primarily during slow-start - /// Starts at 2, which is also the low threshhold - /// Max is the socket receive buffer / MTU - /// CWND = AS * (RTT + SYN) + 16 - double CWND; - - /// When we do an update process on the SYN interval, nextSYNUpdate is set to the next time we should update - /// Normally this is nextSYNUpdate+=SYN, in order to update on a consistent schedule - /// However, if this would result in an immediate update yet again, it is set to SYN microseconds past the current time (in case the thread did not update for a long time) - CCTimeType nextSYNUpdate; - - - /// Index into packetPairRecieptHistory where we will next write - /// The history is always full (starting with default values) so no read index is needed - int packetPairRecieptHistoryWriteIndex; - - /// Sent to the sender by the receiver from packetPairRecieptHistory whenever a back to back packet arrives on the receiver - /// Updated by B = B * .875 + incomingB * .125 - //BytesPerMicrosecond B; - - /// Running round trip time (ping*2) - /// Only sender needs to know this - /// Initialized to UNSET - /// Set to rtt on first calculation - /// Updated gradually by RTT = RTT * 0.875 + rtt * 0.125 - double RTT; - - /// Round trip time variance - /// Only sender needs to know this - /// Initialized to UNSET - /// Set to rtt on first calculation - // double RTTVar; - /// Update: Use min/max, RTTVar follows current variance too closely resulting in packetloss - double minRTT, maxRTT; - - /// Used to calculate packet arrival rate (in UDT) but data arrival rate (in RakNet, where not all datagrams are the same size) - /// Filter is used to cull lowest half of values for bytesPerMicrosecond, to discount spikes and inactivity - /// Referred to in the documentation as AS, data arrival rate - /// AS is sent to the sender and calculated every 10th ack - /// Each node represents (curTime-lastPacketArrivalTime)/bytes - /// Used with ReceiverCalculateDataArrivalRate(); - BytesPerMicrosecond packetArrivalHistory[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH]; - BytesPerMicrosecond packetArrivalHistoryContinuousGaps[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH]; - unsigned char packetArrivalHistoryContinuousGapsIndex; - uint64_t continuousBytesReceived; - CCTimeType continuousBytesReceivedStartTime; - unsigned int packetArrivalHistoryWriteCount; - - /// Index into packetArrivalHistory where we will next write - /// The history is always full (starting with default values) so no read index is needed - int packetArrivalHistoryWriteIndex; - - /// Tracks the time the last packet that arrived, so BytesPerMicrosecond can be calculated for packetArrivalHistory when a new packet arrives - CCTimeType lastPacketArrivalTime; - - /// Data arrival rate from the sender to the receiver, as told to us by the receiver - /// Used to calculate initial sending rate when slow start stops - BytesPerMicrosecond AS; - - /// When the receiver last calculated and send B and AS, from packetArrivalHistory and packetPairRecieptHistory - /// Used to prevent it from being calculated and send too frequently, as they are slow operations - CCTimeType lastTransmitOfBAndAS; - - /// New connections start in slow start - /// During slow start, SND is not used, only CWND - /// Slow start ends when we get a NAK, or the maximum size of CWND is reached - /// SND is initialized to the inverse of the receiver's packet arrival rate when slow start ends - bool isInSlowStart; - - /// How many NAKs arrived this congestion period - /// Initialized to 1 when the congestion period starts - uint32_t NAKCount; - - /// How many NAKs do you get on average during a congestion period? - /// Starts at 1 - /// Used to generate a random number, DecRandom, between 1 and AvgNAKNum - uint32_t AvgNAKNum; - - /// How many times we have decremented SND this congestion period. Used to limit the number of decrements to 5 - uint32_t DecCount; - - /// Every DecInterval NAKs per congestion period, we decrease the send rate - uint32_t DecInterval; - - /// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment - DatagramSequenceNumberType nextDatagramSequenceNumber; - - /// If a packet is marked as a packet pair, lastPacketPairPacketArrivalTime is set to the time it arrives - /// This is used so when the 2nd packet of the pair arrives, we can calculate the time interval between the two - CCTimeType lastPacketPairPacketArrivalTime; - - /// If a packet is marked as a packet pair, lastPacketPairSequenceNumber is checked to see if the last packet we got - /// was the packet immediately before the one that arrived - /// If so, we can use lastPacketPairPacketArrivalTime to get the time between the two packets, and thus estimate the link capacity - /// Initialized to -1, so the first packet of a packet pair won't be treated as the second - DatagramSequenceNumberType lastPacketPairSequenceNumber; - - /// Used to cap UpdateWindowSizeAndAckOnAckPerSyn() to once speed increase per SYN - /// This is to prevent speeding up faster than congestion control can compensate for - CCTimeType lastUpdateWindowSizeAndAck; - - /// Every time SND is halved due to timeout, the RTO is increased - /// This is to prevent massive retransmissions to an unresponsive system - /// Reset on any data arriving - double ExpCount; - - /// Total number of user data bytes sent - /// Used to adjust the window size, on ACK, during slow start - uint64_t totalUserDataBytesSent; - - /// When we get an ack, if oldestUnsentAck==0, set it to the current time - /// When we send out acks, set oldestUnsentAck to 0 - CCTimeType oldestUnsentAck; - - // Maximum amount of bytes that the user can send, e.g. the size of one full datagram - uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER; - - // Max window size - double CWND_MAX_THRESHOLD; - - /// Track which datagram sequence numbers have arrived. - /// If a sequence number is skipped, send a NAK for all skipped messages - DatagramSequenceNumberType expectedNextSequenceNumber; - - // How many times have we sent B and AS? Used to force it to send at least CC_RAKNET_UDT_PACKET_HISTORY_LENGTH times - // Otherwise, the default values in the array generate inaccuracy - uint32_t sendBAndASCount; - - /// Most recent values read into the corresponding lists - /// Used during the beginning of a connection, when the median filter is still inaccurate - BytesPerMicrosecond mostRecentPacketArrivalHistory; - - bool hasWrittenToPacketPairReceiptHistory; - -// uint32_t rttHistory[RTT_HISTORY_LENGTH]; -// uint32_t rttHistoryIndex; -// uint32_t rttHistoryWriteCount; -// uint32_t rttSum, rttLow; -// CCTimeType lastSndUpdateTime; - double estimatedLinkCapacityBytesPerSecond; - - // --------------------------- PROTECTED METHODS --------------------------- - /// Update nextSYNUpdate by SYN, or the same amount past the current time if no updates have occurred for a long time - void SetNextSYNUpdate(CCTimeType currentTime); - - /// Returns the rate of data arrival, based on packets arriving on the sender. - BytesPerMicrosecond ReceiverCalculateDataArrivalRate(CCTimeType curTime) const; - /// Returns the median of the data arrival rate - BytesPerMicrosecond ReceiverCalculateDataArrivalRateMedian(void) const; - - /// Calculates the median an array of BytesPerMicrosecond - static BytesPerMicrosecond CalculateListMedianRecursive(const BytesPerMicrosecond inputList[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum); -// static uint32_t CalculateListMedianRecursive(const uint32_t inputList[RTT_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum); - - /// Same as GetRTOForRetransmission, but does not factor in ExpCount - /// This is because the receiver does not know ExpCount for the sender, and even if it did, acks shouldn't be delayed for this reason - CCTimeType GetSenderRTOForACK(void) const; - - /// Stop slow start, and enter normal transfer rate - void EndSlowStart(void); - - /// Does the named conversion - inline double BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in); - - /// Update the round trip time, from ACK or ACK2 - //void UpdateRTT(CCTimeType rtt); - - /// Update the corresponding variables pre-slow start - void UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked); - - /// Update the corresponding variables post-slow start - void UpdateWindowSizeAndAckOnAckPerSyn(CCTimeType curTime, CCTimeType rtt, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber); - - - /// Sets halveSNDOnNoDataTime to the future, and also resets ExpCount, which is used to multiple the RTO on no data arriving at all - void ResetOnDataArrivalHalveSNDOnNoDataTime(CCTimeType curTime); - - // Init array - void InitPacketArrivalHistory(void); - - // Printf - void PrintLowBandwidthWarning(void); - - // Bug: SND can sometimes get super high - have seen 11693 - void CapMinSnd(const char *file, int line); - - void DecreaseTimeBetweenSends(void); - void IncreaseTimeBetweenSends(void); - - int bytesCanSendThisTick; - - CCTimeType lastRttOnIncreaseSendRate; - CCTimeType lastRtt; - - DatagramSequenceNumberType nextCongestionControlBlock; - bool hadPacketlossThisBlock; - DataStructures::Queue pingsLastInterval; -}; - -} - -#endif - -#endif +#include "RakNetDefines.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 + +#ifndef __CONGESTION_CONTROL_UDT_H +#define __CONGESTION_CONTROL_UDT_H + +#include "NativeTypes.h" +#include "RakNetTime.h" +#include "RakNetTypes.h" +#include "DS_Queue.h" + +/// Set to 4 if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 +#define CC_TIME_TYPE_BYTES 8 + +namespace RakNet +{ + + +typedef uint64_t CCTimeType; + + + + +typedef uint24_t DatagramSequenceNumberType; +typedef double BytesPerMicrosecond; +typedef double BytesPerSecond; +typedef double MicrosecondsPerByte; + +/// CC_RAKNET_UDT_PACKET_HISTORY_LENGTH should be a power of 2 for the writeIndex variables to wrap properly +#define CC_RAKNET_UDT_PACKET_HISTORY_LENGTH 64 +#define RTT_HISTORY_LENGTH 64 + +/// Sizeof an UDP header in byte +#define UDP_HEADER_SIZE 28 + +#define CC_DEBUG_PRINTF_1(x) +#define CC_DEBUG_PRINTF_2(x,y) +#define CC_DEBUG_PRINTF_3(x,y,z) +#define CC_DEBUG_PRINTF_4(x,y,z,a) +#define CC_DEBUG_PRINTF_5(x,y,z,a,b) +//#define CC_DEBUG_PRINTF_1(x) printf(x) +//#define CC_DEBUG_PRINTF_2(x,y) printf(x,y) +//#define CC_DEBUG_PRINTF_3(x,y,z) printf(x,y,z) +//#define CC_DEBUG_PRINTF_4(x,y,z,a) printf(x,y,z,a) +//#define CC_DEBUG_PRINTF_5(x,y,z,a,b) printf(x,y,z,a,b) + +/// \brief Encapsulates UDT congestion control, as used by RakNet +/// Requirements: +///
    +///
  1. Each datagram is no more than MAXIMUM_MTU_SIZE, after accounting for the UDP header +///
  2. Each datagram containing a user message has a sequence number which is set after calling OnSendBytes(). Set it by calling GetAndIncrementNextDatagramSequenceNumber() +///
  3. System is designed to be used from a single thread. +///
  4. Each packet should have a timeout time based on GetSenderRTOForACK(). If this time elapses, add the packet to the head of the send list for retransmission. +///
+/// +/// Recommended: +///
    +///
  1. Call sendto in its own thread. This takes a significant amount of time in high speed networks. +///
+/// +/// Algorithm: +///
    +///
  1. On a new connection, call Init() +///
  2. On a periodic interval (SYN time is the best) call Update(). Also call ShouldSendACKs(), and send buffered ACKS if it returns true. +///
  3. Call OnSendAck() when sending acks. +///
  4. When you want to send or resend data, call GetNumberOfBytesToSend(). It will return you enough bytes to keep you busy for \a estimatedTimeToNextTick. You can send more than this to fill out a datagram, or to send packet pairs +///
  5. Call OnSendBytes() when sending datagrams. +///
  6. When data arrives, record the sequence number and buffer an ACK for it, to be sent from Update() if ShouldSendACKs() returns true +///
  7. Every 16 packets that you send, send two of them back to back (a packet pair) as long as both packets are the same size. If you don't have two packets the same size, it is fine to defer this until you do. +///
  8. When you get a packet, call OnGotPacket(). If the packet is also either of a packet pair, call OnGotPacketPair() +///
  9. If you get a packet, and the sequence number is not 1 + the last sequence number, send a NAK. On the remote system, call OnNAK() and resend that message. +///
  10. If you get an ACK, remove that message from retransmission. Call OnNonDuplicateAck(). +///
  11. If a message is not ACKed for GetRTOForRetransmission(), resend it. +///
+class CCRakNetUDT +{ + public: + + CCRakNetUDT(); + ~CCRakNetUDT(); + + /// Reset all variables to their initial states, for a new connection + void Init(CCTimeType curTime, uint32_t maxDatagramPayload); + + /// Update over time + void Update(CCTimeType curTime, bool hasDataToSendOrResend); + + int GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); + int GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); + + /// Acks do not have to be sent immediately. Instead, they can be buffered up such that groups of acks are sent at a time + /// This reduces overall bandwidth usage + /// How long they can be buffered depends on the retransmit time of the sender + /// Should call once per update tick, and send if needed + bool ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick); + + /// Every data packet sent must contain a sequence number + /// Call this function to get it. The sequence number is passed into OnGotPacketPair() + DatagramSequenceNumberType GetAndIncrementNextDatagramSequenceNumber(void); + DatagramSequenceNumberType GetNextDatagramSequenceNumber(void); + + /// Call this when you send packets + /// Every 15th and 16th packets should be sent as a packet pair if possible + /// When packets marked as a packet pair arrive, pass to OnGotPacketPair() + /// When any packets arrive, (additionally) pass to OnGotPacket + /// Packets should contain our system time, so we can pass rtt to OnNonDuplicateAck() + void OnSendBytes(CCTimeType curTime, uint32_t numBytes); + + /// Call this when you get a packet pair + void OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime); + + /// Call this when you get a packet (including packet pairs) + /// If the DatagramSequenceNumberType is out of order, skippedMessageCount will be non-zero + /// In that case, send a NAK for every sequence number up to that count + bool OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount); + + /// Call when you get a NAK, with the sequence number of the lost message + /// Affects the congestion control + void OnResend(CCTimeType curTime); + void OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber); + + /// Call this when an ACK arrives. + /// hasBAndAS are possibly written with the ack, see OnSendAck() + /// B and AS are used in the calculations in UpdateWindowSizeAndAckOnAckPerSyn + /// B and AS are updated at most once per SYN + void OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ); + void OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ) {} + + /// Call when you send an ack, to see if the ack should have the B and AS parameters transmitted + /// Call before calling OnSendAck() + void OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS); + + /// Call when we send an ack, to write B and AS if needed + /// B and AS are only written once per SYN, to prevent slow calculations + /// Also updates SND, the period between sends, since data is written out + /// Be sure to call OnSendAckGetBAndAS() before calling OnSendAck(), since whether you write it or not affects \a numBytes + void OnSendAck(CCTimeType curTime, uint32_t numBytes); + + /// Call when we send a NACK + /// Also updates SND, the period between sends, since data is written out + void OnSendNACK(CCTimeType curTime, uint32_t numBytes); + + /// Retransmission time out for the sender + /// If the time difference between when a message was last transmitted, and the current time is greater than RTO then packet is eligible for retransmission, pending congestion control + /// RTO = (RTT + 4 * RTTVar) + SYN + /// If we have been continuously sending for the last RTO, and no ACK or NAK at all, SND*=2; + /// This is per message, which is different from UDT, but RakNet supports packetloss with continuing data where UDT is only RELIABLE_ORDERED + /// Minimum value is 100 milliseconds + CCTimeType GetRTOForRetransmission(void) const; + + /// Set the maximum amount of data that can be sent in one datagram + /// Default to MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE + void SetMTU(uint32_t bytes); + + /// Return what was set by SetMTU() + uint32_t GetMTU(void) const; + + /// Query for statistics + BytesPerMicrosecond GetLocalSendRate(void) const {return 1.0 / SND;} + BytesPerMicrosecond GetLocalReceiveRate(CCTimeType currentTime) const; + BytesPerMicrosecond GetRemoveReceiveRate(void) const {return AS;} + //BytesPerMicrosecond GetEstimatedBandwidth(void) const {return B;} + BytesPerMicrosecond GetEstimatedBandwidth(void) const {return GetLinkCapacityBytesPerSecond()*1000000.0;} + double GetLinkCapacityBytesPerSecond(void) const {return estimatedLinkCapacityBytesPerSecond;}; + + /// Query for statistics + double GetRTT(void) const; + + bool GetIsInSlowStart(void) const {return isInSlowStart;} + uint32_t GetCWNDLimit(void) const {return (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER);} + + + /// Is a > b, accounting for variable overflow? + static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); + /// Is a < b, accounting for variable overflow? + static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); +// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond); + uint64_t GetBytesPerSecondLimitByCongestionControl(void) const; + + protected: + // --------------------------- PROTECTED VARIABLES --------------------------- + /// time interval between bytes, in microseconds. + /// Only used when slowStart==false + /// Increased over time as we continually get messages + /// Decreased on NAK and timeout + /// Starts at 0 (invalid) + MicrosecondsPerByte SND; + + /// Supportive window mechanism, controlling the maximum number of in-flight packets + /// Used both during and after slow-start, but primarily during slow-start + /// Starts at 2, which is also the low threshhold + /// Max is the socket receive buffer / MTU + /// CWND = AS * (RTT + SYN) + 16 + double CWND; + + /// When we do an update process on the SYN interval, nextSYNUpdate is set to the next time we should update + /// Normally this is nextSYNUpdate+=SYN, in order to update on a consistent schedule + /// However, if this would result in an immediate update yet again, it is set to SYN microseconds past the current time (in case the thread did not update for a long time) + CCTimeType nextSYNUpdate; + + + /// Index into packetPairRecieptHistory where we will next write + /// The history is always full (starting with default values) so no read index is needed + int packetPairRecieptHistoryWriteIndex; + + /// Sent to the sender by the receiver from packetPairRecieptHistory whenever a back to back packet arrives on the receiver + /// Updated by B = B * .875 + incomingB * .125 + //BytesPerMicrosecond B; + + /// Running round trip time (ping*2) + /// Only sender needs to know this + /// Initialized to UNSET + /// Set to rtt on first calculation + /// Updated gradually by RTT = RTT * 0.875 + rtt * 0.125 + double RTT; + + /// Round trip time variance + /// Only sender needs to know this + /// Initialized to UNSET + /// Set to rtt on first calculation + // double RTTVar; + /// Update: Use min/max, RTTVar follows current variance too closely resulting in packetloss + double minRTT, maxRTT; + + /// Used to calculate packet arrival rate (in UDT) but data arrival rate (in RakNet, where not all datagrams are the same size) + /// Filter is used to cull lowest half of values for bytesPerMicrosecond, to discount spikes and inactivity + /// Referred to in the documentation as AS, data arrival rate + /// AS is sent to the sender and calculated every 10th ack + /// Each node represents (curTime-lastPacketArrivalTime)/bytes + /// Used with ReceiverCalculateDataArrivalRate(); + BytesPerMicrosecond packetArrivalHistory[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH]; + BytesPerMicrosecond packetArrivalHistoryContinuousGaps[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH]; + unsigned char packetArrivalHistoryContinuousGapsIndex; + uint64_t continuousBytesReceived; + CCTimeType continuousBytesReceivedStartTime; + unsigned int packetArrivalHistoryWriteCount; + + /// Index into packetArrivalHistory where we will next write + /// The history is always full (starting with default values) so no read index is needed + int packetArrivalHistoryWriteIndex; + + /// Tracks the time the last packet that arrived, so BytesPerMicrosecond can be calculated for packetArrivalHistory when a new packet arrives + CCTimeType lastPacketArrivalTime; + + /// Data arrival rate from the sender to the receiver, as told to us by the receiver + /// Used to calculate initial sending rate when slow start stops + BytesPerMicrosecond AS; + + /// When the receiver last calculated and send B and AS, from packetArrivalHistory and packetPairRecieptHistory + /// Used to prevent it from being calculated and send too frequently, as they are slow operations + CCTimeType lastTransmitOfBAndAS; + + /// New connections start in slow start + /// During slow start, SND is not used, only CWND + /// Slow start ends when we get a NAK, or the maximum size of CWND is reached + /// SND is initialized to the inverse of the receiver's packet arrival rate when slow start ends + bool isInSlowStart; + + /// How many NAKs arrived this congestion period + /// Initialized to 1 when the congestion period starts + uint32_t NAKCount; + + /// How many NAKs do you get on average during a congestion period? + /// Starts at 1 + /// Used to generate a random number, DecRandom, between 1 and AvgNAKNum + uint32_t AvgNAKNum; + + /// How many times we have decremented SND this congestion period. Used to limit the number of decrements to 5 + uint32_t DecCount; + + /// Every DecInterval NAKs per congestion period, we decrease the send rate + uint32_t DecInterval; + + /// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment + DatagramSequenceNumberType nextDatagramSequenceNumber; + + /// If a packet is marked as a packet pair, lastPacketPairPacketArrivalTime is set to the time it arrives + /// This is used so when the 2nd packet of the pair arrives, we can calculate the time interval between the two + CCTimeType lastPacketPairPacketArrivalTime; + + /// If a packet is marked as a packet pair, lastPacketPairSequenceNumber is checked to see if the last packet we got + /// was the packet immediately before the one that arrived + /// If so, we can use lastPacketPairPacketArrivalTime to get the time between the two packets, and thus estimate the link capacity + /// Initialized to -1, so the first packet of a packet pair won't be treated as the second + DatagramSequenceNumberType lastPacketPairSequenceNumber; + + /// Used to cap UpdateWindowSizeAndAckOnAckPerSyn() to once speed increase per SYN + /// This is to prevent speeding up faster than congestion control can compensate for + CCTimeType lastUpdateWindowSizeAndAck; + + /// Every time SND is halved due to timeout, the RTO is increased + /// This is to prevent massive retransmissions to an unresponsive system + /// Reset on any data arriving + double ExpCount; + + /// Total number of user data bytes sent + /// Used to adjust the window size, on ACK, during slow start + uint64_t totalUserDataBytesSent; + + /// When we get an ack, if oldestUnsentAck==0, set it to the current time + /// When we send out acks, set oldestUnsentAck to 0 + CCTimeType oldestUnsentAck; + + // Maximum amount of bytes that the user can send, e.g. the size of one full datagram + uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER; + + // Max window size + double CWND_MAX_THRESHOLD; + + /// Track which datagram sequence numbers have arrived. + /// If a sequence number is skipped, send a NAK for all skipped messages + DatagramSequenceNumberType expectedNextSequenceNumber; + + // How many times have we sent B and AS? Used to force it to send at least CC_RAKNET_UDT_PACKET_HISTORY_LENGTH times + // Otherwise, the default values in the array generate inaccuracy + uint32_t sendBAndASCount; + + /// Most recent values read into the corresponding lists + /// Used during the beginning of a connection, when the median filter is still inaccurate + BytesPerMicrosecond mostRecentPacketArrivalHistory; + + bool hasWrittenToPacketPairReceiptHistory; + +// uint32_t rttHistory[RTT_HISTORY_LENGTH]; +// uint32_t rttHistoryIndex; +// uint32_t rttHistoryWriteCount; +// uint32_t rttSum, rttLow; +// CCTimeType lastSndUpdateTime; + double estimatedLinkCapacityBytesPerSecond; + + // --------------------------- PROTECTED METHODS --------------------------- + /// Update nextSYNUpdate by SYN, or the same amount past the current time if no updates have occurred for a long time + void SetNextSYNUpdate(CCTimeType currentTime); + + /// Returns the rate of data arrival, based on packets arriving on the sender. + BytesPerMicrosecond ReceiverCalculateDataArrivalRate(CCTimeType curTime) const; + /// Returns the median of the data arrival rate + BytesPerMicrosecond ReceiverCalculateDataArrivalRateMedian(void) const; + + /// Calculates the median an array of BytesPerMicrosecond + static BytesPerMicrosecond CalculateListMedianRecursive(const BytesPerMicrosecond inputList[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum); +// static uint32_t CalculateListMedianRecursive(const uint32_t inputList[RTT_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum); + + /// Same as GetRTOForRetransmission, but does not factor in ExpCount + /// This is because the receiver does not know ExpCount for the sender, and even if it did, acks shouldn't be delayed for this reason + CCTimeType GetSenderRTOForACK(void) const; + + /// Stop slow start, and enter normal transfer rate + void EndSlowStart(void); + + /// Does the named conversion + inline double BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in); + + /// Update the round trip time, from ACK or ACK2 + //void UpdateRTT(CCTimeType rtt); + + /// Update the corresponding variables pre-slow start + void UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked); + + /// Update the corresponding variables post-slow start + void UpdateWindowSizeAndAckOnAckPerSyn(CCTimeType curTime, CCTimeType rtt, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber); + + + /// Sets halveSNDOnNoDataTime to the future, and also resets ExpCount, which is used to multiple the RTO on no data arriving at all + void ResetOnDataArrivalHalveSNDOnNoDataTime(CCTimeType curTime); + + // Init array + void InitPacketArrivalHistory(void); + + // Printf + void PrintLowBandwidthWarning(void); + + // Bug: SND can sometimes get super high - have seen 11693 + void CapMinSnd(const char *file, int line); + + void DecreaseTimeBetweenSends(void); + void IncreaseTimeBetweenSends(void); + + int bytesCanSendThisTick; + + CCTimeType lastRttOnIncreaseSendRate; + CCTimeType lastRtt; + + DatagramSequenceNumberType nextCongestionControlBlock; + bool hadPacketlossThisBlock; + DataStructures::Queue pingsLastInterval; +}; + +} + +#endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/CheckSum.cpp b/project/lib_projects/raknet/jni/RaknetSources/CheckSum.cpp index 4ba7fcd..91ecc3f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CheckSum.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/CheckSum.cpp @@ -1,97 +1,97 @@ -/** -* @file -* @brief CheckSum implementation from http://www.flounder.com/checksum.htm -* -*/ -#include "CheckSum.h" - -/**************************************************************************** -* CheckSum::add -* Inputs: -* unsigned int d: word to add -* Result: void -* -* Effect: -* Adds the bytes of the unsigned int to the CheckSum -****************************************************************************/ - -void CheckSum::Add ( unsigned int value ) -{ - union - { - unsigned int value; - unsigned char bytes[ 4 ]; - } - - data; - data.value = value; - - for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) - Add ( data.bytes[ i ] ) - - ; -} // CheckSum::add(unsigned int) - -/**************************************************************************** -* CheckSum::add -* Inputs: -* unsigned short value: -* Result: void -* -* Effect: -* Adds the bytes of the unsigned short value to the CheckSum -****************************************************************************/ - -void CheckSum::Add ( unsigned short value ) -{ - union - { - unsigned short value; - unsigned char bytes[ 2 ]; - } - - data; - data.value = value; - - for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) - Add ( data.bytes[ i ] ) - - ; -} // CheckSum::add(unsigned short) - -/**************************************************************************** -* CheckSum::add -* Inputs: -* unsigned char value: -* Result: void -* -* Effect: -* Adds the byte to the CheckSum -****************************************************************************/ - -void CheckSum::Add ( unsigned char value ) -{ - unsigned char cipher = (unsigned char)( value ^ ( r >> 8 ) ); - r = ( cipher + r ) * c1 + c2; - sum += cipher; -} // CheckSum::add(unsigned char) - - -/**************************************************************************** -* CheckSum::add -* Inputs: -* LPunsigned char b: pointer to byte array -* unsigned int length: count -* Result: void -* -* Effect: -* Adds the bytes to the CheckSum -****************************************************************************/ - -void CheckSum::Add ( unsigned char *b, unsigned int length ) -{ - for ( unsigned int i = 0; i < length; i++ ) - Add ( b[ i ] ) - - ; -} // CheckSum::add(LPunsigned char, unsigned int) +/** +* @file +* @brief CheckSum implementation from http://www.flounder.com/checksum.htm +* +*/ +#include "CheckSum.h" + +/**************************************************************************** +* CheckSum::add +* Inputs: +* unsigned int d: word to add +* Result: void +* +* Effect: +* Adds the bytes of the unsigned int to the CheckSum +****************************************************************************/ + +void CheckSum::Add ( unsigned int value ) +{ + union + { + unsigned int value; + unsigned char bytes[ 4 ]; + } + + data; + data.value = value; + + for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) + Add ( data.bytes[ i ] ) + + ; +} // CheckSum::add(unsigned int) + +/**************************************************************************** +* CheckSum::add +* Inputs: +* unsigned short value: +* Result: void +* +* Effect: +* Adds the bytes of the unsigned short value to the CheckSum +****************************************************************************/ + +void CheckSum::Add ( unsigned short value ) +{ + union + { + unsigned short value; + unsigned char bytes[ 2 ]; + } + + data; + data.value = value; + + for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) + Add ( data.bytes[ i ] ) + + ; +} // CheckSum::add(unsigned short) + +/**************************************************************************** +* CheckSum::add +* Inputs: +* unsigned char value: +* Result: void +* +* Effect: +* Adds the byte to the CheckSum +****************************************************************************/ + +void CheckSum::Add ( unsigned char value ) +{ + unsigned char cipher = (unsigned char)( value ^ ( r >> 8 ) ); + r = ( cipher + r ) * c1 + c2; + sum += cipher; +} // CheckSum::add(unsigned char) + + +/**************************************************************************** +* CheckSum::add +* Inputs: +* LPunsigned char b: pointer to byte array +* unsigned int length: count +* Result: void +* +* Effect: +* Adds the bytes to the CheckSum +****************************************************************************/ + +void CheckSum::Add ( unsigned char *b, unsigned int length ) +{ + for ( unsigned int i = 0; i < length; i++ ) + Add ( b[ i ] ) + + ; +} // CheckSum::add(LPunsigned char, unsigned int) diff --git a/project/lib_projects/raknet/jni/RaknetSources/CheckSum.h b/project/lib_projects/raknet/jni/RaknetSources/CheckSum.h index 09e57f7..bd768a1 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CheckSum.h +++ b/project/lib_projects/raknet/jni/RaknetSources/CheckSum.h @@ -1,53 +1,53 @@ -/// -/// \file CheckSum.cpp -/// \brief [Internal] CheckSum implementation from http://www.flounder.com/checksum.htm -/// - -#ifndef __CHECKSUM_H -#define __CHECKSUM_H - -#include "RakMemoryOverride.h" - -/// Generates and validates checksums -class CheckSum -{ - -public: - - /// Default constructor - - CheckSum() - { - Clear(); - } - - void Clear() - { - sum = 0; - r = 55665; - c1 = 52845; - c2 = 22719; - } - - void Add ( unsigned int w ); - - - void Add ( unsigned short w ); - - void Add ( unsigned char* b, unsigned int length ); - - void Add ( unsigned char b ); - - unsigned int Get () - { - return sum; - } - -protected: - unsigned short r; - unsigned short c1; - unsigned short c2; - unsigned int sum; -}; - -#endif +/// +/// \file CheckSum.cpp +/// \brief [Internal] CheckSum implementation from http://www.flounder.com/checksum.htm +/// + +#ifndef __CHECKSUM_H +#define __CHECKSUM_H + +#include "RakMemoryOverride.h" + +/// Generates and validates checksums +class CheckSum +{ + +public: + + /// Default constructor + + CheckSum() + { + Clear(); + } + + void Clear() + { + sum = 0; + r = 55665; + c1 = 52845; + c2 = 22719; + } + + void Add ( unsigned int w ); + + + void Add ( unsigned short w ); + + void Add ( unsigned char* b, unsigned int length ); + + void Add ( unsigned char b ); + + unsigned int Get () + { + return sum; + } + +protected: + unsigned short r; + unsigned short c1; + unsigned short c2; + unsigned int sum; +}; + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/CloudClient.cpp b/project/lib_projects/raknet/jni/RaknetSources/CloudClient.cpp index cdfd2c6..248a42d 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CloudClient.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/CloudClient.cpp @@ -1,242 +1,242 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudClient==1 - -#include "CloudClient.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" -#include "RakPeerInterface.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(CloudClient,CloudClient); - -CloudClient::CloudClient() -{ - callback=0; - allocator=&unsetDefaultAllocator; -} -CloudClient::~CloudClient() -{ -} -void CloudClient::SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback) -{ - callback=_callback; - allocator=_allocator; -} -void CloudClient::Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier) -{ - RakAssert(cloudKey); - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_POST_REQUEST); - cloudKey->Serialize(true,&bsOut); - if (data==0) - dataLengthBytes=0; - bsOut.Write(dataLengthBytes); - if (dataLengthBytes>0) - bsOut.WriteAlignedBytes((const unsigned char*) data, dataLengthBytes); - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -void CloudClient::Release(DataStructures::List &keys, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_RELEASE_REQUEST); - RakAssert(keys.Size() < (uint16_t)-1 ); - bsOut.WriteCasted(keys.Size()); - for (uint16_t i=0; i < keys.Size(); i++) - { - keys[i].Serialize(true,&bsOut); - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -bool CloudClient::Get(CloudQuery *keyQuery, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); - keyQuery->Serialize(true, &bsOut); - bsOut.WriteCasted(0); // Specific systems - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); - return true; -} -bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); - keyQuery->Serialize(true, &bsOut); - bsOut.WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - bsOut.Write(specificSystems[i]); - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); - return true; -} -bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); - keyQuery->Serialize(true, &bsOut); - bsOut.WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID) - { - bsOut.Write(true); - bsOut.Write(specificSystems[i]->clientGUID); - } - else - { - bsOut.Write(false); - bsOut.Write(specificSystems[i]->clientSystemAddress); - } - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); - return true; -} -void CloudClient::Unsubscribe(DataStructures::List &keys, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); - RakAssert(keys.Size() < (uint16_t)-1 ); - bsOut.WriteCasted(keys.Size()); - for (uint16_t i=0; i < keys.Size(); i++) - { - keys[i].Serialize(true,&bsOut); - } - bsOut.WriteCasted(0); - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -void CloudClient::Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); - RakAssert(keys.Size() < (uint16_t)-1 ); - bsOut.WriteCasted(keys.Size()); - for (uint16_t i=0; i < keys.Size(); i++) - { - keys[i].Serialize(true,&bsOut); - } - bsOut.WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - bsOut.Write(specificSystems[i]); - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -void CloudClient::Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); - RakAssert(keys.Size() < (uint16_t)-1 ); - bsOut.WriteCasted(keys.Size()); - for (uint16_t i=0; i < keys.Size(); i++) - { - keys[i].Serialize(true,&bsOut); - } - bsOut.WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID) - { - bsOut.Write(true); - bsOut.Write(specificSystems[i]->clientGUID); - } - else - { - bsOut.Write(false); - bsOut.Write(specificSystems[i]->clientSystemAddress); - } - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -PluginReceiveResult CloudClient::OnReceive(Packet *packet) -{ - (void) packet; - - return RR_CONTINUE_PROCESSING; -} -void CloudClient::OnGetReponse(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator) -{ - if (_callback==0) - _callback=callback; - if (_allocator==0) - _allocator=allocator; - - CloudQueryResult cloudQueryResult; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - cloudQueryResult.Serialize(false,&bsIn,_allocator); - bool deallocateRowsAfterReturn=true; - _callback->OnGet(&cloudQueryResult, &deallocateRowsAfterReturn); - if (deallocateRowsAfterReturn) - { - unsigned int i; - for (i=0; i < cloudQueryResult.rowsReturned.Size(); i++) - { - _allocator->DeallocateRowData(cloudQueryResult.rowsReturned[i]->data); - _allocator->DeallocateCloudQueryRow(cloudQueryResult.rowsReturned[i]); - } - } -} -void CloudClient::OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator) -{ - if (_allocator==0) - _allocator=allocator; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - cloudQueryResult->Serialize(false,&bsIn,_allocator); -} -void CloudClient::OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator) -{ - if (_callback==0) - _callback=callback; - if (_allocator==0) - _allocator=allocator; - - bool wasUpdated=false; - CloudQueryRow row; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bsIn.Read(wasUpdated); - row.Serialize(false,&bsIn,_allocator); - bool deallocateRowAfterReturn=true; - _callback->OnSubscriptionNotification(&row, wasUpdated, &deallocateRowAfterReturn); - if (deallocateRowAfterReturn) - { - _allocator->DeallocateRowData(row.data); - } -} -void CloudClient::OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator) -{ - if (_allocator==0) - _allocator=allocator; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bool b=false; - bsIn.Read(b); - *wasUpdated=b; - row->Serialize(false,&bsIn,_allocator); -} -void CloudClient::DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult) -{ - unsigned int i; - for (i=0; i < cloudQueryResult->rowsReturned.Size(); i++) - { - allocator->DeallocateRowData(cloudQueryResult->rowsReturned[i]->data); - allocator->DeallocateCloudQueryRow(cloudQueryResult->rowsReturned[i]); - } -} -void CloudClient::DeallocateWithDefaultAllocator(CloudQueryRow *row) -{ - allocator->DeallocateRowData(row->data); -} -#endif +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudClient==1 + +#include "CloudClient.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" +#include "RakPeerInterface.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(CloudClient,CloudClient); + +CloudClient::CloudClient() +{ + callback=0; + allocator=&unsetDefaultAllocator; +} +CloudClient::~CloudClient() +{ +} +void CloudClient::SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback) +{ + callback=_callback; + allocator=_allocator; +} +void CloudClient::Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier) +{ + RakAssert(cloudKey); + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_POST_REQUEST); + cloudKey->Serialize(true,&bsOut); + if (data==0) + dataLengthBytes=0; + bsOut.Write(dataLengthBytes); + if (dataLengthBytes>0) + bsOut.WriteAlignedBytes((const unsigned char*) data, dataLengthBytes); + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +void CloudClient::Release(DataStructures::List &keys, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_RELEASE_REQUEST); + RakAssert(keys.Size() < (uint16_t)-1 ); + bsOut.WriteCasted(keys.Size()); + for (uint16_t i=0; i < keys.Size(); i++) + { + keys[i].Serialize(true,&bsOut); + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +bool CloudClient::Get(CloudQuery *keyQuery, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); + keyQuery->Serialize(true, &bsOut); + bsOut.WriteCasted(0); // Specific systems + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); + return true; +} +bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); + keyQuery->Serialize(true, &bsOut); + bsOut.WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + bsOut.Write(specificSystems[i]); + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); + return true; +} +bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); + keyQuery->Serialize(true, &bsOut); + bsOut.WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID) + { + bsOut.Write(true); + bsOut.Write(specificSystems[i]->clientGUID); + } + else + { + bsOut.Write(false); + bsOut.Write(specificSystems[i]->clientSystemAddress); + } + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); + return true; +} +void CloudClient::Unsubscribe(DataStructures::List &keys, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); + RakAssert(keys.Size() < (uint16_t)-1 ); + bsOut.WriteCasted(keys.Size()); + for (uint16_t i=0; i < keys.Size(); i++) + { + keys[i].Serialize(true,&bsOut); + } + bsOut.WriteCasted(0); + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +void CloudClient::Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); + RakAssert(keys.Size() < (uint16_t)-1 ); + bsOut.WriteCasted(keys.Size()); + for (uint16_t i=0; i < keys.Size(); i++) + { + keys[i].Serialize(true,&bsOut); + } + bsOut.WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + bsOut.Write(specificSystems[i]); + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +void CloudClient::Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); + RakAssert(keys.Size() < (uint16_t)-1 ); + bsOut.WriteCasted(keys.Size()); + for (uint16_t i=0; i < keys.Size(); i++) + { + keys[i].Serialize(true,&bsOut); + } + bsOut.WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID) + { + bsOut.Write(true); + bsOut.Write(specificSystems[i]->clientGUID); + } + else + { + bsOut.Write(false); + bsOut.Write(specificSystems[i]->clientSystemAddress); + } + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +PluginReceiveResult CloudClient::OnReceive(Packet *packet) +{ + (void) packet; + + return RR_CONTINUE_PROCESSING; +} +void CloudClient::OnGetReponse(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator) +{ + if (_callback==0) + _callback=callback; + if (_allocator==0) + _allocator=allocator; + + CloudQueryResult cloudQueryResult; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + cloudQueryResult.Serialize(false,&bsIn,_allocator); + bool deallocateRowsAfterReturn=true; + _callback->OnGet(&cloudQueryResult, &deallocateRowsAfterReturn); + if (deallocateRowsAfterReturn) + { + unsigned int i; + for (i=0; i < cloudQueryResult.rowsReturned.Size(); i++) + { + _allocator->DeallocateRowData(cloudQueryResult.rowsReturned[i]->data); + _allocator->DeallocateCloudQueryRow(cloudQueryResult.rowsReturned[i]); + } + } +} +void CloudClient::OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator) +{ + if (_allocator==0) + _allocator=allocator; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + cloudQueryResult->Serialize(false,&bsIn,_allocator); +} +void CloudClient::OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator) +{ + if (_callback==0) + _callback=callback; + if (_allocator==0) + _allocator=allocator; + + bool wasUpdated=false; + CloudQueryRow row; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bsIn.Read(wasUpdated); + row.Serialize(false,&bsIn,_allocator); + bool deallocateRowAfterReturn=true; + _callback->OnSubscriptionNotification(&row, wasUpdated, &deallocateRowAfterReturn); + if (deallocateRowAfterReturn) + { + _allocator->DeallocateRowData(row.data); + } +} +void CloudClient::OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator) +{ + if (_allocator==0) + _allocator=allocator; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bool b=false; + bsIn.Read(b); + *wasUpdated=b; + row->Serialize(false,&bsIn,_allocator); +} +void CloudClient::DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult) +{ + unsigned int i; + for (i=0; i < cloudQueryResult->rowsReturned.Size(); i++) + { + allocator->DeallocateRowData(cloudQueryResult->rowsReturned[i]->data); + allocator->DeallocateCloudQueryRow(cloudQueryResult->rowsReturned[i]); + } +} +void CloudClient::DeallocateWithDefaultAllocator(CloudQueryRow *row) +{ + allocator->DeallocateRowData(row->data); +} +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/CloudClient.h b/project/lib_projects/raknet/jni/RaknetSources/CloudClient.h index 293740d..dc75c1e 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CloudClient.h +++ b/project/lib_projects/raknet/jni/RaknetSources/CloudClient.h @@ -1,163 +1,163 @@ -/// \file CloudClient.h -/// \brief Queries CloudMemoryServer to download data that other clients have uploaded -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudClient==1 - -#ifndef __CLOUD_CLIENT_H -#define __CLOUD_CLIENT_H - -#include "PluginInterface2.h" -#include "CloudCommon.h" -#include "RakMemoryOverride.h" -#include "DS_Hash.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -class CloudClientCallback; - -/// \defgroup CLOUD_GROUP CloudComputing -/// \brief Contains the CloudClient and CloudServer plugins -/// \details The CloudServer plugins operates on requests from the CloudClient plugin. The servers are in a fully connected mesh topology, which the clients are connected to any server. Clients can interact with each other by posting and subscribing to memory updates, without being directly connected or even knowing about each other. -/// \ingroup PLUGINS_GROUP - -/// \brief Performs Post() and Get() operations on CloudMemoryServer -/// \details A CloudClient is a computer connected to one or more servers in a cloud configuration. Operations by one CloudClient can be received and subscribed to by other instances of CloudClient, without those clients being connected, even on different servers. -/// \ingroup CLOUD_GROUP -class RAK_DLL_EXPORT CloudClient : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(CloudClient) - - CloudClient(); - virtual ~CloudClient(); - - /// \brief Set the default callbacks for OnGetReponse(), OnSubscriptionNotification(), and OnSubscriptionDataDeleted() - /// \details Pointers to CloudAllocator and CloudClientCallback can be stored by the system if desired. If a callback is not provided to OnGetReponse(), OnSubscriptionNotification(), OnSubscriptionDataDeleted(), the callback passed here will be used instead. - /// \param[in] _allocator An instance of CloudAllocator - /// \param[in] _callback An instance of CloudClientCallback - virtual void SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback); - - /// \brief Uploads data to the cloud - /// \details Data uploaded to the cloud will be stored by the server sent to, identified by \a systemIdentifier. - /// As long as you are connected to this server, the data will persist. Queries for that data by the Get() operation will - /// return the RakNetGUID and SystemAddress of the uploader, as well as the data itself. - /// Furthermore, if any clients are subscribed to the particular CloudKey passed, those clients will get update notices that the data has changed - /// Passing data with the same \a cloudKey more than once will overwrite the prior value. - /// This call will silently fail if CloudServer::SetMaxUploadBytesPerClient() is exceeded - /// \param[in] cloudKey Identifies the data being uploaded - /// \param[in] data A pointer to data to upload. This pointer does not need to persist past the call - /// \param[in] dataLengthBytes The length in bytes of \a data - /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. - virtual void Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier); - - /// \brief Releases one or more data previously uploaded with Post() - /// \details If a remote system has subscribed to one or more of the \a keys uploaded, they will get ID_CLOUD_SUBSCRIPTION_NOTIFICATION notifications containing the last value uploaded before deletions - /// \param[in] cloudKey Identifies the data to release. It is possible to remove uploads from multiple Post() calls at once. - /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. - virtual void Release(DataStructures::List &keys, RakNetGUID systemIdentifier); - - /// \brief Gets data from the cloud - /// \details For a given query containing one or more keys, return data that matches those keys. - /// The values will be returned in the ID_CLOUD_GET_RESPONSE packet, which should be passed to OnGetReponse() and will invoke CloudClientCallback::OnGet() - /// CloudQuery::startingRowIndex is used to skip the first n values that would normally be returned.. - /// CloudQuery::maxRowsToReturn is used to limit the number of rows returned. The number of rows returned may also be limited by CloudServer::SetMaxBytesPerDownload(); - /// CloudQuery::subscribeToResults if set to true, will cause ID_CLOUD_SUBSCRIPTION_NOTIFICATION to be returned to us when any of the keys in the query are updated or are deleted. - /// ID_CLOUD_GET_RESPONSE will be returned even if subscribing to the result list. Only later updates will return ID_CLOUD_SUBSCRIPTION_NOTIFICATION. - /// Calling Get() with CloudQuery::subscribeToResults false, when you are already subscribed, does not remove the subscription. Use Unsubscribe() for this. - /// Resubscribing using the same CloudKey but a different or no \a specificSystems overwrites the subscribed systems for those keys. - /// \param[in] cloudQuery One or more keys, and optional parameters to perform with the Get - /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. - /// \param[in] specificSystems It is possible to get or subscribe to updates only for specific uploading CloudClient instances. Pass the desired instances here. The overload that does not have the specificSystems parameter is treated as subscribing to all updates from all clients. - virtual bool Get(CloudQuery *cloudQuery, RakNetGUID systemIdentifier); - virtual bool Get(CloudQuery *cloudQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); - virtual bool Get(CloudQuery *cloudQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); - - /// \brief Unsubscribe from updates previously subscribed to using Get() with the CloudQuery::subscribeToResults set to true - /// The \a keys and \a specificSystems parameters are logically treated as AND when checking subscriptions on the server - /// The overload that does not take specificSystems unsubscribes to all passed keys, regardless of system - /// You cannot unsubscribe specific systems when previously subscribed to updates from any system. To do this, first Unsubscribe() from all systems, and call Get() with the \a specificSystems parameter explicilty listing the systems you want to subscribe to. - virtual void Unsubscribe(DataStructures::List &keys, RakNetGUID systemIdentifier); - virtual void Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); - virtual void Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); - - /// \brief Call this when you get ID_CLOUD_GET_RESPONSE - /// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used - /// \param[in] packet Packet structure returned from RakPeerInterface - /// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used. - /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. - virtual void OnGetReponse(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0); - - /// \brief Call this when you get ID_CLOUD_GET_RESPONSE - /// Different form of OnGetReponse that returns to a structure that you pass, instead of using a callback - /// You are responsible for deallocation with this form - /// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used - /// \param[out] cloudQueryResult A pointer to a structure that will be filled out with data - /// \param[in] packet Packet structure returned from RakPeerInterface - /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. - virtual void OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator=0); - - /// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION - /// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used - /// \param[in] packet Packet structure returned from RakPeerInterface - /// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used. - /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. - virtual void OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0); - - /// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION - /// Different form of OnSubscriptionNotification that returns to a structure that you pass, instead of using a callback - /// You are responsible for deallocation with this form - /// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used - /// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion - /// \param[out] row A pointer to a structure that will be filled out with data - /// \param[in] packet Packet structure returned from RakPeerInterface - /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. - virtual void OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator=0); - - /// If you never specified an allocator, and used the non-callback form of OnGetReponse(), deallocate cloudQueryResult with this function - virtual void DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult); - - /// If you never specified an allocator, and used the non-callback form of OnSubscriptionNotification(), deallocate row with this function - virtual void DeallocateWithDefaultAllocator(CloudQueryRow *row); - -protected: - PluginReceiveResult OnReceive(Packet *packet); - - CloudClientCallback *callback; - CloudAllocator *allocator; - - CloudAllocator unsetDefaultAllocator; -}; - -/// \ingroup CLOUD_GROUP -/// Parses ID_CLOUD_GET_RESPONSE and ID_CLOUD_SUBSCRIPTION_NOTIFICATION in a convenient callback form -class RAK_DLL_EXPORT CloudClientCallback -{ -public: - CloudClientCallback() {} - virtual ~CloudClientCallback() {} - - /// \brief Called in response to ID_CLOUD_GET_RESPONSE - /// \param[out] result Contains the original query passed to Get(), and a list of rows returned. - /// \param[out] deallocateRowsAfterReturn CloudQueryResult::rowsReturned will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator. - virtual void OnGet(RakNet::CloudQueryResult *result, bool *deallocateRowsAfterReturn) {(void) result; (void) deallocateRowsAfterReturn;} - - /// \brief Called in response to ID_CLOUD_SUBSCRIPTION_NOTIFICATION - /// \param[out] result Contains the row updated - /// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion - /// \param[out] deallocateRowAfterReturn \a result will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator. - virtual void OnSubscriptionNotification(RakNet::CloudQueryRow *result, bool wasUpdated, bool *deallocateRowAfterReturn) {(void) result; (void) wasUpdated; (void) deallocateRowAfterReturn;} -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file CloudClient.h +/// \brief Queries CloudMemoryServer to download data that other clients have uploaded +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudClient==1 + +#ifndef __CLOUD_CLIENT_H +#define __CLOUD_CLIENT_H + +#include "PluginInterface2.h" +#include "CloudCommon.h" +#include "RakMemoryOverride.h" +#include "DS_Hash.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +class CloudClientCallback; + +/// \defgroup CLOUD_GROUP CloudComputing +/// \brief Contains the CloudClient and CloudServer plugins +/// \details The CloudServer plugins operates on requests from the CloudClient plugin. The servers are in a fully connected mesh topology, which the clients are connected to any server. Clients can interact with each other by posting and subscribing to memory updates, without being directly connected or even knowing about each other. +/// \ingroup PLUGINS_GROUP + +/// \brief Performs Post() and Get() operations on CloudMemoryServer +/// \details A CloudClient is a computer connected to one or more servers in a cloud configuration. Operations by one CloudClient can be received and subscribed to by other instances of CloudClient, without those clients being connected, even on different servers. +/// \ingroup CLOUD_GROUP +class RAK_DLL_EXPORT CloudClient : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(CloudClient) + + CloudClient(); + virtual ~CloudClient(); + + /// \brief Set the default callbacks for OnGetReponse(), OnSubscriptionNotification(), and OnSubscriptionDataDeleted() + /// \details Pointers to CloudAllocator and CloudClientCallback can be stored by the system if desired. If a callback is not provided to OnGetReponse(), OnSubscriptionNotification(), OnSubscriptionDataDeleted(), the callback passed here will be used instead. + /// \param[in] _allocator An instance of CloudAllocator + /// \param[in] _callback An instance of CloudClientCallback + virtual void SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback); + + /// \brief Uploads data to the cloud + /// \details Data uploaded to the cloud will be stored by the server sent to, identified by \a systemIdentifier. + /// As long as you are connected to this server, the data will persist. Queries for that data by the Get() operation will + /// return the RakNetGUID and SystemAddress of the uploader, as well as the data itself. + /// Furthermore, if any clients are subscribed to the particular CloudKey passed, those clients will get update notices that the data has changed + /// Passing data with the same \a cloudKey more than once will overwrite the prior value. + /// This call will silently fail if CloudServer::SetMaxUploadBytesPerClient() is exceeded + /// \param[in] cloudKey Identifies the data being uploaded + /// \param[in] data A pointer to data to upload. This pointer does not need to persist past the call + /// \param[in] dataLengthBytes The length in bytes of \a data + /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. + virtual void Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier); + + /// \brief Releases one or more data previously uploaded with Post() + /// \details If a remote system has subscribed to one or more of the \a keys uploaded, they will get ID_CLOUD_SUBSCRIPTION_NOTIFICATION notifications containing the last value uploaded before deletions + /// \param[in] cloudKey Identifies the data to release. It is possible to remove uploads from multiple Post() calls at once. + /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. + virtual void Release(DataStructures::List &keys, RakNetGUID systemIdentifier); + + /// \brief Gets data from the cloud + /// \details For a given query containing one or more keys, return data that matches those keys. + /// The values will be returned in the ID_CLOUD_GET_RESPONSE packet, which should be passed to OnGetReponse() and will invoke CloudClientCallback::OnGet() + /// CloudQuery::startingRowIndex is used to skip the first n values that would normally be returned.. + /// CloudQuery::maxRowsToReturn is used to limit the number of rows returned. The number of rows returned may also be limited by CloudServer::SetMaxBytesPerDownload(); + /// CloudQuery::subscribeToResults if set to true, will cause ID_CLOUD_SUBSCRIPTION_NOTIFICATION to be returned to us when any of the keys in the query are updated or are deleted. + /// ID_CLOUD_GET_RESPONSE will be returned even if subscribing to the result list. Only later updates will return ID_CLOUD_SUBSCRIPTION_NOTIFICATION. + /// Calling Get() with CloudQuery::subscribeToResults false, when you are already subscribed, does not remove the subscription. Use Unsubscribe() for this. + /// Resubscribing using the same CloudKey but a different or no \a specificSystems overwrites the subscribed systems for those keys. + /// \param[in] cloudQuery One or more keys, and optional parameters to perform with the Get + /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. + /// \param[in] specificSystems It is possible to get or subscribe to updates only for specific uploading CloudClient instances. Pass the desired instances here. The overload that does not have the specificSystems parameter is treated as subscribing to all updates from all clients. + virtual bool Get(CloudQuery *cloudQuery, RakNetGUID systemIdentifier); + virtual bool Get(CloudQuery *cloudQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); + virtual bool Get(CloudQuery *cloudQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); + + /// \brief Unsubscribe from updates previously subscribed to using Get() with the CloudQuery::subscribeToResults set to true + /// The \a keys and \a specificSystems parameters are logically treated as AND when checking subscriptions on the server + /// The overload that does not take specificSystems unsubscribes to all passed keys, regardless of system + /// You cannot unsubscribe specific systems when previously subscribed to updates from any system. To do this, first Unsubscribe() from all systems, and call Get() with the \a specificSystems parameter explicilty listing the systems you want to subscribe to. + virtual void Unsubscribe(DataStructures::List &keys, RakNetGUID systemIdentifier); + virtual void Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); + virtual void Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); + + /// \brief Call this when you get ID_CLOUD_GET_RESPONSE + /// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used + /// \param[in] packet Packet structure returned from RakPeerInterface + /// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used. + /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. + virtual void OnGetReponse(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0); + + /// \brief Call this when you get ID_CLOUD_GET_RESPONSE + /// Different form of OnGetReponse that returns to a structure that you pass, instead of using a callback + /// You are responsible for deallocation with this form + /// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used + /// \param[out] cloudQueryResult A pointer to a structure that will be filled out with data + /// \param[in] packet Packet structure returned from RakPeerInterface + /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. + virtual void OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator=0); + + /// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION + /// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used + /// \param[in] packet Packet structure returned from RakPeerInterface + /// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used. + /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. + virtual void OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0); + + /// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION + /// Different form of OnSubscriptionNotification that returns to a structure that you pass, instead of using a callback + /// You are responsible for deallocation with this form + /// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used + /// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion + /// \param[out] row A pointer to a structure that will be filled out with data + /// \param[in] packet Packet structure returned from RakPeerInterface + /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. + virtual void OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator=0); + + /// If you never specified an allocator, and used the non-callback form of OnGetReponse(), deallocate cloudQueryResult with this function + virtual void DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult); + + /// If you never specified an allocator, and used the non-callback form of OnSubscriptionNotification(), deallocate row with this function + virtual void DeallocateWithDefaultAllocator(CloudQueryRow *row); + +protected: + PluginReceiveResult OnReceive(Packet *packet); + + CloudClientCallback *callback; + CloudAllocator *allocator; + + CloudAllocator unsetDefaultAllocator; +}; + +/// \ingroup CLOUD_GROUP +/// Parses ID_CLOUD_GET_RESPONSE and ID_CLOUD_SUBSCRIPTION_NOTIFICATION in a convenient callback form +class RAK_DLL_EXPORT CloudClientCallback +{ +public: + CloudClientCallback() {} + virtual ~CloudClientCallback() {} + + /// \brief Called in response to ID_CLOUD_GET_RESPONSE + /// \param[out] result Contains the original query passed to Get(), and a list of rows returned. + /// \param[out] deallocateRowsAfterReturn CloudQueryResult::rowsReturned will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator. + virtual void OnGet(RakNet::CloudQueryResult *result, bool *deallocateRowsAfterReturn) {(void) result; (void) deallocateRowsAfterReturn;} + + /// \brief Called in response to ID_CLOUD_SUBSCRIPTION_NOTIFICATION + /// \param[out] result Contains the row updated + /// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion + /// \param[out] deallocateRowAfterReturn \a result will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator. + virtual void OnSubscriptionNotification(RakNet::CloudQueryRow *result, bool wasUpdated, bool *deallocateRowAfterReturn) {(void) result; (void) wasUpdated; (void) deallocateRowAfterReturn;} +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/CloudCommon.cpp b/project/lib_projects/raknet/jni/RaknetSources/CloudCommon.cpp index 7d07410..083857d 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CloudCommon.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/CloudCommon.cpp @@ -1,159 +1,159 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 - -#include "CloudCommon.h" -#include "BitStream.h" - -using namespace RakNet; - -int RakNet::CloudKeyComp(const CloudKey &key, const CloudKey &data) -{ - if (key.primaryKey < data.primaryKey) - return -1; - if (key.primaryKey > data.primaryKey) - return 1; - if (key.secondaryKey < data.secondaryKey) - return -1; - if (key.secondaryKey > data.secondaryKey) - return 1; - return 0; -} - -CloudQueryRow* CloudAllocator::AllocateCloudQueryRow(void) -{ - return RakNet::OP_NEW(_FILE_AND_LINE_); -} -void CloudAllocator::DeallocateCloudQueryRow(CloudQueryRow *row) -{ - RakNet::OP_DELETE(row,_FILE_AND_LINE_); -} -unsigned char *CloudAllocator::AllocateRowData(uint32_t bytesNeededForData) -{ - return (unsigned char*) rakMalloc_Ex(bytesNeededForData,_FILE_AND_LINE_); -} -void CloudAllocator::DeallocateRowData(void *data) -{ - rakFree_Ex(data, _FILE_AND_LINE_); -} -void CloudKey::Serialize(bool writeToBitstream, BitStream *bitStream) -{ - bitStream->Serialize(writeToBitstream, primaryKey); - bitStream->Serialize(writeToBitstream, secondaryKey); -} -void CloudQuery::Serialize(bool writeToBitstream, BitStream *bitStream) -{ - bool startingRowIndexIsZero=0; - bool maxRowsToReturnIsZero=0; - startingRowIndexIsZero=startingRowIndex==0; - maxRowsToReturnIsZero=maxRowsToReturn==0; - bitStream->Serialize(writeToBitstream,startingRowIndexIsZero); - bitStream->Serialize(writeToBitstream,maxRowsToReturnIsZero); - bitStream->Serialize(writeToBitstream,subscribeToResults); - if (startingRowIndexIsZero==false) - bitStream->Serialize(writeToBitstream,startingRowIndex); - if (maxRowsToReturnIsZero==false) - bitStream->Serialize(writeToBitstream,maxRowsToReturn); - RakAssert(keys.Size()<(uint16_t)-1); - uint16_t numKeys = (uint16_t) keys.Size(); - bitStream->Serialize(writeToBitstream,numKeys); - if (writeToBitstream) - { - for (uint16_t i=0; i < numKeys; i++) - { - keys[i].Serialize(true,bitStream); - } - } - else - { - CloudKey cmdk; - for (uint16_t i=0; i < numKeys; i++) - { - cmdk.Serialize(false,bitStream); - keys.Push(cmdk, _FILE_AND_LINE_); - } - } -} -void CloudQueryRow::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator) -{ - key.Serialize(writeToBitstream,bitStream); - bitStream->Serialize(writeToBitstream,serverSystemAddress); - bitStream->Serialize(writeToBitstream,clientSystemAddress); - bitStream->Serialize(writeToBitstream,serverGUID); - bitStream->Serialize(writeToBitstream,clientGUID); - bitStream->Serialize(writeToBitstream,length); - if (writeToBitstream) - { - bitStream->WriteAlignedBytes((const unsigned char*) data,length); - } - else - { - if (length>0) - { - data = allocator->AllocateRowData(length); - if (data) - { - bitStream->ReadAlignedBytes((unsigned char *) data,length); - } - else - { - notifyOutOfMemory(_FILE_AND_LINE_); - } - } - else - data=0; - } -} -void CloudQueryResult::SerializeHeader(bool writeToBitstream, BitStream *bitStream) -{ - cloudQuery.Serialize(writeToBitstream,bitStream); - bitStream->Serialize(writeToBitstream,subscribeToResults); -} -void CloudQueryResult::SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream) -{ - bitStream->Serialize(writeToBitstream,numRows); -} -void CloudQueryResult::SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator) -{ - if (writeToBitstream) - { - for (uint16_t i=0; i < numRows; i++) - { - rowsReturned[i]->Serialize(true,bitStream, allocator); - } - } - else - { - CloudQueryRow* cmdr; - for (uint16_t i=0; i < numRows; i++) - { - cmdr = allocator->AllocateCloudQueryRow(); - if (cmdr) - { - cmdr->Serialize(false,bitStream,allocator); - if (cmdr->data==0 && cmdr->length>0) - { - allocator->DeallocateCloudQueryRow(cmdr); - notifyOutOfMemory(_FILE_AND_LINE_); - numRows=i; - return; - } - rowsReturned.Push(cmdr, _FILE_AND_LINE_); - } - else - { - notifyOutOfMemory(_FILE_AND_LINE_); - numRows=i; - return; - } - } - } -} -void CloudQueryResult::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator) -{ - SerializeHeader(writeToBitstream, bitStream); - uint32_t numRows = (uint32_t) rowsReturned.Size(); - SerializeNumRows(writeToBitstream, numRows, bitStream); - SerializeCloudQueryRows(writeToBitstream, numRows, bitStream, allocator); -} - -#endif // #if _RAKNET_SUPPORT_CloudMemoryClient==1 || _RAKNET_SUPPORT_CloudMemoryServer==1 +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 + +#include "CloudCommon.h" +#include "BitStream.h" + +using namespace RakNet; + +int RakNet::CloudKeyComp(const CloudKey &key, const CloudKey &data) +{ + if (key.primaryKey < data.primaryKey) + return -1; + if (key.primaryKey > data.primaryKey) + return 1; + if (key.secondaryKey < data.secondaryKey) + return -1; + if (key.secondaryKey > data.secondaryKey) + return 1; + return 0; +} + +CloudQueryRow* CloudAllocator::AllocateCloudQueryRow(void) +{ + return RakNet::OP_NEW(_FILE_AND_LINE_); +} +void CloudAllocator::DeallocateCloudQueryRow(CloudQueryRow *row) +{ + RakNet::OP_DELETE(row,_FILE_AND_LINE_); +} +unsigned char *CloudAllocator::AllocateRowData(uint32_t bytesNeededForData) +{ + return (unsigned char*) rakMalloc_Ex(bytesNeededForData,_FILE_AND_LINE_); +} +void CloudAllocator::DeallocateRowData(void *data) +{ + rakFree_Ex(data, _FILE_AND_LINE_); +} +void CloudKey::Serialize(bool writeToBitstream, BitStream *bitStream) +{ + bitStream->Serialize(writeToBitstream, primaryKey); + bitStream->Serialize(writeToBitstream, secondaryKey); +} +void CloudQuery::Serialize(bool writeToBitstream, BitStream *bitStream) +{ + bool startingRowIndexIsZero=0; + bool maxRowsToReturnIsZero=0; + startingRowIndexIsZero=startingRowIndex==0; + maxRowsToReturnIsZero=maxRowsToReturn==0; + bitStream->Serialize(writeToBitstream,startingRowIndexIsZero); + bitStream->Serialize(writeToBitstream,maxRowsToReturnIsZero); + bitStream->Serialize(writeToBitstream,subscribeToResults); + if (startingRowIndexIsZero==false) + bitStream->Serialize(writeToBitstream,startingRowIndex); + if (maxRowsToReturnIsZero==false) + bitStream->Serialize(writeToBitstream,maxRowsToReturn); + RakAssert(keys.Size()<(uint16_t)-1); + uint16_t numKeys = (uint16_t) keys.Size(); + bitStream->Serialize(writeToBitstream,numKeys); + if (writeToBitstream) + { + for (uint16_t i=0; i < numKeys; i++) + { + keys[i].Serialize(true,bitStream); + } + } + else + { + CloudKey cmdk; + for (uint16_t i=0; i < numKeys; i++) + { + cmdk.Serialize(false,bitStream); + keys.Push(cmdk, _FILE_AND_LINE_); + } + } +} +void CloudQueryRow::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator) +{ + key.Serialize(writeToBitstream,bitStream); + bitStream->Serialize(writeToBitstream,serverSystemAddress); + bitStream->Serialize(writeToBitstream,clientSystemAddress); + bitStream->Serialize(writeToBitstream,serverGUID); + bitStream->Serialize(writeToBitstream,clientGUID); + bitStream->Serialize(writeToBitstream,length); + if (writeToBitstream) + { + bitStream->WriteAlignedBytes((const unsigned char*) data,length); + } + else + { + if (length>0) + { + data = allocator->AllocateRowData(length); + if (data) + { + bitStream->ReadAlignedBytes((unsigned char *) data,length); + } + else + { + notifyOutOfMemory(_FILE_AND_LINE_); + } + } + else + data=0; + } +} +void CloudQueryResult::SerializeHeader(bool writeToBitstream, BitStream *bitStream) +{ + cloudQuery.Serialize(writeToBitstream,bitStream); + bitStream->Serialize(writeToBitstream,subscribeToResults); +} +void CloudQueryResult::SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream) +{ + bitStream->Serialize(writeToBitstream,numRows); +} +void CloudQueryResult::SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator) +{ + if (writeToBitstream) + { + for (uint16_t i=0; i < numRows; i++) + { + rowsReturned[i]->Serialize(true,bitStream, allocator); + } + } + else + { + CloudQueryRow* cmdr; + for (uint16_t i=0; i < numRows; i++) + { + cmdr = allocator->AllocateCloudQueryRow(); + if (cmdr) + { + cmdr->Serialize(false,bitStream,allocator); + if (cmdr->data==0 && cmdr->length>0) + { + allocator->DeallocateCloudQueryRow(cmdr); + notifyOutOfMemory(_FILE_AND_LINE_); + numRows=i; + return; + } + rowsReturned.Push(cmdr, _FILE_AND_LINE_); + } + else + { + notifyOutOfMemory(_FILE_AND_LINE_); + numRows=i; + return; + } + } + } +} +void CloudQueryResult::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator) +{ + SerializeHeader(writeToBitstream, bitStream); + uint32_t numRows = (uint32_t) rowsReturned.Size(); + SerializeNumRows(writeToBitstream, numRows, bitStream); + SerializeCloudQueryRows(writeToBitstream, numRows, bitStream, allocator); +} + +#endif // #if _RAKNET_SUPPORT_CloudMemoryClient==1 || _RAKNET_SUPPORT_CloudMemoryServer==1 diff --git a/project/lib_projects/raknet/jni/RaknetSources/CloudCommon.h b/project/lib_projects/raknet/jni/RaknetSources/CloudCommon.h index dddb2cc..23fdf99 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CloudCommon.h +++ b/project/lib_projects/raknet/jni/RaknetSources/CloudCommon.h @@ -1,141 +1,141 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 - -#ifndef __CLOUD_COMMON_H -#define __CLOUD_COMMON_H - -#include "RakNetTypes.h" -#include "RakString.h" - -namespace RakNet -{ - -class BitStream; -struct CloudQueryRow; - -/// Allocates CloudQueryRow and the row data. Override to use derived classes or different allocators -/// \ingroup CLOUD_GROUP -class RAK_DLL_EXPORT CloudAllocator -{ -public: - CloudAllocator() {} - virtual ~CloudAllocator() {} - - /// \brief Allocate a row - virtual CloudQueryRow* AllocateCloudQueryRow(void); - /// \brief Free a row - virtual void DeallocateCloudQueryRow(CloudQueryRow *row); - /// \brief Allocate CloudQueryRow::data - virtual unsigned char *AllocateRowData(uint32_t bytesNeededForData); - /// \brief Free CloudQueryRow::data - virtual void DeallocateRowData(void *data); -}; - -/// Serves as a key to identify data uploaded to or queried from the server. -/// \ingroup CLOUD_GROUP -struct RAK_DLL_EXPORT CloudKey -{ - CloudKey() {} - CloudKey(RakNet::RakString _primaryKey, uint32_t _secondaryKey) : primaryKey(_primaryKey), secondaryKey(_secondaryKey) {} - ~CloudKey() {} - - /// Identifies the primary key. This is intended to be a major category, such as the name of the application - /// Must be non-empty - RakNet::RakString primaryKey; - - /// Identifies the secondary key. This is intended to be a subcategory enumeration, such as PLAYER_LIST or RUNNING_SCORES - uint32_t secondaryKey; - - /// \internal - void Serialize(bool writeToBitstream, BitStream *bitStream); -}; - -/// \internal -int CloudKeyComp(const CloudKey &key, const CloudKey &data); - -/// Data members used to query the cloud -/// \ingroup CLOUD_GROUP -struct RAK_DLL_EXPORT CloudQuery -{ - CloudQuery() {startingRowIndex=0; maxRowsToReturn=0; subscribeToResults=false;} - - /// List of keys to query. Must be at least of length 1. - /// This query is run on uploads from all clients, and those that match the combination of primaryKey and secondaryKey are potentially returned - /// If you pass more than one key at a time, the results are concatenated so if you need to differentiate between queries then send two different queries - DataStructures::List keys; - - /// If limiting the number of rows to return, this is the starting offset into the list. Has no effect unless maxRowsToReturn is > 0 - uint32_t startingRowIndex; - - /// Maximum number of rows to return. Actual number may still be less than this. Pass 0 to mean no-limit. - uint32_t maxRowsToReturn; - - /// If true, automatically get updates as the results returned to you change. Unsubscribe with CloudMemoryClient::Unsubscribe() - bool subscribeToResults; - - /// \internal - void Serialize(bool writeToBitstream, BitStream *bitStream); -}; - -/// \ingroup CLOUD_GROUP -struct RAK_DLL_EXPORT CloudQueryRow -{ - /// Key used to identify this data - CloudKey key; - - /// Data uploaded - unsigned char *data; - - /// Length of data uploaded - uint32_t length; - - /// System address of server that is holding this data, and the client is connected to - SystemAddress serverSystemAddress; - - /// System address of client that uploaded this data - SystemAddress clientSystemAddress; - - /// RakNetGUID of server that is holding this data, and the client is connected to - RakNetGUID serverGUID; - - /// RakNetGUID of client that uploaded this data - RakNetGUID clientGUID; - - /// \internal - void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator); -}; - -/// \ingroup CLOUD_GROUP -struct RAK_DLL_EXPORT CloudQueryResult -{ - /// Query originally passed to Download() - CloudQuery cloudQuery; - - /// Results returned from query. If there were multiple keys in CloudQuery::keys then see resultKeyIndices - DataStructures::List rowsReturned; - - /// If there were multiple keys in CloudQuery::keys, then each key is processed in order and the result concatenated to rowsReturned - /// The starting index of each query is written to resultKeyIndices - /// For example, if CloudQuery::keys had 4 keys, returning 3 rows, 0, rows, 5 rows, and 12 rows then - /// resultKeyIndices would be 0, 3, 3, 8 - DataStructures::List resultKeyIndices; - - - /// Whatever was passed to CloudClient::Get() as CloudQuery::subscribeToResults - bool subscribeToResults; - - /// \internal - void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator); - /// \internal - void SerializeHeader(bool writeToBitstream, BitStream *bitStream); - /// \internal - void SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream); - /// \internal - void SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator); -}; - -} // Namespace RakNet - -#endif // __CLOUD_COMMON_H - -#endif // #if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 + +#ifndef __CLOUD_COMMON_H +#define __CLOUD_COMMON_H + +#include "RakNetTypes.h" +#include "RakString.h" + +namespace RakNet +{ + +class BitStream; +struct CloudQueryRow; + +/// Allocates CloudQueryRow and the row data. Override to use derived classes or different allocators +/// \ingroup CLOUD_GROUP +class RAK_DLL_EXPORT CloudAllocator +{ +public: + CloudAllocator() {} + virtual ~CloudAllocator() {} + + /// \brief Allocate a row + virtual CloudQueryRow* AllocateCloudQueryRow(void); + /// \brief Free a row + virtual void DeallocateCloudQueryRow(CloudQueryRow *row); + /// \brief Allocate CloudQueryRow::data + virtual unsigned char *AllocateRowData(uint32_t bytesNeededForData); + /// \brief Free CloudQueryRow::data + virtual void DeallocateRowData(void *data); +}; + +/// Serves as a key to identify data uploaded to or queried from the server. +/// \ingroup CLOUD_GROUP +struct RAK_DLL_EXPORT CloudKey +{ + CloudKey() {} + CloudKey(RakNet::RakString _primaryKey, uint32_t _secondaryKey) : primaryKey(_primaryKey), secondaryKey(_secondaryKey) {} + ~CloudKey() {} + + /// Identifies the primary key. This is intended to be a major category, such as the name of the application + /// Must be non-empty + RakNet::RakString primaryKey; + + /// Identifies the secondary key. This is intended to be a subcategory enumeration, such as PLAYER_LIST or RUNNING_SCORES + uint32_t secondaryKey; + + /// \internal + void Serialize(bool writeToBitstream, BitStream *bitStream); +}; + +/// \internal +int CloudKeyComp(const CloudKey &key, const CloudKey &data); + +/// Data members used to query the cloud +/// \ingroup CLOUD_GROUP +struct RAK_DLL_EXPORT CloudQuery +{ + CloudQuery() {startingRowIndex=0; maxRowsToReturn=0; subscribeToResults=false;} + + /// List of keys to query. Must be at least of length 1. + /// This query is run on uploads from all clients, and those that match the combination of primaryKey and secondaryKey are potentially returned + /// If you pass more than one key at a time, the results are concatenated so if you need to differentiate between queries then send two different queries + DataStructures::List keys; + + /// If limiting the number of rows to return, this is the starting offset into the list. Has no effect unless maxRowsToReturn is > 0 + uint32_t startingRowIndex; + + /// Maximum number of rows to return. Actual number may still be less than this. Pass 0 to mean no-limit. + uint32_t maxRowsToReturn; + + /// If true, automatically get updates as the results returned to you change. Unsubscribe with CloudMemoryClient::Unsubscribe() + bool subscribeToResults; + + /// \internal + void Serialize(bool writeToBitstream, BitStream *bitStream); +}; + +/// \ingroup CLOUD_GROUP +struct RAK_DLL_EXPORT CloudQueryRow +{ + /// Key used to identify this data + CloudKey key; + + /// Data uploaded + unsigned char *data; + + /// Length of data uploaded + uint32_t length; + + /// System address of server that is holding this data, and the client is connected to + SystemAddress serverSystemAddress; + + /// System address of client that uploaded this data + SystemAddress clientSystemAddress; + + /// RakNetGUID of server that is holding this data, and the client is connected to + RakNetGUID serverGUID; + + /// RakNetGUID of client that uploaded this data + RakNetGUID clientGUID; + + /// \internal + void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator); +}; + +/// \ingroup CLOUD_GROUP +struct RAK_DLL_EXPORT CloudQueryResult +{ + /// Query originally passed to Download() + CloudQuery cloudQuery; + + /// Results returned from query. If there were multiple keys in CloudQuery::keys then see resultKeyIndices + DataStructures::List rowsReturned; + + /// If there were multiple keys in CloudQuery::keys, then each key is processed in order and the result concatenated to rowsReturned + /// The starting index of each query is written to resultKeyIndices + /// For example, if CloudQuery::keys had 4 keys, returning 3 rows, 0, rows, 5 rows, and 12 rows then + /// resultKeyIndices would be 0, 3, 3, 8 + DataStructures::List resultKeyIndices; + + + /// Whatever was passed to CloudClient::Get() as CloudQuery::subscribeToResults + bool subscribeToResults; + + /// \internal + void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator); + /// \internal + void SerializeHeader(bool writeToBitstream, BitStream *bitStream); + /// \internal + void SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream); + /// \internal + void SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator); +}; + +} // Namespace RakNet + +#endif // __CLOUD_COMMON_H + +#endif // #if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 diff --git a/project/lib_projects/raknet/jni/RaknetSources/CloudServer.cpp b/project/lib_projects/raknet/jni/RaknetSources/CloudServer.cpp index d5fc8c1..f1c7529 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CloudServer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/CloudServer.cpp @@ -1,1673 +1,1673 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudServer==1 - -#include "CloudServer.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" -#include "RakPeerInterface.h" - -enum ServerToServerCommands -{ - STSC_PROCESS_GET_REQUEST, - STSC_PROCESS_GET_RESPONSE, - STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS, - STSC_ADD_UPLOADED_KEY, - STSC_ADD_SUBSCRIBED_KEY, - STSC_REMOVE_UPLOADED_KEY, - STSC_REMOVE_SUBSCRIBED_KEY, - STSC_DATA_CHANGED, -}; - -using namespace RakNet; - -int CloudServer::RemoteServerComp(const RakNetGUID &key, RemoteServer* const &data ) -{ - if (key < data->serverAddress) - return -1; - if (key > data->serverAddress) - return 1; - return 0; -} -int CloudServer::KeySubscriberIDComp(const CloudKey &key, KeySubscriberID * const &data ) -{ - if (key.primaryKey < data->key.primaryKey) - return -1; - if (key.primaryKey > data->key.primaryKey) - return 1; - if (key.secondaryKey < data->key.secondaryKey) - return -1; - if (key.secondaryKey > data->key.secondaryKey) - return 1; - return 0; -} -int CloudServer::KeyDataPtrComp( const RakNetGUID &key, CloudData* const &data ) -{ - if (key < data->clientGUID) - return -1; - if (key > data->clientGUID) - return 1; - return 0; -} -int CloudServer::KeyDataListComp( const CloudKey &key, CloudDataList * const &data ) -{ - if (key.primaryKey < data->key.primaryKey) - return -1; - if (key.primaryKey > data->key.primaryKey) - return 1; - if (key.secondaryKey < data->key.secondaryKey) - return -1; - if (key.secondaryKey > data->key.secondaryKey) - return 1; - return 0; -} -int CloudServer::BufferedGetResponseFromServerComp(const RakNetGUID &key, CloudServer::BufferedGetResponseFromServer* const &data ) -{ - if (key < data->serverAddress) - return -1; - if (key > data->serverAddress) - return 1; - return 0; -} -int CloudServer::GetRequestComp(const uint32_t &key, CloudServer::GetRequest* const &data ) -{ - if (key < data->requestId) - return -1; - if (key > data->requestId) - return -1; - return 0; -} -void CloudServer::CloudQueryWithAddresses::Serialize(bool writeToBitstream, BitStream *bitStream) -{ - cloudQuery.Serialize(writeToBitstream, bitStream); - - if (writeToBitstream) - { - bitStream->WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - bitStream->Write(specificSystems[i]); - } - } - else - { - uint16_t specificSystemsCount; - RakNetGUID addressOrGuid; - bitStream->Read(specificSystemsCount); - for (uint16_t i=0; i < specificSystemsCount; i++) - { - bitStream->Read(addressOrGuid); - specificSystems.Push(addressOrGuid, _FILE_AND_LINE_); - } - } -} -bool CloudServer::GetRequest::AllRemoteServersHaveResponded(void) const -{ - unsigned int i; - for (i=0; i < remoteServerResponses.Size(); i++) - if (remoteServerResponses[i]->gotResult==false) - return false; - return true; -} -void CloudServer::GetRequest::Clear(CloudAllocator *allocator) -{ - unsigned int i; - for (i=0; i < remoteServerResponses.Size(); i++) - { - remoteServerResponses[i]->Clear(allocator); - RakNet::OP_DELETE(remoteServerResponses[i], _FILE_AND_LINE_); - } - remoteServerResponses.Clear(false, _FILE_AND_LINE_); -} -void CloudServer::BufferedGetResponseFromServer::Clear(CloudAllocator *allocator) -{ - unsigned int i; - for (i=0; i < queryResult.rowsReturned.Size(); i++) - { - allocator->DeallocateRowData(queryResult.rowsReturned[i]->data); - allocator->DeallocateCloudQueryRow(queryResult.rowsReturned[i]); - } - queryResult.rowsReturned.Clear(false, _FILE_AND_LINE_); -} -CloudServer::CloudServer() -{ - maxUploadBytesPerClient=0; - maxBytesPerDowload=0; - nextGetRequestId=0; - nextGetRequestsCheck=0; -} -CloudServer::~CloudServer() -{ - Clear(); -} -void CloudServer::SetMaxUploadBytesPerClient(uint64_t bytes) -{ - maxUploadBytesPerClient=bytes; -} -void CloudServer::SetMaxBytesPerDownload(uint64_t bytes) -{ - maxBytesPerDowload=bytes; -} -void CloudServer::Update(void) -{ - // Timeout getRequests - RakNet::Time time = RakNet::Time(); - if (time > nextGetRequestsCheck) - { - nextGetRequestsCheck=time+1000; - - unsigned int i=0; - while (i < getRequests.Size()) - { - if (time - getRequests[i]->requestStartTime > 3000) - { - // Remote server is not responding, just send back data with whoever did respond - ProcessAndTransmitGetRequest(getRequests[i]); - getRequests[i]->Clear(this); - RakNet::OP_DELETE(getRequests[i],_FILE_AND_LINE_); - getRequests.RemoveAtIndex(i); - } - else - { - i++; - } - } - } -} -PluginReceiveResult CloudServer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_CLOUD_POST_REQUEST: - OnPostRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_CLOUD_RELEASE_REQUEST: - OnReleaseRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_CLOUD_GET_REQUEST: - OnGetRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_CLOUD_UNSUBSCRIBE_REQUEST: - OnUnsubscribeRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_CLOUD_SERVER_TO_SERVER_COMMAND: - if (packet->length>1) - { - switch (packet->data[1]) - { - case STSC_PROCESS_GET_REQUEST: - OnServerToServerGetRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_PROCESS_GET_RESPONSE: - OnServerToServerGetResponse(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS: - OnSendUploadedAndSubscribedKeysToServer(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_ADD_UPLOADED_KEY: - OnSendUploadedKeyToServers(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_ADD_SUBSCRIBED_KEY: - OnSendSubscribedKeyToServers(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_REMOVE_UPLOADED_KEY: - OnRemoveUploadedKeyFromServers(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_REMOVE_SUBSCRIBED_KEY: - OnRemoveSubscribedKeyFromServers(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_DATA_CHANGED: - OnServerDataChanged(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - return RR_CONTINUE_PROCESSING; -} -void CloudServer::OnPostRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - CloudKey key; - key.Serialize(false,&bsIn); - uint32_t dataLengthBytes; - bsIn.Read(dataLengthBytes); - if (maxUploadBytesPerClient>0 && dataLengthBytes>maxUploadBytesPerClient) - return; // Exceeded max upload bytes - - bsIn.AlignReadToByteBoundary(); - for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) - { - if (queryFilters[filterIndex]->OnPostRequest(packet->guid, packet->systemAddress, key, dataLengthBytes, (const char*) bsIn.GetData()+BITS_TO_BYTES(bsIn.GetReadOffset()))==false) - return; - } - - unsigned char *data; - if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) - { - data = (unsigned char *) rakMalloc_Ex(dataLengthBytes,_FILE_AND_LINE_); - if (data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return; - } - bsIn.ReadAlignedBytes(data,dataLengthBytes); - } - else - data=0; - - // Add this system to remoteSystems if they aren't there already - DataStructures::HashIndex remoteSystemsHashIndex = remoteSystems.GetIndexOf(packet->guid); - RemoteCloudClient *remoteCloudClient; - if (remoteSystemsHashIndex.IsInvalid()) - { - remoteCloudClient = RakNet::OP_NEW(_FILE_AND_LINE_); - remoteCloudClient->uploadedKeys.Insert(key,key,true,_FILE_AND_LINE_); - remoteCloudClient->uploadedBytes=0; - remoteSystems.Push(packet->guid, remoteCloudClient, _FILE_AND_LINE_); - } - else - { - remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemsHashIndex); - bool objectExists; - // Add to RemoteCloudClient::uploadedKeys if it isn't there already - unsigned int uploadedKeysIndex = remoteCloudClient->uploadedKeys.GetIndexFromKey(key,&objectExists); - if (objectExists==false) - { - remoteCloudClient->uploadedKeys.InsertAtIndex(key, uploadedKeysIndex, _FILE_AND_LINE_); - } - } - - bool cloudDataAlreadyUploaded; - unsigned int dataRepositoryIndex; - bool dataRepositoryExists; - CloudDataList* cloudDataList = GetOrAllocateCloudDataList(key, &dataRepositoryExists, dataRepositoryIndex); - if (dataRepositoryExists==false) - { - cloudDataList->uploaderCount=1; - cloudDataAlreadyUploaded=false; - } - else - { - cloudDataAlreadyUploaded=cloudDataList->uploaderCount>0; - cloudDataList->uploaderCount++; - } - - CloudData *cloudData; - bool keyDataListExists; - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(packet->guid, &keyDataListExists); - if (keyDataListExists==false) - { - if (maxUploadBytesPerClient>0 && remoteCloudClient->uploadedBytes+dataLengthBytes>maxUploadBytesPerClient) - { - // Undo prior insertion of cloudDataList into cloudData if needed - if (keyDataListExists==false) - { - RakNet::OP_DELETE(cloudDataList,_FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } - - if (remoteCloudClient->IsUnused()) - { - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - remoteSystems.Remove(packet->guid, _FILE_AND_LINE_); - } - - if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) - rakFree_Ex(data, _FILE_AND_LINE_); - - return; - } - - cloudData = RakNet::OP_NEW(_FILE_AND_LINE_); - cloudData->dataLengthBytes=dataLengthBytes; - cloudData->isUploaded=true; - if (forceAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - cloudData->serverSystemAddress=forceAddress; - cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetExternalID(packet->systemAddress).GetPort()); - } - else - { - cloudData->serverSystemAddress=rakPeerInterface->GetExternalID(packet->systemAddress); - } - if (cloudData->serverSystemAddress.GetPort()==0) - { - // Fix localhost port - cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetSocket(UNASSIGNED_SYSTEM_ADDRESS)->boundAddress.GetPort()); - } - cloudData->clientSystemAddress=packet->systemAddress; - cloudData->serverGUID=rakPeerInterface->GetMyGUID(); - cloudData->clientGUID=packet->guid; - cloudDataList->keyData.Insert(packet->guid,cloudData,true,_FILE_AND_LINE_); - } - else - { - cloudData = cloudDataList->keyData[keyDataListIndex]; - - if (cloudDataAlreadyUploaded==false) - { - if (forceAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - cloudData->serverSystemAddress=forceAddress; - cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetExternalID(packet->systemAddress).GetPort()); - } - else - { - cloudData->serverSystemAddress=rakPeerInterface->GetExternalID(packet->systemAddress); - } - if (cloudData->serverSystemAddress.GetPort()==0) - { - // Fix localhost port - cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetSocket(UNASSIGNED_SYSTEM_ADDRESS)->boundAddress.GetPort()); - } - - cloudData->clientSystemAddress=packet->systemAddress; - } - - if (maxUploadBytesPerClient>0 && remoteCloudClient->uploadedBytes-cloudData->dataLengthBytes+dataLengthBytes>maxUploadBytesPerClient) - { - // Undo prior insertion of cloudDataList into cloudData if needed - if (dataRepositoryExists==false) - { - RakNet::OP_DELETE(cloudDataList,_FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } - return; - } - else - { - // Subtract already used bytes we are overwriting - remoteCloudClient->uploadedBytes-=cloudData->dataLengthBytes; - } - - if (cloudData->allocatedData!=0) - rakFree_Ex(cloudData->allocatedData,_FILE_AND_LINE_); - } - - if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) - { - // Data already allocated - cloudData->allocatedData=data; - cloudData->dataPtr=data; - } - else - { - // Read to stack - if (dataLengthBytes>0) - bsIn.ReadAlignedBytes(cloudData->stackData,dataLengthBytes); - cloudData->allocatedData=0; - cloudData->dataPtr=cloudData->stackData; - } - // Update how many bytes were written for this data - cloudData->dataLengthBytes=dataLengthBytes; - remoteCloudClient->uploadedBytes+=dataLengthBytes; - - if (cloudDataAlreadyUploaded==false) - { - // New data field - SendUploadedKeyToServers(cloudDataList->key); - } - - // Existing data field changed - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, true ); - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, true ); - - // Send update to all remote servers that subscribed to this key - NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, true); - - // I could have also subscribed to a key not yet updated locally - // This means I have to go through every RemoteClient that wants this key - // Seems like cloudData->specificSubscribers is unnecessary in that case -} -void CloudServer::OnReleaseRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - - uint16_t keyCount; - bsIn.Read(keyCount); - - if (keyCount==0) - return; - - DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(packet->guid); - if (remoteSystemIndex.IsInvalid()==true) - return; - - RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); - - CloudKey key; - - // Read all in a list first so I can run filter on it - DataStructures::List cloudKeys; - for (uint16_t keyCountIndex=0; keyCountIndex < keyCount; keyCountIndex++) - { - key.Serialize(false, &bsIn); - cloudKeys.Push(key, _FILE_AND_LINE_); - } - - for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) - { - if (queryFilters[filterIndex]->OnReleaseRequest(packet->guid, packet->systemAddress, cloudKeys)) - return; - } - - for (uint16_t keyCountIndex=0; keyCountIndex < keyCount; keyCountIndex++) - { - // Serialize in list above so I can run the filter on it - // key.Serialize(false, &bsIn); - key=cloudKeys[keyCountIndex]; - - // Remove remote systems uploaded keys - bool objectExists; - unsigned int uploadedKeysIndex = remoteCloudClient->uploadedKeys.GetIndexFromKey(key,&objectExists); - if (objectExists) - { - bool dataRepositoryExists; - unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(key, &dataRepositoryExists); - CloudDataList* cloudDataList = dataRepository[dataRepositoryIndex]; - RakAssert(cloudDataList); - - CloudData *cloudData; - bool keyDataListExists; - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(packet->guid, &keyDataListExists); - cloudData = cloudDataList->keyData[keyDataListIndex]; - - remoteCloudClient->uploadedKeys.RemoveAtIndex(uploadedKeysIndex); - remoteCloudClient->uploadedBytes-=cloudData->dataLengthBytes; - cloudDataList->uploaderCount--; - - // Broadcast destruction of this key to subscribers - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, false ); - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, false ); - NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, false ); - - cloudData->Clear(); - - if (cloudData->IsUnused()) - { - RakNet::OP_DELETE(cloudData, _FILE_AND_LINE_); - cloudDataList->keyData.RemoveAtIndex(keyDataListIndex); - if (cloudDataList->IsNotUploaded()) - { - // Tell other servers that this key is no longer uploaded, so they do not request it from us - RemoveUploadedKeyFromServers(cloudDataList->key); - } - - if (cloudDataList->IsUnused()) - { - RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } - } - - if (remoteCloudClient->IsUnused()) - { - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); - break; - } - } - } -} -void CloudServer::OnGetRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - uint16_t specificSystemsCount; - CloudKey cloudKey; - - // Create a new GetRequest - GetRequest *getRequest; - getRequest = RakNet::OP_NEW(_FILE_AND_LINE_); - getRequest->cloudQueryWithAddresses.cloudQuery.Serialize(false, &bsIn); - getRequest->requestingClient=packet->guid; - - RakNetGUID addressOrGuid; - bsIn.Read(specificSystemsCount); - for (uint16_t i=0; i < specificSystemsCount; i++) - { - bsIn.Read(addressOrGuid); - getRequest->cloudQueryWithAddresses.specificSystems.Push(addressOrGuid, _FILE_AND_LINE_); - } - - if (getRequest->cloudQueryWithAddresses.cloudQuery.keys.Size()==0) - { - RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); - return; - } - - for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) - { - if (queryFilters[filterIndex]->OnGetRequest(packet->guid, packet->systemAddress, getRequest->cloudQueryWithAddresses.cloudQuery, getRequest->cloudQueryWithAddresses.specificSystems )==false) - return; - } - - getRequest->requestStartTime=RakNet::GetTime(); - getRequest->requestId=nextGetRequestId++; - - // Send request to servers that have this data - DataStructures::List remoteServersWithData; - GetServersWithUploadedKeys(getRequest->cloudQueryWithAddresses.cloudQuery.keys, remoteServersWithData); - - if (remoteServersWithData.Size()==0) - { - ProcessAndTransmitGetRequest(getRequest); - } - else - { - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_PROCESS_GET_REQUEST); - getRequest->cloudQueryWithAddresses.Serialize(true, &bsOut); - bsOut.Write(getRequest->requestId); - - for (unsigned int remoteServerIndex=0; remoteServerIndex < remoteServersWithData.Size(); remoteServerIndex++) - { - BufferedGetResponseFromServer* bufferedGetResponseFromServer = RakNet::OP_NEW(_FILE_AND_LINE_); - bufferedGetResponseFromServer->serverAddress=remoteServersWithData[remoteServerIndex]->serverAddress; - bufferedGetResponseFromServer->gotResult=false; - getRequest->remoteServerResponses.Insert(remoteServersWithData[remoteServerIndex]->serverAddress, bufferedGetResponseFromServer, true, _FILE_AND_LINE_); - - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServersWithData[remoteServerIndex]->serverAddress, false); - } - - // Record that this system made this request - getRequests.Insert(getRequest->requestId, getRequest, true, _FILE_AND_LINE_); - } - - if (getRequest->cloudQueryWithAddresses.cloudQuery.subscribeToResults) - { - // Add to key subscription list for the client, which contains a keyId / specificUploaderList pair - DataStructures::HashIndex remoteSystemsHashIndex = remoteSystems.GetIndexOf(packet->guid); - RemoteCloudClient *remoteCloudClient; - if (remoteSystemsHashIndex.IsInvalid()) - { - remoteCloudClient = RakNet::OP_NEW(_FILE_AND_LINE_); - remoteCloudClient->uploadedBytes=0; - remoteSystems.Push(packet->guid, remoteCloudClient, _FILE_AND_LINE_); - } - else - { - remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemsHashIndex); - } - - unsigned int keyIndex; - for (keyIndex=0; keyIndex < getRequest->cloudQueryWithAddresses.cloudQuery.keys.Size(); keyIndex++) - { - cloudKey = getRequest->cloudQueryWithAddresses.cloudQuery.keys[keyIndex]; - - unsigned int keySubscriberIndex; - bool hasKeySubscriber; - keySubscriberIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudKey, &hasKeySubscriber); - KeySubscriberID* keySubscriberId; - if (hasKeySubscriber) - { - DataStructures::List specificSystems; - UnsubscribeFromKey(remoteCloudClient, packet->guid, keySubscriberIndex, cloudKey, specificSystems); - } - - keySubscriberId = RakNet::OP_NEW(_FILE_AND_LINE_); - keySubscriberId->key=cloudKey; - - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < getRequest->cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) - { - keySubscriberId->specificSystemsSubscribedTo.Insert(getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex], getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex], true, _FILE_AND_LINE_); - } - - remoteCloudClient->subscribedKeys.InsertAtIndex(keySubscriberId, keySubscriberIndex, _FILE_AND_LINE_); - - // Add CloudData in a similar way - unsigned int dataRepositoryIndex; - bool dataRepositoryExists; - CloudDataList* cloudDataList = GetOrAllocateCloudDataList(cloudKey, &dataRepositoryExists, dataRepositoryIndex); - - // If this is the first local client to subscribe to this key, call SendSubscribedKeyToServers - if (cloudDataList->subscriberCount==0) - SendSubscribedKeyToServers(cloudKey); - - // If the subscription is specific, may have to also allocate CloudData - if (getRequest->cloudQueryWithAddresses.specificSystems.Size()) - { - CloudData *cloudData; - bool keyDataListExists; - - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < getRequest->cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) - { - RakNetGUID specificSystem = getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex]; - - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(specificSystem, &keyDataListExists); - if (keyDataListExists==false) - { - cloudData = RakNet::OP_NEW(_FILE_AND_LINE_); - cloudData->dataLengthBytes=0; - cloudData->allocatedData=0; - cloudData->isUploaded=false; - cloudData->dataPtr=0; - cloudData->serverSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; - cloudData->clientSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; - cloudData->serverGUID=rakPeerInterface->GetMyGUID(); - cloudData->clientGUID=specificSystem; - cloudDataList->keyData.Insert(specificSystem,cloudData,true,_FILE_AND_LINE_); - } - else - { - cloudData = cloudDataList->keyData[keyDataListIndex]; - } - - ++cloudDataList->subscriberCount; - cloudData->specificSubscribers.Insert(packet->guid, packet->guid, true, _FILE_AND_LINE_); - } - } - else - { - ++cloudDataList->subscriberCount; - cloudDataList->nonSpecificSubscribers.Insert(packet->guid, packet->guid, true, _FILE_AND_LINE_); - - // Remove packet->guid from CloudData::specificSubscribers among all instances of cloudDataList->keyData - unsigned int subscribedKeysIndex; - bool subscribedKeysIndexExists; - subscribedKeysIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudDataList->key, &subscribedKeysIndexExists); - if (subscribedKeysIndexExists) - { - KeySubscriberID* keySubscriberId; - keySubscriberId = remoteCloudClient->subscribedKeys[subscribedKeysIndex]; - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < keySubscriberId->specificSystemsSubscribedTo.Size(); specificSystemIndex++) - { - bool keyDataExists; - unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(keySubscriberId->specificSystemsSubscribedTo[specificSystemIndex], &keyDataExists); - if (keyDataExists) - { - CloudData *keyData = cloudDataList->keyData[keyDataIndex]; - keyData->specificSubscribers.Remove(packet->guid); - --cloudDataList->subscriberCount; - } - } - } - } - } - - if (remoteCloudClient->subscribedKeys.Size()==0) - { - // Didn't do anything - remoteSystems.Remove(packet->guid, _FILE_AND_LINE_); - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - } - } - - if (remoteServersWithData.Size()==0) - RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); -} -void CloudServer::OnUnsubscribeRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - - DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(packet->guid); - if (remoteSystemIndex.IsInvalid()==true) - return; - - RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); - - uint16_t keyCount, specificSystemCount; - DataStructures::List cloudKeys; - DataStructures::List specificSystems; - uint16_t index; - - CloudKey cloudKey; - bsIn.Read(keyCount); - for (index=0; index < keyCount; index++) - { - cloudKey.Serialize(false, &bsIn); - cloudKeys.Push(cloudKey, _FILE_AND_LINE_); - } - - RakNetGUID specificSystem; - bsIn.Read(specificSystemCount); - for (index=0; index < specificSystemCount; index++) - { - bsIn.Read(specificSystem); - specificSystems.Push(specificSystem, _FILE_AND_LINE_); - } - - for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) - { - if (queryFilters[filterIndex]->OnUnsubscribeRequest(packet->guid, packet->systemAddress, cloudKeys, specificSystems )==false) - return; - } - -// CloudDataList *cloudDataList; - bool dataRepositoryExists; -// unsigned int dataRepositoryIndex; - - for (index=0; index < keyCount; index++) - { - CloudKey cloudKey = cloudKeys[index]; - - // dataRepositoryIndex = - dataRepository.GetIndexFromKey(cloudKey, &dataRepositoryExists); - if (dataRepositoryExists==false) - continue; -// cloudDataList = dataRepository[dataRepositoryIndex]; - - unsigned int keySubscriberIndex; - bool hasKeySubscriber; - keySubscriberIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudKey, &hasKeySubscriber); - - if (hasKeySubscriber==false) - continue; - - UnsubscribeFromKey(remoteCloudClient, packet->guid, keySubscriberIndex, cloudKey, specificSystems); - } - - if (remoteCloudClient->IsUnused()) - { - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); - } -} -void CloudServer::OnServerToServerGetRequest(Packet *packet) -{ -// unsigned int remoteServerIndex; - bool objectExists; - //remoteServerIndex = - remoteServers.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists==false) - return; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - CloudQueryWithAddresses cloudQueryWithAddresses; - uint32_t requestId; - cloudQueryWithAddresses.Serialize(false, &bsIn); - bsIn.Read(requestId); - - DataStructures::List cloudDataResultList; - DataStructures::List cloudKeyResultList; - ProcessCloudQueryWithAddresses(cloudQueryWithAddresses, cloudDataResultList, cloudKeyResultList); - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_PROCESS_GET_RESPONSE); - bsOut.Write(requestId); - WriteCloudQueryRowFromResultList(cloudDataResultList, cloudKeyResultList, &bsOut); - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false); -} -void CloudServer::OnServerToServerGetResponse(Packet *packet) -{ - unsigned int remoteServerIndex; - bool objectExists; - remoteServerIndex = remoteServers.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists==false) - return; - - RemoteServer *remoteServer = remoteServers[remoteServerIndex]; - if (remoteServer==0) - return; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - uint32_t requestId; - bsIn.Read(requestId); - - // Lookup request id - bool hasGetRequest; - unsigned int getRequestIndex; - getRequestIndex = getRequests.GetIndexFromKey(requestId, &hasGetRequest); - if (hasGetRequest==false) - return; - GetRequest *getRequest = getRequests[getRequestIndex]; - bool hasRemoteServer; - unsigned int remoteServerResponsesIndex; - remoteServerResponsesIndex = getRequest->remoteServerResponses.GetIndexFromKey(packet->guid, &hasRemoteServer); - if (hasRemoteServer==false) - return; - BufferedGetResponseFromServer *bufferedGetResponseFromServer; - bufferedGetResponseFromServer = getRequest->remoteServerResponses[remoteServerResponsesIndex]; - if (bufferedGetResponseFromServer->gotResult==true) - return; - bufferedGetResponseFromServer->gotResult=true; - uint32_t numRows; - bufferedGetResponseFromServer->queryResult.SerializeNumRows(false, numRows, &bsIn); - bufferedGetResponseFromServer->queryResult.SerializeCloudQueryRows(false, numRows, &bsIn, this); - - // If all results returned, then also process locally, and return to user - if (getRequest->AllRemoteServersHaveResponded()) - { - ProcessAndTransmitGetRequest(getRequest); - - getRequest->Clear(this); - RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); - - getRequests.RemoveAtIndex(getRequestIndex); - } -} -void CloudServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - - unsigned int remoteServerIndex; - bool objectExists; - remoteServerIndex = remoteServers.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists) - { - // Update remoteServerResponses by removing this server and sending the response if it is now complete - unsigned int getRequestIndex=0; - while (getRequestIndex < getRequests.Size()) - { - GetRequest *getRequest = getRequests[getRequestIndex]; - bool waitingForThisServer; - unsigned int remoteServerResponsesIndex = getRequest->remoteServerResponses.GetIndexFromKey(rakNetGUID, &waitingForThisServer); - if (waitingForThisServer) - { - getRequest->remoteServerResponses[remoteServerResponsesIndex]->Clear(this); - RakNet::OP_DELETE(getRequest->remoteServerResponses[remoteServerResponsesIndex], _FILE_AND_LINE_); - getRequest->remoteServerResponses.RemoveAtIndex(remoteServerResponsesIndex); - - if (getRequest->AllRemoteServersHaveResponded()) - { - ProcessAndTransmitGetRequest(getRequest); - getRequest->Clear(this); - RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); - - getRequests.RemoveAtIndex(getRequestIndex); - } - else - getRequestIndex++; - } - else - getRequestIndex++; - } - - RakNet::OP_DELETE(remoteServers[remoteServerIndex],_FILE_AND_LINE_); - remoteServers.RemoveAtIndex(remoteServerIndex); - } - - DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(rakNetGUID); - if (remoteSystemIndex.IsInvalid()==false) - { - RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); - unsigned int uploadedKeysIndex; - for (uploadedKeysIndex=0; uploadedKeysIndex < remoteCloudClient->uploadedKeys.Size(); uploadedKeysIndex++) - { - // Delete keys this system has uploaded - bool keyDataRepositoryExists; - unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(remoteCloudClient->uploadedKeys[uploadedKeysIndex], &keyDataRepositoryExists); - if (keyDataRepositoryExists) - { - CloudDataList* cloudDataList = dataRepository[dataRepositoryIndex]; - bool keyDataExists; - unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(rakNetGUID, &keyDataExists); - if (keyDataExists) - { - CloudData *cloudData = cloudDataList->keyData[keyDataIndex]; - cloudDataList->uploaderCount--; - - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, false ); - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, false ); - NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, false ); - - cloudData->Clear(); - - if (cloudData->IsUnused()) - { - RakNet::OP_DELETE(cloudData,_FILE_AND_LINE_); - cloudDataList->keyData.RemoveAtIndex(keyDataIndex); - - if (cloudDataList->IsNotUploaded()) - { - // Tell other servers that this key is no longer uploaded, so they do not request it from us - RemoveUploadedKeyFromServers(cloudDataList->key); - } - - if (cloudDataList->IsUnused()) - { - // Tell other servers that this key is no longer uploaded, so they do not request it from us - RemoveUploadedKeyFromServers(cloudDataList->key); - - RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } - } - } - } - } - - unsigned int subscribedKeysIndex; - for (subscribedKeysIndex=0; subscribedKeysIndex < remoteCloudClient->subscribedKeys.Size(); subscribedKeysIndex++) - { - KeySubscriberID* keySubscriberId; - keySubscriberId = remoteCloudClient->subscribedKeys[subscribedKeysIndex]; - - bool keyDataRepositoryExists; - unsigned int keyDataRepositoryIndex = dataRepository.GetIndexFromKey(remoteCloudClient->subscribedKeys[subscribedKeysIndex]->key, &keyDataRepositoryExists); - if (keyDataRepositoryExists) - { - CloudDataList* cloudDataList = dataRepository[keyDataRepositoryIndex]; - if (keySubscriberId->specificSystemsSubscribedTo.Size()==0) - { - cloudDataList->nonSpecificSubscribers.Remove(rakNetGUID); - --cloudDataList->subscriberCount; - } - else - { - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < keySubscriberId->specificSystemsSubscribedTo.Size(); specificSystemIndex++) - { - bool keyDataExists; - unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(keySubscriberId->specificSystemsSubscribedTo[specificSystemIndex], &keyDataExists); - if (keyDataExists) - { - CloudData *keyData = cloudDataList->keyData[keyDataIndex]; - keyData->specificSubscribers.Remove(rakNetGUID); - --cloudDataList->subscriberCount; - } - } - } - } - - RakNet::OP_DELETE(keySubscriberId, _FILE_AND_LINE_); - } - - // Delete and remove from remoteSystems - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); - } -} -void CloudServer::OnRakPeerShutdown(void) -{ - Clear(); -} -void CloudServer::Clear(void) -{ - unsigned int i,j; - for (i=0; i < dataRepository.Size(); i++) - { - CloudDataList *cloudDataList = dataRepository[i]; - for (j=0; j < cloudDataList->keyData.Size(); j++) - { - cloudDataList->keyData[j]->Clear(); - RakNet::OP_DELETE(cloudDataList->keyData[j], _FILE_AND_LINE_); - } - RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); - } - dataRepository.Clear(false, _FILE_AND_LINE_); - - for (i=0; i < remoteServers.Size(); i++) - { - RakNet::OP_DELETE(remoteServers[i], _FILE_AND_LINE_); - } - remoteServers.Clear(false, _FILE_AND_LINE_); - - for (i=0; i < getRequests.Size(); i++) - { - GetRequest *getRequest = getRequests[i]; - getRequest->Clear(this); - RakNet::OP_DELETE(getRequests[i], _FILE_AND_LINE_); - } - getRequests.Clear(false, _FILE_AND_LINE_); - - DataStructures::List keyList; - DataStructures::List itemList; - remoteSystems.GetAsList(itemList, keyList, _FILE_AND_LINE_); - for (i=0; i < itemList.Size(); i++) - { - RemoteCloudClient* remoteCloudClient = itemList[i]; - for (j=0; j < remoteCloudClient->subscribedKeys.Size(); j++) - { - RakNet::OP_DELETE(remoteCloudClient->subscribedKeys[j], _FILE_AND_LINE_); - } - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - } - remoteSystems.Clear(_FILE_AND_LINE_); -} -void CloudServer::WriteCloudQueryRowFromResultList(DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut) -{ - bsOut->WriteCasted(cloudKeyResultList.Size()); - unsigned int i; - for (i=0; i < cloudKeyResultList.Size(); i++) - { - WriteCloudQueryRowFromResultList(i, cloudDataResultList, cloudKeyResultList, bsOut); - } -} -void CloudServer::WriteCloudQueryRowFromResultList(unsigned int i, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut) -{ - CloudQueryRow cloudQueryRow; - CloudData *cloudData = cloudDataResultList[i]; - cloudQueryRow.key=cloudKeyResultList[i]; - cloudQueryRow.data=cloudData->dataPtr; - cloudQueryRow.length=cloudData->dataLengthBytes; - cloudQueryRow.serverSystemAddress=cloudData->serverSystemAddress; - cloudQueryRow.clientSystemAddress=cloudData->clientSystemAddress; - cloudQueryRow.serverGUID=cloudData->serverGUID; - cloudQueryRow.clientGUID=cloudData->clientGUID; - cloudQueryRow.Serialize(true, bsOut, 0); -} -void CloudServer::NotifyClientSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, DataStructures::OrderedList &subscribers, bool wasUpdated ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID) ID_CLOUD_SUBSCRIPTION_NOTIFICATION); - bsOut.Write(wasUpdated); - CloudQueryRow row; - row.key=key; - row.data=cloudData->dataPtr; - row.length=cloudData->dataLengthBytes; - row.serverSystemAddress=cloudData->serverSystemAddress; - row.clientSystemAddress=cloudData->clientSystemAddress; - row.serverGUID=cloudData->serverGUID; - row.clientGUID=cloudData->clientGUID; - row.Serialize(true,&bsOut,0); - - unsigned int i; - for (i=0; i < subscribers.Size(); i++) - { - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, subscribers[i], false); - } -} -void CloudServer::NotifyClientSubscribersOfDataChange( CloudQueryRow *row, DataStructures::OrderedList &subscribers, bool wasUpdated ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID) ID_CLOUD_SUBSCRIPTION_NOTIFICATION); - bsOut.Write(wasUpdated); - row->Serialize(true,&bsOut,0); - - unsigned int i; - for (i=0; i < subscribers.Size(); i++) - { - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, subscribers[i], false); - } -} -void CloudServer::NotifyServerSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, bool wasUpdated ) -{ - // Find every server that has subscribed - // Send them change notifications - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_DATA_CHANGED); - bsOut.Write(wasUpdated); - CloudQueryRow row; - row.key=key; - row.data=cloudData->dataPtr; - row.length=cloudData->dataLengthBytes; - row.serverSystemAddress=cloudData->serverSystemAddress; - row.clientSystemAddress=cloudData->clientSystemAddress; - row.serverGUID=cloudData->serverGUID; - row.clientGUID=cloudData->clientGUID; - row.Serialize(true,&bsOut,0); - - unsigned int i; - for (i=0; i < remoteServers.Size(); i++) - { - if (remoteServers[i]->gotSubscribedAndUploadedKeys==false || remoteServers[i]->subscribedKeys.HasData(key)) - { - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); - } - } -} -void CloudServer::AddServer(RakNetGUID systemIdentifier) -{ - ConnectionState cs = rakPeerInterface->GetConnectionState(systemIdentifier); - if (cs==IS_DISCONNECTED || cs==IS_NOT_CONNECTED) - return; - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(systemIdentifier,&objectExists); - if (objectExists==false) - { - RemoteServer *remoteServer = RakNet::OP_NEW(_FILE_AND_LINE_); - remoteServer->gotSubscribedAndUploadedKeys=false; - remoteServer->serverAddress=systemIdentifier; - remoteServers.InsertAtIndex(remoteServer, index, _FILE_AND_LINE_); - - SendUploadedAndSubscribedKeysToServer(systemIdentifier); - } -} -void CloudServer::RemoveServer(RakNetGUID systemAddress) -{ - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(systemAddress,&objectExists); - if (objectExists==true) - { - RakNet::OP_DELETE(remoteServers[index],_FILE_AND_LINE_); - remoteServers.RemoveAtIndex(index); - } -} -void CloudServer::GetRemoteServers(DataStructures::List &remoteServersOut) -{ - remoteServersOut.Clear(true, _FILE_AND_LINE_); - - unsigned int i; - for (i=0; i < remoteServers.Size(); i++) - { - remoteServersOut.Push(remoteServers[i]->serverAddress, _FILE_AND_LINE_); - } -} -void CloudServer::ProcessAndTransmitGetRequest(GetRequest *getRequest) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID) ID_CLOUD_GET_RESPONSE); - - // BufferedGetResponseFromServer getResponse; - CloudQueryResult cloudQueryResult; - cloudQueryResult.cloudQuery=getRequest->cloudQueryWithAddresses.cloudQuery; - cloudQueryResult.subscribeToResults=getRequest->cloudQueryWithAddresses.cloudQuery.subscribeToResults; - cloudQueryResult.SerializeHeader(true, &bsOut); - - DataStructures::List cloudDataResultList; - DataStructures::List cloudKeyResultList; - ProcessCloudQueryWithAddresses(getRequest->cloudQueryWithAddresses, cloudDataResultList, cloudKeyResultList); - bool unlimitedRows=getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn==0; - - uint32_t localNumRows = (uint32_t) cloudDataResultList.Size(); - if (unlimitedRows==false && - localNumRows > getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex && - localNumRows - getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex > getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn ) - localNumRows=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex + getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn; - - BitSize_t bitStreamOffset = bsOut.GetWriteOffset(); - uint32_t localRowsToWrite; - unsigned int skipRows; - if (localNumRows>getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex) - { - localRowsToWrite=localNumRows-getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex; - skipRows=0; - } - else - { - localRowsToWrite=0; - skipRows=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex-localNumRows; - } - cloudQueryResult.SerializeNumRows(true, localRowsToWrite, &bsOut); - for (unsigned int i=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex; i < localNumRows; i++) - { - WriteCloudQueryRowFromResultList(i, cloudDataResultList, cloudKeyResultList, &bsOut); - } - - // Append remote systems for remaining rows - if (unlimitedRows==true || getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn>localRowsToWrite) - { - uint32_t remainingRows=0; - uint32_t additionalRowsWritten=0; - if (unlimitedRows==false) - remainingRows=getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn-localRowsToWrite; - - unsigned int remoteServerResponseIndex; - for (remoteServerResponseIndex=0; remoteServerResponseIndex < getRequest->remoteServerResponses.Size(); remoteServerResponseIndex++) - { - BufferedGetResponseFromServer *bufferedGetResponseFromServer = getRequest->remoteServerResponses[remoteServerResponseIndex]; - unsigned int cloudQueryRowIndex; - for (cloudQueryRowIndex=0; cloudQueryRowIndex < bufferedGetResponseFromServer->queryResult.rowsReturned.Size(); cloudQueryRowIndex++) - { - if (skipRows>0) - { - --skipRows; - continue; - } - bufferedGetResponseFromServer->queryResult.rowsReturned[cloudQueryRowIndex]->Serialize(true, &bsOut, this); - - ++additionalRowsWritten; - if (unlimitedRows==false && --remainingRows==0) - break; - } - - if (unlimitedRows==false && remainingRows==0) - break; - } - - if (additionalRowsWritten>0) - { - BitSize_t curOffset = bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(bitStreamOffset); - localRowsToWrite+=additionalRowsWritten; - cloudQueryResult.SerializeNumRows(true, localRowsToWrite, &bsOut); - bsOut.SetWriteOffset(curOffset); - } - } - - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, getRequest->requestingClient, false); -} -void CloudServer::ProcessCloudQueryWithAddresses( CloudServer::CloudQueryWithAddresses &cloudQueryWithAddresses, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList ) -{ - CloudQueryResult cloudQueryResult; - CloudQueryRow cloudQueryRow; - unsigned int queryIndex; - bool dataRepositoryExists; - CloudDataList* cloudDataList; - unsigned int keyDataIndex; - - // If specificSystems list empty, applies to all systems - // For each of keys in cloudQueryWithAddresses, return that data, limited by maxRowsToReturn - for (queryIndex=0; queryIndex < cloudQueryWithAddresses.cloudQuery.keys.Size(); queryIndex++) - { - const CloudKey &key = cloudQueryWithAddresses.cloudQuery.keys[queryIndex]; - - unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(key, &dataRepositoryExists); - if (dataRepositoryExists) - { - cloudDataList=dataRepository[dataRepositoryIndex]; - - if (cloudDataList->uploaderCount>0) - { - // Return all keyData that was uploaded by specificSystems, or all if not specified - if (cloudQueryWithAddresses.specificSystems.Size()>0) - { - // Return data for matching systems - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) - { - bool uploaderExists; - keyDataIndex = cloudDataList->keyData.GetIndexFromKey(cloudQueryWithAddresses.specificSystems[specificSystemIndex], &uploaderExists); - if (uploaderExists) - { - cloudDataResultList.Push(cloudDataList->keyData[keyDataIndex], _FILE_AND_LINE_); - cloudKeyResultList.Push(key, _FILE_AND_LINE_); - } - } - } - else - { - // Return data for all systems - for (keyDataIndex=0; keyDataIndex < cloudDataList->keyData.Size(); keyDataIndex++) - { - cloudDataResultList.Push(cloudDataList->keyData[keyDataIndex], _FILE_AND_LINE_); - cloudKeyResultList.Push(key, _FILE_AND_LINE_); - } - } - } - } - } -} -void CloudServer::SendUploadedAndSubscribedKeysToServer( RakNetGUID systemAddress ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS); - bsOut.WriteCasted(dataRepository.Size()); - for (unsigned int i=0; i < dataRepository.Size(); i++) - dataRepository[i]->key.Serialize(true, &bsOut); - - BitSize_t startOffset, endOffset; - uint16_t subscribedKeyCount=0; - startOffset=bsOut.GetWriteOffset(); - bsOut.WriteCasted(subscribedKeyCount); - for (unsigned int i=0; i < dataRepository.Size(); i++) - { - if (dataRepository[i]->subscriberCount>0) - { - dataRepository[i]->key.Serialize(true, &bsOut); - subscribedKeyCount++; - } - } - endOffset=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(startOffset); - bsOut.WriteCasted(subscribedKeyCount); - bsOut.SetWriteOffset(endOffset); - - if (dataRepository.Size()>0 || subscribedKeyCount>0) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false); -} -void CloudServer::SendUploadedKeyToServers( CloudKey &cloudKey ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_ADD_UPLOADED_KEY); - cloudKey.Serialize(true, &bsOut); - for (unsigned int i=0; i < remoteServers.Size(); i++) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); -} -void CloudServer::SendSubscribedKeyToServers( CloudKey &cloudKey ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_ADD_SUBSCRIBED_KEY); - cloudKey.Serialize(true, &bsOut); - for (unsigned int i=0; i < remoteServers.Size(); i++) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); -} -void CloudServer::RemoveUploadedKeyFromServers( CloudKey &cloudKey ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_REMOVE_UPLOADED_KEY); - cloudKey.Serialize(true, &bsOut); - for (unsigned int i=0; i < remoteServers.Size(); i++) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); -} -void CloudServer::RemoveSubscribedKeyFromServers( CloudKey &cloudKey ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_REMOVE_SUBSCRIBED_KEY); - cloudKey.Serialize(true, &bsOut); - for (unsigned int i=0; i < remoteServers.Size(); i++) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); -} -void CloudServer::OnSendUploadedAndSubscribedKeysToServer( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - remoteServer->gotSubscribedAndUploadedKeys=true; - -// unsigned int insertionIndex; - bool alreadyHasKey; - uint16_t numUploadedKeys, numSubscribedKeys; - bsIn.Read(numUploadedKeys); - for (uint16_t i=0; i < numUploadedKeys; i++) - { - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); - - // insertionIndex = - remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==false) - remoteServer->uploadedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); - } - - bsIn.Read(numSubscribedKeys); - for (uint16_t i=0; i < numSubscribedKeys; i++) - { - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); - - //insertionIndex = - remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==false) - remoteServer->subscribedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); - } - - // Potential todo - join servers - // For each uploaded key that we subscribe to, query it - // For each subscribed key that we have, send it -} -void CloudServer::OnSendUploadedKeyToServers( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); -// unsigned int insertionIndex; - bool alreadyHasKey; -// insertionIndex = - remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==false) - remoteServer->uploadedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); -} -void CloudServer::OnSendSubscribedKeyToServers( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); -// unsigned int insertionIndex; - bool alreadyHasKey; -// insertionIndex = - remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - - // Do not need to send current values, the Get request will do that as the Get request is sent at the same time - if (alreadyHasKey==false) - remoteServer->subscribedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); -} -void CloudServer::OnRemoveUploadedKeyFromServers( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); - unsigned int insertionIndex; - bool alreadyHasKey; - insertionIndex = remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==true) - remoteServer->uploadedKeys.RemoveAtIndex(insertionIndex); -} -void CloudServer::OnRemoveSubscribedKeyFromServers( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); - unsigned int insertionIndex; - bool alreadyHasKey; - insertionIndex = remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==true) - remoteServer->subscribedKeys.RemoveAtIndex(insertionIndex); -} -void CloudServer::OnServerDataChanged( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - - // Find everyone that cares about this change and relay - bool wasUpdated=false; - bsIn.Read(wasUpdated); - CloudQueryRow row; - row.Serialize(false, &bsIn, this); - - CloudDataList *cloudDataList; - bool dataRepositoryExists; - unsigned int dataRepositoryIndex; - dataRepositoryIndex = dataRepository.GetIndexFromKey(row.key, &dataRepositoryExists); - if (dataRepositoryExists==false) - { - DeallocateRowData(row.data); - return; - } - cloudDataList = dataRepository[dataRepositoryIndex]; - CloudData *cloudData; - bool keyDataListExists; - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(row.clientGUID, &keyDataListExists); - if (keyDataListExists==true) - { - cloudData = cloudDataList->keyData[keyDataListIndex]; - NotifyClientSubscribersOfDataChange(&row, cloudData->specificSubscribers, wasUpdated ); - } - - NotifyClientSubscribersOfDataChange(&row, cloudDataList->nonSpecificSubscribers, wasUpdated ); - DeallocateRowData(row.data); -} -void CloudServer::GetServersWithUploadedKeys( - DataStructures::List &keys, - DataStructures::List &remoteServersWithData - ) -{ - remoteServersWithData.Clear(true, _FILE_AND_LINE_); - - unsigned int i,j; - for (i=0; i < remoteServers.Size(); i++) - { - remoteServers[i]->workingFlag=false; - } - - for (i=0; i < remoteServers.Size(); i++) - { - if (remoteServers[i]->workingFlag==false) - { - if (remoteServers[i]->gotSubscribedAndUploadedKeys==false) - { - remoteServers[i]->workingFlag=true; - remoteServersWithData.Push(remoteServers[i], _FILE_AND_LINE_); - } - else - { - remoteServers[i]->workingFlag=false; - for (j=0; j < keys.Size(); j++) - { - if (remoteServers[i]->workingFlag==false && remoteServers[i]->uploadedKeys.HasData(keys[j])) - { - remoteServers[i]->workingFlag=true; - remoteServersWithData.Push(remoteServers[i], _FILE_AND_LINE_); - break; - } - } - } - } - } -} - -CloudServer::CloudDataList *CloudServer::GetOrAllocateCloudDataList(CloudKey key, bool *dataRepositoryExists, unsigned int &dataRepositoryIndex) -{ - CloudDataList *cloudDataList; - - dataRepositoryIndex = dataRepository.GetIndexFromKey(key, dataRepositoryExists); - if (*dataRepositoryExists==false) - { - cloudDataList = RakNet::OP_NEW(_FILE_AND_LINE_); - cloudDataList->key=key; - cloudDataList->uploaderCount=0; - cloudDataList->subscriberCount=0; - dataRepository.InsertAtIndex(cloudDataList,dataRepositoryIndex,_FILE_AND_LINE_); - } - else - { - cloudDataList = dataRepository[dataRepositoryIndex]; - } - - return cloudDataList; -} - -void CloudServer::UnsubscribeFromKey(RemoteCloudClient *remoteCloudClient, RakNetGUID remoteCloudClientGuid, unsigned int keySubscriberIndex, CloudKey &cloudKey, DataStructures::List &specificSystems) -{ - KeySubscriberID* keySubscriberId = remoteCloudClient->subscribedKeys[keySubscriberIndex]; - - // If removing specific systems, but global subscription, fail - if (keySubscriberId->specificSystemsSubscribedTo.Size()==0 && specificSystems.Size()>0) - return; - - bool dataRepositoryExists; - CloudDataList *cloudDataList; - unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(cloudKey, &dataRepositoryExists); - if (dataRepositoryExists==false) - return; - - unsigned int i,j; - - cloudDataList = dataRepository[dataRepositoryIndex]; - if (specificSystems.Size()==0) - { - // Remove global subscriber. If returns false, have to remove specific subscribers - if (cloudDataList->RemoveSubscriber(remoteCloudClientGuid)==false) - { - for (i=0; i < keySubscriberId->specificSystemsSubscribedTo.Size(); i++) - { - RemoveSpecificSubscriber(keySubscriberId->specificSystemsSubscribedTo[i], cloudDataList, remoteCloudClientGuid); - } - } - keySubscriberId->specificSystemsSubscribedTo.Clear(true, _FILE_AND_LINE_); - } - else - { - for (j=0; j < specificSystems.Size(); j++) - { - unsigned int specificSystemsSubscribedToIndex; - bool hasSpecificSystemsSubscribedTo; - specificSystemsSubscribedToIndex=keySubscriberId->specificSystemsSubscribedTo.GetIndexFromKey(specificSystems[j], &hasSpecificSystemsSubscribedTo); - if (hasSpecificSystemsSubscribedTo) - { - RemoveSpecificSubscriber(specificSystems[j], cloudDataList, remoteCloudClientGuid); - keySubscriberId->specificSystemsSubscribedTo.RemoveAtIndex(specificSystemsSubscribedToIndex); - } - } - } - - if (keySubscriberId->specificSystemsSubscribedTo.Size()==0) - { - RakNet::OP_DELETE(keySubscriberId, _FILE_AND_LINE_); - remoteCloudClient->subscribedKeys.RemoveAtIndex(keySubscriberIndex); - } - - if (cloudDataList->subscriberCount==0) - RemoveSubscribedKeyFromServers(cloudKey); - - if (cloudDataList->IsUnused()) - { - RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } -} -void CloudServer::RemoveSpecificSubscriber(RakNetGUID specificSubscriber, CloudDataList *cloudDataList, RakNetGUID remoteCloudClientGuid) -{ - bool keyDataListExists; - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(specificSubscriber, &keyDataListExists); - if (keyDataListExists==false) - return; - CloudData *cloudData = cloudDataList->keyData[keyDataListIndex]; - bool hasSpecificSubscriber; - unsigned int specificSubscriberIndex = cloudData->specificSubscribers.GetIndexFromKey(remoteCloudClientGuid, &hasSpecificSubscriber); - if (hasSpecificSubscriber) - { - cloudData->specificSubscribers.RemoveAtIndex(specificSubscriberIndex); - cloudDataList->subscriberCount--; - - if (cloudData->IsUnused()) - { - RakNet::OP_DELETE(cloudData, _FILE_AND_LINE_); - cloudDataList->keyData.RemoveAtIndex(keyDataListIndex); - } - } -} - -void CloudServer::ForceExternalSystemAddress(SystemAddress forcedAddress) -{ - forceAddress=forcedAddress; -} -void CloudServer::AddQueryFilter(CloudServerQueryFilter* filter) -{ - if (queryFilters.GetIndexOf(filter)!=(unsigned int) -1) - return; - queryFilters.Push(filter, _FILE_AND_LINE_); -} -void CloudServer::RemoveQueryFilter(CloudServerQueryFilter* filter) -{ - unsigned int index; - index = queryFilters.GetIndexOf(filter); - if (index != (unsigned int) -1) - queryFilters.RemoveAtIndex(index); -} -void CloudServer::RemoveAllQueryFilters(void) -{ - queryFilters.Clear(true, _FILE_AND_LINE_); -} - -#endif +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudServer==1 + +#include "CloudServer.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" +#include "RakPeerInterface.h" + +enum ServerToServerCommands +{ + STSC_PROCESS_GET_REQUEST, + STSC_PROCESS_GET_RESPONSE, + STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS, + STSC_ADD_UPLOADED_KEY, + STSC_ADD_SUBSCRIBED_KEY, + STSC_REMOVE_UPLOADED_KEY, + STSC_REMOVE_SUBSCRIBED_KEY, + STSC_DATA_CHANGED, +}; + +using namespace RakNet; + +int CloudServer::RemoteServerComp(const RakNetGUID &key, RemoteServer* const &data ) +{ + if (key < data->serverAddress) + return -1; + if (key > data->serverAddress) + return 1; + return 0; +} +int CloudServer::KeySubscriberIDComp(const CloudKey &key, KeySubscriberID * const &data ) +{ + if (key.primaryKey < data->key.primaryKey) + return -1; + if (key.primaryKey > data->key.primaryKey) + return 1; + if (key.secondaryKey < data->key.secondaryKey) + return -1; + if (key.secondaryKey > data->key.secondaryKey) + return 1; + return 0; +} +int CloudServer::KeyDataPtrComp( const RakNetGUID &key, CloudData* const &data ) +{ + if (key < data->clientGUID) + return -1; + if (key > data->clientGUID) + return 1; + return 0; +} +int CloudServer::KeyDataListComp( const CloudKey &key, CloudDataList * const &data ) +{ + if (key.primaryKey < data->key.primaryKey) + return -1; + if (key.primaryKey > data->key.primaryKey) + return 1; + if (key.secondaryKey < data->key.secondaryKey) + return -1; + if (key.secondaryKey > data->key.secondaryKey) + return 1; + return 0; +} +int CloudServer::BufferedGetResponseFromServerComp(const RakNetGUID &key, CloudServer::BufferedGetResponseFromServer* const &data ) +{ + if (key < data->serverAddress) + return -1; + if (key > data->serverAddress) + return 1; + return 0; +} +int CloudServer::GetRequestComp(const uint32_t &key, CloudServer::GetRequest* const &data ) +{ + if (key < data->requestId) + return -1; + if (key > data->requestId) + return -1; + return 0; +} +void CloudServer::CloudQueryWithAddresses::Serialize(bool writeToBitstream, BitStream *bitStream) +{ + cloudQuery.Serialize(writeToBitstream, bitStream); + + if (writeToBitstream) + { + bitStream->WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + bitStream->Write(specificSystems[i]); + } + } + else + { + uint16_t specificSystemsCount; + RakNetGUID addressOrGuid; + bitStream->Read(specificSystemsCount); + for (uint16_t i=0; i < specificSystemsCount; i++) + { + bitStream->Read(addressOrGuid); + specificSystems.Push(addressOrGuid, _FILE_AND_LINE_); + } + } +} +bool CloudServer::GetRequest::AllRemoteServersHaveResponded(void) const +{ + unsigned int i; + for (i=0; i < remoteServerResponses.Size(); i++) + if (remoteServerResponses[i]->gotResult==false) + return false; + return true; +} +void CloudServer::GetRequest::Clear(CloudAllocator *allocator) +{ + unsigned int i; + for (i=0; i < remoteServerResponses.Size(); i++) + { + remoteServerResponses[i]->Clear(allocator); + RakNet::OP_DELETE(remoteServerResponses[i], _FILE_AND_LINE_); + } + remoteServerResponses.Clear(false, _FILE_AND_LINE_); +} +void CloudServer::BufferedGetResponseFromServer::Clear(CloudAllocator *allocator) +{ + unsigned int i; + for (i=0; i < queryResult.rowsReturned.Size(); i++) + { + allocator->DeallocateRowData(queryResult.rowsReturned[i]->data); + allocator->DeallocateCloudQueryRow(queryResult.rowsReturned[i]); + } + queryResult.rowsReturned.Clear(false, _FILE_AND_LINE_); +} +CloudServer::CloudServer() +{ + maxUploadBytesPerClient=0; + maxBytesPerDowload=0; + nextGetRequestId=0; + nextGetRequestsCheck=0; +} +CloudServer::~CloudServer() +{ + Clear(); +} +void CloudServer::SetMaxUploadBytesPerClient(uint64_t bytes) +{ + maxUploadBytesPerClient=bytes; +} +void CloudServer::SetMaxBytesPerDownload(uint64_t bytes) +{ + maxBytesPerDowload=bytes; +} +void CloudServer::Update(void) +{ + // Timeout getRequests + RakNet::Time time = RakNet::Time(); + if (time > nextGetRequestsCheck) + { + nextGetRequestsCheck=time+1000; + + unsigned int i=0; + while (i < getRequests.Size()) + { + if (time - getRequests[i]->requestStartTime > 3000) + { + // Remote server is not responding, just send back data with whoever did respond + ProcessAndTransmitGetRequest(getRequests[i]); + getRequests[i]->Clear(this); + RakNet::OP_DELETE(getRequests[i],_FILE_AND_LINE_); + getRequests.RemoveAtIndex(i); + } + else + { + i++; + } + } + } +} +PluginReceiveResult CloudServer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_CLOUD_POST_REQUEST: + OnPostRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_CLOUD_RELEASE_REQUEST: + OnReleaseRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_CLOUD_GET_REQUEST: + OnGetRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_CLOUD_UNSUBSCRIBE_REQUEST: + OnUnsubscribeRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_CLOUD_SERVER_TO_SERVER_COMMAND: + if (packet->length>1) + { + switch (packet->data[1]) + { + case STSC_PROCESS_GET_REQUEST: + OnServerToServerGetRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_PROCESS_GET_RESPONSE: + OnServerToServerGetResponse(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS: + OnSendUploadedAndSubscribedKeysToServer(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_ADD_UPLOADED_KEY: + OnSendUploadedKeyToServers(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_ADD_SUBSCRIBED_KEY: + OnSendSubscribedKeyToServers(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_REMOVE_UPLOADED_KEY: + OnRemoveUploadedKeyFromServers(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_REMOVE_SUBSCRIBED_KEY: + OnRemoveSubscribedKeyFromServers(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_DATA_CHANGED: + OnServerDataChanged(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + return RR_CONTINUE_PROCESSING; +} +void CloudServer::OnPostRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + CloudKey key; + key.Serialize(false,&bsIn); + uint32_t dataLengthBytes; + bsIn.Read(dataLengthBytes); + if (maxUploadBytesPerClient>0 && dataLengthBytes>maxUploadBytesPerClient) + return; // Exceeded max upload bytes + + bsIn.AlignReadToByteBoundary(); + for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) + { + if (queryFilters[filterIndex]->OnPostRequest(packet->guid, packet->systemAddress, key, dataLengthBytes, (const char*) bsIn.GetData()+BITS_TO_BYTES(bsIn.GetReadOffset()))==false) + return; + } + + unsigned char *data; + if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) + { + data = (unsigned char *) rakMalloc_Ex(dataLengthBytes,_FILE_AND_LINE_); + if (data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return; + } + bsIn.ReadAlignedBytes(data,dataLengthBytes); + } + else + data=0; + + // Add this system to remoteSystems if they aren't there already + DataStructures::HashIndex remoteSystemsHashIndex = remoteSystems.GetIndexOf(packet->guid); + RemoteCloudClient *remoteCloudClient; + if (remoteSystemsHashIndex.IsInvalid()) + { + remoteCloudClient = RakNet::OP_NEW(_FILE_AND_LINE_); + remoteCloudClient->uploadedKeys.Insert(key,key,true,_FILE_AND_LINE_); + remoteCloudClient->uploadedBytes=0; + remoteSystems.Push(packet->guid, remoteCloudClient, _FILE_AND_LINE_); + } + else + { + remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemsHashIndex); + bool objectExists; + // Add to RemoteCloudClient::uploadedKeys if it isn't there already + unsigned int uploadedKeysIndex = remoteCloudClient->uploadedKeys.GetIndexFromKey(key,&objectExists); + if (objectExists==false) + { + remoteCloudClient->uploadedKeys.InsertAtIndex(key, uploadedKeysIndex, _FILE_AND_LINE_); + } + } + + bool cloudDataAlreadyUploaded; + unsigned int dataRepositoryIndex; + bool dataRepositoryExists; + CloudDataList* cloudDataList = GetOrAllocateCloudDataList(key, &dataRepositoryExists, dataRepositoryIndex); + if (dataRepositoryExists==false) + { + cloudDataList->uploaderCount=1; + cloudDataAlreadyUploaded=false; + } + else + { + cloudDataAlreadyUploaded=cloudDataList->uploaderCount>0; + cloudDataList->uploaderCount++; + } + + CloudData *cloudData; + bool keyDataListExists; + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(packet->guid, &keyDataListExists); + if (keyDataListExists==false) + { + if (maxUploadBytesPerClient>0 && remoteCloudClient->uploadedBytes+dataLengthBytes>maxUploadBytesPerClient) + { + // Undo prior insertion of cloudDataList into cloudData if needed + if (keyDataListExists==false) + { + RakNet::OP_DELETE(cloudDataList,_FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } + + if (remoteCloudClient->IsUnused()) + { + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + remoteSystems.Remove(packet->guid, _FILE_AND_LINE_); + } + + if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) + rakFree_Ex(data, _FILE_AND_LINE_); + + return; + } + + cloudData = RakNet::OP_NEW(_FILE_AND_LINE_); + cloudData->dataLengthBytes=dataLengthBytes; + cloudData->isUploaded=true; + if (forceAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + cloudData->serverSystemAddress=forceAddress; + cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetExternalID(packet->systemAddress).GetPort()); + } + else + { + cloudData->serverSystemAddress=rakPeerInterface->GetExternalID(packet->systemAddress); + } + if (cloudData->serverSystemAddress.GetPort()==0) + { + // Fix localhost port + cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetSocket(UNASSIGNED_SYSTEM_ADDRESS)->boundAddress.GetPort()); + } + cloudData->clientSystemAddress=packet->systemAddress; + cloudData->serverGUID=rakPeerInterface->GetMyGUID(); + cloudData->clientGUID=packet->guid; + cloudDataList->keyData.Insert(packet->guid,cloudData,true,_FILE_AND_LINE_); + } + else + { + cloudData = cloudDataList->keyData[keyDataListIndex]; + + if (cloudDataAlreadyUploaded==false) + { + if (forceAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + cloudData->serverSystemAddress=forceAddress; + cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetExternalID(packet->systemAddress).GetPort()); + } + else + { + cloudData->serverSystemAddress=rakPeerInterface->GetExternalID(packet->systemAddress); + } + if (cloudData->serverSystemAddress.GetPort()==0) + { + // Fix localhost port + cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetSocket(UNASSIGNED_SYSTEM_ADDRESS)->boundAddress.GetPort()); + } + + cloudData->clientSystemAddress=packet->systemAddress; + } + + if (maxUploadBytesPerClient>0 && remoteCloudClient->uploadedBytes-cloudData->dataLengthBytes+dataLengthBytes>maxUploadBytesPerClient) + { + // Undo prior insertion of cloudDataList into cloudData if needed + if (dataRepositoryExists==false) + { + RakNet::OP_DELETE(cloudDataList,_FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } + return; + } + else + { + // Subtract already used bytes we are overwriting + remoteCloudClient->uploadedBytes-=cloudData->dataLengthBytes; + } + + if (cloudData->allocatedData!=0) + rakFree_Ex(cloudData->allocatedData,_FILE_AND_LINE_); + } + + if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) + { + // Data already allocated + cloudData->allocatedData=data; + cloudData->dataPtr=data; + } + else + { + // Read to stack + if (dataLengthBytes>0) + bsIn.ReadAlignedBytes(cloudData->stackData,dataLengthBytes); + cloudData->allocatedData=0; + cloudData->dataPtr=cloudData->stackData; + } + // Update how many bytes were written for this data + cloudData->dataLengthBytes=dataLengthBytes; + remoteCloudClient->uploadedBytes+=dataLengthBytes; + + if (cloudDataAlreadyUploaded==false) + { + // New data field + SendUploadedKeyToServers(cloudDataList->key); + } + + // Existing data field changed + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, true ); + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, true ); + + // Send update to all remote servers that subscribed to this key + NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, true); + + // I could have also subscribed to a key not yet updated locally + // This means I have to go through every RemoteClient that wants this key + // Seems like cloudData->specificSubscribers is unnecessary in that case +} +void CloudServer::OnReleaseRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + + uint16_t keyCount; + bsIn.Read(keyCount); + + if (keyCount==0) + return; + + DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(packet->guid); + if (remoteSystemIndex.IsInvalid()==true) + return; + + RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); + + CloudKey key; + + // Read all in a list first so I can run filter on it + DataStructures::List cloudKeys; + for (uint16_t keyCountIndex=0; keyCountIndex < keyCount; keyCountIndex++) + { + key.Serialize(false, &bsIn); + cloudKeys.Push(key, _FILE_AND_LINE_); + } + + for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) + { + if (queryFilters[filterIndex]->OnReleaseRequest(packet->guid, packet->systemAddress, cloudKeys)) + return; + } + + for (uint16_t keyCountIndex=0; keyCountIndex < keyCount; keyCountIndex++) + { + // Serialize in list above so I can run the filter on it + // key.Serialize(false, &bsIn); + key=cloudKeys[keyCountIndex]; + + // Remove remote systems uploaded keys + bool objectExists; + unsigned int uploadedKeysIndex = remoteCloudClient->uploadedKeys.GetIndexFromKey(key,&objectExists); + if (objectExists) + { + bool dataRepositoryExists; + unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(key, &dataRepositoryExists); + CloudDataList* cloudDataList = dataRepository[dataRepositoryIndex]; + RakAssert(cloudDataList); + + CloudData *cloudData; + bool keyDataListExists; + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(packet->guid, &keyDataListExists); + cloudData = cloudDataList->keyData[keyDataListIndex]; + + remoteCloudClient->uploadedKeys.RemoveAtIndex(uploadedKeysIndex); + remoteCloudClient->uploadedBytes-=cloudData->dataLengthBytes; + cloudDataList->uploaderCount--; + + // Broadcast destruction of this key to subscribers + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, false ); + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, false ); + NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, false ); + + cloudData->Clear(); + + if (cloudData->IsUnused()) + { + RakNet::OP_DELETE(cloudData, _FILE_AND_LINE_); + cloudDataList->keyData.RemoveAtIndex(keyDataListIndex); + if (cloudDataList->IsNotUploaded()) + { + // Tell other servers that this key is no longer uploaded, so they do not request it from us + RemoveUploadedKeyFromServers(cloudDataList->key); + } + + if (cloudDataList->IsUnused()) + { + RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } + } + + if (remoteCloudClient->IsUnused()) + { + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); + break; + } + } + } +} +void CloudServer::OnGetRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + uint16_t specificSystemsCount; + CloudKey cloudKey; + + // Create a new GetRequest + GetRequest *getRequest; + getRequest = RakNet::OP_NEW(_FILE_AND_LINE_); + getRequest->cloudQueryWithAddresses.cloudQuery.Serialize(false, &bsIn); + getRequest->requestingClient=packet->guid; + + RakNetGUID addressOrGuid; + bsIn.Read(specificSystemsCount); + for (uint16_t i=0; i < specificSystemsCount; i++) + { + bsIn.Read(addressOrGuid); + getRequest->cloudQueryWithAddresses.specificSystems.Push(addressOrGuid, _FILE_AND_LINE_); + } + + if (getRequest->cloudQueryWithAddresses.cloudQuery.keys.Size()==0) + { + RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); + return; + } + + for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) + { + if (queryFilters[filterIndex]->OnGetRequest(packet->guid, packet->systemAddress, getRequest->cloudQueryWithAddresses.cloudQuery, getRequest->cloudQueryWithAddresses.specificSystems )==false) + return; + } + + getRequest->requestStartTime=RakNet::GetTime(); + getRequest->requestId=nextGetRequestId++; + + // Send request to servers that have this data + DataStructures::List remoteServersWithData; + GetServersWithUploadedKeys(getRequest->cloudQueryWithAddresses.cloudQuery.keys, remoteServersWithData); + + if (remoteServersWithData.Size()==0) + { + ProcessAndTransmitGetRequest(getRequest); + } + else + { + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_PROCESS_GET_REQUEST); + getRequest->cloudQueryWithAddresses.Serialize(true, &bsOut); + bsOut.Write(getRequest->requestId); + + for (unsigned int remoteServerIndex=0; remoteServerIndex < remoteServersWithData.Size(); remoteServerIndex++) + { + BufferedGetResponseFromServer* bufferedGetResponseFromServer = RakNet::OP_NEW(_FILE_AND_LINE_); + bufferedGetResponseFromServer->serverAddress=remoteServersWithData[remoteServerIndex]->serverAddress; + bufferedGetResponseFromServer->gotResult=false; + getRequest->remoteServerResponses.Insert(remoteServersWithData[remoteServerIndex]->serverAddress, bufferedGetResponseFromServer, true, _FILE_AND_LINE_); + + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServersWithData[remoteServerIndex]->serverAddress, false); + } + + // Record that this system made this request + getRequests.Insert(getRequest->requestId, getRequest, true, _FILE_AND_LINE_); + } + + if (getRequest->cloudQueryWithAddresses.cloudQuery.subscribeToResults) + { + // Add to key subscription list for the client, which contains a keyId / specificUploaderList pair + DataStructures::HashIndex remoteSystemsHashIndex = remoteSystems.GetIndexOf(packet->guid); + RemoteCloudClient *remoteCloudClient; + if (remoteSystemsHashIndex.IsInvalid()) + { + remoteCloudClient = RakNet::OP_NEW(_FILE_AND_LINE_); + remoteCloudClient->uploadedBytes=0; + remoteSystems.Push(packet->guid, remoteCloudClient, _FILE_AND_LINE_); + } + else + { + remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemsHashIndex); + } + + unsigned int keyIndex; + for (keyIndex=0; keyIndex < getRequest->cloudQueryWithAddresses.cloudQuery.keys.Size(); keyIndex++) + { + cloudKey = getRequest->cloudQueryWithAddresses.cloudQuery.keys[keyIndex]; + + unsigned int keySubscriberIndex; + bool hasKeySubscriber; + keySubscriberIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudKey, &hasKeySubscriber); + KeySubscriberID* keySubscriberId; + if (hasKeySubscriber) + { + DataStructures::List specificSystems; + UnsubscribeFromKey(remoteCloudClient, packet->guid, keySubscriberIndex, cloudKey, specificSystems); + } + + keySubscriberId = RakNet::OP_NEW(_FILE_AND_LINE_); + keySubscriberId->key=cloudKey; + + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < getRequest->cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) + { + keySubscriberId->specificSystemsSubscribedTo.Insert(getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex], getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex], true, _FILE_AND_LINE_); + } + + remoteCloudClient->subscribedKeys.InsertAtIndex(keySubscriberId, keySubscriberIndex, _FILE_AND_LINE_); + + // Add CloudData in a similar way + unsigned int dataRepositoryIndex; + bool dataRepositoryExists; + CloudDataList* cloudDataList = GetOrAllocateCloudDataList(cloudKey, &dataRepositoryExists, dataRepositoryIndex); + + // If this is the first local client to subscribe to this key, call SendSubscribedKeyToServers + if (cloudDataList->subscriberCount==0) + SendSubscribedKeyToServers(cloudKey); + + // If the subscription is specific, may have to also allocate CloudData + if (getRequest->cloudQueryWithAddresses.specificSystems.Size()) + { + CloudData *cloudData; + bool keyDataListExists; + + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < getRequest->cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) + { + RakNetGUID specificSystem = getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex]; + + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(specificSystem, &keyDataListExists); + if (keyDataListExists==false) + { + cloudData = RakNet::OP_NEW(_FILE_AND_LINE_); + cloudData->dataLengthBytes=0; + cloudData->allocatedData=0; + cloudData->isUploaded=false; + cloudData->dataPtr=0; + cloudData->serverSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; + cloudData->clientSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; + cloudData->serverGUID=rakPeerInterface->GetMyGUID(); + cloudData->clientGUID=specificSystem; + cloudDataList->keyData.Insert(specificSystem,cloudData,true,_FILE_AND_LINE_); + } + else + { + cloudData = cloudDataList->keyData[keyDataListIndex]; + } + + ++cloudDataList->subscriberCount; + cloudData->specificSubscribers.Insert(packet->guid, packet->guid, true, _FILE_AND_LINE_); + } + } + else + { + ++cloudDataList->subscriberCount; + cloudDataList->nonSpecificSubscribers.Insert(packet->guid, packet->guid, true, _FILE_AND_LINE_); + + // Remove packet->guid from CloudData::specificSubscribers among all instances of cloudDataList->keyData + unsigned int subscribedKeysIndex; + bool subscribedKeysIndexExists; + subscribedKeysIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudDataList->key, &subscribedKeysIndexExists); + if (subscribedKeysIndexExists) + { + KeySubscriberID* keySubscriberId; + keySubscriberId = remoteCloudClient->subscribedKeys[subscribedKeysIndex]; + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < keySubscriberId->specificSystemsSubscribedTo.Size(); specificSystemIndex++) + { + bool keyDataExists; + unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(keySubscriberId->specificSystemsSubscribedTo[specificSystemIndex], &keyDataExists); + if (keyDataExists) + { + CloudData *keyData = cloudDataList->keyData[keyDataIndex]; + keyData->specificSubscribers.Remove(packet->guid); + --cloudDataList->subscriberCount; + } + } + } + } + } + + if (remoteCloudClient->subscribedKeys.Size()==0) + { + // Didn't do anything + remoteSystems.Remove(packet->guid, _FILE_AND_LINE_); + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + } + } + + if (remoteServersWithData.Size()==0) + RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); +} +void CloudServer::OnUnsubscribeRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + + DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(packet->guid); + if (remoteSystemIndex.IsInvalid()==true) + return; + + RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); + + uint16_t keyCount, specificSystemCount; + DataStructures::List cloudKeys; + DataStructures::List specificSystems; + uint16_t index; + + CloudKey cloudKey; + bsIn.Read(keyCount); + for (index=0; index < keyCount; index++) + { + cloudKey.Serialize(false, &bsIn); + cloudKeys.Push(cloudKey, _FILE_AND_LINE_); + } + + RakNetGUID specificSystem; + bsIn.Read(specificSystemCount); + for (index=0; index < specificSystemCount; index++) + { + bsIn.Read(specificSystem); + specificSystems.Push(specificSystem, _FILE_AND_LINE_); + } + + for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) + { + if (queryFilters[filterIndex]->OnUnsubscribeRequest(packet->guid, packet->systemAddress, cloudKeys, specificSystems )==false) + return; + } + +// CloudDataList *cloudDataList; + bool dataRepositoryExists; +// unsigned int dataRepositoryIndex; + + for (index=0; index < keyCount; index++) + { + CloudKey cloudKey = cloudKeys[index]; + + // dataRepositoryIndex = + dataRepository.GetIndexFromKey(cloudKey, &dataRepositoryExists); + if (dataRepositoryExists==false) + continue; +// cloudDataList = dataRepository[dataRepositoryIndex]; + + unsigned int keySubscriberIndex; + bool hasKeySubscriber; + keySubscriberIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudKey, &hasKeySubscriber); + + if (hasKeySubscriber==false) + continue; + + UnsubscribeFromKey(remoteCloudClient, packet->guid, keySubscriberIndex, cloudKey, specificSystems); + } + + if (remoteCloudClient->IsUnused()) + { + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); + } +} +void CloudServer::OnServerToServerGetRequest(Packet *packet) +{ +// unsigned int remoteServerIndex; + bool objectExists; + //remoteServerIndex = + remoteServers.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists==false) + return; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + CloudQueryWithAddresses cloudQueryWithAddresses; + uint32_t requestId; + cloudQueryWithAddresses.Serialize(false, &bsIn); + bsIn.Read(requestId); + + DataStructures::List cloudDataResultList; + DataStructures::List cloudKeyResultList; + ProcessCloudQueryWithAddresses(cloudQueryWithAddresses, cloudDataResultList, cloudKeyResultList); + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_PROCESS_GET_RESPONSE); + bsOut.Write(requestId); + WriteCloudQueryRowFromResultList(cloudDataResultList, cloudKeyResultList, &bsOut); + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false); +} +void CloudServer::OnServerToServerGetResponse(Packet *packet) +{ + unsigned int remoteServerIndex; + bool objectExists; + remoteServerIndex = remoteServers.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists==false) + return; + + RemoteServer *remoteServer = remoteServers[remoteServerIndex]; + if (remoteServer==0) + return; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + uint32_t requestId; + bsIn.Read(requestId); + + // Lookup request id + bool hasGetRequest; + unsigned int getRequestIndex; + getRequestIndex = getRequests.GetIndexFromKey(requestId, &hasGetRequest); + if (hasGetRequest==false) + return; + GetRequest *getRequest = getRequests[getRequestIndex]; + bool hasRemoteServer; + unsigned int remoteServerResponsesIndex; + remoteServerResponsesIndex = getRequest->remoteServerResponses.GetIndexFromKey(packet->guid, &hasRemoteServer); + if (hasRemoteServer==false) + return; + BufferedGetResponseFromServer *bufferedGetResponseFromServer; + bufferedGetResponseFromServer = getRequest->remoteServerResponses[remoteServerResponsesIndex]; + if (bufferedGetResponseFromServer->gotResult==true) + return; + bufferedGetResponseFromServer->gotResult=true; + uint32_t numRows; + bufferedGetResponseFromServer->queryResult.SerializeNumRows(false, numRows, &bsIn); + bufferedGetResponseFromServer->queryResult.SerializeCloudQueryRows(false, numRows, &bsIn, this); + + // If all results returned, then also process locally, and return to user + if (getRequest->AllRemoteServersHaveResponded()) + { + ProcessAndTransmitGetRequest(getRequest); + + getRequest->Clear(this); + RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); + + getRequests.RemoveAtIndex(getRequestIndex); + } +} +void CloudServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + + unsigned int remoteServerIndex; + bool objectExists; + remoteServerIndex = remoteServers.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists) + { + // Update remoteServerResponses by removing this server and sending the response if it is now complete + unsigned int getRequestIndex=0; + while (getRequestIndex < getRequests.Size()) + { + GetRequest *getRequest = getRequests[getRequestIndex]; + bool waitingForThisServer; + unsigned int remoteServerResponsesIndex = getRequest->remoteServerResponses.GetIndexFromKey(rakNetGUID, &waitingForThisServer); + if (waitingForThisServer) + { + getRequest->remoteServerResponses[remoteServerResponsesIndex]->Clear(this); + RakNet::OP_DELETE(getRequest->remoteServerResponses[remoteServerResponsesIndex], _FILE_AND_LINE_); + getRequest->remoteServerResponses.RemoveAtIndex(remoteServerResponsesIndex); + + if (getRequest->AllRemoteServersHaveResponded()) + { + ProcessAndTransmitGetRequest(getRequest); + getRequest->Clear(this); + RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); + + getRequests.RemoveAtIndex(getRequestIndex); + } + else + getRequestIndex++; + } + else + getRequestIndex++; + } + + RakNet::OP_DELETE(remoteServers[remoteServerIndex],_FILE_AND_LINE_); + remoteServers.RemoveAtIndex(remoteServerIndex); + } + + DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(rakNetGUID); + if (remoteSystemIndex.IsInvalid()==false) + { + RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); + unsigned int uploadedKeysIndex; + for (uploadedKeysIndex=0; uploadedKeysIndex < remoteCloudClient->uploadedKeys.Size(); uploadedKeysIndex++) + { + // Delete keys this system has uploaded + bool keyDataRepositoryExists; + unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(remoteCloudClient->uploadedKeys[uploadedKeysIndex], &keyDataRepositoryExists); + if (keyDataRepositoryExists) + { + CloudDataList* cloudDataList = dataRepository[dataRepositoryIndex]; + bool keyDataExists; + unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(rakNetGUID, &keyDataExists); + if (keyDataExists) + { + CloudData *cloudData = cloudDataList->keyData[keyDataIndex]; + cloudDataList->uploaderCount--; + + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, false ); + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, false ); + NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, false ); + + cloudData->Clear(); + + if (cloudData->IsUnused()) + { + RakNet::OP_DELETE(cloudData,_FILE_AND_LINE_); + cloudDataList->keyData.RemoveAtIndex(keyDataIndex); + + if (cloudDataList->IsNotUploaded()) + { + // Tell other servers that this key is no longer uploaded, so they do not request it from us + RemoveUploadedKeyFromServers(cloudDataList->key); + } + + if (cloudDataList->IsUnused()) + { + // Tell other servers that this key is no longer uploaded, so they do not request it from us + RemoveUploadedKeyFromServers(cloudDataList->key); + + RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } + } + } + } + } + + unsigned int subscribedKeysIndex; + for (subscribedKeysIndex=0; subscribedKeysIndex < remoteCloudClient->subscribedKeys.Size(); subscribedKeysIndex++) + { + KeySubscriberID* keySubscriberId; + keySubscriberId = remoteCloudClient->subscribedKeys[subscribedKeysIndex]; + + bool keyDataRepositoryExists; + unsigned int keyDataRepositoryIndex = dataRepository.GetIndexFromKey(remoteCloudClient->subscribedKeys[subscribedKeysIndex]->key, &keyDataRepositoryExists); + if (keyDataRepositoryExists) + { + CloudDataList* cloudDataList = dataRepository[keyDataRepositoryIndex]; + if (keySubscriberId->specificSystemsSubscribedTo.Size()==0) + { + cloudDataList->nonSpecificSubscribers.Remove(rakNetGUID); + --cloudDataList->subscriberCount; + } + else + { + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < keySubscriberId->specificSystemsSubscribedTo.Size(); specificSystemIndex++) + { + bool keyDataExists; + unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(keySubscriberId->specificSystemsSubscribedTo[specificSystemIndex], &keyDataExists); + if (keyDataExists) + { + CloudData *keyData = cloudDataList->keyData[keyDataIndex]; + keyData->specificSubscribers.Remove(rakNetGUID); + --cloudDataList->subscriberCount; + } + } + } + } + + RakNet::OP_DELETE(keySubscriberId, _FILE_AND_LINE_); + } + + // Delete and remove from remoteSystems + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); + } +} +void CloudServer::OnRakPeerShutdown(void) +{ + Clear(); +} +void CloudServer::Clear(void) +{ + unsigned int i,j; + for (i=0; i < dataRepository.Size(); i++) + { + CloudDataList *cloudDataList = dataRepository[i]; + for (j=0; j < cloudDataList->keyData.Size(); j++) + { + cloudDataList->keyData[j]->Clear(); + RakNet::OP_DELETE(cloudDataList->keyData[j], _FILE_AND_LINE_); + } + RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); + } + dataRepository.Clear(false, _FILE_AND_LINE_); + + for (i=0; i < remoteServers.Size(); i++) + { + RakNet::OP_DELETE(remoteServers[i], _FILE_AND_LINE_); + } + remoteServers.Clear(false, _FILE_AND_LINE_); + + for (i=0; i < getRequests.Size(); i++) + { + GetRequest *getRequest = getRequests[i]; + getRequest->Clear(this); + RakNet::OP_DELETE(getRequests[i], _FILE_AND_LINE_); + } + getRequests.Clear(false, _FILE_AND_LINE_); + + DataStructures::List keyList; + DataStructures::List itemList; + remoteSystems.GetAsList(itemList, keyList, _FILE_AND_LINE_); + for (i=0; i < itemList.Size(); i++) + { + RemoteCloudClient* remoteCloudClient = itemList[i]; + for (j=0; j < remoteCloudClient->subscribedKeys.Size(); j++) + { + RakNet::OP_DELETE(remoteCloudClient->subscribedKeys[j], _FILE_AND_LINE_); + } + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + } + remoteSystems.Clear(_FILE_AND_LINE_); +} +void CloudServer::WriteCloudQueryRowFromResultList(DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut) +{ + bsOut->WriteCasted(cloudKeyResultList.Size()); + unsigned int i; + for (i=0; i < cloudKeyResultList.Size(); i++) + { + WriteCloudQueryRowFromResultList(i, cloudDataResultList, cloudKeyResultList, bsOut); + } +} +void CloudServer::WriteCloudQueryRowFromResultList(unsigned int i, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut) +{ + CloudQueryRow cloudQueryRow; + CloudData *cloudData = cloudDataResultList[i]; + cloudQueryRow.key=cloudKeyResultList[i]; + cloudQueryRow.data=cloudData->dataPtr; + cloudQueryRow.length=cloudData->dataLengthBytes; + cloudQueryRow.serverSystemAddress=cloudData->serverSystemAddress; + cloudQueryRow.clientSystemAddress=cloudData->clientSystemAddress; + cloudQueryRow.serverGUID=cloudData->serverGUID; + cloudQueryRow.clientGUID=cloudData->clientGUID; + cloudQueryRow.Serialize(true, bsOut, 0); +} +void CloudServer::NotifyClientSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, DataStructures::OrderedList &subscribers, bool wasUpdated ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID) ID_CLOUD_SUBSCRIPTION_NOTIFICATION); + bsOut.Write(wasUpdated); + CloudQueryRow row; + row.key=key; + row.data=cloudData->dataPtr; + row.length=cloudData->dataLengthBytes; + row.serverSystemAddress=cloudData->serverSystemAddress; + row.clientSystemAddress=cloudData->clientSystemAddress; + row.serverGUID=cloudData->serverGUID; + row.clientGUID=cloudData->clientGUID; + row.Serialize(true,&bsOut,0); + + unsigned int i; + for (i=0; i < subscribers.Size(); i++) + { + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, subscribers[i], false); + } +} +void CloudServer::NotifyClientSubscribersOfDataChange( CloudQueryRow *row, DataStructures::OrderedList &subscribers, bool wasUpdated ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID) ID_CLOUD_SUBSCRIPTION_NOTIFICATION); + bsOut.Write(wasUpdated); + row->Serialize(true,&bsOut,0); + + unsigned int i; + for (i=0; i < subscribers.Size(); i++) + { + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, subscribers[i], false); + } +} +void CloudServer::NotifyServerSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, bool wasUpdated ) +{ + // Find every server that has subscribed + // Send them change notifications + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_DATA_CHANGED); + bsOut.Write(wasUpdated); + CloudQueryRow row; + row.key=key; + row.data=cloudData->dataPtr; + row.length=cloudData->dataLengthBytes; + row.serverSystemAddress=cloudData->serverSystemAddress; + row.clientSystemAddress=cloudData->clientSystemAddress; + row.serverGUID=cloudData->serverGUID; + row.clientGUID=cloudData->clientGUID; + row.Serialize(true,&bsOut,0); + + unsigned int i; + for (i=0; i < remoteServers.Size(); i++) + { + if (remoteServers[i]->gotSubscribedAndUploadedKeys==false || remoteServers[i]->subscribedKeys.HasData(key)) + { + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); + } + } +} +void CloudServer::AddServer(RakNetGUID systemIdentifier) +{ + ConnectionState cs = rakPeerInterface->GetConnectionState(systemIdentifier); + if (cs==IS_DISCONNECTED || cs==IS_NOT_CONNECTED) + return; + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(systemIdentifier,&objectExists); + if (objectExists==false) + { + RemoteServer *remoteServer = RakNet::OP_NEW(_FILE_AND_LINE_); + remoteServer->gotSubscribedAndUploadedKeys=false; + remoteServer->serverAddress=systemIdentifier; + remoteServers.InsertAtIndex(remoteServer, index, _FILE_AND_LINE_); + + SendUploadedAndSubscribedKeysToServer(systemIdentifier); + } +} +void CloudServer::RemoveServer(RakNetGUID systemAddress) +{ + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(systemAddress,&objectExists); + if (objectExists==true) + { + RakNet::OP_DELETE(remoteServers[index],_FILE_AND_LINE_); + remoteServers.RemoveAtIndex(index); + } +} +void CloudServer::GetRemoteServers(DataStructures::List &remoteServersOut) +{ + remoteServersOut.Clear(true, _FILE_AND_LINE_); + + unsigned int i; + for (i=0; i < remoteServers.Size(); i++) + { + remoteServersOut.Push(remoteServers[i]->serverAddress, _FILE_AND_LINE_); + } +} +void CloudServer::ProcessAndTransmitGetRequest(GetRequest *getRequest) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID) ID_CLOUD_GET_RESPONSE); + + // BufferedGetResponseFromServer getResponse; + CloudQueryResult cloudQueryResult; + cloudQueryResult.cloudQuery=getRequest->cloudQueryWithAddresses.cloudQuery; + cloudQueryResult.subscribeToResults=getRequest->cloudQueryWithAddresses.cloudQuery.subscribeToResults; + cloudQueryResult.SerializeHeader(true, &bsOut); + + DataStructures::List cloudDataResultList; + DataStructures::List cloudKeyResultList; + ProcessCloudQueryWithAddresses(getRequest->cloudQueryWithAddresses, cloudDataResultList, cloudKeyResultList); + bool unlimitedRows=getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn==0; + + uint32_t localNumRows = (uint32_t) cloudDataResultList.Size(); + if (unlimitedRows==false && + localNumRows > getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex && + localNumRows - getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex > getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn ) + localNumRows=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex + getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn; + + BitSize_t bitStreamOffset = bsOut.GetWriteOffset(); + uint32_t localRowsToWrite; + unsigned int skipRows; + if (localNumRows>getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex) + { + localRowsToWrite=localNumRows-getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex; + skipRows=0; + } + else + { + localRowsToWrite=0; + skipRows=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex-localNumRows; + } + cloudQueryResult.SerializeNumRows(true, localRowsToWrite, &bsOut); + for (unsigned int i=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex; i < localNumRows; i++) + { + WriteCloudQueryRowFromResultList(i, cloudDataResultList, cloudKeyResultList, &bsOut); + } + + // Append remote systems for remaining rows + if (unlimitedRows==true || getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn>localRowsToWrite) + { + uint32_t remainingRows=0; + uint32_t additionalRowsWritten=0; + if (unlimitedRows==false) + remainingRows=getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn-localRowsToWrite; + + unsigned int remoteServerResponseIndex; + for (remoteServerResponseIndex=0; remoteServerResponseIndex < getRequest->remoteServerResponses.Size(); remoteServerResponseIndex++) + { + BufferedGetResponseFromServer *bufferedGetResponseFromServer = getRequest->remoteServerResponses[remoteServerResponseIndex]; + unsigned int cloudQueryRowIndex; + for (cloudQueryRowIndex=0; cloudQueryRowIndex < bufferedGetResponseFromServer->queryResult.rowsReturned.Size(); cloudQueryRowIndex++) + { + if (skipRows>0) + { + --skipRows; + continue; + } + bufferedGetResponseFromServer->queryResult.rowsReturned[cloudQueryRowIndex]->Serialize(true, &bsOut, this); + + ++additionalRowsWritten; + if (unlimitedRows==false && --remainingRows==0) + break; + } + + if (unlimitedRows==false && remainingRows==0) + break; + } + + if (additionalRowsWritten>0) + { + BitSize_t curOffset = bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(bitStreamOffset); + localRowsToWrite+=additionalRowsWritten; + cloudQueryResult.SerializeNumRows(true, localRowsToWrite, &bsOut); + bsOut.SetWriteOffset(curOffset); + } + } + + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, getRequest->requestingClient, false); +} +void CloudServer::ProcessCloudQueryWithAddresses( CloudServer::CloudQueryWithAddresses &cloudQueryWithAddresses, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList ) +{ + CloudQueryResult cloudQueryResult; + CloudQueryRow cloudQueryRow; + unsigned int queryIndex; + bool dataRepositoryExists; + CloudDataList* cloudDataList; + unsigned int keyDataIndex; + + // If specificSystems list empty, applies to all systems + // For each of keys in cloudQueryWithAddresses, return that data, limited by maxRowsToReturn + for (queryIndex=0; queryIndex < cloudQueryWithAddresses.cloudQuery.keys.Size(); queryIndex++) + { + const CloudKey &key = cloudQueryWithAddresses.cloudQuery.keys[queryIndex]; + + unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(key, &dataRepositoryExists); + if (dataRepositoryExists) + { + cloudDataList=dataRepository[dataRepositoryIndex]; + + if (cloudDataList->uploaderCount>0) + { + // Return all keyData that was uploaded by specificSystems, or all if not specified + if (cloudQueryWithAddresses.specificSystems.Size()>0) + { + // Return data for matching systems + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) + { + bool uploaderExists; + keyDataIndex = cloudDataList->keyData.GetIndexFromKey(cloudQueryWithAddresses.specificSystems[specificSystemIndex], &uploaderExists); + if (uploaderExists) + { + cloudDataResultList.Push(cloudDataList->keyData[keyDataIndex], _FILE_AND_LINE_); + cloudKeyResultList.Push(key, _FILE_AND_LINE_); + } + } + } + else + { + // Return data for all systems + for (keyDataIndex=0; keyDataIndex < cloudDataList->keyData.Size(); keyDataIndex++) + { + cloudDataResultList.Push(cloudDataList->keyData[keyDataIndex], _FILE_AND_LINE_); + cloudKeyResultList.Push(key, _FILE_AND_LINE_); + } + } + } + } + } +} +void CloudServer::SendUploadedAndSubscribedKeysToServer( RakNetGUID systemAddress ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS); + bsOut.WriteCasted(dataRepository.Size()); + for (unsigned int i=0; i < dataRepository.Size(); i++) + dataRepository[i]->key.Serialize(true, &bsOut); + + BitSize_t startOffset, endOffset; + uint16_t subscribedKeyCount=0; + startOffset=bsOut.GetWriteOffset(); + bsOut.WriteCasted(subscribedKeyCount); + for (unsigned int i=0; i < dataRepository.Size(); i++) + { + if (dataRepository[i]->subscriberCount>0) + { + dataRepository[i]->key.Serialize(true, &bsOut); + subscribedKeyCount++; + } + } + endOffset=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(startOffset); + bsOut.WriteCasted(subscribedKeyCount); + bsOut.SetWriteOffset(endOffset); + + if (dataRepository.Size()>0 || subscribedKeyCount>0) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false); +} +void CloudServer::SendUploadedKeyToServers( CloudKey &cloudKey ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_ADD_UPLOADED_KEY); + cloudKey.Serialize(true, &bsOut); + for (unsigned int i=0; i < remoteServers.Size(); i++) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); +} +void CloudServer::SendSubscribedKeyToServers( CloudKey &cloudKey ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_ADD_SUBSCRIBED_KEY); + cloudKey.Serialize(true, &bsOut); + for (unsigned int i=0; i < remoteServers.Size(); i++) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); +} +void CloudServer::RemoveUploadedKeyFromServers( CloudKey &cloudKey ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_REMOVE_UPLOADED_KEY); + cloudKey.Serialize(true, &bsOut); + for (unsigned int i=0; i < remoteServers.Size(); i++) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); +} +void CloudServer::RemoveSubscribedKeyFromServers( CloudKey &cloudKey ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_REMOVE_SUBSCRIBED_KEY); + cloudKey.Serialize(true, &bsOut); + for (unsigned int i=0; i < remoteServers.Size(); i++) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); +} +void CloudServer::OnSendUploadedAndSubscribedKeysToServer( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + remoteServer->gotSubscribedAndUploadedKeys=true; + +// unsigned int insertionIndex; + bool alreadyHasKey; + uint16_t numUploadedKeys, numSubscribedKeys; + bsIn.Read(numUploadedKeys); + for (uint16_t i=0; i < numUploadedKeys; i++) + { + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); + + // insertionIndex = + remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==false) + remoteServer->uploadedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); + } + + bsIn.Read(numSubscribedKeys); + for (uint16_t i=0; i < numSubscribedKeys; i++) + { + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); + + //insertionIndex = + remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==false) + remoteServer->subscribedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); + } + + // Potential todo - join servers + // For each uploaded key that we subscribe to, query it + // For each subscribed key that we have, send it +} +void CloudServer::OnSendUploadedKeyToServers( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); +// unsigned int insertionIndex; + bool alreadyHasKey; +// insertionIndex = + remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==false) + remoteServer->uploadedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); +} +void CloudServer::OnSendSubscribedKeyToServers( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); +// unsigned int insertionIndex; + bool alreadyHasKey; +// insertionIndex = + remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + + // Do not need to send current values, the Get request will do that as the Get request is sent at the same time + if (alreadyHasKey==false) + remoteServer->subscribedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); +} +void CloudServer::OnRemoveUploadedKeyFromServers( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); + unsigned int insertionIndex; + bool alreadyHasKey; + insertionIndex = remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==true) + remoteServer->uploadedKeys.RemoveAtIndex(insertionIndex); +} +void CloudServer::OnRemoveSubscribedKeyFromServers( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); + unsigned int insertionIndex; + bool alreadyHasKey; + insertionIndex = remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==true) + remoteServer->subscribedKeys.RemoveAtIndex(insertionIndex); +} +void CloudServer::OnServerDataChanged( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + + // Find everyone that cares about this change and relay + bool wasUpdated=false; + bsIn.Read(wasUpdated); + CloudQueryRow row; + row.Serialize(false, &bsIn, this); + + CloudDataList *cloudDataList; + bool dataRepositoryExists; + unsigned int dataRepositoryIndex; + dataRepositoryIndex = dataRepository.GetIndexFromKey(row.key, &dataRepositoryExists); + if (dataRepositoryExists==false) + { + DeallocateRowData(row.data); + return; + } + cloudDataList = dataRepository[dataRepositoryIndex]; + CloudData *cloudData; + bool keyDataListExists; + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(row.clientGUID, &keyDataListExists); + if (keyDataListExists==true) + { + cloudData = cloudDataList->keyData[keyDataListIndex]; + NotifyClientSubscribersOfDataChange(&row, cloudData->specificSubscribers, wasUpdated ); + } + + NotifyClientSubscribersOfDataChange(&row, cloudDataList->nonSpecificSubscribers, wasUpdated ); + DeallocateRowData(row.data); +} +void CloudServer::GetServersWithUploadedKeys( + DataStructures::List &keys, + DataStructures::List &remoteServersWithData + ) +{ + remoteServersWithData.Clear(true, _FILE_AND_LINE_); + + unsigned int i,j; + for (i=0; i < remoteServers.Size(); i++) + { + remoteServers[i]->workingFlag=false; + } + + for (i=0; i < remoteServers.Size(); i++) + { + if (remoteServers[i]->workingFlag==false) + { + if (remoteServers[i]->gotSubscribedAndUploadedKeys==false) + { + remoteServers[i]->workingFlag=true; + remoteServersWithData.Push(remoteServers[i], _FILE_AND_LINE_); + } + else + { + remoteServers[i]->workingFlag=false; + for (j=0; j < keys.Size(); j++) + { + if (remoteServers[i]->workingFlag==false && remoteServers[i]->uploadedKeys.HasData(keys[j])) + { + remoteServers[i]->workingFlag=true; + remoteServersWithData.Push(remoteServers[i], _FILE_AND_LINE_); + break; + } + } + } + } + } +} + +CloudServer::CloudDataList *CloudServer::GetOrAllocateCloudDataList(CloudKey key, bool *dataRepositoryExists, unsigned int &dataRepositoryIndex) +{ + CloudDataList *cloudDataList; + + dataRepositoryIndex = dataRepository.GetIndexFromKey(key, dataRepositoryExists); + if (*dataRepositoryExists==false) + { + cloudDataList = RakNet::OP_NEW(_FILE_AND_LINE_); + cloudDataList->key=key; + cloudDataList->uploaderCount=0; + cloudDataList->subscriberCount=0; + dataRepository.InsertAtIndex(cloudDataList,dataRepositoryIndex,_FILE_AND_LINE_); + } + else + { + cloudDataList = dataRepository[dataRepositoryIndex]; + } + + return cloudDataList; +} + +void CloudServer::UnsubscribeFromKey(RemoteCloudClient *remoteCloudClient, RakNetGUID remoteCloudClientGuid, unsigned int keySubscriberIndex, CloudKey &cloudKey, DataStructures::List &specificSystems) +{ + KeySubscriberID* keySubscriberId = remoteCloudClient->subscribedKeys[keySubscriberIndex]; + + // If removing specific systems, but global subscription, fail + if (keySubscriberId->specificSystemsSubscribedTo.Size()==0 && specificSystems.Size()>0) + return; + + bool dataRepositoryExists; + CloudDataList *cloudDataList; + unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(cloudKey, &dataRepositoryExists); + if (dataRepositoryExists==false) + return; + + unsigned int i,j; + + cloudDataList = dataRepository[dataRepositoryIndex]; + if (specificSystems.Size()==0) + { + // Remove global subscriber. If returns false, have to remove specific subscribers + if (cloudDataList->RemoveSubscriber(remoteCloudClientGuid)==false) + { + for (i=0; i < keySubscriberId->specificSystemsSubscribedTo.Size(); i++) + { + RemoveSpecificSubscriber(keySubscriberId->specificSystemsSubscribedTo[i], cloudDataList, remoteCloudClientGuid); + } + } + keySubscriberId->specificSystemsSubscribedTo.Clear(true, _FILE_AND_LINE_); + } + else + { + for (j=0; j < specificSystems.Size(); j++) + { + unsigned int specificSystemsSubscribedToIndex; + bool hasSpecificSystemsSubscribedTo; + specificSystemsSubscribedToIndex=keySubscriberId->specificSystemsSubscribedTo.GetIndexFromKey(specificSystems[j], &hasSpecificSystemsSubscribedTo); + if (hasSpecificSystemsSubscribedTo) + { + RemoveSpecificSubscriber(specificSystems[j], cloudDataList, remoteCloudClientGuid); + keySubscriberId->specificSystemsSubscribedTo.RemoveAtIndex(specificSystemsSubscribedToIndex); + } + } + } + + if (keySubscriberId->specificSystemsSubscribedTo.Size()==0) + { + RakNet::OP_DELETE(keySubscriberId, _FILE_AND_LINE_); + remoteCloudClient->subscribedKeys.RemoveAtIndex(keySubscriberIndex); + } + + if (cloudDataList->subscriberCount==0) + RemoveSubscribedKeyFromServers(cloudKey); + + if (cloudDataList->IsUnused()) + { + RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } +} +void CloudServer::RemoveSpecificSubscriber(RakNetGUID specificSubscriber, CloudDataList *cloudDataList, RakNetGUID remoteCloudClientGuid) +{ + bool keyDataListExists; + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(specificSubscriber, &keyDataListExists); + if (keyDataListExists==false) + return; + CloudData *cloudData = cloudDataList->keyData[keyDataListIndex]; + bool hasSpecificSubscriber; + unsigned int specificSubscriberIndex = cloudData->specificSubscribers.GetIndexFromKey(remoteCloudClientGuid, &hasSpecificSubscriber); + if (hasSpecificSubscriber) + { + cloudData->specificSubscribers.RemoveAtIndex(specificSubscriberIndex); + cloudDataList->subscriberCount--; + + if (cloudData->IsUnused()) + { + RakNet::OP_DELETE(cloudData, _FILE_AND_LINE_); + cloudDataList->keyData.RemoveAtIndex(keyDataListIndex); + } + } +} + +void CloudServer::ForceExternalSystemAddress(SystemAddress forcedAddress) +{ + forceAddress=forcedAddress; +} +void CloudServer::AddQueryFilter(CloudServerQueryFilter* filter) +{ + if (queryFilters.GetIndexOf(filter)!=(unsigned int) -1) + return; + queryFilters.Push(filter, _FILE_AND_LINE_); +} +void CloudServer::RemoveQueryFilter(CloudServerQueryFilter* filter) +{ + unsigned int index; + index = queryFilters.GetIndexOf(filter); + if (index != (unsigned int) -1) + queryFilters.RemoveAtIndex(index); +} +void CloudServer::RemoveAllQueryFilters(void) +{ + queryFilters.Clear(true, _FILE_AND_LINE_); +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/CloudServer.h b/project/lib_projects/raknet/jni/RaknetSources/CloudServer.h index cf4f78f..391a46e 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CloudServer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/CloudServer.h @@ -1,375 +1,375 @@ -/// \file CloudServer.h -/// \brief Stores client data, and allows cross-server communication to retrieve this data -/// \details TODO -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudServer==1 - -#ifndef __CLOUD_SERVER_H -#define __CLOUD_SERVER_H - -#include "PluginInterface2.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" -#include "RakString.h" -#include "DS_Hash.h" -#include "CloudCommon.h" -#include "DS_OrderedList.h" - -/// If the data is smaller than this value, an allocation is avoid. However, this value exists for every row -#define CLOUD_SERVER_DATA_STACK_SIZE 32 - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \brief Zero or more instances of CloudServerQueryFilter can be attached to CloudServer to restrict client queries -/// All attached instances of CloudServerQueryFilter on each corresponding operation, from all directly connected clients -/// If any attached instance returns false for a given operation, that operation is silently rejected -/// \ingroup CLOUD_GROUP -class RAK_DLL_EXPORT CloudServerQueryFilter -{ -public: - CloudServerQueryFilter() {} - virtual ~CloudServerQueryFilter() {} - - /// Called when a local client wants to post data - /// \return true to allow, false to reject - virtual bool OnPostRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudKey key, uint32_t dataLength, const char *data)=0; - - /// Called when a local client wants to release data that it has previously uploaded - /// \return true to allow, false to reject - virtual bool OnReleaseRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List &cloudKeys)=0; - - /// Called when a local client wants to query data - /// If you return false, the client will get no response at all - /// \return true to allow, false to reject - virtual bool OnGetRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudQuery &query, DataStructures::List &specificSystems)=0; - - /// Called when a local client wants to stop getting updates for data - /// If you return false, the client will keep getting updates for that data - /// \return true to allow, false to reject - virtual bool OnUnsubscribeRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List &cloudKeys, DataStructures::List &specificSystems)=0; -}; - -/// \brief Stores client data, and allows cross-server communication to retrieve this data -/// \ingroup CLOUD_GROUP -class RAK_DLL_EXPORT CloudServer : public PluginInterface2, CloudAllocator -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(CloudServer) - - CloudServer(); - virtual ~CloudServer(); - - /// \brief Max bytes a client can upload - /// Data in excess of this value is silently ignored - /// defaults to 0 (unlimited) - /// \param[in] bytes Max bytes a client can upload. 0 means unlimited. - void SetMaxUploadBytesPerClient(uint64_t bytes); - - /// \brief Max bytes returned by a download. If the number of bytes would exceed this amount, the returned list is truncated - /// However, if this would result in no rows downloaded, then one row will be returned. - /// \param[in] bytes Max bytes a client can download from a single Get(). 0 means unlimited. - void SetMaxBytesPerDownload(uint64_t bytes); - - /// \brief Add a server, which is assumed to be connected in a fully connected mesh to all other servers and also running the CloudServer plugin - /// The other system must also call AddServer before getting the subscription data, or it will be rejected. - /// Sending a message telling the other system to call AddServer(), followed by calling AddServer() locally, would be sufficient for this to work. - /// \note This sends subscription data to the other system, using RELIABLE_ORDERED on channel 0 - /// \param[in] systemIdentifier Identifier of the remote system - void AddServer(RakNetGUID systemIdentifier); - - /// \brief Removes a server added through AddServer() - /// \param[in] systemIdentifier Identifier of the remote system - void RemoveServer(RakNetGUID systemIdentifier); - - /// Return list of servers added with AddServer() - /// \param[out] remoteServers List of servers added - void GetRemoteServers(DataStructures::List &remoteServersOut); - - /// \brief Frees all memory. Does not remove query filters - void Clear(void); - - /// \brief Report the specified SystemAddress to client queries, rather than what RakPeer reads. - /// This is useful if you already know your public IP - /// This only applies to future updates, so call it before updating to apply to all queries - /// \param[in] forcedAddress The systmeAddress to return in queries. Use UNASSIGNED_SYSTEM_ADDRESS (default) to use what RakPeer returns - void ForceExternalSystemAddress(SystemAddress forcedAddress); - - /// \brief Adds a callback called on each query. If all filters returns true for an operation, the operation is allowed. - /// If the filter was already added, the function silently fails - /// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters() - void AddQueryFilter(CloudServerQueryFilter* filter); - - /// \brief Removes a callback added with AddQueryFilter() - /// The instance is not deleted, only unreferenced. It is up to the user to delete the instance, if necessary - /// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters() - void RemoveQueryFilter(CloudServerQueryFilter* filter); - - /// \brief Removes all instances of CloudServerQueryFilter added with AddQueryFilter(). - /// The instances are not deleted, only unreferenced. It is up to the user to delete the instances, if necessary - void RemoveAllQueryFilters(void); - -protected: - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnRakPeerShutdown(void); - - - virtual void OnPostRequest(Packet *packet); - virtual void OnReleaseRequest(Packet *packet); - virtual void OnGetRequest(Packet *packet); - virtual void OnUnsubscribeRequest(Packet *packet); - virtual void OnServerToServerGetRequest(Packet *packet); - virtual void OnServerToServerGetResponse(Packet *packet); - - uint64_t maxUploadBytesPerClient, maxBytesPerDowload; - - // ---------------------------------------------------------------------------- - // For a given data key, quickly look up one or all systems that have uploaded - // ---------------------------------------------------------------------------- - struct CloudData - { - CloudData() {} - ~CloudData() {if (allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_);} - bool IsUnused(void) const {return isUploaded==false && specificSubscribers.Size()==0;} - void Clear(void) {if (dataPtr==allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_); allocatedData=0; dataPtr=0; dataLengthBytes=0; isUploaded=false;} - - unsigned char stackData[CLOUD_SERVER_DATA_STACK_SIZE]; - unsigned char *allocatedData; // Uses allocatedData instead of stackData if length of data exceeds CLOUD_SERVER_DATA_STACK_SIZE - unsigned char *dataPtr; // Points to either stackData or allocatedData - uint32_t dataLengthBytes; - bool isUploaded; - - /// System address of server that is holding this data, and the client is connected to - SystemAddress serverSystemAddress; - - /// System address of client that uploaded this data - SystemAddress clientSystemAddress; - - /// RakNetGUID of server that is holding this data, and the client is connected to - RakNetGUID serverGUID; - - /// RakNetGUID of client that uploaded this data - RakNetGUID clientGUID; - - /// When the key data changes from this particular system, notify these subscribers - /// This list mutually exclusive with CloudDataList::nonSpecificSubscribers - DataStructures::OrderedList specificSubscribers; - }; - void WriteCloudQueryRowFromResultList(unsigned int i, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut); - void WriteCloudQueryRowFromResultList(DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut); - - static int KeyDataPtrComp( const RakNetGUID &key, CloudData* const &data ); - struct CloudDataList - { - bool IsUnused(void) const {return keyData.Size()==0 && nonSpecificSubscribers.Size()==0;} - bool IsNotUploaded(void) const {return uploaderCount==0;} - bool RemoveSubscriber(RakNetGUID g) { - bool objectExists; - unsigned int index; - index = nonSpecificSubscribers.GetIndexFromKey(g, &objectExists); - if (objectExists) - { - subscriberCount--; - nonSpecificSubscribers.RemoveAtIndex(index); - return true; - } - return false; - } - - unsigned int uploaderCount, subscriberCount; - CloudKey key; - - // Data uploaded from or subscribed to for various systems - DataStructures::OrderedList keyData; - - /// When the key data changes from any system, notify these subscribers - /// This list mutually exclusive with CloudData::specificSubscribers - DataStructures::OrderedList nonSpecificSubscribers; - }; - - static int KeyDataListComp( const CloudKey &key, CloudDataList * const &data ); - DataStructures::OrderedList dataRepository; - - struct KeySubscriberID - { - CloudKey key; - DataStructures::OrderedList specificSystemsSubscribedTo; - }; - static int KeySubscriberIDComp(const CloudKey &key, KeySubscriberID * const &data ); - - // Remote systems - struct RemoteCloudClient - { - bool IsUnused(void) const {return uploadedKeys.Size()==0 && subscribedKeys.Size()==0;} - - DataStructures::OrderedList uploadedKeys; - DataStructures::OrderedList subscribedKeys; - uint64_t uploadedBytes; - }; - DataStructures::Hash remoteSystems; - - // For a given user, release all subscribed and uploaded keys - void ReleaseSystem(RakNetGUID clientAddress ); - - // For a given user, release a set of keys - void ReleaseKeys(RakNetGUID clientAddress, DataStructures::List &keys ); - - void NotifyClientSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, DataStructures::OrderedList &subscribers, bool wasUpdated ); - void NotifyClientSubscribersOfDataChange( CloudQueryRow *row, DataStructures::OrderedList &subscribers, bool wasUpdated ); - void NotifyServerSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, bool wasUpdated ); - - struct RemoteServer - { - RakNetGUID serverAddress; - // This server needs to know about these keys when they are updated or deleted - DataStructures::OrderedList subscribedKeys; - // This server has uploaded these keys, and needs to know about Get() requests - DataStructures::OrderedList uploadedKeys; - - // Just for processing - bool workingFlag; - - // If false, we don't know what keys they have yet, so send everything - bool gotSubscribedAndUploadedKeys; - }; - - static int RemoteServerComp(const RakNetGUID &key, RemoteServer* const &data ); - DataStructures::OrderedList remoteServers; - - struct BufferedGetResponseFromServer - { - void Clear(CloudAllocator *allocator); - - RakNetGUID serverAddress; - CloudQueryResult queryResult; - bool gotResult; - }; - - struct CloudQueryWithAddresses - { - // Inputs - CloudQuery cloudQuery; - DataStructures::List specificSystems; - - void Serialize(bool writeToBitstream, BitStream *bitStream); - }; - - static int BufferedGetResponseFromServerComp(const RakNetGUID &key, BufferedGetResponseFromServer* const &data ); - struct GetRequest - { - void Clear(CloudAllocator *allocator); - bool AllRemoteServersHaveResponded(void) const; - CloudQueryWithAddresses cloudQueryWithAddresses; - - // When request started. If takes too long for a response from another system, can abort remaining systems - RakNet::Time requestStartTime; - - // Assigned by server that gets the request to identify response. See nextGetRequestId - uint32_t requestId; - - RakNetGUID requestingClient; - - DataStructures::OrderedList remoteServerResponses; - }; - static int GetRequestComp(const uint32_t &key, GetRequest* const &data ); - DataStructures::OrderedList getRequests; - RakNet::Time nextGetRequestsCheck; - - uint32_t nextGetRequestId; - - void ProcessAndTransmitGetRequest(GetRequest *getRequest); - - void ProcessCloudQueryWithAddresses( - CloudServer::CloudQueryWithAddresses &cloudQueryWithAddresses, - DataStructures::List &cloudDataResultList, - DataStructures::List &cloudKeyResultList - ); - - void SendUploadedAndSubscribedKeysToServer( RakNetGUID systemAddress ); - void SendUploadedKeyToServers( CloudKey &cloudKey ); - void SendSubscribedKeyToServers( CloudKey &cloudKey ); - void RemoveUploadedKeyFromServers( CloudKey &cloudKey ); - void RemoveSubscribedKeyFromServers( CloudKey &cloudKey ); - - void OnSendUploadedAndSubscribedKeysToServer( Packet *packet ); - void OnSendUploadedKeyToServers( Packet *packet ); - void OnSendSubscribedKeyToServers( Packet *packet ); - void OnRemoveUploadedKeyFromServers( Packet *packet ); - void OnRemoveSubscribedKeyFromServers( Packet *packet ); - void OnServerDataChanged( Packet *packet ); - - void GetServersWithUploadedKeys( - DataStructures::List &keys, - DataStructures::List &remoteServersWithData - ); - - CloudServer::CloudDataList *GetOrAllocateCloudDataList(CloudKey key, bool *dataRepositoryExists, unsigned int &dataRepositoryIndex); - - void UnsubscribeFromKey(RemoteCloudClient *remoteCloudClient, RakNetGUID remoteCloudClientGuid, unsigned int keySubscriberIndex, CloudKey &cloudKey, DataStructures::List &specificSystems); - void RemoveSpecificSubscriber(RakNetGUID specificSubscriber, CloudDataList *cloudDataList, RakNetGUID remoteCloudClientGuid); - - DataStructures::List queryFilters; - - SystemAddress forceAddress; -}; - - -} // namespace RakNet - -#endif - - -// Key subscription -// -// A given system can subscribe to one or more keys. -// The subscription can be further be defined as only subscribing to keys uploaded by or changed by a given system. -// It is possible to subscribe to keys not yet uploaded, or uploaded to another system -// -// Operations: -// -// 1. SubscribeToKey() - Get() operation with subscription -// A. Add to key subscription list for the client, which contains a keyId / specificUploaderList pair -// B. Send to remote servers that for this key, they should send us updates -// C. (Done, get operation returns current values) -// -// 2. UpdateData() - Post() operation -// A. Find all subscribers to this data, for the uploading system. -// B. Send them the uploaded data -// C. Find all servers that subscribe to this data -// D. Send them the uploaded data -// -// 3. DeleteData() - Release() operation -// A. Find all subscribers to this data, for the deleting system. -// B. Inform them of the deletion -// C. Find all servers that subscribe to this data -// D. Inform them of the deletion -// -// 4. Unsubscribe() -// A. Find this subscriber, and remove their subscription -// B. If no one else is subscribing to this key for any system, notify remote servers we no longer need subscription updates -// -// Internal operations: -// -// 1. Find if any connected client has subscribed to a given key -// A. This is used add and remove our subscription for this key to remote servers -// -// 2. For a given key and updating address, find all connected clients that care -// A. First find connected clients that have subscribed to this key, regardless of address -// B. Then find connected clients that have subscribed to this key for this particular address -// -// 3. Find all remote servers that have subscribed to a given key -// A. This is so when the key is updated or deleted, we know who to send it to -// -// 4. For a given client (such as on disconnect), remove all records of their subscriptions - -#endif // _RAKNET_SUPPORT_* +/// \file CloudServer.h +/// \brief Stores client data, and allows cross-server communication to retrieve this data +/// \details TODO +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudServer==1 + +#ifndef __CLOUD_SERVER_H +#define __CLOUD_SERVER_H + +#include "PluginInterface2.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" +#include "RakString.h" +#include "DS_Hash.h" +#include "CloudCommon.h" +#include "DS_OrderedList.h" + +/// If the data is smaller than this value, an allocation is avoid. However, this value exists for every row +#define CLOUD_SERVER_DATA_STACK_SIZE 32 + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief Zero or more instances of CloudServerQueryFilter can be attached to CloudServer to restrict client queries +/// All attached instances of CloudServerQueryFilter on each corresponding operation, from all directly connected clients +/// If any attached instance returns false for a given operation, that operation is silently rejected +/// \ingroup CLOUD_GROUP +class RAK_DLL_EXPORT CloudServerQueryFilter +{ +public: + CloudServerQueryFilter() {} + virtual ~CloudServerQueryFilter() {} + + /// Called when a local client wants to post data + /// \return true to allow, false to reject + virtual bool OnPostRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudKey key, uint32_t dataLength, const char *data)=0; + + /// Called when a local client wants to release data that it has previously uploaded + /// \return true to allow, false to reject + virtual bool OnReleaseRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List &cloudKeys)=0; + + /// Called when a local client wants to query data + /// If you return false, the client will get no response at all + /// \return true to allow, false to reject + virtual bool OnGetRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudQuery &query, DataStructures::List &specificSystems)=0; + + /// Called when a local client wants to stop getting updates for data + /// If you return false, the client will keep getting updates for that data + /// \return true to allow, false to reject + virtual bool OnUnsubscribeRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List &cloudKeys, DataStructures::List &specificSystems)=0; +}; + +/// \brief Stores client data, and allows cross-server communication to retrieve this data +/// \ingroup CLOUD_GROUP +class RAK_DLL_EXPORT CloudServer : public PluginInterface2, CloudAllocator +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(CloudServer) + + CloudServer(); + virtual ~CloudServer(); + + /// \brief Max bytes a client can upload + /// Data in excess of this value is silently ignored + /// defaults to 0 (unlimited) + /// \param[in] bytes Max bytes a client can upload. 0 means unlimited. + void SetMaxUploadBytesPerClient(uint64_t bytes); + + /// \brief Max bytes returned by a download. If the number of bytes would exceed this amount, the returned list is truncated + /// However, if this would result in no rows downloaded, then one row will be returned. + /// \param[in] bytes Max bytes a client can download from a single Get(). 0 means unlimited. + void SetMaxBytesPerDownload(uint64_t bytes); + + /// \brief Add a server, which is assumed to be connected in a fully connected mesh to all other servers and also running the CloudServer plugin + /// The other system must also call AddServer before getting the subscription data, or it will be rejected. + /// Sending a message telling the other system to call AddServer(), followed by calling AddServer() locally, would be sufficient for this to work. + /// \note This sends subscription data to the other system, using RELIABLE_ORDERED on channel 0 + /// \param[in] systemIdentifier Identifier of the remote system + void AddServer(RakNetGUID systemIdentifier); + + /// \brief Removes a server added through AddServer() + /// \param[in] systemIdentifier Identifier of the remote system + void RemoveServer(RakNetGUID systemIdentifier); + + /// Return list of servers added with AddServer() + /// \param[out] remoteServers List of servers added + void GetRemoteServers(DataStructures::List &remoteServersOut); + + /// \brief Frees all memory. Does not remove query filters + void Clear(void); + + /// \brief Report the specified SystemAddress to client queries, rather than what RakPeer reads. + /// This is useful if you already know your public IP + /// This only applies to future updates, so call it before updating to apply to all queries + /// \param[in] forcedAddress The systmeAddress to return in queries. Use UNASSIGNED_SYSTEM_ADDRESS (default) to use what RakPeer returns + void ForceExternalSystemAddress(SystemAddress forcedAddress); + + /// \brief Adds a callback called on each query. If all filters returns true for an operation, the operation is allowed. + /// If the filter was already added, the function silently fails + /// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters() + void AddQueryFilter(CloudServerQueryFilter* filter); + + /// \brief Removes a callback added with AddQueryFilter() + /// The instance is not deleted, only unreferenced. It is up to the user to delete the instance, if necessary + /// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters() + void RemoveQueryFilter(CloudServerQueryFilter* filter); + + /// \brief Removes all instances of CloudServerQueryFilter added with AddQueryFilter(). + /// The instances are not deleted, only unreferenced. It is up to the user to delete the instances, if necessary + void RemoveAllQueryFilters(void); + +protected: + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnRakPeerShutdown(void); + + + virtual void OnPostRequest(Packet *packet); + virtual void OnReleaseRequest(Packet *packet); + virtual void OnGetRequest(Packet *packet); + virtual void OnUnsubscribeRequest(Packet *packet); + virtual void OnServerToServerGetRequest(Packet *packet); + virtual void OnServerToServerGetResponse(Packet *packet); + + uint64_t maxUploadBytesPerClient, maxBytesPerDowload; + + // ---------------------------------------------------------------------------- + // For a given data key, quickly look up one or all systems that have uploaded + // ---------------------------------------------------------------------------- + struct CloudData + { + CloudData() {} + ~CloudData() {if (allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_);} + bool IsUnused(void) const {return isUploaded==false && specificSubscribers.Size()==0;} + void Clear(void) {if (dataPtr==allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_); allocatedData=0; dataPtr=0; dataLengthBytes=0; isUploaded=false;} + + unsigned char stackData[CLOUD_SERVER_DATA_STACK_SIZE]; + unsigned char *allocatedData; // Uses allocatedData instead of stackData if length of data exceeds CLOUD_SERVER_DATA_STACK_SIZE + unsigned char *dataPtr; // Points to either stackData or allocatedData + uint32_t dataLengthBytes; + bool isUploaded; + + /// System address of server that is holding this data, and the client is connected to + SystemAddress serverSystemAddress; + + /// System address of client that uploaded this data + SystemAddress clientSystemAddress; + + /// RakNetGUID of server that is holding this data, and the client is connected to + RakNetGUID serverGUID; + + /// RakNetGUID of client that uploaded this data + RakNetGUID clientGUID; + + /// When the key data changes from this particular system, notify these subscribers + /// This list mutually exclusive with CloudDataList::nonSpecificSubscribers + DataStructures::OrderedList specificSubscribers; + }; + void WriteCloudQueryRowFromResultList(unsigned int i, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut); + void WriteCloudQueryRowFromResultList(DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut); + + static int KeyDataPtrComp( const RakNetGUID &key, CloudData* const &data ); + struct CloudDataList + { + bool IsUnused(void) const {return keyData.Size()==0 && nonSpecificSubscribers.Size()==0;} + bool IsNotUploaded(void) const {return uploaderCount==0;} + bool RemoveSubscriber(RakNetGUID g) { + bool objectExists; + unsigned int index; + index = nonSpecificSubscribers.GetIndexFromKey(g, &objectExists); + if (objectExists) + { + subscriberCount--; + nonSpecificSubscribers.RemoveAtIndex(index); + return true; + } + return false; + } + + unsigned int uploaderCount, subscriberCount; + CloudKey key; + + // Data uploaded from or subscribed to for various systems + DataStructures::OrderedList keyData; + + /// When the key data changes from any system, notify these subscribers + /// This list mutually exclusive with CloudData::specificSubscribers + DataStructures::OrderedList nonSpecificSubscribers; + }; + + static int KeyDataListComp( const CloudKey &key, CloudDataList * const &data ); + DataStructures::OrderedList dataRepository; + + struct KeySubscriberID + { + CloudKey key; + DataStructures::OrderedList specificSystemsSubscribedTo; + }; + static int KeySubscriberIDComp(const CloudKey &key, KeySubscriberID * const &data ); + + // Remote systems + struct RemoteCloudClient + { + bool IsUnused(void) const {return uploadedKeys.Size()==0 && subscribedKeys.Size()==0;} + + DataStructures::OrderedList uploadedKeys; + DataStructures::OrderedList subscribedKeys; + uint64_t uploadedBytes; + }; + DataStructures::Hash remoteSystems; + + // For a given user, release all subscribed and uploaded keys + void ReleaseSystem(RakNetGUID clientAddress ); + + // For a given user, release a set of keys + void ReleaseKeys(RakNetGUID clientAddress, DataStructures::List &keys ); + + void NotifyClientSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, DataStructures::OrderedList &subscribers, bool wasUpdated ); + void NotifyClientSubscribersOfDataChange( CloudQueryRow *row, DataStructures::OrderedList &subscribers, bool wasUpdated ); + void NotifyServerSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, bool wasUpdated ); + + struct RemoteServer + { + RakNetGUID serverAddress; + // This server needs to know about these keys when they are updated or deleted + DataStructures::OrderedList subscribedKeys; + // This server has uploaded these keys, and needs to know about Get() requests + DataStructures::OrderedList uploadedKeys; + + // Just for processing + bool workingFlag; + + // If false, we don't know what keys they have yet, so send everything + bool gotSubscribedAndUploadedKeys; + }; + + static int RemoteServerComp(const RakNetGUID &key, RemoteServer* const &data ); + DataStructures::OrderedList remoteServers; + + struct BufferedGetResponseFromServer + { + void Clear(CloudAllocator *allocator); + + RakNetGUID serverAddress; + CloudQueryResult queryResult; + bool gotResult; + }; + + struct CloudQueryWithAddresses + { + // Inputs + CloudQuery cloudQuery; + DataStructures::List specificSystems; + + void Serialize(bool writeToBitstream, BitStream *bitStream); + }; + + static int BufferedGetResponseFromServerComp(const RakNetGUID &key, BufferedGetResponseFromServer* const &data ); + struct GetRequest + { + void Clear(CloudAllocator *allocator); + bool AllRemoteServersHaveResponded(void) const; + CloudQueryWithAddresses cloudQueryWithAddresses; + + // When request started. If takes too long for a response from another system, can abort remaining systems + RakNet::Time requestStartTime; + + // Assigned by server that gets the request to identify response. See nextGetRequestId + uint32_t requestId; + + RakNetGUID requestingClient; + + DataStructures::OrderedList remoteServerResponses; + }; + static int GetRequestComp(const uint32_t &key, GetRequest* const &data ); + DataStructures::OrderedList getRequests; + RakNet::Time nextGetRequestsCheck; + + uint32_t nextGetRequestId; + + void ProcessAndTransmitGetRequest(GetRequest *getRequest); + + void ProcessCloudQueryWithAddresses( + CloudServer::CloudQueryWithAddresses &cloudQueryWithAddresses, + DataStructures::List &cloudDataResultList, + DataStructures::List &cloudKeyResultList + ); + + void SendUploadedAndSubscribedKeysToServer( RakNetGUID systemAddress ); + void SendUploadedKeyToServers( CloudKey &cloudKey ); + void SendSubscribedKeyToServers( CloudKey &cloudKey ); + void RemoveUploadedKeyFromServers( CloudKey &cloudKey ); + void RemoveSubscribedKeyFromServers( CloudKey &cloudKey ); + + void OnSendUploadedAndSubscribedKeysToServer( Packet *packet ); + void OnSendUploadedKeyToServers( Packet *packet ); + void OnSendSubscribedKeyToServers( Packet *packet ); + void OnRemoveUploadedKeyFromServers( Packet *packet ); + void OnRemoveSubscribedKeyFromServers( Packet *packet ); + void OnServerDataChanged( Packet *packet ); + + void GetServersWithUploadedKeys( + DataStructures::List &keys, + DataStructures::List &remoteServersWithData + ); + + CloudServer::CloudDataList *GetOrAllocateCloudDataList(CloudKey key, bool *dataRepositoryExists, unsigned int &dataRepositoryIndex); + + void UnsubscribeFromKey(RemoteCloudClient *remoteCloudClient, RakNetGUID remoteCloudClientGuid, unsigned int keySubscriberIndex, CloudKey &cloudKey, DataStructures::List &specificSystems); + void RemoveSpecificSubscriber(RakNetGUID specificSubscriber, CloudDataList *cloudDataList, RakNetGUID remoteCloudClientGuid); + + DataStructures::List queryFilters; + + SystemAddress forceAddress; +}; + + +} // namespace RakNet + +#endif + + +// Key subscription +// +// A given system can subscribe to one or more keys. +// The subscription can be further be defined as only subscribing to keys uploaded by or changed by a given system. +// It is possible to subscribe to keys not yet uploaded, or uploaded to another system +// +// Operations: +// +// 1. SubscribeToKey() - Get() operation with subscription +// A. Add to key subscription list for the client, which contains a keyId / specificUploaderList pair +// B. Send to remote servers that for this key, they should send us updates +// C. (Done, get operation returns current values) +// +// 2. UpdateData() - Post() operation +// A. Find all subscribers to this data, for the uploading system. +// B. Send them the uploaded data +// C. Find all servers that subscribe to this data +// D. Send them the uploaded data +// +// 3. DeleteData() - Release() operation +// A. Find all subscribers to this data, for the deleting system. +// B. Inform them of the deletion +// C. Find all servers that subscribe to this data +// D. Inform them of the deletion +// +// 4. Unsubscribe() +// A. Find this subscriber, and remove their subscription +// B. If no one else is subscribing to this key for any system, notify remote servers we no longer need subscription updates +// +// Internal operations: +// +// 1. Find if any connected client has subscribed to a given key +// A. This is used add and remove our subscription for this key to remote servers +// +// 2. For a given key and updating address, find all connected clients that care +// A. First find connected clients that have subscribed to this key, regardless of address +// B. Then find connected clients that have subscribed to this key for this particular address +// +// 3. Find all remote servers that have subscribed to a given key +// A. This is so when the key is updated or deleted, we know who to send it to +// +// 4. For a given client (such as on disconnect), remove all records of their subscriptions + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/CommandParserInterface.cpp b/project/lib_projects/raknet/jni/RaknetSources/CommandParserInterface.cpp index 33d6695..d61b41c 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CommandParserInterface.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/CommandParserInterface.cpp @@ -1,161 +1,161 @@ -#include "CommandParserInterface.h" -#include "TransportInterface.h" -#include -#include "RakAssert.h" -#include - - -#if defined(_WIN32) -// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib -// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly -#include - -#else -#include -#include -#include -#endif - -#include "LinuxStrings.h" - -using namespace RakNet; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -const unsigned char CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS=255; - -int RakNet::RegisteredCommandComp( const char* const & key, const RegisteredCommand &data ) -{ - return _stricmp(key,data.command); -} - -CommandParserInterface::CommandParserInterface() {} -CommandParserInterface::~CommandParserInterface() {} - -void CommandParserInterface::ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength) -{ - unsigned strIndex, parameterListIndex; - unsigned strLen; - bool replaceDelineator=true; - - strLen = (unsigned) strlen(str); - - // Replace every instance of delineator, \n, \r with 0 - for (strIndex=0; strIndex < strLen; strIndex++) - { - if (str[strIndex]==delineator && replaceDelineator) - str[strIndex]=0; - - if (str[strIndex]=='\n' || str[strIndex]=='\r') - str[strIndex]=0; - - if (str[strIndex]==delineatorToggle) - { - str[strIndex]=0; - replaceDelineator=!replaceDelineator; - } - } - - // Fill up parameterList starting at each non-0 - for (strIndex=0, parameterListIndex=0; strIndex < strLen; ) - { - if (str[strIndex]!=0) - { - parameterList[parameterListIndex]=str+strIndex; - parameterListIndex++; - RakAssert(parameterListIndex < parameterListLength); - if (parameterListIndex >= parameterListLength) - break; - - strIndex++; - while (str[strIndex]!=0 && strIndex < strLen) - strIndex++; - } - else - strIndex++; - } - - parameterList[parameterListIndex]=0; - *numParameters=parameterListIndex; -} -void CommandParserInterface::SendCommandList(TransportInterface *transport, const SystemAddress &systemAddress) -{ - unsigned i; - if (commandList.Size()) - { - for (i=0; i < commandList.Size(); i++) - { - transport->Send(systemAddress, "%s", commandList[i].command); - if (i < commandList.Size()-1) - transport->Send(systemAddress, ", "); - } - transport->Send(systemAddress, "\r\n"); - } - else - transport->Send(systemAddress, "No registered commands\r\n"); -} -void CommandParserInterface::RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp) -{ - RegisteredCommand rc; - rc.command=command; - rc.commandHelp=commandHelp; - rc.parameterCount=parameterCount; - commandList.Insert( command, rc, true, _FILE_AND_LINE_); -} -bool CommandParserInterface::GetRegisteredCommand(const char *command, RegisteredCommand *rc) -{ - bool objectExists; - unsigned index; - index=commandList.GetIndexFromKey(command, &objectExists); - if (objectExists) - *rc=commandList[index]; - return objectExists; -} -void CommandParserInterface::OnTransportChange(TransportInterface *transport) -{ - (void) transport; -} -void CommandParserInterface::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport) -{ - (void) systemAddress; - (void) transport; -} -void CommandParserInterface::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport) -{ - (void) systemAddress; - (void) transport; -} -void CommandParserInterface::ReturnResult(bool res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress) -{ - if (res) - transport->Send(systemAddress, "%s returned true.\r\n", command); - else - transport->Send(systemAddress, "%s returned false.\r\n", command); -} -void CommandParserInterface::ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress) -{ - transport->Send(systemAddress, "%s returned %i.\r\n", command, res); -} -void CommandParserInterface::ReturnResult(const char *command, TransportInterface *transport, const SystemAddress &systemAddress) -{ - transport->Send(systemAddress, "Successfully called %s.\r\n", command); -} -void CommandParserInterface::ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress) -{ - transport->Send(systemAddress, "%s returned %s.\r\n", command, res); -} -void CommandParserInterface::ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress) -{ - char addr[128]; - systemAddress.ToString(false,addr); - char addr2[128]; - res.ToString(false,addr2); - transport->Send(systemAddress, "%s returned %s %s:%i\r\n", command,addr,addr2,res.GetPort()); -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - +#include "CommandParserInterface.h" +#include "TransportInterface.h" +#include +#include "RakAssert.h" +#include + + +#if defined(_WIN32) +// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib +// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly +#include + +#else +#include +#include +#include +#endif + +#include "LinuxStrings.h" + +using namespace RakNet; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +const unsigned char CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS=255; + +int RakNet::RegisteredCommandComp( const char* const & key, const RegisteredCommand &data ) +{ + return _stricmp(key,data.command); +} + +CommandParserInterface::CommandParserInterface() {} +CommandParserInterface::~CommandParserInterface() {} + +void CommandParserInterface::ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength) +{ + unsigned strIndex, parameterListIndex; + unsigned strLen; + bool replaceDelineator=true; + + strLen = (unsigned) strlen(str); + + // Replace every instance of delineator, \n, \r with 0 + for (strIndex=0; strIndex < strLen; strIndex++) + { + if (str[strIndex]==delineator && replaceDelineator) + str[strIndex]=0; + + if (str[strIndex]=='\n' || str[strIndex]=='\r') + str[strIndex]=0; + + if (str[strIndex]==delineatorToggle) + { + str[strIndex]=0; + replaceDelineator=!replaceDelineator; + } + } + + // Fill up parameterList starting at each non-0 + for (strIndex=0, parameterListIndex=0; strIndex < strLen; ) + { + if (str[strIndex]!=0) + { + parameterList[parameterListIndex]=str+strIndex; + parameterListIndex++; + RakAssert(parameterListIndex < parameterListLength); + if (parameterListIndex >= parameterListLength) + break; + + strIndex++; + while (str[strIndex]!=0 && strIndex < strLen) + strIndex++; + } + else + strIndex++; + } + + parameterList[parameterListIndex]=0; + *numParameters=parameterListIndex; +} +void CommandParserInterface::SendCommandList(TransportInterface *transport, const SystemAddress &systemAddress) +{ + unsigned i; + if (commandList.Size()) + { + for (i=0; i < commandList.Size(); i++) + { + transport->Send(systemAddress, "%s", commandList[i].command); + if (i < commandList.Size()-1) + transport->Send(systemAddress, ", "); + } + transport->Send(systemAddress, "\r\n"); + } + else + transport->Send(systemAddress, "No registered commands\r\n"); +} +void CommandParserInterface::RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp) +{ + RegisteredCommand rc; + rc.command=command; + rc.commandHelp=commandHelp; + rc.parameterCount=parameterCount; + commandList.Insert( command, rc, true, _FILE_AND_LINE_); +} +bool CommandParserInterface::GetRegisteredCommand(const char *command, RegisteredCommand *rc) +{ + bool objectExists; + unsigned index; + index=commandList.GetIndexFromKey(command, &objectExists); + if (objectExists) + *rc=commandList[index]; + return objectExists; +} +void CommandParserInterface::OnTransportChange(TransportInterface *transport) +{ + (void) transport; +} +void CommandParserInterface::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport) +{ + (void) systemAddress; + (void) transport; +} +void CommandParserInterface::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport) +{ + (void) systemAddress; + (void) transport; +} +void CommandParserInterface::ReturnResult(bool res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress) +{ + if (res) + transport->Send(systemAddress, "%s returned true.\r\n", command); + else + transport->Send(systemAddress, "%s returned false.\r\n", command); +} +void CommandParserInterface::ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress) +{ + transport->Send(systemAddress, "%s returned %i.\r\n", command, res); +} +void CommandParserInterface::ReturnResult(const char *command, TransportInterface *transport, const SystemAddress &systemAddress) +{ + transport->Send(systemAddress, "Successfully called %s.\r\n", command); +} +void CommandParserInterface::ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress) +{ + transport->Send(systemAddress, "%s returned %s.\r\n", command, res); +} +void CommandParserInterface::ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress) +{ + char addr[128]; + systemAddress.ToString(false,addr); + char addr2[128]; + res.ToString(false,addr2); + transport->Send(systemAddress, "%s returned %s %s:%i\r\n", command,addr,addr2,res.GetPort()); +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/CommandParserInterface.h b/project/lib_projects/raknet/jni/RaknetSources/CommandParserInterface.h index 7a9cc5b..f795d54 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/CommandParserInterface.h +++ b/project/lib_projects/raknet/jni/RaknetSources/CommandParserInterface.h @@ -1,140 +1,140 @@ -/// \file CommandParserInterface.h -/// \brief Contains CommandParserInterface , from which you derive custom command parsers -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __COMMAND_PARSER_INTERFACE -#define __COMMAND_PARSER_INTERFACE - -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "DS_OrderedList.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class TransportInterface; - -/// \internal -/// Contains the information related to one command registered with RegisterCommand() -/// Implemented so I can have an automatic help system via SendCommandList() -struct RAK_DLL_EXPORT RegisteredCommand -{ - const char *command; - const char *commandHelp; - unsigned char parameterCount; -}; - -/// List of commands registered with RegisterCommand() -int RAK_DLL_EXPORT RegisteredCommandComp( const char* const & key, const RegisteredCommand &data ); - -/// \brief The interface used by command parsers. -/// \details CommandParserInterface provides a set of functions and interfaces that plug into the ConsoleServer class. -/// Each CommandParserInterface works at the same time as other interfaces in the system. -class RAK_DLL_EXPORT CommandParserInterface -{ -public: - CommandParserInterface(); - virtual ~CommandParserInterface(); - - /// You are responsible for overriding this function and returning a static string, which will identifier your parser. - /// This should return a static string - /// \return The name that you return. - virtual const char *GetName(void) const=0; - - /// \brief A callback for when \a systemAddress has connected to us. - /// \param[in] systemAddress The player that has connected. - /// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players. - virtual void OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport); - - /// \brief A callback for when \a systemAddress has disconnected, either gracefully or forcefully - /// \param[in] systemAddress The player that has disconnected. - /// \param[in] transport The transport interface that sent us this information. - virtual void OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport); - - /// \brief A callback for when you are expected to send a brief description of your parser to \a systemAddress - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player that requested help. - virtual void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress)=0; - - /// \brief Given \a command with parameters \a parameterList , do whatever processing you wish. - /// \param[in] command The command to process - /// \param[in] numParameters How many parameters were passed along with the command - /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player that sent this command. - /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing - virtual bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString)=0; - - /// \brief This is called every time transport interface is registered. - /// \details If you want to save a copy of the TransportInterface pointer - /// This is the place to do it - /// \param[in] transport The new TransportInterface - virtual void OnTransportChange(TransportInterface *transport); - - /// \internal - /// Scan commandList and return the associated array - /// \param[in] command The string to find - /// \param[out] rc Contains the result of this operation - /// \return True if we found the command, false otherwise - virtual bool GetRegisteredCommand(const char *command, RegisteredCommand *rc); - - /// \internal - /// Goes through str, replacing the delineating character with 0's. - /// \param[in] str The string sent by the transport interface - /// \param[in] delineator The character to scan for to use as a delineator - /// \param[in] delineatorToggle When encountered the delineator replacement is toggled on and off - /// \param[out] numParameters How many pointers were written to \a parameterList - /// \param[out] parameterList An array of pointers to characters. Will hold pointers to locations inside \a str - /// \param[in] parameterListLength How big the \a parameterList array is - static void ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength); - - /// \internal - /// Goes through the variable commandList and sends the command portion of each struct - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player to write to - virtual void SendCommandList(TransportInterface *transport, const SystemAddress &systemAddress); - - static const unsigned char VARIABLE_NUMBER_OF_PARAMETERS; - - // Currently only takes static strings - doesn't make a copy of what you pass. - // parameterCount is the number of parameters that the sender has to include with the command. - // Pass 255 to parameterCount to indicate variable number of parameters - - /// Registers a command. - /// \param[in] parameterCount How many parameters your command requires. If you want to accept a variable number of commands, pass CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS - /// \param[in] command A pointer to a STATIC string that has your command. I keep a copy of the pointer here so don't deallocate the string. - /// \param[in] commandHelp A pointer to a STATIC string that has the help information for your command. I keep a copy of the pointer here so don't deallocate the string. - virtual void RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp); - - /// \brief Just writes a string to the remote system based on the result ( \a res ) of your operation - /// \details This is not necessary to call, but makes it easier to return results of function calls. - /// \param[in] res The result to write - /// \param[in] command The command that this result came from - /// \param[in] transport The transport interface that will be written to - /// \param[in] systemAddress The player this result will be sent to - virtual void ReturnResult(bool res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); - virtual void ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); - virtual void ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); - virtual void ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress); - - /// \brief Just writes a string to the remote system when you are calling a function that has no return value. - /// \details This is not necessary to call, but makes it easier to return results of function calls. - /// \param[in] res The result to write - /// \param[in] command The command that this result came from - /// \param[in] transport The transport interface that will be written to - /// \param[in] systemAddress The player this result will be sent to - virtual void ReturnResult(const char *command,TransportInterface *transport, const SystemAddress &systemAddress); - -protected: - DataStructures::OrderedList commandList; -}; - -} // namespace RakNet - -#endif - +/// \file CommandParserInterface.h +/// \brief Contains CommandParserInterface , from which you derive custom command parsers +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __COMMAND_PARSER_INTERFACE +#define __COMMAND_PARSER_INTERFACE + +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "DS_OrderedList.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class TransportInterface; + +/// \internal +/// Contains the information related to one command registered with RegisterCommand() +/// Implemented so I can have an automatic help system via SendCommandList() +struct RAK_DLL_EXPORT RegisteredCommand +{ + const char *command; + const char *commandHelp; + unsigned char parameterCount; +}; + +/// List of commands registered with RegisterCommand() +int RAK_DLL_EXPORT RegisteredCommandComp( const char* const & key, const RegisteredCommand &data ); + +/// \brief The interface used by command parsers. +/// \details CommandParserInterface provides a set of functions and interfaces that plug into the ConsoleServer class. +/// Each CommandParserInterface works at the same time as other interfaces in the system. +class RAK_DLL_EXPORT CommandParserInterface +{ +public: + CommandParserInterface(); + virtual ~CommandParserInterface(); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + virtual const char *GetName(void) const=0; + + /// \brief A callback for when \a systemAddress has connected to us. + /// \param[in] systemAddress The player that has connected. + /// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players. + virtual void OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport); + + /// \brief A callback for when \a systemAddress has disconnected, either gracefully or forcefully + /// \param[in] systemAddress The player that has disconnected. + /// \param[in] transport The transport interface that sent us this information. + virtual void OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport); + + /// \brief A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + virtual void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress)=0; + + /// \brief Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + virtual bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString)=0; + + /// \brief This is called every time transport interface is registered. + /// \details If you want to save a copy of the TransportInterface pointer + /// This is the place to do it + /// \param[in] transport The new TransportInterface + virtual void OnTransportChange(TransportInterface *transport); + + /// \internal + /// Scan commandList and return the associated array + /// \param[in] command The string to find + /// \param[out] rc Contains the result of this operation + /// \return True if we found the command, false otherwise + virtual bool GetRegisteredCommand(const char *command, RegisteredCommand *rc); + + /// \internal + /// Goes through str, replacing the delineating character with 0's. + /// \param[in] str The string sent by the transport interface + /// \param[in] delineator The character to scan for to use as a delineator + /// \param[in] delineatorToggle When encountered the delineator replacement is toggled on and off + /// \param[out] numParameters How many pointers were written to \a parameterList + /// \param[out] parameterList An array of pointers to characters. Will hold pointers to locations inside \a str + /// \param[in] parameterListLength How big the \a parameterList array is + static void ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength); + + /// \internal + /// Goes through the variable commandList and sends the command portion of each struct + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player to write to + virtual void SendCommandList(TransportInterface *transport, const SystemAddress &systemAddress); + + static const unsigned char VARIABLE_NUMBER_OF_PARAMETERS; + + // Currently only takes static strings - doesn't make a copy of what you pass. + // parameterCount is the number of parameters that the sender has to include with the command. + // Pass 255 to parameterCount to indicate variable number of parameters + + /// Registers a command. + /// \param[in] parameterCount How many parameters your command requires. If you want to accept a variable number of commands, pass CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS + /// \param[in] command A pointer to a STATIC string that has your command. I keep a copy of the pointer here so don't deallocate the string. + /// \param[in] commandHelp A pointer to a STATIC string that has the help information for your command. I keep a copy of the pointer here so don't deallocate the string. + virtual void RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp); + + /// \brief Just writes a string to the remote system based on the result ( \a res ) of your operation + /// \details This is not necessary to call, but makes it easier to return results of function calls. + /// \param[in] res The result to write + /// \param[in] command The command that this result came from + /// \param[in] transport The transport interface that will be written to + /// \param[in] systemAddress The player this result will be sent to + virtual void ReturnResult(bool res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); + virtual void ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); + virtual void ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); + virtual void ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress); + + /// \brief Just writes a string to the remote system when you are calling a function that has no return value. + /// \details This is not necessary to call, but makes it easier to return results of function calls. + /// \param[in] res The result to write + /// \param[in] command The command that this result came from + /// \param[in] transport The transport interface that will be written to + /// \param[in] systemAddress The player this result will be sent to + virtual void ReturnResult(const char *command,TransportInterface *transport, const SystemAddress &systemAddress); + +protected: + DataStructures::OrderedList commandList; +}; + +} // namespace RakNet + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/ConnectionGraph2.cpp b/project/lib_projects/raknet/jni/RaknetSources/ConnectionGraph2.cpp index 683c28f..5950246 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ConnectionGraph2.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/ConnectionGraph2.cpp @@ -1,299 +1,299 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ConnectionGraph2==1 - -#include "ConnectionGraph2.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(ConnectionGraph2,ConnectionGraph2); - -int RakNet::ConnectionGraph2::RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data ) -{ - if (key < data->guid) - return -1; - if (key > data->guid) - return 1; - return 0; -} - -int RakNet::ConnectionGraph2::SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data ) -{ - if (key.guiddata.guid) - return 1; - return 0; -} -ConnectionGraph2::ConnectionGraph2() -{ - autoProcessNewConnections=true; -} -ConnectionGraph2::~ConnectionGraph2() -{ - -} -bool ConnectionGraph2::GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength) -{ - if ((saOut==0 && guidOut==0) || outLength==0 || *outLength==0 || remoteSystemGuid==UNASSIGNED_RAKNET_GUID) - { - *outLength=0; - return false; - } - - bool objectExists; - unsigned int idx = remoteSystems.GetIndexFromKey(remoteSystemGuid, &objectExists); - if (objectExists==false) - { - *outLength=0; - return false; - } - - unsigned int idx2; - if (remoteSystems[idx]->remoteConnections.Size() < *outLength) - *outLength=remoteSystems[idx]->remoteConnections.Size(); - for (idx2=0; idx2 < *outLength; idx2++) - { - if (guidOut) - guidOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].guid; - if (saOut) - saOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].systemAddress; - } - return true; -} -bool ConnectionGraph2::ConnectionExists(RakNetGUID g1, RakNetGUID g2) -{ - if (g1==g2) - return false; - - bool objectExists; - unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists); - if (objectExists==false) - { - return false; - } - SystemAddressAndGuid sag; - sag.guid=g2; - return remoteSystems[idx]->remoteConnections.HasData(sag); -} -uint16_t ConnectionGraph2::GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const -{ - if (g1==g2) - return 0; - - if (g1==rakPeerInterface->GetMyGUID()) - return (uint16_t) rakPeerInterface->GetAveragePing(g2); - if (g2==rakPeerInterface->GetMyGUID()) - return (uint16_t) rakPeerInterface->GetAveragePing(g1); - - bool objectExists; - unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists); - if (objectExists==false) - { - return (uint16_t) -1; - } - - SystemAddressAndGuid sag; - sag.guid=g2; - unsigned int idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(sag, &objectExists); - if (objectExists==false) - { - return (uint16_t) -1; - } - return remoteSystems[idx]->remoteConnections[idx2].sendersPingToThatSystem; -} - -/// Returns the system with the lowest total ping among all its connections. This can be used as the 'best host' for a peer to peer session -RakNetGUID ConnectionGraph2::GetLowestAveragePingSystem(void) const -{ - float lowestPing=-1.0; - unsigned int lowestPingIdx=(unsigned int) -1; - float thisAvePing=0.0f; - unsigned int idx, idx2; - int ap, count=0; - - for (idx=0; idxGetAveragePing(remoteSystems[idx]->guid); - if (ap!=-1) - { - thisAvePing+=(float) ap; - count++; - } - } - - if (count>0) - { - lowestPing=thisAvePing/count; - } - - for (idx=0; idxremoteConnections.Size(); idx2++) - { - ap=remoteSystem->remoteConnections[idx2].sendersPingToThatSystem; - if (ap!=-1) - { - thisAvePing+=(float) ap; - count++; - } - } - - if (count>0 && (lowestPing==-1.0f || thisAvePing/count < lowestPing)) - { - lowestPing=thisAvePing/count; - lowestPingIdx=idx; - } - } - - if (lowestPingIdx==(unsigned int) -1) - return rakPeerInterface->GetMyGUID(); - return remoteSystems[lowestPingIdx]->guid; -} - -void ConnectionGraph2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - // Send notice to all existing connections - RakNet::BitStream bs; - if (lostConnectionReason==LCR_CONNECTION_LOST) - bs.Write((MessageID)ID_REMOTE_CONNECTION_LOST); - else - bs.Write((MessageID)ID_REMOTE_DISCONNECTION_NOTIFICATION); - bs.Write(systemAddress); - bs.Write(rakNetGUID); - SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); - - bool objectExists; - unsigned int idx = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists) - { - RakNet::OP_DELETE(remoteSystems[idx],_FILE_AND_LINE_); - remoteSystems.RemoveAtIndex(idx); - } -} -void ConnectionGraph2::SetAutoProcessNewConnections(bool b) -{ - autoProcessNewConnections=b; -} -bool ConnectionGraph2::GetAutoProcessNewConnections(void) const -{ - return autoProcessNewConnections; -} -void ConnectionGraph2::AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID) -{ - // Relay the new connection to other systems. - RakNet::BitStream bs; - bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); - bs.Write((uint32_t)1); - bs.Write(systemAddress); - bs.Write(rakNetGUID); - bs.WriteCasted(rakPeerInterface->GetAveragePing(rakNetGUID)); - SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); - - // Send everyone to the new guy - DataStructures::List addresses; - DataStructures::List guids; - rakPeerInterface->GetSystemList(addresses, guids); - bs.Reset(); - bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); - BitSize_t writeOffset = bs.GetWriteOffset(); - bs.Write((uint32_t) addresses.Size()); - - unsigned int i; - uint32_t count=0; - for (i=0; i < addresses.Size(); i++) - { - if (addresses[i]==systemAddress) - continue; - - bs.Write(addresses[i]); - bs.Write(guids[i]); - bs.WriteCasted(rakPeerInterface->GetAveragePing(guids[i])); - count++; - } - - if (count>0) - { - BitSize_t writeOffset2 = bs.GetWriteOffset(); - bs.SetWriteOffset(writeOffset); - bs.Write(count); - bs.SetWriteOffset(writeOffset2); - SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false); - } - - bool objectExists; - unsigned int ii = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists==false) - { - RemoteSystem* remoteSystem = RakNet::OP_NEW(_FILE_AND_LINE_); - remoteSystem->guid=rakNetGUID; - remoteSystems.InsertAtIndex(remoteSystem,ii,_FILE_AND_LINE_); - } -} -void ConnectionGraph2::GetParticipantList(DataStructures::OrderedList &participantList) -{ - participantList.Clear(true, _FILE_AND_LINE_); - unsigned int i; - for (i=0; i < remoteSystems.Size(); i++) - participantList.InsertAtEnd(remoteSystems[i]->guid, _FILE_AND_LINE_); -} -void ConnectionGraph2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) isIncoming; - if (autoProcessNewConnections) - AddParticipant(systemAddress, rakNetGUID); -} -PluginReceiveResult ConnectionGraph2::OnReceive(Packet *packet) -{ - if (packet->data[0]==ID_REMOTE_CONNECTION_LOST || packet->data[0]==ID_REMOTE_DISCONNECTION_NOTIFICATION) - { - bool objectExists; - unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists) - { - RakNet::BitStream bs(packet->data,packet->length,false); - bs.IgnoreBytes(1); - SystemAddressAndGuid saag; - bs.Read(saag.systemAddress); - bs.Read(saag.guid); - unsigned long idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists); - if (objectExists) - remoteSystems[idx]->remoteConnections.RemoveAtIndex(idx2); - } - } - else if (packet->data[0]==ID_REMOTE_NEW_INCOMING_CONNECTION) - { - bool objectExists; - unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists) - { - uint32_t numAddresses; - RakNet::BitStream bs(packet->data,packet->length,false); - bs.IgnoreBytes(1); - bs.Read(numAddresses); - for (unsigned int idx2=0; idx2 < numAddresses; idx2++) - { - SystemAddressAndGuid saag; - bs.Read(saag.systemAddress); - bs.Read(saag.guid); - bs.Read(saag.sendersPingToThatSystem); - bool objectExists; - unsigned int ii = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists); - if (objectExists==false) - remoteSystems[idx]->remoteConnections.InsertAtIndex(saag,ii,_FILE_AND_LINE_); - } - } - } - - return RR_CONTINUE_PROCESSING; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ConnectionGraph2==1 + +#include "ConnectionGraph2.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(ConnectionGraph2,ConnectionGraph2); + +int RakNet::ConnectionGraph2::RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data ) +{ + if (key < data->guid) + return -1; + if (key > data->guid) + return 1; + return 0; +} + +int RakNet::ConnectionGraph2::SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data ) +{ + if (key.guiddata.guid) + return 1; + return 0; +} +ConnectionGraph2::ConnectionGraph2() +{ + autoProcessNewConnections=true; +} +ConnectionGraph2::~ConnectionGraph2() +{ + +} +bool ConnectionGraph2::GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength) +{ + if ((saOut==0 && guidOut==0) || outLength==0 || *outLength==0 || remoteSystemGuid==UNASSIGNED_RAKNET_GUID) + { + *outLength=0; + return false; + } + + bool objectExists; + unsigned int idx = remoteSystems.GetIndexFromKey(remoteSystemGuid, &objectExists); + if (objectExists==false) + { + *outLength=0; + return false; + } + + unsigned int idx2; + if (remoteSystems[idx]->remoteConnections.Size() < *outLength) + *outLength=remoteSystems[idx]->remoteConnections.Size(); + for (idx2=0; idx2 < *outLength; idx2++) + { + if (guidOut) + guidOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].guid; + if (saOut) + saOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].systemAddress; + } + return true; +} +bool ConnectionGraph2::ConnectionExists(RakNetGUID g1, RakNetGUID g2) +{ + if (g1==g2) + return false; + + bool objectExists; + unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists); + if (objectExists==false) + { + return false; + } + SystemAddressAndGuid sag; + sag.guid=g2; + return remoteSystems[idx]->remoteConnections.HasData(sag); +} +uint16_t ConnectionGraph2::GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const +{ + if (g1==g2) + return 0; + + if (g1==rakPeerInterface->GetMyGUID()) + return (uint16_t) rakPeerInterface->GetAveragePing(g2); + if (g2==rakPeerInterface->GetMyGUID()) + return (uint16_t) rakPeerInterface->GetAveragePing(g1); + + bool objectExists; + unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists); + if (objectExists==false) + { + return (uint16_t) -1; + } + + SystemAddressAndGuid sag; + sag.guid=g2; + unsigned int idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(sag, &objectExists); + if (objectExists==false) + { + return (uint16_t) -1; + } + return remoteSystems[idx]->remoteConnections[idx2].sendersPingToThatSystem; +} + +/// Returns the system with the lowest total ping among all its connections. This can be used as the 'best host' for a peer to peer session +RakNetGUID ConnectionGraph2::GetLowestAveragePingSystem(void) const +{ + float lowestPing=-1.0; + unsigned int lowestPingIdx=(unsigned int) -1; + float thisAvePing=0.0f; + unsigned int idx, idx2; + int ap, count=0; + + for (idx=0; idxGetAveragePing(remoteSystems[idx]->guid); + if (ap!=-1) + { + thisAvePing+=(float) ap; + count++; + } + } + + if (count>0) + { + lowestPing=thisAvePing/count; + } + + for (idx=0; idxremoteConnections.Size(); idx2++) + { + ap=remoteSystem->remoteConnections[idx2].sendersPingToThatSystem; + if (ap!=-1) + { + thisAvePing+=(float) ap; + count++; + } + } + + if (count>0 && (lowestPing==-1.0f || thisAvePing/count < lowestPing)) + { + lowestPing=thisAvePing/count; + lowestPingIdx=idx; + } + } + + if (lowestPingIdx==(unsigned int) -1) + return rakPeerInterface->GetMyGUID(); + return remoteSystems[lowestPingIdx]->guid; +} + +void ConnectionGraph2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + // Send notice to all existing connections + RakNet::BitStream bs; + if (lostConnectionReason==LCR_CONNECTION_LOST) + bs.Write((MessageID)ID_REMOTE_CONNECTION_LOST); + else + bs.Write((MessageID)ID_REMOTE_DISCONNECTION_NOTIFICATION); + bs.Write(systemAddress); + bs.Write(rakNetGUID); + SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); + + bool objectExists; + unsigned int idx = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists) + { + RakNet::OP_DELETE(remoteSystems[idx],_FILE_AND_LINE_); + remoteSystems.RemoveAtIndex(idx); + } +} +void ConnectionGraph2::SetAutoProcessNewConnections(bool b) +{ + autoProcessNewConnections=b; +} +bool ConnectionGraph2::GetAutoProcessNewConnections(void) const +{ + return autoProcessNewConnections; +} +void ConnectionGraph2::AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID) +{ + // Relay the new connection to other systems. + RakNet::BitStream bs; + bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); + bs.Write((uint32_t)1); + bs.Write(systemAddress); + bs.Write(rakNetGUID); + bs.WriteCasted(rakPeerInterface->GetAveragePing(rakNetGUID)); + SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); + + // Send everyone to the new guy + DataStructures::List addresses; + DataStructures::List guids; + rakPeerInterface->GetSystemList(addresses, guids); + bs.Reset(); + bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); + BitSize_t writeOffset = bs.GetWriteOffset(); + bs.Write((uint32_t) addresses.Size()); + + unsigned int i; + uint32_t count=0; + for (i=0; i < addresses.Size(); i++) + { + if (addresses[i]==systemAddress) + continue; + + bs.Write(addresses[i]); + bs.Write(guids[i]); + bs.WriteCasted(rakPeerInterface->GetAveragePing(guids[i])); + count++; + } + + if (count>0) + { + BitSize_t writeOffset2 = bs.GetWriteOffset(); + bs.SetWriteOffset(writeOffset); + bs.Write(count); + bs.SetWriteOffset(writeOffset2); + SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false); + } + + bool objectExists; + unsigned int ii = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists==false) + { + RemoteSystem* remoteSystem = RakNet::OP_NEW(_FILE_AND_LINE_); + remoteSystem->guid=rakNetGUID; + remoteSystems.InsertAtIndex(remoteSystem,ii,_FILE_AND_LINE_); + } +} +void ConnectionGraph2::GetParticipantList(DataStructures::OrderedList &participantList) +{ + participantList.Clear(true, _FILE_AND_LINE_); + unsigned int i; + for (i=0; i < remoteSystems.Size(); i++) + participantList.InsertAtEnd(remoteSystems[i]->guid, _FILE_AND_LINE_); +} +void ConnectionGraph2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) isIncoming; + if (autoProcessNewConnections) + AddParticipant(systemAddress, rakNetGUID); +} +PluginReceiveResult ConnectionGraph2::OnReceive(Packet *packet) +{ + if (packet->data[0]==ID_REMOTE_CONNECTION_LOST || packet->data[0]==ID_REMOTE_DISCONNECTION_NOTIFICATION) + { + bool objectExists; + unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists) + { + RakNet::BitStream bs(packet->data,packet->length,false); + bs.IgnoreBytes(1); + SystemAddressAndGuid saag; + bs.Read(saag.systemAddress); + bs.Read(saag.guid); + unsigned long idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists); + if (objectExists) + remoteSystems[idx]->remoteConnections.RemoveAtIndex(idx2); + } + } + else if (packet->data[0]==ID_REMOTE_NEW_INCOMING_CONNECTION) + { + bool objectExists; + unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists) + { + uint32_t numAddresses; + RakNet::BitStream bs(packet->data,packet->length,false); + bs.IgnoreBytes(1); + bs.Read(numAddresses); + for (unsigned int idx2=0; idx2 < numAddresses; idx2++) + { + SystemAddressAndGuid saag; + bs.Read(saag.systemAddress); + bs.Read(saag.guid); + bs.Read(saag.sendersPingToThatSystem); + bool objectExists; + unsigned int ii = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists); + if (objectExists==false) + remoteSystems[idx]->remoteConnections.InsertAtIndex(saag,ii,_FILE_AND_LINE_); + } + } + } + + return RR_CONTINUE_PROCESSING; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/ConnectionGraph2.h b/project/lib_projects/raknet/jni/RaknetSources/ConnectionGraph2.h index b639879..d5fa99a 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ConnectionGraph2.h +++ b/project/lib_projects/raknet/jni/RaknetSources/ConnectionGraph2.h @@ -1,118 +1,118 @@ -/// \file ConnectionGraph2.h -/// \brief Connection graph plugin, version 2. Tells new systems about existing and new connections -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ConnectionGraph2==1 - -#ifndef __CONNECTION_GRAPH_2_H -#define __CONNECTION_GRAPH_2_H - -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "DS_List.h" -#include "DS_WeightedGraph.h" -#include "GetTime.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \brief A one hop connection graph. -/// \details Sends ID_REMOTE_CONNECTION_LOST, ID_REMOTE_DISCONNECTION_NOTIFICATION, ID_REMOTE_NEW_INCOMING_CONNECTION
-/// All identifiers are followed by SystemAddress, then RakNetGUID -/// Also stores the list for you, which you can access with GetConnectionListForRemoteSystem -/// \ingroup CONNECTION_GRAPH_GROUP -class RAK_DLL_EXPORT ConnectionGraph2 : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(ConnectionGraph2) - - ConnectionGraph2(); - ~ConnectionGraph2(); - - /// \brief Given a remote system identified by RakNetGUID, return the list of SystemAddresses and RakNetGUIDs they are connected to - /// \param[in] remoteSystemGuid Which system we are referring to. This only works for remote systems, not ourselves. - /// \param[out] saOut A preallocated array to hold the output list of SystemAddress. Can be 0 if you don't care. - /// \param[out] guidOut A preallocated array to hold the output list of RakNetGUID. Can be 0 if you don't care. - /// \param[in,out] outLength On input, the size of \a saOut and \a guidOut. On output, modified to reflect the number of elements actually written - /// \return True if \a remoteSystemGuid was found. Otherwise false, and \a saOut, \a guidOut remain unchanged. \a outLength will be set to 0. - bool GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength); - - /// Returns if g1 is connected to g2 - bool ConnectionExists(RakNetGUID g1, RakNetGUID g2); - - /// Returns the average ping between two systems in the connection graph. Returns -1 if no connection exists between those systems - uint16_t GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const; - - /// Returns the system with the lowest average ping among all its connections. - /// If you need one system in the peer to peer group to relay data, have the FullyConnectedMesh2 host call this function after host migration, and use that system - RakNetGUID GetLowestAveragePingSystem(void) const; - - /// \brief If called with false, then new connections are only added to the connection graph when you call ProcessNewConnection(); - /// \details This is useful if you want to perform validation before connecting a system to a mesh, or if you want a submesh (for example a server cloud) - /// \param[in] b True to automatically call ProcessNewConnection() on any new connection, false to not do so. Defaults to true. - void SetAutoProcessNewConnections(bool b); - - /// \brief Returns value passed to SetAutoProcessNewConnections() - /// \return Value passed to SetAutoProcessNewConnections(), or the default of true if it was never called - bool GetAutoProcessNewConnections(void) const; - - /// \brief If you call SetAutoProcessNewConnections(false);, then you will need to manually call ProcessNewConnection() on new connections - /// \details On ID_NEW_INCOMING_CONNECTION or ID_CONNECTION_REQUEST_ACCEPTED, adds that system to the graph - /// Do not call ProcessNewConnection() manually otherwise - /// \param[in] The packet->SystemAddress member - /// \param[in] The packet->guid member - void AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID); - - /// Get the participants added with AddParticipant() - /// \param[out] participantList Participants added with AddParticipant(); - void GetParticipantList(DataStructures::OrderedList &participantList); - - /// \internal - struct SystemAddressAndGuid - { - SystemAddress systemAddress; - RakNetGUID guid; - uint16_t sendersPingToThatSystem; - }; - /// \internal - static int SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data ); - - /// \internal - struct RemoteSystem - { - DataStructures::OrderedList remoteConnections; - RakNetGUID guid; - }; - /// \internal - static int RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data ); - -protected: - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - - // List of systems I am connected to, which in turn stores which systems they are connected to - DataStructures::OrderedList remoteSystems; - - bool autoProcessNewConnections; - -}; - -} // namespace RakNet - -#endif // #ifndef __CONNECTION_GRAPH_2_H - -#endif // _RAKNET_SUPPORT_* +/// \file ConnectionGraph2.h +/// \brief Connection graph plugin, version 2. Tells new systems about existing and new connections +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ConnectionGraph2==1 + +#ifndef __CONNECTION_GRAPH_2_H +#define __CONNECTION_GRAPH_2_H + +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "DS_List.h" +#include "DS_WeightedGraph.h" +#include "GetTime.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief A one hop connection graph. +/// \details Sends ID_REMOTE_CONNECTION_LOST, ID_REMOTE_DISCONNECTION_NOTIFICATION, ID_REMOTE_NEW_INCOMING_CONNECTION
+/// All identifiers are followed by SystemAddress, then RakNetGUID +/// Also stores the list for you, which you can access with GetConnectionListForRemoteSystem +/// \ingroup CONNECTION_GRAPH_GROUP +class RAK_DLL_EXPORT ConnectionGraph2 : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(ConnectionGraph2) + + ConnectionGraph2(); + ~ConnectionGraph2(); + + /// \brief Given a remote system identified by RakNetGUID, return the list of SystemAddresses and RakNetGUIDs they are connected to + /// \param[in] remoteSystemGuid Which system we are referring to. This only works for remote systems, not ourselves. + /// \param[out] saOut A preallocated array to hold the output list of SystemAddress. Can be 0 if you don't care. + /// \param[out] guidOut A preallocated array to hold the output list of RakNetGUID. Can be 0 if you don't care. + /// \param[in,out] outLength On input, the size of \a saOut and \a guidOut. On output, modified to reflect the number of elements actually written + /// \return True if \a remoteSystemGuid was found. Otherwise false, and \a saOut, \a guidOut remain unchanged. \a outLength will be set to 0. + bool GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength); + + /// Returns if g1 is connected to g2 + bool ConnectionExists(RakNetGUID g1, RakNetGUID g2); + + /// Returns the average ping between two systems in the connection graph. Returns -1 if no connection exists between those systems + uint16_t GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const; + + /// Returns the system with the lowest average ping among all its connections. + /// If you need one system in the peer to peer group to relay data, have the FullyConnectedMesh2 host call this function after host migration, and use that system + RakNetGUID GetLowestAveragePingSystem(void) const; + + /// \brief If called with false, then new connections are only added to the connection graph when you call ProcessNewConnection(); + /// \details This is useful if you want to perform validation before connecting a system to a mesh, or if you want a submesh (for example a server cloud) + /// \param[in] b True to automatically call ProcessNewConnection() on any new connection, false to not do so. Defaults to true. + void SetAutoProcessNewConnections(bool b); + + /// \brief Returns value passed to SetAutoProcessNewConnections() + /// \return Value passed to SetAutoProcessNewConnections(), or the default of true if it was never called + bool GetAutoProcessNewConnections(void) const; + + /// \brief If you call SetAutoProcessNewConnections(false);, then you will need to manually call ProcessNewConnection() on new connections + /// \details On ID_NEW_INCOMING_CONNECTION or ID_CONNECTION_REQUEST_ACCEPTED, adds that system to the graph + /// Do not call ProcessNewConnection() manually otherwise + /// \param[in] The packet->SystemAddress member + /// \param[in] The packet->guid member + void AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID); + + /// Get the participants added with AddParticipant() + /// \param[out] participantList Participants added with AddParticipant(); + void GetParticipantList(DataStructures::OrderedList &participantList); + + /// \internal + struct SystemAddressAndGuid + { + SystemAddress systemAddress; + RakNetGUID guid; + uint16_t sendersPingToThatSystem; + }; + /// \internal + static int SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data ); + + /// \internal + struct RemoteSystem + { + DataStructures::OrderedList remoteConnections; + RakNetGUID guid; + }; + /// \internal + static int RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data ); + +protected: + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + + // List of systems I am connected to, which in turn stores which systems they are connected to + DataStructures::OrderedList remoteSystems; + + bool autoProcessNewConnections; + +}; + +} // namespace RakNet + +#endif // #ifndef __CONNECTION_GRAPH_2_H + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/ConsoleServer.cpp b/project/lib_projects/raknet/jni/RaknetSources/ConsoleServer.cpp index 1b20dfe..dea8703 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ConsoleServer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/ConsoleServer.cpp @@ -1,311 +1,311 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ConsoleServer==1 - -#include "ConsoleServer.h" -#include "TransportInterface.h" -#include "CommandParserInterface.h" -#include -#include - -#define COMMAND_DELINATOR ' ' -#define COMMAND_DELINATOR_TOGGLE '"' - -#include "LinuxStrings.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(ConsoleServer,ConsoleServer); - -ConsoleServer::ConsoleServer() -{ - transport=0; - password[0]=0; - prompt=0; -} -ConsoleServer::~ConsoleServer() -{ - if (prompt) - rakFree_Ex(prompt, _FILE_AND_LINE_); -} -void ConsoleServer::SetTransportProvider(TransportInterface *transportInterface, unsigned short port) -{ - // Replace the current TransportInterface, stopping the old one, if present, and starting the new one. - if (transportInterface) - { - if (transport) - { - RemoveCommandParser(transport->GetCommandParser()); - transport->Stop(); - } - transport=transportInterface; - transport->Start(port, true); - - unsigned i; - for (i=0; i < commandParserList.Size(); i++) - commandParserList[i]->OnTransportChange(transport); - - // The transport itself might have a command parser - for example password for the RakNet transport - AddCommandParser(transport->GetCommandParser()); - } -} -void ConsoleServer::AddCommandParser(CommandParserInterface *commandParserInterface) -{ - if (commandParserInterface==0) - return; - - // Non-duplicate insertion - unsigned i; - for (i=0; i < commandParserList.Size(); i++) - { - if (commandParserList[i]==commandParserInterface) - return; - - if (_stricmp(commandParserList[i]->GetName(), commandParserInterface->GetName())==0) - { - // Naming conflict between two command parsers - RakAssert(0); - return; - } - } - - commandParserList.Insert(commandParserInterface, _FILE_AND_LINE_); - if (transport) - commandParserInterface->OnTransportChange(transport); -} -void ConsoleServer::RemoveCommandParser(CommandParserInterface *commandParserInterface) -{ - if (commandParserInterface==0) - return; - - // Overwrite the element we are removing from the back of the list and delete the back of the list - unsigned i; - for (i=0; i < commandParserList.Size(); i++) - { - if (commandParserList[i]==commandParserInterface) - { - commandParserList[i]=commandParserList[commandParserList.Size()-1]; - commandParserList.RemoveFromEnd(); - return; - } - } -} -void ConsoleServer::Update(void) -{ - unsigned i; - char *parameterList[20]; // Up to 20 parameters - unsigned numParameters; - RakNet::SystemAddress newOrLostConnectionId; - RakNet::Packet *p; - RakNet::RegisteredCommand rc; - - p = transport->Receive(); - newOrLostConnectionId=transport->HasNewIncomingConnection(); - - if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < commandParserList.Size(); i++) - { - commandParserList[i]->OnNewIncomingConnection(newOrLostConnectionId, transport); - } - - transport->Send(newOrLostConnectionId, "Connected to remote command console.\r\nType 'help' for help.\r\n"); - ListParsers(newOrLostConnectionId); - ShowPrompt(newOrLostConnectionId); - } - - newOrLostConnectionId=transport->HasLostConnection(); - if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < commandParserList.Size(); i++) - commandParserList[i]->OnConnectionLost(newOrLostConnectionId, transport); - } - - while (p) - { - bool commandParsed=false; - char copy[REMOTE_MAX_TEXT_INPUT]; - memcpy(copy, p->data, p->length); - copy[p->length]=0; - RakNet::CommandParserInterface::ParseConsoleString((char*)p->data, COMMAND_DELINATOR, COMMAND_DELINATOR_TOGGLE, &numParameters, parameterList, 20); // Up to 20 parameters - if (numParameters==0) - { - transport->DeallocatePacket(p); - p = transport->Receive(); - continue; - } - if (_stricmp(*parameterList, "help")==0 && numParameters<=2) - { - // Find the parser specified and display help for it - if (numParameters==1) - { - transport->Send(p->systemAddress, "\r\nINSTRUCTIONS:\r\n"); - transport->Send(p->systemAddress, "Enter commands on your keyboard, using spaces to delineate parameters.\r\n"); - transport->Send(p->systemAddress, "You can use quotation marks to toggle space delineation.\r\n"); - transport->Send(p->systemAddress, "You can connect multiple times from the same computer.\r\n"); - transport->Send(p->systemAddress, "You can direct commands to a parser by prefixing the parser name or number.\r\n"); - transport->Send(p->systemAddress, "COMMANDS:\r\n"); - transport->Send(p->systemAddress, "help Show this display.\r\n"); - transport->Send(p->systemAddress, "help Show help on a particular parser.\r\n"); - transport->Send(p->systemAddress, "help Show help on a particular command.\r\n"); - transport->Send(p->systemAddress, "quit Disconnects from the server.\r\n"); - transport->Send(p->systemAddress, "[] [] Execute a command\r\n"); - transport->Send(p->systemAddress, "[] [] Execute a command\r\n"); - ListParsers(p->systemAddress); - //ShowPrompt(p->systemAddress); - } - else // numParameters == 2, including the help tag - { - for (i=0; i < commandParserList.Size(); i++) - { - if (_stricmp(parameterList[1], commandParserList[i]->GetName())==0) - { - commandParsed=true; - commandParserList[i]->SendHelp(transport, p->systemAddress); - transport->Send(p->systemAddress, "COMMAND LIST:\r\n"); - commandParserList[i]->SendCommandList(transport, p->systemAddress); - transport->Send(p->systemAddress, "\r\n"); - break; - } - } - - if (commandParsed==false) - { - // Try again, for all commands for all parsers. - RakNet::RegisteredCommand rc; - for (i=0; i < commandParserList.Size(); i++) - { - if (commandParserList[i]->GetRegisteredCommand(parameterList[1], &rc)) - { - if (rc.parameterCount==RakNet::CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS) - transport->Send(p->systemAddress, "(Variable parms): %s %s\r\n", rc.command, rc.commandHelp); - else - transport->Send(p->systemAddress, "(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); - commandParsed=true; - break; - } - } - } - - if (commandParsed==false) - { - // Don't know what to do - transport->Send(p->systemAddress, "Unknown help topic: %s.\r\n", parameterList[1]); - } - //ShowPrompt(p->systemAddress); - } - } - else if (_stricmp(*parameterList, "quit")==0 && numParameters==1) - { - transport->Send(p->systemAddress, "Goodbye!\r\n"); - transport->CloseConnection(p->systemAddress); - } - else - { - bool tryAllParsers=true; - bool failed=false; - - if (numParameters >=2) // At minimum - { - unsigned commandParserIndex=(unsigned)-1; - // Prefixing with numbers directs to a particular parser - if (**parameterList>='0' && **parameterList<='9') - { - commandParserIndex=atoi(*parameterList); // Use specified parser unless it's an invalid number - commandParserIndex--; // Subtract 1 since we displayed numbers starting at index+1 - if (commandParserIndex >= commandParserList.Size()) - { - transport->Send(p->systemAddress, "Invalid index.\r\n"); - failed=true; - } - } - else - { - // // Prefixing with the name of a command parser directs to that parser. See if the first word matches a parser - for (i=0; i < commandParserList.Size(); i++) - { - if (_stricmp(parameterList[0], commandParserList[i]->GetName())==0) - { - commandParserIndex=i; // Matches parser at index i - break; - } - } - } - - if (failed==false) - { - // -1 means undirected, so otherwise this is directed to a target - if (commandParserIndex!=(unsigned)-1) - { - // Only this parser should use this command - tryAllParsers=false; - if (commandParserList[commandParserIndex]->GetRegisteredCommand(parameterList[1], &rc)) - { - commandParsed=true; - if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-2) - commandParserList[commandParserIndex]->OnCommand(rc.command, numParameters-2, parameterList+2, transport, p->systemAddress, copy); - else - transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); - } - } - } - } - - if (failed == false && tryAllParsers) - { - for (i=0; i < commandParserList.Size(); i++) - { - // Undirected command. Try all the parsers to see if they understand the command - // Pass the 1nd element as the command, and the remainder as the parameter list - if (commandParserList[i]->GetRegisteredCommand(parameterList[0], &rc)) - { - commandParsed=true; - - if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-1) - commandParserList[i]->OnCommand(rc.command, numParameters-1, parameterList+1, transport, p->systemAddress, copy); - else - transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); - } - } - } - if (commandParsed==false && commandParserList.Size() > 0) - { - transport->Send(p->systemAddress, "Unknown command: Type 'help' for help.\r\n"); - } - - } - - ShowPrompt(p->systemAddress); - - transport->DeallocatePacket(p); - p = transport->Receive(); - } -} - -void ConsoleServer::ListParsers(SystemAddress systemAddress) -{ - transport->Send(systemAddress,"INSTALLED PARSERS:\r\n"); - unsigned i; - for (i=0; i < commandParserList.Size(); i++) - { - transport->Send(systemAddress, "%i. %s\r\n", i+1, commandParserList[i]->GetName()); - } -} -void ConsoleServer::ShowPrompt(SystemAddress systemAddress) -{ - transport->Send(systemAddress, prompt); -} -void ConsoleServer::SetPrompt(const char *_prompt) -{ - if (prompt) - rakFree_Ex(prompt,_FILE_AND_LINE_); - if (_prompt && _prompt[0]) - { - size_t len = strlen(_prompt); - prompt = (char*) rakMalloc_Ex(len+1,_FILE_AND_LINE_); - strcpy(prompt,_prompt); - } - else - prompt=0; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ConsoleServer==1 + +#include "ConsoleServer.h" +#include "TransportInterface.h" +#include "CommandParserInterface.h" +#include +#include + +#define COMMAND_DELINATOR ' ' +#define COMMAND_DELINATOR_TOGGLE '"' + +#include "LinuxStrings.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(ConsoleServer,ConsoleServer); + +ConsoleServer::ConsoleServer() +{ + transport=0; + password[0]=0; + prompt=0; +} +ConsoleServer::~ConsoleServer() +{ + if (prompt) + rakFree_Ex(prompt, _FILE_AND_LINE_); +} +void ConsoleServer::SetTransportProvider(TransportInterface *transportInterface, unsigned short port) +{ + // Replace the current TransportInterface, stopping the old one, if present, and starting the new one. + if (transportInterface) + { + if (transport) + { + RemoveCommandParser(transport->GetCommandParser()); + transport->Stop(); + } + transport=transportInterface; + transport->Start(port, true); + + unsigned i; + for (i=0; i < commandParserList.Size(); i++) + commandParserList[i]->OnTransportChange(transport); + + // The transport itself might have a command parser - for example password for the RakNet transport + AddCommandParser(transport->GetCommandParser()); + } +} +void ConsoleServer::AddCommandParser(CommandParserInterface *commandParserInterface) +{ + if (commandParserInterface==0) + return; + + // Non-duplicate insertion + unsigned i; + for (i=0; i < commandParserList.Size(); i++) + { + if (commandParserList[i]==commandParserInterface) + return; + + if (_stricmp(commandParserList[i]->GetName(), commandParserInterface->GetName())==0) + { + // Naming conflict between two command parsers + RakAssert(0); + return; + } + } + + commandParserList.Insert(commandParserInterface, _FILE_AND_LINE_); + if (transport) + commandParserInterface->OnTransportChange(transport); +} +void ConsoleServer::RemoveCommandParser(CommandParserInterface *commandParserInterface) +{ + if (commandParserInterface==0) + return; + + // Overwrite the element we are removing from the back of the list and delete the back of the list + unsigned i; + for (i=0; i < commandParserList.Size(); i++) + { + if (commandParserList[i]==commandParserInterface) + { + commandParserList[i]=commandParserList[commandParserList.Size()-1]; + commandParserList.RemoveFromEnd(); + return; + } + } +} +void ConsoleServer::Update(void) +{ + unsigned i; + char *parameterList[20]; // Up to 20 parameters + unsigned numParameters; + RakNet::SystemAddress newOrLostConnectionId; + RakNet::Packet *p; + RakNet::RegisteredCommand rc; + + p = transport->Receive(); + newOrLostConnectionId=transport->HasNewIncomingConnection(); + + if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < commandParserList.Size(); i++) + { + commandParserList[i]->OnNewIncomingConnection(newOrLostConnectionId, transport); + } + + transport->Send(newOrLostConnectionId, "Connected to remote command console.\r\nType 'help' for help.\r\n"); + ListParsers(newOrLostConnectionId); + ShowPrompt(newOrLostConnectionId); + } + + newOrLostConnectionId=transport->HasLostConnection(); + if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < commandParserList.Size(); i++) + commandParserList[i]->OnConnectionLost(newOrLostConnectionId, transport); + } + + while (p) + { + bool commandParsed=false; + char copy[REMOTE_MAX_TEXT_INPUT]; + memcpy(copy, p->data, p->length); + copy[p->length]=0; + RakNet::CommandParserInterface::ParseConsoleString((char*)p->data, COMMAND_DELINATOR, COMMAND_DELINATOR_TOGGLE, &numParameters, parameterList, 20); // Up to 20 parameters + if (numParameters==0) + { + transport->DeallocatePacket(p); + p = transport->Receive(); + continue; + } + if (_stricmp(*parameterList, "help")==0 && numParameters<=2) + { + // Find the parser specified and display help for it + if (numParameters==1) + { + transport->Send(p->systemAddress, "\r\nINSTRUCTIONS:\r\n"); + transport->Send(p->systemAddress, "Enter commands on your keyboard, using spaces to delineate parameters.\r\n"); + transport->Send(p->systemAddress, "You can use quotation marks to toggle space delineation.\r\n"); + transport->Send(p->systemAddress, "You can connect multiple times from the same computer.\r\n"); + transport->Send(p->systemAddress, "You can direct commands to a parser by prefixing the parser name or number.\r\n"); + transport->Send(p->systemAddress, "COMMANDS:\r\n"); + transport->Send(p->systemAddress, "help Show this display.\r\n"); + transport->Send(p->systemAddress, "help Show help on a particular parser.\r\n"); + transport->Send(p->systemAddress, "help Show help on a particular command.\r\n"); + transport->Send(p->systemAddress, "quit Disconnects from the server.\r\n"); + transport->Send(p->systemAddress, "[] [] Execute a command\r\n"); + transport->Send(p->systemAddress, "[] [] Execute a command\r\n"); + ListParsers(p->systemAddress); + //ShowPrompt(p->systemAddress); + } + else // numParameters == 2, including the help tag + { + for (i=0; i < commandParserList.Size(); i++) + { + if (_stricmp(parameterList[1], commandParserList[i]->GetName())==0) + { + commandParsed=true; + commandParserList[i]->SendHelp(transport, p->systemAddress); + transport->Send(p->systemAddress, "COMMAND LIST:\r\n"); + commandParserList[i]->SendCommandList(transport, p->systemAddress); + transport->Send(p->systemAddress, "\r\n"); + break; + } + } + + if (commandParsed==false) + { + // Try again, for all commands for all parsers. + RakNet::RegisteredCommand rc; + for (i=0; i < commandParserList.Size(); i++) + { + if (commandParserList[i]->GetRegisteredCommand(parameterList[1], &rc)) + { + if (rc.parameterCount==RakNet::CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS) + transport->Send(p->systemAddress, "(Variable parms): %s %s\r\n", rc.command, rc.commandHelp); + else + transport->Send(p->systemAddress, "(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); + commandParsed=true; + break; + } + } + } + + if (commandParsed==false) + { + // Don't know what to do + transport->Send(p->systemAddress, "Unknown help topic: %s.\r\n", parameterList[1]); + } + //ShowPrompt(p->systemAddress); + } + } + else if (_stricmp(*parameterList, "quit")==0 && numParameters==1) + { + transport->Send(p->systemAddress, "Goodbye!\r\n"); + transport->CloseConnection(p->systemAddress); + } + else + { + bool tryAllParsers=true; + bool failed=false; + + if (numParameters >=2) // At minimum + { + unsigned commandParserIndex=(unsigned)-1; + // Prefixing with numbers directs to a particular parser + if (**parameterList>='0' && **parameterList<='9') + { + commandParserIndex=atoi(*parameterList); // Use specified parser unless it's an invalid number + commandParserIndex--; // Subtract 1 since we displayed numbers starting at index+1 + if (commandParserIndex >= commandParserList.Size()) + { + transport->Send(p->systemAddress, "Invalid index.\r\n"); + failed=true; + } + } + else + { + // // Prefixing with the name of a command parser directs to that parser. See if the first word matches a parser + for (i=0; i < commandParserList.Size(); i++) + { + if (_stricmp(parameterList[0], commandParserList[i]->GetName())==0) + { + commandParserIndex=i; // Matches parser at index i + break; + } + } + } + + if (failed==false) + { + // -1 means undirected, so otherwise this is directed to a target + if (commandParserIndex!=(unsigned)-1) + { + // Only this parser should use this command + tryAllParsers=false; + if (commandParserList[commandParserIndex]->GetRegisteredCommand(parameterList[1], &rc)) + { + commandParsed=true; + if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-2) + commandParserList[commandParserIndex]->OnCommand(rc.command, numParameters-2, parameterList+2, transport, p->systemAddress, copy); + else + transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); + } + } + } + } + + if (failed == false && tryAllParsers) + { + for (i=0; i < commandParserList.Size(); i++) + { + // Undirected command. Try all the parsers to see if they understand the command + // Pass the 1nd element as the command, and the remainder as the parameter list + if (commandParserList[i]->GetRegisteredCommand(parameterList[0], &rc)) + { + commandParsed=true; + + if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-1) + commandParserList[i]->OnCommand(rc.command, numParameters-1, parameterList+1, transport, p->systemAddress, copy); + else + transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); + } + } + } + if (commandParsed==false && commandParserList.Size() > 0) + { + transport->Send(p->systemAddress, "Unknown command: Type 'help' for help.\r\n"); + } + + } + + ShowPrompt(p->systemAddress); + + transport->DeallocatePacket(p); + p = transport->Receive(); + } +} + +void ConsoleServer::ListParsers(SystemAddress systemAddress) +{ + transport->Send(systemAddress,"INSTALLED PARSERS:\r\n"); + unsigned i; + for (i=0; i < commandParserList.Size(); i++) + { + transport->Send(systemAddress, "%i. %s\r\n", i+1, commandParserList[i]->GetName()); + } +} +void ConsoleServer::ShowPrompt(SystemAddress systemAddress) +{ + transport->Send(systemAddress, prompt); +} +void ConsoleServer::SetPrompt(const char *_prompt) +{ + if (prompt) + rakFree_Ex(prompt,_FILE_AND_LINE_); + if (_prompt && _prompt[0]) + { + size_t len = strlen(_prompt); + prompt = (char*) rakMalloc_Ex(len+1,_FILE_AND_LINE_); + strcpy(prompt,_prompt); + } + else + prompt=0; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/ConsoleServer.h b/project/lib_projects/raknet/jni/RaknetSources/ConsoleServer.h index d65fc96..0776c54 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ConsoleServer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/ConsoleServer.h @@ -1,77 +1,77 @@ -/// \file ConsoleServer.h -/// \brief Contains ConsoleServer , used to plugin to your game to accept remote console-based connections -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ConsoleServer==1 - -#ifndef __CONSOLE_SERVER_H -#define __CONSOLE_SERVER_H - -#include "RakMemoryOverride.h" -#include "DS_List.h" -#include "RakNetTypes.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class TransportInterface; -class CommandParserInterface; - - -/// \brief The main entry point for the server portion of your remote console application support. -/// \details ConsoleServer takes one TransportInterface and one or more CommandParserInterface (s) -/// The TransportInterface will be used to send data between the server and the client. The connecting client must support the -/// protocol used by your derivation of TransportInterface . TelnetTransport and RakNetTransport are two such derivations . -/// When a command is sent by a remote console, it will be processed by your implementations of CommandParserInterface -class RAK_DLL_EXPORT ConsoleServer -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(ConsoleServer) - - ConsoleServer(); - ~ConsoleServer(); - - /// \brief Call this with a derivation of TransportInterface so that the console server can send and receive commands - /// \param[in] transportInterface Your interface to use. - /// \param[in] port The port to host on. Telnet uses port 23 by default. RakNet can use whatever you want. - void SetTransportProvider(TransportInterface *transportInterface, unsigned short port); - - /// \brief Add an implementation of CommandParserInterface to the list of command parsers. - /// \param[in] commandParserInterface The command parser referred to - void AddCommandParser(CommandParserInterface *commandParserInterface); - - /// \brief Remove an implementation of CommandParserInterface previously added with AddCommandParser(). - /// \param[in] commandParserInterface The command parser referred to - void RemoveCommandParser(CommandParserInterface *commandParserInterface); - - /// \brief Call update to read packet sent from your TransportInterface. - /// You should do this fairly frequently. - void Update(void); - - /// \brief Sets a prompt to show when waiting for user input. - /// \details Pass an empty string to clear the prompt - /// Defaults to no prompt - /// \param[in] _prompt Null-terminated string of the prompt to use. If you want a newline, be sure to use /r/n - void SetPrompt(const char *_prompt); - -protected: - void ListParsers(SystemAddress systemAddress); - void ShowPrompt(SystemAddress systemAddress); - TransportInterface *transport; - DataStructures::List commandParserList; - char* password[256]; - char *prompt; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file ConsoleServer.h +/// \brief Contains ConsoleServer , used to plugin to your game to accept remote console-based connections +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ConsoleServer==1 + +#ifndef __CONSOLE_SERVER_H +#define __CONSOLE_SERVER_H + +#include "RakMemoryOverride.h" +#include "DS_List.h" +#include "RakNetTypes.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class TransportInterface; +class CommandParserInterface; + + +/// \brief The main entry point for the server portion of your remote console application support. +/// \details ConsoleServer takes one TransportInterface and one or more CommandParserInterface (s) +/// The TransportInterface will be used to send data between the server and the client. The connecting client must support the +/// protocol used by your derivation of TransportInterface . TelnetTransport and RakNetTransport are two such derivations . +/// When a command is sent by a remote console, it will be processed by your implementations of CommandParserInterface +class RAK_DLL_EXPORT ConsoleServer +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(ConsoleServer) + + ConsoleServer(); + ~ConsoleServer(); + + /// \brief Call this with a derivation of TransportInterface so that the console server can send and receive commands + /// \param[in] transportInterface Your interface to use. + /// \param[in] port The port to host on. Telnet uses port 23 by default. RakNet can use whatever you want. + void SetTransportProvider(TransportInterface *transportInterface, unsigned short port); + + /// \brief Add an implementation of CommandParserInterface to the list of command parsers. + /// \param[in] commandParserInterface The command parser referred to + void AddCommandParser(CommandParserInterface *commandParserInterface); + + /// \brief Remove an implementation of CommandParserInterface previously added with AddCommandParser(). + /// \param[in] commandParserInterface The command parser referred to + void RemoveCommandParser(CommandParserInterface *commandParserInterface); + + /// \brief Call update to read packet sent from your TransportInterface. + /// You should do this fairly frequently. + void Update(void); + + /// \brief Sets a prompt to show when waiting for user input. + /// \details Pass an empty string to clear the prompt + /// Defaults to no prompt + /// \param[in] _prompt Null-terminated string of the prompt to use. If you want a newline, be sure to use /r/n + void SetPrompt(const char *_prompt); + +protected: + void ListParsers(SystemAddress systemAddress); + void ShowPrompt(SystemAddress systemAddress); + TransportInterface *transport; + DataStructures::List commandParserList; + char* password[256]; + char *prompt; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_BPlusTree.h b/project/lib_projects/raknet/jni/RaknetSources/DS_BPlusTree.h index 768bfd8..ad4d443 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_BPlusTree.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_BPlusTree.h @@ -1,1146 +1,1146 @@ -/// \file DS_BPlusTree.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __B_PLUS_TREE_CPP -#define __B_PLUS_TREE_CPP - -#include "DS_MemoryPool.h" -#include "DS_Queue.h" -#include -#include "Export.h" - -// Java -// http://www.seanster.com/BplusTree/BplusTree.html - -// Overview -// http://babbage.clarku.edu/~achou/cs160/B+Trees/B+Trees.htm - -// Deletion -// http://dbpubs.stanford.edu:8090/pub/1995-19 - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -#include "RakMemoryOverride.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// Used in the BPlusTree. Used for both leaf and index nodes. - /// Don't use a constructor or destructor, due to the memory pool I am using - template - struct RAK_DLL_EXPORT Page - { - // We use the same data structure for both leaf and index nodes. - // It uses a little more memory for index nodes but reduces - // memory fragmentation, allocations, and deallocations. - bool isLeaf; - - // Used for both leaf and index nodes. - // For a leaf it means the number of elements in data - // For an index it means the number of keys and is one less than the number of children pointers. - int size; - - // Used for both leaf and index nodes. - KeyType keys[order]; - - // Used only for leaf nodes. Data is the actual data, while next is the pointer to the next leaf (for B+) - DataType data[order]; - Page *next; - Page *previous; - - // Used only for index nodes. Pointers to the children of this node. - Page *children[order+1]; - }; - - /// A BPlus tree - /// Written with efficiency and speed in mind. - template - class RAK_DLL_EXPORT BPlusTree - { - public: - struct ReturnAction - { - KeyType key1; - KeyType key2; - enum - { - NO_ACTION, - REPLACE_KEY1_WITH_KEY2, - PUSH_KEY_TO_PARENT, - SET_BRANCH_KEY, - } action; // 0=none, 1=replace key1 with key2 - }; - - BPlusTree(); - ~BPlusTree(); - void SetPoolPageSize(int size); // Set the page size for the memory pool. Optionsl - bool Get(const KeyType key, DataType &out) const; - bool Delete(const KeyType key); - bool Delete(const KeyType key, DataType &out); - bool Insert(const KeyType key, const DataType &data); - void Clear(void); - unsigned Size(void) const; - bool IsEmpty(void) const; - Page *GetListHead(void) const; - DataType GetDataHead(void) const; - void PrintLeaves(void); - void ForEachLeaf(void (*func)(Page * leaf, int index)); - void ForEachData(void (*func)(DataType input, int index)); - void PrintGraph(void); - protected: - void ValidateTreeRecursive(Page *cur); - void DeleteFromPageAtIndex(const int index, Page *cur); - static void PrintLeaf(Page * leaf, int index); - void FreePages(void); - bool GetIndexOf(const KeyType key, Page *page, int *out) const; - void ShiftKeysLeft(Page *cur); - bool CanRotateLeft(Page *cur, int childIndex); - bool CanRotateRight(Page *cur, int childIndex); - void RotateRight(Page *cur, int childIndex, ReturnAction *returnAction); - void RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction); - Page* InsertIntoNode(const KeyType key, const DataType &childData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction); - Page* InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction* returnAction, bool *success); - Page* GetLeafFromKey(const KeyType key) const; - bool FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out); - bool FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction); - void ShiftNodeLeft(Page *cur); - void ShiftNodeRight(Page *cur); - - MemoryPool > pagePool; - Page *root, *leftmostLeaf; - }; - - template - BPlusTree::BPlusTree () - { - RakAssert(order>1); - root=0; - leftmostLeaf=0; - } - template - BPlusTree::~BPlusTree () - { - Clear(); - } - template - void BPlusTree::SetPoolPageSize(int size) - { - pagePool.SetPageSize(size); - } - template - bool BPlusTree::Get(const KeyType key, DataType &out) const - { - if (root==0) - return false; - - Page* leaf = GetLeafFromKey(key); - int childIndex; - - if (GetIndexOf(key, leaf, &childIndex)) - { - out=leaf->data[childIndex]; - return true; - } - return false; - } - template - void BPlusTree::DeleteFromPageAtIndex(const int index, Page *cur) - { - int i; - for (i=index; i < cur->size-1; i++) - cur->keys[i]=cur->keys[i+1]; - if (cur->isLeaf) - { - for (i=index; i < cur->size-1; i++) - cur->data[i]=cur->data[i+1]; - } - else - { - for (i=index; i < cur->size-1; i++) - cur->children[i+1]=cur->children[i+2]; - } - cur->size--; - } - template - bool BPlusTree::Delete(const KeyType key) - { - DataType temp; - return Delete(key, temp); - } - template - bool BPlusTree::Delete(const KeyType key, DataType &out) - { - if (root==0) - return false; - - ReturnAction returnAction; - returnAction.action=ReturnAction::NO_ACTION; - int childIndex; - bool underflow=false; - if (root==leftmostLeaf) - { - if (GetIndexOf(key, root, &childIndex)==false) - return false; - out=root->data[childIndex]; - DeleteFromPageAtIndex(childIndex,root); - if (root->size==0) - { - pagePool.Release(root, _FILE_AND_LINE_); - root=0; - leftmostLeaf=0; - } - return true; - } - else if (FindDeleteRebalance(key, root, &underflow,root->keys[0], &returnAction, out)==false) - return false; - -// RakAssert(returnAction.action==ReturnAction::NO_ACTION); - - if (underflow && root->size==0) - { - // Move the root down. - Page *oldRoot=root; - root=root->children[0]; - pagePool.Release(oldRoot, _FILE_AND_LINE_); - // memset(oldRoot,0,sizeof(root)); - } - - return true; - } - template - bool BPlusTree::FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out) - { - // Get index of child to follow. - int branchIndex, childIndex; - if (GetIndexOf(key, cur, &childIndex)) - branchIndex=childIndex+1; - else - branchIndex=childIndex; - - // If child is not a leaf, call recursively - if (cur->children[branchIndex]->isLeaf==false) - { - if (branchIndexsize) - rightRootKey=cur->keys[branchIndex]; // Shift right to left - else - rightRootKey=cur->keys[branchIndex-1]; // Shift center to left - - if (FindDeleteRebalance(key, cur->children[branchIndex], underflow, rightRootKey, returnAction, out)==false) - return false; - - // Call again in case the root key changed - if (branchIndexsize) - rightRootKey=cur->keys[branchIndex]; // Shift right to left - else - rightRootKey=cur->keys[branchIndex-1]; // Shift center to left - - if (returnAction->action==ReturnAction::SET_BRANCH_KEY && branchIndex!=childIndex) - { - returnAction->action=ReturnAction::NO_ACTION; - cur->keys[childIndex]=returnAction->key1; - - if (branchIndexsize) - rightRootKey=cur->keys[branchIndex]; // Shift right to left - else - rightRootKey=cur->keys[branchIndex-1]; // Shift center to left - } - } - else - { - // If child is a leaf, get the index of the key. If the item is not found, cancel delete. - if (GetIndexOf(key, cur->children[branchIndex], &childIndex)==false) - return false; - - // Delete: - // Remove childIndex from the child at branchIndex - out=cur->children[branchIndex]->data[childIndex]; - DeleteFromPageAtIndex(childIndex, cur->children[branchIndex]); - - if (childIndex==0) - { - if (branchIndex>0) - cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; - - if (branchIndex==0) - { - returnAction->action=ReturnAction::SET_BRANCH_KEY; - returnAction->key1=cur->children[0]->keys[0]; - } - } - - if (cur->children[branchIndex]->size < order/2) - *underflow=true; - else - *underflow=false; - } - - // Fix underflow: - if (*underflow) - { - *underflow=FixUnderflow(branchIndex, cur, rightRootKey, returnAction); - } - - return true; - } - template - bool BPlusTree::FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction) - { - // Borrow from a neighbor that has excess. - Page *source; - Page *dest; - - if (branchIndex>0 && cur->children[branchIndex-1]->size > order/2) - { - dest=cur->children[branchIndex]; - source=cur->children[branchIndex-1]; - - // Left has excess - ShiftNodeRight(dest); - if (dest->isLeaf) - { - dest->keys[0]=source->keys[source->size-1]; - dest->data[0]=source->data[source->size-1]; - } - else - { - dest->children[0]=source->children[source->size]; - dest->keys[0]=cur->keys[branchIndex-1]; - } - // Update the parent key for the child (middle) - cur->keys[branchIndex-1]=source->keys[source->size-1]; - source->size--; - - // if (branchIndex==0) - // { - // returnAction->action=ReturnAction::SET_BRANCH_KEY; - // returnAction->key1=dest->keys[0]; - // } - - // No underflow - return false; - } - else if (branchIndexsize && cur->children[branchIndex+1]->size > order/2) - { - dest=cur->children[branchIndex]; - source=cur->children[branchIndex+1]; - - // Right has excess - if (dest->isLeaf) - { - dest->keys[dest->size]=source->keys[0]; - dest->data[dest->size]=source->data[0]; - - // The first key in the leaf after shifting is the parent key for the right branch - cur->keys[branchIndex]=source->keys[1]; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (order<=3 && dest->size==0) - { - if (branchIndex==0) - { - returnAction->action=ReturnAction::SET_BRANCH_KEY; - returnAction->key1=dest->keys[0]; - } - else - cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; - } - } - else - { - if (returnAction->action==ReturnAction::NO_ACTION) - { - returnAction->action=ReturnAction::SET_BRANCH_KEY; - returnAction->key1=dest->keys[0]; - } - - dest->keys[dest->size]=rightRootKey; - dest->children[dest->size+1]=source->children[0]; - - // The shifted off key is the leftmost key for a node - cur->keys[branchIndex]=source->keys[0]; - } - - - dest->size++; - ShiftNodeLeft(source); - - //cur->keys[branchIndex]=source->keys[0]; - -// returnAction->action=ReturnAction::SET_BRANCH_KEY; -// returnAction->key1=dest->keys[dest->size-1]; - - // No underflow - return false; - } - else - { - int sourceIndex; - - // If no neighbors have excess, merge two branches. - // - // To merge two leaves, just copy the data and keys over. - // - // To merge two branches, copy the pointers and keys over, using rightRootKey as the key for the extra pointer - if (branchIndexsize) - { - // Merge right child to current child and delete right child. - dest=cur->children[branchIndex]; - source=cur->children[branchIndex+1]; - } - else - { - // Move current child to left and delete current child - dest=cur->children[branchIndex-1]; - source=cur->children[branchIndex]; - } - - // Merge - if (dest->isLeaf) - { - for (sourceIndex=0; sourceIndexsize; sourceIndex++) - { - dest->keys[dest->size]=source->keys[sourceIndex]; - dest->data[dest->size++]=source->data[sourceIndex]; - } - } - else - { - // We want the tree root key of the source, not the current. - dest->keys[dest->size]=rightRootKey; - dest->children[dest->size++ + 1]=source->children[0]; - for (sourceIndex=0; sourceIndexsize; sourceIndex++) - { - dest->keys[dest->size]=source->keys[sourceIndex]; - dest->children[dest->size++ + 1]=source->children[sourceIndex + 1]; - } - } - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (order<=3 && branchIndex>0 && cur->children[branchIndex]->isLeaf) // With order==2 it is possible to delete data[0], which is not possible with higher orders. - cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; - - if (branchIndexsize) - { - // Update the parent key, removing the source (right) - DeleteFromPageAtIndex(branchIndex, cur); - } - else - { - if (branchIndex>0) - { - // Update parent key, removing the source (current) - DeleteFromPageAtIndex(branchIndex-1, cur); - } - } - - if (branchIndex==0 && dest->isLeaf) - { - returnAction->action=ReturnAction::SET_BRANCH_KEY; - returnAction->key1=dest->keys[0]; - } - - if (source==leftmostLeaf) - leftmostLeaf=source->next; - - if (source->isLeaf) - { - if (source->previous) - source->previous->next=source->next; - if (source->next) - source->next->previous=source->previous; - } - - // Free the source node - pagePool.Release(source, _FILE_AND_LINE_); - // memset(source,0,sizeof(root)); - - // Return underflow or not of parent. - return cur->size < order/2; - } - } - template - void BPlusTree::ShiftNodeRight(Page *cur) - { - int i; - for (i=cur->size; i>0; i--) - cur->keys[i]=cur->keys[i-1]; - if (cur->isLeaf) - { - for (i=cur->size; i>0; i--) - cur->data[i]=cur->data[i-1]; - } - else - { - for (i=cur->size+1; i>0; i--) - cur->children[i]=cur->children[i-1]; - } - - cur->size++; - } - template - void BPlusTree::ShiftNodeLeft(Page *cur) - { - int i; - for (i=0; i < cur->size-1; i++) - cur->keys[i]=cur->keys[i+1]; - if (cur->isLeaf) - { - for (i=0; i < cur->size; i++) - cur->data[i]=cur->data[i+1]; - } - else - { - for (i=0; i < cur->size; i++) - cur->children[i]=cur->children[i+1]; - } - cur->size--; - } - template - Page* BPlusTree::InsertIntoNode(const KeyType key, const DataType &leafData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction) - { - int i; - if (cur->size < order) - { - for (i=cur->size; i > insertionIndex; i--) - cur->keys[i]=cur->keys[i-1]; - if (cur->isLeaf) - { - for (i=cur->size; i > insertionIndex; i--) - cur->data[i]=cur->data[i-1]; - } - else - { - for (i=cur->size+1; i > insertionIndex+1; i--) - cur->children[i]=cur->children[i-1]; - } - cur->keys[insertionIndex]=key; - if (cur->isLeaf) - cur->data[insertionIndex]=leafData; - else - cur->children[insertionIndex+1]=nodeData; - - cur->size++; - } - else - { - Page* newPage = pagePool.Allocate( _FILE_AND_LINE_ ); - newPage->isLeaf=cur->isLeaf; - if (cur->isLeaf) - { - newPage->next=cur->next; - if (cur->next) - cur->next->previous=newPage; - newPage->previous=cur; - cur->next=newPage; - } - - int destIndex, sourceIndex; - - if (insertionIndex>=(order+1)/2) - { - destIndex=0; - sourceIndex=order/2; - - for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) - { - newPage->keys[destIndex]=cur->keys[sourceIndex]; - } - newPage->keys[destIndex++]=key; - for (; sourceIndex < order; sourceIndex++, destIndex++) - { - newPage->keys[destIndex]=cur->keys[sourceIndex]; - } - - destIndex=0; - sourceIndex=order/2; - if (cur->isLeaf) - { - for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) - { - newPage->data[destIndex]=cur->data[sourceIndex]; - } - newPage->data[destIndex++]=leafData; - for (; sourceIndex < order; sourceIndex++, destIndex++) - { - newPage->data[destIndex]=cur->data[sourceIndex]; - } - } - else - { - - for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) - { - newPage->children[destIndex]=cur->children[sourceIndex+1]; - } - newPage->children[destIndex++]=nodeData; - - // sourceIndex+1 is sort of a hack but it works - because there is one extra child than keys - // skip past the last child for cur - for (; sourceIndex+1 < cur->size+1; sourceIndex++, destIndex++) - { - newPage->children[destIndex]=cur->children[sourceIndex+1]; - } - - // the first key is the middle key. Remove it from the page and push it to the parent - returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; - returnAction->key1=newPage->keys[0]; - for (int i=0; i < destIndex-1; i++) - newPage->keys[i]=newPage->keys[i+1]; - - } - cur->size=order/2; - } - else - { - destIndex=0; - sourceIndex=(order+1)/2-1; - for (; sourceIndex < order; sourceIndex++, destIndex++) - newPage->keys[destIndex]=cur->keys[sourceIndex]; - destIndex=0; - if (cur->isLeaf) - { - sourceIndex=(order+1)/2-1; - for (; sourceIndex < order; sourceIndex++, destIndex++) - newPage->data[destIndex]=cur->data[sourceIndex]; - } - else - { - sourceIndex=(order+1)/2; - for (; sourceIndex < order+1; sourceIndex++, destIndex++) - newPage->children[destIndex]=cur->children[sourceIndex]; - - // the first key is the middle key. Remove it from the page and push it to the parent - returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; - returnAction->key1=newPage->keys[0]; - for (int i=0; i < destIndex-1; i++) - newPage->keys[i]=newPage->keys[i+1]; - } - cur->size=(order+1)/2-1; - if (cur->size) - { - bool b = GetIndexOf(key, cur, &insertionIndex); - (void) b; - RakAssert(b==false); - } - else - insertionIndex=0; - InsertIntoNode(key, leafData, insertionIndex, nodeData, cur, returnAction); - } - - newPage->size=destIndex; - - return newPage; - } - - return 0; - } - - template - bool BPlusTree::CanRotateLeft(Page *cur, int childIndex) - { - return childIndex>0 && cur->children[childIndex-1]->size - void BPlusTree::RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction) - { - Page *dest = cur->children[childIndex-1]; - Page *source = cur->children[childIndex]; - returnAction->key1=source->keys[0]; - dest->keys[dest->size]=source->keys[0]; - dest->data[dest->size]=source->data[0]; - dest->size++; - for (int i=0; i < source->size-1; i++) - { - source->keys[i]=source->keys[i+1]; - source->data[i]=source->data[i+1]; - } - source->size--; - cur->keys[childIndex-1]=source->keys[0]; - returnAction->key2=source->keys[0]; - } - - template - bool BPlusTree::CanRotateRight(Page *cur, int childIndex) - { - return childIndex < cur->size && cur->children[childIndex+1]->size - void BPlusTree::RotateRight(Page *cur, int childIndex, ReturnAction *returnAction) - { - Page *dest = cur->children[childIndex+1]; - Page *source = cur->children[childIndex]; - returnAction->key1=dest->keys[0]; - for (int i= dest->size; i > 0; i--) - { - dest->keys[i]=dest->keys[i-1]; - dest->data[i]=dest->data[i-1]; - } - dest->keys[0]=source->keys[source->size-1]; - dest->data[0]=source->data[source->size-1]; - dest->size++; - source->size--; - - cur->keys[childIndex]=dest->keys[0]; - returnAction->key2=dest->keys[0]; - } - template - Page* BPlusTree::GetLeafFromKey(const KeyType key) const - { - Page* cur = root; - int childIndex; - while (cur->isLeaf==false) - { - // When searching, if we match the exact key we go down the pointer after that index - if (GetIndexOf(key, cur, &childIndex)) - childIndex++; - cur = cur->children[childIndex]; - } - return cur; - } - - template - Page* BPlusTree::InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction *returnAction, bool *success) - { - int childIndex; - int branchIndex; - if (GetIndexOf(key, cur, &childIndex)) - branchIndex=childIndex+1; - else - branchIndex=childIndex; - Page* newPage; - if (cur->isLeaf==false) - { - if (cur->children[branchIndex]->isLeaf==true && cur->children[branchIndex]->size==order) - { - if (branchIndex==childIndex+1) - { - *success=false; - return 0; // Already exists - } - - if (CanRotateLeft(cur, branchIndex)) - { - returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; - if (key > cur->children[branchIndex]->keys[0]) - { - RotateLeft(cur, branchIndex, returnAction); - - int insertionIndex; - GetIndexOf(key, cur->children[branchIndex], &insertionIndex); - InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); - } - else - { - // Move head element to left and replace it with key,data - Page* dest=cur->children[branchIndex-1]; - Page* source=cur->children[branchIndex]; - returnAction->key1=source->keys[0]; - returnAction->key2=key; - dest->keys[dest->size]=source->keys[0]; - dest->data[dest->size]=source->data[0]; - dest->size++; - source->keys[0]=key; - source->data[0]=data; - } - cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; - - return 0; - } - else if (CanRotateRight(cur, branchIndex)) - { - returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; - - if (key < cur->children[branchIndex]->keys[cur->children[branchIndex]->size-1]) - { - RotateRight(cur, branchIndex, returnAction); - - int insertionIndex; - GetIndexOf(key, cur->children[branchIndex], &insertionIndex); - InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); - - } - else - { - // Insert to the head of the right leaf instead and change our key - returnAction->key1=cur->children[branchIndex+1]->keys[0]; - InsertIntoNode(key, data, 0, 0, cur->children[branchIndex+1], 0); - returnAction->key2=key; - } - cur->keys[branchIndex]=cur->children[branchIndex+1]->keys[0]; - return 0; - } - } - - newPage=InsertBranchDown(key,data,cur->children[branchIndex], returnAction, success); - if (returnAction->action==ReturnAction::REPLACE_KEY1_WITH_KEY2) - { - if (branchIndex>0 && cur->keys[branchIndex-1]==returnAction->key1) - cur->keys[branchIndex-1]=returnAction->key2; - } - if (newPage) - { - if (newPage->isLeaf==false) - { - RakAssert(returnAction->action==ReturnAction::PUSH_KEY_TO_PARENT); - newPage->size--; - return InsertIntoNode(returnAction->key1, data, branchIndex, newPage, cur, returnAction); - } - else - { - return InsertIntoNode(newPage->keys[0], data, branchIndex, newPage, cur, returnAction); - } - } - } - else - { - if (branchIndex==childIndex+1) - { - *success=false; - return 0; // Already exists - } - else - { - return InsertIntoNode(key, data, branchIndex, 0, cur, returnAction); - } - } - - return 0; - } - template - bool BPlusTree::Insert(const KeyType key, const DataType &data) - { - if (root==0) - { - // Allocate root and make root a leaf - root = pagePool.Allocate( _FILE_AND_LINE_ ); - root->isLeaf=true; - leftmostLeaf=root; - root->size=1; - root->keys[0]=key; - root->data[0]=data; - root->next=0; - root->previous=0; - } - else - { - bool success=true; - ReturnAction returnAction; - returnAction.action=ReturnAction::NO_ACTION; - Page* newPage = InsertBranchDown(key, data, root, &returnAction, &success); - if (success==false) - return false; - if (newPage) - { - KeyType newKey; - if (newPage->isLeaf==false) - { - // One key is pushed up through the stack. I store that at keys[0] but it has to be removed for the page to be correct - RakAssert(returnAction.action==ReturnAction::PUSH_KEY_TO_PARENT); - newKey=returnAction.key1; - newPage->size--; - } - else - newKey = newPage->keys[0]; - // propagate the root - Page* newRoot = pagePool.Allocate( _FILE_AND_LINE_ ); - newRoot->isLeaf=false; - newRoot->size=1; - newRoot->keys[0]=newKey; - newRoot->children[0]=root; - newRoot->children[1]=newPage; - root=newRoot; - } - } - - return true; - } - template - void BPlusTree::ShiftKeysLeft(Page *cur) - { - int i; - for (i=0; i < cur->size; i++) - cur->keys[i]=cur->keys[i+1]; - } - template - void BPlusTree::Clear(void) - { - if (root) - { - FreePages(); - leftmostLeaf=0; - root=0; - } - pagePool.Clear(_FILE_AND_LINE_); - } - template - unsigned BPlusTree::Size(void) const - { - int count=0; - DataStructures::Page *cur = GetListHead(); - while (cur) - { - count+=cur->size; - cur=cur->next; - } - return count; - } - template - bool BPlusTree::IsEmpty(void) const - { - return root==0; - } - template - bool BPlusTree::GetIndexOf(const KeyType key, Page *page, int *out) const - { - RakAssert(page->size>0); - int index, upperBound, lowerBound; - upperBound=page->size-1; - lowerBound=0; - index = page->size/2; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - if (key==page->keys[index]) - { - *out=index; - return true; - } - else if (keykeys[index]) - upperBound=index-1; - else - lowerBound=index+1; - - index=lowerBound+(upperBound-lowerBound)/2; - - if (lowerBound>upperBound) - { - *out=lowerBound; - return false; // No match - } - } - } - template - void BPlusTree::FreePages(void) - { - DataStructures::Queue *> queue; - DataStructures::Page *ptr; - int i; - queue.Push(root, _FILE_AND_LINE_ ); - while (queue.Size()) - { - ptr=queue.Pop(); - if (ptr->isLeaf==false) - { - for (i=0; i < ptr->size+1; i++) - queue.Push(ptr->children[i], _FILE_AND_LINE_ ); - } - pagePool.Release(ptr, _FILE_AND_LINE_); - // memset(ptr,0,sizeof(root)); - }; - } - template - Page *BPlusTree::GetListHead(void) const - { - return leftmostLeaf; - } - template - DataType BPlusTree::GetDataHead(void) const - { - return leftmostLeaf->data[0]; - } - template - void BPlusTree::ForEachLeaf(void (*func)(Page * leaf, int index)) - { - int count=0; - DataStructures::Page *cur = GetListHead(); - while (cur) - { - func(cur, count++); - cur=cur->next; - } - } - template - void BPlusTree::ForEachData(void (*func)(DataType input, int index)) - { - int count=0,i; - DataStructures::Page *cur = GetListHead(); - while (cur) - { - for (i=0; i < cur->size; i++) - func(cur->data[i], count++); - cur=cur->next; - } - } - template - void BPlusTree::PrintLeaf(Page * leaf, int index) - { - int i; - RAKNET_DEBUG_PRINTF("%i] SELF=%p\n", index+1, leaf); - for (i=0; i < leaf->size; i++) - RAKNET_DEBUG_PRINTF(" %i. %i\n", i+1, leaf->data[i]); - } - template - void BPlusTree::PrintLeaves(void) - { - ForEachLeaf(PrintLeaf); - } - - template - void BPlusTree::ValidateTreeRecursive(Page *cur) - { - RakAssert(cur==root || cur->size>=order/2); - - if (cur->children[0]->isLeaf) - { - RakAssert(cur->children[0]->keys[0] < cur->keys[0]); - for (int i=0; i < cur->size; i++) - { - RakAssert(cur->children[i+1]->keys[0]==cur->keys[i]); - } - } - else - { - for (int i=0; i < cur->size+1; i++) - ValidateTreeRecursive(cur->children[i]); - } - } - - template - void BPlusTree::PrintGraph(void) - { - DataStructures::Queue *> queue; - queue.Push(root,_FILE_AND_LINE_); - queue.Push(0,_FILE_AND_LINE_); - DataStructures::Page *ptr; - int i,j; - if (root) - { - RAKNET_DEBUG_PRINTF("%p(", root); - for (i=0; i < root->size; i++) - { - RAKNET_DEBUG_PRINTF("%i ", root->keys[i]); - } - RAKNET_DEBUG_PRINTF(") "); - RAKNET_DEBUG_PRINTF("\n"); - } - while (queue.Size()) - { - ptr=queue.Pop(); - if (ptr==0) - RAKNET_DEBUG_PRINTF("\n"); - else if (ptr->isLeaf==false) - { - for (i=0; i < ptr->size+1; i++) - { - RAKNET_DEBUG_PRINTF("%p(", ptr->children[i]); - //RAKNET_DEBUG_PRINTF("(", ptr->children[i]); - for (j=0; j < ptr->children[i]->size; j++) - RAKNET_DEBUG_PRINTF("%i ", ptr->children[i]->keys[j]); - RAKNET_DEBUG_PRINTF(") "); - queue.Push(ptr->children[i],_FILE_AND_LINE_); - } - queue.Push(0,_FILE_AND_LINE_); - RAKNET_DEBUG_PRINTF(" -- "); - } - } - RAKNET_DEBUG_PRINTF("\n"); - } -} -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - -// Code to test this hellish data structure. -/* -#include "DS_BPlusTree.h" -#include - -// Handle underflow on root. If there is only one item left then I can go downwards. -// Make sure I keep the leftmost pointer valid by traversing it -// When I free a leaf, be sure to adjust the pointers around it. - -#include "Rand.h" - -void main(void) -{ - DataStructures::BPlusTree btree; - DataStructures::List haveList, removedList; - int temp; - int i, j, index; - int testSize; - bool b; - - for (testSize=0; testSize < 514; testSize++) - { - RAKNET_DEBUG_PRINTF("TestSize=%i\n", testSize); - - for (i=0; i < testSize; i++) - haveList.Insert(i); - - for (i=0; i < testSize; i++) - { - index=i+randomMT()%(testSize-i); - temp=haveList[index]; - haveList[index]=haveList[i]; - haveList[i]=temp; - } - - for (i=0; i +#include "Export.h" + +// Java +// http://www.seanster.com/BplusTree/BplusTree.html + +// Overview +// http://babbage.clarku.edu/~achou/cs160/B+Trees/B+Trees.htm + +// Deletion +// http://dbpubs.stanford.edu:8090/pub/1995-19 + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// Used in the BPlusTree. Used for both leaf and index nodes. + /// Don't use a constructor or destructor, due to the memory pool I am using + template + struct RAK_DLL_EXPORT Page + { + // We use the same data structure for both leaf and index nodes. + // It uses a little more memory for index nodes but reduces + // memory fragmentation, allocations, and deallocations. + bool isLeaf; + + // Used for both leaf and index nodes. + // For a leaf it means the number of elements in data + // For an index it means the number of keys and is one less than the number of children pointers. + int size; + + // Used for both leaf and index nodes. + KeyType keys[order]; + + // Used only for leaf nodes. Data is the actual data, while next is the pointer to the next leaf (for B+) + DataType data[order]; + Page *next; + Page *previous; + + // Used only for index nodes. Pointers to the children of this node. + Page *children[order+1]; + }; + + /// A BPlus tree + /// Written with efficiency and speed in mind. + template + class RAK_DLL_EXPORT BPlusTree + { + public: + struct ReturnAction + { + KeyType key1; + KeyType key2; + enum + { + NO_ACTION, + REPLACE_KEY1_WITH_KEY2, + PUSH_KEY_TO_PARENT, + SET_BRANCH_KEY, + } action; // 0=none, 1=replace key1 with key2 + }; + + BPlusTree(); + ~BPlusTree(); + void SetPoolPageSize(int size); // Set the page size for the memory pool. Optionsl + bool Get(const KeyType key, DataType &out) const; + bool Delete(const KeyType key); + bool Delete(const KeyType key, DataType &out); + bool Insert(const KeyType key, const DataType &data); + void Clear(void); + unsigned Size(void) const; + bool IsEmpty(void) const; + Page *GetListHead(void) const; + DataType GetDataHead(void) const; + void PrintLeaves(void); + void ForEachLeaf(void (*func)(Page * leaf, int index)); + void ForEachData(void (*func)(DataType input, int index)); + void PrintGraph(void); + protected: + void ValidateTreeRecursive(Page *cur); + void DeleteFromPageAtIndex(const int index, Page *cur); + static void PrintLeaf(Page * leaf, int index); + void FreePages(void); + bool GetIndexOf(const KeyType key, Page *page, int *out) const; + void ShiftKeysLeft(Page *cur); + bool CanRotateLeft(Page *cur, int childIndex); + bool CanRotateRight(Page *cur, int childIndex); + void RotateRight(Page *cur, int childIndex, ReturnAction *returnAction); + void RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction); + Page* InsertIntoNode(const KeyType key, const DataType &childData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction); + Page* InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction* returnAction, bool *success); + Page* GetLeafFromKey(const KeyType key) const; + bool FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out); + bool FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction); + void ShiftNodeLeft(Page *cur); + void ShiftNodeRight(Page *cur); + + MemoryPool > pagePool; + Page *root, *leftmostLeaf; + }; + + template + BPlusTree::BPlusTree () + { + RakAssert(order>1); + root=0; + leftmostLeaf=0; + } + template + BPlusTree::~BPlusTree () + { + Clear(); + } + template + void BPlusTree::SetPoolPageSize(int size) + { + pagePool.SetPageSize(size); + } + template + bool BPlusTree::Get(const KeyType key, DataType &out) const + { + if (root==0) + return false; + + Page* leaf = GetLeafFromKey(key); + int childIndex; + + if (GetIndexOf(key, leaf, &childIndex)) + { + out=leaf->data[childIndex]; + return true; + } + return false; + } + template + void BPlusTree::DeleteFromPageAtIndex(const int index, Page *cur) + { + int i; + for (i=index; i < cur->size-1; i++) + cur->keys[i]=cur->keys[i+1]; + if (cur->isLeaf) + { + for (i=index; i < cur->size-1; i++) + cur->data[i]=cur->data[i+1]; + } + else + { + for (i=index; i < cur->size-1; i++) + cur->children[i+1]=cur->children[i+2]; + } + cur->size--; + } + template + bool BPlusTree::Delete(const KeyType key) + { + DataType temp; + return Delete(key, temp); + } + template + bool BPlusTree::Delete(const KeyType key, DataType &out) + { + if (root==0) + return false; + + ReturnAction returnAction; + returnAction.action=ReturnAction::NO_ACTION; + int childIndex; + bool underflow=false; + if (root==leftmostLeaf) + { + if (GetIndexOf(key, root, &childIndex)==false) + return false; + out=root->data[childIndex]; + DeleteFromPageAtIndex(childIndex,root); + if (root->size==0) + { + pagePool.Release(root, _FILE_AND_LINE_); + root=0; + leftmostLeaf=0; + } + return true; + } + else if (FindDeleteRebalance(key, root, &underflow,root->keys[0], &returnAction, out)==false) + return false; + +// RakAssert(returnAction.action==ReturnAction::NO_ACTION); + + if (underflow && root->size==0) + { + // Move the root down. + Page *oldRoot=root; + root=root->children[0]; + pagePool.Release(oldRoot, _FILE_AND_LINE_); + // memset(oldRoot,0,sizeof(root)); + } + + return true; + } + template + bool BPlusTree::FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out) + { + // Get index of child to follow. + int branchIndex, childIndex; + if (GetIndexOf(key, cur, &childIndex)) + branchIndex=childIndex+1; + else + branchIndex=childIndex; + + // If child is not a leaf, call recursively + if (cur->children[branchIndex]->isLeaf==false) + { + if (branchIndexsize) + rightRootKey=cur->keys[branchIndex]; // Shift right to left + else + rightRootKey=cur->keys[branchIndex-1]; // Shift center to left + + if (FindDeleteRebalance(key, cur->children[branchIndex], underflow, rightRootKey, returnAction, out)==false) + return false; + + // Call again in case the root key changed + if (branchIndexsize) + rightRootKey=cur->keys[branchIndex]; // Shift right to left + else + rightRootKey=cur->keys[branchIndex-1]; // Shift center to left + + if (returnAction->action==ReturnAction::SET_BRANCH_KEY && branchIndex!=childIndex) + { + returnAction->action=ReturnAction::NO_ACTION; + cur->keys[childIndex]=returnAction->key1; + + if (branchIndexsize) + rightRootKey=cur->keys[branchIndex]; // Shift right to left + else + rightRootKey=cur->keys[branchIndex-1]; // Shift center to left + } + } + else + { + // If child is a leaf, get the index of the key. If the item is not found, cancel delete. + if (GetIndexOf(key, cur->children[branchIndex], &childIndex)==false) + return false; + + // Delete: + // Remove childIndex from the child at branchIndex + out=cur->children[branchIndex]->data[childIndex]; + DeleteFromPageAtIndex(childIndex, cur->children[branchIndex]); + + if (childIndex==0) + { + if (branchIndex>0) + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + + if (branchIndex==0) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=cur->children[0]->keys[0]; + } + } + + if (cur->children[branchIndex]->size < order/2) + *underflow=true; + else + *underflow=false; + } + + // Fix underflow: + if (*underflow) + { + *underflow=FixUnderflow(branchIndex, cur, rightRootKey, returnAction); + } + + return true; + } + template + bool BPlusTree::FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction) + { + // Borrow from a neighbor that has excess. + Page *source; + Page *dest; + + if (branchIndex>0 && cur->children[branchIndex-1]->size > order/2) + { + dest=cur->children[branchIndex]; + source=cur->children[branchIndex-1]; + + // Left has excess + ShiftNodeRight(dest); + if (dest->isLeaf) + { + dest->keys[0]=source->keys[source->size-1]; + dest->data[0]=source->data[source->size-1]; + } + else + { + dest->children[0]=source->children[source->size]; + dest->keys[0]=cur->keys[branchIndex-1]; + } + // Update the parent key for the child (middle) + cur->keys[branchIndex-1]=source->keys[source->size-1]; + source->size--; + + // if (branchIndex==0) + // { + // returnAction->action=ReturnAction::SET_BRANCH_KEY; + // returnAction->key1=dest->keys[0]; + // } + + // No underflow + return false; + } + else if (branchIndexsize && cur->children[branchIndex+1]->size > order/2) + { + dest=cur->children[branchIndex]; + source=cur->children[branchIndex+1]; + + // Right has excess + if (dest->isLeaf) + { + dest->keys[dest->size]=source->keys[0]; + dest->data[dest->size]=source->data[0]; + + // The first key in the leaf after shifting is the parent key for the right branch + cur->keys[branchIndex]=source->keys[1]; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (order<=3 && dest->size==0) + { + if (branchIndex==0) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=dest->keys[0]; + } + else + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + } + } + else + { + if (returnAction->action==ReturnAction::NO_ACTION) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=dest->keys[0]; + } + + dest->keys[dest->size]=rightRootKey; + dest->children[dest->size+1]=source->children[0]; + + // The shifted off key is the leftmost key for a node + cur->keys[branchIndex]=source->keys[0]; + } + + + dest->size++; + ShiftNodeLeft(source); + + //cur->keys[branchIndex]=source->keys[0]; + +// returnAction->action=ReturnAction::SET_BRANCH_KEY; +// returnAction->key1=dest->keys[dest->size-1]; + + // No underflow + return false; + } + else + { + int sourceIndex; + + // If no neighbors have excess, merge two branches. + // + // To merge two leaves, just copy the data and keys over. + // + // To merge two branches, copy the pointers and keys over, using rightRootKey as the key for the extra pointer + if (branchIndexsize) + { + // Merge right child to current child and delete right child. + dest=cur->children[branchIndex]; + source=cur->children[branchIndex+1]; + } + else + { + // Move current child to left and delete current child + dest=cur->children[branchIndex-1]; + source=cur->children[branchIndex]; + } + + // Merge + if (dest->isLeaf) + { + for (sourceIndex=0; sourceIndexsize; sourceIndex++) + { + dest->keys[dest->size]=source->keys[sourceIndex]; + dest->data[dest->size++]=source->data[sourceIndex]; + } + } + else + { + // We want the tree root key of the source, not the current. + dest->keys[dest->size]=rightRootKey; + dest->children[dest->size++ + 1]=source->children[0]; + for (sourceIndex=0; sourceIndexsize; sourceIndex++) + { + dest->keys[dest->size]=source->keys[sourceIndex]; + dest->children[dest->size++ + 1]=source->children[sourceIndex + 1]; + } + } + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (order<=3 && branchIndex>0 && cur->children[branchIndex]->isLeaf) // With order==2 it is possible to delete data[0], which is not possible with higher orders. + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + + if (branchIndexsize) + { + // Update the parent key, removing the source (right) + DeleteFromPageAtIndex(branchIndex, cur); + } + else + { + if (branchIndex>0) + { + // Update parent key, removing the source (current) + DeleteFromPageAtIndex(branchIndex-1, cur); + } + } + + if (branchIndex==0 && dest->isLeaf) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=dest->keys[0]; + } + + if (source==leftmostLeaf) + leftmostLeaf=source->next; + + if (source->isLeaf) + { + if (source->previous) + source->previous->next=source->next; + if (source->next) + source->next->previous=source->previous; + } + + // Free the source node + pagePool.Release(source, _FILE_AND_LINE_); + // memset(source,0,sizeof(root)); + + // Return underflow or not of parent. + return cur->size < order/2; + } + } + template + void BPlusTree::ShiftNodeRight(Page *cur) + { + int i; + for (i=cur->size; i>0; i--) + cur->keys[i]=cur->keys[i-1]; + if (cur->isLeaf) + { + for (i=cur->size; i>0; i--) + cur->data[i]=cur->data[i-1]; + } + else + { + for (i=cur->size+1; i>0; i--) + cur->children[i]=cur->children[i-1]; + } + + cur->size++; + } + template + void BPlusTree::ShiftNodeLeft(Page *cur) + { + int i; + for (i=0; i < cur->size-1; i++) + cur->keys[i]=cur->keys[i+1]; + if (cur->isLeaf) + { + for (i=0; i < cur->size; i++) + cur->data[i]=cur->data[i+1]; + } + else + { + for (i=0; i < cur->size; i++) + cur->children[i]=cur->children[i+1]; + } + cur->size--; + } + template + Page* BPlusTree::InsertIntoNode(const KeyType key, const DataType &leafData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction) + { + int i; + if (cur->size < order) + { + for (i=cur->size; i > insertionIndex; i--) + cur->keys[i]=cur->keys[i-1]; + if (cur->isLeaf) + { + for (i=cur->size; i > insertionIndex; i--) + cur->data[i]=cur->data[i-1]; + } + else + { + for (i=cur->size+1; i > insertionIndex+1; i--) + cur->children[i]=cur->children[i-1]; + } + cur->keys[insertionIndex]=key; + if (cur->isLeaf) + cur->data[insertionIndex]=leafData; + else + cur->children[insertionIndex+1]=nodeData; + + cur->size++; + } + else + { + Page* newPage = pagePool.Allocate( _FILE_AND_LINE_ ); + newPage->isLeaf=cur->isLeaf; + if (cur->isLeaf) + { + newPage->next=cur->next; + if (cur->next) + cur->next->previous=newPage; + newPage->previous=cur; + cur->next=newPage; + } + + int destIndex, sourceIndex; + + if (insertionIndex>=(order+1)/2) + { + destIndex=0; + sourceIndex=order/2; + + for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) + { + newPage->keys[destIndex]=cur->keys[sourceIndex]; + } + newPage->keys[destIndex++]=key; + for (; sourceIndex < order; sourceIndex++, destIndex++) + { + newPage->keys[destIndex]=cur->keys[sourceIndex]; + } + + destIndex=0; + sourceIndex=order/2; + if (cur->isLeaf) + { + for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) + { + newPage->data[destIndex]=cur->data[sourceIndex]; + } + newPage->data[destIndex++]=leafData; + for (; sourceIndex < order; sourceIndex++, destIndex++) + { + newPage->data[destIndex]=cur->data[sourceIndex]; + } + } + else + { + + for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) + { + newPage->children[destIndex]=cur->children[sourceIndex+1]; + } + newPage->children[destIndex++]=nodeData; + + // sourceIndex+1 is sort of a hack but it works - because there is one extra child than keys + // skip past the last child for cur + for (; sourceIndex+1 < cur->size+1; sourceIndex++, destIndex++) + { + newPage->children[destIndex]=cur->children[sourceIndex+1]; + } + + // the first key is the middle key. Remove it from the page and push it to the parent + returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; + returnAction->key1=newPage->keys[0]; + for (int i=0; i < destIndex-1; i++) + newPage->keys[i]=newPage->keys[i+1]; + + } + cur->size=order/2; + } + else + { + destIndex=0; + sourceIndex=(order+1)/2-1; + for (; sourceIndex < order; sourceIndex++, destIndex++) + newPage->keys[destIndex]=cur->keys[sourceIndex]; + destIndex=0; + if (cur->isLeaf) + { + sourceIndex=(order+1)/2-1; + for (; sourceIndex < order; sourceIndex++, destIndex++) + newPage->data[destIndex]=cur->data[sourceIndex]; + } + else + { + sourceIndex=(order+1)/2; + for (; sourceIndex < order+1; sourceIndex++, destIndex++) + newPage->children[destIndex]=cur->children[sourceIndex]; + + // the first key is the middle key. Remove it from the page and push it to the parent + returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; + returnAction->key1=newPage->keys[0]; + for (int i=0; i < destIndex-1; i++) + newPage->keys[i]=newPage->keys[i+1]; + } + cur->size=(order+1)/2-1; + if (cur->size) + { + bool b = GetIndexOf(key, cur, &insertionIndex); + (void) b; + RakAssert(b==false); + } + else + insertionIndex=0; + InsertIntoNode(key, leafData, insertionIndex, nodeData, cur, returnAction); + } + + newPage->size=destIndex; + + return newPage; + } + + return 0; + } + + template + bool BPlusTree::CanRotateLeft(Page *cur, int childIndex) + { + return childIndex>0 && cur->children[childIndex-1]->size + void BPlusTree::RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction) + { + Page *dest = cur->children[childIndex-1]; + Page *source = cur->children[childIndex]; + returnAction->key1=source->keys[0]; + dest->keys[dest->size]=source->keys[0]; + dest->data[dest->size]=source->data[0]; + dest->size++; + for (int i=0; i < source->size-1; i++) + { + source->keys[i]=source->keys[i+1]; + source->data[i]=source->data[i+1]; + } + source->size--; + cur->keys[childIndex-1]=source->keys[0]; + returnAction->key2=source->keys[0]; + } + + template + bool BPlusTree::CanRotateRight(Page *cur, int childIndex) + { + return childIndex < cur->size && cur->children[childIndex+1]->size + void BPlusTree::RotateRight(Page *cur, int childIndex, ReturnAction *returnAction) + { + Page *dest = cur->children[childIndex+1]; + Page *source = cur->children[childIndex]; + returnAction->key1=dest->keys[0]; + for (int i= dest->size; i > 0; i--) + { + dest->keys[i]=dest->keys[i-1]; + dest->data[i]=dest->data[i-1]; + } + dest->keys[0]=source->keys[source->size-1]; + dest->data[0]=source->data[source->size-1]; + dest->size++; + source->size--; + + cur->keys[childIndex]=dest->keys[0]; + returnAction->key2=dest->keys[0]; + } + template + Page* BPlusTree::GetLeafFromKey(const KeyType key) const + { + Page* cur = root; + int childIndex; + while (cur->isLeaf==false) + { + // When searching, if we match the exact key we go down the pointer after that index + if (GetIndexOf(key, cur, &childIndex)) + childIndex++; + cur = cur->children[childIndex]; + } + return cur; + } + + template + Page* BPlusTree::InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction *returnAction, bool *success) + { + int childIndex; + int branchIndex; + if (GetIndexOf(key, cur, &childIndex)) + branchIndex=childIndex+1; + else + branchIndex=childIndex; + Page* newPage; + if (cur->isLeaf==false) + { + if (cur->children[branchIndex]->isLeaf==true && cur->children[branchIndex]->size==order) + { + if (branchIndex==childIndex+1) + { + *success=false; + return 0; // Already exists + } + + if (CanRotateLeft(cur, branchIndex)) + { + returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; + if (key > cur->children[branchIndex]->keys[0]) + { + RotateLeft(cur, branchIndex, returnAction); + + int insertionIndex; + GetIndexOf(key, cur->children[branchIndex], &insertionIndex); + InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); + } + else + { + // Move head element to left and replace it with key,data + Page* dest=cur->children[branchIndex-1]; + Page* source=cur->children[branchIndex]; + returnAction->key1=source->keys[0]; + returnAction->key2=key; + dest->keys[dest->size]=source->keys[0]; + dest->data[dest->size]=source->data[0]; + dest->size++; + source->keys[0]=key; + source->data[0]=data; + } + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + + return 0; + } + else if (CanRotateRight(cur, branchIndex)) + { + returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; + + if (key < cur->children[branchIndex]->keys[cur->children[branchIndex]->size-1]) + { + RotateRight(cur, branchIndex, returnAction); + + int insertionIndex; + GetIndexOf(key, cur->children[branchIndex], &insertionIndex); + InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); + + } + else + { + // Insert to the head of the right leaf instead and change our key + returnAction->key1=cur->children[branchIndex+1]->keys[0]; + InsertIntoNode(key, data, 0, 0, cur->children[branchIndex+1], 0); + returnAction->key2=key; + } + cur->keys[branchIndex]=cur->children[branchIndex+1]->keys[0]; + return 0; + } + } + + newPage=InsertBranchDown(key,data,cur->children[branchIndex], returnAction, success); + if (returnAction->action==ReturnAction::REPLACE_KEY1_WITH_KEY2) + { + if (branchIndex>0 && cur->keys[branchIndex-1]==returnAction->key1) + cur->keys[branchIndex-1]=returnAction->key2; + } + if (newPage) + { + if (newPage->isLeaf==false) + { + RakAssert(returnAction->action==ReturnAction::PUSH_KEY_TO_PARENT); + newPage->size--; + return InsertIntoNode(returnAction->key1, data, branchIndex, newPage, cur, returnAction); + } + else + { + return InsertIntoNode(newPage->keys[0], data, branchIndex, newPage, cur, returnAction); + } + } + } + else + { + if (branchIndex==childIndex+1) + { + *success=false; + return 0; // Already exists + } + else + { + return InsertIntoNode(key, data, branchIndex, 0, cur, returnAction); + } + } + + return 0; + } + template + bool BPlusTree::Insert(const KeyType key, const DataType &data) + { + if (root==0) + { + // Allocate root and make root a leaf + root = pagePool.Allocate( _FILE_AND_LINE_ ); + root->isLeaf=true; + leftmostLeaf=root; + root->size=1; + root->keys[0]=key; + root->data[0]=data; + root->next=0; + root->previous=0; + } + else + { + bool success=true; + ReturnAction returnAction; + returnAction.action=ReturnAction::NO_ACTION; + Page* newPage = InsertBranchDown(key, data, root, &returnAction, &success); + if (success==false) + return false; + if (newPage) + { + KeyType newKey; + if (newPage->isLeaf==false) + { + // One key is pushed up through the stack. I store that at keys[0] but it has to be removed for the page to be correct + RakAssert(returnAction.action==ReturnAction::PUSH_KEY_TO_PARENT); + newKey=returnAction.key1; + newPage->size--; + } + else + newKey = newPage->keys[0]; + // propagate the root + Page* newRoot = pagePool.Allocate( _FILE_AND_LINE_ ); + newRoot->isLeaf=false; + newRoot->size=1; + newRoot->keys[0]=newKey; + newRoot->children[0]=root; + newRoot->children[1]=newPage; + root=newRoot; + } + } + + return true; + } + template + void BPlusTree::ShiftKeysLeft(Page *cur) + { + int i; + for (i=0; i < cur->size; i++) + cur->keys[i]=cur->keys[i+1]; + } + template + void BPlusTree::Clear(void) + { + if (root) + { + FreePages(); + leftmostLeaf=0; + root=0; + } + pagePool.Clear(_FILE_AND_LINE_); + } + template + unsigned BPlusTree::Size(void) const + { + int count=0; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + count+=cur->size; + cur=cur->next; + } + return count; + } + template + bool BPlusTree::IsEmpty(void) const + { + return root==0; + } + template + bool BPlusTree::GetIndexOf(const KeyType key, Page *page, int *out) const + { + RakAssert(page->size>0); + int index, upperBound, lowerBound; + upperBound=page->size-1; + lowerBound=0; + index = page->size/2; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + if (key==page->keys[index]) + { + *out=index; + return true; + } + else if (keykeys[index]) + upperBound=index-1; + else + lowerBound=index+1; + + index=lowerBound+(upperBound-lowerBound)/2; + + if (lowerBound>upperBound) + { + *out=lowerBound; + return false; // No match + } + } + } + template + void BPlusTree::FreePages(void) + { + DataStructures::Queue *> queue; + DataStructures::Page *ptr; + int i; + queue.Push(root, _FILE_AND_LINE_ ); + while (queue.Size()) + { + ptr=queue.Pop(); + if (ptr->isLeaf==false) + { + for (i=0; i < ptr->size+1; i++) + queue.Push(ptr->children[i], _FILE_AND_LINE_ ); + } + pagePool.Release(ptr, _FILE_AND_LINE_); + // memset(ptr,0,sizeof(root)); + }; + } + template + Page *BPlusTree::GetListHead(void) const + { + return leftmostLeaf; + } + template + DataType BPlusTree::GetDataHead(void) const + { + return leftmostLeaf->data[0]; + } + template + void BPlusTree::ForEachLeaf(void (*func)(Page * leaf, int index)) + { + int count=0; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + func(cur, count++); + cur=cur->next; + } + } + template + void BPlusTree::ForEachData(void (*func)(DataType input, int index)) + { + int count=0,i; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + for (i=0; i < cur->size; i++) + func(cur->data[i], count++); + cur=cur->next; + } + } + template + void BPlusTree::PrintLeaf(Page * leaf, int index) + { + int i; + RAKNET_DEBUG_PRINTF("%i] SELF=%p\n", index+1, leaf); + for (i=0; i < leaf->size; i++) + RAKNET_DEBUG_PRINTF(" %i. %i\n", i+1, leaf->data[i]); + } + template + void BPlusTree::PrintLeaves(void) + { + ForEachLeaf(PrintLeaf); + } + + template + void BPlusTree::ValidateTreeRecursive(Page *cur) + { + RakAssert(cur==root || cur->size>=order/2); + + if (cur->children[0]->isLeaf) + { + RakAssert(cur->children[0]->keys[0] < cur->keys[0]); + for (int i=0; i < cur->size; i++) + { + RakAssert(cur->children[i+1]->keys[0]==cur->keys[i]); + } + } + else + { + for (int i=0; i < cur->size+1; i++) + ValidateTreeRecursive(cur->children[i]); + } + } + + template + void BPlusTree::PrintGraph(void) + { + DataStructures::Queue *> queue; + queue.Push(root,_FILE_AND_LINE_); + queue.Push(0,_FILE_AND_LINE_); + DataStructures::Page *ptr; + int i,j; + if (root) + { + RAKNET_DEBUG_PRINTF("%p(", root); + for (i=0; i < root->size; i++) + { + RAKNET_DEBUG_PRINTF("%i ", root->keys[i]); + } + RAKNET_DEBUG_PRINTF(") "); + RAKNET_DEBUG_PRINTF("\n"); + } + while (queue.Size()) + { + ptr=queue.Pop(); + if (ptr==0) + RAKNET_DEBUG_PRINTF("\n"); + else if (ptr->isLeaf==false) + { + for (i=0; i < ptr->size+1; i++) + { + RAKNET_DEBUG_PRINTF("%p(", ptr->children[i]); + //RAKNET_DEBUG_PRINTF("(", ptr->children[i]); + for (j=0; j < ptr->children[i]->size; j++) + RAKNET_DEBUG_PRINTF("%i ", ptr->children[i]->keys[j]); + RAKNET_DEBUG_PRINTF(") "); + queue.Push(ptr->children[i],_FILE_AND_LINE_); + } + queue.Push(0,_FILE_AND_LINE_); + RAKNET_DEBUG_PRINTF(" -- "); + } + } + RAKNET_DEBUG_PRINTF("\n"); + } +} +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + +// Code to test this hellish data structure. +/* +#include "DS_BPlusTree.h" +#include + +// Handle underflow on root. If there is only one item left then I can go downwards. +// Make sure I keep the leftmost pointer valid by traversing it +// When I free a leaf, be sure to adjust the pointers around it. + +#include "Rand.h" + +void main(void) +{ + DataStructures::BPlusTree btree; + DataStructures::List haveList, removedList; + int temp; + int i, j, index; + int testSize; + bool b; + + for (testSize=0; testSize < 514; testSize++) + { + RAKNET_DEBUG_PRINTF("TestSize=%i\n", testSize); + + for (i=0; i < testSize; i++) + haveList.Insert(i); + + for (i=0; i < testSize; i++) + { + index=i+randomMT()%(testSize-i); + temp=haveList[index]; + haveList[index]=haveList[i]; + haveList[i]=temp; + } + + for (i=0; i - * - * OR - * - * AVLBalancedBinarySearchTree - * - * Use the AVL balanced tree if you want the tree to be balanced after every deletion and addition. This avoids the potential - * worst case scenario where ordered input to a binary search tree gives linear search time results. It's not needed - * if input will be evenly distributed, in which case the search time is O (log n). The search time for the AVL - * balanced binary tree is O (log n) irregardless of input. - * - * Has the following member functions - * unsigned int Height() - Returns the height of the tree at the optional specified starting index. Default is the root - * add(element) - adds an element to the BinarySearchTree - * bool del(element) - deletes the node containing element if the element is in the tree as defined by a comparison with the == operator. Returns true on success, false if the element is not found - * bool IsInelement) - returns true if element is in the tree as defined by a comparison with the == operator. Otherwise returns false - * DisplayInorder(array) - Fills an array with an inorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. - * DisplayPreorder(array) - Fills an array with an preorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. - * DisplayPostorder(array) - Fills an array with an postorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. - * DisplayBreadthFirstSearch(array) - Fills an array with a breadth first search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. - * clear - Destroys the tree. Same as calling the destructor - * unsigned int Height() - Returns the height of the tree - * unsigned int size() - returns the size of the BinarySearchTree - * GetPointerToNode(element) - returns a pointer to the comparision element in the tree, allowing for direct modification when necessary with complex data types. - * Be warned, it is possible to corrupt the tree if the element used for comparisons is modified. Returns NULL if the item is not found - * - * - * EXAMPLE - * @code - * BinarySearchTree A; - * A.Add(10); - * A.Add(15); - * A.Add(5); - * int* array = RakNet::OP_NEW(A.Size(), _FILE_AND_LINE_ ); - * A.DisplayInorder(array); - * array[0]; // returns 5 - * array[1]; // returns 10 - * array[2]; // returns 15 - * @endcode - * compress - reallocates memory to fit the number of elements. Best used when the number of elements decreases - * - * clear - empties the BinarySearchTree and returns storage - * The assignment and copy constructors are defined - * - * \note The template type must have the copy constructor and - * assignment operator defined and must work with >, <, and == All - * elements in the tree MUST be distinct The assignment operator is - * defined between BinarySearchTree and AVLBalancedBinarySearchTree - * as long as they are of the same template type. However, passing a - * BinarySearchTree to an AVLBalancedBinarySearchTree will lose its - * structure unless it happened to be AVL balanced to begin with - * Requires queue_linked_list.cpp for the breadth first search used - * in the copy constructor, overloaded assignment operator, and - * display_breadth_first_search. - * - * - */ - template - class RAK_DLL_EXPORT BinarySearchTree - { - - public: - - struct node - { - BinarySearchTreeType* item; - node* left; - node* right; - }; - - BinarySearchTree(); - virtual ~BinarySearchTree(); - BinarySearchTree( const BinarySearchTree& original_type ); - BinarySearchTree& operator= ( const BinarySearchTree& original_copy ); - unsigned int Size( void ); - void Clear( const char *file, unsigned int line ); - unsigned int Height( node* starting_node = 0 ); - node* Add ( const BinarySearchTreeType& input, const char *file, unsigned int line ); - node* Del( const BinarySearchTreeType& input, const char *file, unsigned int line ); - bool IsIn( const BinarySearchTreeType& input ); - void DisplayInorder( BinarySearchTreeType* return_array ); - void DisplayPreorder( BinarySearchTreeType* return_array ); - void DisplayPostorder( BinarySearchTreeType* return_array ); - void DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ); - BinarySearchTreeType*& GetPointerToNode( const BinarySearchTreeType& element ); - - protected: - - node* root; - - enum Direction_Types - { - NOT_FOUND, LEFT, RIGHT, ROOT - } direction; - unsigned int HeightRecursive( node* current ); - unsigned int BinarySearchTree_size; - node*& Find( const BinarySearchTreeType& element, node** parent ); - node*& FindParent( const BinarySearchTreeType& element ); - void DisplayPostorderRecursive( node* current, BinarySearchTreeType* return_array, unsigned int& index ); - void FixTree( node* current ); - - }; - - /// An AVLBalancedBinarySearchTree is a binary tree that is always balanced - template - class RAK_DLL_EXPORT AVLBalancedBinarySearchTree : public BinarySearchTree - { - - public: - AVLBalancedBinarySearchTree() {} - virtual ~AVLBalancedBinarySearchTree(); - void Add ( const BinarySearchTreeType& input ); - void Del( const BinarySearchTreeType& input ); - BinarySearchTree& operator= ( BinarySearchTree& original_copy ) - { - return BinarySearchTree::operator= ( original_copy ); - } - - private: - void BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ); - void RotateRight( typename BinarySearchTree::node *C ); - void RotateLeft( typename BinarySearchTree::node* C ); - void DoubleRotateRight( typename BinarySearchTree::node *A ); - void DoubleRotateLeft( typename BinarySearchTree::node* A ); - bool RightHigher( typename BinarySearchTree::node* A ); - bool LeftHigher( typename BinarySearchTree::node* A ); - }; - - template - void AVLBalancedBinarySearchTree::BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ) - { - int left_height, right_height; - - while ( current ) - { - if ( current->left == 0 ) - left_height = 0; - else - left_height = Height( current->left ); - - if ( current->right == 0 ) - right_height = 0; - else - right_height = Height( current->right ); - - if ( right_height - left_height == 2 ) - { - if ( RightHigher( current->right ) ) - RotateLeft( current->right ); - else - DoubleRotateLeft( current ); - - if ( rotateOnce ) - break; - } - - else - if ( right_height - left_height == -2 ) - { - if ( LeftHigher( current->left ) ) - RotateRight( current->left ); - else - DoubleRotateRight( current ); - - if ( rotateOnce ) - break; - } - - if ( current == this->root ) - break; - - current = FindParent( *( current->item ) ); - - } - } - - template - void AVLBalancedBinarySearchTree::Add ( const BinarySearchTreeType& input ) - { - - typename BinarySearchTree::node * current = BinarySearchTree::Add ( input, _FILE_AND_LINE_ ); - BalanceTree( current, true ); - } - - template - void AVLBalancedBinarySearchTree::Del( const BinarySearchTreeType& input ) - { - typename BinarySearchTree::node * current = BinarySearchTree::Del( input, _FILE_AND_LINE_ ); - BalanceTree( current, false ); - - } - - template - bool AVLBalancedBinarySearchTree::RightHigher( typename BinarySearchTree::node *A ) - { - if ( A == 0 ) - return false; - - return Height( A->right ) > Height( A->left ); - } - - template - bool AVLBalancedBinarySearchTree::LeftHigher( typename BinarySearchTree::node *A ) - { - if ( A == 0 ) - return false; - - return Height( A->left ) > Height( A->right ); - } - - template - void AVLBalancedBinarySearchTree::RotateRight( typename BinarySearchTree::node *C ) - { - typename BinarySearchTree::node * A, *B, *D; - /* - RIGHT ROTATION - - A = parent(b) - b= parent(c) - c = node to rotate around - - A - | // Either direction - B - / \ - C - / \ - D - - TO - - A - | // Either Direction - C - / \ - B - / \ - D - - - - - */ - - B = FindParent( *( C->item ) ); - A = FindParent( *( B->item ) ); - D = C->right; - - if ( A ) - { - // Direction was set by the last find_parent call - - if ( this->direction == this->LEFT ) - A->left = C; - else - A->right = C; - } - - else - this->root = C; // If B has no parent parent then B must have been the root node - - B->left = D; - - C->right = B; - } - - template - void AVLBalancedBinarySearchTree::DoubleRotateRight( typename BinarySearchTree::node *A ) - { - // The left side of the left child must be higher for the tree to balance with a right rotation. If it isn't, rotate it left before the normal rotation so it is. - RotateLeft( A->left->right ); - RotateRight( A->left ); - } - - template - void AVLBalancedBinarySearchTree::RotateLeft( typename BinarySearchTree::node *C ) - { - typename BinarySearchTree::node * A, *B, *D; - /* - RIGHT ROTATION - - A = parent(b) - b= parent(c) - c = node to rotate around - - A - | // Either direction - B - / \ - C - / \ - D - - TO - - A - | // Either Direction - C - / \ - B - / \ - D - - - - - */ - - B = FindParent( *( C->item ) ); - A = FindParent( *( B->item ) ); - D = C->left; - - if ( A ) - { - // Direction was set by the last find_parent call - - if ( this->direction == this->LEFT ) - A->left = C; - else - A->right = C; - } - - else - this->root = C; // If B has no parent parent then B must have been the root node - - B->right = D; - - C->left = B; - } - - template - void AVLBalancedBinarySearchTree::DoubleRotateLeft( typename BinarySearchTree::node *A ) - { - // The left side of the right child must be higher for the tree to balance with a left rotation. If it isn't, rotate it right before the normal rotation so it is. - RotateRight( A->right->left ); - RotateLeft( A->right ); - } - - template - AVLBalancedBinarySearchTree::~AVLBalancedBinarySearchTree() - { - this->Clear(_FILE_AND_LINE_); - } - - template - unsigned int BinarySearchTree::Size( void ) - { - return BinarySearchTree_size; - } - - template - unsigned int BinarySearchTree::Height( typename BinarySearchTree::node* starting_node ) - { - if ( BinarySearchTree_size == 0 || starting_node == 0 ) - return 0; - else - return HeightRecursive( starting_node ); - } - - // Recursively return the height of a binary tree - template - unsigned int BinarySearchTree::HeightRecursive( typename BinarySearchTree::node* current ) - { - unsigned int left_height = 0, right_height = 0; - - if ( ( current->left == 0 ) && ( current->right == 0 ) ) - return 1; // Leaf - - if ( current->left != 0 ) - left_height = 1 + HeightRecursive( current->left ); - - if ( current->right != 0 ) - right_height = 1 + HeightRecursive( current->right ); - - if ( left_height > right_height ) - return left_height; - else - return right_height; - } - - template - BinarySearchTree::BinarySearchTree() - { - BinarySearchTree_size = 0; - root = 0; - } - - template - BinarySearchTree::~BinarySearchTree() - { - this->Clear(_FILE_AND_LINE_); - } - - template - BinarySearchTreeType*& BinarySearchTree::GetPointerToNode( const BinarySearchTreeType& element ) - { - static typename BinarySearchTree::node * tempnode; - static BinarySearchTreeType* dummyptr = 0; - tempnode = Find ( element, &tempnode ); - - if ( this->direction == this->NOT_FOUND ) - return dummyptr; - - return tempnode->item; - } - - template - typename BinarySearchTree::node*& BinarySearchTree::Find( const BinarySearchTreeType& element, typename BinarySearchTree::node** parent ) - { - static typename BinarySearchTree::node * current; - - current = this->root; - *parent = 0; - this->direction = this->ROOT; - - if ( BinarySearchTree_size == 0 ) - { - this->direction = this->NOT_FOUND; - return current = 0; - } - - // Check if the item is at the root - if ( element == *( current->item ) ) - { - this->direction = this->ROOT; - return current; - } - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( true ) - { - // Move pointer - - if ( element < *( current->item ) ) - { - *parent = current; - this->direction = this->LEFT; - current = current->left; - } - - else - if ( element > *( current->item ) ) - { - *parent = current; - this->direction = this->RIGHT; - current = current->right; - } - - if ( current == 0 ) - break; - - // Check if new position holds the item - if ( element == *( current->item ) ) - { - return current; - } - } - - - this->direction = this->NOT_FOUND; - return current = 0; - } - - template - typename BinarySearchTree::node*& BinarySearchTree::FindParent( const BinarySearchTreeType& element ) - { - static typename BinarySearchTree::node * parent; - Find ( element, &parent ); - return parent; - } - - // Performs a series of value swaps starting with current to fix the tree if needed - template - void BinarySearchTree::FixTree( typename BinarySearchTree::node* current ) - { - BinarySearchTreeType temp; - - while ( 1 ) - { - if ( ( ( current->left ) != 0 ) && ( *( current->item ) < *( current->left->item ) ) ) - { - // Swap the current value with the one to the left - temp = *( current->left->item ); - *( current->left->item ) = *( current->item ); - *( current->item ) = temp; - current = current->left; - } - - else - if ( ( ( current->right ) != 0 ) && ( *( current->item ) > *( current->right->item ) ) ) - { - // Swap the current value with the one to the right - temp = *( current->right->item ); - *( current->right->item ) = *( current->item ); - *( current->item ) = temp; - current = current->right; - } - - else - break; // current points to the right place so quit - } - } - - template - typename BinarySearchTree::node* BinarySearchTree::Del( const BinarySearchTreeType& input, const char *file, unsigned int line ) - { - typename BinarySearchTree::node * node_to_delete, *current, *parent; - - if ( BinarySearchTree_size == 0 ) - return 0; - - if ( BinarySearchTree_size == 1 ) - { - Clear(file, line); - return 0; - } - - node_to_delete = Find( input, &parent ); - - if ( direction == NOT_FOUND ) - return 0; // Couldn't find the element - - current = node_to_delete; - - // Replace the deleted node with the appropriate value - if ( ( current->right ) == 0 && ( current->left ) == 0 ) // Leaf node, just remove it - { - - if ( parent ) - { - if ( direction == LEFT ) - parent->left = 0; - else - parent->right = 0; - } - - RakNet::OP_DELETE(node_to_delete->item, file, line); - RakNet::OP_DELETE(node_to_delete, file, line); - BinarySearchTree_size--; - return parent; - } - else - if ( ( current->right ) != 0 && ( current->left ) == 0 ) // Node has only one child, delete it and cause the parent to point to that child - { - - if ( parent ) - { - if ( direction == RIGHT ) - parent->right = current->right; - else - parent->left = current->right; - } - - else - root = current->right; // Without a parent this must be the root node - - RakNet::OP_DELETE(node_to_delete->item, file, line); - - RakNet::OP_DELETE(node_to_delete, file, line); - - BinarySearchTree_size--; - - return parent; - } - else - if ( ( current->right ) == 0 && ( current->left ) != 0 ) // Node has only one child, delete it and cause the parent to point to that child - { - - if ( parent ) - { - if ( direction == RIGHT ) - parent->right = current->left; - else - parent->left = current->left; - } - - else - root = current->left; // Without a parent this must be the root node - - RakNet::OP_DELETE(node_to_delete->item, file, line); - - RakNet::OP_DELETE(node_to_delete, file, line); - - BinarySearchTree_size--; - - return parent; - } - else // Go right, then as left as far as you can - { - parent = current; - direction = RIGHT; - current = current->right; // Must have a right branch because the if statements above indicated that it has 2 branches - - while ( current->left ) - { - direction = LEFT; - parent = current; - current = current->left; - } - - // Replace the value held by the node to RakNet::OP_DELETE(with the value pointed to by current, _FILE_AND_LINE_); - *( node_to_delete->item ) = *( current->item ); - - // Delete current. - // If it is a leaf node just delete it - if ( current->right == 0 ) - { - if ( direction == RIGHT ) - parent->right = 0; - else - parent->left = 0; - - RakNet::OP_DELETE(current->item, file, line); - - RakNet::OP_DELETE(current, file, line); - - BinarySearchTree_size--; - - return parent; - } - - else - { - // Skip this node and make its parent point to its right branch - - if ( direction == RIGHT ) - parent->right = current->right; - else - parent->left = current->right; - - RakNet::OP_DELETE(current->item, file, line); - - RakNet::OP_DELETE(current, file, line); - - BinarySearchTree_size--; - - return parent; - } - } - } - - template - typename BinarySearchTree::node* BinarySearchTree::Add ( const BinarySearchTreeType& input, const char *file, unsigned int line ) - { - typename BinarySearchTree::node * current; - - // Add the new element to the tree according to the following alogrithm: - // 1. If the current node is empty add the new leaf - // 2. If the element is less than the current node then go down the left branch - // 3. If the element is greater than the current node then go down the right branch - - if ( BinarySearchTree_size == 0 ) - { - BinarySearchTree_size = 1; - root = RakNet::OP_NEW( file, line ); - root->item = RakNet::OP_NEW( file, line ); - *( root->item ) = input; - root->left = 0; - root->right = 0; - - return root; - } - - else - { - // start at the root - current = root; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( true ) // This loop traverses the tree to find a spot for insertion - { - - if ( input < *( current->item ) ) - { - if ( current->left == 0 ) - { - current->left = RakNet::OP_NEW( file, line ); - current->left->item = RakNet::OP_NEW( file, line ); - current = current->left; - current->left = 0; - current->right = 0; - *( current->item ) = input; - - BinarySearchTree_size++; - return current; - } - - else - { - current = current->left; - } - } - - else - if ( input > *( current->item ) ) - { - if ( current->right == 0 ) - { - current->right = RakNet::OP_NEW( file, line ); - current->right->item = RakNet::OP_NEW( file, line ); - current = current->right; - current->left = 0; - current->right = 0; - *( current->item ) = input; - - BinarySearchTree_size++; - return current; - } - - else - { - current = current->right; - } - } - - else - return 0; // ((input == current->item) == true) which is not allowed since the tree only takes discrete values. Do nothing - } - } - } - - template - bool BinarySearchTree::IsIn( const BinarySearchTreeType& input ) - { - typename BinarySearchTree::node * parent; - find( input, &parent ); - - if ( direction != NOT_FOUND ) - return true; - else - return false; - } - - - template - void BinarySearchTree::DisplayInorder( BinarySearchTreeType* return_array ) - { - typename BinarySearchTree::node * current, *parent; - bool just_printed = false; - - unsigned int index = 0; - - current = root; - - if ( BinarySearchTree_size == 0 ) - return ; // Do nothing for an empty tree - - else - if ( BinarySearchTree_size == 1 ) - { - return_array[ 0 ] = *( root->item ); - return ; - } - - - direction = ROOT; // Reset the direction - - while ( index != BinarySearchTree_size ) - { - // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes - - if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) - { - // Go left if the following 2 conditions are true - // I can go left - // I did not just move up from a right child - // I did not just move up from a left child - - current = current->left; - direction = ROOT; // Reset the direction - } - - else - if ( ( direction != RIGHT ) && ( just_printed == false ) ) - { - // Otherwise, print the current node if the following 3 conditions are true: - // I did not just move up from a right child - // I did not print this ndoe last cycle - - return_array[ index++ ] = *( current->item ); - just_printed = true; - } - - else - if ( ( current->right != 0 ) && ( direction != RIGHT ) ) - { - // Otherwise, go right if the following 2 conditions are true - // I did not just move up from a right child - // I can go right - - current = current->right; - direction = ROOT; // Reset the direction - just_printed = false; - } - - else - { - // Otherwise I've done everything I can. Move up the tree one node - parent = FindParent( *( current->item ) ); - current = parent; - just_printed = false; - } - } - } - - template - void BinarySearchTree::DisplayPreorder( BinarySearchTreeType* return_array ) - { - typename BinarySearchTree::node * current, *parent; - - unsigned int index = 0; - - current = root; - - if ( BinarySearchTree_size == 0 ) - return ; // Do nothing for an empty tree - - else - if ( BinarySearchTree_size == 1 ) - { - return_array[ 0 ] = *( root->item ); - return ; - } - - - direction = ROOT; // Reset the direction - return_array[ index++ ] = *( current->item ); - - while ( index != BinarySearchTree_size ) - { - // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes - - if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) - { - - current = current->left; - direction = ROOT; - - // Everytime you move a node print it - return_array[ index++ ] = *( current->item ); - } - - else - if ( ( current->right != 0 ) && ( direction != RIGHT ) ) - { - current = current->right; - direction = ROOT; - - // Everytime you move a node print it - return_array[ index++ ] = *( current->item ); - } - - else - { - // Otherwise I've done everything I can. Move up the tree one node - parent = FindParent( *( current->item ) ); - current = parent; - } - } - } - - template - inline void BinarySearchTree::DisplayPostorder( BinarySearchTreeType* return_array ) - { - unsigned int index = 0; - - if ( BinarySearchTree_size == 0 ) - return ; // Do nothing for an empty tree - - else - if ( BinarySearchTree_size == 1 ) - { - return_array[ 0 ] = *( root->item ); - return ; - } - - DisplayPostorderRecursive( root, return_array, index ); - } - - - // Recursively do a postorder traversal - template - void BinarySearchTree::DisplayPostorderRecursive( typename BinarySearchTree::node* current, BinarySearchTreeType* return_array, unsigned int& index ) - { - if ( current->left != 0 ) - DisplayPostorderRecursive( current->left, return_array, index ); - - if ( current->right != 0 ) - DisplayPostorderRecursive( current->right, return_array, index ); - - return_array[ index++ ] = *( current->item ); - - } - - - template - void BinarySearchTree::DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ) - { - typename BinarySearchTree::node * current; - unsigned int index = 0; - - // Display the tree using a breadth first search - // Put the children of the current node into the queue - // Pop the queue, put its children into the queue, repeat until queue is empty - - if ( BinarySearchTree_size == 0 ) - return ; // Do nothing for an empty tree - - else - if ( BinarySearchTree_size == 1 ) - { - return_array[ 0 ] = *( root->item ); - return ; - } - - else - { - DataStructures::QueueLinkedList tree_queue; - - // Add the root of the tree I am copying from - tree_queue.Push( root ); - - do - { - current = tree_queue.Pop(); - return_array[ index++ ] = *( current->item ); - - // Add the child or children of the tree I am copying from to the queue - - if ( current->left != 0 ) - tree_queue.Push( current->left ); - - if ( current->right != 0 ) - tree_queue.Push( current->right ); - - } - - while ( tree_queue.Size() > 0 ); - } - } - - - template - BinarySearchTree::BinarySearchTree( const BinarySearchTree& original_copy ) - { - typename BinarySearchTree::node * current; - // Copy the tree using a breadth first search - // Put the children of the current node into the queue - // Pop the queue, put its children into the queue, repeat until queue is empty - - // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. - BinarySearchTree_size = 0; - root = 0; - - if ( original_copy.BinarySearchTree_size == 0 ) - { - BinarySearchTree_size = 0; - } - - else - { - DataStructures::QueueLinkedList tree_queue; - - // Add the root of the tree I am copying from - tree_queue.Push( original_copy.root ); - - do - { - current = tree_queue.Pop(); - - Add ( *( current->item ), _FILE_AND_LINE_ ) - - ; - - // Add the child or children of the tree I am copying from to the queue - if ( current->left != 0 ) - tree_queue.Push( current->left ); - - if ( current->right != 0 ) - tree_queue.Push( current->right ); - - } - - while ( tree_queue.Size() > 0 ); - } - } - - template - BinarySearchTree& BinarySearchTree::operator= ( const BinarySearchTree& original_copy ) - { - typename BinarySearchTree::node * current; - - if ( ( &original_copy ) == this ) - return *this; - - Clear( _FILE_AND_LINE_ ); // Remove the current tree - - // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. - BinarySearchTree_size = 0; - - root = 0; - - - // Copy the tree using a breadth first search - // Put the children of the current node into the queue - // Pop the queue, put its children into the queue, repeat until queue is empty - if ( original_copy.BinarySearchTree_size == 0 ) - { - BinarySearchTree_size = 0; - } - - else - { - DataStructures::QueueLinkedList tree_queue; - - // Add the root of the tree I am copying from - tree_queue.Push( original_copy.root ); - - do - { - current = tree_queue.Pop(); - - Add ( *( current->item ), _FILE_AND_LINE_ ) - - ; - - // Add the child or children of the tree I am copying from to the queue - if ( current->left != 0 ) - tree_queue.Push( current->left ); - - if ( current->right != 0 ) - tree_queue.Push( current->right ); - - } - - while ( tree_queue.Size() > 0 ); - } - - return *this; - } - - template - inline void BinarySearchTree::Clear ( const char *file, unsigned int line ) - { - typename BinarySearchTree::node * current, *parent; - - current = root; - - while ( BinarySearchTree_size > 0 ) - { - if ( BinarySearchTree_size == 1 ) - { - RakNet::OP_DELETE(root->item, file, line); - RakNet::OP_DELETE(root, file, line); - root = 0; - BinarySearchTree_size = 0; - } - - else - { - if ( current->left != 0 ) - { - current = current->left; - } - - else - if ( current->right != 0 ) - { - current = current->right; - } - - else // leaf - { - // Not root node so must have a parent - parent = FindParent( *( current->item ) ); - - if ( ( parent->left ) == current ) - parent->left = 0; - else - parent->right = 0; - - RakNet::OP_DELETE(current->item, file, line); - - RakNet::OP_DELETE(current, file, line); - - current = parent; - - BinarySearchTree_size--; - } - } - } - } - -} // End namespace - -#endif - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +/// \file DS_BinarySearchTree.h +/// \internal +/// \brief A binary search tree, and an AVL balanced BST derivation. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __BINARY_SEARCH_TREE_H +#define __BINARY_SEARCH_TREE_H + +#include "DS_QueueLinkedList.h" +#include "RakMemoryOverride.h" +#include "Export.h" + + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /** + * \brief A binary search tree and an AVL balanced binary search tree. + * \details + * Initilize with the following structure + * + * BinarySearchTree + * + * OR + * + * AVLBalancedBinarySearchTree + * + * Use the AVL balanced tree if you want the tree to be balanced after every deletion and addition. This avoids the potential + * worst case scenario where ordered input to a binary search tree gives linear search time results. It's not needed + * if input will be evenly distributed, in which case the search time is O (log n). The search time for the AVL + * balanced binary tree is O (log n) irregardless of input. + * + * Has the following member functions + * unsigned int Height() - Returns the height of the tree at the optional specified starting index. Default is the root + * add(element) - adds an element to the BinarySearchTree + * bool del(element) - deletes the node containing element if the element is in the tree as defined by a comparison with the == operator. Returns true on success, false if the element is not found + * bool IsInelement) - returns true if element is in the tree as defined by a comparison with the == operator. Otherwise returns false + * DisplayInorder(array) - Fills an array with an inorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * DisplayPreorder(array) - Fills an array with an preorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * DisplayPostorder(array) - Fills an array with an postorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * DisplayBreadthFirstSearch(array) - Fills an array with a breadth first search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * clear - Destroys the tree. Same as calling the destructor + * unsigned int Height() - Returns the height of the tree + * unsigned int size() - returns the size of the BinarySearchTree + * GetPointerToNode(element) - returns a pointer to the comparision element in the tree, allowing for direct modification when necessary with complex data types. + * Be warned, it is possible to corrupt the tree if the element used for comparisons is modified. Returns NULL if the item is not found + * + * + * EXAMPLE + * @code + * BinarySearchTree A; + * A.Add(10); + * A.Add(15); + * A.Add(5); + * int* array = RakNet::OP_NEW(A.Size(), _FILE_AND_LINE_ ); + * A.DisplayInorder(array); + * array[0]; // returns 5 + * array[1]; // returns 10 + * array[2]; // returns 15 + * @endcode + * compress - reallocates memory to fit the number of elements. Best used when the number of elements decreases + * + * clear - empties the BinarySearchTree and returns storage + * The assignment and copy constructors are defined + * + * \note The template type must have the copy constructor and + * assignment operator defined and must work with >, <, and == All + * elements in the tree MUST be distinct The assignment operator is + * defined between BinarySearchTree and AVLBalancedBinarySearchTree + * as long as they are of the same template type. However, passing a + * BinarySearchTree to an AVLBalancedBinarySearchTree will lose its + * structure unless it happened to be AVL balanced to begin with + * Requires queue_linked_list.cpp for the breadth first search used + * in the copy constructor, overloaded assignment operator, and + * display_breadth_first_search. + * + * + */ + template + class RAK_DLL_EXPORT BinarySearchTree + { + + public: + + struct node + { + BinarySearchTreeType* item; + node* left; + node* right; + }; + + BinarySearchTree(); + virtual ~BinarySearchTree(); + BinarySearchTree( const BinarySearchTree& original_type ); + BinarySearchTree& operator= ( const BinarySearchTree& original_copy ); + unsigned int Size( void ); + void Clear( const char *file, unsigned int line ); + unsigned int Height( node* starting_node = 0 ); + node* Add ( const BinarySearchTreeType& input, const char *file, unsigned int line ); + node* Del( const BinarySearchTreeType& input, const char *file, unsigned int line ); + bool IsIn( const BinarySearchTreeType& input ); + void DisplayInorder( BinarySearchTreeType* return_array ); + void DisplayPreorder( BinarySearchTreeType* return_array ); + void DisplayPostorder( BinarySearchTreeType* return_array ); + void DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ); + BinarySearchTreeType*& GetPointerToNode( const BinarySearchTreeType& element ); + + protected: + + node* root; + + enum Direction_Types + { + NOT_FOUND, LEFT, RIGHT, ROOT + } direction; + unsigned int HeightRecursive( node* current ); + unsigned int BinarySearchTree_size; + node*& Find( const BinarySearchTreeType& element, node** parent ); + node*& FindParent( const BinarySearchTreeType& element ); + void DisplayPostorderRecursive( node* current, BinarySearchTreeType* return_array, unsigned int& index ); + void FixTree( node* current ); + + }; + + /// An AVLBalancedBinarySearchTree is a binary tree that is always balanced + template + class RAK_DLL_EXPORT AVLBalancedBinarySearchTree : public BinarySearchTree + { + + public: + AVLBalancedBinarySearchTree() {} + virtual ~AVLBalancedBinarySearchTree(); + void Add ( const BinarySearchTreeType& input ); + void Del( const BinarySearchTreeType& input ); + BinarySearchTree& operator= ( BinarySearchTree& original_copy ) + { + return BinarySearchTree::operator= ( original_copy ); + } + + private: + void BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ); + void RotateRight( typename BinarySearchTree::node *C ); + void RotateLeft( typename BinarySearchTree::node* C ); + void DoubleRotateRight( typename BinarySearchTree::node *A ); + void DoubleRotateLeft( typename BinarySearchTree::node* A ); + bool RightHigher( typename BinarySearchTree::node* A ); + bool LeftHigher( typename BinarySearchTree::node* A ); + }; + + template + void AVLBalancedBinarySearchTree::BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ) + { + int left_height, right_height; + + while ( current ) + { + if ( current->left == 0 ) + left_height = 0; + else + left_height = Height( current->left ); + + if ( current->right == 0 ) + right_height = 0; + else + right_height = Height( current->right ); + + if ( right_height - left_height == 2 ) + { + if ( RightHigher( current->right ) ) + RotateLeft( current->right ); + else + DoubleRotateLeft( current ); + + if ( rotateOnce ) + break; + } + + else + if ( right_height - left_height == -2 ) + { + if ( LeftHigher( current->left ) ) + RotateRight( current->left ); + else + DoubleRotateRight( current ); + + if ( rotateOnce ) + break; + } + + if ( current == this->root ) + break; + + current = FindParent( *( current->item ) ); + + } + } + + template + void AVLBalancedBinarySearchTree::Add ( const BinarySearchTreeType& input ) + { + + typename BinarySearchTree::node * current = BinarySearchTree::Add ( input, _FILE_AND_LINE_ ); + BalanceTree( current, true ); + } + + template + void AVLBalancedBinarySearchTree::Del( const BinarySearchTreeType& input ) + { + typename BinarySearchTree::node * current = BinarySearchTree::Del( input, _FILE_AND_LINE_ ); + BalanceTree( current, false ); + + } + + template + bool AVLBalancedBinarySearchTree::RightHigher( typename BinarySearchTree::node *A ) + { + if ( A == 0 ) + return false; + + return Height( A->right ) > Height( A->left ); + } + + template + bool AVLBalancedBinarySearchTree::LeftHigher( typename BinarySearchTree::node *A ) + { + if ( A == 0 ) + return false; + + return Height( A->left ) > Height( A->right ); + } + + template + void AVLBalancedBinarySearchTree::RotateRight( typename BinarySearchTree::node *C ) + { + typename BinarySearchTree::node * A, *B, *D; + /* + RIGHT ROTATION + + A = parent(b) + b= parent(c) + c = node to rotate around + + A + | // Either direction + B + / \ + C + / \ + D + + TO + + A + | // Either Direction + C + / \ + B + / \ + D + + + + + */ + + B = FindParent( *( C->item ) ); + A = FindParent( *( B->item ) ); + D = C->right; + + if ( A ) + { + // Direction was set by the last find_parent call + + if ( this->direction == this->LEFT ) + A->left = C; + else + A->right = C; + } + + else + this->root = C; // If B has no parent parent then B must have been the root node + + B->left = D; + + C->right = B; + } + + template + void AVLBalancedBinarySearchTree::DoubleRotateRight( typename BinarySearchTree::node *A ) + { + // The left side of the left child must be higher for the tree to balance with a right rotation. If it isn't, rotate it left before the normal rotation so it is. + RotateLeft( A->left->right ); + RotateRight( A->left ); + } + + template + void AVLBalancedBinarySearchTree::RotateLeft( typename BinarySearchTree::node *C ) + { + typename BinarySearchTree::node * A, *B, *D; + /* + RIGHT ROTATION + + A = parent(b) + b= parent(c) + c = node to rotate around + + A + | // Either direction + B + / \ + C + / \ + D + + TO + + A + | // Either Direction + C + / \ + B + / \ + D + + + + + */ + + B = FindParent( *( C->item ) ); + A = FindParent( *( B->item ) ); + D = C->left; + + if ( A ) + { + // Direction was set by the last find_parent call + + if ( this->direction == this->LEFT ) + A->left = C; + else + A->right = C; + } + + else + this->root = C; // If B has no parent parent then B must have been the root node + + B->right = D; + + C->left = B; + } + + template + void AVLBalancedBinarySearchTree::DoubleRotateLeft( typename BinarySearchTree::node *A ) + { + // The left side of the right child must be higher for the tree to balance with a left rotation. If it isn't, rotate it right before the normal rotation so it is. + RotateRight( A->right->left ); + RotateLeft( A->right ); + } + + template + AVLBalancedBinarySearchTree::~AVLBalancedBinarySearchTree() + { + this->Clear(_FILE_AND_LINE_); + } + + template + unsigned int BinarySearchTree::Size( void ) + { + return BinarySearchTree_size; + } + + template + unsigned int BinarySearchTree::Height( typename BinarySearchTree::node* starting_node ) + { + if ( BinarySearchTree_size == 0 || starting_node == 0 ) + return 0; + else + return HeightRecursive( starting_node ); + } + + // Recursively return the height of a binary tree + template + unsigned int BinarySearchTree::HeightRecursive( typename BinarySearchTree::node* current ) + { + unsigned int left_height = 0, right_height = 0; + + if ( ( current->left == 0 ) && ( current->right == 0 ) ) + return 1; // Leaf + + if ( current->left != 0 ) + left_height = 1 + HeightRecursive( current->left ); + + if ( current->right != 0 ) + right_height = 1 + HeightRecursive( current->right ); + + if ( left_height > right_height ) + return left_height; + else + return right_height; + } + + template + BinarySearchTree::BinarySearchTree() + { + BinarySearchTree_size = 0; + root = 0; + } + + template + BinarySearchTree::~BinarySearchTree() + { + this->Clear(_FILE_AND_LINE_); + } + + template + BinarySearchTreeType*& BinarySearchTree::GetPointerToNode( const BinarySearchTreeType& element ) + { + static typename BinarySearchTree::node * tempnode; + static BinarySearchTreeType* dummyptr = 0; + tempnode = Find ( element, &tempnode ); + + if ( this->direction == this->NOT_FOUND ) + return dummyptr; + + return tempnode->item; + } + + template + typename BinarySearchTree::node*& BinarySearchTree::Find( const BinarySearchTreeType& element, typename BinarySearchTree::node** parent ) + { + static typename BinarySearchTree::node * current; + + current = this->root; + *parent = 0; + this->direction = this->ROOT; + + if ( BinarySearchTree_size == 0 ) + { + this->direction = this->NOT_FOUND; + return current = 0; + } + + // Check if the item is at the root + if ( element == *( current->item ) ) + { + this->direction = this->ROOT; + return current; + } + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) + { + // Move pointer + + if ( element < *( current->item ) ) + { + *parent = current; + this->direction = this->LEFT; + current = current->left; + } + + else + if ( element > *( current->item ) ) + { + *parent = current; + this->direction = this->RIGHT; + current = current->right; + } + + if ( current == 0 ) + break; + + // Check if new position holds the item + if ( element == *( current->item ) ) + { + return current; + } + } + + + this->direction = this->NOT_FOUND; + return current = 0; + } + + template + typename BinarySearchTree::node*& BinarySearchTree::FindParent( const BinarySearchTreeType& element ) + { + static typename BinarySearchTree::node * parent; + Find ( element, &parent ); + return parent; + } + + // Performs a series of value swaps starting with current to fix the tree if needed + template + void BinarySearchTree::FixTree( typename BinarySearchTree::node* current ) + { + BinarySearchTreeType temp; + + while ( 1 ) + { + if ( ( ( current->left ) != 0 ) && ( *( current->item ) < *( current->left->item ) ) ) + { + // Swap the current value with the one to the left + temp = *( current->left->item ); + *( current->left->item ) = *( current->item ); + *( current->item ) = temp; + current = current->left; + } + + else + if ( ( ( current->right ) != 0 ) && ( *( current->item ) > *( current->right->item ) ) ) + { + // Swap the current value with the one to the right + temp = *( current->right->item ); + *( current->right->item ) = *( current->item ); + *( current->item ) = temp; + current = current->right; + } + + else + break; // current points to the right place so quit + } + } + + template + typename BinarySearchTree::node* BinarySearchTree::Del( const BinarySearchTreeType& input, const char *file, unsigned int line ) + { + typename BinarySearchTree::node * node_to_delete, *current, *parent; + + if ( BinarySearchTree_size == 0 ) + return 0; + + if ( BinarySearchTree_size == 1 ) + { + Clear(file, line); + return 0; + } + + node_to_delete = Find( input, &parent ); + + if ( direction == NOT_FOUND ) + return 0; // Couldn't find the element + + current = node_to_delete; + + // Replace the deleted node with the appropriate value + if ( ( current->right ) == 0 && ( current->left ) == 0 ) // Leaf node, just remove it + { + + if ( parent ) + { + if ( direction == LEFT ) + parent->left = 0; + else + parent->right = 0; + } + + RakNet::OP_DELETE(node_to_delete->item, file, line); + RakNet::OP_DELETE(node_to_delete, file, line); + BinarySearchTree_size--; + return parent; + } + else + if ( ( current->right ) != 0 && ( current->left ) == 0 ) // Node has only one child, delete it and cause the parent to point to that child + { + + if ( parent ) + { + if ( direction == RIGHT ) + parent->right = current->right; + else + parent->left = current->right; + } + + else + root = current->right; // Without a parent this must be the root node + + RakNet::OP_DELETE(node_to_delete->item, file, line); + + RakNet::OP_DELETE(node_to_delete, file, line); + + BinarySearchTree_size--; + + return parent; + } + else + if ( ( current->right ) == 0 && ( current->left ) != 0 ) // Node has only one child, delete it and cause the parent to point to that child + { + + if ( parent ) + { + if ( direction == RIGHT ) + parent->right = current->left; + else + parent->left = current->left; + } + + else + root = current->left; // Without a parent this must be the root node + + RakNet::OP_DELETE(node_to_delete->item, file, line); + + RakNet::OP_DELETE(node_to_delete, file, line); + + BinarySearchTree_size--; + + return parent; + } + else // Go right, then as left as far as you can + { + parent = current; + direction = RIGHT; + current = current->right; // Must have a right branch because the if statements above indicated that it has 2 branches + + while ( current->left ) + { + direction = LEFT; + parent = current; + current = current->left; + } + + // Replace the value held by the node to RakNet::OP_DELETE(with the value pointed to by current, _FILE_AND_LINE_); + *( node_to_delete->item ) = *( current->item ); + + // Delete current. + // If it is a leaf node just delete it + if ( current->right == 0 ) + { + if ( direction == RIGHT ) + parent->right = 0; + else + parent->left = 0; + + RakNet::OP_DELETE(current->item, file, line); + + RakNet::OP_DELETE(current, file, line); + + BinarySearchTree_size--; + + return parent; + } + + else + { + // Skip this node and make its parent point to its right branch + + if ( direction == RIGHT ) + parent->right = current->right; + else + parent->left = current->right; + + RakNet::OP_DELETE(current->item, file, line); + + RakNet::OP_DELETE(current, file, line); + + BinarySearchTree_size--; + + return parent; + } + } + } + + template + typename BinarySearchTree::node* BinarySearchTree::Add ( const BinarySearchTreeType& input, const char *file, unsigned int line ) + { + typename BinarySearchTree::node * current; + + // Add the new element to the tree according to the following alogrithm: + // 1. If the current node is empty add the new leaf + // 2. If the element is less than the current node then go down the left branch + // 3. If the element is greater than the current node then go down the right branch + + if ( BinarySearchTree_size == 0 ) + { + BinarySearchTree_size = 1; + root = RakNet::OP_NEW( file, line ); + root->item = RakNet::OP_NEW( file, line ); + *( root->item ) = input; + root->left = 0; + root->right = 0; + + return root; + } + + else + { + // start at the root + current = root; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) // This loop traverses the tree to find a spot for insertion + { + + if ( input < *( current->item ) ) + { + if ( current->left == 0 ) + { + current->left = RakNet::OP_NEW( file, line ); + current->left->item = RakNet::OP_NEW( file, line ); + current = current->left; + current->left = 0; + current->right = 0; + *( current->item ) = input; + + BinarySearchTree_size++; + return current; + } + + else + { + current = current->left; + } + } + + else + if ( input > *( current->item ) ) + { + if ( current->right == 0 ) + { + current->right = RakNet::OP_NEW( file, line ); + current->right->item = RakNet::OP_NEW( file, line ); + current = current->right; + current->left = 0; + current->right = 0; + *( current->item ) = input; + + BinarySearchTree_size++; + return current; + } + + else + { + current = current->right; + } + } + + else + return 0; // ((input == current->item) == true) which is not allowed since the tree only takes discrete values. Do nothing + } + } + } + + template + bool BinarySearchTree::IsIn( const BinarySearchTreeType& input ) + { + typename BinarySearchTree::node * parent; + find( input, &parent ); + + if ( direction != NOT_FOUND ) + return true; + else + return false; + } + + + template + void BinarySearchTree::DisplayInorder( BinarySearchTreeType* return_array ) + { + typename BinarySearchTree::node * current, *parent; + bool just_printed = false; + + unsigned int index = 0; + + current = root; + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + + direction = ROOT; // Reset the direction + + while ( index != BinarySearchTree_size ) + { + // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes + + if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) + { + // Go left if the following 2 conditions are true + // I can go left + // I did not just move up from a right child + // I did not just move up from a left child + + current = current->left; + direction = ROOT; // Reset the direction + } + + else + if ( ( direction != RIGHT ) && ( just_printed == false ) ) + { + // Otherwise, print the current node if the following 3 conditions are true: + // I did not just move up from a right child + // I did not print this ndoe last cycle + + return_array[ index++ ] = *( current->item ); + just_printed = true; + } + + else + if ( ( current->right != 0 ) && ( direction != RIGHT ) ) + { + // Otherwise, go right if the following 2 conditions are true + // I did not just move up from a right child + // I can go right + + current = current->right; + direction = ROOT; // Reset the direction + just_printed = false; + } + + else + { + // Otherwise I've done everything I can. Move up the tree one node + parent = FindParent( *( current->item ) ); + current = parent; + just_printed = false; + } + } + } + + template + void BinarySearchTree::DisplayPreorder( BinarySearchTreeType* return_array ) + { + typename BinarySearchTree::node * current, *parent; + + unsigned int index = 0; + + current = root; + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + + direction = ROOT; // Reset the direction + return_array[ index++ ] = *( current->item ); + + while ( index != BinarySearchTree_size ) + { + // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes + + if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) + { + + current = current->left; + direction = ROOT; + + // Everytime you move a node print it + return_array[ index++ ] = *( current->item ); + } + + else + if ( ( current->right != 0 ) && ( direction != RIGHT ) ) + { + current = current->right; + direction = ROOT; + + // Everytime you move a node print it + return_array[ index++ ] = *( current->item ); + } + + else + { + // Otherwise I've done everything I can. Move up the tree one node + parent = FindParent( *( current->item ) ); + current = parent; + } + } + } + + template + inline void BinarySearchTree::DisplayPostorder( BinarySearchTreeType* return_array ) + { + unsigned int index = 0; + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + DisplayPostorderRecursive( root, return_array, index ); + } + + + // Recursively do a postorder traversal + template + void BinarySearchTree::DisplayPostorderRecursive( typename BinarySearchTree::node* current, BinarySearchTreeType* return_array, unsigned int& index ) + { + if ( current->left != 0 ) + DisplayPostorderRecursive( current->left, return_array, index ); + + if ( current->right != 0 ) + DisplayPostorderRecursive( current->right, return_array, index ); + + return_array[ index++ ] = *( current->item ); + + } + + + template + void BinarySearchTree::DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ) + { + typename BinarySearchTree::node * current; + unsigned int index = 0; + + // Display the tree using a breadth first search + // Put the children of the current node into the queue + // Pop the queue, put its children into the queue, repeat until queue is empty + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + else + { + DataStructures::QueueLinkedList tree_queue; + + // Add the root of the tree I am copying from + tree_queue.Push( root ); + + do + { + current = tree_queue.Pop(); + return_array[ index++ ] = *( current->item ); + + // Add the child or children of the tree I am copying from to the queue + + if ( current->left != 0 ) + tree_queue.Push( current->left ); + + if ( current->right != 0 ) + tree_queue.Push( current->right ); + + } + + while ( tree_queue.Size() > 0 ); + } + } + + + template + BinarySearchTree::BinarySearchTree( const BinarySearchTree& original_copy ) + { + typename BinarySearchTree::node * current; + // Copy the tree using a breadth first search + // Put the children of the current node into the queue + // Pop the queue, put its children into the queue, repeat until queue is empty + + // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. + BinarySearchTree_size = 0; + root = 0; + + if ( original_copy.BinarySearchTree_size == 0 ) + { + BinarySearchTree_size = 0; + } + + else + { + DataStructures::QueueLinkedList tree_queue; + + // Add the root of the tree I am copying from + tree_queue.Push( original_copy.root ); + + do + { + current = tree_queue.Pop(); + + Add ( *( current->item ), _FILE_AND_LINE_ ) + + ; + + // Add the child or children of the tree I am copying from to the queue + if ( current->left != 0 ) + tree_queue.Push( current->left ); + + if ( current->right != 0 ) + tree_queue.Push( current->right ); + + } + + while ( tree_queue.Size() > 0 ); + } + } + + template + BinarySearchTree& BinarySearchTree::operator= ( const BinarySearchTree& original_copy ) + { + typename BinarySearchTree::node * current; + + if ( ( &original_copy ) == this ) + return *this; + + Clear( _FILE_AND_LINE_ ); // Remove the current tree + + // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. + BinarySearchTree_size = 0; + + root = 0; + + + // Copy the tree using a breadth first search + // Put the children of the current node into the queue + // Pop the queue, put its children into the queue, repeat until queue is empty + if ( original_copy.BinarySearchTree_size == 0 ) + { + BinarySearchTree_size = 0; + } + + else + { + DataStructures::QueueLinkedList tree_queue; + + // Add the root of the tree I am copying from + tree_queue.Push( original_copy.root ); + + do + { + current = tree_queue.Pop(); + + Add ( *( current->item ), _FILE_AND_LINE_ ) + + ; + + // Add the child or children of the tree I am copying from to the queue + if ( current->left != 0 ) + tree_queue.Push( current->left ); + + if ( current->right != 0 ) + tree_queue.Push( current->right ); + + } + + while ( tree_queue.Size() > 0 ); + } + + return *this; + } + + template + inline void BinarySearchTree::Clear ( const char *file, unsigned int line ) + { + typename BinarySearchTree::node * current, *parent; + + current = root; + + while ( BinarySearchTree_size > 0 ) + { + if ( BinarySearchTree_size == 1 ) + { + RakNet::OP_DELETE(root->item, file, line); + RakNet::OP_DELETE(root, file, line); + root = 0; + BinarySearchTree_size = 0; + } + + else + { + if ( current->left != 0 ) + { + current = current->left; + } + + else + if ( current->right != 0 ) + { + current = current->right; + } + + else // leaf + { + // Not root node so must have a parent + parent = FindParent( *( current->item ) ); + + if ( ( parent->left ) == current ) + parent->left = 0; + else + parent->right = 0; + + RakNet::OP_DELETE(current->item, file, line); + + RakNet::OP_DELETE(current, file, line); + + current = parent; + + BinarySearchTree_size--; + } + } + } + } + +} // End namespace + +#endif + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_BytePool.cpp b/project/lib_projects/raknet/jni/RaknetSources/DS_BytePool.cpp index 823461d..9d40acf 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_BytePool.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_BytePool.cpp @@ -1,149 +1,149 @@ -#include "DS_BytePool.h" -#include "RakAssert.h" -#ifndef __APPLE__ -// Use stdlib and not malloc for compatibility -#include -#endif - -using namespace DataStructures; - -BytePool::BytePool() -{ - pool128.SetPageSize(8192*4); - pool512.SetPageSize(8192*4); - pool2048.SetPageSize(8192*4); - pool8192.SetPageSize(8192*4); -} -BytePool::~BytePool() -{ -} -void BytePool::SetPageSize(int size) -{ - pool128.SetPageSize(size); - pool512.SetPageSize(size); - pool2048.SetPageSize(size); - pool8192.SetPageSize(size); -} -unsigned char *BytePool::Allocate(int bytesWanted, const char *file, unsigned int line) -{ -#ifdef _DISABLE_BYTE_POOL - return rakMalloc_Ex(bytesWanted, _FILE_AND_LINE_); -#endif - unsigned char *out; - if (bytesWanted <= 127) - { - #ifdef _THREADSAFE_BYTE_POOL - mutex128.Lock(); - #endif - out = (unsigned char*) pool128.Allocate(file, line); - #ifdef _THREADSAFE_BYTE_POOL - mutex128.Unlock(); - #endif - out[0]=0; - return ((unsigned char*) out)+1; - } - if (bytesWanted <= 511) - { - #ifdef _THREADSAFE_BYTE_POOL - mutex512.Lock(); - #endif - out = (unsigned char*) pool512.Allocate(file, line); - #ifdef _THREADSAFE_BYTE_POOL - mutex512.Unlock(); - #endif - out[0]=1; - return ((unsigned char*) out)+1; - } - if (bytesWanted <= 2047) - { - #ifdef _THREADSAFE_BYTE_POOL - mutex2048.Lock(); - #endif - out = (unsigned char*) pool2048.Allocate(file, line); - #ifdef _THREADSAFE_BYTE_POOL - mutex2048.Unlock(); - #endif - out[0]=2; - return ((unsigned char*) out)+1; - } - if (bytesWanted <= 8191) - { - #ifdef _THREADSAFE_BYTE_POOL - mutex8192.Lock(); - #endif - out = (unsigned char*) pool8192.Allocate(file, line); - #ifdef _THREADSAFE_BYTE_POOL - mutex8192.Unlock(); - #endif - out[0]=3; - return ((unsigned char*) out)+1; - } - - out = (unsigned char*) rakMalloc_Ex(bytesWanted+1, _FILE_AND_LINE_); - out[0]=(unsigned char)255; - return out+1; -} -void BytePool::Release(unsigned char *data, const char *file, unsigned int line) -{ -#ifdef _DISABLE_BYTE_POOL - _rakFree_Ex(data, _FILE_AND_LINE_ ); -#endif - unsigned char *realData = data-1; - switch (realData[0]) - { - case 0: - #ifdef _THREADSAFE_BYTE_POOL - mutex128.Lock(); - #endif - pool128.Release((unsigned char(*)[128]) realData, file, line ); - #ifdef _THREADSAFE_BYTE_POOL - mutex128.Unlock(); - #endif - break; - case 1: - #ifdef _THREADSAFE_BYTE_POOL - mutex512.Lock(); - #endif - pool512.Release((unsigned char(*)[512]) realData, file, line ); - #ifdef _THREADSAFE_BYTE_POOL - mutex512.Unlock(); - #endif - break; - case 2: - #ifdef _THREADSAFE_BYTE_POOL - mutex2048.Lock(); - #endif - pool2048.Release((unsigned char(*)[2048]) realData, file, line ); - #ifdef _THREADSAFE_BYTE_POOL - mutex2048.Unlock(); - #endif - break; - case 3: - #ifdef _THREADSAFE_BYTE_POOL - mutex8192.Lock(); - #endif - pool8192.Release((unsigned char(*)[8192]) realData, file, line ); - #ifdef _THREADSAFE_BYTE_POOL - mutex8192.Unlock(); - #endif - break; - case 255: - rakFree_Ex(realData, file, line ); - break; - default: - RakAssert(0); - break; - } -} -void BytePool::Clear(const char *file, unsigned int line) -{ - (void) file; - (void) line; - -#ifdef _THREADSAFE_BYTE_POOL - pool128.Clear(file, line); - pool512.Clear(file, line); - pool2048.Clear(file, line); - pool8192.Clear(file, line); -#endif -} +#include "DS_BytePool.h" +#include "RakAssert.h" +#ifndef __APPLE__ +// Use stdlib and not malloc for compatibility +#include +#endif + +using namespace DataStructures; + +BytePool::BytePool() +{ + pool128.SetPageSize(8192*4); + pool512.SetPageSize(8192*4); + pool2048.SetPageSize(8192*4); + pool8192.SetPageSize(8192*4); +} +BytePool::~BytePool() +{ +} +void BytePool::SetPageSize(int size) +{ + pool128.SetPageSize(size); + pool512.SetPageSize(size); + pool2048.SetPageSize(size); + pool8192.SetPageSize(size); +} +unsigned char *BytePool::Allocate(int bytesWanted, const char *file, unsigned int line) +{ +#ifdef _DISABLE_BYTE_POOL + return rakMalloc_Ex(bytesWanted, _FILE_AND_LINE_); +#endif + unsigned char *out; + if (bytesWanted <= 127) + { + #ifdef _THREADSAFE_BYTE_POOL + mutex128.Lock(); + #endif + out = (unsigned char*) pool128.Allocate(file, line); + #ifdef _THREADSAFE_BYTE_POOL + mutex128.Unlock(); + #endif + out[0]=0; + return ((unsigned char*) out)+1; + } + if (bytesWanted <= 511) + { + #ifdef _THREADSAFE_BYTE_POOL + mutex512.Lock(); + #endif + out = (unsigned char*) pool512.Allocate(file, line); + #ifdef _THREADSAFE_BYTE_POOL + mutex512.Unlock(); + #endif + out[0]=1; + return ((unsigned char*) out)+1; + } + if (bytesWanted <= 2047) + { + #ifdef _THREADSAFE_BYTE_POOL + mutex2048.Lock(); + #endif + out = (unsigned char*) pool2048.Allocate(file, line); + #ifdef _THREADSAFE_BYTE_POOL + mutex2048.Unlock(); + #endif + out[0]=2; + return ((unsigned char*) out)+1; + } + if (bytesWanted <= 8191) + { + #ifdef _THREADSAFE_BYTE_POOL + mutex8192.Lock(); + #endif + out = (unsigned char*) pool8192.Allocate(file, line); + #ifdef _THREADSAFE_BYTE_POOL + mutex8192.Unlock(); + #endif + out[0]=3; + return ((unsigned char*) out)+1; + } + + out = (unsigned char*) rakMalloc_Ex(bytesWanted+1, _FILE_AND_LINE_); + out[0]=(unsigned char)255; + return out+1; +} +void BytePool::Release(unsigned char *data, const char *file, unsigned int line) +{ +#ifdef _DISABLE_BYTE_POOL + _rakFree_Ex(data, _FILE_AND_LINE_ ); +#endif + unsigned char *realData = data-1; + switch (realData[0]) + { + case 0: + #ifdef _THREADSAFE_BYTE_POOL + mutex128.Lock(); + #endif + pool128.Release((unsigned char(*)[128]) realData, file, line ); + #ifdef _THREADSAFE_BYTE_POOL + mutex128.Unlock(); + #endif + break; + case 1: + #ifdef _THREADSAFE_BYTE_POOL + mutex512.Lock(); + #endif + pool512.Release((unsigned char(*)[512]) realData, file, line ); + #ifdef _THREADSAFE_BYTE_POOL + mutex512.Unlock(); + #endif + break; + case 2: + #ifdef _THREADSAFE_BYTE_POOL + mutex2048.Lock(); + #endif + pool2048.Release((unsigned char(*)[2048]) realData, file, line ); + #ifdef _THREADSAFE_BYTE_POOL + mutex2048.Unlock(); + #endif + break; + case 3: + #ifdef _THREADSAFE_BYTE_POOL + mutex8192.Lock(); + #endif + pool8192.Release((unsigned char(*)[8192]) realData, file, line ); + #ifdef _THREADSAFE_BYTE_POOL + mutex8192.Unlock(); + #endif + break; + case 255: + rakFree_Ex(realData, file, line ); + break; + default: + RakAssert(0); + break; + } +} +void BytePool::Clear(const char *file, unsigned int line) +{ + (void) file; + (void) line; + +#ifdef _THREADSAFE_BYTE_POOL + pool128.Clear(file, line); + pool512.Clear(file, line); + pool2048.Clear(file, line); + pool8192.Clear(file, line); +#endif +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_BytePool.h b/project/lib_projects/raknet/jni/RaknetSources/DS_BytePool.h index bdb8301..53eb834 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_BytePool.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_BytePool.h @@ -1,46 +1,46 @@ -/// \file DS_BytePool.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __BYTE_POOL_H -#define __BYTE_POOL_H - -#include "RakMemoryOverride.h" -#include "DS_MemoryPool.h" -#include "Export.h" -#include "SimpleMutex.h" -#include "RakAssert.h" - -// #define _DISABLE_BYTE_POOL -// #define _THREADSAFE_BYTE_POOL - -namespace DataStructures -{ - // Allocate some number of bytes from pools. Uses the heap if necessary. - class RAK_DLL_EXPORT BytePool - { - public: - BytePool(); - ~BytePool(); - // Should be at least 8 times bigger than 8192 - void SetPageSize(int size); - unsigned char* Allocate(int bytesWanted, const char *file, unsigned int line); - void Release(unsigned char *data, const char *file, unsigned int line); - void Clear(const char *file, unsigned int line); - protected: - MemoryPool pool128; - MemoryPool pool512; - MemoryPool pool2048; - MemoryPool pool8192; -#ifdef _THREADSAFE_BYTE_POOL - SimpleMutex mutex128; - SimpleMutex mutex512; - SimpleMutex mutex2048; - SimpleMutex mutex8192; -#endif - }; -} - -#endif +/// \file DS_BytePool.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __BYTE_POOL_H +#define __BYTE_POOL_H + +#include "RakMemoryOverride.h" +#include "DS_MemoryPool.h" +#include "Export.h" +#include "SimpleMutex.h" +#include "RakAssert.h" + +// #define _DISABLE_BYTE_POOL +// #define _THREADSAFE_BYTE_POOL + +namespace DataStructures +{ + // Allocate some number of bytes from pools. Uses the heap if necessary. + class RAK_DLL_EXPORT BytePool + { + public: + BytePool(); + ~BytePool(); + // Should be at least 8 times bigger than 8192 + void SetPageSize(int size); + unsigned char* Allocate(int bytesWanted, const char *file, unsigned int line); + void Release(unsigned char *data, const char *file, unsigned int line); + void Clear(const char *file, unsigned int line); + protected: + MemoryPool pool128; + MemoryPool pool512; + MemoryPool pool2048; + MemoryPool pool8192; +#ifdef _THREADSAFE_BYTE_POOL + SimpleMutex mutex128; + SimpleMutex mutex512; + SimpleMutex mutex2048; + SimpleMutex mutex8192; +#endif + }; +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_ByteQueue.cpp b/project/lib_projects/raknet/jni/RaknetSources/DS_ByteQueue.cpp index 5e0ffd8..a87b7af 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_ByteQueue.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_ByteQueue.cpp @@ -1,127 +1,127 @@ -#include "DS_ByteQueue.h" -#include // Memmove -#include // realloc -#include - - -using namespace DataStructures; - -ByteQueue::ByteQueue() -{ - readOffset=writeOffset=lengthAllocated=0; - data=0; -} -ByteQueue::~ByteQueue() -{ - Clear(_FILE_AND_LINE_); - - -} -void ByteQueue::WriteBytes(const char *in, unsigned length, const char *file, unsigned int line) -{ - unsigned bytesWritten; - bytesWritten=GetBytesWritten(); - if (lengthAllocated==0 || length > lengthAllocated-bytesWritten-1) - { - unsigned oldLengthAllocated=lengthAllocated; - // Always need to waste 1 byte for the math to work, else writeoffset==readoffset - unsigned newAmountToAllocate=length+oldLengthAllocated+1; - if (newAmountToAllocate<256) - newAmountToAllocate=256; - lengthAllocated=lengthAllocated + newAmountToAllocate; - data=(char*)rakRealloc_Ex(data, lengthAllocated, file, line); - if (writeOffset < readOffset) - { - if (writeOffset <= newAmountToAllocate) - { - memcpy(data + oldLengthAllocated, data, writeOffset); - writeOffset=readOffset+bytesWritten; - } - else - { - memcpy(data + oldLengthAllocated, data, newAmountToAllocate); - memmove(data, data+newAmountToAllocate, writeOffset-newAmountToAllocate); - writeOffset-=newAmountToAllocate; - } - } - } - - if (length <= lengthAllocated-writeOffset) - memcpy(data+writeOffset, in, length); - else - { - // Wrap - memcpy(data+writeOffset, in, lengthAllocated-writeOffset); - memcpy(data, in+(lengthAllocated-writeOffset), length-(lengthAllocated-writeOffset)); - } - writeOffset=(writeOffset+length) % lengthAllocated; -} -bool ByteQueue::ReadBytes(char *out, unsigned maxLengthToRead, bool peek) -{ - unsigned bytesWritten = GetBytesWritten(); - unsigned bytesToRead = bytesWritten < maxLengthToRead ? bytesWritten : maxLengthToRead; - if (bytesToRead==0) - return false; - if (writeOffset>=readOffset) - { - memcpy(out, data+readOffset, bytesToRead); - } - else - { - unsigned availableUntilWrap = lengthAllocated-readOffset; - if (bytesToRead <= availableUntilWrap) - { - memcpy(out, data+readOffset, bytesToRead); - } - else - { - memcpy(out, data+readOffset, availableUntilWrap); - memcpy(out+availableUntilWrap, data, bytesToRead-availableUntilWrap); - } - } - - if (peek==false) - IncrementReadOffset(bytesToRead); - - return true; -} -char* ByteQueue::PeekContiguousBytes(unsigned int *outLength) const -{ - if (writeOffset>=readOffset) - *outLength=writeOffset-readOffset; - else - *outLength=lengthAllocated-readOffset; - return data+readOffset; -} -void ByteQueue::Clear(const char *file, unsigned int line) -{ - if (lengthAllocated) - rakFree_Ex(data, file, line ); - readOffset=writeOffset=lengthAllocated=0; - data=0; -} -unsigned ByteQueue::GetBytesWritten(void) const -{ - if (writeOffset>=readOffset) - return writeOffset-readOffset; - else - return writeOffset+(lengthAllocated-readOffset); -} -void ByteQueue::IncrementReadOffset(unsigned length) -{ - readOffset=(readOffset+length) % lengthAllocated; -} -void ByteQueue::DecrementReadOffset(unsigned length) -{ - if (length>readOffset) - readOffset=lengthAllocated-(length-readOffset); - else - readOffset-=length; -} -void ByteQueue::Print(void) -{ - unsigned i; - for (i=readOffset; i!=writeOffset; i++) - RAKNET_DEBUG_PRINTF("%i ", data[i]); - RAKNET_DEBUG_PRINTF("\n"); -} +#include "DS_ByteQueue.h" +#include // Memmove +#include // realloc +#include + + +using namespace DataStructures; + +ByteQueue::ByteQueue() +{ + readOffset=writeOffset=lengthAllocated=0; + data=0; +} +ByteQueue::~ByteQueue() +{ + Clear(_FILE_AND_LINE_); + + +} +void ByteQueue::WriteBytes(const char *in, unsigned length, const char *file, unsigned int line) +{ + unsigned bytesWritten; + bytesWritten=GetBytesWritten(); + if (lengthAllocated==0 || length > lengthAllocated-bytesWritten-1) + { + unsigned oldLengthAllocated=lengthAllocated; + // Always need to waste 1 byte for the math to work, else writeoffset==readoffset + unsigned newAmountToAllocate=length+oldLengthAllocated+1; + if (newAmountToAllocate<256) + newAmountToAllocate=256; + lengthAllocated=lengthAllocated + newAmountToAllocate; + data=(char*)rakRealloc_Ex(data, lengthAllocated, file, line); + if (writeOffset < readOffset) + { + if (writeOffset <= newAmountToAllocate) + { + memcpy(data + oldLengthAllocated, data, writeOffset); + writeOffset=readOffset+bytesWritten; + } + else + { + memcpy(data + oldLengthAllocated, data, newAmountToAllocate); + memmove(data, data+newAmountToAllocate, writeOffset-newAmountToAllocate); + writeOffset-=newAmountToAllocate; + } + } + } + + if (length <= lengthAllocated-writeOffset) + memcpy(data+writeOffset, in, length); + else + { + // Wrap + memcpy(data+writeOffset, in, lengthAllocated-writeOffset); + memcpy(data, in+(lengthAllocated-writeOffset), length-(lengthAllocated-writeOffset)); + } + writeOffset=(writeOffset+length) % lengthAllocated; +} +bool ByteQueue::ReadBytes(char *out, unsigned maxLengthToRead, bool peek) +{ + unsigned bytesWritten = GetBytesWritten(); + unsigned bytesToRead = bytesWritten < maxLengthToRead ? bytesWritten : maxLengthToRead; + if (bytesToRead==0) + return false; + if (writeOffset>=readOffset) + { + memcpy(out, data+readOffset, bytesToRead); + } + else + { + unsigned availableUntilWrap = lengthAllocated-readOffset; + if (bytesToRead <= availableUntilWrap) + { + memcpy(out, data+readOffset, bytesToRead); + } + else + { + memcpy(out, data+readOffset, availableUntilWrap); + memcpy(out+availableUntilWrap, data, bytesToRead-availableUntilWrap); + } + } + + if (peek==false) + IncrementReadOffset(bytesToRead); + + return true; +} +char* ByteQueue::PeekContiguousBytes(unsigned int *outLength) const +{ + if (writeOffset>=readOffset) + *outLength=writeOffset-readOffset; + else + *outLength=lengthAllocated-readOffset; + return data+readOffset; +} +void ByteQueue::Clear(const char *file, unsigned int line) +{ + if (lengthAllocated) + rakFree_Ex(data, file, line ); + readOffset=writeOffset=lengthAllocated=0; + data=0; +} +unsigned ByteQueue::GetBytesWritten(void) const +{ + if (writeOffset>=readOffset) + return writeOffset-readOffset; + else + return writeOffset+(lengthAllocated-readOffset); +} +void ByteQueue::IncrementReadOffset(unsigned length) +{ + readOffset=(readOffset+length) % lengthAllocated; +} +void ByteQueue::DecrementReadOffset(unsigned length) +{ + if (length>readOffset) + readOffset=lengthAllocated-(length-readOffset); + else + readOffset-=length; +} +void ByteQueue::Print(void) +{ + unsigned i; + for (i=readOffset; i!=writeOffset; i++) + RAKNET_DEBUG_PRINTF("%i ", data[i]); + RAKNET_DEBUG_PRINTF("\n"); +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_ByteQueue.h b/project/lib_projects/raknet/jni/RaknetSources/DS_ByteQueue.h index e6a0e31..a288813 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_ByteQueue.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_ByteQueue.h @@ -1,40 +1,40 @@ -/// \file DS_ByteQueue.h -/// \internal -/// \brief Byte queue -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __BYTE_QUEUE_H -#define __BYTE_QUEUE_H - -#include "RakMemoryOverride.h" -#include "Export.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - class ByteQueue - { - public: - ByteQueue(); - ~ByteQueue(); - void WriteBytes(const char *in, unsigned length, const char *file, unsigned int line); - bool ReadBytes(char *out, unsigned maxLengthToRead, bool peek); - unsigned GetBytesWritten(void) const; - char* PeekContiguousBytes(unsigned int *outLength) const; - void IncrementReadOffset(unsigned length); - void DecrementReadOffset(unsigned length); - void Clear(const char *file, unsigned int line); - void Print(void); - - protected: - char *data; - unsigned readOffset, writeOffset, lengthAllocated; - }; -} - -#endif +/// \file DS_ByteQueue.h +/// \internal +/// \brief Byte queue +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __BYTE_QUEUE_H +#define __BYTE_QUEUE_H + +#include "RakMemoryOverride.h" +#include "Export.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + class ByteQueue + { + public: + ByteQueue(); + ~ByteQueue(); + void WriteBytes(const char *in, unsigned length, const char *file, unsigned int line); + bool ReadBytes(char *out, unsigned maxLengthToRead, bool peek); + unsigned GetBytesWritten(void) const; + char* PeekContiguousBytes(unsigned int *outLength) const; + void IncrementReadOffset(unsigned length); + void DecrementReadOffset(unsigned length); + void Clear(const char *file, unsigned int line); + void Print(void); + + protected: + char *data; + unsigned readOffset, writeOffset, lengthAllocated; + }; +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_Hash.h b/project/lib_projects/raknet/jni/RaknetSources/DS_Hash.h index 39848d3..b61d556 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_Hash.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_Hash.h @@ -1,344 +1,344 @@ -/// \internal -/// \brief Hashing container -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __HASH_H -#define __HASH_H - -#include "RakAssert.h" -#include // memmove -#include "Export.h" -#include "RakMemoryOverride.h" -#include "RakString.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - struct HashIndex - { - unsigned int primaryIndex; - unsigned int secondaryIndex; - bool IsInvalid(void) const {return primaryIndex==(unsigned int) -1;} - void SetInvalid(void) {primaryIndex=(unsigned int) -1; secondaryIndex=(unsigned int) -1;} - }; - - /// \brief Using a string as a identifier for a node, store an allocated pointer to that node - template - class RAK_DLL_EXPORT Hash - { - public: - /// Default constructor - Hash(); - - // Destructor - ~Hash(); - - void Push(key_type key, const data_type &input, const char *file, unsigned int line ); - data_type* Peek(key_type key ); - bool Pop(data_type& out, key_type key, const char *file, unsigned int line ); - bool RemoveAtIndex(HashIndex index, const char *file, unsigned int line ); - bool Remove(key_type key, const char *file, unsigned int line ); - HashIndex GetIndexOf(key_type key); - bool HasData(key_type key); - data_type& ItemAtIndex(const HashIndex &index); - key_type KeyAtIndex(const HashIndex &index); - void GetAsList(DataStructures::List &itemList,DataStructures::List &keyList,const char *file, unsigned int line) const; - unsigned int Size(void) const; - - /// \brief Clear the list - void Clear( const char *file, unsigned int line ); - - struct Node - { - Node(key_type strIn, const data_type &_data) {string=strIn; data=_data;} - key_type string; - data_type data; - // Next in the list for this key - Node *next; - }; - - protected: - void ClearIndex(unsigned int index,const char *file, unsigned int line); - Node **nodeList; - unsigned int size; - }; - - template - Hash::Hash() - { - nodeList=0; - size=0; - } - - template - Hash::~Hash() - { - Clear(_FILE_AND_LINE_); - } - - template - void Hash::Push(key_type key, const data_type &input, const char *file, unsigned int line ) - { - unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; - if (nodeList==0) - { - nodeList=RakNet::OP_NEW_ARRAY(HASH_SIZE,file,line); - memset(nodeList,0,sizeof(Node *)*HASH_SIZE); - } - - Node *newNode=RakNet::OP_NEW_2(file,line,key,input); - newNode->next=nodeList[hashIndex]; - nodeList[hashIndex]=newNode; - - size++; - } - - template - data_type* Hash::Peek(key_type key ) - { - unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; - Node *node = nodeList[hashIndex]; - while (node!=0) - { - if (node->string==key) - return &node->data; - node=node->next; - } - return 0; - } - - template - bool Hash::Pop(data_type& out, key_type key, const char *file, unsigned int line ) - { - unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; - Node *node = nodeList[hashIndex]; - if (node==0) - return false; - if (node->next==0) - { - // Only one item. - if (node->string==key) - { - // Delete last item - out=node->data; - ClearIndex(hashIndex,_FILE_AND_LINE_); - return true; - } - else - { - // Single item doesn't match - return false; - } - } - else if (node->string==key) - { - // First item does match, but more than one item - out=node->data; - nodeList[hashIndex]=node->next; - RakNet::OP_DELETE(node,file,line); - size--; - return true; - } - - Node *last=node; - node=node->next; - - while (node!=0) - { - // First item does not match, but subsequent item might - if (node->string==key) - { - out=node->data; - // Skip over subsequent item - last->next=node->next; - // Delete existing item - RakNet::OP_DELETE(node,file,line); - size--; - return true; - } - last=node; - node=node->next; - } - return false; - } - - template - bool Hash::RemoveAtIndex(HashIndex index, const char *file, unsigned int line ) - { - if (index.IsInvalid()) - return false; - - Node *node = nodeList[index.primaryIndex]; - if (node==0) - return false; - if (node->next==0) - { - // Delete last item - ClearIndex(index.primaryIndex,file,line); - return true; - } - else if (index.secondaryIndex==0) - { - // First item does match, but more than one item - nodeList[index.primaryIndex]=node->next; - RakNet::OP_DELETE(node,file,line); - size--; - return true; - } - - Node *last=node; - node=node->next; - --index.secondaryIndex; - - while (index.secondaryIndex!=0) - { - last=node; - node=node->next; - --index.secondaryIndex; - } - - // Skip over subsequent item - last->next=node->next; - // Delete existing item - RakNet::OP_DELETE(node,file,line); - size--; - return true; - } - - template - bool Hash::Remove(key_type key, const char *file, unsigned int line ) - { - return RemoveAtIndex(GetIndexOf(key),file,line); - } - - template - HashIndex Hash::GetIndexOf(key_type key) - { - if (nodeList==0) - { - HashIndex temp; - temp.SetInvalid(); - return temp; - } - HashIndex idx; - idx.primaryIndex=(*hashFunction)(key) % HASH_SIZE; - Node *node = nodeList[idx.primaryIndex]; - if (node==0) - { - idx.SetInvalid(); - return idx; - } - idx.secondaryIndex=0; - while (node!=0) - { - if (node->string==key) - { - return idx; - } - node=node->next; - idx.secondaryIndex++; - } - - idx.SetInvalid(); - return idx; - } - - template - bool Hash::HasData(key_type key) - { - return GetIndexOf(key).IsInvalid()==false; - } - - template - data_type& Hash::ItemAtIndex(const HashIndex &index) - { - Node *node = nodeList[index.primaryIndex]; - RakAssert(node); - unsigned int i; - for (i=0; i < index.secondaryIndex; i++) - { - node=node->next; - RakAssert(node); - } - return node->data; - } - - template - key_type Hash::KeyAtIndex(const HashIndex &index) - { - Node *node = nodeList[index.primaryIndex]; - RakAssert(node); - unsigned int i; - for (i=0; i < index.secondaryIndex; i++) - { - node=node->next; - RakAssert(node); - } - return node->string; - } - - template - void Hash::Clear(const char *file, unsigned int line) - { - if (nodeList) - { - unsigned int i; - for (i=0; i < HASH_SIZE; i++) - ClearIndex(i,file,line); - RakNet::OP_DELETE_ARRAY(nodeList,file,line); - nodeList=0; - size=0; - } - } - - template - void Hash::ClearIndex(unsigned int index,const char *file, unsigned int line) - { - Node *node = nodeList[index]; - Node *next; - while (node) - { - next=node->next; - RakNet::OP_DELETE(node,file,line); - node=next; - size--; - } - nodeList[index]=0; - } - - template - void Hash::GetAsList(DataStructures::List &itemList,DataStructures::List &keyList,const char *file, unsigned int line) const - { - if (nodeList==0) - return; - itemList.Clear(false,_FILE_AND_LINE_); - keyList.Clear(false,_FILE_AND_LINE_); - - Node *node; - unsigned int i; - for (i=0; i < HASH_SIZE; i++) - { - if (nodeList[i]) - { - node=nodeList[i]; - while (node) - { - itemList.Push(node->data,file,line); - keyList.Push(node->string,file,line); - node=node->next; - } - } - } - } - template - unsigned int Hash::Size(void) const - { - return size; - } -} -#endif +/// \internal +/// \brief Hashing container +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __HASH_H +#define __HASH_H + +#include "RakAssert.h" +#include // memmove +#include "Export.h" +#include "RakMemoryOverride.h" +#include "RakString.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + struct HashIndex + { + unsigned int primaryIndex; + unsigned int secondaryIndex; + bool IsInvalid(void) const {return primaryIndex==(unsigned int) -1;} + void SetInvalid(void) {primaryIndex=(unsigned int) -1; secondaryIndex=(unsigned int) -1;} + }; + + /// \brief Using a string as a identifier for a node, store an allocated pointer to that node + template + class RAK_DLL_EXPORT Hash + { + public: + /// Default constructor + Hash(); + + // Destructor + ~Hash(); + + void Push(key_type key, const data_type &input, const char *file, unsigned int line ); + data_type* Peek(key_type key ); + bool Pop(data_type& out, key_type key, const char *file, unsigned int line ); + bool RemoveAtIndex(HashIndex index, const char *file, unsigned int line ); + bool Remove(key_type key, const char *file, unsigned int line ); + HashIndex GetIndexOf(key_type key); + bool HasData(key_type key); + data_type& ItemAtIndex(const HashIndex &index); + key_type KeyAtIndex(const HashIndex &index); + void GetAsList(DataStructures::List &itemList,DataStructures::List &keyList,const char *file, unsigned int line) const; + unsigned int Size(void) const; + + /// \brief Clear the list + void Clear( const char *file, unsigned int line ); + + struct Node + { + Node(key_type strIn, const data_type &_data) {string=strIn; data=_data;} + key_type string; + data_type data; + // Next in the list for this key + Node *next; + }; + + protected: + void ClearIndex(unsigned int index,const char *file, unsigned int line); + Node **nodeList; + unsigned int size; + }; + + template + Hash::Hash() + { + nodeList=0; + size=0; + } + + template + Hash::~Hash() + { + Clear(_FILE_AND_LINE_); + } + + template + void Hash::Push(key_type key, const data_type &input, const char *file, unsigned int line ) + { + unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; + if (nodeList==0) + { + nodeList=RakNet::OP_NEW_ARRAY(HASH_SIZE,file,line); + memset(nodeList,0,sizeof(Node *)*HASH_SIZE); + } + + Node *newNode=RakNet::OP_NEW_2(file,line,key,input); + newNode->next=nodeList[hashIndex]; + nodeList[hashIndex]=newNode; + + size++; + } + + template + data_type* Hash::Peek(key_type key ) + { + unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; + Node *node = nodeList[hashIndex]; + while (node!=0) + { + if (node->string==key) + return &node->data; + node=node->next; + } + return 0; + } + + template + bool Hash::Pop(data_type& out, key_type key, const char *file, unsigned int line ) + { + unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; + Node *node = nodeList[hashIndex]; + if (node==0) + return false; + if (node->next==0) + { + // Only one item. + if (node->string==key) + { + // Delete last item + out=node->data; + ClearIndex(hashIndex,_FILE_AND_LINE_); + return true; + } + else + { + // Single item doesn't match + return false; + } + } + else if (node->string==key) + { + // First item does match, but more than one item + out=node->data; + nodeList[hashIndex]=node->next; + RakNet::OP_DELETE(node,file,line); + size--; + return true; + } + + Node *last=node; + node=node->next; + + while (node!=0) + { + // First item does not match, but subsequent item might + if (node->string==key) + { + out=node->data; + // Skip over subsequent item + last->next=node->next; + // Delete existing item + RakNet::OP_DELETE(node,file,line); + size--; + return true; + } + last=node; + node=node->next; + } + return false; + } + + template + bool Hash::RemoveAtIndex(HashIndex index, const char *file, unsigned int line ) + { + if (index.IsInvalid()) + return false; + + Node *node = nodeList[index.primaryIndex]; + if (node==0) + return false; + if (node->next==0) + { + // Delete last item + ClearIndex(index.primaryIndex,file,line); + return true; + } + else if (index.secondaryIndex==0) + { + // First item does match, but more than one item + nodeList[index.primaryIndex]=node->next; + RakNet::OP_DELETE(node,file,line); + size--; + return true; + } + + Node *last=node; + node=node->next; + --index.secondaryIndex; + + while (index.secondaryIndex!=0) + { + last=node; + node=node->next; + --index.secondaryIndex; + } + + // Skip over subsequent item + last->next=node->next; + // Delete existing item + RakNet::OP_DELETE(node,file,line); + size--; + return true; + } + + template + bool Hash::Remove(key_type key, const char *file, unsigned int line ) + { + return RemoveAtIndex(GetIndexOf(key),file,line); + } + + template + HashIndex Hash::GetIndexOf(key_type key) + { + if (nodeList==0) + { + HashIndex temp; + temp.SetInvalid(); + return temp; + } + HashIndex idx; + idx.primaryIndex=(*hashFunction)(key) % HASH_SIZE; + Node *node = nodeList[idx.primaryIndex]; + if (node==0) + { + idx.SetInvalid(); + return idx; + } + idx.secondaryIndex=0; + while (node!=0) + { + if (node->string==key) + { + return idx; + } + node=node->next; + idx.secondaryIndex++; + } + + idx.SetInvalid(); + return idx; + } + + template + bool Hash::HasData(key_type key) + { + return GetIndexOf(key).IsInvalid()==false; + } + + template + data_type& Hash::ItemAtIndex(const HashIndex &index) + { + Node *node = nodeList[index.primaryIndex]; + RakAssert(node); + unsigned int i; + for (i=0; i < index.secondaryIndex; i++) + { + node=node->next; + RakAssert(node); + } + return node->data; + } + + template + key_type Hash::KeyAtIndex(const HashIndex &index) + { + Node *node = nodeList[index.primaryIndex]; + RakAssert(node); + unsigned int i; + for (i=0; i < index.secondaryIndex; i++) + { + node=node->next; + RakAssert(node); + } + return node->string; + } + + template + void Hash::Clear(const char *file, unsigned int line) + { + if (nodeList) + { + unsigned int i; + for (i=0; i < HASH_SIZE; i++) + ClearIndex(i,file,line); + RakNet::OP_DELETE_ARRAY(nodeList,file,line); + nodeList=0; + size=0; + } + } + + template + void Hash::ClearIndex(unsigned int index,const char *file, unsigned int line) + { + Node *node = nodeList[index]; + Node *next; + while (node) + { + next=node->next; + RakNet::OP_DELETE(node,file,line); + node=next; + size--; + } + nodeList[index]=0; + } + + template + void Hash::GetAsList(DataStructures::List &itemList,DataStructures::List &keyList,const char *file, unsigned int line) const + { + if (nodeList==0) + return; + itemList.Clear(false,_FILE_AND_LINE_); + keyList.Clear(false,_FILE_AND_LINE_); + + Node *node; + unsigned int i; + for (i=0; i < HASH_SIZE; i++) + { + if (nodeList[i]) + { + node=nodeList[i]; + while (node) + { + itemList.Push(node->data,file,line); + keyList.Push(node->string,file,line); + node=node->next; + } + } + } + } + template + unsigned int Hash::Size(void) const + { + return size; + } +} +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_Heap.h b/project/lib_projects/raknet/jni/RaknetSources/DS_Heap.h index 92bca66..6551b84 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_Heap.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_Heap.h @@ -1,297 +1,297 @@ -/// \file DS_Heap.h -/// \internal -/// \brief Heap (Also serves as a priority queue) -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAKNET_HEAP_H -#define __RAKNET_HEAP_H - -#include "RakMemoryOverride.h" -#include "DS_List.h" -#include "Export.h" -#include "RakAssert.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template - class RAK_DLL_EXPORT Heap - { - public: - struct HeapNode - { - HeapNode() {} - HeapNode(const weight_type &w, const data_type &d) : weight(w), data(d) {} - weight_type weight; // I'm assuming key is a native numerical type - float or int - data_type data; - }; - - Heap(); - ~Heap(); - void Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line); - /// Call before calling PushSeries, for a new series of items - void StartSeries(void) {optimizeNextSeriesPush=false;} - /// If you are going to push a list of items, where the weights of the items on the list are in order and follow the heap order, PushSeries is faster than Push() - void PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line); - data_type Pop(const unsigned startingIndex); - data_type Peek(const unsigned startingIndex=0) const; - weight_type PeekWeight(const unsigned startingIndex=0) const; - void Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line); - data_type& operator[] ( const unsigned int position ) const; - unsigned Size(void) const; - - protected: - unsigned LeftChild(const unsigned i) const; - unsigned RightChild(const unsigned i) const; - unsigned Parent(const unsigned i) const; - void Swap(const unsigned i, const unsigned j); - DataStructures::List heap; - bool optimizeNextSeriesPush; - }; - - template - Heap::Heap() - { - optimizeNextSeriesPush=false; - } - - template - Heap::~Heap() - { - //Clear(true, _FILE_AND_LINE_); - } - - template - void Heap::PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line) - { - if (optimizeNextSeriesPush==false) - { - // If the weight of what we are inserting is greater than / less than in order of the heap of every sibling and sibling of parent, then can optimize next push - unsigned currentIndex = heap.Size(); - unsigned parentIndex; - if (currentIndex>0) - { - for (parentIndex = Parent(currentIndex); parentIndex < currentIndex; parentIndex++) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (isMaxHeap) - { - // Every child is less than its parent - if (weight>heap[parentIndex].weight) - { - // Can't optimize - Push(weight,data,file,line); - return; - } - } - else - { - // Every child is greater than than its parent - if (weight - void Heap::Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line) - { - unsigned currentIndex = heap.Size(); - unsigned parentIndex; - heap.Insert(HeapNode(weight, data), file, line); - while (currentIndex!=0) - { - parentIndex = Parent(currentIndex); -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (isMaxHeap) - { - if (heap[parentIndex].weight < weight) - { - Swap(currentIndex, parentIndex); - currentIndex=parentIndex; - } - else - break; - } - else - { - if (heap[parentIndex].weight > weight) - { - Swap(currentIndex, parentIndex); - currentIndex=parentIndex; - } - else - break; - } - } - } - - template - data_type Heap::Pop(const unsigned startingIndex) - { - // While we have children, swap out with the larger of the two children. - - // This line will assert on an empty heap - data_type returnValue=heap[startingIndex].data; - - // Move the last element to the head, and re-heapify - heap[startingIndex]=heap[heap.Size()-1]; - - unsigned currentIndex,leftChild,rightChild; - weight_type currentWeight; - currentIndex=startingIndex; - currentWeight=heap[startingIndex].weight; - heap.RemoveFromEnd(); - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - leftChild=LeftChild(currentIndex); - rightChild=RightChild(currentIndex); - if (leftChild >= heap.Size()) - { - // Done - return returnValue; - } - if (rightChild >= heap.Size()) - { - // Only left node. - if ((isMaxHeap==true && currentWeight < heap[leftChild].weight) || - (isMaxHeap==false && currentWeight > heap[leftChild].weight)) - Swap(leftChild, currentIndex); - - return returnValue; - } - else - { - // Swap with the bigger/smaller of the two children and continue - if (isMaxHeap) - { - if (heap[leftChild].weight <= currentWeight && heap[rightChild].weight <= currentWeight) - return returnValue; - - if (heap[leftChild].weight > heap[rightChild].weight) - { - Swap(leftChild, currentIndex); - currentIndex=leftChild; - } - else - { - Swap(rightChild, currentIndex); - currentIndex=rightChild; - } - } - else - { - if (heap[leftChild].weight >= currentWeight && heap[rightChild].weight >= currentWeight) - return returnValue; - - if (heap[leftChild].weight < heap[rightChild].weight) - { - Swap(leftChild, currentIndex); - currentIndex=leftChild; - } - else - { - Swap(rightChild, currentIndex); - currentIndex=rightChild; - } - } - } - } - } - - template - inline data_type Heap::Peek(const unsigned startingIndex) const - { - return heap[startingIndex].data; - } - - template - inline weight_type Heap::PeekWeight(const unsigned startingIndex) const - { - return heap[startingIndex].weight; - } - - template - void Heap::Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line) - { - heap.Clear(doNotDeallocateSmallBlocks, file, line); - } - - template - inline data_type& Heap::operator[] ( const unsigned int position ) const - { - return heap[position].data; - } - template - unsigned Heap::Size(void) const - { - return heap.Size(); - } - - template - inline unsigned Heap::LeftChild(const unsigned i) const - { - return i*2+1; - } - - template - inline unsigned Heap::RightChild(const unsigned i) const - { - return i*2+2; - } - - template - inline unsigned Heap::Parent(const unsigned i) const - { -#ifdef _DEBUG - RakAssert(i!=0); -#endif - return (i-1)/2; - } - - template - void Heap::Swap(const unsigned i, const unsigned j) - { - HeapNode temp; - temp=heap[i]; - heap[i]=heap[j]; - heap[j]=temp; - } -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif +/// \file DS_Heap.h +/// \internal +/// \brief Heap (Also serves as a priority queue) +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAKNET_HEAP_H +#define __RAKNET_HEAP_H + +#include "RakMemoryOverride.h" +#include "DS_List.h" +#include "Export.h" +#include "RakAssert.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + class RAK_DLL_EXPORT Heap + { + public: + struct HeapNode + { + HeapNode() {} + HeapNode(const weight_type &w, const data_type &d) : weight(w), data(d) {} + weight_type weight; // I'm assuming key is a native numerical type - float or int + data_type data; + }; + + Heap(); + ~Heap(); + void Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line); + /// Call before calling PushSeries, for a new series of items + void StartSeries(void) {optimizeNextSeriesPush=false;} + /// If you are going to push a list of items, where the weights of the items on the list are in order and follow the heap order, PushSeries is faster than Push() + void PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line); + data_type Pop(const unsigned startingIndex); + data_type Peek(const unsigned startingIndex=0) const; + weight_type PeekWeight(const unsigned startingIndex=0) const; + void Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line); + data_type& operator[] ( const unsigned int position ) const; + unsigned Size(void) const; + + protected: + unsigned LeftChild(const unsigned i) const; + unsigned RightChild(const unsigned i) const; + unsigned Parent(const unsigned i) const; + void Swap(const unsigned i, const unsigned j); + DataStructures::List heap; + bool optimizeNextSeriesPush; + }; + + template + Heap::Heap() + { + optimizeNextSeriesPush=false; + } + + template + Heap::~Heap() + { + //Clear(true, _FILE_AND_LINE_); + } + + template + void Heap::PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line) + { + if (optimizeNextSeriesPush==false) + { + // If the weight of what we are inserting is greater than / less than in order of the heap of every sibling and sibling of parent, then can optimize next push + unsigned currentIndex = heap.Size(); + unsigned parentIndex; + if (currentIndex>0) + { + for (parentIndex = Parent(currentIndex); parentIndex < currentIndex; parentIndex++) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (isMaxHeap) + { + // Every child is less than its parent + if (weight>heap[parentIndex].weight) + { + // Can't optimize + Push(weight,data,file,line); + return; + } + } + else + { + // Every child is greater than than its parent + if (weight + void Heap::Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line) + { + unsigned currentIndex = heap.Size(); + unsigned parentIndex; + heap.Insert(HeapNode(weight, data), file, line); + while (currentIndex!=0) + { + parentIndex = Parent(currentIndex); +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (isMaxHeap) + { + if (heap[parentIndex].weight < weight) + { + Swap(currentIndex, parentIndex); + currentIndex=parentIndex; + } + else + break; + } + else + { + if (heap[parentIndex].weight > weight) + { + Swap(currentIndex, parentIndex); + currentIndex=parentIndex; + } + else + break; + } + } + } + + template + data_type Heap::Pop(const unsigned startingIndex) + { + // While we have children, swap out with the larger of the two children. + + // This line will assert on an empty heap + data_type returnValue=heap[startingIndex].data; + + // Move the last element to the head, and re-heapify + heap[startingIndex]=heap[heap.Size()-1]; + + unsigned currentIndex,leftChild,rightChild; + weight_type currentWeight; + currentIndex=startingIndex; + currentWeight=heap[startingIndex].weight; + heap.RemoveFromEnd(); + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + leftChild=LeftChild(currentIndex); + rightChild=RightChild(currentIndex); + if (leftChild >= heap.Size()) + { + // Done + return returnValue; + } + if (rightChild >= heap.Size()) + { + // Only left node. + if ((isMaxHeap==true && currentWeight < heap[leftChild].weight) || + (isMaxHeap==false && currentWeight > heap[leftChild].weight)) + Swap(leftChild, currentIndex); + + return returnValue; + } + else + { + // Swap with the bigger/smaller of the two children and continue + if (isMaxHeap) + { + if (heap[leftChild].weight <= currentWeight && heap[rightChild].weight <= currentWeight) + return returnValue; + + if (heap[leftChild].weight > heap[rightChild].weight) + { + Swap(leftChild, currentIndex); + currentIndex=leftChild; + } + else + { + Swap(rightChild, currentIndex); + currentIndex=rightChild; + } + } + else + { + if (heap[leftChild].weight >= currentWeight && heap[rightChild].weight >= currentWeight) + return returnValue; + + if (heap[leftChild].weight < heap[rightChild].weight) + { + Swap(leftChild, currentIndex); + currentIndex=leftChild; + } + else + { + Swap(rightChild, currentIndex); + currentIndex=rightChild; + } + } + } + } + } + + template + inline data_type Heap::Peek(const unsigned startingIndex) const + { + return heap[startingIndex].data; + } + + template + inline weight_type Heap::PeekWeight(const unsigned startingIndex) const + { + return heap[startingIndex].weight; + } + + template + void Heap::Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line) + { + heap.Clear(doNotDeallocateSmallBlocks, file, line); + } + + template + inline data_type& Heap::operator[] ( const unsigned int position ) const + { + return heap[position].data; + } + template + unsigned Heap::Size(void) const + { + return heap.Size(); + } + + template + inline unsigned Heap::LeftChild(const unsigned i) const + { + return i*2+1; + } + + template + inline unsigned Heap::RightChild(const unsigned i) const + { + return i*2+2; + } + + template + inline unsigned Heap::Parent(const unsigned i) const + { +#ifdef _DEBUG + RakAssert(i!=0); +#endif + return (i-1)/2; + } + + template + void Heap::Swap(const unsigned i, const unsigned j) + { + HeapNode temp; + temp=heap[i]; + heap[i]=heap[j]; + heap[j]=temp; + } +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTree.cpp b/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTree.cpp index 3b03f40..abca294 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTree.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTree.cpp @@ -1,299 +1,299 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "DS_HuffmanEncodingTree.h" -#include "DS_Queue.h" -#include "BitStream.h" -#include "RakAssert.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -HuffmanEncodingTree::HuffmanEncodingTree() -{ - root = 0; -} - -HuffmanEncodingTree::~HuffmanEncodingTree() -{ - FreeMemory(); -} - -void HuffmanEncodingTree::FreeMemory( void ) -{ - if ( root == 0 ) - return ; - - // Use an in-order traversal to delete the tree - DataStructures::Queue nodeQueue; - - HuffmanEncodingTreeNode *node; - - nodeQueue.Push( root, _FILE_AND_LINE_ ); - - while ( nodeQueue.Size() > 0 ) - { - node = nodeQueue.Pop(); - - if ( node->left ) - nodeQueue.Push( node->left, _FILE_AND_LINE_ ); - - if ( node->right ) - nodeQueue.Push( node->right, _FILE_AND_LINE_ ); - - RakNet::OP_DELETE(node, _FILE_AND_LINE_); - } - - // Delete the encoding table - for ( int i = 0; i < 256; i++ ) - rakFree_Ex(encodingTable[ i ].encoding, _FILE_AND_LINE_ ); - - root = 0; -} - - -////#include - -// Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree -void HuffmanEncodingTree::GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ) -{ - int counter; - HuffmanEncodingTreeNode * node; - HuffmanEncodingTreeNode *leafList[ 256 ]; // Keep a copy of the pointers to all the leaves so we can generate the encryption table bottom-up, which is easier - // 1. Make 256 trees each with a weight equal to the frequency of the corresponding character - DataStructures::LinkedList huffmanEncodingTreeNodeList; - - FreeMemory(); - - for ( counter = 0; counter < 256; counter++ ) - { - node = RakNet::OP_NEW( _FILE_AND_LINE_ ); - node->left = 0; - node->right = 0; - node->value = (unsigned char) counter; - node->weight = frequencyTable[ counter ]; - - if ( node->weight == 0 ) - node->weight = 1; // 0 weights are illegal - - leafList[ counter ] = node; // Used later to generate the encryption table - - InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); // Insert and maintain sort order. - } - - - // 2. While there is more than one tree, take the two smallest trees and merge them so that the two trees are the left and right - // children of a new node, where the new node has the weight the sum of the weight of the left and right child nodes. -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( 1 ) - { - huffmanEncodingTreeNodeList.Beginning(); - HuffmanEncodingTreeNode *lesser, *greater; - lesser = huffmanEncodingTreeNodeList.Pop(); - greater = huffmanEncodingTreeNodeList.Pop(); - node = RakNet::OP_NEW( _FILE_AND_LINE_ ); - node->left = lesser; - node->right = greater; - node->weight = lesser->weight + greater->weight; - lesser->parent = node; // This is done to make generating the encryption table easier - greater->parent = node; // This is done to make generating the encryption table easier - - if ( huffmanEncodingTreeNodeList.Size() == 0 ) - { - // 3. Assign the one remaining node in the list to the root node. - root = node; - root->parent = 0; - break; - } - - // Put the new node back into the list at the correct spot to maintain the sort. Linear search time - InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); - } - - bool tempPath[ 256 ]; // Maximum path length is 256 - unsigned short tempPathLength; - HuffmanEncodingTreeNode *currentNode; - RakNet::BitStream bitStream; - - // Generate the encryption table. From before, we have an array of pointers to all the leaves which contain pointers to their parents. - // This can be done more efficiently but this isn't bad and it's way easier to program and debug - - for ( counter = 0; counter < 256; counter++ ) - { - // Already done at the end of the loop and before it! - tempPathLength = 0; - - // Set the current node at the leaf - currentNode = leafList[ counter ]; - - do - { - if ( currentNode->parent->left == currentNode ) // We're storing the paths in reverse order.since we are going from the leaf to the root - tempPath[ tempPathLength++ ] = false; - else - tempPath[ tempPathLength++ ] = true; - - currentNode = currentNode->parent; - } - - while ( currentNode != root ); - - // Write to the bitstream in the reverse order that we stored the path, which gives us the correct order from the root to the leaf - while ( tempPathLength-- > 0 ) - { - if ( tempPath[ tempPathLength ] ) // Write 1's and 0's because writing a bool will write the BitStream TYPE_CHECKING validation bits if that is defined along with the actual data bit, which is not what we want - bitStream.Write1(); - else - bitStream.Write0(); - } - - // Read data from the bitstream, which is written to the encoding table in bits and bitlength. Note this function allocates the encodingTable[counter].encoding pointer - encodingTable[ counter ].bitLength = ( unsigned char ) bitStream.CopyData( &encodingTable[ counter ].encoding ); - - // Reset the bitstream for the next iteration - bitStream.Reset(); - } -} - -// Pass an array of bytes to array and a preallocated BitStream to receive the output -void HuffmanEncodingTree::EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output ) -{ - unsigned counter; - - // For each input byte, Write out the corresponding series of 1's and 0's that give the encoded representation - for ( counter = 0; counter < sizeInBytes; counter++ ) - { - output->WriteBits( encodingTable[ input[ counter ] ].encoding, encodingTable[ input[ counter ] ].bitLength, false ); // Data is left aligned - } - - // Byte align the output so the unassigned remaining bits don't equate to some actual value - if ( output->GetNumberOfBitsUsed() % 8 != 0 ) - { - // Find an input that is longer than the remaining bits. Write out part of it to pad the output to be byte aligned. - unsigned char remainingBits = (unsigned char) ( 8 - ( output->GetNumberOfBitsUsed() % 8 ) ); - - for ( counter = 0; counter < 256; counter++ ) - if ( encodingTable[ counter ].bitLength > remainingBits ) - { - output->WriteBits( encodingTable[ counter ].encoding, remainingBits, false ); // Data is left aligned - break; - } - -#ifdef _DEBUG - RakAssert( counter != 256 ); // Given 256 elements, we should always be able to find an input that would be >= 7 bits - -#endif - - } -} - -unsigned HuffmanEncodingTree::DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output ) -{ - HuffmanEncodingTreeNode * currentNode; - - unsigned outputWriteIndex; - outputWriteIndex = 0; - currentNode = root; - - // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root - - for ( unsigned counter = 0; counter < sizeInBits; counter++ ) - { - if ( input->ReadBit() == false ) // left! - currentNode = currentNode->left; - else - currentNode = currentNode->right; - - if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf - { - - if ( outputWriteIndex < maxCharsToWrite ) - output[ outputWriteIndex ] = currentNode->value; - - outputWriteIndex++; - - currentNode = root; - } - } - - return outputWriteIndex; -} - -// Pass an array of encoded bytes to array and a preallocated BitStream to receive the output -void HuffmanEncodingTree::DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output ) -{ - HuffmanEncodingTreeNode * currentNode; - - if ( sizeInBits <= 0 ) - return ; - - RakNet::BitStream bitStream( input, BITS_TO_BYTES(sizeInBits), false ); - - currentNode = root; - - // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root - for ( unsigned counter = 0; counter < sizeInBits; counter++ ) - { - if ( bitStream.ReadBit() == false ) // left! - currentNode = currentNode->left; - else - currentNode = currentNode->right; - - if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf - { - output->WriteBits( &( currentNode->value ), sizeof( char ) * 8, true ); // Use WriteBits instead of Write(char) because we want to avoid TYPE_CHECKING - currentNode = root; - } - } -} - -// Insertion sort. Slow but easy to write in this case -void HuffmanEncodingTree::InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const -{ - if ( huffmanEncodingTreeNodeList->Size() == 0 ) - { - huffmanEncodingTreeNodeList->Insert( node ); - return ; - } - - huffmanEncodingTreeNodeList->Beginning(); - - unsigned counter = 0; -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( 1 ) - { - if ( huffmanEncodingTreeNodeList->Peek()->weight < node->weight ) - ++( *huffmanEncodingTreeNodeList ); - else - { - huffmanEncodingTreeNodeList->Insert( node ); - break; - } - - // Didn't find a spot in the middle - add to the end - if ( ++counter == huffmanEncodingTreeNodeList->Size() ) - { - huffmanEncodingTreeNodeList->End(); - - huffmanEncodingTreeNodeList->Add( node ) - - ; // Add to the end - break; - } - } -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "DS_HuffmanEncodingTree.h" +#include "DS_Queue.h" +#include "BitStream.h" +#include "RakAssert.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +HuffmanEncodingTree::HuffmanEncodingTree() +{ + root = 0; +} + +HuffmanEncodingTree::~HuffmanEncodingTree() +{ + FreeMemory(); +} + +void HuffmanEncodingTree::FreeMemory( void ) +{ + if ( root == 0 ) + return ; + + // Use an in-order traversal to delete the tree + DataStructures::Queue nodeQueue; + + HuffmanEncodingTreeNode *node; + + nodeQueue.Push( root, _FILE_AND_LINE_ ); + + while ( nodeQueue.Size() > 0 ) + { + node = nodeQueue.Pop(); + + if ( node->left ) + nodeQueue.Push( node->left, _FILE_AND_LINE_ ); + + if ( node->right ) + nodeQueue.Push( node->right, _FILE_AND_LINE_ ); + + RakNet::OP_DELETE(node, _FILE_AND_LINE_); + } + + // Delete the encoding table + for ( int i = 0; i < 256; i++ ) + rakFree_Ex(encodingTable[ i ].encoding, _FILE_AND_LINE_ ); + + root = 0; +} + + +////#include + +// Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree +void HuffmanEncodingTree::GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ) +{ + int counter; + HuffmanEncodingTreeNode * node; + HuffmanEncodingTreeNode *leafList[ 256 ]; // Keep a copy of the pointers to all the leaves so we can generate the encryption table bottom-up, which is easier + // 1. Make 256 trees each with a weight equal to the frequency of the corresponding character + DataStructures::LinkedList huffmanEncodingTreeNodeList; + + FreeMemory(); + + for ( counter = 0; counter < 256; counter++ ) + { + node = RakNet::OP_NEW( _FILE_AND_LINE_ ); + node->left = 0; + node->right = 0; + node->value = (unsigned char) counter; + node->weight = frequencyTable[ counter ]; + + if ( node->weight == 0 ) + node->weight = 1; // 0 weights are illegal + + leafList[ counter ] = node; // Used later to generate the encryption table + + InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); // Insert and maintain sort order. + } + + + // 2. While there is more than one tree, take the two smallest trees and merge them so that the two trees are the left and right + // children of a new node, where the new node has the weight the sum of the weight of the left and right child nodes. +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( 1 ) + { + huffmanEncodingTreeNodeList.Beginning(); + HuffmanEncodingTreeNode *lesser, *greater; + lesser = huffmanEncodingTreeNodeList.Pop(); + greater = huffmanEncodingTreeNodeList.Pop(); + node = RakNet::OP_NEW( _FILE_AND_LINE_ ); + node->left = lesser; + node->right = greater; + node->weight = lesser->weight + greater->weight; + lesser->parent = node; // This is done to make generating the encryption table easier + greater->parent = node; // This is done to make generating the encryption table easier + + if ( huffmanEncodingTreeNodeList.Size() == 0 ) + { + // 3. Assign the one remaining node in the list to the root node. + root = node; + root->parent = 0; + break; + } + + // Put the new node back into the list at the correct spot to maintain the sort. Linear search time + InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); + } + + bool tempPath[ 256 ]; // Maximum path length is 256 + unsigned short tempPathLength; + HuffmanEncodingTreeNode *currentNode; + RakNet::BitStream bitStream; + + // Generate the encryption table. From before, we have an array of pointers to all the leaves which contain pointers to their parents. + // This can be done more efficiently but this isn't bad and it's way easier to program and debug + + for ( counter = 0; counter < 256; counter++ ) + { + // Already done at the end of the loop and before it! + tempPathLength = 0; + + // Set the current node at the leaf + currentNode = leafList[ counter ]; + + do + { + if ( currentNode->parent->left == currentNode ) // We're storing the paths in reverse order.since we are going from the leaf to the root + tempPath[ tempPathLength++ ] = false; + else + tempPath[ tempPathLength++ ] = true; + + currentNode = currentNode->parent; + } + + while ( currentNode != root ); + + // Write to the bitstream in the reverse order that we stored the path, which gives us the correct order from the root to the leaf + while ( tempPathLength-- > 0 ) + { + if ( tempPath[ tempPathLength ] ) // Write 1's and 0's because writing a bool will write the BitStream TYPE_CHECKING validation bits if that is defined along with the actual data bit, which is not what we want + bitStream.Write1(); + else + bitStream.Write0(); + } + + // Read data from the bitstream, which is written to the encoding table in bits and bitlength. Note this function allocates the encodingTable[counter].encoding pointer + encodingTable[ counter ].bitLength = ( unsigned char ) bitStream.CopyData( &encodingTable[ counter ].encoding ); + + // Reset the bitstream for the next iteration + bitStream.Reset(); + } +} + +// Pass an array of bytes to array and a preallocated BitStream to receive the output +void HuffmanEncodingTree::EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output ) +{ + unsigned counter; + + // For each input byte, Write out the corresponding series of 1's and 0's that give the encoded representation + for ( counter = 0; counter < sizeInBytes; counter++ ) + { + output->WriteBits( encodingTable[ input[ counter ] ].encoding, encodingTable[ input[ counter ] ].bitLength, false ); // Data is left aligned + } + + // Byte align the output so the unassigned remaining bits don't equate to some actual value + if ( output->GetNumberOfBitsUsed() % 8 != 0 ) + { + // Find an input that is longer than the remaining bits. Write out part of it to pad the output to be byte aligned. + unsigned char remainingBits = (unsigned char) ( 8 - ( output->GetNumberOfBitsUsed() % 8 ) ); + + for ( counter = 0; counter < 256; counter++ ) + if ( encodingTable[ counter ].bitLength > remainingBits ) + { + output->WriteBits( encodingTable[ counter ].encoding, remainingBits, false ); // Data is left aligned + break; + } + +#ifdef _DEBUG + RakAssert( counter != 256 ); // Given 256 elements, we should always be able to find an input that would be >= 7 bits + +#endif + + } +} + +unsigned HuffmanEncodingTree::DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output ) +{ + HuffmanEncodingTreeNode * currentNode; + + unsigned outputWriteIndex; + outputWriteIndex = 0; + currentNode = root; + + // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root + + for ( unsigned counter = 0; counter < sizeInBits; counter++ ) + { + if ( input->ReadBit() == false ) // left! + currentNode = currentNode->left; + else + currentNode = currentNode->right; + + if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf + { + + if ( outputWriteIndex < maxCharsToWrite ) + output[ outputWriteIndex ] = currentNode->value; + + outputWriteIndex++; + + currentNode = root; + } + } + + return outputWriteIndex; +} + +// Pass an array of encoded bytes to array and a preallocated BitStream to receive the output +void HuffmanEncodingTree::DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output ) +{ + HuffmanEncodingTreeNode * currentNode; + + if ( sizeInBits <= 0 ) + return ; + + RakNet::BitStream bitStream( input, BITS_TO_BYTES(sizeInBits), false ); + + currentNode = root; + + // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root + for ( unsigned counter = 0; counter < sizeInBits; counter++ ) + { + if ( bitStream.ReadBit() == false ) // left! + currentNode = currentNode->left; + else + currentNode = currentNode->right; + + if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf + { + output->WriteBits( &( currentNode->value ), sizeof( char ) * 8, true ); // Use WriteBits instead of Write(char) because we want to avoid TYPE_CHECKING + currentNode = root; + } + } +} + +// Insertion sort. Slow but easy to write in this case +void HuffmanEncodingTree::InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const +{ + if ( huffmanEncodingTreeNodeList->Size() == 0 ) + { + huffmanEncodingTreeNodeList->Insert( node ); + return ; + } + + huffmanEncodingTreeNodeList->Beginning(); + + unsigned counter = 0; +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( 1 ) + { + if ( huffmanEncodingTreeNodeList->Peek()->weight < node->weight ) + ++( *huffmanEncodingTreeNodeList ); + else + { + huffmanEncodingTreeNodeList->Insert( node ); + break; + } + + // Didn't find a spot in the middle - add to the end + if ( ++counter == huffmanEncodingTreeNodeList->Size() ) + { + huffmanEncodingTreeNodeList->End(); + + huffmanEncodingTreeNodeList->Add( node ) + + ; // Add to the end + break; + } + } +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTree.h b/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTree.h index 41cae1f..645f285 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTree.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTree.h @@ -1,67 +1,67 @@ -/// \file DS_HuffmanEncodingTree.h -/// \brief \b [Internal] Generates a huffman encoding tree, used for string and global compression. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __HUFFMAN_ENCODING_TREE -#define __HUFFMAN_ENCODING_TREE - -#include "RakMemoryOverride.h" -#include "DS_HuffmanEncodingTreeNode.h" -#include "BitStream.h" -#include "Export.h" -#include "DS_LinkedList.h" - -namespace RakNet -{ - -/// This generates special cases of the huffman encoding tree using 8 bit keys with the additional condition that unused combinations of 8 bits are treated as a frequency of 1 -class RAK_DLL_EXPORT HuffmanEncodingTree -{ - -public: - HuffmanEncodingTree(); - ~HuffmanEncodingTree(); - - /// \brief Pass an array of bytes to array and a preallocated BitStream to receive the output. - /// \param [in] input Array of bytes to encode - /// \param [in] sizeInBytes size of \a input - /// \param [out] output The bitstream to write to - void EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output ); - - // \brief Decodes an array encoded by EncodeArray(). - unsigned DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output ); - void DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output ); - - /// \brief Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree. - void GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ); - - /// \brief Free the memory used by the tree. - void FreeMemory( void ); - -private: - - /// The root node of the tree - - HuffmanEncodingTreeNode *root; - - /// Used to hold bit encoding for one character - - - struct CharacterEncoding - { - unsigned char* encoding; - unsigned short bitLength; - }; - - CharacterEncoding encodingTable[ 256 ]; - - void InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const; -}; - -} // namespace RakNet - -#endif +/// \file DS_HuffmanEncodingTree.h +/// \brief \b [Internal] Generates a huffman encoding tree, used for string and global compression. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __HUFFMAN_ENCODING_TREE +#define __HUFFMAN_ENCODING_TREE + +#include "RakMemoryOverride.h" +#include "DS_HuffmanEncodingTreeNode.h" +#include "BitStream.h" +#include "Export.h" +#include "DS_LinkedList.h" + +namespace RakNet +{ + +/// This generates special cases of the huffman encoding tree using 8 bit keys with the additional condition that unused combinations of 8 bits are treated as a frequency of 1 +class RAK_DLL_EXPORT HuffmanEncodingTree +{ + +public: + HuffmanEncodingTree(); + ~HuffmanEncodingTree(); + + /// \brief Pass an array of bytes to array and a preallocated BitStream to receive the output. + /// \param [in] input Array of bytes to encode + /// \param [in] sizeInBytes size of \a input + /// \param [out] output The bitstream to write to + void EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output ); + + // \brief Decodes an array encoded by EncodeArray(). + unsigned DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output ); + void DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output ); + + /// \brief Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree. + void GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ); + + /// \brief Free the memory used by the tree. + void FreeMemory( void ); + +private: + + /// The root node of the tree + + HuffmanEncodingTreeNode *root; + + /// Used to hold bit encoding for one character + + + struct CharacterEncoding + { + unsigned char* encoding; + unsigned short bitLength; + }; + + CharacterEncoding encodingTable[ 256 ]; + + void InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const; +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTreeFactory.h b/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTreeFactory.h index c6a6acc..eab9c7b 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTreeFactory.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTreeFactory.h @@ -1,57 +1,57 @@ -/// \file DS_HuffmanEncodingTreeFactory.h -/// \internal -/// \brief Creates instances of the class HuffmanEncodingTree -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __HUFFMAN_ENCODING_TREE_FACTORY -#define __HUFFMAN_ENCODING_TREE_FACTORY - -#include "RakMemoryOverride.h" - -namespace RakNet { -/// Forward declarations -class HuffmanEncodingTree; - -/// \brief Creates instances of the class HuffmanEncodingTree -/// \details This class takes a frequency table and given that frequence table, will generate an instance of HuffmanEncodingTree -class HuffmanEncodingTreeFactory -{ -public: - /// Default constructor - HuffmanEncodingTreeFactory(); - - /// \brief Reset the frequency table. - /// \details You don't need to call this unless you want to reuse the class for a new tree - void Reset( void ); - - /// \brief Pass an array of bytes to this to add those elements to the frequency table. - /// \param[in] array the data to insert into the frequency table - /// \param[in] size the size of the data to insert - void AddToFrequencyTable( unsigned char *array, int size ); - - /// \brief Copies the frequency table to the array passed. Retrieve the frequency table. - /// \param[in] _frequency The frequency table used currently - void GetFrequencyTable( unsigned int _frequency[ 256 ] ); - - /// \brief Returns the frequency table as a pointer. - /// \return the address of the frenquency table - unsigned int * GetFrequencyTable( void ); - - /// \brief Generate a HuffmanEncodingTree. - /// \details You can also use GetFrequencyTable and GenerateFromFrequencyTable in the tree itself - /// \return The generated instance of HuffmanEncodingTree - HuffmanEncodingTree * GenerateTree( void ); - -private: - - /// Frequency table - unsigned int frequency[ 256 ]; -}; - -} // namespace RakNet - -#endif +/// \file DS_HuffmanEncodingTreeFactory.h +/// \internal +/// \brief Creates instances of the class HuffmanEncodingTree +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __HUFFMAN_ENCODING_TREE_FACTORY +#define __HUFFMAN_ENCODING_TREE_FACTORY + +#include "RakMemoryOverride.h" + +namespace RakNet { +/// Forward declarations +class HuffmanEncodingTree; + +/// \brief Creates instances of the class HuffmanEncodingTree +/// \details This class takes a frequency table and given that frequence table, will generate an instance of HuffmanEncodingTree +class HuffmanEncodingTreeFactory +{ +public: + /// Default constructor + HuffmanEncodingTreeFactory(); + + /// \brief Reset the frequency table. + /// \details You don't need to call this unless you want to reuse the class for a new tree + void Reset( void ); + + /// \brief Pass an array of bytes to this to add those elements to the frequency table. + /// \param[in] array the data to insert into the frequency table + /// \param[in] size the size of the data to insert + void AddToFrequencyTable( unsigned char *array, int size ); + + /// \brief Copies the frequency table to the array passed. Retrieve the frequency table. + /// \param[in] _frequency The frequency table used currently + void GetFrequencyTable( unsigned int _frequency[ 256 ] ); + + /// \brief Returns the frequency table as a pointer. + /// \return the address of the frenquency table + unsigned int * GetFrequencyTable( void ); + + /// \brief Generate a HuffmanEncodingTree. + /// \details You can also use GetFrequencyTable and GenerateFromFrequencyTable in the tree itself + /// \return The generated instance of HuffmanEncodingTree + HuffmanEncodingTree * GenerateTree( void ); + +private: + + /// Frequency table + unsigned int frequency[ 256 ]; +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTreeNode.h b/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTreeNode.h index 40523e1..f726e4f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTreeNode.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_HuffmanEncodingTreeNode.h @@ -1,21 +1,21 @@ -/// \file -/// \brief \b [Internal] A single node in the Huffman Encoding Tree. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __HUFFMAN_ENCODING_TREE_NODE -#define __HUFFMAN_ENCODING_TREE_NODE - -struct HuffmanEncodingTreeNode -{ - unsigned char value; - unsigned weight; - HuffmanEncodingTreeNode *left; - HuffmanEncodingTreeNode *right; - HuffmanEncodingTreeNode *parent; -}; - -#endif +/// \file +/// \brief \b [Internal] A single node in the Huffman Encoding Tree. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __HUFFMAN_ENCODING_TREE_NODE +#define __HUFFMAN_ENCODING_TREE_NODE + +struct HuffmanEncodingTreeNode +{ + unsigned char value; + unsigned weight; + HuffmanEncodingTreeNode *left; + HuffmanEncodingTreeNode *right; + HuffmanEncodingTreeNode *parent; +}; + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_LinkedList.h b/project/lib_projects/raknet/jni/RaknetSources/DS_LinkedList.h index 4002855..be9548e 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_LinkedList.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_LinkedList.h @@ -1,1252 +1,1252 @@ -/// \file DS_LinkedList.h -/// \internal -/// \brief Straightforward linked list data structure. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __LINKED_LIST_H -#define __LINKED_LIST_H - -#include "Export.h" -#include "RakMemoryOverride.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - // Prototype to prevent error in CircularLinkedList class when a reference is made to a LinkedList class - template - class RAK_DLL_EXPORT LinkedList; - - /** - * \brief (Circular) Linked List ADT (Doubly Linked Pointer to Node Style) - - * - * \details - * By Kevin Jenkins (http://www.rakkar.org) - * Initilize with the following command - * LinkedList - * OR - * CircularLinkedList - * - * Has the following member functions - * - size: returns number of elements in the linked list - * - insert(item): inserts @em item at the current position in - * the LinkedList. - * - add(item): inserts @em item after the current position in - * the LinkedList. Does not increment the position - * - replace(item): replaces the element at the current position @em item. - * - peek: returns the element at the current position - * - pop: returns the element at the current position and deletes it - * - del: deletes the current element. Does nothing for an empty list. - * - clear: empties the LinkedList and returns storage - * - bool IsInitem): Does a linear search for @em item. Does not set - * the position to it, only returns true on item found, false otherwise - * - bool find(item): Does a linear search for @em item and sets the current - * position to point to it if and only if the item is found. Returns true - * on item found, false otherwise - * - sort: Sorts the elements of the list with a mergesort and sets the - * current pointer to the first element - * - concatenate(list L): This appends L to the current list - * - ++(prefix): moves the pointer one element up in the list and returns the - * appropriate copy of the element in the list - * - --(prefix): moves the pointer one element back in the list and returns - * the appropriate copy of the element in the list - * - beginning - moves the pointer to the start of the list. For circular - * linked lists this is first 'position' created. You should call this - * after the sort function to read the first value. - * - end - moves the pointer to the end of the list. For circular linked - * lists this is one less than the first 'position' created - * The assignment and copy constructor operators are defined - * - * \note - * 1. LinkedList and CircularLinkedList are exactly the same except LinkedList - * won't let you wrap around the root and lets you jump to two positions - * relative to the root/ - * 2. Postfix ++ and -- can be used but simply call the prefix versions. - * - * - * EXAMPLE: - * @code - * LinkedList A; // Creates a Linked List of integers called A - * CircularLinkedList B; // Creates a Circular Linked List of - * // integers called B - * - * A.Insert(20); // Adds 20 to A. A: 20 - current is 20 - * A.Insert(5); // Adds 5 to A. A: 5 20 - current is 5 - * A.Insert(1); // Adds 1 to A. A: 1 5 20 - current is 1 - * - * A.IsIn1); // returns true - * A.IsIn200); // returns false - * A.Find(5); // returns true and sets current to 5 - * A.Peek(); // returns 5 - * A.Find(1); // returns true and sets current to 1 - * - * (++A).Peek(); // Returns 5 - * A.Peek(); // Returns 5 - * - * A.Replace(10); // Replaces 5 with 10. - * A.Peek(); // Returns 10 - * - * A.Beginning(); // Current points to the beginning of the list at 1 - * - * (++A).Peek(); // Returns 5 - * A.Peek(); // Returns 10 - * - * A.Del(); // Deletes 10. Current points to the next element, which is 20 - * A.Peek(); // Returns 20 - * - * A.Beginning(); // Current points to the beginning of the list at 1 - * - * (++A).Peek(); // Returns 5 - * A.Peek(); // Returns 20 - * - * A.Clear(_FILE_AND_LINE_); // Deletes all nodes in A - * - * A.Insert(5); // A: 5 - current is 5 - * A.Insert(6); // A: 6 5 - current is 6 - * A.Insert(7); // A: 7 6 5 - current is 7 - * - * A.Clear(_FILE_AND_LINE_); - * B.Clear(_FILE_AND_LINE_); - * - * B.Add(10); - * B.Add(20); - * B.Add(30); - * B.Add(5); - * B.Add(2); - * B.Add(25); - * // Sorts the numbers in the list and sets the current pointer to the - * // first element - * B.sort(); - * - * // Postfix ++ just calls the prefix version and has no functional - * // difference. - * B.Peek(); // Returns 2 - * B++; - * B.Peek(); // Returns 5 - * B++; - * B.Peek(); // Returns 10 - * B++; - * B.Peek(); // Returns 20 - * B++; - * B.Peek(); // Returns 25 - * B++; - * B.Peek(); // Returns 30 - * @endcode - */ - template - - class CircularLinkedList - { - - public: - - struct node - { - CircularLinkedListType item; - - node* previous; - node* next; - }; - - CircularLinkedList(); - ~CircularLinkedList(); - CircularLinkedList( const CircularLinkedList& original_copy ); - // CircularLinkedList(LinkedList original_copy) {CircularLinkedList(original_copy);} // Converts linked list to circular type - bool operator= ( const CircularLinkedList& original_copy ); - CircularLinkedList& operator++(); // CircularLinkedList A; ++A; - CircularLinkedList& operator++( int ); // Circular_Linked List A; A++; - CircularLinkedList& operator--(); // CircularLinkedList A; --A; - CircularLinkedList& operator--( int ); // Circular_Linked List A; A--; - bool IsIn( const CircularLinkedListType& input ); - bool Find( const CircularLinkedListType& input ); - void Insert( const CircularLinkedListType& input ); - - CircularLinkedListType& Add ( const CircularLinkedListType& input ) - - ; // Adds after the current position - void Replace( const CircularLinkedListType& input ); - - void Del( void ); - - unsigned int Size( void ); - - CircularLinkedListType& Peek( void ); - - CircularLinkedListType Pop( void ); - - void Clear( void ); - - void Sort( void ); - - void Beginning( void ); - - void End( void ); - - void Concatenate( const CircularLinkedList& L ); - - protected: - unsigned int list_size; - - node *root; - - node *position; - - node* FindPointer( const CircularLinkedListType& input ); - - private: - CircularLinkedList Merge( CircularLinkedList L1, CircularLinkedList L2 ); - - CircularLinkedList Mergesort( const CircularLinkedList& L ); - }; - - template - - class LinkedList : public CircularLinkedList - { - - public: - LinkedList() - {} - - LinkedList( const LinkedList& original_copy ); - ~LinkedList(); - bool operator= ( const LinkedList& original_copy ); - LinkedList& operator++(); // LinkedList A; ++A; - LinkedList& operator++( int ); // Linked List A; A++; - LinkedList& operator--(); // LinkedList A; --A; - LinkedList& operator--( int ); // Linked List A; A--; - - private: - LinkedList Merge( LinkedList L1, LinkedList L2 ); - LinkedList Mergesort( const LinkedList& L ); - - }; - - - template - inline void CircularLinkedList::Beginning( void ) - { - if ( this->root ) - this->position = this->root; - } - - template - inline void CircularLinkedList::End( void ) - { - if ( this->root ) - this->position = this->root->previous; - } - - template - bool LinkedList::operator= ( const LinkedList& original_copy ) - { - typename LinkedList::node * original_copy_pointer, *last, *save_position; - - if ( ( &original_copy ) != this ) - { - - this->Clear(); - - - if ( original_copy.list_size == 0 ) - { - this->root = 0; - this->position = 0; - this->list_size = 0; - } - - else - if ( original_copy.list_size == 1 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - } - - else - { - // Setup the first part of the root node - original_copy_pointer = original_copy.root; - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - do - { - - - // Save the current element - last = this->position; - - // Point to the next node in the source list - original_copy_pointer = original_copy_pointer->next; - - // Create a new node and point position to it - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // Copy the item to the new node - // *(position->item)=*(original_copy_pointer->item); - this->position->item = original_copy_pointer->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - - // Set the previous pointer for the new node - ( this->position->previous ) = last; - - // Set the next pointer for the old node to the new node - ( last->next ) = this->position; - - } - - while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); - - // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node - this->position->next = this->root; - - this->root->previous = this->position; - - this->list_size = original_copy.list_size; - - this->position = save_position; - } - } - - return true; - } - - - template - CircularLinkedList::CircularLinkedList() - { - this->root = 0; - this->position = 0; - this->list_size = 0; - } - - template - CircularLinkedList::~CircularLinkedList() - { - this->Clear(); - } - - template - LinkedList::~LinkedList() - { - this->Clear(); - } - - template - LinkedList::LinkedList( const LinkedList& original_copy ) - { - typename LinkedList::node * original_copy_pointer, *last, *save_position; - - if ( original_copy.list_size == 0 ) - { - this->root = 0; - this->position = 0; - this->list_size = 0; - return ; - } - - else - if ( original_copy.list_size == 1 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // *(root->item) = *((original_copy.root)->item); - this->root->item = original_copy.root->item; - } - - else - { - // Setup the first part of the root node - original_copy_pointer = original_copy.root; - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - do - { - // Save the current element - last = this->position; - - // Point to the next node in the source list - original_copy_pointer = original_copy_pointer->next; - - // Create a new node and point position to it - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // Copy the item to the new node - // *(position->item)=*(original_copy_pointer->item); - this->position->item = original_copy_pointer->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - // Set the previous pointer for the new node - ( this->position->previous ) = last; - - // Set the next pointer for the old node to the new node - ( last->next ) = this->position; - - } - - while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); - - // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node - this->position->next = this->root; - - this->root->previous = this->position; - - this->list_size = original_copy.list_size; - - this->position = save_position; - } - } - -#ifdef _MSC_VER -#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized -#endif - template - CircularLinkedList::CircularLinkedList( const CircularLinkedList& original_copy ) - { - node * original_copy_pointer; - node *last; - node *save_position; - - if ( original_copy.list_size == 0 ) - { - this->root = 0; - this->position = 0; - this->list_size = 0; - return ; - } - - else - if ( original_copy.list_size == 1 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // *(root->item) = *((original_copy.root)->item); - this->root->item = original_copy.root->item; - } - - else - { - // Setup the first part of the root node - original_copy_pointer = original_copy.root; - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - do - { - - - // Save the current element - last = this->position; - - // Point to the next node in the source list - original_copy_pointer = original_copy_pointer->next; - - // Create a new node and point position to it - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // Copy the item to the new node - // *(position->item)=*(original_copy_pointer->item); - this->position->item = original_copy_pointer->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = position; - - // Set the previous pointer for the new node - ( this->position->previous ) = last; - - // Set the next pointer for the old node to the new node - ( last->next ) = this->position; - - } - - while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); - - // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node - this->position->next = this->root; - - this->root->previous = position; - - this->list_size = original_copy.list_size; - - this->position = save_position; - } - } - -#ifdef _MSC_VER -#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized -#endif - template - bool CircularLinkedList::operator= ( const CircularLinkedList& original_copy ) - { - node * original_copy_pointer; - node *last; - node *save_position; - - if ( ( &original_copy ) != this ) - { - - this->Clear(); - - - if ( original_copy.list_size == 0 ) - { - this->root = 0; - this->position = 0; - this->list_size = 0; - } - - else - if ( original_copy.list_size == 1 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - } - - else - { - // Setup the first part of the root node - original_copy_pointer = original_copy.root; - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - do - { - // Save the current element - last = this->position; - - // Point to the next node in the source list - original_copy_pointer = original_copy_pointer->next; - - // Create a new node and point position to it - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // Copy the item to the new node - // *(position->item)=*(original_copy_pointer->item); - this->position->item = original_copy_pointer->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - // Set the previous pointer for the new node - ( this->position->previous ) = last; - - // Set the next pointer for the old node to the new node - ( last->next ) = this->position; - - } - - while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); - - // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node - this->position->next = this->root; - - this->root->previous = this->position; - - this->list_size = original_copy.list_size; - - this->position = save_position; - } - } - - return true; - } - - template - void CircularLinkedList::Insert( const CircularLinkedListType& input ) - { - node * new_node; - - if ( list_size == 0 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - //*(root->item)=input; - this->root->item = input; - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - } - - else - if ( list_size == 1 ) - { - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->position; - this->root->previous = this->position; - this->position->previous = this->root; - this->position->next = this->root; - // *(position->item)=input; - this->position->item = input; - this->root = this->position; // Since we're inserting into a 1 element list the old root is now the second item - this->list_size = 2; - } - - else - { - /* - - B - | - A --- C - - position->previous=A - new_node=B - position=C - - Note that the order of the following statements is important */ - - new_node = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // new_node->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // *(new_node->item)=input; - new_node->item = input; - - // Point next of A to B - ( this->position->previous ) ->next = new_node; - - // Point last of B to A - new_node->previous = this->position->previous; - - // Point last of C to B - this->position->previous = new_node; - - // Point next of B to C - new_node->next = this->position; - - // Since the root pointer is bound to a node rather than an index this moves it back if you insert an element at the root - - if ( this->position == this->root ) - { - this->root = new_node; - this->position = this->root; - } - - // Increase the recorded size of the list by one - this->list_size++; - } - } - - template - CircularLinkedListType& CircularLinkedList::Add ( const CircularLinkedListType& input ) - { - node * new_node; - - if ( this->list_size == 0 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // *(root->item)=input; - this->root->item = input; - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // return *(position->item); - return this->position->item; - } - - else - if ( list_size == 1 ) - { - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->position; - this->root->previous = this->position; - this->position->previous = this->root; - this->position->next = this->root; - // *(position->item)=input; - this->position->item = input; - this->list_size = 2; - this->position = this->root; // Don't move the position from the root - // return *(position->item); - return this->position->item; - } - - else - { - /* - - B - | - A --- C - - new_node=B - position=A - position->next=C - - Note that the order of the following statements is important */ - - new_node = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // new_node->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // *(new_node->item)=input; - new_node->item = input; - - // Point last of B to A - new_node->previous = this->position; - - // Point next of B to C - new_node->next = ( this->position->next ); - - // Point last of C to B - ( this->position->next ) ->previous = new_node; - - // Point next of A to B - ( this->position->next ) = new_node; - - // Increase the recorded size of the list by one - this->list_size++; - - // return *(new_node->item); - return new_node->item; - } - } - - template - inline void CircularLinkedList::Replace( const CircularLinkedListType& input ) - { - if ( this->list_size > 0 ) - // *(position->item)=input; - this->position->item = input; - } - - template - void CircularLinkedList::Del() - { - node * new_position; - - if ( this->list_size == 0 ) - return ; - - else - if ( this->list_size == 1 ) - { - // RakNet::OP_DELETE(root->item, _FILE_AND_LINE_); - RakNet::OP_DELETE(this->root, _FILE_AND_LINE_); - this->root = this->position = 0; - this->list_size = 0; - } - - else - { - ( this->position->previous ) ->next = this->position->next; - ( this->position->next ) ->previous = this->position->previous; - new_position = this->position->next; - - if ( this->position == this->root ) - this->root = new_position; - - // RakNet::OP_DELETE(position->item, _FILE_AND_LINE_); - RakNet::OP_DELETE(this->position, _FILE_AND_LINE_); - - this->position = new_position; - - this->list_size--; - } - } - - template - bool CircularLinkedList::IsIn(const CircularLinkedListType& input ) - { - node * return_value, *old_position; - - old_position = this->position; - - return_value = FindPointer( input ); - this->position = old_position; - - if ( return_value != 0 ) - return true; - else - return false; // Can't find the item don't do anything - } - - template - bool CircularLinkedList::Find( const CircularLinkedListType& input ) - { - node * return_value; - - return_value = FindPointer( input ); - - if ( return_value != 0 ) - { - this->position = return_value; - return true; - } - - else - return false; // Can't find the item don't do anything - } - - template - typename CircularLinkedList::node* CircularLinkedList::FindPointer( const CircularLinkedListType& input ) - { - node * current; - - if ( this->list_size == 0 ) - return 0; - - current = this->root; - - // Search for the item starting from the root node and incrementing the pointer after every check - // If you wind up pointing at the root again you looped around the list so didn't find the item, in which case return 0 - do - { - // if (*(current->item) == input) return current; - - if ( current->item == input ) - return current; - - current = current->next; - } - - while ( current != this->root ); - - return 0; - - } - - template - inline unsigned int CircularLinkedList::Size( void ) - { - return this->list_size; - } - - template - inline CircularLinkedListType& CircularLinkedList::Peek( void ) - { - // return *(position->item); - return this->position->item; - } - - template - CircularLinkedListType CircularLinkedList::Pop( void ) - { - CircularLinkedListType element; - element = Peek(); - Del(); - return CircularLinkedListType( element ); // return temporary - } - - // Prefix - template - CircularLinkedList& CircularLinkedList::operator++() - { - if ( this->list_size != 0 ) - position = position->next; - - return *this; - } - - /* - // Postfix - template - CircularLinkedList& CircularLinkedList::operator++(int) - { - CircularLinkedList before; - before=*this; - operator++(); - return before; - } - */ - - template - CircularLinkedList& CircularLinkedList::operator++( int ) - { - return this->operator++(); - } - - // Prefix - template - CircularLinkedList& CircularLinkedList::operator--() - { - if ( this->list_size != 0 ) - this->position = this->position->previous; - - return *this; - } - - /* - // Postfix - template - CircularLinkedList& CircularLinkedList::operator--(int) - { - CircularLinkedList before; - before=*this; - operator--(); - return before; - } - */ - - template - CircularLinkedList& CircularLinkedList::operator--( int ) - { - return this->operator--(); - } - - template - void CircularLinkedList::Clear( void ) - { - if ( this->list_size == 0 ) - return ; - else - if ( this->list_size == 1 ) // {RakNet::OP_DELETE(root->item); RakNet::OP_DELETE(root, _FILE_AND_LINE_);} - { - RakNet::OP_DELETE(this->root, _FILE_AND_LINE_); - } - - else - { - node* current; - node* temp; - - current = this->root; - - do - { - temp = current; - current = current->next; - // RakNet::OP_DELETE(temp->item, _FILE_AND_LINE_); - RakNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - - while ( current != this->root ); - } - - this->list_size = 0; - this->root = 0; - this->position = 0; - } - - template - inline void CircularLinkedList::Concatenate( const CircularLinkedList& L ) - { - unsigned int counter; - node* ptr; - - if ( L.list_size == 0 ) - return ; - - if ( this->list_size == 0 ) - * this = L; - - ptr = L.root; - - this->position = this->root->previous; - - // Cycle through each element in L and add it to the current list - for ( counter = 0; counter < L.list_size; counter++ ) - { - // Add item after the current item pointed to - // add(*(ptr->item)); - - Add ( ptr->item ); - - // Update pointers. Moving ptr keeps the current pointer at the end of the list since the add function does not move the pointer - ptr = ptr->next; - - this->position = this->position->next; - } - } - - template - inline void CircularLinkedList::Sort( void ) - { - if ( this->list_size <= 1 ) - return ; - - // Call equal operator to assign result of mergesort to current object - *this = Mergesort( *this ); - - this->position = this->root; - } - - template - CircularLinkedList CircularLinkedList::Mergesort( const CircularLinkedList& L ) - { - unsigned int counter; - node* location; - CircularLinkedList L1; - CircularLinkedList L2; - - location = L.root; - - // Split the list into two equal size sublists, L1 and L2 - - for ( counter = 0; counter < L.list_size / 2; counter++ ) - { - // L1.add (*(location->item)); - L1.Add ( location->item ); - location = location->next; - } - - for ( ;counter < L.list_size; counter++ ) - { - // L2.Add(*(location->item)); - L2.Add ( location->item ); - location = location->next; - } - - // Recursively sort the sublists - if ( L1.list_size > 1 ) - L1 = Mergesort( L1 ); - - if ( L2.list_size > 1 ) - L2 = Mergesort( L2 ); - - // Merge the two sublists - return Merge( L1, L2 ); - } - - template - CircularLinkedList CircularLinkedList::Merge( CircularLinkedList L1, CircularLinkedList L2 ) - { - CircularLinkedList X; - CircularLinkedListType element; - L1.position = L1.root; - L2.position = L2.root; - - // While neither list is empty - - while ( ( L1.list_size != 0 ) && ( L2.list_size != 0 ) ) - { - // Compare the first items of L1 and L2 - // Remove the smaller of the two items from the list - - if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) - // if ((*((L1.root)->item)) < (*((L2.root)->item))) - { - // element = *((L1.root)->item); - element = ( L1.root ) ->item; - L1.Del(); - } - else - { - // element = *((L2.root)->item); - element = ( L2.root ) ->item; - L2.Del(); - } - - // Add this item to the end of X - X.Add( element ); - - X++; - } - - // Add the remaining list to X - if ( L1.list_size != 0 ) - X.Concatenate( L1 ); - else - X.Concatenate( L2 ); - - return X; - } - - template - LinkedList LinkedList::Mergesort( const LinkedList& L ) - { - unsigned int counter; - typename LinkedList::node* location; - LinkedList L1; - LinkedList L2; - - location = L.root; - - // Split the list into two equal size sublists, L1 and L2 - - for ( counter = 0; counter < L.LinkedList_size / 2; counter++ ) - { - // L1.add (*(location->item)); - L1.Add ( location->item ); - location = location->next; - } - - for ( ;counter < L.LinkedList_size; counter++ ) - { - // L2.Add(*(location->item)); - L2.Add ( location->item ); - location = location->next; - } - - // Recursively sort the sublists - if ( L1.list_size > 1 ) - L1 = Mergesort( L1 ); - - if ( L2.list_size > 1 ) - L2 = Mergesort( L2 ); - - // Merge the two sublists - return Merge( L1, L2 ); - } - - template - LinkedList LinkedList::Merge( LinkedList L1, LinkedList L2 ) - { - LinkedList X; - LinkedListType element; - L1.position = L1.root; - L2.position = L2.root; - - // While neither list is empty - - while ( ( L1.LinkedList_size != 0 ) && ( L2.LinkedList_size != 0 ) ) - { - // Compare the first items of L1 and L2 - // Remove the smaller of the two items from the list - - if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) - // if ((*((L1.root)->item)) < (*((L2.root)->item))) - { - element = ( L1.root ) ->item; - // element = *((L1.root)->item); - L1.Del(); - } - else - { - element = ( L2.root ) ->item; - // element = *((L2.root)->item); - L2.Del(); - } - - // Add this item to the end of X - X.Add( element ); - } - - // Add the remaining list to X - if ( L1.LinkedList_size != 0 ) - X.concatenate( L1 ); - else - X.concatenate( L2 ); - - return X; - } - - - // Prefix - template - LinkedList& LinkedList::operator++() - { - if ( ( this->list_size != 0 ) && ( this->position->next != this->root ) ) - this->position = this->position->next; - - return *this; - } - - /* - // Postfix - template - LinkedList& LinkedList::operator++(int) - { - LinkedList before; - before=*this; - operator++(); - return before; - } - */ - // Postfix - template - LinkedList& LinkedList::operator++( int ) - { - return this->operator++(); - } - - // Prefix - template - LinkedList& LinkedList::operator--() - { - if ( ( this->list_size != 0 ) && ( this->position != this->root ) ) - this->position = this->position->previous; - - return *this; - } - - /* - // Postfix - template - LinkedList& LinkedList::operator--(int) - { - LinkedList before; - before=*this; - operator--(); - return before; - } - */ - - // Postfix - template - LinkedList& LinkedList::operator--( int ) - { - return this->operator--(); - } - -} // End namespace - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif +/// \file DS_LinkedList.h +/// \internal +/// \brief Straightforward linked list data structure. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __LINKED_LIST_H +#define __LINKED_LIST_H + +#include "Export.h" +#include "RakMemoryOverride.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + // Prototype to prevent error in CircularLinkedList class when a reference is made to a LinkedList class + template + class RAK_DLL_EXPORT LinkedList; + + /** + * \brief (Circular) Linked List ADT (Doubly Linked Pointer to Node Style) - + * + * \details + * By Kevin Jenkins (http://www.rakkar.org) + * Initilize with the following command + * LinkedList + * OR + * CircularLinkedList + * + * Has the following member functions + * - size: returns number of elements in the linked list + * - insert(item): inserts @em item at the current position in + * the LinkedList. + * - add(item): inserts @em item after the current position in + * the LinkedList. Does not increment the position + * - replace(item): replaces the element at the current position @em item. + * - peek: returns the element at the current position + * - pop: returns the element at the current position and deletes it + * - del: deletes the current element. Does nothing for an empty list. + * - clear: empties the LinkedList and returns storage + * - bool IsInitem): Does a linear search for @em item. Does not set + * the position to it, only returns true on item found, false otherwise + * - bool find(item): Does a linear search for @em item and sets the current + * position to point to it if and only if the item is found. Returns true + * on item found, false otherwise + * - sort: Sorts the elements of the list with a mergesort and sets the + * current pointer to the first element + * - concatenate(list L): This appends L to the current list + * - ++(prefix): moves the pointer one element up in the list and returns the + * appropriate copy of the element in the list + * - --(prefix): moves the pointer one element back in the list and returns + * the appropriate copy of the element in the list + * - beginning - moves the pointer to the start of the list. For circular + * linked lists this is first 'position' created. You should call this + * after the sort function to read the first value. + * - end - moves the pointer to the end of the list. For circular linked + * lists this is one less than the first 'position' created + * The assignment and copy constructor operators are defined + * + * \note + * 1. LinkedList and CircularLinkedList are exactly the same except LinkedList + * won't let you wrap around the root and lets you jump to two positions + * relative to the root/ + * 2. Postfix ++ and -- can be used but simply call the prefix versions. + * + * + * EXAMPLE: + * @code + * LinkedList A; // Creates a Linked List of integers called A + * CircularLinkedList B; // Creates a Circular Linked List of + * // integers called B + * + * A.Insert(20); // Adds 20 to A. A: 20 - current is 20 + * A.Insert(5); // Adds 5 to A. A: 5 20 - current is 5 + * A.Insert(1); // Adds 1 to A. A: 1 5 20 - current is 1 + * + * A.IsIn1); // returns true + * A.IsIn200); // returns false + * A.Find(5); // returns true and sets current to 5 + * A.Peek(); // returns 5 + * A.Find(1); // returns true and sets current to 1 + * + * (++A).Peek(); // Returns 5 + * A.Peek(); // Returns 5 + * + * A.Replace(10); // Replaces 5 with 10. + * A.Peek(); // Returns 10 + * + * A.Beginning(); // Current points to the beginning of the list at 1 + * + * (++A).Peek(); // Returns 5 + * A.Peek(); // Returns 10 + * + * A.Del(); // Deletes 10. Current points to the next element, which is 20 + * A.Peek(); // Returns 20 + * + * A.Beginning(); // Current points to the beginning of the list at 1 + * + * (++A).Peek(); // Returns 5 + * A.Peek(); // Returns 20 + * + * A.Clear(_FILE_AND_LINE_); // Deletes all nodes in A + * + * A.Insert(5); // A: 5 - current is 5 + * A.Insert(6); // A: 6 5 - current is 6 + * A.Insert(7); // A: 7 6 5 - current is 7 + * + * A.Clear(_FILE_AND_LINE_); + * B.Clear(_FILE_AND_LINE_); + * + * B.Add(10); + * B.Add(20); + * B.Add(30); + * B.Add(5); + * B.Add(2); + * B.Add(25); + * // Sorts the numbers in the list and sets the current pointer to the + * // first element + * B.sort(); + * + * // Postfix ++ just calls the prefix version and has no functional + * // difference. + * B.Peek(); // Returns 2 + * B++; + * B.Peek(); // Returns 5 + * B++; + * B.Peek(); // Returns 10 + * B++; + * B.Peek(); // Returns 20 + * B++; + * B.Peek(); // Returns 25 + * B++; + * B.Peek(); // Returns 30 + * @endcode + */ + template + + class CircularLinkedList + { + + public: + + struct node + { + CircularLinkedListType item; + + node* previous; + node* next; + }; + + CircularLinkedList(); + ~CircularLinkedList(); + CircularLinkedList( const CircularLinkedList& original_copy ); + // CircularLinkedList(LinkedList original_copy) {CircularLinkedList(original_copy);} // Converts linked list to circular type + bool operator= ( const CircularLinkedList& original_copy ); + CircularLinkedList& operator++(); // CircularLinkedList A; ++A; + CircularLinkedList& operator++( int ); // Circular_Linked List A; A++; + CircularLinkedList& operator--(); // CircularLinkedList A; --A; + CircularLinkedList& operator--( int ); // Circular_Linked List A; A--; + bool IsIn( const CircularLinkedListType& input ); + bool Find( const CircularLinkedListType& input ); + void Insert( const CircularLinkedListType& input ); + + CircularLinkedListType& Add ( const CircularLinkedListType& input ) + + ; // Adds after the current position + void Replace( const CircularLinkedListType& input ); + + void Del( void ); + + unsigned int Size( void ); + + CircularLinkedListType& Peek( void ); + + CircularLinkedListType Pop( void ); + + void Clear( void ); + + void Sort( void ); + + void Beginning( void ); + + void End( void ); + + void Concatenate( const CircularLinkedList& L ); + + protected: + unsigned int list_size; + + node *root; + + node *position; + + node* FindPointer( const CircularLinkedListType& input ); + + private: + CircularLinkedList Merge( CircularLinkedList L1, CircularLinkedList L2 ); + + CircularLinkedList Mergesort( const CircularLinkedList& L ); + }; + + template + + class LinkedList : public CircularLinkedList + { + + public: + LinkedList() + {} + + LinkedList( const LinkedList& original_copy ); + ~LinkedList(); + bool operator= ( const LinkedList& original_copy ); + LinkedList& operator++(); // LinkedList A; ++A; + LinkedList& operator++( int ); // Linked List A; A++; + LinkedList& operator--(); // LinkedList A; --A; + LinkedList& operator--( int ); // Linked List A; A--; + + private: + LinkedList Merge( LinkedList L1, LinkedList L2 ); + LinkedList Mergesort( const LinkedList& L ); + + }; + + + template + inline void CircularLinkedList::Beginning( void ) + { + if ( this->root ) + this->position = this->root; + } + + template + inline void CircularLinkedList::End( void ) + { + if ( this->root ) + this->position = this->root->previous; + } + + template + bool LinkedList::operator= ( const LinkedList& original_copy ) + { + typename LinkedList::node * original_copy_pointer, *last, *save_position; + + if ( ( &original_copy ) != this ) + { + + this->Clear(); + + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + + + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = this->position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + + return true; + } + + + template + CircularLinkedList::CircularLinkedList() + { + this->root = 0; + this->position = 0; + this->list_size = 0; + } + + template + CircularLinkedList::~CircularLinkedList() + { + this->Clear(); + } + + template + LinkedList::~LinkedList() + { + this->Clear(); + } + + template + LinkedList::LinkedList( const LinkedList& original_copy ) + { + typename LinkedList::node * original_copy_pointer, *last, *save_position; + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + return ; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item) = *((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = this->position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + +#ifdef _MSC_VER +#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized +#endif + template + CircularLinkedList::CircularLinkedList( const CircularLinkedList& original_copy ) + { + node * original_copy_pointer; + node *last; + node *save_position; + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + return ; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item) = *((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + + + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = position; + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + +#ifdef _MSC_VER +#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized +#endif + template + bool CircularLinkedList::operator= ( const CircularLinkedList& original_copy ) + { + node * original_copy_pointer; + node *last; + node *save_position; + + if ( ( &original_copy ) != this ) + { + + this->Clear(); + + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = this->position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + + return true; + } + + template + void CircularLinkedList::Insert( const CircularLinkedListType& input ) + { + node * new_node; + + if ( list_size == 0 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + //*(root->item)=input; + this->root->item = input; + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + } + + else + if ( list_size == 1 ) + { + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->position; + this->root->previous = this->position; + this->position->previous = this->root; + this->position->next = this->root; + // *(position->item)=input; + this->position->item = input; + this->root = this->position; // Since we're inserting into a 1 element list the old root is now the second item + this->list_size = 2; + } + + else + { + /* + + B + | + A --- C + + position->previous=A + new_node=B + position=C + + Note that the order of the following statements is important */ + + new_node = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // new_node->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // *(new_node->item)=input; + new_node->item = input; + + // Point next of A to B + ( this->position->previous ) ->next = new_node; + + // Point last of B to A + new_node->previous = this->position->previous; + + // Point last of C to B + this->position->previous = new_node; + + // Point next of B to C + new_node->next = this->position; + + // Since the root pointer is bound to a node rather than an index this moves it back if you insert an element at the root + + if ( this->position == this->root ) + { + this->root = new_node; + this->position = this->root; + } + + // Increase the recorded size of the list by one + this->list_size++; + } + } + + template + CircularLinkedListType& CircularLinkedList::Add ( const CircularLinkedListType& input ) + { + node * new_node; + + if ( this->list_size == 0 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // *(root->item)=input; + this->root->item = input; + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // return *(position->item); + return this->position->item; + } + + else + if ( list_size == 1 ) + { + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->position; + this->root->previous = this->position; + this->position->previous = this->root; + this->position->next = this->root; + // *(position->item)=input; + this->position->item = input; + this->list_size = 2; + this->position = this->root; // Don't move the position from the root + // return *(position->item); + return this->position->item; + } + + else + { + /* + + B + | + A --- C + + new_node=B + position=A + position->next=C + + Note that the order of the following statements is important */ + + new_node = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // new_node->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // *(new_node->item)=input; + new_node->item = input; + + // Point last of B to A + new_node->previous = this->position; + + // Point next of B to C + new_node->next = ( this->position->next ); + + // Point last of C to B + ( this->position->next ) ->previous = new_node; + + // Point next of A to B + ( this->position->next ) = new_node; + + // Increase the recorded size of the list by one + this->list_size++; + + // return *(new_node->item); + return new_node->item; + } + } + + template + inline void CircularLinkedList::Replace( const CircularLinkedListType& input ) + { + if ( this->list_size > 0 ) + // *(position->item)=input; + this->position->item = input; + } + + template + void CircularLinkedList::Del() + { + node * new_position; + + if ( this->list_size == 0 ) + return ; + + else + if ( this->list_size == 1 ) + { + // RakNet::OP_DELETE(root->item, _FILE_AND_LINE_); + RakNet::OP_DELETE(this->root, _FILE_AND_LINE_); + this->root = this->position = 0; + this->list_size = 0; + } + + else + { + ( this->position->previous ) ->next = this->position->next; + ( this->position->next ) ->previous = this->position->previous; + new_position = this->position->next; + + if ( this->position == this->root ) + this->root = new_position; + + // RakNet::OP_DELETE(position->item, _FILE_AND_LINE_); + RakNet::OP_DELETE(this->position, _FILE_AND_LINE_); + + this->position = new_position; + + this->list_size--; + } + } + + template + bool CircularLinkedList::IsIn(const CircularLinkedListType& input ) + { + node * return_value, *old_position; + + old_position = this->position; + + return_value = FindPointer( input ); + this->position = old_position; + + if ( return_value != 0 ) + return true; + else + return false; // Can't find the item don't do anything + } + + template + bool CircularLinkedList::Find( const CircularLinkedListType& input ) + { + node * return_value; + + return_value = FindPointer( input ); + + if ( return_value != 0 ) + { + this->position = return_value; + return true; + } + + else + return false; // Can't find the item don't do anything + } + + template + typename CircularLinkedList::node* CircularLinkedList::FindPointer( const CircularLinkedListType& input ) + { + node * current; + + if ( this->list_size == 0 ) + return 0; + + current = this->root; + + // Search for the item starting from the root node and incrementing the pointer after every check + // If you wind up pointing at the root again you looped around the list so didn't find the item, in which case return 0 + do + { + // if (*(current->item) == input) return current; + + if ( current->item == input ) + return current; + + current = current->next; + } + + while ( current != this->root ); + + return 0; + + } + + template + inline unsigned int CircularLinkedList::Size( void ) + { + return this->list_size; + } + + template + inline CircularLinkedListType& CircularLinkedList::Peek( void ) + { + // return *(position->item); + return this->position->item; + } + + template + CircularLinkedListType CircularLinkedList::Pop( void ) + { + CircularLinkedListType element; + element = Peek(); + Del(); + return CircularLinkedListType( element ); // return temporary + } + + // Prefix + template + CircularLinkedList& CircularLinkedList::operator++() + { + if ( this->list_size != 0 ) + position = position->next; + + return *this; + } + + /* + // Postfix + template + CircularLinkedList& CircularLinkedList::operator++(int) + { + CircularLinkedList before; + before=*this; + operator++(); + return before; + } + */ + + template + CircularLinkedList& CircularLinkedList::operator++( int ) + { + return this->operator++(); + } + + // Prefix + template + CircularLinkedList& CircularLinkedList::operator--() + { + if ( this->list_size != 0 ) + this->position = this->position->previous; + + return *this; + } + + /* + // Postfix + template + CircularLinkedList& CircularLinkedList::operator--(int) + { + CircularLinkedList before; + before=*this; + operator--(); + return before; + } + */ + + template + CircularLinkedList& CircularLinkedList::operator--( int ) + { + return this->operator--(); + } + + template + void CircularLinkedList::Clear( void ) + { + if ( this->list_size == 0 ) + return ; + else + if ( this->list_size == 1 ) // {RakNet::OP_DELETE(root->item); RakNet::OP_DELETE(root, _FILE_AND_LINE_);} + { + RakNet::OP_DELETE(this->root, _FILE_AND_LINE_); + } + + else + { + node* current; + node* temp; + + current = this->root; + + do + { + temp = current; + current = current->next; + // RakNet::OP_DELETE(temp->item, _FILE_AND_LINE_); + RakNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + + while ( current != this->root ); + } + + this->list_size = 0; + this->root = 0; + this->position = 0; + } + + template + inline void CircularLinkedList::Concatenate( const CircularLinkedList& L ) + { + unsigned int counter; + node* ptr; + + if ( L.list_size == 0 ) + return ; + + if ( this->list_size == 0 ) + * this = L; + + ptr = L.root; + + this->position = this->root->previous; + + // Cycle through each element in L and add it to the current list + for ( counter = 0; counter < L.list_size; counter++ ) + { + // Add item after the current item pointed to + // add(*(ptr->item)); + + Add ( ptr->item ); + + // Update pointers. Moving ptr keeps the current pointer at the end of the list since the add function does not move the pointer + ptr = ptr->next; + + this->position = this->position->next; + } + } + + template + inline void CircularLinkedList::Sort( void ) + { + if ( this->list_size <= 1 ) + return ; + + // Call equal operator to assign result of mergesort to current object + *this = Mergesort( *this ); + + this->position = this->root; + } + + template + CircularLinkedList CircularLinkedList::Mergesort( const CircularLinkedList& L ) + { + unsigned int counter; + node* location; + CircularLinkedList L1; + CircularLinkedList L2; + + location = L.root; + + // Split the list into two equal size sublists, L1 and L2 + + for ( counter = 0; counter < L.list_size / 2; counter++ ) + { + // L1.add (*(location->item)); + L1.Add ( location->item ); + location = location->next; + } + + for ( ;counter < L.list_size; counter++ ) + { + // L2.Add(*(location->item)); + L2.Add ( location->item ); + location = location->next; + } + + // Recursively sort the sublists + if ( L1.list_size > 1 ) + L1 = Mergesort( L1 ); + + if ( L2.list_size > 1 ) + L2 = Mergesort( L2 ); + + // Merge the two sublists + return Merge( L1, L2 ); + } + + template + CircularLinkedList CircularLinkedList::Merge( CircularLinkedList L1, CircularLinkedList L2 ) + { + CircularLinkedList X; + CircularLinkedListType element; + L1.position = L1.root; + L2.position = L2.root; + + // While neither list is empty + + while ( ( L1.list_size != 0 ) && ( L2.list_size != 0 ) ) + { + // Compare the first items of L1 and L2 + // Remove the smaller of the two items from the list + + if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) + // if ((*((L1.root)->item)) < (*((L2.root)->item))) + { + // element = *((L1.root)->item); + element = ( L1.root ) ->item; + L1.Del(); + } + else + { + // element = *((L2.root)->item); + element = ( L2.root ) ->item; + L2.Del(); + } + + // Add this item to the end of X + X.Add( element ); + + X++; + } + + // Add the remaining list to X + if ( L1.list_size != 0 ) + X.Concatenate( L1 ); + else + X.Concatenate( L2 ); + + return X; + } + + template + LinkedList LinkedList::Mergesort( const LinkedList& L ) + { + unsigned int counter; + typename LinkedList::node* location; + LinkedList L1; + LinkedList L2; + + location = L.root; + + // Split the list into two equal size sublists, L1 and L2 + + for ( counter = 0; counter < L.LinkedList_size / 2; counter++ ) + { + // L1.add (*(location->item)); + L1.Add ( location->item ); + location = location->next; + } + + for ( ;counter < L.LinkedList_size; counter++ ) + { + // L2.Add(*(location->item)); + L2.Add ( location->item ); + location = location->next; + } + + // Recursively sort the sublists + if ( L1.list_size > 1 ) + L1 = Mergesort( L1 ); + + if ( L2.list_size > 1 ) + L2 = Mergesort( L2 ); + + // Merge the two sublists + return Merge( L1, L2 ); + } + + template + LinkedList LinkedList::Merge( LinkedList L1, LinkedList L2 ) + { + LinkedList X; + LinkedListType element; + L1.position = L1.root; + L2.position = L2.root; + + // While neither list is empty + + while ( ( L1.LinkedList_size != 0 ) && ( L2.LinkedList_size != 0 ) ) + { + // Compare the first items of L1 and L2 + // Remove the smaller of the two items from the list + + if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) + // if ((*((L1.root)->item)) < (*((L2.root)->item))) + { + element = ( L1.root ) ->item; + // element = *((L1.root)->item); + L1.Del(); + } + else + { + element = ( L2.root ) ->item; + // element = *((L2.root)->item); + L2.Del(); + } + + // Add this item to the end of X + X.Add( element ); + } + + // Add the remaining list to X + if ( L1.LinkedList_size != 0 ) + X.concatenate( L1 ); + else + X.concatenate( L2 ); + + return X; + } + + + // Prefix + template + LinkedList& LinkedList::operator++() + { + if ( ( this->list_size != 0 ) && ( this->position->next != this->root ) ) + this->position = this->position->next; + + return *this; + } + + /* + // Postfix + template + LinkedList& LinkedList::operator++(int) + { + LinkedList before; + before=*this; + operator++(); + return before; + } + */ + // Postfix + template + LinkedList& LinkedList::operator++( int ) + { + return this->operator++(); + } + + // Prefix + template + LinkedList& LinkedList::operator--() + { + if ( ( this->list_size != 0 ) && ( this->position != this->root ) ) + this->position = this->position->previous; + + return *this; + } + + /* + // Postfix + template + LinkedList& LinkedList::operator--(int) + { + LinkedList before; + before=*this; + operator--(); + return before; + } + */ + + // Postfix + template + LinkedList& LinkedList::operator--( int ) + { + return this->operator--(); + } + +} // End namespace + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_List.h b/project/lib_projects/raknet/jni/RaknetSources/DS_List.h index a360053..f99d411 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_List.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_List.h @@ -1,518 +1,518 @@ -/// \file DS_List.h -/// \internal -/// \brief Array based list. -/// \details Usually the Queue class is used instead, since it has all the same functionality and is only worse at random access. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __LIST_H -#define __LIST_H - -#include "RakAssert.h" -#include // memmove -#include "Export.h" -#include "RakMemoryOverride.h" - -/// Maximum unsigned long -static const unsigned int MAX_UNSIGNED_LONG = 4294967295U; - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// \brief Array based implementation of a list. - /// \note ONLY USE THIS FOR SHALLOW COPIES. I don't bother with operator= to improve performance. - template - class RAK_DLL_EXPORT List - { - public: - /// Default constructor - List(); - - // Destructor - ~List(); - - /// \brief Copy constructor. - /// \param[in] original_copy The list to duplicate - List( const List& original_copy ); - - /// \brief Assign one list to another. - List& operator= ( const List& original_copy ); - - /// \brief Access an element by its index in the array. - /// \param[in] position The index into the array. - /// \return The element at position \a position. - list_type& operator[] ( const unsigned int position ) const; - - /// \brief Access an element by its index in the array. - /// \param[in] position The index into the array. - /// \return The element at position \a position. - list_type& Get ( const unsigned int position ) const; - - /// \brief Push an element at the end of the stack. - /// \param[in] input The new element. - void Push(const list_type &input, const char *file, unsigned int line ); - - /// \brief Pop an element from the end of the stack. - /// \pre Size()>0 - /// \return The element at the end. - list_type& Pop(void); - - /// \brief Insert an element at position \a position in the list. - /// \param[in] input The new element. - /// \param[in] position The position of the new element. - void Insert( const list_type &input, const unsigned int position, const char *file, unsigned int line ); - - /// \brief Insert at the end of the list. - /// \param[in] input The new element. - void Insert( const list_type &input, const char *file, unsigned int line ); - - /// \brief Replace the value at \a position by \a input. - /// \details If the size of the list is less than @em position, it increase the capacity of - /// the list and fill slot with @em filler. - /// \param[in] input The element to replace at position @em position. - /// \param[in] filler The element use to fill new allocated capacity. - /// \param[in] position The position of input in the list. - void Replace( const list_type &input, const list_type filler, const unsigned int position, const char *file, unsigned int line ); - - /// \brief Replace the last element of the list by \a input. - /// \param[in] input The element used to replace the last element. - void Replace( const list_type &input ); - - /// \brief Delete the element at position \a position. - /// \param[in] position The index of the element to delete - void RemoveAtIndex( const unsigned int position ); - - /// \brief Delete the element at position \a position. - /// \note - swaps middle with end of list, only use if list order does not matter - /// \param[in] position The index of the element to delete - void RemoveAtIndexFast( const unsigned int position ); - - /// \brief Delete the element at the end of the list. - void RemoveFromEnd(const unsigned num=1); - - /// \brief Returns the index of the specified item or MAX_UNSIGNED_LONG if not found. - /// \param[in] input The element to check for - /// \return The index or position of @em input in the list. - /// \retval MAX_UNSIGNED_LONG The object is not in the list - /// \retval [Integer] The index of the element in the list - unsigned int GetIndexOf( const list_type &input ) const; - - /// \return The number of elements in the list - unsigned int Size( void ) const; - - /// \brief Clear the list - void Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line ); - - /// \brief Preallocate the list, so it needs fewer reallocations at runtime. - void Preallocate( unsigned countNeeded, const char *file, unsigned int line ); - - /// \brief Frees overallocated members, to use the minimum memory necessary. - /// \attention - /// This is a slow operation - void Compress( const char *file, unsigned int line ); - - private: - /// An array of user values - list_type* listArray; - - /// Number of elements in the list - unsigned int list_size; - - /// Size of \a array - unsigned int allocation_size; - }; - template - List::List() - { - allocation_size = 0; - listArray = 0; - list_size = 0; - } - - template - List::~List() - { - if (allocation_size>0) - RakNet::OP_DELETE_ARRAY(listArray, _FILE_AND_LINE_); - } - - - template - List::List( const List& original_copy ) - { - // Allocate memory for copy - - if ( original_copy.list_size == 0 ) - { - list_size = 0; - allocation_size = 0; - } - else - { - listArray = RakNet::OP_NEW_ARRAY( original_copy.list_size , _FILE_AND_LINE_ ); - - for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) - listArray[ counter ] = original_copy.listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); - - list_size = allocation_size = original_copy.list_size; - } - } - - template - List& List::operator= ( const List& original_copy ) - { - if ( ( &original_copy ) != this ) - { - Clear( false, _FILE_AND_LINE_ ); - - // Allocate memory for copy - - if ( original_copy.list_size == 0 ) - { - list_size = 0; - allocation_size = 0; - } - - else - { - listArray = RakNet::OP_NEW_ARRAY( original_copy.list_size , _FILE_AND_LINE_ ); - - for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) - listArray[ counter ] = original_copy.listArray[ counter ]; - // Don't call constructors, assignment operators, etc. - //memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); - - list_size = allocation_size = original_copy.list_size; - } - } - - return *this; - } - - - template - inline list_type& List::operator[] ( const unsigned int position ) const - { - #ifdef _DEBUG - if (position>=list_size) - { - RakAssert ( position < list_size ); - } - #endif - return listArray[ position ]; - } - - // Just here for debugging - template - inline list_type& List::Get ( const unsigned int position ) const - { - return listArray[ position ]; - } - - template - void List::Push(const list_type &input, const char *file, unsigned int line) - { - Insert(input, file, line); - } - - template - inline list_type& List::Pop(void) - { -#ifdef _DEBUG - RakAssert(list_size>0); -#endif - --list_size; - return listArray[list_size]; - } - - template - void List::Insert( const list_type &input, const unsigned int position, const char *file, unsigned int line ) - { -#ifdef _DEBUG - if (position>list_size) - { - RakAssert( position <= list_size ); - } -#endif - - // Reallocate list if necessary - if ( list_size == allocation_size ) - { - // allocate twice the currently allocated memory - list_type * new_array; - - if ( allocation_size == 0 ) - allocation_size = 16; - else - allocation_size *= 2; - - new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); - - // copy old array over - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated and twice as large array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - - listArray = new_array; - } - - // Move the elements in the list to make room - for ( unsigned int counter = list_size; counter != position; counter-- ) - listArray[ counter ] = listArray[ counter - 1 ]; - - // Don't call constructors, assignment operators, etc. - //memmove(listArray+position+1, listArray+position, (list_size-position)*sizeof(list_type)); - - // Insert the new item at the correct spot - listArray[ position ] = input; - - ++list_size; - - } - - - template - void List::Insert( const list_type &input, const char *file, unsigned int line ) - { - // Reallocate list if necessary - - if ( list_size == allocation_size ) - { - // allocate twice the currently allocated memory - list_type * new_array; - - if ( allocation_size == 0 ) - allocation_size = 16; - else - allocation_size *= 2; - - new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); - - if (listArray) - { - // copy old array over - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated and twice as large array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - } - - listArray = new_array; - } - - // Insert the new item at the correct spot - listArray[ list_size ] = input; - - ++list_size; - } - - template - inline void List::Replace( const list_type &input, const list_type filler, const unsigned int position, const char *file, unsigned int line ) - { - if ( ( list_size > 0 ) && ( position < list_size ) ) - { - // Direct replacement - listArray[ position ] = input; - } - else - { - if ( position >= allocation_size ) - { - // Reallocate the list to size position and fill in blanks with filler - list_type * new_array; - allocation_size = position + 1; - - new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); - - // copy old array over - - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - - listArray = new_array; - } - - // Fill in holes with filler - while ( list_size < position ) - listArray[ list_size++ ] = filler; - - // Fill in the last element with the new item - listArray[ list_size++ ] = input; - -#ifdef _DEBUG - - RakAssert( list_size == position + 1 ); - -#endif - - } - } - - template - inline void List::Replace( const list_type &input ) - { - if ( list_size > 0 ) - listArray[ list_size - 1 ] = input; - } - - template - void List::RemoveAtIndex( const unsigned int position ) - { -#ifdef _DEBUG - if (position >= list_size) - { - RakAssert( position < list_size ); - return; - } -#endif - - if ( position < list_size ) - { - // Compress the array - for ( unsigned int counter = position; counter < list_size - 1 ; ++counter ) - listArray[ counter ] = listArray[ counter + 1 ]; - // Don't call constructors, assignment operators, etc. - // memmove(listArray+position, listArray+position+1, (list_size-1-position) * sizeof(list_type)); - - RemoveFromEnd(); - } - } - - template - void List::RemoveAtIndexFast( const unsigned int position ) - { -#ifdef _DEBUG - if (position >= list_size) - { - RakAssert( position < list_size ); - return; - } -#endif - --list_size; - listArray[position]=listArray[list_size]; - } - - template - inline void List::RemoveFromEnd( const unsigned num ) - { - // Delete the last elements on the list. No compression needed -#ifdef _DEBUG - RakAssert(list_size>=num); -#endif - list_size-=num; - } - - template - unsigned int List::GetIndexOf( const list_type &input ) const - { - for ( unsigned int i = 0; i < list_size; ++i ) - if ( listArray[ i ] == input ) - return i; - - return MAX_UNSIGNED_LONG; - } - - template - inline unsigned int List::Size( void ) const - { - return list_size; - } - - template - void List::Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line ) - { - if ( allocation_size == 0 ) - return; - - if (allocation_size>512 || doNotDeallocateSmallBlocks==false) - { - RakNet::OP_DELETE_ARRAY(listArray, file, line); - allocation_size = 0; - listArray = 0; - } - list_size = 0; - } - - template - void List::Compress( const char *file, unsigned int line ) - { - list_type * new_array; - - if ( allocation_size == 0 ) - return ; - - new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); - - // copy old array over - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - - listArray = new_array; - } - - template - void List::Preallocate( unsigned countNeeded, const char *file, unsigned int line ) - { - unsigned amountToAllocate = allocation_size; - if (allocation_size==0) - amountToAllocate=16; - while (amountToAllocate < countNeeded) - amountToAllocate<<=1; - - if ( allocation_size < amountToAllocate) - { - // allocate twice the currently allocated memory - list_type * new_array; - - allocation_size=amountToAllocate; - - new_array = RakNet::OP_NEW_ARRAY< list_type >( allocation_size , file, line ); - - if (listArray) - { - // copy old array over - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated and twice as large array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - } - - listArray = new_array; - } - } - -} // End namespace - -#endif +/// \file DS_List.h +/// \internal +/// \brief Array based list. +/// \details Usually the Queue class is used instead, since it has all the same functionality and is only worse at random access. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __LIST_H +#define __LIST_H + +#include "RakAssert.h" +#include // memmove +#include "Export.h" +#include "RakMemoryOverride.h" + +/// Maximum unsigned long +static const unsigned int MAX_UNSIGNED_LONG = 4294967295U; + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief Array based implementation of a list. + /// \note ONLY USE THIS FOR SHALLOW COPIES. I don't bother with operator= to improve performance. + template + class RAK_DLL_EXPORT List + { + public: + /// Default constructor + List(); + + // Destructor + ~List(); + + /// \brief Copy constructor. + /// \param[in] original_copy The list to duplicate + List( const List& original_copy ); + + /// \brief Assign one list to another. + List& operator= ( const List& original_copy ); + + /// \brief Access an element by its index in the array. + /// \param[in] position The index into the array. + /// \return The element at position \a position. + list_type& operator[] ( const unsigned int position ) const; + + /// \brief Access an element by its index in the array. + /// \param[in] position The index into the array. + /// \return The element at position \a position. + list_type& Get ( const unsigned int position ) const; + + /// \brief Push an element at the end of the stack. + /// \param[in] input The new element. + void Push(const list_type &input, const char *file, unsigned int line ); + + /// \brief Pop an element from the end of the stack. + /// \pre Size()>0 + /// \return The element at the end. + list_type& Pop(void); + + /// \brief Insert an element at position \a position in the list. + /// \param[in] input The new element. + /// \param[in] position The position of the new element. + void Insert( const list_type &input, const unsigned int position, const char *file, unsigned int line ); + + /// \brief Insert at the end of the list. + /// \param[in] input The new element. + void Insert( const list_type &input, const char *file, unsigned int line ); + + /// \brief Replace the value at \a position by \a input. + /// \details If the size of the list is less than @em position, it increase the capacity of + /// the list and fill slot with @em filler. + /// \param[in] input The element to replace at position @em position. + /// \param[in] filler The element use to fill new allocated capacity. + /// \param[in] position The position of input in the list. + void Replace( const list_type &input, const list_type filler, const unsigned int position, const char *file, unsigned int line ); + + /// \brief Replace the last element of the list by \a input. + /// \param[in] input The element used to replace the last element. + void Replace( const list_type &input ); + + /// \brief Delete the element at position \a position. + /// \param[in] position The index of the element to delete + void RemoveAtIndex( const unsigned int position ); + + /// \brief Delete the element at position \a position. + /// \note - swaps middle with end of list, only use if list order does not matter + /// \param[in] position The index of the element to delete + void RemoveAtIndexFast( const unsigned int position ); + + /// \brief Delete the element at the end of the list. + void RemoveFromEnd(const unsigned num=1); + + /// \brief Returns the index of the specified item or MAX_UNSIGNED_LONG if not found. + /// \param[in] input The element to check for + /// \return The index or position of @em input in the list. + /// \retval MAX_UNSIGNED_LONG The object is not in the list + /// \retval [Integer] The index of the element in the list + unsigned int GetIndexOf( const list_type &input ) const; + + /// \return The number of elements in the list + unsigned int Size( void ) const; + + /// \brief Clear the list + void Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line ); + + /// \brief Preallocate the list, so it needs fewer reallocations at runtime. + void Preallocate( unsigned countNeeded, const char *file, unsigned int line ); + + /// \brief Frees overallocated members, to use the minimum memory necessary. + /// \attention + /// This is a slow operation + void Compress( const char *file, unsigned int line ); + + private: + /// An array of user values + list_type* listArray; + + /// Number of elements in the list + unsigned int list_size; + + /// Size of \a array + unsigned int allocation_size; + }; + template + List::List() + { + allocation_size = 0; + listArray = 0; + list_size = 0; + } + + template + List::~List() + { + if (allocation_size>0) + RakNet::OP_DELETE_ARRAY(listArray, _FILE_AND_LINE_); + } + + + template + List::List( const List& original_copy ) + { + // Allocate memory for copy + + if ( original_copy.list_size == 0 ) + { + list_size = 0; + allocation_size = 0; + } + else + { + listArray = RakNet::OP_NEW_ARRAY( original_copy.list_size , _FILE_AND_LINE_ ); + + for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) + listArray[ counter ] = original_copy.listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); + + list_size = allocation_size = original_copy.list_size; + } + } + + template + List& List::operator= ( const List& original_copy ) + { + if ( ( &original_copy ) != this ) + { + Clear( false, _FILE_AND_LINE_ ); + + // Allocate memory for copy + + if ( original_copy.list_size == 0 ) + { + list_size = 0; + allocation_size = 0; + } + + else + { + listArray = RakNet::OP_NEW_ARRAY( original_copy.list_size , _FILE_AND_LINE_ ); + + for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) + listArray[ counter ] = original_copy.listArray[ counter ]; + // Don't call constructors, assignment operators, etc. + //memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); + + list_size = allocation_size = original_copy.list_size; + } + } + + return *this; + } + + + template + inline list_type& List::operator[] ( const unsigned int position ) const + { + #ifdef _DEBUG + if (position>=list_size) + { + RakAssert ( position < list_size ); + } + #endif + return listArray[ position ]; + } + + // Just here for debugging + template + inline list_type& List::Get ( const unsigned int position ) const + { + return listArray[ position ]; + } + + template + void List::Push(const list_type &input, const char *file, unsigned int line) + { + Insert(input, file, line); + } + + template + inline list_type& List::Pop(void) + { +#ifdef _DEBUG + RakAssert(list_size>0); +#endif + --list_size; + return listArray[list_size]; + } + + template + void List::Insert( const list_type &input, const unsigned int position, const char *file, unsigned int line ) + { +#ifdef _DEBUG + if (position>list_size) + { + RakAssert( position <= list_size ); + } +#endif + + // Reallocate list if necessary + if ( list_size == allocation_size ) + { + // allocate twice the currently allocated memory + list_type * new_array; + + if ( allocation_size == 0 ) + allocation_size = 16; + else + allocation_size *= 2; + + new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); + + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated and twice as large array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + + listArray = new_array; + } + + // Move the elements in the list to make room + for ( unsigned int counter = list_size; counter != position; counter-- ) + listArray[ counter ] = listArray[ counter - 1 ]; + + // Don't call constructors, assignment operators, etc. + //memmove(listArray+position+1, listArray+position, (list_size-position)*sizeof(list_type)); + + // Insert the new item at the correct spot + listArray[ position ] = input; + + ++list_size; + + } + + + template + void List::Insert( const list_type &input, const char *file, unsigned int line ) + { + // Reallocate list if necessary + + if ( list_size == allocation_size ) + { + // allocate twice the currently allocated memory + list_type * new_array; + + if ( allocation_size == 0 ) + allocation_size = 16; + else + allocation_size *= 2; + + new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); + + if (listArray) + { + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated and twice as large array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + } + + listArray = new_array; + } + + // Insert the new item at the correct spot + listArray[ list_size ] = input; + + ++list_size; + } + + template + inline void List::Replace( const list_type &input, const list_type filler, const unsigned int position, const char *file, unsigned int line ) + { + if ( ( list_size > 0 ) && ( position < list_size ) ) + { + // Direct replacement + listArray[ position ] = input; + } + else + { + if ( position >= allocation_size ) + { + // Reallocate the list to size position and fill in blanks with filler + list_type * new_array; + allocation_size = position + 1; + + new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); + + // copy old array over + + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + + listArray = new_array; + } + + // Fill in holes with filler + while ( list_size < position ) + listArray[ list_size++ ] = filler; + + // Fill in the last element with the new item + listArray[ list_size++ ] = input; + +#ifdef _DEBUG + + RakAssert( list_size == position + 1 ); + +#endif + + } + } + + template + inline void List::Replace( const list_type &input ) + { + if ( list_size > 0 ) + listArray[ list_size - 1 ] = input; + } + + template + void List::RemoveAtIndex( const unsigned int position ) + { +#ifdef _DEBUG + if (position >= list_size) + { + RakAssert( position < list_size ); + return; + } +#endif + + if ( position < list_size ) + { + // Compress the array + for ( unsigned int counter = position; counter < list_size - 1 ; ++counter ) + listArray[ counter ] = listArray[ counter + 1 ]; + // Don't call constructors, assignment operators, etc. + // memmove(listArray+position, listArray+position+1, (list_size-1-position) * sizeof(list_type)); + + RemoveFromEnd(); + } + } + + template + void List::RemoveAtIndexFast( const unsigned int position ) + { +#ifdef _DEBUG + if (position >= list_size) + { + RakAssert( position < list_size ); + return; + } +#endif + --list_size; + listArray[position]=listArray[list_size]; + } + + template + inline void List::RemoveFromEnd( const unsigned num ) + { + // Delete the last elements on the list. No compression needed +#ifdef _DEBUG + RakAssert(list_size>=num); +#endif + list_size-=num; + } + + template + unsigned int List::GetIndexOf( const list_type &input ) const + { + for ( unsigned int i = 0; i < list_size; ++i ) + if ( listArray[ i ] == input ) + return i; + + return MAX_UNSIGNED_LONG; + } + + template + inline unsigned int List::Size( void ) const + { + return list_size; + } + + template + void List::Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line ) + { + if ( allocation_size == 0 ) + return; + + if (allocation_size>512 || doNotDeallocateSmallBlocks==false) + { + RakNet::OP_DELETE_ARRAY(listArray, file, line); + allocation_size = 0; + listArray = 0; + } + list_size = 0; + } + + template + void List::Compress( const char *file, unsigned int line ) + { + list_type * new_array; + + if ( allocation_size == 0 ) + return ; + + new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); + + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + + listArray = new_array; + } + + template + void List::Preallocate( unsigned countNeeded, const char *file, unsigned int line ) + { + unsigned amountToAllocate = allocation_size; + if (allocation_size==0) + amountToAllocate=16; + while (amountToAllocate < countNeeded) + amountToAllocate<<=1; + + if ( allocation_size < amountToAllocate) + { + // allocate twice the currently allocated memory + list_type * new_array; + + allocation_size=amountToAllocate; + + new_array = RakNet::OP_NEW_ARRAY< list_type >( allocation_size , file, line ); + + if (listArray) + { + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated and twice as large array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + } + + listArray = new_array; + } + } + +} // End namespace + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_Map.h b/project/lib_projects/raknet/jni/RaknetSources/DS_Map.h index 2e789da..7905de1 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_Map.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_Map.h @@ -1,321 +1,321 @@ -/// \file DS_Map.h -/// \internal -/// \brief Map -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAKNET_MAP_H -#define __RAKNET_MAP_H - -#include "DS_OrderedList.h" -#include "Export.h" -#include "RakMemoryOverride.h" -#include "RakAssert.h" - -// If I want to change this to a red-black tree, this is a good site: http://www.cs.auckland.ac.nz/software/AlgAnim/red_black.html -// This makes insertions and deletions faster. But then traversals are slow, while they are currently fast. - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// The default comparison has to be first so it can be called as a default parameter. - /// It then is followed by MapNode, followed by NodeComparisonFunc - template - int defaultMapKeyComparison(const key_type &a, const key_type &b) - { - if (a > - class RAK_DLL_EXPORT Map - { - public: - static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(key_type(),key_type());} - - struct MapNode - { - MapNode() {} - MapNode(key_type _key, data_type _data) : mapNodeKey(_key), mapNodeData(_data) {} - MapNode& operator = ( const MapNode& input ) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData; return *this;} - MapNode( const MapNode & input) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData;} - key_type mapNodeKey; - data_type mapNodeData; - }; - - // Has to be a static because the comparison callback for DataStructures::OrderedList is a C function - static int NodeComparisonFunc(const key_type &a, const MapNode &b) - { -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - return key_comparison_func(a, b.mapNodeKey); - } - - Map(); - ~Map(); - Map( const Map& original_copy ); - Map& operator= ( const Map& original_copy ); - - data_type& Get(const key_type &key) const; - data_type Pop(const key_type &key); - // Add if needed - void Set(const key_type &key, const data_type &data); - // Must already exist - void SetExisting(const key_type &key, const data_type &data); - // Must add - void SetNew(const key_type &key, const data_type &data); - bool Has(const key_type &key) const; - bool Delete(const key_type &key); - data_type& operator[] ( const unsigned int position ) const; - key_type GetKeyAtIndex( const unsigned int position ) const; - unsigned GetIndexAtKey( const key_type &key ); - void RemoveAtIndex(const unsigned index); - void Clear(void); - unsigned Size(void) const; - - protected: - DataStructures::OrderedList< key_type,MapNode,&Map::NodeComparisonFunc > mapNodeList; - - void SaveLastSearch(const key_type &key, unsigned index) const; - bool HasSavedSearchResult(const key_type &key) const; - - unsigned lastSearchIndex; - key_type lastSearchKey; - bool lastSearchIndexValid; - }; - - template - Map::Map() - { - lastSearchIndexValid=false; - } - - template - Map::~Map() - { - Clear(); - } - - template - Map::Map( const Map& original_copy ) - { - mapNodeList=original_copy.mapNodeList; - lastSearchIndex=original_copy.lastSearchIndex; - lastSearchKey=original_copy.lastSearchKey; - lastSearchIndexValid=original_copy.lastSearchIndexValid; - } - - template - Map& Map::operator= ( const Map& original_copy ) - { - mapNodeList=original_copy.mapNodeList; - lastSearchIndex=original_copy.lastSearchIndex; - lastSearchKey=original_copy.lastSearchKey; - lastSearchIndexValid=original_copy.lastSearchIndexValid; - return *this; - } - - template - data_type& Map::Get(const key_type &key) const - { - if (HasSavedSearchResult(key)) - return mapNodeList[lastSearchIndex].mapNodeData; - - bool objectExists; - unsigned index; - index=mapNodeList.GetIndexFromKey(key, &objectExists); - RakAssert(objectExists); - SaveLastSearch(key,index); - return mapNodeList[index].mapNodeData; - } - - template - unsigned Map::GetIndexAtKey( const key_type &key ) - { - if (HasSavedSearchResult(key)) - return lastSearchIndex; - - bool objectExists; - unsigned index; - index=mapNodeList.GetIndexFromKey(key, &objectExists); - if (objectExists==false) - { - RakAssert(objectExists); - } - SaveLastSearch(key,index); - return index; - } - - template - void Map::RemoveAtIndex(const unsigned index) - { - mapNodeList.RemoveAtIndex(index); - lastSearchIndexValid=false; - } - - template - data_type Map::Pop(const key_type &key) - { - bool objectExists; - unsigned index; - if (HasSavedSearchResult(key)) - index=lastSearchIndex; - else - { - index=mapNodeList.GetIndexFromKey(key, &objectExists); - RakAssert(objectExists); - } - data_type tmp = mapNodeList[index].mapNodeData; - mapNodeList.RemoveAtIndex(index); - lastSearchIndexValid=false; - return tmp; - } - - template - void Map::Set(const key_type &key, const data_type &data) - { - bool objectExists; - unsigned index; - - if (HasSavedSearchResult(key)) - { - mapNodeList[lastSearchIndex].mapNodeData=data; - return; - } - - index=mapNodeList.GetIndexFromKey(key, &objectExists); - - if (objectExists) - { - SaveLastSearch(key,index); - mapNodeList[index].mapNodeData=data; - } - else - { - SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_)); - } - } - - template - void Map::SetExisting(const key_type &key, const data_type &data) - { - bool objectExists; - unsigned index; - - if (HasSavedSearchResult(key)) - { - index=lastSearchIndex; - } - else - { - index=mapNodeList.GetIndexFromKey(key, &objectExists); - RakAssert(objectExists); - SaveLastSearch(key,index); - } - - mapNodeList[index].mapNodeData=data; - } - - template - void Map::SetNew(const key_type &key, const data_type &data) - { -#ifdef _DEBUG - bool objectExists; - mapNodeList.GetIndexFromKey(key, &objectExists); - RakAssert(objectExists==false); -#endif - SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_)); - } - - template - bool Map::Has(const key_type &key) const - { - if (HasSavedSearchResult(key)) - return true; - - bool objectExists; - unsigned index; - index=mapNodeList.GetIndexFromKey(key, &objectExists); - if (objectExists) - SaveLastSearch(key,index); - return objectExists; - } - - template - bool Map::Delete(const key_type &key) - { - if (HasSavedSearchResult(key)) - { - lastSearchIndexValid=false; - mapNodeList.RemoveAtIndex(lastSearchIndex); - return true; - } - - bool objectExists; - unsigned index; - index=mapNodeList.GetIndexFromKey(key, &objectExists); - if (objectExists) - { - lastSearchIndexValid=false; - mapNodeList.RemoveAtIndex(index); - return true; - } - else - return false; - } - - template - void Map::Clear(void) - { - lastSearchIndexValid=false; - mapNodeList.Clear(false, _FILE_AND_LINE_); - } - - template - data_type& Map::operator[]( const unsigned int position ) const - { - return mapNodeList[position].mapNodeData; - } - - template - key_type Map::GetKeyAtIndex( const unsigned int position ) const - { - return mapNodeList[position].mapNodeKey; - } - - template - unsigned Map::Size(void) const - { - return mapNodeList.Size(); - } - - template - void Map::SaveLastSearch(const key_type &key, const unsigned index) const - { - (void) key; - (void) index; - - /* - lastSearchIndex=index; - lastSearchKey=key; - lastSearchIndexValid=true; - */ - } - - template - bool Map::HasSavedSearchResult(const key_type &key) const - { - (void) key; - - // Not threadsafe! - return false; - // return lastSearchIndexValid && key_comparison_func(key,lastSearchKey)==0; - } -} - -#endif +/// \file DS_Map.h +/// \internal +/// \brief Map +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAKNET_MAP_H +#define __RAKNET_MAP_H + +#include "DS_OrderedList.h" +#include "Export.h" +#include "RakMemoryOverride.h" +#include "RakAssert.h" + +// If I want to change this to a red-black tree, this is a good site: http://www.cs.auckland.ac.nz/software/AlgAnim/red_black.html +// This makes insertions and deletions faster. But then traversals are slow, while they are currently fast. + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// The default comparison has to be first so it can be called as a default parameter. + /// It then is followed by MapNode, followed by NodeComparisonFunc + template + int defaultMapKeyComparison(const key_type &a, const key_type &b) + { + if (a > + class RAK_DLL_EXPORT Map + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(key_type(),key_type());} + + struct MapNode + { + MapNode() {} + MapNode(key_type _key, data_type _data) : mapNodeKey(_key), mapNodeData(_data) {} + MapNode& operator = ( const MapNode& input ) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData; return *this;} + MapNode( const MapNode & input) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData;} + key_type mapNodeKey; + data_type mapNodeData; + }; + + // Has to be a static because the comparison callback for DataStructures::OrderedList is a C function + static int NodeComparisonFunc(const key_type &a, const MapNode &b) + { +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + return key_comparison_func(a, b.mapNodeKey); + } + + Map(); + ~Map(); + Map( const Map& original_copy ); + Map& operator= ( const Map& original_copy ); + + data_type& Get(const key_type &key) const; + data_type Pop(const key_type &key); + // Add if needed + void Set(const key_type &key, const data_type &data); + // Must already exist + void SetExisting(const key_type &key, const data_type &data); + // Must add + void SetNew(const key_type &key, const data_type &data); + bool Has(const key_type &key) const; + bool Delete(const key_type &key); + data_type& operator[] ( const unsigned int position ) const; + key_type GetKeyAtIndex( const unsigned int position ) const; + unsigned GetIndexAtKey( const key_type &key ); + void RemoveAtIndex(const unsigned index); + void Clear(void); + unsigned Size(void) const; + + protected: + DataStructures::OrderedList< key_type,MapNode,&Map::NodeComparisonFunc > mapNodeList; + + void SaveLastSearch(const key_type &key, unsigned index) const; + bool HasSavedSearchResult(const key_type &key) const; + + unsigned lastSearchIndex; + key_type lastSearchKey; + bool lastSearchIndexValid; + }; + + template + Map::Map() + { + lastSearchIndexValid=false; + } + + template + Map::~Map() + { + Clear(); + } + + template + Map::Map( const Map& original_copy ) + { + mapNodeList=original_copy.mapNodeList; + lastSearchIndex=original_copy.lastSearchIndex; + lastSearchKey=original_copy.lastSearchKey; + lastSearchIndexValid=original_copy.lastSearchIndexValid; + } + + template + Map& Map::operator= ( const Map& original_copy ) + { + mapNodeList=original_copy.mapNodeList; + lastSearchIndex=original_copy.lastSearchIndex; + lastSearchKey=original_copy.lastSearchKey; + lastSearchIndexValid=original_copy.lastSearchIndexValid; + return *this; + } + + template + data_type& Map::Get(const key_type &key) const + { + if (HasSavedSearchResult(key)) + return mapNodeList[lastSearchIndex].mapNodeData; + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists); + SaveLastSearch(key,index); + return mapNodeList[index].mapNodeData; + } + + template + unsigned Map::GetIndexAtKey( const key_type &key ) + { + if (HasSavedSearchResult(key)) + return lastSearchIndex; + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + if (objectExists==false) + { + RakAssert(objectExists); + } + SaveLastSearch(key,index); + return index; + } + + template + void Map::RemoveAtIndex(const unsigned index) + { + mapNodeList.RemoveAtIndex(index); + lastSearchIndexValid=false; + } + + template + data_type Map::Pop(const key_type &key) + { + bool objectExists; + unsigned index; + if (HasSavedSearchResult(key)) + index=lastSearchIndex; + else + { + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists); + } + data_type tmp = mapNodeList[index].mapNodeData; + mapNodeList.RemoveAtIndex(index); + lastSearchIndexValid=false; + return tmp; + } + + template + void Map::Set(const key_type &key, const data_type &data) + { + bool objectExists; + unsigned index; + + if (HasSavedSearchResult(key)) + { + mapNodeList[lastSearchIndex].mapNodeData=data; + return; + } + + index=mapNodeList.GetIndexFromKey(key, &objectExists); + + if (objectExists) + { + SaveLastSearch(key,index); + mapNodeList[index].mapNodeData=data; + } + else + { + SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_)); + } + } + + template + void Map::SetExisting(const key_type &key, const data_type &data) + { + bool objectExists; + unsigned index; + + if (HasSavedSearchResult(key)) + { + index=lastSearchIndex; + } + else + { + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists); + SaveLastSearch(key,index); + } + + mapNodeList[index].mapNodeData=data; + } + + template + void Map::SetNew(const key_type &key, const data_type &data) + { +#ifdef _DEBUG + bool objectExists; + mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists==false); +#endif + SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_)); + } + + template + bool Map::Has(const key_type &key) const + { + if (HasSavedSearchResult(key)) + return true; + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + if (objectExists) + SaveLastSearch(key,index); + return objectExists; + } + + template + bool Map::Delete(const key_type &key) + { + if (HasSavedSearchResult(key)) + { + lastSearchIndexValid=false; + mapNodeList.RemoveAtIndex(lastSearchIndex); + return true; + } + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + if (objectExists) + { + lastSearchIndexValid=false; + mapNodeList.RemoveAtIndex(index); + return true; + } + else + return false; + } + + template + void Map::Clear(void) + { + lastSearchIndexValid=false; + mapNodeList.Clear(false, _FILE_AND_LINE_); + } + + template + data_type& Map::operator[]( const unsigned int position ) const + { + return mapNodeList[position].mapNodeData; + } + + template + key_type Map::GetKeyAtIndex( const unsigned int position ) const + { + return mapNodeList[position].mapNodeKey; + } + + template + unsigned Map::Size(void) const + { + return mapNodeList.Size(); + } + + template + void Map::SaveLastSearch(const key_type &key, const unsigned index) const + { + (void) key; + (void) index; + + /* + lastSearchIndex=index; + lastSearchKey=key; + lastSearchIndexValid=true; + */ + } + + template + bool Map::HasSavedSearchResult(const key_type &key) const + { + (void) key; + + // Not threadsafe! + return false; + // return lastSearchIndexValid && key_comparison_func(key,lastSearchKey)==0; + } +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_MemoryPool.h b/project/lib_projects/raknet/jni/RaknetSources/DS_MemoryPool.h index 62ebe8b..903fd8e 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_MemoryPool.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_MemoryPool.h @@ -1,294 +1,294 @@ -/// \file DS_MemoryPool.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __MEMORY_POOL_H -#define __MEMORY_POOL_H - -#ifndef __APPLE__ -// Use stdlib and not malloc for compatibility -#include -#endif -#include "RakAssert.h" -#include "Export.h" - -#include "RakMemoryOverride.h" - -// DS_MEMORY_POOL_MAX_FREE_PAGES must be > 1 -#define DS_MEMORY_POOL_MAX_FREE_PAGES 4 - -//#define _DISABLE_MEMORY_POOL - -namespace DataStructures -{ - /// Very fast memory pool for allocating and deallocating structures that don't have constructors or destructors. - /// Contains a list of pages, each of which has an array of the user structures - template - class RAK_DLL_EXPORT MemoryPool - { - public: - struct Page; - struct MemoryWithPage - { - MemoryBlockType userMemory; - Page *parentPage; - }; - struct Page - { - MemoryWithPage** availableStack; - int availableStackSize; - MemoryWithPage* block; - Page *next, *prev; - }; - - MemoryPool(); - ~MemoryPool(); - void SetPageSize(int size); // Defaults to 16384 bytes - MemoryBlockType *Allocate(const char *file, unsigned int line); - void Release(MemoryBlockType *m, const char *file, unsigned int line); - void Clear(const char *file, unsigned int line); - - int GetAvailablePagesSize(void) const {return availablePagesSize;} - int GetUnavailablePagesSize(void) const {return unavailablePagesSize;} - int GetMemoryPoolPageSize(void) const {return memoryPoolPageSize;} - protected: - int BlocksPerPage(void) const; - void AllocateFirst(void); - bool InitPage(Page *page, Page *prev, const char *file, unsigned int line); - - // availablePages contains pages which have room to give the user new blocks. We return these blocks from the head of the list - // unavailablePages are pages which are totally full, and from which we do not return new blocks. - // Pages move from the head of unavailablePages to the tail of availablePages, and from the head of availablePages to the tail of unavailablePages - Page *availablePages, *unavailablePages; - int availablePagesSize, unavailablePagesSize; - int memoryPoolPageSize; - }; - - template - MemoryPool::MemoryPool() - { -#ifndef _DISABLE_MEMORY_POOL - //AllocateFirst(); - availablePagesSize=0; - unavailablePagesSize=0; - memoryPoolPageSize=16384; -#endif - } - template - MemoryPool::~MemoryPool() - { -#ifndef _DISABLE_MEMORY_POOL - Clear(_FILE_AND_LINE_); -#endif - } - - template - void MemoryPool::SetPageSize(int size) - { - memoryPoolPageSize=size; - } - - template - MemoryBlockType* MemoryPool::Allocate(const char *file, unsigned int line) - { -#ifdef _DISABLE_MEMORY_POOL - return (MemoryBlockType*) rakMalloc_Ex(sizeof(MemoryBlockType), file, line); -#else - - if (availablePagesSize>0) - { - MemoryBlockType *retVal; - Page *curPage; - curPage=availablePages; - retVal = (MemoryBlockType*) curPage->availableStack[--(curPage->availableStackSize)]; - if (curPage->availableStackSize==0) - { - --availablePagesSize; - availablePages=curPage->next; - RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); - curPage->next->prev=curPage->prev; - curPage->prev->next=curPage->next; - - if (unavailablePagesSize++==0) - { - unavailablePages=curPage; - curPage->next=curPage; - curPage->prev=curPage; - } - else - { - curPage->next=unavailablePages; - curPage->prev=unavailablePages->prev; - unavailablePages->prev->next=curPage; - unavailablePages->prev=curPage; - } - } - - RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); - return retVal; - } - - availablePages = (Page *) rakMalloc_Ex(sizeof(Page), file, line); - if (availablePages==0) - return 0; - availablePagesSize=1; - if (InitPage(availablePages, availablePages, file, line)==false) - return 0; - // If this assert hits, we couldn't allocate even 1 block per page. Increase the page size - RakAssert(availablePages->availableStackSize>1); - - return (MemoryBlockType *) availablePages->availableStack[--availablePages->availableStackSize]; -#endif - } - template - void MemoryPool::Release(MemoryBlockType *m, const char *file, unsigned int line) - { -#ifdef _DISABLE_MEMORY_POOL - rakFree_Ex(m, file, line); - return; -#else - // Find the page this block is in and return it. - Page *curPage; - MemoryWithPage *memoryWithPage = (MemoryWithPage*)m; - curPage=memoryWithPage->parentPage; - - if (curPage->availableStackSize==0) - { - // The page is in the unavailable list so move it to the available list - curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; - unavailablePagesSize--; - - // As this page is no longer totally empty, move it to the end of available pages - curPage->next->prev=curPage->prev; - curPage->prev->next=curPage->next; - - if (unavailablePagesSize>0 && curPage==unavailablePages) - unavailablePages=unavailablePages->next; - - if (availablePagesSize++==0) - { - availablePages=curPage; - curPage->next=curPage; - curPage->prev=curPage; - } - else - { - curPage->next=availablePages; - curPage->prev=availablePages->prev; - availablePages->prev->next=curPage; - availablePages->prev=curPage; - } - } - else - { - curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; - - if (curPage->availableStackSize==BlocksPerPage() && - availablePagesSize>=DS_MEMORY_POOL_MAX_FREE_PAGES) - { - // After a certain point, just deallocate empty pages rather than keep them around - if (curPage==availablePages) - { - availablePages=curPage->next; - RakAssert(availablePages->availableStackSize>0); - } - curPage->prev->next=curPage->next; - curPage->next->prev=curPage->prev; - availablePagesSize--; - rakFree_Ex(curPage->availableStack, file, line ); - rakFree_Ex(curPage->block, file, line ); - rakFree_Ex(curPage, file, line ); - } - } -#endif - } - template - void MemoryPool::Clear(const char *file, unsigned int line) - { -#ifdef _DISABLE_MEMORY_POOL - return; -#else - Page *cur, *freed; - - if (availablePagesSize>0) - { - cur = availablePages; -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - while (true) - // do - { - rakFree_Ex(cur->availableStack, file, line ); - rakFree_Ex(cur->block, file, line ); - freed=cur; - cur=cur->next; - if (cur==availablePages) - { - rakFree_Ex(freed, file, line ); - break; - } - rakFree_Ex(freed, file, line ); - }// while(cur!=availablePages); - } - - if (unavailablePagesSize>0) - { - cur = unavailablePages; - while (1) - //do - { - rakFree_Ex(cur->availableStack, file, line ); - rakFree_Ex(cur->block, file, line ); - freed=cur; - cur=cur->next; - if (cur==unavailablePages) - { - rakFree_Ex(freed, file, line ); - break; - } - rakFree_Ex(freed, file, line ); - } // while(cur!=unavailablePages); - } - - availablePagesSize=0; - unavailablePagesSize=0; -#endif - } - template - int MemoryPool::BlocksPerPage(void) const - { - return memoryPoolPageSize / sizeof(MemoryWithPage); - } - template - bool MemoryPool::InitPage(Page *page, Page *prev, const char *file, unsigned int line) - { - int i=0; - const int bpp = BlocksPerPage(); - page->block=(MemoryWithPage*) rakMalloc_Ex(memoryPoolPageSize, file, line); - if (page->block==0) - return false; - page->availableStack=(MemoryWithPage**)rakMalloc_Ex(sizeof(MemoryWithPage*)*bpp, file, line); - if (page->availableStack==0) - { - rakFree_Ex(page->block, file, line ); - return false; - } - MemoryWithPage *curBlock = page->block; - MemoryWithPage **curStack = page->availableStack; - while (i < bpp) - { - curBlock->parentPage=page; - curStack[i]=curBlock++; - i++; - } - page->availableStackSize=bpp; - page->next=availablePages; - page->prev=prev; - return true; - } -} - -#endif +/// \file DS_MemoryPool.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __MEMORY_POOL_H +#define __MEMORY_POOL_H + +#ifndef __APPLE__ +// Use stdlib and not malloc for compatibility +#include +#endif +#include "RakAssert.h" +#include "Export.h" + +#include "RakMemoryOverride.h" + +// DS_MEMORY_POOL_MAX_FREE_PAGES must be > 1 +#define DS_MEMORY_POOL_MAX_FREE_PAGES 4 + +//#define _DISABLE_MEMORY_POOL + +namespace DataStructures +{ + /// Very fast memory pool for allocating and deallocating structures that don't have constructors or destructors. + /// Contains a list of pages, each of which has an array of the user structures + template + class RAK_DLL_EXPORT MemoryPool + { + public: + struct Page; + struct MemoryWithPage + { + MemoryBlockType userMemory; + Page *parentPage; + }; + struct Page + { + MemoryWithPage** availableStack; + int availableStackSize; + MemoryWithPage* block; + Page *next, *prev; + }; + + MemoryPool(); + ~MemoryPool(); + void SetPageSize(int size); // Defaults to 16384 bytes + MemoryBlockType *Allocate(const char *file, unsigned int line); + void Release(MemoryBlockType *m, const char *file, unsigned int line); + void Clear(const char *file, unsigned int line); + + int GetAvailablePagesSize(void) const {return availablePagesSize;} + int GetUnavailablePagesSize(void) const {return unavailablePagesSize;} + int GetMemoryPoolPageSize(void) const {return memoryPoolPageSize;} + protected: + int BlocksPerPage(void) const; + void AllocateFirst(void); + bool InitPage(Page *page, Page *prev, const char *file, unsigned int line); + + // availablePages contains pages which have room to give the user new blocks. We return these blocks from the head of the list + // unavailablePages are pages which are totally full, and from which we do not return new blocks. + // Pages move from the head of unavailablePages to the tail of availablePages, and from the head of availablePages to the tail of unavailablePages + Page *availablePages, *unavailablePages; + int availablePagesSize, unavailablePagesSize; + int memoryPoolPageSize; + }; + + template + MemoryPool::MemoryPool() + { +#ifndef _DISABLE_MEMORY_POOL + //AllocateFirst(); + availablePagesSize=0; + unavailablePagesSize=0; + memoryPoolPageSize=16384; +#endif + } + template + MemoryPool::~MemoryPool() + { +#ifndef _DISABLE_MEMORY_POOL + Clear(_FILE_AND_LINE_); +#endif + } + + template + void MemoryPool::SetPageSize(int size) + { + memoryPoolPageSize=size; + } + + template + MemoryBlockType* MemoryPool::Allocate(const char *file, unsigned int line) + { +#ifdef _DISABLE_MEMORY_POOL + return (MemoryBlockType*) rakMalloc_Ex(sizeof(MemoryBlockType), file, line); +#else + + if (availablePagesSize>0) + { + MemoryBlockType *retVal; + Page *curPage; + curPage=availablePages; + retVal = (MemoryBlockType*) curPage->availableStack[--(curPage->availableStackSize)]; + if (curPage->availableStackSize==0) + { + --availablePagesSize; + availablePages=curPage->next; + RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); + curPage->next->prev=curPage->prev; + curPage->prev->next=curPage->next; + + if (unavailablePagesSize++==0) + { + unavailablePages=curPage; + curPage->next=curPage; + curPage->prev=curPage; + } + else + { + curPage->next=unavailablePages; + curPage->prev=unavailablePages->prev; + unavailablePages->prev->next=curPage; + unavailablePages->prev=curPage; + } + } + + RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); + return retVal; + } + + availablePages = (Page *) rakMalloc_Ex(sizeof(Page), file, line); + if (availablePages==0) + return 0; + availablePagesSize=1; + if (InitPage(availablePages, availablePages, file, line)==false) + return 0; + // If this assert hits, we couldn't allocate even 1 block per page. Increase the page size + RakAssert(availablePages->availableStackSize>1); + + return (MemoryBlockType *) availablePages->availableStack[--availablePages->availableStackSize]; +#endif + } + template + void MemoryPool::Release(MemoryBlockType *m, const char *file, unsigned int line) + { +#ifdef _DISABLE_MEMORY_POOL + rakFree_Ex(m, file, line); + return; +#else + // Find the page this block is in and return it. + Page *curPage; + MemoryWithPage *memoryWithPage = (MemoryWithPage*)m; + curPage=memoryWithPage->parentPage; + + if (curPage->availableStackSize==0) + { + // The page is in the unavailable list so move it to the available list + curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; + unavailablePagesSize--; + + // As this page is no longer totally empty, move it to the end of available pages + curPage->next->prev=curPage->prev; + curPage->prev->next=curPage->next; + + if (unavailablePagesSize>0 && curPage==unavailablePages) + unavailablePages=unavailablePages->next; + + if (availablePagesSize++==0) + { + availablePages=curPage; + curPage->next=curPage; + curPage->prev=curPage; + } + else + { + curPage->next=availablePages; + curPage->prev=availablePages->prev; + availablePages->prev->next=curPage; + availablePages->prev=curPage; + } + } + else + { + curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; + + if (curPage->availableStackSize==BlocksPerPage() && + availablePagesSize>=DS_MEMORY_POOL_MAX_FREE_PAGES) + { + // After a certain point, just deallocate empty pages rather than keep them around + if (curPage==availablePages) + { + availablePages=curPage->next; + RakAssert(availablePages->availableStackSize>0); + } + curPage->prev->next=curPage->next; + curPage->next->prev=curPage->prev; + availablePagesSize--; + rakFree_Ex(curPage->availableStack, file, line ); + rakFree_Ex(curPage->block, file, line ); + rakFree_Ex(curPage, file, line ); + } + } +#endif + } + template + void MemoryPool::Clear(const char *file, unsigned int line) + { +#ifdef _DISABLE_MEMORY_POOL + return; +#else + Page *cur, *freed; + + if (availablePagesSize>0) + { + cur = availablePages; +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + while (true) + // do + { + rakFree_Ex(cur->availableStack, file, line ); + rakFree_Ex(cur->block, file, line ); + freed=cur; + cur=cur->next; + if (cur==availablePages) + { + rakFree_Ex(freed, file, line ); + break; + } + rakFree_Ex(freed, file, line ); + }// while(cur!=availablePages); + } + + if (unavailablePagesSize>0) + { + cur = unavailablePages; + while (1) + //do + { + rakFree_Ex(cur->availableStack, file, line ); + rakFree_Ex(cur->block, file, line ); + freed=cur; + cur=cur->next; + if (cur==unavailablePages) + { + rakFree_Ex(freed, file, line ); + break; + } + rakFree_Ex(freed, file, line ); + } // while(cur!=unavailablePages); + } + + availablePagesSize=0; + unavailablePagesSize=0; +#endif + } + template + int MemoryPool::BlocksPerPage(void) const + { + return memoryPoolPageSize / sizeof(MemoryWithPage); + } + template + bool MemoryPool::InitPage(Page *page, Page *prev, const char *file, unsigned int line) + { + int i=0; + const int bpp = BlocksPerPage(); + page->block=(MemoryWithPage*) rakMalloc_Ex(memoryPoolPageSize, file, line); + if (page->block==0) + return false; + page->availableStack=(MemoryWithPage**)rakMalloc_Ex(sizeof(MemoryWithPage*)*bpp, file, line); + if (page->availableStack==0) + { + rakFree_Ex(page->block, file, line ); + return false; + } + MemoryWithPage *curBlock = page->block; + MemoryWithPage **curStack = page->availableStack; + while (i < bpp) + { + curBlock->parentPage=page; + curStack[i]=curBlock++; + i++; + } + page->availableStackSize=bpp; + page->next=availablePages; + page->prev=prev; + return true; + } +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_Multilist.h b/project/lib_projects/raknet/jni/RaknetSources/DS_Multilist.h index e6acdb8..370ff8d 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_Multilist.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_Multilist.h @@ -1,1644 +1,1644 @@ -/// \file DS_Multilist.h -/// \internal -/// \brief ADT that can represent an unordered list, ordered list, stack, or queue with a common interface -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __MULTILIST_H -#define __MULTILIST_H - -#include "RakAssert.h" -#include // memmove -#include "Export.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" - - -#ifdef _MSC_VER -#pragma warning( push ) -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#pragma warning( disable : 4512 ) // warning C4512: assignment operator could not be generated -#endif - -/// What algorithm to use to store the data for the Multilist -enum MultilistType -{ - /// Removing from the middle of the list will swap the end of the list rather than shift the elements. Push and Pop operate on the tail. - ML_UNORDERED_LIST, - /// A normal list, with the list order preserved. Push and Pop operate on the tail. - ML_STACK, - /// A queue. Push and Pop operate on the head - ML_QUEUE, - /// A list that is always kept in order. Elements must be unique, and compare against each other consistently using <, ==, and > - ML_ORDERED_LIST, - /// A list whose type can change at runtime - ML_VARIABLE_DURING_RUNTIME -}; - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// Can be used with Multilist::ForEach - /// Assuming the Multilist holds pointers, will delete those pointers - template - void DeletePtr_RakNet(templateType &ptr, const char *file, unsigned int line ) {RakNet::OP_DELETE(ptr, file, line);} - - /// Can be used with Multilist::ForEach - /// Assuming the Multilist holds pointers, will delete those pointers - template - void DeletePtr(templateType &ptr) {delete ptr;} - - /// The following is invalid. - /// bool operator<( const MyClass *myClass, const int &inputKey ) {return myClass->value < inputKey;} - /// At least one type has to be a reference to a class - /// MLKeyRef is a helper class to turn a native type into a class, so you can compare that native type against a pointer to a different class - /// Used for he Multilist, when _DataType != _KeyType - template < class templateType > - class MLKeyRef - { - public: - MLKeyRef(const templateType& input) : val(input) {} - const templateType &Get(void) const {return val;} - bool operator<( const templateType &right ) {return val < right;} - bool operator>( const templateType &right ) {return val > right;} - bool operator==( const templateType &right ) {return val == right;} - protected: - const templateType &val; - }; - - /// For the Multilist, when _DataType != _KeyType, you must define the comparison operators between the key and the data - /// This is non-trivial due to the need to use MLKeyRef in case the type held is a pointer to a structure or class and the key type is not a class - /// For convenience, this macro will implement the comparison operators under the following conditions - /// 1. _DataType is a pointer to a class or structure - /// 2. The key is a member variable of _DataType - #define DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS( _CLASS_NAME_, _KEY_TYPE_, _MEMBER_VARIABLE_NAME_ ) \ - bool operator<( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() < cls->_MEMBER_VARIABLE_NAME_;} \ - bool operator>( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() > cls->_MEMBER_VARIABLE_NAME_;} \ - bool operator==( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() == cls->_MEMBER_VARIABLE_NAME_;} - - typedef uint32_t DefaultIndexType; - - /// \brief The multilist, representing an abstract data type that generally holds lists. - /// \param[in] _MultilistType What type of list this is, \sa MultilistType - /// \param[in] _DataType What type of data this list holds. - /// \param[in] _KeyType If a function takes a key to sort on, what type of key this is. The comparison operator between _DataType and _KeyType must be defined - /// \param[in] _IndexType What variable type to use for indices - template - class RAK_DLL_EXPORT Multilist - { - public: - Multilist(); - ~Multilist(); - Multilist( const Multilist& source ); - Multilist& operator= ( const Multilist& source ); - _DataType& operator[] ( const _IndexType position ) const; - /// Unordered list, stack is LIFO - /// QUEUE is FIFO - /// Ordered list is inserted in order - void Push(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ ); - void Push(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Gets or removes and gets an element from the list, according to the same rules as Push(). - /// Ordered list is LIFO for the purposes of Pop and Peek. - _DataType &Pop(const char *file=__FILE__, unsigned int line=__LINE__); - _DataType &Peek(void) const; - - /// \brief Same as Push(), except FIFO and LIFO are reversed. - /// Ordered list still inserts in order. - void PushOpposite(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ ); - void PushOpposite(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Same as Pop() and Peek(), except FIFO and LIFO are reversed. - _DataType &PopOpposite(const char *file=__FILE__, unsigned int line=__LINE__); - _DataType &PeekOpposite(void) const; - - /// \brief Stack,Queue: Inserts at index indicated, elements are shifted. - /// Ordered list: Inserts, position is ignored - void InsertAtIndex(const _DataType &d, _IndexType index, const char *file=__FILE__, unsigned int line=__LINE__); - - /// \brief Unordered list, removes at index indicated, swaps last element with that element. - /// Otherwise, array is shifted left to overwrite removed element - /// \details Index[0] returns the same as Pop() for a queue. - /// Same as PopOpposite() for the list and ordered list - void RemoveAtIndex(_IndexType position, const char *file=__FILE__, unsigned int line=__LINE__); - - /// \brief Find the index of \a key, and remove at that index. - bool RemoveAtKey(_KeyType key, bool assertIfDoesNotExist, const char *file=__FILE__, unsigned int line=__LINE__); - - /// \brief Finds the index of \a key. Return -1 if the key is not found. - _IndexType GetIndexOf(_KeyType key) const; - - /// \brief Returns where in the list we should insert the item, to preserve list order. - /// Returns -1 if the item is already in the list - _IndexType GetInsertionIndex(_KeyType key) const; - - /// \brief Finds the index of \a key. Return 0 if the key is not found. Useful if _DataType is always non-zero pointers. - _DataType GetPtr(_KeyType key) const; - - /// \brief Iterate over the list, calling the function pointer on each element. - void ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line); - void ForEach(void (*func)(_DataType &item)); - - /// \brief Returns if the list is empty. - bool IsEmpty(void) const; - - /// \brief Returns the number of elements used in the list. - _IndexType GetSize(void) const; - - /// \brief Empties the list. The list is not deallocated if it is small, - /// unless \a deallocateSmallBlocks is true - void Clear( bool deallocateSmallBlocks=true, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Empties the list, first calling RakNet::OP_Delete on all items. - /// \details The list is not deallocated if it is small, unless \a deallocateSmallBlocks is true - void ClearPointers( bool deallocateSmallBlocks=true, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Empty one item from the list, first calling RakNet::OP_Delete on that item. - void ClearPointer( _KeyType key, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Reverses the elements in the list, and flips the sort order - /// returned by GetSortOrder() if IsSorted() returns true at the time the function is called - void ReverseList(void); - - /// \brief Reallocates the list to a larger size. - /// If \a size is smaller than the value returned by GetSize(), the call does nothing. - void Reallocate(_IndexType size, const char *file=__FILE__, unsigned int line=__LINE__); - - /// \brief Sorts the list unless it is an ordered list, in which it does nothing as the list is assumed to already be sorted. - /// \details However, if \a force is true, it will also resort the ordered list, useful if the comparison operator between _KeyType and _DataType would now return different results - /// Once the list is sorted, further operations to lookup by key will be log2(n) until the list is modified - void Sort(bool force); - - /// \brief Sets the list to be remembered as sorted. - /// \details Optimization if the source is sorted already - void TagSorted(void); - - /// \brief Defaults to ascending. - /// \details Used by Sort(), and by ML_ORDERED_LIST - void SetSortOrder(bool ascending); - - /// \brief Returns true if ascending. - bool GetSortOrder(void) const; - - /// \brief Returns true if the list is currently believed to be in a sorted state. - /// \details Doesn't actually check for sortedness, just if Sort() - /// was recently called, or MultilistType is ML_ORDERED_LIST - bool IsSorted(void) const; - - /// Returns what type of list this is - MultilistType GetMultilistType(void) const; - - /// \brief Changes what type of list this is. - /// \pre Template must be defined with ML_VARIABLE_DURING_RUNTIME for this to do anything - /// \param[in] mlType Any value of the enum MultilistType, except ML_VARIABLE_DURING_RUNTIME - void SetMultilistType(MultilistType newType); - - /// \brief Returns the intersection of two lists. - /// Intersection is items common to both lists. - static void FindIntersection( - Multilist& source1, - Multilist& source2, - Multilist& intersection, - Multilist& uniqueToSource1, - Multilist& uniqueToSource2); - - protected: - void ReallocateIfNeeded(const char *file, unsigned int line); - void DeallocateIfNeeded(const char *file, unsigned int line); - void ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line); - void ReverseListInternal(void); - void InsertInOrderedList(const _DataType &d, const _KeyType &key); - _IndexType GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const; - void InsertShiftArrayRight(const _DataType &d, _IndexType index); - void DeleteShiftArrayLeft(_IndexType index); - void QSortAscending(_IndexType left, _IndexType right); - void QSortDescending(_IndexType left, _IndexType right); - void CopySource( const Multilist& source ); - - /// An array of user values - _DataType* data; - - /// Number of elements in the list - _IndexType dataSize; - - /// Size of \a array - _IndexType allocationSize; - - /// Array index for the head of the queue - _IndexType queueHead; - - /// Array index for the tail of the queue - _IndexType queueTail; - - /// How many bytes the user chose to preallocate - /// Won't automatically deallocate below this - _IndexType preallocationSize; - - enum - { - ML_UNSORTED, - ML_SORTED_ASCENDING, - ML_SORTED_DESCENDING - } sortState; - - bool ascendingSort; - - // In case we are using the variable type multilist - MultilistType variableMultilistType; - }; - - template - Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist() - { - data=0; - dataSize=0; - allocationSize=0; - ascendingSort=true; - sortState=ML_UNSORTED; - queueHead=0; - queueTail=0; - preallocationSize=0; - - if (_MultilistType==ML_ORDERED_LIST) - sortState=ML_SORTED_ASCENDING; - else - sortState=ML_UNSORTED; - - if (_MultilistType==ML_VARIABLE_DURING_RUNTIME) - variableMultilistType=ML_UNORDERED_LIST; - } - - template - Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::~Multilist() - { - if (data!=0) - RakNet::OP_DELETE_ARRAY(data, _FILE_AND_LINE_); - } - - template - Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist( const Multilist& source ) - { - CopySource(source); - } - - template - Multilist<_MultilistType, _DataType, _KeyType, _IndexType>& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator= ( const Multilist& source ) - { - Clear(true); - CopySource(source); - return *this; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::CopySource( const Multilist& source ) - { - dataSize=source.GetSize(); - ascendingSort=source.ascendingSort; - sortState=source.sortState; - queueHead=0; - queueTail=dataSize; - preallocationSize=source.preallocationSize; - variableMultilistType=source.variableMultilistType; - if (source.data==0) - { - data=0; - allocationSize=0; - } - else - { - allocationSize=dataSize; - data = RakNet::OP_NEW_ARRAY<_DataType>(dataSize,_FILE_AND_LINE_); - _IndexType i; - for (i=0; i < dataSize; i++) - data[i]=source[i]; - } - } - - template - _DataType& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator[] ( const _IndexType position ) const - { - RakAssert(position= allocationSize ) - return data[ queueHead + position - allocationSize ]; - else - return data[ queueHead + position ]; - } - - return data[position]; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const char *file, unsigned int line ) - { - Push(d,d,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const _KeyType &key, const char *file, unsigned int line ) - { - ReallocateIfNeeded(file,line); - - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) - { - data[dataSize]=d; - dataSize++; - } - else if (GetMultilistType()==ML_QUEUE) - { - data[queueTail++] = d; - - if ( queueTail == allocationSize ) - queueTail = 0; - dataSize++; - } - else - { - RakAssert(GetMultilistType()==ML_ORDERED_LIST); - InsertInOrderedList(d,key); - } - - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE) - { - // Break sort if no longer sorted - if (sortState!=ML_UNSORTED && dataSize>1) - { - if (ascendingSort) - { - if ( MLKeyRef<_KeyType>(key) < operator[](dataSize-2) ) - sortState=ML_UNSORTED; - } - else - { - if ( MLKeyRef<_KeyType>(key) > operator[](dataSize-2) ) - sortState=ML_UNSORTED; - } - - sortState=ML_UNSORTED; - } - } - } - - template - _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Pop(const char *file, unsigned int line) - { - RakAssert(IsEmpty()==false); - DeallocateIfNeeded(file,line); - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - dataSize--; - return data[dataSize]; - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - - if ( ++queueHead == allocationSize ) - queueHead = 0; - - if ( queueHead == 0 ) - return data[ allocationSize -1 ]; - - dataSize--; - return data[ queueHead -1 ]; - } - } - - template - _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Peek(void) const - { - RakAssert(IsEmpty()==false); - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - return data[dataSize-1]; - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - return data[ queueHead ]; - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const char *file, unsigned int line ) - { - PushOpposite(d,d,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const _KeyType &key, const char *file, unsigned int line ) - { - ReallocateIfNeeded(file,line); - - // Unordered list Push at back - if (GetMultilistType()==ML_UNORDERED_LIST) - { - data[dataSize]=d; - dataSize++; - } - else if (GetMultilistType()==ML_STACK) - { - // Stack push at front of the list, instead of back as normal - InsertAtIndex(d,0,file,line); - } - else if (GetMultilistType()==ML_QUEUE) - { - // Queue push at front of the list, instead of back as normal - InsertAtIndex(d,0,file,line); - } - else - { - RakAssert(GetMultilistType()==ML_ORDERED_LIST); - InsertInOrderedList(d,key); - } - - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE) - { - // Break sort if no longer sorted - if (sortState!=ML_UNSORTED && dataSize>1) - { - if (ascendingSort) - { - if ( MLKeyRef<_KeyType>(key) > operator[](1) ) - sortState=ML_UNSORTED; - } - else - { - if ( MLKeyRef<_KeyType>(key) < operator[](1) ) - sortState=ML_UNSORTED; - } - } - } - } - - template - _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PopOpposite(const char *file, unsigned int line) - { - RakAssert(IsEmpty()==false); - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - // Copy leftmost to end - ReallocateIfNeeded(file,line); - data[dataSize]=data[0]; - DeleteShiftArrayLeft(0); - --dataSize; - // Assuming still leaves at least one element past the end of the list allocated - DeallocateIfNeeded(file,line); - // Return end - return data[dataSize+1]; - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - // Deallocate first, since we are returning off the existing list - DeallocateIfNeeded(file,line); - dataSize--; - - if (queueTail==0) - queueTail=allocationSize-1; - else - --queueTail; - - return data[queueTail]; - } - } - - template - _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PeekOpposite(void) const - { - RakAssert(IsEmpty()==false); - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - return data[0]; - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - _IndexType priorIndex; - if (queueTail==0) - priorIndex=allocationSize-1; - else - priorIndex=queueTail-1; - - return data[priorIndex]; - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertAtIndex(const _DataType &d, _IndexType index, const char *file, unsigned int line) - { - ReallocateIfNeeded(file,line); - - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - if (index>=dataSize) - { - // insert at end - data[dataSize]=d; - - dataSize++; - } - else - { - // insert at index - InsertShiftArrayRight(d,index); - } - } - else - { - data[queueTail++] = d; - - if ( queueTail == allocationSize ) - queueTail = 0; - - ++dataSize; - - if (dataSize==1) - return; - - _IndexType writeIndex, readIndex, trueWriteIndex, trueReadIndex; - writeIndex=dataSize-1; - readIndex=writeIndex-1; - while (readIndex >= index) - { - if ( queueHead + writeIndex >= allocationSize ) - trueWriteIndex = queueHead + writeIndex - allocationSize; - else - trueWriteIndex = queueHead + writeIndex; - - if ( queueHead + readIndex >= allocationSize ) - trueReadIndex = queueHead + readIndex - allocationSize; - else - trueReadIndex = queueHead + readIndex; - - data[trueWriteIndex]=data[trueReadIndex]; - - if (readIndex==0) - break; - writeIndex--; - readIndex--; - } - - if ( queueHead + index >= allocationSize ) - trueWriteIndex = queueHead + index - allocationSize; - else - trueWriteIndex = queueHead + index; - - data[trueWriteIndex]=d; - } - - if (_MultilistType!=ML_ORDERED_LIST) - sortState=ML_UNSORTED; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtIndex(_IndexType position, const char *file, unsigned int line) - { - RakAssert(position < dataSize); - RakAssert(IsEmpty()==false); - - if (GetMultilistType()==ML_UNORDERED_LIST) - { - // Copy tail to current - data[position]=data[dataSize-1]; - } - else if (GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - DeleteShiftArrayLeft(position); - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - - _IndexType index, next; - - if ( queueHead + position >= allocationSize ) - index = queueHead + position - allocationSize; - else - index = queueHead + position; - - next = index + 1; - - if ( next == allocationSize ) - next = 0; - - while ( next != queueTail ) - { - // Overwrite the previous element - data[ index ] = data[ next ]; - index = next; - //next = (next + 1) % allocationSize; - - if ( ++next == allocationSize ) - next = 0; - } - - // Move the queueTail back - if ( queueTail == 0 ) - queueTail = allocationSize - 1; - else - --queueTail; - } - - - dataSize--; - DeallocateIfNeeded(file,line); - } - - template - bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtKey(_KeyType key, bool assertIfDoesNotExist, const char *file, unsigned int line) - { - _IndexType index = GetIndexOf(key); - if (index==(_IndexType)-1) - { - RakAssert(assertIfDoesNotExist==false && "RemoveAtKey element not found"); - return false; - } - RemoveAtIndex(index,file,line); - return true; - } - - template - _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexOf(_KeyType key) const - { - _IndexType i; - if (IsSorted()) - { - bool objectExists; - i=GetIndexFromKeyInSortedList(key, &objectExists); - if (objectExists) - return i; - return (_IndexType)-1; - } - else if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) - { - for (i=0; i < dataSize; i++) - { - if (MLKeyRef<_KeyType>(key)==data[i]) - return i; - } - return (_IndexType)-1; - } - else - { - RakAssert( GetMultilistType()==ML_QUEUE ); - - for (i=0; i < dataSize; i++) - { - if (MLKeyRef<_KeyType>(key)==operator[](i)) - return i; - } - return (_IndexType)-1; - } - } - - template - _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetInsertionIndex(_KeyType key) const - { - _IndexType i; - if (IsSorted()) - { - bool objectExists; - i=GetIndexFromKeyInSortedList(key, &objectExists); - if (objectExists) - return (_IndexType)-1; - return i; - } - else if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) - { - for (i=0; i < dataSize; i++) - { - if (MLKeyRef<_KeyType>(key)==data[i]) - return (_IndexType)-1; - } - return dataSize; - } - else - { - RakAssert( GetMultilistType()==ML_QUEUE ); - - for (i=0; i < dataSize; i++) - { - if (MLKeyRef<_KeyType>(key)==operator[](i)) - return (_IndexType)-1; - } - return dataSize; - } - } - - template - _DataType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetPtr(_KeyType key) const - { - _IndexType i = GetIndexOf(key); - if (i==(_IndexType)-1) - return 0; - return data[i]; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line) - { - _IndexType i; - for (i=0; i < dataSize; i++) - func(operator[](i), file, line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item)) - { - _IndexType i; - for (i=0; i < dataSize; i++) - func(operator[](i)); - } - - template - bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsEmpty(void) const - { - return dataSize==0; - } - - template - _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSize(void) const - { - return dataSize; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Clear( bool deallocateSmallBlocks, const char *file, unsigned int line ) - { - dataSize=0; - if (GetMultilistType()==ML_ORDERED_LIST) - if (ascendingSort) - sortState=ML_SORTED_ASCENDING; - else - sortState=ML_SORTED_DESCENDING; - else - sortState=ML_UNSORTED; - queueHead=0; - queueTail=0; - - if (deallocateSmallBlocks && allocationSize < 128 && data) - { - RakNet::OP_DELETE_ARRAY(data,file,line); - data=0; - allocationSize=0; - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ClearPointers( bool deallocateSmallBlocks, const char *file, unsigned int line ) - { - _IndexType i; - for (i=0; i < dataSize; i++) - RakNet::OP_DELETE(operator[](i), file, line); - Clear(deallocateSmallBlocks, file, line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ClearPointer( _KeyType key, const char *file, unsigned int line ) - { - _IndexType i; - i = GetIndexOf(key); - if (i!=-1) - { - RakNet::OP_DELETE(operator[](i), file, line); - RemoveAtIndex(i); - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseList(void) - { - if (IsSorted()) - ascendingSort=!ascendingSort; - - ReverseListInternal(); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Reallocate(_IndexType size, const char *file, unsigned int line) - { - _IndexType newAllocationSize; - if (size < dataSize) - newAllocationSize=dataSize; - else - newAllocationSize=size; - preallocationSize=size; - ReallocToSize(newAllocationSize,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Sort(bool force) - { - if (IsSorted() && force==false) - return; - - if (dataSize>1) - { - if (ascendingSort) - QSortAscending(0,dataSize-1); - else - QSortDescending(0,dataSize-1); - } - - TagSorted(); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::TagSorted(void) - { - if (ascendingSort) - sortState=ML_SORTED_ASCENDING; - else - sortState=ML_SORTED_DESCENDING; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortAscending(_IndexType leftEdge, _IndexType rightEdge) - { - _DataType temp; - _IndexType left=leftEdge; - _IndexType right=rightEdge; - _IndexType pivotIndex=left++; - - while (left data[pivotIndex]) - { - --left; - - data[pivotIndex]=data[left]; - data[left]=temp; - } - else - { - data[pivotIndex]=data[left]; - data[left]=temp; - - --left; - } - - if (left!=leftEdge) - QSortAscending(leftEdge, left); - - if (right!=rightEdge) - QSortAscending(right, rightEdge); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortDescending(_IndexType leftEdge, _IndexType rightEdge) - { - _DataType temp; - _IndexType left=leftEdge; - _IndexType right=rightEdge; - _IndexType pivotIndex=left++; - - while (left= data[pivotIndex]) - { - ++left; - } - else - { - temp=data[left]; - data[left]=data[right]; - data[right]=temp; - --right; - } - } - - temp=data[pivotIndex]; - - // Move pivot to center - if (data[left] < data[pivotIndex]) - { - --left; - - data[pivotIndex]=data[left]; - data[left]=temp; - } - else - { - data[pivotIndex]=data[left]; - data[left]=temp; - - --left; - } - - if (left!=leftEdge) - QSortDescending(leftEdge, left); - - if (right!=rightEdge) - QSortDescending(right, rightEdge); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetSortOrder(bool ascending) - { - if (ascendingSort!=ascending && IsSorted()) - { - ascendingSort=ascending; - // List is sorted, and the sort order has changed. So reverse the list - ReverseListInternal(); - } - else - ascendingSort=ascending; - } - - template - bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSortOrder(void) const - { - return ascendingSort; - } - - template - bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsSorted(void) const - { - return GetMultilistType()==ML_ORDERED_LIST || sortState!=ML_UNSORTED; - } - - template - MultilistType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetMultilistType(void) const - { - if (_MultilistType==ML_VARIABLE_DURING_RUNTIME) - return variableMultilistType; - return _MultilistType; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetMultilistType(MultilistType newType) - { - RakAssert(_MultilistType==ML_VARIABLE_DURING_RUNTIME); - switch (variableMultilistType) - { - case ML_UNORDERED_LIST: - switch (newType) - { - case ML_UNORDERED_LIST: - // No change - break; - case ML_STACK: - // Same data format - break; - case ML_QUEUE: - queueHead=0; - queueTail=dataSize; - break; - case ML_ORDERED_LIST: - Sort(false); - break; - } - break; - case ML_STACK: - switch (newType) - { - case ML_UNORDERED_LIST: - // Same data format - break; - case ML_STACK: - // No change - break; - case ML_QUEUE: - queueHead=0; - queueTail=dataSize; - break; - case ML_ORDERED_LIST: - Sort(false); - break; - } - break; - case ML_QUEUE: - switch (newType) - { - case ML_UNORDERED_LIST: - case ML_STACK: - case ML_ORDERED_LIST: - if (queueTail < queueHead) - { - // Realign data if wrapped - ReallocToSize(dataSize, _FILE_AND_LINE_); - } - else - { - // Else can just copy starting at head - _IndexType i; - for (i=0; i < dataSize; i++) - data[i]=operator[](i); - } - if (newType==ML_ORDERED_LIST) - Sort(false); - break; - case ML_QUEUE: - // No change - break; - } - break; - case ML_ORDERED_LIST: - switch (newType) - { - case ML_UNORDERED_LIST: - case ML_STACK: - case ML_QUEUE: - // Same data format - // Tag as sorted - if (ascendingSort) - sortState=ML_SORTED_ASCENDING; - else - sortState=ML_SORTED_DESCENDING; - if (newType==ML_QUEUE) - { - queueHead=0; - queueTail=dataSize; - } - break; - case ML_ORDERED_LIST: - // No change - break; - } - break; - } - - variableMultilistType=newType; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::FindIntersection( - Multilist& source1, - Multilist& source2, - Multilist& intersection, - Multilist& uniqueToSource1, - Multilist& uniqueToSource2) - { - _IndexType index1=0, index2=0; - source1.SetSortOrder(true); - source2.SetSortOrder(true); - source1.Sort(false); - source2.Sort(false); - intersection.Clear(true,_FILE_AND_LINE_); - uniqueToSource1.Clear(true,_FILE_AND_LINE_); - uniqueToSource2.Clear(true,_FILE_AND_LINE_); - - while (index1 < source1.GetSize() && index2 < source2.GetSize()) - { - if (source1[index1] - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocateIfNeeded(const char *file, unsigned int line) - { - if (dataSize65536) - newAllocationSize=allocationSize+65536; - else - { - newAllocationSize=allocationSize<<1; // * 2 - // Protect against underflow - if (newAllocationSize < allocationSize) - newAllocationSize=allocationSize+65536; - } - - ReallocToSize(newAllocationSize,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeallocateIfNeeded(const char *file, unsigned int line) - { - if (allocationSize<512) - return; - if (dataSize >= allocationSize/3 ) - return; - if (dataSize <= preallocationSize ) - return; - - _IndexType newAllocationSize = dataSize<<1; // * 2 - - ReallocToSize(newAllocationSize,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line) - { - _DataType* newData = RakNet::OP_NEW_ARRAY<_DataType>(newAllocationSize,file,line); - _IndexType i; - for (i=0; i < dataSize; i++) - newData[i]=operator[](i); - if (dataSize>0) - { - RakNet::OP_DELETE_ARRAY(data,file,line); - if (GetMultilistType()==ML_QUEUE) - { - queueHead=0; - queueTail=dataSize; - } - } - data=newData; - allocationSize=newAllocationSize; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseListInternal(void) - { - _DataType temp; - _IndexType i; - for (i=0; i < dataSize/2; i++) - { - temp=operator[](i); - operator[](i)=operator[](dataSize-1-i); - operator[](dataSize-1-i)=temp; - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertInOrderedList(const _DataType &d, const _KeyType &key) - { - RakAssert(GetMultilistType()==ML_ORDERED_LIST); - - bool objectExists; - _IndexType index; - index = GetIndexFromKeyInSortedList(key, &objectExists); - - // if (objectExists) - // { - // Ordered list only allows unique insertions - // RakAssert("Duplicate insertion into ordered list" && false); - // return; - // } - - if (index>=dataSize) - { - // insert at end - data[dataSize]=d; - dataSize++; - } - else - { - // insert at index - InsertShiftArrayRight(d,index); - } - } - - template - _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const - { - RakAssert(IsSorted()); - _IndexType index, upperBound, lowerBound; - - if (dataSize==0) - { - *objectExists=false; - return 0; - } - - upperBound=dataSize-1; - lowerBound=0; - index = dataSize/2; - -#ifdef _MSC_VER - #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - if (MLKeyRef<_KeyType>(key) > operator[](index) ) - { - if (ascendingSort) - lowerBound=index+1; - else - upperBound=index-1; - } - else if (MLKeyRef<_KeyType>(key) < operator[](index) ) - { - if (ascendingSort) - upperBound=index-1; - else - lowerBound=index+1; - } - else - { - // == - *objectExists=true; - return index; - } - - index=lowerBound+(upperBound-lowerBound)/2; - - if (lowerBound>upperBound || upperBound==(_IndexType)-1) - { - *objectExists=false; - return lowerBound; // No match - } - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertShiftArrayRight(const _DataType &d, _IndexType index) - { - RakAssert(_MultilistType!=ML_QUEUE); - - // Move the elements in the list to make room - _IndexType i; - for ( i = dataSize; i != index; i-- ) - data[ i ] = data[ i - 1 ]; - - // Insert the new item at the correct spot - data[ index ] = d; - - ++dataSize; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeleteShiftArrayLeft( _IndexType index ) - { - RakAssert(index < dataSize); - RakAssert(_MultilistType!=ML_QUEUE); - - _IndexType i; - for ( i = index; i < dataSize-1; i++ ) - data[i]=data[i+1]; - } -}; - -/* -struct KeyAndValue -{ - int key; - short value; -}; - -DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS(KeyAndValue,int,key) - -void MultilistUnitTest(void) -{ - DataStructures::DefaultIndexType oldSize; - DataStructures::Multilist ml1; - ml1.Reallocate(64); - RakAssert(ml1.IsEmpty()); - ml1.Push(53); - RakAssert(ml1.Peek()==53); - RakAssert(ml1.IsEmpty()==false); - RakAssert(ml1.Pop()==53); - RakAssert(ml1.IsEmpty()==true); - for (int i=0; i < 512; i++) - ml1.Push(i); - RakAssert(ml1.GetIndexOf(200)==200); - RakAssert(ml1.PeekOpposite()==0); - RakAssert(ml1.PopOpposite()==0); - RakAssert(ml1.PeekOpposite()==1); - RakAssert(ml1.Peek()==511); - ml1.ReverseList(); - for (int i=0; i < 511; i++) - RakAssert(ml1[i]==511-i); - RakAssert(ml1.PeekOpposite()==511); - RakAssert(ml1.Peek()==1); - oldSize = ml1.GetSize(); - ml1.RemoveAtIndex(0); - RakAssert(ml1.GetSize()==oldSize-1); - RakAssert(ml1.PeekOpposite()==1); - ml1.Clear(_FILE_AND_LINE_); - RakAssert(ml1.IsEmpty()==true); - - ml1.Sort(true); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(100); - ml1.Sort(true); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(50); - ml1.Push(100); - ml1.Sort(true); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(100); - ml1.Push(50); - ml1.Sort(true); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(100); - ml1.Push(50); - ml1.Push(150); - ml1.Push(25); - ml1.Push(175); - ml1.Sort(true); - RakAssert(ml1[0]==25); - RakAssert(ml1[1]==50); - RakAssert(ml1[2]==100); - RakAssert(ml1[3]==150); - RakAssert(ml1[4]==175); - RakAssert(ml1.GetIndexOf(25)==0); - RakAssert(ml1.GetIndexOf(50)==1); - RakAssert(ml1.GetIndexOf(100)==2); - RakAssert(ml1.GetIndexOf(150)==3); - RakAssert(ml1.GetIndexOf(175)==4); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(1); - ml1.Push(2); - ml1.Push(3); - ml1.Push(4); - ml1.Push(5); - ml1.Sort(true); - RakAssert(ml1[0]==1); - RakAssert(ml1[1]==2); - RakAssert(ml1[2]==3); - RakAssert(ml1[3]==4); - RakAssert(ml1[4]==5); - RakAssert(ml1.GetIndexOf(1)==0); - RakAssert(ml1.GetIndexOf(2)==1); - RakAssert(ml1.GetIndexOf(3)==2); - RakAssert(ml1.GetIndexOf(4)==3); - RakAssert(ml1.GetIndexOf(5)==4); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(5); - ml1.Push(4); - ml1.Push(3); - ml1.Push(2); - ml1.Push(1); - ml1.Sort(true); - RakAssert(ml1[0]==1); - RakAssert(ml1[1]==2); - RakAssert(ml1[2]==3); - RakAssert(ml1[3]==4); - RakAssert(ml1[4]==5); - RakAssert(ml1.GetIndexOf(1)==0); - RakAssert(ml1.GetIndexOf(2)==1); - RakAssert(ml1.GetIndexOf(3)==2); - RakAssert(ml1.GetIndexOf(4)==3); - RakAssert(ml1.GetIndexOf(5)==4); - ml1.Sort(true); - RakAssert(ml1[0]==1); - RakAssert(ml1[1]==2); - RakAssert(ml1[2]==3); - RakAssert(ml1[3]==4); - RakAssert(ml1[4]==5); - RakAssert(ml1.GetIndexOf(1)==0); - RakAssert(ml1.GetIndexOf(2)==1); - RakAssert(ml1.GetIndexOf(3)==2); - RakAssert(ml1.GetIndexOf(4)==3); - RakAssert(ml1.GetIndexOf(5)==4); - ml1.Clear(_FILE_AND_LINE_); - - DataStructures::Multilist ml2; - ml2.Reallocate(64); - RakAssert(ml2.IsEmpty()); - ml2.Push(53); - RakAssert(ml2.Peek()==53); - RakAssert(ml2.IsEmpty()==false); - RakAssert(ml2.Pop()==53); - RakAssert(ml2.IsEmpty()==true); - for (int i=0; i < 512; i++) - ml2.Push(i); - RakAssert(ml2.GetIndexOf(200)==200); - RakAssert(ml2.PeekOpposite()==0); - RakAssert(ml2.PopOpposite()==0); - RakAssert(ml2.PeekOpposite()==1); - RakAssert(ml2.Peek()==511); - ml2.ReverseList(); - for (int i=0; i < 511; i++) - RakAssert(ml2[i]==511-i); - RakAssert(ml2.PeekOpposite()==511); - RakAssert(ml2.Peek()==1); - oldSize = ml2.GetSize(); - ml2.RemoveAtIndex(0); - RakAssert(ml2.GetSize()==oldSize-1); - RakAssert(ml2.Peek()==1); - RakAssert(ml2.PeekOpposite()==510); - ml2.Clear(_FILE_AND_LINE_); - RakAssert(ml2.IsEmpty()==true); - - DataStructures::Multilist ml3; - RakAssert(ml3.IsEmpty()); - ml3.Push(53); - RakAssert(ml3.Peek()==53); - RakAssert(ml3.IsEmpty()==false); - RakAssert(ml3.Pop()==53); - RakAssert(ml3.IsEmpty()==true); - for (int i=0; i < 512; i++) - ml3.Push(i); - RakAssert(ml3.GetIndexOf(200)==200); - RakAssert(ml3.PeekOpposite()==511); - RakAssert(ml3.PopOpposite()==511); - RakAssert(ml3.PeekOpposite()==510); - RakAssert(ml3.Peek()==0); - ml3.ReverseList(); - for (int i=0; i < 511; i++) - RakAssert(ml3[i]==511-1-i); - RakAssert(ml3.PeekOpposite()==0); - RakAssert(ml3.Peek()==510); - oldSize = ml3.GetSize(); - ml3.RemoveAtIndex(0); - RakAssert(ml3.GetSize()==oldSize-1); - RakAssert(ml3.Peek()==509); - RakAssert(ml3.PeekOpposite()==0); - ml3.Clear(_FILE_AND_LINE_); - RakAssert(ml3.IsEmpty()==true); - - ml3.PushOpposite(100); - ml3.PushOpposite(50); - ml3.PushOpposite(150); - ml3.PushOpposite(25); - ml3.PushOpposite(175); - ml3.Sort(true); - RakAssert(ml3[0]==25); - RakAssert(ml3[1]==50); - RakAssert(ml3[2]==100); - RakAssert(ml3[3]==150); - RakAssert(ml3[4]==175); - RakAssert(ml3.GetIndexOf(25)==0); - RakAssert(ml3.GetIndexOf(50)==1); - RakAssert(ml3.GetIndexOf(100)==2); - RakAssert(ml3.GetIndexOf(150)==3); - RakAssert(ml3.GetIndexOf(175)==4); - ml3.Clear(_FILE_AND_LINE_); - - ml3.PushOpposite(1); - ml3.PushOpposite(2); - ml3.PushOpposite(3); - ml3.PushOpposite(4); - ml3.PushOpposite(5); - ml3.Sort(true); - RakAssert(ml3[0]==1); - RakAssert(ml3[1]==2); - RakAssert(ml3[2]==3); - RakAssert(ml3[3]==4); - RakAssert(ml3[4]==5); - RakAssert(ml3.GetIndexOf(1)==0); - RakAssert(ml3.GetIndexOf(2)==1); - RakAssert(ml3.GetIndexOf(3)==2); - RakAssert(ml3.GetIndexOf(4)==3); - RakAssert(ml3.GetIndexOf(5)==4); - ml3.Clear(_FILE_AND_LINE_); - - ml3.PushOpposite(5); - ml3.PushOpposite(4); - ml3.PushOpposite(3); - ml3.PushOpposite(2); - ml3.PushOpposite(1); - ml3.Sort(true); - RakAssert(ml3[0]==1); - RakAssert(ml3[1]==2); - RakAssert(ml3[2]==3); - RakAssert(ml3[3]==4); - RakAssert(ml3[4]==5); - RakAssert(ml3.GetIndexOf(1)==0); - RakAssert(ml3.GetIndexOf(2)==1); - RakAssert(ml3.GetIndexOf(3)==2); - RakAssert(ml3.GetIndexOf(4)==3); - RakAssert(ml3.GetIndexOf(5)==4); - ml3.Sort(true); - RakAssert(ml3[0]==1); - RakAssert(ml3[1]==2); - RakAssert(ml3[2]==3); - RakAssert(ml3[3]==4); - RakAssert(ml3[4]==5); - RakAssert(ml3.GetIndexOf(1)==0); - RakAssert(ml3.GetIndexOf(2)==1); - RakAssert(ml3.GetIndexOf(3)==2); - RakAssert(ml3.GetIndexOf(4)==3); - RakAssert(ml3.GetIndexOf(5)==4); - - ml3.SetSortOrder(false); - ml3.Sort(false); - RakAssert(ml3[0]==5); - RakAssert(ml3[1]==4); - RakAssert(ml3[2]==3); - RakAssert(ml3[3]==2); - RakAssert(ml3[4]==1); - RakAssert(ml3.GetIndexOf(1)==4); - RakAssert(ml3.GetIndexOf(2)==3); - RakAssert(ml3.GetIndexOf(3)==2); - RakAssert(ml3.GetIndexOf(4)==1); - RakAssert(ml3.GetIndexOf(5)==0); - - ml3.Clear(_FILE_AND_LINE_); - - DataStructures::Multilist ml4; - ml4.Reallocate(64); - RakAssert(ml4.IsEmpty()); - ml4.Push(53); - RakAssert(ml4.Peek()==53); - RakAssert(ml4.IsEmpty()==false); - RakAssert(ml4.Pop()==53); - RakAssert(ml4.IsEmpty()==true); - for (int i=0; i < 512; i++) - ml4.Push(i); - RakAssert(ml4.GetIndexOf(200)==200); - RakAssert(ml4.PeekOpposite()==0); - RakAssert(ml4.PopOpposite()==0); - RakAssert(ml4.PeekOpposite()==1); - RakAssert(ml4.Peek()==511); - ml4.ReverseList(); - for (int i=0; i < 511; i++) - RakAssert(ml4[i]==511-i); - RakAssert(ml4.PeekOpposite()==511); - RakAssert(ml4.Peek()==1); - oldSize = ml4.GetSize(); - ml4.RemoveAtIndex(0); - RakAssert(ml4.GetSize()==oldSize-1); - RakAssert(ml4.Peek()==1); - RakAssert(ml4.PeekOpposite()==510); - ml4.Clear(_FILE_AND_LINE_); - RakAssert(ml4.IsEmpty()==true); - - DataStructures::Multilist ml5; - - for (int i=0; i < 16; i++) - { - KeyAndValue *kav = new KeyAndValue; - kav->key=i; - kav->value=i+100; - ml5.Push(kav,kav->key); - } - - RakAssert(ml5.GetIndexOf(0)==0); - RakAssert(ml5.GetIndexOf(5)==5); - RakAssert(ml5.GetIndexOf(15)==15); - RakAssert(ml5.GetIndexOf(16)==-1); - ml5.RemoveAtKey(0,true); - RakAssert(ml5.GetIndexOf(1)==0); - KeyAndValue *iPtr = ml5.GetPtr(5); - RakAssert(iPtr); - RakAssert(iPtr->value=105); - iPtr = ml5.GetPtr(1234); - RakAssert(iPtr==0); - ml5.ForEach(DataStructures::DeletePtr); - - - DataStructures::Multilist ml6; - ml6.Push(2); - ml6.Push(1); - ml6.Push(6); - ml6.Push(3); - RakAssert(ml6.Peek()==3); - ml6.SetMultilistType(ML_STACK); - RakAssert(ml6.Peek()==3); - ml6.SetMultilistType(ML_QUEUE); - RakAssert(ml6.Peek()==2); - ml6.SetMultilistType(ML_ORDERED_LIST); - RakAssert(ml6.Peek()=6); - ml6.SetMultilistType(ML_STACK); - RakAssert(ml6.Peek()==6); - ml6.SetMultilistType(ML_QUEUE); - RakAssert(ml6.Peek()==1); -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif -*/ - -#endif +/// \file DS_Multilist.h +/// \internal +/// \brief ADT that can represent an unordered list, ordered list, stack, or queue with a common interface +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __MULTILIST_H +#define __MULTILIST_H + +#include "RakAssert.h" +#include // memmove +#include "Export.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" + + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#pragma warning( disable : 4512 ) // warning C4512: assignment operator could not be generated +#endif + +/// What algorithm to use to store the data for the Multilist +enum MultilistType +{ + /// Removing from the middle of the list will swap the end of the list rather than shift the elements. Push and Pop operate on the tail. + ML_UNORDERED_LIST, + /// A normal list, with the list order preserved. Push and Pop operate on the tail. + ML_STACK, + /// A queue. Push and Pop operate on the head + ML_QUEUE, + /// A list that is always kept in order. Elements must be unique, and compare against each other consistently using <, ==, and > + ML_ORDERED_LIST, + /// A list whose type can change at runtime + ML_VARIABLE_DURING_RUNTIME +}; + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// Can be used with Multilist::ForEach + /// Assuming the Multilist holds pointers, will delete those pointers + template + void DeletePtr_RakNet(templateType &ptr, const char *file, unsigned int line ) {RakNet::OP_DELETE(ptr, file, line);} + + /// Can be used with Multilist::ForEach + /// Assuming the Multilist holds pointers, will delete those pointers + template + void DeletePtr(templateType &ptr) {delete ptr;} + + /// The following is invalid. + /// bool operator<( const MyClass *myClass, const int &inputKey ) {return myClass->value < inputKey;} + /// At least one type has to be a reference to a class + /// MLKeyRef is a helper class to turn a native type into a class, so you can compare that native type against a pointer to a different class + /// Used for he Multilist, when _DataType != _KeyType + template < class templateType > + class MLKeyRef + { + public: + MLKeyRef(const templateType& input) : val(input) {} + const templateType &Get(void) const {return val;} + bool operator<( const templateType &right ) {return val < right;} + bool operator>( const templateType &right ) {return val > right;} + bool operator==( const templateType &right ) {return val == right;} + protected: + const templateType &val; + }; + + /// For the Multilist, when _DataType != _KeyType, you must define the comparison operators between the key and the data + /// This is non-trivial due to the need to use MLKeyRef in case the type held is a pointer to a structure or class and the key type is not a class + /// For convenience, this macro will implement the comparison operators under the following conditions + /// 1. _DataType is a pointer to a class or structure + /// 2. The key is a member variable of _DataType + #define DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS( _CLASS_NAME_, _KEY_TYPE_, _MEMBER_VARIABLE_NAME_ ) \ + bool operator<( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() < cls->_MEMBER_VARIABLE_NAME_;} \ + bool operator>( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() > cls->_MEMBER_VARIABLE_NAME_;} \ + bool operator==( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() == cls->_MEMBER_VARIABLE_NAME_;} + + typedef uint32_t DefaultIndexType; + + /// \brief The multilist, representing an abstract data type that generally holds lists. + /// \param[in] _MultilistType What type of list this is, \sa MultilistType + /// \param[in] _DataType What type of data this list holds. + /// \param[in] _KeyType If a function takes a key to sort on, what type of key this is. The comparison operator between _DataType and _KeyType must be defined + /// \param[in] _IndexType What variable type to use for indices + template + class RAK_DLL_EXPORT Multilist + { + public: + Multilist(); + ~Multilist(); + Multilist( const Multilist& source ); + Multilist& operator= ( const Multilist& source ); + _DataType& operator[] ( const _IndexType position ) const; + /// Unordered list, stack is LIFO + /// QUEUE is FIFO + /// Ordered list is inserted in order + void Push(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ ); + void Push(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Gets or removes and gets an element from the list, according to the same rules as Push(). + /// Ordered list is LIFO for the purposes of Pop and Peek. + _DataType &Pop(const char *file=__FILE__, unsigned int line=__LINE__); + _DataType &Peek(void) const; + + /// \brief Same as Push(), except FIFO and LIFO are reversed. + /// Ordered list still inserts in order. + void PushOpposite(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ ); + void PushOpposite(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Same as Pop() and Peek(), except FIFO and LIFO are reversed. + _DataType &PopOpposite(const char *file=__FILE__, unsigned int line=__LINE__); + _DataType &PeekOpposite(void) const; + + /// \brief Stack,Queue: Inserts at index indicated, elements are shifted. + /// Ordered list: Inserts, position is ignored + void InsertAtIndex(const _DataType &d, _IndexType index, const char *file=__FILE__, unsigned int line=__LINE__); + + /// \brief Unordered list, removes at index indicated, swaps last element with that element. + /// Otherwise, array is shifted left to overwrite removed element + /// \details Index[0] returns the same as Pop() for a queue. + /// Same as PopOpposite() for the list and ordered list + void RemoveAtIndex(_IndexType position, const char *file=__FILE__, unsigned int line=__LINE__); + + /// \brief Find the index of \a key, and remove at that index. + bool RemoveAtKey(_KeyType key, bool assertIfDoesNotExist, const char *file=__FILE__, unsigned int line=__LINE__); + + /// \brief Finds the index of \a key. Return -1 if the key is not found. + _IndexType GetIndexOf(_KeyType key) const; + + /// \brief Returns where in the list we should insert the item, to preserve list order. + /// Returns -1 if the item is already in the list + _IndexType GetInsertionIndex(_KeyType key) const; + + /// \brief Finds the index of \a key. Return 0 if the key is not found. Useful if _DataType is always non-zero pointers. + _DataType GetPtr(_KeyType key) const; + + /// \brief Iterate over the list, calling the function pointer on each element. + void ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line); + void ForEach(void (*func)(_DataType &item)); + + /// \brief Returns if the list is empty. + bool IsEmpty(void) const; + + /// \brief Returns the number of elements used in the list. + _IndexType GetSize(void) const; + + /// \brief Empties the list. The list is not deallocated if it is small, + /// unless \a deallocateSmallBlocks is true + void Clear( bool deallocateSmallBlocks=true, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Empties the list, first calling RakNet::OP_Delete on all items. + /// \details The list is not deallocated if it is small, unless \a deallocateSmallBlocks is true + void ClearPointers( bool deallocateSmallBlocks=true, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Empty one item from the list, first calling RakNet::OP_Delete on that item. + void ClearPointer( _KeyType key, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Reverses the elements in the list, and flips the sort order + /// returned by GetSortOrder() if IsSorted() returns true at the time the function is called + void ReverseList(void); + + /// \brief Reallocates the list to a larger size. + /// If \a size is smaller than the value returned by GetSize(), the call does nothing. + void Reallocate(_IndexType size, const char *file=__FILE__, unsigned int line=__LINE__); + + /// \brief Sorts the list unless it is an ordered list, in which it does nothing as the list is assumed to already be sorted. + /// \details However, if \a force is true, it will also resort the ordered list, useful if the comparison operator between _KeyType and _DataType would now return different results + /// Once the list is sorted, further operations to lookup by key will be log2(n) until the list is modified + void Sort(bool force); + + /// \brief Sets the list to be remembered as sorted. + /// \details Optimization if the source is sorted already + void TagSorted(void); + + /// \brief Defaults to ascending. + /// \details Used by Sort(), and by ML_ORDERED_LIST + void SetSortOrder(bool ascending); + + /// \brief Returns true if ascending. + bool GetSortOrder(void) const; + + /// \brief Returns true if the list is currently believed to be in a sorted state. + /// \details Doesn't actually check for sortedness, just if Sort() + /// was recently called, or MultilistType is ML_ORDERED_LIST + bool IsSorted(void) const; + + /// Returns what type of list this is + MultilistType GetMultilistType(void) const; + + /// \brief Changes what type of list this is. + /// \pre Template must be defined with ML_VARIABLE_DURING_RUNTIME for this to do anything + /// \param[in] mlType Any value of the enum MultilistType, except ML_VARIABLE_DURING_RUNTIME + void SetMultilistType(MultilistType newType); + + /// \brief Returns the intersection of two lists. + /// Intersection is items common to both lists. + static void FindIntersection( + Multilist& source1, + Multilist& source2, + Multilist& intersection, + Multilist& uniqueToSource1, + Multilist& uniqueToSource2); + + protected: + void ReallocateIfNeeded(const char *file, unsigned int line); + void DeallocateIfNeeded(const char *file, unsigned int line); + void ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line); + void ReverseListInternal(void); + void InsertInOrderedList(const _DataType &d, const _KeyType &key); + _IndexType GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const; + void InsertShiftArrayRight(const _DataType &d, _IndexType index); + void DeleteShiftArrayLeft(_IndexType index); + void QSortAscending(_IndexType left, _IndexType right); + void QSortDescending(_IndexType left, _IndexType right); + void CopySource( const Multilist& source ); + + /// An array of user values + _DataType* data; + + /// Number of elements in the list + _IndexType dataSize; + + /// Size of \a array + _IndexType allocationSize; + + /// Array index for the head of the queue + _IndexType queueHead; + + /// Array index for the tail of the queue + _IndexType queueTail; + + /// How many bytes the user chose to preallocate + /// Won't automatically deallocate below this + _IndexType preallocationSize; + + enum + { + ML_UNSORTED, + ML_SORTED_ASCENDING, + ML_SORTED_DESCENDING + } sortState; + + bool ascendingSort; + + // In case we are using the variable type multilist + MultilistType variableMultilistType; + }; + + template + Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist() + { + data=0; + dataSize=0; + allocationSize=0; + ascendingSort=true; + sortState=ML_UNSORTED; + queueHead=0; + queueTail=0; + preallocationSize=0; + + if (_MultilistType==ML_ORDERED_LIST) + sortState=ML_SORTED_ASCENDING; + else + sortState=ML_UNSORTED; + + if (_MultilistType==ML_VARIABLE_DURING_RUNTIME) + variableMultilistType=ML_UNORDERED_LIST; + } + + template + Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::~Multilist() + { + if (data!=0) + RakNet::OP_DELETE_ARRAY(data, _FILE_AND_LINE_); + } + + template + Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist( const Multilist& source ) + { + CopySource(source); + } + + template + Multilist<_MultilistType, _DataType, _KeyType, _IndexType>& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator= ( const Multilist& source ) + { + Clear(true); + CopySource(source); + return *this; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::CopySource( const Multilist& source ) + { + dataSize=source.GetSize(); + ascendingSort=source.ascendingSort; + sortState=source.sortState; + queueHead=0; + queueTail=dataSize; + preallocationSize=source.preallocationSize; + variableMultilistType=source.variableMultilistType; + if (source.data==0) + { + data=0; + allocationSize=0; + } + else + { + allocationSize=dataSize; + data = RakNet::OP_NEW_ARRAY<_DataType>(dataSize,_FILE_AND_LINE_); + _IndexType i; + for (i=0; i < dataSize; i++) + data[i]=source[i]; + } + } + + template + _DataType& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator[] ( const _IndexType position ) const + { + RakAssert(position= allocationSize ) + return data[ queueHead + position - allocationSize ]; + else + return data[ queueHead + position ]; + } + + return data[position]; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const char *file, unsigned int line ) + { + Push(d,d,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const _KeyType &key, const char *file, unsigned int line ) + { + ReallocateIfNeeded(file,line); + + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) + { + data[dataSize]=d; + dataSize++; + } + else if (GetMultilistType()==ML_QUEUE) + { + data[queueTail++] = d; + + if ( queueTail == allocationSize ) + queueTail = 0; + dataSize++; + } + else + { + RakAssert(GetMultilistType()==ML_ORDERED_LIST); + InsertInOrderedList(d,key); + } + + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE) + { + // Break sort if no longer sorted + if (sortState!=ML_UNSORTED && dataSize>1) + { + if (ascendingSort) + { + if ( MLKeyRef<_KeyType>(key) < operator[](dataSize-2) ) + sortState=ML_UNSORTED; + } + else + { + if ( MLKeyRef<_KeyType>(key) > operator[](dataSize-2) ) + sortState=ML_UNSORTED; + } + + sortState=ML_UNSORTED; + } + } + } + + template + _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Pop(const char *file, unsigned int line) + { + RakAssert(IsEmpty()==false); + DeallocateIfNeeded(file,line); + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + dataSize--; + return data[dataSize]; + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + + if ( ++queueHead == allocationSize ) + queueHead = 0; + + if ( queueHead == 0 ) + return data[ allocationSize -1 ]; + + dataSize--; + return data[ queueHead -1 ]; + } + } + + template + _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Peek(void) const + { + RakAssert(IsEmpty()==false); + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + return data[dataSize-1]; + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + return data[ queueHead ]; + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const char *file, unsigned int line ) + { + PushOpposite(d,d,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const _KeyType &key, const char *file, unsigned int line ) + { + ReallocateIfNeeded(file,line); + + // Unordered list Push at back + if (GetMultilistType()==ML_UNORDERED_LIST) + { + data[dataSize]=d; + dataSize++; + } + else if (GetMultilistType()==ML_STACK) + { + // Stack push at front of the list, instead of back as normal + InsertAtIndex(d,0,file,line); + } + else if (GetMultilistType()==ML_QUEUE) + { + // Queue push at front of the list, instead of back as normal + InsertAtIndex(d,0,file,line); + } + else + { + RakAssert(GetMultilistType()==ML_ORDERED_LIST); + InsertInOrderedList(d,key); + } + + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE) + { + // Break sort if no longer sorted + if (sortState!=ML_UNSORTED && dataSize>1) + { + if (ascendingSort) + { + if ( MLKeyRef<_KeyType>(key) > operator[](1) ) + sortState=ML_UNSORTED; + } + else + { + if ( MLKeyRef<_KeyType>(key) < operator[](1) ) + sortState=ML_UNSORTED; + } + } + } + } + + template + _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PopOpposite(const char *file, unsigned int line) + { + RakAssert(IsEmpty()==false); + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + // Copy leftmost to end + ReallocateIfNeeded(file,line); + data[dataSize]=data[0]; + DeleteShiftArrayLeft(0); + --dataSize; + // Assuming still leaves at least one element past the end of the list allocated + DeallocateIfNeeded(file,line); + // Return end + return data[dataSize+1]; + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + // Deallocate first, since we are returning off the existing list + DeallocateIfNeeded(file,line); + dataSize--; + + if (queueTail==0) + queueTail=allocationSize-1; + else + --queueTail; + + return data[queueTail]; + } + } + + template + _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PeekOpposite(void) const + { + RakAssert(IsEmpty()==false); + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + return data[0]; + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + _IndexType priorIndex; + if (queueTail==0) + priorIndex=allocationSize-1; + else + priorIndex=queueTail-1; + + return data[priorIndex]; + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertAtIndex(const _DataType &d, _IndexType index, const char *file, unsigned int line) + { + ReallocateIfNeeded(file,line); + + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + if (index>=dataSize) + { + // insert at end + data[dataSize]=d; + + dataSize++; + } + else + { + // insert at index + InsertShiftArrayRight(d,index); + } + } + else + { + data[queueTail++] = d; + + if ( queueTail == allocationSize ) + queueTail = 0; + + ++dataSize; + + if (dataSize==1) + return; + + _IndexType writeIndex, readIndex, trueWriteIndex, trueReadIndex; + writeIndex=dataSize-1; + readIndex=writeIndex-1; + while (readIndex >= index) + { + if ( queueHead + writeIndex >= allocationSize ) + trueWriteIndex = queueHead + writeIndex - allocationSize; + else + trueWriteIndex = queueHead + writeIndex; + + if ( queueHead + readIndex >= allocationSize ) + trueReadIndex = queueHead + readIndex - allocationSize; + else + trueReadIndex = queueHead + readIndex; + + data[trueWriteIndex]=data[trueReadIndex]; + + if (readIndex==0) + break; + writeIndex--; + readIndex--; + } + + if ( queueHead + index >= allocationSize ) + trueWriteIndex = queueHead + index - allocationSize; + else + trueWriteIndex = queueHead + index; + + data[trueWriteIndex]=d; + } + + if (_MultilistType!=ML_ORDERED_LIST) + sortState=ML_UNSORTED; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtIndex(_IndexType position, const char *file, unsigned int line) + { + RakAssert(position < dataSize); + RakAssert(IsEmpty()==false); + + if (GetMultilistType()==ML_UNORDERED_LIST) + { + // Copy tail to current + data[position]=data[dataSize-1]; + } + else if (GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + DeleteShiftArrayLeft(position); + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + + _IndexType index, next; + + if ( queueHead + position >= allocationSize ) + index = queueHead + position - allocationSize; + else + index = queueHead + position; + + next = index + 1; + + if ( next == allocationSize ) + next = 0; + + while ( next != queueTail ) + { + // Overwrite the previous element + data[ index ] = data[ next ]; + index = next; + //next = (next + 1) % allocationSize; + + if ( ++next == allocationSize ) + next = 0; + } + + // Move the queueTail back + if ( queueTail == 0 ) + queueTail = allocationSize - 1; + else + --queueTail; + } + + + dataSize--; + DeallocateIfNeeded(file,line); + } + + template + bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtKey(_KeyType key, bool assertIfDoesNotExist, const char *file, unsigned int line) + { + _IndexType index = GetIndexOf(key); + if (index==(_IndexType)-1) + { + RakAssert(assertIfDoesNotExist==false && "RemoveAtKey element not found"); + return false; + } + RemoveAtIndex(index,file,line); + return true; + } + + template + _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexOf(_KeyType key) const + { + _IndexType i; + if (IsSorted()) + { + bool objectExists; + i=GetIndexFromKeyInSortedList(key, &objectExists); + if (objectExists) + return i; + return (_IndexType)-1; + } + else if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) + { + for (i=0; i < dataSize; i++) + { + if (MLKeyRef<_KeyType>(key)==data[i]) + return i; + } + return (_IndexType)-1; + } + else + { + RakAssert( GetMultilistType()==ML_QUEUE ); + + for (i=0; i < dataSize; i++) + { + if (MLKeyRef<_KeyType>(key)==operator[](i)) + return i; + } + return (_IndexType)-1; + } + } + + template + _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetInsertionIndex(_KeyType key) const + { + _IndexType i; + if (IsSorted()) + { + bool objectExists; + i=GetIndexFromKeyInSortedList(key, &objectExists); + if (objectExists) + return (_IndexType)-1; + return i; + } + else if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) + { + for (i=0; i < dataSize; i++) + { + if (MLKeyRef<_KeyType>(key)==data[i]) + return (_IndexType)-1; + } + return dataSize; + } + else + { + RakAssert( GetMultilistType()==ML_QUEUE ); + + for (i=0; i < dataSize; i++) + { + if (MLKeyRef<_KeyType>(key)==operator[](i)) + return (_IndexType)-1; + } + return dataSize; + } + } + + template + _DataType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetPtr(_KeyType key) const + { + _IndexType i = GetIndexOf(key); + if (i==(_IndexType)-1) + return 0; + return data[i]; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line) + { + _IndexType i; + for (i=0; i < dataSize; i++) + func(operator[](i), file, line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item)) + { + _IndexType i; + for (i=0; i < dataSize; i++) + func(operator[](i)); + } + + template + bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsEmpty(void) const + { + return dataSize==0; + } + + template + _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSize(void) const + { + return dataSize; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Clear( bool deallocateSmallBlocks, const char *file, unsigned int line ) + { + dataSize=0; + if (GetMultilistType()==ML_ORDERED_LIST) + if (ascendingSort) + sortState=ML_SORTED_ASCENDING; + else + sortState=ML_SORTED_DESCENDING; + else + sortState=ML_UNSORTED; + queueHead=0; + queueTail=0; + + if (deallocateSmallBlocks && allocationSize < 128 && data) + { + RakNet::OP_DELETE_ARRAY(data,file,line); + data=0; + allocationSize=0; + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ClearPointers( bool deallocateSmallBlocks, const char *file, unsigned int line ) + { + _IndexType i; + for (i=0; i < dataSize; i++) + RakNet::OP_DELETE(operator[](i), file, line); + Clear(deallocateSmallBlocks, file, line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ClearPointer( _KeyType key, const char *file, unsigned int line ) + { + _IndexType i; + i = GetIndexOf(key); + if (i!=-1) + { + RakNet::OP_DELETE(operator[](i), file, line); + RemoveAtIndex(i); + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseList(void) + { + if (IsSorted()) + ascendingSort=!ascendingSort; + + ReverseListInternal(); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Reallocate(_IndexType size, const char *file, unsigned int line) + { + _IndexType newAllocationSize; + if (size < dataSize) + newAllocationSize=dataSize; + else + newAllocationSize=size; + preallocationSize=size; + ReallocToSize(newAllocationSize,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Sort(bool force) + { + if (IsSorted() && force==false) + return; + + if (dataSize>1) + { + if (ascendingSort) + QSortAscending(0,dataSize-1); + else + QSortDescending(0,dataSize-1); + } + + TagSorted(); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::TagSorted(void) + { + if (ascendingSort) + sortState=ML_SORTED_ASCENDING; + else + sortState=ML_SORTED_DESCENDING; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortAscending(_IndexType leftEdge, _IndexType rightEdge) + { + _DataType temp; + _IndexType left=leftEdge; + _IndexType right=rightEdge; + _IndexType pivotIndex=left++; + + while (left data[pivotIndex]) + { + --left; + + data[pivotIndex]=data[left]; + data[left]=temp; + } + else + { + data[pivotIndex]=data[left]; + data[left]=temp; + + --left; + } + + if (left!=leftEdge) + QSortAscending(leftEdge, left); + + if (right!=rightEdge) + QSortAscending(right, rightEdge); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortDescending(_IndexType leftEdge, _IndexType rightEdge) + { + _DataType temp; + _IndexType left=leftEdge; + _IndexType right=rightEdge; + _IndexType pivotIndex=left++; + + while (left= data[pivotIndex]) + { + ++left; + } + else + { + temp=data[left]; + data[left]=data[right]; + data[right]=temp; + --right; + } + } + + temp=data[pivotIndex]; + + // Move pivot to center + if (data[left] < data[pivotIndex]) + { + --left; + + data[pivotIndex]=data[left]; + data[left]=temp; + } + else + { + data[pivotIndex]=data[left]; + data[left]=temp; + + --left; + } + + if (left!=leftEdge) + QSortDescending(leftEdge, left); + + if (right!=rightEdge) + QSortDescending(right, rightEdge); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetSortOrder(bool ascending) + { + if (ascendingSort!=ascending && IsSorted()) + { + ascendingSort=ascending; + // List is sorted, and the sort order has changed. So reverse the list + ReverseListInternal(); + } + else + ascendingSort=ascending; + } + + template + bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSortOrder(void) const + { + return ascendingSort; + } + + template + bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsSorted(void) const + { + return GetMultilistType()==ML_ORDERED_LIST || sortState!=ML_UNSORTED; + } + + template + MultilistType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetMultilistType(void) const + { + if (_MultilistType==ML_VARIABLE_DURING_RUNTIME) + return variableMultilistType; + return _MultilistType; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetMultilistType(MultilistType newType) + { + RakAssert(_MultilistType==ML_VARIABLE_DURING_RUNTIME); + switch (variableMultilistType) + { + case ML_UNORDERED_LIST: + switch (newType) + { + case ML_UNORDERED_LIST: + // No change + break; + case ML_STACK: + // Same data format + break; + case ML_QUEUE: + queueHead=0; + queueTail=dataSize; + break; + case ML_ORDERED_LIST: + Sort(false); + break; + } + break; + case ML_STACK: + switch (newType) + { + case ML_UNORDERED_LIST: + // Same data format + break; + case ML_STACK: + // No change + break; + case ML_QUEUE: + queueHead=0; + queueTail=dataSize; + break; + case ML_ORDERED_LIST: + Sort(false); + break; + } + break; + case ML_QUEUE: + switch (newType) + { + case ML_UNORDERED_LIST: + case ML_STACK: + case ML_ORDERED_LIST: + if (queueTail < queueHead) + { + // Realign data if wrapped + ReallocToSize(dataSize, _FILE_AND_LINE_); + } + else + { + // Else can just copy starting at head + _IndexType i; + for (i=0; i < dataSize; i++) + data[i]=operator[](i); + } + if (newType==ML_ORDERED_LIST) + Sort(false); + break; + case ML_QUEUE: + // No change + break; + } + break; + case ML_ORDERED_LIST: + switch (newType) + { + case ML_UNORDERED_LIST: + case ML_STACK: + case ML_QUEUE: + // Same data format + // Tag as sorted + if (ascendingSort) + sortState=ML_SORTED_ASCENDING; + else + sortState=ML_SORTED_DESCENDING; + if (newType==ML_QUEUE) + { + queueHead=0; + queueTail=dataSize; + } + break; + case ML_ORDERED_LIST: + // No change + break; + } + break; + } + + variableMultilistType=newType; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::FindIntersection( + Multilist& source1, + Multilist& source2, + Multilist& intersection, + Multilist& uniqueToSource1, + Multilist& uniqueToSource2) + { + _IndexType index1=0, index2=0; + source1.SetSortOrder(true); + source2.SetSortOrder(true); + source1.Sort(false); + source2.Sort(false); + intersection.Clear(true,_FILE_AND_LINE_); + uniqueToSource1.Clear(true,_FILE_AND_LINE_); + uniqueToSource2.Clear(true,_FILE_AND_LINE_); + + while (index1 < source1.GetSize() && index2 < source2.GetSize()) + { + if (source1[index1] + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocateIfNeeded(const char *file, unsigned int line) + { + if (dataSize65536) + newAllocationSize=allocationSize+65536; + else + { + newAllocationSize=allocationSize<<1; // * 2 + // Protect against underflow + if (newAllocationSize < allocationSize) + newAllocationSize=allocationSize+65536; + } + + ReallocToSize(newAllocationSize,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeallocateIfNeeded(const char *file, unsigned int line) + { + if (allocationSize<512) + return; + if (dataSize >= allocationSize/3 ) + return; + if (dataSize <= preallocationSize ) + return; + + _IndexType newAllocationSize = dataSize<<1; // * 2 + + ReallocToSize(newAllocationSize,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line) + { + _DataType* newData = RakNet::OP_NEW_ARRAY<_DataType>(newAllocationSize,file,line); + _IndexType i; + for (i=0; i < dataSize; i++) + newData[i]=operator[](i); + if (dataSize>0) + { + RakNet::OP_DELETE_ARRAY(data,file,line); + if (GetMultilistType()==ML_QUEUE) + { + queueHead=0; + queueTail=dataSize; + } + } + data=newData; + allocationSize=newAllocationSize; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseListInternal(void) + { + _DataType temp; + _IndexType i; + for (i=0; i < dataSize/2; i++) + { + temp=operator[](i); + operator[](i)=operator[](dataSize-1-i); + operator[](dataSize-1-i)=temp; + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertInOrderedList(const _DataType &d, const _KeyType &key) + { + RakAssert(GetMultilistType()==ML_ORDERED_LIST); + + bool objectExists; + _IndexType index; + index = GetIndexFromKeyInSortedList(key, &objectExists); + + // if (objectExists) + // { + // Ordered list only allows unique insertions + // RakAssert("Duplicate insertion into ordered list" && false); + // return; + // } + + if (index>=dataSize) + { + // insert at end + data[dataSize]=d; + dataSize++; + } + else + { + // insert at index + InsertShiftArrayRight(d,index); + } + } + + template + _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const + { + RakAssert(IsSorted()); + _IndexType index, upperBound, lowerBound; + + if (dataSize==0) + { + *objectExists=false; + return 0; + } + + upperBound=dataSize-1; + lowerBound=0; + index = dataSize/2; + +#ifdef _MSC_VER + #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + if (MLKeyRef<_KeyType>(key) > operator[](index) ) + { + if (ascendingSort) + lowerBound=index+1; + else + upperBound=index-1; + } + else if (MLKeyRef<_KeyType>(key) < operator[](index) ) + { + if (ascendingSort) + upperBound=index-1; + else + lowerBound=index+1; + } + else + { + // == + *objectExists=true; + return index; + } + + index=lowerBound+(upperBound-lowerBound)/2; + + if (lowerBound>upperBound || upperBound==(_IndexType)-1) + { + *objectExists=false; + return lowerBound; // No match + } + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertShiftArrayRight(const _DataType &d, _IndexType index) + { + RakAssert(_MultilistType!=ML_QUEUE); + + // Move the elements in the list to make room + _IndexType i; + for ( i = dataSize; i != index; i-- ) + data[ i ] = data[ i - 1 ]; + + // Insert the new item at the correct spot + data[ index ] = d; + + ++dataSize; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeleteShiftArrayLeft( _IndexType index ) + { + RakAssert(index < dataSize); + RakAssert(_MultilistType!=ML_QUEUE); + + _IndexType i; + for ( i = index; i < dataSize-1; i++ ) + data[i]=data[i+1]; + } +}; + +/* +struct KeyAndValue +{ + int key; + short value; +}; + +DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS(KeyAndValue,int,key) + +void MultilistUnitTest(void) +{ + DataStructures::DefaultIndexType oldSize; + DataStructures::Multilist ml1; + ml1.Reallocate(64); + RakAssert(ml1.IsEmpty()); + ml1.Push(53); + RakAssert(ml1.Peek()==53); + RakAssert(ml1.IsEmpty()==false); + RakAssert(ml1.Pop()==53); + RakAssert(ml1.IsEmpty()==true); + for (int i=0; i < 512; i++) + ml1.Push(i); + RakAssert(ml1.GetIndexOf(200)==200); + RakAssert(ml1.PeekOpposite()==0); + RakAssert(ml1.PopOpposite()==0); + RakAssert(ml1.PeekOpposite()==1); + RakAssert(ml1.Peek()==511); + ml1.ReverseList(); + for (int i=0; i < 511; i++) + RakAssert(ml1[i]==511-i); + RakAssert(ml1.PeekOpposite()==511); + RakAssert(ml1.Peek()==1); + oldSize = ml1.GetSize(); + ml1.RemoveAtIndex(0); + RakAssert(ml1.GetSize()==oldSize-1); + RakAssert(ml1.PeekOpposite()==1); + ml1.Clear(_FILE_AND_LINE_); + RakAssert(ml1.IsEmpty()==true); + + ml1.Sort(true); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(100); + ml1.Sort(true); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(50); + ml1.Push(100); + ml1.Sort(true); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(100); + ml1.Push(50); + ml1.Sort(true); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(100); + ml1.Push(50); + ml1.Push(150); + ml1.Push(25); + ml1.Push(175); + ml1.Sort(true); + RakAssert(ml1[0]==25); + RakAssert(ml1[1]==50); + RakAssert(ml1[2]==100); + RakAssert(ml1[3]==150); + RakAssert(ml1[4]==175); + RakAssert(ml1.GetIndexOf(25)==0); + RakAssert(ml1.GetIndexOf(50)==1); + RakAssert(ml1.GetIndexOf(100)==2); + RakAssert(ml1.GetIndexOf(150)==3); + RakAssert(ml1.GetIndexOf(175)==4); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(1); + ml1.Push(2); + ml1.Push(3); + ml1.Push(4); + ml1.Push(5); + ml1.Sort(true); + RakAssert(ml1[0]==1); + RakAssert(ml1[1]==2); + RakAssert(ml1[2]==3); + RakAssert(ml1[3]==4); + RakAssert(ml1[4]==5); + RakAssert(ml1.GetIndexOf(1)==0); + RakAssert(ml1.GetIndexOf(2)==1); + RakAssert(ml1.GetIndexOf(3)==2); + RakAssert(ml1.GetIndexOf(4)==3); + RakAssert(ml1.GetIndexOf(5)==4); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(5); + ml1.Push(4); + ml1.Push(3); + ml1.Push(2); + ml1.Push(1); + ml1.Sort(true); + RakAssert(ml1[0]==1); + RakAssert(ml1[1]==2); + RakAssert(ml1[2]==3); + RakAssert(ml1[3]==4); + RakAssert(ml1[4]==5); + RakAssert(ml1.GetIndexOf(1)==0); + RakAssert(ml1.GetIndexOf(2)==1); + RakAssert(ml1.GetIndexOf(3)==2); + RakAssert(ml1.GetIndexOf(4)==3); + RakAssert(ml1.GetIndexOf(5)==4); + ml1.Sort(true); + RakAssert(ml1[0]==1); + RakAssert(ml1[1]==2); + RakAssert(ml1[2]==3); + RakAssert(ml1[3]==4); + RakAssert(ml1[4]==5); + RakAssert(ml1.GetIndexOf(1)==0); + RakAssert(ml1.GetIndexOf(2)==1); + RakAssert(ml1.GetIndexOf(3)==2); + RakAssert(ml1.GetIndexOf(4)==3); + RakAssert(ml1.GetIndexOf(5)==4); + ml1.Clear(_FILE_AND_LINE_); + + DataStructures::Multilist ml2; + ml2.Reallocate(64); + RakAssert(ml2.IsEmpty()); + ml2.Push(53); + RakAssert(ml2.Peek()==53); + RakAssert(ml2.IsEmpty()==false); + RakAssert(ml2.Pop()==53); + RakAssert(ml2.IsEmpty()==true); + for (int i=0; i < 512; i++) + ml2.Push(i); + RakAssert(ml2.GetIndexOf(200)==200); + RakAssert(ml2.PeekOpposite()==0); + RakAssert(ml2.PopOpposite()==0); + RakAssert(ml2.PeekOpposite()==1); + RakAssert(ml2.Peek()==511); + ml2.ReverseList(); + for (int i=0; i < 511; i++) + RakAssert(ml2[i]==511-i); + RakAssert(ml2.PeekOpposite()==511); + RakAssert(ml2.Peek()==1); + oldSize = ml2.GetSize(); + ml2.RemoveAtIndex(0); + RakAssert(ml2.GetSize()==oldSize-1); + RakAssert(ml2.Peek()==1); + RakAssert(ml2.PeekOpposite()==510); + ml2.Clear(_FILE_AND_LINE_); + RakAssert(ml2.IsEmpty()==true); + + DataStructures::Multilist ml3; + RakAssert(ml3.IsEmpty()); + ml3.Push(53); + RakAssert(ml3.Peek()==53); + RakAssert(ml3.IsEmpty()==false); + RakAssert(ml3.Pop()==53); + RakAssert(ml3.IsEmpty()==true); + for (int i=0; i < 512; i++) + ml3.Push(i); + RakAssert(ml3.GetIndexOf(200)==200); + RakAssert(ml3.PeekOpposite()==511); + RakAssert(ml3.PopOpposite()==511); + RakAssert(ml3.PeekOpposite()==510); + RakAssert(ml3.Peek()==0); + ml3.ReverseList(); + for (int i=0; i < 511; i++) + RakAssert(ml3[i]==511-1-i); + RakAssert(ml3.PeekOpposite()==0); + RakAssert(ml3.Peek()==510); + oldSize = ml3.GetSize(); + ml3.RemoveAtIndex(0); + RakAssert(ml3.GetSize()==oldSize-1); + RakAssert(ml3.Peek()==509); + RakAssert(ml3.PeekOpposite()==0); + ml3.Clear(_FILE_AND_LINE_); + RakAssert(ml3.IsEmpty()==true); + + ml3.PushOpposite(100); + ml3.PushOpposite(50); + ml3.PushOpposite(150); + ml3.PushOpposite(25); + ml3.PushOpposite(175); + ml3.Sort(true); + RakAssert(ml3[0]==25); + RakAssert(ml3[1]==50); + RakAssert(ml3[2]==100); + RakAssert(ml3[3]==150); + RakAssert(ml3[4]==175); + RakAssert(ml3.GetIndexOf(25)==0); + RakAssert(ml3.GetIndexOf(50)==1); + RakAssert(ml3.GetIndexOf(100)==2); + RakAssert(ml3.GetIndexOf(150)==3); + RakAssert(ml3.GetIndexOf(175)==4); + ml3.Clear(_FILE_AND_LINE_); + + ml3.PushOpposite(1); + ml3.PushOpposite(2); + ml3.PushOpposite(3); + ml3.PushOpposite(4); + ml3.PushOpposite(5); + ml3.Sort(true); + RakAssert(ml3[0]==1); + RakAssert(ml3[1]==2); + RakAssert(ml3[2]==3); + RakAssert(ml3[3]==4); + RakAssert(ml3[4]==5); + RakAssert(ml3.GetIndexOf(1)==0); + RakAssert(ml3.GetIndexOf(2)==1); + RakAssert(ml3.GetIndexOf(3)==2); + RakAssert(ml3.GetIndexOf(4)==3); + RakAssert(ml3.GetIndexOf(5)==4); + ml3.Clear(_FILE_AND_LINE_); + + ml3.PushOpposite(5); + ml3.PushOpposite(4); + ml3.PushOpposite(3); + ml3.PushOpposite(2); + ml3.PushOpposite(1); + ml3.Sort(true); + RakAssert(ml3[0]==1); + RakAssert(ml3[1]==2); + RakAssert(ml3[2]==3); + RakAssert(ml3[3]==4); + RakAssert(ml3[4]==5); + RakAssert(ml3.GetIndexOf(1)==0); + RakAssert(ml3.GetIndexOf(2)==1); + RakAssert(ml3.GetIndexOf(3)==2); + RakAssert(ml3.GetIndexOf(4)==3); + RakAssert(ml3.GetIndexOf(5)==4); + ml3.Sort(true); + RakAssert(ml3[0]==1); + RakAssert(ml3[1]==2); + RakAssert(ml3[2]==3); + RakAssert(ml3[3]==4); + RakAssert(ml3[4]==5); + RakAssert(ml3.GetIndexOf(1)==0); + RakAssert(ml3.GetIndexOf(2)==1); + RakAssert(ml3.GetIndexOf(3)==2); + RakAssert(ml3.GetIndexOf(4)==3); + RakAssert(ml3.GetIndexOf(5)==4); + + ml3.SetSortOrder(false); + ml3.Sort(false); + RakAssert(ml3[0]==5); + RakAssert(ml3[1]==4); + RakAssert(ml3[2]==3); + RakAssert(ml3[3]==2); + RakAssert(ml3[4]==1); + RakAssert(ml3.GetIndexOf(1)==4); + RakAssert(ml3.GetIndexOf(2)==3); + RakAssert(ml3.GetIndexOf(3)==2); + RakAssert(ml3.GetIndexOf(4)==1); + RakAssert(ml3.GetIndexOf(5)==0); + + ml3.Clear(_FILE_AND_LINE_); + + DataStructures::Multilist ml4; + ml4.Reallocate(64); + RakAssert(ml4.IsEmpty()); + ml4.Push(53); + RakAssert(ml4.Peek()==53); + RakAssert(ml4.IsEmpty()==false); + RakAssert(ml4.Pop()==53); + RakAssert(ml4.IsEmpty()==true); + for (int i=0; i < 512; i++) + ml4.Push(i); + RakAssert(ml4.GetIndexOf(200)==200); + RakAssert(ml4.PeekOpposite()==0); + RakAssert(ml4.PopOpposite()==0); + RakAssert(ml4.PeekOpposite()==1); + RakAssert(ml4.Peek()==511); + ml4.ReverseList(); + for (int i=0; i < 511; i++) + RakAssert(ml4[i]==511-i); + RakAssert(ml4.PeekOpposite()==511); + RakAssert(ml4.Peek()==1); + oldSize = ml4.GetSize(); + ml4.RemoveAtIndex(0); + RakAssert(ml4.GetSize()==oldSize-1); + RakAssert(ml4.Peek()==1); + RakAssert(ml4.PeekOpposite()==510); + ml4.Clear(_FILE_AND_LINE_); + RakAssert(ml4.IsEmpty()==true); + + DataStructures::Multilist ml5; + + for (int i=0; i < 16; i++) + { + KeyAndValue *kav = new KeyAndValue; + kav->key=i; + kav->value=i+100; + ml5.Push(kav,kav->key); + } + + RakAssert(ml5.GetIndexOf(0)==0); + RakAssert(ml5.GetIndexOf(5)==5); + RakAssert(ml5.GetIndexOf(15)==15); + RakAssert(ml5.GetIndexOf(16)==-1); + ml5.RemoveAtKey(0,true); + RakAssert(ml5.GetIndexOf(1)==0); + KeyAndValue *iPtr = ml5.GetPtr(5); + RakAssert(iPtr); + RakAssert(iPtr->value=105); + iPtr = ml5.GetPtr(1234); + RakAssert(iPtr==0); + ml5.ForEach(DataStructures::DeletePtr); + + + DataStructures::Multilist ml6; + ml6.Push(2); + ml6.Push(1); + ml6.Push(6); + ml6.Push(3); + RakAssert(ml6.Peek()==3); + ml6.SetMultilistType(ML_STACK); + RakAssert(ml6.Peek()==3); + ml6.SetMultilistType(ML_QUEUE); + RakAssert(ml6.Peek()==2); + ml6.SetMultilistType(ML_ORDERED_LIST); + RakAssert(ml6.Peek()=6); + ml6.SetMultilistType(ML_STACK); + RakAssert(ml6.Peek()==6); + ml6.SetMultilistType(ML_QUEUE); + RakAssert(ml6.Peek()==1); +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif +*/ + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_OrderedChannelHeap.h b/project/lib_projects/raknet/jni/RaknetSources/DS_OrderedChannelHeap.h index 104391e..71f71c5 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_OrderedChannelHeap.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_OrderedChannelHeap.h @@ -1,244 +1,244 @@ -/// \file DS_OrderedChannelHeap.h -/// \internal -/// \brief Ordered Channel Heap . This is a heap where you add to it on multiple ordered channels, with each channel having a different weight. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAKNET_ORDERED_CHANNEL_HEAP_H -#define __RAKNET_ORDERED_CHANNEL_HEAP_H - -#include "DS_Heap.h" -#include "DS_Map.h" -#include "DS_Queue.h" -#include "Export.h" -#include "RakAssert.h" -#include "Rand.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template > - class RAK_DLL_EXPORT OrderedChannelHeap - { - public: - static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(channel_key_type(),channel_key_type());} - - OrderedChannelHeap(); - ~OrderedChannelHeap(); - void Push(const channel_key_type &channelID, const heap_data_type &data); - void PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data); - heap_data_type Pop(const unsigned startingIndex=0); - heap_data_type Peek(const unsigned startingIndex) const; - void AddChannel(const channel_key_type &channelID, const double weight); - void RemoveChannel(channel_key_type channelID); - void Clear(void); - heap_data_type& operator[] ( const unsigned int position ) const; - unsigned ChannelSize(const channel_key_type &channelID); - unsigned Size(void) const; - - struct QueueAndWeight - { - DataStructures::Queue randResultQueue; - double weight; - bool signalDeletion; - }; - - struct HeapChannelAndData - { - HeapChannelAndData() {} - HeapChannelAndData(const channel_key_type &_channel, const heap_data_type &_data) : data(_data), channel(_channel) {} - heap_data_type data; - channel_key_type channel; - }; - - protected: - DataStructures::Map map; - DataStructures::Heap heap; - void GreatestRandResult(void); - }; - - template - OrderedChannelHeap::OrderedChannelHeap() - { - } - - template - OrderedChannelHeap::~OrderedChannelHeap() - { - Clear(); - } - - template - void OrderedChannelHeap::Push(const channel_key_type &channelID, const heap_data_type &data) - { - PushAtHead(MAX_UNSIGNED_LONG, channelID, data); - } - - template - void OrderedChannelHeap::GreatestRandResult(void) - { - double greatest; - unsigned i; - greatest=0.0; - for (i=0; i < map.Size(); i++) - { - if (map[i]->randResultQueue.Size() && map[i]->randResultQueue[0]>greatest) - greatest=map[i]->randResultQueue[0]; - } - return greatest; - } - - template - void OrderedChannelHeap::PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data) - { - // If an assert hits here then this is an unknown channel. Call AddChannel first. - QueueAndWeight *queueAndWeight=map.Get(channelID); - double maxRange, minRange, rnd; - if (queueAndWeight->randResultQueue.Size()==0) - { - // Set maxRange to the greatest random number waiting to be returned, rather than 1.0 necessarily - // This is so weights are scaled similarly among channels. For example, if the head weight for a used channel was .25 - // and then we added another channel, the new channel would need to choose between .25 and 0 - // If we chose between 1.0 and 0, it would be 1/.25 (4x) more likely to be at the head of the heap than it should be - maxRange=GreatestRandResult(); - if (maxRange==0.0) - maxRange=1.0; - minRange=0.0; - } - else if (index >= queueAndWeight->randResultQueue.Size()) - { - maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; - minRange=0.0; - } - else - { - if (index==0) - { - maxRange=GreatestRandResult(); - if (maxRange==queueAndWeight->randResultQueue[0]) - maxRange=1.0; - } - else if (index >= queueAndWeight->randResultQueue.Size()) - maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; - else - maxRange=queueAndWeight->randResultQueue[index-1]*.99999999; - - minRange=maxRange=queueAndWeight->randResultQueue[index]*1.00000001; - } - -#ifdef _DEBUG - RakAssert(maxRange!=0.0); -#endif - rnd=frandomMT() * (maxRange - minRange); - if (rnd==0.0) - rnd=maxRange/2.0; - - if (index >= queueAndWeight->randResultQueue.Size()) - queueAndWeight->randResultQueue.Push(rnd); - else - queueAndWeight->randResultQueue.PushAtHead(rnd, index); - - heap.Push(rnd*queueAndWeight->weight, HeapChannelAndData(channelID, data)); - } - - template - heap_data_type OrderedChannelHeap::Pop(const unsigned startingIndex) - { - RakAssert(startingIndex < heap.Size()); - - QueueAndWeight *queueAndWeight=map.Get(heap[startingIndex].channel); - if (startingIndex!=0) - { - // Ugly - have to count in the heap how many nodes have the same channel, so we know where to delete from in the queue - unsigned indiceCount=0; - unsigned i; - for (i=0; i < startingIndex; i++) - if (channel_key_comparison_func(heap[i].channel,heap[startingIndex].channel)==0) - indiceCount++; - queueAndWeight->randResultQueue.RemoveAtIndex(indiceCount); - } - else - { - // TODO - ordered channel heap uses progressively lower values as items are inserted. But this won't give relative ordering among channels. I have to renormalize after every pop. - queueAndWeight->randResultQueue.Pop(); - } - - // Try to remove the channel after every pop, because doing so is not valid while there are elements in the list. - if (queueAndWeight->signalDeletion) - RemoveChannel(heap[startingIndex].channel); - - return heap.Pop(startingIndex).data; - } - - template - heap_data_type OrderedChannelHeap::Peek(const unsigned startingIndex) const - { - HeapChannelAndData heapChannelAndData = heap.Peek(startingIndex); - return heapChannelAndData.data; - } - - template - void OrderedChannelHeap::AddChannel(const channel_key_type &channelID, const double weight) - { - QueueAndWeight *qaw = RakNet::OP_NEW( _FILE_AND_LINE_ ); - qaw->weight=weight; - qaw->signalDeletion=false; - map.SetNew(channelID, qaw); - } - - template - void OrderedChannelHeap::RemoveChannel(channel_key_type channelID) - { - if (map.Has(channelID)) - { - unsigned i; - i=map.GetIndexAtKey(channelID); - if (map[i]->randResultQueue.Size()==0) - { - RakNet::OP_DELETE(map[i], _FILE_AND_LINE_); - map.RemoveAtIndex(i); - } - else - { - // Signal this channel for deletion later, because the heap has nodes with this channel right now - map[i]->signalDeletion=true; - } - } - } - - template - unsigned OrderedChannelHeap::Size(void) const - { - return heap.Size(); - } - - template - heap_data_type& OrderedChannelHeap::operator[]( const unsigned int position ) const - { - return heap[position].data; - } - - - template - unsigned OrderedChannelHeap::ChannelSize(const channel_key_type &channelID) - { - QueueAndWeight *queueAndWeight=map.Get(channelID); - return queueAndWeight->randResultQueue.Size(); - } - - template - void OrderedChannelHeap::Clear(void) - { - unsigned i; - for (i=0; i < map.Size(); i++) - RakNet::OP_DELETE(map[i], _FILE_AND_LINE_); - map.Clear(_FILE_AND_LINE_); - heap.Clear(_FILE_AND_LINE_); - } -} - -#endif +/// \file DS_OrderedChannelHeap.h +/// \internal +/// \brief Ordered Channel Heap . This is a heap where you add to it on multiple ordered channels, with each channel having a different weight. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAKNET_ORDERED_CHANNEL_HEAP_H +#define __RAKNET_ORDERED_CHANNEL_HEAP_H + +#include "DS_Heap.h" +#include "DS_Map.h" +#include "DS_Queue.h" +#include "Export.h" +#include "RakAssert.h" +#include "Rand.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template > + class RAK_DLL_EXPORT OrderedChannelHeap + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(channel_key_type(),channel_key_type());} + + OrderedChannelHeap(); + ~OrderedChannelHeap(); + void Push(const channel_key_type &channelID, const heap_data_type &data); + void PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data); + heap_data_type Pop(const unsigned startingIndex=0); + heap_data_type Peek(const unsigned startingIndex) const; + void AddChannel(const channel_key_type &channelID, const double weight); + void RemoveChannel(channel_key_type channelID); + void Clear(void); + heap_data_type& operator[] ( const unsigned int position ) const; + unsigned ChannelSize(const channel_key_type &channelID); + unsigned Size(void) const; + + struct QueueAndWeight + { + DataStructures::Queue randResultQueue; + double weight; + bool signalDeletion; + }; + + struct HeapChannelAndData + { + HeapChannelAndData() {} + HeapChannelAndData(const channel_key_type &_channel, const heap_data_type &_data) : data(_data), channel(_channel) {} + heap_data_type data; + channel_key_type channel; + }; + + protected: + DataStructures::Map map; + DataStructures::Heap heap; + void GreatestRandResult(void); + }; + + template + OrderedChannelHeap::OrderedChannelHeap() + { + } + + template + OrderedChannelHeap::~OrderedChannelHeap() + { + Clear(); + } + + template + void OrderedChannelHeap::Push(const channel_key_type &channelID, const heap_data_type &data) + { + PushAtHead(MAX_UNSIGNED_LONG, channelID, data); + } + + template + void OrderedChannelHeap::GreatestRandResult(void) + { + double greatest; + unsigned i; + greatest=0.0; + for (i=0; i < map.Size(); i++) + { + if (map[i]->randResultQueue.Size() && map[i]->randResultQueue[0]>greatest) + greatest=map[i]->randResultQueue[0]; + } + return greatest; + } + + template + void OrderedChannelHeap::PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data) + { + // If an assert hits here then this is an unknown channel. Call AddChannel first. + QueueAndWeight *queueAndWeight=map.Get(channelID); + double maxRange, minRange, rnd; + if (queueAndWeight->randResultQueue.Size()==0) + { + // Set maxRange to the greatest random number waiting to be returned, rather than 1.0 necessarily + // This is so weights are scaled similarly among channels. For example, if the head weight for a used channel was .25 + // and then we added another channel, the new channel would need to choose between .25 and 0 + // If we chose between 1.0 and 0, it would be 1/.25 (4x) more likely to be at the head of the heap than it should be + maxRange=GreatestRandResult(); + if (maxRange==0.0) + maxRange=1.0; + minRange=0.0; + } + else if (index >= queueAndWeight->randResultQueue.Size()) + { + maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; + minRange=0.0; + } + else + { + if (index==0) + { + maxRange=GreatestRandResult(); + if (maxRange==queueAndWeight->randResultQueue[0]) + maxRange=1.0; + } + else if (index >= queueAndWeight->randResultQueue.Size()) + maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; + else + maxRange=queueAndWeight->randResultQueue[index-1]*.99999999; + + minRange=maxRange=queueAndWeight->randResultQueue[index]*1.00000001; + } + +#ifdef _DEBUG + RakAssert(maxRange!=0.0); +#endif + rnd=frandomMT() * (maxRange - minRange); + if (rnd==0.0) + rnd=maxRange/2.0; + + if (index >= queueAndWeight->randResultQueue.Size()) + queueAndWeight->randResultQueue.Push(rnd); + else + queueAndWeight->randResultQueue.PushAtHead(rnd, index); + + heap.Push(rnd*queueAndWeight->weight, HeapChannelAndData(channelID, data)); + } + + template + heap_data_type OrderedChannelHeap::Pop(const unsigned startingIndex) + { + RakAssert(startingIndex < heap.Size()); + + QueueAndWeight *queueAndWeight=map.Get(heap[startingIndex].channel); + if (startingIndex!=0) + { + // Ugly - have to count in the heap how many nodes have the same channel, so we know where to delete from in the queue + unsigned indiceCount=0; + unsigned i; + for (i=0; i < startingIndex; i++) + if (channel_key_comparison_func(heap[i].channel,heap[startingIndex].channel)==0) + indiceCount++; + queueAndWeight->randResultQueue.RemoveAtIndex(indiceCount); + } + else + { + // TODO - ordered channel heap uses progressively lower values as items are inserted. But this won't give relative ordering among channels. I have to renormalize after every pop. + queueAndWeight->randResultQueue.Pop(); + } + + // Try to remove the channel after every pop, because doing so is not valid while there are elements in the list. + if (queueAndWeight->signalDeletion) + RemoveChannel(heap[startingIndex].channel); + + return heap.Pop(startingIndex).data; + } + + template + heap_data_type OrderedChannelHeap::Peek(const unsigned startingIndex) const + { + HeapChannelAndData heapChannelAndData = heap.Peek(startingIndex); + return heapChannelAndData.data; + } + + template + void OrderedChannelHeap::AddChannel(const channel_key_type &channelID, const double weight) + { + QueueAndWeight *qaw = RakNet::OP_NEW( _FILE_AND_LINE_ ); + qaw->weight=weight; + qaw->signalDeletion=false; + map.SetNew(channelID, qaw); + } + + template + void OrderedChannelHeap::RemoveChannel(channel_key_type channelID) + { + if (map.Has(channelID)) + { + unsigned i; + i=map.GetIndexAtKey(channelID); + if (map[i]->randResultQueue.Size()==0) + { + RakNet::OP_DELETE(map[i], _FILE_AND_LINE_); + map.RemoveAtIndex(i); + } + else + { + // Signal this channel for deletion later, because the heap has nodes with this channel right now + map[i]->signalDeletion=true; + } + } + } + + template + unsigned OrderedChannelHeap::Size(void) const + { + return heap.Size(); + } + + template + heap_data_type& OrderedChannelHeap::operator[]( const unsigned int position ) const + { + return heap[position].data; + } + + + template + unsigned OrderedChannelHeap::ChannelSize(const channel_key_type &channelID) + { + QueueAndWeight *queueAndWeight=map.Get(channelID); + return queueAndWeight->randResultQueue.Size(); + } + + template + void OrderedChannelHeap::Clear(void) + { + unsigned i; + for (i=0; i < map.Size(); i++) + RakNet::OP_DELETE(map[i], _FILE_AND_LINE_); + map.Clear(_FILE_AND_LINE_); + heap.Clear(_FILE_AND_LINE_); + } +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_OrderedList.h b/project/lib_projects/raknet/jni/RaknetSources/DS_OrderedList.h index efbff60..b979def 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_OrderedList.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_OrderedList.h @@ -1,263 +1,263 @@ -/// \file DS_OrderedList.h -/// \internal -/// \brief Quicksort ordered list. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "DS_List.h" -#include "RakMemoryOverride.h" -#include "Export.h" - -#ifndef __ORDERED_LIST_H -#define __ORDERED_LIST_H - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template - int defaultOrderedListComparison(const key_type &a, const data_type &b) - { - if (a > - class RAK_DLL_EXPORT OrderedList - { - public: - static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultOrderedListComparison(key_type(),data_type());} - - OrderedList(); - ~OrderedList(); - OrderedList( const OrderedList& original_copy ); - OrderedList& operator= ( const OrderedList& original_copy ); - - /// comparisonFunction must take a key_type and a data_type and return <0, ==0, or >0 - /// If the data type has comparison operators already defined then you can just use defaultComparison - bool HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; - // GetIndexFromKey returns where the insert should go at the same time checks if it is there - unsigned GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; - data_type GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; - bool GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; - unsigned Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, int (*cf)(const key_type&, const data_type&)=default_comparison_function); - unsigned Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); - unsigned RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); - data_type& operator[] ( const unsigned int position ) const; - void RemoveAtIndex(const unsigned index); - void InsertAtIndex(const data_type &data, const unsigned index, const char *file, unsigned int line); - void InsertAtEnd(const data_type &data, const char *file, unsigned int line); - void RemoveFromEnd(const unsigned num=1); - void Clear(bool doNotDeallocate, const char *file, unsigned int line); - unsigned Size(void) const; - - protected: - DataStructures::List orderedList; - }; - - template - OrderedList::OrderedList() - { - } - - template - OrderedList::~OrderedList() - { - Clear(false, _FILE_AND_LINE_); - } - - template - OrderedList::OrderedList( const OrderedList& original_copy ) - { - orderedList=original_copy.orderedList; - } - - template - OrderedList& OrderedList::operator= ( const OrderedList& original_copy ) - { - orderedList=original_copy.orderedList; - return *this; - } - - template - bool OrderedList::HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)) const - { - bool objectExists; - GetIndexFromKey(key, &objectExists, cf); - return objectExists; - } - - template - data_type OrderedList::GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)) const - { - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - RakAssert(objectExists); - return orderedList[index]; - } - template - bool OrderedList::GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)) const - { - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - if (objectExists) - element = orderedList[index]; - return objectExists; - } - template - unsigned OrderedList::GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)) const - { - int index, upperBound, lowerBound; - int res; - - if (orderedList.Size()==0) - { - *objectExists=false; - return 0; - } - - upperBound=(int)orderedList.Size()-1; - lowerBound=0; - index = (int)orderedList.Size()/2; - -#ifdef _MSC_VER - #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - res = cf(key,orderedList[index]); - if (res==0) - { - *objectExists=true; - return index; - } - else if (res<0) - { - upperBound=index-1; - } - else// if (res>0) - { - lowerBound=index+1; - } - - index=lowerBound+(upperBound-lowerBound)/2; - - if (lowerBound>upperBound) - { - *objectExists=false; - return lowerBound; // No match - } - } - } - - template - unsigned OrderedList::Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, int (*cf)(const key_type&, const data_type&)) - { - (void) assertOnDuplicate; - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - - // Don't allow duplicate insertion. - if (objectExists) - { - // This is usually a bug! - RakAssert(assertOnDuplicate==false); - return (unsigned)-1; - } - - if (index>=orderedList.Size()) - { - orderedList.Insert(data, file, line); - return orderedList.Size()-1; - } - else - { - orderedList.Insert(data,index, file, line); - return index; - } - } - - template - unsigned OrderedList::Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)) - { - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - - // Can't find the element to remove if this assert hits - // RakAssert(objectExists==true); - if (objectExists==false) - { - RakAssert(objectExists==true); - return 0; - } - - orderedList.RemoveAtIndex(index); - return index; - } - - template - unsigned OrderedList::RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)) - { - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - - // Can't find the element to remove if this assert hits - if (objectExists==false) - return 0; - - orderedList.RemoveAtIndex(index); - return index; - } - - template - void OrderedList::RemoveAtIndex(const unsigned index) - { - orderedList.RemoveAtIndex(index); - } - - template - void OrderedList::InsertAtIndex(const data_type &data, const unsigned index, const char *file, unsigned int line) - { - orderedList.Insert(data, index, file, line); - } - - template - void OrderedList::InsertAtEnd(const data_type &data, const char *file, unsigned int line) - { - orderedList.Insert(data, file, line); - } - - template - void OrderedList::RemoveFromEnd(const unsigned num) - { - orderedList.RemoveFromEnd(num); - } - - template - void OrderedList::Clear(bool doNotDeallocate, const char *file, unsigned int line) - { - orderedList.Clear(doNotDeallocate, file, line); - } - - template - data_type& OrderedList::operator[]( const unsigned int position ) const - { - return orderedList[position]; - } - - template - unsigned OrderedList::Size(void) const - { - return orderedList.Size(); - } -} - -#endif +/// \file DS_OrderedList.h +/// \internal +/// \brief Quicksort ordered list. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "DS_List.h" +#include "RakMemoryOverride.h" +#include "Export.h" + +#ifndef __ORDERED_LIST_H +#define __ORDERED_LIST_H + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + int defaultOrderedListComparison(const key_type &a, const data_type &b) + { + if (a > + class RAK_DLL_EXPORT OrderedList + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultOrderedListComparison(key_type(),data_type());} + + OrderedList(); + ~OrderedList(); + OrderedList( const OrderedList& original_copy ); + OrderedList& operator= ( const OrderedList& original_copy ); + + /// comparisonFunction must take a key_type and a data_type and return <0, ==0, or >0 + /// If the data type has comparison operators already defined then you can just use defaultComparison + bool HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + // GetIndexFromKey returns where the insert should go at the same time checks if it is there + unsigned GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + data_type GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + bool GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + unsigned Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, int (*cf)(const key_type&, const data_type&)=default_comparison_function); + unsigned Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); + unsigned RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); + data_type& operator[] ( const unsigned int position ) const; + void RemoveAtIndex(const unsigned index); + void InsertAtIndex(const data_type &data, const unsigned index, const char *file, unsigned int line); + void InsertAtEnd(const data_type &data, const char *file, unsigned int line); + void RemoveFromEnd(const unsigned num=1); + void Clear(bool doNotDeallocate, const char *file, unsigned int line); + unsigned Size(void) const; + + protected: + DataStructures::List orderedList; + }; + + template + OrderedList::OrderedList() + { + } + + template + OrderedList::~OrderedList() + { + Clear(false, _FILE_AND_LINE_); + } + + template + OrderedList::OrderedList( const OrderedList& original_copy ) + { + orderedList=original_copy.orderedList; + } + + template + OrderedList& OrderedList::operator= ( const OrderedList& original_copy ) + { + orderedList=original_copy.orderedList; + return *this; + } + + template + bool OrderedList::HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)) const + { + bool objectExists; + GetIndexFromKey(key, &objectExists, cf); + return objectExists; + } + + template + data_type OrderedList::GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)) const + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + RakAssert(objectExists); + return orderedList[index]; + } + template + bool OrderedList::GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)) const + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + if (objectExists) + element = orderedList[index]; + return objectExists; + } + template + unsigned OrderedList::GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)) const + { + int index, upperBound, lowerBound; + int res; + + if (orderedList.Size()==0) + { + *objectExists=false; + return 0; + } + + upperBound=(int)orderedList.Size()-1; + lowerBound=0; + index = (int)orderedList.Size()/2; + +#ifdef _MSC_VER + #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + res = cf(key,orderedList[index]); + if (res==0) + { + *objectExists=true; + return index; + } + else if (res<0) + { + upperBound=index-1; + } + else// if (res>0) + { + lowerBound=index+1; + } + + index=lowerBound+(upperBound-lowerBound)/2; + + if (lowerBound>upperBound) + { + *objectExists=false; + return lowerBound; // No match + } + } + } + + template + unsigned OrderedList::Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, int (*cf)(const key_type&, const data_type&)) + { + (void) assertOnDuplicate; + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + + // Don't allow duplicate insertion. + if (objectExists) + { + // This is usually a bug! + RakAssert(assertOnDuplicate==false); + return (unsigned)-1; + } + + if (index>=orderedList.Size()) + { + orderedList.Insert(data, file, line); + return orderedList.Size()-1; + } + else + { + orderedList.Insert(data,index, file, line); + return index; + } + } + + template + unsigned OrderedList::Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)) + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + + // Can't find the element to remove if this assert hits + // RakAssert(objectExists==true); + if (objectExists==false) + { + RakAssert(objectExists==true); + return 0; + } + + orderedList.RemoveAtIndex(index); + return index; + } + + template + unsigned OrderedList::RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)) + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + + // Can't find the element to remove if this assert hits + if (objectExists==false) + return 0; + + orderedList.RemoveAtIndex(index); + return index; + } + + template + void OrderedList::RemoveAtIndex(const unsigned index) + { + orderedList.RemoveAtIndex(index); + } + + template + void OrderedList::InsertAtIndex(const data_type &data, const unsigned index, const char *file, unsigned int line) + { + orderedList.Insert(data, index, file, line); + } + + template + void OrderedList::InsertAtEnd(const data_type &data, const char *file, unsigned int line) + { + orderedList.Insert(data, file, line); + } + + template + void OrderedList::RemoveFromEnd(const unsigned num) + { + orderedList.RemoveFromEnd(num); + } + + template + void OrderedList::Clear(bool doNotDeallocate, const char *file, unsigned int line) + { + orderedList.Clear(doNotDeallocate, file, line); + } + + template + data_type& OrderedList::operator[]( const unsigned int position ) const + { + return orderedList[position]; + } + + template + unsigned OrderedList::Size(void) const + { + return orderedList.Size(); + } +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_Queue.h b/project/lib_projects/raknet/jni/RaknetSources/DS_Queue.h index 6fd0004..ffa22af 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_Queue.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_Queue.h @@ -1,435 +1,435 @@ -/// \file DS_Queue.h -/// \internal -/// \brief A queue used by RakNet. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __QUEUE_H -#define __QUEUE_H - -// Template classes have to have all the code in the header file -#include "RakAssert.h" -#include "Export.h" -#include "RakMemoryOverride.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// \brief A queue implemented as an array with a read and write index. - template - class RAK_DLL_EXPORT Queue - { - public: - Queue(); - ~Queue(); - Queue( Queue& original_copy ); - bool operator= ( const Queue& original_copy ); - void Push( const queue_type& input, const char *file, unsigned int line ); - void PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line ); - queue_type& operator[] ( unsigned int position ) const; // Not a normal thing you do with a queue but can be used for efficiency - void RemoveAtIndex( unsigned int position ); // Not a normal thing you do with a queue but can be used for efficiency - inline queue_type Peek( void ) const; - inline queue_type PeekTail( void ) const; - inline queue_type Pop( void ); - // Debug: Set pointer to 0, for memory leak detection - inline queue_type PopDeref( void ); - inline unsigned int Size( void ) const; - inline bool IsEmpty(void) const; - inline unsigned int AllocationSize( void ) const; - inline void Clear( const char *file, unsigned int line ); - void Compress( const char *file, unsigned int line ); - bool Find ( queue_type q ); - void ClearAndForceAllocation( int size, const char *file, unsigned int line ); // Force a memory allocation to a certain larger size - - private: - queue_type* array; - unsigned int head; // Array index for the head of the queue - unsigned int tail; // Array index for the tail of the queue - unsigned int allocation_size; - }; - - - template - inline unsigned int Queue::Size( void ) const - { - if ( head <= tail ) - return tail -head; - else - return allocation_size -head + tail; - } - - template - inline bool Queue::IsEmpty(void) const - { - return head==tail; - } - - template - inline unsigned int Queue::AllocationSize( void ) const - { - return allocation_size; - } - - template - Queue::Queue() - { - //allocation_size = 16; - //array = RakNet::OP_NEW_ARRAY(allocation_size, _FILE_AND_LINE_ ); - allocation_size = 0; - array=0; - head = 0; - tail = 0; - } - - template - Queue::~Queue() - { - if (allocation_size>0) - RakNet::OP_DELETE_ARRAY(array, _FILE_AND_LINE_); - } - - template - inline queue_type Queue::Pop( void ) - { -#ifdef _DEBUG - RakAssert( head != tail); -#endif - //head=(head+1) % allocation_size; - - if ( ++head == allocation_size ) - head = 0; - - if ( head == 0 ) - return ( queue_type ) array[ allocation_size -1 ]; - - return ( queue_type ) array[ head -1 ]; - } - - template - inline queue_type Queue::PopDeref( void ) - { - if ( ++head == allocation_size ) - head = 0; - - queue_type q; - if ( head == 0 ) - { - q=array[ allocation_size -1 ]; - array[ allocation_size -1 ]=0; - return q; - } - - q=array[ head -1 ]; - array[ head -1 ]=0; - return q; - } - - template - void Queue::PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line ) - { - RakAssert(index <= Size()); - - // Just force a reallocation, will be overwritten - Push(input, file, line ); - - if (Size()==1) - return; - - unsigned writeIndex, readIndex, trueWriteIndex, trueReadIndex; - writeIndex=Size()-1; - readIndex=writeIndex-1; - while (readIndex >= index) - { - if ( head + writeIndex >= allocation_size ) - trueWriteIndex = head + writeIndex - allocation_size; - else - trueWriteIndex = head + writeIndex; - - if ( head + readIndex >= allocation_size ) - trueReadIndex = head + readIndex - allocation_size; - else - trueReadIndex = head + readIndex; - - array[trueWriteIndex]=array[trueReadIndex]; - - if (readIndex==0) - break; - writeIndex--; - readIndex--; - } - - if ( head + index >= allocation_size ) - trueWriteIndex = head + index - allocation_size; - else - trueWriteIndex = head + index; - - array[trueWriteIndex]=input; - } - - - template - inline queue_type Queue::Peek( void ) const - { -#ifdef _DEBUG - RakAssert( head != tail ); -#endif - - return ( queue_type ) array[ head ]; - } - - template - inline queue_type Queue::PeekTail( void ) const - { -#ifdef _DEBUG - RakAssert( head != tail ); -#endif - if (tail!=0) - return ( queue_type ) array[ tail-1 ]; - else - return ( queue_type ) array[ allocation_size-1 ]; - } - - template - void Queue::Push( const queue_type& input, const char *file, unsigned int line ) - { - if ( allocation_size == 0 ) - { - array = RakNet::OP_NEW_ARRAY(16, file, line ); - head = 0; - tail = 1; - array[ 0 ] = input; - allocation_size = 16; - return ; - } - - array[ tail++ ] = input; - - if ( tail == allocation_size ) - tail = 0; - - if ( tail == head ) - { - // unsigned int index=tail; - - // Need to allocate more memory. - queue_type * new_array; - new_array = RakNet::OP_NEW_ARRAY(allocation_size * 2, file, line ); -#ifdef _DEBUG - RakAssert( new_array ); -#endif - if (new_array==0) - return; - - for ( unsigned int counter = 0; counter < allocation_size; ++counter ) - new_array[ counter ] = array[ ( head + counter ) % ( allocation_size ) ]; - - head = 0; - - tail = allocation_size; - - allocation_size *= 2; - - // Delete the old array and move the pointer to the new array - RakNet::OP_DELETE_ARRAY(array, file, line); - - array = new_array; - } - - } - - template - Queue::Queue( Queue& original_copy ) - { - // Allocate memory for copy - - if ( original_copy.Size() == 0 ) - { - allocation_size = 0; - } - - else - { - array = RakNet::OP_NEW_ARRAY( original_copy.Size() + 1 , _FILE_AND_LINE_ ); - - for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) - array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; - - head = 0; - - tail = original_copy.Size(); - - allocation_size = original_copy.Size() + 1; - } - } - - template - bool Queue::operator= ( const Queue& original_copy ) - { - if ( ( &original_copy ) == this ) - return false; - - Clear(_FILE_AND_LINE_); - - // Allocate memory for copy - if ( original_copy.Size() == 0 ) - { - allocation_size = 0; - } - - else - { - array = RakNet::OP_NEW_ARRAY( original_copy.Size() + 1 , _FILE_AND_LINE_ ); - - for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) - array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; - - head = 0; - - tail = original_copy.Size(); - - allocation_size = original_copy.Size() + 1; - } - - return true; - } - - template - inline void Queue::Clear ( const char *file, unsigned int line ) - { - if ( allocation_size == 0 ) - return ; - - if (allocation_size > 32) - { - RakNet::OP_DELETE_ARRAY(array, file, line); - allocation_size = 0; - } - - head = 0; - tail = 0; - } - - template - void Queue::Compress ( const char *file, unsigned int line ) - { - queue_type* new_array; - unsigned int newAllocationSize; - if (allocation_size==0) - return; - - newAllocationSize=1; - while (newAllocationSize <= Size()) - newAllocationSize<<=1; // Must be a better way to do this but I'm too dumb to figure it out quickly :) - - new_array = RakNet::OP_NEW_ARRAY(newAllocationSize, file, line ); - - for (unsigned int counter=0; counter < Size(); ++counter) - new_array[counter] = array[(head + counter)%(allocation_size)]; - - tail=Size(); - allocation_size=newAllocationSize; - head=0; - - // Delete the old array and move the pointer to the new array - RakNet::OP_DELETE_ARRAY(array, file, line); - array=new_array; - } - - template - bool Queue::Find ( queue_type q ) - { - if ( allocation_size == 0 ) - return false; - - unsigned int counter = head; - - while ( counter != tail ) - { - if ( array[ counter ] == q ) - return true; - - counter = ( counter + 1 ) % allocation_size; - } - - return false; - } - - template - void Queue::ClearAndForceAllocation( int size, const char *file, unsigned int line ) - { - RakNet::OP_DELETE_ARRAY(array, file, line); - if (size>0) - array = RakNet::OP_NEW_ARRAY(size, file, line ); - else - array=0; - allocation_size = size; - head = 0; - tail = 0; - } - - template - inline queue_type& Queue::operator[] ( unsigned int position ) const - { -#ifdef _DEBUG - RakAssert( position < Size() ); -#endif - //return array[(head + position) % allocation_size]; - - if ( head + position >= allocation_size ) - return array[ head + position - allocation_size ]; - else - return array[ head + position ]; - } - - template - void Queue::RemoveAtIndex( unsigned int position ) - { -#ifdef _DEBUG - RakAssert( position < Size() ); - RakAssert( head != tail ); -#endif - - if ( head == tail || position >= Size() ) - return ; - - unsigned int index; - - unsigned int next; - - //index = (head + position) % allocation_size; - if ( head + position >= allocation_size ) - index = head + position - allocation_size; - else - index = head + position; - - //next = (index + 1) % allocation_size; - next = index + 1; - - if ( next == allocation_size ) - next = 0; - - while ( next != tail ) - { - // Overwrite the previous element - array[ index ] = array[ next ]; - index = next; - //next = (next + 1) % allocation_size; - - if ( ++next == allocation_size ) - next = 0; - } - - // Move the tail back - if ( tail == 0 ) - tail = allocation_size - 1; - else - --tail; - } -} // End namespace - -#endif - +/// \file DS_Queue.h +/// \internal +/// \brief A queue used by RakNet. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __QUEUE_H +#define __QUEUE_H + +// Template classes have to have all the code in the header file +#include "RakAssert.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief A queue implemented as an array with a read and write index. + template + class RAK_DLL_EXPORT Queue + { + public: + Queue(); + ~Queue(); + Queue( Queue& original_copy ); + bool operator= ( const Queue& original_copy ); + void Push( const queue_type& input, const char *file, unsigned int line ); + void PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line ); + queue_type& operator[] ( unsigned int position ) const; // Not a normal thing you do with a queue but can be used for efficiency + void RemoveAtIndex( unsigned int position ); // Not a normal thing you do with a queue but can be used for efficiency + inline queue_type Peek( void ) const; + inline queue_type PeekTail( void ) const; + inline queue_type Pop( void ); + // Debug: Set pointer to 0, for memory leak detection + inline queue_type PopDeref( void ); + inline unsigned int Size( void ) const; + inline bool IsEmpty(void) const; + inline unsigned int AllocationSize( void ) const; + inline void Clear( const char *file, unsigned int line ); + void Compress( const char *file, unsigned int line ); + bool Find ( queue_type q ); + void ClearAndForceAllocation( int size, const char *file, unsigned int line ); // Force a memory allocation to a certain larger size + + private: + queue_type* array; + unsigned int head; // Array index for the head of the queue + unsigned int tail; // Array index for the tail of the queue + unsigned int allocation_size; + }; + + + template + inline unsigned int Queue::Size( void ) const + { + if ( head <= tail ) + return tail -head; + else + return allocation_size -head + tail; + } + + template + inline bool Queue::IsEmpty(void) const + { + return head==tail; + } + + template + inline unsigned int Queue::AllocationSize( void ) const + { + return allocation_size; + } + + template + Queue::Queue() + { + //allocation_size = 16; + //array = RakNet::OP_NEW_ARRAY(allocation_size, _FILE_AND_LINE_ ); + allocation_size = 0; + array=0; + head = 0; + tail = 0; + } + + template + Queue::~Queue() + { + if (allocation_size>0) + RakNet::OP_DELETE_ARRAY(array, _FILE_AND_LINE_); + } + + template + inline queue_type Queue::Pop( void ) + { +#ifdef _DEBUG + RakAssert( head != tail); +#endif + //head=(head+1) % allocation_size; + + if ( ++head == allocation_size ) + head = 0; + + if ( head == 0 ) + return ( queue_type ) array[ allocation_size -1 ]; + + return ( queue_type ) array[ head -1 ]; + } + + template + inline queue_type Queue::PopDeref( void ) + { + if ( ++head == allocation_size ) + head = 0; + + queue_type q; + if ( head == 0 ) + { + q=array[ allocation_size -1 ]; + array[ allocation_size -1 ]=0; + return q; + } + + q=array[ head -1 ]; + array[ head -1 ]=0; + return q; + } + + template + void Queue::PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line ) + { + RakAssert(index <= Size()); + + // Just force a reallocation, will be overwritten + Push(input, file, line ); + + if (Size()==1) + return; + + unsigned writeIndex, readIndex, trueWriteIndex, trueReadIndex; + writeIndex=Size()-1; + readIndex=writeIndex-1; + while (readIndex >= index) + { + if ( head + writeIndex >= allocation_size ) + trueWriteIndex = head + writeIndex - allocation_size; + else + trueWriteIndex = head + writeIndex; + + if ( head + readIndex >= allocation_size ) + trueReadIndex = head + readIndex - allocation_size; + else + trueReadIndex = head + readIndex; + + array[trueWriteIndex]=array[trueReadIndex]; + + if (readIndex==0) + break; + writeIndex--; + readIndex--; + } + + if ( head + index >= allocation_size ) + trueWriteIndex = head + index - allocation_size; + else + trueWriteIndex = head + index; + + array[trueWriteIndex]=input; + } + + + template + inline queue_type Queue::Peek( void ) const + { +#ifdef _DEBUG + RakAssert( head != tail ); +#endif + + return ( queue_type ) array[ head ]; + } + + template + inline queue_type Queue::PeekTail( void ) const + { +#ifdef _DEBUG + RakAssert( head != tail ); +#endif + if (tail!=0) + return ( queue_type ) array[ tail-1 ]; + else + return ( queue_type ) array[ allocation_size-1 ]; + } + + template + void Queue::Push( const queue_type& input, const char *file, unsigned int line ) + { + if ( allocation_size == 0 ) + { + array = RakNet::OP_NEW_ARRAY(16, file, line ); + head = 0; + tail = 1; + array[ 0 ] = input; + allocation_size = 16; + return ; + } + + array[ tail++ ] = input; + + if ( tail == allocation_size ) + tail = 0; + + if ( tail == head ) + { + // unsigned int index=tail; + + // Need to allocate more memory. + queue_type * new_array; + new_array = RakNet::OP_NEW_ARRAY(allocation_size * 2, file, line ); +#ifdef _DEBUG + RakAssert( new_array ); +#endif + if (new_array==0) + return; + + for ( unsigned int counter = 0; counter < allocation_size; ++counter ) + new_array[ counter ] = array[ ( head + counter ) % ( allocation_size ) ]; + + head = 0; + + tail = allocation_size; + + allocation_size *= 2; + + // Delete the old array and move the pointer to the new array + RakNet::OP_DELETE_ARRAY(array, file, line); + + array = new_array; + } + + } + + template + Queue::Queue( Queue& original_copy ) + { + // Allocate memory for copy + + if ( original_copy.Size() == 0 ) + { + allocation_size = 0; + } + + else + { + array = RakNet::OP_NEW_ARRAY( original_copy.Size() + 1 , _FILE_AND_LINE_ ); + + for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) + array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; + + head = 0; + + tail = original_copy.Size(); + + allocation_size = original_copy.Size() + 1; + } + } + + template + bool Queue::operator= ( const Queue& original_copy ) + { + if ( ( &original_copy ) == this ) + return false; + + Clear(_FILE_AND_LINE_); + + // Allocate memory for copy + if ( original_copy.Size() == 0 ) + { + allocation_size = 0; + } + + else + { + array = RakNet::OP_NEW_ARRAY( original_copy.Size() + 1 , _FILE_AND_LINE_ ); + + for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) + array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; + + head = 0; + + tail = original_copy.Size(); + + allocation_size = original_copy.Size() + 1; + } + + return true; + } + + template + inline void Queue::Clear ( const char *file, unsigned int line ) + { + if ( allocation_size == 0 ) + return ; + + if (allocation_size > 32) + { + RakNet::OP_DELETE_ARRAY(array, file, line); + allocation_size = 0; + } + + head = 0; + tail = 0; + } + + template + void Queue::Compress ( const char *file, unsigned int line ) + { + queue_type* new_array; + unsigned int newAllocationSize; + if (allocation_size==0) + return; + + newAllocationSize=1; + while (newAllocationSize <= Size()) + newAllocationSize<<=1; // Must be a better way to do this but I'm too dumb to figure it out quickly :) + + new_array = RakNet::OP_NEW_ARRAY(newAllocationSize, file, line ); + + for (unsigned int counter=0; counter < Size(); ++counter) + new_array[counter] = array[(head + counter)%(allocation_size)]; + + tail=Size(); + allocation_size=newAllocationSize; + head=0; + + // Delete the old array and move the pointer to the new array + RakNet::OP_DELETE_ARRAY(array, file, line); + array=new_array; + } + + template + bool Queue::Find ( queue_type q ) + { + if ( allocation_size == 0 ) + return false; + + unsigned int counter = head; + + while ( counter != tail ) + { + if ( array[ counter ] == q ) + return true; + + counter = ( counter + 1 ) % allocation_size; + } + + return false; + } + + template + void Queue::ClearAndForceAllocation( int size, const char *file, unsigned int line ) + { + RakNet::OP_DELETE_ARRAY(array, file, line); + if (size>0) + array = RakNet::OP_NEW_ARRAY(size, file, line ); + else + array=0; + allocation_size = size; + head = 0; + tail = 0; + } + + template + inline queue_type& Queue::operator[] ( unsigned int position ) const + { +#ifdef _DEBUG + RakAssert( position < Size() ); +#endif + //return array[(head + position) % allocation_size]; + + if ( head + position >= allocation_size ) + return array[ head + position - allocation_size ]; + else + return array[ head + position ]; + } + + template + void Queue::RemoveAtIndex( unsigned int position ) + { +#ifdef _DEBUG + RakAssert( position < Size() ); + RakAssert( head != tail ); +#endif + + if ( head == tail || position >= Size() ) + return ; + + unsigned int index; + + unsigned int next; + + //index = (head + position) % allocation_size; + if ( head + position >= allocation_size ) + index = head + position - allocation_size; + else + index = head + position; + + //next = (index + 1) % allocation_size; + next = index + 1; + + if ( next == allocation_size ) + next = 0; + + while ( next != tail ) + { + // Overwrite the previous element + array[ index ] = array[ next ]; + index = next; + //next = (next + 1) % allocation_size; + + if ( ++next == allocation_size ) + next = 0; + } + + // Move the tail back + if ( tail == 0 ) + tail = allocation_size - 1; + else + --tail; + } +} // End namespace + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_QueueLinkedList.h b/project/lib_projects/raknet/jni/RaknetSources/DS_QueueLinkedList.h index 0e1d234..4b3499c 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_QueueLinkedList.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_QueueLinkedList.h @@ -1,103 +1,103 @@ -/// \file DS_QueueLinkedList.h -/// \internal -/// \brief A queue implemented as a linked list. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __QUEUE_LINKED_LIST_H -#define __QUEUE_LINKED_LIST_H - -#include "DS_LinkedList.h" -#include "Export.h" -#include "RakMemoryOverride.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// \brief A queue implemented using a linked list. Rarely used. - template - class RAK_DLL_EXPORT QueueLinkedList - { - - public: - QueueLinkedList(); - QueueLinkedList( const QueueLinkedList& original_copy ); - bool operator= ( const QueueLinkedList& original_copy ); - QueueType Pop( void ); - QueueType& Peek( void ); - QueueType& EndPeek( void ); - void Push( const QueueType& input ); - unsigned int Size( void ); - void Clear( void ); - void Compress( void ); - - private: - LinkedList data; - }; - - template - QueueLinkedList::QueueLinkedList() - { - } - - template - inline unsigned int QueueLinkedList::Size() - { - return data.Size(); - } - - template - inline QueueType QueueLinkedList::Pop( void ) - { - data.Beginning(); - return ( QueueType ) data.Pop(); - } - - template - inline QueueType& QueueLinkedList::Peek( void ) - { - data.Beginning(); - return ( QueueType ) data.Peek(); - } - - template - inline QueueType& QueueLinkedList::EndPeek( void ) - { - data.End(); - return ( QueueType ) data.Peek(); - } - - template - void QueueLinkedList::Push( const QueueType& input ) - { - data.End(); - data.Add( input ); - } - - template - QueueLinkedList::QueueLinkedList( const QueueLinkedList& original_copy ) - { - data = original_copy.data; - } - - template - bool QueueLinkedList::operator= ( const QueueLinkedList& original_copy ) - { - if ( ( &original_copy ) == this ) - return false; - - data = original_copy.data; - } - - template - void QueueLinkedList::Clear ( void ) - { - data.Clear(); - } -} // End namespace - -#endif +/// \file DS_QueueLinkedList.h +/// \internal +/// \brief A queue implemented as a linked list. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __QUEUE_LINKED_LIST_H +#define __QUEUE_LINKED_LIST_H + +#include "DS_LinkedList.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief A queue implemented using a linked list. Rarely used. + template + class RAK_DLL_EXPORT QueueLinkedList + { + + public: + QueueLinkedList(); + QueueLinkedList( const QueueLinkedList& original_copy ); + bool operator= ( const QueueLinkedList& original_copy ); + QueueType Pop( void ); + QueueType& Peek( void ); + QueueType& EndPeek( void ); + void Push( const QueueType& input ); + unsigned int Size( void ); + void Clear( void ); + void Compress( void ); + + private: + LinkedList data; + }; + + template + QueueLinkedList::QueueLinkedList() + { + } + + template + inline unsigned int QueueLinkedList::Size() + { + return data.Size(); + } + + template + inline QueueType QueueLinkedList::Pop( void ) + { + data.Beginning(); + return ( QueueType ) data.Pop(); + } + + template + inline QueueType& QueueLinkedList::Peek( void ) + { + data.Beginning(); + return ( QueueType ) data.Peek(); + } + + template + inline QueueType& QueueLinkedList::EndPeek( void ) + { + data.End(); + return ( QueueType ) data.Peek(); + } + + template + void QueueLinkedList::Push( const QueueType& input ) + { + data.End(); + data.Add( input ); + } + + template + QueueLinkedList::QueueLinkedList( const QueueLinkedList& original_copy ) + { + data = original_copy.data; + } + + template + bool QueueLinkedList::operator= ( const QueueLinkedList& original_copy ) + { + if ( ( &original_copy ) == this ) + return false; + + data = original_copy.data; + } + + template + void QueueLinkedList::Clear ( void ) + { + data.Clear(); + } +} // End namespace + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_RangeList.h b/project/lib_projects/raknet/jni/RaknetSources/DS_RangeList.h index b40b859..4136737 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_RangeList.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_RangeList.h @@ -1,236 +1,236 @@ -/// \file DS_RangeList.h -/// \internal -/// \brief A queue implemented as a linked list. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RANGE_LIST_H -#define __RANGE_LIST_H - -#include "DS_OrderedList.h" -#include "BitStream.h" -#include "RakMemoryOverride.h" -#include "RakAssert.h" - -namespace DataStructures -{ - template - struct RangeNode - { - RangeNode() {} - ~RangeNode() {} - RangeNode(range_type min, range_type max) {minIndex=min; maxIndex=max;} - range_type minIndex; - range_type maxIndex; - }; - - - template - int RangeNodeComp(const range_type &a, const RangeNode &b) - { - if (a - class RAK_DLL_EXPORT RangeList - { - public: - RangeList(); - ~RangeList(); - void Insert(range_type index); - void Clear(void); - unsigned Size(void) const; - unsigned RangeSum(void) const; - RakNet::BitSize_t Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized); - bool Deserialize(RakNet::BitStream *out); - - DataStructures::OrderedList , RangeNodeComp > ranges; - }; - - template - RakNet::BitSize_t RangeList::Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized) - { - RakAssert(ranges.Size() < (unsigned short)-1); - RakNet::BitStream tempBS; - RakNet::BitSize_t bitsWritten; - unsigned short countWritten; - unsigned i; - countWritten=0; - bitsWritten=0; - for (i=0; i < ranges.Size(); i++) - { - if ((int)sizeof(unsigned short)*8+bitsWritten+(int)sizeof(range_type)*8*2+1>maxBits) - break; - unsigned char minEqualsMax; - if (ranges[i].minIndex==ranges[i].maxIndex) - minEqualsMax=1; - else - minEqualsMax=0; - tempBS.Write(minEqualsMax); // Use one byte, intead of one bit, for speed, as this is done a lot - tempBS.Write(ranges[i].minIndex); - bitsWritten+=sizeof(range_type)*8+8; - if (ranges[i].minIndex!=ranges[i].maxIndex) - { - tempBS.Write(ranges[i].maxIndex); - bitsWritten+=sizeof(range_type)*8; - } - countWritten++; - } - - in->AlignWriteToByteBoundary(); - RakNet::BitSize_t before=in->GetWriteOffset(); - in->Write(countWritten); - bitsWritten+=in->GetWriteOffset()-before; - // RAKNET_DEBUG_PRINTF("%i ", in->GetNumberOfBitsUsed()); - in->Write(&tempBS, tempBS.GetNumberOfBitsUsed()); - // RAKNET_DEBUG_PRINTF("%i %i \n", tempBS.GetNumberOfBitsUsed(),in->GetNumberOfBitsUsed()); - - if (clearSerialized && countWritten) - { - unsigned rangeSize=ranges.Size(); - for (i=0; i < rangeSize-countWritten; i++) - { - ranges[i]=ranges[i+countWritten]; - } - ranges.RemoveFromEnd(countWritten); - } - - return bitsWritten; - } - template - bool RangeList::Deserialize(RakNet::BitStream *out) - { - ranges.Clear(true, _FILE_AND_LINE_); - unsigned short count; - out->AlignReadToByteBoundary(); - out->Read(count); - unsigned short i; - range_type min,max; - unsigned char maxEqualToMin=0; - - for (i=0; i < count; i++) - { - out->Read(maxEqualToMin); - if (out->Read(min)==false) - return false; - if (maxEqualToMin==false) - { - if (out->Read(max)==false) - return false; - if (max(min,max), _FILE_AND_LINE_); - } - return true; - } - - template - RangeList::RangeList() - { - RangeNodeComp(0, RangeNode()); - } - - template - RangeList::~RangeList() - { - Clear(); - } - - template - void RangeList::Insert(range_type index) - { - if (ranges.Size()==0) - { - ranges.Insert(index, RangeNode(index, index), true, _FILE_AND_LINE_); - return; - } - - bool objectExists; - unsigned insertionIndex=ranges.GetIndexFromKey(index, &objectExists); - if (insertionIndex==ranges.Size()) - { - if (index == ranges[insertionIndex-1].maxIndex+(range_type)1) - ranges[insertionIndex-1].maxIndex++; - else if (index > ranges[insertionIndex-1].maxIndex+(range_type)1) - { - // Insert at end - ranges.Insert(index, RangeNode(index, index), true, _FILE_AND_LINE_); - } - - return; - } - - if (index < ranges[insertionIndex].minIndex-(range_type)1) - { - // Insert here - ranges.InsertAtIndex(RangeNode(index, index), insertionIndex, _FILE_AND_LINE_); - - return; - } - else if (index == ranges[insertionIndex].minIndex-(range_type)1) - { - // Decrease minIndex and join left - ranges[insertionIndex].minIndex--; - if (insertionIndex>0 && ranges[insertionIndex-1].maxIndex+(range_type)1==ranges[insertionIndex].minIndex) - { - ranges[insertionIndex-1].maxIndex=ranges[insertionIndex].maxIndex; - ranges.RemoveAtIndex(insertionIndex); - } - - return; - } - else if (index >= ranges[insertionIndex].minIndex && index <= ranges[insertionIndex].maxIndex) - { - // Already exists - return; - } - else if (index == ranges[insertionIndex].maxIndex+(range_type)1) - { - // Increase maxIndex and join right - ranges[insertionIndex].maxIndex++; - if (insertionIndex - void RangeList::Clear(void) - { - ranges.Clear(true, _FILE_AND_LINE_); - } - - template - unsigned RangeList::Size(void) const - { - return ranges.Size(); - } - - template - unsigned RangeList::RangeSum(void) const - { - unsigned sum=0,i; - for (i=0; i < ranges.Size(); i++) - sum+=ranges[i].maxIndex-ranges[i].minIndex+1; - return sum; - } - -} - -#endif +/// \file DS_RangeList.h +/// \internal +/// \brief A queue implemented as a linked list. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RANGE_LIST_H +#define __RANGE_LIST_H + +#include "DS_OrderedList.h" +#include "BitStream.h" +#include "RakMemoryOverride.h" +#include "RakAssert.h" + +namespace DataStructures +{ + template + struct RangeNode + { + RangeNode() {} + ~RangeNode() {} + RangeNode(range_type min, range_type max) {minIndex=min; maxIndex=max;} + range_type minIndex; + range_type maxIndex; + }; + + + template + int RangeNodeComp(const range_type &a, const RangeNode &b) + { + if (a + class RAK_DLL_EXPORT RangeList + { + public: + RangeList(); + ~RangeList(); + void Insert(range_type index); + void Clear(void); + unsigned Size(void) const; + unsigned RangeSum(void) const; + RakNet::BitSize_t Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized); + bool Deserialize(RakNet::BitStream *out); + + DataStructures::OrderedList , RangeNodeComp > ranges; + }; + + template + RakNet::BitSize_t RangeList::Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized) + { + RakAssert(ranges.Size() < (unsigned short)-1); + RakNet::BitStream tempBS; + RakNet::BitSize_t bitsWritten; + unsigned short countWritten; + unsigned i; + countWritten=0; + bitsWritten=0; + for (i=0; i < ranges.Size(); i++) + { + if ((int)sizeof(unsigned short)*8+bitsWritten+(int)sizeof(range_type)*8*2+1>maxBits) + break; + unsigned char minEqualsMax; + if (ranges[i].minIndex==ranges[i].maxIndex) + minEqualsMax=1; + else + minEqualsMax=0; + tempBS.Write(minEqualsMax); // Use one byte, intead of one bit, for speed, as this is done a lot + tempBS.Write(ranges[i].minIndex); + bitsWritten+=sizeof(range_type)*8+8; + if (ranges[i].minIndex!=ranges[i].maxIndex) + { + tempBS.Write(ranges[i].maxIndex); + bitsWritten+=sizeof(range_type)*8; + } + countWritten++; + } + + in->AlignWriteToByteBoundary(); + RakNet::BitSize_t before=in->GetWriteOffset(); + in->Write(countWritten); + bitsWritten+=in->GetWriteOffset()-before; + // RAKNET_DEBUG_PRINTF("%i ", in->GetNumberOfBitsUsed()); + in->Write(&tempBS, tempBS.GetNumberOfBitsUsed()); + // RAKNET_DEBUG_PRINTF("%i %i \n", tempBS.GetNumberOfBitsUsed(),in->GetNumberOfBitsUsed()); + + if (clearSerialized && countWritten) + { + unsigned rangeSize=ranges.Size(); + for (i=0; i < rangeSize-countWritten; i++) + { + ranges[i]=ranges[i+countWritten]; + } + ranges.RemoveFromEnd(countWritten); + } + + return bitsWritten; + } + template + bool RangeList::Deserialize(RakNet::BitStream *out) + { + ranges.Clear(true, _FILE_AND_LINE_); + unsigned short count; + out->AlignReadToByteBoundary(); + out->Read(count); + unsigned short i; + range_type min,max; + unsigned char maxEqualToMin=0; + + for (i=0; i < count; i++) + { + out->Read(maxEqualToMin); + if (out->Read(min)==false) + return false; + if (maxEqualToMin==false) + { + if (out->Read(max)==false) + return false; + if (max(min,max), _FILE_AND_LINE_); + } + return true; + } + + template + RangeList::RangeList() + { + RangeNodeComp(0, RangeNode()); + } + + template + RangeList::~RangeList() + { + Clear(); + } + + template + void RangeList::Insert(range_type index) + { + if (ranges.Size()==0) + { + ranges.Insert(index, RangeNode(index, index), true, _FILE_AND_LINE_); + return; + } + + bool objectExists; + unsigned insertionIndex=ranges.GetIndexFromKey(index, &objectExists); + if (insertionIndex==ranges.Size()) + { + if (index == ranges[insertionIndex-1].maxIndex+(range_type)1) + ranges[insertionIndex-1].maxIndex++; + else if (index > ranges[insertionIndex-1].maxIndex+(range_type)1) + { + // Insert at end + ranges.Insert(index, RangeNode(index, index), true, _FILE_AND_LINE_); + } + + return; + } + + if (index < ranges[insertionIndex].minIndex-(range_type)1) + { + // Insert here + ranges.InsertAtIndex(RangeNode(index, index), insertionIndex, _FILE_AND_LINE_); + + return; + } + else if (index == ranges[insertionIndex].minIndex-(range_type)1) + { + // Decrease minIndex and join left + ranges[insertionIndex].minIndex--; + if (insertionIndex>0 && ranges[insertionIndex-1].maxIndex+(range_type)1==ranges[insertionIndex].minIndex) + { + ranges[insertionIndex-1].maxIndex=ranges[insertionIndex].maxIndex; + ranges.RemoveAtIndex(insertionIndex); + } + + return; + } + else if (index >= ranges[insertionIndex].minIndex && index <= ranges[insertionIndex].maxIndex) + { + // Already exists + return; + } + else if (index == ranges[insertionIndex].maxIndex+(range_type)1) + { + // Increase maxIndex and join right + ranges[insertionIndex].maxIndex++; + if (insertionIndex + void RangeList::Clear(void) + { + ranges.Clear(true, _FILE_AND_LINE_); + } + + template + unsigned RangeList::Size(void) const + { + return ranges.Size(); + } + + template + unsigned RangeList::RangeSum(void) const + { + unsigned sum=0,i; + for (i=0; i < ranges.Size(); i++) + sum+=ranges[i].maxIndex-ranges[i].minIndex+1; + return sum; + } + +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_Table.cpp b/project/lib_projects/raknet/jni/RaknetSources/DS_Table.cpp index 281a60f..0e35893 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_Table.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_Table.cpp @@ -1,1118 +1,1118 @@ -#include "DS_Table.h" -#include "DS_OrderedList.h" -#include -#include "RakAssert.h" -#include "RakAssert.h" -#include "Itoa.h" - -using namespace DataStructures; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -void ExtendRows(Table::Row* input, int index) -{ - (void) index; - input->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); -} -void FreeRow(Table::Row* input, int index) -{ - (void) index; - - unsigned i; - for (i=0; i < input->cells.Size(); i++) - { - RakNet::OP_DELETE(input->cells[i], _FILE_AND_LINE_); - } - RakNet::OP_DELETE(input, _FILE_AND_LINE_); -} -Table::Cell::Cell() -{ - isEmpty=true; - c=0; - ptr=0; - i=0.0; -} -Table::Cell::~Cell() -{ - Clear(); -} -Table::Cell& Table::Cell::operator = ( const Table::Cell& input ) -{ - isEmpty=input.isEmpty; - i=input.i; - ptr=input.ptr; - if (c) - rakFree_Ex(c, _FILE_AND_LINE_); - if (input.c) - { - c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); - memcpy(c, input.c, (int) i); - } - else - c=0; - return *this; -} -Table::Cell::Cell( const Table::Cell & input) -{ - isEmpty=input.isEmpty; - i=input.i; - ptr=input.ptr; - if (input.c) - { - if (c) - rakFree_Ex(c, _FILE_AND_LINE_); - c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); - memcpy(c, input.c, (int) i); - } -} -void Table::Cell::Set(double input) -{ - Clear(); - i=input; - c=0; - ptr=0; - isEmpty=false; -} -void Table::Cell::Set(unsigned int input) -{ - Set((int) input); -} -void Table::Cell::Set(int input) -{ - Clear(); - i=(double) input; - c=0; - ptr=0; - isEmpty=false; -} - -void Table::Cell::Set(const char *input) -{ - Clear(); - - if (input) - { - i=(int)strlen(input)+1; - c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); - strcpy(c, input); - } - else - { - c=0; - i=0; - } - ptr=0; - isEmpty=false; -} -void Table::Cell::Set(const char *input, int inputLength) -{ - Clear(); - if (input) - { - c = (char*) rakMalloc_Ex( inputLength, _FILE_AND_LINE_ ); - i=inputLength; - memcpy(c, input, inputLength); - } - else - { - c=0; - i=0; - } - ptr=0; - isEmpty=false; -} -void Table::Cell::SetPtr(void* p) -{ - Clear(); - c=0; - ptr=p; - isEmpty=false; -} -void Table::Cell::Get(int *output) -{ - RakAssert(isEmpty==false); - int o = (int) i; - *output=o; -} -void Table::Cell::Get(double *output) -{ - RakAssert(isEmpty==false); - *output=i; -} -void Table::Cell::Get(char *output) -{ - RakAssert(isEmpty==false); - strcpy(output, c); -} -void Table::Cell::Get(char *output, int *outputLength) -{ - RakAssert(isEmpty==false); - memcpy(output, c, (int) i); - if (outputLength) - *outputLength=(int) i; -} -RakNet::RakString Table::Cell::ToString(ColumnType columnType) -{ - if (isEmpty) - return RakNet::RakString(); - - if (columnType==NUMERIC) - { - return RakNet::RakString("%f", i); - } - else if (columnType==STRING) - { - return RakNet::RakString(c); - } - else if (columnType==BINARY) - { - return RakNet::RakString(""); - } - else if (columnType==POINTER) - { - return RakNet::RakString("%p", ptr); - } - - return RakNet::RakString(); -} -Table::Cell::Cell(double numericValue, char *charValue, void *ptr, ColumnType type) -{ - SetByType(numericValue,charValue,ptr,type); -} -void Table::Cell::SetByType(double numericValue, char *charValue, void *ptr, ColumnType type) -{ - isEmpty=true; - if (type==NUMERIC) - { - Set(numericValue); - } - else if (type==STRING) - { - Set(charValue); - } - else if (type==BINARY) - { - Set(charValue, (int) numericValue); - } - else if (type==POINTER) - { - SetPtr(ptr); - } - else - { - ptr=(void*) charValue; - } -} -Table::ColumnType Table::Cell::EstimateColumnType(void) const -{ - if (c) - if (i!=0.0f) - return BINARY; - else - return STRING; - if (ptr) - return POINTER; - return NUMERIC; -} -void Table::Cell::Clear(void) -{ - if (isEmpty==false && c) - { - rakFree_Ex(c, _FILE_AND_LINE_); - c=0; - } - isEmpty=true; -} -Table::ColumnDescriptor::ColumnDescriptor() -{ - -} -Table::ColumnDescriptor::~ColumnDescriptor() -{ - -} -Table::ColumnDescriptor::ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType ct) -{ - columnType=ct; - strcpy(columnName, cn); -} -void Table::Row::UpdateCell(unsigned columnIndex, double value) -{ - cells[columnIndex]->Clear(); - cells[columnIndex]->Set(value); - -// cells[columnIndex]->i=value; -// cells[columnIndex]->c=0; -// cells[columnIndex]->isEmpty=false; -} -void Table::Row::UpdateCell(unsigned columnIndex, const char *str) -{ - cells[columnIndex]->Clear(); - cells[columnIndex]->Set(str); -} -void Table::Row::UpdateCell(unsigned columnIndex, int byteLength, const char *data) -{ - cells[columnIndex]->Clear(); - cells[columnIndex]->Set(data,byteLength); -} -Table::Table() -{ -} -Table::~Table() -{ - Clear(); -} -unsigned Table::AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType) -{ - if (columnName[0]==0) - return (unsigned) -1; - - // Add this column. - columns.Insert(Table::ColumnDescriptor(columnName, columnType), _FILE_AND_LINE_); - - // Extend the rows by one - rows.ForEachData(ExtendRows); - - return columns.Size()-1; -} -void Table::RemoveColumn(unsigned columnIndex) -{ - if (columnIndex >= columns.Size()) - return; - - columns.RemoveAtIndex(columnIndex); - - // Remove this index from each row. - int i; - DataStructures::Page *cur = rows.GetListHead(); - while (cur) - { - for (i=0; i < cur->size; i++) - { - RakNet::OP_DELETE(cur->data[i]->cells[columnIndex], _FILE_AND_LINE_); - cur->data[i]->cells.RemoveAtIndex(columnIndex); - } - - cur=cur->next; - } -} -unsigned Table::ColumnIndex(const char *columnName) const -{ - unsigned columnIndex; - for (columnIndex=0; columnIndex= columns.Size()) - return 0; - else - return (char*)columns[index].columnName; -} -Table::ColumnType Table::GetColumnType(unsigned index) const -{ - if (index >= columns.Size()) - return (Table::ColumnType) 0; - else - return columns[index].columnType; -} -unsigned Table::GetColumnCount(void) const -{ - return columns.Size(); -} -unsigned Table::GetRowCount(void) const -{ - return rows.Size(); -} -Table::Row* Table::AddRow(unsigned rowId) -{ - Row *newRow; - newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); - if (rows.Insert(rowId, newRow)==false) - { - RakNet::OP_DELETE(newRow, _FILE_AND_LINE_); - return 0; // Already exists - } - unsigned rowIndex; - for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) - newRow->cells.Insert( RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); - return newRow; -} -Table::Row* Table::AddRow(unsigned rowId, DataStructures::List &initialCellValues) -{ - Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); - unsigned rowIndex; - for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) - { - if (rowIndex < initialCellValues.Size() && initialCellValues[rowIndex].isEmpty==false) - { - Table::Cell *c; - c = RakNet::OP_NEW(_FILE_AND_LINE_); - c->SetByType(initialCellValues[rowIndex].i,initialCellValues[rowIndex].c,initialCellValues[rowIndex].ptr,columns[rowIndex].columnType); - newRow->cells.Insert(c, _FILE_AND_LINE_ ); - } - else - newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); - } - rows.Insert(rowId, newRow); - return newRow; -} -Table::Row* Table::AddRow(unsigned rowId, DataStructures::List &initialCellValues, bool copyCells) -{ - Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); - unsigned rowIndex; - for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) - { - if (rowIndex < initialCellValues.Size() && initialCellValues[rowIndex] && initialCellValues[rowIndex]->isEmpty==false) - { - if (copyCells==false) - newRow->cells.Insert(RakNet::OP_NEW_4( _FILE_AND_LINE_, initialCellValues[rowIndex]->i, initialCellValues[rowIndex]->c, initialCellValues[rowIndex]->ptr, columns[rowIndex].columnType), _FILE_AND_LINE_); - else - { - Table::Cell *c = RakNet::OP_NEW( _FILE_AND_LINE_ ); - newRow->cells.Insert(c, _FILE_AND_LINE_); - *c=*(initialCellValues[rowIndex]); - } - } - else - newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_); - } - rows.Insert(rowId, newRow); - return newRow; -} -Table::Row* Table::AddRowColumns(unsigned rowId, Row *row, DataStructures::List columnIndices) -{ - Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); - unsigned columnIndex; - for (columnIndex=0; columnIndex < columnIndices.Size(); columnIndex++) - { - if (row->cells[columnIndices[columnIndex]]->isEmpty==false) - { - newRow->cells.Insert(RakNet::OP_NEW_4( _FILE_AND_LINE_, - row->cells[columnIndices[columnIndex]]->i, - row->cells[columnIndices[columnIndex]]->c, - row->cells[columnIndices[columnIndex]]->ptr, - columns[columnIndex].columnType - ), _FILE_AND_LINE_); - } - else - { - newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_); - } - } - rows.Insert(rowId, newRow); - return newRow; -} -bool Table::RemoveRow(unsigned rowId) -{ - Row *out; - if (rows.Delete(rowId, out)) - { - DeleteRow(out); - return true; - } - return false; -} -void Table::RemoveRows(Table *tableContainingRowIDs) -{ - unsigned i; - DataStructures::Page *cur = tableContainingRowIDs->GetRows().GetListHead(); - while (cur) - { - for (i=0; i < (unsigned)cur->size; i++) - { - rows.Delete(cur->keys[i]); - } - cur=cur->next; - } - return; -} -bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int value) -{ - RakAssert(columns[columnIndex].columnType==NUMERIC); - - Row *row = GetRowByID(rowId); - if (row) - { - row->UpdateCell(columnIndex, value); - return true; - } - return false; -} -bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, char *str) -{ - RakAssert(columns[columnIndex].columnType==STRING); - - Row *row = GetRowByID(rowId); - if (row) - { - row->UpdateCell(columnIndex, str); - return true; - } - return false; -} -bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data) -{ - RakAssert(columns[columnIndex].columnType==BINARY); - - Row *row = GetRowByID(rowId); - if (row) - { - row->UpdateCell(columnIndex, byteLength, data); - return true; - } - return false; -} -bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value) -{ - RakAssert(columns[columnIndex].columnType==NUMERIC); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->UpdateCell(columnIndex, value); - return true; - } - return false; -} -bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str) -{ - RakAssert(columns[columnIndex].columnType==STRING); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->UpdateCell(columnIndex, str); - return true; - } - return false; -} -bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data) -{ - RakAssert(columns[columnIndex].columnType==BINARY); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->UpdateCell(columnIndex, byteLength, data); - return true; - } - return false; -} -void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output) -{ - RakAssert(columns[columnIndex].columnType==NUMERIC); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->cells[columnIndex]->Get(output); - } -} -void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output) -{ - RakAssert(columns[columnIndex].columnType==STRING); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->cells[columnIndex]->Get(output); - } -} -void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength) -{ - RakAssert(columns[columnIndex].columnType==BINARY); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->cells[columnIndex]->Get(output, outputLength); - } -} -Table::FilterQuery::FilterQuery() -{ - columnName[0]=0; -} -Table::FilterQuery::~FilterQuery() -{ - -} -Table::FilterQuery::FilterQuery(unsigned column, Cell *cell, FilterQueryType op) -{ - columnIndex=column; - cellValue=cell; - operation=op; -} -Table::Row* Table::GetRowByID(unsigned rowId) const -{ - Row *row; - if (rows.Get(rowId, row)) - return row; - return 0; -} - -Table::Row* Table::GetRowByIndex(unsigned rowIndex, unsigned *key) const -{ - DataStructures::Page *cur = rows.GetListHead(); - while (cur) - { - if (rowIndex < (unsigned)cur->size) - { - if (key) - *key=cur->keys[rowIndex]; - return cur->data[rowIndex]; - } - if (rowIndex <= (unsigned)cur->size) - rowIndex-=cur->size; - else - return 0; - cur=cur->next; - } - return 0; -} - -void Table::QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result) -{ - unsigned i; - DataStructures::List columnIndicesToReturn; - - // Clear the result table. - result->Clear(); - - if (columnIndicesSubset && numColumnSubset>0) - { - for (i=0; i < numColumnSubset; i++) - { - if (columnIndicesSubset[i]AddColumn(columns[columnIndicesToReturn[i]].columnName,columns[columnIndicesToReturn[i]].columnType); - } - - // Get the column indices of the filter queries. - DataStructures::List inclusionFilterColumnIndices; - if (inclusionFilters && numInclusionFilters>0) - { - for (i=0; i < numInclusionFilters; i++) - { - if (inclusionFilters[i].columnName[0]) - inclusionFilters[i].columnIndex=ColumnIndex(inclusionFilters[i].columnName); - if (inclusionFilters[i].columnIndex *cur = rows.GetListHead(); - while (cur) - { - for (i=0; i < (unsigned)cur->size; i++) - { - QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, cur->keys[i], cur->data[i], inclusionFilters, result); - } - cur=cur->next; - } - } - else - { - // Specific rows - Row *row; - for (i=0; i < numRowIDs; i++) - { - if (rows.Get(rowIds[i], row)) - { - QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, rowIds[i], row, inclusionFilters, result); - } - } - } -} - -void Table::QueryRow(DataStructures::List &inclusionFilterColumnIndices, DataStructures::List &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result) -{ - bool pass=false; - unsigned columnIndex; - unsigned j; - - // If no inclusion filters, just add the row - if (inclusionFilterColumnIndices.Size()==0) - { - result->AddRowColumns(key, row, columnIndicesToReturn); - } - else - { - // Go through all inclusion filters. Only add this row if all filters pass. - for (j=0; jcells[columnIndex]->isEmpty==false ) - { - if (columns[inclusionFilterColumnIndices[j]].columnType==STRING && - (row->cells[columnIndex]->c==0 || - inclusionFilters[j].cellValue->c==0) ) - continue; - - switch (inclusionFilters[j].operation) - { - case QF_EQUAL: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)==0; - break; - case BINARY: - pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i && - memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, (int) row->cells[columnIndex]->i)==0; - break; - case POINTER: - pass=row->cells[columnIndex]->ptr==inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_NOT_EQUAL: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i!=inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)!=0; - break; - case BINARY: - pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i && - memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, (int) row->cells[columnIndex]->i)==0; - break; - case POINTER: - pass=row->cells[columnIndex]->ptr!=inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_GREATER_THAN: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i>inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)>0; - break; - case BINARY: - break; - case POINTER: - pass=row->cells[columnIndex]->ptr>inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_GREATER_THAN_EQ: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i>=inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)>=0; - break; - case BINARY: - break; - case POINTER: - pass=row->cells[columnIndex]->ptr>=inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_LESS_THAN: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->ii; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)<0; - break; - case BINARY: - break; - case POINTER: - pass=row->cells[columnIndex]->ptrptr; - break; - } - break; - case QF_LESS_THAN_EQ: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i<=inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)<=0; - break; - case BINARY: - break; - case POINTER: - pass=row->cells[columnIndex]->ptr<=inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_IS_EMPTY: - pass=false; - break; - case QF_NOT_EMPTY: - pass=true; - break; - default: - pass=false; - RakAssert(0); - break; - } - } - else - { - if (inclusionFilters[j].operation==QF_IS_EMPTY) - pass=true; - else - pass=false; // No value for this cell - } - - if (pass==false) - break; - } - - if (pass) - { - result->AddRowColumns(key, row, columnIndicesToReturn); - } - } -} - -static Table::SortQuery *_sortQueries; -static unsigned _numSortQueries; -static DataStructures::List *_columnIndices; -static DataStructures::List *_columns; -int RowSort(Table::Row* const &first, Table::Row* const &second) // first is the one inserting, second is the one already there. -{ - unsigned i, columnIndex; - for (i=0; i<_numSortQueries; i++) - { - columnIndex=(*_columnIndices)[i]; - if (columnIndex==(unsigned)-1) - continue; - - if (first->cells[columnIndex]->isEmpty==true && second->cells[columnIndex]->isEmpty==false) - return 1; // Empty cells always go at the end - - if (first->cells[columnIndex]->isEmpty==false && second->cells[columnIndex]->isEmpty==true) - return -1; // Empty cells always go at the end - - if (_sortQueries[i].operation==Table::QS_INCREASING_ORDER) - { - if ((*_columns)[columnIndex].columnType==Table::NUMERIC) - { - if (first->cells[columnIndex]->i>second->cells[columnIndex]->i) - return 1; - if (first->cells[columnIndex]->icells[columnIndex]->i) - return -1; - } - else - { - // String - if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0) - return 1; - if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0) - return -1; - } - } - else - { - if ((*_columns)[columnIndex].columnType==Table::NUMERIC) - { - if (first->cells[columnIndex]->icells[columnIndex]->i) - return 1; - if (first->cells[columnIndex]->i>second->cells[columnIndex]->i) - return -1; - } - else - { - // String - if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0) - return 1; - if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0) - return -1; - } - } - } - - return 0; -} -void Table::SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out) -{ - unsigned i; - unsigned outLength; - DataStructures::List columnIndices; - _sortQueries=sortQueries; - _numSortQueries=numSortQueries; - _columnIndices=&columnIndices; - _columns=&columns; - bool anyValid=false; - - for (i=0; i < numSortQueries; i++) - { - if (sortQueries[i].columnIndex *cur; - cur = rows.GetListHead(); - if (anyValid==false) - { - outLength=0; - while (cur) - { - for (i=0; i < (unsigned)cur->size; i++) - { - out[(outLength)++]=cur->data[i]; - } - cur=cur->next; - } - return; - } - - // Start adding to ordered list. - DataStructures::OrderedList orderedList; - while (cur) - { - for (i=0; i < (unsigned)cur->size; i++) - { - RakAssert(cur->data[i]); - orderedList.Insert(cur->data[i],cur->data[i], true, _FILE_AND_LINE_); - } - cur=cur->next; - } - - outLength=0; - for (i=0; i < orderedList.Size(); i++) - out[(outLength)++]=orderedList[i]; -} -void Table::PrintColumnHeaders(char *out, int outLength, char columnDelineator) const -{ - if (outLength<=0) - return; - if (outLength==1) - { - *out=0; - return; - } - - unsigned i; - out[0]=0; - int len; - for (i=0; i < columns.Size(); i++) - { - if (i!=0) - { - len = (int) strlen(out); - if (len < outLength-1) - sprintf(out+len, "%c", columnDelineator); - else - return; - } - - len = (int) strlen(out); - if (len < outLength-(int) strlen(columns[i].columnName)) - sprintf(out+len, "%s", columns[i].columnName); - else - return; - } -} -void Table::PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const -{ - if (outLength<=0) - return; - if (outLength==1) - { - *out=0; - return; - } - - if (inputRow->cells.Size()!=columns.Size()) - { - strncpy(out, "Cell width does not match column width.\n", outLength); - out[outLength-1]=0; - return; - } - - char buff[512]; - unsigned i; - int len; - out[0]=0; - for (i=0; i < columns.Size(); i++) - { - if (columns[i].columnType==NUMERIC) - { - if (inputRow->cells[i]->isEmpty==false) - { - sprintf(buff, "%f", inputRow->cells[i]->i); - len=(int)strlen(buff); - } - else - len=0; - if (i+1!=columns.Size()) - buff[len++]=columnDelineator; - buff[len]=0; - } - else if (columns[i].columnType==STRING) - { - if (inputRow->cells[i]->isEmpty==false && inputRow->cells[i]->c) - { - strncpy(buff, inputRow->cells[i]->c, 512-2); - buff[512-2]=0; - len=(int)strlen(buff); - } - else - len=0; - if (i+1!=columns.Size()) - buff[len++]=columnDelineator; - buff[len]=0; - } - else if (columns[i].columnType==POINTER) - { - if (inputRow->cells[i]->isEmpty==false && inputRow->cells[i]->ptr) - { - sprintf(buff, "%p", inputRow->cells[i]->ptr); - len=(int)strlen(buff); - } - else - len=0; - if (i+1!=columns.Size()) - buff[len++]=columnDelineator; - buff[len]=0; - } - else - { - if (printDelineatorForBinary) - { - if (i+1!=columns.Size()) - buff[0]=columnDelineator; - buff[1]=0; - } - else - buff[0]=0; - - } - - len=(int)strlen(out); - if (outLength==len+1) - break; - strncpy(out+len, buff, outLength-len); - out[outLength-1]=0; - } -} - -void Table::Clear(void) -{ - rows.ForEachData(FreeRow); - rows.Clear(); - columns.Clear(true, _FILE_AND_LINE_); -} -const List& Table::GetColumns(void) const -{ - return columns; -} -const DataStructures::BPlusTree& Table::GetRows(void) const -{ - return rows; -} -DataStructures::Page * Table::GetListHead(void) -{ - return rows.GetListHead(); -} -unsigned Table::GetAvailableRowId(void) const -{ - bool setKey=false; - unsigned key=0; - int i; - DataStructures::Page *cur = rows.GetListHead(); - - while (cur) - { - for (i=0; i < cur->size; i++) - { - if (setKey==false) - { - key=cur->keys[i]+1; - setKey=true; - } - else - { - if (key!=cur->keys[i]) - return key; - key++; - } - } - - cur=cur->next; - } - return key; -} -void Table::DeleteRow(Table::Row *row) -{ - unsigned rowIndex; - for (rowIndex=0; rowIndex < row->cells.Size(); rowIndex++) - { - RakNet::OP_DELETE(row->cells[rowIndex], _FILE_AND_LINE_); - } - RakNet::OP_DELETE(row, _FILE_AND_LINE_); -} -Table& Table::operator = ( const Table& input ) -{ - Clear(); - - unsigned int i; - for (i=0; i < input.GetColumnCount(); i++) - AddColumn(input.ColumnName(i), input.GetColumnType(i)); - - DataStructures::Page *cur = input.GetRows().GetListHead(); - while (cur) - { - for (i=0; i < (unsigned int) cur->size; i++) - { - AddRow(cur->keys[i], cur->data[i]->cells, false); - } - - cur=cur->next; - } - - return *this; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +#include "DS_Table.h" +#include "DS_OrderedList.h" +#include +#include "RakAssert.h" +#include "RakAssert.h" +#include "Itoa.h" + +using namespace DataStructures; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +void ExtendRows(Table::Row* input, int index) +{ + (void) index; + input->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); +} +void FreeRow(Table::Row* input, int index) +{ + (void) index; + + unsigned i; + for (i=0; i < input->cells.Size(); i++) + { + RakNet::OP_DELETE(input->cells[i], _FILE_AND_LINE_); + } + RakNet::OP_DELETE(input, _FILE_AND_LINE_); +} +Table::Cell::Cell() +{ + isEmpty=true; + c=0; + ptr=0; + i=0.0; +} +Table::Cell::~Cell() +{ + Clear(); +} +Table::Cell& Table::Cell::operator = ( const Table::Cell& input ) +{ + isEmpty=input.isEmpty; + i=input.i; + ptr=input.ptr; + if (c) + rakFree_Ex(c, _FILE_AND_LINE_); + if (input.c) + { + c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); + memcpy(c, input.c, (int) i); + } + else + c=0; + return *this; +} +Table::Cell::Cell( const Table::Cell & input) +{ + isEmpty=input.isEmpty; + i=input.i; + ptr=input.ptr; + if (input.c) + { + if (c) + rakFree_Ex(c, _FILE_AND_LINE_); + c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); + memcpy(c, input.c, (int) i); + } +} +void Table::Cell::Set(double input) +{ + Clear(); + i=input; + c=0; + ptr=0; + isEmpty=false; +} +void Table::Cell::Set(unsigned int input) +{ + Set((int) input); +} +void Table::Cell::Set(int input) +{ + Clear(); + i=(double) input; + c=0; + ptr=0; + isEmpty=false; +} + +void Table::Cell::Set(const char *input) +{ + Clear(); + + if (input) + { + i=(int)strlen(input)+1; + c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); + strcpy(c, input); + } + else + { + c=0; + i=0; + } + ptr=0; + isEmpty=false; +} +void Table::Cell::Set(const char *input, int inputLength) +{ + Clear(); + if (input) + { + c = (char*) rakMalloc_Ex( inputLength, _FILE_AND_LINE_ ); + i=inputLength; + memcpy(c, input, inputLength); + } + else + { + c=0; + i=0; + } + ptr=0; + isEmpty=false; +} +void Table::Cell::SetPtr(void* p) +{ + Clear(); + c=0; + ptr=p; + isEmpty=false; +} +void Table::Cell::Get(int *output) +{ + RakAssert(isEmpty==false); + int o = (int) i; + *output=o; +} +void Table::Cell::Get(double *output) +{ + RakAssert(isEmpty==false); + *output=i; +} +void Table::Cell::Get(char *output) +{ + RakAssert(isEmpty==false); + strcpy(output, c); +} +void Table::Cell::Get(char *output, int *outputLength) +{ + RakAssert(isEmpty==false); + memcpy(output, c, (int) i); + if (outputLength) + *outputLength=(int) i; +} +RakNet::RakString Table::Cell::ToString(ColumnType columnType) +{ + if (isEmpty) + return RakNet::RakString(); + + if (columnType==NUMERIC) + { + return RakNet::RakString("%f", i); + } + else if (columnType==STRING) + { + return RakNet::RakString(c); + } + else if (columnType==BINARY) + { + return RakNet::RakString(""); + } + else if (columnType==POINTER) + { + return RakNet::RakString("%p", ptr); + } + + return RakNet::RakString(); +} +Table::Cell::Cell(double numericValue, char *charValue, void *ptr, ColumnType type) +{ + SetByType(numericValue,charValue,ptr,type); +} +void Table::Cell::SetByType(double numericValue, char *charValue, void *ptr, ColumnType type) +{ + isEmpty=true; + if (type==NUMERIC) + { + Set(numericValue); + } + else if (type==STRING) + { + Set(charValue); + } + else if (type==BINARY) + { + Set(charValue, (int) numericValue); + } + else if (type==POINTER) + { + SetPtr(ptr); + } + else + { + ptr=(void*) charValue; + } +} +Table::ColumnType Table::Cell::EstimateColumnType(void) const +{ + if (c) + if (i!=0.0f) + return BINARY; + else + return STRING; + if (ptr) + return POINTER; + return NUMERIC; +} +void Table::Cell::Clear(void) +{ + if (isEmpty==false && c) + { + rakFree_Ex(c, _FILE_AND_LINE_); + c=0; + } + isEmpty=true; +} +Table::ColumnDescriptor::ColumnDescriptor() +{ + +} +Table::ColumnDescriptor::~ColumnDescriptor() +{ + +} +Table::ColumnDescriptor::ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType ct) +{ + columnType=ct; + strcpy(columnName, cn); +} +void Table::Row::UpdateCell(unsigned columnIndex, double value) +{ + cells[columnIndex]->Clear(); + cells[columnIndex]->Set(value); + +// cells[columnIndex]->i=value; +// cells[columnIndex]->c=0; +// cells[columnIndex]->isEmpty=false; +} +void Table::Row::UpdateCell(unsigned columnIndex, const char *str) +{ + cells[columnIndex]->Clear(); + cells[columnIndex]->Set(str); +} +void Table::Row::UpdateCell(unsigned columnIndex, int byteLength, const char *data) +{ + cells[columnIndex]->Clear(); + cells[columnIndex]->Set(data,byteLength); +} +Table::Table() +{ +} +Table::~Table() +{ + Clear(); +} +unsigned Table::AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType) +{ + if (columnName[0]==0) + return (unsigned) -1; + + // Add this column. + columns.Insert(Table::ColumnDescriptor(columnName, columnType), _FILE_AND_LINE_); + + // Extend the rows by one + rows.ForEachData(ExtendRows); + + return columns.Size()-1; +} +void Table::RemoveColumn(unsigned columnIndex) +{ + if (columnIndex >= columns.Size()) + return; + + columns.RemoveAtIndex(columnIndex); + + // Remove this index from each row. + int i; + DataStructures::Page *cur = rows.GetListHead(); + while (cur) + { + for (i=0; i < cur->size; i++) + { + RakNet::OP_DELETE(cur->data[i]->cells[columnIndex], _FILE_AND_LINE_); + cur->data[i]->cells.RemoveAtIndex(columnIndex); + } + + cur=cur->next; + } +} +unsigned Table::ColumnIndex(const char *columnName) const +{ + unsigned columnIndex; + for (columnIndex=0; columnIndex= columns.Size()) + return 0; + else + return (char*)columns[index].columnName; +} +Table::ColumnType Table::GetColumnType(unsigned index) const +{ + if (index >= columns.Size()) + return (Table::ColumnType) 0; + else + return columns[index].columnType; +} +unsigned Table::GetColumnCount(void) const +{ + return columns.Size(); +} +unsigned Table::GetRowCount(void) const +{ + return rows.Size(); +} +Table::Row* Table::AddRow(unsigned rowId) +{ + Row *newRow; + newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); + if (rows.Insert(rowId, newRow)==false) + { + RakNet::OP_DELETE(newRow, _FILE_AND_LINE_); + return 0; // Already exists + } + unsigned rowIndex; + for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) + newRow->cells.Insert( RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); + return newRow; +} +Table::Row* Table::AddRow(unsigned rowId, DataStructures::List &initialCellValues) +{ + Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); + unsigned rowIndex; + for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) + { + if (rowIndex < initialCellValues.Size() && initialCellValues[rowIndex].isEmpty==false) + { + Table::Cell *c; + c = RakNet::OP_NEW(_FILE_AND_LINE_); + c->SetByType(initialCellValues[rowIndex].i,initialCellValues[rowIndex].c,initialCellValues[rowIndex].ptr,columns[rowIndex].columnType); + newRow->cells.Insert(c, _FILE_AND_LINE_ ); + } + else + newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); + } + rows.Insert(rowId, newRow); + return newRow; +} +Table::Row* Table::AddRow(unsigned rowId, DataStructures::List &initialCellValues, bool copyCells) +{ + Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); + unsigned rowIndex; + for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) + { + if (rowIndex < initialCellValues.Size() && initialCellValues[rowIndex] && initialCellValues[rowIndex]->isEmpty==false) + { + if (copyCells==false) + newRow->cells.Insert(RakNet::OP_NEW_4( _FILE_AND_LINE_, initialCellValues[rowIndex]->i, initialCellValues[rowIndex]->c, initialCellValues[rowIndex]->ptr, columns[rowIndex].columnType), _FILE_AND_LINE_); + else + { + Table::Cell *c = RakNet::OP_NEW( _FILE_AND_LINE_ ); + newRow->cells.Insert(c, _FILE_AND_LINE_); + *c=*(initialCellValues[rowIndex]); + } + } + else + newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_); + } + rows.Insert(rowId, newRow); + return newRow; +} +Table::Row* Table::AddRowColumns(unsigned rowId, Row *row, DataStructures::List columnIndices) +{ + Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); + unsigned columnIndex; + for (columnIndex=0; columnIndex < columnIndices.Size(); columnIndex++) + { + if (row->cells[columnIndices[columnIndex]]->isEmpty==false) + { + newRow->cells.Insert(RakNet::OP_NEW_4( _FILE_AND_LINE_, + row->cells[columnIndices[columnIndex]]->i, + row->cells[columnIndices[columnIndex]]->c, + row->cells[columnIndices[columnIndex]]->ptr, + columns[columnIndex].columnType + ), _FILE_AND_LINE_); + } + else + { + newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_); + } + } + rows.Insert(rowId, newRow); + return newRow; +} +bool Table::RemoveRow(unsigned rowId) +{ + Row *out; + if (rows.Delete(rowId, out)) + { + DeleteRow(out); + return true; + } + return false; +} +void Table::RemoveRows(Table *tableContainingRowIDs) +{ + unsigned i; + DataStructures::Page *cur = tableContainingRowIDs->GetRows().GetListHead(); + while (cur) + { + for (i=0; i < (unsigned)cur->size; i++) + { + rows.Delete(cur->keys[i]); + } + cur=cur->next; + } + return; +} +bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int value) +{ + RakAssert(columns[columnIndex].columnType==NUMERIC); + + Row *row = GetRowByID(rowId); + if (row) + { + row->UpdateCell(columnIndex, value); + return true; + } + return false; +} +bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, char *str) +{ + RakAssert(columns[columnIndex].columnType==STRING); + + Row *row = GetRowByID(rowId); + if (row) + { + row->UpdateCell(columnIndex, str); + return true; + } + return false; +} +bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data) +{ + RakAssert(columns[columnIndex].columnType==BINARY); + + Row *row = GetRowByID(rowId); + if (row) + { + row->UpdateCell(columnIndex, byteLength, data); + return true; + } + return false; +} +bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value) +{ + RakAssert(columns[columnIndex].columnType==NUMERIC); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->UpdateCell(columnIndex, value); + return true; + } + return false; +} +bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str) +{ + RakAssert(columns[columnIndex].columnType==STRING); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->UpdateCell(columnIndex, str); + return true; + } + return false; +} +bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data) +{ + RakAssert(columns[columnIndex].columnType==BINARY); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->UpdateCell(columnIndex, byteLength, data); + return true; + } + return false; +} +void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output) +{ + RakAssert(columns[columnIndex].columnType==NUMERIC); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->cells[columnIndex]->Get(output); + } +} +void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output) +{ + RakAssert(columns[columnIndex].columnType==STRING); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->cells[columnIndex]->Get(output); + } +} +void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength) +{ + RakAssert(columns[columnIndex].columnType==BINARY); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->cells[columnIndex]->Get(output, outputLength); + } +} +Table::FilterQuery::FilterQuery() +{ + columnName[0]=0; +} +Table::FilterQuery::~FilterQuery() +{ + +} +Table::FilterQuery::FilterQuery(unsigned column, Cell *cell, FilterQueryType op) +{ + columnIndex=column; + cellValue=cell; + operation=op; +} +Table::Row* Table::GetRowByID(unsigned rowId) const +{ + Row *row; + if (rows.Get(rowId, row)) + return row; + return 0; +} + +Table::Row* Table::GetRowByIndex(unsigned rowIndex, unsigned *key) const +{ + DataStructures::Page *cur = rows.GetListHead(); + while (cur) + { + if (rowIndex < (unsigned)cur->size) + { + if (key) + *key=cur->keys[rowIndex]; + return cur->data[rowIndex]; + } + if (rowIndex <= (unsigned)cur->size) + rowIndex-=cur->size; + else + return 0; + cur=cur->next; + } + return 0; +} + +void Table::QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result) +{ + unsigned i; + DataStructures::List columnIndicesToReturn; + + // Clear the result table. + result->Clear(); + + if (columnIndicesSubset && numColumnSubset>0) + { + for (i=0; i < numColumnSubset; i++) + { + if (columnIndicesSubset[i]AddColumn(columns[columnIndicesToReturn[i]].columnName,columns[columnIndicesToReturn[i]].columnType); + } + + // Get the column indices of the filter queries. + DataStructures::List inclusionFilterColumnIndices; + if (inclusionFilters && numInclusionFilters>0) + { + for (i=0; i < numInclusionFilters; i++) + { + if (inclusionFilters[i].columnName[0]) + inclusionFilters[i].columnIndex=ColumnIndex(inclusionFilters[i].columnName); + if (inclusionFilters[i].columnIndex *cur = rows.GetListHead(); + while (cur) + { + for (i=0; i < (unsigned)cur->size; i++) + { + QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, cur->keys[i], cur->data[i], inclusionFilters, result); + } + cur=cur->next; + } + } + else + { + // Specific rows + Row *row; + for (i=0; i < numRowIDs; i++) + { + if (rows.Get(rowIds[i], row)) + { + QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, rowIds[i], row, inclusionFilters, result); + } + } + } +} + +void Table::QueryRow(DataStructures::List &inclusionFilterColumnIndices, DataStructures::List &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result) +{ + bool pass=false; + unsigned columnIndex; + unsigned j; + + // If no inclusion filters, just add the row + if (inclusionFilterColumnIndices.Size()==0) + { + result->AddRowColumns(key, row, columnIndicesToReturn); + } + else + { + // Go through all inclusion filters. Only add this row if all filters pass. + for (j=0; jcells[columnIndex]->isEmpty==false ) + { + if (columns[inclusionFilterColumnIndices[j]].columnType==STRING && + (row->cells[columnIndex]->c==0 || + inclusionFilters[j].cellValue->c==0) ) + continue; + + switch (inclusionFilters[j].operation) + { + case QF_EQUAL: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)==0; + break; + case BINARY: + pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i && + memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, (int) row->cells[columnIndex]->i)==0; + break; + case POINTER: + pass=row->cells[columnIndex]->ptr==inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_NOT_EQUAL: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i!=inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)!=0; + break; + case BINARY: + pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i && + memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, (int) row->cells[columnIndex]->i)==0; + break; + case POINTER: + pass=row->cells[columnIndex]->ptr!=inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_GREATER_THAN: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i>inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)>0; + break; + case BINARY: + break; + case POINTER: + pass=row->cells[columnIndex]->ptr>inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_GREATER_THAN_EQ: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i>=inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)>=0; + break; + case BINARY: + break; + case POINTER: + pass=row->cells[columnIndex]->ptr>=inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_LESS_THAN: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->ii; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)<0; + break; + case BINARY: + break; + case POINTER: + pass=row->cells[columnIndex]->ptrptr; + break; + } + break; + case QF_LESS_THAN_EQ: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i<=inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)<=0; + break; + case BINARY: + break; + case POINTER: + pass=row->cells[columnIndex]->ptr<=inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_IS_EMPTY: + pass=false; + break; + case QF_NOT_EMPTY: + pass=true; + break; + default: + pass=false; + RakAssert(0); + break; + } + } + else + { + if (inclusionFilters[j].operation==QF_IS_EMPTY) + pass=true; + else + pass=false; // No value for this cell + } + + if (pass==false) + break; + } + + if (pass) + { + result->AddRowColumns(key, row, columnIndicesToReturn); + } + } +} + +static Table::SortQuery *_sortQueries; +static unsigned _numSortQueries; +static DataStructures::List *_columnIndices; +static DataStructures::List *_columns; +int RowSort(Table::Row* const &first, Table::Row* const &second) // first is the one inserting, second is the one already there. +{ + unsigned i, columnIndex; + for (i=0; i<_numSortQueries; i++) + { + columnIndex=(*_columnIndices)[i]; + if (columnIndex==(unsigned)-1) + continue; + + if (first->cells[columnIndex]->isEmpty==true && second->cells[columnIndex]->isEmpty==false) + return 1; // Empty cells always go at the end + + if (first->cells[columnIndex]->isEmpty==false && second->cells[columnIndex]->isEmpty==true) + return -1; // Empty cells always go at the end + + if (_sortQueries[i].operation==Table::QS_INCREASING_ORDER) + { + if ((*_columns)[columnIndex].columnType==Table::NUMERIC) + { + if (first->cells[columnIndex]->i>second->cells[columnIndex]->i) + return 1; + if (first->cells[columnIndex]->icells[columnIndex]->i) + return -1; + } + else + { + // String + if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0) + return 1; + if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0) + return -1; + } + } + else + { + if ((*_columns)[columnIndex].columnType==Table::NUMERIC) + { + if (first->cells[columnIndex]->icells[columnIndex]->i) + return 1; + if (first->cells[columnIndex]->i>second->cells[columnIndex]->i) + return -1; + } + else + { + // String + if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0) + return 1; + if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0) + return -1; + } + } + } + + return 0; +} +void Table::SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out) +{ + unsigned i; + unsigned outLength; + DataStructures::List columnIndices; + _sortQueries=sortQueries; + _numSortQueries=numSortQueries; + _columnIndices=&columnIndices; + _columns=&columns; + bool anyValid=false; + + for (i=0; i < numSortQueries; i++) + { + if (sortQueries[i].columnIndex *cur; + cur = rows.GetListHead(); + if (anyValid==false) + { + outLength=0; + while (cur) + { + for (i=0; i < (unsigned)cur->size; i++) + { + out[(outLength)++]=cur->data[i]; + } + cur=cur->next; + } + return; + } + + // Start adding to ordered list. + DataStructures::OrderedList orderedList; + while (cur) + { + for (i=0; i < (unsigned)cur->size; i++) + { + RakAssert(cur->data[i]); + orderedList.Insert(cur->data[i],cur->data[i], true, _FILE_AND_LINE_); + } + cur=cur->next; + } + + outLength=0; + for (i=0; i < orderedList.Size(); i++) + out[(outLength)++]=orderedList[i]; +} +void Table::PrintColumnHeaders(char *out, int outLength, char columnDelineator) const +{ + if (outLength<=0) + return; + if (outLength==1) + { + *out=0; + return; + } + + unsigned i; + out[0]=0; + int len; + for (i=0; i < columns.Size(); i++) + { + if (i!=0) + { + len = (int) strlen(out); + if (len < outLength-1) + sprintf(out+len, "%c", columnDelineator); + else + return; + } + + len = (int) strlen(out); + if (len < outLength-(int) strlen(columns[i].columnName)) + sprintf(out+len, "%s", columns[i].columnName); + else + return; + } +} +void Table::PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const +{ + if (outLength<=0) + return; + if (outLength==1) + { + *out=0; + return; + } + + if (inputRow->cells.Size()!=columns.Size()) + { + strncpy(out, "Cell width does not match column width.\n", outLength); + out[outLength-1]=0; + return; + } + + char buff[512]; + unsigned i; + int len; + out[0]=0; + for (i=0; i < columns.Size(); i++) + { + if (columns[i].columnType==NUMERIC) + { + if (inputRow->cells[i]->isEmpty==false) + { + sprintf(buff, "%f", inputRow->cells[i]->i); + len=(int)strlen(buff); + } + else + len=0; + if (i+1!=columns.Size()) + buff[len++]=columnDelineator; + buff[len]=0; + } + else if (columns[i].columnType==STRING) + { + if (inputRow->cells[i]->isEmpty==false && inputRow->cells[i]->c) + { + strncpy(buff, inputRow->cells[i]->c, 512-2); + buff[512-2]=0; + len=(int)strlen(buff); + } + else + len=0; + if (i+1!=columns.Size()) + buff[len++]=columnDelineator; + buff[len]=0; + } + else if (columns[i].columnType==POINTER) + { + if (inputRow->cells[i]->isEmpty==false && inputRow->cells[i]->ptr) + { + sprintf(buff, "%p", inputRow->cells[i]->ptr); + len=(int)strlen(buff); + } + else + len=0; + if (i+1!=columns.Size()) + buff[len++]=columnDelineator; + buff[len]=0; + } + else + { + if (printDelineatorForBinary) + { + if (i+1!=columns.Size()) + buff[0]=columnDelineator; + buff[1]=0; + } + else + buff[0]=0; + + } + + len=(int)strlen(out); + if (outLength==len+1) + break; + strncpy(out+len, buff, outLength-len); + out[outLength-1]=0; + } +} + +void Table::Clear(void) +{ + rows.ForEachData(FreeRow); + rows.Clear(); + columns.Clear(true, _FILE_AND_LINE_); +} +const List& Table::GetColumns(void) const +{ + return columns; +} +const DataStructures::BPlusTree& Table::GetRows(void) const +{ + return rows; +} +DataStructures::Page * Table::GetListHead(void) +{ + return rows.GetListHead(); +} +unsigned Table::GetAvailableRowId(void) const +{ + bool setKey=false; + unsigned key=0; + int i; + DataStructures::Page *cur = rows.GetListHead(); + + while (cur) + { + for (i=0; i < cur->size; i++) + { + if (setKey==false) + { + key=cur->keys[i]+1; + setKey=true; + } + else + { + if (key!=cur->keys[i]) + return key; + key++; + } + } + + cur=cur->next; + } + return key; +} +void Table::DeleteRow(Table::Row *row) +{ + unsigned rowIndex; + for (rowIndex=0; rowIndex < row->cells.Size(); rowIndex++) + { + RakNet::OP_DELETE(row->cells[rowIndex], _FILE_AND_LINE_); + } + RakNet::OP_DELETE(row, _FILE_AND_LINE_); +} +Table& Table::operator = ( const Table& input ) +{ + Clear(); + + unsigned int i; + for (i=0; i < input.GetColumnCount(); i++) + AddColumn(input.ColumnName(i), input.GetColumnType(i)); + + DataStructures::Page *cur = input.GetRows().GetListHead(); + while (cur) + { + for (i=0; i < (unsigned int) cur->size; i++) + { + AddRow(cur->keys[i], cur->data[i]->cells, false); + } + + cur=cur->next; + } + + return *this; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_Table.h b/project/lib_projects/raknet/jni/RaknetSources/DS_Table.h index 56d08fb..9699b3e 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_Table.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_Table.h @@ -1,343 +1,343 @@ -/// \file DS_Table.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __TABLE_H -#define __TABLE_H - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -#include "DS_List.h" -#include "DS_BPlusTree.h" -#include "RakMemoryOverride.h" -#include "Export.h" -#include "RakString.h" - -#define _TABLE_BPLUS_TREE_ORDER 16 -#define _TABLE_MAX_COLUMN_NAME_LENGTH 64 - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - - /// \brief Holds a set of columns, a set of rows, and rows times columns cells. - /// \details The table data structure is useful if you want to store a set of structures and perform queries on those structures.
- /// This is a relatively simple and fast implementation of the types of tables commonly used in databases.
- /// See TableSerializer to serialize data members of the table.
- /// See LightweightDatabaseClient and LightweightDatabaseServer to transmit the table over the network. - class RAK_DLL_EXPORT Table - { - public: - - enum ColumnType - { - // Cell::i used - NUMERIC, - - // Cell::c used to hold a null terminated string. - STRING, - - // Cell::c holds data. Cell::i holds data length of c in bytes. - BINARY, - - // Cell::c holds data. Not deallocated. Set manually by assigning ptr. - POINTER, - }; - - - /// Holds the actual data in the table - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT Cell - { - Cell(); - ~Cell(); - Cell(double numericValue, char *charValue, void *ptr, ColumnType type); - void SetByType(double numericValue, char *charValue, void *ptr, ColumnType type); - void Clear(void); - - /// Numeric - void Set(int input); - void Set(unsigned int input); - void Set(double input); - - /// String - void Set(const char *input); - - /// Binary - void Set(const char *input, int inputLength); - - /// Pointer - void SetPtr(void* p); - - /// Numeric - void Get(int *output); - void Get(double *output); - - /// String - void Get(char *output); - - /// Binary - void Get(char *output, int *outputLength); - - RakNet::RakString ToString(ColumnType columnType); - - // assignment operator and copy constructor - Cell& operator = ( const Cell& input ); - Cell( const Cell & input); - - ColumnType EstimateColumnType(void) const; - - bool isEmpty; - double i; - char *c; - void *ptr; - }; - - /// Stores the name and type of the column - /// \internal - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT ColumnDescriptor - { - ColumnDescriptor(); - ~ColumnDescriptor(); - ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH],ColumnType ct); - - char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; - ColumnType columnType; - }; - - /// Stores the list of cells for this row, and a special flag used for internal sorting - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT Row - { - // list of cells - DataStructures::List cells; - - /// Numeric - void UpdateCell(unsigned columnIndex, double value); - - /// String - void UpdateCell(unsigned columnIndex, const char *str); - - /// Binary - void UpdateCell(unsigned columnIndex, int byteLength, const char *data); - }; - - // Operations to perform for cell comparison - enum FilterQueryType - { - QF_EQUAL, - QF_NOT_EQUAL, - QF_GREATER_THAN, - QF_GREATER_THAN_EQ, - QF_LESS_THAN, - QF_LESS_THAN_EQ, - QF_IS_EMPTY, - QF_NOT_EMPTY, - }; - - // Compare the cell value for a row at columnName to the cellValue using operation. - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT FilterQuery - { - FilterQuery(); - ~FilterQuery(); - FilterQuery(unsigned column, Cell *cell, FilterQueryType op); - - // If columnName is specified, columnIndex will be looked up using it. - char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; - unsigned columnIndex; - Cell *cellValue; - FilterQueryType operation; - }; - - /// Increasing or decreasing sort order - enum SortQueryType - { - QS_INCREASING_ORDER, - QS_DECREASING_ORDER, - }; - - // Sort on increasing or decreasing order for a particular column - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT SortQuery - { - /// The index of the table column we are sorting on - unsigned columnIndex; - - /// See SortQueryType - SortQueryType operation; - }; - - // Constructor - Table(); - - // Destructor - ~Table(); - - /// \brief Adds a column to the table - /// \param[in] columnName The name of the column - /// \param[in] columnType What type of data this column will hold - /// \return The index of the new column - unsigned AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType); - - /// \brief Removes a column by index - /// \param[in] columnIndex The index of the column to remove - void RemoveColumn(unsigned columnIndex); - - /// \brief Gets the index of a column by name - /// \details Column indices are stored in the order they are added. - /// \param[in] columnName The name of the column - /// \return The index of the column, or (unsigned)-1 if no such column - unsigned ColumnIndex(char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]) const; - unsigned ColumnIndex(const char *columnName) const; - - /// \brief Gives the string name of the column at a certain index - /// \param[in] index The index of the column - /// \return The name of the column, or 0 if an invalid index - char* ColumnName(unsigned index) const; - - /// \brief Returns the type of a column, referenced by index - /// \param[in] index The index of the column - /// \return The type of the column - ColumnType GetColumnType(unsigned index) const; - - /// Returns the number of columns - /// \return The number of columns in the table - unsigned GetColumnCount(void) const; - - /// Returns the number of rows - /// \return The number of rows in the table - unsigned GetRowCount(void) const; - - /// \brief Adds a row to the table - /// \details New rows are added with empty values for all cells. However, if you specify initialCelLValues you can specify initial values - /// It's up to you to ensure that the values in the specific cells match the type of data used by that row - /// rowId can be considered the primary key for the row. It is much faster to lookup a row by its rowId than by searching keys. - /// rowId must be unique - /// Rows are stored in sorted order in the table, using rowId as the sort key - /// \param[in] rowId The UNIQUE primary key for the row. This can never be changed. - /// \param[in] initialCellValues Initial values to give the row (optional) - /// \return The newly added row - Table::Row* AddRow(unsigned rowId); - Table::Row* AddRow(unsigned rowId, DataStructures::List &initialCellValues); - Table::Row* AddRow(unsigned rowId, DataStructures::List &initialCellValues, bool copyCells=false); - - /// \brief Removes a row specified by rowId. - /// \param[in] rowId The ID of the row - /// \return true if the row was deleted. False if not. - bool RemoveRow(unsigned rowId); - - /// \brief Removes all the rows with IDs that the specified table also has. - /// \param[in] tableContainingRowIDs The IDs of the rows - void RemoveRows(Table *tableContainingRowIDs); - - /// \brief Updates a particular cell in the table. - /// \note If you are going to update many cells of a particular row, it is more efficient to call GetRow and perform the operations on the row directly. - /// \note Row pointers do not change, so you can also write directly to the rows for more efficiency. - /// \param[in] rowId The ID of the row - /// \param[in] columnIndex The column of the cell - /// \param[in] value The data to set - bool UpdateCell(unsigned rowId, unsigned columnIndex, int value); - bool UpdateCell(unsigned rowId, unsigned columnIndex, char *str); - bool UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data); - bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value); - bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str); - bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data); - - /// \brief Note this is much less efficient to call than GetRow, then working with the cells directly. - /// Numeric, string, binary - void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output); - void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output); - void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength); - - /// \brief Gets a row. More efficient to do this and access Row::cells than to repeatedly call GetCell. - /// You can also update cells in rows from this function. - /// \param[in] rowId The ID of the row - /// \return The desired row, or 0 if no such row. - Row* GetRowByID(unsigned rowId) const; - - /// \brief Gets a row at a specific index. - /// rowIndex should be less than GetRowCount() - /// \param[in] rowIndex The index of the row - /// \param[out] key The ID of the row returned - /// \return The desired row, or 0 if no such row. - Row* GetRowByIndex(unsigned rowIndex, unsigned *key) const; - - /// \brief Queries the table, optionally returning only a subset of columns and rows. - /// \param[in] columnSubset An array of column indices. Only columns in this array are returned. Pass 0 for all columns - /// \param[in] numColumnSubset The number of elements in \a columnSubset - /// \param[in] inclusionFilters An array of FilterQuery. All filters must pass for the row to be returned. - /// \param[in] numInclusionFilters The number of elements in \a inclusionFilters - /// \param[in] rowIds An arrow of row IDs. Only these rows with these IDs are returned. Pass 0 for all rows. - /// \param[in] numRowIDs The number of elements in \a rowIds - /// \param[out] result The result of the query. If no rows are returned, the table will only have columns. - void QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result); - - /// \brief Sorts the table by rows - /// \details You can sort the table in ascending or descending order on one or more columns - /// Columns have precedence in the order they appear in the \a sortQueries array - /// If a row cell on column n has the same value as a a different row on column n, then the row will be compared on column n+1 - /// \param[in] sortQueries A list of SortQuery structures, defining the sorts to perform on the table - /// \param[in] numColumnSubset The number of elements in \a numSortQueries - /// \param[out] out The address of an array of Rows, which will receive the sorted output. The array must be long enough to contain all returned rows, up to GetRowCount() - void SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out); - - /// \brief Frees all memory in the table. - void Clear(void); - - /// \brief Prints out the names of all the columns. - /// \param[out] out A pointer to an array of bytes which will hold the output. - /// \param[in] outLength The size of the \a out array - /// \param[in] columnDelineator What character to print to delineate columns - void PrintColumnHeaders(char *out, int outLength, char columnDelineator) const; - - /// \brief Writes a text representation of the row to \a out. - /// \param[out] out A pointer to an array of bytes which will hold the output. - /// \param[in] outLength The size of the \a out array - /// \param[in] columnDelineator What character to print to delineate columns - /// \param[in] printDelineatorForBinary Binary output is not printed. True to still print the delineator. - /// \param[in] inputRow The row to print - void PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const; - - /// \brief Direct access to make things easier. - const DataStructures::List& GetColumns(void) const; - - /// \brief Direct access to make things easier. - const DataStructures::BPlusTree& GetRows(void) const; - - /// \brief Get the head of a linked list containing all the row data. - DataStructures::Page * GetListHead(void); - - /// \brief Get the first free row id. - /// This could be made more efficient. - unsigned GetAvailableRowId(void) const; - - Table& operator = ( const Table& input ); - - protected: - Table::Row* AddRowColumns(unsigned rowId, Row *row, DataStructures::List columnIndices); - - void DeleteRow(Row *row); - - void QueryRow(DataStructures::List &inclusionFilterColumnIndices, DataStructures::List &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result); - - // 16 is arbitrary and is the order of the BPlus tree. Higher orders are better for searching while lower orders are better for - // Insertions and deletions. - DataStructures::BPlusTree rows; - - // Columns in the table. - DataStructures::List columns; - }; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif +/// \file DS_Table.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __TABLE_H +#define __TABLE_H + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +#include "DS_List.h" +#include "DS_BPlusTree.h" +#include "RakMemoryOverride.h" +#include "Export.h" +#include "RakString.h" + +#define _TABLE_BPLUS_TREE_ORDER 16 +#define _TABLE_MAX_COLUMN_NAME_LENGTH 64 + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + + /// \brief Holds a set of columns, a set of rows, and rows times columns cells. + /// \details The table data structure is useful if you want to store a set of structures and perform queries on those structures.
+ /// This is a relatively simple and fast implementation of the types of tables commonly used in databases.
+ /// See TableSerializer to serialize data members of the table.
+ /// See LightweightDatabaseClient and LightweightDatabaseServer to transmit the table over the network. + class RAK_DLL_EXPORT Table + { + public: + + enum ColumnType + { + // Cell::i used + NUMERIC, + + // Cell::c used to hold a null terminated string. + STRING, + + // Cell::c holds data. Cell::i holds data length of c in bytes. + BINARY, + + // Cell::c holds data. Not deallocated. Set manually by assigning ptr. + POINTER, + }; + + + /// Holds the actual data in the table + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT Cell + { + Cell(); + ~Cell(); + Cell(double numericValue, char *charValue, void *ptr, ColumnType type); + void SetByType(double numericValue, char *charValue, void *ptr, ColumnType type); + void Clear(void); + + /// Numeric + void Set(int input); + void Set(unsigned int input); + void Set(double input); + + /// String + void Set(const char *input); + + /// Binary + void Set(const char *input, int inputLength); + + /// Pointer + void SetPtr(void* p); + + /// Numeric + void Get(int *output); + void Get(double *output); + + /// String + void Get(char *output); + + /// Binary + void Get(char *output, int *outputLength); + + RakNet::RakString ToString(ColumnType columnType); + + // assignment operator and copy constructor + Cell& operator = ( const Cell& input ); + Cell( const Cell & input); + + ColumnType EstimateColumnType(void) const; + + bool isEmpty; + double i; + char *c; + void *ptr; + }; + + /// Stores the name and type of the column + /// \internal + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT ColumnDescriptor + { + ColumnDescriptor(); + ~ColumnDescriptor(); + ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH],ColumnType ct); + + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; + ColumnType columnType; + }; + + /// Stores the list of cells for this row, and a special flag used for internal sorting + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT Row + { + // list of cells + DataStructures::List cells; + + /// Numeric + void UpdateCell(unsigned columnIndex, double value); + + /// String + void UpdateCell(unsigned columnIndex, const char *str); + + /// Binary + void UpdateCell(unsigned columnIndex, int byteLength, const char *data); + }; + + // Operations to perform for cell comparison + enum FilterQueryType + { + QF_EQUAL, + QF_NOT_EQUAL, + QF_GREATER_THAN, + QF_GREATER_THAN_EQ, + QF_LESS_THAN, + QF_LESS_THAN_EQ, + QF_IS_EMPTY, + QF_NOT_EMPTY, + }; + + // Compare the cell value for a row at columnName to the cellValue using operation. + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT FilterQuery + { + FilterQuery(); + ~FilterQuery(); + FilterQuery(unsigned column, Cell *cell, FilterQueryType op); + + // If columnName is specified, columnIndex will be looked up using it. + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; + unsigned columnIndex; + Cell *cellValue; + FilterQueryType operation; + }; + + /// Increasing or decreasing sort order + enum SortQueryType + { + QS_INCREASING_ORDER, + QS_DECREASING_ORDER, + }; + + // Sort on increasing or decreasing order for a particular column + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT SortQuery + { + /// The index of the table column we are sorting on + unsigned columnIndex; + + /// See SortQueryType + SortQueryType operation; + }; + + // Constructor + Table(); + + // Destructor + ~Table(); + + /// \brief Adds a column to the table + /// \param[in] columnName The name of the column + /// \param[in] columnType What type of data this column will hold + /// \return The index of the new column + unsigned AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType); + + /// \brief Removes a column by index + /// \param[in] columnIndex The index of the column to remove + void RemoveColumn(unsigned columnIndex); + + /// \brief Gets the index of a column by name + /// \details Column indices are stored in the order they are added. + /// \param[in] columnName The name of the column + /// \return The index of the column, or (unsigned)-1 if no such column + unsigned ColumnIndex(char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]) const; + unsigned ColumnIndex(const char *columnName) const; + + /// \brief Gives the string name of the column at a certain index + /// \param[in] index The index of the column + /// \return The name of the column, or 0 if an invalid index + char* ColumnName(unsigned index) const; + + /// \brief Returns the type of a column, referenced by index + /// \param[in] index The index of the column + /// \return The type of the column + ColumnType GetColumnType(unsigned index) const; + + /// Returns the number of columns + /// \return The number of columns in the table + unsigned GetColumnCount(void) const; + + /// Returns the number of rows + /// \return The number of rows in the table + unsigned GetRowCount(void) const; + + /// \brief Adds a row to the table + /// \details New rows are added with empty values for all cells. However, if you specify initialCelLValues you can specify initial values + /// It's up to you to ensure that the values in the specific cells match the type of data used by that row + /// rowId can be considered the primary key for the row. It is much faster to lookup a row by its rowId than by searching keys. + /// rowId must be unique + /// Rows are stored in sorted order in the table, using rowId as the sort key + /// \param[in] rowId The UNIQUE primary key for the row. This can never be changed. + /// \param[in] initialCellValues Initial values to give the row (optional) + /// \return The newly added row + Table::Row* AddRow(unsigned rowId); + Table::Row* AddRow(unsigned rowId, DataStructures::List &initialCellValues); + Table::Row* AddRow(unsigned rowId, DataStructures::List &initialCellValues, bool copyCells=false); + + /// \brief Removes a row specified by rowId. + /// \param[in] rowId The ID of the row + /// \return true if the row was deleted. False if not. + bool RemoveRow(unsigned rowId); + + /// \brief Removes all the rows with IDs that the specified table also has. + /// \param[in] tableContainingRowIDs The IDs of the rows + void RemoveRows(Table *tableContainingRowIDs); + + /// \brief Updates a particular cell in the table. + /// \note If you are going to update many cells of a particular row, it is more efficient to call GetRow and perform the operations on the row directly. + /// \note Row pointers do not change, so you can also write directly to the rows for more efficiency. + /// \param[in] rowId The ID of the row + /// \param[in] columnIndex The column of the cell + /// \param[in] value The data to set + bool UpdateCell(unsigned rowId, unsigned columnIndex, int value); + bool UpdateCell(unsigned rowId, unsigned columnIndex, char *str); + bool UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data); + bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value); + bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str); + bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data); + + /// \brief Note this is much less efficient to call than GetRow, then working with the cells directly. + /// Numeric, string, binary + void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output); + void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output); + void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength); + + /// \brief Gets a row. More efficient to do this and access Row::cells than to repeatedly call GetCell. + /// You can also update cells in rows from this function. + /// \param[in] rowId The ID of the row + /// \return The desired row, or 0 if no such row. + Row* GetRowByID(unsigned rowId) const; + + /// \brief Gets a row at a specific index. + /// rowIndex should be less than GetRowCount() + /// \param[in] rowIndex The index of the row + /// \param[out] key The ID of the row returned + /// \return The desired row, or 0 if no such row. + Row* GetRowByIndex(unsigned rowIndex, unsigned *key) const; + + /// \brief Queries the table, optionally returning only a subset of columns and rows. + /// \param[in] columnSubset An array of column indices. Only columns in this array are returned. Pass 0 for all columns + /// \param[in] numColumnSubset The number of elements in \a columnSubset + /// \param[in] inclusionFilters An array of FilterQuery. All filters must pass for the row to be returned. + /// \param[in] numInclusionFilters The number of elements in \a inclusionFilters + /// \param[in] rowIds An arrow of row IDs. Only these rows with these IDs are returned. Pass 0 for all rows. + /// \param[in] numRowIDs The number of elements in \a rowIds + /// \param[out] result The result of the query. If no rows are returned, the table will only have columns. + void QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result); + + /// \brief Sorts the table by rows + /// \details You can sort the table in ascending or descending order on one or more columns + /// Columns have precedence in the order they appear in the \a sortQueries array + /// If a row cell on column n has the same value as a a different row on column n, then the row will be compared on column n+1 + /// \param[in] sortQueries A list of SortQuery structures, defining the sorts to perform on the table + /// \param[in] numColumnSubset The number of elements in \a numSortQueries + /// \param[out] out The address of an array of Rows, which will receive the sorted output. The array must be long enough to contain all returned rows, up to GetRowCount() + void SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out); + + /// \brief Frees all memory in the table. + void Clear(void); + + /// \brief Prints out the names of all the columns. + /// \param[out] out A pointer to an array of bytes which will hold the output. + /// \param[in] outLength The size of the \a out array + /// \param[in] columnDelineator What character to print to delineate columns + void PrintColumnHeaders(char *out, int outLength, char columnDelineator) const; + + /// \brief Writes a text representation of the row to \a out. + /// \param[out] out A pointer to an array of bytes which will hold the output. + /// \param[in] outLength The size of the \a out array + /// \param[in] columnDelineator What character to print to delineate columns + /// \param[in] printDelineatorForBinary Binary output is not printed. True to still print the delineator. + /// \param[in] inputRow The row to print + void PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const; + + /// \brief Direct access to make things easier. + const DataStructures::List& GetColumns(void) const; + + /// \brief Direct access to make things easier. + const DataStructures::BPlusTree& GetRows(void) const; + + /// \brief Get the head of a linked list containing all the row data. + DataStructures::Page * GetListHead(void); + + /// \brief Get the first free row id. + /// This could be made more efficient. + unsigned GetAvailableRowId(void) const; + + Table& operator = ( const Table& input ); + + protected: + Table::Row* AddRowColumns(unsigned rowId, Row *row, DataStructures::List columnIndices); + + void DeleteRow(Row *row); + + void QueryRow(DataStructures::List &inclusionFilterColumnIndices, DataStructures::List &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result); + + // 16 is arbitrary and is the order of the BPlus tree. Higher orders are better for searching while lower orders are better for + // Insertions and deletions. + DataStructures::BPlusTree rows; + + // Columns in the table. + DataStructures::List columns; + }; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_ThreadsafeAllocatingQueue.h b/project/lib_projects/raknet/jni/RaknetSources/DS_ThreadsafeAllocatingQueue.h index a9a1248..178d77e 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_ThreadsafeAllocatingQueue.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_ThreadsafeAllocatingQueue.h @@ -1,143 +1,143 @@ -/// \file DS_ThreadsafeAllocatingQueue.h -/// \internal -/// A threadsafe queue, that also uses a memory pool for allocation - -#ifndef __THREADSAFE_ALLOCATING_QUEUE -#define __THREADSAFE_ALLOCATING_QUEUE - -#include "DS_Queue.h" -#include "SimpleMutex.h" -#include "DS_MemoryPool.h" - -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - -namespace DataStructures -{ - -template -class RAK_DLL_EXPORT ThreadsafeAllocatingQueue -{ -public: - // Queue operations - void Push(structureType *s); - structureType *PopInaccurate(void); - structureType *Pop(void); - void SetPageSize(int size); - bool IsEmpty(void); - - // Memory pool operations - structureType *Allocate(const char *file, unsigned int line); - void Deallocate(structureType *s, const char *file, unsigned int line); - void Clear(const char *file, unsigned int line); -protected: - - MemoryPool memoryPool; - RakNet::SimpleMutex memoryPoolMutex; - Queue queue; - RakNet::SimpleMutex queueMutex; -}; - -template -void ThreadsafeAllocatingQueue::Push(structureType *s) -{ - queueMutex.Lock(); - queue.Push(s, _FILE_AND_LINE_ ); - queueMutex.Unlock(); -} - -template -structureType *ThreadsafeAllocatingQueue::PopInaccurate(void) -{ - structureType *s; - if (queue.IsEmpty()) - return 0; - queueMutex.Lock(); - if (queue.IsEmpty()==false) - s=queue.Pop(); - else - s=0; - queueMutex.Unlock(); - return s; -} - -template -structureType *ThreadsafeAllocatingQueue::Pop(void) -{ - structureType *s; - queueMutex.Lock(); - if (queue.IsEmpty()) - { - queueMutex.Unlock(); - return 0; - } - s=queue.Pop(); - queueMutex.Unlock(); - return s; -} - -template -structureType *ThreadsafeAllocatingQueue::Allocate(const char *file, unsigned int line) -{ - structureType *s; - memoryPoolMutex.Lock(); - s=memoryPool.Allocate(file, line); - memoryPoolMutex.Unlock(); - // Call new operator, memoryPool doesn't do this - s = new ((void*)s) structureType; - return s; -} -template -void ThreadsafeAllocatingQueue::Deallocate(structureType *s, const char *file, unsigned int line) -{ - // Call delete operator, memory pool doesn't do this - s->~structureType(); - memoryPoolMutex.Lock(); - memoryPool.Release(s, file, line); - memoryPoolMutex.Unlock(); -} - -template -void ThreadsafeAllocatingQueue::Clear(const char *file, unsigned int line) -{ - memoryPoolMutex.Lock(); - for (unsigned int i=0; i < queue.Size(); i++) - { - queue[i]->~structureType(); - memoryPool.Release(queue[i], file, line); - } - queue.Clear(file, line); - memoryPoolMutex.Unlock(); - memoryPoolMutex.Lock(); - memoryPool.Clear(file, line); - memoryPoolMutex.Unlock(); -} - -template -void ThreadsafeAllocatingQueue::SetPageSize(int size) -{ - memoryPool.SetPageSize(size); -} - -template -bool ThreadsafeAllocatingQueue::IsEmpty(void) -{ - bool isEmpty; - queueMutex.Lock(); - isEmpty=queue.IsEmpty(); - queueMutex.Unlock(); - return isEmpty; -} - -}; - - -// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) -// #pragma pop_macro("new") -// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - -#endif +/// \file DS_ThreadsafeAllocatingQueue.h +/// \internal +/// A threadsafe queue, that also uses a memory pool for allocation + +#ifndef __THREADSAFE_ALLOCATING_QUEUE +#define __THREADSAFE_ALLOCATING_QUEUE + +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "DS_MemoryPool.h" + +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + +namespace DataStructures +{ + +template +class RAK_DLL_EXPORT ThreadsafeAllocatingQueue +{ +public: + // Queue operations + void Push(structureType *s); + structureType *PopInaccurate(void); + structureType *Pop(void); + void SetPageSize(int size); + bool IsEmpty(void); + + // Memory pool operations + structureType *Allocate(const char *file, unsigned int line); + void Deallocate(structureType *s, const char *file, unsigned int line); + void Clear(const char *file, unsigned int line); +protected: + + MemoryPool memoryPool; + RakNet::SimpleMutex memoryPoolMutex; + Queue queue; + RakNet::SimpleMutex queueMutex; +}; + +template +void ThreadsafeAllocatingQueue::Push(structureType *s) +{ + queueMutex.Lock(); + queue.Push(s, _FILE_AND_LINE_ ); + queueMutex.Unlock(); +} + +template +structureType *ThreadsafeAllocatingQueue::PopInaccurate(void) +{ + structureType *s; + if (queue.IsEmpty()) + return 0; + queueMutex.Lock(); + if (queue.IsEmpty()==false) + s=queue.Pop(); + else + s=0; + queueMutex.Unlock(); + return s; +} + +template +structureType *ThreadsafeAllocatingQueue::Pop(void) +{ + structureType *s; + queueMutex.Lock(); + if (queue.IsEmpty()) + { + queueMutex.Unlock(); + return 0; + } + s=queue.Pop(); + queueMutex.Unlock(); + return s; +} + +template +structureType *ThreadsafeAllocatingQueue::Allocate(const char *file, unsigned int line) +{ + structureType *s; + memoryPoolMutex.Lock(); + s=memoryPool.Allocate(file, line); + memoryPoolMutex.Unlock(); + // Call new operator, memoryPool doesn't do this + s = new ((void*)s) structureType; + return s; +} +template +void ThreadsafeAllocatingQueue::Deallocate(structureType *s, const char *file, unsigned int line) +{ + // Call delete operator, memory pool doesn't do this + s->~structureType(); + memoryPoolMutex.Lock(); + memoryPool.Release(s, file, line); + memoryPoolMutex.Unlock(); +} + +template +void ThreadsafeAllocatingQueue::Clear(const char *file, unsigned int line) +{ + memoryPoolMutex.Lock(); + for (unsigned int i=0; i < queue.Size(); i++) + { + queue[i]->~structureType(); + memoryPool.Release(queue[i], file, line); + } + queue.Clear(file, line); + memoryPoolMutex.Unlock(); + memoryPoolMutex.Lock(); + memoryPool.Clear(file, line); + memoryPoolMutex.Unlock(); +} + +template +void ThreadsafeAllocatingQueue::SetPageSize(int size) +{ + memoryPool.SetPageSize(size); +} + +template +bool ThreadsafeAllocatingQueue::IsEmpty(void) +{ + bool isEmpty; + queueMutex.Lock(); + isEmpty=queue.IsEmpty(); + queueMutex.Unlock(); + return isEmpty; +} + +}; + + +// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) +// #pragma pop_macro("new") +// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_Tree.h b/project/lib_projects/raknet/jni/RaknetSources/DS_Tree.h index 50b723c..98949f1 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_Tree.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_Tree.h @@ -1,98 +1,98 @@ -/// \file DS_Tree.h -/// \internal -/// \brief Just a regular tree -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __DS_TREE_H -#define __DS_TREE_H - -#include "Export.h" -#include "DS_List.h" -#include "DS_Queue.h" -#include "RakMemoryOverride.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template - class RAK_DLL_EXPORT Tree - { - public: - Tree(); - Tree(TreeType &inputData); - ~Tree(); - void LevelOrderTraversal(DataStructures::List &output); - void AddChild(TreeType &newData); - void DeleteDecendants(void); - - TreeType data; - DataStructures::List children; - }; - - template - Tree::Tree() - { - - } - - template - Tree::Tree(TreeType &inputData) - { - data=inputData; - } - - template - Tree::~Tree() - { - DeleteDecendants(); - } - - template - void Tree::LevelOrderTraversal(DataStructures::List &output) - { - unsigned i; - Tree *node; - DataStructures::Queue*> queue; - - for (i=0; i < children.Size(); i++) - queue.Push(children[i]); - - while (queue.Size()) - { - node=queue.Pop(); - output.Insert(node, _FILE_AND_LINE_); - for (i=0; i < node->children.Size(); i++) - queue.Push(node->children[i]); - } - } - - template - void Tree::AddChild(TreeType &newData) - { - children.Insert(RakNet::OP_NEW(newData, _FILE_AND_LINE_)); - } - - template - void Tree::DeleteDecendants(void) - { - /* - DataStructures::List output; - LevelOrderTraversal(output); - unsigned i; - for (i=0; i < output.Size(); i++) - RakNet::OP_DELETE(output[i], _FILE_AND_LINE_); -*/ - - // Already recursive to do this - unsigned int i; - for (i=0; i < children.Size(); i++) - RakNet::OP_DELETE(children[i], _FILE_AND_LINE_); - } -} - -#endif +/// \file DS_Tree.h +/// \internal +/// \brief Just a regular tree +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __DS_TREE_H +#define __DS_TREE_H + +#include "Export.h" +#include "DS_List.h" +#include "DS_Queue.h" +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + class RAK_DLL_EXPORT Tree + { + public: + Tree(); + Tree(TreeType &inputData); + ~Tree(); + void LevelOrderTraversal(DataStructures::List &output); + void AddChild(TreeType &newData); + void DeleteDecendants(void); + + TreeType data; + DataStructures::List children; + }; + + template + Tree::Tree() + { + + } + + template + Tree::Tree(TreeType &inputData) + { + data=inputData; + } + + template + Tree::~Tree() + { + DeleteDecendants(); + } + + template + void Tree::LevelOrderTraversal(DataStructures::List &output) + { + unsigned i; + Tree *node; + DataStructures::Queue*> queue; + + for (i=0; i < children.Size(); i++) + queue.Push(children[i]); + + while (queue.Size()) + { + node=queue.Pop(); + output.Insert(node, _FILE_AND_LINE_); + for (i=0; i < node->children.Size(); i++) + queue.Push(node->children[i]); + } + } + + template + void Tree::AddChild(TreeType &newData) + { + children.Insert(RakNet::OP_NEW(newData, _FILE_AND_LINE_)); + } + + template + void Tree::DeleteDecendants(void) + { + /* + DataStructures::List output; + LevelOrderTraversal(output); + unsigned i; + for (i=0; i < output.Size(); i++) + RakNet::OP_DELETE(output[i], _FILE_AND_LINE_); +*/ + + // Already recursive to do this + unsigned int i; + for (i=0; i < children.Size(); i++) + RakNet::OP_DELETE(children[i], _FILE_AND_LINE_); + } +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DS_WeightedGraph.h b/project/lib_projects/raknet/jni/RaknetSources/DS_WeightedGraph.h index 28f4424..d2ad665 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DS_WeightedGraph.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DS_WeightedGraph.h @@ -1,537 +1,537 @@ -/// \file DS_WeightedGraph.h -/// \internal -/// \brief Weighted graph. -/// \details I'm assuming the indices are complex map types, rather than sequential numbers (which could be implemented much more efficiently). -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __WEIGHTED_GRAPH_H -#define __WEIGHTED_GRAPH_H - -#include "DS_OrderedList.h" -#include "DS_Map.h" -#include "DS_Heap.h" -#include "DS_Queue.h" -#include "DS_Tree.h" -#include "RakAssert.h" -#include "RakMemoryOverride.h" -#ifdef _DEBUG -#include -#endif - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template - class RAK_DLL_EXPORT WeightedGraph - { - public: - static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(node_type(),node_type());} - - WeightedGraph(); - ~WeightedGraph(); - WeightedGraph( const WeightedGraph& original_copy ); - WeightedGraph& operator= ( const WeightedGraph& original_copy ); - void AddNode(const node_type &node); - void RemoveNode(const node_type &node); - void AddConnection(const node_type &node1, const node_type &node2, weight_type weight); - void RemoveConnection(const node_type &node1, const node_type &node2); - bool HasConnection(const node_type &node1, const node_type &node2); - void Print(void); - void Clear(void); - bool GetShortestPath(DataStructures::List &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT); - bool GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT ); - unsigned GetNodeCount(void) const; - unsigned GetConnectionCount(unsigned nodeIndex) const; - void GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const; - node_type GetNodeAtIndex(unsigned nodeIndex) const; - - protected: - void ClearDijkstra(void); - void GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT); - - DataStructures::Map *> adjacencyLists; - - // All these variables are for path finding with Dijkstra - // 08/23/06 Won't compile as a DLL inside this struct - // struct - // { - bool isValidPath; - node_type rootNode; - DataStructures::OrderedList costMatrixIndices; - weight_type *costMatrix; - node_type *leastNodeArray; - // } dijkstra; - - struct NodeAndParent - { - DataStructures::Tree*node; - DataStructures::Tree*parent; - }; - }; - - template - WeightedGraph::WeightedGraph() - { - isValidPath=false; - costMatrix=0; - } - - template - WeightedGraph::~WeightedGraph() - { - Clear(); - } - - template - WeightedGraph::WeightedGraph( const WeightedGraph& original_copy ) - { - adjacencyLists=original_copy.adjacencyLists; - - isValidPath=original_copy.isValidPath; - if (isValidPath) - { - rootNode=original_copy.rootNode; - costMatrixIndices=original_copy.costMatrixIndices; - costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ ); - leastNodeArray = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size(), _FILE_AND_LINE_ ); - memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type)); - memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type)); - } - } - - template - WeightedGraph& WeightedGraph::operator=( const WeightedGraph& original_copy ) - { - adjacencyLists=original_copy.adjacencyLists; - - isValidPath=original_copy.isValidPath; - if (isValidPath) - { - rootNode=original_copy.rootNode; - costMatrixIndices=original_copy.costMatrixIndices; - costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ ); - leastNodeArray = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size(), _FILE_AND_LINE_ ); - memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type)); - memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type)); - } - - return *this; - } - - template - void WeightedGraph::AddNode(const node_type &node) - { - adjacencyLists.SetNew(node, RakNet::OP_NEW >( _FILE_AND_LINE_) ); - } - - template - void WeightedGraph::RemoveNode(const node_type &node) - { - unsigned i; - DataStructures::Queue removeNodeQueue; - - removeNodeQueue.Push(node, _FILE_AND_LINE_ ); - while (removeNodeQueue.Size()) - { - RakNet::OP_DELETE(adjacencyLists.Pop(removeNodeQueue.Pop()), _FILE_AND_LINE_); - - // Remove this node from all of the other lists as well - for (i=0; i < adjacencyLists.Size(); i++) - { - adjacencyLists[i]->Delete(node); - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (allow_unlinkedNodes==false && adjacencyLists[i]->Size()==0) - removeNodeQueue.Push(adjacencyLists.GetKeyAtIndex(i), _FILE_AND_LINE_ ); - } - } - - ClearDijkstra(); - } - - template - bool WeightedGraph::HasConnection(const node_type &node1, const node_type &node2) - { - if (node1==node2) - return false; - if (adjacencyLists.Has(node1)==false) - return false; - return adjacencyLists.Get(node1)->Has(node2); - } - - template - void WeightedGraph::AddConnection(const node_type &node1, const node_type &node2, weight_type weight) - { - if (node1==node2) - return; - - if (adjacencyLists.Has(node1)==false) - AddNode(node1); - adjacencyLists.Get(node1)->Set(node2, weight); - if (adjacencyLists.Has(node2)==false) - AddNode(node2); - adjacencyLists.Get(node2)->Set(node1, weight); - } - - template - void WeightedGraph::RemoveConnection(const node_type &node1, const node_type &node2) - { - adjacencyLists.Get(node2)->Delete(node1); - adjacencyLists.Get(node1)->Delete(node2); - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (allow_unlinkedNodes==false) // If we do not allow _unlinked nodes, then if there are no connections, remove the node - { - if (adjacencyLists.Get(node1)->Size()==0) - RemoveNode(node1); // Will also remove node1 from the adjacency list of node2 - if (adjacencyLists.Has(node2) && adjacencyLists.Get(node2)->Size()==0) - RemoveNode(node2); - } - - ClearDijkstra(); - } - - template - void WeightedGraph::Clear(void) - { - unsigned i; - for (i=0; i < adjacencyLists.Size(); i++) - RakNet::OP_DELETE(adjacencyLists[i], _FILE_AND_LINE_); - adjacencyLists.Clear(); - - ClearDijkstra(); - } - - template - bool WeightedGraph::GetShortestPath(DataStructures::List &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT) - { - path.Clear(false, _FILE_AND_LINE_); - if (startNode==endNode) - { - path.Insert(startNode, _FILE_AND_LINE_); - path.Insert(endNode, _FILE_AND_LINE_); - return true; - } - - if (isValidPath==false || rootNode!=startNode) - { - ClearDijkstra(); - GenerateDisjktraMatrix(startNode, INFINITE_WEIGHT); - } - - // return the results - bool objectExists; - unsigned col,row; - weight_type currentWeight; - DataStructures::Queue outputQueue; - col=costMatrixIndices.GetIndexFromKey(endNode, &objectExists); - if (costMatrixIndices.Size()<2) - { - return false; - } - if (objectExists==false) - { - return false; - } - node_type vertex; - row=costMatrixIndices.Size()-2; - if (row==0) - { - path.Insert(startNode, _FILE_AND_LINE_); - path.Insert(endNode, _FILE_AND_LINE_); - return true; - } - currentWeight=costMatrix[row*adjacencyLists.Size() + col]; - if (currentWeight==INFINITE_WEIGHT) - { - // No path - return true; - } - vertex=endNode; - outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_); - row--; -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - while (costMatrix[row*adjacencyLists.Size() + col] == currentWeight) - { - if (row==0) - { - path.Insert(startNode, _FILE_AND_LINE_); - for (col=0; outputQueue.Size(); col++) - path.Insert(outputQueue.Pop(), _FILE_AND_LINE_); - return true; - } - --row; - } - - vertex=leastNodeArray[row]; - outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_); - if (row==0) - break; - col=costMatrixIndices.GetIndexFromKey(vertex, &objectExists); - currentWeight=costMatrix[row*adjacencyLists.Size() + col]; - } - - path.Insert(startNode, _FILE_AND_LINE_); - for (col=0; outputQueue.Size(); col++) - path.Insert(outputQueue.Pop(), _FILE_AND_LINE_); - return true; - } - - template - node_type WeightedGraph::GetNodeAtIndex(unsigned nodeIndex) const - { - return adjacencyLists.GetKeyAtIndex(nodeIndex); - } - - template - unsigned WeightedGraph::GetNodeCount(void) const - { - return adjacencyLists.Size(); - } - - template - unsigned WeightedGraph::GetConnectionCount(unsigned nodeIndex) const - { - return adjacencyLists[nodeIndex]->Size(); - } - - template - void WeightedGraph::GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const - { - outWeight=adjacencyLists[nodeIndex]->operator[](connectionIndex); - outNode=adjacencyLists[nodeIndex]->GetKeyAtIndex(connectionIndex); - } - - template - bool WeightedGraph::GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT ) - { - // Find the shortest path from the start node to each of the input nodes. Add this path to a new WeightedGraph if the result is reachable - DataStructures::List path; - DataStructures::WeightedGraph outGraph; - bool res; - unsigned i,j; - for (i=0; i < inputNodes->Size(); i++) - { - res=GetShortestPath(path, startNode, (*inputNodes)[i], INFINITE_WEIGHT); - if (res && path.Size()>0) - { - for (j=0; j < path.Size()-1; j++) - { - // Don't bother looking up the weight - outGraph.AddConnection(path[j], path[j+1], INFINITE_WEIGHT); - } - } - } - - // Copy the graph to a tree. - DataStructures::Queue nodesToProcess; - DataStructures::Tree *current; - DataStructures::Map *adjacencyList; - node_type key; - NodeAndParent nap, nap2; - outTree.DeleteDecendants(); - outTree.data=startNode; - current=&outTree; - if (outGraph.adjacencyLists.Has(startNode)==false) - return false; - adjacencyList = outGraph.adjacencyLists.Get(startNode); - - for (i=0; i < adjacencyList->Size(); i++) - { - nap2.node=RakNet::OP_NEW >( _FILE_AND_LINE_ ); - nap2.node->data=adjacencyList->GetKeyAtIndex(i); - nap2.parent=current; - nodesToProcess.Push(nap2, _FILE_AND_LINE_ ); - current->children.Insert(nap2.node, _FILE_AND_LINE_); - } - - while (nodesToProcess.Size()) - { - nap=nodesToProcess.Pop(); - current=nap.node; - adjacencyList = outGraph.adjacencyLists.Get(nap.node->data); - - for (i=0; i < adjacencyList->Size(); i++) - { - key=adjacencyList->GetKeyAtIndex(i); - if (key!=nap.parent->data) - { - nap2.node=RakNet::OP_NEW >( _FILE_AND_LINE_ ); - nap2.node->data=key; - nap2.parent=current; - nodesToProcess.Push(nap2, _FILE_AND_LINE_ ); - current->children.Insert(nap2.node, _FILE_AND_LINE_); - } - } - } - - return true; - } - - template - void WeightedGraph::GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT) - { - if (adjacencyLists.Size()==0) - return; - - costMatrix = RakNet::OP_NEW_ARRAY(adjacencyLists.Size() * adjacencyLists.Size(), _FILE_AND_LINE_ ); - leastNodeArray = RakNet::OP_NEW_ARRAY(adjacencyLists.Size(), _FILE_AND_LINE_ ); - - node_type currentNode; - unsigned col, row, row2, openSetIndex; - node_type adjacentKey; - unsigned adjacentIndex; - weight_type edgeWeight, currentNodeWeight, adjacentNodeWeight; - DataStructures::Map *adjacencyList; - DataStructures::Heap minHeap; - DataStructures::Map openSet; - - for (col=0; col < adjacencyLists.Size(); col++) - { - // This should be already sorted, so it's a bit inefficient to do an insertion sort, but what the heck - costMatrixIndices.Insert(adjacencyLists.GetKeyAtIndex(col),adjacencyLists.GetKeyAtIndex(col), true, _FILE_AND_LINE_); - } - for (col=0; col < adjacencyLists.Size() * adjacencyLists.Size(); col++) - costMatrix[col]=INFINITE_WEIGHT; - currentNode=startNode; - row=0; - currentNodeWeight=0; - rootNode=startNode; - - // Clear the starting node column - if (adjacencyLists.Size()) - { - adjacentIndex=adjacencyLists.GetIndexAtKey(startNode); - for (row2=0; row2 < adjacencyLists.Size(); row2++) - costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=0; - } - - while (row < adjacencyLists.Size()-1) - { - adjacencyList = adjacencyLists.Get(currentNode); - // Go through all connections from the current node. If the new weight is less than the current weight, then update that weight. - for (col=0; col < adjacencyList->Size(); col++) - { - edgeWeight=(*adjacencyList)[col]; - adjacentKey=adjacencyList->GetKeyAtIndex(col); - adjacentIndex=adjacencyLists.GetIndexAtKey(adjacentKey); - adjacentNodeWeight=costMatrix[row*adjacencyLists.Size() + adjacentIndex]; - - if (currentNodeWeight + edgeWeight < adjacentNodeWeight) - { - // Update the weight for the adjacent node - for (row2=row; row2 < adjacencyLists.Size(); row2++) - costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=currentNodeWeight + edgeWeight; - openSet.Set(adjacentKey, currentNodeWeight + edgeWeight); - } - } - - // Find the lowest in the open set - minHeap.Clear(true,_FILE_AND_LINE_); - for (openSetIndex=0; openSetIndex < openSet.Size(); openSetIndex++) - minHeap.Push(openSet[openSetIndex], openSet.GetKeyAtIndex(openSetIndex),_FILE_AND_LINE_); - - /* - unsigned i,j; - for (i=0; i < adjacencyLists.Size()-1; i++) - { - for (j=0; j < adjacencyLists.Size(); j++) - { - RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); - } - RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); - RAKNET_DEBUG_PRINTF("\n"); - } - */ - - if (minHeap.Size()==0) - { - // Unreachable nodes - isValidPath=true; - return; - } - - currentNodeWeight=minHeap.PeekWeight(0); - leastNodeArray[row]=minHeap.Pop(0); - currentNode=leastNodeArray[row]; - openSet.Delete(currentNode); - row++; - } - - /* -#ifdef _DEBUG - unsigned i,j; - for (i=0; i < adjacencyLists.Size()-1; i++) - { - for (j=0; j < adjacencyLists.Size(); j++) - { - RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); - } - RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); - RAKNET_DEBUG_PRINTF("\n"); - } -#endif - */ - - isValidPath=true; - } - - template - void WeightedGraph::ClearDijkstra(void) - { - if (isValidPath) - { - isValidPath=false; - RakNet::OP_DELETE_ARRAY(costMatrix, _FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(leastNodeArray, _FILE_AND_LINE_); - costMatrixIndices.Clear(false, _FILE_AND_LINE_); - } - } - - template - void WeightedGraph::Print(void) - { -#ifdef _DEBUG - unsigned i,j; - for (i=0; i < adjacencyLists.Size(); i++) - { - //RAKNET_DEBUG_PRINTF("%i connected to ", i); - RAKNET_DEBUG_PRINTF("%s connected to ", adjacencyLists.GetKeyAtIndex(i).systemAddress.ToString()); - - if (adjacencyLists[i]->Size()==0) - RAKNET_DEBUG_PRINTF(""); - else - { - for (j=0; j < adjacencyLists[i]->Size(); j++) - // RAKNET_DEBUG_PRINTF("%i (%.2f) ", adjacencyLists.GetIndexAtKey(adjacencyLists[i]->GetKeyAtIndex(j)), (float) adjacencyLists[i]->operator[](j) ); - RAKNET_DEBUG_PRINTF("%s (%.2f) ", adjacencyLists[i]->GetKeyAtIndex(j).systemAddress.ToString(), (float) adjacencyLists[i]->operator[](j) ); - } - - RAKNET_DEBUG_PRINTF("\n"); - } -#endif - } -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif +/// \file DS_WeightedGraph.h +/// \internal +/// \brief Weighted graph. +/// \details I'm assuming the indices are complex map types, rather than sequential numbers (which could be implemented much more efficiently). +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __WEIGHTED_GRAPH_H +#define __WEIGHTED_GRAPH_H + +#include "DS_OrderedList.h" +#include "DS_Map.h" +#include "DS_Heap.h" +#include "DS_Queue.h" +#include "DS_Tree.h" +#include "RakAssert.h" +#include "RakMemoryOverride.h" +#ifdef _DEBUG +#include +#endif + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + class RAK_DLL_EXPORT WeightedGraph + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(node_type(),node_type());} + + WeightedGraph(); + ~WeightedGraph(); + WeightedGraph( const WeightedGraph& original_copy ); + WeightedGraph& operator= ( const WeightedGraph& original_copy ); + void AddNode(const node_type &node); + void RemoveNode(const node_type &node); + void AddConnection(const node_type &node1, const node_type &node2, weight_type weight); + void RemoveConnection(const node_type &node1, const node_type &node2); + bool HasConnection(const node_type &node1, const node_type &node2); + void Print(void); + void Clear(void); + bool GetShortestPath(DataStructures::List &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT); + bool GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT ); + unsigned GetNodeCount(void) const; + unsigned GetConnectionCount(unsigned nodeIndex) const; + void GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const; + node_type GetNodeAtIndex(unsigned nodeIndex) const; + + protected: + void ClearDijkstra(void); + void GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT); + + DataStructures::Map *> adjacencyLists; + + // All these variables are for path finding with Dijkstra + // 08/23/06 Won't compile as a DLL inside this struct + // struct + // { + bool isValidPath; + node_type rootNode; + DataStructures::OrderedList costMatrixIndices; + weight_type *costMatrix; + node_type *leastNodeArray; + // } dijkstra; + + struct NodeAndParent + { + DataStructures::Tree*node; + DataStructures::Tree*parent; + }; + }; + + template + WeightedGraph::WeightedGraph() + { + isValidPath=false; + costMatrix=0; + } + + template + WeightedGraph::~WeightedGraph() + { + Clear(); + } + + template + WeightedGraph::WeightedGraph( const WeightedGraph& original_copy ) + { + adjacencyLists=original_copy.adjacencyLists; + + isValidPath=original_copy.isValidPath; + if (isValidPath) + { + rootNode=original_copy.rootNode; + costMatrixIndices=original_copy.costMatrixIndices; + costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ ); + leastNodeArray = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size(), _FILE_AND_LINE_ ); + memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type)); + memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type)); + } + } + + template + WeightedGraph& WeightedGraph::operator=( const WeightedGraph& original_copy ) + { + adjacencyLists=original_copy.adjacencyLists; + + isValidPath=original_copy.isValidPath; + if (isValidPath) + { + rootNode=original_copy.rootNode; + costMatrixIndices=original_copy.costMatrixIndices; + costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ ); + leastNodeArray = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size(), _FILE_AND_LINE_ ); + memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type)); + memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type)); + } + + return *this; + } + + template + void WeightedGraph::AddNode(const node_type &node) + { + adjacencyLists.SetNew(node, RakNet::OP_NEW >( _FILE_AND_LINE_) ); + } + + template + void WeightedGraph::RemoveNode(const node_type &node) + { + unsigned i; + DataStructures::Queue removeNodeQueue; + + removeNodeQueue.Push(node, _FILE_AND_LINE_ ); + while (removeNodeQueue.Size()) + { + RakNet::OP_DELETE(adjacencyLists.Pop(removeNodeQueue.Pop()), _FILE_AND_LINE_); + + // Remove this node from all of the other lists as well + for (i=0; i < adjacencyLists.Size(); i++) + { + adjacencyLists[i]->Delete(node); + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (allow_unlinkedNodes==false && adjacencyLists[i]->Size()==0) + removeNodeQueue.Push(adjacencyLists.GetKeyAtIndex(i), _FILE_AND_LINE_ ); + } + } + + ClearDijkstra(); + } + + template + bool WeightedGraph::HasConnection(const node_type &node1, const node_type &node2) + { + if (node1==node2) + return false; + if (adjacencyLists.Has(node1)==false) + return false; + return adjacencyLists.Get(node1)->Has(node2); + } + + template + void WeightedGraph::AddConnection(const node_type &node1, const node_type &node2, weight_type weight) + { + if (node1==node2) + return; + + if (adjacencyLists.Has(node1)==false) + AddNode(node1); + adjacencyLists.Get(node1)->Set(node2, weight); + if (adjacencyLists.Has(node2)==false) + AddNode(node2); + adjacencyLists.Get(node2)->Set(node1, weight); + } + + template + void WeightedGraph::RemoveConnection(const node_type &node1, const node_type &node2) + { + adjacencyLists.Get(node2)->Delete(node1); + adjacencyLists.Get(node1)->Delete(node2); + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (allow_unlinkedNodes==false) // If we do not allow _unlinked nodes, then if there are no connections, remove the node + { + if (adjacencyLists.Get(node1)->Size()==0) + RemoveNode(node1); // Will also remove node1 from the adjacency list of node2 + if (adjacencyLists.Has(node2) && adjacencyLists.Get(node2)->Size()==0) + RemoveNode(node2); + } + + ClearDijkstra(); + } + + template + void WeightedGraph::Clear(void) + { + unsigned i; + for (i=0; i < adjacencyLists.Size(); i++) + RakNet::OP_DELETE(adjacencyLists[i], _FILE_AND_LINE_); + adjacencyLists.Clear(); + + ClearDijkstra(); + } + + template + bool WeightedGraph::GetShortestPath(DataStructures::List &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT) + { + path.Clear(false, _FILE_AND_LINE_); + if (startNode==endNode) + { + path.Insert(startNode, _FILE_AND_LINE_); + path.Insert(endNode, _FILE_AND_LINE_); + return true; + } + + if (isValidPath==false || rootNode!=startNode) + { + ClearDijkstra(); + GenerateDisjktraMatrix(startNode, INFINITE_WEIGHT); + } + + // return the results + bool objectExists; + unsigned col,row; + weight_type currentWeight; + DataStructures::Queue outputQueue; + col=costMatrixIndices.GetIndexFromKey(endNode, &objectExists); + if (costMatrixIndices.Size()<2) + { + return false; + } + if (objectExists==false) + { + return false; + } + node_type vertex; + row=costMatrixIndices.Size()-2; + if (row==0) + { + path.Insert(startNode, _FILE_AND_LINE_); + path.Insert(endNode, _FILE_AND_LINE_); + return true; + } + currentWeight=costMatrix[row*adjacencyLists.Size() + col]; + if (currentWeight==INFINITE_WEIGHT) + { + // No path + return true; + } + vertex=endNode; + outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_); + row--; +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + while (costMatrix[row*adjacencyLists.Size() + col] == currentWeight) + { + if (row==0) + { + path.Insert(startNode, _FILE_AND_LINE_); + for (col=0; outputQueue.Size(); col++) + path.Insert(outputQueue.Pop(), _FILE_AND_LINE_); + return true; + } + --row; + } + + vertex=leastNodeArray[row]; + outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_); + if (row==0) + break; + col=costMatrixIndices.GetIndexFromKey(vertex, &objectExists); + currentWeight=costMatrix[row*adjacencyLists.Size() + col]; + } + + path.Insert(startNode, _FILE_AND_LINE_); + for (col=0; outputQueue.Size(); col++) + path.Insert(outputQueue.Pop(), _FILE_AND_LINE_); + return true; + } + + template + node_type WeightedGraph::GetNodeAtIndex(unsigned nodeIndex) const + { + return adjacencyLists.GetKeyAtIndex(nodeIndex); + } + + template + unsigned WeightedGraph::GetNodeCount(void) const + { + return adjacencyLists.Size(); + } + + template + unsigned WeightedGraph::GetConnectionCount(unsigned nodeIndex) const + { + return adjacencyLists[nodeIndex]->Size(); + } + + template + void WeightedGraph::GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const + { + outWeight=adjacencyLists[nodeIndex]->operator[](connectionIndex); + outNode=adjacencyLists[nodeIndex]->GetKeyAtIndex(connectionIndex); + } + + template + bool WeightedGraph::GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT ) + { + // Find the shortest path from the start node to each of the input nodes. Add this path to a new WeightedGraph if the result is reachable + DataStructures::List path; + DataStructures::WeightedGraph outGraph; + bool res; + unsigned i,j; + for (i=0; i < inputNodes->Size(); i++) + { + res=GetShortestPath(path, startNode, (*inputNodes)[i], INFINITE_WEIGHT); + if (res && path.Size()>0) + { + for (j=0; j < path.Size()-1; j++) + { + // Don't bother looking up the weight + outGraph.AddConnection(path[j], path[j+1], INFINITE_WEIGHT); + } + } + } + + // Copy the graph to a tree. + DataStructures::Queue nodesToProcess; + DataStructures::Tree *current; + DataStructures::Map *adjacencyList; + node_type key; + NodeAndParent nap, nap2; + outTree.DeleteDecendants(); + outTree.data=startNode; + current=&outTree; + if (outGraph.adjacencyLists.Has(startNode)==false) + return false; + adjacencyList = outGraph.adjacencyLists.Get(startNode); + + for (i=0; i < adjacencyList->Size(); i++) + { + nap2.node=RakNet::OP_NEW >( _FILE_AND_LINE_ ); + nap2.node->data=adjacencyList->GetKeyAtIndex(i); + nap2.parent=current; + nodesToProcess.Push(nap2, _FILE_AND_LINE_ ); + current->children.Insert(nap2.node, _FILE_AND_LINE_); + } + + while (nodesToProcess.Size()) + { + nap=nodesToProcess.Pop(); + current=nap.node; + adjacencyList = outGraph.adjacencyLists.Get(nap.node->data); + + for (i=0; i < adjacencyList->Size(); i++) + { + key=adjacencyList->GetKeyAtIndex(i); + if (key!=nap.parent->data) + { + nap2.node=RakNet::OP_NEW >( _FILE_AND_LINE_ ); + nap2.node->data=key; + nap2.parent=current; + nodesToProcess.Push(nap2, _FILE_AND_LINE_ ); + current->children.Insert(nap2.node, _FILE_AND_LINE_); + } + } + } + + return true; + } + + template + void WeightedGraph::GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT) + { + if (adjacencyLists.Size()==0) + return; + + costMatrix = RakNet::OP_NEW_ARRAY(adjacencyLists.Size() * adjacencyLists.Size(), _FILE_AND_LINE_ ); + leastNodeArray = RakNet::OP_NEW_ARRAY(adjacencyLists.Size(), _FILE_AND_LINE_ ); + + node_type currentNode; + unsigned col, row, row2, openSetIndex; + node_type adjacentKey; + unsigned adjacentIndex; + weight_type edgeWeight, currentNodeWeight, adjacentNodeWeight; + DataStructures::Map *adjacencyList; + DataStructures::Heap minHeap; + DataStructures::Map openSet; + + for (col=0; col < adjacencyLists.Size(); col++) + { + // This should be already sorted, so it's a bit inefficient to do an insertion sort, but what the heck + costMatrixIndices.Insert(adjacencyLists.GetKeyAtIndex(col),adjacencyLists.GetKeyAtIndex(col), true, _FILE_AND_LINE_); + } + for (col=0; col < adjacencyLists.Size() * adjacencyLists.Size(); col++) + costMatrix[col]=INFINITE_WEIGHT; + currentNode=startNode; + row=0; + currentNodeWeight=0; + rootNode=startNode; + + // Clear the starting node column + if (adjacencyLists.Size()) + { + adjacentIndex=adjacencyLists.GetIndexAtKey(startNode); + for (row2=0; row2 < adjacencyLists.Size(); row2++) + costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=0; + } + + while (row < adjacencyLists.Size()-1) + { + adjacencyList = adjacencyLists.Get(currentNode); + // Go through all connections from the current node. If the new weight is less than the current weight, then update that weight. + for (col=0; col < adjacencyList->Size(); col++) + { + edgeWeight=(*adjacencyList)[col]; + adjacentKey=adjacencyList->GetKeyAtIndex(col); + adjacentIndex=adjacencyLists.GetIndexAtKey(adjacentKey); + adjacentNodeWeight=costMatrix[row*adjacencyLists.Size() + adjacentIndex]; + + if (currentNodeWeight + edgeWeight < adjacentNodeWeight) + { + // Update the weight for the adjacent node + for (row2=row; row2 < adjacencyLists.Size(); row2++) + costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=currentNodeWeight + edgeWeight; + openSet.Set(adjacentKey, currentNodeWeight + edgeWeight); + } + } + + // Find the lowest in the open set + minHeap.Clear(true,_FILE_AND_LINE_); + for (openSetIndex=0; openSetIndex < openSet.Size(); openSetIndex++) + minHeap.Push(openSet[openSetIndex], openSet.GetKeyAtIndex(openSetIndex),_FILE_AND_LINE_); + + /* + unsigned i,j; + for (i=0; i < adjacencyLists.Size()-1; i++) + { + for (j=0; j < adjacencyLists.Size(); j++) + { + RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); + } + RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); + RAKNET_DEBUG_PRINTF("\n"); + } + */ + + if (minHeap.Size()==0) + { + // Unreachable nodes + isValidPath=true; + return; + } + + currentNodeWeight=minHeap.PeekWeight(0); + leastNodeArray[row]=minHeap.Pop(0); + currentNode=leastNodeArray[row]; + openSet.Delete(currentNode); + row++; + } + + /* +#ifdef _DEBUG + unsigned i,j; + for (i=0; i < adjacencyLists.Size()-1; i++) + { + for (j=0; j < adjacencyLists.Size(); j++) + { + RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); + } + RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); + RAKNET_DEBUG_PRINTF("\n"); + } +#endif + */ + + isValidPath=true; + } + + template + void WeightedGraph::ClearDijkstra(void) + { + if (isValidPath) + { + isValidPath=false; + RakNet::OP_DELETE_ARRAY(costMatrix, _FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(leastNodeArray, _FILE_AND_LINE_); + costMatrixIndices.Clear(false, _FILE_AND_LINE_); + } + } + + template + void WeightedGraph::Print(void) + { +#ifdef _DEBUG + unsigned i,j; + for (i=0; i < adjacencyLists.Size(); i++) + { + //RAKNET_DEBUG_PRINTF("%i connected to ", i); + RAKNET_DEBUG_PRINTF("%s connected to ", adjacencyLists.GetKeyAtIndex(i).systemAddress.ToString()); + + if (adjacencyLists[i]->Size()==0) + RAKNET_DEBUG_PRINTF(""); + else + { + for (j=0; j < adjacencyLists[i]->Size(); j++) + // RAKNET_DEBUG_PRINTF("%i (%.2f) ", adjacencyLists.GetIndexAtKey(adjacencyLists[i]->GetKeyAtIndex(j)), (float) adjacencyLists[i]->operator[](j) ); + RAKNET_DEBUG_PRINTF("%s (%.2f) ", adjacencyLists[i]->GetKeyAtIndex(j).systemAddress.ToString(), (float) adjacencyLists[i]->operator[](j) ); + } + + RAKNET_DEBUG_PRINTF("\n"); + } +#endif + } +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DataCompressor.cpp b/project/lib_projects/raknet/jni/RaknetSources/DataCompressor.cpp index f281ff2..5cf1d34 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DataCompressor.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/DataCompressor.cpp @@ -1,63 +1,63 @@ -#include "DataCompressor.h" -#include "DS_HuffmanEncodingTree.h" -#include "RakAssert.h" -#include // Use string.h rather than memory.h for a console - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(DataCompressor,DataCompressor) - -void DataCompressor::Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ) -{ - // Don't use this for small files as you will just make them bigger! - RakAssert(sizeInBytes > 2048); - - unsigned int frequencyTable[ 256 ]; - unsigned int i; - memset(frequencyTable,0,256*sizeof(unsigned int)); - for (i=0; i < sizeInBytes; i++) - ++frequencyTable[userData[i]]; - HuffmanEncodingTree tree; - BitSize_t writeOffset1, writeOffset2, bitsUsed1, bitsUsed2; - tree.GenerateFromFrequencyTable(frequencyTable); - output->WriteCompressed(sizeInBytes); - for (i=0; i < 256; i++) - output->WriteCompressed(frequencyTable[i]); - output->AlignWriteToByteBoundary(); - writeOffset1=output->GetWriteOffset(); - output->Write((unsigned int)0); // Dummy value - bitsUsed1=output->GetNumberOfBitsUsed(); - tree.EncodeArray(userData, sizeInBytes, output); - bitsUsed2=output->GetNumberOfBitsUsed(); - writeOffset2=output->GetWriteOffset(); - output->SetWriteOffset(writeOffset1); - output->Write(bitsUsed2-bitsUsed1); // Go back and write how many bits were used for the encoding - output->SetWriteOffset(writeOffset2); -} - -unsigned DataCompressor::DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ) -{ - HuffmanEncodingTree tree; - unsigned int bitsUsed, destinationSizeInBytes; - unsigned int decompressedBytes; - unsigned int frequencyTable[ 256 ]; - unsigned i; - - input->ReadCompressed(destinationSizeInBytes); - for (i=0; i < 256; i++) - input->ReadCompressed(frequencyTable[i]); - input->AlignReadToByteBoundary(); - if (input->Read(bitsUsed)==false) - { - // Read error -#ifdef _DEBUG - RakAssert(0); -#endif - return 0; - } - *output = (unsigned char*) rakMalloc_Ex(destinationSizeInBytes, _FILE_AND_LINE_); - tree.GenerateFromFrequencyTable(frequencyTable); - decompressedBytes=tree.DecodeArray(input, bitsUsed, destinationSizeInBytes, *output ); - RakAssert(decompressedBytes==destinationSizeInBytes); - return destinationSizeInBytes; -} +#include "DataCompressor.h" +#include "DS_HuffmanEncodingTree.h" +#include "RakAssert.h" +#include // Use string.h rather than memory.h for a console + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(DataCompressor,DataCompressor) + +void DataCompressor::Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ) +{ + // Don't use this for small files as you will just make them bigger! + RakAssert(sizeInBytes > 2048); + + unsigned int frequencyTable[ 256 ]; + unsigned int i; + memset(frequencyTable,0,256*sizeof(unsigned int)); + for (i=0; i < sizeInBytes; i++) + ++frequencyTable[userData[i]]; + HuffmanEncodingTree tree; + BitSize_t writeOffset1, writeOffset2, bitsUsed1, bitsUsed2; + tree.GenerateFromFrequencyTable(frequencyTable); + output->WriteCompressed(sizeInBytes); + for (i=0; i < 256; i++) + output->WriteCompressed(frequencyTable[i]); + output->AlignWriteToByteBoundary(); + writeOffset1=output->GetWriteOffset(); + output->Write((unsigned int)0); // Dummy value + bitsUsed1=output->GetNumberOfBitsUsed(); + tree.EncodeArray(userData, sizeInBytes, output); + bitsUsed2=output->GetNumberOfBitsUsed(); + writeOffset2=output->GetWriteOffset(); + output->SetWriteOffset(writeOffset1); + output->Write(bitsUsed2-bitsUsed1); // Go back and write how many bits were used for the encoding + output->SetWriteOffset(writeOffset2); +} + +unsigned DataCompressor::DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ) +{ + HuffmanEncodingTree tree; + unsigned int bitsUsed, destinationSizeInBytes; + unsigned int decompressedBytes; + unsigned int frequencyTable[ 256 ]; + unsigned i; + + input->ReadCompressed(destinationSizeInBytes); + for (i=0; i < 256; i++) + input->ReadCompressed(frequencyTable[i]); + input->AlignReadToByteBoundary(); + if (input->Read(bitsUsed)==false) + { + // Read error +#ifdef _DEBUG + RakAssert(0); +#endif + return 0; + } + *output = (unsigned char*) rakMalloc_Ex(destinationSizeInBytes, _FILE_AND_LINE_); + tree.GenerateFromFrequencyTable(frequencyTable); + decompressedBytes=tree.DecodeArray(input, bitsUsed, destinationSizeInBytes, *output ); + RakAssert(decompressedBytes==destinationSizeInBytes); + return destinationSizeInBytes; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/DataCompressor.h b/project/lib_projects/raknet/jni/RaknetSources/DataCompressor.h index 9152396..57f88ce 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DataCompressor.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DataCompressor.h @@ -1,33 +1,33 @@ -/// \file DataCompressor.h -/// \brief DataCompressor does compression on a block of data. -/// \details Not very good compression, but it's small and fast so is something you can use per-message at runtime. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __DATA_COMPRESSOR_H -#define __DATA_COMPRESSOR_H - -#include "RakMemoryOverride.h" -#include "DS_HuffmanEncodingTree.h" -#include "Export.h" - -namespace RakNet -{ - -/// \brief Does compression on a block of data. Not very good compression, but it's small and fast so is something you can compute at runtime. -class RAK_DLL_EXPORT DataCompressor -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(DataCompressor) - - static void Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ); - static unsigned DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ); -}; - -} // namespace RakNet - -#endif +/// \file DataCompressor.h +/// \brief DataCompressor does compression on a block of data. +/// \details Not very good compression, but it's small and fast so is something you can use per-message at runtime. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __DATA_COMPRESSOR_H +#define __DATA_COMPRESSOR_H + +#include "RakMemoryOverride.h" +#include "DS_HuffmanEncodingTree.h" +#include "Export.h" + +namespace RakNet +{ + +/// \brief Does compression on a block of data. Not very good compression, but it's small and fast so is something you can compute at runtime. +class RAK_DLL_EXPORT DataCompressor +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(DataCompressor) + + static void Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ); + static unsigned DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ); +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/DirectoryDeltaTransfer.cpp b/project/lib_projects/raknet/jni/RaknetSources/DirectoryDeltaTransfer.cpp index e612c92..2386ff9 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DirectoryDeltaTransfer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/DirectoryDeltaTransfer.cpp @@ -1,242 +1,242 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 - -#include "DirectoryDeltaTransfer.h" -#include "FileList.h" -#include "StringCompressor.h" -#include "RakPeerInterface.h" -#include "FileListTransfer.h" -#include "FileListTransferCBInterface.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "FileOperations.h" -#include "IncrementalReadInterface.h" - -using namespace RakNet; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -class DDTCallback : public FileListTransferCBInterface -{ -public: - unsigned subdirLen; - char outputSubdir[512]; - FileListTransferCBInterface *onFileCallback; - - DDTCallback() {} - virtual ~DDTCallback() {} - - virtual bool OnFile(OnFileStruct *onFileStruct) - { - char fullPathToDir[1024]; - - if (onFileStruct->fileName && onFileStruct->fileData && subdirLen < strlen(onFileStruct->fileName)) - { - strcpy(fullPathToDir, outputSubdir); - strcat(fullPathToDir, onFileStruct->fileName+subdirLen); - WriteFileWithDirectories(fullPathToDir, (char*)onFileStruct->fileData, (unsigned int ) onFileStruct->byteLengthOfThisFile); - } - else - fullPathToDir[0]=0; - - return onFileCallback->OnFile(onFileStruct); - } - - virtual void OnFileProgress(FileProgressStruct *fps) - { - char fullPathToDir[1024]; - - if (fps->onFileStruct->fileName && subdirLen < strlen(fps->onFileStruct->fileName)) - { - strcpy(fullPathToDir, outputSubdir); - strcat(fullPathToDir, fps->onFileStruct->fileName+subdirLen); - } - else - fullPathToDir[0]=0; - - onFileCallback->OnFileProgress(fps); - } - virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs) - { - return onFileCallback->OnDownloadComplete(dcs); - } -}; - -STATIC_FACTORY_DEFINITIONS(DirectoryDeltaTransfer,DirectoryDeltaTransfer); - -DirectoryDeltaTransfer::DirectoryDeltaTransfer() -{ - applicationDirectory[0]=0; - fileListTransfer=0; - availableUploads = RakNet::OP_NEW( _FILE_AND_LINE_ ); - priority=HIGH_PRIORITY; - orderingChannel=0; - incrementalReadInterface=0; -} -DirectoryDeltaTransfer::~DirectoryDeltaTransfer() -{ - RakNet::OP_DELETE(availableUploads, _FILE_AND_LINE_); -} -void DirectoryDeltaTransfer::SetFileListTransferPlugin(FileListTransfer *flt) -{ - if (fileListTransfer) - { - DataStructures::List fileListProgressList; - fileListTransfer->GetCallbacks(fileListProgressList); - unsigned int i; - for (i=0; i < fileListProgressList.Size(); i++) - availableUploads->RemoveCallback(fileListProgressList[i]); - } - - fileListTransfer=flt; - - if (flt) - { - DataStructures::List fileListProgressList; - flt->GetCallbacks(fileListProgressList); - unsigned int i; - for (i=0; i < fileListProgressList.Size(); i++) - availableUploads->AddCallback(fileListProgressList[i]); - } - else - { - availableUploads->ClearCallbacks(); - } -} -void DirectoryDeltaTransfer::SetApplicationDirectory(const char *pathToApplication) -{ - if (pathToApplication==0 || pathToApplication[0]==0) - applicationDirectory[0]=0; - else - { - strncpy(applicationDirectory, pathToApplication, 510); - if (applicationDirectory[strlen(applicationDirectory)-1]!='/' && applicationDirectory[strlen(applicationDirectory)-1]!='\\') - strcat(applicationDirectory, "/"); - applicationDirectory[511]=0; - } -} -void DirectoryDeltaTransfer::SetUploadSendParameters(PacketPriority _priority, char _orderingChannel) -{ - priority=_priority; - orderingChannel=_orderingChannel; -} -void DirectoryDeltaTransfer::AddUploadsFromSubdirectory(const char *subdir) -{ - availableUploads->AddFilesFromDirectory(applicationDirectory, subdir, true, false, true, FileListNodeContext(0,0)); -} -unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb) -{ - RakAssert(host!=UNASSIGNED_SYSTEM_ADDRESS); - - DDTCallback *transferCallback; - - localFiles.AddCallback(cb); - - // Prepare the callback data - transferCallback = RakNet::OP_NEW( _FILE_AND_LINE_ ); - if (subdir && subdir[0]) - { - transferCallback->subdirLen=(unsigned int)strlen(subdir); - if (subdir[transferCallback->subdirLen-1]!='/' && subdir[transferCallback->subdirLen-1]!='\\') - transferCallback->subdirLen++; - } - else - transferCallback->subdirLen=0; - if (prependAppDirToOutputSubdir) - strcpy(transferCallback->outputSubdir, applicationDirectory); - else - transferCallback->outputSubdir[0]=0; - if (outputSubdir) - strcat(transferCallback->outputSubdir, outputSubdir); - if (transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='/' && transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='\\') - strcat(transferCallback->outputSubdir, "/"); - transferCallback->onFileCallback=onFileCallback; - - // Setup the transfer plugin to get the response to this download request - unsigned short setId = fileListTransfer->SetupReceive(transferCallback, true, host); - - // Send to the host, telling it to process this request - RakNet::BitStream outBitstream; - outBitstream.Write((MessageID)ID_DDT_DOWNLOAD_REQUEST); - outBitstream.Write(setId); - StringCompressor::Instance()->EncodeString(subdir, 256, &outBitstream); - StringCompressor::Instance()->EncodeString(outputSubdir, 256, &outBitstream); - localFiles.Serialize(&outBitstream); - SendUnified(&outBitstream, _priority, RELIABLE_ORDERED, _orderingChannel, host, false); - - return setId; -} -unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb) -{ - FileList localFiles; - // Get a hash of all the files that we already have (if any) - localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0)); - return DownloadFromSubdirectory(localFiles, subdir, outputSubdir, prependAppDirToOutputSubdir, host, onFileCallback, _priority, _orderingChannel, cb); -} -void DirectoryDeltaTransfer::GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir) -{ - localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0)); -} -void DirectoryDeltaTransfer::ClearUploads(void) -{ - availableUploads->Clear(); -} -void DirectoryDeltaTransfer::OnDownloadRequest(Packet *packet) -{ - char subdir[256]; - char remoteSubdir[256]; - RakNet::BitStream inBitstream(packet->data, packet->length, false); - FileList remoteFileHash; - FileList delta; - unsigned short setId; - inBitstream.IgnoreBits(8); - inBitstream.Read(setId); - StringCompressor::Instance()->DecodeString(subdir, 256, &inBitstream); - StringCompressor::Instance()->DecodeString(remoteSubdir, 256, &inBitstream); - if (remoteFileHash.Deserialize(&inBitstream)==false) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return; - } - - availableUploads->GetDeltaToCurrent(&remoteFileHash, &delta, subdir, remoteSubdir); - if (incrementalReadInterface==0) - delta.PopulateDataFromDisk(applicationDirectory, true, false, true); - else - delta.FlagFilesAsReferences(); - - // This will call the ddtCallback interface that was passed to FileListTransfer::SetupReceive on the remote system - fileListTransfer->Send(&delta, rakPeerInterface, packet->systemAddress, setId, priority, orderingChannel, incrementalReadInterface, chunkSize); -} -PluginReceiveResult DirectoryDeltaTransfer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_DDT_DOWNLOAD_REQUEST: - OnDownloadRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - return RR_CONTINUE_PROCESSING; -} - -unsigned DirectoryDeltaTransfer::GetNumberOfFilesForUpload(void) const -{ - return availableUploads->fileList.Size(); -} - -void DirectoryDeltaTransfer::SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) -{ - incrementalReadInterface=_incrementalReadInterface; - chunkSize=_chunkSize; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 + +#include "DirectoryDeltaTransfer.h" +#include "FileList.h" +#include "StringCompressor.h" +#include "RakPeerInterface.h" +#include "FileListTransfer.h" +#include "FileListTransferCBInterface.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "FileOperations.h" +#include "IncrementalReadInterface.h" + +using namespace RakNet; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +class DDTCallback : public FileListTransferCBInterface +{ +public: + unsigned subdirLen; + char outputSubdir[512]; + FileListTransferCBInterface *onFileCallback; + + DDTCallback() {} + virtual ~DDTCallback() {} + + virtual bool OnFile(OnFileStruct *onFileStruct) + { + char fullPathToDir[1024]; + + if (onFileStruct->fileName && onFileStruct->fileData && subdirLen < strlen(onFileStruct->fileName)) + { + strcpy(fullPathToDir, outputSubdir); + strcat(fullPathToDir, onFileStruct->fileName+subdirLen); + WriteFileWithDirectories(fullPathToDir, (char*)onFileStruct->fileData, (unsigned int ) onFileStruct->byteLengthOfThisFile); + } + else + fullPathToDir[0]=0; + + return onFileCallback->OnFile(onFileStruct); + } + + virtual void OnFileProgress(FileProgressStruct *fps) + { + char fullPathToDir[1024]; + + if (fps->onFileStruct->fileName && subdirLen < strlen(fps->onFileStruct->fileName)) + { + strcpy(fullPathToDir, outputSubdir); + strcat(fullPathToDir, fps->onFileStruct->fileName+subdirLen); + } + else + fullPathToDir[0]=0; + + onFileCallback->OnFileProgress(fps); + } + virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs) + { + return onFileCallback->OnDownloadComplete(dcs); + } +}; + +STATIC_FACTORY_DEFINITIONS(DirectoryDeltaTransfer,DirectoryDeltaTransfer); + +DirectoryDeltaTransfer::DirectoryDeltaTransfer() +{ + applicationDirectory[0]=0; + fileListTransfer=0; + availableUploads = RakNet::OP_NEW( _FILE_AND_LINE_ ); + priority=HIGH_PRIORITY; + orderingChannel=0; + incrementalReadInterface=0; +} +DirectoryDeltaTransfer::~DirectoryDeltaTransfer() +{ + RakNet::OP_DELETE(availableUploads, _FILE_AND_LINE_); +} +void DirectoryDeltaTransfer::SetFileListTransferPlugin(FileListTransfer *flt) +{ + if (fileListTransfer) + { + DataStructures::List fileListProgressList; + fileListTransfer->GetCallbacks(fileListProgressList); + unsigned int i; + for (i=0; i < fileListProgressList.Size(); i++) + availableUploads->RemoveCallback(fileListProgressList[i]); + } + + fileListTransfer=flt; + + if (flt) + { + DataStructures::List fileListProgressList; + flt->GetCallbacks(fileListProgressList); + unsigned int i; + for (i=0; i < fileListProgressList.Size(); i++) + availableUploads->AddCallback(fileListProgressList[i]); + } + else + { + availableUploads->ClearCallbacks(); + } +} +void DirectoryDeltaTransfer::SetApplicationDirectory(const char *pathToApplication) +{ + if (pathToApplication==0 || pathToApplication[0]==0) + applicationDirectory[0]=0; + else + { + strncpy(applicationDirectory, pathToApplication, 510); + if (applicationDirectory[strlen(applicationDirectory)-1]!='/' && applicationDirectory[strlen(applicationDirectory)-1]!='\\') + strcat(applicationDirectory, "/"); + applicationDirectory[511]=0; + } +} +void DirectoryDeltaTransfer::SetUploadSendParameters(PacketPriority _priority, char _orderingChannel) +{ + priority=_priority; + orderingChannel=_orderingChannel; +} +void DirectoryDeltaTransfer::AddUploadsFromSubdirectory(const char *subdir) +{ + availableUploads->AddFilesFromDirectory(applicationDirectory, subdir, true, false, true, FileListNodeContext(0,0)); +} +unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb) +{ + RakAssert(host!=UNASSIGNED_SYSTEM_ADDRESS); + + DDTCallback *transferCallback; + + localFiles.AddCallback(cb); + + // Prepare the callback data + transferCallback = RakNet::OP_NEW( _FILE_AND_LINE_ ); + if (subdir && subdir[0]) + { + transferCallback->subdirLen=(unsigned int)strlen(subdir); + if (subdir[transferCallback->subdirLen-1]!='/' && subdir[transferCallback->subdirLen-1]!='\\') + transferCallback->subdirLen++; + } + else + transferCallback->subdirLen=0; + if (prependAppDirToOutputSubdir) + strcpy(transferCallback->outputSubdir, applicationDirectory); + else + transferCallback->outputSubdir[0]=0; + if (outputSubdir) + strcat(transferCallback->outputSubdir, outputSubdir); + if (transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='/' && transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='\\') + strcat(transferCallback->outputSubdir, "/"); + transferCallback->onFileCallback=onFileCallback; + + // Setup the transfer plugin to get the response to this download request + unsigned short setId = fileListTransfer->SetupReceive(transferCallback, true, host); + + // Send to the host, telling it to process this request + RakNet::BitStream outBitstream; + outBitstream.Write((MessageID)ID_DDT_DOWNLOAD_REQUEST); + outBitstream.Write(setId); + StringCompressor::Instance()->EncodeString(subdir, 256, &outBitstream); + StringCompressor::Instance()->EncodeString(outputSubdir, 256, &outBitstream); + localFiles.Serialize(&outBitstream); + SendUnified(&outBitstream, _priority, RELIABLE_ORDERED, _orderingChannel, host, false); + + return setId; +} +unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb) +{ + FileList localFiles; + // Get a hash of all the files that we already have (if any) + localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0)); + return DownloadFromSubdirectory(localFiles, subdir, outputSubdir, prependAppDirToOutputSubdir, host, onFileCallback, _priority, _orderingChannel, cb); +} +void DirectoryDeltaTransfer::GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir) +{ + localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0)); +} +void DirectoryDeltaTransfer::ClearUploads(void) +{ + availableUploads->Clear(); +} +void DirectoryDeltaTransfer::OnDownloadRequest(Packet *packet) +{ + char subdir[256]; + char remoteSubdir[256]; + RakNet::BitStream inBitstream(packet->data, packet->length, false); + FileList remoteFileHash; + FileList delta; + unsigned short setId; + inBitstream.IgnoreBits(8); + inBitstream.Read(setId); + StringCompressor::Instance()->DecodeString(subdir, 256, &inBitstream); + StringCompressor::Instance()->DecodeString(remoteSubdir, 256, &inBitstream); + if (remoteFileHash.Deserialize(&inBitstream)==false) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return; + } + + availableUploads->GetDeltaToCurrent(&remoteFileHash, &delta, subdir, remoteSubdir); + if (incrementalReadInterface==0) + delta.PopulateDataFromDisk(applicationDirectory, true, false, true); + else + delta.FlagFilesAsReferences(); + + // This will call the ddtCallback interface that was passed to FileListTransfer::SetupReceive on the remote system + fileListTransfer->Send(&delta, rakPeerInterface, packet->systemAddress, setId, priority, orderingChannel, incrementalReadInterface, chunkSize); +} +PluginReceiveResult DirectoryDeltaTransfer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_DDT_DOWNLOAD_REQUEST: + OnDownloadRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + return RR_CONTINUE_PROCESSING; +} + +unsigned DirectoryDeltaTransfer::GetNumberOfFilesForUpload(void) const +{ + return availableUploads->fileList.Size(); +} + +void DirectoryDeltaTransfer::SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) +{ + incrementalReadInterface=_incrementalReadInterface; + chunkSize=_chunkSize; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/DirectoryDeltaTransfer.h b/project/lib_projects/raknet/jni/RaknetSources/DirectoryDeltaTransfer.h index 5c78622..1162e28 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DirectoryDeltaTransfer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DirectoryDeltaTransfer.h @@ -1,164 +1,164 @@ -/// \file DirectoryDeltaTransfer.h -/// \brief Simple class to send changes between directories. -/// \details In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __DIRECTORY_DELTA_TRANSFER_H -#define __DIRECTORY_DELTA_TRANSFER_H - -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "DS_Map.h" -#include "PacketPriority.h" - -/// \defgroup DIRECTORY_DELTA_TRANSFER_GROUP DirectoryDeltaTransfer -/// \brief Simple class to send changes between directories -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief Simple class to send changes between directories. In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. -/// \details -/// \sa AutopatcherClient class for database driven patching, including binary deltas and search by date. -/// -/// To use, first set the path to your application. For example "C:/Games/MyRPG/"
-/// To allow other systems to download files, call AddUploadsFromSubdirectory, where the parameter is a path relative
-/// to the path to your application. This includes subdirectories.
-/// For example:
-/// SetApplicationDirectory("C:/Games/MyRPG/");
-/// AddUploadsFromSubdirectory("Mods/Skins/");
-/// would allow downloads from
-/// "C:/Games/MyRPG/Mods/Skins/*.*" as well as "C:/Games/MyRPG/Mods/Skins/Level1/*.*"
-/// It would NOT allow downloads from C:/Games/MyRPG/Levels, nor would it allow downloads from C:/Windows
-/// While pathToApplication can be anything you want, applicationSubdirectory must match either partially or fully between systems. -/// \ingroup DIRECTORY_DELTA_TRANSFER_GROUP - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -class FileList; -struct Packet; -struct InternalPacket; -struct DownloadRequest; -class FileListTransfer; -class FileListTransferCBInterface; -class FileListProgress; -class IncrementalReadInterface; - -class RAK_DLL_EXPORT DirectoryDeltaTransfer : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(DirectoryDeltaTransfer) - - // Constructor - DirectoryDeltaTransfer(); - - // Destructor - virtual ~DirectoryDeltaTransfer(); - - /// \brief This plugin has a dependency on the FileListTransfer plugin, which it uses to actually send the files. - /// \details So you need an instance of that plugin registered with RakPeerInterface, and a pointer to that interface should be passed here. - /// \param[in] flt A pointer to a registered instance of FileListTransfer - void SetFileListTransferPlugin(FileListTransfer *flt); - - /// \brief Set the local root directory to base all file uploads and downloads off of. - /// \param[in] pathToApplication This path will be prepended to \a applicationSubdirectory in AddUploadsFromSubdirectory to find the actual path on disk. - void SetApplicationDirectory(const char *pathToApplication); - - /// \brief What parameters to use for the RakPeerInterface::Send() call when uploading files. - /// \param[in] _priority See RakPeerInterface::Send() - /// \param[in] _orderingChannel See RakPeerInterface::Send() - void SetUploadSendParameters(PacketPriority _priority, char _orderingChannel); - - /// \brief Add all files in the specified subdirectory recursively. - /// \details \a subdir is appended to \a pathToApplication in SetApplicationDirectory(). - /// All files in the resultant directory and subdirectories are then hashed so that users can download them. - /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin - /// \param[in] subdir Concatenated with pathToApplication to form the final path from which to allow uploads. - void AddUploadsFromSubdirectory(const char *subdir); - - /// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory. - /// \details \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory - /// Therefore, - /// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"... - /// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"... - /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin - /// \note Blocking. Will block while hashes of the local files are generated - /// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir. - /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. - /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. - /// \param[in] host The address of the remote system to send the message to. - /// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done - /// \param[in] _priority See RakPeerInterface::Send() - /// \param[in] _orderingChannel See RakPeerInterface::Send() - /// \param[in] cb Callback to get progress updates. Pass 0 to not use. - /// \return A set ID, identifying this download set. Returns 65535 on host unreachable. - unsigned short DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb); - - /// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory. - /// \details \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory - /// Therefore, - /// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"... - /// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"... - /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin - /// \note Nonblocking, but requires call to GenerateHashes() - /// \param[in] localFiles Hashes of local files already on the harddrive. Populate with GenerateHashes(), which you may wish to call from a thread - /// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir. - /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. - /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. - /// \param[in] host The address of the remote system to send the message to. - /// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done - /// \param[in] _priority See RakPeerInterface::Send() - /// \param[in] _orderingChannel See RakPeerInterface::Send() - /// \param[in] cb Callback to get progress updates. Pass 0 to not use. - /// \return A set ID, identifying this download set. Returns 65535 on host unreachable. - unsigned short DownloadFromSubdirectory(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb); - - /// Hash files already on the harddrive, in preparation for a call to DownloadFromSubdirectory(). Passed to second version of DownloadFromSubdirectory() - /// This is slow, and it is exposed so you can call it from a thread before calling DownloadFromSubdirectory() - /// \param[out] localFiles List of hashed files populated from \a outputSubdir and \a prependAppDirToOutputSubdir - /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. - /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. - void GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir); - - /// \brief Clear all allowed uploads previously set with AddUploadsFromSubdirectory - void ClearUploads(void); - - /// \brief Returns how many files are available for upload - /// \return How many files are available for upload - unsigned GetNumberOfFilesForUpload(void) const; - - /// \brief Normally, if a remote system requests files, those files are all loaded into memory and sent immediately. - /// \details This function allows the files to be read in incremental chunks, saving memory - /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, filePullInterface will be used to read the file in chunks of size \a chunkSize - /// \param[in] _chunkSize How large of a block of a file to send at once - void SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); -protected: - void OnDownloadRequest(Packet *packet); - - char applicationDirectory[512]; - FileListTransfer *fileListTransfer; - FileList *availableUploads; - PacketPriority priority; - char orderingChannel; - IncrementalReadInterface *incrementalReadInterface; - unsigned int chunkSize; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file DirectoryDeltaTransfer.h +/// \brief Simple class to send changes between directories. +/// \details In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __DIRECTORY_DELTA_TRANSFER_H +#define __DIRECTORY_DELTA_TRANSFER_H + +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "DS_Map.h" +#include "PacketPriority.h" + +/// \defgroup DIRECTORY_DELTA_TRANSFER_GROUP DirectoryDeltaTransfer +/// \brief Simple class to send changes between directories +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief Simple class to send changes between directories. In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. +/// \details +/// \sa AutopatcherClient class for database driven patching, including binary deltas and search by date. +/// +/// To use, first set the path to your application. For example "C:/Games/MyRPG/"
+/// To allow other systems to download files, call AddUploadsFromSubdirectory, where the parameter is a path relative
+/// to the path to your application. This includes subdirectories.
+/// For example:
+/// SetApplicationDirectory("C:/Games/MyRPG/");
+/// AddUploadsFromSubdirectory("Mods/Skins/");
+/// would allow downloads from
+/// "C:/Games/MyRPG/Mods/Skins/*.*" as well as "C:/Games/MyRPG/Mods/Skins/Level1/*.*"
+/// It would NOT allow downloads from C:/Games/MyRPG/Levels, nor would it allow downloads from C:/Windows
+/// While pathToApplication can be anything you want, applicationSubdirectory must match either partially or fully between systems. +/// \ingroup DIRECTORY_DELTA_TRANSFER_GROUP + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +class FileList; +struct Packet; +struct InternalPacket; +struct DownloadRequest; +class FileListTransfer; +class FileListTransferCBInterface; +class FileListProgress; +class IncrementalReadInterface; + +class RAK_DLL_EXPORT DirectoryDeltaTransfer : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(DirectoryDeltaTransfer) + + // Constructor + DirectoryDeltaTransfer(); + + // Destructor + virtual ~DirectoryDeltaTransfer(); + + /// \brief This plugin has a dependency on the FileListTransfer plugin, which it uses to actually send the files. + /// \details So you need an instance of that plugin registered with RakPeerInterface, and a pointer to that interface should be passed here. + /// \param[in] flt A pointer to a registered instance of FileListTransfer + void SetFileListTransferPlugin(FileListTransfer *flt); + + /// \brief Set the local root directory to base all file uploads and downloads off of. + /// \param[in] pathToApplication This path will be prepended to \a applicationSubdirectory in AddUploadsFromSubdirectory to find the actual path on disk. + void SetApplicationDirectory(const char *pathToApplication); + + /// \brief What parameters to use for the RakPeerInterface::Send() call when uploading files. + /// \param[in] _priority See RakPeerInterface::Send() + /// \param[in] _orderingChannel See RakPeerInterface::Send() + void SetUploadSendParameters(PacketPriority _priority, char _orderingChannel); + + /// \brief Add all files in the specified subdirectory recursively. + /// \details \a subdir is appended to \a pathToApplication in SetApplicationDirectory(). + /// All files in the resultant directory and subdirectories are then hashed so that users can download them. + /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin + /// \param[in] subdir Concatenated with pathToApplication to form the final path from which to allow uploads. + void AddUploadsFromSubdirectory(const char *subdir); + + /// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory. + /// \details \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory + /// Therefore, + /// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"... + /// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"... + /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin + /// \note Blocking. Will block while hashes of the local files are generated + /// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir. + /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. + /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. + /// \param[in] host The address of the remote system to send the message to. + /// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done + /// \param[in] _priority See RakPeerInterface::Send() + /// \param[in] _orderingChannel See RakPeerInterface::Send() + /// \param[in] cb Callback to get progress updates. Pass 0 to not use. + /// \return A set ID, identifying this download set. Returns 65535 on host unreachable. + unsigned short DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb); + + /// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory. + /// \details \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory + /// Therefore, + /// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"... + /// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"... + /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin + /// \note Nonblocking, but requires call to GenerateHashes() + /// \param[in] localFiles Hashes of local files already on the harddrive. Populate with GenerateHashes(), which you may wish to call from a thread + /// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir. + /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. + /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. + /// \param[in] host The address of the remote system to send the message to. + /// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done + /// \param[in] _priority See RakPeerInterface::Send() + /// \param[in] _orderingChannel See RakPeerInterface::Send() + /// \param[in] cb Callback to get progress updates. Pass 0 to not use. + /// \return A set ID, identifying this download set. Returns 65535 on host unreachable. + unsigned short DownloadFromSubdirectory(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb); + + /// Hash files already on the harddrive, in preparation for a call to DownloadFromSubdirectory(). Passed to second version of DownloadFromSubdirectory() + /// This is slow, and it is exposed so you can call it from a thread before calling DownloadFromSubdirectory() + /// \param[out] localFiles List of hashed files populated from \a outputSubdir and \a prependAppDirToOutputSubdir + /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. + /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. + void GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir); + + /// \brief Clear all allowed uploads previously set with AddUploadsFromSubdirectory + void ClearUploads(void); + + /// \brief Returns how many files are available for upload + /// \return How many files are available for upload + unsigned GetNumberOfFilesForUpload(void) const; + + /// \brief Normally, if a remote system requests files, those files are all loaded into memory and sent immediately. + /// \details This function allows the files to be read in incremental chunks, saving memory + /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, filePullInterface will be used to read the file in chunks of size \a chunkSize + /// \param[in] _chunkSize How large of a block of a file to send at once + void SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); +protected: + void OnDownloadRequest(Packet *packet); + + char applicationDirectory[512]; + FileListTransfer *fileListTransfer; + FileList *availableUploads; + PacketPriority priority; + char orderingChannel; + IncrementalReadInterface *incrementalReadInterface; + unsigned int chunkSize; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/DynDNS.cpp b/project/lib_projects/raknet/jni/RaknetSources/DynDNS.cpp index 3e189ee..bd51714 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DynDNS.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/DynDNS.cpp @@ -1,236 +1,236 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "TCPInterface.h" -#include "SocketLayer.h" -#include "DynDNS.h" -#include "GetTime.h" - -using namespace RakNet; - -struct DynDnsResult -{ - const char *description; - const char *code; - DynDnsResultCode resultCode; -}; - -DynDnsResult resultTable[13] = -{ - // See http://www.dyndns.com/developers/specs/flow.pdf - {"DNS update success.\nPlease wait up to 60 seconds for the change to take effect.\n", "good", RC_SUCCESS}, // Even with success, it takes time for the cache to update! - {"No change", "nochg", RC_NO_CHANGE}, - {"Host has been blocked. You will need to contact DynDNS to reenable.", "abuse", RC_ABUSE}, - {"Useragent is blocked", "badagent", RC_BAD_AGENT}, - {"Username/password pair bad", "badauth", RC_BAD_AUTH}, - {"Bad system parameter", "badsys", RC_BAD_SYS}, - {"DNS inconsistency", "dnserr", RC_DNS_ERROR}, - {"Paid account feature", "!donator", RC_NOT_DONATOR}, - {"No such host in system", "nohost", RC_NO_HOST}, - {"Invalid hostname format", "notfqdn", RC_NOT_FQDN}, - {"Serious error", "numhost", RC_NUM_HOST}, - {"This host exists, but does not belong to you", "!yours", RC_NOT_YOURS}, - {"911", "911", RC_911}, -}; -DynDNS::DynDNS() -{ - connectPhase=CP_IDLE; - tcp=0; -} -DynDNS::~DynDNS() -{ - if (tcp) - RakNet::OP_DELETE(tcp, _FILE_AND_LINE_); -} -void DynDNS::Stop(void) -{ - tcp->Stop(); - connectPhase = CP_IDLE; - RakNet::OP_DELETE(tcp, _FILE_AND_LINE_); - tcp=0; -} - - -// newIPAddress is optional - if left out, DynDNS will use whatever it receives -void DynDNS::UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword ) -{ - myIPStr[0]=0; - - if (tcp==0) - tcp = RakNet::OP_NEW(_FILE_AND_LINE_); - connectPhase = CP_IDLE; - host = dnsHost; - - if (tcp->Start(0, 1)==false) - { - SetCompleted(RC_TCP_FAILED_TO_START, "TCP failed to start"); - return; - } - - connectPhase = CP_CONNECTING_TO_CHECKIP; - tcp->Connect("checkip.dyndns.org", 80, false); - - // See https://www.dyndns.com/developers/specs/syntax.html - getString="GET /nic/update?hostname="; - getString+=dnsHost; - if (newIPAddress) - { - getString+="&myip="; - getString+=newIPAddress; - } - getString+="&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG HTTP/1.0\n"; - getString+="Host: members.dyndns.org\n"; - getString+="Authorization: Basic "; - char outputData[512]; - TCPInterface::Base64Encoding(usernameAndPassword, (int) strlen(usernameAndPassword), outputData); - getString+=outputData; - getString+="User-Agent: Jenkins Software LLC - PC - 1.0\n\n"; -} -void DynDNS::Update(void) -{ - if (connectPhase==CP_IDLE) - return; - - serverAddress=tcp->HasFailedConnectionAttempt(); - if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - SetCompleted(RC_TCP_DID_NOT_CONNECT, "Could not connect to DynDNS"); - return; - } - - serverAddress=tcp->HasCompletedConnectionAttempt(); - if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - if (connectPhase == CP_CONNECTING_TO_CHECKIP) - { - checkIpAddress=serverAddress; - connectPhase = CP_WAITING_FOR_CHECKIP_RESPONSE; - tcp->Send("GET\n\n", (unsigned int) strlen("GET\n\n"), serverAddress, false); // Needs 2 newlines! This is not documented and wasted a lot of my time - } - else - { - connectPhase = CP_WAITING_FOR_DYNDNS_RESPONSE; - tcp->Send(getString.C_String(), (unsigned int) getString.GetLength(), serverAddress, false); - } - phaseTimeout=RakNet::GetTime()+1000; - } - - if (connectPhase==CP_WAITING_FOR_CHECKIP_RESPONSE && RakNet::GetTime()>phaseTimeout) - { - connectPhase = CP_CONNECTING_TO_DYNDNS; - tcp->CloseConnection(checkIpAddress); - tcp->Connect("members.dyndns.org", 80, false); - } - else if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE && RakNet::GetTime()>phaseTimeout) - { - SetCompleted(RC_DYNDNS_TIMEOUT, "DynDNS did not respond"); - return; - } - - Packet *packet = tcp->Receive(); - if (packet) - { - if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE) - { - unsigned int i; - - char *result; - result=strstr((char*) packet->data, "Connection: close"); - if (result!=0) - { - result+=strlen("Connection: close"); - while (*result && (*result=='\r') || (*result=='\n') || (*result==' ') ) - result++; - for (i=0; i < 13; i++) - { - if (strncmp(resultTable[i].code, result, strlen(resultTable[i].code))==0) - { - if (resultTable[i].resultCode==RC_SUCCESS) - { - // Read my external IP into myIPStr - // Advance until we hit a number - while (*result && ((*result<'0') || (*result>'9')) ) - result++; - if (*result) - { - SystemAddress parser; - parser.FromString(result); - parser.ToString(false, myIPStr); - } - } - tcp->DeallocatePacket(packet); - SetCompleted(resultTable[i].resultCode, resultTable[i].description); - break; - } - } - if (i==13) - { - tcp->DeallocatePacket(packet); - SetCompleted(RC_UNKNOWN_RESULT, "DynDNS returned unknown result"); - } - } - else - { - tcp->DeallocatePacket(packet); - SetCompleted(RC_PARSING_FAILURE, "Parsing failure on returned string from DynDNS"); - } - - return; - } - else - { - /* - HTTP/1.1 200 OK - Content-Type: text/html - Server: DynDNS-CheckIP/1.0 - Connection: close - Cache-Control: no-cache - Pragma: no-cache - Content-Length: 105 - - Current IP CheckCurrent IP Address: 98.1 - 89.219.22 - - - Connection to host lost. - */ - - char *result; - result=strstr((char*) packet->data, "Current IP Address: "); - if (result!=0) - { - result+=strlen("Current IP Address: "); - SystemAddress myIp; - myIp.FromString(result); - myIp.ToString(false, myIPStr); - - // Resolve DNS we are setting. If equal to current then abort - const char *existingHost = ( char* ) SocketLayer::DomainNameToIP( host.C_String() ); - if (existingHost && strcmp(existingHost, myIPStr)==0) - { - // DynDNS considers setting the IP to what it is already set abuse - tcp->DeallocatePacket(packet); - SetCompleted(RC_DNS_ALREADY_SET, "No action needed"); - return; - } - } - - tcp->DeallocatePacket(packet); - tcp->CloseConnection(packet->systemAddress); - - connectPhase = CP_CONNECTING_TO_DYNDNS; - tcp->Connect("members.dyndns.org", 80, false); - } - } - - if (tcp->HasLostConnection()!=UNASSIGNED_SYSTEM_ADDRESS) - { - if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE) - { - SetCompleted(RC_CONNECTION_LOST_WITHOUT_RESPONSE, "Connection lost to DynDNS during GET operation"); - } - } -} - - -#endif // _RAKNET_SUPPORT_DynDNS +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "TCPInterface.h" +#include "SocketLayer.h" +#include "DynDNS.h" +#include "GetTime.h" + +using namespace RakNet; + +struct DynDnsResult +{ + const char *description; + const char *code; + DynDnsResultCode resultCode; +}; + +DynDnsResult resultTable[13] = +{ + // See http://www.dyndns.com/developers/specs/flow.pdf + {"DNS update success.\nPlease wait up to 60 seconds for the change to take effect.\n", "good", RC_SUCCESS}, // Even with success, it takes time for the cache to update! + {"No change", "nochg", RC_NO_CHANGE}, + {"Host has been blocked. You will need to contact DynDNS to reenable.", "abuse", RC_ABUSE}, + {"Useragent is blocked", "badagent", RC_BAD_AGENT}, + {"Username/password pair bad", "badauth", RC_BAD_AUTH}, + {"Bad system parameter", "badsys", RC_BAD_SYS}, + {"DNS inconsistency", "dnserr", RC_DNS_ERROR}, + {"Paid account feature", "!donator", RC_NOT_DONATOR}, + {"No such host in system", "nohost", RC_NO_HOST}, + {"Invalid hostname format", "notfqdn", RC_NOT_FQDN}, + {"Serious error", "numhost", RC_NUM_HOST}, + {"This host exists, but does not belong to you", "!yours", RC_NOT_YOURS}, + {"911", "911", RC_911}, +}; +DynDNS::DynDNS() +{ + connectPhase=CP_IDLE; + tcp=0; +} +DynDNS::~DynDNS() +{ + if (tcp) + RakNet::OP_DELETE(tcp, _FILE_AND_LINE_); +} +void DynDNS::Stop(void) +{ + tcp->Stop(); + connectPhase = CP_IDLE; + RakNet::OP_DELETE(tcp, _FILE_AND_LINE_); + tcp=0; +} + + +// newIPAddress is optional - if left out, DynDNS will use whatever it receives +void DynDNS::UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword ) +{ + myIPStr[0]=0; + + if (tcp==0) + tcp = RakNet::OP_NEW(_FILE_AND_LINE_); + connectPhase = CP_IDLE; + host = dnsHost; + + if (tcp->Start(0, 1)==false) + { + SetCompleted(RC_TCP_FAILED_TO_START, "TCP failed to start"); + return; + } + + connectPhase = CP_CONNECTING_TO_CHECKIP; + tcp->Connect("checkip.dyndns.org", 80, false); + + // See https://www.dyndns.com/developers/specs/syntax.html + getString="GET /nic/update?hostname="; + getString+=dnsHost; + if (newIPAddress) + { + getString+="&myip="; + getString+=newIPAddress; + } + getString+="&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG HTTP/1.0\n"; + getString+="Host: members.dyndns.org\n"; + getString+="Authorization: Basic "; + char outputData[512]; + TCPInterface::Base64Encoding(usernameAndPassword, (int) strlen(usernameAndPassword), outputData); + getString+=outputData; + getString+="User-Agent: Jenkins Software LLC - PC - 1.0\n\n"; +} +void DynDNS::Update(void) +{ + if (connectPhase==CP_IDLE) + return; + + serverAddress=tcp->HasFailedConnectionAttempt(); + if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + SetCompleted(RC_TCP_DID_NOT_CONNECT, "Could not connect to DynDNS"); + return; + } + + serverAddress=tcp->HasCompletedConnectionAttempt(); + if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + if (connectPhase == CP_CONNECTING_TO_CHECKIP) + { + checkIpAddress=serverAddress; + connectPhase = CP_WAITING_FOR_CHECKIP_RESPONSE; + tcp->Send("GET\n\n", (unsigned int) strlen("GET\n\n"), serverAddress, false); // Needs 2 newlines! This is not documented and wasted a lot of my time + } + else + { + connectPhase = CP_WAITING_FOR_DYNDNS_RESPONSE; + tcp->Send(getString.C_String(), (unsigned int) getString.GetLength(), serverAddress, false); + } + phaseTimeout=RakNet::GetTime()+1000; + } + + if (connectPhase==CP_WAITING_FOR_CHECKIP_RESPONSE && RakNet::GetTime()>phaseTimeout) + { + connectPhase = CP_CONNECTING_TO_DYNDNS; + tcp->CloseConnection(checkIpAddress); + tcp->Connect("members.dyndns.org", 80, false); + } + else if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE && RakNet::GetTime()>phaseTimeout) + { + SetCompleted(RC_DYNDNS_TIMEOUT, "DynDNS did not respond"); + return; + } + + Packet *packet = tcp->Receive(); + if (packet) + { + if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE) + { + unsigned int i; + + char *result; + result=strstr((char*) packet->data, "Connection: close"); + if (result!=0) + { + result+=strlen("Connection: close"); + while (*result && (*result=='\r') || (*result=='\n') || (*result==' ') ) + result++; + for (i=0; i < 13; i++) + { + if (strncmp(resultTable[i].code, result, strlen(resultTable[i].code))==0) + { + if (resultTable[i].resultCode==RC_SUCCESS) + { + // Read my external IP into myIPStr + // Advance until we hit a number + while (*result && ((*result<'0') || (*result>'9')) ) + result++; + if (*result) + { + SystemAddress parser; + parser.FromString(result); + parser.ToString(false, myIPStr); + } + } + tcp->DeallocatePacket(packet); + SetCompleted(resultTable[i].resultCode, resultTable[i].description); + break; + } + } + if (i==13) + { + tcp->DeallocatePacket(packet); + SetCompleted(RC_UNKNOWN_RESULT, "DynDNS returned unknown result"); + } + } + else + { + tcp->DeallocatePacket(packet); + SetCompleted(RC_PARSING_FAILURE, "Parsing failure on returned string from DynDNS"); + } + + return; + } + else + { + /* + HTTP/1.1 200 OK + Content-Type: text/html + Server: DynDNS-CheckIP/1.0 + Connection: close + Cache-Control: no-cache + Pragma: no-cache + Content-Length: 105 + + Current IP CheckCurrent IP Address: 98.1 + 89.219.22 + + + Connection to host lost. + */ + + char *result; + result=strstr((char*) packet->data, "Current IP Address: "); + if (result!=0) + { + result+=strlen("Current IP Address: "); + SystemAddress myIp; + myIp.FromString(result); + myIp.ToString(false, myIPStr); + + // Resolve DNS we are setting. If equal to current then abort + const char *existingHost = ( char* ) SocketLayer::DomainNameToIP( host.C_String() ); + if (existingHost && strcmp(existingHost, myIPStr)==0) + { + // DynDNS considers setting the IP to what it is already set abuse + tcp->DeallocatePacket(packet); + SetCompleted(RC_DNS_ALREADY_SET, "No action needed"); + return; + } + } + + tcp->DeallocatePacket(packet); + tcp->CloseConnection(packet->systemAddress); + + connectPhase = CP_CONNECTING_TO_DYNDNS; + tcp->Connect("members.dyndns.org", 80, false); + } + } + + if (tcp->HasLostConnection()!=UNASSIGNED_SYSTEM_ADDRESS) + { + if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE) + { + SetCompleted(RC_CONNECTION_LOST_WITHOUT_RESPONSE, "Connection lost to DynDNS during GET operation"); + } + } +} + + +#endif // _RAKNET_SUPPORT_DynDNS diff --git a/project/lib_projects/raknet/jni/RaknetSources/DynDNS.h b/project/lib_projects/raknet/jni/RaknetSources/DynDNS.h index ed30bdd..e163377 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/DynDNS.h +++ b/project/lib_projects/raknet/jni/RaknetSources/DynDNS.h @@ -1,100 +1,100 @@ -/// \file DynDNS.h -/// \brief Helper to class to update DynDNS -/// This can be used to determine what permissions are should be allowed to the other system -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1 - -class TCPInterface; - -#ifndef __DYN_DNS_H -#define __DYN_DNS_H - -namespace RakNet -{ - -enum DynDnsResultCode -{ - // ----- Success ----- - RC_SUCCESS, - RC_DNS_ALREADY_SET, // RakNet detects no action is needed - - // ----- Ignorable failure (treat same as success) ----- - RC_NO_CHANGE, // DynDNS detects no action is needed (treated as abuse though) - - // ----- User error ----- - RC_NOT_DONATOR, // You have to pay to do this - RC_NO_HOST, // This host does not exist at all - RC_BAD_AUTH, // You set the wrong password - RC_NOT_YOURS, // This is not your host - - // ----- Permanent failure ----- - RC_ABUSE, // Your host has been blocked, too many failures disable your account - RC_TCP_FAILED_TO_START, // TCP port already in use - RC_TCP_DID_NOT_CONNECT, // DynDNS down? - RC_UNKNOWN_RESULT, // DynDNS returned a result code that was not documented as of 12/4/2010 on http://www.dyndns.com/developers/specs/flow.pdf - RC_PARSING_FAILURE, // Can't read the result returned, format change? - RC_CONNECTION_LOST_WITHOUT_RESPONSE, // Lost the connection to DynDNS while communicating - RC_BAD_AGENT, // ??? - RC_BAD_SYS, // ??? - RC_DNS_ERROR, // ??? - RC_NOT_FQDN, // ??? - RC_NUM_HOST, // ??? - RC_911, // ??? - RC_DYNDNS_TIMEOUT, // DynDNS did not respond -}; - -// Can only process one at a time with the current implementation -class RAK_DLL_EXPORT DynDNS -{ -public: - DynDNS(); - ~DynDNS(); - - // Pass 0 for newIPAddress to autodetect whatever you are uploading from - // usernameAndPassword should be in the format username:password - void UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword ); - void Update(void); - - // Output - bool IsRunning(void) const {return connectPhase!=CP_IDLE;} - bool IsCompleted(void) const {return connectPhase==CP_IDLE;} - RakNet::DynDnsResultCode GetCompletedResultCode(void) {return result;} - const char *GetCompletedDescription(void) const {return resultDescription;} - bool WasResultSuccessful(void) const {return result==RC_SUCCESS || result==RC_DNS_ALREADY_SET || result==RC_NO_CHANGE;} - char *GetMyPublicIP(void) const {return (char*) myIPStr;} // We get our public IP as part of the process. This is valid once completed - -protected: - void Stop(void); - void SetCompleted(RakNet::DynDnsResultCode _result, const char *_resultDescription) {Stop(); result=_result; resultDescription=_resultDescription;} - - enum ConnectPhase - { - CP_CONNECTING_TO_CHECKIP, - CP_WAITING_FOR_CHECKIP_RESPONSE, - CP_CONNECTING_TO_DYNDNS, - CP_WAITING_FOR_DYNDNS_RESPONSE, - CP_IDLE, - }; - - TCPInterface *tcp; - RakNet::RakString getString; - SystemAddress serverAddress; - ConnectPhase connectPhase; - RakNet::RakString host; - RakNet::Time phaseTimeout; - SystemAddress checkIpAddress; - const char *resultDescription; - RakNet::DynDnsResultCode result; - char myIPStr[32]; -}; - -} // namespace RakNet - -#endif // __DYN_DNS_H - -#endif // _RAKNET_SUPPORT_DynDNS +/// \file DynDNS.h +/// \brief Helper to class to update DynDNS +/// This can be used to determine what permissions are should be allowed to the other system +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1 + +class TCPInterface; + +#ifndef __DYN_DNS_H +#define __DYN_DNS_H + +namespace RakNet +{ + +enum DynDnsResultCode +{ + // ----- Success ----- + RC_SUCCESS, + RC_DNS_ALREADY_SET, // RakNet detects no action is needed + + // ----- Ignorable failure (treat same as success) ----- + RC_NO_CHANGE, // DynDNS detects no action is needed (treated as abuse though) + + // ----- User error ----- + RC_NOT_DONATOR, // You have to pay to do this + RC_NO_HOST, // This host does not exist at all + RC_BAD_AUTH, // You set the wrong password + RC_NOT_YOURS, // This is not your host + + // ----- Permanent failure ----- + RC_ABUSE, // Your host has been blocked, too many failures disable your account + RC_TCP_FAILED_TO_START, // TCP port already in use + RC_TCP_DID_NOT_CONNECT, // DynDNS down? + RC_UNKNOWN_RESULT, // DynDNS returned a result code that was not documented as of 12/4/2010 on http://www.dyndns.com/developers/specs/flow.pdf + RC_PARSING_FAILURE, // Can't read the result returned, format change? + RC_CONNECTION_LOST_WITHOUT_RESPONSE, // Lost the connection to DynDNS while communicating + RC_BAD_AGENT, // ??? + RC_BAD_SYS, // ??? + RC_DNS_ERROR, // ??? + RC_NOT_FQDN, // ??? + RC_NUM_HOST, // ??? + RC_911, // ??? + RC_DYNDNS_TIMEOUT, // DynDNS did not respond +}; + +// Can only process one at a time with the current implementation +class RAK_DLL_EXPORT DynDNS +{ +public: + DynDNS(); + ~DynDNS(); + + // Pass 0 for newIPAddress to autodetect whatever you are uploading from + // usernameAndPassword should be in the format username:password + void UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword ); + void Update(void); + + // Output + bool IsRunning(void) const {return connectPhase!=CP_IDLE;} + bool IsCompleted(void) const {return connectPhase==CP_IDLE;} + RakNet::DynDnsResultCode GetCompletedResultCode(void) {return result;} + const char *GetCompletedDescription(void) const {return resultDescription;} + bool WasResultSuccessful(void) const {return result==RC_SUCCESS || result==RC_DNS_ALREADY_SET || result==RC_NO_CHANGE;} + char *GetMyPublicIP(void) const {return (char*) myIPStr;} // We get our public IP as part of the process. This is valid once completed + +protected: + void Stop(void); + void SetCompleted(RakNet::DynDnsResultCode _result, const char *_resultDescription) {Stop(); result=_result; resultDescription=_resultDescription;} + + enum ConnectPhase + { + CP_CONNECTING_TO_CHECKIP, + CP_WAITING_FOR_CHECKIP_RESPONSE, + CP_CONNECTING_TO_DYNDNS, + CP_WAITING_FOR_DYNDNS_RESPONSE, + CP_IDLE, + }; + + TCPInterface *tcp; + RakNet::RakString getString; + SystemAddress serverAddress; + ConnectPhase connectPhase; + RakNet::RakString host; + RakNet::Time phaseTimeout; + SystemAddress checkIpAddress; + const char *resultDescription; + RakNet::DynDnsResultCode result; + char myIPStr[32]; +}; + +} // namespace RakNet + +#endif // __DYN_DNS_H + +#endif // _RAKNET_SUPPORT_DynDNS diff --git a/project/lib_projects/raknet/jni/RaknetSources/EmailSender.cpp b/project/lib_projects/raknet/jni/RaknetSources/EmailSender.cpp index 4b38481..2a0e4a4 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/EmailSender.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/EmailSender.cpp @@ -1,362 +1,362 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1 - -// Useful sites -// http://www.faqs.org\rfcs\rfc2821.html -// http://www2.rad.com\networks/1995/mime/examples.htm - -#include "EmailSender.h" -#include "TCPInterface.h" -#include "GetTime.h" -#include "Rand.h" -#include "FileList.h" -#include "BitStream.h" -#include - - - - - -#include "RakSleep.h" - -using namespace RakNet; - - -STATIC_FACTORY_DEFINITIONS(EmailSender,EmailSender); - -const char *EmailSender::Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password) -{ - RakNet::Packet *packet; - char query[1024]; - TCPInterface tcpInterface; - SystemAddress emailServer; - if (tcpInterface.Start(0, 0)==false) - return "Unknown error starting TCP"; - emailServer=tcpInterface.Connect(hostAddress, hostPort,true); - if (emailServer==UNASSIGNED_SYSTEM_ADDRESS) - return "Failed to connect to host"; -#if OPEN_SSL_CLIENT_SUPPORT==1 - tcpInterface.StartSSLClient(emailServer); -#endif - RakNet::TimeMS timeoutTime = RakNet::GetTimeMS()+3000; - packet=0; - while (RakNet::GetTimeMS() < timeoutTime) - { - packet = tcpInterface.Receive(); - if (packet) - { - if (doPrintf) - RAKNET_DEBUG_PRINTF("%s", packet->data); - break; - } - RakSleep(250); - } - - if (packet==0) - return "Timeout while waiting for initial data from server."; - - tcpInterface.Send("EHLO\r\n", 6, emailServer,false); - const char *response; - bool authenticate=false; -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - while (1) - { - response=GetResponse(&tcpInterface, emailServer, doPrintf); - - if (response!=0 && strcmp(response, "AUTHENTICATE")==0) - { - authenticate=true; - break; - } - - // Something other than continue? - if (response!=0 && strcmp(response, "CONTINUE")!=0) - return response; - - // Success? - if (response==0) - break; - } - - if (authenticate) - { - sprintf(query, "EHLO %s\r\n", sender); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - if (password==0) - return "Password needed"; - char *outputData = RakNet::OP_NEW_ARRAY((const int) (strlen(sender)+strlen(password)+2)*3, _FILE_AND_LINE_ ); - RakNet::BitStream bs; - char zero=0; - bs.Write(&zero,1); - bs.Write(sender,(const unsigned int)strlen(sender)); - //bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net")); - bs.Write(&zero,1); - bs.Write(password,(const unsigned int)strlen(password)); - bs.Write(&zero,1); - //bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password")); - TCPInterface::Base64Encoding((const char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData); - sprintf(query, "AUTH PLAIN %s", outputData); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - } - - - if (sender) - sprintf(query, "MAIL From: <%s>\r\n", sender); - else - sprintf(query, "MAIL From: <>\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - - if (recipient) - sprintf(query, "RCPT TO: <%s>\r\n", recipient); - else - sprintf(query, "RCPT TO: <>\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - - tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer,false); - - // Wait for 354... - - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - - if (subject) - { - sprintf(query, "Subject: %s\r\n", subject); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - if (senderName) - { - sprintf(query, "From: %s\r\n", senderName); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - if (recipientName) - { - sprintf(query, "To: %s\r\n", recipientName); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - - const int boundarySize=60; - char boundary[boundarySize+1]; - int i,j; - if (attachedFiles && attachedFiles->fileList.Size()) - { - rakNetRandom.SeedMT((unsigned int) RakNet::GetTimeMS()); - // Random multipart message boundary - for (i=0; i < boundarySize; i++) - boundary[i]=TCPInterface::Base64Map()[rakNetRandom.RandomMT()%64]; - boundary[boundarySize]=0; - } - - sprintf(query, "MIME-version: 1.0\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - if (attachedFiles && attachedFiles->fileList.Size()) - { - sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - - sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - // Write the body of the email, doing some lame shitty shit where I have to make periods at the start of a newline have a second period. - char *newBody; - int bodyLength; - bodyLength=(int)strlen(body); - newBody = (char*) rakMalloc_Ex( bodyLength*3, _FILE_AND_LINE_ ); - if (bodyLength>0) - newBody[0]=body[0]; - for (i=1, j=1; i < bodyLength; i++) - { - // Transform \n . \r \n into \n . . \r \n - if (i < bodyLength-2 && - body[i-1]=='\n' && - body[i+0]=='.' && - body[i+1]=='\r' && - body[i+2]=='\n') - { - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='\r'; - newBody[j++]='\n'; - i+=2; - } - // Transform \n . . \r \n into \n . . . \r \n - // Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed - else if (i <= bodyLength-3 && - body[i-1]=='\n' && - body[i+0]=='.' && - body[i+1]=='.' && - body[i+2]=='\r' && - body[i+3]=='\n') - { - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='\r'; - newBody[j++]='\n'; - i+=3; - } - // Transform \n . \n into \n . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does) - else if (i < bodyLength-1 && - body[i-1]=='\n' && - body[i+0]=='.' && - body[i+1]=='\n') - { - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='\r'; - newBody[j++]='\n'; - i+=1; - } - // Transform \n . . \n into \n . . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does) - // In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed - else if (i <= bodyLength-2 && - body[i-1]=='\n' && - body[i+0]=='.' && - body[i+1]=='.' && - body[i+2]=='\n') - { - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='\r'; - newBody[j++]='\n'; - i+=2; - } - else - newBody[j++]=body[i]; - } - - newBody[j++]='\r'; - newBody[j++]='\n'; - tcpInterface.Send(newBody, j, emailServer,false); - - rakFree_Ex(newBody, _FILE_AND_LINE_ ); - int outputOffset; - - // What a pain in the rear. I have to map the binary to printable characters using 6 bits per character. - if (attachedFiles && attachedFiles->fileList.Size()) - { - for (i=0; i < (int) attachedFiles->fileList.Size(); i++) - { - // Write boundary - sprintf(query, "\r\n--%s\r\n", boundary); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - sprintf(query, "Content-Type: APPLICATION/Octet-Stream; SizeOnDisk=%i; name=\"%s\"\r\nContent-Transfer-Encoding: BASE64\r\nContent-Description: %s\r\n\r\n", attachedFiles->fileList[i].dataLengthBytes, attachedFiles->fileList[i].filename.C_String(), attachedFiles->fileList[i].filename.C_String()); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - newBody = (char*) rakMalloc_Ex( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2, _FILE_AND_LINE_ ); - - outputOffset=TCPInterface::Base64Encoding(attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody); - - // Send the base64 mapped file. - tcpInterface.Send(newBody, outputOffset, emailServer,false); - rakFree_Ex(newBody, _FILE_AND_LINE_ ); - - } - - // Write last boundary - sprintf(query, "\r\n--%s--\r\n", boundary); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - - - sprintf(query, "\r\n.\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - - tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer,false); - - RakSleep(30); - if (doPrintf) - { - packet = tcpInterface.Receive(); - while (packet) - { - RAKNET_DEBUG_PRINTF("%s", packet->data); - packet = tcpInterface.Receive(); - } - } - tcpInterface.Stop(); - return 0; // Success -} - -const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf) -{ - RakNet::Packet *packet; - RakNet::TimeMS timeout; - timeout=RakNet::GetTimeMS()+5000; -#ifdef _MSC_VER - #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - if (tcpInterface->HasLostConnection()==emailServer) - return "Connection to server lost."; - packet = tcpInterface->Receive(); - if (packet) - { - if (doPrintf) - { - RAKNET_DEBUG_PRINTF("%s", packet->data); - } -#if OPEN_SSL_CLIENT_SUPPORT==1 - if (strstr((const char*)packet->data, "220")) - { - tcpInterface->StartSSLClient(packet->systemAddress); - return "AUTHENTICATE"; // OK - } -// if (strstr((const char*)packet->data, "250-AUTH LOGIN PLAIN")) -// { -// tcpInterface->StartSSLClient(packet->systemAddress); -// return "AUTHENTICATE"; // OK -// } -#endif - if (strstr((const char*)packet->data, "235")) - return 0; // Authentication accepted - if (strstr((const char*)packet->data, "354")) - return 0; // Go ahead -#if OPEN_SSL_CLIENT_SUPPORT==1 - if (strstr((const char*)packet->data, "250-STARTTLS")) - { - tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress, false); - return "CONTINUE"; - } -#endif - if (strstr((const char*)packet->data, "250")) - return 0; // OK - if (strstr((const char*)packet->data, "550")) - return "Failed on error code 550"; - if (strstr((const char*)packet->data, "553")) - return "Failed on error code 553"; - } - if (RakNet::GetTimeMS() > timeout) - return "Timed out"; - RakSleep(100); - } -} - - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1 + +// Useful sites +// http://www.faqs.org\rfcs\rfc2821.html +// http://www2.rad.com\networks/1995/mime/examples.htm + +#include "EmailSender.h" +#include "TCPInterface.h" +#include "GetTime.h" +#include "Rand.h" +#include "FileList.h" +#include "BitStream.h" +#include + + + + + +#include "RakSleep.h" + +using namespace RakNet; + + +STATIC_FACTORY_DEFINITIONS(EmailSender,EmailSender); + +const char *EmailSender::Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password) +{ + RakNet::Packet *packet; + char query[1024]; + TCPInterface tcpInterface; + SystemAddress emailServer; + if (tcpInterface.Start(0, 0)==false) + return "Unknown error starting TCP"; + emailServer=tcpInterface.Connect(hostAddress, hostPort,true); + if (emailServer==UNASSIGNED_SYSTEM_ADDRESS) + return "Failed to connect to host"; +#if OPEN_SSL_CLIENT_SUPPORT==1 + tcpInterface.StartSSLClient(emailServer); +#endif + RakNet::TimeMS timeoutTime = RakNet::GetTimeMS()+3000; + packet=0; + while (RakNet::GetTimeMS() < timeoutTime) + { + packet = tcpInterface.Receive(); + if (packet) + { + if (doPrintf) + RAKNET_DEBUG_PRINTF("%s", packet->data); + break; + } + RakSleep(250); + } + + if (packet==0) + return "Timeout while waiting for initial data from server."; + + tcpInterface.Send("EHLO\r\n", 6, emailServer,false); + const char *response; + bool authenticate=false; +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + while (1) + { + response=GetResponse(&tcpInterface, emailServer, doPrintf); + + if (response!=0 && strcmp(response, "AUTHENTICATE")==0) + { + authenticate=true; + break; + } + + // Something other than continue? + if (response!=0 && strcmp(response, "CONTINUE")!=0) + return response; + + // Success? + if (response==0) + break; + } + + if (authenticate) + { + sprintf(query, "EHLO %s\r\n", sender); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + if (password==0) + return "Password needed"; + char *outputData = RakNet::OP_NEW_ARRAY((const int) (strlen(sender)+strlen(password)+2)*3, _FILE_AND_LINE_ ); + RakNet::BitStream bs; + char zero=0; + bs.Write(&zero,1); + bs.Write(sender,(const unsigned int)strlen(sender)); + //bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net")); + bs.Write(&zero,1); + bs.Write(password,(const unsigned int)strlen(password)); + bs.Write(&zero,1); + //bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password")); + TCPInterface::Base64Encoding((const char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData); + sprintf(query, "AUTH PLAIN %s", outputData); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + } + + + if (sender) + sprintf(query, "MAIL From: <%s>\r\n", sender); + else + sprintf(query, "MAIL From: <>\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + + if (recipient) + sprintf(query, "RCPT TO: <%s>\r\n", recipient); + else + sprintf(query, "RCPT TO: <>\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + + tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer,false); + + // Wait for 354... + + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + + if (subject) + { + sprintf(query, "Subject: %s\r\n", subject); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + if (senderName) + { + sprintf(query, "From: %s\r\n", senderName); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + if (recipientName) + { + sprintf(query, "To: %s\r\n", recipientName); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + + const int boundarySize=60; + char boundary[boundarySize+1]; + int i,j; + if (attachedFiles && attachedFiles->fileList.Size()) + { + rakNetRandom.SeedMT((unsigned int) RakNet::GetTimeMS()); + // Random multipart message boundary + for (i=0; i < boundarySize; i++) + boundary[i]=TCPInterface::Base64Map()[rakNetRandom.RandomMT()%64]; + boundary[boundarySize]=0; + } + + sprintf(query, "MIME-version: 1.0\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + if (attachedFiles && attachedFiles->fileList.Size()) + { + sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + + sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + // Write the body of the email, doing some lame shitty shit where I have to make periods at the start of a newline have a second period. + char *newBody; + int bodyLength; + bodyLength=(int)strlen(body); + newBody = (char*) rakMalloc_Ex( bodyLength*3, _FILE_AND_LINE_ ); + if (bodyLength>0) + newBody[0]=body[0]; + for (i=1, j=1; i < bodyLength; i++) + { + // Transform \n . \r \n into \n . . \r \n + if (i < bodyLength-2 && + body[i-1]=='\n' && + body[i+0]=='.' && + body[i+1]=='\r' && + body[i+2]=='\n') + { + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='\r'; + newBody[j++]='\n'; + i+=2; + } + // Transform \n . . \r \n into \n . . . \r \n + // Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed + else if (i <= bodyLength-3 && + body[i-1]=='\n' && + body[i+0]=='.' && + body[i+1]=='.' && + body[i+2]=='\r' && + body[i+3]=='\n') + { + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='\r'; + newBody[j++]='\n'; + i+=3; + } + // Transform \n . \n into \n . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does) + else if (i < bodyLength-1 && + body[i-1]=='\n' && + body[i+0]=='.' && + body[i+1]=='\n') + { + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='\r'; + newBody[j++]='\n'; + i+=1; + } + // Transform \n . . \n into \n . . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does) + // In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed + else if (i <= bodyLength-2 && + body[i-1]=='\n' && + body[i+0]=='.' && + body[i+1]=='.' && + body[i+2]=='\n') + { + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='\r'; + newBody[j++]='\n'; + i+=2; + } + else + newBody[j++]=body[i]; + } + + newBody[j++]='\r'; + newBody[j++]='\n'; + tcpInterface.Send(newBody, j, emailServer,false); + + rakFree_Ex(newBody, _FILE_AND_LINE_ ); + int outputOffset; + + // What a pain in the rear. I have to map the binary to printable characters using 6 bits per character. + if (attachedFiles && attachedFiles->fileList.Size()) + { + for (i=0; i < (int) attachedFiles->fileList.Size(); i++) + { + // Write boundary + sprintf(query, "\r\n--%s\r\n", boundary); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + sprintf(query, "Content-Type: APPLICATION/Octet-Stream; SizeOnDisk=%i; name=\"%s\"\r\nContent-Transfer-Encoding: BASE64\r\nContent-Description: %s\r\n\r\n", attachedFiles->fileList[i].dataLengthBytes, attachedFiles->fileList[i].filename.C_String(), attachedFiles->fileList[i].filename.C_String()); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + newBody = (char*) rakMalloc_Ex( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2, _FILE_AND_LINE_ ); + + outputOffset=TCPInterface::Base64Encoding(attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody); + + // Send the base64 mapped file. + tcpInterface.Send(newBody, outputOffset, emailServer,false); + rakFree_Ex(newBody, _FILE_AND_LINE_ ); + + } + + // Write last boundary + sprintf(query, "\r\n--%s--\r\n", boundary); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + + + sprintf(query, "\r\n.\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + + tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer,false); + + RakSleep(30); + if (doPrintf) + { + packet = tcpInterface.Receive(); + while (packet) + { + RAKNET_DEBUG_PRINTF("%s", packet->data); + packet = tcpInterface.Receive(); + } + } + tcpInterface.Stop(); + return 0; // Success +} + +const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf) +{ + RakNet::Packet *packet; + RakNet::TimeMS timeout; + timeout=RakNet::GetTimeMS()+5000; +#ifdef _MSC_VER + #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + if (tcpInterface->HasLostConnection()==emailServer) + return "Connection to server lost."; + packet = tcpInterface->Receive(); + if (packet) + { + if (doPrintf) + { + RAKNET_DEBUG_PRINTF("%s", packet->data); + } +#if OPEN_SSL_CLIENT_SUPPORT==1 + if (strstr((const char*)packet->data, "220")) + { + tcpInterface->StartSSLClient(packet->systemAddress); + return "AUTHENTICATE"; // OK + } +// if (strstr((const char*)packet->data, "250-AUTH LOGIN PLAIN")) +// { +// tcpInterface->StartSSLClient(packet->systemAddress); +// return "AUTHENTICATE"; // OK +// } +#endif + if (strstr((const char*)packet->data, "235")) + return 0; // Authentication accepted + if (strstr((const char*)packet->data, "354")) + return 0; // Go ahead +#if OPEN_SSL_CLIENT_SUPPORT==1 + if (strstr((const char*)packet->data, "250-STARTTLS")) + { + tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress, false); + return "CONTINUE"; + } +#endif + if (strstr((const char*)packet->data, "250")) + return 0; // OK + if (strstr((const char*)packet->data, "550")) + return "Failed on error code 550"; + if (strstr((const char*)packet->data, "553")) + return "Failed on error code 553"; + } + if (RakNet::GetTimeMS() > timeout) + return "Timed out"; + RakSleep(100); + } +} + + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/EmailSender.h b/project/lib_projects/raknet/jni/RaknetSources/EmailSender.h index b2a75b4..04c0880 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/EmailSender.h +++ b/project/lib_projects/raknet/jni/RaknetSources/EmailSender.h @@ -1,58 +1,58 @@ -/// \file EmailSender.h -/// \brief Rudimentary class to send email from code. Don't expect anything fancy. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __EMAIL_SENDER_H -#define __EMAIL_SENDER_H - -#include "RakNetTypes.h" -#include "RakMemoryOverride.h" -#include "Export.h" -#include "Rand.h" -#include "TCPInterface.h" - -namespace RakNet -{ -/// Forward declarations -class FileList; -class TCPInterface; - -/// \brief Rudimentary class to send email from code. -class RAK_DLL_EXPORT EmailSender -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(EmailSender) - - /// \brief Sends an email. - /// \param[in] hostAddress The address of the email server. - /// \param[in] hostPort The port of the email server (usually 25) - /// \param[in] sender The email address you are sending from. - /// \param[in] recipient The email address you are sending to. - /// \param[in] senderName The email address you claim to be sending from - /// \param[in] recipientName The email address you claim to be sending to - /// \param[in] subject Email subject - /// \param[in] body Email body - /// \param[in] attachedFiles List of files to attach to the email. (Can be 0 to send none). - /// \param[in] doPrintf true to output SMTP info to console(for debugging?) - /// \param[in] password Used if the server uses AUTHENTICATE PLAIN over TLS (such as gmail) - /// \return 0 on success, otherwise a string indicating the error message - const char *Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password); - -protected: - const char *GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf); - RakNetRandom rakNetRandom; -}; - -} // namespace RakNet - -#endif - - -#endif // _RAKNET_SUPPORT_* +/// \file EmailSender.h +/// \brief Rudimentary class to send email from code. Don't expect anything fancy. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __EMAIL_SENDER_H +#define __EMAIL_SENDER_H + +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" +#include "Export.h" +#include "Rand.h" +#include "TCPInterface.h" + +namespace RakNet +{ +/// Forward declarations +class FileList; +class TCPInterface; + +/// \brief Rudimentary class to send email from code. +class RAK_DLL_EXPORT EmailSender +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(EmailSender) + + /// \brief Sends an email. + /// \param[in] hostAddress The address of the email server. + /// \param[in] hostPort The port of the email server (usually 25) + /// \param[in] sender The email address you are sending from. + /// \param[in] recipient The email address you are sending to. + /// \param[in] senderName The email address you claim to be sending from + /// \param[in] recipientName The email address you claim to be sending to + /// \param[in] subject Email subject + /// \param[in] body Email body + /// \param[in] attachedFiles List of files to attach to the email. (Can be 0 to send none). + /// \param[in] doPrintf true to output SMTP info to console(for debugging?) + /// \param[in] password Used if the server uses AUTHENTICATE PLAIN over TLS (such as gmail) + /// \return 0 on success, otherwise a string indicating the error message + const char *Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password); + +protected: + const char *GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf); + RakNetRandom rakNetRandom; +}; + +} // namespace RakNet + +#endif + + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/EncodeClassName.cpp b/project/lib_projects/raknet/jni/RaknetSources/EncodeClassName.cpp index d3f5a12..8b13789 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/EncodeClassName.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/EncodeClassName.cpp @@ -1 +1 @@ - + diff --git a/project/lib_projects/raknet/jni/RaknetSources/EpochTimeToString.cpp b/project/lib_projects/raknet/jni/RaknetSources/EpochTimeToString.cpp index b3410ca..c0174aa 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/EpochTimeToString.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/EpochTimeToString.cpp @@ -1,35 +1,35 @@ -#include "FormatString.h" -#include "EpochTimeToString.h" -#include -#include -#include -// localtime -#include -#include "LinuxStrings.h" - -char * EpochTimeToString(long long time) -{ - static int textIndex=0; - static char text[4][64]; - - if (++textIndex==4) - textIndex=0; - - struct tm * timeinfo; - time_t t = time; - timeinfo = localtime ( &t ); - strftime (text[textIndex],64,"%c.",timeinfo); - - /* - time_t - // Copied from the docs - struct tm *newtime; - newtime = _localtime64(& time); - asctime_s( text[textIndex], sizeof(text[textIndex]), newtime ); - - while (text[textIndex][0] && (text[textIndex][strlen(text[textIndex])-1]=='\n' || text[textIndex][strlen(text[textIndex])-1]=='\r')) - text[textIndex][strlen(text[textIndex])-1]=0; - */ - - return text[textIndex]; -} +#include "FormatString.h" +#include "EpochTimeToString.h" +#include +#include +#include +// localtime +#include +#include "LinuxStrings.h" + +char * EpochTimeToString(long long time) +{ + static int textIndex=0; + static char text[4][64]; + + if (++textIndex==4) + textIndex=0; + + struct tm * timeinfo; + time_t t = time; + timeinfo = localtime ( &t ); + strftime (text[textIndex],64,"%c.",timeinfo); + + /* + time_t + // Copied from the docs + struct tm *newtime; + newtime = _localtime64(& time); + asctime_s( text[textIndex], sizeof(text[textIndex]), newtime ); + + while (text[textIndex][0] && (text[textIndex][strlen(text[textIndex])-1]=='\n' || text[textIndex][strlen(text[textIndex])-1]=='\r')) + text[textIndex][strlen(text[textIndex])-1]=0; + */ + + return text[textIndex]; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/EpochTimeToString.h b/project/lib_projects/raknet/jni/RaknetSources/EpochTimeToString.h index 8644dc4..b6b299a 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/EpochTimeToString.h +++ b/project/lib_projects/raknet/jni/RaknetSources/EpochTimeToString.h @@ -1,15 +1,15 @@ -/// \file EpochTimeToString.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __EPOCH_TIME_TO_STRING_H -#define __EPOCH_TIME_TO_STRING_H - -#include "Export.h" - -RAK_DLL_EXPORT char * EpochTimeToString(long long time); - -#endif - +/// \file EpochTimeToString.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __EPOCH_TIME_TO_STRING_H +#define __EPOCH_TIME_TO_STRING_H + +#include "Export.h" + +RAK_DLL_EXPORT char * EpochTimeToString(long long time); + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/Export.h b/project/lib_projects/raknet/jni/RaknetSources/Export.h index 5c90a19..50ca3eb 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Export.h +++ b/project/lib_projects/raknet/jni/RaknetSources/Export.h @@ -1,13 +1,13 @@ -#include "RakNetDefines.h" - -#if defined(_WIN32) && !(defined(__GNUC__) || defined(__GCCXML__)) && !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) -#define RAK_DLL_EXPORT __declspec(dllexport) -#else -#define RAK_DLL_EXPORT -#endif - -#define STATIC_FACTORY_DECLARATIONS(x) static x* GetInstance(void); \ -static void DestroyInstance( x *i); - -#define STATIC_FACTORY_DEFINITIONS(x,y) x* x::GetInstance(void) {return RakNet::OP_NEW( _FILE_AND_LINE_ );} \ -void x::DestroyInstance( x *i) {RakNet::OP_DELETE(( y* ) i, _FILE_AND_LINE_);} +#include "RakNetDefines.h" + +#if defined(_WIN32) && !(defined(__GNUC__) || defined(__GCCXML__)) && !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) +#define RAK_DLL_EXPORT __declspec(dllexport) +#else +#define RAK_DLL_EXPORT +#endif + +#define STATIC_FACTORY_DECLARATIONS(x) static x* GetInstance(void); \ +static void DestroyInstance( x *i); + +#define STATIC_FACTORY_DEFINITIONS(x,y) x* x::GetInstance(void) {return RakNet::OP_NEW( _FILE_AND_LINE_ );} \ +void x::DestroyInstance( x *i) {RakNet::OP_DELETE(( y* ) i, _FILE_AND_LINE_);} diff --git a/project/lib_projects/raknet/jni/RaknetSources/FileList.cpp b/project/lib_projects/raknet/jni/RaknetSources/FileList.cpp index 0825ca0..e89973f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FileList.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/FileList.cpp @@ -1,794 +1,794 @@ -#include "FileList.h" - -#if _RAKNET_SUPPORT_FileOperations==1 - -#include // RAKNET_DEBUG_PRINTF -#include "RakAssert.h" -#if defined(ANDROID) -// not needed and unavailable on arm64 -#elif defined(_WIN32) || defined(__CYGWIN__) -#include - - -// #elif !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) && !defined ( __PPC__ ) && !defined ( __FreeBSD__ ) && !defined ( __S3E__ ) -// #include -#endif - - -#ifdef _WIN32 -// For mkdir -#include - - -#else -#include -#endif - -//#include "SHA1.h" -#include "DS_Queue.h" -#include "StringCompressor.h" -#include "BitStream.h" -#include "FileOperations.h" -#include "SuperFastHash.h" -#include "RakAssert.h" -#include "LinuxStrings.h" - -#define MAX_FILENAME_LENGTH 512 -static const unsigned HASH_LENGTH=4; - -using namespace RakNet; - -// alloca - -#if defined(_WIN32) -#include - - -#else -#if !defined ( __FreeBSD__ ) -#include -#endif -#include -#include -#include -#include "_FindFirst.h" -#include //defines intptr_t -#endif - -#include "RakAlloca.h" - -//int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data ) -//{ -// return strcmp(key, data.filename); -//} - - -STATIC_FACTORY_DEFINITIONS(FileListProgress,FileListProgress) -STATIC_FACTORY_DEFINITIONS(FLP_Printf,FLP_Printf) -STATIC_FACTORY_DEFINITIONS(FileList,FileList) - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// First callback called when FileList::AddFilesFromDirectory() starts -void FLP_Printf::OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) { - (void) fileList; - RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dir);} - -/// Called for each directory, when that directory begins processing -void FLP_Printf::OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) { - (void) fileList; - RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", dir, directoriesRemaining);} -void FLP_Printf::OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID ) -{ - (void) setID; - - char str[32]; - systemAddress.ToString(true, (char*) str); - RAKNET_DEBUG_PRINTF("File pushes complete to %s\n", str); -} -void FLP_Printf::OnSendAborted( SystemAddress systemAddress ) -{ - char str[32]; - systemAddress.ToString(true, (char*) str); - RAKNET_DEBUG_PRINTF("Send aborted to %s\n", str); -} -FileList::FileList() -{ -} -FileList::~FileList() -{ - Clear(); -} -void FileList::AddFile(const char *filepath, const char *filename, FileListNodeContext context) -{ - if (filepath==0 || filename==0) - return; - - char *data; - //std::fstream file; - //file.open(filename, std::ios::in | std::ios::binary); - - FILE *fp = fopen(filepath, "rb"); - if (fp==0) - return; - fseek(fp, 0, SEEK_END); - int length = ftell(fp); - fseek(fp, 0, SEEK_SET); - - if (length > (int) ((unsigned int)-1 / 8)) - { - // If this assert hits, split up your file. You could also change BitSize_t in RakNetTypes.h to unsigned long long but this is not recommended for performance reasons - RakAssert("Cannot add files over 536 MB" && 0); - fclose(fp); - return; - } - - - - bool usedAlloca=false; - if (length < MAX_ALLOCA_STACK_ALLOCATION) - { - data = ( char* ) alloca( length ); - usedAlloca=true; - } - else - - { - data = (char*) rakMalloc_Ex( length, _FILE_AND_LINE_ ); - } - - fread(data, 1, length, fp); - AddFile(filename, filepath, data, length, length, context); - fclose(fp); - - - if (usedAlloca==false) - - rakFree_Ex(data, _FILE_AND_LINE_ ); - -} -void FileList::AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference, bool takeDataPointer) -{ - if (filename==0) - return; - if (strlen(filename)>MAX_FILENAME_LENGTH) - { - // Should be enough for anyone - RakAssert(0); - return; - } - // If adding a reference, do not send data - RakAssert(isAReference==false || data==0); - // Avoid duplicate insertions unless the data is different, in which case overwrite the old data - unsigned i; - for (i=0; i dirList; - char root[260]; - char fullPath[520]; - _finddata_t fileInfo; - intptr_t dir; - FILE *fp; - char *dirSoFar, *fileData; - dirSoFar=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ ); - - if (applicationDirectory) - strcpy(root, applicationDirectory); - else - root[0]=0; - - int rootLen=(int)strlen(root); - if (rootLen) - { - strcpy(dirSoFar, root); - if (FixEndingSlash(dirSoFar)) - rootLen++; - } - else - dirSoFar[0]=0; - - if (subDirectory) - { - strcat(dirSoFar, subDirectory); - FixEndingSlash(dirSoFar); - } - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnAddFilesFromDirectoryStarted(this, dirSoFar); - // RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dirSoFar); - dirList.Push(dirSoFar, _FILE_AND_LINE_ ); - while (dirList.Size()) - { - dirSoFar=dirList.Pop(); - strcpy(fullPath, dirSoFar); - // Changed from *.* to * for Linux compatibility - strcat(fullPath, "*"); - - - dir=_findfirst(fullPath, &fileInfo ); - if (dir==-1) - { - _findclose(dir); - rakFree_Ex(dirSoFar, _FILE_AND_LINE_ ); - unsigned i; - for (i=0; i < dirList.Size(); i++) - rakFree_Ex(dirList[i], _FILE_AND_LINE_ ); - return; - } - -// RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", fullPath, dirList.Size()); - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnDirectory(this, fullPath, dirList.Size()); - - do - { - // no guarantee these entries are first... - if (strcmp("." , fileInfo.name) == 0 || - strcmp("..", fileInfo.name) == 0) - { - continue; - } - - if ((fileInfo.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM))==0) - { - strcpy(fullPath, dirSoFar); - strcat(fullPath, fileInfo.name); - fileData=0; - - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnFile(this, dirSoFar, fileInfo.name, fileInfo.size); - - if (writeData && writeHash) - { - fp = fopen(fullPath, "rb"); - if (fp) - { - fileData= (char*) rakMalloc_Ex( fileInfo.size+HASH_LENGTH, _FILE_AND_LINE_ ); - fread(fileData+HASH_LENGTH, fileInfo.size, 1, fp); - fclose(fp); - - unsigned int hash = SuperFastHash(fileData+HASH_LENGTH, fileInfo.size); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); - memcpy(fileData, &hash, HASH_LENGTH); - - // sha1.Reset(); - // sha1.Update( ( unsigned char* ) fileData+HASH_LENGTH, fileInfo.size ); - // sha1.Final(); - // memcpy(fileData, sha1.GetHash(), HASH_LENGTH); - // File data and hash - AddFile((const char*)fullPath+rootLen, fullPath, fileData, fileInfo.size+HASH_LENGTH, fileInfo.size, context); - } - } - else if (writeHash) - { -// sha1.Reset(); -// sha1.HashFile((char*)fullPath); -// sha1.Final(); - - unsigned int hash = SuperFastHashFile(fullPath); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); - - // Hash only - // AddFile((const char*)fullPath+rootLen, (const char*)sha1.GetHash(), HASH_LENGTH, fileInfo.size, context); - AddFile((const char*)fullPath+rootLen, fullPath, (const char*)&hash, HASH_LENGTH, fileInfo.size, context); - } - else if (writeData) - { - fileData= (char*) rakMalloc_Ex( fileInfo.size, _FILE_AND_LINE_ ); - fp = fopen(fullPath, "rb"); - fread(fileData, fileInfo.size, 1, fp); - fclose(fp); - - // File data only - AddFile(fullPath+rootLen, fullPath, fileData, fileInfo.size, fileInfo.size, context); - } - else - { - // Just the filename - AddFile(fullPath+rootLen, fullPath, 0, 0, fileInfo.size, context); - } - - if (fileData) - rakFree_Ex(fileData, _FILE_AND_LINE_ ); - } - else if ((fileInfo.attrib & _A_SUBDIR) && (fileInfo.attrib & (_A_HIDDEN | _A_SYSTEM))==0 && recursive) - { - char *newDir=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ ); - strcpy(newDir, dirSoFar); - strcat(newDir, fileInfo.name); - strcat(newDir, "/"); - dirList.Push(newDir, _FILE_AND_LINE_ ); - } - - } while (_findnext(dir, &fileInfo ) != -1); - - _findclose(dir); - rakFree_Ex(dirSoFar, _FILE_AND_LINE_ ); - } - -} -void FileList::Clear(void) -{ - unsigned i; - for (i=0; iWriteCompressed(fileList.Size()); - unsigned i; - for (i=0; i < fileList.Size(); i++) - { - outBitStream->WriteCompressed(fileList[i].context.op); - outBitStream->WriteCompressed(fileList[i].context.fileId); - StringCompressor::Instance()->EncodeString(fileList[i].filename.C_String(), MAX_FILENAME_LENGTH, outBitStream); - - bool writeFileData = fileList[i].dataLengthBytes>0==true; - outBitStream->Write(writeFileData); - if (writeFileData) - { - outBitStream->WriteCompressed(fileList[i].dataLengthBytes); - outBitStream->Write(fileList[i].data, fileList[i].dataLengthBytes); - } - - outBitStream->Write((bool)(fileList[i].fileLengthBytes==fileList[i].dataLengthBytes)); - if (fileList[i].fileLengthBytes!=fileList[i].dataLengthBytes) - outBitStream->WriteCompressed(fileList[i].fileLengthBytes); - } -} -bool FileList::Deserialize(RakNet::BitStream *inBitStream) -{ - bool b, dataLenNonZero=false, fileLenMatchesDataLen=false; - char filename[512]; - unsigned int fileListSize; - FileListNode n; - b=inBitStream->ReadCompressed(fileListSize); -#ifdef _DEBUG - RakAssert(b); - RakAssert(fileListSize < 10000); -#endif - if (b==false || fileListSize > 10000) - return false; // Sanity check - Clear(); - unsigned i; - for (i=0; i < fileListSize; i++) - { - inBitStream->ReadCompressed(n.context.op); - inBitStream->ReadCompressed(n.context.fileId); - StringCompressor::Instance()->DecodeString((char*)filename, MAX_FILENAME_LENGTH, inBitStream); - inBitStream->Read(dataLenNonZero); - if (dataLenNonZero) - { - inBitStream->ReadCompressed(n.dataLengthBytes); - // sanity check - if (n.dataLengthBytes>2000000000) - { -#ifdef _DEBUG - RakAssert(n.dataLengthBytes<=2000000000); -#endif - return false; - } - n.data=(char*) rakMalloc_Ex( (size_t) n.dataLengthBytes, _FILE_AND_LINE_ ); - inBitStream->Read(n.data, n.dataLengthBytes); - } - else - { - n.dataLengthBytes=0; - n.data=0; - } - - b=inBitStream->Read(fileLenMatchesDataLen); - if (fileLenMatchesDataLen) - n.fileLengthBytes=(unsigned) n.dataLengthBytes; - else - b=inBitStream->ReadCompressed(n.fileLengthBytes); -#ifdef _DEBUG - RakAssert(b); -#endif - if (b==0) - { - Clear(); - return false; - } - n.filename=filename; - n.fullPathToFile=filename; - fileList.Insert(n, _FILE_AND_LINE_); - } - - return true; -} -void FileList::GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir) -{ - // For all files in this list that do not match the input list, write them to the output list. - // dirSubset allows checking only a portion of the files in this list. - unsigned thisIndex, inputIndex; - unsigned dirSubsetLen, localPathLen, remoteSubdirLen; - bool match; - if (dirSubset) - dirSubsetLen = (unsigned int) strlen(dirSubset); - else - dirSubsetLen = 0; - if (remoteSubdir && remoteSubdir[0]) - { - remoteSubdirLen=(unsigned int) strlen(remoteSubdir); - if (IsSlash(remoteSubdir[remoteSubdirLen-1])) - remoteSubdirLen--; - } - else - remoteSubdirLen=0; - - for (thisIndex=0; thisIndex < fileList.Size(); thisIndex++) - { - localPathLen = (unsigned int) fileList[thisIndex].filename.GetLength(); - while (localPathLen>0) - { - if (IsSlash(fileList[thisIndex].filename[localPathLen-1])) - { - localPathLen--; - break; - } - localPathLen--; - } - - // fileList[thisIndex].filename has to match dirSubset and be shorter or equal to it in length. - if (dirSubsetLen>0 && - (localPathLendirSubsetLen && IsSlash(fileList[thisIndex].filename[dirSubsetLen])==false))) - continue; - - match=false; - for (inputIndex=0; inputIndex < input->fileList.Size(); inputIndex++) - { - // If the filenames, hashes, and lengths match then skip this element in fileList. Otherwise write it to output - if (_stricmp(input->fileList[inputIndex].filename.C_String()+remoteSubdirLen,fileList[thisIndex].filename.C_String()+dirSubsetLen)==0) - { - match=true; - if (input->fileList[inputIndex].fileLengthBytes==fileList[thisIndex].fileLengthBytes && - input->fileList[inputIndex].dataLengthBytes==fileList[thisIndex].dataLengthBytes && - memcmp(input->fileList[inputIndex].data,fileList[thisIndex].data,(size_t) fileList[thisIndex].dataLengthBytes)==0) - { - // File exists on both machines and is the same. - break; - } - else - { - // File exists on both machines and is not the same. - output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false); - break; - } - } - } - if (match==false) - { - // Other system does not have the file at all - output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false); - } - } -} -void FileList::ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash) -{ - unsigned fileLength; -// CSHA1 sha1; - FILE *fp; - char fullPath[512]; - unsigned i; -// char *fileData; - - for (i=0; i < fileList.Size(); i++) - { - strcpy(fullPath, applicationDirectory); - FixEndingSlash(fullPath); - strcat(fullPath,fileList[i].filename); - fp=fopen(fullPath, "rb"); - if (fp==0) - { - missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, 0, FileListNodeContext(0,0), false); - } - else - { - fseek(fp, 0, SEEK_END); - fileLength = ftell(fp); - fseek(fp, 0, SEEK_SET); - - if (fileLength != fileList[i].fileLengthBytes && alwaysWriteHash==false) - { - missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false); - } - else - { - -// fileData= (char*) rakMalloc_Ex( fileLength, _FILE_AND_LINE_ ); -// fread(fileData, fileLength, 1, fp); - -// sha1.Reset(); -// sha1.Update( ( unsigned char* ) fileData, fileLength ); -// sha1.Final(); - -// rakFree_Ex(fileData, _FILE_AND_LINE_ ); - - unsigned int hash = SuperFastHashFilePtr(fp); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); - - //if (fileLength != fileList[i].fileLength || memcmp( sha1.GetHash(), fileList[i].data, HASH_LENGTH)!=0) - if (fileLength != fileList[i].fileLengthBytes || memcmp( &hash, fileList[i].data, HASH_LENGTH)!=0) - { - if (neverWriteHash==false) - // missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)sha1.GetHash(), HASH_LENGTH, fileLength, 0); - missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, (const char *) &hash, HASH_LENGTH, fileLength, FileListNodeContext(0,0), false); - else - missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false); - } - } - fclose(fp); - } - } -} -void FileList::PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles) -{ - FILE *fp; - char fullPath[512]; - unsigned i; -// CSHA1 sha1; - - i=0; - while (i < fileList.Size()) - { - rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ ); - strcpy(fullPath, applicationDirectory); - FixEndingSlash(fullPath); - strcat(fullPath,fileList[i].filename.C_String()); - fp=fopen(fullPath, "rb"); - if (fp) - { - if (writeFileHash || writeFileData) - { - fseek(fp, 0, SEEK_END); - fileList[i].fileLengthBytes = ftell(fp); - fseek(fp, 0, SEEK_SET); - if (writeFileHash) - { - if (writeFileData) - { - // Hash + data so offset the data by HASH_LENGTH - fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes+HASH_LENGTH, _FILE_AND_LINE_ ); - fread(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes, 1, fp); -// sha1.Reset(); -// sha1.Update((unsigned char*)fileList[i].data+HASH_LENGTH, fileList[i].fileLength); -// sha1.Final(); - unsigned int hash = SuperFastHash(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); -// memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH); - memcpy(fileList[i].data, &hash, HASH_LENGTH); - } - else - { - // Hash only - fileList[i].dataLengthBytes=HASH_LENGTH; - if (fileList[i].fileLengthBytes < HASH_LENGTH) - fileList[i].data=(char*) rakMalloc_Ex( HASH_LENGTH, _FILE_AND_LINE_ ); - else - fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ ); - fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp); - // sha1.Reset(); - // sha1.Update((unsigned char*)fileList[i].data, fileList[i].fileLength); - // sha1.Final(); - unsigned int hash = SuperFastHash(fileList[i].data, fileList[i].fileLengthBytes); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); - // memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH); - memcpy(fileList[i].data, &hash, HASH_LENGTH); - } - } - else - { - // Data only - fileList[i].dataLengthBytes=fileList[i].fileLengthBytes; - fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ ); - fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp); - } - - fclose(fp); - i++; - } - else - { - fileList[i].data=0; - fileList[i].dataLengthBytes=0; - } - } - else - { - if (removeUnknownFiles) - { - fileList.RemoveAtIndex(i); - } - else - i++; - } - } -} -void FileList::FlagFilesAsReferences(void) -{ - for (unsigned int i=0; i < fileList.Size(); i++) - { - fileList[i].isAReference=true; - fileList[i].dataLengthBytes=fileList[i].fileLengthBytes; - } -} -void FileList::WriteDataToDisk(const char *applicationDirectory) -{ - char fullPath[512]; - unsigned i,j; - - for (i=0; i < fileList.Size(); i++) - { - strcpy(fullPath, applicationDirectory); - FixEndingSlash(fullPath); - strcat(fullPath,fileList[i].filename.C_String()); - - // Security - Don't allow .. in the filename anywhere so you can't write outside of the root directory - for (j=1; j < fileList[i].filename.GetLength(); j++) - { - if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.') - { -#ifdef _DEBUG - RakAssert(0); -#endif - // Just cancel the write entirely - return; - } - } - - WriteFileWithDirectories(fullPath, fileList[i].data, (unsigned int) fileList[i].dataLengthBytes); - } -} - -#ifdef _MSC_VER -#pragma warning( disable : 4966 ) // unlink declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. -#endif -void FileList::DeleteFiles(const char *applicationDirectory) -{ - - - - char fullPath[512]; - unsigned i,j; - - for (i=0; i < fileList.Size(); i++) - { - // The filename should not have .. in the path - if it does ignore it - for (j=1; j < fileList[i].filename.GetLength(); j++) - { - if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.') - { -#ifdef _DEBUG - RakAssert(0); -#endif - // Just cancel the deletion entirely - return; - } - } - - strcpy(fullPath, applicationDirectory); - FixEndingSlash(fullPath); - strcat(fullPath, fileList[i].filename.C_String()); - -#ifdef _MSC_VER -#pragma warning( disable : 4966 ) // unlink declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. -#endif - int result = unlink(fullPath); - if (result!=0) - { - RAKNET_DEBUG_PRINTF("FileList::DeleteFiles: unlink (%s) failed.\n", fullPath); - } - } - -} - -void FileList::AddCallback(FileListProgress *cb) -{ - if (cb==0) - return; - - if ((unsigned int) fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int)-1) - fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_); -} -void FileList::RemoveCallback(FileListProgress *cb) -{ - unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb); - if (idx!=(unsigned int) -1) - fileListProgressCallbacks.RemoveAtIndex(idx); -} -void FileList::ClearCallbacks(void) -{ - fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_); -} -void FileList::GetCallbacks(DataStructures::List &callbacks) -{ - callbacks = fileListProgressCallbacks; -} - - -bool FileList::FixEndingSlash(char *str) -{ -#ifdef _WIN32 - if (str[strlen(str)-1]!='/' && str[strlen(str)-1]!='\\') - { - strcat(str, "\\"); // Only \ works with system commands, used by AutopatcherClient - return true; - } -#else - if (str[strlen(str)-1]!='\\' && str[strlen(str)-1]!='/') - { - strcat(str, "/"); // Only / works with Linux - return true; - } -#endif - - return false; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_FileOperations +#include "FileList.h" + +#if _RAKNET_SUPPORT_FileOperations==1 + +#include // RAKNET_DEBUG_PRINTF +#include "RakAssert.h" +#if defined(ANDROID) +// not needed and unavailable on arm64 +#elif defined(_WIN32) || defined(__CYGWIN__) +#include + + +// #elif !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) && !defined ( __PPC__ ) && !defined ( __FreeBSD__ ) && !defined ( __S3E__ ) +// #include +#endif + + +#ifdef _WIN32 +// For mkdir +#include + + +#else +#include +#endif + +//#include "SHA1.h" +#include "DS_Queue.h" +#include "StringCompressor.h" +#include "BitStream.h" +#include "FileOperations.h" +#include "SuperFastHash.h" +#include "RakAssert.h" +#include "LinuxStrings.h" + +#define MAX_FILENAME_LENGTH 512 +static const unsigned HASH_LENGTH=4; + +using namespace RakNet; + +// alloca + +#if defined(_WIN32) +#include + + +#else +#if !defined ( __FreeBSD__ ) +#include +#endif +#include +#include +#include +#include "_FindFirst.h" +#include //defines intptr_t +#endif + +#include "RakAlloca.h" + +//int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data ) +//{ +// return strcmp(key, data.filename); +//} + + +STATIC_FACTORY_DEFINITIONS(FileListProgress,FileListProgress) +STATIC_FACTORY_DEFINITIONS(FLP_Printf,FLP_Printf) +STATIC_FACTORY_DEFINITIONS(FileList,FileList) + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// First callback called when FileList::AddFilesFromDirectory() starts +void FLP_Printf::OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) { + (void) fileList; + RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dir);} + +/// Called for each directory, when that directory begins processing +void FLP_Printf::OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) { + (void) fileList; + RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", dir, directoriesRemaining);} +void FLP_Printf::OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID ) +{ + (void) setID; + + char str[32]; + systemAddress.ToString(true, (char*) str); + RAKNET_DEBUG_PRINTF("File pushes complete to %s\n", str); +} +void FLP_Printf::OnSendAborted( SystemAddress systemAddress ) +{ + char str[32]; + systemAddress.ToString(true, (char*) str); + RAKNET_DEBUG_PRINTF("Send aborted to %s\n", str); +} +FileList::FileList() +{ +} +FileList::~FileList() +{ + Clear(); +} +void FileList::AddFile(const char *filepath, const char *filename, FileListNodeContext context) +{ + if (filepath==0 || filename==0) + return; + + char *data; + //std::fstream file; + //file.open(filename, std::ios::in | std::ios::binary); + + FILE *fp = fopen(filepath, "rb"); + if (fp==0) + return; + fseek(fp, 0, SEEK_END); + int length = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (length > (int) ((unsigned int)-1 / 8)) + { + // If this assert hits, split up your file. You could also change BitSize_t in RakNetTypes.h to unsigned long long but this is not recommended for performance reasons + RakAssert("Cannot add files over 536 MB" && 0); + fclose(fp); + return; + } + + + + bool usedAlloca=false; + if (length < MAX_ALLOCA_STACK_ALLOCATION) + { + data = ( char* ) alloca( length ); + usedAlloca=true; + } + else + + { + data = (char*) rakMalloc_Ex( length, _FILE_AND_LINE_ ); + } + + fread(data, 1, length, fp); + AddFile(filename, filepath, data, length, length, context); + fclose(fp); + + + if (usedAlloca==false) + + rakFree_Ex(data, _FILE_AND_LINE_ ); + +} +void FileList::AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference, bool takeDataPointer) +{ + if (filename==0) + return; + if (strlen(filename)>MAX_FILENAME_LENGTH) + { + // Should be enough for anyone + RakAssert(0); + return; + } + // If adding a reference, do not send data + RakAssert(isAReference==false || data==0); + // Avoid duplicate insertions unless the data is different, in which case overwrite the old data + unsigned i; + for (i=0; i dirList; + char root[260]; + char fullPath[520]; + _finddata_t fileInfo; + intptr_t dir; + FILE *fp; + char *dirSoFar, *fileData; + dirSoFar=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ ); + + if (applicationDirectory) + strcpy(root, applicationDirectory); + else + root[0]=0; + + int rootLen=(int)strlen(root); + if (rootLen) + { + strcpy(dirSoFar, root); + if (FixEndingSlash(dirSoFar)) + rootLen++; + } + else + dirSoFar[0]=0; + + if (subDirectory) + { + strcat(dirSoFar, subDirectory); + FixEndingSlash(dirSoFar); + } + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnAddFilesFromDirectoryStarted(this, dirSoFar); + // RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dirSoFar); + dirList.Push(dirSoFar, _FILE_AND_LINE_ ); + while (dirList.Size()) + { + dirSoFar=dirList.Pop(); + strcpy(fullPath, dirSoFar); + // Changed from *.* to * for Linux compatibility + strcat(fullPath, "*"); + + + dir=_findfirst(fullPath, &fileInfo ); + if (dir==-1) + { + _findclose(dir); + rakFree_Ex(dirSoFar, _FILE_AND_LINE_ ); + unsigned i; + for (i=0; i < dirList.Size(); i++) + rakFree_Ex(dirList[i], _FILE_AND_LINE_ ); + return; + } + +// RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", fullPath, dirList.Size()); + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnDirectory(this, fullPath, dirList.Size()); + + do + { + // no guarantee these entries are first... + if (strcmp("." , fileInfo.name) == 0 || + strcmp("..", fileInfo.name) == 0) + { + continue; + } + + if ((fileInfo.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM))==0) + { + strcpy(fullPath, dirSoFar); + strcat(fullPath, fileInfo.name); + fileData=0; + + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnFile(this, dirSoFar, fileInfo.name, fileInfo.size); + + if (writeData && writeHash) + { + fp = fopen(fullPath, "rb"); + if (fp) + { + fileData= (char*) rakMalloc_Ex( fileInfo.size+HASH_LENGTH, _FILE_AND_LINE_ ); + fread(fileData+HASH_LENGTH, fileInfo.size, 1, fp); + fclose(fp); + + unsigned int hash = SuperFastHash(fileData+HASH_LENGTH, fileInfo.size); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); + memcpy(fileData, &hash, HASH_LENGTH); + + // sha1.Reset(); + // sha1.Update( ( unsigned char* ) fileData+HASH_LENGTH, fileInfo.size ); + // sha1.Final(); + // memcpy(fileData, sha1.GetHash(), HASH_LENGTH); + // File data and hash + AddFile((const char*)fullPath+rootLen, fullPath, fileData, fileInfo.size+HASH_LENGTH, fileInfo.size, context); + } + } + else if (writeHash) + { +// sha1.Reset(); +// sha1.HashFile((char*)fullPath); +// sha1.Final(); + + unsigned int hash = SuperFastHashFile(fullPath); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); + + // Hash only + // AddFile((const char*)fullPath+rootLen, (const char*)sha1.GetHash(), HASH_LENGTH, fileInfo.size, context); + AddFile((const char*)fullPath+rootLen, fullPath, (const char*)&hash, HASH_LENGTH, fileInfo.size, context); + } + else if (writeData) + { + fileData= (char*) rakMalloc_Ex( fileInfo.size, _FILE_AND_LINE_ ); + fp = fopen(fullPath, "rb"); + fread(fileData, fileInfo.size, 1, fp); + fclose(fp); + + // File data only + AddFile(fullPath+rootLen, fullPath, fileData, fileInfo.size, fileInfo.size, context); + } + else + { + // Just the filename + AddFile(fullPath+rootLen, fullPath, 0, 0, fileInfo.size, context); + } + + if (fileData) + rakFree_Ex(fileData, _FILE_AND_LINE_ ); + } + else if ((fileInfo.attrib & _A_SUBDIR) && (fileInfo.attrib & (_A_HIDDEN | _A_SYSTEM))==0 && recursive) + { + char *newDir=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ ); + strcpy(newDir, dirSoFar); + strcat(newDir, fileInfo.name); + strcat(newDir, "/"); + dirList.Push(newDir, _FILE_AND_LINE_ ); + } + + } while (_findnext(dir, &fileInfo ) != -1); + + _findclose(dir); + rakFree_Ex(dirSoFar, _FILE_AND_LINE_ ); + } + +} +void FileList::Clear(void) +{ + unsigned i; + for (i=0; iWriteCompressed(fileList.Size()); + unsigned i; + for (i=0; i < fileList.Size(); i++) + { + outBitStream->WriteCompressed(fileList[i].context.op); + outBitStream->WriteCompressed(fileList[i].context.fileId); + StringCompressor::Instance()->EncodeString(fileList[i].filename.C_String(), MAX_FILENAME_LENGTH, outBitStream); + + bool writeFileData = fileList[i].dataLengthBytes>0==true; + outBitStream->Write(writeFileData); + if (writeFileData) + { + outBitStream->WriteCompressed(fileList[i].dataLengthBytes); + outBitStream->Write(fileList[i].data, fileList[i].dataLengthBytes); + } + + outBitStream->Write((bool)(fileList[i].fileLengthBytes==fileList[i].dataLengthBytes)); + if (fileList[i].fileLengthBytes!=fileList[i].dataLengthBytes) + outBitStream->WriteCompressed(fileList[i].fileLengthBytes); + } +} +bool FileList::Deserialize(RakNet::BitStream *inBitStream) +{ + bool b, dataLenNonZero=false, fileLenMatchesDataLen=false; + char filename[512]; + unsigned int fileListSize; + FileListNode n; + b=inBitStream->ReadCompressed(fileListSize); +#ifdef _DEBUG + RakAssert(b); + RakAssert(fileListSize < 10000); +#endif + if (b==false || fileListSize > 10000) + return false; // Sanity check + Clear(); + unsigned i; + for (i=0; i < fileListSize; i++) + { + inBitStream->ReadCompressed(n.context.op); + inBitStream->ReadCompressed(n.context.fileId); + StringCompressor::Instance()->DecodeString((char*)filename, MAX_FILENAME_LENGTH, inBitStream); + inBitStream->Read(dataLenNonZero); + if (dataLenNonZero) + { + inBitStream->ReadCompressed(n.dataLengthBytes); + // sanity check + if (n.dataLengthBytes>2000000000) + { +#ifdef _DEBUG + RakAssert(n.dataLengthBytes<=2000000000); +#endif + return false; + } + n.data=(char*) rakMalloc_Ex( (size_t) n.dataLengthBytes, _FILE_AND_LINE_ ); + inBitStream->Read(n.data, n.dataLengthBytes); + } + else + { + n.dataLengthBytes=0; + n.data=0; + } + + b=inBitStream->Read(fileLenMatchesDataLen); + if (fileLenMatchesDataLen) + n.fileLengthBytes=(unsigned) n.dataLengthBytes; + else + b=inBitStream->ReadCompressed(n.fileLengthBytes); +#ifdef _DEBUG + RakAssert(b); +#endif + if (b==0) + { + Clear(); + return false; + } + n.filename=filename; + n.fullPathToFile=filename; + fileList.Insert(n, _FILE_AND_LINE_); + } + + return true; +} +void FileList::GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir) +{ + // For all files in this list that do not match the input list, write them to the output list. + // dirSubset allows checking only a portion of the files in this list. + unsigned thisIndex, inputIndex; + unsigned dirSubsetLen, localPathLen, remoteSubdirLen; + bool match; + if (dirSubset) + dirSubsetLen = (unsigned int) strlen(dirSubset); + else + dirSubsetLen = 0; + if (remoteSubdir && remoteSubdir[0]) + { + remoteSubdirLen=(unsigned int) strlen(remoteSubdir); + if (IsSlash(remoteSubdir[remoteSubdirLen-1])) + remoteSubdirLen--; + } + else + remoteSubdirLen=0; + + for (thisIndex=0; thisIndex < fileList.Size(); thisIndex++) + { + localPathLen = (unsigned int) fileList[thisIndex].filename.GetLength(); + while (localPathLen>0) + { + if (IsSlash(fileList[thisIndex].filename[localPathLen-1])) + { + localPathLen--; + break; + } + localPathLen--; + } + + // fileList[thisIndex].filename has to match dirSubset and be shorter or equal to it in length. + if (dirSubsetLen>0 && + (localPathLendirSubsetLen && IsSlash(fileList[thisIndex].filename[dirSubsetLen])==false))) + continue; + + match=false; + for (inputIndex=0; inputIndex < input->fileList.Size(); inputIndex++) + { + // If the filenames, hashes, and lengths match then skip this element in fileList. Otherwise write it to output + if (_stricmp(input->fileList[inputIndex].filename.C_String()+remoteSubdirLen,fileList[thisIndex].filename.C_String()+dirSubsetLen)==0) + { + match=true; + if (input->fileList[inputIndex].fileLengthBytes==fileList[thisIndex].fileLengthBytes && + input->fileList[inputIndex].dataLengthBytes==fileList[thisIndex].dataLengthBytes && + memcmp(input->fileList[inputIndex].data,fileList[thisIndex].data,(size_t) fileList[thisIndex].dataLengthBytes)==0) + { + // File exists on both machines and is the same. + break; + } + else + { + // File exists on both machines and is not the same. + output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false); + break; + } + } + } + if (match==false) + { + // Other system does not have the file at all + output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false); + } + } +} +void FileList::ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash) +{ + unsigned fileLength; +// CSHA1 sha1; + FILE *fp; + char fullPath[512]; + unsigned i; +// char *fileData; + + for (i=0; i < fileList.Size(); i++) + { + strcpy(fullPath, applicationDirectory); + FixEndingSlash(fullPath); + strcat(fullPath,fileList[i].filename); + fp=fopen(fullPath, "rb"); + if (fp==0) + { + missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, 0, FileListNodeContext(0,0), false); + } + else + { + fseek(fp, 0, SEEK_END); + fileLength = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (fileLength != fileList[i].fileLengthBytes && alwaysWriteHash==false) + { + missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false); + } + else + { + +// fileData= (char*) rakMalloc_Ex( fileLength, _FILE_AND_LINE_ ); +// fread(fileData, fileLength, 1, fp); + +// sha1.Reset(); +// sha1.Update( ( unsigned char* ) fileData, fileLength ); +// sha1.Final(); + +// rakFree_Ex(fileData, _FILE_AND_LINE_ ); + + unsigned int hash = SuperFastHashFilePtr(fp); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); + + //if (fileLength != fileList[i].fileLength || memcmp( sha1.GetHash(), fileList[i].data, HASH_LENGTH)!=0) + if (fileLength != fileList[i].fileLengthBytes || memcmp( &hash, fileList[i].data, HASH_LENGTH)!=0) + { + if (neverWriteHash==false) + // missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)sha1.GetHash(), HASH_LENGTH, fileLength, 0); + missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, (const char *) &hash, HASH_LENGTH, fileLength, FileListNodeContext(0,0), false); + else + missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false); + } + } + fclose(fp); + } + } +} +void FileList::PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles) +{ + FILE *fp; + char fullPath[512]; + unsigned i; +// CSHA1 sha1; + + i=0; + while (i < fileList.Size()) + { + rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ ); + strcpy(fullPath, applicationDirectory); + FixEndingSlash(fullPath); + strcat(fullPath,fileList[i].filename.C_String()); + fp=fopen(fullPath, "rb"); + if (fp) + { + if (writeFileHash || writeFileData) + { + fseek(fp, 0, SEEK_END); + fileList[i].fileLengthBytes = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (writeFileHash) + { + if (writeFileData) + { + // Hash + data so offset the data by HASH_LENGTH + fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes+HASH_LENGTH, _FILE_AND_LINE_ ); + fread(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes, 1, fp); +// sha1.Reset(); +// sha1.Update((unsigned char*)fileList[i].data+HASH_LENGTH, fileList[i].fileLength); +// sha1.Final(); + unsigned int hash = SuperFastHash(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); +// memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH); + memcpy(fileList[i].data, &hash, HASH_LENGTH); + } + else + { + // Hash only + fileList[i].dataLengthBytes=HASH_LENGTH; + if (fileList[i].fileLengthBytes < HASH_LENGTH) + fileList[i].data=(char*) rakMalloc_Ex( HASH_LENGTH, _FILE_AND_LINE_ ); + else + fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ ); + fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp); + // sha1.Reset(); + // sha1.Update((unsigned char*)fileList[i].data, fileList[i].fileLength); + // sha1.Final(); + unsigned int hash = SuperFastHash(fileList[i].data, fileList[i].fileLengthBytes); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); + // memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH); + memcpy(fileList[i].data, &hash, HASH_LENGTH); + } + } + else + { + // Data only + fileList[i].dataLengthBytes=fileList[i].fileLengthBytes; + fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ ); + fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp); + } + + fclose(fp); + i++; + } + else + { + fileList[i].data=0; + fileList[i].dataLengthBytes=0; + } + } + else + { + if (removeUnknownFiles) + { + fileList.RemoveAtIndex(i); + } + else + i++; + } + } +} +void FileList::FlagFilesAsReferences(void) +{ + for (unsigned int i=0; i < fileList.Size(); i++) + { + fileList[i].isAReference=true; + fileList[i].dataLengthBytes=fileList[i].fileLengthBytes; + } +} +void FileList::WriteDataToDisk(const char *applicationDirectory) +{ + char fullPath[512]; + unsigned i,j; + + for (i=0; i < fileList.Size(); i++) + { + strcpy(fullPath, applicationDirectory); + FixEndingSlash(fullPath); + strcat(fullPath,fileList[i].filename.C_String()); + + // Security - Don't allow .. in the filename anywhere so you can't write outside of the root directory + for (j=1; j < fileList[i].filename.GetLength(); j++) + { + if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.') + { +#ifdef _DEBUG + RakAssert(0); +#endif + // Just cancel the write entirely + return; + } + } + + WriteFileWithDirectories(fullPath, fileList[i].data, (unsigned int) fileList[i].dataLengthBytes); + } +} + +#ifdef _MSC_VER +#pragma warning( disable : 4966 ) // unlink declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. +#endif +void FileList::DeleteFiles(const char *applicationDirectory) +{ + + + + char fullPath[512]; + unsigned i,j; + + for (i=0; i < fileList.Size(); i++) + { + // The filename should not have .. in the path - if it does ignore it + for (j=1; j < fileList[i].filename.GetLength(); j++) + { + if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.') + { +#ifdef _DEBUG + RakAssert(0); +#endif + // Just cancel the deletion entirely + return; + } + } + + strcpy(fullPath, applicationDirectory); + FixEndingSlash(fullPath); + strcat(fullPath, fileList[i].filename.C_String()); + +#ifdef _MSC_VER +#pragma warning( disable : 4966 ) // unlink declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. +#endif + int result = unlink(fullPath); + if (result!=0) + { + RAKNET_DEBUG_PRINTF("FileList::DeleteFiles: unlink (%s) failed.\n", fullPath); + } + } + +} + +void FileList::AddCallback(FileListProgress *cb) +{ + if (cb==0) + return; + + if ((unsigned int) fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int)-1) + fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_); +} +void FileList::RemoveCallback(FileListProgress *cb) +{ + unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb); + if (idx!=(unsigned int) -1) + fileListProgressCallbacks.RemoveAtIndex(idx); +} +void FileList::ClearCallbacks(void) +{ + fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_); +} +void FileList::GetCallbacks(DataStructures::List &callbacks) +{ + callbacks = fileListProgressCallbacks; +} + + +bool FileList::FixEndingSlash(char *str) +{ +#ifdef _WIN32 + if (str[strlen(str)-1]!='/' && str[strlen(str)-1]!='\\') + { + strcat(str, "\\"); // Only \ works with system commands, used by AutopatcherClient + return true; + } +#else + if (str[strlen(str)-1]!='\\' && str[strlen(str)-1]!='/') + { + strcat(str, "/"); // Only / works with Linux + return true; + } +#endif + + return false; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_FileOperations diff --git a/project/lib_projects/raknet/jni/RaknetSources/FileList.h b/project/lib_projects/raknet/jni/RaknetSources/FileList.h index 6469a1f..4642658 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FileList.h +++ b/project/lib_projects/raknet/jni/RaknetSources/FileList.h @@ -1,258 +1,258 @@ -/// \file FileList.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __FILE_LIST -#define __FILE_LIST - -#include "Export.h" -#include "DS_List.h" -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "FileListNodeContext.h" -#include "RakString.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -namespace RakNet -{ - class BitStream; -} - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -class FileList; - - -/// Represents once instance of a file -struct FileListNode -{ - /// Name of the file - RakNet::RakString filename; - - /// Full path to the file, which may be different than filename - RakNet::RakString fullPathToFile; - - /// File data (may be null if not ready) - char *data; - - /// Length of \a data. May be greater than fileLength if prepended with a file hash - BitSize_t dataLengthBytes; - - /// Length of the file - unsigned fileLengthBytes; - - /// User specific data for whatever, describing this file. - FileListNodeContext context; - - /// If true, data and dataLengthBytes should be empty. This is just storing the filename - bool isAReference; -}; - -/// Callback interface set with FileList::SetCallback() in case you want progress notifications when FileList::AddFilesFromDirectory() is called -class RAK_DLL_EXPORT FileListProgress -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FileListProgress) - - FileListProgress() {} - virtual ~FileListProgress() {} - - /// First callback called when FileList::AddFilesFromDirectory() starts - virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) { - (void) fileList; - (void) dir; - } - - /// Called for each directory, when that directory begins processing - virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) { - (void) fileList; - (void) dir; - (void) directoriesRemaining; - } - - /// Called for each file, when that file begins processing - virtual void OnFile(FileList *fileList, char *dir, char *fileName, unsigned int fileSize) { - (void) fileList; - (void) dir; - (void) fileName; - (void) fileSize; - } - - /// \brief This function is called when we are sending a file to a remote system. - /// \param[in] fileName The name of the file being sent - /// \param[in] fileLengthBytes How long the file is - /// \param[in] offset The offset in bytes into the file that we are sending - /// \param[in] bytesBeingSent How many bytes we are sending this push - /// \param[in] done If this file is now done with this push - /// \param[in] targetSystem Who we are sending to - virtual void OnFilePush(const char *fileName, unsigned int fileLengthBytes, unsigned int offset, unsigned int bytesBeingSent, bool done, SystemAddress targetSystem, unsigned short setId) - { - (void) fileName; - (void) fileLengthBytes; - (void) offset; - (void) bytesBeingSent; - (void) done; - (void) targetSystem; - (void) setId; - } - - /// \brief This function is called when all files have been read and are being transferred to a remote system - virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setId ) - { - (void) systemAddress; - (void) setId; - } - - /// \brief This function is called when a send to a system was aborted (probably due to disconnection) - virtual void OnSendAborted( SystemAddress systemAddress ) - { - (void) systemAddress; - } -}; - -/// Implementation of FileListProgress to use RAKNET_DEBUG_PRINTF -class RAK_DLL_EXPORT FLP_Printf : public FileListProgress -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FLP_Printf) - - FLP_Printf() {} - virtual ~FLP_Printf() {} - - /// First callback called when FileList::AddFilesFromDirectory() starts - virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir); - - /// Called for each directory, when that directory begins processing - virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining); - - /// \brief This function is called when all files have been transferred to a particular remote system - virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID ); - - /// \brief This function is called when a send to a system was aborted (probably due to disconnection) - virtual void OnSendAborted( SystemAddress systemAddress ); -}; - -class RAK_DLL_EXPORT FileList -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FileList) - - FileList(); - ~FileList(); - /// \brief Add all the files at a given directory. - /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. - /// \param[in] subDirectory The rest of the path to the file. This is stored as a prefix to the filename - /// \param[in] writeHash The first 4 bytes is a hash of the file, with the remainder the actual file data (should \a writeData be true) - /// \param[in] writeData Write the contents of each file - /// \param[in] recursive Whether or not to visit subdirectories - /// \param[in] context User defined byte to store with each file. Use for whatever you want. - void AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, FileListNodeContext context); - - /// Deallocate all memory - void Clear(void); - - /// Write all encoded data into a bitstream - void Serialize(RakNet::BitStream *outBitStream); - - /// Read all encoded data from a bitstream. Clear() is called before deserializing. - bool Deserialize(RakNet::BitStream *inBitStream); - - /// \brief Given the existing set of files, search applicationDirectory for the same files. - /// \details For each file that is missing or different, add that file to \a missingOrChangedFiles. Note: the file contents are not written, and only the hash if written if \a alwaysWriteHash is true - /// alwaysWriteHash and neverWriteHash are optimizations to avoid reading the file contents to generate the hash if not necessary because the file is missing or has different lengths anyway. - /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. - /// \param[out] missingOrChangedFiles Output list written to - /// \param[in] alwaysWriteHash If true, and neverWriteHash is false, will hash the file content of the file on disk, and write that as the file data with a length of SHA1_LENGTH bytes. If false, if the file length is different, will only write the filename. - /// \param[in] neverWriteHash If true, will never write the hash, even if available. If false, will write the hash if the file lengths are the same and it was forced to do a comparison. - void ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash); - - /// \brief Return the files that need to be written to make \a input match this current FileList. - /// \details Specify dirSubset to only consider files that start with this path - /// specify remoteSubdir to assume that all filenames in input start with this path, so strip it off when comparing filenames. - /// \param[in] input Full list of files - /// \param[out] output Files that we need to match input - /// \param[in] dirSubset If the filename does not start with this path, just skip this file. - /// \param[in] remoteSubdir Remove this from the filenames of \a input when comparing to existing filenames. - void GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir); - - /// \brief Assuming FileList contains a list of filenames presumably without data, read the data for these filenames - /// \param[in] applicationDirectory Prepend this path to each filename. Trailing slash will be added if necessary. Use \ as the path delineator. - /// \param[in] writeFileData True to read and store the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true - /// \param[in] writeFileHash True to read and store the hash of the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true - /// \param[in] removeUnknownFiles If a file does not exist on disk but is in the file list, remove it from the file list? - void PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles); - - /// By default, GetDeltaToCurrent tags files as non-references, meaning they are assumed to be populated later - /// This tags all files as references, required for IncrementalReadInterface to process them incrementally - void FlagFilesAsReferences(void); - - /// \brief Write all files to disk, prefixing the paths with applicationDirectory - /// \param[in] applicationDirectory path prefix - void WriteDataToDisk(const char *applicationDirectory); - - /// \brief Add a file, given data already in memory. - /// \param[in] filename Name of a file, optionally prefixed with a partial or complete path. Use \ as the path delineator. - /// \param[in] fullPathToFile Full path to the file on disk - /// \param[in] data Contents to write - /// \param[in] dataLength length of the data, which may be greater than fileLength should you prefix extra data, such as the hash - /// \param[in] fileLength Length of the file - /// \param[in] context User defined byte to store with each file. Use for whatever you want. - /// \param[in] isAReference Means that this is just a reference to a file elsewhere - does not actually have any data - /// \param[in] takeDataPointer If true, do not allocate dataLength. Just take the pointer passed to the \a data parameter - void AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference=false, bool takeDataPointer=false); - - /// \brief Add a file, reading it from disk. - /// \param[in] filepath Complete path to the file, including the filename itself - /// \param[in] filename filename to store internally, anything you want, but usually either the complete path or a subset of the complete path. - /// \param[in] context User defined byte to store with each file. Use for whatever you want. - void AddFile(const char *filepath, const char *filename, FileListNodeContext context); - - /// \brief Delete all files stored in the file list. - /// \param[in] applicationDirectory Prefixed to the path to each filename. Use \ as the path delineator. - void DeleteFiles(const char *applicationDirectory); - - /// \brief Adds a callback to get progress reports about what the file list instances do. - /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. - void AddCallback(FileListProgress *cb); - - /// \brief Removes a callback - /// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback() - void RemoveCallback(FileListProgress *cb); - - /// \brief Removes all callbacks - void ClearCallbacks(void); - - /// Returns all callbacks added with AddCallback() - /// \param[out] callbacks The list is set to the list of callbacks - void GetCallbacks(DataStructures::List &callbacks); - - // Here so you can read it, but don't modify it - DataStructures::List fileList; - - static bool FixEndingSlash(char *str); -protected: - DataStructures::List fileListProgressCallbacks; -}; - -} // namespace RakNet - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - -#endif // _RAKNET_SUPPORT_FileOperations +/// \file FileList.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __FILE_LIST +#define __FILE_LIST + +#include "Export.h" +#include "DS_List.h" +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "FileListNodeContext.h" +#include "RakString.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +namespace RakNet +{ + class BitStream; +} + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +class FileList; + + +/// Represents once instance of a file +struct FileListNode +{ + /// Name of the file + RakNet::RakString filename; + + /// Full path to the file, which may be different than filename + RakNet::RakString fullPathToFile; + + /// File data (may be null if not ready) + char *data; + + /// Length of \a data. May be greater than fileLength if prepended with a file hash + BitSize_t dataLengthBytes; + + /// Length of the file + unsigned fileLengthBytes; + + /// User specific data for whatever, describing this file. + FileListNodeContext context; + + /// If true, data and dataLengthBytes should be empty. This is just storing the filename + bool isAReference; +}; + +/// Callback interface set with FileList::SetCallback() in case you want progress notifications when FileList::AddFilesFromDirectory() is called +class RAK_DLL_EXPORT FileListProgress +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FileListProgress) + + FileListProgress() {} + virtual ~FileListProgress() {} + + /// First callback called when FileList::AddFilesFromDirectory() starts + virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) { + (void) fileList; + (void) dir; + } + + /// Called for each directory, when that directory begins processing + virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) { + (void) fileList; + (void) dir; + (void) directoriesRemaining; + } + + /// Called for each file, when that file begins processing + virtual void OnFile(FileList *fileList, char *dir, char *fileName, unsigned int fileSize) { + (void) fileList; + (void) dir; + (void) fileName; + (void) fileSize; + } + + /// \brief This function is called when we are sending a file to a remote system. + /// \param[in] fileName The name of the file being sent + /// \param[in] fileLengthBytes How long the file is + /// \param[in] offset The offset in bytes into the file that we are sending + /// \param[in] bytesBeingSent How many bytes we are sending this push + /// \param[in] done If this file is now done with this push + /// \param[in] targetSystem Who we are sending to + virtual void OnFilePush(const char *fileName, unsigned int fileLengthBytes, unsigned int offset, unsigned int bytesBeingSent, bool done, SystemAddress targetSystem, unsigned short setId) + { + (void) fileName; + (void) fileLengthBytes; + (void) offset; + (void) bytesBeingSent; + (void) done; + (void) targetSystem; + (void) setId; + } + + /// \brief This function is called when all files have been read and are being transferred to a remote system + virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setId ) + { + (void) systemAddress; + (void) setId; + } + + /// \brief This function is called when a send to a system was aborted (probably due to disconnection) + virtual void OnSendAborted( SystemAddress systemAddress ) + { + (void) systemAddress; + } +}; + +/// Implementation of FileListProgress to use RAKNET_DEBUG_PRINTF +class RAK_DLL_EXPORT FLP_Printf : public FileListProgress +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FLP_Printf) + + FLP_Printf() {} + virtual ~FLP_Printf() {} + + /// First callback called when FileList::AddFilesFromDirectory() starts + virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir); + + /// Called for each directory, when that directory begins processing + virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining); + + /// \brief This function is called when all files have been transferred to a particular remote system + virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID ); + + /// \brief This function is called when a send to a system was aborted (probably due to disconnection) + virtual void OnSendAborted( SystemAddress systemAddress ); +}; + +class RAK_DLL_EXPORT FileList +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FileList) + + FileList(); + ~FileList(); + /// \brief Add all the files at a given directory. + /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. + /// \param[in] subDirectory The rest of the path to the file. This is stored as a prefix to the filename + /// \param[in] writeHash The first 4 bytes is a hash of the file, with the remainder the actual file data (should \a writeData be true) + /// \param[in] writeData Write the contents of each file + /// \param[in] recursive Whether or not to visit subdirectories + /// \param[in] context User defined byte to store with each file. Use for whatever you want. + void AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, FileListNodeContext context); + + /// Deallocate all memory + void Clear(void); + + /// Write all encoded data into a bitstream + void Serialize(RakNet::BitStream *outBitStream); + + /// Read all encoded data from a bitstream. Clear() is called before deserializing. + bool Deserialize(RakNet::BitStream *inBitStream); + + /// \brief Given the existing set of files, search applicationDirectory for the same files. + /// \details For each file that is missing or different, add that file to \a missingOrChangedFiles. Note: the file contents are not written, and only the hash if written if \a alwaysWriteHash is true + /// alwaysWriteHash and neverWriteHash are optimizations to avoid reading the file contents to generate the hash if not necessary because the file is missing or has different lengths anyway. + /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. + /// \param[out] missingOrChangedFiles Output list written to + /// \param[in] alwaysWriteHash If true, and neverWriteHash is false, will hash the file content of the file on disk, and write that as the file data with a length of SHA1_LENGTH bytes. If false, if the file length is different, will only write the filename. + /// \param[in] neverWriteHash If true, will never write the hash, even if available. If false, will write the hash if the file lengths are the same and it was forced to do a comparison. + void ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash); + + /// \brief Return the files that need to be written to make \a input match this current FileList. + /// \details Specify dirSubset to only consider files that start with this path + /// specify remoteSubdir to assume that all filenames in input start with this path, so strip it off when comparing filenames. + /// \param[in] input Full list of files + /// \param[out] output Files that we need to match input + /// \param[in] dirSubset If the filename does not start with this path, just skip this file. + /// \param[in] remoteSubdir Remove this from the filenames of \a input when comparing to existing filenames. + void GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir); + + /// \brief Assuming FileList contains a list of filenames presumably without data, read the data for these filenames + /// \param[in] applicationDirectory Prepend this path to each filename. Trailing slash will be added if necessary. Use \ as the path delineator. + /// \param[in] writeFileData True to read and store the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true + /// \param[in] writeFileHash True to read and store the hash of the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true + /// \param[in] removeUnknownFiles If a file does not exist on disk but is in the file list, remove it from the file list? + void PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles); + + /// By default, GetDeltaToCurrent tags files as non-references, meaning they are assumed to be populated later + /// This tags all files as references, required for IncrementalReadInterface to process them incrementally + void FlagFilesAsReferences(void); + + /// \brief Write all files to disk, prefixing the paths with applicationDirectory + /// \param[in] applicationDirectory path prefix + void WriteDataToDisk(const char *applicationDirectory); + + /// \brief Add a file, given data already in memory. + /// \param[in] filename Name of a file, optionally prefixed with a partial or complete path. Use \ as the path delineator. + /// \param[in] fullPathToFile Full path to the file on disk + /// \param[in] data Contents to write + /// \param[in] dataLength length of the data, which may be greater than fileLength should you prefix extra data, such as the hash + /// \param[in] fileLength Length of the file + /// \param[in] context User defined byte to store with each file. Use for whatever you want. + /// \param[in] isAReference Means that this is just a reference to a file elsewhere - does not actually have any data + /// \param[in] takeDataPointer If true, do not allocate dataLength. Just take the pointer passed to the \a data parameter + void AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference=false, bool takeDataPointer=false); + + /// \brief Add a file, reading it from disk. + /// \param[in] filepath Complete path to the file, including the filename itself + /// \param[in] filename filename to store internally, anything you want, but usually either the complete path or a subset of the complete path. + /// \param[in] context User defined byte to store with each file. Use for whatever you want. + void AddFile(const char *filepath, const char *filename, FileListNodeContext context); + + /// \brief Delete all files stored in the file list. + /// \param[in] applicationDirectory Prefixed to the path to each filename. Use \ as the path delineator. + void DeleteFiles(const char *applicationDirectory); + + /// \brief Adds a callback to get progress reports about what the file list instances do. + /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. + void AddCallback(FileListProgress *cb); + + /// \brief Removes a callback + /// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback() + void RemoveCallback(FileListProgress *cb); + + /// \brief Removes all callbacks + void ClearCallbacks(void); + + /// Returns all callbacks added with AddCallback() + /// \param[out] callbacks The list is set to the list of callbacks + void GetCallbacks(DataStructures::List &callbacks); + + // Here so you can read it, but don't modify it + DataStructures::List fileList; + + static bool FixEndingSlash(char *str); +protected: + DataStructures::List fileListProgressCallbacks; +}; + +} // namespace RakNet + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + +#endif // _RAKNET_SUPPORT_FileOperations diff --git a/project/lib_projects/raknet/jni/RaknetSources/FileListNodeContext.h b/project/lib_projects/raknet/jni/RaknetSources/FileListNodeContext.h index c0fd2e7..6ec227a 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FileListNodeContext.h +++ b/project/lib_projects/raknet/jni/RaknetSources/FileListNodeContext.h @@ -1,39 +1,39 @@ -/// \file FileListNodeContext.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __FILE_LIST_NODE_CONTEXT_H -#define __FILE_LIST_NODE_CONTEXT_H - -#include "BitStream.h" - -struct FileListNodeContext -{ - FileListNodeContext() {dataPtr=0; dataLength=0;} - FileListNodeContext(unsigned char o, unsigned int f) : op(o), fileId(f) {dataPtr=0; dataLength=0;} - ~FileListNodeContext() {} - - unsigned char op; - unsigned int fileId; - void *dataPtr; - unsigned int dataLength; -}; - -inline RakNet::BitStream& operator<<(RakNet::BitStream& out, FileListNodeContext& in) -{ - out.Write(in.op); - out.Write(in.fileId); - return out; -} -inline RakNet::BitStream& operator>>(RakNet::BitStream& in, FileListNodeContext& out) -{ - in.Read(out.op); - bool success = in.Read(out.fileId); - (void) success; - assert(success); - return in; -} - -#endif +/// \file FileListNodeContext.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __FILE_LIST_NODE_CONTEXT_H +#define __FILE_LIST_NODE_CONTEXT_H + +#include "BitStream.h" + +struct FileListNodeContext +{ + FileListNodeContext() {dataPtr=0; dataLength=0;} + FileListNodeContext(unsigned char o, unsigned int f) : op(o), fileId(f) {dataPtr=0; dataLength=0;} + ~FileListNodeContext() {} + + unsigned char op; + unsigned int fileId; + void *dataPtr; + unsigned int dataLength; +}; + +inline RakNet::BitStream& operator<<(RakNet::BitStream& out, FileListNodeContext& in) +{ + out.Write(in.op); + out.Write(in.fileId); + return out; +} +inline RakNet::BitStream& operator>>(RakNet::BitStream& in, FileListNodeContext& out) +{ + in.Read(out.op); + bool success = in.Read(out.fileId); + (void) success; + assert(success); + return in; +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/FileListTransfer.cpp b/project/lib_projects/raknet/jni/RaknetSources/FileListTransfer.cpp index 887fb7e..11fbe5f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FileListTransfer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/FileListTransfer.cpp @@ -1,1086 +1,1086 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FileListTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 - -#include "FileListTransfer.h" -#include "DS_HuffmanEncodingTree.h" -#include "FileListTransferCBInterface.h" -#include "StringCompressor.h" -#include "FileList.h" -#include "DS_Queue.h" -#include "MessageIdentifiers.h" -#include "RakNetTypes.h" -#include "RakPeerInterface.h" -#include "RakNetStatistics.h" -#include "IncrementalReadInterface.h" -#include "RakAssert.h" -#include "RakAlloca.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -namespace RakNet -{ - -struct FLR_MemoryBlock -{ - char *flrMemoryBlock; -}; - -struct FileListReceiver -{ - FileListReceiver(); - ~FileListReceiver(); - FileListTransferCBInterface *downloadHandler; - SystemAddress allowedSender; - unsigned short setID; - unsigned setCount; - unsigned setTotalCompressedTransmissionLength; - unsigned setTotalFinalLength; - unsigned setTotalDownloadedLength; - bool gotSetHeader; - bool deleteDownloadHandler; - bool isCompressed; - int filesReceived; - DataStructures::Map pushedFiles; - - // Notifications - unsigned int partLength; - -}; - -} // namespace RakNet - -using namespace RakNet; - -FileListReceiver::FileListReceiver() {filesReceived=0; setTotalDownloadedLength=0; partLength=1; DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON();} -FileListReceiver::~FileListReceiver() { - unsigned int i=0; - for (i=0; i < pushedFiles.Size(); i++) - rakFree_Ex(pushedFiles[i].flrMemoryBlock, _FILE_AND_LINE_ ); -} - -STATIC_FACTORY_DEFINITIONS(FileListTransfer,FileListTransfer) - -void FileListTransfer::FileToPushRecipient::DeleteThis(void) -{ - for (unsigned int j=0; j < filesToPush.Size(); j++) - RakNet::OP_DELETE(filesToPush[j],_FILE_AND_LINE_); - RakNet::OP_DELETE(this,_FILE_AND_LINE_); -} -void FileListTransfer::FileToPushRecipient::AddRef(void) -{ - refCountMutex.Lock(); - ++refCount; - refCountMutex.Unlock(); -} -void FileListTransfer::FileToPushRecipient::Deref(void) -{ - refCountMutex.Lock(); - --refCount; - if (refCount==0) - { - refCountMutex.Unlock(); - DeleteThis(); - return; - } - refCountMutex.Unlock(); -} -FileListTransfer::FileListTransfer() -{ - setId=0; - DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON(); -} -FileListTransfer::~FileListTransfer() -{ - threadPool.StopThreads(); - Clear(); -} -void FileListTransfer::StartIncrementalReadThreads(int numThreads, int threadPriority) -{ - (void) threadPriority; - - threadPool.StartThreads(numThreads, 0); -} -unsigned short FileListTransfer::SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender) -{ - if (rakPeerInterface && rakPeerInterface->GetConnectionState(allowedSender)!=IS_CONNECTED) - return (unsigned short)-1; - FileListReceiver *receiver; - - if (fileListReceivers.Has(setId)) - { - receiver=fileListReceivers.Get(setId); - receiver->downloadHandler->OnDereference(); - if (receiver->deleteDownloadHandler) - RakNet::OP_DELETE(receiver->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(receiver, _FILE_AND_LINE_); - fileListReceivers.Delete(setId); - } - - unsigned short oldId; - receiver = RakNet::OP_NEW( _FILE_AND_LINE_ ); - RakAssert(handler); - receiver->downloadHandler=handler; - receiver->allowedSender=allowedSender; - receiver->gotSetHeader=false; - receiver->deleteDownloadHandler=deleteHandler; - receiver->setID=setId; - fileListReceivers.Set(setId, receiver); - oldId=setId; - if (++setId==(unsigned short)-1) - setId=0; - return oldId; -} - -void FileListTransfer::Send(FileList *fileList, RakNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) -{ - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileList->AddCallback(fileListProgressCallbacks[flpcIndex]); - - unsigned int i, totalLength; - RakNet::BitStream outBitstream; - bool sendReference; - const char *dataBlocks[2]; - int lengths[2]; - totalLength=0; - for (i=0; i < fileList->fileList.Size(); i++) - { - const FileListNode &fileListNode = fileList->fileList[i]; - totalLength+=fileListNode.fileLengthBytes; - } - - // Write the chunk header, which contains the frequency table, the total number of files, and the total number of bytes - bool anythingToWrite; - outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_HEADER); - outBitstream.Write(setID); - anythingToWrite=fileList->fileList.Size()>0; - outBitstream.Write(anythingToWrite); - if (anythingToWrite) - { - outBitstream.WriteCompressed(fileList->fileList.Size()); - outBitstream.WriteCompressed(totalLength); - - if (rakPeer) - rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - else - SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - - DataStructures::Queue filesToPush; - - for (i=0; i < fileList->fileList.Size(); i++) - { - sendReference = fileList->fileList[i].isAReference && _incrementalReadInterface!=0; - if (sendReference) - { - FileToPush *fileToPush = RakNet::OP_NEW(_FILE_AND_LINE_); - fileToPush->fileListNode.context=fileList->fileList[i].context; - fileToPush->setIndex=i; - fileToPush->fileListNode.filename=fileList->fileList[i].filename; - fileToPush->fileListNode.fullPathToFile=fileList->fileList[i].fullPathToFile; - fileToPush->fileListNode.fileLengthBytes=fileList->fileList[i].fileLengthBytes; - fileToPush->fileListNode.dataLengthBytes=fileList->fileList[i].dataLengthBytes; - // fileToPush->systemAddress=recipient; - fileToPush->setID=setID; - fileToPush->packetPriority=priority; - fileToPush->orderingChannel=orderingChannel; - fileToPush->currentOffset=0; - fileToPush->incrementalReadInterface=_incrementalReadInterface; - fileToPush->chunkSize=_chunkSize; - filesToPush.Push(fileToPush,_FILE_AND_LINE_); - } - else - { - outBitstream.Reset(); - outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); - outBitstream << fileList->fileList[i].context; - // outBitstream.Write(fileList->fileList[i].context); - outBitstream.Write(setID); - StringCompressor::Instance()->EncodeString(fileList->fileList[i].filename, 512, &outBitstream); - - outBitstream.WriteCompressed(i); - outBitstream.WriteCompressed(fileList->fileList[i].dataLengthBytes); // Original length in bytes - - outBitstream.AlignWriteToByteBoundary(); - - dataBlocks[0]=(char*) outBitstream.GetData(); - lengths[0]=outBitstream.GetNumberOfBytesUsed(); - dataBlocks[1]=fileList->fileList[i].data; - lengths[1]=fileList->fileList[i].dataLengthBytes; - SendListUnified(dataBlocks,lengths,2,priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - } - } - - if (filesToPush.IsEmpty()==false) - { - FileToPushRecipient *ftpr=0; - - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - if (fileToPushRecipientList[i]->systemAddress==recipient) - { - ftpr=fileToPushRecipientList[i]; - ftpr->AddRef(); - break; - } - } - fileToPushRecipientListMutex.Unlock(); - - if (ftpr==0) - { - ftpr = RakNet::OP_NEW(_FILE_AND_LINE_); - ftpr->systemAddress=recipient; - ftpr->refCount=2; // Allocated and in the list - fileToPushRecipientList.Push(ftpr, _FILE_AND_LINE_); - } - while (filesToPush.IsEmpty()==false) - { - ftpr->filesToPush.Push(filesToPush.Pop(), _FILE_AND_LINE_); - } - // ftpr out of scope - ftpr->Deref(); - SendIRIToAddress(recipient); - return; - } - else - { - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID); - } - } - else - { - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID); - - if (rakPeer) - rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - else - SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - } -} - -bool FileListTransfer::DecodeSetHeader(Packet *packet) -{ - bool anythingToWrite=false; - unsigned short setID; - RakNet::BitStream inBitStream(packet->data, packet->length, false); - inBitStream.IgnoreBits(8); - inBitStream.Read(setID); - FileListReceiver *fileListReceiver; - if (fileListReceivers.Has(setID)==false) - { - // If this assert hits you didn't call SetupReceive -#ifdef _DEBUG - RakAssert(0); -#endif - return false; - } - fileListReceiver=fileListReceivers.Get(setID); - if (fileListReceiver->allowedSender!=packet->systemAddress) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return false; - } - -#ifdef _DEBUG - RakAssert(fileListReceiver->gotSetHeader==false); -#endif - - inBitStream.Read(anythingToWrite); - - if (anythingToWrite) - { - inBitStream.ReadCompressed(fileListReceiver->setCount); - if (inBitStream.ReadCompressed(fileListReceiver->setTotalFinalLength)) - { - fileListReceiver->setTotalCompressedTransmissionLength=fileListReceiver->setTotalFinalLength; - fileListReceiver->gotSetHeader=true; - return true; - } - - } - else - { - FileListTransferCBInterface::DownloadCompleteStruct dcs; - dcs.setID=fileListReceiver->setID; - dcs.numberOfFilesInThisSet=fileListReceiver->setCount; - dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - dcs.senderSystemAddress=packet->systemAddress; - dcs.senderGuid=packet->guid; - - if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) - { - fileListReceiver->downloadHandler->OnDereference(); - fileListReceivers.Delete(setID); - if (fileListReceiver->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); - } - - return true; - } - - return false; -} - -bool FileListTransfer::DecodeFile(Packet *packet, bool isTheFileAndIsNotDownloadProgress) -{ - FileListTransferCBInterface::OnFileStruct onFileStruct; - RakNet::BitStream inBitStream(packet->data, packet->length, false); - inBitStream.IgnoreBits(8); - - onFileStruct.senderSystemAddress=packet->systemAddress; - onFileStruct.senderGuid=packet->guid; - - unsigned int partCount=0; - unsigned int partTotal=0; - unsigned int partLength=0; - onFileStruct.fileData=0; - if (isTheFileAndIsNotDownloadProgress==false) - { - // Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp - inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true ); - inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true ); - inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true ); - inBitStream.IgnoreBits(8); - // The header is appended to every chunk, which we continue to read after this statement flrMemoryBlock - } - inBitStream >> onFileStruct.context; - // inBitStream.Read(onFileStruct.context); - inBitStream.Read(onFileStruct.setID); - FileListReceiver *fileListReceiver; - if (fileListReceivers.Has(onFileStruct.setID)==false) - { - return false; - } - fileListReceiver=fileListReceivers.Get(onFileStruct.setID); - if (fileListReceiver->allowedSender!=packet->systemAddress) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return false; - } - -#ifdef _DEBUG - RakAssert(fileListReceiver->gotSetHeader==true); -#endif - - if (StringCompressor::Instance()->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return false; - } - - inBitStream.ReadCompressed(onFileStruct.fileIndex); - inBitStream.ReadCompressed(onFileStruct.byteLengthOfThisFile); - onFileStruct.bytesDownloadedForThisFile=onFileStruct.byteLengthOfThisFile; - - if (isTheFileAndIsNotDownloadProgress) - { - // Support SendLists - inBitStream.AlignReadToByteBoundary(); - - onFileStruct.fileData = (char*) rakMalloc_Ex( (size_t) onFileStruct.byteLengthOfThisFile, _FILE_AND_LINE_ ); - - inBitStream.Read((char*)onFileStruct.fileData, onFileStruct.byteLengthOfThisFile); - - fileListReceiver->setTotalDownloadedLength+=onFileStruct.byteLengthOfThisFile; - } - - - onFileStruct.numberOfFilesInThisSet=fileListReceiver->setCount; -// onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength; - onFileStruct.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - - // User callback for this file. - if (isTheFileAndIsNotDownloadProgress) - { - onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength; - - FileListTransferCBInterface::FileProgressStruct fps; - fps.onFileStruct=&onFileStruct; - fps.partCount=1; - fps.partTotal=1; - fps.dataChunkLength=onFileStruct.byteLengthOfThisFile; - fps.firstDataChunk=onFileStruct.fileData; - fps.iriDataChunk=onFileStruct.fileData; - fps.allocateIrIDataChunkAutomatically=true; - fps.iriWriteOffset=0; - fps.senderSystemAddress=packet->systemAddress; - fps.senderGuid=packet->guid; - fileListReceiver->downloadHandler->OnFileProgress(&fps); - - // Got a complete file - // Either we are using IncrementalReadInterface and it was a small file or - // We are not using IncrementalReadInterface - if (fileListReceiver->downloadHandler->OnFile(&onFileStruct)) - rakFree_Ex(onFileStruct.fileData, _FILE_AND_LINE_ ); - - fileListReceiver->filesReceived++; - - // If this set is done, free the memory for it. - if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived) - { - FileListTransferCBInterface::DownloadCompleteStruct dcs; - dcs.setID=fileListReceiver->setID; - dcs.numberOfFilesInThisSet=fileListReceiver->setCount; - dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - dcs.senderSystemAddress=packet->systemAddress; - dcs.senderGuid=packet->guid; - - if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) - { - fileListReceiver->downloadHandler->OnDereference(); - if (fileListReceiver->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); - fileListReceivers.Delete(onFileStruct.setID); - RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); - } - } - - } - else - { - inBitStream.AlignReadToByteBoundary(); - - char *firstDataChunk; - unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits(); - unsigned int unreadBytes = BITS_TO_BYTES(unreadBits); - firstDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); - - onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength+unreadBytes; - onFileStruct.bytesDownloadedForThisFile=onFileStruct.byteLengthOfThisFile; - - FileListTransferCBInterface::FileProgressStruct fps; - fps.onFileStruct=&onFileStruct; - fps.partCount=partCount; - fps.partTotal=partTotal; - fps.dataChunkLength=unreadBytes; - fps.firstDataChunk=firstDataChunk; - fps.iriDataChunk=0; - fps.allocateIrIDataChunkAutomatically=true; - fps.iriWriteOffset=0; - fps.senderSystemAddress=packet->systemAddress; - fps.senderGuid=packet->guid; - - // Remote system is sending a complete file, but the file is large enough that we get ID_PROGRESS_NOTIFICATION from the transport layer - fileListReceiver->downloadHandler->OnFileProgress(&fps); - - } - - return true; -} -PluginReceiveResult FileListTransfer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_FILE_LIST_TRANSFER_HEADER: - DecodeSetHeader(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FILE_LIST_TRANSFER_FILE: - DecodeFile(packet, true); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FILE_LIST_REFERENCE_PUSH: - OnReferencePush(packet, true); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FILE_LIST_REFERENCE_PUSH_ACK: - OnReferencePushAck(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_DOWNLOAD_PROGRESS: - if (packet->length>sizeof(MessageID)+sizeof(unsigned int)*3) - { - if (packet->data[sizeof(MessageID)+sizeof(unsigned int)*3]==ID_FILE_LIST_TRANSFER_FILE) - { - DecodeFile(packet, false); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - if (packet->data[sizeof(MessageID)+sizeof(unsigned int)*3]==ID_FILE_LIST_REFERENCE_PUSH) - { - OnReferencePush(packet, false); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - break; - } - - return RR_CONTINUE_PROCESSING; -} -void FileListTransfer::OnRakPeerShutdown(void) -{ - threadPool.StopThreads(); - threadPool.ClearInput(); - Clear(); -} -void FileListTransfer::Clear(void) -{ - unsigned i; - for (i=0; i < fileListReceivers.Size(); i++) - { - fileListReceivers[i]->downloadHandler->OnDereference(); - if (fileListReceivers[i]->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); - } - fileListReceivers.Clear(); - - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - FileToPushRecipient *ftpr = fileToPushRecipientList[i]; - // Taken out of the list - ftpr->Deref(); - } - fileToPushRecipientList.Clear(false,_FILE_AND_LINE_); - fileToPushRecipientListMutex.Unlock(); - - //filesToPush.Clear(false, _FILE_AND_LINE_); -} -void FileListTransfer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - RemoveReceiver(systemAddress); -} -void FileListTransfer::CancelReceive(unsigned short setId) -{ - if (fileListReceivers.Has(setId)==false) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return; - } - FileListReceiver *fileListReceiver=fileListReceivers.Get(setId); - fileListReceiver->downloadHandler->OnDereference(); - if (fileListReceiver->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); - fileListReceivers.Delete(setId); -} -void FileListTransfer::RemoveReceiver(SystemAddress systemAddress) -{ - unsigned i; - i=0; - threadPool.LockInput(); - while (i < threadPool.InputSize()) - { - if (threadPool.GetInputAtIndex(i).systemAddress==systemAddress) - { - threadPool.RemoveInputAtIndex(i); - } - else - i++; - } - threadPool.UnlockInput(); - - i=0; - while (i < fileListReceivers.Size()) - { - if (fileListReceivers[i]->allowedSender==systemAddress) - { - fileListReceivers[i]->downloadHandler->OnDereference(); - if (fileListReceivers[i]->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); - fileListReceivers.RemoveAtIndex(i); - } - else - i++; - } - - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - if (fileToPushRecipientList[i]->systemAddress==systemAddress) - { - FileToPushRecipient *ftpr = fileToPushRecipientList[i]; - - // Tell the user that this recipient was lost - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnSendAborted(ftpr->systemAddress); - - fileToPushRecipientList.RemoveAtIndex(i); - // Taken out of the list - ftpr->Deref(); - break; - } - } - fileToPushRecipientListMutex.Unlock(); -} -bool FileListTransfer::IsHandlerActive(unsigned short setId) -{ - return fileListReceivers.Has(setId); -} -void FileListTransfer::AddCallback(FileListProgress *cb) -{ - if (cb==0) - return; - - if (fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int) -1) - fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_); -} -void FileListTransfer::RemoveCallback(FileListProgress *cb) -{ - unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb); - if (idx!=(unsigned int) -1) - fileListProgressCallbacks.RemoveAtIndex(idx); -} -void FileListTransfer::ClearCallbacks(void) -{ - fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_); -} -void FileListTransfer::GetCallbacks(DataStructures::List &callbacks) -{ - callbacks = fileListProgressCallbacks; -} - -void FileListTransfer::Update(void) -{ - unsigned i; - i=0; - while (i < fileListReceivers.Size()) - { - if (fileListReceivers[i]->downloadHandler->Update()==false) - { - fileListReceivers[i]->downloadHandler->OnDereference(); - if (fileListReceivers[i]->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); - fileListReceivers.RemoveAtIndex(i); - } - else - i++; - } -} -void FileListTransfer::OnReferencePush(Packet *packet, bool isTheFileAndIsNotDownloadProgress) -{ - RakNet::BitStream refPushAck; - if (isTheFileAndIsNotDownloadProgress) - { - // This is not a progress notification, it is actually the entire packet - refPushAck.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH_ACK); - SendUnified(&refPushAck,HIGH_PRIORITY, RELIABLE, 0, packet->systemAddress, false); - } - else - { - // 12/23/09 Why do I care about ID_DOWNLOAD_PROGRESS for reference pushes? - return; - } - - FileListTransferCBInterface::OnFileStruct onFileStruct; - RakNet::BitStream inBitStream(packet->data, packet->length, false); - inBitStream.IgnoreBits(8); - - unsigned int partCount=0; - unsigned int partTotal=1; - unsigned int partLength=0; - onFileStruct.fileData=0; - if (isTheFileAndIsNotDownloadProgress==false) - { - // UNREACHABLE CODE - // Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp - inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true ); - inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true ); - inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true ); - inBitStream.IgnoreBits(8); - // The header is appended to every chunk, which we continue to read after this statement flrMemoryBlock - } - - inBitStream >> onFileStruct.context; - // inBitStream.Read(onFileStruct.context); - inBitStream.Read(onFileStruct.setID); - FileListReceiver *fileListReceiver; - if (fileListReceivers.Has(onFileStruct.setID)==false) - { - return; - } - fileListReceiver=fileListReceivers.Get(onFileStruct.setID); - if (fileListReceiver->allowedSender!=packet->systemAddress) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return; - } - -#ifdef _DEBUG - RakAssert(fileListReceiver->gotSetHeader==true); -#endif - - if (StringCompressor::Instance()->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return; - } - - inBitStream.ReadCompressed(onFileStruct.fileIndex); - inBitStream.ReadCompressed(onFileStruct.byteLengthOfThisFile); - unsigned int offset; - unsigned int chunkLength; - inBitStream.ReadCompressed(offset); - inBitStream.ReadCompressed(chunkLength); - - bool lastChunk=false; - inBitStream.Read(lastChunk); - bool finished = lastChunk && isTheFileAndIsNotDownloadProgress; - - if (isTheFileAndIsNotDownloadProgress==false) - fileListReceiver->partLength=partLength; - - FLR_MemoryBlock mb; - if (fileListReceiver->pushedFiles.Has(onFileStruct.fileIndex)==false) - { - mb.flrMemoryBlock=(char*) rakMalloc_Ex(onFileStruct.byteLengthOfThisFile, _FILE_AND_LINE_); - fileListReceiver->pushedFiles.SetNew(onFileStruct.fileIndex, mb); - } - else - { - mb=fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex); - } - - unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits(); - unsigned int unreadBytes = BITS_TO_BYTES(unreadBits); - unsigned int amountToRead; - if (isTheFileAndIsNotDownloadProgress) - amountToRead=chunkLength; - else - amountToRead=unreadBytes; - - inBitStream.AlignReadToByteBoundary(); - - FileListTransferCBInterface::FileProgressStruct fps; - - if (isTheFileAndIsNotDownloadProgress) - { - if (mb.flrMemoryBlock) - { - // Either the very first block, or a subsequent block and allocateIrIDataChunkAutomatically was true for the first block - memcpy(mb.flrMemoryBlock+offset, inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()), amountToRead); - fps.iriDataChunk=mb.flrMemoryBlock+offset; - } - else - { - // In here mb.flrMemoryBlock is null - // This means the first block explicitly deallocated the memory, and no blocks will be permanently held by RakNet - fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); - } - - onFileStruct.bytesDownloadedForThisFile=offset+amountToRead; - } - else - { - fileListReceiver->setTotalDownloadedLength+=partLength; - onFileStruct.bytesDownloadedForThisFile=partCount*partLength; - fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); - } - onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength; - - onFileStruct.numberOfFilesInThisSet=fileListReceiver->setCount; -// onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength; - onFileStruct.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - // Note: mb.flrMemoryBlock may be null here - onFileStruct.fileData=mb.flrMemoryBlock; - onFileStruct.senderSystemAddress=packet->systemAddress; - onFileStruct.senderGuid=packet->guid; - - unsigned int totalNotifications; - unsigned int currentNotificationIndex; - if (chunkLength==0 || chunkLength==onFileStruct.byteLengthOfThisFile) - totalNotifications=1; - else - totalNotifications = onFileStruct.byteLengthOfThisFile / chunkLength + 1; - - if (chunkLength==0) - currentNotificationIndex = 0; - else - currentNotificationIndex = offset / chunkLength; - - fps.onFileStruct=&onFileStruct; - fps.partCount=currentNotificationIndex; - fps.partTotal=totalNotifications; - fps.dataChunkLength=amountToRead; - fps.firstDataChunk=mb.flrMemoryBlock; - fps.allocateIrIDataChunkAutomatically=true; - fps.onFileStruct->fileData=mb.flrMemoryBlock; - fps.iriWriteOffset=offset; - fps.senderSystemAddress=packet->systemAddress; - fps.senderGuid=packet->guid; - - if (finished) - { - char *oldFileData=fps.onFileStruct->fileData; - if (fps.partCount==0) - fps.firstDataChunk=fps.iriDataChunk; - if (fps.partTotal==1) - fps.onFileStruct->fileData=fps.iriDataChunk; - fileListReceiver->downloadHandler->OnFileProgress(&fps); - - // Incremental read interface sent us a file chunk - // This is the last file chunk we were waiting for to consider the file done - if (fileListReceiver->downloadHandler->OnFile(&onFileStruct)) - rakFree_Ex(oldFileData, _FILE_AND_LINE_ ); - fileListReceiver->pushedFiles.Delete(onFileStruct.fileIndex); - - fileListReceiver->filesReceived++; - - // If this set is done, free the memory for it. - if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived) - { - FileListTransferCBInterface::DownloadCompleteStruct dcs; - dcs.setID=fileListReceiver->setID; - dcs.numberOfFilesInThisSet=fileListReceiver->setCount; - dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - dcs.senderSystemAddress=packet->systemAddress; - dcs.senderGuid=packet->guid; - - if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) - { - fileListReceiver->downloadHandler->OnDereference(); - fileListReceivers.Delete(onFileStruct.setID); - if (fileListReceiver->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); - } - } - } - else - { - if (isTheFileAndIsNotDownloadProgress) - { - // 12/23/09 Don't use OnReferencePush anymore, just use OnFileProgress - fileListReceiver->downloadHandler->OnFileProgress(&fps); - - if (fps.allocateIrIDataChunkAutomatically==false) - { - rakFree_Ex(fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock, _FILE_AND_LINE_ ); - fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock=0; - } - } - else - { - // This is a download progress notification for a file chunk using incremental read interface - // We don't have all the data for this chunk yet - - // UNREACHABLE CODE - totalNotifications = onFileStruct.byteLengthOfThisFile / fileListReceiver->partLength + 1; - if (isTheFileAndIsNotDownloadProgress==false) - currentNotificationIndex = (offset+partCount*fileListReceiver->partLength) / fileListReceiver->partLength ; - else - currentNotificationIndex = (offset+chunkLength) / fileListReceiver->partLength ; - unreadBytes = onFileStruct.byteLengthOfThisFile - ((currentNotificationIndex+1) * fileListReceiver->partLength); - - if (rakPeerInterface) - { - // Thus chunk is incomplete - fps.iriDataChunk=0; - - fileListReceiver->downloadHandler->OnFileProgress(&fps); - } - } - } - - return; -} -namespace RakNet -{ -int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData) -{ - (void) perThreadData; - - FileListTransfer *fileListTransfer = threadData.fileListTransfer; - SystemAddress systemAddress = threadData.systemAddress; - *returnOutput=false; - - // Was previously using GetStatistics to get outgoing buffer size, but TCP with UnifiedSend doesn't have this - unsigned int bytesRead; - const char *dataBlocks[2]; - int lengths[2]; - unsigned int smallFileTotalSize=0; - RakNet::BitStream outBitstream; - unsigned int ftpIndex; - - fileListTransfer->fileToPushRecipientListMutex.Lock(); - for (ftpIndex=0; ftpIndex < fileListTransfer->fileToPushRecipientList.Size(); ftpIndex++) - { - FileListTransfer::FileToPushRecipient *ftpr = fileListTransfer->fileToPushRecipientList[ftpIndex]; - // Referenced by both ftpr and list - ftpr->AddRef(); - - fileListTransfer->fileToPushRecipientListMutex.Unlock(); - - if (ftpr->systemAddress==systemAddress) - { - FileListTransfer::FileToPush *ftp = ftpr->filesToPush.Peek(); - - // Read and send chunk. If done, delete at this index - void *buff = rakMalloc_Ex(ftp->chunkSize, _FILE_AND_LINE_); - if (buff==0) - { - ftpr->Deref(); - notifyOutOfMemory(_FILE_AND_LINE_); - return 0; - } - - // Read the next file chunk - bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context); - - bool done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead; - while (done && ftp->currentOffset==0 && ftpr->filesToPush.Size()>=2 && smallFileTotalSizechunkSize) - { - // Send all small files at once, rather than wait for ID_FILE_LIST_REFERENCE_PUSH. But at least one ID_FILE_LIST_REFERENCE_PUSH must be sent - outBitstream.Reset(); - outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); - // outBitstream.Write(ftp->fileListNode.context); - outBitstream << ftp->fileListNode.context; - outBitstream.Write(ftp->setID); - StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream); - outBitstream.WriteCompressed(ftp->setIndex); - outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes - outBitstream.AlignWriteToByteBoundary(); - dataBlocks[0]=(char*) outBitstream.GetData(); - lengths[0]=outBitstream.GetNumberOfBytesUsed(); - dataBlocks[1]=(const char*) buff; - lengths[1]=bytesRead; - - fileListTransfer->SendListUnified(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, systemAddress, false); - - // LWS : fixed freed pointer reference -// unsigned int chunkSize = ftp->chunkSize; - RakNet::OP_DELETE(ftp,_FILE_AND_LINE_); - ftpr->filesToPush.Pop(); - smallFileTotalSize+=bytesRead; - //done = bytesRead!=ftp->chunkSize; - ftp = ftpr->filesToPush.Peek(); - - bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context); - done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead; - } - - - outBitstream.Reset(); - outBitstream.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH); - // outBitstream.Write(ftp->fileListNode.context); - outBitstream << ftp->fileListNode.context; - outBitstream.Write(ftp->setID); - StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream); - outBitstream.WriteCompressed(ftp->setIndex); - outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes - outBitstream.WriteCompressed(ftp->currentOffset); - ftp->currentOffset+=bytesRead; - outBitstream.WriteCompressed(bytesRead); - outBitstream.Write(done); - - for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++) - fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePush(ftp->fileListNode.filename, ftp->fileListNode.fileLengthBytes, ftp->currentOffset-bytesRead, bytesRead, done, systemAddress, ftp->setID); - - dataBlocks[0]=(char*) outBitstream.GetData(); - lengths[0]=outBitstream.GetNumberOfBytesUsed(); - dataBlocks[1]=(char*) buff; - lengths[1]=bytesRead; - //rakPeerInterface->SendList(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, ftp->systemAddress, false); - fileListTransfer->SendListUnified(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, systemAddress, false); - - // Mutex state: FileToPushRecipient (ftpr) has AddRef. fileToPushRecipientListMutex not locked. - if (done) - { - // Done - unsigned short setId = ftp->setID; - RakNet::OP_DELETE(ftp,_FILE_AND_LINE_); - ftpr->filesToPush.Pop(); - - if (ftpr->filesToPush.Size()==0) - { - for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++) - fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(systemAddress, setId); - - // Remove ftpr from fileToPushRecipientList - fileListTransfer->RemoveFromList(ftpr); - } - } - - // ftpr out of scope - ftpr->Deref(); - - rakFree_Ex(buff, _FILE_AND_LINE_ ); - return 0; - } - else - { - ftpr->Deref(); - fileListTransfer->fileToPushRecipientListMutex.Lock(); - } - } - - fileListTransfer->fileToPushRecipientListMutex.Unlock(); - - return 0; -} -} -void FileListTransfer::SendIRIToAddress(SystemAddress systemAddress) -{ - ThreadData threadData; - threadData.fileListTransfer=this; - threadData.systemAddress=systemAddress; - - if (threadPool.WasStarted()) - { - threadPool.AddInput(SendIRIToAddressCB, threadData); - } - else - { - bool doesNothing; - SendIRIToAddressCB(threadData, &doesNothing, 0); - } -} -void FileListTransfer::OnReferencePushAck(Packet *packet) -{ - SendIRIToAddress(packet->systemAddress); -} -void FileListTransfer::RemoveFromList(FileToPushRecipient *ftpr) -{ - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - if (fileToPushRecipientList[i]==ftpr) - { - fileToPushRecipientList.RemoveAtIndex(i); - // List no longer references - ftpr->Deref(); - fileToPushRecipientListMutex.Unlock(); - return; - } - } - fileToPushRecipientListMutex.Unlock(); -} -unsigned int FileListTransfer::GetPendingFilesToAddress(SystemAddress recipient) -{ - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - if (fileToPushRecipientList[i]->systemAddress==recipient) - { - unsigned int size = fileToPushRecipientList[i]->filesToPush.Size(); - fileToPushRecipientListMutex.Unlock(); - return size; - } - } - fileToPushRecipientListMutex.Unlock(); - - return 0; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FileListTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 + +#include "FileListTransfer.h" +#include "DS_HuffmanEncodingTree.h" +#include "FileListTransferCBInterface.h" +#include "StringCompressor.h" +#include "FileList.h" +#include "DS_Queue.h" +#include "MessageIdentifiers.h" +#include "RakNetTypes.h" +#include "RakPeerInterface.h" +#include "RakNetStatistics.h" +#include "IncrementalReadInterface.h" +#include "RakAssert.h" +#include "RakAlloca.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +namespace RakNet +{ + +struct FLR_MemoryBlock +{ + char *flrMemoryBlock; +}; + +struct FileListReceiver +{ + FileListReceiver(); + ~FileListReceiver(); + FileListTransferCBInterface *downloadHandler; + SystemAddress allowedSender; + unsigned short setID; + unsigned setCount; + unsigned setTotalCompressedTransmissionLength; + unsigned setTotalFinalLength; + unsigned setTotalDownloadedLength; + bool gotSetHeader; + bool deleteDownloadHandler; + bool isCompressed; + int filesReceived; + DataStructures::Map pushedFiles; + + // Notifications + unsigned int partLength; + +}; + +} // namespace RakNet + +using namespace RakNet; + +FileListReceiver::FileListReceiver() {filesReceived=0; setTotalDownloadedLength=0; partLength=1; DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON();} +FileListReceiver::~FileListReceiver() { + unsigned int i=0; + for (i=0; i < pushedFiles.Size(); i++) + rakFree_Ex(pushedFiles[i].flrMemoryBlock, _FILE_AND_LINE_ ); +} + +STATIC_FACTORY_DEFINITIONS(FileListTransfer,FileListTransfer) + +void FileListTransfer::FileToPushRecipient::DeleteThis(void) +{ + for (unsigned int j=0; j < filesToPush.Size(); j++) + RakNet::OP_DELETE(filesToPush[j],_FILE_AND_LINE_); + RakNet::OP_DELETE(this,_FILE_AND_LINE_); +} +void FileListTransfer::FileToPushRecipient::AddRef(void) +{ + refCountMutex.Lock(); + ++refCount; + refCountMutex.Unlock(); +} +void FileListTransfer::FileToPushRecipient::Deref(void) +{ + refCountMutex.Lock(); + --refCount; + if (refCount==0) + { + refCountMutex.Unlock(); + DeleteThis(); + return; + } + refCountMutex.Unlock(); +} +FileListTransfer::FileListTransfer() +{ + setId=0; + DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON(); +} +FileListTransfer::~FileListTransfer() +{ + threadPool.StopThreads(); + Clear(); +} +void FileListTransfer::StartIncrementalReadThreads(int numThreads, int threadPriority) +{ + (void) threadPriority; + + threadPool.StartThreads(numThreads, 0); +} +unsigned short FileListTransfer::SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender) +{ + if (rakPeerInterface && rakPeerInterface->GetConnectionState(allowedSender)!=IS_CONNECTED) + return (unsigned short)-1; + FileListReceiver *receiver; + + if (fileListReceivers.Has(setId)) + { + receiver=fileListReceivers.Get(setId); + receiver->downloadHandler->OnDereference(); + if (receiver->deleteDownloadHandler) + RakNet::OP_DELETE(receiver->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(receiver, _FILE_AND_LINE_); + fileListReceivers.Delete(setId); + } + + unsigned short oldId; + receiver = RakNet::OP_NEW( _FILE_AND_LINE_ ); + RakAssert(handler); + receiver->downloadHandler=handler; + receiver->allowedSender=allowedSender; + receiver->gotSetHeader=false; + receiver->deleteDownloadHandler=deleteHandler; + receiver->setID=setId; + fileListReceivers.Set(setId, receiver); + oldId=setId; + if (++setId==(unsigned short)-1) + setId=0; + return oldId; +} + +void FileListTransfer::Send(FileList *fileList, RakNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) +{ + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileList->AddCallback(fileListProgressCallbacks[flpcIndex]); + + unsigned int i, totalLength; + RakNet::BitStream outBitstream; + bool sendReference; + const char *dataBlocks[2]; + int lengths[2]; + totalLength=0; + for (i=0; i < fileList->fileList.Size(); i++) + { + const FileListNode &fileListNode = fileList->fileList[i]; + totalLength+=fileListNode.fileLengthBytes; + } + + // Write the chunk header, which contains the frequency table, the total number of files, and the total number of bytes + bool anythingToWrite; + outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_HEADER); + outBitstream.Write(setID); + anythingToWrite=fileList->fileList.Size()>0; + outBitstream.Write(anythingToWrite); + if (anythingToWrite) + { + outBitstream.WriteCompressed(fileList->fileList.Size()); + outBitstream.WriteCompressed(totalLength); + + if (rakPeer) + rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + else + SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + + DataStructures::Queue filesToPush; + + for (i=0; i < fileList->fileList.Size(); i++) + { + sendReference = fileList->fileList[i].isAReference && _incrementalReadInterface!=0; + if (sendReference) + { + FileToPush *fileToPush = RakNet::OP_NEW(_FILE_AND_LINE_); + fileToPush->fileListNode.context=fileList->fileList[i].context; + fileToPush->setIndex=i; + fileToPush->fileListNode.filename=fileList->fileList[i].filename; + fileToPush->fileListNode.fullPathToFile=fileList->fileList[i].fullPathToFile; + fileToPush->fileListNode.fileLengthBytes=fileList->fileList[i].fileLengthBytes; + fileToPush->fileListNode.dataLengthBytes=fileList->fileList[i].dataLengthBytes; + // fileToPush->systemAddress=recipient; + fileToPush->setID=setID; + fileToPush->packetPriority=priority; + fileToPush->orderingChannel=orderingChannel; + fileToPush->currentOffset=0; + fileToPush->incrementalReadInterface=_incrementalReadInterface; + fileToPush->chunkSize=_chunkSize; + filesToPush.Push(fileToPush,_FILE_AND_LINE_); + } + else + { + outBitstream.Reset(); + outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); + outBitstream << fileList->fileList[i].context; + // outBitstream.Write(fileList->fileList[i].context); + outBitstream.Write(setID); + StringCompressor::Instance()->EncodeString(fileList->fileList[i].filename, 512, &outBitstream); + + outBitstream.WriteCompressed(i); + outBitstream.WriteCompressed(fileList->fileList[i].dataLengthBytes); // Original length in bytes + + outBitstream.AlignWriteToByteBoundary(); + + dataBlocks[0]=(char*) outBitstream.GetData(); + lengths[0]=outBitstream.GetNumberOfBytesUsed(); + dataBlocks[1]=fileList->fileList[i].data; + lengths[1]=fileList->fileList[i].dataLengthBytes; + SendListUnified(dataBlocks,lengths,2,priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + } + } + + if (filesToPush.IsEmpty()==false) + { + FileToPushRecipient *ftpr=0; + + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + if (fileToPushRecipientList[i]->systemAddress==recipient) + { + ftpr=fileToPushRecipientList[i]; + ftpr->AddRef(); + break; + } + } + fileToPushRecipientListMutex.Unlock(); + + if (ftpr==0) + { + ftpr = RakNet::OP_NEW(_FILE_AND_LINE_); + ftpr->systemAddress=recipient; + ftpr->refCount=2; // Allocated and in the list + fileToPushRecipientList.Push(ftpr, _FILE_AND_LINE_); + } + while (filesToPush.IsEmpty()==false) + { + ftpr->filesToPush.Push(filesToPush.Pop(), _FILE_AND_LINE_); + } + // ftpr out of scope + ftpr->Deref(); + SendIRIToAddress(recipient); + return; + } + else + { + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID); + } + } + else + { + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID); + + if (rakPeer) + rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + else + SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + } +} + +bool FileListTransfer::DecodeSetHeader(Packet *packet) +{ + bool anythingToWrite=false; + unsigned short setID; + RakNet::BitStream inBitStream(packet->data, packet->length, false); + inBitStream.IgnoreBits(8); + inBitStream.Read(setID); + FileListReceiver *fileListReceiver; + if (fileListReceivers.Has(setID)==false) + { + // If this assert hits you didn't call SetupReceive +#ifdef _DEBUG + RakAssert(0); +#endif + return false; + } + fileListReceiver=fileListReceivers.Get(setID); + if (fileListReceiver->allowedSender!=packet->systemAddress) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return false; + } + +#ifdef _DEBUG + RakAssert(fileListReceiver->gotSetHeader==false); +#endif + + inBitStream.Read(anythingToWrite); + + if (anythingToWrite) + { + inBitStream.ReadCompressed(fileListReceiver->setCount); + if (inBitStream.ReadCompressed(fileListReceiver->setTotalFinalLength)) + { + fileListReceiver->setTotalCompressedTransmissionLength=fileListReceiver->setTotalFinalLength; + fileListReceiver->gotSetHeader=true; + return true; + } + + } + else + { + FileListTransferCBInterface::DownloadCompleteStruct dcs; + dcs.setID=fileListReceiver->setID; + dcs.numberOfFilesInThisSet=fileListReceiver->setCount; + dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + dcs.senderSystemAddress=packet->systemAddress; + dcs.senderGuid=packet->guid; + + if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) + { + fileListReceiver->downloadHandler->OnDereference(); + fileListReceivers.Delete(setID); + if (fileListReceiver->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); + } + + return true; + } + + return false; +} + +bool FileListTransfer::DecodeFile(Packet *packet, bool isTheFileAndIsNotDownloadProgress) +{ + FileListTransferCBInterface::OnFileStruct onFileStruct; + RakNet::BitStream inBitStream(packet->data, packet->length, false); + inBitStream.IgnoreBits(8); + + onFileStruct.senderSystemAddress=packet->systemAddress; + onFileStruct.senderGuid=packet->guid; + + unsigned int partCount=0; + unsigned int partTotal=0; + unsigned int partLength=0; + onFileStruct.fileData=0; + if (isTheFileAndIsNotDownloadProgress==false) + { + // Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp + inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true ); + inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true ); + inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true ); + inBitStream.IgnoreBits(8); + // The header is appended to every chunk, which we continue to read after this statement flrMemoryBlock + } + inBitStream >> onFileStruct.context; + // inBitStream.Read(onFileStruct.context); + inBitStream.Read(onFileStruct.setID); + FileListReceiver *fileListReceiver; + if (fileListReceivers.Has(onFileStruct.setID)==false) + { + return false; + } + fileListReceiver=fileListReceivers.Get(onFileStruct.setID); + if (fileListReceiver->allowedSender!=packet->systemAddress) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return false; + } + +#ifdef _DEBUG + RakAssert(fileListReceiver->gotSetHeader==true); +#endif + + if (StringCompressor::Instance()->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return false; + } + + inBitStream.ReadCompressed(onFileStruct.fileIndex); + inBitStream.ReadCompressed(onFileStruct.byteLengthOfThisFile); + onFileStruct.bytesDownloadedForThisFile=onFileStruct.byteLengthOfThisFile; + + if (isTheFileAndIsNotDownloadProgress) + { + // Support SendLists + inBitStream.AlignReadToByteBoundary(); + + onFileStruct.fileData = (char*) rakMalloc_Ex( (size_t) onFileStruct.byteLengthOfThisFile, _FILE_AND_LINE_ ); + + inBitStream.Read((char*)onFileStruct.fileData, onFileStruct.byteLengthOfThisFile); + + fileListReceiver->setTotalDownloadedLength+=onFileStruct.byteLengthOfThisFile; + } + + + onFileStruct.numberOfFilesInThisSet=fileListReceiver->setCount; +// onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength; + onFileStruct.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + + // User callback for this file. + if (isTheFileAndIsNotDownloadProgress) + { + onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength; + + FileListTransferCBInterface::FileProgressStruct fps; + fps.onFileStruct=&onFileStruct; + fps.partCount=1; + fps.partTotal=1; + fps.dataChunkLength=onFileStruct.byteLengthOfThisFile; + fps.firstDataChunk=onFileStruct.fileData; + fps.iriDataChunk=onFileStruct.fileData; + fps.allocateIrIDataChunkAutomatically=true; + fps.iriWriteOffset=0; + fps.senderSystemAddress=packet->systemAddress; + fps.senderGuid=packet->guid; + fileListReceiver->downloadHandler->OnFileProgress(&fps); + + // Got a complete file + // Either we are using IncrementalReadInterface and it was a small file or + // We are not using IncrementalReadInterface + if (fileListReceiver->downloadHandler->OnFile(&onFileStruct)) + rakFree_Ex(onFileStruct.fileData, _FILE_AND_LINE_ ); + + fileListReceiver->filesReceived++; + + // If this set is done, free the memory for it. + if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived) + { + FileListTransferCBInterface::DownloadCompleteStruct dcs; + dcs.setID=fileListReceiver->setID; + dcs.numberOfFilesInThisSet=fileListReceiver->setCount; + dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + dcs.senderSystemAddress=packet->systemAddress; + dcs.senderGuid=packet->guid; + + if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) + { + fileListReceiver->downloadHandler->OnDereference(); + if (fileListReceiver->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); + fileListReceivers.Delete(onFileStruct.setID); + RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); + } + } + + } + else + { + inBitStream.AlignReadToByteBoundary(); + + char *firstDataChunk; + unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits(); + unsigned int unreadBytes = BITS_TO_BYTES(unreadBits); + firstDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); + + onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength+unreadBytes; + onFileStruct.bytesDownloadedForThisFile=onFileStruct.byteLengthOfThisFile; + + FileListTransferCBInterface::FileProgressStruct fps; + fps.onFileStruct=&onFileStruct; + fps.partCount=partCount; + fps.partTotal=partTotal; + fps.dataChunkLength=unreadBytes; + fps.firstDataChunk=firstDataChunk; + fps.iriDataChunk=0; + fps.allocateIrIDataChunkAutomatically=true; + fps.iriWriteOffset=0; + fps.senderSystemAddress=packet->systemAddress; + fps.senderGuid=packet->guid; + + // Remote system is sending a complete file, but the file is large enough that we get ID_PROGRESS_NOTIFICATION from the transport layer + fileListReceiver->downloadHandler->OnFileProgress(&fps); + + } + + return true; +} +PluginReceiveResult FileListTransfer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_FILE_LIST_TRANSFER_HEADER: + DecodeSetHeader(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FILE_LIST_TRANSFER_FILE: + DecodeFile(packet, true); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FILE_LIST_REFERENCE_PUSH: + OnReferencePush(packet, true); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FILE_LIST_REFERENCE_PUSH_ACK: + OnReferencePushAck(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_DOWNLOAD_PROGRESS: + if (packet->length>sizeof(MessageID)+sizeof(unsigned int)*3) + { + if (packet->data[sizeof(MessageID)+sizeof(unsigned int)*3]==ID_FILE_LIST_TRANSFER_FILE) + { + DecodeFile(packet, false); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + if (packet->data[sizeof(MessageID)+sizeof(unsigned int)*3]==ID_FILE_LIST_REFERENCE_PUSH) + { + OnReferencePush(packet, false); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + break; + } + + return RR_CONTINUE_PROCESSING; +} +void FileListTransfer::OnRakPeerShutdown(void) +{ + threadPool.StopThreads(); + threadPool.ClearInput(); + Clear(); +} +void FileListTransfer::Clear(void) +{ + unsigned i; + for (i=0; i < fileListReceivers.Size(); i++) + { + fileListReceivers[i]->downloadHandler->OnDereference(); + if (fileListReceivers[i]->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); + } + fileListReceivers.Clear(); + + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + FileToPushRecipient *ftpr = fileToPushRecipientList[i]; + // Taken out of the list + ftpr->Deref(); + } + fileToPushRecipientList.Clear(false,_FILE_AND_LINE_); + fileToPushRecipientListMutex.Unlock(); + + //filesToPush.Clear(false, _FILE_AND_LINE_); +} +void FileListTransfer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + RemoveReceiver(systemAddress); +} +void FileListTransfer::CancelReceive(unsigned short setId) +{ + if (fileListReceivers.Has(setId)==false) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return; + } + FileListReceiver *fileListReceiver=fileListReceivers.Get(setId); + fileListReceiver->downloadHandler->OnDereference(); + if (fileListReceiver->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); + fileListReceivers.Delete(setId); +} +void FileListTransfer::RemoveReceiver(SystemAddress systemAddress) +{ + unsigned i; + i=0; + threadPool.LockInput(); + while (i < threadPool.InputSize()) + { + if (threadPool.GetInputAtIndex(i).systemAddress==systemAddress) + { + threadPool.RemoveInputAtIndex(i); + } + else + i++; + } + threadPool.UnlockInput(); + + i=0; + while (i < fileListReceivers.Size()) + { + if (fileListReceivers[i]->allowedSender==systemAddress) + { + fileListReceivers[i]->downloadHandler->OnDereference(); + if (fileListReceivers[i]->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); + fileListReceivers.RemoveAtIndex(i); + } + else + i++; + } + + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + if (fileToPushRecipientList[i]->systemAddress==systemAddress) + { + FileToPushRecipient *ftpr = fileToPushRecipientList[i]; + + // Tell the user that this recipient was lost + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnSendAborted(ftpr->systemAddress); + + fileToPushRecipientList.RemoveAtIndex(i); + // Taken out of the list + ftpr->Deref(); + break; + } + } + fileToPushRecipientListMutex.Unlock(); +} +bool FileListTransfer::IsHandlerActive(unsigned short setId) +{ + return fileListReceivers.Has(setId); +} +void FileListTransfer::AddCallback(FileListProgress *cb) +{ + if (cb==0) + return; + + if (fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int) -1) + fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_); +} +void FileListTransfer::RemoveCallback(FileListProgress *cb) +{ + unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb); + if (idx!=(unsigned int) -1) + fileListProgressCallbacks.RemoveAtIndex(idx); +} +void FileListTransfer::ClearCallbacks(void) +{ + fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_); +} +void FileListTransfer::GetCallbacks(DataStructures::List &callbacks) +{ + callbacks = fileListProgressCallbacks; +} + +void FileListTransfer::Update(void) +{ + unsigned i; + i=0; + while (i < fileListReceivers.Size()) + { + if (fileListReceivers[i]->downloadHandler->Update()==false) + { + fileListReceivers[i]->downloadHandler->OnDereference(); + if (fileListReceivers[i]->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); + fileListReceivers.RemoveAtIndex(i); + } + else + i++; + } +} +void FileListTransfer::OnReferencePush(Packet *packet, bool isTheFileAndIsNotDownloadProgress) +{ + RakNet::BitStream refPushAck; + if (isTheFileAndIsNotDownloadProgress) + { + // This is not a progress notification, it is actually the entire packet + refPushAck.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH_ACK); + SendUnified(&refPushAck,HIGH_PRIORITY, RELIABLE, 0, packet->systemAddress, false); + } + else + { + // 12/23/09 Why do I care about ID_DOWNLOAD_PROGRESS for reference pushes? + return; + } + + FileListTransferCBInterface::OnFileStruct onFileStruct; + RakNet::BitStream inBitStream(packet->data, packet->length, false); + inBitStream.IgnoreBits(8); + + unsigned int partCount=0; + unsigned int partTotal=1; + unsigned int partLength=0; + onFileStruct.fileData=0; + if (isTheFileAndIsNotDownloadProgress==false) + { + // UNREACHABLE CODE + // Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp + inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true ); + inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true ); + inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true ); + inBitStream.IgnoreBits(8); + // The header is appended to every chunk, which we continue to read after this statement flrMemoryBlock + } + + inBitStream >> onFileStruct.context; + // inBitStream.Read(onFileStruct.context); + inBitStream.Read(onFileStruct.setID); + FileListReceiver *fileListReceiver; + if (fileListReceivers.Has(onFileStruct.setID)==false) + { + return; + } + fileListReceiver=fileListReceivers.Get(onFileStruct.setID); + if (fileListReceiver->allowedSender!=packet->systemAddress) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return; + } + +#ifdef _DEBUG + RakAssert(fileListReceiver->gotSetHeader==true); +#endif + + if (StringCompressor::Instance()->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return; + } + + inBitStream.ReadCompressed(onFileStruct.fileIndex); + inBitStream.ReadCompressed(onFileStruct.byteLengthOfThisFile); + unsigned int offset; + unsigned int chunkLength; + inBitStream.ReadCompressed(offset); + inBitStream.ReadCompressed(chunkLength); + + bool lastChunk=false; + inBitStream.Read(lastChunk); + bool finished = lastChunk && isTheFileAndIsNotDownloadProgress; + + if (isTheFileAndIsNotDownloadProgress==false) + fileListReceiver->partLength=partLength; + + FLR_MemoryBlock mb; + if (fileListReceiver->pushedFiles.Has(onFileStruct.fileIndex)==false) + { + mb.flrMemoryBlock=(char*) rakMalloc_Ex(onFileStruct.byteLengthOfThisFile, _FILE_AND_LINE_); + fileListReceiver->pushedFiles.SetNew(onFileStruct.fileIndex, mb); + } + else + { + mb=fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex); + } + + unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits(); + unsigned int unreadBytes = BITS_TO_BYTES(unreadBits); + unsigned int amountToRead; + if (isTheFileAndIsNotDownloadProgress) + amountToRead=chunkLength; + else + amountToRead=unreadBytes; + + inBitStream.AlignReadToByteBoundary(); + + FileListTransferCBInterface::FileProgressStruct fps; + + if (isTheFileAndIsNotDownloadProgress) + { + if (mb.flrMemoryBlock) + { + // Either the very first block, or a subsequent block and allocateIrIDataChunkAutomatically was true for the first block + memcpy(mb.flrMemoryBlock+offset, inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()), amountToRead); + fps.iriDataChunk=mb.flrMemoryBlock+offset; + } + else + { + // In here mb.flrMemoryBlock is null + // This means the first block explicitly deallocated the memory, and no blocks will be permanently held by RakNet + fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); + } + + onFileStruct.bytesDownloadedForThisFile=offset+amountToRead; + } + else + { + fileListReceiver->setTotalDownloadedLength+=partLength; + onFileStruct.bytesDownloadedForThisFile=partCount*partLength; + fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); + } + onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength; + + onFileStruct.numberOfFilesInThisSet=fileListReceiver->setCount; +// onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength; + onFileStruct.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + // Note: mb.flrMemoryBlock may be null here + onFileStruct.fileData=mb.flrMemoryBlock; + onFileStruct.senderSystemAddress=packet->systemAddress; + onFileStruct.senderGuid=packet->guid; + + unsigned int totalNotifications; + unsigned int currentNotificationIndex; + if (chunkLength==0 || chunkLength==onFileStruct.byteLengthOfThisFile) + totalNotifications=1; + else + totalNotifications = onFileStruct.byteLengthOfThisFile / chunkLength + 1; + + if (chunkLength==0) + currentNotificationIndex = 0; + else + currentNotificationIndex = offset / chunkLength; + + fps.onFileStruct=&onFileStruct; + fps.partCount=currentNotificationIndex; + fps.partTotal=totalNotifications; + fps.dataChunkLength=amountToRead; + fps.firstDataChunk=mb.flrMemoryBlock; + fps.allocateIrIDataChunkAutomatically=true; + fps.onFileStruct->fileData=mb.flrMemoryBlock; + fps.iriWriteOffset=offset; + fps.senderSystemAddress=packet->systemAddress; + fps.senderGuid=packet->guid; + + if (finished) + { + char *oldFileData=fps.onFileStruct->fileData; + if (fps.partCount==0) + fps.firstDataChunk=fps.iriDataChunk; + if (fps.partTotal==1) + fps.onFileStruct->fileData=fps.iriDataChunk; + fileListReceiver->downloadHandler->OnFileProgress(&fps); + + // Incremental read interface sent us a file chunk + // This is the last file chunk we were waiting for to consider the file done + if (fileListReceiver->downloadHandler->OnFile(&onFileStruct)) + rakFree_Ex(oldFileData, _FILE_AND_LINE_ ); + fileListReceiver->pushedFiles.Delete(onFileStruct.fileIndex); + + fileListReceiver->filesReceived++; + + // If this set is done, free the memory for it. + if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived) + { + FileListTransferCBInterface::DownloadCompleteStruct dcs; + dcs.setID=fileListReceiver->setID; + dcs.numberOfFilesInThisSet=fileListReceiver->setCount; + dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + dcs.senderSystemAddress=packet->systemAddress; + dcs.senderGuid=packet->guid; + + if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) + { + fileListReceiver->downloadHandler->OnDereference(); + fileListReceivers.Delete(onFileStruct.setID); + if (fileListReceiver->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); + } + } + } + else + { + if (isTheFileAndIsNotDownloadProgress) + { + // 12/23/09 Don't use OnReferencePush anymore, just use OnFileProgress + fileListReceiver->downloadHandler->OnFileProgress(&fps); + + if (fps.allocateIrIDataChunkAutomatically==false) + { + rakFree_Ex(fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock, _FILE_AND_LINE_ ); + fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock=0; + } + } + else + { + // This is a download progress notification for a file chunk using incremental read interface + // We don't have all the data for this chunk yet + + // UNREACHABLE CODE + totalNotifications = onFileStruct.byteLengthOfThisFile / fileListReceiver->partLength + 1; + if (isTheFileAndIsNotDownloadProgress==false) + currentNotificationIndex = (offset+partCount*fileListReceiver->partLength) / fileListReceiver->partLength ; + else + currentNotificationIndex = (offset+chunkLength) / fileListReceiver->partLength ; + unreadBytes = onFileStruct.byteLengthOfThisFile - ((currentNotificationIndex+1) * fileListReceiver->partLength); + + if (rakPeerInterface) + { + // Thus chunk is incomplete + fps.iriDataChunk=0; + + fileListReceiver->downloadHandler->OnFileProgress(&fps); + } + } + } + + return; +} +namespace RakNet +{ +int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData) +{ + (void) perThreadData; + + FileListTransfer *fileListTransfer = threadData.fileListTransfer; + SystemAddress systemAddress = threadData.systemAddress; + *returnOutput=false; + + // Was previously using GetStatistics to get outgoing buffer size, but TCP with UnifiedSend doesn't have this + unsigned int bytesRead; + const char *dataBlocks[2]; + int lengths[2]; + unsigned int smallFileTotalSize=0; + RakNet::BitStream outBitstream; + unsigned int ftpIndex; + + fileListTransfer->fileToPushRecipientListMutex.Lock(); + for (ftpIndex=0; ftpIndex < fileListTransfer->fileToPushRecipientList.Size(); ftpIndex++) + { + FileListTransfer::FileToPushRecipient *ftpr = fileListTransfer->fileToPushRecipientList[ftpIndex]; + // Referenced by both ftpr and list + ftpr->AddRef(); + + fileListTransfer->fileToPushRecipientListMutex.Unlock(); + + if (ftpr->systemAddress==systemAddress) + { + FileListTransfer::FileToPush *ftp = ftpr->filesToPush.Peek(); + + // Read and send chunk. If done, delete at this index + void *buff = rakMalloc_Ex(ftp->chunkSize, _FILE_AND_LINE_); + if (buff==0) + { + ftpr->Deref(); + notifyOutOfMemory(_FILE_AND_LINE_); + return 0; + } + + // Read the next file chunk + bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context); + + bool done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead; + while (done && ftp->currentOffset==0 && ftpr->filesToPush.Size()>=2 && smallFileTotalSizechunkSize) + { + // Send all small files at once, rather than wait for ID_FILE_LIST_REFERENCE_PUSH. But at least one ID_FILE_LIST_REFERENCE_PUSH must be sent + outBitstream.Reset(); + outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); + // outBitstream.Write(ftp->fileListNode.context); + outBitstream << ftp->fileListNode.context; + outBitstream.Write(ftp->setID); + StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream); + outBitstream.WriteCompressed(ftp->setIndex); + outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes + outBitstream.AlignWriteToByteBoundary(); + dataBlocks[0]=(char*) outBitstream.GetData(); + lengths[0]=outBitstream.GetNumberOfBytesUsed(); + dataBlocks[1]=(const char*) buff; + lengths[1]=bytesRead; + + fileListTransfer->SendListUnified(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, systemAddress, false); + + // LWS : fixed freed pointer reference +// unsigned int chunkSize = ftp->chunkSize; + RakNet::OP_DELETE(ftp,_FILE_AND_LINE_); + ftpr->filesToPush.Pop(); + smallFileTotalSize+=bytesRead; + //done = bytesRead!=ftp->chunkSize; + ftp = ftpr->filesToPush.Peek(); + + bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context); + done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead; + } + + + outBitstream.Reset(); + outBitstream.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH); + // outBitstream.Write(ftp->fileListNode.context); + outBitstream << ftp->fileListNode.context; + outBitstream.Write(ftp->setID); + StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream); + outBitstream.WriteCompressed(ftp->setIndex); + outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes + outBitstream.WriteCompressed(ftp->currentOffset); + ftp->currentOffset+=bytesRead; + outBitstream.WriteCompressed(bytesRead); + outBitstream.Write(done); + + for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++) + fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePush(ftp->fileListNode.filename, ftp->fileListNode.fileLengthBytes, ftp->currentOffset-bytesRead, bytesRead, done, systemAddress, ftp->setID); + + dataBlocks[0]=(char*) outBitstream.GetData(); + lengths[0]=outBitstream.GetNumberOfBytesUsed(); + dataBlocks[1]=(char*) buff; + lengths[1]=bytesRead; + //rakPeerInterface->SendList(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, ftp->systemAddress, false); + fileListTransfer->SendListUnified(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, systemAddress, false); + + // Mutex state: FileToPushRecipient (ftpr) has AddRef. fileToPushRecipientListMutex not locked. + if (done) + { + // Done + unsigned short setId = ftp->setID; + RakNet::OP_DELETE(ftp,_FILE_AND_LINE_); + ftpr->filesToPush.Pop(); + + if (ftpr->filesToPush.Size()==0) + { + for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++) + fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(systemAddress, setId); + + // Remove ftpr from fileToPushRecipientList + fileListTransfer->RemoveFromList(ftpr); + } + } + + // ftpr out of scope + ftpr->Deref(); + + rakFree_Ex(buff, _FILE_AND_LINE_ ); + return 0; + } + else + { + ftpr->Deref(); + fileListTransfer->fileToPushRecipientListMutex.Lock(); + } + } + + fileListTransfer->fileToPushRecipientListMutex.Unlock(); + + return 0; +} +} +void FileListTransfer::SendIRIToAddress(SystemAddress systemAddress) +{ + ThreadData threadData; + threadData.fileListTransfer=this; + threadData.systemAddress=systemAddress; + + if (threadPool.WasStarted()) + { + threadPool.AddInput(SendIRIToAddressCB, threadData); + } + else + { + bool doesNothing; + SendIRIToAddressCB(threadData, &doesNothing, 0); + } +} +void FileListTransfer::OnReferencePushAck(Packet *packet) +{ + SendIRIToAddress(packet->systemAddress); +} +void FileListTransfer::RemoveFromList(FileToPushRecipient *ftpr) +{ + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + if (fileToPushRecipientList[i]==ftpr) + { + fileToPushRecipientList.RemoveAtIndex(i); + // List no longer references + ftpr->Deref(); + fileToPushRecipientListMutex.Unlock(); + return; + } + } + fileToPushRecipientListMutex.Unlock(); +} +unsigned int FileListTransfer::GetPendingFilesToAddress(SystemAddress recipient) +{ + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + if (fileToPushRecipientList[i]->systemAddress==recipient) + { + unsigned int size = fileToPushRecipientList[i]->filesToPush.Size(); + fileToPushRecipientListMutex.Unlock(); + return size; + } + } + fileToPushRecipientListMutex.Unlock(); + + return 0; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/FileListTransfer.h b/project/lib_projects/raknet/jni/RaknetSources/FileListTransfer.h index 11bc4ad..c3d794c 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FileListTransfer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/FileListTransfer.h @@ -1,173 +1,173 @@ -/// \file FileListTransfer.h -/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FileListTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __FILE_LIST_TRANFER_H -#define __FILE_LIST_TRANFER_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "DS_Map.h" -#include "RakNetTypes.h" -#include "PacketPriority.h" -#include "RakMemoryOverride.h" -#include "FileList.h" -#include "DS_Queue.h" -#include "SimpleMutex.h" -#include "ThreadPool.h" - -namespace RakNet -{ -/// Forward declarations -class IncrementalReadInterface; -class FileListTransferCBInterface; -class FileListProgress; -struct FileListReceiver; - -/// \defgroup FILE_LIST_TRANSFER_GROUP FileListTransfer -/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. -/// \details Similar to the DirectoryDeltaTransfer plugin, except that it doesn't send deltas based on pre-existing files or actually write the files to disk. -/// -/// Usage: -/// Call SetupReceive to allow one file set to arrive. The value returned by FileListTransfer::SetupReceive() -/// is the setID that is allowed. -/// It's up to you to transmit this value to the other system, along with information indicating what kind of files you want to get. -/// The other system should then prepare a FileList and call FileListTransfer::Send(), passing the return value of FileListTransfer::SetupReceive() -/// as the \a setID parameter to FileListTransfer::Send() -/// \ingroup FILE_LIST_TRANSFER_GROUP -class RAK_DLL_EXPORT FileListTransfer : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FileListTransfer) - - FileListTransfer(); - virtual ~FileListTransfer(); - - /// \brief Optionally start worker threads when using _incrementalReadInterface for the Send() operation - /// \param[in] numThreads how many worker threads to start - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - void StartIncrementalReadThreads(int numThreads, int threadPriority=-99999); - - /// \brief Allows one corresponding Send() call from another system to arrive. - /// \param[in] handler The class to call on each file - /// \param[in] deleteHandler True to delete the handler when it is no longer needed. False to not do so. - /// \param[in] allowedSender Which system to allow files from. - /// \return A set ID value, which should be passed as the \a setID value to the Send() call on the other system. This value will be returned in the callback and is unique per file set. Returns 65535 on failure (not connected to sender) - unsigned short SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender); - - /// \brief Send the FileList structure to another system, which must have previously called SetupReceive(). - /// \param[in] fileList A list of files. The data contained in FileList::data will be sent incrementally and compressed among all files in the set - /// \param[in] rakPeer The instance of RakNet to use to send the message. Pass 0 to use the instance the plugin is attached to - /// \param[in] recipient The address of the system to send to - /// \param[in] setID The return value of SetupReceive() which was previously called on \a recipient - /// \param[in] priority Passed to RakPeerInterface::Send() - /// \param[in] orderingChannel Passed to RakPeerInterface::Send() - /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, _incrementalReadInterface will be used to read the file in chunks of size \a chunkSize - /// \param[in] _chunkSize How large of a block of a file to send at once - void Send(FileList *fileList, RakNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface=0, unsigned int _chunkSize=262144*4*16); - - /// Return number of files waiting to go out to a particular address - unsigned int GetPendingFilesToAddress(SystemAddress recipient); - - /// \brief Stop a download. - void CancelReceive(unsigned short setId); - - /// \brief Remove all handlers associated with a particular system address. - void RemoveReceiver(SystemAddress systemAddress); - - /// \brief Is a handler passed to SetupReceive still running? - bool IsHandlerActive(unsigned short setId); - - /// \brief Adds a callback to get progress reports about what the file list instances do. - /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. - void AddCallback(FileListProgress *cb); - - /// \brief Removes a callback - /// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback() - void RemoveCallback(FileListProgress *cb); - - /// \brief Removes all callbacks - void ClearCallbacks(void); - - /// Returns all callbacks added with AddCallback() - /// \param[out] callbacks The list is set to the list of callbacks - void GetCallbacks(DataStructures::List &callbacks); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal For plugin handling - virtual void OnRakPeerShutdown(void); - /// \internal For plugin handling - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal For plugin handling - virtual void Update(void); - -protected: - bool DecodeSetHeader(Packet *packet); - bool DecodeFile(Packet *packet, bool fullFile); - - void Clear(void); - - void OnReferencePush(Packet *packet, bool fullFile); - void OnReferencePushAck(Packet *packet); - void SendIRIToAddress(SystemAddress systemAddress); - - DataStructures::Map fileListReceivers; - unsigned short setId; - DataStructures::List fileListProgressCallbacks; - - struct FileToPush - { - FileListNode fileListNode; - PacketPriority packetPriority; - char orderingChannel; - unsigned int currentOffset; - unsigned short setID; - unsigned int setIndex; - IncrementalReadInterface *incrementalReadInterface; - unsigned int chunkSize; - }; - struct FileToPushRecipient - { - unsigned int refCount; - SimpleMutex refCountMutex; - void DeleteThis(void); - void AddRef(void); - void Deref(void); - - SystemAddress systemAddress; - DataStructures::Queue filesToPush; - }; - DataStructures::List< FileToPushRecipient* > fileToPushRecipientList; - SimpleMutex fileToPushRecipientListMutex; - void RemoveFromList(FileToPushRecipient *ftpr); - - struct ThreadData - { - FileListTransfer *fileListTransfer; - SystemAddress systemAddress; - }; - - ThreadPool threadPool; - - friend int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData); -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file FileListTransfer.h +/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FileListTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __FILE_LIST_TRANFER_H +#define __FILE_LIST_TRANFER_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "DS_Map.h" +#include "RakNetTypes.h" +#include "PacketPriority.h" +#include "RakMemoryOverride.h" +#include "FileList.h" +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "ThreadPool.h" + +namespace RakNet +{ +/// Forward declarations +class IncrementalReadInterface; +class FileListTransferCBInterface; +class FileListProgress; +struct FileListReceiver; + +/// \defgroup FILE_LIST_TRANSFER_GROUP FileListTransfer +/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. +/// \details Similar to the DirectoryDeltaTransfer plugin, except that it doesn't send deltas based on pre-existing files or actually write the files to disk. +/// +/// Usage: +/// Call SetupReceive to allow one file set to arrive. The value returned by FileListTransfer::SetupReceive() +/// is the setID that is allowed. +/// It's up to you to transmit this value to the other system, along with information indicating what kind of files you want to get. +/// The other system should then prepare a FileList and call FileListTransfer::Send(), passing the return value of FileListTransfer::SetupReceive() +/// as the \a setID parameter to FileListTransfer::Send() +/// \ingroup FILE_LIST_TRANSFER_GROUP +class RAK_DLL_EXPORT FileListTransfer : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FileListTransfer) + + FileListTransfer(); + virtual ~FileListTransfer(); + + /// \brief Optionally start worker threads when using _incrementalReadInterface for the Send() operation + /// \param[in] numThreads how many worker threads to start + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + void StartIncrementalReadThreads(int numThreads, int threadPriority=-99999); + + /// \brief Allows one corresponding Send() call from another system to arrive. + /// \param[in] handler The class to call on each file + /// \param[in] deleteHandler True to delete the handler when it is no longer needed. False to not do so. + /// \param[in] allowedSender Which system to allow files from. + /// \return A set ID value, which should be passed as the \a setID value to the Send() call on the other system. This value will be returned in the callback and is unique per file set. Returns 65535 on failure (not connected to sender) + unsigned short SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender); + + /// \brief Send the FileList structure to another system, which must have previously called SetupReceive(). + /// \param[in] fileList A list of files. The data contained in FileList::data will be sent incrementally and compressed among all files in the set + /// \param[in] rakPeer The instance of RakNet to use to send the message. Pass 0 to use the instance the plugin is attached to + /// \param[in] recipient The address of the system to send to + /// \param[in] setID The return value of SetupReceive() which was previously called on \a recipient + /// \param[in] priority Passed to RakPeerInterface::Send() + /// \param[in] orderingChannel Passed to RakPeerInterface::Send() + /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, _incrementalReadInterface will be used to read the file in chunks of size \a chunkSize + /// \param[in] _chunkSize How large of a block of a file to send at once + void Send(FileList *fileList, RakNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface=0, unsigned int _chunkSize=262144*4*16); + + /// Return number of files waiting to go out to a particular address + unsigned int GetPendingFilesToAddress(SystemAddress recipient); + + /// \brief Stop a download. + void CancelReceive(unsigned short setId); + + /// \brief Remove all handlers associated with a particular system address. + void RemoveReceiver(SystemAddress systemAddress); + + /// \brief Is a handler passed to SetupReceive still running? + bool IsHandlerActive(unsigned short setId); + + /// \brief Adds a callback to get progress reports about what the file list instances do. + /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. + void AddCallback(FileListProgress *cb); + + /// \brief Removes a callback + /// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback() + void RemoveCallback(FileListProgress *cb); + + /// \brief Removes all callbacks + void ClearCallbacks(void); + + /// Returns all callbacks added with AddCallback() + /// \param[out] callbacks The list is set to the list of callbacks + void GetCallbacks(DataStructures::List &callbacks); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal For plugin handling + virtual void OnRakPeerShutdown(void); + /// \internal For plugin handling + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal For plugin handling + virtual void Update(void); + +protected: + bool DecodeSetHeader(Packet *packet); + bool DecodeFile(Packet *packet, bool fullFile); + + void Clear(void); + + void OnReferencePush(Packet *packet, bool fullFile); + void OnReferencePushAck(Packet *packet); + void SendIRIToAddress(SystemAddress systemAddress); + + DataStructures::Map fileListReceivers; + unsigned short setId; + DataStructures::List fileListProgressCallbacks; + + struct FileToPush + { + FileListNode fileListNode; + PacketPriority packetPriority; + char orderingChannel; + unsigned int currentOffset; + unsigned short setID; + unsigned int setIndex; + IncrementalReadInterface *incrementalReadInterface; + unsigned int chunkSize; + }; + struct FileToPushRecipient + { + unsigned int refCount; + SimpleMutex refCountMutex; + void DeleteThis(void); + void AddRef(void); + void Deref(void); + + SystemAddress systemAddress; + DataStructures::Queue filesToPush; + }; + DataStructures::List< FileToPushRecipient* > fileToPushRecipientList; + SimpleMutex fileToPushRecipientListMutex; + void RemoveFromList(FileToPushRecipient *ftpr); + + struct ThreadData + { + FileListTransfer *fileListTransfer; + SystemAddress systemAddress; + }; + + ThreadPool threadPool; + + friend int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData); +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/FileListTransferCBInterface.h b/project/lib_projects/raknet/jni/RaknetSources/FileListTransferCBInterface.h index 7337317..ed1fb91 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FileListTransferCBInterface.h +++ b/project/lib_projects/raknet/jni/RaknetSources/FileListTransferCBInterface.h @@ -1,154 +1,154 @@ -/// \file FileListTransferCBInterface.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H -#define __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H - -#include "RakMemoryOverride.h" -#include "FileListNodeContext.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -namespace RakNet -{ - -/// \brief Used by FileListTransfer plugin as a callback for when we get a file. -/// \details You get the last file when fileIndex==numberOfFilesInThisSet -/// \sa FileListTransfer -class FileListTransferCBInterface -{ -public: - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct OnFileStruct - { - /// \brief The index into the set of files, from 0 to numberOfFilesInThisSet - unsigned fileIndex; - - /// \brief The name of the file - char fileName[512]; - - /// \brief The data pointed to by the file - char *fileData; - - /// \brief The actual length of this file. - BitSize_t byteLengthOfThisFile; - - /// \brief How many bytes of this file has been downloaded - BitSize_t bytesDownloadedForThisFile; - - /// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time. - /// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive - unsigned short setID; - - /// \brief The number of files that are in this set. - unsigned numberOfFilesInThisSet; - - /// \brief The total length of the transmitted files for this set, after being uncompressed - unsigned byteLengthOfThisSet; - - /// \brief The total length, in bytes, downloaded for this set. - unsigned bytesDownloadedForThisSet; - - /// \brief User data passed to one of the functions in the FileList class. - /// \details However, on error, this is instead changed to one of the enumerations in the PatchContext structure. - FileListNodeContext context; - - /// \brief Who sent this file - SystemAddress senderSystemAddress; - - /// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP) - RakNetGUID senderGuid; - }; - - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct FileProgressStruct - { - /// \param[out] onFileStruct General information about this file, such as the filename and the first \a partLength bytes. You do NOT need to save this data yourself. The complete file will arrive normally. - OnFileStruct *onFileStruct; - /// \param[out] partCount The zero based index into partTotal. The percentage complete done of this file is 100 * (partCount+1)/partTotal - unsigned int partCount; - /// \param[out] partTotal The total number of parts this file was split into. Each part will be roughly the MTU size, minus the UDP header and RakNet headers - unsigned int partTotal; - /// \param[out] dataChunkLength How many bytes long firstDataChunk and iriDataChunk are - unsigned int dataChunkLength; - /// \param[out] firstDataChunk The first \a partLength of the final file. If you store identifying information about the file in the first \a partLength bytes, you can read them while the download is taking place. If this hasn't arrived yet, firstDataChunk will be 0 - char *firstDataChunk; - /// \param[out] iriDataChunk If the remote system is sending this file using IncrementalReadInterface, then this is the chunk we just downloaded. It will not exist in memory after this callback. You should either store this to disk, or in memory. If it is 0, then the file is smaller than one chunk, and will be held in memory automatically - char *iriDataChunk; - /// \param[out] iriWriteOffset Offset in bytes from the start of the file for the data pointed to by iriDataChunk - unsigned int iriWriteOffset; - /// \param[out] Who sent this file - SystemAddress senderSystemAddress; - /// \param[out] Who sent this file. Not valid when using TCP, only RakPeer (UDP) - RakNetGUID senderGuid; - /// \param[in] allocateIrIDataChunkAutomatically If true, then RakNet will hold iriDataChunk for you and return it in OnFile. Defaults to true - bool allocateIrIDataChunkAutomatically; - }; - - struct DownloadCompleteStruct - { - /// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time. - /// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive - unsigned short setID; - - /// \brief The number of files that are in this set. - unsigned numberOfFilesInThisSet; - - /// \brief The total length of the transmitted files for this set, after being uncompressed - unsigned byteLengthOfThisSet; - - /// \brief Who sent this file - SystemAddress senderSystemAddress; - - /// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP) - RakNetGUID senderGuid; - }; - - FileListTransferCBInterface() {} - virtual ~FileListTransferCBInterface() {} - - /// \brief Got a file. - /// \details This structure is only valid for the duration of this function call. - /// \return Return true to have RakNet delete the memory allocated to hold this file for this function call. - virtual bool OnFile(OnFileStruct *onFileStruct)=0; - - /// \brief Got part of a big file internally in RakNet - /// \details This is called in one of two circumstances: Either the transport layer is returning ID_PROGRESS_NOTIFICATION, or you got a block via IncrementalReadInterface - /// If the transport layer is returning ID_PROGRESS_NOTIFICATION (see RakPeer::SetSplitMessageProgressInterval()) then FileProgressStruct::iriDataChunk will be 0. - /// If this is a block via IncrementalReadInterface, then iriDataChunk will point to the block just downloaded. - /// If not using IncrementalReadInterface, then you only care about partCount and partTotal to tell how far the download has progressed. YOu can use firstDataChunk to read the first part of the file if desired. The file is usable when you get the OnFile callback. - /// If using IncrementalReadInterface and you let RakNet buffer the files in memory (default), then it is the same as above. The file is usable when you get the OnFile callback. - /// If using IncrementalReadInterface and you do not let RakNet buffer the files in memory, then set allocateIrIDataChunkAutomatically to false. Write the file to disk whenever you get OnFileProgress and iriDataChunk is not 0, and ignore OnFile. - virtual void OnFileProgress(FileProgressStruct *fps)=0; - - /// \brief Called while the handler is active by FileListTransfer - /// \details Return false when you are done with the class. - /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. - virtual bool Update(void) {return true;} - - /// \brief Called when the download is completed. - /// \details If you are finished with this class, return false. - /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. - /// Otherwise return true, and Update will continue to be called. - virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs) {(void) dcs; return false;} - - /// \brief This function is called when this instance is about to be dereferenced by the FileListTransfer plugin. - /// \details Update will no longer be called. - /// It will will be deleted automatically if true was passed to FileListTransfer::SetupReceive::deleteHandler - /// Otherwise it is up to you to delete it yourself. - virtual void OnDereference(void) {} -}; - -} // namespace RakNet - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - +/// \file FileListTransferCBInterface.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H +#define __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H + +#include "RakMemoryOverride.h" +#include "FileListNodeContext.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +namespace RakNet +{ + +/// \brief Used by FileListTransfer plugin as a callback for when we get a file. +/// \details You get the last file when fileIndex==numberOfFilesInThisSet +/// \sa FileListTransfer +class FileListTransferCBInterface +{ +public: + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct OnFileStruct + { + /// \brief The index into the set of files, from 0 to numberOfFilesInThisSet + unsigned fileIndex; + + /// \brief The name of the file + char fileName[512]; + + /// \brief The data pointed to by the file + char *fileData; + + /// \brief The actual length of this file. + BitSize_t byteLengthOfThisFile; + + /// \brief How many bytes of this file has been downloaded + BitSize_t bytesDownloadedForThisFile; + + /// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time. + /// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive + unsigned short setID; + + /// \brief The number of files that are in this set. + unsigned numberOfFilesInThisSet; + + /// \brief The total length of the transmitted files for this set, after being uncompressed + unsigned byteLengthOfThisSet; + + /// \brief The total length, in bytes, downloaded for this set. + unsigned bytesDownloadedForThisSet; + + /// \brief User data passed to one of the functions in the FileList class. + /// \details However, on error, this is instead changed to one of the enumerations in the PatchContext structure. + FileListNodeContext context; + + /// \brief Who sent this file + SystemAddress senderSystemAddress; + + /// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP) + RakNetGUID senderGuid; + }; + + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct FileProgressStruct + { + /// \param[out] onFileStruct General information about this file, such as the filename and the first \a partLength bytes. You do NOT need to save this data yourself. The complete file will arrive normally. + OnFileStruct *onFileStruct; + /// \param[out] partCount The zero based index into partTotal. The percentage complete done of this file is 100 * (partCount+1)/partTotal + unsigned int partCount; + /// \param[out] partTotal The total number of parts this file was split into. Each part will be roughly the MTU size, minus the UDP header and RakNet headers + unsigned int partTotal; + /// \param[out] dataChunkLength How many bytes long firstDataChunk and iriDataChunk are + unsigned int dataChunkLength; + /// \param[out] firstDataChunk The first \a partLength of the final file. If you store identifying information about the file in the first \a partLength bytes, you can read them while the download is taking place. If this hasn't arrived yet, firstDataChunk will be 0 + char *firstDataChunk; + /// \param[out] iriDataChunk If the remote system is sending this file using IncrementalReadInterface, then this is the chunk we just downloaded. It will not exist in memory after this callback. You should either store this to disk, or in memory. If it is 0, then the file is smaller than one chunk, and will be held in memory automatically + char *iriDataChunk; + /// \param[out] iriWriteOffset Offset in bytes from the start of the file for the data pointed to by iriDataChunk + unsigned int iriWriteOffset; + /// \param[out] Who sent this file + SystemAddress senderSystemAddress; + /// \param[out] Who sent this file. Not valid when using TCP, only RakPeer (UDP) + RakNetGUID senderGuid; + /// \param[in] allocateIrIDataChunkAutomatically If true, then RakNet will hold iriDataChunk for you and return it in OnFile. Defaults to true + bool allocateIrIDataChunkAutomatically; + }; + + struct DownloadCompleteStruct + { + /// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time. + /// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive + unsigned short setID; + + /// \brief The number of files that are in this set. + unsigned numberOfFilesInThisSet; + + /// \brief The total length of the transmitted files for this set, after being uncompressed + unsigned byteLengthOfThisSet; + + /// \brief Who sent this file + SystemAddress senderSystemAddress; + + /// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP) + RakNetGUID senderGuid; + }; + + FileListTransferCBInterface() {} + virtual ~FileListTransferCBInterface() {} + + /// \brief Got a file. + /// \details This structure is only valid for the duration of this function call. + /// \return Return true to have RakNet delete the memory allocated to hold this file for this function call. + virtual bool OnFile(OnFileStruct *onFileStruct)=0; + + /// \brief Got part of a big file internally in RakNet + /// \details This is called in one of two circumstances: Either the transport layer is returning ID_PROGRESS_NOTIFICATION, or you got a block via IncrementalReadInterface + /// If the transport layer is returning ID_PROGRESS_NOTIFICATION (see RakPeer::SetSplitMessageProgressInterval()) then FileProgressStruct::iriDataChunk will be 0. + /// If this is a block via IncrementalReadInterface, then iriDataChunk will point to the block just downloaded. + /// If not using IncrementalReadInterface, then you only care about partCount and partTotal to tell how far the download has progressed. YOu can use firstDataChunk to read the first part of the file if desired. The file is usable when you get the OnFile callback. + /// If using IncrementalReadInterface and you let RakNet buffer the files in memory (default), then it is the same as above. The file is usable when you get the OnFile callback. + /// If using IncrementalReadInterface and you do not let RakNet buffer the files in memory, then set allocateIrIDataChunkAutomatically to false. Write the file to disk whenever you get OnFileProgress and iriDataChunk is not 0, and ignore OnFile. + virtual void OnFileProgress(FileProgressStruct *fps)=0; + + /// \brief Called while the handler is active by FileListTransfer + /// \details Return false when you are done with the class. + /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. + virtual bool Update(void) {return true;} + + /// \brief Called when the download is completed. + /// \details If you are finished with this class, return false. + /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. + /// Otherwise return true, and Update will continue to be called. + virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs) {(void) dcs; return false;} + + /// \brief This function is called when this instance is about to be dereferenced by the FileListTransfer plugin. + /// \details Update will no longer be called. + /// It will will be deleted automatically if true was passed to FileListTransfer::SetupReceive::deleteHandler + /// Otherwise it is up to you to delete it yourself. + virtual void OnDereference(void) {} +}; + +} // namespace RakNet + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/FileOperations.cpp b/project/lib_projects/raknet/jni/RaknetSources/FileOperations.cpp index 93f6f31..92f3af3 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FileOperations.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/FileOperations.cpp @@ -1,173 +1,173 @@ -#include "FileOperations.h" -#if _RAKNET_SUPPORT_FileOperations==1 -#include "RakMemoryOverride.h" -#include "_FindFirst.h" // For linux -#include -#include -#ifdef _WIN32 -// For mkdir -#include -#include -#else -#include -#include -#include "_FindFirst.h" -#endif -#include "errno.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -#ifdef _MSC_VER -#pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. -#endif -bool WriteFileWithDirectories( const char *path, char *data, unsigned dataLength ) -{ - int index; - FILE *fp; - char *pathCopy; - int res; - - if ( path == 0 || path[ 0 ] == 0 ) - return false; - - pathCopy = (char*) rakMalloc_Ex( strlen( path ) + 1, _FILE_AND_LINE_ ); - - strcpy( pathCopy, path ); - - // Ignore first / if there is one - if (pathCopy[0]) - { - index = 1; - while ( pathCopy[ index ] ) - { - if ( pathCopy[ index ] == '/' || pathCopy[ index ] == '\\') - { - pathCopy[ index ] = 0; - - #ifdef _WIN32 - #pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. - res = mkdir( pathCopy ); - #else - - res = mkdir( pathCopy, 0744 ); - #endif - if (res<0 && errno!=EEXIST && errno!=EACCES) - { - rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); - return false; - } - - pathCopy[ index ] = '/'; - } - - index++; - } - } - - if (data) - { - fp = fopen( path, "wb" ); - - if ( fp == 0 ) - { - rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); - return false; - } - - fwrite( data, 1, dataLength, fp ); - - fclose( fp ); - } - else - { -#ifdef _WIN32 -#pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. - res = mkdir( pathCopy ); -#else - res = mkdir( pathCopy, 0744 ); -#endif - - if (res<0 && errno!=EEXIST) - { - rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); - return false; - } - } - - rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); - - return true; -} -bool IsSlash(unsigned char c) -{ - return c=='/' || c=='\\'; -} - -void AddSlash( char *input ) -{ - if (input==0 || input[0]==0) - return; - - int lastCharIndex=(int) strlen(input)-1; - if (input[lastCharIndex]=='\\') - input[lastCharIndex]='/'; - else if (input[lastCharIndex]!='/') - { - input[lastCharIndex+1]='/'; - input[lastCharIndex+2]=0; - } -} -bool DirectoryExists(const char *directory) -{ - _finddata_t fileInfo; - intptr_t dir; - char baseDirWithStars[560]; - strcpy(baseDirWithStars, directory); - AddSlash(baseDirWithStars); - strcat(baseDirWithStars, "*.*"); - dir=_findfirst(baseDirWithStars, &fileInfo ); - if (dir==-1) - return false; - _findclose(dir); - return true; -} -void QuoteIfSpaces(char *str) -{ - unsigned i; - bool hasSpace=false; - for (i=0; str[i]; i++) - { - if (str[i]==' ') - { - hasSpace=true; - break; - } - } - if (hasSpace) - { - int len=(int)strlen(str); - memmove(str+1, str, len); - str[0]='\"'; - str[len]='\"'; - str[len+1]=0; - } -} -unsigned int GetFileLength(const char *path) -{ - FILE *fp = fopen(path, "rb"); - if (fp==0) return 0; - fseek(fp, 0, SEEK_END); - unsigned int fileLength = ftell(fp); - fclose(fp); - return fileLength; - -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_FileOperations - +#include "FileOperations.h" +#if _RAKNET_SUPPORT_FileOperations==1 +#include "RakMemoryOverride.h" +#include "_FindFirst.h" // For linux +#include +#include +#ifdef _WIN32 +// For mkdir +#include +#include +#else +#include +#include +#include "_FindFirst.h" +#endif +#include "errno.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +#ifdef _MSC_VER +#pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. +#endif +bool WriteFileWithDirectories( const char *path, char *data, unsigned dataLength ) +{ + int index; + FILE *fp; + char *pathCopy; + int res; + + if ( path == 0 || path[ 0 ] == 0 ) + return false; + + pathCopy = (char*) rakMalloc_Ex( strlen( path ) + 1, _FILE_AND_LINE_ ); + + strcpy( pathCopy, path ); + + // Ignore first / if there is one + if (pathCopy[0]) + { + index = 1; + while ( pathCopy[ index ] ) + { + if ( pathCopy[ index ] == '/' || pathCopy[ index ] == '\\') + { + pathCopy[ index ] = 0; + + #ifdef _WIN32 + #pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. + res = mkdir( pathCopy ); + #else + + res = mkdir( pathCopy, 0744 ); + #endif + if (res<0 && errno!=EEXIST && errno!=EACCES) + { + rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); + return false; + } + + pathCopy[ index ] = '/'; + } + + index++; + } + } + + if (data) + { + fp = fopen( path, "wb" ); + + if ( fp == 0 ) + { + rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); + return false; + } + + fwrite( data, 1, dataLength, fp ); + + fclose( fp ); + } + else + { +#ifdef _WIN32 +#pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. + res = mkdir( pathCopy ); +#else + res = mkdir( pathCopy, 0744 ); +#endif + + if (res<0 && errno!=EEXIST) + { + rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); + return false; + } + } + + rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); + + return true; +} +bool IsSlash(unsigned char c) +{ + return c=='/' || c=='\\'; +} + +void AddSlash( char *input ) +{ + if (input==0 || input[0]==0) + return; + + int lastCharIndex=(int) strlen(input)-1; + if (input[lastCharIndex]=='\\') + input[lastCharIndex]='/'; + else if (input[lastCharIndex]!='/') + { + input[lastCharIndex+1]='/'; + input[lastCharIndex+2]=0; + } +} +bool DirectoryExists(const char *directory) +{ + _finddata_t fileInfo; + intptr_t dir; + char baseDirWithStars[560]; + strcpy(baseDirWithStars, directory); + AddSlash(baseDirWithStars); + strcat(baseDirWithStars, "*.*"); + dir=_findfirst(baseDirWithStars, &fileInfo ); + if (dir==-1) + return false; + _findclose(dir); + return true; +} +void QuoteIfSpaces(char *str) +{ + unsigned i; + bool hasSpace=false; + for (i=0; str[i]; i++) + { + if (str[i]==' ') + { + hasSpace=true; + break; + } + } + if (hasSpace) + { + int len=(int)strlen(str); + memmove(str+1, str, len); + str[0]='\"'; + str[len]='\"'; + str[len+1]=0; + } +} +unsigned int GetFileLength(const char *path) +{ + FILE *fp = fopen(path, "rb"); + if (fp==0) return 0; + fseek(fp, 0, SEEK_END); + unsigned int fileLength = ftell(fp); + fclose(fp); + return fileLength; + +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_FileOperations + diff --git a/project/lib_projects/raknet/jni/RaknetSources/FileOperations.h b/project/lib_projects/raknet/jni/RaknetSources/FileOperations.h index fc7be06..1e44bd8 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FileOperations.h +++ b/project/lib_projects/raknet/jni/RaknetSources/FileOperations.h @@ -1,24 +1,24 @@ -/// \file FileOperations.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __FILE_OPERATIONS_H -#define __FILE_OPERATIONS_H - -#include "Export.h" - -bool RAK_DLL_EXPORT WriteFileWithDirectories( const char *path, char *data, unsigned dataLength ); -bool RAK_DLL_EXPORT IsSlash(unsigned char c); -void RAK_DLL_EXPORT AddSlash( char *input ); -void RAK_DLL_EXPORT QuoteIfSpaces(char *str); -bool RAK_DLL_EXPORT DirectoryExists(const char *directory); -unsigned int RAK_DLL_EXPORT GetFileLength(const char *path); - -#endif - -#endif // _RAKNET_SUPPORT_FileOperations +/// \file FileOperations.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __FILE_OPERATIONS_H +#define __FILE_OPERATIONS_H + +#include "Export.h" + +bool RAK_DLL_EXPORT WriteFileWithDirectories( const char *path, char *data, unsigned dataLength ); +bool RAK_DLL_EXPORT IsSlash(unsigned char c); +void RAK_DLL_EXPORT AddSlash( char *input ); +void RAK_DLL_EXPORT QuoteIfSpaces(char *str); +bool RAK_DLL_EXPORT DirectoryExists(const char *directory); +unsigned int RAK_DLL_EXPORT GetFileLength(const char *path); + +#endif + +#endif // _RAKNET_SUPPORT_FileOperations diff --git a/project/lib_projects/raknet/jni/RaknetSources/FormatString.cpp b/project/lib_projects/raknet/jni/RaknetSources/FormatString.cpp index f91ae31..e4df09e 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FormatString.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/FormatString.cpp @@ -1,30 +1,30 @@ -#include "FormatString.h" -#include -#include -#include -#include "LinuxStrings.h" - -char * FormatString(const char *format, ...) -{ - static int textIndex=0; - static char text[4][8096]; - va_list ap; - va_start(ap, format); - - if (++textIndex==4) - textIndex=0; - _vsnprintf(text[textIndex], 8096, format, ap); - va_end(ap); - text[textIndex][8096-1]=0; - - return text[textIndex]; -} - -char * FormatStringTS(char *output, const char *format, ...) -{ - va_list ap; - va_start(ap, format); - _vsnprintf(output, 512, format, ap); - va_end(ap); - return output; -} +#include "FormatString.h" +#include +#include +#include +#include "LinuxStrings.h" + +char * FormatString(const char *format, ...) +{ + static int textIndex=0; + static char text[4][8096]; + va_list ap; + va_start(ap, format); + + if (++textIndex==4) + textIndex=0; + _vsnprintf(text[textIndex], 8096, format, ap); + va_end(ap); + text[textIndex][8096-1]=0; + + return text[textIndex]; +} + +char * FormatStringTS(char *output, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + _vsnprintf(output, 512, format, ap); + va_end(ap); + return output; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/FormatString.h b/project/lib_projects/raknet/jni/RaknetSources/FormatString.h index 9badba8..1ebdab9 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FormatString.h +++ b/project/lib_projects/raknet/jni/RaknetSources/FormatString.h @@ -1,22 +1,22 @@ -/// \file FormatString.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __FORMAT_STRING_H -#define __FORMAT_STRING_H - -#include "Export.h" - -extern "C" { -char * FormatString(const char *format, ...); -} -// Threadsafe -extern "C" { -char * FormatStringTS(char *output, const char *format, ...); -} - - -#endif - +/// \file FormatString.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __FORMAT_STRING_H +#define __FORMAT_STRING_H + +#include "Export.h" + +extern "C" { +char * FormatString(const char *format, ...); +} +// Threadsafe +extern "C" { +char * FormatStringTS(char *output, const char *format, ...); +} + + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/FullyConnectedMesh2.cpp b/project/lib_projects/raknet/jni/RaknetSources/FullyConnectedMesh2.cpp index fb9877e..3837a3e 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FullyConnectedMesh2.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/FullyConnectedMesh2.cpp @@ -1,552 +1,552 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FullyConnectedMesh2==1 - -#include "FullyConnectedMesh2.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" -#include "RakAssert.h" -#include "GetTime.h" -#include "Rand.h" -#include "DS_OrderedList.h" - -using namespace RakNet; - -int FCM2ParticipantComp( const FullyConnectedMesh2::FCM2Participant &key, const FullyConnectedMesh2::FCM2Participant &data ) -{ - if (key.fcm2Guid < data.fcm2Guid) - return -1; - if (key.fcm2Guid > data.fcm2Guid) - return 1; - return 0; -} - -STATIC_FACTORY_DEFINITIONS(FullyConnectedMesh2,FullyConnectedMesh2); - -FullyConnectedMesh2::FullyConnectedMesh2() -{ - startupTime=0; - totalConnectionCount=0; - ourFCMGuid=0; - autoParticipateConnections=true; - - - - - connectOnNewRemoteConnections=true; - - hostRakNetGuid=UNASSIGNED_RAKNET_GUID; -} -FullyConnectedMesh2::~FullyConnectedMesh2() -{ - Clear(); -} -RakNetGUID FullyConnectedMesh2::GetConnectedHost(void) const -{ - if (ourFCMGuid==0) - return UNASSIGNED_RAKNET_GUID; - return hostRakNetGuid; -} -SystemAddress FullyConnectedMesh2::GetConnectedHostAddr(void) const -{ - if (ourFCMGuid==0) - return UNASSIGNED_SYSTEM_ADDRESS; - return rakPeerInterface->GetSystemAddressFromGuid(hostRakNetGuid); -} -RakNetGUID FullyConnectedMesh2::GetHostSystem(void) const -{ - if (ourFCMGuid==0) - return rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - - return hostRakNetGuid; -} -bool FullyConnectedMesh2::IsHostSystem(void) const -{ - return GetHostSystem()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -} -void FullyConnectedMesh2::GetHostOrder(DataStructures::List &hostList) -{ - hostList.Clear(true, _FILE_AND_LINE_); - - if (ourFCMGuid==0 || fcm2ParticipantList.Size()==0) - { - hostList.Push(rakPeerInterface->GetMyGUID(), _FILE_AND_LINE_); - return; - } - - FCM2Participant fcm2; - fcm2.fcm2Guid=ourFCMGuid; - fcm2.rakNetGuid=rakPeerInterface->GetMyGUID(); - - DataStructures::OrderedList olist; - olist.Insert(fcm2, fcm2, true, _FILE_AND_LINE_); - for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) - olist.Insert(fcm2ParticipantList[i], fcm2ParticipantList[i], true, _FILE_AND_LINE_); - - for (unsigned int i=0; i < olist.Size(); i++) - { - hostList.Push(olist[i].rakNetGuid, _FILE_AND_LINE_); - } -} -bool FullyConnectedMesh2::IsConnectedHost(void) const -{ - return GetConnectedHost()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -} -void FullyConnectedMesh2::SetAutoparticipateConnections(bool b) -{ - autoParticipateConnections=b; -} -void FullyConnectedMesh2::ResetHostCalculation(void) -{ - hostRakNetGuid=UNASSIGNED_RAKNET_GUID; - startupTime=RakNet::GetTimeUS(); - totalConnectionCount=0; - ourFCMGuid=0; - for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) - SendFCMGuidRequest(fcm2ParticipantList[i].rakNetGuid); -} -bool FullyConnectedMesh2::AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid ) -{ - for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) - { - if (fcm2ParticipantList[i].rakNetGuid==rakNetGuid) - { - if (theirFCMGuid!=0) - fcm2ParticipantList[i].fcm2Guid=theirFCMGuid; - return false; - } - } - - FCM2Participant participant; - participant.rakNetGuid=rakNetGuid; - participant.fcm2Guid=theirFCMGuid; - fcm2ParticipantList.Push(participant,_FILE_AND_LINE_); - - SendFCMGuidRequest(rakNetGuid); - - return true; -} -void FullyConnectedMesh2::AddParticipant( RakNetGUID rakNetGuid ) -{ - if (rakPeerInterface->GetConnectionState(rakPeerInterface->GetSystemAddressFromGuid(rakNetGuid))!=IS_CONNECTED) - { -#ifdef DEBUG_FCM2 - printf("AddParticipant to %s failed (not connected)\n", rakNetGuid.ToString()); -#endif - return; - } - - AddParticipantInternal(rakNetGuid,0); -} -void FullyConnectedMesh2::GetParticipantList(DataStructures::List &participantList) -{ - participantList.Clear(true, _FILE_AND_LINE_); - unsigned int i; - for (i=0; i < fcm2ParticipantList.Size(); i++) - participantList.Push(fcm2ParticipantList[i].rakNetGuid, _FILE_AND_LINE_); -} -PluginReceiveResult FullyConnectedMesh2::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_REMOTE_NEW_INCOMING_CONNECTION: - { - if (connectOnNewRemoteConnections) - ConnectToRemoteNewIncomingConnections(packet); - } - break; - case ID_FCM2_REQUEST_FCMGUID: - OnRequestFCMGuid(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FCM2_RESPOND_CONNECTION_COUNT: - OnRespondConnectionCount(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FCM2_INFORM_FCMGUID: - OnInformFCMGuid(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT: - OnUpdateMinTotalConnectionCount(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FCM2_NEW_HOST: - if (packet->wasGeneratedLocally==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - break; - } - return RR_CONTINUE_PROCESSING; -} -void FullyConnectedMesh2::OnRakPeerStartup(void) -{ - Clear(); - startupTime=RakNet::GetTimeUS(); -} -void FullyConnectedMesh2::OnAttach(void) -{ - Clear(); - // In case Startup() was called first - if (rakPeerInterface->IsActive()) - startupTime=RakNet::GetTimeUS(); -} -void FullyConnectedMesh2::OnRakPeerShutdown(void) -{ - Clear(); - startupTime=0; -} -void FullyConnectedMesh2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - (void) rakNetGUID; - - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - { - if (fcm2ParticipantList[idx].rakNetGuid==rakNetGUID) - { - fcm2ParticipantList[idx]=fcm2ParticipantList[fcm2ParticipantList.Size()-1]; -#ifdef DEBUG_FCM2 - printf("Popping participant %s\n", fcm2ParticipantList[fcm2ParticipantList.Size()-1].rakNetGuid.ToString()); -#endif - - fcm2ParticipantList.Pop(); - if (rakNetGUID==hostRakNetGuid && ourFCMGuid!=0) - { - if (fcm2ParticipantList.Size()==0) - { - hostRakNetGuid=rakPeerInterface->GetMyGUID(); - hostFCM2Guid=ourFCMGuid; - } - else - { - CalculateHost(&hostRakNetGuid, &hostFCM2Guid); - } - PushNewHost(hostRakNetGuid, rakNetGUID); - } - return; - } - } - -} -RakNet::TimeUS FullyConnectedMesh2::GetElapsedRuntime(void) -{ - RakNet::TimeUS curTime=RakNet::GetTimeUS(); - if (curTime>startupTime) - return curTime-startupTime; - else - return 0; -} -void FullyConnectedMesh2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) isIncoming; - (void) rakNetGUID; - (void) systemAddress; - - if (autoParticipateConnections) - AddParticipant(rakNetGUID); -} -void FullyConnectedMesh2::Clear(void) -{ - fcm2ParticipantList.Clear(false, _FILE_AND_LINE_); - - totalConnectionCount=0; - ourFCMGuid=0; - lastPushedHost=UNASSIGNED_RAKNET_GUID; -} -void FullyConnectedMesh2::PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(oldHost)); - RakNet::BitStream bs(p->data,p->length,false); - bs.SetWriteOffset(0); - bs.Write((MessageID)ID_FCM2_NEW_HOST); - bs.Write(oldHost); - p->systemAddress=rakPeerInterface->GetSystemAddressFromGuid(guid); - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=guid; - p->wasGeneratedLocally=true; - rakPeerInterface->PushBackPacket(p, true); - - lastPushedHost=guid; -} -void FullyConnectedMesh2::SendFCMGuidRequest(RakNetGUID rakNetGuid) -{ - if (rakNetGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return; - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_FCM2_REQUEST_FCMGUID); - if (ourFCMGuid==0) - { - bsOut.Write(false); - bsOut.Write(GetElapsedRuntime()); - } - else - { - bsOut.Write(true); - bsOut.Write(totalConnectionCount); - bsOut.Write(ourFCMGuid); - } - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,rakNetGuid,false); -} -void FullyConnectedMesh2::SendOurFCMGuid(SystemAddress addr) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_FCM2_INFORM_FCMGUID); - RakAssert(ourFCMGuid!=0); // Can't inform others of our FCM2Guid if it's unset! - bsOut.Write(ourFCMGuid); - bsOut.Write(totalConnectionCount); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false); -} -void FullyConnectedMesh2::SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_FCM2_RESPOND_CONNECTION_COUNT); - bsOut.Write(responseTotalConnectionCount); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false); -} -void FullyConnectedMesh2::AssignOurFCMGuid(void) -{ - // Only assigned once ever - RakAssert(ourFCMGuid==0); - unsigned int randomNumber = randomMT(); - randomNumber ^= (unsigned int) (RakNet::GetTimeUS() & 0xFFFFFFFF); - randomNumber ^= (unsigned int) (rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).g & 0xFFFFFFFF); - ourFCMGuid |= randomNumber; - uint64_t reponse64 = totalConnectionCount; - ourFCMGuid |= reponse64<<32; -} -void FullyConnectedMesh2::CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid) -{ - // Can't calculate host without knowing our own - RakAssert(ourFCMGuid!=0); - - // Can't calculate host without being connected to anyone else - RakAssert(fcm2ParticipantList.Size()>0); - - // Return the lowest value of all FCM2Guid - FCM2Guid lowestFCMGuid=ourFCMGuid; - // SystemAddress associatedSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; - RakNetGUID associatedRakNetGuid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - { - if (fcm2ParticipantList[idx].fcm2Guid!=0 && fcm2ParticipantList[idx].fcm2Guiddata,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bool hasRemoteFCMGuid=false; - bsIn.Read(hasRemoteFCMGuid); - RakNet::TimeUS senderElapsedRuntime=0; - unsigned int remoteTotalConnectionCount=0; - FCM2Guid theirFCMGuid=0; - if (hasRemoteFCMGuid) - { - bsIn.Read(remoteTotalConnectionCount); - bsIn.Read(theirFCMGuid); - } - else - { - bsIn.Read(senderElapsedRuntime); - } - AddParticipantInternal(packet->guid,theirFCMGuid); - if (ourFCMGuid==0) - { - if (hasRemoteFCMGuid==false) - { - // Nobody has a fcmGuid - - RakNet::TimeUS ourElapsedRuntime = GetElapsedRuntime(); - if (ourElapsedRuntime>senderElapsedRuntime) - { - // We are probably host - SendConnectionCountResponse(packet->systemAddress, 2); - } - else - { - // They are probably host - SendConnectionCountResponse(packet->systemAddress, 1); - } - } - else - { - // They have a fcmGuid, we do not - IncrementTotalConnectionCount(remoteTotalConnectionCount+1); - - AssignOurFCMGuid(); - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); - } - } - else - { - if (hasRemoteFCMGuid==false) - { - // We have a fcmGuid they do not - SendConnectionCountResponse(packet->systemAddress, totalConnectionCount+1); - } - else - { - // We both have fcmGuids - IncrementTotalConnectionCount(remoteTotalConnectionCount); - - SendOurFCMGuid(packet->systemAddress); - } - } - CalculateAndPushHost(); -} -void FullyConnectedMesh2::OnRespondConnectionCount(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - unsigned int responseTotalConnectionCount; - bsIn.Read(responseTotalConnectionCount); - IncrementTotalConnectionCount(responseTotalConnectionCount); - bool wasAssigned; - if (ourFCMGuid==0) - { - wasAssigned=true; - AssignOurFCMGuid(); - } - else - wasAssigned=false; - - // 1 is returned to give us lower priority, but the actual minimum is 2 - IncrementTotalConnectionCount(2); - - if (wasAssigned==true) - { - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); - CalculateAndPushHost(); - } -} -void FullyConnectedMesh2::OnInformFCMGuid(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - - FCM2Guid theirFCMGuid; - unsigned int theirTotalConnectionCount; - bsIn.Read(theirFCMGuid); - bsIn.Read(theirTotalConnectionCount); - IncrementTotalConnectionCount(theirTotalConnectionCount); - - if (AddParticipantInternal(packet->guid,theirFCMGuid)) - { - // 1/19/2010 - Relay increased total connection count in case new participant only connects to part of the mesh - unsigned int idx; - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT); - bsOut.Write(totalConnectionCount); - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - { - if (packet->guid!=fcm2ParticipantList[idx].rakNetGuid) - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,fcm2ParticipantList[idx].rakNetGuid,false); - } - } - - if (ourFCMGuid==0) - { - AssignOurFCMGuid(); - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); - } - - CalculateAndPushHost(); -} -void FullyConnectedMesh2::OnUpdateMinTotalConnectionCount(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - unsigned int newMin; - bsIn.Read(newMin); - IncrementTotalConnectionCount(newMin); -} -void FullyConnectedMesh2::GetParticipantCount(unsigned int *participantListSize) const -{ - *participantListSize=fcm2ParticipantList.Size(); -} - -unsigned int FullyConnectedMesh2::GetParticipantCount(void) const -{ - return fcm2ParticipantList.Size(); -} -void FullyConnectedMesh2::CalculateAndPushHost(void) -{ - RakNetGUID newHostGuid; - FCM2Guid newFcmGuid; - if (ParticipantListComplete()) - { - CalculateHost(&newHostGuid, &newFcmGuid); - if (newHostGuid!=lastPushedHost) - { - hostRakNetGuid=newHostGuid; - hostFCM2Guid=newFcmGuid; - PushNewHost(hostRakNetGuid, hostRakNetGuid); - } - } -} -bool FullyConnectedMesh2::ParticipantListComplete(void) -{ - for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) - { - if (fcm2ParticipantList[i].fcm2Guid==0) - return false; - } - return true; -} -void FullyConnectedMesh2::IncrementTotalConnectionCount(unsigned int i) -{ - if (i>totalConnectionCount) - { - totalConnectionCount=i; - // printf("totalConnectionCount=%i\n",i); - } -} -void FullyConnectedMesh2::SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw) -{ - connectOnNewRemoteConnections=attemptConnection; - connectionPassword=pw; -} - -void FullyConnectedMesh2::ConnectToRemoteNewIncomingConnections(Packet *packet) -{ - unsigned int count; - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bsIn.Read(count); - SystemAddress remoteAddress; - RakNetGUID remoteGuid; - char str[64]; - for (unsigned int i=0; i < count; i++) - { - bsIn.Read(remoteAddress); - bsIn.Read(remoteGuid); - remoteAddress.ToString(false,str); - rakPeerInterface->Connect(str,remoteAddress.GetPort(),connectionPassword.C_String(),(int) connectionPassword.GetLength()); - } -} -unsigned int FullyConnectedMesh2::GetTotalConnectionCount(void) const -{ - return totalConnectionCount; -} - -#endif // _RAKNET_SUPPORT_* +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FullyConnectedMesh2==1 + +#include "FullyConnectedMesh2.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" +#include "RakAssert.h" +#include "GetTime.h" +#include "Rand.h" +#include "DS_OrderedList.h" + +using namespace RakNet; + +int FCM2ParticipantComp( const FullyConnectedMesh2::FCM2Participant &key, const FullyConnectedMesh2::FCM2Participant &data ) +{ + if (key.fcm2Guid < data.fcm2Guid) + return -1; + if (key.fcm2Guid > data.fcm2Guid) + return 1; + return 0; +} + +STATIC_FACTORY_DEFINITIONS(FullyConnectedMesh2,FullyConnectedMesh2); + +FullyConnectedMesh2::FullyConnectedMesh2() +{ + startupTime=0; + totalConnectionCount=0; + ourFCMGuid=0; + autoParticipateConnections=true; + + + + + connectOnNewRemoteConnections=true; + + hostRakNetGuid=UNASSIGNED_RAKNET_GUID; +} +FullyConnectedMesh2::~FullyConnectedMesh2() +{ + Clear(); +} +RakNetGUID FullyConnectedMesh2::GetConnectedHost(void) const +{ + if (ourFCMGuid==0) + return UNASSIGNED_RAKNET_GUID; + return hostRakNetGuid; +} +SystemAddress FullyConnectedMesh2::GetConnectedHostAddr(void) const +{ + if (ourFCMGuid==0) + return UNASSIGNED_SYSTEM_ADDRESS; + return rakPeerInterface->GetSystemAddressFromGuid(hostRakNetGuid); +} +RakNetGUID FullyConnectedMesh2::GetHostSystem(void) const +{ + if (ourFCMGuid==0) + return rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + + return hostRakNetGuid; +} +bool FullyConnectedMesh2::IsHostSystem(void) const +{ + return GetHostSystem()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +} +void FullyConnectedMesh2::GetHostOrder(DataStructures::List &hostList) +{ + hostList.Clear(true, _FILE_AND_LINE_); + + if (ourFCMGuid==0 || fcm2ParticipantList.Size()==0) + { + hostList.Push(rakPeerInterface->GetMyGUID(), _FILE_AND_LINE_); + return; + } + + FCM2Participant fcm2; + fcm2.fcm2Guid=ourFCMGuid; + fcm2.rakNetGuid=rakPeerInterface->GetMyGUID(); + + DataStructures::OrderedList olist; + olist.Insert(fcm2, fcm2, true, _FILE_AND_LINE_); + for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) + olist.Insert(fcm2ParticipantList[i], fcm2ParticipantList[i], true, _FILE_AND_LINE_); + + for (unsigned int i=0; i < olist.Size(); i++) + { + hostList.Push(olist[i].rakNetGuid, _FILE_AND_LINE_); + } +} +bool FullyConnectedMesh2::IsConnectedHost(void) const +{ + return GetConnectedHost()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +} +void FullyConnectedMesh2::SetAutoparticipateConnections(bool b) +{ + autoParticipateConnections=b; +} +void FullyConnectedMesh2::ResetHostCalculation(void) +{ + hostRakNetGuid=UNASSIGNED_RAKNET_GUID; + startupTime=RakNet::GetTimeUS(); + totalConnectionCount=0; + ourFCMGuid=0; + for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) + SendFCMGuidRequest(fcm2ParticipantList[i].rakNetGuid); +} +bool FullyConnectedMesh2::AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid ) +{ + for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) + { + if (fcm2ParticipantList[i].rakNetGuid==rakNetGuid) + { + if (theirFCMGuid!=0) + fcm2ParticipantList[i].fcm2Guid=theirFCMGuid; + return false; + } + } + + FCM2Participant participant; + participant.rakNetGuid=rakNetGuid; + participant.fcm2Guid=theirFCMGuid; + fcm2ParticipantList.Push(participant,_FILE_AND_LINE_); + + SendFCMGuidRequest(rakNetGuid); + + return true; +} +void FullyConnectedMesh2::AddParticipant( RakNetGUID rakNetGuid ) +{ + if (rakPeerInterface->GetConnectionState(rakPeerInterface->GetSystemAddressFromGuid(rakNetGuid))!=IS_CONNECTED) + { +#ifdef DEBUG_FCM2 + printf("AddParticipant to %s failed (not connected)\n", rakNetGuid.ToString()); +#endif + return; + } + + AddParticipantInternal(rakNetGuid,0); +} +void FullyConnectedMesh2::GetParticipantList(DataStructures::List &participantList) +{ + participantList.Clear(true, _FILE_AND_LINE_); + unsigned int i; + for (i=0; i < fcm2ParticipantList.Size(); i++) + participantList.Push(fcm2ParticipantList[i].rakNetGuid, _FILE_AND_LINE_); +} +PluginReceiveResult FullyConnectedMesh2::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_REMOTE_NEW_INCOMING_CONNECTION: + { + if (connectOnNewRemoteConnections) + ConnectToRemoteNewIncomingConnections(packet); + } + break; + case ID_FCM2_REQUEST_FCMGUID: + OnRequestFCMGuid(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FCM2_RESPOND_CONNECTION_COUNT: + OnRespondConnectionCount(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FCM2_INFORM_FCMGUID: + OnInformFCMGuid(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT: + OnUpdateMinTotalConnectionCount(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FCM2_NEW_HOST: + if (packet->wasGeneratedLocally==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + break; + } + return RR_CONTINUE_PROCESSING; +} +void FullyConnectedMesh2::OnRakPeerStartup(void) +{ + Clear(); + startupTime=RakNet::GetTimeUS(); +} +void FullyConnectedMesh2::OnAttach(void) +{ + Clear(); + // In case Startup() was called first + if (rakPeerInterface->IsActive()) + startupTime=RakNet::GetTimeUS(); +} +void FullyConnectedMesh2::OnRakPeerShutdown(void) +{ + Clear(); + startupTime=0; +} +void FullyConnectedMesh2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + (void) rakNetGUID; + + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + { + if (fcm2ParticipantList[idx].rakNetGuid==rakNetGUID) + { + fcm2ParticipantList[idx]=fcm2ParticipantList[fcm2ParticipantList.Size()-1]; +#ifdef DEBUG_FCM2 + printf("Popping participant %s\n", fcm2ParticipantList[fcm2ParticipantList.Size()-1].rakNetGuid.ToString()); +#endif + + fcm2ParticipantList.Pop(); + if (rakNetGUID==hostRakNetGuid && ourFCMGuid!=0) + { + if (fcm2ParticipantList.Size()==0) + { + hostRakNetGuid=rakPeerInterface->GetMyGUID(); + hostFCM2Guid=ourFCMGuid; + } + else + { + CalculateHost(&hostRakNetGuid, &hostFCM2Guid); + } + PushNewHost(hostRakNetGuid, rakNetGUID); + } + return; + } + } + +} +RakNet::TimeUS FullyConnectedMesh2::GetElapsedRuntime(void) +{ + RakNet::TimeUS curTime=RakNet::GetTimeUS(); + if (curTime>startupTime) + return curTime-startupTime; + else + return 0; +} +void FullyConnectedMesh2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) isIncoming; + (void) rakNetGUID; + (void) systemAddress; + + if (autoParticipateConnections) + AddParticipant(rakNetGUID); +} +void FullyConnectedMesh2::Clear(void) +{ + fcm2ParticipantList.Clear(false, _FILE_AND_LINE_); + + totalConnectionCount=0; + ourFCMGuid=0; + lastPushedHost=UNASSIGNED_RAKNET_GUID; +} +void FullyConnectedMesh2::PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(oldHost)); + RakNet::BitStream bs(p->data,p->length,false); + bs.SetWriteOffset(0); + bs.Write((MessageID)ID_FCM2_NEW_HOST); + bs.Write(oldHost); + p->systemAddress=rakPeerInterface->GetSystemAddressFromGuid(guid); + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=guid; + p->wasGeneratedLocally=true; + rakPeerInterface->PushBackPacket(p, true); + + lastPushedHost=guid; +} +void FullyConnectedMesh2::SendFCMGuidRequest(RakNetGUID rakNetGuid) +{ + if (rakNetGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return; + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_FCM2_REQUEST_FCMGUID); + if (ourFCMGuid==0) + { + bsOut.Write(false); + bsOut.Write(GetElapsedRuntime()); + } + else + { + bsOut.Write(true); + bsOut.Write(totalConnectionCount); + bsOut.Write(ourFCMGuid); + } + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,rakNetGuid,false); +} +void FullyConnectedMesh2::SendOurFCMGuid(SystemAddress addr) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_FCM2_INFORM_FCMGUID); + RakAssert(ourFCMGuid!=0); // Can't inform others of our FCM2Guid if it's unset! + bsOut.Write(ourFCMGuid); + bsOut.Write(totalConnectionCount); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false); +} +void FullyConnectedMesh2::SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_FCM2_RESPOND_CONNECTION_COUNT); + bsOut.Write(responseTotalConnectionCount); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false); +} +void FullyConnectedMesh2::AssignOurFCMGuid(void) +{ + // Only assigned once ever + RakAssert(ourFCMGuid==0); + unsigned int randomNumber = randomMT(); + randomNumber ^= (unsigned int) (RakNet::GetTimeUS() & 0xFFFFFFFF); + randomNumber ^= (unsigned int) (rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).g & 0xFFFFFFFF); + ourFCMGuid |= randomNumber; + uint64_t reponse64 = totalConnectionCount; + ourFCMGuid |= reponse64<<32; +} +void FullyConnectedMesh2::CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid) +{ + // Can't calculate host without knowing our own + RakAssert(ourFCMGuid!=0); + + // Can't calculate host without being connected to anyone else + RakAssert(fcm2ParticipantList.Size()>0); + + // Return the lowest value of all FCM2Guid + FCM2Guid lowestFCMGuid=ourFCMGuid; + // SystemAddress associatedSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; + RakNetGUID associatedRakNetGuid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + { + if (fcm2ParticipantList[idx].fcm2Guid!=0 && fcm2ParticipantList[idx].fcm2Guiddata,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bool hasRemoteFCMGuid=false; + bsIn.Read(hasRemoteFCMGuid); + RakNet::TimeUS senderElapsedRuntime=0; + unsigned int remoteTotalConnectionCount=0; + FCM2Guid theirFCMGuid=0; + if (hasRemoteFCMGuid) + { + bsIn.Read(remoteTotalConnectionCount); + bsIn.Read(theirFCMGuid); + } + else + { + bsIn.Read(senderElapsedRuntime); + } + AddParticipantInternal(packet->guid,theirFCMGuid); + if (ourFCMGuid==0) + { + if (hasRemoteFCMGuid==false) + { + // Nobody has a fcmGuid + + RakNet::TimeUS ourElapsedRuntime = GetElapsedRuntime(); + if (ourElapsedRuntime>senderElapsedRuntime) + { + // We are probably host + SendConnectionCountResponse(packet->systemAddress, 2); + } + else + { + // They are probably host + SendConnectionCountResponse(packet->systemAddress, 1); + } + } + else + { + // They have a fcmGuid, we do not + IncrementTotalConnectionCount(remoteTotalConnectionCount+1); + + AssignOurFCMGuid(); + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); + } + } + else + { + if (hasRemoteFCMGuid==false) + { + // We have a fcmGuid they do not + SendConnectionCountResponse(packet->systemAddress, totalConnectionCount+1); + } + else + { + // We both have fcmGuids + IncrementTotalConnectionCount(remoteTotalConnectionCount); + + SendOurFCMGuid(packet->systemAddress); + } + } + CalculateAndPushHost(); +} +void FullyConnectedMesh2::OnRespondConnectionCount(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + unsigned int responseTotalConnectionCount; + bsIn.Read(responseTotalConnectionCount); + IncrementTotalConnectionCount(responseTotalConnectionCount); + bool wasAssigned; + if (ourFCMGuid==0) + { + wasAssigned=true; + AssignOurFCMGuid(); + } + else + wasAssigned=false; + + // 1 is returned to give us lower priority, but the actual minimum is 2 + IncrementTotalConnectionCount(2); + + if (wasAssigned==true) + { + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); + CalculateAndPushHost(); + } +} +void FullyConnectedMesh2::OnInformFCMGuid(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + + FCM2Guid theirFCMGuid; + unsigned int theirTotalConnectionCount; + bsIn.Read(theirFCMGuid); + bsIn.Read(theirTotalConnectionCount); + IncrementTotalConnectionCount(theirTotalConnectionCount); + + if (AddParticipantInternal(packet->guid,theirFCMGuid)) + { + // 1/19/2010 - Relay increased total connection count in case new participant only connects to part of the mesh + unsigned int idx; + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT); + bsOut.Write(totalConnectionCount); + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + { + if (packet->guid!=fcm2ParticipantList[idx].rakNetGuid) + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,fcm2ParticipantList[idx].rakNetGuid,false); + } + } + + if (ourFCMGuid==0) + { + AssignOurFCMGuid(); + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); + } + + CalculateAndPushHost(); +} +void FullyConnectedMesh2::OnUpdateMinTotalConnectionCount(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + unsigned int newMin; + bsIn.Read(newMin); + IncrementTotalConnectionCount(newMin); +} +void FullyConnectedMesh2::GetParticipantCount(unsigned int *participantListSize) const +{ + *participantListSize=fcm2ParticipantList.Size(); +} + +unsigned int FullyConnectedMesh2::GetParticipantCount(void) const +{ + return fcm2ParticipantList.Size(); +} +void FullyConnectedMesh2::CalculateAndPushHost(void) +{ + RakNetGUID newHostGuid; + FCM2Guid newFcmGuid; + if (ParticipantListComplete()) + { + CalculateHost(&newHostGuid, &newFcmGuid); + if (newHostGuid!=lastPushedHost) + { + hostRakNetGuid=newHostGuid; + hostFCM2Guid=newFcmGuid; + PushNewHost(hostRakNetGuid, hostRakNetGuid); + } + } +} +bool FullyConnectedMesh2::ParticipantListComplete(void) +{ + for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) + { + if (fcm2ParticipantList[i].fcm2Guid==0) + return false; + } + return true; +} +void FullyConnectedMesh2::IncrementTotalConnectionCount(unsigned int i) +{ + if (i>totalConnectionCount) + { + totalConnectionCount=i; + // printf("totalConnectionCount=%i\n",i); + } +} +void FullyConnectedMesh2::SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw) +{ + connectOnNewRemoteConnections=attemptConnection; + connectionPassword=pw; +} + +void FullyConnectedMesh2::ConnectToRemoteNewIncomingConnections(Packet *packet) +{ + unsigned int count; + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bsIn.Read(count); + SystemAddress remoteAddress; + RakNetGUID remoteGuid; + char str[64]; + for (unsigned int i=0; i < count; i++) + { + bsIn.Read(remoteAddress); + bsIn.Read(remoteGuid); + remoteAddress.ToString(false,str); + rakPeerInterface->Connect(str,remoteAddress.GetPort(),connectionPassword.C_String(),(int) connectionPassword.GetLength()); + } +} +unsigned int FullyConnectedMesh2::GetTotalConnectionCount(void) const +{ + return totalConnectionCount; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/FullyConnectedMesh2.h b/project/lib_projects/raknet/jni/RaknetSources/FullyConnectedMesh2.h index f3859ea..a8f94b1 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/FullyConnectedMesh2.h +++ b/project/lib_projects/raknet/jni/RaknetSources/FullyConnectedMesh2.h @@ -1,253 +1,253 @@ -/// \file FullyConnectedMesh2.h -/// \brief Fully connected mesh plugin, revision 2. -/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FullyConnectedMesh2==1 - -#ifndef __FULLY_CONNECTED_MESH_2_H -#define __FULLY_CONNECTED_MESH_2_H - -#include "PluginInterface2.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" -#include "DS_List.h" -#include "RakString.h" - -typedef int64_t FCM2Guid; - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \brief Fully connected mesh plugin, revision 2 -/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.
-/// It will also calculate which system has been running longest, to find out who should be host, if you need one system to act as a host -/// \pre You must also install the ConnectionGraph2 plugin in order to use SetConnectOnNewRemoteConnection() -/// \ingroup FULLY_CONNECTED_MESH_GROUP -class RAK_DLL_EXPORT FullyConnectedMesh2 : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FullyConnectedMesh2) - - FullyConnectedMesh2(); - virtual ~FullyConnectedMesh2(); - - /// When the message ID_REMOTE_NEW_INCOMING_CONNECTION arrives, we try to connect to that system - /// If \a attemptConnection is false, you can manually connect to all systems listed in ID_REMOTE_NEW_INCOMING_CONNECTION with ConnectToRemoteNewIncomingConnections() - /// \note This will not work on any console. It will also not work if NAT punchthrough is needed. Generally, this should be false and you should connect manually. It is here for legacy reasons. - /// \param[in] attemptConnection If true, we try to connect to any systems we are notified about with ID_REMOTE_NEW_INCOMING_CONNECTION, which comes from the ConnectionGraph2 plugin. Defaults to true. - /// \param[in] pw The password to use to connect with. Only used if \a attemptConnection is true - void SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw); - - /// \brief The connected host is whichever system we are connected to that has been running the longest. - /// \details Will return UNASSIGNED_RAKNET_GUID if we are not connected to anyone, or if we are connected and are calculating the host - /// If includeCalculating is true, will return the estimated calculated host as long as the calculation is nearly complete - /// includeCalculating should be true if you are taking action based on another system becoming host, because not all host calculations may complete at the exact same time - /// \sa ConnectionGraph2::GetLowestAveragePingSystem() . If you need one system in the peer to peer group to relay data, have the host call this function after host migration, and use that system - /// \return System address of whichever system is host. - RakNetGUID GetConnectedHost(void) const; - SystemAddress GetConnectedHostAddr(void) const; - - /// \return System address of whichever system is host. Always returns something, even though it may be our own system. - RakNetGUID GetHostSystem(void) const; - - /// \return If our system is host - bool IsHostSystem(void) const; - - /// Get the list of connected systems, from oldest connected to newest - /// This is also the order that the hosts will be chosen in - void GetHostOrder(DataStructures::List &hostList); - - /// \param[in] includeCalculating If true, and we are currently calculating a new host, return the new host if the calculation is nearly complete - /// \return If our system is host - bool IsConnectedHost(void) const; - - /// \brief Automatically add new connections to the fully connected mesh. - /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function - /// \details Defaults to true. - /// \param[in] b As stated - void SetAutoparticipateConnections(bool b); - - /// Clear our own host order, and recalculate as if we had just reconnected - /// Call this to reset the running time of the host just before joining/creating a game room for networking - void ResetHostCalculation(void); - - /// \brief if SetAutoparticipateConnections() is called with false, then you need to use AddParticipant before these systems will be added to the mesh - /// FullyConnectedMesh2 will track who is the who host among a fully connected mesh of participants - /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function - /// \param[in] participant The new participant - void AddParticipant(RakNetGUID rakNetGuid); - - /// Get the participants added with AddParticipant() - /// \param[out] participantList Participants added with AddParticipant(); - void GetParticipantList(DataStructures::List &participantList); - - /// Connect to all systems from ID_REMOTE_NEW_INCOMING_CONNECTION - /// You can call this if SetConnectOnNewRemoteConnection is false - /// \param[in] packet The packet containing ID_REMOTE_NEW_INCOMING_CONNECTION - /// \param[in] connectionPassword Password passed to RakPeerInterface::Connect() - /// \param[in] connectionPasswordLength Password length passed to RakPeerInterface::Connect() - void ConnectToRemoteNewIncomingConnections(Packet *packet); - - /// \brief Clear all memory and reset everything - void Clear(void); - - unsigned int GetParticipantCount(void) const; - void GetParticipantCount(unsigned int *participantListSize) const; - /// \internal - RakNet::TimeUS GetElapsedRuntime(void); - - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal - virtual void OnRakPeerStartup(void); - /// \internal - virtual void OnAttach(void); - /// \internal - virtual void OnRakPeerShutdown(void); - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - - /// \internal - struct FCM2Participant - { - FCM2Participant() {} - FCM2Participant(const FCM2Guid &_fcm2Guid, const RakNetGUID &_rakNetGuid) : fcm2Guid(_fcm2Guid), rakNetGuid(_rakNetGuid) {} - - // Low half is a random number. - // High half is the order we connected in (totalConnectionCount) - FCM2Guid fcm2Guid; - RakNetGUID rakNetGuid; - }; - - /// \internal for debugging - unsigned int GetTotalConnectionCount(void) const; - -protected: - void PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost); - void SendOurFCMGuid(SystemAddress addr); - void SendFCMGuidRequest(RakNetGUID rakNetGuid); - void SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount); - void OnRequestFCMGuid(Packet *packet); - void OnRespondConnectionCount(Packet *packet); - void OnInformFCMGuid(Packet *packet); - void OnUpdateMinTotalConnectionCount(Packet *packet); - void AssignOurFCMGuid(void); - void CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid); - bool AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid ); - void CalculateAndPushHost(void); - bool ParticipantListComplete(void); - void IncrementTotalConnectionCount(unsigned int i); - - // Used to track how long RakNet has been running. This is so we know who has been running longest - RakNet::TimeUS startupTime; - - // Option for SetAutoparticipateConnections - bool autoParticipateConnections; - - // totalConnectionCount is roughly maintained across all systems, and increments by 1 each time a new system connects to the mesh - // It is always kept at the highest known value - // It is used as the high 4 bytes for new FCMGuids. This causes newer values of FCM2Guid to be higher than lower values. The lowest value is the host. - unsigned int totalConnectionCount; - - // Our own ourFCMGuid. Starts at unassigned (0). Assigned once we send ID_FCM2_REQUEST_FCMGUID and get back ID_FCM2_RESPOND_CONNECTION_COUNT - FCM2Guid ourFCMGuid; - - /// List of systems we know the FCM2Guid for - DataStructures::List fcm2ParticipantList; - - RakNetGUID lastPushedHost; - - // Optimization: Store last calculated host in these variables. - RakNetGUID hostRakNetGuid; - FCM2Guid hostFCM2Guid; - - RakNet::RakString connectionPassword; - bool connectOnNewRemoteConnections; -}; - -} // namespace RakNet - -/* -Startup() -ourFCMGuid=unknown -totalConnectionCount=0 -Set startupTime - -AddParticipant() -if (sender by guid is a participant) -return; -AddParticipantInternal(guid); -if (ourFCMGuid==unknown) -Send to that system a request for their fcmGuid, totalConnectionCount. Inform startupTime. -else -Send to that system a request for their fcmGuid. Inform total connection count, our fcmGuid - -OnRequestGuid() -if (sender by guid is not a participant) -{ - // They added us as a participant, but we didn't add them. This can be caused by lag where both participants are not added at the same time. - // It doesn't affect the outcome as long as we still process the data - AddParticipantInternal(guid); -} -if (ourFCMGuid==unknown) -{ - if (includedStartupTime) - { - // Nobody has a fcmGuid - - if (their startup time is greater than our startup time) - ReplyConnectionCount(1); - else - ReplyConnectionCount(2); - } - else - { - // They have a fcmGuid, we do not - - SetMaxTotalConnectionCount(remoteCount); - AssignTheirGuid() - GenerateOurGuid(); - SendOurGuid(all); - } -} -else -{ - if (includedStartupTime) - { - // We have a fcmGuid they do not - - ReplyConnectionCount(totalConnectionCount+1); - SendOurGuid(sender); - } - else - { - // We both have fcmGuids - - SetMaxTotalConnectionCount(remoteCount); - AssignTheirGuid(); - SendOurGuid(sender); - } -} - -OnReplyConnectionCount() -SetMaxTotalConnectionCount(remoteCount); -GenerateOurGuid(); -SendOurGuid(allParticipants); - -OnReceiveTheirGuid() -AssignTheirGuid() -*/ - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file FullyConnectedMesh2.h +/// \brief Fully connected mesh plugin, revision 2. +/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FullyConnectedMesh2==1 + +#ifndef __FULLY_CONNECTED_MESH_2_H +#define __FULLY_CONNECTED_MESH_2_H + +#include "PluginInterface2.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" +#include "DS_List.h" +#include "RakString.h" + +typedef int64_t FCM2Guid; + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief Fully connected mesh plugin, revision 2 +/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.
+/// It will also calculate which system has been running longest, to find out who should be host, if you need one system to act as a host +/// \pre You must also install the ConnectionGraph2 plugin in order to use SetConnectOnNewRemoteConnection() +/// \ingroup FULLY_CONNECTED_MESH_GROUP +class RAK_DLL_EXPORT FullyConnectedMesh2 : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FullyConnectedMesh2) + + FullyConnectedMesh2(); + virtual ~FullyConnectedMesh2(); + + /// When the message ID_REMOTE_NEW_INCOMING_CONNECTION arrives, we try to connect to that system + /// If \a attemptConnection is false, you can manually connect to all systems listed in ID_REMOTE_NEW_INCOMING_CONNECTION with ConnectToRemoteNewIncomingConnections() + /// \note This will not work on any console. It will also not work if NAT punchthrough is needed. Generally, this should be false and you should connect manually. It is here for legacy reasons. + /// \param[in] attemptConnection If true, we try to connect to any systems we are notified about with ID_REMOTE_NEW_INCOMING_CONNECTION, which comes from the ConnectionGraph2 plugin. Defaults to true. + /// \param[in] pw The password to use to connect with. Only used if \a attemptConnection is true + void SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw); + + /// \brief The connected host is whichever system we are connected to that has been running the longest. + /// \details Will return UNASSIGNED_RAKNET_GUID if we are not connected to anyone, or if we are connected and are calculating the host + /// If includeCalculating is true, will return the estimated calculated host as long as the calculation is nearly complete + /// includeCalculating should be true if you are taking action based on another system becoming host, because not all host calculations may complete at the exact same time + /// \sa ConnectionGraph2::GetLowestAveragePingSystem() . If you need one system in the peer to peer group to relay data, have the host call this function after host migration, and use that system + /// \return System address of whichever system is host. + RakNetGUID GetConnectedHost(void) const; + SystemAddress GetConnectedHostAddr(void) const; + + /// \return System address of whichever system is host. Always returns something, even though it may be our own system. + RakNetGUID GetHostSystem(void) const; + + /// \return If our system is host + bool IsHostSystem(void) const; + + /// Get the list of connected systems, from oldest connected to newest + /// This is also the order that the hosts will be chosen in + void GetHostOrder(DataStructures::List &hostList); + + /// \param[in] includeCalculating If true, and we are currently calculating a new host, return the new host if the calculation is nearly complete + /// \return If our system is host + bool IsConnectedHost(void) const; + + /// \brief Automatically add new connections to the fully connected mesh. + /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function + /// \details Defaults to true. + /// \param[in] b As stated + void SetAutoparticipateConnections(bool b); + + /// Clear our own host order, and recalculate as if we had just reconnected + /// Call this to reset the running time of the host just before joining/creating a game room for networking + void ResetHostCalculation(void); + + /// \brief if SetAutoparticipateConnections() is called with false, then you need to use AddParticipant before these systems will be added to the mesh + /// FullyConnectedMesh2 will track who is the who host among a fully connected mesh of participants + /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function + /// \param[in] participant The new participant + void AddParticipant(RakNetGUID rakNetGuid); + + /// Get the participants added with AddParticipant() + /// \param[out] participantList Participants added with AddParticipant(); + void GetParticipantList(DataStructures::List &participantList); + + /// Connect to all systems from ID_REMOTE_NEW_INCOMING_CONNECTION + /// You can call this if SetConnectOnNewRemoteConnection is false + /// \param[in] packet The packet containing ID_REMOTE_NEW_INCOMING_CONNECTION + /// \param[in] connectionPassword Password passed to RakPeerInterface::Connect() + /// \param[in] connectionPasswordLength Password length passed to RakPeerInterface::Connect() + void ConnectToRemoteNewIncomingConnections(Packet *packet); + + /// \brief Clear all memory and reset everything + void Clear(void); + + unsigned int GetParticipantCount(void) const; + void GetParticipantCount(unsigned int *participantListSize) const; + /// \internal + RakNet::TimeUS GetElapsedRuntime(void); + + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal + virtual void OnRakPeerStartup(void); + /// \internal + virtual void OnAttach(void); + /// \internal + virtual void OnRakPeerShutdown(void); + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + + /// \internal + struct FCM2Participant + { + FCM2Participant() {} + FCM2Participant(const FCM2Guid &_fcm2Guid, const RakNetGUID &_rakNetGuid) : fcm2Guid(_fcm2Guid), rakNetGuid(_rakNetGuid) {} + + // Low half is a random number. + // High half is the order we connected in (totalConnectionCount) + FCM2Guid fcm2Guid; + RakNetGUID rakNetGuid; + }; + + /// \internal for debugging + unsigned int GetTotalConnectionCount(void) const; + +protected: + void PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost); + void SendOurFCMGuid(SystemAddress addr); + void SendFCMGuidRequest(RakNetGUID rakNetGuid); + void SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount); + void OnRequestFCMGuid(Packet *packet); + void OnRespondConnectionCount(Packet *packet); + void OnInformFCMGuid(Packet *packet); + void OnUpdateMinTotalConnectionCount(Packet *packet); + void AssignOurFCMGuid(void); + void CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid); + bool AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid ); + void CalculateAndPushHost(void); + bool ParticipantListComplete(void); + void IncrementTotalConnectionCount(unsigned int i); + + // Used to track how long RakNet has been running. This is so we know who has been running longest + RakNet::TimeUS startupTime; + + // Option for SetAutoparticipateConnections + bool autoParticipateConnections; + + // totalConnectionCount is roughly maintained across all systems, and increments by 1 each time a new system connects to the mesh + // It is always kept at the highest known value + // It is used as the high 4 bytes for new FCMGuids. This causes newer values of FCM2Guid to be higher than lower values. The lowest value is the host. + unsigned int totalConnectionCount; + + // Our own ourFCMGuid. Starts at unassigned (0). Assigned once we send ID_FCM2_REQUEST_FCMGUID and get back ID_FCM2_RESPOND_CONNECTION_COUNT + FCM2Guid ourFCMGuid; + + /// List of systems we know the FCM2Guid for + DataStructures::List fcm2ParticipantList; + + RakNetGUID lastPushedHost; + + // Optimization: Store last calculated host in these variables. + RakNetGUID hostRakNetGuid; + FCM2Guid hostFCM2Guid; + + RakNet::RakString connectionPassword; + bool connectOnNewRemoteConnections; +}; + +} // namespace RakNet + +/* +Startup() +ourFCMGuid=unknown +totalConnectionCount=0 +Set startupTime + +AddParticipant() +if (sender by guid is a participant) +return; +AddParticipantInternal(guid); +if (ourFCMGuid==unknown) +Send to that system a request for their fcmGuid, totalConnectionCount. Inform startupTime. +else +Send to that system a request for their fcmGuid. Inform total connection count, our fcmGuid + +OnRequestGuid() +if (sender by guid is not a participant) +{ + // They added us as a participant, but we didn't add them. This can be caused by lag where both participants are not added at the same time. + // It doesn't affect the outcome as long as we still process the data + AddParticipantInternal(guid); +} +if (ourFCMGuid==unknown) +{ + if (includedStartupTime) + { + // Nobody has a fcmGuid + + if (their startup time is greater than our startup time) + ReplyConnectionCount(1); + else + ReplyConnectionCount(2); + } + else + { + // They have a fcmGuid, we do not + + SetMaxTotalConnectionCount(remoteCount); + AssignTheirGuid() + GenerateOurGuid(); + SendOurGuid(all); + } +} +else +{ + if (includedStartupTime) + { + // We have a fcmGuid they do not + + ReplyConnectionCount(totalConnectionCount+1); + SendOurGuid(sender); + } + else + { + // We both have fcmGuids + + SetMaxTotalConnectionCount(remoteCount); + AssignTheirGuid(); + SendOurGuid(sender); + } +} + +OnReplyConnectionCount() +SetMaxTotalConnectionCount(remoteCount); +GenerateOurGuid(); +SendOurGuid(allParticipants); + +OnReceiveTheirGuid() +AssignTheirGuid() +*/ + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/GetTime.cpp b/project/lib_projects/raknet/jni/RaknetSources/GetTime.cpp index 4f868f8..deec092 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/GetTime.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/GetTime.cpp @@ -1,220 +1,220 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#if defined(_WIN32) -#include "WindowsIncludes.h" -// To call timeGetTime -// on Code::Blocks, this needs to be libwinmm.a instead -#pragma comment(lib, "Winmm.lib") -#endif - -#include "GetTime.h" - - - - -#if defined(_WIN32) -DWORD mProcMask; -DWORD mSysMask; -HANDLE mThread; - - - - - - - - - -#else -#include -#include -RakNet::TimeUS initialTime; -#endif - -static bool initialized=false; - -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 -#include "SimpleMutex.h" -RakNet::TimeUS lastNormalizedReturnedValue=0; -RakNet::TimeUS lastNormalizedInputValue=0; -/// This constraints timer forward jumps to 1 second, and does not let it jump backwards -/// See http://support.microsoft.com/kb/274323 where the timer can sometimes jump forward by hours or days -/// This also has the effect where debugging a sending system won't treat the time spent halted past 1 second as elapsed network time -RakNet::TimeUS NormalizeTime(RakNet::TimeUS timeIn) -{ - RakNet::TimeUS diff, lastNormalizedReturnedValueCopy; - static RakNet::SimpleMutex mutex; - - mutex.Lock(); - if (timeIn>=lastNormalizedInputValue) - { - diff = timeIn-lastNormalizedInputValue; - if (diff > GET_TIME_SPIKE_LIMIT) - lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT; - else - lastNormalizedReturnedValue+=diff; - } - else - lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT; - - lastNormalizedInputValue=timeIn; - lastNormalizedReturnedValueCopy=lastNormalizedReturnedValue; - mutex.Unlock(); - - return lastNormalizedReturnedValueCopy; -} -#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 -RakNet::Time RakNet::GetTime( void ) -{ - return (RakNet::Time)(GetTimeUS()/1000); -} -RakNet::TimeMS RakNet::GetTimeMS( void ) -{ - return (RakNet::TimeMS)(GetTimeUS()/1000); -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#if defined(_WIN32) -RakNet::TimeUS GetTimeUS_Windows( void ) -{ - if ( initialized == false) - { - initialized = true; - - // Save the current process -#if !defined(_WIN32_WCE) - HANDLE mProc = GetCurrentProcess(); - - // Get the current Affinity -#if _MSC_VER >= 1400 && defined (_M_X64) - GetProcessAffinityMask(mProc, (PDWORD_PTR)&mProcMask, (PDWORD_PTR)&mSysMask); -#else - GetProcessAffinityMask(mProc, &mProcMask, &mSysMask); -#endif - mThread = GetCurrentThread(); - -#endif // _WIN32_WCE - } - - // 9/26/2010 In China running LuDaShi, QueryPerformanceFrequency has to be called every time because CPU clock speeds can be different - RakNet::TimeUS curTime; - LARGE_INTEGER PerfVal; - LARGE_INTEGER yo1; - - QueryPerformanceFrequency( &yo1 ); - QueryPerformanceCounter( &PerfVal ); - - __int64 quotient, remainder; - quotient=((PerfVal.QuadPart) / yo1.QuadPart); - remainder=((PerfVal.QuadPart) % yo1.QuadPart); - curTime = (RakNet::TimeUS) quotient*(RakNet::TimeUS)1000000 + (remainder*(RakNet::TimeUS)1000000 / yo1.QuadPart); - -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 - return NormalizeTime(curTime); -#else - return curTime; -#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 -} -#elif defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__) -RakNet::TimeUS GetTimeUS_Linux( void ) -{ - timeval tp; - if ( initialized == false) - { - gettimeofday( &tp, 0 ); - initialized=true; - // I do this because otherwise RakNet::Time in milliseconds won't work as it will underflow when dividing by 1000 to do the conversion - initialTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec ); - } - - // GCC - RakNet::TimeUS curTime; - gettimeofday( &tp, 0 ); - - curTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec ); - -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 - return NormalizeTime(curTime - initialTime); -#else - return curTime - initialTime; -#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 -} -#endif - -RakNet::TimeUS RakNet::GetTimeUS( void ) -{ - - - - - - -#if defined(_WIN32) - return GetTimeUS_Windows(); -#else - return GetTimeUS_Linux(); -#endif -} -bool RakNet::GreaterThan(RakNet::Time a, RakNet::Time b) -{ - // a > b? - const RakNet::Time halfSpan =(RakNet::Time) (((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2); - return b!=a && b-a>halfSpan; -} -bool RakNet::LessThan(RakNet::Time a, RakNet::Time b) -{ - // a < b? - const RakNet::Time halfSpan = ((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2; - return b!=a && b-a +#include +RakNet::TimeUS initialTime; +#endif + +static bool initialized=false; + +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 +#include "SimpleMutex.h" +RakNet::TimeUS lastNormalizedReturnedValue=0; +RakNet::TimeUS lastNormalizedInputValue=0; +/// This constraints timer forward jumps to 1 second, and does not let it jump backwards +/// See http://support.microsoft.com/kb/274323 where the timer can sometimes jump forward by hours or days +/// This also has the effect where debugging a sending system won't treat the time spent halted past 1 second as elapsed network time +RakNet::TimeUS NormalizeTime(RakNet::TimeUS timeIn) +{ + RakNet::TimeUS diff, lastNormalizedReturnedValueCopy; + static RakNet::SimpleMutex mutex; + + mutex.Lock(); + if (timeIn>=lastNormalizedInputValue) + { + diff = timeIn-lastNormalizedInputValue; + if (diff > GET_TIME_SPIKE_LIMIT) + lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT; + else + lastNormalizedReturnedValue+=diff; + } + else + lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT; + + lastNormalizedInputValue=timeIn; + lastNormalizedReturnedValueCopy=lastNormalizedReturnedValue; + mutex.Unlock(); + + return lastNormalizedReturnedValueCopy; +} +#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 +RakNet::Time RakNet::GetTime( void ) +{ + return (RakNet::Time)(GetTimeUS()/1000); +} +RakNet::TimeMS RakNet::GetTimeMS( void ) +{ + return (RakNet::TimeMS)(GetTimeUS()/1000); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if defined(_WIN32) +RakNet::TimeUS GetTimeUS_Windows( void ) +{ + if ( initialized == false) + { + initialized = true; + + // Save the current process +#if !defined(_WIN32_WCE) + HANDLE mProc = GetCurrentProcess(); + + // Get the current Affinity +#if _MSC_VER >= 1400 && defined (_M_X64) + GetProcessAffinityMask(mProc, (PDWORD_PTR)&mProcMask, (PDWORD_PTR)&mSysMask); +#else + GetProcessAffinityMask(mProc, &mProcMask, &mSysMask); +#endif + mThread = GetCurrentThread(); + +#endif // _WIN32_WCE + } + + // 9/26/2010 In China running LuDaShi, QueryPerformanceFrequency has to be called every time because CPU clock speeds can be different + RakNet::TimeUS curTime; + LARGE_INTEGER PerfVal; + LARGE_INTEGER yo1; + + QueryPerformanceFrequency( &yo1 ); + QueryPerformanceCounter( &PerfVal ); + + __int64 quotient, remainder; + quotient=((PerfVal.QuadPart) / yo1.QuadPart); + remainder=((PerfVal.QuadPart) % yo1.QuadPart); + curTime = (RakNet::TimeUS) quotient*(RakNet::TimeUS)1000000 + (remainder*(RakNet::TimeUS)1000000 / yo1.QuadPart); + +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 + return NormalizeTime(curTime); +#else + return curTime; +#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 +} +#elif defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__) +RakNet::TimeUS GetTimeUS_Linux( void ) +{ + timeval tp; + if ( initialized == false) + { + gettimeofday( &tp, 0 ); + initialized=true; + // I do this because otherwise RakNet::Time in milliseconds won't work as it will underflow when dividing by 1000 to do the conversion + initialTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec ); + } + + // GCC + RakNet::TimeUS curTime; + gettimeofday( &tp, 0 ); + + curTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec ); + +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 + return NormalizeTime(curTime - initialTime); +#else + return curTime - initialTime; +#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 +} +#endif + +RakNet::TimeUS RakNet::GetTimeUS( void ) +{ + + + + + + +#if defined(_WIN32) + return GetTimeUS_Windows(); +#else + return GetTimeUS_Linux(); +#endif +} +bool RakNet::GreaterThan(RakNet::Time a, RakNet::Time b) +{ + // a > b? + const RakNet::Time halfSpan =(RakNet::Time) (((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2); + return b!=a && b-a>halfSpan; +} +bool RakNet::LessThan(RakNet::Time a, RakNet::Time b) +{ + // a < b? + const RakNet::Time halfSpan = ((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2; + return b!=a && b-a b? - extern RAK_DLL_EXPORT bool GreaterThan(RakNet::Time a, RakNet::Time b); - /// a < b? - extern RAK_DLL_EXPORT bool LessThan(RakNet::Time a, RakNet::Time b); -} - -#endif +/// \file GetTime.h +/// \brief Returns the value from QueryPerformanceCounter. This is the function RakNet uses to represent time. This time won't match the time returned by GetTimeCount(). See http://www.jenkinssoftware.com/forum/index.php?topic=2798.0 +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __GET_TIME_H +#define __GET_TIME_H + +#include "Export.h" +#include "RakNetTime.h" // For RakNet::TimeMS + +namespace RakNet +{ + /// Same as GetTimeMS + /// Holds the time in either a 32 or 64 bit variable, depending on __GET_TIME_64BIT + RakNet::Time RAK_DLL_EXPORT GetTime( void ); + + /// Return the time as 32 bit + /// \note The maximum delta between returned calls is 1 second - however, RakNet calls this constantly anyway. See NormalizeTime() in the cpp. + RakNet::TimeMS RAK_DLL_EXPORT GetTimeMS( void ); + + /// Return the time as 64 bit + /// \note The maximum delta between returned calls is 1 second - however, RakNet calls this constantly anyway. See NormalizeTime() in the cpp. + RakNet::TimeUS RAK_DLL_EXPORT GetTimeUS( void ); + + /// a > b? + extern RAK_DLL_EXPORT bool GreaterThan(RakNet::Time a, RakNet::Time b); + /// a < b? + extern RAK_DLL_EXPORT bool LessThan(RakNet::Time a, RakNet::Time b); +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/Getche.cpp b/project/lib_projects/raknet/jni/RaknetSources/Getche.cpp index c6b6e21..6f8e420 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Getche.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/Getche.cpp @@ -1,27 +1,27 @@ - - -#if defined(_WIN32) -#include /* getche() */ -#elif defined(__S3E__) - -#else - -#include "Getche.h" - -char getche() -{ - - - struct termios oldt, - newt; - char ch; - tcgetattr( STDIN_FILENO, &oldt ); - newt = oldt; - newt.c_lflag &= ~( ICANON | ECHO ); - tcsetattr( STDIN_FILENO, TCSANOW, &newt ); - ch = getchar(); - tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); - return ch; - -} -#endif + + +#if defined(_WIN32) +#include /* getche() */ +#elif defined(__S3E__) + +#else + +#include "Getche.h" + +char getche() +{ + + + struct termios oldt, + newt; + char ch; + tcgetattr( STDIN_FILENO, &oldt ); + newt = oldt; + newt.c_lflag &= ~( ICANON | ECHO ); + tcsetattr( STDIN_FILENO, TCSANOW, &newt ); + ch = getchar(); + tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); + return ch; + +} +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/Getche.h b/project/lib_projects/raknet/jni/RaknetSources/Getche.h index 70326d9..dff2ac3 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Getche.h +++ b/project/lib_projects/raknet/jni/RaknetSources/Getche.h @@ -1,11 +1,11 @@ - - -#if defined(_WIN32) -#include /* getche() */ - -#else -#include -#include -#include -char getche(); -#endif + + +#if defined(_WIN32) +#include /* getche() */ + +#else +#include +#include +#include +char getche(); +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/Gets.cpp b/project/lib_projects/raknet/jni/RaknetSources/Gets.cpp index 2ff468a..be17d65 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Gets.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/Gets.cpp @@ -1,25 +1,25 @@ -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -char * Gets ( char * str, int num ) -{ - fgets(str, num, stdin); - if (str[0]=='\n' || str[0]=='\r') - str[0]=0; - - size_t len=strlen(str); - if (len>0 && (str[len-1]=='\n' || str[len-1]=='\r')) - str[len-1]=0; - if (len>1 && (str[len-2]=='\n' || str[len-2]=='\r')) - str[len-2]=0; - - return str; -} - -#ifdef __cplusplus -} -#endif +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +char * Gets ( char * str, int num ) +{ + fgets(str, num, stdin); + if (str[0]=='\n' || str[0]=='\r') + str[0]=0; + + size_t len=strlen(str); + if (len>0 && (str[len-1]=='\n' || str[len-1]=='\r')) + str[len-1]=0; + if (len>1 && (str[len-2]=='\n' || str[len-2]=='\r')) + str[len-2]=0; + + return str; +} + +#ifdef __cplusplus +} +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/Gets.h b/project/lib_projects/raknet/jni/RaknetSources/Gets.h index 78bcb6d..8925288 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Gets.h +++ b/project/lib_projects/raknet/jni/RaknetSources/Gets.h @@ -1,14 +1,14 @@ -#ifndef __GETS__H_ -#define __GETS__H_ - -#ifdef __cplusplus -extern "C" { -#endif - -char * Gets ( char * str, int num ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef __GETS__H_ +#define __GETS__H_ + +#ifdef __cplusplus +extern "C" { +#endif + +char * Gets ( char * str, int num ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/GridSectorizer.cpp b/project/lib_projects/raknet/jni/RaknetSources/GridSectorizer.cpp index 1fafc30..3fb9870 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/GridSectorizer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/GridSectorizer.cpp @@ -1,191 +1,191 @@ -#include "RakAssert.h" -#include "GridSectorizer.h" -//#include -#include - -GridSectorizer::GridSectorizer() -{ - grid=0; -} -GridSectorizer::~GridSectorizer() -{ - if (grid) - RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_); -} -void GridSectorizer::Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY) -{ - RakAssert(_maxCellWidth > 0.0f && _maxCellHeight > 0.0f); - if (grid) - RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_); - - cellOriginX=minX; - cellOriginY=minY; - gridWidth=maxX-minX; - gridHeight=maxY-minY; - gridCellWidthCount=(int) ceil(gridWidth/_maxCellWidth); - gridCellHeightCount=(int) ceil(gridHeight/_maxCellHeight); - // Make the cells slightly smaller, so we allocate an extra unneeded cell if on the edge. This way we don't go outside the array on rounding errors. - cellWidth=gridWidth/gridCellWidthCount; - cellHeight=gridHeight/gridCellHeightCount; - invCellWidth = 1.0f / cellWidth; - invCellHeight = 1.0f / cellHeight; - -#ifdef _USE_ORDERED_LIST - grid = RakNet::OP_NEW>(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ ); - DataStructures::OrderedList::IMPLEMENT_DEFAULT_COMPARISON(); -#else - grid = RakNet::OP_NEW_ARRAY >(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ ); -#endif -} -void GridSectorizer::AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY) -{ - RakAssert(cellWidth>0.0f); - RakAssert(minX < maxX && minY < maxY); - - int xStart, yStart, xEnd, yEnd, xCur, yCur; - xStart=WorldToCellXOffsetAndClamped(minX); - yStart=WorldToCellYOffsetAndClamped(minY); - xEnd=WorldToCellXOffsetAndClamped(maxX); - yEnd=WorldToCellYOffsetAndClamped(maxY); - - for (xCur=xStart; xCur <= xEnd; ++xCur) - { - for (yCur=yStart; yCur <= yEnd; ++yCur) - { -#ifdef _USE_ORDERED_LIST - grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true); -#else - grid[yCur*gridCellWidthCount+xCur].Insert(entry, _FILE_AND_LINE_); -#endif - } - } -} -#ifdef _USE_ORDERED_LIST -void GridSectorizer::RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY) -{ - RakAssert(cellWidth>0.0f); - RakAssert(minX <= maxX && minY <= maxY); - - int xStart, yStart, xEnd, yEnd, xCur, yCur; - xStart=WorldToCellXOffsetAndClamped(minX); - yStart=WorldToCellYOffsetAndClamped(minY); - xEnd=WorldToCellXOffsetAndClamped(maxX); - yEnd=WorldToCellYOffsetAndClamped(maxY); - - for (xCur=xStart; xCur <= xEnd; ++xCur) - { - for (yCur=yStart; yCur <= yEnd; ++yCur) - { - grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry); - } - } -} -void GridSectorizer::MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY, - const float destMinX, const float destMinY, const float destMaxX, const float destMaxY) -{ - RakAssert(cellWidth>0.0f); - RakAssert(sourceMinX < sourceMaxX && sourceMinY < sourceMaxY); - RakAssert(destMinX < destMaxX && destMinY < destMaxY); - - if (PositionCrossesCells(sourceMinX, sourceMinY, destMinX, destMinY)==false && - PositionCrossesCells(destMinX, destMinY, destMinX, destMinY)==false) - return; - - int xStartSource, yStartSource, xEndSource, yEndSource; - int xStartDest, yStartDest, xEndDest, yEndDest; - int xCur, yCur; - xStartSource=WorldToCellXOffsetAndClamped(sourceMinX); - yStartSource=WorldToCellYOffsetAndClamped(sourceMinY); - xEndSource=WorldToCellXOffsetAndClamped(sourceMaxX); - yEndSource=WorldToCellYOffsetAndClamped(sourceMaxY); - - xStartDest=WorldToCellXOffsetAndClamped(destMinX); - yStartDest=WorldToCellYOffsetAndClamped(destMinY); - xEndDest=WorldToCellXOffsetAndClamped(destMaxX); - yEndDest=WorldToCellYOffsetAndClamped(destMaxY); - - // Remove source that is not in dest - for (xCur=xStartSource; xCur <= xEndSource; ++xCur) - { - for (yCur=yStartSource; yCur <= yEndSource; ++yCur) - { - if (xCur < xStartDest || xCur > xEndDest || - yCur < yStartDest || yCur > yEndDest) - { - grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry); - } - } - } - - // Add dest that is not in source - for (xCur=xStartDest; xCur <= xEndDest; ++xCur) - { - for (yCur=yStartDest; yCur <= yEndDest; ++yCur) - { - if (xCur < xStartSource || xCur > xEndSource || - yCur < yStartSource || yCur > yEndSource) - { - grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true); - } - } - } -} -#endif -void GridSectorizer::GetEntries(DataStructures::List& intersectionList, const float minX, const float minY, const float maxX, const float maxY) -{ -#ifdef _USE_ORDERED_LIST - DataStructures::OrderedList* cell; -#else - DataStructures::List* cell; -#endif - int xStart, yStart, xEnd, yEnd, xCur, yCur; - unsigned index; - xStart=WorldToCellXOffsetAndClamped(minX); - yStart=WorldToCellYOffsetAndClamped(minY); - xEnd=WorldToCellXOffsetAndClamped(maxX); - yEnd=WorldToCellYOffsetAndClamped(maxY); - - intersectionList.Clear(true, _FILE_AND_LINE_); - for (xCur=xStart; xCur <= xEnd; ++xCur) - { - for (yCur=yStart; yCur <= yEnd; ++yCur) - { - cell = grid+yCur*gridCellWidthCount+xCur; - for (index=0; index < cell->Size(); ++index) - intersectionList.Insert(cell->operator [](index), _FILE_AND_LINE_); - } - } -} -bool GridSectorizer::PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const -{ - return originX/cellWidth!=destinationX/cellWidth || originY/cellHeight!=destinationY/cellHeight; -} -int GridSectorizer::WorldToCellX(const float input) const -{ - return (int)((input-cellOriginX)*invCellWidth); -} -int GridSectorizer::WorldToCellY(const float input) const -{ - return (int)((input-cellOriginY)*invCellHeight); -} -int GridSectorizer::WorldToCellXOffsetAndClamped(const float input) const -{ - int cell=WorldToCellX(input); - cell = cell > 0 ? cell : 0; // __max(cell,0); - cell = gridCellWidthCount-1 < cell ? gridCellWidthCount-1 : cell; // __min(gridCellWidthCount-1, cell); - return cell; -} -int GridSectorizer::WorldToCellYOffsetAndClamped(const float input) const -{ - int cell=WorldToCellY(input); - cell = cell > 0 ? cell : 0; // __max(cell,0); - cell = gridCellHeightCount-1 < cell ? gridCellHeightCount-1 : cell; // __min(gridCellHeightCount-1, cell); - return cell; -} -void GridSectorizer::Clear(void) -{ - int cur; - int count = gridCellWidthCount*gridCellHeightCount; - for (cur=0; cur +#include + +GridSectorizer::GridSectorizer() +{ + grid=0; +} +GridSectorizer::~GridSectorizer() +{ + if (grid) + RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_); +} +void GridSectorizer::Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY) +{ + RakAssert(_maxCellWidth > 0.0f && _maxCellHeight > 0.0f); + if (grid) + RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_); + + cellOriginX=minX; + cellOriginY=minY; + gridWidth=maxX-minX; + gridHeight=maxY-minY; + gridCellWidthCount=(int) ceil(gridWidth/_maxCellWidth); + gridCellHeightCount=(int) ceil(gridHeight/_maxCellHeight); + // Make the cells slightly smaller, so we allocate an extra unneeded cell if on the edge. This way we don't go outside the array on rounding errors. + cellWidth=gridWidth/gridCellWidthCount; + cellHeight=gridHeight/gridCellHeightCount; + invCellWidth = 1.0f / cellWidth; + invCellHeight = 1.0f / cellHeight; + +#ifdef _USE_ORDERED_LIST + grid = RakNet::OP_NEW>(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ ); + DataStructures::OrderedList::IMPLEMENT_DEFAULT_COMPARISON(); +#else + grid = RakNet::OP_NEW_ARRAY >(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ ); +#endif +} +void GridSectorizer::AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY) +{ + RakAssert(cellWidth>0.0f); + RakAssert(minX < maxX && minY < maxY); + + int xStart, yStart, xEnd, yEnd, xCur, yCur; + xStart=WorldToCellXOffsetAndClamped(minX); + yStart=WorldToCellYOffsetAndClamped(minY); + xEnd=WorldToCellXOffsetAndClamped(maxX); + yEnd=WorldToCellYOffsetAndClamped(maxY); + + for (xCur=xStart; xCur <= xEnd; ++xCur) + { + for (yCur=yStart; yCur <= yEnd; ++yCur) + { +#ifdef _USE_ORDERED_LIST + grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true); +#else + grid[yCur*gridCellWidthCount+xCur].Insert(entry, _FILE_AND_LINE_); +#endif + } + } +} +#ifdef _USE_ORDERED_LIST +void GridSectorizer::RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY) +{ + RakAssert(cellWidth>0.0f); + RakAssert(minX <= maxX && minY <= maxY); + + int xStart, yStart, xEnd, yEnd, xCur, yCur; + xStart=WorldToCellXOffsetAndClamped(minX); + yStart=WorldToCellYOffsetAndClamped(minY); + xEnd=WorldToCellXOffsetAndClamped(maxX); + yEnd=WorldToCellYOffsetAndClamped(maxY); + + for (xCur=xStart; xCur <= xEnd; ++xCur) + { + for (yCur=yStart; yCur <= yEnd; ++yCur) + { + grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry); + } + } +} +void GridSectorizer::MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY, + const float destMinX, const float destMinY, const float destMaxX, const float destMaxY) +{ + RakAssert(cellWidth>0.0f); + RakAssert(sourceMinX < sourceMaxX && sourceMinY < sourceMaxY); + RakAssert(destMinX < destMaxX && destMinY < destMaxY); + + if (PositionCrossesCells(sourceMinX, sourceMinY, destMinX, destMinY)==false && + PositionCrossesCells(destMinX, destMinY, destMinX, destMinY)==false) + return; + + int xStartSource, yStartSource, xEndSource, yEndSource; + int xStartDest, yStartDest, xEndDest, yEndDest; + int xCur, yCur; + xStartSource=WorldToCellXOffsetAndClamped(sourceMinX); + yStartSource=WorldToCellYOffsetAndClamped(sourceMinY); + xEndSource=WorldToCellXOffsetAndClamped(sourceMaxX); + yEndSource=WorldToCellYOffsetAndClamped(sourceMaxY); + + xStartDest=WorldToCellXOffsetAndClamped(destMinX); + yStartDest=WorldToCellYOffsetAndClamped(destMinY); + xEndDest=WorldToCellXOffsetAndClamped(destMaxX); + yEndDest=WorldToCellYOffsetAndClamped(destMaxY); + + // Remove source that is not in dest + for (xCur=xStartSource; xCur <= xEndSource; ++xCur) + { + for (yCur=yStartSource; yCur <= yEndSource; ++yCur) + { + if (xCur < xStartDest || xCur > xEndDest || + yCur < yStartDest || yCur > yEndDest) + { + grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry); + } + } + } + + // Add dest that is not in source + for (xCur=xStartDest; xCur <= xEndDest; ++xCur) + { + for (yCur=yStartDest; yCur <= yEndDest; ++yCur) + { + if (xCur < xStartSource || xCur > xEndSource || + yCur < yStartSource || yCur > yEndSource) + { + grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true); + } + } + } +} +#endif +void GridSectorizer::GetEntries(DataStructures::List& intersectionList, const float minX, const float minY, const float maxX, const float maxY) +{ +#ifdef _USE_ORDERED_LIST + DataStructures::OrderedList* cell; +#else + DataStructures::List* cell; +#endif + int xStart, yStart, xEnd, yEnd, xCur, yCur; + unsigned index; + xStart=WorldToCellXOffsetAndClamped(minX); + yStart=WorldToCellYOffsetAndClamped(minY); + xEnd=WorldToCellXOffsetAndClamped(maxX); + yEnd=WorldToCellYOffsetAndClamped(maxY); + + intersectionList.Clear(true, _FILE_AND_LINE_); + for (xCur=xStart; xCur <= xEnd; ++xCur) + { + for (yCur=yStart; yCur <= yEnd; ++yCur) + { + cell = grid+yCur*gridCellWidthCount+xCur; + for (index=0; index < cell->Size(); ++index) + intersectionList.Insert(cell->operator [](index), _FILE_AND_LINE_); + } + } +} +bool GridSectorizer::PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const +{ + return originX/cellWidth!=destinationX/cellWidth || originY/cellHeight!=destinationY/cellHeight; +} +int GridSectorizer::WorldToCellX(const float input) const +{ + return (int)((input-cellOriginX)*invCellWidth); +} +int GridSectorizer::WorldToCellY(const float input) const +{ + return (int)((input-cellOriginY)*invCellHeight); +} +int GridSectorizer::WorldToCellXOffsetAndClamped(const float input) const +{ + int cell=WorldToCellX(input); + cell = cell > 0 ? cell : 0; // __max(cell,0); + cell = gridCellWidthCount-1 < cell ? gridCellWidthCount-1 : cell; // __min(gridCellWidthCount-1, cell); + return cell; +} +int GridSectorizer::WorldToCellYOffsetAndClamped(const float input) const +{ + int cell=WorldToCellY(input); + cell = cell > 0 ? cell : 0; // __max(cell,0); + cell = gridCellHeightCount-1 < cell ? gridCellHeightCount-1 : cell; // __min(gridCellHeightCount-1, cell); + return cell; +} +void GridSectorizer::Clear(void) +{ + int cur; + int count = gridCellWidthCount*gridCellHeightCount; + for (cur=0; cur& intersectionList, const float minX, const float minY, const float maxX, const float maxY); - - void Clear(void); - -protected: - int WorldToCellX(const float input) const; - int WorldToCellY(const float input) const; - int WorldToCellXOffsetAndClamped(const float input) const; - int WorldToCellYOffsetAndClamped(const float input) const; - - // Returns true or false if a position crosses cells in the grid. If false, you don't need to move entries - bool PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const; - - float cellOriginX, cellOriginY; - float cellWidth, cellHeight; - float invCellWidth, invCellHeight; - float gridWidth, gridHeight; - int gridCellWidthCount, gridCellHeightCount; - - - // int gridWidth, gridHeight; - -#ifdef _USE_ORDERED_LIST - DataStructures::OrderedList* grid; -#else - DataStructures::List* grid; -#endif -}; - -#endif +#ifndef _GRID_SECTORIZER_H +#define _GRID_SECTORIZER_H + +//#define _USE_ORDERED_LIST + +#include "RakMemoryOverride.h" + +#ifdef _USE_ORDERED_LIST +#include "DS_OrderedList.h" +#else +#include "DS_List.h" +#endif + +class GridSectorizer +{ +public: + GridSectorizer(); + ~GridSectorizer(); + + // _cellWidth, _cellHeight is the width and height of each cell in world units + // minX, minY, maxX, maxY are the world dimensions (can be changed to dynamically allocate later if needed) + void Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY); + + // Adds a pointer to the grid with bounding rectangle dimensions + void AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY); + +#ifdef _USE_ORDERED_LIST + + // Removes a pointer, as above + void RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY); + + // Adds and removes in one pass, more efficient than calling both functions consecutively + void MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY, + const float destMinX, const float destMinY, const float destMaxX, const float destMaxY); + +#endif + + // Adds to intersectionList all entries in a certain radius + void GetEntries(DataStructures::List& intersectionList, const float minX, const float minY, const float maxX, const float maxY); + + void Clear(void); + +protected: + int WorldToCellX(const float input) const; + int WorldToCellY(const float input) const; + int WorldToCellXOffsetAndClamped(const float input) const; + int WorldToCellYOffsetAndClamped(const float input) const; + + // Returns true or false if a position crosses cells in the grid. If false, you don't need to move entries + bool PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const; + + float cellOriginX, cellOriginY; + float cellWidth, cellHeight; + float invCellWidth, invCellHeight; + float gridWidth, gridHeight; + int gridCellWidthCount, gridCellHeightCount; + + + // int gridWidth, gridHeight; + +#ifdef _USE_ORDERED_LIST + DataStructures::OrderedList* grid; +#else + DataStructures::List* grid; +#endif +}; + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/HTTPConnection.cpp b/project/lib_projects/raknet/jni/RaknetSources/HTTPConnection.cpp index dd541fb..78b6e1c 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/HTTPConnection.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/HTTPConnection.cpp @@ -1,313 +1,313 @@ -/// \file -/// \brief Contains HTTPConnection, used to communicate with web servers -/// -/// This file is part of RakNet Copyright 2008 Kevin Jenkins. -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. -/// GPL license users are subject to the GNU General Public -/// License as published by the Free -/// Software Foundation; either version 2 of the License, or (at your -/// option) any later version. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "TCPInterface.h" -#include "HTTPConnection.h" -#include "RakSleep.h" -#include "RakString.h" -#include "RakAssert.h" -#include -#include -#include -#include - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(HTTPConnection,HTTPConnection); - -HTTPConnection::HTTPConnection() : connectionState(CS_NONE) -{ - tcp=0; -} - -void HTTPConnection::Init(TCPInterface* _tcp, const char *_host, unsigned short _port) -{ - tcp=_tcp; - host=_host; - port=_port; -} - -void HTTPConnection::Post(const char *remote_path, const char *data, const char *_contentType) -{ - OutgoingCommand op; - op.contentType=_contentType; - op.data=data; - op.remotePath=remote_path; - op.isPost=true; - outgoingCommand.Push(op, _FILE_AND_LINE_ ); - //printf("Adding outgoing post\n"); -} - -void HTTPConnection::Get(const char *path) -{ - OutgoingCommand op; - op.remotePath=path; - op.isPost=false; - outgoingCommand.Push(op, _FILE_AND_LINE_ ); -} - -bool HTTPConnection::HasBadResponse(int *code, RakNet::RakString *data) -{ - if(badResponses.IsEmpty()) - return false; - - if (code) - *code = badResponses.Peek().code; - if (data) - *data = badResponses.Pop().data; - return true; -} -void HTTPConnection::CloseConnection() -{ - connectionState=CS_DISCONNECTING; -} -void HTTPConnection::Update(void) -{ - SystemAddress sa; - sa = tcp->HasCompletedConnectionAttempt(); - while (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { -// printf("Connected\n"); - connectionState=CS_CONNECTED; - server=sa; - sa = tcp->HasCompletedConnectionAttempt(); - } - - sa = tcp->HasFailedConnectionAttempt(); - while (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - //printf("Failed connected\n"); - CloseConnection(); - sa = tcp->HasFailedConnectionAttempt(); - } - - sa = tcp->HasLostConnection(); - while (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - //printf("Lost connection\n"); - CloseConnection(); - sa = tcp->HasLostConnection(); - } - - - switch (connectionState) - { - case CS_NONE: - { - if (outgoingCommand.IsEmpty()) - return; - - //printf("Connecting\n"); - server = tcp->Connect(host, port, false); - connectionState = CS_CONNECTING; - } - break; - case CS_DISCONNECTING: - { - if (tcp->ReceiveHasPackets()==false) - { - if (incomingData.IsEmpty()==false) - { - results.Push(incomingData, _FILE_AND_LINE_ ); - } - incomingData.Clear(); - tcp->CloseConnection(server); - connectionState=CS_NONE; - } - } - break; - case CS_CONNECTING: - { - } - break; - case CS_CONNECTED: - { - //printf("Connected\n"); - if (outgoingCommand.IsEmpty()) - { - //printf("Closed connection (nothing to do)\n"); - CloseConnection(); - return; - } - -#if OPEN_SSL_CLIENT_SUPPORT==1 - tcp->StartSSLClient(server); -#endif - - //printf("Sending request\n"); - currentProcessingCommand = outgoingCommand.Pop(); - RakString request; - if (currentProcessingCommand.isPost) - { - request.Set("POST %s HTTP/1.0\r\n" - "Host: %s:%i\r\n" - "Content-Type: %s\r\n" - "Content-Length: %u\r\n" - "\r\n" - "%s", - currentProcessingCommand.remotePath.C_String(), - host.C_String(), - port, - currentProcessingCommand.contentType.C_String(), - (unsigned) currentProcessingCommand.data.GetLength(), - currentProcessingCommand.data.C_String()); - } - else - { - // request.Set("GET %s\r\n", host.C_String()); - // http://www.jenkinssoftware.com/forum/index.php?topic=4601.0;topicseen - request.Set("GET %s HTTP/1.0\r\n" - "Host: %s:%i\r\n" - "\r\n", - currentProcessingCommand.remotePath.C_String(), - host.C_String(), - port); - } - - // printf(request.C_String()); - // request.URLEncode(); - tcp->Send(request.C_String(), (unsigned int) request.GetLength(), server,false); - connectionState=CS_PROCESSING; - } - break; - case CS_PROCESSING: - { - } - } - -// if (connectionState==CS_PROCESSING && currentProcessingCommand.data.IsEmpty()==false) -// outgoingCommand.PushAtHead(currentProcessingCommand); -} -bool HTTPConnection::HasRead(void) const -{ - return results.IsEmpty()==false; -} -RakString HTTPConnection::Read(void) -{ - if (results.IsEmpty()) - return RakString(); - - RakNet::RakString resultStr = results.Pop(); - // const char *start_of_body = strstr(resultStr.C_String(), "\r\n\r\n"); - const char *start_of_body = strpbrk(resultStr.C_String(), "\001\002\003%"); - - if(start_of_body) - return RakNet::RakString::NonVariadic(start_of_body); - else - return resultStr; -} -SystemAddress HTTPConnection::GetServerAddress(void) const -{ - return server; -} -void HTTPConnection::ProcessTCPPacket(Packet *packet) -{ - RakAssert(packet); - - // read all the packets possible - if(packet->systemAddress == server) - { - if(incomingData.GetLength() == 0) - { - int response_code = atoi((char *)packet->data + strlen("HTTP/1.0 ")); - - if(response_code > 299) - { - badResponses.Push(BadResponse(packet->data, response_code), _FILE_AND_LINE_ ); - //printf("Closed connection (Bad response 2)\n"); - CloseConnection(); - return; - } - } - - RakNet::RakString incomingTemp = RakNet::RakString::NonVariadic((const char*) packet->data); - incomingTemp.URLDecode(); - incomingData += incomingTemp; - - // printf((const char*) packet->data); - // printf("\n"); - - RakAssert(strlen((char *)packet->data) == packet->length); // otherwise it contains Null bytes - - const char *start_of_body = strstr(incomingData, "\r\n\r\n"); - - // besides having the server close the connection, they may - // provide a length header and supply that many bytes - if( - // Why was start_of_body here? Makes the GET command fail - // start_of_body && - connectionState == CS_PROCESSING) - { - /* - // The stupid programmer that wrote this originally didn't think that just because the header contains this value doesn't mean you got the whole message - if (strstr((const char*) packet->data, "\r\nConnection: close\r\n")) - { - CloseConnection(); - } - else - { - */ - long length_of_headers; - if (start_of_body) - { - length_of_headers = (long)(start_of_body + 4 - incomingData.C_String()); - const char *length_header = strstr(incomingData, "\r\nLength: "); - - if(length_header) - { - long length = atol(length_header + 10) + length_of_headers; - - if((long) incomingData.GetLength() >= length) - { - //printf("Closed connection (Got all data due to length header)\n"); - CloseConnection(); - } - } - } - else - { - // No processing needed - } - - - //} - } - } -} - -bool HTTPConnection::IsBusy(void) const -{ - return connectionState != CS_NONE; -} - -int HTTPConnection::GetState(void) const -{ - return connectionState; -} - - -HTTPConnection::~HTTPConnection(void) -{ - if (tcp) - tcp->CloseConnection(server); -} - - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains HTTPConnection, used to communicate with web servers +/// +/// This file is part of RakNet Copyright 2008 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "TCPInterface.h" +#include "HTTPConnection.h" +#include "RakSleep.h" +#include "RakString.h" +#include "RakAssert.h" +#include +#include +#include +#include + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(HTTPConnection,HTTPConnection); + +HTTPConnection::HTTPConnection() : connectionState(CS_NONE) +{ + tcp=0; +} + +void HTTPConnection::Init(TCPInterface* _tcp, const char *_host, unsigned short _port) +{ + tcp=_tcp; + host=_host; + port=_port; +} + +void HTTPConnection::Post(const char *remote_path, const char *data, const char *_contentType) +{ + OutgoingCommand op; + op.contentType=_contentType; + op.data=data; + op.remotePath=remote_path; + op.isPost=true; + outgoingCommand.Push(op, _FILE_AND_LINE_ ); + //printf("Adding outgoing post\n"); +} + +void HTTPConnection::Get(const char *path) +{ + OutgoingCommand op; + op.remotePath=path; + op.isPost=false; + outgoingCommand.Push(op, _FILE_AND_LINE_ ); +} + +bool HTTPConnection::HasBadResponse(int *code, RakNet::RakString *data) +{ + if(badResponses.IsEmpty()) + return false; + + if (code) + *code = badResponses.Peek().code; + if (data) + *data = badResponses.Pop().data; + return true; +} +void HTTPConnection::CloseConnection() +{ + connectionState=CS_DISCONNECTING; +} +void HTTPConnection::Update(void) +{ + SystemAddress sa; + sa = tcp->HasCompletedConnectionAttempt(); + while (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { +// printf("Connected\n"); + connectionState=CS_CONNECTED; + server=sa; + sa = tcp->HasCompletedConnectionAttempt(); + } + + sa = tcp->HasFailedConnectionAttempt(); + while (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + //printf("Failed connected\n"); + CloseConnection(); + sa = tcp->HasFailedConnectionAttempt(); + } + + sa = tcp->HasLostConnection(); + while (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + //printf("Lost connection\n"); + CloseConnection(); + sa = tcp->HasLostConnection(); + } + + + switch (connectionState) + { + case CS_NONE: + { + if (outgoingCommand.IsEmpty()) + return; + + //printf("Connecting\n"); + server = tcp->Connect(host, port, false); + connectionState = CS_CONNECTING; + } + break; + case CS_DISCONNECTING: + { + if (tcp->ReceiveHasPackets()==false) + { + if (incomingData.IsEmpty()==false) + { + results.Push(incomingData, _FILE_AND_LINE_ ); + } + incomingData.Clear(); + tcp->CloseConnection(server); + connectionState=CS_NONE; + } + } + break; + case CS_CONNECTING: + { + } + break; + case CS_CONNECTED: + { + //printf("Connected\n"); + if (outgoingCommand.IsEmpty()) + { + //printf("Closed connection (nothing to do)\n"); + CloseConnection(); + return; + } + +#if OPEN_SSL_CLIENT_SUPPORT==1 + tcp->StartSSLClient(server); +#endif + + //printf("Sending request\n"); + currentProcessingCommand = outgoingCommand.Pop(); + RakString request; + if (currentProcessingCommand.isPost) + { + request.Set("POST %s HTTP/1.0\r\n" + "Host: %s:%i\r\n" + "Content-Type: %s\r\n" + "Content-Length: %u\r\n" + "\r\n" + "%s", + currentProcessingCommand.remotePath.C_String(), + host.C_String(), + port, + currentProcessingCommand.contentType.C_String(), + (unsigned) currentProcessingCommand.data.GetLength(), + currentProcessingCommand.data.C_String()); + } + else + { + // request.Set("GET %s\r\n", host.C_String()); + // http://www.jenkinssoftware.com/forum/index.php?topic=4601.0;topicseen + request.Set("GET %s HTTP/1.0\r\n" + "Host: %s:%i\r\n" + "\r\n", + currentProcessingCommand.remotePath.C_String(), + host.C_String(), + port); + } + + // printf(request.C_String()); + // request.URLEncode(); + tcp->Send(request.C_String(), (unsigned int) request.GetLength(), server,false); + connectionState=CS_PROCESSING; + } + break; + case CS_PROCESSING: + { + } + } + +// if (connectionState==CS_PROCESSING && currentProcessingCommand.data.IsEmpty()==false) +// outgoingCommand.PushAtHead(currentProcessingCommand); +} +bool HTTPConnection::HasRead(void) const +{ + return results.IsEmpty()==false; +} +RakString HTTPConnection::Read(void) +{ + if (results.IsEmpty()) + return RakString(); + + RakNet::RakString resultStr = results.Pop(); + // const char *start_of_body = strstr(resultStr.C_String(), "\r\n\r\n"); + const char *start_of_body = strpbrk(resultStr.C_String(), "\001\002\003%"); + + if(start_of_body) + return RakNet::RakString::NonVariadic(start_of_body); + else + return resultStr; +} +SystemAddress HTTPConnection::GetServerAddress(void) const +{ + return server; +} +void HTTPConnection::ProcessTCPPacket(Packet *packet) +{ + RakAssert(packet); + + // read all the packets possible + if(packet->systemAddress == server) + { + if(incomingData.GetLength() == 0) + { + int response_code = atoi((char *)packet->data + strlen("HTTP/1.0 ")); + + if(response_code > 299) + { + badResponses.Push(BadResponse(packet->data, response_code), _FILE_AND_LINE_ ); + //printf("Closed connection (Bad response 2)\n"); + CloseConnection(); + return; + } + } + + RakNet::RakString incomingTemp = RakNet::RakString::NonVariadic((const char*) packet->data); + incomingTemp.URLDecode(); + incomingData += incomingTemp; + + // printf((const char*) packet->data); + // printf("\n"); + + RakAssert(strlen((char *)packet->data) == packet->length); // otherwise it contains Null bytes + + const char *start_of_body = strstr(incomingData, "\r\n\r\n"); + + // besides having the server close the connection, they may + // provide a length header and supply that many bytes + if( + // Why was start_of_body here? Makes the GET command fail + // start_of_body && + connectionState == CS_PROCESSING) + { + /* + // The stupid programmer that wrote this originally didn't think that just because the header contains this value doesn't mean you got the whole message + if (strstr((const char*) packet->data, "\r\nConnection: close\r\n")) + { + CloseConnection(); + } + else + { + */ + long length_of_headers; + if (start_of_body) + { + length_of_headers = (long)(start_of_body + 4 - incomingData.C_String()); + const char *length_header = strstr(incomingData, "\r\nLength: "); + + if(length_header) + { + long length = atol(length_header + 10) + length_of_headers; + + if((long) incomingData.GetLength() >= length) + { + //printf("Closed connection (Got all data due to length header)\n"); + CloseConnection(); + } + } + } + else + { + // No processing needed + } + + + //} + } + } +} + +bool HTTPConnection::IsBusy(void) const +{ + return connectionState != CS_NONE; +} + +int HTTPConnection::GetState(void) const +{ + return connectionState; +} + + +HTTPConnection::~HTTPConnection(void) +{ + if (tcp) + tcp->CloseConnection(server); +} + + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/HTTPConnection.h b/project/lib_projects/raknet/jni/RaknetSources/HTTPConnection.h index cd1b0c6..b2e3921 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/HTTPConnection.h +++ b/project/lib_projects/raknet/jni/RaknetSources/HTTPConnection.h @@ -1,176 +1,176 @@ -/// \file HTTPConnection.h -/// \brief Contains HTTPConnection, used to communicate with web servers -/// -/// This file is part of RakNet Copyright 2008 Kevin Jenkins. -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. -/// GPL license users are subject to the GNU General Public -/// License as published by the Free -/// Software Foundation; either version 2 of the License, or (at your -/// option) any later version. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#ifndef __HTTP_CONNECTION -#define __HTTP_CONNECTION - -#include "Export.h" -#include "RakString.h" -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "DS_Queue.h" - -namespace RakNet -{ -/// Forward declarations -class TCPInterface; -struct SystemAddress; - -/// \brief Use HTTPConnection to communicate with a web server. -/// \details Start an instance of TCPInterface via the Start() command. -/// Instantiate a new instance of HTTPConnection, and associate TCPInterface with the class in the constructor. -/// Use Post() to send commands to the web server, and ProcessDataPacket() to update the connection with packets returned from TCPInterface that have the system address of the web server -/// This class will handle connecting and reconnecting as necessary. -/// -/// Note that only one Post() can be handled at a time. -class RAK_DLL_EXPORT HTTPConnection -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(HTTPConnection) - - /// Returns a HTTP object associated with this tcp connection - HTTPConnection(); - virtual ~HTTPConnection(); - - /// \pre tcp should already be started - void Init(TCPInterface *_tcp, const char *host, unsigned short port=80); - - /// Submit data to the HTTP server - /// HTTP only allows one request at a time per connection - /// - /// \pre IsBusy()==false - /// \param path the path on the remote server you want to POST to. For example "index.html" - /// \param data A NULL terminated string to submit to the server - /// \param contentType "Content-Type:" passed to post. - void Post(const char *path, const char *data, const char *_contentType="application/x-www-form-urlencoded"); - - /// Get a file from a webserver - /// \param path the path on the remote server you want to GET from. For example "index.html" - void Get(const char *path); - - /// Is there a Read result ready? - bool HasRead(void) const; - - /// Get one result from the server - /// \pre HasResult must return true - RakNet::RakString Read(void); - - /// Call periodically to do time-based updates - void Update(void); - - /// Returns the address of the server we are connected to - SystemAddress GetServerAddress(void) const; - - /// Process an HTTP data packet returned from TCPInterface - /// Returns true when we have gotten all the data from the HTTP server. - /// If this returns true then it's safe to Post() another request - /// Deallocate the packet as usual via TCPInterface - /// \param packet NULL or a packet associated with our host and port - void ProcessTCPPacket(Packet *packet); - - /// Results of HTTP requests. Standard response codes are < 999 - /// ( define HTTP codes and our internal codes as needed ) - enum ResponseCodes { NoBody=1001, OK=200, Deleted=1002 }; - - HTTPConnection& operator=(const HTTPConnection& rhs){(void) rhs; return *this;} - - /// Encapsulates a raw HTTP response and response code - struct BadResponse - { - public: - BadResponse() {code=0;} - - BadResponse(const unsigned char *_data, int _code) - : data((const char *)_data), code(_code) {} - - BadResponse(const char *_data, int _code) - : data(_data), code(_code) {} - - operator int () const { return code; } - - RakNet::RakString data; - int code; // ResponseCodes - }; - - /// Queued events of failed exchanges with the HTTP server - bool HasBadResponse(int *code, RakNet::RakString *data); - - /// Returns false if the connection is not doing anything else - bool IsBusy(void) const; - - /// \internal - int GetState(void) const; - - struct OutgoingCommand - { - RakNet::RakString remotePath; - RakNet::RakString data; - RakNet::RakString contentType; - bool isPost; - }; - - DataStructures::Queue outgoingCommand; - OutgoingCommand currentProcessingCommand; - -private: - SystemAddress server; - TCPInterface *tcp; - RakNet::RakString host; - unsigned short port; - DataStructures::Queue badResponses; - - enum ConnectionState - { - CS_NONE, - CS_DISCONNECTING, - CS_CONNECTING, - CS_CONNECTED, - CS_PROCESSING, - } connectionState; - - RakNet::RakString incomingData; - DataStructures::Queue results; - - void CloseConnection(); - - /* - enum { RAK_HTTP_INITIAL, - RAK_HTTP_STARTING, - RAK_HTTP_CONNECTING, - RAK_HTTP_ESTABLISHED, - RAK_HTTP_REQUEST_SENT, - RAK_HTTP_IDLE } state; - - RakNet::RakString outgoing, incoming, path, contentType; - void Process(Packet *packet); // the workhorse - - // this helps check the various status lists in TCPInterface - typedef SystemAddress (TCPInterface::*StatusCheckFunction)(void); - bool InList(StatusCheckFunction func); - */ - -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file HTTPConnection.h +/// \brief Contains HTTPConnection, used to communicate with web servers +/// +/// This file is part of RakNet Copyright 2008 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#ifndef __HTTP_CONNECTION +#define __HTTP_CONNECTION + +#include "Export.h" +#include "RakString.h" +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "DS_Queue.h" + +namespace RakNet +{ +/// Forward declarations +class TCPInterface; +struct SystemAddress; + +/// \brief Use HTTPConnection to communicate with a web server. +/// \details Start an instance of TCPInterface via the Start() command. +/// Instantiate a new instance of HTTPConnection, and associate TCPInterface with the class in the constructor. +/// Use Post() to send commands to the web server, and ProcessDataPacket() to update the connection with packets returned from TCPInterface that have the system address of the web server +/// This class will handle connecting and reconnecting as necessary. +/// +/// Note that only one Post() can be handled at a time. +class RAK_DLL_EXPORT HTTPConnection +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(HTTPConnection) + + /// Returns a HTTP object associated with this tcp connection + HTTPConnection(); + virtual ~HTTPConnection(); + + /// \pre tcp should already be started + void Init(TCPInterface *_tcp, const char *host, unsigned short port=80); + + /// Submit data to the HTTP server + /// HTTP only allows one request at a time per connection + /// + /// \pre IsBusy()==false + /// \param path the path on the remote server you want to POST to. For example "index.html" + /// \param data A NULL terminated string to submit to the server + /// \param contentType "Content-Type:" passed to post. + void Post(const char *path, const char *data, const char *_contentType="application/x-www-form-urlencoded"); + + /// Get a file from a webserver + /// \param path the path on the remote server you want to GET from. For example "index.html" + void Get(const char *path); + + /// Is there a Read result ready? + bool HasRead(void) const; + + /// Get one result from the server + /// \pre HasResult must return true + RakNet::RakString Read(void); + + /// Call periodically to do time-based updates + void Update(void); + + /// Returns the address of the server we are connected to + SystemAddress GetServerAddress(void) const; + + /// Process an HTTP data packet returned from TCPInterface + /// Returns true when we have gotten all the data from the HTTP server. + /// If this returns true then it's safe to Post() another request + /// Deallocate the packet as usual via TCPInterface + /// \param packet NULL or a packet associated with our host and port + void ProcessTCPPacket(Packet *packet); + + /// Results of HTTP requests. Standard response codes are < 999 + /// ( define HTTP codes and our internal codes as needed ) + enum ResponseCodes { NoBody=1001, OK=200, Deleted=1002 }; + + HTTPConnection& operator=(const HTTPConnection& rhs){(void) rhs; return *this;} + + /// Encapsulates a raw HTTP response and response code + struct BadResponse + { + public: + BadResponse() {code=0;} + + BadResponse(const unsigned char *_data, int _code) + : data((const char *)_data), code(_code) {} + + BadResponse(const char *_data, int _code) + : data(_data), code(_code) {} + + operator int () const { return code; } + + RakNet::RakString data; + int code; // ResponseCodes + }; + + /// Queued events of failed exchanges with the HTTP server + bool HasBadResponse(int *code, RakNet::RakString *data); + + /// Returns false if the connection is not doing anything else + bool IsBusy(void) const; + + /// \internal + int GetState(void) const; + + struct OutgoingCommand + { + RakNet::RakString remotePath; + RakNet::RakString data; + RakNet::RakString contentType; + bool isPost; + }; + + DataStructures::Queue outgoingCommand; + OutgoingCommand currentProcessingCommand; + +private: + SystemAddress server; + TCPInterface *tcp; + RakNet::RakString host; + unsigned short port; + DataStructures::Queue badResponses; + + enum ConnectionState + { + CS_NONE, + CS_DISCONNECTING, + CS_CONNECTING, + CS_CONNECTED, + CS_PROCESSING, + } connectionState; + + RakNet::RakString incomingData; + DataStructures::Queue results; + + void CloseConnection(); + + /* + enum { RAK_HTTP_INITIAL, + RAK_HTTP_STARTING, + RAK_HTTP_CONNECTING, + RAK_HTTP_ESTABLISHED, + RAK_HTTP_REQUEST_SENT, + RAK_HTTP_IDLE } state; + + RakNet::RakString outgoing, incoming, path, contentType; + void Process(Packet *packet); // the workhorse + + // this helps check the various status lists in TCPInterface + typedef SystemAddress (TCPInterface::*StatusCheckFunction)(void); + bool InList(StatusCheckFunction func); + */ + +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/IncrementalReadInterface.cpp b/project/lib_projects/raknet/jni/RaknetSources/IncrementalReadInterface.cpp index 2809380..238f8a9 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/IncrementalReadInterface.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/IncrementalReadInterface.cpp @@ -1,15 +1,15 @@ -#include "IncrementalReadInterface.h" -#include - -using namespace RakNet; - -unsigned int IncrementalReadInterface::GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context) -{ - FILE *fp = fopen(filename, "rb"); - if (fp==0) - return 0; - fseek(fp,startReadBytes,SEEK_SET); - unsigned int numRead = (unsigned int) fread(preallocatedDestination,1,numBytesToRead, fp); - fclose(fp); - return numRead; -} +#include "IncrementalReadInterface.h" +#include + +using namespace RakNet; + +unsigned int IncrementalReadInterface::GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context) +{ + FILE *fp = fopen(filename, "rb"); + if (fp==0) + return 0; + fseek(fp,startReadBytes,SEEK_SET); + unsigned int numRead = (unsigned int) fread(preallocatedDestination,1,numBytesToRead, fp); + fclose(fp); + return numRead; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/IncrementalReadInterface.h b/project/lib_projects/raknet/jni/RaknetSources/IncrementalReadInterface.h index 0bf56a8..3f396e4 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/IncrementalReadInterface.h +++ b/project/lib_projects/raknet/jni/RaknetSources/IncrementalReadInterface.h @@ -1,28 +1,28 @@ -#ifndef __INCREMENTAL_READ_INTERFACE_H -#define __INCREMENTAL_READ_INTERFACE_H - -#include "FileListNodeContext.h" -#include "Export.h" - -namespace RakNet -{ - -class RAK_DLL_EXPORT IncrementalReadInterface -{ -public: - IncrementalReadInterface() {} - virtual ~IncrementalReadInterface() {} - - /// Read part of a file into \a destination - /// Return the number of bytes written. Return 0 when file is done. - /// \param[in] filename Filename to read - /// \param[in] startReadBytes What offset from the start of the file to read from - /// \param[in] numBytesToRead How many bytes to read. This is also how many bytes have been allocated to preallocatedDestination - /// \param[out] preallocatedDestination Write your data here - /// \return The number of bytes read, or 0 if none - virtual unsigned int GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context); -}; - -} // namespace RakNet - -#endif +#ifndef __INCREMENTAL_READ_INTERFACE_H +#define __INCREMENTAL_READ_INTERFACE_H + +#include "FileListNodeContext.h" +#include "Export.h" + +namespace RakNet +{ + +class RAK_DLL_EXPORT IncrementalReadInterface +{ +public: + IncrementalReadInterface() {} + virtual ~IncrementalReadInterface() {} + + /// Read part of a file into \a destination + /// Return the number of bytes written. Return 0 when file is done. + /// \param[in] filename Filename to read + /// \param[in] startReadBytes What offset from the start of the file to read from + /// \param[in] numBytesToRead How many bytes to read. This is also how many bytes have been allocated to preallocatedDestination + /// \param[out] preallocatedDestination Write your data here + /// \return The number of bytes read, or 0 if none + virtual unsigned int GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context); +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/InternalPacket.h b/project/lib_projects/raknet/jni/RaknetSources/InternalPacket.h index 4461cce..790fbbe 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/InternalPacket.h +++ b/project/lib_projects/raknet/jni/RaknetSources/InternalPacket.h @@ -1,131 +1,131 @@ -/// \file -/// \brief \b [Internal] A class which stores a user message, and all information associated with sending and receiving that message. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. -/// GPL license users are subject to the GNU General Public -/// License as published by the Free -/// Software Foundation; either version 2 of the License, or (at your -/// option) any later version. - -#ifndef __INTERNAL_PACKET_H -#define __INTERNAL_PACKET_H - -#include "PacketPriority.h" -#include "RakNetTypes.h" -#include "RakMemoryOverride.h" -#include "RakNetDefines.h" -#include "NativeTypes.h" -#include "RakNetDefines.h" -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 -#include "CCRakNetUDT.h" -#else -#include "CCRakNetSlidingWindow.h" -#endif - -namespace RakNet { - -typedef uint16_t SplitPacketIdType; -typedef uint32_t SplitPacketIndexType; - -/// This is the counter used for holding packet numbers, so we can detect duplicate packets. It should be large enough that if the variables -/// Internally assumed to be 4 bytes, but written as 3 bytes in ReliabilityLayer::WriteToBitStreamFromInternalPacket -typedef uint24_t MessageNumberType; - -/// This is the counter used for holding ordered packet numbers, so we can detect out-of-order packets. It should be large enough that if the variables -/// were to wrap, the newly wrapped values would no longer be in use. Warning: Too large of a value wastes bandwidth! -typedef MessageNumberType OrderingIndexType; - -typedef RakNet::TimeUS RemoteSystemTimeType; - -struct InternalPacketFixedSizeTransmissionHeader -{ - /// A unique numerical identifier given to this user message. Used to identify reliable messages on the network - MessageNumberType reliableMessageNumber; - ///The ID used as identification for ordering messages. Also included in sequenced messages - OrderingIndexType orderingIndex; - // Used only with sequenced messages - OrderingIndexType sequencingIndex; - ///What ordering channel this packet is on, if the reliability type uses ordering channels - unsigned char orderingChannel; - ///The ID of the split packet, if we have split packets. This is the maximum number of split messages we can send simultaneously per connection. - SplitPacketIdType splitPacketId; - ///If this is a split packet, the index into the array of subsplit packets - SplitPacketIndexType splitPacketIndex; - ///The size of the array of subsplit packets - SplitPacketIndexType splitPacketCount;; - ///How many bits long the data is - BitSize_t dataBitLength; - ///What type of reliability algorithm to use with this packet - PacketReliability reliability; - // Not endian safe - // unsigned char priority : 3; - // unsigned char reliability : 5; -}; - -/// Used in InternalPacket when pointing to sharedDataBlock, rather than allocating itself -struct InternalPacketRefCountedData -{ - unsigned char *sharedDataBlock; - unsigned int refCount; -}; - -/// Holds a user message, and related information -/// Don't use a constructor or destructor, due to the memory pool I am using -struct InternalPacket : public InternalPacketFixedSizeTransmissionHeader -{ - /// Identifies the order in which this number was sent. Used locally - MessageNumberType messageInternalOrder; - /// Has this message number been assigned yet? We don't assign until the message is actually sent. - /// This fixes a bug where pre-determining message numbers and then sending a message on a different channel creates a huge gap. - /// This causes performance problems and causes those messages to timeout. - bool messageNumberAssigned; - /// Was this packet number used this update to track windowing drops or increases? Each packet number is only used once per update. -// bool allowWindowUpdate; - ///When this packet was created - RakNet::TimeUS creationTime; - ///The resendNext time to take action on this packet - RakNet::TimeUS nextActionTime; - // Size of the header when encoded into a bitstream - BitSize_t headerLength; - /// Buffer is a pointer to the actual data, assuming this packet has data at all - unsigned char *data; - /// How to alloc and delete the data member - enum AllocationScheme - { - /// Data is allocated using rakMalloc. Just free it - NORMAL, - - /// data points to a larger block of data, where the larger block is reference counted. internalPacketRefCountedData is used in this case - REF_COUNTED, - - /// If allocation scheme is STACK, data points to stackData and should not be deallocated - /// This is only used when sending. Received packets are deallocated in RakPeer - STACK - } allocationScheme; - InternalPacketRefCountedData *refCountedData; - /// How many attempts we made at sending this message -// unsigned char timesSent; - /// The priority level of this packet - PacketPriority priority; - /// If the reliability type requires a receipt, then return this number with it - uint32_t sendReceiptSerial; - - // Used for the resend queue - // Linked list implementation so I can remove from the list via a pointer, without finding it in the list - InternalPacket *resendPrev, *resendNext,*unreliablePrev,*unreliableNext; - - unsigned char stackData[128]; -}; - -} // namespace RakNet - -#endif - +/// \file +/// \brief \b [Internal] A class which stores a user message, and all information associated with sending and receiving that message. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __INTERNAL_PACKET_H +#define __INTERNAL_PACKET_H + +#include "PacketPriority.h" +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" +#include "RakNetDefines.h" +#include "NativeTypes.h" +#include "RakNetDefines.h" +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 +#include "CCRakNetUDT.h" +#else +#include "CCRakNetSlidingWindow.h" +#endif + +namespace RakNet { + +typedef uint16_t SplitPacketIdType; +typedef uint32_t SplitPacketIndexType; + +/// This is the counter used for holding packet numbers, so we can detect duplicate packets. It should be large enough that if the variables +/// Internally assumed to be 4 bytes, but written as 3 bytes in ReliabilityLayer::WriteToBitStreamFromInternalPacket +typedef uint24_t MessageNumberType; + +/// This is the counter used for holding ordered packet numbers, so we can detect out-of-order packets. It should be large enough that if the variables +/// were to wrap, the newly wrapped values would no longer be in use. Warning: Too large of a value wastes bandwidth! +typedef MessageNumberType OrderingIndexType; + +typedef RakNet::TimeUS RemoteSystemTimeType; + +struct InternalPacketFixedSizeTransmissionHeader +{ + /// A unique numerical identifier given to this user message. Used to identify reliable messages on the network + MessageNumberType reliableMessageNumber; + ///The ID used as identification for ordering messages. Also included in sequenced messages + OrderingIndexType orderingIndex; + // Used only with sequenced messages + OrderingIndexType sequencingIndex; + ///What ordering channel this packet is on, if the reliability type uses ordering channels + unsigned char orderingChannel; + ///The ID of the split packet, if we have split packets. This is the maximum number of split messages we can send simultaneously per connection. + SplitPacketIdType splitPacketId; + ///If this is a split packet, the index into the array of subsplit packets + SplitPacketIndexType splitPacketIndex; + ///The size of the array of subsplit packets + SplitPacketIndexType splitPacketCount;; + ///How many bits long the data is + BitSize_t dataBitLength; + ///What type of reliability algorithm to use with this packet + PacketReliability reliability; + // Not endian safe + // unsigned char priority : 3; + // unsigned char reliability : 5; +}; + +/// Used in InternalPacket when pointing to sharedDataBlock, rather than allocating itself +struct InternalPacketRefCountedData +{ + unsigned char *sharedDataBlock; + unsigned int refCount; +}; + +/// Holds a user message, and related information +/// Don't use a constructor or destructor, due to the memory pool I am using +struct InternalPacket : public InternalPacketFixedSizeTransmissionHeader +{ + /// Identifies the order in which this number was sent. Used locally + MessageNumberType messageInternalOrder; + /// Has this message number been assigned yet? We don't assign until the message is actually sent. + /// This fixes a bug where pre-determining message numbers and then sending a message on a different channel creates a huge gap. + /// This causes performance problems and causes those messages to timeout. + bool messageNumberAssigned; + /// Was this packet number used this update to track windowing drops or increases? Each packet number is only used once per update. +// bool allowWindowUpdate; + ///When this packet was created + RakNet::TimeUS creationTime; + ///The resendNext time to take action on this packet + RakNet::TimeUS nextActionTime; + // Size of the header when encoded into a bitstream + BitSize_t headerLength; + /// Buffer is a pointer to the actual data, assuming this packet has data at all + unsigned char *data; + /// How to alloc and delete the data member + enum AllocationScheme + { + /// Data is allocated using rakMalloc. Just free it + NORMAL, + + /// data points to a larger block of data, where the larger block is reference counted. internalPacketRefCountedData is used in this case + REF_COUNTED, + + /// If allocation scheme is STACK, data points to stackData and should not be deallocated + /// This is only used when sending. Received packets are deallocated in RakPeer + STACK + } allocationScheme; + InternalPacketRefCountedData *refCountedData; + /// How many attempts we made at sending this message +// unsigned char timesSent; + /// The priority level of this packet + PacketPriority priority; + /// If the reliability type requires a receipt, then return this number with it + uint32_t sendReceiptSerial; + + // Used for the resend queue + // Linked list implementation so I can remove from the list via a pointer, without finding it in the list + InternalPacket *resendPrev, *resendNext,*unreliablePrev,*unreliableNext; + + unsigned char stackData[128]; +}; + +} // namespace RakNet + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/Itoa.cpp b/project/lib_projects/raknet/jni/RaknetSources/Itoa.cpp index c5dc1a7..0d8a7ae 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Itoa.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/Itoa.cpp @@ -1,52 +1,52 @@ -#ifdef __cplusplus -extern "C" { -#endif - -// Fast itoa from http://www.jb.man.ac.uk/~slowe/cpp/itoa.html for Linux since it seems like Linux doesn't support this function. -// I modified it to remove the std dependencies. -char* Itoa( int value, char* result, int base ) - { - // check that the base if valid - if (base < 2 || base > 16) { *result = 0; return result; } - char* out = result; - int quotient = value; - - int absQModB; - - do { - // KevinJ - get rid of this dependency - //*out = "0123456789abcdef"[ std::abs( quotient % base ) ]; - absQModB=quotient % base; - if (absQModB < 0) - absQModB=-absQModB; - *out = "0123456789abcdef"[ absQModB ]; - ++out; - quotient /= base; - } while ( quotient ); - - // Only apply negative sign for base 10 - if ( value < 0 && base == 10) *out++ = '-'; - - // KevinJ - get rid of this dependency - // std::reverse( result, out ); - *out = 0; - - // KevinJ - My own reverse code - char *start = result; - char temp; - out--; - while (start < out) - { - temp=*start; - *start=*out; - *out=temp; - start++; - out--; - } - - return result; -} - -#ifdef __cplusplus -} -#endif +#ifdef __cplusplus +extern "C" { +#endif + +// Fast itoa from http://www.jb.man.ac.uk/~slowe/cpp/itoa.html for Linux since it seems like Linux doesn't support this function. +// I modified it to remove the std dependencies. +char* Itoa( int value, char* result, int base ) + { + // check that the base if valid + if (base < 2 || base > 16) { *result = 0; return result; } + char* out = result; + int quotient = value; + + int absQModB; + + do { + // KevinJ - get rid of this dependency + //*out = "0123456789abcdef"[ std::abs( quotient % base ) ]; + absQModB=quotient % base; + if (absQModB < 0) + absQModB=-absQModB; + *out = "0123456789abcdef"[ absQModB ]; + ++out; + quotient /= base; + } while ( quotient ); + + // Only apply negative sign for base 10 + if ( value < 0 && base == 10) *out++ = '-'; + + // KevinJ - get rid of this dependency + // std::reverse( result, out ); + *out = 0; + + // KevinJ - My own reverse code + char *start = result; + char temp; + out--; + while (start < out) + { + temp=*start; + *start=*out; + *out=temp; + start++; + out--; + } + + return result; +} + +#ifdef __cplusplus +} +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/Itoa.h b/project/lib_projects/raknet/jni/RaknetSources/Itoa.h index 1b41ad7..d7eb6a3 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Itoa.h +++ b/project/lib_projects/raknet/jni/RaknetSources/Itoa.h @@ -1,15 +1,15 @@ -#ifndef __RAK_ITOA_H -#define __RAK_ITOA_H - -#ifdef __cplusplus -extern "C" { -#endif - -char* Itoa( int value, char* result, int base ); - -#ifdef __cplusplus -} -#endif - - -#endif +#ifndef __RAK_ITOA_H +#define __RAK_ITOA_H + +#ifdef __cplusplus +extern "C" { +#endif + +char* Itoa( int value, char* result, int base ); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/Kbhit.h b/project/lib_projects/raknet/jni/RaknetSources/Kbhit.h index c311c15..8568d35 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Kbhit.h +++ b/project/lib_projects/raknet/jni/RaknetSources/Kbhit.h @@ -1,84 +1,84 @@ -/***************************************************************************** -kbhit() and getch() for Linux/UNIX -Chris Giese http://my.execpc.com/~geezer -Release date: ? -This code is public domain (no copyright). -You can do whatever you want with it. -*****************************************************************************/ -#if defined(_WIN32) -#include /* kbhit(), getch() */ - -#else -#include /* struct timeval, select() */ -/* ICANON, ECHO, TCSANOW, struct termios */ -#include /* tcgetattr(), tcsetattr() */ -#include /* atexit(), exit() */ -#include /* read() */ -#include /* printf() */ -#include /* memcpy */ - -static struct termios g_old_kbd_mode; -/***************************************************************************** -*****************************************************************************/ -static void cooked(void) -{ - tcsetattr(0, TCSANOW, &g_old_kbd_mode); -} -/***************************************************************************** -*****************************************************************************/ -static void raw(void) -{ - static char init; -/**/ - struct termios new_kbd_mode; - - if(init) - return; -/* put keyboard (stdin, actually) in raw, unbuffered mode */ - tcgetattr(0, &g_old_kbd_mode); - memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios)); - new_kbd_mode.c_lflag &= ~(ICANON /*| ECHO */ ); - new_kbd_mode.c_cc[VTIME] = 0; - new_kbd_mode.c_cc[VMIN] = 1; - tcsetattr(0, TCSANOW, &new_kbd_mode); -/* when we exit, go back to normal, "cooked" mode */ - atexit(cooked); - - init = 1; -} -/***************************************************************************** -*****************************************************************************/ -static int kbhit(void) -{ - struct timeval timeout; - fd_set read_handles; - int status; - - raw(); -/* check stdin (fd 0) for activity */ - FD_ZERO(&read_handles); - FD_SET(0, &read_handles); - timeout.tv_sec = timeout.tv_usec = 0; - status = select(0 + 1, &read_handles, NULL, NULL, &timeout); - if(status < 0) - { - printf("select() failed in kbhit()\n"); - exit(1); - } - return status; -} -/***************************************************************************** -*****************************************************************************/ -static int getch(void) -{ - unsigned char temp; - - raw(); -/* stdin = fd 0 */ - if(read(0, &temp, 1) != 1) - return 0; - return temp; -} -#endif - - +/***************************************************************************** +kbhit() and getch() for Linux/UNIX +Chris Giese http://my.execpc.com/~geezer +Release date: ? +This code is public domain (no copyright). +You can do whatever you want with it. +*****************************************************************************/ +#if defined(_WIN32) +#include /* kbhit(), getch() */ + +#else +#include /* struct timeval, select() */ +/* ICANON, ECHO, TCSANOW, struct termios */ +#include /* tcgetattr(), tcsetattr() */ +#include /* atexit(), exit() */ +#include /* read() */ +#include /* printf() */ +#include /* memcpy */ + +static struct termios g_old_kbd_mode; +/***************************************************************************** +*****************************************************************************/ +static void cooked(void) +{ + tcsetattr(0, TCSANOW, &g_old_kbd_mode); +} +/***************************************************************************** +*****************************************************************************/ +static void raw(void) +{ + static char init; +/**/ + struct termios new_kbd_mode; + + if(init) + return; +/* put keyboard (stdin, actually) in raw, unbuffered mode */ + tcgetattr(0, &g_old_kbd_mode); + memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios)); + new_kbd_mode.c_lflag &= ~(ICANON /*| ECHO */ ); + new_kbd_mode.c_cc[VTIME] = 0; + new_kbd_mode.c_cc[VMIN] = 1; + tcsetattr(0, TCSANOW, &new_kbd_mode); +/* when we exit, go back to normal, "cooked" mode */ + atexit(cooked); + + init = 1; +} +/***************************************************************************** +*****************************************************************************/ +static int kbhit(void) +{ + struct timeval timeout; + fd_set read_handles; + int status; + + raw(); +/* check stdin (fd 0) for activity */ + FD_ZERO(&read_handles); + FD_SET(0, &read_handles); + timeout.tv_sec = timeout.tv_usec = 0; + status = select(0 + 1, &read_handles, NULL, NULL, &timeout); + if(status < 0) + { + printf("select() failed in kbhit()\n"); + exit(1); + } + return status; +} +/***************************************************************************** +*****************************************************************************/ +static int getch(void) +{ + unsigned char temp; + + raw(); +/* stdin = fd 0 */ + if(read(0, &temp, 1) != 1) + return 0; + return temp; +} +#endif + + diff --git a/project/lib_projects/raknet/jni/RaknetSources/LinuxStrings.cpp b/project/lib_projects/raknet/jni/RaknetSources/LinuxStrings.cpp index e29ed14..93c8491 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/LinuxStrings.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/LinuxStrings.cpp @@ -1,26 +1,26 @@ -#if (defined(__GNUC__) || defined(__ARMCC_VERSION) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32) -#include -#ifndef _stricmp -int _stricmp(const char* s1, const char* s2) -{ - return strcasecmp(s1,s2); -} -#endif -int _strnicmp(const char* s1, const char* s2, size_t n) -{ - return strncasecmp(s1,s2,n); -} -#ifndef __APPLE__ -char *_strlwr(char * str ) -{ - if (str==0) - return 0; - for (int i=0; str[i]; i++) - { - if (str[i]>='A' && str[i]<='Z') - str[i]+='a'-'A'; - } - return str; -} -#endif -#endif +#if (defined(__GNUC__) || defined(__ARMCC_VERSION) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32) +#include +#ifndef _stricmp +int _stricmp(const char* s1, const char* s2) +{ + return strcasecmp(s1,s2); +} +#endif +int _strnicmp(const char* s1, const char* s2, size_t n) +{ + return strncasecmp(s1,s2,n); +} +#ifndef __APPLE__ +char *_strlwr(char * str ) +{ + if (str==0) + return 0; + for (int i=0; str[i]; i++) + { + if (str[i]>='A' && str[i]<='Z') + str[i]+='a'-'A'; + } + return str; +} +#endif +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/LinuxStrings.h b/project/lib_projects/raknet/jni/RaknetSources/LinuxStrings.h index 11c8589..f92b444 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/LinuxStrings.h +++ b/project/lib_projects/raknet/jni/RaknetSources/LinuxStrings.h @@ -1,26 +1,26 @@ -#ifndef _GCC_WIN_STRINGS -#define _GCC_WIN_STRINGS - - - - - - - - - - #if (defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32) - #ifndef _stricmp - int _stricmp(const char* s1, const char* s2); - #endif - int _strnicmp(const char* s1, const char* s2, size_t n); -#ifndef __APPLE__ - char *_strlwr(char * str ); //this won't compile on OSX for some reason -#endif - #ifndef _vsnprintf - #define _vsnprintf vsnprintf - #endif - #endif - - -#endif // _GCC_WIN_STRINGS +#ifndef _GCC_WIN_STRINGS +#define _GCC_WIN_STRINGS + + + + + + + + + + #if (defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32) + #ifndef _stricmp + int _stricmp(const char* s1, const char* s2); + #endif + int _strnicmp(const char* s1, const char* s2, size_t n); +#ifndef __APPLE__ + char *_strlwr(char * str ); //this won't compile on OSX for some reason +#endif + #ifndef _vsnprintf + #define _vsnprintf vsnprintf + #endif + #endif + + +#endif // _GCC_WIN_STRINGS diff --git a/project/lib_projects/raknet/jni/RaknetSources/LocklessTypes.cpp b/project/lib_projects/raknet/jni/RaknetSources/LocklessTypes.cpp index 501e33b..650320a 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/LocklessTypes.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/LocklessTypes.cpp @@ -1,42 +1,42 @@ -#include "LocklessTypes.h" - -using namespace RakNet; - -LocklessUint32_t::LocklessUint32_t() -{ - value=0; -} -LocklessUint32_t::LocklessUint32_t(uint32_t initial) -{ - value=initial; -} -uint32_t LocklessUint32_t::Increment(void) -{ -#ifdef _WIN32 - return (uint32_t) InterlockedIncrement(&value); -#elif defined(ANDROID) || defined(__S3E__) - uint32_t v; - mutex.Lock(); - ++value; - v=value; - mutex.Unlock(); - return v; -#else - return __sync_fetch_and_add (&value, (uint32_t) 1); -#endif -} -uint32_t LocklessUint32_t::Decrement(void) -{ -#ifdef _WIN32 - return (uint32_t) InterlockedDecrement(&value); -#elif defined(ANDROID) || defined(__S3E__) - uint32_t v; - mutex.Lock(); - --value; - v=value; - mutex.Unlock(); - return v; -#else - return __sync_fetch_and_add (&value, (uint32_t) -1); -#endif -} +#include "LocklessTypes.h" + +using namespace RakNet; + +LocklessUint32_t::LocklessUint32_t() +{ + value=0; +} +LocklessUint32_t::LocklessUint32_t(uint32_t initial) +{ + value=initial; +} +uint32_t LocklessUint32_t::Increment(void) +{ +#ifdef _WIN32 + return (uint32_t) InterlockedIncrement(&value); +#elif defined(ANDROID) || defined(__S3E__) + uint32_t v; + mutex.Lock(); + ++value; + v=value; + mutex.Unlock(); + return v; +#else + return __sync_fetch_and_add (&value, (uint32_t) 1); +#endif +} +uint32_t LocklessUint32_t::Decrement(void) +{ +#ifdef _WIN32 + return (uint32_t) InterlockedDecrement(&value); +#elif defined(ANDROID) || defined(__S3E__) + uint32_t v; + mutex.Lock(); + --value; + v=value; + mutex.Unlock(); + return v; +#else + return __sync_fetch_and_add (&value, (uint32_t) -1); +#endif +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/LocklessTypes.h b/project/lib_projects/raknet/jni/RaknetSources/LocklessTypes.h index 441e6bd..36902a6 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/LocklessTypes.h +++ b/project/lib_projects/raknet/jni/RaknetSources/LocklessTypes.h @@ -1,40 +1,40 @@ -#ifndef __LOCKLESS_TYPES_H -#define __LOCKLESS_TYPES_H - -#include "Export.h" -#include "NativeTypes.h" -#include "WindowsIncludes.h" -#if defined(ANDROID) || defined(__S3E__) -// __sync_fetch_and_add not supported apparently -#include "SimpleMutex.h" -#endif - -namespace RakNet -{ - -class RAK_DLL_EXPORT LocklessUint32_t -{ -public: - LocklessUint32_t(); - explicit LocklessUint32_t(uint32_t initial); - // Returns variable value after changing it - uint32_t Increment(void); - // Returns variable value after changing it - uint32_t Decrement(void); - volatile uint32_t GetValue(void) const {return value;} - -protected: -#ifdef _WIN32 - volatile LONG value; -#elif defined(ANDROID) || defined(__S3E__) - // __sync_fetch_and_add not supported apparently - SimpleMutex mutex; - uint32_t value; -#else - volatile uint32_t value; -#endif -}; - -}; - -#endif +#ifndef __LOCKLESS_TYPES_H +#define __LOCKLESS_TYPES_H + +#include "Export.h" +#include "NativeTypes.h" +#include "WindowsIncludes.h" +#if defined(ANDROID) || defined(__S3E__) +// __sync_fetch_and_add not supported apparently +#include "SimpleMutex.h" +#endif + +namespace RakNet +{ + +class RAK_DLL_EXPORT LocklessUint32_t +{ +public: + LocklessUint32_t(); + explicit LocklessUint32_t(uint32_t initial); + // Returns variable value after changing it + uint32_t Increment(void); + // Returns variable value after changing it + uint32_t Decrement(void); + volatile uint32_t GetValue(void) const {return value;} + +protected: +#ifdef _WIN32 + volatile LONG value; +#elif defined(ANDROID) || defined(__S3E__) + // __sync_fetch_and_add not supported apparently + SimpleMutex mutex; + uint32_t value; +#else + volatile uint32_t value; +#endif +}; + +}; + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/LogCommandParser.cpp b/project/lib_projects/raknet/jni/RaknetSources/LogCommandParser.cpp index 50ed7fa..c6bbe77 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/LogCommandParser.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/LogCommandParser.cpp @@ -1,279 +1,279 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_LogCommandParser==1 - -#include "LogCommandParser.h" -#include "TransportInterface.h" - -#include - -#include -#include -#include - -#include "LinuxStrings.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(LogCommandParser,LogCommandParser); - -LogCommandParser::LogCommandParser() -{ - RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Subscribe","[] - Subscribes to a named channel, or all channels"); - RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Unsubscribe","[] - Unsubscribes from a named channel, or all channels"); - memset(channelNames,0,sizeof(channelNames)); -} -LogCommandParser::~LogCommandParser() -{ -} -bool LogCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString) -{ - (void) originalString; - - if (strcmp(command, "Subscribe")==0) - { - unsigned channelIndex; - if (numParameters==0) - { - Subscribe(systemAddress, 0); - transport->Send(systemAddress, "Subscribed to all channels.\r\n"); - } - else if (numParameters==1) - { - if ((channelIndex=Subscribe(systemAddress, parameterList[0]))!=(unsigned)-1) - { - transport->Send(systemAddress, "You are now subscribed to channel %s.\r\n", channelNames[channelIndex]); - } - else - { - transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]); - PrintChannels(systemAddress, transport); - } - } - else - { - transport->Send(systemAddress, "Subscribe takes either 0 or 1 parameters.\r\n"); - } - } - else if (strcmp(command, "Unsubscribe")==0) - { - unsigned channelIndex; - if (numParameters==0) - { - Unsubscribe(systemAddress, 0); - transport->Send(systemAddress, "Unsubscribed from all channels.\r\n"); - } - else if (numParameters==1) - { - if ((channelIndex=Unsubscribe(systemAddress, parameterList[0]))!=(unsigned)-1) - { - transport->Send(systemAddress, "You are now unsubscribed from channel %s.\r\n", channelNames[channelIndex]); - } - else - { - transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]); - PrintChannels(systemAddress, transport); - } - } - else - { - transport->Send(systemAddress, "Unsubscribe takes either 0 or 1 parameters.\r\n"); - } - } - - return true; -} -const char *LogCommandParser::GetName(void) const -{ - return "Logger"; -} -void LogCommandParser::SendHelp(TransportInterface *transport, const SystemAddress &systemAddress) -{ - transport->Send(systemAddress, "The logger will accept user log data via the Log(...) function.\r\n"); - transport->Send(systemAddress, "Each log is associated with a named channel.\r\n"); - transport->Send(systemAddress, "You can subscribe to or unsubscribe from named channels.\r\n"); - PrintChannels(systemAddress, transport); -} -void LogCommandParser::AddChannel(const char *channelName) -{ - unsigned channelIndex=0; - channelIndex = GetChannelIndexFromName(channelName); - // Each channel can only be added once. - RakAssert(channelIndex==(unsigned)-1); - - unsigned i; - for (i=0; i < 32; i++) - { - if (channelNames[i]==0) - { - // Assuming a persistent static string. - channelNames[i]=channelName; - return; - } - } - - // No more available channels - max 32 with this implementation where I save subscribed channels with bit operations - RakAssert(0); -} -void LogCommandParser::WriteLog(const char *channelName, const char *format, ...) -{ - if (channelName==0 || format==0) - return; - - unsigned channelIndex; - channelIndex = GetChannelIndexFromName(channelName); - if (channelIndex==(unsigned)-1) - { - AddChannel(channelName); - } - - char text[REMOTE_MAX_TEXT_INPUT]; - va_list ap; - va_start(ap, format); - _vsnprintf(text, REMOTE_MAX_TEXT_INPUT, format, ap); - va_end(ap); - text[REMOTE_MAX_TEXT_INPUT-1]=0; - - // Make sure that text ends in \r\n - int textLen; - textLen=(int)strlen(text); - if (textLen==0) - return; - if (text[textLen-1]=='\n') - { - text[textLen-1]=0; - } - if (textLen < REMOTE_MAX_TEXT_INPUT-4) - strcat(text, "\r\n"); - else - { - text[textLen-3]='\r'; - text[textLen-2]='\n'; - text[textLen-1]=0; - } - - // For each user that subscribes to this channel, send to them. - unsigned i; - for (i=0; i < remoteUsers.Size(); i++) - { - if (remoteUsers[i].channels & (1 << channelIndex)) - { - trans->Send(remoteUsers[i].systemAddress, text); - } - } -} -void LogCommandParser::PrintChannels(const SystemAddress &systemAddress, TransportInterface *transport) const -{ - unsigned i; - bool anyChannels=false; - transport->Send(systemAddress, "CHANNELS:\r\n"); - for (i=0; i < 32; i++) - { - if (channelNames[i]) - { - transport->Send(systemAddress, "%i. %s\r\n", i+1,channelNames[i]); - anyChannels=true; - } - } - if (anyChannels==false) - transport->Send(systemAddress, "None.\r\n"); -} -void LogCommandParser::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport) -{ - (void) systemAddress; - (void) transport; -} -void LogCommandParser::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport) -{ - (void) transport; - Unsubscribe(systemAddress, 0); -} -unsigned LogCommandParser::Unsubscribe(const SystemAddress &systemAddress, const char *channelName) -{ - unsigned i; - for (i=0; i < remoteUsers.Size(); i++) - { - if (remoteUsers[i].systemAddress==systemAddress) - { - if (channelName==0) - { - // Unsubscribe from all and delete this user. - remoteUsers[i]=remoteUsers[remoteUsers.Size()-1]; - remoteUsers.RemoveFromEnd(); - return 0; - } - else - { - unsigned channelIndex; - channelIndex = GetChannelIndexFromName(channelName); - if (channelIndex!=(unsigned)-1) - { - remoteUsers[i].channels&=0xFFFF ^ (1< + +#include +#include +#include + +#include "LinuxStrings.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(LogCommandParser,LogCommandParser); + +LogCommandParser::LogCommandParser() +{ + RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Subscribe","[] - Subscribes to a named channel, or all channels"); + RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Unsubscribe","[] - Unsubscribes from a named channel, or all channels"); + memset(channelNames,0,sizeof(channelNames)); +} +LogCommandParser::~LogCommandParser() +{ +} +bool LogCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString) +{ + (void) originalString; + + if (strcmp(command, "Subscribe")==0) + { + unsigned channelIndex; + if (numParameters==0) + { + Subscribe(systemAddress, 0); + transport->Send(systemAddress, "Subscribed to all channels.\r\n"); + } + else if (numParameters==1) + { + if ((channelIndex=Subscribe(systemAddress, parameterList[0]))!=(unsigned)-1) + { + transport->Send(systemAddress, "You are now subscribed to channel %s.\r\n", channelNames[channelIndex]); + } + else + { + transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]); + PrintChannels(systemAddress, transport); + } + } + else + { + transport->Send(systemAddress, "Subscribe takes either 0 or 1 parameters.\r\n"); + } + } + else if (strcmp(command, "Unsubscribe")==0) + { + unsigned channelIndex; + if (numParameters==0) + { + Unsubscribe(systemAddress, 0); + transport->Send(systemAddress, "Unsubscribed from all channels.\r\n"); + } + else if (numParameters==1) + { + if ((channelIndex=Unsubscribe(systemAddress, parameterList[0]))!=(unsigned)-1) + { + transport->Send(systemAddress, "You are now unsubscribed from channel %s.\r\n", channelNames[channelIndex]); + } + else + { + transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]); + PrintChannels(systemAddress, transport); + } + } + else + { + transport->Send(systemAddress, "Unsubscribe takes either 0 or 1 parameters.\r\n"); + } + } + + return true; +} +const char *LogCommandParser::GetName(void) const +{ + return "Logger"; +} +void LogCommandParser::SendHelp(TransportInterface *transport, const SystemAddress &systemAddress) +{ + transport->Send(systemAddress, "The logger will accept user log data via the Log(...) function.\r\n"); + transport->Send(systemAddress, "Each log is associated with a named channel.\r\n"); + transport->Send(systemAddress, "You can subscribe to or unsubscribe from named channels.\r\n"); + PrintChannels(systemAddress, transport); +} +void LogCommandParser::AddChannel(const char *channelName) +{ + unsigned channelIndex=0; + channelIndex = GetChannelIndexFromName(channelName); + // Each channel can only be added once. + RakAssert(channelIndex==(unsigned)-1); + + unsigned i; + for (i=0; i < 32; i++) + { + if (channelNames[i]==0) + { + // Assuming a persistent static string. + channelNames[i]=channelName; + return; + } + } + + // No more available channels - max 32 with this implementation where I save subscribed channels with bit operations + RakAssert(0); +} +void LogCommandParser::WriteLog(const char *channelName, const char *format, ...) +{ + if (channelName==0 || format==0) + return; + + unsigned channelIndex; + channelIndex = GetChannelIndexFromName(channelName); + if (channelIndex==(unsigned)-1) + { + AddChannel(channelName); + } + + char text[REMOTE_MAX_TEXT_INPUT]; + va_list ap; + va_start(ap, format); + _vsnprintf(text, REMOTE_MAX_TEXT_INPUT, format, ap); + va_end(ap); + text[REMOTE_MAX_TEXT_INPUT-1]=0; + + // Make sure that text ends in \r\n + int textLen; + textLen=(int)strlen(text); + if (textLen==0) + return; + if (text[textLen-1]=='\n') + { + text[textLen-1]=0; + } + if (textLen < REMOTE_MAX_TEXT_INPUT-4) + strcat(text, "\r\n"); + else + { + text[textLen-3]='\r'; + text[textLen-2]='\n'; + text[textLen-1]=0; + } + + // For each user that subscribes to this channel, send to them. + unsigned i; + for (i=0; i < remoteUsers.Size(); i++) + { + if (remoteUsers[i].channels & (1 << channelIndex)) + { + trans->Send(remoteUsers[i].systemAddress, text); + } + } +} +void LogCommandParser::PrintChannels(const SystemAddress &systemAddress, TransportInterface *transport) const +{ + unsigned i; + bool anyChannels=false; + transport->Send(systemAddress, "CHANNELS:\r\n"); + for (i=0; i < 32; i++) + { + if (channelNames[i]) + { + transport->Send(systemAddress, "%i. %s\r\n", i+1,channelNames[i]); + anyChannels=true; + } + } + if (anyChannels==false) + transport->Send(systemAddress, "None.\r\n"); +} +void LogCommandParser::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport) +{ + (void) systemAddress; + (void) transport; +} +void LogCommandParser::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport) +{ + (void) transport; + Unsubscribe(systemAddress, 0); +} +unsigned LogCommandParser::Unsubscribe(const SystemAddress &systemAddress, const char *channelName) +{ + unsigned i; + for (i=0; i < remoteUsers.Size(); i++) + { + if (remoteUsers[i].systemAddress==systemAddress) + { + if (channelName==0) + { + // Unsubscribe from all and delete this user. + remoteUsers[i]=remoteUsers[remoteUsers.Size()-1]; + remoteUsers.RemoveFromEnd(); + return 0; + } + else + { + unsigned channelIndex; + channelIndex = GetChannelIndexFromName(channelName); + if (channelIndex!=(unsigned)-1) + { + remoteUsers[i].channels&=0xFFFF ^ (1< remoteUsers; - - /// Names of the channels at each bit, or 0 for an unused channel - const char *channelNames[32]; - - /// This is so I can save the current transport provider, solely so I can use it without having the user pass it to Log - TransportInterface *trans; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains LogCommandParser , Used to send logs to connected consoles +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_LogCommandParser==1 + +#ifndef __LOG_COMMAND_PARSER +#define __LOG_COMMAND_PARSER + +#include "CommandParserInterface.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief Adds the ability to send logging output to a remote console +class RAK_DLL_EXPORT LogCommandParser : public CommandParserInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(LogCommandParser) + + LogCommandParser(); + ~LogCommandParser(); + + /// Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + const char *GetName(void) const; + + /// A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress); + + /// All logs must be associated with a channel. This is a filter so that remote clients only get logs for a system they care about. + // If you call Log with a channel that is unknown, that channel will automatically be added + /// \param[in] channelName A persistent string naming the channel. Don't deallocate this string. + void AddChannel(const char *channelName); + + /// Write a log to a channel. + /// Logs are not buffered, so only remote consoles connected and subscribing at the time you write will get the output. + /// \param[in] format Same as RAKNET_DEBUG_PRINTF() + /// \param[in] ... Same as RAKNET_DEBUG_PRINTF() + void WriteLog(const char *channelName, const char *format, ...); + + /// A callback for when \a systemAddress has connected to us. + /// \param[in] systemAddress The player that has connected. + /// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players. + void OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport); + + /// A callback for when \a systemAddress has disconnected, either gracefully or forcefully + /// \param[in] systemAddress The player that has disconnected. + /// \param[in] transport The transport interface that sent us this information. + void OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport); + + /// This is called every time transport interface is registered. If you want to save a copy of the TransportInterface pointer + /// This is the place to do it + /// \param[in] transport The new TransportInterface + void OnTransportChange(TransportInterface *transport); +protected: + /// Sends the currently active channels to the user + /// \param[in] systemAddress The player to send to + /// \param[in] transport The transport interface to use to send the channels + void PrintChannels(const SystemAddress &systemAddress, TransportInterface *transport) const; + + /// Unsubscribe a user from a channel (or from all channels) + /// \param[in] systemAddress The player to unsubscribe to + /// \param[in] channelName If 0, then unsubscribe from all channels. Otherwise unsubscribe from the named channel + unsigned Unsubscribe(const SystemAddress &systemAddress, const char *channelName); + + /// Subscribe a user to a channel (or to all channels) + /// \param[in] systemAddress The player to subscribe to + /// \param[in] channelName If 0, then subscribe from all channels. Otherwise subscribe to the named channel + unsigned Subscribe(const SystemAddress &systemAddress, const char *channelName); + + /// Given the name of a channel, return the index into channelNames where it is located + /// \param[in] channelName The name of the channel + unsigned GetChannelIndexFromName(const char *channelName); + + /// One of these structures is created per player + struct SystemAddressAndChannel + { + /// The ID of the player + SystemAddress systemAddress; + + /// Bitwise representations of the channels subscribed to. If bit 0 is set, then we subscribe to channelNames[0] and so on. + unsigned channels; + }; + + /// The list of remote users. Added to when users subscribe, removed when they disconnect or unsubscribe + DataStructures::List remoteUsers; + + /// Names of the channels at each bit, or 0 for an unused channel + const char *channelNames[32]; + + /// This is so I can save the current transport provider, solely so I can use it without having the user pass it to Log + TransportInterface *trans; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/MTUSize.h b/project/lib_projects/raknet/jni/RaknetSources/MTUSize.h index 0786b4f..4a627b6 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/MTUSize.h +++ b/project/lib_projects/raknet/jni/RaknetSources/MTUSize.h @@ -1,31 +1,31 @@ -/// \file -/// \brief \b [Internal] Defines the default maximum transfer unit. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef MAXIMUM_MTU_SIZE - -/// \li \em 17914 16 Mbit/Sec Token Ring -/// \li \em 4464 4 Mbits/Sec Token Ring -/// \li \em 4352 FDDI -/// \li \em 1500. The largest Ethernet packet size \b recommended. This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. -/// \li \em 1492. The size PPPoE prefers. -/// \li \em 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) -/// \li \em 1468. The size DHCP prefers. -/// \li \em 1460. Usable by AOL if you don't have large email attachments, etc. -/// \li \em 1430. The size VPN and PPTP prefer. -/// \li \em 1400. Maximum size for AOL DSL. -/// \li \em 576. Typical value to connect to dial-up ISPs. -/// The largest value for an UDP datagram - - - -#define MAXIMUM_MTU_SIZE 1492 - - -#define MINIMUM_MTU_SIZE 400 - -#endif +/// \file +/// \brief \b [Internal] Defines the default maximum transfer unit. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef MAXIMUM_MTU_SIZE + +/// \li \em 17914 16 Mbit/Sec Token Ring +/// \li \em 4464 4 Mbits/Sec Token Ring +/// \li \em 4352 FDDI +/// \li \em 1500. The largest Ethernet packet size \b recommended. This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. +/// \li \em 1492. The size PPPoE prefers. +/// \li \em 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) +/// \li \em 1468. The size DHCP prefers. +/// \li \em 1460. Usable by AOL if you don't have large email attachments, etc. +/// \li \em 1430. The size VPN and PPTP prefer. +/// \li \em 1400. Maximum size for AOL DSL. +/// \li \em 576. Typical value to connect to dial-up ISPs. +/// The largest value for an UDP datagram + + + +#define MAXIMUM_MTU_SIZE 1492 + + +#define MINIMUM_MTU_SIZE 400 + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/MessageFilter.cpp b/project/lib_projects/raknet/jni/RaknetSources/MessageFilter.cpp index 28fa423..c2fc37a 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/MessageFilter.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/MessageFilter.cpp @@ -1,408 +1,408 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_MessageFilter==1 - -#include "MessageFilter.h" -#include "RakAssert.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "RakAssert.h" -#include "RakPeerInterface.h" -#include "PacketizedTCP.h" -#include "BitStream.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -int RakNet::MessageFilterStrComp( char *const &key,char *const &data ) -{ - return strcmp(key,data); -} - -int RakNet::FilterSetComp( const int &key, FilterSet * const &data ) -{ - if (key < data->filterSetID) - return -1; - else if (key==data->filterSetID) - return 0; - else - return 1; -} -STATIC_FACTORY_DEFINITIONS(MessageFilter,MessageFilter); - -MessageFilter::MessageFilter() -{ - whenLastTimeoutCheck=RakNet::GetTime(); -} -MessageFilter::~MessageFilter() -{ - Clear(); -} -void MessageFilter::SetAutoAddNewConnectionsToFilter(int filterSetID) -{ - autoAddNewConnectionsToFilter=filterSetID; -} -void MessageFilter::SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID) -{ - RakAssert(messageIDStart <= messageIDEnd); - FilterSet *filterSet = GetFilterSetByID(filterSetID); - int i; - for (i=messageIDStart; i <= messageIDEnd; ++i) - filterSet->allowedIDs[i]=allow; -} -void MessageFilter::SetAllowRPC4(bool allow, const char* uniqueID, int filterSetID) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - bool objectExists; - unsigned int idx = filterSet->allowedRPC4.GetIndexFromKey(uniqueID, &objectExists); - if (allow) - { - if (objectExists==false) - { - filterSet->allowedRPC4.InsertAtIndex(uniqueID, idx, _FILE_AND_LINE_); - filterSet->allowedIDs[ID_RPC_PLUGIN]=true; - } - } - else - { - if (objectExists==true) - { - filterSet->allowedRPC4.RemoveAtIndex(idx); - if (filterSet->allowedRPC4.Size()==0) - { - filterSet->allowedIDs[ID_RPC_PLUGIN]=false; - } - } - } -} -void MessageFilter::SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNet::TimeMS banTimeMS, int filterSetID) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - filterSet->kickOnDisallowedMessage=kickOnDisallowed; - filterSet->disallowedMessageBanTimeMS=banTimeMS; - filterSet->banOnDisallowedMessage=banOnDisallowed; -} -void MessageFilter::SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID)) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - filterSet->invalidMessageCallback=invalidMessageCallback; - filterSet->disallowedCallbackUserData=userData; -} -void MessageFilter::SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData)) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - filterSet->timeoutCallback=invalidMessageCallback; - filterSet->timeoutUserData=userData; -} -void MessageFilter::SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNet::TimeMS banTimeMS, int filterSetID) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - filterSet->maxMemberTimeMS=allowedTimeMS; - filterSet->banOnFilterTimeExceed=banOnExceed; - filterSet->timeExceedBanTimeMS=banTimeMS; -} -int MessageFilter::GetSystemFilterSet(AddressOrGUID systemAddress) -{ -// bool objectExists; -// unsigned index = systemList.GetIndexFromKey(systemAddress, &objectExists); -// if (objectExists==false) -// return -1; -// else -// return systemList[index].filter->filterSetID; - - DataStructures::HashIndex index = systemList.GetIndexOf(systemAddress); - if (index.IsInvalid()) - return -1; - else - return systemList.ItemAtIndex(index).filter->filterSetID; -} -void MessageFilter::SetSystemFilterSet(AddressOrGUID addressOrGUID, int filterSetID) -{ - // Allocate this filter set if it doesn't exist. - RakAssert(addressOrGUID.IsUndefined()==false); -// bool objectExists; -// unsigned index = systemList.GetIndexFromKey(addressOrGUID, &objectExists); -// if (objectExists==false) - DataStructures::HashIndex index = systemList.GetIndexOf(addressOrGUID); - if (index.IsInvalid()) - { - if (filterSetID<0) - return; - - FilteredSystem filteredSystem; - filteredSystem.filter = GetFilterSetByID(filterSetID); - // filteredSystem.addressOrGUID=addressOrGUID; - filteredSystem.timeEnteredThisSet=RakNet::GetTimeMS(); - // systemList.Insert(addressOrGUID, filteredSystem, true, _FILE_AND_LINE_); - systemList.Push(addressOrGUID,filteredSystem,_FILE_AND_LINE_); - } - else - { - if (filterSetID>=0) - { - FilterSet *filterSet = GetFilterSetByID(filterSetID); - systemList.ItemAtIndex(index).timeEnteredThisSet=RakNet::GetTimeMS(); - systemList.ItemAtIndex(index).filter=filterSet; - } - else - { - systemList.RemoveAtIndex(index, _FILE_AND_LINE_); - } - } -} -unsigned MessageFilter::GetSystemCount(int filterSetID) const -{ - if (filterSetID==-1) - { - return systemList.Size(); - } - else - { - unsigned i; - unsigned count=0; - DataStructures::List< FilteredSystem > itemList; - DataStructures::List< AddressOrGUID > keyList; - systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); - for (i=0; i < itemList.Size(); i++) - if (itemList[i].filter->filterSetID==filterSetID) - ++count; - return count; - } -} -unsigned MessageFilter::GetFilterSetCount(void) const -{ - return filterList.Size(); -} -int MessageFilter::GetFilterSetIDByIndex(unsigned index) -{ - return filterList[index]->filterSetID; -} -void MessageFilter::DeleteFilterSet(int filterSetID) -{ - FilterSet *filterSet; - bool objectExists; - unsigned i,index; - index = filterList.GetIndexFromKey(filterSetID, &objectExists); - if (objectExists) - { - filterSet=filterList[index]; - DeallocateFilterSet(filterSet); - filterList.RemoveAtIndex(index); - - DataStructures::List< FilteredSystem > itemList; - DataStructures::List< AddressOrGUID > keyList; - systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); - for (i=0; i < itemList.Size(); i++) - { - if (itemList[i].filter==filterSet) - { - systemList.Remove(keyList[i], _FILE_AND_LINE_); - } - } - - /* - // Don't reference this pointer any longer - i=0; - while (i < systemList.Size()) - { - if (systemList[i].filter==filterSet) - systemList.RemoveAtIndex(i); - else - ++i; - } - */ - } -} -void MessageFilter::Clear(void) -{ - unsigned i; - systemList.Clear(_FILE_AND_LINE_); - for (i=0; i < filterList.Size(); i++) - DeallocateFilterSet(filterList[i]); - filterList.Clear(false, _FILE_AND_LINE_); -} -void MessageFilter::DeallocateFilterSet(FilterSet* filterSet) -{ - RakNet::OP_DELETE(filterSet, _FILE_AND_LINE_); -} -FilterSet* MessageFilter::GetFilterSetByID(int filterSetID) -{ - RakAssert(filterSetID>=0); - bool objectExists; - unsigned index; - index = filterList.GetIndexFromKey(filterSetID, &objectExists); - if (objectExists) - return filterList[index]; - else - { - FilterSet *newFilterSet = RakNet::OP_NEW( _FILE_AND_LINE_ ); - memset(newFilterSet->allowedIDs, 0, MESSAGE_FILTER_MAX_MESSAGE_ID * sizeof(bool)); - newFilterSet->banOnFilterTimeExceed=false; - newFilterSet->kickOnDisallowedMessage=false; - newFilterSet->banOnDisallowedMessage=false; - newFilterSet->disallowedMessageBanTimeMS=0; - newFilterSet->timeExceedBanTimeMS=0; - newFilterSet->maxMemberTimeMS=0; - newFilterSet->filterSetID=filterSetID; - newFilterSet->invalidMessageCallback=0; - newFilterSet->timeoutCallback=0; - newFilterSet->timeoutUserData=0; - filterList.Insert(filterSetID, newFilterSet, true, _FILE_AND_LINE_); - return newFilterSet; - } -} -void MessageFilter::OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID) -{ - if (filterSet->invalidMessageCallback) - filterSet->invalidMessageCallback(rakPeerInterface, systemAddress, filterSet->filterSetID, filterSet->disallowedCallbackUserData, messageID); - if (filterSet->banOnDisallowedMessage && rakPeerInterface) - { - char str1[64]; - systemAddress.systemAddress.ToString(false, str1); - rakPeerInterface->AddToBanList(str1, filterSet->disallowedMessageBanTimeMS); - } - if (filterSet->kickOnDisallowedMessage) - { - if (rakPeerInterface) - rakPeerInterface->CloseConnection(systemAddress, true, 0); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->CloseConnection(systemAddress.systemAddress); -#endif - } -} -void MessageFilter::Update(void) -{ - // Update all timers for all systems. If those systems' filter sets are expired, take the appropriate action. - RakNet::Time curTime = RakNet::GetTime(); - if (GreaterThan(curTime - 1000, whenLastTimeoutCheck)) - { - DataStructures::List< FilteredSystem > itemList; - DataStructures::List< AddressOrGUID > keyList; - systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); - - unsigned int index; - for (index=0; index < itemList.Size(); index++) - { - if (itemList[index].filter && - itemList[index].filter->maxMemberTimeMS>0 && - curTime-itemList[index].timeEnteredThisSet >= itemList[index].filter->maxMemberTimeMS) - { - if (itemList[index].filter->timeoutCallback) - itemList[index].filter->timeoutCallback(rakPeerInterface, keyList[index], itemList[index].filter->filterSetID, itemList[index].filter->timeoutUserData); - - if (itemList[index].filter->banOnFilterTimeExceed && rakPeerInterface) - { - char str1[64]; - keyList[index].ToString(false, str1); - rakPeerInterface->AddToBanList(str1, itemList[index].filter->timeExceedBanTimeMS); - } - if (rakPeerInterface) - rakPeerInterface->CloseConnection(keyList[index], true, 0); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->CloseConnection(keyList[index].systemAddress); -#endif - - systemList.Remove(keyList[index], _FILE_AND_LINE_); - } - } - - whenLastTimeoutCheck=curTime+1000; - } -} -void MessageFilter::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) systemAddress; - (void) rakNetGUID; - (void) isIncoming; - - AddressOrGUID aog; - aog.rakNetGuid=rakNetGUID; - aog.systemAddress=systemAddress; - - // New system, automatically assign to filter set if appropriate - if (autoAddNewConnectionsToFilter>=0 && systemList.HasData(aog)==false) - SetSystemFilterSet(aog, autoAddNewConnectionsToFilter); -} -void MessageFilter::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) rakNetGUID; - (void) lostConnectionReason; - - AddressOrGUID aog; - aog.rakNetGuid=rakNetGUID; - aog.systemAddress=systemAddress; - - // Lost system, remove from the list - systemList.Remove(aog, _FILE_AND_LINE_); -} - PluginReceiveResult MessageFilter::OnReceive(Packet *packet) -{ - DataStructures::HashIndex index; - unsigned char messageId; - - switch (packet->data[0]) - { - case ID_NEW_INCOMING_CONNECTION: - case ID_CONNECTION_REQUEST_ACCEPTED: - case ID_CONNECTION_LOST: - case ID_DISCONNECTION_NOTIFICATION: - case ID_CONNECTION_ATTEMPT_FAILED: - case ID_NO_FREE_INCOMING_CONNECTIONS: - case ID_IP_RECENTLY_CONNECTED: - case ID_CONNECTION_BANNED: - case ID_INVALID_PASSWORD: - case ID_UNCONNECTED_PONG: - case ID_ALREADY_CONNECTED: - case ID_ADVERTISE_SYSTEM: - case ID_REMOTE_DISCONNECTION_NOTIFICATION: - case ID_REMOTE_CONNECTION_LOST: - case ID_REMOTE_NEW_INCOMING_CONNECTION: - case ID_DOWNLOAD_PROGRESS: - break; - default: - if (packet->data[0]==ID_TIMESTAMP) - { - if (packet->lengthdata[sizeof(MessageID) + sizeof(RakNet::TimeMS)]; - } - else - messageId=packet->data[0]; - // If this system is filtered, check if this message is allowed. If not allowed, return RR_STOP_PROCESSING_AND_DEALLOCATE - // index = systemList.GetIndexFromKey(packet->addressOrGUID, &objectExists); - index = systemList.GetIndexOf(packet); - if (index.IsInvalid()) - break; - if (systemList.ItemAtIndex(index).filter->allowedIDs[messageId]==false) - { - OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - if (packet->data[0]==ID_RPC_PLUGIN) - { - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - RakNet::RakString functionName; - bsIn.ReadCompressed(functionName); - if (systemList.ItemAtIndex(index).filter->allowedRPC4.HasData(functionName)==false) - { - OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - - break; - } - - return RR_CONTINUE_PROCESSING; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_MessageFilter==1 + +#include "MessageFilter.h" +#include "RakAssert.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "RakAssert.h" +#include "RakPeerInterface.h" +#include "PacketizedTCP.h" +#include "BitStream.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +int RakNet::MessageFilterStrComp( char *const &key,char *const &data ) +{ + return strcmp(key,data); +} + +int RakNet::FilterSetComp( const int &key, FilterSet * const &data ) +{ + if (key < data->filterSetID) + return -1; + else if (key==data->filterSetID) + return 0; + else + return 1; +} +STATIC_FACTORY_DEFINITIONS(MessageFilter,MessageFilter); + +MessageFilter::MessageFilter() +{ + whenLastTimeoutCheck=RakNet::GetTime(); +} +MessageFilter::~MessageFilter() +{ + Clear(); +} +void MessageFilter::SetAutoAddNewConnectionsToFilter(int filterSetID) +{ + autoAddNewConnectionsToFilter=filterSetID; +} +void MessageFilter::SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID) +{ + RakAssert(messageIDStart <= messageIDEnd); + FilterSet *filterSet = GetFilterSetByID(filterSetID); + int i; + for (i=messageIDStart; i <= messageIDEnd; ++i) + filterSet->allowedIDs[i]=allow; +} +void MessageFilter::SetAllowRPC4(bool allow, const char* uniqueID, int filterSetID) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + bool objectExists; + unsigned int idx = filterSet->allowedRPC4.GetIndexFromKey(uniqueID, &objectExists); + if (allow) + { + if (objectExists==false) + { + filterSet->allowedRPC4.InsertAtIndex(uniqueID, idx, _FILE_AND_LINE_); + filterSet->allowedIDs[ID_RPC_PLUGIN]=true; + } + } + else + { + if (objectExists==true) + { + filterSet->allowedRPC4.RemoveAtIndex(idx); + if (filterSet->allowedRPC4.Size()==0) + { + filterSet->allowedIDs[ID_RPC_PLUGIN]=false; + } + } + } +} +void MessageFilter::SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNet::TimeMS banTimeMS, int filterSetID) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + filterSet->kickOnDisallowedMessage=kickOnDisallowed; + filterSet->disallowedMessageBanTimeMS=banTimeMS; + filterSet->banOnDisallowedMessage=banOnDisallowed; +} +void MessageFilter::SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID)) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + filterSet->invalidMessageCallback=invalidMessageCallback; + filterSet->disallowedCallbackUserData=userData; +} +void MessageFilter::SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData)) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + filterSet->timeoutCallback=invalidMessageCallback; + filterSet->timeoutUserData=userData; +} +void MessageFilter::SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNet::TimeMS banTimeMS, int filterSetID) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + filterSet->maxMemberTimeMS=allowedTimeMS; + filterSet->banOnFilterTimeExceed=banOnExceed; + filterSet->timeExceedBanTimeMS=banTimeMS; +} +int MessageFilter::GetSystemFilterSet(AddressOrGUID systemAddress) +{ +// bool objectExists; +// unsigned index = systemList.GetIndexFromKey(systemAddress, &objectExists); +// if (objectExists==false) +// return -1; +// else +// return systemList[index].filter->filterSetID; + + DataStructures::HashIndex index = systemList.GetIndexOf(systemAddress); + if (index.IsInvalid()) + return -1; + else + return systemList.ItemAtIndex(index).filter->filterSetID; +} +void MessageFilter::SetSystemFilterSet(AddressOrGUID addressOrGUID, int filterSetID) +{ + // Allocate this filter set if it doesn't exist. + RakAssert(addressOrGUID.IsUndefined()==false); +// bool objectExists; +// unsigned index = systemList.GetIndexFromKey(addressOrGUID, &objectExists); +// if (objectExists==false) + DataStructures::HashIndex index = systemList.GetIndexOf(addressOrGUID); + if (index.IsInvalid()) + { + if (filterSetID<0) + return; + + FilteredSystem filteredSystem; + filteredSystem.filter = GetFilterSetByID(filterSetID); + // filteredSystem.addressOrGUID=addressOrGUID; + filteredSystem.timeEnteredThisSet=RakNet::GetTimeMS(); + // systemList.Insert(addressOrGUID, filteredSystem, true, _FILE_AND_LINE_); + systemList.Push(addressOrGUID,filteredSystem,_FILE_AND_LINE_); + } + else + { + if (filterSetID>=0) + { + FilterSet *filterSet = GetFilterSetByID(filterSetID); + systemList.ItemAtIndex(index).timeEnteredThisSet=RakNet::GetTimeMS(); + systemList.ItemAtIndex(index).filter=filterSet; + } + else + { + systemList.RemoveAtIndex(index, _FILE_AND_LINE_); + } + } +} +unsigned MessageFilter::GetSystemCount(int filterSetID) const +{ + if (filterSetID==-1) + { + return systemList.Size(); + } + else + { + unsigned i; + unsigned count=0; + DataStructures::List< FilteredSystem > itemList; + DataStructures::List< AddressOrGUID > keyList; + systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); + for (i=0; i < itemList.Size(); i++) + if (itemList[i].filter->filterSetID==filterSetID) + ++count; + return count; + } +} +unsigned MessageFilter::GetFilterSetCount(void) const +{ + return filterList.Size(); +} +int MessageFilter::GetFilterSetIDByIndex(unsigned index) +{ + return filterList[index]->filterSetID; +} +void MessageFilter::DeleteFilterSet(int filterSetID) +{ + FilterSet *filterSet; + bool objectExists; + unsigned i,index; + index = filterList.GetIndexFromKey(filterSetID, &objectExists); + if (objectExists) + { + filterSet=filterList[index]; + DeallocateFilterSet(filterSet); + filterList.RemoveAtIndex(index); + + DataStructures::List< FilteredSystem > itemList; + DataStructures::List< AddressOrGUID > keyList; + systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); + for (i=0; i < itemList.Size(); i++) + { + if (itemList[i].filter==filterSet) + { + systemList.Remove(keyList[i], _FILE_AND_LINE_); + } + } + + /* + // Don't reference this pointer any longer + i=0; + while (i < systemList.Size()) + { + if (systemList[i].filter==filterSet) + systemList.RemoveAtIndex(i); + else + ++i; + } + */ + } +} +void MessageFilter::Clear(void) +{ + unsigned i; + systemList.Clear(_FILE_AND_LINE_); + for (i=0; i < filterList.Size(); i++) + DeallocateFilterSet(filterList[i]); + filterList.Clear(false, _FILE_AND_LINE_); +} +void MessageFilter::DeallocateFilterSet(FilterSet* filterSet) +{ + RakNet::OP_DELETE(filterSet, _FILE_AND_LINE_); +} +FilterSet* MessageFilter::GetFilterSetByID(int filterSetID) +{ + RakAssert(filterSetID>=0); + bool objectExists; + unsigned index; + index = filterList.GetIndexFromKey(filterSetID, &objectExists); + if (objectExists) + return filterList[index]; + else + { + FilterSet *newFilterSet = RakNet::OP_NEW( _FILE_AND_LINE_ ); + memset(newFilterSet->allowedIDs, 0, MESSAGE_FILTER_MAX_MESSAGE_ID * sizeof(bool)); + newFilterSet->banOnFilterTimeExceed=false; + newFilterSet->kickOnDisallowedMessage=false; + newFilterSet->banOnDisallowedMessage=false; + newFilterSet->disallowedMessageBanTimeMS=0; + newFilterSet->timeExceedBanTimeMS=0; + newFilterSet->maxMemberTimeMS=0; + newFilterSet->filterSetID=filterSetID; + newFilterSet->invalidMessageCallback=0; + newFilterSet->timeoutCallback=0; + newFilterSet->timeoutUserData=0; + filterList.Insert(filterSetID, newFilterSet, true, _FILE_AND_LINE_); + return newFilterSet; + } +} +void MessageFilter::OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID) +{ + if (filterSet->invalidMessageCallback) + filterSet->invalidMessageCallback(rakPeerInterface, systemAddress, filterSet->filterSetID, filterSet->disallowedCallbackUserData, messageID); + if (filterSet->banOnDisallowedMessage && rakPeerInterface) + { + char str1[64]; + systemAddress.systemAddress.ToString(false, str1); + rakPeerInterface->AddToBanList(str1, filterSet->disallowedMessageBanTimeMS); + } + if (filterSet->kickOnDisallowedMessage) + { + if (rakPeerInterface) + rakPeerInterface->CloseConnection(systemAddress, true, 0); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->CloseConnection(systemAddress.systemAddress); +#endif + } +} +void MessageFilter::Update(void) +{ + // Update all timers for all systems. If those systems' filter sets are expired, take the appropriate action. + RakNet::Time curTime = RakNet::GetTime(); + if (GreaterThan(curTime - 1000, whenLastTimeoutCheck)) + { + DataStructures::List< FilteredSystem > itemList; + DataStructures::List< AddressOrGUID > keyList; + systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); + + unsigned int index; + for (index=0; index < itemList.Size(); index++) + { + if (itemList[index].filter && + itemList[index].filter->maxMemberTimeMS>0 && + curTime-itemList[index].timeEnteredThisSet >= itemList[index].filter->maxMemberTimeMS) + { + if (itemList[index].filter->timeoutCallback) + itemList[index].filter->timeoutCallback(rakPeerInterface, keyList[index], itemList[index].filter->filterSetID, itemList[index].filter->timeoutUserData); + + if (itemList[index].filter->banOnFilterTimeExceed && rakPeerInterface) + { + char str1[64]; + keyList[index].ToString(false, str1); + rakPeerInterface->AddToBanList(str1, itemList[index].filter->timeExceedBanTimeMS); + } + if (rakPeerInterface) + rakPeerInterface->CloseConnection(keyList[index], true, 0); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->CloseConnection(keyList[index].systemAddress); +#endif + + systemList.Remove(keyList[index], _FILE_AND_LINE_); + } + } + + whenLastTimeoutCheck=curTime+1000; + } +} +void MessageFilter::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) systemAddress; + (void) rakNetGUID; + (void) isIncoming; + + AddressOrGUID aog; + aog.rakNetGuid=rakNetGUID; + aog.systemAddress=systemAddress; + + // New system, automatically assign to filter set if appropriate + if (autoAddNewConnectionsToFilter>=0 && systemList.HasData(aog)==false) + SetSystemFilterSet(aog, autoAddNewConnectionsToFilter); +} +void MessageFilter::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) rakNetGUID; + (void) lostConnectionReason; + + AddressOrGUID aog; + aog.rakNetGuid=rakNetGUID; + aog.systemAddress=systemAddress; + + // Lost system, remove from the list + systemList.Remove(aog, _FILE_AND_LINE_); +} + PluginReceiveResult MessageFilter::OnReceive(Packet *packet) +{ + DataStructures::HashIndex index; + unsigned char messageId; + + switch (packet->data[0]) + { + case ID_NEW_INCOMING_CONNECTION: + case ID_CONNECTION_REQUEST_ACCEPTED: + case ID_CONNECTION_LOST: + case ID_DISCONNECTION_NOTIFICATION: + case ID_CONNECTION_ATTEMPT_FAILED: + case ID_NO_FREE_INCOMING_CONNECTIONS: + case ID_IP_RECENTLY_CONNECTED: + case ID_CONNECTION_BANNED: + case ID_INVALID_PASSWORD: + case ID_UNCONNECTED_PONG: + case ID_ALREADY_CONNECTED: + case ID_ADVERTISE_SYSTEM: + case ID_REMOTE_DISCONNECTION_NOTIFICATION: + case ID_REMOTE_CONNECTION_LOST: + case ID_REMOTE_NEW_INCOMING_CONNECTION: + case ID_DOWNLOAD_PROGRESS: + break; + default: + if (packet->data[0]==ID_TIMESTAMP) + { + if (packet->lengthdata[sizeof(MessageID) + sizeof(RakNet::TimeMS)]; + } + else + messageId=packet->data[0]; + // If this system is filtered, check if this message is allowed. If not allowed, return RR_STOP_PROCESSING_AND_DEALLOCATE + // index = systemList.GetIndexFromKey(packet->addressOrGUID, &objectExists); + index = systemList.GetIndexOf(packet); + if (index.IsInvalid()) + break; + if (systemList.ItemAtIndex(index).filter->allowedIDs[messageId]==false) + { + OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + if (packet->data[0]==ID_RPC_PLUGIN) + { + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + RakNet::RakString functionName; + bsIn.ReadCompressed(functionName); + if (systemList.ItemAtIndex(index).filter->allowedRPC4.HasData(functionName)==false) + { + OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + + break; + } + + return RR_CONTINUE_PROCESSING; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/MessageFilter.h b/project/lib_projects/raknet/jni/RaknetSources/MessageFilter.h index 011bb36..8befe7b 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/MessageFilter.h +++ b/project/lib_projects/raknet/jni/RaknetSources/MessageFilter.h @@ -1,191 +1,191 @@ -/// \file -/// \brief Message filter plugin. Assigns systems to FilterSets. Each FilterSet limits what messages are allowed. This is a security related plugin. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_MessageFilter==1 - -#ifndef __MESSAGE_FILTER_PLUGIN_H -#define __MESSAGE_FILTER_PLUGIN_H - -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "DS_OrderedList.h" -#include "DS_Hash.h" -#include "Export.h" - -/// MessageIdentifier (ID_*) values shoudln't go higher than this. Change it if you do. -#define MESSAGE_FILTER_MAX_MESSAGE_ID 256 - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \internal Has to be public so some of the shittier compilers can use it. -int RAK_DLL_EXPORT MessageFilterStrComp( char *const &key,char *const &data ); - -/// \internal Has to be public so some of the shittier compilers can use it. -struct FilterSet -{ - bool banOnFilterTimeExceed; - bool kickOnDisallowedMessage; - bool banOnDisallowedMessage; - RakNet::TimeMS disallowedMessageBanTimeMS; - RakNet::TimeMS timeExceedBanTimeMS; - RakNet::TimeMS maxMemberTimeMS; - void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID); - void *disallowedCallbackUserData; - void (*timeoutCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData); - void *timeoutUserData; - int filterSetID; - bool allowedIDs[MESSAGE_FILTER_MAX_MESSAGE_ID]; - DataStructures::OrderedList allowedRPC4; -}; - -/// \internal Has to be public so some of the shittier compilers can use it. -int RAK_DLL_EXPORT FilterSetComp( const int &key, FilterSet * const &data ); - -/// \internal Has to be public so some of the shittier compilers can use it. -struct FilteredSystem -{ - FilterSet *filter; - RakNet::TimeMS timeEnteredThisSet; -}; - -/// \defgroup MESSAGEFILTER_GROUP MessageFilter -/// \brief Remote incoming packets from unauthorized systems -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief Assigns systems to FilterSets. Each FilterSet limits what kinds of messages are allowed. -/// \details The MessageFilter plugin is used for security where you limit what systems can send what kind of messages.
-/// You implicitly define FilterSets, and add allowed message IDs to these FilterSets.
-/// You then add systems to these filters, such that those systems are limited to sending what the filters allows.
-/// You can automatically assign systems to a filter.
-/// You can automatically kick and possibly ban users that stay in a filter too long, or send the wrong message.
-/// Each system is a member of either zero or one filters.
-/// Add this plugin before any plugin you wish to filter (most likely just add this plugin before any other). -/// \ingroup MESSAGEFILTER_GROUP -class RAK_DLL_EXPORT MessageFilter : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(MessageFilter) - - MessageFilter(); - virtual ~MessageFilter(); - - // -------------------------------------------------------------------------------------------- - // User functions - // -------------------------------------------------------------------------------------------- - - /// Automatically add all new systems to a particular filter - /// Defaults to -1 - /// \param[in] filterSetID Which filter to add new systems to. <0 for do not add. - void SetAutoAddNewConnectionsToFilter(int filterSetID); - - /// Allow a range of message IDs - /// Always allowed by default: ID_CONNECTION_REQUEST_ACCEPTED through ID_DOWNLOAD_PROGRESS - /// Usually you specify a range to make it easier to add new enumerations without having to constantly refer back to this function. - /// \param[in] allow True to allow this message ID, false to disallow. By default, all messageIDs except the noted types are disallowed. This includes messages from other plugins! - /// \param[in] messageIDStart The first ID_* message to allow in the range. Inclusive. - /// \param[in] messageIDEnd The last ID_* message to allow in the range. Inclusive. - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - void SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID); - - /// Allow a specific RPC4 call - /// \pre MessageFilter must be attached before RPC4 - /// \param[in] uniqueID Identifier passed to RegisterFunction() - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - void SetAllowRPC4(bool allow, const char* uniqueID, int filterSetID); - - /// What action to take on a disallowed message. You can kick or not. You can add them to the ban list for some time - /// By default no action is taken. The message is simply ignored. - /// param[in] 0 for permanent ban, >0 for ban time in milliseconds. - /// \param[in] kickOnDisallowed kick the system that sent a disallowed message. - /// \param[in] banOnDisallowed ban the system that sent a disallowed message. See \a banTimeMS for the ban duration - /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - void SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNet::TimeMS banTimeMS, int filterSetID); - - /// Set a user callback to be called on an invalid message for a particular filterSet - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - /// \param[in] userData A pointer passed with the callback - /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. - void SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID addressOrGUID, int filterSetID, void *userData, unsigned char messageID)); - - /// Set a user callback to be called when a user is disconnected due to SetFilterMaxTime - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - /// \param[in] userData A pointer passed with the callback - /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. - void SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID addressOrGUID, int filterSetID, void *userData)); - - /// Limit how long a connection can stay in a particular filterSetID. After this time, the connection is kicked and possibly banned. - /// By default there is no limit to how long a connection can stay in a particular filter set. - /// \param[in] allowedTimeMS How many milliseconds to allow a connection to stay in this filter set. - /// \param[in] banOnExceed True or false to ban the system, or not, when \a allowedTimeMS is exceeded - /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - void SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNet::TimeMS banTimeMS, int filterSetID); - - /// Get the filterSetID a system is using. Returns -1 for none. - /// \param[in] addressOrGUID The system we are referring to - int GetSystemFilterSet(AddressOrGUID addressOrGUID); - - /// Assign a system to a filter set. - /// Systems are automatically added to filter sets (or not) based on SetAutoAddNewConnectionsToFilter() - /// This function is used to change the filter set a system is using, to add it to a new filter set, or to remove it from all existin filter sets. - /// \param[in] addressOrGUID The system we are referring to - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. If -1, the system will be removed from all filter sets. - void SetSystemFilterSet(AddressOrGUID addressOrGUID, int filterSetID); - - /// Returns the number of systems subscribed to a particular filter set - /// Using anything other than -1 for \a filterSetID is slow, so you should store the returned value. - /// \param[in] filterSetID The filter set to limit to. Use -1 for none (just returns the total number of filter systems in that case). - unsigned GetSystemCount(int filterSetID) const; - - /// Returns the total number of filter sets. - /// \return The total number of filter sets. - unsigned GetFilterSetCount(void) const; - - /// Returns the ID of a filter set, by index - /// \param[in] An index between 0 and GetFilterSetCount()-1 inclusive - int GetFilterSetIDByIndex(unsigned index); - - /// Delete a FilterSet. All systems formerly subscribed to this filter are now unrestricted. - /// \param[in] filterSetID The ID of the filter set to delete. - void DeleteFilterSet(int filterSetID); - - // -------------------------------------------------------------------------------------------- - // Packet handling functions - // -------------------------------------------------------------------------------------------- - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - -protected: - - void Clear(void); - void DeallocateFilterSet(FilterSet *filterSet); - FilterSet* GetFilterSetByID(int filterSetID); - void OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID); - - DataStructures::OrderedList filterList; - // Change to guid - DataStructures::Hash systemList; - - int autoAddNewConnectionsToFilter; - RakNet::Time whenLastTimeoutCheck; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Message filter plugin. Assigns systems to FilterSets. Each FilterSet limits what messages are allowed. This is a security related plugin. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_MessageFilter==1 + +#ifndef __MESSAGE_FILTER_PLUGIN_H +#define __MESSAGE_FILTER_PLUGIN_H + +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "DS_OrderedList.h" +#include "DS_Hash.h" +#include "Export.h" + +/// MessageIdentifier (ID_*) values shoudln't go higher than this. Change it if you do. +#define MESSAGE_FILTER_MAX_MESSAGE_ID 256 + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \internal Has to be public so some of the shittier compilers can use it. +int RAK_DLL_EXPORT MessageFilterStrComp( char *const &key,char *const &data ); + +/// \internal Has to be public so some of the shittier compilers can use it. +struct FilterSet +{ + bool banOnFilterTimeExceed; + bool kickOnDisallowedMessage; + bool banOnDisallowedMessage; + RakNet::TimeMS disallowedMessageBanTimeMS; + RakNet::TimeMS timeExceedBanTimeMS; + RakNet::TimeMS maxMemberTimeMS; + void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID); + void *disallowedCallbackUserData; + void (*timeoutCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData); + void *timeoutUserData; + int filterSetID; + bool allowedIDs[MESSAGE_FILTER_MAX_MESSAGE_ID]; + DataStructures::OrderedList allowedRPC4; +}; + +/// \internal Has to be public so some of the shittier compilers can use it. +int RAK_DLL_EXPORT FilterSetComp( const int &key, FilterSet * const &data ); + +/// \internal Has to be public so some of the shittier compilers can use it. +struct FilteredSystem +{ + FilterSet *filter; + RakNet::TimeMS timeEnteredThisSet; +}; + +/// \defgroup MESSAGEFILTER_GROUP MessageFilter +/// \brief Remote incoming packets from unauthorized systems +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief Assigns systems to FilterSets. Each FilterSet limits what kinds of messages are allowed. +/// \details The MessageFilter plugin is used for security where you limit what systems can send what kind of messages.
+/// You implicitly define FilterSets, and add allowed message IDs to these FilterSets.
+/// You then add systems to these filters, such that those systems are limited to sending what the filters allows.
+/// You can automatically assign systems to a filter.
+/// You can automatically kick and possibly ban users that stay in a filter too long, or send the wrong message.
+/// Each system is a member of either zero or one filters.
+/// Add this plugin before any plugin you wish to filter (most likely just add this plugin before any other). +/// \ingroup MESSAGEFILTER_GROUP +class RAK_DLL_EXPORT MessageFilter : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(MessageFilter) + + MessageFilter(); + virtual ~MessageFilter(); + + // -------------------------------------------------------------------------------------------- + // User functions + // -------------------------------------------------------------------------------------------- + + /// Automatically add all new systems to a particular filter + /// Defaults to -1 + /// \param[in] filterSetID Which filter to add new systems to. <0 for do not add. + void SetAutoAddNewConnectionsToFilter(int filterSetID); + + /// Allow a range of message IDs + /// Always allowed by default: ID_CONNECTION_REQUEST_ACCEPTED through ID_DOWNLOAD_PROGRESS + /// Usually you specify a range to make it easier to add new enumerations without having to constantly refer back to this function. + /// \param[in] allow True to allow this message ID, false to disallow. By default, all messageIDs except the noted types are disallowed. This includes messages from other plugins! + /// \param[in] messageIDStart The first ID_* message to allow in the range. Inclusive. + /// \param[in] messageIDEnd The last ID_* message to allow in the range. Inclusive. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID); + + /// Allow a specific RPC4 call + /// \pre MessageFilter must be attached before RPC4 + /// \param[in] uniqueID Identifier passed to RegisterFunction() + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetAllowRPC4(bool allow, const char* uniqueID, int filterSetID); + + /// What action to take on a disallowed message. You can kick or not. You can add them to the ban list for some time + /// By default no action is taken. The message is simply ignored. + /// param[in] 0 for permanent ban, >0 for ban time in milliseconds. + /// \param[in] kickOnDisallowed kick the system that sent a disallowed message. + /// \param[in] banOnDisallowed ban the system that sent a disallowed message. See \a banTimeMS for the ban duration + /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNet::TimeMS banTimeMS, int filterSetID); + + /// Set a user callback to be called on an invalid message for a particular filterSet + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + /// \param[in] userData A pointer passed with the callback + /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. + void SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID addressOrGUID, int filterSetID, void *userData, unsigned char messageID)); + + /// Set a user callback to be called when a user is disconnected due to SetFilterMaxTime + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + /// \param[in] userData A pointer passed with the callback + /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. + void SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID addressOrGUID, int filterSetID, void *userData)); + + /// Limit how long a connection can stay in a particular filterSetID. After this time, the connection is kicked and possibly banned. + /// By default there is no limit to how long a connection can stay in a particular filter set. + /// \param[in] allowedTimeMS How many milliseconds to allow a connection to stay in this filter set. + /// \param[in] banOnExceed True or false to ban the system, or not, when \a allowedTimeMS is exceeded + /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNet::TimeMS banTimeMS, int filterSetID); + + /// Get the filterSetID a system is using. Returns -1 for none. + /// \param[in] addressOrGUID The system we are referring to + int GetSystemFilterSet(AddressOrGUID addressOrGUID); + + /// Assign a system to a filter set. + /// Systems are automatically added to filter sets (or not) based on SetAutoAddNewConnectionsToFilter() + /// This function is used to change the filter set a system is using, to add it to a new filter set, or to remove it from all existin filter sets. + /// \param[in] addressOrGUID The system we are referring to + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. If -1, the system will be removed from all filter sets. + void SetSystemFilterSet(AddressOrGUID addressOrGUID, int filterSetID); + + /// Returns the number of systems subscribed to a particular filter set + /// Using anything other than -1 for \a filterSetID is slow, so you should store the returned value. + /// \param[in] filterSetID The filter set to limit to. Use -1 for none (just returns the total number of filter systems in that case). + unsigned GetSystemCount(int filterSetID) const; + + /// Returns the total number of filter sets. + /// \return The total number of filter sets. + unsigned GetFilterSetCount(void) const; + + /// Returns the ID of a filter set, by index + /// \param[in] An index between 0 and GetFilterSetCount()-1 inclusive + int GetFilterSetIDByIndex(unsigned index); + + /// Delete a FilterSet. All systems formerly subscribed to this filter are now unrestricted. + /// \param[in] filterSetID The ID of the filter set to delete. + void DeleteFilterSet(int filterSetID); + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + +protected: + + void Clear(void); + void DeallocateFilterSet(FilterSet *filterSet); + FilterSet* GetFilterSetByID(int filterSetID); + void OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID); + + DataStructures::OrderedList filterList; + // Change to guid + DataStructures::Hash systemList; + + int autoAddNewConnectionsToFilter; + RakNet::Time whenLastTimeoutCheck; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/MessageIdentifiers.h b/project/lib_projects/raknet/jni/RaknetSources/MessageIdentifiers.h index 65917db..6788de2 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/MessageIdentifiers.h +++ b/project/lib_projects/raknet/jni/RaknetSources/MessageIdentifiers.h @@ -1,403 +1,403 @@ -/// \file -/// \brief All the message identifiers used by RakNet. Message identifiers comprise the first byte of any message. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __MESSAGE_IDENTIFIERS_H -#define __MESSAGE_IDENTIFIERS_H - -#if defined(RAKNET_USE_CUSTOM_PACKET_IDS) -#include "CustomPacketIdentifiers.h" -#else - -enum OutOfBandIdentifiers -{ - ID_NAT_ESTABLISH_UNIDIRECTIONAL, - ID_NAT_ESTABLISH_BIDIRECTIONAL, - ID_NAT_TYPE_DETECT, - ID_ROUTER_2_REPLY_TO_SENDER_PORT, - ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, - ID_ROUTER_2_MINI_PUNCH_REPLY, - ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, - ID_XBOX_360_VOICE, - ID_XBOX_360_GET_NETWORK_ROOM, - ID_XBOX_360_RETURN_NETWORK_ROOM, -}; - -/// You should not edit the file MessageIdentifiers.h as it is a part of RakNet static library -/// To define your own message id, define an enum following the code example that follows. -/// -/// \code -/// enum { -/// ID_MYPROJECT_MSG_1 = ID_USER_PACKET_ENUM, -/// ID_MYPROJECT_MSG_2, -/// ... -/// }; -/// \endcode -/// -/// \note All these enumerations should be casted to (unsigned char) before writing them to RakNet::BitStream -enum DefaultMessageIDTypes -{ - // - // RESERVED TYPES - DO NOT CHANGE THESE - // All types from RakPeer - // - /// These types are never returned to the user. - /// Ping from a connected system. Update timestamps (internal use only) - ID_CONNECTED_PING, - /// Ping from an unconnected system. Reply but do not update timestamps. (internal use only) - ID_UNCONNECTED_PING, - /// Ping from an unconnected system. Only reply if we have open connections. Do not update timestamps. (internal use only) - ID_UNCONNECTED_PING_OPEN_CONNECTIONS, - /// Pong from a connected system. Update timestamps (internal use only) - ID_CONNECTED_PONG, - /// A reliable packet to detect lost connections (internal use only) - ID_DETECT_LOST_CONNECTIONS, - /// C2S: Initial query: Header(1), OfflineMesageID(16), Protocol number(1), Pad(toMTU), sent with no fragment set. - /// If protocol fails on server, returns ID_INCOMPATIBLE_PROTOCOL_VERSION to client - ID_OPEN_CONNECTION_REQUEST_1, - /// S2C: Header(1), OfflineMesageID(16), server GUID(8), HasSecurity(1), Cookie(4, if HasSecurity) - /// , public key (if do security is true), MTU(2). If public key fails on client, returns ID_PUBLIC_KEY_MISMATCH - ID_OPEN_CONNECTION_REPLY_1, - /// C2S: Header(1), OfflineMesageID(16), Cookie(4, if HasSecurity is true on the server), clientSupportsSecurity(1 bit), - /// handshakeChallenge (if has security on both server and client), remoteBindingAddress(6), MTU(2), client GUID(8) - /// Connection slot allocated if cookie is valid, server is not full, GUID and IP not already in use. - ID_OPEN_CONNECTION_REQUEST_2, - /// S2C: Header(1), OfflineMesageID(16), server GUID(8), mtu(2), doSecurity(1 bit), handshakeAnswer (if do security is true) - ID_OPEN_CONNECTION_REPLY_2, - /// C2S: Header(1), GUID(8), Timestamp, HasSecurity(1), Proof(32) - ID_CONNECTION_REQUEST, - /// RakPeer - Remote system requires secure connections, pass a public key to RakPeerInterface::Connect() - ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY, - /// RakPeer - We passed a public key to RakPeerInterface::Connect(), but the other system did not have security turned on - ID_OUR_SYSTEM_REQUIRES_SECURITY, - /// RakPeer - Wrong public key passed to RakPeerInterface::Connect() - ID_PUBLIC_KEY_MISMATCH, - /// RakPeer - Same as ID_ADVERTISE_SYSTEM, but intended for internal use rather than being passed to the user. - /// Second byte indicates type. Used currently for NAT punchthrough for receiver port advertisement. See ID_NAT_ADVERTISE_RECIPIENT_PORT - ID_OUT_OF_BAND_INTERNAL, - /// If RakPeerInterface::Send() is called where PacketReliability contains _WITH_ACK_RECEIPT, then on a later call to - /// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long, - /// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message. - /// This number will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_ACKED means that - /// the message arrived - ID_SND_RECEIPT_ACKED, - /// If RakPeerInterface::Send() is called where PacketReliability contains UNRELIABLE_WITH_ACK_RECEIPT, then on a later call to - /// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long, - /// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message. This number - /// will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_LOSS means that an ack for the - /// message did not arrive (it may or may not have been delivered, probably not). On disconnect or shutdown, you will not get - /// ID_SND_RECEIPT_LOSS for unsent messages, you should consider those messages as all lost. - ID_SND_RECEIPT_LOSS, - - - // - // USER TYPES - DO NOT CHANGE THESE - // - - /// RakPeer - In a client/server environment, our connection request to the server has been accepted. - ID_CONNECTION_REQUEST_ACCEPTED, - /// RakPeer - Sent to the player when a connection request cannot be completed due to inability to connect. - ID_CONNECTION_ATTEMPT_FAILED, - /// RakPeer - Sent a connect request to a system we are currently connected to. - ID_ALREADY_CONNECTED, - /// RakPeer - A remote system has successfully connected. - ID_NEW_INCOMING_CONNECTION, - /// RakPeer - The system we attempted to connect to is not accepting new connections. - ID_NO_FREE_INCOMING_CONNECTIONS, - /// RakPeer - The system specified in Packet::systemAddress has disconnected from us. For the client, this would mean the - /// server has shutdown. - ID_DISCONNECTION_NOTIFICATION, - /// RakPeer - Reliable packets cannot be delivered to the system specified in Packet::systemAddress. The connection to that - /// system has been closed. - ID_CONNECTION_LOST, - /// RakPeer - We are banned from the system we attempted to connect to. - ID_CONNECTION_BANNED, - /// RakPeer - The remote system is using a password and has refused our connection because we did not set the correct password. - ID_INVALID_PASSWORD, - // RAKNET_PROTOCOL_VERSION in RakNetVersion.h does not match on the remote system what we have on our system - // This means the two systems cannot communicate. - // The 2nd byte of the message contains the value of RAKNET_PROTOCOL_VERSION for the remote system - ID_INCOMPATIBLE_PROTOCOL_VERSION, - // Means that this IP address connected recently, and can't connect again as a security measure. See - /// RakPeer::SetLimitIPConnectionFrequency() - ID_IP_RECENTLY_CONNECTED, - /// RakPeer - The sizeof(RakNetTime) bytes following this byte represent a value which is automatically modified by the difference - /// in system times between the sender and the recipient. Requires that you call SetOccasionalPing. - ID_TIMESTAMP, - /// RakPeer - Pong from an unconnected system. First byte is ID_UNCONNECTED_PONG, second sizeof(RakNet::TimeMS) bytes is the ping, - /// following bytes is system specific enumeration data. - /// Read using bitstreams - ID_UNCONNECTED_PONG, - /// RakPeer - Inform a remote system of our IP/Port. On the recipient, all data past ID_ADVERTISE_SYSTEM is whatever was passed to - /// the data parameter - ID_ADVERTISE_SYSTEM, - // RakPeer - Downloading a large message. Format is ID_DOWNLOAD_PROGRESS (MessageID), partCount (unsigned int), - /// partTotal (unsigned int), - /// partLength (unsigned int), first part data (length <= MAX_MTU_SIZE). See the three parameters partCount, partTotal - /// and partLength in OnFileProgress in FileListTransferCBInterface.h - ID_DOWNLOAD_PROGRESS, - - /// ConnectionGraph2 plugin - In a client/server environment, a client other than ourselves has disconnected gracefully. - /// Packet::systemAddress is modified to reflect the systemAddress of this client. - ID_REMOTE_DISCONNECTION_NOTIFICATION, - /// ConnectionGraph2 plugin - In a client/server environment, a client other than ourselves has been forcefully dropped. - /// Packet::systemAddress is modified to reflect the systemAddress of this client. - ID_REMOTE_CONNECTION_LOST, - /// ConnectionGraph2 plugin: Bytes 1-4 = count. for (count items) contains {SystemAddress, RakNetGUID, 2 byte ping} - ID_REMOTE_NEW_INCOMING_CONNECTION, - - /// FileListTransfer plugin - Setup data - ID_FILE_LIST_TRANSFER_HEADER, - /// FileListTransfer plugin - A file - ID_FILE_LIST_TRANSFER_FILE, - // Ack for reference push, to send more of the file - ID_FILE_LIST_REFERENCE_PUSH_ACK, - - /// DirectoryDeltaTransfer plugin - Request from a remote system for a download of a directory - ID_DDT_DOWNLOAD_REQUEST, - - /// RakNetTransport plugin - Transport provider message, used for remote console - ID_TRANSPORT_STRING, - - /// ReplicaManager plugin - Create an object - ID_REPLICA_MANAGER_CONSTRUCTION, - /// ReplicaManager plugin - Changed scope of an object - ID_REPLICA_MANAGER_SCOPE_CHANGE, - /// ReplicaManager plugin - Serialized data of an object - ID_REPLICA_MANAGER_SERIALIZE, - /// ReplicaManager plugin - New connection, about to send all world objects - ID_REPLICA_MANAGER_DOWNLOAD_STARTED, - /// ReplicaManager plugin - Finished downloading all serialized objects - ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE, - - /// RakVoice plugin - Open a communication channel - ID_RAKVOICE_OPEN_CHANNEL_REQUEST, - /// RakVoice plugin - Communication channel accepted - ID_RAKVOICE_OPEN_CHANNEL_REPLY, - /// RakVoice plugin - Close a communication channel - ID_RAKVOICE_CLOSE_CHANNEL, - /// RakVoice plugin - Voice data - ID_RAKVOICE_DATA, - - /// Autopatcher plugin - Get a list of files that have changed since a certain date - ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE, - /// Autopatcher plugin - A list of files to create - ID_AUTOPATCHER_CREATION_LIST, - /// Autopatcher plugin - A list of files to delete - ID_AUTOPATCHER_DELETION_LIST, - /// Autopatcher plugin - A list of files to get patches for - ID_AUTOPATCHER_GET_PATCH, - /// Autopatcher plugin - A list of patches for a list of files - ID_AUTOPATCHER_PATCH_LIST, - /// Autopatcher plugin - Returned to the user: An error from the database repository for the autopatcher. - ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR, - /// Autopatcher plugin - Finished getting all files from the autopatcher - ID_AUTOPATCHER_FINISHED_INTERNAL, - ID_AUTOPATCHER_FINISHED, - /// Autopatcher plugin - Returned to the user: You must restart the application to finish patching. - ID_AUTOPATCHER_RESTART_APPLICATION, - - /// NATPunchthrough plugin: internal - ID_NAT_PUNCHTHROUGH_REQUEST, - /// NATPunchthrough plugin: internal - ID_NAT_GROUP_PUNCHTHROUGH_REQUEST, - /// NATPunchthrough plugin: internal - ID_NAT_GROUP_PUNCHTHROUGH_REPLY, - /// NATPunchthrough plugin: internal - ID_NAT_CONNECT_AT_TIME, - /// NATPunchthrough plugin: internal - ID_NAT_GET_MOST_RECENT_PORT, - /// NATPunchthrough plugin: internal - ID_NAT_CLIENT_READY, - /// NATPunchthrough plugin: internal - ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION, - - /// NATPunchthrough plugin: Destination system is not connected to the server. Bytes starting at offset 1 contains the - /// RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). - ID_NAT_TARGET_NOT_CONNECTED, - /// NATPunchthrough plugin: Destination system is not responding to ID_NAT_GET_MOST_RECENT_PORT. Possibly the plugin is not installed. - /// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). - ID_NAT_TARGET_UNRESPONSIVE, - /// NATPunchthrough plugin: The server lost the connection to the destination system while setting up punchthrough. - /// Possibly the plugin is not installed. Bytes starting at offset 1 contains the RakNetGUID destination - /// field of NatPunchthroughClient::OpenNAT(). - ID_NAT_CONNECTION_TO_TARGET_LOST, - /// NATPunchthrough plugin: This punchthrough is already in progress. Possibly the plugin is not installed. - /// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). - ID_NAT_ALREADY_IN_PROGRESS, - /// NATPunchthrough plugin: This message is generated on the local system, and does not come from the network. - /// packet::guid contains the destination field of NatPunchthroughClient::OpenNAT(). Byte 1 contains 1 if you are the sender, 0 if not - ID_NAT_PUNCHTHROUGH_FAILED, - /// NATPunchthrough plugin: Punchthrough succeeded. See packet::systemAddress and packet::guid. Byte 1 contains 1 if you are the sender, - /// 0 if not. You can now use RakPeer::Connect() or other calls to communicate with this system. - ID_NAT_PUNCHTHROUGH_SUCCEEDED, - /// NATPunchthrough plugin: OpenNATGroup failed. - /// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT() - /// Data format starts at byte 1:
- /// (char) passedSystemsCount,
- /// (RakNetGuid, SystemAddress) (for passedSystemsCount),
- /// (char) ignoredSystemsCount (caused by ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_TARGET_UNRESPONSIVE),
- /// RakNetGuid (for ignoredSystemsCount),
- /// (char) failedSystemsCount,
- /// RakNetGuid (for failedSystemsCount)
- ID_NAT_GROUP_PUNCH_FAILED, - /// NATPunchthrough plugin: OpenNATGroup succeeded. - /// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT() - /// See ID_NAT_GROUP_PUNCH_FAILED for data format - ID_NAT_GROUP_PUNCH_SUCCEEDED, - - /// ReadyEvent plugin - Set the ready state for a particular system - /// First 4 bytes after the message contains the id - ID_READY_EVENT_SET, - /// ReadyEvent plugin - Unset the ready state for a particular system - /// First 4 bytes after the message contains the id - ID_READY_EVENT_UNSET, - /// All systems are in state ID_READY_EVENT_SET - /// First 4 bytes after the message contains the id - ID_READY_EVENT_ALL_SET, - /// \internal, do not process in your game - /// ReadyEvent plugin - Request of ready event state - used for pulling data when newly connecting - ID_READY_EVENT_QUERY, - - /// Lobby packets. Second byte indicates type. - ID_LOBBY_GENERAL, - - // RPC3, RPC4Plugin error - ID_RPC_REMOTE_ERROR, - /// Plugin based replacement for RPC system - ID_RPC_PLUGIN, - - /// FileListTransfer transferring large files in chunks that are read only when needed, to save memory - ID_FILE_LIST_REFERENCE_PUSH, - /// Force the ready event to all set - ID_READY_EVENT_FORCE_ALL_SET, - - /// Rooms function - ID_ROOMS_EXECUTE_FUNC, - ID_ROOMS_LOGON_STATUS, - ID_ROOMS_HANDLE_CHANGE, - - /// Lobby2 message - ID_LOBBY2_SEND_MESSAGE, - ID_LOBBY2_SERVER_ERROR, - - /// Informs user of a new host GUID. Packet::Guid contains this new host RakNetGuid. The old host can be read out using BitStream->Read(RakNetGuid) starting on byte 1 - /// This is not returned until connected to a remote system - /// If the oldHost is UNASSIGNED_RAKNET_GUID, then this is the first time the host has been determined - ID_FCM2_NEW_HOST, - /// \internal For FullyConnectedMesh2 plugin - ID_FCM2_REQUEST_FCMGUID, - /// \internal For FullyConnectedMesh2 plugin - ID_FCM2_RESPOND_CONNECTION_COUNT, - /// \internal For FullyConnectedMesh2 plugin - ID_FCM2_INFORM_FCMGUID, - /// \internal For FullyConnectedMesh2 plugin - ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT, - - /// UDP proxy messages. Second byte indicates type. - ID_UDP_PROXY_GENERAL, - - /// SQLite3Plugin - execute - ID_SQLite3_EXEC, - /// SQLite3Plugin - Remote database is unknown - ID_SQLite3_UNKNOWN_DB, - /// Events happening with SQLiteClientLoggerPlugin - ID_SQLLITE_LOGGER, - - /// Sent to NatTypeDetectionServer - ID_NAT_TYPE_DETECTION_REQUEST, - /// Sent to NatTypeDetectionClient. Byte 1 contains the type of NAT detected. - ID_NAT_TYPE_DETECTION_RESULT, - - /// Used by the router2 plugin - ID_ROUTER_2_INTERNAL, - /// No path is available or can be established to the remote system - /// Packet::guid contains the endpoint guid that we were trying to reach - ID_ROUTER_2_FORWARDING_NO_PATH, - /// \brief You can now call connect, ping, or other operations to the destination system. - /// - /// Connect as follows: - /// - /// RakNet::BitStream bs(packet->data, packet->length, false); - /// bs.IgnoreBytes(sizeof(MessageID)); - /// RakNetGUID endpointGuid; - /// bs.Read(endpointGuid); - /// unsigned short sourceToDestPort; - /// bs.Read(sourceToDestPort); - /// char ipAddressString[32]; - /// packet->systemAddress.ToString(false, ipAddressString); - /// rakPeerInterface->Connect(ipAddressString, sourceToDestPort, 0,0); - ID_ROUTER_2_FORWARDING_ESTABLISHED, - /// The IP address for a forwarded connection has changed - /// Read endpointGuid and port as per ID_ROUTER_2_FORWARDING_ESTABLISHED - ID_ROUTER_2_REROUTED, - - /// \internal Used by the team balancer plugin - ID_TEAM_BALANCER_INTERNAL, - /// Cannot switch to the desired team because it is full. However, if someone on that team leaves, you will - /// get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member. - ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING, - /// Cannot switch to the desired team because all teams are locked. However, if someone on that team leaves, - /// you will get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join. - ID_TEAM_BALANCER_TEAMS_LOCKED, - /// Team balancer plugin informing you of your team. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member. - ID_TEAM_BALANCER_TEAM_ASSIGNED, - - /// Gamebryo Lightspeed integration - ID_LIGHTSPEED_INTEGRATION, - - /// XBOX integration - ID_XBOX_LOBBY, - - /// The password we used to challenge the other system passed, meaning the other system has called TwoWayAuthentication::AddPassword() with the same password we passed to TwoWayAuthentication::Challenge() - /// You can read the identifier used to challenge as follows: - /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(RakNet::MessageID)); RakNet::RakString password; bs.Read(password); - ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, - ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS, - /// A remote system sent us a challenge using TwoWayAuthentication::Challenge(), and the challenge failed. - /// If the other system must pass the challenge to stay connected, you should call RakPeer::CloseConnection() to terminate the connection to the other system. - ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE, - /// The other system did not add the password we used to TwoWayAuthentication::AddPassword() - /// You can read the identifier used to challenge as follows: - /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password); - ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE, - /// The other system did not respond within a timeout threshhold. Either the other system is not running the plugin or the other system was blocking on some operation for a long time. - /// You can read the identifier used to challenge as follows: - /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password); - ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, - /// \internal - ID_TWO_WAY_AUTHENTICATION_NEGOTIATION, - - /// CloudClient / CloudServer - ID_CLOUD_POST_REQUEST, - ID_CLOUD_RELEASE_REQUEST, - ID_CLOUD_GET_REQUEST, - ID_CLOUD_GET_RESPONSE, - ID_CLOUD_UNSUBSCRIBE_REQUEST, - ID_CLOUD_SERVER_TO_SERVER_COMMAND, - ID_CLOUD_SUBSCRIPTION_NOTIFICATION, - - // So I can add more without changing user enumerations - ID_RESERVED_1, - ID_RESERVED_2, - ID_RESERVED_3, - ID_RESERVED_4, - ID_RESERVED_5, - ID_RESERVED_6, - ID_RESERVED_7, - ID_RESERVED_8, - ID_RESERVED_9, - - // For the user to use. Start your first enumeration at this value. - ID_USER_PACKET_ENUM, - //------------------------------------------------------------------------------------------------------------- - -}; - -#endif // RAKNET_USE_CUSTOM_PACKET_IDS - -#endif +/// \file +/// \brief All the message identifiers used by RakNet. Message identifiers comprise the first byte of any message. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __MESSAGE_IDENTIFIERS_H +#define __MESSAGE_IDENTIFIERS_H + +#if defined(RAKNET_USE_CUSTOM_PACKET_IDS) +#include "CustomPacketIdentifiers.h" +#else + +enum OutOfBandIdentifiers +{ + ID_NAT_ESTABLISH_UNIDIRECTIONAL, + ID_NAT_ESTABLISH_BIDIRECTIONAL, + ID_NAT_TYPE_DETECT, + ID_ROUTER_2_REPLY_TO_SENDER_PORT, + ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, + ID_ROUTER_2_MINI_PUNCH_REPLY, + ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, + ID_XBOX_360_VOICE, + ID_XBOX_360_GET_NETWORK_ROOM, + ID_XBOX_360_RETURN_NETWORK_ROOM, +}; + +/// You should not edit the file MessageIdentifiers.h as it is a part of RakNet static library +/// To define your own message id, define an enum following the code example that follows. +/// +/// \code +/// enum { +/// ID_MYPROJECT_MSG_1 = ID_USER_PACKET_ENUM, +/// ID_MYPROJECT_MSG_2, +/// ... +/// }; +/// \endcode +/// +/// \note All these enumerations should be casted to (unsigned char) before writing them to RakNet::BitStream +enum DefaultMessageIDTypes +{ + // + // RESERVED TYPES - DO NOT CHANGE THESE + // All types from RakPeer + // + /// These types are never returned to the user. + /// Ping from a connected system. Update timestamps (internal use only) + ID_CONNECTED_PING, + /// Ping from an unconnected system. Reply but do not update timestamps. (internal use only) + ID_UNCONNECTED_PING, + /// Ping from an unconnected system. Only reply if we have open connections. Do not update timestamps. (internal use only) + ID_UNCONNECTED_PING_OPEN_CONNECTIONS, + /// Pong from a connected system. Update timestamps (internal use only) + ID_CONNECTED_PONG, + /// A reliable packet to detect lost connections (internal use only) + ID_DETECT_LOST_CONNECTIONS, + /// C2S: Initial query: Header(1), OfflineMesageID(16), Protocol number(1), Pad(toMTU), sent with no fragment set. + /// If protocol fails on server, returns ID_INCOMPATIBLE_PROTOCOL_VERSION to client + ID_OPEN_CONNECTION_REQUEST_1, + /// S2C: Header(1), OfflineMesageID(16), server GUID(8), HasSecurity(1), Cookie(4, if HasSecurity) + /// , public key (if do security is true), MTU(2). If public key fails on client, returns ID_PUBLIC_KEY_MISMATCH + ID_OPEN_CONNECTION_REPLY_1, + /// C2S: Header(1), OfflineMesageID(16), Cookie(4, if HasSecurity is true on the server), clientSupportsSecurity(1 bit), + /// handshakeChallenge (if has security on both server and client), remoteBindingAddress(6), MTU(2), client GUID(8) + /// Connection slot allocated if cookie is valid, server is not full, GUID and IP not already in use. + ID_OPEN_CONNECTION_REQUEST_2, + /// S2C: Header(1), OfflineMesageID(16), server GUID(8), mtu(2), doSecurity(1 bit), handshakeAnswer (if do security is true) + ID_OPEN_CONNECTION_REPLY_2, + /// C2S: Header(1), GUID(8), Timestamp, HasSecurity(1), Proof(32) + ID_CONNECTION_REQUEST, + /// RakPeer - Remote system requires secure connections, pass a public key to RakPeerInterface::Connect() + ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY, + /// RakPeer - We passed a public key to RakPeerInterface::Connect(), but the other system did not have security turned on + ID_OUR_SYSTEM_REQUIRES_SECURITY, + /// RakPeer - Wrong public key passed to RakPeerInterface::Connect() + ID_PUBLIC_KEY_MISMATCH, + /// RakPeer - Same as ID_ADVERTISE_SYSTEM, but intended for internal use rather than being passed to the user. + /// Second byte indicates type. Used currently for NAT punchthrough for receiver port advertisement. See ID_NAT_ADVERTISE_RECIPIENT_PORT + ID_OUT_OF_BAND_INTERNAL, + /// If RakPeerInterface::Send() is called where PacketReliability contains _WITH_ACK_RECEIPT, then on a later call to + /// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long, + /// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message. + /// This number will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_ACKED means that + /// the message arrived + ID_SND_RECEIPT_ACKED, + /// If RakPeerInterface::Send() is called where PacketReliability contains UNRELIABLE_WITH_ACK_RECEIPT, then on a later call to + /// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long, + /// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message. This number + /// will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_LOSS means that an ack for the + /// message did not arrive (it may or may not have been delivered, probably not). On disconnect or shutdown, you will not get + /// ID_SND_RECEIPT_LOSS for unsent messages, you should consider those messages as all lost. + ID_SND_RECEIPT_LOSS, + + + // + // USER TYPES - DO NOT CHANGE THESE + // + + /// RakPeer - In a client/server environment, our connection request to the server has been accepted. + ID_CONNECTION_REQUEST_ACCEPTED, + /// RakPeer - Sent to the player when a connection request cannot be completed due to inability to connect. + ID_CONNECTION_ATTEMPT_FAILED, + /// RakPeer - Sent a connect request to a system we are currently connected to. + ID_ALREADY_CONNECTED, + /// RakPeer - A remote system has successfully connected. + ID_NEW_INCOMING_CONNECTION, + /// RakPeer - The system we attempted to connect to is not accepting new connections. + ID_NO_FREE_INCOMING_CONNECTIONS, + /// RakPeer - The system specified in Packet::systemAddress has disconnected from us. For the client, this would mean the + /// server has shutdown. + ID_DISCONNECTION_NOTIFICATION, + /// RakPeer - Reliable packets cannot be delivered to the system specified in Packet::systemAddress. The connection to that + /// system has been closed. + ID_CONNECTION_LOST, + /// RakPeer - We are banned from the system we attempted to connect to. + ID_CONNECTION_BANNED, + /// RakPeer - The remote system is using a password and has refused our connection because we did not set the correct password. + ID_INVALID_PASSWORD, + // RAKNET_PROTOCOL_VERSION in RakNetVersion.h does not match on the remote system what we have on our system + // This means the two systems cannot communicate. + // The 2nd byte of the message contains the value of RAKNET_PROTOCOL_VERSION for the remote system + ID_INCOMPATIBLE_PROTOCOL_VERSION, + // Means that this IP address connected recently, and can't connect again as a security measure. See + /// RakPeer::SetLimitIPConnectionFrequency() + ID_IP_RECENTLY_CONNECTED, + /// RakPeer - The sizeof(RakNetTime) bytes following this byte represent a value which is automatically modified by the difference + /// in system times between the sender and the recipient. Requires that you call SetOccasionalPing. + ID_TIMESTAMP, + /// RakPeer - Pong from an unconnected system. First byte is ID_UNCONNECTED_PONG, second sizeof(RakNet::TimeMS) bytes is the ping, + /// following bytes is system specific enumeration data. + /// Read using bitstreams + ID_UNCONNECTED_PONG, + /// RakPeer - Inform a remote system of our IP/Port. On the recipient, all data past ID_ADVERTISE_SYSTEM is whatever was passed to + /// the data parameter + ID_ADVERTISE_SYSTEM, + // RakPeer - Downloading a large message. Format is ID_DOWNLOAD_PROGRESS (MessageID), partCount (unsigned int), + /// partTotal (unsigned int), + /// partLength (unsigned int), first part data (length <= MAX_MTU_SIZE). See the three parameters partCount, partTotal + /// and partLength in OnFileProgress in FileListTransferCBInterface.h + ID_DOWNLOAD_PROGRESS, + + /// ConnectionGraph2 plugin - In a client/server environment, a client other than ourselves has disconnected gracefully. + /// Packet::systemAddress is modified to reflect the systemAddress of this client. + ID_REMOTE_DISCONNECTION_NOTIFICATION, + /// ConnectionGraph2 plugin - In a client/server environment, a client other than ourselves has been forcefully dropped. + /// Packet::systemAddress is modified to reflect the systemAddress of this client. + ID_REMOTE_CONNECTION_LOST, + /// ConnectionGraph2 plugin: Bytes 1-4 = count. for (count items) contains {SystemAddress, RakNetGUID, 2 byte ping} + ID_REMOTE_NEW_INCOMING_CONNECTION, + + /// FileListTransfer plugin - Setup data + ID_FILE_LIST_TRANSFER_HEADER, + /// FileListTransfer plugin - A file + ID_FILE_LIST_TRANSFER_FILE, + // Ack for reference push, to send more of the file + ID_FILE_LIST_REFERENCE_PUSH_ACK, + + /// DirectoryDeltaTransfer plugin - Request from a remote system for a download of a directory + ID_DDT_DOWNLOAD_REQUEST, + + /// RakNetTransport plugin - Transport provider message, used for remote console + ID_TRANSPORT_STRING, + + /// ReplicaManager plugin - Create an object + ID_REPLICA_MANAGER_CONSTRUCTION, + /// ReplicaManager plugin - Changed scope of an object + ID_REPLICA_MANAGER_SCOPE_CHANGE, + /// ReplicaManager plugin - Serialized data of an object + ID_REPLICA_MANAGER_SERIALIZE, + /// ReplicaManager plugin - New connection, about to send all world objects + ID_REPLICA_MANAGER_DOWNLOAD_STARTED, + /// ReplicaManager plugin - Finished downloading all serialized objects + ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE, + + /// RakVoice plugin - Open a communication channel + ID_RAKVOICE_OPEN_CHANNEL_REQUEST, + /// RakVoice plugin - Communication channel accepted + ID_RAKVOICE_OPEN_CHANNEL_REPLY, + /// RakVoice plugin - Close a communication channel + ID_RAKVOICE_CLOSE_CHANNEL, + /// RakVoice plugin - Voice data + ID_RAKVOICE_DATA, + + /// Autopatcher plugin - Get a list of files that have changed since a certain date + ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE, + /// Autopatcher plugin - A list of files to create + ID_AUTOPATCHER_CREATION_LIST, + /// Autopatcher plugin - A list of files to delete + ID_AUTOPATCHER_DELETION_LIST, + /// Autopatcher plugin - A list of files to get patches for + ID_AUTOPATCHER_GET_PATCH, + /// Autopatcher plugin - A list of patches for a list of files + ID_AUTOPATCHER_PATCH_LIST, + /// Autopatcher plugin - Returned to the user: An error from the database repository for the autopatcher. + ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR, + /// Autopatcher plugin - Finished getting all files from the autopatcher + ID_AUTOPATCHER_FINISHED_INTERNAL, + ID_AUTOPATCHER_FINISHED, + /// Autopatcher plugin - Returned to the user: You must restart the application to finish patching. + ID_AUTOPATCHER_RESTART_APPLICATION, + + /// NATPunchthrough plugin: internal + ID_NAT_PUNCHTHROUGH_REQUEST, + /// NATPunchthrough plugin: internal + ID_NAT_GROUP_PUNCHTHROUGH_REQUEST, + /// NATPunchthrough plugin: internal + ID_NAT_GROUP_PUNCHTHROUGH_REPLY, + /// NATPunchthrough plugin: internal + ID_NAT_CONNECT_AT_TIME, + /// NATPunchthrough plugin: internal + ID_NAT_GET_MOST_RECENT_PORT, + /// NATPunchthrough plugin: internal + ID_NAT_CLIENT_READY, + /// NATPunchthrough plugin: internal + ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION, + + /// NATPunchthrough plugin: Destination system is not connected to the server. Bytes starting at offset 1 contains the + /// RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). + ID_NAT_TARGET_NOT_CONNECTED, + /// NATPunchthrough plugin: Destination system is not responding to ID_NAT_GET_MOST_RECENT_PORT. Possibly the plugin is not installed. + /// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). + ID_NAT_TARGET_UNRESPONSIVE, + /// NATPunchthrough plugin: The server lost the connection to the destination system while setting up punchthrough. + /// Possibly the plugin is not installed. Bytes starting at offset 1 contains the RakNetGUID destination + /// field of NatPunchthroughClient::OpenNAT(). + ID_NAT_CONNECTION_TO_TARGET_LOST, + /// NATPunchthrough plugin: This punchthrough is already in progress. Possibly the plugin is not installed. + /// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). + ID_NAT_ALREADY_IN_PROGRESS, + /// NATPunchthrough plugin: This message is generated on the local system, and does not come from the network. + /// packet::guid contains the destination field of NatPunchthroughClient::OpenNAT(). Byte 1 contains 1 if you are the sender, 0 if not + ID_NAT_PUNCHTHROUGH_FAILED, + /// NATPunchthrough plugin: Punchthrough succeeded. See packet::systemAddress and packet::guid. Byte 1 contains 1 if you are the sender, + /// 0 if not. You can now use RakPeer::Connect() or other calls to communicate with this system. + ID_NAT_PUNCHTHROUGH_SUCCEEDED, + /// NATPunchthrough plugin: OpenNATGroup failed. + /// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT() + /// Data format starts at byte 1:
+ /// (char) passedSystemsCount,
+ /// (RakNetGuid, SystemAddress) (for passedSystemsCount),
+ /// (char) ignoredSystemsCount (caused by ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_TARGET_UNRESPONSIVE),
+ /// RakNetGuid (for ignoredSystemsCount),
+ /// (char) failedSystemsCount,
+ /// RakNetGuid (for failedSystemsCount)
+ ID_NAT_GROUP_PUNCH_FAILED, + /// NATPunchthrough plugin: OpenNATGroup succeeded. + /// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT() + /// See ID_NAT_GROUP_PUNCH_FAILED for data format + ID_NAT_GROUP_PUNCH_SUCCEEDED, + + /// ReadyEvent plugin - Set the ready state for a particular system + /// First 4 bytes after the message contains the id + ID_READY_EVENT_SET, + /// ReadyEvent plugin - Unset the ready state for a particular system + /// First 4 bytes after the message contains the id + ID_READY_EVENT_UNSET, + /// All systems are in state ID_READY_EVENT_SET + /// First 4 bytes after the message contains the id + ID_READY_EVENT_ALL_SET, + /// \internal, do not process in your game + /// ReadyEvent plugin - Request of ready event state - used for pulling data when newly connecting + ID_READY_EVENT_QUERY, + + /// Lobby packets. Second byte indicates type. + ID_LOBBY_GENERAL, + + // RPC3, RPC4Plugin error + ID_RPC_REMOTE_ERROR, + /// Plugin based replacement for RPC system + ID_RPC_PLUGIN, + + /// FileListTransfer transferring large files in chunks that are read only when needed, to save memory + ID_FILE_LIST_REFERENCE_PUSH, + /// Force the ready event to all set + ID_READY_EVENT_FORCE_ALL_SET, + + /// Rooms function + ID_ROOMS_EXECUTE_FUNC, + ID_ROOMS_LOGON_STATUS, + ID_ROOMS_HANDLE_CHANGE, + + /// Lobby2 message + ID_LOBBY2_SEND_MESSAGE, + ID_LOBBY2_SERVER_ERROR, + + /// Informs user of a new host GUID. Packet::Guid contains this new host RakNetGuid. The old host can be read out using BitStream->Read(RakNetGuid) starting on byte 1 + /// This is not returned until connected to a remote system + /// If the oldHost is UNASSIGNED_RAKNET_GUID, then this is the first time the host has been determined + ID_FCM2_NEW_HOST, + /// \internal For FullyConnectedMesh2 plugin + ID_FCM2_REQUEST_FCMGUID, + /// \internal For FullyConnectedMesh2 plugin + ID_FCM2_RESPOND_CONNECTION_COUNT, + /// \internal For FullyConnectedMesh2 plugin + ID_FCM2_INFORM_FCMGUID, + /// \internal For FullyConnectedMesh2 plugin + ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT, + + /// UDP proxy messages. Second byte indicates type. + ID_UDP_PROXY_GENERAL, + + /// SQLite3Plugin - execute + ID_SQLite3_EXEC, + /// SQLite3Plugin - Remote database is unknown + ID_SQLite3_UNKNOWN_DB, + /// Events happening with SQLiteClientLoggerPlugin + ID_SQLLITE_LOGGER, + + /// Sent to NatTypeDetectionServer + ID_NAT_TYPE_DETECTION_REQUEST, + /// Sent to NatTypeDetectionClient. Byte 1 contains the type of NAT detected. + ID_NAT_TYPE_DETECTION_RESULT, + + /// Used by the router2 plugin + ID_ROUTER_2_INTERNAL, + /// No path is available or can be established to the remote system + /// Packet::guid contains the endpoint guid that we were trying to reach + ID_ROUTER_2_FORWARDING_NO_PATH, + /// \brief You can now call connect, ping, or other operations to the destination system. + /// + /// Connect as follows: + /// + /// RakNet::BitStream bs(packet->data, packet->length, false); + /// bs.IgnoreBytes(sizeof(MessageID)); + /// RakNetGUID endpointGuid; + /// bs.Read(endpointGuid); + /// unsigned short sourceToDestPort; + /// bs.Read(sourceToDestPort); + /// char ipAddressString[32]; + /// packet->systemAddress.ToString(false, ipAddressString); + /// rakPeerInterface->Connect(ipAddressString, sourceToDestPort, 0,0); + ID_ROUTER_2_FORWARDING_ESTABLISHED, + /// The IP address for a forwarded connection has changed + /// Read endpointGuid and port as per ID_ROUTER_2_FORWARDING_ESTABLISHED + ID_ROUTER_2_REROUTED, + + /// \internal Used by the team balancer plugin + ID_TEAM_BALANCER_INTERNAL, + /// Cannot switch to the desired team because it is full. However, if someone on that team leaves, you will + /// get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member. + ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING, + /// Cannot switch to the desired team because all teams are locked. However, if someone on that team leaves, + /// you will get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join. + ID_TEAM_BALANCER_TEAMS_LOCKED, + /// Team balancer plugin informing you of your team. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member. + ID_TEAM_BALANCER_TEAM_ASSIGNED, + + /// Gamebryo Lightspeed integration + ID_LIGHTSPEED_INTEGRATION, + + /// XBOX integration + ID_XBOX_LOBBY, + + /// The password we used to challenge the other system passed, meaning the other system has called TwoWayAuthentication::AddPassword() with the same password we passed to TwoWayAuthentication::Challenge() + /// You can read the identifier used to challenge as follows: + /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(RakNet::MessageID)); RakNet::RakString password; bs.Read(password); + ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, + ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS, + /// A remote system sent us a challenge using TwoWayAuthentication::Challenge(), and the challenge failed. + /// If the other system must pass the challenge to stay connected, you should call RakPeer::CloseConnection() to terminate the connection to the other system. + ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE, + /// The other system did not add the password we used to TwoWayAuthentication::AddPassword() + /// You can read the identifier used to challenge as follows: + /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password); + ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE, + /// The other system did not respond within a timeout threshhold. Either the other system is not running the plugin or the other system was blocking on some operation for a long time. + /// You can read the identifier used to challenge as follows: + /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password); + ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, + /// \internal + ID_TWO_WAY_AUTHENTICATION_NEGOTIATION, + + /// CloudClient / CloudServer + ID_CLOUD_POST_REQUEST, + ID_CLOUD_RELEASE_REQUEST, + ID_CLOUD_GET_REQUEST, + ID_CLOUD_GET_RESPONSE, + ID_CLOUD_UNSUBSCRIBE_REQUEST, + ID_CLOUD_SERVER_TO_SERVER_COMMAND, + ID_CLOUD_SUBSCRIPTION_NOTIFICATION, + + // So I can add more without changing user enumerations + ID_RESERVED_1, + ID_RESERVED_2, + ID_RESERVED_3, + ID_RESERVED_4, + ID_RESERVED_5, + ID_RESERVED_6, + ID_RESERVED_7, + ID_RESERVED_8, + ID_RESERVED_9, + + // For the user to use. Start your first enumeration at this value. + ID_USER_PACKET_ENUM, + //------------------------------------------------------------------------------------------------------------- + +}; + +#endif // RAKNET_USE_CUSTOM_PACKET_IDS + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughClient.cpp b/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughClient.cpp index da1c948..28a9de4 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughClient.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughClient.cpp @@ -1,970 +1,970 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatPunchthroughClient==1 - -#include "NatPunchthroughClient.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "GetTime.h" -#include "PacketLogger.h" -#include "Itoa.h" - -using namespace RakNet; - -void NatPunchthroughDebugInterface_Printf::OnClientMessage(const char *msg) -{ - printf("%s\n", msg); -} -#if _RAKNET_SUPPORT_PacketLogger==1 -void NatPunchthroughDebugInterface_PacketLogger::OnClientMessage(const char *msg) -{ - if (pl) - { - pl->WriteMiscellaneous("Nat", msg); - } -} -#endif - -STATIC_FACTORY_DEFINITIONS(NatPunchthroughClient,NatPunchthroughClient); - -NatPunchthroughClient::NatPunchthroughClient() -{ - natPunchthroughDebugInterface=0; - mostRecentNewExternalPort=0; - sp.nextActionTime=0; -} -NatPunchthroughClient::~NatPunchthroughClient() -{ - rakPeerInterface=0; - Clear(); -} -bool NatPunchthroughClient::OpenNAT(RakNetGUID destination, const SystemAddress &facilitator) -{ - ConnectionState cs = rakPeerInterface->GetConnectionState(facilitator); - if (cs!=IS_CONNECTED) - return false; - - SendPunchthrough(destination, facilitator); - return true; -} -bool NatPunchthroughClient::OpenNATGroup(DataStructures::List destinationSystems, const SystemAddress &facilitator) -{ - ConnectionState cs = rakPeerInterface->GetConnectionState(facilitator); - if (cs!=IS_CONNECTED) - return false; - - unsigned long i; - for (i=0; i < destinationSystems.Size(); i++) - { - SendPunchthrough(destinationSystems[i], facilitator); - } - - GroupPunchRequest *gpr = RakNet::OP_NEW(_FILE_AND_LINE_); - gpr->facilitator=facilitator; - gpr->pendingList=destinationSystems; - groupPunchRequests.Push(gpr, _FILE_AND_LINE_); - - return true; -} -void NatPunchthroughClient::SetDebugInterface(NatPunchthroughDebugInterface *i) -{ - natPunchthroughDebugInterface=i; -} -void NatPunchthroughClient::Update(void) -{ - RakNet::Time time = RakNet::GetTime(); - if (sp.nextActionTime && sp.nextActionTime < time) - { - RakNet::Time delta = time - sp.nextActionTime; - if (sp.testMode==SendPing::TESTING_INTERNAL_IPS) - { - SendOutOfBand(sp.internalIds[sp.attemptCount],ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_INTERNAL) - { - ++sp.attemptCount; - sp.retryCount=0; - } - - if (sp.attemptCount>=pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK) - { - sp.testMode=SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE; - if (pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS>0) - { - sp.nextActionTime=time+pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS-delta; - } - else - { - // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned - sp.testMode=SendPing::SEND_WITH_TTL; - // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; - sp.attemptCount=0; - } - } - else - { - sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL-delta; - } - } - else if (sp.testMode==SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE) - { - // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned - sp.testMode=SendPing::SEND_WITH_TTL; - // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; - sp.attemptCount=0; - } - else if (sp.testMode==SendPing::SEND_WITH_TTL) - { - // See http://www.jenkinssoftware.com/forum/index.php?topic=4021.0 - // For Linux 2.6.32 soft-router (ip-tables) - /* - If I understand correctly, getting a datagram on a particular address that was not previously used causes that the port for that address to not be used for when a reply would have otherwise been sent back from that address. - - PHASE 1: - 1. System 1 and 2 send to each other. - 2. Due to latency, system 1 sends first using the server port. - 3. System 2 gets the datagram and no longer uses the server port. Instead, it replies using port 1024. - 4. System 1 gets the reply. The source port is wrong, so it is rejected. - - To put it another way, if a router gets a datagram on a port that was not previously used, it will not reply on that port. However, if it doesn't reply on that port, the message will not be accepted by the remote system. - */ - - // Send to unused port. We do not want the message to arrive, just to open our router's table - SystemAddress sa=sp.targetAddress; - int ttlSendIndex; - for (ttlSendIndex=0; ttlSendIndex <= pc.MAX_PREDICTIVE_PORT_RANGE; ttlSendIndex++) - { - sa.SetPort((unsigned short) (sp.targetAddress.GetPort()+ttlSendIndex)); - SendTTL(sa); - } - - // Only do this stage once - // Wait 250 milliseconds for next stage. The delay is so that even with timing errors both systems send out the - // datagram with TTL before either sends a real one - sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; - sp.nextActionTime=time-delta+250; - } - else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT) - { - SystemAddress sa=sp.targetAddress; - sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); - SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - IncrementExternalAttemptCount(time, delta); - - if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) - { - sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; - - // Skip TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT, etc. - /* - sp.testMode=SendPing::TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT; - sp.attemptCount=0; - */ - } - } - else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT) - { - SystemAddress sa=sp.targetAddress; - if ( sp.targetGuid < rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) ) - sa.SetPort((unsigned short) (1024+sp.attemptCount)); - else - sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); - SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - IncrementExternalAttemptCount(time, delta); - - if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) - { - // From 1024 disabled, never helps as I've seen, but slows down the process by half - sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024; - sp.attemptCount=0; - } - - } - else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024) - { - SystemAddress sa=sp.targetAddress; - if ( sp.targetGuid > rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) ) - sa.SetPort((unsigned short) (1024+sp.attemptCount)); - else - sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); - SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - IncrementExternalAttemptCount(time, delta); - - if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) - { - // From 1024 disabled, never helps as I've seen, but slows down the process by half - sp.testMode=SendPing::TESTING_EXTERNAL_IPS_1024_TO_1024; - sp.attemptCount=0; - } - } - else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_1024_TO_1024) - { - SystemAddress sa=sp.targetAddress; - sa.SetPort((unsigned short) (1024+sp.attemptCount)); - SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - IncrementExternalAttemptCount(time, delta); - - if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely bidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString)); - } - - sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; - } - } - else if (sp.testMode==SendPing::WAITING_AFTER_ALL_ATTEMPTS) - { - // Failed. Tell the user - OnPunchthroughFailure(); - UpdateGroupPunchOnNatResult(sp.facilitator, sp.targetGuid, sp.targetAddress, 1); - } - - if (sp.testMode==SendPing::PUNCHING_FIXED_PORT) - { - SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); - if (++sp.retryCount>=sp.punchingFixedPortAttempts) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely unidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString)); - } - - sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; - } - else - { - if ((sp.retryCount%pc.UDP_SENDS_PER_PORT_EXTERNAL)==0) - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta; - else - sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta; - } - } - } - - // Remove elapsed groupRequestsInProgress - unsigned int i; - i=0; - while (i < groupRequestsInProgress.Size()) - { - if (time > groupRequestsInProgress[i].time) - groupRequestsInProgress.RemoveAtIndexFast(i); - else - i++; - } -} -void NatPunchthroughClient::PushFailure(void) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); - p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED; - p->systemAddress=sp.targetAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=sp.targetGuid; - if (sp.weAreSender) - p->data[1]=1; - else - p->data[1]=0; - p->wasGeneratedLocally=true; - rakPeerInterface->PushBackPacket(p, true); -} -void NatPunchthroughClient::OnPunchthroughFailure(void) -{ - if (pc.retryOnFailure==false) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough once. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString)); - } - - PushFailure(); - OnReadyForNextPunchthrough(); - return; - } - - unsigned int i; - for (i=0; i < failedAttemptList.Size(); i++) - { - if (failedAttemptList[i].guid==sp.targetGuid) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough twice. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString)); - } - - // Failed a second time, so return failed to user - PushFailure(); - - OnReadyForNextPunchthrough(); - - failedAttemptList.RemoveAtIndexFast(i); - return; - } - } - - if (rakPeerInterface->GetConnectionState(sp.facilitator)!=IS_CONNECTED) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Not connected to facilitator, so cannot retry punchthrough after first failure. Returning failure onj guid %s, system address %s to user.", guidString, ipAddressString)); - } - - // Failed, and can't try again because no facilitator - PushFailure(); - return; - } - - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("First punchthrough failure on guid %s, system address %s. Reattempting.", guidString, ipAddressString)); - } - - // Failed the first time. Add to the failure queue and try again - AddrAndGuid aag; - aag.addr=sp.targetAddress; - aag.guid=sp.targetGuid; - failedAttemptList.Push(aag, _FILE_AND_LINE_); - - // Tell the server we are ready - OnReadyForNextPunchthrough(); - - // If we are the sender, try again, immediately if possible, else added to the queue on the faciltiator - if (sp.weAreSender) - SendPunchthrough(sp.targetGuid, sp.facilitator); -} -PluginReceiveResult NatPunchthroughClient::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_NAT_GET_MOST_RECENT_PORT: - { - OnGetMostRecentPort(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_NAT_PUNCHTHROUGH_FAILED: - case ID_NAT_PUNCHTHROUGH_SUCCEEDED: - if (packet->wasGeneratedLocally==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - break; - case ID_OUT_OF_BAND_INTERNAL: - if (packet->length>=2 && - (packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL || packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL) && - sp.nextActionTime!=0) - { - RakNet::BitStream bs(packet->data,packet->length,false); - bs.IgnoreBytes(2); - uint16_t sessionId; - bs.Read(sessionId); -// RakAssert(sessionId<100); - if (sessionId!=sp.sessionId) - break; - - char ipAddressString[32]; - packet->systemAddress.ToString(true,ipAddressString); - // sp.targetGuid==packet->guid is because the internal IP addresses reported may include loopbacks not reported by RakPeer::IsLocalIP() - if (packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL && sp.targetGuid==packet->guid) - { - if (natPunchthroughDebugInterface) - { - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Received ID_NAT_ESTABLISH_UNIDIRECTIONAL from guid %s, system address %s.", guidString, ipAddressString)); - } - if (sp.testMode!=SendPing::PUNCHING_FIXED_PORT) - { - sp.testMode=SendPing::PUNCHING_FIXED_PORT; - sp.retryCount+=sp.attemptCount*pc.UDP_SENDS_PER_PORT_EXTERNAL; - sp.targetAddress=packet->systemAddress; - // Keeps trying until the other side gives up too, in case it is unidirectional - sp.punchingFixedPortAttempts=pc.UDP_SENDS_PER_PORT_EXTERNAL*(pc.MAX_PREDICTIVE_PORT_RANGE+1); - } - - SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); - } - else if (packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL && - sp.targetGuid==packet->guid) - { - // They send back our port - bs.Read(mostRecentNewExternalPort); - - SendOutOfBand(packet->systemAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); - - // Tell the user about the success - sp.targetAddress=packet->systemAddress; - PushSuccess(); - UpdateGroupPunchOnNatResult(sp.facilitator, sp.targetGuid, sp.targetAddress, 1); - OnReadyForNextPunchthrough(); - bool removedFromFailureQueue=RemoveFromFailureQueue(); - - if (natPunchthroughDebugInterface) - { - char guidString[128]; - sp.targetGuid.ToString(guidString); - if (removedFromFailureQueue) - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 2nd attempt.", guidString, ipAddressString)); - else - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 1st attempt.", guidString, ipAddressString)); - } - } - - // mostRecentNewExternalPort=packet->systemAddress.GetPort(); - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_NAT_ALREADY_IN_PROGRESS: - { - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID targetGuid; - incomingBs.Read(targetGuid); - // Don't update group, just use later message - // UpdateGroupPunchOnNatResult(packet->systemAddress, targetGuid, UNASSIGNED_SYSTEM_ADDRESS, 2); - if (natPunchthroughDebugInterface) - { - char guidString[128]; - targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to ID_NAT_ALREADY_IN_PROGRESS. Returning failure.", guidString)); - } - - } - break; - case ID_NAT_TARGET_NOT_CONNECTED: - case ID_NAT_CONNECTION_TO_TARGET_LOST: - case ID_NAT_TARGET_UNRESPONSIVE: - { - const char *reason; - if (packet->data[0]==ID_NAT_TARGET_NOT_CONNECTED) - reason=(char *)"ID_NAT_TARGET_NOT_CONNECTED"; - else if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST) - reason=(char *)"ID_NAT_CONNECTION_TO_TARGET_LOST"; - else - reason=(char *)"ID_NAT_TARGET_UNRESPONSIVE"; - - - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - - RakNetGUID targetGuid; - incomingBs.Read(targetGuid); - UpdateGroupPunchOnNatResult(packet->systemAddress, targetGuid, UNASSIGNED_SYSTEM_ADDRESS, 2); - - if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST || - packet->data[0]==ID_NAT_TARGET_UNRESPONSIVE) - { - uint16_t sessionId; - incomingBs.Read(sessionId); - if (sessionId!=sp.sessionId) - break; - } - - unsigned int i; - for (i=0; i < failedAttemptList.Size(); i++) - { - if (failedAttemptList[i].guid==targetGuid) - { - if (natPunchthroughDebugInterface) - { - char guidString[128]; - targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to %s.", guidString, reason)); - - } - - // If the retry target is not connected, or loses connection, or is not responsive, then previous failures cannot be retried. - - // Don't need to return failed, the other messages indicate failure anyway - /* - Packet *p = AllocatePacketUnified(sizeof(MessageID)); - p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED; - p->systemAddress=failedAttemptList[i].addr; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=failedAttemptList[i].guid; - rakPeerInterface->PushBackPacket(p, false); - */ - - failedAttemptList.RemoveAtIndexFast(i); - break; - } - } - - if (natPunchthroughDebugInterface) - { - char guidString[128]; - targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough attempt to guid %s failed due to %s.", guidString, reason)); - } - - // Stop trying punchthrough - sp.nextActionTime=0; - - /* - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID failedSystem; - bs.Read(failedSystem); - bool deletedFirst=false; - unsigned int i=0; - while (i < pendingOpenNAT.Size()) - { - if (pendingOpenNAT[i].destination==failedSystem) - { - if (i==0) - deletedFirst=true; - pendingOpenNAT.RemoveAtIndex(i); - } - else - i++; - } - // Failed while in progress. Go to next in attempt queue - if (deletedFirst && pendingOpenNAT.Size()) - { - SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator); - sp.nextActionTime=0; - } - */ - } - break; - case ID_TIMESTAMP: - if (packet->data[sizeof(MessageID)+sizeof(RakNet::Time)]==ID_NAT_CONNECT_AT_TIME) - { - OnConnectAtTime(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - break; - } - return RR_CONTINUE_PROCESSING; -} -/* -void NatPunchthroughClient::ProcessNextPunchthroughQueue(void) -{ - // Go to the next attempt - if (pendingOpenNAT.Size()) - pendingOpenNAT.RemoveAtIndex(0); - - // Do next punchthrough attempt - if (pendingOpenNAT.Size()) - SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator); - - sp.nextActionTime=0; -} -*/ -void NatPunchthroughClient::OnConnectAtTime(Packet *packet) -{ -// RakAssert(sp.nextActionTime==0); - - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.Read(sp.nextActionTime); - bs.IgnoreBytes(sizeof(MessageID)); - bs.Read(sp.sessionId); - bs.Read(sp.targetAddress); - int j; -// int k; -// k=0; - for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) - bs.Read(sp.internalIds[j]); - - // Prevents local testing - /* - for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) - { - SystemAddress id; - bs.Read(id); - char str[32]; - id.ToString(false,str); - if (rakPeerInterface->IsLocalIP(str)==false) - sp.internalIds[k++]=id; - } - */ - sp.attemptCount=0; - sp.retryCount=0; - if (pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK>0) - { - sp.testMode=SendPing::TESTING_INTERNAL_IPS; - } - else - { - // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned - sp.testMode=SendPing::SEND_WITH_TTL; - // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; - } - bs.Read(sp.targetGuid); - bs.Read(sp.weAreSender); -} -void NatPunchthroughClient::SendTTL(const SystemAddress &sa) -{ - if (sa==UNASSIGNED_SYSTEM_ADDRESS) - return; - if (sa.GetPort()==0) - return; - - char ipAddressString[32]; - sa.ToString(false, ipAddressString); - // TTL of 1 doesn't get past the router, 2 might hit the other system on a LAN - rakPeerInterface->SendTTL(ipAddressString,sa.GetPort(), 2); -} -void NatPunchthroughClient::SendOutOfBand(SystemAddress sa, MessageID oobId) -{ - if (sa==UNASSIGNED_SYSTEM_ADDRESS) - return; - if (sa.GetPort()==0) - return; - - RakNet::BitStream oob; - oob.Write(oobId); - oob.Write(sp.sessionId); -// RakAssert(sp.sessionId<100); - if (oobId==ID_NAT_ESTABLISH_BIDIRECTIONAL) - oob.Write(sa.GetPort()); - char ipAddressString[32]; - sa.ToString(false, ipAddressString); - rakPeerInterface->SendOutOfBand((const char*) ipAddressString,sa.GetPort(),(const char*) oob.GetData(),oob.GetNumberOfBytesUsed()); - - if (natPunchthroughDebugInterface) - { - sa.ToString(true,ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - - if (oobId==ID_NAT_ESTABLISH_UNIDIRECTIONAL) - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_UNIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString)); - else - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_BIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString)); - } -} -void NatPunchthroughClient::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) rakNetGUID; - (void) isIncoming; - - // Try to track new port mappings on the router. Not reliable, but better than nothing. - SystemAddress ourExternalId = rakPeerInterface->GetExternalID(systemAddress); - if (ourExternalId!=UNASSIGNED_SYSTEM_ADDRESS) - mostRecentNewExternalPort=ourExternalId.GetPort(); - - unsigned int i; - i=0; - while (i < groupRequestsInProgress.Size()) - { - if (groupRequestsInProgress[i].guid==rakNetGUID) - { - groupRequestsInProgress.RemoveAtIndexFast(i); - } - else - { - i++; - } - } -} - -void NatPunchthroughClient::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) systemAddress; - (void) rakNetGUID; - (void) lostConnectionReason; - - if (sp.facilitator==systemAddress) - { - // If we lose the connection to the facilitator, all previous failures not currently in progress are returned as such - unsigned int i=0; - while (i < failedAttemptList.Size()) - { - if (sp.nextActionTime!=0 && sp.targetGuid==failedAttemptList[i].guid) - { - i++; - continue; - } - - PushFailure(); - - failedAttemptList.RemoveAtIndexFast(i); - } - } - - unsigned int i; - i=0; - while (i < groupPunchRequests.Size()) - { - if (groupPunchRequests[i]->facilitator==systemAddress) - { - RakNet::OP_DELETE(groupPunchRequests[i],_FILE_AND_LINE_); - groupPunchRequests.RemoveAtIndexFast(i); - } - else - { - i++; - } - } - -} -void NatPunchthroughClient::GetUPNPPortMappings(char *externalPort, char *internalPort, const SystemAddress &natPunchthroughServerAddress) -{ - DataStructures::List > sockets; - rakPeerInterface->GetSockets(sockets); - Itoa(sockets[0]->boundAddress.GetPort(),internalPort,10); - if (mostRecentNewExternalPort==0) - mostRecentNewExternalPort=rakPeerInterface->GetExternalID(natPunchthroughServerAddress).GetPort(); - Itoa(mostRecentNewExternalPort,externalPort,10); -} -void NatPunchthroughClient::OnFailureNotification(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data,packet->length,false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID senderGuid; - incomingBs.Read(senderGuid); - - unsigned int i; - i=0; - while (i < groupRequestsInProgress.Size()) - { - if (groupRequestsInProgress[i].guid==senderGuid) - { - groupRequestsInProgress.RemoveAtIndexFast(i); - break; - } - else - { - i++; - } - } -} -void NatPunchthroughClient::OnGetMostRecentPort(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data,packet->length,false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - uint16_t sessionId; - incomingBs.Read(sessionId); - - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT); - outgoingBs.Write(sessionId); - if (mostRecentNewExternalPort==0) - mostRecentNewExternalPort=rakPeerInterface->GetExternalID(packet->systemAddress).GetPort(); - RakAssert(mostRecentNewExternalPort!=0); - outgoingBs.Write(mostRecentNewExternalPort); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - sp.facilitator=packet->systemAddress; -} -/* -unsigned int NatPunchthroughClient::GetPendingOpenNATIndex(RakNetGUID destination, const SystemAddress &facilitator) -{ - unsigned int i; - for (i=0; i < pendingOpenNAT.Size(); i++) - { - if (pendingOpenNAT[i].destination==destination && pendingOpenNAT[i].facilitator==facilitator) - return i; - } - return (unsigned int) -1; -} -*/ -void NatPunchthroughClient::SendPunchthrough(RakNetGUID destination, const SystemAddress &facilitator) -{ - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_PUNCHTHROUGH_REQUEST); - outgoingBs.Write(destination); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,facilitator,false); - -// RakAssert(rakPeerInterface->GetSystemAddressFromGuid(destination)==UNASSIGNED_SYSTEM_ADDRESS); - - if (natPunchthroughDebugInterface) - { - char guidString[128]; - destination.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Starting ID_NAT_PUNCHTHROUGH_REQUEST to guid %s.", guidString)); - } -} -void NatPunchthroughClient::OnAttach(void) -{ - Clear(); -} -void NatPunchthroughClient::OnDetach(void) -{ - Clear(); -} -void NatPunchthroughClient::OnRakPeerShutdown(void) -{ - Clear(); -} -void NatPunchthroughClient::Clear(void) -{ - OnReadyForNextPunchthrough(); - - failedAttemptList.Clear(false, _FILE_AND_LINE_); - groupRequestsInProgress.Clear(false, _FILE_AND_LINE_); - unsigned int i; - for (i=0; i < groupPunchRequests.Size(); i++) - { - RakNet::OP_DELETE(groupPunchRequests[i],_FILE_AND_LINE_); - } - groupPunchRequests.Clear(true, _FILE_AND_LINE_); -} -PunchthroughConfiguration* NatPunchthroughClient::GetPunchthroughConfiguration(void) -{ - return &pc; -} -void NatPunchthroughClient::OnReadyForNextPunchthrough(void) -{ - if (rakPeerInterface==0) - return; - - sp.nextActionTime=0; - - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_CLIENT_READY); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sp.facilitator,false); -} - -void NatPunchthroughClient::PushSuccess(void) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); - p->data[0]=ID_NAT_PUNCHTHROUGH_SUCCEEDED; - p->systemAddress=sp.targetAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=sp.targetGuid; - if (sp.weAreSender) - p->data[1]=1; - else - p->data[1]=0; - p->wasGeneratedLocally=true; - rakPeerInterface->PushBackPacket(p, true); -} -bool NatPunchthroughClient::RemoveFromFailureQueue(void) -{ - unsigned int i; - for (i=0; i < failedAttemptList.Size(); i++) - { - if (failedAttemptList[i].guid==sp.targetGuid) - { - // Remove from failure queue - failedAttemptList.RemoveAtIndexFast(i); - return true; - } - } - return false; -} - -void NatPunchthroughClient::IncrementExternalAttemptCount(RakNet::Time time, RakNet::Time delta) -{ - if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_EXTERNAL) - { - ++sp.attemptCount; - sp.retryCount=0; - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta; - } - else - { - sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta; - } -} -// 0=failed, 1=success, 2=ignore -void NatPunchthroughClient::UpdateGroupPunchOnNatResult(SystemAddress facilitator, RakNetGUID targetSystem, SystemAddress targetSystemAddress, int result) -{ - GroupPunchRequest *gpr; - unsigned long i,j,k; - i=0; - while (i < groupPunchRequests.Size()) - { - gpr = groupPunchRequests[i]; - if (gpr->facilitator==facilitator) - { - j=0; - while (j < gpr->pendingList.Size()) - { - if (gpr->pendingList[j]==targetSystem) - { - if (result==0) - { - gpr->failedList.Push(targetSystem, _FILE_AND_LINE_); - } - else if (result==1) - { - gpr->passedListGuid.Push(targetSystem, _FILE_AND_LINE_); - gpr->passedListAddress.Push(targetSystemAddress, _FILE_AND_LINE_); - } - else - { - gpr->ignoredList.Push(targetSystem, _FILE_AND_LINE_); - } - gpr->pendingList.RemoveAtIndex(j); - } - else - j++; - } - } - if (gpr->pendingList.Size()==0) - { - RakNet::BitStream output; - if (gpr->failedList.Size()==0) - { - output.Write(ID_NAT_GROUP_PUNCH_SUCCEEDED); - } - else - { - output.Write(ID_NAT_GROUP_PUNCH_FAILED); - } - - output.WriteCasted(gpr->passedListGuid.Size()); - for (k=0; k < gpr->passedListGuid.Size(); k++) - { - output.Write(gpr->passedListGuid[k]); - output.Write(gpr->passedListAddress[k]); - } - output.WriteCasted(gpr->ignoredList.Size()); - for (k=0; k < gpr->ignoredList.Size(); k++) - { - output.Write(gpr->ignoredList[k]); - } - output.WriteCasted(gpr->failedList.Size()); - for (k=0; k < gpr->failedList.Size(); k++) - { - output.Write(gpr->failedList[k]); - } - - Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed()); - p->systemAddress=gpr->facilitator; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=rakPeerInterface->GetGuidFromSystemAddress(gpr->facilitator); - p->wasGeneratedLocally=true; - memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed()); - rakPeerInterface->PushBackPacket(p, true); - - groupPunchRequests.RemoveAtIndex(i); - RakNet::OP_DELETE(gpr, _FILE_AND_LINE_); - } - else - i++; - } -} - -#endif // _RAKNET_SUPPORT_* - +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatPunchthroughClient==1 + +#include "NatPunchthroughClient.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "GetTime.h" +#include "PacketLogger.h" +#include "Itoa.h" + +using namespace RakNet; + +void NatPunchthroughDebugInterface_Printf::OnClientMessage(const char *msg) +{ + printf("%s\n", msg); +} +#if _RAKNET_SUPPORT_PacketLogger==1 +void NatPunchthroughDebugInterface_PacketLogger::OnClientMessage(const char *msg) +{ + if (pl) + { + pl->WriteMiscellaneous("Nat", msg); + } +} +#endif + +STATIC_FACTORY_DEFINITIONS(NatPunchthroughClient,NatPunchthroughClient); + +NatPunchthroughClient::NatPunchthroughClient() +{ + natPunchthroughDebugInterface=0; + mostRecentNewExternalPort=0; + sp.nextActionTime=0; +} +NatPunchthroughClient::~NatPunchthroughClient() +{ + rakPeerInterface=0; + Clear(); +} +bool NatPunchthroughClient::OpenNAT(RakNetGUID destination, const SystemAddress &facilitator) +{ + ConnectionState cs = rakPeerInterface->GetConnectionState(facilitator); + if (cs!=IS_CONNECTED) + return false; + + SendPunchthrough(destination, facilitator); + return true; +} +bool NatPunchthroughClient::OpenNATGroup(DataStructures::List destinationSystems, const SystemAddress &facilitator) +{ + ConnectionState cs = rakPeerInterface->GetConnectionState(facilitator); + if (cs!=IS_CONNECTED) + return false; + + unsigned long i; + for (i=0; i < destinationSystems.Size(); i++) + { + SendPunchthrough(destinationSystems[i], facilitator); + } + + GroupPunchRequest *gpr = RakNet::OP_NEW(_FILE_AND_LINE_); + gpr->facilitator=facilitator; + gpr->pendingList=destinationSystems; + groupPunchRequests.Push(gpr, _FILE_AND_LINE_); + + return true; +} +void NatPunchthroughClient::SetDebugInterface(NatPunchthroughDebugInterface *i) +{ + natPunchthroughDebugInterface=i; +} +void NatPunchthroughClient::Update(void) +{ + RakNet::Time time = RakNet::GetTime(); + if (sp.nextActionTime && sp.nextActionTime < time) + { + RakNet::Time delta = time - sp.nextActionTime; + if (sp.testMode==SendPing::TESTING_INTERNAL_IPS) + { + SendOutOfBand(sp.internalIds[sp.attemptCount],ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_INTERNAL) + { + ++sp.attemptCount; + sp.retryCount=0; + } + + if (sp.attemptCount>=pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK) + { + sp.testMode=SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE; + if (pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS>0) + { + sp.nextActionTime=time+pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS-delta; + } + else + { + // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned + sp.testMode=SendPing::SEND_WITH_TTL; + // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; + sp.attemptCount=0; + } + } + else + { + sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL-delta; + } + } + else if (sp.testMode==SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE) + { + // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned + sp.testMode=SendPing::SEND_WITH_TTL; + // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; + sp.attemptCount=0; + } + else if (sp.testMode==SendPing::SEND_WITH_TTL) + { + // See http://www.jenkinssoftware.com/forum/index.php?topic=4021.0 + // For Linux 2.6.32 soft-router (ip-tables) + /* + If I understand correctly, getting a datagram on a particular address that was not previously used causes that the port for that address to not be used for when a reply would have otherwise been sent back from that address. + + PHASE 1: + 1. System 1 and 2 send to each other. + 2. Due to latency, system 1 sends first using the server port. + 3. System 2 gets the datagram and no longer uses the server port. Instead, it replies using port 1024. + 4. System 1 gets the reply. The source port is wrong, so it is rejected. + + To put it another way, if a router gets a datagram on a port that was not previously used, it will not reply on that port. However, if it doesn't reply on that port, the message will not be accepted by the remote system. + */ + + // Send to unused port. We do not want the message to arrive, just to open our router's table + SystemAddress sa=sp.targetAddress; + int ttlSendIndex; + for (ttlSendIndex=0; ttlSendIndex <= pc.MAX_PREDICTIVE_PORT_RANGE; ttlSendIndex++) + { + sa.SetPort((unsigned short) (sp.targetAddress.GetPort()+ttlSendIndex)); + SendTTL(sa); + } + + // Only do this stage once + // Wait 250 milliseconds for next stage. The delay is so that even with timing errors both systems send out the + // datagram with TTL before either sends a real one + sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; + sp.nextActionTime=time-delta+250; + } + else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT) + { + SystemAddress sa=sp.targetAddress; + sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); + SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + IncrementExternalAttemptCount(time, delta); + + if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) + { + sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; + + // Skip TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT, etc. + /* + sp.testMode=SendPing::TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT; + sp.attemptCount=0; + */ + } + } + else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT) + { + SystemAddress sa=sp.targetAddress; + if ( sp.targetGuid < rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) ) + sa.SetPort((unsigned short) (1024+sp.attemptCount)); + else + sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); + SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + IncrementExternalAttemptCount(time, delta); + + if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) + { + // From 1024 disabled, never helps as I've seen, but slows down the process by half + sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024; + sp.attemptCount=0; + } + + } + else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024) + { + SystemAddress sa=sp.targetAddress; + if ( sp.targetGuid > rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) ) + sa.SetPort((unsigned short) (1024+sp.attemptCount)); + else + sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); + SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + IncrementExternalAttemptCount(time, delta); + + if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) + { + // From 1024 disabled, never helps as I've seen, but slows down the process by half + sp.testMode=SendPing::TESTING_EXTERNAL_IPS_1024_TO_1024; + sp.attemptCount=0; + } + } + else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_1024_TO_1024) + { + SystemAddress sa=sp.targetAddress; + sa.SetPort((unsigned short) (1024+sp.attemptCount)); + SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + IncrementExternalAttemptCount(time, delta); + + if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely bidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString)); + } + + sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; + } + } + else if (sp.testMode==SendPing::WAITING_AFTER_ALL_ATTEMPTS) + { + // Failed. Tell the user + OnPunchthroughFailure(); + UpdateGroupPunchOnNatResult(sp.facilitator, sp.targetGuid, sp.targetAddress, 1); + } + + if (sp.testMode==SendPing::PUNCHING_FIXED_PORT) + { + SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); + if (++sp.retryCount>=sp.punchingFixedPortAttempts) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely unidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString)); + } + + sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; + } + else + { + if ((sp.retryCount%pc.UDP_SENDS_PER_PORT_EXTERNAL)==0) + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta; + else + sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta; + } + } + } + + // Remove elapsed groupRequestsInProgress + unsigned int i; + i=0; + while (i < groupRequestsInProgress.Size()) + { + if (time > groupRequestsInProgress[i].time) + groupRequestsInProgress.RemoveAtIndexFast(i); + else + i++; + } +} +void NatPunchthroughClient::PushFailure(void) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); + p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED; + p->systemAddress=sp.targetAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=sp.targetGuid; + if (sp.weAreSender) + p->data[1]=1; + else + p->data[1]=0; + p->wasGeneratedLocally=true; + rakPeerInterface->PushBackPacket(p, true); +} +void NatPunchthroughClient::OnPunchthroughFailure(void) +{ + if (pc.retryOnFailure==false) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough once. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString)); + } + + PushFailure(); + OnReadyForNextPunchthrough(); + return; + } + + unsigned int i; + for (i=0; i < failedAttemptList.Size(); i++) + { + if (failedAttemptList[i].guid==sp.targetGuid) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough twice. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString)); + } + + // Failed a second time, so return failed to user + PushFailure(); + + OnReadyForNextPunchthrough(); + + failedAttemptList.RemoveAtIndexFast(i); + return; + } + } + + if (rakPeerInterface->GetConnectionState(sp.facilitator)!=IS_CONNECTED) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Not connected to facilitator, so cannot retry punchthrough after first failure. Returning failure onj guid %s, system address %s to user.", guidString, ipAddressString)); + } + + // Failed, and can't try again because no facilitator + PushFailure(); + return; + } + + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("First punchthrough failure on guid %s, system address %s. Reattempting.", guidString, ipAddressString)); + } + + // Failed the first time. Add to the failure queue and try again + AddrAndGuid aag; + aag.addr=sp.targetAddress; + aag.guid=sp.targetGuid; + failedAttemptList.Push(aag, _FILE_AND_LINE_); + + // Tell the server we are ready + OnReadyForNextPunchthrough(); + + // If we are the sender, try again, immediately if possible, else added to the queue on the faciltiator + if (sp.weAreSender) + SendPunchthrough(sp.targetGuid, sp.facilitator); +} +PluginReceiveResult NatPunchthroughClient::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_NAT_GET_MOST_RECENT_PORT: + { + OnGetMostRecentPort(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_NAT_PUNCHTHROUGH_FAILED: + case ID_NAT_PUNCHTHROUGH_SUCCEEDED: + if (packet->wasGeneratedLocally==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + break; + case ID_OUT_OF_BAND_INTERNAL: + if (packet->length>=2 && + (packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL || packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL) && + sp.nextActionTime!=0) + { + RakNet::BitStream bs(packet->data,packet->length,false); + bs.IgnoreBytes(2); + uint16_t sessionId; + bs.Read(sessionId); +// RakAssert(sessionId<100); + if (sessionId!=sp.sessionId) + break; + + char ipAddressString[32]; + packet->systemAddress.ToString(true,ipAddressString); + // sp.targetGuid==packet->guid is because the internal IP addresses reported may include loopbacks not reported by RakPeer::IsLocalIP() + if (packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL && sp.targetGuid==packet->guid) + { + if (natPunchthroughDebugInterface) + { + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Received ID_NAT_ESTABLISH_UNIDIRECTIONAL from guid %s, system address %s.", guidString, ipAddressString)); + } + if (sp.testMode!=SendPing::PUNCHING_FIXED_PORT) + { + sp.testMode=SendPing::PUNCHING_FIXED_PORT; + sp.retryCount+=sp.attemptCount*pc.UDP_SENDS_PER_PORT_EXTERNAL; + sp.targetAddress=packet->systemAddress; + // Keeps trying until the other side gives up too, in case it is unidirectional + sp.punchingFixedPortAttempts=pc.UDP_SENDS_PER_PORT_EXTERNAL*(pc.MAX_PREDICTIVE_PORT_RANGE+1); + } + + SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); + } + else if (packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL && + sp.targetGuid==packet->guid) + { + // They send back our port + bs.Read(mostRecentNewExternalPort); + + SendOutOfBand(packet->systemAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); + + // Tell the user about the success + sp.targetAddress=packet->systemAddress; + PushSuccess(); + UpdateGroupPunchOnNatResult(sp.facilitator, sp.targetGuid, sp.targetAddress, 1); + OnReadyForNextPunchthrough(); + bool removedFromFailureQueue=RemoveFromFailureQueue(); + + if (natPunchthroughDebugInterface) + { + char guidString[128]; + sp.targetGuid.ToString(guidString); + if (removedFromFailureQueue) + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 2nd attempt.", guidString, ipAddressString)); + else + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 1st attempt.", guidString, ipAddressString)); + } + } + + // mostRecentNewExternalPort=packet->systemAddress.GetPort(); + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_NAT_ALREADY_IN_PROGRESS: + { + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID targetGuid; + incomingBs.Read(targetGuid); + // Don't update group, just use later message + // UpdateGroupPunchOnNatResult(packet->systemAddress, targetGuid, UNASSIGNED_SYSTEM_ADDRESS, 2); + if (natPunchthroughDebugInterface) + { + char guidString[128]; + targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to ID_NAT_ALREADY_IN_PROGRESS. Returning failure.", guidString)); + } + + } + break; + case ID_NAT_TARGET_NOT_CONNECTED: + case ID_NAT_CONNECTION_TO_TARGET_LOST: + case ID_NAT_TARGET_UNRESPONSIVE: + { + const char *reason; + if (packet->data[0]==ID_NAT_TARGET_NOT_CONNECTED) + reason=(char *)"ID_NAT_TARGET_NOT_CONNECTED"; + else if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST) + reason=(char *)"ID_NAT_CONNECTION_TO_TARGET_LOST"; + else + reason=(char *)"ID_NAT_TARGET_UNRESPONSIVE"; + + + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + + RakNetGUID targetGuid; + incomingBs.Read(targetGuid); + UpdateGroupPunchOnNatResult(packet->systemAddress, targetGuid, UNASSIGNED_SYSTEM_ADDRESS, 2); + + if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST || + packet->data[0]==ID_NAT_TARGET_UNRESPONSIVE) + { + uint16_t sessionId; + incomingBs.Read(sessionId); + if (sessionId!=sp.sessionId) + break; + } + + unsigned int i; + for (i=0; i < failedAttemptList.Size(); i++) + { + if (failedAttemptList[i].guid==targetGuid) + { + if (natPunchthroughDebugInterface) + { + char guidString[128]; + targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to %s.", guidString, reason)); + + } + + // If the retry target is not connected, or loses connection, or is not responsive, then previous failures cannot be retried. + + // Don't need to return failed, the other messages indicate failure anyway + /* + Packet *p = AllocatePacketUnified(sizeof(MessageID)); + p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED; + p->systemAddress=failedAttemptList[i].addr; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=failedAttemptList[i].guid; + rakPeerInterface->PushBackPacket(p, false); + */ + + failedAttemptList.RemoveAtIndexFast(i); + break; + } + } + + if (natPunchthroughDebugInterface) + { + char guidString[128]; + targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough attempt to guid %s failed due to %s.", guidString, reason)); + } + + // Stop trying punchthrough + sp.nextActionTime=0; + + /* + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID failedSystem; + bs.Read(failedSystem); + bool deletedFirst=false; + unsigned int i=0; + while (i < pendingOpenNAT.Size()) + { + if (pendingOpenNAT[i].destination==failedSystem) + { + if (i==0) + deletedFirst=true; + pendingOpenNAT.RemoveAtIndex(i); + } + else + i++; + } + // Failed while in progress. Go to next in attempt queue + if (deletedFirst && pendingOpenNAT.Size()) + { + SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator); + sp.nextActionTime=0; + } + */ + } + break; + case ID_TIMESTAMP: + if (packet->data[sizeof(MessageID)+sizeof(RakNet::Time)]==ID_NAT_CONNECT_AT_TIME) + { + OnConnectAtTime(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + break; + } + return RR_CONTINUE_PROCESSING; +} +/* +void NatPunchthroughClient::ProcessNextPunchthroughQueue(void) +{ + // Go to the next attempt + if (pendingOpenNAT.Size()) + pendingOpenNAT.RemoveAtIndex(0); + + // Do next punchthrough attempt + if (pendingOpenNAT.Size()) + SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator); + + sp.nextActionTime=0; +} +*/ +void NatPunchthroughClient::OnConnectAtTime(Packet *packet) +{ +// RakAssert(sp.nextActionTime==0); + + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.Read(sp.nextActionTime); + bs.IgnoreBytes(sizeof(MessageID)); + bs.Read(sp.sessionId); + bs.Read(sp.targetAddress); + int j; +// int k; +// k=0; + for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) + bs.Read(sp.internalIds[j]); + + // Prevents local testing + /* + for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) + { + SystemAddress id; + bs.Read(id); + char str[32]; + id.ToString(false,str); + if (rakPeerInterface->IsLocalIP(str)==false) + sp.internalIds[k++]=id; + } + */ + sp.attemptCount=0; + sp.retryCount=0; + if (pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK>0) + { + sp.testMode=SendPing::TESTING_INTERNAL_IPS; + } + else + { + // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned + sp.testMode=SendPing::SEND_WITH_TTL; + // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; + } + bs.Read(sp.targetGuid); + bs.Read(sp.weAreSender); +} +void NatPunchthroughClient::SendTTL(const SystemAddress &sa) +{ + if (sa==UNASSIGNED_SYSTEM_ADDRESS) + return; + if (sa.GetPort()==0) + return; + + char ipAddressString[32]; + sa.ToString(false, ipAddressString); + // TTL of 1 doesn't get past the router, 2 might hit the other system on a LAN + rakPeerInterface->SendTTL(ipAddressString,sa.GetPort(), 2); +} +void NatPunchthroughClient::SendOutOfBand(SystemAddress sa, MessageID oobId) +{ + if (sa==UNASSIGNED_SYSTEM_ADDRESS) + return; + if (sa.GetPort()==0) + return; + + RakNet::BitStream oob; + oob.Write(oobId); + oob.Write(sp.sessionId); +// RakAssert(sp.sessionId<100); + if (oobId==ID_NAT_ESTABLISH_BIDIRECTIONAL) + oob.Write(sa.GetPort()); + char ipAddressString[32]; + sa.ToString(false, ipAddressString); + rakPeerInterface->SendOutOfBand((const char*) ipAddressString,sa.GetPort(),(const char*) oob.GetData(),oob.GetNumberOfBytesUsed()); + + if (natPunchthroughDebugInterface) + { + sa.ToString(true,ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + + if (oobId==ID_NAT_ESTABLISH_UNIDIRECTIONAL) + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_UNIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString)); + else + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_BIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString)); + } +} +void NatPunchthroughClient::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) rakNetGUID; + (void) isIncoming; + + // Try to track new port mappings on the router. Not reliable, but better than nothing. + SystemAddress ourExternalId = rakPeerInterface->GetExternalID(systemAddress); + if (ourExternalId!=UNASSIGNED_SYSTEM_ADDRESS) + mostRecentNewExternalPort=ourExternalId.GetPort(); + + unsigned int i; + i=0; + while (i < groupRequestsInProgress.Size()) + { + if (groupRequestsInProgress[i].guid==rakNetGUID) + { + groupRequestsInProgress.RemoveAtIndexFast(i); + } + else + { + i++; + } + } +} + +void NatPunchthroughClient::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) systemAddress; + (void) rakNetGUID; + (void) lostConnectionReason; + + if (sp.facilitator==systemAddress) + { + // If we lose the connection to the facilitator, all previous failures not currently in progress are returned as such + unsigned int i=0; + while (i < failedAttemptList.Size()) + { + if (sp.nextActionTime!=0 && sp.targetGuid==failedAttemptList[i].guid) + { + i++; + continue; + } + + PushFailure(); + + failedAttemptList.RemoveAtIndexFast(i); + } + } + + unsigned int i; + i=0; + while (i < groupPunchRequests.Size()) + { + if (groupPunchRequests[i]->facilitator==systemAddress) + { + RakNet::OP_DELETE(groupPunchRequests[i],_FILE_AND_LINE_); + groupPunchRequests.RemoveAtIndexFast(i); + } + else + { + i++; + } + } + +} +void NatPunchthroughClient::GetUPNPPortMappings(char *externalPort, char *internalPort, const SystemAddress &natPunchthroughServerAddress) +{ + DataStructures::List > sockets; + rakPeerInterface->GetSockets(sockets); + Itoa(sockets[0]->boundAddress.GetPort(),internalPort,10); + if (mostRecentNewExternalPort==0) + mostRecentNewExternalPort=rakPeerInterface->GetExternalID(natPunchthroughServerAddress).GetPort(); + Itoa(mostRecentNewExternalPort,externalPort,10); +} +void NatPunchthroughClient::OnFailureNotification(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data,packet->length,false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID senderGuid; + incomingBs.Read(senderGuid); + + unsigned int i; + i=0; + while (i < groupRequestsInProgress.Size()) + { + if (groupRequestsInProgress[i].guid==senderGuid) + { + groupRequestsInProgress.RemoveAtIndexFast(i); + break; + } + else + { + i++; + } + } +} +void NatPunchthroughClient::OnGetMostRecentPort(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data,packet->length,false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + uint16_t sessionId; + incomingBs.Read(sessionId); + + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT); + outgoingBs.Write(sessionId); + if (mostRecentNewExternalPort==0) + mostRecentNewExternalPort=rakPeerInterface->GetExternalID(packet->systemAddress).GetPort(); + RakAssert(mostRecentNewExternalPort!=0); + outgoingBs.Write(mostRecentNewExternalPort); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + sp.facilitator=packet->systemAddress; +} +/* +unsigned int NatPunchthroughClient::GetPendingOpenNATIndex(RakNetGUID destination, const SystemAddress &facilitator) +{ + unsigned int i; + for (i=0; i < pendingOpenNAT.Size(); i++) + { + if (pendingOpenNAT[i].destination==destination && pendingOpenNAT[i].facilitator==facilitator) + return i; + } + return (unsigned int) -1; +} +*/ +void NatPunchthroughClient::SendPunchthrough(RakNetGUID destination, const SystemAddress &facilitator) +{ + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_PUNCHTHROUGH_REQUEST); + outgoingBs.Write(destination); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,facilitator,false); + +// RakAssert(rakPeerInterface->GetSystemAddressFromGuid(destination)==UNASSIGNED_SYSTEM_ADDRESS); + + if (natPunchthroughDebugInterface) + { + char guidString[128]; + destination.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Starting ID_NAT_PUNCHTHROUGH_REQUEST to guid %s.", guidString)); + } +} +void NatPunchthroughClient::OnAttach(void) +{ + Clear(); +} +void NatPunchthroughClient::OnDetach(void) +{ + Clear(); +} +void NatPunchthroughClient::OnRakPeerShutdown(void) +{ + Clear(); +} +void NatPunchthroughClient::Clear(void) +{ + OnReadyForNextPunchthrough(); + + failedAttemptList.Clear(false, _FILE_AND_LINE_); + groupRequestsInProgress.Clear(false, _FILE_AND_LINE_); + unsigned int i; + for (i=0; i < groupPunchRequests.Size(); i++) + { + RakNet::OP_DELETE(groupPunchRequests[i],_FILE_AND_LINE_); + } + groupPunchRequests.Clear(true, _FILE_AND_LINE_); +} +PunchthroughConfiguration* NatPunchthroughClient::GetPunchthroughConfiguration(void) +{ + return &pc; +} +void NatPunchthroughClient::OnReadyForNextPunchthrough(void) +{ + if (rakPeerInterface==0) + return; + + sp.nextActionTime=0; + + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_CLIENT_READY); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sp.facilitator,false); +} + +void NatPunchthroughClient::PushSuccess(void) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); + p->data[0]=ID_NAT_PUNCHTHROUGH_SUCCEEDED; + p->systemAddress=sp.targetAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=sp.targetGuid; + if (sp.weAreSender) + p->data[1]=1; + else + p->data[1]=0; + p->wasGeneratedLocally=true; + rakPeerInterface->PushBackPacket(p, true); +} +bool NatPunchthroughClient::RemoveFromFailureQueue(void) +{ + unsigned int i; + for (i=0; i < failedAttemptList.Size(); i++) + { + if (failedAttemptList[i].guid==sp.targetGuid) + { + // Remove from failure queue + failedAttemptList.RemoveAtIndexFast(i); + return true; + } + } + return false; +} + +void NatPunchthroughClient::IncrementExternalAttemptCount(RakNet::Time time, RakNet::Time delta) +{ + if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_EXTERNAL) + { + ++sp.attemptCount; + sp.retryCount=0; + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta; + } + else + { + sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta; + } +} +// 0=failed, 1=success, 2=ignore +void NatPunchthroughClient::UpdateGroupPunchOnNatResult(SystemAddress facilitator, RakNetGUID targetSystem, SystemAddress targetSystemAddress, int result) +{ + GroupPunchRequest *gpr; + unsigned long i,j,k; + i=0; + while (i < groupPunchRequests.Size()) + { + gpr = groupPunchRequests[i]; + if (gpr->facilitator==facilitator) + { + j=0; + while (j < gpr->pendingList.Size()) + { + if (gpr->pendingList[j]==targetSystem) + { + if (result==0) + { + gpr->failedList.Push(targetSystem, _FILE_AND_LINE_); + } + else if (result==1) + { + gpr->passedListGuid.Push(targetSystem, _FILE_AND_LINE_); + gpr->passedListAddress.Push(targetSystemAddress, _FILE_AND_LINE_); + } + else + { + gpr->ignoredList.Push(targetSystem, _FILE_AND_LINE_); + } + gpr->pendingList.RemoveAtIndex(j); + } + else + j++; + } + } + if (gpr->pendingList.Size()==0) + { + RakNet::BitStream output; + if (gpr->failedList.Size()==0) + { + output.Write(ID_NAT_GROUP_PUNCH_SUCCEEDED); + } + else + { + output.Write(ID_NAT_GROUP_PUNCH_FAILED); + } + + output.WriteCasted(gpr->passedListGuid.Size()); + for (k=0; k < gpr->passedListGuid.Size(); k++) + { + output.Write(gpr->passedListGuid[k]); + output.Write(gpr->passedListAddress[k]); + } + output.WriteCasted(gpr->ignoredList.Size()); + for (k=0; k < gpr->ignoredList.Size(); k++) + { + output.Write(gpr->ignoredList[k]); + } + output.WriteCasted(gpr->failedList.Size()); + for (k=0; k < gpr->failedList.Size(); k++) + { + output.Write(gpr->failedList[k]); + } + + Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed()); + p->systemAddress=gpr->facilitator; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=rakPeerInterface->GetGuidFromSystemAddress(gpr->facilitator); + p->wasGeneratedLocally=true; + memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed()); + rakPeerInterface->PushBackPacket(p, true); + + groupPunchRequests.RemoveAtIndex(i); + RakNet::OP_DELETE(gpr, _FILE_AND_LINE_); + } + else + i++; + } +} + +#endif // _RAKNET_SUPPORT_* + diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughClient.h b/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughClient.h index 827de71..c12b390 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughClient.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughClient.h @@ -1,265 +1,265 @@ - -/// \file -/// \brief Contains the NAT-punchthrough plugin for the client. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatPunchthroughClient==1 - -#ifndef __NAT_PUNCHTHROUGH_CLIENT_H -#define __NAT_PUNCHTHROUGH_CLIENT_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "SocketIncludes.h" -#include "DS_List.h" -#include "RakString.h" - -// Trendnet TEW-632BRP sometimes starts at port 1024 and increments sequentially. -// Zonnet zsr1134we. Replies go out on the net, but are always absorbed by the remote router?? -// Dlink ebr2310 to Trendnet ok -// Trendnet TEW-652BRP to Trendnet 632BRP OK -// Trendnet TEW-632BRP to Trendnet 632BRP OK -// Buffalo WHR-HP-G54 OK -// Netgear WGR614 ok - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -struct Packet; -#if _RAKNET_SUPPORT_PacketLogger==1 -class PacketLogger; -#endif - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT PunchthroughConfiguration -{ - /// internal: (15 ms * 2 tries + 30 wait) * 5 ports * 8 players = 2.4 seconds - /// external: (50 ms * 8 sends + 100 wait) * 2 port * 8 players = 8 seconds - /// Total: 8 seconds - PunchthroughConfiguration() { - TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL=15; - TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL=50; - UDP_SENDS_PER_PORT_INTERNAL=2; - UDP_SENDS_PER_PORT_EXTERNAL=8; - INTERNAL_IP_WAIT_AFTER_ATTEMPTS=30; - MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK=5; /// set to 0 to not do lan connects - MAX_PREDICTIVE_PORT_RANGE=2; - EXTERNAL_IP_WAIT_BETWEEN_PORTS=100; - EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS=EXTERNAL_IP_WAIT_BETWEEN_PORTS; - retryOnFailure=false; - } - - /// How much time between each UDP send - RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL; - RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL; - - /// How many tries for one port before giving up and going to the next port - int UDP_SENDS_PER_PORT_INTERNAL; - int UDP_SENDS_PER_PORT_EXTERNAL; - - /// After giving up on one internal port, how long to wait before trying the next port - int INTERNAL_IP_WAIT_AFTER_ATTEMPTS; - - /// How many external ports to try past the last known starting port - int MAX_PREDICTIVE_PORT_RANGE; - - /// After giving up on one external port, how long to wait before trying the next port - int EXTERNAL_IP_WAIT_BETWEEN_PORTS; - - /// After trying all external ports, how long to wait before returning ID_NAT_PUNCHTHROUGH_FAILED - int EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS; - - /// Maximum number of internal IP address to try to connect to. - /// Cannot be greater than MAXIMUM_NUMBER_OF_INTERNAL_IDS - /// Should be high enough to try all internal IP addresses on the majority of computers - int MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK; - - /// If the first punchthrough attempt fails, try again - /// This sometimes works because the remote router was looking for an incoming message on a higher numbered port before responding to a lower numbered port from the other system - bool retryOnFailure; -}; - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughDebugInterface -{ - NatPunchthroughDebugInterface() {} - virtual ~NatPunchthroughDebugInterface() {} - virtual void OnClientMessage(const char *msg)=0; -}; - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_Printf : public NatPunchthroughDebugInterface -{ - virtual void OnClientMessage(const char *msg); -}; - -#if _RAKNET_SUPPORT_PacketLogger==1 -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_PacketLogger : public NatPunchthroughDebugInterface -{ - // Set to non-zero to write to the packetlogger! - PacketLogger *pl; - - NatPunchthroughDebugInterface_PacketLogger() {pl=0;} - ~NatPunchthroughDebugInterface_PacketLogger() {} - virtual void OnClientMessage(const char *msg); -}; -#endif - -/// \brief Client code for NATPunchthrough -/// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient
-/// Client will send datagrams to port to estimate next port
-/// Will simultaneously connect with another client once ports are estimated. -/// \sa NatTypeDetectionClient -/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html -/// \ingroup NAT_PUNCHTHROUGH_GROUP -class RAK_DLL_EXPORT NatPunchthroughClient : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(NatPunchthroughClient) - - NatPunchthroughClient(); - ~NatPunchthroughClient(); - - /// Punchthrough a NAT. Doesn't connect, just tries to setup the routing table - /// \param[in] destination The system to punch. Must already be connected to \a facilitator - /// \param[in] facilitator A system we are already connected to running the NatPunchthroughServer plugin - /// \sa OpenNATGroup() - /// You will get ID_NAT_PUNCHTHROUGH_SUCCEEDED on success - /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_TARGET_UNRESPONSIVE, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_PUNCHTHROUGH_FAILED on failures of various types - /// However, if you lose connection to the facilitator, you may not necessarily get above - bool OpenNAT(RakNetGUID destination, const SystemAddress &facilitator); - - /// Same as calling OpenNAT for a list of systems, but reply is delayed until all systems pass. - /// This is useful for peer to peer games where you want to connect to every system in the remote session, not just one particular system - /// \note For cloud computing, all systems in the group must be connected to the same facilitator since we're only specifying one - /// You will get ID_NAT_GROUP_PUNCH_SUCCEEDED on success - /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_GROUP_PUNCH_FAILED on failures of various types - /// However, if you lose connection to the facilitator, you may not necessarily get above - bool OpenNATGroup(DataStructures::List destinationSystems, const SystemAddress &facilitator); - - /// Modify the system configuration if desired - /// Don't modify the variables in the structure while punchthrough is in progress - PunchthroughConfiguration* GetPunchthroughConfiguration(void); - - /// Sets a callback to be called with debug messages - /// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear. - void SetDebugInterface(NatPunchthroughDebugInterface *i); - - /// Get the port mappings you should pass to UPNP (for miniupnpc-1.5, for the function UPNP_AddPortMapping) - void GetUPNPPortMappings(char *externalPort, char *internalPort, const SystemAddress &natPunchthroughServerAddress); - - /// \internal For plugin handling - virtual void Update(void); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - - /// \internal For plugin handling - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - - /// \internal For plugin handling - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - virtual void OnAttach(void); - virtual void OnDetach(void); - virtual void OnRakPeerShutdown(void); - void Clear(void); - -protected: - unsigned short mostRecentNewExternalPort; - void OnNatGroupPunchthroughRequest(Packet *packet); - void OnFailureNotification(Packet *packet); - void OnNatGroupPunchthroughReply(Packet *packet); - void OnGetMostRecentPort(Packet *packet); - void OnConnectAtTime(Packet *packet); - unsigned int GetPendingOpenNATIndex(RakNetGUID destination, const SystemAddress &facilitator); - void SendPunchthrough(RakNetGUID destination, const SystemAddress &facilitator); - void SendTTL(const SystemAddress &sa); - void SendOutOfBand(SystemAddress sa, MessageID oobId); - void OnPunchthroughFailure(void); - void OnReadyForNextPunchthrough(void); - void PushFailure(void); - bool RemoveFromFailureQueue(void); - void PushSuccess(void); - - struct SendPing - { - RakNet::Time nextActionTime; - SystemAddress targetAddress; - SystemAddress facilitator; - SystemAddress internalIds[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; - RakNetGUID targetGuid; - bool weAreSender; - int attemptCount; - int retryCount; - int punchingFixedPortAttempts; // only used for TestMode::PUNCHING_FIXED_PORT - uint16_t sessionId; - // Give priority to internal IP addresses because if we are on a LAN, we don't want to try to connect through the internet - enum TestMode - { - TESTING_INTERNAL_IPS, - WAITING_FOR_INTERNAL_IPS_RESPONSE, - SEND_WITH_TTL, - TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT, - TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT, - TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024, - TESTING_EXTERNAL_IPS_1024_TO_1024, - WAITING_AFTER_ALL_ATTEMPTS, - - // The trendnet remaps the remote port to 1024. - // If you continue punching on a different port for the same IP it bans you and the communication becomes unidirectioal - PUNCHING_FIXED_PORT, - - // try port 1024-1028 - } testMode; - } sp; - - PunchthroughConfiguration pc; - NatPunchthroughDebugInterface *natPunchthroughDebugInterface; - - // The first time we fail a NAT attempt, we add it to failedAttemptList and try again, since sometimes trying again later fixes the problem - // The second time we fail, we return ID_NAT_PUNCHTHROUGH_FAILED - struct AddrAndGuid - { - SystemAddress addr; - RakNetGUID guid; - }; - DataStructures::List failedAttemptList; - - void IncrementExternalAttemptCount(RakNet::Time time, RakNet::Time delta); - - struct TimeAndGuid - { - RakNet::Time time; - RakNetGUID guid; - }; - DataStructures::List groupRequestsInProgress; - - struct GroupPunchRequest - { - SystemAddress facilitator; - DataStructures::List pendingList; - DataStructures::List passedListGuid; - DataStructures::List passedListAddress; - DataStructures::List failedList; - DataStructures::List ignoredList; - }; - DataStructures::List groupPunchRequests; - void UpdateGroupPunchOnNatResult(SystemAddress facilitator, RakNetGUID targetSystem, SystemAddress targetSystemAddress, int result); // 0=failed, 1=success, 2=ignore -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* + +/// \file +/// \brief Contains the NAT-punchthrough plugin for the client. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatPunchthroughClient==1 + +#ifndef __NAT_PUNCHTHROUGH_CLIENT_H +#define __NAT_PUNCHTHROUGH_CLIENT_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "SocketIncludes.h" +#include "DS_List.h" +#include "RakString.h" + +// Trendnet TEW-632BRP sometimes starts at port 1024 and increments sequentially. +// Zonnet zsr1134we. Replies go out on the net, but are always absorbed by the remote router?? +// Dlink ebr2310 to Trendnet ok +// Trendnet TEW-652BRP to Trendnet 632BRP OK +// Trendnet TEW-632BRP to Trendnet 632BRP OK +// Buffalo WHR-HP-G54 OK +// Netgear WGR614 ok + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +struct Packet; +#if _RAKNET_SUPPORT_PacketLogger==1 +class PacketLogger; +#endif + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT PunchthroughConfiguration +{ + /// internal: (15 ms * 2 tries + 30 wait) * 5 ports * 8 players = 2.4 seconds + /// external: (50 ms * 8 sends + 100 wait) * 2 port * 8 players = 8 seconds + /// Total: 8 seconds + PunchthroughConfiguration() { + TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL=15; + TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL=50; + UDP_SENDS_PER_PORT_INTERNAL=2; + UDP_SENDS_PER_PORT_EXTERNAL=8; + INTERNAL_IP_WAIT_AFTER_ATTEMPTS=30; + MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK=5; /// set to 0 to not do lan connects + MAX_PREDICTIVE_PORT_RANGE=2; + EXTERNAL_IP_WAIT_BETWEEN_PORTS=100; + EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS=EXTERNAL_IP_WAIT_BETWEEN_PORTS; + retryOnFailure=false; + } + + /// How much time between each UDP send + RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL; + RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL; + + /// How many tries for one port before giving up and going to the next port + int UDP_SENDS_PER_PORT_INTERNAL; + int UDP_SENDS_PER_PORT_EXTERNAL; + + /// After giving up on one internal port, how long to wait before trying the next port + int INTERNAL_IP_WAIT_AFTER_ATTEMPTS; + + /// How many external ports to try past the last known starting port + int MAX_PREDICTIVE_PORT_RANGE; + + /// After giving up on one external port, how long to wait before trying the next port + int EXTERNAL_IP_WAIT_BETWEEN_PORTS; + + /// After trying all external ports, how long to wait before returning ID_NAT_PUNCHTHROUGH_FAILED + int EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS; + + /// Maximum number of internal IP address to try to connect to. + /// Cannot be greater than MAXIMUM_NUMBER_OF_INTERNAL_IDS + /// Should be high enough to try all internal IP addresses on the majority of computers + int MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK; + + /// If the first punchthrough attempt fails, try again + /// This sometimes works because the remote router was looking for an incoming message on a higher numbered port before responding to a lower numbered port from the other system + bool retryOnFailure; +}; + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughDebugInterface +{ + NatPunchthroughDebugInterface() {} + virtual ~NatPunchthroughDebugInterface() {} + virtual void OnClientMessage(const char *msg)=0; +}; + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_Printf : public NatPunchthroughDebugInterface +{ + virtual void OnClientMessage(const char *msg); +}; + +#if _RAKNET_SUPPORT_PacketLogger==1 +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_PacketLogger : public NatPunchthroughDebugInterface +{ + // Set to non-zero to write to the packetlogger! + PacketLogger *pl; + + NatPunchthroughDebugInterface_PacketLogger() {pl=0;} + ~NatPunchthroughDebugInterface_PacketLogger() {} + virtual void OnClientMessage(const char *msg); +}; +#endif + +/// \brief Client code for NATPunchthrough +/// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient
+/// Client will send datagrams to port to estimate next port
+/// Will simultaneously connect with another client once ports are estimated. +/// \sa NatTypeDetectionClient +/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html +/// \ingroup NAT_PUNCHTHROUGH_GROUP +class RAK_DLL_EXPORT NatPunchthroughClient : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(NatPunchthroughClient) + + NatPunchthroughClient(); + ~NatPunchthroughClient(); + + /// Punchthrough a NAT. Doesn't connect, just tries to setup the routing table + /// \param[in] destination The system to punch. Must already be connected to \a facilitator + /// \param[in] facilitator A system we are already connected to running the NatPunchthroughServer plugin + /// \sa OpenNATGroup() + /// You will get ID_NAT_PUNCHTHROUGH_SUCCEEDED on success + /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_TARGET_UNRESPONSIVE, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_PUNCHTHROUGH_FAILED on failures of various types + /// However, if you lose connection to the facilitator, you may not necessarily get above + bool OpenNAT(RakNetGUID destination, const SystemAddress &facilitator); + + /// Same as calling OpenNAT for a list of systems, but reply is delayed until all systems pass. + /// This is useful for peer to peer games where you want to connect to every system in the remote session, not just one particular system + /// \note For cloud computing, all systems in the group must be connected to the same facilitator since we're only specifying one + /// You will get ID_NAT_GROUP_PUNCH_SUCCEEDED on success + /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_GROUP_PUNCH_FAILED on failures of various types + /// However, if you lose connection to the facilitator, you may not necessarily get above + bool OpenNATGroup(DataStructures::List destinationSystems, const SystemAddress &facilitator); + + /// Modify the system configuration if desired + /// Don't modify the variables in the structure while punchthrough is in progress + PunchthroughConfiguration* GetPunchthroughConfiguration(void); + + /// Sets a callback to be called with debug messages + /// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear. + void SetDebugInterface(NatPunchthroughDebugInterface *i); + + /// Get the port mappings you should pass to UPNP (for miniupnpc-1.5, for the function UPNP_AddPortMapping) + void GetUPNPPortMappings(char *externalPort, char *internalPort, const SystemAddress &natPunchthroughServerAddress); + + /// \internal For plugin handling + virtual void Update(void); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + + /// \internal For plugin handling + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + + /// \internal For plugin handling + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + virtual void OnAttach(void); + virtual void OnDetach(void); + virtual void OnRakPeerShutdown(void); + void Clear(void); + +protected: + unsigned short mostRecentNewExternalPort; + void OnNatGroupPunchthroughRequest(Packet *packet); + void OnFailureNotification(Packet *packet); + void OnNatGroupPunchthroughReply(Packet *packet); + void OnGetMostRecentPort(Packet *packet); + void OnConnectAtTime(Packet *packet); + unsigned int GetPendingOpenNATIndex(RakNetGUID destination, const SystemAddress &facilitator); + void SendPunchthrough(RakNetGUID destination, const SystemAddress &facilitator); + void SendTTL(const SystemAddress &sa); + void SendOutOfBand(SystemAddress sa, MessageID oobId); + void OnPunchthroughFailure(void); + void OnReadyForNextPunchthrough(void); + void PushFailure(void); + bool RemoveFromFailureQueue(void); + void PushSuccess(void); + + struct SendPing + { + RakNet::Time nextActionTime; + SystemAddress targetAddress; + SystemAddress facilitator; + SystemAddress internalIds[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; + RakNetGUID targetGuid; + bool weAreSender; + int attemptCount; + int retryCount; + int punchingFixedPortAttempts; // only used for TestMode::PUNCHING_FIXED_PORT + uint16_t sessionId; + // Give priority to internal IP addresses because if we are on a LAN, we don't want to try to connect through the internet + enum TestMode + { + TESTING_INTERNAL_IPS, + WAITING_FOR_INTERNAL_IPS_RESPONSE, + SEND_WITH_TTL, + TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT, + TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT, + TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024, + TESTING_EXTERNAL_IPS_1024_TO_1024, + WAITING_AFTER_ALL_ATTEMPTS, + + // The trendnet remaps the remote port to 1024. + // If you continue punching on a different port for the same IP it bans you and the communication becomes unidirectioal + PUNCHING_FIXED_PORT, + + // try port 1024-1028 + } testMode; + } sp; + + PunchthroughConfiguration pc; + NatPunchthroughDebugInterface *natPunchthroughDebugInterface; + + // The first time we fail a NAT attempt, we add it to failedAttemptList and try again, since sometimes trying again later fixes the problem + // The second time we fail, we return ID_NAT_PUNCHTHROUGH_FAILED + struct AddrAndGuid + { + SystemAddress addr; + RakNetGUID guid; + }; + DataStructures::List failedAttemptList; + + void IncrementExternalAttemptCount(RakNet::Time time, RakNet::Time delta); + + struct TimeAndGuid + { + RakNet::Time time; + RakNetGUID guid; + }; + DataStructures::List groupRequestsInProgress; + + struct GroupPunchRequest + { + SystemAddress facilitator; + DataStructures::List pendingList; + DataStructures::List passedListGuid; + DataStructures::List passedListAddress; + DataStructures::List failedList; + DataStructures::List ignoredList; + }; + DataStructures::List groupPunchRequests; + void UpdateGroupPunchOnNatResult(SystemAddress facilitator, RakNetGUID targetSystem, SystemAddress targetSystemAddress, int result); // 0=failed, 1=success, 2=ignore +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughServer.cpp b/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughServer.cpp index 4130b65..fd057eb 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughServer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughServer.cpp @@ -1,563 +1,563 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatPunchthroughServer==1 - -#include "NatPunchthroughServer.h" -#include "SocketLayer.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "MTUSize.h" -#include "GetTime.h" -#include "PacketLogger.h" - -using namespace RakNet; - -void NatPunchthroughServerDebugInterface_Printf::OnServerMessage(const char *msg) -{ - printf("%s\n", msg); -} -#if _RAKNET_SUPPORT_PacketLogger==1 -void NatPunchthroughServerDebugInterface_PacketLogger::OnServerMessage(const char *msg) -{ - if (pl) - { - pl->WriteMiscellaneous("Nat", msg); - } -} -#endif - -void NatPunchthroughServer::User::DeleteConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca) -{ - unsigned int index = connectionAttempts.GetIndexOf(ca); - if ((unsigned int)index!=(unsigned int)-1) - { - RakNet::OP_DELETE(ca,_FILE_AND_LINE_); - connectionAttempts.RemoveAtIndex(index); - } -} -void NatPunchthroughServer::User::DerefConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca) -{ - unsigned int index = connectionAttempts.GetIndexOf(ca); - if ((unsigned int)index!=(unsigned int)-1) - { - connectionAttempts.RemoveAtIndex(index); - } -} -bool NatPunchthroughServer::User::HasConnectionAttemptToUser(User *user) -{ - unsigned int index; - for (index=0; index < connectionAttempts.Size(); index++) - { - if (connectionAttempts[index]->recipient->guid==user->guid || - connectionAttempts[index]->sender->guid==user->guid) - return true; - } - return false; -} -void NatPunchthroughServer::User::LogConnectionAttempts(RakNet::RakString &rs) -{ - rs.Clear(); - unsigned int index; - char guidStr[128], ipStr[128]; - guid.ToString(guidStr); - systemAddress.ToString(true,ipStr); - rs=RakNet::RakString("User systemAddress=%s guid=%s\n", ipStr, guidStr); - rs+=RakNet::RakString("%i attempts in list:\n", connectionAttempts.Size()); - for (index=0; index < connectionAttempts.Size(); index++) - { - rs+=RakNet::RakString("%i. SessionID=%i ", index+1, connectionAttempts[index]->sessionId); - if (connectionAttempts[index]->sender==this) - rs+="(We are sender) "; - else - rs+="(We are recipient) "; - if (isReady) - rs+="(READY TO START) "; - else - rs+="(NOT READY TO START) "; - if (connectionAttempts[index]->attemptPhase==NatPunchthroughServer::ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED) - rs+="(NOT_STARTED). "; - else - rs+="(GETTING_RECENT_PORTS). "; - if (connectionAttempts[index]->sender==this) - { - connectionAttempts[index]->recipient->guid.ToString(guidStr); - connectionAttempts[index]->recipient->systemAddress.ToString(true,ipStr); - } - else - { - connectionAttempts[index]->sender->guid.ToString(guidStr); - connectionAttempts[index]->sender->systemAddress.ToString(true,ipStr); - } - - rs+=RakNet::RakString("Target systemAddress=%s, guid=%s.\n", ipStr, guidStr); - } -} - -int RakNet::NatPunchthroughServer::NatPunchthroughUserComp( const RakNetGUID &key, User * const &data ) -{ - if (key < data->guid) - return -1; - if (key > data->guid) - return 1; - return 0; -} - -STATIC_FACTORY_DEFINITIONS(NatPunchthroughServer,NatPunchthroughServer); - -NatPunchthroughServer::NatPunchthroughServer() -{ - lastUpdate=0; - sessionId=0; - natPunchthroughServerDebugInterface=0; -} -NatPunchthroughServer::~NatPunchthroughServer() -{ - User *user, *otherUser; - ConnectionAttempt *connectionAttempt; - unsigned int j; - while(users.Size()) - { - user = users[0]; - for (j=0; j < user->connectionAttempts.Size(); j++) - { - connectionAttempt=user->connectionAttempts[j]; - if (connectionAttempt->sender==user) - otherUser=connectionAttempt->recipient; - else - otherUser=connectionAttempt->sender; - otherUser->DeleteConnectionAttempt(connectionAttempt); - } - RakNet::OP_DELETE(user,_FILE_AND_LINE_); - users[0]=users[users.Size()-1]; - users.RemoveAtIndex(users.Size()-1); - } -} -void NatPunchthroughServer::SetDebugInterface(NatPunchthroughServerDebugInterface *i) -{ - natPunchthroughServerDebugInterface=i; -} -void NatPunchthroughServer::Update(void) -{ - ConnectionAttempt *connectionAttempt; - User *user, *recipient; - unsigned int i,j; - RakNet::Time time = RakNet::GetTime(); - if (time > lastUpdate+250) - { - lastUpdate=time; - - for (i=0; i < users.Size(); i++) - { - user=users[i]; - for (j=0; j < user->connectionAttempts.Size(); j++) - { - connectionAttempt=user->connectionAttempts[j]; - if (connectionAttempt->sender==user) - { - if (connectionAttempt->attemptPhase!=ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED && - time > connectionAttempt->startTime && - time > 10000 + connectionAttempt->startTime ) // Formerly 5000, but sometimes false positives - { - RakNet::BitStream outgoingBs; - - // that other system might not be running the plugin - outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE); - outgoingBs.Write(connectionAttempt->recipient->guid); - outgoingBs.Write(connectionAttempt->sessionId); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->sender->systemAddress,false); - - // 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST - // However, recipient may be expecting it due to external code - // In that case, recipient would never get any response if the sender dropped - outgoingBs.Reset(); - outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE); - outgoingBs.Write(connectionAttempt->sender->guid); - outgoingBs.Write(connectionAttempt->sessionId); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->recipient->systemAddress,false); - - connectionAttempt->sender->isReady=true; - connectionAttempt->recipient->isReady=true; - recipient=connectionAttempt->recipient; - - - if (natPunchthroughServerDebugInterface) - { - char str[1024]; - char addr1[128], addr2[128]; - // 8/01/09 Fixed bug where this was after DeleteConnectionAttempt() - connectionAttempt->sender->systemAddress.ToString(true,addr1); - connectionAttempt->recipient->systemAddress.ToString(true,addr2); - sprintf(str, "Sending ID_NAT_TARGET_UNRESPONSIVE to sender %s and recipient %s.", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(str); - RakNet::RakString log; - connectionAttempt->sender->LogConnectionAttempts(log); - connectionAttempt->recipient->LogConnectionAttempts(log); - } - - - connectionAttempt->sender->DerefConnectionAttempt(connectionAttempt); - connectionAttempt->recipient->DeleteConnectionAttempt(connectionAttempt); - - StartPunchthroughForUser(user); - StartPunchthroughForUser(recipient); - - break; - } - } - } - } - } -} -PluginReceiveResult NatPunchthroughServer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_NAT_PUNCHTHROUGH_REQUEST: - OnNATPunchthroughRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_NAT_GET_MOST_RECENT_PORT: - OnGetMostRecentPort(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_NAT_CLIENT_READY: - OnClientReady(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - return RR_CONTINUE_PROCESSING; -} -void NatPunchthroughServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - - unsigned int i=0; - bool objectExists; - i = users.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists) - { - RakNet::BitStream outgoingBs; - DataStructures::List freedUpInProgressUsers; - User *user = users[i]; - User *otherUser; - unsigned int connectionAttemptIndex; - ConnectionAttempt *connectionAttempt; - for (connectionAttemptIndex=0; connectionAttemptIndex < user->connectionAttempts.Size(); connectionAttemptIndex++) - { - connectionAttempt=user->connectionAttempts[connectionAttemptIndex]; - outgoingBs.Reset(); - if (connectionAttempt->recipient==user) - { - otherUser=connectionAttempt->sender; - } - else - { - otherUser=connectionAttempt->recipient; - } - - // 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST - // However, recipient may be expecting it due to external code - // In that case, recipient would never get any response if the sender dropped - outgoingBs.Write((MessageID)ID_NAT_CONNECTION_TO_TARGET_LOST); - outgoingBs.Write(rakNetGUID); - outgoingBs.Write(connectionAttempt->sessionId); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,otherUser->systemAddress,false); - - // 4/22/09 - Bug: was checking inProgress, legacy variable not used elsewhere - if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS) - { - otherUser->isReady=true; - freedUpInProgressUsers.Insert(otherUser, _FILE_AND_LINE_ ); - } - - otherUser->DeleteConnectionAttempt(connectionAttempt); - } - - RakNet::OP_DELETE(users[i], _FILE_AND_LINE_); - users.RemoveAtIndex(i); - - for (i=0; i < freedUpInProgressUsers.Size(); i++) - { - StartPunchthroughForUser(freedUpInProgressUsers[i]); - } - } - - // Also remove from groupPunchthroughRequests - for (i=0; i < users.Size(); i++) - { - bool objectExists; - unsigned int gprIndex; - gprIndex = users[i]->groupPunchthroughRequests.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists) - { -// printf("DEBUG %i\n", __LINE__); - - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED); - outgoingBs.Write(rakNetGUID); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,users[i]->systemAddress,false); - - users[i]->groupPunchthroughRequests.RemoveAtIndex(gprIndex); - } - } -} - -void NatPunchthroughServer::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) systemAddress; - (void) isIncoming; - - User *user = RakNet::OP_NEW(_FILE_AND_LINE_); - user->guid=rakNetGUID; - user->mostRecentPort=0; - user->systemAddress=systemAddress; - user->isReady=true; - users.Insert(rakNetGUID, user, true, _FILE_AND_LINE_); - -// printf("Adding to users %s\n", rakNetGUID.ToString()); -// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString()); -} -void NatPunchthroughServer::OnNATPunchthroughRequest(Packet *packet) -{ - RakNet::BitStream outgoingBs; - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID recipientGuid, senderGuid; - incomingBs.Read(recipientGuid); - senderGuid=packet->guid; - unsigned int i; - bool objectExists; - i = users.GetIndexFromKey(senderGuid, &objectExists); - RakAssert(objectExists); - - ConnectionAttempt *ca = RakNet::OP_NEW(_FILE_AND_LINE_); - ca->sender=users[i]; - ca->sessionId=sessionId++; - i = users.GetIndexFromKey(recipientGuid, &objectExists); - if (objectExists==false) - { -// printf("DEBUG %i\n", __LINE__); -// printf("DEBUG recipientGuid=%s\n", recipientGuid.ToString()); -// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString()); - - outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED); - outgoingBs.Write(recipientGuid); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - RakNet::OP_DELETE(ca,_FILE_AND_LINE_); - return; - } - ca->recipient=users[i]; - if (ca->recipient->HasConnectionAttemptToUser(ca->sender)) - { - outgoingBs.Write((MessageID)ID_NAT_ALREADY_IN_PROGRESS); - outgoingBs.Write(recipientGuid); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - RakNet::OP_DELETE(ca,_FILE_AND_LINE_); - return; - } - - ca->sender->connectionAttempts.Insert(ca, _FILE_AND_LINE_ ); - ca->recipient->connectionAttempts.Insert(ca, _FILE_AND_LINE_ ); - - StartPunchthroughForUser(ca->sender); -} -void NatPunchthroughServer::OnClientReady(Packet *packet) -{ - unsigned int i; - bool objectExists; - i = users.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists) - { - users[i]->isReady=true; - StartPunchthroughForUser(users[i]); - } -} -void NatPunchthroughServer::OnGetMostRecentPort(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - uint16_t sessionId; - unsigned short mostRecentPort; - bsIn.Read(sessionId); - bsIn.Read(mostRecentPort); - - unsigned int i,j; - User *user; - ConnectionAttempt *connectionAttempt; - bool objectExists; - i = users.GetIndexFromKey(packet->guid, &objectExists); - - if (natPunchthroughServerDebugInterface) - { - RakNet::RakString log; - char addr1[128], addr2[128]; - packet->systemAddress.ToString(true,addr1); - packet->guid.ToString(addr2); - log=RakNet::RakString("Got ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s. port=%i. sessionId=%i. userFound=%i.", addr1, addr2, mostRecentPort, sessionId, objectExists); - natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); - } - - if (objectExists) - { - user=users[i]; - user->mostRecentPort=mostRecentPort; - RakNet::Time time = RakNet::GetTime(); - - for (j=0; j < user->connectionAttempts.Size(); j++) - { - connectionAttempt=user->connectionAttempts[j]; - if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS && - connectionAttempt->sender->mostRecentPort!=0 && - connectionAttempt->recipient->mostRecentPort!=0 && - // 04/29/08 add sessionId to prevent processing for other systems - connectionAttempt->sessionId==sessionId) - { - SystemAddress senderSystemAddress = connectionAttempt->sender->systemAddress; - SystemAddress recipientSystemAddress = connectionAttempt->recipient->systemAddress; - SystemAddress recipientTargetAddress = recipientSystemAddress; - SystemAddress senderTargetAddress = senderSystemAddress; - recipientTargetAddress.SetPort(connectionAttempt->recipient->mostRecentPort); - senderTargetAddress.SetPort(connectionAttempt->sender->mostRecentPort); - - // Pick a time far enough in the future that both systems will have gotten the message - int targetPing = rakPeerInterface->GetAveragePing(recipientTargetAddress); - int senderPing = rakPeerInterface->GetAveragePing(senderSystemAddress); - RakNet::Time simultaneousAttemptTime; - if (targetPing==-1 || senderPing==-1) - simultaneousAttemptTime = time + 1500; - else - { - int largerPing = targetPing > senderPing ? targetPing : senderPing; - if (largerPing * 4 < 100) - simultaneousAttemptTime = time + 100; - else - simultaneousAttemptTime = time + (largerPing * 4); - } - - if (natPunchthroughServerDebugInterface) - { - RakNet::RakString log; - char addr1[128], addr2[128]; - recipientSystemAddress.ToString(true,addr1); - connectionAttempt->recipient->guid.ToString(addr2); - log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to recipient systemAddress %s guid %s", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); - } - - // Send to recipient timestamped message to connect at time - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TIMESTAMP); - bsOut.Write(simultaneousAttemptTime); - bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME); - bsOut.Write(connectionAttempt->sessionId); - bsOut.Write(senderTargetAddress); // Public IP, using most recent port - for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP - bsOut.Write(rakPeerInterface->GetInternalID(senderSystemAddress,j)); - bsOut.Write(connectionAttempt->sender->guid); - bsOut.Write(false); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipientSystemAddress,false); - - - if (natPunchthroughServerDebugInterface) - { - RakNet::RakString log; - char addr1[128], addr2[128]; - senderSystemAddress.ToString(true,addr1); - connectionAttempt->sender->guid.ToString(addr2); - log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to sender systemAddress %s guid %s", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); - } - - - // Same for sender - bsOut.Reset(); - bsOut.Write((MessageID)ID_TIMESTAMP); - bsOut.Write(simultaneousAttemptTime); - bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME); - bsOut.Write(connectionAttempt->sessionId); - bsOut.Write(recipientTargetAddress); // Public IP, using most recent port - for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP - bsOut.Write(rakPeerInterface->GetInternalID(recipientSystemAddress,j)); - bsOut.Write(connectionAttempt->recipient->guid); - bsOut.Write(true); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,senderSystemAddress,false); - - connectionAttempt->recipient->DerefConnectionAttempt(connectionAttempt); - connectionAttempt->sender->DeleteConnectionAttempt(connectionAttempt); - - // 04/29/08 missing return - return; - } - } - } - else - { - - if (natPunchthroughServerDebugInterface) - { - RakNet::RakString log; - char addr1[128], addr2[128]; - packet->systemAddress.ToString(true,addr1); - packet->guid.ToString(addr2); - log=RakNet::RakString("Ignoring ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); - } - - } -} -void NatPunchthroughServer::StartPunchthroughForUser(User *user) -{ - if (user->isReady==false) - return; - - ConnectionAttempt *connectionAttempt; - User *sender,*recipient,*otherUser; - unsigned int i; - for (i=0; i < user->connectionAttempts.Size(); i++) - { - connectionAttempt=user->connectionAttempts[i]; - if (connectionAttempt->sender==user) - { - otherUser=connectionAttempt->recipient; - sender=user; - recipient=otherUser; - } - else - { - otherUser=connectionAttempt->sender; - recipient=user; - sender=otherUser; - } - - if (otherUser->isReady) - { - if (natPunchthroughServerDebugInterface) - { - char str[1024]; - char addr1[128], addr2[128]; - sender->systemAddress.ToString(true,addr1); - recipient->systemAddress.ToString(true,addr2); - sprintf(str, "Sending NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS to sender %s and recipient %s.", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(str); - } - - sender->isReady=false; - recipient->isReady=false; - connectionAttempt->attemptPhase=ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS; - connectionAttempt->startTime=RakNet::GetTime(); - - sender->mostRecentPort=0; - recipient->mostRecentPort=0; - - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT); - // 4/29/09 Write sessionID so we don't use returned port for a system we don't want - outgoingBs.Write(connectionAttempt->sessionId); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sender->systemAddress,false); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipient->systemAddress,false); - - // 4/22/09 - BUG: missing break statement here - break; - } - } -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatPunchthroughServer==1 + +#include "NatPunchthroughServer.h" +#include "SocketLayer.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "MTUSize.h" +#include "GetTime.h" +#include "PacketLogger.h" + +using namespace RakNet; + +void NatPunchthroughServerDebugInterface_Printf::OnServerMessage(const char *msg) +{ + printf("%s\n", msg); +} +#if _RAKNET_SUPPORT_PacketLogger==1 +void NatPunchthroughServerDebugInterface_PacketLogger::OnServerMessage(const char *msg) +{ + if (pl) + { + pl->WriteMiscellaneous("Nat", msg); + } +} +#endif + +void NatPunchthroughServer::User::DeleteConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca) +{ + unsigned int index = connectionAttempts.GetIndexOf(ca); + if ((unsigned int)index!=(unsigned int)-1) + { + RakNet::OP_DELETE(ca,_FILE_AND_LINE_); + connectionAttempts.RemoveAtIndex(index); + } +} +void NatPunchthroughServer::User::DerefConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca) +{ + unsigned int index = connectionAttempts.GetIndexOf(ca); + if ((unsigned int)index!=(unsigned int)-1) + { + connectionAttempts.RemoveAtIndex(index); + } +} +bool NatPunchthroughServer::User::HasConnectionAttemptToUser(User *user) +{ + unsigned int index; + for (index=0; index < connectionAttempts.Size(); index++) + { + if (connectionAttempts[index]->recipient->guid==user->guid || + connectionAttempts[index]->sender->guid==user->guid) + return true; + } + return false; +} +void NatPunchthroughServer::User::LogConnectionAttempts(RakNet::RakString &rs) +{ + rs.Clear(); + unsigned int index; + char guidStr[128], ipStr[128]; + guid.ToString(guidStr); + systemAddress.ToString(true,ipStr); + rs=RakNet::RakString("User systemAddress=%s guid=%s\n", ipStr, guidStr); + rs+=RakNet::RakString("%i attempts in list:\n", connectionAttempts.Size()); + for (index=0; index < connectionAttempts.Size(); index++) + { + rs+=RakNet::RakString("%i. SessionID=%i ", index+1, connectionAttempts[index]->sessionId); + if (connectionAttempts[index]->sender==this) + rs+="(We are sender) "; + else + rs+="(We are recipient) "; + if (isReady) + rs+="(READY TO START) "; + else + rs+="(NOT READY TO START) "; + if (connectionAttempts[index]->attemptPhase==NatPunchthroughServer::ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED) + rs+="(NOT_STARTED). "; + else + rs+="(GETTING_RECENT_PORTS). "; + if (connectionAttempts[index]->sender==this) + { + connectionAttempts[index]->recipient->guid.ToString(guidStr); + connectionAttempts[index]->recipient->systemAddress.ToString(true,ipStr); + } + else + { + connectionAttempts[index]->sender->guid.ToString(guidStr); + connectionAttempts[index]->sender->systemAddress.ToString(true,ipStr); + } + + rs+=RakNet::RakString("Target systemAddress=%s, guid=%s.\n", ipStr, guidStr); + } +} + +int RakNet::NatPunchthroughServer::NatPunchthroughUserComp( const RakNetGUID &key, User * const &data ) +{ + if (key < data->guid) + return -1; + if (key > data->guid) + return 1; + return 0; +} + +STATIC_FACTORY_DEFINITIONS(NatPunchthroughServer,NatPunchthroughServer); + +NatPunchthroughServer::NatPunchthroughServer() +{ + lastUpdate=0; + sessionId=0; + natPunchthroughServerDebugInterface=0; +} +NatPunchthroughServer::~NatPunchthroughServer() +{ + User *user, *otherUser; + ConnectionAttempt *connectionAttempt; + unsigned int j; + while(users.Size()) + { + user = users[0]; + for (j=0; j < user->connectionAttempts.Size(); j++) + { + connectionAttempt=user->connectionAttempts[j]; + if (connectionAttempt->sender==user) + otherUser=connectionAttempt->recipient; + else + otherUser=connectionAttempt->sender; + otherUser->DeleteConnectionAttempt(connectionAttempt); + } + RakNet::OP_DELETE(user,_FILE_AND_LINE_); + users[0]=users[users.Size()-1]; + users.RemoveAtIndex(users.Size()-1); + } +} +void NatPunchthroughServer::SetDebugInterface(NatPunchthroughServerDebugInterface *i) +{ + natPunchthroughServerDebugInterface=i; +} +void NatPunchthroughServer::Update(void) +{ + ConnectionAttempt *connectionAttempt; + User *user, *recipient; + unsigned int i,j; + RakNet::Time time = RakNet::GetTime(); + if (time > lastUpdate+250) + { + lastUpdate=time; + + for (i=0; i < users.Size(); i++) + { + user=users[i]; + for (j=0; j < user->connectionAttempts.Size(); j++) + { + connectionAttempt=user->connectionAttempts[j]; + if (connectionAttempt->sender==user) + { + if (connectionAttempt->attemptPhase!=ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED && + time > connectionAttempt->startTime && + time > 10000 + connectionAttempt->startTime ) // Formerly 5000, but sometimes false positives + { + RakNet::BitStream outgoingBs; + + // that other system might not be running the plugin + outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE); + outgoingBs.Write(connectionAttempt->recipient->guid); + outgoingBs.Write(connectionAttempt->sessionId); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->sender->systemAddress,false); + + // 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST + // However, recipient may be expecting it due to external code + // In that case, recipient would never get any response if the sender dropped + outgoingBs.Reset(); + outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE); + outgoingBs.Write(connectionAttempt->sender->guid); + outgoingBs.Write(connectionAttempt->sessionId); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->recipient->systemAddress,false); + + connectionAttempt->sender->isReady=true; + connectionAttempt->recipient->isReady=true; + recipient=connectionAttempt->recipient; + + + if (natPunchthroughServerDebugInterface) + { + char str[1024]; + char addr1[128], addr2[128]; + // 8/01/09 Fixed bug where this was after DeleteConnectionAttempt() + connectionAttempt->sender->systemAddress.ToString(true,addr1); + connectionAttempt->recipient->systemAddress.ToString(true,addr2); + sprintf(str, "Sending ID_NAT_TARGET_UNRESPONSIVE to sender %s and recipient %s.", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(str); + RakNet::RakString log; + connectionAttempt->sender->LogConnectionAttempts(log); + connectionAttempt->recipient->LogConnectionAttempts(log); + } + + + connectionAttempt->sender->DerefConnectionAttempt(connectionAttempt); + connectionAttempt->recipient->DeleteConnectionAttempt(connectionAttempt); + + StartPunchthroughForUser(user); + StartPunchthroughForUser(recipient); + + break; + } + } + } + } + } +} +PluginReceiveResult NatPunchthroughServer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_NAT_PUNCHTHROUGH_REQUEST: + OnNATPunchthroughRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_NAT_GET_MOST_RECENT_PORT: + OnGetMostRecentPort(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_NAT_CLIENT_READY: + OnClientReady(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + return RR_CONTINUE_PROCESSING; +} +void NatPunchthroughServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + + unsigned int i=0; + bool objectExists; + i = users.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists) + { + RakNet::BitStream outgoingBs; + DataStructures::List freedUpInProgressUsers; + User *user = users[i]; + User *otherUser; + unsigned int connectionAttemptIndex; + ConnectionAttempt *connectionAttempt; + for (connectionAttemptIndex=0; connectionAttemptIndex < user->connectionAttempts.Size(); connectionAttemptIndex++) + { + connectionAttempt=user->connectionAttempts[connectionAttemptIndex]; + outgoingBs.Reset(); + if (connectionAttempt->recipient==user) + { + otherUser=connectionAttempt->sender; + } + else + { + otherUser=connectionAttempt->recipient; + } + + // 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST + // However, recipient may be expecting it due to external code + // In that case, recipient would never get any response if the sender dropped + outgoingBs.Write((MessageID)ID_NAT_CONNECTION_TO_TARGET_LOST); + outgoingBs.Write(rakNetGUID); + outgoingBs.Write(connectionAttempt->sessionId); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,otherUser->systemAddress,false); + + // 4/22/09 - Bug: was checking inProgress, legacy variable not used elsewhere + if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS) + { + otherUser->isReady=true; + freedUpInProgressUsers.Insert(otherUser, _FILE_AND_LINE_ ); + } + + otherUser->DeleteConnectionAttempt(connectionAttempt); + } + + RakNet::OP_DELETE(users[i], _FILE_AND_LINE_); + users.RemoveAtIndex(i); + + for (i=0; i < freedUpInProgressUsers.Size(); i++) + { + StartPunchthroughForUser(freedUpInProgressUsers[i]); + } + } + + // Also remove from groupPunchthroughRequests + for (i=0; i < users.Size(); i++) + { + bool objectExists; + unsigned int gprIndex; + gprIndex = users[i]->groupPunchthroughRequests.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists) + { +// printf("DEBUG %i\n", __LINE__); + + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED); + outgoingBs.Write(rakNetGUID); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,users[i]->systemAddress,false); + + users[i]->groupPunchthroughRequests.RemoveAtIndex(gprIndex); + } + } +} + +void NatPunchthroughServer::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) systemAddress; + (void) isIncoming; + + User *user = RakNet::OP_NEW(_FILE_AND_LINE_); + user->guid=rakNetGUID; + user->mostRecentPort=0; + user->systemAddress=systemAddress; + user->isReady=true; + users.Insert(rakNetGUID, user, true, _FILE_AND_LINE_); + +// printf("Adding to users %s\n", rakNetGUID.ToString()); +// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString()); +} +void NatPunchthroughServer::OnNATPunchthroughRequest(Packet *packet) +{ + RakNet::BitStream outgoingBs; + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID recipientGuid, senderGuid; + incomingBs.Read(recipientGuid); + senderGuid=packet->guid; + unsigned int i; + bool objectExists; + i = users.GetIndexFromKey(senderGuid, &objectExists); + RakAssert(objectExists); + + ConnectionAttempt *ca = RakNet::OP_NEW(_FILE_AND_LINE_); + ca->sender=users[i]; + ca->sessionId=sessionId++; + i = users.GetIndexFromKey(recipientGuid, &objectExists); + if (objectExists==false) + { +// printf("DEBUG %i\n", __LINE__); +// printf("DEBUG recipientGuid=%s\n", recipientGuid.ToString()); +// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString()); + + outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED); + outgoingBs.Write(recipientGuid); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + RakNet::OP_DELETE(ca,_FILE_AND_LINE_); + return; + } + ca->recipient=users[i]; + if (ca->recipient->HasConnectionAttemptToUser(ca->sender)) + { + outgoingBs.Write((MessageID)ID_NAT_ALREADY_IN_PROGRESS); + outgoingBs.Write(recipientGuid); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + RakNet::OP_DELETE(ca,_FILE_AND_LINE_); + return; + } + + ca->sender->connectionAttempts.Insert(ca, _FILE_AND_LINE_ ); + ca->recipient->connectionAttempts.Insert(ca, _FILE_AND_LINE_ ); + + StartPunchthroughForUser(ca->sender); +} +void NatPunchthroughServer::OnClientReady(Packet *packet) +{ + unsigned int i; + bool objectExists; + i = users.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists) + { + users[i]->isReady=true; + StartPunchthroughForUser(users[i]); + } +} +void NatPunchthroughServer::OnGetMostRecentPort(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + uint16_t sessionId; + unsigned short mostRecentPort; + bsIn.Read(sessionId); + bsIn.Read(mostRecentPort); + + unsigned int i,j; + User *user; + ConnectionAttempt *connectionAttempt; + bool objectExists; + i = users.GetIndexFromKey(packet->guid, &objectExists); + + if (natPunchthroughServerDebugInterface) + { + RakNet::RakString log; + char addr1[128], addr2[128]; + packet->systemAddress.ToString(true,addr1); + packet->guid.ToString(addr2); + log=RakNet::RakString("Got ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s. port=%i. sessionId=%i. userFound=%i.", addr1, addr2, mostRecentPort, sessionId, objectExists); + natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); + } + + if (objectExists) + { + user=users[i]; + user->mostRecentPort=mostRecentPort; + RakNet::Time time = RakNet::GetTime(); + + for (j=0; j < user->connectionAttempts.Size(); j++) + { + connectionAttempt=user->connectionAttempts[j]; + if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS && + connectionAttempt->sender->mostRecentPort!=0 && + connectionAttempt->recipient->mostRecentPort!=0 && + // 04/29/08 add sessionId to prevent processing for other systems + connectionAttempt->sessionId==sessionId) + { + SystemAddress senderSystemAddress = connectionAttempt->sender->systemAddress; + SystemAddress recipientSystemAddress = connectionAttempt->recipient->systemAddress; + SystemAddress recipientTargetAddress = recipientSystemAddress; + SystemAddress senderTargetAddress = senderSystemAddress; + recipientTargetAddress.SetPort(connectionAttempt->recipient->mostRecentPort); + senderTargetAddress.SetPort(connectionAttempt->sender->mostRecentPort); + + // Pick a time far enough in the future that both systems will have gotten the message + int targetPing = rakPeerInterface->GetAveragePing(recipientTargetAddress); + int senderPing = rakPeerInterface->GetAveragePing(senderSystemAddress); + RakNet::Time simultaneousAttemptTime; + if (targetPing==-1 || senderPing==-1) + simultaneousAttemptTime = time + 1500; + else + { + int largerPing = targetPing > senderPing ? targetPing : senderPing; + if (largerPing * 4 < 100) + simultaneousAttemptTime = time + 100; + else + simultaneousAttemptTime = time + (largerPing * 4); + } + + if (natPunchthroughServerDebugInterface) + { + RakNet::RakString log; + char addr1[128], addr2[128]; + recipientSystemAddress.ToString(true,addr1); + connectionAttempt->recipient->guid.ToString(addr2); + log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to recipient systemAddress %s guid %s", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); + } + + // Send to recipient timestamped message to connect at time + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TIMESTAMP); + bsOut.Write(simultaneousAttemptTime); + bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME); + bsOut.Write(connectionAttempt->sessionId); + bsOut.Write(senderTargetAddress); // Public IP, using most recent port + for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP + bsOut.Write(rakPeerInterface->GetInternalID(senderSystemAddress,j)); + bsOut.Write(connectionAttempt->sender->guid); + bsOut.Write(false); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipientSystemAddress,false); + + + if (natPunchthroughServerDebugInterface) + { + RakNet::RakString log; + char addr1[128], addr2[128]; + senderSystemAddress.ToString(true,addr1); + connectionAttempt->sender->guid.ToString(addr2); + log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to sender systemAddress %s guid %s", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); + } + + + // Same for sender + bsOut.Reset(); + bsOut.Write((MessageID)ID_TIMESTAMP); + bsOut.Write(simultaneousAttemptTime); + bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME); + bsOut.Write(connectionAttempt->sessionId); + bsOut.Write(recipientTargetAddress); // Public IP, using most recent port + for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP + bsOut.Write(rakPeerInterface->GetInternalID(recipientSystemAddress,j)); + bsOut.Write(connectionAttempt->recipient->guid); + bsOut.Write(true); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,senderSystemAddress,false); + + connectionAttempt->recipient->DerefConnectionAttempt(connectionAttempt); + connectionAttempt->sender->DeleteConnectionAttempt(connectionAttempt); + + // 04/29/08 missing return + return; + } + } + } + else + { + + if (natPunchthroughServerDebugInterface) + { + RakNet::RakString log; + char addr1[128], addr2[128]; + packet->systemAddress.ToString(true,addr1); + packet->guid.ToString(addr2); + log=RakNet::RakString("Ignoring ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); + } + + } +} +void NatPunchthroughServer::StartPunchthroughForUser(User *user) +{ + if (user->isReady==false) + return; + + ConnectionAttempt *connectionAttempt; + User *sender,*recipient,*otherUser; + unsigned int i; + for (i=0; i < user->connectionAttempts.Size(); i++) + { + connectionAttempt=user->connectionAttempts[i]; + if (connectionAttempt->sender==user) + { + otherUser=connectionAttempt->recipient; + sender=user; + recipient=otherUser; + } + else + { + otherUser=connectionAttempt->sender; + recipient=user; + sender=otherUser; + } + + if (otherUser->isReady) + { + if (natPunchthroughServerDebugInterface) + { + char str[1024]; + char addr1[128], addr2[128]; + sender->systemAddress.ToString(true,addr1); + recipient->systemAddress.ToString(true,addr2); + sprintf(str, "Sending NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS to sender %s and recipient %s.", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(str); + } + + sender->isReady=false; + recipient->isReady=false; + connectionAttempt->attemptPhase=ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS; + connectionAttempt->startTime=RakNet::GetTime(); + + sender->mostRecentPort=0; + recipient->mostRecentPort=0; + + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT); + // 4/29/09 Write sessionID so we don't use returned port for a system we don't want + outgoingBs.Write(connectionAttempt->sessionId); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sender->systemAddress,false); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipient->systemAddress,false); + + // 4/22/09 - BUG: missing break statement here + break; + } + } +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughServer.h b/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughServer.h index 3b4f6df..7a7e138 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughServer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NatPunchthroughServer.h @@ -1,145 +1,145 @@ -/// \file -/// \brief Contains the NAT-punchthrough plugin for the server. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatPunchthroughServer==1 - -#ifndef __NAT_PUNCHTHROUGH_SERVER_H -#define __NAT_PUNCHTHROUGH_SERVER_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "SocketIncludes.h" -#include "DS_OrderedList.h" -#include "RakString.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -struct Packet; -#if _RAKNET_SUPPORT_PacketLogger==1 -class PacketLogger; -#endif - -/// \defgroup NAT_PUNCHTHROUGH_GROUP NatPunchthrough -/// \brief Connect systems despite both systems being behind a router -/// \details -/// \ingroup PLUGINS_GROUP - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface -{ - NatPunchthroughServerDebugInterface() {} - virtual ~NatPunchthroughServerDebugInterface() {} - virtual void OnServerMessage(const char *msg)=0; -}; - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface_Printf : public NatPunchthroughServerDebugInterface -{ - virtual void OnServerMessage(const char *msg); -}; - -#if _RAKNET_SUPPORT_PacketLogger==1 -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface_PacketLogger : public NatPunchthroughServerDebugInterface -{ - // Set to non-zero to write to the packetlogger! - PacketLogger *pl; - - NatPunchthroughServerDebugInterface_PacketLogger() {pl=0;} - ~NatPunchthroughServerDebugInterface_PacketLogger() {} - virtual void OnServerMessage(const char *msg); -}; -#endif - -/// \brief Server code for NATPunchthrough -/// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient
-/// Server maintains two sockets clients can connect to so as to estimate the next port choice
-/// Server tells other client about port estimate, current public port to the server, and a time to start connection attempts -/// \sa NatTypeDetectionClient -/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html -/// \ingroup NAT_PUNCHTHROUGH_GROUP -class RAK_DLL_EXPORT NatPunchthroughServer : public PluginInterface2 -{ -public: - - STATIC_FACTORY_DECLARATIONS(NatPunchthroughServer) - - // Constructor - NatPunchthroughServer(); - - // Destructor - virtual ~NatPunchthroughServer(); - - /// Sets a callback to be called with debug messages - /// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear. - void SetDebugInterface(NatPunchthroughServerDebugInterface *i); - - /// \internal For plugin handling - virtual void Update(void); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - - /// \internal For plugin handling - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - - // Each connected user has a ready state. Ready means ready for nat punchthrough. - struct User; - struct ConnectionAttempt - { - ConnectionAttempt() {sender=0; recipient=0; startTime=0; attemptPhase=NAT_ATTEMPT_PHASE_NOT_STARTED;} - User *sender, *recipient; - uint16_t sessionId; - RakNet::Time startTime; - enum - { - NAT_ATTEMPT_PHASE_NOT_STARTED, - NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS, - } attemptPhase; - }; - struct User - { - RakNetGUID guid; - SystemAddress systemAddress; - unsigned short mostRecentPort; - bool isReady; - DataStructures::OrderedList groupPunchthroughRequests; - - DataStructures::List connectionAttempts; - bool HasConnectionAttemptToUser(User *user); - void DerefConnectionAttempt(ConnectionAttempt *ca); - void DeleteConnectionAttempt(ConnectionAttempt *ca); - void LogConnectionAttempts(RakNet::RakString &rs); - }; - RakNet::Time lastUpdate; - static int NatPunchthroughUserComp( const RakNetGUID &key, User * const &data ); -protected: - void OnNATPunchthroughRequest(Packet *packet); - DataStructures::OrderedList users; - - void OnGetMostRecentPort(Packet *packet); - void OnClientReady(Packet *packet); - - void SendTimestamps(void); - void StartPendingPunchthrough(void); - void StartPunchthroughForUser(User*user); - uint16_t sessionId; - NatPunchthroughServerDebugInterface *natPunchthroughServerDebugInterface; - -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains the NAT-punchthrough plugin for the server. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatPunchthroughServer==1 + +#ifndef __NAT_PUNCHTHROUGH_SERVER_H +#define __NAT_PUNCHTHROUGH_SERVER_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "SocketIncludes.h" +#include "DS_OrderedList.h" +#include "RakString.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +struct Packet; +#if _RAKNET_SUPPORT_PacketLogger==1 +class PacketLogger; +#endif + +/// \defgroup NAT_PUNCHTHROUGH_GROUP NatPunchthrough +/// \brief Connect systems despite both systems being behind a router +/// \details +/// \ingroup PLUGINS_GROUP + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface +{ + NatPunchthroughServerDebugInterface() {} + virtual ~NatPunchthroughServerDebugInterface() {} + virtual void OnServerMessage(const char *msg)=0; +}; + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface_Printf : public NatPunchthroughServerDebugInterface +{ + virtual void OnServerMessage(const char *msg); +}; + +#if _RAKNET_SUPPORT_PacketLogger==1 +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface_PacketLogger : public NatPunchthroughServerDebugInterface +{ + // Set to non-zero to write to the packetlogger! + PacketLogger *pl; + + NatPunchthroughServerDebugInterface_PacketLogger() {pl=0;} + ~NatPunchthroughServerDebugInterface_PacketLogger() {} + virtual void OnServerMessage(const char *msg); +}; +#endif + +/// \brief Server code for NATPunchthrough +/// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient
+/// Server maintains two sockets clients can connect to so as to estimate the next port choice
+/// Server tells other client about port estimate, current public port to the server, and a time to start connection attempts +/// \sa NatTypeDetectionClient +/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html +/// \ingroup NAT_PUNCHTHROUGH_GROUP +class RAK_DLL_EXPORT NatPunchthroughServer : public PluginInterface2 +{ +public: + + STATIC_FACTORY_DECLARATIONS(NatPunchthroughServer) + + // Constructor + NatPunchthroughServer(); + + // Destructor + virtual ~NatPunchthroughServer(); + + /// Sets a callback to be called with debug messages + /// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear. + void SetDebugInterface(NatPunchthroughServerDebugInterface *i); + + /// \internal For plugin handling + virtual void Update(void); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + + /// \internal For plugin handling + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + + // Each connected user has a ready state. Ready means ready for nat punchthrough. + struct User; + struct ConnectionAttempt + { + ConnectionAttempt() {sender=0; recipient=0; startTime=0; attemptPhase=NAT_ATTEMPT_PHASE_NOT_STARTED;} + User *sender, *recipient; + uint16_t sessionId; + RakNet::Time startTime; + enum + { + NAT_ATTEMPT_PHASE_NOT_STARTED, + NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS, + } attemptPhase; + }; + struct User + { + RakNetGUID guid; + SystemAddress systemAddress; + unsigned short mostRecentPort; + bool isReady; + DataStructures::OrderedList groupPunchthroughRequests; + + DataStructures::List connectionAttempts; + bool HasConnectionAttemptToUser(User *user); + void DerefConnectionAttempt(ConnectionAttempt *ca); + void DeleteConnectionAttempt(ConnectionAttempt *ca); + void LogConnectionAttempts(RakNet::RakString &rs); + }; + RakNet::Time lastUpdate; + static int NatPunchthroughUserComp( const RakNetGUID &key, User * const &data ); +protected: + void OnNATPunchthroughRequest(Packet *packet); + DataStructures::OrderedList users; + + void OnGetMostRecentPort(Packet *packet); + void OnClientReady(Packet *packet); + + void SendTimestamps(void); + void StartPendingPunchthrough(void); + void StartPunchthroughForUser(User*user); + uint16_t sessionId; + NatPunchthroughServerDebugInterface *natPunchthroughServerDebugInterface; + +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionClient.cpp b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionClient.cpp index 89e28fd..b18ac7f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionClient.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionClient.cpp @@ -1,170 +1,170 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatTypeDetectionClient==1 - -#include "NatTypeDetectionClient.h" -#include "RakNetSocket.h" -#include "RakNetSmartPtr.h" -#include "BitStream.h" -#include "SocketIncludes.h" -#include "RakString.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "SocketLayer.h" -#include "SocketDefines.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(NatTypeDetectionClient,NatTypeDetectionClient); - -NatTypeDetectionClient::NatTypeDetectionClient() -{ - c2=INVALID_SOCKET; -} -NatTypeDetectionClient::~NatTypeDetectionClient() -{ - if (c2!=INVALID_SOCKET) - { - closesocket__(c2); - } -} -void NatTypeDetectionClient::DetectNATType(SystemAddress _serverAddress) -{ - if (IsInProgress()) - return; - - if (c2==INVALID_SOCKET) - { - DataStructures::List > sockets; - rakPeerInterface->GetSockets(sockets); - SystemAddress sockAddr; - SocketLayer::GetSystemAddress(sockets[0]->s, &sockAddr); - char str[64]; - sockAddr.ToString(false,str); - c2=CreateNonblockingBoundSocket(str); - c2Port=SocketLayer::GetLocalPort(c2); - } - - - serverAddress=_serverAddress; - - RakNet::BitStream bs; - bs.Write((unsigned char)ID_NAT_TYPE_DETECTION_REQUEST); - bs.Write(true); // IsRequest - bs.Write(c2Port); - rakPeerInterface->Send(&bs,MEDIUM_PRIORITY,RELIABLE,0,serverAddress,false); -} -void NatTypeDetectionClient::OnCompletion(NATTypeDetectionResult result) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)*2); - printf("Returning nat detection result to the user\n"); - p->data[0]=ID_NAT_TYPE_DETECTION_RESULT; - p->systemAddress=serverAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=rakPeerInterface->GetGuidFromSystemAddress(serverAddress); - p->data[1]=(unsigned char) result; - p->wasGeneratedLocally=true; - rakPeerInterface->PushBackPacket(p, true); - - // Symmetric and port restricted are determined by server, so no need to notify server we are done - if (result!=NAT_TYPE_PORT_RESTRICTED && result!=NAT_TYPE_SYMMETRIC) - { - // Otherwise tell the server we got this message, so it stops sending tests to us - RakNet::BitStream bs; - bs.Write((unsigned char)ID_NAT_TYPE_DETECTION_REQUEST); - bs.Write(false); // Done - rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,serverAddress,false); - } - - Shutdown(); -} -bool NatTypeDetectionClient::IsInProgress(void) const -{ - return serverAddress!=UNASSIGNED_SYSTEM_ADDRESS; -} -void NatTypeDetectionClient::Update(void) -{ - if (IsInProgress()) - { - char data[ MAXIMUM_MTU_SIZE ]; - int len; - SystemAddress sender; - len=NatTypeRecvFrom(data, c2, sender); - if (len==1 && data[0]==NAT_TYPE_NONE) - { - OnCompletion(NAT_TYPE_NONE); - RakAssert(IsInProgress()==false); - } - } -} -PluginReceiveResult NatTypeDetectionClient::OnReceive(Packet *packet) -{ - if (IsInProgress()) - { - switch (packet->data[0]) - { - case ID_OUT_OF_BAND_INTERNAL: - { - if (packet->length>=3 && packet->data[1]==ID_NAT_TYPE_DETECT) - { - OnCompletion((NATTypeDetectionResult)packet->data[2]); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - break; - case ID_NAT_TYPE_DETECTION_RESULT: - if (packet->wasGeneratedLocally==false) - { - OnCompletion((NATTypeDetectionResult)packet->data[1]); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - else - break; - case ID_NAT_TYPE_DETECTION_REQUEST: - OnTestPortRestricted(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - - return RR_CONTINUE_PROCESSING; -} -void NatTypeDetectionClient::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - if (IsInProgress() && systemAddress==serverAddress) - Shutdown(); -} -void NatTypeDetectionClient::OnTestPortRestricted(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - RakNet::RakString s3p4StrAddress; - bsIn.Read(s3p4StrAddress); - unsigned short s3p4Port; - bsIn.Read(s3p4Port); - - DataStructures::List > sockets; - rakPeerInterface->GetSockets(sockets); - SystemAddress s3p4Addr = sockets[0]->boundAddress; - s3p4Addr.FromStringExplicitPort(s3p4StrAddress.C_String(), s3p4Port); - - // Send off the RakNet socket to the specified address, message is unformatted - // Server does this twice, so don't have to unduly worry about packetloss - RakNet::BitStream bsOut; - bsOut.Write((MessageID) NAT_TYPE_PORT_RESTRICTED); - bsOut.Write(rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - SocketLayer::SendTo_PC( sockets[0]->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), s3p4Addr, __FILE__, __LINE__ ); -} -void NatTypeDetectionClient::Shutdown(void) -{ - serverAddress=UNASSIGNED_SYSTEM_ADDRESS; - if (c2!=INVALID_SOCKET) - { - closesocket__(c2); - c2=INVALID_SOCKET; - } - -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatTypeDetectionClient==1 + +#include "NatTypeDetectionClient.h" +#include "RakNetSocket.h" +#include "RakNetSmartPtr.h" +#include "BitStream.h" +#include "SocketIncludes.h" +#include "RakString.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "SocketLayer.h" +#include "SocketDefines.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(NatTypeDetectionClient,NatTypeDetectionClient); + +NatTypeDetectionClient::NatTypeDetectionClient() +{ + c2=INVALID_SOCKET; +} +NatTypeDetectionClient::~NatTypeDetectionClient() +{ + if (c2!=INVALID_SOCKET) + { + closesocket__(c2); + } +} +void NatTypeDetectionClient::DetectNATType(SystemAddress _serverAddress) +{ + if (IsInProgress()) + return; + + if (c2==INVALID_SOCKET) + { + DataStructures::List > sockets; + rakPeerInterface->GetSockets(sockets); + SystemAddress sockAddr; + SocketLayer::GetSystemAddress(sockets[0]->s, &sockAddr); + char str[64]; + sockAddr.ToString(false,str); + c2=CreateNonblockingBoundSocket(str); + c2Port=SocketLayer::GetLocalPort(c2); + } + + + serverAddress=_serverAddress; + + RakNet::BitStream bs; + bs.Write((unsigned char)ID_NAT_TYPE_DETECTION_REQUEST); + bs.Write(true); // IsRequest + bs.Write(c2Port); + rakPeerInterface->Send(&bs,MEDIUM_PRIORITY,RELIABLE,0,serverAddress,false); +} +void NatTypeDetectionClient::OnCompletion(NATTypeDetectionResult result) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)*2); + printf("Returning nat detection result to the user\n"); + p->data[0]=ID_NAT_TYPE_DETECTION_RESULT; + p->systemAddress=serverAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=rakPeerInterface->GetGuidFromSystemAddress(serverAddress); + p->data[1]=(unsigned char) result; + p->wasGeneratedLocally=true; + rakPeerInterface->PushBackPacket(p, true); + + // Symmetric and port restricted are determined by server, so no need to notify server we are done + if (result!=NAT_TYPE_PORT_RESTRICTED && result!=NAT_TYPE_SYMMETRIC) + { + // Otherwise tell the server we got this message, so it stops sending tests to us + RakNet::BitStream bs; + bs.Write((unsigned char)ID_NAT_TYPE_DETECTION_REQUEST); + bs.Write(false); // Done + rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,serverAddress,false); + } + + Shutdown(); +} +bool NatTypeDetectionClient::IsInProgress(void) const +{ + return serverAddress!=UNASSIGNED_SYSTEM_ADDRESS; +} +void NatTypeDetectionClient::Update(void) +{ + if (IsInProgress()) + { + char data[ MAXIMUM_MTU_SIZE ]; + int len; + SystemAddress sender; + len=NatTypeRecvFrom(data, c2, sender); + if (len==1 && data[0]==NAT_TYPE_NONE) + { + OnCompletion(NAT_TYPE_NONE); + RakAssert(IsInProgress()==false); + } + } +} +PluginReceiveResult NatTypeDetectionClient::OnReceive(Packet *packet) +{ + if (IsInProgress()) + { + switch (packet->data[0]) + { + case ID_OUT_OF_BAND_INTERNAL: + { + if (packet->length>=3 && packet->data[1]==ID_NAT_TYPE_DETECT) + { + OnCompletion((NATTypeDetectionResult)packet->data[2]); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + break; + case ID_NAT_TYPE_DETECTION_RESULT: + if (packet->wasGeneratedLocally==false) + { + OnCompletion((NATTypeDetectionResult)packet->data[1]); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + else + break; + case ID_NAT_TYPE_DETECTION_REQUEST: + OnTestPortRestricted(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + + return RR_CONTINUE_PROCESSING; +} +void NatTypeDetectionClient::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + if (IsInProgress() && systemAddress==serverAddress) + Shutdown(); +} +void NatTypeDetectionClient::OnTestPortRestricted(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + RakNet::RakString s3p4StrAddress; + bsIn.Read(s3p4StrAddress); + unsigned short s3p4Port; + bsIn.Read(s3p4Port); + + DataStructures::List > sockets; + rakPeerInterface->GetSockets(sockets); + SystemAddress s3p4Addr = sockets[0]->boundAddress; + s3p4Addr.FromStringExplicitPort(s3p4StrAddress.C_String(), s3p4Port); + + // Send off the RakNet socket to the specified address, message is unformatted + // Server does this twice, so don't have to unduly worry about packetloss + RakNet::BitStream bsOut; + bsOut.Write((MessageID) NAT_TYPE_PORT_RESTRICTED); + bsOut.Write(rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + SocketLayer::SendTo_PC( sockets[0]->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), s3p4Addr, __FILE__, __LINE__ ); +} +void NatTypeDetectionClient::Shutdown(void) +{ + serverAddress=UNASSIGNED_SYSTEM_ADDRESS; + if (c2!=INVALID_SOCKET) + { + closesocket__(c2); + c2=INVALID_SOCKET; + } + +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionClient.h b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionClient.h index 86cc74e..09614dc 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionClient.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionClient.h @@ -1,81 +1,81 @@ -/// \file -/// \brief Contains the NAT-type detection code for the client -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatTypeDetectionClient==1 - -#ifndef __NAT_TYPE_DETECTION_CLIENT_H -#define __NAT_TYPE_DETECTION_CLIENT_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "SocketIncludes.h" -#include "DS_OrderedList.h" -#include "RakString.h" -#include "NatTypeDetectionCommon.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -struct Packet; - - /// \brief Client code for NatTypeDetection - /// \details See NatTypeDetectionServer.h for algorithm - /// To use, just connect to the server, and call DetectNAT - /// You will get back ID_NAT_TYPE_DETECTION_RESULT with one of the enumerated values of NATTypeDetectionResult found in NATTypeDetectionCommon.h - /// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html - /// \sa NatPunchthroughClient - /// \sa NatTypeDetectionServer - /// \ingroup NAT_TYPE_DETECTION_GROUP - class RAK_DLL_EXPORT NatTypeDetectionClient : public PluginInterface2 - { - public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(NatTypeDetectionClient) - - // Constructor - NatTypeDetectionClient(); - - // Destructor - virtual ~NatTypeDetectionClient(); - - /// Send the message to the server to detect the nat type - /// Server must be running NatTypeDetectionServer - /// We must already be connected to the server - /// \param[in] serverAddress address of the server - void DetectNATType(SystemAddress _serverAddress); - - /// \internal For plugin handling - virtual void Update(void); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - protected: - SOCKET c2; - unsigned short c2Port; - void Shutdown(void); - void OnCompletion(NATTypeDetectionResult result); - bool IsInProgress(void) const; - - void OnTestPortRestricted(Packet *packet); - SystemAddress serverAddress; - }; - - -} - - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains the NAT-type detection code for the client +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatTypeDetectionClient==1 + +#ifndef __NAT_TYPE_DETECTION_CLIENT_H +#define __NAT_TYPE_DETECTION_CLIENT_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "SocketIncludes.h" +#include "DS_OrderedList.h" +#include "RakString.h" +#include "NatTypeDetectionCommon.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +struct Packet; + + /// \brief Client code for NatTypeDetection + /// \details See NatTypeDetectionServer.h for algorithm + /// To use, just connect to the server, and call DetectNAT + /// You will get back ID_NAT_TYPE_DETECTION_RESULT with one of the enumerated values of NATTypeDetectionResult found in NATTypeDetectionCommon.h + /// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html + /// \sa NatPunchthroughClient + /// \sa NatTypeDetectionServer + /// \ingroup NAT_TYPE_DETECTION_GROUP + class RAK_DLL_EXPORT NatTypeDetectionClient : public PluginInterface2 + { + public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(NatTypeDetectionClient) + + // Constructor + NatTypeDetectionClient(); + + // Destructor + virtual ~NatTypeDetectionClient(); + + /// Send the message to the server to detect the nat type + /// Server must be running NatTypeDetectionServer + /// We must already be connected to the server + /// \param[in] serverAddress address of the server + void DetectNATType(SystemAddress _serverAddress); + + /// \internal For plugin handling + virtual void Update(void); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + protected: + SOCKET c2; + unsigned short c2Port; + void Shutdown(void); + void OnCompletion(NATTypeDetectionResult result); + bool IsInProgress(void) const; + + void OnTestPortRestricted(Packet *packet); + SystemAddress serverAddress; + }; + + +} + + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionCommon.cpp b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionCommon.cpp index ddfc5d1..89856d5 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionCommon.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionCommon.cpp @@ -1,116 +1,116 @@ -#include "NatTypeDetectionCommon.h" -#include "SocketLayer.h" -#include "SocketIncludes.h" -#include "SocketDefines.h" - -using namespace RakNet; - -bool RakNet::CanConnect(NATTypeDetectionResult type1, NATTypeDetectionResult type2) -{ - /// If one system is NAT_TYPE_SYMMETRIC, the other must be NAT_TYPE_ADDRESS_RESTRICTED or less - /// If one system is NAT_TYPE_PORT_RESTRICTED, the other must be NAT_TYPE_PORT_RESTRICTED or less - bool connectionGraph[NAT_TYPE_COUNT][NAT_TYPE_COUNT] = - { - // None, Full Cone, Address Restricted, Port Restricted, Symmetric, Unknown, InProgress, Supports_UPNP - {true, true, true, true, true, false, false, false}, // None - {true, true, true, true, true, false, false, false}, // Full Cone - {true, true, true, true, true, false, false, false}, // Address restricted - {true, true, true, true, false, false, false, false}, // Port restricted - {true, true, true, false, false, false, false, false}, // Symmetric - {false, false, false, false, false, false, false, false}, // Unknown - {false, false, false, false, false, false, false, false}, // InProgress - {false, false, false, false, false, false, false, false} // Supports_UPNP - }; - - return connectionGraph[(int) type1][(int) type2]; -} - -const char *RakNet::NATTypeDetectionResultToString(NATTypeDetectionResult type) -{ - switch (type) - { - case NAT_TYPE_NONE: - return "None"; - case NAT_TYPE_FULL_CONE: - return "Full cone"; - case NAT_TYPE_ADDRESS_RESTRICTED: - return "Address restricted"; - case NAT_TYPE_PORT_RESTRICTED: - return "Port restricted"; - case NAT_TYPE_SYMMETRIC: - return "Symmetric"; - case NAT_TYPE_UNKNOWN: - return "Unknown"; - case NAT_TYPE_DETECTION_IN_PROGRESS: - return "In Progress"; - case NAT_TYPE_SUPPORTS_UPNP: - return "Supports UPNP"; - case NAT_TYPE_COUNT: - return "NAT_TYPE_COUNT"; - } - return "Error, unknown enum in NATTypeDetectionResult"; -} - -// None and relaxed can connect to anything -// Moderate can connect to moderate or less -// Strict can connect to relaxed or less -const char *RakNet::NATTypeDetectionResultToStringFriendly(NATTypeDetectionResult type) -{ - switch (type) - { - case NAT_TYPE_NONE: - return "Open"; - case NAT_TYPE_FULL_CONE: - return "Relaxed"; - case NAT_TYPE_ADDRESS_RESTRICTED: - return "Relaxed"; - case NAT_TYPE_PORT_RESTRICTED: - return "Moderate"; - case NAT_TYPE_SYMMETRIC: - return "Strict"; - case NAT_TYPE_UNKNOWN: - return "Unknown"; - case NAT_TYPE_DETECTION_IN_PROGRESS: - return "In Progress"; - case NAT_TYPE_SUPPORTS_UPNP: - return "Supports UPNP"; - case NAT_TYPE_COUNT: - return "NAT_TYPE_COUNT"; - } - return "Error, unknown enum in NATTypeDetectionResult"; -} - - -SOCKET RakNet::CreateNonblockingBoundSocket(const char *bindAddr ) -{ - SOCKET s = SocketLayer::CreateBoundSocket( 0, false, bindAddr, true, 0, AF_INET ); - #ifdef _WIN32 - unsigned long nonblocking = 1; - ioctlsocket__( s, FIONBIO, &nonblocking ); - - - - #else - fcntl( s, F_SETFL, O_NONBLOCK ); - #endif - return s; -} - -int RakNet::NatTypeRecvFrom(char *data, SOCKET socket, SystemAddress &sender) -{ - sockaddr_in sa; - socklen_t len2; - const int flag=0; - len2 = sizeof( sa ); - sa.sin_family = AF_INET; - sa.sin_port=0; - int len = recvfrom__( socket, data, MAXIMUM_MTU_SIZE, flag, ( sockaddr* ) & sa, ( socklen_t* ) & len2 ); - if (len>0) - { - sender.address.addr4.sin_family=AF_INET; - sender.address.addr4.sin_addr.s_addr = sa.sin_addr.s_addr; - //sender.SetPort( ntohs( sa.sin_port ) ); - sender.SetPort( ntohs( sa.sin_port ) ); - } - return len; -} +#include "NatTypeDetectionCommon.h" +#include "SocketLayer.h" +#include "SocketIncludes.h" +#include "SocketDefines.h" + +using namespace RakNet; + +bool RakNet::CanConnect(NATTypeDetectionResult type1, NATTypeDetectionResult type2) +{ + /// If one system is NAT_TYPE_SYMMETRIC, the other must be NAT_TYPE_ADDRESS_RESTRICTED or less + /// If one system is NAT_TYPE_PORT_RESTRICTED, the other must be NAT_TYPE_PORT_RESTRICTED or less + bool connectionGraph[NAT_TYPE_COUNT][NAT_TYPE_COUNT] = + { + // None, Full Cone, Address Restricted, Port Restricted, Symmetric, Unknown, InProgress, Supports_UPNP + {true, true, true, true, true, false, false, false}, // None + {true, true, true, true, true, false, false, false}, // Full Cone + {true, true, true, true, true, false, false, false}, // Address restricted + {true, true, true, true, false, false, false, false}, // Port restricted + {true, true, true, false, false, false, false, false}, // Symmetric + {false, false, false, false, false, false, false, false}, // Unknown + {false, false, false, false, false, false, false, false}, // InProgress + {false, false, false, false, false, false, false, false} // Supports_UPNP + }; + + return connectionGraph[(int) type1][(int) type2]; +} + +const char *RakNet::NATTypeDetectionResultToString(NATTypeDetectionResult type) +{ + switch (type) + { + case NAT_TYPE_NONE: + return "None"; + case NAT_TYPE_FULL_CONE: + return "Full cone"; + case NAT_TYPE_ADDRESS_RESTRICTED: + return "Address restricted"; + case NAT_TYPE_PORT_RESTRICTED: + return "Port restricted"; + case NAT_TYPE_SYMMETRIC: + return "Symmetric"; + case NAT_TYPE_UNKNOWN: + return "Unknown"; + case NAT_TYPE_DETECTION_IN_PROGRESS: + return "In Progress"; + case NAT_TYPE_SUPPORTS_UPNP: + return "Supports UPNP"; + case NAT_TYPE_COUNT: + return "NAT_TYPE_COUNT"; + } + return "Error, unknown enum in NATTypeDetectionResult"; +} + +// None and relaxed can connect to anything +// Moderate can connect to moderate or less +// Strict can connect to relaxed or less +const char *RakNet::NATTypeDetectionResultToStringFriendly(NATTypeDetectionResult type) +{ + switch (type) + { + case NAT_TYPE_NONE: + return "Open"; + case NAT_TYPE_FULL_CONE: + return "Relaxed"; + case NAT_TYPE_ADDRESS_RESTRICTED: + return "Relaxed"; + case NAT_TYPE_PORT_RESTRICTED: + return "Moderate"; + case NAT_TYPE_SYMMETRIC: + return "Strict"; + case NAT_TYPE_UNKNOWN: + return "Unknown"; + case NAT_TYPE_DETECTION_IN_PROGRESS: + return "In Progress"; + case NAT_TYPE_SUPPORTS_UPNP: + return "Supports UPNP"; + case NAT_TYPE_COUNT: + return "NAT_TYPE_COUNT"; + } + return "Error, unknown enum in NATTypeDetectionResult"; +} + + +SOCKET RakNet::CreateNonblockingBoundSocket(const char *bindAddr ) +{ + SOCKET s = SocketLayer::CreateBoundSocket( 0, false, bindAddr, true, 0, AF_INET ); + #ifdef _WIN32 + unsigned long nonblocking = 1; + ioctlsocket__( s, FIONBIO, &nonblocking ); + + + + #else + fcntl( s, F_SETFL, O_NONBLOCK ); + #endif + return s; +} + +int RakNet::NatTypeRecvFrom(char *data, SOCKET socket, SystemAddress &sender) +{ + sockaddr_in sa; + socklen_t len2; + const int flag=0; + len2 = sizeof( sa ); + sa.sin_family = AF_INET; + sa.sin_port=0; + int len = recvfrom__( socket, data, MAXIMUM_MTU_SIZE, flag, ( sockaddr* ) & sa, ( socklen_t* ) & len2 ); + if (len>0) + { + sender.address.addr4.sin_family=AF_INET; + sender.address.addr4.sin_addr.s_addr = sa.sin_addr.s_addr; + //sender.SetPort( ntohs( sa.sin_port ) ); + sender.SetPort( ntohs( sa.sin_port ) ); + } + return len; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionCommon.h b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionCommon.h index 1fbdacf..6c59c03 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionCommon.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionCommon.h @@ -1,56 +1,56 @@ -/// \defgroup NAT_TYPE_DETECTION_GROUP NatTypeDetection -/// \brief Use a remote server with multiple IP addresses to determine what type of NAT your router is using -/// \details -/// \ingroup PLUGINS_GROUP - -#ifndef __NAT_TYPE_DETECTION_COMMON_H -#define __NAT_TYPE_DETECTION_COMMON_H - -#include "SocketIncludes.h" -#include "RakNetTypes.h" - -namespace RakNet -{ - /// All possible types of NATs (except NAT_TYPE_COUNT, which is an internal value) - enum NATTypeDetectionResult - { - /// Works with anyone - NAT_TYPE_NONE, - /// Accepts any datagrams to a port that has been previously used. Will accept the first datagram from the remote peer. - NAT_TYPE_FULL_CONE, - /// Accepts datagrams to a port as long as the datagram source IP address is a system we have already sent to. Will accept the first datagram if both systems send simultaneously. Otherwise, will accept the first datagram after we have sent one datagram. - NAT_TYPE_ADDRESS_RESTRICTED, - /// Same as address-restricted cone NAT, but we had to send to both the correct remote IP address and correct remote port. The same source address and port to a different destination uses the same mapping. - NAT_TYPE_PORT_RESTRICTED, - /// A different port is chosen for every remote destination. The same source address and port to a different destination uses a different mapping. Since the port will be different, the first external punchthrough attempt will fail. For this to work it requires port-prediction (MAX_PREDICTIVE_PORT_RANGE>1) and that the router chooses ports sequentially. - NAT_TYPE_SYMMETRIC, - /// Hasn't been determined. NATTypeDetectionClient does not use this, but other plugins might - NAT_TYPE_UNKNOWN, - /// In progress. NATTypeDetectionClient does not use this, but other plugins might - NAT_TYPE_DETECTION_IN_PROGRESS, - /// Didn't bother figuring it out, as we support UPNP, so it is equivalent to NAT_TYPE_NONE. NATTypeDetectionClient does not use this, but other plugins might - NAT_TYPE_SUPPORTS_UPNP, - /// \internal Must be last - NAT_TYPE_COUNT - }; - - /// \return Can one system with NATTypeDetectionResult \a type1 connect to \a type2 - bool RAK_DLL_EXPORT CanConnect(NATTypeDetectionResult type1, NATTypeDetectionResult type2); - - /// Return a technical string representin the enumeration - RAK_DLL_EXPORT const char * NATTypeDetectionResultToString(NATTypeDetectionResult type); - - /// Return a friendly string representing the enumeration - /// None and relaxed can connect to anything - /// Moderate can connect to moderate or less - /// Strict can connect to relaxed or less - RAK_DLL_EXPORT const char * NATTypeDetectionResultToStringFriendly(NATTypeDetectionResult type); - - /// \internal - SOCKET RAK_DLL_EXPORT CreateNonblockingBoundSocket(const char *bindAddr); - - /// \internal - int NatTypeRecvFrom(char *data, SOCKET socket, SystemAddress &sender); -} - -#endif +/// \defgroup NAT_TYPE_DETECTION_GROUP NatTypeDetection +/// \brief Use a remote server with multiple IP addresses to determine what type of NAT your router is using +/// \details +/// \ingroup PLUGINS_GROUP + +#ifndef __NAT_TYPE_DETECTION_COMMON_H +#define __NAT_TYPE_DETECTION_COMMON_H + +#include "SocketIncludes.h" +#include "RakNetTypes.h" + +namespace RakNet +{ + /// All possible types of NATs (except NAT_TYPE_COUNT, which is an internal value) + enum NATTypeDetectionResult + { + /// Works with anyone + NAT_TYPE_NONE, + /// Accepts any datagrams to a port that has been previously used. Will accept the first datagram from the remote peer. + NAT_TYPE_FULL_CONE, + /// Accepts datagrams to a port as long as the datagram source IP address is a system we have already sent to. Will accept the first datagram if both systems send simultaneously. Otherwise, will accept the first datagram after we have sent one datagram. + NAT_TYPE_ADDRESS_RESTRICTED, + /// Same as address-restricted cone NAT, but we had to send to both the correct remote IP address and correct remote port. The same source address and port to a different destination uses the same mapping. + NAT_TYPE_PORT_RESTRICTED, + /// A different port is chosen for every remote destination. The same source address and port to a different destination uses a different mapping. Since the port will be different, the first external punchthrough attempt will fail. For this to work it requires port-prediction (MAX_PREDICTIVE_PORT_RANGE>1) and that the router chooses ports sequentially. + NAT_TYPE_SYMMETRIC, + /// Hasn't been determined. NATTypeDetectionClient does not use this, but other plugins might + NAT_TYPE_UNKNOWN, + /// In progress. NATTypeDetectionClient does not use this, but other plugins might + NAT_TYPE_DETECTION_IN_PROGRESS, + /// Didn't bother figuring it out, as we support UPNP, so it is equivalent to NAT_TYPE_NONE. NATTypeDetectionClient does not use this, but other plugins might + NAT_TYPE_SUPPORTS_UPNP, + /// \internal Must be last + NAT_TYPE_COUNT + }; + + /// \return Can one system with NATTypeDetectionResult \a type1 connect to \a type2 + bool RAK_DLL_EXPORT CanConnect(NATTypeDetectionResult type1, NATTypeDetectionResult type2); + + /// Return a technical string representin the enumeration + RAK_DLL_EXPORT const char * NATTypeDetectionResultToString(NATTypeDetectionResult type); + + /// Return a friendly string representing the enumeration + /// None and relaxed can connect to anything + /// Moderate can connect to moderate or less + /// Strict can connect to relaxed or less + RAK_DLL_EXPORT const char * NATTypeDetectionResultToStringFriendly(NATTypeDetectionResult type); + + /// \internal + SOCKET RAK_DLL_EXPORT CreateNonblockingBoundSocket(const char *bindAddr); + + /// \internal + int NatTypeRecvFrom(char *data, SOCKET socket, SystemAddress &sender); +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionServer.cpp b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionServer.cpp index cf9e7be..ede3af3 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionServer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionServer.cpp @@ -1,263 +1,263 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatTypeDetectionServer==1 - -#include "NatTypeDetectionServer.h" -#include "SocketLayer.h" -#include "RakNetSocket.h" -#include "RakNetSmartPtr.h" -#include "SocketIncludes.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "GetTime.h" -#include "BitStream.h" -#include "SocketDefines.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(NatTypeDetectionServer,NatTypeDetectionServer); - -NatTypeDetectionServer::NatTypeDetectionServer() -{ - s1p2=s2p3=s3p4=s4p5=INVALID_SOCKET; -} -NatTypeDetectionServer::~NatTypeDetectionServer() -{ - Shutdown(); -} -void NatTypeDetectionServer::Startup( - const char *nonRakNetIP2, - const char *nonRakNetIP3, - const char *nonRakNetIP4) -{ - DataStructures::List > sockets; - rakPeerInterface->GetSockets(sockets); - char str[64]; - sockets[0]->boundAddress.ToString(false,str); - s1p2=CreateNonblockingBoundSocket(str); - s1p2Port=SocketLayer::GetLocalPort(s1p2); - s2p3=CreateNonblockingBoundSocket(nonRakNetIP2); - s2p3Port=SocketLayer::GetLocalPort(s2p3); - s3p4=CreateNonblockingBoundSocket(nonRakNetIP3); - s3p4Port=SocketLayer::GetLocalPort(s3p4); - s4p5=CreateNonblockingBoundSocket(nonRakNetIP4); - s4p5Port=SocketLayer::GetLocalPort(s4p5); - strcpy(s3p4Address, nonRakNetIP3); -} -void NatTypeDetectionServer::Shutdown() -{ - if (s1p2!=INVALID_SOCKET) - { - closesocket__(s1p2); - s1p2=INVALID_SOCKET; - } - if (s2p3!=INVALID_SOCKET) - { - closesocket__(s2p3); - s2p3=INVALID_SOCKET; - } - if (s3p4!=INVALID_SOCKET) - { - closesocket__(s3p4); - s3p4=INVALID_SOCKET; - } - if (s4p5!=INVALID_SOCKET) - { - closesocket__(s4p5); - s4p5=INVALID_SOCKET; - } -} -void NatTypeDetectionServer::Update(void) -{ - int i=0; - RakNet::TimeMS time = RakNet::GetTimeMS(); - RakNet::BitStream bs; - SystemAddress boundAddress; - - // Only socket that receives messages is s3p4, to see if the external address is different than that of the connection to rakPeerInterface - char data[ MAXIMUM_MTU_SIZE ]; - int len; - SystemAddress senderAddr; - len=NatTypeRecvFrom(data, s3p4, senderAddr); - // Client is asking us if this is port restricted. Only client requests of this type come in on s3p4 - while (len>0 && data[0]==NAT_TYPE_PORT_RESTRICTED) - { - RakNet::BitStream bsIn((unsigned char*) data,len,false); - RakNetGUID senderGuid; - bsIn.IgnoreBytes(sizeof(MessageID)); - bool readSuccess = bsIn.Read(senderGuid); - RakAssert(readSuccess); - if (readSuccess) - { - unsigned int i = GetDetectionAttemptIndex(senderGuid); - if (i!=(unsigned int)-1) - { - bs.Reset(); - bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); - // If different, then symmetric - if (senderAddr!=natDetectionAttempts[i].systemAddress) - { - printf("Determined client is symmetric\n"); - bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); - } - else - { - // else port restricted - printf("Determined client is port restricted\n"); - bs.Write((unsigned char) NAT_TYPE_PORT_RESTRICTED); - } - - rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); - - // Done - natDetectionAttempts.RemoveAtIndexFast(i); - } - else - { - // RakAssert("i==0 in Update when looking up GUID in NatTypeDetectionServer.cpp. Either a bug or a late resend" && 0); - } - } - else - { - // RakAssert("Didn't read GUID in Update in NatTypeDetectionServer.cpp. Message format error" && 0); - } - - len=NatTypeRecvFrom(data, s3p4, senderAddr); - } - - - while (i < (int) natDetectionAttempts.Size()) - { - if (time > natDetectionAttempts[i].nextStateTime) - { - natDetectionAttempts[i].detectionState=(NATDetectionState)((int)natDetectionAttempts[i].detectionState+1); - natDetectionAttempts[i].nextStateTime=time+natDetectionAttempts[i].timeBetweenAttempts; - SystemAddress saOut; - unsigned char c; - bs.Reset(); - switch (natDetectionAttempts[i].detectionState) - { - case STATE_TESTING_NONE_1: - case STATE_TESTING_NONE_2: - c = NAT_TYPE_NONE; - printf("Testing NAT_TYPE_NONE\n"); - // S4P5 sends to C2. If arrived, no NAT. Done. (Else S4P5 potentially banned, do not use again). - saOut=natDetectionAttempts[i].systemAddress; - saOut.SetPort(natDetectionAttempts[i].c2Port); - SocketLayer::SendTo_PC( s4p5, (const char*) &c, 1, saOut, __FILE__, __LINE__ ); - break; - case STATE_TESTING_FULL_CONE_1: - case STATE_TESTING_FULL_CONE_2: - printf("Testing NAT_TYPE_FULL_CONE\n"); - rakPeerInterface->WriteOutOfBandHeader(&bs); - bs.Write((unsigned char) ID_NAT_TYPE_DETECT); - bs.Write((unsigned char) NAT_TYPE_FULL_CONE); - // S2P3 sends to C1 (Different address, different port, to previously used port on client). If received, Full-cone nat. Done. (Else S2P3 potentially banned, do not use again). - saOut=natDetectionAttempts[i].systemAddress; - saOut.SetPort(natDetectionAttempts[i].systemAddress.GetPort()); - SocketLayer::SendTo_PC( s2p3, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); - break; - case STATE_TESTING_ADDRESS_RESTRICTED_1: - case STATE_TESTING_ADDRESS_RESTRICTED_2: - printf("Testing NAT_TYPE_ADDRESS_RESTRICTED\n"); - rakPeerInterface->WriteOutOfBandHeader(&bs); - bs.Write((unsigned char) ID_NAT_TYPE_DETECT); - bs.Write((unsigned char) NAT_TYPE_ADDRESS_RESTRICTED); - // S1P2 sends to C1 (Same address, different port, to previously used port on client). If received, address-restricted cone nat. Done. - saOut=natDetectionAttempts[i].systemAddress; - saOut.SetPort(natDetectionAttempts[i].systemAddress.GetPort()); - SocketLayer::SendTo_PC( s1p2, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); - break; - case STATE_TESTING_PORT_RESTRICTED_1: - case STATE_TESTING_PORT_RESTRICTED_2: - // C1 sends to S3P4. If address of C1 as seen by S3P4 is the same as the address of C1 as seen by S1P1, then port-restricted cone nat. Done - printf("Testing NAT_TYPE_PORT_RESTRICTED\n"); - bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_REQUEST); - bs.Write(RakString::NonVariadic(s3p4Address)); - bs.Write(s3p4Port); - rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); - break; - default: - printf("Warning, exceeded final check STATE_TESTING_PORT_RESTRICTED_2.\nExpected that client would have sent NAT_TYPE_PORT_RESTRICTED on s3p4.\nDefaulting to Symmetric\n"); - bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); - bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); - rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); - natDetectionAttempts.RemoveAtIndexFast(i); - i--; - break; - } - - } - i++; - } -} -PluginReceiveResult NatTypeDetectionServer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_NAT_TYPE_DETECTION_REQUEST: - OnDetectionRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - return RR_CONTINUE_PROCESSING; -} -void NatTypeDetectionServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - unsigned int i = GetDetectionAttemptIndex(systemAddress); - if (i==(unsigned int)-1) - return; - natDetectionAttempts.RemoveAtIndexFast(i); -} -void NatTypeDetectionServer::OnDetectionRequest(Packet *packet) -{ - unsigned int i = GetDetectionAttemptIndex(packet->systemAddress); - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(1); - bool isRequest=false; - bsIn.Read(isRequest); - if (isRequest) - { - if (i!=(unsigned int)-1) - return; // Already in progress - - NATDetectionAttempt nda; - nda.detectionState=STATE_NONE; - nda.systemAddress=packet->systemAddress; - nda.guid=packet->guid; - bsIn.Read(nda.c2Port); - nda.nextStateTime=0; - nda.timeBetweenAttempts=rakPeerInterface->GetLastPing(nda.systemAddress)*3+50; - natDetectionAttempts.Push(nda, _FILE_AND_LINE_); - } - else - { - if (i==(unsigned int)-1) - return; // Unknown - // They are done - natDetectionAttempts.RemoveAtIndexFast(i); - } - -} -unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(const SystemAddress &sa) -{ - for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) - { - if (natDetectionAttempts[i].systemAddress==sa) - return i; - } - return (unsigned int) -1; -} -unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(RakNetGUID guid) -{ - for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) - { - if (natDetectionAttempts[i].guid==guid) - return i; - } - return (unsigned int) -1; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatTypeDetectionServer==1 + +#include "NatTypeDetectionServer.h" +#include "SocketLayer.h" +#include "RakNetSocket.h" +#include "RakNetSmartPtr.h" +#include "SocketIncludes.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "GetTime.h" +#include "BitStream.h" +#include "SocketDefines.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(NatTypeDetectionServer,NatTypeDetectionServer); + +NatTypeDetectionServer::NatTypeDetectionServer() +{ + s1p2=s2p3=s3p4=s4p5=INVALID_SOCKET; +} +NatTypeDetectionServer::~NatTypeDetectionServer() +{ + Shutdown(); +} +void NatTypeDetectionServer::Startup( + const char *nonRakNetIP2, + const char *nonRakNetIP3, + const char *nonRakNetIP4) +{ + DataStructures::List > sockets; + rakPeerInterface->GetSockets(sockets); + char str[64]; + sockets[0]->boundAddress.ToString(false,str); + s1p2=CreateNonblockingBoundSocket(str); + s1p2Port=SocketLayer::GetLocalPort(s1p2); + s2p3=CreateNonblockingBoundSocket(nonRakNetIP2); + s2p3Port=SocketLayer::GetLocalPort(s2p3); + s3p4=CreateNonblockingBoundSocket(nonRakNetIP3); + s3p4Port=SocketLayer::GetLocalPort(s3p4); + s4p5=CreateNonblockingBoundSocket(nonRakNetIP4); + s4p5Port=SocketLayer::GetLocalPort(s4p5); + strcpy(s3p4Address, nonRakNetIP3); +} +void NatTypeDetectionServer::Shutdown() +{ + if (s1p2!=INVALID_SOCKET) + { + closesocket__(s1p2); + s1p2=INVALID_SOCKET; + } + if (s2p3!=INVALID_SOCKET) + { + closesocket__(s2p3); + s2p3=INVALID_SOCKET; + } + if (s3p4!=INVALID_SOCKET) + { + closesocket__(s3p4); + s3p4=INVALID_SOCKET; + } + if (s4p5!=INVALID_SOCKET) + { + closesocket__(s4p5); + s4p5=INVALID_SOCKET; + } +} +void NatTypeDetectionServer::Update(void) +{ + int i=0; + RakNet::TimeMS time = RakNet::GetTimeMS(); + RakNet::BitStream bs; + SystemAddress boundAddress; + + // Only socket that receives messages is s3p4, to see if the external address is different than that of the connection to rakPeerInterface + char data[ MAXIMUM_MTU_SIZE ]; + int len; + SystemAddress senderAddr; + len=NatTypeRecvFrom(data, s3p4, senderAddr); + // Client is asking us if this is port restricted. Only client requests of this type come in on s3p4 + while (len>0 && data[0]==NAT_TYPE_PORT_RESTRICTED) + { + RakNet::BitStream bsIn((unsigned char*) data,len,false); + RakNetGUID senderGuid; + bsIn.IgnoreBytes(sizeof(MessageID)); + bool readSuccess = bsIn.Read(senderGuid); + RakAssert(readSuccess); + if (readSuccess) + { + unsigned int i = GetDetectionAttemptIndex(senderGuid); + if (i!=(unsigned int)-1) + { + bs.Reset(); + bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); + // If different, then symmetric + if (senderAddr!=natDetectionAttempts[i].systemAddress) + { + printf("Determined client is symmetric\n"); + bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); + } + else + { + // else port restricted + printf("Determined client is port restricted\n"); + bs.Write((unsigned char) NAT_TYPE_PORT_RESTRICTED); + } + + rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); + + // Done + natDetectionAttempts.RemoveAtIndexFast(i); + } + else + { + // RakAssert("i==0 in Update when looking up GUID in NatTypeDetectionServer.cpp. Either a bug or a late resend" && 0); + } + } + else + { + // RakAssert("Didn't read GUID in Update in NatTypeDetectionServer.cpp. Message format error" && 0); + } + + len=NatTypeRecvFrom(data, s3p4, senderAddr); + } + + + while (i < (int) natDetectionAttempts.Size()) + { + if (time > natDetectionAttempts[i].nextStateTime) + { + natDetectionAttempts[i].detectionState=(NATDetectionState)((int)natDetectionAttempts[i].detectionState+1); + natDetectionAttempts[i].nextStateTime=time+natDetectionAttempts[i].timeBetweenAttempts; + SystemAddress saOut; + unsigned char c; + bs.Reset(); + switch (natDetectionAttempts[i].detectionState) + { + case STATE_TESTING_NONE_1: + case STATE_TESTING_NONE_2: + c = NAT_TYPE_NONE; + printf("Testing NAT_TYPE_NONE\n"); + // S4P5 sends to C2. If arrived, no NAT. Done. (Else S4P5 potentially banned, do not use again). + saOut=natDetectionAttempts[i].systemAddress; + saOut.SetPort(natDetectionAttempts[i].c2Port); + SocketLayer::SendTo_PC( s4p5, (const char*) &c, 1, saOut, __FILE__, __LINE__ ); + break; + case STATE_TESTING_FULL_CONE_1: + case STATE_TESTING_FULL_CONE_2: + printf("Testing NAT_TYPE_FULL_CONE\n"); + rakPeerInterface->WriteOutOfBandHeader(&bs); + bs.Write((unsigned char) ID_NAT_TYPE_DETECT); + bs.Write((unsigned char) NAT_TYPE_FULL_CONE); + // S2P3 sends to C1 (Different address, different port, to previously used port on client). If received, Full-cone nat. Done. (Else S2P3 potentially banned, do not use again). + saOut=natDetectionAttempts[i].systemAddress; + saOut.SetPort(natDetectionAttempts[i].systemAddress.GetPort()); + SocketLayer::SendTo_PC( s2p3, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); + break; + case STATE_TESTING_ADDRESS_RESTRICTED_1: + case STATE_TESTING_ADDRESS_RESTRICTED_2: + printf("Testing NAT_TYPE_ADDRESS_RESTRICTED\n"); + rakPeerInterface->WriteOutOfBandHeader(&bs); + bs.Write((unsigned char) ID_NAT_TYPE_DETECT); + bs.Write((unsigned char) NAT_TYPE_ADDRESS_RESTRICTED); + // S1P2 sends to C1 (Same address, different port, to previously used port on client). If received, address-restricted cone nat. Done. + saOut=natDetectionAttempts[i].systemAddress; + saOut.SetPort(natDetectionAttempts[i].systemAddress.GetPort()); + SocketLayer::SendTo_PC( s1p2, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); + break; + case STATE_TESTING_PORT_RESTRICTED_1: + case STATE_TESTING_PORT_RESTRICTED_2: + // C1 sends to S3P4. If address of C1 as seen by S3P4 is the same as the address of C1 as seen by S1P1, then port-restricted cone nat. Done + printf("Testing NAT_TYPE_PORT_RESTRICTED\n"); + bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_REQUEST); + bs.Write(RakString::NonVariadic(s3p4Address)); + bs.Write(s3p4Port); + rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); + break; + default: + printf("Warning, exceeded final check STATE_TESTING_PORT_RESTRICTED_2.\nExpected that client would have sent NAT_TYPE_PORT_RESTRICTED on s3p4.\nDefaulting to Symmetric\n"); + bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); + bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); + rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); + natDetectionAttempts.RemoveAtIndexFast(i); + i--; + break; + } + + } + i++; + } +} +PluginReceiveResult NatTypeDetectionServer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_NAT_TYPE_DETECTION_REQUEST: + OnDetectionRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + return RR_CONTINUE_PROCESSING; +} +void NatTypeDetectionServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + unsigned int i = GetDetectionAttemptIndex(systemAddress); + if (i==(unsigned int)-1) + return; + natDetectionAttempts.RemoveAtIndexFast(i); +} +void NatTypeDetectionServer::OnDetectionRequest(Packet *packet) +{ + unsigned int i = GetDetectionAttemptIndex(packet->systemAddress); + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(1); + bool isRequest=false; + bsIn.Read(isRequest); + if (isRequest) + { + if (i!=(unsigned int)-1) + return; // Already in progress + + NATDetectionAttempt nda; + nda.detectionState=STATE_NONE; + nda.systemAddress=packet->systemAddress; + nda.guid=packet->guid; + bsIn.Read(nda.c2Port); + nda.nextStateTime=0; + nda.timeBetweenAttempts=rakPeerInterface->GetLastPing(nda.systemAddress)*3+50; + natDetectionAttempts.Push(nda, _FILE_AND_LINE_); + } + else + { + if (i==(unsigned int)-1) + return; // Unknown + // They are done + natDetectionAttempts.RemoveAtIndexFast(i); + } + +} +unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(const SystemAddress &sa) +{ + for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) + { + if (natDetectionAttempts[i].systemAddress==sa) + return i; + } + return (unsigned int) -1; +} +unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(RakNetGUID guid) +{ + for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) + { + if (natDetectionAttempts[i].guid==guid) + return i; + } + return (unsigned int) -1; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionServer.h b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionServer.h index 093382d..6c72559 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionServer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NatTypeDetectionServer.h @@ -1,120 +1,120 @@ -/// \file -/// \brief Contains the NAT-type detection code for the server -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatTypeDetectionServer==1 - -#ifndef __NAT_TYPE_DETECTION_SERVER_H -#define __NAT_TYPE_DETECTION_SERVER_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "SocketIncludes.h" -#include "DS_OrderedList.h" -#include "RakString.h" -#include "NatTypeDetectionCommon.h" - - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -struct Packet; - -/// \brief Server code for NatTypeDetection -/// \details -/// Sends to a remote system on certain ports and addresses to determine what type of router, if any, that client is behind -/// Requires that the server have 4 external IP addresses -///
    -///
  1. Server has 1 instance of RakNet. Server has four external ip addresses S1 to S4. Five ports are used in total P1 to P5. RakNet is bound to S1P1. Sockets are bound to S1P2, S2P3, S3P4, S4P5 -///
  2. Client with one port using RakNet (C1). Another port not using anything (C2). -///
  3. C1 connects to S1P1 for normal communication. -///
  4. S4P5 sends to C2. If arrived, no NAT. Done. (If didn't arrive, S4P5 potentially banned, do not use again). -///
  5. S2P3 sends to C1 (Different address, different port, to previously used port on client). If received, Full-cone nat. Done. (If didn't arrive, S2P3 potentially banned, do not use again). -///
  6. S1P2 sends to C1 (Same address, different port, to previously used port on client). If received, address-restricted cone nat. Done. -///
  7. Server via RakNet connection tells C1 to send to to S3P4. If address of C1 as seen by S3P4 is the same as the address of C1 as seen by S1P1 (RakNet connection), then port-restricted cone nat. Done -///
  8. Else symmetric nat. Done. -///
-/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html -/// \sa NatPunchthroughServer -/// \sa NatTypeDetectionClient -/// \ingroup NAT_TYPE_DETECTION_GROUP -class RAK_DLL_EXPORT NatTypeDetectionServer : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(NatTypeDetectionServer) - - // Constructor - NatTypeDetectionServer(); - - // Destructor - virtual ~NatTypeDetectionServer(); - - /// Start the system, binding to 3 external IPs not already in useS - /// \param[in] nonRakNetIP2 First unused external IP - /// \param[in] nonRakNetIP3 Second unused external IP - /// \param[in] nonRakNetIP4 Third unused external IP - void Startup( - const char *nonRakNetIP2, - const char *nonRakNetIP3, - const char *nonRakNetIP4); - - // Releases the sockets created in Startup(); - void Shutdown(void); - - /// \internal For plugin handling - virtual void Update(void); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - enum NATDetectionState - { - STATE_NONE, - STATE_TESTING_NONE_1, - STATE_TESTING_NONE_2, - STATE_TESTING_FULL_CONE_1, - STATE_TESTING_FULL_CONE_2, - STATE_TESTING_ADDRESS_RESTRICTED_1, - STATE_TESTING_ADDRESS_RESTRICTED_2, - STATE_TESTING_PORT_RESTRICTED_1, - STATE_TESTING_PORT_RESTRICTED_2, - STATE_DONE, - }; - - struct NATDetectionAttempt - { - SystemAddress systemAddress; - NATDetectionState detectionState; - RakNet::TimeMS nextStateTime; - RakNet::TimeMS timeBetweenAttempts; - unsigned short c2Port; - RakNetGUID guid; - }; - -protected: - void OnDetectionRequest(Packet *packet); - DataStructures::List natDetectionAttempts; - unsigned int GetDetectionAttemptIndex(const SystemAddress &sa); - unsigned int GetDetectionAttemptIndex(RakNetGUID guid); - - // s1p1 is rakpeer itself - SOCKET s1p2,s2p3,s3p4,s4p5; - unsigned short s1p2Port, s2p3Port, s3p4Port, s4p5Port; - char s3p4Address[64]; -}; -} - - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains the NAT-type detection code for the server +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatTypeDetectionServer==1 + +#ifndef __NAT_TYPE_DETECTION_SERVER_H +#define __NAT_TYPE_DETECTION_SERVER_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "SocketIncludes.h" +#include "DS_OrderedList.h" +#include "RakString.h" +#include "NatTypeDetectionCommon.h" + + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +struct Packet; + +/// \brief Server code for NatTypeDetection +/// \details +/// Sends to a remote system on certain ports and addresses to determine what type of router, if any, that client is behind +/// Requires that the server have 4 external IP addresses +///
    +///
  1. Server has 1 instance of RakNet. Server has four external ip addresses S1 to S4. Five ports are used in total P1 to P5. RakNet is bound to S1P1. Sockets are bound to S1P2, S2P3, S3P4, S4P5 +///
  2. Client with one port using RakNet (C1). Another port not using anything (C2). +///
  3. C1 connects to S1P1 for normal communication. +///
  4. S4P5 sends to C2. If arrived, no NAT. Done. (If didn't arrive, S4P5 potentially banned, do not use again). +///
  5. S2P3 sends to C1 (Different address, different port, to previously used port on client). If received, Full-cone nat. Done. (If didn't arrive, S2P3 potentially banned, do not use again). +///
  6. S1P2 sends to C1 (Same address, different port, to previously used port on client). If received, address-restricted cone nat. Done. +///
  7. Server via RakNet connection tells C1 to send to to S3P4. If address of C1 as seen by S3P4 is the same as the address of C1 as seen by S1P1 (RakNet connection), then port-restricted cone nat. Done +///
  8. Else symmetric nat. Done. +///
+/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html +/// \sa NatPunchthroughServer +/// \sa NatTypeDetectionClient +/// \ingroup NAT_TYPE_DETECTION_GROUP +class RAK_DLL_EXPORT NatTypeDetectionServer : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(NatTypeDetectionServer) + + // Constructor + NatTypeDetectionServer(); + + // Destructor + virtual ~NatTypeDetectionServer(); + + /// Start the system, binding to 3 external IPs not already in useS + /// \param[in] nonRakNetIP2 First unused external IP + /// \param[in] nonRakNetIP3 Second unused external IP + /// \param[in] nonRakNetIP4 Third unused external IP + void Startup( + const char *nonRakNetIP2, + const char *nonRakNetIP3, + const char *nonRakNetIP4); + + // Releases the sockets created in Startup(); + void Shutdown(void); + + /// \internal For plugin handling + virtual void Update(void); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + enum NATDetectionState + { + STATE_NONE, + STATE_TESTING_NONE_1, + STATE_TESTING_NONE_2, + STATE_TESTING_FULL_CONE_1, + STATE_TESTING_FULL_CONE_2, + STATE_TESTING_ADDRESS_RESTRICTED_1, + STATE_TESTING_ADDRESS_RESTRICTED_2, + STATE_TESTING_PORT_RESTRICTED_1, + STATE_TESTING_PORT_RESTRICTED_2, + STATE_DONE, + }; + + struct NATDetectionAttempt + { + SystemAddress systemAddress; + NATDetectionState detectionState; + RakNet::TimeMS nextStateTime; + RakNet::TimeMS timeBetweenAttempts; + unsigned short c2Port; + RakNetGUID guid; + }; + +protected: + void OnDetectionRequest(Packet *packet); + DataStructures::List natDetectionAttempts; + unsigned int GetDetectionAttemptIndex(const SystemAddress &sa); + unsigned int GetDetectionAttemptIndex(RakNetGUID guid); + + // s1p1 is rakpeer itself + SOCKET s1p2,s2p3,s3p4,s4p5; + unsigned short s1p2Port, s2p3Port, s3p4Port, s4p5Port; + char s3p4Address[64]; +}; +} + + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/NativeFeatureIncludes.h b/project/lib_projects/raknet/jni/RaknetSources/NativeFeatureIncludes.h index 1a8df4d..5c59be3 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NativeFeatureIncludes.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NativeFeatureIncludes.h @@ -1,175 +1,175 @@ -// If you want to change these defines, put them in NativeFeatureIncludesOverrides so your changes are not lost when updating RakNet -// The user should not edit this file -#include "NativeFeatureIncludesOverrides.h" - -#ifndef __NATIVE_FEATURE_INCLDUES_H -#define __NATIVE_FEATURE_INCLDUES_H - -// Uncomment below defines, and paste to NativeFeatureIncludesOverrides.h, to exclude plugins that you do not want to build into the static library, or DLL -// These are not all the plugins, only those that are in the core library -// Other plugins are located in DependentExtensions -// #define _RAKNET_SUPPORT_ConnectionGraph2 0 -// #define _RAKNET_SUPPORT_DirectoryDeltaTransfer 0 -// #define _RAKNET_SUPPORT_FileListTransfer 0 -// #define _RAKNET_SUPPORT_FullyConnectedMesh2 0 -// #define _RAKNET_SUPPORT_MessageFilter 0 -// #define _RAKNET_SUPPORT_NatPunchthroughClient 0 -// #define _RAKNET_SUPPORT_NatPunchthroughServer 0 -// #define _RAKNET_SUPPORT_NatTypeDetectionClient 0 -// #define _RAKNET_SUPPORT_NatTypeDetectionServer 0 -// #define _RAKNET_SUPPORT_PacketLogger 0 -// #define _RAKNET_SUPPORT_ReadyEvent 0 -// #define _RAKNET_SUPPORT_ReplicaManager3 0 -// #define _RAKNET_SUPPORT_Router2 0 -// #define _RAKNET_SUPPORT_RPC4Plugin 0 -// #define _RAKNET_SUPPORT_TeamBalancer 0 -// #define _RAKNET_SUPPORT_UDPProxyClient 0 -// #define _RAKNET_SUPPORT_UDPProxyCoordinator 0 -// #define _RAKNET_SUPPORT_UDPProxyServer 0 -// #define _RAKNET_SUPPORT_ConsoleServer 0 -// #define _RAKNET_SUPPORT_RakNetTransport 0 -// #define _RAKNET_SUPPORT_TelnetTransport 0 -// #define _RAKNET_SUPPORT_TCPInterface 0 -// #define _RAKNET_SUPPORT_LogCommandParser 0 -// #define _RAKNET_SUPPORT_RakNetCommandParser 0 -// #define _RAKNET_SUPPORT_EmailSender 0 -// #define _RAKNET_SUPPORT_HTTPConnection 0 -// #define _RAKNET_SUPPORT_PacketizedTCP 0 -// #define _RAKNET_SUPPORT_TwoWayAuthentication 0 - -// SET DEFAULTS IF UNDEFINED -#ifndef LIBCAT_SECURITY -#define LIBCAT_SECURITY 0 -#endif -#ifndef _RAKNET_SUPPORT_ConnectionGraph2 -#define _RAKNET_SUPPORT_ConnectionGraph2 1 -#endif -#ifndef _RAKNET_SUPPORT_DirectoryDeltaTransfer -#define _RAKNET_SUPPORT_DirectoryDeltaTransfer 1 -#endif -#ifndef _RAKNET_SUPPORT_FileListTransfer -#define _RAKNET_SUPPORT_FileListTransfer 1 -#endif -#ifndef _RAKNET_SUPPORT_FullyConnectedMesh -#define _RAKNET_SUPPORT_FullyConnectedMesh 1 -#endif -#ifndef _RAKNET_SUPPORT_FullyConnectedMesh2 -#define _RAKNET_SUPPORT_FullyConnectedMesh2 1 -#endif -#ifndef _RAKNET_SUPPORT_MessageFilter -#define _RAKNET_SUPPORT_MessageFilter 1 -#endif -#ifndef _RAKNET_SUPPORT_NatPunchthroughClient -#define _RAKNET_SUPPORT_NatPunchthroughClient 1 -#endif -#ifndef _RAKNET_SUPPORT_NatPunchthroughServer -#define _RAKNET_SUPPORT_NatPunchthroughServer 1 -#endif -#ifndef _RAKNET_SUPPORT_NatTypeDetectionClient -#define _RAKNET_SUPPORT_NatTypeDetectionClient 1 -#endif -#ifndef _RAKNET_SUPPORT_NatTypeDetectionServer -#define _RAKNET_SUPPORT_NatTypeDetectionServer 1 -#endif -#ifndef _RAKNET_SUPPORT_PacketLogger -#define _RAKNET_SUPPORT_PacketLogger 1 -#endif -#ifndef _RAKNET_SUPPORT_ReadyEvent -#define _RAKNET_SUPPORT_ReadyEvent 1 -#endif -#ifndef _RAKNET_SUPPORT_ReplicaManager3 -#define _RAKNET_SUPPORT_ReplicaManager3 1 -#endif -#ifndef _RAKNET_SUPPORT_Router2 -#define _RAKNET_SUPPORT_Router2 1 -#endif -#ifndef _RAKNET_SUPPORT_RPC4Plugin -#define _RAKNET_SUPPORT_RPC4Plugin 1 -#endif -#ifndef _RAKNET_SUPPORT_TeamBalancer -#define _RAKNET_SUPPORT_TeamBalancer 1 -#endif -#ifndef _RAKNET_SUPPORT_UDPProxyClient -#define _RAKNET_SUPPORT_UDPProxyClient 1 -#endif -#ifndef _RAKNET_SUPPORT_UDPProxyCoordinator -#define _RAKNET_SUPPORT_UDPProxyCoordinator 1 -#endif -#ifndef _RAKNET_SUPPORT_UDPProxyServer -#define _RAKNET_SUPPORT_UDPProxyServer 1 -#endif -#ifndef _RAKNET_SUPPORT_ConsoleServer -#define _RAKNET_SUPPORT_ConsoleServer 1 -#endif -#ifndef _RAKNET_SUPPORT_RakNetTransport -#define _RAKNET_SUPPORT_RakNetTransport 1 -#endif -#ifndef _RAKNET_SUPPORT_TelnetTransport -#define _RAKNET_SUPPORT_TelnetTransport 1 -#endif -#ifndef _RAKNET_SUPPORT_TCPInterface -#define _RAKNET_SUPPORT_TCPInterface 1 -#endif -#ifndef _RAKNET_SUPPORT_LogCommandParser -#define _RAKNET_SUPPORT_LogCommandParser 1 -#endif -#ifndef _RAKNET_SUPPORT_RakNetCommandParser -#define _RAKNET_SUPPORT_RakNetCommandParser 1 -#endif -#ifndef _RAKNET_SUPPORT_EmailSender -#define _RAKNET_SUPPORT_EmailSender 1 -#endif -#ifndef _RAKNET_SUPPORT_HTTPConnection -#define _RAKNET_SUPPORT_HTTPConnection 1 -#endif -#ifndef _RAKNET_SUPPORT_PacketizedTCP -#define _RAKNET_SUPPORT_PacketizedTCP 1 -#endif -#ifndef _RAKNET_SUPPORT_TwoWayAuthentication -#define _RAKNET_SUPPORT_TwoWayAuthentication 1 -#endif -#ifndef _RAKNET_SUPPORT_CloudClient -#define _RAKNET_SUPPORT_CloudClient 1 -#endif -#ifndef _RAKNET_SUPPORT_CloudServer -#define _RAKNET_SUPPORT_CloudServer 1 -#endif -#ifndef _RAKNET_SUPPORT_DynDNS -#define _RAKNET_SUPPORT_DynDNS 1 -#endif -#ifndef _RAKNET_SUPPORT_Rackspace -#define _RAKNET_SUPPORT_Rackspace 1 -#endif -#ifndef _RAKNET_SUPPORT_FileOperations -#define _RAKNET_SUPPORT_FileOperations 1 -#endif -#ifndef _RAKNET_SUPPORT_UDPForwarder -#define _RAKNET_SUPPORT_UDPForwarder 1 -#endif - -// Take care of dependencies - -#undef _RAKNET_SUPPORT_FileListTransfer -#define _RAKNET_SUPPORT_FileListTransfer 1 - - -#undef _RAKNET_SUPPORT_ConnectionGraph2 -#define _RAKNET_SUPPORT_ConnectionGraph2 1 - - -#undef _RAKNET_SUPPORT_PacketizedTCP -#define _RAKNET_SUPPORT_PacketizedTCP 1 - - -#undef _RAKNET_SUPPORT_TCPInterface -#define _RAKNET_SUPPORT_TCPInterface 1 - - - - - - - - - -#endif // __NATIVE_FEATURE_INCLDUES_H +// If you want to change these defines, put them in NativeFeatureIncludesOverrides so your changes are not lost when updating RakNet +// The user should not edit this file +#include "NativeFeatureIncludesOverrides.h" + +#ifndef __NATIVE_FEATURE_INCLDUES_H +#define __NATIVE_FEATURE_INCLDUES_H + +// Uncomment below defines, and paste to NativeFeatureIncludesOverrides.h, to exclude plugins that you do not want to build into the static library, or DLL +// These are not all the plugins, only those that are in the core library +// Other plugins are located in DependentExtensions +// #define _RAKNET_SUPPORT_ConnectionGraph2 0 +// #define _RAKNET_SUPPORT_DirectoryDeltaTransfer 0 +// #define _RAKNET_SUPPORT_FileListTransfer 0 +// #define _RAKNET_SUPPORT_FullyConnectedMesh2 0 +// #define _RAKNET_SUPPORT_MessageFilter 0 +// #define _RAKNET_SUPPORT_NatPunchthroughClient 0 +// #define _RAKNET_SUPPORT_NatPunchthroughServer 0 +// #define _RAKNET_SUPPORT_NatTypeDetectionClient 0 +// #define _RAKNET_SUPPORT_NatTypeDetectionServer 0 +// #define _RAKNET_SUPPORT_PacketLogger 0 +// #define _RAKNET_SUPPORT_ReadyEvent 0 +// #define _RAKNET_SUPPORT_ReplicaManager3 0 +// #define _RAKNET_SUPPORT_Router2 0 +// #define _RAKNET_SUPPORT_RPC4Plugin 0 +// #define _RAKNET_SUPPORT_TeamBalancer 0 +// #define _RAKNET_SUPPORT_UDPProxyClient 0 +// #define _RAKNET_SUPPORT_UDPProxyCoordinator 0 +// #define _RAKNET_SUPPORT_UDPProxyServer 0 +// #define _RAKNET_SUPPORT_ConsoleServer 0 +// #define _RAKNET_SUPPORT_RakNetTransport 0 +// #define _RAKNET_SUPPORT_TelnetTransport 0 +// #define _RAKNET_SUPPORT_TCPInterface 0 +// #define _RAKNET_SUPPORT_LogCommandParser 0 +// #define _RAKNET_SUPPORT_RakNetCommandParser 0 +// #define _RAKNET_SUPPORT_EmailSender 0 +// #define _RAKNET_SUPPORT_HTTPConnection 0 +// #define _RAKNET_SUPPORT_PacketizedTCP 0 +// #define _RAKNET_SUPPORT_TwoWayAuthentication 0 + +// SET DEFAULTS IF UNDEFINED +#ifndef LIBCAT_SECURITY +#define LIBCAT_SECURITY 0 +#endif +#ifndef _RAKNET_SUPPORT_ConnectionGraph2 +#define _RAKNET_SUPPORT_ConnectionGraph2 1 +#endif +#ifndef _RAKNET_SUPPORT_DirectoryDeltaTransfer +#define _RAKNET_SUPPORT_DirectoryDeltaTransfer 1 +#endif +#ifndef _RAKNET_SUPPORT_FileListTransfer +#define _RAKNET_SUPPORT_FileListTransfer 1 +#endif +#ifndef _RAKNET_SUPPORT_FullyConnectedMesh +#define _RAKNET_SUPPORT_FullyConnectedMesh 1 +#endif +#ifndef _RAKNET_SUPPORT_FullyConnectedMesh2 +#define _RAKNET_SUPPORT_FullyConnectedMesh2 1 +#endif +#ifndef _RAKNET_SUPPORT_MessageFilter +#define _RAKNET_SUPPORT_MessageFilter 1 +#endif +#ifndef _RAKNET_SUPPORT_NatPunchthroughClient +#define _RAKNET_SUPPORT_NatPunchthroughClient 1 +#endif +#ifndef _RAKNET_SUPPORT_NatPunchthroughServer +#define _RAKNET_SUPPORT_NatPunchthroughServer 1 +#endif +#ifndef _RAKNET_SUPPORT_NatTypeDetectionClient +#define _RAKNET_SUPPORT_NatTypeDetectionClient 1 +#endif +#ifndef _RAKNET_SUPPORT_NatTypeDetectionServer +#define _RAKNET_SUPPORT_NatTypeDetectionServer 1 +#endif +#ifndef _RAKNET_SUPPORT_PacketLogger +#define _RAKNET_SUPPORT_PacketLogger 1 +#endif +#ifndef _RAKNET_SUPPORT_ReadyEvent +#define _RAKNET_SUPPORT_ReadyEvent 1 +#endif +#ifndef _RAKNET_SUPPORT_ReplicaManager3 +#define _RAKNET_SUPPORT_ReplicaManager3 1 +#endif +#ifndef _RAKNET_SUPPORT_Router2 +#define _RAKNET_SUPPORT_Router2 1 +#endif +#ifndef _RAKNET_SUPPORT_RPC4Plugin +#define _RAKNET_SUPPORT_RPC4Plugin 1 +#endif +#ifndef _RAKNET_SUPPORT_TeamBalancer +#define _RAKNET_SUPPORT_TeamBalancer 1 +#endif +#ifndef _RAKNET_SUPPORT_UDPProxyClient +#define _RAKNET_SUPPORT_UDPProxyClient 1 +#endif +#ifndef _RAKNET_SUPPORT_UDPProxyCoordinator +#define _RAKNET_SUPPORT_UDPProxyCoordinator 1 +#endif +#ifndef _RAKNET_SUPPORT_UDPProxyServer +#define _RAKNET_SUPPORT_UDPProxyServer 1 +#endif +#ifndef _RAKNET_SUPPORT_ConsoleServer +#define _RAKNET_SUPPORT_ConsoleServer 1 +#endif +#ifndef _RAKNET_SUPPORT_RakNetTransport +#define _RAKNET_SUPPORT_RakNetTransport 1 +#endif +#ifndef _RAKNET_SUPPORT_TelnetTransport +#define _RAKNET_SUPPORT_TelnetTransport 1 +#endif +#ifndef _RAKNET_SUPPORT_TCPInterface +#define _RAKNET_SUPPORT_TCPInterface 1 +#endif +#ifndef _RAKNET_SUPPORT_LogCommandParser +#define _RAKNET_SUPPORT_LogCommandParser 1 +#endif +#ifndef _RAKNET_SUPPORT_RakNetCommandParser +#define _RAKNET_SUPPORT_RakNetCommandParser 1 +#endif +#ifndef _RAKNET_SUPPORT_EmailSender +#define _RAKNET_SUPPORT_EmailSender 1 +#endif +#ifndef _RAKNET_SUPPORT_HTTPConnection +#define _RAKNET_SUPPORT_HTTPConnection 1 +#endif +#ifndef _RAKNET_SUPPORT_PacketizedTCP +#define _RAKNET_SUPPORT_PacketizedTCP 1 +#endif +#ifndef _RAKNET_SUPPORT_TwoWayAuthentication +#define _RAKNET_SUPPORT_TwoWayAuthentication 1 +#endif +#ifndef _RAKNET_SUPPORT_CloudClient +#define _RAKNET_SUPPORT_CloudClient 1 +#endif +#ifndef _RAKNET_SUPPORT_CloudServer +#define _RAKNET_SUPPORT_CloudServer 1 +#endif +#ifndef _RAKNET_SUPPORT_DynDNS +#define _RAKNET_SUPPORT_DynDNS 1 +#endif +#ifndef _RAKNET_SUPPORT_Rackspace +#define _RAKNET_SUPPORT_Rackspace 1 +#endif +#ifndef _RAKNET_SUPPORT_FileOperations +#define _RAKNET_SUPPORT_FileOperations 1 +#endif +#ifndef _RAKNET_SUPPORT_UDPForwarder +#define _RAKNET_SUPPORT_UDPForwarder 1 +#endif + +// Take care of dependencies + +#undef _RAKNET_SUPPORT_FileListTransfer +#define _RAKNET_SUPPORT_FileListTransfer 1 + + +#undef _RAKNET_SUPPORT_ConnectionGraph2 +#define _RAKNET_SUPPORT_ConnectionGraph2 1 + + +#undef _RAKNET_SUPPORT_PacketizedTCP +#define _RAKNET_SUPPORT_PacketizedTCP 1 + + +#undef _RAKNET_SUPPORT_TCPInterface +#define _RAKNET_SUPPORT_TCPInterface 1 + + + + + + + + + +#endif // __NATIVE_FEATURE_INCLDUES_H diff --git a/project/lib_projects/raknet/jni/RaknetSources/NativeFeatureIncludesOverrides.h b/project/lib_projects/raknet/jni/RaknetSources/NativeFeatureIncludesOverrides.h index 820f7ab..d76ef48 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NativeFeatureIncludesOverrides.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NativeFeatureIncludesOverrides.h @@ -1,9 +1,9 @@ -// USER EDITABLE FILE -// See NativeFeatureIncludes.h - -#ifndef __NATIVE_FEATURE_INCLDUES_OVERRIDES_H -#define __NATIVE_FEATURE_INCLDUES_OVERRIDES_H - -//#define LIBCAT_SECURITY 1 - -#endif +// USER EDITABLE FILE +// See NativeFeatureIncludes.h + +#ifndef __NATIVE_FEATURE_INCLDUES_OVERRIDES_H +#define __NATIVE_FEATURE_INCLDUES_OVERRIDES_H + +//#define LIBCAT_SECURITY 1 + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/NativeTypes.h b/project/lib_projects/raknet/jni/RaknetSources/NativeTypes.h index f1ee4cf..bf3c5bb 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NativeTypes.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NativeTypes.h @@ -1,23 +1,23 @@ -#ifndef __NATIVE_TYPES_H -#define __NATIVE_TYPES_H - -#if defined(__GNUC__) || defined(__GCCXML__) || defined(__SNC__) || defined(__S3E__) -#include -#elif !defined(_STDINT_H) && !defined(_SN_STDINT_H) && !defined(_SYS_STDINT_H_) && !defined(_STDINT) && !defined(_MACHTYPES_H_) && !defined(_STDINT_H_) - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned __int32 uint32_t; - typedef signed char int8_t; - typedef signed short int16_t; - typedef __int32 int32_t; - #if defined(_MSC_VER) && _MSC_VER < 1300 - typedef unsigned __int64 uint64_t; - typedef signed __int64 int64_t; - #else - typedef unsigned long long int uint64_t; - typedef signed long long int64_t; - #endif -#endif - - -#endif +#ifndef __NATIVE_TYPES_H +#define __NATIVE_TYPES_H + +#if defined(__GNUC__) || defined(__GCCXML__) || defined(__SNC__) || defined(__S3E__) +#include +#elif !defined(_STDINT_H) && !defined(_SN_STDINT_H) && !defined(_SYS_STDINT_H_) && !defined(_STDINT) && !defined(_MACHTYPES_H_) && !defined(_STDINT_H_) + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned __int32 uint32_t; + typedef signed char int8_t; + typedef signed short int16_t; + typedef __int32 int32_t; + #if defined(_MSC_VER) && _MSC_VER < 1300 + typedef unsigned __int64 uint64_t; + typedef signed __int64 int64_t; + #else + typedef unsigned long long int uint64_t; + typedef signed long long int64_t; + #endif +#endif + + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/NetworkIDManager.cpp b/project/lib_projects/raknet/jni/RaknetSources/NetworkIDManager.cpp index 35f429a..ed82ee2 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NetworkIDManager.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/NetworkIDManager.cpp @@ -1,108 +1,108 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NetworkIDManager.h" -#include "NetworkIDObject.h" -#include "RakAssert.h" -#include "GetTime.h" -#include "RakSleep.h" -#include "SuperFastHash.h" -#include "RakPeerInterface.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(NetworkIDManager,NetworkIDManager) - -NetworkIDManager::NetworkIDManager() -{ - startingOffset = RakPeerInterface::Get64BitUniqueRandomNumber(); - memset(networkIdHash,0,sizeof(networkIdHash)); -} -NetworkIDManager::~NetworkIDManager(void) -{ - -} -NetworkIDObject *NetworkIDManager::GET_BASE_OBJECT_FROM_ID(NetworkID x) -{ - unsigned int hashIndex=NetworkIDToHashIndex(x); - NetworkIDObject *nio=networkIdHash[hashIndex]; - while (nio) - { - if (nio->GetNetworkID()==x) - return nio; - nio=nio->nextInstanceForNetworkIDManager; - } - return 0; -} -NetworkID NetworkIDManager::GetNewNetworkID(void) -{ - while (GET_BASE_OBJECT_FROM_ID(++startingOffset)) - ; - return startingOffset; -} -unsigned int NetworkIDManager::NetworkIDToHashIndex(NetworkID networkId) -{ -// return SuperFastHash((const char*) &networkId.guid.g,sizeof(networkId.guid.g)) % NETWORK_ID_MANAGER_HASH_LENGTH; - return (unsigned int) (networkId % NETWORK_ID_MANAGER_HASH_LENGTH); -} -void NetworkIDManager::TrackNetworkIDObject(NetworkIDObject *networkIdObject) -{ - RakAssert(networkIdObject->GetNetworkID()!=UNASSIGNED_NETWORK_ID); - unsigned int hashIndex=NetworkIDToHashIndex(networkIdObject->GetNetworkID()); -// printf("TrackNetworkIDObject hashIndex=%i guid=%s\n",hashIndex, networkIdObject->GetNetworkID().guid.ToString()); // removeme - if (networkIdHash[hashIndex]==0) - { - networkIdHash[hashIndex]=networkIdObject; - return; - } - NetworkIDObject *nio=networkIdHash[hashIndex]; - // Duplicate insertion? - RakAssert(nio!=networkIdObject); - // Random GUID conflict? - RakAssert(nio->GetNetworkID()!=networkIdObject->GetNetworkID()); - - while (nio->nextInstanceForNetworkIDManager!=0) - { - nio=nio->nextInstanceForNetworkIDManager; - - // Duplicate insertion? - RakAssert(nio!=networkIdObject); - // Random GUID conflict? - RakAssert(nio->GetNetworkID()!=networkIdObject->GetNetworkID()); - } - - networkIdObject->nextInstanceForNetworkIDManager=0; - nio->nextInstanceForNetworkIDManager=networkIdObject; -} -void NetworkIDManager::StopTrackingNetworkIDObject(NetworkIDObject *networkIdObject) -{ - RakAssert(networkIdObject->GetNetworkID()!=UNASSIGNED_NETWORK_ID); - unsigned int hashIndex=NetworkIDToHashIndex(networkIdObject->GetNetworkID()); -// printf("hashIndex=%i\n",hashIndex); // removeme - NetworkIDObject *nio=networkIdHash[hashIndex]; - if (nio==0) - { - RakAssert("NetworkIDManager::StopTrackingNetworkIDObject didn't find object" && 0); - return; - } - if (nio==networkIdObject) - { - networkIdHash[hashIndex]=nio->nextInstanceForNetworkIDManager; - return; - } - - while (nio) - { - if (nio->nextInstanceForNetworkIDManager==networkIdObject) - { - nio->nextInstanceForNetworkIDManager=networkIdObject->nextInstanceForNetworkIDManager; - return; - } - nio=nio->nextInstanceForNetworkIDManager; - } - - RakAssert("NetworkIDManager::StopTrackingNetworkIDObject didn't find object" && 0); -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NetworkIDManager.h" +#include "NetworkIDObject.h" +#include "RakAssert.h" +#include "GetTime.h" +#include "RakSleep.h" +#include "SuperFastHash.h" +#include "RakPeerInterface.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(NetworkIDManager,NetworkIDManager) + +NetworkIDManager::NetworkIDManager() +{ + startingOffset = RakPeerInterface::Get64BitUniqueRandomNumber(); + memset(networkIdHash,0,sizeof(networkIdHash)); +} +NetworkIDManager::~NetworkIDManager(void) +{ + +} +NetworkIDObject *NetworkIDManager::GET_BASE_OBJECT_FROM_ID(NetworkID x) +{ + unsigned int hashIndex=NetworkIDToHashIndex(x); + NetworkIDObject *nio=networkIdHash[hashIndex]; + while (nio) + { + if (nio->GetNetworkID()==x) + return nio; + nio=nio->nextInstanceForNetworkIDManager; + } + return 0; +} +NetworkID NetworkIDManager::GetNewNetworkID(void) +{ + while (GET_BASE_OBJECT_FROM_ID(++startingOffset)) + ; + return startingOffset; +} +unsigned int NetworkIDManager::NetworkIDToHashIndex(NetworkID networkId) +{ +// return SuperFastHash((const char*) &networkId.guid.g,sizeof(networkId.guid.g)) % NETWORK_ID_MANAGER_HASH_LENGTH; + return (unsigned int) (networkId % NETWORK_ID_MANAGER_HASH_LENGTH); +} +void NetworkIDManager::TrackNetworkIDObject(NetworkIDObject *networkIdObject) +{ + RakAssert(networkIdObject->GetNetworkID()!=UNASSIGNED_NETWORK_ID); + unsigned int hashIndex=NetworkIDToHashIndex(networkIdObject->GetNetworkID()); +// printf("TrackNetworkIDObject hashIndex=%i guid=%s\n",hashIndex, networkIdObject->GetNetworkID().guid.ToString()); // removeme + if (networkIdHash[hashIndex]==0) + { + networkIdHash[hashIndex]=networkIdObject; + return; + } + NetworkIDObject *nio=networkIdHash[hashIndex]; + // Duplicate insertion? + RakAssert(nio!=networkIdObject); + // Random GUID conflict? + RakAssert(nio->GetNetworkID()!=networkIdObject->GetNetworkID()); + + while (nio->nextInstanceForNetworkIDManager!=0) + { + nio=nio->nextInstanceForNetworkIDManager; + + // Duplicate insertion? + RakAssert(nio!=networkIdObject); + // Random GUID conflict? + RakAssert(nio->GetNetworkID()!=networkIdObject->GetNetworkID()); + } + + networkIdObject->nextInstanceForNetworkIDManager=0; + nio->nextInstanceForNetworkIDManager=networkIdObject; +} +void NetworkIDManager::StopTrackingNetworkIDObject(NetworkIDObject *networkIdObject) +{ + RakAssert(networkIdObject->GetNetworkID()!=UNASSIGNED_NETWORK_ID); + unsigned int hashIndex=NetworkIDToHashIndex(networkIdObject->GetNetworkID()); +// printf("hashIndex=%i\n",hashIndex); // removeme + NetworkIDObject *nio=networkIdHash[hashIndex]; + if (nio==0) + { + RakAssert("NetworkIDManager::StopTrackingNetworkIDObject didn't find object" && 0); + return; + } + if (nio==networkIdObject) + { + networkIdHash[hashIndex]=nio->nextInstanceForNetworkIDManager; + return; + } + + while (nio) + { + if (nio->nextInstanceForNetworkIDManager==networkIdObject) + { + nio->nextInstanceForNetworkIDManager=networkIdObject->nextInstanceForNetworkIDManager; + return; + } + nio=nio->nextInstanceForNetworkIDManager; + } + + RakAssert("NetworkIDManager::StopTrackingNetworkIDObject didn't find object" && 0); +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/NetworkIDManager.h b/project/lib_projects/raknet/jni/RaknetSources/NetworkIDManager.h index 0afd057..ae009be 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NetworkIDManager.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NetworkIDManager.h @@ -1,70 +1,70 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __NETWORK_ID_MANAGER_H -#define __NETWORK_ID_MANAGER_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "RakMemoryOverride.h" -#include "NetworkIDObject.h" -#include "Rand.h" - -namespace RakNet -{ - -/// Increase this value if you plan to have many persistent objects -/// This value must match on all systems -#define NETWORK_ID_MANAGER_HASH_LENGTH 1024 - -/// This class is simply used to generate a unique number for a group of instances of NetworkIDObject -/// An instance of this class is required to use the ObjectID to pointer lookup system -/// You should have one instance of this class per game instance. -/// Call SetIsNetworkIDAuthority before using any functions of this class, or of NetworkIDObject -class RAK_DLL_EXPORT NetworkIDManager -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(NetworkIDManager) - - NetworkIDManager(); - virtual ~NetworkIDManager(void); - - /// Returns the parent object, or this instance if you don't use a parent. - /// Supports NetworkIDObject anywhere in the inheritance hierarchy - /// \pre You must first call SetNetworkIDManager before using this function - template - returnType GET_OBJECT_FROM_ID(NetworkID x) { - NetworkIDObject *nio = GET_BASE_OBJECT_FROM_ID(x); - if (nio==0) - return 0; - if (nio->GetParent()) - return (returnType) nio->GetParent(); - return (returnType) nio; - } - - /// \internal - NetworkIDObject *GET_BASE_OBJECT_FROM_ID(NetworkID x); - - /// \internal - void TrackNetworkIDObject(NetworkIDObject *networkIdObject); - void StopTrackingNetworkIDObject(NetworkIDObject *networkIdObject); - -protected: - friend class NetworkIDObject; - - NetworkIDObject *networkIdHash[NETWORK_ID_MANAGER_HASH_LENGTH]; - unsigned int NetworkIDToHashIndex(NetworkID networkId); - uint64_t startingOffset; - /// \internal - NetworkID GetNewNetworkID(void); - -}; - -} // namespace RakNet - -#endif +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __NETWORK_ID_MANAGER_H +#define __NETWORK_ID_MANAGER_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "RakMemoryOverride.h" +#include "NetworkIDObject.h" +#include "Rand.h" + +namespace RakNet +{ + +/// Increase this value if you plan to have many persistent objects +/// This value must match on all systems +#define NETWORK_ID_MANAGER_HASH_LENGTH 1024 + +/// This class is simply used to generate a unique number for a group of instances of NetworkIDObject +/// An instance of this class is required to use the ObjectID to pointer lookup system +/// You should have one instance of this class per game instance. +/// Call SetIsNetworkIDAuthority before using any functions of this class, or of NetworkIDObject +class RAK_DLL_EXPORT NetworkIDManager +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(NetworkIDManager) + + NetworkIDManager(); + virtual ~NetworkIDManager(void); + + /// Returns the parent object, or this instance if you don't use a parent. + /// Supports NetworkIDObject anywhere in the inheritance hierarchy + /// \pre You must first call SetNetworkIDManager before using this function + template + returnType GET_OBJECT_FROM_ID(NetworkID x) { + NetworkIDObject *nio = GET_BASE_OBJECT_FROM_ID(x); + if (nio==0) + return 0; + if (nio->GetParent()) + return (returnType) nio->GetParent(); + return (returnType) nio; + } + + /// \internal + NetworkIDObject *GET_BASE_OBJECT_FROM_ID(NetworkID x); + + /// \internal + void TrackNetworkIDObject(NetworkIDObject *networkIdObject); + void StopTrackingNetworkIDObject(NetworkIDObject *networkIdObject); + +protected: + friend class NetworkIDObject; + + NetworkIDObject *networkIdHash[NETWORK_ID_MANAGER_HASH_LENGTH]; + unsigned int NetworkIDToHashIndex(NetworkID networkId); + uint64_t startingOffset; + /// \internal + NetworkID GetNewNetworkID(void); + +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/NetworkIDObject.cpp b/project/lib_projects/raknet/jni/RaknetSources/NetworkIDObject.cpp index 89b4209..60a2345 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NetworkIDObject.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/NetworkIDObject.cpp @@ -1,62 +1,62 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "NetworkIDObject.h" -#include "NetworkIDManager.h" -#include "RakAssert.h" -#include "RakAlloca.h" - -using namespace RakNet; - -NetworkIDObject::NetworkIDObject() -{ - networkID=UNASSIGNED_NETWORK_ID; - parent=0; - networkIDManager=0; - nextInstanceForNetworkIDManager=0; -} -NetworkIDObject::~NetworkIDObject() -{ - if (networkID!=UNASSIGNED_NETWORK_ID) - networkIDManager->StopTrackingNetworkIDObject(this); -} -void NetworkIDObject::SetNetworkIDManager( NetworkIDManager *manager) -{ - networkIDManager=manager; -} -NetworkIDManager * NetworkIDObject::GetNetworkIDManager( void ) -{ - return networkIDManager; -} -NetworkID NetworkIDObject::GetNetworkID( void ) -{ - if (networkID==UNASSIGNED_NETWORK_ID) - { - RakAssert(networkIDManager); - networkID = networkIDManager->GetNewNetworkID(); - networkIDManager->TrackNetworkIDObject(this); - } - return networkID; -} -void NetworkIDObject::SetNetworkID( NetworkID id ) -{ - if (id==networkID) - return; - - if (networkID!=UNASSIGNED_NETWORK_ID) - networkIDManager->StopTrackingNetworkIDObject(this); - networkID = id; - networkIDManager->TrackNetworkIDObject(this); -} -void NetworkIDObject::SetParent( void *_parent ) -{ - parent=_parent; -} -void* NetworkIDObject::GetParent( void ) const -{ - return parent; -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "NetworkIDObject.h" +#include "NetworkIDManager.h" +#include "RakAssert.h" +#include "RakAlloca.h" + +using namespace RakNet; + +NetworkIDObject::NetworkIDObject() +{ + networkID=UNASSIGNED_NETWORK_ID; + parent=0; + networkIDManager=0; + nextInstanceForNetworkIDManager=0; +} +NetworkIDObject::~NetworkIDObject() +{ + if (networkID!=UNASSIGNED_NETWORK_ID) + networkIDManager->StopTrackingNetworkIDObject(this); +} +void NetworkIDObject::SetNetworkIDManager( NetworkIDManager *manager) +{ + networkIDManager=manager; +} +NetworkIDManager * NetworkIDObject::GetNetworkIDManager( void ) +{ + return networkIDManager; +} +NetworkID NetworkIDObject::GetNetworkID( void ) +{ + if (networkID==UNASSIGNED_NETWORK_ID) + { + RakAssert(networkIDManager); + networkID = networkIDManager->GetNewNetworkID(); + networkIDManager->TrackNetworkIDObject(this); + } + return networkID; +} +void NetworkIDObject::SetNetworkID( NetworkID id ) +{ + if (id==networkID) + return; + + if (networkID!=UNASSIGNED_NETWORK_ID) + networkIDManager->StopTrackingNetworkIDObject(this); + networkID = id; + networkIDManager->TrackNetworkIDObject(this); +} +void NetworkIDObject::SetParent( void *_parent ) +{ + parent=_parent; +} +void* NetworkIDObject::GetParent( void ) const +{ + return parent; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/NetworkIDObject.h b/project/lib_projects/raknet/jni/RaknetSources/NetworkIDObject.h index b53b702..69aaf34 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/NetworkIDObject.h +++ b/project/lib_projects/raknet/jni/RaknetSources/NetworkIDObject.h @@ -1,81 +1,81 @@ -/// \file -/// \brief A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#if !defined(__NETWORK_ID_GENERATOR) -#define __NETWORK_ID_GENERATOR - -#include "RakNetTypes.h" -#include "RakMemoryOverride.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class NetworkIDManager; - -typedef uint32_t NetworkIDType; - -/// \brief Unique shared ids for each object instance -/// \details A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network. -/// One system should return true for IsNetworkIDAuthority() and the rest should return false. When an object needs to be created, have the the one system create the object. -/// Then have that system send a message to all other systems, and include the value returned from GetNetworkID() in that packet. All other systems should then create the same -/// class of object, and call SetNetworkID() on that class with the NetworkID in the packet. -/// \see the manual for more information on this. -class RAK_DLL_EXPORT NetworkIDObject -{ -public: - // Constructor. NetworkIDs, if IsNetworkIDAuthority() is true, are created here. - NetworkIDObject(); - - // Destructor. Used NetworkIDs, if any, are freed here. - virtual ~NetworkIDObject(); - - /// Sets the manager class from which to request unique network IDs - /// Unlike previous versions, the NetworkIDObject relies on a manager class to provide IDs, rather than using statics, - /// So you can have more than one set of IDs on the same system. - virtual void SetNetworkIDManager( NetworkIDManager *manager); - - /// Returns what was passed to SetNetworkIDManager - virtual NetworkIDManager * GetNetworkIDManager( void ); - - /// Returns the NetworkID that you can use to refer to this object over the network. - /// \pre You must first call SetNetworkIDManager before using this function - /// \retval UNASSIGNED_NETWORK_ID UNASSIGNED_NETWORK_ID is returned IsNetworkIDAuthority() is false and SetNetworkID() was not previously called. This is also returned if you call this function in the constructor. - /// \retval 0-65534 Any other value is a valid NetworkID. NetworkIDs start at 0 and go to 65534, wrapping at that point. - virtual NetworkID GetNetworkID( void ); - - /// Sets the NetworkID for this instance. Usually this is called by the clients and determined from the servers. However, if you save multiplayer games you would likely use - /// This on load as well. - virtual void SetNetworkID( NetworkID id ); - - /// Your class does not have to derive from NetworkIDObject, although that is the easiest way to implement this. - /// If you want this to be a member object of another class, rather than inherit, then call SetParent() with a pointer to the parent class instance. - /// GET_OBJECT_FROM_ID will then return the parent rather than this instance. - virtual void SetParent( void *_parent ); - - /// Return what was passed to SetParent - /// \return The value passed to SetParent, or 0 if it was never called. - virtual void* GetParent( void ) const; - -protected: - /// The network ID of this object - NetworkID networkID; - - NetworkIDManager *networkIDManager; - - /// The parent set by SetParent() - void *parent; - - /// \internal, used by NetworkIDManager - friend class NetworkIDManager; - NetworkIDObject *nextInstanceForNetworkIDManager; -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#if !defined(__NETWORK_ID_GENERATOR) +#define __NETWORK_ID_GENERATOR + +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class NetworkIDManager; + +typedef uint32_t NetworkIDType; + +/// \brief Unique shared ids for each object instance +/// \details A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network. +/// One system should return true for IsNetworkIDAuthority() and the rest should return false. When an object needs to be created, have the the one system create the object. +/// Then have that system send a message to all other systems, and include the value returned from GetNetworkID() in that packet. All other systems should then create the same +/// class of object, and call SetNetworkID() on that class with the NetworkID in the packet. +/// \see the manual for more information on this. +class RAK_DLL_EXPORT NetworkIDObject +{ +public: + // Constructor. NetworkIDs, if IsNetworkIDAuthority() is true, are created here. + NetworkIDObject(); + + // Destructor. Used NetworkIDs, if any, are freed here. + virtual ~NetworkIDObject(); + + /// Sets the manager class from which to request unique network IDs + /// Unlike previous versions, the NetworkIDObject relies on a manager class to provide IDs, rather than using statics, + /// So you can have more than one set of IDs on the same system. + virtual void SetNetworkIDManager( NetworkIDManager *manager); + + /// Returns what was passed to SetNetworkIDManager + virtual NetworkIDManager * GetNetworkIDManager( void ); + + /// Returns the NetworkID that you can use to refer to this object over the network. + /// \pre You must first call SetNetworkIDManager before using this function + /// \retval UNASSIGNED_NETWORK_ID UNASSIGNED_NETWORK_ID is returned IsNetworkIDAuthority() is false and SetNetworkID() was not previously called. This is also returned if you call this function in the constructor. + /// \retval 0-65534 Any other value is a valid NetworkID. NetworkIDs start at 0 and go to 65534, wrapping at that point. + virtual NetworkID GetNetworkID( void ); + + /// Sets the NetworkID for this instance. Usually this is called by the clients and determined from the servers. However, if you save multiplayer games you would likely use + /// This on load as well. + virtual void SetNetworkID( NetworkID id ); + + /// Your class does not have to derive from NetworkIDObject, although that is the easiest way to implement this. + /// If you want this to be a member object of another class, rather than inherit, then call SetParent() with a pointer to the parent class instance. + /// GET_OBJECT_FROM_ID will then return the parent rather than this instance. + virtual void SetParent( void *_parent ); + + /// Return what was passed to SetParent + /// \return The value passed to SetParent, or 0 if it was never called. + virtual void* GetParent( void ) const; + +protected: + /// The network ID of this object + NetworkID networkID; + + NetworkIDManager *networkIDManager; + + /// The parent set by SetParent() + void *parent; + + /// \internal, used by NetworkIDManager + friend class NetworkIDManager; + NetworkIDObject *nextInstanceForNetworkIDManager; +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/PS3Includes.h b/project/lib_projects/raknet/jni/RaknetSources/PS3Includes.h index 5d0e340..ff1a125 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PS3Includes.h +++ b/project/lib_projects/raknet/jni/RaknetSources/PS3Includes.h @@ -1,71 +1,71 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketConsoleLogger.cpp b/project/lib_projects/raknet/jni/RaknetSources/PacketConsoleLogger.cpp index 4d69f99..4788618 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketConsoleLogger.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketConsoleLogger.cpp @@ -1,26 +1,26 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_LogCommandParser==1 && _RAKNET_SUPPORT_PacketLogger==1 -#include "PacketConsoleLogger.h" -#include "LogCommandParser.h" -#include - -using namespace RakNet; - -PacketConsoleLogger::PacketConsoleLogger() -{ - logCommandParser=0; -} - -void PacketConsoleLogger::SetLogCommandParser(LogCommandParser *lcp) -{ - logCommandParser=lcp; - if (logCommandParser) - logCommandParser->AddChannel("PacketConsoleLogger"); -} -void PacketConsoleLogger::WriteLog(const char *str) -{ - if (logCommandParser) - logCommandParser->WriteLog("PacketConsoleLogger", str); -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_LogCommandParser==1 && _RAKNET_SUPPORT_PacketLogger==1 +#include "PacketConsoleLogger.h" +#include "LogCommandParser.h" +#include + +using namespace RakNet; + +PacketConsoleLogger::PacketConsoleLogger() +{ + logCommandParser=0; +} + +void PacketConsoleLogger::SetLogCommandParser(LogCommandParser *lcp) +{ + logCommandParser=lcp; + if (logCommandParser) + logCommandParser->AddChannel("PacketConsoleLogger"); +} +void PacketConsoleLogger::WriteLog(const char *str) +{ + if (logCommandParser) + logCommandParser->WriteLog("PacketConsoleLogger", str); +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketConsoleLogger.h b/project/lib_projects/raknet/jni/RaknetSources/PacketConsoleLogger.h index 28aaef9..62bc001 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketConsoleLogger.h +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketConsoleLogger.h @@ -1,38 +1,38 @@ -/// \file -/// \brief This will write all incoming and outgoing network messages to the log command parser, which can be accessed through Telnet -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_LogCommandParser==1 && _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __PACKET_CONSOLE_LOGGER_H_ -#define __PACKET_CONSOLE_LOGGER_H_ - -#include "PacketLogger.h" - -namespace RakNet -{ -/// Forward declarations -class LogCommandParser; - -/// \ingroup PACKETLOGGER_GROUP -/// \brief Packetlogger that logs to a remote command console -class RAK_DLL_EXPORT PacketConsoleLogger : public PacketLogger -{ -public: - PacketConsoleLogger(); - // Writes to the command parser used for logging, which is accessed through a secondary communication layer (such as Telnet or RakNet) - See ConsoleServer.h - virtual void SetLogCommandParser(LogCommandParser *lcp); - virtual void WriteLog(const char *str); -protected: - LogCommandParser *logCommandParser; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief This will write all incoming and outgoing network messages to the log command parser, which can be accessed through Telnet +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_LogCommandParser==1 && _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __PACKET_CONSOLE_LOGGER_H_ +#define __PACKET_CONSOLE_LOGGER_H_ + +#include "PacketLogger.h" + +namespace RakNet +{ +/// Forward declarations +class LogCommandParser; + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Packetlogger that logs to a remote command console +class RAK_DLL_EXPORT PacketConsoleLogger : public PacketLogger +{ +public: + PacketConsoleLogger(); + // Writes to the command parser used for logging, which is accessed through a secondary communication layer (such as Telnet or RakNet) - See ConsoleServer.h + virtual void SetLogCommandParser(LogCommandParser *lcp); + virtual void WriteLog(const char *str); +protected: + LogCommandParser *logCommandParser; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketFileLogger.cpp b/project/lib_projects/raknet/jni/RaknetSources/PacketFileLogger.cpp index 18631c2..02aabd7 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketFileLogger.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketFileLogger.cpp @@ -1,45 +1,45 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 -#include "PacketFileLogger.h" -#include "GetTime.h" - -using namespace RakNet; - -PacketFileLogger::PacketFileLogger() -{ - packetLogFile=0; -} -PacketFileLogger::~PacketFileLogger() -{ - if (packetLogFile) - { - fflush(packetLogFile); - fclose(packetLogFile); - } -} -void PacketFileLogger::StartLog(const char *filenamePrefix) -{ - // Open file for writing - char filename[256]; - if (filenamePrefix) - sprintf(filename, "%s_%i.csv", filenamePrefix, (int) RakNet::GetTimeMS()); - else - sprintf(filename, "PacketLog_%i.csv", (int) RakNet::GetTimeMS()); - packetLogFile = fopen(filename, "wt"); - LogHeader(); - if (packetLogFile) - { - fflush(packetLogFile); - } -} - -void PacketFileLogger::WriteLog(const char *str) -{ - if (packetLogFile) - { - fprintf(packetLogFile, "%s\n", str); - fflush(packetLogFile); - } -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 +#include "PacketFileLogger.h" +#include "GetTime.h" + +using namespace RakNet; + +PacketFileLogger::PacketFileLogger() +{ + packetLogFile=0; +} +PacketFileLogger::~PacketFileLogger() +{ + if (packetLogFile) + { + fflush(packetLogFile); + fclose(packetLogFile); + } +} +void PacketFileLogger::StartLog(const char *filenamePrefix) +{ + // Open file for writing + char filename[256]; + if (filenamePrefix) + sprintf(filename, "%s_%i.csv", filenamePrefix, (int) RakNet::GetTimeMS()); + else + sprintf(filename, "PacketLog_%i.csv", (int) RakNet::GetTimeMS()); + packetLogFile = fopen(filename, "wt"); + LogHeader(); + if (packetLogFile) + { + fflush(packetLogFile); + } +} + +void PacketFileLogger::WriteLog(const char *str) +{ + if (packetLogFile) + { + fprintf(packetLogFile, "%s\n", str); + fflush(packetLogFile); + } +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketFileLogger.h b/project/lib_projects/raknet/jni/RaknetSources/PacketFileLogger.h index 48f5ce2..1e08f28 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketFileLogger.h +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketFileLogger.h @@ -1,37 +1,37 @@ -/// \file -/// \brief This will write all incoming and outgoing network messages to a file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __PACKET_FILE_LOGGER_H_ -#define __PACKET_FILE_LOGGER_H_ - -#include "PacketLogger.h" -#include - -namespace RakNet -{ - -/// \ingroup PACKETLOGGER_GROUP -/// \brief Packetlogger that outputs to a file -class RAK_DLL_EXPORT PacketFileLogger : public PacketLogger -{ -public: - PacketFileLogger(); - virtual ~PacketFileLogger(); - void StartLog(const char *filenamePrefix); - virtual void WriteLog(const char *str); -protected: - FILE *packetLogFile; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief This will write all incoming and outgoing network messages to a file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __PACKET_FILE_LOGGER_H_ +#define __PACKET_FILE_LOGGER_H_ + +#include "PacketLogger.h" +#include + +namespace RakNet +{ + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Packetlogger that outputs to a file +class RAK_DLL_EXPORT PacketFileLogger : public PacketLogger +{ +public: + PacketFileLogger(); + virtual ~PacketFileLogger(); + void StartLog(const char *filenamePrefix); + virtual void WriteLog(const char *str); +protected: + FILE *packetLogFile; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketLogger.cpp b/project/lib_projects/raknet/jni/RaknetSources/PacketLogger.cpp index 35e8c68..6762629 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketLogger.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketLogger.cpp @@ -1,456 +1,456 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#include "PacketLogger.h" -#include "BitStream.h" -#include "DS_List.h" -#include "InternalPacket.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "StringCompressor.h" -#include "GetTime.h" -#include -#include -#include -#include "Itoa.h" -#include -#include "SocketIncludes.h" -#include "gettimeofday.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(PacketLogger,PacketLogger); - -PacketLogger::PacketLogger() -{ - printId=true; - printAcks=true; - prefix[0]=0; - suffix[0]=0; - logDirectMessages=true; -} -PacketLogger::~PacketLogger() -{ -} -void PacketLogger::FormatLine( -char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame, unsigned char id -, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, -unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex) -{ - char numericID[16]; - const char* idToPrint = NULL; - if(printId) - { - if (splitPacketCount>0 && splitPacketCount!=(unsigned int)-1) - idToPrint="(SPLIT PACKET)"; - else - idToPrint = IDTOString(id); - } - // If printId is false, idToPrint will be NULL, as it will - // in the case of an unrecognized id. Testing printId for false - // would just be redundant. - if(idToPrint == NULL) - { - sprintf(numericID, "%5u", id); - idToPrint = numericID; - } - - FormatLine(into, dir, type, reliableMessageNumber, frame, idToPrint, bitLen, time, local, remote,splitPacketId,splitPacketIndex,splitPacketCount, orderingIndex); -} - -void PacketLogger::FormatLine( -char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame, const char* idToPrint -, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, -unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex) -{ - char str1[64], str2[62]; - local.ToString(true, str1); - remote.ToString(true, str2); - char localtime[128]; - GetLocalTime(localtime); - char str3[64]; - if (reliableMessageNumber==(unsigned int)-1) - { - str3[0]='N'; - str3[1]='/'; - str3[2]='A'; - str3[3]=0; - } - else - { - sprintf(str3,"%5u",reliableMessageNumber); - } - - sprintf(into, "%s,%s%s,%s,%s,%5u,%s,%u,%llu,%s,%s,%i,%i,%i,%i,%s," - , localtime - , prefix - , dir - , type - , str3 - , frame - , idToPrint - , bitLen - , time - , str1 - , str2 - , splitPacketId - , splitPacketIndex - , splitPacketCount - , orderingIndex - , suffix - ); -} -void PacketLogger::OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) -{ - if (logDirectMessages==false) - return; - - char str[256]; - FormatLine(str, "Snd", "Raw", 0, 0, data[0], bitsUsed, RakNet::GetTimeMS(), rakPeerInterface->GetExternalID(remoteSystemAddress), remoteSystemAddress, (unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); - AddToLog(str); -} - -void PacketLogger::LogHeader(void) -{ - // Last 5 are splitpacket id, split packet index, split packet count, ordering index, suffix - AddToLog("Clock,S|R,Typ,Reliable#,Frm #,PktID,BitLn,Time ,Local IP:Port ,RemoteIP:Port,SPID,SPIN,SPCO,OI,Suffix,Miscellaneous\n"); -} -void PacketLogger::OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) -{ - if (logDirectMessages==false) - return; - - char str[256]; - FormatLine(str, "Rcv", "Raw", 0, 0, data[0], bitsUsed, RakNet::GetTime(), rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS), remoteSystemAddress,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); - AddToLog(str); -} -void PacketLogger::OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) -{ - char str[1024]; - FormatLine(str, "RcvErr", errorMessage, 0, 0, "", bitsUsed, RakNet::GetTime(), rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS), remoteSystemAddress,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); - AddToLog(str); -} -void PacketLogger::OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time) -{ - char str[256]; - char str1[64], str2[62]; - SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); - localSystemAddress.ToString(true, str1); - remoteSystemAddress.ToString(true, str2); - char localtime[128]; - GetLocalTime(localtime); - - sprintf(str, "%s,Rcv,Ack,%i,,,,%llu,%s,%s,,,,,," - , localtime - , messageNumber - , (unsigned long long) time - , str1 - , str2 - ); - AddToLog(str); -} -void PacketLogger::OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) -{ - char str[256]; - char str1[64], str2[62]; - SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); - localSystemAddress.ToString(true, str1); - remoteSystemAddress.ToString(true, str2); - RakNet::TimeMS time = RakNet::GetTimeMS(); - char localtime[128]; - GetLocalTime(localtime); - - sprintf(str, "%s,Lcl,PBP,,,%s,%i,%llu,%s,%s,,,,,," - , localtime - , BaseIDTOString(data[0]) - , bitsUsed - , (unsigned long long) time - , str1 - , str2 - ); - AddToLog(str); -} -void PacketLogger::OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend) -{ - char str[256]; - const char *sendTypes[] = - { - "Rcv", - "Snd", - "Err1", - "Err2", - "Err3", - "Err4", - "Err5", - "Err6", - }; - const char *sendType = sendTypes[isSend]; - SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); - - unsigned int reliableMessageNumber; - if (internalPacket->reliability==UNRELIABLE || internalPacket->reliability==UNRELIABLE_SEQUENCED || internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) - reliableMessageNumber=(unsigned int)-1; - else - reliableMessageNumber=internalPacket->reliableMessageNumber; - - if (internalPacket->data[0]==ID_TIMESTAMP) - { - FormatLine(str, sendType, "Tms", reliableMessageNumber, frameNumber, internalPacket->data[1+sizeof(RakNet::Time)], internalPacket->dataBitLength, (unsigned long long)time, localSystemAddress, remoteSystemAddress, internalPacket->splitPacketId, internalPacket->splitPacketIndex, internalPacket->splitPacketCount, internalPacket->orderingIndex); - } - else - { - FormatLine(str, sendType, "Nrm", reliableMessageNumber, frameNumber, internalPacket->data[0], internalPacket->dataBitLength, (unsigned long long)time, localSystemAddress, remoteSystemAddress, internalPacket->splitPacketId, internalPacket->splitPacketIndex, internalPacket->splitPacketCount, internalPacket->orderingIndex); - } - - AddToLog(str); -} -void PacketLogger::AddToLog(const char *str) -{ - WriteLog(str); -} -void PacketLogger::WriteLog(const char *str) -{ - RAKNET_DEBUG_PRINTF("%s\n", str); -} -void PacketLogger::WriteMiscellaneous(const char *type, const char *msg) -{ - char str[1024]; - char str1[64]; - SystemAddress localSystemAddress = rakPeerInterface->GetInternalID(); - localSystemAddress.ToString(true, str1); - RakNet::TimeMS time = RakNet::GetTimeMS(); - char localtime[128]; - GetLocalTime(localtime); - - sprintf(str, "%s,Lcl,%s,,,,,%llu,%s,,,,,,,%s" - , localtime - , type - , (unsigned long long) time - , str1 - , msg - ); - - AddToLog(msg); -} -void PacketLogger::SetPrintID(bool print) -{ - printId=print; -} -void PacketLogger::SetPrintAcks(bool print) -{ - printAcks=print; -} -const char* PacketLogger::BaseIDTOString(unsigned char Id) -{ - if (Id >= ID_USER_PACKET_ENUM) - return 0; - - const char *IDTable[((int)ID_USER_PACKET_ENUM)+1]= - { - "ID_CONNECTED_PING", - "ID_UNCONNECTED_PING", - "ID_UNCONNECTED_PING_OPEN_CONNECTIONS", - "ID_CONNECTED_PONG", - "ID_DETECT_LOST_CONNECTIONS", - "ID_OPEN_CONNECTION_REQUEST_1", - "ID_OPEN_CONNECTION_REPLY_1", - "ID_OPEN_CONNECTION_REQUEST_2", - "ID_OPEN_CONNECTION_REPLY_2", - "ID_CONNECTION_REQUEST", - "ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY", - "ID_OUR_SYSTEM_REQUIRES_SECURITY", - "ID_PUBLIC_KEY_MISMATCH", - "ID_OUT_OF_BAND_INTERNAL", - "ID_SND_RECEIPT_ACKED", - "ID_SND_RECEIPT_LOSS", - "ID_CONNECTION_REQUEST_ACCEPTED", - "ID_CONNECTION_ATTEMPT_FAILED", - "ID_ALREADY_CONNECTED", - "ID_NEW_INCOMING_CONNECTION", - "ID_NO_FREE_INCOMING_CONNECTIONS", - "ID_DISCONNECTION_NOTIFICATION", - "ID_CONNECTION_LOST", - "ID_CONNECTION_BANNED", - "ID_INVALID_PASSWORD", - "ID_INCOMPATIBLE_PROTOCOL_VERSION", - "ID_IP_RECENTLY_CONNECTED", - "ID_TIMESTAMP", - "ID_UNCONNECTED_PONG", - "ID_ADVERTISE_SYSTEM", - "ID_DOWNLOAD_PROGRESS", - "ID_REMOTE_DISCONNECTION_NOTIFICATION", - "ID_REMOTE_CONNECTION_LOST", - "ID_REMOTE_NEW_INCOMING_CONNECTION", - "ID_FILE_LIST_TRANSFER_HEADER", - "ID_FILE_LIST_TRANSFER_FILE", - "ID_FILE_LIST_REFERENCE_PUSH_ACK", - "ID_DDT_DOWNLOAD_REQUEST", - "ID_TRANSPORT_STRING", - "ID_REPLICA_MANAGER_CONSTRUCTION", - "ID_REPLICA_MANAGER_SCOPE_CHANGE", - "ID_REPLICA_MANAGER_SERIALIZE", - "ID_REPLICA_MANAGER_DOWNLOAD_STARTED", - "ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE", - "ID_RAKVOICE_OPEN_CHANNEL_REQUEST", - "ID_RAKVOICE_OPEN_CHANNEL_REPLY", - "ID_RAKVOICE_CLOSE_CHANNEL", - "ID_RAKVOICE_DATA", - "ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE", - "ID_AUTOPATCHER_CREATION_LIST", - "ID_AUTOPATCHER_DELETION_LIST", - "ID_AUTOPATCHER_GET_PATCH", - "ID_AUTOPATCHER_PATCH_LIST", - "ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR", - "ID_AUTOPATCHER_FINISHED_INTERNAL", - "ID_AUTOPATCHER_FINISHED", - "ID_AUTOPATCHER_RESTART_APPLICATION", - "ID_NAT_PUNCHTHROUGH_REQUEST", - "ID_NAT_GROUP_PUNCHTHROUGH_REQUEST", - "ID_NAT_GROUP_PUNCHTHROUGH_REPLY", - "ID_NAT_CONNECT_AT_TIME", - "ID_NAT_GET_MOST_RECENT_PORT", - "ID_NAT_CLIENT_READY", - "ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION", - "ID_NAT_TARGET_NOT_CONNECTED", - "ID_NAT_TARGET_UNRESPONSIVE", - "ID_NAT_CONNECTION_TO_TARGET_LOST", - "ID_NAT_ALREADY_IN_PROGRESS", - "ID_NAT_PUNCHTHROUGH_FAILED", - "ID_NAT_PUNCHTHROUGH_SUCCEEDED", - "ID_NAT_GROUP_PUNCH_FAILED", - "ID_NAT_GROUP_PUNCH_SUCCEEDED", - "ID_READY_EVENT_SET", - "ID_READY_EVENT_UNSET", - "ID_READY_EVENT_ALL_SET", - "ID_READY_EVENT_QUERY", - "ID_LOBBY_GENERAL", - "ID_RPC_REMOTE_ERROR", - "ID_RPC_PLUGIN", - "ID_FILE_LIST_REFERENCE_PUSH", - "ID_READY_EVENT_FORCE_ALL_SET", - "ID_ROOMS_EXECUTE_FUNC", - "ID_ROOMS_LOGON_STATUS", - "ID_ROOMS_HANDLE_CHANGE", - "ID_LOBBY2_SEND_MESSAGE", - "ID_LOBBY2_SERVER_ERROR", - "ID_FCM2_NEW_HOST", - "ID_FCM2_REQUEST_FCMGUID", - "ID_FCM2_RESPOND_CONNECTION_COUNT", - "ID_FCM2_INFORM_FCMGUID", - "ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT", - "ID_UDP_PROXY_GENERAL", - "ID_SQLite3_EXEC", - "ID_SQLite3_UNKNOWN_DB", - "ID_SQLLITE_LOGGER", - "ID_NAT_TYPE_DETECTION_REQUEST", - "ID_NAT_TYPE_DETECTION_RESULT", - "ID_ROUTER_2_INTERNAL", - "ID_ROUTER_2_FORWARDING_NO_PATH", - "ID_ROUTER_2_FORWARDING_ESTABLISHED", - "ID_ROUTER_2_REROUTED", - "ID_TEAM_BALANCER_INTERNAL", - "ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING", - "ID_TEAM_BALANCER_TEAMS_LOCKED", - "ID_TEAM_BALANCER_TEAM_ASSIGNED", - "ID_LIGHTSPEED_INTEGRATION", - "ID_XBOX_LOBBY", - "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS", - "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS", - "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE", - "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE", - "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT", - "ID_TWO_WAY_AUTHENTICATION_NEGOTIATION", - "ID_CLOUD_POST_REQUEST", - "ID_CLOUD_RELEASE_REQUEST", - "ID_CLOUD_GET_REQUEST", - "ID_CLOUD_GET_RESPONSE", - "ID_CLOUD_UNSUBSCRIBE_REQUEST", - "ID_CLOUD_SERVER_TO_SERVER_COMMAND", - "ID_CLOUD_SUBSCRIPTION_NOTIFICATION", - "ID_RESERVED_1", - "ID_RESERVED_2", - "ID_RESERVED_3", - "ID_RESERVED_4", - "ID_RESERVED_5", - "ID_RESERVED_6", - "ID_RESERVED_7", - "ID_RESERVED_8", - "ID_RESERVED_9", - "ID_USER_PACKET_ENUM", - }; - - return (char*)IDTable[Id]; -} -const char* PacketLogger::UserIDTOString(unsigned char Id) -{ - // Users should override this - static char str[256]; - Itoa(Id, str, 10); - return (const char*) str; -} -const char* PacketLogger::IDTOString(unsigned char Id) -{ - const char *out; - out=BaseIDTOString(Id); - if (out) - return out; - return UserIDTOString(Id); -} -void PacketLogger::SetPrefix(const char *_prefix) -{ - strncpy(prefix, _prefix, 255); - prefix[255]=0; -} -void PacketLogger::SetSuffix(const char *_suffix) -{ - strncpy(suffix, _suffix, 255); - suffix[255]=0; -} -void PacketLogger::GetLocalTime(char buffer[128]) -{ -#if defined(_WIN32) && !defined(__GNUC__) && !defined(__GCCXML__) - time_t rawtime; - struct timeval tv; - // If you get an arror about an incomplete type, just delete this file - struct timezone tz; - gettimeofday(&tv, &tz); - // time ( &rawtime ); - rawtime=tv.tv_sec; - - struct tm * timeinfo; - timeinfo = localtime ( &rawtime ); - strftime (buffer,128,"%x %X",timeinfo); - char buff[32]; - sprintf(buff, ".%i", tv.tv_usec); - strcat(buffer,buff); - - // Commented version puts the time first - /* - struct tm * timeinfo; - timeinfo = localtime ( &rawtime ); - strftime (buffer,128,"%X",timeinfo); - char buff[32]; - sprintf(buff, ".%i ", tv.tv_usec); - strcat(buffer,buff); - char buff2[32]; - strftime (buff2,32,"%x",timeinfo); - strcat(buffer,buff2); - */ -#else - buffer[0]=0; -#endif -} -void PacketLogger::SetLogDirectMessages(bool send) -{ - logDirectMessages=send; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#include "PacketLogger.h" +#include "BitStream.h" +#include "DS_List.h" +#include "InternalPacket.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "StringCompressor.h" +#include "GetTime.h" +#include +#include +#include +#include "Itoa.h" +#include +#include "SocketIncludes.h" +#include "gettimeofday.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(PacketLogger,PacketLogger); + +PacketLogger::PacketLogger() +{ + printId=true; + printAcks=true; + prefix[0]=0; + suffix[0]=0; + logDirectMessages=true; +} +PacketLogger::~PacketLogger() +{ +} +void PacketLogger::FormatLine( +char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame, unsigned char id +, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, +unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex) +{ + char numericID[16]; + const char* idToPrint = NULL; + if(printId) + { + if (splitPacketCount>0 && splitPacketCount!=(unsigned int)-1) + idToPrint="(SPLIT PACKET)"; + else + idToPrint = IDTOString(id); + } + // If printId is false, idToPrint will be NULL, as it will + // in the case of an unrecognized id. Testing printId for false + // would just be redundant. + if(idToPrint == NULL) + { + sprintf(numericID, "%5u", id); + idToPrint = numericID; + } + + FormatLine(into, dir, type, reliableMessageNumber, frame, idToPrint, bitLen, time, local, remote,splitPacketId,splitPacketIndex,splitPacketCount, orderingIndex); +} + +void PacketLogger::FormatLine( +char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame, const char* idToPrint +, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, +unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex) +{ + char str1[64], str2[62]; + local.ToString(true, str1); + remote.ToString(true, str2); + char localtime[128]; + GetLocalTime(localtime); + char str3[64]; + if (reliableMessageNumber==(unsigned int)-1) + { + str3[0]='N'; + str3[1]='/'; + str3[2]='A'; + str3[3]=0; + } + else + { + sprintf(str3,"%5u",reliableMessageNumber); + } + + sprintf(into, "%s,%s%s,%s,%s,%5u,%s,%u,%llu,%s,%s,%i,%i,%i,%i,%s," + , localtime + , prefix + , dir + , type + , str3 + , frame + , idToPrint + , bitLen + , time + , str1 + , str2 + , splitPacketId + , splitPacketIndex + , splitPacketCount + , orderingIndex + , suffix + ); +} +void PacketLogger::OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) +{ + if (logDirectMessages==false) + return; + + char str[256]; + FormatLine(str, "Snd", "Raw", 0, 0, data[0], bitsUsed, RakNet::GetTimeMS(), rakPeerInterface->GetExternalID(remoteSystemAddress), remoteSystemAddress, (unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); + AddToLog(str); +} + +void PacketLogger::LogHeader(void) +{ + // Last 5 are splitpacket id, split packet index, split packet count, ordering index, suffix + AddToLog("Clock,S|R,Typ,Reliable#,Frm #,PktID,BitLn,Time ,Local IP:Port ,RemoteIP:Port,SPID,SPIN,SPCO,OI,Suffix,Miscellaneous\n"); +} +void PacketLogger::OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) +{ + if (logDirectMessages==false) + return; + + char str[256]; + FormatLine(str, "Rcv", "Raw", 0, 0, data[0], bitsUsed, RakNet::GetTime(), rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS), remoteSystemAddress,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); + AddToLog(str); +} +void PacketLogger::OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) +{ + char str[1024]; + FormatLine(str, "RcvErr", errorMessage, 0, 0, "", bitsUsed, RakNet::GetTime(), rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS), remoteSystemAddress,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); + AddToLog(str); +} +void PacketLogger::OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time) +{ + char str[256]; + char str1[64], str2[62]; + SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); + localSystemAddress.ToString(true, str1); + remoteSystemAddress.ToString(true, str2); + char localtime[128]; + GetLocalTime(localtime); + + sprintf(str, "%s,Rcv,Ack,%i,,,,%llu,%s,%s,,,,,," + , localtime + , messageNumber + , (unsigned long long) time + , str1 + , str2 + ); + AddToLog(str); +} +void PacketLogger::OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) +{ + char str[256]; + char str1[64], str2[62]; + SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); + localSystemAddress.ToString(true, str1); + remoteSystemAddress.ToString(true, str2); + RakNet::TimeMS time = RakNet::GetTimeMS(); + char localtime[128]; + GetLocalTime(localtime); + + sprintf(str, "%s,Lcl,PBP,,,%s,%i,%llu,%s,%s,,,,,," + , localtime + , BaseIDTOString(data[0]) + , bitsUsed + , (unsigned long long) time + , str1 + , str2 + ); + AddToLog(str); +} +void PacketLogger::OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend) +{ + char str[256]; + const char *sendTypes[] = + { + "Rcv", + "Snd", + "Err1", + "Err2", + "Err3", + "Err4", + "Err5", + "Err6", + }; + const char *sendType = sendTypes[isSend]; + SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); + + unsigned int reliableMessageNumber; + if (internalPacket->reliability==UNRELIABLE || internalPacket->reliability==UNRELIABLE_SEQUENCED || internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) + reliableMessageNumber=(unsigned int)-1; + else + reliableMessageNumber=internalPacket->reliableMessageNumber; + + if (internalPacket->data[0]==ID_TIMESTAMP) + { + FormatLine(str, sendType, "Tms", reliableMessageNumber, frameNumber, internalPacket->data[1+sizeof(RakNet::Time)], internalPacket->dataBitLength, (unsigned long long)time, localSystemAddress, remoteSystemAddress, internalPacket->splitPacketId, internalPacket->splitPacketIndex, internalPacket->splitPacketCount, internalPacket->orderingIndex); + } + else + { + FormatLine(str, sendType, "Nrm", reliableMessageNumber, frameNumber, internalPacket->data[0], internalPacket->dataBitLength, (unsigned long long)time, localSystemAddress, remoteSystemAddress, internalPacket->splitPacketId, internalPacket->splitPacketIndex, internalPacket->splitPacketCount, internalPacket->orderingIndex); + } + + AddToLog(str); +} +void PacketLogger::AddToLog(const char *str) +{ + WriteLog(str); +} +void PacketLogger::WriteLog(const char *str) +{ + RAKNET_DEBUG_PRINTF("%s\n", str); +} +void PacketLogger::WriteMiscellaneous(const char *type, const char *msg) +{ + char str[1024]; + char str1[64]; + SystemAddress localSystemAddress = rakPeerInterface->GetInternalID(); + localSystemAddress.ToString(true, str1); + RakNet::TimeMS time = RakNet::GetTimeMS(); + char localtime[128]; + GetLocalTime(localtime); + + sprintf(str, "%s,Lcl,%s,,,,,%llu,%s,,,,,,,%s" + , localtime + , type + , (unsigned long long) time + , str1 + , msg + ); + + AddToLog(msg); +} +void PacketLogger::SetPrintID(bool print) +{ + printId=print; +} +void PacketLogger::SetPrintAcks(bool print) +{ + printAcks=print; +} +const char* PacketLogger::BaseIDTOString(unsigned char Id) +{ + if (Id >= ID_USER_PACKET_ENUM) + return 0; + + const char *IDTable[((int)ID_USER_PACKET_ENUM)+1]= + { + "ID_CONNECTED_PING", + "ID_UNCONNECTED_PING", + "ID_UNCONNECTED_PING_OPEN_CONNECTIONS", + "ID_CONNECTED_PONG", + "ID_DETECT_LOST_CONNECTIONS", + "ID_OPEN_CONNECTION_REQUEST_1", + "ID_OPEN_CONNECTION_REPLY_1", + "ID_OPEN_CONNECTION_REQUEST_2", + "ID_OPEN_CONNECTION_REPLY_2", + "ID_CONNECTION_REQUEST", + "ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY", + "ID_OUR_SYSTEM_REQUIRES_SECURITY", + "ID_PUBLIC_KEY_MISMATCH", + "ID_OUT_OF_BAND_INTERNAL", + "ID_SND_RECEIPT_ACKED", + "ID_SND_RECEIPT_LOSS", + "ID_CONNECTION_REQUEST_ACCEPTED", + "ID_CONNECTION_ATTEMPT_FAILED", + "ID_ALREADY_CONNECTED", + "ID_NEW_INCOMING_CONNECTION", + "ID_NO_FREE_INCOMING_CONNECTIONS", + "ID_DISCONNECTION_NOTIFICATION", + "ID_CONNECTION_LOST", + "ID_CONNECTION_BANNED", + "ID_INVALID_PASSWORD", + "ID_INCOMPATIBLE_PROTOCOL_VERSION", + "ID_IP_RECENTLY_CONNECTED", + "ID_TIMESTAMP", + "ID_UNCONNECTED_PONG", + "ID_ADVERTISE_SYSTEM", + "ID_DOWNLOAD_PROGRESS", + "ID_REMOTE_DISCONNECTION_NOTIFICATION", + "ID_REMOTE_CONNECTION_LOST", + "ID_REMOTE_NEW_INCOMING_CONNECTION", + "ID_FILE_LIST_TRANSFER_HEADER", + "ID_FILE_LIST_TRANSFER_FILE", + "ID_FILE_LIST_REFERENCE_PUSH_ACK", + "ID_DDT_DOWNLOAD_REQUEST", + "ID_TRANSPORT_STRING", + "ID_REPLICA_MANAGER_CONSTRUCTION", + "ID_REPLICA_MANAGER_SCOPE_CHANGE", + "ID_REPLICA_MANAGER_SERIALIZE", + "ID_REPLICA_MANAGER_DOWNLOAD_STARTED", + "ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE", + "ID_RAKVOICE_OPEN_CHANNEL_REQUEST", + "ID_RAKVOICE_OPEN_CHANNEL_REPLY", + "ID_RAKVOICE_CLOSE_CHANNEL", + "ID_RAKVOICE_DATA", + "ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE", + "ID_AUTOPATCHER_CREATION_LIST", + "ID_AUTOPATCHER_DELETION_LIST", + "ID_AUTOPATCHER_GET_PATCH", + "ID_AUTOPATCHER_PATCH_LIST", + "ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR", + "ID_AUTOPATCHER_FINISHED_INTERNAL", + "ID_AUTOPATCHER_FINISHED", + "ID_AUTOPATCHER_RESTART_APPLICATION", + "ID_NAT_PUNCHTHROUGH_REQUEST", + "ID_NAT_GROUP_PUNCHTHROUGH_REQUEST", + "ID_NAT_GROUP_PUNCHTHROUGH_REPLY", + "ID_NAT_CONNECT_AT_TIME", + "ID_NAT_GET_MOST_RECENT_PORT", + "ID_NAT_CLIENT_READY", + "ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION", + "ID_NAT_TARGET_NOT_CONNECTED", + "ID_NAT_TARGET_UNRESPONSIVE", + "ID_NAT_CONNECTION_TO_TARGET_LOST", + "ID_NAT_ALREADY_IN_PROGRESS", + "ID_NAT_PUNCHTHROUGH_FAILED", + "ID_NAT_PUNCHTHROUGH_SUCCEEDED", + "ID_NAT_GROUP_PUNCH_FAILED", + "ID_NAT_GROUP_PUNCH_SUCCEEDED", + "ID_READY_EVENT_SET", + "ID_READY_EVENT_UNSET", + "ID_READY_EVENT_ALL_SET", + "ID_READY_EVENT_QUERY", + "ID_LOBBY_GENERAL", + "ID_RPC_REMOTE_ERROR", + "ID_RPC_PLUGIN", + "ID_FILE_LIST_REFERENCE_PUSH", + "ID_READY_EVENT_FORCE_ALL_SET", + "ID_ROOMS_EXECUTE_FUNC", + "ID_ROOMS_LOGON_STATUS", + "ID_ROOMS_HANDLE_CHANGE", + "ID_LOBBY2_SEND_MESSAGE", + "ID_LOBBY2_SERVER_ERROR", + "ID_FCM2_NEW_HOST", + "ID_FCM2_REQUEST_FCMGUID", + "ID_FCM2_RESPOND_CONNECTION_COUNT", + "ID_FCM2_INFORM_FCMGUID", + "ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT", + "ID_UDP_PROXY_GENERAL", + "ID_SQLite3_EXEC", + "ID_SQLite3_UNKNOWN_DB", + "ID_SQLLITE_LOGGER", + "ID_NAT_TYPE_DETECTION_REQUEST", + "ID_NAT_TYPE_DETECTION_RESULT", + "ID_ROUTER_2_INTERNAL", + "ID_ROUTER_2_FORWARDING_NO_PATH", + "ID_ROUTER_2_FORWARDING_ESTABLISHED", + "ID_ROUTER_2_REROUTED", + "ID_TEAM_BALANCER_INTERNAL", + "ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING", + "ID_TEAM_BALANCER_TEAMS_LOCKED", + "ID_TEAM_BALANCER_TEAM_ASSIGNED", + "ID_LIGHTSPEED_INTEGRATION", + "ID_XBOX_LOBBY", + "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS", + "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS", + "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE", + "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE", + "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT", + "ID_TWO_WAY_AUTHENTICATION_NEGOTIATION", + "ID_CLOUD_POST_REQUEST", + "ID_CLOUD_RELEASE_REQUEST", + "ID_CLOUD_GET_REQUEST", + "ID_CLOUD_GET_RESPONSE", + "ID_CLOUD_UNSUBSCRIBE_REQUEST", + "ID_CLOUD_SERVER_TO_SERVER_COMMAND", + "ID_CLOUD_SUBSCRIPTION_NOTIFICATION", + "ID_RESERVED_1", + "ID_RESERVED_2", + "ID_RESERVED_3", + "ID_RESERVED_4", + "ID_RESERVED_5", + "ID_RESERVED_6", + "ID_RESERVED_7", + "ID_RESERVED_8", + "ID_RESERVED_9", + "ID_USER_PACKET_ENUM", + }; + + return (char*)IDTable[Id]; +} +const char* PacketLogger::UserIDTOString(unsigned char Id) +{ + // Users should override this + static char str[256]; + Itoa(Id, str, 10); + return (const char*) str; +} +const char* PacketLogger::IDTOString(unsigned char Id) +{ + const char *out; + out=BaseIDTOString(Id); + if (out) + return out; + return UserIDTOString(Id); +} +void PacketLogger::SetPrefix(const char *_prefix) +{ + strncpy(prefix, _prefix, 255); + prefix[255]=0; +} +void PacketLogger::SetSuffix(const char *_suffix) +{ + strncpy(suffix, _suffix, 255); + suffix[255]=0; +} +void PacketLogger::GetLocalTime(char buffer[128]) +{ +#if defined(_WIN32) && !defined(__GNUC__) && !defined(__GCCXML__) + time_t rawtime; + struct timeval tv; + // If you get an arror about an incomplete type, just delete this file + struct timezone tz; + gettimeofday(&tv, &tz); + // time ( &rawtime ); + rawtime=tv.tv_sec; + + struct tm * timeinfo; + timeinfo = localtime ( &rawtime ); + strftime (buffer,128,"%x %X",timeinfo); + char buff[32]; + sprintf(buff, ".%i", tv.tv_usec); + strcat(buffer,buff); + + // Commented version puts the time first + /* + struct tm * timeinfo; + timeinfo = localtime ( &rawtime ); + strftime (buffer,128,"%X",timeinfo); + char buff[32]; + sprintf(buff, ".%i ", tv.tv_usec); + strcat(buffer,buff); + char buff2[32]; + strftime (buff2,32,"%x",timeinfo); + strcat(buffer,buff2); + */ +#else + buffer[0]=0; +#endif +} +void PacketLogger::SetLogDirectMessages(bool send) +{ + logDirectMessages=send; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketLogger.h b/project/lib_projects/raknet/jni/RaknetSources/PacketLogger.h index 740620f..3ad9988 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketLogger.h +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketLogger.h @@ -1,101 +1,101 @@ -/// \file -/// \brief This will write all incoming and outgoing network messages to the local console screen. See derived functions for other outputs -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __PACKET_LOGGER_H -#define __PACKET_LOGGER_H - -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \defgroup PACKETLOGGER_GROUP PacketLogger -/// \brief Print out incoming messages to a target destination -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief Writes incoming and outgoing messages to the screen. -/// This will write all incoming and outgoing messages to the console window, or to a file if you override it and give it this functionality. -/// \ingroup PACKETLOGGER_GROUP -class RAK_DLL_EXPORT PacketLogger : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(PacketLogger) - - PacketLogger(); - virtual ~PacketLogger(); - - // Translate the supplied parameters into an output line - overloaded version that takes a MessageIdentifier - // and translates it into a string (numeric or textual representation based on printId); this calls the - // second version which takes a const char* argument for the messageIdentifier - virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame - , unsigned char messageIdentifier, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, - unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); - virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame - , const char* idToPrint, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, - unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); - - /// Events on low level sends and receives. These functions may be called from different threads at the same time. - virtual void OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); - virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); - virtual void OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); - virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend); - virtual void OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time); - virtual void OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); - - /// Logs out a header for all the data - virtual void LogHeader(void); - - /// Override this to log strings to wherever. Log should be threadsafe - virtual void WriteLog(const char *str); - - // Write informational messages - virtual void WriteMiscellaneous(const char *type, const char *msg); - - - // Set to true to print ID_* instead of numbers - virtual void SetPrintID(bool print); - // Print or hide acks (clears up the screen not to print them but is worse for debugging) - virtual void SetPrintAcks(bool print); - - /// Prepend this string to output logs. - virtual void SetPrefix(const char *_prefix); - - /// Append this string to output logs. (newline is useful here) - virtual void SetSuffix(const char *_suffix); - static const char* BaseIDTOString(unsigned char Id); - - /// Log the direct sends and receives or not. Default true - void SetLogDirectMessages(bool send); -protected: - - virtual bool UsesReliabilityLayer(void) const {return true;} - const char* IDTOString(unsigned char Id); - virtual void AddToLog(const char *str); - // Users should override this - virtual const char* UserIDTOString(unsigned char Id); - void GetLocalTime(char buffer[128]); - bool logDirectMessages; - - bool printId, printAcks; - char prefix[256]; - char suffix[256]; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief This will write all incoming and outgoing network messages to the local console screen. See derived functions for other outputs +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __PACKET_LOGGER_H +#define __PACKET_LOGGER_H + +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \defgroup PACKETLOGGER_GROUP PacketLogger +/// \brief Print out incoming messages to a target destination +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief Writes incoming and outgoing messages to the screen. +/// This will write all incoming and outgoing messages to the console window, or to a file if you override it and give it this functionality. +/// \ingroup PACKETLOGGER_GROUP +class RAK_DLL_EXPORT PacketLogger : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(PacketLogger) + + PacketLogger(); + virtual ~PacketLogger(); + + // Translate the supplied parameters into an output line - overloaded version that takes a MessageIdentifier + // and translates it into a string (numeric or textual representation based on printId); this calls the + // second version which takes a const char* argument for the messageIdentifier + virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame + , unsigned char messageIdentifier, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, + unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); + virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame + , const char* idToPrint, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, + unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); + + /// Events on low level sends and receives. These functions may be called from different threads at the same time. + virtual void OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + virtual void OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend); + virtual void OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time); + virtual void OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + + /// Logs out a header for all the data + virtual void LogHeader(void); + + /// Override this to log strings to wherever. Log should be threadsafe + virtual void WriteLog(const char *str); + + // Write informational messages + virtual void WriteMiscellaneous(const char *type, const char *msg); + + + // Set to true to print ID_* instead of numbers + virtual void SetPrintID(bool print); + // Print or hide acks (clears up the screen not to print them but is worse for debugging) + virtual void SetPrintAcks(bool print); + + /// Prepend this string to output logs. + virtual void SetPrefix(const char *_prefix); + + /// Append this string to output logs. (newline is useful here) + virtual void SetSuffix(const char *_suffix); + static const char* BaseIDTOString(unsigned char Id); + + /// Log the direct sends and receives or not. Default true + void SetLogDirectMessages(bool send); +protected: + + virtual bool UsesReliabilityLayer(void) const {return true;} + const char* IDTOString(unsigned char Id); + virtual void AddToLog(const char *str); + // Users should override this + virtual const char* UserIDTOString(unsigned char Id); + void GetLocalTime(char buffer[128]); + bool logDirectMessages; + + bool printId, printAcks; + char prefix[256]; + char suffix[256]; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketOutputWindowLogger.cpp b/project/lib_projects/raknet/jni/RaknetSources/PacketOutputWindowLogger.cpp index b5ffb5f..7f2d3aa 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketOutputWindowLogger.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketOutputWindowLogger.cpp @@ -1,39 +1,39 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#if defined(UNICODE) -#include "RakWString.h" -#endif - -#include "PacketOutputWindowLogger.h" -#include "RakString.h" -#if defined(_WIN32) && !defined(X360__) -#include "WindowsIncludes.h" -#endif - -using namespace RakNet; - -PacketOutputWindowLogger::PacketOutputWindowLogger() -{ -} -PacketOutputWindowLogger::~PacketOutputWindowLogger() -{ -} -void PacketOutputWindowLogger::WriteLog(const char *str) -{ -#if defined(_WIN32) && !defined(X360__) - - #if defined(UNICODE) - RakNet::RakWString str2 = str; - str2+="\n"; - OutputDebugString(str2.C_String()); - #else - RakNet::RakString str2 = str; - str2+="\n"; - OutputDebugString(str2.C_String()); - #endif - -#endif -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#if defined(UNICODE) +#include "RakWString.h" +#endif + +#include "PacketOutputWindowLogger.h" +#include "RakString.h" +#if defined(_WIN32) && !defined(X360__) +#include "WindowsIncludes.h" +#endif + +using namespace RakNet; + +PacketOutputWindowLogger::PacketOutputWindowLogger() +{ +} +PacketOutputWindowLogger::~PacketOutputWindowLogger() +{ +} +void PacketOutputWindowLogger::WriteLog(const char *str) +{ +#if defined(_WIN32) && !defined(X360__) + + #if defined(UNICODE) + RakNet::RakWString str2 = str; + str2+="\n"; + OutputDebugString(str2.C_String()); + #else + RakNet::RakString str2 = str; + str2+="\n"; + OutputDebugString(str2.C_String()); + #endif + +#endif +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketOutputWindowLogger.h b/project/lib_projects/raknet/jni/RaknetSources/PacketOutputWindowLogger.h index f2655a0..a1dd760 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketOutputWindowLogger.h +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketOutputWindowLogger.h @@ -1,34 +1,34 @@ -/// \file -/// \brief This will write all incoming and outgoing network messages to a file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __PACKET_OUTPUT_WINDOW_LOGGER_H_ -#define __PACKET_OUTPUT_WINDOW_LOGGER_H_ - -#include "PacketLogger.h" - -namespace RakNet -{ - -/// \ingroup PACKETLOGGER_GROUP -/// \brief Packetlogger that outputs to the output window in the debugger. Windows only. -class RAK_DLL_EXPORT PacketOutputWindowLogger : public PacketLogger -{ -public: - PacketOutputWindowLogger(); - virtual ~PacketOutputWindowLogger(); - virtual void WriteLog(const char *str); -protected: -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief This will write all incoming and outgoing network messages to a file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __PACKET_OUTPUT_WINDOW_LOGGER_H_ +#define __PACKET_OUTPUT_WINDOW_LOGGER_H_ + +#include "PacketLogger.h" + +namespace RakNet +{ + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Packetlogger that outputs to the output window in the debugger. Windows only. +class RAK_DLL_EXPORT PacketOutputWindowLogger : public PacketLogger +{ +public: + PacketOutputWindowLogger(); + virtual ~PacketOutputWindowLogger(); + virtual void WriteLog(const char *str); +protected: +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketPriority.h b/project/lib_projects/raknet/jni/RaknetSources/PacketPriority.h index 83bcd57..ff84dab 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketPriority.h +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketPriority.h @@ -1,79 +1,79 @@ -/// \file -/// \brief This file contains enumerations for packet priority and reliability enumerations. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __PACKET_PRIORITY_H -#define __PACKET_PRIORITY_H - -/// These enumerations are used to describe when packets are delivered. -enum PacketPriority -{ - /// The highest possible priority. These message trigger sends immediately, and are generally not buffered or aggregated into a single datagram. - IMMEDIATE_PRIORITY, - - /// For every 2 IMMEDIATE_PRIORITY messages, 1 HIGH_PRIORITY will be sent. - /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. - HIGH_PRIORITY, - - /// For every 2 HIGH_PRIORITY messages, 1 MEDIUM_PRIORITY will be sent. - /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. - MEDIUM_PRIORITY, - - /// For every 2 MEDIUM_PRIORITY messages, 1 LOW_PRIORITY will be sent. - /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. - LOW_PRIORITY, - - /// \internal - NUMBER_OF_PRIORITIES -}; - -/// These enumerations are used to describe how packets are delivered. -/// \note Note to self: I write this with 3 bits in the stream. If I add more remember to change that -/// \note In ReliabilityLayer::WriteToBitStreamFromInternalPacket I assume there are 5 major types -/// \note Do not reorder, I check on >= UNRELIABLE_WITH_ACK_RECEIPT -enum PacketReliability -{ - /// Same as regular UDP, except that it will also discard duplicate datagrams. RakNet adds (6 to 17) + 21 bits of overhead, 16 of which is used to detect duplicate packets and 6 to 17 of which is used for message length. - UNRELIABLE, - - /// Regular UDP with a sequence counter. Out of order messages will be discarded. - /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. - UNRELIABLE_SEQUENCED, - - /// The message is sent reliably, but not necessarily in any order. Same overhead as UNRELIABLE. - RELIABLE, - - /// This message is reliable and will arrive in the order you sent it. Messages will be delayed while waiting for out of order messages. Same overhead as UNRELIABLE_SEQUENCED. - /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. - RELIABLE_ORDERED, - - /// This message is reliable and will arrive in the sequence you sent it. Out or order messages will be dropped. Same overhead as UNRELIABLE_SEQUENCED. - /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. - RELIABLE_SEQUENCED, - - /// Same as UNRELIABLE, however the user will get either ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS based on the result of sending this message when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. - UNRELIABLE_WITH_ACK_RECEIPT, - - /// Same as UNRELIABLE_SEQUENCED, however the user will get either ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS based on the result of sending this message when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. - /// 05/04/10 You can't have sequenced and ack receipts, because you don't know if the other system discarded the message, meaning you don't know if the message was processed - // UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT, - - /// Same as RELIABLE. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). ID_SND_RECEIPT_ACKED is returned when the message arrives, not necessarily the order when it was sent. Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. This does not return ID_SND_RECEIPT_LOSS. - RELIABLE_WITH_ACK_RECEIPT, - - /// Same as RELIABLE_ORDERED_ACK_RECEIPT. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). ID_SND_RECEIPT_ACKED is returned when the message arrives, not necessarily the order when it was sent. Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. This does not return ID_SND_RECEIPT_LOSS. - RELIABLE_ORDERED_WITH_ACK_RECEIPT, - - /// Same as RELIABLE_SEQUENCED. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. - /// 05/04/10 You can't have sequenced and ack receipts, because you don't know if the other system discarded the message, meaning you don't know if the message was processed - // RELIABLE_SEQUENCED_WITH_ACK_RECEIPT, - - /// \internal - NUMBER_OF_RELIABILITIES -}; - -#endif +/// \file +/// \brief This file contains enumerations for packet priority and reliability enumerations. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __PACKET_PRIORITY_H +#define __PACKET_PRIORITY_H + +/// These enumerations are used to describe when packets are delivered. +enum PacketPriority +{ + /// The highest possible priority. These message trigger sends immediately, and are generally not buffered or aggregated into a single datagram. + IMMEDIATE_PRIORITY, + + /// For every 2 IMMEDIATE_PRIORITY messages, 1 HIGH_PRIORITY will be sent. + /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. + HIGH_PRIORITY, + + /// For every 2 HIGH_PRIORITY messages, 1 MEDIUM_PRIORITY will be sent. + /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. + MEDIUM_PRIORITY, + + /// For every 2 MEDIUM_PRIORITY messages, 1 LOW_PRIORITY will be sent. + /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. + LOW_PRIORITY, + + /// \internal + NUMBER_OF_PRIORITIES +}; + +/// These enumerations are used to describe how packets are delivered. +/// \note Note to self: I write this with 3 bits in the stream. If I add more remember to change that +/// \note In ReliabilityLayer::WriteToBitStreamFromInternalPacket I assume there are 5 major types +/// \note Do not reorder, I check on >= UNRELIABLE_WITH_ACK_RECEIPT +enum PacketReliability +{ + /// Same as regular UDP, except that it will also discard duplicate datagrams. RakNet adds (6 to 17) + 21 bits of overhead, 16 of which is used to detect duplicate packets and 6 to 17 of which is used for message length. + UNRELIABLE, + + /// Regular UDP with a sequence counter. Out of order messages will be discarded. + /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. + UNRELIABLE_SEQUENCED, + + /// The message is sent reliably, but not necessarily in any order. Same overhead as UNRELIABLE. + RELIABLE, + + /// This message is reliable and will arrive in the order you sent it. Messages will be delayed while waiting for out of order messages. Same overhead as UNRELIABLE_SEQUENCED. + /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. + RELIABLE_ORDERED, + + /// This message is reliable and will arrive in the sequence you sent it. Out or order messages will be dropped. Same overhead as UNRELIABLE_SEQUENCED. + /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. + RELIABLE_SEQUENCED, + + /// Same as UNRELIABLE, however the user will get either ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS based on the result of sending this message when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. + UNRELIABLE_WITH_ACK_RECEIPT, + + /// Same as UNRELIABLE_SEQUENCED, however the user will get either ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS based on the result of sending this message when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. + /// 05/04/10 You can't have sequenced and ack receipts, because you don't know if the other system discarded the message, meaning you don't know if the message was processed + // UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT, + + /// Same as RELIABLE. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). ID_SND_RECEIPT_ACKED is returned when the message arrives, not necessarily the order when it was sent. Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. This does not return ID_SND_RECEIPT_LOSS. + RELIABLE_WITH_ACK_RECEIPT, + + /// Same as RELIABLE_ORDERED_ACK_RECEIPT. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). ID_SND_RECEIPT_ACKED is returned when the message arrives, not necessarily the order when it was sent. Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. This does not return ID_SND_RECEIPT_LOSS. + RELIABLE_ORDERED_WITH_ACK_RECEIPT, + + /// Same as RELIABLE_SEQUENCED. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. + /// 05/04/10 You can't have sequenced and ack receipts, because you don't know if the other system discarded the message, meaning you don't know if the message was processed + // RELIABLE_SEQUENCED_WITH_ACK_RECEIPT, + + /// \internal + NUMBER_OF_RELIABILITIES +}; + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketizedTCP.cpp b/project/lib_projects/raknet/jni/RaknetSources/PacketizedTCP.cpp index bffab02..b5f4fe5 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketizedTCP.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketizedTCP.cpp @@ -1,408 +1,408 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "PacketizedTCP.h" -#include "NativeTypes.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakAlloca.h" - -using namespace RakNet; - -typedef uint32_t PTCPHeader; - -STATIC_FACTORY_DEFINITIONS(PacketizedTCP,PacketizedTCP); - -PacketizedTCP::PacketizedTCP() -{ - -} -PacketizedTCP::~PacketizedTCP() -{ - ClearAllConnections(); -} - -bool PacketizedTCP::Start(unsigned short port, unsigned short maxIncomingConnections, int threadPriority, unsigned short socketFamily) -{ - bool success = TCPInterface::Start(port, maxIncomingConnections,0,threadPriority, socketFamily); - if (success) - { - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnRakPeerStartup(); - } - return success; -} - -void PacketizedTCP::Stop(void) -{ - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnRakPeerShutdown(); - for (i=0; i < waitingPackets.Size(); i++) - DeallocatePacket(waitingPackets[i]); - TCPInterface::Stop(); - ClearAllConnections(); -} - -void PacketizedTCP::Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ) -{ - PTCPHeader dataLength; - dataLength=length; -#ifndef __BITSTREAM_NATIVE_END - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytes((unsigned char*) &length,(unsigned char*) &dataLength,sizeof(dataLength)); -#else - dataLength=length; -#endif - - unsigned int lengthsArray[2]; - const char *dataArray[2]; - dataArray[0]=(char*) &dataLength; - dataArray[1]=data; - lengthsArray[0]=sizeof(dataLength); - lengthsArray[1]=length; - TCPInterface::SendList(dataArray,lengthsArray,2,systemAddress,broadcast); -} -bool PacketizedTCP::SendList( const char **data, const int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ) -{ - if (isStarted==false) - return false; - if (data==0) - return false; - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS && broadcast==false) - return false; - PTCPHeader totalLengthOfUserData=0; - int i; - for (i=0; i < numParameters; i++) - { - if (lengths[i]>0) - totalLengthOfUserData+=lengths[i]; - } - if (totalLengthOfUserData==0) - return false; - - PTCPHeader dataLength; -#ifndef __BITSTREAM_NATIVE_END - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytes((unsigned char*) &totalLengthOfUserData,(unsigned char*) &dataLength,sizeof(dataLength)); -#else - dataLength=totalLengthOfUserData; -#endif - - - unsigned int lengthsArray[512]; - const char *dataArray[512]; - dataArray[0]=(char*) &dataLength; - lengthsArray[0]=sizeof(dataLength); - for (int i=0; i < 512 && i < numParameters; i++) - { - dataArray[i+1]=data[i]; - lengthsArray[i+1]=lengths[i]; - } - return TCPInterface::SendList(dataArray,lengthsArray,numParameters+1,systemAddress,broadcast); -} -void PacketizedTCP::PushNotificationsToQueues(void) -{ - SystemAddress sa; - sa = TCPInterface::HasNewIncomingConnection(); - if (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - _newIncomingConnections.Push(sa, _FILE_AND_LINE_ ); - AddToConnectionList(sa); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnNewConnection(sa, UNASSIGNED_RAKNET_GUID, true); - } - - sa = TCPInterface::HasFailedConnectionAttempt(); - if (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - _failedConnectionAttempts.Push(sa, _FILE_AND_LINE_ ); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - { - Packet p; - p.systemAddress=sa; - p.data=0; - p.length=0; - p.bitSize=0; - messageHandlerList[i]->OnFailedConnectionAttempt(&p, FCAR_CONNECTION_ATTEMPT_FAILED); - } - } - - sa = TCPInterface::HasLostConnection(); - if (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - _lostConnections.Push(sa, _FILE_AND_LINE_ ); - RemoveFromConnectionList(sa); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnClosedConnection(sa, UNASSIGNED_RAKNET_GUID, LCR_DISCONNECTION_NOTIFICATION); - } - - sa = TCPInterface::HasCompletedConnectionAttempt(); - if (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - _completedConnectionAttempts.Push(sa, _FILE_AND_LINE_ ); - AddToConnectionList(sa); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnNewConnection(sa, UNASSIGNED_RAKNET_GUID, true); - } -} -Packet* PacketizedTCP::Receive( void ) -{ - PushNotificationsToQueues(); - - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->Update(); - - Packet *outgoingPacket=ReturnOutgoingPacket(); - if (outgoingPacket) - return outgoingPacket; - - Packet *incomingPacket; - incomingPacket = TCPInterface::Receive(); - unsigned int index; - - while (incomingPacket) - { - if (connections.Has(incomingPacket->systemAddress)) - index = connections.GetIndexAtKey(incomingPacket->systemAddress); - else - index=(unsigned int) -1; - if ((unsigned int)index==(unsigned int)-1) - { - DeallocatePacket(incomingPacket); - incomingPacket = TCPInterface::Receive(); - continue; - } - - - if (incomingPacket->deleteData==true) - { - // Came from network - SystemAddress systemAddressFromPacket; - if (index < connections.Size()) - { - DataStructures::ByteQueue *bq = connections[index]; - // Buffer data - bq->WriteBytes((const char*) incomingPacket->data,incomingPacket->length, _FILE_AND_LINE_); - systemAddressFromPacket=incomingPacket->systemAddress; - PTCPHeader dataLength; - - // Peek the header to see if a full message is waiting - bq->ReadBytes((char*) &dataLength,sizeof(PTCPHeader),true); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &dataLength,sizeof(dataLength)); - // Header indicates packet length. If enough data is available, read out and return one packet - if (bq->GetBytesWritten()>=dataLength+sizeof(PTCPHeader)) - { - do - { - bq->IncrementReadOffset(sizeof(PTCPHeader)); - outgoingPacket = RakNet::OP_NEW(_FILE_AND_LINE_); - outgoingPacket->length=dataLength; - outgoingPacket->bitSize=BYTES_TO_BITS(dataLength); - outgoingPacket->guid=UNASSIGNED_RAKNET_GUID; - outgoingPacket->systemAddress=systemAddressFromPacket; - outgoingPacket->deleteData=false; // Did not come from the network - outgoingPacket->data=(unsigned char*) rakMalloc_Ex(dataLength, _FILE_AND_LINE_); - if (outgoingPacket->data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - RakNet::OP_DELETE(outgoingPacket,_FILE_AND_LINE_); - return 0; - } - bq->ReadBytes((char*) outgoingPacket->data,dataLength,false); - - waitingPackets.Push(outgoingPacket, _FILE_AND_LINE_ ); - - // Peek the header to see if a full message is waiting - if (bq->ReadBytes((char*) &dataLength,sizeof(PTCPHeader),true)) - { - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &dataLength,sizeof(dataLength)); - } - else - break; - } while (bq->GetBytesWritten()>=dataLength+sizeof(PTCPHeader)); - } - else - { - - unsigned int oldWritten = bq->GetBytesWritten()-incomingPacket->length; - unsigned int newWritten = bq->GetBytesWritten(); - - // Return ID_DOWNLOAD_PROGRESS - if (newWritten/65536!=oldWritten/65536) - { - outgoingPacket = RakNet::OP_NEW(_FILE_AND_LINE_); - outgoingPacket->length=sizeof(MessageID) + - sizeof(unsigned int)*2 + - sizeof(unsigned int) + - 65536; - outgoingPacket->bitSize=BYTES_TO_BITS(incomingPacket->length); - outgoingPacket->guid=UNASSIGNED_RAKNET_GUID; - outgoingPacket->systemAddress=incomingPacket->systemAddress; - outgoingPacket->deleteData=false; - outgoingPacket->data=(unsigned char*) rakMalloc_Ex(outgoingPacket->length, _FILE_AND_LINE_); - if (outgoingPacket->data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - RakNet::OP_DELETE(outgoingPacket,_FILE_AND_LINE_); - return 0; - } - - outgoingPacket->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; - unsigned int totalParts=dataLength/65536; - unsigned int partIndex=newWritten/65536; - unsigned int oneChunkSize=65536; - memcpy(outgoingPacket->data+sizeof(MessageID), &partIndex, sizeof(unsigned int)); - memcpy(outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*1, &totalParts, sizeof(unsigned int)); - memcpy(outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*2, &oneChunkSize, sizeof(unsigned int)); - bq->IncrementReadOffset(sizeof(PTCPHeader)); - bq->ReadBytes((char*) outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*3,oneChunkSize,true); - bq->DecrementReadOffset(sizeof(PTCPHeader)); - - waitingPackets.Push(outgoingPacket, _FILE_AND_LINE_ ); - } - } - - } - - DeallocatePacket(incomingPacket); - incomingPacket=0; - } - else - waitingPackets.Push(incomingPacket, _FILE_AND_LINE_ ); - - incomingPacket = TCPInterface::Receive(); - } - - return ReturnOutgoingPacket(); -} -Packet *PacketizedTCP::ReturnOutgoingPacket(void) -{ - Packet *outgoingPacket=0; - unsigned int i; - while (outgoingPacket==0 && waitingPackets.IsEmpty()==false) - { - outgoingPacket=waitingPackets.Pop(); - PluginReceiveResult pluginResult; - for (i=0; i < messageHandlerList.Size(); i++) - { - pluginResult=messageHandlerList[i]->OnReceive(outgoingPacket); - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( outgoingPacket ); - outgoingPacket=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - outgoingPacket=0; - break; - } - } - } - - return outgoingPacket; -} - -void PacketizedTCP::AttachPlugin( PluginInterface2 *plugin ) -{ - if (messageHandlerList.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - messageHandlerList.Insert(plugin, _FILE_AND_LINE_); - plugin->SetPacketizedTCP(this); - plugin->OnAttach(); - } -} -void PacketizedTCP::DetachPlugin( PluginInterface2 *plugin ) -{ - if (plugin==0) - return; - - unsigned int index; - index = messageHandlerList.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - messageHandlerList[index]->OnDetach(); - // Unordered list so delete from end for speed - messageHandlerList[index]=messageHandlerList[messageHandlerList.Size()-1]; - messageHandlerList.RemoveFromEnd(); - plugin->SetPacketizedTCP(0); - } -} -void PacketizedTCP::CloseConnection( SystemAddress systemAddress ) -{ - RemoveFromConnectionList(systemAddress); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnClosedConnection(systemAddress, UNASSIGNED_RAKNET_GUID, LCR_CLOSED_BY_USER); - TCPInterface::CloseConnection(systemAddress); -} -void PacketizedTCP::RemoveFromConnectionList(const SystemAddress &sa) -{ - if (sa==UNASSIGNED_SYSTEM_ADDRESS) - return; - if (connections.Has(sa)) - { - unsigned int index = connections.GetIndexAtKey(sa); - if (index!=(unsigned int)-1) - { - RakNet::OP_DELETE(connections[index],_FILE_AND_LINE_); - connections.RemoveAtIndex(index); - } - } -} -void PacketizedTCP::AddToConnectionList(const SystemAddress &sa) -{ - if (sa==UNASSIGNED_SYSTEM_ADDRESS) - return; - connections.SetNew(sa, RakNet::OP_NEW(_FILE_AND_LINE_)); -} -void PacketizedTCP::ClearAllConnections(void) -{ - unsigned int i; - for (i=0; i < connections.Size(); i++) - RakNet::OP_DELETE(connections[i],_FILE_AND_LINE_); - connections.Clear(); -} -SystemAddress PacketizedTCP::HasCompletedConnectionAttempt(void) -{ - PushNotificationsToQueues(); - - if (_completedConnectionAttempts.IsEmpty()==false) - return _completedConnectionAttempts.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -SystemAddress PacketizedTCP::HasFailedConnectionAttempt(void) -{ - PushNotificationsToQueues(); - - if (_failedConnectionAttempts.IsEmpty()==false) - return _failedConnectionAttempts.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -SystemAddress PacketizedTCP::HasNewIncomingConnection(void) -{ - PushNotificationsToQueues(); - - if (_newIncomingConnections.IsEmpty()==false) - return _newIncomingConnections.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -SystemAddress PacketizedTCP::HasLostConnection(void) -{ - PushNotificationsToQueues(); - - if (_lostConnections.IsEmpty()==false) - return _lostConnections.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "PacketizedTCP.h" +#include "NativeTypes.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakAlloca.h" + +using namespace RakNet; + +typedef uint32_t PTCPHeader; + +STATIC_FACTORY_DEFINITIONS(PacketizedTCP,PacketizedTCP); + +PacketizedTCP::PacketizedTCP() +{ + +} +PacketizedTCP::~PacketizedTCP() +{ + ClearAllConnections(); +} + +bool PacketizedTCP::Start(unsigned short port, unsigned short maxIncomingConnections, int threadPriority, unsigned short socketFamily) +{ + bool success = TCPInterface::Start(port, maxIncomingConnections,0,threadPriority, socketFamily); + if (success) + { + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnRakPeerStartup(); + } + return success; +} + +void PacketizedTCP::Stop(void) +{ + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnRakPeerShutdown(); + for (i=0; i < waitingPackets.Size(); i++) + DeallocatePacket(waitingPackets[i]); + TCPInterface::Stop(); + ClearAllConnections(); +} + +void PacketizedTCP::Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ) +{ + PTCPHeader dataLength; + dataLength=length; +#ifndef __BITSTREAM_NATIVE_END + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytes((unsigned char*) &length,(unsigned char*) &dataLength,sizeof(dataLength)); +#else + dataLength=length; +#endif + + unsigned int lengthsArray[2]; + const char *dataArray[2]; + dataArray[0]=(char*) &dataLength; + dataArray[1]=data; + lengthsArray[0]=sizeof(dataLength); + lengthsArray[1]=length; + TCPInterface::SendList(dataArray,lengthsArray,2,systemAddress,broadcast); +} +bool PacketizedTCP::SendList( const char **data, const int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ) +{ + if (isStarted==false) + return false; + if (data==0) + return false; + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS && broadcast==false) + return false; + PTCPHeader totalLengthOfUserData=0; + int i; + for (i=0; i < numParameters; i++) + { + if (lengths[i]>0) + totalLengthOfUserData+=lengths[i]; + } + if (totalLengthOfUserData==0) + return false; + + PTCPHeader dataLength; +#ifndef __BITSTREAM_NATIVE_END + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytes((unsigned char*) &totalLengthOfUserData,(unsigned char*) &dataLength,sizeof(dataLength)); +#else + dataLength=totalLengthOfUserData; +#endif + + + unsigned int lengthsArray[512]; + const char *dataArray[512]; + dataArray[0]=(char*) &dataLength; + lengthsArray[0]=sizeof(dataLength); + for (int i=0; i < 512 && i < numParameters; i++) + { + dataArray[i+1]=data[i]; + lengthsArray[i+1]=lengths[i]; + } + return TCPInterface::SendList(dataArray,lengthsArray,numParameters+1,systemAddress,broadcast); +} +void PacketizedTCP::PushNotificationsToQueues(void) +{ + SystemAddress sa; + sa = TCPInterface::HasNewIncomingConnection(); + if (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + _newIncomingConnections.Push(sa, _FILE_AND_LINE_ ); + AddToConnectionList(sa); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnNewConnection(sa, UNASSIGNED_RAKNET_GUID, true); + } + + sa = TCPInterface::HasFailedConnectionAttempt(); + if (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + _failedConnectionAttempts.Push(sa, _FILE_AND_LINE_ ); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + { + Packet p; + p.systemAddress=sa; + p.data=0; + p.length=0; + p.bitSize=0; + messageHandlerList[i]->OnFailedConnectionAttempt(&p, FCAR_CONNECTION_ATTEMPT_FAILED); + } + } + + sa = TCPInterface::HasLostConnection(); + if (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + _lostConnections.Push(sa, _FILE_AND_LINE_ ); + RemoveFromConnectionList(sa); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnClosedConnection(sa, UNASSIGNED_RAKNET_GUID, LCR_DISCONNECTION_NOTIFICATION); + } + + sa = TCPInterface::HasCompletedConnectionAttempt(); + if (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + _completedConnectionAttempts.Push(sa, _FILE_AND_LINE_ ); + AddToConnectionList(sa); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnNewConnection(sa, UNASSIGNED_RAKNET_GUID, true); + } +} +Packet* PacketizedTCP::Receive( void ) +{ + PushNotificationsToQueues(); + + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->Update(); + + Packet *outgoingPacket=ReturnOutgoingPacket(); + if (outgoingPacket) + return outgoingPacket; + + Packet *incomingPacket; + incomingPacket = TCPInterface::Receive(); + unsigned int index; + + while (incomingPacket) + { + if (connections.Has(incomingPacket->systemAddress)) + index = connections.GetIndexAtKey(incomingPacket->systemAddress); + else + index=(unsigned int) -1; + if ((unsigned int)index==(unsigned int)-1) + { + DeallocatePacket(incomingPacket); + incomingPacket = TCPInterface::Receive(); + continue; + } + + + if (incomingPacket->deleteData==true) + { + // Came from network + SystemAddress systemAddressFromPacket; + if (index < connections.Size()) + { + DataStructures::ByteQueue *bq = connections[index]; + // Buffer data + bq->WriteBytes((const char*) incomingPacket->data,incomingPacket->length, _FILE_AND_LINE_); + systemAddressFromPacket=incomingPacket->systemAddress; + PTCPHeader dataLength; + + // Peek the header to see if a full message is waiting + bq->ReadBytes((char*) &dataLength,sizeof(PTCPHeader),true); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &dataLength,sizeof(dataLength)); + // Header indicates packet length. If enough data is available, read out and return one packet + if (bq->GetBytesWritten()>=dataLength+sizeof(PTCPHeader)) + { + do + { + bq->IncrementReadOffset(sizeof(PTCPHeader)); + outgoingPacket = RakNet::OP_NEW(_FILE_AND_LINE_); + outgoingPacket->length=dataLength; + outgoingPacket->bitSize=BYTES_TO_BITS(dataLength); + outgoingPacket->guid=UNASSIGNED_RAKNET_GUID; + outgoingPacket->systemAddress=systemAddressFromPacket; + outgoingPacket->deleteData=false; // Did not come from the network + outgoingPacket->data=(unsigned char*) rakMalloc_Ex(dataLength, _FILE_AND_LINE_); + if (outgoingPacket->data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + RakNet::OP_DELETE(outgoingPacket,_FILE_AND_LINE_); + return 0; + } + bq->ReadBytes((char*) outgoingPacket->data,dataLength,false); + + waitingPackets.Push(outgoingPacket, _FILE_AND_LINE_ ); + + // Peek the header to see if a full message is waiting + if (bq->ReadBytes((char*) &dataLength,sizeof(PTCPHeader),true)) + { + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &dataLength,sizeof(dataLength)); + } + else + break; + } while (bq->GetBytesWritten()>=dataLength+sizeof(PTCPHeader)); + } + else + { + + unsigned int oldWritten = bq->GetBytesWritten()-incomingPacket->length; + unsigned int newWritten = bq->GetBytesWritten(); + + // Return ID_DOWNLOAD_PROGRESS + if (newWritten/65536!=oldWritten/65536) + { + outgoingPacket = RakNet::OP_NEW(_FILE_AND_LINE_); + outgoingPacket->length=sizeof(MessageID) + + sizeof(unsigned int)*2 + + sizeof(unsigned int) + + 65536; + outgoingPacket->bitSize=BYTES_TO_BITS(incomingPacket->length); + outgoingPacket->guid=UNASSIGNED_RAKNET_GUID; + outgoingPacket->systemAddress=incomingPacket->systemAddress; + outgoingPacket->deleteData=false; + outgoingPacket->data=(unsigned char*) rakMalloc_Ex(outgoingPacket->length, _FILE_AND_LINE_); + if (outgoingPacket->data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + RakNet::OP_DELETE(outgoingPacket,_FILE_AND_LINE_); + return 0; + } + + outgoingPacket->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; + unsigned int totalParts=dataLength/65536; + unsigned int partIndex=newWritten/65536; + unsigned int oneChunkSize=65536; + memcpy(outgoingPacket->data+sizeof(MessageID), &partIndex, sizeof(unsigned int)); + memcpy(outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*1, &totalParts, sizeof(unsigned int)); + memcpy(outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*2, &oneChunkSize, sizeof(unsigned int)); + bq->IncrementReadOffset(sizeof(PTCPHeader)); + bq->ReadBytes((char*) outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*3,oneChunkSize,true); + bq->DecrementReadOffset(sizeof(PTCPHeader)); + + waitingPackets.Push(outgoingPacket, _FILE_AND_LINE_ ); + } + } + + } + + DeallocatePacket(incomingPacket); + incomingPacket=0; + } + else + waitingPackets.Push(incomingPacket, _FILE_AND_LINE_ ); + + incomingPacket = TCPInterface::Receive(); + } + + return ReturnOutgoingPacket(); +} +Packet *PacketizedTCP::ReturnOutgoingPacket(void) +{ + Packet *outgoingPacket=0; + unsigned int i; + while (outgoingPacket==0 && waitingPackets.IsEmpty()==false) + { + outgoingPacket=waitingPackets.Pop(); + PluginReceiveResult pluginResult; + for (i=0; i < messageHandlerList.Size(); i++) + { + pluginResult=messageHandlerList[i]->OnReceive(outgoingPacket); + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( outgoingPacket ); + outgoingPacket=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + outgoingPacket=0; + break; + } + } + } + + return outgoingPacket; +} + +void PacketizedTCP::AttachPlugin( PluginInterface2 *plugin ) +{ + if (messageHandlerList.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + messageHandlerList.Insert(plugin, _FILE_AND_LINE_); + plugin->SetPacketizedTCP(this); + plugin->OnAttach(); + } +} +void PacketizedTCP::DetachPlugin( PluginInterface2 *plugin ) +{ + if (plugin==0) + return; + + unsigned int index; + index = messageHandlerList.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + messageHandlerList[index]->OnDetach(); + // Unordered list so delete from end for speed + messageHandlerList[index]=messageHandlerList[messageHandlerList.Size()-1]; + messageHandlerList.RemoveFromEnd(); + plugin->SetPacketizedTCP(0); + } +} +void PacketizedTCP::CloseConnection( SystemAddress systemAddress ) +{ + RemoveFromConnectionList(systemAddress); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnClosedConnection(systemAddress, UNASSIGNED_RAKNET_GUID, LCR_CLOSED_BY_USER); + TCPInterface::CloseConnection(systemAddress); +} +void PacketizedTCP::RemoveFromConnectionList(const SystemAddress &sa) +{ + if (sa==UNASSIGNED_SYSTEM_ADDRESS) + return; + if (connections.Has(sa)) + { + unsigned int index = connections.GetIndexAtKey(sa); + if (index!=(unsigned int)-1) + { + RakNet::OP_DELETE(connections[index],_FILE_AND_LINE_); + connections.RemoveAtIndex(index); + } + } +} +void PacketizedTCP::AddToConnectionList(const SystemAddress &sa) +{ + if (sa==UNASSIGNED_SYSTEM_ADDRESS) + return; + connections.SetNew(sa, RakNet::OP_NEW(_FILE_AND_LINE_)); +} +void PacketizedTCP::ClearAllConnections(void) +{ + unsigned int i; + for (i=0; i < connections.Size(); i++) + RakNet::OP_DELETE(connections[i],_FILE_AND_LINE_); + connections.Clear(); +} +SystemAddress PacketizedTCP::HasCompletedConnectionAttempt(void) +{ + PushNotificationsToQueues(); + + if (_completedConnectionAttempts.IsEmpty()==false) + return _completedConnectionAttempts.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +SystemAddress PacketizedTCP::HasFailedConnectionAttempt(void) +{ + PushNotificationsToQueues(); + + if (_failedConnectionAttempts.IsEmpty()==false) + return _failedConnectionAttempts.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +SystemAddress PacketizedTCP::HasNewIncomingConnection(void) +{ + PushNotificationsToQueues(); + + if (_newIncomingConnections.IsEmpty()==false) + return _newIncomingConnections.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +SystemAddress PacketizedTCP::HasLostConnection(void) +{ + PushNotificationsToQueues(); + + if (_lostConnections.IsEmpty()==false) + return _lostConnections.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PacketizedTCP.h b/project/lib_projects/raknet/jni/RaknetSources/PacketizedTCP.h index 9a08fe3..986fc11 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PacketizedTCP.h +++ b/project/lib_projects/raknet/jni/RaknetSources/PacketizedTCP.h @@ -1,93 +1,93 @@ -/// \file -/// \brief A simple TCP based server allowing sends and receives. Can be connected by any TCP client, including telnet. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#ifndef __PACKETIZED_TCP -#define __PACKETIZED_TCP - -#include "TCPInterface.h" -#include "DS_ByteQueue.h" -#include "PluginInterface2.h" -#include "DS_Map.h" - -namespace RakNet -{ - -class RAK_DLL_EXPORT PacketizedTCP : public TCPInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(PacketizedTCP) - - PacketizedTCP(); - virtual ~PacketizedTCP(); - - /// Starts the TCP server on the indicated port - /// \param[in] port Which port to listen on. - /// \param[in] maxIncomingConnections Max incoming connections we will accept - /// \param[in] maxConnections Max total connections, which should be >= maxIncomingConnections - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - bool Start(unsigned short port, unsigned short maxIncomingConnections, int threadPriority=-99999, unsigned short socketFamily=AF_INET); - - /// Stops the TCP server - void Stop(void); - - /// Sends a byte stream - void Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ); - - // Sends a concatenated list of byte streams - bool SendList( const char **data, const int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ); - - /// Returns data received - Packet* Receive( void ); - - /// Disconnects a player/address - void CloseConnection( SystemAddress systemAddress ); - - /// Has a previous call to connect succeeded? - /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. - SystemAddress HasCompletedConnectionAttempt(void); - - /// Has a previous call to connect failed? - /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. - SystemAddress HasFailedConnectionAttempt(void); - - /// Queued events of new incoming connections - SystemAddress HasNewIncomingConnection(void); - - /// Queued events of lost connections - SystemAddress HasLostConnection(void); - - // Only currently tested with FileListTransfer! - void AttachPlugin( PluginInterface2 *plugin ); - void DetachPlugin( PluginInterface2 *plugin ); - -protected: - void ClearAllConnections(void); - void RemoveFromConnectionList(const SystemAddress &sa); - void AddToConnectionList(const SystemAddress &sa); - void PushNotificationsToQueues(void); - Packet *ReturnOutgoingPacket(void); - - // Plugins - DataStructures::List messageHandlerList; - // A single TCP recieve may generate multiple split packets. They are stored in the waitingPackets list until Receive is called - DataStructures::Queue waitingPackets; - DataStructures::Map connections; - - // Mirrors single producer / consumer, but processes them in Receive() before returning to user - DataStructures::Queue _newIncomingConnections, _lostConnections, _failedConnectionAttempts, _completedConnectionAttempts; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief A simple TCP based server allowing sends and receives. Can be connected by any TCP client, including telnet. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#ifndef __PACKETIZED_TCP +#define __PACKETIZED_TCP + +#include "TCPInterface.h" +#include "DS_ByteQueue.h" +#include "PluginInterface2.h" +#include "DS_Map.h" + +namespace RakNet +{ + +class RAK_DLL_EXPORT PacketizedTCP : public TCPInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(PacketizedTCP) + + PacketizedTCP(); + virtual ~PacketizedTCP(); + + /// Starts the TCP server on the indicated port + /// \param[in] port Which port to listen on. + /// \param[in] maxIncomingConnections Max incoming connections we will accept + /// \param[in] maxConnections Max total connections, which should be >= maxIncomingConnections + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + bool Start(unsigned short port, unsigned short maxIncomingConnections, int threadPriority=-99999, unsigned short socketFamily=AF_INET); + + /// Stops the TCP server + void Stop(void); + + /// Sends a byte stream + void Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ); + + // Sends a concatenated list of byte streams + bool SendList( const char **data, const int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ); + + /// Returns data received + Packet* Receive( void ); + + /// Disconnects a player/address + void CloseConnection( SystemAddress systemAddress ); + + /// Has a previous call to connect succeeded? + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasCompletedConnectionAttempt(void); + + /// Has a previous call to connect failed? + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasFailedConnectionAttempt(void); + + /// Queued events of new incoming connections + SystemAddress HasNewIncomingConnection(void); + + /// Queued events of lost connections + SystemAddress HasLostConnection(void); + + // Only currently tested with FileListTransfer! + void AttachPlugin( PluginInterface2 *plugin ); + void DetachPlugin( PluginInterface2 *plugin ); + +protected: + void ClearAllConnections(void); + void RemoveFromConnectionList(const SystemAddress &sa); + void AddToConnectionList(const SystemAddress &sa); + void PushNotificationsToQueues(void); + Packet *ReturnOutgoingPacket(void); + + // Plugins + DataStructures::List messageHandlerList; + // A single TCP recieve may generate multiple split packets. They are stored in the waitingPackets list until Receive is called + DataStructures::Queue waitingPackets; + DataStructures::Map connections; + + // Mirrors single producer / consumer, but processes them in Receive() before returning to user + DataStructures::Queue _newIncomingConnections, _lostConnections, _failedConnectionAttempts, _completedConnectionAttempts; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/PluginInterface2.cpp b/project/lib_projects/raknet/jni/RaknetSources/PluginInterface2.cpp index 706962d..10d9bd7 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PluginInterface2.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/PluginInterface2.cpp @@ -1,89 +1,89 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "PluginInterface2.h" -#include "PacketizedTCP.h" -#include "RakPeerInterface.h" -#include "BitStream.h" - -using namespace RakNet; - -PluginInterface2::PluginInterface2() -{ - rakPeerInterface=0; -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - packetizedTCP=0; -#endif -} -PluginInterface2::~PluginInterface2() -{ - -} -void PluginInterface2::SendUnified( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) -{ - if (rakPeerInterface) - rakPeerInterface->Send(bitStream,priority,reliability,orderingChannel,systemIdentifier,broadcast); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->Send((const char*) bitStream->GetData(), bitStream->GetNumberOfBytesUsed(), systemIdentifier.systemAddress, broadcast); -#endif -} -Packet *PluginInterface2::AllocatePacketUnified(unsigned dataSize) -{ - if (rakPeerInterface) - return rakPeerInterface->AllocatePacket(dataSize); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - return packetizedTCP->AllocatePacket(dataSize); -#else - return 0; -#endif - -} -void PluginInterface2::PushBackPacketUnified(Packet *packet, bool pushAtHead) -{ - if (rakPeerInterface) - rakPeerInterface->PushBackPacket(packet,pushAtHead); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->PushBackPacket(packet,pushAtHead); -#endif -} -void PluginInterface2::DeallocPacketUnified(Packet *packet) -{ - if (rakPeerInterface) - rakPeerInterface->DeallocatePacket(packet); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->DeallocatePacket(packet); -#endif -} -bool PluginInterface2::SendListUnified( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) -{ - if (rakPeerInterface) - { - return rakPeerInterface->SendList(data,lengths,numParameters,priority,reliability,orderingChannel,systemIdentifier,broadcast)!=0; - } -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - { - return packetizedTCP->SendList(data,lengths,numParameters,systemIdentifier.systemAddress,broadcast ); - } -#else - return false; -#endif -} -void PluginInterface2::SetRakPeerInterface( RakPeerInterface *ptr ) -{ - rakPeerInterface=ptr; -} -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 -void PluginInterface2::SetPacketizedTCP( PacketizedTCP *ptr ) -{ - packetizedTCP=ptr; -} -#endif +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "PluginInterface2.h" +#include "PacketizedTCP.h" +#include "RakPeerInterface.h" +#include "BitStream.h" + +using namespace RakNet; + +PluginInterface2::PluginInterface2() +{ + rakPeerInterface=0; +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + packetizedTCP=0; +#endif +} +PluginInterface2::~PluginInterface2() +{ + +} +void PluginInterface2::SendUnified( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) +{ + if (rakPeerInterface) + rakPeerInterface->Send(bitStream,priority,reliability,orderingChannel,systemIdentifier,broadcast); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->Send((const char*) bitStream->GetData(), bitStream->GetNumberOfBytesUsed(), systemIdentifier.systemAddress, broadcast); +#endif +} +Packet *PluginInterface2::AllocatePacketUnified(unsigned dataSize) +{ + if (rakPeerInterface) + return rakPeerInterface->AllocatePacket(dataSize); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + return packetizedTCP->AllocatePacket(dataSize); +#else + return 0; +#endif + +} +void PluginInterface2::PushBackPacketUnified(Packet *packet, bool pushAtHead) +{ + if (rakPeerInterface) + rakPeerInterface->PushBackPacket(packet,pushAtHead); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->PushBackPacket(packet,pushAtHead); +#endif +} +void PluginInterface2::DeallocPacketUnified(Packet *packet) +{ + if (rakPeerInterface) + rakPeerInterface->DeallocatePacket(packet); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->DeallocatePacket(packet); +#endif +} +bool PluginInterface2::SendListUnified( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) +{ + if (rakPeerInterface) + { + return rakPeerInterface->SendList(data,lengths,numParameters,priority,reliability,orderingChannel,systemIdentifier,broadcast)!=0; + } +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + { + return packetizedTCP->SendList(data,lengths,numParameters,systemIdentifier.systemAddress,broadcast ); + } +#else + return false; +#endif +} +void PluginInterface2::SetRakPeerInterface( RakPeerInterface *ptr ) +{ + rakPeerInterface=ptr; +} +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 +void PluginInterface2::SetPacketizedTCP( PacketizedTCP *ptr ) +{ + packetizedTCP=ptr; +} +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/PluginInterface2.h b/project/lib_projects/raknet/jni/RaknetSources/PluginInterface2.h index 549885d..48f8284 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/PluginInterface2.h +++ b/project/lib_projects/raknet/jni/RaknetSources/PluginInterface2.h @@ -1,200 +1,200 @@ -/// \file -/// \brief \b RakNet's plugin functionality system, version 2. You can derive from this to create your own plugins. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __PLUGIN_INTERFACE_2_H -#define __PLUGIN_INTERFACE_2_H - -#include "NativeFeatureIncludes.h" -#include "RakNetTypes.h" -#include "Export.h" -#include "PacketPriority.h" - -namespace RakNet { - -/// Forward declarations -class RakPeerInterface; -class PacketizedTCP; -struct Packet; -struct InternalPacket; - -/// \defgroup PLUGIN_INTERFACE_GROUP PluginInterface2 - -/// \defgroup PLUGINS_GROUP Plugins -/// \ingroup PLUGIN_INTERFACE_GROUP - -/// For each message that arrives on an instance of RakPeer, the plugins get an opportunity to process them first. This enumeration represents what to do with the message -/// \ingroup PLUGIN_INTERFACE_GROUP -enum PluginReceiveResult -{ - /// The plugin used this message and it shouldn't be given to the user. - RR_STOP_PROCESSING_AND_DEALLOCATE=0, - - /// This message will be processed by other plugins, and at last by the user. - RR_CONTINUE_PROCESSING, - - /// The plugin is going to hold on to this message. Do not deallocate it but do not pass it to other plugins either. - RR_STOP_PROCESSING, -}; - -/// Reasons why a connection was lost -/// \ingroup PLUGIN_INTERFACE_GROUP -enum PI2_LostConnectionReason -{ - /// Called RakPeer::CloseConnection() - LCR_CLOSED_BY_USER, - - /// Got ID_DISCONNECTION_NOTIFICATION - LCR_DISCONNECTION_NOTIFICATION, - - /// GOT ID_CONNECTION_LOST - LCR_CONNECTION_LOST -}; - -/// Returns why a connection attempt failed -/// \ingroup PLUGIN_INTERFACE_GROUP -enum PI2_FailedConnectionAttemptReason -{ - FCAR_CONNECTION_ATTEMPT_FAILED, - FCAR_ALREADY_CONNECTED, - FCAR_NO_FREE_INCOMING_CONNECTIONS, - FCAR_SECURITY_PUBLIC_KEY_MISMATCH, - FCAR_CONNECTION_BANNED, - FCAR_INVALID_PASSWORD, - FCAR_INCOMPATIBLE_PROTOCOL, - FCAR_IP_RECENTLY_CONNECTED, - FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY, - FCAR_OUR_SYSTEM_REQUIRES_SECURITY, - FCAR_PUBLIC_KEY_MISMATCH, -}; - -/// RakNet's plugin system. Each plugin processes the following events: -/// -Connection attempts -/// -The result of connection attempts -/// -Each incoming message -/// -Updates over time, when RakPeer::Receive() is called -/// -/// \ingroup PLUGIN_INTERFACE_GROUP -class RAK_DLL_EXPORT PluginInterface2 -{ -public: - PluginInterface2(); - virtual ~PluginInterface2(); - - /// Called when the interface is attached - virtual void OnAttach(void) {} - - /// Called when the interface is detached - virtual void OnDetach(void) {} - - /// Update is called every time a packet is checked for . - virtual void Update(void) {} - - /// OnReceive is called for every packet. - /// \param[in] packet the packet that is being returned to the user - /// \return True to allow the game and other plugins to get this message, false to absorb it - virtual PluginReceiveResult OnReceive(Packet *packet) {(void) packet; return RR_CONTINUE_PROCESSING;} - - /// Called when RakPeer is initialized - virtual void OnRakPeerStartup(void) {} - - /// Called when RakPeer is shutdown - virtual void OnRakPeerShutdown(void) {} - - /// Called when a connection is dropped because the user called RakPeer::CloseConnection() for a particular system - /// \param[in] systemAddress The system whose connection was closed - /// \param[in] rakNetGuid The guid of the specified system - /// \param[in] lostConnectionReason How the connection was closed: manually, connection lost, or notification of disconnection - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ){(void) systemAddress; (void) rakNetGUID; (void) lostConnectionReason;} - - /// Called when we got a new connection - /// \param[in] systemAddress Address of the new connection - /// \param[in] rakNetGuid The guid of the specified system - /// \param[in] isIncoming If true, this is ID_NEW_INCOMING_CONNECTION, or the equivalent - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) {(void) systemAddress; (void) rakNetGUID; (void) isIncoming;} - - /// Called when a connection attempt fails - /// \param[in] packet Packet to be returned to the user - /// \param[in] failedConnectionReason Why the connection failed - virtual void OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason) {(void) packet; (void) failedConnectionAttemptReason;} - - /// Queried when attached to RakPeer - /// Return true to call OnDirectSocketSend(), OnDirectSocketReceive(), OnReliabilityLayerPacketError(), OnInternalPacket(), and OnAck() - /// If true, then you cannot call RakPeer::AttachPlugin() or RakPeer::DetachPlugin() for this plugin, while RakPeer is active - virtual bool UsesReliabilityLayer(void) const {return false;} - - /// Called on a send to the socket, per datagram, that does not go through the reliability layer - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] data The data being sent - /// \param[in] bitsUsed How many bits long \a data is - /// \param[in] remoteSystemAddress Which system this message is being sent to - virtual void OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} - - /// Called on a receive from the socket, per datagram, that does not go through the reliability layer - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] data The data being sent - /// \param[in] bitsUsed How many bits long \a data is - /// \param[in] remoteSystemAddress Which system this message is being sent to - virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} - - /// Called when the reliability layer rejects a send or receive - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] bitsUsed How many bits long \a data is - /// \param[in] remoteSystemAddress Which system this message is being sent to - virtual void OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) errorMessage; (void) bitsUsed; (void) remoteSystemAddress;} - - /// Called on a send or receive of a message within the reliability layer - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] internalPacket The user message, along with all send data. - /// \param[in] frameNumber The number of frames sent or received so far for this player depending on \a isSend . Indicates the frame of this user message. - /// \param[in] remoteSystemAddress The player we sent or got this packet from - /// \param[in] time The current time as returned by RakNet::GetTimeMS() - /// \param[in] isSend Is this callback representing a send event or receive event? - virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend) {(void) internalPacket; (void) frameNumber; (void) remoteSystemAddress; (void) time; (void) isSend;} - - /// Called when we get an ack for a message we reliably sent - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] messageNumber The numerical identifier for which message this is - /// \param[in] remoteSystemAddress The player we sent or got this packet from - /// \param[in] time The current time as returned by RakNet::GetTimeMS() - virtual void OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time) {(void) messageNumber; (void) remoteSystemAddress; (void) time;} - - /// System called RakPeerInterface::PushBackPacket - /// \param[in] data The data being sent - /// \param[in] bitsUsed How many bits long \a data is - /// \param[in] remoteSystemAddress The player we sent or got this packet from - virtual void OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} - - RakPeerInterface *GetRakPeerInterface(void) const {return rakPeerInterface;} - - /// \internal - void SetRakPeerInterface( RakPeerInterface *ptr ); - -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - /// \internal - void SetPacketizedTCP( PacketizedTCP *ptr ); -#endif -protected: - // Send through either rakPeerInterface or packetizedTCP, whichever is available - void SendUnified( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); - bool SendListUnified( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); - - Packet *AllocatePacketUnified(unsigned dataSize); - void PushBackPacketUnified(Packet *packet, bool pushAtHead); - void DeallocPacketUnified(Packet *packet); - - // Filled automatically in when attached - RakPeerInterface *rakPeerInterface; -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - PacketizedTCP *packetizedTCP; -#endif -}; - -} // namespace RakNet - -#endif - +/// \file +/// \brief \b RakNet's plugin functionality system, version 2. You can derive from this to create your own plugins. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __PLUGIN_INTERFACE_2_H +#define __PLUGIN_INTERFACE_2_H + +#include "NativeFeatureIncludes.h" +#include "RakNetTypes.h" +#include "Export.h" +#include "PacketPriority.h" + +namespace RakNet { + +/// Forward declarations +class RakPeerInterface; +class PacketizedTCP; +struct Packet; +struct InternalPacket; + +/// \defgroup PLUGIN_INTERFACE_GROUP PluginInterface2 + +/// \defgroup PLUGINS_GROUP Plugins +/// \ingroup PLUGIN_INTERFACE_GROUP + +/// For each message that arrives on an instance of RakPeer, the plugins get an opportunity to process them first. This enumeration represents what to do with the message +/// \ingroup PLUGIN_INTERFACE_GROUP +enum PluginReceiveResult +{ + /// The plugin used this message and it shouldn't be given to the user. + RR_STOP_PROCESSING_AND_DEALLOCATE=0, + + /// This message will be processed by other plugins, and at last by the user. + RR_CONTINUE_PROCESSING, + + /// The plugin is going to hold on to this message. Do not deallocate it but do not pass it to other plugins either. + RR_STOP_PROCESSING, +}; + +/// Reasons why a connection was lost +/// \ingroup PLUGIN_INTERFACE_GROUP +enum PI2_LostConnectionReason +{ + /// Called RakPeer::CloseConnection() + LCR_CLOSED_BY_USER, + + /// Got ID_DISCONNECTION_NOTIFICATION + LCR_DISCONNECTION_NOTIFICATION, + + /// GOT ID_CONNECTION_LOST + LCR_CONNECTION_LOST +}; + +/// Returns why a connection attempt failed +/// \ingroup PLUGIN_INTERFACE_GROUP +enum PI2_FailedConnectionAttemptReason +{ + FCAR_CONNECTION_ATTEMPT_FAILED, + FCAR_ALREADY_CONNECTED, + FCAR_NO_FREE_INCOMING_CONNECTIONS, + FCAR_SECURITY_PUBLIC_KEY_MISMATCH, + FCAR_CONNECTION_BANNED, + FCAR_INVALID_PASSWORD, + FCAR_INCOMPATIBLE_PROTOCOL, + FCAR_IP_RECENTLY_CONNECTED, + FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY, + FCAR_OUR_SYSTEM_REQUIRES_SECURITY, + FCAR_PUBLIC_KEY_MISMATCH, +}; + +/// RakNet's plugin system. Each plugin processes the following events: +/// -Connection attempts +/// -The result of connection attempts +/// -Each incoming message +/// -Updates over time, when RakPeer::Receive() is called +/// +/// \ingroup PLUGIN_INTERFACE_GROUP +class RAK_DLL_EXPORT PluginInterface2 +{ +public: + PluginInterface2(); + virtual ~PluginInterface2(); + + /// Called when the interface is attached + virtual void OnAttach(void) {} + + /// Called when the interface is detached + virtual void OnDetach(void) {} + + /// Update is called every time a packet is checked for . + virtual void Update(void) {} + + /// OnReceive is called for every packet. + /// \param[in] packet the packet that is being returned to the user + /// \return True to allow the game and other plugins to get this message, false to absorb it + virtual PluginReceiveResult OnReceive(Packet *packet) {(void) packet; return RR_CONTINUE_PROCESSING;} + + /// Called when RakPeer is initialized + virtual void OnRakPeerStartup(void) {} + + /// Called when RakPeer is shutdown + virtual void OnRakPeerShutdown(void) {} + + /// Called when a connection is dropped because the user called RakPeer::CloseConnection() for a particular system + /// \param[in] systemAddress The system whose connection was closed + /// \param[in] rakNetGuid The guid of the specified system + /// \param[in] lostConnectionReason How the connection was closed: manually, connection lost, or notification of disconnection + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ){(void) systemAddress; (void) rakNetGUID; (void) lostConnectionReason;} + + /// Called when we got a new connection + /// \param[in] systemAddress Address of the new connection + /// \param[in] rakNetGuid The guid of the specified system + /// \param[in] isIncoming If true, this is ID_NEW_INCOMING_CONNECTION, or the equivalent + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) {(void) systemAddress; (void) rakNetGUID; (void) isIncoming;} + + /// Called when a connection attempt fails + /// \param[in] packet Packet to be returned to the user + /// \param[in] failedConnectionReason Why the connection failed + virtual void OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason) {(void) packet; (void) failedConnectionAttemptReason;} + + /// Queried when attached to RakPeer + /// Return true to call OnDirectSocketSend(), OnDirectSocketReceive(), OnReliabilityLayerPacketError(), OnInternalPacket(), and OnAck() + /// If true, then you cannot call RakPeer::AttachPlugin() or RakPeer::DetachPlugin() for this plugin, while RakPeer is active + virtual bool UsesReliabilityLayer(void) const {return false;} + + /// Called on a send to the socket, per datagram, that does not go through the reliability layer + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] data The data being sent + /// \param[in] bitsUsed How many bits long \a data is + /// \param[in] remoteSystemAddress Which system this message is being sent to + virtual void OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} + + /// Called on a receive from the socket, per datagram, that does not go through the reliability layer + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] data The data being sent + /// \param[in] bitsUsed How many bits long \a data is + /// \param[in] remoteSystemAddress Which system this message is being sent to + virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} + + /// Called when the reliability layer rejects a send or receive + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] bitsUsed How many bits long \a data is + /// \param[in] remoteSystemAddress Which system this message is being sent to + virtual void OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) errorMessage; (void) bitsUsed; (void) remoteSystemAddress;} + + /// Called on a send or receive of a message within the reliability layer + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] internalPacket The user message, along with all send data. + /// \param[in] frameNumber The number of frames sent or received so far for this player depending on \a isSend . Indicates the frame of this user message. + /// \param[in] remoteSystemAddress The player we sent or got this packet from + /// \param[in] time The current time as returned by RakNet::GetTimeMS() + /// \param[in] isSend Is this callback representing a send event or receive event? + virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend) {(void) internalPacket; (void) frameNumber; (void) remoteSystemAddress; (void) time; (void) isSend;} + + /// Called when we get an ack for a message we reliably sent + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] messageNumber The numerical identifier for which message this is + /// \param[in] remoteSystemAddress The player we sent or got this packet from + /// \param[in] time The current time as returned by RakNet::GetTimeMS() + virtual void OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time) {(void) messageNumber; (void) remoteSystemAddress; (void) time;} + + /// System called RakPeerInterface::PushBackPacket + /// \param[in] data The data being sent + /// \param[in] bitsUsed How many bits long \a data is + /// \param[in] remoteSystemAddress The player we sent or got this packet from + virtual void OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} + + RakPeerInterface *GetRakPeerInterface(void) const {return rakPeerInterface;} + + /// \internal + void SetRakPeerInterface( RakPeerInterface *ptr ); + +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + /// \internal + void SetPacketizedTCP( PacketizedTCP *ptr ); +#endif +protected: + // Send through either rakPeerInterface or packetizedTCP, whichever is available + void SendUnified( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); + bool SendListUnified( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); + + Packet *AllocatePacketUnified(unsigned dataSize); + void PushBackPacketUnified(Packet *packet, bool pushAtHead); + void DeallocPacketUnified(Packet *packet); + + // Filled automatically in when attached + RakPeerInterface *rakPeerInterface; +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + PacketizedTCP *packetizedTCP; +#endif +}; + +} // namespace RakNet + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/RPC4Plugin.cpp b/project/lib_projects/raknet/jni/RaknetSources/RPC4Plugin.cpp index 7583d28..64303ba 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RPC4Plugin.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RPC4Plugin.cpp @@ -1,594 +1,594 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_RPC4Plugin==1 - -#include "RPC4Plugin.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "PacketizedTCP.h" -#include "RakSleep.h" -#include "RakNetDefines.h" -#include "DS_Queue.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(RPC4,RPC4); - -struct GlobalRegistration -{ - void ( *registerFunctionPointer ) ( RakNet::BitStream *userData, Packet *packet ); - void ( *registerBlockingFunctionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet ); - char functionName[RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH]; - MessageID messageId; - int callPriority; -}; -static GlobalRegistration globalRegistrationBuffer[RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS]; -static unsigned int globalRegistrationIndex=0; - -RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) -{ - RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); - unsigned int i; - for (i=0; uniqueID[i]; i++) - { - RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); - globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; - } - globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=functionPointer; - globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; - globalRegistrationBuffer[globalRegistrationIndex].callPriority=0xFFFFFFFF; - globalRegistrationIndex++; -} -RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority) -{ - RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); - unsigned int i; - for (i=0; uniqueID[i]; i++) - { - RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); - globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; - } - globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=functionPointer; - globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; - RakAssert(callPriority!=0xFFFFFFFF); - globalRegistrationBuffer[globalRegistrationIndex].callPriority=callPriority; - globalRegistrationIndex++; -} -RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )) -{ - RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); - unsigned int i; - for (i=0; uniqueID[i]; i++) - { - RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); - globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; - } - globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=0; - globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=functionPointer; - globalRegistrationIndex++; -} -RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, MessageID messageId) -{ - RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); - unsigned int i; - for (i=0; uniqueID[i]; i++) - { - RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); - globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; - } - globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=0; - globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; - globalRegistrationBuffer[globalRegistrationIndex].messageId=messageId; - globalRegistrationIndex++; -} - -enum RPC4Identifiers -{ - ID_RPC4_CALL, - ID_RPC4_RETURN, - ID_RPC4_SIGNAL, -}; -int RPC4::LocalSlotObjectComp( const LocalSlotObject &key, const LocalSlotObject &data ) -{ - if (key.callPriority>data.callPriority) - return -1; - if (key.callPriority==data.callPriority) - { - if (key.registrationCountmessageId) - return -1; - if (key > data->messageId) - return 1; - return 0; -} - -RPC4::RPC4() -{ - gotBlockingReturnValue=false; - nextSlotRegistrationCount=0; - interruptSignal=false; -} -RPC4::~RPC4() -{ - unsigned int i; - for (i=0; i < localCallbacks.Size(); i++) - { - RakNet::OP_DELETE(localCallbacks[i],_FILE_AND_LINE_); - } - - DataStructures::List keyList; - DataStructures::List outputList; - localSlots.GetAsList(outputList,keyList,_FILE_AND_LINE_); - unsigned int j; - for (j=0; j < outputList.Size(); j++) - { - RakNet::OP_DELETE(outputList[j],_FILE_AND_LINE_); - } - localSlots.Clear(_FILE_AND_LINE_); -} -bool RPC4::RegisterFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) -{ - DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(uniqueID); - if (skhi.IsInvalid()==false) - return false; - - registeredNonblockingFunctions.Push(uniqueID,functionPointer,_FILE_AND_LINE_); - return true; -} -void RPC4::RegisterSlot(const char *sharedIdentifier, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority) -{ - LocalSlotObject lso(nextSlotRegistrationCount++, callPriority, functionPointer); - DataStructures::HashIndex idx = GetLocalSlotIndex(sharedIdentifier); - LocalSlot *localSlot; - if (idx.IsInvalid()) - { - localSlot = RakNet::OP_NEW(_FILE_AND_LINE_); - localSlots.Push(sharedIdentifier, localSlot,_FILE_AND_LINE_); - } - else - { - localSlot=localSlots.ItemAtIndex(idx); - } - localSlot->slotObjects.Insert(lso,lso,true,_FILE_AND_LINE_); -} -bool RPC4::RegisterBlockingFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )) -{ - DataStructures::HashIndex skhi = registeredBlockingFunctions.GetIndexOf(uniqueID); - if (skhi.IsInvalid()==false) - return false; - - registeredBlockingFunctions.Push(uniqueID,functionPointer,_FILE_AND_LINE_); - return true; -} -void RPC4::RegisterLocalCallback(const char* uniqueID, MessageID messageId) -{ - bool objectExists; - unsigned int index; - LocalCallback *lc; - RakNet::RakString str; - str=uniqueID; - index = localCallbacks.GetIndexFromKey(messageId,&objectExists); - if (objectExists) - { - lc = localCallbacks[index]; - index = lc->functions.GetIndexFromKey(str,&objectExists); - if (objectExists==false) - lc->functions.InsertAtIndex(str,index,_FILE_AND_LINE_); - } - else - { - lc = RakNet::OP_NEW(_FILE_AND_LINE_); - lc->messageId=messageId; - lc->functions.Insert(str,str,false,_FILE_AND_LINE_); - localCallbacks.InsertAtIndex(lc,index,_FILE_AND_LINE_); - } -} -bool RPC4::UnregisterFunction(const char* uniqueID) -{ - void ( *f ) ( RakNet::BitStream *, Packet * ); - return registeredNonblockingFunctions.Pop(f,uniqueID,_FILE_AND_LINE_); -} -bool RPC4::UnregisterBlockingFunction(const char* uniqueID) -{ - void ( *f ) ( RakNet::BitStream *, RakNet::BitStream *,Packet * ); - return registeredBlockingFunctions.Pop(f,uniqueID,_FILE_AND_LINE_); -} -bool RPC4::UnregisterLocalCallback(const char* uniqueID, MessageID messageId) -{ - bool objectExists; - unsigned int index, index2; - LocalCallback *lc; - RakNet::RakString str; - str=uniqueID; - index = localCallbacks.GetIndexFromKey(messageId,&objectExists); - if (objectExists) - { - lc = localCallbacks[index]; - index2 = lc->functions.GetIndexFromKey(str,&objectExists); - if (objectExists) - { - lc->functions.RemoveAtIndex(index2); - if (lc->functions.Size()==0) - { - RakNet::OP_DELETE(lc,_FILE_AND_LINE_); - localCallbacks.RemoveAtIndex(index); - return true; - } - } - } - return false; -} -bool RPC4::UnregisterSlot(const char* sharedIdentifier) -{ - DataStructures::HashIndex hi = localSlots.GetIndexOf(sharedIdentifier); - if (hi.IsInvalid()==false) - { - LocalSlot *ls = localSlots.ItemAtIndex(hi); - RakNet::OP_DELETE(ls, _FILE_AND_LINE_); - localSlots.RemoveAtIndex(hi, _FILE_AND_LINE_); - return true; - } - - return false; -} -void RPC4::CallLoopback( const char* uniqueID, RakNet::BitStream * bitStream ) -{ - Packet *p=0; - - DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(uniqueID); - - if (skhi.IsInvalid()==true) - { - if (rakPeerInterface) - p=AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)+(unsigned int) strlen(uniqueID)+1); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - p=packetizedTCP->AllocatePacket(sizeof(MessageID)+sizeof(unsigned char)+(unsigned int) strlen(uniqueID)+1); -#endif - - if (rakPeerInterface) - p->guid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - p->guid=UNASSIGNED_RAKNET_GUID; -#endif - - p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->data[0]=ID_RPC_REMOTE_ERROR; - p->data[1]=RPC_ERROR_FUNCTION_NOT_REGISTERED; - strcpy((char*) p->data+2, uniqueID); - - PushBackPacketUnified(p,false); - - return; - } - - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_CALL); - out.WriteCompressed(uniqueID); - out.Write(false); // nonblocking - if (bitStream) - { - bitStream->ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(bitStream); - } - if (rakPeerInterface) - p=AllocatePacketUnified(out.GetNumberOfBytesUsed()); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - p=packetizedTCP->AllocatePacket(out.GetNumberOfBytesUsed()); -#endif - - if (rakPeerInterface) - p->guid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - p->guid=UNASSIGNED_RAKNET_GUID; -#endif - p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - p->systemAddress.systemIndex=(SystemIndex)-1; - memcpy(p->data,out.GetData(),out.GetNumberOfBytesUsed()); - PushBackPacketUnified(p,false); - return; -} -void RPC4::Call( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) -{ - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_CALL); - out.WriteCompressed(uniqueID); - out.Write(false); // Nonblocking - if (bitStream) - { - bitStream->ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(bitStream); - } - SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,broadcast); -} -bool RPC4::CallBlocking( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, RakNet::BitStream *returnData ) -{ - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_CALL); - out.WriteCompressed(uniqueID); - out.Write(true); // Blocking - if (bitStream) - { - bitStream->ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(bitStream); - } - RakAssert(returnData); - RakAssert(rakPeerInterface); - ConnectionState cs; - cs = rakPeerInterface->GetConnectionState(systemIdentifier); - if (cs!=IS_CONNECTED) - return false; - - SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,false); - - returnData->Reset(); - blockingReturnValue.Reset(); - gotBlockingReturnValue=false; - Packet *packet; - DataStructures::Queue packetQueue; - while (gotBlockingReturnValue==false) - { - // TODO - block, filter until gotBlockingReturnValue==true or ID_CONNECTION_LOST or ID_DISCONNECTION_NOTIFICXATION or ID_RPC_REMOTE_ERROR/RPC_ERROR_FUNCTION_NOT_REGISTERED - RakSleep(30); - - packet=rakPeerInterface->Receive(); - - if (packet) - { - if ( - (packet->data[0]==ID_CONNECTION_LOST || packet->data[0]==ID_DISCONNECTION_NOTIFICATION) && - ((systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID && packet->guid==systemIdentifier.rakNetGuid) || - (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && packet->systemAddress==systemIdentifier.systemAddress)) - ) - { - // Push back to head in reverse order - rakPeerInterface->PushBackPacket(packet,true); - while (packetQueue.Size()) - rakPeerInterface->PushBackPacket(packetQueue.Pop(),true); - return false; - } - else if (packet->data[0]==ID_RPC_REMOTE_ERROR && packet->data[1]==RPC_ERROR_FUNCTION_NOT_REGISTERED) - { - RakNet::RakString functionName; - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - bsIn.Read(functionName); - if (functionName==uniqueID) - { - // Push back to head in reverse order - rakPeerInterface->PushBackPacket(packet,true); - while (packetQueue.Size()) - rakPeerInterface->PushBackPacket(packetQueue.Pop(),true); - return false; - } - else - { - packetQueue.PushAtHead(packet,0,_FILE_AND_LINE_); - } - } - else - { - packetQueue.PushAtHead(packet,0,_FILE_AND_LINE_); - } - } - } - - returnData->Read(blockingReturnValue); - return true; -} -void RPC4::Signal(const char *sharedIdentifier, RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool invokeLocal) -{ - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_SIGNAL); - out.WriteCompressed(sharedIdentifier); - if (bitStream) - { - bitStream->ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(bitStream); - } - SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,broadcast); - - if (invokeLocal) - { - DataStructures::HashIndex functionIndex; - functionIndex = localSlots.GetIndexOf(sharedIdentifier); - if (functionIndex.IsInvalid()) - return; - - Packet p; - p.guid=rakPeerInterface->GetMyGUID(); - p.systemAddress=rakPeerInterface->GetMyBoundAddress(0); - p.wasGeneratedLocally=true; - RakNet::BitStream *bsptr, bstemp; - if (bitStream) - { - bitStream->ResetReadPointer(); - p.length=bitStream->GetNumberOfBytesUsed(); - p.bitSize=bitStream->GetNumberOfBitsUsed(); - bsptr=bitStream; - } - else - { - p.length=0; - p.bitSize=0; - bsptr=&bstemp; - } - InvokeSignal(functionIndex, bsptr, &p); - } -} -void RPC4::InvokeSignal(DataStructures::HashIndex functionIndex, RakNet::BitStream *serializedParameters, Packet *packet) -{ - if (functionIndex.IsInvalid()) - return; - - interruptSignal=false; - LocalSlot *localSlot = localSlots.ItemAtIndex(functionIndex); - unsigned int i; - i=0; - while (i < localSlot->slotObjects.Size()) - { - localSlot->slotObjects[i].functionPointer(serializedParameters, packet); - - // Not threadsafe - if (interruptSignal==true) - break; - - serializedParameters->ResetReadPointer(); - - i++; - } -} -void RPC4::InterruptSignal(void) -{ - interruptSignal=true; -} -void RPC4::OnAttach(void) -{ - unsigned int i; - for (i=0; i < globalRegistrationIndex; i++) - { - if (globalRegistrationBuffer[i].registerFunctionPointer) - { - if (globalRegistrationBuffer[i].callPriority==(int)0xFFFFFFFF) - RegisterFunction(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerFunctionPointer); - else - RegisterSlot(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerFunctionPointer, globalRegistrationBuffer[i].callPriority); - } - else if (globalRegistrationBuffer[i].registerBlockingFunctionPointer) - RegisterBlockingFunction(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerBlockingFunctionPointer); - else - RegisterLocalCallback(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].messageId); - } -} -PluginReceiveResult RPC4::OnReceive(Packet *packet) -{ - if (packet->data[0]==ID_RPC_PLUGIN) - { - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - - if (packet->data[1]==ID_RPC4_CALL) - { - RakNet::RakString functionName; - bsIn.ReadCompressed(functionName); - bool isBlocking=false; - bsIn.Read(isBlocking); - if (isBlocking==false) - { - DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(functionName.C_String()); - if (skhi.IsInvalid()) - { - RakNet::BitStream bsOut; - bsOut.Write((unsigned char) ID_RPC_REMOTE_ERROR); - bsOut.Write((unsigned char) RPC_ERROR_FUNCTION_NOT_REGISTERED); - bsOut.Write(functionName.C_String(),(unsigned int) functionName.GetLength()+1); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - void ( *fp ) ( RakNet::BitStream *, Packet * ); - fp = registeredNonblockingFunctions.ItemAtIndex(skhi); - bsIn.AlignReadToByteBoundary(); - fp(&bsIn,packet); - } - else - { - DataStructures::HashIndex skhi = registeredBlockingFunctions.GetIndexOf(functionName.C_String()); - if (skhi.IsInvalid()) - { - RakNet::BitStream bsOut; - bsOut.Write((unsigned char) ID_RPC_REMOTE_ERROR); - bsOut.Write((unsigned char) RPC_ERROR_FUNCTION_NOT_REGISTERED); - bsOut.Write(functionName.C_String(),(unsigned int) functionName.GetLength()+1); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - void ( *fp ) ( RakNet::BitStream *, RakNet::BitStream *, Packet * ); - fp = registeredBlockingFunctions.ItemAtIndex(skhi); - RakNet::BitStream returnData; - bsIn.AlignReadToByteBoundary(); - fp(&bsIn, &returnData, packet); - - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_RETURN); - returnData.ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(returnData); - SendUnified(&out,IMMEDIATE_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - } - } - else if (packet->data[1]==ID_RPC4_SIGNAL) - { - RakNet::RakString sharedIdentifier; - bsIn.ReadCompressed(sharedIdentifier); - DataStructures::HashIndex functionIndex; - functionIndex = localSlots.GetIndexOf(sharedIdentifier); - RakNet::BitStream serializedParameters; - bsIn.AlignReadToByteBoundary(); - bsIn.Read(&serializedParameters); - InvokeSignal(functionIndex, &serializedParameters, packet); - } - else - { - RakAssert(packet->data[1]==ID_RPC4_RETURN); - blockingReturnValue.Reset(); - blockingReturnValue.Read(bsIn); - gotBlockingReturnValue=true; - } - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - bool objectExists; - unsigned int index, index2; - index = localCallbacks.GetIndexFromKey(packet->data[0],&objectExists); - if (objectExists) - { - LocalCallback *lc; - lc = localCallbacks[index]; - for (index2=0; index2 < lc->functions.Size(); index2++) - { - RakNet::BitStream bsIn(packet->data, packet->length, false); - - DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(lc->functions[index2].C_String()); - if (skhi.IsInvalid()==false) - { - void ( *fp ) ( RakNet::BitStream *, Packet * ); - fp = registeredNonblockingFunctions.ItemAtIndex(skhi); - bsIn.AlignReadToByteBoundary(); - fp(&bsIn,packet); - } - } - } - - return RR_CONTINUE_PROCESSING; -} -DataStructures::HashIndex RPC4::GetLocalSlotIndex(const char *sharedIdentifier) -{ - return localSlots.GetIndexOf(sharedIdentifier); -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_RPC4Plugin==1 + +#include "RPC4Plugin.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "PacketizedTCP.h" +#include "RakSleep.h" +#include "RakNetDefines.h" +#include "DS_Queue.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(RPC4,RPC4); + +struct GlobalRegistration +{ + void ( *registerFunctionPointer ) ( RakNet::BitStream *userData, Packet *packet ); + void ( *registerBlockingFunctionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet ); + char functionName[RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH]; + MessageID messageId; + int callPriority; +}; +static GlobalRegistration globalRegistrationBuffer[RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS]; +static unsigned int globalRegistrationIndex=0; + +RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) +{ + RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); + unsigned int i; + for (i=0; uniqueID[i]; i++) + { + RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); + globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; + } + globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=functionPointer; + globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; + globalRegistrationBuffer[globalRegistrationIndex].callPriority=0xFFFFFFFF; + globalRegistrationIndex++; +} +RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority) +{ + RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); + unsigned int i; + for (i=0; uniqueID[i]; i++) + { + RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); + globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; + } + globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=functionPointer; + globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; + RakAssert(callPriority!=0xFFFFFFFF); + globalRegistrationBuffer[globalRegistrationIndex].callPriority=callPriority; + globalRegistrationIndex++; +} +RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )) +{ + RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); + unsigned int i; + for (i=0; uniqueID[i]; i++) + { + RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); + globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; + } + globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=0; + globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=functionPointer; + globalRegistrationIndex++; +} +RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, MessageID messageId) +{ + RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); + unsigned int i; + for (i=0; uniqueID[i]; i++) + { + RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); + globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; + } + globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=0; + globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; + globalRegistrationBuffer[globalRegistrationIndex].messageId=messageId; + globalRegistrationIndex++; +} + +enum RPC4Identifiers +{ + ID_RPC4_CALL, + ID_RPC4_RETURN, + ID_RPC4_SIGNAL, +}; +int RPC4::LocalSlotObjectComp( const LocalSlotObject &key, const LocalSlotObject &data ) +{ + if (key.callPriority>data.callPriority) + return -1; + if (key.callPriority==data.callPriority) + { + if (key.registrationCountmessageId) + return -1; + if (key > data->messageId) + return 1; + return 0; +} + +RPC4::RPC4() +{ + gotBlockingReturnValue=false; + nextSlotRegistrationCount=0; + interruptSignal=false; +} +RPC4::~RPC4() +{ + unsigned int i; + for (i=0; i < localCallbacks.Size(); i++) + { + RakNet::OP_DELETE(localCallbacks[i],_FILE_AND_LINE_); + } + + DataStructures::List keyList; + DataStructures::List outputList; + localSlots.GetAsList(outputList,keyList,_FILE_AND_LINE_); + unsigned int j; + for (j=0; j < outputList.Size(); j++) + { + RakNet::OP_DELETE(outputList[j],_FILE_AND_LINE_); + } + localSlots.Clear(_FILE_AND_LINE_); +} +bool RPC4::RegisterFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) +{ + DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(uniqueID); + if (skhi.IsInvalid()==false) + return false; + + registeredNonblockingFunctions.Push(uniqueID,functionPointer,_FILE_AND_LINE_); + return true; +} +void RPC4::RegisterSlot(const char *sharedIdentifier, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority) +{ + LocalSlotObject lso(nextSlotRegistrationCount++, callPriority, functionPointer); + DataStructures::HashIndex idx = GetLocalSlotIndex(sharedIdentifier); + LocalSlot *localSlot; + if (idx.IsInvalid()) + { + localSlot = RakNet::OP_NEW(_FILE_AND_LINE_); + localSlots.Push(sharedIdentifier, localSlot,_FILE_AND_LINE_); + } + else + { + localSlot=localSlots.ItemAtIndex(idx); + } + localSlot->slotObjects.Insert(lso,lso,true,_FILE_AND_LINE_); +} +bool RPC4::RegisterBlockingFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )) +{ + DataStructures::HashIndex skhi = registeredBlockingFunctions.GetIndexOf(uniqueID); + if (skhi.IsInvalid()==false) + return false; + + registeredBlockingFunctions.Push(uniqueID,functionPointer,_FILE_AND_LINE_); + return true; +} +void RPC4::RegisterLocalCallback(const char* uniqueID, MessageID messageId) +{ + bool objectExists; + unsigned int index; + LocalCallback *lc; + RakNet::RakString str; + str=uniqueID; + index = localCallbacks.GetIndexFromKey(messageId,&objectExists); + if (objectExists) + { + lc = localCallbacks[index]; + index = lc->functions.GetIndexFromKey(str,&objectExists); + if (objectExists==false) + lc->functions.InsertAtIndex(str,index,_FILE_AND_LINE_); + } + else + { + lc = RakNet::OP_NEW(_FILE_AND_LINE_); + lc->messageId=messageId; + lc->functions.Insert(str,str,false,_FILE_AND_LINE_); + localCallbacks.InsertAtIndex(lc,index,_FILE_AND_LINE_); + } +} +bool RPC4::UnregisterFunction(const char* uniqueID) +{ + void ( *f ) ( RakNet::BitStream *, Packet * ); + return registeredNonblockingFunctions.Pop(f,uniqueID,_FILE_AND_LINE_); +} +bool RPC4::UnregisterBlockingFunction(const char* uniqueID) +{ + void ( *f ) ( RakNet::BitStream *, RakNet::BitStream *,Packet * ); + return registeredBlockingFunctions.Pop(f,uniqueID,_FILE_AND_LINE_); +} +bool RPC4::UnregisterLocalCallback(const char* uniqueID, MessageID messageId) +{ + bool objectExists; + unsigned int index, index2; + LocalCallback *lc; + RakNet::RakString str; + str=uniqueID; + index = localCallbacks.GetIndexFromKey(messageId,&objectExists); + if (objectExists) + { + lc = localCallbacks[index]; + index2 = lc->functions.GetIndexFromKey(str,&objectExists); + if (objectExists) + { + lc->functions.RemoveAtIndex(index2); + if (lc->functions.Size()==0) + { + RakNet::OP_DELETE(lc,_FILE_AND_LINE_); + localCallbacks.RemoveAtIndex(index); + return true; + } + } + } + return false; +} +bool RPC4::UnregisterSlot(const char* sharedIdentifier) +{ + DataStructures::HashIndex hi = localSlots.GetIndexOf(sharedIdentifier); + if (hi.IsInvalid()==false) + { + LocalSlot *ls = localSlots.ItemAtIndex(hi); + RakNet::OP_DELETE(ls, _FILE_AND_LINE_); + localSlots.RemoveAtIndex(hi, _FILE_AND_LINE_); + return true; + } + + return false; +} +void RPC4::CallLoopback( const char* uniqueID, RakNet::BitStream * bitStream ) +{ + Packet *p=0; + + DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(uniqueID); + + if (skhi.IsInvalid()==true) + { + if (rakPeerInterface) + p=AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)+(unsigned int) strlen(uniqueID)+1); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + p=packetizedTCP->AllocatePacket(sizeof(MessageID)+sizeof(unsigned char)+(unsigned int) strlen(uniqueID)+1); +#endif + + if (rakPeerInterface) + p->guid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + p->guid=UNASSIGNED_RAKNET_GUID; +#endif + + p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->data[0]=ID_RPC_REMOTE_ERROR; + p->data[1]=RPC_ERROR_FUNCTION_NOT_REGISTERED; + strcpy((char*) p->data+2, uniqueID); + + PushBackPacketUnified(p,false); + + return; + } + + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_CALL); + out.WriteCompressed(uniqueID); + out.Write(false); // nonblocking + if (bitStream) + { + bitStream->ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(bitStream); + } + if (rakPeerInterface) + p=AllocatePacketUnified(out.GetNumberOfBytesUsed()); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + p=packetizedTCP->AllocatePacket(out.GetNumberOfBytesUsed()); +#endif + + if (rakPeerInterface) + p->guid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + p->guid=UNASSIGNED_RAKNET_GUID; +#endif + p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + p->systemAddress.systemIndex=(SystemIndex)-1; + memcpy(p->data,out.GetData(),out.GetNumberOfBytesUsed()); + PushBackPacketUnified(p,false); + return; +} +void RPC4::Call( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) +{ + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_CALL); + out.WriteCompressed(uniqueID); + out.Write(false); // Nonblocking + if (bitStream) + { + bitStream->ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(bitStream); + } + SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,broadcast); +} +bool RPC4::CallBlocking( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, RakNet::BitStream *returnData ) +{ + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_CALL); + out.WriteCompressed(uniqueID); + out.Write(true); // Blocking + if (bitStream) + { + bitStream->ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(bitStream); + } + RakAssert(returnData); + RakAssert(rakPeerInterface); + ConnectionState cs; + cs = rakPeerInterface->GetConnectionState(systemIdentifier); + if (cs!=IS_CONNECTED) + return false; + + SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,false); + + returnData->Reset(); + blockingReturnValue.Reset(); + gotBlockingReturnValue=false; + Packet *packet; + DataStructures::Queue packetQueue; + while (gotBlockingReturnValue==false) + { + // TODO - block, filter until gotBlockingReturnValue==true or ID_CONNECTION_LOST or ID_DISCONNECTION_NOTIFICXATION or ID_RPC_REMOTE_ERROR/RPC_ERROR_FUNCTION_NOT_REGISTERED + RakSleep(30); + + packet=rakPeerInterface->Receive(); + + if (packet) + { + if ( + (packet->data[0]==ID_CONNECTION_LOST || packet->data[0]==ID_DISCONNECTION_NOTIFICATION) && + ((systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID && packet->guid==systemIdentifier.rakNetGuid) || + (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && packet->systemAddress==systemIdentifier.systemAddress)) + ) + { + // Push back to head in reverse order + rakPeerInterface->PushBackPacket(packet,true); + while (packetQueue.Size()) + rakPeerInterface->PushBackPacket(packetQueue.Pop(),true); + return false; + } + else if (packet->data[0]==ID_RPC_REMOTE_ERROR && packet->data[1]==RPC_ERROR_FUNCTION_NOT_REGISTERED) + { + RakNet::RakString functionName; + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + bsIn.Read(functionName); + if (functionName==uniqueID) + { + // Push back to head in reverse order + rakPeerInterface->PushBackPacket(packet,true); + while (packetQueue.Size()) + rakPeerInterface->PushBackPacket(packetQueue.Pop(),true); + return false; + } + else + { + packetQueue.PushAtHead(packet,0,_FILE_AND_LINE_); + } + } + else + { + packetQueue.PushAtHead(packet,0,_FILE_AND_LINE_); + } + } + } + + returnData->Read(blockingReturnValue); + return true; +} +void RPC4::Signal(const char *sharedIdentifier, RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool invokeLocal) +{ + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_SIGNAL); + out.WriteCompressed(sharedIdentifier); + if (bitStream) + { + bitStream->ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(bitStream); + } + SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,broadcast); + + if (invokeLocal) + { + DataStructures::HashIndex functionIndex; + functionIndex = localSlots.GetIndexOf(sharedIdentifier); + if (functionIndex.IsInvalid()) + return; + + Packet p; + p.guid=rakPeerInterface->GetMyGUID(); + p.systemAddress=rakPeerInterface->GetMyBoundAddress(0); + p.wasGeneratedLocally=true; + RakNet::BitStream *bsptr, bstemp; + if (bitStream) + { + bitStream->ResetReadPointer(); + p.length=bitStream->GetNumberOfBytesUsed(); + p.bitSize=bitStream->GetNumberOfBitsUsed(); + bsptr=bitStream; + } + else + { + p.length=0; + p.bitSize=0; + bsptr=&bstemp; + } + InvokeSignal(functionIndex, bsptr, &p); + } +} +void RPC4::InvokeSignal(DataStructures::HashIndex functionIndex, RakNet::BitStream *serializedParameters, Packet *packet) +{ + if (functionIndex.IsInvalid()) + return; + + interruptSignal=false; + LocalSlot *localSlot = localSlots.ItemAtIndex(functionIndex); + unsigned int i; + i=0; + while (i < localSlot->slotObjects.Size()) + { + localSlot->slotObjects[i].functionPointer(serializedParameters, packet); + + // Not threadsafe + if (interruptSignal==true) + break; + + serializedParameters->ResetReadPointer(); + + i++; + } +} +void RPC4::InterruptSignal(void) +{ + interruptSignal=true; +} +void RPC4::OnAttach(void) +{ + unsigned int i; + for (i=0; i < globalRegistrationIndex; i++) + { + if (globalRegistrationBuffer[i].registerFunctionPointer) + { + if (globalRegistrationBuffer[i].callPriority==(int)0xFFFFFFFF) + RegisterFunction(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerFunctionPointer); + else + RegisterSlot(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerFunctionPointer, globalRegistrationBuffer[i].callPriority); + } + else if (globalRegistrationBuffer[i].registerBlockingFunctionPointer) + RegisterBlockingFunction(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerBlockingFunctionPointer); + else + RegisterLocalCallback(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].messageId); + } +} +PluginReceiveResult RPC4::OnReceive(Packet *packet) +{ + if (packet->data[0]==ID_RPC_PLUGIN) + { + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + + if (packet->data[1]==ID_RPC4_CALL) + { + RakNet::RakString functionName; + bsIn.ReadCompressed(functionName); + bool isBlocking=false; + bsIn.Read(isBlocking); + if (isBlocking==false) + { + DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(functionName.C_String()); + if (skhi.IsInvalid()) + { + RakNet::BitStream bsOut; + bsOut.Write((unsigned char) ID_RPC_REMOTE_ERROR); + bsOut.Write((unsigned char) RPC_ERROR_FUNCTION_NOT_REGISTERED); + bsOut.Write(functionName.C_String(),(unsigned int) functionName.GetLength()+1); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + void ( *fp ) ( RakNet::BitStream *, Packet * ); + fp = registeredNonblockingFunctions.ItemAtIndex(skhi); + bsIn.AlignReadToByteBoundary(); + fp(&bsIn,packet); + } + else + { + DataStructures::HashIndex skhi = registeredBlockingFunctions.GetIndexOf(functionName.C_String()); + if (skhi.IsInvalid()) + { + RakNet::BitStream bsOut; + bsOut.Write((unsigned char) ID_RPC_REMOTE_ERROR); + bsOut.Write((unsigned char) RPC_ERROR_FUNCTION_NOT_REGISTERED); + bsOut.Write(functionName.C_String(),(unsigned int) functionName.GetLength()+1); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + void ( *fp ) ( RakNet::BitStream *, RakNet::BitStream *, Packet * ); + fp = registeredBlockingFunctions.ItemAtIndex(skhi); + RakNet::BitStream returnData; + bsIn.AlignReadToByteBoundary(); + fp(&bsIn, &returnData, packet); + + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_RETURN); + returnData.ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(returnData); + SendUnified(&out,IMMEDIATE_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + } + } + else if (packet->data[1]==ID_RPC4_SIGNAL) + { + RakNet::RakString sharedIdentifier; + bsIn.ReadCompressed(sharedIdentifier); + DataStructures::HashIndex functionIndex; + functionIndex = localSlots.GetIndexOf(sharedIdentifier); + RakNet::BitStream serializedParameters; + bsIn.AlignReadToByteBoundary(); + bsIn.Read(&serializedParameters); + InvokeSignal(functionIndex, &serializedParameters, packet); + } + else + { + RakAssert(packet->data[1]==ID_RPC4_RETURN); + blockingReturnValue.Reset(); + blockingReturnValue.Read(bsIn); + gotBlockingReturnValue=true; + } + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + bool objectExists; + unsigned int index, index2; + index = localCallbacks.GetIndexFromKey(packet->data[0],&objectExists); + if (objectExists) + { + LocalCallback *lc; + lc = localCallbacks[index]; + for (index2=0; index2 < lc->functions.Size(); index2++) + { + RakNet::BitStream bsIn(packet->data, packet->length, false); + + DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(lc->functions[index2].C_String()); + if (skhi.IsInvalid()==false) + { + void ( *fp ) ( RakNet::BitStream *, Packet * ); + fp = registeredNonblockingFunctions.ItemAtIndex(skhi); + bsIn.AlignReadToByteBoundary(); + fp(&bsIn,packet); + } + } + } + + return RR_CONTINUE_PROCESSING; +} +DataStructures::HashIndex RPC4::GetLocalSlotIndex(const char *sharedIdentifier) +{ + return localSlots.GetIndexOf(sharedIdentifier); +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/RPC4Plugin.h b/project/lib_projects/raknet/jni/RaknetSources/RPC4Plugin.h index d5296a6..cce5978 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RPC4Plugin.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RPC4Plugin.h @@ -1,236 +1,236 @@ -/// \file -/// \brief Remote procedure call, supporting C functions only. No external dependencies required. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_RPC4Plugin==1 - -#ifndef __RPC_4_PLUGIN_H -#define __RPC_4_PLUGIN_H - -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "RakNetTypes.h" -#include "BitStream.h" -#include "RakString.h" -#include "NetworkIDObject.h" -#include "DS_Hash.h" -#include "DS_OrderedList.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// \defgroup RPC_PLUGIN_GROUP RPC -/// \brief Remote procedure calls, without external dependencies. -/// \details This should not be used at the same time as RPC3. This is a less functional version of RPC3, and is here for users that do not want the Boost dependency of RPC3. -/// \ingroup PLUGINS_GROUP - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -class NetworkIDManager; - - /// \brief Error codes returned by a remote system as to why an RPC function call cannot execute - /// \details Error code follows packet ID ID_RPC_REMOTE_ERROR, that is packet->data[1]
- /// Name of the function will be appended starting at packet->data[2] - /// \ingroup RPC_PLUGIN_GROUP - enum RPCErrorCodes - { - /// Named function was not registered with RegisterFunction(). Check your spelling. - RPC_ERROR_FUNCTION_NOT_REGISTERED, - }; - - /// \brief Instantiate this class globally if you want to register a function with RPC4 at the global space - class RAK_DLL_EXPORT RPC4GlobalRegistration - { - public: - /// \brief Queue a call to RPC4::RegisterFunction() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. - RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )); - - /// \brief Queue a call to RPC4::RegisterSlot() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. - RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority); - - /// \brief Queue a call to RPC4::RegisterBlockingFunction() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. - RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )); - - /// \brief Queue a call to RPC4::RegisterLocalCallback() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. - RPC4GlobalRegistration(const char* uniqueID, MessageID messageId); - }; - - /// \brief The RPC4 plugin is just an association between a C function pointer and a string. - /// \details It is for users that want to use RPC, but do not want to use boost. - /// You do not have the automatic serialization or other features of RPC3, and C++ member calls are not supported. - /// \note You cannot use RPC4 at the same time as RPC3Plugin - /// \ingroup RPC_PLUGIN_GROUP - class RAK_DLL_EXPORT RPC4 : public PluginInterface2 - { - public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(RPC4) - - // Constructor - RPC4(); - - // Destructor - virtual ~RPC4(); - - /// \deprecated Use RegisterSlot - /// \brief Register a function pointer to be callable from a remote system - /// \details The hash of the function name will be stored as an association with the function pointer - /// When a call is made to call this function from the \a Call() or CallLoopback() function, the function pointer will be invoked with the passed bitStream to Call() and the actual Packet that RakNet got. - /// \sa RegisterPacketCallback() - /// \param[in] uniqueID Identifier to be associated with \a functionPointer. If this identifier is already in use, the call will return false. - /// \param[in] functionPointer C function pointer to be called - /// \return True if the hash of uniqueID is not in use, false otherwise. - bool RegisterFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )); - - /// Register a slot, which is a function pointer to one or more implementations that supports this function signature - /// When a signal occurs, all slots with the same identifier are called. - /// \param[in] sharedIdentifier A string to identify the slot. Recommended to be the same as the name of the function. - /// \param[in] functionPtr Pointer to the function. For C, just pass the name of the function. For C++, use ARPC_REGISTER_CPP_FUNCTION - /// \param[in] callPriority Slots are called by order of the highest callPriority first. For slots with the same priority, they are called in the order they are registered - void RegisterSlot(const char *sharedIdentifier, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority); - - /// \brief Same as \a RegisterFunction, but is called with CallBlocking() instead of Call() and returns a value to the caller - bool RegisterBlockingFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )); - - /// \deprecated Use RegisterSlot and invoke on self only when the packet you want arrives - /// When a RakNet Packet with the specified identifier is returned, execute CallLoopback() on a function previously registered with RegisterFunction() - /// For example, you could call "OnClosedConnection" whenever you get ID_DISCONNECTION_NOTIFICATION or ID_CONNECTION_LOST - /// \param[in] uniqueID Identifier passed to RegisterFunction() - /// \param[in] messageId What RakNet packet ID to call on, for example ID_DISCONNECTION_NOTIFICATION or ID_CONNECTION_LOST - void RegisterLocalCallback(const char* uniqueID, MessageID messageId); - - /// \brief Unregister a function pointer previously registered with RegisterFunction() - /// \param[in] Identifier originally passed to RegisterFunction() - /// \return True if the hash of uniqueID was in use, and hence removed. false otherwise. - bool UnregisterFunction(const char* uniqueID); - - /// \brief Same as UnregisterFunction, except for a blocking function - bool UnregisterBlockingFunction(const char* uniqueID); - - /// Remove the association created with RegisterPacketCallback() - /// \param[in] uniqueID Identifier passed as uniqueID to RegisterLocalCallback() - /// \param[in] messageId Identifier passed as messageId to RegisterLocalCallback() - /// \return True if the combination of uniqueID and messageId was in use, and hence removed - bool UnregisterLocalCallback(const char* uniqueID, MessageID messageId); - - /// Remove the association created with RegisterSlot() - /// \param[in] sharedIdentifier Identifier passed as sharedIdentifier to RegisterSlot() - bool UnregisterSlot(const char* sharedIdentifier); - - /// \deprecated Use RegisterSlot() and Signal() with your own RakNetGUID as the send target - /// Send to the attached instance of RakPeer. See RakPeerInterface::SendLoopback() - /// \param[in] Identifier originally passed to RegisterFunction() on the local system - /// \param[in] bitStream bitStream encoded data to send to the function callback - void CallLoopback( const char* uniqueID, RakNet::BitStream * bitStream ); - - /// \deprecated, use Signal() - /// Send to the specified remote instance of RakPeer. - /// \param[in] uniqueID Identifier originally passed to RegisterFunction() on the remote system(s) - /// \param[in] bitStream bitStream encoded data to send to the function callback - /// \param[in] priority See RakPeerInterface::Send() - /// \param[in] reliability See RakPeerInterface::Send() - /// \param[in] orderingChannel See RakPeerInterface::Send() - /// \param[in] systemIdentifier See RakPeerInterface::Send() - /// \param[in] broadcast See RakPeerInterface::Send() - void Call( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); - - /// \brief Same as call, but don't return until the remote system replies. - /// Broadcasting parameter does not exist, this can only call one remote system - /// \note This function does not return until the remote system responds, disconnects, or was never connected to begin with - /// \param[in] Identifier originally passed to RegisterBlockingFunction() on the remote system(s) - /// \param[in] bitStream bitStream encoded data to send to the function callback - /// \param[in] priority See RakPeerInterface::Send() - /// \param[in] reliability See RakPeerInterface::Send() - /// \param[in] orderingChannel See RakPeerInterface::Send() - /// \param[in] systemIdentifier See RakPeerInterface::Send() - /// \param[out] returnData Written to by the function registered with RegisterBlockingFunction. - /// \return true if successfully called. False on disconnect, function not registered, or not connected to begin with - bool CallBlocking( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, RakNet::BitStream *returnData ); - - /// Calls zero or more functions identified by sharedIdentifier registered with RegisterSlot() - /// \param[in] sharedIdentifier parameter of the same name passed to RegisterSlot() on the remote system - /// \param[in] bitStream bitStream encoded data to send to the function callback - /// \param[in] priority See RakPeerInterface::Send() - /// \param[in] reliability See RakPeerInterface::Send() - /// \param[in] orderingChannel See RakPeerInterface::Send() - /// \param[in] systemIdentifier See RakPeerInterface::Send() - /// \param[in] broadcast See RakPeerInterface::Send() - /// \param[in] invokeLocal If true, also sends to self. - void Signal(const char *sharedIdentifier, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool invokeLocal); - - /// If called while processing a slot, no further slots for the currently executing signal will be executed - void InterruptSignal(void); - - /// \internal - struct LocalCallback - { - MessageID messageId; - DataStructures::OrderedList functions; - }; - static int LocalCallbackComp(const MessageID &key, LocalCallback* const &data ); - - /// \internal - // Callable object, along with priority to call relative to other objects - struct LocalSlotObject - { - LocalSlotObject() {} - LocalSlotObject(unsigned int _registrationCount,int _callPriority, void ( *_functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) - {registrationCount=_registrationCount;callPriority=_callPriority;functionPointer=_functionPointer;} - ~LocalSlotObject() {} - - // Used so slots are called in the order they are registered - unsigned int registrationCount; - int callPriority; - void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ); - }; - - static int LocalSlotObjectComp( const LocalSlotObject &key, const LocalSlotObject &data ); - - /// \internal - struct LocalSlot - { - DataStructures::OrderedList slotObjects; - }; - DataStructures::Hash localSlots; - - protected: - - // -------------------------------------------------------------------------------------------- - // Packet handling functions - // -------------------------------------------------------------------------------------------- - virtual void OnAttach(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - - DataStructures::Hash registeredNonblockingFunctions; - DataStructures::Hash registeredBlockingFunctions; - DataStructures::OrderedList localCallbacks; - - RakNet::BitStream blockingReturnValue; - bool gotBlockingReturnValue; - - DataStructures::HashIndex GetLocalSlotIndex(const char *sharedIdentifier); - - /// Used so slots are called in the order they are registered - unsigned int nextSlotRegistrationCount; - - bool interruptSignal; - - void InvokeSignal(DataStructures::HashIndex functionIndex, RakNet::BitStream *serializedParameters, Packet *packet); - }; - -} // End namespace - -#endif - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Remote procedure call, supporting C functions only. No external dependencies required. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_RPC4Plugin==1 + +#ifndef __RPC_4_PLUGIN_H +#define __RPC_4_PLUGIN_H + +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "RakNetTypes.h" +#include "BitStream.h" +#include "RakString.h" +#include "NetworkIDObject.h" +#include "DS_Hash.h" +#include "DS_OrderedList.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// \defgroup RPC_PLUGIN_GROUP RPC +/// \brief Remote procedure calls, without external dependencies. +/// \details This should not be used at the same time as RPC3. This is a less functional version of RPC3, and is here for users that do not want the Boost dependency of RPC3. +/// \ingroup PLUGINS_GROUP + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +class NetworkIDManager; + + /// \brief Error codes returned by a remote system as to why an RPC function call cannot execute + /// \details Error code follows packet ID ID_RPC_REMOTE_ERROR, that is packet->data[1]
+ /// Name of the function will be appended starting at packet->data[2] + /// \ingroup RPC_PLUGIN_GROUP + enum RPCErrorCodes + { + /// Named function was not registered with RegisterFunction(). Check your spelling. + RPC_ERROR_FUNCTION_NOT_REGISTERED, + }; + + /// \brief Instantiate this class globally if you want to register a function with RPC4 at the global space + class RAK_DLL_EXPORT RPC4GlobalRegistration + { + public: + /// \brief Queue a call to RPC4::RegisterFunction() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. + RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )); + + /// \brief Queue a call to RPC4::RegisterSlot() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. + RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority); + + /// \brief Queue a call to RPC4::RegisterBlockingFunction() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. + RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )); + + /// \brief Queue a call to RPC4::RegisterLocalCallback() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. + RPC4GlobalRegistration(const char* uniqueID, MessageID messageId); + }; + + /// \brief The RPC4 plugin is just an association between a C function pointer and a string. + /// \details It is for users that want to use RPC, but do not want to use boost. + /// You do not have the automatic serialization or other features of RPC3, and C++ member calls are not supported. + /// \note You cannot use RPC4 at the same time as RPC3Plugin + /// \ingroup RPC_PLUGIN_GROUP + class RAK_DLL_EXPORT RPC4 : public PluginInterface2 + { + public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(RPC4) + + // Constructor + RPC4(); + + // Destructor + virtual ~RPC4(); + + /// \deprecated Use RegisterSlot + /// \brief Register a function pointer to be callable from a remote system + /// \details The hash of the function name will be stored as an association with the function pointer + /// When a call is made to call this function from the \a Call() or CallLoopback() function, the function pointer will be invoked with the passed bitStream to Call() and the actual Packet that RakNet got. + /// \sa RegisterPacketCallback() + /// \param[in] uniqueID Identifier to be associated with \a functionPointer. If this identifier is already in use, the call will return false. + /// \param[in] functionPointer C function pointer to be called + /// \return True if the hash of uniqueID is not in use, false otherwise. + bool RegisterFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )); + + /// Register a slot, which is a function pointer to one or more implementations that supports this function signature + /// When a signal occurs, all slots with the same identifier are called. + /// \param[in] sharedIdentifier A string to identify the slot. Recommended to be the same as the name of the function. + /// \param[in] functionPtr Pointer to the function. For C, just pass the name of the function. For C++, use ARPC_REGISTER_CPP_FUNCTION + /// \param[in] callPriority Slots are called by order of the highest callPriority first. For slots with the same priority, they are called in the order they are registered + void RegisterSlot(const char *sharedIdentifier, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority); + + /// \brief Same as \a RegisterFunction, but is called with CallBlocking() instead of Call() and returns a value to the caller + bool RegisterBlockingFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )); + + /// \deprecated Use RegisterSlot and invoke on self only when the packet you want arrives + /// When a RakNet Packet with the specified identifier is returned, execute CallLoopback() on a function previously registered with RegisterFunction() + /// For example, you could call "OnClosedConnection" whenever you get ID_DISCONNECTION_NOTIFICATION or ID_CONNECTION_LOST + /// \param[in] uniqueID Identifier passed to RegisterFunction() + /// \param[in] messageId What RakNet packet ID to call on, for example ID_DISCONNECTION_NOTIFICATION or ID_CONNECTION_LOST + void RegisterLocalCallback(const char* uniqueID, MessageID messageId); + + /// \brief Unregister a function pointer previously registered with RegisterFunction() + /// \param[in] Identifier originally passed to RegisterFunction() + /// \return True if the hash of uniqueID was in use, and hence removed. false otherwise. + bool UnregisterFunction(const char* uniqueID); + + /// \brief Same as UnregisterFunction, except for a blocking function + bool UnregisterBlockingFunction(const char* uniqueID); + + /// Remove the association created with RegisterPacketCallback() + /// \param[in] uniqueID Identifier passed as uniqueID to RegisterLocalCallback() + /// \param[in] messageId Identifier passed as messageId to RegisterLocalCallback() + /// \return True if the combination of uniqueID and messageId was in use, and hence removed + bool UnregisterLocalCallback(const char* uniqueID, MessageID messageId); + + /// Remove the association created with RegisterSlot() + /// \param[in] sharedIdentifier Identifier passed as sharedIdentifier to RegisterSlot() + bool UnregisterSlot(const char* sharedIdentifier); + + /// \deprecated Use RegisterSlot() and Signal() with your own RakNetGUID as the send target + /// Send to the attached instance of RakPeer. See RakPeerInterface::SendLoopback() + /// \param[in] Identifier originally passed to RegisterFunction() on the local system + /// \param[in] bitStream bitStream encoded data to send to the function callback + void CallLoopback( const char* uniqueID, RakNet::BitStream * bitStream ); + + /// \deprecated, use Signal() + /// Send to the specified remote instance of RakPeer. + /// \param[in] uniqueID Identifier originally passed to RegisterFunction() on the remote system(s) + /// \param[in] bitStream bitStream encoded data to send to the function callback + /// \param[in] priority See RakPeerInterface::Send() + /// \param[in] reliability See RakPeerInterface::Send() + /// \param[in] orderingChannel See RakPeerInterface::Send() + /// \param[in] systemIdentifier See RakPeerInterface::Send() + /// \param[in] broadcast See RakPeerInterface::Send() + void Call( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); + + /// \brief Same as call, but don't return until the remote system replies. + /// Broadcasting parameter does not exist, this can only call one remote system + /// \note This function does not return until the remote system responds, disconnects, or was never connected to begin with + /// \param[in] Identifier originally passed to RegisterBlockingFunction() on the remote system(s) + /// \param[in] bitStream bitStream encoded data to send to the function callback + /// \param[in] priority See RakPeerInterface::Send() + /// \param[in] reliability See RakPeerInterface::Send() + /// \param[in] orderingChannel See RakPeerInterface::Send() + /// \param[in] systemIdentifier See RakPeerInterface::Send() + /// \param[out] returnData Written to by the function registered with RegisterBlockingFunction. + /// \return true if successfully called. False on disconnect, function not registered, or not connected to begin with + bool CallBlocking( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, RakNet::BitStream *returnData ); + + /// Calls zero or more functions identified by sharedIdentifier registered with RegisterSlot() + /// \param[in] sharedIdentifier parameter of the same name passed to RegisterSlot() on the remote system + /// \param[in] bitStream bitStream encoded data to send to the function callback + /// \param[in] priority See RakPeerInterface::Send() + /// \param[in] reliability See RakPeerInterface::Send() + /// \param[in] orderingChannel See RakPeerInterface::Send() + /// \param[in] systemIdentifier See RakPeerInterface::Send() + /// \param[in] broadcast See RakPeerInterface::Send() + /// \param[in] invokeLocal If true, also sends to self. + void Signal(const char *sharedIdentifier, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool invokeLocal); + + /// If called while processing a slot, no further slots for the currently executing signal will be executed + void InterruptSignal(void); + + /// \internal + struct LocalCallback + { + MessageID messageId; + DataStructures::OrderedList functions; + }; + static int LocalCallbackComp(const MessageID &key, LocalCallback* const &data ); + + /// \internal + // Callable object, along with priority to call relative to other objects + struct LocalSlotObject + { + LocalSlotObject() {} + LocalSlotObject(unsigned int _registrationCount,int _callPriority, void ( *_functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) + {registrationCount=_registrationCount;callPriority=_callPriority;functionPointer=_functionPointer;} + ~LocalSlotObject() {} + + // Used so slots are called in the order they are registered + unsigned int registrationCount; + int callPriority; + void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ); + }; + + static int LocalSlotObjectComp( const LocalSlotObject &key, const LocalSlotObject &data ); + + /// \internal + struct LocalSlot + { + DataStructures::OrderedList slotObjects; + }; + DataStructures::Hash localSlots; + + protected: + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual void OnAttach(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + + DataStructures::Hash registeredNonblockingFunctions; + DataStructures::Hash registeredBlockingFunctions; + DataStructures::OrderedList localCallbacks; + + RakNet::BitStream blockingReturnValue; + bool gotBlockingReturnValue; + + DataStructures::HashIndex GetLocalSlotIndex(const char *sharedIdentifier); + + /// Used so slots are called in the order they are registered + unsigned int nextSlotRegistrationCount; + + bool interruptSignal; + + void InvokeSignal(DataStructures::HashIndex functionIndex, RakNet::BitStream *serializedParameters, Packet *packet); + }; + +} // End namespace + +#endif + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/Rackspace.cpp b/project/lib_projects/raknet/jni/RaknetSources/Rackspace.cpp index 9d0dfe8..13541ce 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Rackspace.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/Rackspace.cpp @@ -1,655 +1,655 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_Rackspace==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "Rackspace.h" -#include "RakString.h" -#include "TCPInterface.h" - -using namespace RakNet; - -Rackspace::Rackspace() -{ - tcpInterface=0; -} - -Rackspace::~Rackspace() -{ - -} - -void Rackspace::AddEventCallback(RackspaceEventCallback *callback) -{ - unsigned int idx = eventCallbacks.GetIndexOf(callback); - if (idx == (unsigned int)-1) - eventCallbacks.Push(callback,_FILE_AND_LINE_); -} -void Rackspace::RemoveEventCallback(RackspaceEventCallback *callback) -{ - unsigned int idx = eventCallbacks.GetIndexOf(callback); - if (idx != (unsigned int)-1) - eventCallbacks.RemoveAtIndex(idx); -} -void Rackspace::ClearEventCallbacks(void) -{ - eventCallbacks.Clear(true, _FILE_AND_LINE_); -} -SystemAddress Rackspace::Authenticate(TCPInterface *_tcpInterface, const char *_authenticationURL, const char *_rackspaceCloudUsername, const char *_apiAccessKey) -{ - unsigned int index = GetOperationOfTypeIndex(RO_CONNECT_AND_AUTHENTICATE); - if (index!=(unsigned int)-1) - { - // In progress - return operations[index].connectionAddress; - } - - tcpInterface=_tcpInterface; - - rackspaceCloudUsername=_rackspaceCloudUsername; - apiAccessKey=_apiAccessKey; - - unsigned int i; - - RackspaceOperation ro; - ro.type=RO_CONNECT_AND_AUTHENTICATE; - ro.isPendingAuthentication=false; - - RakAssert(tcpInterface->WasStarted()); - ro.connectionAddress=tcpInterface->Connect(_authenticationURL,443,true); - if (ro.connectionAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnConnectionAttemptFailure(RO_CONNECT_AND_AUTHENTICATE, _authenticationURL); - - return UNASSIGNED_SYSTEM_ADDRESS; - } - -#if OPEN_SSL_CLIENT_SUPPORT==1 - tcpInterface->StartSSLClient(ro.connectionAddress); -#endif - - RakNet::RakString command( - "GET /v1.0 HTTP/1.1\n" - "Host: %s\n" - "X-Auth-User: %s\n" - "X-Auth-Key: %s\n\n" - ,_authenticationURL, _rackspaceCloudUsername, _apiAccessKey); - tcpInterface->Send(command.C_String(), (unsigned int) command.GetLength(), ro.connectionAddress, false); - - operations.Insert(ro,_FILE_AND_LINE_); - return ro.connectionAddress; -} - -const char * Rackspace::EventTypeToString(RackspaceEventType eventType) -{ - switch (eventType) - { - case RET_Success_200: - return "Success_200"; - case RET_Success_201: - return "Success_201"; - case RET_Success_202: - return "Success_202"; - case RET_Success_203: - return "Success_203"; - case RET_Success_204: - return "Success_204"; - case RET_Cloud_Servers_Fault_500: - return "Cloud_Servers_Fault_500"; - case RET_Service_Unavailable_503: - return "Service_Unavailable_503"; - case RET_Unauthorized_401: - return "Unauthorized_401"; - case RET_Bad_Request_400: - return "Bad_Request_400"; - case RET_Over_Limit_413: - return "Over_Limit_413"; - case RET_Bad_Media_Type_415: - return "Bad_Media_Type_415"; - case RET_Item_Not_Found_404: - return "Item_Not_Found_404"; - case RET_Build_In_Progress_409: - return "Build_In_Progress_409"; - case RET_Resize_Not_Allowed_403: - return "Resize_Not_Allowed_403"; - case RET_Connection_Closed_Without_Reponse: - return "Connection_Closed_Without_Reponse"; - case RET_Unknown_Failure: - return "Unknown_Failure"; - } - return "Unknown event type (bug)"; -} -void Rackspace::AddOperation(RackspaceOperationType type, RakNet::RakString httpCommand, RakNet::RakString operation, RakNet::RakString xml) -{ - RackspaceOperation ro; - ro.type=type; - ro.httpCommand=httpCommand; - ro.operation=operation; - ro.xml=xml; - ro.isPendingAuthentication=HasOperationOfType(RO_CONNECT_AND_AUTHENTICATE); - if (ro.isPendingAuthentication==false) - { - if (ExecuteOperation(ro)) - operations.Insert(ro,_FILE_AND_LINE_); - } - else - operations.Insert(ro,_FILE_AND_LINE_); -} -void Rackspace::ListServers(void) -{ - AddOperation(RO_LIST_SERVERS, "GET", "servers", ""); -} -void Rackspace::ListServersWithDetails(void) -{ - AddOperation(RO_LIST_SERVERS_WITH_DETAILS, "GET", "servers/detail", ""); -} -void Rackspace::CreateServer(RakNet::RakString name, RakNet::RakString imageId, RakNet::RakString flavorId) -{ - RakNet::RakString xml( - "" - "" - "" - ,name.C_String() ,imageId.C_String(), flavorId.C_String()); - AddOperation(RO_CREATE_SERVER, "POST", "servers", xml); -} -void Rackspace::GetServerDetails(RakNet::RakString serverId) -{ - AddOperation(RO_GET_SERVER_DETAILS, "GET", RakNet::RakString("servers/%s", serverId.C_String()), ""); -} -void Rackspace::UpdateServerNameOrPassword(RakNet::RakString serverId, RakNet::RakString newName, RakNet::RakString newPassword) -{ - if (newName.IsEmpty() && newPassword.IsEmpty()) - return; - RakNet::RakString xml( - "" - "" - "", - rebootType.C_String()); - - AddOperation(RO_REBOOT_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::RebuildServer(RakNet::RakString serverId, RakNet::RakString imageId) -{ - RakNet::RakString xml( - "" - "", - imageId.C_String()); - - AddOperation(RO_REBUILD_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::ResizeServer(RakNet::RakString serverId, RakNet::RakString flavorId) -{ - RakNet::RakString xml( - "" - "", - flavorId.C_String()); - - AddOperation(RO_RESIZE_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::ConfirmResizedServer(RakNet::RakString serverId) -{ - RakNet::RakString xml( - "" - ""); - AddOperation(RO_CONFIRM_RESIZED_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::RevertResizedServer(RakNet::RakString serverId) -{ - RakNet::RakString xml( - "" - ""); - AddOperation(RO_REVERT_RESIZED_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::ListFlavors(void) -{ - AddOperation(RO_LIST_FLAVORS, "GET", "flavors", ""); -} -void Rackspace::GetFlavorDetails(RakNet::RakString flavorId) -{ - AddOperation(RO_GET_FLAVOR_DETAILS, "GET", RakNet::RakString("flavors/%s", flavorId.C_String()), ""); -} -void Rackspace::ListImages(void) -{ - AddOperation(RO_LIST_IMAGES, "GET", "images", ""); -} -void Rackspace::CreateImage(RakNet::RakString serverId, RakNet::RakString imageName) -{ - RakNet::RakString xml( - "" - "", - imageName.C_String(),serverId.C_String()); - - AddOperation(RO_CREATE_IMAGE, "POST", "images", xml); -} -void Rackspace::GetImageDetails(RakNet::RakString imageId) -{ - AddOperation(RO_GET_IMAGE_DETAILS, "GET", RakNet::RakString("images/%s", imageId.C_String()), ""); -} -void Rackspace::DeleteImage(RakNet::RakString imageId) -{ - AddOperation(RO_DELETE_IMAGE, "DELETE", RakNet::RakString("images/%s", imageId.C_String()), ""); -} -void Rackspace::ListSharedIPGroups(void) -{ - AddOperation(RO_LIST_SHARED_IP_GROUPS, "GET", "shared_ip_groups", ""); -} -void Rackspace::ListSharedIPGroupsWithDetails(void) -{ - AddOperation(RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS, "GET", "shared_ip_groups/detail", ""); -} -void Rackspace::CreateSharedIPGroup(RakNet::RakString name, RakNet::RakString optionalServerId) -{ - RakNet::RakString xml( - "" - "", name.C_String()); - if (optionalServerId.IsEmpty()==false) - xml+=RakNet::RakString("", optionalServerId.C_String()); - xml+=""; - - AddOperation(RO_CREATE_SHARED_IP_GROUP, "POST", "shared_ip_groups", xml); -} -void Rackspace::GetSharedIPGroupDetails(RakNet::RakString groupId) -{ - AddOperation(RO_GET_SHARED_IP_GROUP_DETAILS, "GET", RakNet::RakString("shared_ip_groups/%s", groupId.C_String()), ""); -} -void Rackspace::DeleteSharedIPGroup(RakNet::RakString groupId) -{ - AddOperation(RO_DELETE_SHARED_IP_GROUP, "DELETE", RakNet::RakString("shared_ip_groups/%s", groupId.C_String()), ""); -} -void Rackspace::OnClosedConnection(SystemAddress systemAddress) -{ - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - return; - - unsigned int i, operationsIndex; - operationsIndex=0; - while (operationsIndex < operations.Size()) - { - if (operations[operationsIndex].isPendingAuthentication==false && operations[operationsIndex].connectionAddress==systemAddress) - { - RackspaceOperation ro = operations[operationsIndex]; - operations.RemoveAtIndex(operationsIndex); - - RakNet::RakString packetDataString = ro.incomingStream; - const char *packetData = packetDataString.C_String(); - - char resultCodeStr[32]; - int resultCodeInt; - - RackspaceEventType rackspaceEventType; - char *result; - result=strstr((char*) packetData, "HTTP/1.1 "); - if (result!=0) - { - result+=strlen("HTTP/1.1 "); - for (i=0; i < sizeof(resultCodeStr)-1 && result[i] && result[i]>='0' && result[i]<='9'; i++) - resultCodeStr[i]=result[i]; - resultCodeStr[i]=0; - resultCodeInt=atoi(resultCodeStr); - - switch (resultCodeInt) - { - case 200: rackspaceEventType=RET_Success_200; break; - case 201: rackspaceEventType=RET_Success_201; break; - case 202: rackspaceEventType=RET_Success_202; break; - case 203: rackspaceEventType=RET_Success_203; break; - case 204: rackspaceEventType=RET_Success_204; break; - case 500: rackspaceEventType=RET_Cloud_Servers_Fault_500; break; - case 503: rackspaceEventType=RET_Service_Unavailable_503; break; - case 401: rackspaceEventType=RET_Unauthorized_401; break; - case 400: rackspaceEventType=RET_Bad_Request_400; break; - case 413: rackspaceEventType=RET_Over_Limit_413; break; - case 415: rackspaceEventType=RET_Bad_Media_Type_415; break; - case 404: rackspaceEventType=RET_Item_Not_Found_404; break; - case 409: rackspaceEventType=RET_Build_In_Progress_409; break; - case 403: rackspaceEventType=RET_Resize_Not_Allowed_403; break; - default: rackspaceEventType=RET_Unknown_Failure; break; - } - } - else - { - rackspaceEventType=RET_Connection_Closed_Without_Reponse; - } - - switch (ro.type) - { - case RO_CONNECT_AND_AUTHENTICATE: - { - if (rackspaceEventType==RET_Success_204) - { - RakNet::RakString header; - ReadLine(packetData, "X-Server-Management-Url: ", serverManagementURL); - serverManagementURL.SplitURI(header, serverManagementDomain, serverManagementPath); - ReadLine(packetData, "X-Storage-Url: ", storageURL); - storageURL.SplitURI(header, storageDomain, storagePath); - ReadLine(packetData, "X-CDN-Management-Url: ", cdnManagementURL); - cdnManagementURL.SplitURI(header, cdnManagementDomain, cdnManagementPath); - ReadLine(packetData, "X-Auth-Token: ", authToken); - ReadLine(packetData, "X-Storage-Token: ", storageToken); - - operationsIndex=0; - while (operationsIndex < operations.Size()) - { - if (operations[operationsIndex].isPendingAuthentication==true) - { - operations[operationsIndex].isPendingAuthentication=false; - if (ExecuteOperation(operations[operationsIndex])==false) - { - operations.RemoveAtIndex(operationsIndex); - } - else - operationsIndex++; - } - else - operationsIndex++; - } - - // Restart in list - operationsIndex=0; - } - - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnAuthenticationResult(rackspaceEventType, (const char*) packetData); - - break; - } - case RO_LIST_SERVERS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListServersResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_SERVERS_WITH_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListServersWithDetailsResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_CREATE_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnCreateServerResult(rackspaceEventType, (const char*) packetData); - break; - } - - case RO_GET_SERVER_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnGetServerDetails(rackspaceEventType, (const char*) packetData); - break; - } - case RO_UPDATE_SERVER_NAME_OR_PASSWORD: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnUpdateServerNameOrPassword(rackspaceEventType, (const char*) packetData); - break; - } - case RO_DELETE_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnDeleteServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_SERVER_ADDRESSES: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListServerAddresses(rackspaceEventType, (const char*) packetData); - break; - } - case RO_SHARE_SERVER_ADDRESS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnShareServerAddress(rackspaceEventType, (const char*) packetData); - break; - } - case RO_DELETE_SERVER_ADDRESS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnDeleteServerAddress(rackspaceEventType, (const char*) packetData); - break; - } - case RO_REBOOT_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnRebootServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_REBUILD_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnRebuildServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_RESIZE_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnResizeServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_CONFIRM_RESIZED_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnConfirmResizedServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_REVERT_RESIZED_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnRevertResizedServer(rackspaceEventType, (const char*) packetData); - break; - } - - - case RO_LIST_FLAVORS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListFlavorsResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_GET_FLAVOR_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnGetFlavorDetailsResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_IMAGES: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListImagesResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_CREATE_IMAGE: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnCreateImageResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_GET_IMAGE_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnGetImageDetailsResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_DELETE_IMAGE: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnDeleteImageResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_SHARED_IP_GROUPS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListSharedIPGroups(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListSharedIPGroupsWithDetails(rackspaceEventType, (const char*) packetData); - break; - } - case RO_CREATE_SHARED_IP_GROUP: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnCreateSharedIPGroup(rackspaceEventType, (const char*) packetData); - break; - } - case RO_GET_SHARED_IP_GROUP_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnGetSharedIPGroupDetails(rackspaceEventType, (const char*) packetData); - break; - } - case RO_DELETE_SHARED_IP_GROUP: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnDeleteSharedIPGroup(rackspaceEventType, (const char*) packetData); - break; - } - default: - break; - - } - } - else - { - operationsIndex++; - } - } -} -void Rackspace::OnReceive(Packet *packet) -{ - unsigned int operationsIndex; - for (operationsIndex=0; operationsIndex < operations.Size(); operationsIndex++) - { - if (operations[operationsIndex].isPendingAuthentication==false && operations[operationsIndex].connectionAddress==packet->systemAddress) - { - operations[operationsIndex].incomingStream+=packet->data; - } - } -} -bool Rackspace::ExecuteOperation(RackspaceOperation &ro) -{ - if (ConnectToServerManagementDomain(ro)==false) - return false; - - RakNet::RakString command( - "%s %s/%s HTTP/1.1\n" - "Host: %s\n" - "Content-Type: application/xml\n" - "Content-Length: %i\n" - "Accept: application/xml\n" - "X-Auth-Token: %s\n", - ro.httpCommand.C_String(), serverManagementPath.C_String(), ro.operation.C_String(), serverManagementDomain.C_String(), - ro.xml.GetLength(), - authToken.C_String()); - - if (ro.xml.IsEmpty()==false) - { - command+="\n"; - command+=ro.xml; - command+="\n"; - } - - command+="\n"; - - //printf(command.C_String()); - - tcpInterface->Send(command.C_String(), (unsigned int) command.GetLength(), ro.connectionAddress, false); - return true; -} -void Rackspace::ReadLine(const char *data, const char *stringStart, RakNet::RakString &output) -{ - output.Clear(); - - char *result, *resultEnd; - - result=strstr((char*) data, stringStart); - result+=strlen(stringStart); - if (result==0) - { - RakAssert(0); - return; - } - - output=result; - resultEnd=result; - while (*resultEnd && (*resultEnd!='\r') && (*resultEnd!='\n') ) - resultEnd++; - output.Truncate((unsigned int) (resultEnd-result)); -} - - -bool Rackspace::ConnectToServerManagementDomain(RackspaceOperation &ro) -{ - unsigned int i; - - ro.connectionAddress=tcpInterface->Connect(serverManagementDomain.C_String(),443,true); - if (ro.connectionAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnConnectionAttemptFailure(ro.type, serverManagementURL); - return false; - } - -#if OPEN_SSL_CLIENT_SUPPORT==1 - tcpInterface->StartSSLClient(ro.connectionAddress); -#endif - - return true; -} -bool Rackspace::HasOperationOfType(RackspaceOperationType t) -{ - unsigned int i; - for (i=0; i < operations.Size(); i++) - { - if (operations[i].type==t) - return true; - } - return false; -} -unsigned int Rackspace::GetOperationOfTypeIndex(RackspaceOperationType t) -{ - unsigned int i; - for (i=0; i < operations.Size(); i++) - { - if (operations[i].type==t) - return i; - } - return (unsigned int) -1; -} - -#endif +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_Rackspace==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "Rackspace.h" +#include "RakString.h" +#include "TCPInterface.h" + +using namespace RakNet; + +Rackspace::Rackspace() +{ + tcpInterface=0; +} + +Rackspace::~Rackspace() +{ + +} + +void Rackspace::AddEventCallback(RackspaceEventCallback *callback) +{ + unsigned int idx = eventCallbacks.GetIndexOf(callback); + if (idx == (unsigned int)-1) + eventCallbacks.Push(callback,_FILE_AND_LINE_); +} +void Rackspace::RemoveEventCallback(RackspaceEventCallback *callback) +{ + unsigned int idx = eventCallbacks.GetIndexOf(callback); + if (idx != (unsigned int)-1) + eventCallbacks.RemoveAtIndex(idx); +} +void Rackspace::ClearEventCallbacks(void) +{ + eventCallbacks.Clear(true, _FILE_AND_LINE_); +} +SystemAddress Rackspace::Authenticate(TCPInterface *_tcpInterface, const char *_authenticationURL, const char *_rackspaceCloudUsername, const char *_apiAccessKey) +{ + unsigned int index = GetOperationOfTypeIndex(RO_CONNECT_AND_AUTHENTICATE); + if (index!=(unsigned int)-1) + { + // In progress + return operations[index].connectionAddress; + } + + tcpInterface=_tcpInterface; + + rackspaceCloudUsername=_rackspaceCloudUsername; + apiAccessKey=_apiAccessKey; + + unsigned int i; + + RackspaceOperation ro; + ro.type=RO_CONNECT_AND_AUTHENTICATE; + ro.isPendingAuthentication=false; + + RakAssert(tcpInterface->WasStarted()); + ro.connectionAddress=tcpInterface->Connect(_authenticationURL,443,true); + if (ro.connectionAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnConnectionAttemptFailure(RO_CONNECT_AND_AUTHENTICATE, _authenticationURL); + + return UNASSIGNED_SYSTEM_ADDRESS; + } + +#if OPEN_SSL_CLIENT_SUPPORT==1 + tcpInterface->StartSSLClient(ro.connectionAddress); +#endif + + RakNet::RakString command( + "GET /v1.0 HTTP/1.1\n" + "Host: %s\n" + "X-Auth-User: %s\n" + "X-Auth-Key: %s\n\n" + ,_authenticationURL, _rackspaceCloudUsername, _apiAccessKey); + tcpInterface->Send(command.C_String(), (unsigned int) command.GetLength(), ro.connectionAddress, false); + + operations.Insert(ro,_FILE_AND_LINE_); + return ro.connectionAddress; +} + +const char * Rackspace::EventTypeToString(RackspaceEventType eventType) +{ + switch (eventType) + { + case RET_Success_200: + return "Success_200"; + case RET_Success_201: + return "Success_201"; + case RET_Success_202: + return "Success_202"; + case RET_Success_203: + return "Success_203"; + case RET_Success_204: + return "Success_204"; + case RET_Cloud_Servers_Fault_500: + return "Cloud_Servers_Fault_500"; + case RET_Service_Unavailable_503: + return "Service_Unavailable_503"; + case RET_Unauthorized_401: + return "Unauthorized_401"; + case RET_Bad_Request_400: + return "Bad_Request_400"; + case RET_Over_Limit_413: + return "Over_Limit_413"; + case RET_Bad_Media_Type_415: + return "Bad_Media_Type_415"; + case RET_Item_Not_Found_404: + return "Item_Not_Found_404"; + case RET_Build_In_Progress_409: + return "Build_In_Progress_409"; + case RET_Resize_Not_Allowed_403: + return "Resize_Not_Allowed_403"; + case RET_Connection_Closed_Without_Reponse: + return "Connection_Closed_Without_Reponse"; + case RET_Unknown_Failure: + return "Unknown_Failure"; + } + return "Unknown event type (bug)"; +} +void Rackspace::AddOperation(RackspaceOperationType type, RakNet::RakString httpCommand, RakNet::RakString operation, RakNet::RakString xml) +{ + RackspaceOperation ro; + ro.type=type; + ro.httpCommand=httpCommand; + ro.operation=operation; + ro.xml=xml; + ro.isPendingAuthentication=HasOperationOfType(RO_CONNECT_AND_AUTHENTICATE); + if (ro.isPendingAuthentication==false) + { + if (ExecuteOperation(ro)) + operations.Insert(ro,_FILE_AND_LINE_); + } + else + operations.Insert(ro,_FILE_AND_LINE_); +} +void Rackspace::ListServers(void) +{ + AddOperation(RO_LIST_SERVERS, "GET", "servers", ""); +} +void Rackspace::ListServersWithDetails(void) +{ + AddOperation(RO_LIST_SERVERS_WITH_DETAILS, "GET", "servers/detail", ""); +} +void Rackspace::CreateServer(RakNet::RakString name, RakNet::RakString imageId, RakNet::RakString flavorId) +{ + RakNet::RakString xml( + "" + "" + "" + ,name.C_String() ,imageId.C_String(), flavorId.C_String()); + AddOperation(RO_CREATE_SERVER, "POST", "servers", xml); +} +void Rackspace::GetServerDetails(RakNet::RakString serverId) +{ + AddOperation(RO_GET_SERVER_DETAILS, "GET", RakNet::RakString("servers/%s", serverId.C_String()), ""); +} +void Rackspace::UpdateServerNameOrPassword(RakNet::RakString serverId, RakNet::RakString newName, RakNet::RakString newPassword) +{ + if (newName.IsEmpty() && newPassword.IsEmpty()) + return; + RakNet::RakString xml( + "" + "" + "", + rebootType.C_String()); + + AddOperation(RO_REBOOT_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::RebuildServer(RakNet::RakString serverId, RakNet::RakString imageId) +{ + RakNet::RakString xml( + "" + "", + imageId.C_String()); + + AddOperation(RO_REBUILD_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::ResizeServer(RakNet::RakString serverId, RakNet::RakString flavorId) +{ + RakNet::RakString xml( + "" + "", + flavorId.C_String()); + + AddOperation(RO_RESIZE_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::ConfirmResizedServer(RakNet::RakString serverId) +{ + RakNet::RakString xml( + "" + ""); + AddOperation(RO_CONFIRM_RESIZED_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::RevertResizedServer(RakNet::RakString serverId) +{ + RakNet::RakString xml( + "" + ""); + AddOperation(RO_REVERT_RESIZED_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::ListFlavors(void) +{ + AddOperation(RO_LIST_FLAVORS, "GET", "flavors", ""); +} +void Rackspace::GetFlavorDetails(RakNet::RakString flavorId) +{ + AddOperation(RO_GET_FLAVOR_DETAILS, "GET", RakNet::RakString("flavors/%s", flavorId.C_String()), ""); +} +void Rackspace::ListImages(void) +{ + AddOperation(RO_LIST_IMAGES, "GET", "images", ""); +} +void Rackspace::CreateImage(RakNet::RakString serverId, RakNet::RakString imageName) +{ + RakNet::RakString xml( + "" + "", + imageName.C_String(),serverId.C_String()); + + AddOperation(RO_CREATE_IMAGE, "POST", "images", xml); +} +void Rackspace::GetImageDetails(RakNet::RakString imageId) +{ + AddOperation(RO_GET_IMAGE_DETAILS, "GET", RakNet::RakString("images/%s", imageId.C_String()), ""); +} +void Rackspace::DeleteImage(RakNet::RakString imageId) +{ + AddOperation(RO_DELETE_IMAGE, "DELETE", RakNet::RakString("images/%s", imageId.C_String()), ""); +} +void Rackspace::ListSharedIPGroups(void) +{ + AddOperation(RO_LIST_SHARED_IP_GROUPS, "GET", "shared_ip_groups", ""); +} +void Rackspace::ListSharedIPGroupsWithDetails(void) +{ + AddOperation(RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS, "GET", "shared_ip_groups/detail", ""); +} +void Rackspace::CreateSharedIPGroup(RakNet::RakString name, RakNet::RakString optionalServerId) +{ + RakNet::RakString xml( + "" + "", name.C_String()); + if (optionalServerId.IsEmpty()==false) + xml+=RakNet::RakString("", optionalServerId.C_String()); + xml+=""; + + AddOperation(RO_CREATE_SHARED_IP_GROUP, "POST", "shared_ip_groups", xml); +} +void Rackspace::GetSharedIPGroupDetails(RakNet::RakString groupId) +{ + AddOperation(RO_GET_SHARED_IP_GROUP_DETAILS, "GET", RakNet::RakString("shared_ip_groups/%s", groupId.C_String()), ""); +} +void Rackspace::DeleteSharedIPGroup(RakNet::RakString groupId) +{ + AddOperation(RO_DELETE_SHARED_IP_GROUP, "DELETE", RakNet::RakString("shared_ip_groups/%s", groupId.C_String()), ""); +} +void Rackspace::OnClosedConnection(SystemAddress systemAddress) +{ + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + return; + + unsigned int i, operationsIndex; + operationsIndex=0; + while (operationsIndex < operations.Size()) + { + if (operations[operationsIndex].isPendingAuthentication==false && operations[operationsIndex].connectionAddress==systemAddress) + { + RackspaceOperation ro = operations[operationsIndex]; + operations.RemoveAtIndex(operationsIndex); + + RakNet::RakString packetDataString = ro.incomingStream; + const char *packetData = packetDataString.C_String(); + + char resultCodeStr[32]; + int resultCodeInt; + + RackspaceEventType rackspaceEventType; + char *result; + result=strstr((char*) packetData, "HTTP/1.1 "); + if (result!=0) + { + result+=strlen("HTTP/1.1 "); + for (i=0; i < sizeof(resultCodeStr)-1 && result[i] && result[i]>='0' && result[i]<='9'; i++) + resultCodeStr[i]=result[i]; + resultCodeStr[i]=0; + resultCodeInt=atoi(resultCodeStr); + + switch (resultCodeInt) + { + case 200: rackspaceEventType=RET_Success_200; break; + case 201: rackspaceEventType=RET_Success_201; break; + case 202: rackspaceEventType=RET_Success_202; break; + case 203: rackspaceEventType=RET_Success_203; break; + case 204: rackspaceEventType=RET_Success_204; break; + case 500: rackspaceEventType=RET_Cloud_Servers_Fault_500; break; + case 503: rackspaceEventType=RET_Service_Unavailable_503; break; + case 401: rackspaceEventType=RET_Unauthorized_401; break; + case 400: rackspaceEventType=RET_Bad_Request_400; break; + case 413: rackspaceEventType=RET_Over_Limit_413; break; + case 415: rackspaceEventType=RET_Bad_Media_Type_415; break; + case 404: rackspaceEventType=RET_Item_Not_Found_404; break; + case 409: rackspaceEventType=RET_Build_In_Progress_409; break; + case 403: rackspaceEventType=RET_Resize_Not_Allowed_403; break; + default: rackspaceEventType=RET_Unknown_Failure; break; + } + } + else + { + rackspaceEventType=RET_Connection_Closed_Without_Reponse; + } + + switch (ro.type) + { + case RO_CONNECT_AND_AUTHENTICATE: + { + if (rackspaceEventType==RET_Success_204) + { + RakNet::RakString header; + ReadLine(packetData, "X-Server-Management-Url: ", serverManagementURL); + serverManagementURL.SplitURI(header, serverManagementDomain, serverManagementPath); + ReadLine(packetData, "X-Storage-Url: ", storageURL); + storageURL.SplitURI(header, storageDomain, storagePath); + ReadLine(packetData, "X-CDN-Management-Url: ", cdnManagementURL); + cdnManagementURL.SplitURI(header, cdnManagementDomain, cdnManagementPath); + ReadLine(packetData, "X-Auth-Token: ", authToken); + ReadLine(packetData, "X-Storage-Token: ", storageToken); + + operationsIndex=0; + while (operationsIndex < operations.Size()) + { + if (operations[operationsIndex].isPendingAuthentication==true) + { + operations[operationsIndex].isPendingAuthentication=false; + if (ExecuteOperation(operations[operationsIndex])==false) + { + operations.RemoveAtIndex(operationsIndex); + } + else + operationsIndex++; + } + else + operationsIndex++; + } + + // Restart in list + operationsIndex=0; + } + + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnAuthenticationResult(rackspaceEventType, (const char*) packetData); + + break; + } + case RO_LIST_SERVERS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListServersResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_SERVERS_WITH_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListServersWithDetailsResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_CREATE_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnCreateServerResult(rackspaceEventType, (const char*) packetData); + break; + } + + case RO_GET_SERVER_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnGetServerDetails(rackspaceEventType, (const char*) packetData); + break; + } + case RO_UPDATE_SERVER_NAME_OR_PASSWORD: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnUpdateServerNameOrPassword(rackspaceEventType, (const char*) packetData); + break; + } + case RO_DELETE_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnDeleteServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_SERVER_ADDRESSES: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListServerAddresses(rackspaceEventType, (const char*) packetData); + break; + } + case RO_SHARE_SERVER_ADDRESS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnShareServerAddress(rackspaceEventType, (const char*) packetData); + break; + } + case RO_DELETE_SERVER_ADDRESS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnDeleteServerAddress(rackspaceEventType, (const char*) packetData); + break; + } + case RO_REBOOT_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnRebootServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_REBUILD_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnRebuildServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_RESIZE_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnResizeServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_CONFIRM_RESIZED_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnConfirmResizedServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_REVERT_RESIZED_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnRevertResizedServer(rackspaceEventType, (const char*) packetData); + break; + } + + + case RO_LIST_FLAVORS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListFlavorsResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_GET_FLAVOR_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnGetFlavorDetailsResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_IMAGES: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListImagesResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_CREATE_IMAGE: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnCreateImageResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_GET_IMAGE_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnGetImageDetailsResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_DELETE_IMAGE: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnDeleteImageResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_SHARED_IP_GROUPS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListSharedIPGroups(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListSharedIPGroupsWithDetails(rackspaceEventType, (const char*) packetData); + break; + } + case RO_CREATE_SHARED_IP_GROUP: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnCreateSharedIPGroup(rackspaceEventType, (const char*) packetData); + break; + } + case RO_GET_SHARED_IP_GROUP_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnGetSharedIPGroupDetails(rackspaceEventType, (const char*) packetData); + break; + } + case RO_DELETE_SHARED_IP_GROUP: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnDeleteSharedIPGroup(rackspaceEventType, (const char*) packetData); + break; + } + default: + break; + + } + } + else + { + operationsIndex++; + } + } +} +void Rackspace::OnReceive(Packet *packet) +{ + unsigned int operationsIndex; + for (operationsIndex=0; operationsIndex < operations.Size(); operationsIndex++) + { + if (operations[operationsIndex].isPendingAuthentication==false && operations[operationsIndex].connectionAddress==packet->systemAddress) + { + operations[operationsIndex].incomingStream+=packet->data; + } + } +} +bool Rackspace::ExecuteOperation(RackspaceOperation &ro) +{ + if (ConnectToServerManagementDomain(ro)==false) + return false; + + RakNet::RakString command( + "%s %s/%s HTTP/1.1\n" + "Host: %s\n" + "Content-Type: application/xml\n" + "Content-Length: %i\n" + "Accept: application/xml\n" + "X-Auth-Token: %s\n", + ro.httpCommand.C_String(), serverManagementPath.C_String(), ro.operation.C_String(), serverManagementDomain.C_String(), + ro.xml.GetLength(), + authToken.C_String()); + + if (ro.xml.IsEmpty()==false) + { + command+="\n"; + command+=ro.xml; + command+="\n"; + } + + command+="\n"; + + //printf(command.C_String()); + + tcpInterface->Send(command.C_String(), (unsigned int) command.GetLength(), ro.connectionAddress, false); + return true; +} +void Rackspace::ReadLine(const char *data, const char *stringStart, RakNet::RakString &output) +{ + output.Clear(); + + char *result, *resultEnd; + + result=strstr((char*) data, stringStart); + result+=strlen(stringStart); + if (result==0) + { + RakAssert(0); + return; + } + + output=result; + resultEnd=result; + while (*resultEnd && (*resultEnd!='\r') && (*resultEnd!='\n') ) + resultEnd++; + output.Truncate((unsigned int) (resultEnd-result)); +} + + +bool Rackspace::ConnectToServerManagementDomain(RackspaceOperation &ro) +{ + unsigned int i; + + ro.connectionAddress=tcpInterface->Connect(serverManagementDomain.C_String(),443,true); + if (ro.connectionAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnConnectionAttemptFailure(ro.type, serverManagementURL); + return false; + } + +#if OPEN_SSL_CLIENT_SUPPORT==1 + tcpInterface->StartSSLClient(ro.connectionAddress); +#endif + + return true; +} +bool Rackspace::HasOperationOfType(RackspaceOperationType t) +{ + unsigned int i; + for (i=0; i < operations.Size(); i++) + { + if (operations[i].type==t) + return true; + } + return false; +} +unsigned int Rackspace::GetOperationOfTypeIndex(RackspaceOperationType t) +{ + unsigned int i; + for (i=0; i < operations.Size(); i++) + { + if (operations[i].type==t) + return i; + } + return (unsigned int) -1; +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/Rackspace.h b/project/lib_projects/raknet/jni/RaknetSources/Rackspace.h index 145e55c..d2fc16e 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Rackspace.h +++ b/project/lib_projects/raknet/jni/RaknetSources/Rackspace.h @@ -1,405 +1,405 @@ -/// \file Rackspace.h -/// \brief Helper to class to manage Rackspace servers -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" - -#if _RAKNET_SUPPORT_Rackspace==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "Export.h" -#include "DS_List.h" -#include "RakNetTypes.h" -#include "DS_Queue.h" -#include "RakString.h" - -#ifndef __RACKSPACE_H -#define __RACKSPACE_H - -namespace RakNet -{ - - class TCPInterface; - struct Packet; - - /// \brief Result codes for Rackspace commands - /// /sa Rackspace::EventTypeToString() - enum RackspaceEventType - { - RET_Success_200, - RET_Success_201, - RET_Success_202, - RET_Success_203, - RET_Success_204, - RET_Cloud_Servers_Fault_500, - RET_Service_Unavailable_503, - RET_Unauthorized_401, - RET_Bad_Request_400, - RET_Over_Limit_413, - RET_Bad_Media_Type_415, - RET_Item_Not_Found_404, - RET_Build_In_Progress_409, - RET_Resize_Not_Allowed_403, - RET_Connection_Closed_Without_Reponse, - RET_Unknown_Failure, - }; - - /// \internal - enum RackspaceOperationType - { - RO_CONNECT_AND_AUTHENTICATE, - RO_LIST_SERVERS, - RO_LIST_SERVERS_WITH_DETAILS, - RO_CREATE_SERVER, - RO_GET_SERVER_DETAILS, - RO_UPDATE_SERVER_NAME_OR_PASSWORD, - RO_DELETE_SERVER, - RO_LIST_SERVER_ADDRESSES, - RO_SHARE_SERVER_ADDRESS, - RO_DELETE_SERVER_ADDRESS, - RO_REBOOT_SERVER, - RO_REBUILD_SERVER, - RO_RESIZE_SERVER, - RO_CONFIRM_RESIZED_SERVER, - RO_REVERT_RESIZED_SERVER, - RO_LIST_FLAVORS, - RO_GET_FLAVOR_DETAILS, - RO_LIST_IMAGES, - RO_CREATE_IMAGE, - RO_GET_IMAGE_DETAILS, - RO_DELETE_IMAGE, - RO_LIST_SHARED_IP_GROUPS, - RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS, - RO_CREATE_SHARED_IP_GROUP, - RO_GET_SHARED_IP_GROUP_DETAILS, - RO_DELETE_SHARED_IP_GROUP, - - RO_NONE, - }; - - /// \brief Callback interface to receive the results of operations - class RAK_DLL_EXPORT RackspaceEventCallback - { - public: - RackspaceEventCallback() {} - virtual ~RackspaceEventCallback() {} - virtual void OnAuthenticationResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListServersResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListServersWithDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnCreateServerResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnGetServerDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnUpdateServerNameOrPassword(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnDeleteServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListServerAddresses(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnShareServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnDeleteServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnRebootServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnRebuildServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnResizeServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnConfirmResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnRevertResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListFlavorsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnGetFlavorDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListImagesResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnCreateImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnGetImageDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnDeleteImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListSharedIPGroups(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListSharedIPGroupsWithDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnCreateSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnGetSharedIPGroupDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnDeleteSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - - virtual void OnConnectionAttemptFailure(RackspaceOperationType operationType, const char *url)=0; - }; - - /// \brief Callback interface to receive the results of operations, with a default result - class RAK_DLL_EXPORT RackspaceEventCallback_Default : public RackspaceEventCallback - { - public: - virtual void ExecuteDefault(const char *callbackName, RackspaceEventType eventType, const char *htmlAdditionalInfo) {(void) callbackName; (void) eventType; (void) htmlAdditionalInfo;} - - virtual void OnAuthenticationResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnAuthenticationResult", eventType, htmlAdditionalInfo);} - virtual void OnListServersResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServersResult", eventType, htmlAdditionalInfo);} - virtual void OnListServersWithDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServersWithDetailsResult", eventType, htmlAdditionalInfo);} - virtual void OnCreateServerResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateServerResult", eventType, htmlAdditionalInfo);} - virtual void OnGetServerDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetServerDetails", eventType, htmlAdditionalInfo);} - virtual void OnUpdateServerNameOrPassword(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnUpdateServerNameOrPassword", eventType, htmlAdditionalInfo);} - virtual void OnDeleteServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteServer", eventType, htmlAdditionalInfo);} - virtual void OnListServerAddresses(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServerAddresses", eventType, htmlAdditionalInfo);} - virtual void OnShareServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnShareServerAddress", eventType, htmlAdditionalInfo);} - virtual void OnDeleteServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteServerAddress", eventType, htmlAdditionalInfo);} - virtual void OnRebootServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRebootServer", eventType, htmlAdditionalInfo);} - virtual void OnRebuildServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRebuildServer", eventType, htmlAdditionalInfo);} - virtual void OnResizeServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnResizeServer", eventType, htmlAdditionalInfo);} - virtual void OnConfirmResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnConfirmResizedServer", eventType, htmlAdditionalInfo);} - virtual void OnRevertResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRevertResizedServer", eventType, htmlAdditionalInfo);} - virtual void OnListFlavorsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListFlavorsResult", eventType, htmlAdditionalInfo);} - virtual void OnGetFlavorDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetFlavorDetailsResult", eventType, htmlAdditionalInfo);} - virtual void OnListImagesResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListImagesResult", eventType, htmlAdditionalInfo);} - virtual void OnCreateImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateImageResult", eventType, htmlAdditionalInfo);} - virtual void OnGetImageDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetImageDetailsResult", eventType, htmlAdditionalInfo);} - virtual void OnDeleteImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteImageResult", eventType, htmlAdditionalInfo);} - virtual void OnListSharedIPGroups(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListSharedIPGroups", eventType, htmlAdditionalInfo);} - virtual void OnListSharedIPGroupsWithDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListSharedIPGroupsWithDetails", eventType, htmlAdditionalInfo);} - virtual void OnCreateSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateSharedIPGroup", eventType, htmlAdditionalInfo);} - virtual void OnGetSharedIPGroupDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetSharedIPGroupDetails", eventType, htmlAdditionalInfo);} - virtual void OnDeleteSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteSharedIPGroup", eventType, htmlAdditionalInfo);} - - virtual void OnConnectionAttemptFailure(RackspaceOperationType operationType, const char *url) {(void) operationType; (void) url;} - }; - - /// \brief Code that uses the TCPInterface class to communicate with the Rackspace API servers - /// \pre Compile RakNet with OPEN_SSL_CLIENT_SUPPORT set to 1 - /// \pre Packets returned from TCPInterface::OnReceive() must be passed to Rackspace::OnReceive() - /// \pre Packets returned from TCPInterface::HasLostConnection() must be passed to Rackspace::OnClosedConnection() - class RAK_DLL_EXPORT Rackspace - { - public: - Rackspace(); - ~Rackspace(); - - /// \brief Authenticate with Rackspace servers, required before executing any commands. - /// \details All requests to authenticate and operate against Cloud Servers are performed using SSL over HTTP (HTTPS) on TCP port 443. - /// Times out after 24 hours - if you get RET_Authenticate_Unauthorized in the RackspaceEventCallback callback, call again - /// \sa RackspaceEventCallback::OnAuthenticationResult() - /// \param[in] _tcpInterface An instance of TCPInterface, build with OPEN_SSL_CLIENT_SUPPORT 1 and already started - /// \param[in] _authenticationURL See http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf . US-based accounts authenticate through auth.api.rackspacecloud.com. UK-based accounts authenticate through lon.auth.api.rackspacecloud.com - /// \param[in] _rackspaceCloudUsername Username you registered with Rackspace on their website - /// \param[in] _apiAccessKey Obtain your API access key from the Rackspace Cloud Control Panel in the Your Account API Access section. - /// \return The address of the authentication server, or UNASSIGNED_SYSTEM_ADDRESS if the connection attempt failed - SystemAddress Authenticate(TCPInterface *_tcpInterface, const char *_authenticationURL, const char *_rackspaceCloudUsername, const char *_apiAccessKey); - - /// \brief Get a list of running servers - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListServersResult() - void ListServers(void); - - /// \brief Get a list of running servers, with extended details on each server - /// \sa GetServerDetails() - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListServersWithDetailsResult() - void ListServersWithDetails(void); - - /// \brief Create a server - /// \details Create a server with a given image (harddrive contents) and flavor (hardware configuration) - /// Get the available images with ListImages() - /// Get the available flavors with ListFlavors() - /// It is possible to configure the server in more detail. See the XML schema at http://docs.rackspacecloud.com/servers/api/v1.0 - /// You can execute such a custom command by calling AddOperation() manually. See the implementation of CreateServer for how to do so. - /// The server takes a while to build. Call GetServerDetails() to get the current build status. Server id to pass to GetServerDetails() is returned in the field - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnCreateServerResult() - /// \param[in] name Name of the server. Only alphanumeric characters, periods, and hyphens are valid. Server Name cannot start or end with a period or hyphen. - /// \param[in] imageId Which image (harddrive contents, including OS) to use - /// \param[in] flavorId Which flavor (hardware config) to use, primarily how much memory is available. - void CreateServer(RakNet::RakString name, RakNet::RakString imageId, RakNet::RakString flavorId); - - /// \brief Get details on a particular server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnGetServerDetailsResult() - /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. - void GetServerDetails(RakNet::RakString serverId); - - /// \brief Changes the name or password for a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnUpdateServerNameOrPasswordResult() - /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. - /// \param[in] newName The new server name. Leave blank to leave unchanged. Only alphanumeric characters, periods, and hyphens are valid. Server Name cannot start or end with a period or hyphen. - /// \param[in] newPassword The new server password. Leave blank to leave unchanged. - void UpdateServerNameOrPassword(RakNet::RakString serverId, RakNet::RakString newName, RakNet::RakString newPassword); - - /// \brief Deletes a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnDeleteServerResult() - /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. - void DeleteServer(RakNet::RakString serverId); - - /// \brief Lists the IP addresses available to a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListServerAddressesResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - void ListServerAddresses(RakNet::RakString serverId); - - /// \brief Shares an IP address with a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnShareServerAddressResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] ipAddress Which IP address. You can call ListServerAddresses() to get the list of addresses for the specified server - void ShareServerAddress(RakNet::RakString serverId, RakNet::RakString ipAddress); - - /// \brief Stops sharing an IP address with a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnDeleteServerAddressResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] ipAddress Which IP address. You can call ListServerAddresses() to get the list of addresses for the specified server - void DeleteServerAddress(RakNet::RakString serverId, RakNet::RakString ipAddress); - - /// \brief Reboots a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnRebootServerResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] rebootType Should be either "HARD" or "SOFT" - void RebootServer(RakNet::RakString serverId, RakNet::RakString rebootType); - - /// \brief Rebuilds a server with a different image (harddrive contents) - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnRebuildServerResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] imageId Which image (harddrive contents, including OS) to use - void RebuildServer(RakNet::RakString serverId, RakNet::RakString imageId); - - /// \brief Changes the hardware configuration of a server. This does not take effect until you call ConfirmResizedServer() - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnResizeServerResult() - /// \sa RevertResizedServer() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] flavorId Which flavor (hardware config) to use, primarily how much memory is available. - void ResizeServer(RakNet::RakString serverId, RakNet::RakString flavorId); - - /// \brief Confirm a resize for the specified server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnConfirmResizedServerResult() - /// \sa ResizeServer() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - void ConfirmResizedServer(RakNet::RakString serverId); - - /// \brief Reverts a resize for the specified server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnRevertResizedServerResult() - /// \sa ResizeServer() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - void RevertResizedServer(RakNet::RakString serverId); - - /// \brief List all flavors (hardware configs, primarily memory) - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListFlavorsResult() - void ListFlavors(void); - - /// \brief Get extended details about a specific flavor - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnGetFlavorDetailsResult() - /// \sa ListFlavors() - /// \param[in] flavorId Which flavor (hardware config) - void GetFlavorDetails(RakNet::RakString flavorId); - - /// \brief List all images (software configs, including operating systems), which includes images you create yourself - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListImagesResult() - /// \sa CreateImage() - void ListImages(void); - - /// \brief Images a running server. This essentially copies the harddrive, and lets you start a server with the same harddrive contents later - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnCreateImageResult() - /// \sa ListImages() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] imageName What to call this image - void CreateImage(RakNet::RakString serverId, RakNet::RakString imageName); - - /// \brief Get extended details about a particular image - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnGetImageDetailsResult() - /// \sa ListImages() - /// \param[in] imageId Which image - void GetImageDetails(RakNet::RakString imageId); - - /// \brief Delete a custom image created with CreateImage() - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnDeleteImageResult() - /// \sa ListImages() - /// \param[in] imageId Which image - void DeleteImage(RakNet::RakString imageId); - - /// \brief List IP groups - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListSharedIPGroupsResult() - void ListSharedIPGroups(void); - - /// \brief List IP groups with extended details - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListSharedIPGroupsWithDetailsResult() - void ListSharedIPGroupsWithDetails(void); - - // I don't know what this does - void CreateSharedIPGroup(RakNet::RakString name, RakNet::RakString optionalServerId); - // I don't know what this does - void GetSharedIPGroupDetails(RakNet::RakString groupId); - // I don't know what this does - void DeleteSharedIPGroup(RakNet::RakString groupId); - - /// \brief Adds a callback to the list of callbacks to be called when any of the above functions finish executing - /// The callbacks are called in the order they are added - void AddEventCallback(RackspaceEventCallback *callback); - /// \brief Removes a callback from the list of callbacks to be called when any of the above functions finish executing - /// The callbacks are called in the order they are added - void RemoveEventCallback(RackspaceEventCallback *callback); - /// \brief Removes all callbacks - void ClearEventCallbacks(void); - - /// Call this anytime TCPInterface returns a packet - void OnReceive(Packet *packet); - - /// Call this when TCPInterface returns something other than UNASSIGNED_SYSTEM_ADDRESS from HasLostConnection() - void OnClosedConnection(SystemAddress systemAddress); - - /// String representation of each RackspaceEventType - static const char * EventTypeToString(RackspaceEventType eventType); - - /// \brief Mostly for internal use, but you can use it to execute an operation with more complex xml if desired - /// See the Rackspace.cpp on how to use it - void AddOperation(RackspaceOperationType type, RakNet::RakString httpCommand, RakNet::RakString operation, RakNet::RakString xml); - protected: - - DataStructures::List eventCallbacks; - - struct RackspaceOperation - { - RackspaceOperationType type; - // RakNet::RakString stringInfo; - SystemAddress connectionAddress; - bool isPendingAuthentication; - RakNet::RakString incomingStream; - RakNet::RakString httpCommand; - RakNet::RakString operation; - RakNet::RakString xml; - }; - - TCPInterface *tcpInterface; - - // RackspaceOperationType currentOperation; - // DataStructures::Queue nextOperationQueue; - - DataStructures::List operations; - bool HasOperationOfType(RackspaceOperationType t); - unsigned int GetOperationOfTypeIndex(RackspaceOperationType t); - - RakNet::RakString serverManagementURL; - RakNet::RakString serverManagementDomain; - RakNet::RakString serverManagementPath; - RakNet::RakString storageURL; - RakNet::RakString storageDomain; - RakNet::RakString storagePath; - RakNet::RakString cdnManagementURL; - RakNet::RakString cdnManagementDomain; - RakNet::RakString cdnManagementPath; - - RakNet::RakString storageToken; - RakNet::RakString authToken; - RakNet::RakString rackspaceCloudUsername; - RakNet::RakString apiAccessKey; - - bool ExecuteOperation(RackspaceOperation &ro); - void ReadLine(const char *data, const char *stringStart, RakNet::RakString &output); - bool ConnectToServerManagementDomain(RackspaceOperation &ro); - - - }; - -} // namespace RakNet - -#endif // __RACKSPACE_API_H - -#endif // _RAKNET_SUPPORT_Rackspace +/// \file Rackspace.h +/// \brief Helper to class to manage Rackspace servers +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" + +#if _RAKNET_SUPPORT_Rackspace==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "Export.h" +#include "DS_List.h" +#include "RakNetTypes.h" +#include "DS_Queue.h" +#include "RakString.h" + +#ifndef __RACKSPACE_H +#define __RACKSPACE_H + +namespace RakNet +{ + + class TCPInterface; + struct Packet; + + /// \brief Result codes for Rackspace commands + /// /sa Rackspace::EventTypeToString() + enum RackspaceEventType + { + RET_Success_200, + RET_Success_201, + RET_Success_202, + RET_Success_203, + RET_Success_204, + RET_Cloud_Servers_Fault_500, + RET_Service_Unavailable_503, + RET_Unauthorized_401, + RET_Bad_Request_400, + RET_Over_Limit_413, + RET_Bad_Media_Type_415, + RET_Item_Not_Found_404, + RET_Build_In_Progress_409, + RET_Resize_Not_Allowed_403, + RET_Connection_Closed_Without_Reponse, + RET_Unknown_Failure, + }; + + /// \internal + enum RackspaceOperationType + { + RO_CONNECT_AND_AUTHENTICATE, + RO_LIST_SERVERS, + RO_LIST_SERVERS_WITH_DETAILS, + RO_CREATE_SERVER, + RO_GET_SERVER_DETAILS, + RO_UPDATE_SERVER_NAME_OR_PASSWORD, + RO_DELETE_SERVER, + RO_LIST_SERVER_ADDRESSES, + RO_SHARE_SERVER_ADDRESS, + RO_DELETE_SERVER_ADDRESS, + RO_REBOOT_SERVER, + RO_REBUILD_SERVER, + RO_RESIZE_SERVER, + RO_CONFIRM_RESIZED_SERVER, + RO_REVERT_RESIZED_SERVER, + RO_LIST_FLAVORS, + RO_GET_FLAVOR_DETAILS, + RO_LIST_IMAGES, + RO_CREATE_IMAGE, + RO_GET_IMAGE_DETAILS, + RO_DELETE_IMAGE, + RO_LIST_SHARED_IP_GROUPS, + RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS, + RO_CREATE_SHARED_IP_GROUP, + RO_GET_SHARED_IP_GROUP_DETAILS, + RO_DELETE_SHARED_IP_GROUP, + + RO_NONE, + }; + + /// \brief Callback interface to receive the results of operations + class RAK_DLL_EXPORT RackspaceEventCallback + { + public: + RackspaceEventCallback() {} + virtual ~RackspaceEventCallback() {} + virtual void OnAuthenticationResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListServersResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListServersWithDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnCreateServerResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnGetServerDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnUpdateServerNameOrPassword(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnDeleteServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListServerAddresses(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnShareServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnDeleteServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnRebootServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnRebuildServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnResizeServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnConfirmResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnRevertResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListFlavorsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnGetFlavorDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListImagesResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnCreateImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnGetImageDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnDeleteImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListSharedIPGroups(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListSharedIPGroupsWithDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnCreateSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnGetSharedIPGroupDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnDeleteSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + + virtual void OnConnectionAttemptFailure(RackspaceOperationType operationType, const char *url)=0; + }; + + /// \brief Callback interface to receive the results of operations, with a default result + class RAK_DLL_EXPORT RackspaceEventCallback_Default : public RackspaceEventCallback + { + public: + virtual void ExecuteDefault(const char *callbackName, RackspaceEventType eventType, const char *htmlAdditionalInfo) {(void) callbackName; (void) eventType; (void) htmlAdditionalInfo;} + + virtual void OnAuthenticationResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnAuthenticationResult", eventType, htmlAdditionalInfo);} + virtual void OnListServersResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServersResult", eventType, htmlAdditionalInfo);} + virtual void OnListServersWithDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServersWithDetailsResult", eventType, htmlAdditionalInfo);} + virtual void OnCreateServerResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateServerResult", eventType, htmlAdditionalInfo);} + virtual void OnGetServerDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetServerDetails", eventType, htmlAdditionalInfo);} + virtual void OnUpdateServerNameOrPassword(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnUpdateServerNameOrPassword", eventType, htmlAdditionalInfo);} + virtual void OnDeleteServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteServer", eventType, htmlAdditionalInfo);} + virtual void OnListServerAddresses(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServerAddresses", eventType, htmlAdditionalInfo);} + virtual void OnShareServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnShareServerAddress", eventType, htmlAdditionalInfo);} + virtual void OnDeleteServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteServerAddress", eventType, htmlAdditionalInfo);} + virtual void OnRebootServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRebootServer", eventType, htmlAdditionalInfo);} + virtual void OnRebuildServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRebuildServer", eventType, htmlAdditionalInfo);} + virtual void OnResizeServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnResizeServer", eventType, htmlAdditionalInfo);} + virtual void OnConfirmResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnConfirmResizedServer", eventType, htmlAdditionalInfo);} + virtual void OnRevertResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRevertResizedServer", eventType, htmlAdditionalInfo);} + virtual void OnListFlavorsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListFlavorsResult", eventType, htmlAdditionalInfo);} + virtual void OnGetFlavorDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetFlavorDetailsResult", eventType, htmlAdditionalInfo);} + virtual void OnListImagesResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListImagesResult", eventType, htmlAdditionalInfo);} + virtual void OnCreateImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateImageResult", eventType, htmlAdditionalInfo);} + virtual void OnGetImageDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetImageDetailsResult", eventType, htmlAdditionalInfo);} + virtual void OnDeleteImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteImageResult", eventType, htmlAdditionalInfo);} + virtual void OnListSharedIPGroups(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListSharedIPGroups", eventType, htmlAdditionalInfo);} + virtual void OnListSharedIPGroupsWithDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListSharedIPGroupsWithDetails", eventType, htmlAdditionalInfo);} + virtual void OnCreateSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateSharedIPGroup", eventType, htmlAdditionalInfo);} + virtual void OnGetSharedIPGroupDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetSharedIPGroupDetails", eventType, htmlAdditionalInfo);} + virtual void OnDeleteSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteSharedIPGroup", eventType, htmlAdditionalInfo);} + + virtual void OnConnectionAttemptFailure(RackspaceOperationType operationType, const char *url) {(void) operationType; (void) url;} + }; + + /// \brief Code that uses the TCPInterface class to communicate with the Rackspace API servers + /// \pre Compile RakNet with OPEN_SSL_CLIENT_SUPPORT set to 1 + /// \pre Packets returned from TCPInterface::OnReceive() must be passed to Rackspace::OnReceive() + /// \pre Packets returned from TCPInterface::HasLostConnection() must be passed to Rackspace::OnClosedConnection() + class RAK_DLL_EXPORT Rackspace + { + public: + Rackspace(); + ~Rackspace(); + + /// \brief Authenticate with Rackspace servers, required before executing any commands. + /// \details All requests to authenticate and operate against Cloud Servers are performed using SSL over HTTP (HTTPS) on TCP port 443. + /// Times out after 24 hours - if you get RET_Authenticate_Unauthorized in the RackspaceEventCallback callback, call again + /// \sa RackspaceEventCallback::OnAuthenticationResult() + /// \param[in] _tcpInterface An instance of TCPInterface, build with OPEN_SSL_CLIENT_SUPPORT 1 and already started + /// \param[in] _authenticationURL See http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf . US-based accounts authenticate through auth.api.rackspacecloud.com. UK-based accounts authenticate through lon.auth.api.rackspacecloud.com + /// \param[in] _rackspaceCloudUsername Username you registered with Rackspace on their website + /// \param[in] _apiAccessKey Obtain your API access key from the Rackspace Cloud Control Panel in the Your Account API Access section. + /// \return The address of the authentication server, or UNASSIGNED_SYSTEM_ADDRESS if the connection attempt failed + SystemAddress Authenticate(TCPInterface *_tcpInterface, const char *_authenticationURL, const char *_rackspaceCloudUsername, const char *_apiAccessKey); + + /// \brief Get a list of running servers + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListServersResult() + void ListServers(void); + + /// \brief Get a list of running servers, with extended details on each server + /// \sa GetServerDetails() + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListServersWithDetailsResult() + void ListServersWithDetails(void); + + /// \brief Create a server + /// \details Create a server with a given image (harddrive contents) and flavor (hardware configuration) + /// Get the available images with ListImages() + /// Get the available flavors with ListFlavors() + /// It is possible to configure the server in more detail. See the XML schema at http://docs.rackspacecloud.com/servers/api/v1.0 + /// You can execute such a custom command by calling AddOperation() manually. See the implementation of CreateServer for how to do so. + /// The server takes a while to build. Call GetServerDetails() to get the current build status. Server id to pass to GetServerDetails() is returned in the field + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnCreateServerResult() + /// \param[in] name Name of the server. Only alphanumeric characters, periods, and hyphens are valid. Server Name cannot start or end with a period or hyphen. + /// \param[in] imageId Which image (harddrive contents, including OS) to use + /// \param[in] flavorId Which flavor (hardware config) to use, primarily how much memory is available. + void CreateServer(RakNet::RakString name, RakNet::RakString imageId, RakNet::RakString flavorId); + + /// \brief Get details on a particular server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnGetServerDetailsResult() + /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. + void GetServerDetails(RakNet::RakString serverId); + + /// \brief Changes the name or password for a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnUpdateServerNameOrPasswordResult() + /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. + /// \param[in] newName The new server name. Leave blank to leave unchanged. Only alphanumeric characters, periods, and hyphens are valid. Server Name cannot start or end with a period or hyphen. + /// \param[in] newPassword The new server password. Leave blank to leave unchanged. + void UpdateServerNameOrPassword(RakNet::RakString serverId, RakNet::RakString newName, RakNet::RakString newPassword); + + /// \brief Deletes a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnDeleteServerResult() + /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. + void DeleteServer(RakNet::RakString serverId); + + /// \brief Lists the IP addresses available to a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListServerAddressesResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + void ListServerAddresses(RakNet::RakString serverId); + + /// \brief Shares an IP address with a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnShareServerAddressResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] ipAddress Which IP address. You can call ListServerAddresses() to get the list of addresses for the specified server + void ShareServerAddress(RakNet::RakString serverId, RakNet::RakString ipAddress); + + /// \brief Stops sharing an IP address with a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnDeleteServerAddressResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] ipAddress Which IP address. You can call ListServerAddresses() to get the list of addresses for the specified server + void DeleteServerAddress(RakNet::RakString serverId, RakNet::RakString ipAddress); + + /// \brief Reboots a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnRebootServerResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] rebootType Should be either "HARD" or "SOFT" + void RebootServer(RakNet::RakString serverId, RakNet::RakString rebootType); + + /// \brief Rebuilds a server with a different image (harddrive contents) + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnRebuildServerResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] imageId Which image (harddrive contents, including OS) to use + void RebuildServer(RakNet::RakString serverId, RakNet::RakString imageId); + + /// \brief Changes the hardware configuration of a server. This does not take effect until you call ConfirmResizedServer() + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnResizeServerResult() + /// \sa RevertResizedServer() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] flavorId Which flavor (hardware config) to use, primarily how much memory is available. + void ResizeServer(RakNet::RakString serverId, RakNet::RakString flavorId); + + /// \brief Confirm a resize for the specified server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnConfirmResizedServerResult() + /// \sa ResizeServer() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + void ConfirmResizedServer(RakNet::RakString serverId); + + /// \brief Reverts a resize for the specified server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnRevertResizedServerResult() + /// \sa ResizeServer() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + void RevertResizedServer(RakNet::RakString serverId); + + /// \brief List all flavors (hardware configs, primarily memory) + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListFlavorsResult() + void ListFlavors(void); + + /// \brief Get extended details about a specific flavor + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnGetFlavorDetailsResult() + /// \sa ListFlavors() + /// \param[in] flavorId Which flavor (hardware config) + void GetFlavorDetails(RakNet::RakString flavorId); + + /// \brief List all images (software configs, including operating systems), which includes images you create yourself + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListImagesResult() + /// \sa CreateImage() + void ListImages(void); + + /// \brief Images a running server. This essentially copies the harddrive, and lets you start a server with the same harddrive contents later + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnCreateImageResult() + /// \sa ListImages() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] imageName What to call this image + void CreateImage(RakNet::RakString serverId, RakNet::RakString imageName); + + /// \brief Get extended details about a particular image + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnGetImageDetailsResult() + /// \sa ListImages() + /// \param[in] imageId Which image + void GetImageDetails(RakNet::RakString imageId); + + /// \brief Delete a custom image created with CreateImage() + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnDeleteImageResult() + /// \sa ListImages() + /// \param[in] imageId Which image + void DeleteImage(RakNet::RakString imageId); + + /// \brief List IP groups + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListSharedIPGroupsResult() + void ListSharedIPGroups(void); + + /// \brief List IP groups with extended details + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListSharedIPGroupsWithDetailsResult() + void ListSharedIPGroupsWithDetails(void); + + // I don't know what this does + void CreateSharedIPGroup(RakNet::RakString name, RakNet::RakString optionalServerId); + // I don't know what this does + void GetSharedIPGroupDetails(RakNet::RakString groupId); + // I don't know what this does + void DeleteSharedIPGroup(RakNet::RakString groupId); + + /// \brief Adds a callback to the list of callbacks to be called when any of the above functions finish executing + /// The callbacks are called in the order they are added + void AddEventCallback(RackspaceEventCallback *callback); + /// \brief Removes a callback from the list of callbacks to be called when any of the above functions finish executing + /// The callbacks are called in the order they are added + void RemoveEventCallback(RackspaceEventCallback *callback); + /// \brief Removes all callbacks + void ClearEventCallbacks(void); + + /// Call this anytime TCPInterface returns a packet + void OnReceive(Packet *packet); + + /// Call this when TCPInterface returns something other than UNASSIGNED_SYSTEM_ADDRESS from HasLostConnection() + void OnClosedConnection(SystemAddress systemAddress); + + /// String representation of each RackspaceEventType + static const char * EventTypeToString(RackspaceEventType eventType); + + /// \brief Mostly for internal use, but you can use it to execute an operation with more complex xml if desired + /// See the Rackspace.cpp on how to use it + void AddOperation(RackspaceOperationType type, RakNet::RakString httpCommand, RakNet::RakString operation, RakNet::RakString xml); + protected: + + DataStructures::List eventCallbacks; + + struct RackspaceOperation + { + RackspaceOperationType type; + // RakNet::RakString stringInfo; + SystemAddress connectionAddress; + bool isPendingAuthentication; + RakNet::RakString incomingStream; + RakNet::RakString httpCommand; + RakNet::RakString operation; + RakNet::RakString xml; + }; + + TCPInterface *tcpInterface; + + // RackspaceOperationType currentOperation; + // DataStructures::Queue nextOperationQueue; + + DataStructures::List operations; + bool HasOperationOfType(RackspaceOperationType t); + unsigned int GetOperationOfTypeIndex(RackspaceOperationType t); + + RakNet::RakString serverManagementURL; + RakNet::RakString serverManagementDomain; + RakNet::RakString serverManagementPath; + RakNet::RakString storageURL; + RakNet::RakString storageDomain; + RakNet::RakString storagePath; + RakNet::RakString cdnManagementURL; + RakNet::RakString cdnManagementDomain; + RakNet::RakString cdnManagementPath; + + RakNet::RakString storageToken; + RakNet::RakString authToken; + RakNet::RakString rackspaceCloudUsername; + RakNet::RakString apiAccessKey; + + bool ExecuteOperation(RackspaceOperation &ro); + void ReadLine(const char *data, const char *stringStart, RakNet::RakString &output); + bool ConnectToServerManagementDomain(RackspaceOperation &ro); + + + }; + +} // namespace RakNet + +#endif // __RACKSPACE_API_H + +#endif // _RAKNET_SUPPORT_Rackspace diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakAlloca.h b/project/lib_projects/raknet/jni/RaknetSources/RakAlloca.h index b25e5ba..1647f27 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakAlloca.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakAlloca.h @@ -1,16 +1,16 @@ -#if defined(__FreeBSD__) -#include - - - - -#elif defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) -#include -#include -#elif defined(_WIN32) -#include -#else -#include -// Alloca needed on Ubuntu apparently -#include -#endif +#if defined(__FreeBSD__) +#include + + + + +#elif defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) +#include +#include +#elif defined(_WIN32) +#include +#else +#include +// Alloca needed on Ubuntu apparently +#include +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakAssert.h b/project/lib_projects/raknet/jni/RaknetSources/RakAssert.h index 4521d5f..f0cc96f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakAssert.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakAssert.h @@ -1,2 +1,2 @@ -#include -#include "RakNetDefines.h" +#include +#include "RakNetDefines.h" diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakMemoryOverride.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakMemoryOverride.cpp index 8e20f8f..45d77ab 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakMemoryOverride.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakMemoryOverride.cpp @@ -1,294 +1,294 @@ -#include "RakMemoryOverride.h" -#include "RakAssert.h" -#include - -#ifdef _RAKNET_SUPPORT_DL_MALLOC -#include "rdlmalloc.h" -#endif - - - - - -using namespace RakNet; - -#if _USE_RAK_MEMORY_OVERRIDE==1 - #if defined(malloc) - #pragma push_macro("malloc") - #undef malloc - #define RMO_MALLOC_UNDEF - #endif - - #if defined(realloc) - #pragma push_macro("realloc") - #undef realloc - #define RMO_REALLOC_UNDEF - #endif - - #if defined(free) - #pragma push_macro("free") - #undef free - #define RMO_FREE_UNDEF - #endif -#endif - -void DefaultOutOfMemoryHandler(const char *file, const long line) -{ - (void) file; - (void) line; - RakAssert(0); -} - -void * (*rakMalloc) (size_t size) = RakNet::_RakMalloc; -void* (*rakRealloc) (void *p, size_t size) = RakNet::_RakRealloc; -void (*rakFree) (void *p) = RakNet::_RakFree; -void* (*rakMalloc_Ex) (size_t size, const char *file, unsigned int line) = RakNet::_RakMalloc_Ex; -void* (*rakRealloc_Ex) (void *p, size_t size, const char *file, unsigned int line) = RakNet::_RakRealloc_Ex; -void (*rakFree_Ex) (void *p, const char *file, unsigned int line) = RakNet::_RakFree_Ex; -void (*notifyOutOfMemory) (const char *file, const long line)=DefaultOutOfMemoryHandler; -void * (*dlMallocMMap) (size_t size) = RakNet::_DLMallocMMap; -void * (*dlMallocDirectMMap) (size_t size) = RakNet::_DLMallocDirectMMap; -int (*dlMallocMUnmap) (void* ptr, size_t size) = RakNet::_DLMallocMUnmap; - -void SetMalloc( void* (*userFunction)(size_t size) ) -{ - rakMalloc=userFunction; -} -void SetRealloc( void* (*userFunction)(void *p, size_t size) ) -{ - rakRealloc=userFunction; -} -void SetFree( void (*userFunction)(void *p) ) -{ - rakFree=userFunction; -} -void SetMalloc_Ex( void* (*userFunction)(size_t size, const char *file, unsigned int line) ) -{ - rakMalloc_Ex=userFunction; -} -void SetRealloc_Ex( void* (*userFunction)(void *p, size_t size, const char *file, unsigned int line) ) -{ - rakRealloc_Ex=userFunction; -} -void SetFree_Ex( void (*userFunction)(void *p, const char *file, unsigned int line) ) -{ - rakFree_Ex=userFunction; -} -void SetNotifyOutOfMemory( void (*userFunction)(const char *file, const long line) ) -{ - notifyOutOfMemory=userFunction; -} -void SetDLMallocMMap( void* (*userFunction)(size_t size) ) -{ - dlMallocMMap=userFunction; -} -void SetDLMallocDirectMMap( void* (*userFunction)(size_t size) ) -{ - dlMallocDirectMMap=userFunction; -} -void SetDLMallocMUnmap( int (*userFunction)(void* ptr, size_t size) ) -{ - dlMallocMUnmap=userFunction; -} -void * (*GetMalloc()) (size_t size) -{ - return rakMalloc; -} -void * (*GetRealloc()) (void *p, size_t size) -{ - return rakRealloc; -} -void (*GetFree()) (void *p) -{ - return rakFree; -} -void * (*GetMalloc_Ex()) (size_t size, const char *file, unsigned int line) -{ - return rakMalloc_Ex; -} -void * (*GetRealloc_Ex()) (void *p, size_t size, const char *file, unsigned int line) -{ - return rakRealloc_Ex; -} -void (*GetFree_Ex()) (void *p, const char *file, unsigned int line) -{ - return rakFree_Ex; -} -void *(*GetDLMallocMMap())(size_t size) -{ - return dlMallocMMap; -} -void *(*GetDLMallocDirectMMap())(size_t size) -{ - return dlMallocDirectMMap; -} -int (*GetDLMallocMUnmap())(void* ptr, size_t size) -{ - return dlMallocMUnmap; -} -void* RakNet::_RakMalloc (size_t size) -{ - return malloc(size); -} - -void* RakNet::_RakRealloc (void *p, size_t size) -{ - return realloc(p,size); -} - -void RakNet::_RakFree (void *p) -{ - free(p); -} - -void* RakNet::_RakMalloc_Ex (size_t size, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - return malloc(size); -} - -void* RakNet::_RakRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - return realloc(p,size); -} - -void RakNet::_RakFree_Ex (void *p, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - free(p); -} -#ifdef _RAKNET_SUPPORT_DL_MALLOC -void * RakNet::_DLMallocMMap (size_t size) -{ - return RAK_MMAP_DEFAULT(size); -} -void * RakNet::_DLMallocDirectMMap (size_t size) -{ - return RAK_DIRECT_MMAP_DEFAULT(size); -} -int RakNet::_DLMallocMUnmap (void *p, size_t size) -{ - return RAK_MUNMAP_DEFAULT(p,size); -} - -static mspace rakNetFixedHeapMSpace=0; - -void* _DLMalloc(size_t size) -{ - return rak_mspace_malloc(rakNetFixedHeapMSpace,size); -} - -void* _DLRealloc(void *p, size_t size) -{ - return rak_mspace_realloc(rakNetFixedHeapMSpace,p,size); -} - -void _DLFree(void *p) -{ - if (p) - rak_mspace_free(rakNetFixedHeapMSpace,p); -} -void* _DLMalloc_Ex (size_t size, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - return rak_mspace_malloc(rakNetFixedHeapMSpace,size); -} - -void* _DLRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - return rak_mspace_realloc(rakNetFixedHeapMSpace,p,size); -} - -void _DLFree_Ex (void *p, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - if (p) - rak_mspace_free(rakNetFixedHeapMSpace,p); -} - -void UseRaknetFixedHeap(size_t initialCapacity, - void * (*yourMMapFunction) (size_t size), - void * (*yourDirectMMapFunction) (size_t size), - int (*yourMUnmapFunction) (void *p, size_t size)) -{ - SetDLMallocMMap(yourMMapFunction); - SetDLMallocDirectMMap(yourDirectMMapFunction); - SetDLMallocMUnmap(yourMUnmapFunction); - SetMalloc(_DLMalloc); - SetRealloc(_DLRealloc); - SetFree(_DLFree); - SetMalloc_Ex(_DLMalloc_Ex); - SetRealloc_Ex(_DLRealloc_Ex); - SetFree_Ex(_DLFree_Ex); - - rakNetFixedHeapMSpace=rak_create_mspace(initialCapacity, 0); -} -void FreeRakNetFixedHeap(void) -{ - if (rakNetFixedHeapMSpace) - { - rak_destroy_mspace(rakNetFixedHeapMSpace); - rakNetFixedHeapMSpace=0; - } - - SetMalloc(_RakMalloc); - SetRealloc(_RakRealloc); - SetFree(_RakFree); - SetMalloc_Ex(_RakMalloc_Ex); - SetRealloc_Ex(_RakRealloc_Ex); - SetFree_Ex(_RakFree_Ex); -} -#else -void * RakNet::_DLMallocMMap (size_t size) {(void) size; return 0;} -void * RakNet::_DLMallocDirectMMap (size_t size) {(void) size; return 0;} -int RakNet::_DLMallocMUnmap (void *p, size_t size) {(void) size; (void) p; return 0;} -void* _DLMalloc(size_t size) {(void) size; return 0;} -void* _DLRealloc(void *p, size_t size) {(void) p; (void) size; return 0;} -void _DLFree(void *p) {(void) p;} -void* _DLMalloc_Ex (size_t size, const char *file, unsigned int line) {(void) size; (void) file; (void) line; return 0;} -void* _DLRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) {(void) p; (void) size; (void) file; (void) line; return 0;} -void _DLFree_Ex (void *p, const char *file, unsigned int line) {(void) p; (void) file; (void) line;} - -void UseRaknetFixedHeap(size_t initialCapacity, - void * (*yourMMapFunction) (size_t size), - void * (*yourDirectMMapFunction) (size_t size), - int (*yourMUnmapFunction) (void *p, size_t size)) -{ - (void) initialCapacity; - (void) yourMMapFunction; - (void) yourDirectMMapFunction; - (void) yourMUnmapFunction; -} -void FreeRakNetFixedHeap(void) {} -#endif - -#if _USE_RAK_MEMORY_OVERRIDE==1 - - #pragma pop_macro("malloc") - #undef RMO_MALLOC_UNDEF - - - - #pragma pop_macro("realloc") - #undef RMO_REALLOC_UNDEF - - - - #pragma pop_macro("free") - #undef RMO_FREE_UNDEF - -#endif +#include "RakMemoryOverride.h" +#include "RakAssert.h" +#include + +#ifdef _RAKNET_SUPPORT_DL_MALLOC +#include "rdlmalloc.h" +#endif + + + + + +using namespace RakNet; + +#if _USE_RAK_MEMORY_OVERRIDE==1 + #if defined(malloc) + #pragma push_macro("malloc") + #undef malloc + #define RMO_MALLOC_UNDEF + #endif + + #if defined(realloc) + #pragma push_macro("realloc") + #undef realloc + #define RMO_REALLOC_UNDEF + #endif + + #if defined(free) + #pragma push_macro("free") + #undef free + #define RMO_FREE_UNDEF + #endif +#endif + +void DefaultOutOfMemoryHandler(const char *file, const long line) +{ + (void) file; + (void) line; + RakAssert(0); +} + +void * (*rakMalloc) (size_t size) = RakNet::_RakMalloc; +void* (*rakRealloc) (void *p, size_t size) = RakNet::_RakRealloc; +void (*rakFree) (void *p) = RakNet::_RakFree; +void* (*rakMalloc_Ex) (size_t size, const char *file, unsigned int line) = RakNet::_RakMalloc_Ex; +void* (*rakRealloc_Ex) (void *p, size_t size, const char *file, unsigned int line) = RakNet::_RakRealloc_Ex; +void (*rakFree_Ex) (void *p, const char *file, unsigned int line) = RakNet::_RakFree_Ex; +void (*notifyOutOfMemory) (const char *file, const long line)=DefaultOutOfMemoryHandler; +void * (*dlMallocMMap) (size_t size) = RakNet::_DLMallocMMap; +void * (*dlMallocDirectMMap) (size_t size) = RakNet::_DLMallocDirectMMap; +int (*dlMallocMUnmap) (void* ptr, size_t size) = RakNet::_DLMallocMUnmap; + +void SetMalloc( void* (*userFunction)(size_t size) ) +{ + rakMalloc=userFunction; +} +void SetRealloc( void* (*userFunction)(void *p, size_t size) ) +{ + rakRealloc=userFunction; +} +void SetFree( void (*userFunction)(void *p) ) +{ + rakFree=userFunction; +} +void SetMalloc_Ex( void* (*userFunction)(size_t size, const char *file, unsigned int line) ) +{ + rakMalloc_Ex=userFunction; +} +void SetRealloc_Ex( void* (*userFunction)(void *p, size_t size, const char *file, unsigned int line) ) +{ + rakRealloc_Ex=userFunction; +} +void SetFree_Ex( void (*userFunction)(void *p, const char *file, unsigned int line) ) +{ + rakFree_Ex=userFunction; +} +void SetNotifyOutOfMemory( void (*userFunction)(const char *file, const long line) ) +{ + notifyOutOfMemory=userFunction; +} +void SetDLMallocMMap( void* (*userFunction)(size_t size) ) +{ + dlMallocMMap=userFunction; +} +void SetDLMallocDirectMMap( void* (*userFunction)(size_t size) ) +{ + dlMallocDirectMMap=userFunction; +} +void SetDLMallocMUnmap( int (*userFunction)(void* ptr, size_t size) ) +{ + dlMallocMUnmap=userFunction; +} +void * (*GetMalloc()) (size_t size) +{ + return rakMalloc; +} +void * (*GetRealloc()) (void *p, size_t size) +{ + return rakRealloc; +} +void (*GetFree()) (void *p) +{ + return rakFree; +} +void * (*GetMalloc_Ex()) (size_t size, const char *file, unsigned int line) +{ + return rakMalloc_Ex; +} +void * (*GetRealloc_Ex()) (void *p, size_t size, const char *file, unsigned int line) +{ + return rakRealloc_Ex; +} +void (*GetFree_Ex()) (void *p, const char *file, unsigned int line) +{ + return rakFree_Ex; +} +void *(*GetDLMallocMMap())(size_t size) +{ + return dlMallocMMap; +} +void *(*GetDLMallocDirectMMap())(size_t size) +{ + return dlMallocDirectMMap; +} +int (*GetDLMallocMUnmap())(void* ptr, size_t size) +{ + return dlMallocMUnmap; +} +void* RakNet::_RakMalloc (size_t size) +{ + return malloc(size); +} + +void* RakNet::_RakRealloc (void *p, size_t size) +{ + return realloc(p,size); +} + +void RakNet::_RakFree (void *p) +{ + free(p); +} + +void* RakNet::_RakMalloc_Ex (size_t size, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + return malloc(size); +} + +void* RakNet::_RakRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + return realloc(p,size); +} + +void RakNet::_RakFree_Ex (void *p, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + free(p); +} +#ifdef _RAKNET_SUPPORT_DL_MALLOC +void * RakNet::_DLMallocMMap (size_t size) +{ + return RAK_MMAP_DEFAULT(size); +} +void * RakNet::_DLMallocDirectMMap (size_t size) +{ + return RAK_DIRECT_MMAP_DEFAULT(size); +} +int RakNet::_DLMallocMUnmap (void *p, size_t size) +{ + return RAK_MUNMAP_DEFAULT(p,size); +} + +static mspace rakNetFixedHeapMSpace=0; + +void* _DLMalloc(size_t size) +{ + return rak_mspace_malloc(rakNetFixedHeapMSpace,size); +} + +void* _DLRealloc(void *p, size_t size) +{ + return rak_mspace_realloc(rakNetFixedHeapMSpace,p,size); +} + +void _DLFree(void *p) +{ + if (p) + rak_mspace_free(rakNetFixedHeapMSpace,p); +} +void* _DLMalloc_Ex (size_t size, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + return rak_mspace_malloc(rakNetFixedHeapMSpace,size); +} + +void* _DLRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + return rak_mspace_realloc(rakNetFixedHeapMSpace,p,size); +} + +void _DLFree_Ex (void *p, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + if (p) + rak_mspace_free(rakNetFixedHeapMSpace,p); +} + +void UseRaknetFixedHeap(size_t initialCapacity, + void * (*yourMMapFunction) (size_t size), + void * (*yourDirectMMapFunction) (size_t size), + int (*yourMUnmapFunction) (void *p, size_t size)) +{ + SetDLMallocMMap(yourMMapFunction); + SetDLMallocDirectMMap(yourDirectMMapFunction); + SetDLMallocMUnmap(yourMUnmapFunction); + SetMalloc(_DLMalloc); + SetRealloc(_DLRealloc); + SetFree(_DLFree); + SetMalloc_Ex(_DLMalloc_Ex); + SetRealloc_Ex(_DLRealloc_Ex); + SetFree_Ex(_DLFree_Ex); + + rakNetFixedHeapMSpace=rak_create_mspace(initialCapacity, 0); +} +void FreeRakNetFixedHeap(void) +{ + if (rakNetFixedHeapMSpace) + { + rak_destroy_mspace(rakNetFixedHeapMSpace); + rakNetFixedHeapMSpace=0; + } + + SetMalloc(_RakMalloc); + SetRealloc(_RakRealloc); + SetFree(_RakFree); + SetMalloc_Ex(_RakMalloc_Ex); + SetRealloc_Ex(_RakRealloc_Ex); + SetFree_Ex(_RakFree_Ex); +} +#else +void * RakNet::_DLMallocMMap (size_t size) {(void) size; return 0;} +void * RakNet::_DLMallocDirectMMap (size_t size) {(void) size; return 0;} +int RakNet::_DLMallocMUnmap (void *p, size_t size) {(void) size; (void) p; return 0;} +void* _DLMalloc(size_t size) {(void) size; return 0;} +void* _DLRealloc(void *p, size_t size) {(void) p; (void) size; return 0;} +void _DLFree(void *p) {(void) p;} +void* _DLMalloc_Ex (size_t size, const char *file, unsigned int line) {(void) size; (void) file; (void) line; return 0;} +void* _DLRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) {(void) p; (void) size; (void) file; (void) line; return 0;} +void _DLFree_Ex (void *p, const char *file, unsigned int line) {(void) p; (void) file; (void) line;} + +void UseRaknetFixedHeap(size_t initialCapacity, + void * (*yourMMapFunction) (size_t size), + void * (*yourDirectMMapFunction) (size_t size), + int (*yourMUnmapFunction) (void *p, size_t size)) +{ + (void) initialCapacity; + (void) yourMMapFunction; + (void) yourDirectMMapFunction; + (void) yourMUnmapFunction; +} +void FreeRakNetFixedHeap(void) {} +#endif + +#if _USE_RAK_MEMORY_OVERRIDE==1 + + #pragma pop_macro("malloc") + #undef RMO_MALLOC_UNDEF + + + + #pragma pop_macro("realloc") + #undef RMO_REALLOC_UNDEF + + + + #pragma pop_macro("free") + #undef RMO_FREE_UNDEF + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakMemoryOverride.h b/project/lib_projects/raknet/jni/RaknetSources/RakMemoryOverride.h index 57b0de6..5eb84b8 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakMemoryOverride.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakMemoryOverride.h @@ -1,236 +1,236 @@ -/// \file -/// \brief If _USE_RAK_MEMORY_OVERRIDE is defined, memory allocations go through rakMalloc, rakRealloc, and rakFree -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAK_MEMORY_H -#define __RAK_MEMORY_H - -#include "Export.h" -#include "RakNetDefines.h" -#include - - - - - - - -#include "RakAlloca.h" - -// #if _USE_RAK_MEMORY_OVERRIDE==1 -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RMO_NEW_UNDEF -// #endif -// #endif - - -// These pointers are statically and globally defined in RakMemoryOverride.cpp -// Change them to point to your own allocators if you want. -// Use the functions for a DLL, or just reassign the variable if using source -extern RAK_DLL_EXPORT void * (*rakMalloc) (size_t size); -extern RAK_DLL_EXPORT void * (*rakRealloc) (void *p, size_t size); -extern RAK_DLL_EXPORT void (*rakFree) (void *p); -extern RAK_DLL_EXPORT void * (*rakMalloc_Ex) (size_t size, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void * (*rakRealloc_Ex) (void *p, size_t size, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void (*rakFree_Ex) (void *p, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void (*notifyOutOfMemory) (const char *file, const long line); -extern RAK_DLL_EXPORT void * (*dlMallocMMap) (size_t size); -extern RAK_DLL_EXPORT void * (*dlMallocDirectMMap) (size_t size); -extern RAK_DLL_EXPORT int (*dlMallocMUnmap) (void* ptr, size_t size); - -// Change to a user defined allocation function -void RAK_DLL_EXPORT SetMalloc( void* (*userFunction)(size_t size) ); -void RAK_DLL_EXPORT SetRealloc( void* (*userFunction)(void *p, size_t size) ); -void RAK_DLL_EXPORT SetFree( void (*userFunction)(void *p) ); -void RAK_DLL_EXPORT SetMalloc_Ex( void* (*userFunction)(size_t size, const char *file, unsigned int line) ); -void RAK_DLL_EXPORT SetRealloc_Ex( void* (*userFunction)(void *p, size_t size, const char *file, unsigned int line) ); -void RAK_DLL_EXPORT SetFree_Ex( void (*userFunction)(void *p, const char *file, unsigned int line) ); -// Change to a user defined out of memory function -void RAK_DLL_EXPORT SetNotifyOutOfMemory( void (*userFunction)(const char *file, const long line) ); -void RAK_DLL_EXPORT SetDLMallocMMap( void* (*userFunction)(size_t size) ); -void RAK_DLL_EXPORT SetDLMallocDirectMMap( void* (*userFunction)(size_t size) ); -void RAK_DLL_EXPORT SetDLMallocMUnmap( int (*userFunction)(void* ptr, size_t size) ); - -extern RAK_DLL_EXPORT void * (*GetMalloc()) (size_t size); -extern RAK_DLL_EXPORT void * (*GetRealloc()) (void *p, size_t size); -extern RAK_DLL_EXPORT void (*GetFree()) (void *p); -extern RAK_DLL_EXPORT void * (*GetMalloc_Ex()) (size_t size, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void * (*GetRealloc_Ex()) (void *p, size_t size, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void (*GetFree_Ex()) (void *p, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void *(*GetDLMallocMMap())(size_t size); -extern RAK_DLL_EXPORT void *(*GetDLMallocDirectMMap())(size_t size); -extern RAK_DLL_EXPORT int (*GetDLMallocMUnmap())(void* ptr, size_t size); - -namespace RakNet -{ - - template - RAK_DLL_EXPORT Type* OP_NEW(const char *file, unsigned int line) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type; - return t; -#else - (void) file; - (void) line; - return new Type; -#endif - } - - template - RAK_DLL_EXPORT Type* OP_NEW_1(const char *file, unsigned int line, const P1 &p1) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type(p1); - return t; -#else - (void) file; - (void) line; - return new Type(p1); -#endif - } - - template - RAK_DLL_EXPORT Type* OP_NEW_2(const char *file, unsigned int line, const P1 &p1, const P2 &p2) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type(p1, p2); - return t; -#else - (void) file; - (void) line; - return new Type(p1, p2); -#endif - } - - template - RAK_DLL_EXPORT Type* OP_NEW_3(const char *file, unsigned int line, const P1 &p1, const P2 &p2, const P3 &p3) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type(p1, p2, p3); - return t; -#else - (void) file; - (void) line; - return new Type(p1, p2, p3); -#endif - } - - template - RAK_DLL_EXPORT Type* OP_NEW_4(const char *file, unsigned int line, const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type(p1, p2, p3, p4); - return t; -#else - (void) file; - (void) line; - return new Type(p1, p2, p3, p4); -#endif - } - - - template - RAK_DLL_EXPORT Type* OP_NEW_ARRAY(const int count, const char *file, unsigned int line) - { - if (count==0) - return 0; - -#if _USE_RAK_MEMORY_OVERRIDE==1 -// Type *t; - char *buffer = (char *) (GetMalloc_Ex())(sizeof(int)+sizeof(Type)*count, file, line); - ((int*)buffer)[0]=count; - for (int i=0; i - RAK_DLL_EXPORT void OP_DELETE(Type *buff, const char *file, unsigned int line) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - if (buff==0) return; - buff->~Type(); - (GetFree_Ex())((char*)buff, file, line ); -#else - (void) file; - (void) line; - delete buff; -#endif - - } - - template - RAK_DLL_EXPORT void OP_DELETE_ARRAY(Type *buff, const char *file, unsigned int line) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - if (buff==0) - return; - - int count = ((int*)((char*)buff-sizeof(int)))[0]; - Type *t; - for (int i=0; i~Type(); - } - (GetFree_Ex())((char*)buff-sizeof(int), file, line ); -#else - (void) file; - (void) line; - delete [] buff; -#endif - - } - - void RAK_DLL_EXPORT * _RakMalloc (size_t size); - void RAK_DLL_EXPORT * _RakRealloc (void *p, size_t size); - void RAK_DLL_EXPORT _RakFree (void *p); - void RAK_DLL_EXPORT * _RakMalloc_Ex (size_t size, const char *file, unsigned int line); - void RAK_DLL_EXPORT * _RakRealloc_Ex (void *p, size_t size, const char *file, unsigned int line); - void RAK_DLL_EXPORT _RakFree_Ex (void *p, const char *file, unsigned int line); - void RAK_DLL_EXPORT * _DLMallocMMap (size_t size); - void RAK_DLL_EXPORT * _DLMallocDirectMMap (size_t size); - int RAK_DLL_EXPORT _DLMallocMUnmap (void *p, size_t size); - -} - -// Call to make RakNet allocate a large block of memory, and do all subsequent allocations in that memory block -// Initial and reallocations will be done through whatever function is pointed to by yourMMapFunction, and yourDirectMMapFunction (default is malloc) -// Allocations will be freed through whatever function is pointed to by yourMUnmapFunction (default free) -void UseRaknetFixedHeap(size_t initialCapacity, - void * (*yourMMapFunction) (size_t size) = RakNet::_DLMallocMMap, - void * (*yourDirectMMapFunction) (size_t size) = RakNet::_DLMallocDirectMMap, - int (*yourMUnmapFunction) (void *p, size_t size) = RakNet::_DLMallocMUnmap); - -// Free memory allocated from UseRaknetFixedHeap -void FreeRakNetFixedHeap(void); - -// #if _USE_RAK_MEMORY_OVERRIDE==1 -// #if defined(RMO_NEW_UNDEF) -// #pragma pop_macro("new") -// #undef RMO_NEW_UNDEF -// #endif -// #endif - -#endif +/// \file +/// \brief If _USE_RAK_MEMORY_OVERRIDE is defined, memory allocations go through rakMalloc, rakRealloc, and rakFree +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAK_MEMORY_H +#define __RAK_MEMORY_H + +#include "Export.h" +#include "RakNetDefines.h" +#include + + + + + + + +#include "RakAlloca.h" + +// #if _USE_RAK_MEMORY_OVERRIDE==1 +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RMO_NEW_UNDEF +// #endif +// #endif + + +// These pointers are statically and globally defined in RakMemoryOverride.cpp +// Change them to point to your own allocators if you want. +// Use the functions for a DLL, or just reassign the variable if using source +extern RAK_DLL_EXPORT void * (*rakMalloc) (size_t size); +extern RAK_DLL_EXPORT void * (*rakRealloc) (void *p, size_t size); +extern RAK_DLL_EXPORT void (*rakFree) (void *p); +extern RAK_DLL_EXPORT void * (*rakMalloc_Ex) (size_t size, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void * (*rakRealloc_Ex) (void *p, size_t size, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void (*rakFree_Ex) (void *p, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void (*notifyOutOfMemory) (const char *file, const long line); +extern RAK_DLL_EXPORT void * (*dlMallocMMap) (size_t size); +extern RAK_DLL_EXPORT void * (*dlMallocDirectMMap) (size_t size); +extern RAK_DLL_EXPORT int (*dlMallocMUnmap) (void* ptr, size_t size); + +// Change to a user defined allocation function +void RAK_DLL_EXPORT SetMalloc( void* (*userFunction)(size_t size) ); +void RAK_DLL_EXPORT SetRealloc( void* (*userFunction)(void *p, size_t size) ); +void RAK_DLL_EXPORT SetFree( void (*userFunction)(void *p) ); +void RAK_DLL_EXPORT SetMalloc_Ex( void* (*userFunction)(size_t size, const char *file, unsigned int line) ); +void RAK_DLL_EXPORT SetRealloc_Ex( void* (*userFunction)(void *p, size_t size, const char *file, unsigned int line) ); +void RAK_DLL_EXPORT SetFree_Ex( void (*userFunction)(void *p, const char *file, unsigned int line) ); +// Change to a user defined out of memory function +void RAK_DLL_EXPORT SetNotifyOutOfMemory( void (*userFunction)(const char *file, const long line) ); +void RAK_DLL_EXPORT SetDLMallocMMap( void* (*userFunction)(size_t size) ); +void RAK_DLL_EXPORT SetDLMallocDirectMMap( void* (*userFunction)(size_t size) ); +void RAK_DLL_EXPORT SetDLMallocMUnmap( int (*userFunction)(void* ptr, size_t size) ); + +extern RAK_DLL_EXPORT void * (*GetMalloc()) (size_t size); +extern RAK_DLL_EXPORT void * (*GetRealloc()) (void *p, size_t size); +extern RAK_DLL_EXPORT void (*GetFree()) (void *p); +extern RAK_DLL_EXPORT void * (*GetMalloc_Ex()) (size_t size, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void * (*GetRealloc_Ex()) (void *p, size_t size, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void (*GetFree_Ex()) (void *p, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void *(*GetDLMallocMMap())(size_t size); +extern RAK_DLL_EXPORT void *(*GetDLMallocDirectMMap())(size_t size); +extern RAK_DLL_EXPORT int (*GetDLMallocMUnmap())(void* ptr, size_t size); + +namespace RakNet +{ + + template + RAK_DLL_EXPORT Type* OP_NEW(const char *file, unsigned int line) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type; + return t; +#else + (void) file; + (void) line; + return new Type; +#endif + } + + template + RAK_DLL_EXPORT Type* OP_NEW_1(const char *file, unsigned int line, const P1 &p1) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type(p1); + return t; +#else + (void) file; + (void) line; + return new Type(p1); +#endif + } + + template + RAK_DLL_EXPORT Type* OP_NEW_2(const char *file, unsigned int line, const P1 &p1, const P2 &p2) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type(p1, p2); + return t; +#else + (void) file; + (void) line; + return new Type(p1, p2); +#endif + } + + template + RAK_DLL_EXPORT Type* OP_NEW_3(const char *file, unsigned int line, const P1 &p1, const P2 &p2, const P3 &p3) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type(p1, p2, p3); + return t; +#else + (void) file; + (void) line; + return new Type(p1, p2, p3); +#endif + } + + template + RAK_DLL_EXPORT Type* OP_NEW_4(const char *file, unsigned int line, const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type(p1, p2, p3, p4); + return t; +#else + (void) file; + (void) line; + return new Type(p1, p2, p3, p4); +#endif + } + + + template + RAK_DLL_EXPORT Type* OP_NEW_ARRAY(const int count, const char *file, unsigned int line) + { + if (count==0) + return 0; + +#if _USE_RAK_MEMORY_OVERRIDE==1 +// Type *t; + char *buffer = (char *) (GetMalloc_Ex())(sizeof(int)+sizeof(Type)*count, file, line); + ((int*)buffer)[0]=count; + for (int i=0; i + RAK_DLL_EXPORT void OP_DELETE(Type *buff, const char *file, unsigned int line) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + if (buff==0) return; + buff->~Type(); + (GetFree_Ex())((char*)buff, file, line ); +#else + (void) file; + (void) line; + delete buff; +#endif + + } + + template + RAK_DLL_EXPORT void OP_DELETE_ARRAY(Type *buff, const char *file, unsigned int line) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + if (buff==0) + return; + + int count = ((int*)((char*)buff-sizeof(int)))[0]; + Type *t; + for (int i=0; i~Type(); + } + (GetFree_Ex())((char*)buff-sizeof(int), file, line ); +#else + (void) file; + (void) line; + delete [] buff; +#endif + + } + + void RAK_DLL_EXPORT * _RakMalloc (size_t size); + void RAK_DLL_EXPORT * _RakRealloc (void *p, size_t size); + void RAK_DLL_EXPORT _RakFree (void *p); + void RAK_DLL_EXPORT * _RakMalloc_Ex (size_t size, const char *file, unsigned int line); + void RAK_DLL_EXPORT * _RakRealloc_Ex (void *p, size_t size, const char *file, unsigned int line); + void RAK_DLL_EXPORT _RakFree_Ex (void *p, const char *file, unsigned int line); + void RAK_DLL_EXPORT * _DLMallocMMap (size_t size); + void RAK_DLL_EXPORT * _DLMallocDirectMMap (size_t size); + int RAK_DLL_EXPORT _DLMallocMUnmap (void *p, size_t size); + +} + +// Call to make RakNet allocate a large block of memory, and do all subsequent allocations in that memory block +// Initial and reallocations will be done through whatever function is pointed to by yourMMapFunction, and yourDirectMMapFunction (default is malloc) +// Allocations will be freed through whatever function is pointed to by yourMUnmapFunction (default free) +void UseRaknetFixedHeap(size_t initialCapacity, + void * (*yourMMapFunction) (size_t size) = RakNet::_DLMallocMMap, + void * (*yourDirectMMapFunction) (size_t size) = RakNet::_DLMallocDirectMMap, + int (*yourMUnmapFunction) (void *p, size_t size) = RakNet::_DLMallocMUnmap); + +// Free memory allocated from UseRaknetFixedHeap +void FreeRakNetFixedHeap(void); + +// #if _USE_RAK_MEMORY_OVERRIDE==1 +// #if defined(RMO_NEW_UNDEF) +// #pragma pop_macro("new") +// #undef RMO_NEW_UNDEF +// #endif +// #endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNet.vcproj b/project/lib_projects/raknet/jni/RaknetSources/RakNet.vcproj index b8f5f07..78bd057 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNet.vcproj +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNet.vcproj @@ -1,845 +1,845 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetCommandParser.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakNetCommandParser.cpp index 8e156d1..f572d54 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetCommandParser.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetCommandParser.cpp @@ -1,302 +1,302 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_RakNetCommandParser==1 - -#include "RakNetCommandParser.h" -#include "TransportInterface.h" -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "RakAssert.h" -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(RakNetCommandParser,RakNetCommandParser); - -RakNetCommandParser::RakNetCommandParser() -{ - RegisterCommand(4, "Startup","( unsigned short maxConnections, unsigned short localPort, const char *forceHostAddress );"); - RegisterCommand(0,"InitializeSecurity","();"); - RegisterCommand(0,"DisableSecurity","( void );"); - RegisterCommand(1,"AddToSecurityExceptionList","( const char *ip );"); - RegisterCommand(1,"RemoveFromSecurityExceptionList","( const char *ip );"); - RegisterCommand(1,"IsInSecurityExceptionList","( const char *ip );"); - RegisterCommand(1,"SetMaximumIncomingConnections","( unsigned short numberAllowed );"); - RegisterCommand(0,"GetMaximumIncomingConnections","( void ) const;"); - RegisterCommand(4,"Connect","( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength );"); - RegisterCommand(2,"Disconnect","( unsigned int blockDuration, unsigned char orderingChannel=0 );"); - RegisterCommand(0,"IsActive","( void ) const;"); - RegisterCommand(0,"GetConnectionList","() const;"); - RegisterCommand(3,"CloseConnection","( const SystemAddress target, bool sendDisconnectionNotification, unsigned char orderingChannel=0 );"); - RegisterCommand(2,"IsConnected","( );"); - RegisterCommand(1,"GetIndexFromSystemAddress","( const SystemAddress systemAddress );"); - RegisterCommand(1,"GetSystemAddressFromIndex","( int index );"); - RegisterCommand(2,"AddToBanList","( const char *IP, RakNet::TimeMS milliseconds=0 );"); - RegisterCommand(1,"RemoveFromBanList","( const char *IP );"); - RegisterCommand(0,"ClearBanList","( void );"); - RegisterCommand(1,"IsBanned","( const char *IP );"); - RegisterCommand(1,"Ping1","( const SystemAddress target );"); - RegisterCommand(3,"Ping2","( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections );"); - RegisterCommand(1,"GetAveragePing","( const SystemAddress systemAddress );"); - RegisterCommand(1,"GetLastPing","( const SystemAddress systemAddress ) const;"); - RegisterCommand(1,"GetLowestPing","( const SystemAddress systemAddress ) const;"); - RegisterCommand(1,"SetOccasionalPing","( bool doPing );"); - RegisterCommand(2,"SetOfflinePingResponse","( const char *data, const unsigned int length );"); - RegisterCommand(0,"GetInternalID","( void ) const;"); - RegisterCommand(1,"GetExternalID","( const SystemAddress target ) const;"); - RegisterCommand(2,"SetTimeoutTime","( RakNet::TimeMS timeMS, const SystemAddress target );"); -// RegisterCommand(1,"SetMTUSize","( int size );"); - RegisterCommand(0,"GetMTUSize","( void ) const;"); - RegisterCommand(0,"GetNumberOfAddresses","( void );"); - RegisterCommand(1,"GetLocalIP","( unsigned int index );"); - RegisterCommand(1,"AllowConnectionResponseIPMigration","( bool allow );"); - RegisterCommand(4,"AdvertiseSystem","( const char *host, unsigned short remotePort, const char *data, int dataLength );"); - RegisterCommand(2,"SetIncomingPassword","( const char* passwordData, int passwordDataLength );"); - RegisterCommand(0,"GetIncomingPassword","( void );"); - RegisterCommand(0,"IsNetworkSimulatorActive","( void );"); -} -RakNetCommandParser::~RakNetCommandParser() -{ -} -void RakNetCommandParser::SetRakPeerInterface(RakNet::RakPeerInterface *rakPeer) -{ - peer=rakPeer; -} -bool RakNetCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString) -{ - (void) originalString; - (void) numParameters; - - if (peer==0) - return false; - - if (strcmp(command, "Startup")==0) - { - RakNet::SocketDescriptor socketDescriptor((unsigned short)atoi(parameterList[1]), parameterList[2]); - ReturnResult(peer->Startup((unsigned short)atoi(parameterList[0]), &socketDescriptor, 1), command, transport, systemAddress); - } - else if (strcmp(command, "InitializeSecurity")==0) - { - ReturnResult(peer->InitializeSecurity(parameterList[0],parameterList[1]), command, transport, systemAddress); - } - else if (strcmp(command, "DisableSecurity")==0) - { - peer->DisableSecurity(); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "AddToSecurityExceptionList")==0) - { - peer->AddToSecurityExceptionList(parameterList[1]); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "RemoveFromSecurityExceptionList")==0) - { - peer->RemoveFromSecurityExceptionList(parameterList[1]); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "IsInSecurityExceptionList")==0) - { - ReturnResult(peer->IsInSecurityExceptionList(parameterList[1]),command, transport, systemAddress); - } - else if (strcmp(command, "SetMaximumIncomingConnections")==0) - { - peer->SetMaximumIncomingConnections((unsigned short)atoi(parameterList[0])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetMaximumIncomingConnections")==0) - { - ReturnResult(peer->GetMaximumIncomingConnections(), command, transport, systemAddress); - } - else if (strcmp(command, "Connect")==0) - { - ReturnResult(peer->Connect(parameterList[0], (unsigned short)atoi(parameterList[1]),parameterList[2],atoi(parameterList[3]))==RakNet::CONNECTION_ATTEMPT_STARTED, command, transport, systemAddress); - } - else if (strcmp(command, "Disconnect")==0) - { - peer->Shutdown(atoi(parameterList[0]), (unsigned char)atoi(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "IsActive")==0) - { - ReturnResult(peer->IsActive(), command, transport, systemAddress); - } - else if (strcmp(command, "GetConnectionList")==0) - { - SystemAddress remoteSystems[32]; - unsigned short count=32; - unsigned i; - if (peer->GetConnectionList(remoteSystems, &count)) - { - if (count==0) - { - transport->Send(systemAddress, "GetConnectionList() returned no systems connected.\r\n"); - } - else - { - transport->Send(systemAddress, "GetConnectionList() returned:\r\n"); - for (i=0; i < count; i++) - { - char str1[64]; - remoteSystems[i].ToString(true, str1); - transport->Send(systemAddress, "%i %s\r\n", i, str1); - } - } - } - else - transport->Send(systemAddress, "GetConnectionList() returned false.\r\n"); - } - else if (strcmp(command, "CloseConnection")==0) - { - peer->CloseConnection(SystemAddress(parameterList[0]), atoi(parameterList[1])!=0,(unsigned char)atoi(parameterList[2])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetConnectionState")==0) - { - ReturnResult((int) peer->GetConnectionState(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "GetIndexFromSystemAddress")==0) - { - ReturnResult(peer->GetIndexFromSystemAddress(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "GetSystemAddressFromIndex")==0) - { - ReturnResult(peer->GetSystemAddressFromIndex(atoi(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "AddToBanList")==0) - { - peer->AddToBanList(parameterList[0], atoi(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "RemoveFromBanList")==0) - { - peer->RemoveFromBanList(parameterList[0]); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "ClearBanList")==0) - { - peer->ClearBanList(); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "IsBanned")==0) - { - ReturnResult(peer->IsBanned(parameterList[0]), command, transport, systemAddress); - } - else if (strcmp(command, "Ping1")==0) - { - peer->Ping(SystemAddress(parameterList[0])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "Ping2")==0) - { - peer->Ping(parameterList[0], (unsigned short) atoi(parameterList[1]), atoi(parameterList[2])!=0); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetAveragePing")==0) - { - ReturnResult(peer->GetAveragePing(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "GetLastPing")==0) - { - ReturnResult(peer->GetLastPing(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "GetLowestPing")==0) - { - ReturnResult(peer->GetLowestPing(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "SetOccasionalPing")==0) - { - peer->SetOccasionalPing(atoi(parameterList[0])!=0); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "SetOfflinePingResponse")==0) - { - peer->SetOfflinePingResponse(parameterList[0], atoi(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetInternalID")==0) - { - ReturnResult(peer->GetInternalID(), command, transport, systemAddress); - } - else if (strcmp(command, "GetExternalID")==0) - { - ReturnResult(peer->GetExternalID(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "SetTimeoutTime")==0) - { - peer->SetTimeoutTime(atoi(parameterList[0]), SystemAddress(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - /* - else if (strcmp(command, "SetMTUSize")==0) - { - ReturnResult(peer->SetMTUSize(atoi(parameterList[0]), UNASSIGNED_SYSTEM_ADDRESS), command, transport, systemAddress); - } - */ - else if (strcmp(command, "GetMTUSize")==0) - { - ReturnResult(peer->GetMTUSize(UNASSIGNED_SYSTEM_ADDRESS), command, transport, systemAddress); - } - else if (strcmp(command, "GetNumberOfAddresses")==0) - { - ReturnResult((int)peer->GetNumberOfAddresses(), command, transport, systemAddress); - } - else if (strcmp(command, "GetLocalIP")==0) - { - ReturnResult((char*) peer->GetLocalIP(atoi(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "AllowConnectionResponseIPMigration")==0) - { - peer->AllowConnectionResponseIPMigration(atoi(parameterList[0])!=0); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "AdvertiseSystem")==0) - { - peer->AdvertiseSystem(parameterList[0], (unsigned short) atoi(parameterList[1]),parameterList[2],atoi(parameterList[3])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "SetIncomingPassword")==0) - { - peer->SetIncomingPassword(parameterList[0], atoi(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetIncomingPassword")==0) - { - char password[256]; - int passwordLength; - peer->GetIncomingPassword(password, &passwordLength); - if (passwordLength) - ReturnResult((char*)password, command, transport, systemAddress); - else - ReturnResult(0, command, transport, systemAddress); - } - - return true; -} -const char *RakNetCommandParser::GetName(void) const -{ - return "RakNet"; -} -void RakNetCommandParser::SendHelp(TransportInterface *transport, const SystemAddress &systemAddress) -{ - if (peer) - { - transport->Send(systemAddress, "The RakNet parser provides mirror functions to RakPeer\r\n"); - transport->Send(systemAddress, "SystemAddresss take two parameters: send .\r\n"); - transport->Send(systemAddress, "For bool, send 1 or 0.\r\n"); - } - else - { - transport->Send(systemAddress, "Parser not active. Call SetRakPeerInterface.\r\n"); - } -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_RakNetCommandParser==1 + +#include "RakNetCommandParser.h" +#include "TransportInterface.h" +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "RakAssert.h" +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(RakNetCommandParser,RakNetCommandParser); + +RakNetCommandParser::RakNetCommandParser() +{ + RegisterCommand(4, "Startup","( unsigned short maxConnections, unsigned short localPort, const char *forceHostAddress );"); + RegisterCommand(0,"InitializeSecurity","();"); + RegisterCommand(0,"DisableSecurity","( void );"); + RegisterCommand(1,"AddToSecurityExceptionList","( const char *ip );"); + RegisterCommand(1,"RemoveFromSecurityExceptionList","( const char *ip );"); + RegisterCommand(1,"IsInSecurityExceptionList","( const char *ip );"); + RegisterCommand(1,"SetMaximumIncomingConnections","( unsigned short numberAllowed );"); + RegisterCommand(0,"GetMaximumIncomingConnections","( void ) const;"); + RegisterCommand(4,"Connect","( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength );"); + RegisterCommand(2,"Disconnect","( unsigned int blockDuration, unsigned char orderingChannel=0 );"); + RegisterCommand(0,"IsActive","( void ) const;"); + RegisterCommand(0,"GetConnectionList","() const;"); + RegisterCommand(3,"CloseConnection","( const SystemAddress target, bool sendDisconnectionNotification, unsigned char orderingChannel=0 );"); + RegisterCommand(2,"IsConnected","( );"); + RegisterCommand(1,"GetIndexFromSystemAddress","( const SystemAddress systemAddress );"); + RegisterCommand(1,"GetSystemAddressFromIndex","( int index );"); + RegisterCommand(2,"AddToBanList","( const char *IP, RakNet::TimeMS milliseconds=0 );"); + RegisterCommand(1,"RemoveFromBanList","( const char *IP );"); + RegisterCommand(0,"ClearBanList","( void );"); + RegisterCommand(1,"IsBanned","( const char *IP );"); + RegisterCommand(1,"Ping1","( const SystemAddress target );"); + RegisterCommand(3,"Ping2","( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections );"); + RegisterCommand(1,"GetAveragePing","( const SystemAddress systemAddress );"); + RegisterCommand(1,"GetLastPing","( const SystemAddress systemAddress ) const;"); + RegisterCommand(1,"GetLowestPing","( const SystemAddress systemAddress ) const;"); + RegisterCommand(1,"SetOccasionalPing","( bool doPing );"); + RegisterCommand(2,"SetOfflinePingResponse","( const char *data, const unsigned int length );"); + RegisterCommand(0,"GetInternalID","( void ) const;"); + RegisterCommand(1,"GetExternalID","( const SystemAddress target ) const;"); + RegisterCommand(2,"SetTimeoutTime","( RakNet::TimeMS timeMS, const SystemAddress target );"); +// RegisterCommand(1,"SetMTUSize","( int size );"); + RegisterCommand(0,"GetMTUSize","( void ) const;"); + RegisterCommand(0,"GetNumberOfAddresses","( void );"); + RegisterCommand(1,"GetLocalIP","( unsigned int index );"); + RegisterCommand(1,"AllowConnectionResponseIPMigration","( bool allow );"); + RegisterCommand(4,"AdvertiseSystem","( const char *host, unsigned short remotePort, const char *data, int dataLength );"); + RegisterCommand(2,"SetIncomingPassword","( const char* passwordData, int passwordDataLength );"); + RegisterCommand(0,"GetIncomingPassword","( void );"); + RegisterCommand(0,"IsNetworkSimulatorActive","( void );"); +} +RakNetCommandParser::~RakNetCommandParser() +{ +} +void RakNetCommandParser::SetRakPeerInterface(RakNet::RakPeerInterface *rakPeer) +{ + peer=rakPeer; +} +bool RakNetCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString) +{ + (void) originalString; + (void) numParameters; + + if (peer==0) + return false; + + if (strcmp(command, "Startup")==0) + { + RakNet::SocketDescriptor socketDescriptor((unsigned short)atoi(parameterList[1]), parameterList[2]); + ReturnResult(peer->Startup((unsigned short)atoi(parameterList[0]), &socketDescriptor, 1), command, transport, systemAddress); + } + else if (strcmp(command, "InitializeSecurity")==0) + { + ReturnResult(peer->InitializeSecurity(parameterList[0],parameterList[1]), command, transport, systemAddress); + } + else if (strcmp(command, "DisableSecurity")==0) + { + peer->DisableSecurity(); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "AddToSecurityExceptionList")==0) + { + peer->AddToSecurityExceptionList(parameterList[1]); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "RemoveFromSecurityExceptionList")==0) + { + peer->RemoveFromSecurityExceptionList(parameterList[1]); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "IsInSecurityExceptionList")==0) + { + ReturnResult(peer->IsInSecurityExceptionList(parameterList[1]),command, transport, systemAddress); + } + else if (strcmp(command, "SetMaximumIncomingConnections")==0) + { + peer->SetMaximumIncomingConnections((unsigned short)atoi(parameterList[0])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetMaximumIncomingConnections")==0) + { + ReturnResult(peer->GetMaximumIncomingConnections(), command, transport, systemAddress); + } + else if (strcmp(command, "Connect")==0) + { + ReturnResult(peer->Connect(parameterList[0], (unsigned short)atoi(parameterList[1]),parameterList[2],atoi(parameterList[3]))==RakNet::CONNECTION_ATTEMPT_STARTED, command, transport, systemAddress); + } + else if (strcmp(command, "Disconnect")==0) + { + peer->Shutdown(atoi(parameterList[0]), (unsigned char)atoi(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "IsActive")==0) + { + ReturnResult(peer->IsActive(), command, transport, systemAddress); + } + else if (strcmp(command, "GetConnectionList")==0) + { + SystemAddress remoteSystems[32]; + unsigned short count=32; + unsigned i; + if (peer->GetConnectionList(remoteSystems, &count)) + { + if (count==0) + { + transport->Send(systemAddress, "GetConnectionList() returned no systems connected.\r\n"); + } + else + { + transport->Send(systemAddress, "GetConnectionList() returned:\r\n"); + for (i=0; i < count; i++) + { + char str1[64]; + remoteSystems[i].ToString(true, str1); + transport->Send(systemAddress, "%i %s\r\n", i, str1); + } + } + } + else + transport->Send(systemAddress, "GetConnectionList() returned false.\r\n"); + } + else if (strcmp(command, "CloseConnection")==0) + { + peer->CloseConnection(SystemAddress(parameterList[0]), atoi(parameterList[1])!=0,(unsigned char)atoi(parameterList[2])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetConnectionState")==0) + { + ReturnResult((int) peer->GetConnectionState(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "GetIndexFromSystemAddress")==0) + { + ReturnResult(peer->GetIndexFromSystemAddress(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "GetSystemAddressFromIndex")==0) + { + ReturnResult(peer->GetSystemAddressFromIndex(atoi(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "AddToBanList")==0) + { + peer->AddToBanList(parameterList[0], atoi(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "RemoveFromBanList")==0) + { + peer->RemoveFromBanList(parameterList[0]); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "ClearBanList")==0) + { + peer->ClearBanList(); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "IsBanned")==0) + { + ReturnResult(peer->IsBanned(parameterList[0]), command, transport, systemAddress); + } + else if (strcmp(command, "Ping1")==0) + { + peer->Ping(SystemAddress(parameterList[0])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "Ping2")==0) + { + peer->Ping(parameterList[0], (unsigned short) atoi(parameterList[1]), atoi(parameterList[2])!=0); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetAveragePing")==0) + { + ReturnResult(peer->GetAveragePing(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "GetLastPing")==0) + { + ReturnResult(peer->GetLastPing(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "GetLowestPing")==0) + { + ReturnResult(peer->GetLowestPing(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "SetOccasionalPing")==0) + { + peer->SetOccasionalPing(atoi(parameterList[0])!=0); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "SetOfflinePingResponse")==0) + { + peer->SetOfflinePingResponse(parameterList[0], atoi(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetInternalID")==0) + { + ReturnResult(peer->GetInternalID(), command, transport, systemAddress); + } + else if (strcmp(command, "GetExternalID")==0) + { + ReturnResult(peer->GetExternalID(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "SetTimeoutTime")==0) + { + peer->SetTimeoutTime(atoi(parameterList[0]), SystemAddress(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + /* + else if (strcmp(command, "SetMTUSize")==0) + { + ReturnResult(peer->SetMTUSize(atoi(parameterList[0]), UNASSIGNED_SYSTEM_ADDRESS), command, transport, systemAddress); + } + */ + else if (strcmp(command, "GetMTUSize")==0) + { + ReturnResult(peer->GetMTUSize(UNASSIGNED_SYSTEM_ADDRESS), command, transport, systemAddress); + } + else if (strcmp(command, "GetNumberOfAddresses")==0) + { + ReturnResult((int)peer->GetNumberOfAddresses(), command, transport, systemAddress); + } + else if (strcmp(command, "GetLocalIP")==0) + { + ReturnResult((char*) peer->GetLocalIP(atoi(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "AllowConnectionResponseIPMigration")==0) + { + peer->AllowConnectionResponseIPMigration(atoi(parameterList[0])!=0); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "AdvertiseSystem")==0) + { + peer->AdvertiseSystem(parameterList[0], (unsigned short) atoi(parameterList[1]),parameterList[2],atoi(parameterList[3])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "SetIncomingPassword")==0) + { + peer->SetIncomingPassword(parameterList[0], atoi(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetIncomingPassword")==0) + { + char password[256]; + int passwordLength; + peer->GetIncomingPassword(password, &passwordLength); + if (passwordLength) + ReturnResult((char*)password, command, transport, systemAddress); + else + ReturnResult(0, command, transport, systemAddress); + } + + return true; +} +const char *RakNetCommandParser::GetName(void) const +{ + return "RakNet"; +} +void RakNetCommandParser::SendHelp(TransportInterface *transport, const SystemAddress &systemAddress) +{ + if (peer) + { + transport->Send(systemAddress, "The RakNet parser provides mirror functions to RakPeer\r\n"); + transport->Send(systemAddress, "SystemAddresss take two parameters: send .\r\n"); + transport->Send(systemAddress, "For bool, send 1 or 0.\r\n"); + } + else + { + transport->Send(systemAddress, "Parser not active. Call SetRakPeerInterface.\r\n"); + } +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetCommandParser.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetCommandParser.h index 3b684e4..4760291 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetCommandParser.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetCommandParser.h @@ -1,63 +1,63 @@ -/// \file -/// \brief Contains RakNetCommandParser , used to send commands to an instance of RakPeer -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_RakNetCommandParser==1 - -#ifndef __RAKNET_COMMAND_PARSER -#define __RAKNET_COMMAND_PARSER - -#include "CommandParserInterface.h" -#include "Export.h" - -namespace RakNet -{ -class RakPeerInterface; - -/// \brief This allows a console client to call most of the functions in RakPeer -class RAK_DLL_EXPORT RakNetCommandParser : public CommandParserInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(RakNetCommandParser) - - RakNetCommandParser(); - ~RakNetCommandParser(); - - /// Given \a command with parameters \a parameterList , do whatever processing you wish. - /// \param[in] command The command to process - /// \param[in] numParameters How many parameters were passed along with the command - /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player that sent this command. - /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing - bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString); - - /// You are responsible for overriding this function and returning a static string, which will identifier your parser. - /// This should return a static string - /// \return The name that you return. - const char *GetName(void) const; - - /// A callback for when you are expected to send a brief description of your parser to \a systemAddress - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player that requested help. - void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress); - - /// Records the instance of RakPeer to perform the desired commands on - /// \param[in] rakPeer The RakPeer instance, or a derived class (e.g. RakPeer or RakPeer) - void SetRakPeerInterface(RakNet::RakPeerInterface *rakPeer); -protected: - - /// Which instance of RakPeer we are working on. Set from SetRakPeerInterface() - RakPeerInterface *peer; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains RakNetCommandParser , used to send commands to an instance of RakPeer +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_RakNetCommandParser==1 + +#ifndef __RAKNET_COMMAND_PARSER +#define __RAKNET_COMMAND_PARSER + +#include "CommandParserInterface.h" +#include "Export.h" + +namespace RakNet +{ +class RakPeerInterface; + +/// \brief This allows a console client to call most of the functions in RakPeer +class RAK_DLL_EXPORT RakNetCommandParser : public CommandParserInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(RakNetCommandParser) + + RakNetCommandParser(); + ~RakNetCommandParser(); + + /// Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + const char *GetName(void) const; + + /// A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress); + + /// Records the instance of RakPeer to perform the desired commands on + /// \param[in] rakPeer The RakPeer instance, or a derived class (e.g. RakPeer or RakPeer) + void SetRakPeerInterface(RakNet::RakPeerInterface *rakPeer); +protected: + + /// Which instance of RakPeer we are working on. Set from SetRakPeerInterface() + RakPeerInterface *peer; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetDefines.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetDefines.h index 78822f6..a0d1cfe 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetDefines.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetDefines.h @@ -1,169 +1,169 @@ -#ifndef __RAKNET_DEFINES_H -#define __RAKNET_DEFINES_H - -// If you want to change these defines, put them in RakNetDefinesOverrides so your changes are not lost when updating RakNet -// The user should not edit this file -#include "RakNetDefinesOverrides.h" - -/// Define __GET_TIME_64BIT to have RakNet::TimeMS use a 64, rather than 32 bit value. A 32 bit value will overflow after about 5 weeks. -/// However, this doubles the bandwidth use for sending times, so don't do it unless you have a reason to. -/// Comment out if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 -/// This must be the same on all systems, or they won't connect -#ifndef __GET_TIME_64BIT -#define __GET_TIME_64BIT 1 -#endif - -// Define _FILE_AND_LINE_ to "",0 if you want to strip out file and line info for memory tracking from the EXE -#ifndef _FILE_AND_LINE_ -#define _FILE_AND_LINE_ __FILE__,__LINE__ -#endif - -/// Define __BITSTREAM_NATIVE_END to NOT support endian swapping in the BitStream class. This is faster and is what you should use -/// unless you actually plan to have different endianness systems connect to each other -/// Enabled by default. -// #define __BITSTREAM_NATIVE_END - -/// Maximum (stack) size to use with _alloca before using new and delete instead. -#ifndef MAX_ALLOCA_STACK_ALLOCATION -#define MAX_ALLOCA_STACK_ALLOCATION 1048576 -#endif - -// Use WaitForSingleObject instead of sleep. -// Defining it plays nicer with other systems, and uses less CPU, but gives worse RakNet performance -// Undefining it uses more CPU time, but is more responsive and faster. -#ifndef _WIN32_WCE -#define USE_WAIT_FOR_MULTIPLE_EVENTS -#endif - -/// Uncomment to use RakMemoryOverride for custom memory tracking -/// See RakMemoryOverride.h. -#ifndef _USE_RAK_MEMORY_OVERRIDE -#define _USE_RAK_MEMORY_OVERRIDE 0 -#endif - -/// If defined, OpenSSL is enabled for the class TCPInterface -/// This is necessary to use the SendEmail class with Google POP servers -/// Note that OpenSSL carries its own license restrictions that you should be aware of. If you don't agree, don't enable this define -/// This also requires that you enable header search paths to DependentExtensions\openssl-0.9.8g -// #define OPEN_SSL_CLIENT_SUPPORT -#ifndef OPEN_SSL_CLIENT_SUPPORT -#define OPEN_SSL_CLIENT_SUPPORT 0 -#endif - -/// Threshold at which to do a malloc / free rather than pushing data onto a fixed stack for the bitstream class -/// Arbitrary size, just picking something likely to be larger than most packets -#ifndef BITSTREAM_STACK_ALLOCATION_SIZE -#define BITSTREAM_STACK_ALLOCATION_SIZE 256 -#endif - -// Redefine if you want to disable or change the target for debug RAKNET_DEBUG_PRINTF -#ifndef RAKNET_DEBUG_PRINTF -#define RAKNET_DEBUG_PRINTF printf -#endif - -// Maximum number of local IP addresses supported -#ifndef MAXIMUM_NUMBER_OF_INTERNAL_IDS -#define MAXIMUM_NUMBER_OF_INTERNAL_IDS 10 -#endif - -#ifndef RakAssert - - - - -#if defined(_DEBUG) -#define RakAssert(x) assert(x); -#else -#define RakAssert(x) -#endif - -#endif - -/// This controls the amount of memory used per connection. -/// This many datagrams are tracked by datagramNumber. If more than this many datagrams are sent, then an ack for an older datagram would be ignored -/// This results in an unnecessary resend in that case -#ifndef DATAGRAM_MESSAGE_ID_ARRAY_LENGTH -#define DATAGRAM_MESSAGE_ID_ARRAY_LENGTH 512 -#endif - -/// This is the maximum number of reliable user messages that can be on the wire at a time -/// If this is too low, then high ping connections with a large throughput will be underutilized -/// This will be evident because RakNetStatistics::messagesInSend buffer will increase over time, yet at the same time the outgoing bandwidth per second is less than your connection supports -#ifndef RESEND_BUFFER_ARRAY_LENGTH -#define RESEND_BUFFER_ARRAY_LENGTH 512 -#define RESEND_BUFFER_ARRAY_MASK 511 -#endif - -/// Uncomment if you want to link in the DLMalloc library to use with RakMemoryOverride -// #define _LINK_DL_MALLOC - -#ifndef GET_TIME_SPIKE_LIMIT -/// Workaround for http://support.microsoft.com/kb/274323 -/// If two calls between RakNet::GetTime() happen farther apart than this time in microseconds, this delta will be returned instead -/// Note: This will cause ID_TIMESTAMP to be temporarily inaccurate if you set a breakpoint that pauses the UpdateNetworkLoop() thread in RakPeer -/// Define in RakNetDefinesOverrides.h to enable (non-zero) or disable (0) -#define GET_TIME_SPIKE_LIMIT 0 -#endif - -// Use sliding window congestion control instead of ping based congestion control -#ifndef USE_SLIDING_WINDOW_CONGESTION_CONTROL -#define USE_SLIDING_WINDOW_CONGESTION_CONTROL 1 -#endif - -// When a large message is arriving, preallocate the memory for the entire block -// This results in large messages not taking up time to reassembly with memcpy, but is vulnerable to attackers causing the host to run out of memory -#ifndef PREALLOCATE_LARGE_MESSAGES -#define PREALLOCATE_LARGE_MESSAGES 0 -#endif - -#ifndef RAKNET_SUPPORT_IPV6 -#define RAKNET_SUPPORT_IPV6 0 -#endif - - - - - - - - - - - -#ifndef RAKSTRING_TYPE -#if defined(_UNICODE) -#define RAKSTRING_TYPE RakWString -#define RAKSTRING_TYPE_IS_UNICODE 1 -#else -#define RAKSTRING_TYPE RakString -#define RAKSTRING_TYPE_IS_UNICODE 0 -#endif -#endif - -#ifndef RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS -#define RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS 32 -#endif - -#ifndef RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH -#define RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH 32 -#endif - -#ifndef XBOX_BYPASS_SECURITY -#define XBOX_BYPASS_SECURITY 1 -#endif - -// Controls how many allocations occur at once for the memory pool of incoming datagrams waiting to be transferred between the recvfrom thread and the main update thread -// Has large effect on memory usage, per instance of RakPeer. Approximately MAXIMUM_MTU_SIZE*BUFFERED_PACKETS_PAGE_SIZE bytes, once after calling RakPeer::Startup() -#ifndef BUFFERED_PACKETS_PAGE_SIZE -#define BUFFERED_PACKETS_PAGE_SIZE 8 -#endif - -// Controls how many allocations occur at once for the memory pool of incoming or outgoing datagrams. -// Has small effect on memory usage per connection. Uses about 256 bytes*INTERNAL_PACKET_PAGE_SIZE per connection -#ifndef INTERNAL_PACKET_PAGE_SIZE -#define INTERNAL_PACKET_PAGE_SIZE 8 -#endif - -//#define USE_THREADED_SEND - -#endif // __RAKNET_DEFINES_H +#ifndef __RAKNET_DEFINES_H +#define __RAKNET_DEFINES_H + +// If you want to change these defines, put them in RakNetDefinesOverrides so your changes are not lost when updating RakNet +// The user should not edit this file +#include "RakNetDefinesOverrides.h" + +/// Define __GET_TIME_64BIT to have RakNet::TimeMS use a 64, rather than 32 bit value. A 32 bit value will overflow after about 5 weeks. +/// However, this doubles the bandwidth use for sending times, so don't do it unless you have a reason to. +/// Comment out if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 +/// This must be the same on all systems, or they won't connect +#ifndef __GET_TIME_64BIT +#define __GET_TIME_64BIT 1 +#endif + +// Define _FILE_AND_LINE_ to "",0 if you want to strip out file and line info for memory tracking from the EXE +#ifndef _FILE_AND_LINE_ +#define _FILE_AND_LINE_ __FILE__,__LINE__ +#endif + +/// Define __BITSTREAM_NATIVE_END to NOT support endian swapping in the BitStream class. This is faster and is what you should use +/// unless you actually plan to have different endianness systems connect to each other +/// Enabled by default. +// #define __BITSTREAM_NATIVE_END + +/// Maximum (stack) size to use with _alloca before using new and delete instead. +#ifndef MAX_ALLOCA_STACK_ALLOCATION +#define MAX_ALLOCA_STACK_ALLOCATION 1048576 +#endif + +// Use WaitForSingleObject instead of sleep. +// Defining it plays nicer with other systems, and uses less CPU, but gives worse RakNet performance +// Undefining it uses more CPU time, but is more responsive and faster. +#ifndef _WIN32_WCE +#define USE_WAIT_FOR_MULTIPLE_EVENTS +#endif + +/// Uncomment to use RakMemoryOverride for custom memory tracking +/// See RakMemoryOverride.h. +#ifndef _USE_RAK_MEMORY_OVERRIDE +#define _USE_RAK_MEMORY_OVERRIDE 0 +#endif + +/// If defined, OpenSSL is enabled for the class TCPInterface +/// This is necessary to use the SendEmail class with Google POP servers +/// Note that OpenSSL carries its own license restrictions that you should be aware of. If you don't agree, don't enable this define +/// This also requires that you enable header search paths to DependentExtensions\openssl-0.9.8g +// #define OPEN_SSL_CLIENT_SUPPORT +#ifndef OPEN_SSL_CLIENT_SUPPORT +#define OPEN_SSL_CLIENT_SUPPORT 0 +#endif + +/// Threshold at which to do a malloc / free rather than pushing data onto a fixed stack for the bitstream class +/// Arbitrary size, just picking something likely to be larger than most packets +#ifndef BITSTREAM_STACK_ALLOCATION_SIZE +#define BITSTREAM_STACK_ALLOCATION_SIZE 256 +#endif + +// Redefine if you want to disable or change the target for debug RAKNET_DEBUG_PRINTF +#ifndef RAKNET_DEBUG_PRINTF +#define RAKNET_DEBUG_PRINTF printf +#endif + +// Maximum number of local IP addresses supported +#ifndef MAXIMUM_NUMBER_OF_INTERNAL_IDS +#define MAXIMUM_NUMBER_OF_INTERNAL_IDS 10 +#endif + +#ifndef RakAssert + + + + +#if defined(_DEBUG) +#define RakAssert(x) assert(x); +#else +#define RakAssert(x) +#endif + +#endif + +/// This controls the amount of memory used per connection. +/// This many datagrams are tracked by datagramNumber. If more than this many datagrams are sent, then an ack for an older datagram would be ignored +/// This results in an unnecessary resend in that case +#ifndef DATAGRAM_MESSAGE_ID_ARRAY_LENGTH +#define DATAGRAM_MESSAGE_ID_ARRAY_LENGTH 512 +#endif + +/// This is the maximum number of reliable user messages that can be on the wire at a time +/// If this is too low, then high ping connections with a large throughput will be underutilized +/// This will be evident because RakNetStatistics::messagesInSend buffer will increase over time, yet at the same time the outgoing bandwidth per second is less than your connection supports +#ifndef RESEND_BUFFER_ARRAY_LENGTH +#define RESEND_BUFFER_ARRAY_LENGTH 512 +#define RESEND_BUFFER_ARRAY_MASK 511 +#endif + +/// Uncomment if you want to link in the DLMalloc library to use with RakMemoryOverride +// #define _LINK_DL_MALLOC + +#ifndef GET_TIME_SPIKE_LIMIT +/// Workaround for http://support.microsoft.com/kb/274323 +/// If two calls between RakNet::GetTime() happen farther apart than this time in microseconds, this delta will be returned instead +/// Note: This will cause ID_TIMESTAMP to be temporarily inaccurate if you set a breakpoint that pauses the UpdateNetworkLoop() thread in RakPeer +/// Define in RakNetDefinesOverrides.h to enable (non-zero) or disable (0) +#define GET_TIME_SPIKE_LIMIT 0 +#endif + +// Use sliding window congestion control instead of ping based congestion control +#ifndef USE_SLIDING_WINDOW_CONGESTION_CONTROL +#define USE_SLIDING_WINDOW_CONGESTION_CONTROL 1 +#endif + +// When a large message is arriving, preallocate the memory for the entire block +// This results in large messages not taking up time to reassembly with memcpy, but is vulnerable to attackers causing the host to run out of memory +#ifndef PREALLOCATE_LARGE_MESSAGES +#define PREALLOCATE_LARGE_MESSAGES 0 +#endif + +#ifndef RAKNET_SUPPORT_IPV6 +#define RAKNET_SUPPORT_IPV6 0 +#endif + + + + + + + + + + + +#ifndef RAKSTRING_TYPE +#if defined(_UNICODE) +#define RAKSTRING_TYPE RakWString +#define RAKSTRING_TYPE_IS_UNICODE 1 +#else +#define RAKSTRING_TYPE RakString +#define RAKSTRING_TYPE_IS_UNICODE 0 +#endif +#endif + +#ifndef RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS +#define RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS 32 +#endif + +#ifndef RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH +#define RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH 32 +#endif + +#ifndef XBOX_BYPASS_SECURITY +#define XBOX_BYPASS_SECURITY 1 +#endif + +// Controls how many allocations occur at once for the memory pool of incoming datagrams waiting to be transferred between the recvfrom thread and the main update thread +// Has large effect on memory usage, per instance of RakPeer. Approximately MAXIMUM_MTU_SIZE*BUFFERED_PACKETS_PAGE_SIZE bytes, once after calling RakPeer::Startup() +#ifndef BUFFERED_PACKETS_PAGE_SIZE +#define BUFFERED_PACKETS_PAGE_SIZE 8 +#endif + +// Controls how many allocations occur at once for the memory pool of incoming or outgoing datagrams. +// Has small effect on memory usage per connection. Uses about 256 bytes*INTERNAL_PACKET_PAGE_SIZE per connection +#ifndef INTERNAL_PACKET_PAGE_SIZE +#define INTERNAL_PACKET_PAGE_SIZE 8 +#endif + +//#define USE_THREADED_SEND + +#endif // __RAKNET_DEFINES_H diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetDefinesOverrides.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetDefinesOverrides.h index 7613ea2..913357a 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetDefinesOverrides.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetDefinesOverrides.h @@ -1,2 +1,2 @@ -// USER EDITABLE FILE - +// USER EDITABLE FILE + diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetSmartPtr.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetSmartPtr.h index fb9b9d8..4e5a8d7 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetSmartPtr.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetSmartPtr.h @@ -1,173 +1,173 @@ -#ifndef __RAKNET_SMART_PTR_H -#define __RAKNET_SMART_PTR_H - -// From http://www.codeproject.com/KB/cpp/SmartPointers.aspx -// with bugs fixed - -#include "RakMemoryOverride.h" -#include "Export.h" - -//static int allocCount=0; -//static int deallocCount=0; - -namespace RakNet -{ - -class RAK_DLL_EXPORT ReferenceCounter -{ -private: - int refCount; - -public: - ReferenceCounter() {refCount=0;} - ~ReferenceCounter() {} - void AddRef() {refCount++;} - int Release() {return --refCount;} - int GetRefCount(void) const {return refCount;} -}; - -template < typename T > class RAK_DLL_EXPORT RakNetSmartPtr -{ -private: - T* ptr; // pointer - ReferenceCounter* reference; // Reference refCount - -public: - RakNetSmartPtr() : ptr(0), reference(0) - { - // Do not allocate by default, wasteful if we just have a list of preallocated and unassigend smart pointers - } - - RakNetSmartPtr(T* pValue) : ptr(pValue) - { - reference = RakNet::OP_NEW(_FILE_AND_LINE_); - reference->AddRef(); - -// allocCount+=2; -// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); - } - - RakNetSmartPtr(const RakNetSmartPtr& sp) : ptr(sp.ptr), reference(sp.reference) - { - if (reference) - reference->AddRef(); - } - - ~RakNetSmartPtr() - { - if(reference && reference->Release() == 0) - { - RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); - RakNet::OP_DELETE(reference, _FILE_AND_LINE_); - -// deallocCount+=2; -// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); - } - } - - bool IsNull(void) const - { - return ptr==0; - } - - void SetNull(void) - { - if(reference && reference->Release() == 0) - { - RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); - RakNet::OP_DELETE(reference, _FILE_AND_LINE_); - -// deallocCount+=2; -// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); - } - ptr=0; - reference=0; - } - - bool IsUnique(void) const - { - return reference->GetRefCount()==1; - } - - // Allow you to change the values of the internal contents of the pointer, without changing what is pointed to by other instances of the smart pointer - void Clone(bool copyContents) - { - if (IsUnique()==false) - { - reference->Release(); - - reference = RakNet::OP_NEW(_FILE_AND_LINE_); - reference->AddRef(); - T* oldPtr=ptr; - ptr=RakNet::OP_NEW(_FILE_AND_LINE_); - if (copyContents) - *ptr=*oldPtr; - } - } - - int GetRefCount(void) const - { - return reference->GetRefCount(); - } - - T& operator* () - { - return *ptr; - } - - const T& operator* () const - { - return *ptr; - } - - T* operator-> () - { - return ptr; - } - - const T* operator-> () const - { - return ptr; - } - - bool operator == (const RakNetSmartPtr& sp) - { - return ptr == sp.ptr; - } - bool operator<( const RakNetSmartPtr &right ) {return ptr < right.ptr;} - bool operator>( const RakNetSmartPtr &right ) {return ptr > right.ptr;} - - bool operator != (const RakNetSmartPtr& sp) - { - return ptr != sp.ptr; - } - - RakNetSmartPtr& operator = (const RakNetSmartPtr& sp) - { - // Assignment operator - - if (this != &sp) // Avoid self assignment - { - if(reference && reference->Release() == 0) - { - RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); - RakNet::OP_DELETE(reference, _FILE_AND_LINE_); - -// deallocCount+=2; -// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); - } - - ptr = sp.ptr; - reference = sp.reference; - if (reference) - reference->AddRef(); - } - return *this; - } - - -}; - -} // namespace RakNet - -#endif +#ifndef __RAKNET_SMART_PTR_H +#define __RAKNET_SMART_PTR_H + +// From http://www.codeproject.com/KB/cpp/SmartPointers.aspx +// with bugs fixed + +#include "RakMemoryOverride.h" +#include "Export.h" + +//static int allocCount=0; +//static int deallocCount=0; + +namespace RakNet +{ + +class RAK_DLL_EXPORT ReferenceCounter +{ +private: + int refCount; + +public: + ReferenceCounter() {refCount=0;} + ~ReferenceCounter() {} + void AddRef() {refCount++;} + int Release() {return --refCount;} + int GetRefCount(void) const {return refCount;} +}; + +template < typename T > class RAK_DLL_EXPORT RakNetSmartPtr +{ +private: + T* ptr; // pointer + ReferenceCounter* reference; // Reference refCount + +public: + RakNetSmartPtr() : ptr(0), reference(0) + { + // Do not allocate by default, wasteful if we just have a list of preallocated and unassigend smart pointers + } + + RakNetSmartPtr(T* pValue) : ptr(pValue) + { + reference = RakNet::OP_NEW(_FILE_AND_LINE_); + reference->AddRef(); + +// allocCount+=2; +// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); + } + + RakNetSmartPtr(const RakNetSmartPtr& sp) : ptr(sp.ptr), reference(sp.reference) + { + if (reference) + reference->AddRef(); + } + + ~RakNetSmartPtr() + { + if(reference && reference->Release() == 0) + { + RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); + RakNet::OP_DELETE(reference, _FILE_AND_LINE_); + +// deallocCount+=2; +// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); + } + } + + bool IsNull(void) const + { + return ptr==0; + } + + void SetNull(void) + { + if(reference && reference->Release() == 0) + { + RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); + RakNet::OP_DELETE(reference, _FILE_AND_LINE_); + +// deallocCount+=2; +// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); + } + ptr=0; + reference=0; + } + + bool IsUnique(void) const + { + return reference->GetRefCount()==1; + } + + // Allow you to change the values of the internal contents of the pointer, without changing what is pointed to by other instances of the smart pointer + void Clone(bool copyContents) + { + if (IsUnique()==false) + { + reference->Release(); + + reference = RakNet::OP_NEW(_FILE_AND_LINE_); + reference->AddRef(); + T* oldPtr=ptr; + ptr=RakNet::OP_NEW(_FILE_AND_LINE_); + if (copyContents) + *ptr=*oldPtr; + } + } + + int GetRefCount(void) const + { + return reference->GetRefCount(); + } + + T& operator* () + { + return *ptr; + } + + const T& operator* () const + { + return *ptr; + } + + T* operator-> () + { + return ptr; + } + + const T* operator-> () const + { + return ptr; + } + + bool operator == (const RakNetSmartPtr& sp) + { + return ptr == sp.ptr; + } + bool operator<( const RakNetSmartPtr &right ) {return ptr < right.ptr;} + bool operator>( const RakNetSmartPtr &right ) {return ptr > right.ptr;} + + bool operator != (const RakNetSmartPtr& sp) + { + return ptr != sp.ptr; + } + + RakNetSmartPtr& operator = (const RakNetSmartPtr& sp) + { + // Assignment operator + + if (this != &sp) // Avoid self assignment + { + if(reference && reference->Release() == 0) + { + RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); + RakNet::OP_DELETE(reference, _FILE_AND_LINE_); + +// deallocCount+=2; +// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); + } + + ptr = sp.ptr; + reference = sp.reference; + if (reference) + reference->AddRef(); + } + return *this; + } + + +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetSocket.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakNetSocket.cpp index a3903b1..da95fb2 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetSocket.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetSocket.cpp @@ -1,25 +1,25 @@ -#include "RakNetSocket.h" -#include "SocketIncludes.h" -#include "SocketDefines.h" - -using namespace RakNet; - -RakNetSocket::RakNetSocket() { - s = (unsigned int)-1; -#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - recvEvent=INVALID_HANDLE_VALUE; -#endif -} -RakNetSocket::~RakNetSocket() -{ - if ((SOCKET)s != (SOCKET)-1) - closesocket__(s); - -#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - if (recvEvent!=INVALID_HANDLE_VALUE) - { - CloseHandle( recvEvent ); - recvEvent = INVALID_HANDLE_VALUE; - } -#endif -} +#include "RakNetSocket.h" +#include "SocketIncludes.h" +#include "SocketDefines.h" + +using namespace RakNet; + +RakNetSocket::RakNetSocket() { + s = (unsigned int)-1; +#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + recvEvent=INVALID_HANDLE_VALUE; +#endif +} +RakNetSocket::~RakNetSocket() +{ + if ((SOCKET)s != (SOCKET)-1) + closesocket__(s); + +#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + if (recvEvent!=INVALID_HANDLE_VALUE) + { + CloseHandle( recvEvent ); + recvEvent = INVALID_HANDLE_VALUE; + } +#endif +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetSocket.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetSocket.h index 49c9557..d55068f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetSocket.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetSocket.h @@ -1,38 +1,38 @@ -#ifndef __RAKNET_SOCKET_H -#define __RAKNET_SOCKET_H - -#include "RakNetTypes.h" -#include "RakNetDefines.h" -#include "Export.h" - -namespace RakNet -{ - -struct RAK_DLL_EXPORT RakNetSocket -{ - RakNetSocket(); - ~RakNetSocket(); - // SocketIncludes.h includes Windows.h, which messes up a lot of compiles - // SOCKET s; - unsigned int s; - unsigned int userConnectionSocketIndex; - SystemAddress boundAddress; - unsigned short socketFamily; - -#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - void* recvEvent; -#endif - - - - - - - unsigned short remotePortRakNetWasStartedOn_PS3_PSP2; - - unsigned int extraSocketOptions; -}; - -} // namespace RakNet - -#endif +#ifndef __RAKNET_SOCKET_H +#define __RAKNET_SOCKET_H + +#include "RakNetTypes.h" +#include "RakNetDefines.h" +#include "Export.h" + +namespace RakNet +{ + +struct RAK_DLL_EXPORT RakNetSocket +{ + RakNetSocket(); + ~RakNetSocket(); + // SocketIncludes.h includes Windows.h, which messes up a lot of compiles + // SOCKET s; + unsigned int s; + unsigned int userConnectionSocketIndex; + SystemAddress boundAddress; + unsigned short socketFamily; + +#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + void* recvEvent; +#endif + + + + + + + unsigned short remotePortRakNetWasStartedOn_PS3_PSP2; + + unsigned int extraSocketOptions; +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetStatistics.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakNetStatistics.cpp index eba7885..8f7ec2b 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetStatistics.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetStatistics.cpp @@ -1,148 +1,148 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "RakNetStatistics.h" -#include // sprintf -#include "GetTime.h" -#include "RakString.h" - -using namespace RakNet; - -// Verbosity level currently supports 0 (low), 1 (medium), 2 (high) -// Buffer must be hold enough to hold the output string. See the source to get an idea of how many bytes will be output -void RAK_DLL_EXPORT RakNet::StatisticsToString( RakNetStatistics *s, char *buffer, int verbosityLevel ) -{ - if ( s == 0 ) - { - sprintf( buffer, "stats is a NULL pointer in statsToString\n" ); - return ; - } - - if (verbosityLevel==0) - { - sprintf(buffer, - "Bytes per second sent %llu\n" - "Bytes per second received %llu\n" - "Current packetloss %.1f%%\n", - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], - s->packetlossLastSecond*100.0f - ); - } - else if (verbosityLevel==1) - { - sprintf(buffer, - "Actual bytes per second sent %llu\n" - "Actual bytes per second received %llu\n" - "Message bytes per second pushed %llu\n" - "Total actual bytes sent %llu\n" - "Total actual bytes received %llu\n" - "Total message bytes pushed %llu\n" - "Current packetloss %.1f%%\n" - "Average packetloss %.1f%%\n" - "Elapsed connection time in seconds %llu\n", - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_PUSHED], - (long long unsigned int) s->runningTotal[ACTUAL_BYTES_SENT], - (long long unsigned int) s->runningTotal[ACTUAL_BYTES_RECEIVED], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_PUSHED], - s->packetlossLastSecond*100.0f, - s->packetlossTotal*100.0f, - (long long unsigned int) (uint64_t)((RakNet::GetTimeUS()-s->connectionStartTime)/1000000) - ); - - if (s->BPSLimitByCongestionControl!=0) - { - char buff2[128]; - sprintf(buff2, - "Send capacity %llu bytes per second (%.0f%%)\n", - (long long unsigned int) s->BPSLimitByCongestionControl, - 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByCongestionControl - ); - strcat(buffer,buff2); - } - if (s->BPSLimitByOutgoingBandwidthLimit!=0) - { - char buff2[128]; - sprintf(buff2, - "Send limit %llu (%.0f%%)\n", - (long long unsigned int) s->BPSLimitByOutgoingBandwidthLimit, - 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByOutgoingBandwidthLimit - ); - strcat(buffer,buff2); - } - } - else - { - sprintf(buffer, - "Actual bytes per second sent %llu\n" - "Actual bytes per second received %llu\n" - "Message bytes per second sent %llu\n" - "Message bytes per second resent %llu\n" - "Message bytes per second pushed %llu\n" - "Message bytes per second processed %llu\n" - "Message bytes per second ignored %llu\n" - "Total bytes sent %llu\n" - "Total bytes received %llu\n" - "Total message bytes sent %llu\n" - "Total message bytes resent %llu\n" - "Total message bytes pushed %llu\n" - "Total message bytes received %llu\n" - "Total message bytes ignored %llu\n" - "Messages in send buffer, by priority %i,%i,%i,%i\n" - "Bytes in send buffer, by priority %i,%i,%i,%i\n" - "Messages in resend buffer %i\n" - "Bytes in resend buffer %llu\n" - "Current packetloss %.1f%%\n" - "Average packetloss %.1f%%\n" - "Elapsed connection time in seconds %llu\n", - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_SENT], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_PUSHED], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RECEIVED_PROCESSED], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RECEIVED_IGNORED], - (long long unsigned int) s->runningTotal[ACTUAL_BYTES_SENT], - (long long unsigned int) s->runningTotal[ACTUAL_BYTES_RECEIVED], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_SENT], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RESENT], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_PUSHED], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RECEIVED_PROCESSED], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RECEIVED_IGNORED], - s->messageInSendBuffer[IMMEDIATE_PRIORITY],s->messageInSendBuffer[HIGH_PRIORITY],s->messageInSendBuffer[MEDIUM_PRIORITY],s->messageInSendBuffer[LOW_PRIORITY], - (unsigned int) s->bytesInSendBuffer[IMMEDIATE_PRIORITY],(unsigned int) s->bytesInSendBuffer[HIGH_PRIORITY],(unsigned int) s->bytesInSendBuffer[MEDIUM_PRIORITY],(unsigned int) s->bytesInSendBuffer[LOW_PRIORITY], - s->messagesInResendBuffer, - (long long unsigned int) s->bytesInResendBuffer, - s->packetlossLastSecond*100.0f, - s->packetlossTotal*100.0f, - (long long unsigned int) (uint64_t)((RakNet::GetTimeUS()-s->connectionStartTime)/1000000) - ); - - if (s->BPSLimitByCongestionControl!=0) - { - char buff2[128]; - sprintf(buff2, - "Send capacity %llu bytes per second (%.0f%%)\n", - (long long unsigned int) s->BPSLimitByCongestionControl, - 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByCongestionControl - ); - strcat(buffer,buff2); - } - if (s->BPSLimitByOutgoingBandwidthLimit!=0) - { - char buff2[128]; - sprintf(buff2, - "Send limit %llu (%.0f%%)\n", - (long long unsigned int) s->BPSLimitByOutgoingBandwidthLimit, - 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByOutgoingBandwidthLimit - ); - strcat(buffer,buff2); - } - } -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "RakNetStatistics.h" +#include // sprintf +#include "GetTime.h" +#include "RakString.h" + +using namespace RakNet; + +// Verbosity level currently supports 0 (low), 1 (medium), 2 (high) +// Buffer must be hold enough to hold the output string. See the source to get an idea of how many bytes will be output +void RAK_DLL_EXPORT RakNet::StatisticsToString( RakNetStatistics *s, char *buffer, int verbosityLevel ) +{ + if ( s == 0 ) + { + sprintf( buffer, "stats is a NULL pointer in statsToString\n" ); + return ; + } + + if (verbosityLevel==0) + { + sprintf(buffer, + "Bytes per second sent %llu\n" + "Bytes per second received %llu\n" + "Current packetloss %.1f%%\n", + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], + s->packetlossLastSecond*100.0f + ); + } + else if (verbosityLevel==1) + { + sprintf(buffer, + "Actual bytes per second sent %llu\n" + "Actual bytes per second received %llu\n" + "Message bytes per second pushed %llu\n" + "Total actual bytes sent %llu\n" + "Total actual bytes received %llu\n" + "Total message bytes pushed %llu\n" + "Current packetloss %.1f%%\n" + "Average packetloss %.1f%%\n" + "Elapsed connection time in seconds %llu\n", + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_PUSHED], + (long long unsigned int) s->runningTotal[ACTUAL_BYTES_SENT], + (long long unsigned int) s->runningTotal[ACTUAL_BYTES_RECEIVED], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_PUSHED], + s->packetlossLastSecond*100.0f, + s->packetlossTotal*100.0f, + (long long unsigned int) (uint64_t)((RakNet::GetTimeUS()-s->connectionStartTime)/1000000) + ); + + if (s->BPSLimitByCongestionControl!=0) + { + char buff2[128]; + sprintf(buff2, + "Send capacity %llu bytes per second (%.0f%%)\n", + (long long unsigned int) s->BPSLimitByCongestionControl, + 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByCongestionControl + ); + strcat(buffer,buff2); + } + if (s->BPSLimitByOutgoingBandwidthLimit!=0) + { + char buff2[128]; + sprintf(buff2, + "Send limit %llu (%.0f%%)\n", + (long long unsigned int) s->BPSLimitByOutgoingBandwidthLimit, + 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByOutgoingBandwidthLimit + ); + strcat(buffer,buff2); + } + } + else + { + sprintf(buffer, + "Actual bytes per second sent %llu\n" + "Actual bytes per second received %llu\n" + "Message bytes per second sent %llu\n" + "Message bytes per second resent %llu\n" + "Message bytes per second pushed %llu\n" + "Message bytes per second processed %llu\n" + "Message bytes per second ignored %llu\n" + "Total bytes sent %llu\n" + "Total bytes received %llu\n" + "Total message bytes sent %llu\n" + "Total message bytes resent %llu\n" + "Total message bytes pushed %llu\n" + "Total message bytes received %llu\n" + "Total message bytes ignored %llu\n" + "Messages in send buffer, by priority %i,%i,%i,%i\n" + "Bytes in send buffer, by priority %i,%i,%i,%i\n" + "Messages in resend buffer %i\n" + "Bytes in resend buffer %llu\n" + "Current packetloss %.1f%%\n" + "Average packetloss %.1f%%\n" + "Elapsed connection time in seconds %llu\n", + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_SENT], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_PUSHED], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RECEIVED_PROCESSED], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RECEIVED_IGNORED], + (long long unsigned int) s->runningTotal[ACTUAL_BYTES_SENT], + (long long unsigned int) s->runningTotal[ACTUAL_BYTES_RECEIVED], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_SENT], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RESENT], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_PUSHED], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RECEIVED_PROCESSED], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RECEIVED_IGNORED], + s->messageInSendBuffer[IMMEDIATE_PRIORITY],s->messageInSendBuffer[HIGH_PRIORITY],s->messageInSendBuffer[MEDIUM_PRIORITY],s->messageInSendBuffer[LOW_PRIORITY], + (unsigned int) s->bytesInSendBuffer[IMMEDIATE_PRIORITY],(unsigned int) s->bytesInSendBuffer[HIGH_PRIORITY],(unsigned int) s->bytesInSendBuffer[MEDIUM_PRIORITY],(unsigned int) s->bytesInSendBuffer[LOW_PRIORITY], + s->messagesInResendBuffer, + (long long unsigned int) s->bytesInResendBuffer, + s->packetlossLastSecond*100.0f, + s->packetlossTotal*100.0f, + (long long unsigned int) (uint64_t)((RakNet::GetTimeUS()-s->connectionStartTime)/1000000) + ); + + if (s->BPSLimitByCongestionControl!=0) + { + char buff2[128]; + sprintf(buff2, + "Send capacity %llu bytes per second (%.0f%%)\n", + (long long unsigned int) s->BPSLimitByCongestionControl, + 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByCongestionControl + ); + strcat(buffer,buff2); + } + if (s->BPSLimitByOutgoingBandwidthLimit!=0) + { + char buff2[128]; + sprintf(buff2, + "Send limit %llu (%.0f%%)\n", + (long long unsigned int) s->BPSLimitByOutgoingBandwidthLimit, + 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByOutgoingBandwidthLimit + ); + strcat(buffer,buff2); + } + } +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetStatistics.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetStatistics.h index 4f9cd6b..634a010 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetStatistics.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetStatistics.h @@ -1,126 +1,126 @@ -/// \file -/// \brief A structure that holds all statistical data returned by RakNet. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - - -#ifndef __RAK_NET_STATISTICS_H -#define __RAK_NET_STATISTICS_H - -#include "PacketPriority.h" -#include "Export.h" -#include "RakNetTypes.h" - -namespace RakNet -{ - -enum RNSPerSecondMetrics -{ - /// How many bytes per pushed via a call to RakPeerInterface::Send() - USER_MESSAGE_BYTES_PUSHED, - - /// How many user message bytes were sent via a call to RakPeerInterface::Send(). This is less than or equal to USER_MESSAGE_BYTES_PUSHED. - /// A message would be pushed, but not yet sent, due to congestion control - USER_MESSAGE_BYTES_SENT, - - /// How many user message bytes were resent. A message is resent if it is marked as reliable, and either the message didn't arrive or the message ack didn't arrive. - USER_MESSAGE_BYTES_RESENT, - - /// How many user message bytes were received, and processed successfully. - USER_MESSAGE_BYTES_RECEIVED_PROCESSED, - - /// How many user message bytes were received, but ignored due to data format errors. This will usually be 0. - USER_MESSAGE_BYTES_RECEIVED_IGNORED, - - /// How many actual bytes were sent, including per-message and per-datagram overhead, and reliable message acks - ACTUAL_BYTES_SENT, - - /// How many actual bytes were received, including overead and acks. - ACTUAL_BYTES_RECEIVED, - - /// \internal - RNS_PER_SECOND_METRICS_COUNT -}; - -/// \brief Network Statisics Usage -/// -/// Store Statistics information related to network usage -struct RAK_DLL_EXPORT RakNetStatistics -{ - /// For each type in RNSPerSecondMetrics, what is the value over the last 1 second? - uint64_t valueOverLastSecond[RNS_PER_SECOND_METRICS_COUNT]; - - /// For each type in RNSPerSecondMetrics, what is the total value over the lifetime of the connection? - uint64_t runningTotal[RNS_PER_SECOND_METRICS_COUNT]; - - /// When did the connection start? - /// \sa RakNet::GetTimeUS() - RakNet::TimeUS connectionStartTime; - - /// Is our current send rate throttled by congestion control? - /// This value should be true if you send more data per second than your bandwidth capacity - bool isLimitedByCongestionControl; - - /// If \a isLimitedByCongestionControl is true, what is the limit, in bytes per second? - uint64_t BPSLimitByCongestionControl; - - /// Is our current send rate throttled by a call to RakPeer::SetPerConnectionOutgoingBandwidthLimit()? - bool isLimitedByOutgoingBandwidthLimit; - - /// If \a isLimitedByOutgoingBandwidthLimit is true, what is the limit, in bytes per second? - uint64_t BPSLimitByOutgoingBandwidthLimit; - - /// For each priority level, how many messages are waiting to be sent out? - unsigned int messageInSendBuffer[NUMBER_OF_PRIORITIES]; - - /// For each priority level, how many bytes are waiting to be sent out? - double bytesInSendBuffer[NUMBER_OF_PRIORITIES]; - - /// How many messages are waiting in the resend buffer? This includes messages waiting for an ack, so should normally be a small value - /// If the value is rising over time, you are exceeding the bandwidth capacity. See BPSLimitByCongestionControl - unsigned int messagesInResendBuffer; - - /// How many bytes are waiting in the resend buffer. See also messagesInResendBuffer - uint64_t bytesInResendBuffer; - - /// Over the last second, what was our packetloss? This number will range from 0.0 (for none) to 1.0 (for 100%) - float packetlossLastSecond; - - /// What is the average total packetloss over the lifetime of the connection? - float packetlossTotal; - - RakNetStatistics& operator +=(const RakNetStatistics& other) - { - unsigned i; - for (i=0; i < NUMBER_OF_PRIORITIES; i++) - { - messageInSendBuffer[i]+=other.messageInSendBuffer[i]; - bytesInSendBuffer[i]+=other.bytesInSendBuffer[i]; - } - - for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) - { - valueOverLastSecond[i]+=other.valueOverLastSecond[i]; - runningTotal[i]+=other.runningTotal[i]; - } - - return *this; - } -}; - -/// Verbosity level currently supports 0 (low), 1 (medium), 2 (high) -/// \param[in] s The Statistical information to format out -/// \param[in] buffer The buffer containing a formated report -/// \param[in] verbosityLevel -/// 0 low -/// 1 medium -/// 2 high -/// 3 debugging congestion control -void RAK_DLL_EXPORT StatisticsToString( RakNetStatistics *s, char *buffer, int verbosityLevel ); - -} // namespace RakNet - -#endif +/// \file +/// \brief A structure that holds all statistical data returned by RakNet. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + + +#ifndef __RAK_NET_STATISTICS_H +#define __RAK_NET_STATISTICS_H + +#include "PacketPriority.h" +#include "Export.h" +#include "RakNetTypes.h" + +namespace RakNet +{ + +enum RNSPerSecondMetrics +{ + /// How many bytes per pushed via a call to RakPeerInterface::Send() + USER_MESSAGE_BYTES_PUSHED, + + /// How many user message bytes were sent via a call to RakPeerInterface::Send(). This is less than or equal to USER_MESSAGE_BYTES_PUSHED. + /// A message would be pushed, but not yet sent, due to congestion control + USER_MESSAGE_BYTES_SENT, + + /// How many user message bytes were resent. A message is resent if it is marked as reliable, and either the message didn't arrive or the message ack didn't arrive. + USER_MESSAGE_BYTES_RESENT, + + /// How many user message bytes were received, and processed successfully. + USER_MESSAGE_BYTES_RECEIVED_PROCESSED, + + /// How many user message bytes were received, but ignored due to data format errors. This will usually be 0. + USER_MESSAGE_BYTES_RECEIVED_IGNORED, + + /// How many actual bytes were sent, including per-message and per-datagram overhead, and reliable message acks + ACTUAL_BYTES_SENT, + + /// How many actual bytes were received, including overead and acks. + ACTUAL_BYTES_RECEIVED, + + /// \internal + RNS_PER_SECOND_METRICS_COUNT +}; + +/// \brief Network Statisics Usage +/// +/// Store Statistics information related to network usage +struct RAK_DLL_EXPORT RakNetStatistics +{ + /// For each type in RNSPerSecondMetrics, what is the value over the last 1 second? + uint64_t valueOverLastSecond[RNS_PER_SECOND_METRICS_COUNT]; + + /// For each type in RNSPerSecondMetrics, what is the total value over the lifetime of the connection? + uint64_t runningTotal[RNS_PER_SECOND_METRICS_COUNT]; + + /// When did the connection start? + /// \sa RakNet::GetTimeUS() + RakNet::TimeUS connectionStartTime; + + /// Is our current send rate throttled by congestion control? + /// This value should be true if you send more data per second than your bandwidth capacity + bool isLimitedByCongestionControl; + + /// If \a isLimitedByCongestionControl is true, what is the limit, in bytes per second? + uint64_t BPSLimitByCongestionControl; + + /// Is our current send rate throttled by a call to RakPeer::SetPerConnectionOutgoingBandwidthLimit()? + bool isLimitedByOutgoingBandwidthLimit; + + /// If \a isLimitedByOutgoingBandwidthLimit is true, what is the limit, in bytes per second? + uint64_t BPSLimitByOutgoingBandwidthLimit; + + /// For each priority level, how many messages are waiting to be sent out? + unsigned int messageInSendBuffer[NUMBER_OF_PRIORITIES]; + + /// For each priority level, how many bytes are waiting to be sent out? + double bytesInSendBuffer[NUMBER_OF_PRIORITIES]; + + /// How many messages are waiting in the resend buffer? This includes messages waiting for an ack, so should normally be a small value + /// If the value is rising over time, you are exceeding the bandwidth capacity. See BPSLimitByCongestionControl + unsigned int messagesInResendBuffer; + + /// How many bytes are waiting in the resend buffer. See also messagesInResendBuffer + uint64_t bytesInResendBuffer; + + /// Over the last second, what was our packetloss? This number will range from 0.0 (for none) to 1.0 (for 100%) + float packetlossLastSecond; + + /// What is the average total packetloss over the lifetime of the connection? + float packetlossTotal; + + RakNetStatistics& operator +=(const RakNetStatistics& other) + { + unsigned i; + for (i=0; i < NUMBER_OF_PRIORITIES; i++) + { + messageInSendBuffer[i]+=other.messageInSendBuffer[i]; + bytesInSendBuffer[i]+=other.bytesInSendBuffer[i]; + } + + for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) + { + valueOverLastSecond[i]+=other.valueOverLastSecond[i]; + runningTotal[i]+=other.runningTotal[i]; + } + + return *this; + } +}; + +/// Verbosity level currently supports 0 (low), 1 (medium), 2 (high) +/// \param[in] s The Statistical information to format out +/// \param[in] buffer The buffer containing a formated report +/// \param[in] verbosityLevel +/// 0 low +/// 1 medium +/// 2 high +/// 3 debugging congestion control +void RAK_DLL_EXPORT StatisticsToString( RakNetStatistics *s, char *buffer, int verbosityLevel ); + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetTime.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetTime.h index f9e7cd9..1c76b83 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetTime.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetTime.h @@ -1,23 +1,23 @@ -#ifndef __RAKNET_TIME_H -#define __RAKNET_TIME_H - -#include "NativeTypes.h" -#include "RakNetDefines.h" - -namespace RakNet { - -// Define __GET_TIME_64BIT if you want to use large types for GetTime (takes more bandwidth when you transmit time though!) -// You would want to do this if your system is going to run long enough to overflow the millisecond counter (over a month) -#if __GET_TIME_64BIT==1 -typedef uint64_t Time; -typedef uint32_t TimeMS; -typedef uint64_t TimeUS; -#else -typedef uint32_t Time; -typedef uint32_t TimeMS; -typedef uint64_t TimeUS; -#endif - -}; // namespace RakNet - -#endif +#ifndef __RAKNET_TIME_H +#define __RAKNET_TIME_H + +#include "NativeTypes.h" +#include "RakNetDefines.h" + +namespace RakNet { + +// Define __GET_TIME_64BIT if you want to use large types for GetTime (takes more bandwidth when you transmit time though!) +// You would want to do this if your system is going to run long enough to overflow the millisecond counter (over a month) +#if __GET_TIME_64BIT==1 +typedef uint64_t Time; +typedef uint32_t TimeMS; +typedef uint64_t TimeUS; +#else +typedef uint32_t Time; +typedef uint32_t TimeMS; +typedef uint64_t TimeUS; +#endif + +}; // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetTransport2.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakNetTransport2.cpp index 514318f..e03e155 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetTransport2.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetTransport2.cpp @@ -1,128 +1,128 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TelnetTransport==1 - -#include "RakNetTransport2.h" - -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include -#include -#include -#include "LinuxStrings.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(RakNetTransport2,RakNetTransport2); - -RakNetTransport2::RakNetTransport2() -{ -} -RakNetTransport2::~RakNetTransport2() -{ - Stop(); -} -bool RakNetTransport2::Start(unsigned short port, bool serverMode) -{ - (void) port; - (void) serverMode; - return true; -} -void RakNetTransport2::Stop(void) -{ - newConnections.Clear(_FILE_AND_LINE_); - lostConnections.Clear(_FILE_AND_LINE_); - for (unsigned int i=0; i < packetQueue.Size(); i++) - { - rakFree_Ex(packetQueue[i]->data,_FILE_AND_LINE_); - RakNet::OP_DELETE(packetQueue[i],_FILE_AND_LINE_); - } - packetQueue.Clear(_FILE_AND_LINE_); -} -void RakNetTransport2::Send( SystemAddress systemAddress, const char *data, ... ) -{ - if (data==0 || data[0]==0) return; - - char text[REMOTE_MAX_TEXT_INPUT]; - va_list ap; - va_start(ap, data); - _vsnprintf(text, REMOTE_MAX_TEXT_INPUT, data, ap); - va_end(ap); - text[REMOTE_MAX_TEXT_INPUT-1]=0; - - RakNet::BitStream str; - str.Write((MessageID)ID_TRANSPORT_STRING); - str.Write(text, (int) strlen(text)); - str.Write((unsigned char) 0); // Null terminate the string - rakPeerInterface->Send(&str, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, (systemAddress==UNASSIGNED_SYSTEM_ADDRESS)!=0); -} -void RakNetTransport2::CloseConnection( SystemAddress systemAddress ) -{ - rakPeerInterface->CloseConnection(systemAddress, true, 0); -} -Packet* RakNetTransport2::Receive( void ) -{ - if (packetQueue.Size()==0) - return 0; - return packetQueue.Pop(); -} -SystemAddress RakNetTransport2::HasNewIncomingConnection(void) -{ - if (newConnections.Size()) - return newConnections.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -SystemAddress RakNetTransport2::HasLostConnection(void) -{ - if (lostConnections.Size()) - return lostConnections.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -void RakNetTransport2::DeallocatePacket( Packet *packet ) -{ - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(packet, _FILE_AND_LINE_ ); -} -PluginReceiveResult RakNetTransport2::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_TRANSPORT_STRING: - { - if (packet->length==sizeof(MessageID)) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - Packet *p = RakNet::OP_NEW(_FILE_AND_LINE_); - *p=*packet; - p->bitSize-=8; - p->length--; - p->data=(unsigned char*) rakMalloc_Ex(p->length,_FILE_AND_LINE_); - memcpy(p->data, packet->data+1, p->length); - packetQueue.Push(p, _FILE_AND_LINE_ ); - - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - return RR_CONTINUE_PROCESSING; -} -void RakNetTransport2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) rakNetGUID; - (void) lostConnectionReason; - lostConnections.Push(systemAddress, _FILE_AND_LINE_ ); -} -void RakNetTransport2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) rakNetGUID; - (void) isIncoming; - newConnections.Push(systemAddress, _FILE_AND_LINE_ ); -} -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TelnetTransport==1 + +#include "RakNetTransport2.h" + +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include +#include +#include +#include "LinuxStrings.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(RakNetTransport2,RakNetTransport2); + +RakNetTransport2::RakNetTransport2() +{ +} +RakNetTransport2::~RakNetTransport2() +{ + Stop(); +} +bool RakNetTransport2::Start(unsigned short port, bool serverMode) +{ + (void) port; + (void) serverMode; + return true; +} +void RakNetTransport2::Stop(void) +{ + newConnections.Clear(_FILE_AND_LINE_); + lostConnections.Clear(_FILE_AND_LINE_); + for (unsigned int i=0; i < packetQueue.Size(); i++) + { + rakFree_Ex(packetQueue[i]->data,_FILE_AND_LINE_); + RakNet::OP_DELETE(packetQueue[i],_FILE_AND_LINE_); + } + packetQueue.Clear(_FILE_AND_LINE_); +} +void RakNetTransport2::Send( SystemAddress systemAddress, const char *data, ... ) +{ + if (data==0 || data[0]==0) return; + + char text[REMOTE_MAX_TEXT_INPUT]; + va_list ap; + va_start(ap, data); + _vsnprintf(text, REMOTE_MAX_TEXT_INPUT, data, ap); + va_end(ap); + text[REMOTE_MAX_TEXT_INPUT-1]=0; + + RakNet::BitStream str; + str.Write((MessageID)ID_TRANSPORT_STRING); + str.Write(text, (int) strlen(text)); + str.Write((unsigned char) 0); // Null terminate the string + rakPeerInterface->Send(&str, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, (systemAddress==UNASSIGNED_SYSTEM_ADDRESS)!=0); +} +void RakNetTransport2::CloseConnection( SystemAddress systemAddress ) +{ + rakPeerInterface->CloseConnection(systemAddress, true, 0); +} +Packet* RakNetTransport2::Receive( void ) +{ + if (packetQueue.Size()==0) + return 0; + return packetQueue.Pop(); +} +SystemAddress RakNetTransport2::HasNewIncomingConnection(void) +{ + if (newConnections.Size()) + return newConnections.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +SystemAddress RakNetTransport2::HasLostConnection(void) +{ + if (lostConnections.Size()) + return lostConnections.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +void RakNetTransport2::DeallocatePacket( Packet *packet ) +{ + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(packet, _FILE_AND_LINE_ ); +} +PluginReceiveResult RakNetTransport2::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_TRANSPORT_STRING: + { + if (packet->length==sizeof(MessageID)) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + Packet *p = RakNet::OP_NEW(_FILE_AND_LINE_); + *p=*packet; + p->bitSize-=8; + p->length--; + p->data=(unsigned char*) rakMalloc_Ex(p->length,_FILE_AND_LINE_); + memcpy(p->data, packet->data+1, p->length); + packetQueue.Push(p, _FILE_AND_LINE_ ); + + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + return RR_CONTINUE_PROCESSING; +} +void RakNetTransport2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) rakNetGUID; + (void) lostConnectionReason; + lostConnections.Push(systemAddress, _FILE_AND_LINE_ ); +} +void RakNetTransport2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) rakNetGUID; + (void) isIncoming; + newConnections.Push(systemAddress, _FILE_AND_LINE_ ); +} +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetTransport2.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetTransport2.h index a8b522d..1125048 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetTransport2.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetTransport2.h @@ -1,102 +1,102 @@ -/// \file -/// \brief Contains RakNetTransportCommandParser and RakNetTransport used to provide a secure console connection. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TelnetTransport==1 - -#ifndef __RAKNET_TRANSPORT_2 -#define __RAKNET_TRANSPORT_2 - -#include "TransportInterface.h" -#include "DS_Queue.h" -#include "CommandParserInterface.h" -#include "PluginInterface2.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class BitStream; -class RakPeerInterface; -class RakNetTransport; - -/// \defgroup RAKNET_TRANSPORT_GROUP RakNetTransport -/// \brief UDP based transport implementation for the ConsoleServer -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief Use RakNetTransport if you need a secure connection between the client and the console server. -/// \details RakNetTransport automatically initializes security for the system. Use the project CommandConsoleClient to connect -/// To the ConsoleServer if you use RakNetTransport -/// \ingroup RAKNET_TRANSPORT_GROUP -class RAK_DLL_EXPORT RakNetTransport2 : public TransportInterface, public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(RakNetTransport2) - - RakNetTransport2(); - virtual ~RakNetTransport2(); - - /// Start the transport provider on the indicated port. - /// \param[in] port The port to start the transport provider on - /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) - /// \return Return true on success, false on failure. - bool Start(unsigned short port, bool serverMode); - - /// Stop the transport provider. You can clear memory and shutdown threads here. - void Stop(void); - - /// Send a null-terminated string to \a systemAddress - /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here - /// and parse it out in Receive(). - /// \param[in] systemAddress The player to send the string to - /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF - /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF - void Send( SystemAddress systemAddress, const char *data, ... ); - - /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. - /// \param[in] systemAddress The player/address to disconnect - void CloseConnection( SystemAddress systemAddress ); - - /// Return a string. The string should be allocated and written to Packet::data . - /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress - /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet - /// and thus only return a string in Packet::data - /// \return The packet structure containing the result of Receive, or 0 if no data is available - Packet* Receive( void ); - - /// Deallocate the Packet structure returned by Receive - /// \param[in] The packet to deallocate - void DeallocatePacket( Packet *packet ); - - /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. - /// \return The SystemAddress/address of the system - SystemAddress HasNewIncomingConnection(void); - - /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. - /// \return The SystemAddress/address of the system - SystemAddress HasLostConnection(void); - - virtual CommandParserInterface* GetCommandParser(void) {return 0;} - - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); -protected: - DataStructures::Queue newConnections, lostConnections; - DataStructures::Queue packetQueue; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains RakNetTransportCommandParser and RakNetTransport used to provide a secure console connection. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TelnetTransport==1 + +#ifndef __RAKNET_TRANSPORT_2 +#define __RAKNET_TRANSPORT_2 + +#include "TransportInterface.h" +#include "DS_Queue.h" +#include "CommandParserInterface.h" +#include "PluginInterface2.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class BitStream; +class RakPeerInterface; +class RakNetTransport; + +/// \defgroup RAKNET_TRANSPORT_GROUP RakNetTransport +/// \brief UDP based transport implementation for the ConsoleServer +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief Use RakNetTransport if you need a secure connection between the client and the console server. +/// \details RakNetTransport automatically initializes security for the system. Use the project CommandConsoleClient to connect +/// To the ConsoleServer if you use RakNetTransport +/// \ingroup RAKNET_TRANSPORT_GROUP +class RAK_DLL_EXPORT RakNetTransport2 : public TransportInterface, public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(RakNetTransport2) + + RakNetTransport2(); + virtual ~RakNetTransport2(); + + /// Start the transport provider on the indicated port. + /// \param[in] port The port to start the transport provider on + /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) + /// \return Return true on success, false on failure. + bool Start(unsigned short port, bool serverMode); + + /// Stop the transport provider. You can clear memory and shutdown threads here. + void Stop(void); + + /// Send a null-terminated string to \a systemAddress + /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here + /// and parse it out in Receive(). + /// \param[in] systemAddress The player to send the string to + /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF + /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF + void Send( SystemAddress systemAddress, const char *data, ... ); + + /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. + /// \param[in] systemAddress The player/address to disconnect + void CloseConnection( SystemAddress systemAddress ); + + /// Return a string. The string should be allocated and written to Packet::data . + /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress + /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet + /// and thus only return a string in Packet::data + /// \return The packet structure containing the result of Receive, or 0 if no data is available + Packet* Receive( void ); + + /// Deallocate the Packet structure returned by Receive + /// \param[in] The packet to deallocate + void DeallocatePacket( Packet *packet ); + + /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + SystemAddress HasNewIncomingConnection(void); + + /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + SystemAddress HasLostConnection(void); + + virtual CommandParserInterface* GetCommandParser(void) {return 0;} + + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); +protected: + DataStructures::Queue newConnections, lostConnections; + DataStructures::Queue packetQueue; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetTypes.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakNetTypes.cpp index d39af87..2a2b109 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetTypes.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetTypes.cpp @@ -1,725 +1,725 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "RakNetTypes.h" -#include "RakAssert.h" -#include -#include -#include "WindowsIncludes.h" -#include "WSAStartupSingleton.h" -#include "SocketDefines.h" - - -#if defined(_WIN32) -// extern __int64 _strtoui64(const char*, char**, int); // needed for Code::Blocks. Does not compile on Visual Studio 2010 -// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib -// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly -#include - -#else -#include -#include -#include -#endif - -#include // strncasecmp -#include "Itoa.h" -#include "SocketLayer.h" -#include "SuperFastHash.h" -#include - -using namespace RakNet; - -const char *IPV6_LOOPBACK="::1"; -const char *IPV4_LOOPBACK="127.0.0.1"; - -AddressOrGUID::AddressOrGUID( Packet *packet ) -{ - rakNetGuid=packet->guid; - systemAddress=packet->systemAddress; -} - -unsigned long AddressOrGUID::ToInteger( const AddressOrGUID &aog ) -{ - if (aog.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return RakNetGUID::ToUint32(aog.rakNetGuid); - return SystemAddress::ToInteger(aog.systemAddress); -} -const char *AddressOrGUID::ToString(bool writePort) const -{ - if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return rakNetGuid.ToString(); - return systemAddress.ToString(writePort); -} -void AddressOrGUID::ToString(bool writePort, char *dest) const -{ - if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return rakNetGuid.ToString(dest); - return systemAddress.ToString(writePort,dest); -} -bool RakNet::NonNumericHostString( const char *host ) -{ - if ( host[ 0 ] >= '0' && host[ 0 ] <= '9' ) - return false; - - if ( (host[ 0 ] == '-') && ( host[ 1 ] >= '0' && host[ 1 ] <= '9' ) ) - return false; - - if ( strstr(host,":") ) - return false; - - return true; -} - -SocketDescriptor::SocketDescriptor() {port=0; hostAddress[0]=0; remotePortRakNetWasStartedOn_PS3_PSP2=0; extraSocketOptions=0; socketFamily=AF_INET;} -SocketDescriptor::SocketDescriptor(unsigned short _port, const char *_hostAddress) -{ - remotePortRakNetWasStartedOn_PS3_PSP2=0; - port=_port; - if (_hostAddress) - strcpy(hostAddress, _hostAddress); - else - hostAddress[0]=0; - extraSocketOptions=0; - socketFamily=AF_INET; -} - -// Defaults to not in peer to peer mode for NetworkIDs. This only sends the localSystemAddress portion in the BitStream class -// This is what you want for client/server, where the server assigns all NetworkIDs and it is unnecessary to transmit the full structure. -// For peer to peer, this will transmit the systemAddress of the system that created the object in addition to localSystemAddress. This allows -// Any system to create unique ids locally. -// All systems must use the same value for this variable. -//bool RAK_DLL_EXPORT NetworkID::peerToPeerMode=false; - -SystemAddress& SystemAddress::operator = ( const SystemAddress& input ) -{ - memcpy(&address, &input.address, sizeof(address)); - systemIndex = input.systemIndex; - debugPort = input.debugPort; - return *this; -} -bool SystemAddress::EqualsExcludingPort( const SystemAddress& right ) const -{ - return (address.addr4.sin_family==AF_INET && address.addr4.sin_addr.s_addr==right.address.addr4.sin_addr.s_addr) -#if RAKNET_SUPPORT_IPV6==1 - || (address.addr4.sin_family==AF_INET6 && memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))==0) -#endif - ; -} -unsigned short SystemAddress::GetPort(void) const -{ - return ntohs(address.addr4.sin_port); -} -unsigned short SystemAddress::GetPortNetworkOrder(void) const -{ - return address.addr4.sin_port; -} -void SystemAddress::SetPort(unsigned short s) -{ - address.addr4.sin_port=htons(s); - debugPort=s; -} -void SystemAddress::SetPortNetworkOrder(unsigned short s) -{ - address.addr4.sin_port=s; - debugPort=ntohs(s); -} -bool SystemAddress::operator==( const SystemAddress& right ) const -{ - return address.addr4.sin_port == right.address.addr4.sin_port && EqualsExcludingPort(right); -} - -bool SystemAddress::operator!=( const SystemAddress& right ) const -{ - return (*this==right)==false; -} - -bool SystemAddress::operator>( const SystemAddress& right ) const -{ - if (address.addr4.sin_port == right.address.addr4.sin_port) - { -#if RAKNET_SUPPORT_IPV6==1 - if (address.addr4.sin_family==AF_INET) - return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr; - return memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))>0; -#else - return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr; -#endif - } - return address.addr4.sin_port>right.address.addr4.sin_port; -} - -bool SystemAddress::operator<( const SystemAddress& right ) const -{ - if (address.addr4.sin_port == right.address.addr4.sin_port) - { -#if RAKNET_SUPPORT_IPV6==1 - if (address.addr4.sin_family==AF_INET) - return address.addr4.sin_addr.s_addr0; -#else - return address.addr4.sin_addr.s_addr'9')) - break; - IPPart[index]=str[index]; - } - IPPart[index]=0; - portPart[0]=0; - if (str[index] && str[index+1]) - { - index++; - for (portIndex=0; portIndex<10 && str[index] && index < 22+10; index++, portIndex++) - { - if (str[index]<'0' || str[index]>'9') - break; - - portPart[portIndex]=str[index]; - } - portPart[portIndex]=0; - } - - - - - - - - - - - - - - if (IPPart[0]) - { - - - - address.addr4.sin_addr.s_addr=inet_addr__(IPPart); - - } - - - if (portPart[0]) - { - address.addr4.sin_port=htons((unsigned short) atoi(portPart)); - debugPort=ntohs(address.addr4.sin_port); - } - //#endif - } -} - -bool SystemAddress::FromString(const char *str, char portDelineator, int ipVersion) -{ -#if RAKNET_SUPPORT_IPV6!=1 - (void) ipVersion; - SetBinaryAddress(str,portDelineator); - return true; -#else - if (str==0) - { - memset(&address,0,sizeof(address)); - address.addr4.sin_family=AF_INET; - return true; - } -#if RAKNET_SUPPORT_IPV6==1 - char ipPart[INET6_ADDRSTRLEN]; -#else - char ipPart[INET_ADDRSTRLEN]; -#endif - char portPart[32]; - int i=0,j; - - // TODO - what about 255.255.255.255? - if (ipVersion==4 && strcmp(str, IPV6_LOOPBACK)==0) - { - strcpy(ipPart,IPV4_LOOPBACK); - } - else if (ipVersion==6 && strcmp(str, IPV4_LOOPBACK)==0) - { - address.addr4.sin_family=AF_INET6; - strcpy(ipPart,IPV6_LOOPBACK); - } - else if (NonNumericHostString(str)==false) - { - for (; i < sizeof(ipPart) && str[i]!=0 && str[i]!=portDelineator; i++) - { - if ((str[i]<'0' || str[i]>'9') && (str[i]<'a' || str[i]>'f') && (str[i]<'A' || str[i]>'F') && str[i]!='.' && str[i]!=':' && str[i]!='%' && str[i]!='-' && str[i]!='/') - break; - - ipPart[i]=str[i]; - } - ipPart[i]=0; - } - else - { - strncpy(ipPart,str,sizeof(ipPart)); - ipPart[sizeof(ipPart)-1]=0; - } - - j=0; - if (str[i]==portDelineator && portDelineator!=0) - { - i++; - for (; j < sizeof(portPart) && str[i]!=0; i++, j++) - { - portPart[j]=str[i]; - } - } - portPart[j]=0; - - - - - - - - - - - // needed for getaddrinfo - WSAStartupSingleton::AddRef(); - - // This could be a domain, or a printable address such as "192.0.2.1" or "2001:db8:63b3:1::3490" - // I want to convert it to its binary representation - addrinfo hints, *servinfo=0; - memset(&hints, 0, sizeof hints); - hints.ai_socktype = SOCK_DGRAM; - if (ipVersion==6) - hints.ai_family = AF_INET6; - else if (ipVersion==4) - hints.ai_family = AF_INET; - else - hints.ai_family = AF_UNSPEC; - getaddrinfo(ipPart, "", &hints, &servinfo); - if (servinfo==0) - return false; - RakAssert(servinfo); - - unsigned short oldPort = address.addr4.sin_port; -#if RAKNET_SUPPORT_IPV6==1 - if (servinfo->ai_family == AF_INET) - { -// if (ipVersion==6) -// { - address.addr4.sin_family=AF_INET6; -// memset(&address.addr6,0,sizeof(address.addr6)); -// memcpy(address.addr6.sin6_addr.s6_addr+12,&((struct sockaddr_in *)servinfo->ai_addr)->sin_addr.s_addr,sizeof(unsigned long)); -// } -// else -// { - address.addr4.sin_family=AF_INET; - memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in)); -// } - } - else - { - address.addr4.sin_family=AF_INET6; - memcpy(&address.addr6, (struct sockaddr_in6 *)servinfo->ai_addr,sizeof(struct sockaddr_in6)); - } -#else - address.addr4.sin_family=AF_INET4; - memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in)); -#endif - - freeaddrinfo(servinfo); // free the linked list - - // needed for getaddrinfo - WSAStartupSingleton::Deref(); - - // PORT - if (portPart[0]) - { - address.addr4.sin_port=htons((unsigned short) atoi(portPart)); - debugPort=ntohs(address.addr4.sin_port); - } - else - { - address.addr4.sin_port=oldPort; - } -#endif // #if RAKNET_SUPPORT_IPV6!=1 - - return true; -} -bool SystemAddress::FromStringExplicitPort(const char *str, unsigned short port, int ipVersion) -{ - bool b = FromString(str,(char) 0,ipVersion); - if (b==false) - return false; - address.addr4.sin_port=htons(port); - debugPort=ntohs(address.addr4.sin_port); - return true; -} -void SystemAddress::CopyPort( const SystemAddress& right ) -{ - address.addr4.sin_port=right.address.addr4.sin_port; - debugPort=right.debugPort; -} -RakNetGUID::RakNetGUID() -{ - systemIndex=(SystemIndex)-1; - *this=UNASSIGNED_RAKNET_GUID; -} -bool RakNetGUID::operator==( const RakNetGUID& right ) const -{ - return g==right.g; -} -bool RakNetGUID::operator!=( const RakNetGUID& right ) const -{ - return g!=right.g; -} -bool RakNetGUID::operator > ( const RakNetGUID& right ) const -{ - return g > right.g; -} -bool RakNetGUID::operator < ( const RakNetGUID& right ) const -{ - return g < right.g; -} -const char *RakNetGUID::ToString(void) const -{ - static unsigned char strIndex=0; - static char str[8][64]; - - unsigned char lastStrIndex=strIndex; - strIndex++; - ToString(str[lastStrIndex&7]); - return (char*) str[lastStrIndex&7]; -} -void RakNetGUID::ToString(char *dest) const -{ - if (*this==UNASSIGNED_RAKNET_GUID) - strcpy(dest, "UNASSIGNED_RAKNET_GUID"); - - //sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]); - sprintf(dest, "%" PRINTF_64_BIT_MODIFIER "u", (long long unsigned int) g); - // sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]); -} -bool RakNetGUID::FromString(const char *source) -{ - if (source==0) - return false; - - - -#if defined(WIN32) - g=_strtoui64(source, NULL, 10); - - -#else - // Changed from g=strtoull(source,0,10); for android - g=strtoull(source, (char **)NULL, 10); -#endif - return true; - -} -unsigned long RakNetGUID::ToUint32( const RakNetGUID &g ) -{ - return ((unsigned long) (g.g >> 32)) ^ ((unsigned long) (g.g & 0xFFFFFFFF)); -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "RakNetTypes.h" +#include "RakAssert.h" +#include +#include +#include "WindowsIncludes.h" +#include "WSAStartupSingleton.h" +#include "SocketDefines.h" + + +#if defined(_WIN32) +// extern __int64 _strtoui64(const char*, char**, int); // needed for Code::Blocks. Does not compile on Visual Studio 2010 +// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib +// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly +#include + +#else +#include +#include +#include +#endif + +#include // strncasecmp +#include "Itoa.h" +#include "SocketLayer.h" +#include "SuperFastHash.h" +#include + +using namespace RakNet; + +const char *IPV6_LOOPBACK="::1"; +const char *IPV4_LOOPBACK="127.0.0.1"; + +AddressOrGUID::AddressOrGUID( Packet *packet ) +{ + rakNetGuid=packet->guid; + systemAddress=packet->systemAddress; +} + +unsigned long AddressOrGUID::ToInteger( const AddressOrGUID &aog ) +{ + if (aog.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return RakNetGUID::ToUint32(aog.rakNetGuid); + return SystemAddress::ToInteger(aog.systemAddress); +} +const char *AddressOrGUID::ToString(bool writePort) const +{ + if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return rakNetGuid.ToString(); + return systemAddress.ToString(writePort); +} +void AddressOrGUID::ToString(bool writePort, char *dest) const +{ + if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return rakNetGuid.ToString(dest); + return systemAddress.ToString(writePort,dest); +} +bool RakNet::NonNumericHostString( const char *host ) +{ + if ( host[ 0 ] >= '0' && host[ 0 ] <= '9' ) + return false; + + if ( (host[ 0 ] == '-') && ( host[ 1 ] >= '0' && host[ 1 ] <= '9' ) ) + return false; + + if ( strstr(host,":") ) + return false; + + return true; +} + +SocketDescriptor::SocketDescriptor() {port=0; hostAddress[0]=0; remotePortRakNetWasStartedOn_PS3_PSP2=0; extraSocketOptions=0; socketFamily=AF_INET;} +SocketDescriptor::SocketDescriptor(unsigned short _port, const char *_hostAddress) +{ + remotePortRakNetWasStartedOn_PS3_PSP2=0; + port=_port; + if (_hostAddress) + strcpy(hostAddress, _hostAddress); + else + hostAddress[0]=0; + extraSocketOptions=0; + socketFamily=AF_INET; +} + +// Defaults to not in peer to peer mode for NetworkIDs. This only sends the localSystemAddress portion in the BitStream class +// This is what you want for client/server, where the server assigns all NetworkIDs and it is unnecessary to transmit the full structure. +// For peer to peer, this will transmit the systemAddress of the system that created the object in addition to localSystemAddress. This allows +// Any system to create unique ids locally. +// All systems must use the same value for this variable. +//bool RAK_DLL_EXPORT NetworkID::peerToPeerMode=false; + +SystemAddress& SystemAddress::operator = ( const SystemAddress& input ) +{ + memcpy(&address, &input.address, sizeof(address)); + systemIndex = input.systemIndex; + debugPort = input.debugPort; + return *this; +} +bool SystemAddress::EqualsExcludingPort( const SystemAddress& right ) const +{ + return (address.addr4.sin_family==AF_INET && address.addr4.sin_addr.s_addr==right.address.addr4.sin_addr.s_addr) +#if RAKNET_SUPPORT_IPV6==1 + || (address.addr4.sin_family==AF_INET6 && memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))==0) +#endif + ; +} +unsigned short SystemAddress::GetPort(void) const +{ + return ntohs(address.addr4.sin_port); +} +unsigned short SystemAddress::GetPortNetworkOrder(void) const +{ + return address.addr4.sin_port; +} +void SystemAddress::SetPort(unsigned short s) +{ + address.addr4.sin_port=htons(s); + debugPort=s; +} +void SystemAddress::SetPortNetworkOrder(unsigned short s) +{ + address.addr4.sin_port=s; + debugPort=ntohs(s); +} +bool SystemAddress::operator==( const SystemAddress& right ) const +{ + return address.addr4.sin_port == right.address.addr4.sin_port && EqualsExcludingPort(right); +} + +bool SystemAddress::operator!=( const SystemAddress& right ) const +{ + return (*this==right)==false; +} + +bool SystemAddress::operator>( const SystemAddress& right ) const +{ + if (address.addr4.sin_port == right.address.addr4.sin_port) + { +#if RAKNET_SUPPORT_IPV6==1 + if (address.addr4.sin_family==AF_INET) + return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr; + return memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))>0; +#else + return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr; +#endif + } + return address.addr4.sin_port>right.address.addr4.sin_port; +} + +bool SystemAddress::operator<( const SystemAddress& right ) const +{ + if (address.addr4.sin_port == right.address.addr4.sin_port) + { +#if RAKNET_SUPPORT_IPV6==1 + if (address.addr4.sin_family==AF_INET) + return address.addr4.sin_addr.s_addr0; +#else + return address.addr4.sin_addr.s_addr'9')) + break; + IPPart[index]=str[index]; + } + IPPart[index]=0; + portPart[0]=0; + if (str[index] && str[index+1]) + { + index++; + for (portIndex=0; portIndex<10 && str[index] && index < 22+10; index++, portIndex++) + { + if (str[index]<'0' || str[index]>'9') + break; + + portPart[portIndex]=str[index]; + } + portPart[portIndex]=0; + } + + + + + + + + + + + + + + if (IPPart[0]) + { + + + + address.addr4.sin_addr.s_addr=inet_addr__(IPPart); + + } + + + if (portPart[0]) + { + address.addr4.sin_port=htons((unsigned short) atoi(portPart)); + debugPort=ntohs(address.addr4.sin_port); + } + //#endif + } +} + +bool SystemAddress::FromString(const char *str, char portDelineator, int ipVersion) +{ +#if RAKNET_SUPPORT_IPV6!=1 + (void) ipVersion; + SetBinaryAddress(str,portDelineator); + return true; +#else + if (str==0) + { + memset(&address,0,sizeof(address)); + address.addr4.sin_family=AF_INET; + return true; + } +#if RAKNET_SUPPORT_IPV6==1 + char ipPart[INET6_ADDRSTRLEN]; +#else + char ipPart[INET_ADDRSTRLEN]; +#endif + char portPart[32]; + int i=0,j; + + // TODO - what about 255.255.255.255? + if (ipVersion==4 && strcmp(str, IPV6_LOOPBACK)==0) + { + strcpy(ipPart,IPV4_LOOPBACK); + } + else if (ipVersion==6 && strcmp(str, IPV4_LOOPBACK)==0) + { + address.addr4.sin_family=AF_INET6; + strcpy(ipPart,IPV6_LOOPBACK); + } + else if (NonNumericHostString(str)==false) + { + for (; i < sizeof(ipPart) && str[i]!=0 && str[i]!=portDelineator; i++) + { + if ((str[i]<'0' || str[i]>'9') && (str[i]<'a' || str[i]>'f') && (str[i]<'A' || str[i]>'F') && str[i]!='.' && str[i]!=':' && str[i]!='%' && str[i]!='-' && str[i]!='/') + break; + + ipPart[i]=str[i]; + } + ipPart[i]=0; + } + else + { + strncpy(ipPart,str,sizeof(ipPart)); + ipPart[sizeof(ipPart)-1]=0; + } + + j=0; + if (str[i]==portDelineator && portDelineator!=0) + { + i++; + for (; j < sizeof(portPart) && str[i]!=0; i++, j++) + { + portPart[j]=str[i]; + } + } + portPart[j]=0; + + + + + + + + + + + // needed for getaddrinfo + WSAStartupSingleton::AddRef(); + + // This could be a domain, or a printable address such as "192.0.2.1" or "2001:db8:63b3:1::3490" + // I want to convert it to its binary representation + addrinfo hints, *servinfo=0; + memset(&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_DGRAM; + if (ipVersion==6) + hints.ai_family = AF_INET6; + else if (ipVersion==4) + hints.ai_family = AF_INET; + else + hints.ai_family = AF_UNSPEC; + getaddrinfo(ipPart, "", &hints, &servinfo); + if (servinfo==0) + return false; + RakAssert(servinfo); + + unsigned short oldPort = address.addr4.sin_port; +#if RAKNET_SUPPORT_IPV6==1 + if (servinfo->ai_family == AF_INET) + { +// if (ipVersion==6) +// { + address.addr4.sin_family=AF_INET6; +// memset(&address.addr6,0,sizeof(address.addr6)); +// memcpy(address.addr6.sin6_addr.s6_addr+12,&((struct sockaddr_in *)servinfo->ai_addr)->sin_addr.s_addr,sizeof(unsigned long)); +// } +// else +// { + address.addr4.sin_family=AF_INET; + memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in)); +// } + } + else + { + address.addr4.sin_family=AF_INET6; + memcpy(&address.addr6, (struct sockaddr_in6 *)servinfo->ai_addr,sizeof(struct sockaddr_in6)); + } +#else + address.addr4.sin_family=AF_INET4; + memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in)); +#endif + + freeaddrinfo(servinfo); // free the linked list + + // needed for getaddrinfo + WSAStartupSingleton::Deref(); + + // PORT + if (portPart[0]) + { + address.addr4.sin_port=htons((unsigned short) atoi(portPart)); + debugPort=ntohs(address.addr4.sin_port); + } + else + { + address.addr4.sin_port=oldPort; + } +#endif // #if RAKNET_SUPPORT_IPV6!=1 + + return true; +} +bool SystemAddress::FromStringExplicitPort(const char *str, unsigned short port, int ipVersion) +{ + bool b = FromString(str,(char) 0,ipVersion); + if (b==false) + return false; + address.addr4.sin_port=htons(port); + debugPort=ntohs(address.addr4.sin_port); + return true; +} +void SystemAddress::CopyPort( const SystemAddress& right ) +{ + address.addr4.sin_port=right.address.addr4.sin_port; + debugPort=right.debugPort; +} +RakNetGUID::RakNetGUID() +{ + systemIndex=(SystemIndex)-1; + *this=UNASSIGNED_RAKNET_GUID; +} +bool RakNetGUID::operator==( const RakNetGUID& right ) const +{ + return g==right.g; +} +bool RakNetGUID::operator!=( const RakNetGUID& right ) const +{ + return g!=right.g; +} +bool RakNetGUID::operator > ( const RakNetGUID& right ) const +{ + return g > right.g; +} +bool RakNetGUID::operator < ( const RakNetGUID& right ) const +{ + return g < right.g; +} +const char *RakNetGUID::ToString(void) const +{ + static unsigned char strIndex=0; + static char str[8][64]; + + unsigned char lastStrIndex=strIndex; + strIndex++; + ToString(str[lastStrIndex&7]); + return (char*) str[lastStrIndex&7]; +} +void RakNetGUID::ToString(char *dest) const +{ + if (*this==UNASSIGNED_RAKNET_GUID) + strcpy(dest, "UNASSIGNED_RAKNET_GUID"); + + //sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]); + sprintf(dest, "%" PRINTF_64_BIT_MODIFIER "u", (long long unsigned int) g); + // sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]); +} +bool RakNetGUID::FromString(const char *source) +{ + if (source==0) + return false; + + + +#if defined(WIN32) + g=_strtoui64(source, NULL, 10); + + +#else + // Changed from g=strtoull(source,0,10); for android + g=strtoull(source, (char **)NULL, 10); +#endif + return true; + +} +unsigned long RakNetGUID::ToUint32( const RakNetGUID &g ) +{ + return ((unsigned long) (g.g >> 32)) ^ ((unsigned long) (g.g & 0xFFFFFFFF)); +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetTypes.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetTypes.h index befcfc1..063be59 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetTypes.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetTypes.h @@ -1,482 +1,482 @@ -/// \file -/// \brief Types used by RakNet, most of which involve user code. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __NETWORK_TYPES_H -#define __NETWORK_TYPES_H - -#include "RakNetDefines.h" -#include "NativeTypes.h" -#include "RakNetTime.h" -#include "Export.h" -#include "SocketIncludes.h" -#include "WindowsIncludes.h" -#include "XBox360Includes.h" - - - - - -namespace RakNet { -/// Forward declarations -class RakPeerInterface; -class BitStream; -struct Packet; - -enum StartupResult -{ - RAKNET_STARTED, - RAKNET_ALREADY_STARTED, - INVALID_SOCKET_DESCRIPTORS, - INVALID_MAX_CONNECTIONS, - SOCKET_FAMILY_NOT_SUPPORTED, - SOCKET_PORT_ALREADY_IN_USE, - SOCKET_FAILED_TO_BIND, - SOCKET_FAILED_TEST_SEND, - PORT_CANNOT_BE_ZERO, - FAILED_TO_CREATE_NETWORK_THREAD, - STARTUP_OTHER_FAILURE, -}; - - -enum ConnectionAttemptResult -{ - CONNECTION_ATTEMPT_STARTED, - INVALID_PARAMETER, - CANNOT_RESOLVE_DOMAIN_NAME, - ALREADY_CONNECTED_TO_ENDPOINT, - CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS, - SECURITY_INITIALIZATION_FAILED -}; - -/// Returned from RakPeerInterface::GetConnectionState() -enum ConnectionState -{ - /// Connect() was called, but the process hasn't started yet - IS_PENDING, - /// Processing the connection attempt - IS_CONNECTING, - /// Is connected and able to communicate - IS_CONNECTED, - /// Was connected, but will disconnect as soon as the remaining messages are delivered - IS_DISCONNECTING, - /// A connection attempt failed and will be aborted - IS_SILENTLY_DISCONNECTING, - /// No longer connected - IS_DISCONNECTED, - /// Was never connected, or else was disconnected long enough ago that the entry has been discarded - IS_NOT_CONNECTED, -}; - -/// Given a number of bits, return how many bytes are needed to represent that. -#define BITS_TO_BYTES(x) (((x)+7)>>3) -#define BYTES_TO_BITS(x) ((x)<<3) - -/// \sa NetworkIDObject.h -typedef unsigned char UniqueIDType; -typedef unsigned short SystemIndex; -typedef unsigned char RPCIndex; -const int MAX_RPC_MAP_SIZE=((RPCIndex)-1)-1; -const int UNDEFINED_RPC_INDEX=((RPCIndex)-1); - -/// First byte of a network message -typedef unsigned char MessageID; - -typedef uint32_t BitSize_t; - -#if defined(_MSC_VER) && _MSC_VER > 0 -#define PRINTF_64_BIT_MODIFIER "I64" -#else -#define PRINTF_64_BIT_MODIFIER "ll" -#endif - -/// Used with the PublicKey structure -enum PublicKeyMode -{ - /// The connection is insecure. You can also just pass 0 for the pointer to PublicKey in RakPeerInterface::Connect() - PKM_INSECURE_CONNECTION, - - /// Accept whatever public key the server gives us. This is vulnerable to man in the middle, but does not require - /// distribution of the public key in advance of connecting. - PKM_ACCEPT_ANY_PUBLIC_KEY, - - /// Use a known remote server public key. PublicKey::remoteServerPublicKey must be non-zero. - /// This is the recommended mode for secure connections. - PKM_USE_KNOWN_PUBLIC_KEY, - - /// Use a known remote server public key AND provide a public key for the connecting client. - /// PublicKey::remoteServerPublicKey, myPublicKey and myPrivateKey must be all be non-zero. - /// The server must cooperate for this mode to work. - /// I recommend not using this mode except for server-to-server communication as it significantly increases the CPU requirements during connections for both sides. - /// Furthermore, when it is used, a connection password should be used as well to avoid DoS attacks. - PKM_USE_TWO_WAY_AUTHENTICATION -}; - -/// Passed to RakPeerInterface::Connect() -struct RAK_DLL_EXPORT PublicKey -{ - /// How to interpret the public key, see above - PublicKeyMode publicKeyMode; - - /// Pointer to a public key of length cat::EasyHandshake::PUBLIC_KEY_BYTES. See the Encryption sample. - char *remoteServerPublicKey; - - /// (Optional) Pointer to a public key of length cat::EasyHandshake::PUBLIC_KEY_BYTES - char *myPublicKey; - - /// (Optional) Pointer to a private key of length cat::EasyHandshake::PRIVATE_KEY_BYTES - char *myPrivateKey; -}; - -/// Describes the local socket to use for RakPeer::Startup -struct RAK_DLL_EXPORT SocketDescriptor -{ - SocketDescriptor(); - SocketDescriptor(unsigned short _port, const char *_hostAddress); - - /// The local port to bind to. Pass 0 to have the OS autoassign a port. - unsigned short port; - - /// The local network card address to bind to, such as "127.0.0.1". Pass an empty string to use INADDR_ANY. - char hostAddress[32]; - - /// IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - /// IPV6 is the newer internet protocol. Instead of addresses such as 94.198.81.195, you may have an address such as fe80::7c:31f7:fec4:27de%14. - /// Encoding takes 16 bytes instead of 4, so IPV6 is less efficient for bandwidth. - /// On the positive side, NAT Punchthrough is not needed and should not be used with IPV6 because there are enough addresses that routers do not need to create address mappings. - /// RakPeer::Startup() will fail if this IP version is not supported. - /// \pre RAKNET_SUPPORT_IPV6 must be set to 1 in RakNetDefines.h for AF_INET6 - short socketFamily; - - - - - - - - - - unsigned short remotePortRakNetWasStartedOn_PS3_PSP2; - - /// XBOX only: set IPPROTO_VDP if you want to use VDP. If enabled, this socket does not support broadcast to 255.255.255.255 - unsigned int extraSocketOptions; -}; - -extern bool NonNumericHostString( const char *host ); - -/// \brief Network address for a system -/// \details Corresponds to a network address
-/// This is not necessarily a unique identifier. For example, if a system has both LAN and internet connections, the system may be identified by either one, depending on who is communicating
-/// Therefore, you should not transmit the SystemAddress over the network and expect it to identify a system, or use it to connect to that system, except in the case where that system is not behind a NAT (such as with a dedciated server) -/// Use RakNetGUID for a unique per-instance of RakPeer to identify systems -struct RAK_DLL_EXPORT SystemAddress -{ - /// Constructors - SystemAddress(); - SystemAddress(const char *str); - SystemAddress(const char *str, unsigned short port); - - - - - - - - /// SystemAddress, with RAKNET_SUPPORT_IPV6 defined, holds both an sockaddr_in6 and a sockaddr_in - union// In6OrIn4 - { -#if RAKNET_SUPPORT_IPV6==1 - struct sockaddr_in6 addr6; -#endif - - struct sockaddr_in addr4; - } address; - - /// This is not used internally, but holds a copy of the port held in the address union, so for debugging it's easier to check what port is being held - unsigned short debugPort; - - /// \internal Return the size to write to a bitStream - static int size(void); - - /// Hash the system address - static unsigned long ToInteger( const SystemAddress &sa ); - - /// Return the IP version, either IPV4 or IPV6 - /// \return Either 4 or 6 - unsigned char GetIPVersion(void) const; - - /// \internal Returns either IPPROTO_IP or IPPROTO_IPV6 - /// \sa GetIPVersion - unsigned int GetIPPROTO(void) const; - - /// Call SetToLoopback(), with whatever IP version is currently held. Defaults to IPV4 - void SetToLoopback(void); - - /// Call SetToLoopback() with a specific IP version - /// \param[in] ipVersion Either 4 for IPV4 or 6 for IPV6 - void SetToLoopback(unsigned char ipVersion); - - /// \return If was set to 127.0.0.1 or ::1 - bool IsLoopback(void) const; - - // Return the systemAddress as a string in the format | - // Returns a static string - // NOT THREADSAFE - // portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f - const char *ToString(bool writePort=true, char portDelineator='|') const; - - // Return the systemAddress as a string in the format | - // dest must be large enough to hold the output - // portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f - // THREADSAFE - void ToString(bool writePort, char *dest, char portDelineator='|') const; - - /// Set the system address from a printable IP string, for example "192.0.2.1" or "2001:db8:63b3:1::3490" - /// You can write the port as well, using the portDelineator, for example "192.0.2.1|1234" - /// \param[in] str A printable IP string, for example "192.0.2.1" or "2001:db8:63b3:1::3490". Pass 0 for \a str to set to UNASSIGNED_SYSTEM_ADDRESS - /// \param[in] portDelineator if \a str contains a port, delineate the port with this character. portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f - /// \param[in] ipVersion Only used if str is a pre-defined address in the wrong format, such as 127.0.0.1 but you want ip version 6, so you can pass 6 here to do the conversion - /// \note The current port is unchanged if a port is not specified in \a str - /// \return True on success, false on ipVersion does not match type of passed string - bool FromString(const char *str, char portDelineator='|', int ipVersion=0); - - /// Same as FromString(), but you explicitly set a port at the same time - bool FromStringExplicitPort(const char *str, unsigned short port, int ipVersion=0); - - /// Copy the port from another SystemAddress structure - void CopyPort( const SystemAddress& right ); - - /// Returns if two system addresses have the same IP (port is not checked) - bool EqualsExcludingPort( const SystemAddress& right ) const; - - /// Returns the port in host order (this is what you normally use) - unsigned short GetPort(void) const; - - /// \internal Returns the port in network order - unsigned short GetPortNetworkOrder(void) const; - - /// Sets the port. The port value should be in host order (this is what you normally use) - void SetPort(unsigned short s); - - /// \internal Sets the port. The port value should already be in network order. - void SetPortNetworkOrder(unsigned short s); - - /// Old version, for crap platforms that don't support newer socket functions - void SetBinaryAddress(const char *str, char portDelineator=':'); - /// Old version, for crap platforms that don't support newer socket functions - void ToString_Old(bool writePort, char *dest, char portDelineator=':') const; - - /// \internal sockaddr_in6 requires extra data beyond just the IP and port. Copy that extra data from an existing SystemAddress that already has it - void FixForIPVersion(const SystemAddress &boundAddressToSocket); - - SystemAddress& operator = ( const SystemAddress& input ); - bool operator==( const SystemAddress& right ) const; - bool operator!=( const SystemAddress& right ) const; - bool operator > ( const SystemAddress& right ) const; - bool operator < ( const SystemAddress& right ) const; - - /// \internal Used internally for fast lookup. Optional (use -1 to do regular lookup). Don't transmit this. - SystemIndex systemIndex; - - private: - -#if RAKNET_SUPPORT_IPV6==1 - void ToString_New(bool writePort, char *dest, char portDelineator) const; -#endif -}; - -/// Uniquely identifies an instance of RakPeer. Use RakPeer::GetGuidFromSystemAddress() and RakPeer::GetSystemAddressFromGuid() to go between SystemAddress and RakNetGUID -/// Use RakPeer::GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) to get your own GUID -struct RAK_DLL_EXPORT RakNetGUID -{ - RakNetGUID(); - explicit RakNetGUID(uint64_t _g) {g=_g; systemIndex=(SystemIndex)-1;} -// uint32_t g[6]; - uint64_t g; - - // Return the GUID as a string - // Returns a static string - // NOT THREADSAFE - const char *ToString(void) const; - - // Return the GUID as a string - // dest must be large enough to hold the output - // THREADSAFE - void ToString(char *dest) const; - - bool FromString(const char *source); - - static unsigned long ToUint32( const RakNetGUID &g ); - - RakNetGUID& operator = ( const RakNetGUID& input ) - { - g=input.g; - systemIndex=input.systemIndex; - return *this; - } - - // Used internally for fast lookup. Optional (use -1 to do regular lookup). Don't transmit this. - SystemIndex systemIndex; - static int size() {return (int) sizeof(uint64_t);} - - bool operator==( const RakNetGUID& right ) const; - bool operator!=( const RakNetGUID& right ) const; - bool operator > ( const RakNetGUID& right ) const; - bool operator < ( const RakNetGUID& right ) const; -}; - -/// Index of an invalid SystemAddress -//const SystemAddress UNASSIGNED_SYSTEM_ADDRESS = -//{ -// 0xFFFFFFFF, 0xFFFF -//}; -#ifndef SWIG -const SystemAddress UNASSIGNED_SYSTEM_ADDRESS; -const RakNetGUID UNASSIGNED_RAKNET_GUID((uint64_t)-1); -#endif -//{ -// {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF} -// 0xFFFFFFFFFFFFFFFF -//}; - - -struct RAK_DLL_EXPORT AddressOrGUID -{ - RakNetGUID rakNetGuid; - SystemAddress systemAddress; - - SystemIndex GetSystemIndex(void) const {if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) return rakNetGuid.systemIndex; else return systemAddress.systemIndex;} - bool IsUndefined(void) const {return rakNetGuid==UNASSIGNED_RAKNET_GUID && systemAddress==UNASSIGNED_SYSTEM_ADDRESS;} - void SetUndefined(void) {rakNetGuid=UNASSIGNED_RAKNET_GUID; systemAddress=UNASSIGNED_SYSTEM_ADDRESS;} - static unsigned long ToInteger( const AddressOrGUID &aog ); - const char *ToString(bool writePort=true) const; - void ToString(bool writePort, char *dest) const; - - AddressOrGUID() {} - AddressOrGUID( const AddressOrGUID& input ) - { - rakNetGuid=input.rakNetGuid; - systemAddress=input.systemAddress; - } - AddressOrGUID( const SystemAddress& input ) - { - rakNetGuid=UNASSIGNED_RAKNET_GUID; - systemAddress=input; - } - AddressOrGUID( Packet *packet ); - AddressOrGUID( const RakNetGUID& input ) - { - rakNetGuid=input; - systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - } - AddressOrGUID& operator = ( const AddressOrGUID& input ) - { - rakNetGuid=input.rakNetGuid; - systemAddress=input.systemAddress; - return *this; - } - - AddressOrGUID& operator = ( const SystemAddress& input ) - { - rakNetGuid=UNASSIGNED_RAKNET_GUID; - systemAddress=input; - return *this; - } - - AddressOrGUID& operator = ( const RakNetGUID& input ) - { - rakNetGuid=input; - systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - return *this; - } - - inline bool operator==( const AddressOrGUID& right ) const {return (rakNetGuid!=UNASSIGNED_RAKNET_GUID && rakNetGuid==right.rakNetGuid) || (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && systemAddress==right.systemAddress);} -}; - -typedef uint64_t NetworkID; - -/// This represents a user message from another system. -struct Packet -{ - /// The system that send this packet. - SystemAddress systemAddress; - - /// A unique identifier for the system that sent this packet, regardless of IP address (internal / external / remote system) - /// Only valid once a connection has been established (ID_CONNECTION_REQUEST_ACCEPTED, or ID_NEW_INCOMING_CONNECTION) - /// Until that time, will be UNASSIGNED_RAKNET_GUID - RakNetGUID guid; - - /// The length of the data in bytes - unsigned int length; - - /// The length of the data in bits - BitSize_t bitSize; - - /// The data from the sender - unsigned char* data; - - /// @internal - /// Indicates whether to delete the data, or to simply delete the packet. - bool deleteData; - - /// @internal - /// If true, this message is meant for the user, not for the plugins, so do not process it through plugins - bool wasGeneratedLocally; -}; - -/// Index of an unassigned player -const SystemIndex UNASSIGNED_PLAYER_INDEX = 65535; - -/// Unassigned object ID -const NetworkID UNASSIGNED_NETWORK_ID = (uint64_t) -1; - -const int PING_TIMES_ARRAY_SIZE = 5; - -struct RAK_DLL_EXPORT uint24_t -{ - uint32_t val; - - uint24_t() {} - inline operator uint32_t() { return val; } - inline operator uint32_t() const { return val; } - - inline uint24_t(const uint24_t& a) {val=a.val;} - inline uint24_t operator++() {++val; val&=0x00FFFFFF; return *this;} - inline uint24_t operator--() {--val; val&=0x00FFFFFF; return *this;} - inline uint24_t operator++(int) {uint24_t temp(val); ++val; val&=0x00FFFFFF; return temp;} - inline uint24_t operator--(int) {uint24_t temp(val); --val; val&=0x00FFFFFF; return temp;} - inline uint24_t operator&(const uint24_t& a) {return uint24_t(val&a.val);} - inline uint24_t& operator=(const uint24_t& a) { val=a.val; return *this; } - inline uint24_t& operator+=(const uint24_t& a) { val+=a.val; val&=0x00FFFFFF; return *this; } - inline uint24_t& operator-=(const uint24_t& a) { val-=a.val; val&=0x00FFFFFF; return *this; } - inline bool operator==( const uint24_t& right ) const {return val==right.val;} - inline bool operator!=( const uint24_t& right ) const {return val!=right.val;} - inline bool operator > ( const uint24_t& right ) const {return val>right.val;} - inline bool operator < ( const uint24_t& right ) const {return val ( const uint32_t& right ) const {return val>(right&0x00FFFFFF);} - inline bool operator < ( const uint32_t& right ) const {return val<(right&0x00FFFFFF);} - inline const uint24_t operator+( const uint32_t &other ) const { return uint24_t(val+other); } - inline const uint24_t operator-( const uint32_t &other ) const { return uint24_t(val-other); } - inline const uint24_t operator/( const uint32_t &other ) const { return uint24_t(val/other); } - inline const uint24_t operator*( const uint32_t &other ) const { return uint24_t(val*other); } -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief Types used by RakNet, most of which involve user code. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __NETWORK_TYPES_H +#define __NETWORK_TYPES_H + +#include "RakNetDefines.h" +#include "NativeTypes.h" +#include "RakNetTime.h" +#include "Export.h" +#include "SocketIncludes.h" +#include "WindowsIncludes.h" +#include "XBox360Includes.h" + + + + + +namespace RakNet { +/// Forward declarations +class RakPeerInterface; +class BitStream; +struct Packet; + +enum StartupResult +{ + RAKNET_STARTED, + RAKNET_ALREADY_STARTED, + INVALID_SOCKET_DESCRIPTORS, + INVALID_MAX_CONNECTIONS, + SOCKET_FAMILY_NOT_SUPPORTED, + SOCKET_PORT_ALREADY_IN_USE, + SOCKET_FAILED_TO_BIND, + SOCKET_FAILED_TEST_SEND, + PORT_CANNOT_BE_ZERO, + FAILED_TO_CREATE_NETWORK_THREAD, + STARTUP_OTHER_FAILURE, +}; + + +enum ConnectionAttemptResult +{ + CONNECTION_ATTEMPT_STARTED, + INVALID_PARAMETER, + CANNOT_RESOLVE_DOMAIN_NAME, + ALREADY_CONNECTED_TO_ENDPOINT, + CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS, + SECURITY_INITIALIZATION_FAILED +}; + +/// Returned from RakPeerInterface::GetConnectionState() +enum ConnectionState +{ + /// Connect() was called, but the process hasn't started yet + IS_PENDING, + /// Processing the connection attempt + IS_CONNECTING, + /// Is connected and able to communicate + IS_CONNECTED, + /// Was connected, but will disconnect as soon as the remaining messages are delivered + IS_DISCONNECTING, + /// A connection attempt failed and will be aborted + IS_SILENTLY_DISCONNECTING, + /// No longer connected + IS_DISCONNECTED, + /// Was never connected, or else was disconnected long enough ago that the entry has been discarded + IS_NOT_CONNECTED, +}; + +/// Given a number of bits, return how many bytes are needed to represent that. +#define BITS_TO_BYTES(x) (((x)+7)>>3) +#define BYTES_TO_BITS(x) ((x)<<3) + +/// \sa NetworkIDObject.h +typedef unsigned char UniqueIDType; +typedef unsigned short SystemIndex; +typedef unsigned char RPCIndex; +const int MAX_RPC_MAP_SIZE=((RPCIndex)-1)-1; +const int UNDEFINED_RPC_INDEX=((RPCIndex)-1); + +/// First byte of a network message +typedef unsigned char MessageID; + +typedef uint32_t BitSize_t; + +#if defined(_MSC_VER) && _MSC_VER > 0 +#define PRINTF_64_BIT_MODIFIER "I64" +#else +#define PRINTF_64_BIT_MODIFIER "ll" +#endif + +/// Used with the PublicKey structure +enum PublicKeyMode +{ + /// The connection is insecure. You can also just pass 0 for the pointer to PublicKey in RakPeerInterface::Connect() + PKM_INSECURE_CONNECTION, + + /// Accept whatever public key the server gives us. This is vulnerable to man in the middle, but does not require + /// distribution of the public key in advance of connecting. + PKM_ACCEPT_ANY_PUBLIC_KEY, + + /// Use a known remote server public key. PublicKey::remoteServerPublicKey must be non-zero. + /// This is the recommended mode for secure connections. + PKM_USE_KNOWN_PUBLIC_KEY, + + /// Use a known remote server public key AND provide a public key for the connecting client. + /// PublicKey::remoteServerPublicKey, myPublicKey and myPrivateKey must be all be non-zero. + /// The server must cooperate for this mode to work. + /// I recommend not using this mode except for server-to-server communication as it significantly increases the CPU requirements during connections for both sides. + /// Furthermore, when it is used, a connection password should be used as well to avoid DoS attacks. + PKM_USE_TWO_WAY_AUTHENTICATION +}; + +/// Passed to RakPeerInterface::Connect() +struct RAK_DLL_EXPORT PublicKey +{ + /// How to interpret the public key, see above + PublicKeyMode publicKeyMode; + + /// Pointer to a public key of length cat::EasyHandshake::PUBLIC_KEY_BYTES. See the Encryption sample. + char *remoteServerPublicKey; + + /// (Optional) Pointer to a public key of length cat::EasyHandshake::PUBLIC_KEY_BYTES + char *myPublicKey; + + /// (Optional) Pointer to a private key of length cat::EasyHandshake::PRIVATE_KEY_BYTES + char *myPrivateKey; +}; + +/// Describes the local socket to use for RakPeer::Startup +struct RAK_DLL_EXPORT SocketDescriptor +{ + SocketDescriptor(); + SocketDescriptor(unsigned short _port, const char *_hostAddress); + + /// The local port to bind to. Pass 0 to have the OS autoassign a port. + unsigned short port; + + /// The local network card address to bind to, such as "127.0.0.1". Pass an empty string to use INADDR_ANY. + char hostAddress[32]; + + /// IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + /// IPV6 is the newer internet protocol. Instead of addresses such as 94.198.81.195, you may have an address such as fe80::7c:31f7:fec4:27de%14. + /// Encoding takes 16 bytes instead of 4, so IPV6 is less efficient for bandwidth. + /// On the positive side, NAT Punchthrough is not needed and should not be used with IPV6 because there are enough addresses that routers do not need to create address mappings. + /// RakPeer::Startup() will fail if this IP version is not supported. + /// \pre RAKNET_SUPPORT_IPV6 must be set to 1 in RakNetDefines.h for AF_INET6 + short socketFamily; + + + + + + + + + + unsigned short remotePortRakNetWasStartedOn_PS3_PSP2; + + /// XBOX only: set IPPROTO_VDP if you want to use VDP. If enabled, this socket does not support broadcast to 255.255.255.255 + unsigned int extraSocketOptions; +}; + +extern bool NonNumericHostString( const char *host ); + +/// \brief Network address for a system +/// \details Corresponds to a network address
+/// This is not necessarily a unique identifier. For example, if a system has both LAN and internet connections, the system may be identified by either one, depending on who is communicating
+/// Therefore, you should not transmit the SystemAddress over the network and expect it to identify a system, or use it to connect to that system, except in the case where that system is not behind a NAT (such as with a dedciated server) +/// Use RakNetGUID for a unique per-instance of RakPeer to identify systems +struct RAK_DLL_EXPORT SystemAddress +{ + /// Constructors + SystemAddress(); + SystemAddress(const char *str); + SystemAddress(const char *str, unsigned short port); + + + + + + + + /// SystemAddress, with RAKNET_SUPPORT_IPV6 defined, holds both an sockaddr_in6 and a sockaddr_in + union// In6OrIn4 + { +#if RAKNET_SUPPORT_IPV6==1 + struct sockaddr_in6 addr6; +#endif + + struct sockaddr_in addr4; + } address; + + /// This is not used internally, but holds a copy of the port held in the address union, so for debugging it's easier to check what port is being held + unsigned short debugPort; + + /// \internal Return the size to write to a bitStream + static int size(void); + + /// Hash the system address + static unsigned long ToInteger( const SystemAddress &sa ); + + /// Return the IP version, either IPV4 or IPV6 + /// \return Either 4 or 6 + unsigned char GetIPVersion(void) const; + + /// \internal Returns either IPPROTO_IP or IPPROTO_IPV6 + /// \sa GetIPVersion + unsigned int GetIPPROTO(void) const; + + /// Call SetToLoopback(), with whatever IP version is currently held. Defaults to IPV4 + void SetToLoopback(void); + + /// Call SetToLoopback() with a specific IP version + /// \param[in] ipVersion Either 4 for IPV4 or 6 for IPV6 + void SetToLoopback(unsigned char ipVersion); + + /// \return If was set to 127.0.0.1 or ::1 + bool IsLoopback(void) const; + + // Return the systemAddress as a string in the format | + // Returns a static string + // NOT THREADSAFE + // portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f + const char *ToString(bool writePort=true, char portDelineator='|') const; + + // Return the systemAddress as a string in the format | + // dest must be large enough to hold the output + // portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f + // THREADSAFE + void ToString(bool writePort, char *dest, char portDelineator='|') const; + + /// Set the system address from a printable IP string, for example "192.0.2.1" or "2001:db8:63b3:1::3490" + /// You can write the port as well, using the portDelineator, for example "192.0.2.1|1234" + /// \param[in] str A printable IP string, for example "192.0.2.1" or "2001:db8:63b3:1::3490". Pass 0 for \a str to set to UNASSIGNED_SYSTEM_ADDRESS + /// \param[in] portDelineator if \a str contains a port, delineate the port with this character. portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f + /// \param[in] ipVersion Only used if str is a pre-defined address in the wrong format, such as 127.0.0.1 but you want ip version 6, so you can pass 6 here to do the conversion + /// \note The current port is unchanged if a port is not specified in \a str + /// \return True on success, false on ipVersion does not match type of passed string + bool FromString(const char *str, char portDelineator='|', int ipVersion=0); + + /// Same as FromString(), but you explicitly set a port at the same time + bool FromStringExplicitPort(const char *str, unsigned short port, int ipVersion=0); + + /// Copy the port from another SystemAddress structure + void CopyPort( const SystemAddress& right ); + + /// Returns if two system addresses have the same IP (port is not checked) + bool EqualsExcludingPort( const SystemAddress& right ) const; + + /// Returns the port in host order (this is what you normally use) + unsigned short GetPort(void) const; + + /// \internal Returns the port in network order + unsigned short GetPortNetworkOrder(void) const; + + /// Sets the port. The port value should be in host order (this is what you normally use) + void SetPort(unsigned short s); + + /// \internal Sets the port. The port value should already be in network order. + void SetPortNetworkOrder(unsigned short s); + + /// Old version, for crap platforms that don't support newer socket functions + void SetBinaryAddress(const char *str, char portDelineator=':'); + /// Old version, for crap platforms that don't support newer socket functions + void ToString_Old(bool writePort, char *dest, char portDelineator=':') const; + + /// \internal sockaddr_in6 requires extra data beyond just the IP and port. Copy that extra data from an existing SystemAddress that already has it + void FixForIPVersion(const SystemAddress &boundAddressToSocket); + + SystemAddress& operator = ( const SystemAddress& input ); + bool operator==( const SystemAddress& right ) const; + bool operator!=( const SystemAddress& right ) const; + bool operator > ( const SystemAddress& right ) const; + bool operator < ( const SystemAddress& right ) const; + + /// \internal Used internally for fast lookup. Optional (use -1 to do regular lookup). Don't transmit this. + SystemIndex systemIndex; + + private: + +#if RAKNET_SUPPORT_IPV6==1 + void ToString_New(bool writePort, char *dest, char portDelineator) const; +#endif +}; + +/// Uniquely identifies an instance of RakPeer. Use RakPeer::GetGuidFromSystemAddress() and RakPeer::GetSystemAddressFromGuid() to go between SystemAddress and RakNetGUID +/// Use RakPeer::GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) to get your own GUID +struct RAK_DLL_EXPORT RakNetGUID +{ + RakNetGUID(); + explicit RakNetGUID(uint64_t _g) {g=_g; systemIndex=(SystemIndex)-1;} +// uint32_t g[6]; + uint64_t g; + + // Return the GUID as a string + // Returns a static string + // NOT THREADSAFE + const char *ToString(void) const; + + // Return the GUID as a string + // dest must be large enough to hold the output + // THREADSAFE + void ToString(char *dest) const; + + bool FromString(const char *source); + + static unsigned long ToUint32( const RakNetGUID &g ); + + RakNetGUID& operator = ( const RakNetGUID& input ) + { + g=input.g; + systemIndex=input.systemIndex; + return *this; + } + + // Used internally for fast lookup. Optional (use -1 to do regular lookup). Don't transmit this. + SystemIndex systemIndex; + static int size() {return (int) sizeof(uint64_t);} + + bool operator==( const RakNetGUID& right ) const; + bool operator!=( const RakNetGUID& right ) const; + bool operator > ( const RakNetGUID& right ) const; + bool operator < ( const RakNetGUID& right ) const; +}; + +/// Index of an invalid SystemAddress +//const SystemAddress UNASSIGNED_SYSTEM_ADDRESS = +//{ +// 0xFFFFFFFF, 0xFFFF +//}; +#ifndef SWIG +const SystemAddress UNASSIGNED_SYSTEM_ADDRESS; +const RakNetGUID UNASSIGNED_RAKNET_GUID((uint64_t)-1); +#endif +//{ +// {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF} +// 0xFFFFFFFFFFFFFFFF +//}; + + +struct RAK_DLL_EXPORT AddressOrGUID +{ + RakNetGUID rakNetGuid; + SystemAddress systemAddress; + + SystemIndex GetSystemIndex(void) const {if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) return rakNetGuid.systemIndex; else return systemAddress.systemIndex;} + bool IsUndefined(void) const {return rakNetGuid==UNASSIGNED_RAKNET_GUID && systemAddress==UNASSIGNED_SYSTEM_ADDRESS;} + void SetUndefined(void) {rakNetGuid=UNASSIGNED_RAKNET_GUID; systemAddress=UNASSIGNED_SYSTEM_ADDRESS;} + static unsigned long ToInteger( const AddressOrGUID &aog ); + const char *ToString(bool writePort=true) const; + void ToString(bool writePort, char *dest) const; + + AddressOrGUID() {} + AddressOrGUID( const AddressOrGUID& input ) + { + rakNetGuid=input.rakNetGuid; + systemAddress=input.systemAddress; + } + AddressOrGUID( const SystemAddress& input ) + { + rakNetGuid=UNASSIGNED_RAKNET_GUID; + systemAddress=input; + } + AddressOrGUID( Packet *packet ); + AddressOrGUID( const RakNetGUID& input ) + { + rakNetGuid=input; + systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + } + AddressOrGUID& operator = ( const AddressOrGUID& input ) + { + rakNetGuid=input.rakNetGuid; + systemAddress=input.systemAddress; + return *this; + } + + AddressOrGUID& operator = ( const SystemAddress& input ) + { + rakNetGuid=UNASSIGNED_RAKNET_GUID; + systemAddress=input; + return *this; + } + + AddressOrGUID& operator = ( const RakNetGUID& input ) + { + rakNetGuid=input; + systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + return *this; + } + + inline bool operator==( const AddressOrGUID& right ) const {return (rakNetGuid!=UNASSIGNED_RAKNET_GUID && rakNetGuid==right.rakNetGuid) || (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && systemAddress==right.systemAddress);} +}; + +typedef uint64_t NetworkID; + +/// This represents a user message from another system. +struct Packet +{ + /// The system that send this packet. + SystemAddress systemAddress; + + /// A unique identifier for the system that sent this packet, regardless of IP address (internal / external / remote system) + /// Only valid once a connection has been established (ID_CONNECTION_REQUEST_ACCEPTED, or ID_NEW_INCOMING_CONNECTION) + /// Until that time, will be UNASSIGNED_RAKNET_GUID + RakNetGUID guid; + + /// The length of the data in bytes + unsigned int length; + + /// The length of the data in bits + BitSize_t bitSize; + + /// The data from the sender + unsigned char* data; + + /// @internal + /// Indicates whether to delete the data, or to simply delete the packet. + bool deleteData; + + /// @internal + /// If true, this message is meant for the user, not for the plugins, so do not process it through plugins + bool wasGeneratedLocally; +}; + +/// Index of an unassigned player +const SystemIndex UNASSIGNED_PLAYER_INDEX = 65535; + +/// Unassigned object ID +const NetworkID UNASSIGNED_NETWORK_ID = (uint64_t) -1; + +const int PING_TIMES_ARRAY_SIZE = 5; + +struct RAK_DLL_EXPORT uint24_t +{ + uint32_t val; + + uint24_t() {} + inline operator uint32_t() { return val; } + inline operator uint32_t() const { return val; } + + inline uint24_t(const uint24_t& a) {val=a.val;} + inline uint24_t operator++() {++val; val&=0x00FFFFFF; return *this;} + inline uint24_t operator--() {--val; val&=0x00FFFFFF; return *this;} + inline uint24_t operator++(int) {uint24_t temp(val); ++val; val&=0x00FFFFFF; return temp;} + inline uint24_t operator--(int) {uint24_t temp(val); --val; val&=0x00FFFFFF; return temp;} + inline uint24_t operator&(const uint24_t& a) {return uint24_t(val&a.val);} + inline uint24_t& operator=(const uint24_t& a) { val=a.val; return *this; } + inline uint24_t& operator+=(const uint24_t& a) { val+=a.val; val&=0x00FFFFFF; return *this; } + inline uint24_t& operator-=(const uint24_t& a) { val-=a.val; val&=0x00FFFFFF; return *this; } + inline bool operator==( const uint24_t& right ) const {return val==right.val;} + inline bool operator!=( const uint24_t& right ) const {return val!=right.val;} + inline bool operator > ( const uint24_t& right ) const {return val>right.val;} + inline bool operator < ( const uint24_t& right ) const {return val ( const uint32_t& right ) const {return val>(right&0x00FFFFFF);} + inline bool operator < ( const uint32_t& right ) const {return val<(right&0x00FFFFFF);} + inline const uint24_t operator+( const uint32_t &other ) const { return uint24_t(val+other); } + inline const uint24_t operator-( const uint32_t &other ) const { return uint24_t(val-other); } + inline const uint24_t operator/( const uint32_t &other ) const { return uint24_t(val/other); } + inline const uint24_t operator*( const uint32_t &other ) const { return uint24_t(val*other); } +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNetVersion.h b/project/lib_projects/raknet/jni/RaknetSources/RakNetVersion.h index 4e61675..5ef88c8 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNetVersion.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNetVersion.h @@ -1,8 +1,8 @@ -#define RAKNET_VERSION "4.036" -#define RAKNET_VERSION_NUMBER 4.036 - -#define RAKNET_DATE "2/1/2012" - -// What compatible protocol version RakNet is using. When this value changes, it indicates this version of RakNet cannot connection to an older version. -// ID_INCOMPATIBLE_PROTOCOL_VERSION will be returned on connection attempt in this case -#define RAKNET_PROTOCOL_VERSION 5 +#define RAKNET_VERSION "4.036" +#define RAKNET_VERSION_NUMBER 4.036 + +#define RAKNET_DATE "2/1/2012" + +// What compatible protocol version RakNet is using. When this value changes, it indicates this version of RakNet cannot connection to an older version. +// ID_INCOMPATIBLE_PROTOCOL_VERSION will be returned on connection attempt in this case +#define RAKNET_PROTOCOL_VERSION 5 diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNet_vc8.vcproj b/project/lib_projects/raknet/jni/RaknetSources/RakNet_vc8.vcproj index 694c818..a75edee 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNet_vc8.vcproj +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNet_vc8.vcproj @@ -1,849 +1,849 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakNet_vc9.vcproj b/project/lib_projects/raknet/jni/RaknetSources/RakNet_vc9.vcproj index 694c818..a75edee 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakNet_vc9.vcproj +++ b/project/lib_projects/raknet/jni/RaknetSources/RakNet_vc9.vcproj @@ -1,849 +1,849 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakPeer.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakPeer.cpp index a24a2b5..7ba9ec6 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakPeer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakPeer.cpp @@ -1,5964 +1,5964 @@ -// \file -// -// This file is part of RakNet Copyright 2003 Jenkins Software LLC -// -// Usage of RakNet is subject to the appropriate license agreement. - - -#define CAT_NEUTER_EXPORT /* Neuter dllimport for libcat */ - -#include "RakNetDefines.h" -#include "RakPeer.h" -#include "RakNetTypes.h" - -#ifdef _WIN32 - -#else -#include -#endif - -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - -#include -#include // toupper -#include -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "DS_HuffmanEncodingTree.h" -#include "Rand.h" -#include "PluginInterface2.h" -#include "StringCompressor.h" -#include "StringTable.h" -#include "NetworkIDObject.h" -#include "RakNetTypes.h" -#include "SHA1.h" -#include "RakSleep.h" -#include "RakAssert.h" -#include "RakNetVersion.h" -#include "NetworkIDManager.h" -#include "gettimeofday.h" -#include "SignaledEvent.h" -#include "SuperFastHash.h" -#include "RakAlloca.h" -#include "WSAStartupSingleton.h" - -#ifdef USE_THREADED_SEND -#include "SendToThread.h" -#endif - -#ifdef CAT_AUDIT -#define CAT_AUDIT_PRINTF(...) printf(__VA_ARGS__) -#else -#define CAT_AUDIT_PRINTF(...) -#endif - -namespace RakNet -{ -RAK_THREAD_DECLARATION(UpdateNetworkLoop); -RAK_THREAD_DECLARATION(RecvFromLoop); -RAK_THREAD_DECLARATION(UDTConnect); -} -#define REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE 8 - -#if !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) -#include // malloc -#endif - - - -#if defined(_WIN32) -// -#else -/* -#include // Console 2 -#include -extern bool _extern_Console2LoadModules(void); -extern int _extern_Console2GetConnectionStatus(void); -extern int _extern_Console2GetLobbyStatus(void); -//extern bool Console2StartupFluff(unsigned int *); -extern void Console2ShutdownFluff(void); -//extern unsigned int Console2ActivateConnection(unsigned int, void *); -//extern bool Console2BlockOnEstablished(void); -extern void Console2GetIPAndPort(unsigned int, char *, unsigned short *, unsigned int ); -//extern void Console2DeactivateConnection(unsigned int, unsigned int); -*/ -#endif - - -static const int NUM_MTU_SIZES=3; - - - -static const int mtuSizes[NUM_MTU_SIZES]={MAXIMUM_MTU_SIZE, 1200, 576}; - - -// Note to self - if I change this it might affect RECIPIENT_OFFLINE_MESSAGE_INTERVAL in Natpunchthrough.cpp -//static const int MAX_OPEN_CONNECTION_REQUESTS=8; -//static const int TIME_BETWEEN_OPEN_CONNECTION_REQUESTS=500; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -static RakNetRandom rnr; - -struct RakPeerAndIndex -{ - SOCKET s; - unsigned short remotePortRakNetWasStartedOn_PS3; - RakPeer *rakPeer; - unsigned int extraSocketOptions; -}; - -static const unsigned int MAX_OFFLINE_DATA_LENGTH=400; // I set this because I limit ID_CONNECTION_REQUEST to 512 bytes, and the password is appended to that packet. - -// Used to distinguish between offline messages with data, and messages from the reliability layer -// Should be different than any message that could result from messages from the reliability layer -#if !defined(__GNUC__) -#pragma warning(disable:4309) // 'initializing' : truncation of constant value -#endif -// Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false -static const unsigned char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78}; - -struct PacketFollowedByData -{ - Packet p; - unsigned char data[1]; -}; - -Packet *RakPeer::AllocPacket(unsigned dataSize, const char *file, unsigned int line) -{ - // Crashes when dataSize is 4 bytes - not sure why -// unsigned char *data = (unsigned char *) rakMalloc_Ex(sizeof(PacketFollowedByData)+dataSize, file, line); -// Packet *p = &((PacketFollowedByData *)data)->p; -// p->data=((PacketFollowedByData *)data)->data; -// p->length=dataSize; -// p->bitSize=BYTES_TO_BITS(dataSize); -// p->deleteData=false; -// p->guid=UNASSIGNED_RAKNET_GUID; -// return p; - - RakNet::Packet *p; - packetAllocationPoolMutex.Lock(); - p = packetAllocationPool.Allocate(file,line); - packetAllocationPoolMutex.Unlock(); - p = new ((void*)p) Packet; - p->data=(unsigned char*) rakMalloc_Ex(dataSize,file,line); - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=true; - p->guid=UNASSIGNED_RAKNET_GUID; - p->wasGeneratedLocally=false; - return p; -} - -Packet *RakPeer::AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line) -{ - // Packet *p = (Packet *)rakMalloc_Ex(sizeof(Packet), file, line); - RakNet::Packet *p; - packetAllocationPoolMutex.Lock(); - p = packetAllocationPool.Allocate(file,line); - packetAllocationPoolMutex.Unlock(); - p = new ((void*)p) Packet; - RakAssert(p); - p->data=data; - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=true; - p->guid=UNASSIGNED_RAKNET_GUID; - p->wasGeneratedLocally=false; - return p; -} - -STATIC_FACTORY_DEFINITIONS(RakPeerInterface,RakPeer) - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Constructor -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RakPeer() -{ - errorState = 0; -#if LIBCAT_SECURITY==1 - // Encryption and security - CAT_AUDIT_PRINTF("AUDIT: Initializing RakPeer security flags: using_security = false, server_handshake = null, cookie_jar = null\n"); - _using_security = false; - _server_handshake = 0; - _cookie_jar = 0; -#endif - - StringCompressor::AddReference(); - RakNet::StringTable::AddReference(); - WSAStartupSingleton::AddRef(); - - defaultMTUSize = mtuSizes[NUM_MTU_SIZES-1]; - trackFrequencyTable = false; - maximumIncomingConnections = 0; - maximumNumberOfPeers = 0; - //remoteSystemListSize=0; - remoteSystemList = 0; - activeSystemList = 0; - activeSystemListSize=0; - remoteSystemLookup=0; - bytesSentPerSecond = bytesReceivedPerSecond = 0; - endThreads = true; - isMainLoopThreadActive = false; - - - - - - // isRecvfromThreadActive=false; -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 - occasionalPing = true; -#else - occasionalPing = false; -#endif - allowInternalRouting=false; - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - ipList[i]=UNASSIGNED_SYSTEM_ADDRESS; - allowConnectionResponseIPMigration = false; - //incomingPasswordLength=outgoingPasswordLength=0; - incomingPasswordLength=0; - splitMessageProgressInterval=0; - //unreliableTimeout=0; - unreliableTimeout=1000; - maxOutgoingBPS=0; - firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; - myGuid=UNASSIGNED_RAKNET_GUID; - userUpdateThreadPtr=0; - userUpdateThreadData=0; - -#ifdef _DEBUG - // Wait longer to disconnect in debug so I don't get disconnected while tracing - defaultTimeoutTime=30000; -#else - defaultTimeoutTime=10000; -#endif - -#ifdef _DEBUG - _packetloss=0.0; - _minExtraPing=0; - _extraPingVariance=0; -#endif - - bufferedCommands.SetPageSize(sizeof(BufferedCommandStruct)*16); - socketQueryOutput.SetPageSize(sizeof(SocketQueryOutput)*8); - bufferedPackets.SetPageSize(sizeof(RecvFromStruct)*BUFFERED_PACKETS_PAGE_SIZE); // This is big, keep the size down - - packetAllocationPoolMutex.Lock(); - packetAllocationPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); - packetAllocationPoolMutex.Unlock(); - - remoteSystemIndexPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GenerateGUID(); - - quitAndDataEvents.InitEvent(); - limitConnectionFrequencyFromTheSameIP=false; - ResetSendReceipt(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Destructor -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::~RakPeer() -{ - Shutdown( 0, 0 ); - - // Free the ban list. - ClearBanList(); - - StringCompressor::RemoveReference(); - RakNet::StringTable::RemoveReference(); - WSAStartupSingleton::Deref(); - - quitAndDataEvents.CloseEvent(); - -#if LIBCAT_SECURITY==1 - // Encryption and security - CAT_AUDIT_PRINTF("AUDIT: Deleting RakPeer security objects, handshake = %x, cookie jar = %x\n", _server_handshake, _cookie_jar); - if (_server_handshake) RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - if (_cookie_jar) RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); -#endif - - - - - - - - - - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// \brief Starts the network threads, opens the listen port. -// You must call this before calling Connect(). -// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). -// \note Call SetMaximumIncomingConnections if you want to accept incoming connections -// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections -// \param[in] localPort The port to listen for connections on. -// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle. With new congestion control, the best results will be obtained by passing 10. -// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass &socketDescriptor, 1SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); -// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. -// \return False on failure (can't create socket or thread), true on success. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -StartupResult RakPeer::Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority ) -{ - errorState = 0; - - if (IsActive()) - return RAKNET_ALREADY_STARTED; - - if (threadPriority==-99999) - { - - -#if defined(_WIN32) - threadPriority=0; - - -#else - threadPriority=1000; -#endif - } - - - // Fill out ipList structure - unsigned int i; - - SocketLayer::GetMyIP( ipList ); -// for (i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) -// { -// if (ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS) -// { -// char str[128]; -// ipList[i].ToString(false,str); -// printf("%s\n",str); -// } -// } - - - if (myGuid==UNASSIGNED_RAKNET_GUID) - { - rnr.SeedMT( GenerateSeedFromGuid() ); - } - - RakPeerAndIndex rpai[32]; - RakAssert(socketDescriptorCount<32); - - RakAssert(socketDescriptors && socketDescriptorCount>=1); - - if (socketDescriptors==0 || socketDescriptorCount<1) - return INVALID_SOCKET_DESCRIPTORS; - - //unsigned short localPort; - //localPort=socketDescriptors[0].port; - - RakAssert( maxConnections > 0 ); - - if ( maxConnections <= 0 ) - return INVALID_MAX_CONNECTIONS; - - DerefAllSockets(); - - - // Go through all socket descriptors and precreate sockets on the specified addresses - for (i=0; i rns(RakNet::OP_NEW(_FILE_AND_LINE_)); - if (socketDescriptors[i].remotePortRakNetWasStartedOn_PS3_PSP2==0) - { - rns->s = (unsigned int) SocketLayer::CreateBoundSocket( socketDescriptors[i].port, true, addrToBind, 100, socketDescriptors[i].extraSocketOptions, socketDescriptors[i].socketFamily ); - } - else - { - - - - - - } - - if ((SOCKET)rns->s==(SOCKET)-1) - { - DerefAllSockets(); - return SOCKET_FAILED_TO_BIND; - } - - SocketLayer::GetSystemAddress( rns->s, &rns->boundAddress ); - - rns->remotePortRakNetWasStartedOn_PS3_PSP2=socketDescriptors[i].remotePortRakNetWasStartedOn_PS3_PSP2; - rns->extraSocketOptions=socketDescriptors[i].extraSocketOptions; - rns->userConnectionSocketIndex=i; - -#if RAKNET_SUPPORT_IPV6==0 - if (addrToBind==0) - rns->boundAddress.SetToLoopback(4); -#endif - - int zero=0; - if (SocketLayer::SendTo((SOCKET)rns->s, (const char*) &zero,4,rns->boundAddress, rns->remotePortRakNetWasStartedOn_PS3_PSP2, rns->extraSocketOptions, _FILE_AND_LINE_)!=0) - { - DerefAllSockets(); - return SOCKET_FAILED_TEST_SEND; - } - - socketList.Push(rns, _FILE_AND_LINE_ ); - - } - - - if ( maximumNumberOfPeers == 0 ) - { - // Don't allow more incoming connections than we have peers. - if ( maximumIncomingConnections > maxConnections ) - maximumIncomingConnections = maxConnections; - - maximumNumberOfPeers = maxConnections; - // 04/19/2006 - Don't overallocate because I'm no longer allowing connected pings. - // The disconnects are not consistently processed and the process was sloppy and complicated. - // Allocate 10% extra to handle new connections from players trying to connect when the server is full - //remoteSystemListSize = maxConnections;// * 11 / 10 + 1; - - // remoteSystemList in Single thread - //remoteSystemList = RakNet::OP_NEW( _FILE_AND_LINE_ ); - remoteSystemList = RakNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); - - remoteSystemLookup = RakNet::OP_NEW_ARRAY((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE, _FILE_AND_LINE_ ); - - activeSystemList = RakNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); - - for ( i = 0; i < maximumNumberOfPeers; i++ ) - //for ( i = 0; i < remoteSystemListSize; i++ ) - { - // remoteSystemList in Single thread - remoteSystemList[ i ].isActive = false; - remoteSystemList[ i ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystemList[ i ].guid = UNASSIGNED_RAKNET_GUID; - remoteSystemList[ i ].myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystemList[ i ].connectMode=RemoteSystemStruct::NO_ACTION; - remoteSystemList[ i ].MTUSize = defaultMTUSize; - remoteSystemList[ i ].remoteSystemIndex = (SystemIndex) i; -#ifdef _DEBUG - remoteSystemList[ i ].reliabilityLayer.ApplyNetworkSimulator(_packetloss, _minExtraPing, _extraPingVariance); -#endif - - // All entries in activeSystemList have valid pointers all the time. - activeSystemList[ i ] = &remoteSystemList[ i ]; - } - - for (unsigned int i=0; i < (unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE; i++) - { - remoteSystemLookup[i]=0; - } - } - - // For histogram statistics - // nextReadBytesTime=0; - // lastSentBytes=lastReceivedBytes=0; - - if ( endThreads ) - { - updateCycleIsRunning = false; - endThreads = false; - - ClearBufferedCommands(); - ClearBufferedPackets(); - ClearSocketQueryOutput(); - - if ( isMainLoopThreadActive == false ) - { - int errorCode; - - - - - - - - errorCode = RakNet::RakThread::Create(UpdateNetworkLoop, this, threadPriority); - - - if ( errorCode != 0 ) - { - Shutdown( 0, 0 ); - return FAILED_TO_CREATE_NETWORK_THREAD; - } - - RakAssert(isRecvFromLoopThreadActive.GetValue()==0); - for (i=0; is; - rpai[i].rakPeer=this; - - - - - - errorCode = RakNet::RakThread::Create(RecvFromLoop, &rpai[i], threadPriority); - - - if ( errorCode != 0 ) - { - Shutdown( 0, 0 ); - return FAILED_TO_CREATE_NETWORK_THREAD; - } - } - - - while ( isRecvFromLoopThreadActive.GetValue() < (uint32_t) socketDescriptorCount ) - RakSleep(10); - } - - // Wait for the threads to activate. When they are active they will set these variables to true - - while ( isMainLoopThreadActive == false ) - RakSleep(10); - - } - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->OnRakPeerStartup(); - } - - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->OnRakPeerStartup(); - } - -#ifdef USE_THREADED_SEND - RakNet::SendToThread::AddRef(); -#endif - - return RAKNET_STARTED; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Must be called while offline -// -// If you accept connections, you must call this or else security will not be enabled for incoming connections. -// -// This feature requires more round trips, bandwidth, and CPU time for the connection handshake -// x64 builds require under 25% of the CPU time of other builds -// -// See the Encryption sample for example usage -// -// Parameters: -// publicKey = A pointer to the public key for accepting new connections -// privateKey = A pointer to the private key for accepting new connections -// If the private keys are 0, then a new key will be generated when this function is called -// bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::InitializeSecurity(const char *public_key, const char *private_key, bool bRequireClientKey) -{ -#if LIBCAT_SECURITY==1 - if ( endThreads == false ) - return false; - - // Copy client public key requirement flag - _require_client_public_key = bRequireClientKey; - - if (_server_handshake) - { - CAT_AUDIT_PRINTF("AUDIT: Deleting old server_handshake %x\n", _server_handshake); - RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - } - if (_cookie_jar) - { - CAT_AUDIT_PRINTF("AUDIT: Deleting old cookie jar %x\n", _cookie_jar); - RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - } - - _server_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); - _cookie_jar = RakNet::OP_NEW(_FILE_AND_LINE_); - - CAT_AUDIT_PRINTF("AUDIT: Created new server_handshake %x\n", _server_handshake); - CAT_AUDIT_PRINTF("AUDIT: Created new cookie jar %x\n", _cookie_jar); - CAT_AUDIT_PRINTF("AUDIT: Running _server_handshake->Initialize()\n"); - - if (_server_handshake->Initialize(public_key, private_key)) - { - CAT_AUDIT_PRINTF("AUDIT: Successfully initialized, filling cookie jar with goodies, storing public key and setting using security flag to true\n"); - - _server_handshake->FillCookieJar(_cookie_jar); - - memcpy(my_public_key, public_key, sizeof(my_public_key)); - - _using_security = true; - return true; - } - - CAT_AUDIT_PRINTF("AUDIT: Failure to initialize so deleting server handshake and cookie jar; also setting using_security flag = false\n"); - - RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - _server_handshake=0; - RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - _cookie_jar=0; - _using_security = false; - return false; -#else - (void) public_key; - (void) private_key; - (void) bRequireClientKey; - - return false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description -// Must be called while offline -// Disables security for incoming connections. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DisableSecurity( void ) -{ -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: DisableSecurity() called, so deleting _server_handshake %x and cookie_jar %x\n", _server_handshake, _cookie_jar); - RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - _server_handshake=0; - RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - _cookie_jar=0; - - _using_security = false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToSecurityExceptionList(const char *ip) -{ - securityExceptionMutex.Lock(); - securityExceptionList.Insert(RakString(ip), _FILE_AND_LINE_); - securityExceptionMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromSecurityExceptionList(const char *ip) -{ - if (securityExceptionList.Size()==0) - return; - - if (ip==0) - { - securityExceptionMutex.Lock(); - securityExceptionList.Clear(false, _FILE_AND_LINE_); - securityExceptionMutex.Unlock(); - } - else - { - unsigned i=0; - securityExceptionMutex.Lock(); - while (i < securityExceptionList.Size()) - { - if (securityExceptionList[i].IPAddressMatch(ip)) - { - securityExceptionList[i]=securityExceptionList[securityExceptionList.Size()-1]; - securityExceptionList.RemoveAtIndex(securityExceptionList.Size()-1); - } - else - i++; - } - securityExceptionMutex.Unlock(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsInSecurityExceptionList(const char *ip) -{ - if (securityExceptionList.Size()==0) - return false; - - unsigned i=0; - securityExceptionMutex.Lock(); - for (; i < securityExceptionList.Size(); i++) - { - if (securityExceptionList[i].IPAddressMatch(ip)) - { - securityExceptionMutex.Unlock(); - return true; - } - } - securityExceptionMutex.Unlock(); - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no -// more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced -// to the maximum number of peers allowed. Defaults to 0. -// -// Parameters: -// numberAllowed - Maximum number of incoming connections allowed. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetMaximumIncomingConnections( unsigned short numberAllowed ) -{ - maximumIncomingConnections = numberAllowed; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the maximum number of incoming connections, which is always <= maxConnections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::GetMaximumIncomingConnections( void ) const -{ - return maximumIncomingConnections; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns how many open connections there are at this time -// \return the number of open connections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::NumberOfConnections(void) const -{ - DataStructures::List addresses; - DataStructures::List guids; - GetSystemList(addresses, guids); - return (unsigned short) addresses.Size(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sets the password incoming connections must match in the call to Connect (defaults to none) -// Pass 0 to passwordData to specify no password -// -// Parameters: -// passwordData: A data block that incoming connections must match. This can be just a password, or can be a stream of data. -// - Specify 0 for no password data -// passwordDataLength: The length in bytes of passwordData -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetIncomingPassword( const char* passwordData, int passwordDataLength ) -{ - //if (passwordDataLength > MAX_OFFLINE_DATA_LENGTH) - // passwordDataLength=MAX_OFFLINE_DATA_LENGTH; - - if (passwordDataLength > 255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? - // It won't overflow at least because incomingPasswordLength is an unsigned char - if (passwordDataLength>0) - memcpy(incomingPassword, passwordData, passwordDataLength); - incomingPasswordLength=(unsigned char)passwordDataLength; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetIncomingPassword( char* passwordData, int *passwordDataLength ) -{ - if (passwordData==0) - { - *passwordDataLength=incomingPasswordLength; - return; - } - - if (*passwordDataLength > incomingPasswordLength) - *passwordDataLength=incomingPasswordLength; - - if (*passwordDataLength>0) - memcpy(passwordData, incomingPassword, *passwordDataLength); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Call this to connect to the specified host (ip or domain name) and server port. -// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. -// This is a non-blocking connection. You know the connection is successful when IsConnected() returns true -// or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. If the connection is not -// successful, such as rejected connection or no response then neither of these things will happen. -// Requires that you first call Initialize -// -// Parameters: -// host: Either a dotted IP address or a domain name -// remotePort: Which port to connect to on the remote machine. -// passwordData: A data block that must match the data block on the server. This can be just a password, or can be a stream of data -// passwordDataLength: The length in bytes of passwordData -// -// Returns: -// True on successful initiation. False on incorrect parameters, internal error, or too many existing peers -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -ConnectionAttemptResult RakPeer::Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ) -{ - // If endThreads is true here you didn't call Startup() first. - if ( host == 0 || endThreads || connectionSocketIndex>=socketList.Size() ) - return INVALID_PARAMETER; - - RakAssert(remotePort!=0); - - connectionSocketIndex=GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - if (passwordDataLength>255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? - // It won't overflow at least because outgoingPasswordLength is an unsigned char -// if (passwordDataLength>0) -// memcpy(outgoingPassword, passwordData, passwordDataLength); -// outgoingPasswordLength=(unsigned char) passwordDataLength; - - // 04/02/09 - Can't remember why I disabled connecting to self, but it seems to work - // Connecting to ourselves in the same instance of the program? -// if ( ( strcmp( host, "127.0.0.1" ) == 0 || strcmp( host, "0.0.0.0" ) == 0 ) && remotePort == mySystemAddress[0].port ) -// return false; - - return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, connectionSocketIndex, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ConnectionAttemptResult RakPeer::ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime) -{ - if ( host == 0 || endThreads || socket.IsNull() ) - return INVALID_PARAMETER; - - if (passwordDataLength>255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, 0, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime, socket ); - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Stops the network threads and close all connections. Multiple calls are ok. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::Shutdown( unsigned int blockDuration, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) -{ - unsigned i,j; - bool anyActive; - RakNet::TimeMS startWaitingTime; -// SystemAddress systemAddress; - RakNet::TimeMS time; - //unsigned short systemListSize = remoteSystemListSize; // This is done for threading reasons - unsigned short systemListSize = maximumNumberOfPeers; - - if ( blockDuration > 0 ) - { - for ( i = 0; i < systemListSize; i++ ) - { - // remoteSystemList in user thread - if (remoteSystemList[i].isActive) - NotifyAndFlagForShutdown(remoteSystemList[i].systemAddress, false, orderingChannel, disconnectionNotificationPriority); - } - - time = RakNet::GetTimeMS(); - startWaitingTime = time; - while ( time - startWaitingTime < blockDuration ) - { - anyActive=false; - for (j=0; j < systemListSize; j++) - { - // remoteSystemList in user thread - if (remoteSystemList[j].isActive) - { - anyActive=true; - break; - } - } - - // If this system is out of packets to send, then stop waiting - if ( anyActive==false ) - break; - - // This will probably cause the update thread to run which will probably - // send the disconnection notification - - RakSleep(15); - time = RakNet::GetTimeMS(); - } - } - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->OnRakPeerShutdown(); - } - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->OnRakPeerShutdown(); - } - - activeSystemListSize=0; - - quitAndDataEvents.SetEvent(); - - endThreads = true; - // Get recvfrom to unblock - for (i=0; i < socketList.Size(); i++) - { - SystemAddress sa2 = socketList[i]->boundAddress; - if (SocketLayer::SendTo(socketList[i]->s, (const char*) &i,1,sa2, socketList[i]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[i]->extraSocketOptions, _FILE_AND_LINE_)!=0) - break; - } - while ( isMainLoopThreadActive ) - { - endThreads = true; - RakSleep(15); - } - - RakNet::TimeMS timeout = RakNet::GetTimeMS()+1000; - while ( isRecvFromLoopThreadActive.GetValue()>0 && RakNet::GetTimeMS()boundAddress; - SocketLayer::SendTo(socketList[i]->s, (const char*) &i,1,sa2, socketList[i]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[i]->extraSocketOptions, _FILE_AND_LINE_); - } - - RakSleep(30); - } - -// char c=0; -// unsigned int socketIndex; - // remoteSystemList in Single thread - for ( i = 0; i < systemListSize; i++ ) - { - // Reserve this reliability layer for ourselves - remoteSystemList[ i ].isActive = false; - - // Remove any remaining packets - remoteSystemList[ i ].reliabilityLayer.Reset(false, remoteSystemList[ i ].MTUSize, false); - remoteSystemList[ i ].rakNetSocket.SetNull(); - - } - - - // Setting maximumNumberOfPeers to 0 allows remoteSystemList to be reallocated in Initialize. - // Setting remoteSystemListSize prevents threads from accessing the reliability layer - maximumNumberOfPeers = 0; - //remoteSystemListSize = 0; - - // Free any packets the user didn't deallocate - packetReturnMutex.Lock(); - for (i=0; i < packetReturnQueue.Size(); i++) - DeallocatePacket(packetReturnQueue[i]); - packetReturnQueue.Clear(_FILE_AND_LINE_); - packetReturnMutex.Unlock(); - packetAllocationPoolMutex.Lock(); - packetAllocationPool.Clear(_FILE_AND_LINE_); - packetAllocationPoolMutex.Unlock(); - - if (isRecvFromLoopThreadActive.GetValue()>0) - { - timeout = RakNet::GetTimeMS()+1000; - while ( isRecvFromLoopThreadActive.GetValue()>0 && RakNet::GetTimeMS() addresses; - DataStructures::List guids; - GetSystemList(addresses, guids); - if (remoteSystems) - { - unsigned short i; - for (i=0; i < *numberOfSystems && i < addresses.Size(); i++) - remoteSystems[i]=addresses[i]; - *numberOfSystems=i; - } - else - { - *numberOfSystems=(unsigned short) addresses.Size(); - } - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::GetNextSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - uint32_t retVal = sendReceiptSerial; - sendReceiptSerialMutex.Unlock(); - return retVal; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::IncrementNextSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - uint32_t returned = sendReceiptSerial; - if (++sendReceiptSerial==0) - sendReceiptSerial=1; - sendReceiptSerialMutex.Unlock(); - return returned; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sends a block of data to the specified system that you are connected to. -// This function only works while the client is connected (Use the Connect function). -// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM -// -// Parameters: -// data: The block of data to send -// length: The size in bytes of the data to send -// bitStream: The bitstream to send -// priority: What priority level to send on. -// reliability: How reliability to send this data -// orderingChannel: When using ordered or sequenced packets, what channel to order these on. -// - Packets are only ordered relative to other packets on the same stream -// systemAddress: Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none -// broadcast: True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. -// Returns: -// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( data && length > 0 ); -#endif - RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); - RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - if ( data == 0 || length < 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined()) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback(data,length); - - if (reliability>=UNRELIABLE_WITH_ACK_RECEIPT) - { - char buff[5]; - buff[0]=ID_SND_RECEIPT_ACKED; - sendReceiptSerialMutex.Lock(); - memcpy(buff+1, &sendReceiptSerial, 4); - sendReceiptSerialMutex.Unlock(); - SendLoopback( buff, 5 ); - } - - return usedSendReceipt; - } - - SendBuffered(data, length*8, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - return usedSendReceipt; -} - -void RakPeer::SendLoopback( const char *data, const int length ) -{ - if ( data == 0 || length < 0 ) - return; - - Packet *packet = AllocPacket(length, _FILE_AND_LINE_); - memcpy(packet->data, data, length); - packet->systemAddress = GetLoopbackAddress(); - packet->guid=myGuid; - PushBackPacket(packet, false); -} - -uint32_t RakPeer::Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( bitStream->GetNumberOfBytesUsed() > 0 ); -#endif - - RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); - RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - if ( bitStream->GetNumberOfBytesUsed() == 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined() ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback((const char*) bitStream->GetData(),bitStream->GetNumberOfBytesUsed()); - if (reliability>=UNRELIABLE_WITH_ACK_RECEIPT) - { - char buff[5]; - buff[0]=ID_SND_RECEIPT_ACKED; - sendReceiptSerialMutex.Lock(); - memcpy(buff+1, &sendReceiptSerial,4); - sendReceiptSerialMutex.Unlock(); - SendLoopback( buff, 5 ); - } - return usedSendReceipt; - } - - // Sends need to be buffered and processed in the update thread because the systemAddress associated with the reliability layer can change, - // from that thread, resulting in a send to the wrong player! While I could mutex the systemAddress, that is much slower than doing this - SendBuffered((const char*)bitStream->GetData(), bitStream->GetNumberOfBitsUsed(), priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - - return usedSendReceipt; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Sends multiple blocks of data, concatenating them automatically. -// -// This is equivalent to: -// RakNet::BitStream bs; -// bs.WriteAlignedBytes(block1, blockLength1); -// bs.WriteAlignedBytes(block2, blockLength2); -// bs.WriteAlignedBytes(block3, blockLength3); -// Send(&bs, ...) -// -// This function only works while the connected -// \param[in] data An array of pointers to blocks of data -// \param[in] lengths An array of integers indicating the length of each block of data -// \param[in] numParameters Length of the arrays data and lengths -// \param[in] priority What priority level to send on. See PacketPriority.h -// \param[in] reliability How reliability to send this data. See PacketPriority.h -// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream -// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none -// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. -// \return False if we are not connected to the specified recipient. True otherwise -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( data ); -#endif - - if ( data == 0 || lengths == 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if (numParameters==0) - return 0; - - if (lengths==0) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined() ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - SendBufferedList(data, lengths, numParameters, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - return usedSendReceipt; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. -// Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct -// -// Returns: -// 0 if no packets are waiting to be handled, otherwise an allocated packet -// If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected -// This also updates all memory blocks associated with synchronized memory and distributed objects -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -#ifdef _MSC_VER -#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized -#endif -Packet* RakPeer::Receive( void ) -{ - if ( !( IsActive() ) ) - return 0; - - RakNet::Packet *packet; -// Packet **threadPacket; - PluginReceiveResult pluginResult; - - int offset; - unsigned int i; - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->Update(); - } - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->Update(); - } - - do - { - packetReturnMutex.Lock(); - if (packetReturnQueue.IsEmpty()) - packet=0; - else - packet = packetReturnQueue.Pop(); - packetReturnMutex.Unlock(); - if (packet==0) - return 0; - -// unsigned char msgId; - if ( ( packet->length >= sizeof(unsigned char) + sizeof( RakNet::Time ) ) && - ( (unsigned char) packet->data[ 0 ] == ID_TIMESTAMP ) ) - { - offset = sizeof(unsigned char); - ShiftIncomingTimestamp( packet->data + offset, packet->systemAddress ); -// msgId=packet->data[sizeof(unsigned char) + sizeof( RakNet::Time )]; - } -// else - // msgId=packet->data[0]; - - // Some locally generated packets need to be processed by plugins, for example ID_FCM2_NEW_HOST - // The plugin itself should intercept these messages generated remotely -// if (packet->wasGeneratedLocally) -// return packet; - - - CallPluginCallbacks(pluginListTS, packet); - CallPluginCallbacks(pluginListNTS, packet); - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginResult=pluginListTS[i]->OnReceive(packet); - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( packet ); - packet=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - packet=0; - break; - } - } - - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginResult=pluginListNTS[i]->OnReceive(packet); - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( packet ); - packet=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - packet=0; - break; - } - } - - } while(packet==0); - -#ifdef _DEBUG - RakAssert( packet->data ); -#endif - - return packet; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Call this to deallocate a packet returned by Receive -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DeallocatePacket( Packet *packet ) -{ - if ( packet == 0 ) - return; - - if (packet->deleteData) - { - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - packet->~Packet(); - packetAllocationPoolMutex.Lock(); - packetAllocationPool.Release(packet,_FILE_AND_LINE_); - packetAllocationPoolMutex.Unlock(); - } - else - { - rakFree_Ex(packet, _FILE_AND_LINE_ ); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the total number of connections we are allowed -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::GetMaximumNumberOfPeers( void ) const -{ - return maximumNumberOfPeers; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). -// -// Parameters: -// target: Which connection to close -// sendDisconnectionNotification: True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. -// channel: If blockDuration > 0, the disconnect packet will be sent on this channel -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) -{ - /* - // This only be called from the user thread, for the user shutting down. - // From the network thread, this should occur because of ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST - unsigned j; - for (j=0; j < messageHandlerList.Size(); j++) - { - messageHandlerList[j]->OnClosedConnection( - target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress, - target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid, - LCR_CLOSED_BY_USER); - } - */ - - CloseConnectionInternal(target, sendDisconnectionNotification, false, orderingChannel, disconnectionNotificationPriority); - - // 12/14/09 Return ID_CONNECTION_LOST when calling CloseConnection with sendDisconnectionNotification==false, elsewise it is never returned - if (sendDisconnectionNotification==false && GetConnectionState(target)==IS_CONNECTED) - { - Packet *packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection - packet->guid = target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid; - packet->systemAddress = target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress; - packet->systemAddress.systemIndex = (SystemIndex) GetIndexFromSystemAddress(packet->systemAddress); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - packet->wasGeneratedLocally=true; // else processed twice - AddPacketToProducer(packet); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Cancel a pending connection attempt -// If we are already connected, the connection stays open -// \param[in] target Which system to cancel -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CancelConnectionAttempt( const SystemAddress target ) -{ - unsigned int i; - - // Cancel pending connection attempt, if there is one - i=0; - requestedConnectionQueueMutex.Lock(); - while (i < requestedConnectionQueue.Size()) - { - if (requestedConnectionQueue[i]->systemAddress==target) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Deleting requestedConnectionQueue %i client_handshake %x\n", i, requestedConnectionQueue[ i ]->client_handshake); - RakNet::OP_DELETE(requestedConnectionQueue[i]->client_handshake, _FILE_AND_LINE_ ); -#endif - RakNet::OP_DELETE(requestedConnectionQueue[i], _FILE_AND_LINE_ ); - requestedConnectionQueue.RemoveAtIndex(i); - break; - } - else - i++; - } - requestedConnectionQueueMutex.Unlock(); - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ConnectionState RakPeer::GetConnectionState(const AddressOrGUID systemIdentifier) -{ - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemIdentifier.systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - return IS_PENDING; - } - } - requestedConnectionQueueMutex.Unlock(); - } - - int index; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); - } - else - { - index = GetIndexFromGuid(systemIdentifier.rakNetGuid); - } - - if (index==-1) - return IS_NOT_CONNECTED; - - if (remoteSystemList[index].isActive==false) - return IS_DISCONNECTED; - - switch (remoteSystemList[index].connectMode) - { - case RemoteSystemStruct::DISCONNECT_ASAP: - return IS_DISCONNECTING; - case RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY: - return IS_SILENTLY_DISCONNECTING; - case RemoteSystemStruct::DISCONNECT_ON_NO_ACK: - return IS_DISCONNECTING; - case RemoteSystemStruct::REQUESTED_CONNECTION: - return IS_CONNECTING; - case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: - return IS_CONNECTING; - case RemoteSystemStruct::UNVERIFIED_SENDER: - return IS_CONNECTING; - case RemoteSystemStruct::CONNECTED: - return IS_CONNECTED; - default: - return IS_NOT_CONNECTED; - } - - return IS_NOT_CONNECTED; -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. -// -// Parameters -// systemAddress - The systemAddress to search for -// -// Returns -// An integer from 0 to the maximum number of peers -1, or -1 if that player is not found -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress ) const -{ - return GetIndexFromSystemAddress(systemAddress, false); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// This function is only useful for looping through all players. -// -// Parameters -// index - an integer between 0 and the maximum number of players allowed - 1. -// -// Returns -// A valid systemAddress or UNASSIGNED_SYSTEM_ADDRESS if no such player at that index -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetSystemAddressFromIndex( int index ) -{ - // remoteSystemList in user thread - //if ( index >= 0 && index < remoteSystemListSize ) - if ( index >= 0 && index < maximumNumberOfPeers ) - if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail - return remoteSystemList[ index ].systemAddress; - - return UNASSIGNED_SYSTEM_ADDRESS; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Same as GetSystemAddressFromIndex but returns RakNetGUID -// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. -// \return The RakNetGUID -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetGUID RakPeer::GetGUIDFromIndex( int index ) -{ - // remoteSystemList in user thread - //if ( index >= 0 && index < remoteSystemListSize ) - if ( index >= 0 && index < maximumNumberOfPeers ) - if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail - return remoteSystemList[ index ].guid; - - return UNASSIGNED_RAKNET_GUID; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient -// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system -// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. -// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const -{ - addresses.Clear(false, _FILE_AND_LINE_); - guids.Clear(false, _FILE_AND_LINE_); - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) - { - addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); - guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Bans an IP from connecting. Banned IPs persist between connections. -// -// Parameters -// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban -// All IP addresses starting with 128.0.0 -// milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToBanList( const char *IP, RakNet::TimeMS milliseconds ) -{ - unsigned index; - RakNet::TimeMS time = RakNet::GetTimeMS(); - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return ; - - // If this guy is already in the ban list, do nothing - index = 0; - - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - if ( strcmp( IP, banList[ index ]->IP ) == 0 ) - { - // Already in the ban list. Just update the time - if (milliseconds==0) - banList[ index ]->timeout=0; // Infinite - else - banList[ index ]->timeout=time+milliseconds; - banListMutex.Unlock(); - return; - } - } - - banListMutex.Unlock(); - - BanStruct *banStruct = RakNet::OP_NEW( _FILE_AND_LINE_ ); - banStruct->IP = (char*) rakMalloc_Ex( 16, _FILE_AND_LINE_ ); - if (milliseconds==0) - banStruct->timeout=0; // Infinite - else - banStruct->timeout=time+milliseconds; - strcpy( banStruct->IP, IP ); - banListMutex.Lock(); - banList.Insert( banStruct, _FILE_AND_LINE_ ); - banListMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allows a previously banned IP to connect. -// -// Parameters -// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban -// All IP addresses starting with 128.0.0 -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromBanList( const char *IP ) -{ - unsigned index; - BanStruct *temp; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return ; - - index = 0; - temp=0; - - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - if ( strcmp( IP, banList[ index ]->IP ) == 0 ) - { - temp = banList[ index ]; - banList[ index ] = banList[ banList.Size() - 1 ]; - banList.RemoveAtIndex( banList.Size() - 1 ); - break; - } - } - - banListMutex.Unlock(); - - if (temp) - { - rakFree_Ex(temp->IP, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allows all previously banned IPs to connect. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBanList( void ) -{ - unsigned index; - index = 0; - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - rakFree_Ex(banList[ index ]->IP, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(banList[ index ], _FILE_AND_LINE_); - } - - banList.Clear(false, _FILE_AND_LINE_); - - banListMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetLimitIPConnectionFrequency(bool b) -{ - limitConnectionFrequencyFromTheSameIP=b; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Determines if a particular IP is banned. -// -// Parameters -// IP - Complete dotted IP address -// -// Returns -// True if IP matches any IPs in the ban list, accounting for any wildcards. -// False otherwise. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsBanned( const char *IP ) -{ - unsigned banListIndex, characterIndex; - RakNet::TimeMS time; - BanStruct *temp; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return false; - - banListIndex = 0; - - if ( banList.Size() == 0 ) - return false; // Skip the mutex if possible - - time = RakNet::GetTimeMS(); - - banListMutex.Lock(); - - while ( banListIndex < banList.Size() ) - { - if (banList[ banListIndex ]->timeout>0 && banList[ banListIndex ]->timeoutIP, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - else - { - characterIndex = 0; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( true ) - { - if ( banList[ banListIndex ]->IP[ characterIndex ] == IP[ characterIndex ] ) - { - // Equal characters - - if ( IP[ characterIndex ] == 0 ) - { - banListMutex.Unlock(); - // End of the string and the strings match - - return true; - } - - characterIndex++; - } - - else - { - if ( banList[ banListIndex ]->IP[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) - { - // End of one of the strings - break; - } - - // Characters do not match - if ( banList[ banListIndex ]->IP[ characterIndex ] == '*' ) - { - banListMutex.Unlock(); - - // Domain is banned. - return true; - } - - // Characters do not match and it is not a * - break; - } - } - - banListIndex++; - } - } - - banListMutex.Unlock(); - - // No match found. - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Send a ping to the specified connected system. -// -// Parameters: -// target - who to ping -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::Ping( const SystemAddress target ) -{ - PingInternal(target, false, UNRELIABLE); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Send a ping to the specified unconnected system. -// The remote system, if it is Initialized, will respond with ID_UNCONNECTED_PONG. -// The final ping time will be encoded in the following sizeof(RakNet::TimeMS) bytes. (Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h -// -// Parameters: -// host: Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. -// remotePort: Which port to connect to on the remote machine. -// onlyReplyOnAcceptingConnections: Only request a reply if the remote system has open connections -// connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex ) -{ - if ( host == 0 ) - return false; - - // If this assert hits then Startup wasn't called or the call failed. - RakAssert(connectionSocketIndex < socketList.Size()); - -// if ( IsActive() == false ) -// return; - - RakNet::BitStream bitStream( sizeof(unsigned char) + sizeof(RakNet::Time) ); - if ( onlyReplyOnAcceptingConnections ) - bitStream.Write((MessageID)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); - else - bitStream.Write((MessageID)ID_UNCONNECTED_PING); - - bitStream.Write(RakNet::GetTime()); - - bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - - // No timestamp for 255.255.255.255 - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->boundAddress.GetIPVersion()); - systemAddress.FixForIPVersion(socketList[realIndex]->boundAddress); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( socketList[realIndex]->s, (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, socketList[realIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[realIndex]->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the average of all ping times read for a specified target -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetAveragePing( const AddressOrGUID systemIdentifier ) -{ - int sum, quantity; - RemoteSystemStruct *remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - - for ( sum = 0, quantity = 0; quantity < PING_TIMES_ARRAY_SIZE; quantity++ ) - { - if ( remoteSystem->pingAndClockDifferential[ quantity ].pingTime == 65535 ) - break; - else - sum += remoteSystem->pingAndClockDifferential[ quantity ].pingTime; - } - - if ( quantity > 0 ) - return sum / quantity; - else - return -1; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the last ping time read for the specific player or -1 if none read yet -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetLastPing( const AddressOrGUID systemIdentifier ) const -{ - RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - -// return (int)(remoteSystem->reliabilityLayer.GetAckPing()/(RakNet::TimeUS)1000); - - if ( remoteSystem->pingAndClockDifferentialWriteIndex == 0 ) - return remoteSystem->pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE - 1 ].pingTime; - else - return remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex - 1 ].pingTime; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the lowest ping time read or -1 if none read yet -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetLowestPing( const AddressOrGUID systemIdentifier ) const -{ - RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - - return remoteSystem->lowestPing; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Ping the remote systems every so often. This is off by default -// This will work anytime -// -// Parameters: -// doPing - True to start occasional pings. False to stop them. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetOccasionalPing( bool doPing ) -{ - occasionalPing = doPing; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Length should be under 400 bytes, as a security measure against flood attacks -// Sets the data to send with an (LAN server discovery) /(offline ping) response -// See the Ping sample project for how this is used. -// data: a block of data to store, or 0 for none -// length: The length of data in bytes, or 0 for none -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetOfflinePingResponse( const char *data, const unsigned int length ) -{ - RakAssert(length < 400); - - rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); - offlinePingResponse.Reset(); - - if ( data && length > 0 ) - offlinePingResponse.Write( data, length ); - - rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns pointers to a copy of the data passed to SetOfflinePingResponse -// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() -// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() -// \sa SetOfflinePingResponse -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetOfflinePingResponse( char **data, unsigned int *length ) -{ - rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); - *data = (char*) offlinePingResponse.GetData(); - *length = (int) offlinePingResponse.GetNumberOfBytesUsed(); - rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the unique SystemAddress that represents you on the the network -// Note that unlike in previous versions, this is a struct and is not sequential -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetInternalID( const SystemAddress systemAddress, const int index ) const -{ - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - return ipList[index]; - } - else - { - -// SystemAddress returnValue; - RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, false, true ); - if (remoteSystem==0) - return UNASSIGNED_SYSTEM_ADDRESS; - - return remoteSystem->theirInternalSystemAddress[index]; - /* - sockaddr_in sa; - socklen_t len = sizeof(sa); - if (getsockname__(connectionSockets[remoteSystem->connectionSocketIndex], (sockaddr*)&sa, &len)!=0) - return UNASSIGNED_SYSTEM_ADDRESS; - returnValue.port=ntohs(sa.sin_port); - returnValue.binaryAddress=sa.sin_addr.s_addr; - return returnValue; -*/ - - - - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the unique address identifier that represents you on the the network and is based on your external -// IP / port (the IP / port the specified player uses to communicate with you) -// Note that unlike in previous versions, this is a struct and is not sequential -// -// Parameters: -// target: Which remote system you are referring to for your external ID -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetExternalID( const SystemAddress target ) const -{ - unsigned i; - SystemAddress inactiveExternalId; - - inactiveExternalId=UNASSIGNED_SYSTEM_ADDRESS; - - if (target==UNASSIGNED_SYSTEM_ADDRESS) - return firstExternalID; - - // First check for active connection with this systemAddress - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].systemAddress == target ) - { - if ( remoteSystemList[ i ].isActive ) - return remoteSystemList[ i ].myExternalSystemAddress; - else if (remoteSystemList[ i ].myExternalSystemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - inactiveExternalId=remoteSystemList[ i ].myExternalSystemAddress; - } - } - - return inactiveExternalId; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -const RakNetGUID RakPeer::GetMyGUID(void) const -{ - return myGuid; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetMyBoundAddress(const int socketIndex) -{ - DataStructures::List > sockets; - GetSockets( sockets ); - if (sockets.Size()>0) - return sockets[socketIndex]->boundAddress; - else - return UNASSIGNED_SYSTEM_ADDRESS; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -const RakNetGUID& RakPeer::GetGuidFromSystemAddress( const SystemAddress input ) const -{ - if (input==UNASSIGNED_SYSTEM_ADDRESS) - return myGuid; - - if (input.systemIndex!=(SystemIndex)-1 && input.systemIndexreliabilityLayer.SetTimeoutTime(timeMS); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RakNet::TimeMS RakPeer::GetTimeoutTime( const SystemAddress target ) -{ - if (target==UNASSIGNED_SYSTEM_ADDRESS) - { - return defaultTimeoutTime; - } - else - { - RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( target, false, true ); - - if ( remoteSystem != 0 ) - remoteSystem->reliabilityLayer.GetTimeoutTime(); - } - return defaultTimeoutTime; -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the current MTU size -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetMTUSize( const SystemAddress target ) const -{ - if (target!=UNASSIGNED_SYSTEM_ADDRESS) - { - RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(target, false, true); - if (rss) - return rss->MTUSize; - } - return defaultMTUSize; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the number of IP addresses we have -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetNumberOfAddresses( void ) -{ - - int i = 0; - - while ( ipList[ i ]!=UNASSIGNED_SYSTEM_ADDRESS ) - i++; - - return i; - - - - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns an IP address at index 0 to GetNumberOfAddresses-1 -// \param[in] index index into the list of IP addresses -// \return The local IP address at this index -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -const char* RakPeer::GetLocalIP( unsigned int index ) -{ - if (IsActive()==false) - { - // Fill out ipList structure - - SocketLayer::GetMyIP( ipList ); - - } - - - static char str[128]; - ipList[index].ToString(false,str); - return str; - - - - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Is this a local IP? -// \param[in] An IP address to check -// \return True if this is one of the IP addresses returned by GetLocalIP -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsLocalIP( const char *ip ) -{ - if (ip==0 || ip[0]==0) - return false; - - - if (strcmp(ip, "127.0.0.1")==0 || strcmp(ip, "localhost")==0) - return true; - - int num = GetNumberOfAddresses(); - int i; - for (i=0; i < num; i++) - { - if (strcmp(ip, GetLocalIP(i))==0) - return true; - } - - - - - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary -// when connection to servers with multiple IP addresses -// -// Parameters: -// allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AllowConnectionResponseIPMigration( bool allow ) -{ - allowConnectionResponseIPMigration = allow; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sends a message ID_ADVERTISE_SYSTEM to the remote unconnected system. -// This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through -// -// Requires: -// The sender and recipient must already be started via a successful call to Initialize -// -// host: Either a dotted IP address or a domain name -// remotePort: Which port to connect to on the remote machine. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex ) -{ - RakNet::BitStream bs; - bs.Write((MessageID)ID_ADVERTISE_SYSTEM); - bs.WriteAlignedBytes((const unsigned char*) data,dataLength); - return SendOutOfBand(host, remotePort, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), connectionSocketIndex ); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. -// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived -// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. -// Defaults to 0 (never return this notification) -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSplitMessageProgressInterval(int interval) -{ - RakAssert(interval>=0); - splitMessageProgressInterval=interval; - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - remoteSystemList[ i ].reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns what was passed to SetSplitMessageProgressInterval() -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetSplitMessageProgressInterval(void) const -{ - return splitMessageProgressInterval; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Set how long to wait before giving up on sending an unreliable message -// Useful if the network is clogged up. -// Set to 0 or less to never timeout. Defaults to 0. -// timeoutMS How many ms to wait before simply not sending an unreliable message. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetUnreliableTimeout(RakNet::TimeMS timeoutMS) -{ - unreliableTimeout=timeoutMS; - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - remoteSystemList[ i ].reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Send a message to host, with the IP socket option TTL set to 3 -// This message will not reach the host, but will open the router. -// Used for NAT-Punchthrough -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex ) -{ - char fakeData[2]; - fakeData[0]=0; - fakeData[1]=1; - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - SystemAddress systemAddress = socketList[realIndex]->boundAddress; - systemAddress.FromStringExplicitPort(host,remotePort); - SocketLayer::SendToTTL( socketList[realIndex]->s, (char*)fakeData, 2, systemAddress, ttl ); -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Attatches a Plugin interface to run code automatically on message receipt in the Receive call -// -// \param messageHandler Pointer to a plugin to attach -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AttachPlugin( PluginInterface2 *plugin ) -{ - bool isNotThreadsafe = plugin->UsesReliabilityLayer(); - if (isNotThreadsafe) - { - if (pluginListNTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - plugin->SetRakPeerInterface(this); - plugin->OnAttach(); - pluginListNTS.Insert(plugin, _FILE_AND_LINE_); - } - } - else - { - if (pluginListTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - plugin->SetRakPeerInterface(this); - plugin->OnAttach(); - pluginListTS.Insert(plugin, _FILE_AND_LINE_); - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Detaches a Plugin interface to run code automatically on message receipt -// -// \param messageHandler Pointer to a plugin to detach -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DetachPlugin( PluginInterface2 *plugin ) -{ - if (plugin==0) - return; - - unsigned int index; - - bool isNotThreadsafe = plugin->UsesReliabilityLayer(); - if (isNotThreadsafe) - { - index = pluginListNTS.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - // Unordered list so delete from end for speed - pluginListNTS[index]=pluginListNTS[pluginListNTS.Size()-1]; - pluginListNTS.RemoveFromEnd(); - } - } - else - { - index = pluginListTS.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - // Unordered list so delete from end for speed - pluginListTS[index]=pluginListTS[pluginListTS.Size()-1]; - pluginListTS.RemoveFromEnd(); - } - } - plugin->OnDetach(); - plugin->SetRakPeerInterface(0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Put a packet back at the end of the receive queue in case you don't want to deal with it immediately -// -// packet The packet you want to push back. -// pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PushBackPacket( Packet *packet, bool pushAtHead) -{ - if (packet==0) - return; - - unsigned i; - for (i=0; i < pluginListTS.Size(); i++) - pluginListTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); - - packetReturnMutex.Lock(); - if (pushAtHead) - packetReturnQueue.PushAtHead(packet,0,_FILE_AND_LINE_); - else - packetReturnQueue.Push(packet,_FILE_AND_LINE_); - packetReturnMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress) -{ - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = 0; - bcs->systemIdentifier.systemAddress=systemAddress; - bcs->systemIdentifier.rakNetGuid=guid; - bcs->command=BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS; - bufferedCommands.Push(bcs); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -Packet* RakPeer::AllocatePacket(unsigned dataSize) -{ - return AllocPacket(dataSize, _FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetSmartPtr RakPeer::GetSocket( const SystemAddress target ) -{ - // Send a query to the thread to get the socket, and return when we got it - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; - bcs->systemIdentifier=target; - bcs->data=0; - bufferedCommands.Push(bcs); - - // Block up to one second to get the socket, although it should actually take virtually no time - SocketQueryOutput *sqo; - RakNet::TimeMS stopWaiting = RakNet::GetTimeMS()+1000; - DataStructures::List > output; - while (RakNet::GetTimeMS() < stopWaiting) - { - if (isMainLoopThreadActive==false) - return RakNetSmartPtr(); - - RakSleep(0); - - sqo = socketQueryOutput.Pop(); - if (sqo) - { - output=sqo->sockets; - sqo->sockets.Clear(false, _FILE_AND_LINE_); - socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); - if (output.Size()) - return output[0]; - break; - } - } - return RakNetSmartPtr(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSockets( DataStructures::List > &sockets ) -{ - sockets.Clear(false, _FILE_AND_LINE_); - - // Send a query to the thread to get the socket, and return when we got it - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; - bcs->systemIdentifier=UNASSIGNED_SYSTEM_ADDRESS; - bcs->data=0; - bufferedCommands.Push(bcs); - - // Block up to one second to get the socket, although it should actually take virtually no time - SocketQueryOutput *sqo; - RakNetSmartPtr output; - while (1) - { - if (isMainLoopThreadActive==false) - return; - - RakSleep(0); - - sqo = socketQueryOutput.Pop(); - if (sqo) - { - sockets=sqo->sockets; - sqo->sockets.Clear(false, _FILE_AND_LINE_); - socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); - return; - } - } - return; -} -void RakPeer::ReleaseSockets( DataStructures::List > &sockets ) -{ - sockets.Clear(false,_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Adds simulated ping and packet loss to the outgoing data flow. -// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance) -{ -#ifdef _DEBUG - if (remoteSystemList) - { - unsigned short i; - for (i=0; i < maximumNumberOfPeers; i++) - //for (i=0; i < remoteSystemListSize; i++) - remoteSystemList[i].reliabilityLayer.ApplyNetworkSimulator(packetloss, minExtraPing, extraPingVariance); - } - - _packetloss=packetloss; - _minExtraPing=minExtraPing; - _extraPingVariance=extraPingVariance; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void RakPeer::SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ) -{ - maxOutgoingBPS=maxBitsPerSecond; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns if you previously called ApplyNetworkSimulator -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsNetworkSimulatorActive( void ) -{ -#ifdef _DEBUG - return _packetloss>0 || _minExtraPing>0 || _extraPingVariance>0; -#else - return false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::WriteOutOfBandHeader(RakNet::BitStream *bitStream) -{ - bitStream->Write((MessageID)ID_OUT_OF_BAND_INTERNAL); - bitStream->Write(myGuid); - bitStream->WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData) -{ - userUpdateThreadPtr=_userUpdateThreadPtr; - userUpdateThreadData=_userUpdateThreadData; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex ) -{ - if ( IsActive() == false ) - return false; - - if (host==0 || host[0]==0) - return false; - - // If this assert hits then Startup wasn't called or the call failed. - RakAssert(connectionSocketIndex < socketList.Size()); - - // This is a security measure. Don't send data longer than this value - RakAssert(dataLength <= (MAX_OFFLINE_DATA_LENGTH + sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID))); - - if (host==0) - return false; - - // 34 bytes - RakNet::BitStream bitStream; - WriteOutOfBandHeader(&bitStream); - - if (dataLength>0) - { - bitStream.Write(data, dataLength); - } - - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->boundAddress.GetIPVersion()); - systemAddress.FixForIPVersion(socketList[realIndex]->boundAddress); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); - - SocketLayer::SendTo( socketList[realIndex]->s, (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, socketList[realIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[realIndex]->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetStatistics * RakPeer::GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns ) -{ - static RakNetStatistics staticStatistics; - RakNetStatistics *systemStats; - if (rns==0) - systemStats=&staticStatistics; - else - systemStats=rns; - - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - bool firstWrite=false; - // Return a crude sum - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].isActive) - { - RakNetStatistics rnsTemp; - remoteSystemList[ i ].reliabilityLayer.GetStatistics(&rnsTemp); - - if (firstWrite==false) - { - memcpy(systemStats, &rnsTemp, sizeof(RakNetStatistics)); - firstWrite=true; - } - else - (*systemStats)+=rnsTemp; - } - } - return systemStats; - } - else - { - RemoteSystemStruct * rss; - rss = GetRemoteSystemFromSystemAddress( systemAddress, false, false ); - if ( rss && endThreads==false ) - { - rss->reliabilityLayer.GetStatistics(systemStats); - return systemStats; - } - } - - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::GetStatistics( const int index, RakNetStatistics *rns ) -{ - if (index < maximumNumberOfPeers && remoteSystemList[ index ].isActive) - { - remoteSystemList[ index ].reliabilityLayer.GetStatistics(rns); - return true; - } - return false; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetReceiveBufferSize(void) -{ - unsigned int size; - packetReturnMutex.Lock(); - size=packetReturnQueue.Size(); - packetReturnMutex.Unlock(); - return size; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const -{ - unsigned i; - - if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) - return -1; - - if (systemAddress.systemIndex!=(SystemIndex)-1 && systemAddress.systemIndex < maximumNumberOfPeers && remoteSystemList[systemAddress.systemIndex].systemAddress==systemAddress && remoteSystemList[ systemAddress.systemIndex ].isActive) - return systemAddress.systemIndex; - - if (calledFromNetworkThread) - { - return GetRemoteSystemIndex(systemAddress); - } - else - { - // remoteSystemList in user and network thread - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].systemAddress == systemAddress ) - return i; - - // If no active results found, try previously active results. - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].systemAddress == systemAddress ) - return i; - } - - return -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromGuid( const RakNetGUID guid ) -{ - unsigned i; - - if ( guid == UNASSIGNED_RAKNET_GUID ) - return -1; - - if (guid.systemIndex!=(SystemIndex)-1 && guid.systemIndex < maximumNumberOfPeers && remoteSystemList[guid.systemIndex].guid==guid && remoteSystemList[ guid.systemIndex ].isActive) - return guid.systemIndex; - - // remoteSystemList in user and network thread - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].guid == guid ) - return i; - - // If no active results found, try previously active results. - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].guid == guid ) - return i; - - return -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -#if LIBCAT_SECURITY==1 -bool RakPeer::GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey) -{ - CAT_AUDIT_PRINTF("AUDIT: In GenerateConnectionRequestChallenge()\n"); - - rcs->client_handshake = 0; - rcs->publicKeyMode = PKM_INSECURE_CONNECTION; - - if (!publicKey) return true; - - switch (publicKey->publicKeyMode) - { - default: - case PKM_INSECURE_CONNECTION: - break; - - case PKM_ACCEPT_ANY_PUBLIC_KEY: - CAT_OBJCLR(rcs->remote_public_key); - rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); - - rcs->publicKeyMode = PKM_ACCEPT_ANY_PUBLIC_KEY; - break; - - case PKM_USE_TWO_WAY_AUTHENTICATION: - if (publicKey->myPublicKey == 0 || publicKey->myPrivateKey == 0 || - publicKey->remoteServerPublicKey == 0) - { - return false; - } - - rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); - memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); - - if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || - !rcs->client_handshake->SetIdentity(publicKey->myPublicKey, publicKey->myPrivateKey) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object with identity for this RequestedConnectionStruct\n"); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - return false; - } - - CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object with identity for this RequestedConnectionStruct -- pre-generated challenge\n"); - - rcs->publicKeyMode = PKM_USE_TWO_WAY_AUTHENTICATION; - break; - - case PKM_USE_KNOWN_PUBLIC_KEY: - if (publicKey->remoteServerPublicKey == 0) - return false; - - rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); - memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); - - if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object for this RequestedConnectionStruct\n"); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - return false; - } - - CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object for this RequestedConnectionStruct -- pre-generated challenge\n"); - - rcs->publicKeyMode = PKM_USE_KNOWN_PUBLIC_KEY; - break; - } - - return true; -} -#endif -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ) -{ - RakAssert(passwordDataLength <= 256); - RakAssert(remotePort!=0); - SystemAddress systemAddress; - if (!systemAddress.FromStringExplicitPort(host,remotePort,socketList[connectionSocketIndex]->boundAddress.GetIPVersion())) - return CANNOT_RESOLVE_DOMAIN_NAME; - - // Already connected? - if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) - return ALREADY_CONNECTED_TO_ENDPOINT; - - //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); - RequestedConnectionStruct *rcs = RakNet::OP_NEW(_FILE_AND_LINE_); - - rcs->systemAddress=systemAddress; - rcs->nextRequestTime=RakNet::GetTimeMS(); - rcs->requestsMade=0; - rcs->data=0; - rcs->extraData=extraData; - rcs->socketIndex=connectionSocketIndex; - rcs->actionToTake=RequestedConnectionStruct::CONNECT; - rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; - rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; - memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); - rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; - rcs->timeoutTime=timeoutTime; - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: In SendConnectionRequest()\n"); - if (!GenerateConnectionRequestChallenge(rcs,publicKey)) - return SECURITY_INITIALIZATION_FAILED; -#else - (void) publicKey; -#endif - - // Return false if already pending, else push on queue - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - // Not necessary - //RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; - } - } - requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - - return CONNECTION_ATTEMPT_STARTED; -} -ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime, RakNetSmartPtr socket ) -{ - RakAssert(passwordDataLength <= 256); - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort); - - // Already connected? - if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) - return ALREADY_CONNECTED_TO_ENDPOINT; - - //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); - RequestedConnectionStruct *rcs = RakNet::OP_NEW(_FILE_AND_LINE_); - - rcs->systemAddress=systemAddress; - rcs->nextRequestTime=RakNet::GetTimeMS(); - rcs->requestsMade=0; - rcs->data=0; - rcs->extraData=extraData; - rcs->socketIndex=connectionSocketIndex; - rcs->actionToTake=RequestedConnectionStruct::CONNECT; - rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; - rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; - memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); - rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; - rcs->timeoutTime=timeoutTime; - rcs->socket=socket; - -#if LIBCAT_SECURITY==1 - if (!GenerateConnectionRequestChallenge(rcs,publicKey)) - return SECURITY_INITIALIZATION_FAILED; -#else - (void) publicKey; -#endif - - // Return false if already pending, else push on queue - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - // Not necessary - //RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; - } - } - requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - - return CONNECTION_ATTEMPT_STARTED; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ValidateRemoteSystemLookup(void) const -{ -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const -{ - if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return GetRemoteSystemFromGUID(systemIdentifier.rakNetGuid, onlyActive); - else - return GetRemoteSystemFromSystemAddress(systemIdentifier.systemAddress, calledFromNetworkThread, onlyActive); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const -{ - unsigned i; - - if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) - return 0; - - if (calledFromNetworkThread) - { - unsigned int index = GetRemoteSystemIndex(systemAddress); - if (index!=(unsigned int) -1) - { - if (onlyActive==false || remoteSystemList[ index ].isActive==true ) - { - RakAssert(remoteSystemList[index].systemAddress==systemAddress); - return remoteSystemList + index; - } - } - } - else - { - int deadConnectionIndex=-1; - - // Active connections take priority. But if there are no active connections, return the first systemAddress match found - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].systemAddress == systemAddress) - { - if ( remoteSystemList[ i ].isActive ) - return remoteSystemList + i; - else if (deadConnectionIndex==-1) - deadConnectionIndex=i; - } - } - - if (deadConnectionIndex!=-1 && onlyActive==false) - return remoteSystemList + deadConnectionIndex; - } - - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const -{ - if (guid==UNASSIGNED_RAKNET_GUID) - return 0; - - unsigned i; - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].guid == guid && (onlyActive==false || remoteSystemList[ i ].isActive)) - { - return remoteSystemList + i; - } - } - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize ) -{ - RakNet::BitStream bs((unsigned char*) data,byteSize,false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID guid; - bs.Read(guid); - RakNet::Time incomingTimestamp; - bs.Read(incomingTimestamp); - unsigned char doSecurity; - bs.Read(doSecurity); - -#if LIBCAT_SECURITY==1 - unsigned char doClientKey; - if (_using_security) - { - // Ignore message on bad state - if (doSecurity != 1 || !remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()) - return; - - // Validate client proof of key - unsigned char proof[cat::EasyHandshake::PROOF_BYTES]; - bs.ReadAlignedBytes(proof, sizeof(proof)); - if (!remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->ValidateProof(proof, sizeof(proof))) - { - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - - CAT_OBJCLR(remoteSystem->client_public_key); - - bs.Read(doClientKey); - - // Check if client wants to prove identity - if (doClientKey == 1) - { - // Read identity proof - unsigned char ident[cat::EasyHandshake::IDENTITY_BYTES]; - bs.ReadAlignedBytes(ident, sizeof(ident)); - - // If we are listening to these proofs, - if (_require_client_public_key) - { - // Validate client identity - if (!_server_handshake->VerifyInitiatorIdentity(remoteSystem->answer, ident, remoteSystem->client_public_key)) - { - RakNet::BitStream bitStream; - bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect - bitStream.Write((unsigned char)2); // Indicate client identity is invalid - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - } - - // Otherwise ignore the client public key - } - else - { - // If no client key was provided but it is required, - if (_require_client_public_key) - { - RakNet::BitStream bitStream; - bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect - bitStream.Write((unsigned char)1); // Indicate client identity is missing - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - } - } -#endif // LIBCAT_SECURITY - - unsigned char *password = bs.GetData()+BITS_TO_BYTES(bs.GetReadOffset()); - int passwordLength = byteSize - BITS_TO_BYTES(bs.GetReadOffset()); - if ( incomingPasswordLength != passwordLength || - memcmp( password, incomingPassword, incomingPasswordLength ) != 0 ) - { - CAT_AUDIT_PRINTF("AUDIT: Invalid password\n"); - // This one we only send once since we don't care if it arrives. - RakNet::BitStream bitStream; - bitStream.Write((MessageID)ID_INVALID_PASSWORD); - bitStream.Write(GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - - // OK - remoteSystem->connectMode=RemoteSystemStruct::HANDLING_CONNECTION_REQUEST; - - OnConnectionRequest( remoteSystem, incomingTimestamp ); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, RakNet::Time incomingTimestamp ) -{ - RakNet::BitStream bitStream; - bitStream.Write((MessageID)ID_CONNECTION_REQUEST_ACCEPTED); - bitStream.Write(remoteSystem->systemAddress); - SystemIndex systemIndex = (SystemIndex) GetIndexFromSystemAddress( remoteSystem->systemAddress, true ); - RakAssert(systemIndex!=65535); - bitStream.Write(systemIndex); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - bitStream.Write(ipList[i]); - bitStream.Write(incomingTimestamp); - bitStream.Write(RakNet::GetTime()); - - SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, remoteSystem->systemAddress, false, false, RakNet::GetTimeUS(), 0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) -{ - RakNet::BitStream temp( sizeof(unsigned char) ); - temp.Write( (MessageID)ID_DISCONNECTION_NOTIFICATION ); - if (performImmediate) - { - SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, RELIABLE_ORDERED, orderingChannel, systemAddress, false, false, RakNet::GetTimeUS(), 0); - RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(systemAddress, true, true); - rss->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; - } - else - { - SendBuffered((const char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, RELIABLE_ORDERED, orderingChannel, systemAddress, false, RemoteSystemStruct::DISCONNECT_ASAP, 0); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::GetNumberOfRemoteInitiatedConnections( void ) const -{ - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - unsigned short numberOfIncomingConnections; - numberOfIncomingConnections = 0; - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED && - (activeSystemList[i])->weInitiatedTheConnection==false - ) - { - numberOfIncomingConnections++; - } - } - return numberOfIncomingConnections; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct * RakPeer::AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSmartPtr incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ) -{ - RemoteSystemStruct * remoteSystem; - unsigned i,j,assignedIndex; - RakNet::TimeMS time = RakNet::GetTimeMS(); -#ifdef _DEBUG - RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); -#endif - - if (limitConnectionFrequencyFromTheSameIP) - { - if (IsLoopbackAddress(systemAddress,false)==false) - { - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if ( remoteSystemList[ i ].isActive==true && - remoteSystemList[ i ].systemAddress.EqualsExcludingPort(systemAddress) && - time >= remoteSystemList[ i ].connectionTime && - time - remoteSystemList[ i ].connectionTime < 100 - ) - { - // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots - // Ignore connection attempts if this IP address connected within the last 100 milliseconds - *thisIPConnectedRecently=true; - ValidateRemoteSystemLookup(); - return 0; - } - } - } - } - - // Don't use a different port than what we received on - bindingAddress.CopyPort(incomingRakNetSocket->boundAddress); - - *thisIPConnectedRecently=false; - for ( assignedIndex = 0; assignedIndex < maximumNumberOfPeers; assignedIndex++ ) - { - if ( remoteSystemList[ assignedIndex ].isActive==false ) - { - remoteSystem=remoteSystemList+assignedIndex; - ReferenceRemoteSystem(systemAddress, assignedIndex); - remoteSystem->MTUSize=defaultMTUSize; - remoteSystem->guid=guid; - remoteSystem->isActive = true; // This one line causes future incoming packets to go through the reliability layer - // Reserve this reliability layer for ourselves. - if (incomingMTU > remoteSystem->MTUSize) - remoteSystem->MTUSize=incomingMTU; - remoteSystem->reliabilityLayer.Reset(true, remoteSystem->MTUSize, useSecurity); - remoteSystem->reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); - remoteSystem->reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); - remoteSystem->reliabilityLayer.SetTimeoutTime(defaultTimeoutTime); - AddToActiveSystemList(assignedIndex); - if (incomingRakNetSocket->boundAddress==bindingAddress) - { - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - else - { - char str[256]; - bindingAddress.ToString(true,str); - // See if this is an internal IP address. - // If so, force binding on it so we reply on the same IP address as they sent to. - unsigned int ipListIndex, foundIndex=(unsigned int)-1; - - for (ipListIndex=0; ipListIndex < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ipListIndex++) - { - if (ipList[ipListIndex]==UNASSIGNED_SYSTEM_ADDRESS) - break; - - if (bindingAddress.EqualsExcludingPort(ipList[ipListIndex])) - { - foundIndex=ipListIndex; - break; - } - } - - // 06/26/09 Unconfirmed report that Vista firewall blocks the reply if we force a binding - // For now use the incoming socket only - // Originally this code was to force a machine with multiple IP addresses to reply back on the IP - // that the datagram came in on - if (1 || foundIndex==(unsigned int)-1) - { - // Must not be an internal LAN address. Just use whatever socket it came in on - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - else - { - // Force binding - unsigned int socketListIndex; - for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) - { - if (socketList[socketListIndex]->boundAddress==bindingAddress) - { - // Force binding with existing socket - remoteSystem->rakNetSocket=socketList[socketListIndex]; - break; - } - } - - if (socketListIndex==socketList.Size()) - { - char ipListFoundIndexStr[128]; - ipList[foundIndex].ToString(false,str); - - // Force binding with new socket - RakNetSmartPtr rns(RakNet::OP_NEW(_FILE_AND_LINE_)); - if (incomingRakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2==0) - rns->s = (unsigned int) SocketLayer::CreateBoundSocket( bindingAddress.GetPort(), true, ipListFoundIndexStr, 0, incomingRakNetSocket->extraSocketOptions, incomingRakNetSocket->socketFamily ); - else - rns->s = (unsigned int) SocketLayer::CreateBoundSocket_PS3Lobby( bindingAddress.GetPort(), true, ipListFoundIndexStr, incomingRakNetSocket->socketFamily ); - if ((SOCKET)rns->s==(SOCKET)-1) - { - // Can't bind. Just use whatever socket it came in on - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - else - { - rns->boundAddress=bindingAddress; - rns->remotePortRakNetWasStartedOn_PS3_PSP2=incomingRakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2; - rns->extraSocketOptions=incomingRakNetSocket->extraSocketOptions; - rns->userConnectionSocketIndex=(unsigned int)-1; - socketList.Push(rns, _FILE_AND_LINE_ ); - remoteSystem->rakNetSocket=rns; - - RakPeerAndIndex rpai; - rpai.remotePortRakNetWasStartedOn_PS3=rns->remotePortRakNetWasStartedOn_PS3_PSP2; - rpai.extraSocketOptions=rns->extraSocketOptions; - rpai.s=rns->s; - rpai.rakPeer=this; -#ifdef _WIN32 - int highPriority=THREAD_PRIORITY_ABOVE_NORMAL; -#else - int highPriority=-10; -#endif - -//#if !defined(_XBOX) && !defined(X360) - highPriority=0; -//#endif - - - - - - - - RakNet::RakThread::Create(RecvFromLoop, &rpai, highPriority); - - RakSleep(10); - - - /* -#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - if (threadSleepTimer>0) - { - rns->recvEvent=CreateEvent(0,FALSE,FALSE,0); - WSAEventSelect(rns->s,rns->recvEvent,FD_READ); - } -#endif - */ - } - } - } - } - - for ( j = 0; j < (unsigned) PING_TIMES_ARRAY_SIZE; j++ ) - { - remoteSystem->pingAndClockDifferential[ j ].pingTime = 65535; - remoteSystem->pingAndClockDifferential[ j ].clockDifferential = 0; - } - - remoteSystem->connectMode=connectionMode; - remoteSystem->pingAndClockDifferentialWriteIndex = 0; - remoteSystem->lowestPing = 65535; - remoteSystem->nextPingTime = 0; // Ping immediately - remoteSystem->weInitiatedTheConnection = false; - remoteSystem->connectionTime = time; - remoteSystem->myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystem->lastReliableSend=time; - -#ifdef _DEBUG - int indexLoopupCheck=GetIndexFromSystemAddress( systemAddress, true ); - if ((int) indexLoopupCheck!=(int) assignedIndex) - { - RakAssert((int) indexLoopupCheck==(int) assignedIndex); - } -#endif - - return remoteSystem; - } - } - - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Adjust the first four bytes (treated as unsigned int) of the pointer -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const -{ -#ifdef _DEBUG - RakAssert( IsActive() ); - RakAssert( data ); -#endif - - RakNet::BitStream timeBS( data, sizeof(RakNet::Time), false); - RakNet::Time encodedTimestamp; - timeBS.Read(encodedTimestamp); - - encodedTimestamp = encodedTimestamp - GetBestClockDifferential( systemAddress ); - timeBS.SetWriteOffset(0); - timeBS.Write(encodedTimestamp); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm -RakNet::Time RakPeer::GetBestClockDifferential( const SystemAddress systemAddress ) const -{ - int counter, lowestPingSoFar; - RakNet::Time clockDifferential; - RemoteSystemStruct *remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - - if ( remoteSystem == 0 ) - return 0; - - lowestPingSoFar = 65535; - - clockDifferential = 0; - - for ( counter = 0; counter < PING_TIMES_ARRAY_SIZE; counter++ ) - { - if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime == 65535 ) - break; - - if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime < lowestPingSoFar ) - { - clockDifferential = remoteSystem->pingAndClockDifferential[ counter ].clockDifferential; - lowestPingSoFar = remoteSystem->pingAndClockDifferential[ counter ].pingTime; - } - } - - return clockDifferential; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::RemoteSystemLookupHashIndex(const SystemAddress &sa) const -{ - return SystemAddress::ToInteger(sa) % ((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex) -{ -// #ifdef _DEBUG -// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// RakAssert(hashIndex==remoteSystemIndex); -// } -// } -// #endif - - - SystemAddress oldAddress = remoteSystemList[remoteSystemListIndex].systemAddress; - if (oldAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - // The system might be active if rerouting -// RakAssert(remoteSystemList[remoteSystemListIndex].isActive==false); - - // Remove the reference if the reference is pointing to this inactive system - if (GetRemoteSystem(oldAddress)==&remoteSystemList[remoteSystemListIndex]) - DereferenceRemoteSystem(oldAddress); - } - DereferenceRemoteSystem(sa); - -// #ifdef _DEBUG -// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// if (hashIndex!=remoteSystemIndex) -// { -// RakAssert(hashIndex==remoteSystemIndex); -// } -// } -// } -// #endif - - - remoteSystemList[remoteSystemListIndex].systemAddress=sa; - - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *rsi; - rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); - if (remoteSystemLookup[hashIndex]==0) - { - rsi->next=0; - rsi->index=remoteSystemListIndex; - remoteSystemLookup[hashIndex]=rsi; - } - else - { - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - while (cur->next!=0) - { - cur=cur->next; - } - - rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); - rsi->next=0; - rsi->index=remoteSystemListIndex; - cur->next=rsi; - } - -// #ifdef _DEBUG -// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// RakAssert(hashIndex==remoteSystemIndex); -// } -// } -// #endif - - - RakAssert(GetRemoteSystemIndex(sa)==remoteSystemListIndex); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DereferenceRemoteSystem(const SystemAddress &sa) -{ - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - RemoteSystemIndex *last = 0; - while (cur!=0) - { - if (remoteSystemList[cur->index].systemAddress==sa) - { - if (last==0) - { - remoteSystemLookup[hashIndex]=cur->next; - } - else - { - last->next=cur->next; - } - remoteSystemIndexPool.Release(cur,_FILE_AND_LINE_); - break; - } - last=cur; - cur=cur->next; - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetRemoteSystemIndex(const SystemAddress &sa) const -{ - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - while (cur!=0) - { - if (remoteSystemList[cur->index].systemAddress==sa) - return cur->index; - cur=cur->next; - } - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct* RakPeer::GetRemoteSystem(const SystemAddress &sa) const -{ - unsigned int remoteSystemIndex = GetRemoteSystemIndex(sa); - if (remoteSystemIndex==(unsigned int)-1) - return 0; - return remoteSystemList + remoteSystemIndex; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearRemoteSystemLookup(void) -{ - remoteSystemIndexPool.Clear(_FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(remoteSystemLookup,_FILE_AND_LINE_); - remoteSystemLookup=0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToActiveSystemList(unsigned int remoteSystemListIndex) -{ - activeSystemList[activeSystemListSize++]=remoteSystemList+remoteSystemListIndex; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromActiveSystemList(const SystemAddress &sa) -{ - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - RemoteSystemStruct *rss=activeSystemList[i]; - if (rss->systemAddress==sa) - { - activeSystemList[i]=activeSystemList[activeSystemListSize-1]; - activeSystemListSize--; - return; - } - } - RakAssert("activeSystemList invalid, entry not found in RemoveFromActiveSystemList. Ensure that AddToActiveSystemList and RemoveFromActiveSystemList are called by the same thread." && 0); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/* -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::LookupIndexUsingHashIndex(const SystemAddress &sa) const -{ - unsigned int scanCount=0; - unsigned int index = RemoteSystemLookupHashIndex(sa); - if (remoteSystemLookup[index].index==(unsigned int)-1) - return (unsigned int) -1; - while (remoteSystemList[remoteSystemLookup[index].index].systemAddress!=sa) - { - if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - index=0; - if (++scanCount>(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - return (unsigned int) -1; - if (remoteSystemLookup[index].index==-1) - return (unsigned int) -1; - } - return index; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const -{ - unsigned int index = LookupIndexUsingHashIndex(sa); - if (index!=(unsigned int) -1) - { - return remoteSystemLookup[index].index; - } - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const -{ -// unsigned int collisionCount=0; - unsigned int index = RemoteSystemLookupHashIndex(sa); - while (remoteSystemLookup[index].index!=(unsigned int)-1) - { - if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - index=0; -// collisionCount++; - } -// printf("%i collisions. Using index %i\n", collisionCount, index); - return index; -} -*/ -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const -{ - if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return systemIdentifier.rakNetGuid==myGuid; - - for (int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS; i++) - { - if (matchPort) - { - if (ipList[i]==systemIdentifier.systemAddress) - return true; - } - else - { - if (ipList[i].EqualsExcludingPort(systemIdentifier.systemAddress)) - return true; - } - } - - return (matchPort==true && systemIdentifier.systemAddress==firstExternalID) || - (matchPort==false && systemIdentifier.systemAddress.EqualsExcludingPort(firstExternalID)); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetLoopbackAddress(void) const -{ - - return ipList[0]; - - - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AllowIncomingConnections(void) const -{ - return GetNumberOfRemoteInitiatedConnections() < GetMaximumIncomingConnections(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PingInternal( const SystemAddress target, bool performImmediate, PacketReliability reliability ) -{ - if ( IsActive() == false ) - return ; - - RakNet::BitStream bitStream(sizeof(unsigned char)+sizeof(RakNet::Time)); - bitStream.Write((MessageID)ID_CONNECTED_PING); - bitStream.Write(RakNet::GetTime()); - if (performImmediate) - SendImmediate( (char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, reliability, 0, target, false, false, RakNet::GetTimeUS(), 0 ); - else - Send( &bitStream, IMMEDIATE_PRIORITY, reliability, 0, target, false ); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) -{ -#ifdef _DEBUG - RakAssert(orderingChannel < 32); -#endif - - if (systemIdentifier.IsUndefined()) - return; - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - SystemAddress target; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - target=systemIdentifier.systemAddress; - } - else - { - target=GetSystemAddressFromGuid(systemIdentifier.rakNetGuid); - } - - if (target!=UNASSIGNED_SYSTEM_ADDRESS && performImmediate) - target.FixForIPVersion(socketList[0]->boundAddress); - - if (sendDisconnectionNotification) - { - NotifyAndFlagForShutdown(target, performImmediate, orderingChannel, disconnectionNotificationPriority); - } - else - { - if (performImmediate) - { - unsigned int index = GetRemoteSystemIndex(target); - if (index!=(unsigned int) -1) - { - if ( remoteSystemList[index].isActive ) - { - RemoveFromActiveSystemList(target); - - // Found the index to stop - remoteSystemList[index].isActive = false; - - remoteSystemList[index].guid=UNASSIGNED_RAKNET_GUID; - - // Reserve this reliability layer for ourselves - //remoteSystemList[ remoteSystemLookup[index].index ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; - - // Clear any remaining messages - remoteSystemList[index].reliabilityLayer.Reset(false, remoteSystemList[index].MTUSize, false); - - // Not using this socket - remoteSystemList[index].rakNetSocket.SetNull(); - } - } - } - else - { - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_CLOSE_CONNECTION; - bcs->systemIdentifier=target; - bcs->data=0; - bcs->orderingChannel=orderingChannel; - bcs->priority=disconnectionNotificationPriority; - bufferedCommands.Push(bcs); - } - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendBuffered( const char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) -{ - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = (char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES(numberOfBitsToSend), _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy - if (bcs->data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - return; - } - memcpy(bcs->data, data, (size_t) BITS_TO_BYTES(numberOfBitsToSend)); - bcs->numberOfBitsToSend=numberOfBitsToSend; - bcs->priority=priority; - bcs->reliability=reliability; - bcs->orderingChannel=orderingChannel; - bcs->systemIdentifier=systemIdentifier; - bcs->broadcast=broadcast; - bcs->connectionMode=connectionMode; - bcs->receipt=receipt; - bcs->command=BufferedCommandStruct::BCS_SEND; - bufferedCommands.Push(bcs); - - if (priority==IMMEDIATE_PRIORITY) - { - // Forces pending sends to go out now, rather than waiting to the next update interval - quitAndDataEvents.SetEvent(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendBufferedList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) -{ - BufferedCommandStruct *bcs; - unsigned int totalLength=0; - unsigned int lengthOffset; - int i; - for (i=0; i < numParameters; i++) - { - if (lengths[i]>0) - totalLength+=lengths[i]; - } - if (totalLength==0) - return; - - char *dataAggregate; - dataAggregate = (char*) rakMalloc_Ex( (size_t) totalLength, _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy - if (dataAggregate==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return; - } - for (i=0, lengthOffset=0; i < numParameters; i++) - { - if (lengths[i]>0) - { - memcpy(dataAggregate+lengthOffset, data[i], lengths[i]); - lengthOffset+=lengths[i]; - } - } - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback(dataAggregate,totalLength); - rakFree_Ex(dataAggregate,_FILE_AND_LINE_); - return; - } - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = dataAggregate; - bcs->numberOfBitsToSend=BYTES_TO_BITS(totalLength); - bcs->priority=priority; - bcs->reliability=reliability; - bcs->orderingChannel=orderingChannel; - bcs->systemIdentifier=systemIdentifier; - bcs->broadcast=broadcast; - bcs->connectionMode=connectionMode; - bcs->receipt=receipt; - bcs->command=BufferedCommandStruct::BCS_SEND; - bufferedCommands.Push(bcs); - - if (priority==IMMEDIATE_PRIORITY) - { - // Forces pending sends to go out now, rather than waiting to the next update interval - quitAndDataEvents.SetEvent(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::SendImmediate( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, RakNet::TimeUS currentTime, uint32_t receipt ) -{ - unsigned *sendList; - unsigned sendListSize; - bool callerDataAllocationUsed; - unsigned int remoteSystemIndex, sendListIndex; // Iterates into the list of remote systems -// unsigned numberOfBytesUsed = (unsigned) BITS_TO_BYTES(numberOfBitsToSend); - callerDataAllocationUsed=false; - - sendListSize=0; - - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - remoteSystemIndex=GetIndexFromSystemAddress( systemIdentifier.systemAddress, true ); - else if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - remoteSystemIndex=GetSystemIndexFromGuid(systemIdentifier.rakNetGuid); - else - remoteSystemIndex=(unsigned int) -1; - - // 03/06/06 - If broadcast is false, use the optimized version of GetIndexFromSystemAddress - if (broadcast==false) - { - if (remoteSystemIndex==(unsigned int) -1) - { -#ifdef _DEBUG -// int debugIndex = GetRemoteSystemIndex(systemIdentifier.systemAddress); -#endif - return false; - } - - - sendList=(unsigned *)alloca(sizeof(unsigned)); - - - - - if (remoteSystemList[remoteSystemIndex].isActive && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ON_NO_ACK) - { - sendList[0]=remoteSystemIndex; - sendListSize=1; - } - } - else - { - - //sendList=(unsigned *)alloca(sizeof(unsigned)*remoteSystemListSize); - sendList=(unsigned *)alloca(sizeof(unsigned)*maximumNumberOfPeers); - - - - - - // remoteSystemList in network thread - unsigned int idx; - for ( idx = 0; idx < maximumNumberOfPeers; idx++ ) - { - if (remoteSystemIndex!=(unsigned int) -1 && idx==remoteSystemIndex) - continue; - - if ( remoteSystemList[ idx ].isActive && remoteSystemList[ idx ].systemAddress != UNASSIGNED_SYSTEM_ADDRESS ) - sendList[sendListSize++]=idx; - } - } - - if (sendListSize==0) - { - - - - return false; - } - - for (sendListIndex=0; sendListIndex < sendListSize; sendListIndex++) - { - // Send may split the packet and thus deallocate data. Don't assume data is valid if we use the callerAllocationData - bool useData = useCallerDataAllocation && callerDataAllocationUsed==false && sendListIndex+1==sendListSize; - remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( data, numberOfBitsToSend, priority, reliability, orderingChannel, useData==false, remoteSystemList[sendList[sendListIndex]].MTUSize, currentTime, receipt ); - if (useData) - callerDataAllocationUsed=true; - - if (reliability==RELIABLE || - reliability==RELIABLE_ORDERED || - reliability==RELIABLE_SEQUENCED || - reliability==RELIABLE_WITH_ACK_RECEIPT || - reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT -// || -// reliability==RELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - remoteSystemList[sendList[sendListIndex]].lastReliableSend=(RakNet::TimeMS)(currentTime/(RakNet::TimeUS)1000); - } - - - - - - // Return value only meaningful if true was passed for useCallerDataAllocation. Means the reliability layer used that data copy, so the caller should not deallocate it - return callerDataAllocationUsed; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ResetSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - sendReceiptSerial=1; - sendReceiptSerialMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::OnConnectedPong(RakNet::Time sendPingTime, RakNet::Time sendPongTime, RemoteSystemStruct *remoteSystem) -{ - RakNet::Time ping; -// RakNet::TimeMS lastPing; - RakNet::Time time = RakNet::GetTime(); // Update the time value to be accurate - if (time > sendPingTime) - ping = time - sendPingTime; - else - ping=0; - -// lastPing = remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime; - - remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime = ( unsigned short ) ping; - // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm - // Divide each integer by 2, rather than the sum by 2, to prevent overflow - remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].clockDifferential = sendPongTime - ( time/2 + sendPingTime/2 ); - - if ( remoteSystem->lowestPing == (unsigned short)-1 || remoteSystem->lowestPing > (int) ping ) - remoteSystem->lowestPing = (unsigned short) ping; - - if ( ++( remoteSystem->pingAndClockDifferentialWriteIndex ) == (RakNet::Time) PING_TIMES_ARRAY_SIZE ) - remoteSystem->pingAndClockDifferentialWriteIndex = 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBufferedCommands(void) -{ - BufferedCommandStruct *bcs; - - while ((bcs=bufferedCommands.Pop())!=0) - { - if (bcs->data) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - } - bufferedCommands.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBufferedPackets(void) -{ - RecvFromStruct *bcs; - - while ((bcs=bufferedPackets.Pop())!=0) - { - bufferedPackets.Deallocate(bcs, _FILE_AND_LINE_); - } - bufferedPackets.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearSocketQueryOutput(void) -{ - socketQueryOutput.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearRequestedConnectionList(void) -{ - DataStructures::Queue freeQueue; - requestedConnectionQueueMutex.Lock(); - while (requestedConnectionQueue.Size()) - freeQueue.Push(requestedConnectionQueue.Pop(), _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - unsigned i; - for (i=0; i < freeQueue.Size(); i++) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: In ClearRequestedConnectionList(), Deleting freeQueue index %i client_handshake %x\n", i, freeQueue[i]->client_handshake); - RakNet::OP_DELETE(freeQueue[i]->client_handshake,_FILE_AND_LINE_); -#endif - RakNet::OP_DELETE(freeQueue[i], _FILE_AND_LINE_ ); - } -} -inline void RakPeer::AddPacketToProducer(RakNet::Packet *p) -{ - packetReturnMutex.Lock(); - packetReturnQueue.Push(p,_FILE_AND_LINE_); - packetReturnMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -union Buff6AndBuff8 -{ - unsigned char buff6[6]; - uint64_t buff8; -}; -uint64_t RakPeerInterface::Get64BitUniqueRandomNumber(void) -{ - // Mac address is a poor solution because you can't have multiple connections from the same system - - - - - - - - - - - - - -#if defined(_WIN32) - uint64_t g=RakNet::GetTimeUS(); - - RakNet::TimeUS lastTime, thisTime; - int j; - // Sleep a small random time, then use the last 4 bits as a source of randomness - for (j=0; j < 8; j++) - { - lastTime = RakNet::GetTimeUS(); - RakSleep(1); - RakSleep(0); - thisTime = RakNet::GetTimeUS(); - RakNet::TimeUS diff = thisTime-lastTime; - unsigned int diff4Bits = (unsigned int) (diff & 15); - diff4Bits <<= 32-4; - diff4Bits >>= j*4; - ((char*)&g)[j] ^= diff4Bits; - } - return g; - -#else - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_usec + tv.tv_sec * 1000000; -#endif -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GenerateGUID(void) -{ - myGuid.g=Get64BitUniqueRandomNumber(); - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// void RakNet::ProcessPortUnreachable( SystemAddress systemAddress, RakPeer *rakPeer ) -// { -// (void) binaryAddress; -// (void) port; -// (void) rakPeer; -// -// } -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -namespace RakNet { -bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, bool *isOfflineMessage, RakNet::TimeUS timeRead ) -{ - (void) timeRead; - RakPeer::RemoteSystemStruct *remoteSystem; - RakNet::Packet *packet; - unsigned i; - - - char str1[64]; - systemAddress.ToString(false, str1); - if (rakPeer->IsBanned( str1 )) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - RakNet::BitStream bs; - bs.Write((MessageID)ID_CONNECTION_BANNED); - bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - unsigned i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (char*) bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - - - - // The reason for all this is that the reliability layer has no way to tell between offline messages that arrived late for a player that is now connected, - // and a regular encoding. So I insert OFFLINE_MESSAGE_DATA_ID into the stream, the encoding of which is essentially impossible to hit by chance - if (length <=2) - { - *isOfflineMessage=true; - } - else if ( - ((unsigned char)data[0] == ID_UNCONNECTED_PING || - (unsigned char)data[0] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS) && - length == sizeof(unsigned char) + sizeof(RakNet::Time) + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(RakNet::Time), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ((unsigned char)data[0] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char) + sizeof(RakNet::TimeMS) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(RakNet::Time) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ( - (unsigned char)data[0] == ID_OUT_OF_BAND_INTERNAL && - (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ( - ( - (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_1 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_2 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_1 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_2 || - (unsigned char)data[0] == ID_CONNECTION_ATTEMPT_FAILED || - (unsigned char)data[0] == ID_NO_FREE_INCOMING_CONNECTIONS || - (unsigned char)data[0] == ID_CONNECTION_BANNED || - (unsigned char)data[0] == ID_ALREADY_CONNECTED || - (unsigned char)data[0] == ID_IP_RECENTLY_CONNECTED) && - (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if (((unsigned char)data[0] == ID_INCOMPATIBLE_PROTOCOL_VERSION&& - (size_t) length == sizeof(MessageID)*2 + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID))) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID)*2, OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else - { - *isOfflineMessage=false; - } - - if (*isOfflineMessage) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - // These are all messages from unconnected systems. Messages here can be any size, but are never processed from connected systems. - if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS - || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && length == sizeof(unsigned char)+sizeof(RakNet::Time)+sizeof(OFFLINE_MESSAGE_DATA_ID) ) - { - if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || - rakPeer->AllowIncomingConnections() ) // Open connections with players - { -// #if !defined(_XBOX) && !defined(X360) - RakNet::BitStream inBitStream( (unsigned char *) data, length, false ); - inBitStream.IgnoreBits(8); - RakNet::Time sendPingTime; - inBitStream.Read(sendPingTime); - - RakNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_UNCONNECTED_PONG); // Should be named ID_UNCONNECTED_PONG eventually - outBitStream.Write(sendPingTime); - outBitStream.Write(rakPeer->myGuid); - outBitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - - rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Lock(); - // They are connected, so append offline ping data - outBitStream.Write( (char*)rakPeer->offlinePingResponse.GetData(), rakPeer->offlinePingResponse.GetNumberOfBytesUsed() ); - rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Unlock(); - - unsigned i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*)outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), systemAddress); - - SocketLayer::SendTo( rakNetSocket->s, (const char*)outBitStream.GetData(), (unsigned int) outBitStream.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - packet=rakPeer->AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - packet->data[0]=data[0]; - packet->systemAddress = systemAddress; - packet->guid=UNASSIGNED_RAKNET_GUID; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); -// #endif - } - } - // UNCONNECTED MESSAGE Pong with no data. - else if ((unsigned char) data[ 0 ] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID) && (size_t) length < sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID)+MAX_OFFLINE_DATA_LENGTH) - { - packet=rakPeer->AllocPacket((unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(RakNet::Time)+sizeof(RakNet::TimeMS)), _FILE_AND_LINE_); - RakNet::BitStream bsIn((unsigned char*) data, length, false); - bsIn.IgnoreBytes(sizeof(unsigned char)); - RakNet::Time ping; - bsIn.Read(ping); - bsIn.Read(packet->guid); - - RakNet::BitStream bsOut((unsigned char*) packet->data, packet->length, false); - bsOut.ResetWritePointer(); - bsOut.Write((unsigned char)ID_UNCONNECTED_PONG); - RakNet::TimeMS pingMS=(RakNet::TimeMS)ping; - bsOut.Write(pingMS); - bsOut.WriteAlignedBytes( - (const unsigned char*)data+sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID), - length-sizeof(unsigned char)-sizeof(RakNet::Time)-RakNetGUID::size()-sizeof(OFFLINE_MESSAGE_DATA_ID) - ); - - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - else if ((unsigned char) data[ 0 ] == ID_OUT_OF_BAND_INTERNAL && - (size_t) length < MAX_OFFLINE_DATA_LENGTH+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size()) - { - unsigned int dataLength = (unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MessageID)); - RakAssert(dataLength<1024); - packet=rakPeer->AllocPacket(dataLength+1, _FILE_AND_LINE_); - RakAssert(packet->length<1024); - - RakNet::BitStream bs2((unsigned char*) data, length, false); - bs2.IgnoreBytes(sizeof(MessageID)); - bs2.Read(packet->guid); - - if (data[sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size()]==ID_ADVERTISE_SYSTEM) - { - packet->length--; - packet->bitSize=BYTES_TO_BITS(packet->length); - packet->data[0]=ID_ADVERTISE_SYSTEM; - memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)*2 + RakNetGUID::size(), dataLength-1); - } - else - { - packet->data[0]=ID_OUT_OF_BAND_INTERNAL; - memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size(), dataLength); - } - - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_1) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - RakNet::BitStream bsIn((unsigned char*) data,length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bsIn.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID serverGuid; - bsIn.Read(serverGuid); - unsigned char serverHasSecurity; - uint32_t cookie; - (void) cookie; - bsIn.Read(serverHasSecurity); - // Even if the server has security, it may not be required of us if we are in the security exception list - if (serverHasSecurity) - { - bsIn.Read(cookie); - } - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_2); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - if (serverHasSecurity) - bsOut.Write(cookie); - - unsigned i; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - RakPeer::RequestedConnectionStruct *rcs; - rcs=rakPeer->requestedConnectionQueue[i]; - if (rcs->systemAddress==systemAddress) - { - if (serverHasSecurity) - { -#if LIBCAT_SECURITY==1 - unsigned char public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; - bsIn.ReadAlignedBytes(public_key, sizeof(public_key)); - - if (rcs->publicKeyMode==PKM_ACCEPT_ANY_PUBLIC_KEY) - { - memcpy(rcs->remote_public_key, public_key, cat::EasyHandshake::PUBLIC_KEY_BYTES); - if (!rcs->client_handshake->Initialize(public_key) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Server passed a bad public key with PKM_ACCEPT_ANY_PUBLIC_KEY"); - return true; - } - } - - if (cat::SecureEqual(public_key, - rcs->remote_public_key, - cat::EasyHandshake::PUBLIC_KEY_BYTES)==false) - { - rakPeer->requestedConnectionQueueMutex.Unlock(); - CAT_AUDIT_PRINTF("AUDIT: Expected public key does not match what was sent by server -- Reporting back ID_PUBLIC_KEY_MISMATCH to user\n"); - - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_PUBLIC_KEY_MISMATCH; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=serverGuid; - rakPeer->AddPacketToProducer(packet); - return true; - } - - if (rcs->client_handshake==0) - { - // Message does not contain a challenge - // We might still pass if we are in the security exception list - bsOut.Write((unsigned char)0); - } - else - { - // Message contains a challenge - bsOut.Write((unsigned char)1); - // challenge - CAT_AUDIT_PRINTF("AUDIT: Sending challenge\n"); - bsOut.WriteAlignedBytes((const unsigned char*) rcs->handshakeChallenge,cat::EasyHandshake::CHALLENGE_BYTES); - } -#else // LIBCAT_SECURITY - // Message does not contain a challenge - bsOut.Write((unsigned char)0); -#endif // LIBCAT_SECURITY - } - else - { - // Server does not need security -#if LIBCAT_SECURITY==1 - if (rcs->client_handshake!=0) - { - rakPeer->requestedConnectionQueueMutex.Unlock(); - CAT_AUDIT_PRINTF("AUDIT: Security disabled by server but we expected security (indicated by client_handshake not null) so failing!\n"); - - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_OUR_SYSTEM_REQUIRES_SECURITY; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=serverGuid; - rakPeer->AddPacketToProducer(packet); - return true; - } -#endif // LIBCAT_SECURITY - - } - - uint16_t mtu; - bsIn.Read(mtu); - - // Binding address - bsOut.Write(rcs->systemAddress); - rakPeer->requestedConnectionQueueMutex.Unlock(); - // MTU - bsOut.Write(mtu); - // Our guid - bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), rcs->systemAddress); - - SocketLayer::SendTo( rakPeer->socketList[rcs->socketIndex]->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), rcs->systemAddress, rakPeer->socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, rakPeer->socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - } - rakPeer->requestedConnectionQueueMutex.Unlock(); - } - else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_2) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - RakNet::BitStream bs((unsigned char*) data,length,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID guid; - bs.Read(guid); - SystemAddress bindingAddress; - bool b = bs.Read(bindingAddress); - RakAssert(b); - uint16_t mtu; - b=bs.Read(mtu); - RakAssert(b); - bool doSecurity=false; - b=bs.Read(doSecurity); - RakAssert(b); - -#if LIBCAT_SECURITY==1 - char answer[cat::EasyHandshake::ANSWER_BYTES]; - CAT_AUDIT_PRINTF("AUDIT: Got ID_OPEN_CONNECTION_REPLY_2 and given doSecurity=%i\n", (int)doSecurity); - if (doSecurity) - { - CAT_AUDIT_PRINTF("AUDIT: Reading cookie and public key\n"); - bs.ReadAlignedBytes((unsigned char*) answer, sizeof(answer)); - } - cat::ClientEasyHandshake *client_handshake=0; -#endif // LIBCAT_SECURITY - - RakPeer::RequestedConnectionStruct *rcs; - bool unlock=true; - unsigned i; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - rcs=rakPeer->requestedConnectionQueue[i]; - - - if (rcs->systemAddress==systemAddress) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: System address matches an entry in the requestedConnectionQueue and doSecurity=%i\n", (int)doSecurity); - if (doSecurity) - { - if (rcs->client_handshake==0) - { - CAT_AUDIT_PRINTF("AUDIT: Server wants security but we didn't set a public key -- Reporting back ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY to user\n"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - - packet=rakPeer->AllocPacket(2, _FILE_AND_LINE_); - packet->data[ 0 ] = ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY; // Attempted a connection and couldn't - packet->data[ 1 ] = 0; // Indicate server public key is missing - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - return true; - } - - CAT_AUDIT_PRINTF("AUDIT: Looks good, preparing to send challenge to server! client_handshake = %x\n", client_handshake); - } - -#endif // LIBCAT_SECURITY - - rakPeer->requestedConnectionQueueMutex.Unlock(); - unlock=false; - - RakAssert(rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT); - // You might get this when already connected because of cross-connections - bool thisIPConnectedRecently=false; - remoteSystem=rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - if (remoteSystem==0) - { - if (rcs->socket.IsNull()) - { - remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); - } - else - { - remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rcs->socket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); - } - } - - // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots - // Ignore connection attempts if this IP address connected within the last 100 milliseconds - if (thisIPConnectedRecently==false) - { - // Don't check GetRemoteSystemFromGUID, server will verify - if (remoteSystem) - { - // Move pointer from RequestedConnectionStruct to RemoteSystemStruct -#if LIBCAT_SECURITY==1 - cat::u8 ident[cat::EasyHandshake::IDENTITY_BYTES]; - bool doIdentity = false; - - if (rcs->client_handshake) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer\n"); - if (rcs->publicKeyMode == PKM_USE_TWO_WAY_AUTHENTICATION) - { - if (!rcs->client_handshake->ProcessAnswerWithIdentity(answer, ident, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - - return true; - } - - doIdentity = true; - } - else - { - if (!rcs->client_handshake->ProcessAnswer(answer, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - - return true; - } - } - CAT_AUDIT_PRINTF("AUDIT: Success!\n"); - - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - } -#endif // LIBCAT_SECURITY - - remoteSystem->weInitiatedTheConnection=true; - remoteSystem->connectMode=RakPeer::RemoteSystemStruct::REQUESTED_CONNECTION; - if (rcs->timeoutTime!=0) - remoteSystem->reliabilityLayer.SetTimeoutTime(rcs->timeoutTime); - - RakNet::BitStream temp; - temp.Write( (MessageID)ID_CONNECTION_REQUEST); - temp.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - temp.Write(RakNet::GetTime()); - -#if LIBCAT_SECURITY==1 - temp.Write((unsigned char)(doSecurity ? 1 : 0)); - - if (doSecurity) - { - unsigned char proof[32]; - remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->GenerateProof(proof, sizeof(proof)); - temp.WriteAlignedBytes(proof, sizeof(proof)); - - temp.Write((unsigned char)(doIdentity ? 1 : 0)); - - if (doIdentity) - { - temp.WriteAlignedBytes(ident, sizeof(ident)); - } - } -#else - temp.Write((unsigned char)0); -#endif // LIBCAT_SECURITY - - if ( rcs->outgoingPasswordLength > 0 ) - temp.Write( ( char* ) rcs->outgoingPassword, rcs->outgoingPasswordLength ); - - rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, timeRead, 0 ); - } - else - { - // Failed, no connections available anymore - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - } - } - - rakPeer->requestedConnectionQueueMutex.Lock(); - for (unsigned int k=0; k < rakPeer->requestedConnectionQueue.Size(); k++) - { - if (rakPeer->requestedConnectionQueue[k]->systemAddress==systemAddress) - { - rakPeer->requestedConnectionQueue.RemoveAtIndex(k); - break; - } - } - rakPeer->requestedConnectionQueueMutex.Unlock(); - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Deleting client_handshake object %x and rcs->client_handshake object %x\n", client_handshake, rcs->client_handshake); - RakNet::OP_DELETE(client_handshake,_FILE_AND_LINE_); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif // LIBCAT_SECURITY - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - - break; - } - } - - if (unlock) - rakPeer->requestedConnectionQueueMutex.Unlock(); - - return true; - - } - else if ((unsigned char)(data)[0] == (MessageID)ID_CONNECTION_ATTEMPT_FAILED || - (unsigned char)(data)[0] == (MessageID)ID_NO_FREE_INCOMING_CONNECTIONS || - (unsigned char)(data)[0] == (MessageID)ID_CONNECTION_BANNED || - (unsigned char)(data)[0] == (MessageID)ID_ALREADY_CONNECTED || - (unsigned char)(data)[0] == (MessageID)ID_INVALID_PASSWORD || - (unsigned char)(data)[0] == (MessageID)ID_IP_RECENTLY_CONNECTED || - (unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) - { - - RakNet::BitStream bs((unsigned char*) data,length,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - if ((unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) - bs.IgnoreBytes(sizeof(unsigned char)); - - RakNetGUID guid; - bs.Read(guid); - - RakPeer::RequestedConnectionStruct *rcs; - bool connectionAttemptCancelled=false; - unsigned i; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - rcs=rakPeer->requestedConnectionQueue[i]; - if (rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT && rcs->systemAddress==systemAddress) - { - connectionAttemptCancelled=true; - rakPeer->requestedConnectionQueue.RemoveAtIndex(i); - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Connection attempt canceled so deleting rcs->client_handshake object %x\n", rcs->client_handshake); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif // LIBCAT_SECURITY - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - break; - } - } - - rakPeer->requestedConnectionQueueMutex.Unlock(); - - if (connectionAttemptCancelled) - { - // Tell user of connection attempt failed - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = data[0]; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - } - } - else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_1 && length > (int) (1+sizeof(OFFLINE_MESSAGE_DATA_ID))) - {/* - static int x = 0; - ++x; - - SystemAddress *addr = (SystemAddress*)&systemAddress; - addr->binaryAddress += x;*/ - - unsigned int i; - //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); - char remoteProtocol=data[1+sizeof(OFFLINE_MESSAGE_DATA_ID)]; - if (remoteProtocol!=RAKNET_PROTOCOL_VERSION) - { - RakNet::BitStream bs; - bs.Write((MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION); - bs.Write((unsigned char)RAKNET_PROTOCOL_VERSION); - bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*)bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - - SocketLayer::SendTo( rakNetSocket->s, (char*)bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - return true; - } - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_OPEN_CONNECTION_REPLY_1); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); -#if LIBCAT_SECURITY==1 - if (rakPeer->_using_security) - { - bsOut.Write((unsigned char) 1); // HasCookie Yes - // Write cookie - uint32_t cookie = rakPeer->_cookie_jar->Generate(&systemAddress.address,sizeof(systemAddress.address)); - CAT_AUDIT_PRINTF("AUDIT: Writing cookie %i to %i:%i\n", cookie, systemAddress); - bsOut.Write(cookie); - // Write my public key - bsOut.WriteAlignedBytes((const unsigned char *) rakPeer->my_public_key,sizeof(rakPeer->my_public_key)); - } - else -#endif // LIBCAT_SECURITY - bsOut.Write((unsigned char) 0); // HasCookie No - - // MTU - bsOut.Write((uint16_t) (length+UDP_HEADER_SIZE)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - } - else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_2) - { - SystemAddress bindingAddress; - RakNetGUID guid; - RakNet::BitStream bsOut; - RakNet::BitStream bs((unsigned char*) data, length, false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - - bool requiresSecurityOfThisClient=false; -#if LIBCAT_SECURITY==1 - char remoteHandshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; - - if (rakPeer->_using_security) - { - char str1[64]; - systemAddress.ToString(false, str1); - requiresSecurityOfThisClient=rakPeer->IsInSecurityExceptionList(str1)==false; - - uint32_t cookie; - bs.Read(cookie); - CAT_AUDIT_PRINTF("AUDIT: Got cookie %i from %i:%i\n", cookie, systemAddress); - if (rakPeer->_cookie_jar->Verify(&systemAddress.address,sizeof(systemAddress.address), cookie)==false) - { - return true; - } - CAT_AUDIT_PRINTF("AUDIT: Cookie good!\n"); - - unsigned char clientWroteChallenge; - bs.Read(clientWroteChallenge); - - if (requiresSecurityOfThisClient==true && clientWroteChallenge==0) - { - // Fail, client doesn't support security, and it is required - return true; - } - - if (clientWroteChallenge) - { - bs.ReadAlignedBytes((unsigned char*) remoteHandshakeChallenge, cat::EasyHandshake::CHALLENGE_BYTES); -#ifdef CAT_AUDIT - printf("AUDIT: RECV CHALLENGE "); - for (int ii = 0; ii < sizeof(remoteHandshakeChallenge); ++ii) - { - printf("%02x", (cat::u8)remoteHandshakeChallenge[ii]); - } - printf("\n"); -#endif - } - } -#endif // LIBCAT_SECURITY - - bs.Read(bindingAddress); - uint16_t mtu; - bs.Read(mtu); - bs.Read(guid); - - RakPeer::RemoteSystemStruct *rssFromSA = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - bool IPAddrInUse = rssFromSA != 0 && rssFromSA->isActive; - RakPeer::RemoteSystemStruct *rssFromGuid = rakPeer->GetRemoteSystemFromGUID(guid, true); - bool GUIDInUse = rssFromGuid != 0 && rssFromGuid->isActive; - - // IPAddrInUse, GuidInUse, outcome - // TRUE, , TRUE , ID_OPEN_CONNECTION_REPLY if they are the same, else ID_ALREADY_CONNECTED - // FALSE, , TRUE , ID_ALREADY_CONNECTED (someone else took this guid) - // TRUE, , FALSE , ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) - // FALSE , FALSE , Allow connection - - int outcome; - if (IPAddrInUse & GUIDInUse) - { - if (rssFromSA==rssFromGuid && rssFromSA->connectMode==RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER) - { - // ID_OPEN_CONNECTION_REPLY if they are the same - outcome=1; - } - else - { - // ID_ALREADY_CONNECTED (restarted raknet, connected again from same ip, plus someone else took this guid) - outcome=2; - } - } - else if (IPAddrInUse==false && GUIDInUse==true) - { - // ID_ALREADY_CONNECTED (someone else took this guid) - outcome=3; - } - else if (IPAddrInUse==true && GUIDInUse==false) - { - // ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) - outcome=4; - } - else - { - // Allow connection - outcome=0; - } - - RakNet::BitStream bsAnswer; - bsAnswer.Write((MessageID)ID_OPEN_CONNECTION_REPLY_2); - bsAnswer.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsAnswer.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - bsAnswer.Write(systemAddress); - bsAnswer.Write(mtu); - bsAnswer.Write(requiresSecurityOfThisClient); - - if (outcome==1) - { - // Duplicate connection request packet from packetloss - // Send back the same answer -#if LIBCAT_SECURITY==1 - if (requiresSecurityOfThisClient) - { - CAT_AUDIT_PRINTF("AUDIT: Resending public key and answer from packetloss. Sending ID_OPEN_CONNECTION_REPLY_2\n"); - bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); - } -#endif // LIBCAT_SECURITY - - unsigned int i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - else if (outcome!=0) - { - bsOut.Write((MessageID)ID_ALREADY_CONNECTED); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(guid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - - if (rakPeer->AllowIncomingConnections()==false) - { - bsOut.Write((MessageID)ID_NO_FREE_INCOMING_CONNECTIONS); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(guid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - - bool thisIPConnectedRecently=false; - rssFromSA = rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, requiresSecurityOfThisClient); - - if (thisIPConnectedRecently==true) - { - bsOut.Write((MessageID)ID_IP_RECENTLY_CONNECTED); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(guid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - -#if LIBCAT_SECURITY==1 - if (requiresSecurityOfThisClient) - { - CAT_AUDIT_PRINTF("AUDIT: Writing public key. Sending ID_OPEN_CONNECTION_REPLY_2\n"); - if (rakPeer->_server_handshake->ProcessChallenge(remoteHandshakeChallenge, rssFromSA->answer, rssFromSA->reliabilityLayer.GetAuthenticatedEncryption() )) - { - CAT_AUDIT_PRINTF("AUDIT: Challenge good!\n"); - // Keep going to OK block - } - else - { - CAT_AUDIT_PRINTF("AUDIT: Challenge BAD!\n"); - - // Unassign this remote system - rakPeer->DereferenceRemoteSystem(systemAddress); - return true; - } - - bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); - } -#endif // LIBCAT_SECURITY - - unsigned int i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - } - return true; - } - - return false; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead, BitStream &updateBitStream ) -{ - ProcessNetworkPacket(systemAddress,data,length,rakPeer,rakPeer->socketList[0],timeRead, updateBitStream); -} -void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead, BitStream &updateBitStream ) -{ -#if LIBCAT_SECURITY==1 -#ifdef CAT_AUDIT - printf("AUDIT: RECV "); - for (int ii = 0; ii < length; ++ii) - { - printf("%02x", (cat::u8)data[ii]); - } - printf("\n"); -#endif -#endif // LIBCAT_SECURITY - - RakAssert(systemAddress.GetPort()); - bool isOfflineMessage; - if (ProcessOfflineNetworkPacket(systemAddress, data, length, rakPeer, rakNetSocket, &isOfflineMessage, timeRead)) - { - return; - } - -// RakNet::Packet *packet; - RakPeer::RemoteSystemStruct *remoteSystem; - - // See if this datagram came from a connected system - remoteSystem = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - if ( remoteSystem ) - { - // Handle regular incoming data - // HandleSocketReceiveFromConnectedPlayer is only safe to be called from the same thread as Update, which is this thread - if ( isOfflineMessage==false) - { - remoteSystem->reliabilityLayer.HandleSocketReceiveFromConnectedPlayer( - data, length, systemAddress, rakPeer->pluginListNTS, remoteSystem->MTUSize, - rakNetSocket->s, &rnr, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, timeRead, updateBitStream); - } - } -} - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GenerateSeedFromGuid(void) -{ - /* - // Construct a random seed based on the initial guid value, and the last digits of the difference to each subsequent number - // This assumes that only the last 3 bits of each guidId integer has a meaningful amount of randomness between it and the prior number - unsigned int t = guid.g[0]; - unsigned int i; - for (i=1; i < sizeof(guid.g) / sizeof(guid.g[0]); i++) - { - unsigned int diff = guid.g[i]-guid.g[i-1]; - unsigned int diff3Bits = diff & 0x0007; - diff3Bits <<= 29; - diff3Bits >>= (i-1)*3; - t ^= diff3Bits; - } - - return t; - */ - return (unsigned int) ((myGuid.g >> 32) ^ myGuid.g); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DerefAllSockets(void) -{ - unsigned int i; - for (i=0; i < socketList.Size(); i++) - { - socketList[i].SetNull(); - } - socketList.Clear(false, _FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const -{ - unsigned int i; - for (i=0; i < socketList.Size(); i++) - { - if (socketList[i]->userConnectionSocketIndex==userIndex) - return i; - } - RakAssert("GetRakNetSocketFromUserConnectionSocketIndex failed" && 0); - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::RunUpdateCycle( RakNet::TimeUS timeNS, RakNet::Time timeMS, BitStream &updateBitStream ) -{ - RakPeer::RemoteSystemStruct * remoteSystem; - unsigned int activeSystemListIndex; - Packet *packet; - // int currentSentBytes,currentReceivedBytes; -// unsigned numberOfBytesUsed; -// BitSize_t numberOfBitsUsed; - //SystemAddress authoritativeClientSystemAddress; - BitSize_t bitSize; - unsigned int byteSize; - unsigned char *data; - SystemAddress systemAddress; - BufferedCommandStruct *bcs; - bool callerDataAllocationUsed; - RakNetStatistics *rnss; - - // This is here so RecvFromBlocking actually gets data from the same thread - if (SocketLayer::GetSocketLayerOverride()) - { - int len; - SystemAddress sender; - char dataOut[ MAXIMUM_MTU_SIZE ]; - do { - len = SocketLayer::GetSocketLayerOverride()->RakNetRecvFrom(socketList[0]->s,this,dataOut,&sender,true); - if (len>0) - ProcessNetworkPacket( sender, dataOut, len, this, socketList[0], RakNet::GetTimeUS(), updateBitStream ); - } while (len>0); - } - - unsigned int socketListIndex; - RakPeer::RecvFromStruct *recvFromStruct; - while ((recvFromStruct=bufferedPackets.PopInaccurate())!=0) - { - for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) - { - if ((SOCKET) socketList[socketListIndex]->s==recvFromStruct->s) - break; - } - if (socketListIndex!=socketList.Size()) - ProcessNetworkPacket(recvFromStruct->systemAddress, recvFromStruct->data, recvFromStruct->bytesRead, this, socketList[socketListIndex], recvFromStruct->timeRead, updateBitStream); - bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); - } - - while ((bcs=bufferedCommands.PopInaccurate())!=0) - { - if (bcs->command==BufferedCommandStruct::BCS_SEND) - { - // GetTime is a very slow call so do it once and as late as possible - if (timeNS==0) - { - timeNS = RakNet::GetTimeUS(); - timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); - } - - callerDataAllocationUsed=SendImmediate((char*)bcs->data, bcs->numberOfBitsToSend, bcs->priority, bcs->reliability, bcs->orderingChannel, bcs->systemIdentifier, bcs->broadcast, true, timeNS, bcs->receipt); - if ( callerDataAllocationUsed==false ) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - - // Set the new connection state AFTER we call sendImmediate in case we are setting it to a disconnection state, which does not allow further sends - if (bcs->connectionMode!=RemoteSystemStruct::NO_ACTION ) - { - remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); - if (remoteSystem) - remoteSystem->connectMode=bcs->connectionMode; - } - } - else if (bcs->command==BufferedCommandStruct::BCS_CLOSE_CONNECTION) - { - CloseConnectionInternal(bcs->systemIdentifier, false, true, bcs->orderingChannel, bcs->priority); - } - else if (bcs->command==BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS) - { - // Reroute - RakPeer::RemoteSystemStruct *rssFromGuid = GetRemoteSystem(bcs->systemIdentifier.rakNetGuid,true,true); - if (rssFromGuid!=0) - { - unsigned int existingSystemIndex = GetRemoteSystemIndex(rssFromGuid->systemAddress); - ReferenceRemoteSystem(bcs->systemIdentifier.systemAddress, existingSystemIndex); - } - } - else if (bcs->command==BufferedCommandStruct::BCS_GET_SOCKET) - { - SocketQueryOutput *sqo; - if (bcs->systemIdentifier.IsUndefined()) - { - sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); - sqo->sockets=socketList; - socketQueryOutput.Push(sqo); - } - else - { - remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); - sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); - - sqo->sockets.Clear(false, _FILE_AND_LINE_); - if (remoteSystem) - { - sqo->sockets.Push(remoteSystem->rakNetSocket, _FILE_AND_LINE_ ); - } - else - { - // Leave empty smart pointer - } - socketQueryOutput.Push(sqo); - } - - } - -#ifdef _DEBUG - bcs->data=0; -#endif - - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - } - - if (requestedConnectionQueue.IsEmpty()==false) - { - if (timeNS==0) - { - timeNS = RakNet::GetTimeUS(); - timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); - } - - bool condition1, condition2; - unsigned requestedConnectionQueueIndex=0; - requestedConnectionQueueMutex.Lock(); - while (requestedConnectionQueueIndex < requestedConnectionQueue.Size()) - { - RequestedConnectionStruct *rcs; - rcs = requestedConnectionQueue[requestedConnectionQueueIndex]; - requestedConnectionQueueMutex.Unlock(); - if (rcs->nextRequestTime < timeMS) - { - condition1=rcs->requestsMade==rcs->sendConnectionAttemptCount+1; - condition2=(bool)((rcs->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)==1); - // If too many requests made or a hole then remove this if possible, otherwise invalidate it - if (condition1 || condition2) - { - if (rcs->data) - { - rakFree_Ex(rcs->data, _FILE_AND_LINE_ ); - rcs->data=0; - } - - if (condition1 && !condition2 && rcs->actionToTake==RequestedConnectionStruct::CONNECT) - { - // Tell user of connection attempt failed - packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - AddPacketToProducer(packet); - } - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Connection attempt FAILED so deleting rcs->client_handshake object %x\n", rcs->client_handshake); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - - requestedConnectionQueueMutex.Lock(); - for (unsigned int k=0; k < requestedConnectionQueue.Size(); k++) - { - if (requestedConnectionQueue[k]==rcs) - { - requestedConnectionQueue.RemoveAtIndex(k); - break; - } - } - requestedConnectionQueueMutex.Unlock(); - } - else - { - - int MTUSizeIndex = rcs->requestsMade / (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES); - if (MTUSizeIndex>=NUM_MTU_SIZES) - MTUSizeIndex=NUM_MTU_SIZES-1; - rcs->requestsMade++; - rcs->nextRequestTime=timeMS+rcs->timeBetweenSendConnectionAttemptsMS; - - RakNet::BitStream bitStream; - //WriteOutOfBandHeader(&bitStream, ID_USER_PACKET_ENUM); - bitStream.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_1); - bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bitStream.Write((MessageID)RAKNET_PROTOCOL_VERSION); - bitStream.PadWithZeroToByteLength(mtuSizes[MTUSizeIndex]-UDP_HEADER_SIZE); - - char str[256]; - rcs->systemAddress.ToString(true,str); - - //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*) bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), rcs->systemAddress); - - if (rcs->socket.IsNull()) - { - rcs->systemAddress.FixForIPVersion(socketList[rcs->socketIndex]->boundAddress); - SocketLayer::SetDoNotFragment(socketList[rcs->socketIndex]->s, 1, socketList[rcs->socketIndex]->boundAddress.GetIPPROTO()); - RakNet::Time sendToStart=RakNet::GetTime(); - if (SocketLayer::SendTo( socketList[rcs->socketIndex]->s, (const char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), rcs->systemAddress, socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ )==-10040) - { - // Don't use this MTU size again - rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES)); - rcs->nextRequestTime=timeMS; - } - else - { - RakNet::Time sendToEnd=RakNet::GetTime(); - if (sendToEnd-sendToStart>100) - { - // Drop to lowest MTU - int lowestMtuIndex = rcs->sendConnectionAttemptCount/NUM_MTU_SIZES * (NUM_MTU_SIZES - 1); - if (lowestMtuIndex > rcs->requestsMade) - { - rcs->requestsMade = (unsigned char) lowestMtuIndex; - rcs->nextRequestTime=timeMS; - } - else - rcs->requestsMade=(unsigned char)(rcs->sendConnectionAttemptCount+1); - } - } - SocketLayer::SetDoNotFragment(socketList[rcs->socketIndex]->s, 0, socketList[rcs->socketIndex]->boundAddress.GetIPPROTO()); - } - else - { - rcs->systemAddress.FixForIPVersion(rcs->socket->boundAddress); - SocketLayer::SetDoNotFragment(rcs->socket->s, 1, rcs->socket->boundAddress.GetIPPROTO()); - RakNet::Time sendToStart=RakNet::GetTime(); - if (SocketLayer::SendTo( rcs->socket->s, (const char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), rcs->systemAddress, socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ )==-10040) - { - // Don't use this MTU size again - rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES)); - rcs->nextRequestTime=timeMS; - } - else - { - RakNet::Time sendToEnd=RakNet::GetTime(); - if (sendToEnd-sendToStart>100) - { - // Drop to lowest MTU - int lowestMtuIndex = rcs->sendConnectionAttemptCount/NUM_MTU_SIZES * (NUM_MTU_SIZES - 1); - if (lowestMtuIndex > rcs->requestsMade) - { - rcs->requestsMade = (unsigned char) lowestMtuIndex; - rcs->nextRequestTime=timeMS; - } - else - rcs->requestsMade= (unsigned char) rcs->sendConnectionAttemptCount+1; - } - } - SocketLayer::SetDoNotFragment(rcs->socket->s, 0, rcs->socket->boundAddress.GetIPPROTO()); - } - - requestedConnectionQueueIndex++; - } - } - else - requestedConnectionQueueIndex++; - - requestedConnectionQueueMutex.Lock(); - } - requestedConnectionQueueMutex.Unlock(); - } - - // remoteSystemList in network thread - for ( activeSystemListIndex = 0; activeSystemListIndex < activeSystemListSize; ++activeSystemListIndex ) - //for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; ++remoteSystemIndex ) - { - // I'm using systemAddress from remoteSystemList but am not locking it because this loop is called very frequently and it doesn't - // matter if we miss or do an extra update. The reliability layers themselves never care which player they are associated with - //systemAddress = remoteSystemList[ remoteSystemIndex ].systemAddress; - // Allow the systemAddress for this remote system list to change. We don't care if it changes now. - // remoteSystemList[ remoteSystemIndex ].allowSystemAddressAssigment=true; - - - // Found an active remote system - remoteSystem = activeSystemList[ activeSystemListIndex ]; - systemAddress = remoteSystem->systemAddress; - RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); - // Update is only safe to call from the same thread that calls HandleSocketReceiveFromConnectedPlayer, - // which is this thread - - if (timeNS==0) - { - timeNS = RakNet::GetTimeUS(); - timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); - //RAKNET_DEBUG_PRINTF("timeNS = %I64i timeMS=%i\n", timeNS, timeMS); - } - - - if (timeMS > remoteSystem->lastReliableSend && timeMS-remoteSystem->lastReliableSend > remoteSystem->reliabilityLayer.GetTimeoutTime()/2 && remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) - { - // If no reliable packets are waiting for an ack, do a one byte reliable send so that disconnections are noticed - RakNetStatistics rakNetStatistics; - rnss=remoteSystem->reliabilityLayer.GetStatistics(&rakNetStatistics); - if (rnss->messagesInResendBuffer==0) - { - PingInternal( systemAddress, true, RELIABLE ); - - //remoteSystem->lastReliableSend=timeMS+remoteSystem->reliabilityLayer.GetTimeoutTime(); - remoteSystem->lastReliableSend=timeMS; - } - } - - remoteSystem->reliabilityLayer.Update( remoteSystem->rakNetSocket->s, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, remoteSystem->rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, remoteSystem->rakNetSocket->extraSocketOptions, updateBitStream ); // systemAddress only used for the internet simulator test - - // Check for failure conditions - if ( remoteSystem->reliabilityLayer.IsDeadConnection() || - ((remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY) && remoteSystem->reliabilityLayer.IsOutgoingDataWaiting()==false) || - (remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && (remoteSystem->reliabilityLayer.AreAcksWaiting()==false || remoteSystem->reliabilityLayer.AckTimeout(timeMS)==true)) || - (( - (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || - remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || - remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) - && timeMS > remoteSystem->connectionTime && timeMS - remoteSystem->connectionTime > 10000)) - ) - { - // RAKNET_DEBUG_PRINTF("timeMS=%i remoteSystem->connectionTime=%i\n", timeMS, remoteSystem->connectionTime ); - - // Failed. Inform the user? - // TODO - RakNet 4.0 - Return a different message identifier for DISCONNECT_ASAP_SILENTLY and DISCONNECT_ASAP than for DISCONNECT_ON_NO_ACK - // The first two mean we called CloseConnection(), the last means the other system sent us ID_DISCONNECTION_NOTIFICATION - if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION - || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK) - { - -// RakNet::BitStream undeliveredMessages; -// remoteSystem->reliabilityLayer.GetUndeliveredMessages(&undeliveredMessages,remoteSystem->MTUSize); - -// packet=AllocPacket(sizeof( char ) + undeliveredMessages.GetNumberOfBytesUsed()); - packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - else if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) - packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection - else - packet->data[ 0 ] = ID_DISCONNECTION_NOTIFICATION; // DeadConnection - -// memcpy(packet->data+1, undeliveredMessages.GetData(), undeliveredMessages.GetNumberOfBytesUsed()); - - packet->guid = remoteSystem->guid; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - - AddPacketToProducer(packet); - } - // else connection shutting down, don't bother telling the user - -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Connection dropped for player %i:%i\n", systemAddress); -#endif - CloseConnectionInternal( systemAddress, false, true, 0, LOW_PRIORITY ); - continue; - } - - // Ping this guy if it is time to do so - if ( remoteSystem->connectMode==RemoteSystemStruct::CONNECTED && timeMS > remoteSystem->nextPingTime && ( occasionalPing || remoteSystem->lowestPing == (unsigned short)-1 ) ) - { - remoteSystem->nextPingTime = timeMS + 5000; - PingInternal( systemAddress, true, UNRELIABLE ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - } - - // Find whoever has the lowest player ID - //if (systemAddress < authoritativeClientSystemAddress) - // authoritativeClientSystemAddress=systemAddress; - - // Does the reliability layer have any packets waiting for us? - // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer - bitSize = remoteSystem->reliabilityLayer.Receive( &data ); - - while ( bitSize > 0 ) - { - // These types are for internal use and should never arrive from a network packet - if (data[0]==ID_CONNECTION_ATTEMPT_FAILED) - { - RakAssert(0); - bitSize=0; - continue; - } - - // Fast and easy - just use the data that was returned - byteSize = (unsigned int) BITS_TO_BYTES( bitSize ); - - // For unknown senders we only accept a few specific packets - if (remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) - { - if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) - { - ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else - { - CloseConnectionInternal( systemAddress, false, true, 0, LOW_PRIORITY ); -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Temporarily banning %i:%i for sending nonsense data\n", systemAddress); -#endif - - char str1[64]; - systemAddress.ToString(false, str1); - AddToBanList(str1, remoteSystem->reliabilityLayer.GetTimeoutTime()); - - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // However, if we are connected we still take a connection request in case both systems are trying to connect to each other - // at the same time - if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) - { - // 04/27/06 This is wrong. With cross connections, we can both have initiated the connection are in state REQUESTED_CONNECTION - // 04/28/06 Downgrading connections from connected will close the connection due to security at ((remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED && time > remoteSystem->connectionTime && time - remoteSystem->connectionTime > 10000)) - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - { - ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); - } - else - { - - RakNet::BitStream bs((unsigned char*) data,byteSize,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.IgnoreBytes(RakNetGUID::size()); - RakNet::Time incomingTimestamp; - bs.Read(incomingTimestamp); - - // Got a connection request message from someone we are already connected to. Just reply normally. - // This can happen due to race conditions with the fully connected mesh - OnConnectionRequest( remoteSystem, incomingTimestamp ); - } - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char) data[ 0 ] == ID_NEW_INCOMING_CONNECTION && byteSize > sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(RakNet::Time)*2 ) - { - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) - { - remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; - PingInternal( systemAddress, true, UNRELIABLE ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - - RakNet::BitStream inBitStream((unsigned char *) data, byteSize, false); - SystemAddress bsSystemAddress; - - inBitStream.IgnoreBits(8); - inBitStream.Read(bsSystemAddress); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); - - RakNet::Time sendPingTime, sendPongTime; - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); - - // Overwrite the data in the packet - // NewIncomingConnectionStruct newIncomingConnectionStruct; - // RakNet::BitStream nICS_BS( data, NewIncomingConnectionStruct_Size, false ); - // newIncomingConnectionStruct.Deserialize( nICS_BS ); - - remoteSystem->myExternalSystemAddress = bsSystemAddress; - firstExternalID=bsSystemAddress; - firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); - - // Send this info down to the game - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - } - else - { - // Send to game even if already connected. This could happen when connecting to 127.0.0.1 - // Ignore, already connected - // rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char) data[ 0 ] == ID_CONNECTED_PONG && byteSize == sizeof(unsigned char)+sizeof(RakNet::Time)*2 ) - { - RakNet::Time sendPingTime, sendPongTime; - - // Copy into the ping times array the current time - the value returned - // First extract the sent ping - RakNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); - //PingStruct ps; - //ps.Deserialize(psBS); - inBitStream.IgnoreBits(8); - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - - OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)data[0] == ID_CONNECTED_PING && byteSize == sizeof(unsigned char)+sizeof(RakNet::Time) ) - { - RakNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); - inBitStream.IgnoreBits(8); - RakNet::Time sendPingTime; - inBitStream.Read(sendPingTime); - - RakNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_CONNECTED_PONG); - outBitStream.Write(sendPingTime); - outBitStream.Write(RakNet::GetTime()); - SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, UNRELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0 ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char) data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) - { - // We shouldn't close the connection immediately because we need to ack the ID_DISCONNECTION_NOTIFICATION - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ON_NO_ACK; - rakFree_Ex(data, _FILE_AND_LINE_ ); - - // AddPacketToProducer(packet); - } - else if ( (unsigned char)(data)[0] == ID_DETECT_LOST_CONNECTIONS && byteSize == sizeof(unsigned char) ) - { - // Do nothing - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)(data)[0] == ID_INVALID_PASSWORD ) - { - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - { - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST_ACCEPTED ) - { - if (byteSize > sizeof(MessageID)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(SystemIndex)+sizeof(RakNet::Time)*2) - { - // Make sure this connection accept is from someone we wanted to connect to - bool allowConnection, alreadyConnected; - - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || - remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || - allowConnectionResponseIPMigration) - allowConnection=true; - else - allowConnection=false; - - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) - alreadyConnected=true; - else - alreadyConnected=false; - - if ( allowConnection ) - { - SystemAddress externalID; - SystemIndex systemIndex; -// SystemAddress internalID; - - RakNet::BitStream inBitStream((unsigned char *) data, byteSize, false); - inBitStream.IgnoreBits(8); - // inBitStream.Read(remotePort); - inBitStream.Read(externalID); - inBitStream.Read(systemIndex); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); - - RakNet::Time sendPingTime, sendPongTime; - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - OnConnectedPong(sendPingTime, sendPongTime, remoteSystem); - - // Find a free remote system struct to use - // RakNet::BitStream casBitS(data, byteSize, false); - // ConnectionAcceptStruct cas; - // cas.Deserialize(casBitS); - // systemAddress.GetPort() = remotePort; - - // The remote system told us our external IP, so save it - remoteSystem->myExternalSystemAddress = externalID; - remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; - - firstExternalID=externalID; - firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); - - // Send the connection request complete to the game - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = byteSize * 8; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) GetIndexFromSystemAddress( systemAddress, true ); - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - - RakNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_NEW_INCOMING_CONNECTION); - outBitStream.Write(systemAddress); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - outBitStream.Write(ipList[i]); - outBitStream.Write(sendPongTime); - outBitStream.Write(RakNet::GetTime()); - - - // We turned on encryption with SetEncryptionKey. This pads packets to up to a multiple of 16 bytes. - // As soon as a multiple of 16 byte packet arrives on the remote system, we will turn on AES. This works because all encrypted packets are multiples of 16 and the - // packets I happen to be sending before this are not a multiple of 16 bytes. Otherwise there is no way to know if a packet that arrived is - // encrypted or not so the other side won't know to turn on encryption or not. - RakAssert((outBitStream.GetNumberOfBytesUsed()&15)!=0); - SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0 ); - - if (alreadyConnected==false) - { - PingInternal( systemAddress, true, UNRELIABLE ); - } - } - else - { - // Ignore, already connected - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // Version mismatch error? - RakAssert(0); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // What do I do if I get a message from a system, before I am fully connected? - // I can either ignore it or give it to the user - // It seems like giving it to the user is a better option - if ((data[0]>=(MessageID)ID_TIMESTAMP || data[0]==ID_SND_RECEIPT_ACKED || data[0]==ID_SND_RECEIPT_LOSS) && - remoteSystem->isActive - ) - { - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - } - - // Does the reliability layer have any more packets waiting for us? - // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer - bitSize = remoteSystem->reliabilityLayer.Receive( &data ); - } - - } - - return true; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RAK_THREAD_DECLARATION(RakNet::RecvFromLoop) -{ - - - - RakPeerAndIndex *rpai = ( RakPeerAndIndex * ) arguments; - - RakPeer * rakPeer = rpai->rakPeer; - SOCKET s = rpai->s; - unsigned short remotePortRakNetWasStartedOn_PS3 = rpai->remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions = rpai->extraSocketOptions; - - rakPeer->isRecvFromLoopThreadActive.Increment(); - - RakPeer::RecvFromStruct *recvFromStruct; - while ( rakPeer->endThreads == false ) - { - recvFromStruct=rakPeer->bufferedPackets.Allocate( _FILE_AND_LINE_ ); - if (recvFromStruct != NULL) - { - recvFromStruct->s=s; - recvFromStruct->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; - recvFromStruct->extraSocketOptions=extraSocketOptions; - SocketLayer::RecvFromBlocking(s, rakPeer, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); - - if (recvFromStruct->bytesRead>0) - { - RakAssert(recvFromStruct->systemAddress.GetPort()); - rakPeer->bufferedPackets.Push(recvFromStruct); - rakPeer->quitAndDataEvents.SetEvent(); - rakPeer->errorState = 0; - } - else - { - if (recvFromStruct->bytesRead < 0) { - --rakPeer->errorState; - RakSleep(30); - } - rakPeer->bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); - } - } - else - RakSleep(30); - } - rakPeer->isRecvFromLoopThreadActive.Decrement(); - - - - - return 0; - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RAK_THREAD_DECLARATION(RakNet::UpdateNetworkLoop) -{ - - - - RakPeer * rakPeer = ( RakPeer * ) arguments; - - -/* - // 11/15/05 - this is slower than Sleep() -#ifdef _WIN32 -#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) - // Lets see if these timers give better performance than Sleep - HANDLE timerHandle; - LARGE_INTEGER dueTime; - - if ( rakPeer->threadSleepTimer <= 0 ) - rakPeer->threadSleepTimer = 1; - - // 2nd parameter of false means synchronization timer instead of manual-reset timer - timerHandle = CreateWaitableTimer( NULL, FALSE, 0 ); - - RakAssert( timerHandle ); - - dueTime.QuadPart = -10000 * rakPeer->threadSleepTimer; // 10000 is 1 ms? - - BOOL success = SetWaitableTimer( timerHandle, &dueTime, rakPeer->threadSleepTimer, NULL, NULL, FALSE ); - (void) success; - RakAssert( success ); - -#endif -#endif -*/ - - BitStream updateBitStream( MAXIMUM_MTU_SIZE -#if LIBCAT_SECURITY==1 - + cat::AuthenticatedEncryption::OVERHEAD_BYTES -#endif - ); - - RakNet::TimeUS timeNS; - RakNet::TimeMS timeMS; - - rakPeer->isMainLoopThreadActive = true; - - while ( rakPeer->endThreads == false ) - { - // Set inside RunUpdateCycle() itself, this is here for testing - timeNS=0; - timeMS=0; - -// #ifdef _DEBUG -// // Sanity check, make sure RunUpdateCycle does not block or not otherwise get called for a long time -// RakNetTime thisCall=RakNet::GetTime(); -// RakAssert(thisCall-lastCall<250); -// lastCall=thisCall; -// #endif - if (rakPeer->userUpdateThreadPtr) - rakPeer->userUpdateThreadPtr(rakPeer, rakPeer->userUpdateThreadData); - - rakPeer->RunUpdateCycle(timeNS, timeMS, updateBitStream); - - // Pending sends go out this often, unless quitAndDataEvents is set - rakPeer->quitAndDataEvents.WaitOnEvent(10); - - /* - -// #if ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && -#if defined(USE_WAIT_FOR_MULTIPLE_EVENTS) && defined(_WIN32) - - if (rakPeer->threadSleepTimer>0) - { - WSAEVENT eventArray[256]; - unsigned int i, eventArrayIndex; - for (i=0,eventArrayIndex=0; i < rakPeer->socketList.Size(); i++) - { - if (rakPeer->socketList[i]->recvEvent!=INVALID_HANDLE_VALUE) - { - eventArray[eventArrayIndex]=rakPeer->socketList[i]->recvEvent; - eventArrayIndex++; - if (eventArrayIndex==256) - break; - } - } - WSAWaitForMultipleEvents(eventArrayIndex,(const HANDLE*) &eventArray,FALSE,rakPeer->threadSleepTimer,FALSE); - } - else - { - RakSleep(0); - } - -#else // ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - #pragma message("-- RakNet: Using Sleep(). Uncomment USE_WAIT_FOR_MULTIPLE_EVENTS in RakNetDefines.h if you want to use WaitForSingleObject instead. --") - - RakSleep( rakPeer->threadSleepTimer ); -#endif - */ - } - - rakPeer->isMainLoopThreadActive = false; - - /* -#ifdef _WIN32 -#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) - CloseHandle(timerHandle); -#endif -#endif - */ - - - - - return 0; - -} - -void RakPeer::CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet) -{ - for (unsigned int i=0; i < pluginList.Size(); i++) - { - switch (packet->data[0]) - { - case ID_DISCONNECTION_NOTIFICATION: - pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_DISCONNECTION_NOTIFICATION); - break; - case ID_CONNECTION_LOST: - pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_CONNECTION_LOST); - break; - case ID_NEW_INCOMING_CONNECTION: - pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, true); - break; - case ID_CONNECTION_REQUEST_ACCEPTED: - pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, false); - break; - case ID_CONNECTION_ATTEMPT_FAILED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_ATTEMPT_FAILED); - break; - case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); - break; - case ID_OUR_SYSTEM_REQUIRES_SECURITY: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_OUR_SYSTEM_REQUIRES_SECURITY); - break; - case ID_PUBLIC_KEY_MISMATCH: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_PUBLIC_KEY_MISMATCH); - break; - case ID_ALREADY_CONNECTED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_ALREADY_CONNECTED); - break; - case ID_NO_FREE_INCOMING_CONNECTIONS: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_NO_FREE_INCOMING_CONNECTIONS); - break; - case ID_CONNECTION_BANNED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_BANNED); - break; - case ID_INVALID_PASSWORD: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INVALID_PASSWORD); - break; - case ID_INCOMPATIBLE_PROTOCOL_VERSION: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INCOMPATIBLE_PROTOCOL); - break; - case ID_IP_RECENTLY_CONNECTED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_IP_RECENTLY_CONNECTED); - break; - } - } -} - -// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) -// #pragma pop_macro("new") -// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +// \file +// +// This file is part of RakNet Copyright 2003 Jenkins Software LLC +// +// Usage of RakNet is subject to the appropriate license agreement. + + +#define CAT_NEUTER_EXPORT /* Neuter dllimport for libcat */ + +#include "RakNetDefines.h" +#include "RakPeer.h" +#include "RakNetTypes.h" + +#ifdef _WIN32 + +#else +#include +#endif + +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + +#include +#include // toupper +#include +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "DS_HuffmanEncodingTree.h" +#include "Rand.h" +#include "PluginInterface2.h" +#include "StringCompressor.h" +#include "StringTable.h" +#include "NetworkIDObject.h" +#include "RakNetTypes.h" +#include "SHA1.h" +#include "RakSleep.h" +#include "RakAssert.h" +#include "RakNetVersion.h" +#include "NetworkIDManager.h" +#include "gettimeofday.h" +#include "SignaledEvent.h" +#include "SuperFastHash.h" +#include "RakAlloca.h" +#include "WSAStartupSingleton.h" + +#ifdef USE_THREADED_SEND +#include "SendToThread.h" +#endif + +#ifdef CAT_AUDIT +#define CAT_AUDIT_PRINTF(...) printf(__VA_ARGS__) +#else +#define CAT_AUDIT_PRINTF(...) +#endif + +namespace RakNet +{ +RAK_THREAD_DECLARATION(UpdateNetworkLoop); +RAK_THREAD_DECLARATION(RecvFromLoop); +RAK_THREAD_DECLARATION(UDTConnect); +} +#define REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE 8 + +#if !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) +#include // malloc +#endif + + + +#if defined(_WIN32) +// +#else +/* +#include // Console 2 +#include +extern bool _extern_Console2LoadModules(void); +extern int _extern_Console2GetConnectionStatus(void); +extern int _extern_Console2GetLobbyStatus(void); +//extern bool Console2StartupFluff(unsigned int *); +extern void Console2ShutdownFluff(void); +//extern unsigned int Console2ActivateConnection(unsigned int, void *); +//extern bool Console2BlockOnEstablished(void); +extern void Console2GetIPAndPort(unsigned int, char *, unsigned short *, unsigned int ); +//extern void Console2DeactivateConnection(unsigned int, unsigned int); +*/ +#endif + + +static const int NUM_MTU_SIZES=3; + + + +static const int mtuSizes[NUM_MTU_SIZES]={MAXIMUM_MTU_SIZE, 1200, 576}; + + +// Note to self - if I change this it might affect RECIPIENT_OFFLINE_MESSAGE_INTERVAL in Natpunchthrough.cpp +//static const int MAX_OPEN_CONNECTION_REQUESTS=8; +//static const int TIME_BETWEEN_OPEN_CONNECTION_REQUESTS=500; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +static RakNetRandom rnr; + +struct RakPeerAndIndex +{ + SOCKET s; + unsigned short remotePortRakNetWasStartedOn_PS3; + RakPeer *rakPeer; + unsigned int extraSocketOptions; +}; + +static const unsigned int MAX_OFFLINE_DATA_LENGTH=400; // I set this because I limit ID_CONNECTION_REQUEST to 512 bytes, and the password is appended to that packet. + +// Used to distinguish between offline messages with data, and messages from the reliability layer +// Should be different than any message that could result from messages from the reliability layer +#if !defined(__GNUC__) +#pragma warning(disable:4309) // 'initializing' : truncation of constant value +#endif +// Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false +static const unsigned char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78}; + +struct PacketFollowedByData +{ + Packet p; + unsigned char data[1]; +}; + +Packet *RakPeer::AllocPacket(unsigned dataSize, const char *file, unsigned int line) +{ + // Crashes when dataSize is 4 bytes - not sure why +// unsigned char *data = (unsigned char *) rakMalloc_Ex(sizeof(PacketFollowedByData)+dataSize, file, line); +// Packet *p = &((PacketFollowedByData *)data)->p; +// p->data=((PacketFollowedByData *)data)->data; +// p->length=dataSize; +// p->bitSize=BYTES_TO_BITS(dataSize); +// p->deleteData=false; +// p->guid=UNASSIGNED_RAKNET_GUID; +// return p; + + RakNet::Packet *p; + packetAllocationPoolMutex.Lock(); + p = packetAllocationPool.Allocate(file,line); + packetAllocationPoolMutex.Unlock(); + p = new ((void*)p) Packet; + p->data=(unsigned char*) rakMalloc_Ex(dataSize,file,line); + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=true; + p->guid=UNASSIGNED_RAKNET_GUID; + p->wasGeneratedLocally=false; + return p; +} + +Packet *RakPeer::AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line) +{ + // Packet *p = (Packet *)rakMalloc_Ex(sizeof(Packet), file, line); + RakNet::Packet *p; + packetAllocationPoolMutex.Lock(); + p = packetAllocationPool.Allocate(file,line); + packetAllocationPoolMutex.Unlock(); + p = new ((void*)p) Packet; + RakAssert(p); + p->data=data; + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=true; + p->guid=UNASSIGNED_RAKNET_GUID; + p->wasGeneratedLocally=false; + return p; +} + +STATIC_FACTORY_DEFINITIONS(RakPeerInterface,RakPeer) + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Constructor +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RakPeer() +{ + errorState = 0; +#if LIBCAT_SECURITY==1 + // Encryption and security + CAT_AUDIT_PRINTF("AUDIT: Initializing RakPeer security flags: using_security = false, server_handshake = null, cookie_jar = null\n"); + _using_security = false; + _server_handshake = 0; + _cookie_jar = 0; +#endif + + StringCompressor::AddReference(); + RakNet::StringTable::AddReference(); + WSAStartupSingleton::AddRef(); + + defaultMTUSize = mtuSizes[NUM_MTU_SIZES-1]; + trackFrequencyTable = false; + maximumIncomingConnections = 0; + maximumNumberOfPeers = 0; + //remoteSystemListSize=0; + remoteSystemList = 0; + activeSystemList = 0; + activeSystemListSize=0; + remoteSystemLookup=0; + bytesSentPerSecond = bytesReceivedPerSecond = 0; + endThreads = true; + isMainLoopThreadActive = false; + + + + + + // isRecvfromThreadActive=false; +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 + occasionalPing = true; +#else + occasionalPing = false; +#endif + allowInternalRouting=false; + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + ipList[i]=UNASSIGNED_SYSTEM_ADDRESS; + allowConnectionResponseIPMigration = false; + //incomingPasswordLength=outgoingPasswordLength=0; + incomingPasswordLength=0; + splitMessageProgressInterval=0; + //unreliableTimeout=0; + unreliableTimeout=1000; + maxOutgoingBPS=0; + firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; + myGuid=UNASSIGNED_RAKNET_GUID; + userUpdateThreadPtr=0; + userUpdateThreadData=0; + +#ifdef _DEBUG + // Wait longer to disconnect in debug so I don't get disconnected while tracing + defaultTimeoutTime=30000; +#else + defaultTimeoutTime=10000; +#endif + +#ifdef _DEBUG + _packetloss=0.0; + _minExtraPing=0; + _extraPingVariance=0; +#endif + + bufferedCommands.SetPageSize(sizeof(BufferedCommandStruct)*16); + socketQueryOutput.SetPageSize(sizeof(SocketQueryOutput)*8); + bufferedPackets.SetPageSize(sizeof(RecvFromStruct)*BUFFERED_PACKETS_PAGE_SIZE); // This is big, keep the size down + + packetAllocationPoolMutex.Lock(); + packetAllocationPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); + packetAllocationPoolMutex.Unlock(); + + remoteSystemIndexPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenerateGUID(); + + quitAndDataEvents.InitEvent(); + limitConnectionFrequencyFromTheSameIP=false; + ResetSendReceipt(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Destructor +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::~RakPeer() +{ + Shutdown( 0, 0 ); + + // Free the ban list. + ClearBanList(); + + StringCompressor::RemoveReference(); + RakNet::StringTable::RemoveReference(); + WSAStartupSingleton::Deref(); + + quitAndDataEvents.CloseEvent(); + +#if LIBCAT_SECURITY==1 + // Encryption and security + CAT_AUDIT_PRINTF("AUDIT: Deleting RakPeer security objects, handshake = %x, cookie jar = %x\n", _server_handshake, _cookie_jar); + if (_server_handshake) RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + if (_cookie_jar) RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); +#endif + + + + + + + + + + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// \brief Starts the network threads, opens the listen port. +// You must call this before calling Connect(). +// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). +// \note Call SetMaximumIncomingConnections if you want to accept incoming connections +// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections +// \param[in] localPort The port to listen for connections on. +// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle. With new congestion control, the best results will be obtained by passing 10. +// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass &socketDescriptor, 1SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); +// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. +// \return False on failure (can't create socket or thread), true on success. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +StartupResult RakPeer::Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority ) +{ + errorState = 0; + + if (IsActive()) + return RAKNET_ALREADY_STARTED; + + if (threadPriority==-99999) + { + + +#if defined(_WIN32) + threadPriority=0; + + +#else + threadPriority=1000; +#endif + } + + + // Fill out ipList structure + unsigned int i; + + SocketLayer::GetMyIP( ipList ); +// for (i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) +// { +// if (ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS) +// { +// char str[128]; +// ipList[i].ToString(false,str); +// printf("%s\n",str); +// } +// } + + + if (myGuid==UNASSIGNED_RAKNET_GUID) + { + rnr.SeedMT( GenerateSeedFromGuid() ); + } + + RakPeerAndIndex rpai[32]; + RakAssert(socketDescriptorCount<32); + + RakAssert(socketDescriptors && socketDescriptorCount>=1); + + if (socketDescriptors==0 || socketDescriptorCount<1) + return INVALID_SOCKET_DESCRIPTORS; + + //unsigned short localPort; + //localPort=socketDescriptors[0].port; + + RakAssert( maxConnections > 0 ); + + if ( maxConnections <= 0 ) + return INVALID_MAX_CONNECTIONS; + + DerefAllSockets(); + + + // Go through all socket descriptors and precreate sockets on the specified addresses + for (i=0; i rns(RakNet::OP_NEW(_FILE_AND_LINE_)); + if (socketDescriptors[i].remotePortRakNetWasStartedOn_PS3_PSP2==0) + { + rns->s = (unsigned int) SocketLayer::CreateBoundSocket( socketDescriptors[i].port, true, addrToBind, 100, socketDescriptors[i].extraSocketOptions, socketDescriptors[i].socketFamily ); + } + else + { + + + + + + } + + if ((SOCKET)rns->s==(SOCKET)-1) + { + DerefAllSockets(); + return SOCKET_FAILED_TO_BIND; + } + + SocketLayer::GetSystemAddress( rns->s, &rns->boundAddress ); + + rns->remotePortRakNetWasStartedOn_PS3_PSP2=socketDescriptors[i].remotePortRakNetWasStartedOn_PS3_PSP2; + rns->extraSocketOptions=socketDescriptors[i].extraSocketOptions; + rns->userConnectionSocketIndex=i; + +#if RAKNET_SUPPORT_IPV6==0 + if (addrToBind==0) + rns->boundAddress.SetToLoopback(4); +#endif + + int zero=0; + if (SocketLayer::SendTo((SOCKET)rns->s, (const char*) &zero,4,rns->boundAddress, rns->remotePortRakNetWasStartedOn_PS3_PSP2, rns->extraSocketOptions, _FILE_AND_LINE_)!=0) + { + DerefAllSockets(); + return SOCKET_FAILED_TEST_SEND; + } + + socketList.Push(rns, _FILE_AND_LINE_ ); + + } + + + if ( maximumNumberOfPeers == 0 ) + { + // Don't allow more incoming connections than we have peers. + if ( maximumIncomingConnections > maxConnections ) + maximumIncomingConnections = maxConnections; + + maximumNumberOfPeers = maxConnections; + // 04/19/2006 - Don't overallocate because I'm no longer allowing connected pings. + // The disconnects are not consistently processed and the process was sloppy and complicated. + // Allocate 10% extra to handle new connections from players trying to connect when the server is full + //remoteSystemListSize = maxConnections;// * 11 / 10 + 1; + + // remoteSystemList in Single thread + //remoteSystemList = RakNet::OP_NEW( _FILE_AND_LINE_ ); + remoteSystemList = RakNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); + + remoteSystemLookup = RakNet::OP_NEW_ARRAY((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE, _FILE_AND_LINE_ ); + + activeSystemList = RakNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); + + for ( i = 0; i < maximumNumberOfPeers; i++ ) + //for ( i = 0; i < remoteSystemListSize; i++ ) + { + // remoteSystemList in Single thread + remoteSystemList[ i ].isActive = false; + remoteSystemList[ i ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystemList[ i ].guid = UNASSIGNED_RAKNET_GUID; + remoteSystemList[ i ].myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystemList[ i ].connectMode=RemoteSystemStruct::NO_ACTION; + remoteSystemList[ i ].MTUSize = defaultMTUSize; + remoteSystemList[ i ].remoteSystemIndex = (SystemIndex) i; +#ifdef _DEBUG + remoteSystemList[ i ].reliabilityLayer.ApplyNetworkSimulator(_packetloss, _minExtraPing, _extraPingVariance); +#endif + + // All entries in activeSystemList have valid pointers all the time. + activeSystemList[ i ] = &remoteSystemList[ i ]; + } + + for (unsigned int i=0; i < (unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE; i++) + { + remoteSystemLookup[i]=0; + } + } + + // For histogram statistics + // nextReadBytesTime=0; + // lastSentBytes=lastReceivedBytes=0; + + if ( endThreads ) + { + updateCycleIsRunning = false; + endThreads = false; + + ClearBufferedCommands(); + ClearBufferedPackets(); + ClearSocketQueryOutput(); + + if ( isMainLoopThreadActive == false ) + { + int errorCode; + + + + + + + + errorCode = RakNet::RakThread::Create(UpdateNetworkLoop, this, threadPriority); + + + if ( errorCode != 0 ) + { + Shutdown( 0, 0 ); + return FAILED_TO_CREATE_NETWORK_THREAD; + } + + RakAssert(isRecvFromLoopThreadActive.GetValue()==0); + for (i=0; is; + rpai[i].rakPeer=this; + + + + + + errorCode = RakNet::RakThread::Create(RecvFromLoop, &rpai[i], threadPriority); + + + if ( errorCode != 0 ) + { + Shutdown( 0, 0 ); + return FAILED_TO_CREATE_NETWORK_THREAD; + } + } + + + while ( isRecvFromLoopThreadActive.GetValue() < (uint32_t) socketDescriptorCount ) + RakSleep(10); + } + + // Wait for the threads to activate. When they are active they will set these variables to true + + while ( isMainLoopThreadActive == false ) + RakSleep(10); + + } + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->OnRakPeerStartup(); + } + + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->OnRakPeerStartup(); + } + +#ifdef USE_THREADED_SEND + RakNet::SendToThread::AddRef(); +#endif + + return RAKNET_STARTED; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Must be called while offline +// +// If you accept connections, you must call this or else security will not be enabled for incoming connections. +// +// This feature requires more round trips, bandwidth, and CPU time for the connection handshake +// x64 builds require under 25% of the CPU time of other builds +// +// See the Encryption sample for example usage +// +// Parameters: +// publicKey = A pointer to the public key for accepting new connections +// privateKey = A pointer to the private key for accepting new connections +// If the private keys are 0, then a new key will be generated when this function is called +// bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::InitializeSecurity(const char *public_key, const char *private_key, bool bRequireClientKey) +{ +#if LIBCAT_SECURITY==1 + if ( endThreads == false ) + return false; + + // Copy client public key requirement flag + _require_client_public_key = bRequireClientKey; + + if (_server_handshake) + { + CAT_AUDIT_PRINTF("AUDIT: Deleting old server_handshake %x\n", _server_handshake); + RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + } + if (_cookie_jar) + { + CAT_AUDIT_PRINTF("AUDIT: Deleting old cookie jar %x\n", _cookie_jar); + RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + } + + _server_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); + _cookie_jar = RakNet::OP_NEW(_FILE_AND_LINE_); + + CAT_AUDIT_PRINTF("AUDIT: Created new server_handshake %x\n", _server_handshake); + CAT_AUDIT_PRINTF("AUDIT: Created new cookie jar %x\n", _cookie_jar); + CAT_AUDIT_PRINTF("AUDIT: Running _server_handshake->Initialize()\n"); + + if (_server_handshake->Initialize(public_key, private_key)) + { + CAT_AUDIT_PRINTF("AUDIT: Successfully initialized, filling cookie jar with goodies, storing public key and setting using security flag to true\n"); + + _server_handshake->FillCookieJar(_cookie_jar); + + memcpy(my_public_key, public_key, sizeof(my_public_key)); + + _using_security = true; + return true; + } + + CAT_AUDIT_PRINTF("AUDIT: Failure to initialize so deleting server handshake and cookie jar; also setting using_security flag = false\n"); + + RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + _server_handshake=0; + RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + _cookie_jar=0; + _using_security = false; + return false; +#else + (void) public_key; + (void) private_key; + (void) bRequireClientKey; + + return false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description +// Must be called while offline +// Disables security for incoming connections. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DisableSecurity( void ) +{ +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: DisableSecurity() called, so deleting _server_handshake %x and cookie_jar %x\n", _server_handshake, _cookie_jar); + RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + _server_handshake=0; + RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + _cookie_jar=0; + + _using_security = false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToSecurityExceptionList(const char *ip) +{ + securityExceptionMutex.Lock(); + securityExceptionList.Insert(RakString(ip), _FILE_AND_LINE_); + securityExceptionMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromSecurityExceptionList(const char *ip) +{ + if (securityExceptionList.Size()==0) + return; + + if (ip==0) + { + securityExceptionMutex.Lock(); + securityExceptionList.Clear(false, _FILE_AND_LINE_); + securityExceptionMutex.Unlock(); + } + else + { + unsigned i=0; + securityExceptionMutex.Lock(); + while (i < securityExceptionList.Size()) + { + if (securityExceptionList[i].IPAddressMatch(ip)) + { + securityExceptionList[i]=securityExceptionList[securityExceptionList.Size()-1]; + securityExceptionList.RemoveAtIndex(securityExceptionList.Size()-1); + } + else + i++; + } + securityExceptionMutex.Unlock(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsInSecurityExceptionList(const char *ip) +{ + if (securityExceptionList.Size()==0) + return false; + + unsigned i=0; + securityExceptionMutex.Lock(); + for (; i < securityExceptionList.Size(); i++) + { + if (securityExceptionList[i].IPAddressMatch(ip)) + { + securityExceptionMutex.Unlock(); + return true; + } + } + securityExceptionMutex.Unlock(); + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no +// more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced +// to the maximum number of peers allowed. Defaults to 0. +// +// Parameters: +// numberAllowed - Maximum number of incoming connections allowed. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetMaximumIncomingConnections( unsigned short numberAllowed ) +{ + maximumIncomingConnections = numberAllowed; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the maximum number of incoming connections, which is always <= maxConnections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::GetMaximumIncomingConnections( void ) const +{ + return maximumIncomingConnections; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns how many open connections there are at this time +// \return the number of open connections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::NumberOfConnections(void) const +{ + DataStructures::List addresses; + DataStructures::List guids; + GetSystemList(addresses, guids); + return (unsigned short) addresses.Size(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sets the password incoming connections must match in the call to Connect (defaults to none) +// Pass 0 to passwordData to specify no password +// +// Parameters: +// passwordData: A data block that incoming connections must match. This can be just a password, or can be a stream of data. +// - Specify 0 for no password data +// passwordDataLength: The length in bytes of passwordData +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetIncomingPassword( const char* passwordData, int passwordDataLength ) +{ + //if (passwordDataLength > MAX_OFFLINE_DATA_LENGTH) + // passwordDataLength=MAX_OFFLINE_DATA_LENGTH; + + if (passwordDataLength > 255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? + // It won't overflow at least because incomingPasswordLength is an unsigned char + if (passwordDataLength>0) + memcpy(incomingPassword, passwordData, passwordDataLength); + incomingPasswordLength=(unsigned char)passwordDataLength; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetIncomingPassword( char* passwordData, int *passwordDataLength ) +{ + if (passwordData==0) + { + *passwordDataLength=incomingPasswordLength; + return; + } + + if (*passwordDataLength > incomingPasswordLength) + *passwordDataLength=incomingPasswordLength; + + if (*passwordDataLength>0) + memcpy(passwordData, incomingPassword, *passwordDataLength); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Call this to connect to the specified host (ip or domain name) and server port. +// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. +// This is a non-blocking connection. You know the connection is successful when IsConnected() returns true +// or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. If the connection is not +// successful, such as rejected connection or no response then neither of these things will happen. +// Requires that you first call Initialize +// +// Parameters: +// host: Either a dotted IP address or a domain name +// remotePort: Which port to connect to on the remote machine. +// passwordData: A data block that must match the data block on the server. This can be just a password, or can be a stream of data +// passwordDataLength: The length in bytes of passwordData +// +// Returns: +// True on successful initiation. False on incorrect parameters, internal error, or too many existing peers +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ConnectionAttemptResult RakPeer::Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ) +{ + // If endThreads is true here you didn't call Startup() first. + if ( host == 0 || endThreads || connectionSocketIndex>=socketList.Size() ) + return INVALID_PARAMETER; + + RakAssert(remotePort!=0); + + connectionSocketIndex=GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + if (passwordDataLength>255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? + // It won't overflow at least because outgoingPasswordLength is an unsigned char +// if (passwordDataLength>0) +// memcpy(outgoingPassword, passwordData, passwordDataLength); +// outgoingPasswordLength=(unsigned char) passwordDataLength; + + // 04/02/09 - Can't remember why I disabled connecting to self, but it seems to work + // Connecting to ourselves in the same instance of the program? +// if ( ( strcmp( host, "127.0.0.1" ) == 0 || strcmp( host, "0.0.0.0" ) == 0 ) && remotePort == mySystemAddress[0].port ) +// return false; + + return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, connectionSocketIndex, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ConnectionAttemptResult RakPeer::ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime) +{ + if ( host == 0 || endThreads || socket.IsNull() ) + return INVALID_PARAMETER; + + if (passwordDataLength>255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, 0, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime, socket ); + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Stops the network threads and close all connections. Multiple calls are ok. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::Shutdown( unsigned int blockDuration, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) +{ + unsigned i,j; + bool anyActive; + RakNet::TimeMS startWaitingTime; +// SystemAddress systemAddress; + RakNet::TimeMS time; + //unsigned short systemListSize = remoteSystemListSize; // This is done for threading reasons + unsigned short systemListSize = maximumNumberOfPeers; + + if ( blockDuration > 0 ) + { + for ( i = 0; i < systemListSize; i++ ) + { + // remoteSystemList in user thread + if (remoteSystemList[i].isActive) + NotifyAndFlagForShutdown(remoteSystemList[i].systemAddress, false, orderingChannel, disconnectionNotificationPriority); + } + + time = RakNet::GetTimeMS(); + startWaitingTime = time; + while ( time - startWaitingTime < blockDuration ) + { + anyActive=false; + for (j=0; j < systemListSize; j++) + { + // remoteSystemList in user thread + if (remoteSystemList[j].isActive) + { + anyActive=true; + break; + } + } + + // If this system is out of packets to send, then stop waiting + if ( anyActive==false ) + break; + + // This will probably cause the update thread to run which will probably + // send the disconnection notification + + RakSleep(15); + time = RakNet::GetTimeMS(); + } + } + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->OnRakPeerShutdown(); + } + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->OnRakPeerShutdown(); + } + + activeSystemListSize=0; + + quitAndDataEvents.SetEvent(); + + endThreads = true; + // Get recvfrom to unblock + for (i=0; i < socketList.Size(); i++) + { + SystemAddress sa2 = socketList[i]->boundAddress; + if (SocketLayer::SendTo(socketList[i]->s, (const char*) &i,1,sa2, socketList[i]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[i]->extraSocketOptions, _FILE_AND_LINE_)!=0) + break; + } + while ( isMainLoopThreadActive ) + { + endThreads = true; + RakSleep(15); + } + + RakNet::TimeMS timeout = RakNet::GetTimeMS()+1000; + while ( isRecvFromLoopThreadActive.GetValue()>0 && RakNet::GetTimeMS()boundAddress; + SocketLayer::SendTo(socketList[i]->s, (const char*) &i,1,sa2, socketList[i]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[i]->extraSocketOptions, _FILE_AND_LINE_); + } + + RakSleep(30); + } + +// char c=0; +// unsigned int socketIndex; + // remoteSystemList in Single thread + for ( i = 0; i < systemListSize; i++ ) + { + // Reserve this reliability layer for ourselves + remoteSystemList[ i ].isActive = false; + + // Remove any remaining packets + remoteSystemList[ i ].reliabilityLayer.Reset(false, remoteSystemList[ i ].MTUSize, false); + remoteSystemList[ i ].rakNetSocket.SetNull(); + + } + + + // Setting maximumNumberOfPeers to 0 allows remoteSystemList to be reallocated in Initialize. + // Setting remoteSystemListSize prevents threads from accessing the reliability layer + maximumNumberOfPeers = 0; + //remoteSystemListSize = 0; + + // Free any packets the user didn't deallocate + packetReturnMutex.Lock(); + for (i=0; i < packetReturnQueue.Size(); i++) + DeallocatePacket(packetReturnQueue[i]); + packetReturnQueue.Clear(_FILE_AND_LINE_); + packetReturnMutex.Unlock(); + packetAllocationPoolMutex.Lock(); + packetAllocationPool.Clear(_FILE_AND_LINE_); + packetAllocationPoolMutex.Unlock(); + + if (isRecvFromLoopThreadActive.GetValue()>0) + { + timeout = RakNet::GetTimeMS()+1000; + while ( isRecvFromLoopThreadActive.GetValue()>0 && RakNet::GetTimeMS() addresses; + DataStructures::List guids; + GetSystemList(addresses, guids); + if (remoteSystems) + { + unsigned short i; + for (i=0; i < *numberOfSystems && i < addresses.Size(); i++) + remoteSystems[i]=addresses[i]; + *numberOfSystems=i; + } + else + { + *numberOfSystems=(unsigned short) addresses.Size(); + } + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::GetNextSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + uint32_t retVal = sendReceiptSerial; + sendReceiptSerialMutex.Unlock(); + return retVal; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::IncrementNextSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + uint32_t returned = sendReceiptSerial; + if (++sendReceiptSerial==0) + sendReceiptSerial=1; + sendReceiptSerialMutex.Unlock(); + return returned; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sends a block of data to the specified system that you are connected to. +// This function only works while the client is connected (Use the Connect function). +// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM +// +// Parameters: +// data: The block of data to send +// length: The size in bytes of the data to send +// bitStream: The bitstream to send +// priority: What priority level to send on. +// reliability: How reliability to send this data +// orderingChannel: When using ordered or sequenced packets, what channel to order these on. +// - Packets are only ordered relative to other packets on the same stream +// systemAddress: Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none +// broadcast: True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. +// Returns: +// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( data && length > 0 ); +#endif + RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); + RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + if ( data == 0 || length < 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined()) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback(data,length); + + if (reliability>=UNRELIABLE_WITH_ACK_RECEIPT) + { + char buff[5]; + buff[0]=ID_SND_RECEIPT_ACKED; + sendReceiptSerialMutex.Lock(); + memcpy(buff+1, &sendReceiptSerial, 4); + sendReceiptSerialMutex.Unlock(); + SendLoopback( buff, 5 ); + } + + return usedSendReceipt; + } + + SendBuffered(data, length*8, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + return usedSendReceipt; +} + +void RakPeer::SendLoopback( const char *data, const int length ) +{ + if ( data == 0 || length < 0 ) + return; + + Packet *packet = AllocPacket(length, _FILE_AND_LINE_); + memcpy(packet->data, data, length); + packet->systemAddress = GetLoopbackAddress(); + packet->guid=myGuid; + PushBackPacket(packet, false); +} + +uint32_t RakPeer::Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( bitStream->GetNumberOfBytesUsed() > 0 ); +#endif + + RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); + RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + if ( bitStream->GetNumberOfBytesUsed() == 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined() ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback((const char*) bitStream->GetData(),bitStream->GetNumberOfBytesUsed()); + if (reliability>=UNRELIABLE_WITH_ACK_RECEIPT) + { + char buff[5]; + buff[0]=ID_SND_RECEIPT_ACKED; + sendReceiptSerialMutex.Lock(); + memcpy(buff+1, &sendReceiptSerial,4); + sendReceiptSerialMutex.Unlock(); + SendLoopback( buff, 5 ); + } + return usedSendReceipt; + } + + // Sends need to be buffered and processed in the update thread because the systemAddress associated with the reliability layer can change, + // from that thread, resulting in a send to the wrong player! While I could mutex the systemAddress, that is much slower than doing this + SendBuffered((const char*)bitStream->GetData(), bitStream->GetNumberOfBitsUsed(), priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + + return usedSendReceipt; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Sends multiple blocks of data, concatenating them automatically. +// +// This is equivalent to: +// RakNet::BitStream bs; +// bs.WriteAlignedBytes(block1, blockLength1); +// bs.WriteAlignedBytes(block2, blockLength2); +// bs.WriteAlignedBytes(block3, blockLength3); +// Send(&bs, ...) +// +// This function only works while the connected +// \param[in] data An array of pointers to blocks of data +// \param[in] lengths An array of integers indicating the length of each block of data +// \param[in] numParameters Length of the arrays data and lengths +// \param[in] priority What priority level to send on. See PacketPriority.h +// \param[in] reliability How reliability to send this data. See PacketPriority.h +// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream +// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none +// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. +// \return False if we are not connected to the specified recipient. True otherwise +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( data ); +#endif + + if ( data == 0 || lengths == 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if (numParameters==0) + return 0; + + if (lengths==0) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined() ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + SendBufferedList(data, lengths, numParameters, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + return usedSendReceipt; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. +// Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct +// +// Returns: +// 0 if no packets are waiting to be handled, otherwise an allocated packet +// If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected +// This also updates all memory blocks associated with synchronized memory and distributed objects +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +#ifdef _MSC_VER +#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized +#endif +Packet* RakPeer::Receive( void ) +{ + if ( !( IsActive() ) ) + return 0; + + RakNet::Packet *packet; +// Packet **threadPacket; + PluginReceiveResult pluginResult; + + int offset; + unsigned int i; + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->Update(); + } + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->Update(); + } + + do + { + packetReturnMutex.Lock(); + if (packetReturnQueue.IsEmpty()) + packet=0; + else + packet = packetReturnQueue.Pop(); + packetReturnMutex.Unlock(); + if (packet==0) + return 0; + +// unsigned char msgId; + if ( ( packet->length >= sizeof(unsigned char) + sizeof( RakNet::Time ) ) && + ( (unsigned char) packet->data[ 0 ] == ID_TIMESTAMP ) ) + { + offset = sizeof(unsigned char); + ShiftIncomingTimestamp( packet->data + offset, packet->systemAddress ); +// msgId=packet->data[sizeof(unsigned char) + sizeof( RakNet::Time )]; + } +// else + // msgId=packet->data[0]; + + // Some locally generated packets need to be processed by plugins, for example ID_FCM2_NEW_HOST + // The plugin itself should intercept these messages generated remotely +// if (packet->wasGeneratedLocally) +// return packet; + + + CallPluginCallbacks(pluginListTS, packet); + CallPluginCallbacks(pluginListNTS, packet); + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginResult=pluginListTS[i]->OnReceive(packet); + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( packet ); + packet=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + packet=0; + break; + } + } + + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginResult=pluginListNTS[i]->OnReceive(packet); + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( packet ); + packet=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + packet=0; + break; + } + } + + } while(packet==0); + +#ifdef _DEBUG + RakAssert( packet->data ); +#endif + + return packet; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Call this to deallocate a packet returned by Receive +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DeallocatePacket( Packet *packet ) +{ + if ( packet == 0 ) + return; + + if (packet->deleteData) + { + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + packet->~Packet(); + packetAllocationPoolMutex.Lock(); + packetAllocationPool.Release(packet,_FILE_AND_LINE_); + packetAllocationPoolMutex.Unlock(); + } + else + { + rakFree_Ex(packet, _FILE_AND_LINE_ ); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the total number of connections we are allowed +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::GetMaximumNumberOfPeers( void ) const +{ + return maximumNumberOfPeers; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). +// +// Parameters: +// target: Which connection to close +// sendDisconnectionNotification: True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. +// channel: If blockDuration > 0, the disconnect packet will be sent on this channel +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) +{ + /* + // This only be called from the user thread, for the user shutting down. + // From the network thread, this should occur because of ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST + unsigned j; + for (j=0; j < messageHandlerList.Size(); j++) + { + messageHandlerList[j]->OnClosedConnection( + target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress, + target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid, + LCR_CLOSED_BY_USER); + } + */ + + CloseConnectionInternal(target, sendDisconnectionNotification, false, orderingChannel, disconnectionNotificationPriority); + + // 12/14/09 Return ID_CONNECTION_LOST when calling CloseConnection with sendDisconnectionNotification==false, elsewise it is never returned + if (sendDisconnectionNotification==false && GetConnectionState(target)==IS_CONNECTED) + { + Packet *packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection + packet->guid = target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid; + packet->systemAddress = target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress; + packet->systemAddress.systemIndex = (SystemIndex) GetIndexFromSystemAddress(packet->systemAddress); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + packet->wasGeneratedLocally=true; // else processed twice + AddPacketToProducer(packet); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Cancel a pending connection attempt +// If we are already connected, the connection stays open +// \param[in] target Which system to cancel +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CancelConnectionAttempt( const SystemAddress target ) +{ + unsigned int i; + + // Cancel pending connection attempt, if there is one + i=0; + requestedConnectionQueueMutex.Lock(); + while (i < requestedConnectionQueue.Size()) + { + if (requestedConnectionQueue[i]->systemAddress==target) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Deleting requestedConnectionQueue %i client_handshake %x\n", i, requestedConnectionQueue[ i ]->client_handshake); + RakNet::OP_DELETE(requestedConnectionQueue[i]->client_handshake, _FILE_AND_LINE_ ); +#endif + RakNet::OP_DELETE(requestedConnectionQueue[i], _FILE_AND_LINE_ ); + requestedConnectionQueue.RemoveAtIndex(i); + break; + } + else + i++; + } + requestedConnectionQueueMutex.Unlock(); + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ConnectionState RakPeer::GetConnectionState(const AddressOrGUID systemIdentifier) +{ + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemIdentifier.systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + return IS_PENDING; + } + } + requestedConnectionQueueMutex.Unlock(); + } + + int index; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); + } + else + { + index = GetIndexFromGuid(systemIdentifier.rakNetGuid); + } + + if (index==-1) + return IS_NOT_CONNECTED; + + if (remoteSystemList[index].isActive==false) + return IS_DISCONNECTED; + + switch (remoteSystemList[index].connectMode) + { + case RemoteSystemStruct::DISCONNECT_ASAP: + return IS_DISCONNECTING; + case RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY: + return IS_SILENTLY_DISCONNECTING; + case RemoteSystemStruct::DISCONNECT_ON_NO_ACK: + return IS_DISCONNECTING; + case RemoteSystemStruct::REQUESTED_CONNECTION: + return IS_CONNECTING; + case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: + return IS_CONNECTING; + case RemoteSystemStruct::UNVERIFIED_SENDER: + return IS_CONNECTING; + case RemoteSystemStruct::CONNECTED: + return IS_CONNECTED; + default: + return IS_NOT_CONNECTED; + } + + return IS_NOT_CONNECTED; +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. +// +// Parameters +// systemAddress - The systemAddress to search for +// +// Returns +// An integer from 0 to the maximum number of peers -1, or -1 if that player is not found +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress ) const +{ + return GetIndexFromSystemAddress(systemAddress, false); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// This function is only useful for looping through all players. +// +// Parameters +// index - an integer between 0 and the maximum number of players allowed - 1. +// +// Returns +// A valid systemAddress or UNASSIGNED_SYSTEM_ADDRESS if no such player at that index +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetSystemAddressFromIndex( int index ) +{ + // remoteSystemList in user thread + //if ( index >= 0 && index < remoteSystemListSize ) + if ( index >= 0 && index < maximumNumberOfPeers ) + if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail + return remoteSystemList[ index ].systemAddress; + + return UNASSIGNED_SYSTEM_ADDRESS; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Same as GetSystemAddressFromIndex but returns RakNetGUID +// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. +// \return The RakNetGUID +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetGUID RakPeer::GetGUIDFromIndex( int index ) +{ + // remoteSystemList in user thread + //if ( index >= 0 && index < remoteSystemListSize ) + if ( index >= 0 && index < maximumNumberOfPeers ) + if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail + return remoteSystemList[ index ].guid; + + return UNASSIGNED_RAKNET_GUID; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient +// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system +// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. +// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const +{ + addresses.Clear(false, _FILE_AND_LINE_); + guids.Clear(false, _FILE_AND_LINE_); + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) + { + addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); + guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Bans an IP from connecting. Banned IPs persist between connections. +// +// Parameters +// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban +// All IP addresses starting with 128.0.0 +// milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToBanList( const char *IP, RakNet::TimeMS milliseconds ) +{ + unsigned index; + RakNet::TimeMS time = RakNet::GetTimeMS(); + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return ; + + // If this guy is already in the ban list, do nothing + index = 0; + + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + if ( strcmp( IP, banList[ index ]->IP ) == 0 ) + { + // Already in the ban list. Just update the time + if (milliseconds==0) + banList[ index ]->timeout=0; // Infinite + else + banList[ index ]->timeout=time+milliseconds; + banListMutex.Unlock(); + return; + } + } + + banListMutex.Unlock(); + + BanStruct *banStruct = RakNet::OP_NEW( _FILE_AND_LINE_ ); + banStruct->IP = (char*) rakMalloc_Ex( 16, _FILE_AND_LINE_ ); + if (milliseconds==0) + banStruct->timeout=0; // Infinite + else + banStruct->timeout=time+milliseconds; + strcpy( banStruct->IP, IP ); + banListMutex.Lock(); + banList.Insert( banStruct, _FILE_AND_LINE_ ); + banListMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allows a previously banned IP to connect. +// +// Parameters +// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban +// All IP addresses starting with 128.0.0 +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromBanList( const char *IP ) +{ + unsigned index; + BanStruct *temp; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return ; + + index = 0; + temp=0; + + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + if ( strcmp( IP, banList[ index ]->IP ) == 0 ) + { + temp = banList[ index ]; + banList[ index ] = banList[ banList.Size() - 1 ]; + banList.RemoveAtIndex( banList.Size() - 1 ); + break; + } + } + + banListMutex.Unlock(); + + if (temp) + { + rakFree_Ex(temp->IP, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allows all previously banned IPs to connect. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBanList( void ) +{ + unsigned index; + index = 0; + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + rakFree_Ex(banList[ index ]->IP, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(banList[ index ], _FILE_AND_LINE_); + } + + banList.Clear(false, _FILE_AND_LINE_); + + banListMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetLimitIPConnectionFrequency(bool b) +{ + limitConnectionFrequencyFromTheSameIP=b; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Determines if a particular IP is banned. +// +// Parameters +// IP - Complete dotted IP address +// +// Returns +// True if IP matches any IPs in the ban list, accounting for any wildcards. +// False otherwise. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsBanned( const char *IP ) +{ + unsigned banListIndex, characterIndex; + RakNet::TimeMS time; + BanStruct *temp; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return false; + + banListIndex = 0; + + if ( banList.Size() == 0 ) + return false; // Skip the mutex if possible + + time = RakNet::GetTimeMS(); + + banListMutex.Lock(); + + while ( banListIndex < banList.Size() ) + { + if (banList[ banListIndex ]->timeout>0 && banList[ banListIndex ]->timeoutIP, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + else + { + characterIndex = 0; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) + { + if ( banList[ banListIndex ]->IP[ characterIndex ] == IP[ characterIndex ] ) + { + // Equal characters + + if ( IP[ characterIndex ] == 0 ) + { + banListMutex.Unlock(); + // End of the string and the strings match + + return true; + } + + characterIndex++; + } + + else + { + if ( banList[ banListIndex ]->IP[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) + { + // End of one of the strings + break; + } + + // Characters do not match + if ( banList[ banListIndex ]->IP[ characterIndex ] == '*' ) + { + banListMutex.Unlock(); + + // Domain is banned. + return true; + } + + // Characters do not match and it is not a * + break; + } + } + + banListIndex++; + } + } + + banListMutex.Unlock(); + + // No match found. + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Send a ping to the specified connected system. +// +// Parameters: +// target - who to ping +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::Ping( const SystemAddress target ) +{ + PingInternal(target, false, UNRELIABLE); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Send a ping to the specified unconnected system. +// The remote system, if it is Initialized, will respond with ID_UNCONNECTED_PONG. +// The final ping time will be encoded in the following sizeof(RakNet::TimeMS) bytes. (Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h +// +// Parameters: +// host: Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. +// remotePort: Which port to connect to on the remote machine. +// onlyReplyOnAcceptingConnections: Only request a reply if the remote system has open connections +// connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex ) +{ + if ( host == 0 ) + return false; + + // If this assert hits then Startup wasn't called or the call failed. + RakAssert(connectionSocketIndex < socketList.Size()); + +// if ( IsActive() == false ) +// return; + + RakNet::BitStream bitStream( sizeof(unsigned char) + sizeof(RakNet::Time) ); + if ( onlyReplyOnAcceptingConnections ) + bitStream.Write((MessageID)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); + else + bitStream.Write((MessageID)ID_UNCONNECTED_PING); + + bitStream.Write(RakNet::GetTime()); + + bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + + // No timestamp for 255.255.255.255 + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->boundAddress.GetIPVersion()); + systemAddress.FixForIPVersion(socketList[realIndex]->boundAddress); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( socketList[realIndex]->s, (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, socketList[realIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[realIndex]->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the average of all ping times read for a specified target +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetAveragePing( const AddressOrGUID systemIdentifier ) +{ + int sum, quantity; + RemoteSystemStruct *remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + + for ( sum = 0, quantity = 0; quantity < PING_TIMES_ARRAY_SIZE; quantity++ ) + { + if ( remoteSystem->pingAndClockDifferential[ quantity ].pingTime == 65535 ) + break; + else + sum += remoteSystem->pingAndClockDifferential[ quantity ].pingTime; + } + + if ( quantity > 0 ) + return sum / quantity; + else + return -1; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the last ping time read for the specific player or -1 if none read yet +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetLastPing( const AddressOrGUID systemIdentifier ) const +{ + RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + +// return (int)(remoteSystem->reliabilityLayer.GetAckPing()/(RakNet::TimeUS)1000); + + if ( remoteSystem->pingAndClockDifferentialWriteIndex == 0 ) + return remoteSystem->pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE - 1 ].pingTime; + else + return remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex - 1 ].pingTime; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the lowest ping time read or -1 if none read yet +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetLowestPing( const AddressOrGUID systemIdentifier ) const +{ + RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + + return remoteSystem->lowestPing; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Ping the remote systems every so often. This is off by default +// This will work anytime +// +// Parameters: +// doPing - True to start occasional pings. False to stop them. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetOccasionalPing( bool doPing ) +{ + occasionalPing = doPing; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Length should be under 400 bytes, as a security measure against flood attacks +// Sets the data to send with an (LAN server discovery) /(offline ping) response +// See the Ping sample project for how this is used. +// data: a block of data to store, or 0 for none +// length: The length of data in bytes, or 0 for none +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetOfflinePingResponse( const char *data, const unsigned int length ) +{ + RakAssert(length < 400); + + rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); + offlinePingResponse.Reset(); + + if ( data && length > 0 ) + offlinePingResponse.Write( data, length ); + + rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns pointers to a copy of the data passed to SetOfflinePingResponse +// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() +// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() +// \sa SetOfflinePingResponse +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetOfflinePingResponse( char **data, unsigned int *length ) +{ + rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); + *data = (char*) offlinePingResponse.GetData(); + *length = (int) offlinePingResponse.GetNumberOfBytesUsed(); + rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the unique SystemAddress that represents you on the the network +// Note that unlike in previous versions, this is a struct and is not sequential +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetInternalID( const SystemAddress systemAddress, const int index ) const +{ + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + return ipList[index]; + } + else + { + +// SystemAddress returnValue; + RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, false, true ); + if (remoteSystem==0) + return UNASSIGNED_SYSTEM_ADDRESS; + + return remoteSystem->theirInternalSystemAddress[index]; + /* + sockaddr_in sa; + socklen_t len = sizeof(sa); + if (getsockname__(connectionSockets[remoteSystem->connectionSocketIndex], (sockaddr*)&sa, &len)!=0) + return UNASSIGNED_SYSTEM_ADDRESS; + returnValue.port=ntohs(sa.sin_port); + returnValue.binaryAddress=sa.sin_addr.s_addr; + return returnValue; +*/ + + + + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the unique address identifier that represents you on the the network and is based on your external +// IP / port (the IP / port the specified player uses to communicate with you) +// Note that unlike in previous versions, this is a struct and is not sequential +// +// Parameters: +// target: Which remote system you are referring to for your external ID +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetExternalID( const SystemAddress target ) const +{ + unsigned i; + SystemAddress inactiveExternalId; + + inactiveExternalId=UNASSIGNED_SYSTEM_ADDRESS; + + if (target==UNASSIGNED_SYSTEM_ADDRESS) + return firstExternalID; + + // First check for active connection with this systemAddress + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].systemAddress == target ) + { + if ( remoteSystemList[ i ].isActive ) + return remoteSystemList[ i ].myExternalSystemAddress; + else if (remoteSystemList[ i ].myExternalSystemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + inactiveExternalId=remoteSystemList[ i ].myExternalSystemAddress; + } + } + + return inactiveExternalId; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +const RakNetGUID RakPeer::GetMyGUID(void) const +{ + return myGuid; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetMyBoundAddress(const int socketIndex) +{ + DataStructures::List > sockets; + GetSockets( sockets ); + if (sockets.Size()>0) + return sockets[socketIndex]->boundAddress; + else + return UNASSIGNED_SYSTEM_ADDRESS; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +const RakNetGUID& RakPeer::GetGuidFromSystemAddress( const SystemAddress input ) const +{ + if (input==UNASSIGNED_SYSTEM_ADDRESS) + return myGuid; + + if (input.systemIndex!=(SystemIndex)-1 && input.systemIndexreliabilityLayer.SetTimeoutTime(timeMS); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RakNet::TimeMS RakPeer::GetTimeoutTime( const SystemAddress target ) +{ + if (target==UNASSIGNED_SYSTEM_ADDRESS) + { + return defaultTimeoutTime; + } + else + { + RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( target, false, true ); + + if ( remoteSystem != 0 ) + remoteSystem->reliabilityLayer.GetTimeoutTime(); + } + return defaultTimeoutTime; +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the current MTU size +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetMTUSize( const SystemAddress target ) const +{ + if (target!=UNASSIGNED_SYSTEM_ADDRESS) + { + RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(target, false, true); + if (rss) + return rss->MTUSize; + } + return defaultMTUSize; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the number of IP addresses we have +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetNumberOfAddresses( void ) +{ + + int i = 0; + + while ( ipList[ i ]!=UNASSIGNED_SYSTEM_ADDRESS ) + i++; + + return i; + + + + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns an IP address at index 0 to GetNumberOfAddresses-1 +// \param[in] index index into the list of IP addresses +// \return The local IP address at this index +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +const char* RakPeer::GetLocalIP( unsigned int index ) +{ + if (IsActive()==false) + { + // Fill out ipList structure + + SocketLayer::GetMyIP( ipList ); + + } + + + static char str[128]; + ipList[index].ToString(false,str); + return str; + + + + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Is this a local IP? +// \param[in] An IP address to check +// \return True if this is one of the IP addresses returned by GetLocalIP +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsLocalIP( const char *ip ) +{ + if (ip==0 || ip[0]==0) + return false; + + + if (strcmp(ip, "127.0.0.1")==0 || strcmp(ip, "localhost")==0) + return true; + + int num = GetNumberOfAddresses(); + int i; + for (i=0; i < num; i++) + { + if (strcmp(ip, GetLocalIP(i))==0) + return true; + } + + + + + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary +// when connection to servers with multiple IP addresses +// +// Parameters: +// allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AllowConnectionResponseIPMigration( bool allow ) +{ + allowConnectionResponseIPMigration = allow; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sends a message ID_ADVERTISE_SYSTEM to the remote unconnected system. +// This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through +// +// Requires: +// The sender and recipient must already be started via a successful call to Initialize +// +// host: Either a dotted IP address or a domain name +// remotePort: Which port to connect to on the remote machine. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex ) +{ + RakNet::BitStream bs; + bs.Write((MessageID)ID_ADVERTISE_SYSTEM); + bs.WriteAlignedBytes((const unsigned char*) data,dataLength); + return SendOutOfBand(host, remotePort, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), connectionSocketIndex ); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. +// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived +// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. +// Defaults to 0 (never return this notification) +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSplitMessageProgressInterval(int interval) +{ + RakAssert(interval>=0); + splitMessageProgressInterval=interval; + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + remoteSystemList[ i ].reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns what was passed to SetSplitMessageProgressInterval() +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetSplitMessageProgressInterval(void) const +{ + return splitMessageProgressInterval; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Set how long to wait before giving up on sending an unreliable message +// Useful if the network is clogged up. +// Set to 0 or less to never timeout. Defaults to 0. +// timeoutMS How many ms to wait before simply not sending an unreliable message. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetUnreliableTimeout(RakNet::TimeMS timeoutMS) +{ + unreliableTimeout=timeoutMS; + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + remoteSystemList[ i ].reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Send a message to host, with the IP socket option TTL set to 3 +// This message will not reach the host, but will open the router. +// Used for NAT-Punchthrough +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex ) +{ + char fakeData[2]; + fakeData[0]=0; + fakeData[1]=1; + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + SystemAddress systemAddress = socketList[realIndex]->boundAddress; + systemAddress.FromStringExplicitPort(host,remotePort); + SocketLayer::SendToTTL( socketList[realIndex]->s, (char*)fakeData, 2, systemAddress, ttl ); +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Attatches a Plugin interface to run code automatically on message receipt in the Receive call +// +// \param messageHandler Pointer to a plugin to attach +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AttachPlugin( PluginInterface2 *plugin ) +{ + bool isNotThreadsafe = plugin->UsesReliabilityLayer(); + if (isNotThreadsafe) + { + if (pluginListNTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + plugin->SetRakPeerInterface(this); + plugin->OnAttach(); + pluginListNTS.Insert(plugin, _FILE_AND_LINE_); + } + } + else + { + if (pluginListTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + plugin->SetRakPeerInterface(this); + plugin->OnAttach(); + pluginListTS.Insert(plugin, _FILE_AND_LINE_); + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Detaches a Plugin interface to run code automatically on message receipt +// +// \param messageHandler Pointer to a plugin to detach +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DetachPlugin( PluginInterface2 *plugin ) +{ + if (plugin==0) + return; + + unsigned int index; + + bool isNotThreadsafe = plugin->UsesReliabilityLayer(); + if (isNotThreadsafe) + { + index = pluginListNTS.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + // Unordered list so delete from end for speed + pluginListNTS[index]=pluginListNTS[pluginListNTS.Size()-1]; + pluginListNTS.RemoveFromEnd(); + } + } + else + { + index = pluginListTS.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + // Unordered list so delete from end for speed + pluginListTS[index]=pluginListTS[pluginListTS.Size()-1]; + pluginListTS.RemoveFromEnd(); + } + } + plugin->OnDetach(); + plugin->SetRakPeerInterface(0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Put a packet back at the end of the receive queue in case you don't want to deal with it immediately +// +// packet The packet you want to push back. +// pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PushBackPacket( Packet *packet, bool pushAtHead) +{ + if (packet==0) + return; + + unsigned i; + for (i=0; i < pluginListTS.Size(); i++) + pluginListTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); + + packetReturnMutex.Lock(); + if (pushAtHead) + packetReturnQueue.PushAtHead(packet,0,_FILE_AND_LINE_); + else + packetReturnQueue.Push(packet,_FILE_AND_LINE_); + packetReturnMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress) +{ + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = 0; + bcs->systemIdentifier.systemAddress=systemAddress; + bcs->systemIdentifier.rakNetGuid=guid; + bcs->command=BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS; + bufferedCommands.Push(bcs); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +Packet* RakPeer::AllocatePacket(unsigned dataSize) +{ + return AllocPacket(dataSize, _FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetSmartPtr RakPeer::GetSocket( const SystemAddress target ) +{ + // Send a query to the thread to get the socket, and return when we got it + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; + bcs->systemIdentifier=target; + bcs->data=0; + bufferedCommands.Push(bcs); + + // Block up to one second to get the socket, although it should actually take virtually no time + SocketQueryOutput *sqo; + RakNet::TimeMS stopWaiting = RakNet::GetTimeMS()+1000; + DataStructures::List > output; + while (RakNet::GetTimeMS() < stopWaiting) + { + if (isMainLoopThreadActive==false) + return RakNetSmartPtr(); + + RakSleep(0); + + sqo = socketQueryOutput.Pop(); + if (sqo) + { + output=sqo->sockets; + sqo->sockets.Clear(false, _FILE_AND_LINE_); + socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); + if (output.Size()) + return output[0]; + break; + } + } + return RakNetSmartPtr(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSockets( DataStructures::List > &sockets ) +{ + sockets.Clear(false, _FILE_AND_LINE_); + + // Send a query to the thread to get the socket, and return when we got it + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; + bcs->systemIdentifier=UNASSIGNED_SYSTEM_ADDRESS; + bcs->data=0; + bufferedCommands.Push(bcs); + + // Block up to one second to get the socket, although it should actually take virtually no time + SocketQueryOutput *sqo; + RakNetSmartPtr output; + while (1) + { + if (isMainLoopThreadActive==false) + return; + + RakSleep(0); + + sqo = socketQueryOutput.Pop(); + if (sqo) + { + sockets=sqo->sockets; + sqo->sockets.Clear(false, _FILE_AND_LINE_); + socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); + return; + } + } + return; +} +void RakPeer::ReleaseSockets( DataStructures::List > &sockets ) +{ + sockets.Clear(false,_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Adds simulated ping and packet loss to the outgoing data flow. +// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance) +{ +#ifdef _DEBUG + if (remoteSystemList) + { + unsigned short i; + for (i=0; i < maximumNumberOfPeers; i++) + //for (i=0; i < remoteSystemListSize; i++) + remoteSystemList[i].reliabilityLayer.ApplyNetworkSimulator(packetloss, minExtraPing, extraPingVariance); + } + + _packetloss=packetloss; + _minExtraPing=minExtraPing; + _extraPingVariance=extraPingVariance; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void RakPeer::SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ) +{ + maxOutgoingBPS=maxBitsPerSecond; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns if you previously called ApplyNetworkSimulator +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsNetworkSimulatorActive( void ) +{ +#ifdef _DEBUG + return _packetloss>0 || _minExtraPing>0 || _extraPingVariance>0; +#else + return false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::WriteOutOfBandHeader(RakNet::BitStream *bitStream) +{ + bitStream->Write((MessageID)ID_OUT_OF_BAND_INTERNAL); + bitStream->Write(myGuid); + bitStream->WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData) +{ + userUpdateThreadPtr=_userUpdateThreadPtr; + userUpdateThreadData=_userUpdateThreadData; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex ) +{ + if ( IsActive() == false ) + return false; + + if (host==0 || host[0]==0) + return false; + + // If this assert hits then Startup wasn't called or the call failed. + RakAssert(connectionSocketIndex < socketList.Size()); + + // This is a security measure. Don't send data longer than this value + RakAssert(dataLength <= (MAX_OFFLINE_DATA_LENGTH + sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID))); + + if (host==0) + return false; + + // 34 bytes + RakNet::BitStream bitStream; + WriteOutOfBandHeader(&bitStream); + + if (dataLength>0) + { + bitStream.Write(data, dataLength); + } + + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->boundAddress.GetIPVersion()); + systemAddress.FixForIPVersion(socketList[realIndex]->boundAddress); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); + + SocketLayer::SendTo( socketList[realIndex]->s, (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, socketList[realIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[realIndex]->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetStatistics * RakPeer::GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns ) +{ + static RakNetStatistics staticStatistics; + RakNetStatistics *systemStats; + if (rns==0) + systemStats=&staticStatistics; + else + systemStats=rns; + + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + bool firstWrite=false; + // Return a crude sum + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].isActive) + { + RakNetStatistics rnsTemp; + remoteSystemList[ i ].reliabilityLayer.GetStatistics(&rnsTemp); + + if (firstWrite==false) + { + memcpy(systemStats, &rnsTemp, sizeof(RakNetStatistics)); + firstWrite=true; + } + else + (*systemStats)+=rnsTemp; + } + } + return systemStats; + } + else + { + RemoteSystemStruct * rss; + rss = GetRemoteSystemFromSystemAddress( systemAddress, false, false ); + if ( rss && endThreads==false ) + { + rss->reliabilityLayer.GetStatistics(systemStats); + return systemStats; + } + } + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::GetStatistics( const int index, RakNetStatistics *rns ) +{ + if (index < maximumNumberOfPeers && remoteSystemList[ index ].isActive) + { + remoteSystemList[ index ].reliabilityLayer.GetStatistics(rns); + return true; + } + return false; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetReceiveBufferSize(void) +{ + unsigned int size; + packetReturnMutex.Lock(); + size=packetReturnQueue.Size(); + packetReturnMutex.Unlock(); + return size; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const +{ + unsigned i; + + if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) + return -1; + + if (systemAddress.systemIndex!=(SystemIndex)-1 && systemAddress.systemIndex < maximumNumberOfPeers && remoteSystemList[systemAddress.systemIndex].systemAddress==systemAddress && remoteSystemList[ systemAddress.systemIndex ].isActive) + return systemAddress.systemIndex; + + if (calledFromNetworkThread) + { + return GetRemoteSystemIndex(systemAddress); + } + else + { + // remoteSystemList in user and network thread + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].systemAddress == systemAddress ) + return i; + + // If no active results found, try previously active results. + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].systemAddress == systemAddress ) + return i; + } + + return -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromGuid( const RakNetGUID guid ) +{ + unsigned i; + + if ( guid == UNASSIGNED_RAKNET_GUID ) + return -1; + + if (guid.systemIndex!=(SystemIndex)-1 && guid.systemIndex < maximumNumberOfPeers && remoteSystemList[guid.systemIndex].guid==guid && remoteSystemList[ guid.systemIndex ].isActive) + return guid.systemIndex; + + // remoteSystemList in user and network thread + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].guid == guid ) + return i; + + // If no active results found, try previously active results. + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].guid == guid ) + return i; + + return -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +#if LIBCAT_SECURITY==1 +bool RakPeer::GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey) +{ + CAT_AUDIT_PRINTF("AUDIT: In GenerateConnectionRequestChallenge()\n"); + + rcs->client_handshake = 0; + rcs->publicKeyMode = PKM_INSECURE_CONNECTION; + + if (!publicKey) return true; + + switch (publicKey->publicKeyMode) + { + default: + case PKM_INSECURE_CONNECTION: + break; + + case PKM_ACCEPT_ANY_PUBLIC_KEY: + CAT_OBJCLR(rcs->remote_public_key); + rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); + + rcs->publicKeyMode = PKM_ACCEPT_ANY_PUBLIC_KEY; + break; + + case PKM_USE_TWO_WAY_AUTHENTICATION: + if (publicKey->myPublicKey == 0 || publicKey->myPrivateKey == 0 || + publicKey->remoteServerPublicKey == 0) + { + return false; + } + + rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); + memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); + + if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || + !rcs->client_handshake->SetIdentity(publicKey->myPublicKey, publicKey->myPrivateKey) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object with identity for this RequestedConnectionStruct\n"); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + return false; + } + + CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object with identity for this RequestedConnectionStruct -- pre-generated challenge\n"); + + rcs->publicKeyMode = PKM_USE_TWO_WAY_AUTHENTICATION; + break; + + case PKM_USE_KNOWN_PUBLIC_KEY: + if (publicKey->remoteServerPublicKey == 0) + return false; + + rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); + memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); + + if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object for this RequestedConnectionStruct\n"); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + return false; + } + + CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object for this RequestedConnectionStruct -- pre-generated challenge\n"); + + rcs->publicKeyMode = PKM_USE_KNOWN_PUBLIC_KEY; + break; + } + + return true; +} +#endif +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ) +{ + RakAssert(passwordDataLength <= 256); + RakAssert(remotePort!=0); + SystemAddress systemAddress; + if (!systemAddress.FromStringExplicitPort(host,remotePort,socketList[connectionSocketIndex]->boundAddress.GetIPVersion())) + return CANNOT_RESOLVE_DOMAIN_NAME; + + // Already connected? + if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) + return ALREADY_CONNECTED_TO_ENDPOINT; + + //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); + RequestedConnectionStruct *rcs = RakNet::OP_NEW(_FILE_AND_LINE_); + + rcs->systemAddress=systemAddress; + rcs->nextRequestTime=RakNet::GetTimeMS(); + rcs->requestsMade=0; + rcs->data=0; + rcs->extraData=extraData; + rcs->socketIndex=connectionSocketIndex; + rcs->actionToTake=RequestedConnectionStruct::CONNECT; + rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; + rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; + memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); + rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; + rcs->timeoutTime=timeoutTime; + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: In SendConnectionRequest()\n"); + if (!GenerateConnectionRequestChallenge(rcs,publicKey)) + return SECURITY_INITIALIZATION_FAILED; +#else + (void) publicKey; +#endif + + // Return false if already pending, else push on queue + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + // Not necessary + //RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; + } + } + requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + + return CONNECTION_ATTEMPT_STARTED; +} +ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime, RakNetSmartPtr socket ) +{ + RakAssert(passwordDataLength <= 256); + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort); + + // Already connected? + if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) + return ALREADY_CONNECTED_TO_ENDPOINT; + + //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); + RequestedConnectionStruct *rcs = RakNet::OP_NEW(_FILE_AND_LINE_); + + rcs->systemAddress=systemAddress; + rcs->nextRequestTime=RakNet::GetTimeMS(); + rcs->requestsMade=0; + rcs->data=0; + rcs->extraData=extraData; + rcs->socketIndex=connectionSocketIndex; + rcs->actionToTake=RequestedConnectionStruct::CONNECT; + rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; + rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; + memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); + rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; + rcs->timeoutTime=timeoutTime; + rcs->socket=socket; + +#if LIBCAT_SECURITY==1 + if (!GenerateConnectionRequestChallenge(rcs,publicKey)) + return SECURITY_INITIALIZATION_FAILED; +#else + (void) publicKey; +#endif + + // Return false if already pending, else push on queue + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + // Not necessary + //RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; + } + } + requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + + return CONNECTION_ATTEMPT_STARTED; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ValidateRemoteSystemLookup(void) const +{ +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const +{ + if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return GetRemoteSystemFromGUID(systemIdentifier.rakNetGuid, onlyActive); + else + return GetRemoteSystemFromSystemAddress(systemIdentifier.systemAddress, calledFromNetworkThread, onlyActive); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const +{ + unsigned i; + + if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) + return 0; + + if (calledFromNetworkThread) + { + unsigned int index = GetRemoteSystemIndex(systemAddress); + if (index!=(unsigned int) -1) + { + if (onlyActive==false || remoteSystemList[ index ].isActive==true ) + { + RakAssert(remoteSystemList[index].systemAddress==systemAddress); + return remoteSystemList + index; + } + } + } + else + { + int deadConnectionIndex=-1; + + // Active connections take priority. But if there are no active connections, return the first systemAddress match found + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].systemAddress == systemAddress) + { + if ( remoteSystemList[ i ].isActive ) + return remoteSystemList + i; + else if (deadConnectionIndex==-1) + deadConnectionIndex=i; + } + } + + if (deadConnectionIndex!=-1 && onlyActive==false) + return remoteSystemList + deadConnectionIndex; + } + + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const +{ + if (guid==UNASSIGNED_RAKNET_GUID) + return 0; + + unsigned i; + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].guid == guid && (onlyActive==false || remoteSystemList[ i ].isActive)) + { + return remoteSystemList + i; + } + } + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize ) +{ + RakNet::BitStream bs((unsigned char*) data,byteSize,false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID guid; + bs.Read(guid); + RakNet::Time incomingTimestamp; + bs.Read(incomingTimestamp); + unsigned char doSecurity; + bs.Read(doSecurity); + +#if LIBCAT_SECURITY==1 + unsigned char doClientKey; + if (_using_security) + { + // Ignore message on bad state + if (doSecurity != 1 || !remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()) + return; + + // Validate client proof of key + unsigned char proof[cat::EasyHandshake::PROOF_BYTES]; + bs.ReadAlignedBytes(proof, sizeof(proof)); + if (!remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->ValidateProof(proof, sizeof(proof))) + { + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + + CAT_OBJCLR(remoteSystem->client_public_key); + + bs.Read(doClientKey); + + // Check if client wants to prove identity + if (doClientKey == 1) + { + // Read identity proof + unsigned char ident[cat::EasyHandshake::IDENTITY_BYTES]; + bs.ReadAlignedBytes(ident, sizeof(ident)); + + // If we are listening to these proofs, + if (_require_client_public_key) + { + // Validate client identity + if (!_server_handshake->VerifyInitiatorIdentity(remoteSystem->answer, ident, remoteSystem->client_public_key)) + { + RakNet::BitStream bitStream; + bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect + bitStream.Write((unsigned char)2); // Indicate client identity is invalid + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + } + + // Otherwise ignore the client public key + } + else + { + // If no client key was provided but it is required, + if (_require_client_public_key) + { + RakNet::BitStream bitStream; + bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect + bitStream.Write((unsigned char)1); // Indicate client identity is missing + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + } + } +#endif // LIBCAT_SECURITY + + unsigned char *password = bs.GetData()+BITS_TO_BYTES(bs.GetReadOffset()); + int passwordLength = byteSize - BITS_TO_BYTES(bs.GetReadOffset()); + if ( incomingPasswordLength != passwordLength || + memcmp( password, incomingPassword, incomingPasswordLength ) != 0 ) + { + CAT_AUDIT_PRINTF("AUDIT: Invalid password\n"); + // This one we only send once since we don't care if it arrives. + RakNet::BitStream bitStream; + bitStream.Write((MessageID)ID_INVALID_PASSWORD); + bitStream.Write(GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + + // OK + remoteSystem->connectMode=RemoteSystemStruct::HANDLING_CONNECTION_REQUEST; + + OnConnectionRequest( remoteSystem, incomingTimestamp ); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, RakNet::Time incomingTimestamp ) +{ + RakNet::BitStream bitStream; + bitStream.Write((MessageID)ID_CONNECTION_REQUEST_ACCEPTED); + bitStream.Write(remoteSystem->systemAddress); + SystemIndex systemIndex = (SystemIndex) GetIndexFromSystemAddress( remoteSystem->systemAddress, true ); + RakAssert(systemIndex!=65535); + bitStream.Write(systemIndex); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + bitStream.Write(ipList[i]); + bitStream.Write(incomingTimestamp); + bitStream.Write(RakNet::GetTime()); + + SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, remoteSystem->systemAddress, false, false, RakNet::GetTimeUS(), 0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) +{ + RakNet::BitStream temp( sizeof(unsigned char) ); + temp.Write( (MessageID)ID_DISCONNECTION_NOTIFICATION ); + if (performImmediate) + { + SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, RELIABLE_ORDERED, orderingChannel, systemAddress, false, false, RakNet::GetTimeUS(), 0); + RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(systemAddress, true, true); + rss->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; + } + else + { + SendBuffered((const char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, RELIABLE_ORDERED, orderingChannel, systemAddress, false, RemoteSystemStruct::DISCONNECT_ASAP, 0); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::GetNumberOfRemoteInitiatedConnections( void ) const +{ + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + unsigned short numberOfIncomingConnections; + numberOfIncomingConnections = 0; + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED && + (activeSystemList[i])->weInitiatedTheConnection==false + ) + { + numberOfIncomingConnections++; + } + } + return numberOfIncomingConnections; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct * RakPeer::AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSmartPtr incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ) +{ + RemoteSystemStruct * remoteSystem; + unsigned i,j,assignedIndex; + RakNet::TimeMS time = RakNet::GetTimeMS(); +#ifdef _DEBUG + RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); +#endif + + if (limitConnectionFrequencyFromTheSameIP) + { + if (IsLoopbackAddress(systemAddress,false)==false) + { + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if ( remoteSystemList[ i ].isActive==true && + remoteSystemList[ i ].systemAddress.EqualsExcludingPort(systemAddress) && + time >= remoteSystemList[ i ].connectionTime && + time - remoteSystemList[ i ].connectionTime < 100 + ) + { + // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots + // Ignore connection attempts if this IP address connected within the last 100 milliseconds + *thisIPConnectedRecently=true; + ValidateRemoteSystemLookup(); + return 0; + } + } + } + } + + // Don't use a different port than what we received on + bindingAddress.CopyPort(incomingRakNetSocket->boundAddress); + + *thisIPConnectedRecently=false; + for ( assignedIndex = 0; assignedIndex < maximumNumberOfPeers; assignedIndex++ ) + { + if ( remoteSystemList[ assignedIndex ].isActive==false ) + { + remoteSystem=remoteSystemList+assignedIndex; + ReferenceRemoteSystem(systemAddress, assignedIndex); + remoteSystem->MTUSize=defaultMTUSize; + remoteSystem->guid=guid; + remoteSystem->isActive = true; // This one line causes future incoming packets to go through the reliability layer + // Reserve this reliability layer for ourselves. + if (incomingMTU > remoteSystem->MTUSize) + remoteSystem->MTUSize=incomingMTU; + remoteSystem->reliabilityLayer.Reset(true, remoteSystem->MTUSize, useSecurity); + remoteSystem->reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); + remoteSystem->reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); + remoteSystem->reliabilityLayer.SetTimeoutTime(defaultTimeoutTime); + AddToActiveSystemList(assignedIndex); + if (incomingRakNetSocket->boundAddress==bindingAddress) + { + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + else + { + char str[256]; + bindingAddress.ToString(true,str); + // See if this is an internal IP address. + // If so, force binding on it so we reply on the same IP address as they sent to. + unsigned int ipListIndex, foundIndex=(unsigned int)-1; + + for (ipListIndex=0; ipListIndex < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ipListIndex++) + { + if (ipList[ipListIndex]==UNASSIGNED_SYSTEM_ADDRESS) + break; + + if (bindingAddress.EqualsExcludingPort(ipList[ipListIndex])) + { + foundIndex=ipListIndex; + break; + } + } + + // 06/26/09 Unconfirmed report that Vista firewall blocks the reply if we force a binding + // For now use the incoming socket only + // Originally this code was to force a machine with multiple IP addresses to reply back on the IP + // that the datagram came in on + if (1 || foundIndex==(unsigned int)-1) + { + // Must not be an internal LAN address. Just use whatever socket it came in on + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + else + { + // Force binding + unsigned int socketListIndex; + for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) + { + if (socketList[socketListIndex]->boundAddress==bindingAddress) + { + // Force binding with existing socket + remoteSystem->rakNetSocket=socketList[socketListIndex]; + break; + } + } + + if (socketListIndex==socketList.Size()) + { + char ipListFoundIndexStr[128]; + ipList[foundIndex].ToString(false,str); + + // Force binding with new socket + RakNetSmartPtr rns(RakNet::OP_NEW(_FILE_AND_LINE_)); + if (incomingRakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2==0) + rns->s = (unsigned int) SocketLayer::CreateBoundSocket( bindingAddress.GetPort(), true, ipListFoundIndexStr, 0, incomingRakNetSocket->extraSocketOptions, incomingRakNetSocket->socketFamily ); + else + rns->s = (unsigned int) SocketLayer::CreateBoundSocket_PS3Lobby( bindingAddress.GetPort(), true, ipListFoundIndexStr, incomingRakNetSocket->socketFamily ); + if ((SOCKET)rns->s==(SOCKET)-1) + { + // Can't bind. Just use whatever socket it came in on + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + else + { + rns->boundAddress=bindingAddress; + rns->remotePortRakNetWasStartedOn_PS3_PSP2=incomingRakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2; + rns->extraSocketOptions=incomingRakNetSocket->extraSocketOptions; + rns->userConnectionSocketIndex=(unsigned int)-1; + socketList.Push(rns, _FILE_AND_LINE_ ); + remoteSystem->rakNetSocket=rns; + + RakPeerAndIndex rpai; + rpai.remotePortRakNetWasStartedOn_PS3=rns->remotePortRakNetWasStartedOn_PS3_PSP2; + rpai.extraSocketOptions=rns->extraSocketOptions; + rpai.s=rns->s; + rpai.rakPeer=this; +#ifdef _WIN32 + int highPriority=THREAD_PRIORITY_ABOVE_NORMAL; +#else + int highPriority=-10; +#endif + +//#if !defined(_XBOX) && !defined(X360) + highPriority=0; +//#endif + + + + + + + + RakNet::RakThread::Create(RecvFromLoop, &rpai, highPriority); + + RakSleep(10); + + + /* +#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + if (threadSleepTimer>0) + { + rns->recvEvent=CreateEvent(0,FALSE,FALSE,0); + WSAEventSelect(rns->s,rns->recvEvent,FD_READ); + } +#endif + */ + } + } + } + } + + for ( j = 0; j < (unsigned) PING_TIMES_ARRAY_SIZE; j++ ) + { + remoteSystem->pingAndClockDifferential[ j ].pingTime = 65535; + remoteSystem->pingAndClockDifferential[ j ].clockDifferential = 0; + } + + remoteSystem->connectMode=connectionMode; + remoteSystem->pingAndClockDifferentialWriteIndex = 0; + remoteSystem->lowestPing = 65535; + remoteSystem->nextPingTime = 0; // Ping immediately + remoteSystem->weInitiatedTheConnection = false; + remoteSystem->connectionTime = time; + remoteSystem->myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystem->lastReliableSend=time; + +#ifdef _DEBUG + int indexLoopupCheck=GetIndexFromSystemAddress( systemAddress, true ); + if ((int) indexLoopupCheck!=(int) assignedIndex) + { + RakAssert((int) indexLoopupCheck==(int) assignedIndex); + } +#endif + + return remoteSystem; + } + } + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Adjust the first four bytes (treated as unsigned int) of the pointer +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const +{ +#ifdef _DEBUG + RakAssert( IsActive() ); + RakAssert( data ); +#endif + + RakNet::BitStream timeBS( data, sizeof(RakNet::Time), false); + RakNet::Time encodedTimestamp; + timeBS.Read(encodedTimestamp); + + encodedTimestamp = encodedTimestamp - GetBestClockDifferential( systemAddress ); + timeBS.SetWriteOffset(0); + timeBS.Write(encodedTimestamp); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm +RakNet::Time RakPeer::GetBestClockDifferential( const SystemAddress systemAddress ) const +{ + int counter, lowestPingSoFar; + RakNet::Time clockDifferential; + RemoteSystemStruct *remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + + if ( remoteSystem == 0 ) + return 0; + + lowestPingSoFar = 65535; + + clockDifferential = 0; + + for ( counter = 0; counter < PING_TIMES_ARRAY_SIZE; counter++ ) + { + if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime == 65535 ) + break; + + if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime < lowestPingSoFar ) + { + clockDifferential = remoteSystem->pingAndClockDifferential[ counter ].clockDifferential; + lowestPingSoFar = remoteSystem->pingAndClockDifferential[ counter ].pingTime; + } + } + + return clockDifferential; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::RemoteSystemLookupHashIndex(const SystemAddress &sa) const +{ + return SystemAddress::ToInteger(sa) % ((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex) +{ +// #ifdef _DEBUG +// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// RakAssert(hashIndex==remoteSystemIndex); +// } +// } +// #endif + + + SystemAddress oldAddress = remoteSystemList[remoteSystemListIndex].systemAddress; + if (oldAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + // The system might be active if rerouting +// RakAssert(remoteSystemList[remoteSystemListIndex].isActive==false); + + // Remove the reference if the reference is pointing to this inactive system + if (GetRemoteSystem(oldAddress)==&remoteSystemList[remoteSystemListIndex]) + DereferenceRemoteSystem(oldAddress); + } + DereferenceRemoteSystem(sa); + +// #ifdef _DEBUG +// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// if (hashIndex!=remoteSystemIndex) +// { +// RakAssert(hashIndex==remoteSystemIndex); +// } +// } +// } +// #endif + + + remoteSystemList[remoteSystemListIndex].systemAddress=sa; + + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *rsi; + rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); + if (remoteSystemLookup[hashIndex]==0) + { + rsi->next=0; + rsi->index=remoteSystemListIndex; + remoteSystemLookup[hashIndex]=rsi; + } + else + { + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + while (cur->next!=0) + { + cur=cur->next; + } + + rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); + rsi->next=0; + rsi->index=remoteSystemListIndex; + cur->next=rsi; + } + +// #ifdef _DEBUG +// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// RakAssert(hashIndex==remoteSystemIndex); +// } +// } +// #endif + + + RakAssert(GetRemoteSystemIndex(sa)==remoteSystemListIndex); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DereferenceRemoteSystem(const SystemAddress &sa) +{ + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + RemoteSystemIndex *last = 0; + while (cur!=0) + { + if (remoteSystemList[cur->index].systemAddress==sa) + { + if (last==0) + { + remoteSystemLookup[hashIndex]=cur->next; + } + else + { + last->next=cur->next; + } + remoteSystemIndexPool.Release(cur,_FILE_AND_LINE_); + break; + } + last=cur; + cur=cur->next; + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetRemoteSystemIndex(const SystemAddress &sa) const +{ + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + while (cur!=0) + { + if (remoteSystemList[cur->index].systemAddress==sa) + return cur->index; + cur=cur->next; + } + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct* RakPeer::GetRemoteSystem(const SystemAddress &sa) const +{ + unsigned int remoteSystemIndex = GetRemoteSystemIndex(sa); + if (remoteSystemIndex==(unsigned int)-1) + return 0; + return remoteSystemList + remoteSystemIndex; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearRemoteSystemLookup(void) +{ + remoteSystemIndexPool.Clear(_FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(remoteSystemLookup,_FILE_AND_LINE_); + remoteSystemLookup=0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToActiveSystemList(unsigned int remoteSystemListIndex) +{ + activeSystemList[activeSystemListSize++]=remoteSystemList+remoteSystemListIndex; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromActiveSystemList(const SystemAddress &sa) +{ + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + RemoteSystemStruct *rss=activeSystemList[i]; + if (rss->systemAddress==sa) + { + activeSystemList[i]=activeSystemList[activeSystemListSize-1]; + activeSystemListSize--; + return; + } + } + RakAssert("activeSystemList invalid, entry not found in RemoveFromActiveSystemList. Ensure that AddToActiveSystemList and RemoveFromActiveSystemList are called by the same thread." && 0); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/* +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::LookupIndexUsingHashIndex(const SystemAddress &sa) const +{ + unsigned int scanCount=0; + unsigned int index = RemoteSystemLookupHashIndex(sa); + if (remoteSystemLookup[index].index==(unsigned int)-1) + return (unsigned int) -1; + while (remoteSystemList[remoteSystemLookup[index].index].systemAddress!=sa) + { + if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + index=0; + if (++scanCount>(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + return (unsigned int) -1; + if (remoteSystemLookup[index].index==-1) + return (unsigned int) -1; + } + return index; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const +{ + unsigned int index = LookupIndexUsingHashIndex(sa); + if (index!=(unsigned int) -1) + { + return remoteSystemLookup[index].index; + } + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const +{ +// unsigned int collisionCount=0; + unsigned int index = RemoteSystemLookupHashIndex(sa); + while (remoteSystemLookup[index].index!=(unsigned int)-1) + { + if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + index=0; +// collisionCount++; + } +// printf("%i collisions. Using index %i\n", collisionCount, index); + return index; +} +*/ +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const +{ + if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return systemIdentifier.rakNetGuid==myGuid; + + for (int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS; i++) + { + if (matchPort) + { + if (ipList[i]==systemIdentifier.systemAddress) + return true; + } + else + { + if (ipList[i].EqualsExcludingPort(systemIdentifier.systemAddress)) + return true; + } + } + + return (matchPort==true && systemIdentifier.systemAddress==firstExternalID) || + (matchPort==false && systemIdentifier.systemAddress.EqualsExcludingPort(firstExternalID)); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetLoopbackAddress(void) const +{ + + return ipList[0]; + + + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AllowIncomingConnections(void) const +{ + return GetNumberOfRemoteInitiatedConnections() < GetMaximumIncomingConnections(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PingInternal( const SystemAddress target, bool performImmediate, PacketReliability reliability ) +{ + if ( IsActive() == false ) + return ; + + RakNet::BitStream bitStream(sizeof(unsigned char)+sizeof(RakNet::Time)); + bitStream.Write((MessageID)ID_CONNECTED_PING); + bitStream.Write(RakNet::GetTime()); + if (performImmediate) + SendImmediate( (char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, reliability, 0, target, false, false, RakNet::GetTimeUS(), 0 ); + else + Send( &bitStream, IMMEDIATE_PRIORITY, reliability, 0, target, false ); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) +{ +#ifdef _DEBUG + RakAssert(orderingChannel < 32); +#endif + + if (systemIdentifier.IsUndefined()) + return; + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + SystemAddress target; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + target=systemIdentifier.systemAddress; + } + else + { + target=GetSystemAddressFromGuid(systemIdentifier.rakNetGuid); + } + + if (target!=UNASSIGNED_SYSTEM_ADDRESS && performImmediate) + target.FixForIPVersion(socketList[0]->boundAddress); + + if (sendDisconnectionNotification) + { + NotifyAndFlagForShutdown(target, performImmediate, orderingChannel, disconnectionNotificationPriority); + } + else + { + if (performImmediate) + { + unsigned int index = GetRemoteSystemIndex(target); + if (index!=(unsigned int) -1) + { + if ( remoteSystemList[index].isActive ) + { + RemoveFromActiveSystemList(target); + + // Found the index to stop + remoteSystemList[index].isActive = false; + + remoteSystemList[index].guid=UNASSIGNED_RAKNET_GUID; + + // Reserve this reliability layer for ourselves + //remoteSystemList[ remoteSystemLookup[index].index ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; + + // Clear any remaining messages + remoteSystemList[index].reliabilityLayer.Reset(false, remoteSystemList[index].MTUSize, false); + + // Not using this socket + remoteSystemList[index].rakNetSocket.SetNull(); + } + } + } + else + { + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_CLOSE_CONNECTION; + bcs->systemIdentifier=target; + bcs->data=0; + bcs->orderingChannel=orderingChannel; + bcs->priority=disconnectionNotificationPriority; + bufferedCommands.Push(bcs); + } + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendBuffered( const char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) +{ + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = (char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES(numberOfBitsToSend), _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy + if (bcs->data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + return; + } + memcpy(bcs->data, data, (size_t) BITS_TO_BYTES(numberOfBitsToSend)); + bcs->numberOfBitsToSend=numberOfBitsToSend; + bcs->priority=priority; + bcs->reliability=reliability; + bcs->orderingChannel=orderingChannel; + bcs->systemIdentifier=systemIdentifier; + bcs->broadcast=broadcast; + bcs->connectionMode=connectionMode; + bcs->receipt=receipt; + bcs->command=BufferedCommandStruct::BCS_SEND; + bufferedCommands.Push(bcs); + + if (priority==IMMEDIATE_PRIORITY) + { + // Forces pending sends to go out now, rather than waiting to the next update interval + quitAndDataEvents.SetEvent(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendBufferedList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) +{ + BufferedCommandStruct *bcs; + unsigned int totalLength=0; + unsigned int lengthOffset; + int i; + for (i=0; i < numParameters; i++) + { + if (lengths[i]>0) + totalLength+=lengths[i]; + } + if (totalLength==0) + return; + + char *dataAggregate; + dataAggregate = (char*) rakMalloc_Ex( (size_t) totalLength, _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy + if (dataAggregate==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return; + } + for (i=0, lengthOffset=0; i < numParameters; i++) + { + if (lengths[i]>0) + { + memcpy(dataAggregate+lengthOffset, data[i], lengths[i]); + lengthOffset+=lengths[i]; + } + } + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback(dataAggregate,totalLength); + rakFree_Ex(dataAggregate,_FILE_AND_LINE_); + return; + } + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = dataAggregate; + bcs->numberOfBitsToSend=BYTES_TO_BITS(totalLength); + bcs->priority=priority; + bcs->reliability=reliability; + bcs->orderingChannel=orderingChannel; + bcs->systemIdentifier=systemIdentifier; + bcs->broadcast=broadcast; + bcs->connectionMode=connectionMode; + bcs->receipt=receipt; + bcs->command=BufferedCommandStruct::BCS_SEND; + bufferedCommands.Push(bcs); + + if (priority==IMMEDIATE_PRIORITY) + { + // Forces pending sends to go out now, rather than waiting to the next update interval + quitAndDataEvents.SetEvent(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::SendImmediate( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, RakNet::TimeUS currentTime, uint32_t receipt ) +{ + unsigned *sendList; + unsigned sendListSize; + bool callerDataAllocationUsed; + unsigned int remoteSystemIndex, sendListIndex; // Iterates into the list of remote systems +// unsigned numberOfBytesUsed = (unsigned) BITS_TO_BYTES(numberOfBitsToSend); + callerDataAllocationUsed=false; + + sendListSize=0; + + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + remoteSystemIndex=GetIndexFromSystemAddress( systemIdentifier.systemAddress, true ); + else if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + remoteSystemIndex=GetSystemIndexFromGuid(systemIdentifier.rakNetGuid); + else + remoteSystemIndex=(unsigned int) -1; + + // 03/06/06 - If broadcast is false, use the optimized version of GetIndexFromSystemAddress + if (broadcast==false) + { + if (remoteSystemIndex==(unsigned int) -1) + { +#ifdef _DEBUG +// int debugIndex = GetRemoteSystemIndex(systemIdentifier.systemAddress); +#endif + return false; + } + + + sendList=(unsigned *)alloca(sizeof(unsigned)); + + + + + if (remoteSystemList[remoteSystemIndex].isActive && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ON_NO_ACK) + { + sendList[0]=remoteSystemIndex; + sendListSize=1; + } + } + else + { + + //sendList=(unsigned *)alloca(sizeof(unsigned)*remoteSystemListSize); + sendList=(unsigned *)alloca(sizeof(unsigned)*maximumNumberOfPeers); + + + + + + // remoteSystemList in network thread + unsigned int idx; + for ( idx = 0; idx < maximumNumberOfPeers; idx++ ) + { + if (remoteSystemIndex!=(unsigned int) -1 && idx==remoteSystemIndex) + continue; + + if ( remoteSystemList[ idx ].isActive && remoteSystemList[ idx ].systemAddress != UNASSIGNED_SYSTEM_ADDRESS ) + sendList[sendListSize++]=idx; + } + } + + if (sendListSize==0) + { + + + + return false; + } + + for (sendListIndex=0; sendListIndex < sendListSize; sendListIndex++) + { + // Send may split the packet and thus deallocate data. Don't assume data is valid if we use the callerAllocationData + bool useData = useCallerDataAllocation && callerDataAllocationUsed==false && sendListIndex+1==sendListSize; + remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( data, numberOfBitsToSend, priority, reliability, orderingChannel, useData==false, remoteSystemList[sendList[sendListIndex]].MTUSize, currentTime, receipt ); + if (useData) + callerDataAllocationUsed=true; + + if (reliability==RELIABLE || + reliability==RELIABLE_ORDERED || + reliability==RELIABLE_SEQUENCED || + reliability==RELIABLE_WITH_ACK_RECEIPT || + reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT +// || +// reliability==RELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + remoteSystemList[sendList[sendListIndex]].lastReliableSend=(RakNet::TimeMS)(currentTime/(RakNet::TimeUS)1000); + } + + + + + + // Return value only meaningful if true was passed for useCallerDataAllocation. Means the reliability layer used that data copy, so the caller should not deallocate it + return callerDataAllocationUsed; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ResetSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + sendReceiptSerial=1; + sendReceiptSerialMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::OnConnectedPong(RakNet::Time sendPingTime, RakNet::Time sendPongTime, RemoteSystemStruct *remoteSystem) +{ + RakNet::Time ping; +// RakNet::TimeMS lastPing; + RakNet::Time time = RakNet::GetTime(); // Update the time value to be accurate + if (time > sendPingTime) + ping = time - sendPingTime; + else + ping=0; + +// lastPing = remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime; + + remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime = ( unsigned short ) ping; + // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm + // Divide each integer by 2, rather than the sum by 2, to prevent overflow + remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].clockDifferential = sendPongTime - ( time/2 + sendPingTime/2 ); + + if ( remoteSystem->lowestPing == (unsigned short)-1 || remoteSystem->lowestPing > (int) ping ) + remoteSystem->lowestPing = (unsigned short) ping; + + if ( ++( remoteSystem->pingAndClockDifferentialWriteIndex ) == (RakNet::Time) PING_TIMES_ARRAY_SIZE ) + remoteSystem->pingAndClockDifferentialWriteIndex = 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBufferedCommands(void) +{ + BufferedCommandStruct *bcs; + + while ((bcs=bufferedCommands.Pop())!=0) + { + if (bcs->data) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + } + bufferedCommands.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBufferedPackets(void) +{ + RecvFromStruct *bcs; + + while ((bcs=bufferedPackets.Pop())!=0) + { + bufferedPackets.Deallocate(bcs, _FILE_AND_LINE_); + } + bufferedPackets.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearSocketQueryOutput(void) +{ + socketQueryOutput.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearRequestedConnectionList(void) +{ + DataStructures::Queue freeQueue; + requestedConnectionQueueMutex.Lock(); + while (requestedConnectionQueue.Size()) + freeQueue.Push(requestedConnectionQueue.Pop(), _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + unsigned i; + for (i=0; i < freeQueue.Size(); i++) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: In ClearRequestedConnectionList(), Deleting freeQueue index %i client_handshake %x\n", i, freeQueue[i]->client_handshake); + RakNet::OP_DELETE(freeQueue[i]->client_handshake,_FILE_AND_LINE_); +#endif + RakNet::OP_DELETE(freeQueue[i], _FILE_AND_LINE_ ); + } +} +inline void RakPeer::AddPacketToProducer(RakNet::Packet *p) +{ + packetReturnMutex.Lock(); + packetReturnQueue.Push(p,_FILE_AND_LINE_); + packetReturnMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +union Buff6AndBuff8 +{ + unsigned char buff6[6]; + uint64_t buff8; +}; +uint64_t RakPeerInterface::Get64BitUniqueRandomNumber(void) +{ + // Mac address is a poor solution because you can't have multiple connections from the same system + + + + + + + + + + + + + +#if defined(_WIN32) + uint64_t g=RakNet::GetTimeUS(); + + RakNet::TimeUS lastTime, thisTime; + int j; + // Sleep a small random time, then use the last 4 bits as a source of randomness + for (j=0; j < 8; j++) + { + lastTime = RakNet::GetTimeUS(); + RakSleep(1); + RakSleep(0); + thisTime = RakNet::GetTimeUS(); + RakNet::TimeUS diff = thisTime-lastTime; + unsigned int diff4Bits = (unsigned int) (diff & 15); + diff4Bits <<= 32-4; + diff4Bits >>= j*4; + ((char*)&g)[j] ^= diff4Bits; + } + return g; + +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_usec + tv.tv_sec * 1000000; +#endif +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GenerateGUID(void) +{ + myGuid.g=Get64BitUniqueRandomNumber(); + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// void RakNet::ProcessPortUnreachable( SystemAddress systemAddress, RakPeer *rakPeer ) +// { +// (void) binaryAddress; +// (void) port; +// (void) rakPeer; +// +// } +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +namespace RakNet { +bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, bool *isOfflineMessage, RakNet::TimeUS timeRead ) +{ + (void) timeRead; + RakPeer::RemoteSystemStruct *remoteSystem; + RakNet::Packet *packet; + unsigned i; + + + char str1[64]; + systemAddress.ToString(false, str1); + if (rakPeer->IsBanned( str1 )) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + RakNet::BitStream bs; + bs.Write((MessageID)ID_CONNECTION_BANNED); + bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + unsigned i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (char*) bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + + + + // The reason for all this is that the reliability layer has no way to tell between offline messages that arrived late for a player that is now connected, + // and a regular encoding. So I insert OFFLINE_MESSAGE_DATA_ID into the stream, the encoding of which is essentially impossible to hit by chance + if (length <=2) + { + *isOfflineMessage=true; + } + else if ( + ((unsigned char)data[0] == ID_UNCONNECTED_PING || + (unsigned char)data[0] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS) && + length == sizeof(unsigned char) + sizeof(RakNet::Time) + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(RakNet::Time), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ((unsigned char)data[0] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char) + sizeof(RakNet::TimeMS) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(RakNet::Time) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ( + (unsigned char)data[0] == ID_OUT_OF_BAND_INTERNAL && + (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ( + ( + (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_1 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_2 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_1 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_2 || + (unsigned char)data[0] == ID_CONNECTION_ATTEMPT_FAILED || + (unsigned char)data[0] == ID_NO_FREE_INCOMING_CONNECTIONS || + (unsigned char)data[0] == ID_CONNECTION_BANNED || + (unsigned char)data[0] == ID_ALREADY_CONNECTED || + (unsigned char)data[0] == ID_IP_RECENTLY_CONNECTED) && + (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if (((unsigned char)data[0] == ID_INCOMPATIBLE_PROTOCOL_VERSION&& + (size_t) length == sizeof(MessageID)*2 + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID))) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID)*2, OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else + { + *isOfflineMessage=false; + } + + if (*isOfflineMessage) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + // These are all messages from unconnected systems. Messages here can be any size, but are never processed from connected systems. + if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS + || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && length == sizeof(unsigned char)+sizeof(RakNet::Time)+sizeof(OFFLINE_MESSAGE_DATA_ID) ) + { + if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || + rakPeer->AllowIncomingConnections() ) // Open connections with players + { +// #if !defined(_XBOX) && !defined(X360) + RakNet::BitStream inBitStream( (unsigned char *) data, length, false ); + inBitStream.IgnoreBits(8); + RakNet::Time sendPingTime; + inBitStream.Read(sendPingTime); + + RakNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_UNCONNECTED_PONG); // Should be named ID_UNCONNECTED_PONG eventually + outBitStream.Write(sendPingTime); + outBitStream.Write(rakPeer->myGuid); + outBitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + + rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Lock(); + // They are connected, so append offline ping data + outBitStream.Write( (char*)rakPeer->offlinePingResponse.GetData(), rakPeer->offlinePingResponse.GetNumberOfBytesUsed() ); + rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Unlock(); + + unsigned i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*)outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), systemAddress); + + SocketLayer::SendTo( rakNetSocket->s, (const char*)outBitStream.GetData(), (unsigned int) outBitStream.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + packet=rakPeer->AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + packet->data[0]=data[0]; + packet->systemAddress = systemAddress; + packet->guid=UNASSIGNED_RAKNET_GUID; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); +// #endif + } + } + // UNCONNECTED MESSAGE Pong with no data. + else if ((unsigned char) data[ 0 ] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID) && (size_t) length < sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID)+MAX_OFFLINE_DATA_LENGTH) + { + packet=rakPeer->AllocPacket((unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(RakNet::Time)+sizeof(RakNet::TimeMS)), _FILE_AND_LINE_); + RakNet::BitStream bsIn((unsigned char*) data, length, false); + bsIn.IgnoreBytes(sizeof(unsigned char)); + RakNet::Time ping; + bsIn.Read(ping); + bsIn.Read(packet->guid); + + RakNet::BitStream bsOut((unsigned char*) packet->data, packet->length, false); + bsOut.ResetWritePointer(); + bsOut.Write((unsigned char)ID_UNCONNECTED_PONG); + RakNet::TimeMS pingMS=(RakNet::TimeMS)ping; + bsOut.Write(pingMS); + bsOut.WriteAlignedBytes( + (const unsigned char*)data+sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID), + length-sizeof(unsigned char)-sizeof(RakNet::Time)-RakNetGUID::size()-sizeof(OFFLINE_MESSAGE_DATA_ID) + ); + + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + else if ((unsigned char) data[ 0 ] == ID_OUT_OF_BAND_INTERNAL && + (size_t) length < MAX_OFFLINE_DATA_LENGTH+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size()) + { + unsigned int dataLength = (unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MessageID)); + RakAssert(dataLength<1024); + packet=rakPeer->AllocPacket(dataLength+1, _FILE_AND_LINE_); + RakAssert(packet->length<1024); + + RakNet::BitStream bs2((unsigned char*) data, length, false); + bs2.IgnoreBytes(sizeof(MessageID)); + bs2.Read(packet->guid); + + if (data[sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size()]==ID_ADVERTISE_SYSTEM) + { + packet->length--; + packet->bitSize=BYTES_TO_BITS(packet->length); + packet->data[0]=ID_ADVERTISE_SYSTEM; + memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)*2 + RakNetGUID::size(), dataLength-1); + } + else + { + packet->data[0]=ID_OUT_OF_BAND_INTERNAL; + memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size(), dataLength); + } + + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_1) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + RakNet::BitStream bsIn((unsigned char*) data,length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bsIn.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID serverGuid; + bsIn.Read(serverGuid); + unsigned char serverHasSecurity; + uint32_t cookie; + (void) cookie; + bsIn.Read(serverHasSecurity); + // Even if the server has security, it may not be required of us if we are in the security exception list + if (serverHasSecurity) + { + bsIn.Read(cookie); + } + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_2); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + if (serverHasSecurity) + bsOut.Write(cookie); + + unsigned i; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + RakPeer::RequestedConnectionStruct *rcs; + rcs=rakPeer->requestedConnectionQueue[i]; + if (rcs->systemAddress==systemAddress) + { + if (serverHasSecurity) + { +#if LIBCAT_SECURITY==1 + unsigned char public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; + bsIn.ReadAlignedBytes(public_key, sizeof(public_key)); + + if (rcs->publicKeyMode==PKM_ACCEPT_ANY_PUBLIC_KEY) + { + memcpy(rcs->remote_public_key, public_key, cat::EasyHandshake::PUBLIC_KEY_BYTES); + if (!rcs->client_handshake->Initialize(public_key) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Server passed a bad public key with PKM_ACCEPT_ANY_PUBLIC_KEY"); + return true; + } + } + + if (cat::SecureEqual(public_key, + rcs->remote_public_key, + cat::EasyHandshake::PUBLIC_KEY_BYTES)==false) + { + rakPeer->requestedConnectionQueueMutex.Unlock(); + CAT_AUDIT_PRINTF("AUDIT: Expected public key does not match what was sent by server -- Reporting back ID_PUBLIC_KEY_MISMATCH to user\n"); + + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_PUBLIC_KEY_MISMATCH; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=serverGuid; + rakPeer->AddPacketToProducer(packet); + return true; + } + + if (rcs->client_handshake==0) + { + // Message does not contain a challenge + // We might still pass if we are in the security exception list + bsOut.Write((unsigned char)0); + } + else + { + // Message contains a challenge + bsOut.Write((unsigned char)1); + // challenge + CAT_AUDIT_PRINTF("AUDIT: Sending challenge\n"); + bsOut.WriteAlignedBytes((const unsigned char*) rcs->handshakeChallenge,cat::EasyHandshake::CHALLENGE_BYTES); + } +#else // LIBCAT_SECURITY + // Message does not contain a challenge + bsOut.Write((unsigned char)0); +#endif // LIBCAT_SECURITY + } + else + { + // Server does not need security +#if LIBCAT_SECURITY==1 + if (rcs->client_handshake!=0) + { + rakPeer->requestedConnectionQueueMutex.Unlock(); + CAT_AUDIT_PRINTF("AUDIT: Security disabled by server but we expected security (indicated by client_handshake not null) so failing!\n"); + + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_OUR_SYSTEM_REQUIRES_SECURITY; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=serverGuid; + rakPeer->AddPacketToProducer(packet); + return true; + } +#endif // LIBCAT_SECURITY + + } + + uint16_t mtu; + bsIn.Read(mtu); + + // Binding address + bsOut.Write(rcs->systemAddress); + rakPeer->requestedConnectionQueueMutex.Unlock(); + // MTU + bsOut.Write(mtu); + // Our guid + bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), rcs->systemAddress); + + SocketLayer::SendTo( rakPeer->socketList[rcs->socketIndex]->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), rcs->systemAddress, rakPeer->socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, rakPeer->socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + } + rakPeer->requestedConnectionQueueMutex.Unlock(); + } + else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_2) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + RakNet::BitStream bs((unsigned char*) data,length,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID guid; + bs.Read(guid); + SystemAddress bindingAddress; + bool b = bs.Read(bindingAddress); + RakAssert(b); + uint16_t mtu; + b=bs.Read(mtu); + RakAssert(b); + bool doSecurity=false; + b=bs.Read(doSecurity); + RakAssert(b); + +#if LIBCAT_SECURITY==1 + char answer[cat::EasyHandshake::ANSWER_BYTES]; + CAT_AUDIT_PRINTF("AUDIT: Got ID_OPEN_CONNECTION_REPLY_2 and given doSecurity=%i\n", (int)doSecurity); + if (doSecurity) + { + CAT_AUDIT_PRINTF("AUDIT: Reading cookie and public key\n"); + bs.ReadAlignedBytes((unsigned char*) answer, sizeof(answer)); + } + cat::ClientEasyHandshake *client_handshake=0; +#endif // LIBCAT_SECURITY + + RakPeer::RequestedConnectionStruct *rcs; + bool unlock=true; + unsigned i; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + rcs=rakPeer->requestedConnectionQueue[i]; + + + if (rcs->systemAddress==systemAddress) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: System address matches an entry in the requestedConnectionQueue and doSecurity=%i\n", (int)doSecurity); + if (doSecurity) + { + if (rcs->client_handshake==0) + { + CAT_AUDIT_PRINTF("AUDIT: Server wants security but we didn't set a public key -- Reporting back ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY to user\n"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + + packet=rakPeer->AllocPacket(2, _FILE_AND_LINE_); + packet->data[ 0 ] = ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY; // Attempted a connection and couldn't + packet->data[ 1 ] = 0; // Indicate server public key is missing + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + return true; + } + + CAT_AUDIT_PRINTF("AUDIT: Looks good, preparing to send challenge to server! client_handshake = %x\n", client_handshake); + } + +#endif // LIBCAT_SECURITY + + rakPeer->requestedConnectionQueueMutex.Unlock(); + unlock=false; + + RakAssert(rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT); + // You might get this when already connected because of cross-connections + bool thisIPConnectedRecently=false; + remoteSystem=rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + if (remoteSystem==0) + { + if (rcs->socket.IsNull()) + { + remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); + } + else + { + remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rcs->socket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); + } + } + + // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots + // Ignore connection attempts if this IP address connected within the last 100 milliseconds + if (thisIPConnectedRecently==false) + { + // Don't check GetRemoteSystemFromGUID, server will verify + if (remoteSystem) + { + // Move pointer from RequestedConnectionStruct to RemoteSystemStruct +#if LIBCAT_SECURITY==1 + cat::u8 ident[cat::EasyHandshake::IDENTITY_BYTES]; + bool doIdentity = false; + + if (rcs->client_handshake) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer\n"); + if (rcs->publicKeyMode == PKM_USE_TWO_WAY_AUTHENTICATION) + { + if (!rcs->client_handshake->ProcessAnswerWithIdentity(answer, ident, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + + return true; + } + + doIdentity = true; + } + else + { + if (!rcs->client_handshake->ProcessAnswer(answer, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + + return true; + } + } + CAT_AUDIT_PRINTF("AUDIT: Success!\n"); + + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + } +#endif // LIBCAT_SECURITY + + remoteSystem->weInitiatedTheConnection=true; + remoteSystem->connectMode=RakPeer::RemoteSystemStruct::REQUESTED_CONNECTION; + if (rcs->timeoutTime!=0) + remoteSystem->reliabilityLayer.SetTimeoutTime(rcs->timeoutTime); + + RakNet::BitStream temp; + temp.Write( (MessageID)ID_CONNECTION_REQUEST); + temp.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + temp.Write(RakNet::GetTime()); + +#if LIBCAT_SECURITY==1 + temp.Write((unsigned char)(doSecurity ? 1 : 0)); + + if (doSecurity) + { + unsigned char proof[32]; + remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->GenerateProof(proof, sizeof(proof)); + temp.WriteAlignedBytes(proof, sizeof(proof)); + + temp.Write((unsigned char)(doIdentity ? 1 : 0)); + + if (doIdentity) + { + temp.WriteAlignedBytes(ident, sizeof(ident)); + } + } +#else + temp.Write((unsigned char)0); +#endif // LIBCAT_SECURITY + + if ( rcs->outgoingPasswordLength > 0 ) + temp.Write( ( char* ) rcs->outgoingPassword, rcs->outgoingPasswordLength ); + + rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, timeRead, 0 ); + } + else + { + // Failed, no connections available anymore + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + } + } + + rakPeer->requestedConnectionQueueMutex.Lock(); + for (unsigned int k=0; k < rakPeer->requestedConnectionQueue.Size(); k++) + { + if (rakPeer->requestedConnectionQueue[k]->systemAddress==systemAddress) + { + rakPeer->requestedConnectionQueue.RemoveAtIndex(k); + break; + } + } + rakPeer->requestedConnectionQueueMutex.Unlock(); + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Deleting client_handshake object %x and rcs->client_handshake object %x\n", client_handshake, rcs->client_handshake); + RakNet::OP_DELETE(client_handshake,_FILE_AND_LINE_); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif // LIBCAT_SECURITY + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + + break; + } + } + + if (unlock) + rakPeer->requestedConnectionQueueMutex.Unlock(); + + return true; + + } + else if ((unsigned char)(data)[0] == (MessageID)ID_CONNECTION_ATTEMPT_FAILED || + (unsigned char)(data)[0] == (MessageID)ID_NO_FREE_INCOMING_CONNECTIONS || + (unsigned char)(data)[0] == (MessageID)ID_CONNECTION_BANNED || + (unsigned char)(data)[0] == (MessageID)ID_ALREADY_CONNECTED || + (unsigned char)(data)[0] == (MessageID)ID_INVALID_PASSWORD || + (unsigned char)(data)[0] == (MessageID)ID_IP_RECENTLY_CONNECTED || + (unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) + { + + RakNet::BitStream bs((unsigned char*) data,length,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + if ((unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) + bs.IgnoreBytes(sizeof(unsigned char)); + + RakNetGUID guid; + bs.Read(guid); + + RakPeer::RequestedConnectionStruct *rcs; + bool connectionAttemptCancelled=false; + unsigned i; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + rcs=rakPeer->requestedConnectionQueue[i]; + if (rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT && rcs->systemAddress==systemAddress) + { + connectionAttemptCancelled=true; + rakPeer->requestedConnectionQueue.RemoveAtIndex(i); + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Connection attempt canceled so deleting rcs->client_handshake object %x\n", rcs->client_handshake); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif // LIBCAT_SECURITY + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + break; + } + } + + rakPeer->requestedConnectionQueueMutex.Unlock(); + + if (connectionAttemptCancelled) + { + // Tell user of connection attempt failed + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = data[0]; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + } + } + else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_1 && length > (int) (1+sizeof(OFFLINE_MESSAGE_DATA_ID))) + {/* + static int x = 0; + ++x; + + SystemAddress *addr = (SystemAddress*)&systemAddress; + addr->binaryAddress += x;*/ + + unsigned int i; + //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); + char remoteProtocol=data[1+sizeof(OFFLINE_MESSAGE_DATA_ID)]; + if (remoteProtocol!=RAKNET_PROTOCOL_VERSION) + { + RakNet::BitStream bs; + bs.Write((MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION); + bs.Write((unsigned char)RAKNET_PROTOCOL_VERSION); + bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*)bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + + SocketLayer::SendTo( rakNetSocket->s, (char*)bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + return true; + } + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_OPEN_CONNECTION_REPLY_1); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); +#if LIBCAT_SECURITY==1 + if (rakPeer->_using_security) + { + bsOut.Write((unsigned char) 1); // HasCookie Yes + // Write cookie + uint32_t cookie = rakPeer->_cookie_jar->Generate(&systemAddress.address,sizeof(systemAddress.address)); + CAT_AUDIT_PRINTF("AUDIT: Writing cookie %i to %i:%i\n", cookie, systemAddress); + bsOut.Write(cookie); + // Write my public key + bsOut.WriteAlignedBytes((const unsigned char *) rakPeer->my_public_key,sizeof(rakPeer->my_public_key)); + } + else +#endif // LIBCAT_SECURITY + bsOut.Write((unsigned char) 0); // HasCookie No + + // MTU + bsOut.Write((uint16_t) (length+UDP_HEADER_SIZE)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + } + else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_2) + { + SystemAddress bindingAddress; + RakNetGUID guid; + RakNet::BitStream bsOut; + RakNet::BitStream bs((unsigned char*) data, length, false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + + bool requiresSecurityOfThisClient=false; +#if LIBCAT_SECURITY==1 + char remoteHandshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; + + if (rakPeer->_using_security) + { + char str1[64]; + systemAddress.ToString(false, str1); + requiresSecurityOfThisClient=rakPeer->IsInSecurityExceptionList(str1)==false; + + uint32_t cookie; + bs.Read(cookie); + CAT_AUDIT_PRINTF("AUDIT: Got cookie %i from %i:%i\n", cookie, systemAddress); + if (rakPeer->_cookie_jar->Verify(&systemAddress.address,sizeof(systemAddress.address), cookie)==false) + { + return true; + } + CAT_AUDIT_PRINTF("AUDIT: Cookie good!\n"); + + unsigned char clientWroteChallenge; + bs.Read(clientWroteChallenge); + + if (requiresSecurityOfThisClient==true && clientWroteChallenge==0) + { + // Fail, client doesn't support security, and it is required + return true; + } + + if (clientWroteChallenge) + { + bs.ReadAlignedBytes((unsigned char*) remoteHandshakeChallenge, cat::EasyHandshake::CHALLENGE_BYTES); +#ifdef CAT_AUDIT + printf("AUDIT: RECV CHALLENGE "); + for (int ii = 0; ii < sizeof(remoteHandshakeChallenge); ++ii) + { + printf("%02x", (cat::u8)remoteHandshakeChallenge[ii]); + } + printf("\n"); +#endif + } + } +#endif // LIBCAT_SECURITY + + bs.Read(bindingAddress); + uint16_t mtu; + bs.Read(mtu); + bs.Read(guid); + + RakPeer::RemoteSystemStruct *rssFromSA = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + bool IPAddrInUse = rssFromSA != 0 && rssFromSA->isActive; + RakPeer::RemoteSystemStruct *rssFromGuid = rakPeer->GetRemoteSystemFromGUID(guid, true); + bool GUIDInUse = rssFromGuid != 0 && rssFromGuid->isActive; + + // IPAddrInUse, GuidInUse, outcome + // TRUE, , TRUE , ID_OPEN_CONNECTION_REPLY if they are the same, else ID_ALREADY_CONNECTED + // FALSE, , TRUE , ID_ALREADY_CONNECTED (someone else took this guid) + // TRUE, , FALSE , ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) + // FALSE , FALSE , Allow connection + + int outcome; + if (IPAddrInUse & GUIDInUse) + { + if (rssFromSA==rssFromGuid && rssFromSA->connectMode==RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER) + { + // ID_OPEN_CONNECTION_REPLY if they are the same + outcome=1; + } + else + { + // ID_ALREADY_CONNECTED (restarted raknet, connected again from same ip, plus someone else took this guid) + outcome=2; + } + } + else if (IPAddrInUse==false && GUIDInUse==true) + { + // ID_ALREADY_CONNECTED (someone else took this guid) + outcome=3; + } + else if (IPAddrInUse==true && GUIDInUse==false) + { + // ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) + outcome=4; + } + else + { + // Allow connection + outcome=0; + } + + RakNet::BitStream bsAnswer; + bsAnswer.Write((MessageID)ID_OPEN_CONNECTION_REPLY_2); + bsAnswer.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsAnswer.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + bsAnswer.Write(systemAddress); + bsAnswer.Write(mtu); + bsAnswer.Write(requiresSecurityOfThisClient); + + if (outcome==1) + { + // Duplicate connection request packet from packetloss + // Send back the same answer +#if LIBCAT_SECURITY==1 + if (requiresSecurityOfThisClient) + { + CAT_AUDIT_PRINTF("AUDIT: Resending public key and answer from packetloss. Sending ID_OPEN_CONNECTION_REPLY_2\n"); + bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); + } +#endif // LIBCAT_SECURITY + + unsigned int i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + else if (outcome!=0) + { + bsOut.Write((MessageID)ID_ALREADY_CONNECTED); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(guid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + + if (rakPeer->AllowIncomingConnections()==false) + { + bsOut.Write((MessageID)ID_NO_FREE_INCOMING_CONNECTIONS); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(guid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + + bool thisIPConnectedRecently=false; + rssFromSA = rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, requiresSecurityOfThisClient); + + if (thisIPConnectedRecently==true) + { + bsOut.Write((MessageID)ID_IP_RECENTLY_CONNECTED); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(guid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + +#if LIBCAT_SECURITY==1 + if (requiresSecurityOfThisClient) + { + CAT_AUDIT_PRINTF("AUDIT: Writing public key. Sending ID_OPEN_CONNECTION_REPLY_2\n"); + if (rakPeer->_server_handshake->ProcessChallenge(remoteHandshakeChallenge, rssFromSA->answer, rssFromSA->reliabilityLayer.GetAuthenticatedEncryption() )) + { + CAT_AUDIT_PRINTF("AUDIT: Challenge good!\n"); + // Keep going to OK block + } + else + { + CAT_AUDIT_PRINTF("AUDIT: Challenge BAD!\n"); + + // Unassign this remote system + rakPeer->DereferenceRemoteSystem(systemAddress); + return true; + } + + bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); + } +#endif // LIBCAT_SECURITY + + unsigned int i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + } + return true; + } + + return false; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead, BitStream &updateBitStream ) +{ + ProcessNetworkPacket(systemAddress,data,length,rakPeer,rakPeer->socketList[0],timeRead, updateBitStream); +} +void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead, BitStream &updateBitStream ) +{ +#if LIBCAT_SECURITY==1 +#ifdef CAT_AUDIT + printf("AUDIT: RECV "); + for (int ii = 0; ii < length; ++ii) + { + printf("%02x", (cat::u8)data[ii]); + } + printf("\n"); +#endif +#endif // LIBCAT_SECURITY + + RakAssert(systemAddress.GetPort()); + bool isOfflineMessage; + if (ProcessOfflineNetworkPacket(systemAddress, data, length, rakPeer, rakNetSocket, &isOfflineMessage, timeRead)) + { + return; + } + +// RakNet::Packet *packet; + RakPeer::RemoteSystemStruct *remoteSystem; + + // See if this datagram came from a connected system + remoteSystem = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + if ( remoteSystem ) + { + // Handle regular incoming data + // HandleSocketReceiveFromConnectedPlayer is only safe to be called from the same thread as Update, which is this thread + if ( isOfflineMessage==false) + { + remoteSystem->reliabilityLayer.HandleSocketReceiveFromConnectedPlayer( + data, length, systemAddress, rakPeer->pluginListNTS, remoteSystem->MTUSize, + rakNetSocket->s, &rnr, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, timeRead, updateBitStream); + } + } +} + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GenerateSeedFromGuid(void) +{ + /* + // Construct a random seed based on the initial guid value, and the last digits of the difference to each subsequent number + // This assumes that only the last 3 bits of each guidId integer has a meaningful amount of randomness between it and the prior number + unsigned int t = guid.g[0]; + unsigned int i; + for (i=1; i < sizeof(guid.g) / sizeof(guid.g[0]); i++) + { + unsigned int diff = guid.g[i]-guid.g[i-1]; + unsigned int diff3Bits = diff & 0x0007; + diff3Bits <<= 29; + diff3Bits >>= (i-1)*3; + t ^= diff3Bits; + } + + return t; + */ + return (unsigned int) ((myGuid.g >> 32) ^ myGuid.g); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DerefAllSockets(void) +{ + unsigned int i; + for (i=0; i < socketList.Size(); i++) + { + socketList[i].SetNull(); + } + socketList.Clear(false, _FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const +{ + unsigned int i; + for (i=0; i < socketList.Size(); i++) + { + if (socketList[i]->userConnectionSocketIndex==userIndex) + return i; + } + RakAssert("GetRakNetSocketFromUserConnectionSocketIndex failed" && 0); + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::RunUpdateCycle( RakNet::TimeUS timeNS, RakNet::Time timeMS, BitStream &updateBitStream ) +{ + RakPeer::RemoteSystemStruct * remoteSystem; + unsigned int activeSystemListIndex; + Packet *packet; + // int currentSentBytes,currentReceivedBytes; +// unsigned numberOfBytesUsed; +// BitSize_t numberOfBitsUsed; + //SystemAddress authoritativeClientSystemAddress; + BitSize_t bitSize; + unsigned int byteSize; + unsigned char *data; + SystemAddress systemAddress; + BufferedCommandStruct *bcs; + bool callerDataAllocationUsed; + RakNetStatistics *rnss; + + // This is here so RecvFromBlocking actually gets data from the same thread + if (SocketLayer::GetSocketLayerOverride()) + { + int len; + SystemAddress sender; + char dataOut[ MAXIMUM_MTU_SIZE ]; + do { + len = SocketLayer::GetSocketLayerOverride()->RakNetRecvFrom(socketList[0]->s,this,dataOut,&sender,true); + if (len>0) + ProcessNetworkPacket( sender, dataOut, len, this, socketList[0], RakNet::GetTimeUS(), updateBitStream ); + } while (len>0); + } + + unsigned int socketListIndex; + RakPeer::RecvFromStruct *recvFromStruct; + while ((recvFromStruct=bufferedPackets.PopInaccurate())!=0) + { + for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) + { + if ((SOCKET) socketList[socketListIndex]->s==recvFromStruct->s) + break; + } + if (socketListIndex!=socketList.Size()) + ProcessNetworkPacket(recvFromStruct->systemAddress, recvFromStruct->data, recvFromStruct->bytesRead, this, socketList[socketListIndex], recvFromStruct->timeRead, updateBitStream); + bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); + } + + while ((bcs=bufferedCommands.PopInaccurate())!=0) + { + if (bcs->command==BufferedCommandStruct::BCS_SEND) + { + // GetTime is a very slow call so do it once and as late as possible + if (timeNS==0) + { + timeNS = RakNet::GetTimeUS(); + timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); + } + + callerDataAllocationUsed=SendImmediate((char*)bcs->data, bcs->numberOfBitsToSend, bcs->priority, bcs->reliability, bcs->orderingChannel, bcs->systemIdentifier, bcs->broadcast, true, timeNS, bcs->receipt); + if ( callerDataAllocationUsed==false ) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + + // Set the new connection state AFTER we call sendImmediate in case we are setting it to a disconnection state, which does not allow further sends + if (bcs->connectionMode!=RemoteSystemStruct::NO_ACTION ) + { + remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); + if (remoteSystem) + remoteSystem->connectMode=bcs->connectionMode; + } + } + else if (bcs->command==BufferedCommandStruct::BCS_CLOSE_CONNECTION) + { + CloseConnectionInternal(bcs->systemIdentifier, false, true, bcs->orderingChannel, bcs->priority); + } + else if (bcs->command==BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS) + { + // Reroute + RakPeer::RemoteSystemStruct *rssFromGuid = GetRemoteSystem(bcs->systemIdentifier.rakNetGuid,true,true); + if (rssFromGuid!=0) + { + unsigned int existingSystemIndex = GetRemoteSystemIndex(rssFromGuid->systemAddress); + ReferenceRemoteSystem(bcs->systemIdentifier.systemAddress, existingSystemIndex); + } + } + else if (bcs->command==BufferedCommandStruct::BCS_GET_SOCKET) + { + SocketQueryOutput *sqo; + if (bcs->systemIdentifier.IsUndefined()) + { + sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); + sqo->sockets=socketList; + socketQueryOutput.Push(sqo); + } + else + { + remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); + sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); + + sqo->sockets.Clear(false, _FILE_AND_LINE_); + if (remoteSystem) + { + sqo->sockets.Push(remoteSystem->rakNetSocket, _FILE_AND_LINE_ ); + } + else + { + // Leave empty smart pointer + } + socketQueryOutput.Push(sqo); + } + + } + +#ifdef _DEBUG + bcs->data=0; +#endif + + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + } + + if (requestedConnectionQueue.IsEmpty()==false) + { + if (timeNS==0) + { + timeNS = RakNet::GetTimeUS(); + timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); + } + + bool condition1, condition2; + unsigned requestedConnectionQueueIndex=0; + requestedConnectionQueueMutex.Lock(); + while (requestedConnectionQueueIndex < requestedConnectionQueue.Size()) + { + RequestedConnectionStruct *rcs; + rcs = requestedConnectionQueue[requestedConnectionQueueIndex]; + requestedConnectionQueueMutex.Unlock(); + if (rcs->nextRequestTime < timeMS) + { + condition1=rcs->requestsMade==rcs->sendConnectionAttemptCount+1; + condition2=(bool)((rcs->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)==1); + // If too many requests made or a hole then remove this if possible, otherwise invalidate it + if (condition1 || condition2) + { + if (rcs->data) + { + rakFree_Ex(rcs->data, _FILE_AND_LINE_ ); + rcs->data=0; + } + + if (condition1 && !condition2 && rcs->actionToTake==RequestedConnectionStruct::CONNECT) + { + // Tell user of connection attempt failed + packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + AddPacketToProducer(packet); + } + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Connection attempt FAILED so deleting rcs->client_handshake object %x\n", rcs->client_handshake); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + + requestedConnectionQueueMutex.Lock(); + for (unsigned int k=0; k < requestedConnectionQueue.Size(); k++) + { + if (requestedConnectionQueue[k]==rcs) + { + requestedConnectionQueue.RemoveAtIndex(k); + break; + } + } + requestedConnectionQueueMutex.Unlock(); + } + else + { + + int MTUSizeIndex = rcs->requestsMade / (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES); + if (MTUSizeIndex>=NUM_MTU_SIZES) + MTUSizeIndex=NUM_MTU_SIZES-1; + rcs->requestsMade++; + rcs->nextRequestTime=timeMS+rcs->timeBetweenSendConnectionAttemptsMS; + + RakNet::BitStream bitStream; + //WriteOutOfBandHeader(&bitStream, ID_USER_PACKET_ENUM); + bitStream.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_1); + bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bitStream.Write((MessageID)RAKNET_PROTOCOL_VERSION); + bitStream.PadWithZeroToByteLength(mtuSizes[MTUSizeIndex]-UDP_HEADER_SIZE); + + char str[256]; + rcs->systemAddress.ToString(true,str); + + //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*) bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), rcs->systemAddress); + + if (rcs->socket.IsNull()) + { + rcs->systemAddress.FixForIPVersion(socketList[rcs->socketIndex]->boundAddress); + SocketLayer::SetDoNotFragment(socketList[rcs->socketIndex]->s, 1, socketList[rcs->socketIndex]->boundAddress.GetIPPROTO()); + RakNet::Time sendToStart=RakNet::GetTime(); + if (SocketLayer::SendTo( socketList[rcs->socketIndex]->s, (const char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), rcs->systemAddress, socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ )==-10040) + { + // Don't use this MTU size again + rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES)); + rcs->nextRequestTime=timeMS; + } + else + { + RakNet::Time sendToEnd=RakNet::GetTime(); + if (sendToEnd-sendToStart>100) + { + // Drop to lowest MTU + int lowestMtuIndex = rcs->sendConnectionAttemptCount/NUM_MTU_SIZES * (NUM_MTU_SIZES - 1); + if (lowestMtuIndex > rcs->requestsMade) + { + rcs->requestsMade = (unsigned char) lowestMtuIndex; + rcs->nextRequestTime=timeMS; + } + else + rcs->requestsMade=(unsigned char)(rcs->sendConnectionAttemptCount+1); + } + } + SocketLayer::SetDoNotFragment(socketList[rcs->socketIndex]->s, 0, socketList[rcs->socketIndex]->boundAddress.GetIPPROTO()); + } + else + { + rcs->systemAddress.FixForIPVersion(rcs->socket->boundAddress); + SocketLayer::SetDoNotFragment(rcs->socket->s, 1, rcs->socket->boundAddress.GetIPPROTO()); + RakNet::Time sendToStart=RakNet::GetTime(); + if (SocketLayer::SendTo( rcs->socket->s, (const char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), rcs->systemAddress, socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ )==-10040) + { + // Don't use this MTU size again + rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES)); + rcs->nextRequestTime=timeMS; + } + else + { + RakNet::Time sendToEnd=RakNet::GetTime(); + if (sendToEnd-sendToStart>100) + { + // Drop to lowest MTU + int lowestMtuIndex = rcs->sendConnectionAttemptCount/NUM_MTU_SIZES * (NUM_MTU_SIZES - 1); + if (lowestMtuIndex > rcs->requestsMade) + { + rcs->requestsMade = (unsigned char) lowestMtuIndex; + rcs->nextRequestTime=timeMS; + } + else + rcs->requestsMade= (unsigned char) rcs->sendConnectionAttemptCount+1; + } + } + SocketLayer::SetDoNotFragment(rcs->socket->s, 0, rcs->socket->boundAddress.GetIPPROTO()); + } + + requestedConnectionQueueIndex++; + } + } + else + requestedConnectionQueueIndex++; + + requestedConnectionQueueMutex.Lock(); + } + requestedConnectionQueueMutex.Unlock(); + } + + // remoteSystemList in network thread + for ( activeSystemListIndex = 0; activeSystemListIndex < activeSystemListSize; ++activeSystemListIndex ) + //for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; ++remoteSystemIndex ) + { + // I'm using systemAddress from remoteSystemList but am not locking it because this loop is called very frequently and it doesn't + // matter if we miss or do an extra update. The reliability layers themselves never care which player they are associated with + //systemAddress = remoteSystemList[ remoteSystemIndex ].systemAddress; + // Allow the systemAddress for this remote system list to change. We don't care if it changes now. + // remoteSystemList[ remoteSystemIndex ].allowSystemAddressAssigment=true; + + + // Found an active remote system + remoteSystem = activeSystemList[ activeSystemListIndex ]; + systemAddress = remoteSystem->systemAddress; + RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); + // Update is only safe to call from the same thread that calls HandleSocketReceiveFromConnectedPlayer, + // which is this thread + + if (timeNS==0) + { + timeNS = RakNet::GetTimeUS(); + timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); + //RAKNET_DEBUG_PRINTF("timeNS = %I64i timeMS=%i\n", timeNS, timeMS); + } + + + if (timeMS > remoteSystem->lastReliableSend && timeMS-remoteSystem->lastReliableSend > remoteSystem->reliabilityLayer.GetTimeoutTime()/2 && remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) + { + // If no reliable packets are waiting for an ack, do a one byte reliable send so that disconnections are noticed + RakNetStatistics rakNetStatistics; + rnss=remoteSystem->reliabilityLayer.GetStatistics(&rakNetStatistics); + if (rnss->messagesInResendBuffer==0) + { + PingInternal( systemAddress, true, RELIABLE ); + + //remoteSystem->lastReliableSend=timeMS+remoteSystem->reliabilityLayer.GetTimeoutTime(); + remoteSystem->lastReliableSend=timeMS; + } + } + + remoteSystem->reliabilityLayer.Update( remoteSystem->rakNetSocket->s, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, remoteSystem->rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, remoteSystem->rakNetSocket->extraSocketOptions, updateBitStream ); // systemAddress only used for the internet simulator test + + // Check for failure conditions + if ( remoteSystem->reliabilityLayer.IsDeadConnection() || + ((remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY) && remoteSystem->reliabilityLayer.IsOutgoingDataWaiting()==false) || + (remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && (remoteSystem->reliabilityLayer.AreAcksWaiting()==false || remoteSystem->reliabilityLayer.AckTimeout(timeMS)==true)) || + (( + (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || + remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || + remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) + && timeMS > remoteSystem->connectionTime && timeMS - remoteSystem->connectionTime > 10000)) + ) + { + // RAKNET_DEBUG_PRINTF("timeMS=%i remoteSystem->connectionTime=%i\n", timeMS, remoteSystem->connectionTime ); + + // Failed. Inform the user? + // TODO - RakNet 4.0 - Return a different message identifier for DISCONNECT_ASAP_SILENTLY and DISCONNECT_ASAP than for DISCONNECT_ON_NO_ACK + // The first two mean we called CloseConnection(), the last means the other system sent us ID_DISCONNECTION_NOTIFICATION + if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION + || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK) + { + +// RakNet::BitStream undeliveredMessages; +// remoteSystem->reliabilityLayer.GetUndeliveredMessages(&undeliveredMessages,remoteSystem->MTUSize); + +// packet=AllocPacket(sizeof( char ) + undeliveredMessages.GetNumberOfBytesUsed()); + packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + else if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) + packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection + else + packet->data[ 0 ] = ID_DISCONNECTION_NOTIFICATION; // DeadConnection + +// memcpy(packet->data+1, undeliveredMessages.GetData(), undeliveredMessages.GetNumberOfBytesUsed()); + + packet->guid = remoteSystem->guid; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + + AddPacketToProducer(packet); + } + // else connection shutting down, don't bother telling the user + +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Connection dropped for player %i:%i\n", systemAddress); +#endif + CloseConnectionInternal( systemAddress, false, true, 0, LOW_PRIORITY ); + continue; + } + + // Ping this guy if it is time to do so + if ( remoteSystem->connectMode==RemoteSystemStruct::CONNECTED && timeMS > remoteSystem->nextPingTime && ( occasionalPing || remoteSystem->lowestPing == (unsigned short)-1 ) ) + { + remoteSystem->nextPingTime = timeMS + 5000; + PingInternal( systemAddress, true, UNRELIABLE ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + } + + // Find whoever has the lowest player ID + //if (systemAddress < authoritativeClientSystemAddress) + // authoritativeClientSystemAddress=systemAddress; + + // Does the reliability layer have any packets waiting for us? + // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer + bitSize = remoteSystem->reliabilityLayer.Receive( &data ); + + while ( bitSize > 0 ) + { + // These types are for internal use and should never arrive from a network packet + if (data[0]==ID_CONNECTION_ATTEMPT_FAILED) + { + RakAssert(0); + bitSize=0; + continue; + } + + // Fast and easy - just use the data that was returned + byteSize = (unsigned int) BITS_TO_BYTES( bitSize ); + + // For unknown senders we only accept a few specific packets + if (remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) + { + if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) + { + ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else + { + CloseConnectionInternal( systemAddress, false, true, 0, LOW_PRIORITY ); +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Temporarily banning %i:%i for sending nonsense data\n", systemAddress); +#endif + + char str1[64]; + systemAddress.ToString(false, str1); + AddToBanList(str1, remoteSystem->reliabilityLayer.GetTimeoutTime()); + + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // However, if we are connected we still take a connection request in case both systems are trying to connect to each other + // at the same time + if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) + { + // 04/27/06 This is wrong. With cross connections, we can both have initiated the connection are in state REQUESTED_CONNECTION + // 04/28/06 Downgrading connections from connected will close the connection due to security at ((remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED && time > remoteSystem->connectionTime && time - remoteSystem->connectionTime > 10000)) + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + { + ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); + } + else + { + + RakNet::BitStream bs((unsigned char*) data,byteSize,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.IgnoreBytes(RakNetGUID::size()); + RakNet::Time incomingTimestamp; + bs.Read(incomingTimestamp); + + // Got a connection request message from someone we are already connected to. Just reply normally. + // This can happen due to race conditions with the fully connected mesh + OnConnectionRequest( remoteSystem, incomingTimestamp ); + } + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char) data[ 0 ] == ID_NEW_INCOMING_CONNECTION && byteSize > sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(RakNet::Time)*2 ) + { + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) + { + remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; + PingInternal( systemAddress, true, UNRELIABLE ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + + RakNet::BitStream inBitStream((unsigned char *) data, byteSize, false); + SystemAddress bsSystemAddress; + + inBitStream.IgnoreBits(8); + inBitStream.Read(bsSystemAddress); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); + + RakNet::Time sendPingTime, sendPongTime; + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); + + // Overwrite the data in the packet + // NewIncomingConnectionStruct newIncomingConnectionStruct; + // RakNet::BitStream nICS_BS( data, NewIncomingConnectionStruct_Size, false ); + // newIncomingConnectionStruct.Deserialize( nICS_BS ); + + remoteSystem->myExternalSystemAddress = bsSystemAddress; + firstExternalID=bsSystemAddress; + firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); + + // Send this info down to the game + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + } + else + { + // Send to game even if already connected. This could happen when connecting to 127.0.0.1 + // Ignore, already connected + // rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char) data[ 0 ] == ID_CONNECTED_PONG && byteSize == sizeof(unsigned char)+sizeof(RakNet::Time)*2 ) + { + RakNet::Time sendPingTime, sendPongTime; + + // Copy into the ping times array the current time - the value returned + // First extract the sent ping + RakNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); + //PingStruct ps; + //ps.Deserialize(psBS); + inBitStream.IgnoreBits(8); + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + + OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)data[0] == ID_CONNECTED_PING && byteSize == sizeof(unsigned char)+sizeof(RakNet::Time) ) + { + RakNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); + inBitStream.IgnoreBits(8); + RakNet::Time sendPingTime; + inBitStream.Read(sendPingTime); + + RakNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_CONNECTED_PONG); + outBitStream.Write(sendPingTime); + outBitStream.Write(RakNet::GetTime()); + SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, UNRELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0 ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char) data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) + { + // We shouldn't close the connection immediately because we need to ack the ID_DISCONNECTION_NOTIFICATION + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ON_NO_ACK; + rakFree_Ex(data, _FILE_AND_LINE_ ); + + // AddPacketToProducer(packet); + } + else if ( (unsigned char)(data)[0] == ID_DETECT_LOST_CONNECTIONS && byteSize == sizeof(unsigned char) ) + { + // Do nothing + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)(data)[0] == ID_INVALID_PASSWORD ) + { + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + { + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST_ACCEPTED ) + { + if (byteSize > sizeof(MessageID)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(SystemIndex)+sizeof(RakNet::Time)*2) + { + // Make sure this connection accept is from someone we wanted to connect to + bool allowConnection, alreadyConnected; + + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || + remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || + allowConnectionResponseIPMigration) + allowConnection=true; + else + allowConnection=false; + + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) + alreadyConnected=true; + else + alreadyConnected=false; + + if ( allowConnection ) + { + SystemAddress externalID; + SystemIndex systemIndex; +// SystemAddress internalID; + + RakNet::BitStream inBitStream((unsigned char *) data, byteSize, false); + inBitStream.IgnoreBits(8); + // inBitStream.Read(remotePort); + inBitStream.Read(externalID); + inBitStream.Read(systemIndex); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); + + RakNet::Time sendPingTime, sendPongTime; + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + OnConnectedPong(sendPingTime, sendPongTime, remoteSystem); + + // Find a free remote system struct to use + // RakNet::BitStream casBitS(data, byteSize, false); + // ConnectionAcceptStruct cas; + // cas.Deserialize(casBitS); + // systemAddress.GetPort() = remotePort; + + // The remote system told us our external IP, so save it + remoteSystem->myExternalSystemAddress = externalID; + remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; + + firstExternalID=externalID; + firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); + + // Send the connection request complete to the game + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = byteSize * 8; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) GetIndexFromSystemAddress( systemAddress, true ); + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + + RakNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_NEW_INCOMING_CONNECTION); + outBitStream.Write(systemAddress); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + outBitStream.Write(ipList[i]); + outBitStream.Write(sendPongTime); + outBitStream.Write(RakNet::GetTime()); + + + // We turned on encryption with SetEncryptionKey. This pads packets to up to a multiple of 16 bytes. + // As soon as a multiple of 16 byte packet arrives on the remote system, we will turn on AES. This works because all encrypted packets are multiples of 16 and the + // packets I happen to be sending before this are not a multiple of 16 bytes. Otherwise there is no way to know if a packet that arrived is + // encrypted or not so the other side won't know to turn on encryption or not. + RakAssert((outBitStream.GetNumberOfBytesUsed()&15)!=0); + SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0 ); + + if (alreadyConnected==false) + { + PingInternal( systemAddress, true, UNRELIABLE ); + } + } + else + { + // Ignore, already connected + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // Version mismatch error? + RakAssert(0); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // What do I do if I get a message from a system, before I am fully connected? + // I can either ignore it or give it to the user + // It seems like giving it to the user is a better option + if ((data[0]>=(MessageID)ID_TIMESTAMP || data[0]==ID_SND_RECEIPT_ACKED || data[0]==ID_SND_RECEIPT_LOSS) && + remoteSystem->isActive + ) + { + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + } + + // Does the reliability layer have any more packets waiting for us? + // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer + bitSize = remoteSystem->reliabilityLayer.Receive( &data ); + } + + } + + return true; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RAK_THREAD_DECLARATION(RakNet::RecvFromLoop) +{ + + + + RakPeerAndIndex *rpai = ( RakPeerAndIndex * ) arguments; + + RakPeer * rakPeer = rpai->rakPeer; + SOCKET s = rpai->s; + unsigned short remotePortRakNetWasStartedOn_PS3 = rpai->remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions = rpai->extraSocketOptions; + + rakPeer->isRecvFromLoopThreadActive.Increment(); + + RakPeer::RecvFromStruct *recvFromStruct; + while ( rakPeer->endThreads == false ) + { + recvFromStruct=rakPeer->bufferedPackets.Allocate( _FILE_AND_LINE_ ); + if (recvFromStruct != NULL) + { + recvFromStruct->s=s; + recvFromStruct->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; + recvFromStruct->extraSocketOptions=extraSocketOptions; + SocketLayer::RecvFromBlocking(s, rakPeer, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); + + if (recvFromStruct->bytesRead>0) + { + RakAssert(recvFromStruct->systemAddress.GetPort()); + rakPeer->bufferedPackets.Push(recvFromStruct); + rakPeer->quitAndDataEvents.SetEvent(); + rakPeer->errorState = 0; + } + else + { + if (recvFromStruct->bytesRead < 0) { + --rakPeer->errorState; + RakSleep(30); + } + rakPeer->bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); + } + } + else + RakSleep(30); + } + rakPeer->isRecvFromLoopThreadActive.Decrement(); + + + + + return 0; + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RAK_THREAD_DECLARATION(RakNet::UpdateNetworkLoop) +{ + + + + RakPeer * rakPeer = ( RakPeer * ) arguments; + + +/* + // 11/15/05 - this is slower than Sleep() +#ifdef _WIN32 +#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) + // Lets see if these timers give better performance than Sleep + HANDLE timerHandle; + LARGE_INTEGER dueTime; + + if ( rakPeer->threadSleepTimer <= 0 ) + rakPeer->threadSleepTimer = 1; + + // 2nd parameter of false means synchronization timer instead of manual-reset timer + timerHandle = CreateWaitableTimer( NULL, FALSE, 0 ); + + RakAssert( timerHandle ); + + dueTime.QuadPart = -10000 * rakPeer->threadSleepTimer; // 10000 is 1 ms? + + BOOL success = SetWaitableTimer( timerHandle, &dueTime, rakPeer->threadSleepTimer, NULL, NULL, FALSE ); + (void) success; + RakAssert( success ); + +#endif +#endif +*/ + + BitStream updateBitStream( MAXIMUM_MTU_SIZE +#if LIBCAT_SECURITY==1 + + cat::AuthenticatedEncryption::OVERHEAD_BYTES +#endif + ); + + RakNet::TimeUS timeNS; + RakNet::TimeMS timeMS; + + rakPeer->isMainLoopThreadActive = true; + + while ( rakPeer->endThreads == false ) + { + // Set inside RunUpdateCycle() itself, this is here for testing + timeNS=0; + timeMS=0; + +// #ifdef _DEBUG +// // Sanity check, make sure RunUpdateCycle does not block or not otherwise get called for a long time +// RakNetTime thisCall=RakNet::GetTime(); +// RakAssert(thisCall-lastCall<250); +// lastCall=thisCall; +// #endif + if (rakPeer->userUpdateThreadPtr) + rakPeer->userUpdateThreadPtr(rakPeer, rakPeer->userUpdateThreadData); + + rakPeer->RunUpdateCycle(timeNS, timeMS, updateBitStream); + + // Pending sends go out this often, unless quitAndDataEvents is set + rakPeer->quitAndDataEvents.WaitOnEvent(10); + + /* + +// #if ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && +#if defined(USE_WAIT_FOR_MULTIPLE_EVENTS) && defined(_WIN32) + + if (rakPeer->threadSleepTimer>0) + { + WSAEVENT eventArray[256]; + unsigned int i, eventArrayIndex; + for (i=0,eventArrayIndex=0; i < rakPeer->socketList.Size(); i++) + { + if (rakPeer->socketList[i]->recvEvent!=INVALID_HANDLE_VALUE) + { + eventArray[eventArrayIndex]=rakPeer->socketList[i]->recvEvent; + eventArrayIndex++; + if (eventArrayIndex==256) + break; + } + } + WSAWaitForMultipleEvents(eventArrayIndex,(const HANDLE*) &eventArray,FALSE,rakPeer->threadSleepTimer,FALSE); + } + else + { + RakSleep(0); + } + +#else // ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + #pragma message("-- RakNet: Using Sleep(). Uncomment USE_WAIT_FOR_MULTIPLE_EVENTS in RakNetDefines.h if you want to use WaitForSingleObject instead. --") + + RakSleep( rakPeer->threadSleepTimer ); +#endif + */ + } + + rakPeer->isMainLoopThreadActive = false; + + /* +#ifdef _WIN32 +#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) + CloseHandle(timerHandle); +#endif +#endif + */ + + + + + return 0; + +} + +void RakPeer::CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet) +{ + for (unsigned int i=0; i < pluginList.Size(); i++) + { + switch (packet->data[0]) + { + case ID_DISCONNECTION_NOTIFICATION: + pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_DISCONNECTION_NOTIFICATION); + break; + case ID_CONNECTION_LOST: + pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_CONNECTION_LOST); + break; + case ID_NEW_INCOMING_CONNECTION: + pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, true); + break; + case ID_CONNECTION_REQUEST_ACCEPTED: + pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, false); + break; + case ID_CONNECTION_ATTEMPT_FAILED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_ATTEMPT_FAILED); + break; + case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); + break; + case ID_OUR_SYSTEM_REQUIRES_SECURITY: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_OUR_SYSTEM_REQUIRES_SECURITY); + break; + case ID_PUBLIC_KEY_MISMATCH: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_PUBLIC_KEY_MISMATCH); + break; + case ID_ALREADY_CONNECTED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_ALREADY_CONNECTED); + break; + case ID_NO_FREE_INCOMING_CONNECTIONS: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_NO_FREE_INCOMING_CONNECTIONS); + break; + case ID_CONNECTION_BANNED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_BANNED); + break; + case ID_INVALID_PASSWORD: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INVALID_PASSWORD); + break; + case ID_INCOMPATIBLE_PROTOCOL_VERSION: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INCOMPATIBLE_PROTOCOL); + break; + case ID_IP_RECENTLY_CONNECTED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_IP_RECENTLY_CONNECTED); + break; + } + } +} + +// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) +// #pragma pop_macro("new") +// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakPeer.h b/project/lib_projects/raknet/jni/RaknetSources/RakPeer.h index 8fff5c7..ded067c 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakPeer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakPeer.h @@ -1,984 +1,984 @@ -/// \file -/// \brief Declares RakPeer class. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -// TODO - RakNet 4 - Add network simulator -// TODO - RakNet 4 - Enable disabling flow control per connections - -#ifndef __RAK_PEER_H -#define __RAK_PEER_H - -#include "ReliabilityLayer.h" -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "SingleProducerConsumer.h" -#include "SimpleMutex.h" -#include "DS_OrderedList.h" -#include "Export.h" -#include "RakString.h" -#include "RakThread.h" -#include "RakNetSocket.h" -#include "RakNetSmartPtr.h" -#include "DS_ThreadsafeAllocatingQueue.h" -#include "SignaledEvent.h" -#include "NativeFeatureIncludes.h" -#include "SecureHandshake.h" -#include "LocklessTypes.h" - -namespace RakNet { -/// Forward declarations -class HuffmanEncodingTree; -class PluginInterface2; - -// Sucks but this struct has to be outside the class. Inside and DevCPP won't let you refer to the struct as RakPeer::RemoteSystemIndex while GCC -// forces you to do RakPeer::RemoteSystemIndex -struct RemoteSystemIndex{unsigned index; RemoteSystemIndex *next;}; -//int RAK_DLL_EXPORT SystemAddressAndIndexComp( const SystemAddress &key, const RemoteSystemIndex &data ); // GCC requires RakPeer::RemoteSystemIndex or it won't compile - -///\brief Main interface for network communications. -/// \details It implements most of RakNet's functionality and is the primary interface for RakNet. -/// -/// Inherits RakPeerInterface. -/// -/// See the individual functions for what the class can do. -/// -class RAK_DLL_EXPORT RakPeer : public RakPeerInterface -{ -public: - ///Constructor - RakPeer(); - - ///Destructor - virtual ~RakPeer(); - - // --------------------------------------------------------------------------------------------Major Low Level Functions - Functions needed by most users-------------------------------------------------------------------------------------------- - /// \brief Starts the network threads and opens the listen port. - /// \details You must call this before calling Connect(). - /// \pre On the PS3, call Startup() after Client_Login() - /// \note Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). - /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections. - /// \param[in] maxConnections Maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so that the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.A hybrid would set it to the sum of both types of connections. - /// \param[in] localPort The port to listen for connections on. On linux the system may be set up so thast ports under 1024 are restricted for everything but the root user. Use a higher port for maximum compatibility. - /// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); However, on the XBOX be sure to use IPPROTO_VDP - /// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - /// \return RAKNET_STARTED on success, otherwise appropriate failure enumeration. - StartupResult Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority=-99999 ); - - /// If you accept connections, you must call this or else security will not be enabled for incoming connections. - /// This feature requires more round trips, bandwidth, and CPU time for the connection handshake - /// x64 builds require under 25% of the CPU time of other builds - /// See the Encryption sample for example usage - /// \pre Must be called while offline - /// \pre LIBCAT_SECURITY must be defined to 1 in NativeFeatureIncludes.h for this function to have any effect - /// \param[in] publicKey A pointer to the public key for accepting new connections - /// \param[in] privateKey A pointer to the private key for accepting new connections - /// \param[in] bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection - bool InitializeSecurity( const char *publicKey, const char *privateKey, bool bRequireClientKey = false ); - - /// Disables security for incoming connections. - /// \note Must be called while offline - void DisableSecurity( void ); - - /// \brief This is useful if you have a fixed-address internal server behind a LAN. - /// - /// Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. - /// \note If secure connections are on, do not use secure connections for a specific IP address. - /// \param[in] ip IP address to add. * wildcards are supported. - void AddToSecurityExceptionList(const char *ip); - - /// \brief Remove a specific connection previously added via AddToSecurityExceptionList. - /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. - void RemoveFromSecurityExceptionList(const char *ip); - - /// \brief Checks to see if a given IP is in the security exception list. - /// \param[in] IP address to check. - /// \return True if the IP address is found in security exception list, else returns false. - bool IsInSecurityExceptionList(const char *ip); - - /// \brief Sets the maximum number of incoming connections allowed. - /// \details If the number of incoming connections is less than the number of players currently connected, - /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, - /// it will be reduced to the maximum number of peers allowed. - /// - /// Defaults to 0, meaning by default, nobody can connect to you - /// \param[in] numberAllowed Maximum number of incoming connections allowed. - void SetMaximumIncomingConnections( unsigned short numberAllowed ); - - /// \brief Returns the value passed to SetMaximumIncomingConnections(). - /// \return Maximum number of incoming connections, which is always <= maxConnections - unsigned short GetMaximumIncomingConnections( void ) const; - - /// \brief Returns how many open connections exist at this time. - /// \return Number of open connections. - unsigned short NumberOfConnections(void) const; - - /// \brief Sets the password for the incoming connections. - /// \details The password must match in the call to Connect (defaults to none). - /// Pass 0 to passwordData to specify no password. - /// This is a way to set a low level password for all incoming connections. To selectively reject connections, implement your own scheme using CloseConnection() to remove unwanted connections. - /// \param[in] passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. Specify 0 for no password data - /// \param[in] passwordDataLength The length in bytes of passwordData - void SetIncomingPassword( const char* passwordData, int passwordDataLength ); - - /// \brief Gets the password passed to SetIncomingPassword - /// \param[out] passwordData Should point to a block large enough to hold the password data you passed to SetIncomingPassword() - /// \param[in,out] passwordDataLength Maximum size of the passwordData array. Modified to hold the number of bytes actually written. - void GetIncomingPassword( char* passwordData, int *passwordDataLength ); - - /// \brief Connect to the specified host (ip or domain name) and server port. - /// \details Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. - /// Calling both acts as a true peer. - /// - /// This is a non-blocking connection. - /// - /// The connection is successful when GetConnectionState() returns IS_CONNECTED or Receive() gets a message with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. - /// If the connection is not successful, such as a rejected connection or no response then neither of these things will happen. - /// \pre Requires that you first call Initialize. - /// \param[in] host Either a dotted IP address or a domain name. - /// \param[in] remotePort Port to connect to on the remote machine. - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData. - /// \param[in] publicKey The public key the server is using. If 0, the server is not using security. If non-zero, the publicKeyMode member determines how to connect - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to determine the one to send on. - /// \param[in] sendConnectionAttemptCount Number of datagrams to send to the other system to try to connect. - /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. - /// \param[in] timeoutTime Time to elapse before dropping the connection if a reliable message could not be sent. 0 to use the default value from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); - /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. - /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! - /// \note It is possible to immediately get back ID_CONNECTION_ATTEMPT_FAILED if you exceed the maxConnections parameter passed to Startup(). This could happen if you call CloseConnection() with sendDisconnectionNotificaiton true, then immediately call Connect() before the connection has closed. - ConnectionAttemptResult Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey=0, unsigned connectionSocketIndex=0, unsigned sendConnectionAttemptCount=6, unsigned timeBetweenSendConnectionAttemptsMS=1000, RakNet::TimeMS timeoutTime=0 ); - - /// \brief Connect to the specified host (ip or domain name) and server port. - /// \param[in] host Either a dotted IP address or a domain name. - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData. - /// \param[in] socket A bound socket returned by another instance of RakPeerInterface. - /// \param[in] sendConnectionAttemptCount Number of datagrams to send to the other system to try to connect. - /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned.. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. - /// \param[in] timeoutTime Time to elapse before dropping the connection if a reliable message could not be sent. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); - /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. - /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! - virtual ConnectionAttemptResult ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey=0, unsigned sendConnectionAttemptCount=6, unsigned timeBetweenSendConnectionAttemptsMS=1000, RakNet::TimeMS timeoutTime=0); - - /* /// \brief Connect to the specified network ID (Platform specific console function) - /// \details Does built-in NAT traversal - /// \param[in] networkServiceId Network ID structure for the online service - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData. - //bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength );*/ - - /// \brief Stops the network threads and closes all connections. - /// \param[in] blockDuration Wait time(milli seconds) for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. - /// \param[in] orderingChannel Channel on which ID_DISCONNECTION_NOTIFICATION will be sent, if blockDuration > 0. - /// \param[in] disconnectionNotificationPriority Priority of sending ID_DISCONNECTION_NOTIFICATION. - /// If set to 0, the disconnection notification won't be sent. - void Shutdown( unsigned int blockDuration, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY ); - - /// \brief Returns true if the network thread is running. - /// \return True if the network thread is running, False otherwise - bool IsActive( void ) const; - - /// \brief Fills the array remoteSystems with the SystemAddress of all the systems we are connected to. - /// \param[out] remoteSystems An array of SystemAddress structures, to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to get the number of systems we are connected to. - /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array. - bool GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const; - - /// Returns the next uint32_t that Send() will return - /// \note If using RakPeer from multiple threads, this may not be accurate for your thread. Use IncrementNextSendReceipt() in that case. - /// \return The next uint32_t that Send() or SendList will return - virtual uint32_t GetNextSendReceipt(void); - - /// Returns the next uint32_t that Send() will return, and increments the value by one - /// \note If using RakPeer from multiple threads, pass this to forceReceipt in the send function - /// \return The next uint32_t that Send() or SendList will return - virtual uint32_t IncrementNextSendReceipt(void); - - /// \brief Sends a block of data to the specified system that you are connected to. - /// \note This function only works while the connected. - /// \note The first byte should be a message identifier starting at ID_USER_PACKET_ENUM. - /// \param[in] data Block of data to send. - /// \param[in] length Size in bytes of the data to send. - /// \param[in] priority Priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliably to send this data. See PacketPriority.h - /// \param[in] orderingChannel When using ordered or sequenced messages, the channel to order these on. Messages are only ordered relative to other messages on the same stream. - /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - uint32_t Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); - - /// \brief "Send" to yourself rather than a remote system. - /// \details The message will be processed through the plugins and returned to the game as usual. - /// This function works anytime - /// \note The first byte should be a message identifier starting at ID_USER_PACKET_ENUM - /// \param[in] data Block of data to send. - /// \param[in] length Size in bytes of the data to send. - void SendLoopback( const char *data, const int length ); - - /// \brief Sends a block of data to the specified system that you are connected to. - /// - /// Same as the above version, but takes a BitStream as input. - /// \param[in] bitStream Bitstream to send - /// \param[in] priority Priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliably to send this data. See PacketPriority.h - /// \param[in] orderingChannel Channel to order the messages on, when using ordered or sequenced messages. Messages are only ordered relative to other messages on the same stream. - /// \param[in] systemIdentifier System Address or RakNetGUID to send this packet to, or in the case of broadcasting, the address not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none. - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. - uint32_t Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); - - /// \brief Sends multiple blocks of data, concatenating them automatically. - /// - /// This is equivalent to: - /// RakNet::BitStream bs; - /// bs.WriteAlignedBytes(block1, blockLength1); - /// bs.WriteAlignedBytes(block2, blockLength2); - /// bs.WriteAlignedBytes(block3, blockLength3); - /// Send(&bs, ...) - /// - /// This function only works when connected. - /// \param[in] data An array of pointers to blocks of data - /// \param[in] lengths An array of integers indicating the length of each block of data - /// \param[in] numParameters Length of the arrays data and lengths - /// \param[in] priority Priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliably to send this data. See PacketPriority.h - /// \param[in] orderingChannel Channel to order the messages on, when using ordered or sequenced messages. Messages are only ordered relative to other messages on the same stream. - /// \param[in] systemIdentifier System Address or RakNetGUID to send this packet to, or in the case of broadcasting, the address not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none. - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - uint32_t SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); - - /// \brief Gets a message from the incoming message queue. - /// \details Use DeallocatePacket() to deallocate the message after you are done with it. - /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. - /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. - /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. - /// \sa RakNetTypes.h contains struct Packet. - Packet* Receive( void ); - - /// \brief Call this to deallocate a message returned by Receive() when you are done handling it. - /// \param[in] packet Message to deallocate. - void DeallocatePacket( Packet *packet ); - - /// \brief Return the total number of connections we are allowed. - /// \return Total number of connections allowed. - unsigned short GetMaximumNumberOfPeers( void ) const; - - // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- - /// \brief Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). - /// \details This method closes the connection irrespective of who initiated the connection. - /// \param[in] target Which system to close the connection to. - /// \param[in] sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. - /// \param[in] channel Which ordering channel to send the disconnection notification on, if any - /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. - void CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY ); - - /// \brief Cancel a pending connection attempt. - /// \details If we are already connected, the connection stays open - /// \param[in] target Target system to cancel. - void CancelConnectionAttempt( const SystemAddress target ); - /// Returns if a system is connected, disconnected, connecting in progress, or various other states - /// \param[in] systemIdentifier The system we are referring to - /// \note This locks a mutex, do not call too frequently during connection attempts or the attempt will take longer and possibly even timeout - /// \return What state the remote system is in - ConnectionState GetConnectionState(const AddressOrGUID systemIdentifier); - - /// \brief Given \a systemAddress, returns its index into remoteSystemList. - /// \details Values range from 0 to the maximum number of players allowed - 1. - /// This includes systems which were formerly connected, but are now not connected. - /// \param[in] systemAddress The SystemAddress we are referring to - /// \return The index of this SystemAddress or -1 on system not found. - int GetIndexFromSystemAddress( const SystemAddress systemAddress ) const; - - /// \brief Given \a index into remoteSystemList, will return a SystemAddress. - /// This function is only useful for looping through all systems. - /// - /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. - /// \return The SystemAddress structure corresponding to \a index in remoteSystemList. - SystemAddress GetSystemAddressFromIndex( int index ); - - /// \brief Same as GetSystemAddressFromIndex but returns RakNetGUID - /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. - /// \return The RakNetGUID - RakNetGUID GetGUIDFromIndex( int index ); - - /// \brief Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient - /// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system - /// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the \a addresses list will match the size of the \a guids list. - /// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. - void GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const; - - /// \brief Bans an IP from connecting. - /// \details Banned IPs persist between connections but are not saved on shutdown nor loaded on startup. - /// \param[in] IP Dotted IP address. You can use * for a wildcard address, such as 128.0.0. * will ban all IP addresses starting with 128.0.0. - /// \param[in] milliseconds Gives time in milli seconds for a temporary ban of the IP address. Use 0 for a permanent ban. - void AddToBanList( const char *IP, RakNet::TimeMS milliseconds=0 ); - - /// \brief Allows a previously banned IP to connect. - /// param[in] Dotted IP address. You can use * as a wildcard. An IP such as 128.0.0.* will ban all IP addresses starting with 128.0.0. - void RemoveFromBanList( const char *IP ); - - /// \brief Allows all previously banned IPs to connect. - void ClearBanList( void ); - - /// \brief Returns true or false indicating if a particular IP is banned. - /// \param[in] IP Dotted IP address. - /// \return True if IP matches any IPs in the ban list, accounting for any wildcards. False otherwise. - bool IsBanned( const char *IP ); - - /// \brief Enable or disable allowing frequent connections from the same IP adderss - /// \details This is a security measure which is disabled by default, but can be set to true to prevent attackers from using up all connection slots. - /// \param[in] b True to limit connections from the same ip to at most 1 per 100 milliseconds. - void SetLimitIPConnectionFrequency(bool b); - - // --------------------------------------------------------------------------------------------Pinging Functions - Functions dealing with the automatic ping mechanism-------------------------------------------------------------------------------------------- - /// Send a ping to the specified connected system. - /// \pre The sender and recipient must already be started via a successful call to Startup() - /// \param[in] target Which system to ping - void Ping( const SystemAddress target ); - - /// \brief Send a ping to the specified unconnected system. - /// \details The remote system, if it is Initialized, will respond with ID_PONG followed by sizeof(RakNet::TimeMS) containing the system time the ping was sent. Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h - /// System should reply with ID_PONG if it is active - /// \param[in] host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \return true on success, false on failure (unknown hostname) - bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 ); - - /// \brief Returns the average of all ping times read for the specific system or -1 if none read yet - /// \param[in] systemAddress Which system we are referring to - /// \return The ping time for this system, or -1 - int GetAveragePing( const AddressOrGUID systemIdentifier ); - - /// \brief Returns the last ping time read for the specific system or -1 if none read yet. - /// \param[in] systemAddress Which system we are referring to - /// \return The last ping time for this system, or -1. - int GetLastPing( const AddressOrGUID systemIdentifier ) const; - - /// \brief Returns the lowest ping time read or -1 if none read yet. - /// \param[in] systemAddress Which system we are referring to - /// \return The lowest ping time for this system, or -1. - int GetLowestPing( const AddressOrGUID systemIdentifier ) const; - - /// Ping the remote systems every so often, or not. Can be called anytime. - /// By default this is true. Recommended to leave on, because congestion control uses it to determine how often to resend lost packets. - /// It would be true by default to prevent timestamp drift, since in the event of a clock spike, the timestamp deltas would no longer be accurate - /// \param[in] doPing True to start occasional pings. False to stop them. - void SetOccasionalPing( bool doPing ); - - // --------------------------------------------------------------------------------------------Static Data Functions - Functions dealing with API defined synchronized memory-------------------------------------------------------------------------------------------- - /// \brief Sets the data to send along with a LAN server discovery or offline ping reply. - /// \param[in] data Block of data to send, or 0 for none - /// \param[in] length Length of the data in bytes, or 0 for none - /// \note \a length should be under 400 bytes, as a security measure against flood attacks - /// \sa Ping.cpp - void SetOfflinePingResponse( const char *data, const unsigned int length ); - - /// \brief Returns pointers to a copy of the \a data passed to SetOfflinePingResponse. - /// \param[out] data A pointer to a copy of the data passed to SetOfflinePingResponse() - /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() - /// \sa SetOfflinePingResponse - void GetOfflinePingResponse( char **data, unsigned int *length ); - - //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- - /// \brief Returns the unique address identifier that represents you or another system on the the network and is based on your local IP / port. - /// \note Not supported by the XBOX - /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address - /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) - /// \return Identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy - SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const; - - /// \brief Returns the unique address identifier that represents the target on the the network and is based on the target's external IP / port. - /// \param[in] target The SystemAddress of the remote system. Usually the same for all systems, unless you have two or more network cards. - SystemAddress GetExternalID( const SystemAddress target ) const; - - /// Return my own GUID - const RakNetGUID GetMyGUID(void) const; - - /// Return the address bound to a socket at the specified index - SystemAddress GetMyBoundAddress(const int socketIndex=0); - - /// \brief Given a connected system address, this method gives the unique GUID representing that instance of RakPeer. - /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different. - /// Complexity is O(log2(n)). - /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID - /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID - /// \param[in] input The system address of the target system we are connected to. - const RakNetGUID& GetGuidFromSystemAddress( const SystemAddress input ) const; - - /// \brief Gives the system address of a connected system, given its GUID. - /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different. - /// Currently O(log(n)), but this may be improved in the future - /// If \a input is UNASSIGNED_RAKNET_GUID, UNASSIGNED_SYSTEM_ADDRESS is returned. - /// \param[in] input The RakNetGUID of the target system. - SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const; - - /// Given the SystemAddress of a connected system, get the public key they provided as an identity - /// Returns false if system address was not found or client public key is not known - /// \param[in] input The RakNetGUID of the system - /// \param[in] client_public_key The connected client's public key is copied to this address. Buffer must be cat::EasyHandshake::PUBLIC_KEY_BYTES bytes in length. - bool GetClientPublicKeyFromSystemAddress( const SystemAddress input, char *client_public_key ) const; - - /// \brief Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. - - /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. - /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. - /// Do not set different values for different computers that are connected to each other, or you won't be able to reconnect after ID_CONNECTION_LOST - /// \param[in] timeMS Time, in MS - /// \param[in] target SystemAddress structure of the target system. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. - void SetTimeoutTime( RakNet::TimeMS timeMS, const SystemAddress target ); - - /// \brief Returns the Timeout time for the given system. - /// \param[in] target Target system to get the TimeoutTime for. Pass UNASSIGNED_SYSTEM_ADDRESS to get the default value. - /// \return Timeout time for a given system. - RakNet::TimeMS GetTimeoutTime( const SystemAddress target ); - - /// \brief Returns the current MTU size - /// \param[in] target Which system to get MTU for. UNASSIGNED_SYSTEM_ADDRESS to get the default - /// \return The current MTU size of the target system. - int GetMTUSize( const SystemAddress target ) const; - - /// \brief Returns the number of IP addresses this system has internally. - /// \details Get the actual addresses from GetLocalIP() - unsigned GetNumberOfAddresses( void ); - - /// Returns an IP address at index 0 to GetNumberOfAddresses-1 in ipList array. - /// \param[in] index index into the list of IP addresses - /// \return The local IP address at this index - const char* GetLocalIP( unsigned int index ); - - /// Is this a local IP? - /// Checks if this ip is in the ipList array. - /// \param[in] An IP address to check, excluding the port. - /// \return True if this is one of the IP addresses returned by GetLocalIP - bool IsLocalIP( const char *ip ); - - /// \brief Allow or disallow connection responses from any IP. - /// \details Normally this should be false, but may be necessary when connecting to servers with multiple IP addresses. - /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections. - void AllowConnectionResponseIPMigration( bool allow ); - - /// \brief Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. - /// This will send our external IP outside the LAN along with some user data to the remote system. - /// \pre The sender and recipient must already be started via a successful call to Initialize - /// \param[in] host Either a dotted IP address or a domain name - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] data Optional data to append to the packet. - /// \param[in] dataLength Length of data in bytes. Use 0 if no data. - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \return False if IsActive()==false or the host is unresolvable. True otherwise. - bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 ); - - /// \brief Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. - /// \details ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived. - /// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. - /// Defaults to 0 (never return this notification). - /// \param[in] interval How many messages to use as an interval before a download progress notification is returned. - void SetSplitMessageProgressInterval(int interval); - - /// \brief Returns what was passed to SetSplitMessageProgressInterval(). - /// \return Number of messages to be recieved before a download progress notification is returned. Default to 0. - int GetSplitMessageProgressInterval(void) const; - - /// \brief Set how long to wait before giving up on sending an unreliable message. - /// Useful if the network is clogged up. - /// Set to 0 or less to never timeout. Defaults to 0. - /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. - void SetUnreliableTimeout(RakNet::TimeMS timeoutMS); - - /// \brief Send a message to a host, with the IP socket option TTL set to 3. - /// \details This message will not reach the host, but will open the router. - /// \param[in] host The address of the remote host in dotted notation. - /// \param[in] remotePort The port number to send to. - /// \param[in] ttl Max hops of datagram, set to 3 - /// \param[in] connectionSocketIndex userConnectionSocketIndex. - /// \remarks Used for NAT-Punchthrough - void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 ); - - // -------------------------------------------------------------------------------------------- Plugin Functions-------------------------------------------------------------------------------------------- - /// \brief Attaches a Plugin interface to an instance of the base class (RakPeer or PacketizedTCP) to run code automatically on message receipt in the Receive call. - /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. - /// \param[in] messageHandler Pointer to the plugin to attach. - void AttachPlugin( PluginInterface2 *plugin ); - - /// \brief Detaches a Plugin interface from the instance of the base class (RakPeer or PacketizedTCP) it is attached to. - /// \details This method disables the plugin code from running automatically on base class's updates or message receipt. - /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. - /// \param[in] messageHandler Pointer to a plugin to detach. - void DetachPlugin( PluginInterface2 *messageHandler ); - - // --------------------------------------------------------------------------------------------Miscellaneous Functions-------------------------------------------------------------------------------------------- - /// \brief Puts a message back in the receive queue in case you don't want to deal with it immediately. - /// \param[in] packet The pointer to the packet you want to push back. - /// \param[in] pushAtHead True to push the packet at the start of the queue so that the next receive call returns it. False to push it at the end of the queue. - /// \note Setting pushAtHead to false end makes the packets out of order. - void PushBackPacket( Packet *packet, bool pushAtHead ); - - /// \internal - /// \brief For a given system identified by \a guid, change the SystemAddress to send to. - /// \param[in] guid The connection we are referring to - /// \param[in] systemAddress The new address to send to - void ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress); - - /// \brief Returns a packet for you to write to if you want to create a Packet for some reason. - /// You can add it to the receive buffer with PushBackPacket - /// \param[in] dataSize How many bytes to allocate for the buffer - /// \return A packet. - Packet* AllocatePacket(unsigned dataSize); - - /// \brief Get the socket used with a particular active connection. - /// The smart pointer reference counts the RakNetSocket object, so the socket will remain active as long as the smart pointer does, even if RakNet were to shutdown or close the connection. - /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. - /// \param[in] target Which system. - /// \return A smart pointer object containing the socket information about the target. Be sure to check IsNull() which is returned if the update thread is unresponsive, shutting down, or if this system is not connected. - virtual RakNetSmartPtr GetSocket( const SystemAddress target ); - - /// \brief Gets all sockets in use. - /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. - /// \param[out] sockets List of RakNetSocket structures in use. Sockets will not be closed until \a sockets goes out of scope - virtual void GetSockets( DataStructures::List > &sockets ); - virtual void ReleaseSockets( DataStructures::List > &sockets ); - - /// \internal - virtual void WriteOutOfBandHeader(RakNet::BitStream *bitStream); - - /// If you need code to run in the same thread as RakNet's update thread, this function can be used for that - /// \param[in] _userUpdateThreadPtr C callback function - /// \param[in] _userUpdateThreadData Passed to C callback function - virtual void SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData); - - // --------------------------------------------------------------------------------------------Network Simulator Functions-------------------------------------------------------------------------------------------- - /// Adds simulated ping and packet loss to the outgoing data flow. - /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and packetloss value on each. - /// You can exclude network simulator code with the _RELEASE #define to decrease code size - /// \deprecated Use http://www.jenkinssoftware.com/forum/index.php?topic=1671.0 instead. - /// \note Doesn't work past version 3.6201 - /// \param[in] packetloss Chance to lose a packet. Ranges from 0 to 1. - /// \param[in] minExtraPing The minimum time to delay sends. - /// \param[in] extraPingVariance The additional random time to delay sends. - virtual void ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance); - - /// Limits how much outgoing bandwidth can be sent per-connection. - /// This limit does not apply to the sum of all connections! - /// Exceeding the limit queues up outgoing traffic - /// \param[in] maxBitsPerSecond Maximum bits per second to send. Use 0 for unlimited (default). Once set, it takes effect immedately and persists until called again. - virtual void SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ); - - /// Returns if you previously called ApplyNetworkSimulator - /// \return If you previously called ApplyNetworkSimulator - virtual bool IsNetworkSimulatorActive( void ); - - // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- - - /// \brief Returns a structure containing a large set of network statistics for the specified system. - /// You can map this data to a string using the C style StatisticsToString() function - /// \param[in] systemAddress Which connected system to get statistics for. - /// \param[in] rns If you supply this structure,the network statistics will be written to it. Otherwise the method uses a static struct to write the data, which is not threadsafe. - /// \return 0 if the specified system can't be found. Otherwise a pointer to the struct containing the specified system's network statistics. - /// \sa RakNetStatistics.h - RakNetStatistics * GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 ); - /// \brief Returns the network statistics of the system at the given index in the remoteSystemList. - /// \return True if the index is less than the maximum number of peers allowed and the system is active. False otherwise. - bool GetStatistics( const int index, RakNetStatistics *rns ); - - /// \Returns how many messages are waiting when you call Receive() - virtual unsigned int GetReceiveBufferSize(void); - - // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- - - /// \internal - bool SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 ); - - // static Packet *AllocPacket(unsigned dataSize, const char *file, unsigned int line); - - /// \internal - /// \brief Holds the clock differences between systems, along with the ping - struct PingAndClockDifferential - { - unsigned short pingTime; - RakNet::Time clockDifferential; - }; - - /// \internal - /// \brief All the information representing a connected system - struct RemoteSystemStruct - { - bool isActive; // Is this structure in use? - SystemAddress systemAddress; /// Their external IP on the internet - SystemAddress myExternalSystemAddress; /// Your external IP on the internet, from their perspective - SystemAddress theirInternalSystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; /// Their internal IP, behind the LAN - ReliabilityLayer reliabilityLayer; /// The reliability layer associated with this player - bool weInitiatedTheConnection; /// True if we started this connection via Connect. False if someone else connected to us. - PingAndClockDifferential pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE ]; /// last x ping times and calculated clock differentials with it - RakNet::Time pingAndClockDifferentialWriteIndex; /// The index we are writing into the pingAndClockDifferential circular buffer - unsigned short lowestPing; ///The lowest ping value encountered - RakNet::Time nextPingTime; /// When to next ping this player - RakNet::Time lastReliableSend; /// When did the last reliable send occur. Reliable sends must occur at least once every timeoutTime/2 units to notice disconnects - RakNet::Time connectionTime; /// connection time, if active. -// int connectionSocketIndex; // index into connectionSockets to send back on. - RakNetGUID guid; - int MTUSize; - // Reference counted socket to send back on - RakNetSmartPtr rakNetSocket; - SystemIndex remoteSystemIndex; - -#if LIBCAT_SECURITY==1 - // Cached answer used internally by RakPeer to prevent DoS attacks based on the connexion handshake - char answer[cat::EasyHandshake::ANSWER_BYTES]; - - // If the server has bRequireClientKey = true, then this is set to the validated public key of the connected client - // Valid after connectMode reaches HANDLING_CONNECTION_REQUEST - char client_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; -#endif - - enum ConnectMode {NO_ACTION, DISCONNECT_ASAP, DISCONNECT_ASAP_SILENTLY, DISCONNECT_ON_NO_ACK, REQUESTED_CONNECTION, HANDLING_CONNECTION_REQUEST, UNVERIFIED_SENDER, CONNECTED} connectMode; - }; - -protected: - - friend RAK_THREAD_DECLARATION(UpdateNetworkLoop); - friend RAK_THREAD_DECLARATION(RecvFromLoop); - friend RAK_THREAD_DECLARATION(UDTConnect); - - friend bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, bool *isOfflineMessage, RakNet::TimeUS timeRead ); - friend void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead, BitStream &updateBitStream ); - friend void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead, BitStream &updateBitStream ); - - int GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const; - int GetIndexFromGuid( const RakNetGUID guid ); - - //void RemoveFromRequestedConnectionsList( const SystemAddress systemAddress ); - // Two versions needed because some buggy compilers strip the last parameter if unused, and crashes - ConnectionAttemptResult SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime, RakNetSmartPtr socket ); - ConnectionAttemptResult SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ); - ///Get the reliability layer associated with a systemAddress. - /// \param[in] systemAddress The player identifier - /// \return 0 if none - RemoteSystemStruct *GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const; - RakPeer::RemoteSystemStruct *GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const; - void ValidateRemoteSystemLookup(void) const; - RemoteSystemStruct *GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const; - ///Parse out a connection request packet - void ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize); - void OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, RakNet::Time incomingTimestamp ); - ///Send a reliable disconnect packet to this player and disconnect them when it is delivered - void NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ); - ///Returns how many remote systems initiated a connection to us - unsigned short GetNumberOfRemoteInitiatedConnections( void ) const; - /// \brief Get a free remote system from the list and assign our systemAddress to it. - /// \note Should only be called from the update thread - not the user thread. - /// \param[in] systemAddress systemAddress to be assigned - /// \param[in] connectionMode connection mode of the RemoteSystem. - /// \param[in] rakNetSocket - /// \param[in] thisIPConnectedRecently Is this IP connected recently? set to False; - /// \param[in] bindingAddress Address to be binded with the remote system - /// \param[in] incomingMTU MTU for the remote system - RemoteSystemStruct * AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSmartPtr incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ); - /// \brief Adjust the timestamp of the incoming packet to be relative to this system. - /// \param[in] data Data in the incoming packet. - /// \param[in] systemAddress Sender of the incoming packet. - void ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const; - /// Get the most accurate clock differential for a certain player. - /// \param[in] systemAddress The player with whose clock the time difference is calculated. - /// \returns The clock differential for a certain player. - RakNet::Time GetBestClockDifferential( const SystemAddress systemAddress ) const; - - bool IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const; - SystemAddress GetLoopbackAddress(void) const; - - ///Set this to true to terminate the Peer thread execution - volatile bool endThreads; - ///true if the peer thread is active. - volatile bool isMainLoopThreadActive; - - RakNet::LocklessUint32_t isRecvFromLoopThreadActive; - - - bool occasionalPing; /// Do we occasionally ping the other systems?*/ - ///Store the maximum number of peers allowed to connect - unsigned short maximumNumberOfPeers; - //05/02/06 Just using maximumNumberOfPeers instead - ///Store the maximum number of peers able to connect, including reserved connection slots for pings, etc. - //unsigned short remoteSystemListSize; - ///Store the maximum incoming connection allowed - unsigned short maximumIncomingConnections; - RakNet::BitStream offlinePingResponse; - ///Local Player ID - // SystemAddress mySystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; - char incomingPassword[256]; - unsigned char incomingPasswordLength; - - /// This is an array of pointers to RemoteSystemStruct - /// This allows us to preallocate the list when starting, so we don't have to allocate or delete at runtime. - /// Another benefit is that is lets us add and remove active players simply by setting systemAddress - /// and moving elements in the list by copying pointers variables without affecting running threads, even if they are in the reliability layer - RemoteSystemStruct* remoteSystemList; - /// activeSystemList holds a list of pointers and is preallocated to be the same size as remoteSystemList. It is updated only by the network thread, but read by both threads - /// When the isActive member of RemoteSystemStruct is set to true or false, that system is added to this list of pointers - /// Threadsafe because RemoteSystemStruct is preallocated, and the list is only added to, not removed from - RemoteSystemStruct** activeSystemList; - unsigned int activeSystemListSize; - - // Use a hash, with binaryAddress plus port mod length as the index - RemoteSystemIndex **remoteSystemLookup; - unsigned int RemoteSystemLookupHashIndex(const SystemAddress &sa) const; - void ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex); - void DereferenceRemoteSystem(const SystemAddress &sa); - RemoteSystemStruct* GetRemoteSystem(const SystemAddress &sa) const; - unsigned int GetRemoteSystemIndex(const SystemAddress &sa) const; - void ClearRemoteSystemLookup(void); - DataStructures::MemoryPool remoteSystemIndexPool; - - void AddToActiveSystemList(unsigned int remoteSystemListIndex); - void RemoveFromActiveSystemList(const SystemAddress &sa); - -// unsigned int LookupIndexUsingHashIndex(const SystemAddress &sa) const; -// unsigned int RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const; -// unsigned int FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const; - - enum - { - // Only put these mutexes in user thread functions! - requestedConnectionList_Mutex, - offlinePingResponse_Mutex, - NUMBER_OF_RAKPEER_MUTEXES - }; - SimpleMutex rakPeerMutexes[ NUMBER_OF_RAKPEER_MUTEXES ]; - ///RunUpdateCycle is not thread safe but we don't need to mutex calls. Just skip calls if it is running already - - bool updateCycleIsRunning; - ///The list of people we have tried to connect to recently - - //DataStructures::Queue requestedConnectionsList; - ///Data that both the client and the server needs - - unsigned int bytesSentPerSecond, bytesReceivedPerSecond; - // bool isSocketLayerBlocking; - // bool continualPing,isRecvfromThreadActive,isMainLoopThreadActive, endThreads, isSocketLayerBlocking; - unsigned int validationInteger; - SimpleMutex incomingQueueMutex, banListMutex; //,synchronizedMemoryQueueMutex, automaticVariableSynchronizationMutex; - //DataStructures::Queue incomingpacketSingleProducerConsumer; //, synchronizedMemorypacketSingleProducerConsumer; - // BitStream enumerationData; - - struct BanStruct - { - char *IP; - RakNet::TimeMS timeout; // 0 for none - }; - - struct RequestedConnectionStruct - { - SystemAddress systemAddress; - RakNet::Time nextRequestTime; - unsigned char requestsMade; - char *data; - unsigned short dataLength; - char outgoingPassword[256]; - unsigned char outgoingPasswordLength; - unsigned socketIndex; - unsigned int extraData; - unsigned sendConnectionAttemptCount; - unsigned timeBetweenSendConnectionAttemptsMS; - RakNet::TimeMS timeoutTime; - PublicKeyMode publicKeyMode; - RakNetSmartPtr socket; - enum {CONNECT=1, /*PING=2, PING_OPEN_CONNECTIONS=4,*/ /*ADVERTISE_SYSTEM=2*/} actionToTake; - -#if LIBCAT_SECURITY==1 - char handshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; - cat::ClientEasyHandshake *client_handshake; - char remote_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; -// char remote_challenge[cat::EasyHandshake::CHALLENGE_BYTES]; - // char random[16]; -#endif - }; -#if LIBCAT_SECURITY==1 - bool GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey); -#endif - - //DataStructures::List* > automaticVariableSynchronizationList; - DataStructures::List banList; - // Threadsafe, and not thread safe - DataStructures::List pluginListTS, pluginListNTS; - - DataStructures::Queue requestedConnectionQueue; - SimpleMutex requestedConnectionQueueMutex; - - bool RunUpdateCycle( RakNet::TimeUS timeNS, RakNet::Time timeMS, BitStream &updateBitStream ); - // void RunMutexedUpdateCycle(void); - - struct BufferedCommandStruct - { - BitSize_t numberOfBitsToSend; - PacketPriority priority; - PacketReliability reliability; - char orderingChannel; - AddressOrGUID systemIdentifier; - bool broadcast; - RemoteSystemStruct::ConnectMode connectionMode; - NetworkID networkID; - bool blockingCommand; // Only used for RPC - char *data; - bool haveRakNetCloseSocket; - unsigned connectionSocketIndex; - unsigned short remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions; - SOCKET socket; - unsigned short port; - uint32_t receipt; - enum {BCS_SEND, BCS_CLOSE_CONNECTION, BCS_GET_SOCKET, BCS_CHANGE_SYSTEM_ADDRESS,/* BCS_USE_USER_SOCKET, BCS_REBIND_SOCKET_ADDRESS, BCS_RPC, BCS_RPC_SHIFT,*/ BCS_DO_NOTHING} command; - }; - - // Single producer single consumer queue using a linked list - //BufferedCommandStruct* bufferedCommandReadIndex, bufferedCommandWriteIndex; - - DataStructures::ThreadsafeAllocatingQueue bufferedCommands; - - - // Constructor not called! - struct RecvFromStruct - { - - - - char data[MAXIMUM_MTU_SIZE]; - - int bytesRead; - SystemAddress systemAddress; - RakNet::TimeUS timeRead; - SOCKET s; - unsigned short remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions; - }; - - - DataStructures::ThreadsafeAllocatingQueue bufferedPackets; - - - struct SocketQueryOutput - { - SocketQueryOutput() {} - ~SocketQueryOutput() {} - DataStructures::List > sockets; - }; - - DataStructures::ThreadsafeAllocatingQueue socketQueryOutput; - - - bool AllowIncomingConnections(void) const; - - void PingInternal( const SystemAddress target, bool performImmediate, PacketReliability reliability ); - // This stores the user send calls to be handled by the update thread. This way we don't have thread contention over systemAddresss - void CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ); - void SendBuffered( const char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ); - void SendBufferedList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ); - bool SendImmediate( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, RakNet::TimeUS currentTime, uint32_t receipt ); - //bool HandleBufferedRPC(BufferedCommandStruct *bcs, RakNet::TimeMS time); - void ClearBufferedCommands(void); - void ClearBufferedPackets(void); - void ClearSocketQueryOutput(void); - void ClearRequestedConnectionList(void); - void AddPacketToProducer(RakNet::Packet *p); - unsigned int GenerateSeedFromGuid(void); - SimpleMutex securityExceptionMutex; - - //DataStructures::AVLBalancedBinarySearchTree rpcTree; - int defaultMTUSize; - bool trackFrequencyTable; - - // Smart pointer so I can return the object to the user - DataStructures::List > socketList; - void DerefAllSockets(void); - unsigned int GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const; - // Used for RPC replies - RakNet::BitStream *replyFromTargetBS; - SystemAddress replyFromTargetPlayer; - bool replyFromTargetBroadcast; - - RakNet::TimeMS defaultTimeoutTime; - - // Generate and store a unique GUID - void GenerateGUID(void); - unsigned int GetSystemIndexFromGuid( const RakNetGUID input ) const; - RakNetGUID myGuid; - - unsigned maxOutgoingBPS; - - // Nobody would use the internet simulator in a final build. -#ifdef _DEBUG - double _packetloss; - unsigned short _minExtraPing, _extraPingVariance; -#endif - - ///How long it has been since things were updated by a call to receiveUpdate thread uses this to determine how long to sleep for - //unsigned int lastUserUpdateCycle; - /// True to allow connection accepted packets from anyone. False to only allow these packets from servers we requested a connection to. - bool allowConnectionResponseIPMigration; - - SystemAddress firstExternalID; - int splitMessageProgressInterval; - RakNet::TimeMS unreliableTimeout; - - // Systems in this list will not go through the secure connection process, even when secure connections are turned on. Wildcards are accepted. - DataStructures::List securityExceptionList; - - SystemAddress ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ]; - - bool allowInternalRouting; - - void (*userUpdateThreadPtr)(RakPeerInterface *, void *); - void *userUpdateThreadData; - - - SignaledEvent quitAndDataEvents; - bool limitConnectionFrequencyFromTheSameIP; - - SimpleMutex packetAllocationPoolMutex; - DataStructures::MemoryPool packetAllocationPool; - - SimpleMutex packetReturnMutex; - DataStructures::Queue packetReturnQueue; - Packet *AllocPacket(unsigned dataSize, const char *file, unsigned int line); - Packet *AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line); - - /// This is used to return a number to the user when they call Send identifying the message - /// This number will be returned back with ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS and is only returned - /// with the reliability types that contain RECEIPT in the name - SimpleMutex sendReceiptSerialMutex; - uint32_t sendReceiptSerial; - void ResetSendReceipt(void); - void OnConnectedPong(RakNet::Time sendPingTime, RakNet::Time sendPongTime, RemoteSystemStruct *remoteSystem); - void CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet); - -#if LIBCAT_SECURITY==1 - // Encryption and security - bool _using_security, _require_client_public_key; - char my_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; - cat::ServerEasyHandshake *_server_handshake; - cat::CookieJar *_cookie_jar; - bool InitializeClientSecurity(RequestedConnectionStruct *rcs, const char *public_key); -#endif - - - - - -} -// #if defined(SN_TARGET_PSP2) -// __attribute__((aligned(8))) -// #endif -; - -} // namespace RakNet - -#endif +/// \file +/// \brief Declares RakPeer class. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +// TODO - RakNet 4 - Add network simulator +// TODO - RakNet 4 - Enable disabling flow control per connections + +#ifndef __RAK_PEER_H +#define __RAK_PEER_H + +#include "ReliabilityLayer.h" +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "SingleProducerConsumer.h" +#include "SimpleMutex.h" +#include "DS_OrderedList.h" +#include "Export.h" +#include "RakString.h" +#include "RakThread.h" +#include "RakNetSocket.h" +#include "RakNetSmartPtr.h" +#include "DS_ThreadsafeAllocatingQueue.h" +#include "SignaledEvent.h" +#include "NativeFeatureIncludes.h" +#include "SecureHandshake.h" +#include "LocklessTypes.h" + +namespace RakNet { +/// Forward declarations +class HuffmanEncodingTree; +class PluginInterface2; + +// Sucks but this struct has to be outside the class. Inside and DevCPP won't let you refer to the struct as RakPeer::RemoteSystemIndex while GCC +// forces you to do RakPeer::RemoteSystemIndex +struct RemoteSystemIndex{unsigned index; RemoteSystemIndex *next;}; +//int RAK_DLL_EXPORT SystemAddressAndIndexComp( const SystemAddress &key, const RemoteSystemIndex &data ); // GCC requires RakPeer::RemoteSystemIndex or it won't compile + +///\brief Main interface for network communications. +/// \details It implements most of RakNet's functionality and is the primary interface for RakNet. +/// +/// Inherits RakPeerInterface. +/// +/// See the individual functions for what the class can do. +/// +class RAK_DLL_EXPORT RakPeer : public RakPeerInterface +{ +public: + ///Constructor + RakPeer(); + + ///Destructor + virtual ~RakPeer(); + + // --------------------------------------------------------------------------------------------Major Low Level Functions - Functions needed by most users-------------------------------------------------------------------------------------------- + /// \brief Starts the network threads and opens the listen port. + /// \details You must call this before calling Connect(). + /// \pre On the PS3, call Startup() after Client_Login() + /// \note Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). + /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections. + /// \param[in] maxConnections Maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so that the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.A hybrid would set it to the sum of both types of connections. + /// \param[in] localPort The port to listen for connections on. On linux the system may be set up so thast ports under 1024 are restricted for everything but the root user. Use a higher port for maximum compatibility. + /// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); However, on the XBOX be sure to use IPPROTO_VDP + /// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + /// \return RAKNET_STARTED on success, otherwise appropriate failure enumeration. + StartupResult Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority=-99999 ); + + /// If you accept connections, you must call this or else security will not be enabled for incoming connections. + /// This feature requires more round trips, bandwidth, and CPU time for the connection handshake + /// x64 builds require under 25% of the CPU time of other builds + /// See the Encryption sample for example usage + /// \pre Must be called while offline + /// \pre LIBCAT_SECURITY must be defined to 1 in NativeFeatureIncludes.h for this function to have any effect + /// \param[in] publicKey A pointer to the public key for accepting new connections + /// \param[in] privateKey A pointer to the private key for accepting new connections + /// \param[in] bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection + bool InitializeSecurity( const char *publicKey, const char *privateKey, bool bRequireClientKey = false ); + + /// Disables security for incoming connections. + /// \note Must be called while offline + void DisableSecurity( void ); + + /// \brief This is useful if you have a fixed-address internal server behind a LAN. + /// + /// Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. + /// \note If secure connections are on, do not use secure connections for a specific IP address. + /// \param[in] ip IP address to add. * wildcards are supported. + void AddToSecurityExceptionList(const char *ip); + + /// \brief Remove a specific connection previously added via AddToSecurityExceptionList. + /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. + void RemoveFromSecurityExceptionList(const char *ip); + + /// \brief Checks to see if a given IP is in the security exception list. + /// \param[in] IP address to check. + /// \return True if the IP address is found in security exception list, else returns false. + bool IsInSecurityExceptionList(const char *ip); + + /// \brief Sets the maximum number of incoming connections allowed. + /// \details If the number of incoming connections is less than the number of players currently connected, + /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, + /// it will be reduced to the maximum number of peers allowed. + /// + /// Defaults to 0, meaning by default, nobody can connect to you + /// \param[in] numberAllowed Maximum number of incoming connections allowed. + void SetMaximumIncomingConnections( unsigned short numberAllowed ); + + /// \brief Returns the value passed to SetMaximumIncomingConnections(). + /// \return Maximum number of incoming connections, which is always <= maxConnections + unsigned short GetMaximumIncomingConnections( void ) const; + + /// \brief Returns how many open connections exist at this time. + /// \return Number of open connections. + unsigned short NumberOfConnections(void) const; + + /// \brief Sets the password for the incoming connections. + /// \details The password must match in the call to Connect (defaults to none). + /// Pass 0 to passwordData to specify no password. + /// This is a way to set a low level password for all incoming connections. To selectively reject connections, implement your own scheme using CloseConnection() to remove unwanted connections. + /// \param[in] passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. Specify 0 for no password data + /// \param[in] passwordDataLength The length in bytes of passwordData + void SetIncomingPassword( const char* passwordData, int passwordDataLength ); + + /// \brief Gets the password passed to SetIncomingPassword + /// \param[out] passwordData Should point to a block large enough to hold the password data you passed to SetIncomingPassword() + /// \param[in,out] passwordDataLength Maximum size of the passwordData array. Modified to hold the number of bytes actually written. + void GetIncomingPassword( char* passwordData, int *passwordDataLength ); + + /// \brief Connect to the specified host (ip or domain name) and server port. + /// \details Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. + /// Calling both acts as a true peer. + /// + /// This is a non-blocking connection. + /// + /// The connection is successful when GetConnectionState() returns IS_CONNECTED or Receive() gets a message with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. + /// If the connection is not successful, such as a rejected connection or no response then neither of these things will happen. + /// \pre Requires that you first call Initialize. + /// \param[in] host Either a dotted IP address or a domain name. + /// \param[in] remotePort Port to connect to on the remote machine. + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData. + /// \param[in] publicKey The public key the server is using. If 0, the server is not using security. If non-zero, the publicKeyMode member determines how to connect + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to determine the one to send on. + /// \param[in] sendConnectionAttemptCount Number of datagrams to send to the other system to try to connect. + /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. + /// \param[in] timeoutTime Time to elapse before dropping the connection if a reliable message could not be sent. 0 to use the default value from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); + /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. + /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! + /// \note It is possible to immediately get back ID_CONNECTION_ATTEMPT_FAILED if you exceed the maxConnections parameter passed to Startup(). This could happen if you call CloseConnection() with sendDisconnectionNotificaiton true, then immediately call Connect() before the connection has closed. + ConnectionAttemptResult Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey=0, unsigned connectionSocketIndex=0, unsigned sendConnectionAttemptCount=6, unsigned timeBetweenSendConnectionAttemptsMS=1000, RakNet::TimeMS timeoutTime=0 ); + + /// \brief Connect to the specified host (ip or domain name) and server port. + /// \param[in] host Either a dotted IP address or a domain name. + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData. + /// \param[in] socket A bound socket returned by another instance of RakPeerInterface. + /// \param[in] sendConnectionAttemptCount Number of datagrams to send to the other system to try to connect. + /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned.. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. + /// \param[in] timeoutTime Time to elapse before dropping the connection if a reliable message could not be sent. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); + /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. + /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! + virtual ConnectionAttemptResult ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey=0, unsigned sendConnectionAttemptCount=6, unsigned timeBetweenSendConnectionAttemptsMS=1000, RakNet::TimeMS timeoutTime=0); + + /* /// \brief Connect to the specified network ID (Platform specific console function) + /// \details Does built-in NAT traversal + /// \param[in] networkServiceId Network ID structure for the online service + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData. + //bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength );*/ + + /// \brief Stops the network threads and closes all connections. + /// \param[in] blockDuration Wait time(milli seconds) for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. + /// \param[in] orderingChannel Channel on which ID_DISCONNECTION_NOTIFICATION will be sent, if blockDuration > 0. + /// \param[in] disconnectionNotificationPriority Priority of sending ID_DISCONNECTION_NOTIFICATION. + /// If set to 0, the disconnection notification won't be sent. + void Shutdown( unsigned int blockDuration, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY ); + + /// \brief Returns true if the network thread is running. + /// \return True if the network thread is running, False otherwise + bool IsActive( void ) const; + + /// \brief Fills the array remoteSystems with the SystemAddress of all the systems we are connected to. + /// \param[out] remoteSystems An array of SystemAddress structures, to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to get the number of systems we are connected to. + /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array. + bool GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const; + + /// Returns the next uint32_t that Send() will return + /// \note If using RakPeer from multiple threads, this may not be accurate for your thread. Use IncrementNextSendReceipt() in that case. + /// \return The next uint32_t that Send() or SendList will return + virtual uint32_t GetNextSendReceipt(void); + + /// Returns the next uint32_t that Send() will return, and increments the value by one + /// \note If using RakPeer from multiple threads, pass this to forceReceipt in the send function + /// \return The next uint32_t that Send() or SendList will return + virtual uint32_t IncrementNextSendReceipt(void); + + /// \brief Sends a block of data to the specified system that you are connected to. + /// \note This function only works while the connected. + /// \note The first byte should be a message identifier starting at ID_USER_PACKET_ENUM. + /// \param[in] data Block of data to send. + /// \param[in] length Size in bytes of the data to send. + /// \param[in] priority Priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliably to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, the channel to order these on. Messages are only ordered relative to other messages on the same stream. + /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + uint32_t Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); + + /// \brief "Send" to yourself rather than a remote system. + /// \details The message will be processed through the plugins and returned to the game as usual. + /// This function works anytime + /// \note The first byte should be a message identifier starting at ID_USER_PACKET_ENUM + /// \param[in] data Block of data to send. + /// \param[in] length Size in bytes of the data to send. + void SendLoopback( const char *data, const int length ); + + /// \brief Sends a block of data to the specified system that you are connected to. + /// + /// Same as the above version, but takes a BitStream as input. + /// \param[in] bitStream Bitstream to send + /// \param[in] priority Priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliably to send this data. See PacketPriority.h + /// \param[in] orderingChannel Channel to order the messages on, when using ordered or sequenced messages. Messages are only ordered relative to other messages on the same stream. + /// \param[in] systemIdentifier System Address or RakNetGUID to send this packet to, or in the case of broadcasting, the address not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none. + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. + uint32_t Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); + + /// \brief Sends multiple blocks of data, concatenating them automatically. + /// + /// This is equivalent to: + /// RakNet::BitStream bs; + /// bs.WriteAlignedBytes(block1, blockLength1); + /// bs.WriteAlignedBytes(block2, blockLength2); + /// bs.WriteAlignedBytes(block3, blockLength3); + /// Send(&bs, ...) + /// + /// This function only works when connected. + /// \param[in] data An array of pointers to blocks of data + /// \param[in] lengths An array of integers indicating the length of each block of data + /// \param[in] numParameters Length of the arrays data and lengths + /// \param[in] priority Priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliably to send this data. See PacketPriority.h + /// \param[in] orderingChannel Channel to order the messages on, when using ordered or sequenced messages. Messages are only ordered relative to other messages on the same stream. + /// \param[in] systemIdentifier System Address or RakNetGUID to send this packet to, or in the case of broadcasting, the address not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none. + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + uint32_t SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); + + /// \brief Gets a message from the incoming message queue. + /// \details Use DeallocatePacket() to deallocate the message after you are done with it. + /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. + /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. + /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. + /// \sa RakNetTypes.h contains struct Packet. + Packet* Receive( void ); + + /// \brief Call this to deallocate a message returned by Receive() when you are done handling it. + /// \param[in] packet Message to deallocate. + void DeallocatePacket( Packet *packet ); + + /// \brief Return the total number of connections we are allowed. + /// \return Total number of connections allowed. + unsigned short GetMaximumNumberOfPeers( void ) const; + + // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- + /// \brief Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). + /// \details This method closes the connection irrespective of who initiated the connection. + /// \param[in] target Which system to close the connection to. + /// \param[in] sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. + /// \param[in] channel Which ordering channel to send the disconnection notification on, if any + /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. + void CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY ); + + /// \brief Cancel a pending connection attempt. + /// \details If we are already connected, the connection stays open + /// \param[in] target Target system to cancel. + void CancelConnectionAttempt( const SystemAddress target ); + /// Returns if a system is connected, disconnected, connecting in progress, or various other states + /// \param[in] systemIdentifier The system we are referring to + /// \note This locks a mutex, do not call too frequently during connection attempts or the attempt will take longer and possibly even timeout + /// \return What state the remote system is in + ConnectionState GetConnectionState(const AddressOrGUID systemIdentifier); + + /// \brief Given \a systemAddress, returns its index into remoteSystemList. + /// \details Values range from 0 to the maximum number of players allowed - 1. + /// This includes systems which were formerly connected, but are now not connected. + /// \param[in] systemAddress The SystemAddress we are referring to + /// \return The index of this SystemAddress or -1 on system not found. + int GetIndexFromSystemAddress( const SystemAddress systemAddress ) const; + + /// \brief Given \a index into remoteSystemList, will return a SystemAddress. + /// This function is only useful for looping through all systems. + /// + /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. + /// \return The SystemAddress structure corresponding to \a index in remoteSystemList. + SystemAddress GetSystemAddressFromIndex( int index ); + + /// \brief Same as GetSystemAddressFromIndex but returns RakNetGUID + /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. + /// \return The RakNetGUID + RakNetGUID GetGUIDFromIndex( int index ); + + /// \brief Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient + /// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system + /// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the \a addresses list will match the size of the \a guids list. + /// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. + void GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const; + + /// \brief Bans an IP from connecting. + /// \details Banned IPs persist between connections but are not saved on shutdown nor loaded on startup. + /// \param[in] IP Dotted IP address. You can use * for a wildcard address, such as 128.0.0. * will ban all IP addresses starting with 128.0.0. + /// \param[in] milliseconds Gives time in milli seconds for a temporary ban of the IP address. Use 0 for a permanent ban. + void AddToBanList( const char *IP, RakNet::TimeMS milliseconds=0 ); + + /// \brief Allows a previously banned IP to connect. + /// param[in] Dotted IP address. You can use * as a wildcard. An IP such as 128.0.0.* will ban all IP addresses starting with 128.0.0. + void RemoveFromBanList( const char *IP ); + + /// \brief Allows all previously banned IPs to connect. + void ClearBanList( void ); + + /// \brief Returns true or false indicating if a particular IP is banned. + /// \param[in] IP Dotted IP address. + /// \return True if IP matches any IPs in the ban list, accounting for any wildcards. False otherwise. + bool IsBanned( const char *IP ); + + /// \brief Enable or disable allowing frequent connections from the same IP adderss + /// \details This is a security measure which is disabled by default, but can be set to true to prevent attackers from using up all connection slots. + /// \param[in] b True to limit connections from the same ip to at most 1 per 100 milliseconds. + void SetLimitIPConnectionFrequency(bool b); + + // --------------------------------------------------------------------------------------------Pinging Functions - Functions dealing with the automatic ping mechanism-------------------------------------------------------------------------------------------- + /// Send a ping to the specified connected system. + /// \pre The sender and recipient must already be started via a successful call to Startup() + /// \param[in] target Which system to ping + void Ping( const SystemAddress target ); + + /// \brief Send a ping to the specified unconnected system. + /// \details The remote system, if it is Initialized, will respond with ID_PONG followed by sizeof(RakNet::TimeMS) containing the system time the ping was sent. Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h + /// System should reply with ID_PONG if it is active + /// \param[in] host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return true on success, false on failure (unknown hostname) + bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 ); + + /// \brief Returns the average of all ping times read for the specific system or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The ping time for this system, or -1 + int GetAveragePing( const AddressOrGUID systemIdentifier ); + + /// \brief Returns the last ping time read for the specific system or -1 if none read yet. + /// \param[in] systemAddress Which system we are referring to + /// \return The last ping time for this system, or -1. + int GetLastPing( const AddressOrGUID systemIdentifier ) const; + + /// \brief Returns the lowest ping time read or -1 if none read yet. + /// \param[in] systemAddress Which system we are referring to + /// \return The lowest ping time for this system, or -1. + int GetLowestPing( const AddressOrGUID systemIdentifier ) const; + + /// Ping the remote systems every so often, or not. Can be called anytime. + /// By default this is true. Recommended to leave on, because congestion control uses it to determine how often to resend lost packets. + /// It would be true by default to prevent timestamp drift, since in the event of a clock spike, the timestamp deltas would no longer be accurate + /// \param[in] doPing True to start occasional pings. False to stop them. + void SetOccasionalPing( bool doPing ); + + // --------------------------------------------------------------------------------------------Static Data Functions - Functions dealing with API defined synchronized memory-------------------------------------------------------------------------------------------- + /// \brief Sets the data to send along with a LAN server discovery or offline ping reply. + /// \param[in] data Block of data to send, or 0 for none + /// \param[in] length Length of the data in bytes, or 0 for none + /// \note \a length should be under 400 bytes, as a security measure against flood attacks + /// \sa Ping.cpp + void SetOfflinePingResponse( const char *data, const unsigned int length ); + + /// \brief Returns pointers to a copy of the \a data passed to SetOfflinePingResponse. + /// \param[out] data A pointer to a copy of the data passed to SetOfflinePingResponse() + /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() + /// \sa SetOfflinePingResponse + void GetOfflinePingResponse( char **data, unsigned int *length ); + + //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- + /// \brief Returns the unique address identifier that represents you or another system on the the network and is based on your local IP / port. + /// \note Not supported by the XBOX + /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address + /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) + /// \return Identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy + SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const; + + /// \brief Returns the unique address identifier that represents the target on the the network and is based on the target's external IP / port. + /// \param[in] target The SystemAddress of the remote system. Usually the same for all systems, unless you have two or more network cards. + SystemAddress GetExternalID( const SystemAddress target ) const; + + /// Return my own GUID + const RakNetGUID GetMyGUID(void) const; + + /// Return the address bound to a socket at the specified index + SystemAddress GetMyBoundAddress(const int socketIndex=0); + + /// \brief Given a connected system address, this method gives the unique GUID representing that instance of RakPeer. + /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different. + /// Complexity is O(log2(n)). + /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID + /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID + /// \param[in] input The system address of the target system we are connected to. + const RakNetGUID& GetGuidFromSystemAddress( const SystemAddress input ) const; + + /// \brief Gives the system address of a connected system, given its GUID. + /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different. + /// Currently O(log(n)), but this may be improved in the future + /// If \a input is UNASSIGNED_RAKNET_GUID, UNASSIGNED_SYSTEM_ADDRESS is returned. + /// \param[in] input The RakNetGUID of the target system. + SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const; + + /// Given the SystemAddress of a connected system, get the public key they provided as an identity + /// Returns false if system address was not found or client public key is not known + /// \param[in] input The RakNetGUID of the system + /// \param[in] client_public_key The connected client's public key is copied to this address. Buffer must be cat::EasyHandshake::PUBLIC_KEY_BYTES bytes in length. + bool GetClientPublicKeyFromSystemAddress( const SystemAddress input, char *client_public_key ) const; + + /// \brief Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. + + /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. + /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. + /// Do not set different values for different computers that are connected to each other, or you won't be able to reconnect after ID_CONNECTION_LOST + /// \param[in] timeMS Time, in MS + /// \param[in] target SystemAddress structure of the target system. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. + void SetTimeoutTime( RakNet::TimeMS timeMS, const SystemAddress target ); + + /// \brief Returns the Timeout time for the given system. + /// \param[in] target Target system to get the TimeoutTime for. Pass UNASSIGNED_SYSTEM_ADDRESS to get the default value. + /// \return Timeout time for a given system. + RakNet::TimeMS GetTimeoutTime( const SystemAddress target ); + + /// \brief Returns the current MTU size + /// \param[in] target Which system to get MTU for. UNASSIGNED_SYSTEM_ADDRESS to get the default + /// \return The current MTU size of the target system. + int GetMTUSize( const SystemAddress target ) const; + + /// \brief Returns the number of IP addresses this system has internally. + /// \details Get the actual addresses from GetLocalIP() + unsigned GetNumberOfAddresses( void ); + + /// Returns an IP address at index 0 to GetNumberOfAddresses-1 in ipList array. + /// \param[in] index index into the list of IP addresses + /// \return The local IP address at this index + const char* GetLocalIP( unsigned int index ); + + /// Is this a local IP? + /// Checks if this ip is in the ipList array. + /// \param[in] An IP address to check, excluding the port. + /// \return True if this is one of the IP addresses returned by GetLocalIP + bool IsLocalIP( const char *ip ); + + /// \brief Allow or disallow connection responses from any IP. + /// \details Normally this should be false, but may be necessary when connecting to servers with multiple IP addresses. + /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections. + void AllowConnectionResponseIPMigration( bool allow ); + + /// \brief Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. + /// This will send our external IP outside the LAN along with some user data to the remote system. + /// \pre The sender and recipient must already be started via a successful call to Initialize + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] data Optional data to append to the packet. + /// \param[in] dataLength Length of data in bytes. Use 0 if no data. + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return False if IsActive()==false or the host is unresolvable. True otherwise. + bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 ); + + /// \brief Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. + /// \details ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived. + /// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. + /// Defaults to 0 (never return this notification). + /// \param[in] interval How many messages to use as an interval before a download progress notification is returned. + void SetSplitMessageProgressInterval(int interval); + + /// \brief Returns what was passed to SetSplitMessageProgressInterval(). + /// \return Number of messages to be recieved before a download progress notification is returned. Default to 0. + int GetSplitMessageProgressInterval(void) const; + + /// \brief Set how long to wait before giving up on sending an unreliable message. + /// Useful if the network is clogged up. + /// Set to 0 or less to never timeout. Defaults to 0. + /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. + void SetUnreliableTimeout(RakNet::TimeMS timeoutMS); + + /// \brief Send a message to a host, with the IP socket option TTL set to 3. + /// \details This message will not reach the host, but will open the router. + /// \param[in] host The address of the remote host in dotted notation. + /// \param[in] remotePort The port number to send to. + /// \param[in] ttl Max hops of datagram, set to 3 + /// \param[in] connectionSocketIndex userConnectionSocketIndex. + /// \remarks Used for NAT-Punchthrough + void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 ); + + // -------------------------------------------------------------------------------------------- Plugin Functions-------------------------------------------------------------------------------------------- + /// \brief Attaches a Plugin interface to an instance of the base class (RakPeer or PacketizedTCP) to run code automatically on message receipt in the Receive call. + /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. + /// \param[in] messageHandler Pointer to the plugin to attach. + void AttachPlugin( PluginInterface2 *plugin ); + + /// \brief Detaches a Plugin interface from the instance of the base class (RakPeer or PacketizedTCP) it is attached to. + /// \details This method disables the plugin code from running automatically on base class's updates or message receipt. + /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. + /// \param[in] messageHandler Pointer to a plugin to detach. + void DetachPlugin( PluginInterface2 *messageHandler ); + + // --------------------------------------------------------------------------------------------Miscellaneous Functions-------------------------------------------------------------------------------------------- + /// \brief Puts a message back in the receive queue in case you don't want to deal with it immediately. + /// \param[in] packet The pointer to the packet you want to push back. + /// \param[in] pushAtHead True to push the packet at the start of the queue so that the next receive call returns it. False to push it at the end of the queue. + /// \note Setting pushAtHead to false end makes the packets out of order. + void PushBackPacket( Packet *packet, bool pushAtHead ); + + /// \internal + /// \brief For a given system identified by \a guid, change the SystemAddress to send to. + /// \param[in] guid The connection we are referring to + /// \param[in] systemAddress The new address to send to + void ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress); + + /// \brief Returns a packet for you to write to if you want to create a Packet for some reason. + /// You can add it to the receive buffer with PushBackPacket + /// \param[in] dataSize How many bytes to allocate for the buffer + /// \return A packet. + Packet* AllocatePacket(unsigned dataSize); + + /// \brief Get the socket used with a particular active connection. + /// The smart pointer reference counts the RakNetSocket object, so the socket will remain active as long as the smart pointer does, even if RakNet were to shutdown or close the connection. + /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. + /// \param[in] target Which system. + /// \return A smart pointer object containing the socket information about the target. Be sure to check IsNull() which is returned if the update thread is unresponsive, shutting down, or if this system is not connected. + virtual RakNetSmartPtr GetSocket( const SystemAddress target ); + + /// \brief Gets all sockets in use. + /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. + /// \param[out] sockets List of RakNetSocket structures in use. Sockets will not be closed until \a sockets goes out of scope + virtual void GetSockets( DataStructures::List > &sockets ); + virtual void ReleaseSockets( DataStructures::List > &sockets ); + + /// \internal + virtual void WriteOutOfBandHeader(RakNet::BitStream *bitStream); + + /// If you need code to run in the same thread as RakNet's update thread, this function can be used for that + /// \param[in] _userUpdateThreadPtr C callback function + /// \param[in] _userUpdateThreadData Passed to C callback function + virtual void SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData); + + // --------------------------------------------------------------------------------------------Network Simulator Functions-------------------------------------------------------------------------------------------- + /// Adds simulated ping and packet loss to the outgoing data flow. + /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and packetloss value on each. + /// You can exclude network simulator code with the _RELEASE #define to decrease code size + /// \deprecated Use http://www.jenkinssoftware.com/forum/index.php?topic=1671.0 instead. + /// \note Doesn't work past version 3.6201 + /// \param[in] packetloss Chance to lose a packet. Ranges from 0 to 1. + /// \param[in] minExtraPing The minimum time to delay sends. + /// \param[in] extraPingVariance The additional random time to delay sends. + virtual void ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance); + + /// Limits how much outgoing bandwidth can be sent per-connection. + /// This limit does not apply to the sum of all connections! + /// Exceeding the limit queues up outgoing traffic + /// \param[in] maxBitsPerSecond Maximum bits per second to send. Use 0 for unlimited (default). Once set, it takes effect immedately and persists until called again. + virtual void SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ); + + /// Returns if you previously called ApplyNetworkSimulator + /// \return If you previously called ApplyNetworkSimulator + virtual bool IsNetworkSimulatorActive( void ); + + // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- + + /// \brief Returns a structure containing a large set of network statistics for the specified system. + /// You can map this data to a string using the C style StatisticsToString() function + /// \param[in] systemAddress Which connected system to get statistics for. + /// \param[in] rns If you supply this structure,the network statistics will be written to it. Otherwise the method uses a static struct to write the data, which is not threadsafe. + /// \return 0 if the specified system can't be found. Otherwise a pointer to the struct containing the specified system's network statistics. + /// \sa RakNetStatistics.h + RakNetStatistics * GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 ); + /// \brief Returns the network statistics of the system at the given index in the remoteSystemList. + /// \return True if the index is less than the maximum number of peers allowed and the system is active. False otherwise. + bool GetStatistics( const int index, RakNetStatistics *rns ); + + /// \Returns how many messages are waiting when you call Receive() + virtual unsigned int GetReceiveBufferSize(void); + + // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- + + /// \internal + bool SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 ); + + // static Packet *AllocPacket(unsigned dataSize, const char *file, unsigned int line); + + /// \internal + /// \brief Holds the clock differences between systems, along with the ping + struct PingAndClockDifferential + { + unsigned short pingTime; + RakNet::Time clockDifferential; + }; + + /// \internal + /// \brief All the information representing a connected system + struct RemoteSystemStruct + { + bool isActive; // Is this structure in use? + SystemAddress systemAddress; /// Their external IP on the internet + SystemAddress myExternalSystemAddress; /// Your external IP on the internet, from their perspective + SystemAddress theirInternalSystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; /// Their internal IP, behind the LAN + ReliabilityLayer reliabilityLayer; /// The reliability layer associated with this player + bool weInitiatedTheConnection; /// True if we started this connection via Connect. False if someone else connected to us. + PingAndClockDifferential pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE ]; /// last x ping times and calculated clock differentials with it + RakNet::Time pingAndClockDifferentialWriteIndex; /// The index we are writing into the pingAndClockDifferential circular buffer + unsigned short lowestPing; ///The lowest ping value encountered + RakNet::Time nextPingTime; /// When to next ping this player + RakNet::Time lastReliableSend; /// When did the last reliable send occur. Reliable sends must occur at least once every timeoutTime/2 units to notice disconnects + RakNet::Time connectionTime; /// connection time, if active. +// int connectionSocketIndex; // index into connectionSockets to send back on. + RakNetGUID guid; + int MTUSize; + // Reference counted socket to send back on + RakNetSmartPtr rakNetSocket; + SystemIndex remoteSystemIndex; + +#if LIBCAT_SECURITY==1 + // Cached answer used internally by RakPeer to prevent DoS attacks based on the connexion handshake + char answer[cat::EasyHandshake::ANSWER_BYTES]; + + // If the server has bRequireClientKey = true, then this is set to the validated public key of the connected client + // Valid after connectMode reaches HANDLING_CONNECTION_REQUEST + char client_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; +#endif + + enum ConnectMode {NO_ACTION, DISCONNECT_ASAP, DISCONNECT_ASAP_SILENTLY, DISCONNECT_ON_NO_ACK, REQUESTED_CONNECTION, HANDLING_CONNECTION_REQUEST, UNVERIFIED_SENDER, CONNECTED} connectMode; + }; + +protected: + + friend RAK_THREAD_DECLARATION(UpdateNetworkLoop); + friend RAK_THREAD_DECLARATION(RecvFromLoop); + friend RAK_THREAD_DECLARATION(UDTConnect); + + friend bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, bool *isOfflineMessage, RakNet::TimeUS timeRead ); + friend void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead, BitStream &updateBitStream ); + friend void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead, BitStream &updateBitStream ); + + int GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const; + int GetIndexFromGuid( const RakNetGUID guid ); + + //void RemoveFromRequestedConnectionsList( const SystemAddress systemAddress ); + // Two versions needed because some buggy compilers strip the last parameter if unused, and crashes + ConnectionAttemptResult SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime, RakNetSmartPtr socket ); + ConnectionAttemptResult SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ); + ///Get the reliability layer associated with a systemAddress. + /// \param[in] systemAddress The player identifier + /// \return 0 if none + RemoteSystemStruct *GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const; + RakPeer::RemoteSystemStruct *GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const; + void ValidateRemoteSystemLookup(void) const; + RemoteSystemStruct *GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const; + ///Parse out a connection request packet + void ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize); + void OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, RakNet::Time incomingTimestamp ); + ///Send a reliable disconnect packet to this player and disconnect them when it is delivered + void NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ); + ///Returns how many remote systems initiated a connection to us + unsigned short GetNumberOfRemoteInitiatedConnections( void ) const; + /// \brief Get a free remote system from the list and assign our systemAddress to it. + /// \note Should only be called from the update thread - not the user thread. + /// \param[in] systemAddress systemAddress to be assigned + /// \param[in] connectionMode connection mode of the RemoteSystem. + /// \param[in] rakNetSocket + /// \param[in] thisIPConnectedRecently Is this IP connected recently? set to False; + /// \param[in] bindingAddress Address to be binded with the remote system + /// \param[in] incomingMTU MTU for the remote system + RemoteSystemStruct * AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSmartPtr incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ); + /// \brief Adjust the timestamp of the incoming packet to be relative to this system. + /// \param[in] data Data in the incoming packet. + /// \param[in] systemAddress Sender of the incoming packet. + void ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const; + /// Get the most accurate clock differential for a certain player. + /// \param[in] systemAddress The player with whose clock the time difference is calculated. + /// \returns The clock differential for a certain player. + RakNet::Time GetBestClockDifferential( const SystemAddress systemAddress ) const; + + bool IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const; + SystemAddress GetLoopbackAddress(void) const; + + ///Set this to true to terminate the Peer thread execution + volatile bool endThreads; + ///true if the peer thread is active. + volatile bool isMainLoopThreadActive; + + RakNet::LocklessUint32_t isRecvFromLoopThreadActive; + + + bool occasionalPing; /// Do we occasionally ping the other systems?*/ + ///Store the maximum number of peers allowed to connect + unsigned short maximumNumberOfPeers; + //05/02/06 Just using maximumNumberOfPeers instead + ///Store the maximum number of peers able to connect, including reserved connection slots for pings, etc. + //unsigned short remoteSystemListSize; + ///Store the maximum incoming connection allowed + unsigned short maximumIncomingConnections; + RakNet::BitStream offlinePingResponse; + ///Local Player ID + // SystemAddress mySystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; + char incomingPassword[256]; + unsigned char incomingPasswordLength; + + /// This is an array of pointers to RemoteSystemStruct + /// This allows us to preallocate the list when starting, so we don't have to allocate or delete at runtime. + /// Another benefit is that is lets us add and remove active players simply by setting systemAddress + /// and moving elements in the list by copying pointers variables without affecting running threads, even if they are in the reliability layer + RemoteSystemStruct* remoteSystemList; + /// activeSystemList holds a list of pointers and is preallocated to be the same size as remoteSystemList. It is updated only by the network thread, but read by both threads + /// When the isActive member of RemoteSystemStruct is set to true or false, that system is added to this list of pointers + /// Threadsafe because RemoteSystemStruct is preallocated, and the list is only added to, not removed from + RemoteSystemStruct** activeSystemList; + unsigned int activeSystemListSize; + + // Use a hash, with binaryAddress plus port mod length as the index + RemoteSystemIndex **remoteSystemLookup; + unsigned int RemoteSystemLookupHashIndex(const SystemAddress &sa) const; + void ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex); + void DereferenceRemoteSystem(const SystemAddress &sa); + RemoteSystemStruct* GetRemoteSystem(const SystemAddress &sa) const; + unsigned int GetRemoteSystemIndex(const SystemAddress &sa) const; + void ClearRemoteSystemLookup(void); + DataStructures::MemoryPool remoteSystemIndexPool; + + void AddToActiveSystemList(unsigned int remoteSystemListIndex); + void RemoveFromActiveSystemList(const SystemAddress &sa); + +// unsigned int LookupIndexUsingHashIndex(const SystemAddress &sa) const; +// unsigned int RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const; +// unsigned int FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const; + + enum + { + // Only put these mutexes in user thread functions! + requestedConnectionList_Mutex, + offlinePingResponse_Mutex, + NUMBER_OF_RAKPEER_MUTEXES + }; + SimpleMutex rakPeerMutexes[ NUMBER_OF_RAKPEER_MUTEXES ]; + ///RunUpdateCycle is not thread safe but we don't need to mutex calls. Just skip calls if it is running already + + bool updateCycleIsRunning; + ///The list of people we have tried to connect to recently + + //DataStructures::Queue requestedConnectionsList; + ///Data that both the client and the server needs + + unsigned int bytesSentPerSecond, bytesReceivedPerSecond; + // bool isSocketLayerBlocking; + // bool continualPing,isRecvfromThreadActive,isMainLoopThreadActive, endThreads, isSocketLayerBlocking; + unsigned int validationInteger; + SimpleMutex incomingQueueMutex, banListMutex; //,synchronizedMemoryQueueMutex, automaticVariableSynchronizationMutex; + //DataStructures::Queue incomingpacketSingleProducerConsumer; //, synchronizedMemorypacketSingleProducerConsumer; + // BitStream enumerationData; + + struct BanStruct + { + char *IP; + RakNet::TimeMS timeout; // 0 for none + }; + + struct RequestedConnectionStruct + { + SystemAddress systemAddress; + RakNet::Time nextRequestTime; + unsigned char requestsMade; + char *data; + unsigned short dataLength; + char outgoingPassword[256]; + unsigned char outgoingPasswordLength; + unsigned socketIndex; + unsigned int extraData; + unsigned sendConnectionAttemptCount; + unsigned timeBetweenSendConnectionAttemptsMS; + RakNet::TimeMS timeoutTime; + PublicKeyMode publicKeyMode; + RakNetSmartPtr socket; + enum {CONNECT=1, /*PING=2, PING_OPEN_CONNECTIONS=4,*/ /*ADVERTISE_SYSTEM=2*/} actionToTake; + +#if LIBCAT_SECURITY==1 + char handshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; + cat::ClientEasyHandshake *client_handshake; + char remote_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; +// char remote_challenge[cat::EasyHandshake::CHALLENGE_BYTES]; + // char random[16]; +#endif + }; +#if LIBCAT_SECURITY==1 + bool GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey); +#endif + + //DataStructures::List* > automaticVariableSynchronizationList; + DataStructures::List banList; + // Threadsafe, and not thread safe + DataStructures::List pluginListTS, pluginListNTS; + + DataStructures::Queue requestedConnectionQueue; + SimpleMutex requestedConnectionQueueMutex; + + bool RunUpdateCycle( RakNet::TimeUS timeNS, RakNet::Time timeMS, BitStream &updateBitStream ); + // void RunMutexedUpdateCycle(void); + + struct BufferedCommandStruct + { + BitSize_t numberOfBitsToSend; + PacketPriority priority; + PacketReliability reliability; + char orderingChannel; + AddressOrGUID systemIdentifier; + bool broadcast; + RemoteSystemStruct::ConnectMode connectionMode; + NetworkID networkID; + bool blockingCommand; // Only used for RPC + char *data; + bool haveRakNetCloseSocket; + unsigned connectionSocketIndex; + unsigned short remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions; + SOCKET socket; + unsigned short port; + uint32_t receipt; + enum {BCS_SEND, BCS_CLOSE_CONNECTION, BCS_GET_SOCKET, BCS_CHANGE_SYSTEM_ADDRESS,/* BCS_USE_USER_SOCKET, BCS_REBIND_SOCKET_ADDRESS, BCS_RPC, BCS_RPC_SHIFT,*/ BCS_DO_NOTHING} command; + }; + + // Single producer single consumer queue using a linked list + //BufferedCommandStruct* bufferedCommandReadIndex, bufferedCommandWriteIndex; + + DataStructures::ThreadsafeAllocatingQueue bufferedCommands; + + + // Constructor not called! + struct RecvFromStruct + { + + + + char data[MAXIMUM_MTU_SIZE]; + + int bytesRead; + SystemAddress systemAddress; + RakNet::TimeUS timeRead; + SOCKET s; + unsigned short remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions; + }; + + + DataStructures::ThreadsafeAllocatingQueue bufferedPackets; + + + struct SocketQueryOutput + { + SocketQueryOutput() {} + ~SocketQueryOutput() {} + DataStructures::List > sockets; + }; + + DataStructures::ThreadsafeAllocatingQueue socketQueryOutput; + + + bool AllowIncomingConnections(void) const; + + void PingInternal( const SystemAddress target, bool performImmediate, PacketReliability reliability ); + // This stores the user send calls to be handled by the update thread. This way we don't have thread contention over systemAddresss + void CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ); + void SendBuffered( const char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ); + void SendBufferedList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ); + bool SendImmediate( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, RakNet::TimeUS currentTime, uint32_t receipt ); + //bool HandleBufferedRPC(BufferedCommandStruct *bcs, RakNet::TimeMS time); + void ClearBufferedCommands(void); + void ClearBufferedPackets(void); + void ClearSocketQueryOutput(void); + void ClearRequestedConnectionList(void); + void AddPacketToProducer(RakNet::Packet *p); + unsigned int GenerateSeedFromGuid(void); + SimpleMutex securityExceptionMutex; + + //DataStructures::AVLBalancedBinarySearchTree rpcTree; + int defaultMTUSize; + bool trackFrequencyTable; + + // Smart pointer so I can return the object to the user + DataStructures::List > socketList; + void DerefAllSockets(void); + unsigned int GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const; + // Used for RPC replies + RakNet::BitStream *replyFromTargetBS; + SystemAddress replyFromTargetPlayer; + bool replyFromTargetBroadcast; + + RakNet::TimeMS defaultTimeoutTime; + + // Generate and store a unique GUID + void GenerateGUID(void); + unsigned int GetSystemIndexFromGuid( const RakNetGUID input ) const; + RakNetGUID myGuid; + + unsigned maxOutgoingBPS; + + // Nobody would use the internet simulator in a final build. +#ifdef _DEBUG + double _packetloss; + unsigned short _minExtraPing, _extraPingVariance; +#endif + + ///How long it has been since things were updated by a call to receiveUpdate thread uses this to determine how long to sleep for + //unsigned int lastUserUpdateCycle; + /// True to allow connection accepted packets from anyone. False to only allow these packets from servers we requested a connection to. + bool allowConnectionResponseIPMigration; + + SystemAddress firstExternalID; + int splitMessageProgressInterval; + RakNet::TimeMS unreliableTimeout; + + // Systems in this list will not go through the secure connection process, even when secure connections are turned on. Wildcards are accepted. + DataStructures::List securityExceptionList; + + SystemAddress ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ]; + + bool allowInternalRouting; + + void (*userUpdateThreadPtr)(RakPeerInterface *, void *); + void *userUpdateThreadData; + + + SignaledEvent quitAndDataEvents; + bool limitConnectionFrequencyFromTheSameIP; + + SimpleMutex packetAllocationPoolMutex; + DataStructures::MemoryPool packetAllocationPool; + + SimpleMutex packetReturnMutex; + DataStructures::Queue packetReturnQueue; + Packet *AllocPacket(unsigned dataSize, const char *file, unsigned int line); + Packet *AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line); + + /// This is used to return a number to the user when they call Send identifying the message + /// This number will be returned back with ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS and is only returned + /// with the reliability types that contain RECEIPT in the name + SimpleMutex sendReceiptSerialMutex; + uint32_t sendReceiptSerial; + void ResetSendReceipt(void); + void OnConnectedPong(RakNet::Time sendPingTime, RakNet::Time sendPongTime, RemoteSystemStruct *remoteSystem); + void CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet); + +#if LIBCAT_SECURITY==1 + // Encryption and security + bool _using_security, _require_client_public_key; + char my_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; + cat::ServerEasyHandshake *_server_handshake; + cat::CookieJar *_cookie_jar; + bool InitializeClientSecurity(RequestedConnectionStruct *rcs, const char *public_key); +#endif + + + + + +} +// #if defined(SN_TARGET_PSP2) +// __attribute__((aligned(8))) +// #endif +; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakPeerInterface.h b/project/lib_projects/raknet/jni/RaknetSources/RakPeerInterface.h index 599980d..5eed507 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakPeerInterface.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakPeerInterface.h @@ -1,571 +1,571 @@ -/// \file -/// \brief An interface for RakPeer. Simply contains all user functions as pure virtuals. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAK_PEER_INTERFACE_H -#define __RAK_PEER_INTERFACE_H - -#include "PacketPriority.h" -#include "RakNetTypes.h" -#include "RakMemoryOverride.h" -#include "Export.h" -#include "DS_List.h" -#include "RakNetSmartPtr.h" -#include "RakNetSocket.h" - -namespace RakNet -{ -// Forward declarations -class BitStream; -class PluginInterface2; -struct RPCMap; -struct RakNetStatistics; -struct RakNetBandwidth; -class RouterInterface; -class NetworkIDManager; - -/// The primary interface for RakNet, RakPeer contains all major functions for the library. -/// See the individual functions for what the class can do. -/// \brief The main interface for network communications -class RAK_DLL_EXPORT RakPeerInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(RakPeerInterface) - - ///Destructor - virtual ~RakPeerInterface() {} - - // --------------------------------------------------------------------------------------------Major Low Level Functions - Functions needed by most users-------------------------------------------------------------------------------------------- - /// \brief Starts the network threads, opens the listen port. - /// \details You must call this before calling Connect(). - /// \pre On the PS3, call Startup() after Client_Login() - /// \pre On Android, add the necessary permission to your application's androidmanifest.xml: - /// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). - /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections - /// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections - /// \param[in] localPort The port to listen for connections on. On linux the system may be set up so thast ports under 1024 are restricted for everything but the root user. Use a higher port for maximum compatibility. - /// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); However, on the XBOX be sure to use IPPROTO_VDP - /// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - /// \return RAKNET_STARTED on success, otherwise appropriate failure enumeration. - virtual StartupResult Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority=-99999 )=0; - - /// If you accept connections, you must call this or else security will not be enabled for incoming connections. - /// This feature requires more round trips, bandwidth, and CPU time for the connection handshake - /// x64 builds require under 25% of the CPU time of other builds - /// See the Encryption sample for example usage - /// \pre Must be called while offline - /// \pre LIBCAT_SECURITY must be defined to 1 in NativeFeatureIncludes.h for this function to have any effect - /// \param[in] publicKey A pointer to the public key for accepting new connections - /// \param[in] privateKey A pointer to the private key for accepting new connections - /// \param[in] bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection - virtual bool InitializeSecurity( const char *publicKey, const char *privateKey, bool bRequireClientKey = false )=0; - - /// Disables security for incoming connections. - /// \note Must be called while offline - virtual void DisableSecurity( void )=0; - - /// If secure connections are on, do not use secure connections for a specific IP address. - /// This is useful if you have a fixed-address internal server behind a LAN. - /// \note Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. - /// \param[in] ip IP address to add. * wildcards are supported. - virtual void AddToSecurityExceptionList(const char *ip)=0; - - /// Remove a specific connection previously added via AddToSecurityExceptionList - /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. - virtual void RemoveFromSecurityExceptionList(const char *ip)=0; - - /// Checks to see if a given IP is in the security exception list - /// \param[in] IP address to check. - virtual bool IsInSecurityExceptionList(const char *ip)=0; - - /// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, - /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, - /// it will be reduced to the maximum number of peers allowed. - /// Defaults to 0, meaning by default, nobody can connect to you - /// \param[in] numberAllowed Maximum number of incoming connections allowed. - virtual void SetMaximumIncomingConnections( unsigned short numberAllowed )=0; - - /// Returns the value passed to SetMaximumIncomingConnections() - /// \return the maximum number of incoming connections, which is always <= maxConnections - virtual unsigned short GetMaximumIncomingConnections( void ) const=0; - - /// Returns how many open connections there are at this time - /// \return the number of open connections - virtual unsigned short NumberOfConnections(void) const=0; - - /// Sets the password incoming connections must match in the call to Connect (defaults to none). Pass 0 to passwordData to specify no password - /// This is a way to set a low level password for all incoming connections. To selectively reject connections, implement your own scheme using CloseConnection() to remove unwanted connections - /// \param[in] passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. Specify 0 for no password data - /// \param[in] passwordDataLength The length in bytes of passwordData - virtual void SetIncomingPassword( const char* passwordData, int passwordDataLength )=0; - - /// Gets the password passed to SetIncomingPassword - /// \param[out] passwordData Should point to a block large enough to hold the password data you passed to SetIncomingPassword() - /// \param[in,out] passwordDataLength Maximum size of the array passwordData. Modified to hold the number of bytes actually written - virtual void GetIncomingPassword( char* passwordData, int *passwordDataLength )=0; - - /// \brief Connect to the specified host (ip or domain name) and server port. - /// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. - /// Calling both acts as a true peer. This is a non-blocking connection. - /// You know the connection is successful when GetConnectionState() returns IS_CONNECTED or Receive() gets a message with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. - /// If the connection is not successful, such as a rejected connection or no response then neither of these things will happen. - /// \pre Requires that you first call Initialize - /// \param[in] host Either a dotted IP address or a domain name - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData - /// \param[in] publicKey The public key the server is using. If 0, the server is not using security. If non-zero, the publicKeyMode member determines how to connect - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \param[in] sendConnectionAttemptCount How many datagrams to send to the other system to try to connect. - /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. - /// \param[in] timeoutTime How long to keep the connection alive before dropping it on unable to send a reliable message. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); - /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. - /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! - /// \note It is possible to immediately get back ID_CONNECTION_ATTEMPT_FAILED if you exceed the maxConnections parameter passed to Startup(). This could happen if you call CloseConnection() with sendDisconnectionNotificaiton true, then immediately call Connect() before the connection has closed. - virtual ConnectionAttemptResult Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey=0, unsigned connectionSocketIndex=0, unsigned sendConnectionAttemptCount=12, unsigned timeBetweenSendConnectionAttemptsMS=500, RakNet::TimeMS timeoutTime=0 )=0; - - /// \brief Connect to the specified host (ip or domain name) and server port, using a shared socket from another instance of RakNet - /// \param[in] host Either a dotted IP address or a domain name - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData - /// \param[in] socket A bound socket returned by another instance of RakPeerInterface - /// \param[in] sendConnectionAttemptCount How many datagrams to send to the other system to try to connect. - /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. - /// \param[in] timeoutTime How long to keep the connection alive before dropping it on unable to send a reliable message. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); - /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. - /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! - virtual ConnectionAttemptResult ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey=0, unsigned sendConnectionAttemptCount=12, unsigned timeBetweenSendConnectionAttemptsMS=500, RakNet::TimeMS timeoutTime=0)=0; - - /// \brief Connect to the specified network ID (Platform specific console function) - /// \details Does built-in NAt traversal - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData - //virtual bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength )=0; - - /// \brief Stops the network threads and closes all connections. - /// \param[in] blockDuration How long, in milliseconds, you should wait for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. - /// \param[in] orderingChannel If blockDuration > 0, ID_DISCONNECTION_NOTIFICATION will be sent on this channel - /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. - /// If you set it to 0 then the disconnection notification won't be sent - virtual void Shutdown( unsigned int blockDuration, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY )=0; - - /// Returns if the network thread is running - /// \return true if the network thread is running, false otherwise - virtual bool IsActive( void ) const=0; - - /// Fills the array remoteSystems with the SystemAddress of all the systems we are connected to - /// \param[out] remoteSystems An array of SystemAddress structures to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to only get the number of systems we are connected to - /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array - virtual bool GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const=0; - - /// Returns the next uint32_t that Send() will return - /// \note If using RakPeer from multiple threads, this may not be accurate for your thread. Use IncrementNextSendReceipt() in that case. - /// \return The next uint32_t that Send() or SendList will return - virtual uint32_t GetNextSendReceipt(void)=0; - - /// Returns the next uint32_t that Send() will return, and increments the value by one - /// \note If using RakPeer from multiple threads, pass this to forceReceipt in the send function - /// \return The next uint32_t that Send() or SendList will return - virtual uint32_t IncrementNextSendReceipt(void)=0; - - /// Sends a block of data to the specified system that you are connected to. - /// This function only works while the connected - /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM - /// \param[in] data The block of data to send - /// \param[in] length The size in bytes of the data to send - /// \param[in] priority What priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliability to send this data. See PacketPriority.h - /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream - /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - virtual uint32_t Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; - - /// "Send" to yourself rather than a remote system. The message will be processed through the plugins and returned to the game as usual - /// This function works anytime - /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM - /// \param[in] data The block of data to send - /// \param[in] length The size in bytes of the data to send - virtual void SendLoopback( const char *data, const int length )=0; - - /// Sends a block of data to the specified system that you are connected to. Same as the above version, but takes a BitStream as input. - /// \param[in] bitStream The bitstream to send - /// \param[in] priority What priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliability to send this data. See PacketPriority.h - /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream - /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. - virtual uint32_t Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; - - /// Sends multiple blocks of data, concatenating them automatically. - /// - /// This is equivalent to: - /// RakNet::BitStream bs; - /// bs.WriteAlignedBytes(block1, blockLength1); - /// bs.WriteAlignedBytes(block2, blockLength2); - /// bs.WriteAlignedBytes(block3, blockLength3); - /// Send(&bs, ...) - /// - /// This function only works while the connected - /// \param[in] data An array of pointers to blocks of data - /// \param[in] lengths An array of integers indicating the length of each block of data - /// \param[in] numParameters Length of the arrays data and lengths - /// \param[in] priority What priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliability to send this data. See PacketPriority.h - /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream - /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - virtual uint32_t SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; - - /// Gets a message from the incoming message queue. - /// Use DeallocatePacket() to deallocate the message after you are done with it. - /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. - /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. - /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. - /// sa RakNetTypes.h contains struct Packet - virtual Packet* Receive( void )=0; - - /// Call this to deallocate a message returned by Receive() when you are done handling it. - /// \param[in] packet The message to deallocate. - virtual void DeallocatePacket( Packet *packet )=0; - - /// Return the total number of connections we are allowed - virtual unsigned short GetMaximumNumberOfPeers( void ) const=0; - - // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- - /// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). - /// \param[in] target Which system to close the connection to. - /// \param[in] sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. - /// \param[in] channel Which ordering channel to send the disconnection notification on, if any - /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. - virtual void CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY )=0; - - /// Returns if a system is connected, disconnected, connecting in progress, or various other states - /// \param[in] systemIdentifier The system we are referring to - /// \note This locks a mutex, do not call too frequently during connection attempts or the attempt will take longer and possibly even timeout - /// \return What state the remote system is in - virtual ConnectionState GetConnectionState(const AddressOrGUID systemIdentifier)=0; - - /// Cancel a pending connection attempt - /// If we are already connected, the connection stays open - /// \param[in] target Which system to cancel - virtual void CancelConnectionAttempt( const SystemAddress target )=0; - - /// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. - /// \param[in] systemAddress The SystemAddress we are referring to - /// \return The index of this SystemAddress or -1 on system not found. - virtual int GetIndexFromSystemAddress( const SystemAddress systemAddress ) const=0; - - /// This function is only useful for looping through all systems - /// Given an index, will return a SystemAddress. - /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. - /// \return The SystemAddress - virtual SystemAddress GetSystemAddressFromIndex( int index )=0; - - /// Same as GetSystemAddressFromIndex but returns RakNetGUID - /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. - /// \return The RakNetGUID - virtual RakNetGUID GetGUIDFromIndex( int index )=0; - - /// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient - /// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system - /// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. - /// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. - virtual void GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const=0; - - /// Bans an IP from connecting. Banned IPs persist between connections but are not saved on shutdown nor loaded on startup. - /// param[in] IP Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban all IP addresses starting with 128.0.0 - /// \param[in] milliseconds how many ms for a temporary ban. Use 0 for a permanent ban - virtual void AddToBanList( const char *IP, RakNet::TimeMS milliseconds=0 )=0; - - /// Allows a previously banned IP to connect. - /// param[in] Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will banAll IP addresses starting with 128.0.0 - virtual void RemoveFromBanList( const char *IP )=0; - - /// Allows all previously banned IPs to connect. - virtual void ClearBanList( void )=0; - - /// Returns true or false indicating if a particular IP is banned. - /// \param[in] IP - Dotted IP address. - /// \return true if IP matches any IPs in the ban list, accounting for any wildcards. False otherwise. - virtual bool IsBanned( const char *IP )=0; - - /// Enable or disable allowing frequent connections from the same IP adderss - /// This is a security measure which is disabled by default, but can be set to true to prevent attackers from using up all connection slots - /// \param[in] b True to limit connections from the same ip to at most 1 per 100 milliseconds. - virtual void SetLimitIPConnectionFrequency(bool b)=0; - - // --------------------------------------------------------------------------------------------Pinging Functions - Functions dealing with the automatic ping mechanism-------------------------------------------------------------------------------------------- - /// Send a ping to the specified connected system. - /// \pre The sender and recipient must already be started via a successful call to Startup() - /// \param[in] target Which system to ping - virtual void Ping( const SystemAddress target )=0; - - /// Send a ping to the specified unconnected system. The remote system, if it is Initialized, will respond with ID_PONG followed by sizeof(RakNet::TimeMS) containing the system time the ping was sent.(Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h - /// System should reply with ID_PONG if it is active - /// \param[in] host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \return true on success, false on failure (unknown hostname) - virtual bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 )=0; - - /// Returns the average of all ping times read for the specific system or -1 if none read yet - /// \param[in] systemAddress Which system we are referring to - /// \return The ping time for this system, or -1 - virtual int GetAveragePing( const AddressOrGUID systemIdentifier )=0; - - /// Returns the last ping time read for the specific system or -1 if none read yet - /// \param[in] systemAddress Which system we are referring to - /// \return The last ping time for this system, or -1 - virtual int GetLastPing( const AddressOrGUID systemIdentifier ) const=0; - - /// Returns the lowest ping time read or -1 if none read yet - /// \param[in] systemAddress Which system we are referring to - /// \return The lowest ping time for this system, or -1 - virtual int GetLowestPing( const AddressOrGUID systemIdentifier ) const=0; - - /// Ping the remote systems every so often, or not. Can be called anytime. - /// By default this is true. Recommended to leave on, because congestion control uses it to determine how often to resend lost packets. - /// It would be true by default to prevent timestamp drift, since in the event of a clock spike, the timestamp deltas would no longer be accurate - /// \param[in] doPing True to start occasional pings. False to stop them. - virtual void SetOccasionalPing( bool doPing )=0; - - // --------------------------------------------------------------------------------------------Static Data Functions - Functions dealing with API defined synchronized memory-------------------------------------------------------------------------------------------- - /// Sets the data to send along with a LAN server discovery or offline ping reply. - /// \a length should be under 400 bytes, as a security measure against flood attacks - /// \param[in] data a block of data to store, or 0 for none - /// \param[in] length The length of data in bytes, or 0 for none - /// \sa Ping.cpp - virtual void SetOfflinePingResponse( const char *data, const unsigned int length )=0; - - /// Returns pointers to a copy of the data passed to SetOfflinePingResponse - /// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() - /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() - /// \sa SetOfflinePingResponse - virtual void GetOfflinePingResponse( char **data, unsigned int *length )=0; - - //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- - /// Return the unique address identifier that represents you or another system on the the network and is based on your local IP / port. - /// \note Not supported by the XBOX - /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address - /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) - /// \return the identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy - virtual SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const=0; - - /// Return the unique address identifier that represents you on the the network and is based on your externalIP / port - /// (the IP / port the specified player uses to communicate with you) - /// \param[in] target Which remote system you are referring to for your external ID. Usually the same for all systems, unless you have two or more network cards. - virtual SystemAddress GetExternalID( const SystemAddress target ) const=0; - - /// Return my own GUID - virtual const RakNetGUID GetMyGUID(void) const=0; - - /// Return the address bound to a socket at the specified index - virtual SystemAddress GetMyBoundAddress(const int socketIndex=0)=0; - - /// Get a random number (to generate a GUID) - static uint64_t Get64BitUniqueRandomNumber(void); - - /// Given a connected system, give us the unique GUID representing that instance of RakPeer. - /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different - /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. - /// Returns UNASSIGNED_RAKNET_GUID if system address can't be found. - /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID - /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID - /// \param[in] input The system address of the system we are connected to - virtual const RakNetGUID& GetGuidFromSystemAddress( const SystemAddress input ) const=0; - - /// Given the GUID of a connected system, give us the system address of that system. - /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different - /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. - /// If \a input is UNASSIGNED_RAKNET_GUID, will return UNASSIGNED_SYSTEM_ADDRESS - /// \param[in] input The RakNetGUID of the system we are checking to see if we are connected to - virtual SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const=0; - - /// Given the SystemAddress of a connected system, get the public key they provided as an identity - /// Returns false if system address was not found or client public key is not known - /// \param[in] input The RakNetGUID of the system - /// \param[in] client_public_key The connected client's public key is copied to this address. Buffer must be cat::EasyHandshake::PUBLIC_KEY_BYTES bytes in length. - virtual bool GetClientPublicKeyFromSystemAddress( const SystemAddress input, char *client_public_key ) const=0; - - /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. - /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. - /// Do not set different values for different computers that are connected to each other, or you won't be able to reconnect after ID_CONNECTION_LOST - /// \param[in] timeMS Time, in MS - /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. - virtual void SetTimeoutTime( RakNet::TimeMS timeMS, const SystemAddress target )=0; - - /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS to get the default value - /// \return timeoutTime for a given system. - virtual RakNet::TimeMS GetTimeoutTime( const SystemAddress target )=0; - - /// Returns the current MTU size - /// \param[in] target Which system to get this for. UNASSIGNED_SYSTEM_ADDRESS to get the default - /// \return The current MTU size - virtual int GetMTUSize( const SystemAddress target ) const=0; - - /// Returns the number of IP addresses this system has internally. Get the actual addresses from GetLocalIP() - virtual unsigned GetNumberOfAddresses( void )=0; - - /// Returns an IP address at index 0 to GetNumberOfAddresses-1 - /// \param[in] index index into the list of IP addresses - /// \return The local IP address at this index - virtual const char* GetLocalIP( unsigned int index )=0; - - /// Is this a local IP? - /// \param[in] An IP address to check, excluding the port - /// \return True if this is one of the IP addresses returned by GetLocalIP - virtual bool IsLocalIP( const char *ip )=0; - - /// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary - /// when connecting to servers with multiple IP addresses. - /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections - virtual void AllowConnectionResponseIPMigration( bool allow )=0; - - /// Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. - /// This will tell the remote system our external IP outside the LAN along with some user data. - /// \pre The sender and recipient must already be started via a successful call to Initialize - /// \param[in] host Either a dotted IP address or a domain name - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] data Optional data to append to the packet. - /// \param[in] dataLength length of data in bytes. Use 0 if no data. - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \return false if IsActive()==false or the host is unresolvable. True otherwise - virtual bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 )=0; - - /// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. - /// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived - /// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. - /// Defaults to 0 (never return this notification) - /// \param[in] interval How many messages to use as an interval - virtual void SetSplitMessageProgressInterval(int interval)=0; - - /// Returns what was passed to SetSplitMessageProgressInterval() - /// \return What was passed to SetSplitMessageProgressInterval(). Default to 0. - virtual int GetSplitMessageProgressInterval(void) const=0; - - /// Set how long to wait before giving up on sending an unreliable message - /// Useful if the network is clogged up. - /// Set to 0 or less to never timeout. Defaults to 0. - /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. - virtual void SetUnreliableTimeout(RakNet::TimeMS timeoutMS)=0; - - /// Send a message to host, with the IP socket option TTL set to 3 - /// This message will not reach the host, but will open the router. - /// Used for NAT-Punchthrough - virtual void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 )=0; - - // -------------------------------------------------------------------------------------------- Plugin Functions-------------------------------------------------------------------------------------------- - /// \brief Attaches a Plugin interface to an instance of the base class (RakPeer or PacketizedTCP) to run code automatically on message receipt in the Receive call. - /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. - /// \param[in] messageHandler Pointer to the plugin to attach. - virtual void AttachPlugin( PluginInterface2 *plugin )=0; - - /// \brief Detaches a Plugin interface from the instance of the base class (RakPeer or PacketizedTCP) it is attached to. - /// \details This method disables the plugin code from running automatically on base class's updates or message receipt. - /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. - /// \param[in] messageHandler Pointer to a plugin to detach. - virtual void DetachPlugin( PluginInterface2 *messageHandler )=0; - - // --------------------------------------------------------------------------------------------Miscellaneous Functions-------------------------------------------------------------------------------------------- - /// Put a message back at the end of the receive queue in case you don't want to deal with it immediately - /// \param[in] packet The packet you want to push back. - /// \param[in] pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) - virtual void PushBackPacket( Packet *packet, bool pushAtHead )=0; - - /// \internal - /// \brief For a given system identified by \a guid, change the SystemAddress to send to. - /// \param[in] guid The connection we are referring to - /// \param[in] systemAddress The new address to send to - virtual void ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress)=0; - - /// \returns a packet for you to write to if you want to create a Packet for some reason. - /// You can add it to the receive buffer with PushBackPacket - /// \param[in] dataSize How many bytes to allocate for the buffer - /// \return A packet you can write to - virtual Packet* AllocatePacket(unsigned dataSize)=0; - - /// Get the socket used with a particular active connection - /// The smart pointer reference counts the RakNetSocket object, so the socket will remain active as long as the smart pointer does, even if RakNet were to shutdown or close the connection. - /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. - /// \param[in] target Which system - /// \return A smart pointer object containing the socket information about the socket. Be sure to check IsNull() which is returned if the update thread is unresponsive, shutting down, or if this system is not connected - virtual RakNetSmartPtr GetSocket( const SystemAddress target )=0; - - /// Get all sockets in use - /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. - /// \param[out] sockets List of RakNetSocket structures in use. Sockets will not be closed until \a sockets goes out of scope - virtual void GetSockets( DataStructures::List > &sockets )=0; - virtual void ReleaseSockets( DataStructures::List > &sockets )=0; - - virtual void WriteOutOfBandHeader(RakNet::BitStream *bitStream)=0; - - /// If you need code to run in the same thread as RakNet's update thread, this function can be used for that - /// \param[in] _userUpdateThreadPtr C callback function - /// \param[in] _userUpdateThreadData Passed to C callback function - virtual void SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData)=0; - - // --------------------------------------------------------------------------------------------Network Simulator Functions-------------------------------------------------------------------------------------------- - /// Adds simulated ping and packet loss to the outgoing data flow. - /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and packetloss value on each. - /// You can exclude network simulator code with the _RELEASE #define to decrease code size - /// \deprecated Use http://www.jenkinssoftware.com/forum/index.php?topic=1671.0 instead. - /// \note Doesn't work past version 3.6201 - /// \param[in] packetloss Chance to lose a packet. Ranges from 0 to 1. - /// \param[in] minExtraPing The minimum time to delay sends. - /// \param[in] extraPingVariance The additional random time to delay sends. - virtual void ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance)=0; - - /// Limits how much outgoing bandwidth can be sent per-connection. - /// This limit does not apply to the sum of all connections! - /// Exceeding the limit queues up outgoing traffic - /// \param[in] maxBitsPerSecond Maximum bits per second to send. Use 0 for unlimited (default). Once set, it takes effect immedately and persists until called again. - virtual void SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond )=0; - - /// Returns if you previously called ApplyNetworkSimulator - /// \return If you previously called ApplyNetworkSimulator - virtual bool IsNetworkSimulatorActive( void )=0; - - // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- - - /// Returns a structure containing a large set of network statistics for the specified system. - /// You can map this data to a string using the C style StatisticsToString() function - /// \param[in] systemAddress: Which connected system to get statistics for - /// \param[in] rns If you supply this structure, it will be written to it. Otherwise it will use a static struct, which is not threadsafe - /// \return 0 on can't find the specified system. A pointer to a set of data otherwise. - /// \sa RakNetStatistics.h - virtual RakNetStatistics * GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 )=0; - virtual bool GetStatistics( const int index, RakNetStatistics *rns )=0; - - /// \Returns how many messages are waiting when you call Receive() - virtual unsigned int GetReceiveBufferSize(void)=0; - - // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- - - /// \internal - virtual bool SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 )=0; - - int errorState; -} -// #if defined(SN_TARGET_PSP2) -// __attribute__((aligned(8))) -// #endif -; - -} // namespace RakNet - -#endif +/// \file +/// \brief An interface for RakPeer. Simply contains all user functions as pure virtuals. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAK_PEER_INTERFACE_H +#define __RAK_PEER_INTERFACE_H + +#include "PacketPriority.h" +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" +#include "Export.h" +#include "DS_List.h" +#include "RakNetSmartPtr.h" +#include "RakNetSocket.h" + +namespace RakNet +{ +// Forward declarations +class BitStream; +class PluginInterface2; +struct RPCMap; +struct RakNetStatistics; +struct RakNetBandwidth; +class RouterInterface; +class NetworkIDManager; + +/// The primary interface for RakNet, RakPeer contains all major functions for the library. +/// See the individual functions for what the class can do. +/// \brief The main interface for network communications +class RAK_DLL_EXPORT RakPeerInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(RakPeerInterface) + + ///Destructor + virtual ~RakPeerInterface() {} + + // --------------------------------------------------------------------------------------------Major Low Level Functions - Functions needed by most users-------------------------------------------------------------------------------------------- + /// \brief Starts the network threads, opens the listen port. + /// \details You must call this before calling Connect(). + /// \pre On the PS3, call Startup() after Client_Login() + /// \pre On Android, add the necessary permission to your application's androidmanifest.xml: + /// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). + /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections + /// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections + /// \param[in] localPort The port to listen for connections on. On linux the system may be set up so thast ports under 1024 are restricted for everything but the root user. Use a higher port for maximum compatibility. + /// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); However, on the XBOX be sure to use IPPROTO_VDP + /// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + /// \return RAKNET_STARTED on success, otherwise appropriate failure enumeration. + virtual StartupResult Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority=-99999 )=0; + + /// If you accept connections, you must call this or else security will not be enabled for incoming connections. + /// This feature requires more round trips, bandwidth, and CPU time for the connection handshake + /// x64 builds require under 25% of the CPU time of other builds + /// See the Encryption sample for example usage + /// \pre Must be called while offline + /// \pre LIBCAT_SECURITY must be defined to 1 in NativeFeatureIncludes.h for this function to have any effect + /// \param[in] publicKey A pointer to the public key for accepting new connections + /// \param[in] privateKey A pointer to the private key for accepting new connections + /// \param[in] bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection + virtual bool InitializeSecurity( const char *publicKey, const char *privateKey, bool bRequireClientKey = false )=0; + + /// Disables security for incoming connections. + /// \note Must be called while offline + virtual void DisableSecurity( void )=0; + + /// If secure connections are on, do not use secure connections for a specific IP address. + /// This is useful if you have a fixed-address internal server behind a LAN. + /// \note Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. + /// \param[in] ip IP address to add. * wildcards are supported. + virtual void AddToSecurityExceptionList(const char *ip)=0; + + /// Remove a specific connection previously added via AddToSecurityExceptionList + /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. + virtual void RemoveFromSecurityExceptionList(const char *ip)=0; + + /// Checks to see if a given IP is in the security exception list + /// \param[in] IP address to check. + virtual bool IsInSecurityExceptionList(const char *ip)=0; + + /// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, + /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, + /// it will be reduced to the maximum number of peers allowed. + /// Defaults to 0, meaning by default, nobody can connect to you + /// \param[in] numberAllowed Maximum number of incoming connections allowed. + virtual void SetMaximumIncomingConnections( unsigned short numberAllowed )=0; + + /// Returns the value passed to SetMaximumIncomingConnections() + /// \return the maximum number of incoming connections, which is always <= maxConnections + virtual unsigned short GetMaximumIncomingConnections( void ) const=0; + + /// Returns how many open connections there are at this time + /// \return the number of open connections + virtual unsigned short NumberOfConnections(void) const=0; + + /// Sets the password incoming connections must match in the call to Connect (defaults to none). Pass 0 to passwordData to specify no password + /// This is a way to set a low level password for all incoming connections. To selectively reject connections, implement your own scheme using CloseConnection() to remove unwanted connections + /// \param[in] passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. Specify 0 for no password data + /// \param[in] passwordDataLength The length in bytes of passwordData + virtual void SetIncomingPassword( const char* passwordData, int passwordDataLength )=0; + + /// Gets the password passed to SetIncomingPassword + /// \param[out] passwordData Should point to a block large enough to hold the password data you passed to SetIncomingPassword() + /// \param[in,out] passwordDataLength Maximum size of the array passwordData. Modified to hold the number of bytes actually written + virtual void GetIncomingPassword( char* passwordData, int *passwordDataLength )=0; + + /// \brief Connect to the specified host (ip or domain name) and server port. + /// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. + /// Calling both acts as a true peer. This is a non-blocking connection. + /// You know the connection is successful when GetConnectionState() returns IS_CONNECTED or Receive() gets a message with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. + /// If the connection is not successful, such as a rejected connection or no response then neither of these things will happen. + /// \pre Requires that you first call Initialize + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData + /// \param[in] publicKey The public key the server is using. If 0, the server is not using security. If non-zero, the publicKeyMode member determines how to connect + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \param[in] sendConnectionAttemptCount How many datagrams to send to the other system to try to connect. + /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. + /// \param[in] timeoutTime How long to keep the connection alive before dropping it on unable to send a reliable message. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); + /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. + /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! + /// \note It is possible to immediately get back ID_CONNECTION_ATTEMPT_FAILED if you exceed the maxConnections parameter passed to Startup(). This could happen if you call CloseConnection() with sendDisconnectionNotificaiton true, then immediately call Connect() before the connection has closed. + virtual ConnectionAttemptResult Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey=0, unsigned connectionSocketIndex=0, unsigned sendConnectionAttemptCount=12, unsigned timeBetweenSendConnectionAttemptsMS=500, RakNet::TimeMS timeoutTime=0 )=0; + + /// \brief Connect to the specified host (ip or domain name) and server port, using a shared socket from another instance of RakNet + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData + /// \param[in] socket A bound socket returned by another instance of RakPeerInterface + /// \param[in] sendConnectionAttemptCount How many datagrams to send to the other system to try to connect. + /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. + /// \param[in] timeoutTime How long to keep the connection alive before dropping it on unable to send a reliable message. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); + /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. + /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! + virtual ConnectionAttemptResult ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey=0, unsigned sendConnectionAttemptCount=12, unsigned timeBetweenSendConnectionAttemptsMS=500, RakNet::TimeMS timeoutTime=0)=0; + + /// \brief Connect to the specified network ID (Platform specific console function) + /// \details Does built-in NAt traversal + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData + //virtual bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength )=0; + + /// \brief Stops the network threads and closes all connections. + /// \param[in] blockDuration How long, in milliseconds, you should wait for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. + /// \param[in] orderingChannel If blockDuration > 0, ID_DISCONNECTION_NOTIFICATION will be sent on this channel + /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. + /// If you set it to 0 then the disconnection notification won't be sent + virtual void Shutdown( unsigned int blockDuration, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY )=0; + + /// Returns if the network thread is running + /// \return true if the network thread is running, false otherwise + virtual bool IsActive( void ) const=0; + + /// Fills the array remoteSystems with the SystemAddress of all the systems we are connected to + /// \param[out] remoteSystems An array of SystemAddress structures to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to only get the number of systems we are connected to + /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array + virtual bool GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const=0; + + /// Returns the next uint32_t that Send() will return + /// \note If using RakPeer from multiple threads, this may not be accurate for your thread. Use IncrementNextSendReceipt() in that case. + /// \return The next uint32_t that Send() or SendList will return + virtual uint32_t GetNextSendReceipt(void)=0; + + /// Returns the next uint32_t that Send() will return, and increments the value by one + /// \note If using RakPeer from multiple threads, pass this to forceReceipt in the send function + /// \return The next uint32_t that Send() or SendList will return + virtual uint32_t IncrementNextSendReceipt(void)=0; + + /// Sends a block of data to the specified system that you are connected to. + /// This function only works while the connected + /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM + /// \param[in] data The block of data to send + /// \param[in] length The size in bytes of the data to send + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + virtual uint32_t Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; + + /// "Send" to yourself rather than a remote system. The message will be processed through the plugins and returned to the game as usual + /// This function works anytime + /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM + /// \param[in] data The block of data to send + /// \param[in] length The size in bytes of the data to send + virtual void SendLoopback( const char *data, const int length )=0; + + /// Sends a block of data to the specified system that you are connected to. Same as the above version, but takes a BitStream as input. + /// \param[in] bitStream The bitstream to send + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. + virtual uint32_t Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; + + /// Sends multiple blocks of data, concatenating them automatically. + /// + /// This is equivalent to: + /// RakNet::BitStream bs; + /// bs.WriteAlignedBytes(block1, blockLength1); + /// bs.WriteAlignedBytes(block2, blockLength2); + /// bs.WriteAlignedBytes(block3, blockLength3); + /// Send(&bs, ...) + /// + /// This function only works while the connected + /// \param[in] data An array of pointers to blocks of data + /// \param[in] lengths An array of integers indicating the length of each block of data + /// \param[in] numParameters Length of the arrays data and lengths + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + virtual uint32_t SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; + + /// Gets a message from the incoming message queue. + /// Use DeallocatePacket() to deallocate the message after you are done with it. + /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. + /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. + /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. + /// sa RakNetTypes.h contains struct Packet + virtual Packet* Receive( void )=0; + + /// Call this to deallocate a message returned by Receive() when you are done handling it. + /// \param[in] packet The message to deallocate. + virtual void DeallocatePacket( Packet *packet )=0; + + /// Return the total number of connections we are allowed + virtual unsigned short GetMaximumNumberOfPeers( void ) const=0; + + // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- + /// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). + /// \param[in] target Which system to close the connection to. + /// \param[in] sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. + /// \param[in] channel Which ordering channel to send the disconnection notification on, if any + /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. + virtual void CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY )=0; + + /// Returns if a system is connected, disconnected, connecting in progress, or various other states + /// \param[in] systemIdentifier The system we are referring to + /// \note This locks a mutex, do not call too frequently during connection attempts or the attempt will take longer and possibly even timeout + /// \return What state the remote system is in + virtual ConnectionState GetConnectionState(const AddressOrGUID systemIdentifier)=0; + + /// Cancel a pending connection attempt + /// If we are already connected, the connection stays open + /// \param[in] target Which system to cancel + virtual void CancelConnectionAttempt( const SystemAddress target )=0; + + /// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. + /// \param[in] systemAddress The SystemAddress we are referring to + /// \return The index of this SystemAddress or -1 on system not found. + virtual int GetIndexFromSystemAddress( const SystemAddress systemAddress ) const=0; + + /// This function is only useful for looping through all systems + /// Given an index, will return a SystemAddress. + /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. + /// \return The SystemAddress + virtual SystemAddress GetSystemAddressFromIndex( int index )=0; + + /// Same as GetSystemAddressFromIndex but returns RakNetGUID + /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. + /// \return The RakNetGUID + virtual RakNetGUID GetGUIDFromIndex( int index )=0; + + /// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient + /// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system + /// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. + /// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. + virtual void GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const=0; + + /// Bans an IP from connecting. Banned IPs persist between connections but are not saved on shutdown nor loaded on startup. + /// param[in] IP Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban all IP addresses starting with 128.0.0 + /// \param[in] milliseconds how many ms for a temporary ban. Use 0 for a permanent ban + virtual void AddToBanList( const char *IP, RakNet::TimeMS milliseconds=0 )=0; + + /// Allows a previously banned IP to connect. + /// param[in] Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will banAll IP addresses starting with 128.0.0 + virtual void RemoveFromBanList( const char *IP )=0; + + /// Allows all previously banned IPs to connect. + virtual void ClearBanList( void )=0; + + /// Returns true or false indicating if a particular IP is banned. + /// \param[in] IP - Dotted IP address. + /// \return true if IP matches any IPs in the ban list, accounting for any wildcards. False otherwise. + virtual bool IsBanned( const char *IP )=0; + + /// Enable or disable allowing frequent connections from the same IP adderss + /// This is a security measure which is disabled by default, but can be set to true to prevent attackers from using up all connection slots + /// \param[in] b True to limit connections from the same ip to at most 1 per 100 milliseconds. + virtual void SetLimitIPConnectionFrequency(bool b)=0; + + // --------------------------------------------------------------------------------------------Pinging Functions - Functions dealing with the automatic ping mechanism-------------------------------------------------------------------------------------------- + /// Send a ping to the specified connected system. + /// \pre The sender and recipient must already be started via a successful call to Startup() + /// \param[in] target Which system to ping + virtual void Ping( const SystemAddress target )=0; + + /// Send a ping to the specified unconnected system. The remote system, if it is Initialized, will respond with ID_PONG followed by sizeof(RakNet::TimeMS) containing the system time the ping was sent.(Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h + /// System should reply with ID_PONG if it is active + /// \param[in] host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return true on success, false on failure (unknown hostname) + virtual bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 )=0; + + /// Returns the average of all ping times read for the specific system or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The ping time for this system, or -1 + virtual int GetAveragePing( const AddressOrGUID systemIdentifier )=0; + + /// Returns the last ping time read for the specific system or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The last ping time for this system, or -1 + virtual int GetLastPing( const AddressOrGUID systemIdentifier ) const=0; + + /// Returns the lowest ping time read or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The lowest ping time for this system, or -1 + virtual int GetLowestPing( const AddressOrGUID systemIdentifier ) const=0; + + /// Ping the remote systems every so often, or not. Can be called anytime. + /// By default this is true. Recommended to leave on, because congestion control uses it to determine how often to resend lost packets. + /// It would be true by default to prevent timestamp drift, since in the event of a clock spike, the timestamp deltas would no longer be accurate + /// \param[in] doPing True to start occasional pings. False to stop them. + virtual void SetOccasionalPing( bool doPing )=0; + + // --------------------------------------------------------------------------------------------Static Data Functions - Functions dealing with API defined synchronized memory-------------------------------------------------------------------------------------------- + /// Sets the data to send along with a LAN server discovery or offline ping reply. + /// \a length should be under 400 bytes, as a security measure against flood attacks + /// \param[in] data a block of data to store, or 0 for none + /// \param[in] length The length of data in bytes, or 0 for none + /// \sa Ping.cpp + virtual void SetOfflinePingResponse( const char *data, const unsigned int length )=0; + + /// Returns pointers to a copy of the data passed to SetOfflinePingResponse + /// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() + /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() + /// \sa SetOfflinePingResponse + virtual void GetOfflinePingResponse( char **data, unsigned int *length )=0; + + //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- + /// Return the unique address identifier that represents you or another system on the the network and is based on your local IP / port. + /// \note Not supported by the XBOX + /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address + /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) + /// \return the identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy + virtual SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const=0; + + /// Return the unique address identifier that represents you on the the network and is based on your externalIP / port + /// (the IP / port the specified player uses to communicate with you) + /// \param[in] target Which remote system you are referring to for your external ID. Usually the same for all systems, unless you have two or more network cards. + virtual SystemAddress GetExternalID( const SystemAddress target ) const=0; + + /// Return my own GUID + virtual const RakNetGUID GetMyGUID(void) const=0; + + /// Return the address bound to a socket at the specified index + virtual SystemAddress GetMyBoundAddress(const int socketIndex=0)=0; + + /// Get a random number (to generate a GUID) + static uint64_t Get64BitUniqueRandomNumber(void); + + /// Given a connected system, give us the unique GUID representing that instance of RakPeer. + /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different + /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. + /// Returns UNASSIGNED_RAKNET_GUID if system address can't be found. + /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID + /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID + /// \param[in] input The system address of the system we are connected to + virtual const RakNetGUID& GetGuidFromSystemAddress( const SystemAddress input ) const=0; + + /// Given the GUID of a connected system, give us the system address of that system. + /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different + /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. + /// If \a input is UNASSIGNED_RAKNET_GUID, will return UNASSIGNED_SYSTEM_ADDRESS + /// \param[in] input The RakNetGUID of the system we are checking to see if we are connected to + virtual SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const=0; + + /// Given the SystemAddress of a connected system, get the public key they provided as an identity + /// Returns false if system address was not found or client public key is not known + /// \param[in] input The RakNetGUID of the system + /// \param[in] client_public_key The connected client's public key is copied to this address. Buffer must be cat::EasyHandshake::PUBLIC_KEY_BYTES bytes in length. + virtual bool GetClientPublicKeyFromSystemAddress( const SystemAddress input, char *client_public_key ) const=0; + + /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. + /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. + /// Do not set different values for different computers that are connected to each other, or you won't be able to reconnect after ID_CONNECTION_LOST + /// \param[in] timeMS Time, in MS + /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. + virtual void SetTimeoutTime( RakNet::TimeMS timeMS, const SystemAddress target )=0; + + /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS to get the default value + /// \return timeoutTime for a given system. + virtual RakNet::TimeMS GetTimeoutTime( const SystemAddress target )=0; + + /// Returns the current MTU size + /// \param[in] target Which system to get this for. UNASSIGNED_SYSTEM_ADDRESS to get the default + /// \return The current MTU size + virtual int GetMTUSize( const SystemAddress target ) const=0; + + /// Returns the number of IP addresses this system has internally. Get the actual addresses from GetLocalIP() + virtual unsigned GetNumberOfAddresses( void )=0; + + /// Returns an IP address at index 0 to GetNumberOfAddresses-1 + /// \param[in] index index into the list of IP addresses + /// \return The local IP address at this index + virtual const char* GetLocalIP( unsigned int index )=0; + + /// Is this a local IP? + /// \param[in] An IP address to check, excluding the port + /// \return True if this is one of the IP addresses returned by GetLocalIP + virtual bool IsLocalIP( const char *ip )=0; + + /// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary + /// when connecting to servers with multiple IP addresses. + /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections + virtual void AllowConnectionResponseIPMigration( bool allow )=0; + + /// Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. + /// This will tell the remote system our external IP outside the LAN along with some user data. + /// \pre The sender and recipient must already be started via a successful call to Initialize + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] data Optional data to append to the packet. + /// \param[in] dataLength length of data in bytes. Use 0 if no data. + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return false if IsActive()==false or the host is unresolvable. True otherwise + virtual bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 )=0; + + /// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. + /// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived + /// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. + /// Defaults to 0 (never return this notification) + /// \param[in] interval How many messages to use as an interval + virtual void SetSplitMessageProgressInterval(int interval)=0; + + /// Returns what was passed to SetSplitMessageProgressInterval() + /// \return What was passed to SetSplitMessageProgressInterval(). Default to 0. + virtual int GetSplitMessageProgressInterval(void) const=0; + + /// Set how long to wait before giving up on sending an unreliable message + /// Useful if the network is clogged up. + /// Set to 0 or less to never timeout. Defaults to 0. + /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. + virtual void SetUnreliableTimeout(RakNet::TimeMS timeoutMS)=0; + + /// Send a message to host, with the IP socket option TTL set to 3 + /// This message will not reach the host, but will open the router. + /// Used for NAT-Punchthrough + virtual void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 )=0; + + // -------------------------------------------------------------------------------------------- Plugin Functions-------------------------------------------------------------------------------------------- + /// \brief Attaches a Plugin interface to an instance of the base class (RakPeer or PacketizedTCP) to run code automatically on message receipt in the Receive call. + /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. + /// \param[in] messageHandler Pointer to the plugin to attach. + virtual void AttachPlugin( PluginInterface2 *plugin )=0; + + /// \brief Detaches a Plugin interface from the instance of the base class (RakPeer or PacketizedTCP) it is attached to. + /// \details This method disables the plugin code from running automatically on base class's updates or message receipt. + /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. + /// \param[in] messageHandler Pointer to a plugin to detach. + virtual void DetachPlugin( PluginInterface2 *messageHandler )=0; + + // --------------------------------------------------------------------------------------------Miscellaneous Functions-------------------------------------------------------------------------------------------- + /// Put a message back at the end of the receive queue in case you don't want to deal with it immediately + /// \param[in] packet The packet you want to push back. + /// \param[in] pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) + virtual void PushBackPacket( Packet *packet, bool pushAtHead )=0; + + /// \internal + /// \brief For a given system identified by \a guid, change the SystemAddress to send to. + /// \param[in] guid The connection we are referring to + /// \param[in] systemAddress The new address to send to + virtual void ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress)=0; + + /// \returns a packet for you to write to if you want to create a Packet for some reason. + /// You can add it to the receive buffer with PushBackPacket + /// \param[in] dataSize How many bytes to allocate for the buffer + /// \return A packet you can write to + virtual Packet* AllocatePacket(unsigned dataSize)=0; + + /// Get the socket used with a particular active connection + /// The smart pointer reference counts the RakNetSocket object, so the socket will remain active as long as the smart pointer does, even if RakNet were to shutdown or close the connection. + /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. + /// \param[in] target Which system + /// \return A smart pointer object containing the socket information about the socket. Be sure to check IsNull() which is returned if the update thread is unresponsive, shutting down, or if this system is not connected + virtual RakNetSmartPtr GetSocket( const SystemAddress target )=0; + + /// Get all sockets in use + /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. + /// \param[out] sockets List of RakNetSocket structures in use. Sockets will not be closed until \a sockets goes out of scope + virtual void GetSockets( DataStructures::List > &sockets )=0; + virtual void ReleaseSockets( DataStructures::List > &sockets )=0; + + virtual void WriteOutOfBandHeader(RakNet::BitStream *bitStream)=0; + + /// If you need code to run in the same thread as RakNet's update thread, this function can be used for that + /// \param[in] _userUpdateThreadPtr C callback function + /// \param[in] _userUpdateThreadData Passed to C callback function + virtual void SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData)=0; + + // --------------------------------------------------------------------------------------------Network Simulator Functions-------------------------------------------------------------------------------------------- + /// Adds simulated ping and packet loss to the outgoing data flow. + /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and packetloss value on each. + /// You can exclude network simulator code with the _RELEASE #define to decrease code size + /// \deprecated Use http://www.jenkinssoftware.com/forum/index.php?topic=1671.0 instead. + /// \note Doesn't work past version 3.6201 + /// \param[in] packetloss Chance to lose a packet. Ranges from 0 to 1. + /// \param[in] minExtraPing The minimum time to delay sends. + /// \param[in] extraPingVariance The additional random time to delay sends. + virtual void ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance)=0; + + /// Limits how much outgoing bandwidth can be sent per-connection. + /// This limit does not apply to the sum of all connections! + /// Exceeding the limit queues up outgoing traffic + /// \param[in] maxBitsPerSecond Maximum bits per second to send. Use 0 for unlimited (default). Once set, it takes effect immedately and persists until called again. + virtual void SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond )=0; + + /// Returns if you previously called ApplyNetworkSimulator + /// \return If you previously called ApplyNetworkSimulator + virtual bool IsNetworkSimulatorActive( void )=0; + + // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- + + /// Returns a structure containing a large set of network statistics for the specified system. + /// You can map this data to a string using the C style StatisticsToString() function + /// \param[in] systemAddress: Which connected system to get statistics for + /// \param[in] rns If you supply this structure, it will be written to it. Otherwise it will use a static struct, which is not threadsafe + /// \return 0 on can't find the specified system. A pointer to a set of data otherwise. + /// \sa RakNetStatistics.h + virtual RakNetStatistics * GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 )=0; + virtual bool GetStatistics( const int index, RakNetStatistics *rns )=0; + + /// \Returns how many messages are waiting when you call Receive() + virtual unsigned int GetReceiveBufferSize(void)=0; + + // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- + + /// \internal + virtual bool SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 )=0; + + int errorState; +} +// #if defined(SN_TARGET_PSP2) +// __attribute__((aligned(8))) +// #endif +; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakSleep.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakSleep.cpp index 41bc904..cf43a4d 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakSleep.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakSleep.cpp @@ -1,53 +1,53 @@ - - -#if defined(_WIN32) -#include "WindowsIncludes.h" // Sleep - - - - - -#else -#include -#include -#include -#endif - -#include "RakSleep.h" - -void RakSleep(unsigned int ms) -{ -#ifdef _WIN32 - Sleep(ms); - - - - - -#else - //Single thread sleep code thanks to Furquan Shaikh, http://somethingswhichidintknow.blogspot.com/2009/09/sleep-in-pthread.html - //Modified slightly from the original - pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER; - pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER; - struct timespec timeToWait; - struct timeval now; - int rt; - - gettimeofday(&now,NULL); - - long seconds = ms/1000; - long nanoseconds = (ms - seconds * 1000) * 1000000; - timeToWait.tv_sec = now.tv_sec + seconds; - timeToWait.tv_nsec = now.tv_usec*1000 + nanoseconds; - - if (timeToWait.tv_nsec >= 1000000000) - { - timeToWait.tv_nsec -= 1000000000; - timeToWait.tv_sec++; - } - - pthread_mutex_lock(&fakeMutex); - rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait); - pthread_mutex_unlock(&fakeMutex); -#endif -} + + +#if defined(_WIN32) +#include "WindowsIncludes.h" // Sleep + + + + + +#else +#include +#include +#include +#endif + +#include "RakSleep.h" + +void RakSleep(unsigned int ms) +{ +#ifdef _WIN32 + Sleep(ms); + + + + + +#else + //Single thread sleep code thanks to Furquan Shaikh, http://somethingswhichidintknow.blogspot.com/2009/09/sleep-in-pthread.html + //Modified slightly from the original + pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER; + struct timespec timeToWait; + struct timeval now; + int rt; + + gettimeofday(&now,NULL); + + long seconds = ms/1000; + long nanoseconds = (ms - seconds * 1000) * 1000000; + timeToWait.tv_sec = now.tv_sec + seconds; + timeToWait.tv_nsec = now.tv_usec*1000 + nanoseconds; + + if (timeToWait.tv_nsec >= 1000000000) + { + timeToWait.tv_nsec -= 1000000000; + timeToWait.tv_sec++; + } + + pthread_mutex_lock(&fakeMutex); + rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait); + pthread_mutex_unlock(&fakeMutex); +#endif +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakSleep.h b/project/lib_projects/raknet/jni/RaknetSources/RakSleep.h index f1a4148..27936bf 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakSleep.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakSleep.h @@ -1,8 +1,8 @@ -#ifndef __RAK_SLEEP_H -#define __RAK_SLEEP_H - -#include "Export.h" - -void RAK_DLL_EXPORT RakSleep(unsigned int ms); - -#endif +#ifndef __RAK_SLEEP_H +#define __RAK_SLEEP_H + +#include "Export.h" + +void RAK_DLL_EXPORT RakSleep(unsigned int ms); + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakString.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakString.cpp index b51c955..9c54eca 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakString.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakString.cpp @@ -1,1422 +1,1422 @@ -#include "RakString.h" -#include "RakAssert.h" -#include "RakMemoryOverride.h" -#include "BitStream.h" -#include -#include -#include "LinuxStrings.h" -#include "StringCompressor.h" -#include "SimpleMutex.h" - -using namespace RakNet; - -//DataStructures::MemoryPool RakString::pool; -RakString::SharedString RakString::emptyString={0,0,0,(char*) "",(char*) ""}; -//RakString::SharedString *RakString::sharedStringFreeList=0; -//unsigned int RakString::sharedStringFreeListAllocationCount=0; -DataStructures::List RakString::freeList; - -class RakStringCleanup -{ -public: - ~RakStringCleanup() - { - RakNet::RakString::FreeMemoryNoMutex(); - } -}; - -static RakStringCleanup cleanup; - -SimpleMutex& GetPoolMutex(void) -{ - static SimpleMutex poolMutex; - return poolMutex; -} - -int RakNet::RakString::RakStringComp( RakString const &key, RakString const &data ) -{ - return key.StrCmp(data); -} - -RakString::RakString() -{ - sharedString=&emptyString; -} -RakString::RakString( RakString::SharedString *_sharedString ) -{ - sharedString=_sharedString; -} -RakString::RakString(char input) -{ - char str[2]; - str[0]=input; - str[1]=0; - Assign(str); -} -RakString::RakString(unsigned char input) -{ - char str[2]; - str[0]=(char) input; - str[1]=0; - Assign(str); -} -RakString::RakString(const unsigned char *format, ...){ - va_list ap; - va_start(ap, format); - Assign((const char*) format,ap); -} -RakString::RakString(const char *format, ...){ - va_list ap; - va_start(ap, format); - Assign(format,ap); -} -RakString::RakString( const RakString & rhs) -{ - if (rhs.sharedString==&emptyString) - { - sharedString=&emptyString; - return; - } - - rhs.sharedString->refCountMutex->Lock(); - if (rhs.sharedString->refCount==0) - { - sharedString=&emptyString; - } - else - { - rhs.sharedString->refCount++; - sharedString=rhs.sharedString; - } - rhs.sharedString->refCountMutex->Unlock(); -} -RakString::~RakString() -{ - Free(); -} -RakString& RakString::operator = ( const RakString& rhs ) -{ - Free(); - if (rhs.sharedString==&emptyString) - return *this; - - rhs.sharedString->refCountMutex->Lock(); - if (rhs.sharedString->refCount==0) - { - sharedString=&emptyString; - } - else - { - sharedString=rhs.sharedString; - sharedString->refCount++; - } - rhs.sharedString->refCountMutex->Unlock(); - return *this; -} -RakString& RakString::operator = ( const char *str ) -{ - Free(); - Assign(str); - return *this; -} -RakString& RakString::operator = ( char *str ) -{ - return operator = ((const char*)str); -} -RakString& RakString::operator = ( const unsigned char *str ) -{ - return operator = ((const char*)str); -} -RakString& RakString::operator = ( char unsigned *str ) -{ - return operator = ((const char*)str); -} -RakString& RakString::operator = ( const char c ) -{ - char buff[2]; - buff[0]=c; - buff[1]=0; - return operator = ((const char*)buff); -} -void RakString::Realloc(SharedString *sharedString, size_t bytes) -{ - if (bytes<=sharedString->bytesUsed) - return; - RakAssert(bytes>0); - size_t oldBytes = sharedString->bytesUsed; - size_t newBytes; - const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - newBytes = GetSizeToAllocate(bytes); - if (oldBytes <=(size_t) smallStringSize && newBytes > (size_t) smallStringSize) - { - sharedString->bigString=(char*) rakMalloc_Ex(newBytes, _FILE_AND_LINE_); - strcpy(sharedString->bigString, sharedString->smallString); - sharedString->c_str=sharedString->bigString; - } - else if (oldBytes > smallStringSize) - { - sharedString->bigString=(char*) rakRealloc_Ex(sharedString->bigString,newBytes, _FILE_AND_LINE_); - sharedString->c_str=sharedString->bigString; - } - sharedString->bytesUsed=newBytes; -} -RakString& RakString::operator +=( const RakString& rhs) -{ - if (rhs.IsEmpty()) - return *this; - - if (IsEmpty()) - { - return operator=(rhs); - } - else - { - Clone(); - size_t strLen=rhs.GetLength()+GetLength()+1; - Realloc(sharedString, strLen+GetLength()); - strcat(sharedString->c_str,rhs.C_String()); - } - return *this; -} -RakString& RakString::operator +=( const char *str ) -{ - if (str==0 || str[0]==0) - return *this; - - if (IsEmpty()) - { - Assign(str); - } - else - { - Clone(); - size_t strLen=strlen(str)+GetLength()+1; - Realloc(sharedString, strLen); - strcat(sharedString->c_str,str); - } - return *this; -} -RakString& RakString::operator +=( char *str ) -{ - return operator += ((const char*)str); -} -RakString& RakString::operator +=( const unsigned char *str ) -{ - return operator += ((const char*)str); -} -RakString& RakString::operator +=( unsigned char *str ) -{ - return operator += ((const char*)str); -} -RakString& RakString::operator +=( const char c ) -{ - char buff[2]; - buff[0]=c; - buff[1]=0; - return operator += ((const char*)buff); -} -unsigned char RakString::operator[] ( const unsigned int position ) const -{ - RakAssert(positionc_str[position]; -} -bool RakString::operator==(const RakString &rhs) const -{ - return strcmp(sharedString->c_str,rhs.sharedString->c_str)==0; -} -bool RakString::operator==(const char *str) const -{ - return strcmp(sharedString->c_str,str)==0; -} -bool RakString::operator==(char *str) const -{ - return strcmp(sharedString->c_str,str)==0; -} -bool RakString::operator < ( const RakString& right ) const -{ - return strcmp(sharedString->c_str,right.C_String()) < 0; -} -bool RakString::operator <= ( const RakString& right ) const -{ - return strcmp(sharedString->c_str,right.C_String()) <= 0; -} -bool RakString::operator > ( const RakString& right ) const -{ - return strcmp(sharedString->c_str,right.C_String()) > 0; -} -bool RakString::operator >= ( const RakString& right ) const -{ - return strcmp(sharedString->c_str,right.C_String()) >= 0; -} -bool RakString::operator!=(const RakString &rhs) const -{ - return strcmp(sharedString->c_str,rhs.sharedString->c_str)!=0; -} -bool RakString::operator!=(const char *str) const -{ - return strcmp(sharedString->c_str,str)!=0; -} -bool RakString::operator!=(char *str) const -{ - return strcmp(sharedString->c_str,str)!=0; -} -const RakNet::RakString operator+(const RakNet::RakString &lhs, const RakNet::RakString &rhs) -{ - if (lhs.IsEmpty() && rhs.IsEmpty()) - { - return RakString(&RakString::emptyString); - } - if (lhs.IsEmpty()) - { - rhs.sharedString->refCountMutex->Lock(); - if (rhs.sharedString->refCount==0) - { - rhs.sharedString->refCountMutex->Unlock(); - lhs.sharedString->refCountMutex->Lock(); - lhs.sharedString->refCount++; - lhs.sharedString->refCountMutex->Unlock(); - return RakString(lhs.sharedString); - } - else - { - rhs.sharedString->refCount++; - rhs.sharedString->refCountMutex->Unlock(); - return RakString(rhs.sharedString); - } - // rhs.sharedString->refCountMutex->Unlock(); - } - if (rhs.IsEmpty()) - { - lhs.sharedString->refCountMutex->Lock(); - lhs.sharedString->refCount++; - lhs.sharedString->refCountMutex->Unlock(); - return RakString(lhs.sharedString); - } - - size_t len1 = lhs.GetLength(); - size_t len2 = rhs.GetLength(); - size_t allocatedBytes = len1 + len2 + 1; - allocatedBytes = RakString::GetSizeToAllocate(allocatedBytes); - RakString::SharedString *sharedString; - - RakString::LockMutex(); - // sharedString = RakString::pool.Allocate( _FILE_AND_LINE_ ); - if (RakString::freeList.Size()==0) - { - //RakString::sharedStringFreeList=(RakString::SharedString*) rakRealloc_Ex(RakString::sharedStringFreeList,(RakString::sharedStringFreeListAllocationCount+1024)*sizeof(RakString::SharedString), _FILE_AND_LINE_); - unsigned i; - for (i=0; i < 128; i++) - { - // RakString::freeList.Insert(RakString::sharedStringFreeList+i+RakString::sharedStringFreeListAllocationCount); - RakString::SharedString *ss; - ss = (RakString::SharedString*) rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_); - ss->refCountMutex=RakNet::OP_NEW(_FILE_AND_LINE_); - RakString::freeList.Insert(ss, _FILE_AND_LINE_); - - } - //RakString::sharedStringFreeListAllocationCount+=1024; - } - sharedString = RakString::freeList[RakString::freeList.Size()-1]; - RakString::freeList.RemoveAtIndex(RakString::freeList.Size()-1); - RakString::UnlockMutex(); - - const int smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - sharedString->bytesUsed=allocatedBytes; - sharedString->refCount=1; - if (allocatedBytes <= (size_t) smallStringSize) - { - sharedString->c_str=sharedString->smallString; - } - else - { - sharedString->bigString=(char*)rakMalloc_Ex(sharedString->bytesUsed, _FILE_AND_LINE_); - sharedString->c_str=sharedString->bigString; - } - - strcpy(sharedString->c_str, lhs); - strcat(sharedString->c_str, rhs); - - return RakString(sharedString); -} -const char * RakString::ToLower(void) -{ - Clone(); - - size_t strLen = strlen(sharedString->c_str); - unsigned i; - for (i=0; i < strLen; i++) - sharedString->c_str[i]=ToLower(sharedString->c_str[i]); - return sharedString->c_str; -} -const char * RakString::ToUpper(void) -{ - Clone(); - - size_t strLen = strlen(sharedString->c_str); - unsigned i; - for (i=0; i < strLen; i++) - sharedString->c_str[i]=ToUpper(sharedString->c_str[i]); - return sharedString->c_str; -} -void RakString::Set(const char *format, ...) -{ - va_list ap; - va_start(ap, format); - Clear(); - Assign(format,ap); -} -bool RakString::IsEmpty(void) const -{ - return sharedString==&emptyString; -} -size_t RakString::GetLength(void) const -{ - return strlen(sharedString->c_str); -} -// http://porg.es/blog/counting-characters-in-utf-8-strings-is-faster -int porges_strlen2(char *s) -{ - int i = 0; - int iBefore = 0; - int count = 0; - - while (s[i] > 0) -ascii: i++; - - count += i-iBefore; - while (s[i]) - { - if (s[i] > 0) - { - iBefore = i; - goto ascii; - } - else - switch (0xF0 & s[i]) - { - case 0xE0: i += 3; break; - case 0xF0: i += 4; break; - default: i += 2; break; - } - ++count; - } - return count; -} -size_t RakString::GetLengthUTF8(void) const -{ - return porges_strlen2(sharedString->c_str); -} -void RakString::Replace(unsigned index, unsigned count, unsigned char c) -{ - RakAssert(index+count < GetLength()); - Clone(); - unsigned countIndex=0; - while (countIndexc_str[index]=c; - index++; - countIndex++; - } -} -void RakString::SetChar( unsigned index, unsigned char c ) -{ - RakAssert(index < GetLength()); - Clone(); - sharedString->c_str[index]=c; -} -void RakString::SetChar( unsigned index, RakNet::RakString s ) -{ - RakAssert(index < GetLength()); - Clone(); - RakNet::RakString firstHalf = SubStr(0, index); - RakNet::RakString secondHalf = SubStr(index+1, (unsigned int)-1); - *this = firstHalf; - *this += s; - *this += secondHalf; -} - -#ifdef _WIN32 -WCHAR * RakString::ToWideChar(void) -{ - // - // Special case of NULL or empty input string - // - if ( (sharedString->c_str == NULL) || (*sharedString->c_str == '\0') ) - { - // Return empty string - return L""; - } - - // - // Get size of destination UTF-16 buffer, in WCHAR's - // - int cchUTF16 = ::MultiByteToWideChar( - CP_UTF8, // convert from UTF-8 - 0, // Flags - sharedString->c_str, // source UTF-8 string - GetLength()+1, // total length of source UTF-8 string, - // in CHAR's (= bytes), including end-of-string \0 - NULL, // unused - no conversion done in this step - 0 // request size of destination buffer, in WCHAR's - ); - - if ( cchUTF16 == 0 ) - { - RakAssert("RakString::ToWideChar exception from cchUTF16==0" && 0); - return 0; - } - - // - // Allocate destination buffer to store UTF-16 string - // - WCHAR * pszUTF16 = RakNet::OP_NEW_ARRAY(cchUTF16,__FILE__,__LINE__); - - // - // Do the conversion from UTF-8 to UTF-16 - // - int result = ::MultiByteToWideChar( - CP_UTF8, // convert from UTF-8 - 0, // Buffer - sharedString->c_str, // source UTF-8 string - GetLength()+1, // total length of source UTF-8 string, - // in CHAR's (= bytes), including end-of-string \0 - pszUTF16, // destination buffer - cchUTF16 // size of destination buffer, in WCHAR's - ); - - if ( result == 0 ) - { - RakAssert("RakString::ToWideChar exception from MultiByteToWideChar" && 0); - return 0; - } - - return pszUTF16; -} -void RakString::DeallocWideChar(WCHAR * w) -{ - RakNet::OP_DELETE_ARRAY(w,__FILE__,__LINE__); -} -#endif - -size_t RakString::Find(const char *stringToFind,size_t pos) -{ - size_t len=GetLength(); - if (pos>=len || stringToFind==0 || stringToFind[0]==0) - { - return (size_t) -1; - } - size_t matchLen= strlen(stringToFind); - size_t matchPos=0; - size_t iStart=0; - - for (size_t i=pos;ic_str[i]) - { - if(matchPos==0) - { - iStart=i; - } - matchPos++; - } - else - { - matchPos=0; - } - - if (matchPos>=matchLen) - { - return iStart; - } - } - - return (size_t) -1; -} - -void RakString::TruncateUTF8(unsigned int length) -{ - int i = 0; - unsigned int count = 0; - - while (sharedString->c_str[i]!=0) - { - if (count==length) - { - sharedString->c_str[i]=0; - return; - } - else if (sharedString->c_str[i]>0) - { - i++; - } - else - { - switch (0xF0 & sharedString->c_str[i]) - { - case 0xE0: i += 3; break; - case 0xF0: i += 4; break; - default: i += 2; break; - } - } - - count++; - } -} - -void RakString::Truncate(unsigned int length) -{ - if (length < GetLength()) - { - SetChar(length, 0); - } -} - -RakString RakString::SubStr(unsigned int index, unsigned int count) const -{ - size_t length = GetLength(); - if (index >= length || count==0) - return RakString(); - RakString copy; - size_t numBytes = length-index; - if (count < numBytes) - numBytes=count; - copy.Allocate(numBytes+1); - size_t i; - for (i=0; i < numBytes; i++) - copy.sharedString->c_str[i]=sharedString->c_str[index+i]; - copy.sharedString->c_str[i]=0; - return copy; -} -void RakString::Erase(unsigned int index, unsigned int count) -{ - size_t len = GetLength(); - RakAssert(index+count <= len); - - Clone(); - unsigned i; - for (i=index; i < len-count; i++) - { - sharedString->c_str[i]=sharedString->c_str[i+count]; - } - sharedString->c_str[i]=0; -} -void RakString::TerminateAtLastCharacter(char c) -{ - int i, len=(int) GetLength(); - for (i=len-1; i >= 0; i--) - { - if (sharedString->c_str[i]==c) - { - Clone(); - sharedString->c_str[i]=0; - return; - } - } -} -void RakString::TerminateAtFirstCharacter(char c) -{ - unsigned int i, len=(unsigned int) GetLength(); - for (i=0; i < len; i++) - { - if (sharedString->c_str[i]==c) - { - Clone(); - sharedString->c_str[i]=0; - return; - } - } -} -void RakString::RemoveCharacter(char c) -{ - if (c==0) - return; - - unsigned int readIndex, writeIndex=0; - for (readIndex=0; sharedString->c_str[readIndex]; readIndex++) - { - if (sharedString->c_str[readIndex]!=c) - sharedString->c_str[writeIndex++]=sharedString->c_str[readIndex]; - else - Clone(); - } - sharedString->c_str[writeIndex]=0; -} -int RakString::StrCmp(const RakString &rhs) const -{ - return strcmp(sharedString->c_str, rhs); -} -int RakString::StrICmp(const RakString &rhs) const -{ - return _stricmp(sharedString->c_str, rhs); -} -void RakString::Printf(void) -{ - RAKNET_DEBUG_PRINTF("%s", sharedString->c_str); -} -void RakString::FPrintf(FILE *fp) -{ - fprintf(fp,"%s", sharedString->c_str); -} -bool RakString::IPAddressMatch(const char *IP) -{ - unsigned characterIndex; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return false; - - characterIndex = 0; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( true ) - { - if (sharedString->c_str[ characterIndex ] == IP[ characterIndex ] ) - { - // Equal characters - if ( IP[ characterIndex ] == 0 ) - { - // End of the string and the strings match - - return true; - } - - characterIndex++; - } - - else - { - if ( sharedString->c_str[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) - { - // End of one of the strings - break; - } - - // Characters do not match - if ( sharedString->c_str[ characterIndex ] == '*' ) - { - // Domain is banned. - return true; - } - - // Characters do not match and it is not a * - break; - } - } - - - // No match found. - return false; -} -bool RakString::ContainsNonprintableExceptSpaces(void) const -{ - size_t strLen = strlen(sharedString->c_str); - unsigned i; - for (i=0; i < strLen; i++) - { - if (sharedString->c_str[i] < ' ' || sharedString->c_str[i] >126) - return true; - } - return false; -} -bool RakString::IsEmailAddress(void) const -{ - if (IsEmpty()) - return false; - size_t strLen = strlen(sharedString->c_str); - if (strLen < 6) // a@b.de - return false; - if (sharedString->c_str[strLen-4]!='.' && sharedString->c_str[strLen-3]!='.') // .com, .net., .org, .de - return false; - unsigned i; - // Has non-printable? - for (i=0; i < strLen; i++) - { - if (sharedString->c_str[i] <= ' ' || sharedString->c_str[i] >126) - return false; - } - int atCount=0; - for (i=0; i < strLen; i++) - { - if (sharedString->c_str[i]=='@') - { - atCount++; - } - } - if (atCount!=1) - return false; - int dotCount=0; - for (i=0; i < strLen; i++) - { - if (sharedString->c_str[i]=='.') - { - dotCount++; - } - } - if (dotCount==0) - return false; - - // There's more I could check, but this is good enough - return true; -} -RakNet::RakString& RakString::URLEncode(void) -{ - RakString result; - size_t strLen = strlen(sharedString->c_str); - result.Allocate(strLen*3); - char *output=result.sharedString->c_str; - unsigned int outputIndex=0; - unsigned i; - char c; - for (i=0; i < strLen; i++) - { - c=sharedString->c_str[i]; - if ( - (c<=47) || - (c>=58 && c<=64) || - (c>=91 && c<=96) || - (c>=123) - ) - { - RakNet::RakString tmp("%2X", c); - output[outputIndex++]='%'; - output[outputIndex++]=tmp.sharedString->c_str[0]; - output[outputIndex++]=tmp.sharedString->c_str[1]; - } - else - { - output[outputIndex++]=c; - } - } - - output[outputIndex]=0; - - *this = result; - return *this; -} -RakNet::RakString& RakString::URLDecode(void) -{ - RakString result; - size_t strLen = strlen(sharedString->c_str); - result.Allocate(strLen); - char *output=result.sharedString->c_str; - unsigned int outputIndex=0; - char c; - char hexDigits[2]; - char hexValues[2]; - unsigned int i; - for (i=0; i < strLen; i++) - { - c=sharedString->c_str[i]; - if (c=='%') - { - hexDigits[0]=sharedString->c_str[++i]; - hexDigits[1]=sharedString->c_str[++i]; - if (hexDigits[0]==' ') - hexValues[0]=0; - else if (hexDigits[0]>='A') - hexValues[0]=hexDigits[0]-'A'+10; - else - hexValues[0]=hexDigits[0]-'0'; - if (hexDigits[1]>='A') - hexValues[1]=hexDigits[1]-'A'+10; - else - hexValues[1]=hexDigits[1]-'0'; - output[outputIndex++]=hexValues[0]*16+hexValues[1]; - } - else - { - output[outputIndex++]=c; - } - } - - output[outputIndex]=0; - - *this = result; - return *this; -} -void RakString::SplitURI(RakNet::RakString &header, RakNet::RakString &domain, RakNet::RakString &path) -{ - header.Clear(); - domain.Clear(); - path.Clear(); - - size_t strLen = strlen(sharedString->c_str); - - char c; - unsigned int i=0; - if (strncmp(sharedString->c_str, "http://", 7)==0) - i+=(unsigned int) strlen("http://"); - else if (strncmp(sharedString->c_str, "https://", 8)==0) - i+=(unsigned int) strlen("https://"); - - if (strncmp(sharedString->c_str, "www.", 4)==0) - i+=(unsigned int) strlen("www."); - - if (i!=0) - { - header.Allocate(i+1); - strncpy(header.sharedString->c_str, sharedString->c_str, i); - header.sharedString->c_str[i]=0; - } - - - domain.Allocate(strLen-i+1); - char *domainOutput=domain.sharedString->c_str; - unsigned int outputIndex=0; - for (; i < strLen; i++) - { - c=sharedString->c_str[i]; - if (c=='/') - { - break; - } - else - { - domainOutput[outputIndex++]=sharedString->c_str[i]; - } - } - - domainOutput[outputIndex]=0; - - path.Allocate(strLen-header.GetLength()-outputIndex+1); - outputIndex=0; - char *pathOutput=path.sharedString->c_str; - for (; i < strLen; i++) - { - pathOutput[outputIndex++]=sharedString->c_str[i]; - } - pathOutput[outputIndex]=0; -} -RakNet::RakString& RakString::SQLEscape(void) -{ - int strLen=(int)GetLength(); - int escapedCharacterCount=0; - int index; - for (index=0; index < strLen; index++) - { - if (sharedString->c_str[index]=='\'' || - sharedString->c_str[index]=='"' || - sharedString->c_str[index]=='\\') - escapedCharacterCount++; - } - if (escapedCharacterCount==0) - return *this; - - Clone(); - Realloc(sharedString, strLen+escapedCharacterCount); - int writeIndex, readIndex; - writeIndex = strLen+escapedCharacterCount; - readIndex=strLen; - while (readIndex>=0) - { - if (sharedString->c_str[readIndex]=='\'' || - sharedString->c_str[readIndex]=='"' || - sharedString->c_str[readIndex]=='\\') - { - sharedString->c_str[writeIndex--]=sharedString->c_str[readIndex--]; - sharedString->c_str[writeIndex--]='\\'; - } - else - { - sharedString->c_str[writeIndex--]=sharedString->c_str[readIndex--]; - } - } - return *this; -} -RakNet::RakString& RakString::MakeFilePath(void) -{ - if (IsEmpty()) - return *this; - - RakNet::RakString fixedString = *this; - fixedString.Clone(); - for (int i=0; fixedString.sharedString->c_str[i]; i++) - { -#ifdef _WIN32 - if (fixedString.sharedString->c_str[i]=='/') - fixedString.sharedString->c_str[i]='\\'; -#else - if (fixedString.sharedString->c_str[i]=='\\') - fixedString.sharedString->c_str[i]='/'; -#endif - } - -#ifdef _WIN32 - if (fixedString.sharedString->c_str[strlen(fixedString.sharedString->c_str)-1]!='\\') - { - fixedString+='\\'; - } -#else - if (fixedString.sharedString->c_str[strlen(fixedString.sharedString->c_str)-1]!='/') - { - fixedString+='/'; - } -#endif - - if (fixedString!=*this) - *this = fixedString; - return *this; -} -void RakString::FreeMemory(void) -{ - LockMutex(); - FreeMemoryNoMutex(); - UnlockMutex(); -} -void RakString::FreeMemoryNoMutex(void) -{ - for (unsigned int i=0; i < freeList.Size(); i++) - { - RakNet::OP_DELETE(freeList[i]->refCountMutex,_FILE_AND_LINE_); - rakFree_Ex(freeList[i], _FILE_AND_LINE_ ); - } - freeList.Clear(false, _FILE_AND_LINE_); -} -void RakString::Serialize(BitStream *bs) const -{ - Serialize(sharedString->c_str, bs); -} -void RakString::Serialize(const char *str, BitStream *bs) -{ - unsigned short l = (unsigned short) strlen(str); - bs->Write(l); - bs->WriteAlignedBytes((const unsigned char*) str, (const unsigned int) l); -} -void RakString::SerializeCompressed(BitStream *bs, uint8_t languageId, bool writeLanguageId) const -{ - SerializeCompressed(C_String(), bs, languageId, writeLanguageId); -} -void RakString::SerializeCompressed(const char *str, BitStream *bs, uint8_t languageId, bool writeLanguageId) -{ - if (writeLanguageId) - bs->WriteCompressed(languageId); - StringCompressor::Instance()->EncodeString(str,0xFFFF,bs,languageId); -} -bool RakString::Deserialize(BitStream *bs) -{ - Clear(); - - bool b; - unsigned short l; - b=bs->Read(l); - if (l>0) - { - Allocate(((unsigned int) l)+1); - b=bs->ReadAlignedBytes((unsigned char*) sharedString->c_str, l); - if (b) - sharedString->c_str[l]=0; - else - Clear(); - } - else - bs->AlignReadToByteBoundary(); - return b; -} -bool RakString::Deserialize(char *str, BitStream *bs) -{ - bool b; - unsigned short l; - b=bs->Read(l); - if (b && l>0) - b=bs->ReadAlignedBytes((unsigned char*) str, l); - - if (b==false) - str[0]=0; - - str[l]=0; - return b; -} -bool RakString::DeserializeCompressed(BitStream *bs, bool readLanguageId) -{ - uint8_t languageId; - if (readLanguageId) - bs->ReadCompressed(languageId); - else - languageId=0; - return StringCompressor::Instance()->DecodeString(this,0xFFFF,bs,languageId); -} -bool RakString::DeserializeCompressed(char *str, BitStream *bs, bool readLanguageId) -{ - uint8_t languageId; - if (readLanguageId) - bs->ReadCompressed(languageId); - else - languageId=0; - return StringCompressor::Instance()->DecodeString(str,0xFFFF,bs,languageId); -} -const char *RakString::ToString(int64_t i) -{ - static int index=0; - static char buff[64][64]; -#if defined(_WIN32) - sprintf(buff[index], "%I64d", i); -#else - sprintf(buff[index], "%lld", (long long unsigned int) i); -#endif - int lastIndex=index; - if (++index==64) - index=0; - return buff[lastIndex]; -} -const char *RakString::ToString(uint64_t i) -{ - static int index=0; - static char buff[64][64]; -#if defined(_WIN32) - sprintf(buff[index], "%I64u", i); -#else - sprintf(buff[index], "%llu", (long long unsigned int) i); -#endif - int lastIndex=index; - if (++index==64) - index=0; - return buff[lastIndex]; -} -void RakString::Clear(void) -{ - Free(); -} -void RakString::Allocate(size_t len) -{ - RakString::LockMutex(); - // sharedString = RakString::pool.Allocate( _FILE_AND_LINE_ ); - if (RakString::freeList.Size()==0) - { - //RakString::sharedStringFreeList=(RakString::SharedString*) rakRealloc_Ex(RakString::sharedStringFreeList,(RakString::sharedStringFreeListAllocationCount+1024)*sizeof(RakString::SharedString), _FILE_AND_LINE_); - unsigned i; - for (i=0; i < 128; i++) - { - // RakString::freeList.Insert(RakString::sharedStringFreeList+i+RakString::sharedStringFreeListAllocationCount); - // RakString::freeList.Insert((RakString::SharedString*)rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_), _FILE_AND_LINE_); - - RakString::SharedString *ss; - ss = (RakString::SharedString*) rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_); - ss->refCountMutex=RakNet::OP_NEW(_FILE_AND_LINE_); - RakString::freeList.Insert(ss, _FILE_AND_LINE_); - } - //RakString::sharedStringFreeListAllocationCount+=1024; - } - sharedString = RakString::freeList[RakString::freeList.Size()-1]; - RakString::freeList.RemoveAtIndex(RakString::freeList.Size()-1); - RakString::UnlockMutex(); - - const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - sharedString->refCount=1; - if (len <= smallStringSize) - { - sharedString->bytesUsed=smallStringSize; - sharedString->c_str=sharedString->smallString; - } - else - { - sharedString->bytesUsed=len<<1; - sharedString->bigString=(char*)rakMalloc_Ex(sharedString->bytesUsed, _FILE_AND_LINE_); - sharedString->c_str=sharedString->bigString; - } -} -void RakString::Assign(const char *str) -{ - if (str==0 || str[0]==0) - { - sharedString=&emptyString; - return; - } - - size_t len = strlen(str)+1; - Allocate(len); - memcpy(sharedString->c_str, str, len); -} -void RakString::Assign(const char *str, va_list ap) -{ - char stackBuff[512]; - if (_vsnprintf(stackBuff, 512, str, ap)!=-1 -#ifndef _WIN32 - // Here Windows will return -1 if the string is too long; Linux just truncates the string. - && strlen(str) <511 -#endif - ) - { - Assign(stackBuff); - return; - } - char *buff=0, *newBuff; - size_t buffSize=8096; - while (1) - { - newBuff = (char*) rakRealloc_Ex(buff, buffSize,__FILE__,__LINE__); - if (newBuff==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - if (buff!=0) - { - Assign(buff); - rakFree_Ex(buff,__FILE__,__LINE__); - } - else - { - Assign(stackBuff); - } - return; - } - buff=newBuff; - if (_vsnprintf(buff, buffSize, str, ap)!=-1) - { - Assign(buff); - rakFree_Ex(buff,__FILE__,__LINE__); - return; - } - buffSize*=2; - } -} -RakNet::RakString RakString::Assign(const char *str,size_t pos, size_t n ) -{ - size_t incomingLen=strlen(str); - - Clone(); - - if (str==0 || str[0]==0||pos>=incomingLen) - { - sharedString=&emptyString; - return (*this); - } - - if (pos+n>=incomingLen) - { - n=incomingLen-pos; - - } - const char * tmpStr=&(str[pos]); - - size_t len = n+1; - Allocate(len); - memcpy(sharedString->c_str, tmpStr, len); - sharedString->c_str[n]=0; - - return (*this); -} - -RakNet::RakString RakString::NonVariadic(const char *str) -{ - RakNet::RakString rs; - rs=str; - return rs; -} -unsigned long RakString::ToInteger(const char *str) -{ - unsigned long hash = 0; - int c; - - while ((c = *str++)) - hash = c + (hash << 6) + (hash << 16) - hash; - - return hash; -} -unsigned long RakString::ToInteger(const RakString &rs) -{ - return RakString::ToInteger(rs.C_String()); -} -void RakString::AppendBytes(const char *bytes, unsigned int count) -{ - Clone(); - Realloc(sharedString, count); - unsigned int length=(unsigned int) GetLength(); - memcpy(sharedString->c_str+length, bytes, count); - sharedString->c_str[length+count]=0; -} -void RakString::Clone(void) -{ - if (sharedString==&emptyString) - { - return; - } - - // Empty or solo then no point to cloning - sharedString->refCountMutex->Lock(); - if (sharedString->refCount==1) - { - sharedString->refCountMutex->Unlock(); - return; - } - - sharedString->refCount--; - sharedString->refCountMutex->Unlock(); - Assign(sharedString->c_str); -} -void RakString::Free(void) -{ - if (sharedString==&emptyString) - return; - sharedString->refCountMutex->Lock(); - sharedString->refCount--; - if (sharedString->refCount==0) - { - sharedString->refCountMutex->Unlock(); - const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - if (sharedString->bytesUsed>smallStringSize) - rakFree_Ex(sharedString->bigString, _FILE_AND_LINE_ ); - /* - poolMutex->Lock(); - pool.Release(sharedString); - poolMutex->Unlock(); - */ - - RakString::LockMutex(); - RakString::freeList.Insert(sharedString, _FILE_AND_LINE_); - RakString::UnlockMutex(); - - sharedString=&emptyString; - } - else - { - sharedString->refCountMutex->Unlock(); - } - sharedString=&emptyString; -} -unsigned char RakString::ToLower(unsigned char c) -{ - if (c >= 'A' && c <= 'Z') - return c-'A'+'a'; - return c; -} -unsigned char RakString::ToUpper(unsigned char c) -{ - if (c >= 'a' && c <= 'z') - return c-'a'+'A'; - return c; -} -void RakString::LockMutex(void) -{ - GetPoolMutex().Lock(); -} -void RakString::UnlockMutex(void) -{ - GetPoolMutex().Unlock(); -} - -/* -#include "RakString.h" -#include -#include "GetTime.h" - -using namespace RakNet; - -int main(void) -{ - RakString s3("Hello world"); - RakString s5=s3; - - RakString s1; - RakString s2('a'); - - RakString s4("%i %f", 5, 6.0); - - RakString s6=s3; - RakString s7=s6; - RakString s8=s6; - RakString s9; - s9=s9; - RakString s10(s3); - RakString s11=s10 + s4 + s9 + s2; - s11+=RakString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - RakString s12("Test"); - s12+=s11; - bool b1 = s12==s12; - s11=s5; - s12.ToUpper(); - s12.ToLower(); - RakString s13; - bool b3 = s13.IsEmpty(); - s13.Set("blah %s", s12.C_String()); - bool b4 = s13.IsEmpty(); - size_t i1=s13.GetLength(); - s3.Clear(_FILE_AND_LINE_); - s4.Clear(_FILE_AND_LINE_); - s5.Clear(_FILE_AND_LINE_); - s5.Clear(_FILE_AND_LINE_); - s6.Printf(); - s7.Printf(); - RAKNET_DEBUG_PRINTF("\n"); - - static const int repeatCount=750; - DataStructures::List rakStringList; - DataStructures::List stdStringList; - DataStructures::List referenceStringList; - char *c; - unsigned i; - RakNet::TimeMS beforeReferenceList, beforeRakString, beforeStdString, afterStdString; - - unsigned loop; - for (loop=0; loop<2; loop++) - { - beforeReferenceList=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - { - c = RakNet::OP_NEW_ARRAY(56,_FILE_AND_LINE_ ); - strcpy(c, "Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - referenceStringList.Insert(c); - } - beforeRakString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - rakStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - beforeStdString=RakNet::GetTimeMS(); - - for (i=0; i < repeatCount; i++) - stdStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - afterStdString=RakNet::GetTimeMS(); - RAKNET_DEBUG_PRINTF("Insertion 1 Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); - - beforeReferenceList=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - { - RakNet::OP_DELETE_ARRAY(referenceStringList[0], _FILE_AND_LINE_); - referenceStringList.RemoveAtIndex(0); - } - beforeRakString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - rakStringList.RemoveAtIndex(0); - beforeStdString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - stdStringList.RemoveAtIndex(0); - afterStdString=RakNet::GetTimeMS(); - RAKNET_DEBUG_PRINTF("RemoveHead Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); - - beforeReferenceList=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - { - c = RakNet::OP_NEW_ARRAY(56, _FILE_AND_LINE_ ); - strcpy(c, "Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - referenceStringList.Insert(0); - } - beforeRakString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - rakStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - beforeStdString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - stdStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - afterStdString=RakNet::GetTimeMS(); - RAKNET_DEBUG_PRINTF("Insertion 2 Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); - - beforeReferenceList=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - { - RakNet::OP_DELETE_ARRAY(referenceStringList[referenceStringList.Size()-1], _FILE_AND_LINE_); - referenceStringList.RemoveAtIndex(referenceStringList.Size()-1); - } - beforeRakString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - rakStringList.RemoveAtIndex(rakStringList.Size()-1); - beforeStdString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - stdStringList.RemoveAtIndex(stdStringList.Size()-1); - afterStdString=RakNet::GetTimeMS(); - RAKNET_DEBUG_PRINTF("RemoveTail Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); - - } - - printf("Done."); - char str[128]; - Gets(str, sizeof(str)); - return 1; -} -*/ +#include "RakString.h" +#include "RakAssert.h" +#include "RakMemoryOverride.h" +#include "BitStream.h" +#include +#include +#include "LinuxStrings.h" +#include "StringCompressor.h" +#include "SimpleMutex.h" + +using namespace RakNet; + +//DataStructures::MemoryPool RakString::pool; +RakString::SharedString RakString::emptyString={0,0,0,(char*) "",(char*) ""}; +//RakString::SharedString *RakString::sharedStringFreeList=0; +//unsigned int RakString::sharedStringFreeListAllocationCount=0; +DataStructures::List RakString::freeList; + +class RakStringCleanup +{ +public: + ~RakStringCleanup() + { + RakNet::RakString::FreeMemoryNoMutex(); + } +}; + +static RakStringCleanup cleanup; + +SimpleMutex& GetPoolMutex(void) +{ + static SimpleMutex poolMutex; + return poolMutex; +} + +int RakNet::RakString::RakStringComp( RakString const &key, RakString const &data ) +{ + return key.StrCmp(data); +} + +RakString::RakString() +{ + sharedString=&emptyString; +} +RakString::RakString( RakString::SharedString *_sharedString ) +{ + sharedString=_sharedString; +} +RakString::RakString(char input) +{ + char str[2]; + str[0]=input; + str[1]=0; + Assign(str); +} +RakString::RakString(unsigned char input) +{ + char str[2]; + str[0]=(char) input; + str[1]=0; + Assign(str); +} +RakString::RakString(const unsigned char *format, ...){ + va_list ap; + va_start(ap, format); + Assign((const char*) format,ap); +} +RakString::RakString(const char *format, ...){ + va_list ap; + va_start(ap, format); + Assign(format,ap); +} +RakString::RakString( const RakString & rhs) +{ + if (rhs.sharedString==&emptyString) + { + sharedString=&emptyString; + return; + } + + rhs.sharedString->refCountMutex->Lock(); + if (rhs.sharedString->refCount==0) + { + sharedString=&emptyString; + } + else + { + rhs.sharedString->refCount++; + sharedString=rhs.sharedString; + } + rhs.sharedString->refCountMutex->Unlock(); +} +RakString::~RakString() +{ + Free(); +} +RakString& RakString::operator = ( const RakString& rhs ) +{ + Free(); + if (rhs.sharedString==&emptyString) + return *this; + + rhs.sharedString->refCountMutex->Lock(); + if (rhs.sharedString->refCount==0) + { + sharedString=&emptyString; + } + else + { + sharedString=rhs.sharedString; + sharedString->refCount++; + } + rhs.sharedString->refCountMutex->Unlock(); + return *this; +} +RakString& RakString::operator = ( const char *str ) +{ + Free(); + Assign(str); + return *this; +} +RakString& RakString::operator = ( char *str ) +{ + return operator = ((const char*)str); +} +RakString& RakString::operator = ( const unsigned char *str ) +{ + return operator = ((const char*)str); +} +RakString& RakString::operator = ( char unsigned *str ) +{ + return operator = ((const char*)str); +} +RakString& RakString::operator = ( const char c ) +{ + char buff[2]; + buff[0]=c; + buff[1]=0; + return operator = ((const char*)buff); +} +void RakString::Realloc(SharedString *sharedString, size_t bytes) +{ + if (bytes<=sharedString->bytesUsed) + return; + RakAssert(bytes>0); + size_t oldBytes = sharedString->bytesUsed; + size_t newBytes; + const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + newBytes = GetSizeToAllocate(bytes); + if (oldBytes <=(size_t) smallStringSize && newBytes > (size_t) smallStringSize) + { + sharedString->bigString=(char*) rakMalloc_Ex(newBytes, _FILE_AND_LINE_); + strcpy(sharedString->bigString, sharedString->smallString); + sharedString->c_str=sharedString->bigString; + } + else if (oldBytes > smallStringSize) + { + sharedString->bigString=(char*) rakRealloc_Ex(sharedString->bigString,newBytes, _FILE_AND_LINE_); + sharedString->c_str=sharedString->bigString; + } + sharedString->bytesUsed=newBytes; +} +RakString& RakString::operator +=( const RakString& rhs) +{ + if (rhs.IsEmpty()) + return *this; + + if (IsEmpty()) + { + return operator=(rhs); + } + else + { + Clone(); + size_t strLen=rhs.GetLength()+GetLength()+1; + Realloc(sharedString, strLen+GetLength()); + strcat(sharedString->c_str,rhs.C_String()); + } + return *this; +} +RakString& RakString::operator +=( const char *str ) +{ + if (str==0 || str[0]==0) + return *this; + + if (IsEmpty()) + { + Assign(str); + } + else + { + Clone(); + size_t strLen=strlen(str)+GetLength()+1; + Realloc(sharedString, strLen); + strcat(sharedString->c_str,str); + } + return *this; +} +RakString& RakString::operator +=( char *str ) +{ + return operator += ((const char*)str); +} +RakString& RakString::operator +=( const unsigned char *str ) +{ + return operator += ((const char*)str); +} +RakString& RakString::operator +=( unsigned char *str ) +{ + return operator += ((const char*)str); +} +RakString& RakString::operator +=( const char c ) +{ + char buff[2]; + buff[0]=c; + buff[1]=0; + return operator += ((const char*)buff); +} +unsigned char RakString::operator[] ( const unsigned int position ) const +{ + RakAssert(positionc_str[position]; +} +bool RakString::operator==(const RakString &rhs) const +{ + return strcmp(sharedString->c_str,rhs.sharedString->c_str)==0; +} +bool RakString::operator==(const char *str) const +{ + return strcmp(sharedString->c_str,str)==0; +} +bool RakString::operator==(char *str) const +{ + return strcmp(sharedString->c_str,str)==0; +} +bool RakString::operator < ( const RakString& right ) const +{ + return strcmp(sharedString->c_str,right.C_String()) < 0; +} +bool RakString::operator <= ( const RakString& right ) const +{ + return strcmp(sharedString->c_str,right.C_String()) <= 0; +} +bool RakString::operator > ( const RakString& right ) const +{ + return strcmp(sharedString->c_str,right.C_String()) > 0; +} +bool RakString::operator >= ( const RakString& right ) const +{ + return strcmp(sharedString->c_str,right.C_String()) >= 0; +} +bool RakString::operator!=(const RakString &rhs) const +{ + return strcmp(sharedString->c_str,rhs.sharedString->c_str)!=0; +} +bool RakString::operator!=(const char *str) const +{ + return strcmp(sharedString->c_str,str)!=0; +} +bool RakString::operator!=(char *str) const +{ + return strcmp(sharedString->c_str,str)!=0; +} +const RakNet::RakString operator+(const RakNet::RakString &lhs, const RakNet::RakString &rhs) +{ + if (lhs.IsEmpty() && rhs.IsEmpty()) + { + return RakString(&RakString::emptyString); + } + if (lhs.IsEmpty()) + { + rhs.sharedString->refCountMutex->Lock(); + if (rhs.sharedString->refCount==0) + { + rhs.sharedString->refCountMutex->Unlock(); + lhs.sharedString->refCountMutex->Lock(); + lhs.sharedString->refCount++; + lhs.sharedString->refCountMutex->Unlock(); + return RakString(lhs.sharedString); + } + else + { + rhs.sharedString->refCount++; + rhs.sharedString->refCountMutex->Unlock(); + return RakString(rhs.sharedString); + } + // rhs.sharedString->refCountMutex->Unlock(); + } + if (rhs.IsEmpty()) + { + lhs.sharedString->refCountMutex->Lock(); + lhs.sharedString->refCount++; + lhs.sharedString->refCountMutex->Unlock(); + return RakString(lhs.sharedString); + } + + size_t len1 = lhs.GetLength(); + size_t len2 = rhs.GetLength(); + size_t allocatedBytes = len1 + len2 + 1; + allocatedBytes = RakString::GetSizeToAllocate(allocatedBytes); + RakString::SharedString *sharedString; + + RakString::LockMutex(); + // sharedString = RakString::pool.Allocate( _FILE_AND_LINE_ ); + if (RakString::freeList.Size()==0) + { + //RakString::sharedStringFreeList=(RakString::SharedString*) rakRealloc_Ex(RakString::sharedStringFreeList,(RakString::sharedStringFreeListAllocationCount+1024)*sizeof(RakString::SharedString), _FILE_AND_LINE_); + unsigned i; + for (i=0; i < 128; i++) + { + // RakString::freeList.Insert(RakString::sharedStringFreeList+i+RakString::sharedStringFreeListAllocationCount); + RakString::SharedString *ss; + ss = (RakString::SharedString*) rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_); + ss->refCountMutex=RakNet::OP_NEW(_FILE_AND_LINE_); + RakString::freeList.Insert(ss, _FILE_AND_LINE_); + + } + //RakString::sharedStringFreeListAllocationCount+=1024; + } + sharedString = RakString::freeList[RakString::freeList.Size()-1]; + RakString::freeList.RemoveAtIndex(RakString::freeList.Size()-1); + RakString::UnlockMutex(); + + const int smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + sharedString->bytesUsed=allocatedBytes; + sharedString->refCount=1; + if (allocatedBytes <= (size_t) smallStringSize) + { + sharedString->c_str=sharedString->smallString; + } + else + { + sharedString->bigString=(char*)rakMalloc_Ex(sharedString->bytesUsed, _FILE_AND_LINE_); + sharedString->c_str=sharedString->bigString; + } + + strcpy(sharedString->c_str, lhs); + strcat(sharedString->c_str, rhs); + + return RakString(sharedString); +} +const char * RakString::ToLower(void) +{ + Clone(); + + size_t strLen = strlen(sharedString->c_str); + unsigned i; + for (i=0; i < strLen; i++) + sharedString->c_str[i]=ToLower(sharedString->c_str[i]); + return sharedString->c_str; +} +const char * RakString::ToUpper(void) +{ + Clone(); + + size_t strLen = strlen(sharedString->c_str); + unsigned i; + for (i=0; i < strLen; i++) + sharedString->c_str[i]=ToUpper(sharedString->c_str[i]); + return sharedString->c_str; +} +void RakString::Set(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + Clear(); + Assign(format,ap); +} +bool RakString::IsEmpty(void) const +{ + return sharedString==&emptyString; +} +size_t RakString::GetLength(void) const +{ + return strlen(sharedString->c_str); +} +// http://porg.es/blog/counting-characters-in-utf-8-strings-is-faster +int porges_strlen2(char *s) +{ + int i = 0; + int iBefore = 0; + int count = 0; + + while (s[i] > 0) +ascii: i++; + + count += i-iBefore; + while (s[i]) + { + if (s[i] > 0) + { + iBefore = i; + goto ascii; + } + else + switch (0xF0 & s[i]) + { + case 0xE0: i += 3; break; + case 0xF0: i += 4; break; + default: i += 2; break; + } + ++count; + } + return count; +} +size_t RakString::GetLengthUTF8(void) const +{ + return porges_strlen2(sharedString->c_str); +} +void RakString::Replace(unsigned index, unsigned count, unsigned char c) +{ + RakAssert(index+count < GetLength()); + Clone(); + unsigned countIndex=0; + while (countIndexc_str[index]=c; + index++; + countIndex++; + } +} +void RakString::SetChar( unsigned index, unsigned char c ) +{ + RakAssert(index < GetLength()); + Clone(); + sharedString->c_str[index]=c; +} +void RakString::SetChar( unsigned index, RakNet::RakString s ) +{ + RakAssert(index < GetLength()); + Clone(); + RakNet::RakString firstHalf = SubStr(0, index); + RakNet::RakString secondHalf = SubStr(index+1, (unsigned int)-1); + *this = firstHalf; + *this += s; + *this += secondHalf; +} + +#ifdef _WIN32 +WCHAR * RakString::ToWideChar(void) +{ + // + // Special case of NULL or empty input string + // + if ( (sharedString->c_str == NULL) || (*sharedString->c_str == '\0') ) + { + // Return empty string + return L""; + } + + // + // Get size of destination UTF-16 buffer, in WCHAR's + // + int cchUTF16 = ::MultiByteToWideChar( + CP_UTF8, // convert from UTF-8 + 0, // Flags + sharedString->c_str, // source UTF-8 string + GetLength()+1, // total length of source UTF-8 string, + // in CHAR's (= bytes), including end-of-string \0 + NULL, // unused - no conversion done in this step + 0 // request size of destination buffer, in WCHAR's + ); + + if ( cchUTF16 == 0 ) + { + RakAssert("RakString::ToWideChar exception from cchUTF16==0" && 0); + return 0; + } + + // + // Allocate destination buffer to store UTF-16 string + // + WCHAR * pszUTF16 = RakNet::OP_NEW_ARRAY(cchUTF16,__FILE__,__LINE__); + + // + // Do the conversion from UTF-8 to UTF-16 + // + int result = ::MultiByteToWideChar( + CP_UTF8, // convert from UTF-8 + 0, // Buffer + sharedString->c_str, // source UTF-8 string + GetLength()+1, // total length of source UTF-8 string, + // in CHAR's (= bytes), including end-of-string \0 + pszUTF16, // destination buffer + cchUTF16 // size of destination buffer, in WCHAR's + ); + + if ( result == 0 ) + { + RakAssert("RakString::ToWideChar exception from MultiByteToWideChar" && 0); + return 0; + } + + return pszUTF16; +} +void RakString::DeallocWideChar(WCHAR * w) +{ + RakNet::OP_DELETE_ARRAY(w,__FILE__,__LINE__); +} +#endif + +size_t RakString::Find(const char *stringToFind,size_t pos) +{ + size_t len=GetLength(); + if (pos>=len || stringToFind==0 || stringToFind[0]==0) + { + return (size_t) -1; + } + size_t matchLen= strlen(stringToFind); + size_t matchPos=0; + size_t iStart=0; + + for (size_t i=pos;ic_str[i]) + { + if(matchPos==0) + { + iStart=i; + } + matchPos++; + } + else + { + matchPos=0; + } + + if (matchPos>=matchLen) + { + return iStart; + } + } + + return (size_t) -1; +} + +void RakString::TruncateUTF8(unsigned int length) +{ + int i = 0; + unsigned int count = 0; + + while (sharedString->c_str[i]!=0) + { + if (count==length) + { + sharedString->c_str[i]=0; + return; + } + else if (sharedString->c_str[i]>0) + { + i++; + } + else + { + switch (0xF0 & sharedString->c_str[i]) + { + case 0xE0: i += 3; break; + case 0xF0: i += 4; break; + default: i += 2; break; + } + } + + count++; + } +} + +void RakString::Truncate(unsigned int length) +{ + if (length < GetLength()) + { + SetChar(length, 0); + } +} + +RakString RakString::SubStr(unsigned int index, unsigned int count) const +{ + size_t length = GetLength(); + if (index >= length || count==0) + return RakString(); + RakString copy; + size_t numBytes = length-index; + if (count < numBytes) + numBytes=count; + copy.Allocate(numBytes+1); + size_t i; + for (i=0; i < numBytes; i++) + copy.sharedString->c_str[i]=sharedString->c_str[index+i]; + copy.sharedString->c_str[i]=0; + return copy; +} +void RakString::Erase(unsigned int index, unsigned int count) +{ + size_t len = GetLength(); + RakAssert(index+count <= len); + + Clone(); + unsigned i; + for (i=index; i < len-count; i++) + { + sharedString->c_str[i]=sharedString->c_str[i+count]; + } + sharedString->c_str[i]=0; +} +void RakString::TerminateAtLastCharacter(char c) +{ + int i, len=(int) GetLength(); + for (i=len-1; i >= 0; i--) + { + if (sharedString->c_str[i]==c) + { + Clone(); + sharedString->c_str[i]=0; + return; + } + } +} +void RakString::TerminateAtFirstCharacter(char c) +{ + unsigned int i, len=(unsigned int) GetLength(); + for (i=0; i < len; i++) + { + if (sharedString->c_str[i]==c) + { + Clone(); + sharedString->c_str[i]=0; + return; + } + } +} +void RakString::RemoveCharacter(char c) +{ + if (c==0) + return; + + unsigned int readIndex, writeIndex=0; + for (readIndex=0; sharedString->c_str[readIndex]; readIndex++) + { + if (sharedString->c_str[readIndex]!=c) + sharedString->c_str[writeIndex++]=sharedString->c_str[readIndex]; + else + Clone(); + } + sharedString->c_str[writeIndex]=0; +} +int RakString::StrCmp(const RakString &rhs) const +{ + return strcmp(sharedString->c_str, rhs); +} +int RakString::StrICmp(const RakString &rhs) const +{ + return _stricmp(sharedString->c_str, rhs); +} +void RakString::Printf(void) +{ + RAKNET_DEBUG_PRINTF("%s", sharedString->c_str); +} +void RakString::FPrintf(FILE *fp) +{ + fprintf(fp,"%s", sharedString->c_str); +} +bool RakString::IPAddressMatch(const char *IP) +{ + unsigned characterIndex; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return false; + + characterIndex = 0; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) + { + if (sharedString->c_str[ characterIndex ] == IP[ characterIndex ] ) + { + // Equal characters + if ( IP[ characterIndex ] == 0 ) + { + // End of the string and the strings match + + return true; + } + + characterIndex++; + } + + else + { + if ( sharedString->c_str[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) + { + // End of one of the strings + break; + } + + // Characters do not match + if ( sharedString->c_str[ characterIndex ] == '*' ) + { + // Domain is banned. + return true; + } + + // Characters do not match and it is not a * + break; + } + } + + + // No match found. + return false; +} +bool RakString::ContainsNonprintableExceptSpaces(void) const +{ + size_t strLen = strlen(sharedString->c_str); + unsigned i; + for (i=0; i < strLen; i++) + { + if (sharedString->c_str[i] < ' ' || sharedString->c_str[i] >126) + return true; + } + return false; +} +bool RakString::IsEmailAddress(void) const +{ + if (IsEmpty()) + return false; + size_t strLen = strlen(sharedString->c_str); + if (strLen < 6) // a@b.de + return false; + if (sharedString->c_str[strLen-4]!='.' && sharedString->c_str[strLen-3]!='.') // .com, .net., .org, .de + return false; + unsigned i; + // Has non-printable? + for (i=0; i < strLen; i++) + { + if (sharedString->c_str[i] <= ' ' || sharedString->c_str[i] >126) + return false; + } + int atCount=0; + for (i=0; i < strLen; i++) + { + if (sharedString->c_str[i]=='@') + { + atCount++; + } + } + if (atCount!=1) + return false; + int dotCount=0; + for (i=0; i < strLen; i++) + { + if (sharedString->c_str[i]=='.') + { + dotCount++; + } + } + if (dotCount==0) + return false; + + // There's more I could check, but this is good enough + return true; +} +RakNet::RakString& RakString::URLEncode(void) +{ + RakString result; + size_t strLen = strlen(sharedString->c_str); + result.Allocate(strLen*3); + char *output=result.sharedString->c_str; + unsigned int outputIndex=0; + unsigned i; + char c; + for (i=0; i < strLen; i++) + { + c=sharedString->c_str[i]; + if ( + (c<=47) || + (c>=58 && c<=64) || + (c>=91 && c<=96) || + (c>=123) + ) + { + RakNet::RakString tmp("%2X", c); + output[outputIndex++]='%'; + output[outputIndex++]=tmp.sharedString->c_str[0]; + output[outputIndex++]=tmp.sharedString->c_str[1]; + } + else + { + output[outputIndex++]=c; + } + } + + output[outputIndex]=0; + + *this = result; + return *this; +} +RakNet::RakString& RakString::URLDecode(void) +{ + RakString result; + size_t strLen = strlen(sharedString->c_str); + result.Allocate(strLen); + char *output=result.sharedString->c_str; + unsigned int outputIndex=0; + char c; + char hexDigits[2]; + char hexValues[2]; + unsigned int i; + for (i=0; i < strLen; i++) + { + c=sharedString->c_str[i]; + if (c=='%') + { + hexDigits[0]=sharedString->c_str[++i]; + hexDigits[1]=sharedString->c_str[++i]; + if (hexDigits[0]==' ') + hexValues[0]=0; + else if (hexDigits[0]>='A') + hexValues[0]=hexDigits[0]-'A'+10; + else + hexValues[0]=hexDigits[0]-'0'; + if (hexDigits[1]>='A') + hexValues[1]=hexDigits[1]-'A'+10; + else + hexValues[1]=hexDigits[1]-'0'; + output[outputIndex++]=hexValues[0]*16+hexValues[1]; + } + else + { + output[outputIndex++]=c; + } + } + + output[outputIndex]=0; + + *this = result; + return *this; +} +void RakString::SplitURI(RakNet::RakString &header, RakNet::RakString &domain, RakNet::RakString &path) +{ + header.Clear(); + domain.Clear(); + path.Clear(); + + size_t strLen = strlen(sharedString->c_str); + + char c; + unsigned int i=0; + if (strncmp(sharedString->c_str, "http://", 7)==0) + i+=(unsigned int) strlen("http://"); + else if (strncmp(sharedString->c_str, "https://", 8)==0) + i+=(unsigned int) strlen("https://"); + + if (strncmp(sharedString->c_str, "www.", 4)==0) + i+=(unsigned int) strlen("www."); + + if (i!=0) + { + header.Allocate(i+1); + strncpy(header.sharedString->c_str, sharedString->c_str, i); + header.sharedString->c_str[i]=0; + } + + + domain.Allocate(strLen-i+1); + char *domainOutput=domain.sharedString->c_str; + unsigned int outputIndex=0; + for (; i < strLen; i++) + { + c=sharedString->c_str[i]; + if (c=='/') + { + break; + } + else + { + domainOutput[outputIndex++]=sharedString->c_str[i]; + } + } + + domainOutput[outputIndex]=0; + + path.Allocate(strLen-header.GetLength()-outputIndex+1); + outputIndex=0; + char *pathOutput=path.sharedString->c_str; + for (; i < strLen; i++) + { + pathOutput[outputIndex++]=sharedString->c_str[i]; + } + pathOutput[outputIndex]=0; +} +RakNet::RakString& RakString::SQLEscape(void) +{ + int strLen=(int)GetLength(); + int escapedCharacterCount=0; + int index; + for (index=0; index < strLen; index++) + { + if (sharedString->c_str[index]=='\'' || + sharedString->c_str[index]=='"' || + sharedString->c_str[index]=='\\') + escapedCharacterCount++; + } + if (escapedCharacterCount==0) + return *this; + + Clone(); + Realloc(sharedString, strLen+escapedCharacterCount); + int writeIndex, readIndex; + writeIndex = strLen+escapedCharacterCount; + readIndex=strLen; + while (readIndex>=0) + { + if (sharedString->c_str[readIndex]=='\'' || + sharedString->c_str[readIndex]=='"' || + sharedString->c_str[readIndex]=='\\') + { + sharedString->c_str[writeIndex--]=sharedString->c_str[readIndex--]; + sharedString->c_str[writeIndex--]='\\'; + } + else + { + sharedString->c_str[writeIndex--]=sharedString->c_str[readIndex--]; + } + } + return *this; +} +RakNet::RakString& RakString::MakeFilePath(void) +{ + if (IsEmpty()) + return *this; + + RakNet::RakString fixedString = *this; + fixedString.Clone(); + for (int i=0; fixedString.sharedString->c_str[i]; i++) + { +#ifdef _WIN32 + if (fixedString.sharedString->c_str[i]=='/') + fixedString.sharedString->c_str[i]='\\'; +#else + if (fixedString.sharedString->c_str[i]=='\\') + fixedString.sharedString->c_str[i]='/'; +#endif + } + +#ifdef _WIN32 + if (fixedString.sharedString->c_str[strlen(fixedString.sharedString->c_str)-1]!='\\') + { + fixedString+='\\'; + } +#else + if (fixedString.sharedString->c_str[strlen(fixedString.sharedString->c_str)-1]!='/') + { + fixedString+='/'; + } +#endif + + if (fixedString!=*this) + *this = fixedString; + return *this; +} +void RakString::FreeMemory(void) +{ + LockMutex(); + FreeMemoryNoMutex(); + UnlockMutex(); +} +void RakString::FreeMemoryNoMutex(void) +{ + for (unsigned int i=0; i < freeList.Size(); i++) + { + RakNet::OP_DELETE(freeList[i]->refCountMutex,_FILE_AND_LINE_); + rakFree_Ex(freeList[i], _FILE_AND_LINE_ ); + } + freeList.Clear(false, _FILE_AND_LINE_); +} +void RakString::Serialize(BitStream *bs) const +{ + Serialize(sharedString->c_str, bs); +} +void RakString::Serialize(const char *str, BitStream *bs) +{ + unsigned short l = (unsigned short) strlen(str); + bs->Write(l); + bs->WriteAlignedBytes((const unsigned char*) str, (const unsigned int) l); +} +void RakString::SerializeCompressed(BitStream *bs, uint8_t languageId, bool writeLanguageId) const +{ + SerializeCompressed(C_String(), bs, languageId, writeLanguageId); +} +void RakString::SerializeCompressed(const char *str, BitStream *bs, uint8_t languageId, bool writeLanguageId) +{ + if (writeLanguageId) + bs->WriteCompressed(languageId); + StringCompressor::Instance()->EncodeString(str,0xFFFF,bs,languageId); +} +bool RakString::Deserialize(BitStream *bs) +{ + Clear(); + + bool b; + unsigned short l; + b=bs->Read(l); + if (l>0) + { + Allocate(((unsigned int) l)+1); + b=bs->ReadAlignedBytes((unsigned char*) sharedString->c_str, l); + if (b) + sharedString->c_str[l]=0; + else + Clear(); + } + else + bs->AlignReadToByteBoundary(); + return b; +} +bool RakString::Deserialize(char *str, BitStream *bs) +{ + bool b; + unsigned short l; + b=bs->Read(l); + if (b && l>0) + b=bs->ReadAlignedBytes((unsigned char*) str, l); + + if (b==false) + str[0]=0; + + str[l]=0; + return b; +} +bool RakString::DeserializeCompressed(BitStream *bs, bool readLanguageId) +{ + uint8_t languageId; + if (readLanguageId) + bs->ReadCompressed(languageId); + else + languageId=0; + return StringCompressor::Instance()->DecodeString(this,0xFFFF,bs,languageId); +} +bool RakString::DeserializeCompressed(char *str, BitStream *bs, bool readLanguageId) +{ + uint8_t languageId; + if (readLanguageId) + bs->ReadCompressed(languageId); + else + languageId=0; + return StringCompressor::Instance()->DecodeString(str,0xFFFF,bs,languageId); +} +const char *RakString::ToString(int64_t i) +{ + static int index=0; + static char buff[64][64]; +#if defined(_WIN32) + sprintf(buff[index], "%I64d", i); +#else + sprintf(buff[index], "%lld", (long long unsigned int) i); +#endif + int lastIndex=index; + if (++index==64) + index=0; + return buff[lastIndex]; +} +const char *RakString::ToString(uint64_t i) +{ + static int index=0; + static char buff[64][64]; +#if defined(_WIN32) + sprintf(buff[index], "%I64u", i); +#else + sprintf(buff[index], "%llu", (long long unsigned int) i); +#endif + int lastIndex=index; + if (++index==64) + index=0; + return buff[lastIndex]; +} +void RakString::Clear(void) +{ + Free(); +} +void RakString::Allocate(size_t len) +{ + RakString::LockMutex(); + // sharedString = RakString::pool.Allocate( _FILE_AND_LINE_ ); + if (RakString::freeList.Size()==0) + { + //RakString::sharedStringFreeList=(RakString::SharedString*) rakRealloc_Ex(RakString::sharedStringFreeList,(RakString::sharedStringFreeListAllocationCount+1024)*sizeof(RakString::SharedString), _FILE_AND_LINE_); + unsigned i; + for (i=0; i < 128; i++) + { + // RakString::freeList.Insert(RakString::sharedStringFreeList+i+RakString::sharedStringFreeListAllocationCount); + // RakString::freeList.Insert((RakString::SharedString*)rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_), _FILE_AND_LINE_); + + RakString::SharedString *ss; + ss = (RakString::SharedString*) rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_); + ss->refCountMutex=RakNet::OP_NEW(_FILE_AND_LINE_); + RakString::freeList.Insert(ss, _FILE_AND_LINE_); + } + //RakString::sharedStringFreeListAllocationCount+=1024; + } + sharedString = RakString::freeList[RakString::freeList.Size()-1]; + RakString::freeList.RemoveAtIndex(RakString::freeList.Size()-1); + RakString::UnlockMutex(); + + const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + sharedString->refCount=1; + if (len <= smallStringSize) + { + sharedString->bytesUsed=smallStringSize; + sharedString->c_str=sharedString->smallString; + } + else + { + sharedString->bytesUsed=len<<1; + sharedString->bigString=(char*)rakMalloc_Ex(sharedString->bytesUsed, _FILE_AND_LINE_); + sharedString->c_str=sharedString->bigString; + } +} +void RakString::Assign(const char *str) +{ + if (str==0 || str[0]==0) + { + sharedString=&emptyString; + return; + } + + size_t len = strlen(str)+1; + Allocate(len); + memcpy(sharedString->c_str, str, len); +} +void RakString::Assign(const char *str, va_list ap) +{ + char stackBuff[512]; + if (_vsnprintf(stackBuff, 512, str, ap)!=-1 +#ifndef _WIN32 + // Here Windows will return -1 if the string is too long; Linux just truncates the string. + && strlen(str) <511 +#endif + ) + { + Assign(stackBuff); + return; + } + char *buff=0, *newBuff; + size_t buffSize=8096; + while (1) + { + newBuff = (char*) rakRealloc_Ex(buff, buffSize,__FILE__,__LINE__); + if (newBuff==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + if (buff!=0) + { + Assign(buff); + rakFree_Ex(buff,__FILE__,__LINE__); + } + else + { + Assign(stackBuff); + } + return; + } + buff=newBuff; + if (_vsnprintf(buff, buffSize, str, ap)!=-1) + { + Assign(buff); + rakFree_Ex(buff,__FILE__,__LINE__); + return; + } + buffSize*=2; + } +} +RakNet::RakString RakString::Assign(const char *str,size_t pos, size_t n ) +{ + size_t incomingLen=strlen(str); + + Clone(); + + if (str==0 || str[0]==0||pos>=incomingLen) + { + sharedString=&emptyString; + return (*this); + } + + if (pos+n>=incomingLen) + { + n=incomingLen-pos; + + } + const char * tmpStr=&(str[pos]); + + size_t len = n+1; + Allocate(len); + memcpy(sharedString->c_str, tmpStr, len); + sharedString->c_str[n]=0; + + return (*this); +} + +RakNet::RakString RakString::NonVariadic(const char *str) +{ + RakNet::RakString rs; + rs=str; + return rs; +} +unsigned long RakString::ToInteger(const char *str) +{ + unsigned long hash = 0; + int c; + + while ((c = *str++)) + hash = c + (hash << 6) + (hash << 16) - hash; + + return hash; +} +unsigned long RakString::ToInteger(const RakString &rs) +{ + return RakString::ToInteger(rs.C_String()); +} +void RakString::AppendBytes(const char *bytes, unsigned int count) +{ + Clone(); + Realloc(sharedString, count); + unsigned int length=(unsigned int) GetLength(); + memcpy(sharedString->c_str+length, bytes, count); + sharedString->c_str[length+count]=0; +} +void RakString::Clone(void) +{ + if (sharedString==&emptyString) + { + return; + } + + // Empty or solo then no point to cloning + sharedString->refCountMutex->Lock(); + if (sharedString->refCount==1) + { + sharedString->refCountMutex->Unlock(); + return; + } + + sharedString->refCount--; + sharedString->refCountMutex->Unlock(); + Assign(sharedString->c_str); +} +void RakString::Free(void) +{ + if (sharedString==&emptyString) + return; + sharedString->refCountMutex->Lock(); + sharedString->refCount--; + if (sharedString->refCount==0) + { + sharedString->refCountMutex->Unlock(); + const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + if (sharedString->bytesUsed>smallStringSize) + rakFree_Ex(sharedString->bigString, _FILE_AND_LINE_ ); + /* + poolMutex->Lock(); + pool.Release(sharedString); + poolMutex->Unlock(); + */ + + RakString::LockMutex(); + RakString::freeList.Insert(sharedString, _FILE_AND_LINE_); + RakString::UnlockMutex(); + + sharedString=&emptyString; + } + else + { + sharedString->refCountMutex->Unlock(); + } + sharedString=&emptyString; +} +unsigned char RakString::ToLower(unsigned char c) +{ + if (c >= 'A' && c <= 'Z') + return c-'A'+'a'; + return c; +} +unsigned char RakString::ToUpper(unsigned char c) +{ + if (c >= 'a' && c <= 'z') + return c-'a'+'A'; + return c; +} +void RakString::LockMutex(void) +{ + GetPoolMutex().Lock(); +} +void RakString::UnlockMutex(void) +{ + GetPoolMutex().Unlock(); +} + +/* +#include "RakString.h" +#include +#include "GetTime.h" + +using namespace RakNet; + +int main(void) +{ + RakString s3("Hello world"); + RakString s5=s3; + + RakString s1; + RakString s2('a'); + + RakString s4("%i %f", 5, 6.0); + + RakString s6=s3; + RakString s7=s6; + RakString s8=s6; + RakString s9; + s9=s9; + RakString s10(s3); + RakString s11=s10 + s4 + s9 + s2; + s11+=RakString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + RakString s12("Test"); + s12+=s11; + bool b1 = s12==s12; + s11=s5; + s12.ToUpper(); + s12.ToLower(); + RakString s13; + bool b3 = s13.IsEmpty(); + s13.Set("blah %s", s12.C_String()); + bool b4 = s13.IsEmpty(); + size_t i1=s13.GetLength(); + s3.Clear(_FILE_AND_LINE_); + s4.Clear(_FILE_AND_LINE_); + s5.Clear(_FILE_AND_LINE_); + s5.Clear(_FILE_AND_LINE_); + s6.Printf(); + s7.Printf(); + RAKNET_DEBUG_PRINTF("\n"); + + static const int repeatCount=750; + DataStructures::List rakStringList; + DataStructures::List stdStringList; + DataStructures::List referenceStringList; + char *c; + unsigned i; + RakNet::TimeMS beforeReferenceList, beforeRakString, beforeStdString, afterStdString; + + unsigned loop; + for (loop=0; loop<2; loop++) + { + beforeReferenceList=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + { + c = RakNet::OP_NEW_ARRAY(56,_FILE_AND_LINE_ ); + strcpy(c, "Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + referenceStringList.Insert(c); + } + beforeRakString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + rakStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + beforeStdString=RakNet::GetTimeMS(); + + for (i=0; i < repeatCount; i++) + stdStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + afterStdString=RakNet::GetTimeMS(); + RAKNET_DEBUG_PRINTF("Insertion 1 Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); + + beforeReferenceList=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + { + RakNet::OP_DELETE_ARRAY(referenceStringList[0], _FILE_AND_LINE_); + referenceStringList.RemoveAtIndex(0); + } + beforeRakString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + rakStringList.RemoveAtIndex(0); + beforeStdString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + stdStringList.RemoveAtIndex(0); + afterStdString=RakNet::GetTimeMS(); + RAKNET_DEBUG_PRINTF("RemoveHead Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); + + beforeReferenceList=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + { + c = RakNet::OP_NEW_ARRAY(56, _FILE_AND_LINE_ ); + strcpy(c, "Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + referenceStringList.Insert(0); + } + beforeRakString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + rakStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + beforeStdString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + stdStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + afterStdString=RakNet::GetTimeMS(); + RAKNET_DEBUG_PRINTF("Insertion 2 Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); + + beforeReferenceList=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + { + RakNet::OP_DELETE_ARRAY(referenceStringList[referenceStringList.Size()-1], _FILE_AND_LINE_); + referenceStringList.RemoveAtIndex(referenceStringList.Size()-1); + } + beforeRakString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + rakStringList.RemoveAtIndex(rakStringList.Size()-1); + beforeStdString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + stdStringList.RemoveAtIndex(stdStringList.Size()-1); + afterStdString=RakNet::GetTimeMS(); + RAKNET_DEBUG_PRINTF("RemoveTail Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); + + } + + printf("Done."); + char str[128]; + Gets(str, sizeof(str)); + return 1; +} +*/ diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakString.h b/project/lib_projects/raknet/jni/RaknetSources/RakString.h index 125586e..49ac882 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakString.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakString.h @@ -1,307 +1,307 @@ -#ifndef __RAK_STRING_H -#define __RAK_STRING_H - -#include "Export.h" -#include "DS_List.h" -#include "RakNetTypes.h" // int64_t -#include -#include "stdarg.h" - - -#ifdef _WIN32 - - - -#include "WindowsIncludes.h" -#endif - -namespace RakNet -{ -/// Forward declarations -class SimpleMutex; -class BitStream; - -/// \brief String class -/// \details Has the following improvements over std::string -/// -Reference counting: Suitable to store in lists -/// -Variadic assignment operator -/// -Doesn't cause linker errors -class RAK_DLL_EXPORT RakString -{ -public: - // Constructors - RakString(); - RakString(char input); - RakString(unsigned char input); - RakString(const unsigned char *format, ...); - RakString(const char *format, ...); - ~RakString(); - RakString( const RakString & rhs); - - /// Implicit return of const char* - operator const char* () const {return sharedString->c_str;} - - /// Same as std::string::c_str - const char *C_String(void) const {return sharedString->c_str;} - - // Lets you modify the string. Do not make the string longer - however, you can make it shorter, or change the contents. - // Pointer is only valid in the scope of RakString itself - char *C_StringUnsafe(void) {Clone(); return sharedString->c_str;} - - /// Assigment operators - RakString& operator = ( const RakString& rhs ); - RakString& operator = ( const char *str ); - RakString& operator = ( char *str ); - RakString& operator = ( const unsigned char *str ); - RakString& operator = ( char unsigned *str ); - RakString& operator = ( const char c ); - - /// Concatenation - RakString& operator +=( const RakString& rhs); - RakString& operator += ( const char *str ); - RakString& operator += ( char *str ); - RakString& operator += ( const unsigned char *str ); - RakString& operator += ( char unsigned *str ); - RakString& operator += ( const char c ); - - /// Character index. Do not use to change the string however. - unsigned char operator[] ( const unsigned int position ) const; - -#ifdef _WIN32 - // Return as Wide char - // Deallocate with DeallocWideChar - WCHAR * ToWideChar(void); - void DeallocWideChar(WCHAR * w); -#endif - - ///String class find replacement - ///Searches the string for the content specified in stringToFind and returns the position of the first occurrence in the string. - ///Search only includes characters on or after position pos, ignoring any possible occurrences in previous locations. - /// \param[in] stringToFind The string to find inside of this object's string - /// \param[in] pos The position in the string to start the search - /// \return Returns the position of the first occurrence in the string. - size_t Find(const char *stringToFind,size_t pos = 0 ); - - /// Equality - bool operator==(const RakString &rhs) const; - bool operator==(const char *str) const; - bool operator==(char *str) const; - - // Comparison - bool operator < ( const RakString& right ) const; - bool operator <= ( const RakString& right ) const; - bool operator > ( const RakString& right ) const; - bool operator >= ( const RakString& right ) const; - - /// Inequality - bool operator!=(const RakString &rhs) const; - bool operator!=(const char *str) const; - bool operator!=(char *str) const; - - /// Change all characters to lowercase - const char * ToLower(void); - - /// Change all characters to uppercase - const char * ToUpper(void); - - /// Set the value of the string - void Set(const char *format, ...); - - /// Sets a copy of a substring of str as the new content. The substring is the portion of str - /// that begins at the character position pos and takes up to n characters - /// (it takes less than n if the end of str is reached before). - /// \param[in] str The string to copy in - /// \param[in] pos The position on str to start the copy - /// \param[in] n How many chars to copy - /// \return Returns the string, note that the current string is set to that value as well - RakString Assign(const char *str,size_t pos, size_t n ); - - /// Returns if the string is empty. Also, C_String() would return "" - bool IsEmpty(void) const; - - /// Returns the length of the string - size_t GetLength(void) const; - size_t GetLengthUTF8(void) const; - - /// Replace character(s) in starting at index, for count, with c - void Replace(unsigned index, unsigned count, unsigned char c); - - /// Replace character at index with c - void SetChar( unsigned index, unsigned char c ); - - /// Replace character at index with string s - void SetChar( unsigned index, RakNet::RakString s ); - - /// Make sure string is no longer than \a length - void Truncate(unsigned int length); - void TruncateUTF8(unsigned int length); - - // Gets the substring starting at index for count characters - RakString SubStr(unsigned int index, unsigned int count) const; - - /// Erase characters out of the string at index for count - void Erase(unsigned int index, unsigned int count); - - /// Set the first instance of c with a NULL terminator - void TerminateAtFirstCharacter(char c); - /// Set the last instance of c with a NULL terminator - void TerminateAtLastCharacter(char c); - - /// Remove all instances of c - void RemoveCharacter(char c); - - /// Create a RakString with a value, without doing printf style parsing - /// Equivalent to assignment operator - static RakNet::RakString NonVariadic(const char *str); - - /// Has the string into an unsigned int - static unsigned long ToInteger(const char *str); - static unsigned long ToInteger(const RakString &rs); - - // Like strncat, but for a fixed length - void AppendBytes(const char *bytes, unsigned int count); - - /// Compare strings (case sensitive) - int StrCmp(const RakString &rhs) const; - - /// Compare strings (not case sensitive) - int StrICmp(const RakString &rhs) const; - - /// Clear the string - void Clear(void); - - /// Print the string to the screen - void Printf(void); - - /// Print the string to a file - void FPrintf(FILE *fp); - - /// Does the given IP address match the IP address encoded into this string, accounting for wildcards? - bool IPAddressMatch(const char *IP); - - /// Does the string contain non-printable characters other than spaces? - bool ContainsNonprintableExceptSpaces(void) const; - - /// Is this a valid email address? - bool IsEmailAddress(void) const; - - /// URL Encode the string. See http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4029/ - RakNet::RakString& URLEncode(void); - - /// URL decode the string - RakNet::RakString& URLDecode(void); - - /// https://servers.api.rackspacecloud.com/v1.0 to https://, servers.api.rackspacecloud.com, /v1.0 - void SplitURI(RakNet::RakString &header, RakNet::RakString &domain, RakNet::RakString &path); - - /// Scan for quote, double quote, and backslash and prepend with backslash - RakNet::RakString& SQLEscape(void); - - /// Fix to be a file path, ending with / - RakNet::RakString& MakeFilePath(void); - - /// RakString uses a freeList of old no-longer used strings - /// Call this function to clear this memory on shutdown - static void FreeMemory(void); - /// \internal - static void FreeMemoryNoMutex(void); - - /// Serialize to a bitstream, uncompressed (slightly faster) - /// \param[out] bs Bitstream to serialize to - void Serialize(BitStream *bs) const; - - /// Static version of the Serialize function - static void Serialize(const char *str, BitStream *bs); - - /// Serialize to a bitstream, compressed (better bandwidth usage) - /// \param[out] bs Bitstream to serialize to - /// \param[in] languageId languageId to pass to the StringCompressor class - /// \param[in] writeLanguageId encode the languageId variable in the stream. If false, 0 is assumed, and DeserializeCompressed will not look for this variable in the stream (saves bandwidth) - /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) - void SerializeCompressed(BitStream *bs, uint8_t languageId=0, bool writeLanguageId=false) const; - - /// Static version of the SerializeCompressed function - static void SerializeCompressed(const char *str, BitStream *bs, uint8_t languageId=0, bool writeLanguageId=false); - - /// Deserialize what was written by Serialize - /// \param[in] bs Bitstream to serialize from - /// \return true if the deserialization was successful - bool Deserialize(BitStream *bs); - - /// Static version of the Deserialize() function - static bool Deserialize(char *str, BitStream *bs); - - /// Deserialize compressed string, written by SerializeCompressed - /// \param[in] bs Bitstream to serialize from - /// \param[in] readLanguageId If true, looks for the variable langaugeId in the data stream. Must match what was passed to SerializeCompressed - /// \return true if the deserialization was successful - /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) - bool DeserializeCompressed(BitStream *bs, bool readLanguageId=false); - - /// Static version of the DeserializeCompressed() function - static bool DeserializeCompressed(char *str, BitStream *bs, bool readLanguageId=false); - - static const char *ToString(int64_t i); - static const char *ToString(uint64_t i); - - /// \internal - static size_t GetSizeToAllocate(size_t bytes) - { - const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - if (bytes<=smallStringSize) - return smallStringSize; - else - return bytes*2; - } - - /// \internal - struct SharedString - { - SimpleMutex *refCountMutex; - unsigned int refCount; - size_t bytesUsed; - char *bigString; - char *c_str; - char smallString[128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2]; - }; - - /// \internal - RakString( SharedString *_sharedString ); - - /// \internal - SharedString *sharedString; - -// static SimpleMutex poolMutex; -// static DataStructures::MemoryPool pool; - /// \internal - static SharedString emptyString; - - //static SharedString *sharedStringFreeList; - //static unsigned int sharedStringFreeListAllocationCount; - /// \internal - /// List of free objects to reduce memory reallocations - static DataStructures::List freeList; - - static int RakStringComp( RakString const &key, RakString const &data ); - - static void LockMutex(void); - static void UnlockMutex(void); - -protected: - void Allocate(size_t len); - void Assign(const char *str); - void Assign(const char *str, va_list ap); - - void Clone(void); - void Free(void); - unsigned char ToLower(unsigned char c); - unsigned char ToUpper(unsigned char c); - void Realloc(SharedString *sharedString, size_t bytes); -}; - -} - -const RakNet::RakString RAK_DLL_EXPORT operator+(const RakNet::RakString &lhs, const RakNet::RakString &rhs); - - -#endif +#ifndef __RAK_STRING_H +#define __RAK_STRING_H + +#include "Export.h" +#include "DS_List.h" +#include "RakNetTypes.h" // int64_t +#include +#include "stdarg.h" + + +#ifdef _WIN32 + + + +#include "WindowsIncludes.h" +#endif + +namespace RakNet +{ +/// Forward declarations +class SimpleMutex; +class BitStream; + +/// \brief String class +/// \details Has the following improvements over std::string +/// -Reference counting: Suitable to store in lists +/// -Variadic assignment operator +/// -Doesn't cause linker errors +class RAK_DLL_EXPORT RakString +{ +public: + // Constructors + RakString(); + RakString(char input); + RakString(unsigned char input); + RakString(const unsigned char *format, ...); + RakString(const char *format, ...); + ~RakString(); + RakString( const RakString & rhs); + + /// Implicit return of const char* + operator const char* () const {return sharedString->c_str;} + + /// Same as std::string::c_str + const char *C_String(void) const {return sharedString->c_str;} + + // Lets you modify the string. Do not make the string longer - however, you can make it shorter, or change the contents. + // Pointer is only valid in the scope of RakString itself + char *C_StringUnsafe(void) {Clone(); return sharedString->c_str;} + + /// Assigment operators + RakString& operator = ( const RakString& rhs ); + RakString& operator = ( const char *str ); + RakString& operator = ( char *str ); + RakString& operator = ( const unsigned char *str ); + RakString& operator = ( char unsigned *str ); + RakString& operator = ( const char c ); + + /// Concatenation + RakString& operator +=( const RakString& rhs); + RakString& operator += ( const char *str ); + RakString& operator += ( char *str ); + RakString& operator += ( const unsigned char *str ); + RakString& operator += ( char unsigned *str ); + RakString& operator += ( const char c ); + + /// Character index. Do not use to change the string however. + unsigned char operator[] ( const unsigned int position ) const; + +#ifdef _WIN32 + // Return as Wide char + // Deallocate with DeallocWideChar + WCHAR * ToWideChar(void); + void DeallocWideChar(WCHAR * w); +#endif + + ///String class find replacement + ///Searches the string for the content specified in stringToFind and returns the position of the first occurrence in the string. + ///Search only includes characters on or after position pos, ignoring any possible occurrences in previous locations. + /// \param[in] stringToFind The string to find inside of this object's string + /// \param[in] pos The position in the string to start the search + /// \return Returns the position of the first occurrence in the string. + size_t Find(const char *stringToFind,size_t pos = 0 ); + + /// Equality + bool operator==(const RakString &rhs) const; + bool operator==(const char *str) const; + bool operator==(char *str) const; + + // Comparison + bool operator < ( const RakString& right ) const; + bool operator <= ( const RakString& right ) const; + bool operator > ( const RakString& right ) const; + bool operator >= ( const RakString& right ) const; + + /// Inequality + bool operator!=(const RakString &rhs) const; + bool operator!=(const char *str) const; + bool operator!=(char *str) const; + + /// Change all characters to lowercase + const char * ToLower(void); + + /// Change all characters to uppercase + const char * ToUpper(void); + + /// Set the value of the string + void Set(const char *format, ...); + + /// Sets a copy of a substring of str as the new content. The substring is the portion of str + /// that begins at the character position pos and takes up to n characters + /// (it takes less than n if the end of str is reached before). + /// \param[in] str The string to copy in + /// \param[in] pos The position on str to start the copy + /// \param[in] n How many chars to copy + /// \return Returns the string, note that the current string is set to that value as well + RakString Assign(const char *str,size_t pos, size_t n ); + + /// Returns if the string is empty. Also, C_String() would return "" + bool IsEmpty(void) const; + + /// Returns the length of the string + size_t GetLength(void) const; + size_t GetLengthUTF8(void) const; + + /// Replace character(s) in starting at index, for count, with c + void Replace(unsigned index, unsigned count, unsigned char c); + + /// Replace character at index with c + void SetChar( unsigned index, unsigned char c ); + + /// Replace character at index with string s + void SetChar( unsigned index, RakNet::RakString s ); + + /// Make sure string is no longer than \a length + void Truncate(unsigned int length); + void TruncateUTF8(unsigned int length); + + // Gets the substring starting at index for count characters + RakString SubStr(unsigned int index, unsigned int count) const; + + /// Erase characters out of the string at index for count + void Erase(unsigned int index, unsigned int count); + + /// Set the first instance of c with a NULL terminator + void TerminateAtFirstCharacter(char c); + /// Set the last instance of c with a NULL terminator + void TerminateAtLastCharacter(char c); + + /// Remove all instances of c + void RemoveCharacter(char c); + + /// Create a RakString with a value, without doing printf style parsing + /// Equivalent to assignment operator + static RakNet::RakString NonVariadic(const char *str); + + /// Has the string into an unsigned int + static unsigned long ToInteger(const char *str); + static unsigned long ToInteger(const RakString &rs); + + // Like strncat, but for a fixed length + void AppendBytes(const char *bytes, unsigned int count); + + /// Compare strings (case sensitive) + int StrCmp(const RakString &rhs) const; + + /// Compare strings (not case sensitive) + int StrICmp(const RakString &rhs) const; + + /// Clear the string + void Clear(void); + + /// Print the string to the screen + void Printf(void); + + /// Print the string to a file + void FPrintf(FILE *fp); + + /// Does the given IP address match the IP address encoded into this string, accounting for wildcards? + bool IPAddressMatch(const char *IP); + + /// Does the string contain non-printable characters other than spaces? + bool ContainsNonprintableExceptSpaces(void) const; + + /// Is this a valid email address? + bool IsEmailAddress(void) const; + + /// URL Encode the string. See http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4029/ + RakNet::RakString& URLEncode(void); + + /// URL decode the string + RakNet::RakString& URLDecode(void); + + /// https://servers.api.rackspacecloud.com/v1.0 to https://, servers.api.rackspacecloud.com, /v1.0 + void SplitURI(RakNet::RakString &header, RakNet::RakString &domain, RakNet::RakString &path); + + /// Scan for quote, double quote, and backslash and prepend with backslash + RakNet::RakString& SQLEscape(void); + + /// Fix to be a file path, ending with / + RakNet::RakString& MakeFilePath(void); + + /// RakString uses a freeList of old no-longer used strings + /// Call this function to clear this memory on shutdown + static void FreeMemory(void); + /// \internal + static void FreeMemoryNoMutex(void); + + /// Serialize to a bitstream, uncompressed (slightly faster) + /// \param[out] bs Bitstream to serialize to + void Serialize(BitStream *bs) const; + + /// Static version of the Serialize function + static void Serialize(const char *str, BitStream *bs); + + /// Serialize to a bitstream, compressed (better bandwidth usage) + /// \param[out] bs Bitstream to serialize to + /// \param[in] languageId languageId to pass to the StringCompressor class + /// \param[in] writeLanguageId encode the languageId variable in the stream. If false, 0 is assumed, and DeserializeCompressed will not look for this variable in the stream (saves bandwidth) + /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) + void SerializeCompressed(BitStream *bs, uint8_t languageId=0, bool writeLanguageId=false) const; + + /// Static version of the SerializeCompressed function + static void SerializeCompressed(const char *str, BitStream *bs, uint8_t languageId=0, bool writeLanguageId=false); + + /// Deserialize what was written by Serialize + /// \param[in] bs Bitstream to serialize from + /// \return true if the deserialization was successful + bool Deserialize(BitStream *bs); + + /// Static version of the Deserialize() function + static bool Deserialize(char *str, BitStream *bs); + + /// Deserialize compressed string, written by SerializeCompressed + /// \param[in] bs Bitstream to serialize from + /// \param[in] readLanguageId If true, looks for the variable langaugeId in the data stream. Must match what was passed to SerializeCompressed + /// \return true if the deserialization was successful + /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) + bool DeserializeCompressed(BitStream *bs, bool readLanguageId=false); + + /// Static version of the DeserializeCompressed() function + static bool DeserializeCompressed(char *str, BitStream *bs, bool readLanguageId=false); + + static const char *ToString(int64_t i); + static const char *ToString(uint64_t i); + + /// \internal + static size_t GetSizeToAllocate(size_t bytes) + { + const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + if (bytes<=smallStringSize) + return smallStringSize; + else + return bytes*2; + } + + /// \internal + struct SharedString + { + SimpleMutex *refCountMutex; + unsigned int refCount; + size_t bytesUsed; + char *bigString; + char *c_str; + char smallString[128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2]; + }; + + /// \internal + RakString( SharedString *_sharedString ); + + /// \internal + SharedString *sharedString; + +// static SimpleMutex poolMutex; +// static DataStructures::MemoryPool pool; + /// \internal + static SharedString emptyString; + + //static SharedString *sharedStringFreeList; + //static unsigned int sharedStringFreeListAllocationCount; + /// \internal + /// List of free objects to reduce memory reallocations + static DataStructures::List freeList; + + static int RakStringComp( RakString const &key, RakString const &data ); + + static void LockMutex(void); + static void UnlockMutex(void); + +protected: + void Allocate(size_t len); + void Assign(const char *str); + void Assign(const char *str, va_list ap); + + void Clone(void); + void Free(void); + unsigned char ToLower(unsigned char c); + unsigned char ToUpper(unsigned char c); + void Realloc(SharedString *sharedString, size_t bytes); +}; + +} + +const RakNet::RakString RAK_DLL_EXPORT operator+(const RakNet::RakString &lhs, const RakNet::RakString &rhs); + + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakThread.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakThread.cpp index 268ee3a..fefe388 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakThread.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakThread.cpp @@ -1,163 +1,163 @@ -#include "RakThread.h" -#include "RakAssert.h" -#include "RakNetDefines.h" -#include "RakSleep.h" -#include "RakMemoryOverride.h" - -using namespace RakNet; - - - - -#if defined(_WIN32) -#include "WindowsIncludes.h" -#include - #if !defined(_WIN32_WCE) - #include - #endif - - - - -#else -#include -#endif - - -#if defined(_WIN32_WCE) -int RakThread::Create( LPTHREAD_START_ROUTINE start_address, void *arglist, int priority) -#elif defined(_WIN32) -int RakThread::Create( unsigned __stdcall start_address( void* ), void *arglist, int priority) - - - -#else -int RakThread::Create( void* start_address( void* ), void *arglist, int priority) -#endif -{ -#ifdef _WIN32 - HANDLE threadHandle; - unsigned threadID = 0; - - -#if defined (_WIN32_WCE) - threadHandle = CreateThread(NULL,MAX_ALLOCA_STACK_ALLOCATION*2,start_address,arglist,0,(DWORD*)&threadID); - SetThreadPriority(threadHandle, priority); -#else - threadHandle = (HANDLE) _beginthreadex( NULL, MAX_ALLOCA_STACK_ALLOCATION*2, start_address, arglist, 0, &threadID ); -#endif - SetThreadPriority(threadHandle, priority); - - if (threadHandle==0) - { - return 1; - } - else - { - CloseHandle( threadHandle ); - return 0; - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#else - pthread_t threadHandle; - // Create thread linux - pthread_attr_t attr; - sched_param param; - param.sched_priority=priority; - pthread_attr_init( &attr ); - pthread_attr_setschedparam(&attr, ¶m); - - - - - - pthread_attr_setstacksize(&attr, MAX_ALLOCA_STACK_ALLOCATION*2); - - pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); - int res = pthread_create( &threadHandle, &attr, start_address, arglist ); - RakAssert(res==0 && "pthread_create in RakThread.cpp failed.") - return res; -#endif -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#include "RakThread.h" +#include "RakAssert.h" +#include "RakNetDefines.h" +#include "RakSleep.h" +#include "RakMemoryOverride.h" + +using namespace RakNet; + + + + +#if defined(_WIN32) +#include "WindowsIncludes.h" +#include + #if !defined(_WIN32_WCE) + #include + #endif + + + + +#else +#include +#endif + + +#if defined(_WIN32_WCE) +int RakThread::Create( LPTHREAD_START_ROUTINE start_address, void *arglist, int priority) +#elif defined(_WIN32) +int RakThread::Create( unsigned __stdcall start_address( void* ), void *arglist, int priority) + + + +#else +int RakThread::Create( void* start_address( void* ), void *arglist, int priority) +#endif +{ +#ifdef _WIN32 + HANDLE threadHandle; + unsigned threadID = 0; + + +#if defined (_WIN32_WCE) + threadHandle = CreateThread(NULL,MAX_ALLOCA_STACK_ALLOCATION*2,start_address,arglist,0,(DWORD*)&threadID); + SetThreadPriority(threadHandle, priority); +#else + threadHandle = (HANDLE) _beginthreadex( NULL, MAX_ALLOCA_STACK_ALLOCATION*2, start_address, arglist, 0, &threadID ); +#endif + SetThreadPriority(threadHandle, priority); + + if (threadHandle==0) + { + return 1; + } + else + { + CloseHandle( threadHandle ); + return 0; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#else + pthread_t threadHandle; + // Create thread linux + pthread_attr_t attr; + sched_param param; + param.sched_priority=priority; + pthread_attr_init( &attr ); + pthread_attr_setschedparam(&attr, ¶m); + + + + + + pthread_attr_setstacksize(&attr, MAX_ALLOCA_STACK_ALLOCATION*2); + + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); + int res = pthread_create( &threadHandle, &attr, start_address, arglist ); + RakAssert(res==0 && "pthread_create in RakThread.cpp failed.") + return res; +#endif +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakThread.h b/project/lib_projects/raknet/jni/RaknetSources/RakThread.h index e6bf005..6cacd83 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakThread.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakThread.h @@ -1,88 +1,88 @@ -#ifndef __RAK_THREAD_H -#define __RAK_THREAD_H - -#if defined(_WIN32_WCE) -#include "WindowsIncludes.h" -#endif - -#include "Export.h" - - - - - - -namespace RakNet -{ - -/// To define a thread, use RAK_THREAD_DECLARATION(functionName); -#if defined(_WIN32_WCE) -#define RAK_THREAD_DECLARATION(functionName) DWORD WINAPI functionName(LPVOID arguments) - - -#elif defined(_WIN32) -#define RAK_THREAD_DECLARATION(functionName) unsigned __stdcall functionName( void* arguments ) - - -#else -#define RAK_THREAD_DECLARATION(functionName) void* functionName( void* arguments ) -#endif - -class RAK_DLL_EXPORT RakThread -{ -public: - - - - - /// Create a thread, simplified to be cross platform without all the extra junk - /// To then start that thread, call RakCreateThread(functionName, arguments); - /// \param[in] start_address Function you want to call - /// \param[in] arglist Arguments to pass to the function - /// \return 0=success. >0 = error code - - /* - nice value Win32 Priority - -20 to -16 THREAD_PRIORITY_HIGHEST - -15 to -6 THREAD_PRIORITY_ABOVE_NORMAL - -5 to +4 THREAD_PRIORITY_NORMAL - +5 to +14 THREAD_PRIORITY_BELOW_NORMAL - +15 to +19 THREAD_PRIORITY_LOWEST - */ -#if defined(_WIN32_WCE) - static int Create( LPTHREAD_START_ROUTINE start_address, void *arglist, int priority=0); - - -#elif defined(_WIN32) - static int Create( unsigned __stdcall start_address( void* ), void *arglist, int priority=0); - - - -#else - static int Create( void* start_address( void* ), void *arglist, int priority=0); -#endif - - - - - - - - - - - - - - - - - - - - -}; - -} - -#endif +#ifndef __RAK_THREAD_H +#define __RAK_THREAD_H + +#if defined(_WIN32_WCE) +#include "WindowsIncludes.h" +#endif + +#include "Export.h" + + + + + + +namespace RakNet +{ + +/// To define a thread, use RAK_THREAD_DECLARATION(functionName); +#if defined(_WIN32_WCE) +#define RAK_THREAD_DECLARATION(functionName) DWORD WINAPI functionName(LPVOID arguments) + + +#elif defined(_WIN32) +#define RAK_THREAD_DECLARATION(functionName) unsigned __stdcall functionName( void* arguments ) + + +#else +#define RAK_THREAD_DECLARATION(functionName) void* functionName( void* arguments ) +#endif + +class RAK_DLL_EXPORT RakThread +{ +public: + + + + + /// Create a thread, simplified to be cross platform without all the extra junk + /// To then start that thread, call RakCreateThread(functionName, arguments); + /// \param[in] start_address Function you want to call + /// \param[in] arglist Arguments to pass to the function + /// \return 0=success. >0 = error code + + /* + nice value Win32 Priority + -20 to -16 THREAD_PRIORITY_HIGHEST + -15 to -6 THREAD_PRIORITY_ABOVE_NORMAL + -5 to +4 THREAD_PRIORITY_NORMAL + +5 to +14 THREAD_PRIORITY_BELOW_NORMAL + +15 to +19 THREAD_PRIORITY_LOWEST + */ +#if defined(_WIN32_WCE) + static int Create( LPTHREAD_START_ROUTINE start_address, void *arglist, int priority=0); + + +#elif defined(_WIN32) + static int Create( unsigned __stdcall start_address( void* ), void *arglist, int priority=0); + + + +#else + static int Create( void* start_address( void* ), void *arglist, int priority=0); +#endif + + + + + + + + + + + + + + + + + + + + +}; + +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakWString.cpp b/project/lib_projects/raknet/jni/RaknetSources/RakWString.cpp index fbaea50..56e2a31 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakWString.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/RakWString.cpp @@ -1,345 +1,345 @@ -#include "RakWString.h" -#include "BitStream.h" -#include -#include -#include - -using namespace RakNet; - -// From http://www.joelonsoftware.com/articles/Unicode.html -// Only code points 128 and above are stored using 2, 3, in fact, up to 6 bytes. -//#define MAX_BYTES_PER_UNICODE_CHAR 6 -#define MAX_BYTES_PER_UNICODE_CHAR 2 - -RakWString::RakWString() -{ - c_str=0; - c_strCharLength=0; -} -RakWString::RakWString( const RakString &right ) -{ - c_str=0; - c_strCharLength=0; - *this=right; -} -RakWString::RakWString( const char *input ) -{ - c_str=0; - c_strCharLength=0; - *this = input; -} -RakWString::RakWString( const wchar_t *input ) -{ - c_str=0; - c_strCharLength=0; - *this = input; -} -RakWString::RakWString( const RakWString & right) -{ - c_str=0; - c_strCharLength=0; - *this = right; -} -RakWString::~RakWString() -{ - rakFree_Ex(c_str,_FILE_AND_LINE_); -} -RakWString& RakWString::operator = ( const RakWString& right ) -{ - Clear(); - if (right.IsEmpty()) - return *this; - c_str = (wchar_t *) rakMalloc_Ex( (right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!c_str) - { - c_strCharLength=0; - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - c_strCharLength = right.GetLength(); - memcpy(c_str,right.C_String(),(right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR); - - return *this; -} -RakWString& RakWString::operator = ( const RakString& right ) -{ - return *this = right.C_String(); -} -RakWString& RakWString::operator = ( const wchar_t * const str ) -{ - Clear(); - if (str==0) - return *this; - c_strCharLength = wcslen(str); - if (c_strCharLength==0) - return *this; - c_str = (wchar_t *) rakMalloc_Ex( (c_strCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!c_str) - { - c_strCharLength=0; - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - wcscpy(c_str,str); - - return *this; -} -RakWString& RakWString::operator = ( wchar_t *str ) -{ - *this = ( const wchar_t * const) str; - return *this; -} -RakWString& RakWString::operator = ( const char * const str ) -{ - Clear(); - -// Not supported on android -#if !defined(ANDROID) - if (str==0) - return *this; - if (str[0]==0) - return *this; - - c_strCharLength = mbstowcs(NULL, str, 0); - c_str = (wchar_t *) rakMalloc_Ex( (c_strCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!c_str) - { - c_strCharLength=0; - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - - c_strCharLength = mbstowcs(c_str, str, c_strCharLength+1); - if (c_strCharLength == (size_t) (-1)) - { - RAKNET_DEBUG_PRINTF("Couldn't convert string--invalid multibyte character.\n"); - Clear(); - return *this; - } -#else - // mbstowcs not supported on android - RakAssert("mbstowcs not supported on Android" && 0); -#endif // defined(ANDROID) - - return *this; -} -RakWString& RakWString::operator = ( char *str ) -{ - *this = ( const char * const) str; - return *this; -} -RakWString& RakWString::operator +=( const RakWString& right) -{ - if (right.IsEmpty()) - return *this; - size_t newCharLength = c_strCharLength + right.GetLength(); - wchar_t *newCStr; - bool isEmpty = IsEmpty(); - if (isEmpty) - newCStr = (wchar_t *) rakMalloc_Ex( (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - else - newCStr = (wchar_t *) rakRealloc_Ex( c_str, (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!newCStr) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - c_str = newCStr; - c_strCharLength = newCharLength; - if (isEmpty) - { - memcpy(newCStr,right.C_String(),(right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR); - } - else - { - wcscat(c_str, right.C_String()); - } - - return *this; -} -RakWString& RakWString::operator += ( const wchar_t * const right ) -{ - if (right==0) - return *this; - size_t rightLength = wcslen(right); - size_t newCharLength = c_strCharLength + rightLength; - wchar_t *newCStr; - bool isEmpty = IsEmpty(); - if (isEmpty) - newCStr = (wchar_t *) rakMalloc_Ex( (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - else - newCStr = (wchar_t *) rakRealloc_Ex( c_str, (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!newCStr) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - c_str = newCStr; - c_strCharLength = newCharLength; - if (isEmpty) - { - memcpy(newCStr,right,(rightLength + 1) * MAX_BYTES_PER_UNICODE_CHAR); - } - else - { - wcscat(c_str, right); - } - - return *this; -} -RakWString& RakWString::operator += ( wchar_t *right ) -{ - return *this += (const wchar_t * const) right; -} -bool RakWString::operator==(const RakWString &right) const -{ - if (GetLength()!=right.GetLength()) - return false; - return wcscmp(c_str,right.C_String())==0; -} -bool RakWString::operator < ( const RakWString& right ) const -{ - return wcscmp(c_str,right.C_String())<0; -} -bool RakWString::operator <= ( const RakWString& right ) const -{ - return wcscmp(c_str,right.C_String())<=0; -} -bool RakWString::operator > ( const RakWString& right ) const -{ - return wcscmp(c_str,right.C_String())>0; -} -bool RakWString::operator >= ( const RakWString& right ) const -{ - return wcscmp(c_str,right.C_String())>=0; -} -bool RakWString::operator!=(const RakWString &right) const -{ - if (GetLength()!=right.GetLength()) - return true; - return wcscmp(c_str,right.C_String())!=0; -} -void RakWString::Set( wchar_t *str ) -{ - *this = str; -} -bool RakWString::IsEmpty(void) const -{ - return GetLength()==0; -} -size_t RakWString::GetLength(void) const -{ - return c_strCharLength; -} -unsigned long RakWString::ToInteger(const RakWString &rs) -{ - unsigned long hash = 0; - int c; - - const char *str = (const char *)rs.C_String(); - size_t i; - for (i=0; i < rs.GetLength()*MAX_BYTES_PER_UNICODE_CHAR*sizeof(wchar_t); i++) - { - c = *str++; - hash = c + (hash << 6) + (hash << 16) - hash; - } - - return hash; -} -int RakWString::StrCmp(const RakWString &right) const -{ - return wcscmp(C_String(), right.C_String()); -} -int RakWString::StrICmp(const RakWString &right) const -{ -#ifdef _WIN32 - return wcsicmp(C_String(), right.C_String()); -#else - // Not supported - return wcscmp(C_String(), right.C_String()); -#endif -} -void RakWString::Clear(void) -{ - rakFree_Ex(c_str,_FILE_AND_LINE_); - c_str=0; - c_strCharLength=0; -} -void RakWString::Printf(void) -{ - printf("%ls", C_String()); -} -void RakWString::FPrintf(FILE *fp) -{ - fprintf(fp,"%ls", C_String()); -} -void RakWString::Serialize(BitStream *bs) const -{ - bs->WriteCasted(c_strCharLength); - bs->WriteAlignedBytes((const unsigned char*) c_str,(const unsigned int) (c_strCharLength+1)*MAX_BYTES_PER_UNICODE_CHAR); -} -void RakWString::Serialize(const wchar_t * const str, BitStream *bs) -{ - size_t length = wcslen(str); - bs->WriteCasted(length); - bs->WriteAlignedBytes((const unsigned char*) str,(const unsigned int) length*MAX_BYTES_PER_UNICODE_CHAR); -} -bool RakWString::Deserialize(BitStream *bs) -{ - Clear(); - size_t length; - bs->ReadCasted(length); - if (length>0) - { - c_str = (wchar_t *) rakMalloc_Ex( (length + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!c_str) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return false; - } - c_strCharLength = (size_t) length; - return bs->ReadAlignedBytes((unsigned char*) c_str,(const unsigned int) (c_strCharLength+1)*MAX_BYTES_PER_UNICODE_CHAR); - } - else - { - return true; - } -} -bool RakWString::Deserialize(wchar_t *str, BitStream *bs) -{ - size_t length; - bs->ReadCasted(length); - if (length>0) - { - return bs->ReadAlignedBytes((unsigned char*) str,(const unsigned int) (length+1)*MAX_BYTES_PER_UNICODE_CHAR); - } - else - { - wcscpy(str,L""); - } - return true; -} - -/* -RakNet::BitStream bsTest; -RakNet::RakWString testString("cat"), testString2; -testString = "Hllo"; -testString = L"Hello"; -testString += L" world"; -testString2 += testString2; -RakNet::RakWString ts3(L" from here"); -testString2+=ts3; -RakNet::RakWString ts4(L" 222"); -testString2=ts4; -RakNet::RakString rs("rakstring"); -testString2+=rs; -testString2=rs; -bsTest.Write(L"one"); -bsTest.Write(testString2); -bsTest.SetReadOffset(0); -RakNet::RakWString ts5, ts6; -wchar_t buff[99]; -wchar_t *wptr = (wchar_t*)buff; -bsTest.Read(wptr); -bsTest.Read(ts5); -*/ +#include "RakWString.h" +#include "BitStream.h" +#include +#include +#include + +using namespace RakNet; + +// From http://www.joelonsoftware.com/articles/Unicode.html +// Only code points 128 and above are stored using 2, 3, in fact, up to 6 bytes. +//#define MAX_BYTES_PER_UNICODE_CHAR 6 +#define MAX_BYTES_PER_UNICODE_CHAR 2 + +RakWString::RakWString() +{ + c_str=0; + c_strCharLength=0; +} +RakWString::RakWString( const RakString &right ) +{ + c_str=0; + c_strCharLength=0; + *this=right; +} +RakWString::RakWString( const char *input ) +{ + c_str=0; + c_strCharLength=0; + *this = input; +} +RakWString::RakWString( const wchar_t *input ) +{ + c_str=0; + c_strCharLength=0; + *this = input; +} +RakWString::RakWString( const RakWString & right) +{ + c_str=0; + c_strCharLength=0; + *this = right; +} +RakWString::~RakWString() +{ + rakFree_Ex(c_str,_FILE_AND_LINE_); +} +RakWString& RakWString::operator = ( const RakWString& right ) +{ + Clear(); + if (right.IsEmpty()) + return *this; + c_str = (wchar_t *) rakMalloc_Ex( (right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!c_str) + { + c_strCharLength=0; + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + c_strCharLength = right.GetLength(); + memcpy(c_str,right.C_String(),(right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR); + + return *this; +} +RakWString& RakWString::operator = ( const RakString& right ) +{ + return *this = right.C_String(); +} +RakWString& RakWString::operator = ( const wchar_t * const str ) +{ + Clear(); + if (str==0) + return *this; + c_strCharLength = wcslen(str); + if (c_strCharLength==0) + return *this; + c_str = (wchar_t *) rakMalloc_Ex( (c_strCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!c_str) + { + c_strCharLength=0; + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + wcscpy(c_str,str); + + return *this; +} +RakWString& RakWString::operator = ( wchar_t *str ) +{ + *this = ( const wchar_t * const) str; + return *this; +} +RakWString& RakWString::operator = ( const char * const str ) +{ + Clear(); + +// Not supported on android +#if !defined(ANDROID) + if (str==0) + return *this; + if (str[0]==0) + return *this; + + c_strCharLength = mbstowcs(NULL, str, 0); + c_str = (wchar_t *) rakMalloc_Ex( (c_strCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!c_str) + { + c_strCharLength=0; + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + + c_strCharLength = mbstowcs(c_str, str, c_strCharLength+1); + if (c_strCharLength == (size_t) (-1)) + { + RAKNET_DEBUG_PRINTF("Couldn't convert string--invalid multibyte character.\n"); + Clear(); + return *this; + } +#else + // mbstowcs not supported on android + RakAssert("mbstowcs not supported on Android" && 0); +#endif // defined(ANDROID) + + return *this; +} +RakWString& RakWString::operator = ( char *str ) +{ + *this = ( const char * const) str; + return *this; +} +RakWString& RakWString::operator +=( const RakWString& right) +{ + if (right.IsEmpty()) + return *this; + size_t newCharLength = c_strCharLength + right.GetLength(); + wchar_t *newCStr; + bool isEmpty = IsEmpty(); + if (isEmpty) + newCStr = (wchar_t *) rakMalloc_Ex( (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + else + newCStr = (wchar_t *) rakRealloc_Ex( c_str, (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!newCStr) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + c_str = newCStr; + c_strCharLength = newCharLength; + if (isEmpty) + { + memcpy(newCStr,right.C_String(),(right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR); + } + else + { + wcscat(c_str, right.C_String()); + } + + return *this; +} +RakWString& RakWString::operator += ( const wchar_t * const right ) +{ + if (right==0) + return *this; + size_t rightLength = wcslen(right); + size_t newCharLength = c_strCharLength + rightLength; + wchar_t *newCStr; + bool isEmpty = IsEmpty(); + if (isEmpty) + newCStr = (wchar_t *) rakMalloc_Ex( (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + else + newCStr = (wchar_t *) rakRealloc_Ex( c_str, (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!newCStr) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + c_str = newCStr; + c_strCharLength = newCharLength; + if (isEmpty) + { + memcpy(newCStr,right,(rightLength + 1) * MAX_BYTES_PER_UNICODE_CHAR); + } + else + { + wcscat(c_str, right); + } + + return *this; +} +RakWString& RakWString::operator += ( wchar_t *right ) +{ + return *this += (const wchar_t * const) right; +} +bool RakWString::operator==(const RakWString &right) const +{ + if (GetLength()!=right.GetLength()) + return false; + return wcscmp(c_str,right.C_String())==0; +} +bool RakWString::operator < ( const RakWString& right ) const +{ + return wcscmp(c_str,right.C_String())<0; +} +bool RakWString::operator <= ( const RakWString& right ) const +{ + return wcscmp(c_str,right.C_String())<=0; +} +bool RakWString::operator > ( const RakWString& right ) const +{ + return wcscmp(c_str,right.C_String())>0; +} +bool RakWString::operator >= ( const RakWString& right ) const +{ + return wcscmp(c_str,right.C_String())>=0; +} +bool RakWString::operator!=(const RakWString &right) const +{ + if (GetLength()!=right.GetLength()) + return true; + return wcscmp(c_str,right.C_String())!=0; +} +void RakWString::Set( wchar_t *str ) +{ + *this = str; +} +bool RakWString::IsEmpty(void) const +{ + return GetLength()==0; +} +size_t RakWString::GetLength(void) const +{ + return c_strCharLength; +} +unsigned long RakWString::ToInteger(const RakWString &rs) +{ + unsigned long hash = 0; + int c; + + const char *str = (const char *)rs.C_String(); + size_t i; + for (i=0; i < rs.GetLength()*MAX_BYTES_PER_UNICODE_CHAR*sizeof(wchar_t); i++) + { + c = *str++; + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} +int RakWString::StrCmp(const RakWString &right) const +{ + return wcscmp(C_String(), right.C_String()); +} +int RakWString::StrICmp(const RakWString &right) const +{ +#ifdef _WIN32 + return wcsicmp(C_String(), right.C_String()); +#else + // Not supported + return wcscmp(C_String(), right.C_String()); +#endif +} +void RakWString::Clear(void) +{ + rakFree_Ex(c_str,_FILE_AND_LINE_); + c_str=0; + c_strCharLength=0; +} +void RakWString::Printf(void) +{ + printf("%ls", C_String()); +} +void RakWString::FPrintf(FILE *fp) +{ + fprintf(fp,"%ls", C_String()); +} +void RakWString::Serialize(BitStream *bs) const +{ + bs->WriteCasted(c_strCharLength); + bs->WriteAlignedBytes((const unsigned char*) c_str,(const unsigned int) (c_strCharLength+1)*MAX_BYTES_PER_UNICODE_CHAR); +} +void RakWString::Serialize(const wchar_t * const str, BitStream *bs) +{ + size_t length = wcslen(str); + bs->WriteCasted(length); + bs->WriteAlignedBytes((const unsigned char*) str,(const unsigned int) length*MAX_BYTES_PER_UNICODE_CHAR); +} +bool RakWString::Deserialize(BitStream *bs) +{ + Clear(); + size_t length; + bs->ReadCasted(length); + if (length>0) + { + c_str = (wchar_t *) rakMalloc_Ex( (length + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!c_str) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return false; + } + c_strCharLength = (size_t) length; + return bs->ReadAlignedBytes((unsigned char*) c_str,(const unsigned int) (c_strCharLength+1)*MAX_BYTES_PER_UNICODE_CHAR); + } + else + { + return true; + } +} +bool RakWString::Deserialize(wchar_t *str, BitStream *bs) +{ + size_t length; + bs->ReadCasted(length); + if (length>0) + { + return bs->ReadAlignedBytes((unsigned char*) str,(const unsigned int) (length+1)*MAX_BYTES_PER_UNICODE_CHAR); + } + else + { + wcscpy(str,L""); + } + return true; +} + +/* +RakNet::BitStream bsTest; +RakNet::RakWString testString("cat"), testString2; +testString = "Hllo"; +testString = L"Hello"; +testString += L" world"; +testString2 += testString2; +RakNet::RakWString ts3(L" from here"); +testString2+=ts3; +RakNet::RakWString ts4(L" 222"); +testString2=ts4; +RakNet::RakString rs("rakstring"); +testString2+=rs; +testString2=rs; +bsTest.Write(L"one"); +bsTest.Write(testString2); +bsTest.SetReadOffset(0); +RakNet::RakWString ts5, ts6; +wchar_t buff[99]; +wchar_t *wptr = (wchar_t*)buff; +bsTest.Read(wptr); +bsTest.Read(ts5); +*/ diff --git a/project/lib_projects/raknet/jni/RaknetSources/RakWString.h b/project/lib_projects/raknet/jni/RaknetSources/RakWString.h index e3baa7e..f633596 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RakWString.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RakWString.h @@ -1,113 +1,113 @@ -#ifndef __RAK_W_STRING_H -#define __RAK_W_STRING_H - -#include "Export.h" -#include "RakNetTypes.h" // int64_t -#include "RakString.h" - -#ifdef _WIN32 - - - -#include "WindowsIncludes.h" -#endif - -namespace RakNet -{ - /// \brief String class for Unicode - class RAK_DLL_EXPORT RakWString - { - public: - // Constructors - RakWString(); - RakWString( const RakString &right ); - RakWString( const wchar_t *input ); - RakWString( const RakWString & right); - RakWString( const char *input ); - ~RakWString(); - - /// Implicit return of wchar_t* - operator wchar_t* () const {if (c_str) return c_str; return (wchar_t*) L"";} - - /// Same as std::string::c_str - const wchar_t* C_String(void) const {if (c_str) return c_str; return (const wchar_t*) L"";} - - /// Assignment operators - RakWString& operator = ( const RakWString& right ); - RakWString& operator = ( const RakString& right ); - RakWString& operator = ( const wchar_t * const str ); - RakWString& operator = ( wchar_t *str ); - RakWString& operator = ( const char * const str ); - RakWString& operator = ( char *str ); - - /// Concatenation - RakWString& operator +=( const RakWString& right); - RakWString& operator += ( const wchar_t * const right ); - RakWString& operator += ( wchar_t *right ); - - /// Equality - bool operator==(const RakWString &right) const; - - // Comparison - bool operator < ( const RakWString& right ) const; - bool operator <= ( const RakWString& right ) const; - bool operator > ( const RakWString& right ) const; - bool operator >= ( const RakWString& right ) const; - - /// Inequality - bool operator!=(const RakWString &right) const; - - /// Set the value of the string - void Set( wchar_t *str ); - - /// Returns if the string is empty. Also, C_String() would return "" - bool IsEmpty(void) const; - - /// Returns the length of the string - size_t GetLength(void) const; - - /// Has the string into an unsigned int - static unsigned long ToInteger(const RakWString &rs); - - /// Compare strings (case sensitive) - int StrCmp(const RakWString &right) const; - - /// Compare strings (not case sensitive) - int StrICmp(const RakWString &right) const; - - /// Clear the string - void Clear(void); - - /// Print the string to the screen - void Printf(void); - - /// Print the string to a file - void FPrintf(FILE *fp); - - /// Serialize to a bitstream, uncompressed (slightly faster) - /// \param[out] bs Bitstream to serialize to - void Serialize(BitStream *bs) const; - - /// Static version of the Serialize function - static void Serialize(const wchar_t * const str, BitStream *bs); - - /// Deserialize what was written by Serialize - /// \param[in] bs Bitstream to serialize from - /// \return true if the deserialization was successful - bool Deserialize(BitStream *bs); - - /// Static version of the Deserialize() function - static bool Deserialize(wchar_t *str, BitStream *bs); - - - protected: - wchar_t* c_str; - size_t c_strCharLength; - }; - -} - -const RakNet::RakWString RAK_DLL_EXPORT operator+(const RakNet::RakWString &lhs, const RakNet::RakWString &rhs); - - -#endif +#ifndef __RAK_W_STRING_H +#define __RAK_W_STRING_H + +#include "Export.h" +#include "RakNetTypes.h" // int64_t +#include "RakString.h" + +#ifdef _WIN32 + + + +#include "WindowsIncludes.h" +#endif + +namespace RakNet +{ + /// \brief String class for Unicode + class RAK_DLL_EXPORT RakWString + { + public: + // Constructors + RakWString(); + RakWString( const RakString &right ); + RakWString( const wchar_t *input ); + RakWString( const RakWString & right); + RakWString( const char *input ); + ~RakWString(); + + /// Implicit return of wchar_t* + operator wchar_t* () const {if (c_str) return c_str; return (wchar_t*) L"";} + + /// Same as std::string::c_str + const wchar_t* C_String(void) const {if (c_str) return c_str; return (const wchar_t*) L"";} + + /// Assignment operators + RakWString& operator = ( const RakWString& right ); + RakWString& operator = ( const RakString& right ); + RakWString& operator = ( const wchar_t * const str ); + RakWString& operator = ( wchar_t *str ); + RakWString& operator = ( const char * const str ); + RakWString& operator = ( char *str ); + + /// Concatenation + RakWString& operator +=( const RakWString& right); + RakWString& operator += ( const wchar_t * const right ); + RakWString& operator += ( wchar_t *right ); + + /// Equality + bool operator==(const RakWString &right) const; + + // Comparison + bool operator < ( const RakWString& right ) const; + bool operator <= ( const RakWString& right ) const; + bool operator > ( const RakWString& right ) const; + bool operator >= ( const RakWString& right ) const; + + /// Inequality + bool operator!=(const RakWString &right) const; + + /// Set the value of the string + void Set( wchar_t *str ); + + /// Returns if the string is empty. Also, C_String() would return "" + bool IsEmpty(void) const; + + /// Returns the length of the string + size_t GetLength(void) const; + + /// Has the string into an unsigned int + static unsigned long ToInteger(const RakWString &rs); + + /// Compare strings (case sensitive) + int StrCmp(const RakWString &right) const; + + /// Compare strings (not case sensitive) + int StrICmp(const RakWString &right) const; + + /// Clear the string + void Clear(void); + + /// Print the string to the screen + void Printf(void); + + /// Print the string to a file + void FPrintf(FILE *fp); + + /// Serialize to a bitstream, uncompressed (slightly faster) + /// \param[out] bs Bitstream to serialize to + void Serialize(BitStream *bs) const; + + /// Static version of the Serialize function + static void Serialize(const wchar_t * const str, BitStream *bs); + + /// Deserialize what was written by Serialize + /// \param[in] bs Bitstream to serialize from + /// \return true if the deserialization was successful + bool Deserialize(BitStream *bs); + + /// Static version of the Deserialize() function + static bool Deserialize(wchar_t *str, BitStream *bs); + + + protected: + wchar_t* c_str; + size_t c_strCharLength; + }; + +} + +const RakNet::RakWString RAK_DLL_EXPORT operator+(const RakNet::RakWString &lhs, const RakNet::RakWString &rhs); + + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/Rand.cpp b/project/lib_projects/raknet/jni/RaknetSources/Rand.cpp index c3d8413..79fc25f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Rand.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/Rand.cpp @@ -1,274 +1,274 @@ -/** -* -* Grabbed by Kevin from http://www.math.keio.ac.jp/~matumoto/cokus.c -* This is the ``Mersenne Twister'' random number generator MT19937, which -* generates pseudorandom integers uniformly distributed in 0..(2^32 - 1) -* starting from any odd seed in 0..(2^32 - 1). This version is a recode -* by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by -* Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in -* July-August 1997). -* -* Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha -* running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to -* generate 300 million random numbers; after recoding: 24.0 sec. for the same -* (i.e., 46.5% of original time), so speed is now about 12.5 million random -* number generations per second on this machine. -* -* According to the URL -* (and paraphrasing a bit in places), the Mersenne Twister is ``designed -* with consideration of the flaws of various existing generators,'' has -* a period of 2^19937 - 1, gives a sequence that is 623-dimensionally -* equidistributed, and ``has passed many stringent tests, including the -* die-hard test of G. Marsaglia and the load test of P. Hellekalek and -* S. Wegenkittl.'' It is efficient in memory usage (typically using 2506 -* to 5012 bytes of static data, depending on data type sizes, and the code -* is quite short as well). It generates random numbers in batches of 624 -* at a time, so the caching and pipelining of modern systems is exploited. -* It is also divide- and mod-free. -* -* Licensing is free http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/elicense.html -* -* The code as Shawn received it included the following notice: -* -* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. When -* you use this, send an e-mail to with -* an appropriate reference to your work. -* -* It would be nice to CC: when you write. -*/ - -#include -#include -#include -#include "Rand.h" - -// -// uint32 must be an unsigned integer type capable of holding at least 32 -// bits; exactly 32 should be fastest, but 64 is better on an Alpha with -// GCC at -O3 optimization so try your options and see what's best for you -// - -//typedef unsigned int uint32; - -#define N (624) // length of state vector -#define M (397) // a period parameter -#define K (0x9908B0DFU) // a magic constant -#define hiBit(u) ((u) & 0x80000000U) // mask all but highest bit of u -#define loBit(u) ((u) & 0x00000001U) // mask all but lowest bit of u -#define loBits(u) ((u) & 0x7FFFFFFFU) // mask the highest bit of u -#define mixBits(u, v) (hiBit(u)|loBits(v)) // move hi bit of u to hi bit of v - -static unsigned int _state[ N + 1 ]; // state vector + 1 extra to not violate ANSI C -static unsigned int *_next; // next random value is computed from here -static int _left = -1; // can *next++ this many times before reloading - -using namespace RakNet; - -void seedMT( unsigned int seed, unsigned int *state, unsigned int *&next, int &left ); -unsigned int reloadMT( unsigned int *state, unsigned int *&next, int &left ); -unsigned int randomMT( unsigned int *state, unsigned int *&next, int &left ); -void fillBufferMT( void *buffer, unsigned int bytes, unsigned int *state, unsigned int *&next, int &left ); -float frandomMT( unsigned int *state, unsigned int *&next, int &left ); - -// Uses global vars -void seedMT( unsigned int seed ) -{ - seedMT(seed, _state, _next, _left); -} -unsigned int reloadMT( void ) -{ - return reloadMT(_state, _next, _left); -} -unsigned int randomMT( void ) -{ - return randomMT(_state, _next, _left); -} -float frandomMT( void ) -{ - return frandomMT(_state, _next, _left); -} -void fillBufferMT( void *buffer, unsigned int bytes ) -{ - fillBufferMT(buffer, bytes, _state, _next, _left); -} - -void seedMT( unsigned int seed, unsigned int *state, unsigned int *&next, int &left ) // Defined in cokus_c.c -{ - (void) next; - - // - // We initialize state[0..(N-1)] via the generator - // - // x_new = (69069 * x_old) mod 2^32 - // - // from Line 15 of Table 1, p. 106, Sec. 3.3.4 of Knuth's - // _The Art of Computer Programming_, Volume 2, 3rd ed. - // - // Notes (SJC): I do not know what the initial state requirements - // of the Mersenne Twister are, but it seems this seeding generator - // could be better. It achieves the maximum period for its modulus - // (2^30) iff x_initial is odd (p. 20-21, Sec. 3.2.1.2, Knuth); if - // x_initial can be even, you have sequences like 0, 0, 0, ...; - // 2^31, 2^31, 2^31, ...; 2^30, 2^30, 2^30, ...; 2^29, 2^29 + 2^31, - // 2^29, 2^29 + 2^31, ..., etc. so I force seed to be odd below. - // - // Even if x_initial is odd, if x_initial is 1 mod 4 then - // - // the lowest bit of x is always 1, - // the next-to-lowest bit of x is always 0, - // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , - // the 3rd-from-lowest bit of x 4-cycles ... 0 1 1 0 0 1 1 0 ... , - // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 0 1 1 1 1 0 ... , - // ... - // - // and if x_initial is 3 mod 4 then - // - // the lowest bit of x is always 1, - // the next-to-lowest bit of x is always 1, - // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , - // the 3rd-from-lowest bit of x 4-cycles ... 0 0 1 1 0 0 1 1 ... , - // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 1 1 1 1 0 0 ... , - // ... - // - // The generator's potency (min. s>=0 with (69069-1)^s = 0 mod 2^32) is - // 16, which seems to be alright by p. 25, Sec. 3.2.1.3 of Knuth. It - // also does well in the dimension 2..5 spectral tests, but it could be - // better in dimension 6 (Line 15, Table 1, p. 106, Sec. 3.3.4, Knuth). - // - // Note that the random number user does not see the values generated - // here directly since reloadMT() will always munge them first, so maybe - // none of all of this matters. In fact, the seed values made here could - // even be extra-special desirable if the Mersenne Twister theory says - // so-- that's why the only change I made is to restrict to odd seeds. - // - - register unsigned int x = ( seed | 1U ) & 0xFFFFFFFFU, *s = state; - register int j; - - for ( left = 0, *s++ = x, j = N; --j; - *s++ = ( x *= 69069U ) & 0xFFFFFFFFU ) - - ; -} - - -unsigned int reloadMT( unsigned int *state, unsigned int *&next, int &left ) -{ - register unsigned int * p0 = state, *p2 = state + 2, *pM = state + M, s0, s1; - register int j; - - if ( left < -1 ) - seedMT( 4357U ); - - left = N - 1, next = state + 1; - - for ( s0 = state[ 0 ], s1 = state[ 1 ], j = N - M + 1; --j; s0 = s1, s1 = *p2++ ) - * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); - - for ( pM = state, j = M; --j; s0 = s1, s1 = *p2++ ) - * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); - - s1 = state[ 0 ], *p0 = *pM ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); - - s1 ^= ( s1 >> 11 ); - - s1 ^= ( s1 << 7 ) & 0x9D2C5680U; - - s1 ^= ( s1 << 15 ) & 0xEFC60000U; - - return ( s1 ^ ( s1 >> 18 ) ); -} - - -unsigned int randomMT( unsigned int *state, unsigned int *&next, int &left ) -{ - unsigned int y; - - if ( --left < 0 ) - return ( reloadMT(state, next, left) ); - - y = *next++; - - y ^= ( y >> 11 ); - - y ^= ( y << 7 ) & 0x9D2C5680U; - - y ^= ( y << 15 ) & 0xEFC60000U; - - return ( y ^ ( y >> 18 ) ); - - // This change made so the value returned is in the same range as what rand() returns - // return(y ^ (y >> 18)) % 32767; -} - -void fillBufferMT( void *buffer, unsigned int bytes, unsigned int *state, unsigned int *&next, int &left ) -{ - unsigned int offset=0; - unsigned int r; - while (bytes-offset>=sizeof(r)) - { - r = randomMT(state, next, left); - memcpy((char*)buffer+offset, &r, sizeof(r)); - offset+=sizeof(r); - } - - r = randomMT(state, next, left); - memcpy((char*)buffer+offset, &r, bytes-offset); -} - -float frandomMT( unsigned int *state, unsigned int *&next, int &left ) -{ - return ( float ) ( ( double ) randomMT(state, next, left) / 4294967296.0 ); -} -RakNetRandom::RakNetRandom() -{ - left=-1; -} -RakNetRandom::~RakNetRandom() -{ -} -void RakNetRandom::SeedMT( unsigned int seed ) -{ - printf("%i\n",seed); - seedMT(seed, state, next, left); -} - -unsigned int RakNetRandom::ReloadMT( void ) -{ - return reloadMT(state, next, left); -} - -unsigned int RakNetRandom::RandomMT( void ) -{ - return randomMT(state, next, left); -} - -float RakNetRandom::FrandomMT( void ) -{ - return frandomMT(state, next, left); -} - -void RakNetRandom::FillBufferMT( void *buffer, unsigned int bytes ) -{ - fillBufferMT(buffer, bytes, state, next, left); -} - -/* -int main(void) -{ -int j; - -// you can seed with any uint32, but the best are odds in 0..(2^32 - 1) - -seedMT(4357U); - -// print the first 2,002 random numbers seven to a line as an example - -for(j=0; j<2002; j++) -RAKNET_DEBUG_PRINTF(" %10lu%s", (unsigned int) randomMT(), (j%7)==6 ? "\n" : ""); - -return(EXIT_SUCCESS); -} - -*/ - +/** +* +* Grabbed by Kevin from http://www.math.keio.ac.jp/~matumoto/cokus.c +* This is the ``Mersenne Twister'' random number generator MT19937, which +* generates pseudorandom integers uniformly distributed in 0..(2^32 - 1) +* starting from any odd seed in 0..(2^32 - 1). This version is a recode +* by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by +* Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in +* July-August 1997). +* +* Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha +* running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to +* generate 300 million random numbers; after recoding: 24.0 sec. for the same +* (i.e., 46.5% of original time), so speed is now about 12.5 million random +* number generations per second on this machine. +* +* According to the URL +* (and paraphrasing a bit in places), the Mersenne Twister is ``designed +* with consideration of the flaws of various existing generators,'' has +* a period of 2^19937 - 1, gives a sequence that is 623-dimensionally +* equidistributed, and ``has passed many stringent tests, including the +* die-hard test of G. Marsaglia and the load test of P. Hellekalek and +* S. Wegenkittl.'' It is efficient in memory usage (typically using 2506 +* to 5012 bytes of static data, depending on data type sizes, and the code +* is quite short as well). It generates random numbers in batches of 624 +* at a time, so the caching and pipelining of modern systems is exploited. +* It is also divide- and mod-free. +* +* Licensing is free http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/elicense.html +* +* The code as Shawn received it included the following notice: +* +* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. When +* you use this, send an e-mail to with +* an appropriate reference to your work. +* +* It would be nice to CC: when you write. +*/ + +#include +#include +#include +#include "Rand.h" + +// +// uint32 must be an unsigned integer type capable of holding at least 32 +// bits; exactly 32 should be fastest, but 64 is better on an Alpha with +// GCC at -O3 optimization so try your options and see what's best for you +// + +//typedef unsigned int uint32; + +#define N (624) // length of state vector +#define M (397) // a period parameter +#define K (0x9908B0DFU) // a magic constant +#define hiBit(u) ((u) & 0x80000000U) // mask all but highest bit of u +#define loBit(u) ((u) & 0x00000001U) // mask all but lowest bit of u +#define loBits(u) ((u) & 0x7FFFFFFFU) // mask the highest bit of u +#define mixBits(u, v) (hiBit(u)|loBits(v)) // move hi bit of u to hi bit of v + +static unsigned int _state[ N + 1 ]; // state vector + 1 extra to not violate ANSI C +static unsigned int *_next; // next random value is computed from here +static int _left = -1; // can *next++ this many times before reloading + +using namespace RakNet; + +void seedMT( unsigned int seed, unsigned int *state, unsigned int *&next, int &left ); +unsigned int reloadMT( unsigned int *state, unsigned int *&next, int &left ); +unsigned int randomMT( unsigned int *state, unsigned int *&next, int &left ); +void fillBufferMT( void *buffer, unsigned int bytes, unsigned int *state, unsigned int *&next, int &left ); +float frandomMT( unsigned int *state, unsigned int *&next, int &left ); + +// Uses global vars +void seedMT( unsigned int seed ) +{ + seedMT(seed, _state, _next, _left); +} +unsigned int reloadMT( void ) +{ + return reloadMT(_state, _next, _left); +} +unsigned int randomMT( void ) +{ + return randomMT(_state, _next, _left); +} +float frandomMT( void ) +{ + return frandomMT(_state, _next, _left); +} +void fillBufferMT( void *buffer, unsigned int bytes ) +{ + fillBufferMT(buffer, bytes, _state, _next, _left); +} + +void seedMT( unsigned int seed, unsigned int *state, unsigned int *&next, int &left ) // Defined in cokus_c.c +{ + (void) next; + + // + // We initialize state[0..(N-1)] via the generator + // + // x_new = (69069 * x_old) mod 2^32 + // + // from Line 15 of Table 1, p. 106, Sec. 3.3.4 of Knuth's + // _The Art of Computer Programming_, Volume 2, 3rd ed. + // + // Notes (SJC): I do not know what the initial state requirements + // of the Mersenne Twister are, but it seems this seeding generator + // could be better. It achieves the maximum period for its modulus + // (2^30) iff x_initial is odd (p. 20-21, Sec. 3.2.1.2, Knuth); if + // x_initial can be even, you have sequences like 0, 0, 0, ...; + // 2^31, 2^31, 2^31, ...; 2^30, 2^30, 2^30, ...; 2^29, 2^29 + 2^31, + // 2^29, 2^29 + 2^31, ..., etc. so I force seed to be odd below. + // + // Even if x_initial is odd, if x_initial is 1 mod 4 then + // + // the lowest bit of x is always 1, + // the next-to-lowest bit of x is always 0, + // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , + // the 3rd-from-lowest bit of x 4-cycles ... 0 1 1 0 0 1 1 0 ... , + // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 0 1 1 1 1 0 ... , + // ... + // + // and if x_initial is 3 mod 4 then + // + // the lowest bit of x is always 1, + // the next-to-lowest bit of x is always 1, + // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , + // the 3rd-from-lowest bit of x 4-cycles ... 0 0 1 1 0 0 1 1 ... , + // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 1 1 1 1 0 0 ... , + // ... + // + // The generator's potency (min. s>=0 with (69069-1)^s = 0 mod 2^32) is + // 16, which seems to be alright by p. 25, Sec. 3.2.1.3 of Knuth. It + // also does well in the dimension 2..5 spectral tests, but it could be + // better in dimension 6 (Line 15, Table 1, p. 106, Sec. 3.3.4, Knuth). + // + // Note that the random number user does not see the values generated + // here directly since reloadMT() will always munge them first, so maybe + // none of all of this matters. In fact, the seed values made here could + // even be extra-special desirable if the Mersenne Twister theory says + // so-- that's why the only change I made is to restrict to odd seeds. + // + + register unsigned int x = ( seed | 1U ) & 0xFFFFFFFFU, *s = state; + register int j; + + for ( left = 0, *s++ = x, j = N; --j; + *s++ = ( x *= 69069U ) & 0xFFFFFFFFU ) + + ; +} + + +unsigned int reloadMT( unsigned int *state, unsigned int *&next, int &left ) +{ + register unsigned int * p0 = state, *p2 = state + 2, *pM = state + M, s0, s1; + register int j; + + if ( left < -1 ) + seedMT( 4357U ); + + left = N - 1, next = state + 1; + + for ( s0 = state[ 0 ], s1 = state[ 1 ], j = N - M + 1; --j; s0 = s1, s1 = *p2++ ) + * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); + + for ( pM = state, j = M; --j; s0 = s1, s1 = *p2++ ) + * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); + + s1 = state[ 0 ], *p0 = *pM ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); + + s1 ^= ( s1 >> 11 ); + + s1 ^= ( s1 << 7 ) & 0x9D2C5680U; + + s1 ^= ( s1 << 15 ) & 0xEFC60000U; + + return ( s1 ^ ( s1 >> 18 ) ); +} + + +unsigned int randomMT( unsigned int *state, unsigned int *&next, int &left ) +{ + unsigned int y; + + if ( --left < 0 ) + return ( reloadMT(state, next, left) ); + + y = *next++; + + y ^= ( y >> 11 ); + + y ^= ( y << 7 ) & 0x9D2C5680U; + + y ^= ( y << 15 ) & 0xEFC60000U; + + return ( y ^ ( y >> 18 ) ); + + // This change made so the value returned is in the same range as what rand() returns + // return(y ^ (y >> 18)) % 32767; +} + +void fillBufferMT( void *buffer, unsigned int bytes, unsigned int *state, unsigned int *&next, int &left ) +{ + unsigned int offset=0; + unsigned int r; + while (bytes-offset>=sizeof(r)) + { + r = randomMT(state, next, left); + memcpy((char*)buffer+offset, &r, sizeof(r)); + offset+=sizeof(r); + } + + r = randomMT(state, next, left); + memcpy((char*)buffer+offset, &r, bytes-offset); +} + +float frandomMT( unsigned int *state, unsigned int *&next, int &left ) +{ + return ( float ) ( ( double ) randomMT(state, next, left) / 4294967296.0 ); +} +RakNetRandom::RakNetRandom() +{ + left=-1; +} +RakNetRandom::~RakNetRandom() +{ +} +void RakNetRandom::SeedMT( unsigned int seed ) +{ + printf("%i\n",seed); + seedMT(seed, state, next, left); +} + +unsigned int RakNetRandom::ReloadMT( void ) +{ + return reloadMT(state, next, left); +} + +unsigned int RakNetRandom::RandomMT( void ) +{ + return randomMT(state, next, left); +} + +float RakNetRandom::FrandomMT( void ) +{ + return frandomMT(state, next, left); +} + +void RakNetRandom::FillBufferMT( void *buffer, unsigned int bytes ) +{ + fillBufferMT(buffer, bytes, state, next, left); +} + +/* +int main(void) +{ +int j; + +// you can seed with any uint32, but the best are odds in 0..(2^32 - 1) + +seedMT(4357U); + +// print the first 2,002 random numbers seven to a line as an example + +for(j=0; j<2002; j++) +RAKNET_DEBUG_PRINTF(" %10lu%s", (unsigned int) randomMT(), (j%7)==6 ? "\n" : ""); + +return(EXIT_SUCCESS); +} + +*/ + diff --git a/project/lib_projects/raknet/jni/RaknetSources/Rand.h b/project/lib_projects/raknet/jni/RaknetSources/Rand.h index c7efab3..508e11a 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Rand.h +++ b/project/lib_projects/raknet/jni/RaknetSources/Rand.h @@ -1,60 +1,60 @@ -/// \file -/// \brief \b [Internal] Random number generator -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - - -#ifndef __RAND_H -#define __RAND_H - -#include "Export.h" - -/// Initialise seed for Random Generator -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -/// \param[in] seed The seed value for the random number generator. -extern void RAK_DLL_EXPORT seedMT( unsigned int seed ); - -/// \internal -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -extern unsigned int RAK_DLL_EXPORT reloadMT( void ); - -/// Gets a random unsigned int -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -/// \return an integer random value. -extern unsigned int RAK_DLL_EXPORT randomMT( void ); - -/// Gets a random float -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -/// \return 0 to 1.0f, inclusive -extern float RAK_DLL_EXPORT frandomMT( void ); - -/// Randomizes a buffer -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -extern void RAK_DLL_EXPORT fillBufferMT( void *buffer, unsigned int bytes ); - -namespace RakNet { - -// Same thing as above functions, but not global -class RAK_DLL_EXPORT RakNetRandom -{ -public: - RakNetRandom(); - ~RakNetRandom(); - void SeedMT( unsigned int seed ); - unsigned int ReloadMT( void ); - unsigned int RandomMT( void ); - float FrandomMT( void ); - void FillBufferMT( void *buffer, unsigned int bytes ); - -protected: - unsigned int state[ 624 + 1 ]; - unsigned int *next; - int left; -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief \b [Internal] Random number generator +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + + +#ifndef __RAND_H +#define __RAND_H + +#include "Export.h" + +/// Initialise seed for Random Generator +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +/// \param[in] seed The seed value for the random number generator. +extern void RAK_DLL_EXPORT seedMT( unsigned int seed ); + +/// \internal +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +extern unsigned int RAK_DLL_EXPORT reloadMT( void ); + +/// Gets a random unsigned int +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +/// \return an integer random value. +extern unsigned int RAK_DLL_EXPORT randomMT( void ); + +/// Gets a random float +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +/// \return 0 to 1.0f, inclusive +extern float RAK_DLL_EXPORT frandomMT( void ); + +/// Randomizes a buffer +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +extern void RAK_DLL_EXPORT fillBufferMT( void *buffer, unsigned int bytes ); + +namespace RakNet { + +// Same thing as above functions, but not global +class RAK_DLL_EXPORT RakNetRandom +{ +public: + RakNetRandom(); + ~RakNetRandom(); + void SeedMT( unsigned int seed ); + unsigned int ReloadMT( void ); + unsigned int RandomMT( void ); + float FrandomMT( void ); + void FillBufferMT( void *buffer, unsigned int bytes ); + +protected: + unsigned int state[ 624 + 1 ]; + unsigned int *next; + int left; +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/ReadyEvent.cpp b/project/lib_projects/raknet/jni/RaknetSources/ReadyEvent.cpp index 277efb4..8e58542 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ReadyEvent.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/ReadyEvent.cpp @@ -1,564 +1,564 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ReadyEvent==1 - -#include "ReadyEvent.h" -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakAssert.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -int RakNet::ReadyEvent::RemoteSystemCompBySystemAddress( const SystemAddress &key, const RemoteSystem &data ) -{ - if (key < data.systemAddress) - return -1; - else if (key==data.systemAddress) - return 0; - else - return 1; -} - -int RakNet::ReadyEvent::ReadyEventNodeComp( const int &key, ReadyEvent::ReadyEventNode * const &data ) -{ - if (key < data->eventId) - return -1; - else if (key==data->eventId) - return 0; - else - return 1; -} - -STATIC_FACTORY_DEFINITIONS(ReadyEvent,ReadyEvent); - -ReadyEvent::ReadyEvent() -{ - channel=0; -} - -ReadyEvent::~ReadyEvent() -{ - Clear(); -} - - -bool ReadyEvent::SetEvent(int eventId, bool isReady) -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists==false) - { - // Totally new event - CreateNewEvent(eventId, isReady); - } - else - { - return SetEventByIndex(eventIndex, isReady); - } - return true; -} -bool ReadyEvent::ForceCompletion(int eventId) -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists==false) - { - // Totally new event - CreateNewEvent(eventId, true); - eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - } - - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - ren->eventStatus=ID_READY_EVENT_FORCE_ALL_SET; - UpdateReadyStatus(eventIndex); - - return true; -} -bool ReadyEvent::DeleteEvent(int eventId) -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - RakNet::OP_DELETE(readyEventNodeList[eventIndex], _FILE_AND_LINE_); - readyEventNodeList.RemoveAtIndex(eventIndex); - return true; - } - return false; -} -bool ReadyEvent::IsEventSet(int eventId) -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return readyEventNodeList[eventIndex]->eventStatus==ID_READY_EVENT_SET || readyEventNodeList[eventIndex]->eventStatus==ID_READY_EVENT_ALL_SET; - } - return false; -} -bool ReadyEvent::IsEventCompletionProcessing(int eventId) const -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - bool anyAllReady=false; - bool allAllReady=true; - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) - return false; - for (unsigned i=0; i < ren->systemList.Size(); i++) - { - if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_ALL_SET) - anyAllReady=true; - else - allAllReady=false; - } - return anyAllReady==true && allAllReady==false; - } - return false; -} -bool ReadyEvent::IsEventCompleted(int eventId) const -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return IsEventCompletedByIndex(eventIndex); - } - return false; -} - -bool ReadyEvent::HasEvent(int eventId) -{ - return readyEventNodeList.HasData(eventId); -} - -unsigned ReadyEvent::GetEventListSize(void) const -{ - return readyEventNodeList.Size(); -} - -int ReadyEvent::GetEventAtIndex(unsigned index) const -{ - return readyEventNodeList[index]->eventId; -} - -bool ReadyEvent::AddToWaitList(int eventId, SystemAddress address) -{ - bool eventExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &eventExists); - if (eventExists==false) - eventIndex=CreateNewEvent(eventId, false); - - // Don't do this, otherwise if we are trying to start a 3 player game, it will not allow the 3rd player to hit ready if the first two players have already done so - //if (IsLocked(eventIndex)) - // return false; // Not in the list, but event is already completed, or is starting to complete, and adding more waiters would fail this. - - unsigned i; - unsigned numAdded=0; - if (address==UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < rakPeerInterface->GetMaximumNumberOfPeers(); i++) - { - SystemAddress internalAddress = rakPeerInterface->GetSystemAddressFromIndex(i); - if (internalAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - numAdded+=AddToWaitListInternal(eventIndex, internalAddress); - } - } - } - else - { - numAdded=AddToWaitListInternal(eventIndex, address); - } - - if (numAdded>0) - UpdateReadyStatus(eventIndex); - return numAdded>0; -} -bool ReadyEvent::RemoveFromWaitList(int eventId, SystemAddress address) -{ - bool eventExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &eventExists); - if (eventExists) - { - if (address==UNASSIGNED_SYSTEM_ADDRESS) - { - // Remove all waiters - readyEventNodeList[eventIndex]->systemList.Clear(false, _FILE_AND_LINE_); - UpdateReadyStatus(eventIndex); - } - else - { - bool systemExists; - unsigned systemIndex = readyEventNodeList[eventIndex]->systemList.GetIndexFromKey(address, &systemExists); - if (systemExists) - { - bool isCompleted = IsEventCompletedByIndex(eventIndex); - readyEventNodeList[eventIndex]->systemList.RemoveAtIndex(systemIndex); - - if (isCompleted==false && IsEventCompletedByIndex(eventIndex)) - PushCompletionPacket(readyEventNodeList[eventIndex]->eventId); - - UpdateReadyStatus(eventIndex); - - return true; - } - } - } - - return false; -} -bool ReadyEvent::IsInWaitList(int eventId, SystemAddress address) -{ - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return readyEventNodeList[readyIndex]->systemList.HasData(address); - } - return false; -} - -unsigned ReadyEvent::GetRemoteWaitListSize(int eventId) const -{ - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return readyEventNodeList[readyIndex]->systemList.Size(); - } - return 0; -} - -SystemAddress ReadyEvent::GetFromWaitListAtIndex(int eventId, unsigned index) const -{ - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return readyEventNodeList[readyIndex]->systemList[index].systemAddress; - } - return UNASSIGNED_SYSTEM_ADDRESS; -} -ReadyEventSystemStatus ReadyEvent::GetReadyStatus(int eventId, SystemAddress address) -{ - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - ReadyEventNode *ren = readyEventNodeList[readyIndex]; - unsigned systemIndex = ren->systemList.GetIndexFromKey(address, &objectExists); - if (objectExists==false) - return RES_NOT_WAITING; - if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_SET) - return RES_READY; - if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_UNSET) - return RES_WAITING; - if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_ALL_SET) - return RES_ALL_READY; - } - - return RES_UNKNOWN_EVENT; -} -void ReadyEvent::SetSendChannel(unsigned char newChannel) -{ - channel=newChannel; -} -PluginReceiveResult ReadyEvent::OnReceive(Packet *packet) -{ - unsigned char packetIdentifier; - packetIdentifier = ( unsigned char ) packet->data[ 0 ]; - -// bool doPrint = packet->systemAddress.GetPort()==60002 || rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).GetPort()==60002; - - switch (packetIdentifier) - { - case ID_READY_EVENT_UNSET: - case ID_READY_EVENT_SET: - case ID_READY_EVENT_ALL_SET: -// if (doPrint) {if (packet->systemAddress.GetPort()==60002) RAKNET_DEBUG_PRINTF("FROM 60002: "); else if (rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port==60002) RAKNET_DEBUG_PRINTF("TO 60002: "); RAKNET_DEBUG_PRINTF("ID_READY_EVENT_SET\n");} - OnReadyEventPacketUpdate(packet); - return RR_CONTINUE_PROCESSING; - case ID_READY_EVENT_FORCE_ALL_SET: - OnReadyEventForceAllSet(packet); - return RR_CONTINUE_PROCESSING; - case ID_READY_EVENT_QUERY: -// if (doPrint) {if (packet->systemAddress.GetPort()==60002) RAKNET_DEBUG_PRINTF("FROM 60002: "); else if (rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port==60002) RAKNET_DEBUG_PRINTF("TO 60002: "); RAKNET_DEBUG_PRINTF("ID_READY_EVENT_QUERY\n");} - OnReadyEventQuery(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - return RR_CONTINUE_PROCESSING; -} -bool ReadyEvent::AddToWaitListInternal(unsigned eventIndex, SystemAddress address) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - bool objectExists; - unsigned systemIndex = ren->systemList.GetIndexFromKey(address, &objectExists); - if (objectExists==false) - { - RemoteSystem rs; - rs.lastReceivedStatus=ID_READY_EVENT_UNSET; - rs.lastSentStatus=ID_READY_EVENT_UNSET; - rs.systemAddress=address; - ren->systemList.InsertAtIndex(rs,systemIndex, _FILE_AND_LINE_); - - SendReadyStateQuery(ren->eventId, address); - return true; - } - return false; -} -void ReadyEvent::OnReadyEventForceAllSet(Packet *packet) -{ - RakNet::BitStream incomingBitStream(packet->data, packet->length, false); - incomingBitStream.IgnoreBits(8); - int eventId; - incomingBitStream.Read(eventId); - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - ReadyEventNode *ren = readyEventNodeList[readyIndex]; - if (ren->eventStatus!=ID_READY_EVENT_FORCE_ALL_SET) - { - ren->eventStatus=ID_READY_EVENT_FORCE_ALL_SET; - PushCompletionPacket(ren->eventId); - } - } -} -void ReadyEvent::OnReadyEventPacketUpdate(Packet *packet) -{ - RakNet::BitStream incomingBitStream(packet->data, packet->length, false); - incomingBitStream.IgnoreBits(8); - int eventId; - incomingBitStream.Read(eventId); - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - ReadyEventNode *ren = readyEventNodeList[readyIndex]; - bool systemExists; - unsigned systemIndex = ren->systemList.GetIndexFromKey(packet->systemAddress, &systemExists); - if (systemExists) - { - // Just return if no change - if (ren->systemList[systemIndex].lastReceivedStatus==packet->data[0]) - return; - - bool wasCompleted = IsEventCompletedByIndex(readyIndex); - ren->systemList[systemIndex].lastReceivedStatus=packet->data[0]; - // If forced all set, doesn't matter what the new packet is - if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) - return; - UpdateReadyStatus(readyIndex); - if (wasCompleted==false && IsEventCompletedByIndex(readyIndex)) - PushCompletionPacket(readyIndex); - } - } -} -void ReadyEvent::OnReadyEventQuery(Packet *packet) -{ - RakNet::BitStream incomingBitStream(packet->data, packet->length, false); - incomingBitStream.IgnoreBits(8); - int eventId; - incomingBitStream.Read(eventId); - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - unsigned systemIndex = readyEventNodeList[readyIndex]->systemList.GetIndexFromKey(packet->systemAddress,&objectExists); - // Force the non-default send, because our initial send may have arrived at a system that didn't yet create the ready event - if (objectExists) - SendReadyUpdate(readyIndex, systemIndex, true); - } -} -void ReadyEvent::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) systemAddress; - (void) rakNetGUID; - (void) lostConnectionReason; - - RemoveFromAllLists(systemAddress); -} -void ReadyEvent::OnRakPeerShutdown(void) -{ - Clear(); -} - -bool ReadyEvent::SetEventByIndex(int eventIndex, bool isReady) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - if ((ren->eventStatus==ID_READY_EVENT_ALL_SET || ren->eventStatus==ID_READY_EVENT_SET) && isReady==true) - return true; // Success - no change - if (ren->eventStatus==ID_READY_EVENT_UNSET && isReady==false) - return true; // Success - no change - if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) - return true; // Can't change - - if (isReady) - ren->eventStatus=ID_READY_EVENT_SET; - else - ren->eventStatus=ID_READY_EVENT_UNSET; - - UpdateReadyStatus(eventIndex); - - // Check if now completed, and if so, tell the user about it - if (IsEventCompletedByIndex(eventIndex)) - { - PushCompletionPacket(ren->eventId); - } - - return true; -} - -bool ReadyEvent::IsEventCompletedByIndex(unsigned eventIndex) const -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - unsigned i; - if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) - return true; - if (ren->eventStatus!=ID_READY_EVENT_ALL_SET) - return false; - for (i=0; i < ren->systemList.Size(); i++) - if (ren->systemList[i].lastReceivedStatus!=ID_READY_EVENT_ALL_SET) - return false; - return true; -} - -void ReadyEvent::Clear(void) -{ - unsigned i; - for (i=0; i < readyEventNodeList.Size(); i++) - { - RakNet::OP_DELETE(readyEventNodeList[i], _FILE_AND_LINE_); - } - readyEventNodeList.Clear(false, _FILE_AND_LINE_); -} - -unsigned ReadyEvent::CreateNewEvent(int eventId, bool isReady) -{ - ReadyEventNode *ren = RakNet::OP_NEW( _FILE_AND_LINE_ ); - ren->eventId=eventId; - if (isReady==false) - ren->eventStatus=ID_READY_EVENT_UNSET; - else - ren->eventStatus=ID_READY_EVENT_SET; - return readyEventNodeList.Insert(eventId, ren, true, _FILE_AND_LINE_); -} -void ReadyEvent::UpdateReadyStatus(unsigned eventIndex) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - bool anyUnset; - unsigned i; - if (ren->eventStatus==ID_READY_EVENT_SET) - { - // If you are set, and no other systems are ID_READY_EVENT_UNSET, then change your status to ID_READY_EVENT_ALL_SET - anyUnset=false; - for (i=0; i < ren->systemList.Size(); i++) - { - if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_UNSET) - { - anyUnset=true; - break; - } - } - if (anyUnset==false) - { - ren->eventStatus=ID_READY_EVENT_ALL_SET; - } - } - else if (ren->eventStatus==ID_READY_EVENT_ALL_SET) - { - // If you are all set, and any systems are ID_READY_EVENT_UNSET, then change your status to ID_READY_EVENT_SET - anyUnset=false; - for (i=0; i < ren->systemList.Size(); i++) - { - if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_UNSET) - { - anyUnset=true; - break; - } - } - if (anyUnset==true) - { - ren->eventStatus=ID_READY_EVENT_SET; - } - } - BroadcastReadyUpdate(eventIndex, false); -} -void ReadyEvent::SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - RakNet::BitStream bs; - // I do this rather than write true or false, so users that do not use BitStreams can still read the data - if ((ren->eventStatus!=ren->systemList[systemIndex].lastSentStatus) || - (forceIfNotDefault && ren->eventStatus!=ID_READY_EVENT_UNSET)) - { - bs.Write(ren->eventStatus); - bs.Write(ren->eventId); - SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, ren->systemList[systemIndex].systemAddress, false); - - ren->systemList[systemIndex].lastSentStatus=ren->eventStatus; - } - -} -void ReadyEvent::BroadcastReadyUpdate(unsigned eventIndex, bool forceIfNotDefault) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - unsigned systemIndex; - for (systemIndex=0; systemIndex < ren->systemList.Size(); systemIndex++) - { - SendReadyUpdate(eventIndex, systemIndex, forceIfNotDefault); - } -} -void ReadyEvent::SendReadyStateQuery(unsigned eventId, SystemAddress address) -{ - RakNet::BitStream bs; - bs.Write((MessageID)ID_READY_EVENT_QUERY); - bs.Write(eventId); - SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, address, false); -} -void ReadyEvent::RemoveFromAllLists(SystemAddress address) -{ - unsigned eventIndex; - for (eventIndex=0; eventIndex < readyEventNodeList.Size(); eventIndex++) - { - bool isCompleted = IsEventCompletedByIndex(eventIndex); - bool systemExists; - unsigned systemIndex; - - systemIndex = readyEventNodeList[eventIndex]->systemList.GetIndexFromKey(address, &systemExists); - if (systemExists) - readyEventNodeList[eventIndex]->systemList.RemoveAtIndex(systemIndex); - - UpdateReadyStatus(eventIndex); - - if (isCompleted==false && IsEventCompletedByIndex(eventIndex)) - PushCompletionPacket(readyEventNodeList[eventIndex]->eventId); - } -} -void ReadyEvent::PushCompletionPacket(unsigned eventId) -{ - (void) eventId; - // Not necessary - /* - // Pass a packet to the user that we are now completed, as setting ourselves to signaled was the last thing being waited on - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(int)); - RakNet::BitStream bs(p->data, sizeof(MessageID)+sizeof(int), false); - bs.SetWriteOffset(0); - bs.Write((MessageID)ID_READY_EVENT_ALL_SET); - bs.Write(eventId); - rakPeerInterface->PushBackPacket(p, false); - */ -} -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ReadyEvent==1 + +#include "ReadyEvent.h" +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakAssert.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +int RakNet::ReadyEvent::RemoteSystemCompBySystemAddress( const SystemAddress &key, const RemoteSystem &data ) +{ + if (key < data.systemAddress) + return -1; + else if (key==data.systemAddress) + return 0; + else + return 1; +} + +int RakNet::ReadyEvent::ReadyEventNodeComp( const int &key, ReadyEvent::ReadyEventNode * const &data ) +{ + if (key < data->eventId) + return -1; + else if (key==data->eventId) + return 0; + else + return 1; +} + +STATIC_FACTORY_DEFINITIONS(ReadyEvent,ReadyEvent); + +ReadyEvent::ReadyEvent() +{ + channel=0; +} + +ReadyEvent::~ReadyEvent() +{ + Clear(); +} + + +bool ReadyEvent::SetEvent(int eventId, bool isReady) +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists==false) + { + // Totally new event + CreateNewEvent(eventId, isReady); + } + else + { + return SetEventByIndex(eventIndex, isReady); + } + return true; +} +bool ReadyEvent::ForceCompletion(int eventId) +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists==false) + { + // Totally new event + CreateNewEvent(eventId, true); + eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + } + + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + ren->eventStatus=ID_READY_EVENT_FORCE_ALL_SET; + UpdateReadyStatus(eventIndex); + + return true; +} +bool ReadyEvent::DeleteEvent(int eventId) +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + RakNet::OP_DELETE(readyEventNodeList[eventIndex], _FILE_AND_LINE_); + readyEventNodeList.RemoveAtIndex(eventIndex); + return true; + } + return false; +} +bool ReadyEvent::IsEventSet(int eventId) +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return readyEventNodeList[eventIndex]->eventStatus==ID_READY_EVENT_SET || readyEventNodeList[eventIndex]->eventStatus==ID_READY_EVENT_ALL_SET; + } + return false; +} +bool ReadyEvent::IsEventCompletionProcessing(int eventId) const +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + bool anyAllReady=false; + bool allAllReady=true; + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) + return false; + for (unsigned i=0; i < ren->systemList.Size(); i++) + { + if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_ALL_SET) + anyAllReady=true; + else + allAllReady=false; + } + return anyAllReady==true && allAllReady==false; + } + return false; +} +bool ReadyEvent::IsEventCompleted(int eventId) const +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return IsEventCompletedByIndex(eventIndex); + } + return false; +} + +bool ReadyEvent::HasEvent(int eventId) +{ + return readyEventNodeList.HasData(eventId); +} + +unsigned ReadyEvent::GetEventListSize(void) const +{ + return readyEventNodeList.Size(); +} + +int ReadyEvent::GetEventAtIndex(unsigned index) const +{ + return readyEventNodeList[index]->eventId; +} + +bool ReadyEvent::AddToWaitList(int eventId, SystemAddress address) +{ + bool eventExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &eventExists); + if (eventExists==false) + eventIndex=CreateNewEvent(eventId, false); + + // Don't do this, otherwise if we are trying to start a 3 player game, it will not allow the 3rd player to hit ready if the first two players have already done so + //if (IsLocked(eventIndex)) + // return false; // Not in the list, but event is already completed, or is starting to complete, and adding more waiters would fail this. + + unsigned i; + unsigned numAdded=0; + if (address==UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < rakPeerInterface->GetMaximumNumberOfPeers(); i++) + { + SystemAddress internalAddress = rakPeerInterface->GetSystemAddressFromIndex(i); + if (internalAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + numAdded+=AddToWaitListInternal(eventIndex, internalAddress); + } + } + } + else + { + numAdded=AddToWaitListInternal(eventIndex, address); + } + + if (numAdded>0) + UpdateReadyStatus(eventIndex); + return numAdded>0; +} +bool ReadyEvent::RemoveFromWaitList(int eventId, SystemAddress address) +{ + bool eventExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &eventExists); + if (eventExists) + { + if (address==UNASSIGNED_SYSTEM_ADDRESS) + { + // Remove all waiters + readyEventNodeList[eventIndex]->systemList.Clear(false, _FILE_AND_LINE_); + UpdateReadyStatus(eventIndex); + } + else + { + bool systemExists; + unsigned systemIndex = readyEventNodeList[eventIndex]->systemList.GetIndexFromKey(address, &systemExists); + if (systemExists) + { + bool isCompleted = IsEventCompletedByIndex(eventIndex); + readyEventNodeList[eventIndex]->systemList.RemoveAtIndex(systemIndex); + + if (isCompleted==false && IsEventCompletedByIndex(eventIndex)) + PushCompletionPacket(readyEventNodeList[eventIndex]->eventId); + + UpdateReadyStatus(eventIndex); + + return true; + } + } + } + + return false; +} +bool ReadyEvent::IsInWaitList(int eventId, SystemAddress address) +{ + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return readyEventNodeList[readyIndex]->systemList.HasData(address); + } + return false; +} + +unsigned ReadyEvent::GetRemoteWaitListSize(int eventId) const +{ + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return readyEventNodeList[readyIndex]->systemList.Size(); + } + return 0; +} + +SystemAddress ReadyEvent::GetFromWaitListAtIndex(int eventId, unsigned index) const +{ + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return readyEventNodeList[readyIndex]->systemList[index].systemAddress; + } + return UNASSIGNED_SYSTEM_ADDRESS; +} +ReadyEventSystemStatus ReadyEvent::GetReadyStatus(int eventId, SystemAddress address) +{ + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + ReadyEventNode *ren = readyEventNodeList[readyIndex]; + unsigned systemIndex = ren->systemList.GetIndexFromKey(address, &objectExists); + if (objectExists==false) + return RES_NOT_WAITING; + if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_SET) + return RES_READY; + if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_UNSET) + return RES_WAITING; + if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_ALL_SET) + return RES_ALL_READY; + } + + return RES_UNKNOWN_EVENT; +} +void ReadyEvent::SetSendChannel(unsigned char newChannel) +{ + channel=newChannel; +} +PluginReceiveResult ReadyEvent::OnReceive(Packet *packet) +{ + unsigned char packetIdentifier; + packetIdentifier = ( unsigned char ) packet->data[ 0 ]; + +// bool doPrint = packet->systemAddress.GetPort()==60002 || rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).GetPort()==60002; + + switch (packetIdentifier) + { + case ID_READY_EVENT_UNSET: + case ID_READY_EVENT_SET: + case ID_READY_EVENT_ALL_SET: +// if (doPrint) {if (packet->systemAddress.GetPort()==60002) RAKNET_DEBUG_PRINTF("FROM 60002: "); else if (rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port==60002) RAKNET_DEBUG_PRINTF("TO 60002: "); RAKNET_DEBUG_PRINTF("ID_READY_EVENT_SET\n");} + OnReadyEventPacketUpdate(packet); + return RR_CONTINUE_PROCESSING; + case ID_READY_EVENT_FORCE_ALL_SET: + OnReadyEventForceAllSet(packet); + return RR_CONTINUE_PROCESSING; + case ID_READY_EVENT_QUERY: +// if (doPrint) {if (packet->systemAddress.GetPort()==60002) RAKNET_DEBUG_PRINTF("FROM 60002: "); else if (rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port==60002) RAKNET_DEBUG_PRINTF("TO 60002: "); RAKNET_DEBUG_PRINTF("ID_READY_EVENT_QUERY\n");} + OnReadyEventQuery(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + return RR_CONTINUE_PROCESSING; +} +bool ReadyEvent::AddToWaitListInternal(unsigned eventIndex, SystemAddress address) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + bool objectExists; + unsigned systemIndex = ren->systemList.GetIndexFromKey(address, &objectExists); + if (objectExists==false) + { + RemoteSystem rs; + rs.lastReceivedStatus=ID_READY_EVENT_UNSET; + rs.lastSentStatus=ID_READY_EVENT_UNSET; + rs.systemAddress=address; + ren->systemList.InsertAtIndex(rs,systemIndex, _FILE_AND_LINE_); + + SendReadyStateQuery(ren->eventId, address); + return true; + } + return false; +} +void ReadyEvent::OnReadyEventForceAllSet(Packet *packet) +{ + RakNet::BitStream incomingBitStream(packet->data, packet->length, false); + incomingBitStream.IgnoreBits(8); + int eventId; + incomingBitStream.Read(eventId); + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + ReadyEventNode *ren = readyEventNodeList[readyIndex]; + if (ren->eventStatus!=ID_READY_EVENT_FORCE_ALL_SET) + { + ren->eventStatus=ID_READY_EVENT_FORCE_ALL_SET; + PushCompletionPacket(ren->eventId); + } + } +} +void ReadyEvent::OnReadyEventPacketUpdate(Packet *packet) +{ + RakNet::BitStream incomingBitStream(packet->data, packet->length, false); + incomingBitStream.IgnoreBits(8); + int eventId; + incomingBitStream.Read(eventId); + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + ReadyEventNode *ren = readyEventNodeList[readyIndex]; + bool systemExists; + unsigned systemIndex = ren->systemList.GetIndexFromKey(packet->systemAddress, &systemExists); + if (systemExists) + { + // Just return if no change + if (ren->systemList[systemIndex].lastReceivedStatus==packet->data[0]) + return; + + bool wasCompleted = IsEventCompletedByIndex(readyIndex); + ren->systemList[systemIndex].lastReceivedStatus=packet->data[0]; + // If forced all set, doesn't matter what the new packet is + if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) + return; + UpdateReadyStatus(readyIndex); + if (wasCompleted==false && IsEventCompletedByIndex(readyIndex)) + PushCompletionPacket(readyIndex); + } + } +} +void ReadyEvent::OnReadyEventQuery(Packet *packet) +{ + RakNet::BitStream incomingBitStream(packet->data, packet->length, false); + incomingBitStream.IgnoreBits(8); + int eventId; + incomingBitStream.Read(eventId); + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + unsigned systemIndex = readyEventNodeList[readyIndex]->systemList.GetIndexFromKey(packet->systemAddress,&objectExists); + // Force the non-default send, because our initial send may have arrived at a system that didn't yet create the ready event + if (objectExists) + SendReadyUpdate(readyIndex, systemIndex, true); + } +} +void ReadyEvent::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) systemAddress; + (void) rakNetGUID; + (void) lostConnectionReason; + + RemoveFromAllLists(systemAddress); +} +void ReadyEvent::OnRakPeerShutdown(void) +{ + Clear(); +} + +bool ReadyEvent::SetEventByIndex(int eventIndex, bool isReady) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + if ((ren->eventStatus==ID_READY_EVENT_ALL_SET || ren->eventStatus==ID_READY_EVENT_SET) && isReady==true) + return true; // Success - no change + if (ren->eventStatus==ID_READY_EVENT_UNSET && isReady==false) + return true; // Success - no change + if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) + return true; // Can't change + + if (isReady) + ren->eventStatus=ID_READY_EVENT_SET; + else + ren->eventStatus=ID_READY_EVENT_UNSET; + + UpdateReadyStatus(eventIndex); + + // Check if now completed, and if so, tell the user about it + if (IsEventCompletedByIndex(eventIndex)) + { + PushCompletionPacket(ren->eventId); + } + + return true; +} + +bool ReadyEvent::IsEventCompletedByIndex(unsigned eventIndex) const +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + unsigned i; + if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) + return true; + if (ren->eventStatus!=ID_READY_EVENT_ALL_SET) + return false; + for (i=0; i < ren->systemList.Size(); i++) + if (ren->systemList[i].lastReceivedStatus!=ID_READY_EVENT_ALL_SET) + return false; + return true; +} + +void ReadyEvent::Clear(void) +{ + unsigned i; + for (i=0; i < readyEventNodeList.Size(); i++) + { + RakNet::OP_DELETE(readyEventNodeList[i], _FILE_AND_LINE_); + } + readyEventNodeList.Clear(false, _FILE_AND_LINE_); +} + +unsigned ReadyEvent::CreateNewEvent(int eventId, bool isReady) +{ + ReadyEventNode *ren = RakNet::OP_NEW( _FILE_AND_LINE_ ); + ren->eventId=eventId; + if (isReady==false) + ren->eventStatus=ID_READY_EVENT_UNSET; + else + ren->eventStatus=ID_READY_EVENT_SET; + return readyEventNodeList.Insert(eventId, ren, true, _FILE_AND_LINE_); +} +void ReadyEvent::UpdateReadyStatus(unsigned eventIndex) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + bool anyUnset; + unsigned i; + if (ren->eventStatus==ID_READY_EVENT_SET) + { + // If you are set, and no other systems are ID_READY_EVENT_UNSET, then change your status to ID_READY_EVENT_ALL_SET + anyUnset=false; + for (i=0; i < ren->systemList.Size(); i++) + { + if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_UNSET) + { + anyUnset=true; + break; + } + } + if (anyUnset==false) + { + ren->eventStatus=ID_READY_EVENT_ALL_SET; + } + } + else if (ren->eventStatus==ID_READY_EVENT_ALL_SET) + { + // If you are all set, and any systems are ID_READY_EVENT_UNSET, then change your status to ID_READY_EVENT_SET + anyUnset=false; + for (i=0; i < ren->systemList.Size(); i++) + { + if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_UNSET) + { + anyUnset=true; + break; + } + } + if (anyUnset==true) + { + ren->eventStatus=ID_READY_EVENT_SET; + } + } + BroadcastReadyUpdate(eventIndex, false); +} +void ReadyEvent::SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + RakNet::BitStream bs; + // I do this rather than write true or false, so users that do not use BitStreams can still read the data + if ((ren->eventStatus!=ren->systemList[systemIndex].lastSentStatus) || + (forceIfNotDefault && ren->eventStatus!=ID_READY_EVENT_UNSET)) + { + bs.Write(ren->eventStatus); + bs.Write(ren->eventId); + SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, ren->systemList[systemIndex].systemAddress, false); + + ren->systemList[systemIndex].lastSentStatus=ren->eventStatus; + } + +} +void ReadyEvent::BroadcastReadyUpdate(unsigned eventIndex, bool forceIfNotDefault) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + unsigned systemIndex; + for (systemIndex=0; systemIndex < ren->systemList.Size(); systemIndex++) + { + SendReadyUpdate(eventIndex, systemIndex, forceIfNotDefault); + } +} +void ReadyEvent::SendReadyStateQuery(unsigned eventId, SystemAddress address) +{ + RakNet::BitStream bs; + bs.Write((MessageID)ID_READY_EVENT_QUERY); + bs.Write(eventId); + SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, address, false); +} +void ReadyEvent::RemoveFromAllLists(SystemAddress address) +{ + unsigned eventIndex; + for (eventIndex=0; eventIndex < readyEventNodeList.Size(); eventIndex++) + { + bool isCompleted = IsEventCompletedByIndex(eventIndex); + bool systemExists; + unsigned systemIndex; + + systemIndex = readyEventNodeList[eventIndex]->systemList.GetIndexFromKey(address, &systemExists); + if (systemExists) + readyEventNodeList[eventIndex]->systemList.RemoveAtIndex(systemIndex); + + UpdateReadyStatus(eventIndex); + + if (isCompleted==false && IsEventCompletedByIndex(eventIndex)) + PushCompletionPacket(readyEventNodeList[eventIndex]->eventId); + } +} +void ReadyEvent::PushCompletionPacket(unsigned eventId) +{ + (void) eventId; + // Not necessary + /* + // Pass a packet to the user that we are now completed, as setting ourselves to signaled was the last thing being waited on + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(int)); + RakNet::BitStream bs(p->data, sizeof(MessageID)+sizeof(int), false); + bs.SetWriteOffset(0); + bs.Write((MessageID)ID_READY_EVENT_ALL_SET); + bs.Write(eventId); + rakPeerInterface->PushBackPacket(p, false); + */ +} +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/ReadyEvent.h b/project/lib_projects/raknet/jni/RaknetSources/ReadyEvent.h index a6d516d..3b08dfc 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ReadyEvent.h +++ b/project/lib_projects/raknet/jni/RaknetSources/ReadyEvent.h @@ -1,234 +1,234 @@ -/// \file -/// \brief Ready event plugin. This enables a set of systems to create a signal event, set this signal as ready or unready, and to trigger the event when all systems are ready -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ReadyEvent==1 - -#ifndef __READY_EVENT_H -#define __READY_EVENT_H - -#include "PluginInterface2.h" -#include "DS_OrderedList.h" - -namespace RakNet { - -class RakPeerInterface; - -/// \defgroup READY_EVENT_GROUP ReadyEvent -/// \brief Peer to peer synchronized ready and unready events -/// \details -/// \ingroup PLUGINS_GROUP - -/// \ingroup READY_EVENT_GROUP -/// Returns the status of a remote system when querying with ReadyEvent::GetReadyStatus -enum ReadyEventSystemStatus -{ - /// ----------- Normal states --------------- - /// The remote system is not in the wait list, and we have never gotten a ready or complete message from it. - /// This is the default state for valid events - RES_NOT_WAITING, - /// We are waiting for this remote system to call SetEvent(thisEvent,true). - RES_WAITING, - /// The remote system called SetEvent(thisEvent,true), but it still waiting for other systems before completing the ReadyEvent. - RES_READY, - /// The remote system called SetEvent(thisEvent,true), and is no longer waiting for any other systems. - /// This remote system has completed the ReadyEvent - RES_ALL_READY, - - /// Error code, we couldn't look up the system because the event was unknown - RES_UNKNOWN_EVENT, -}; - -/// \brief Peer to peer synchronized ready and unready events -/// \details For peer to peer networks in a fully connected mesh.
-/// Solves the problem of how to tell if all peers, relative to all other peers, are in a certain ready state.
-/// For example, if A is connected to B and C, A may see that B and C are ready, but does not know if B is ready to C, or vice-versa.
-/// This plugin uses two stages to solve that problem, first, everyone I know about is ready. Second, everyone I know about is ready to everyone they know about.
-/// The user will get ID_READY_EVENT_SET and ID_READY_EVENT_UNSET as the signal flag is set or unset
-/// The user will get ID_READY_EVENT_ALL_SET when all systems are done waiting for all other systems, in which case the event is considered complete, and no longer tracked.
-/// \sa FullyConnectedMesh2 -/// \ingroup READY_EVENT_GROUP -class ReadyEvent : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(ReadyEvent) - - // Constructor - ReadyEvent(); - - // Destructor - virtual ~ReadyEvent(); - - // -------------------------------------------------------------------------------------------- - // User functions - // -------------------------------------------------------------------------------------------- - /// Sets or updates the initial ready state for our local system. - /// If eventId is an unknown event the event is created. - /// If eventId was previously used and you want to reuse it, call DeleteEvent first, or else you will keep the same event signals from before - /// Systems previously or later added through AddToWaitList() with the same \a eventId when isReady=true will get ID_READY_EVENT_SET - /// Systems previously added through AddToWaitList with the same \a eventId will get ID_READY_EVENT_UNSET - /// For both ID_READY_EVENT_SET and ID_READY_EVENT_UNSET, eventId is encoded in bytes 1 through 1+sizeof(int) - /// \param[in] eventId A user-defined identifier to wait on. This can be a sequence counter, an event identifier, or anything else you want. - /// \param[in] isReady True to signal we are ready to proceed with this event, false to unsignal - /// \return True on success. False (failure) on unknown eventId - bool SetEvent(int eventId, bool isReady); - - /// When systems can call SetEvent() with isReady==false, it is possible for one system to return true from IsEventCompleted() while the other systems return false - /// This can occur if a system SetEvent() with isReady==false while the completion message is still being transmitted. - /// If your game has the situation where some action should be taken on all systems when IsEventCompleted() is true for any system, then call ForceCompletion() when the action begins. - /// This will force all systems to return true from IsEventCompleted(). - /// \param[in] eventId A user-defined identifier to immediately set as completed - bool ForceCompletion(int eventId); - - /// Deletes an event. We will no longer wait for this event, and any systems that we know have set the event will be forgotten. - /// Call this to clear memory when events are completed and you know you will never need them again. - /// \param[in] eventId A user-defined identifier - /// \return True on success. False (failure) on unknown eventId - bool DeleteEvent(int eventId); - - /// Returns what was passed to SetEvent() - /// \return The value of isReady passed to SetEvent(). Also returns false on unknown event. - bool IsEventSet(int eventId); - - /// Returns if the event is about to be ready and we are negotiating the final packets. - /// This will usually only be true for a very short time, after which IsEventCompleted should return true. - /// While this is true you cannot add to the wait list, or SetEvent() isReady to false anymore. - /// \param[in] eventId A user-defined identifier - /// \return True if any other system has completed processing. Will always be true if IsEventCompleted() is true - bool IsEventCompletionProcessing(int eventId) const; - - /// Returns if the wait list is a subset of the completion list. - /// Call this after all systems you want to wait for have been added with AddToWaitList - /// If you are waiting for a specific number of systems (such as players later connecting), also check GetRemoteWaitListSize(eventId) to be equal to 1 less than the total number of participants. - /// \param[in] eventId A user-defined identifier - /// \return True on completion. False (failure) on unknown eventId, or the set is not completed. - bool IsEventCompleted(int eventId) const; - - /// Returns if this is a known event. - /// Events may be known even if we never ourselves referenced them with SetEvent, because other systems created them via ID_READY_EVENT_SET. - /// \param[in] eventId A user-defined identifier - /// \return true if we have this event, false otherwise - bool HasEvent(int eventId); - - /// Returns the total number of events stored in the system. - /// \return The total number of events stored in the system. - unsigned GetEventListSize(void) const; - - /// Returns the event ID stored at a particular index. EventIDs are stored sorted from least to greatest. - /// \param[in] index Index into the array, from 0 to GetEventListSize() - /// \return The event ID stored at a particular index - int GetEventAtIndex(unsigned index) const; - - /// Adds a system to wait for to signal an event before considering the event complete and returning ID_READY_EVENT_ALL_SET. - /// As we add systems, if this event was previously set to true with SetEvent, these systems will get ID_READY_EVENT_SET. - /// As these systems disconnect (directly or indirectly through the router) they are removed. - /// \note If the event completion process has already started, you cannot add more systems, as this would cause the completion process to fail - /// \param[in] eventId A user-defined number previously passed to SetEvent that has not yet completed - /// \param[in] addressArray An address to wait for event replies from. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. Until all systems in this list have called SetEvent with this ID and true, and have this system in the list, we won't get ID_READY_EVENT_COMPLETE - /// \return True on success, false on unknown eventId (this should be considered an error), or if the completion process has already started. - bool AddToWaitList(int eventId, SystemAddress address); - - /// Removes systems from the wait list, which should have been previously added with AddToWaitList - /// \note Systems that directly or indirectly disconnect from us are automatically removed from the wait list - /// \param[in] address The system to remove from the wait list. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. - /// \return True on success, false on unknown eventId (this should be considered an error) - bool RemoveFromWaitList(int eventId, SystemAddress address); - - /// Returns if a particular system is waiting on a particular event. - /// \param[in] eventId A user-defined identifier - /// \param[in] The address of the system we are checking up on - /// \return True if this system is waiting on this event, false otherwise. - bool IsInWaitList(int eventId, SystemAddress address); - - /// Returns the total number of systems we are waiting on for this event. - /// Does not include yourself - /// \param[in] eventId A user-defined identifier - /// \return The total number of systems we are waiting on for this event. - unsigned GetRemoteWaitListSize(int eventId) const; - - /// Returns the system address of a system at a particular index, for this event. - /// \param[in] eventId A user-defined identifier - /// \param[in] index Index into the array, from 0 to GetWaitListSize() - /// \return The system address of a system at a particular index, for this event. - SystemAddress GetFromWaitListAtIndex(int eventId, unsigned index) const; - - /// For a remote system, find out what their ready status is (waiting, signaled, complete). - /// \param[in] eventId A user-defined identifier - /// \param[in] address Which system we are checking up on - /// \return The status of this system, for this particular event. \sa ReadyEventSystemStatus - ReadyEventSystemStatus GetReadyStatus(int eventId, SystemAddress address); - - /// This channel will be used for all RakPeer::Send calls - /// \param[in] newChannel The channel to use for internal RakPeer::Send calls from this system. Defaults to 0. - void SetSendChannel(unsigned char newChannel); - - // ---------------------------- ALL INTERNAL AFTER HERE ---------------------------- - /// \internal - /// Status of a remote system - struct RemoteSystem - { - MessageID lastSentStatus, lastReceivedStatus; - SystemAddress systemAddress; - }; - static int RemoteSystemCompBySystemAddress( const SystemAddress &key, const RemoteSystem &data ); - /// \internal - /// An event, with a set of systems we are waiting for, a set of systems that are signaled, and a set of systems with completed events - struct ReadyEventNode - { - int eventId; // Sorted on this - MessageID eventStatus; - DataStructures::OrderedList systemList; - }; - static int ReadyEventNodeComp( const int &key, ReadyEvent::ReadyEventNode * const &data ); - - -protected: - // -------------------------------------------------------------------------------------------- - // Packet handling functions - // -------------------------------------------------------------------------------------------- - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnRakPeerShutdown(void); - - void Clear(void); - /* - bool AnyWaitersCompleted(unsigned eventIndex) const; - bool AllWaitersCompleted(unsigned eventIndex) const; - bool AllWaitersReady(unsigned eventIndex) const; - void SendAllReady(unsigned eventId, SystemAddress address); - void BroadcastAllReady(unsigned eventIndex); - void SendReadyStateQuery(unsigned eventId, SystemAddress address); - void BroadcastReadyUpdate(unsigned eventIndex); - bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); - bool IsLocked(unsigned eventIndex) const; - bool IsAllReadyByIndex(unsigned eventIndex) const; - */ - - void SendReadyStateQuery(unsigned eventId, SystemAddress address); - void SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault); - void BroadcastReadyUpdate(unsigned eventIndex, bool forceIfNotDefault); - void RemoveFromAllLists(SystemAddress address); - void OnReadyEventQuery(Packet *packet); - void PushCompletionPacket(unsigned eventId); - bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); - void OnReadyEventForceAllSet(Packet *packet); - void OnReadyEventPacketUpdate(Packet *packet); - void UpdateReadyStatus(unsigned eventIndex); - bool IsEventCompletedByIndex(unsigned eventIndex) const; - unsigned CreateNewEvent(int eventId, bool isReady); - bool SetEventByIndex(int eventIndex, bool isReady); - - DataStructures::OrderedList readyEventNodeList; - unsigned char channel; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Ready event plugin. This enables a set of systems to create a signal event, set this signal as ready or unready, and to trigger the event when all systems are ready +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ReadyEvent==1 + +#ifndef __READY_EVENT_H +#define __READY_EVENT_H + +#include "PluginInterface2.h" +#include "DS_OrderedList.h" + +namespace RakNet { + +class RakPeerInterface; + +/// \defgroup READY_EVENT_GROUP ReadyEvent +/// \brief Peer to peer synchronized ready and unready events +/// \details +/// \ingroup PLUGINS_GROUP + +/// \ingroup READY_EVENT_GROUP +/// Returns the status of a remote system when querying with ReadyEvent::GetReadyStatus +enum ReadyEventSystemStatus +{ + /// ----------- Normal states --------------- + /// The remote system is not in the wait list, and we have never gotten a ready or complete message from it. + /// This is the default state for valid events + RES_NOT_WAITING, + /// We are waiting for this remote system to call SetEvent(thisEvent,true). + RES_WAITING, + /// The remote system called SetEvent(thisEvent,true), but it still waiting for other systems before completing the ReadyEvent. + RES_READY, + /// The remote system called SetEvent(thisEvent,true), and is no longer waiting for any other systems. + /// This remote system has completed the ReadyEvent + RES_ALL_READY, + + /// Error code, we couldn't look up the system because the event was unknown + RES_UNKNOWN_EVENT, +}; + +/// \brief Peer to peer synchronized ready and unready events +/// \details For peer to peer networks in a fully connected mesh.
+/// Solves the problem of how to tell if all peers, relative to all other peers, are in a certain ready state.
+/// For example, if A is connected to B and C, A may see that B and C are ready, but does not know if B is ready to C, or vice-versa.
+/// This plugin uses two stages to solve that problem, first, everyone I know about is ready. Second, everyone I know about is ready to everyone they know about.
+/// The user will get ID_READY_EVENT_SET and ID_READY_EVENT_UNSET as the signal flag is set or unset
+/// The user will get ID_READY_EVENT_ALL_SET when all systems are done waiting for all other systems, in which case the event is considered complete, and no longer tracked.
+/// \sa FullyConnectedMesh2 +/// \ingroup READY_EVENT_GROUP +class ReadyEvent : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(ReadyEvent) + + // Constructor + ReadyEvent(); + + // Destructor + virtual ~ReadyEvent(); + + // -------------------------------------------------------------------------------------------- + // User functions + // -------------------------------------------------------------------------------------------- + /// Sets or updates the initial ready state for our local system. + /// If eventId is an unknown event the event is created. + /// If eventId was previously used and you want to reuse it, call DeleteEvent first, or else you will keep the same event signals from before + /// Systems previously or later added through AddToWaitList() with the same \a eventId when isReady=true will get ID_READY_EVENT_SET + /// Systems previously added through AddToWaitList with the same \a eventId will get ID_READY_EVENT_UNSET + /// For both ID_READY_EVENT_SET and ID_READY_EVENT_UNSET, eventId is encoded in bytes 1 through 1+sizeof(int) + /// \param[in] eventId A user-defined identifier to wait on. This can be a sequence counter, an event identifier, or anything else you want. + /// \param[in] isReady True to signal we are ready to proceed with this event, false to unsignal + /// \return True on success. False (failure) on unknown eventId + bool SetEvent(int eventId, bool isReady); + + /// When systems can call SetEvent() with isReady==false, it is possible for one system to return true from IsEventCompleted() while the other systems return false + /// This can occur if a system SetEvent() with isReady==false while the completion message is still being transmitted. + /// If your game has the situation where some action should be taken on all systems when IsEventCompleted() is true for any system, then call ForceCompletion() when the action begins. + /// This will force all systems to return true from IsEventCompleted(). + /// \param[in] eventId A user-defined identifier to immediately set as completed + bool ForceCompletion(int eventId); + + /// Deletes an event. We will no longer wait for this event, and any systems that we know have set the event will be forgotten. + /// Call this to clear memory when events are completed and you know you will never need them again. + /// \param[in] eventId A user-defined identifier + /// \return True on success. False (failure) on unknown eventId + bool DeleteEvent(int eventId); + + /// Returns what was passed to SetEvent() + /// \return The value of isReady passed to SetEvent(). Also returns false on unknown event. + bool IsEventSet(int eventId); + + /// Returns if the event is about to be ready and we are negotiating the final packets. + /// This will usually only be true for a very short time, after which IsEventCompleted should return true. + /// While this is true you cannot add to the wait list, or SetEvent() isReady to false anymore. + /// \param[in] eventId A user-defined identifier + /// \return True if any other system has completed processing. Will always be true if IsEventCompleted() is true + bool IsEventCompletionProcessing(int eventId) const; + + /// Returns if the wait list is a subset of the completion list. + /// Call this after all systems you want to wait for have been added with AddToWaitList + /// If you are waiting for a specific number of systems (such as players later connecting), also check GetRemoteWaitListSize(eventId) to be equal to 1 less than the total number of participants. + /// \param[in] eventId A user-defined identifier + /// \return True on completion. False (failure) on unknown eventId, or the set is not completed. + bool IsEventCompleted(int eventId) const; + + /// Returns if this is a known event. + /// Events may be known even if we never ourselves referenced them with SetEvent, because other systems created them via ID_READY_EVENT_SET. + /// \param[in] eventId A user-defined identifier + /// \return true if we have this event, false otherwise + bool HasEvent(int eventId); + + /// Returns the total number of events stored in the system. + /// \return The total number of events stored in the system. + unsigned GetEventListSize(void) const; + + /// Returns the event ID stored at a particular index. EventIDs are stored sorted from least to greatest. + /// \param[in] index Index into the array, from 0 to GetEventListSize() + /// \return The event ID stored at a particular index + int GetEventAtIndex(unsigned index) const; + + /// Adds a system to wait for to signal an event before considering the event complete and returning ID_READY_EVENT_ALL_SET. + /// As we add systems, if this event was previously set to true with SetEvent, these systems will get ID_READY_EVENT_SET. + /// As these systems disconnect (directly or indirectly through the router) they are removed. + /// \note If the event completion process has already started, you cannot add more systems, as this would cause the completion process to fail + /// \param[in] eventId A user-defined number previously passed to SetEvent that has not yet completed + /// \param[in] addressArray An address to wait for event replies from. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. Until all systems in this list have called SetEvent with this ID and true, and have this system in the list, we won't get ID_READY_EVENT_COMPLETE + /// \return True on success, false on unknown eventId (this should be considered an error), or if the completion process has already started. + bool AddToWaitList(int eventId, SystemAddress address); + + /// Removes systems from the wait list, which should have been previously added with AddToWaitList + /// \note Systems that directly or indirectly disconnect from us are automatically removed from the wait list + /// \param[in] address The system to remove from the wait list. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. + /// \return True on success, false on unknown eventId (this should be considered an error) + bool RemoveFromWaitList(int eventId, SystemAddress address); + + /// Returns if a particular system is waiting on a particular event. + /// \param[in] eventId A user-defined identifier + /// \param[in] The address of the system we are checking up on + /// \return True if this system is waiting on this event, false otherwise. + bool IsInWaitList(int eventId, SystemAddress address); + + /// Returns the total number of systems we are waiting on for this event. + /// Does not include yourself + /// \param[in] eventId A user-defined identifier + /// \return The total number of systems we are waiting on for this event. + unsigned GetRemoteWaitListSize(int eventId) const; + + /// Returns the system address of a system at a particular index, for this event. + /// \param[in] eventId A user-defined identifier + /// \param[in] index Index into the array, from 0 to GetWaitListSize() + /// \return The system address of a system at a particular index, for this event. + SystemAddress GetFromWaitListAtIndex(int eventId, unsigned index) const; + + /// For a remote system, find out what their ready status is (waiting, signaled, complete). + /// \param[in] eventId A user-defined identifier + /// \param[in] address Which system we are checking up on + /// \return The status of this system, for this particular event. \sa ReadyEventSystemStatus + ReadyEventSystemStatus GetReadyStatus(int eventId, SystemAddress address); + + /// This channel will be used for all RakPeer::Send calls + /// \param[in] newChannel The channel to use for internal RakPeer::Send calls from this system. Defaults to 0. + void SetSendChannel(unsigned char newChannel); + + // ---------------------------- ALL INTERNAL AFTER HERE ---------------------------- + /// \internal + /// Status of a remote system + struct RemoteSystem + { + MessageID lastSentStatus, lastReceivedStatus; + SystemAddress systemAddress; + }; + static int RemoteSystemCompBySystemAddress( const SystemAddress &key, const RemoteSystem &data ); + /// \internal + /// An event, with a set of systems we are waiting for, a set of systems that are signaled, and a set of systems with completed events + struct ReadyEventNode + { + int eventId; // Sorted on this + MessageID eventStatus; + DataStructures::OrderedList systemList; + }; + static int ReadyEventNodeComp( const int &key, ReadyEvent::ReadyEventNode * const &data ); + + +protected: + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnRakPeerShutdown(void); + + void Clear(void); + /* + bool AnyWaitersCompleted(unsigned eventIndex) const; + bool AllWaitersCompleted(unsigned eventIndex) const; + bool AllWaitersReady(unsigned eventIndex) const; + void SendAllReady(unsigned eventId, SystemAddress address); + void BroadcastAllReady(unsigned eventIndex); + void SendReadyStateQuery(unsigned eventId, SystemAddress address); + void BroadcastReadyUpdate(unsigned eventIndex); + bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); + bool IsLocked(unsigned eventIndex) const; + bool IsAllReadyByIndex(unsigned eventIndex) const; + */ + + void SendReadyStateQuery(unsigned eventId, SystemAddress address); + void SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault); + void BroadcastReadyUpdate(unsigned eventIndex, bool forceIfNotDefault); + void RemoveFromAllLists(SystemAddress address); + void OnReadyEventQuery(Packet *packet); + void PushCompletionPacket(unsigned eventId); + bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); + void OnReadyEventForceAllSet(Packet *packet); + void OnReadyEventPacketUpdate(Packet *packet); + void UpdateReadyStatus(unsigned eventIndex); + bool IsEventCompletedByIndex(unsigned eventIndex) const; + unsigned CreateNewEvent(int eventId, bool isReady); + bool SetEventByIndex(int eventIndex, bool isReady); + + DataStructures::OrderedList readyEventNodeList; + unsigned char channel; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/RefCountedObj.h b/project/lib_projects/raknet/jni/RaknetSources/RefCountedObj.h index 221ec81..f0064f9 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/RefCountedObj.h +++ b/project/lib_projects/raknet/jni/RaknetSources/RefCountedObj.h @@ -1,25 +1,25 @@ -/// \file -/// \brief \b Reference counted object. Very simple class for quick and dirty uses. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __REF_COUNTED_OBJ_H -#define __REF_COUNTED_OBJ_H - -#include "RakMemoryOverride.h" - -/// World's simplest class :) -class RefCountedObj -{ - public: - RefCountedObj() {refCount=1;} - virtual ~RefCountedObj() {} - void AddRef(void) {refCount++;} - void Deref(void) {if (--refCount==0) RakNet::OP_DELETE(this, _FILE_AND_LINE_);} - int refCount; -}; - -#endif +/// \file +/// \brief \b Reference counted object. Very simple class for quick and dirty uses. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __REF_COUNTED_OBJ_H +#define __REF_COUNTED_OBJ_H + +#include "RakMemoryOverride.h" + +/// World's simplest class :) +class RefCountedObj +{ + public: + RefCountedObj() {refCount=1;} + virtual ~RefCountedObj() {} + void AddRef(void) {refCount++;} + void Deref(void) {if (--refCount==0) RakNet::OP_DELETE(this, _FILE_AND_LINE_);} + int refCount; +}; + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/ReliabilityLayer.cpp b/project/lib_projects/raknet/jni/RaknetSources/ReliabilityLayer.cpp index 3ac9cfa..767e64b 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ReliabilityLayer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/ReliabilityLayer.cpp @@ -1,3827 +1,3827 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "ReliabilityLayer.h" -#include "GetTime.h" -#include "SocketLayer.h" -#include "PluginInterface2.h" -#include "RakAssert.h" -#include "Rand.h" -#include "MessageIdentifiers.h" -#ifdef USE_THREADED_SEND -#include "SendToThread.h" -#endif -#include - -using namespace RakNet; - -// Can't figure out which library has this function on the PS3 -double Ceil(double d) {if (((double)((int)d))==d) return d; return (int) (d+1.0);} - -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RELIABILITY_LAYER_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - - -//#define _DEBUG_LOGGER - -#if CC_TIME_TYPE_BYTES==4 -static const CCTimeType MAX_TIME_BETWEEN_PACKETS= 350; // 350 milliseconds -static const CCTimeType HISTOGRAM_RESTART_CYCLE=10000; // Every 10 seconds reset the histogram -#else -static const CCTimeType MAX_TIME_BETWEEN_PACKETS= 350000; // 350 milliseconds -//static const CCTimeType HISTOGRAM_RESTART_CYCLE=10000000; // Every 10 seconds reset the histogram -#endif -static const int DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE=512; -static const CCTimeType STARTING_TIME_BETWEEN_PACKETS=MAX_TIME_BETWEEN_PACKETS; -//static const long double TIME_BETWEEN_PACKETS_INCREASE_MULTIPLIER_DEFAULT=.02; -//static const long double TIME_BETWEEN_PACKETS_DECREASE_MULTIPLIER_DEFAULT=1.0 / 9.0; - -typedef uint32_t BitstreamLengthEncoding; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -//#define PRINT_TO_FILE_ROT -#ifdef PRINT_TO_FILE_ROT -static FILE *fp=0; -#endif - -BPSTracker::TimeAndValue2::TimeAndValue2() {} -BPSTracker::TimeAndValue2::~TimeAndValue2() {} -BPSTracker::TimeAndValue2::TimeAndValue2(RakNet::TimeUS t, uint64_t v1) : value1(v1), time(t) {} -//BPSTracker::TimeAndValue2::TimeAndValue2(RakNet::TimeUS t, uint64_t v1, uint64_t v2) : time(t), value1(v1), value2(v2) {} -BPSTracker::BPSTracker() {Reset(_FILE_AND_LINE_);} -BPSTracker::~BPSTracker() {} -//void BPSTracker::Reset(const char *file, unsigned int line) {total1=total2=lastSec1=lastSec2=0; dataQueue.Clear(file,line);} -void BPSTracker::Reset(const char *file, unsigned int line) {total1=lastSec1=0; dataQueue.Clear(file,line);} -//void BPSTracker::Push2(RakNetTimeUS time, uint64_t value1, uint64_t value2) {dataQueue.Push(TimeAndValue2(time,value1,value2),_FILE_AND_LINE_); total1+=value1; lastSec1+=value1; total2+=value2; lastSec2+=value2;} -//uint64_t BPSTracker::GetBPS2(RakNetTimeUS time) {ClearExpired2(time); return lastSec2;} -//void BPSTracker::GetBPS1And2(RakNetTimeUS time, uint64_t &out1, uint64_t &out2) {ClearExpired2(time); out1=lastSec1; out2=lastSec2;} -uint64_t BPSTracker::GetTotal1(void) const {return total1;} -//uint64_t BPSTracker::GetTotal2(void) const {return total2;} - -// void BPSTracker::ClearExpired2(RakNet::TimeUS time) { -// RakNet::TimeUS threshold=time; -// if (threshold < 1000000) -// return; -// threshold-=1000000; -// while (dataQueue.IsEmpty()==false && dataQueue.Peek().time < threshold) -// { -// lastSec1-=dataQueue.Peek().value1; -// lastSec2-=dataQueue.Peek().value2; -// dataQueue.Pop(); -// } -// } -void BPSTracker::ClearExpired1(RakNet::TimeUS time) -{ - while (dataQueue.IsEmpty()==false && -#if CC_TIME_TYPE_BYTES==8 - dataQueue.Peek().time+1000000 < time -#else - dataQueue.Peek().time+1000 < time -#endif - ) - { - lastSec1-=dataQueue.Peek().value1; - dataQueue.Pop(); - } -} - -struct DatagramHeaderFormat -{ -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - CCTimeType sourceSystemTime; -#endif - DatagramSequenceNumberType datagramNumber; - - // Use floats to save bandwidth - // float B; // Link capacity - float AS; // Data arrival rate - bool isACK; - bool isNAK; - bool isPacketPair; - bool hasBAndAS; - bool isContinuousSend; - bool needsBAndAs; - bool isValid; // To differentiate between what I serialized, and offline data - - static BitSize_t GetDataHeaderBitLength() - { - return BYTES_TO_BITS(GetDataHeaderByteLength()); - } - - static unsigned int GetDataHeaderByteLength() - { - //return 2 + 3 + sizeof(RakNet::TimeMS) + sizeof(float)*2; - return 2 + 3 + -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - sizeof(RakNetTimeMS) + -#endif - sizeof(float)*1; - } - - void Serialize(RakNet::BitStream *b) - { - // Not endian safe - // RakAssert(GetDataHeaderByteLength()==sizeof(DatagramHeaderFormat)); - // b->WriteAlignedBytes((const unsigned char*) this, sizeof(DatagramHeaderFormat)); - // return; - - b->Write(true); // IsValid - if (isACK) - { - b->Write(true); - b->Write(hasBAndAS); - b->AlignWriteToByteBoundary(); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMSLow=(RakNet::TimeMS) sourceSystemTime&0xFFFFFFFF; b->Write(timeMSLow); -#endif - if (hasBAndAS) - { - // b->Write(B); - b->Write(AS); - } - } - else if (isNAK) - { - b->Write(false); - b->Write(true); - } - else - { - b->Write(false); - b->Write(false); - b->Write(isPacketPair); - b->Write(isContinuousSend); - b->Write(needsBAndAs); - b->AlignWriteToByteBoundary(); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMSLow=(RakNet::TimeMS) sourceSystemTime&0xFFFFFFFF; b->Write(timeMSLow); -#endif - b->Write(datagramNumber); - } - } - void Deserialize(RakNet::BitStream *b) - { - // Not endian safe - // b->ReadAlignedBytes((unsigned char*) this, sizeof(DatagramHeaderFormat)); - // return; - - b->Read(isValid); - b->Read(isACK); - if (isACK) - { - isNAK=false; - isPacketPair=false; - b->Read(hasBAndAS); - b->AlignReadToByteBoundary(); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMS; b->Read(timeMS); sourceSystemTime=(CCTimeType) timeMS; -#endif - if (hasBAndAS) - { - // b->Read(B); - b->Read(AS); - } - } - else - { - b->Read(isNAK); - if (isNAK) - { - isPacketPair=false; - } - else - { - b->Read(isPacketPair); - b->Read(isContinuousSend); - b->Read(needsBAndAs); - b->AlignReadToByteBoundary(); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMS; b->Read(timeMS); sourceSystemTime=(CCTimeType) timeMS; -#endif - b->Read(datagramNumber); - } - } - } -}; - -#if !defined(__GNUC__) && !defined(__ARMCC) -#pragma warning(disable:4702) // unreachable code -#endif - -#ifdef _WIN32 -//#define _DEBUG_LOGGER -#ifdef _DEBUG_LOGGER -#include "WindowsIncludes.h" -#endif -#endif - -//#define DEBUG_SPLIT_PACKET_PROBLEMS -#if defined (DEBUG_SPLIT_PACKET_PROBLEMS) -static int waitFlag=-1; -#endif - -using namespace RakNet; - -int RakNet::SplitPacketChannelComp( SplitPacketIdType const &key, SplitPacketChannel* const &data ) -{ -#if PREALLOCATE_LARGE_MESSAGES==1 - if (key < data->returnedPacket->splitPacketId) - return -1; - if (key == data->returnedPacket->splitPacketId) - return 0; -#else - if (key < data->splitPacketList[0]->splitPacketId) - return -1; - if (key == data->splitPacketList[0]->splitPacketId) - return 0; -#endif - return 1; -} - -// DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS( InternalPacket, SplitPacketIndexType, splitPacketIndex ) -/* -bool operator<( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get() < cls->splitPacketIndex; -} -bool operator>( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get() > cls->splitPacketIndex; -} -bool operator==( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get() == cls->splitPacketIndex; -} -/// Semi-hack: This is necessary to call Sort() -bool operator<( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get()->splitPacketIndex < cls->splitPacketIndex; -} -bool operator>( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get()->splitPacketIndex > cls->splitPacketIndex; -} -bool operator==( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get()->splitPacketIndex == cls->splitPacketIndex; -} -*/ - -int SplitPacketIndexComp( SplitPacketIndexType const &key, InternalPacket* const &data ) -{ -if (key < data->splitPacketIndex) -return -1; -if (key == data->splitPacketIndex) -return 0; -return 1; -} - -//------------------------------------------------------------------------------------------------------- -// Constructor -//------------------------------------------------------------------------------------------------------- -// Add 21 to the default MTU so if we encrypt it can hold potentially 21 more bytes of extra data + padding. -ReliabilityLayer::ReliabilityLayer() -{ - -#ifdef _DEBUG - // Wait longer to disconnect in debug so I don't get disconnected while tracing - timeoutTime=30000; -#else - timeoutTime=10000; -#endif - -#ifdef _DEBUG - minExtraPing=extraPingVariance=0; - packetloss=(double) minExtraPing; -#endif - - -#ifdef PRINT_TO_FILE_ROT - if (fp==0 && 0) - { - fp = fopen("reliableorderedoutput.txt", "wt"); - } -#endif - - InitializeVariables(); -//int i = sizeof(InternalPacket); - datagramHistoryMessagePool.SetPageSize(sizeof(MessageNumberNode)*128); - internalPacketPool.SetPageSize(sizeof(InternalPacket)*INTERNAL_PACKET_PAGE_SIZE); - refCountedDataPool.SetPageSize(sizeof(InternalPacketRefCountedData)*32); -} - -//------------------------------------------------------------------------------------------------------- -// Destructor -//------------------------------------------------------------------------------------------------------- -ReliabilityLayer::~ReliabilityLayer() -{ - FreeMemory( true ); // Free all memory immediately -} -//------------------------------------------------------------------------------------------------------- -// Resets the layer for reuse -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::Reset( bool resetVariables, int MTUSize, bool _useSecurity ) -{ - - FreeMemory( true ); // true because making a memory reset pending in the update cycle causes resets after reconnects. Instead, just call Reset from a single thread - if (resetVariables) - { - InitializeVariables(); - -#if LIBCAT_SECURITY==1 - useSecurity = _useSecurity; - - if (_useSecurity) - MTUSize -= cat::AuthenticatedEncryption::OVERHEAD_BYTES; -#else - (void) _useSecurity; -#endif // LIBCAT_SECURITY - congestionManager.Init(RakNet::GetTimeUS(), MTUSize - UDP_HEADER_SIZE); - } -} - -//------------------------------------------------------------------------------------------------------- -// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SetTimeoutTime( RakNet::TimeMS time ) -{ - timeoutTime=time; -} - -//------------------------------------------------------------------------------------------------------- -// Returns the value passed to SetTimeoutTime. or the default if it was never called -//------------------------------------------------------------------------------------------------------- -RakNet::TimeMS ReliabilityLayer::GetTimeoutTime(void) -{ - return timeoutTime; -} - -//------------------------------------------------------------------------------------------------------- -// Initialize the variables -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::InitializeVariables( void ) -{ - memset( orderedWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType)); - memset( sequencedWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); - memset( orderedReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); - memset( highestSequencedReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); - memset( &statistics, 0, sizeof( statistics ) ); - memset( &heapIndexOffsets, 0, sizeof( heapIndexOffsets ) ); - - statistics.connectionStartTime = RakNet::GetTimeUS(); - splitPacketId = 0; - elapsedTimeSinceLastUpdate=0; - throughputCapCountdown=0; - sendReliableMessageNumberIndex = 0; - internalOrderIndex=0; - timeToNextUnreliableCull=0; - unreliableLinkedListHead=0; - lastUpdateTime= RakNet::GetTimeUS(); - bandwidthExceededStatistic=false; - remoteSystemTime=0; - unreliableTimeout=0; - lastBpsClear=0; - - // Disable packet pairs - countdownToNextPacketPair=15; - - nextAllowedThroughputSample=0; - deadConnection = cheater = false; - timeOfLastContinualSend=0; - - // timeResendQueueNonEmpty = 0; - timeLastDatagramArrived=RakNet::GetTimeMS(); - // packetlossThisSample=false; - // backoffThisSample=0; - // packetlossThisSampleResendCount=0; - // lastPacketlossTime=0; - statistics.messagesInResendBuffer=0; - statistics.bytesInResendBuffer=0; - - receivedPacketsBaseIndex=0; - resetReceivedPackets=true; - receivePacketCount=0; - - // SetPing( 1000 ); - - timeBetweenPackets=STARTING_TIME_BETWEEN_PACKETS; - - ackPingIndex=0; - ackPingSum=(CCTimeType)0; - - nextSendTime=lastUpdateTime; - //nextLowestPingReset=(CCTimeType)0; - // continuousSend=false; - - // histogramStart=(CCTimeType)0; - // histogramBitsSent=0; - unacknowledgedBytes=0; - resendLinkedListHead=0; - totalUserDataBytesAcked=0; - - datagramHistoryPopCount=0; - - InitHeapWeights(); - for (int i=0; i < NUMBER_OF_PRIORITIES; i++) - { - statistics.messageInSendBuffer[i]=0; - statistics.bytesInSendBuffer[i]=0.0; - } - - for (int i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) - { - bpsMetrics[i].Reset(_FILE_AND_LINE_); - } -} - -//------------------------------------------------------------------------------------------------------- -// Frees all allocated memory -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::FreeMemory( bool freeAllImmediately ) -{ - (void) freeAllImmediately; - FreeThreadSafeMemory(); -} - -void ReliabilityLayer::FreeThreadSafeMemory( void ) -{ - unsigned i,j; - InternalPacket *internalPacket; - - ClearPacketsAndDatagrams(); - - for (i=0; i < splitPacketChannelList.Size(); i++) - { - for (j=0; j < splitPacketChannelList[i]->splitPacketList.Size(); j++) - { - FreeInternalPacketData(splitPacketChannelList[i]->splitPacketList[j], _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( splitPacketChannelList[i]->splitPacketList[j] ); - } -#if PREALLOCATE_LARGE_MESSAGES==1 - if (splitPacketChannelList[i]->returnedPacket) - { - FreeInternalPacketData(splitPacketChannelList[i]->returnedPacket, __FILE__, __LINE__ ); - ReleaseToInternalPacketPool( splitPacketChannelList[i]->returnedPacket ); - } -#endif - RakNet::OP_DELETE(splitPacketChannelList[i], __FILE__, __LINE__); - } - splitPacketChannelList.Clear(false, _FILE_AND_LINE_); - - while ( outputQueue.Size() > 0 ) - { - internalPacket = outputQueue.Pop(); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - } - - outputQueue.ClearAndForceAllocation( 32, _FILE_AND_LINE_ ); - - /* - for ( i = 0; i < orderingList.Size(); i++ ) - { - if ( orderingList[ i ] ) - { - DataStructures::LinkedList* theList = orderingList[ i ]; - - if ( theList ) - { - while ( theList->Size() ) - { - internalPacket = orderingList[ i ]->Pop(); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - } - - RakNet::OP_DELETE(theList, _FILE_AND_LINE_); - } - } - } - - orderingList.Clear(false, _FILE_AND_LINE_); - */ - - for (i=0; i < NUMBER_OF_ORDERED_STREAMS; i++) - { - for (j=0; j < orderingHeaps[i].Size(); j++) - { - FreeInternalPacketData(orderingHeaps[i][j], _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( orderingHeaps[i][j] ); - } - orderingHeaps[i].Clear(true, _FILE_AND_LINE_); - } - - //resendList.ForEachData(DeleteInternalPacket); - // resendTree.Clear(_FILE_AND_LINE_); - memset(resendBuffer, 0, sizeof(resendBuffer)); - statistics.messagesInResendBuffer=0; - statistics.bytesInResendBuffer=0; - - if (resendLinkedListHead) - { - InternalPacket *prev; - InternalPacket *iter = resendLinkedListHead; - while (1) - { - if (iter->data) - FreeInternalPacketData(iter, _FILE_AND_LINE_ ); - prev=iter; - iter=iter->resendNext; - if (iter==resendLinkedListHead) - { - ReleaseToInternalPacketPool(prev); - break; - } - ReleaseToInternalPacketPool(prev); - } - resendLinkedListHead=0; - } - unacknowledgedBytes=0; - - // acknowlegements.Clear(_FILE_AND_LINE_); - - for ( j=0 ; j < outgoingPacketBuffer.Size(); j++ ) - { - if ( outgoingPacketBuffer[ j ]->data) - FreeInternalPacketData( outgoingPacketBuffer[ j ], _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( outgoingPacketBuffer[ j ] ); - } - - outgoingPacketBuffer.Clear(true, _FILE_AND_LINE_); - -#ifdef _DEBUG - for (unsigned i = 0; i < delayList.Size(); i++ ) - RakNet::OP_DELETE(delayList[ i ], __FILE__, __LINE__); - delayList.Clear(__FILE__, __LINE__); -#endif - - unreliableWithAckReceiptHistory.Clear(false, _FILE_AND_LINE_); - - packetsToSendThisUpdate.Clear(false, _FILE_AND_LINE_); - packetsToSendThisUpdate.Preallocate(512, _FILE_AND_LINE_); - packetsToDeallocThisUpdate.Clear(false, _FILE_AND_LINE_); - packetsToDeallocThisUpdate.Preallocate(512, _FILE_AND_LINE_); - packetsToSendThisUpdateDatagramBoundaries.Clear(false, _FILE_AND_LINE_); - packetsToSendThisUpdateDatagramBoundaries.Preallocate(128, _FILE_AND_LINE_); - datagramSizesInBytes.Clear(false, _FILE_AND_LINE_); - datagramSizesInBytes.Preallocate(128, _FILE_AND_LINE_); - - internalPacketPool.Clear(_FILE_AND_LINE_); - - refCountedDataPool.Clear(_FILE_AND_LINE_); - - /* - DataStructures::Page *cur = datagramMessageIDTree.GetListHead(); - while (cur) - { - int treeIndex; - for (treeIndex=0; treeIndex < cur->size; treeIndex++) - ReleaseToDatagramMessageIDPool(cur->data[treeIndex]); - cur=cur->resendNext; - } - datagramMessageIDTree.Clear(_FILE_AND_LINE_); - datagramMessageIDPool.Clear(_FILE_AND_LINE_); - */ - - while (datagramHistory.Size()) - { - RemoveFromDatagramHistory(datagramHistoryPopCount); - datagramHistory.Pop(); - datagramHistoryPopCount++; - } - datagramHistoryMessagePool.Clear(_FILE_AND_LINE_); - datagramHistoryPopCount=0; - - acknowlegements.Clear(); - NAKs.Clear(); - - unreliableLinkedListHead=0; -} - -//------------------------------------------------------------------------------------------------------- -// Packets are read directly from the socket layer and skip the reliability -//layer because unconnected players do not use the reliability layer -// This function takes packet data after a player has been confirmed as -//connected. The game should not use that data directly -// because some data is used internally, such as packet acknowledgment and -//split packets -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::HandleSocketReceiveFromConnectedPlayer( - const char *buffer, unsigned int length, SystemAddress &systemAddress, DataStructures::List &messageHandlerList, int MTUSize, - SOCKET s, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType timeRead, - BitStream &updateBitStream) -{ -#ifdef _DEBUG - RakAssert( !( buffer == 0 ) ); -#endif - -#if CC_TIME_TYPE_BYTES==4 - timeRead/=1000; -#endif - - - bpsMetrics[(int) ACTUAL_BYTES_RECEIVED].Push1(timeRead,length); - - (void) MTUSize; - - if ( length <= 2 || buffer == 0 ) // Length of 1 is a connection request resend that we just ignore - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("length <= 2 || buffer == 0", BYTES_TO_BITS(length), systemAddress); - return true; - } - - timeLastDatagramArrived=RakNet::GetTimeMS(); - - // CCTimeType time; -// bool indexFound; -// int count, size; - DatagramSequenceNumberType holeCount; - unsigned i; - -#if LIBCAT_SECURITY==1 - if (useSecurity) - { - unsigned int received = length; - - if (!auth_enc.Decrypt((cat::u8*)buffer, received)) - return false; - - length = received; - } -#endif - - RakNet::BitStream socketData( (unsigned char*) buffer, length, false ); // Convert the incoming data to a bitstream for easy parsing - // time = RakNet::GetTimeUS(); - - // Set to the current time if it is not zero, and we get incoming data - // if (timeResendQueueNonEmpty!=0) - // timeResendQueueNonEmpty=timeRead; - - DatagramHeaderFormat dhf; - dhf.Deserialize(&socketData); - if (dhf.isValid==false) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("dhf.isValid==false", BYTES_TO_BITS(length), systemAddress); - - return true; - } - if (dhf.isACK) - { - DatagramSequenceNumberType datagramNumber; - // datagramNumber=dhf.datagramNumber; - -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMSLow=(RakNet::TimeMS) timeRead&0xFFFFFFFF; - CCTimeType rtt = timeMSLow-dhf.sourceSystemTime; -#if CC_TIME_TYPE_BYTES==4 - if (rtt > 10000) -#else - if (rtt > 10000000) -#endif - { - // Sanity check. This could happen due to type overflow, especially since I only send the low 4 bytes to reduce bandwidth - rtt=(CCTimeType) congestionManager.GetRTT(); - } - // RakAssert(rtt < 500000); - // printf("%i ", (RakNet::TimeMS)(rtt/1000)); - ackPing=rtt; -#endif - -#ifdef _DEBUG - if (dhf.hasBAndAS==false) - { - // dhf.B=0; - dhf.AS=0; - } -#endif - // congestionManager.OnAck(timeRead, rtt, dhf.hasBAndAS, dhf.B, dhf.AS, totalUserDataBytesAcked ); - - - incomingAcks.Clear(); - if (incomingAcks.Deserialize(&socketData)==false) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingAcks.Deserialize failed", BYTES_TO_BITS(length), systemAddress); - - return false; - } - for (i=0; iincomingAcks.ranges[i].maxIndex) - { - RakAssert(incomingAcks.ranges[i].minIndex<=incomingAcks.ranges[i].maxIndex); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingAcks minIndex > maxIndex", BYTES_TO_BITS(length), systemAddress); - return false; - } - for (datagramNumber=incomingAcks.ranges[i].minIndex; datagramNumber >= incomingAcks.ranges[i].minIndex && datagramNumber <= incomingAcks.ranges[i].maxIndex; datagramNumber++) - { - CCTimeType whenSent; - - if (unreliableWithAckReceiptHistory.Size()>0) - { - unsigned int k=0; - while (k < unreliableWithAckReceiptHistory.Size()) - { - if (unreliableWithAckReceiptHistory[k].datagramNumber == datagramNumber) - { - InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); - AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); - ackReceipt->dataBitLength=BYTES_TO_BITS(5); - ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_ACKED; - memcpy(ackReceipt->data+sizeof(MessageID), &unreliableWithAckReceiptHistory[k].sendReceiptSerial, sizeof(uint32_t)); - outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); - - // Remove, swap with last - unreliableWithAckReceiptHistory.RemoveAtIndex(k); - } - else - k++; - } - } - - MessageNumberNode *messageNumberNode = GetMessageNumberNodeByDatagramIndex(datagramNumber, &whenSent); - if (messageNumberNode) - { - // printf("%p Got ack for %i\n", this, datagramNumber.val); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - congestionManager.OnAck(timeRead, rtt, dhf.hasBAndAS, 0, dhf.AS, totalUserDataBytesAcked, bandwidthExceededStatistic, datagramNumber ); -#else - CCTimeType ping; - if (timeRead>whenSent) - ping=timeRead-whenSent; - else - ping=0; - congestionManager.OnAck(timeRead, ping, dhf.hasBAndAS, 0, dhf.AS, totalUserDataBytesAcked, bandwidthExceededStatistic, datagramNumber ); -#endif - while (messageNumberNode) - { - // TESTING1 -// printf("Remove %i on ack for datagramNumber=%i.\n", messageNumberNode->messageNumber.val, datagramNumber.val); - - RemovePacketFromResendListAndDeleteOlderReliableSequenced( messageNumberNode->messageNumber, timeRead, messageHandlerList, systemAddress ); - messageNumberNode=messageNumberNode->next; - } - - RemoveFromDatagramHistory(datagramNumber); - } -// else if (isReliable) -// { -// // Previously used slot, rather than empty unreliable slot -// printf("%p Ack %i is duplicate\n", this, datagramNumber.val); -// -// congestionManager.OnDuplicateAck(timeRead, datagramNumber); -// } - } - } - } - else if (dhf.isNAK) - { - DatagramSequenceNumberType messageNumber; - DataStructures::RangeList incomingNAKs; - if (incomingNAKs.Deserialize(&socketData)==false) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingNAKs.Deserialize failed", BYTES_TO_BITS(length), systemAddress); - - return false; - } - for (i=0; iincomingNAKs.ranges[i].maxIndex) - { - RakAssert(incomingNAKs.ranges[i].minIndex<=incomingNAKs.ranges[i].maxIndex); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingNAKs minIndex>maxIndex", BYTES_TO_BITS(length), systemAddress); - - return false; - } - // Sanity check - RakAssert(incomingNAKs.ranges[i].maxIndex.val-incomingNAKs.ranges[i].minIndex.val<1000); - for (messageNumber=incomingNAKs.ranges[i].minIndex; messageNumber >= incomingNAKs.ranges[i].minIndex && messageNumber <= incomingNAKs.ranges[i].maxIndex; messageNumber++) - { - congestionManager.OnNAK(timeRead, messageNumber); - - // REMOVEME - // printf("%p NAK %i\n", this, dhf.datagramNumber.val); - - - CCTimeType timeSent; - MessageNumberNode *messageNumberNode = GetMessageNumberNodeByDatagramIndex(messageNumber, &timeSent); - while (messageNumberNode) - { - // Update timers so resends occur immediately - InternalPacket *internalPacket = resendBuffer[messageNumberNode->messageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK]; - if (internalPacket) - { - if (internalPacket->nextActionTime!=0) - { - internalPacket->nextActionTime=timeRead; - } - } - - messageNumberNode=messageNumberNode->next; - } - } - } - } - else - { - uint32_t skippedMessageCount; - if (!congestionManager.OnGotPacket(dhf.datagramNumber, dhf.isContinuousSend, timeRead, length, &skippedMessageCount)) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("congestionManager.OnGotPacket failed", BYTES_TO_BITS(length), systemAddress); - - return true; - } - if (dhf.isPacketPair) - congestionManager.OnGotPacketPair(dhf.datagramNumber, length, timeRead); - - DatagramHeaderFormat dhfNAK; - dhfNAK.isNAK=true; - uint32_t skippedMessageOffset; - for (skippedMessageOffset=skippedMessageCount; skippedMessageOffset > 0; skippedMessageOffset--) - { - NAKs.Insert(dhf.datagramNumber-skippedMessageOffset); - } - remoteSystemNeedsBAndAS=dhf.needsBAndAs; - - // Ack dhf.datagramNumber - // Ack even unreliable messages for congestion control, just don't resend them on no ack -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - SendAcknowledgementPacket( dhf.datagramNumber, dhf.sourceSystemTime); -#else - SendAcknowledgementPacket( dhf.datagramNumber, 0); -#endif - - InternalPacket* internalPacket = CreateInternalPacketFromBitStream( &socketData, timeRead ); - if (internalPacket==0) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("CreateInternalPacketFromBitStream failed", BYTES_TO_BITS(length), systemAddress); - - return true; - } - - while ( internalPacket ) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - { -#if CC_TIME_TYPE_BYTES==4 - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, receivePacketCount, systemAddress, timeRead, false); -#else - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, receivePacketCount, systemAddress, (RakNet::TimeMS)(timeRead/(CCTimeType)1000), false); -#endif - } - - { - - // resetReceivedPackets is set from a non-threadsafe function. - // We do the actual reset in this function so the data is not modified by multiple threads - if (resetReceivedPackets) - { - hasReceivedPacketQueue.ClearAndForceAllocation(DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE, _FILE_AND_LINE_); - receivedPacketsBaseIndex=0; - resetReceivedPackets=false; - } - - // Check for corrupt orderingChannel - if ( - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED - ) - { - if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS", BYTES_TO_BITS(length), systemAddress); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - - // 8/12/09 was previously not checking if the message was reliable. However, on packetloss this would mean you'd eventually exceed the - // hole count because unreliable messages were never resent, and you'd stop getting messages - if (internalPacket->reliability == RELIABLE || internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_ORDERED ) - { - // If the following conditional is true then this either a duplicate packet - // or an older out of order packet - // The subtraction unsigned overflow is intentional - holeCount = (DatagramSequenceNumberType)(internalPacket->reliableMessageNumber-receivedPacketsBaseIndex); - const DatagramSequenceNumberType typeRange = (DatagramSequenceNumberType)(const uint32_t)-1; - - // TESTING1 -// printf("waiting on reliableMessageNumber=%i holeCount=%i datagramNumber=%i\n", receivedPacketsBaseIndex.val, holeCount.val, dhf.datagramNumber.val); - - if (holeCount==(DatagramSequenceNumberType) 0) - { - // Got what we were expecting - if (hasReceivedPacketQueue.Size()) - hasReceivedPacketQueue.Pop(); - ++receivedPacketsBaseIndex; - } - else if (holeCount > typeRange/(DatagramSequenceNumberType) 2) - { - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Duplicate packet - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - else if ((unsigned int) holeCountdataBitLength)); - - // Duplicate packet - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - else // holeCount>=receivedPackets.Size() - { - if (holeCount > (DatagramSequenceNumberType) 1000000) - { - RakAssert("Hole count too high. See ReliabilityLayer.h" && 0); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("holeCount > 1000000", BYTES_TO_BITS(length), systemAddress); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Would crash due to out of memory! - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - - // Fix - sending on a higher priority gives us a very very high received packets base index if we formerly had pre-split a lot of messages and - // used that as the message number. Because of this, a lot of time is spent in this linear loop and the timeout time expires because not - // all of the message is sent in time. - // Fixed by late assigning message IDs on the sender - - // Add 0 times to the queue until (reliableMessageNumber - baseIndex) < queue size. - while ((unsigned int)(holeCount) > hasReceivedPacketQueue.Size()) - hasReceivedPacketQueue.Push(true, _FILE_AND_LINE_ ); // time+(CCTimeType)60 * (CCTimeType)1000 * (CCTimeType)1000); // Didn't get this packet - set the time to give up waiting - hasReceivedPacketQueue.Push(false, _FILE_AND_LINE_ ); // Got the packet -#ifdef _DEBUG - // If this assert hits then DatagramSequenceNumberType has overflowed - RakAssert(hasReceivedPacketQueue.Size() < (unsigned int)((DatagramSequenceNumberType)(const uint32_t)(-1))); -#endif - } - - while ( hasReceivedPacketQueue.Size()>0 && hasReceivedPacketQueue.Peek()==false ) - { - hasReceivedPacketQueue.Pop(); - ++receivedPacketsBaseIndex; - } - } - - // If the allocated buffer is > DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE and it is 3x greater than the number of elements actually being used - if (hasReceivedPacketQueue.AllocationSize() > (unsigned int) DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE && hasReceivedPacketQueue.AllocationSize() > hasReceivedPacketQueue.Size() * 3) - hasReceivedPacketQueue.Compress(_FILE_AND_LINE_); - - - /* - if ( internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == UNRELIABLE_SEQUENCED ) - { -#ifdef _DEBUG - RakAssert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); -#endif - - if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) - { - - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS", BYTES_TO_BITS(length), systemAddress); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - if ( IsOlderOrderedPacket( internalPacket->orderingIndex, waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] ) == false ) - { - // Is this a split packet? - if ( internalPacket->splitPacketCount > 0 ) - { - // Generate the split - // Verify some parameters to make sure we don't get junk data - - - // Check for a rebuilt packet - InsertIntoSplitPacketList( internalPacket, timeRead ); - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Sequenced - internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, - s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions); - - if ( internalPacket ) - { - // Update our index to the newest packet - waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + (OrderingIndexType)1; - - // If there is a rebuilt packet, add it to the output queue - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - internalPacket = 0; - } - - // else don't have all the parts yet - } - else - { - // Update our index to the newest packet - waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + (OrderingIndexType)1; - - // Not a split packet. Add the packet to the output queue - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - internalPacket = 0; - } - } - else - { - // Older sequenced packet. Discard it - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - } - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - // Is this an unsequenced split packet? - if ( internalPacket->splitPacketCount > 0 ) - { - // Check for a rebuilt packet - if ( internalPacket->reliability != RELIABLE_ORDERED ) - internalPacket->orderingChannel = 255; // Use 255 to designate not sequenced and not ordered - - InsertIntoSplitPacketList( internalPacket, timeRead ); - - internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, - s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions); - - if ( internalPacket == 0 ) - { - - // Don't have all the parts yet - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - */ - - /* - if ( internalPacket->reliability == RELIABLE_ORDERED ) - { -#ifdef _DEBUG - RakAssert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); -#endif - - if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) - { - // Invalid packet - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - if ( waitingForOrderedPacketReadIndex[ internalPacket->orderingChannel ] == internalPacket->orderingIndex ) - { - // Get the list to hold ordered packets for this stream - DataStructures::LinkedList *orderingListAtOrderingStream; - unsigned char orderingChannelCopy = internalPacket->orderingChannel; - - // Push the packet for the user to read - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - internalPacket = 0; // Don't reference this any longer since other threads access it - - // Wait for the resendNext ordered packet in sequence - waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ++; // This wraps - - orderingListAtOrderingStream = GetOrderingListAtOrderingStream( orderingChannelCopy ); - - if ( orderingListAtOrderingStream != 0) - { - while ( orderingListAtOrderingStream->Size() > 0 ) - { - // Cycle through the list until nothing is found - orderingListAtOrderingStream->Beginning(); - indexFound=false; - size=orderingListAtOrderingStream->Size(); - count=0; - - while (count++ < size) - { - if ( orderingListAtOrderingStream->Peek()->orderingIndex == waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ) - { - outputQueue.Push( orderingListAtOrderingStream->Pop(), _FILE_AND_LINE_ ); - waitingForOrderedPacketReadIndex[ orderingChannelCopy ]++; - indexFound=true; - } - else - (*orderingListAtOrderingStream)++; - } - - if (indexFound==false) - break; - } - } - internalPacket = 0; - } - else - { - // This is a newer ordered packet than we are waiting for. Store it for future use - AddToOrderingList( internalPacket ); - } - - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - */ - - // Is this a split packet? If so then reassemble - if ( internalPacket->splitPacketCount > 0 ) - { - // Check for a rebuilt packet - if ( internalPacket->reliability != RELIABLE_ORDERED && internalPacket->reliability!=RELIABLE_SEQUENCED && internalPacket->reliability!=UNRELIABLE_SEQUENCED) - internalPacket->orderingChannel = 255; // Use 255 to designate not sequenced and not ordered - - InsertIntoSplitPacketList( internalPacket, timeRead ); - - internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, - s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); - - if ( internalPacket == 0 ) - { - // Don't have all the parts yet - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - - if (internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED) - { -#ifdef PRINT_TO_FILE_ROT - // ___________________ - BitStream bitStream(internalPacket->data, BITS_TO_BYTES(internalPacket->dataBitLength), false); - unsigned int receivedPacketNumber; - RakNet::Time receivedTime; - unsigned char streamNumber; - PacketReliability reliability; - // ___________________ - - - bitStream.IgnoreBits(8); // Ignore ID_TIMESTAMP - bitStream.Read(receivedTime); - bitStream.IgnoreBits(8); // Ignore ID_USER_ENUM+1 - bitStream.Read(receivedPacketNumber); - bitStream.Read(streamNumber); - bitStream.Read(reliability); - char *type="UNDEFINED"; - if (reliability==UNRELIABLE_SEQUENCED) - type="UNRELIABLE_SEQUENCED"; - else if (reliability==RELIABLE_ORDERED) - type="RELIABLE_ORDERED"; - // ___________________ -#endif - - - if (internalPacket->orderingIndex==orderedReadIndex[internalPacket->orderingChannel]) - { - // Has current ordering index - if (internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED) - { - // Is sequenced - if (IsOlderOrderedPacket(internalPacket->sequencingIndex,highestSequencedReadIndex[internalPacket->orderingChannel])==false) - { - // Expected or highest known value - - // Update highest sequence - highestSequencedReadIndex[internalPacket->orderingChannel] = internalPacket->sequencingIndex; - -#ifdef PRINT_TO_FILE_ROT - if (fp) - { - fprintf(fp, "Returning %i, %s by fallthrough. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); - fflush(fp); - } -#endif - - // Fallthrough, returned to user below - } - else - { -#ifdef PRINT_TO_FILE_ROT - if (fp) - { - fprintf(fp, "Discarding %i, %s late sequenced. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); - fflush(fp); - } -#endif - - // Lower than highest known value - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - else - { - // Push to output buffer immediately - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - -#ifdef PRINT_TO_FILE_ROT - if (fp) - { - fprintf(fp, "outputting immediate %i, %s. OI=%i. SI=%i.", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); - if (orderingHeaps[internalPacket->orderingChannel].Size()==0) - fprintf(fp, "heap empty\n"); - else - fprintf(fp, "heap head=%i\n", orderingHeaps[internalPacket->orderingChannel].Peek()->orderingIndex.val); - fflush(fp); - } -#endif - - orderedReadIndex[internalPacket->orderingChannel]++; - highestSequencedReadIndex[internalPacket->orderingChannel] = 0; - - // Return off heap until order lost - while (orderingHeaps[internalPacket->orderingChannel].Size()>0 && - orderingHeaps[internalPacket->orderingChannel].Peek()->orderingIndex==orderedReadIndex[internalPacket->orderingChannel]) - { - internalPacket = orderingHeaps[internalPacket->orderingChannel].Pop(0); - -#ifdef PRINT_TO_FILE_ROT - BitStream bitStream2(internalPacket->data, BITS_TO_BYTES(internalPacket->dataBitLength), false); - bitStream2.IgnoreBits(8); // Ignore ID_TIMESTAMP - bitStream2.Read(receivedTime); - bitStream2.IgnoreBits(8); // Ignore ID_USER_ENUM+1 - bitStream2.Read(receivedPacketNumber); - bitStream2.Read(streamNumber); - bitStream2.Read(reliability); - char *type="UNDEFINED"; - if (reliability==UNRELIABLE_SEQUENCED) - type="UNRELIABLE_SEQUENCED"; - else if (reliability==RELIABLE_ORDERED) - type="RELIABLE_ORDERED"; - - if (fp) - { - fprintf(fp, "Heap pop %i, %s. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); - fflush(fp); - } -#endif - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - - if (internalPacket->reliability == RELIABLE_ORDERED) - { - orderedReadIndex[internalPacket->orderingChannel]++; - } - else - { - highestSequencedReadIndex[internalPacket->orderingChannel] = internalPacket->sequencingIndex; - } - } - - // Done - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - else if (IsOlderOrderedPacket(internalPacket->orderingIndex,orderedReadIndex[internalPacket->orderingChannel])==false) - { - // internalPacket->_orderingIndex is greater - // If a message has a greater ordering index, and is sequenced or ordered, buffer it - // Sequenced has a lower heap weight, ordered has max sequenced weight - - // Keep orderedHoleCount count small - if (orderingHeaps[internalPacket->orderingChannel].Size()==0) - heapIndexOffsets[internalPacket->orderingChannel]=orderedReadIndex[internalPacket->orderingChannel]; - - reliabilityHeapWeightType orderedHoleCount = internalPacket->orderingIndex-heapIndexOffsets[internalPacket->orderingChannel]; - reliabilityHeapWeightType weight = orderedHoleCount*1048576; - if (internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED) - weight+=internalPacket->sequencingIndex; - else - weight+=(1048576-1); - orderingHeaps[internalPacket->orderingChannel].Push(weight, internalPacket, _FILE_AND_LINE_); - -#ifdef PRINT_TO_FILE_ROT - if (fp) - { - fprintf(fp, "Heap push %i, %s, weight=%llu. OI=%i. waiting on %i. SI=%i.\n", receivedPacketNumber, type, weight, internalPacket->orderingIndex.val, orderedReadIndex[internalPacket->orderingChannel].val, internalPacket->sequencingIndex); - fflush(fp); - } -#endif - - // Buffered, nothing to do - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - else - { - // Out of order - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - // Ignored, nothing to do - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - } - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Nothing special about this packet. Add it to the output queue - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - - internalPacket = 0; - } - - // Used for a goto to jump to the resendNext packet immediately - -CONTINUE_SOCKET_DATA_PARSE_LOOP: - // Parse the bitstream to create an internal packet - internalPacket = CreateInternalPacketFromBitStream( &socketData, timeRead ); - } - - } - - - receivePacketCount++; - - return true; -} - -//------------------------------------------------------------------------------------------------------- -// This gets an end-user packet already parsed out. Returns number of BITS put into the buffer -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::Receive( unsigned char **data ) -{ - InternalPacket * internalPacket; - - if ( outputQueue.Size() > 0 ) - { - // #ifdef _DEBUG - // RakAssert(bitStream->GetNumberOfBitsUsed()==0); - // #endif - internalPacket = outputQueue.Pop(); - - BitSize_t bitLength; - *data = internalPacket->data; - bitLength = internalPacket->dataBitLength; - ReleaseToInternalPacketPool( internalPacket ); - return bitLength; - } - - else - { - return 0; - } - -} - -//------------------------------------------------------------------------------------------------------- -// Puts data on the send queue -// bitStream contains the data to send -// priority is what priority to send the data at -// reliability is what reliability to use -// ordering channel is from 0 to 255 and specifies what stream to use -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::Send( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, CCTimeType currentTime, uint32_t receipt ) -{ -#ifdef _DEBUG - RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); - RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - RakAssert( numberOfBitsToSend > 0 ); -#endif - -#if CC_TIME_TYPE_BYTES==4 - currentTime/=1000; -#endif - - (void) MTUSize; - - // int a = BITS_TO_BYTES(numberOfBitsToSend); - - // Fix any bad parameters - if ( reliability > RELIABLE_ORDERED_WITH_ACK_RECEIPT || reliability < 0 ) - reliability = RELIABLE; - - if ( priority > NUMBER_OF_PRIORITIES || priority < 0 ) - priority = HIGH_PRIORITY; - - if ( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) - orderingChannel = 0; - - unsigned int numberOfBytesToSend=(unsigned int) BITS_TO_BYTES(numberOfBitsToSend); - if ( numberOfBitsToSend == 0 ) - { - return false; - } - InternalPacket * internalPacket = AllocateFromInternalPacketPool(); - if (internalPacket==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return false; // Out of memory - } - - bpsMetrics[(int) USER_MESSAGE_BYTES_PUSHED].Push1(currentTime,numberOfBytesToSend); - - internalPacket->creationTime = currentTime; - - if ( makeDataCopy ) - { - AllocInternalPacketData(internalPacket, numberOfBytesToSend, true, _FILE_AND_LINE_ ); - //internalPacket->data = (unsigned char*) rakMalloc_Ex( numberOfBytesToSend, _FILE_AND_LINE_ ); - memcpy( internalPacket->data, data, numberOfBytesToSend ); - } - else - { - // Allocated the data elsewhere, delete it in here - //internalPacket->data = ( unsigned char* ) data; - AllocInternalPacketData(internalPacket, (unsigned char*) data ); - } - - internalPacket->dataBitLength = numberOfBitsToSend; - internalPacket->messageInternalOrder = internalOrderIndex++; - internalPacket->priority = priority; - internalPacket->reliability = reliability; - internalPacket->sendReceiptSerial=receipt; - - // Calculate if I need to split the packet - // int headerLength = BITS_TO_BYTES( GetMessageHeaderLengthBits( internalPacket, true ) ); - - unsigned int maxDataSizeBytes = GetMaxDatagramSizeExcludingMessageHeaderBytes() - BITS_TO_BYTES(GetMaxMessageHeaderLengthBits()); - - bool splitPacket = numberOfBytesToSend > maxDataSizeBytes; - - // If a split packet, we might have to upgrade the reliability - if ( splitPacket ) - { - // Split packets cannot be unreliable, in case that one part doesn't arrive and the whole cannot be reassembled. - // One part could not arrive either due to packetloss or due to unreliable discard - if (internalPacket->reliability==UNRELIABLE) - internalPacket->reliability=RELIABLE; - else if (internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) - internalPacket->reliability=RELIABLE_WITH_ACK_RECEIPT; - else if (internalPacket->reliability==UNRELIABLE_SEQUENCED) - internalPacket->reliability=RELIABLE_SEQUENCED; -// else if (internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT) -// internalPacket->reliability=RELIABLE_SEQUENCED_WITH_ACK_RECEIPT; - } - - // ++sendMessageNumberIndex; - - if ( internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED -// || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || -// internalPacket->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - { - // Assign the sequence stream and index - internalPacket->orderingChannel = orderingChannel; - internalPacket->orderingIndex = orderedWriteIndex[ orderingChannel ]; - internalPacket->sequencingIndex = sequencedWriteIndex[ orderingChannel ]++; - - // This packet supersedes all other sequenced packets on the same ordering channel - // Delete all packets in all send lists that are sequenced and on the same ordering channel - // UPDATE: - // Disabled. We don't have enough info to consistently do this. Sometimes newer data does supercede - // older data such as with constantly declining health, but not in all cases. - // For example, with sequenced unreliable sound packets just because you send a newer one doesn't mean you - // don't need the older ones because the odds are they will still arrive in order - /* - for (int i=0; i < NUMBER_OF_PRIORITIES; i++) - { - DeleteSequencedPacketsInList(orderingChannel, sendQueue[i]); - } - */ - } - else if ( internalPacket->reliability == RELIABLE_ORDERED || internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT ) - { - // Assign the ordering channel and index - internalPacket->orderingChannel = orderingChannel; - internalPacket->orderingIndex = orderedWriteIndex[ orderingChannel ] ++; - sequencedWriteIndex[ orderingChannel ]=0; - } - - if ( splitPacket ) // If it uses a secure header it will be generated here - { - // Must split the packet. This will also generate the SHA1 if it is required. It also adds it to the send list. - //InternalPacket packetCopy; - //memcpy(&packetCopy, internalPacket, sizeof(InternalPacket)); - //sendPacketSet[priority].CancelWriteLock(internalPacket); - //SplitPacket( &packetCopy, MTUSize ); - SplitPacket( internalPacket ); - //RakNet::OP_DELETE_ARRAY(packetCopy.data, _FILE_AND_LINE_); - return true; - } - - RakAssert(internalPacket->dataBitLengthdataBitLengthmessageNumberAssigned==false); - outgoingPacketBuffer.Push( GetNextWeight(internalPacket->priority), internalPacket, _FILE_AND_LINE_ ); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]++; - statistics.bytesInSendBuffer[(int)internalPacket->priority]+=(double) BITS_TO_BYTES(internalPacket->dataBitLength); - - // sendPacketSet[priority].WriteUnlock(); - return true; -} -//------------------------------------------------------------------------------------------------------- -// Run this once per game cycle. Handles internal lists and actually does the send -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::Update( SOCKET s, SystemAddress &systemAddress, int MTUSize, CCTimeType time, - unsigned bitsPerSecondLimit, - DataStructures::List &messageHandlerList, - RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, - BitStream &updateBitStream) - -{ - (void) MTUSize; - - RakNet::TimeMS timeMs; -#if CC_TIME_TYPE_BYTES==4 - time/=1000; - timeMs=time; -#else - timeMs=(RakNet::TimeMS) (time/(CCTimeType)1000); -#endif - -#ifdef _DEBUG - while (delayList.Size()) - { - if (delayList.Peek()->sendTime <= timeMs) - { - DataAndTime *dat = delayList.Pop(); - SocketLayer::SendTo( dat->s, dat->data, dat->length, systemAddress, dat->remotePortRakNetWasStartedOn_PS3, dat->extraSocketOptions, __FILE__, __LINE__ ); - RakNet::OP_DELETE(dat,__FILE__,__LINE__); - } - break; - } -#endif - - // This line is necessary because the timer isn't accurate - if (time <= lastUpdateTime) - { - // Always set the last time in case of overflow - lastUpdateTime=time; - return; - } - - CCTimeType timeSinceLastTick = time - lastUpdateTime; - lastUpdateTime=time; -#if CC_TIME_TYPE_BYTES==4 - if (timeSinceLastTick>100) - timeSinceLastTick=100; -#else - if (timeSinceLastTick>100000) - timeSinceLastTick=100000; -#endif - - if (unreliableTimeout>0) - { - if (timeSinceLastTick>=timeToNextUnreliableCull) - { - if (unreliableLinkedListHead) - { - // Cull out all unreliable messages that have exceeded the timeout - InternalPacket *cur = unreliableLinkedListHead; - InternalPacket *end = unreliableLinkedListHead->unreliablePrev; - while (1) - { - if (time > cur->creationTime+(CCTimeType)unreliableTimeout) - { - // Flag invalid, and clear the memory. Still needs to be removed from the sendPacketSet later - // This fixes a problem where a remote system disconnects, but we don't know it yet, and memory consumption increases to a huge value - FreeInternalPacketData(cur, _FILE_AND_LINE_ ); - cur->data=0; - InternalPacket *next = cur->unreliableNext; - RemoveFromUnreliableLinkedList(cur); - - if (cur==end) - break; - - cur=next; - } - else - { - // if (cur==end) - // break; - // - // cur=cur->unreliableNext; - - // They should be inserted in-order, so no need to iterate past the first failure - break; - } - } - } - - timeToNextUnreliableCull=unreliableTimeout/(CCTimeType)2; - } - else - { - timeToNextUnreliableCull-=timeSinceLastTick; - } - } - - - // Due to thread vagarities and the way I store the time to avoid slow calls to RakNet::GetTime - // time may be less than lastAck -#if CC_TIME_TYPE_BYTES==4 - if ( statistics.messagesInResendBuffer!=0 && AckTimeout(time) ) -#else - if ( statistics.messagesInResendBuffer!=0 && AckTimeout(RakNet::TimeMS(time/(CCTimeType)1000)) ) -#endif - { - // SHOW - dead connection - // We've waited a very long time for a reliable packet to get an ack and it never has - deadConnection = true; - return; - } - - if (congestionManager.ShouldSendACKs(time,timeSinceLastTick)) - { - SendACKs(s, systemAddress, time, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); - } - - if (NAKs.Size()>0) - { - updateBitStream.Reset(); - DatagramHeaderFormat dhfNAK; - dhfNAK.isNAK=true; - dhfNAK.isACK=false; - dhfNAK.isPacketPair=false; - dhfNAK.Serialize(&updateBitStream); - NAKs.Serialize(&updateBitStream, GetMaxDatagramSizeExcludingMessageHeaderBits(), true); - SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); - } - - DatagramHeaderFormat dhf; - dhf.needsBAndAs=congestionManager.GetIsInSlowStart(); - dhf.isContinuousSend=bandwidthExceededStatistic; - // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || - // sendPacketSet[1].IsEmpty()==false || - // sendPacketSet[2].IsEmpty()==false || - // sendPacketSet[3].IsEmpty()==false; - bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; - - const bool hasDataToSendOrResend = IsResendQueueEmpty()==false || bandwidthExceededStatistic; - RakAssert(NUMBER_OF_PRIORITIES==4); - congestionManager.Update(time, hasDataToSendOrResend); - - statistics.BPSLimitByOutgoingBandwidthLimit = BITS_TO_BYTES(bitsPerSecondLimit); - statistics.BPSLimitByCongestionControl = congestionManager.GetBytesPerSecondLimitByCongestionControl(); - - unsigned int i; - if (time > lastBpsClear+ -#if CC_TIME_TYPE_BYTES==4 - 100 -#else - 100000 -#endif - ) - { - for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) - { - bpsMetrics[i].ClearExpired1(time); - } - - lastBpsClear=time; - } - - if (unreliableWithAckReceiptHistory.Size()>0) - { - i=0; - while (i < unreliableWithAckReceiptHistory.Size()) - { - if (unreliableWithAckReceiptHistory[i].nextActionTime < time) - { - InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); - AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); - ackReceipt->dataBitLength=BYTES_TO_BITS(5); - ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_LOSS; - memcpy(ackReceipt->data+sizeof(MessageID), &unreliableWithAckReceiptHistory[i].sendReceiptSerial, sizeof(uint32_t)); - outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); - - // Remove, swap with last - unreliableWithAckReceiptHistory.RemoveAtIndex(i); - } - else - i++; - } - } - - if (hasDataToSendOrResend==true) - { - InternalPacket *internalPacket; - // bool forceSend=false; - bool pushedAnything; - BitSize_t nextPacketBitLength; - dhf.isACK=false; - dhf.isNAK=false; - dhf.hasBAndAS=false; - ResetPacketsAndDatagrams(); - - int transmissionBandwidth = congestionManager.GetTransmissionBandwidth(time, timeSinceLastTick, unacknowledgedBytes,dhf.isContinuousSend); - int retransmissionBandwidth = congestionManager.GetRetransmissionBandwidth(time, timeSinceLastTick, unacknowledgedBytes,dhf.isContinuousSend); - if (retransmissionBandwidth>0 || transmissionBandwidth>0) - { - statistics.isLimitedByCongestionControl=false; - - allDatagramSizesSoFar=0; - - // Keep filling datagrams until we exceed retransmission bandwidth - while ((int)BITS_TO_BYTES(allDatagramSizesSoFar)messageNumberAssigned==true); - - if ( internalPacket->nextActionTime < time ) - { - nextPacketBitLength = internalPacket->headerLength + internalPacket->dataBitLength; - if ( datagramSizeSoFar + nextPacketBitLength > GetMaxDatagramSizeExcludingMessageHeaderBits() ) - { - // Gathers all PushPackets() - PushDatagram(); - break; - } - - PopListHead(false); - - CC_DEBUG_PRINTF_2("Rs %i ", internalPacket->reliableMessageNumber.val); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RESENT].Push1(time,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Testing1 -// if (internalPacket->reliability==RELIABLE_ORDERED || internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) -// printf("RESEND reliableMessageNumber %i with datagram %i\n", internalPacket->reliableMessageNumber.val, congestionManager.GetNextDatagramSequenceNumber().val); - - PushPacket(time,internalPacket,true); // Affects GetNewTransmissionBandwidth() -// internalPacket->timesSent++; - internalPacket->nextActionTime = congestionManager.GetRTOForRetransmission()+time; -#if CC_TIME_TYPE_BYTES==4 - if (internalPacket->nextActionTime-time > 10000) -#else - if (internalPacket->nextActionTime-time > 10000000) -#endif - { - // int a=5; - RakAssert(0); - } - - congestionManager.OnResend(time); - - pushedAnything=true; - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - { -#if CC_TIME_TYPE_BYTES==4 - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS) time, true); -#else - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000), true); -#endif - } - - // Put the packet back into the resend list at the correct spot - // Don't make a copy since I'm reinserting an allocated struct - InsertPacketIntoResendList( internalPacket, time, false, false ); - - // Removeme - // printf("Resend:%i ", internalPacket->reliableMessageNumber); - } - else - { - // Filled one datagram. - // If the 2nd and it's time to send a datagram pair, will be marked as a pair - PushDatagram(); - break; - } - } - - if (pushedAnything==false) - break; - } - } - else - { - statistics.isLimitedByCongestionControl=true; - } - - if ((int)BITS_TO_BYTES(allDatagramSizesSoFar)messageNumberAssigned==false); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthdata==0) - { - //sendPacketSet[ i ].Pop(); - outgoingPacketBuffer.Pop(0); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]--; - statistics.bytesInSendBuffer[(int)internalPacket->priority]-=(double) BITS_TO_BYTES(internalPacket->dataBitLength); - ReleaseToInternalPacketPool( internalPacket ); - continue; - } - - internalPacket->headerLength=GetMessageHeaderLengthBits(internalPacket); - nextPacketBitLength = internalPacket->headerLength + internalPacket->dataBitLength; - if ( datagramSizeSoFar + nextPacketBitLength > GetMaxDatagramSizeExcludingMessageHeaderBits() ) - { - // Hit MTU. May still push packets if smaller ones exist at a lower priority - RakAssert(datagramSizeSoFar!=0); - RakAssert(internalPacket->dataBitLengthreliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - isReliable = true; - else - isReliable = false; - - //sendPacketSet[ i ].Pop(); - outgoingPacketBuffer.Pop(0); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthmessageNumberAssigned==false); - statistics.messageInSendBuffer[(int)internalPacket->priority]--; - statistics.bytesInSendBuffer[(int)internalPacket->priority]-=(double) BITS_TO_BYTES(internalPacket->dataBitLength); - if (isReliable - /* - I thought about this and agree that UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT and RELIABLE_SEQUENCED_WITH_ACK_RECEIPT is not useful unless you also know if the message was discarded. - - The problem is that internally, message numbers are only assigned to reliable messages, because message numbers are only used to discard duplicate message receipt and only reliable messages get sent more than once. However, without message numbers getting assigned and transmitted, there is no way to tell the sender about which messages were discarded. In fact, in looking this over I realized that UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT introduced a bug, because the remote system assumes all message numbers are used (no holes). With that send type, on packetloss, a permanent hole would have been created which eventually would cause the system to discard all further packets. - - So I have two options. Either do not support ack receipts when sending sequenced, or write complex and major new systems. UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT would need to send the message ID number on a special channel which allows for non-delivery. And both of them would need to have a special range list to indicate which message numbers were not delivered, so when acks are sent that can be indicated as well. A further problem is that the ack itself can be lost - it is possible that the message can arrive but be discarded, yet the ack is lost. On resend, the resent message would be ignored as duplicate, and you'd never get the discard message either (unless I made a special buffer for that case too). -*/ -// || - // If needs an ack receipt, keep the internal packet around in the list -// internalPacket->reliability == UNRELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - { - internalPacket->messageNumberAssigned=true; - internalPacket->reliableMessageNumber=sendReliableMessageNumberIndex; - internalPacket->nextActionTime = congestionManager.GetRTOForRetransmission()+time; -#if CC_TIME_TYPE_BYTES==4 - const CCTimeType threshhold = 10000; -#else - const CCTimeType threshhold = 10000000; -#endif - if (internalPacket->nextActionTime-time > threshhold) - { - // int a=5; - RakAssert(time-internalPacket->nextActionTime < threshhold); - } - //resendTree.Insert( internalPacket->reliableMessageNumber, internalPacket); - if (resendBuffer[internalPacket->reliableMessageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK]!=0) - { - // bool overflow = ResendBufferOverflow(); - RakAssert(0); - } - resendBuffer[internalPacket->reliableMessageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK] = internalPacket; - statistics.messagesInResendBuffer++; - statistics.bytesInResendBuffer+=BITS_TO_BYTES(internalPacket->dataBitLength); - - // printf("pre:%i ", unacknowledgedBytes); - - InsertPacketIntoResendList( internalPacket, time, true, isReliable); - - - // printf("post:%i ", unacknowledgedBytes); - sendReliableMessageNumberIndex++; - } - else if (internalPacket->reliability == UNRELIABLE_WITH_ACK_RECEIPT) - { - unreliableWithAckReceiptHistory.Push(UnreliableWithAckReceiptNode( - congestionManager.GetNextDatagramSequenceNumber() + packetsToSendThisUpdateDatagramBoundaries.Size(), - internalPacket->sendReceiptSerial, - congestionManager.GetRTOForRetransmission()+time - ), _FILE_AND_LINE_); - } - -// internalPacket->timesSent=1; - // If isReliable is false, the packet and its contents will be added to a list to be freed in ClearPacketsAndDatagrams - // However, the internalPacket structure will remain allocated and be in the resendBuffer list if it requires a receipt - bpsMetrics[(int) USER_MESSAGE_BYTES_SENT].Push1(time,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Testing1 -// if (internalPacket->reliability==RELIABLE_ORDERED || internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) -// printf("SEND reliableMessageNumber %i in datagram %i\n", internalPacket->reliableMessageNumber.val, congestionManager.GetNextDatagramSequenceNumber().val); - - PushPacket(time,internalPacket, isReliable); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - { -#if CC_TIME_TYPE_BYTES==4 - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)time, true); -#else - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000), true); -#endif - } - pushedAnything=true; - - if (ResendBufferOverflow()) - break; - } - // if (ResendBufferOverflow()) - // break; - // }z - - // No datagrams pushed? - if (datagramSizeSoFar==0) - break; - - // Filled one datagram. - // If the 2nd and it's time to send a datagram pair, will be marked as a pair - PushDatagram(); - } - } - - - for (unsigned int datagramIndex=0; datagramIndex < packetsToSendThisUpdateDatagramBoundaries.Size(); datagramIndex++) - { - if (datagramIndex>0) - dhf.isContinuousSend=true; - MessageNumberNode* messageNumberNode = 0; - dhf.datagramNumber=congestionManager.GetAndIncrementNextDatagramSequenceNumber(); - dhf.isPacketPair=datagramsToSendThisUpdateIsPair[datagramIndex]; - - //printf("%p pushing datagram %i\n", this, dhf.datagramNumber.val); - - bool isSecondOfPacketPair=dhf.isPacketPair && datagramIndex>0 && datagramsToSendThisUpdateIsPair[datagramIndex-1]; - unsigned int msgIndex, msgTerm; - if (datagramIndex==0) - { - msgIndex=0; - msgTerm=packetsToSendThisUpdateDatagramBoundaries[0]; - } - else - { - msgIndex=packetsToSendThisUpdateDatagramBoundaries[datagramIndex-1]; - msgTerm=packetsToSendThisUpdateDatagramBoundaries[datagramIndex]; - } - - // More accurate time to reset here -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - dhf.sourceSystemTime=RakNet::GetTimeUS(); -#endif - updateBitStream.Reset(); - dhf.Serialize(&updateBitStream); - CC_DEBUG_PRINTF_2("S%i ",dhf.datagramNumber.val); - - while (msgIndex < msgTerm) - { - // If reliable or needs receipt - if ( packetsToSendThisUpdate[msgIndex]->reliability != UNRELIABLE && - packetsToSendThisUpdate[msgIndex]->reliability != UNRELIABLE_SEQUENCED - ) - { - if (messageNumberNode==0) - { - messageNumberNode = AddFirstToDatagramHistory(dhf.datagramNumber, packetsToSendThisUpdate[msgIndex]->reliableMessageNumber, time); - } - else - { - messageNumberNode = AddSubsequentToDatagramHistory(messageNumberNode, packetsToSendThisUpdate[msgIndex]->reliableMessageNumber); - } - } - - RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - WriteToBitStreamFromInternalPacket( &updateBitStream, packetsToSendThisUpdate[msgIndex], time ); - RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - msgIndex++; - } - - if (isSecondOfPacketPair) - { - // Pad to size of first datagram - RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - updateBitStream.PadWithZeroToByteLength(datagramSizesInBytes[datagramIndex-1]); - RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - } - - if (messageNumberNode==0) - { - // Unreliable, add dummy node - AddFirstToDatagramHistory(dhf.datagramNumber, time); - } - - // Store what message ids were sent with this datagram - // datagramMessageIDTree.Insert(dhf.datagramNumber,idList); - - congestionManager.OnSendBytes(time,UDP_HEADER_SIZE+DatagramHeaderFormat::GetDataHeaderByteLength()); - - SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); - - bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; - // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || - // sendPacketSet[1].IsEmpty()==false || - // sendPacketSet[2].IsEmpty()==false || - // sendPacketSet[3].IsEmpty()==false; - - - - if (bandwidthExceededStatistic==true) - timeOfLastContinualSend=time; - else - timeOfLastContinualSend=0; - } - - ClearPacketsAndDatagrams(); - - // Any data waiting to send after attempting to send, then bandwidth is exceeded - bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; - // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || - // sendPacketSet[1].IsEmpty()==false || - // sendPacketSet[2].IsEmpty()==false || - // sendPacketSet[3].IsEmpty()==false; - } - - - // Keep on top of deleting old unreliable split packets so they don't clog the list. - //DeleteOldUnreliableSplitPackets( time ); -} - -//------------------------------------------------------------------------------------------------------- -// Writes a bitstream to the socket -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SendBitStream( SOCKET s, SystemAddress &systemAddress, RakNet::BitStream *bitStream, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType currentTime) -{ - (void) systemAddress; - (void) rnr; - - unsigned int length; - - length = (unsigned int) bitStream->GetNumberOfBytesUsed(); - - -#ifdef _DEBUG - if (packetloss > 0.0) - { - if (frandomMT() < packetloss) - return; - } - - if (minExtraPing > 0 || extraPingVariance > 0) - { - RakNet::TimeMS delay = minExtraPing; - if (extraPingVariance>0) - delay += (randomMT() % extraPingVariance); - if (delay > 0) - { - DataAndTime *dat = RakNet::OP_NEW(__FILE__,__LINE__); - memcpy(dat->data, ( char* ) bitStream->GetData(), length ); - dat->s=s; - dat->length=length; - dat->sendTime = RakNet::GetTimeMS() + delay; - dat->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; - dat->extraSocketOptions=extraSocketOptions; - for (unsigned int i=0; i < delayList.Size(); i++) - { - if (dat->sendTime < delayList[i]->sendTime) - { - delayList.PushAtHead(dat, i, __FILE__, __LINE__); - dat=0; - break; - } - } - if (dat!=0) - delayList.Push(dat,__FILE__,__LINE__); - return; - } - } -#endif - -#if LIBCAT_SECURITY==1 - if (useSecurity) - { - unsigned char *buffer = reinterpret_cast( bitStream->GetData() ); - - int buffer_size = bitStream->GetNumberOfBitsAllocated() / 8; - - // Verify there is enough room for encrypted output and encrypt - // Encrypt() will increase length - bool success = auth_enc.Encrypt(buffer, buffer_size, length); - RakAssert(success); - } -#endif - - bpsMetrics[(int) ACTUAL_BYTES_SENT].Push1(currentTime,length); - - RakAssert(length <= congestionManager.GetMTU()); - -#ifdef USE_THREADED_SEND - SendToThread::SendToThreadBlock *block = SendToThread::AllocateBlock(); - memcpy(block->data, bitStream->GetData(), length); - block->dataWriteOffset=length; - block->extraSocketOptions=extraSocketOptions; - block->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; - block->s=s; - block->systemAddress=systemAddress; - SendToThread::ProcessBlock(block); -#else - SocketLayer::SendTo( s, ( char* ) bitStream->GetData(), length, systemAddress, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, __FILE__, __LINE__ ); -#endif -} - -//------------------------------------------------------------------------------------------------------- -// Are we waiting for any data to be sent out or be processed by the player? -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsOutgoingDataWaiting(void) -{ - if (outgoingPacketBuffer.Size()>0) - return true; - - // unsigned i; - // for ( i = 0; i < NUMBER_OF_PRIORITIES; i++ ) - // { - // if (sendPacketSet[ i ].Size() > 0) - // return true; - // } - - return - //acknowlegements.Size() > 0 || - //resendTree.IsEmpty()==false;// || outputQueue.Size() > 0 || orderingList.Size() > 0 || splitPacketChannelList.Size() > 0; - statistics.messagesInResendBuffer!=0; -} -bool ReliabilityLayer::AreAcksWaiting(void) -{ - return acknowlegements.Size() > 0; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ApplyNetworkSimulator( double _packetloss, RakNet::TimeMS _minExtraPing, RakNet::TimeMS _extraPingVariance ) -{ -#ifdef _DEBUG - packetloss=_packetloss; - minExtraPing=_minExtraPing; - extraPingVariance=_extraPingVariance; - // if (ping < (unsigned int)(minExtraPing+extraPingVariance)*2) - // ping=(minExtraPing+extraPingVariance)*2; -#endif -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SetSplitMessageProgressInterval(int interval) -{ - splitMessageProgressInterval=interval; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SetUnreliableTimeout(RakNet::TimeMS timeoutMS) -{ -#if CC_TIME_TYPE_BYTES==4 - unreliableTimeout=timeoutMS; -#else - unreliableTimeout=(CCTimeType)timeoutMS*(CCTimeType)1000; -#endif -} - -//------------------------------------------------------------------------------------------------------- -// This will return true if we should not send at this time -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsSendThrottled( int MTUSize ) -{ - (void) MTUSize; - - return false; - // return resendList.Size() > windowSize; - - // Disabling this, because it can get stuck here forever - /* - unsigned packetsWaiting; - unsigned resendListDataSize=0; - unsigned i; - for (i=0; i < resendList.Size(); i++) - { - if (resendList[i]) - resendListDataSize+=resendList[i]->dataBitLength; - } - packetsWaiting = 1 + ((BITS_TO_BYTES(resendListDataSize)) / (MTUSize - UDP_HEADER_SIZE - 10)); // 10 to roughly estimate the raknet header - - return packetsWaiting >= windowSize; - */ -} - -//------------------------------------------------------------------------------------------------------- -// We lost a packet -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::UpdateWindowFromPacketloss( CCTimeType time ) -{ - (void) time; -} - -//------------------------------------------------------------------------------------------------------- -// Increase the window size -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::UpdateWindowFromAck( CCTimeType time ) -{ - (void) time; -} - -//------------------------------------------------------------------------------------------------------- -// Does what the function name says -//------------------------------------------------------------------------------------------------------- -unsigned ReliabilityLayer::RemovePacketFromResendListAndDeleteOlderReliableSequenced( const MessageNumberType messageNumber, CCTimeType time, DataStructures::List &messageHandlerList, const SystemAddress &systemAddress ) -{ - (void) time; - (void) messageNumber; - InternalPacket * internalPacket; - //InternalPacket *temp; -// PacketReliability reliability; // What type of reliability algorithm to use with this packet -// unsigned char orderingChannel; // What ordering channel this packet is on, if the reliability type uses ordering channels -// OrderingIndexType orderingIndex; // The ID used as identification for ordering channels - // unsigned j; - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - { -#if CC_TIME_TYPE_BYTES==4 - messageHandlerList[messageHandlerIndex]->OnAck(messageNumber, systemAddress, time); -#else - messageHandlerList[messageHandlerIndex]->OnAck(messageNumber, systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000)); -#endif - } - - // Testing1 -// if (resendLinkedListHead) -// { -// InternalPacket *internalPacket = resendLinkedListHead; -// do -// { -// internalPacket=internalPacket->resendNext; -// printf("%i ", internalPacket->reliableMessageNumber.val); -// } while (internalPacket!=resendLinkedListHead); -// printf("\n"); -// } - - // bool deleted; - // deleted=resendTree.Delete(messageNumber, internalPacket); - internalPacket = resendBuffer[messageNumber & RESEND_BUFFER_ARRAY_MASK]; - // May ask to remove twice, for example resend twice, then second ack - if (internalPacket && internalPacket->reliableMessageNumber==messageNumber) - { - // ValidateResendList(); - resendBuffer[messageNumber & RESEND_BUFFER_ARRAY_MASK]=0; - CC_DEBUG_PRINTF_2("AckRcv %i ", messageNumber); - - statistics.messagesInResendBuffer--; - statistics.bytesInResendBuffer-=BITS_TO_BYTES(internalPacket->dataBitLength); - -// orderingIndex = internalPacket->orderingIndex; - totalUserDataBytesAcked+=(double) BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); - - // Return receipt if asked for - if (internalPacket->reliability>=RELIABLE_WITH_ACK_RECEIPT && - (internalPacket->splitPacketCount==0 || internalPacket->splitPacketIndex+1==internalPacket->splitPacketCount) - ) - { - InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); - AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); - ackReceipt->dataBitLength=BYTES_TO_BITS(5); - ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_ACKED; - memcpy(ackReceipt->data+sizeof(MessageID), &internalPacket->sendReceiptSerial, sizeof(internalPacket->sendReceiptSerial)); - outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); - } - - bool isReliable; - if ( internalPacket->reliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - isReliable = true; - else - isReliable = false; - - RemoveFromList(internalPacket, isReliable); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - - return 0; - } - else - { - - } - - return (unsigned)-1; -} - -//------------------------------------------------------------------------------------------------------- -// Acknowledge receipt of the packet with the specified messageNumber -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SendAcknowledgementPacket( const DatagramSequenceNumberType messageNumber, CCTimeType time ) -{ - - // REMOVEME - // printf("%p Send ack %i\n", this, messageNumber.val); - - nextAckTimeToSend=time; - acknowlegements.Insert(messageNumber); - - //printf("ACK_DG:%i ", messageNumber.val); - - CC_DEBUG_PRINTF_2("AckPush %i ", messageNumber); - -} - -//------------------------------------------------------------------------------------------------------- -// Parse an internalPacket and figure out how many header bits would be -// written. Returns that number -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::GetMaxMessageHeaderLengthBits( void ) -{ - InternalPacket ip; - ip.reliability=RELIABLE_SEQUENCED; - ip.splitPacketCount=1; - return GetMessageHeaderLengthBits(&ip); -} -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::GetMessageHeaderLengthBits( const InternalPacket *const internalPacket ) -{ - BitSize_t bitLength; - - // bitStream->AlignWriteToByteBoundary(); // Potentially unaligned - // tempChar=(unsigned char)internalPacket->reliability; bitStream->WriteBits( (const unsigned char *)&tempChar, 3, true ); // 3 bits to write reliability. - // bool hasSplitPacket = internalPacket->splitPacketCount>0; bitStream->Write(hasSplitPacket); // Write 1 bit to indicate if splitPacketCount>0 - bitLength = 8*1; - - // bitStream->AlignWriteToByteBoundary(); - // RakAssert(internalPacket->dataBitLength < 65535); - // unsigned short s; s = (unsigned short) internalPacket->dataBitLength; bitStream->WriteAlignedVar16((const char*)& s); - bitLength += 8*2; - - if ( internalPacket->reliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - bitLength += 8*3; // bitStream->Write(internalPacket->reliableMessageNumber); // Message sequence number - // bitStream->AlignWriteToByteBoundary(); // Potentially nothing else to write - - - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED - ) - { - bitLength += 8*3;; // bitStream->Write(internalPacket->_sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - } - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - { - bitLength += 8*3; // bitStream->Write(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - bitLength += 8*1; // tempChar=internalPacket->orderingChannel; bitStream->WriteAlignedVar8((const char*)& tempChar); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, write one byte - } - if (internalPacket->splitPacketCount>0) - { - bitLength += 8*4; // bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketCount); RakAssert(sizeof(SplitPacketIndexType)==4); // Only needed if splitPacketCount>0. 4 bytes - bitLength += 8*sizeof(SplitPacketIdType); // bitStream->WriteAlignedVar16((const char*)& internalPacket->splitPacketId); RakAssert(sizeof(SplitPacketIdType)==2); // Only needed if splitPacketCount>0. - bitLength += 8*4; // bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes - } - - return bitLength; -} - -//------------------------------------------------------------------------------------------------------- -// Parse an internalPacket and create a bitstream to represent this data -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket, CCTimeType curTime ) -{ - (void) curTime; - - BitSize_t start = bitStream->GetNumberOfBitsUsed(); - unsigned char tempChar; - - // (Incoming data may be all zeros due to padding) - bitStream->AlignWriteToByteBoundary(); // Potentially unaligned - if (internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) - tempChar=UNRELIABLE; - else if (internalPacket->reliability==RELIABLE_WITH_ACK_RECEIPT) - tempChar=RELIABLE; - else if (internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) - tempChar=RELIABLE_ORDERED; - else - tempChar=(unsigned char)internalPacket->reliability; - - bitStream->WriteBits( (const unsigned char *)&tempChar, 3, true ); // 3 bits to write reliability. - - bool hasSplitPacket = internalPacket->splitPacketCount>0; bitStream->Write(hasSplitPacket); // Write 1 bit to indicate if splitPacketCount>0 - bitStream->AlignWriteToByteBoundary(); - RakAssert(internalPacket->dataBitLength < 65535); - unsigned short s; s = (unsigned short) internalPacket->dataBitLength; bitStream->WriteAlignedVar16((const char*)& s); - if ( internalPacket->reliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - bitStream->Write(internalPacket->reliableMessageNumber); // Used for all reliable types - bitStream->AlignWriteToByteBoundary(); // Potentially nothing else to write - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED - ) - { - bitStream->Write(internalPacket->sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - } - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - { - bitStream->Write(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - tempChar=internalPacket->orderingChannel; bitStream->WriteAlignedVar8((const char*)& tempChar); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, write one byte - } - - if (internalPacket->splitPacketCount>0) - { - // printf("Write before\n"); - // bitStream->PrintBits(); - - bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketCount); RakAssert(sizeof(SplitPacketIndexType)==4); // Only needed if splitPacketCount>0. 4 bytes - bitStream->WriteAlignedVar16((const char*)& internalPacket->splitPacketId); RakAssert(sizeof(SplitPacketIdType)==2); // Only needed if splitPacketCount>0. - bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes - - // printf("Write after\n"); - // bitStream->PrintBits(); - } - - // Write the actual data. - bitStream->WriteAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); - - return bitStream->GetNumberOfBitsUsed() - start; -} - -//------------------------------------------------------------------------------------------------------- -// Parse a bitstream and create an internal packet to represent this data -//------------------------------------------------------------------------------------------------------- -InternalPacket* ReliabilityLayer::CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, CCTimeType time ) -{ - bool bitStreamSucceeded; - InternalPacket* internalPacket; - unsigned char tempChar; - bool hasSplitPacket=false; - bool readSuccess; - - if ( bitStream->GetNumberOfUnreadBits() < (int) sizeof( internalPacket->reliableMessageNumber ) * 8 ) - return 0; // leftover bits - - internalPacket = AllocateFromInternalPacketPool(); - if (internalPacket==0) - { - // Out of memory - RakAssert(0); - return 0; - } - internalPacket->creationTime = time; - - // (Incoming data may be all zeros due to padding) - bitStream->AlignReadToByteBoundary(); // Potentially unaligned - bitStream->ReadBits( ( unsigned char* ) ( &( tempChar ) ), 3 ); - internalPacket->reliability = ( const PacketReliability ) tempChar; - readSuccess=bitStream->Read(hasSplitPacket); // Read 1 bit to indicate if splitPacketCount>0 - bitStream->AlignReadToByteBoundary(); - unsigned short s; bitStream->ReadAlignedVar16((char*)&s); internalPacket->dataBitLength=s; // Length of message (2 bytes) - if ( internalPacket->reliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED - // I don't write ACK_RECEIPT to the remote system -// || -// internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - bitStream->Read(internalPacket->reliableMessageNumber); // Message sequence number - else - internalPacket->reliableMessageNumber=(MessageNumberType)(const uint32_t)-1; - bitStream->AlignReadToByteBoundary(); // Potentially nothing else to Read - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED - ) - { - bitStream->Read(internalPacket->sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - } - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - { - bitStream->Read(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 4 bytes. - readSuccess=bitStream->ReadAlignedVar8((char*)& internalPacket->orderingChannel); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, Read one byte - } - else - internalPacket->orderingChannel=0; - - if (hasSplitPacket) - { -// printf("Read before\n"); -// bitStream->PrintBits(); - - bitStream->ReadAlignedVar32((char*)& internalPacket->splitPacketCount); // Only needed if splitPacketCount>0. 4 bytes - bitStream->ReadAlignedVar16((char*)& internalPacket->splitPacketId); // Only needed if splitPacketCount>0. - readSuccess=bitStream->ReadAlignedVar32((char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes - RakAssert(readSuccess); - -// printf("Read after\n"); -// bitStream->PrintBits(); - } - else - { - internalPacket->splitPacketCount=0; - } - - if (readSuccess==false || - internalPacket->dataBitLength==0 || - internalPacket->reliability>=NUMBER_OF_RELIABILITIES || - internalPacket->orderingChannel>=32 || - (hasSplitPacket && (internalPacket->splitPacketIndex >= internalPacket->splitPacketCount))) - { - // If this assert hits, encoding is garbage - RakAssert("Encoding is garbage" && 0); - ReleaseToInternalPacketPool( internalPacket ); - return 0; - } - - // Allocate memory to hold our data - AllocInternalPacketData(internalPacket, BITS_TO_BYTES( internalPacket->dataBitLength ), false, _FILE_AND_LINE_ ); - RakAssert(BITS_TO_BYTES( internalPacket->dataBitLength )data == 0) - { - RakAssert("Out of memory in ReliabilityLayer::CreateInternalPacketFromBitStream" && 0); - notifyOutOfMemory(_FILE_AND_LINE_); - ReleaseToInternalPacketPool( internalPacket ); - return 0; - } - - // Set the last byte to 0 so if ReadBits does not read a multiple of 8 the last bits are 0'ed out - internalPacket->data[ BITS_TO_BYTES( internalPacket->dataBitLength ) - 1 ] = 0; - - // Read the data the packet holds - bitStreamSucceeded = bitStream->ReadAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); - - if ( bitStreamSucceeded == false ) - { - // If this hits, most likely the variable buff is too small in RunUpdateCycle in RakPeer.cpp - RakAssert("Couldn't read all the data" && 0); - - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - return 0; - } - - return internalPacket; -} - - -//------------------------------------------------------------------------------------------------------- -// Get the SHA1 code -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::GetSHA1( unsigned char * const buffer, unsigned int - nbytes, char code[ SHA1_LENGTH ] ) -{ - CSHA1 sha1; - - sha1.Reset(); - sha1.Update( ( unsigned char* ) buffer, nbytes ); - sha1.Final(); - memcpy( code, sha1.GetHash(), SHA1_LENGTH ); -} - -//------------------------------------------------------------------------------------------------------- -// Check the SHA1 code -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * - const buffer, unsigned int nbytes ) -{ - char code2[ SHA1_LENGTH ]; - GetSHA1( buffer, nbytes, code2 ); - - for ( int i = 0; i < SHA1_LENGTH; i++ ) - if ( code[ i ] != code2[ i ] ) - return false; - - return true; -} - -//------------------------------------------------------------------------------------------------------- -// Search the specified list for sequenced packets on the specified ordering -// stream, optionally skipping those with splitPacketId, and delete them -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::List&theList, int splitPacketId ) -{ - unsigned i = 0; - - while ( i < theList.Size() ) - { - if ( ( - theList[ i ]->reliability == RELIABLE_SEQUENCED || - theList[ i ]->reliability == UNRELIABLE_SEQUENCED -// || -// theList[ i ]->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || -// theList[ i ]->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) && - theList[ i ]->orderingChannel == orderingChannel && ( splitPacketId == -1 || theList[ i ]->splitPacketId != (unsigned int) splitPacketId ) ) - { - InternalPacket * internalPacket = theList[ i ]; - theList.RemoveAtIndex( i ); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - } - - else - i++; - } -} - -//------------------------------------------------------------------------------------------------------- -// Search the specified list for sequenced packets with a value less than orderingIndex and delete them -// Note - I added functionality so you can use the Queue as a list (in this case for searching) but it is less efficient to do so than a regular list -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::Queue&theList ) -{ - InternalPacket * internalPacket; - int listSize = theList.Size(); - int i = 0; - - while ( i < listSize ) - { - if ( ( - theList[ i ]->reliability == RELIABLE_SEQUENCED || - theList[ i ]->reliability == UNRELIABLE_SEQUENCED -// || -// theList[ i ]->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || -// theList[ i ]->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) && theList[ i ]->orderingChannel == orderingChannel ) - { - internalPacket = theList[ i ]; - theList.RemoveAtIndex( i ); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - listSize--; - } - - else - i++; - } -} - -//------------------------------------------------------------------------------------------------------- -// Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ) -{ - OrderingIndexType maxRange = (OrderingIndexType) (const uint32_t)-1; - - if ( waitingForPacketOrderingIndex > maxRange/(OrderingIndexType)2 ) - { - if ( newPacketOrderingIndex >= waitingForPacketOrderingIndex - maxRange/(OrderingIndexType)2+(OrderingIndexType)1 && newPacketOrderingIndex < waitingForPacketOrderingIndex ) - { - return true; - } - } - - else - if ( newPacketOrderingIndex >= ( OrderingIndexType ) ( waitingForPacketOrderingIndex - (( OrderingIndexType ) maxRange/(OrderingIndexType)2+(OrderingIndexType)1) ) || - newPacketOrderingIndex < waitingForPacketOrderingIndex ) - { - return true; - } - - // Old packet - return false; -} - -//------------------------------------------------------------------------------------------------------- -// Split the passed packet into chunks under MTU_SIZEbytes (including headers) and save those new chunks -// Optimized version -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SplitPacket( InternalPacket *internalPacket ) -{ - // Doing all sizes in bytes in this function so I don't write partial bytes with split packets - internalPacket->splitPacketCount = 1; // This causes GetMessageHeaderLengthBits to account for the split packet header - unsigned int headerLength = (unsigned int) BITS_TO_BYTES( GetMessageHeaderLengthBits( internalPacket ) ); - unsigned int dataByteLength = (unsigned int) BITS_TO_BYTES( internalPacket->dataBitLength ); - int maximumSendBlockBytes, byteOffset, bytesToSend; - SplitPacketIndexType splitPacketIndex; - int i; - InternalPacket **internalPacketArray; - - maximumSendBlockBytes = GetMaxDatagramSizeExcludingMessageHeaderBytes() - BITS_TO_BYTES(GetMaxMessageHeaderLengthBits()); - - // Calculate how many packets we need to create - internalPacket->splitPacketCount = ( ( dataByteLength - 1 ) / ( maximumSendBlockBytes ) + 1 ); - - // Optimization - // internalPacketArray = RakNet::OP_NEW(internalPacket->splitPacketCount, _FILE_AND_LINE_ ); - bool usedAlloca=false; - - if (sizeof( InternalPacket* ) * internalPacket->splitPacketCount < MAX_ALLOCA_STACK_ALLOCATION) - { - internalPacketArray = ( InternalPacket** ) alloca( sizeof( InternalPacket* ) * internalPacket->splitPacketCount ); - usedAlloca=true; - } - else - - internalPacketArray = (InternalPacket**) rakMalloc_Ex( sizeof(InternalPacket*) * internalPacket->splitPacketCount, _FILE_AND_LINE_ ); - - for ( i = 0; i < ( int ) internalPacket->splitPacketCount; i++ ) - { - internalPacketArray[ i ] = AllocateFromInternalPacketPool(); - - //internalPacketArray[ i ] = (InternalPacket*) alloca( sizeof( InternalPacket ) ); - // internalPacketArray[ i ] = sendPacketSet[internalPacket->priority].WriteLock(); - *internalPacketArray[ i ]=*internalPacket; - internalPacketArray[ i ]->messageNumberAssigned=false; - - if (i!=0) - internalPacket->messageInternalOrder = internalOrderIndex++; - } - - // This identifies which packet this is in the set - splitPacketIndex = 0; - - InternalPacketRefCountedData *refCounter=0; - - // Do a loop to send out all the packets - do - { - byteOffset = splitPacketIndex * maximumSendBlockBytes; - bytesToSend = dataByteLength - byteOffset; - - if ( bytesToSend > maximumSendBlockBytes ) - bytesToSend = maximumSendBlockBytes; - - // Copy over our chunk of data - - AllocInternalPacketData(internalPacketArray[ splitPacketIndex ], &refCounter, internalPacket->data, internalPacket->data + byteOffset); - // internalPacketArray[ splitPacketIndex ]->data = (unsigned char*) rakMalloc_Ex( bytesToSend, _FILE_AND_LINE_ ); - // memcpy( internalPacketArray[ splitPacketIndex ]->data, internalPacket->data + byteOffset, bytesToSend ); - - if ( bytesToSend != maximumSendBlockBytes ) - internalPacketArray[ splitPacketIndex ]->dataBitLength = internalPacket->dataBitLength - splitPacketIndex * ( maximumSendBlockBytes << 3 ); - else - internalPacketArray[ splitPacketIndex ]->dataBitLength = bytesToSend << 3; - - internalPacketArray[ splitPacketIndex ]->splitPacketIndex = splitPacketIndex; - internalPacketArray[ splitPacketIndex ]->splitPacketId = splitPacketId; - internalPacketArray[ splitPacketIndex ]->splitPacketCount = internalPacket->splitPacketCount; - RakAssert(internalPacketArray[ splitPacketIndex ]->dataBitLengthsplitPacketCount ); - - splitPacketId++; // It's ok if this wraps to 0 - - // InternalPacket *workingPacket; - - // Tell the heap we are going to push a list of elements where each element in the list follows the heap order - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthsplitPacketCount; i++ ) - { - internalPacketArray[ i ]->headerLength=headerLength; - RakAssert(internalPacketArray[ i ]->dataBitLengthpriority ].Push( internalPacketArray[ i ], _FILE_AND_LINE_ ); - RakAssert(internalPacketArray[ i ]->dataBitLengthmessageNumberAssigned==false); - outgoingPacketBuffer.PushSeries(GetNextWeight(internalPacketArray[ i ]->priority), internalPacketArray[ i ], _FILE_AND_LINE_); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]++; - statistics.bytesInSendBuffer[(int)(int)internalPacketArray[ i ]->priority]+=(double) BITS_TO_BYTES(internalPacketArray[ i ]->dataBitLength); - // workingPacket=sendPacketSet[internalPacket->priority].WriteLock(); - // memcpy(workingPacket, internalPacketArray[ i ], sizeof(InternalPacket)); - // sendPacketSet[internalPacket->priority].WriteUnlock(); - } - - // Do not delete, original is referenced by all split packets to avoid numerous allocations. See AllocInternalPacketData above - // FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - if (usedAlloca==false) - rakFree_Ex(internalPacketArray, _FILE_AND_LINE_ ); -} - -//------------------------------------------------------------------------------------------------------- -// Insert a packet into the split packet list -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::InsertIntoSplitPacketList( InternalPacket * internalPacket, CCTimeType time ) -{ - bool objectExists; - unsigned index; - index=splitPacketChannelList.GetIndexFromKey(internalPacket->splitPacketId, &objectExists); - if (objectExists==false) - { - SplitPacketChannel *newChannel = RakNet::OP_NEW( __FILE__, __LINE__ ); -#if PREALLOCATE_LARGE_MESSAGES==1 - index=splitPacketChannelList.Insert(internalPacket->splitPacketId, newChannel, true, __FILE__,__LINE__); - newChannel->returnedPacket=CreateInternalPacketCopy( internalPacket, 0, 0, time ); - newChannel->gotFirstPacket=false; - newChannel->splitPacketsArrived=0; - AllocInternalPacketData(newChannel->returnedPacket, BITS_TO_BYTES( internalPacket->dataBitLength*internalPacket->splitPacketCount ), false, __FILE__, __LINE__ ); - RakAssert(newChannel->returnedPacket->data); -#else - newChannel->firstPacket=0; - index=splitPacketChannelList.Insert(internalPacket->splitPacketId, newChannel, true, __FILE__,__LINE__); - // Preallocate to the final size, to avoid runtime copies - newChannel->splitPacketList.Preallocate(internalPacket->splitPacketCount, __FILE__,__LINE__); - -#endif - } - -#if PREALLOCATE_LARGE_MESSAGES==1 - splitPacketChannelList[index]->lastUpdateTime=time; - splitPacketChannelList[index]->splitPacketsArrived++; - splitPacketChannelList[index]->returnedPacket->dataBitLength+=internalPacket->dataBitLength; - - bool dealloc; - if (internalPacket->splitPacketIndex==0) - { - splitPacketChannelList[index]->gotFirstPacket=true; - splitPacketChannelList[index]->stride=BITS_TO_BYTES(internalPacket->dataBitLength); - - for (unsigned int j=0; j < splitPacketChannelList[index]->splitPacketList.Size(); j++) - { - memcpy(splitPacketChannelList[index]->returnedPacket->data+internalPacket->splitPacketIndex*splitPacketChannelList[index]->stride, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); - FreeInternalPacketData(splitPacketChannelList[index]->splitPacketList[j], __FILE__, __LINE__ ); - ReleaseToInternalPacketPool(splitPacketChannelList[index]->splitPacketList[j]); - } - - memcpy(splitPacketChannelList[index]->returnedPacket->data, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); - splitPacketChannelList[index]->splitPacketList.Clear(true,__FILE__,__LINE__); - dealloc=true; - } - else - { - if (splitPacketChannelList[index]->gotFirstPacket==true) - { - memcpy(splitPacketChannelList[index]->returnedPacket->data+internalPacket->splitPacketIndex*splitPacketChannelList[index]->stride, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); - dealloc=true; - } - else - { - splitPacketChannelList[index]->splitPacketList.Push(internalPacket,__FILE__,__LINE__); - dealloc=false; - } - } - - if (splitPacketChannelList[index]->gotFirstPacket==true && - splitMessageProgressInterval && - // splitPacketChannelList[index]->firstPacket && - // splitPacketChannelList[index]->splitPacketList.Size()!=splitPacketChannelList[index]->firstPacket->splitPacketCount && - // (splitPacketChannelList[index]->splitPacketList.Size()%splitMessageProgressInterval)==0 - splitPacketChannelList[index]->gotFirstPacket && - splitPacketChannelList[index]->splitPacketsArrived!=splitPacketChannelList[index]->returnedPacket->splitPacketCount && - (splitPacketChannelList[index]->splitPacketsArrived%splitMessageProgressInterval)==0 - ) - { - // Return ID_DOWNLOAD_PROGRESS - // Write splitPacketIndex (SplitPacketIndexType) - // Write splitPacketCount (SplitPacketIndexType) - // Write byteLength (4) - // Write data, splitPacketChannelList[index]->splitPacketList[0]->data - InternalPacket *progressIndicator = AllocateFromInternalPacketPool(); - // unsigned int len = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + (unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); - unsigned int l = (unsigned int) splitPacketChannelList[index]->stride; - const unsigned int len = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + l; - AllocInternalPacketData(progressIndicator, len, false, __FILE__, __LINE__ ); - progressIndicator->dataBitLength=BYTES_TO_BITS(len); - progressIndicator->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; - unsigned int temp; - // temp=splitPacketChannelList[index]->splitPacketList.Size(); - temp=splitPacketChannelList[index]->splitPacketsArrived; - memcpy(progressIndicator->data+sizeof(MessageID), &temp, sizeof(unsigned int)); - temp=(unsigned int)internalPacket->splitPacketCount; - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*1, &temp, sizeof(unsigned int)); - // temp=(unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); - temp=(unsigned int) BITS_TO_BYTES(l); - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*2, &temp, sizeof(unsigned int)); - //memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->firstPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength)); - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->returnedPacket->data, (size_t) BITS_TO_BYTES(l)); - } - - if (dealloc) - { - FreeInternalPacketData(internalPacket, __FILE__, __LINE__ ); - ReleaseToInternalPacketPool(internalPacket); - } -#else - splitPacketChannelList[index]->splitPacketList.Insert(internalPacket, __FILE__, __LINE__ ); - splitPacketChannelList[index]->lastUpdateTime=time; - - if (internalPacket->splitPacketIndex==0) - splitPacketChannelList[index]->firstPacket=internalPacket; - - if (splitMessageProgressInterval && - splitPacketChannelList[index]->firstPacket && - splitPacketChannelList[index]->splitPacketList.Size()!=splitPacketChannelList[index]->firstPacket->splitPacketCount && - (splitPacketChannelList[index]->splitPacketList.Size()%splitMessageProgressInterval)==0) - { - // Return ID_DOWNLOAD_PROGRESS - // Write splitPacketIndex (SplitPacketIndexType) - // Write splitPacketCount (SplitPacketIndexType) - // Write byteLength (4) - // Write data, splitPacketChannelList[index]->splitPacketList[0]->data - InternalPacket *progressIndicator = AllocateFromInternalPacketPool(); - unsigned int length = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + (unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); - AllocInternalPacketData(progressIndicator, length, false, __FILE__, __LINE__ ); - progressIndicator->dataBitLength=BYTES_TO_BITS(length); - progressIndicator->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; - unsigned int temp; - temp=splitPacketChannelList[index]->splitPacketList.Size(); - memcpy(progressIndicator->data+sizeof(MessageID), &temp, sizeof(unsigned int)); - temp=(unsigned int)internalPacket->splitPacketCount; - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*1, &temp, sizeof(unsigned int)); - temp=(unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*2, &temp, sizeof(unsigned int)); - - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->firstPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength)); - outputQueue.Push(progressIndicator, __FILE__, __LINE__ ); - } - -#endif -} - -//------------------------------------------------------------------------------------------------------- -// Take all split chunks with the specified splitPacketId and try to -//reconstruct a packet. If we can, allocate and return it. Otherwise return 0 -// Optimized version -//------------------------------------------------------------------------------------------------------- -InternalPacket * ReliabilityLayer::BuildPacketFromSplitPacketList( SplitPacketChannel *splitPacketChannel, CCTimeType time ) -{ -#if PREALLOCATE_LARGE_MESSAGES==1 - InternalPacket *returnedPacket=splitPacketChannel->returnedPacket; - RakNet::OP_DELETE(splitPacketChannel, __FILE__, __LINE__); - (void) time; - return returnedPacket; -#else - unsigned int j; - InternalPacket * internalPacket, *splitPacket; - int splitPacketPartLength; - - // Reconstruct - internalPacket = CreateInternalPacketCopy( splitPacketChannel->splitPacketList[0], 0, 0, time ); - internalPacket->dataBitLength=0; - for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) - internalPacket->dataBitLength+=splitPacketChannel->splitPacketList[j]->dataBitLength; - splitPacketPartLength=BITS_TO_BYTES(splitPacketChannel->firstPacket->dataBitLength); - - internalPacket->data = (unsigned char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES( internalPacket->dataBitLength ), _FILE_AND_LINE_ ); - internalPacket->allocationScheme=InternalPacket::NORMAL; - - for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) - { - splitPacket=splitPacketChannel->splitPacketList[j]; - memcpy(internalPacket->data+splitPacket->splitPacketIndex*splitPacketPartLength, splitPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannel->splitPacketList[j]->dataBitLength)); - } - - for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) - { - FreeInternalPacketData(splitPacketChannel->splitPacketList[j], _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool(splitPacketChannel->splitPacketList[j]); - } - RakNet::OP_DELETE(splitPacketChannel, __FILE__, __LINE__); - - return internalPacket; -#endif -} -//------------------------------------------------------------------------------------------------------- -InternalPacket * ReliabilityLayer::BuildPacketFromSplitPacketList( SplitPacketIdType splitPacketId, CCTimeType time, - SOCKET s, SystemAddress &systemAddress, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, - BitStream &updateBitStream) -{ - unsigned int i; - bool objectExists; - SplitPacketChannel *splitPacketChannel; - InternalPacket * internalPacket; - - i=splitPacketChannelList.GetIndexFromKey(splitPacketId, &objectExists); - splitPacketChannel=splitPacketChannelList[i]; - -#if PREALLOCATE_LARGE_MESSAGES==1 - if (splitPacketChannel->splitPacketsArrived==splitPacketChannel->returnedPacket->splitPacketCount) -#else - if (splitPacketChannel->splitPacketList.Size()==splitPacketChannel->splitPacketList[0]->splitPacketCount) -#endif - { - // Ack immediately, because for large files this can take a long time - SendACKs(s, systemAddress, time, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); - internalPacket=BuildPacketFromSplitPacketList(splitPacketChannel,time); - splitPacketChannelList.RemoveAtIndex(i); - return internalPacket; - } - else - { - return 0; - } -} -/* -//------------------------------------------------------------------------------------------------------- -// Delete any unreliable split packets that have long since expired -void ReliabilityLayer::DeleteOldUnreliableSplitPackets( CCTimeType time ) -{ -unsigned i,j; -i=0; -while (i < splitPacketChannelList.Size()) -{ -#if CC_TIME_TYPE_BYTES==4 -if (time > splitPacketChannelList[i]->lastUpdateTime + timeoutTime && -#else -if (time > splitPacketChannelList[i]->lastUpdateTime + (CCTimeType)timeoutTime*(CCTimeType)1000 && -#endif -(splitPacketChannelList[i]->splitPacketList[0]->reliability==UNRELIABLE || splitPacketChannelList[i]->splitPacketList[0]->reliability==UNRELIABLE_SEQUENCED)) -{ -for (j=0; j < splitPacketChannelList[i]->splitPacketList.Size(); j++) -{ -RakNet::OP_DELETE_ARRAY(splitPacketChannelList[i]->splitPacketList[j]->data, _FILE_AND_LINE_); -ReleaseToInternalPacketPool(splitPacketChannelList[i]->splitPacketList[j]); -} -RakNet::OP_DELETE(splitPacketChannelList[i], _FILE_AND_LINE_); -splitPacketChannelList.RemoveAtIndex(i); -} -else -i++; -} -} -*/ - -//------------------------------------------------------------------------------------------------------- -// Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. -// Does not copy any split data parameters as that information is always generated does not have any reason to be copied -//------------------------------------------------------------------------------------------------------- -InternalPacket * ReliabilityLayer::CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, CCTimeType time ) -{ - InternalPacket * copy = AllocateFromInternalPacketPool(); -#ifdef _DEBUG - // Remove accessing undefined memory error - memset( copy, 255, sizeof( InternalPacket ) ); -#endif - // Copy over our chunk of data - - if ( dataByteLength > 0 ) - { - AllocInternalPacketData(copy, BITS_TO_BYTES(dataByteLength ), false, _FILE_AND_LINE_ ); - memcpy( copy->data, original->data + dataByteOffset, dataByteLength ); - } - else - copy->data = 0; - - copy->dataBitLength = dataByteLength << 3; - copy->creationTime = time; - copy->nextActionTime = 0; - copy->orderingIndex = original->orderingIndex; - copy->sequencingIndex = original->sequencingIndex; - copy->orderingChannel = original->orderingChannel; - copy->reliableMessageNumber = original->reliableMessageNumber; - copy->priority = original->priority; - copy->reliability = original->reliability; -#if PREALLOCATE_LARGE_MESSAGES==1 - copy->splitPacketCount = original->splitPacketCount; - copy->splitPacketId = original->splitPacketId; - copy->splitPacketIndex = original->splitPacketIndex; -#endif - - return copy; -} - -//------------------------------------------------------------------------------------------------------- -// Get the specified ordering list -//------------------------------------------------------------------------------------------------------- -/* -DataStructures::LinkedList *ReliabilityLayer::GetOrderingListAtOrderingStream( unsigned char orderingChannel ) -{ - if ( orderingChannel >= orderingList.Size() ) - return 0; - - return orderingList[ orderingChannel ]; -} - -//------------------------------------------------------------------------------------------------------- -// Add the internal packet to the ordering list in order based on order index -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AddToOrderingList( InternalPacket * internalPacket ) -{ - } -*/ - -//------------------------------------------------------------------------------------------------------- -// Inserts a packet into the resend list in order -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::InsertPacketIntoResendList( InternalPacket *internalPacket, CCTimeType time, bool firstResend, bool modifyUnacknowledgedBytes ) -{ - (void) firstResend; - (void) time; - (void) internalPacket; - - AddToListTail(internalPacket, modifyUnacknowledgedBytes); - RakAssert(internalPacket->nextActionTime!=0); - -} - -//------------------------------------------------------------------------------------------------------- -// Were you ever unable to deliver a packet despite retries? -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsDeadConnection( void ) const -{ - return deadConnection; -} - -//------------------------------------------------------------------------------------------------------- -// Causes IsDeadConnection to return true -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::KillConnection( void ) -{ - deadConnection=true; -} - - -//------------------------------------------------------------------------------------------------------- -// Statistics -//------------------------------------------------------------------------------------------------------- -RakNetStatistics * ReliabilityLayer::GetStatistics( RakNetStatistics *rns ) -{ - unsigned i; - RakNet::TimeUS time = RakNet::GetTimeUS(); - uint64_t uint64Denominator; - double doubleDenominator; - - for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) - { - statistics.valueOverLastSecond[i]=bpsMetrics[i].GetBPS1Threadsafe(time); - statistics.runningTotal[i]=bpsMetrics[i].GetTotal1(); - } - - memcpy(rns, &statistics, sizeof(statistics)); - - if (rns->valueOverLastSecond[USER_MESSAGE_BYTES_SENT]+rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT]>0) - rns->packetlossLastSecond=(float)((double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT]/((double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_SENT]+(double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT])); - else - rns->packetlossLastSecond=0.0f; - - rns->packetlossTotal=0.0f; - uint64Denominator=(rns->runningTotal[USER_MESSAGE_BYTES_SENT]+rns->runningTotal[USER_MESSAGE_BYTES_RESENT]); - if (uint64Denominator!=0&&rns->runningTotal[USER_MESSAGE_BYTES_SENT]/uint64Denominator>0) - { - doubleDenominator=((double) rns->runningTotal[USER_MESSAGE_BYTES_SENT]+(double) rns->runningTotal[USER_MESSAGE_BYTES_RESENT]); - if(doubleDenominator!=0) - { - rns->packetlossTotal=(float)((double) rns->runningTotal[USER_MESSAGE_BYTES_RESENT]/doubleDenominator); - } - } - - rns->isLimitedByCongestionControl=statistics.isLimitedByCongestionControl; - rns->BPSLimitByCongestionControl=statistics.BPSLimitByCongestionControl; - rns->isLimitedByOutgoingBandwidthLimit=statistics.isLimitedByOutgoingBandwidthLimit; - rns->BPSLimitByOutgoingBandwidthLimit=statistics.BPSLimitByOutgoingBandwidthLimit; - - return rns; -} - -//------------------------------------------------------------------------------------------------------- -// Returns the number of packets in the resend queue, not counting holes -//------------------------------------------------------------------------------------------------------- -unsigned int ReliabilityLayer::GetResendListDataSize(void) const -{ - // Not accurate but thread-safe. The commented version might crash if the queue is cleared while we loop through it - // return resendTree.Size(); - return statistics.messagesInResendBuffer; -} - -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::AckTimeout(RakNet::Time curTime) -{ - // I check timeLastDatagramArrived-curTime because with threading it is possible that timeLastDatagramArrived is - // slightly greater than curTime, in which case this is NOT an ack timeout - return (timeLastDatagramArrived-curTime)>10000 && curTime-timeLastDatagramArrived>timeoutTime; -} -//------------------------------------------------------------------------------------------------------- -CCTimeType ReliabilityLayer::GetNextSendTime(void) const -{ - return nextSendTime; -} -//------------------------------------------------------------------------------------------------------- -CCTimeType ReliabilityLayer::GetTimeBetweenPackets(void) const -{ - return timeBetweenPackets; -} -//------------------------------------------------------------------------------------------------------- -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 -CCTimeType ReliabilityLayer::GetAckPing(void) const -{ - return ackPing; -} -#endif -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ResetPacketsAndDatagrams(void) -{ - packetsToSendThisUpdate.Clear(true, _FILE_AND_LINE_); - packetsToDeallocThisUpdate.Clear(true, _FILE_AND_LINE_); - packetsToSendThisUpdateDatagramBoundaries.Clear(true, _FILE_AND_LINE_); - datagramsToSendThisUpdateIsPair.Clear(true, _FILE_AND_LINE_); - datagramSizesInBytes.Clear(true, _FILE_AND_LINE_); - datagramSizeSoFar=0; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::PushPacket(CCTimeType time, InternalPacket *internalPacket, bool isReliable) -{ - BitSize_t bitsForThisPacket=BYTES_TO_BITS(BITS_TO_BYTES(internalPacket->dataBitLength)+BITS_TO_BYTES(internalPacket->headerLength)); - datagramSizeSoFar+=bitsForThisPacket; - RakAssert(BITS_TO_BYTES(datagramSizeSoFar)headerLength==GetMessageHeaderLengthBits(internalPacket)); - -// This code tells me how much time elapses between when you send, and when the message actually goes out -// if (internalPacket->data[0]==0) -// { -// RakNet::TimeMS t; -// RakNet::BitStream bs(internalPacket->data+1,sizeof(t),false); -// bs.Read(t); -// RakNet::TimeMS curTime=RakNet::GetTimeMS(); -// RakNet::TimeMS diff = curTime-t; -// } - - congestionManager.OnSendBytes(time, BITS_TO_BYTES(internalPacket->dataBitLength)+BITS_TO_BYTES(internalPacket->headerLength)); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::PushDatagram(void) -{ - if (datagramSizeSoFar>0) - { - packetsToSendThisUpdateDatagramBoundaries.Push(packetsToSendThisUpdate.Size(), _FILE_AND_LINE_ ); - datagramsToSendThisUpdateIsPair.Push(false, _FILE_AND_LINE_ ); - RakAssert(BITS_TO_BYTES(datagramSizeSoFar)=2) - { - datagramsToSendThisUpdateIsPair[datagramsToSendThisUpdateIsPair.Size()-2]=true; - datagramsToSendThisUpdateIsPair[datagramsToSendThisUpdateIsPair.Size()-1]=true; - return true; - } - return false; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ClearPacketsAndDatagrams(void) -{ - unsigned int i; - for (i=0; i < packetsToDeallocThisUpdate.Size(); i++) - { - // packetsToDeallocThisUpdate holds a boolean indicating if packetsToSendThisUpdate at this index should be freed - if (packetsToDeallocThisUpdate[i]) - { - RemoveFromUnreliableLinkedList(packetsToSendThisUpdate[i]); - FreeInternalPacketData(packetsToSendThisUpdate[i], _FILE_AND_LINE_ ); - // if (keepInternalPacketIfNeedsAck==false || packetsToSendThisUpdate[i]->reliabilityresendNext=internalPacket; - internalPacket->resendPrev=internalPacket; - resendLinkedListHead=internalPacket; - return; - } - internalPacket->resendPrev->resendNext = internalPacket->resendNext; - internalPacket->resendNext->resendPrev = internalPacket->resendPrev; - internalPacket->resendNext=resendLinkedListHead; - internalPacket->resendPrev=resendLinkedListHead->resendPrev; - internalPacket->resendPrev->resendNext=internalPacket; - resendLinkedListHead->resendPrev=internalPacket; - resendLinkedListHead=internalPacket; - RakAssert(internalPacket->headerLength+internalPacket->dataBitLength>0); - - //ValidateResendList(); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::RemoveFromList(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes) -{ - InternalPacket *newPosition; - internalPacket->resendPrev->resendNext = internalPacket->resendNext; - internalPacket->resendNext->resendPrev = internalPacket->resendPrev; - newPosition = internalPacket->resendNext; - if ( internalPacket == resendLinkedListHead ) - resendLinkedListHead = newPosition; - if (resendLinkedListHead==internalPacket) - resendLinkedListHead=0; - - if (modifyUnacknowledgedBytes) - { - RakAssert(unacknowledgedBytes>=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength)); - unacknowledgedBytes-=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); - // printf("-unacknowledgedBytes:%i ", unacknowledgedBytes); - - -// ValidateResendList(); - } -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AddToListTail(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes) -{ - if (modifyUnacknowledgedBytes) - { - unacknowledgedBytes+=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); - // printf("+unacknowledgedBytes:%i ", unacknowledgedBytes); - } - - if (resendLinkedListHead==0) - { - internalPacket->resendNext=internalPacket; - internalPacket->resendPrev=internalPacket; - resendLinkedListHead=internalPacket; - return; - } - internalPacket->resendNext=resendLinkedListHead; - internalPacket->resendPrev=resendLinkedListHead->resendPrev; - internalPacket->resendPrev->resendNext=internalPacket; - resendLinkedListHead->resendPrev=internalPacket; - -// ValidateResendList(); - -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::PopListHead(bool modifyUnacknowledgedBytes) -{ - RakAssert(resendLinkedListHead!=0); - RemoveFromList(resendLinkedListHead, modifyUnacknowledgedBytes); -} -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsResendQueueEmpty(void) const -{ - return resendLinkedListHead==0; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SendACKs(SOCKET s, SystemAddress &systemAddress, CCTimeType time, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream) -{ - BitSize_t maxDatagramPayload = GetMaxDatagramSizeExcludingMessageHeaderBits(); - - while (acknowlegements.Size()>0) - { - // Send acks - updateBitStream.Reset(); - DatagramHeaderFormat dhf; - dhf.isACK=true; - dhf.isNAK=false; - dhf.isPacketPair=false; -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - dhf.sourceSystemTime=time; -#endif - double B; - double AS; - bool hasBAndAS; - if (remoteSystemNeedsBAndAS) - { - congestionManager.OnSendAckGetBAndAS(time, &hasBAndAS,&B,&AS); - dhf.AS=(float)AS; - dhf.hasBAndAS=hasBAndAS; - } - else - dhf.hasBAndAS=false; -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - dhf.sourceSystemTime=nextAckTimeToSend; -#endif - // dhf.B=(float)B; - updateBitStream.Reset(); - dhf.Serialize(&updateBitStream); - CC_DEBUG_PRINTF_1("AckSnd "); - acknowlegements.Serialize(&updateBitStream, maxDatagramPayload, true); - SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); - congestionManager.OnSendAck(time,updateBitStream.GetNumberOfBytesUsed()); - - // I think this is causing a bug where if the estimated bandwidth is very low for the recipient, only acks ever get sent - // congestionManager.OnSendBytes(time,UDP_HEADER_SIZE+updateBitStream.GetNumberOfBytesUsed()); - } -} -/* -//------------------------------------------------------------------------------------------------------- -ReliabilityLayer::DatagramMessageIDList* ReliabilityLayer::AllocateFromDatagramMessageIDPool(void) -{ -DatagramMessageIDList*s; -s=datagramMessageIDPool.Allocate( _FILE_AND_LINE_ ); -// Call new operator, memoryPool doesn't do this -s = new ((void*)s) DatagramMessageIDList; -return s; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ReleaseToDatagramMessageIDPool(DatagramMessageIDList* d) -{ -d->~DatagramMessageIDList(); -datagramMessageIDPool.Release(d); -} -*/ -//------------------------------------------------------------------------------------------------------- -InternalPacket* ReliabilityLayer::AllocateFromInternalPacketPool(void) -{ - InternalPacket *ip = internalPacketPool.Allocate( _FILE_AND_LINE_ ); - ip->reliableMessageNumber = (MessageNumberType) (const uint32_t)-1; - ip->messageNumberAssigned=false; - ip->nextActionTime = 0; - ip->splitPacketCount = 0; - ip->allocationScheme=InternalPacket::NORMAL; - ip->data=0; - return ip; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ReleaseToInternalPacketPool(InternalPacket *ip) -{ - internalPacketPool.Release(ip, _FILE_AND_LINE_); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::RemoveFromUnreliableLinkedList(InternalPacket *internalPacket) -{ - if (internalPacket->reliability==UNRELIABLE || - internalPacket->reliability==UNRELIABLE_SEQUENCED || - internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT -// || -// internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - { - InternalPacket *newPosition; - internalPacket->unreliablePrev->unreliableNext = internalPacket->unreliableNext; - internalPacket->unreliableNext->unreliablePrev = internalPacket->unreliablePrev; - newPosition = internalPacket->unreliableNext; - if ( internalPacket == unreliableLinkedListHead ) - unreliableLinkedListHead = newPosition; - if (unreliableLinkedListHead==internalPacket) - unreliableLinkedListHead=0; - } -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AddToUnreliableLinkedList(InternalPacket *internalPacket) -{ - if (internalPacket->reliability==UNRELIABLE || - internalPacket->reliability==UNRELIABLE_SEQUENCED || - internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT -// || -// internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - { - if (unreliableLinkedListHead==0) - { - internalPacket->unreliableNext=internalPacket; - internalPacket->unreliablePrev=internalPacket; - unreliableLinkedListHead=internalPacket; - return; - } - internalPacket->unreliableNext=unreliableLinkedListHead; - internalPacket->unreliablePrev=unreliableLinkedListHead->unreliablePrev; - internalPacket->unreliablePrev->unreliableNext=internalPacket; - unreliableLinkedListHead->unreliablePrev=internalPacket; - } -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ValidateResendList(void) const -{ -// unsigned int count1=0, count2=0; -// for (unsigned int i=0; i < RESEND_BUFFER_ARRAY_LENGTH; i++) -// if (resendBuffer[i]) -// count1++; -// -// if (resendLinkedListHead) -// { -// InternalPacket *internalPacket = resendLinkedListHead; -// do -// { -// count2++; -// internalPacket=internalPacket->resendNext; -// } while (internalPacket!=resendLinkedListHead); -// } -// RakAssert(count1==count2); -// RakAssert(count2<=RESEND_BUFFER_ARRAY_LENGTH); -} -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::ResendBufferOverflow(void) const -{ - int index1 = sendReliableMessageNumberIndex & (uint32_t) RESEND_BUFFER_ARRAY_MASK; - // int index2 = (sendReliableMessageNumberIndex+(uint32_t)1) & (uint32_t) RESEND_BUFFER_ARRAY_MASK; - RakAssert(index1= datagramHistory.Size()) - return 0; - - *timeSent=datagramHistory[offsetIntoList].timeSent; - return datagramHistory[offsetIntoList].head; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::RemoveFromDatagramHistory(DatagramSequenceNumberType index) -{ - DatagramSequenceNumberType offsetIntoList = index - datagramHistoryPopCount; - MessageNumberNode *mnm = datagramHistory[offsetIntoList].head; - MessageNumberNode *next; - while (mnm) - { - next=mnm->next; - datagramHistoryMessagePool.Release(mnm, _FILE_AND_LINE_); - mnm=next; - } - datagramHistory[offsetIntoList].head=0; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, CCTimeType timeSent) -{ - (void) datagramNumber; - if (datagramHistory.Size()>DATAGRAM_MESSAGE_ID_ARRAY_LENGTH) - { - RemoveFromDatagramHistory(datagramHistoryPopCount); - datagramHistory.Pop(); - datagramHistoryPopCount++; - } - - datagramHistory.Push(DatagramHistoryNode(0, timeSent), _FILE_AND_LINE_); - // printf("%p Pushed empty DatagramHistoryNode to datagram history at index %i\n", this, datagramHistory.Size()-1); -} -//------------------------------------------------------------------------------------------------------- -ReliabilityLayer::MessageNumberNode* ReliabilityLayer::AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, DatagramSequenceNumberType messageNumber, CCTimeType timeSent) -{ - (void) datagramNumber; -// RakAssert(datagramHistoryPopCount+(unsigned int) datagramHistory.Size()==datagramNumber); - if (datagramHistory.Size()>DATAGRAM_MESSAGE_ID_ARRAY_LENGTH) - { - RemoveFromDatagramHistory(datagramHistoryPopCount); - datagramHistory.Pop(); - datagramHistoryPopCount++; - } - - MessageNumberNode *mnm = datagramHistoryMessagePool.Allocate(_FILE_AND_LINE_); - mnm->next=0; - mnm->messageNumber=messageNumber; - datagramHistory.Push(DatagramHistoryNode(mnm, timeSent), _FILE_AND_LINE_); - // printf("%p Pushed message %i to DatagramHistoryNode to datagram history at index %i\n", this, messageNumber.val, datagramHistory.Size()-1); - return mnm; -} -//------------------------------------------------------------------------------------------------------- -ReliabilityLayer::MessageNumberNode* ReliabilityLayer::AddSubsequentToDatagramHistory(MessageNumberNode *messageNumberNode, DatagramSequenceNumberType messageNumber) -{ - messageNumberNode->next=datagramHistoryMessagePool.Allocate(_FILE_AND_LINE_); - messageNumberNode->next->messageNumber=messageNumber; - messageNumberNode->next->next=0; - return messageNumberNode->next; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, InternalPacketRefCountedData **refCounter, unsigned char *externallyAllocatedPtr, unsigned char *ourOffset) -{ - internalPacket->allocationScheme=InternalPacket::REF_COUNTED; - internalPacket->data=ourOffset; - if (*refCounter==0) - { - *refCounter = refCountedDataPool.Allocate(_FILE_AND_LINE_); - // *refCounter = RakNet::OP_NEW(_FILE_AND_LINE_); - (*refCounter)->refCount=1; - (*refCounter)->sharedDataBlock=externallyAllocatedPtr; - } - else - (*refCounter)->refCount++; - internalPacket->refCountedData=(*refCounter); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, unsigned char *externallyAllocatedPtr) -{ - internalPacket->allocationScheme=InternalPacket::NORMAL; - internalPacket->data=externallyAllocatedPtr; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, unsigned int numBytes, bool allowStack, const char *file, unsigned int line) -{ - if (allowStack && numBytes <= sizeof(internalPacket->stackData)) - { - internalPacket->allocationScheme=InternalPacket::STACK; - internalPacket->data=internalPacket->stackData; - } - else - { - internalPacket->allocationScheme=InternalPacket::NORMAL; - internalPacket->data=(unsigned char*) rakMalloc_Ex(numBytes,file,line); - } -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::FreeInternalPacketData(InternalPacket *internalPacket, const char *file, unsigned int line) -{ - if (internalPacket==0) - return; - - if (internalPacket->allocationScheme==InternalPacket::REF_COUNTED) - { - if (internalPacket->refCountedData==0) - return; - - internalPacket->refCountedData->refCount--; - if (internalPacket->refCountedData->refCount==0) - { - rakFree_Ex(internalPacket->refCountedData->sharedDataBlock, file, line ); - internalPacket->refCountedData->sharedDataBlock=0; - // RakNet::OP_DELETE(internalPacket->refCountedData,file, line); - refCountedDataPool.Release(internalPacket->refCountedData,file, line); - internalPacket->refCountedData=0; - } - } - else if (internalPacket->allocationScheme==InternalPacket::NORMAL) - { - if (internalPacket->data==0) - return; - - rakFree_Ex(internalPacket->data, file, line ); - internalPacket->data=0; - } - else - { - // Data was on stack - internalPacket->data=0; - } -} -//------------------------------------------------------------------------------------------------------- -unsigned int ReliabilityLayer::GetMaxDatagramSizeExcludingMessageHeaderBytes(void) -{ - unsigned int val = congestionManager.GetMTU() - DatagramHeaderFormat::GetDataHeaderByteLength(); - -#if LIBCAT_SECURITY==1 - if (useSecurity) - val -= cat::AuthenticatedEncryption::OVERHEAD_BYTES; -#endif - - return val; -} -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::GetMaxDatagramSizeExcludingMessageHeaderBits(void) -{ - return BYTES_TO_BITS(GetMaxDatagramSizeExcludingMessageHeaderBytes()); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::InitHeapWeights(void) -{ - for (int priorityLevel=0; priorityLevel < NUMBER_OF_PRIORITIES; priorityLevel++) - outgoingPacketBufferNextWeights[priorityLevel]=(1<0) - { - int peekPL = outgoingPacketBuffer.Peek()->priority; - reliabilityHeapWeightType weight = outgoingPacketBuffer.PeekWeight(); - reliabilityHeapWeightType min = weight - (1< + +using namespace RakNet; + +// Can't figure out which library has this function on the PS3 +double Ceil(double d) {if (((double)((int)d))==d) return d; return (int) (d+1.0);} + +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RELIABILITY_LAYER_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + + +//#define _DEBUG_LOGGER + +#if CC_TIME_TYPE_BYTES==4 +static const CCTimeType MAX_TIME_BETWEEN_PACKETS= 350; // 350 milliseconds +static const CCTimeType HISTOGRAM_RESTART_CYCLE=10000; // Every 10 seconds reset the histogram +#else +static const CCTimeType MAX_TIME_BETWEEN_PACKETS= 350000; // 350 milliseconds +//static const CCTimeType HISTOGRAM_RESTART_CYCLE=10000000; // Every 10 seconds reset the histogram +#endif +static const int DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE=512; +static const CCTimeType STARTING_TIME_BETWEEN_PACKETS=MAX_TIME_BETWEEN_PACKETS; +//static const long double TIME_BETWEEN_PACKETS_INCREASE_MULTIPLIER_DEFAULT=.02; +//static const long double TIME_BETWEEN_PACKETS_DECREASE_MULTIPLIER_DEFAULT=1.0 / 9.0; + +typedef uint32_t BitstreamLengthEncoding; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +//#define PRINT_TO_FILE_ROT +#ifdef PRINT_TO_FILE_ROT +static FILE *fp=0; +#endif + +BPSTracker::TimeAndValue2::TimeAndValue2() {} +BPSTracker::TimeAndValue2::~TimeAndValue2() {} +BPSTracker::TimeAndValue2::TimeAndValue2(RakNet::TimeUS t, uint64_t v1) : value1(v1), time(t) {} +//BPSTracker::TimeAndValue2::TimeAndValue2(RakNet::TimeUS t, uint64_t v1, uint64_t v2) : time(t), value1(v1), value2(v2) {} +BPSTracker::BPSTracker() {Reset(_FILE_AND_LINE_);} +BPSTracker::~BPSTracker() {} +//void BPSTracker::Reset(const char *file, unsigned int line) {total1=total2=lastSec1=lastSec2=0; dataQueue.Clear(file,line);} +void BPSTracker::Reset(const char *file, unsigned int line) {total1=lastSec1=0; dataQueue.Clear(file,line);} +//void BPSTracker::Push2(RakNetTimeUS time, uint64_t value1, uint64_t value2) {dataQueue.Push(TimeAndValue2(time,value1,value2),_FILE_AND_LINE_); total1+=value1; lastSec1+=value1; total2+=value2; lastSec2+=value2;} +//uint64_t BPSTracker::GetBPS2(RakNetTimeUS time) {ClearExpired2(time); return lastSec2;} +//void BPSTracker::GetBPS1And2(RakNetTimeUS time, uint64_t &out1, uint64_t &out2) {ClearExpired2(time); out1=lastSec1; out2=lastSec2;} +uint64_t BPSTracker::GetTotal1(void) const {return total1;} +//uint64_t BPSTracker::GetTotal2(void) const {return total2;} + +// void BPSTracker::ClearExpired2(RakNet::TimeUS time) { +// RakNet::TimeUS threshold=time; +// if (threshold < 1000000) +// return; +// threshold-=1000000; +// while (dataQueue.IsEmpty()==false && dataQueue.Peek().time < threshold) +// { +// lastSec1-=dataQueue.Peek().value1; +// lastSec2-=dataQueue.Peek().value2; +// dataQueue.Pop(); +// } +// } +void BPSTracker::ClearExpired1(RakNet::TimeUS time) +{ + while (dataQueue.IsEmpty()==false && +#if CC_TIME_TYPE_BYTES==8 + dataQueue.Peek().time+1000000 < time +#else + dataQueue.Peek().time+1000 < time +#endif + ) + { + lastSec1-=dataQueue.Peek().value1; + dataQueue.Pop(); + } +} + +struct DatagramHeaderFormat +{ +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + CCTimeType sourceSystemTime; +#endif + DatagramSequenceNumberType datagramNumber; + + // Use floats to save bandwidth + // float B; // Link capacity + float AS; // Data arrival rate + bool isACK; + bool isNAK; + bool isPacketPair; + bool hasBAndAS; + bool isContinuousSend; + bool needsBAndAs; + bool isValid; // To differentiate between what I serialized, and offline data + + static BitSize_t GetDataHeaderBitLength() + { + return BYTES_TO_BITS(GetDataHeaderByteLength()); + } + + static unsigned int GetDataHeaderByteLength() + { + //return 2 + 3 + sizeof(RakNet::TimeMS) + sizeof(float)*2; + return 2 + 3 + +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + sizeof(RakNetTimeMS) + +#endif + sizeof(float)*1; + } + + void Serialize(RakNet::BitStream *b) + { + // Not endian safe + // RakAssert(GetDataHeaderByteLength()==sizeof(DatagramHeaderFormat)); + // b->WriteAlignedBytes((const unsigned char*) this, sizeof(DatagramHeaderFormat)); + // return; + + b->Write(true); // IsValid + if (isACK) + { + b->Write(true); + b->Write(hasBAndAS); + b->AlignWriteToByteBoundary(); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMSLow=(RakNet::TimeMS) sourceSystemTime&0xFFFFFFFF; b->Write(timeMSLow); +#endif + if (hasBAndAS) + { + // b->Write(B); + b->Write(AS); + } + } + else if (isNAK) + { + b->Write(false); + b->Write(true); + } + else + { + b->Write(false); + b->Write(false); + b->Write(isPacketPair); + b->Write(isContinuousSend); + b->Write(needsBAndAs); + b->AlignWriteToByteBoundary(); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMSLow=(RakNet::TimeMS) sourceSystemTime&0xFFFFFFFF; b->Write(timeMSLow); +#endif + b->Write(datagramNumber); + } + } + void Deserialize(RakNet::BitStream *b) + { + // Not endian safe + // b->ReadAlignedBytes((unsigned char*) this, sizeof(DatagramHeaderFormat)); + // return; + + b->Read(isValid); + b->Read(isACK); + if (isACK) + { + isNAK=false; + isPacketPair=false; + b->Read(hasBAndAS); + b->AlignReadToByteBoundary(); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMS; b->Read(timeMS); sourceSystemTime=(CCTimeType) timeMS; +#endif + if (hasBAndAS) + { + // b->Read(B); + b->Read(AS); + } + } + else + { + b->Read(isNAK); + if (isNAK) + { + isPacketPair=false; + } + else + { + b->Read(isPacketPair); + b->Read(isContinuousSend); + b->Read(needsBAndAs); + b->AlignReadToByteBoundary(); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMS; b->Read(timeMS); sourceSystemTime=(CCTimeType) timeMS; +#endif + b->Read(datagramNumber); + } + } + } +}; + +#if !defined(__GNUC__) && !defined(__ARMCC) +#pragma warning(disable:4702) // unreachable code +#endif + +#ifdef _WIN32 +//#define _DEBUG_LOGGER +#ifdef _DEBUG_LOGGER +#include "WindowsIncludes.h" +#endif +#endif + +//#define DEBUG_SPLIT_PACKET_PROBLEMS +#if defined (DEBUG_SPLIT_PACKET_PROBLEMS) +static int waitFlag=-1; +#endif + +using namespace RakNet; + +int RakNet::SplitPacketChannelComp( SplitPacketIdType const &key, SplitPacketChannel* const &data ) +{ +#if PREALLOCATE_LARGE_MESSAGES==1 + if (key < data->returnedPacket->splitPacketId) + return -1; + if (key == data->returnedPacket->splitPacketId) + return 0; +#else + if (key < data->splitPacketList[0]->splitPacketId) + return -1; + if (key == data->splitPacketList[0]->splitPacketId) + return 0; +#endif + return 1; +} + +// DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS( InternalPacket, SplitPacketIndexType, splitPacketIndex ) +/* +bool operator<( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get() < cls->splitPacketIndex; +} +bool operator>( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get() > cls->splitPacketIndex; +} +bool operator==( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get() == cls->splitPacketIndex; +} +/// Semi-hack: This is necessary to call Sort() +bool operator<( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get()->splitPacketIndex < cls->splitPacketIndex; +} +bool operator>( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get()->splitPacketIndex > cls->splitPacketIndex; +} +bool operator==( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get()->splitPacketIndex == cls->splitPacketIndex; +} +*/ + +int SplitPacketIndexComp( SplitPacketIndexType const &key, InternalPacket* const &data ) +{ +if (key < data->splitPacketIndex) +return -1; +if (key == data->splitPacketIndex) +return 0; +return 1; +} + +//------------------------------------------------------------------------------------------------------- +// Constructor +//------------------------------------------------------------------------------------------------------- +// Add 21 to the default MTU so if we encrypt it can hold potentially 21 more bytes of extra data + padding. +ReliabilityLayer::ReliabilityLayer() +{ + +#ifdef _DEBUG + // Wait longer to disconnect in debug so I don't get disconnected while tracing + timeoutTime=30000; +#else + timeoutTime=10000; +#endif + +#ifdef _DEBUG + minExtraPing=extraPingVariance=0; + packetloss=(double) minExtraPing; +#endif + + +#ifdef PRINT_TO_FILE_ROT + if (fp==0 && 0) + { + fp = fopen("reliableorderedoutput.txt", "wt"); + } +#endif + + InitializeVariables(); +//int i = sizeof(InternalPacket); + datagramHistoryMessagePool.SetPageSize(sizeof(MessageNumberNode)*128); + internalPacketPool.SetPageSize(sizeof(InternalPacket)*INTERNAL_PACKET_PAGE_SIZE); + refCountedDataPool.SetPageSize(sizeof(InternalPacketRefCountedData)*32); +} + +//------------------------------------------------------------------------------------------------------- +// Destructor +//------------------------------------------------------------------------------------------------------- +ReliabilityLayer::~ReliabilityLayer() +{ + FreeMemory( true ); // Free all memory immediately +} +//------------------------------------------------------------------------------------------------------- +// Resets the layer for reuse +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::Reset( bool resetVariables, int MTUSize, bool _useSecurity ) +{ + + FreeMemory( true ); // true because making a memory reset pending in the update cycle causes resets after reconnects. Instead, just call Reset from a single thread + if (resetVariables) + { + InitializeVariables(); + +#if LIBCAT_SECURITY==1 + useSecurity = _useSecurity; + + if (_useSecurity) + MTUSize -= cat::AuthenticatedEncryption::OVERHEAD_BYTES; +#else + (void) _useSecurity; +#endif // LIBCAT_SECURITY + congestionManager.Init(RakNet::GetTimeUS(), MTUSize - UDP_HEADER_SIZE); + } +} + +//------------------------------------------------------------------------------------------------------- +// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SetTimeoutTime( RakNet::TimeMS time ) +{ + timeoutTime=time; +} + +//------------------------------------------------------------------------------------------------------- +// Returns the value passed to SetTimeoutTime. or the default if it was never called +//------------------------------------------------------------------------------------------------------- +RakNet::TimeMS ReliabilityLayer::GetTimeoutTime(void) +{ + return timeoutTime; +} + +//------------------------------------------------------------------------------------------------------- +// Initialize the variables +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::InitializeVariables( void ) +{ + memset( orderedWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType)); + memset( sequencedWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); + memset( orderedReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); + memset( highestSequencedReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); + memset( &statistics, 0, sizeof( statistics ) ); + memset( &heapIndexOffsets, 0, sizeof( heapIndexOffsets ) ); + + statistics.connectionStartTime = RakNet::GetTimeUS(); + splitPacketId = 0; + elapsedTimeSinceLastUpdate=0; + throughputCapCountdown=0; + sendReliableMessageNumberIndex = 0; + internalOrderIndex=0; + timeToNextUnreliableCull=0; + unreliableLinkedListHead=0; + lastUpdateTime= RakNet::GetTimeUS(); + bandwidthExceededStatistic=false; + remoteSystemTime=0; + unreliableTimeout=0; + lastBpsClear=0; + + // Disable packet pairs + countdownToNextPacketPair=15; + + nextAllowedThroughputSample=0; + deadConnection = cheater = false; + timeOfLastContinualSend=0; + + // timeResendQueueNonEmpty = 0; + timeLastDatagramArrived=RakNet::GetTimeMS(); + // packetlossThisSample=false; + // backoffThisSample=0; + // packetlossThisSampleResendCount=0; + // lastPacketlossTime=0; + statistics.messagesInResendBuffer=0; + statistics.bytesInResendBuffer=0; + + receivedPacketsBaseIndex=0; + resetReceivedPackets=true; + receivePacketCount=0; + + // SetPing( 1000 ); + + timeBetweenPackets=STARTING_TIME_BETWEEN_PACKETS; + + ackPingIndex=0; + ackPingSum=(CCTimeType)0; + + nextSendTime=lastUpdateTime; + //nextLowestPingReset=(CCTimeType)0; + // continuousSend=false; + + // histogramStart=(CCTimeType)0; + // histogramBitsSent=0; + unacknowledgedBytes=0; + resendLinkedListHead=0; + totalUserDataBytesAcked=0; + + datagramHistoryPopCount=0; + + InitHeapWeights(); + for (int i=0; i < NUMBER_OF_PRIORITIES; i++) + { + statistics.messageInSendBuffer[i]=0; + statistics.bytesInSendBuffer[i]=0.0; + } + + for (int i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) + { + bpsMetrics[i].Reset(_FILE_AND_LINE_); + } +} + +//------------------------------------------------------------------------------------------------------- +// Frees all allocated memory +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::FreeMemory( bool freeAllImmediately ) +{ + (void) freeAllImmediately; + FreeThreadSafeMemory(); +} + +void ReliabilityLayer::FreeThreadSafeMemory( void ) +{ + unsigned i,j; + InternalPacket *internalPacket; + + ClearPacketsAndDatagrams(); + + for (i=0; i < splitPacketChannelList.Size(); i++) + { + for (j=0; j < splitPacketChannelList[i]->splitPacketList.Size(); j++) + { + FreeInternalPacketData(splitPacketChannelList[i]->splitPacketList[j], _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( splitPacketChannelList[i]->splitPacketList[j] ); + } +#if PREALLOCATE_LARGE_MESSAGES==1 + if (splitPacketChannelList[i]->returnedPacket) + { + FreeInternalPacketData(splitPacketChannelList[i]->returnedPacket, __FILE__, __LINE__ ); + ReleaseToInternalPacketPool( splitPacketChannelList[i]->returnedPacket ); + } +#endif + RakNet::OP_DELETE(splitPacketChannelList[i], __FILE__, __LINE__); + } + splitPacketChannelList.Clear(false, _FILE_AND_LINE_); + + while ( outputQueue.Size() > 0 ) + { + internalPacket = outputQueue.Pop(); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + } + + outputQueue.ClearAndForceAllocation( 32, _FILE_AND_LINE_ ); + + /* + for ( i = 0; i < orderingList.Size(); i++ ) + { + if ( orderingList[ i ] ) + { + DataStructures::LinkedList* theList = orderingList[ i ]; + + if ( theList ) + { + while ( theList->Size() ) + { + internalPacket = orderingList[ i ]->Pop(); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + } + + RakNet::OP_DELETE(theList, _FILE_AND_LINE_); + } + } + } + + orderingList.Clear(false, _FILE_AND_LINE_); + */ + + for (i=0; i < NUMBER_OF_ORDERED_STREAMS; i++) + { + for (j=0; j < orderingHeaps[i].Size(); j++) + { + FreeInternalPacketData(orderingHeaps[i][j], _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( orderingHeaps[i][j] ); + } + orderingHeaps[i].Clear(true, _FILE_AND_LINE_); + } + + //resendList.ForEachData(DeleteInternalPacket); + // resendTree.Clear(_FILE_AND_LINE_); + memset(resendBuffer, 0, sizeof(resendBuffer)); + statistics.messagesInResendBuffer=0; + statistics.bytesInResendBuffer=0; + + if (resendLinkedListHead) + { + InternalPacket *prev; + InternalPacket *iter = resendLinkedListHead; + while (1) + { + if (iter->data) + FreeInternalPacketData(iter, _FILE_AND_LINE_ ); + prev=iter; + iter=iter->resendNext; + if (iter==resendLinkedListHead) + { + ReleaseToInternalPacketPool(prev); + break; + } + ReleaseToInternalPacketPool(prev); + } + resendLinkedListHead=0; + } + unacknowledgedBytes=0; + + // acknowlegements.Clear(_FILE_AND_LINE_); + + for ( j=0 ; j < outgoingPacketBuffer.Size(); j++ ) + { + if ( outgoingPacketBuffer[ j ]->data) + FreeInternalPacketData( outgoingPacketBuffer[ j ], _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( outgoingPacketBuffer[ j ] ); + } + + outgoingPacketBuffer.Clear(true, _FILE_AND_LINE_); + +#ifdef _DEBUG + for (unsigned i = 0; i < delayList.Size(); i++ ) + RakNet::OP_DELETE(delayList[ i ], __FILE__, __LINE__); + delayList.Clear(__FILE__, __LINE__); +#endif + + unreliableWithAckReceiptHistory.Clear(false, _FILE_AND_LINE_); + + packetsToSendThisUpdate.Clear(false, _FILE_AND_LINE_); + packetsToSendThisUpdate.Preallocate(512, _FILE_AND_LINE_); + packetsToDeallocThisUpdate.Clear(false, _FILE_AND_LINE_); + packetsToDeallocThisUpdate.Preallocate(512, _FILE_AND_LINE_); + packetsToSendThisUpdateDatagramBoundaries.Clear(false, _FILE_AND_LINE_); + packetsToSendThisUpdateDatagramBoundaries.Preallocate(128, _FILE_AND_LINE_); + datagramSizesInBytes.Clear(false, _FILE_AND_LINE_); + datagramSizesInBytes.Preallocate(128, _FILE_AND_LINE_); + + internalPacketPool.Clear(_FILE_AND_LINE_); + + refCountedDataPool.Clear(_FILE_AND_LINE_); + + /* + DataStructures::Page *cur = datagramMessageIDTree.GetListHead(); + while (cur) + { + int treeIndex; + for (treeIndex=0; treeIndex < cur->size; treeIndex++) + ReleaseToDatagramMessageIDPool(cur->data[treeIndex]); + cur=cur->resendNext; + } + datagramMessageIDTree.Clear(_FILE_AND_LINE_); + datagramMessageIDPool.Clear(_FILE_AND_LINE_); + */ + + while (datagramHistory.Size()) + { + RemoveFromDatagramHistory(datagramHistoryPopCount); + datagramHistory.Pop(); + datagramHistoryPopCount++; + } + datagramHistoryMessagePool.Clear(_FILE_AND_LINE_); + datagramHistoryPopCount=0; + + acknowlegements.Clear(); + NAKs.Clear(); + + unreliableLinkedListHead=0; +} + +//------------------------------------------------------------------------------------------------------- +// Packets are read directly from the socket layer and skip the reliability +//layer because unconnected players do not use the reliability layer +// This function takes packet data after a player has been confirmed as +//connected. The game should not use that data directly +// because some data is used internally, such as packet acknowledgment and +//split packets +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::HandleSocketReceiveFromConnectedPlayer( + const char *buffer, unsigned int length, SystemAddress &systemAddress, DataStructures::List &messageHandlerList, int MTUSize, + SOCKET s, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType timeRead, + BitStream &updateBitStream) +{ +#ifdef _DEBUG + RakAssert( !( buffer == 0 ) ); +#endif + +#if CC_TIME_TYPE_BYTES==4 + timeRead/=1000; +#endif + + + bpsMetrics[(int) ACTUAL_BYTES_RECEIVED].Push1(timeRead,length); + + (void) MTUSize; + + if ( length <= 2 || buffer == 0 ) // Length of 1 is a connection request resend that we just ignore + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("length <= 2 || buffer == 0", BYTES_TO_BITS(length), systemAddress); + return true; + } + + timeLastDatagramArrived=RakNet::GetTimeMS(); + + // CCTimeType time; +// bool indexFound; +// int count, size; + DatagramSequenceNumberType holeCount; + unsigned i; + +#if LIBCAT_SECURITY==1 + if (useSecurity) + { + unsigned int received = length; + + if (!auth_enc.Decrypt((cat::u8*)buffer, received)) + return false; + + length = received; + } +#endif + + RakNet::BitStream socketData( (unsigned char*) buffer, length, false ); // Convert the incoming data to a bitstream for easy parsing + // time = RakNet::GetTimeUS(); + + // Set to the current time if it is not zero, and we get incoming data + // if (timeResendQueueNonEmpty!=0) + // timeResendQueueNonEmpty=timeRead; + + DatagramHeaderFormat dhf; + dhf.Deserialize(&socketData); + if (dhf.isValid==false) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("dhf.isValid==false", BYTES_TO_BITS(length), systemAddress); + + return true; + } + if (dhf.isACK) + { + DatagramSequenceNumberType datagramNumber; + // datagramNumber=dhf.datagramNumber; + +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMSLow=(RakNet::TimeMS) timeRead&0xFFFFFFFF; + CCTimeType rtt = timeMSLow-dhf.sourceSystemTime; +#if CC_TIME_TYPE_BYTES==4 + if (rtt > 10000) +#else + if (rtt > 10000000) +#endif + { + // Sanity check. This could happen due to type overflow, especially since I only send the low 4 bytes to reduce bandwidth + rtt=(CCTimeType) congestionManager.GetRTT(); + } + // RakAssert(rtt < 500000); + // printf("%i ", (RakNet::TimeMS)(rtt/1000)); + ackPing=rtt; +#endif + +#ifdef _DEBUG + if (dhf.hasBAndAS==false) + { + // dhf.B=0; + dhf.AS=0; + } +#endif + // congestionManager.OnAck(timeRead, rtt, dhf.hasBAndAS, dhf.B, dhf.AS, totalUserDataBytesAcked ); + + + incomingAcks.Clear(); + if (incomingAcks.Deserialize(&socketData)==false) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingAcks.Deserialize failed", BYTES_TO_BITS(length), systemAddress); + + return false; + } + for (i=0; iincomingAcks.ranges[i].maxIndex) + { + RakAssert(incomingAcks.ranges[i].minIndex<=incomingAcks.ranges[i].maxIndex); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingAcks minIndex > maxIndex", BYTES_TO_BITS(length), systemAddress); + return false; + } + for (datagramNumber=incomingAcks.ranges[i].minIndex; datagramNumber >= incomingAcks.ranges[i].minIndex && datagramNumber <= incomingAcks.ranges[i].maxIndex; datagramNumber++) + { + CCTimeType whenSent; + + if (unreliableWithAckReceiptHistory.Size()>0) + { + unsigned int k=0; + while (k < unreliableWithAckReceiptHistory.Size()) + { + if (unreliableWithAckReceiptHistory[k].datagramNumber == datagramNumber) + { + InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); + AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); + ackReceipt->dataBitLength=BYTES_TO_BITS(5); + ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_ACKED; + memcpy(ackReceipt->data+sizeof(MessageID), &unreliableWithAckReceiptHistory[k].sendReceiptSerial, sizeof(uint32_t)); + outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); + + // Remove, swap with last + unreliableWithAckReceiptHistory.RemoveAtIndex(k); + } + else + k++; + } + } + + MessageNumberNode *messageNumberNode = GetMessageNumberNodeByDatagramIndex(datagramNumber, &whenSent); + if (messageNumberNode) + { + // printf("%p Got ack for %i\n", this, datagramNumber.val); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + congestionManager.OnAck(timeRead, rtt, dhf.hasBAndAS, 0, dhf.AS, totalUserDataBytesAcked, bandwidthExceededStatistic, datagramNumber ); +#else + CCTimeType ping; + if (timeRead>whenSent) + ping=timeRead-whenSent; + else + ping=0; + congestionManager.OnAck(timeRead, ping, dhf.hasBAndAS, 0, dhf.AS, totalUserDataBytesAcked, bandwidthExceededStatistic, datagramNumber ); +#endif + while (messageNumberNode) + { + // TESTING1 +// printf("Remove %i on ack for datagramNumber=%i.\n", messageNumberNode->messageNumber.val, datagramNumber.val); + + RemovePacketFromResendListAndDeleteOlderReliableSequenced( messageNumberNode->messageNumber, timeRead, messageHandlerList, systemAddress ); + messageNumberNode=messageNumberNode->next; + } + + RemoveFromDatagramHistory(datagramNumber); + } +// else if (isReliable) +// { +// // Previously used slot, rather than empty unreliable slot +// printf("%p Ack %i is duplicate\n", this, datagramNumber.val); +// +// congestionManager.OnDuplicateAck(timeRead, datagramNumber); +// } + } + } + } + else if (dhf.isNAK) + { + DatagramSequenceNumberType messageNumber; + DataStructures::RangeList incomingNAKs; + if (incomingNAKs.Deserialize(&socketData)==false) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingNAKs.Deserialize failed", BYTES_TO_BITS(length), systemAddress); + + return false; + } + for (i=0; iincomingNAKs.ranges[i].maxIndex) + { + RakAssert(incomingNAKs.ranges[i].minIndex<=incomingNAKs.ranges[i].maxIndex); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingNAKs minIndex>maxIndex", BYTES_TO_BITS(length), systemAddress); + + return false; + } + // Sanity check + RakAssert(incomingNAKs.ranges[i].maxIndex.val-incomingNAKs.ranges[i].minIndex.val<1000); + for (messageNumber=incomingNAKs.ranges[i].minIndex; messageNumber >= incomingNAKs.ranges[i].minIndex && messageNumber <= incomingNAKs.ranges[i].maxIndex; messageNumber++) + { + congestionManager.OnNAK(timeRead, messageNumber); + + // REMOVEME + // printf("%p NAK %i\n", this, dhf.datagramNumber.val); + + + CCTimeType timeSent; + MessageNumberNode *messageNumberNode = GetMessageNumberNodeByDatagramIndex(messageNumber, &timeSent); + while (messageNumberNode) + { + // Update timers so resends occur immediately + InternalPacket *internalPacket = resendBuffer[messageNumberNode->messageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK]; + if (internalPacket) + { + if (internalPacket->nextActionTime!=0) + { + internalPacket->nextActionTime=timeRead; + } + } + + messageNumberNode=messageNumberNode->next; + } + } + } + } + else + { + uint32_t skippedMessageCount; + if (!congestionManager.OnGotPacket(dhf.datagramNumber, dhf.isContinuousSend, timeRead, length, &skippedMessageCount)) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("congestionManager.OnGotPacket failed", BYTES_TO_BITS(length), systemAddress); + + return true; + } + if (dhf.isPacketPair) + congestionManager.OnGotPacketPair(dhf.datagramNumber, length, timeRead); + + DatagramHeaderFormat dhfNAK; + dhfNAK.isNAK=true; + uint32_t skippedMessageOffset; + for (skippedMessageOffset=skippedMessageCount; skippedMessageOffset > 0; skippedMessageOffset--) + { + NAKs.Insert(dhf.datagramNumber-skippedMessageOffset); + } + remoteSystemNeedsBAndAS=dhf.needsBAndAs; + + // Ack dhf.datagramNumber + // Ack even unreliable messages for congestion control, just don't resend them on no ack +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + SendAcknowledgementPacket( dhf.datagramNumber, dhf.sourceSystemTime); +#else + SendAcknowledgementPacket( dhf.datagramNumber, 0); +#endif + + InternalPacket* internalPacket = CreateInternalPacketFromBitStream( &socketData, timeRead ); + if (internalPacket==0) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("CreateInternalPacketFromBitStream failed", BYTES_TO_BITS(length), systemAddress); + + return true; + } + + while ( internalPacket ) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + { +#if CC_TIME_TYPE_BYTES==4 + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, receivePacketCount, systemAddress, timeRead, false); +#else + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, receivePacketCount, systemAddress, (RakNet::TimeMS)(timeRead/(CCTimeType)1000), false); +#endif + } + + { + + // resetReceivedPackets is set from a non-threadsafe function. + // We do the actual reset in this function so the data is not modified by multiple threads + if (resetReceivedPackets) + { + hasReceivedPacketQueue.ClearAndForceAllocation(DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE, _FILE_AND_LINE_); + receivedPacketsBaseIndex=0; + resetReceivedPackets=false; + } + + // Check for corrupt orderingChannel + if ( + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED + ) + { + if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS", BYTES_TO_BITS(length), systemAddress); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + + // 8/12/09 was previously not checking if the message was reliable. However, on packetloss this would mean you'd eventually exceed the + // hole count because unreliable messages were never resent, and you'd stop getting messages + if (internalPacket->reliability == RELIABLE || internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_ORDERED ) + { + // If the following conditional is true then this either a duplicate packet + // or an older out of order packet + // The subtraction unsigned overflow is intentional + holeCount = (DatagramSequenceNumberType)(internalPacket->reliableMessageNumber-receivedPacketsBaseIndex); + const DatagramSequenceNumberType typeRange = (DatagramSequenceNumberType)(const uint32_t)-1; + + // TESTING1 +// printf("waiting on reliableMessageNumber=%i holeCount=%i datagramNumber=%i\n", receivedPacketsBaseIndex.val, holeCount.val, dhf.datagramNumber.val); + + if (holeCount==(DatagramSequenceNumberType) 0) + { + // Got what we were expecting + if (hasReceivedPacketQueue.Size()) + hasReceivedPacketQueue.Pop(); + ++receivedPacketsBaseIndex; + } + else if (holeCount > typeRange/(DatagramSequenceNumberType) 2) + { + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Duplicate packet + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + else if ((unsigned int) holeCountdataBitLength)); + + // Duplicate packet + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + else // holeCount>=receivedPackets.Size() + { + if (holeCount > (DatagramSequenceNumberType) 1000000) + { + RakAssert("Hole count too high. See ReliabilityLayer.h" && 0); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("holeCount > 1000000", BYTES_TO_BITS(length), systemAddress); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Would crash due to out of memory! + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + + // Fix - sending on a higher priority gives us a very very high received packets base index if we formerly had pre-split a lot of messages and + // used that as the message number. Because of this, a lot of time is spent in this linear loop and the timeout time expires because not + // all of the message is sent in time. + // Fixed by late assigning message IDs on the sender + + // Add 0 times to the queue until (reliableMessageNumber - baseIndex) < queue size. + while ((unsigned int)(holeCount) > hasReceivedPacketQueue.Size()) + hasReceivedPacketQueue.Push(true, _FILE_AND_LINE_ ); // time+(CCTimeType)60 * (CCTimeType)1000 * (CCTimeType)1000); // Didn't get this packet - set the time to give up waiting + hasReceivedPacketQueue.Push(false, _FILE_AND_LINE_ ); // Got the packet +#ifdef _DEBUG + // If this assert hits then DatagramSequenceNumberType has overflowed + RakAssert(hasReceivedPacketQueue.Size() < (unsigned int)((DatagramSequenceNumberType)(const uint32_t)(-1))); +#endif + } + + while ( hasReceivedPacketQueue.Size()>0 && hasReceivedPacketQueue.Peek()==false ) + { + hasReceivedPacketQueue.Pop(); + ++receivedPacketsBaseIndex; + } + } + + // If the allocated buffer is > DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE and it is 3x greater than the number of elements actually being used + if (hasReceivedPacketQueue.AllocationSize() > (unsigned int) DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE && hasReceivedPacketQueue.AllocationSize() > hasReceivedPacketQueue.Size() * 3) + hasReceivedPacketQueue.Compress(_FILE_AND_LINE_); + + + /* + if ( internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == UNRELIABLE_SEQUENCED ) + { +#ifdef _DEBUG + RakAssert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); +#endif + + if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) + { + + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS", BYTES_TO_BITS(length), systemAddress); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + if ( IsOlderOrderedPacket( internalPacket->orderingIndex, waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] ) == false ) + { + // Is this a split packet? + if ( internalPacket->splitPacketCount > 0 ) + { + // Generate the split + // Verify some parameters to make sure we don't get junk data + + + // Check for a rebuilt packet + InsertIntoSplitPacketList( internalPacket, timeRead ); + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Sequenced + internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, + s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions); + + if ( internalPacket ) + { + // Update our index to the newest packet + waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + (OrderingIndexType)1; + + // If there is a rebuilt packet, add it to the output queue + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + internalPacket = 0; + } + + // else don't have all the parts yet + } + else + { + // Update our index to the newest packet + waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + (OrderingIndexType)1; + + // Not a split packet. Add the packet to the output queue + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + internalPacket = 0; + } + } + else + { + // Older sequenced packet. Discard it + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + } + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + // Is this an unsequenced split packet? + if ( internalPacket->splitPacketCount > 0 ) + { + // Check for a rebuilt packet + if ( internalPacket->reliability != RELIABLE_ORDERED ) + internalPacket->orderingChannel = 255; // Use 255 to designate not sequenced and not ordered + + InsertIntoSplitPacketList( internalPacket, timeRead ); + + internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, + s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions); + + if ( internalPacket == 0 ) + { + + // Don't have all the parts yet + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + */ + + /* + if ( internalPacket->reliability == RELIABLE_ORDERED ) + { +#ifdef _DEBUG + RakAssert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); +#endif + + if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) + { + // Invalid packet + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + if ( waitingForOrderedPacketReadIndex[ internalPacket->orderingChannel ] == internalPacket->orderingIndex ) + { + // Get the list to hold ordered packets for this stream + DataStructures::LinkedList *orderingListAtOrderingStream; + unsigned char orderingChannelCopy = internalPacket->orderingChannel; + + // Push the packet for the user to read + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + internalPacket = 0; // Don't reference this any longer since other threads access it + + // Wait for the resendNext ordered packet in sequence + waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ++; // This wraps + + orderingListAtOrderingStream = GetOrderingListAtOrderingStream( orderingChannelCopy ); + + if ( orderingListAtOrderingStream != 0) + { + while ( orderingListAtOrderingStream->Size() > 0 ) + { + // Cycle through the list until nothing is found + orderingListAtOrderingStream->Beginning(); + indexFound=false; + size=orderingListAtOrderingStream->Size(); + count=0; + + while (count++ < size) + { + if ( orderingListAtOrderingStream->Peek()->orderingIndex == waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ) + { + outputQueue.Push( orderingListAtOrderingStream->Pop(), _FILE_AND_LINE_ ); + waitingForOrderedPacketReadIndex[ orderingChannelCopy ]++; + indexFound=true; + } + else + (*orderingListAtOrderingStream)++; + } + + if (indexFound==false) + break; + } + } + internalPacket = 0; + } + else + { + // This is a newer ordered packet than we are waiting for. Store it for future use + AddToOrderingList( internalPacket ); + } + + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + */ + + // Is this a split packet? If so then reassemble + if ( internalPacket->splitPacketCount > 0 ) + { + // Check for a rebuilt packet + if ( internalPacket->reliability != RELIABLE_ORDERED && internalPacket->reliability!=RELIABLE_SEQUENCED && internalPacket->reliability!=UNRELIABLE_SEQUENCED) + internalPacket->orderingChannel = 255; // Use 255 to designate not sequenced and not ordered + + InsertIntoSplitPacketList( internalPacket, timeRead ); + + internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, + s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); + + if ( internalPacket == 0 ) + { + // Don't have all the parts yet + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + + if (internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED) + { +#ifdef PRINT_TO_FILE_ROT + // ___________________ + BitStream bitStream(internalPacket->data, BITS_TO_BYTES(internalPacket->dataBitLength), false); + unsigned int receivedPacketNumber; + RakNet::Time receivedTime; + unsigned char streamNumber; + PacketReliability reliability; + // ___________________ + + + bitStream.IgnoreBits(8); // Ignore ID_TIMESTAMP + bitStream.Read(receivedTime); + bitStream.IgnoreBits(8); // Ignore ID_USER_ENUM+1 + bitStream.Read(receivedPacketNumber); + bitStream.Read(streamNumber); + bitStream.Read(reliability); + char *type="UNDEFINED"; + if (reliability==UNRELIABLE_SEQUENCED) + type="UNRELIABLE_SEQUENCED"; + else if (reliability==RELIABLE_ORDERED) + type="RELIABLE_ORDERED"; + // ___________________ +#endif + + + if (internalPacket->orderingIndex==orderedReadIndex[internalPacket->orderingChannel]) + { + // Has current ordering index + if (internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED) + { + // Is sequenced + if (IsOlderOrderedPacket(internalPacket->sequencingIndex,highestSequencedReadIndex[internalPacket->orderingChannel])==false) + { + // Expected or highest known value + + // Update highest sequence + highestSequencedReadIndex[internalPacket->orderingChannel] = internalPacket->sequencingIndex; + +#ifdef PRINT_TO_FILE_ROT + if (fp) + { + fprintf(fp, "Returning %i, %s by fallthrough. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); + fflush(fp); + } +#endif + + // Fallthrough, returned to user below + } + else + { +#ifdef PRINT_TO_FILE_ROT + if (fp) + { + fprintf(fp, "Discarding %i, %s late sequenced. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); + fflush(fp); + } +#endif + + // Lower than highest known value + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + else + { + // Push to output buffer immediately + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + +#ifdef PRINT_TO_FILE_ROT + if (fp) + { + fprintf(fp, "outputting immediate %i, %s. OI=%i. SI=%i.", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); + if (orderingHeaps[internalPacket->orderingChannel].Size()==0) + fprintf(fp, "heap empty\n"); + else + fprintf(fp, "heap head=%i\n", orderingHeaps[internalPacket->orderingChannel].Peek()->orderingIndex.val); + fflush(fp); + } +#endif + + orderedReadIndex[internalPacket->orderingChannel]++; + highestSequencedReadIndex[internalPacket->orderingChannel] = 0; + + // Return off heap until order lost + while (orderingHeaps[internalPacket->orderingChannel].Size()>0 && + orderingHeaps[internalPacket->orderingChannel].Peek()->orderingIndex==orderedReadIndex[internalPacket->orderingChannel]) + { + internalPacket = orderingHeaps[internalPacket->orderingChannel].Pop(0); + +#ifdef PRINT_TO_FILE_ROT + BitStream bitStream2(internalPacket->data, BITS_TO_BYTES(internalPacket->dataBitLength), false); + bitStream2.IgnoreBits(8); // Ignore ID_TIMESTAMP + bitStream2.Read(receivedTime); + bitStream2.IgnoreBits(8); // Ignore ID_USER_ENUM+1 + bitStream2.Read(receivedPacketNumber); + bitStream2.Read(streamNumber); + bitStream2.Read(reliability); + char *type="UNDEFINED"; + if (reliability==UNRELIABLE_SEQUENCED) + type="UNRELIABLE_SEQUENCED"; + else if (reliability==RELIABLE_ORDERED) + type="RELIABLE_ORDERED"; + + if (fp) + { + fprintf(fp, "Heap pop %i, %s. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); + fflush(fp); + } +#endif + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + + if (internalPacket->reliability == RELIABLE_ORDERED) + { + orderedReadIndex[internalPacket->orderingChannel]++; + } + else + { + highestSequencedReadIndex[internalPacket->orderingChannel] = internalPacket->sequencingIndex; + } + } + + // Done + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + else if (IsOlderOrderedPacket(internalPacket->orderingIndex,orderedReadIndex[internalPacket->orderingChannel])==false) + { + // internalPacket->_orderingIndex is greater + // If a message has a greater ordering index, and is sequenced or ordered, buffer it + // Sequenced has a lower heap weight, ordered has max sequenced weight + + // Keep orderedHoleCount count small + if (orderingHeaps[internalPacket->orderingChannel].Size()==0) + heapIndexOffsets[internalPacket->orderingChannel]=orderedReadIndex[internalPacket->orderingChannel]; + + reliabilityHeapWeightType orderedHoleCount = internalPacket->orderingIndex-heapIndexOffsets[internalPacket->orderingChannel]; + reliabilityHeapWeightType weight = orderedHoleCount*1048576; + if (internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED) + weight+=internalPacket->sequencingIndex; + else + weight+=(1048576-1); + orderingHeaps[internalPacket->orderingChannel].Push(weight, internalPacket, _FILE_AND_LINE_); + +#ifdef PRINT_TO_FILE_ROT + if (fp) + { + fprintf(fp, "Heap push %i, %s, weight=%llu. OI=%i. waiting on %i. SI=%i.\n", receivedPacketNumber, type, weight, internalPacket->orderingIndex.val, orderedReadIndex[internalPacket->orderingChannel].val, internalPacket->sequencingIndex); + fflush(fp); + } +#endif + + // Buffered, nothing to do + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + else + { + // Out of order + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + // Ignored, nothing to do + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + } + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Nothing special about this packet. Add it to the output queue + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + + internalPacket = 0; + } + + // Used for a goto to jump to the resendNext packet immediately + +CONTINUE_SOCKET_DATA_PARSE_LOOP: + // Parse the bitstream to create an internal packet + internalPacket = CreateInternalPacketFromBitStream( &socketData, timeRead ); + } + + } + + + receivePacketCount++; + + return true; +} + +//------------------------------------------------------------------------------------------------------- +// This gets an end-user packet already parsed out. Returns number of BITS put into the buffer +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::Receive( unsigned char **data ) +{ + InternalPacket * internalPacket; + + if ( outputQueue.Size() > 0 ) + { + // #ifdef _DEBUG + // RakAssert(bitStream->GetNumberOfBitsUsed()==0); + // #endif + internalPacket = outputQueue.Pop(); + + BitSize_t bitLength; + *data = internalPacket->data; + bitLength = internalPacket->dataBitLength; + ReleaseToInternalPacketPool( internalPacket ); + return bitLength; + } + + else + { + return 0; + } + +} + +//------------------------------------------------------------------------------------------------------- +// Puts data on the send queue +// bitStream contains the data to send +// priority is what priority to send the data at +// reliability is what reliability to use +// ordering channel is from 0 to 255 and specifies what stream to use +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::Send( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, CCTimeType currentTime, uint32_t receipt ) +{ +#ifdef _DEBUG + RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); + RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + RakAssert( numberOfBitsToSend > 0 ); +#endif + +#if CC_TIME_TYPE_BYTES==4 + currentTime/=1000; +#endif + + (void) MTUSize; + + // int a = BITS_TO_BYTES(numberOfBitsToSend); + + // Fix any bad parameters + if ( reliability > RELIABLE_ORDERED_WITH_ACK_RECEIPT || reliability < 0 ) + reliability = RELIABLE; + + if ( priority > NUMBER_OF_PRIORITIES || priority < 0 ) + priority = HIGH_PRIORITY; + + if ( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) + orderingChannel = 0; + + unsigned int numberOfBytesToSend=(unsigned int) BITS_TO_BYTES(numberOfBitsToSend); + if ( numberOfBitsToSend == 0 ) + { + return false; + } + InternalPacket * internalPacket = AllocateFromInternalPacketPool(); + if (internalPacket==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return false; // Out of memory + } + + bpsMetrics[(int) USER_MESSAGE_BYTES_PUSHED].Push1(currentTime,numberOfBytesToSend); + + internalPacket->creationTime = currentTime; + + if ( makeDataCopy ) + { + AllocInternalPacketData(internalPacket, numberOfBytesToSend, true, _FILE_AND_LINE_ ); + //internalPacket->data = (unsigned char*) rakMalloc_Ex( numberOfBytesToSend, _FILE_AND_LINE_ ); + memcpy( internalPacket->data, data, numberOfBytesToSend ); + } + else + { + // Allocated the data elsewhere, delete it in here + //internalPacket->data = ( unsigned char* ) data; + AllocInternalPacketData(internalPacket, (unsigned char*) data ); + } + + internalPacket->dataBitLength = numberOfBitsToSend; + internalPacket->messageInternalOrder = internalOrderIndex++; + internalPacket->priority = priority; + internalPacket->reliability = reliability; + internalPacket->sendReceiptSerial=receipt; + + // Calculate if I need to split the packet + // int headerLength = BITS_TO_BYTES( GetMessageHeaderLengthBits( internalPacket, true ) ); + + unsigned int maxDataSizeBytes = GetMaxDatagramSizeExcludingMessageHeaderBytes() - BITS_TO_BYTES(GetMaxMessageHeaderLengthBits()); + + bool splitPacket = numberOfBytesToSend > maxDataSizeBytes; + + // If a split packet, we might have to upgrade the reliability + if ( splitPacket ) + { + // Split packets cannot be unreliable, in case that one part doesn't arrive and the whole cannot be reassembled. + // One part could not arrive either due to packetloss or due to unreliable discard + if (internalPacket->reliability==UNRELIABLE) + internalPacket->reliability=RELIABLE; + else if (internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) + internalPacket->reliability=RELIABLE_WITH_ACK_RECEIPT; + else if (internalPacket->reliability==UNRELIABLE_SEQUENCED) + internalPacket->reliability=RELIABLE_SEQUENCED; +// else if (internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT) +// internalPacket->reliability=RELIABLE_SEQUENCED_WITH_ACK_RECEIPT; + } + + // ++sendMessageNumberIndex; + + if ( internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED +// || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || +// internalPacket->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + { + // Assign the sequence stream and index + internalPacket->orderingChannel = orderingChannel; + internalPacket->orderingIndex = orderedWriteIndex[ orderingChannel ]; + internalPacket->sequencingIndex = sequencedWriteIndex[ orderingChannel ]++; + + // This packet supersedes all other sequenced packets on the same ordering channel + // Delete all packets in all send lists that are sequenced and on the same ordering channel + // UPDATE: + // Disabled. We don't have enough info to consistently do this. Sometimes newer data does supercede + // older data such as with constantly declining health, but not in all cases. + // For example, with sequenced unreliable sound packets just because you send a newer one doesn't mean you + // don't need the older ones because the odds are they will still arrive in order + /* + for (int i=0; i < NUMBER_OF_PRIORITIES; i++) + { + DeleteSequencedPacketsInList(orderingChannel, sendQueue[i]); + } + */ + } + else if ( internalPacket->reliability == RELIABLE_ORDERED || internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT ) + { + // Assign the ordering channel and index + internalPacket->orderingChannel = orderingChannel; + internalPacket->orderingIndex = orderedWriteIndex[ orderingChannel ] ++; + sequencedWriteIndex[ orderingChannel ]=0; + } + + if ( splitPacket ) // If it uses a secure header it will be generated here + { + // Must split the packet. This will also generate the SHA1 if it is required. It also adds it to the send list. + //InternalPacket packetCopy; + //memcpy(&packetCopy, internalPacket, sizeof(InternalPacket)); + //sendPacketSet[priority].CancelWriteLock(internalPacket); + //SplitPacket( &packetCopy, MTUSize ); + SplitPacket( internalPacket ); + //RakNet::OP_DELETE_ARRAY(packetCopy.data, _FILE_AND_LINE_); + return true; + } + + RakAssert(internalPacket->dataBitLengthdataBitLengthmessageNumberAssigned==false); + outgoingPacketBuffer.Push( GetNextWeight(internalPacket->priority), internalPacket, _FILE_AND_LINE_ ); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]++; + statistics.bytesInSendBuffer[(int)internalPacket->priority]+=(double) BITS_TO_BYTES(internalPacket->dataBitLength); + + // sendPacketSet[priority].WriteUnlock(); + return true; +} +//------------------------------------------------------------------------------------------------------- +// Run this once per game cycle. Handles internal lists and actually does the send +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::Update( SOCKET s, SystemAddress &systemAddress, int MTUSize, CCTimeType time, + unsigned bitsPerSecondLimit, + DataStructures::List &messageHandlerList, + RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, + BitStream &updateBitStream) + +{ + (void) MTUSize; + + RakNet::TimeMS timeMs; +#if CC_TIME_TYPE_BYTES==4 + time/=1000; + timeMs=time; +#else + timeMs=(RakNet::TimeMS) (time/(CCTimeType)1000); +#endif + +#ifdef _DEBUG + while (delayList.Size()) + { + if (delayList.Peek()->sendTime <= timeMs) + { + DataAndTime *dat = delayList.Pop(); + SocketLayer::SendTo( dat->s, dat->data, dat->length, systemAddress, dat->remotePortRakNetWasStartedOn_PS3, dat->extraSocketOptions, __FILE__, __LINE__ ); + RakNet::OP_DELETE(dat,__FILE__,__LINE__); + } + break; + } +#endif + + // This line is necessary because the timer isn't accurate + if (time <= lastUpdateTime) + { + // Always set the last time in case of overflow + lastUpdateTime=time; + return; + } + + CCTimeType timeSinceLastTick = time - lastUpdateTime; + lastUpdateTime=time; +#if CC_TIME_TYPE_BYTES==4 + if (timeSinceLastTick>100) + timeSinceLastTick=100; +#else + if (timeSinceLastTick>100000) + timeSinceLastTick=100000; +#endif + + if (unreliableTimeout>0) + { + if (timeSinceLastTick>=timeToNextUnreliableCull) + { + if (unreliableLinkedListHead) + { + // Cull out all unreliable messages that have exceeded the timeout + InternalPacket *cur = unreliableLinkedListHead; + InternalPacket *end = unreliableLinkedListHead->unreliablePrev; + while (1) + { + if (time > cur->creationTime+(CCTimeType)unreliableTimeout) + { + // Flag invalid, and clear the memory. Still needs to be removed from the sendPacketSet later + // This fixes a problem where a remote system disconnects, but we don't know it yet, and memory consumption increases to a huge value + FreeInternalPacketData(cur, _FILE_AND_LINE_ ); + cur->data=0; + InternalPacket *next = cur->unreliableNext; + RemoveFromUnreliableLinkedList(cur); + + if (cur==end) + break; + + cur=next; + } + else + { + // if (cur==end) + // break; + // + // cur=cur->unreliableNext; + + // They should be inserted in-order, so no need to iterate past the first failure + break; + } + } + } + + timeToNextUnreliableCull=unreliableTimeout/(CCTimeType)2; + } + else + { + timeToNextUnreliableCull-=timeSinceLastTick; + } + } + + + // Due to thread vagarities and the way I store the time to avoid slow calls to RakNet::GetTime + // time may be less than lastAck +#if CC_TIME_TYPE_BYTES==4 + if ( statistics.messagesInResendBuffer!=0 && AckTimeout(time) ) +#else + if ( statistics.messagesInResendBuffer!=0 && AckTimeout(RakNet::TimeMS(time/(CCTimeType)1000)) ) +#endif + { + // SHOW - dead connection + // We've waited a very long time for a reliable packet to get an ack and it never has + deadConnection = true; + return; + } + + if (congestionManager.ShouldSendACKs(time,timeSinceLastTick)) + { + SendACKs(s, systemAddress, time, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); + } + + if (NAKs.Size()>0) + { + updateBitStream.Reset(); + DatagramHeaderFormat dhfNAK; + dhfNAK.isNAK=true; + dhfNAK.isACK=false; + dhfNAK.isPacketPair=false; + dhfNAK.Serialize(&updateBitStream); + NAKs.Serialize(&updateBitStream, GetMaxDatagramSizeExcludingMessageHeaderBits(), true); + SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); + } + + DatagramHeaderFormat dhf; + dhf.needsBAndAs=congestionManager.GetIsInSlowStart(); + dhf.isContinuousSend=bandwidthExceededStatistic; + // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || + // sendPacketSet[1].IsEmpty()==false || + // sendPacketSet[2].IsEmpty()==false || + // sendPacketSet[3].IsEmpty()==false; + bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; + + const bool hasDataToSendOrResend = IsResendQueueEmpty()==false || bandwidthExceededStatistic; + RakAssert(NUMBER_OF_PRIORITIES==4); + congestionManager.Update(time, hasDataToSendOrResend); + + statistics.BPSLimitByOutgoingBandwidthLimit = BITS_TO_BYTES(bitsPerSecondLimit); + statistics.BPSLimitByCongestionControl = congestionManager.GetBytesPerSecondLimitByCongestionControl(); + + unsigned int i; + if (time > lastBpsClear+ +#if CC_TIME_TYPE_BYTES==4 + 100 +#else + 100000 +#endif + ) + { + for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) + { + bpsMetrics[i].ClearExpired1(time); + } + + lastBpsClear=time; + } + + if (unreliableWithAckReceiptHistory.Size()>0) + { + i=0; + while (i < unreliableWithAckReceiptHistory.Size()) + { + if (unreliableWithAckReceiptHistory[i].nextActionTime < time) + { + InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); + AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); + ackReceipt->dataBitLength=BYTES_TO_BITS(5); + ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_LOSS; + memcpy(ackReceipt->data+sizeof(MessageID), &unreliableWithAckReceiptHistory[i].sendReceiptSerial, sizeof(uint32_t)); + outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); + + // Remove, swap with last + unreliableWithAckReceiptHistory.RemoveAtIndex(i); + } + else + i++; + } + } + + if (hasDataToSendOrResend==true) + { + InternalPacket *internalPacket; + // bool forceSend=false; + bool pushedAnything; + BitSize_t nextPacketBitLength; + dhf.isACK=false; + dhf.isNAK=false; + dhf.hasBAndAS=false; + ResetPacketsAndDatagrams(); + + int transmissionBandwidth = congestionManager.GetTransmissionBandwidth(time, timeSinceLastTick, unacknowledgedBytes,dhf.isContinuousSend); + int retransmissionBandwidth = congestionManager.GetRetransmissionBandwidth(time, timeSinceLastTick, unacknowledgedBytes,dhf.isContinuousSend); + if (retransmissionBandwidth>0 || transmissionBandwidth>0) + { + statistics.isLimitedByCongestionControl=false; + + allDatagramSizesSoFar=0; + + // Keep filling datagrams until we exceed retransmission bandwidth + while ((int)BITS_TO_BYTES(allDatagramSizesSoFar)messageNumberAssigned==true); + + if ( internalPacket->nextActionTime < time ) + { + nextPacketBitLength = internalPacket->headerLength + internalPacket->dataBitLength; + if ( datagramSizeSoFar + nextPacketBitLength > GetMaxDatagramSizeExcludingMessageHeaderBits() ) + { + // Gathers all PushPackets() + PushDatagram(); + break; + } + + PopListHead(false); + + CC_DEBUG_PRINTF_2("Rs %i ", internalPacket->reliableMessageNumber.val); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RESENT].Push1(time,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Testing1 +// if (internalPacket->reliability==RELIABLE_ORDERED || internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) +// printf("RESEND reliableMessageNumber %i with datagram %i\n", internalPacket->reliableMessageNumber.val, congestionManager.GetNextDatagramSequenceNumber().val); + + PushPacket(time,internalPacket,true); // Affects GetNewTransmissionBandwidth() +// internalPacket->timesSent++; + internalPacket->nextActionTime = congestionManager.GetRTOForRetransmission()+time; +#if CC_TIME_TYPE_BYTES==4 + if (internalPacket->nextActionTime-time > 10000) +#else + if (internalPacket->nextActionTime-time > 10000000) +#endif + { + // int a=5; + RakAssert(0); + } + + congestionManager.OnResend(time); + + pushedAnything=true; + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + { +#if CC_TIME_TYPE_BYTES==4 + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS) time, true); +#else + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000), true); +#endif + } + + // Put the packet back into the resend list at the correct spot + // Don't make a copy since I'm reinserting an allocated struct + InsertPacketIntoResendList( internalPacket, time, false, false ); + + // Removeme + // printf("Resend:%i ", internalPacket->reliableMessageNumber); + } + else + { + // Filled one datagram. + // If the 2nd and it's time to send a datagram pair, will be marked as a pair + PushDatagram(); + break; + } + } + + if (pushedAnything==false) + break; + } + } + else + { + statistics.isLimitedByCongestionControl=true; + } + + if ((int)BITS_TO_BYTES(allDatagramSizesSoFar)messageNumberAssigned==false); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthdata==0) + { + //sendPacketSet[ i ].Pop(); + outgoingPacketBuffer.Pop(0); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]--; + statistics.bytesInSendBuffer[(int)internalPacket->priority]-=(double) BITS_TO_BYTES(internalPacket->dataBitLength); + ReleaseToInternalPacketPool( internalPacket ); + continue; + } + + internalPacket->headerLength=GetMessageHeaderLengthBits(internalPacket); + nextPacketBitLength = internalPacket->headerLength + internalPacket->dataBitLength; + if ( datagramSizeSoFar + nextPacketBitLength > GetMaxDatagramSizeExcludingMessageHeaderBits() ) + { + // Hit MTU. May still push packets if smaller ones exist at a lower priority + RakAssert(datagramSizeSoFar!=0); + RakAssert(internalPacket->dataBitLengthreliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + isReliable = true; + else + isReliable = false; + + //sendPacketSet[ i ].Pop(); + outgoingPacketBuffer.Pop(0); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthmessageNumberAssigned==false); + statistics.messageInSendBuffer[(int)internalPacket->priority]--; + statistics.bytesInSendBuffer[(int)internalPacket->priority]-=(double) BITS_TO_BYTES(internalPacket->dataBitLength); + if (isReliable + /* + I thought about this and agree that UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT and RELIABLE_SEQUENCED_WITH_ACK_RECEIPT is not useful unless you also know if the message was discarded. + + The problem is that internally, message numbers are only assigned to reliable messages, because message numbers are only used to discard duplicate message receipt and only reliable messages get sent more than once. However, without message numbers getting assigned and transmitted, there is no way to tell the sender about which messages were discarded. In fact, in looking this over I realized that UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT introduced a bug, because the remote system assumes all message numbers are used (no holes). With that send type, on packetloss, a permanent hole would have been created which eventually would cause the system to discard all further packets. + + So I have two options. Either do not support ack receipts when sending sequenced, or write complex and major new systems. UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT would need to send the message ID number on a special channel which allows for non-delivery. And both of them would need to have a special range list to indicate which message numbers were not delivered, so when acks are sent that can be indicated as well. A further problem is that the ack itself can be lost - it is possible that the message can arrive but be discarded, yet the ack is lost. On resend, the resent message would be ignored as duplicate, and you'd never get the discard message either (unless I made a special buffer for that case too). +*/ +// || + // If needs an ack receipt, keep the internal packet around in the list +// internalPacket->reliability == UNRELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + { + internalPacket->messageNumberAssigned=true; + internalPacket->reliableMessageNumber=sendReliableMessageNumberIndex; + internalPacket->nextActionTime = congestionManager.GetRTOForRetransmission()+time; +#if CC_TIME_TYPE_BYTES==4 + const CCTimeType threshhold = 10000; +#else + const CCTimeType threshhold = 10000000; +#endif + if (internalPacket->nextActionTime-time > threshhold) + { + // int a=5; + RakAssert(time-internalPacket->nextActionTime < threshhold); + } + //resendTree.Insert( internalPacket->reliableMessageNumber, internalPacket); + if (resendBuffer[internalPacket->reliableMessageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK]!=0) + { + // bool overflow = ResendBufferOverflow(); + RakAssert(0); + } + resendBuffer[internalPacket->reliableMessageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK] = internalPacket; + statistics.messagesInResendBuffer++; + statistics.bytesInResendBuffer+=BITS_TO_BYTES(internalPacket->dataBitLength); + + // printf("pre:%i ", unacknowledgedBytes); + + InsertPacketIntoResendList( internalPacket, time, true, isReliable); + + + // printf("post:%i ", unacknowledgedBytes); + sendReliableMessageNumberIndex++; + } + else if (internalPacket->reliability == UNRELIABLE_WITH_ACK_RECEIPT) + { + unreliableWithAckReceiptHistory.Push(UnreliableWithAckReceiptNode( + congestionManager.GetNextDatagramSequenceNumber() + packetsToSendThisUpdateDatagramBoundaries.Size(), + internalPacket->sendReceiptSerial, + congestionManager.GetRTOForRetransmission()+time + ), _FILE_AND_LINE_); + } + +// internalPacket->timesSent=1; + // If isReliable is false, the packet and its contents will be added to a list to be freed in ClearPacketsAndDatagrams + // However, the internalPacket structure will remain allocated and be in the resendBuffer list if it requires a receipt + bpsMetrics[(int) USER_MESSAGE_BYTES_SENT].Push1(time,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Testing1 +// if (internalPacket->reliability==RELIABLE_ORDERED || internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) +// printf("SEND reliableMessageNumber %i in datagram %i\n", internalPacket->reliableMessageNumber.val, congestionManager.GetNextDatagramSequenceNumber().val); + + PushPacket(time,internalPacket, isReliable); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + { +#if CC_TIME_TYPE_BYTES==4 + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)time, true); +#else + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000), true); +#endif + } + pushedAnything=true; + + if (ResendBufferOverflow()) + break; + } + // if (ResendBufferOverflow()) + // break; + // }z + + // No datagrams pushed? + if (datagramSizeSoFar==0) + break; + + // Filled one datagram. + // If the 2nd and it's time to send a datagram pair, will be marked as a pair + PushDatagram(); + } + } + + + for (unsigned int datagramIndex=0; datagramIndex < packetsToSendThisUpdateDatagramBoundaries.Size(); datagramIndex++) + { + if (datagramIndex>0) + dhf.isContinuousSend=true; + MessageNumberNode* messageNumberNode = 0; + dhf.datagramNumber=congestionManager.GetAndIncrementNextDatagramSequenceNumber(); + dhf.isPacketPair=datagramsToSendThisUpdateIsPair[datagramIndex]; + + //printf("%p pushing datagram %i\n", this, dhf.datagramNumber.val); + + bool isSecondOfPacketPair=dhf.isPacketPair && datagramIndex>0 && datagramsToSendThisUpdateIsPair[datagramIndex-1]; + unsigned int msgIndex, msgTerm; + if (datagramIndex==0) + { + msgIndex=0; + msgTerm=packetsToSendThisUpdateDatagramBoundaries[0]; + } + else + { + msgIndex=packetsToSendThisUpdateDatagramBoundaries[datagramIndex-1]; + msgTerm=packetsToSendThisUpdateDatagramBoundaries[datagramIndex]; + } + + // More accurate time to reset here +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + dhf.sourceSystemTime=RakNet::GetTimeUS(); +#endif + updateBitStream.Reset(); + dhf.Serialize(&updateBitStream); + CC_DEBUG_PRINTF_2("S%i ",dhf.datagramNumber.val); + + while (msgIndex < msgTerm) + { + // If reliable or needs receipt + if ( packetsToSendThisUpdate[msgIndex]->reliability != UNRELIABLE && + packetsToSendThisUpdate[msgIndex]->reliability != UNRELIABLE_SEQUENCED + ) + { + if (messageNumberNode==0) + { + messageNumberNode = AddFirstToDatagramHistory(dhf.datagramNumber, packetsToSendThisUpdate[msgIndex]->reliableMessageNumber, time); + } + else + { + messageNumberNode = AddSubsequentToDatagramHistory(messageNumberNode, packetsToSendThisUpdate[msgIndex]->reliableMessageNumber); + } + } + + RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + WriteToBitStreamFromInternalPacket( &updateBitStream, packetsToSendThisUpdate[msgIndex], time ); + RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + msgIndex++; + } + + if (isSecondOfPacketPair) + { + // Pad to size of first datagram + RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + updateBitStream.PadWithZeroToByteLength(datagramSizesInBytes[datagramIndex-1]); + RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + } + + if (messageNumberNode==0) + { + // Unreliable, add dummy node + AddFirstToDatagramHistory(dhf.datagramNumber, time); + } + + // Store what message ids were sent with this datagram + // datagramMessageIDTree.Insert(dhf.datagramNumber,idList); + + congestionManager.OnSendBytes(time,UDP_HEADER_SIZE+DatagramHeaderFormat::GetDataHeaderByteLength()); + + SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); + + bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; + // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || + // sendPacketSet[1].IsEmpty()==false || + // sendPacketSet[2].IsEmpty()==false || + // sendPacketSet[3].IsEmpty()==false; + + + + if (bandwidthExceededStatistic==true) + timeOfLastContinualSend=time; + else + timeOfLastContinualSend=0; + } + + ClearPacketsAndDatagrams(); + + // Any data waiting to send after attempting to send, then bandwidth is exceeded + bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; + // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || + // sendPacketSet[1].IsEmpty()==false || + // sendPacketSet[2].IsEmpty()==false || + // sendPacketSet[3].IsEmpty()==false; + } + + + // Keep on top of deleting old unreliable split packets so they don't clog the list. + //DeleteOldUnreliableSplitPackets( time ); +} + +//------------------------------------------------------------------------------------------------------- +// Writes a bitstream to the socket +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SendBitStream( SOCKET s, SystemAddress &systemAddress, RakNet::BitStream *bitStream, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType currentTime) +{ + (void) systemAddress; + (void) rnr; + + unsigned int length; + + length = (unsigned int) bitStream->GetNumberOfBytesUsed(); + + +#ifdef _DEBUG + if (packetloss > 0.0) + { + if (frandomMT() < packetloss) + return; + } + + if (minExtraPing > 0 || extraPingVariance > 0) + { + RakNet::TimeMS delay = minExtraPing; + if (extraPingVariance>0) + delay += (randomMT() % extraPingVariance); + if (delay > 0) + { + DataAndTime *dat = RakNet::OP_NEW(__FILE__,__LINE__); + memcpy(dat->data, ( char* ) bitStream->GetData(), length ); + dat->s=s; + dat->length=length; + dat->sendTime = RakNet::GetTimeMS() + delay; + dat->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; + dat->extraSocketOptions=extraSocketOptions; + for (unsigned int i=0; i < delayList.Size(); i++) + { + if (dat->sendTime < delayList[i]->sendTime) + { + delayList.PushAtHead(dat, i, __FILE__, __LINE__); + dat=0; + break; + } + } + if (dat!=0) + delayList.Push(dat,__FILE__,__LINE__); + return; + } + } +#endif + +#if LIBCAT_SECURITY==1 + if (useSecurity) + { + unsigned char *buffer = reinterpret_cast( bitStream->GetData() ); + + int buffer_size = bitStream->GetNumberOfBitsAllocated() / 8; + + // Verify there is enough room for encrypted output and encrypt + // Encrypt() will increase length + bool success = auth_enc.Encrypt(buffer, buffer_size, length); + RakAssert(success); + } +#endif + + bpsMetrics[(int) ACTUAL_BYTES_SENT].Push1(currentTime,length); + + RakAssert(length <= congestionManager.GetMTU()); + +#ifdef USE_THREADED_SEND + SendToThread::SendToThreadBlock *block = SendToThread::AllocateBlock(); + memcpy(block->data, bitStream->GetData(), length); + block->dataWriteOffset=length; + block->extraSocketOptions=extraSocketOptions; + block->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; + block->s=s; + block->systemAddress=systemAddress; + SendToThread::ProcessBlock(block); +#else + SocketLayer::SendTo( s, ( char* ) bitStream->GetData(), length, systemAddress, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, __FILE__, __LINE__ ); +#endif +} + +//------------------------------------------------------------------------------------------------------- +// Are we waiting for any data to be sent out or be processed by the player? +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsOutgoingDataWaiting(void) +{ + if (outgoingPacketBuffer.Size()>0) + return true; + + // unsigned i; + // for ( i = 0; i < NUMBER_OF_PRIORITIES; i++ ) + // { + // if (sendPacketSet[ i ].Size() > 0) + // return true; + // } + + return + //acknowlegements.Size() > 0 || + //resendTree.IsEmpty()==false;// || outputQueue.Size() > 0 || orderingList.Size() > 0 || splitPacketChannelList.Size() > 0; + statistics.messagesInResendBuffer!=0; +} +bool ReliabilityLayer::AreAcksWaiting(void) +{ + return acknowlegements.Size() > 0; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ApplyNetworkSimulator( double _packetloss, RakNet::TimeMS _minExtraPing, RakNet::TimeMS _extraPingVariance ) +{ +#ifdef _DEBUG + packetloss=_packetloss; + minExtraPing=_minExtraPing; + extraPingVariance=_extraPingVariance; + // if (ping < (unsigned int)(minExtraPing+extraPingVariance)*2) + // ping=(minExtraPing+extraPingVariance)*2; +#endif +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SetSplitMessageProgressInterval(int interval) +{ + splitMessageProgressInterval=interval; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SetUnreliableTimeout(RakNet::TimeMS timeoutMS) +{ +#if CC_TIME_TYPE_BYTES==4 + unreliableTimeout=timeoutMS; +#else + unreliableTimeout=(CCTimeType)timeoutMS*(CCTimeType)1000; +#endif +} + +//------------------------------------------------------------------------------------------------------- +// This will return true if we should not send at this time +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsSendThrottled( int MTUSize ) +{ + (void) MTUSize; + + return false; + // return resendList.Size() > windowSize; + + // Disabling this, because it can get stuck here forever + /* + unsigned packetsWaiting; + unsigned resendListDataSize=0; + unsigned i; + for (i=0; i < resendList.Size(); i++) + { + if (resendList[i]) + resendListDataSize+=resendList[i]->dataBitLength; + } + packetsWaiting = 1 + ((BITS_TO_BYTES(resendListDataSize)) / (MTUSize - UDP_HEADER_SIZE - 10)); // 10 to roughly estimate the raknet header + + return packetsWaiting >= windowSize; + */ +} + +//------------------------------------------------------------------------------------------------------- +// We lost a packet +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::UpdateWindowFromPacketloss( CCTimeType time ) +{ + (void) time; +} + +//------------------------------------------------------------------------------------------------------- +// Increase the window size +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::UpdateWindowFromAck( CCTimeType time ) +{ + (void) time; +} + +//------------------------------------------------------------------------------------------------------- +// Does what the function name says +//------------------------------------------------------------------------------------------------------- +unsigned ReliabilityLayer::RemovePacketFromResendListAndDeleteOlderReliableSequenced( const MessageNumberType messageNumber, CCTimeType time, DataStructures::List &messageHandlerList, const SystemAddress &systemAddress ) +{ + (void) time; + (void) messageNumber; + InternalPacket * internalPacket; + //InternalPacket *temp; +// PacketReliability reliability; // What type of reliability algorithm to use with this packet +// unsigned char orderingChannel; // What ordering channel this packet is on, if the reliability type uses ordering channels +// OrderingIndexType orderingIndex; // The ID used as identification for ordering channels + // unsigned j; + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + { +#if CC_TIME_TYPE_BYTES==4 + messageHandlerList[messageHandlerIndex]->OnAck(messageNumber, systemAddress, time); +#else + messageHandlerList[messageHandlerIndex]->OnAck(messageNumber, systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000)); +#endif + } + + // Testing1 +// if (resendLinkedListHead) +// { +// InternalPacket *internalPacket = resendLinkedListHead; +// do +// { +// internalPacket=internalPacket->resendNext; +// printf("%i ", internalPacket->reliableMessageNumber.val); +// } while (internalPacket!=resendLinkedListHead); +// printf("\n"); +// } + + // bool deleted; + // deleted=resendTree.Delete(messageNumber, internalPacket); + internalPacket = resendBuffer[messageNumber & RESEND_BUFFER_ARRAY_MASK]; + // May ask to remove twice, for example resend twice, then second ack + if (internalPacket && internalPacket->reliableMessageNumber==messageNumber) + { + // ValidateResendList(); + resendBuffer[messageNumber & RESEND_BUFFER_ARRAY_MASK]=0; + CC_DEBUG_PRINTF_2("AckRcv %i ", messageNumber); + + statistics.messagesInResendBuffer--; + statistics.bytesInResendBuffer-=BITS_TO_BYTES(internalPacket->dataBitLength); + +// orderingIndex = internalPacket->orderingIndex; + totalUserDataBytesAcked+=(double) BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); + + // Return receipt if asked for + if (internalPacket->reliability>=RELIABLE_WITH_ACK_RECEIPT && + (internalPacket->splitPacketCount==0 || internalPacket->splitPacketIndex+1==internalPacket->splitPacketCount) + ) + { + InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); + AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); + ackReceipt->dataBitLength=BYTES_TO_BITS(5); + ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_ACKED; + memcpy(ackReceipt->data+sizeof(MessageID), &internalPacket->sendReceiptSerial, sizeof(internalPacket->sendReceiptSerial)); + outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); + } + + bool isReliable; + if ( internalPacket->reliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + isReliable = true; + else + isReliable = false; + + RemoveFromList(internalPacket, isReliable); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + + return 0; + } + else + { + + } + + return (unsigned)-1; +} + +//------------------------------------------------------------------------------------------------------- +// Acknowledge receipt of the packet with the specified messageNumber +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SendAcknowledgementPacket( const DatagramSequenceNumberType messageNumber, CCTimeType time ) +{ + + // REMOVEME + // printf("%p Send ack %i\n", this, messageNumber.val); + + nextAckTimeToSend=time; + acknowlegements.Insert(messageNumber); + + //printf("ACK_DG:%i ", messageNumber.val); + + CC_DEBUG_PRINTF_2("AckPush %i ", messageNumber); + +} + +//------------------------------------------------------------------------------------------------------- +// Parse an internalPacket and figure out how many header bits would be +// written. Returns that number +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::GetMaxMessageHeaderLengthBits( void ) +{ + InternalPacket ip; + ip.reliability=RELIABLE_SEQUENCED; + ip.splitPacketCount=1; + return GetMessageHeaderLengthBits(&ip); +} +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::GetMessageHeaderLengthBits( const InternalPacket *const internalPacket ) +{ + BitSize_t bitLength; + + // bitStream->AlignWriteToByteBoundary(); // Potentially unaligned + // tempChar=(unsigned char)internalPacket->reliability; bitStream->WriteBits( (const unsigned char *)&tempChar, 3, true ); // 3 bits to write reliability. + // bool hasSplitPacket = internalPacket->splitPacketCount>0; bitStream->Write(hasSplitPacket); // Write 1 bit to indicate if splitPacketCount>0 + bitLength = 8*1; + + // bitStream->AlignWriteToByteBoundary(); + // RakAssert(internalPacket->dataBitLength < 65535); + // unsigned short s; s = (unsigned short) internalPacket->dataBitLength; bitStream->WriteAlignedVar16((const char*)& s); + bitLength += 8*2; + + if ( internalPacket->reliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + bitLength += 8*3; // bitStream->Write(internalPacket->reliableMessageNumber); // Message sequence number + // bitStream->AlignWriteToByteBoundary(); // Potentially nothing else to write + + + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED + ) + { + bitLength += 8*3;; // bitStream->Write(internalPacket->_sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + } + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + { + bitLength += 8*3; // bitStream->Write(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + bitLength += 8*1; // tempChar=internalPacket->orderingChannel; bitStream->WriteAlignedVar8((const char*)& tempChar); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, write one byte + } + if (internalPacket->splitPacketCount>0) + { + bitLength += 8*4; // bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketCount); RakAssert(sizeof(SplitPacketIndexType)==4); // Only needed if splitPacketCount>0. 4 bytes + bitLength += 8*sizeof(SplitPacketIdType); // bitStream->WriteAlignedVar16((const char*)& internalPacket->splitPacketId); RakAssert(sizeof(SplitPacketIdType)==2); // Only needed if splitPacketCount>0. + bitLength += 8*4; // bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes + } + + return bitLength; +} + +//------------------------------------------------------------------------------------------------------- +// Parse an internalPacket and create a bitstream to represent this data +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket, CCTimeType curTime ) +{ + (void) curTime; + + BitSize_t start = bitStream->GetNumberOfBitsUsed(); + unsigned char tempChar; + + // (Incoming data may be all zeros due to padding) + bitStream->AlignWriteToByteBoundary(); // Potentially unaligned + if (internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) + tempChar=UNRELIABLE; + else if (internalPacket->reliability==RELIABLE_WITH_ACK_RECEIPT) + tempChar=RELIABLE; + else if (internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) + tempChar=RELIABLE_ORDERED; + else + tempChar=(unsigned char)internalPacket->reliability; + + bitStream->WriteBits( (const unsigned char *)&tempChar, 3, true ); // 3 bits to write reliability. + + bool hasSplitPacket = internalPacket->splitPacketCount>0; bitStream->Write(hasSplitPacket); // Write 1 bit to indicate if splitPacketCount>0 + bitStream->AlignWriteToByteBoundary(); + RakAssert(internalPacket->dataBitLength < 65535); + unsigned short s; s = (unsigned short) internalPacket->dataBitLength; bitStream->WriteAlignedVar16((const char*)& s); + if ( internalPacket->reliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + bitStream->Write(internalPacket->reliableMessageNumber); // Used for all reliable types + bitStream->AlignWriteToByteBoundary(); // Potentially nothing else to write + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED + ) + { + bitStream->Write(internalPacket->sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + } + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + { + bitStream->Write(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + tempChar=internalPacket->orderingChannel; bitStream->WriteAlignedVar8((const char*)& tempChar); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, write one byte + } + + if (internalPacket->splitPacketCount>0) + { + // printf("Write before\n"); + // bitStream->PrintBits(); + + bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketCount); RakAssert(sizeof(SplitPacketIndexType)==4); // Only needed if splitPacketCount>0. 4 bytes + bitStream->WriteAlignedVar16((const char*)& internalPacket->splitPacketId); RakAssert(sizeof(SplitPacketIdType)==2); // Only needed if splitPacketCount>0. + bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes + + // printf("Write after\n"); + // bitStream->PrintBits(); + } + + // Write the actual data. + bitStream->WriteAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); + + return bitStream->GetNumberOfBitsUsed() - start; +} + +//------------------------------------------------------------------------------------------------------- +// Parse a bitstream and create an internal packet to represent this data +//------------------------------------------------------------------------------------------------------- +InternalPacket* ReliabilityLayer::CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, CCTimeType time ) +{ + bool bitStreamSucceeded; + InternalPacket* internalPacket; + unsigned char tempChar; + bool hasSplitPacket=false; + bool readSuccess; + + if ( bitStream->GetNumberOfUnreadBits() < (int) sizeof( internalPacket->reliableMessageNumber ) * 8 ) + return 0; // leftover bits + + internalPacket = AllocateFromInternalPacketPool(); + if (internalPacket==0) + { + // Out of memory + RakAssert(0); + return 0; + } + internalPacket->creationTime = time; + + // (Incoming data may be all zeros due to padding) + bitStream->AlignReadToByteBoundary(); // Potentially unaligned + bitStream->ReadBits( ( unsigned char* ) ( &( tempChar ) ), 3 ); + internalPacket->reliability = ( const PacketReliability ) tempChar; + readSuccess=bitStream->Read(hasSplitPacket); // Read 1 bit to indicate if splitPacketCount>0 + bitStream->AlignReadToByteBoundary(); + unsigned short s; bitStream->ReadAlignedVar16((char*)&s); internalPacket->dataBitLength=s; // Length of message (2 bytes) + if ( internalPacket->reliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED + // I don't write ACK_RECEIPT to the remote system +// || +// internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + bitStream->Read(internalPacket->reliableMessageNumber); // Message sequence number + else + internalPacket->reliableMessageNumber=(MessageNumberType)(const uint32_t)-1; + bitStream->AlignReadToByteBoundary(); // Potentially nothing else to Read + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED + ) + { + bitStream->Read(internalPacket->sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + } + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + { + bitStream->Read(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 4 bytes. + readSuccess=bitStream->ReadAlignedVar8((char*)& internalPacket->orderingChannel); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, Read one byte + } + else + internalPacket->orderingChannel=0; + + if (hasSplitPacket) + { +// printf("Read before\n"); +// bitStream->PrintBits(); + + bitStream->ReadAlignedVar32((char*)& internalPacket->splitPacketCount); // Only needed if splitPacketCount>0. 4 bytes + bitStream->ReadAlignedVar16((char*)& internalPacket->splitPacketId); // Only needed if splitPacketCount>0. + readSuccess=bitStream->ReadAlignedVar32((char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes + RakAssert(readSuccess); + +// printf("Read after\n"); +// bitStream->PrintBits(); + } + else + { + internalPacket->splitPacketCount=0; + } + + if (readSuccess==false || + internalPacket->dataBitLength==0 || + internalPacket->reliability>=NUMBER_OF_RELIABILITIES || + internalPacket->orderingChannel>=32 || + (hasSplitPacket && (internalPacket->splitPacketIndex >= internalPacket->splitPacketCount))) + { + // If this assert hits, encoding is garbage + RakAssert("Encoding is garbage" && 0); + ReleaseToInternalPacketPool( internalPacket ); + return 0; + } + + // Allocate memory to hold our data + AllocInternalPacketData(internalPacket, BITS_TO_BYTES( internalPacket->dataBitLength ), false, _FILE_AND_LINE_ ); + RakAssert(BITS_TO_BYTES( internalPacket->dataBitLength )data == 0) + { + RakAssert("Out of memory in ReliabilityLayer::CreateInternalPacketFromBitStream" && 0); + notifyOutOfMemory(_FILE_AND_LINE_); + ReleaseToInternalPacketPool( internalPacket ); + return 0; + } + + // Set the last byte to 0 so if ReadBits does not read a multiple of 8 the last bits are 0'ed out + internalPacket->data[ BITS_TO_BYTES( internalPacket->dataBitLength ) - 1 ] = 0; + + // Read the data the packet holds + bitStreamSucceeded = bitStream->ReadAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); + + if ( bitStreamSucceeded == false ) + { + // If this hits, most likely the variable buff is too small in RunUpdateCycle in RakPeer.cpp + RakAssert("Couldn't read all the data" && 0); + + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + return 0; + } + + return internalPacket; +} + + +//------------------------------------------------------------------------------------------------------- +// Get the SHA1 code +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::GetSHA1( unsigned char * const buffer, unsigned int + nbytes, char code[ SHA1_LENGTH ] ) +{ + CSHA1 sha1; + + sha1.Reset(); + sha1.Update( ( unsigned char* ) buffer, nbytes ); + sha1.Final(); + memcpy( code, sha1.GetHash(), SHA1_LENGTH ); +} + +//------------------------------------------------------------------------------------------------------- +// Check the SHA1 code +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * + const buffer, unsigned int nbytes ) +{ + char code2[ SHA1_LENGTH ]; + GetSHA1( buffer, nbytes, code2 ); + + for ( int i = 0; i < SHA1_LENGTH; i++ ) + if ( code[ i ] != code2[ i ] ) + return false; + + return true; +} + +//------------------------------------------------------------------------------------------------------- +// Search the specified list for sequenced packets on the specified ordering +// stream, optionally skipping those with splitPacketId, and delete them +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::List&theList, int splitPacketId ) +{ + unsigned i = 0; + + while ( i < theList.Size() ) + { + if ( ( + theList[ i ]->reliability == RELIABLE_SEQUENCED || + theList[ i ]->reliability == UNRELIABLE_SEQUENCED +// || +// theList[ i ]->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || +// theList[ i ]->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) && + theList[ i ]->orderingChannel == orderingChannel && ( splitPacketId == -1 || theList[ i ]->splitPacketId != (unsigned int) splitPacketId ) ) + { + InternalPacket * internalPacket = theList[ i ]; + theList.RemoveAtIndex( i ); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + } + + else + i++; + } +} + +//------------------------------------------------------------------------------------------------------- +// Search the specified list for sequenced packets with a value less than orderingIndex and delete them +// Note - I added functionality so you can use the Queue as a list (in this case for searching) but it is less efficient to do so than a regular list +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::Queue&theList ) +{ + InternalPacket * internalPacket; + int listSize = theList.Size(); + int i = 0; + + while ( i < listSize ) + { + if ( ( + theList[ i ]->reliability == RELIABLE_SEQUENCED || + theList[ i ]->reliability == UNRELIABLE_SEQUENCED +// || +// theList[ i ]->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || +// theList[ i ]->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) && theList[ i ]->orderingChannel == orderingChannel ) + { + internalPacket = theList[ i ]; + theList.RemoveAtIndex( i ); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + listSize--; + } + + else + i++; + } +} + +//------------------------------------------------------------------------------------------------------- +// Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ) +{ + OrderingIndexType maxRange = (OrderingIndexType) (const uint32_t)-1; + + if ( waitingForPacketOrderingIndex > maxRange/(OrderingIndexType)2 ) + { + if ( newPacketOrderingIndex >= waitingForPacketOrderingIndex - maxRange/(OrderingIndexType)2+(OrderingIndexType)1 && newPacketOrderingIndex < waitingForPacketOrderingIndex ) + { + return true; + } + } + + else + if ( newPacketOrderingIndex >= ( OrderingIndexType ) ( waitingForPacketOrderingIndex - (( OrderingIndexType ) maxRange/(OrderingIndexType)2+(OrderingIndexType)1) ) || + newPacketOrderingIndex < waitingForPacketOrderingIndex ) + { + return true; + } + + // Old packet + return false; +} + +//------------------------------------------------------------------------------------------------------- +// Split the passed packet into chunks under MTU_SIZEbytes (including headers) and save those new chunks +// Optimized version +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SplitPacket( InternalPacket *internalPacket ) +{ + // Doing all sizes in bytes in this function so I don't write partial bytes with split packets + internalPacket->splitPacketCount = 1; // This causes GetMessageHeaderLengthBits to account for the split packet header + unsigned int headerLength = (unsigned int) BITS_TO_BYTES( GetMessageHeaderLengthBits( internalPacket ) ); + unsigned int dataByteLength = (unsigned int) BITS_TO_BYTES( internalPacket->dataBitLength ); + int maximumSendBlockBytes, byteOffset, bytesToSend; + SplitPacketIndexType splitPacketIndex; + int i; + InternalPacket **internalPacketArray; + + maximumSendBlockBytes = GetMaxDatagramSizeExcludingMessageHeaderBytes() - BITS_TO_BYTES(GetMaxMessageHeaderLengthBits()); + + // Calculate how many packets we need to create + internalPacket->splitPacketCount = ( ( dataByteLength - 1 ) / ( maximumSendBlockBytes ) + 1 ); + + // Optimization + // internalPacketArray = RakNet::OP_NEW(internalPacket->splitPacketCount, _FILE_AND_LINE_ ); + bool usedAlloca=false; + + if (sizeof( InternalPacket* ) * internalPacket->splitPacketCount < MAX_ALLOCA_STACK_ALLOCATION) + { + internalPacketArray = ( InternalPacket** ) alloca( sizeof( InternalPacket* ) * internalPacket->splitPacketCount ); + usedAlloca=true; + } + else + + internalPacketArray = (InternalPacket**) rakMalloc_Ex( sizeof(InternalPacket*) * internalPacket->splitPacketCount, _FILE_AND_LINE_ ); + + for ( i = 0; i < ( int ) internalPacket->splitPacketCount; i++ ) + { + internalPacketArray[ i ] = AllocateFromInternalPacketPool(); + + //internalPacketArray[ i ] = (InternalPacket*) alloca( sizeof( InternalPacket ) ); + // internalPacketArray[ i ] = sendPacketSet[internalPacket->priority].WriteLock(); + *internalPacketArray[ i ]=*internalPacket; + internalPacketArray[ i ]->messageNumberAssigned=false; + + if (i!=0) + internalPacket->messageInternalOrder = internalOrderIndex++; + } + + // This identifies which packet this is in the set + splitPacketIndex = 0; + + InternalPacketRefCountedData *refCounter=0; + + // Do a loop to send out all the packets + do + { + byteOffset = splitPacketIndex * maximumSendBlockBytes; + bytesToSend = dataByteLength - byteOffset; + + if ( bytesToSend > maximumSendBlockBytes ) + bytesToSend = maximumSendBlockBytes; + + // Copy over our chunk of data + + AllocInternalPacketData(internalPacketArray[ splitPacketIndex ], &refCounter, internalPacket->data, internalPacket->data + byteOffset); + // internalPacketArray[ splitPacketIndex ]->data = (unsigned char*) rakMalloc_Ex( bytesToSend, _FILE_AND_LINE_ ); + // memcpy( internalPacketArray[ splitPacketIndex ]->data, internalPacket->data + byteOffset, bytesToSend ); + + if ( bytesToSend != maximumSendBlockBytes ) + internalPacketArray[ splitPacketIndex ]->dataBitLength = internalPacket->dataBitLength - splitPacketIndex * ( maximumSendBlockBytes << 3 ); + else + internalPacketArray[ splitPacketIndex ]->dataBitLength = bytesToSend << 3; + + internalPacketArray[ splitPacketIndex ]->splitPacketIndex = splitPacketIndex; + internalPacketArray[ splitPacketIndex ]->splitPacketId = splitPacketId; + internalPacketArray[ splitPacketIndex ]->splitPacketCount = internalPacket->splitPacketCount; + RakAssert(internalPacketArray[ splitPacketIndex ]->dataBitLengthsplitPacketCount ); + + splitPacketId++; // It's ok if this wraps to 0 + + // InternalPacket *workingPacket; + + // Tell the heap we are going to push a list of elements where each element in the list follows the heap order + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthsplitPacketCount; i++ ) + { + internalPacketArray[ i ]->headerLength=headerLength; + RakAssert(internalPacketArray[ i ]->dataBitLengthpriority ].Push( internalPacketArray[ i ], _FILE_AND_LINE_ ); + RakAssert(internalPacketArray[ i ]->dataBitLengthmessageNumberAssigned==false); + outgoingPacketBuffer.PushSeries(GetNextWeight(internalPacketArray[ i ]->priority), internalPacketArray[ i ], _FILE_AND_LINE_); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]++; + statistics.bytesInSendBuffer[(int)(int)internalPacketArray[ i ]->priority]+=(double) BITS_TO_BYTES(internalPacketArray[ i ]->dataBitLength); + // workingPacket=sendPacketSet[internalPacket->priority].WriteLock(); + // memcpy(workingPacket, internalPacketArray[ i ], sizeof(InternalPacket)); + // sendPacketSet[internalPacket->priority].WriteUnlock(); + } + + // Do not delete, original is referenced by all split packets to avoid numerous allocations. See AllocInternalPacketData above + // FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + if (usedAlloca==false) + rakFree_Ex(internalPacketArray, _FILE_AND_LINE_ ); +} + +//------------------------------------------------------------------------------------------------------- +// Insert a packet into the split packet list +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::InsertIntoSplitPacketList( InternalPacket * internalPacket, CCTimeType time ) +{ + bool objectExists; + unsigned index; + index=splitPacketChannelList.GetIndexFromKey(internalPacket->splitPacketId, &objectExists); + if (objectExists==false) + { + SplitPacketChannel *newChannel = RakNet::OP_NEW( __FILE__, __LINE__ ); +#if PREALLOCATE_LARGE_MESSAGES==1 + index=splitPacketChannelList.Insert(internalPacket->splitPacketId, newChannel, true, __FILE__,__LINE__); + newChannel->returnedPacket=CreateInternalPacketCopy( internalPacket, 0, 0, time ); + newChannel->gotFirstPacket=false; + newChannel->splitPacketsArrived=0; + AllocInternalPacketData(newChannel->returnedPacket, BITS_TO_BYTES( internalPacket->dataBitLength*internalPacket->splitPacketCount ), false, __FILE__, __LINE__ ); + RakAssert(newChannel->returnedPacket->data); +#else + newChannel->firstPacket=0; + index=splitPacketChannelList.Insert(internalPacket->splitPacketId, newChannel, true, __FILE__,__LINE__); + // Preallocate to the final size, to avoid runtime copies + newChannel->splitPacketList.Preallocate(internalPacket->splitPacketCount, __FILE__,__LINE__); + +#endif + } + +#if PREALLOCATE_LARGE_MESSAGES==1 + splitPacketChannelList[index]->lastUpdateTime=time; + splitPacketChannelList[index]->splitPacketsArrived++; + splitPacketChannelList[index]->returnedPacket->dataBitLength+=internalPacket->dataBitLength; + + bool dealloc; + if (internalPacket->splitPacketIndex==0) + { + splitPacketChannelList[index]->gotFirstPacket=true; + splitPacketChannelList[index]->stride=BITS_TO_BYTES(internalPacket->dataBitLength); + + for (unsigned int j=0; j < splitPacketChannelList[index]->splitPacketList.Size(); j++) + { + memcpy(splitPacketChannelList[index]->returnedPacket->data+internalPacket->splitPacketIndex*splitPacketChannelList[index]->stride, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); + FreeInternalPacketData(splitPacketChannelList[index]->splitPacketList[j], __FILE__, __LINE__ ); + ReleaseToInternalPacketPool(splitPacketChannelList[index]->splitPacketList[j]); + } + + memcpy(splitPacketChannelList[index]->returnedPacket->data, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); + splitPacketChannelList[index]->splitPacketList.Clear(true,__FILE__,__LINE__); + dealloc=true; + } + else + { + if (splitPacketChannelList[index]->gotFirstPacket==true) + { + memcpy(splitPacketChannelList[index]->returnedPacket->data+internalPacket->splitPacketIndex*splitPacketChannelList[index]->stride, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); + dealloc=true; + } + else + { + splitPacketChannelList[index]->splitPacketList.Push(internalPacket,__FILE__,__LINE__); + dealloc=false; + } + } + + if (splitPacketChannelList[index]->gotFirstPacket==true && + splitMessageProgressInterval && + // splitPacketChannelList[index]->firstPacket && + // splitPacketChannelList[index]->splitPacketList.Size()!=splitPacketChannelList[index]->firstPacket->splitPacketCount && + // (splitPacketChannelList[index]->splitPacketList.Size()%splitMessageProgressInterval)==0 + splitPacketChannelList[index]->gotFirstPacket && + splitPacketChannelList[index]->splitPacketsArrived!=splitPacketChannelList[index]->returnedPacket->splitPacketCount && + (splitPacketChannelList[index]->splitPacketsArrived%splitMessageProgressInterval)==0 + ) + { + // Return ID_DOWNLOAD_PROGRESS + // Write splitPacketIndex (SplitPacketIndexType) + // Write splitPacketCount (SplitPacketIndexType) + // Write byteLength (4) + // Write data, splitPacketChannelList[index]->splitPacketList[0]->data + InternalPacket *progressIndicator = AllocateFromInternalPacketPool(); + // unsigned int len = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + (unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); + unsigned int l = (unsigned int) splitPacketChannelList[index]->stride; + const unsigned int len = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + l; + AllocInternalPacketData(progressIndicator, len, false, __FILE__, __LINE__ ); + progressIndicator->dataBitLength=BYTES_TO_BITS(len); + progressIndicator->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; + unsigned int temp; + // temp=splitPacketChannelList[index]->splitPacketList.Size(); + temp=splitPacketChannelList[index]->splitPacketsArrived; + memcpy(progressIndicator->data+sizeof(MessageID), &temp, sizeof(unsigned int)); + temp=(unsigned int)internalPacket->splitPacketCount; + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*1, &temp, sizeof(unsigned int)); + // temp=(unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); + temp=(unsigned int) BITS_TO_BYTES(l); + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*2, &temp, sizeof(unsigned int)); + //memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->firstPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength)); + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->returnedPacket->data, (size_t) BITS_TO_BYTES(l)); + } + + if (dealloc) + { + FreeInternalPacketData(internalPacket, __FILE__, __LINE__ ); + ReleaseToInternalPacketPool(internalPacket); + } +#else + splitPacketChannelList[index]->splitPacketList.Insert(internalPacket, __FILE__, __LINE__ ); + splitPacketChannelList[index]->lastUpdateTime=time; + + if (internalPacket->splitPacketIndex==0) + splitPacketChannelList[index]->firstPacket=internalPacket; + + if (splitMessageProgressInterval && + splitPacketChannelList[index]->firstPacket && + splitPacketChannelList[index]->splitPacketList.Size()!=splitPacketChannelList[index]->firstPacket->splitPacketCount && + (splitPacketChannelList[index]->splitPacketList.Size()%splitMessageProgressInterval)==0) + { + // Return ID_DOWNLOAD_PROGRESS + // Write splitPacketIndex (SplitPacketIndexType) + // Write splitPacketCount (SplitPacketIndexType) + // Write byteLength (4) + // Write data, splitPacketChannelList[index]->splitPacketList[0]->data + InternalPacket *progressIndicator = AllocateFromInternalPacketPool(); + unsigned int length = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + (unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); + AllocInternalPacketData(progressIndicator, length, false, __FILE__, __LINE__ ); + progressIndicator->dataBitLength=BYTES_TO_BITS(length); + progressIndicator->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; + unsigned int temp; + temp=splitPacketChannelList[index]->splitPacketList.Size(); + memcpy(progressIndicator->data+sizeof(MessageID), &temp, sizeof(unsigned int)); + temp=(unsigned int)internalPacket->splitPacketCount; + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*1, &temp, sizeof(unsigned int)); + temp=(unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*2, &temp, sizeof(unsigned int)); + + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->firstPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength)); + outputQueue.Push(progressIndicator, __FILE__, __LINE__ ); + } + +#endif +} + +//------------------------------------------------------------------------------------------------------- +// Take all split chunks with the specified splitPacketId and try to +//reconstruct a packet. If we can, allocate and return it. Otherwise return 0 +// Optimized version +//------------------------------------------------------------------------------------------------------- +InternalPacket * ReliabilityLayer::BuildPacketFromSplitPacketList( SplitPacketChannel *splitPacketChannel, CCTimeType time ) +{ +#if PREALLOCATE_LARGE_MESSAGES==1 + InternalPacket *returnedPacket=splitPacketChannel->returnedPacket; + RakNet::OP_DELETE(splitPacketChannel, __FILE__, __LINE__); + (void) time; + return returnedPacket; +#else + unsigned int j; + InternalPacket * internalPacket, *splitPacket; + int splitPacketPartLength; + + // Reconstruct + internalPacket = CreateInternalPacketCopy( splitPacketChannel->splitPacketList[0], 0, 0, time ); + internalPacket->dataBitLength=0; + for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) + internalPacket->dataBitLength+=splitPacketChannel->splitPacketList[j]->dataBitLength; + splitPacketPartLength=BITS_TO_BYTES(splitPacketChannel->firstPacket->dataBitLength); + + internalPacket->data = (unsigned char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES( internalPacket->dataBitLength ), _FILE_AND_LINE_ ); + internalPacket->allocationScheme=InternalPacket::NORMAL; + + for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) + { + splitPacket=splitPacketChannel->splitPacketList[j]; + memcpy(internalPacket->data+splitPacket->splitPacketIndex*splitPacketPartLength, splitPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannel->splitPacketList[j]->dataBitLength)); + } + + for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) + { + FreeInternalPacketData(splitPacketChannel->splitPacketList[j], _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool(splitPacketChannel->splitPacketList[j]); + } + RakNet::OP_DELETE(splitPacketChannel, __FILE__, __LINE__); + + return internalPacket; +#endif +} +//------------------------------------------------------------------------------------------------------- +InternalPacket * ReliabilityLayer::BuildPacketFromSplitPacketList( SplitPacketIdType splitPacketId, CCTimeType time, + SOCKET s, SystemAddress &systemAddress, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, + BitStream &updateBitStream) +{ + unsigned int i; + bool objectExists; + SplitPacketChannel *splitPacketChannel; + InternalPacket * internalPacket; + + i=splitPacketChannelList.GetIndexFromKey(splitPacketId, &objectExists); + splitPacketChannel=splitPacketChannelList[i]; + +#if PREALLOCATE_LARGE_MESSAGES==1 + if (splitPacketChannel->splitPacketsArrived==splitPacketChannel->returnedPacket->splitPacketCount) +#else + if (splitPacketChannel->splitPacketList.Size()==splitPacketChannel->splitPacketList[0]->splitPacketCount) +#endif + { + // Ack immediately, because for large files this can take a long time + SendACKs(s, systemAddress, time, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); + internalPacket=BuildPacketFromSplitPacketList(splitPacketChannel,time); + splitPacketChannelList.RemoveAtIndex(i); + return internalPacket; + } + else + { + return 0; + } +} +/* +//------------------------------------------------------------------------------------------------------- +// Delete any unreliable split packets that have long since expired +void ReliabilityLayer::DeleteOldUnreliableSplitPackets( CCTimeType time ) +{ +unsigned i,j; +i=0; +while (i < splitPacketChannelList.Size()) +{ +#if CC_TIME_TYPE_BYTES==4 +if (time > splitPacketChannelList[i]->lastUpdateTime + timeoutTime && +#else +if (time > splitPacketChannelList[i]->lastUpdateTime + (CCTimeType)timeoutTime*(CCTimeType)1000 && +#endif +(splitPacketChannelList[i]->splitPacketList[0]->reliability==UNRELIABLE || splitPacketChannelList[i]->splitPacketList[0]->reliability==UNRELIABLE_SEQUENCED)) +{ +for (j=0; j < splitPacketChannelList[i]->splitPacketList.Size(); j++) +{ +RakNet::OP_DELETE_ARRAY(splitPacketChannelList[i]->splitPacketList[j]->data, _FILE_AND_LINE_); +ReleaseToInternalPacketPool(splitPacketChannelList[i]->splitPacketList[j]); +} +RakNet::OP_DELETE(splitPacketChannelList[i], _FILE_AND_LINE_); +splitPacketChannelList.RemoveAtIndex(i); +} +else +i++; +} +} +*/ + +//------------------------------------------------------------------------------------------------------- +// Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. +// Does not copy any split data parameters as that information is always generated does not have any reason to be copied +//------------------------------------------------------------------------------------------------------- +InternalPacket * ReliabilityLayer::CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, CCTimeType time ) +{ + InternalPacket * copy = AllocateFromInternalPacketPool(); +#ifdef _DEBUG + // Remove accessing undefined memory error + memset( copy, 255, sizeof( InternalPacket ) ); +#endif + // Copy over our chunk of data + + if ( dataByteLength > 0 ) + { + AllocInternalPacketData(copy, BITS_TO_BYTES(dataByteLength ), false, _FILE_AND_LINE_ ); + memcpy( copy->data, original->data + dataByteOffset, dataByteLength ); + } + else + copy->data = 0; + + copy->dataBitLength = dataByteLength << 3; + copy->creationTime = time; + copy->nextActionTime = 0; + copy->orderingIndex = original->orderingIndex; + copy->sequencingIndex = original->sequencingIndex; + copy->orderingChannel = original->orderingChannel; + copy->reliableMessageNumber = original->reliableMessageNumber; + copy->priority = original->priority; + copy->reliability = original->reliability; +#if PREALLOCATE_LARGE_MESSAGES==1 + copy->splitPacketCount = original->splitPacketCount; + copy->splitPacketId = original->splitPacketId; + copy->splitPacketIndex = original->splitPacketIndex; +#endif + + return copy; +} + +//------------------------------------------------------------------------------------------------------- +// Get the specified ordering list +//------------------------------------------------------------------------------------------------------- +/* +DataStructures::LinkedList *ReliabilityLayer::GetOrderingListAtOrderingStream( unsigned char orderingChannel ) +{ + if ( orderingChannel >= orderingList.Size() ) + return 0; + + return orderingList[ orderingChannel ]; +} + +//------------------------------------------------------------------------------------------------------- +// Add the internal packet to the ordering list in order based on order index +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AddToOrderingList( InternalPacket * internalPacket ) +{ + } +*/ + +//------------------------------------------------------------------------------------------------------- +// Inserts a packet into the resend list in order +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::InsertPacketIntoResendList( InternalPacket *internalPacket, CCTimeType time, bool firstResend, bool modifyUnacknowledgedBytes ) +{ + (void) firstResend; + (void) time; + (void) internalPacket; + + AddToListTail(internalPacket, modifyUnacknowledgedBytes); + RakAssert(internalPacket->nextActionTime!=0); + +} + +//------------------------------------------------------------------------------------------------------- +// Were you ever unable to deliver a packet despite retries? +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsDeadConnection( void ) const +{ + return deadConnection; +} + +//------------------------------------------------------------------------------------------------------- +// Causes IsDeadConnection to return true +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::KillConnection( void ) +{ + deadConnection=true; +} + + +//------------------------------------------------------------------------------------------------------- +// Statistics +//------------------------------------------------------------------------------------------------------- +RakNetStatistics * ReliabilityLayer::GetStatistics( RakNetStatistics *rns ) +{ + unsigned i; + RakNet::TimeUS time = RakNet::GetTimeUS(); + uint64_t uint64Denominator; + double doubleDenominator; + + for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) + { + statistics.valueOverLastSecond[i]=bpsMetrics[i].GetBPS1Threadsafe(time); + statistics.runningTotal[i]=bpsMetrics[i].GetTotal1(); + } + + memcpy(rns, &statistics, sizeof(statistics)); + + if (rns->valueOverLastSecond[USER_MESSAGE_BYTES_SENT]+rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT]>0) + rns->packetlossLastSecond=(float)((double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT]/((double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_SENT]+(double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT])); + else + rns->packetlossLastSecond=0.0f; + + rns->packetlossTotal=0.0f; + uint64Denominator=(rns->runningTotal[USER_MESSAGE_BYTES_SENT]+rns->runningTotal[USER_MESSAGE_BYTES_RESENT]); + if (uint64Denominator!=0&&rns->runningTotal[USER_MESSAGE_BYTES_SENT]/uint64Denominator>0) + { + doubleDenominator=((double) rns->runningTotal[USER_MESSAGE_BYTES_SENT]+(double) rns->runningTotal[USER_MESSAGE_BYTES_RESENT]); + if(doubleDenominator!=0) + { + rns->packetlossTotal=(float)((double) rns->runningTotal[USER_MESSAGE_BYTES_RESENT]/doubleDenominator); + } + } + + rns->isLimitedByCongestionControl=statistics.isLimitedByCongestionControl; + rns->BPSLimitByCongestionControl=statistics.BPSLimitByCongestionControl; + rns->isLimitedByOutgoingBandwidthLimit=statistics.isLimitedByOutgoingBandwidthLimit; + rns->BPSLimitByOutgoingBandwidthLimit=statistics.BPSLimitByOutgoingBandwidthLimit; + + return rns; +} + +//------------------------------------------------------------------------------------------------------- +// Returns the number of packets in the resend queue, not counting holes +//------------------------------------------------------------------------------------------------------- +unsigned int ReliabilityLayer::GetResendListDataSize(void) const +{ + // Not accurate but thread-safe. The commented version might crash if the queue is cleared while we loop through it + // return resendTree.Size(); + return statistics.messagesInResendBuffer; +} + +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::AckTimeout(RakNet::Time curTime) +{ + // I check timeLastDatagramArrived-curTime because with threading it is possible that timeLastDatagramArrived is + // slightly greater than curTime, in which case this is NOT an ack timeout + return (timeLastDatagramArrived-curTime)>10000 && curTime-timeLastDatagramArrived>timeoutTime; +} +//------------------------------------------------------------------------------------------------------- +CCTimeType ReliabilityLayer::GetNextSendTime(void) const +{ + return nextSendTime; +} +//------------------------------------------------------------------------------------------------------- +CCTimeType ReliabilityLayer::GetTimeBetweenPackets(void) const +{ + return timeBetweenPackets; +} +//------------------------------------------------------------------------------------------------------- +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 +CCTimeType ReliabilityLayer::GetAckPing(void) const +{ + return ackPing; +} +#endif +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ResetPacketsAndDatagrams(void) +{ + packetsToSendThisUpdate.Clear(true, _FILE_AND_LINE_); + packetsToDeallocThisUpdate.Clear(true, _FILE_AND_LINE_); + packetsToSendThisUpdateDatagramBoundaries.Clear(true, _FILE_AND_LINE_); + datagramsToSendThisUpdateIsPair.Clear(true, _FILE_AND_LINE_); + datagramSizesInBytes.Clear(true, _FILE_AND_LINE_); + datagramSizeSoFar=0; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::PushPacket(CCTimeType time, InternalPacket *internalPacket, bool isReliable) +{ + BitSize_t bitsForThisPacket=BYTES_TO_BITS(BITS_TO_BYTES(internalPacket->dataBitLength)+BITS_TO_BYTES(internalPacket->headerLength)); + datagramSizeSoFar+=bitsForThisPacket; + RakAssert(BITS_TO_BYTES(datagramSizeSoFar)headerLength==GetMessageHeaderLengthBits(internalPacket)); + +// This code tells me how much time elapses between when you send, and when the message actually goes out +// if (internalPacket->data[0]==0) +// { +// RakNet::TimeMS t; +// RakNet::BitStream bs(internalPacket->data+1,sizeof(t),false); +// bs.Read(t); +// RakNet::TimeMS curTime=RakNet::GetTimeMS(); +// RakNet::TimeMS diff = curTime-t; +// } + + congestionManager.OnSendBytes(time, BITS_TO_BYTES(internalPacket->dataBitLength)+BITS_TO_BYTES(internalPacket->headerLength)); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::PushDatagram(void) +{ + if (datagramSizeSoFar>0) + { + packetsToSendThisUpdateDatagramBoundaries.Push(packetsToSendThisUpdate.Size(), _FILE_AND_LINE_ ); + datagramsToSendThisUpdateIsPair.Push(false, _FILE_AND_LINE_ ); + RakAssert(BITS_TO_BYTES(datagramSizeSoFar)=2) + { + datagramsToSendThisUpdateIsPair[datagramsToSendThisUpdateIsPair.Size()-2]=true; + datagramsToSendThisUpdateIsPair[datagramsToSendThisUpdateIsPair.Size()-1]=true; + return true; + } + return false; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ClearPacketsAndDatagrams(void) +{ + unsigned int i; + for (i=0; i < packetsToDeallocThisUpdate.Size(); i++) + { + // packetsToDeallocThisUpdate holds a boolean indicating if packetsToSendThisUpdate at this index should be freed + if (packetsToDeallocThisUpdate[i]) + { + RemoveFromUnreliableLinkedList(packetsToSendThisUpdate[i]); + FreeInternalPacketData(packetsToSendThisUpdate[i], _FILE_AND_LINE_ ); + // if (keepInternalPacketIfNeedsAck==false || packetsToSendThisUpdate[i]->reliabilityresendNext=internalPacket; + internalPacket->resendPrev=internalPacket; + resendLinkedListHead=internalPacket; + return; + } + internalPacket->resendPrev->resendNext = internalPacket->resendNext; + internalPacket->resendNext->resendPrev = internalPacket->resendPrev; + internalPacket->resendNext=resendLinkedListHead; + internalPacket->resendPrev=resendLinkedListHead->resendPrev; + internalPacket->resendPrev->resendNext=internalPacket; + resendLinkedListHead->resendPrev=internalPacket; + resendLinkedListHead=internalPacket; + RakAssert(internalPacket->headerLength+internalPacket->dataBitLength>0); + + //ValidateResendList(); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::RemoveFromList(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes) +{ + InternalPacket *newPosition; + internalPacket->resendPrev->resendNext = internalPacket->resendNext; + internalPacket->resendNext->resendPrev = internalPacket->resendPrev; + newPosition = internalPacket->resendNext; + if ( internalPacket == resendLinkedListHead ) + resendLinkedListHead = newPosition; + if (resendLinkedListHead==internalPacket) + resendLinkedListHead=0; + + if (modifyUnacknowledgedBytes) + { + RakAssert(unacknowledgedBytes>=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength)); + unacknowledgedBytes-=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); + // printf("-unacknowledgedBytes:%i ", unacknowledgedBytes); + + +// ValidateResendList(); + } +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AddToListTail(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes) +{ + if (modifyUnacknowledgedBytes) + { + unacknowledgedBytes+=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); + // printf("+unacknowledgedBytes:%i ", unacknowledgedBytes); + } + + if (resendLinkedListHead==0) + { + internalPacket->resendNext=internalPacket; + internalPacket->resendPrev=internalPacket; + resendLinkedListHead=internalPacket; + return; + } + internalPacket->resendNext=resendLinkedListHead; + internalPacket->resendPrev=resendLinkedListHead->resendPrev; + internalPacket->resendPrev->resendNext=internalPacket; + resendLinkedListHead->resendPrev=internalPacket; + +// ValidateResendList(); + +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::PopListHead(bool modifyUnacknowledgedBytes) +{ + RakAssert(resendLinkedListHead!=0); + RemoveFromList(resendLinkedListHead, modifyUnacknowledgedBytes); +} +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsResendQueueEmpty(void) const +{ + return resendLinkedListHead==0; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SendACKs(SOCKET s, SystemAddress &systemAddress, CCTimeType time, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream) +{ + BitSize_t maxDatagramPayload = GetMaxDatagramSizeExcludingMessageHeaderBits(); + + while (acknowlegements.Size()>0) + { + // Send acks + updateBitStream.Reset(); + DatagramHeaderFormat dhf; + dhf.isACK=true; + dhf.isNAK=false; + dhf.isPacketPair=false; +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + dhf.sourceSystemTime=time; +#endif + double B; + double AS; + bool hasBAndAS; + if (remoteSystemNeedsBAndAS) + { + congestionManager.OnSendAckGetBAndAS(time, &hasBAndAS,&B,&AS); + dhf.AS=(float)AS; + dhf.hasBAndAS=hasBAndAS; + } + else + dhf.hasBAndAS=false; +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + dhf.sourceSystemTime=nextAckTimeToSend; +#endif + // dhf.B=(float)B; + updateBitStream.Reset(); + dhf.Serialize(&updateBitStream); + CC_DEBUG_PRINTF_1("AckSnd "); + acknowlegements.Serialize(&updateBitStream, maxDatagramPayload, true); + SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); + congestionManager.OnSendAck(time,updateBitStream.GetNumberOfBytesUsed()); + + // I think this is causing a bug where if the estimated bandwidth is very low for the recipient, only acks ever get sent + // congestionManager.OnSendBytes(time,UDP_HEADER_SIZE+updateBitStream.GetNumberOfBytesUsed()); + } +} +/* +//------------------------------------------------------------------------------------------------------- +ReliabilityLayer::DatagramMessageIDList* ReliabilityLayer::AllocateFromDatagramMessageIDPool(void) +{ +DatagramMessageIDList*s; +s=datagramMessageIDPool.Allocate( _FILE_AND_LINE_ ); +// Call new operator, memoryPool doesn't do this +s = new ((void*)s) DatagramMessageIDList; +return s; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ReleaseToDatagramMessageIDPool(DatagramMessageIDList* d) +{ +d->~DatagramMessageIDList(); +datagramMessageIDPool.Release(d); +} +*/ +//------------------------------------------------------------------------------------------------------- +InternalPacket* ReliabilityLayer::AllocateFromInternalPacketPool(void) +{ + InternalPacket *ip = internalPacketPool.Allocate( _FILE_AND_LINE_ ); + ip->reliableMessageNumber = (MessageNumberType) (const uint32_t)-1; + ip->messageNumberAssigned=false; + ip->nextActionTime = 0; + ip->splitPacketCount = 0; + ip->allocationScheme=InternalPacket::NORMAL; + ip->data=0; + return ip; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ReleaseToInternalPacketPool(InternalPacket *ip) +{ + internalPacketPool.Release(ip, _FILE_AND_LINE_); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::RemoveFromUnreliableLinkedList(InternalPacket *internalPacket) +{ + if (internalPacket->reliability==UNRELIABLE || + internalPacket->reliability==UNRELIABLE_SEQUENCED || + internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT +// || +// internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + { + InternalPacket *newPosition; + internalPacket->unreliablePrev->unreliableNext = internalPacket->unreliableNext; + internalPacket->unreliableNext->unreliablePrev = internalPacket->unreliablePrev; + newPosition = internalPacket->unreliableNext; + if ( internalPacket == unreliableLinkedListHead ) + unreliableLinkedListHead = newPosition; + if (unreliableLinkedListHead==internalPacket) + unreliableLinkedListHead=0; + } +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AddToUnreliableLinkedList(InternalPacket *internalPacket) +{ + if (internalPacket->reliability==UNRELIABLE || + internalPacket->reliability==UNRELIABLE_SEQUENCED || + internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT +// || +// internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + { + if (unreliableLinkedListHead==0) + { + internalPacket->unreliableNext=internalPacket; + internalPacket->unreliablePrev=internalPacket; + unreliableLinkedListHead=internalPacket; + return; + } + internalPacket->unreliableNext=unreliableLinkedListHead; + internalPacket->unreliablePrev=unreliableLinkedListHead->unreliablePrev; + internalPacket->unreliablePrev->unreliableNext=internalPacket; + unreliableLinkedListHead->unreliablePrev=internalPacket; + } +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ValidateResendList(void) const +{ +// unsigned int count1=0, count2=0; +// for (unsigned int i=0; i < RESEND_BUFFER_ARRAY_LENGTH; i++) +// if (resendBuffer[i]) +// count1++; +// +// if (resendLinkedListHead) +// { +// InternalPacket *internalPacket = resendLinkedListHead; +// do +// { +// count2++; +// internalPacket=internalPacket->resendNext; +// } while (internalPacket!=resendLinkedListHead); +// } +// RakAssert(count1==count2); +// RakAssert(count2<=RESEND_BUFFER_ARRAY_LENGTH); +} +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::ResendBufferOverflow(void) const +{ + int index1 = sendReliableMessageNumberIndex & (uint32_t) RESEND_BUFFER_ARRAY_MASK; + // int index2 = (sendReliableMessageNumberIndex+(uint32_t)1) & (uint32_t) RESEND_BUFFER_ARRAY_MASK; + RakAssert(index1= datagramHistory.Size()) + return 0; + + *timeSent=datagramHistory[offsetIntoList].timeSent; + return datagramHistory[offsetIntoList].head; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::RemoveFromDatagramHistory(DatagramSequenceNumberType index) +{ + DatagramSequenceNumberType offsetIntoList = index - datagramHistoryPopCount; + MessageNumberNode *mnm = datagramHistory[offsetIntoList].head; + MessageNumberNode *next; + while (mnm) + { + next=mnm->next; + datagramHistoryMessagePool.Release(mnm, _FILE_AND_LINE_); + mnm=next; + } + datagramHistory[offsetIntoList].head=0; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, CCTimeType timeSent) +{ + (void) datagramNumber; + if (datagramHistory.Size()>DATAGRAM_MESSAGE_ID_ARRAY_LENGTH) + { + RemoveFromDatagramHistory(datagramHistoryPopCount); + datagramHistory.Pop(); + datagramHistoryPopCount++; + } + + datagramHistory.Push(DatagramHistoryNode(0, timeSent), _FILE_AND_LINE_); + // printf("%p Pushed empty DatagramHistoryNode to datagram history at index %i\n", this, datagramHistory.Size()-1); +} +//------------------------------------------------------------------------------------------------------- +ReliabilityLayer::MessageNumberNode* ReliabilityLayer::AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, DatagramSequenceNumberType messageNumber, CCTimeType timeSent) +{ + (void) datagramNumber; +// RakAssert(datagramHistoryPopCount+(unsigned int) datagramHistory.Size()==datagramNumber); + if (datagramHistory.Size()>DATAGRAM_MESSAGE_ID_ARRAY_LENGTH) + { + RemoveFromDatagramHistory(datagramHistoryPopCount); + datagramHistory.Pop(); + datagramHistoryPopCount++; + } + + MessageNumberNode *mnm = datagramHistoryMessagePool.Allocate(_FILE_AND_LINE_); + mnm->next=0; + mnm->messageNumber=messageNumber; + datagramHistory.Push(DatagramHistoryNode(mnm, timeSent), _FILE_AND_LINE_); + // printf("%p Pushed message %i to DatagramHistoryNode to datagram history at index %i\n", this, messageNumber.val, datagramHistory.Size()-1); + return mnm; +} +//------------------------------------------------------------------------------------------------------- +ReliabilityLayer::MessageNumberNode* ReliabilityLayer::AddSubsequentToDatagramHistory(MessageNumberNode *messageNumberNode, DatagramSequenceNumberType messageNumber) +{ + messageNumberNode->next=datagramHistoryMessagePool.Allocate(_FILE_AND_LINE_); + messageNumberNode->next->messageNumber=messageNumber; + messageNumberNode->next->next=0; + return messageNumberNode->next; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, InternalPacketRefCountedData **refCounter, unsigned char *externallyAllocatedPtr, unsigned char *ourOffset) +{ + internalPacket->allocationScheme=InternalPacket::REF_COUNTED; + internalPacket->data=ourOffset; + if (*refCounter==0) + { + *refCounter = refCountedDataPool.Allocate(_FILE_AND_LINE_); + // *refCounter = RakNet::OP_NEW(_FILE_AND_LINE_); + (*refCounter)->refCount=1; + (*refCounter)->sharedDataBlock=externallyAllocatedPtr; + } + else + (*refCounter)->refCount++; + internalPacket->refCountedData=(*refCounter); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, unsigned char *externallyAllocatedPtr) +{ + internalPacket->allocationScheme=InternalPacket::NORMAL; + internalPacket->data=externallyAllocatedPtr; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, unsigned int numBytes, bool allowStack, const char *file, unsigned int line) +{ + if (allowStack && numBytes <= sizeof(internalPacket->stackData)) + { + internalPacket->allocationScheme=InternalPacket::STACK; + internalPacket->data=internalPacket->stackData; + } + else + { + internalPacket->allocationScheme=InternalPacket::NORMAL; + internalPacket->data=(unsigned char*) rakMalloc_Ex(numBytes,file,line); + } +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::FreeInternalPacketData(InternalPacket *internalPacket, const char *file, unsigned int line) +{ + if (internalPacket==0) + return; + + if (internalPacket->allocationScheme==InternalPacket::REF_COUNTED) + { + if (internalPacket->refCountedData==0) + return; + + internalPacket->refCountedData->refCount--; + if (internalPacket->refCountedData->refCount==0) + { + rakFree_Ex(internalPacket->refCountedData->sharedDataBlock, file, line ); + internalPacket->refCountedData->sharedDataBlock=0; + // RakNet::OP_DELETE(internalPacket->refCountedData,file, line); + refCountedDataPool.Release(internalPacket->refCountedData,file, line); + internalPacket->refCountedData=0; + } + } + else if (internalPacket->allocationScheme==InternalPacket::NORMAL) + { + if (internalPacket->data==0) + return; + + rakFree_Ex(internalPacket->data, file, line ); + internalPacket->data=0; + } + else + { + // Data was on stack + internalPacket->data=0; + } +} +//------------------------------------------------------------------------------------------------------- +unsigned int ReliabilityLayer::GetMaxDatagramSizeExcludingMessageHeaderBytes(void) +{ + unsigned int val = congestionManager.GetMTU() - DatagramHeaderFormat::GetDataHeaderByteLength(); + +#if LIBCAT_SECURITY==1 + if (useSecurity) + val -= cat::AuthenticatedEncryption::OVERHEAD_BYTES; +#endif + + return val; +} +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::GetMaxDatagramSizeExcludingMessageHeaderBits(void) +{ + return BYTES_TO_BITS(GetMaxDatagramSizeExcludingMessageHeaderBytes()); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::InitHeapWeights(void) +{ + for (int priorityLevel=0; priorityLevel < NUMBER_OF_PRIORITIES; priorityLevel++) + outgoingPacketBufferNextWeights[priorityLevel]=(1<0) + { + int peekPL = outgoingPacketBuffer.Peek()->priority; + reliabilityHeapWeightType weight = outgoingPacketBuffer.PeekWeight(); + reliabilityHeapWeightType min = weight - (1< -{ - CCTimeType lastUpdateTime; - - DataStructures::List splitPacketList; - -#if PREALLOCATE_LARGE_MESSAGES==1 - InternalPacket *returnedPacket; - bool gotFirstPacket; - unsigned int stride; - unsigned int splitPacketsArrived; -#else - // This is here for progress notifications, since progress notifications return the first packet data, if available - InternalPacket *firstPacket; -#endif - -}; -int RAK_DLL_EXPORT SplitPacketChannelComp( SplitPacketIdType const &key, SplitPacketChannel* const &data ); - -// Helper class -struct BPSTracker -{ - BPSTracker(); - ~BPSTracker(); - void Reset(const char *file, unsigned int line); - inline void Push1(CCTimeType time, uint64_t value1) {dataQueue.Push(TimeAndValue2(time,value1),_FILE_AND_LINE_); total1+=value1; lastSec1+=value1;} -// void Push2(RakNet::TimeUS time, uint64_t value1, uint64_t value2); - inline uint64_t GetBPS1(CCTimeType time) {(void) time; return lastSec1;} - inline uint64_t GetBPS1Threadsafe(CCTimeType time) {(void) time; return lastSec1;} -// uint64_t GetBPS2(RakNetTimeUS time); -// void GetBPS1And2(RakNetTimeUS time, uint64_t &out1, uint64_t &out2); - uint64_t GetTotal1(void) const; -// uint64_t GetTotal2(void) const; - - struct TimeAndValue2 - { - TimeAndValue2(); - ~TimeAndValue2(); - TimeAndValue2(CCTimeType t, uint64_t v1); - // TimeAndValue2(RakNet::TimeUS t, uint64_t v1, uint64_t v2); - // uint64_t value1, value2; - uint64_t value1; - CCTimeType time; - }; - - uint64_t total1, lastSec1; -// uint64_t total2, lastSec2; - DataStructures::Queue dataQueue; - void ClearExpired1(CCTimeType time); -// void ClearExpired2(RakNet::TimeUS time); -}; - -/// Datagram reliable, ordered, unordered and sequenced sends. Flow control. Message splitting, reassembly, and coalescence. -class ReliabilityLayer// -{ -public: - - // Constructor - ReliabilityLayer(); - - // Destructor - ~ReliabilityLayer(); - - /// Resets the layer for reuse - void Reset( bool resetVariables, int MTUSize, bool _useSecurity ); - - /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet - /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. - /// \param[in] time Time, in MS - void SetTimeoutTime( RakNet::TimeMS time ); - - /// Returns the value passed to SetTimeoutTime. or the default if it was never called - /// \param[out] the value passed to SetTimeoutTime - RakNet::TimeMS GetTimeoutTime(void); - - /// Packets are read directly from the socket layer and skip the reliability layer because unconnected players do not use the reliability layer - /// This function takes packet data after a player has been confirmed as connected. - /// \param[in] buffer The socket data - /// \param[in] length The length of the socket data - /// \param[in] systemAddress The player that this data is from - /// \param[in] messageHandlerList A list of registered plugins - /// \param[in] MTUSize maximum datagram size - /// \retval true Success - /// \retval false Modified packet - bool HandleSocketReceiveFromConnectedPlayer( - const char *buffer, unsigned int length, SystemAddress &systemAddress, DataStructures::List &messageHandlerList, int MTUSize, - SOCKET s, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType timeRead, BitStream &updateBitStream); - - /// This allocates bytes and writes a user-level message to those bytes. - /// \param[out] data The message - /// \return Returns number of BITS put into the buffer - BitSize_t Receive( unsigned char**data ); - - /// Puts data on the send queue - /// \param[in] data The data to send - /// \param[in] numberOfBitsToSend The length of \a data in bits - /// \param[in] priority The priority level for the send - /// \param[in] reliability The reliability type for the send - /// \param[in] orderingChannel 0 to 31. Specifies what channel to use, for relational ordering and sequencing of packets. - /// \param[in] makeDataCopy If true \a data will be copied. Otherwise, only a pointer will be stored. - /// \param[in] MTUSize maximum datagram size - /// \param[in] currentTime Current time, as per RakNet::GetTimeMS() - /// \param[in] receipt This number will be returned back with ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS and is only returned with the reliability types that contain RECEIPT in the name - /// \return True or false for success or failure. - bool Send( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, CCTimeType currentTime, uint32_t receipt ); - - /// Call once per game cycle. Handles internal lists and actually does the send. - /// \param[in] s the communication end point - /// \param[in] systemAddress The Unique Player Identifier who shouldhave sent some packets - /// \param[in] MTUSize maximum datagram size - /// \param[in] time current system time - /// \param[in] maxBitsPerSecond if non-zero, enforces that outgoing bandwidth does not exceed this amount - /// \param[in] messageHandlerList A list of registered plugins - void Update( SOCKET s, SystemAddress &systemAddress, int MTUSize, CCTimeType time, - unsigned bitsPerSecondLimit, - DataStructures::List &messageHandlerList, - RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); - - /// Were you ever unable to deliver a packet despite retries? - /// \return true means the connection has been lost. Otherwise not. - bool IsDeadConnection( void ) const; - - /// Causes IsDeadConnection to return true - void KillConnection(void); - - /// Get Statistics - /// \return A pointer to a static struct, filled out with current statistical information. - RakNetStatistics * GetStatistics( RakNetStatistics *rns ); - - ///Are we waiting for any data to be sent out or be processed by the player? - bool IsOutgoingDataWaiting(void); - bool AreAcksWaiting(void); - - // Set outgoing lag and packet loss properties - void ApplyNetworkSimulator( double _maxSendBPS, RakNet::TimeMS _minExtraPing, RakNet::TimeMS _extraPingVariance ); - - /// Returns if you previously called ApplyNetworkSimulator - /// \return If you previously called ApplyNetworkSimulator - bool IsNetworkSimulatorActive( void ); - - void SetSplitMessageProgressInterval(int interval); - void SetUnreliableTimeout(RakNet::TimeMS timeoutMS); - /// Has a lot of time passed since the last ack - bool AckTimeout(RakNet::Time curTime); - CCTimeType GetNextSendTime(void) const; - CCTimeType GetTimeBetweenPackets(void) const; - - - - RakNet::TimeMS GetTimeLastDatagramArrived(void) const {return timeLastDatagramArrived;} - - // If true, will update time between packets quickly based on ping calculations - //void SetDoFastThroughputReactions(bool fast); - - // Encoded as numMessages[unsigned int], message1BitLength[unsigned int], message1Data (aligned), ... - //void GetUndeliveredMessages(RakNet::BitStream *messages, int MTUSize); - -private: - /// Send the contents of a bitstream to the socket - /// \param[in] s The socket used for sending data - /// \param[in] systemAddress The address and port to send to - /// \param[in] bitStream The data to send. - void SendBitStream( SOCKET s, SystemAddress &systemAddress, RakNet::BitStream *bitStream, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType currentTime); - - ///Parse an internalPacket and create a bitstream to represent this data - /// \return Returns number of bits used - BitSize_t WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket, CCTimeType curTime ); - - - /// Parse a bitstream and create an internal packet to represent this data - InternalPacket* CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, CCTimeType time ); - - /// Does what the function name says - unsigned RemovePacketFromResendListAndDeleteOlderReliableSequenced( const MessageNumberType messageNumber, CCTimeType time, DataStructures::List &messageHandlerList, const SystemAddress &systemAddress ); - - /// Acknowledge receipt of the packet with the specified messageNumber - void SendAcknowledgementPacket( const DatagramSequenceNumberType messageNumber, CCTimeType time); - - /// This will return true if we should not send at this time - bool IsSendThrottled( int MTUSize ); - - /// We lost a packet - void UpdateWindowFromPacketloss( CCTimeType time ); - - /// Increase the window size - void UpdateWindowFromAck( CCTimeType time ); - - /// Parse an internalPacket and figure out how many header bits would be written. Returns that number - BitSize_t GetMaxMessageHeaderLengthBits( void ); - BitSize_t GetMessageHeaderLengthBits( const InternalPacket *const internalPacket ); - - /// Get the SHA1 code - void GetSHA1( unsigned char * const buffer, unsigned int nbytes, char code[ SHA1_LENGTH ] ); - - /// Check the SHA1 code - bool CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * const buffer, unsigned int nbytes ); - - /// Search the specified list for sequenced packets on the specified ordering channel, optionally skipping those with splitPacketId, and delete them - void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::List&theList, int splitPacketId = -1 ); - - /// Search the specified list for sequenced packets with a value less than orderingIndex and delete them - void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::Queue&theList ); - - /// Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex - bool IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ); - - /// Split the passed packet into chunks under MTU_SIZE bytes (including headers) and save those new chunks - void SplitPacket( InternalPacket *internalPacket ); - - /// Insert a packet into the split packet list - void InsertIntoSplitPacketList( InternalPacket * internalPacket, CCTimeType time ); - - /// Take all split chunks with the specified splitPacketId and try to reconstruct a packet. If we can, allocate and return it. Otherwise return 0 - InternalPacket * BuildPacketFromSplitPacketList( SplitPacketIdType splitPacketId, CCTimeType time, - SOCKET s, SystemAddress &systemAddress, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); - InternalPacket * BuildPacketFromSplitPacketList( SplitPacketChannel *splitPacketChannel, CCTimeType time ); - - /// Delete any unreliable split packets that have long since expired - //void DeleteOldUnreliableSplitPackets( CCTimeType time ); - - /// Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. - /// Does not copy any split data parameters as that information is always generated does not have any reason to be copied - InternalPacket * CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, CCTimeType time ); - - /// Get the specified ordering list - // DataStructures::LinkedList *GetOrderingListAtOrderingStream( unsigned char orderingChannel ); - - /// Add the internal packet to the ordering list in order based on order index - // void AddToOrderingList( InternalPacket * internalPacket ); - - /// Inserts a packet into the resend list in order - void InsertPacketIntoResendList( InternalPacket *internalPacket, CCTimeType time, bool firstResend, bool modifyUnacknowledgedBytes ); - - /// Memory handling - void FreeMemory( bool freeAllImmediately ); - - /// Memory handling - void FreeThreadSafeMemory( void ); - - // Initialize the variables - void InitializeVariables( void ); - - /// Given the current time, is this time so old that we should consider it a timeout? - bool IsExpiredTime(unsigned int input, CCTimeType currentTime) const; - - // Make it so we don't do resends within a minimum threshold of time - void UpdateNextActionTime(void); - - - /// Does this packet number represent a packet that was skipped (out of order?) - //unsigned int IsReceivedPacketHole(unsigned int input, RakNet::TimeMS currentTime) const; - - /// Skip an element in the received packets list - //unsigned int MakeReceivedPacketHole(unsigned int input) const; - - /// How many elements are waiting to be resent? - unsigned int GetResendListDataSize(void) const; - - /// Update all memory which is not threadsafe - void UpdateThreadedMemory(void); - - void CalculateHistogramAckSize(void); - - // Used ONLY for RELIABLE_ORDERED - // RELIABLE_SEQUENCED just returns the newest one - // DataStructures::List*> orderingList; - DataStructures::Queue outputQueue; - int splitMessageProgressInterval; - CCTimeType unreliableTimeout; - - struct MessageNumberNode - { - DatagramSequenceNumberType messageNumber; - MessageNumberNode *next; - }; - struct DatagramHistoryNode - { - DatagramHistoryNode() {} - DatagramHistoryNode(MessageNumberNode *_head, CCTimeType ts - ) : - head(_head), timeSent(ts) - {} - MessageNumberNode *head; - CCTimeType timeSent; - }; - // Queue length is programmatically restricted to DATAGRAM_MESSAGE_ID_ARRAY_LENGTH - // This is essentially an O(1) lookup to get a DatagramHistoryNode given an index - // datagramHistory holds a linked list of MessageNumberNode. Each MessageNumberNode refers to one element in resendList which can be cleared on an ack. - DataStructures::Queue datagramHistory; - DataStructures::MemoryPool datagramHistoryMessagePool; - - struct UnreliableWithAckReceiptNode - { - UnreliableWithAckReceiptNode() {} - UnreliableWithAckReceiptNode(DatagramSequenceNumberType _datagramNumber, uint32_t _sendReceiptSerial, RakNet::TimeUS _nextActionTime) : - datagramNumber(_datagramNumber), sendReceiptSerial(_sendReceiptSerial), nextActionTime(_nextActionTime) - {} - DatagramSequenceNumberType datagramNumber; - uint32_t sendReceiptSerial; - RakNet::TimeUS nextActionTime; - }; - DataStructures::List unreliableWithAckReceiptHistory; - - void RemoveFromDatagramHistory(DatagramSequenceNumberType index); - MessageNumberNode* GetMessageNumberNodeByDatagramIndex(DatagramSequenceNumberType index, CCTimeType *timeSent); - void AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, CCTimeType timeSent); - MessageNumberNode* AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, DatagramSequenceNumberType messageNumber, CCTimeType timeSent); - MessageNumberNode* AddSubsequentToDatagramHistory(MessageNumberNode *messageNumberNode, DatagramSequenceNumberType messageNumber); - DatagramSequenceNumberType datagramHistoryPopCount; - - DataStructures::MemoryPool internalPacketPool; - // DataStructures::BPlusTree resendTree; - InternalPacket *resendBuffer[RESEND_BUFFER_ARRAY_LENGTH]; - InternalPacket *resendLinkedListHead; - InternalPacket *unreliableLinkedListHead; - void RemoveFromUnreliableLinkedList(InternalPacket *internalPacket); - void AddToUnreliableLinkedList(InternalPacket *internalPacket); -// unsigned int numPacketsOnResendBuffer; - //unsigned int blockWindowIncreaseUntilTime; - // DataStructures::RangeList acknowlegements; - // Resend list is a tree of packets we need to resend - - // Set to the current time when the resend queue is no longer empty - // Set to zero when it becomes empty - // Set to the current time if it is not zero, and we get incoming data - // If the current time - timeResendQueueNonEmpty is greater than a threshold, we are disconnected -// CCTimeType timeResendQueueNonEmpty; - RakNet::TimeMS timeLastDatagramArrived; - - - // If we backoff due to packetloss, don't remeasure until all waiting resends have gone out or else we overcount -// bool packetlossThisSample; -// int backoffThisSample; -// unsigned packetlossThisSampleResendCount; -// CCTimeType lastPacketlossTime; - - //DataStructures::Queue sendPacketSet[ NUMBER_OF_PRIORITIES ]; - DataStructures::Heap outgoingPacketBuffer; - reliabilityHeapWeightType outgoingPacketBufferNextWeights[NUMBER_OF_PRIORITIES]; - void InitHeapWeights(void); - reliabilityHeapWeightType GetNextWeight(int priorityLevel); -// unsigned int messageInSendBuffer[NUMBER_OF_PRIORITIES]; -// double bytesInSendBuffer[NUMBER_OF_PRIORITIES]; - - - DataStructures::OrderedList splitPacketChannelList; - - MessageNumberType sendReliableMessageNumberIndex; - MessageNumberType internalOrderIndex; - //unsigned int windowSize; - //RakNet::BitStream updateBitStream; - bool deadConnection, cheater; - SplitPacketIdType splitPacketId; - RakNet::TimeMS timeoutTime; // How long to wait in MS before timing someone out - //int MAX_AVERAGE_PACKETS_PER_SECOND; // Name says it all -// int RECEIVED_PACKET_LOG_LENGTH, requestedReceivedPacketLogLength; // How big the receivedPackets array is -// unsigned int *receivedPackets; - RakNetStatistics statistics; - - // Algorithm for blending ordered and sequenced on the same channel: - // 1. Each ordered message transmits OrderingIndexType orderedWriteIndex. There are NUMBER_OF_ORDERED_STREAMS independent values of these. The value - // starts at 0. Every time an ordered message is sent, the value increments by 1 - // 2. Each sequenced message contains the current value of orderedWriteIndex for that channel, and additionally OrderingIndexType sequencedWriteIndex. - // sequencedWriteIndex resets to 0 every time orderedWriteIndex increments. It increments by 1 every time a sequenced message is sent. - // 3. The receiver maintains the next expected value for the orderedWriteIndex, stored in orderedReadIndex. - // 4. As messages arrive: - // If a message has the current ordering index, and is sequenced, and is < the current highest sequence value, discard - // If a message has the current ordering index, and is sequenced, and is >= the current highest sequence value, return immediately - // If a message has a greater ordering index, and is sequenced or ordered, buffer it - // If a message has the current ordering index, and is ordered, buffer, then push off messages from buffer - // 5. Pushing off messages from buffer: - // Messages in buffer are put in a minheap. The value of each node is calculated such that messages are returned: - // A. (lowest ordering index, lowest sequence index) - // B. (lowest ordering index, no sequence index) - // Messages are pushed off until the heap is empty, or the next message to be returned does not preserve the ordered index - // For an empty heap, the heap weight should start at the lowest value based on the next expected ordering index, to avoid variable overflow - - // Sender increments this by 1 for every ordered message sent - OrderingIndexType orderedWriteIndex[NUMBER_OF_ORDERED_STREAMS]; - // Sender increments by 1 for every sequenced message sent. Resets to 0 when an ordered message is sent - OrderingIndexType sequencedWriteIndex[NUMBER_OF_ORDERED_STREAMS]; - // Next expected index for ordered messages. - OrderingIndexType orderedReadIndex[NUMBER_OF_ORDERED_STREAMS]; - // Highest value received for sequencedWriteIndex for the current value of orderedReadIndex on the same channel. - OrderingIndexType highestSequencedReadIndex[NUMBER_OF_ORDERED_STREAMS]; - DataStructures::Heap orderingHeaps[NUMBER_OF_ORDERED_STREAMS]; - OrderingIndexType heapIndexOffsets[NUMBER_OF_ORDERED_STREAMS]; - - - - - - - -// CCTimeType histogramStart; -// unsigned histogramBitsSent; - - - /// Memory-efficient receivedPackets algorithm: - /// receivedPacketsBaseIndex is the packet number we are expecting - /// Everything under receivedPacketsBaseIndex is a packet we already got - /// Everything over receivedPacketsBaseIndex is stored in hasReceivedPacketQueue - /// It stores the time to stop waiting for a particular packet number, where the packet number is receivedPacketsBaseIndex + the index into the queue - /// If 0, we got got that packet. Otherwise, the time to give up waiting for that packet. - /// If we get a packet number where (receivedPacketsBaseIndex-packetNumber) is less than half the range of receivedPacketsBaseIndex then it is a duplicate - /// Otherwise, it is a duplicate packet (and ignore it). - // DataStructures::Queue hasReceivedPacketQueue; - DataStructures::Queue hasReceivedPacketQueue; - DatagramSequenceNumberType receivedPacketsBaseIndex; - bool resetReceivedPackets; - - CCTimeType lastUpdateTime; - CCTimeType timeBetweenPackets, nextSendTime; - - - -// CCTimeType ackPingSamples[ACK_PING_SAMPLES_SIZE]; // Must be range of unsigned char to wrap ackPingIndex properly - CCTimeType ackPingSum; - unsigned char ackPingIndex; - //CCTimeType nextLowestPingReset; - RemoteSystemTimeType remoteSystemTime; -// bool continuousSend; -// CCTimeType lastTimeBetweenPacketsIncrease,lastTimeBetweenPacketsDecrease; - // Limit changes in throughput to once per ping - otherwise even if lag starts we don't know about it - // In the meantime the connection is flooded and overrun. - CCTimeType nextAllowedThroughputSample; - bool bandwidthExceededStatistic; - - // If Update::maxBitsPerSecond > 0, then throughputCapCountdown is used as a timer to prevent sends for some amount of time after each send, depending on - // the amount of data sent - long long throughputCapCountdown; - - unsigned receivePacketCount; - -#ifdef _DEBUG - struct DataAndTime// - { - SOCKET s; - char data[ MAXIMUM_MTU_SIZE ]; - unsigned int length; - RakNet::TimeMS sendTime; - // SystemAddress systemAddress; - unsigned short remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions; - }; - DataStructures::Queue delayList; - - // Internet simulator - double packetloss; - RakNet::TimeMS minExtraPing, extraPingVariance; -#endif - - CCTimeType elapsedTimeSinceLastUpdate; - - CCTimeType nextAckTimeToSend; - - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1 - RakNet::CCRakNetSlidingWindow congestionManager; -#else - RakNet::CCRakNetUDT congestionManager; -#endif - - - uint32_t unacknowledgedBytes; - - bool ResendBufferOverflow(void) const; - void ValidateResendList(void) const; - void ResetPacketsAndDatagrams(void); - void PushPacket(CCTimeType time, InternalPacket *internalPacket, bool isReliable); - void PushDatagram(void); - bool TagMostRecentPushAsSecondOfPacketPair(void); - void ClearPacketsAndDatagrams(void); - void MoveToListHead(InternalPacket *internalPacket); - void RemoveFromList(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes); - void AddToListTail(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes); - void PopListHead(bool modifyUnacknowledgedBytes); - bool IsResendQueueEmpty(void) const; - void SortSplitPacketList(DataStructures::List &data, unsigned int leftEdge, unsigned int rightEdge) const; - void SendACKs(SOCKET s, SystemAddress &systemAddress, CCTimeType time, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); - - DataStructures::List packetsToSendThisUpdate; - DataStructures::List packetsToDeallocThisUpdate; - // boundary is in packetsToSendThisUpdate, inclusive - DataStructures::List packetsToSendThisUpdateDatagramBoundaries; - DataStructures::List datagramsToSendThisUpdateIsPair; - DataStructures::List datagramSizesInBytes; - BitSize_t datagramSizeSoFar; - BitSize_t allDatagramSizesSoFar; - double totalUserDataBytesAcked; - CCTimeType timeOfLastContinualSend; - CCTimeType timeToNextUnreliableCull; - - // This doesn't need to be a member, but I do it to avoid reallocations - DataStructures::RangeList incomingAcks; - - // Every 16 datagrams, we make sure the 17th datagram goes out the same update tick, and is the same size as the 16th - int countdownToNextPacketPair; - InternalPacket* AllocateFromInternalPacketPool(void); - void ReleaseToInternalPacketPool(InternalPacket *ip); - - DataStructures::RangeList acknowlegements; - DataStructures::RangeList NAKs; - bool remoteSystemNeedsBAndAS; - - unsigned int GetMaxDatagramSizeExcludingMessageHeaderBytes(void); - BitSize_t GetMaxDatagramSizeExcludingMessageHeaderBits(void); - - // ourOffset refers to a section within externallyAllocatedPtr. Do not deallocate externallyAllocatedPtr until all references are lost - void AllocInternalPacketData(InternalPacket *internalPacket, InternalPacketRefCountedData **refCounter, unsigned char *externallyAllocatedPtr, unsigned char *ourOffset); - // Set the data pointer to externallyAllocatedPtr, do not allocate - void AllocInternalPacketData(InternalPacket *internalPacket, unsigned char *externallyAllocatedPtr); - // Allocate new - void AllocInternalPacketData(InternalPacket *internalPacket, unsigned int numBytes, bool allowStack, const char *file, unsigned int line); - void FreeInternalPacketData(InternalPacket *internalPacket, const char *file, unsigned int line); - DataStructures::MemoryPool refCountedDataPool; - - BPSTracker bpsMetrics[RNS_PER_SECOND_METRICS_COUNT]; - CCTimeType lastBpsClear; - -#if LIBCAT_SECURITY==1 -public: - cat::AuthenticatedEncryption* GetAuthenticatedEncryption(void) { return &auth_enc; } - -protected: - cat::AuthenticatedEncryption auth_enc; - bool useSecurity; -#endif // LIBCAT_SECURITY -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief \b [Internal] Datagram reliable, ordered, unordered and sequenced sends. Flow control. Message splitting, reassembly, and coalescence. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RELIABILITY_LAYER_H +#define __RELIABILITY_LAYER_H + +#include "RakMemoryOverride.h" +#include "MTUSize.h" +#include "DS_LinkedList.h" +#include "DS_List.h" +#include "SocketLayer.h" +#include "PacketPriority.h" +#include "DS_Queue.h" +#include "BitStream.h" +#include "InternalPacket.h" +#include "RakNetStatistics.h" +#include "SHA1.h" +#include "DS_OrderedList.h" +#include "DS_RangeList.h" +#include "DS_BPlusTree.h" +#include "DS_MemoryPool.h" +#include "RakNetDefines.h" +#include "DS_Heap.h" +#include "BitStream.h" +#include "NativeFeatureIncludes.h" +#include "SecureHandshake.h" +#include "PluginInterface2.h" +#include "Rand.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 +#include "CCRakNetUDT.h" +#define INCLUDE_TIMESTAMP_WITH_DATAGRAMS 1 +#else +#include "CCRakNetSlidingWindow.h" +#define INCLUDE_TIMESTAMP_WITH_DATAGRAMS 0 +#endif + +/// Number of ordered streams available. You can use up to 32 ordered streams +#define NUMBER_OF_ORDERED_STREAMS 32 // 2^5 + +#define RESEND_TREE_ORDER 32 + +namespace RakNet { + + /// Forward declarations +class PluginInterface2; +class RakNetRandom; +typedef uint64_t reliabilityHeapWeightType; + +// int SplitPacketIndexComp( SplitPacketIndexType const &key, InternalPacket* const &data ); +struct SplitPacketChannel// +{ + CCTimeType lastUpdateTime; + + DataStructures::List splitPacketList; + +#if PREALLOCATE_LARGE_MESSAGES==1 + InternalPacket *returnedPacket; + bool gotFirstPacket; + unsigned int stride; + unsigned int splitPacketsArrived; +#else + // This is here for progress notifications, since progress notifications return the first packet data, if available + InternalPacket *firstPacket; +#endif + +}; +int RAK_DLL_EXPORT SplitPacketChannelComp( SplitPacketIdType const &key, SplitPacketChannel* const &data ); + +// Helper class +struct BPSTracker +{ + BPSTracker(); + ~BPSTracker(); + void Reset(const char *file, unsigned int line); + inline void Push1(CCTimeType time, uint64_t value1) {dataQueue.Push(TimeAndValue2(time,value1),_FILE_AND_LINE_); total1+=value1; lastSec1+=value1;} +// void Push2(RakNet::TimeUS time, uint64_t value1, uint64_t value2); + inline uint64_t GetBPS1(CCTimeType time) {(void) time; return lastSec1;} + inline uint64_t GetBPS1Threadsafe(CCTimeType time) {(void) time; return lastSec1;} +// uint64_t GetBPS2(RakNetTimeUS time); +// void GetBPS1And2(RakNetTimeUS time, uint64_t &out1, uint64_t &out2); + uint64_t GetTotal1(void) const; +// uint64_t GetTotal2(void) const; + + struct TimeAndValue2 + { + TimeAndValue2(); + ~TimeAndValue2(); + TimeAndValue2(CCTimeType t, uint64_t v1); + // TimeAndValue2(RakNet::TimeUS t, uint64_t v1, uint64_t v2); + // uint64_t value1, value2; + uint64_t value1; + CCTimeType time; + }; + + uint64_t total1, lastSec1; +// uint64_t total2, lastSec2; + DataStructures::Queue dataQueue; + void ClearExpired1(CCTimeType time); +// void ClearExpired2(RakNet::TimeUS time); +}; + +/// Datagram reliable, ordered, unordered and sequenced sends. Flow control. Message splitting, reassembly, and coalescence. +class ReliabilityLayer// +{ +public: + + // Constructor + ReliabilityLayer(); + + // Destructor + ~ReliabilityLayer(); + + /// Resets the layer for reuse + void Reset( bool resetVariables, int MTUSize, bool _useSecurity ); + + /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet + /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. + /// \param[in] time Time, in MS + void SetTimeoutTime( RakNet::TimeMS time ); + + /// Returns the value passed to SetTimeoutTime. or the default if it was never called + /// \param[out] the value passed to SetTimeoutTime + RakNet::TimeMS GetTimeoutTime(void); + + /// Packets are read directly from the socket layer and skip the reliability layer because unconnected players do not use the reliability layer + /// This function takes packet data after a player has been confirmed as connected. + /// \param[in] buffer The socket data + /// \param[in] length The length of the socket data + /// \param[in] systemAddress The player that this data is from + /// \param[in] messageHandlerList A list of registered plugins + /// \param[in] MTUSize maximum datagram size + /// \retval true Success + /// \retval false Modified packet + bool HandleSocketReceiveFromConnectedPlayer( + const char *buffer, unsigned int length, SystemAddress &systemAddress, DataStructures::List &messageHandlerList, int MTUSize, + SOCKET s, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType timeRead, BitStream &updateBitStream); + + /// This allocates bytes and writes a user-level message to those bytes. + /// \param[out] data The message + /// \return Returns number of BITS put into the buffer + BitSize_t Receive( unsigned char**data ); + + /// Puts data on the send queue + /// \param[in] data The data to send + /// \param[in] numberOfBitsToSend The length of \a data in bits + /// \param[in] priority The priority level for the send + /// \param[in] reliability The reliability type for the send + /// \param[in] orderingChannel 0 to 31. Specifies what channel to use, for relational ordering and sequencing of packets. + /// \param[in] makeDataCopy If true \a data will be copied. Otherwise, only a pointer will be stored. + /// \param[in] MTUSize maximum datagram size + /// \param[in] currentTime Current time, as per RakNet::GetTimeMS() + /// \param[in] receipt This number will be returned back with ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS and is only returned with the reliability types that contain RECEIPT in the name + /// \return True or false for success or failure. + bool Send( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, CCTimeType currentTime, uint32_t receipt ); + + /// Call once per game cycle. Handles internal lists and actually does the send. + /// \param[in] s the communication end point + /// \param[in] systemAddress The Unique Player Identifier who shouldhave sent some packets + /// \param[in] MTUSize maximum datagram size + /// \param[in] time current system time + /// \param[in] maxBitsPerSecond if non-zero, enforces that outgoing bandwidth does not exceed this amount + /// \param[in] messageHandlerList A list of registered plugins + void Update( SOCKET s, SystemAddress &systemAddress, int MTUSize, CCTimeType time, + unsigned bitsPerSecondLimit, + DataStructures::List &messageHandlerList, + RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); + + /// Were you ever unable to deliver a packet despite retries? + /// \return true means the connection has been lost. Otherwise not. + bool IsDeadConnection( void ) const; + + /// Causes IsDeadConnection to return true + void KillConnection(void); + + /// Get Statistics + /// \return A pointer to a static struct, filled out with current statistical information. + RakNetStatistics * GetStatistics( RakNetStatistics *rns ); + + ///Are we waiting for any data to be sent out or be processed by the player? + bool IsOutgoingDataWaiting(void); + bool AreAcksWaiting(void); + + // Set outgoing lag and packet loss properties + void ApplyNetworkSimulator( double _maxSendBPS, RakNet::TimeMS _minExtraPing, RakNet::TimeMS _extraPingVariance ); + + /// Returns if you previously called ApplyNetworkSimulator + /// \return If you previously called ApplyNetworkSimulator + bool IsNetworkSimulatorActive( void ); + + void SetSplitMessageProgressInterval(int interval); + void SetUnreliableTimeout(RakNet::TimeMS timeoutMS); + /// Has a lot of time passed since the last ack + bool AckTimeout(RakNet::Time curTime); + CCTimeType GetNextSendTime(void) const; + CCTimeType GetTimeBetweenPackets(void) const; + + + + RakNet::TimeMS GetTimeLastDatagramArrived(void) const {return timeLastDatagramArrived;} + + // If true, will update time between packets quickly based on ping calculations + //void SetDoFastThroughputReactions(bool fast); + + // Encoded as numMessages[unsigned int], message1BitLength[unsigned int], message1Data (aligned), ... + //void GetUndeliveredMessages(RakNet::BitStream *messages, int MTUSize); + +private: + /// Send the contents of a bitstream to the socket + /// \param[in] s The socket used for sending data + /// \param[in] systemAddress The address and port to send to + /// \param[in] bitStream The data to send. + void SendBitStream( SOCKET s, SystemAddress &systemAddress, RakNet::BitStream *bitStream, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType currentTime); + + ///Parse an internalPacket and create a bitstream to represent this data + /// \return Returns number of bits used + BitSize_t WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket, CCTimeType curTime ); + + + /// Parse a bitstream and create an internal packet to represent this data + InternalPacket* CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, CCTimeType time ); + + /// Does what the function name says + unsigned RemovePacketFromResendListAndDeleteOlderReliableSequenced( const MessageNumberType messageNumber, CCTimeType time, DataStructures::List &messageHandlerList, const SystemAddress &systemAddress ); + + /// Acknowledge receipt of the packet with the specified messageNumber + void SendAcknowledgementPacket( const DatagramSequenceNumberType messageNumber, CCTimeType time); + + /// This will return true if we should not send at this time + bool IsSendThrottled( int MTUSize ); + + /// We lost a packet + void UpdateWindowFromPacketloss( CCTimeType time ); + + /// Increase the window size + void UpdateWindowFromAck( CCTimeType time ); + + /// Parse an internalPacket and figure out how many header bits would be written. Returns that number + BitSize_t GetMaxMessageHeaderLengthBits( void ); + BitSize_t GetMessageHeaderLengthBits( const InternalPacket *const internalPacket ); + + /// Get the SHA1 code + void GetSHA1( unsigned char * const buffer, unsigned int nbytes, char code[ SHA1_LENGTH ] ); + + /// Check the SHA1 code + bool CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * const buffer, unsigned int nbytes ); + + /// Search the specified list for sequenced packets on the specified ordering channel, optionally skipping those with splitPacketId, and delete them + void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::List&theList, int splitPacketId = -1 ); + + /// Search the specified list for sequenced packets with a value less than orderingIndex and delete them + void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::Queue&theList ); + + /// Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex + bool IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ); + + /// Split the passed packet into chunks under MTU_SIZE bytes (including headers) and save those new chunks + void SplitPacket( InternalPacket *internalPacket ); + + /// Insert a packet into the split packet list + void InsertIntoSplitPacketList( InternalPacket * internalPacket, CCTimeType time ); + + /// Take all split chunks with the specified splitPacketId and try to reconstruct a packet. If we can, allocate and return it. Otherwise return 0 + InternalPacket * BuildPacketFromSplitPacketList( SplitPacketIdType splitPacketId, CCTimeType time, + SOCKET s, SystemAddress &systemAddress, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); + InternalPacket * BuildPacketFromSplitPacketList( SplitPacketChannel *splitPacketChannel, CCTimeType time ); + + /// Delete any unreliable split packets that have long since expired + //void DeleteOldUnreliableSplitPackets( CCTimeType time ); + + /// Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. + /// Does not copy any split data parameters as that information is always generated does not have any reason to be copied + InternalPacket * CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, CCTimeType time ); + + /// Get the specified ordering list + // DataStructures::LinkedList *GetOrderingListAtOrderingStream( unsigned char orderingChannel ); + + /// Add the internal packet to the ordering list in order based on order index + // void AddToOrderingList( InternalPacket * internalPacket ); + + /// Inserts a packet into the resend list in order + void InsertPacketIntoResendList( InternalPacket *internalPacket, CCTimeType time, bool firstResend, bool modifyUnacknowledgedBytes ); + + /// Memory handling + void FreeMemory( bool freeAllImmediately ); + + /// Memory handling + void FreeThreadSafeMemory( void ); + + // Initialize the variables + void InitializeVariables( void ); + + /// Given the current time, is this time so old that we should consider it a timeout? + bool IsExpiredTime(unsigned int input, CCTimeType currentTime) const; + + // Make it so we don't do resends within a minimum threshold of time + void UpdateNextActionTime(void); + + + /// Does this packet number represent a packet that was skipped (out of order?) + //unsigned int IsReceivedPacketHole(unsigned int input, RakNet::TimeMS currentTime) const; + + /// Skip an element in the received packets list + //unsigned int MakeReceivedPacketHole(unsigned int input) const; + + /// How many elements are waiting to be resent? + unsigned int GetResendListDataSize(void) const; + + /// Update all memory which is not threadsafe + void UpdateThreadedMemory(void); + + void CalculateHistogramAckSize(void); + + // Used ONLY for RELIABLE_ORDERED + // RELIABLE_SEQUENCED just returns the newest one + // DataStructures::List*> orderingList; + DataStructures::Queue outputQueue; + int splitMessageProgressInterval; + CCTimeType unreliableTimeout; + + struct MessageNumberNode + { + DatagramSequenceNumberType messageNumber; + MessageNumberNode *next; + }; + struct DatagramHistoryNode + { + DatagramHistoryNode() {} + DatagramHistoryNode(MessageNumberNode *_head, CCTimeType ts + ) : + head(_head), timeSent(ts) + {} + MessageNumberNode *head; + CCTimeType timeSent; + }; + // Queue length is programmatically restricted to DATAGRAM_MESSAGE_ID_ARRAY_LENGTH + // This is essentially an O(1) lookup to get a DatagramHistoryNode given an index + // datagramHistory holds a linked list of MessageNumberNode. Each MessageNumberNode refers to one element in resendList which can be cleared on an ack. + DataStructures::Queue datagramHistory; + DataStructures::MemoryPool datagramHistoryMessagePool; + + struct UnreliableWithAckReceiptNode + { + UnreliableWithAckReceiptNode() {} + UnreliableWithAckReceiptNode(DatagramSequenceNumberType _datagramNumber, uint32_t _sendReceiptSerial, RakNet::TimeUS _nextActionTime) : + datagramNumber(_datagramNumber), sendReceiptSerial(_sendReceiptSerial), nextActionTime(_nextActionTime) + {} + DatagramSequenceNumberType datagramNumber; + uint32_t sendReceiptSerial; + RakNet::TimeUS nextActionTime; + }; + DataStructures::List unreliableWithAckReceiptHistory; + + void RemoveFromDatagramHistory(DatagramSequenceNumberType index); + MessageNumberNode* GetMessageNumberNodeByDatagramIndex(DatagramSequenceNumberType index, CCTimeType *timeSent); + void AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, CCTimeType timeSent); + MessageNumberNode* AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, DatagramSequenceNumberType messageNumber, CCTimeType timeSent); + MessageNumberNode* AddSubsequentToDatagramHistory(MessageNumberNode *messageNumberNode, DatagramSequenceNumberType messageNumber); + DatagramSequenceNumberType datagramHistoryPopCount; + + DataStructures::MemoryPool internalPacketPool; + // DataStructures::BPlusTree resendTree; + InternalPacket *resendBuffer[RESEND_BUFFER_ARRAY_LENGTH]; + InternalPacket *resendLinkedListHead; + InternalPacket *unreliableLinkedListHead; + void RemoveFromUnreliableLinkedList(InternalPacket *internalPacket); + void AddToUnreliableLinkedList(InternalPacket *internalPacket); +// unsigned int numPacketsOnResendBuffer; + //unsigned int blockWindowIncreaseUntilTime; + // DataStructures::RangeList acknowlegements; + // Resend list is a tree of packets we need to resend + + // Set to the current time when the resend queue is no longer empty + // Set to zero when it becomes empty + // Set to the current time if it is not zero, and we get incoming data + // If the current time - timeResendQueueNonEmpty is greater than a threshold, we are disconnected +// CCTimeType timeResendQueueNonEmpty; + RakNet::TimeMS timeLastDatagramArrived; + + + // If we backoff due to packetloss, don't remeasure until all waiting resends have gone out or else we overcount +// bool packetlossThisSample; +// int backoffThisSample; +// unsigned packetlossThisSampleResendCount; +// CCTimeType lastPacketlossTime; + + //DataStructures::Queue sendPacketSet[ NUMBER_OF_PRIORITIES ]; + DataStructures::Heap outgoingPacketBuffer; + reliabilityHeapWeightType outgoingPacketBufferNextWeights[NUMBER_OF_PRIORITIES]; + void InitHeapWeights(void); + reliabilityHeapWeightType GetNextWeight(int priorityLevel); +// unsigned int messageInSendBuffer[NUMBER_OF_PRIORITIES]; +// double bytesInSendBuffer[NUMBER_OF_PRIORITIES]; + + + DataStructures::OrderedList splitPacketChannelList; + + MessageNumberType sendReliableMessageNumberIndex; + MessageNumberType internalOrderIndex; + //unsigned int windowSize; + //RakNet::BitStream updateBitStream; + bool deadConnection, cheater; + SplitPacketIdType splitPacketId; + RakNet::TimeMS timeoutTime; // How long to wait in MS before timing someone out + //int MAX_AVERAGE_PACKETS_PER_SECOND; // Name says it all +// int RECEIVED_PACKET_LOG_LENGTH, requestedReceivedPacketLogLength; // How big the receivedPackets array is +// unsigned int *receivedPackets; + RakNetStatistics statistics; + + // Algorithm for blending ordered and sequenced on the same channel: + // 1. Each ordered message transmits OrderingIndexType orderedWriteIndex. There are NUMBER_OF_ORDERED_STREAMS independent values of these. The value + // starts at 0. Every time an ordered message is sent, the value increments by 1 + // 2. Each sequenced message contains the current value of orderedWriteIndex for that channel, and additionally OrderingIndexType sequencedWriteIndex. + // sequencedWriteIndex resets to 0 every time orderedWriteIndex increments. It increments by 1 every time a sequenced message is sent. + // 3. The receiver maintains the next expected value for the orderedWriteIndex, stored in orderedReadIndex. + // 4. As messages arrive: + // If a message has the current ordering index, and is sequenced, and is < the current highest sequence value, discard + // If a message has the current ordering index, and is sequenced, and is >= the current highest sequence value, return immediately + // If a message has a greater ordering index, and is sequenced or ordered, buffer it + // If a message has the current ordering index, and is ordered, buffer, then push off messages from buffer + // 5. Pushing off messages from buffer: + // Messages in buffer are put in a minheap. The value of each node is calculated such that messages are returned: + // A. (lowest ordering index, lowest sequence index) + // B. (lowest ordering index, no sequence index) + // Messages are pushed off until the heap is empty, or the next message to be returned does not preserve the ordered index + // For an empty heap, the heap weight should start at the lowest value based on the next expected ordering index, to avoid variable overflow + + // Sender increments this by 1 for every ordered message sent + OrderingIndexType orderedWriteIndex[NUMBER_OF_ORDERED_STREAMS]; + // Sender increments by 1 for every sequenced message sent. Resets to 0 when an ordered message is sent + OrderingIndexType sequencedWriteIndex[NUMBER_OF_ORDERED_STREAMS]; + // Next expected index for ordered messages. + OrderingIndexType orderedReadIndex[NUMBER_OF_ORDERED_STREAMS]; + // Highest value received for sequencedWriteIndex for the current value of orderedReadIndex on the same channel. + OrderingIndexType highestSequencedReadIndex[NUMBER_OF_ORDERED_STREAMS]; + DataStructures::Heap orderingHeaps[NUMBER_OF_ORDERED_STREAMS]; + OrderingIndexType heapIndexOffsets[NUMBER_OF_ORDERED_STREAMS]; + + + + + + + +// CCTimeType histogramStart; +// unsigned histogramBitsSent; + + + /// Memory-efficient receivedPackets algorithm: + /// receivedPacketsBaseIndex is the packet number we are expecting + /// Everything under receivedPacketsBaseIndex is a packet we already got + /// Everything over receivedPacketsBaseIndex is stored in hasReceivedPacketQueue + /// It stores the time to stop waiting for a particular packet number, where the packet number is receivedPacketsBaseIndex + the index into the queue + /// If 0, we got got that packet. Otherwise, the time to give up waiting for that packet. + /// If we get a packet number where (receivedPacketsBaseIndex-packetNumber) is less than half the range of receivedPacketsBaseIndex then it is a duplicate + /// Otherwise, it is a duplicate packet (and ignore it). + // DataStructures::Queue hasReceivedPacketQueue; + DataStructures::Queue hasReceivedPacketQueue; + DatagramSequenceNumberType receivedPacketsBaseIndex; + bool resetReceivedPackets; + + CCTimeType lastUpdateTime; + CCTimeType timeBetweenPackets, nextSendTime; + + + +// CCTimeType ackPingSamples[ACK_PING_SAMPLES_SIZE]; // Must be range of unsigned char to wrap ackPingIndex properly + CCTimeType ackPingSum; + unsigned char ackPingIndex; + //CCTimeType nextLowestPingReset; + RemoteSystemTimeType remoteSystemTime; +// bool continuousSend; +// CCTimeType lastTimeBetweenPacketsIncrease,lastTimeBetweenPacketsDecrease; + // Limit changes in throughput to once per ping - otherwise even if lag starts we don't know about it + // In the meantime the connection is flooded and overrun. + CCTimeType nextAllowedThroughputSample; + bool bandwidthExceededStatistic; + + // If Update::maxBitsPerSecond > 0, then throughputCapCountdown is used as a timer to prevent sends for some amount of time after each send, depending on + // the amount of data sent + long long throughputCapCountdown; + + unsigned receivePacketCount; + +#ifdef _DEBUG + struct DataAndTime// + { + SOCKET s; + char data[ MAXIMUM_MTU_SIZE ]; + unsigned int length; + RakNet::TimeMS sendTime; + // SystemAddress systemAddress; + unsigned short remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions; + }; + DataStructures::Queue delayList; + + // Internet simulator + double packetloss; + RakNet::TimeMS minExtraPing, extraPingVariance; +#endif + + CCTimeType elapsedTimeSinceLastUpdate; + + CCTimeType nextAckTimeToSend; + + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1 + RakNet::CCRakNetSlidingWindow congestionManager; +#else + RakNet::CCRakNetUDT congestionManager; +#endif + + + uint32_t unacknowledgedBytes; + + bool ResendBufferOverflow(void) const; + void ValidateResendList(void) const; + void ResetPacketsAndDatagrams(void); + void PushPacket(CCTimeType time, InternalPacket *internalPacket, bool isReliable); + void PushDatagram(void); + bool TagMostRecentPushAsSecondOfPacketPair(void); + void ClearPacketsAndDatagrams(void); + void MoveToListHead(InternalPacket *internalPacket); + void RemoveFromList(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes); + void AddToListTail(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes); + void PopListHead(bool modifyUnacknowledgedBytes); + bool IsResendQueueEmpty(void) const; + void SortSplitPacketList(DataStructures::List &data, unsigned int leftEdge, unsigned int rightEdge) const; + void SendACKs(SOCKET s, SystemAddress &systemAddress, CCTimeType time, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); + + DataStructures::List packetsToSendThisUpdate; + DataStructures::List packetsToDeallocThisUpdate; + // boundary is in packetsToSendThisUpdate, inclusive + DataStructures::List packetsToSendThisUpdateDatagramBoundaries; + DataStructures::List datagramsToSendThisUpdateIsPair; + DataStructures::List datagramSizesInBytes; + BitSize_t datagramSizeSoFar; + BitSize_t allDatagramSizesSoFar; + double totalUserDataBytesAcked; + CCTimeType timeOfLastContinualSend; + CCTimeType timeToNextUnreliableCull; + + // This doesn't need to be a member, but I do it to avoid reallocations + DataStructures::RangeList incomingAcks; + + // Every 16 datagrams, we make sure the 17th datagram goes out the same update tick, and is the same size as the 16th + int countdownToNextPacketPair; + InternalPacket* AllocateFromInternalPacketPool(void); + void ReleaseToInternalPacketPool(InternalPacket *ip); + + DataStructures::RangeList acknowlegements; + DataStructures::RangeList NAKs; + bool remoteSystemNeedsBAndAS; + + unsigned int GetMaxDatagramSizeExcludingMessageHeaderBytes(void); + BitSize_t GetMaxDatagramSizeExcludingMessageHeaderBits(void); + + // ourOffset refers to a section within externallyAllocatedPtr. Do not deallocate externallyAllocatedPtr until all references are lost + void AllocInternalPacketData(InternalPacket *internalPacket, InternalPacketRefCountedData **refCounter, unsigned char *externallyAllocatedPtr, unsigned char *ourOffset); + // Set the data pointer to externallyAllocatedPtr, do not allocate + void AllocInternalPacketData(InternalPacket *internalPacket, unsigned char *externallyAllocatedPtr); + // Allocate new + void AllocInternalPacketData(InternalPacket *internalPacket, unsigned int numBytes, bool allowStack, const char *file, unsigned int line); + void FreeInternalPacketData(InternalPacket *internalPacket, const char *file, unsigned int line); + DataStructures::MemoryPool refCountedDataPool; + + BPSTracker bpsMetrics[RNS_PER_SECOND_METRICS_COUNT]; + CCTimeType lastBpsClear; + +#if LIBCAT_SECURITY==1 +public: + cat::AuthenticatedEncryption* GetAuthenticatedEncryption(void) { return &auth_enc; } + +protected: + cat::AuthenticatedEncryption auth_enc; + bool useSecurity; +#endif // LIBCAT_SECURITY +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/ReplicaEnums.h b/project/lib_projects/raknet/jni/RaknetSources/ReplicaEnums.h index 93aeef8..7b0b4a0 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ReplicaEnums.h +++ b/project/lib_projects/raknet/jni/RaknetSources/ReplicaEnums.h @@ -1,43 +1,43 @@ -/// \file -/// \brief Contains enumerations used by the ReplicaManager system. This file is a lightweight header, so you can include it without worrying about linking in lots of other crap -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __REPLICA_ENUMS_H -#define __REPLICA_ENUMS_H - -/// Replica interface flags, used to enable and disable function calls on the Replica object -/// Passed to ReplicaManager::EnableReplicaInterfaces and ReplicaManager::DisableReplicaInterfaces -enum -{ - REPLICA_RECEIVE_DESTRUCTION=1<<0, - REPLICA_RECEIVE_SERIALIZE=1<<1, - REPLICA_RECEIVE_SCOPE_CHANGE=1<<2, - REPLICA_SEND_CONSTRUCTION=1<<3, - REPLICA_SEND_DESTRUCTION=1<<4, - REPLICA_SEND_SCOPE_CHANGE=1<<5, - REPLICA_SEND_SERIALIZE=1<<6, - REPLICA_SET_ALL = 0xFF // Allow all of the above -}; - -enum ReplicaReturnResult -{ - /// This means call the function again later, with the same parameters - REPLICA_PROCESS_LATER, - /// This means we are done processing (the normal result to return) - REPLICA_PROCESSING_DONE, - /// This means cancel the processing - don't send any network messages and don't change the current state. - REPLICA_CANCEL_PROCESS, - /// Same as REPLICA_PROCESSING_DONE, where a message is sent, but does not clear the send bit. - /// Useful for multi-part sends with different reliability levels. - /// Only currently used by Replica::Serialize - REPLICA_PROCESS_AGAIN, - /// Only returned from the Replica::SendConstruction interface, means act as if the other system had this object but don't actually - /// Send a construction packet. This way you will still send scope and serialize packets to that system - REPLICA_PROCESS_IMPLICIT -}; - -#endif +/// \file +/// \brief Contains enumerations used by the ReplicaManager system. This file is a lightweight header, so you can include it without worrying about linking in lots of other crap +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __REPLICA_ENUMS_H +#define __REPLICA_ENUMS_H + +/// Replica interface flags, used to enable and disable function calls on the Replica object +/// Passed to ReplicaManager::EnableReplicaInterfaces and ReplicaManager::DisableReplicaInterfaces +enum +{ + REPLICA_RECEIVE_DESTRUCTION=1<<0, + REPLICA_RECEIVE_SERIALIZE=1<<1, + REPLICA_RECEIVE_SCOPE_CHANGE=1<<2, + REPLICA_SEND_CONSTRUCTION=1<<3, + REPLICA_SEND_DESTRUCTION=1<<4, + REPLICA_SEND_SCOPE_CHANGE=1<<5, + REPLICA_SEND_SERIALIZE=1<<6, + REPLICA_SET_ALL = 0xFF // Allow all of the above +}; + +enum ReplicaReturnResult +{ + /// This means call the function again later, with the same parameters + REPLICA_PROCESS_LATER, + /// This means we are done processing (the normal result to return) + REPLICA_PROCESSING_DONE, + /// This means cancel the processing - don't send any network messages and don't change the current state. + REPLICA_CANCEL_PROCESS, + /// Same as REPLICA_PROCESSING_DONE, where a message is sent, but does not clear the send bit. + /// Useful for multi-part sends with different reliability levels. + /// Only currently used by Replica::Serialize + REPLICA_PROCESS_AGAIN, + /// Only returned from the Replica::SendConstruction interface, means act as if the other system had this object but don't actually + /// Send a construction packet. This way you will still send scope and serialize packets to that system + REPLICA_PROCESS_IMPLICIT +}; + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/ReplicaManager3.cpp b/project/lib_projects/raknet/jni/RaknetSources/ReplicaManager3.cpp index 0bf0958..41d40c7 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ReplicaManager3.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/ReplicaManager3.cpp @@ -1,2250 +1,2250 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ReplicaManager3==1 - -#include "ReplicaManager3.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "NetworkIDManager.h" - -using namespace RakNet; - -// DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS(LastSerializationResult,Replica3*,replica); - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool PRO::operator==( const PRO& right ) const -{ - return priority == right.priority && reliability == right.reliability && orderingChannel == right.orderingChannel && sendReceipt == right.sendReceipt; -} - -bool PRO::operator!=( const PRO& right ) const -{ - return priority != right.priority || reliability != right.reliability || orderingChannel != right.orderingChannel || sendReceipt != right.sendReceipt; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -int Connection_RM3::Replica3LSRComp( Replica3 * const &replica3, LastSerializationResult * const &data ) -{ - if (replica3->GetNetworkID() < data->replica->GetNetworkID()) - return -1; - if (replica3->GetNetworkID() > data->replica->GetNetworkID()) - return 1; - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -LastSerializationResult::LastSerializationResult() -{ - replica=0; - lastSerializationResultBS=0; -} -LastSerializationResult::~LastSerializationResult() -{ - if (lastSerializationResultBS) - RakNet::OP_DELETE(lastSerializationResultBS,_FILE_AND_LINE_); -} -void LastSerializationResult::AllocBS(void) -{ - if (lastSerializationResultBS==0) - { - lastSerializationResultBS=RakNet::OP_NEW(_FILE_AND_LINE_); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ReplicaManager3::ReplicaManager3() -{ - defaultSendParameters.orderingChannel=0; - defaultSendParameters.priority=HIGH_PRIORITY; - defaultSendParameters.reliability=RELIABLE_ORDERED; - defaultSendParameters.sendReceipt=0; - autoSerializeInterval=30; - lastAutoSerializeOccurance=0; - worldId=0; - autoCreateConnections=true; - autoDestroyConnections=true; - networkIDManager=0; - currentlyDeallocatingReplica=0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ReplicaManager3::~ReplicaManager3() -{ - if (autoDestroyConnections) - { - // Clear() calls DeallocConnection(), which is pure virtual and cannot be called from the destructor - RakAssert(connectionList.Size()==0); - } - Clear(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetAutoManageConnections(bool autoCreate, bool autoDestroy) -{ - autoCreateConnections=autoCreate; - autoDestroyConnections=autoDestroy; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool ReplicaManager3::PushConnection(RakNet::Connection_RM3 *newConnection) -{ - if (newConnection==0) - return false; - if (GetConnectionByGUID(newConnection->GetRakNetGUID())) - return false; - unsigned int index = connectionList.GetIndexOf(newConnection); - if (index==(unsigned int)-1) - { - connectionList.Push(newConnection,_FILE_AND_LINE_); - - // Send message to validate the connection - newConnection->SendValidation(rakPeerInterface, worldId); - - Connection_RM3::ConstructionMode constructionMode = newConnection->QueryConstructionMode(); - if (constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - unsigned int pushIdx; - for (pushIdx=0; pushIdx < userReplicaList.Size(); pushIdx++) - newConnection->OnLocalReference(userReplicaList[pushIdx], this); - } - } - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::DeallocReplicaNoBroadcastDestruction(RakNet::Connection_RM3 *connection, RakNet::Replica3 *replica3) -{ - currentlyDeallocatingReplica=replica3; - replica3->DeallocReplica(connection); - currentlyDeallocatingReplica=0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RakNet::Connection_RM3 * ReplicaManager3::PopConnection(unsigned int index) -{ - DataStructures::List replicaList; - DataStructures::List destructionList; - DataStructures::List broadcastList; - RakNet::Connection_RM3 *connection; - unsigned int index2; - RM3ActionOnPopConnection action; - - connection=connectionList[index]; - - // Clear out downloadGroup - connection->ClearDownloadGroup(rakPeerInterface); - - RakNetGUID guid = connection->GetRakNetGUID(); - // This might be wrong, I am relying on the variable creatingSystemGuid which is transmitted - // automatically from the first system to reference the object. However, if an object changes - // owners then it is not going to be returned here, and therefore QueryActionOnPopConnection() - // will not be called for the new owner. - GetReplicasCreatedByGuid(guid, replicaList); - - for (index2=0; index2 < replicaList.Size(); index2++) - { - action = replicaList[index2]->QueryActionOnPopConnection(connection); - replicaList[index2]->OnPoppedConnection(connection); - if (action==RM3AOPC_DELETE_REPLICA) - { - destructionList.Push( replicaList[index2], _FILE_AND_LINE_ ); - } - else if (action==RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION) - { - destructionList.Push( replicaList[index2], _FILE_AND_LINE_ ); - - broadcastList.Push( replicaList[index2], _FILE_AND_LINE_ ); - } - } - - BroadcastDestructionList(broadcastList, connection->GetSystemAddress()); - for (index2=0; index2 < destructionList.Size(); index2++) - { - destructionList[index2]->PreDestruction(connection); - destructionList[index2]->DeallocReplica(connection); - } - - connectionList.RemoveAtIndex(index); - return connection; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RakNet::Connection_RM3 * ReplicaManager3::PopConnection(RakNetGUID guid) -{ - unsigned int index; - - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->GetRakNetGUID()==guid) - { - return PopConnection(index); - } - } - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::Reference(RakNet::Replica3 *replica3) -{ - unsigned int index = ReferenceInternal(replica3); - - if (index!=(unsigned int)-1) - { - unsigned int pushIdx; - for (pushIdx=0; pushIdx < connectionList.Size(); pushIdx++) - { - Connection_RM3::ConstructionMode constructionMode = connectionList[pushIdx]->QueryConstructionMode(); - if (constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - connectionList[pushIdx]->OnLocalReference(replica3, this); - } - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -unsigned int ReplicaManager3::ReferenceInternal(RakNet::Replica3 *replica3) -{ - unsigned int index; - index = userReplicaList.GetIndexOf(replica3); - if (index==(unsigned int)-1) - { - RakAssert(networkIDManager); - replica3->SetNetworkIDManager(networkIDManager); - if (replica3->creatingSystemGUID==UNASSIGNED_RAKNET_GUID) - replica3->creatingSystemGUID=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - replica3->replicaManager=this; - userReplicaList.Push(replica3,_FILE_AND_LINE_); - index=userReplicaList.Size()-1; - } - return index; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::Dereference(RakNet::Replica3 *replica3) -{ - unsigned int index, index2; - for (index=0; index < userReplicaList.Size(); index++) - { - if (userReplicaList[index]==replica3) - { - userReplicaList.RemoveAtIndex(index); - break; - } - } - - // Remove from all connections - for (index2=0; index2 < connectionList.Size(); index2++) - { - connectionList[index2]->OnDereference(replica3, this); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::DereferenceList(DataStructures::List &replicaListIn) -{ - unsigned int index; - for (index=0; index < replicaListIn.Size(); index++) - Dereference(replicaListIn[index]); -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::GetReplicasCreatedByMe(DataStructures::List &replicaListOut) -{ - RakNetGUID myGuid = rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - GetReplicasCreatedByGuid(rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS), replicaListOut); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::GetReferencedReplicaList(DataStructures::List &replicaListOut) -{ - replicaListOut=userReplicaList; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::GetReplicasCreatedByGuid(RakNetGUID guid, DataStructures::List &replicaListOut) -{ - replicaListOut.Clear(false,_FILE_AND_LINE_); - unsigned int index; - for (index=0; index < userReplicaList.Size(); index++) - { - if (userReplicaList[index]->creatingSystemGUID==guid) - replicaListOut.Push(userReplicaList[index],_FILE_AND_LINE_); - } -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -unsigned ReplicaManager3::GetReplicaCount(void) const -{ - return userReplicaList.Size(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Replica3 *ReplicaManager3::GetReplicaAtIndex(unsigned index) -{ - return userReplicaList[index]; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -unsigned int ReplicaManager3::GetConnectionCount(void) const -{ - return connectionList.Size(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3* ReplicaManager3::GetConnectionAtIndex(unsigned index) const -{ - return connectionList[index]; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3* ReplicaManager3::GetConnectionBySystemAddress(const SystemAddress &sa) const -{ - unsigned int index; - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->GetSystemAddress()==sa) - { - return connectionList[index]; - } - } - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3* ReplicaManager3::GetConnectionByGUID(RakNetGUID guid) const -{ - unsigned int index; - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->GetRakNetGUID()==guid) - { - return connectionList[index]; - } - } - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetDefaultOrderingChannel(char def) -{ - defaultSendParameters.orderingChannel=def; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetDefaultPacketPriority(PacketPriority def) -{ - defaultSendParameters.priority=def; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetDefaultPacketReliability(PacketReliability def) -{ - defaultSendParameters.reliability=def; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetAutoSerializeInterval(RakNet::Time intervalMS) -{ - autoSerializeInterval=intervalMS; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::GetConnectionsThatHaveReplicaConstructed(Replica3 *replica, DataStructures::List &connectionsThatHaveConstructedThisReplica) -{ - connectionsThatHaveConstructedThisReplica.Clear(false,_FILE_AND_LINE_); - unsigned int index; - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->HasReplicaConstructed(replica)) - connectionsThatHaveConstructedThisReplica.Push(connectionList[index],_FILE_AND_LINE_); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::Clear(void) -{ - if (autoDestroyConnections) - { - for (unsigned int i=0; i < connectionList.Size(); i++) - DeallocConnection(connectionList[i]); - } - else - { - // Clear out downloadGroup even if not auto destroying the connection, since the packets need to go back to RakPeer - for (unsigned int i=0; i < connectionList.Size(); i++) - connectionList[i]->ClearDownloadGroup(rakPeerInterface); - } - - - - connectionList.Clear(true,_FILE_AND_LINE_); - userReplicaList.Clear(true,_FILE_AND_LINE_); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PRO ReplicaManager3::GetDefaultSendParameters(void) const -{ - return defaultSendParameters; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetWorldID(unsigned char id) -{ - worldId=id; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -unsigned char ReplicaManager3::GetWorldID(void) const -{ - return worldId; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -NetworkIDManager *ReplicaManager3::GetNetworkIDManager(void) const -{ - return networkIDManager; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetNetworkIDManager(NetworkIDManager *_networkIDManager) -{ - networkIDManager=_networkIDManager; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnReceive(Packet *packet) -{ - if (packet->length<2) - return RR_CONTINUE_PROCESSING; - - unsigned char incomingWorldId; - - RakNet::Time timestamp=0; - unsigned char packetIdentifier, packetDataOffset; - if ( ( unsigned char ) packet->data[ 0 ] == ID_TIMESTAMP ) - { - if ( packet->length > sizeof( unsigned char ) + sizeof( RakNet::Time ) ) - { - packetIdentifier = ( unsigned char ) packet->data[ sizeof( unsigned char ) + sizeof( RakNet::Time ) ]; - // Required for proper endian swapping - RakNet::BitStream tsBs(packet->data+sizeof(MessageID),packet->length-1,false); - tsBs.Read(timestamp); - incomingWorldId=packet->data[sizeof( unsigned char )*2 + sizeof( RakNet::Time )]; - packetDataOffset=sizeof( unsigned char )*3 + sizeof( RakNet::Time ); - } - else - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - else - { - packetIdentifier = ( unsigned char ) packet->data[ 0 ]; - incomingWorldId=packet->data[sizeof( unsigned char )]; - packetDataOffset=sizeof( unsigned char )*2; - } - - switch (packetIdentifier) - { - case ID_REPLICA_MANAGER_CONSTRUCTION: - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - return OnConstruction(packet, packet->data, packet->length, packet->guid, packetDataOffset); - case ID_REPLICA_MANAGER_SERIALIZE: - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - return OnSerialize(packet, packet->data, packet->length, packet->guid, timestamp, packetDataOffset); - case ID_REPLICA_MANAGER_DOWNLOAD_STARTED: - if (packet->wasGeneratedLocally==false) - { - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - return OnDownloadStarted(packet, packet->data, packet->length, packet->guid, packetDataOffset); - } - else - break; - case ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE: - if (packet->wasGeneratedLocally==false) - { - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - return OnDownloadComplete(packet, packet->data, packet->length, packet->guid, packetDataOffset); - } - else - break; - case ID_REPLICA_MANAGER_SCOPE_CHANGE: - { - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - - Connection_RM3 *connection = GetConnectionByGUID(packet->guid); - if (connection && connection->isValidated==false) - { - // This connection is now confirmed bidirectional - connection->isValidated=true; - // Reply back on validation - connection->SendValidation(rakPeerInterface,worldId); - } - } - } - - return RR_CONTINUE_PROCESSING; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::AutoConstructByQuery(ReplicaManager3 *replicaManager3) -{ - ValidateLists(replicaManager3); - - ConstructionMode constructionMode = QueryConstructionMode(); - - unsigned int index; - RM3ConstructionState constructionState; - LastSerializationResult *lsr; - index=0; - - constructedReplicasCulled.Clear(false,_FILE_AND_LINE_); - destroyedReplicasCulled.Clear(false,_FILE_AND_LINE_); - - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - while (index < queryToConstructReplicaList.Size()) - { - lsr=queryToConstructReplicaList[index]; - constructionState=lsr->replica->QueryConstruction(this, replicaManager3); - if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY || constructionState==RM3CS_ALREADY_EXISTS_REMOTELY_DO_NOT_CONSTRUCT) - { - OnReplicaAlreadyExists(index, replicaManager3); - if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY) - constructedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); - - /* - if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY) - { - // Serialize construction data to this connection - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_REPLICA_MANAGER_3_SERIALIZE_CONSTRUCTION_EXISTING); - bsOut.Write(replicaManager3->GetWorldID()); - NetworkID networkId; - networkId=lsr->replica->GetNetworkID(); - bsOut.Write(networkId); - BitSize_t bitsWritten = bsOut.GetNumberOfBitsUsed(); - lsr->replica->SerializeConstructionExisting(&bsOut, this); - if (bsOut.GetNumberOfBitsUsed()!=bitsWritten) - replicaManager3->SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,GetSystemAddress(), false); - } - - // Serialize first serialization to this connection. - // This is done here, as it isn't done in PushConstruction - SerializeParameters sp; - RakNet::BitStream emptyBs; - for (index=0; index < (unsigned int) RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; index++) - { - sp.lastSentBitstream[index]=&emptyBs; - sp.pro[index]=replicaManager3->GetDefaultSendParameters(); - } - sp.bitsWrittenSoFar=0; - sp.destinationConnection=this; - sp.messageTimestamp=0; - sp.whenLastSerialized=0; - - RakNet::Replica3 *replica = lsr->replica; - - RM3SerializationResult res = replica->Serialize(&sp); - if (res!=RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION && - res!=RM3SR_DO_NOT_SERIALIZE && - res!=RM3SR_SERIALIZED_UNIQUELY) - { - bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp.bitsWrittenSoFar+=sp.outputBitstream[z].GetNumberOfBitsUsed(); - allIndices[z]=true; - } - if (SendSerialize(replica, allIndices, sp.outputBitstream, sp.messageTimestamp, sp.pro, replicaManager3->GetRakPeerInterface(), replicaManager3->GetWorldID())==SSICR_SENT_DATA) - lsr->replica->whenLastSerialized=RakNet::GetTimeMS(); - } - */ - } - else if (constructionState==RM3CS_SEND_CONSTRUCTION) - { - OnConstructToThisConnection(index, replicaManager3); - constructedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); - } - else if (constructionState==RM3CS_NEVER_CONSTRUCT) - { - OnNeverConstruct(index, replicaManager3); - } - else// if (constructionState==RM3CS_NO_ACTION) - { - // Do nothing - index++; - } - } - - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - RM3DestructionState destructionState; - index=0; - while (index < queryToDestructReplicaList.Size()) - { - lsr=queryToDestructReplicaList[index]; - destructionState=lsr->replica->QueryDestruction(this, replicaManager3); - if (destructionState==RM3DS_SEND_DESTRUCTION) - { - OnSendDestructionFromQuery(index, replicaManager3); - destroyedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); - } - else if (destructionState==RM3DS_DO_NOT_QUERY_DESTRUCTION) - { - OnDoNotQueryDestruction(index, replicaManager3); - } - else// if (destructionState==RM3CS_NO_ACTION) - { - // Do nothing - index++; - } - } - } - } - else if (constructionMode==QUERY_CONNECTION_FOR_REPLICA_LIST) - { - QueryReplicaList(constructedReplicasCulled,destroyedReplicasCulled); - - unsigned int idx1, idx2; - - // Create new - for (idx2=0; idx2 < constructedReplicasCulled.Size(); idx2++) - OnConstructToThisConnection(constructedReplicasCulled[idx2], replicaManager3); - - bool exists; - for (idx2=0; idx2 < destroyedReplicasCulled.Size(); idx2++) - { - exists=false; - bool objectExists; - idx1=constructedReplicaList.GetIndexFromKey(destroyedReplicasCulled[idx2], &objectExists); - RakAssert(objectExists); - if (objectExists) - { - OnSendDestructionFromQuery(idx1,replicaManager3); - } - - // If this assert hits, the user tried to destroy a replica that doesn't exist on the remote system - RakAssert(exists); - } - } - - SendConstruction(constructedReplicasCulled,destroyedReplicasCulled,replicaManager3->defaultSendParameters,replicaManager3->rakPeerInterface,replicaManager3->worldId,replicaManager3); -} -void ReplicaManager3::Update(void) -{ - unsigned int index,index2; - - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->isValidated==false) - continue; - connectionList[index]->AutoConstructByQuery(this); - } - - RakNet::Time time = RakNet::GetTimeMS(); - - if (time - lastAutoSerializeOccurance >= autoSerializeInterval) - { - for (index=0; index < userReplicaList.Size(); index++) - { - userReplicaList[index]->forceSendUntilNextUpdate=false; - userReplicaList[index]->OnUserReplicaPreSerializeTick(); - } - - - unsigned int index; - SerializeParameters sp; - sp.curTime=time; - Connection_RM3 *connection; - SendSerializeIfChangedResult ssicr; - sp.messageTimestamp=0; - for (int i=0; i < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; i++) - sp.pro[i]=defaultSendParameters; - index2=0; - for (index=0; index < connectionList.Size(); index++) - { - connection = connectionList[index]; - sp.bitsWrittenSoFar=0; - index2=0; - while (index2 < connection->queryToSerializeReplicaList.Size()) - { - sp.destinationConnection=connection; - sp.whenLastSerialized=connection->queryToSerializeReplicaList[index2]->replica->whenLastSerialized; - ssicr=connection->SendSerializeIfChanged(index2, &sp, GetRakPeerInterface(), GetWorldID(), this); - if (ssicr==SSICR_SENT_DATA) - { - connection->queryToSerializeReplicaList[index2]->replica->whenLastSerialized=time; - index2++; - } - else if (ssicr==SSICR_NEVER_SERIALIZE) - { - // Removed from the middle of the list - } - else - index2++; - } - } - - lastAutoSerializeOccurance=time; - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - if (autoDestroyConnections) - { - Connection_RM3 *connection = PopConnection(rakNetGUID); - if (connection) - DeallocConnection(connection); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) isIncoming; - if (autoCreateConnections) - { - Connection_RM3 *connection = AllocConnection(systemAddress, rakNetGUID); - if (connection) - PushConnection(connection); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::OnRakPeerShutdown(void) -{ - if (autoDestroyConnections) - { - while (connectionList.Size()) - { - Connection_RM3 *connection = PopConnection(connectionList.Size()-1); - if (connection) - DeallocConnection(connection); - } - } - - Clear(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::OnDetach(void) -{ - OnRakPeerShutdown(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnConstruction(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) -{ - Connection_RM3 *connection = GetConnectionByGUID(senderGuid); - if (connection==0) - { - // Almost certainly a bug - RakAssert("Got OnConstruction but no connection yet" && 0); - return RR_CONTINUE_PROCESSING; - } - if (connection->groupConstructionAndSerialize) - { - connection->downloadGroup.Push(packet, __FILE__, __LINE__); - return RR_STOP_PROCESSING; - } - - RakNet::BitStream bsIn(packetData,packetDataLength,false); - bsIn.IgnoreBytes(packetDataOffset); - uint16_t constructionObjectListSize, destructionObjectListSize, index, index2; - BitSize_t streamEnd, writeAllocationIDEnd; - Replica3 *replica; - NetworkID networkId; - RakNetGUID creatingSystemGuid; - bool actuallyCreateObject=false; - - DataStructures::List actuallyCreateObjectList; - DataStructures::List constructionTickStack; - - RakAssert(networkIDManager); - - bsIn.Read(constructionObjectListSize); - for (index=0; index < constructionObjectListSize; index++) - { - bsIn.Read(streamEnd); - bsIn.Read(networkId); - Replica3* existingReplica = networkIDManager->GET_OBJECT_FROM_ID(networkId); - bsIn.Read(actuallyCreateObject); - actuallyCreateObjectList.Push(actuallyCreateObject, _FILE_AND_LINE_); - bsIn.AlignReadToByteBoundary(); - - if (actuallyCreateObject) - { - bsIn.Read(creatingSystemGuid); - bsIn.Read(writeAllocationIDEnd); - - //printf("OnConstruction: %i\n",networkId.guid.g); // Removeme - if (existingReplica) - { - existingReplica->replicaManager=this; - - // Network ID already in use - connection->OnDownloadExisting(existingReplica, this); - - constructionTickStack.Push(0, _FILE_AND_LINE_); - bsIn.SetReadOffset(streamEnd); - continue; - } - - bsIn.AlignReadToByteBoundary(); - replica = connection->AllocReplica(&bsIn, this); - if (replica==0) - { - constructionTickStack.Push(0, _FILE_AND_LINE_); - bsIn.SetReadOffset(streamEnd); - continue; - } - - // Go past the bitStream written to with WriteAllocationID(). Necessary in case the user didn't read out the bitStream the same way it was written - // bitOffset2 is already aligned - bsIn.SetReadOffset(writeAllocationIDEnd); - - replica->SetNetworkIDManager(networkIDManager); - replica->SetNetworkID(networkId); - - replica->replicaManager=this; - replica->creatingSystemGUID=creatingSystemGuid; - - if (!replica->QueryRemoteConstruction(connection) || - !replica->DeserializeConstruction(&bsIn, connection)) - { - DeallocReplicaNoBroadcastDestruction(connection, replica); - bsIn.SetReadOffset(streamEnd); - constructionTickStack.Push(0, _FILE_AND_LINE_); - continue; - } - - constructionTickStack.Push(replica, _FILE_AND_LINE_); - - // Register the replica - ReferenceInternal(replica); - } - else - { - if (existingReplica) - { - existingReplica->DeserializeConstructionExisting(&bsIn, connection); - constructionTickStack.Push(existingReplica, _FILE_AND_LINE_); - } - else - { - constructionTickStack.Push(0, _FILE_AND_LINE_); - } - } - - - bsIn.SetReadOffset(streamEnd); - bsIn.AlignReadToByteBoundary(); - } - - RakAssert(constructionTickStack.Size()==constructionObjectListSize); - RakAssert(actuallyCreateObjectList.Size()==constructionObjectListSize); - - RakNet::BitStream empty; - for (index=0; index < constructionObjectListSize; index++) - { - bool pdcWritten=false; - bsIn.Read(pdcWritten); - if (pdcWritten) - { - bsIn.AlignReadToByteBoundary(); - bsIn.Read(streamEnd); - bsIn.Read(networkId); - if (constructionTickStack[index]!=0) - { - bsIn.AlignReadToByteBoundary(); - if (actuallyCreateObjectList[index]) - constructionTickStack[index]->PostDeserializeConstruction(&bsIn, connection); - else - constructionTickStack[index]->PostDeserializeConstructionExisting(&bsIn, connection); - } - bsIn.SetReadOffset(streamEnd); - } - else - { - if (constructionTickStack[index]!=0) - constructionTickStack[index]->PostDeserializeConstruction(&empty, connection); - } - } - - for (index=0; index < constructionObjectListSize; index++) - { - if (constructionTickStack[index]!=0) - { - if (actuallyCreateObjectList[index]) - { - // Tell the connection(s) that this object exists since they just sent it to us - connection->OnDownloadFromThisSystem(constructionTickStack[index], this); - - for (index2=0; index2 < connectionList.Size(); index2++) - { - if (connectionList[index2]!=connection) - connectionList[index2]->OnDownloadFromOtherSystem(constructionTickStack[index], this); - } - } - } - } - - // Destructions - bool b = bsIn.Read(destructionObjectListSize); - (void) b; - RakAssert(b); - for (index=0; index < destructionObjectListSize; index++) - { - bsIn.Read(networkId); - bsIn.Read(streamEnd); - replica = networkIDManager->GET_OBJECT_FROM_ID(networkId); - if (replica==0) - { - // Unknown object - bsIn.SetReadOffset(streamEnd); - continue; - } - bsIn.Read(replica->deletingSystemGUID); - if (replica->DeserializeDestruction(&bsIn,connection)) - { - // Make sure it wasn't deleted in DeserializeDestruction - if (networkIDManager->GET_OBJECT_FROM_ID(networkId)) - { - replica->PreDestruction(connection); - - // Forward deletion by remote system - if (replica->QueryRelayDestruction(connection)) - BroadcastDestruction(replica,connection->GetSystemAddress()); - Dereference(replica); - DeallocReplicaNoBroadcastDestruction(connection, replica); - } - } - else - { - replica->PreDestruction(connection); - connection->OnDereference(replica, this); - } - - bsIn.AlignReadToByteBoundary(); - } - return RR_CONTINUE_PROCESSING; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnSerialize(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, RakNet::Time timestamp, unsigned char packetDataOffset) -{ - Connection_RM3 *connection = GetConnectionByGUID(senderGuid); - if (connection==0) - return RR_CONTINUE_PROCESSING; - if (connection->groupConstructionAndSerialize) - { - connection->downloadGroup.Push(packet, __FILE__, __LINE__); - return RR_STOP_PROCESSING; - } - RakAssert(networkIDManager); - RakNet::BitStream bsIn(packetData,packetDataLength,false); - bsIn.IgnoreBytes(packetDataOffset); - - struct DeserializeParameters ds; - ds.timeStamp=timestamp; - ds.sourceConnection=connection; - - Replica3 *replica; - NetworkID networkId; - BitSize_t bitsUsed; - bsIn.Read(networkId); - //printf("OnSerialize: %i\n",networkId.guid.g); // Removeme - replica = networkIDManager->GET_OBJECT_FROM_ID(networkId); - if (replica) - { - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - bsIn.Read(ds.bitstreamWrittenTo[z]); - if (ds.bitstreamWrittenTo[z]) - { - bsIn.ReadCompressed(bitsUsed); - bsIn.AlignReadToByteBoundary(); - bsIn.Read(ds.serializationBitstream[z], bitsUsed); - } - } - replica->Deserialize(&ds); - } - return RR_CONTINUE_PROCESSING; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnDownloadStarted(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) -{ - Connection_RM3 *connection = GetConnectionByGUID(senderGuid); - if (connection==0) - return RR_CONTINUE_PROCESSING; - if (connection->QueryGroupDownloadMessages() && - // ID_DOWNLOAD_STARTED will be processed twice, being processed the second time once ID_DOWNLOAD_COMPLETE arrives. - // However, the second time groupConstructionAndSerialize will be set to true so it won't be processed a third time - connection->groupConstructionAndSerialize==false - ) - { - // These messages will be held by the plugin and returned when the download is complete - connection->groupConstructionAndSerialize=true; - RakAssert(connection->downloadGroup.Size()==0); - connection->downloadGroup.Push(packet, __FILE__, __LINE__); - return RR_STOP_PROCESSING; - } - - connection->groupConstructionAndSerialize=false; - RakNet::BitStream bsIn(packetData,packetDataLength,false); - bsIn.IgnoreBytes(packetDataOffset); - connection->DeserializeOnDownloadStarted(&bsIn); - return RR_CONTINUE_PROCESSING; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnDownloadComplete(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) -{ - Connection_RM3 *connection = GetConnectionByGUID(senderGuid); - if (connection==0) - return RR_CONTINUE_PROCESSING; - - if (connection->groupConstructionAndSerialize==true && connection->downloadGroup.Size()>0) - { - // Push back buffered packets in front of this one - unsigned int i; - for (i=0; i < connection->downloadGroup.Size(); i++) - rakPeerInterface->PushBackPacket(connection->downloadGroup[i],false); - - // Push this one to be last too. It will be processed again, but the second time - // groupConstructionAndSerialize will be false and downloadGroup will be empty, so it will go past this block - connection->downloadGroup.Clear(__FILE__,__LINE__); - rakPeerInterface->PushBackPacket(packet,false); - - return RR_STOP_PROCESSING; - } - - RakNet::BitStream bsIn(packetData,packetDataLength,false); - bsIn.IgnoreBytes(packetDataOffset); - connection->DeserializeOnDownloadComplete(&bsIn); - return RR_CONTINUE_PROCESSING; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Replica3* ReplicaManager3::GetReplicaByNetworkID(NetworkID networkId) -{ - unsigned int i; - for (i=0; i < userReplicaList.Size(); i++) - { - if (userReplicaList[i]->GetNetworkID()==networkId) - return userReplicaList[i]; - } - return 0; -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - - -void ReplicaManager3::BroadcastDestructionList(DataStructures::List &replicaListSource, const SystemAddress &exclusionAddress) -{ - RakNet::BitStream bsOut; - unsigned int i,j; - - DataStructures::List replicaList; - - for (i=0; i < replicaListSource.Size(); i++) - { - if (replicaListSource[i]==currentlyDeallocatingReplica) - continue; - replicaList.Push(replicaListSource[i], __FILE__, __LINE__); - } - - if (replicaList.Size()==0) - return; - - for (i=0; i < replicaList.Size(); i++) - { - if (replicaList[i]->deletingSystemGUID==UNASSIGNED_RAKNET_GUID) - replicaList[i]->deletingSystemGUID=GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - } - - for (j=0; j < connectionList.Size(); j++) - { - if (connectionList[j]->GetSystemAddress()==exclusionAddress) - continue; - - bsOut.Reset(); - bsOut.Write((MessageID)ID_REPLICA_MANAGER_CONSTRUCTION); - bsOut.Write(worldId); - uint16_t cnt=0; - bsOut.Write(cnt); // No construction - cnt=(uint16_t) replicaList.Size(); - BitSize_t cntOffset=bsOut.GetWriteOffset();; - bsOut.Write(cnt); // Overwritten at send call - cnt=0; - - for (i=0; i < replicaList.Size(); i++) - { - if (connectionList[j]->HasReplicaConstructed(replicaList[i])==false) - continue; - cnt++; - - NetworkID networkId; - networkId=replicaList[i]->GetNetworkID(); - bsOut.Write(networkId); - BitSize_t offsetStart, offsetEnd; - offsetStart=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart); - bsOut.Write(replicaList[i]->deletingSystemGUID); - replicaList[i]->SerializeDestruction(&bsOut, connectionList[j]); - bsOut.AlignWriteToByteBoundary(); - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - } - - if (cnt>0) - { - BitSize_t curOffset=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(cntOffset); - bsOut.Write(cnt); - bsOut.SetWriteOffset(curOffset); - rakPeerInterface->Send(&bsOut,defaultSendParameters.priority,defaultSendParameters.reliability,defaultSendParameters.orderingChannel,connectionList[j]->GetSystemAddress(),false, defaultSendParameters.sendReceipt); - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - - -void ReplicaManager3::BroadcastDestruction(Replica3 *replica, const SystemAddress &exclusionAddress) -{ - DataStructures::List replicaList; - replicaList.Push(replica, _FILE_AND_LINE_ ); - BroadcastDestructionList(replicaList,exclusionAddress); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3::Connection_RM3(const SystemAddress &_systemAddress, RakNetGUID _guid) -: systemAddress(_systemAddress), guid(_guid) -{ - isValidated=false; - isFirstConstruction=true; - groupConstructionAndSerialize=false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3::~Connection_RM3() -{ - unsigned int i; - for (i=0; i < constructedReplicaList.Size(); i++) - RakNet::OP_DELETE(constructedReplicaList[i], _FILE_AND_LINE_); - for (i=0; i < queryToConstructReplicaList.Size(); i++) - RakNet::OP_DELETE(queryToConstructReplicaList[i], _FILE_AND_LINE_); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::GetConstructedReplicas(DataStructures::List &objectsTheyDoHave) -{ - objectsTheyDoHave.Clear(true,_FILE_AND_LINE_); - for (unsigned int idx=0; idx < constructedReplicaList.Size(); idx++) - { - objectsTheyDoHave.Push(constructedReplicaList[idx]->replica, _FILE_AND_LINE_ ); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool Connection_RM3::HasReplicaConstructed(RakNet::Replica3 *replica) -{ - bool objectExists; - constructedReplicaList.GetIndexFromKey(replica, &objectExists); - return objectExists; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void Connection_RM3::SendSerializeHeader(RakNet::Replica3 *replica, RakNet::Time timestamp, RakNet::BitStream *bs, unsigned char worldId) -{ - bs->Reset(); - - if (timestamp!=0) - { - bs->Write((MessageID)ID_TIMESTAMP); - bs->Write(timestamp); - } - bs->Write((MessageID)ID_REPLICA_MANAGER_SERIALIZE); - bs->Write(worldId); - bs->Write(replica->GetNetworkID()); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void Connection_RM3::ClearDownloadGroup(RakPeerInterface *rakPeerInterface) -{ - unsigned int i; - for (i=0; i < downloadGroup.Size(); i++) - rakPeerInterface->DeallocatePacket(downloadGroup[i]); - downloadGroup.Clear(__FILE__,__LINE__); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SendSerializeIfChangedResult Connection_RM3::SendSerialize(RakNet::Replica3 *replica, bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::BitStream serializationData[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::Time timestamp, PRO sendParameters[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakPeerInterface *rakPeer, unsigned char worldId) -{ - bool channelHasData; - BitSize_t sum=0; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - if (indicesToSend[z]) - sum+=serializationData[z].GetNumberOfBitsUsed(); - } - if (sum==0) - return SSICR_DID_NOT_SEND_DATA; - - RakAssert(replica->GetNetworkID()!=UNASSIGNED_NETWORK_ID); - - RakNet::BitStream out; - BitSize_t bitsUsed; - - int channelIndex; - PRO lastPro=sendParameters[0]; - - for (channelIndex=0; channelIndex < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex++) - { - if (channelIndex==0) - { - SendSerializeHeader(replica, timestamp, &out, worldId); - } - else if (lastPro!=sendParameters[channelIndex]) - { - // Write out remainder - for (int channelIndex2=channelIndex; channelIndex2 < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex2++) - out.Write(false); - - // Send remainder - replica->OnSerializeTransmission(&out, systemAddress); - rakPeer->Send(&out,lastPro.priority,lastPro.reliability,lastPro.orderingChannel,systemAddress,false,lastPro.sendReceipt); - - // If no data left to send, quit out - bool anyData=false; - for (int channelIndex2=channelIndex; channelIndex2 < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex2++) - { - if (serializationData[channelIndex2].GetNumberOfBitsUsed()>0) - { - anyData=true; - break; - } - } - if (anyData==false) - return SSICR_SENT_DATA; - - // Restart stream - SendSerializeHeader(replica, timestamp, &out, worldId); - - for (int channelIndex2=0; channelIndex2 < channelIndex; channelIndex2++) - out.Write(false); - lastPro=sendParameters[channelIndex]; - } - - bitsUsed=serializationData[channelIndex].GetNumberOfBitsUsed(); - channelHasData = indicesToSend[channelIndex]==true && bitsUsed>0; - out.Write(channelHasData); - if (channelHasData) - { - out.WriteCompressed(bitsUsed); - out.AlignWriteToByteBoundary(); - out.Write(serializationData[channelIndex]); - // Crap, forgot this line, was a huge bug in that I'd only send to the first 3 systems - serializationData[channelIndex].ResetReadPointer(); - } - } - replica->OnSerializeTransmission(&out, systemAddress); - rakPeer->Send(&out,lastPro.priority,lastPro.reliability,lastPro.orderingChannel,systemAddress,false,lastPro.sendReceipt); - return SSICR_SENT_DATA; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -SendSerializeIfChangedResult Connection_RM3::SendSerializeIfChanged(unsigned int queryToSerializeIndex, SerializeParameters *sp, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager) -{ - RakNet::Replica3 *replica = queryToSerializeReplicaList[queryToSerializeIndex]->replica; - - if (replica->GetNetworkID()==UNASSIGNED_NETWORK_ID) - return SSICR_DID_NOT_SEND_DATA; - - RM3QuerySerializationResult rm3qsr = replica->QuerySerialization(this); - if (rm3qsr==RM3QSR_NEVER_CALL_SERIALIZE) - { - // Never again for this connection and replica pair - OnNeverSerialize(queryToSerializeIndex, replicaManager); - return SSICR_NEVER_SERIALIZE; - } - - if (rm3qsr==RM3QSR_DO_NOT_CALL_SERIALIZE) - return SSICR_DID_NOT_SEND_DATA; - - if (replica->forceSendUntilNextUpdate) - { - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - if (replica->lastSentSerialization.indicesToSend[z]) - sp->bitsWrittenSoFar+=replica->lastSentSerialization.bitStream[z].GetNumberOfBitsUsed(); - } - return SendSerialize(replica, replica->lastSentSerialization.indicesToSend, replica->lastSentSerialization.bitStream, sp->messageTimestamp, sp->pro, rakPeer, worldId); - } - - for (int i=0; i < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; i++) - { - sp->outputBitstream[i].Reset(); - if (queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS) - sp->lastSentBitstream[i]=&queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[i]; - else - sp->lastSentBitstream[i]=&replica->lastSentSerialization.bitStream[i]; - } - - RM3SerializationResult serializationResult = replica->Serialize(sp); - - if (serializationResult==RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION) - { - // Never again for this connection and replica pair - OnNeverSerialize(queryToSerializeIndex, replicaManager); - return SSICR_NEVER_SERIALIZE; - } - - if (serializationResult==RM3SR_DO_NOT_SERIALIZE) - { - // Don't serialize this tick only - return SSICR_DID_NOT_SEND_DATA; - } - - // This is necessary in case the user in the Serialize() function for some reason read the bitstream they also wrote - // WIthout this code, the Write calls to another bitstream would not write the entire bitstream - BitSize_t sum=0; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp->outputBitstream[z].ResetReadPointer(); - sum+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - } - - if (sum==0) - { - // Don't serialize this tick only - return SSICR_DID_NOT_SEND_DATA; - } - - if (serializationResult==RM3SR_SERIALIZED_ALWAYS) - { - bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - allIndices[z]=true; - - queryToSerializeReplicaList[queryToSerializeIndex]->AllocBS(); - queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Reset(); - queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Write(&sp->outputBitstream[z]); - sp->outputBitstream[z].ResetReadPointer(); - } - return SendSerialize(replica, allIndices, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); - } - - if (serializationResult==RM3SR_SERIALIZED_ALWAYS_IDENTICALLY) - { - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - replica->lastSentSerialization.indicesToSend[z]=sp->outputBitstream[z].GetNumberOfBitsUsed()>0; - sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - replica->lastSentSerialization.bitStream[z].Reset(); - replica->lastSentSerialization.bitStream[z].Write(&sp->outputBitstream[z]); - sp->outputBitstream[z].ResetReadPointer(); - replica->forceSendUntilNextUpdate=true; - } - return SendSerialize(replica, replica->lastSentSerialization.indicesToSend, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); - } - - bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - if (serializationResult==RM3SR_BROADCAST_IDENTICALLY || serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION) - { - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - if (sp->outputBitstream[z].GetNumberOfBitsUsed() > 0 && - (serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION || - ((sp->outputBitstream[z].GetNumberOfBitsUsed()!=replica->lastSentSerialization.bitStream[z].GetNumberOfBitsUsed() || - memcmp(sp->outputBitstream[z].GetData(), replica->lastSentSerialization.bitStream[z].GetData(), sp->outputBitstream[z].GetNumberOfBytesUsed())!=0)))) - { - indicesToSend[z]=true; - replica->lastSentSerialization.indicesToSend[z]=true; - sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - replica->lastSentSerialization.bitStream[z].Reset(); - replica->lastSentSerialization.bitStream[z].Write(&sp->outputBitstream[z]); - sp->outputBitstream[z].ResetReadPointer(); - replica->forceSendUntilNextUpdate=true; - } - else - { - indicesToSend[z]=false; - replica->lastSentSerialization.indicesToSend[z]=false; - } - } - } - else - { - queryToSerializeReplicaList[queryToSerializeIndex]->AllocBS(); - - // RM3SR_SERIALIZED_UNIQUELY - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - if (sp->outputBitstream[z].GetNumberOfBitsUsed() > 0 && - (sp->outputBitstream[z].GetNumberOfBitsUsed()!=queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].GetNumberOfBitsUsed() || - memcmp(sp->outputBitstream[z].GetData(), queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].GetData(), sp->outputBitstream[z].GetNumberOfBytesUsed())!=0) - ) - { - indicesToSend[z]=true; - sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Reset(); - queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Write(&sp->outputBitstream[z]); - sp->outputBitstream[z].ResetReadPointer(); - } - else - { - indicesToSend[z]=false; - } - } - } - - - if (serializationResult==RM3SR_BROADCAST_IDENTICALLY || serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION) - replica->forceSendUntilNextUpdate=true; - - // Send out the data - return SendSerialize(replica, indicesToSend, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void Connection_RM3::OnLocalReference(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) replicaManager; - (void) constructionMode; - - LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); - lsr->replica=replica3; - queryToConstructReplicaList.Push(lsr,_FILE_AND_LINE_); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDereference(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - - LastSerializationResult* lsr=0; - unsigned int idx; - - bool objectExists; - idx=constructedReplicaList.GetIndexFromKey(replica3, &objectExists); - if (objectExists) - { - lsr=constructedReplicaList[idx]; - constructedReplicaList.RemoveAtIndex(idx); - } - - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - if (queryToConstructReplicaList[idx]->replica==replica3) - { - lsr=queryToConstructReplicaList[idx]; - queryToConstructReplicaList.RemoveAtIndex(idx); - break; - } - } - - for (idx=0; idx < queryToSerializeReplicaList.Size(); idx++) - { - if (queryToSerializeReplicaList[idx]->replica==replica3) - { - lsr=queryToSerializeReplicaList[idx]; - queryToSerializeReplicaList.RemoveAtIndex(idx); - break; - } - } - - for (idx=0; idx < queryToDestructReplicaList.Size(); idx++) - { - if (queryToDestructReplicaList[idx]->replica==replica3) - { - lsr=queryToDestructReplicaList[idx]; - queryToDestructReplicaList.RemoveAtIndex(idx); - break; - } - } - - ValidateLists(replicaManager); - - if (lsr) - RakNet::OP_DELETE(lsr,_FILE_AND_LINE_); - - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDownloadFromThisSystem(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); - lsr->replica=replica3; - - ConstructionMode constructionMode = QueryConstructionMode(); - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - unsigned int j; - for (j=0; j < queryToConstructReplicaList.Size(); j++) - { - if (queryToConstructReplicaList[j]->replica->GetNetworkID()==replica3->GetNetworkID() ) - { - queryToConstructReplicaList.RemoveAtIndex(j); - break; - } - } - - queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); - } - - constructedReplicaList.Insert(lsr->replica, lsr, true, _FILE_AND_LINE_); - //assert(queryToSerializeReplicaList.GetIndexOf(replica3)==(unsigned int)-1); - queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); - - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDownloadFromOtherSystem(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - unsigned int j; - for (j=0; j < queryToConstructReplicaList.Size(); j++) - { - if (queryToConstructReplicaList[j]->replica->GetNetworkID()==replica3->GetNetworkID() ) - { - return; - } - } - - OnLocalReference(replica3, replicaManager); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnNeverConstruct(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) constructionMode; - - ValidateLists(replicaManager); - LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; - queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); - RakNet::OP_DELETE(lsr,_FILE_AND_LINE_); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnConstructToThisConnection(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) constructionMode; - - ValidateLists(replicaManager); - LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; - queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); - //assert(constructedReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - constructedReplicaList.Insert(lsr->replica,lsr,true,_FILE_AND_LINE_); - //assert(queryToDestructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); - //assert(queryToSerializeReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnConstructToThisConnection(Replica3 *replica, ReplicaManager3 *replicaManager) -{ - RakAssert(QueryConstructionMode()==QUERY_CONNECTION_FOR_REPLICA_LIST); - (void) replicaManager; - - LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); - lsr->replica=replica; - constructedReplicaList.Insert(replica,lsr,true,_FILE_AND_LINE_); - queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnNeverSerialize(unsigned int queryToSerializeIndex, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - queryToSerializeReplicaList.RemoveAtIndex(queryToSerializeIndex); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnReplicaAlreadyExists(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) constructionMode; - - ValidateLists(replicaManager); - LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; - queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); - //assert(constructedReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - constructedReplicaList.Insert(lsr->replica,lsr,true,_FILE_AND_LINE_); - //assert(queryToDestructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); - //assert(queryToSerializeReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDownloadExisting(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - - ConstructionMode constructionMode = QueryConstructionMode(); - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - unsigned int idx; - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - if (queryToConstructReplicaList[idx]->replica==replica3) - { - OnConstructToThisConnection(idx, replicaManager); - return; - } - } - } - else - { - OnConstructToThisConnection(replica3, replicaManager); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnSendDestructionFromQuery(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) constructionMode; - - ValidateLists(replicaManager); - LastSerializationResult* lsr = queryToDestructReplicaList[queryToDestructIdx]; - queryToDestructReplicaList.RemoveAtIndex(queryToDestructIdx); - unsigned int j; - for (j=0; j < queryToSerializeReplicaList.Size(); j++) - { - if (queryToSerializeReplicaList[j]->replica->GetNetworkID()==lsr->replica->GetNetworkID() ) - { - queryToSerializeReplicaList.RemoveAtIndex(j); - break; - } - } - for (j=0; j < constructedReplicaList.Size(); j++) - { - if (constructedReplicaList[j]->replica->GetNetworkID()==lsr->replica->GetNetworkID() ) - { - constructedReplicaList.RemoveAtIndex(j); - break; - } - } - //assert(queryToConstructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToConstructReplicaList.Push(lsr,_FILE_AND_LINE_); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDoNotQueryDestruction(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - queryToDestructReplicaList.RemoveAtIndex(queryToDestructIdx); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::ValidateLists(ReplicaManager3 *replicaManager) const -{ - (void) replicaManager; - /* -#ifdef _DEBUG - // Each object should exist only once in either constructedReplicaList or queryToConstructReplicaList - // replicaPointer from LastSerializationResult should be same among all lists - unsigned int idx, idx2; - for (idx=0; idx < constructedReplicaList.Size(); idx++) - { - idx2=queryToConstructReplicaList.GetIndexOf(constructedReplicaList[idx]->replica); - if (idx2!=(unsigned int)-1) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - idx2=constructedReplicaList.GetIndexOf(queryToConstructReplicaList[idx]->replica); - if (idx2!=(unsigned int)-1) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - LastSerializationResult *lsr, *lsr2; - for (idx=0; idx < constructedReplicaList.Size(); idx++) - { - lsr=constructedReplicaList[idx]; - - idx2=queryToSerializeReplicaList.GetIndexOf(lsr->replica); - if (idx2!=(unsigned int)-1) - { - lsr2=queryToSerializeReplicaList[idx2]; - if (lsr2!=lsr) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - idx2=queryToDestructReplicaList.GetIndexOf(lsr->replica); - if (idx2!=(unsigned int)-1) - { - lsr2=queryToDestructReplicaList[idx2]; - if (lsr2!=lsr) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - } - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - lsr=queryToConstructReplicaList[idx]; - - idx2=queryToSerializeReplicaList.GetIndexOf(lsr->replica); - if (idx2!=(unsigned int)-1) - { - lsr2=queryToSerializeReplicaList[idx2]; - if (lsr2!=lsr) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - idx2=queryToDestructReplicaList.GetIndexOf(lsr->replica); - if (idx2!=(unsigned int)-1) - { - lsr2=queryToDestructReplicaList[idx2]; - if (lsr2!=lsr) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - } - - // Verify pointer integrity - for (idx=0; idx < constructedReplicaList.Size(); idx++) - { - if (constructedReplicaList[idx]->replica->replicaManager!=replicaManager) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - // Verify pointer integrity - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - if (queryToConstructReplicaList[idx]->replica->replicaManager!=replicaManager) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } -#endif - */ -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::SendConstruction(DataStructures::List &newObjects, DataStructures::List &deletedObjects, PRO sendParameters, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager3) -{ - if (newObjects.Size()==0 && deletedObjects.Size()==0) - return; - - // All construction and destruction takes place in the same network message - // Otherwise, if objects rely on each other being created the same tick to be valid, this won't always be true - // DataStructures::List serializedObjects; - BitSize_t offsetStart, offsetStart2, offsetEnd; - unsigned int newListIndex, oldListIndex; - RakNet::BitStream bsOut; - NetworkID networkId; - if (isFirstConstruction) - { - bsOut.Write((MessageID)ID_REPLICA_MANAGER_DOWNLOAD_STARTED); - bsOut.Write(worldId); - SerializeOnDownloadStarted(&bsOut); - rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); - } - - // LastSerializationResult* lsr; - bsOut.Reset(); - bsOut.Write((MessageID)ID_REPLICA_MANAGER_CONSTRUCTION); - bsOut.Write(worldId); - uint16_t objectSize = (uint16_t) newObjects.Size(); - bsOut.Write(objectSize); - - // Construction - for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) - { - offsetStart=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart); // overwritten to point to the end of the stream - networkId=newObjects[newListIndex]->GetNetworkID(); - bsOut.Write(networkId); - - RM3ConstructionState cs = newObjects[newListIndex]->QueryConstruction(this, replicaManager3); - bool actuallyCreateObject = cs==RM3CS_SEND_CONSTRUCTION; - bsOut.Write(actuallyCreateObject); - bsOut.AlignWriteToByteBoundary(); - - if (actuallyCreateObject) - { - // Actually create the object - bsOut.Write(newObjects[newListIndex]->creatingSystemGUID); - offsetStart2=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart2); // overwritten to point to after the call to WriteAllocationID - bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy - newObjects[newListIndex]->WriteAllocationID(this, &bsOut); - bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart2); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - newObjects[newListIndex]->SerializeConstruction(&bsOut, this); - } - else - { - newObjects[newListIndex]->SerializeConstructionExisting(&bsOut, this); - } - - bsOut.AlignWriteToByteBoundary(); - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - } - - RakNet::BitStream bsOut2; - for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) - { - bsOut2.Reset(); - RM3ConstructionState cs = newObjects[newListIndex]->QueryConstruction(this, replicaManager3); - if (cs==RM3CS_SEND_CONSTRUCTION) - { - newObjects[newListIndex]->PostSerializeConstruction(&bsOut2, this); - } - else - { - RakAssert(cs==RM3CS_ALREADY_EXISTS_REMOTELY); - newObjects[newListIndex]->PostSerializeConstructionExisting(&bsOut2, this); - } - if (bsOut2.GetNumberOfBitsUsed()>0) - { - bsOut.Write(true); - bsOut.AlignWriteToByteBoundary(); - offsetStart=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart); // overwritten to point to the end of the stream - networkId=newObjects[newListIndex]->GetNetworkID(); - bsOut.Write(networkId); - bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy - bsOut.Write(&bsOut2); - bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - } - else - bsOut.Write(false); - } - bsOut.AlignWriteToByteBoundary(); - - // Destruction - objectSize = (uint16_t) deletedObjects.Size(); - bsOut.Write(objectSize); - for (oldListIndex=0; oldListIndex < deletedObjects.Size(); oldListIndex++) - { - networkId=deletedObjects[oldListIndex]->GetNetworkID(); - bsOut.Write(networkId); - offsetStart=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart); - deletedObjects[oldListIndex]->deletingSystemGUID=rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - bsOut.Write(deletedObjects[oldListIndex]->deletingSystemGUID); - deletedObjects[oldListIndex]->SerializeDestruction(&bsOut, this); - bsOut.AlignWriteToByteBoundary(); - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - } - rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); - - // TODO - shouldn't this be part of construction? - - // Initial Download serialize to a new system - // Immediately send serialize after construction if the replica object already has saved data - // If the object was serialized identically, and does not change later on, then the new connection never gets the data - SerializeParameters sp; - sp.whenLastSerialized=0; - RakNet::BitStream emptyBs; - for (int index=0; index < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; index++) - { - sp.lastSentBitstream[index]=&emptyBs; - sp.pro[index]=sendParameters; - sp.pro[index].reliability=RELIABLE_ORDERED; - } - - sp.bitsWrittenSoFar=0; - RakNet::Time t = RakNet::GetTimeMS(); - for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) - { - sp.destinationConnection=this; - sp.messageTimestamp=0; - RakNet::Replica3 *replica = newObjects[newListIndex]; - // 8/22/09 Forgot ResetWritePointer - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp.outputBitstream[z].ResetWritePointer(); - } - - RM3SerializationResult res = replica->Serialize(&sp); - if (res!=RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION && - res!=RM3SR_DO_NOT_SERIALIZE && - res!=RM3SR_SERIALIZED_UNIQUELY) - { - bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp.bitsWrittenSoFar+=sp.outputBitstream[z].GetNumberOfBitsUsed(); - allIndices[z]=true; - } - SendSerialize(replica, allIndices, sp.outputBitstream, sp.messageTimestamp, sp.pro, rakPeer, worldId); - newObjects[newListIndex]->whenLastSerialized=t; - - } - // else wait for construction request accepted before serializing - } - - if (isFirstConstruction) - { - bsOut.Reset(); - bsOut.Write((MessageID)ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE); - bsOut.Write(worldId); - SerializeOnDownloadComplete(&bsOut); - rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); - } - - isFirstConstruction=false; - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::SendValidation(RakNet::RakPeerInterface *rakPeer, unsigned char worldId) -{ - // Hijack to mean sendValidation - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_REPLICA_MANAGER_SCOPE_CHANGE); - bsOut.Write(worldId); - rakPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Replica3::Replica3() -{ - creatingSystemGUID=UNASSIGNED_RAKNET_GUID; - deletingSystemGUID=UNASSIGNED_RAKNET_GUID; - replicaManager=0; - forceSendUntilNextUpdate=false; - whenLastSerialized=0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Replica3::~Replica3() -{ - if (replicaManager) - { - replicaManager->Dereference(this); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Replica3::BroadcastDestruction(void) -{ - replicaManager->BroadcastDestruction(this,UNASSIGNED_SYSTEM_ADDRESS); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RakNetGUID Replica3::GetCreatingSystemGUID(void) const -{ - return creatingSystemGUID; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ConstructionState Replica3::QueryConstruction_ClientConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) -{ - (void) destinationConnection; - if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return RM3CS_SEND_CONSTRUCTION; - // Send back to the owner client too, because they couldn't assign the network ID - if (isThisTheServer) - return RM3CS_SEND_CONSTRUCTION; - return RM3CS_NEVER_CONSTRUCT; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool Replica3::QueryRemoteConstruction_ClientConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer) -{ - (void) sourceConnection; - (void) isThisTheServer; - - // OK to create - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ConstructionState Replica3::QueryConstruction_ServerConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) -{ - (void) destinationConnection; - - if (isThisTheServer) - return RM3CS_SEND_CONSTRUCTION; - return RM3CS_NEVER_CONSTRUCT; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool Replica3::QueryRemoteConstruction_ServerConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer) -{ - (void) sourceConnection; - if (isThisTheServer) - return false; - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ConstructionState Replica3::QueryConstruction_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode) -{ - (void) destinationConnection; - - if (p2pMode==R3P2PM_SINGLE_OWNER) - { - // We send to all, others do nothing - if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return RM3CS_SEND_CONSTRUCTION; - - // RM3CS_NEVER_CONSTRUCT will not send the object, and will not Serialize() it - return RM3CS_NEVER_CONSTRUCT; - } - else if (p2pMode==R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE) - { - return RM3CS_SEND_CONSTRUCTION; - } - else - { - RakAssert(p2pMode==R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE); - - // RM3CS_ALREADY_EXISTS_REMOTELY will not send the object, but WILL call QuerySerialization() and Serialize() on it. - return RM3CS_ALREADY_EXISTS_REMOTELY; - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool Replica3::QueryRemoteConstruction_PeerToPeer(RakNet::Connection_RM3 *sourceConnection) -{ - (void) sourceConnection; - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3QuerySerializationResult Replica3::QuerySerialization_ClientSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) -{ - // Owner client sends to all - if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return RM3QSR_CALL_SERIALIZE; - // Server sends to all but owner client - if (isThisTheServer && destinationConnection->GetRakNetGUID()!=creatingSystemGUID) - return RM3QSR_CALL_SERIALIZE; - // Remote clients do not send - return RM3QSR_NEVER_CALL_SERIALIZE; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3QuerySerializationResult Replica3::QuerySerialization_ServerSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) -{ - (void) destinationConnection; - // Server sends to all - if (isThisTheServer) - return RM3QSR_CALL_SERIALIZE; - - // Clients do not send - return RM3QSR_NEVER_CALL_SERIALIZE; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3QuerySerializationResult Replica3::QuerySerialization_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode) -{ - (void) destinationConnection; - - if (p2pMode==R3P2PM_SINGLE_OWNER) - { - // Owner peer sends to all - if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return RM3QSR_CALL_SERIALIZE; - - // Remote peers do not send - return RM3QSR_NEVER_CALL_SERIALIZE; - } - else if (p2pMode==R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE) - { - return RM3QSR_CALL_SERIALIZE; - } - else - { - RakAssert(p2pMode==R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE); - return RM3QSR_DO_NOT_CALL_SERIALIZE; - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_Client(RakNet::Connection_RM3 *droppedConnection) const -{ - (void) droppedConnection; - return RM3AOPC_DELETE_REPLICA; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_Server(RakNet::Connection_RM3 *droppedConnection) const -{ - (void) droppedConnection; - return RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_PeerToPeer(RakNet::Connection_RM3 *droppedConnection) const -{ - (void) droppedConnection; - return RM3AOPC_DELETE_REPLICA; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ReplicaManager3==1 + +#include "ReplicaManager3.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "NetworkIDManager.h" + +using namespace RakNet; + +// DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS(LastSerializationResult,Replica3*,replica); + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool PRO::operator==( const PRO& right ) const +{ + return priority == right.priority && reliability == right.reliability && orderingChannel == right.orderingChannel && sendReceipt == right.sendReceipt; +} + +bool PRO::operator!=( const PRO& right ) const +{ + return priority != right.priority || reliability != right.reliability || orderingChannel != right.orderingChannel || sendReceipt != right.sendReceipt; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +int Connection_RM3::Replica3LSRComp( Replica3 * const &replica3, LastSerializationResult * const &data ) +{ + if (replica3->GetNetworkID() < data->replica->GetNetworkID()) + return -1; + if (replica3->GetNetworkID() > data->replica->GetNetworkID()) + return 1; + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +LastSerializationResult::LastSerializationResult() +{ + replica=0; + lastSerializationResultBS=0; +} +LastSerializationResult::~LastSerializationResult() +{ + if (lastSerializationResultBS) + RakNet::OP_DELETE(lastSerializationResultBS,_FILE_AND_LINE_); +} +void LastSerializationResult::AllocBS(void) +{ + if (lastSerializationResultBS==0) + { + lastSerializationResultBS=RakNet::OP_NEW(_FILE_AND_LINE_); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ReplicaManager3::ReplicaManager3() +{ + defaultSendParameters.orderingChannel=0; + defaultSendParameters.priority=HIGH_PRIORITY; + defaultSendParameters.reliability=RELIABLE_ORDERED; + defaultSendParameters.sendReceipt=0; + autoSerializeInterval=30; + lastAutoSerializeOccurance=0; + worldId=0; + autoCreateConnections=true; + autoDestroyConnections=true; + networkIDManager=0; + currentlyDeallocatingReplica=0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ReplicaManager3::~ReplicaManager3() +{ + if (autoDestroyConnections) + { + // Clear() calls DeallocConnection(), which is pure virtual and cannot be called from the destructor + RakAssert(connectionList.Size()==0); + } + Clear(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetAutoManageConnections(bool autoCreate, bool autoDestroy) +{ + autoCreateConnections=autoCreate; + autoDestroyConnections=autoDestroy; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool ReplicaManager3::PushConnection(RakNet::Connection_RM3 *newConnection) +{ + if (newConnection==0) + return false; + if (GetConnectionByGUID(newConnection->GetRakNetGUID())) + return false; + unsigned int index = connectionList.GetIndexOf(newConnection); + if (index==(unsigned int)-1) + { + connectionList.Push(newConnection,_FILE_AND_LINE_); + + // Send message to validate the connection + newConnection->SendValidation(rakPeerInterface, worldId); + + Connection_RM3::ConstructionMode constructionMode = newConnection->QueryConstructionMode(); + if (constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + unsigned int pushIdx; + for (pushIdx=0; pushIdx < userReplicaList.Size(); pushIdx++) + newConnection->OnLocalReference(userReplicaList[pushIdx], this); + } + } + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::DeallocReplicaNoBroadcastDestruction(RakNet::Connection_RM3 *connection, RakNet::Replica3 *replica3) +{ + currentlyDeallocatingReplica=replica3; + replica3->DeallocReplica(connection); + currentlyDeallocatingReplica=0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RakNet::Connection_RM3 * ReplicaManager3::PopConnection(unsigned int index) +{ + DataStructures::List replicaList; + DataStructures::List destructionList; + DataStructures::List broadcastList; + RakNet::Connection_RM3 *connection; + unsigned int index2; + RM3ActionOnPopConnection action; + + connection=connectionList[index]; + + // Clear out downloadGroup + connection->ClearDownloadGroup(rakPeerInterface); + + RakNetGUID guid = connection->GetRakNetGUID(); + // This might be wrong, I am relying on the variable creatingSystemGuid which is transmitted + // automatically from the first system to reference the object. However, if an object changes + // owners then it is not going to be returned here, and therefore QueryActionOnPopConnection() + // will not be called for the new owner. + GetReplicasCreatedByGuid(guid, replicaList); + + for (index2=0; index2 < replicaList.Size(); index2++) + { + action = replicaList[index2]->QueryActionOnPopConnection(connection); + replicaList[index2]->OnPoppedConnection(connection); + if (action==RM3AOPC_DELETE_REPLICA) + { + destructionList.Push( replicaList[index2], _FILE_AND_LINE_ ); + } + else if (action==RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION) + { + destructionList.Push( replicaList[index2], _FILE_AND_LINE_ ); + + broadcastList.Push( replicaList[index2], _FILE_AND_LINE_ ); + } + } + + BroadcastDestructionList(broadcastList, connection->GetSystemAddress()); + for (index2=0; index2 < destructionList.Size(); index2++) + { + destructionList[index2]->PreDestruction(connection); + destructionList[index2]->DeallocReplica(connection); + } + + connectionList.RemoveAtIndex(index); + return connection; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RakNet::Connection_RM3 * ReplicaManager3::PopConnection(RakNetGUID guid) +{ + unsigned int index; + + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->GetRakNetGUID()==guid) + { + return PopConnection(index); + } + } + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::Reference(RakNet::Replica3 *replica3) +{ + unsigned int index = ReferenceInternal(replica3); + + if (index!=(unsigned int)-1) + { + unsigned int pushIdx; + for (pushIdx=0; pushIdx < connectionList.Size(); pushIdx++) + { + Connection_RM3::ConstructionMode constructionMode = connectionList[pushIdx]->QueryConstructionMode(); + if (constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + connectionList[pushIdx]->OnLocalReference(replica3, this); + } + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +unsigned int ReplicaManager3::ReferenceInternal(RakNet::Replica3 *replica3) +{ + unsigned int index; + index = userReplicaList.GetIndexOf(replica3); + if (index==(unsigned int)-1) + { + RakAssert(networkIDManager); + replica3->SetNetworkIDManager(networkIDManager); + if (replica3->creatingSystemGUID==UNASSIGNED_RAKNET_GUID) + replica3->creatingSystemGUID=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + replica3->replicaManager=this; + userReplicaList.Push(replica3,_FILE_AND_LINE_); + index=userReplicaList.Size()-1; + } + return index; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::Dereference(RakNet::Replica3 *replica3) +{ + unsigned int index, index2; + for (index=0; index < userReplicaList.Size(); index++) + { + if (userReplicaList[index]==replica3) + { + userReplicaList.RemoveAtIndex(index); + break; + } + } + + // Remove from all connections + for (index2=0; index2 < connectionList.Size(); index2++) + { + connectionList[index2]->OnDereference(replica3, this); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::DereferenceList(DataStructures::List &replicaListIn) +{ + unsigned int index; + for (index=0; index < replicaListIn.Size(); index++) + Dereference(replicaListIn[index]); +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::GetReplicasCreatedByMe(DataStructures::List &replicaListOut) +{ + RakNetGUID myGuid = rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + GetReplicasCreatedByGuid(rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS), replicaListOut); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::GetReferencedReplicaList(DataStructures::List &replicaListOut) +{ + replicaListOut=userReplicaList; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::GetReplicasCreatedByGuid(RakNetGUID guid, DataStructures::List &replicaListOut) +{ + replicaListOut.Clear(false,_FILE_AND_LINE_); + unsigned int index; + for (index=0; index < userReplicaList.Size(); index++) + { + if (userReplicaList[index]->creatingSystemGUID==guid) + replicaListOut.Push(userReplicaList[index],_FILE_AND_LINE_); + } +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +unsigned ReplicaManager3::GetReplicaCount(void) const +{ + return userReplicaList.Size(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Replica3 *ReplicaManager3::GetReplicaAtIndex(unsigned index) +{ + return userReplicaList[index]; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +unsigned int ReplicaManager3::GetConnectionCount(void) const +{ + return connectionList.Size(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3* ReplicaManager3::GetConnectionAtIndex(unsigned index) const +{ + return connectionList[index]; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3* ReplicaManager3::GetConnectionBySystemAddress(const SystemAddress &sa) const +{ + unsigned int index; + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->GetSystemAddress()==sa) + { + return connectionList[index]; + } + } + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3* ReplicaManager3::GetConnectionByGUID(RakNetGUID guid) const +{ + unsigned int index; + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->GetRakNetGUID()==guid) + { + return connectionList[index]; + } + } + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetDefaultOrderingChannel(char def) +{ + defaultSendParameters.orderingChannel=def; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetDefaultPacketPriority(PacketPriority def) +{ + defaultSendParameters.priority=def; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetDefaultPacketReliability(PacketReliability def) +{ + defaultSendParameters.reliability=def; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetAutoSerializeInterval(RakNet::Time intervalMS) +{ + autoSerializeInterval=intervalMS; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::GetConnectionsThatHaveReplicaConstructed(Replica3 *replica, DataStructures::List &connectionsThatHaveConstructedThisReplica) +{ + connectionsThatHaveConstructedThisReplica.Clear(false,_FILE_AND_LINE_); + unsigned int index; + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->HasReplicaConstructed(replica)) + connectionsThatHaveConstructedThisReplica.Push(connectionList[index],_FILE_AND_LINE_); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::Clear(void) +{ + if (autoDestroyConnections) + { + for (unsigned int i=0; i < connectionList.Size(); i++) + DeallocConnection(connectionList[i]); + } + else + { + // Clear out downloadGroup even if not auto destroying the connection, since the packets need to go back to RakPeer + for (unsigned int i=0; i < connectionList.Size(); i++) + connectionList[i]->ClearDownloadGroup(rakPeerInterface); + } + + + + connectionList.Clear(true,_FILE_AND_LINE_); + userReplicaList.Clear(true,_FILE_AND_LINE_); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PRO ReplicaManager3::GetDefaultSendParameters(void) const +{ + return defaultSendParameters; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetWorldID(unsigned char id) +{ + worldId=id; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +unsigned char ReplicaManager3::GetWorldID(void) const +{ + return worldId; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +NetworkIDManager *ReplicaManager3::GetNetworkIDManager(void) const +{ + return networkIDManager; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetNetworkIDManager(NetworkIDManager *_networkIDManager) +{ + networkIDManager=_networkIDManager; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnReceive(Packet *packet) +{ + if (packet->length<2) + return RR_CONTINUE_PROCESSING; + + unsigned char incomingWorldId; + + RakNet::Time timestamp=0; + unsigned char packetIdentifier, packetDataOffset; + if ( ( unsigned char ) packet->data[ 0 ] == ID_TIMESTAMP ) + { + if ( packet->length > sizeof( unsigned char ) + sizeof( RakNet::Time ) ) + { + packetIdentifier = ( unsigned char ) packet->data[ sizeof( unsigned char ) + sizeof( RakNet::Time ) ]; + // Required for proper endian swapping + RakNet::BitStream tsBs(packet->data+sizeof(MessageID),packet->length-1,false); + tsBs.Read(timestamp); + incomingWorldId=packet->data[sizeof( unsigned char )*2 + sizeof( RakNet::Time )]; + packetDataOffset=sizeof( unsigned char )*3 + sizeof( RakNet::Time ); + } + else + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + else + { + packetIdentifier = ( unsigned char ) packet->data[ 0 ]; + incomingWorldId=packet->data[sizeof( unsigned char )]; + packetDataOffset=sizeof( unsigned char )*2; + } + + switch (packetIdentifier) + { + case ID_REPLICA_MANAGER_CONSTRUCTION: + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + return OnConstruction(packet, packet->data, packet->length, packet->guid, packetDataOffset); + case ID_REPLICA_MANAGER_SERIALIZE: + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + return OnSerialize(packet, packet->data, packet->length, packet->guid, timestamp, packetDataOffset); + case ID_REPLICA_MANAGER_DOWNLOAD_STARTED: + if (packet->wasGeneratedLocally==false) + { + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + return OnDownloadStarted(packet, packet->data, packet->length, packet->guid, packetDataOffset); + } + else + break; + case ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE: + if (packet->wasGeneratedLocally==false) + { + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + return OnDownloadComplete(packet, packet->data, packet->length, packet->guid, packetDataOffset); + } + else + break; + case ID_REPLICA_MANAGER_SCOPE_CHANGE: + { + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + + Connection_RM3 *connection = GetConnectionByGUID(packet->guid); + if (connection && connection->isValidated==false) + { + // This connection is now confirmed bidirectional + connection->isValidated=true; + // Reply back on validation + connection->SendValidation(rakPeerInterface,worldId); + } + } + } + + return RR_CONTINUE_PROCESSING; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::AutoConstructByQuery(ReplicaManager3 *replicaManager3) +{ + ValidateLists(replicaManager3); + + ConstructionMode constructionMode = QueryConstructionMode(); + + unsigned int index; + RM3ConstructionState constructionState; + LastSerializationResult *lsr; + index=0; + + constructedReplicasCulled.Clear(false,_FILE_AND_LINE_); + destroyedReplicasCulled.Clear(false,_FILE_AND_LINE_); + + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + while (index < queryToConstructReplicaList.Size()) + { + lsr=queryToConstructReplicaList[index]; + constructionState=lsr->replica->QueryConstruction(this, replicaManager3); + if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY || constructionState==RM3CS_ALREADY_EXISTS_REMOTELY_DO_NOT_CONSTRUCT) + { + OnReplicaAlreadyExists(index, replicaManager3); + if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY) + constructedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); + + /* + if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY) + { + // Serialize construction data to this connection + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_REPLICA_MANAGER_3_SERIALIZE_CONSTRUCTION_EXISTING); + bsOut.Write(replicaManager3->GetWorldID()); + NetworkID networkId; + networkId=lsr->replica->GetNetworkID(); + bsOut.Write(networkId); + BitSize_t bitsWritten = bsOut.GetNumberOfBitsUsed(); + lsr->replica->SerializeConstructionExisting(&bsOut, this); + if (bsOut.GetNumberOfBitsUsed()!=bitsWritten) + replicaManager3->SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,GetSystemAddress(), false); + } + + // Serialize first serialization to this connection. + // This is done here, as it isn't done in PushConstruction + SerializeParameters sp; + RakNet::BitStream emptyBs; + for (index=0; index < (unsigned int) RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; index++) + { + sp.lastSentBitstream[index]=&emptyBs; + sp.pro[index]=replicaManager3->GetDefaultSendParameters(); + } + sp.bitsWrittenSoFar=0; + sp.destinationConnection=this; + sp.messageTimestamp=0; + sp.whenLastSerialized=0; + + RakNet::Replica3 *replica = lsr->replica; + + RM3SerializationResult res = replica->Serialize(&sp); + if (res!=RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION && + res!=RM3SR_DO_NOT_SERIALIZE && + res!=RM3SR_SERIALIZED_UNIQUELY) + { + bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp.bitsWrittenSoFar+=sp.outputBitstream[z].GetNumberOfBitsUsed(); + allIndices[z]=true; + } + if (SendSerialize(replica, allIndices, sp.outputBitstream, sp.messageTimestamp, sp.pro, replicaManager3->GetRakPeerInterface(), replicaManager3->GetWorldID())==SSICR_SENT_DATA) + lsr->replica->whenLastSerialized=RakNet::GetTimeMS(); + } + */ + } + else if (constructionState==RM3CS_SEND_CONSTRUCTION) + { + OnConstructToThisConnection(index, replicaManager3); + constructedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); + } + else if (constructionState==RM3CS_NEVER_CONSTRUCT) + { + OnNeverConstruct(index, replicaManager3); + } + else// if (constructionState==RM3CS_NO_ACTION) + { + // Do nothing + index++; + } + } + + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + RM3DestructionState destructionState; + index=0; + while (index < queryToDestructReplicaList.Size()) + { + lsr=queryToDestructReplicaList[index]; + destructionState=lsr->replica->QueryDestruction(this, replicaManager3); + if (destructionState==RM3DS_SEND_DESTRUCTION) + { + OnSendDestructionFromQuery(index, replicaManager3); + destroyedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); + } + else if (destructionState==RM3DS_DO_NOT_QUERY_DESTRUCTION) + { + OnDoNotQueryDestruction(index, replicaManager3); + } + else// if (destructionState==RM3CS_NO_ACTION) + { + // Do nothing + index++; + } + } + } + } + else if (constructionMode==QUERY_CONNECTION_FOR_REPLICA_LIST) + { + QueryReplicaList(constructedReplicasCulled,destroyedReplicasCulled); + + unsigned int idx1, idx2; + + // Create new + for (idx2=0; idx2 < constructedReplicasCulled.Size(); idx2++) + OnConstructToThisConnection(constructedReplicasCulled[idx2], replicaManager3); + + bool exists; + for (idx2=0; idx2 < destroyedReplicasCulled.Size(); idx2++) + { + exists=false; + bool objectExists; + idx1=constructedReplicaList.GetIndexFromKey(destroyedReplicasCulled[idx2], &objectExists); + RakAssert(objectExists); + if (objectExists) + { + OnSendDestructionFromQuery(idx1,replicaManager3); + } + + // If this assert hits, the user tried to destroy a replica that doesn't exist on the remote system + RakAssert(exists); + } + } + + SendConstruction(constructedReplicasCulled,destroyedReplicasCulled,replicaManager3->defaultSendParameters,replicaManager3->rakPeerInterface,replicaManager3->worldId,replicaManager3); +} +void ReplicaManager3::Update(void) +{ + unsigned int index,index2; + + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->isValidated==false) + continue; + connectionList[index]->AutoConstructByQuery(this); + } + + RakNet::Time time = RakNet::GetTimeMS(); + + if (time - lastAutoSerializeOccurance >= autoSerializeInterval) + { + for (index=0; index < userReplicaList.Size(); index++) + { + userReplicaList[index]->forceSendUntilNextUpdate=false; + userReplicaList[index]->OnUserReplicaPreSerializeTick(); + } + + + unsigned int index; + SerializeParameters sp; + sp.curTime=time; + Connection_RM3 *connection; + SendSerializeIfChangedResult ssicr; + sp.messageTimestamp=0; + for (int i=0; i < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; i++) + sp.pro[i]=defaultSendParameters; + index2=0; + for (index=0; index < connectionList.Size(); index++) + { + connection = connectionList[index]; + sp.bitsWrittenSoFar=0; + index2=0; + while (index2 < connection->queryToSerializeReplicaList.Size()) + { + sp.destinationConnection=connection; + sp.whenLastSerialized=connection->queryToSerializeReplicaList[index2]->replica->whenLastSerialized; + ssicr=connection->SendSerializeIfChanged(index2, &sp, GetRakPeerInterface(), GetWorldID(), this); + if (ssicr==SSICR_SENT_DATA) + { + connection->queryToSerializeReplicaList[index2]->replica->whenLastSerialized=time; + index2++; + } + else if (ssicr==SSICR_NEVER_SERIALIZE) + { + // Removed from the middle of the list + } + else + index2++; + } + } + + lastAutoSerializeOccurance=time; + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + if (autoDestroyConnections) + { + Connection_RM3 *connection = PopConnection(rakNetGUID); + if (connection) + DeallocConnection(connection); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) isIncoming; + if (autoCreateConnections) + { + Connection_RM3 *connection = AllocConnection(systemAddress, rakNetGUID); + if (connection) + PushConnection(connection); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::OnRakPeerShutdown(void) +{ + if (autoDestroyConnections) + { + while (connectionList.Size()) + { + Connection_RM3 *connection = PopConnection(connectionList.Size()-1); + if (connection) + DeallocConnection(connection); + } + } + + Clear(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::OnDetach(void) +{ + OnRakPeerShutdown(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnConstruction(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) +{ + Connection_RM3 *connection = GetConnectionByGUID(senderGuid); + if (connection==0) + { + // Almost certainly a bug + RakAssert("Got OnConstruction but no connection yet" && 0); + return RR_CONTINUE_PROCESSING; + } + if (connection->groupConstructionAndSerialize) + { + connection->downloadGroup.Push(packet, __FILE__, __LINE__); + return RR_STOP_PROCESSING; + } + + RakNet::BitStream bsIn(packetData,packetDataLength,false); + bsIn.IgnoreBytes(packetDataOffset); + uint16_t constructionObjectListSize, destructionObjectListSize, index, index2; + BitSize_t streamEnd, writeAllocationIDEnd; + Replica3 *replica; + NetworkID networkId; + RakNetGUID creatingSystemGuid; + bool actuallyCreateObject=false; + + DataStructures::List actuallyCreateObjectList; + DataStructures::List constructionTickStack; + + RakAssert(networkIDManager); + + bsIn.Read(constructionObjectListSize); + for (index=0; index < constructionObjectListSize; index++) + { + bsIn.Read(streamEnd); + bsIn.Read(networkId); + Replica3* existingReplica = networkIDManager->GET_OBJECT_FROM_ID(networkId); + bsIn.Read(actuallyCreateObject); + actuallyCreateObjectList.Push(actuallyCreateObject, _FILE_AND_LINE_); + bsIn.AlignReadToByteBoundary(); + + if (actuallyCreateObject) + { + bsIn.Read(creatingSystemGuid); + bsIn.Read(writeAllocationIDEnd); + + //printf("OnConstruction: %i\n",networkId.guid.g); // Removeme + if (existingReplica) + { + existingReplica->replicaManager=this; + + // Network ID already in use + connection->OnDownloadExisting(existingReplica, this); + + constructionTickStack.Push(0, _FILE_AND_LINE_); + bsIn.SetReadOffset(streamEnd); + continue; + } + + bsIn.AlignReadToByteBoundary(); + replica = connection->AllocReplica(&bsIn, this); + if (replica==0) + { + constructionTickStack.Push(0, _FILE_AND_LINE_); + bsIn.SetReadOffset(streamEnd); + continue; + } + + // Go past the bitStream written to with WriteAllocationID(). Necessary in case the user didn't read out the bitStream the same way it was written + // bitOffset2 is already aligned + bsIn.SetReadOffset(writeAllocationIDEnd); + + replica->SetNetworkIDManager(networkIDManager); + replica->SetNetworkID(networkId); + + replica->replicaManager=this; + replica->creatingSystemGUID=creatingSystemGuid; + + if (!replica->QueryRemoteConstruction(connection) || + !replica->DeserializeConstruction(&bsIn, connection)) + { + DeallocReplicaNoBroadcastDestruction(connection, replica); + bsIn.SetReadOffset(streamEnd); + constructionTickStack.Push(0, _FILE_AND_LINE_); + continue; + } + + constructionTickStack.Push(replica, _FILE_AND_LINE_); + + // Register the replica + ReferenceInternal(replica); + } + else + { + if (existingReplica) + { + existingReplica->DeserializeConstructionExisting(&bsIn, connection); + constructionTickStack.Push(existingReplica, _FILE_AND_LINE_); + } + else + { + constructionTickStack.Push(0, _FILE_AND_LINE_); + } + } + + + bsIn.SetReadOffset(streamEnd); + bsIn.AlignReadToByteBoundary(); + } + + RakAssert(constructionTickStack.Size()==constructionObjectListSize); + RakAssert(actuallyCreateObjectList.Size()==constructionObjectListSize); + + RakNet::BitStream empty; + for (index=0; index < constructionObjectListSize; index++) + { + bool pdcWritten=false; + bsIn.Read(pdcWritten); + if (pdcWritten) + { + bsIn.AlignReadToByteBoundary(); + bsIn.Read(streamEnd); + bsIn.Read(networkId); + if (constructionTickStack[index]!=0) + { + bsIn.AlignReadToByteBoundary(); + if (actuallyCreateObjectList[index]) + constructionTickStack[index]->PostDeserializeConstruction(&bsIn, connection); + else + constructionTickStack[index]->PostDeserializeConstructionExisting(&bsIn, connection); + } + bsIn.SetReadOffset(streamEnd); + } + else + { + if (constructionTickStack[index]!=0) + constructionTickStack[index]->PostDeserializeConstruction(&empty, connection); + } + } + + for (index=0; index < constructionObjectListSize; index++) + { + if (constructionTickStack[index]!=0) + { + if (actuallyCreateObjectList[index]) + { + // Tell the connection(s) that this object exists since they just sent it to us + connection->OnDownloadFromThisSystem(constructionTickStack[index], this); + + for (index2=0; index2 < connectionList.Size(); index2++) + { + if (connectionList[index2]!=connection) + connectionList[index2]->OnDownloadFromOtherSystem(constructionTickStack[index], this); + } + } + } + } + + // Destructions + bool b = bsIn.Read(destructionObjectListSize); + (void) b; + RakAssert(b); + for (index=0; index < destructionObjectListSize; index++) + { + bsIn.Read(networkId); + bsIn.Read(streamEnd); + replica = networkIDManager->GET_OBJECT_FROM_ID(networkId); + if (replica==0) + { + // Unknown object + bsIn.SetReadOffset(streamEnd); + continue; + } + bsIn.Read(replica->deletingSystemGUID); + if (replica->DeserializeDestruction(&bsIn,connection)) + { + // Make sure it wasn't deleted in DeserializeDestruction + if (networkIDManager->GET_OBJECT_FROM_ID(networkId)) + { + replica->PreDestruction(connection); + + // Forward deletion by remote system + if (replica->QueryRelayDestruction(connection)) + BroadcastDestruction(replica,connection->GetSystemAddress()); + Dereference(replica); + DeallocReplicaNoBroadcastDestruction(connection, replica); + } + } + else + { + replica->PreDestruction(connection); + connection->OnDereference(replica, this); + } + + bsIn.AlignReadToByteBoundary(); + } + return RR_CONTINUE_PROCESSING; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnSerialize(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, RakNet::Time timestamp, unsigned char packetDataOffset) +{ + Connection_RM3 *connection = GetConnectionByGUID(senderGuid); + if (connection==0) + return RR_CONTINUE_PROCESSING; + if (connection->groupConstructionAndSerialize) + { + connection->downloadGroup.Push(packet, __FILE__, __LINE__); + return RR_STOP_PROCESSING; + } + RakAssert(networkIDManager); + RakNet::BitStream bsIn(packetData,packetDataLength,false); + bsIn.IgnoreBytes(packetDataOffset); + + struct DeserializeParameters ds; + ds.timeStamp=timestamp; + ds.sourceConnection=connection; + + Replica3 *replica; + NetworkID networkId; + BitSize_t bitsUsed; + bsIn.Read(networkId); + //printf("OnSerialize: %i\n",networkId.guid.g); // Removeme + replica = networkIDManager->GET_OBJECT_FROM_ID(networkId); + if (replica) + { + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + bsIn.Read(ds.bitstreamWrittenTo[z]); + if (ds.bitstreamWrittenTo[z]) + { + bsIn.ReadCompressed(bitsUsed); + bsIn.AlignReadToByteBoundary(); + bsIn.Read(ds.serializationBitstream[z], bitsUsed); + } + } + replica->Deserialize(&ds); + } + return RR_CONTINUE_PROCESSING; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnDownloadStarted(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) +{ + Connection_RM3 *connection = GetConnectionByGUID(senderGuid); + if (connection==0) + return RR_CONTINUE_PROCESSING; + if (connection->QueryGroupDownloadMessages() && + // ID_DOWNLOAD_STARTED will be processed twice, being processed the second time once ID_DOWNLOAD_COMPLETE arrives. + // However, the second time groupConstructionAndSerialize will be set to true so it won't be processed a third time + connection->groupConstructionAndSerialize==false + ) + { + // These messages will be held by the plugin and returned when the download is complete + connection->groupConstructionAndSerialize=true; + RakAssert(connection->downloadGroup.Size()==0); + connection->downloadGroup.Push(packet, __FILE__, __LINE__); + return RR_STOP_PROCESSING; + } + + connection->groupConstructionAndSerialize=false; + RakNet::BitStream bsIn(packetData,packetDataLength,false); + bsIn.IgnoreBytes(packetDataOffset); + connection->DeserializeOnDownloadStarted(&bsIn); + return RR_CONTINUE_PROCESSING; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnDownloadComplete(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) +{ + Connection_RM3 *connection = GetConnectionByGUID(senderGuid); + if (connection==0) + return RR_CONTINUE_PROCESSING; + + if (connection->groupConstructionAndSerialize==true && connection->downloadGroup.Size()>0) + { + // Push back buffered packets in front of this one + unsigned int i; + for (i=0; i < connection->downloadGroup.Size(); i++) + rakPeerInterface->PushBackPacket(connection->downloadGroup[i],false); + + // Push this one to be last too. It will be processed again, but the second time + // groupConstructionAndSerialize will be false and downloadGroup will be empty, so it will go past this block + connection->downloadGroup.Clear(__FILE__,__LINE__); + rakPeerInterface->PushBackPacket(packet,false); + + return RR_STOP_PROCESSING; + } + + RakNet::BitStream bsIn(packetData,packetDataLength,false); + bsIn.IgnoreBytes(packetDataOffset); + connection->DeserializeOnDownloadComplete(&bsIn); + return RR_CONTINUE_PROCESSING; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Replica3* ReplicaManager3::GetReplicaByNetworkID(NetworkID networkId) +{ + unsigned int i; + for (i=0; i < userReplicaList.Size(); i++) + { + if (userReplicaList[i]->GetNetworkID()==networkId) + return userReplicaList[i]; + } + return 0; +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +void ReplicaManager3::BroadcastDestructionList(DataStructures::List &replicaListSource, const SystemAddress &exclusionAddress) +{ + RakNet::BitStream bsOut; + unsigned int i,j; + + DataStructures::List replicaList; + + for (i=0; i < replicaListSource.Size(); i++) + { + if (replicaListSource[i]==currentlyDeallocatingReplica) + continue; + replicaList.Push(replicaListSource[i], __FILE__, __LINE__); + } + + if (replicaList.Size()==0) + return; + + for (i=0; i < replicaList.Size(); i++) + { + if (replicaList[i]->deletingSystemGUID==UNASSIGNED_RAKNET_GUID) + replicaList[i]->deletingSystemGUID=GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + } + + for (j=0; j < connectionList.Size(); j++) + { + if (connectionList[j]->GetSystemAddress()==exclusionAddress) + continue; + + bsOut.Reset(); + bsOut.Write((MessageID)ID_REPLICA_MANAGER_CONSTRUCTION); + bsOut.Write(worldId); + uint16_t cnt=0; + bsOut.Write(cnt); // No construction + cnt=(uint16_t) replicaList.Size(); + BitSize_t cntOffset=bsOut.GetWriteOffset();; + bsOut.Write(cnt); // Overwritten at send call + cnt=0; + + for (i=0; i < replicaList.Size(); i++) + { + if (connectionList[j]->HasReplicaConstructed(replicaList[i])==false) + continue; + cnt++; + + NetworkID networkId; + networkId=replicaList[i]->GetNetworkID(); + bsOut.Write(networkId); + BitSize_t offsetStart, offsetEnd; + offsetStart=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart); + bsOut.Write(replicaList[i]->deletingSystemGUID); + replicaList[i]->SerializeDestruction(&bsOut, connectionList[j]); + bsOut.AlignWriteToByteBoundary(); + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + } + + if (cnt>0) + { + BitSize_t curOffset=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(cntOffset); + bsOut.Write(cnt); + bsOut.SetWriteOffset(curOffset); + rakPeerInterface->Send(&bsOut,defaultSendParameters.priority,defaultSendParameters.reliability,defaultSendParameters.orderingChannel,connectionList[j]->GetSystemAddress(),false, defaultSendParameters.sendReceipt); + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +void ReplicaManager3::BroadcastDestruction(Replica3 *replica, const SystemAddress &exclusionAddress) +{ + DataStructures::List replicaList; + replicaList.Push(replica, _FILE_AND_LINE_ ); + BroadcastDestructionList(replicaList,exclusionAddress); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3::Connection_RM3(const SystemAddress &_systemAddress, RakNetGUID _guid) +: systemAddress(_systemAddress), guid(_guid) +{ + isValidated=false; + isFirstConstruction=true; + groupConstructionAndSerialize=false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3::~Connection_RM3() +{ + unsigned int i; + for (i=0; i < constructedReplicaList.Size(); i++) + RakNet::OP_DELETE(constructedReplicaList[i], _FILE_AND_LINE_); + for (i=0; i < queryToConstructReplicaList.Size(); i++) + RakNet::OP_DELETE(queryToConstructReplicaList[i], _FILE_AND_LINE_); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::GetConstructedReplicas(DataStructures::List &objectsTheyDoHave) +{ + objectsTheyDoHave.Clear(true,_FILE_AND_LINE_); + for (unsigned int idx=0; idx < constructedReplicaList.Size(); idx++) + { + objectsTheyDoHave.Push(constructedReplicaList[idx]->replica, _FILE_AND_LINE_ ); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool Connection_RM3::HasReplicaConstructed(RakNet::Replica3 *replica) +{ + bool objectExists; + constructedReplicaList.GetIndexFromKey(replica, &objectExists); + return objectExists; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void Connection_RM3::SendSerializeHeader(RakNet::Replica3 *replica, RakNet::Time timestamp, RakNet::BitStream *bs, unsigned char worldId) +{ + bs->Reset(); + + if (timestamp!=0) + { + bs->Write((MessageID)ID_TIMESTAMP); + bs->Write(timestamp); + } + bs->Write((MessageID)ID_REPLICA_MANAGER_SERIALIZE); + bs->Write(worldId); + bs->Write(replica->GetNetworkID()); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void Connection_RM3::ClearDownloadGroup(RakPeerInterface *rakPeerInterface) +{ + unsigned int i; + for (i=0; i < downloadGroup.Size(); i++) + rakPeerInterface->DeallocatePacket(downloadGroup[i]); + downloadGroup.Clear(__FILE__,__LINE__); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SendSerializeIfChangedResult Connection_RM3::SendSerialize(RakNet::Replica3 *replica, bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::BitStream serializationData[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::Time timestamp, PRO sendParameters[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakPeerInterface *rakPeer, unsigned char worldId) +{ + bool channelHasData; + BitSize_t sum=0; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + if (indicesToSend[z]) + sum+=serializationData[z].GetNumberOfBitsUsed(); + } + if (sum==0) + return SSICR_DID_NOT_SEND_DATA; + + RakAssert(replica->GetNetworkID()!=UNASSIGNED_NETWORK_ID); + + RakNet::BitStream out; + BitSize_t bitsUsed; + + int channelIndex; + PRO lastPro=sendParameters[0]; + + for (channelIndex=0; channelIndex < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex++) + { + if (channelIndex==0) + { + SendSerializeHeader(replica, timestamp, &out, worldId); + } + else if (lastPro!=sendParameters[channelIndex]) + { + // Write out remainder + for (int channelIndex2=channelIndex; channelIndex2 < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex2++) + out.Write(false); + + // Send remainder + replica->OnSerializeTransmission(&out, systemAddress); + rakPeer->Send(&out,lastPro.priority,lastPro.reliability,lastPro.orderingChannel,systemAddress,false,lastPro.sendReceipt); + + // If no data left to send, quit out + bool anyData=false; + for (int channelIndex2=channelIndex; channelIndex2 < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex2++) + { + if (serializationData[channelIndex2].GetNumberOfBitsUsed()>0) + { + anyData=true; + break; + } + } + if (anyData==false) + return SSICR_SENT_DATA; + + // Restart stream + SendSerializeHeader(replica, timestamp, &out, worldId); + + for (int channelIndex2=0; channelIndex2 < channelIndex; channelIndex2++) + out.Write(false); + lastPro=sendParameters[channelIndex]; + } + + bitsUsed=serializationData[channelIndex].GetNumberOfBitsUsed(); + channelHasData = indicesToSend[channelIndex]==true && bitsUsed>0; + out.Write(channelHasData); + if (channelHasData) + { + out.WriteCompressed(bitsUsed); + out.AlignWriteToByteBoundary(); + out.Write(serializationData[channelIndex]); + // Crap, forgot this line, was a huge bug in that I'd only send to the first 3 systems + serializationData[channelIndex].ResetReadPointer(); + } + } + replica->OnSerializeTransmission(&out, systemAddress); + rakPeer->Send(&out,lastPro.priority,lastPro.reliability,lastPro.orderingChannel,systemAddress,false,lastPro.sendReceipt); + return SSICR_SENT_DATA; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +SendSerializeIfChangedResult Connection_RM3::SendSerializeIfChanged(unsigned int queryToSerializeIndex, SerializeParameters *sp, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager) +{ + RakNet::Replica3 *replica = queryToSerializeReplicaList[queryToSerializeIndex]->replica; + + if (replica->GetNetworkID()==UNASSIGNED_NETWORK_ID) + return SSICR_DID_NOT_SEND_DATA; + + RM3QuerySerializationResult rm3qsr = replica->QuerySerialization(this); + if (rm3qsr==RM3QSR_NEVER_CALL_SERIALIZE) + { + // Never again for this connection and replica pair + OnNeverSerialize(queryToSerializeIndex, replicaManager); + return SSICR_NEVER_SERIALIZE; + } + + if (rm3qsr==RM3QSR_DO_NOT_CALL_SERIALIZE) + return SSICR_DID_NOT_SEND_DATA; + + if (replica->forceSendUntilNextUpdate) + { + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + if (replica->lastSentSerialization.indicesToSend[z]) + sp->bitsWrittenSoFar+=replica->lastSentSerialization.bitStream[z].GetNumberOfBitsUsed(); + } + return SendSerialize(replica, replica->lastSentSerialization.indicesToSend, replica->lastSentSerialization.bitStream, sp->messageTimestamp, sp->pro, rakPeer, worldId); + } + + for (int i=0; i < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; i++) + { + sp->outputBitstream[i].Reset(); + if (queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS) + sp->lastSentBitstream[i]=&queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[i]; + else + sp->lastSentBitstream[i]=&replica->lastSentSerialization.bitStream[i]; + } + + RM3SerializationResult serializationResult = replica->Serialize(sp); + + if (serializationResult==RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION) + { + // Never again for this connection and replica pair + OnNeverSerialize(queryToSerializeIndex, replicaManager); + return SSICR_NEVER_SERIALIZE; + } + + if (serializationResult==RM3SR_DO_NOT_SERIALIZE) + { + // Don't serialize this tick only + return SSICR_DID_NOT_SEND_DATA; + } + + // This is necessary in case the user in the Serialize() function for some reason read the bitstream they also wrote + // WIthout this code, the Write calls to another bitstream would not write the entire bitstream + BitSize_t sum=0; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp->outputBitstream[z].ResetReadPointer(); + sum+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + } + + if (sum==0) + { + // Don't serialize this tick only + return SSICR_DID_NOT_SEND_DATA; + } + + if (serializationResult==RM3SR_SERIALIZED_ALWAYS) + { + bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + allIndices[z]=true; + + queryToSerializeReplicaList[queryToSerializeIndex]->AllocBS(); + queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Reset(); + queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Write(&sp->outputBitstream[z]); + sp->outputBitstream[z].ResetReadPointer(); + } + return SendSerialize(replica, allIndices, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); + } + + if (serializationResult==RM3SR_SERIALIZED_ALWAYS_IDENTICALLY) + { + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + replica->lastSentSerialization.indicesToSend[z]=sp->outputBitstream[z].GetNumberOfBitsUsed()>0; + sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + replica->lastSentSerialization.bitStream[z].Reset(); + replica->lastSentSerialization.bitStream[z].Write(&sp->outputBitstream[z]); + sp->outputBitstream[z].ResetReadPointer(); + replica->forceSendUntilNextUpdate=true; + } + return SendSerialize(replica, replica->lastSentSerialization.indicesToSend, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); + } + + bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + if (serializationResult==RM3SR_BROADCAST_IDENTICALLY || serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION) + { + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + if (sp->outputBitstream[z].GetNumberOfBitsUsed() > 0 && + (serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION || + ((sp->outputBitstream[z].GetNumberOfBitsUsed()!=replica->lastSentSerialization.bitStream[z].GetNumberOfBitsUsed() || + memcmp(sp->outputBitstream[z].GetData(), replica->lastSentSerialization.bitStream[z].GetData(), sp->outputBitstream[z].GetNumberOfBytesUsed())!=0)))) + { + indicesToSend[z]=true; + replica->lastSentSerialization.indicesToSend[z]=true; + sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + replica->lastSentSerialization.bitStream[z].Reset(); + replica->lastSentSerialization.bitStream[z].Write(&sp->outputBitstream[z]); + sp->outputBitstream[z].ResetReadPointer(); + replica->forceSendUntilNextUpdate=true; + } + else + { + indicesToSend[z]=false; + replica->lastSentSerialization.indicesToSend[z]=false; + } + } + } + else + { + queryToSerializeReplicaList[queryToSerializeIndex]->AllocBS(); + + // RM3SR_SERIALIZED_UNIQUELY + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + if (sp->outputBitstream[z].GetNumberOfBitsUsed() > 0 && + (sp->outputBitstream[z].GetNumberOfBitsUsed()!=queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].GetNumberOfBitsUsed() || + memcmp(sp->outputBitstream[z].GetData(), queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].GetData(), sp->outputBitstream[z].GetNumberOfBytesUsed())!=0) + ) + { + indicesToSend[z]=true; + sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Reset(); + queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Write(&sp->outputBitstream[z]); + sp->outputBitstream[z].ResetReadPointer(); + } + else + { + indicesToSend[z]=false; + } + } + } + + + if (serializationResult==RM3SR_BROADCAST_IDENTICALLY || serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION) + replica->forceSendUntilNextUpdate=true; + + // Send out the data + return SendSerialize(replica, indicesToSend, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void Connection_RM3::OnLocalReference(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) replicaManager; + (void) constructionMode; + + LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); + lsr->replica=replica3; + queryToConstructReplicaList.Push(lsr,_FILE_AND_LINE_); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDereference(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + + LastSerializationResult* lsr=0; + unsigned int idx; + + bool objectExists; + idx=constructedReplicaList.GetIndexFromKey(replica3, &objectExists); + if (objectExists) + { + lsr=constructedReplicaList[idx]; + constructedReplicaList.RemoveAtIndex(idx); + } + + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + if (queryToConstructReplicaList[idx]->replica==replica3) + { + lsr=queryToConstructReplicaList[idx]; + queryToConstructReplicaList.RemoveAtIndex(idx); + break; + } + } + + for (idx=0; idx < queryToSerializeReplicaList.Size(); idx++) + { + if (queryToSerializeReplicaList[idx]->replica==replica3) + { + lsr=queryToSerializeReplicaList[idx]; + queryToSerializeReplicaList.RemoveAtIndex(idx); + break; + } + } + + for (idx=0; idx < queryToDestructReplicaList.Size(); idx++) + { + if (queryToDestructReplicaList[idx]->replica==replica3) + { + lsr=queryToDestructReplicaList[idx]; + queryToDestructReplicaList.RemoveAtIndex(idx); + break; + } + } + + ValidateLists(replicaManager); + + if (lsr) + RakNet::OP_DELETE(lsr,_FILE_AND_LINE_); + + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDownloadFromThisSystem(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); + lsr->replica=replica3; + + ConstructionMode constructionMode = QueryConstructionMode(); + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + unsigned int j; + for (j=0; j < queryToConstructReplicaList.Size(); j++) + { + if (queryToConstructReplicaList[j]->replica->GetNetworkID()==replica3->GetNetworkID() ) + { + queryToConstructReplicaList.RemoveAtIndex(j); + break; + } + } + + queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); + } + + constructedReplicaList.Insert(lsr->replica, lsr, true, _FILE_AND_LINE_); + //assert(queryToSerializeReplicaList.GetIndexOf(replica3)==(unsigned int)-1); + queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); + + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDownloadFromOtherSystem(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + unsigned int j; + for (j=0; j < queryToConstructReplicaList.Size(); j++) + { + if (queryToConstructReplicaList[j]->replica->GetNetworkID()==replica3->GetNetworkID() ) + { + return; + } + } + + OnLocalReference(replica3, replicaManager); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnNeverConstruct(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) constructionMode; + + ValidateLists(replicaManager); + LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; + queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); + RakNet::OP_DELETE(lsr,_FILE_AND_LINE_); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnConstructToThisConnection(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) constructionMode; + + ValidateLists(replicaManager); + LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; + queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); + //assert(constructedReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + constructedReplicaList.Insert(lsr->replica,lsr,true,_FILE_AND_LINE_); + //assert(queryToDestructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); + //assert(queryToSerializeReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnConstructToThisConnection(Replica3 *replica, ReplicaManager3 *replicaManager) +{ + RakAssert(QueryConstructionMode()==QUERY_CONNECTION_FOR_REPLICA_LIST); + (void) replicaManager; + + LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); + lsr->replica=replica; + constructedReplicaList.Insert(replica,lsr,true,_FILE_AND_LINE_); + queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnNeverSerialize(unsigned int queryToSerializeIndex, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + queryToSerializeReplicaList.RemoveAtIndex(queryToSerializeIndex); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnReplicaAlreadyExists(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) constructionMode; + + ValidateLists(replicaManager); + LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; + queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); + //assert(constructedReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + constructedReplicaList.Insert(lsr->replica,lsr,true,_FILE_AND_LINE_); + //assert(queryToDestructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); + //assert(queryToSerializeReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDownloadExisting(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + + ConstructionMode constructionMode = QueryConstructionMode(); + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + unsigned int idx; + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + if (queryToConstructReplicaList[idx]->replica==replica3) + { + OnConstructToThisConnection(idx, replicaManager); + return; + } + } + } + else + { + OnConstructToThisConnection(replica3, replicaManager); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnSendDestructionFromQuery(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) constructionMode; + + ValidateLists(replicaManager); + LastSerializationResult* lsr = queryToDestructReplicaList[queryToDestructIdx]; + queryToDestructReplicaList.RemoveAtIndex(queryToDestructIdx); + unsigned int j; + for (j=0; j < queryToSerializeReplicaList.Size(); j++) + { + if (queryToSerializeReplicaList[j]->replica->GetNetworkID()==lsr->replica->GetNetworkID() ) + { + queryToSerializeReplicaList.RemoveAtIndex(j); + break; + } + } + for (j=0; j < constructedReplicaList.Size(); j++) + { + if (constructedReplicaList[j]->replica->GetNetworkID()==lsr->replica->GetNetworkID() ) + { + constructedReplicaList.RemoveAtIndex(j); + break; + } + } + //assert(queryToConstructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToConstructReplicaList.Push(lsr,_FILE_AND_LINE_); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDoNotQueryDestruction(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + queryToDestructReplicaList.RemoveAtIndex(queryToDestructIdx); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::ValidateLists(ReplicaManager3 *replicaManager) const +{ + (void) replicaManager; + /* +#ifdef _DEBUG + // Each object should exist only once in either constructedReplicaList or queryToConstructReplicaList + // replicaPointer from LastSerializationResult should be same among all lists + unsigned int idx, idx2; + for (idx=0; idx < constructedReplicaList.Size(); idx++) + { + idx2=queryToConstructReplicaList.GetIndexOf(constructedReplicaList[idx]->replica); + if (idx2!=(unsigned int)-1) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + idx2=constructedReplicaList.GetIndexOf(queryToConstructReplicaList[idx]->replica); + if (idx2!=(unsigned int)-1) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + LastSerializationResult *lsr, *lsr2; + for (idx=0; idx < constructedReplicaList.Size(); idx++) + { + lsr=constructedReplicaList[idx]; + + idx2=queryToSerializeReplicaList.GetIndexOf(lsr->replica); + if (idx2!=(unsigned int)-1) + { + lsr2=queryToSerializeReplicaList[idx2]; + if (lsr2!=lsr) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + idx2=queryToDestructReplicaList.GetIndexOf(lsr->replica); + if (idx2!=(unsigned int)-1) + { + lsr2=queryToDestructReplicaList[idx2]; + if (lsr2!=lsr) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + } + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + lsr=queryToConstructReplicaList[idx]; + + idx2=queryToSerializeReplicaList.GetIndexOf(lsr->replica); + if (idx2!=(unsigned int)-1) + { + lsr2=queryToSerializeReplicaList[idx2]; + if (lsr2!=lsr) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + idx2=queryToDestructReplicaList.GetIndexOf(lsr->replica); + if (idx2!=(unsigned int)-1) + { + lsr2=queryToDestructReplicaList[idx2]; + if (lsr2!=lsr) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + } + + // Verify pointer integrity + for (idx=0; idx < constructedReplicaList.Size(); idx++) + { + if (constructedReplicaList[idx]->replica->replicaManager!=replicaManager) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + // Verify pointer integrity + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + if (queryToConstructReplicaList[idx]->replica->replicaManager!=replicaManager) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } +#endif + */ +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::SendConstruction(DataStructures::List &newObjects, DataStructures::List &deletedObjects, PRO sendParameters, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager3) +{ + if (newObjects.Size()==0 && deletedObjects.Size()==0) + return; + + // All construction and destruction takes place in the same network message + // Otherwise, if objects rely on each other being created the same tick to be valid, this won't always be true + // DataStructures::List serializedObjects; + BitSize_t offsetStart, offsetStart2, offsetEnd; + unsigned int newListIndex, oldListIndex; + RakNet::BitStream bsOut; + NetworkID networkId; + if (isFirstConstruction) + { + bsOut.Write((MessageID)ID_REPLICA_MANAGER_DOWNLOAD_STARTED); + bsOut.Write(worldId); + SerializeOnDownloadStarted(&bsOut); + rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); + } + + // LastSerializationResult* lsr; + bsOut.Reset(); + bsOut.Write((MessageID)ID_REPLICA_MANAGER_CONSTRUCTION); + bsOut.Write(worldId); + uint16_t objectSize = (uint16_t) newObjects.Size(); + bsOut.Write(objectSize); + + // Construction + for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) + { + offsetStart=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart); // overwritten to point to the end of the stream + networkId=newObjects[newListIndex]->GetNetworkID(); + bsOut.Write(networkId); + + RM3ConstructionState cs = newObjects[newListIndex]->QueryConstruction(this, replicaManager3); + bool actuallyCreateObject = cs==RM3CS_SEND_CONSTRUCTION; + bsOut.Write(actuallyCreateObject); + bsOut.AlignWriteToByteBoundary(); + + if (actuallyCreateObject) + { + // Actually create the object + bsOut.Write(newObjects[newListIndex]->creatingSystemGUID); + offsetStart2=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart2); // overwritten to point to after the call to WriteAllocationID + bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy + newObjects[newListIndex]->WriteAllocationID(this, &bsOut); + bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart2); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + newObjects[newListIndex]->SerializeConstruction(&bsOut, this); + } + else + { + newObjects[newListIndex]->SerializeConstructionExisting(&bsOut, this); + } + + bsOut.AlignWriteToByteBoundary(); + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + } + + RakNet::BitStream bsOut2; + for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) + { + bsOut2.Reset(); + RM3ConstructionState cs = newObjects[newListIndex]->QueryConstruction(this, replicaManager3); + if (cs==RM3CS_SEND_CONSTRUCTION) + { + newObjects[newListIndex]->PostSerializeConstruction(&bsOut2, this); + } + else + { + RakAssert(cs==RM3CS_ALREADY_EXISTS_REMOTELY); + newObjects[newListIndex]->PostSerializeConstructionExisting(&bsOut2, this); + } + if (bsOut2.GetNumberOfBitsUsed()>0) + { + bsOut.Write(true); + bsOut.AlignWriteToByteBoundary(); + offsetStart=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart); // overwritten to point to the end of the stream + networkId=newObjects[newListIndex]->GetNetworkID(); + bsOut.Write(networkId); + bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy + bsOut.Write(&bsOut2); + bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + } + else + bsOut.Write(false); + } + bsOut.AlignWriteToByteBoundary(); + + // Destruction + objectSize = (uint16_t) deletedObjects.Size(); + bsOut.Write(objectSize); + for (oldListIndex=0; oldListIndex < deletedObjects.Size(); oldListIndex++) + { + networkId=deletedObjects[oldListIndex]->GetNetworkID(); + bsOut.Write(networkId); + offsetStart=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart); + deletedObjects[oldListIndex]->deletingSystemGUID=rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + bsOut.Write(deletedObjects[oldListIndex]->deletingSystemGUID); + deletedObjects[oldListIndex]->SerializeDestruction(&bsOut, this); + bsOut.AlignWriteToByteBoundary(); + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + } + rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); + + // TODO - shouldn't this be part of construction? + + // Initial Download serialize to a new system + // Immediately send serialize after construction if the replica object already has saved data + // If the object was serialized identically, and does not change later on, then the new connection never gets the data + SerializeParameters sp; + sp.whenLastSerialized=0; + RakNet::BitStream emptyBs; + for (int index=0; index < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; index++) + { + sp.lastSentBitstream[index]=&emptyBs; + sp.pro[index]=sendParameters; + sp.pro[index].reliability=RELIABLE_ORDERED; + } + + sp.bitsWrittenSoFar=0; + RakNet::Time t = RakNet::GetTimeMS(); + for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) + { + sp.destinationConnection=this; + sp.messageTimestamp=0; + RakNet::Replica3 *replica = newObjects[newListIndex]; + // 8/22/09 Forgot ResetWritePointer + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp.outputBitstream[z].ResetWritePointer(); + } + + RM3SerializationResult res = replica->Serialize(&sp); + if (res!=RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION && + res!=RM3SR_DO_NOT_SERIALIZE && + res!=RM3SR_SERIALIZED_UNIQUELY) + { + bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp.bitsWrittenSoFar+=sp.outputBitstream[z].GetNumberOfBitsUsed(); + allIndices[z]=true; + } + SendSerialize(replica, allIndices, sp.outputBitstream, sp.messageTimestamp, sp.pro, rakPeer, worldId); + newObjects[newListIndex]->whenLastSerialized=t; + + } + // else wait for construction request accepted before serializing + } + + if (isFirstConstruction) + { + bsOut.Reset(); + bsOut.Write((MessageID)ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE); + bsOut.Write(worldId); + SerializeOnDownloadComplete(&bsOut); + rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); + } + + isFirstConstruction=false; + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::SendValidation(RakNet::RakPeerInterface *rakPeer, unsigned char worldId) +{ + // Hijack to mean sendValidation + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_REPLICA_MANAGER_SCOPE_CHANGE); + bsOut.Write(worldId); + rakPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Replica3::Replica3() +{ + creatingSystemGUID=UNASSIGNED_RAKNET_GUID; + deletingSystemGUID=UNASSIGNED_RAKNET_GUID; + replicaManager=0; + forceSendUntilNextUpdate=false; + whenLastSerialized=0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Replica3::~Replica3() +{ + if (replicaManager) + { + replicaManager->Dereference(this); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Replica3::BroadcastDestruction(void) +{ + replicaManager->BroadcastDestruction(this,UNASSIGNED_SYSTEM_ADDRESS); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RakNetGUID Replica3::GetCreatingSystemGUID(void) const +{ + return creatingSystemGUID; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ConstructionState Replica3::QueryConstruction_ClientConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) +{ + (void) destinationConnection; + if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return RM3CS_SEND_CONSTRUCTION; + // Send back to the owner client too, because they couldn't assign the network ID + if (isThisTheServer) + return RM3CS_SEND_CONSTRUCTION; + return RM3CS_NEVER_CONSTRUCT; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool Replica3::QueryRemoteConstruction_ClientConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer) +{ + (void) sourceConnection; + (void) isThisTheServer; + + // OK to create + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ConstructionState Replica3::QueryConstruction_ServerConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) +{ + (void) destinationConnection; + + if (isThisTheServer) + return RM3CS_SEND_CONSTRUCTION; + return RM3CS_NEVER_CONSTRUCT; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool Replica3::QueryRemoteConstruction_ServerConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer) +{ + (void) sourceConnection; + if (isThisTheServer) + return false; + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ConstructionState Replica3::QueryConstruction_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode) +{ + (void) destinationConnection; + + if (p2pMode==R3P2PM_SINGLE_OWNER) + { + // We send to all, others do nothing + if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return RM3CS_SEND_CONSTRUCTION; + + // RM3CS_NEVER_CONSTRUCT will not send the object, and will not Serialize() it + return RM3CS_NEVER_CONSTRUCT; + } + else if (p2pMode==R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE) + { + return RM3CS_SEND_CONSTRUCTION; + } + else + { + RakAssert(p2pMode==R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE); + + // RM3CS_ALREADY_EXISTS_REMOTELY will not send the object, but WILL call QuerySerialization() and Serialize() on it. + return RM3CS_ALREADY_EXISTS_REMOTELY; + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool Replica3::QueryRemoteConstruction_PeerToPeer(RakNet::Connection_RM3 *sourceConnection) +{ + (void) sourceConnection; + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3QuerySerializationResult Replica3::QuerySerialization_ClientSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) +{ + // Owner client sends to all + if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return RM3QSR_CALL_SERIALIZE; + // Server sends to all but owner client + if (isThisTheServer && destinationConnection->GetRakNetGUID()!=creatingSystemGUID) + return RM3QSR_CALL_SERIALIZE; + // Remote clients do not send + return RM3QSR_NEVER_CALL_SERIALIZE; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3QuerySerializationResult Replica3::QuerySerialization_ServerSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) +{ + (void) destinationConnection; + // Server sends to all + if (isThisTheServer) + return RM3QSR_CALL_SERIALIZE; + + // Clients do not send + return RM3QSR_NEVER_CALL_SERIALIZE; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3QuerySerializationResult Replica3::QuerySerialization_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode) +{ + (void) destinationConnection; + + if (p2pMode==R3P2PM_SINGLE_OWNER) + { + // Owner peer sends to all + if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return RM3QSR_CALL_SERIALIZE; + + // Remote peers do not send + return RM3QSR_NEVER_CALL_SERIALIZE; + } + else if (p2pMode==R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE) + { + return RM3QSR_CALL_SERIALIZE; + } + else + { + RakAssert(p2pMode==R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE); + return RM3QSR_DO_NOT_CALL_SERIALIZE; + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_Client(RakNet::Connection_RM3 *droppedConnection) const +{ + (void) droppedConnection; + return RM3AOPC_DELETE_REPLICA; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_Server(RakNet::Connection_RM3 *droppedConnection) const +{ + (void) droppedConnection; + return RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_PeerToPeer(RakNet::Connection_RM3 *droppedConnection) const +{ + (void) droppedConnection; + return RM3AOPC_DELETE_REPLICA; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/ReplicaManager3.h b/project/lib_projects/raknet/jni/RaknetSources/ReplicaManager3.h index d256ad9..527b8eb 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ReplicaManager3.h +++ b/project/lib_projects/raknet/jni/RaknetSources/ReplicaManager3.h @@ -1,1040 +1,1040 @@ -/// \file -/// \brief Contains the third iteration of the ReplicaManager class. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ReplicaManager3==1 - -#ifndef __REPLICA_MANAGER_3 -#define __REPLICA_MANAGER_3 - -#include "RakNetTypes.h" -#include "RakNetTime.h" -#include "BitStream.h" -#include "PacketPriority.h" -#include "PluginInterface2.h" -#include "NetworkIDObject.h" -#include "DS_OrderedList.h" -#include "DS_Queue.h" - -/// \defgroup REPLICA_MANAGER_GROUP3 ReplicaManager3 -/// \brief Third implementation of object replication -/// \details -/// \ingroup REPLICA_MANAGER_GROUP - -namespace RakNet -{ -class Connection_RM3; -class Replica3; - - -/// \internal -/// \ingroup REPLICA_MANAGER_GROUP3 -struct PRO -{ - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketPriority(). - PacketPriority priority; - - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketReliability(). - PacketReliability reliability; - - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultOrderingChannel(). - char orderingChannel; - - /// Passed to RakPeerInterface::Send(). Defaults to 0. - uint32_t sendReceipt; - - bool operator==( const PRO& right ) const; - bool operator!=( const PRO& right ) const; -}; - - -/// \brief System to help automate game object construction, destruction, and serialization -/// \details ReplicaManager3 tracks your game objects and automates the networking for replicating them across the network
-/// As objects are created, destroyed, or serialized differently, those changes are pushed out to other systems.
-/// To use:
-///
    -///
  1. Derive from Connection_RM3 and implement Connection_RM3::AllocReplica(). This is a factory function where given a user-supplied identifier for a class (such as name) return an instance of that class. Should be able to return any networked object in your game. -///
  2. Derive from ReplicaManager3 and implement AllocConnection() and DeallocConnection() to return the class you created in step 1. -///
  3. Derive your networked game objects from Replica3. All pure virtuals have to be implemented, however defaults are provided for Replica3::QueryConstruction(), Replica3::QueryRemoteConstruction(), and Replica3::QuerySerialization() depending on your network architecture. -///
  4. When a new game object is created on the local system, pass it to ReplicaManager3::Reference(). -///
  5. When a game object is destroyed on the local system, and you want other systems to know about it, call Replica3::BroadcastDestruction() -///
-///
-/// At this point, all new connections will automatically download, get construction messages, get destruction messages, and update serialization automatically. -/// \ingroup REPLICA_MANAGER_GROUP3 -class RAK_DLL_EXPORT ReplicaManager3 : public PluginInterface2 -{ -public: - ReplicaManager3(); - virtual ~ReplicaManager3(); - - /// \brief Implement to return a game specific derivation of Connection_RM3 - /// \details The connection object represents a remote system connected to you that is using the ReplicaManager3 system.
- /// It has functions to perform operations per-connection.
- /// AllocConnection() and DeallocConnection() are factory functions to create and destroy instances of the connection object.
- /// It is used if autoCreate is true via SetAutoManageConnections() (true by default). Otherwise, the function is not called, and you will have to call PushConnection() manually
- /// \note If you do not want a new network connection to immediately download game objects, SetAutoManageConnections() and PushConnection() are how you do this. - /// \sa SetAutoManageConnections() - /// \param[in] systemAddress Address of the system you are adding - /// \param[in] rakNetGUID GUID of the system you are adding. See Packet::rakNetGUID or RakPeerInterface::GetGUIDFromSystemAddress() - /// \return The new connection instance. - virtual Connection_RM3* AllocConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID) const=0; - - /// \brief Implement to destroy a class instanced returned by AllocConnection() - /// \details Most likely just implement as {delete connection;}
- /// It is used if autoDestroy is true via SetAutoManageConnections() (true by default). Otherwise, the function is not called and you would then be responsible for deleting your own connection objects. - /// \param[in] connection The pointer instance to delete - virtual void DeallocConnection(Connection_RM3 *connection) const=0; - - /// \brief Enable or disable automatically assigning connections to new instances of Connection_RM3 - /// \details ReplicaManager3 can automatically create and/or destroy Connection_RM3 as systems connect or disconnect from RakPeerInterface.
- /// By default this is on, to make the system easier to learn and setup.
- /// If you don't want all connections to take part in the game, or you want to delay when a connection downloads the game, set \a autoCreate to false.
- /// If you want to delay deleting a connection that has dropped, set \a autoDestroy to false. If you do this, then you must call PopConnection() to remove that connection from being internally tracked. You'll also have to delete the connection instance on your own.
- /// \param[in] autoCreate Automatically call ReplicaManager3::AllocConnection() for each new connection. Defaults to true. - /// \param[in] autoDestroy Automatically call ReplicaManager3::DeallocConnection() for each dropped connection. Defaults to true. - void SetAutoManageConnections(bool autoCreate, bool autoDestroy); - - /// \brief Track a new Connection_RM3 instance - /// \details If \a autoCreate is false for SetAutoManageConnections(), then you need this function to add new instances of Connection_RM3 yourself.
- /// You don't need to track this pointer yourself, you can get it with GetConnectionAtIndex(), GetConnectionByGUID(), or GetConnectionBySystemAddress().
- /// \param[in] newConnection The new connection instance to track. - bool PushConnection(RakNet::Connection_RM3 *newConnection); - - /// \brief Stop tracking a connection - /// \details On call, for each replica returned by GetReplicasCreatedByGuid(), QueryActionOnPopConnection() will be called. Depending on the return value, this may delete the corresponding replica.
- /// If autoDestroy is true in the call to SetAutoManageConnections() (true by default) then this is called automatically when the connection is lost. In that case, the returned connection instance is deleted.
- /// \param[in] guid of the connection to get. Passed to ReplicaManager3::AllocConnection() originally. - RakNet::Connection_RM3 * PopConnection(RakNetGUID guid); - - /// \brief Adds a replicated object to the system. - /// \details Anytime you create a new object that derives from Replica3, and you want ReplicaManager3 to use it, pass it to Reference().
- /// Remote systems already connected will potentially download this object the next time ReplicaManager3::Update() is called, which happens every time you call RakPeerInterface::Receive().
- /// You can also call ReplicaManager3::Update() manually to send referenced objects right away - /// \param[in] replica3 The object to start tracking - void Reference(RakNet::Replica3 *replica3); - - /// \brief Removes a replicated object from the system. - /// \details The object is not deallocated, it is up to the caller to do so.
- /// This is called automatically from the destructor of Replica3, so you don't need to call it manually unless you want to stop tracking an object before it is destroyed. - /// \param[in] replica3 The object to stop tracking - void Dereference(RakNet::Replica3 *replica3); - - /// \brief Removes multiple replicated objects from the system. - /// \details Same as Dereference(), but for a list of objects.
- /// Useful with the lists returned by GetReplicasCreatedByGuid(), GetReplicasCreatedByMe(), or GetReferencedReplicaList().
- /// \param[in] replicaListIn List of objects - void DereferenceList(DataStructures::List &replicaListIn); - - /// \brief Returns all objects originally created by a particular system - /// \details Originally created is defined as the value of Replica3::creatingSystemGUID, which is automatically assigned in ReplicaManager3::Reference().
- /// You do not have to be directly connected to that system to get the objects originally created by that system.
- /// \param[in] guid GUID of the system we are referring to. Originally passed as the \a guid parameter to ReplicaManager3::AllocConnection() - /// \param[out] List of Replica3 instances to be returned - void GetReplicasCreatedByGuid(RakNetGUID guid, DataStructures::List &replicaListOut); - - /// \brief Returns all objects originally created by your system - /// \details Calls GetReplicasCreatedByGuid() for your own system guid. - /// \param[out] List of Replica3 instances to be returned - void GetReplicasCreatedByMe(DataStructures::List &replicaListOut); - - /// \brief Returns the entire list of Replicas that we know about. - /// \details This is all Replica3 instances passed to Reference, as well as instances we downloaded and created via Connection_RM3::AllocReference() - /// \param[out] List of Replica3 instances to be returned - void GetReferencedReplicaList(DataStructures::List &replicaListOut); - - /// \brief Returns the number of replicas known about - /// \details Returns the size of the list that would be returned by GetReferencedReplicaList() - /// \return How many replica objects are in the list of replica objects - unsigned GetReplicaCount(void) const; - - /// \brief Returns a replica by index - /// \details Returns one of the items in the list that would be returned by GetReferencedReplicaList() - /// \param[in] index An index, from 0 to GetReplicaCount()-1. - /// \return A Replica3 instance - Replica3 *GetReplicaAtIndex(unsigned index); - - /// \brief Returns the number of connections - /// \details Returns the number of connections added with ReplicaManager3::PushConnection(), minus the number removed with ReplicaManager3::PopConnection() - /// \return The number of registered connections - unsigned int GetConnectionCount(void) const; - - /// \brief Returns a connection pointer previously added with PushConnection() - /// \param[in] index An index, from 0 to GetConnectionCount()-1. - /// \return A Connection_RM3 pointer - Connection_RM3* GetConnectionAtIndex(unsigned index) const; - - /// \brief Returns a connection pointer previously added with PushConnection() - /// \param[in] sa The system address of the connection to return - /// \return A Connection_RM3 pointer, or 0 if not found - Connection_RM3* GetConnectionBySystemAddress(const SystemAddress &sa) const; - - /// \brief Returns a connection pointer previously added with PushConnection.() - /// \param[in] guid The guid of the connection to return - /// \return A Connection_RM3 pointer, or 0 if not found - Connection_RM3* GetConnectionByGUID(RakNetGUID guid) const; - - /// \param[in] Default ordering channel to use for object creation, destruction, and serializations - void SetDefaultOrderingChannel(char def); - - /// \param[in] Default packet priority to use for object creation, destruction, and serializations - void SetDefaultPacketPriority(PacketPriority def); - - /// \param[in] Default packet reliability to use for object creation, destruction, and serializations - void SetDefaultPacketReliability(PacketReliability def); - - /// \details Every \a intervalMS milliseconds, Connection_RM3::OnAutoserializeInterval() will be called.
- /// Defaults to 30.
- /// Pass with <0 to disable. Pass 0 to Serialize() every time RakPeer::Recieve() is called
- /// If you want to control the update interval with more granularity, use the return values from Replica3::Serialize().
- /// \param[in] intervalMS How frequently to autoserialize all objects. This controls the maximum number of game object updates per second. - void SetAutoSerializeInterval(RakNet::Time intervalMS); - - /// \brief Return the connections that we think have an instance of the specified Replica3 instance - /// \details This can be wrong, for example if that system locally deleted the outside the scope of ReplicaManager3, if QueryRemoteConstruction() returned false, or if DeserializeConstruction() returned false. - /// \param[in] replica The replica to check against. - /// \param[out] connectionsThatHaveConstructedThisReplica Populated with connection instances that we believe have \a replica allocated - void GetConnectionsThatHaveReplicaConstructed(Replica3 *replica, DataStructures::List &connectionsThatHaveConstructedThisReplica); - - /// \brief Defines the unique instance of ReplicaManager3 if multiple instances are on the same instance of RakPeerInterface - /// \details ReplicaManager3 supports multiple instances of itself attached to the same instance of rakPeer, in case your game has multiple worlds.
- /// Call SetWorldID with a different number for each instance.
- /// The default worldID is 0.
- /// To use multiple worlds, you will also need to call ReplicaManager3::SetNetworkIDManager() to have a different NetworkIDManager instance per world - void SetWorldID(unsigned char id); - - /// \return Whatever was passed to SetWorldID(), or 0 if it was never called. - unsigned char GetWorldID(void) const; - - /// \details Sets the networkIDManager instance that this plugin relys upon.
- /// Uses whatever instance is attached to RakPeerInterface if unset.
- /// To support multiple worlds, you should set it to a different manager for each instance of the plugin - /// \param[in] _networkIDManager The externally allocated NetworkIDManager instance for this plugin to use. - void SetNetworkIDManager(NetworkIDManager *_networkIDManager); - - /// Returns what was passed to SetNetworkIDManager(), or the instance on RakPeerInterface if unset. - NetworkIDManager *GetNetworkIDManager(void) const; - - /// \details Send a network command to destroy one or more Replica3 instances - /// Usually you won't need this, but use Replica3::BroadcastDestruction() instead. - /// The objects are unaffected locally - /// \param[in] replicaList List of Replica3 objects to tell other systems to destroy. - /// \param[in] exclusionAddress Which system to not send to. UNASSIGNED_SYSTEM_ADDRESS to send to all. - void BroadcastDestructionList(DataStructures::List &replicaListSource, const SystemAddress &exclusionAddress); - - /// \internal - /// \details Tell other systems that have this replica to destroy this replica.
- /// You shouldn't need to call this, as it happens in the Replica3 destructor - void BroadcastDestruction(Replica3 *replica, const SystemAddress &exclusionAddress); - - /// \internal - /// \details Frees internal lists.
- /// Externally allocated pointers are not deallocated - void Clear(void); - - /// \internal - PRO GetDefaultSendParameters(void) const; - - /// Call interfaces, send data - virtual void Update(void); -protected: - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - virtual void OnRakPeerShutdown(void); - virtual void OnDetach(void); - - PluginReceiveResult OnConstruction(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); - PluginReceiveResult OnSerialize(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, RakNet::Time timestamp, unsigned char packetDataOffset); - PluginReceiveResult OnDownloadStarted(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); - PluginReceiveResult OnDownloadComplete(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); - - void DeallocReplicaNoBroadcastDestruction(RakNet::Connection_RM3 *connection, RakNet::Replica3 *replica3); - RakNet::Connection_RM3 * PopConnection(unsigned int index); - Replica3* GetReplicaByNetworkID(NetworkID networkId); - unsigned int ReferenceInternal(RakNet::Replica3 *replica3); - - DataStructures::List connectionList; - DataStructures::List userReplicaList; - - PRO defaultSendParameters; - RakNet::Time autoSerializeInterval; - RakNet::Time lastAutoSerializeOccurance; - unsigned char worldId; - NetworkIDManager *networkIDManager; - bool autoCreateConnections, autoDestroyConnections; - Replica3 *currentlyDeallocatingReplica; - - friend class Connection_RM3; -}; - -static const int RM3_NUM_OUTPUT_BITSTREAM_CHANNELS=16; - -/// \ingroup REPLICA_MANAGER_GROUP3 -struct LastSerializationResultBS -{ - RakNet::BitStream bitStream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; -}; - -/// Represents the serialized data for an object the last time it was sent. Used by Connection_RM3::OnAutoserializeInterval() and Connection_RM3::SendSerializeIfChanged() -/// \ingroup REPLICA_MANAGER_GROUP3 -struct LastSerializationResult -{ - LastSerializationResult(); - ~LastSerializationResult(); - - /// The replica instance we serialized - RakNet::Replica3 *replica; - //bool neverSerialize; -// bool isConstructed; - - void AllocBS(void); - LastSerializationResultBS* lastSerializationResultBS; -}; - -/// Parameters passed to Replica3::Serialize() -/// \ingroup REPLICA_MANAGER_GROUP3 -struct SerializeParameters -{ - /// Write your output for serialization here - /// If nothing is written, the serialization will not occur - /// Write to any or all of the NUM_OUTPUT_BITSTREAM_CHANNELS channels available. Channels can hold independent data - RakNet::BitStream outputBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - - /// Last bitstream we sent for this replica to this system. - /// Read, but DO NOT MODIFY - RakNet::BitStream* lastSentBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - - /// Set to non-zero to transmit a timestamp with this message. - /// Defaults to 0 - /// Use RakNet::GetTime() for this - RakNet::Time messageTimestamp; - - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketPriority(). - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketReliability(). - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultOrderingChannel(). - PRO pro[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - - /// Passed to RakPeerInterface::Send(). - RakNet::Connection_RM3 *destinationConnection; - - /// For prior serializations this tick, for the same connection, how many bits have we written so far? - /// Use this to limit how many objects you send to update per-tick if desired - BitSize_t bitsWrittenSoFar; - - /// When this object was last serialized to the connection - /// 0 means never - RakNet::Time whenLastSerialized; - - /// Current time, in milliseconds. - /// curTime - whenLastSerialized is how long it has been since this object was last sent - RakNet::Time curTime; -}; - -/// \ingroup REPLICA_MANAGER_GROUP3 -struct DeserializeParameters -{ - RakNet::BitStream serializationBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - bool bitstreamWrittenTo[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - RakNet::Time timeStamp; - RakNet::Connection_RM3 *sourceConnection; -}; - -/// \ingroup REPLICA_MANAGER_GROUP3 -enum SendSerializeIfChangedResult -{ - SSICR_SENT_DATA, - SSICR_DID_NOT_SEND_DATA, - SSICR_NEVER_SERIALIZE, -}; - -/// \brief Each remote system is represented by Connection_RM3. Used to allocate Replica3 and track which instances have been allocated -/// \details Important function: AllocReplica() - must be overridden to create an object given an identifier for that object, which you define for all objects in your game -/// \ingroup REPLICA_MANAGER_GROUP3 -class RAK_DLL_EXPORT Connection_RM3 -{ -public: - - Connection_RM3(const SystemAddress &_systemAddress, RakNetGUID _guid); - virtual ~Connection_RM3(); - - /// \brief Class factory to create a Replica3 instance, given a user-defined identifier - /// \details Identifier is returned by Replica3::WriteAllocationID() for what type of class to create.
- /// This is called when you download a replica from another system.
- /// See Replica3::Dealloc for the corresponding destruction message.
- /// Return 0 if unable to create the intended object. Note, in that case the other system will still think we have the object and will try to serialize object updates to us. Generally, you should not send objects the other system cannot create.
- /// \sa Replica3::WriteAllocationID(). - /// Sample implementation:
- /// {RakNet::RakString typeName; allocationIdBitstream->Read(typeName); if (typeName=="Soldier") return new Soldier; return 0;}
- /// \param[in] allocationIdBitstream user-defined bitstream uniquely identifying a game object type - /// \param[in] replicaManager3 Instance of ReplicaManager3 that controls this connection - /// \return The new replica instance - virtual Replica3 *AllocReplica(RakNet::BitStream *allocationIdBitstream, ReplicaManager3 *replicaManager3)=0; - - /// \brief Get list of all replicas that are constructed for this connection - /// \param[out] objectsTheyDoHave Destination list. Returned in sorted ascending order, sorted on the value of the Replica3 pointer. - virtual void GetConstructedReplicas(DataStructures::List &objectsTheyDoHave); - - /// Returns true if we think this remote connection has this replica constructed - /// \param[in] replica3 Which replica we are querying - /// \return True if constructed, false othewise - bool HasReplicaConstructed(RakNet::Replica3 *replica); - - /// When a new connection connects, before sending any objects, SerializeOnDownloadStarted() is called - /// \param[out] bitStream Passed to DeserializeOnDownloadStarted() - virtual void SerializeOnDownloadStarted(RakNet::BitStream *bitStream) {(void) bitStream;} - - /// Receives whatever was written in SerializeOnDownloadStarted() - /// \param[in] bitStream Written in SerializeOnDownloadStarted() - virtual void DeserializeOnDownloadStarted(RakNet::BitStream *bitStream) {(void) bitStream;} - - /// When a new connection connects, after constructing and serialization all objects, SerializeOnDownloadComplete() is called - /// \param[out] bitStream Passed to DeserializeOnDownloadComplete() - virtual void SerializeOnDownloadComplete(RakNet::BitStream *bitStream) {(void) bitStream;} - - /// Receives whatever was written in DeserializeOnDownloadComplete() - /// \param[in] bitStream Written in SerializeOnDownloadComplete() - virtual void DeserializeOnDownloadComplete(RakNet::BitStream *bitStream) {(void) bitStream;} - - /// \return The system address passed to the constructor of this object - SystemAddress GetSystemAddress(void) const {return systemAddress;} - - /// \return Returns the RakNetGUID passed to the constructor of this object - RakNetGUID GetRakNetGUID(void) const {return guid;} - - /// List of enumerations for how to get the list of valid objects for other systems - enum ConstructionMode - { - /// For every object that does not exist on the remote system, call Replica3::QueryConstruction() every tick. - /// Do not call Replica3::QueryDestruction() - /// Do not call Connection_RM3::QueryReplicaList() - QUERY_REPLICA_FOR_CONSTRUCTION, - - /// For every object that does not exist on the remote system, call Replica3::QueryConstruction() every tick. Based on the call, the object may be sent to the other system. - /// For every object that does exist on the remote system, call Replica3::QueryDestruction() every tick. Based on the call, the object may be deleted on the other system. - /// Do not call Connection_RM3::QueryReplicaList() - QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION, - - /// Do not call Replica3::QueryConstruction() or Replica3::QueryDestruction() - /// Call Connection_RM3::QueryReplicaList() to determine which objects exist on remote systems - /// This can be faster than QUERY_REPLICA_FOR_CONSTRUCTION and QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION for large worlds - /// See GridSectorizer.h under /Source for code that can help with this - QUERY_CONNECTION_FOR_REPLICA_LIST - }; - - /// \brief Return whether or not downloads to our system should all be processed the same tick (call to RakPeer::Receive() ) - /// \details Normally the system will send ID_REPLICA_MANAGER_DOWNLOAD_STARTED, ID_REPLICA_MANAGER_CONSTRUCTION for all downloaded objects, - /// ID_REPLICA_MANAGER_SERIALIZE for each downloaded object, and lastly ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE. - /// This enables the application to show a downloading splash screen on ID_REPLICA_MANAGER_DOWNLOAD_STARTED, a progress bar, and to close the splash screen and activate all objects on ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE - /// However, if the application was not set up for this then it would result in incomplete objects spread out over time, and cause problems - /// If you return true from QueryGroupDownloadMessages(), then these messages will be returned all in one tick, returned only when the download is complete - /// \note ID_REPLICA_MANAGER_DOWNLOAD_STARTED calls the callback DeserializeOnDownloadStarted() - /// \note ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE calls the callback DeserializeOnDownloadComplete() - virtual bool QueryGroupDownloadMessages(void) const {return false;} - - /// \brief Queries how to get the list of objects that exist on remote systems - /// \details The default of calling QueryConstruction for every known object is easy to use, but not efficient, especially for large worlds where many objects are outside of the player's circle of influence.
- /// QueryDestruction is also not necessarily useful or efficient, as object destruction tends to happen in known cases, and can be accomplished by calling Replica3::BroadcastDestruction() - /// QueryConstructionMode() allows you to specify more efficient algorithms than the default when overriden. - /// \return How to get the list of objects that exist on the remote system. You should always return the same value for a given connection - virtual ConstructionMode QueryConstructionMode(void) const {return QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION;} - - /// \brief Callback used when QueryConstructionMode() returns QUERY_CONNECTION_FOR_REPLICA_LIST - /// \details This advantage of this callback is if that there are many objects that a particular connection does not have, then we do not have to iterate through those - /// objects calling QueryConstruction() for each of them.
- ///
- /// The following code uses a sorted merge sort to quickly find new and deleted objects, given a list of objects we know should exist.
- ///
- /// DataStructures::List objectsTheyShouldHave; // You have to fill in this list
- /// DataStructures::List objectsTheyCurrentlyHave,objectsTheyStillHave,existingReplicasToDestro,newReplicasToCreatey;
- /// GetConstructedReplicas(objectsTheyCurrentlyHave);
- /// DataStructures::Multilist::FindIntersection(objectsTheyCurrentlyHave, objectsTheyShouldHave, objectsTheyStillHave, existingReplicasToDestroy, newReplicasToCreate);
- ///
- /// See GridSectorizer in the Source directory as a method to find all objects within a certain radius in a fast way.
- ///
- /// \param[out] newReplicasToCreate Anything in this list will be created on the remote system - /// \param[out] existingReplicasToDestroy Anything in this list will be destroyed on the remote system - virtual void QueryReplicaList( - DataStructures::List newReplicasToCreate, - DataStructures::List existingReplicasToDestroy) {} - - /// \internal This is used internally - however, you can also call it manually to send a data update for a remote replica.
- /// \brief Sends over a serialization update for \a replica.
- /// NetworkID::GetNetworkID() is written automatically, serializationData is the object data.
- /// \param[in] replica Which replica to serialize - /// \param[in] serializationData Serialized object data - /// \param[in] timestamp 0 means no timestamp. Otherwise message is prepended with ID_TIMESTAMP - /// \param[in] sendParameters Parameters on how to send - /// \param[in] rakPeer Instance of RakPeerInterface to send on - /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() - virtual SendSerializeIfChangedResult SendSerialize(RakNet::Replica3 *replica, bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::BitStream serializationData[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::Time timestamp, PRO sendParameters[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::RakPeerInterface *rakPeer, unsigned char worldId); - - /// \internal - /// \details Calls Connection_RM3::SendSerialize() if Replica3::Serialize() returns a different result than what is contained in \a lastSerializationResult.
- /// Used by autoserialization in Connection_RM3::OnAutoserializeInterval() - /// \param[in] queryToSerializeIndex Index into queryToSerializeReplicaList for whichever replica this is - /// \param[in] sp Controlling parameters over the serialization - /// \param[in] rakPeer Instance of RakPeerInterface to send on - /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() - virtual SendSerializeIfChangedResult SendSerializeIfChanged(unsigned int queryToSerializeIndex, SerializeParameters *sp, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager); - - /// \internal - /// \brief Given a list of objects that were created and destroyed, serialize and send them to another system. - /// \param[in] newObjects Objects to serialize construction - /// \param[in] deletedObjects Objects to serialize destruction - /// \param[in] sendParameters Controlling parameters over the serialization - /// \param[in] rakPeer Instance of RakPeerInterface to send on - /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() - /// \param[in] replicaManager3 ReplicaManager3 instance - virtual void SendConstruction(DataStructures::List &newObjects, DataStructures::List &deletedObjects, PRO sendParameters, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager3); - - /// \internal - /// Remove from \a newObjectsIn objects that already exist and save to \a newObjectsOut - /// Remove from \a deletedObjectsIn objects that do not exist, and save to \a deletedObjectsOut - void CullUniqueNewAndDeletedObjects(DataStructures::List &newObjectsIn, - DataStructures::List &deletedObjectsIn, - DataStructures::List &newObjectsOut, - DataStructures::List &deletedObjectsOut); - - /// \internal - void SendValidation(RakNet::RakPeerInterface *rakPeer, unsigned char worldId); - - /// \internal - void AutoConstructByQuery(ReplicaManager3 *replicaManager3); - - - // Internal - does the other system have this connection too? Validated means we can now use it - bool isValidated; - // Internal - Used to see if we should send download started - bool isFirstConstruction; - - static int Replica3LSRComp( Replica3 * const &replica3, LastSerializationResult * const &data ); - -protected: - - SystemAddress systemAddress; - RakNetGUID guid; - - /* - Operations: - - Locally reference a new replica: - Add to queryToConstructReplicaList for all objects - - Add all objects to queryToConstructReplicaList - - Download: - Add to constructedReplicaList for connection that send the object to us - Add to queryToSerializeReplicaList for connection that send the object to us - Add to queryToConstructReplicaList for all other connections - - Never construct for this connection: - Remove from queryToConstructReplicaList - - Construct to this connection - Remove from queryToConstructReplicaList - Add to constructedReplicaList for this connection - Add to queryToSerializeReplicaList for this connection - - Serialize: - Iterate through queryToSerializeReplicaList - - Never serialize for this connection - Remove from queryToSerializeReplicaList - - Reference (this system has this object already) - Remove from queryToConstructReplicaList - Add to constructedReplicaList for this connection - Add to queryToSerializeReplicaList for this connection - - Downloaded an existing object - if replica is in queryToConstructReplicaList, OnConstructToThisConnection() - else ignore - - Send destruction from query - Remove from queryToDestructReplicaList - Remove from queryToSerializeReplicaList - Remove from constructedReplicaList - Add to queryToConstructReplicaList - - Do not query destruction again - Remove from queryToDestructReplicaList - */ - void OnLocalReference(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnDereference(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnDownloadFromThisSystem(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnDownloadFromOtherSystem(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnNeverConstruct(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); - void OnConstructToThisConnection(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); - void OnConstructToThisConnection(Replica3 *replica, ReplicaManager3 *replicaManager); - void OnNeverSerialize(unsigned int queryToSerializeIndex, ReplicaManager3 *replicaManager); - void OnReplicaAlreadyExists(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); - void OnDownloadExisting(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnSendDestructionFromQuery(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager); - void OnDoNotQueryDestruction(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager); - void ValidateLists(ReplicaManager3 *replicaManager) const; - void SendSerializeHeader(RakNet::Replica3 *replica, RakNet::Time timestamp, RakNet::BitStream *bs, unsigned char worldId); - - // The list of objects that our local system and this remote system both have - // Either we sent this object to them, or they sent this object to us - // A given Replica can be either in queryToConstructReplicaList or constructedReplicaList but not both at the same time - DataStructures::OrderedList constructedReplicaList; - - // Objects that we have, but this system does not, and we will query each tick to see if it should be sent to them - // If we do send it to them, the replica is moved to constructedReplicaList - // A given Replica can be either in queryToConstructReplicaList or constructedReplicaList but not both at the same time - DataStructures::List queryToConstructReplicaList; - - // Objects that this system has constructed are added at the same time to queryToSerializeReplicaList - // This list is used to serialize all objects that this system has to this connection - DataStructures::List queryToSerializeReplicaList; - - // Objects that are constructed on this system are also queried if they should be destroyed to this system - DataStructures::List queryToDestructReplicaList; - - // Working lists - DataStructures::List constructedReplicasCulled, destroyedReplicasCulled; - - // This is used if QueryGroupDownloadMessages() returns true when ID_REPLICA_MANAGER_DOWNLOAD_STARTED arrives - // Packets will be gathered and not returned until ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE arrives - bool groupConstructionAndSerialize; - DataStructures::Queue downloadGroup; - void ClearDownloadGroup(RakPeerInterface *rakPeerInterface); - - friend class ReplicaManager3; -private: - Connection_RM3() {}; - - ConstructionMode constructionMode; -}; - -/// \brief Return codes for Connection_RM3::GetConstructionState() and Replica3::QueryConstruction() -/// \details Indicates what state the object should be in for the remote system -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3ConstructionState -{ - /// This object should exist on the remote system. Send a construction message if necessary - /// If the NetworkID is already in use, it will not do anything - /// If it is not in use, it will create the object, and then call DeserializeConstruction - RM3CS_SEND_CONSTRUCTION, - - /// This object should exist on the remote system. - /// The other system already has the object, and the object will never be deleted. - /// This is true of objects that are loaded with the level, for example. - /// Treat it as if it existed, without sending a construction message. - /// Will call Serialize() and SerializeConstructionExisting() to the object on the remote system - RM3CS_ALREADY_EXISTS_REMOTELY, - - /// Same as RM3CS_ALREADY_EXISTS_REMOTELY but does not call SerializeConstructionExisting() - RM3CS_ALREADY_EXISTS_REMOTELY_DO_NOT_CONSTRUCT, - - /// This object will never be sent to the target system - /// This object will never be serialized from this system to the target system - RM3CS_NEVER_CONSTRUCT, - - /// Don't do anything this tick. Will query again next tick - RM3CS_NO_ACTION, - - /// Max enum - RM3CS_MAX, -}; - -/// If this object already exists for this system, should it be removed? -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3DestructionState -{ - /// This object should not exist on the remote system. Send a destruction message if necessary. - RM3DS_SEND_DESTRUCTION, - - /// This object will never be destroyed by a per-tick query. Don't call again - RM3DS_DO_NOT_QUERY_DESTRUCTION, - - /// Don't do anything this tick. Will query again next tick - RM3DS_NO_ACTION, - - /// Max enum - RM3DS_MAX, -}; - -/// Return codes when constructing an object -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3SerializationResult -{ - /// This object serializes identically no matter who we send to - /// We also send it to every connection (broadcast). - /// Efficient for memory, speed, and bandwidth but only if the object is always broadcast identically. - RM3SR_BROADCAST_IDENTICALLY, - - /// Same as RM3SR_BROADCAST_IDENTICALLY, but assume the object needs to be serialized, do not check with a memcmp - /// Assume the object changed, and serialize it - /// Use this if you know exactly when your object needs to change. Can be faster than RM3SR_BROADCAST_IDENTICALLY. - /// An example of this is if every member variable has an accessor, changing a member sets a flag, and you check that flag in Replica3::QuerySerialization() - /// The opposite of this is RM3SR_DO_NOT_SERIALIZE, in case the object did not change - RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION, - - /// Either this object serializes differently depending on who we send to or we send it to some systems and not others. - /// Inefficient for memory and speed, but efficient for bandwidth - /// However, if you don't know what to return, return this - RM3SR_SERIALIZED_UNIQUELY, - - /// Do not compare against last sent value. Just send even if the data is the same as the last tick - /// If the data is always changing anyway, or you want to send unreliably, this is a good method of serialization - /// Can send unique data per connection if desired. If same data is sent to all connections, use RM3SR_SERIALIZED_ALWAYS_IDENTICALLY for even better performance - /// Efficient for memory and speed, but not necessarily bandwidth - RM3SR_SERIALIZED_ALWAYS, - - /// Even faster than RM3SR_SERIALIZED_ALWAYS - /// Serialize() will only be called for the first system. The remaining systems will get the same data as the first system. - RM3SR_SERIALIZED_ALWAYS_IDENTICALLY, - - /// Do not serialize this object this tick, for this connection. Will query again next autoserialize timer - RM3SR_DO_NOT_SERIALIZE, - - /// Never serialize this object for this connection - /// Useful for objects that are downloaded, and never change again - /// Efficient - RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION, - - /// Max enum - RM3SR_MAX, -}; - -/// First pass at topology to see if an object should be serialized -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3QuerySerializationResult -{ - /// Call Serialize() to see if this object should be serializable for this connection - RM3QSR_CALL_SERIALIZE, - /// Do not call Serialize() this tick to see if this object should be serializable for this connection - RM3QSR_DO_NOT_CALL_SERIALIZE, - /// Never call Serialize() for this object and connection. This system will not serialize this object for this topology - RM3QSR_NEVER_CALL_SERIALIZE, - /// Max enum - RM3QSR_MAX, -}; - -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3ActionOnPopConnection -{ - RM3AOPC_DO_NOTHING, - RM3AOPC_DELETE_REPLICA, - RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION, - RM3AOPC_MAX, -}; - -/// \ingroup REPLICA_MANAGER_GROUP3 -/// Used for Replica3::QueryConstruction_PeerToPeer() and Replica3::QuerySerialization_PeerToPeer() to describe how the object replicates between hosts -enum Replica3P2PMode -{ - /// The Replica3 instance is constructed and serialized by one system only. - /// Example: Your avatar. No other player serializes or can create your avatar. - R3P2PM_SINGLE_OWNER, - /// The Replica3 instance is constructed and/or serialized by different systems - /// This system is currently in charge of construction and/or serialization - /// Example: A pickup. When an avatar holds it, that avatar controls it. When it is on the ground, the host controls it. - R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE, - /// The Replica3 instance is constructed and/or serialized by different systems - /// Another system is in charge of construction and/or serialization, but this system may be in charge at a later time - /// Example: A pickup held by another player. That player sends creation of that object to new connections, and serializes it until it is dropped. - R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE, -}; - -/// \brief Base class for your replicated objects for the ReplicaManager3 system. -/// \details To use, derive your class, or a member of your class, from Replica3.
-/// \ingroup REPLICA_MANAGER_GROUP3 -class RAK_DLL_EXPORT Replica3 : public NetworkIDObject -{ -public: - Replica3(); - - /// Before deleting a local instance of Replica3, call Replica3::BroadcastDestruction() for the deletion notification to go out on the network. - /// It is not necessary to call ReplicaManager3::Dereference(), as this happens automatically in the destructor - virtual ~Replica3(); - - /// \brief Write a unique identifer that can be read on a remote system to create an object of this same class. - /// \details The value written to \a allocationIdBitstream will be passed to Connection_RM3::AllocReplica().
- /// Sample implementation:
- /// {allocationIdBitstream->Write(RakNet::RakString("Soldier");}
- /// \param[out] allocationIdBitstream Bitstream for the user to write to, to identify this class - virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const=0; - - /// \brief Ask if this object, which does not exist on \a destinationConnection should (now) be sent to that system. - /// \details If ReplicaManager3::QueryConstructionMode() returns QUERY_CONNECTION_FOR_REPLICA_LIST or QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION (default), - /// then QueyrConstruction() is called once per tick from ReplicaManager3::Update() to determine if an object should exist on a given system.
- /// Based on the return value, a network message may be sent to the other system to create the object.
- /// If QueryConstructionMode() is overriden to return QUERY_CONNECTION_FOR_REPLICA_LIST, this function is unused.
- /// \note Defaults are provided: QueryConstruction_PeerToPeer(), QueryConstruction_ServerConstruction(), QueryConstruction_ClientConstruction(). Return one of these functions for a working default for the relevant topology. - /// \param[in] destinationConnection Which system we will send to - /// \param[in] replicaManager3 Plugin instance for this Replica3 - /// \return What action to take - virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3)=0; - - /// \brief Ask if this object, which does exist on \a destinationConnection should be removed from the remote system - /// \details If ReplicaManager3::QueryConstructionMode() returns QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION (default), - /// then QueryDestruction() is called once per tick from ReplicaManager3::Update() to determine if an object that exists on a remote system should be destroyed for a given system.
- /// Based on the return value, a network message may be sent to the other system to destroy the object.
- /// Note that you can also destroy objects with BroadcastDestruction(), so this function is not useful unless you plan to delete objects for only a particular connection.
- /// If QueryConstructionMode() is overriden to return QUERY_CONNECTION_FOR_REPLICA_LIST, this function is unused.
- /// \param[in] destinationConnection Which system we will send to - /// \param[in] replicaManager3 Plugin instance for this Replica3 - /// \return What action to take. Only RM3CS_SEND_DESTRUCTION does anything at this time. - virtual RM3DestructionState QueryDestruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {(void) destinationConnection; (void) replicaManager3; return RM3DS_DO_NOT_QUERY_DESTRUCTION;} - - /// \brief We're about to call DeserializeConstruction() on this Replica3. If QueryRemoteConstruction() returns false, this object is deleted instead. - /// \details By default, QueryRemoteConstruction_ServerConstruction() does not allow clients to create objects. The client will get Replica3::DeserializeConstructionRequestRejected().
- /// If you want the client to be able to potentially create objects for client/server, override accordingly.
- /// Other variants of QueryRemoteConstruction_* just return true. - /// \note Defaults are provided: QueryRemoteConstruction_PeerToPeer(), QueryRemoteConstruction_ServerConstruction(), QueryRemoteConstruction_ClientConstruction(). Return one of these functions for a working default for the relevant topology. - /// \param[in] sourceConnection Which system sent us the object creation request message. - /// \return True to allow the object to pass onto DeserializeConstruction() (where it may also be rejected), false to immediately reject the remote construction request - virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection)=0; - - /// \brief We got a message from a connection to destroy this replica - /// Return true to automatically relay the destruction message to all our other connections - /// For a client in client/server, it does not matter what this funtion returns - /// For a server in client/server, this should normally return true - /// For a peer in peer to peer, you can normally return false since the original destroying peer would have told all other peers about the destruction - /// If a system gets a destruction command for an object that was already destroyed, the destruction message is ignored - virtual bool QueryRelayDestruction(Connection_RM3 *sourceConnection) const {(void) sourceConnection; return true;} - - /// \brief Write data to be sent only when the object is constructed on a remote system. - /// \details SerializeConstruction is used to write out data that you need to create this object in the context of your game, such as health, score, name. Use it for data you only need to send when the object is created.
- /// After SerializeConstruction() is called, Serialize() will be called immediately thereafter. However, they are sent in different messages, so Serialize() may arrive a later frame than SerializeConstruction() - /// For that reason, the object should be valid after a call to DeserializeConstruction() for at least a short time.
- /// \note The object's NetworkID and allocation id are handled by the system automatically, you do not need to write these values to \a constructionBitstream - /// \param[out] constructionBitstream Destination bitstream to write your data to - /// \param[in] destinationConnection System that will receive this network message. - virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection)=0; - - /// \brief Read data written by Replica3::SerializeConstruction() - /// \details Reads whatever data was written to \a constructionBitstream in Replica3::SerializeConstruction() - /// \param[out] constructionBitstream Bitstream written to in Replica3::SerializeConstruction() - /// \param[in] sourceConnection System that sent us this network message. - /// \return true to accept construction of the object. false to reject, in which case the object will be deleted via Replica3::DeallocReplica() - virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection)=0; - - /// Same as SerializeConstruction(), but for an object that already exists on the remote system. - /// Used if you return RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction - virtual void SerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;}; - - /// Same as DeserializeConstruction(), but for an object that already exists on the remote system. - /// Used if you return RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction - virtual void DeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;}; - - /// \brief Write extra data to send with the object deletion event, if desired - /// \details Replica3::SerializeDestruction() will be called to write any object destruction specific data you want to send with this event. - /// \a destructionBitstream can be read in DeserializeDestruction() - /// \param[out] destructionBitstream Bitstream for you to write to - /// \param[in] destinationConnection System that will receive this network message. - virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection)=0; - - /// \brief Read data written by Replica3::SerializeDestruction() - /// \details Return true to delete the object. BroadcastDestruction() will be called automatically, followed by ReplicaManager3::Dereference.
- /// Return false to not delete it. If you delete it at a later point, you are responsible for calling BroadcastDestruction() yourself. - virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection)=0; - - /// \brief The system is asking what to do with this replica when the connection is dropped - /// \details Return QueryActionOnPopConnection_Client, QueryActionOnPopConnection_Server, or QueryActionOnPopConnection_PeerToPeer - virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const=0; - - /// Notification called for each of our replicas when a connection is popped - virtual void OnPoppedConnection(RakNet::Connection_RM3 *droppedConnection) {(void) droppedConnection;} - - /// \brief Override with {delete this;} - /// \details - ///
    - ///
  1. Got a remote message to delete this object which passed DeserializeDestruction(), OR - ///
  2. ReplicaManager3::SetAutoManageConnections() was called autoDestroy true (which is the default setting), and a remote system that owns this object disconnected) OR - /// <\OL> - ///
    - /// Override with {delete this;} to actually delete the object (and any other processing you wish).
    - /// If you don't want to delete the object, just do nothing, however, the system will not know this so you are responsible for deleting it yoruself later.
    - /// destructionBitstream may be 0 if the object was deleted locally - virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection)=0; - - /// \brief Implement with QuerySerialization_ClientSerializable(), QuerySerialization_ServerSerializable(), or QuerySerialization_PeerToPeer() - /// \details QuerySerialization() is a first pass query to check if a given object should serializable to a given system. The intent is that the user implements with one of the defaults for client, server, or peer to peer.
    - /// Without this function, a careless implementation would serialize an object anytime it changed to all systems. This would give you feedback loops as the sender gets the same message back from the recipient it just sent to.
    - /// If more than one system can serialize the same object then you will need to override to return true, and control the serialization result from Replica3::Serialize(). Be careful not to send back the same data to the system that just sent to you! - /// \return True to allow calling Replica3::Serialize() for this connection, false to not call. - virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection)=0; - - /// \brief Called for each replica owned by the user, once per Serialization tick, before Serialize() is called. - /// If you want to do some kind of operation on the Replica objects that you own, just before Serialization(), then overload this function - virtual void OnUserReplicaPreSerializeTick(void) {} - - /// \brief Serialize our class to a bitstream - /// \details User should implement this function to write the contents of this class to SerializationParamters::serializationBitstream.
    - /// If data only needs to be written once, you can write it to SerializeConstruction() instead for efficiency.
    - /// Transmitted over the network if it changed from the last time we called Serialize().
    - /// Called every time the time interval to ReplicaManager3::SetAutoSerializeInterval() elapses and ReplicaManager3::Update is subsequently called. - /// \param[in/out] serializeParameters Parameters controlling the serialization, including destination bitstream to write to - /// \return Whether to serialize, and if so, how to optimize the results - virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters)=0; - - /// \brief Called when the class is actually transmitted via Serialize() - /// \details Use to track how much bandwidth this class it taking - virtual void OnSerializeTransmission(RakNet::BitStream *bitStream, const SystemAddress &systemAddress) {(void) bitStream; (void) systemAddress; } - - /// \brief Read what was written in Serialize() - /// \details Reads the contents of the class from SerializationParamters::serializationBitstream.
    - /// Called whenever Serialize() is called with different data from the last send. - /// \param[in] serializationBitstream Bitstream passed to Serialize() - /// \param[in] timeStamp 0 if unused, else contains the time the message originated on the remote system - /// \param[in] sourceConnection Which system sent to us - virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters)=0; - - /// \brief Called after SerializeConstruction completes for all objects in a given update tick.
    - /// Writes to PostDeserializeConstruction(), which is called after all objects are created for a given Construction tick(). - /// Override to send data to PostDeserializeConstruction(), such as the NetworkID of other objects to resolve pointers to - virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;} - - /// Called after DeserializeConstruction completes for all objects in a given update tick.
    - /// This is used to resolve dependency chains, where two objects would refer to each other in DeserializeConstruction, yet one had not been constructed yet - /// In PostDeserializeConstruction(), you know that all objects have already been created, so can resolve NetworkIDs to pointers safely. - /// You can also use it to trigger some sort of event when you know the object has completed deserialization. - /// \param[in] constructionBitstream BitStream written in PostSerializeConstruction() - /// \param[in] sourceConnection System that sent us this network message. - virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;} - - /// Same as PostSerializeConstruction(), but for objects that returned RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction - virtual void PostSerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;} - - /// Same as PostDeserializeConstruction(), but for objects that returned RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction - virtual void PostDeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;} - - /// Called after DeserializeDestruction completes for the object successfully, but obviously before the object is deleted.
    - /// Override to trigger some sort of event when you know the object has completed destruction. - /// \param[in] sourceConnection System that sent us this network message. - virtual void PreDestruction(RakNet::Connection_RM3 *sourceConnection) {(void) sourceConnection;} - - /// \brief Default call for QueryConstruction(). - /// \details Both the client and the server is allowed to create this object. The network topology is client/server - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual RM3ConstructionState QueryConstruction_ClientConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); - /// Default call for QueryRemoteConstruction(). - /// \details Both the client and the server is allowed to create this object. The network topology is client/server - /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual bool QueryRemoteConstruction_ClientConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer); - - /// \brief Default call for QueryConstruction(). - /// \details Only the server is allowed to create this object. The network topology is client/server - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual RM3ConstructionState QueryConstruction_ServerConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); - /// \brief Default call for QueryRemoteConstruction(). Allow the server to create this object, but not the client. - /// \details Only the server is allowed to create this object. The network topology is client/server - /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual bool QueryRemoteConstruction_ServerConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer); - - /// \brief Default call for QueryConstruction(). - /// \details All clients are allowed to create all objects. The object is not relayed when remotely created - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] p2pMode If controlled only by this system ever, pass R3P2PM_SINGLE_OWNER. Otherwise pass R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE or R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE - virtual RM3ConstructionState QueryConstruction_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode=R3P2PM_SINGLE_OWNER); - /// \brief Default call for QueryRemoteConstruction(). - /// \details All clients are allowed to create all objects. The object is not relayed when remotely created - /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() - virtual bool QueryRemoteConstruction_PeerToPeer(RakNet::Connection_RM3 *sourceConnection); - - /// \brief Default call for QuerySerialization(). - /// \details Use if the values you are serializing are generated by the client that owns the object. The serialization will be relayed through the server to the other clients. - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual RakNet::RM3QuerySerializationResult QuerySerialization_ClientSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); - /// \brief Default call for QuerySerialization(). - /// \details Use if the values you are serializing are generated only by the server. The serialization will be sent to all clients, but the clients will not send back to the server. - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual RakNet::RM3QuerySerializationResult QuerySerialization_ServerSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); - /// \brief Default call for QuerySerialization(). - /// \details Use if the values you are serializing are on a peer to peer network. The peer that owns the object will send to all. Remote peers will not send. - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] p2pMode If controlled only by this system ever, pass R3P2PM_SINGLE_OWNER. Otherwise pass R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE or R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE - virtual RakNet::RM3QuerySerializationResult QuerySerialization_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode=R3P2PM_SINGLE_OWNER); - - /// Default: If we are a client, and the connection is lost, delete the server's objects - virtual RM3ActionOnPopConnection QueryActionOnPopConnection_Client(RakNet::Connection_RM3 *droppedConnection) const; - /// Default: If we are a client, and the connection is lost, delete the client's objects and broadcast the destruction - virtual RM3ActionOnPopConnection QueryActionOnPopConnection_Server(RakNet::Connection_RM3 *droppedConnection) const; - /// Default: If we are a peer, and the connection is lost, delete the peer's objects - virtual RM3ActionOnPopConnection QueryActionOnPopConnection_PeerToPeer(RakNet::Connection_RM3 *droppedConnection) const; - - /// Call to send a network message to delete this object on other systems.
    - /// Call it before deleting the object - virtual void BroadcastDestruction(void); - - /// creatingSystemGUID is set the first time Reference() is called, or if we get the object from another system - /// \return System that originally created this object - RakNetGUID GetCreatingSystemGUID(void) const; - - /// \return If ReplicaManager3::Reference() was called on this object. - bool WasReferenced(void) const {return replicaManager!=0;} - - /// GUID of the system that first called Reference() on this object. - /// Transmitted automatically when the object is constructed - RakNetGUID creatingSystemGUID; - /// GUID of the system that caused the item to send a deletion command over the network - RakNetGUID deletingSystemGUID; - - /// \internal - /// ReplicaManager3 plugin associated with this object - ReplicaManager3 *replicaManager; - - LastSerializationResultBS lastSentSerialization; - RakNet::Time whenLastSerialized; - bool forceSendUntilNextUpdate; -}; - -/// \brief Use Replica3 through composition instead of inheritance by containing an instance of this templated class -/// Calls to parent class for all functions -/// Parent class must still define and functions though! -/// \pre Parent class must call SetParent() on this object -template -class RAK_DLL_EXPORT Replica3Composite : public Replica3 -{ -protected: - parent_type *r3CompositeOwner; -public: - void SetCompositeOwner(parent_type *p) {r3CompositeOwner=p;} - parent_type* GetCompositeOwner(void) const {return r3CompositeOwner;}; - virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const {r3CompositeOwner->WriteAllocationID(destinationConnection, allocationIdBitstream);} - virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {return r3CompositeOwner->QueryConstruction(destinationConnection, replicaManager3);} - virtual RM3DestructionState QueryDestruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {return r3CompositeOwner->QueryDestruction(destinationConnection, replicaManager3);} - virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->QueryRemoteConstruction(sourceConnection);} - virtual bool QueryRelayDestruction(Connection_RM3 *sourceConnection) const {return r3CompositeOwner->QueryRelayDestruction(sourceConnection);} - virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeConstruction(constructionBitstream, destinationConnection);} - virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->DeserializeConstruction(constructionBitstream, sourceConnection);} - virtual void SerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeConstructionExisting(constructionBitstream, destinationConnection);} - virtual void DeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->DeserializeConstructionExisting(constructionBitstream, sourceConnection);} - virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeDestruction(destructionBitstream, destinationConnection);} - virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->DeserializeDestruction(destructionBitstream, sourceConnection);} - virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {return r3CompositeOwner->QueryActionOnPopConnection(droppedConnection);} - virtual void OnPoppedConnection(RakNet::Connection_RM3 *droppedConnection) {r3CompositeOwner->OnPoppedConnection(droppedConnection);} - virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->DeallocReplica(sourceConnection);} - virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {return r3CompositeOwner->QuerySerialization(destinationConnection);} - virtual void OnUserReplicaPreSerializeTick(void) {r3CompositeOwner->OnUserReplicaPreSerializeTick();} - virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters) {return r3CompositeOwner->Serialize(serializeParameters);} - virtual void OnSerializeTransmission(RakNet::BitStream *bitStream, const SystemAddress &systemAddress) {r3CompositeOwner->OnSerializeTransmission(bitStream, systemAddress);} - virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters) {r3CompositeOwner->Deserialize(deserializeParameters);} - virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->PostSerializeConstruction(constructionBitstream, destinationConnection);} - virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PostDeserializeConstruction(constructionBitstream, sourceConnection);} - virtual void PostSerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->PostSerializeConstructionExisting(constructionBitstream, destinationConnection);} - virtual void PostDeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PostDeserializeConstructionExisting(constructionBitstream, sourceConnection);} - virtual void PreDestruction(RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PreDestruction(sourceConnection);} -}; - -} // namespace RakNet - - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains the third iteration of the ReplicaManager class. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ReplicaManager3==1 + +#ifndef __REPLICA_MANAGER_3 +#define __REPLICA_MANAGER_3 + +#include "RakNetTypes.h" +#include "RakNetTime.h" +#include "BitStream.h" +#include "PacketPriority.h" +#include "PluginInterface2.h" +#include "NetworkIDObject.h" +#include "DS_OrderedList.h" +#include "DS_Queue.h" + +/// \defgroup REPLICA_MANAGER_GROUP3 ReplicaManager3 +/// \brief Third implementation of object replication +/// \details +/// \ingroup REPLICA_MANAGER_GROUP + +namespace RakNet +{ +class Connection_RM3; +class Replica3; + + +/// \internal +/// \ingroup REPLICA_MANAGER_GROUP3 +struct PRO +{ + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketPriority(). + PacketPriority priority; + + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketReliability(). + PacketReliability reliability; + + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultOrderingChannel(). + char orderingChannel; + + /// Passed to RakPeerInterface::Send(). Defaults to 0. + uint32_t sendReceipt; + + bool operator==( const PRO& right ) const; + bool operator!=( const PRO& right ) const; +}; + + +/// \brief System to help automate game object construction, destruction, and serialization +/// \details ReplicaManager3 tracks your game objects and automates the networking for replicating them across the network
    +/// As objects are created, destroyed, or serialized differently, those changes are pushed out to other systems.
    +/// To use:
    +///
      +///
    1. Derive from Connection_RM3 and implement Connection_RM3::AllocReplica(). This is a factory function where given a user-supplied identifier for a class (such as name) return an instance of that class. Should be able to return any networked object in your game. +///
    2. Derive from ReplicaManager3 and implement AllocConnection() and DeallocConnection() to return the class you created in step 1. +///
    3. Derive your networked game objects from Replica3. All pure virtuals have to be implemented, however defaults are provided for Replica3::QueryConstruction(), Replica3::QueryRemoteConstruction(), and Replica3::QuerySerialization() depending on your network architecture. +///
    4. When a new game object is created on the local system, pass it to ReplicaManager3::Reference(). +///
    5. When a game object is destroyed on the local system, and you want other systems to know about it, call Replica3::BroadcastDestruction() +///
    +///
    +/// At this point, all new connections will automatically download, get construction messages, get destruction messages, and update serialization automatically. +/// \ingroup REPLICA_MANAGER_GROUP3 +class RAK_DLL_EXPORT ReplicaManager3 : public PluginInterface2 +{ +public: + ReplicaManager3(); + virtual ~ReplicaManager3(); + + /// \brief Implement to return a game specific derivation of Connection_RM3 + /// \details The connection object represents a remote system connected to you that is using the ReplicaManager3 system.
    + /// It has functions to perform operations per-connection.
    + /// AllocConnection() and DeallocConnection() are factory functions to create and destroy instances of the connection object.
    + /// It is used if autoCreate is true via SetAutoManageConnections() (true by default). Otherwise, the function is not called, and you will have to call PushConnection() manually
    + /// \note If you do not want a new network connection to immediately download game objects, SetAutoManageConnections() and PushConnection() are how you do this. + /// \sa SetAutoManageConnections() + /// \param[in] systemAddress Address of the system you are adding + /// \param[in] rakNetGUID GUID of the system you are adding. See Packet::rakNetGUID or RakPeerInterface::GetGUIDFromSystemAddress() + /// \return The new connection instance. + virtual Connection_RM3* AllocConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID) const=0; + + /// \brief Implement to destroy a class instanced returned by AllocConnection() + /// \details Most likely just implement as {delete connection;}
    + /// It is used if autoDestroy is true via SetAutoManageConnections() (true by default). Otherwise, the function is not called and you would then be responsible for deleting your own connection objects. + /// \param[in] connection The pointer instance to delete + virtual void DeallocConnection(Connection_RM3 *connection) const=0; + + /// \brief Enable or disable automatically assigning connections to new instances of Connection_RM3 + /// \details ReplicaManager3 can automatically create and/or destroy Connection_RM3 as systems connect or disconnect from RakPeerInterface.
    + /// By default this is on, to make the system easier to learn and setup.
    + /// If you don't want all connections to take part in the game, or you want to delay when a connection downloads the game, set \a autoCreate to false.
    + /// If you want to delay deleting a connection that has dropped, set \a autoDestroy to false. If you do this, then you must call PopConnection() to remove that connection from being internally tracked. You'll also have to delete the connection instance on your own.
    + /// \param[in] autoCreate Automatically call ReplicaManager3::AllocConnection() for each new connection. Defaults to true. + /// \param[in] autoDestroy Automatically call ReplicaManager3::DeallocConnection() for each dropped connection. Defaults to true. + void SetAutoManageConnections(bool autoCreate, bool autoDestroy); + + /// \brief Track a new Connection_RM3 instance + /// \details If \a autoCreate is false for SetAutoManageConnections(), then you need this function to add new instances of Connection_RM3 yourself.
    + /// You don't need to track this pointer yourself, you can get it with GetConnectionAtIndex(), GetConnectionByGUID(), or GetConnectionBySystemAddress().
    + /// \param[in] newConnection The new connection instance to track. + bool PushConnection(RakNet::Connection_RM3 *newConnection); + + /// \brief Stop tracking a connection + /// \details On call, for each replica returned by GetReplicasCreatedByGuid(), QueryActionOnPopConnection() will be called. Depending on the return value, this may delete the corresponding replica.
    + /// If autoDestroy is true in the call to SetAutoManageConnections() (true by default) then this is called automatically when the connection is lost. In that case, the returned connection instance is deleted.
    + /// \param[in] guid of the connection to get. Passed to ReplicaManager3::AllocConnection() originally. + RakNet::Connection_RM3 * PopConnection(RakNetGUID guid); + + /// \brief Adds a replicated object to the system. + /// \details Anytime you create a new object that derives from Replica3, and you want ReplicaManager3 to use it, pass it to Reference().
    + /// Remote systems already connected will potentially download this object the next time ReplicaManager3::Update() is called, which happens every time you call RakPeerInterface::Receive().
    + /// You can also call ReplicaManager3::Update() manually to send referenced objects right away + /// \param[in] replica3 The object to start tracking + void Reference(RakNet::Replica3 *replica3); + + /// \brief Removes a replicated object from the system. + /// \details The object is not deallocated, it is up to the caller to do so.
    + /// This is called automatically from the destructor of Replica3, so you don't need to call it manually unless you want to stop tracking an object before it is destroyed. + /// \param[in] replica3 The object to stop tracking + void Dereference(RakNet::Replica3 *replica3); + + /// \brief Removes multiple replicated objects from the system. + /// \details Same as Dereference(), but for a list of objects.
    + /// Useful with the lists returned by GetReplicasCreatedByGuid(), GetReplicasCreatedByMe(), or GetReferencedReplicaList().
    + /// \param[in] replicaListIn List of objects + void DereferenceList(DataStructures::List &replicaListIn); + + /// \brief Returns all objects originally created by a particular system + /// \details Originally created is defined as the value of Replica3::creatingSystemGUID, which is automatically assigned in ReplicaManager3::Reference().
    + /// You do not have to be directly connected to that system to get the objects originally created by that system.
    + /// \param[in] guid GUID of the system we are referring to. Originally passed as the \a guid parameter to ReplicaManager3::AllocConnection() + /// \param[out] List of Replica3 instances to be returned + void GetReplicasCreatedByGuid(RakNetGUID guid, DataStructures::List &replicaListOut); + + /// \brief Returns all objects originally created by your system + /// \details Calls GetReplicasCreatedByGuid() for your own system guid. + /// \param[out] List of Replica3 instances to be returned + void GetReplicasCreatedByMe(DataStructures::List &replicaListOut); + + /// \brief Returns the entire list of Replicas that we know about. + /// \details This is all Replica3 instances passed to Reference, as well as instances we downloaded and created via Connection_RM3::AllocReference() + /// \param[out] List of Replica3 instances to be returned + void GetReferencedReplicaList(DataStructures::List &replicaListOut); + + /// \brief Returns the number of replicas known about + /// \details Returns the size of the list that would be returned by GetReferencedReplicaList() + /// \return How many replica objects are in the list of replica objects + unsigned GetReplicaCount(void) const; + + /// \brief Returns a replica by index + /// \details Returns one of the items in the list that would be returned by GetReferencedReplicaList() + /// \param[in] index An index, from 0 to GetReplicaCount()-1. + /// \return A Replica3 instance + Replica3 *GetReplicaAtIndex(unsigned index); + + /// \brief Returns the number of connections + /// \details Returns the number of connections added with ReplicaManager3::PushConnection(), minus the number removed with ReplicaManager3::PopConnection() + /// \return The number of registered connections + unsigned int GetConnectionCount(void) const; + + /// \brief Returns a connection pointer previously added with PushConnection() + /// \param[in] index An index, from 0 to GetConnectionCount()-1. + /// \return A Connection_RM3 pointer + Connection_RM3* GetConnectionAtIndex(unsigned index) const; + + /// \brief Returns a connection pointer previously added with PushConnection() + /// \param[in] sa The system address of the connection to return + /// \return A Connection_RM3 pointer, or 0 if not found + Connection_RM3* GetConnectionBySystemAddress(const SystemAddress &sa) const; + + /// \brief Returns a connection pointer previously added with PushConnection.() + /// \param[in] guid The guid of the connection to return + /// \return A Connection_RM3 pointer, or 0 if not found + Connection_RM3* GetConnectionByGUID(RakNetGUID guid) const; + + /// \param[in] Default ordering channel to use for object creation, destruction, and serializations + void SetDefaultOrderingChannel(char def); + + /// \param[in] Default packet priority to use for object creation, destruction, and serializations + void SetDefaultPacketPriority(PacketPriority def); + + /// \param[in] Default packet reliability to use for object creation, destruction, and serializations + void SetDefaultPacketReliability(PacketReliability def); + + /// \details Every \a intervalMS milliseconds, Connection_RM3::OnAutoserializeInterval() will be called.
    + /// Defaults to 30.
    + /// Pass with <0 to disable. Pass 0 to Serialize() every time RakPeer::Recieve() is called
    + /// If you want to control the update interval with more granularity, use the return values from Replica3::Serialize().
    + /// \param[in] intervalMS How frequently to autoserialize all objects. This controls the maximum number of game object updates per second. + void SetAutoSerializeInterval(RakNet::Time intervalMS); + + /// \brief Return the connections that we think have an instance of the specified Replica3 instance + /// \details This can be wrong, for example if that system locally deleted the outside the scope of ReplicaManager3, if QueryRemoteConstruction() returned false, or if DeserializeConstruction() returned false. + /// \param[in] replica The replica to check against. + /// \param[out] connectionsThatHaveConstructedThisReplica Populated with connection instances that we believe have \a replica allocated + void GetConnectionsThatHaveReplicaConstructed(Replica3 *replica, DataStructures::List &connectionsThatHaveConstructedThisReplica); + + /// \brief Defines the unique instance of ReplicaManager3 if multiple instances are on the same instance of RakPeerInterface + /// \details ReplicaManager3 supports multiple instances of itself attached to the same instance of rakPeer, in case your game has multiple worlds.
    + /// Call SetWorldID with a different number for each instance.
    + /// The default worldID is 0.
    + /// To use multiple worlds, you will also need to call ReplicaManager3::SetNetworkIDManager() to have a different NetworkIDManager instance per world + void SetWorldID(unsigned char id); + + /// \return Whatever was passed to SetWorldID(), or 0 if it was never called. + unsigned char GetWorldID(void) const; + + /// \details Sets the networkIDManager instance that this plugin relys upon.
    + /// Uses whatever instance is attached to RakPeerInterface if unset.
    + /// To support multiple worlds, you should set it to a different manager for each instance of the plugin + /// \param[in] _networkIDManager The externally allocated NetworkIDManager instance for this plugin to use. + void SetNetworkIDManager(NetworkIDManager *_networkIDManager); + + /// Returns what was passed to SetNetworkIDManager(), or the instance on RakPeerInterface if unset. + NetworkIDManager *GetNetworkIDManager(void) const; + + /// \details Send a network command to destroy one or more Replica3 instances + /// Usually you won't need this, but use Replica3::BroadcastDestruction() instead. + /// The objects are unaffected locally + /// \param[in] replicaList List of Replica3 objects to tell other systems to destroy. + /// \param[in] exclusionAddress Which system to not send to. UNASSIGNED_SYSTEM_ADDRESS to send to all. + void BroadcastDestructionList(DataStructures::List &replicaListSource, const SystemAddress &exclusionAddress); + + /// \internal + /// \details Tell other systems that have this replica to destroy this replica.
    + /// You shouldn't need to call this, as it happens in the Replica3 destructor + void BroadcastDestruction(Replica3 *replica, const SystemAddress &exclusionAddress); + + /// \internal + /// \details Frees internal lists.
    + /// Externally allocated pointers are not deallocated + void Clear(void); + + /// \internal + PRO GetDefaultSendParameters(void) const; + + /// Call interfaces, send data + virtual void Update(void); +protected: + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + virtual void OnRakPeerShutdown(void); + virtual void OnDetach(void); + + PluginReceiveResult OnConstruction(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); + PluginReceiveResult OnSerialize(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, RakNet::Time timestamp, unsigned char packetDataOffset); + PluginReceiveResult OnDownloadStarted(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); + PluginReceiveResult OnDownloadComplete(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); + + void DeallocReplicaNoBroadcastDestruction(RakNet::Connection_RM3 *connection, RakNet::Replica3 *replica3); + RakNet::Connection_RM3 * PopConnection(unsigned int index); + Replica3* GetReplicaByNetworkID(NetworkID networkId); + unsigned int ReferenceInternal(RakNet::Replica3 *replica3); + + DataStructures::List connectionList; + DataStructures::List userReplicaList; + + PRO defaultSendParameters; + RakNet::Time autoSerializeInterval; + RakNet::Time lastAutoSerializeOccurance; + unsigned char worldId; + NetworkIDManager *networkIDManager; + bool autoCreateConnections, autoDestroyConnections; + Replica3 *currentlyDeallocatingReplica; + + friend class Connection_RM3; +}; + +static const int RM3_NUM_OUTPUT_BITSTREAM_CHANNELS=16; + +/// \ingroup REPLICA_MANAGER_GROUP3 +struct LastSerializationResultBS +{ + RakNet::BitStream bitStream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; +}; + +/// Represents the serialized data for an object the last time it was sent. Used by Connection_RM3::OnAutoserializeInterval() and Connection_RM3::SendSerializeIfChanged() +/// \ingroup REPLICA_MANAGER_GROUP3 +struct LastSerializationResult +{ + LastSerializationResult(); + ~LastSerializationResult(); + + /// The replica instance we serialized + RakNet::Replica3 *replica; + //bool neverSerialize; +// bool isConstructed; + + void AllocBS(void); + LastSerializationResultBS* lastSerializationResultBS; +}; + +/// Parameters passed to Replica3::Serialize() +/// \ingroup REPLICA_MANAGER_GROUP3 +struct SerializeParameters +{ + /// Write your output for serialization here + /// If nothing is written, the serialization will not occur + /// Write to any or all of the NUM_OUTPUT_BITSTREAM_CHANNELS channels available. Channels can hold independent data + RakNet::BitStream outputBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + + /// Last bitstream we sent for this replica to this system. + /// Read, but DO NOT MODIFY + RakNet::BitStream* lastSentBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + + /// Set to non-zero to transmit a timestamp with this message. + /// Defaults to 0 + /// Use RakNet::GetTime() for this + RakNet::Time messageTimestamp; + + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketPriority(). + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketReliability(). + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultOrderingChannel(). + PRO pro[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + + /// Passed to RakPeerInterface::Send(). + RakNet::Connection_RM3 *destinationConnection; + + /// For prior serializations this tick, for the same connection, how many bits have we written so far? + /// Use this to limit how many objects you send to update per-tick if desired + BitSize_t bitsWrittenSoFar; + + /// When this object was last serialized to the connection + /// 0 means never + RakNet::Time whenLastSerialized; + + /// Current time, in milliseconds. + /// curTime - whenLastSerialized is how long it has been since this object was last sent + RakNet::Time curTime; +}; + +/// \ingroup REPLICA_MANAGER_GROUP3 +struct DeserializeParameters +{ + RakNet::BitStream serializationBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + bool bitstreamWrittenTo[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + RakNet::Time timeStamp; + RakNet::Connection_RM3 *sourceConnection; +}; + +/// \ingroup REPLICA_MANAGER_GROUP3 +enum SendSerializeIfChangedResult +{ + SSICR_SENT_DATA, + SSICR_DID_NOT_SEND_DATA, + SSICR_NEVER_SERIALIZE, +}; + +/// \brief Each remote system is represented by Connection_RM3. Used to allocate Replica3 and track which instances have been allocated +/// \details Important function: AllocReplica() - must be overridden to create an object given an identifier for that object, which you define for all objects in your game +/// \ingroup REPLICA_MANAGER_GROUP3 +class RAK_DLL_EXPORT Connection_RM3 +{ +public: + + Connection_RM3(const SystemAddress &_systemAddress, RakNetGUID _guid); + virtual ~Connection_RM3(); + + /// \brief Class factory to create a Replica3 instance, given a user-defined identifier + /// \details Identifier is returned by Replica3::WriteAllocationID() for what type of class to create.
    + /// This is called when you download a replica from another system.
    + /// See Replica3::Dealloc for the corresponding destruction message.
    + /// Return 0 if unable to create the intended object. Note, in that case the other system will still think we have the object and will try to serialize object updates to us. Generally, you should not send objects the other system cannot create.
    + /// \sa Replica3::WriteAllocationID(). + /// Sample implementation:
    + /// {RakNet::RakString typeName; allocationIdBitstream->Read(typeName); if (typeName=="Soldier") return new Soldier; return 0;}
    + /// \param[in] allocationIdBitstream user-defined bitstream uniquely identifying a game object type + /// \param[in] replicaManager3 Instance of ReplicaManager3 that controls this connection + /// \return The new replica instance + virtual Replica3 *AllocReplica(RakNet::BitStream *allocationIdBitstream, ReplicaManager3 *replicaManager3)=0; + + /// \brief Get list of all replicas that are constructed for this connection + /// \param[out] objectsTheyDoHave Destination list. Returned in sorted ascending order, sorted on the value of the Replica3 pointer. + virtual void GetConstructedReplicas(DataStructures::List &objectsTheyDoHave); + + /// Returns true if we think this remote connection has this replica constructed + /// \param[in] replica3 Which replica we are querying + /// \return True if constructed, false othewise + bool HasReplicaConstructed(RakNet::Replica3 *replica); + + /// When a new connection connects, before sending any objects, SerializeOnDownloadStarted() is called + /// \param[out] bitStream Passed to DeserializeOnDownloadStarted() + virtual void SerializeOnDownloadStarted(RakNet::BitStream *bitStream) {(void) bitStream;} + + /// Receives whatever was written in SerializeOnDownloadStarted() + /// \param[in] bitStream Written in SerializeOnDownloadStarted() + virtual void DeserializeOnDownloadStarted(RakNet::BitStream *bitStream) {(void) bitStream;} + + /// When a new connection connects, after constructing and serialization all objects, SerializeOnDownloadComplete() is called + /// \param[out] bitStream Passed to DeserializeOnDownloadComplete() + virtual void SerializeOnDownloadComplete(RakNet::BitStream *bitStream) {(void) bitStream;} + + /// Receives whatever was written in DeserializeOnDownloadComplete() + /// \param[in] bitStream Written in SerializeOnDownloadComplete() + virtual void DeserializeOnDownloadComplete(RakNet::BitStream *bitStream) {(void) bitStream;} + + /// \return The system address passed to the constructor of this object + SystemAddress GetSystemAddress(void) const {return systemAddress;} + + /// \return Returns the RakNetGUID passed to the constructor of this object + RakNetGUID GetRakNetGUID(void) const {return guid;} + + /// List of enumerations for how to get the list of valid objects for other systems + enum ConstructionMode + { + /// For every object that does not exist on the remote system, call Replica3::QueryConstruction() every tick. + /// Do not call Replica3::QueryDestruction() + /// Do not call Connection_RM3::QueryReplicaList() + QUERY_REPLICA_FOR_CONSTRUCTION, + + /// For every object that does not exist on the remote system, call Replica3::QueryConstruction() every tick. Based on the call, the object may be sent to the other system. + /// For every object that does exist on the remote system, call Replica3::QueryDestruction() every tick. Based on the call, the object may be deleted on the other system. + /// Do not call Connection_RM3::QueryReplicaList() + QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION, + + /// Do not call Replica3::QueryConstruction() or Replica3::QueryDestruction() + /// Call Connection_RM3::QueryReplicaList() to determine which objects exist on remote systems + /// This can be faster than QUERY_REPLICA_FOR_CONSTRUCTION and QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION for large worlds + /// See GridSectorizer.h under /Source for code that can help with this + QUERY_CONNECTION_FOR_REPLICA_LIST + }; + + /// \brief Return whether or not downloads to our system should all be processed the same tick (call to RakPeer::Receive() ) + /// \details Normally the system will send ID_REPLICA_MANAGER_DOWNLOAD_STARTED, ID_REPLICA_MANAGER_CONSTRUCTION for all downloaded objects, + /// ID_REPLICA_MANAGER_SERIALIZE for each downloaded object, and lastly ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE. + /// This enables the application to show a downloading splash screen on ID_REPLICA_MANAGER_DOWNLOAD_STARTED, a progress bar, and to close the splash screen and activate all objects on ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE + /// However, if the application was not set up for this then it would result in incomplete objects spread out over time, and cause problems + /// If you return true from QueryGroupDownloadMessages(), then these messages will be returned all in one tick, returned only when the download is complete + /// \note ID_REPLICA_MANAGER_DOWNLOAD_STARTED calls the callback DeserializeOnDownloadStarted() + /// \note ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE calls the callback DeserializeOnDownloadComplete() + virtual bool QueryGroupDownloadMessages(void) const {return false;} + + /// \brief Queries how to get the list of objects that exist on remote systems + /// \details The default of calling QueryConstruction for every known object is easy to use, but not efficient, especially for large worlds where many objects are outside of the player's circle of influence.
    + /// QueryDestruction is also not necessarily useful or efficient, as object destruction tends to happen in known cases, and can be accomplished by calling Replica3::BroadcastDestruction() + /// QueryConstructionMode() allows you to specify more efficient algorithms than the default when overriden. + /// \return How to get the list of objects that exist on the remote system. You should always return the same value for a given connection + virtual ConstructionMode QueryConstructionMode(void) const {return QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION;} + + /// \brief Callback used when QueryConstructionMode() returns QUERY_CONNECTION_FOR_REPLICA_LIST + /// \details This advantage of this callback is if that there are many objects that a particular connection does not have, then we do not have to iterate through those + /// objects calling QueryConstruction() for each of them.
    + ///
    + /// The following code uses a sorted merge sort to quickly find new and deleted objects, given a list of objects we know should exist.
    + ///
    + /// DataStructures::List objectsTheyShouldHave; // You have to fill in this list
    + /// DataStructures::List objectsTheyCurrentlyHave,objectsTheyStillHave,existingReplicasToDestro,newReplicasToCreatey;
    + /// GetConstructedReplicas(objectsTheyCurrentlyHave);
    + /// DataStructures::Multilist::FindIntersection(objectsTheyCurrentlyHave, objectsTheyShouldHave, objectsTheyStillHave, existingReplicasToDestroy, newReplicasToCreate);
    + ///
    + /// See GridSectorizer in the Source directory as a method to find all objects within a certain radius in a fast way.
    + ///
    + /// \param[out] newReplicasToCreate Anything in this list will be created on the remote system + /// \param[out] existingReplicasToDestroy Anything in this list will be destroyed on the remote system + virtual void QueryReplicaList( + DataStructures::List newReplicasToCreate, + DataStructures::List existingReplicasToDestroy) {} + + /// \internal This is used internally - however, you can also call it manually to send a data update for a remote replica.
    + /// \brief Sends over a serialization update for \a replica.
    + /// NetworkID::GetNetworkID() is written automatically, serializationData is the object data.
    + /// \param[in] replica Which replica to serialize + /// \param[in] serializationData Serialized object data + /// \param[in] timestamp 0 means no timestamp. Otherwise message is prepended with ID_TIMESTAMP + /// \param[in] sendParameters Parameters on how to send + /// \param[in] rakPeer Instance of RakPeerInterface to send on + /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() + virtual SendSerializeIfChangedResult SendSerialize(RakNet::Replica3 *replica, bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::BitStream serializationData[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::Time timestamp, PRO sendParameters[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::RakPeerInterface *rakPeer, unsigned char worldId); + + /// \internal + /// \details Calls Connection_RM3::SendSerialize() if Replica3::Serialize() returns a different result than what is contained in \a lastSerializationResult.
    + /// Used by autoserialization in Connection_RM3::OnAutoserializeInterval() + /// \param[in] queryToSerializeIndex Index into queryToSerializeReplicaList for whichever replica this is + /// \param[in] sp Controlling parameters over the serialization + /// \param[in] rakPeer Instance of RakPeerInterface to send on + /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() + virtual SendSerializeIfChangedResult SendSerializeIfChanged(unsigned int queryToSerializeIndex, SerializeParameters *sp, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager); + + /// \internal + /// \brief Given a list of objects that were created and destroyed, serialize and send them to another system. + /// \param[in] newObjects Objects to serialize construction + /// \param[in] deletedObjects Objects to serialize destruction + /// \param[in] sendParameters Controlling parameters over the serialization + /// \param[in] rakPeer Instance of RakPeerInterface to send on + /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() + /// \param[in] replicaManager3 ReplicaManager3 instance + virtual void SendConstruction(DataStructures::List &newObjects, DataStructures::List &deletedObjects, PRO sendParameters, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager3); + + /// \internal + /// Remove from \a newObjectsIn objects that already exist and save to \a newObjectsOut + /// Remove from \a deletedObjectsIn objects that do not exist, and save to \a deletedObjectsOut + void CullUniqueNewAndDeletedObjects(DataStructures::List &newObjectsIn, + DataStructures::List &deletedObjectsIn, + DataStructures::List &newObjectsOut, + DataStructures::List &deletedObjectsOut); + + /// \internal + void SendValidation(RakNet::RakPeerInterface *rakPeer, unsigned char worldId); + + /// \internal + void AutoConstructByQuery(ReplicaManager3 *replicaManager3); + + + // Internal - does the other system have this connection too? Validated means we can now use it + bool isValidated; + // Internal - Used to see if we should send download started + bool isFirstConstruction; + + static int Replica3LSRComp( Replica3 * const &replica3, LastSerializationResult * const &data ); + +protected: + + SystemAddress systemAddress; + RakNetGUID guid; + + /* + Operations: + + Locally reference a new replica: + Add to queryToConstructReplicaList for all objects + + Add all objects to queryToConstructReplicaList + + Download: + Add to constructedReplicaList for connection that send the object to us + Add to queryToSerializeReplicaList for connection that send the object to us + Add to queryToConstructReplicaList for all other connections + + Never construct for this connection: + Remove from queryToConstructReplicaList + + Construct to this connection + Remove from queryToConstructReplicaList + Add to constructedReplicaList for this connection + Add to queryToSerializeReplicaList for this connection + + Serialize: + Iterate through queryToSerializeReplicaList + + Never serialize for this connection + Remove from queryToSerializeReplicaList + + Reference (this system has this object already) + Remove from queryToConstructReplicaList + Add to constructedReplicaList for this connection + Add to queryToSerializeReplicaList for this connection + + Downloaded an existing object + if replica is in queryToConstructReplicaList, OnConstructToThisConnection() + else ignore + + Send destruction from query + Remove from queryToDestructReplicaList + Remove from queryToSerializeReplicaList + Remove from constructedReplicaList + Add to queryToConstructReplicaList + + Do not query destruction again + Remove from queryToDestructReplicaList + */ + void OnLocalReference(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnDereference(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnDownloadFromThisSystem(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnDownloadFromOtherSystem(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnNeverConstruct(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); + void OnConstructToThisConnection(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); + void OnConstructToThisConnection(Replica3 *replica, ReplicaManager3 *replicaManager); + void OnNeverSerialize(unsigned int queryToSerializeIndex, ReplicaManager3 *replicaManager); + void OnReplicaAlreadyExists(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); + void OnDownloadExisting(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnSendDestructionFromQuery(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager); + void OnDoNotQueryDestruction(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager); + void ValidateLists(ReplicaManager3 *replicaManager) const; + void SendSerializeHeader(RakNet::Replica3 *replica, RakNet::Time timestamp, RakNet::BitStream *bs, unsigned char worldId); + + // The list of objects that our local system and this remote system both have + // Either we sent this object to them, or they sent this object to us + // A given Replica can be either in queryToConstructReplicaList or constructedReplicaList but not both at the same time + DataStructures::OrderedList constructedReplicaList; + + // Objects that we have, but this system does not, and we will query each tick to see if it should be sent to them + // If we do send it to them, the replica is moved to constructedReplicaList + // A given Replica can be either in queryToConstructReplicaList or constructedReplicaList but not both at the same time + DataStructures::List queryToConstructReplicaList; + + // Objects that this system has constructed are added at the same time to queryToSerializeReplicaList + // This list is used to serialize all objects that this system has to this connection + DataStructures::List queryToSerializeReplicaList; + + // Objects that are constructed on this system are also queried if they should be destroyed to this system + DataStructures::List queryToDestructReplicaList; + + // Working lists + DataStructures::List constructedReplicasCulled, destroyedReplicasCulled; + + // This is used if QueryGroupDownloadMessages() returns true when ID_REPLICA_MANAGER_DOWNLOAD_STARTED arrives + // Packets will be gathered and not returned until ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE arrives + bool groupConstructionAndSerialize; + DataStructures::Queue downloadGroup; + void ClearDownloadGroup(RakPeerInterface *rakPeerInterface); + + friend class ReplicaManager3; +private: + Connection_RM3() {}; + + ConstructionMode constructionMode; +}; + +/// \brief Return codes for Connection_RM3::GetConstructionState() and Replica3::QueryConstruction() +/// \details Indicates what state the object should be in for the remote system +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3ConstructionState +{ + /// This object should exist on the remote system. Send a construction message if necessary + /// If the NetworkID is already in use, it will not do anything + /// If it is not in use, it will create the object, and then call DeserializeConstruction + RM3CS_SEND_CONSTRUCTION, + + /// This object should exist on the remote system. + /// The other system already has the object, and the object will never be deleted. + /// This is true of objects that are loaded with the level, for example. + /// Treat it as if it existed, without sending a construction message. + /// Will call Serialize() and SerializeConstructionExisting() to the object on the remote system + RM3CS_ALREADY_EXISTS_REMOTELY, + + /// Same as RM3CS_ALREADY_EXISTS_REMOTELY but does not call SerializeConstructionExisting() + RM3CS_ALREADY_EXISTS_REMOTELY_DO_NOT_CONSTRUCT, + + /// This object will never be sent to the target system + /// This object will never be serialized from this system to the target system + RM3CS_NEVER_CONSTRUCT, + + /// Don't do anything this tick. Will query again next tick + RM3CS_NO_ACTION, + + /// Max enum + RM3CS_MAX, +}; + +/// If this object already exists for this system, should it be removed? +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3DestructionState +{ + /// This object should not exist on the remote system. Send a destruction message if necessary. + RM3DS_SEND_DESTRUCTION, + + /// This object will never be destroyed by a per-tick query. Don't call again + RM3DS_DO_NOT_QUERY_DESTRUCTION, + + /// Don't do anything this tick. Will query again next tick + RM3DS_NO_ACTION, + + /// Max enum + RM3DS_MAX, +}; + +/// Return codes when constructing an object +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3SerializationResult +{ + /// This object serializes identically no matter who we send to + /// We also send it to every connection (broadcast). + /// Efficient for memory, speed, and bandwidth but only if the object is always broadcast identically. + RM3SR_BROADCAST_IDENTICALLY, + + /// Same as RM3SR_BROADCAST_IDENTICALLY, but assume the object needs to be serialized, do not check with a memcmp + /// Assume the object changed, and serialize it + /// Use this if you know exactly when your object needs to change. Can be faster than RM3SR_BROADCAST_IDENTICALLY. + /// An example of this is if every member variable has an accessor, changing a member sets a flag, and you check that flag in Replica3::QuerySerialization() + /// The opposite of this is RM3SR_DO_NOT_SERIALIZE, in case the object did not change + RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION, + + /// Either this object serializes differently depending on who we send to or we send it to some systems and not others. + /// Inefficient for memory and speed, but efficient for bandwidth + /// However, if you don't know what to return, return this + RM3SR_SERIALIZED_UNIQUELY, + + /// Do not compare against last sent value. Just send even if the data is the same as the last tick + /// If the data is always changing anyway, or you want to send unreliably, this is a good method of serialization + /// Can send unique data per connection if desired. If same data is sent to all connections, use RM3SR_SERIALIZED_ALWAYS_IDENTICALLY for even better performance + /// Efficient for memory and speed, but not necessarily bandwidth + RM3SR_SERIALIZED_ALWAYS, + + /// Even faster than RM3SR_SERIALIZED_ALWAYS + /// Serialize() will only be called for the first system. The remaining systems will get the same data as the first system. + RM3SR_SERIALIZED_ALWAYS_IDENTICALLY, + + /// Do not serialize this object this tick, for this connection. Will query again next autoserialize timer + RM3SR_DO_NOT_SERIALIZE, + + /// Never serialize this object for this connection + /// Useful for objects that are downloaded, and never change again + /// Efficient + RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION, + + /// Max enum + RM3SR_MAX, +}; + +/// First pass at topology to see if an object should be serialized +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3QuerySerializationResult +{ + /// Call Serialize() to see if this object should be serializable for this connection + RM3QSR_CALL_SERIALIZE, + /// Do not call Serialize() this tick to see if this object should be serializable for this connection + RM3QSR_DO_NOT_CALL_SERIALIZE, + /// Never call Serialize() for this object and connection. This system will not serialize this object for this topology + RM3QSR_NEVER_CALL_SERIALIZE, + /// Max enum + RM3QSR_MAX, +}; + +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3ActionOnPopConnection +{ + RM3AOPC_DO_NOTHING, + RM3AOPC_DELETE_REPLICA, + RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION, + RM3AOPC_MAX, +}; + +/// \ingroup REPLICA_MANAGER_GROUP3 +/// Used for Replica3::QueryConstruction_PeerToPeer() and Replica3::QuerySerialization_PeerToPeer() to describe how the object replicates between hosts +enum Replica3P2PMode +{ + /// The Replica3 instance is constructed and serialized by one system only. + /// Example: Your avatar. No other player serializes or can create your avatar. + R3P2PM_SINGLE_OWNER, + /// The Replica3 instance is constructed and/or serialized by different systems + /// This system is currently in charge of construction and/or serialization + /// Example: A pickup. When an avatar holds it, that avatar controls it. When it is on the ground, the host controls it. + R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE, + /// The Replica3 instance is constructed and/or serialized by different systems + /// Another system is in charge of construction and/or serialization, but this system may be in charge at a later time + /// Example: A pickup held by another player. That player sends creation of that object to new connections, and serializes it until it is dropped. + R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE, +}; + +/// \brief Base class for your replicated objects for the ReplicaManager3 system. +/// \details To use, derive your class, or a member of your class, from Replica3.
    +/// \ingroup REPLICA_MANAGER_GROUP3 +class RAK_DLL_EXPORT Replica3 : public NetworkIDObject +{ +public: + Replica3(); + + /// Before deleting a local instance of Replica3, call Replica3::BroadcastDestruction() for the deletion notification to go out on the network. + /// It is not necessary to call ReplicaManager3::Dereference(), as this happens automatically in the destructor + virtual ~Replica3(); + + /// \brief Write a unique identifer that can be read on a remote system to create an object of this same class. + /// \details The value written to \a allocationIdBitstream will be passed to Connection_RM3::AllocReplica().
    + /// Sample implementation:
    + /// {allocationIdBitstream->Write(RakNet::RakString("Soldier");}
    + /// \param[out] allocationIdBitstream Bitstream for the user to write to, to identify this class + virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const=0; + + /// \brief Ask if this object, which does not exist on \a destinationConnection should (now) be sent to that system. + /// \details If ReplicaManager3::QueryConstructionMode() returns QUERY_CONNECTION_FOR_REPLICA_LIST or QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION (default), + /// then QueyrConstruction() is called once per tick from ReplicaManager3::Update() to determine if an object should exist on a given system.
    + /// Based on the return value, a network message may be sent to the other system to create the object.
    + /// If QueryConstructionMode() is overriden to return QUERY_CONNECTION_FOR_REPLICA_LIST, this function is unused.
    + /// \note Defaults are provided: QueryConstruction_PeerToPeer(), QueryConstruction_ServerConstruction(), QueryConstruction_ClientConstruction(). Return one of these functions for a working default for the relevant topology. + /// \param[in] destinationConnection Which system we will send to + /// \param[in] replicaManager3 Plugin instance for this Replica3 + /// \return What action to take + virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3)=0; + + /// \brief Ask if this object, which does exist on \a destinationConnection should be removed from the remote system + /// \details If ReplicaManager3::QueryConstructionMode() returns QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION (default), + /// then QueryDestruction() is called once per tick from ReplicaManager3::Update() to determine if an object that exists on a remote system should be destroyed for a given system.
    + /// Based on the return value, a network message may be sent to the other system to destroy the object.
    + /// Note that you can also destroy objects with BroadcastDestruction(), so this function is not useful unless you plan to delete objects for only a particular connection.
    + /// If QueryConstructionMode() is overriden to return QUERY_CONNECTION_FOR_REPLICA_LIST, this function is unused.
    + /// \param[in] destinationConnection Which system we will send to + /// \param[in] replicaManager3 Plugin instance for this Replica3 + /// \return What action to take. Only RM3CS_SEND_DESTRUCTION does anything at this time. + virtual RM3DestructionState QueryDestruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {(void) destinationConnection; (void) replicaManager3; return RM3DS_DO_NOT_QUERY_DESTRUCTION;} + + /// \brief We're about to call DeserializeConstruction() on this Replica3. If QueryRemoteConstruction() returns false, this object is deleted instead. + /// \details By default, QueryRemoteConstruction_ServerConstruction() does not allow clients to create objects. The client will get Replica3::DeserializeConstructionRequestRejected().
    + /// If you want the client to be able to potentially create objects for client/server, override accordingly.
    + /// Other variants of QueryRemoteConstruction_* just return true. + /// \note Defaults are provided: QueryRemoteConstruction_PeerToPeer(), QueryRemoteConstruction_ServerConstruction(), QueryRemoteConstruction_ClientConstruction(). Return one of these functions for a working default for the relevant topology. + /// \param[in] sourceConnection Which system sent us the object creation request message. + /// \return True to allow the object to pass onto DeserializeConstruction() (where it may also be rejected), false to immediately reject the remote construction request + virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection)=0; + + /// \brief We got a message from a connection to destroy this replica + /// Return true to automatically relay the destruction message to all our other connections + /// For a client in client/server, it does not matter what this funtion returns + /// For a server in client/server, this should normally return true + /// For a peer in peer to peer, you can normally return false since the original destroying peer would have told all other peers about the destruction + /// If a system gets a destruction command for an object that was already destroyed, the destruction message is ignored + virtual bool QueryRelayDestruction(Connection_RM3 *sourceConnection) const {(void) sourceConnection; return true;} + + /// \brief Write data to be sent only when the object is constructed on a remote system. + /// \details SerializeConstruction is used to write out data that you need to create this object in the context of your game, such as health, score, name. Use it for data you only need to send when the object is created.
    + /// After SerializeConstruction() is called, Serialize() will be called immediately thereafter. However, they are sent in different messages, so Serialize() may arrive a later frame than SerializeConstruction() + /// For that reason, the object should be valid after a call to DeserializeConstruction() for at least a short time.
    + /// \note The object's NetworkID and allocation id are handled by the system automatically, you do not need to write these values to \a constructionBitstream + /// \param[out] constructionBitstream Destination bitstream to write your data to + /// \param[in] destinationConnection System that will receive this network message. + virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection)=0; + + /// \brief Read data written by Replica3::SerializeConstruction() + /// \details Reads whatever data was written to \a constructionBitstream in Replica3::SerializeConstruction() + /// \param[out] constructionBitstream Bitstream written to in Replica3::SerializeConstruction() + /// \param[in] sourceConnection System that sent us this network message. + /// \return true to accept construction of the object. false to reject, in which case the object will be deleted via Replica3::DeallocReplica() + virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection)=0; + + /// Same as SerializeConstruction(), but for an object that already exists on the remote system. + /// Used if you return RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction + virtual void SerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;}; + + /// Same as DeserializeConstruction(), but for an object that already exists on the remote system. + /// Used if you return RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction + virtual void DeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;}; + + /// \brief Write extra data to send with the object deletion event, if desired + /// \details Replica3::SerializeDestruction() will be called to write any object destruction specific data you want to send with this event. + /// \a destructionBitstream can be read in DeserializeDestruction() + /// \param[out] destructionBitstream Bitstream for you to write to + /// \param[in] destinationConnection System that will receive this network message. + virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection)=0; + + /// \brief Read data written by Replica3::SerializeDestruction() + /// \details Return true to delete the object. BroadcastDestruction() will be called automatically, followed by ReplicaManager3::Dereference.
    + /// Return false to not delete it. If you delete it at a later point, you are responsible for calling BroadcastDestruction() yourself. + virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection)=0; + + /// \brief The system is asking what to do with this replica when the connection is dropped + /// \details Return QueryActionOnPopConnection_Client, QueryActionOnPopConnection_Server, or QueryActionOnPopConnection_PeerToPeer + virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const=0; + + /// Notification called for each of our replicas when a connection is popped + virtual void OnPoppedConnection(RakNet::Connection_RM3 *droppedConnection) {(void) droppedConnection;} + + /// \brief Override with {delete this;} + /// \details + ///
      + ///
    1. Got a remote message to delete this object which passed DeserializeDestruction(), OR + ///
    2. ReplicaManager3::SetAutoManageConnections() was called autoDestroy true (which is the default setting), and a remote system that owns this object disconnected) OR + /// <\OL> + ///
      + /// Override with {delete this;} to actually delete the object (and any other processing you wish).
      + /// If you don't want to delete the object, just do nothing, however, the system will not know this so you are responsible for deleting it yoruself later.
      + /// destructionBitstream may be 0 if the object was deleted locally + virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection)=0; + + /// \brief Implement with QuerySerialization_ClientSerializable(), QuerySerialization_ServerSerializable(), or QuerySerialization_PeerToPeer() + /// \details QuerySerialization() is a first pass query to check if a given object should serializable to a given system. The intent is that the user implements with one of the defaults for client, server, or peer to peer.
      + /// Without this function, a careless implementation would serialize an object anytime it changed to all systems. This would give you feedback loops as the sender gets the same message back from the recipient it just sent to.
      + /// If more than one system can serialize the same object then you will need to override to return true, and control the serialization result from Replica3::Serialize(). Be careful not to send back the same data to the system that just sent to you! + /// \return True to allow calling Replica3::Serialize() for this connection, false to not call. + virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection)=0; + + /// \brief Called for each replica owned by the user, once per Serialization tick, before Serialize() is called. + /// If you want to do some kind of operation on the Replica objects that you own, just before Serialization(), then overload this function + virtual void OnUserReplicaPreSerializeTick(void) {} + + /// \brief Serialize our class to a bitstream + /// \details User should implement this function to write the contents of this class to SerializationParamters::serializationBitstream.
      + /// If data only needs to be written once, you can write it to SerializeConstruction() instead for efficiency.
      + /// Transmitted over the network if it changed from the last time we called Serialize().
      + /// Called every time the time interval to ReplicaManager3::SetAutoSerializeInterval() elapses and ReplicaManager3::Update is subsequently called. + /// \param[in/out] serializeParameters Parameters controlling the serialization, including destination bitstream to write to + /// \return Whether to serialize, and if so, how to optimize the results + virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters)=0; + + /// \brief Called when the class is actually transmitted via Serialize() + /// \details Use to track how much bandwidth this class it taking + virtual void OnSerializeTransmission(RakNet::BitStream *bitStream, const SystemAddress &systemAddress) {(void) bitStream; (void) systemAddress; } + + /// \brief Read what was written in Serialize() + /// \details Reads the contents of the class from SerializationParamters::serializationBitstream.
      + /// Called whenever Serialize() is called with different data from the last send. + /// \param[in] serializationBitstream Bitstream passed to Serialize() + /// \param[in] timeStamp 0 if unused, else contains the time the message originated on the remote system + /// \param[in] sourceConnection Which system sent to us + virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters)=0; + + /// \brief Called after SerializeConstruction completes for all objects in a given update tick.
      + /// Writes to PostDeserializeConstruction(), which is called after all objects are created for a given Construction tick(). + /// Override to send data to PostDeserializeConstruction(), such as the NetworkID of other objects to resolve pointers to + virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;} + + /// Called after DeserializeConstruction completes for all objects in a given update tick.
      + /// This is used to resolve dependency chains, where two objects would refer to each other in DeserializeConstruction, yet one had not been constructed yet + /// In PostDeserializeConstruction(), you know that all objects have already been created, so can resolve NetworkIDs to pointers safely. + /// You can also use it to trigger some sort of event when you know the object has completed deserialization. + /// \param[in] constructionBitstream BitStream written in PostSerializeConstruction() + /// \param[in] sourceConnection System that sent us this network message. + virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;} + + /// Same as PostSerializeConstruction(), but for objects that returned RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction + virtual void PostSerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;} + + /// Same as PostDeserializeConstruction(), but for objects that returned RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction + virtual void PostDeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;} + + /// Called after DeserializeDestruction completes for the object successfully, but obviously before the object is deleted.
      + /// Override to trigger some sort of event when you know the object has completed destruction. + /// \param[in] sourceConnection System that sent us this network message. + virtual void PreDestruction(RakNet::Connection_RM3 *sourceConnection) {(void) sourceConnection;} + + /// \brief Default call for QueryConstruction(). + /// \details Both the client and the server is allowed to create this object. The network topology is client/server + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual RM3ConstructionState QueryConstruction_ClientConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); + /// Default call for QueryRemoteConstruction(). + /// \details Both the client and the server is allowed to create this object. The network topology is client/server + /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual bool QueryRemoteConstruction_ClientConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer); + + /// \brief Default call for QueryConstruction(). + /// \details Only the server is allowed to create this object. The network topology is client/server + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual RM3ConstructionState QueryConstruction_ServerConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); + /// \brief Default call for QueryRemoteConstruction(). Allow the server to create this object, but not the client. + /// \details Only the server is allowed to create this object. The network topology is client/server + /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual bool QueryRemoteConstruction_ServerConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer); + + /// \brief Default call for QueryConstruction(). + /// \details All clients are allowed to create all objects. The object is not relayed when remotely created + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] p2pMode If controlled only by this system ever, pass R3P2PM_SINGLE_OWNER. Otherwise pass R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE or R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE + virtual RM3ConstructionState QueryConstruction_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode=R3P2PM_SINGLE_OWNER); + /// \brief Default call for QueryRemoteConstruction(). + /// \details All clients are allowed to create all objects. The object is not relayed when remotely created + /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() + virtual bool QueryRemoteConstruction_PeerToPeer(RakNet::Connection_RM3 *sourceConnection); + + /// \brief Default call for QuerySerialization(). + /// \details Use if the values you are serializing are generated by the client that owns the object. The serialization will be relayed through the server to the other clients. + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual RakNet::RM3QuerySerializationResult QuerySerialization_ClientSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); + /// \brief Default call for QuerySerialization(). + /// \details Use if the values you are serializing are generated only by the server. The serialization will be sent to all clients, but the clients will not send back to the server. + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual RakNet::RM3QuerySerializationResult QuerySerialization_ServerSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); + /// \brief Default call for QuerySerialization(). + /// \details Use if the values you are serializing are on a peer to peer network. The peer that owns the object will send to all. Remote peers will not send. + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] p2pMode If controlled only by this system ever, pass R3P2PM_SINGLE_OWNER. Otherwise pass R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE or R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE + virtual RakNet::RM3QuerySerializationResult QuerySerialization_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode=R3P2PM_SINGLE_OWNER); + + /// Default: If we are a client, and the connection is lost, delete the server's objects + virtual RM3ActionOnPopConnection QueryActionOnPopConnection_Client(RakNet::Connection_RM3 *droppedConnection) const; + /// Default: If we are a client, and the connection is lost, delete the client's objects and broadcast the destruction + virtual RM3ActionOnPopConnection QueryActionOnPopConnection_Server(RakNet::Connection_RM3 *droppedConnection) const; + /// Default: If we are a peer, and the connection is lost, delete the peer's objects + virtual RM3ActionOnPopConnection QueryActionOnPopConnection_PeerToPeer(RakNet::Connection_RM3 *droppedConnection) const; + + /// Call to send a network message to delete this object on other systems.
      + /// Call it before deleting the object + virtual void BroadcastDestruction(void); + + /// creatingSystemGUID is set the first time Reference() is called, or if we get the object from another system + /// \return System that originally created this object + RakNetGUID GetCreatingSystemGUID(void) const; + + /// \return If ReplicaManager3::Reference() was called on this object. + bool WasReferenced(void) const {return replicaManager!=0;} + + /// GUID of the system that first called Reference() on this object. + /// Transmitted automatically when the object is constructed + RakNetGUID creatingSystemGUID; + /// GUID of the system that caused the item to send a deletion command over the network + RakNetGUID deletingSystemGUID; + + /// \internal + /// ReplicaManager3 plugin associated with this object + ReplicaManager3 *replicaManager; + + LastSerializationResultBS lastSentSerialization; + RakNet::Time whenLastSerialized; + bool forceSendUntilNextUpdate; +}; + +/// \brief Use Replica3 through composition instead of inheritance by containing an instance of this templated class +/// Calls to parent class for all functions +/// Parent class must still define and functions though! +/// \pre Parent class must call SetParent() on this object +template +class RAK_DLL_EXPORT Replica3Composite : public Replica3 +{ +protected: + parent_type *r3CompositeOwner; +public: + void SetCompositeOwner(parent_type *p) {r3CompositeOwner=p;} + parent_type* GetCompositeOwner(void) const {return r3CompositeOwner;}; + virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const {r3CompositeOwner->WriteAllocationID(destinationConnection, allocationIdBitstream);} + virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {return r3CompositeOwner->QueryConstruction(destinationConnection, replicaManager3);} + virtual RM3DestructionState QueryDestruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {return r3CompositeOwner->QueryDestruction(destinationConnection, replicaManager3);} + virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->QueryRemoteConstruction(sourceConnection);} + virtual bool QueryRelayDestruction(Connection_RM3 *sourceConnection) const {return r3CompositeOwner->QueryRelayDestruction(sourceConnection);} + virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeConstruction(constructionBitstream, destinationConnection);} + virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->DeserializeConstruction(constructionBitstream, sourceConnection);} + virtual void SerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeConstructionExisting(constructionBitstream, destinationConnection);} + virtual void DeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->DeserializeConstructionExisting(constructionBitstream, sourceConnection);} + virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeDestruction(destructionBitstream, destinationConnection);} + virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->DeserializeDestruction(destructionBitstream, sourceConnection);} + virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {return r3CompositeOwner->QueryActionOnPopConnection(droppedConnection);} + virtual void OnPoppedConnection(RakNet::Connection_RM3 *droppedConnection) {r3CompositeOwner->OnPoppedConnection(droppedConnection);} + virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->DeallocReplica(sourceConnection);} + virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {return r3CompositeOwner->QuerySerialization(destinationConnection);} + virtual void OnUserReplicaPreSerializeTick(void) {r3CompositeOwner->OnUserReplicaPreSerializeTick();} + virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters) {return r3CompositeOwner->Serialize(serializeParameters);} + virtual void OnSerializeTransmission(RakNet::BitStream *bitStream, const SystemAddress &systemAddress) {r3CompositeOwner->OnSerializeTransmission(bitStream, systemAddress);} + virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters) {r3CompositeOwner->Deserialize(deserializeParameters);} + virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->PostSerializeConstruction(constructionBitstream, destinationConnection);} + virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PostDeserializeConstruction(constructionBitstream, sourceConnection);} + virtual void PostSerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->PostSerializeConstructionExisting(constructionBitstream, destinationConnection);} + virtual void PostDeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PostDeserializeConstructionExisting(constructionBitstream, sourceConnection);} + virtual void PreDestruction(RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PreDestruction(sourceConnection);} +}; + +} // namespace RakNet + + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/Router2.cpp b/project/lib_projects/raknet/jni/RaknetSources/Router2.cpp index a442fb5..1f06638 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Router2.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/Router2.cpp @@ -1,1329 +1,1329 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_Router2==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#include "Router2.h" -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "RakNetTime.h" -#include "GetTime.h" -#include "DS_OrderedList.h" -#include "SocketLayer.h" -#include "FormatString.h" - -using namespace RakNet; - -/* -Algorithm: - -1. Sender calls ConnectInternal(). A ConnnectRequest structure is allocated and stored in the connectionRequests list, containing a list of every system we are connected to. ID_ROUTER_2_QUERY_FORWARDING is sent to all connected systems. - -2. Upon the router getting ID_ROUTER_2_QUERY_FORWARDING, ID_ROUTER_2_REPLY_FORWARDING is sent to the sender indicating if that router is connected to the endpoint, along with the ping from the router to the endpoint. - -3. Upon the sender getting ID_ROUTER_2_REPLY_FORWARDING, the connection request structure is looked up in Router2::UpdateForwarding. The ping is stored in that structure. Once all systems have replied, the system continues to the next state. If every system in step 1 has been exhausted, and routing has occured at least once, then ID_CONNECTION_LOST is returned. If every system in step 1 has been exhausted and routing has never occured, then ID_ROUTER_2_FORWARDING_NO_PATH is returned. Otherwise, the router with the lowest ping is chosen, and RequestForwarding() is called with that system, which sends ID_ROUTER_2_REQUEST_FORWARDING to the router. - -4. When the router gets ID_ROUTER_2_REQUEST_FORWARDING, a MiniPunchRequest structure is allocated and stored in the miniPunchesInProgress list. The function SendOOBMessages() sends ID_ROUTER_2_REPLY_TO_SENDER_PORT from the routing sockets to both the sender and endpoint. It also sends ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT through the regular RakNet connection. - -5. The sender and endpoint should get ID_ROUTER_2_REPLY_TO_SENDER_PORT and/or ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT depending on what type of router they have. If ID_ROUTER_2_REPLY_TO_SENDER_PORT arrives, then this will reply back to the routing socket directly. If ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT arrives, then the reply port is modified to be the port specified by the router system. In both cases, ID_ROUTER_2_MINI_PUNCH_REPLY is sent. As the router has already setup the forwarding, ID_ROUTER_2_MINI_PUNCH_REPLY will actually arrive to the endpoint from the sender, and from the sender to the endpoint. - -6. When ID_ROUTER_2_MINI_PUNCH_REPLY arrives, ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE will be sent to the router. This is to tell the router that the forwarding has succeeded. - -7. When ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE arrives on the router, the router will find the two systems in the miniPunchesInProgress list, which was added in step 4 (See OnMiniPunchReplyBounce()). gotReplyFromSource or gotReplyFromEndpoint will be set to true, depending on the sender. When both gotReplyFromSource and gotReplyFromEndpoint have replied, then ID_ROUTER_2_REROUTE is sent to the endpoint, and ID_ROUTER_2_FORWARDING_ESTABLISHED is sent to the sender. - -8. When the endpoint gets ID_ROUTER_2_REROUTE, the system address is updated for the guid of the sender using RakPeer::ChangeSystemAddress(). This happens in OnRerouted(). - -9. When the sender gets ID_ROUTER_2_FORWARDING_ESTABLISHED, then in OnForwardingSuccess() the endpoint is removed from the connectionRequest list and moved to the forwardedConnectionList. - -10. In OnClosedConnection(), for the sender, if the closed connection is the endpoint, then the endpoint is removed from the forwardedConnectionList (this is a graceful disconnect). If the connection was instead lost to the router, then ConnectInternal() gets called, which goes back to step 1. If instead this was a connection requset in progress, then UpdateForwarding() gets called, which goes back to step 3. - -11. When the user connects the endpoint and sender, then the sender will get ID_CONNECTION_REQUEST_ACCEPTED. The sender will look up the endpoint in the forwardedConnectionList, and send ID_ROUTER_2_INCREASE_TIMEOUT to the endpoint. This message will call SetTimeoutTime() on the endpoint, so that if the router disconnects, enough time is available for the reroute to complete. -*/ - -#define MIN_MINIPUNCH_TIMEOUT 5000 - - - - -void Router2DebugInterface::ShowFailure(const char *message) -{ - printf("%s", message); -} -void Router2DebugInterface::ShowDiagnostic(const char *message) -{ - printf("%s", message); -} - -enum Router2MessageIdentifiers -{ - ID_ROUTER_2_QUERY_FORWARDING, - ID_ROUTER_2_REPLY_FORWARDING, - ID_ROUTER_2_REQUEST_FORWARDING, - ID_ROUTER_2_INCREASE_TIMEOUT, -}; -Router2::ConnnectRequest::ConnnectRequest() -{ - -} -Router2::ConnnectRequest::~ConnnectRequest() -{ - -} - -STATIC_FACTORY_DEFINITIONS(Router2,Router2); - -Router2::Router2() -{ - udpForwarder=0; - maximumForwardingRequests=0; - debugInterface=0; - socketFamily=AF_INET; -} -Router2::~Router2() -{ - ClearAll(); - - if (udpForwarder) - { - udpForwarder->Shutdown(); - RakNet::OP_DELETE(udpForwarder,_FILE_AND_LINE_); - } -} -void Router2::ClearMinipunches(void) -{ - miniPunchesInProgressMutex.Lock(); - miniPunchesInProgress.Clear(false,_FILE_AND_LINE_); - miniPunchesInProgressMutex.Unlock(); -} -void Router2::ClearConnectionRequests(void) -{ - connectionRequestsMutex.Lock(); - for (unsigned int i=0; i < connectionRequests.Size(); i++) - { - RakNet::OP_DELETE(connectionRequests[i],_FILE_AND_LINE_); - } - connectionRequests.Clear(false,_FILE_AND_LINE_); - connectionRequestsMutex.Unlock(); -} -bool Router2::ConnectInternal(RakNetGUID endpointGuid, bool returnConnectionLostOnFailure) -{ - int largestPing = GetLargestPingAmongConnectedSystems(); - if (largestPing<0) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2: ConnectInternal(%I64d) failed at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); - - // Not connected to anyone - return false; - } - - // ALready in progress? - connectionRequestsMutex.Lock(); - if (GetConnectionRequestIndex(endpointGuid)!=(unsigned int)-1) - { - connectionRequestsMutex.Unlock(); - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2: ConnectInternal(%I64d) failed at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); - - return false; - } - connectionRequestsMutex.Unlock(); - - // StoreRequest(endpointGuid, Largest(ping*2), systemsSentTo). Set state REQUEST_STATE_QUERY_FORWARDING - Router2::ConnnectRequest *cr = RakNet::OP_NEW(_FILE_AND_LINE_); - DataStructures::List addresses; - DataStructures::List guids; - rakPeerInterface->GetSystemList(addresses, guids); - if (guids.Size()==0) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", _FILE_AND_LINE_)); - - return false; - } - cr->requestState=R2RS_REQUEST_STATE_QUERY_FORWARDING; - cr->pingTimeout=RakNet::GetTimeMS()+largestPing*2+1000; - cr->endpointGuid=endpointGuid; - cr->returnConnectionLostOnFailure=returnConnectionLostOnFailure; - for (unsigned int i=0; i < guids.Size(); i++) - { - ConnectionRequestSystem crs; - if (guids[i]!=endpointGuid) - { - crs.guid=guids[i]; - crs.pingToEndpoint=-1; - cr->connectionRequestSystemsMutex.Lock(); - cr->connectionRequestSystems.Push(crs,_FILE_AND_LINE_); - cr->connectionRequestSystemsMutex.Unlock(); - - // Broadcast(ID_ROUTER_2_QUERY_FORWARDING, endpointGuid); - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); - bsOut.Write((unsigned char) ID_ROUTER_2_QUERY_FORWARDING); - bsOut.Write(endpointGuid); - uint32_t pack_id = rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,crs.guid,false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Router2::ConnectInternal: at %s:%i, pack_id = %d", __FILE__, __LINE__,pack_id)); - } - - } - else - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Router2::ConnectInternal: at %s:%i [else ..].: %I64d==%I64d", __FILE__, __LINE__, - guids[i].g,endpointGuid.g)); - } - } - } - connectionRequestsMutex.Lock(); - connectionRequests.Push(cr,_FILE_AND_LINE_); - connectionRequestsMutex.Unlock(); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Broadcasting ID_ROUTER_2_QUERY_FORWARDING to %I64d at %s:%i\n", endpointGuid.g , __FILE__, __LINE__)); - } - - return true; -} -void Router2::SetSocketFamily(unsigned short _socketFamily) -{ - socketFamily=_socketFamily; -} -void Router2::EstablishRouting(RakNetGUID endpointGuid) -{ - ConnectionState cs = rakPeerInterface->GetConnectionState(endpointGuid); - if (cs!=IS_DISCONNECTED && cs!=IS_NOT_CONNECTED) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i " - "(already connected to the %I64d)\n", __FILE__, __LINE__, endpointGuid.g )); - return; - } - - ConnectInternal(endpointGuid,false); -} -void Router2::SetMaximumForwardingRequests(int max) -{ - if (max>0 && maximumForwardingRequests<=0) - { - udpForwarder = RakNet::OP_NEW(_FILE_AND_LINE_); - udpForwarder->Startup(); - } - else if (max<=0 && maximumForwardingRequests>0) - { - udpForwarder->Shutdown(); - RakNet::OP_DELETE(udpForwarder,_FILE_AND_LINE_); - udpForwarder=0; - } - - maximumForwardingRequests=max; -} -PluginReceiveResult Router2::OnReceive(Packet *packet) -{ - SystemAddress sa; - RakNet::BitStream bs(packet->data,packet->length,false); - if (packet->data[0]==ID_ROUTER_2_INTERNAL) - { - switch (packet->data[1]) - { - case ID_ROUTER_2_QUERY_FORWARDING: - { - OnQueryForwarding(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_REPLY_FORWARDING: - { - OnQueryForwardingReply(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_REQUEST_FORWARDING: - { - - if (debugInterface) - { - char buff[512]; - char buff2[32]; - packet->systemAddress.ToString(true,buff2); - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING on ip %s from %I64d, ", - buff2,packet->guid.g)); - } - - OnRequestForwarding(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_INCREASE_TIMEOUT: - { - /// The routed system wants more time to stay alive on no communication, in case the router drops or crashes - rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - } - else if (packet->data[0]==ID_OUT_OF_BAND_INTERNAL && packet->length>=2) - { - switch (packet->data[1]) - { - case ID_ROUTER_2_REPLY_TO_SENDER_PORT: - { - RakNet::BitStream bsOut; - bsOut.Write(packet->guid); - SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, packet->systemAddress); - - if (debugInterface) - { - char buff[512]; - char buff2[32]; - sa.ToString(false,buff2); - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT %i on address %s, replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, _FILE_AND_LINE_)); - -// packet->systemAddress.ToString(true,buff2); -// debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT on address %s (%I64d), " -// "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", buff2,packet->guid.g, __FILE__, __LINE__)); - } - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT: - { - RakNet::BitStream bsOut; - bsOut.Write(packet->guid); - bs.IgnoreBytes(2); - sa=packet->systemAddress; - unsigned short port; - bs.Read(port); - sa.SetPort(port); - RakAssert(sa.GetPort()!=0); - SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, sa); - - if (debugInterface) - { - char buff[512]; - char buff2[32]; - sa.ToString(false,buff2); - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT %i on address %s, " - "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, __FILE__, __LINE__)); - } - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_MINI_PUNCH_REPLY: - OnMiniPunchReply(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE: - OnMiniPunchReplyBounce(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - else if (packet->data[0]==ID_ROUTER_2_FORWARDING_ESTABLISHED) - { -// printf("Got ID_ROUTER_2_FORWARDING_ESTABLISHED\n"); - if (OnForwardingSuccess(packet)==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - else if (packet->data[0]==ID_ROUTER_2_REROUTED) - { - OnRerouted(packet); - } - else if (packet->data[0]==ID_CONNECTION_REQUEST_ACCEPTED) - { - unsigned int forwardingIndex; - forwardedConnectionListMutex.Lock(); - for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) - { - if (forwardedConnectionList[forwardingIndex].endpointGuid==packet->guid && forwardedConnectionList[forwardingIndex].weInitiatedForwarding) - break; - } - - if (forwardingIndexSend(&bsOut,HIGH_PRIORITY,RELIABLE,0,packet->guid,false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_CONNECTION_REQUEST_ACCEPTED, " - "sending ID_ROUTER_2_INCREASE_TIMEOUT to the %I64d at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); - } - - // Also take longer ourselves - rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress); - } - else { - // ~Gwynn: Fix for Receive hanging up problem on Windows XP - // See http://blog.delphi-jedi.net/2008/04/23/the-case-of-the-unexplained-dead-lock-in-a-single-thread/ for details - forwardedConnectionListMutex.Unlock(); - } - } - else if (packet->data[0]==ID_ROUTER_2_FORWARDING_NO_PATH) - { - if (packet->wasGeneratedLocally==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - return RR_CONTINUE_PROCESSING; -} -void Router2::Update(void) -{ - RakNet::TimeMS curTime = RakNet::GetTimeMS(); - unsigned int connectionRequestIndex=0; - connectionRequestsMutex.Lock(); - while (connectionRequestIndex < connectionRequests.Size()) - { - ConnnectRequest* connectionRequest = connectionRequests[connectionRequestIndex]; - - // pingTimeout is only used with R2RS_REQUEST_STATE_QUERY_FORWARDING - if (connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING && - connectionRequest->pingTimeout < curTime) - { - bool anyRemoved=false; - unsigned int connectionRequestGuidIndex=0; - connectionRequest->connectionRequestSystemsMutex.Lock(); - while (connectionRequestGuidIndex < connectionRequest->connectionRequestSystems.Size()) - { - if (connectionRequest->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint<0) - { - anyRemoved=true; - connectionRequest->connectionRequestSystems.RemoveAtIndexFast(connectionRequestGuidIndex); - } - else - { - connectionRequestGuidIndex++; - } - } - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - if (anyRemoved) - { - if (connectionRequestIndex!=(unsigned int)-1) - { - // connectionRequestsMutex should be locked before calling this function - if (UpdateForwarding(connectionRequest)==false) - { - RemoveConnectionRequest(connectionRequestIndex); - } - else - { - connectionRequestIndex++; - } - } - else - { - connectionRequestIndex++; - } - } - else - { - connectionRequestIndex++; - } - } - else - { - connectionRequestIndex++; - } - } - connectionRequestsMutex.Unlock(); - - unsigned int i=0; - miniPunchesInProgressMutex.Lock(); - while (i < miniPunchesInProgress.Size()) - { - if (miniPunchesInProgress[i].timeoutminiPunchesInProgress[i].nextAction) - { - miniPunchesInProgress[i].nextAction=curTime+100; - SendOOBMessages(&miniPunchesInProgress[i]); - } - else - i++; - } - miniPunchesInProgressMutex.Unlock(); - -} -void Router2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - - - unsigned int forwardedConnectionIndex=0; - forwardedConnectionListMutex.Lock(); - while (forwardedConnectionIndexShowDiagnostic(FormatStringTS(buff,"Closed connection to the %I64d, removing forwarding from list at %s:%i\n", rakNetGUID.g, __FILE__, __LINE__)); - } - - // No longer need forwarding - forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); - } - else if (forwardedConnectionList[forwardedConnectionIndex].intermediaryGuid==rakNetGUID && forwardedConnectionList[forwardedConnectionIndex].weInitiatedForwarding) - { - // Lost connection to intermediary. Restart process to connect to endpoint. If failed, push ID_CONNECTION_LOST. Also remove connection request if it already is in the list, to restart it - connectionRequestsMutex.Lock(); - unsigned int pos = GetConnectionRequestIndex(forwardedConnectionList[forwardedConnectionIndex].endpointGuid); - if((unsigned int)-1 != pos) {RakNet::OP_DELETE(connectionRequests[pos], __FILE__, __LINE__); connectionRequests.RemoveAtIndexFast(pos);} - connectionRequestsMutex.Unlock(); - - ConnectInternal(forwardedConnectionList[forwardedConnectionIndex].endpointGuid, true); - - forwardedConnectionIndex++; - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Closed connection %I64d, restarting forwarding at %s:%i\n",rakNetGUID.g, __FILE__, __LINE__)); - } - - // This should not be removed - the connection is still forwarded, but perhaps through another system -// forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); - } - else - forwardedConnectionIndex++; - } - forwardedConnectionListMutex.Unlock(); - - unsigned int connectionRequestIndex=0; - connectionRequestsMutex.Lock(); - while (connectionRequestIndex < connectionRequests.Size()) - { - ConnnectRequest *cr = connectionRequests[connectionRequestIndex]; - cr->connectionRequestSystemsMutex.Lock(); - unsigned int connectionRequestGuidIndex = cr->GetGuidIndex(rakNetGUID); - if (connectionRequestGuidIndex!=(unsigned int)-1) - { - cr->connectionRequestSystems.RemoveAtIndexFast(connectionRequestGuidIndex); - cr->connectionRequestSystemsMutex.Unlock(); - if (UpdateForwarding(cr)==false) // If returns false, no connection request systems left - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Aborted connection to the %I64d, aborted forwarding at %s:%i\n", rakNetGUID.g, __FILE__, __LINE__)); - } - - RemoveConnectionRequest(connectionRequestIndex); - } - else // Else a system in the connection request list dropped. If cr->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING then we are still waiting for other systems to respond. - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Aborted connection attempt to %I64d, restarting forwarding to %I64d at %s:%i\n", rakNetGUID.g,cr->endpointGuid.g,__FILE__, __LINE__)); - } -// if(volatile bool is_my_fix_a_truth = true) { // A system in the list of potential systems to try routing to dropped. There is no need to restart the whole process. -// connectionRequestsMutex.Lock(); -// connectionRequests.RemoveAtIndexFast(connectionRequestIndex); -// connectionRequestsMutex.Unlock(); -// -// if(false == ConnectInternal(cr->endpointGuid,cr->returnConnectionLostOnFailure)) -// if (debugInterface) -// { -// char buff[512]; -// debugInterface->ShowDiagnostic(FormatStringTS(buff,"ConnectInternal(cr->endpointGuid,cr->returnConnectionLostOnFailure) is false. at %s:%i\n", __FILE__, __LINE__)); -// } -// } - - connectionRequestIndex++; - } - } - else - { - cr->connectionRequestSystemsMutex.Unlock(); - connectionRequestIndex++; - } - } - connectionRequestsMutex.Unlock(); - - - unsigned int i=0; - miniPunchesInProgressMutex.Lock(); - while (i < miniPunchesInProgress.Size()) - { - if (miniPunchesInProgress[i].sourceGuid==rakNetGUID || miniPunchesInProgress[i].endpointGuid==rakNetGUID) - { - if (miniPunchesInProgress[i].sourceGuid!=rakNetGUID) - { - SendFailureOnCannotForward(miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].endpointGuid); - } - miniPunchesInProgress.RemoveAtIndexFast(i); - } - else - i++; - } - miniPunchesInProgressMutex.Unlock(); -} -void Router2::OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason) -{ - (void) failedConnectionAttemptReason; - (void) packet; - - unsigned int forwardedConnectionIndex=0; - forwardedConnectionListMutex.Lock(); - while (forwardedConnectionIndexsystemAddress) - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Failed connection attempt to forwarded system (%I64d : %s) at %s:%i\n", - forwardedConnectionList[forwardedConnectionIndex].endpointGuid.g, packet->systemAddress.ToString(true), __FILE__, __LINE__)); - } - - packet->guid=forwardedConnectionList[forwardedConnectionIndex].endpointGuid; - forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); - } - else - forwardedConnectionIndex++; - } - forwardedConnectionListMutex.Unlock(); -} -void Router2::OnRakPeerShutdown(void) -{ - ClearAll(); -} -// connectionRequestsMutex should already be locked -bool Router2::UpdateForwarding(ConnnectRequest* connectionRequest) -{ - connectionRequest->connectionRequestSystemsMutex.Lock(); - - // RAKNET_DEBUG_PRINTF(__FUNCTION__": connectionRequest->connectionRequestSystems.Size = %d", connectionRequest->connectionRequestSystems.Size()); - - if (connectionRequest->connectionRequestSystems.Size()==0) - { - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - // printf("Router2 failed at %s:%i\n", __FILE__, __LINE__); - if (connectionRequest->returnConnectionLostOnFailure) { - ReturnToUser(ID_CONNECTION_LOST, connectionRequest->endpointGuid, UNASSIGNED_SYSTEM_ADDRESS, true); // This is a connection which was previously established. Rerouting is not possible. -// bool sendDisconnectionNotification = false; -// rakPeerInterface->CloseConnection(rakPeerInterface->GetSystemAddressFromGuid(connectionRequest->endpointGuid), sendDisconnectionNotification); -// RAKNET_DEBUG_PRINTF(__FUNCTION__": call rakPeerInterface->CloseConnection(%I64d)" , connectionRequest->endpointGuid.g); - } - else - ReturnToUser(ID_ROUTER_2_FORWARDING_NO_PATH, connectionRequest->endpointGuid, UNASSIGNED_SYSTEM_ADDRESS, false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Forwarding failed, no remaining systems at %s:%i\n", _FILE_AND_LINE_)); - } - - forwardedConnectionListMutex.Lock(); - - for (unsigned int forwardedConnectionIndex=0; forwardedConnectionIndex < forwardedConnectionList.Size(); forwardedConnectionIndex++) - { - if (forwardedConnectionList[forwardedConnectionIndex].endpointGuid==connectionRequest->endpointGuid && forwardedConnectionList[forwardedConnectionIndex].weInitiatedForwarding) - { - forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); - break; - } - } - forwardedConnectionListMutex.Unlock(); - - return false; - } - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - if (connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING) - { - connectionRequest->connectionRequestSystemsMutex.Lock(); - - for (unsigned int i=0; i < connectionRequest->connectionRequestSystems.Size(); i++) - { - if (connectionRequest->connectionRequestSystems[i].pingToEndpoint<0) - { - connectionRequest->connectionRequestSystemsMutex.Unlock(); - return true; // Forward query still in progress, just return - } - } - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - RequestForwarding(connectionRequest); - } -// else if (connectionRequest->requestState==REQUEST_STATE_REQUEST_FORWARDING) -// { -// RequestForwarding(connectionRequestIndex); -// } - - return true; -} -// connectionRequestsMutex should already be locked -void Router2::RemoveConnectionRequest(unsigned int connectionRequestIndex) -{ - RakNet::OP_DELETE(connectionRequests[connectionRequestIndex],_FILE_AND_LINE_); - connectionRequests.RemoveAtIndexFast(connectionRequestIndex); -} -int ConnectionRequestSystemComp( const Router2::ConnectionRequestSystem & key, const Router2::ConnectionRequestSystem &data ) -{ - if (key.pingToEndpoint * (key.usedForwardingEntries+1) < data.pingToEndpoint * (data.usedForwardingEntries+1)) - return -1; - if (key.pingToEndpoint * (key.usedForwardingEntries+1) == data.pingToEndpoint * (data.usedForwardingEntries+1)) - return 1; - if (key.guid < data.guid) - return -1; - if (key.guid > data.guid) - return -1; - return 0; -} -// connectionRequestsMutex should already be locked -void Router2::RequestForwarding(ConnnectRequest* connectionRequest) -{ - RakAssert(connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING); - connectionRequest->requestState=REQUEST_STATE_REQUEST_FORWARDING; - - if (connectionRequest->GetGuidIndex(connectionRequest->lastRequestedForwardingSystem)!=(unsigned int)-1) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", _FILE_AND_LINE_)); - return; - } - - // Prioritize systems to request forwarding - DataStructures::OrderedList commandList; - unsigned int connectionRequestGuidIndex; - connectionRequest->connectionRequestSystemsMutex.Lock(); - for (connectionRequestGuidIndex=0; connectionRequestGuidIndex < connectionRequest->connectionRequestSystems.Size(); connectionRequestGuidIndex++) - { - RakAssert(connectionRequest->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint>=0); - commandList.Insert(connectionRequest->connectionRequestSystems[connectionRequestGuidIndex], - connectionRequest->connectionRequestSystems[connectionRequestGuidIndex], - true, - _FILE_AND_LINE_); - } - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - connectionRequest->lastRequestedForwardingSystem=commandList[0].guid; - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); - bsOut.Write((unsigned char) ID_ROUTER_2_REQUEST_FORWARDING); - bsOut.Write(connectionRequest->endpointGuid); - rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,connectionRequest->lastRequestedForwardingSystem,false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REQUEST_FORWARDING " - "(connectionRequest->lastRequestedForwardingSystem = %I64d, connectionRequest->endpointGuid = %I64d) at %s:%i\n", - connectionRequest->lastRequestedForwardingSystem.g,connectionRequest->endpointGuid.g, __FILE__, __LINE__)); - } -} -void Router2::SendFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); - bsOut.Write((unsigned char) ID_ROUTER_2_REPLY_FORWARDING); - bsOut.Write(endpointGuid); - bsOut.Write(false); - rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,sourceGuid,false); -} -int Router2::ReturnFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid) -{ - // If the number of systems we are currently forwarding>=maxForwarding, return ID_ROUTER_2_REPLY_FORWARDING,endpointGuid,false - if (udpForwarder==0 || udpForwarder->GetUsedForwardEntries()/2>maximumForwardingRequests) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d -> %I64d) at %s:%i\n", - sourceGuid.g, endpointGuid.g,__FILE__, __LINE__)); - SendFailureOnCannotForward(sourceGuid,endpointGuid); - return -1; - } - - // We cannot forward connections which are themselves forwarded. Return fail in that case - forwardedConnectionListMutex.Lock(); - for (unsigned int i=0; i < forwardedConnectionList.Size(); i++) - { - if ((forwardedConnectionList[i].endpointGuid==endpointGuid) - || (forwardedConnectionList[i].endpointGuid==sourceGuid)) // sourceGuid is here so you do not respond to routing requests from systems you are already routing through. - { - forwardedConnectionListMutex.Unlock(); - - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", __FILE__, __LINE__)); - SendFailureOnCannotForward(sourceGuid,endpointGuid); - return -1; - } - } - forwardedConnectionListMutex.Unlock(); - - int pingToEndpoint; - pingToEndpoint = rakPeerInterface->GetAveragePing(endpointGuid); - if (pingToEndpoint==-1) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d -> %I64d) at %s:%i\n", - sourceGuid.g, endpointGuid.g,__FILE__, __LINE__)); - - SendFailureOnCannotForward(sourceGuid,endpointGuid); - return -1; - } - return pingToEndpoint; -} -void Router2::OnQueryForwarding(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); - RakNetGUID endpointGuid; - // Read endpointGuid - bs.Read(endpointGuid); - - int pingToEndpoint = ReturnFailureOnCannotForward(packet->guid, endpointGuid); - if (pingToEndpoint==-1) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); - return; - } - - // If we are connected to endpointGuid, reply ID_ROUTER_2_REPLY_FORWARDING,endpointGuid,true,ping,numCurrentlyForwarding - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); - bsOut.Write((unsigned char) ID_ROUTER_2_REPLY_FORWARDING); - bsOut.Write(endpointGuid); - bsOut.Write(true); - bsOut.Write((unsigned short) pingToEndpoint); - bsOut.Write((unsigned short) udpForwarder->GetUsedForwardEntries()/2); - rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,packet->guid,false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REPLY_FORWARDING to the %I64d at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); - } -} -void Router2::OnQueryForwardingReply(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); - RakNetGUID endpointGuid; - bs.Read(endpointGuid); - // Find endpointGuid among stored requests - bool canForward=false; - bs.Read(canForward); - - - connectionRequestsMutex.Lock(); - unsigned int connectionRequestIndex = GetConnectionRequestIndex(endpointGuid); - if (connectionRequestIndex==(unsigned int)-1) - { - connectionRequestsMutex.Unlock(); - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); - return; - } - - connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Lock(); - unsigned int connectionRequestGuidIndex = connectionRequests[connectionRequestIndex]->GetGuidIndex(packet->guid); - if (connectionRequestGuidIndex==(unsigned int)-1) - { - connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Unlock(); - connectionRequestsMutex.Unlock(); - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); - return; - } - - if (debugInterface) - { - char buff[512]; - char buff2[512]; - packet->systemAddress.ToString(true,buff2); - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_FORWARDING on address %s(%I64d -> %I64d) canForward=%i at %s:%i\n", - buff2, packet->guid.g, endpointGuid.g, canForward, __FILE__, __LINE__)); - } - - if (canForward) - { - unsigned short pingToEndpoint; - unsigned short usedEntries; - bs.Read(pingToEndpoint); - bs.Read(usedEntries); - connectionRequests[connectionRequestIndex]->connectionRequestSystems[connectionRequestGuidIndex].usedForwardingEntries=usedEntries; - connectionRequests[connectionRequestIndex]->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint=rakPeerInterface->GetAveragePing(packet->guid)+pingToEndpoint; - } - else - { - connectionRequests[connectionRequestIndex]->connectionRequestSystems.RemoveAtIndex(connectionRequestGuidIndex); - } - connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Unlock(); - - if (UpdateForwarding(connectionRequests[connectionRequestIndex])==false) - { - RemoveConnectionRequest(connectionRequestIndex); - } - connectionRequestsMutex.Unlock(); -} -void Router2::SendForwardingSuccess(MessageID messageId, RakNetGUID sourceGuid, RakNetGUID endpointGuid, unsigned short sourceToDstPort) -{ - RakNet::BitStream bsOut; - bsOut.Write(messageId); - bsOut.Write(endpointGuid); - bsOut.Write(sourceToDstPort); - rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,sourceGuid,false); - - if (debugInterface) - { - char buff[512]; - if (messageId==ID_ROUTER_2_FORWARDING_ESTABLISHED) - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_FORWARDING_ESTABLISHED at %s:%i\n", _FILE_AND_LINE_ )); - else - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REROUTED at %s:%i\n", _FILE_AND_LINE_ )); - } -} -void Router2::SendOOBFromRakNetPort(OutOfBandIdentifiers oob, BitStream *extraData, SystemAddress sa) -{ - RakNet::BitStream oobBs; - oobBs.Write((unsigned char)oob); - if (extraData) - { - extraData->ResetReadPointer(); - oobBs.Write(*extraData); - } - char ipAddressString[32]; - sa.ToString(false, ipAddressString); - rakPeerInterface->SendOutOfBand((const char*) ipAddressString,sa.GetPort(),(const char*) oobBs.GetData(),oobBs.GetNumberOfBytesUsed()); -} -void Router2::SendOOBFromSpecifiedSocket(OutOfBandIdentifiers oob, SystemAddress sa, SOCKET socket) -{ - RakNet::BitStream bs; - rakPeerInterface->WriteOutOfBandHeader(&bs); - bs.Write((unsigned char) oob); - SocketLayer::SendTo_PC( socket, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), sa, __FILE__, __LINE__ ); -} -void Router2::SendOOBMessages(Router2::MiniPunchRequest *mpr) -{ - // Mini NAT punch - // Send from srcToDestPort to packet->systemAddress (source). If the message arrives, the remote system should reply. - SendOOBFromSpecifiedSocket(ID_ROUTER_2_REPLY_TO_SENDER_PORT, mpr->sourceAddress, mpr->forwardingSocket); - - // Send from destToSourcePort to endpointSystemAddress (destination). If the message arrives, the remote system should reply. - SendOOBFromSpecifiedSocket(ID_ROUTER_2_REPLY_TO_SENDER_PORT, mpr->endpointAddress, mpr->forwardingSocket); - - - if (debugInterface) { - char buff [512]; - - char buff2[128]; - - mpr->sourceAddress .ToString(true,buff2); - - debugInterface->ShowDiagnostic(FormatStringTS(buff,"call SendOOBFromSpecifiedSocket(...,%s,...)", buff2)); - - mpr->endpointAddress .ToString(true,buff2); - - debugInterface->ShowDiagnostic(FormatStringTS(buff,"call SendOOBFromSpecifiedSocket(...,%s,...)", buff2)); - } - - // Tell source to send to forwardingPort - RakNet::BitStream extraData; - extraData.Write(mpr->forwardingPort); - RakAssert(mpr->forwardingPort!=0); - SendOOBFromRakNetPort(ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, &extraData, mpr->sourceAddress); - - // Tell destination to send to forwardingPort - extraData.Reset(); - extraData.Write(mpr->forwardingPort); - RakAssert(mpr->forwardingPort); - SendOOBFromRakNetPort(ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, &extraData, mpr->endpointAddress); -} -void Router2::OnRequestForwarding(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); - RakNetGUID endpointGuid; - bs.Read(endpointGuid); - - int pingToEndpoint = ReturnFailureOnCannotForward(packet->guid, endpointGuid); - if (pingToEndpoint==-1) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (packet->guid =%I64d, endpointGuid = %I64d) at %s:%i\n", - packet->guid.g, endpointGuid.g, __FILE__, __LINE__)); - return; - } - - unsigned short forwardingPort=0; - SOCKET forwardingSocket=0; - SystemAddress endpointSystemAddress = rakPeerInterface->GetSystemAddressFromGuid(endpointGuid); - UDPForwarderResult result = udpForwarder->StartForwarding( - packet->systemAddress, endpointSystemAddress, 30000, 0, socketFamily, - &forwardingPort, &forwardingSocket); - - if (result==UDPFORWARDER_FORWARDING_ALREADY_EXISTS) - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING, result=UDPFORWARDER_FORWARDING_ALREADY_EXISTS " - "(packet->guid =%I64d, endpointGuid = %I64d) at %s:%i\n", - packet->guid.g, endpointGuid.g,__FILE__, __LINE__)); - } - - SendForwardingSuccess(ID_ROUTER_2_FORWARDING_ESTABLISHED, packet->guid, endpointGuid, forwardingPort); - } - else if (result==UDPFORWARDER_NO_SOCKETS) - { - char buff[512]; - char buff2[64]; - char buff3[64]; - packet->systemAddress.ToString(true,buff2); - endpointSystemAddress.ToString(true,buff3); - if (debugInterface) - debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_NO_SOCKETS, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", - __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); - SendFailureOnCannotForward(packet->guid, endpointGuid); - } - else if (result==UDPFORWARDER_INVALID_PARAMETERS) - { - char buff[512]; - char buff2[64]; - char buff3[64]; - packet->systemAddress.ToString(true,buff2); - endpointSystemAddress.ToString(true,buff3); - if (debugInterface) - debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_INVALID_PARAMETERS, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", - __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); - SendFailureOnCannotForward(packet->guid, endpointGuid); - } - else if (result==UDPFORWARDER_BIND_FAILED) - { - char buff[512]; - char buff2[64]; - char buff3[64]; - packet->systemAddress.ToString(true,buff2); - endpointSystemAddress.ToString(true,buff3); - if (debugInterface) - debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_BIND_FAILED, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", - __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); - SendFailureOnCannotForward(packet->guid, endpointGuid); - } - else - { - if (debugInterface) - { - char buff2[32]; - char buff3[32]; - endpointSystemAddress.ToString(true,buff2); - packet->systemAddress.ToString(true,buff3); - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING.\n" - "endpointAddress=%s\nsourceAddress=%s\nforwardingPort=%i\n " - "calling SendOOBMessages at %s:%i\n", buff2,buff3,forwardingPort,_FILE_AND_LINE_)); - } - - // Store the punch request - MiniPunchRequest miniPunchRequest; - miniPunchRequest.endpointAddress=endpointSystemAddress; - miniPunchRequest.endpointGuid=endpointGuid; - miniPunchRequest.gotReplyFromEndpoint=false; - miniPunchRequest.gotReplyFromSource=false; - miniPunchRequest.sourceGuid=packet->guid; - miniPunchRequest.sourceAddress=packet->systemAddress; - miniPunchRequest.forwardingPort=forwardingPort; - miniPunchRequest.forwardingSocket=forwardingSocket; - int ping1 = rakPeerInterface->GetAveragePing(packet->guid); - int ping2 = rakPeerInterface->GetAveragePing(endpointGuid); - if (ping1>ping2) - miniPunchRequest.timeout=RakNet::GetTimeMS() + ping1*8+300; - else - miniPunchRequest.timeout=RakNet::GetTimeMS() + ping2*8+300; - miniPunchRequest.nextAction=RakNet::GetTimeMS()+100; - SendOOBMessages(&miniPunchRequest); - miniPunchesInProgressMutex.Lock(); - miniPunchesInProgress.Push(miniPunchRequest,_FILE_AND_LINE_); - miniPunchesInProgressMutex.Unlock(); - } -} -void Router2::OnMiniPunchReplyBounce(Packet *packet) -{ - // Find stored punch request - unsigned int i=0; - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE from guid=%I64d (miniPunchesInProgress.Size() = %d)", - packet->guid.g, miniPunchesInProgress.Size())); - } - - miniPunchesInProgressMutex.Lock(); - while (i < miniPunchesInProgress.Size()) - { - if (miniPunchesInProgress[i].sourceGuid==packet->guid || miniPunchesInProgress[i].endpointGuid==packet->guid) - { - if (miniPunchesInProgress[i].sourceGuid==packet->guid) - miniPunchesInProgress[i].gotReplyFromSource=true; - if (miniPunchesInProgress[i].endpointGuid==packet->guid) - miniPunchesInProgress[i].gotReplyFromEndpoint=true; - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Processing ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, gotReplyFromSource=%i gotReplyFromEndpoint=%i at %s:%i\n", miniPunchesInProgress[i].gotReplyFromSource, miniPunchesInProgress[i].gotReplyFromEndpoint, __FILE__, __LINE__)); - } - - if (miniPunchesInProgress[i].gotReplyFromEndpoint==true && - miniPunchesInProgress[i].gotReplyFromSource==true) - { - SendForwardingSuccess(ID_ROUTER_2_REROUTED, miniPunchesInProgress[i].endpointGuid, miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].forwardingPort); - SendForwardingSuccess(ID_ROUTER_2_FORWARDING_ESTABLISHED, miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].endpointGuid, miniPunchesInProgress[i].forwardingPort); - miniPunchesInProgress.RemoveAtIndexFast(i); - } - else - { - i++; - } - } - else - i++; - } - miniPunchesInProgressMutex.Unlock(); -} -void Router2::OnMiniPunchReply(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); - RakNetGUID routerGuid; - bs.Read(routerGuid); - SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, 0, rakPeerInterface->GetSystemAddressFromGuid(routerGuid)); - - if (debugInterface) - { - char buff[512]; - - char buff2[512]; - - rakPeerInterface->GetSystemAddressFromGuid(routerGuid).ToString(true,buff2); - - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE (%s) at %s:%i\n", buff2, __FILE__, __LINE__)); - } -} -void Router2::OnRerouted(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID endpointGuid; - bs.Read(endpointGuid); - unsigned short sourceToDestPort; - bs.Read(sourceToDestPort); - - // Return rerouted notice - SystemAddress intermediaryAddress=packet->systemAddress; - intermediaryAddress.SetPort(sourceToDestPort); - rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); - - unsigned int forwardingIndex; - forwardedConnectionListMutex.Lock(); - for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) - { - if (forwardedConnectionList[forwardingIndex].endpointGuid==endpointGuid) - break; - } - - if (forwardingIndexsystemAddress; - ref_fc.intermediaryAddress.SetPort(sourceToDestPort); - ref_fc.intermediaryGuid = packet->guid; - - rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"FIX: Got ID_ROUTER_2_REROUTE, returning ID_ROUTER_2_REROUTED," - " Calling RakPeer::ChangeSystemAddress(%I64d, %s) at %s:%i\n",endpointGuid.g, intermediaryAddress.ToString(true), __FILE__, __LINE__)); - } - } - else - { - ForwardedConnection fc; - fc.endpointGuid=endpointGuid; - fc.intermediaryAddress=packet->systemAddress; - fc.intermediaryAddress.SetPort(sourceToDestPort); - fc.intermediaryGuid=packet->guid; - fc.weInitiatedForwarding=false; - // add to forwarding list. This is only here to avoid reporting direct connections in Router2::ReturnFailureOnCannotForward - forwardedConnectionList.Push (fc,__FILE__, __LINE__); - forwardedConnectionListMutex.Unlock(); - - rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REROUTE, returning ID_ROUTER_2_REROUTED, Calling RakPeer::ChangeSystemAddress at %s:%i\n", __FILE__, __LINE__)); - } - } - -} -bool Router2::OnForwardingSuccess(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID endpointGuid; - bs.Read(endpointGuid); - unsigned short sourceToDestPort; - bs.Read(sourceToDestPort); - - unsigned int forwardingIndex; - forwardedConnectionListMutex.Lock(); - for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) - { - if (forwardedConnectionList[forwardingIndex].endpointGuid==endpointGuid) - break; - } - - if (forwardingIndexsystemAddress; - intermediaryAddress.SetPort(sourceToDestPort); - rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); - - //////////////////////////////////////////////////////////////////////////// - ForwardedConnection& ref_fc = forwardedConnectionList[forwardingIndex]; - ref_fc.intermediaryAddress = packet->systemAddress; - ref_fc.intermediaryAddress.SetPort(sourceToDestPort); - ref_fc.intermediaryGuid = packet->guid; - //////////////////////////////////////////////////////////////////////////// - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_FORWARDING_ESTABLISHED, returning ID_ROUTER_2_REROUTED, Calling RakPeer::ChangeSystemAddress at %s:%i\n", _FILE_AND_LINE_)); - } - - packet->data[0]=ID_ROUTER_2_REROUTED; - - forwardedConnectionListMutex.Unlock(); - return true; // Return packet to user - } - else - { - forwardedConnectionListMutex.Unlock(); - - // removeFrom connectionRequests; - ForwardedConnection fc; - connectionRequestsMutex.Lock(); - unsigned int connectionRequestIndex = GetConnectionRequestIndex(endpointGuid); - fc.returnConnectionLostOnFailure=connectionRequests[connectionRequestIndex]->returnConnectionLostOnFailure; - connectionRequests.RemoveAtIndexFast(connectionRequestIndex); - connectionRequestsMutex.Unlock(); - fc.endpointGuid=endpointGuid; - fc.intermediaryAddress=packet->systemAddress; - fc.intermediaryAddress.SetPort(sourceToDestPort); - fc.intermediaryGuid=packet->guid; - fc.weInitiatedForwarding=true; - - // add to forwarding list - forwardedConnectionListMutex.Lock(); - forwardedConnectionList.Push (fc,_FILE_AND_LINE_); - forwardedConnectionListMutex.Unlock(); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got and returning to user ID_ROUTER_2_FORWARDING_ESTABLISHED at %s:%i\n", _FILE_AND_LINE_)); - } - - } - return true; // Return packet to user -} -int Router2::GetLargestPingAmongConnectedSystems(void) const -{ - int avePing; - int largestPing=-1; - unsigned short maxPeers = rakPeerInterface->GetMaximumNumberOfPeers(); - if (maxPeers==0) - return 9999; - unsigned short index; - for (index=0; index < rakPeerInterface->GetMaximumNumberOfPeers(); index++) - { - RakNetGUID g = rakPeerInterface->GetGUIDFromIndex(index); - if (g!=UNASSIGNED_RAKNET_GUID) - { - avePing=rakPeerInterface->GetAveragePing(rakPeerInterface->GetGUIDFromIndex(index)); - if (avePing>largestPing) - largestPing=avePing; - } - } - return largestPing; -} - -unsigned int Router2::GetConnectionRequestIndex(RakNetGUID endpointGuid) -{ - unsigned int i; - for (i=0; i < connectionRequests.Size(); i++) - { - if (connectionRequests[i]->endpointGuid==endpointGuid) - return i; - } - return (unsigned int) -1; -} -unsigned int Router2::ConnnectRequest::GetGuidIndex(RakNetGUID guid) -{ - unsigned int i; - for (i=0; i < connectionRequestSystems.Size(); i++) - { - if (connectionRequestSystems[i].guid==guid) - return i; - } - return (unsigned int) -1; -} -void Router2::ReturnToUser(MessageID messageId, RakNetGUID endpointGuid, const SystemAddress &systemAddress, bool wasGeneratedLocally) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); - p->data[0]=messageId; - p->systemAddress=systemAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=endpointGuid; - p->wasGeneratedLocally=wasGeneratedLocally; - rakPeerInterface->PushBackPacket(p, true); -} -void Router2::ClearForwardedConnections(void) -{ - forwardedConnectionListMutex.Lock(); - forwardedConnectionList.Clear(false,_FILE_AND_LINE_); - forwardedConnectionListMutex.Unlock(); -} -void Router2::ClearAll(void) -{ - ClearConnectionRequests(); - ClearMinipunches(); - ClearForwardedConnections(); -} -void Router2::SetDebugInterface(Router2DebugInterface *_debugInterface) -{ - debugInterface=_debugInterface; -} -Router2DebugInterface *Router2::GetDebugInterface(void) const -{ - return debugInterface; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_Router2==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#include "Router2.h" +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "RakNetTime.h" +#include "GetTime.h" +#include "DS_OrderedList.h" +#include "SocketLayer.h" +#include "FormatString.h" + +using namespace RakNet; + +/* +Algorithm: + +1. Sender calls ConnectInternal(). A ConnnectRequest structure is allocated and stored in the connectionRequests list, containing a list of every system we are connected to. ID_ROUTER_2_QUERY_FORWARDING is sent to all connected systems. + +2. Upon the router getting ID_ROUTER_2_QUERY_FORWARDING, ID_ROUTER_2_REPLY_FORWARDING is sent to the sender indicating if that router is connected to the endpoint, along with the ping from the router to the endpoint. + +3. Upon the sender getting ID_ROUTER_2_REPLY_FORWARDING, the connection request structure is looked up in Router2::UpdateForwarding. The ping is stored in that structure. Once all systems have replied, the system continues to the next state. If every system in step 1 has been exhausted, and routing has occured at least once, then ID_CONNECTION_LOST is returned. If every system in step 1 has been exhausted and routing has never occured, then ID_ROUTER_2_FORWARDING_NO_PATH is returned. Otherwise, the router with the lowest ping is chosen, and RequestForwarding() is called with that system, which sends ID_ROUTER_2_REQUEST_FORWARDING to the router. + +4. When the router gets ID_ROUTER_2_REQUEST_FORWARDING, a MiniPunchRequest structure is allocated and stored in the miniPunchesInProgress list. The function SendOOBMessages() sends ID_ROUTER_2_REPLY_TO_SENDER_PORT from the routing sockets to both the sender and endpoint. It also sends ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT through the regular RakNet connection. + +5. The sender and endpoint should get ID_ROUTER_2_REPLY_TO_SENDER_PORT and/or ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT depending on what type of router they have. If ID_ROUTER_2_REPLY_TO_SENDER_PORT arrives, then this will reply back to the routing socket directly. If ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT arrives, then the reply port is modified to be the port specified by the router system. In both cases, ID_ROUTER_2_MINI_PUNCH_REPLY is sent. As the router has already setup the forwarding, ID_ROUTER_2_MINI_PUNCH_REPLY will actually arrive to the endpoint from the sender, and from the sender to the endpoint. + +6. When ID_ROUTER_2_MINI_PUNCH_REPLY arrives, ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE will be sent to the router. This is to tell the router that the forwarding has succeeded. + +7. When ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE arrives on the router, the router will find the two systems in the miniPunchesInProgress list, which was added in step 4 (See OnMiniPunchReplyBounce()). gotReplyFromSource or gotReplyFromEndpoint will be set to true, depending on the sender. When both gotReplyFromSource and gotReplyFromEndpoint have replied, then ID_ROUTER_2_REROUTE is sent to the endpoint, and ID_ROUTER_2_FORWARDING_ESTABLISHED is sent to the sender. + +8. When the endpoint gets ID_ROUTER_2_REROUTE, the system address is updated for the guid of the sender using RakPeer::ChangeSystemAddress(). This happens in OnRerouted(). + +9. When the sender gets ID_ROUTER_2_FORWARDING_ESTABLISHED, then in OnForwardingSuccess() the endpoint is removed from the connectionRequest list and moved to the forwardedConnectionList. + +10. In OnClosedConnection(), for the sender, if the closed connection is the endpoint, then the endpoint is removed from the forwardedConnectionList (this is a graceful disconnect). If the connection was instead lost to the router, then ConnectInternal() gets called, which goes back to step 1. If instead this was a connection requset in progress, then UpdateForwarding() gets called, which goes back to step 3. + +11. When the user connects the endpoint and sender, then the sender will get ID_CONNECTION_REQUEST_ACCEPTED. The sender will look up the endpoint in the forwardedConnectionList, and send ID_ROUTER_2_INCREASE_TIMEOUT to the endpoint. This message will call SetTimeoutTime() on the endpoint, so that if the router disconnects, enough time is available for the reroute to complete. +*/ + +#define MIN_MINIPUNCH_TIMEOUT 5000 + + + + +void Router2DebugInterface::ShowFailure(const char *message) +{ + printf("%s", message); +} +void Router2DebugInterface::ShowDiagnostic(const char *message) +{ + printf("%s", message); +} + +enum Router2MessageIdentifiers +{ + ID_ROUTER_2_QUERY_FORWARDING, + ID_ROUTER_2_REPLY_FORWARDING, + ID_ROUTER_2_REQUEST_FORWARDING, + ID_ROUTER_2_INCREASE_TIMEOUT, +}; +Router2::ConnnectRequest::ConnnectRequest() +{ + +} +Router2::ConnnectRequest::~ConnnectRequest() +{ + +} + +STATIC_FACTORY_DEFINITIONS(Router2,Router2); + +Router2::Router2() +{ + udpForwarder=0; + maximumForwardingRequests=0; + debugInterface=0; + socketFamily=AF_INET; +} +Router2::~Router2() +{ + ClearAll(); + + if (udpForwarder) + { + udpForwarder->Shutdown(); + RakNet::OP_DELETE(udpForwarder,_FILE_AND_LINE_); + } +} +void Router2::ClearMinipunches(void) +{ + miniPunchesInProgressMutex.Lock(); + miniPunchesInProgress.Clear(false,_FILE_AND_LINE_); + miniPunchesInProgressMutex.Unlock(); +} +void Router2::ClearConnectionRequests(void) +{ + connectionRequestsMutex.Lock(); + for (unsigned int i=0; i < connectionRequests.Size(); i++) + { + RakNet::OP_DELETE(connectionRequests[i],_FILE_AND_LINE_); + } + connectionRequests.Clear(false,_FILE_AND_LINE_); + connectionRequestsMutex.Unlock(); +} +bool Router2::ConnectInternal(RakNetGUID endpointGuid, bool returnConnectionLostOnFailure) +{ + int largestPing = GetLargestPingAmongConnectedSystems(); + if (largestPing<0) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2: ConnectInternal(%I64d) failed at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); + + // Not connected to anyone + return false; + } + + // ALready in progress? + connectionRequestsMutex.Lock(); + if (GetConnectionRequestIndex(endpointGuid)!=(unsigned int)-1) + { + connectionRequestsMutex.Unlock(); + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2: ConnectInternal(%I64d) failed at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); + + return false; + } + connectionRequestsMutex.Unlock(); + + // StoreRequest(endpointGuid, Largest(ping*2), systemsSentTo). Set state REQUEST_STATE_QUERY_FORWARDING + Router2::ConnnectRequest *cr = RakNet::OP_NEW(_FILE_AND_LINE_); + DataStructures::List addresses; + DataStructures::List guids; + rakPeerInterface->GetSystemList(addresses, guids); + if (guids.Size()==0) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", _FILE_AND_LINE_)); + + return false; + } + cr->requestState=R2RS_REQUEST_STATE_QUERY_FORWARDING; + cr->pingTimeout=RakNet::GetTimeMS()+largestPing*2+1000; + cr->endpointGuid=endpointGuid; + cr->returnConnectionLostOnFailure=returnConnectionLostOnFailure; + for (unsigned int i=0; i < guids.Size(); i++) + { + ConnectionRequestSystem crs; + if (guids[i]!=endpointGuid) + { + crs.guid=guids[i]; + crs.pingToEndpoint=-1; + cr->connectionRequestSystemsMutex.Lock(); + cr->connectionRequestSystems.Push(crs,_FILE_AND_LINE_); + cr->connectionRequestSystemsMutex.Unlock(); + + // Broadcast(ID_ROUTER_2_QUERY_FORWARDING, endpointGuid); + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); + bsOut.Write((unsigned char) ID_ROUTER_2_QUERY_FORWARDING); + bsOut.Write(endpointGuid); + uint32_t pack_id = rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,crs.guid,false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Router2::ConnectInternal: at %s:%i, pack_id = %d", __FILE__, __LINE__,pack_id)); + } + + } + else + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Router2::ConnectInternal: at %s:%i [else ..].: %I64d==%I64d", __FILE__, __LINE__, + guids[i].g,endpointGuid.g)); + } + } + } + connectionRequestsMutex.Lock(); + connectionRequests.Push(cr,_FILE_AND_LINE_); + connectionRequestsMutex.Unlock(); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Broadcasting ID_ROUTER_2_QUERY_FORWARDING to %I64d at %s:%i\n", endpointGuid.g , __FILE__, __LINE__)); + } + + return true; +} +void Router2::SetSocketFamily(unsigned short _socketFamily) +{ + socketFamily=_socketFamily; +} +void Router2::EstablishRouting(RakNetGUID endpointGuid) +{ + ConnectionState cs = rakPeerInterface->GetConnectionState(endpointGuid); + if (cs!=IS_DISCONNECTED && cs!=IS_NOT_CONNECTED) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i " + "(already connected to the %I64d)\n", __FILE__, __LINE__, endpointGuid.g )); + return; + } + + ConnectInternal(endpointGuid,false); +} +void Router2::SetMaximumForwardingRequests(int max) +{ + if (max>0 && maximumForwardingRequests<=0) + { + udpForwarder = RakNet::OP_NEW(_FILE_AND_LINE_); + udpForwarder->Startup(); + } + else if (max<=0 && maximumForwardingRequests>0) + { + udpForwarder->Shutdown(); + RakNet::OP_DELETE(udpForwarder,_FILE_AND_LINE_); + udpForwarder=0; + } + + maximumForwardingRequests=max; +} +PluginReceiveResult Router2::OnReceive(Packet *packet) +{ + SystemAddress sa; + RakNet::BitStream bs(packet->data,packet->length,false); + if (packet->data[0]==ID_ROUTER_2_INTERNAL) + { + switch (packet->data[1]) + { + case ID_ROUTER_2_QUERY_FORWARDING: + { + OnQueryForwarding(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_REPLY_FORWARDING: + { + OnQueryForwardingReply(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_REQUEST_FORWARDING: + { + + if (debugInterface) + { + char buff[512]; + char buff2[32]; + packet->systemAddress.ToString(true,buff2); + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING on ip %s from %I64d, ", + buff2,packet->guid.g)); + } + + OnRequestForwarding(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_INCREASE_TIMEOUT: + { + /// The routed system wants more time to stay alive on no communication, in case the router drops or crashes + rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + } + else if (packet->data[0]==ID_OUT_OF_BAND_INTERNAL && packet->length>=2) + { + switch (packet->data[1]) + { + case ID_ROUTER_2_REPLY_TO_SENDER_PORT: + { + RakNet::BitStream bsOut; + bsOut.Write(packet->guid); + SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, packet->systemAddress); + + if (debugInterface) + { + char buff[512]; + char buff2[32]; + sa.ToString(false,buff2); + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT %i on address %s, replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, _FILE_AND_LINE_)); + +// packet->systemAddress.ToString(true,buff2); +// debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT on address %s (%I64d), " +// "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", buff2,packet->guid.g, __FILE__, __LINE__)); + } + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT: + { + RakNet::BitStream bsOut; + bsOut.Write(packet->guid); + bs.IgnoreBytes(2); + sa=packet->systemAddress; + unsigned short port; + bs.Read(port); + sa.SetPort(port); + RakAssert(sa.GetPort()!=0); + SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, sa); + + if (debugInterface) + { + char buff[512]; + char buff2[32]; + sa.ToString(false,buff2); + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT %i on address %s, " + "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, __FILE__, __LINE__)); + } + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_MINI_PUNCH_REPLY: + OnMiniPunchReply(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE: + OnMiniPunchReplyBounce(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + else if (packet->data[0]==ID_ROUTER_2_FORWARDING_ESTABLISHED) + { +// printf("Got ID_ROUTER_2_FORWARDING_ESTABLISHED\n"); + if (OnForwardingSuccess(packet)==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + else if (packet->data[0]==ID_ROUTER_2_REROUTED) + { + OnRerouted(packet); + } + else if (packet->data[0]==ID_CONNECTION_REQUEST_ACCEPTED) + { + unsigned int forwardingIndex; + forwardedConnectionListMutex.Lock(); + for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) + { + if (forwardedConnectionList[forwardingIndex].endpointGuid==packet->guid && forwardedConnectionList[forwardingIndex].weInitiatedForwarding) + break; + } + + if (forwardingIndexSend(&bsOut,HIGH_PRIORITY,RELIABLE,0,packet->guid,false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_CONNECTION_REQUEST_ACCEPTED, " + "sending ID_ROUTER_2_INCREASE_TIMEOUT to the %I64d at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); + } + + // Also take longer ourselves + rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress); + } + else { + // ~Gwynn: Fix for Receive hanging up problem on Windows XP + // See http://blog.delphi-jedi.net/2008/04/23/the-case-of-the-unexplained-dead-lock-in-a-single-thread/ for details + forwardedConnectionListMutex.Unlock(); + } + } + else if (packet->data[0]==ID_ROUTER_2_FORWARDING_NO_PATH) + { + if (packet->wasGeneratedLocally==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + return RR_CONTINUE_PROCESSING; +} +void Router2::Update(void) +{ + RakNet::TimeMS curTime = RakNet::GetTimeMS(); + unsigned int connectionRequestIndex=0; + connectionRequestsMutex.Lock(); + while (connectionRequestIndex < connectionRequests.Size()) + { + ConnnectRequest* connectionRequest = connectionRequests[connectionRequestIndex]; + + // pingTimeout is only used with R2RS_REQUEST_STATE_QUERY_FORWARDING + if (connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING && + connectionRequest->pingTimeout < curTime) + { + bool anyRemoved=false; + unsigned int connectionRequestGuidIndex=0; + connectionRequest->connectionRequestSystemsMutex.Lock(); + while (connectionRequestGuidIndex < connectionRequest->connectionRequestSystems.Size()) + { + if (connectionRequest->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint<0) + { + anyRemoved=true; + connectionRequest->connectionRequestSystems.RemoveAtIndexFast(connectionRequestGuidIndex); + } + else + { + connectionRequestGuidIndex++; + } + } + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + if (anyRemoved) + { + if (connectionRequestIndex!=(unsigned int)-1) + { + // connectionRequestsMutex should be locked before calling this function + if (UpdateForwarding(connectionRequest)==false) + { + RemoveConnectionRequest(connectionRequestIndex); + } + else + { + connectionRequestIndex++; + } + } + else + { + connectionRequestIndex++; + } + } + else + { + connectionRequestIndex++; + } + } + else + { + connectionRequestIndex++; + } + } + connectionRequestsMutex.Unlock(); + + unsigned int i=0; + miniPunchesInProgressMutex.Lock(); + while (i < miniPunchesInProgress.Size()) + { + if (miniPunchesInProgress[i].timeoutminiPunchesInProgress[i].nextAction) + { + miniPunchesInProgress[i].nextAction=curTime+100; + SendOOBMessages(&miniPunchesInProgress[i]); + } + else + i++; + } + miniPunchesInProgressMutex.Unlock(); + +} +void Router2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + + + unsigned int forwardedConnectionIndex=0; + forwardedConnectionListMutex.Lock(); + while (forwardedConnectionIndexShowDiagnostic(FormatStringTS(buff,"Closed connection to the %I64d, removing forwarding from list at %s:%i\n", rakNetGUID.g, __FILE__, __LINE__)); + } + + // No longer need forwarding + forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); + } + else if (forwardedConnectionList[forwardedConnectionIndex].intermediaryGuid==rakNetGUID && forwardedConnectionList[forwardedConnectionIndex].weInitiatedForwarding) + { + // Lost connection to intermediary. Restart process to connect to endpoint. If failed, push ID_CONNECTION_LOST. Also remove connection request if it already is in the list, to restart it + connectionRequestsMutex.Lock(); + unsigned int pos = GetConnectionRequestIndex(forwardedConnectionList[forwardedConnectionIndex].endpointGuid); + if((unsigned int)-1 != pos) {RakNet::OP_DELETE(connectionRequests[pos], __FILE__, __LINE__); connectionRequests.RemoveAtIndexFast(pos);} + connectionRequestsMutex.Unlock(); + + ConnectInternal(forwardedConnectionList[forwardedConnectionIndex].endpointGuid, true); + + forwardedConnectionIndex++; + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Closed connection %I64d, restarting forwarding at %s:%i\n",rakNetGUID.g, __FILE__, __LINE__)); + } + + // This should not be removed - the connection is still forwarded, but perhaps through another system +// forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); + } + else + forwardedConnectionIndex++; + } + forwardedConnectionListMutex.Unlock(); + + unsigned int connectionRequestIndex=0; + connectionRequestsMutex.Lock(); + while (connectionRequestIndex < connectionRequests.Size()) + { + ConnnectRequest *cr = connectionRequests[connectionRequestIndex]; + cr->connectionRequestSystemsMutex.Lock(); + unsigned int connectionRequestGuidIndex = cr->GetGuidIndex(rakNetGUID); + if (connectionRequestGuidIndex!=(unsigned int)-1) + { + cr->connectionRequestSystems.RemoveAtIndexFast(connectionRequestGuidIndex); + cr->connectionRequestSystemsMutex.Unlock(); + if (UpdateForwarding(cr)==false) // If returns false, no connection request systems left + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Aborted connection to the %I64d, aborted forwarding at %s:%i\n", rakNetGUID.g, __FILE__, __LINE__)); + } + + RemoveConnectionRequest(connectionRequestIndex); + } + else // Else a system in the connection request list dropped. If cr->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING then we are still waiting for other systems to respond. + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Aborted connection attempt to %I64d, restarting forwarding to %I64d at %s:%i\n", rakNetGUID.g,cr->endpointGuid.g,__FILE__, __LINE__)); + } +// if(volatile bool is_my_fix_a_truth = true) { // A system in the list of potential systems to try routing to dropped. There is no need to restart the whole process. +// connectionRequestsMutex.Lock(); +// connectionRequests.RemoveAtIndexFast(connectionRequestIndex); +// connectionRequestsMutex.Unlock(); +// +// if(false == ConnectInternal(cr->endpointGuid,cr->returnConnectionLostOnFailure)) +// if (debugInterface) +// { +// char buff[512]; +// debugInterface->ShowDiagnostic(FormatStringTS(buff,"ConnectInternal(cr->endpointGuid,cr->returnConnectionLostOnFailure) is false. at %s:%i\n", __FILE__, __LINE__)); +// } +// } + + connectionRequestIndex++; + } + } + else + { + cr->connectionRequestSystemsMutex.Unlock(); + connectionRequestIndex++; + } + } + connectionRequestsMutex.Unlock(); + + + unsigned int i=0; + miniPunchesInProgressMutex.Lock(); + while (i < miniPunchesInProgress.Size()) + { + if (miniPunchesInProgress[i].sourceGuid==rakNetGUID || miniPunchesInProgress[i].endpointGuid==rakNetGUID) + { + if (miniPunchesInProgress[i].sourceGuid!=rakNetGUID) + { + SendFailureOnCannotForward(miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].endpointGuid); + } + miniPunchesInProgress.RemoveAtIndexFast(i); + } + else + i++; + } + miniPunchesInProgressMutex.Unlock(); +} +void Router2::OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason) +{ + (void) failedConnectionAttemptReason; + (void) packet; + + unsigned int forwardedConnectionIndex=0; + forwardedConnectionListMutex.Lock(); + while (forwardedConnectionIndexsystemAddress) + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Failed connection attempt to forwarded system (%I64d : %s) at %s:%i\n", + forwardedConnectionList[forwardedConnectionIndex].endpointGuid.g, packet->systemAddress.ToString(true), __FILE__, __LINE__)); + } + + packet->guid=forwardedConnectionList[forwardedConnectionIndex].endpointGuid; + forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); + } + else + forwardedConnectionIndex++; + } + forwardedConnectionListMutex.Unlock(); +} +void Router2::OnRakPeerShutdown(void) +{ + ClearAll(); +} +// connectionRequestsMutex should already be locked +bool Router2::UpdateForwarding(ConnnectRequest* connectionRequest) +{ + connectionRequest->connectionRequestSystemsMutex.Lock(); + + // RAKNET_DEBUG_PRINTF(__FUNCTION__": connectionRequest->connectionRequestSystems.Size = %d", connectionRequest->connectionRequestSystems.Size()); + + if (connectionRequest->connectionRequestSystems.Size()==0) + { + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + // printf("Router2 failed at %s:%i\n", __FILE__, __LINE__); + if (connectionRequest->returnConnectionLostOnFailure) { + ReturnToUser(ID_CONNECTION_LOST, connectionRequest->endpointGuid, UNASSIGNED_SYSTEM_ADDRESS, true); // This is a connection which was previously established. Rerouting is not possible. +// bool sendDisconnectionNotification = false; +// rakPeerInterface->CloseConnection(rakPeerInterface->GetSystemAddressFromGuid(connectionRequest->endpointGuid), sendDisconnectionNotification); +// RAKNET_DEBUG_PRINTF(__FUNCTION__": call rakPeerInterface->CloseConnection(%I64d)" , connectionRequest->endpointGuid.g); + } + else + ReturnToUser(ID_ROUTER_2_FORWARDING_NO_PATH, connectionRequest->endpointGuid, UNASSIGNED_SYSTEM_ADDRESS, false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Forwarding failed, no remaining systems at %s:%i\n", _FILE_AND_LINE_)); + } + + forwardedConnectionListMutex.Lock(); + + for (unsigned int forwardedConnectionIndex=0; forwardedConnectionIndex < forwardedConnectionList.Size(); forwardedConnectionIndex++) + { + if (forwardedConnectionList[forwardedConnectionIndex].endpointGuid==connectionRequest->endpointGuid && forwardedConnectionList[forwardedConnectionIndex].weInitiatedForwarding) + { + forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); + break; + } + } + forwardedConnectionListMutex.Unlock(); + + return false; + } + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + if (connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING) + { + connectionRequest->connectionRequestSystemsMutex.Lock(); + + for (unsigned int i=0; i < connectionRequest->connectionRequestSystems.Size(); i++) + { + if (connectionRequest->connectionRequestSystems[i].pingToEndpoint<0) + { + connectionRequest->connectionRequestSystemsMutex.Unlock(); + return true; // Forward query still in progress, just return + } + } + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + RequestForwarding(connectionRequest); + } +// else if (connectionRequest->requestState==REQUEST_STATE_REQUEST_FORWARDING) +// { +// RequestForwarding(connectionRequestIndex); +// } + + return true; +} +// connectionRequestsMutex should already be locked +void Router2::RemoveConnectionRequest(unsigned int connectionRequestIndex) +{ + RakNet::OP_DELETE(connectionRequests[connectionRequestIndex],_FILE_AND_LINE_); + connectionRequests.RemoveAtIndexFast(connectionRequestIndex); +} +int ConnectionRequestSystemComp( const Router2::ConnectionRequestSystem & key, const Router2::ConnectionRequestSystem &data ) +{ + if (key.pingToEndpoint * (key.usedForwardingEntries+1) < data.pingToEndpoint * (data.usedForwardingEntries+1)) + return -1; + if (key.pingToEndpoint * (key.usedForwardingEntries+1) == data.pingToEndpoint * (data.usedForwardingEntries+1)) + return 1; + if (key.guid < data.guid) + return -1; + if (key.guid > data.guid) + return -1; + return 0; +} +// connectionRequestsMutex should already be locked +void Router2::RequestForwarding(ConnnectRequest* connectionRequest) +{ + RakAssert(connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING); + connectionRequest->requestState=REQUEST_STATE_REQUEST_FORWARDING; + + if (connectionRequest->GetGuidIndex(connectionRequest->lastRequestedForwardingSystem)!=(unsigned int)-1) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", _FILE_AND_LINE_)); + return; + } + + // Prioritize systems to request forwarding + DataStructures::OrderedList commandList; + unsigned int connectionRequestGuidIndex; + connectionRequest->connectionRequestSystemsMutex.Lock(); + for (connectionRequestGuidIndex=0; connectionRequestGuidIndex < connectionRequest->connectionRequestSystems.Size(); connectionRequestGuidIndex++) + { + RakAssert(connectionRequest->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint>=0); + commandList.Insert(connectionRequest->connectionRequestSystems[connectionRequestGuidIndex], + connectionRequest->connectionRequestSystems[connectionRequestGuidIndex], + true, + _FILE_AND_LINE_); + } + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + connectionRequest->lastRequestedForwardingSystem=commandList[0].guid; + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); + bsOut.Write((unsigned char) ID_ROUTER_2_REQUEST_FORWARDING); + bsOut.Write(connectionRequest->endpointGuid); + rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,connectionRequest->lastRequestedForwardingSystem,false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REQUEST_FORWARDING " + "(connectionRequest->lastRequestedForwardingSystem = %I64d, connectionRequest->endpointGuid = %I64d) at %s:%i\n", + connectionRequest->lastRequestedForwardingSystem.g,connectionRequest->endpointGuid.g, __FILE__, __LINE__)); + } +} +void Router2::SendFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); + bsOut.Write((unsigned char) ID_ROUTER_2_REPLY_FORWARDING); + bsOut.Write(endpointGuid); + bsOut.Write(false); + rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,sourceGuid,false); +} +int Router2::ReturnFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid) +{ + // If the number of systems we are currently forwarding>=maxForwarding, return ID_ROUTER_2_REPLY_FORWARDING,endpointGuid,false + if (udpForwarder==0 || udpForwarder->GetUsedForwardEntries()/2>maximumForwardingRequests) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d -> %I64d) at %s:%i\n", + sourceGuid.g, endpointGuid.g,__FILE__, __LINE__)); + SendFailureOnCannotForward(sourceGuid,endpointGuid); + return -1; + } + + // We cannot forward connections which are themselves forwarded. Return fail in that case + forwardedConnectionListMutex.Lock(); + for (unsigned int i=0; i < forwardedConnectionList.Size(); i++) + { + if ((forwardedConnectionList[i].endpointGuid==endpointGuid) + || (forwardedConnectionList[i].endpointGuid==sourceGuid)) // sourceGuid is here so you do not respond to routing requests from systems you are already routing through. + { + forwardedConnectionListMutex.Unlock(); + + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", __FILE__, __LINE__)); + SendFailureOnCannotForward(sourceGuid,endpointGuid); + return -1; + } + } + forwardedConnectionListMutex.Unlock(); + + int pingToEndpoint; + pingToEndpoint = rakPeerInterface->GetAveragePing(endpointGuid); + if (pingToEndpoint==-1) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d -> %I64d) at %s:%i\n", + sourceGuid.g, endpointGuid.g,__FILE__, __LINE__)); + + SendFailureOnCannotForward(sourceGuid,endpointGuid); + return -1; + } + return pingToEndpoint; +} +void Router2::OnQueryForwarding(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); + RakNetGUID endpointGuid; + // Read endpointGuid + bs.Read(endpointGuid); + + int pingToEndpoint = ReturnFailureOnCannotForward(packet->guid, endpointGuid); + if (pingToEndpoint==-1) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); + return; + } + + // If we are connected to endpointGuid, reply ID_ROUTER_2_REPLY_FORWARDING,endpointGuid,true,ping,numCurrentlyForwarding + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); + bsOut.Write((unsigned char) ID_ROUTER_2_REPLY_FORWARDING); + bsOut.Write(endpointGuid); + bsOut.Write(true); + bsOut.Write((unsigned short) pingToEndpoint); + bsOut.Write((unsigned short) udpForwarder->GetUsedForwardEntries()/2); + rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,packet->guid,false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REPLY_FORWARDING to the %I64d at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); + } +} +void Router2::OnQueryForwardingReply(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); + RakNetGUID endpointGuid; + bs.Read(endpointGuid); + // Find endpointGuid among stored requests + bool canForward=false; + bs.Read(canForward); + + + connectionRequestsMutex.Lock(); + unsigned int connectionRequestIndex = GetConnectionRequestIndex(endpointGuid); + if (connectionRequestIndex==(unsigned int)-1) + { + connectionRequestsMutex.Unlock(); + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); + return; + } + + connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Lock(); + unsigned int connectionRequestGuidIndex = connectionRequests[connectionRequestIndex]->GetGuidIndex(packet->guid); + if (connectionRequestGuidIndex==(unsigned int)-1) + { + connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Unlock(); + connectionRequestsMutex.Unlock(); + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); + return; + } + + if (debugInterface) + { + char buff[512]; + char buff2[512]; + packet->systemAddress.ToString(true,buff2); + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_FORWARDING on address %s(%I64d -> %I64d) canForward=%i at %s:%i\n", + buff2, packet->guid.g, endpointGuid.g, canForward, __FILE__, __LINE__)); + } + + if (canForward) + { + unsigned short pingToEndpoint; + unsigned short usedEntries; + bs.Read(pingToEndpoint); + bs.Read(usedEntries); + connectionRequests[connectionRequestIndex]->connectionRequestSystems[connectionRequestGuidIndex].usedForwardingEntries=usedEntries; + connectionRequests[connectionRequestIndex]->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint=rakPeerInterface->GetAveragePing(packet->guid)+pingToEndpoint; + } + else + { + connectionRequests[connectionRequestIndex]->connectionRequestSystems.RemoveAtIndex(connectionRequestGuidIndex); + } + connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Unlock(); + + if (UpdateForwarding(connectionRequests[connectionRequestIndex])==false) + { + RemoveConnectionRequest(connectionRequestIndex); + } + connectionRequestsMutex.Unlock(); +} +void Router2::SendForwardingSuccess(MessageID messageId, RakNetGUID sourceGuid, RakNetGUID endpointGuid, unsigned short sourceToDstPort) +{ + RakNet::BitStream bsOut; + bsOut.Write(messageId); + bsOut.Write(endpointGuid); + bsOut.Write(sourceToDstPort); + rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,sourceGuid,false); + + if (debugInterface) + { + char buff[512]; + if (messageId==ID_ROUTER_2_FORWARDING_ESTABLISHED) + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_FORWARDING_ESTABLISHED at %s:%i\n", _FILE_AND_LINE_ )); + else + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REROUTED at %s:%i\n", _FILE_AND_LINE_ )); + } +} +void Router2::SendOOBFromRakNetPort(OutOfBandIdentifiers oob, BitStream *extraData, SystemAddress sa) +{ + RakNet::BitStream oobBs; + oobBs.Write((unsigned char)oob); + if (extraData) + { + extraData->ResetReadPointer(); + oobBs.Write(*extraData); + } + char ipAddressString[32]; + sa.ToString(false, ipAddressString); + rakPeerInterface->SendOutOfBand((const char*) ipAddressString,sa.GetPort(),(const char*) oobBs.GetData(),oobBs.GetNumberOfBytesUsed()); +} +void Router2::SendOOBFromSpecifiedSocket(OutOfBandIdentifiers oob, SystemAddress sa, SOCKET socket) +{ + RakNet::BitStream bs; + rakPeerInterface->WriteOutOfBandHeader(&bs); + bs.Write((unsigned char) oob); + SocketLayer::SendTo_PC( socket, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), sa, __FILE__, __LINE__ ); +} +void Router2::SendOOBMessages(Router2::MiniPunchRequest *mpr) +{ + // Mini NAT punch + // Send from srcToDestPort to packet->systemAddress (source). If the message arrives, the remote system should reply. + SendOOBFromSpecifiedSocket(ID_ROUTER_2_REPLY_TO_SENDER_PORT, mpr->sourceAddress, mpr->forwardingSocket); + + // Send from destToSourcePort to endpointSystemAddress (destination). If the message arrives, the remote system should reply. + SendOOBFromSpecifiedSocket(ID_ROUTER_2_REPLY_TO_SENDER_PORT, mpr->endpointAddress, mpr->forwardingSocket); + + + if (debugInterface) { + char buff [512]; + + char buff2[128]; + + mpr->sourceAddress .ToString(true,buff2); + + debugInterface->ShowDiagnostic(FormatStringTS(buff,"call SendOOBFromSpecifiedSocket(...,%s,...)", buff2)); + + mpr->endpointAddress .ToString(true,buff2); + + debugInterface->ShowDiagnostic(FormatStringTS(buff,"call SendOOBFromSpecifiedSocket(...,%s,...)", buff2)); + } + + // Tell source to send to forwardingPort + RakNet::BitStream extraData; + extraData.Write(mpr->forwardingPort); + RakAssert(mpr->forwardingPort!=0); + SendOOBFromRakNetPort(ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, &extraData, mpr->sourceAddress); + + // Tell destination to send to forwardingPort + extraData.Reset(); + extraData.Write(mpr->forwardingPort); + RakAssert(mpr->forwardingPort); + SendOOBFromRakNetPort(ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, &extraData, mpr->endpointAddress); +} +void Router2::OnRequestForwarding(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); + RakNetGUID endpointGuid; + bs.Read(endpointGuid); + + int pingToEndpoint = ReturnFailureOnCannotForward(packet->guid, endpointGuid); + if (pingToEndpoint==-1) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (packet->guid =%I64d, endpointGuid = %I64d) at %s:%i\n", + packet->guid.g, endpointGuid.g, __FILE__, __LINE__)); + return; + } + + unsigned short forwardingPort=0; + SOCKET forwardingSocket=0; + SystemAddress endpointSystemAddress = rakPeerInterface->GetSystemAddressFromGuid(endpointGuid); + UDPForwarderResult result = udpForwarder->StartForwarding( + packet->systemAddress, endpointSystemAddress, 30000, 0, socketFamily, + &forwardingPort, &forwardingSocket); + + if (result==UDPFORWARDER_FORWARDING_ALREADY_EXISTS) + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING, result=UDPFORWARDER_FORWARDING_ALREADY_EXISTS " + "(packet->guid =%I64d, endpointGuid = %I64d) at %s:%i\n", + packet->guid.g, endpointGuid.g,__FILE__, __LINE__)); + } + + SendForwardingSuccess(ID_ROUTER_2_FORWARDING_ESTABLISHED, packet->guid, endpointGuid, forwardingPort); + } + else if (result==UDPFORWARDER_NO_SOCKETS) + { + char buff[512]; + char buff2[64]; + char buff3[64]; + packet->systemAddress.ToString(true,buff2); + endpointSystemAddress.ToString(true,buff3); + if (debugInterface) + debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_NO_SOCKETS, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", + __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); + SendFailureOnCannotForward(packet->guid, endpointGuid); + } + else if (result==UDPFORWARDER_INVALID_PARAMETERS) + { + char buff[512]; + char buff2[64]; + char buff3[64]; + packet->systemAddress.ToString(true,buff2); + endpointSystemAddress.ToString(true,buff3); + if (debugInterface) + debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_INVALID_PARAMETERS, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", + __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); + SendFailureOnCannotForward(packet->guid, endpointGuid); + } + else if (result==UDPFORWARDER_BIND_FAILED) + { + char buff[512]; + char buff2[64]; + char buff3[64]; + packet->systemAddress.ToString(true,buff2); + endpointSystemAddress.ToString(true,buff3); + if (debugInterface) + debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_BIND_FAILED, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", + __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); + SendFailureOnCannotForward(packet->guid, endpointGuid); + } + else + { + if (debugInterface) + { + char buff2[32]; + char buff3[32]; + endpointSystemAddress.ToString(true,buff2); + packet->systemAddress.ToString(true,buff3); + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING.\n" + "endpointAddress=%s\nsourceAddress=%s\nforwardingPort=%i\n " + "calling SendOOBMessages at %s:%i\n", buff2,buff3,forwardingPort,_FILE_AND_LINE_)); + } + + // Store the punch request + MiniPunchRequest miniPunchRequest; + miniPunchRequest.endpointAddress=endpointSystemAddress; + miniPunchRequest.endpointGuid=endpointGuid; + miniPunchRequest.gotReplyFromEndpoint=false; + miniPunchRequest.gotReplyFromSource=false; + miniPunchRequest.sourceGuid=packet->guid; + miniPunchRequest.sourceAddress=packet->systemAddress; + miniPunchRequest.forwardingPort=forwardingPort; + miniPunchRequest.forwardingSocket=forwardingSocket; + int ping1 = rakPeerInterface->GetAveragePing(packet->guid); + int ping2 = rakPeerInterface->GetAveragePing(endpointGuid); + if (ping1>ping2) + miniPunchRequest.timeout=RakNet::GetTimeMS() + ping1*8+300; + else + miniPunchRequest.timeout=RakNet::GetTimeMS() + ping2*8+300; + miniPunchRequest.nextAction=RakNet::GetTimeMS()+100; + SendOOBMessages(&miniPunchRequest); + miniPunchesInProgressMutex.Lock(); + miniPunchesInProgress.Push(miniPunchRequest,_FILE_AND_LINE_); + miniPunchesInProgressMutex.Unlock(); + } +} +void Router2::OnMiniPunchReplyBounce(Packet *packet) +{ + // Find stored punch request + unsigned int i=0; + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE from guid=%I64d (miniPunchesInProgress.Size() = %d)", + packet->guid.g, miniPunchesInProgress.Size())); + } + + miniPunchesInProgressMutex.Lock(); + while (i < miniPunchesInProgress.Size()) + { + if (miniPunchesInProgress[i].sourceGuid==packet->guid || miniPunchesInProgress[i].endpointGuid==packet->guid) + { + if (miniPunchesInProgress[i].sourceGuid==packet->guid) + miniPunchesInProgress[i].gotReplyFromSource=true; + if (miniPunchesInProgress[i].endpointGuid==packet->guid) + miniPunchesInProgress[i].gotReplyFromEndpoint=true; + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Processing ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, gotReplyFromSource=%i gotReplyFromEndpoint=%i at %s:%i\n", miniPunchesInProgress[i].gotReplyFromSource, miniPunchesInProgress[i].gotReplyFromEndpoint, __FILE__, __LINE__)); + } + + if (miniPunchesInProgress[i].gotReplyFromEndpoint==true && + miniPunchesInProgress[i].gotReplyFromSource==true) + { + SendForwardingSuccess(ID_ROUTER_2_REROUTED, miniPunchesInProgress[i].endpointGuid, miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].forwardingPort); + SendForwardingSuccess(ID_ROUTER_2_FORWARDING_ESTABLISHED, miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].endpointGuid, miniPunchesInProgress[i].forwardingPort); + miniPunchesInProgress.RemoveAtIndexFast(i); + } + else + { + i++; + } + } + else + i++; + } + miniPunchesInProgressMutex.Unlock(); +} +void Router2::OnMiniPunchReply(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); + RakNetGUID routerGuid; + bs.Read(routerGuid); + SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, 0, rakPeerInterface->GetSystemAddressFromGuid(routerGuid)); + + if (debugInterface) + { + char buff[512]; + + char buff2[512]; + + rakPeerInterface->GetSystemAddressFromGuid(routerGuid).ToString(true,buff2); + + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE (%s) at %s:%i\n", buff2, __FILE__, __LINE__)); + } +} +void Router2::OnRerouted(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID endpointGuid; + bs.Read(endpointGuid); + unsigned short sourceToDestPort; + bs.Read(sourceToDestPort); + + // Return rerouted notice + SystemAddress intermediaryAddress=packet->systemAddress; + intermediaryAddress.SetPort(sourceToDestPort); + rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); + + unsigned int forwardingIndex; + forwardedConnectionListMutex.Lock(); + for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) + { + if (forwardedConnectionList[forwardingIndex].endpointGuid==endpointGuid) + break; + } + + if (forwardingIndexsystemAddress; + ref_fc.intermediaryAddress.SetPort(sourceToDestPort); + ref_fc.intermediaryGuid = packet->guid; + + rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"FIX: Got ID_ROUTER_2_REROUTE, returning ID_ROUTER_2_REROUTED," + " Calling RakPeer::ChangeSystemAddress(%I64d, %s) at %s:%i\n",endpointGuid.g, intermediaryAddress.ToString(true), __FILE__, __LINE__)); + } + } + else + { + ForwardedConnection fc; + fc.endpointGuid=endpointGuid; + fc.intermediaryAddress=packet->systemAddress; + fc.intermediaryAddress.SetPort(sourceToDestPort); + fc.intermediaryGuid=packet->guid; + fc.weInitiatedForwarding=false; + // add to forwarding list. This is only here to avoid reporting direct connections in Router2::ReturnFailureOnCannotForward + forwardedConnectionList.Push (fc,__FILE__, __LINE__); + forwardedConnectionListMutex.Unlock(); + + rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REROUTE, returning ID_ROUTER_2_REROUTED, Calling RakPeer::ChangeSystemAddress at %s:%i\n", __FILE__, __LINE__)); + } + } + +} +bool Router2::OnForwardingSuccess(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID endpointGuid; + bs.Read(endpointGuid); + unsigned short sourceToDestPort; + bs.Read(sourceToDestPort); + + unsigned int forwardingIndex; + forwardedConnectionListMutex.Lock(); + for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) + { + if (forwardedConnectionList[forwardingIndex].endpointGuid==endpointGuid) + break; + } + + if (forwardingIndexsystemAddress; + intermediaryAddress.SetPort(sourceToDestPort); + rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); + + //////////////////////////////////////////////////////////////////////////// + ForwardedConnection& ref_fc = forwardedConnectionList[forwardingIndex]; + ref_fc.intermediaryAddress = packet->systemAddress; + ref_fc.intermediaryAddress.SetPort(sourceToDestPort); + ref_fc.intermediaryGuid = packet->guid; + //////////////////////////////////////////////////////////////////////////// + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_FORWARDING_ESTABLISHED, returning ID_ROUTER_2_REROUTED, Calling RakPeer::ChangeSystemAddress at %s:%i\n", _FILE_AND_LINE_)); + } + + packet->data[0]=ID_ROUTER_2_REROUTED; + + forwardedConnectionListMutex.Unlock(); + return true; // Return packet to user + } + else + { + forwardedConnectionListMutex.Unlock(); + + // removeFrom connectionRequests; + ForwardedConnection fc; + connectionRequestsMutex.Lock(); + unsigned int connectionRequestIndex = GetConnectionRequestIndex(endpointGuid); + fc.returnConnectionLostOnFailure=connectionRequests[connectionRequestIndex]->returnConnectionLostOnFailure; + connectionRequests.RemoveAtIndexFast(connectionRequestIndex); + connectionRequestsMutex.Unlock(); + fc.endpointGuid=endpointGuid; + fc.intermediaryAddress=packet->systemAddress; + fc.intermediaryAddress.SetPort(sourceToDestPort); + fc.intermediaryGuid=packet->guid; + fc.weInitiatedForwarding=true; + + // add to forwarding list + forwardedConnectionListMutex.Lock(); + forwardedConnectionList.Push (fc,_FILE_AND_LINE_); + forwardedConnectionListMutex.Unlock(); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got and returning to user ID_ROUTER_2_FORWARDING_ESTABLISHED at %s:%i\n", _FILE_AND_LINE_)); + } + + } + return true; // Return packet to user +} +int Router2::GetLargestPingAmongConnectedSystems(void) const +{ + int avePing; + int largestPing=-1; + unsigned short maxPeers = rakPeerInterface->GetMaximumNumberOfPeers(); + if (maxPeers==0) + return 9999; + unsigned short index; + for (index=0; index < rakPeerInterface->GetMaximumNumberOfPeers(); index++) + { + RakNetGUID g = rakPeerInterface->GetGUIDFromIndex(index); + if (g!=UNASSIGNED_RAKNET_GUID) + { + avePing=rakPeerInterface->GetAveragePing(rakPeerInterface->GetGUIDFromIndex(index)); + if (avePing>largestPing) + largestPing=avePing; + } + } + return largestPing; +} + +unsigned int Router2::GetConnectionRequestIndex(RakNetGUID endpointGuid) +{ + unsigned int i; + for (i=0; i < connectionRequests.Size(); i++) + { + if (connectionRequests[i]->endpointGuid==endpointGuid) + return i; + } + return (unsigned int) -1; +} +unsigned int Router2::ConnnectRequest::GetGuidIndex(RakNetGUID guid) +{ + unsigned int i; + for (i=0; i < connectionRequestSystems.Size(); i++) + { + if (connectionRequestSystems[i].guid==guid) + return i; + } + return (unsigned int) -1; +} +void Router2::ReturnToUser(MessageID messageId, RakNetGUID endpointGuid, const SystemAddress &systemAddress, bool wasGeneratedLocally) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); + p->data[0]=messageId; + p->systemAddress=systemAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=endpointGuid; + p->wasGeneratedLocally=wasGeneratedLocally; + rakPeerInterface->PushBackPacket(p, true); +} +void Router2::ClearForwardedConnections(void) +{ + forwardedConnectionListMutex.Lock(); + forwardedConnectionList.Clear(false,_FILE_AND_LINE_); + forwardedConnectionListMutex.Unlock(); +} +void Router2::ClearAll(void) +{ + ClearConnectionRequests(); + ClearMinipunches(); + ClearForwardedConnections(); +} +void Router2::SetDebugInterface(Router2DebugInterface *_debugInterface) +{ + debugInterface=_debugInterface; +} +Router2DebugInterface *Router2::GetDebugInterface(void) const +{ + return debugInterface; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/Router2.h b/project/lib_projects/raknet/jni/RaknetSources/Router2.h index b4debe0..7912da5 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/Router2.h +++ b/project/lib_projects/raknet/jni/RaknetSources/Router2.h @@ -1,195 +1,195 @@ -/// \file -/// \brief Router2 plugin. Allows you to connect to a system by routing packets through another system that is connected to both you and the destination. Useful for getting around NATs. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_Router2==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#ifndef __ROUTER_2_PLUGIN_H -#define __ROUTER_2_PLUGIN_H - -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "Export.h" -#include "UDPForwarder.h" -#include "MessageIdentifiers.h" -#include "DS_List.h" -#include "SimpleMutex.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -struct Router2DebugInterface -{ - Router2DebugInterface() {} - virtual ~Router2DebugInterface() {} - virtual void ShowFailure(const char *message); - virtual void ShowDiagnostic(const char *message); -}; - -/// \defgroup ROUTER_2_GROUP Router2 -/// \brief Part of the NAT punchthrough solution, allowing you to connect to systems by routing through a shared connection. -/// \details Router2 routes datagrams between two systems that are not directly connected by using the bandwidth of a third system, to which the other two systems were connected -/// It is of benefit when a fully connected mesh topology is desired, but could not be completely established due to routers and/or firewalls -/// As the system address of a remote system will be the system address of the intermediary, it is necessary to use the RakNetGUID object to refer to systems, including with other plugins -/// \ingroup PLUGINS_GROUP - -/// \ingroup ROUTER_2_GROUP -/// \brief Class interface for the Router2 system -/// \details -class RAK_DLL_EXPORT Router2 : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(Router2) - - Router2(); - virtual ~Router2(); - - /// Sets the socket family to use, either IPV4 or IPV6 - /// \param[in] socketFamily For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - void SetSocketFamily(unsigned short _socketFamily); - - /// \brief Query all connected systems to connect through them to a third system. - /// System will return ID_ROUTER_2_FORWARDING_NO_PATH if unable to connect. - /// Else you will get ID_ROUTER_2_FORWARDING_ESTABLISHED - /// - /// On ID_ROUTER_2_FORWARDING_ESTABLISHED, EstablishRouting as follows: - /// - /// RakNet::BitStream bs(packet->data, packet->length, false); - /// bs.IgnoreBytes(sizeof(MessageID)); - /// RakNetGUID endpointGuid; - /// bs.Read(endpointGuid); - /// unsigned short sourceToDestPort; - /// bs.Read(sourceToDestPort); - /// char ipAddressString[32]; - /// packet->systemAddress.ToString(false, ipAddressString); - /// rakPeerInterface->EstablishRouting(ipAddressString, sourceToDestPort, 0,0); - /// - /// \note The SystemAddress for a connection should not be used - always use RakNetGuid as the address can change at any time. - /// When the address changes, you will get ID_ROUTER_2_REROUTED - void EstablishRouting(RakNetGUID endpointGuid); - - /// Set the maximum number of bidirectional connections this system will support - /// Defaults to 0 - void SetMaximumForwardingRequests(int max); - - /// For testing and debugging - void SetDebugInterface(Router2DebugInterface *_debugInterface); - - /// Get the pointer passed to SetDebugInterface() - Router2DebugInterface *GetDebugInterface(void) const; - - // -------------------------------------------------------------------------------------------- - // Packet handling functions - // -------------------------------------------------------------------------------------------- - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void Update(void); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason); - virtual void OnRakPeerShutdown(void); - - - enum Router2RequestStates - { - R2RS_REQUEST_STATE_QUERY_FORWARDING, - REQUEST_STATE_REQUEST_FORWARDING, - }; - - struct ConnectionRequestSystem - { - RakNetGUID guid; - int pingToEndpoint; - unsigned short usedForwardingEntries; - }; - - struct ConnnectRequest - { - ConnnectRequest(); - ~ConnnectRequest(); - - DataStructures::List connectionRequestSystems; - SimpleMutex connectionRequestSystemsMutex; - Router2RequestStates requestState; - RakNet::TimeMS pingTimeout; - RakNetGUID endpointGuid; - RakNetGUID lastRequestedForwardingSystem; - bool returnConnectionLostOnFailure; - unsigned int GetGuidIndex(RakNetGUID guid); - }; - - unsigned int GetConnectionRequestIndex(RakNetGUID endpointGuid); - - struct MiniPunchRequest - { - RakNetGUID endpointGuid; - SystemAddress endpointAddress; - bool gotReplyFromEndpoint; - RakNetGUID sourceGuid; - SystemAddress sourceAddress; - bool gotReplyFromSource; - RakNet::TimeMS timeout; - RakNet::TimeMS nextAction; - unsigned short forwardingPort; - SOCKET forwardingSocket; - }; - - struct ForwardedConnection - { - RakNetGUID endpointGuid; - RakNetGUID intermediaryGuid; - SystemAddress intermediaryAddress; - bool returnConnectionLostOnFailure; - bool weInitiatedForwarding; - }; - -protected: - - bool UpdateForwarding(ConnnectRequest* connectionRequest); - void RemoveConnectionRequest(unsigned int connectionRequestIndex); - void RequestForwarding(ConnnectRequest* connectionRequest); - void OnQueryForwarding(Packet *packet); - void OnQueryForwardingReply(Packet *packet); - void OnRequestForwarding(Packet *packet); - void OnRerouted(Packet *packet); - void OnMiniPunchReply(Packet *packet); - void OnMiniPunchReplyBounce(Packet *packet); - bool OnForwardingSuccess(Packet *packet); - int GetLargestPingAmongConnectedSystems(void) const; - void ReturnToUser(MessageID messageId, RakNetGUID endpointGuid, const SystemAddress &systemAddress, bool wasGeneratedLocally); - bool ConnectInternal(RakNetGUID endpointGuid, bool returnConnectionLostOnFailure); - - UDPForwarder *udpForwarder; - int maximumForwardingRequests; - SimpleMutex connectionRequestsMutex, miniPunchesInProgressMutex, forwardedConnectionListMutex; - DataStructures::List connectionRequests; - DataStructures::List miniPunchesInProgress; - // Forwarding we have initiated - DataStructures::List forwardedConnectionList; - - void ClearConnectionRequests(void); - void ClearMinipunches(void); - void ClearForwardedConnections(void); - void ClearAll(void); - int ReturnFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid); - void SendFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid); - void SendForwardingSuccess(MessageID messageId, RakNetGUID sourceGuid, RakNetGUID endpointGuid, unsigned short sourceToDstPort); - void SendOOBFromRakNetPort(OutOfBandIdentifiers oob, BitStream *extraData, SystemAddress sa); - void SendOOBFromSpecifiedSocket(OutOfBandIdentifiers oob, SystemAddress sa, SOCKET socket); - void SendOOBMessages(MiniPunchRequest *mpr); - - Router2DebugInterface *debugInterface; - unsigned short socketFamily; -}; - -} - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Router2 plugin. Allows you to connect to a system by routing packets through another system that is connected to both you and the destination. Useful for getting around NATs. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_Router2==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#ifndef __ROUTER_2_PLUGIN_H +#define __ROUTER_2_PLUGIN_H + +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "Export.h" +#include "UDPForwarder.h" +#include "MessageIdentifiers.h" +#include "DS_List.h" +#include "SimpleMutex.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +struct Router2DebugInterface +{ + Router2DebugInterface() {} + virtual ~Router2DebugInterface() {} + virtual void ShowFailure(const char *message); + virtual void ShowDiagnostic(const char *message); +}; + +/// \defgroup ROUTER_2_GROUP Router2 +/// \brief Part of the NAT punchthrough solution, allowing you to connect to systems by routing through a shared connection. +/// \details Router2 routes datagrams between two systems that are not directly connected by using the bandwidth of a third system, to which the other two systems were connected +/// It is of benefit when a fully connected mesh topology is desired, but could not be completely established due to routers and/or firewalls +/// As the system address of a remote system will be the system address of the intermediary, it is necessary to use the RakNetGUID object to refer to systems, including with other plugins +/// \ingroup PLUGINS_GROUP + +/// \ingroup ROUTER_2_GROUP +/// \brief Class interface for the Router2 system +/// \details +class RAK_DLL_EXPORT Router2 : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(Router2) + + Router2(); + virtual ~Router2(); + + /// Sets the socket family to use, either IPV4 or IPV6 + /// \param[in] socketFamily For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + void SetSocketFamily(unsigned short _socketFamily); + + /// \brief Query all connected systems to connect through them to a third system. + /// System will return ID_ROUTER_2_FORWARDING_NO_PATH if unable to connect. + /// Else you will get ID_ROUTER_2_FORWARDING_ESTABLISHED + /// + /// On ID_ROUTER_2_FORWARDING_ESTABLISHED, EstablishRouting as follows: + /// + /// RakNet::BitStream bs(packet->data, packet->length, false); + /// bs.IgnoreBytes(sizeof(MessageID)); + /// RakNetGUID endpointGuid; + /// bs.Read(endpointGuid); + /// unsigned short sourceToDestPort; + /// bs.Read(sourceToDestPort); + /// char ipAddressString[32]; + /// packet->systemAddress.ToString(false, ipAddressString); + /// rakPeerInterface->EstablishRouting(ipAddressString, sourceToDestPort, 0,0); + /// + /// \note The SystemAddress for a connection should not be used - always use RakNetGuid as the address can change at any time. + /// When the address changes, you will get ID_ROUTER_2_REROUTED + void EstablishRouting(RakNetGUID endpointGuid); + + /// Set the maximum number of bidirectional connections this system will support + /// Defaults to 0 + void SetMaximumForwardingRequests(int max); + + /// For testing and debugging + void SetDebugInterface(Router2DebugInterface *_debugInterface); + + /// Get the pointer passed to SetDebugInterface() + Router2DebugInterface *GetDebugInterface(void) const; + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void Update(void); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason); + virtual void OnRakPeerShutdown(void); + + + enum Router2RequestStates + { + R2RS_REQUEST_STATE_QUERY_FORWARDING, + REQUEST_STATE_REQUEST_FORWARDING, + }; + + struct ConnectionRequestSystem + { + RakNetGUID guid; + int pingToEndpoint; + unsigned short usedForwardingEntries; + }; + + struct ConnnectRequest + { + ConnnectRequest(); + ~ConnnectRequest(); + + DataStructures::List connectionRequestSystems; + SimpleMutex connectionRequestSystemsMutex; + Router2RequestStates requestState; + RakNet::TimeMS pingTimeout; + RakNetGUID endpointGuid; + RakNetGUID lastRequestedForwardingSystem; + bool returnConnectionLostOnFailure; + unsigned int GetGuidIndex(RakNetGUID guid); + }; + + unsigned int GetConnectionRequestIndex(RakNetGUID endpointGuid); + + struct MiniPunchRequest + { + RakNetGUID endpointGuid; + SystemAddress endpointAddress; + bool gotReplyFromEndpoint; + RakNetGUID sourceGuid; + SystemAddress sourceAddress; + bool gotReplyFromSource; + RakNet::TimeMS timeout; + RakNet::TimeMS nextAction; + unsigned short forwardingPort; + SOCKET forwardingSocket; + }; + + struct ForwardedConnection + { + RakNetGUID endpointGuid; + RakNetGUID intermediaryGuid; + SystemAddress intermediaryAddress; + bool returnConnectionLostOnFailure; + bool weInitiatedForwarding; + }; + +protected: + + bool UpdateForwarding(ConnnectRequest* connectionRequest); + void RemoveConnectionRequest(unsigned int connectionRequestIndex); + void RequestForwarding(ConnnectRequest* connectionRequest); + void OnQueryForwarding(Packet *packet); + void OnQueryForwardingReply(Packet *packet); + void OnRequestForwarding(Packet *packet); + void OnRerouted(Packet *packet); + void OnMiniPunchReply(Packet *packet); + void OnMiniPunchReplyBounce(Packet *packet); + bool OnForwardingSuccess(Packet *packet); + int GetLargestPingAmongConnectedSystems(void) const; + void ReturnToUser(MessageID messageId, RakNetGUID endpointGuid, const SystemAddress &systemAddress, bool wasGeneratedLocally); + bool ConnectInternal(RakNetGUID endpointGuid, bool returnConnectionLostOnFailure); + + UDPForwarder *udpForwarder; + int maximumForwardingRequests; + SimpleMutex connectionRequestsMutex, miniPunchesInProgressMutex, forwardedConnectionListMutex; + DataStructures::List connectionRequests; + DataStructures::List miniPunchesInProgress; + // Forwarding we have initiated + DataStructures::List forwardedConnectionList; + + void ClearConnectionRequests(void); + void ClearMinipunches(void); + void ClearForwardedConnections(void); + void ClearAll(void); + int ReturnFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid); + void SendFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid); + void SendForwardingSuccess(MessageID messageId, RakNetGUID sourceGuid, RakNetGUID endpointGuid, unsigned short sourceToDstPort); + void SendOOBFromRakNetPort(OutOfBandIdentifiers oob, BitStream *extraData, SystemAddress sa); + void SendOOBFromSpecifiedSocket(OutOfBandIdentifiers oob, SystemAddress sa, SOCKET socket); + void SendOOBMessages(MiniPunchRequest *mpr); + + Router2DebugInterface *debugInterface; + unsigned short socketFamily; +}; + +} + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/SHA1.cpp b/project/lib_projects/raknet/jni/RaknetSources/SHA1.cpp index b53b72c..fbfe7d0 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SHA1.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/SHA1.cpp @@ -1,326 +1,326 @@ -/** -* @brief SHA-1 Hash key computation -* -* 100% free public domain implementation of the SHA-1 -* algorithm by Dominik Reichl -* -* -* === Test Vectors (from FIPS PUB 180-1) === -* -* "abc" -* A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -* -* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" -* 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -* -* A million repetitions of "a" -* 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - - -#include "SHA1.h" -#include - - -CSHA1::CSHA1() -{ - Reset(); -} - -CSHA1::~CSHA1() -{ - Reset(); -} - - -void CSHA1::Reset() -{ - // SHA1 initialization constants - m_state[ 0 ] = 0x67452301; - m_state[ 1 ] = 0xEFCDAB89; - m_state[ 2 ] = 0x98BADCFE; - m_state[ 3 ] = 0x10325476; - m_state[ 4 ] = 0xC3D2E1F0; - - m_count[ 0 ] = 0; - m_count[ 1 ] = 0; -} - -void CSHA1::Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ) -{ - unsigned int a = 0, b = 0, c = 0, d = 0, e = 0; - - SHA1_WORKSPACE_BLOCK* block; - // static unsigned char workspace[64]; - block = ( SHA1_WORKSPACE_BLOCK * ) workspace; - memcpy( block, buffer, 64 ); - - // Copy state[] to working vars - a = state[ 0 ]; - b = state[ 1 ]; - c = state[ 2 ]; - d = state[ 3 ]; - e = state[ 4 ]; - - // 4 rounds of 20 operations each. Loop unrolled. - R0( a, b, c, d, e, 0 ); - R0( e, a, b, c, d, 1 ); - R0( d, e, a, b, c, 2 ); - R0( c, d, e, a, b, 3 ); - R0( b, c, d, e, a, 4 ); - R0( a, b, c, d, e, 5 ); - R0( e, a, b, c, d, 6 ); - R0( d, e, a, b, c, 7 ); - R0( c, d, e, a, b, 8 ); - R0( b, c, d, e, a, 9 ); - R0( a, b, c, d, e, 10 ); - R0( e, a, b, c, d, 11 ); - R0( d, e, a, b, c, 12 ); - R0( c, d, e, a, b, 13 ); - R0( b, c, d, e, a, 14 ); - R0( a, b, c, d, e, 15 ); - R1( e, a, b, c, d, 16 ); - R1( d, e, a, b, c, 17 ); - R1( c, d, e, a, b, 18 ); - R1( b, c, d, e, a, 19 ); - R2( a, b, c, d, e, 20 ); - R2( e, a, b, c, d, 21 ); - R2( d, e, a, b, c, 22 ); - R2( c, d, e, a, b, 23 ); - R2( b, c, d, e, a, 24 ); - R2( a, b, c, d, e, 25 ); - R2( e, a, b, c, d, 26 ); - R2( d, e, a, b, c, 27 ); - R2( c, d, e, a, b, 28 ); - R2( b, c, d, e, a, 29 ); - R2( a, b, c, d, e, 30 ); - R2( e, a, b, c, d, 31 ); - R2( d, e, a, b, c, 32 ); - R2( c, d, e, a, b, 33 ); - R2( b, c, d, e, a, 34 ); - R2( a, b, c, d, e, 35 ); - R2( e, a, b, c, d, 36 ); - R2( d, e, a, b, c, 37 ); - R2( c, d, e, a, b, 38 ); - R2( b, c, d, e, a, 39 ); - R3( a, b, c, d, e, 40 ); - R3( e, a, b, c, d, 41 ); - R3( d, e, a, b, c, 42 ); - R3( c, d, e, a, b, 43 ); - R3( b, c, d, e, a, 44 ); - R3( a, b, c, d, e, 45 ); - R3( e, a, b, c, d, 46 ); - R3( d, e, a, b, c, 47 ); - R3( c, d, e, a, b, 48 ); - R3( b, c, d, e, a, 49 ); - R3( a, b, c, d, e, 50 ); - R3( e, a, b, c, d, 51 ); - R3( d, e, a, b, c, 52 ); - R3( c, d, e, a, b, 53 ); - R3( b, c, d, e, a, 54 ); - R3( a, b, c, d, e, 55 ); - R3( e, a, b, c, d, 56 ); - R3( d, e, a, b, c, 57 ); - R3( c, d, e, a, b, 58 ); - R3( b, c, d, e, a, 59 ); - R4( a, b, c, d, e, 60 ); - R4( e, a, b, c, d, 61 ); - R4( d, e, a, b, c, 62 ); - R4( c, d, e, a, b, 63 ); - R4( b, c, d, e, a, 64 ); - R4( a, b, c, d, e, 65 ); - R4( e, a, b, c, d, 66 ); - R4( d, e, a, b, c, 67 ); - R4( c, d, e, a, b, 68 ); - R4( b, c, d, e, a, 69 ); - R4( a, b, c, d, e, 70 ); - R4( e, a, b, c, d, 71 ); - R4( d, e, a, b, c, 72 ); - R4( c, d, e, a, b, 73 ); - R4( b, c, d, e, a, 74 ); - R4( a, b, c, d, e, 75 ); - R4( e, a, b, c, d, 76 ); - R4( d, e, a, b, c, 77 ); - R4( c, d, e, a, b, 78 ); - R4( b, c, d, e, a, 79 ); - - // Add the working vars back into state[] - state[ 0 ] += a; - state[ 1 ] += b; - state[ 2 ] += c; - state[ 3 ] += d; - state[ 4 ] += e; - - // Wipe variables - a = 0; - b = 0; - c = 0; - d = 0; - e = 0; -} - -// Use this function to hash in binary data and strings -void CSHA1::Update( unsigned char* data, unsigned int len ) -{ - unsigned int i = 0, j = 0; - - j = ( m_count[ 0 ] >> 3 ) & 63; - - if ( ( m_count[ 0 ] += len << 3 ) < ( len << 3 ) ) - m_count[ 1 ] ++; - - m_count[ 1 ] += ( len >> 29 ); - - if ( ( j + len ) > 63 ) - { - memcpy( &m_buffer[ j ], data, ( i = 64 - j ) ); - Transform( m_state, m_buffer ); - - for ( ; i + 63 < len; i += 64 ) - { - Transform( m_state, &data[ i ] ); - } - - j = 0; - } - - else - i = 0; - - memcpy( &m_buffer[ j ], &data[ i ], len - i ); -} - -// Hash in file contents -bool CSHA1::HashFile( char *szFileName ) -{ - unsigned long ulFileSize = 0, ulRest = 0, ulBlocks = 0; - unsigned long i = 0; - unsigned char uData[ MAX_FILE_READ_BUFFER ]; - FILE *fIn = NULL; - - if ( ( fIn = fopen( szFileName, "rb" ) ) == NULL ) - return ( false ); - - fseek( fIn, 0, SEEK_END ); - - ulFileSize = ftell( fIn ); - - fseek( fIn, 0, SEEK_SET ); - - // This is faster - div_t temp; - - temp = div( ulFileSize, MAX_FILE_READ_BUFFER ); - - ulRest = temp.rem; - - ulBlocks = temp.quot; - - // ulRest = ulFileSize % MAX_FILE_READ_BUFFER; - // ulBlocks = ulFileSize / MAX_FILE_READ_BUFFER; - - for ( i = 0; i < ulBlocks; i++ ) - { - fread( uData, 1, MAX_FILE_READ_BUFFER, fIn ); - Update( uData, MAX_FILE_READ_BUFFER ); - } - - if ( ulRest != 0 ) - { - fread( uData, 1, ulRest, fIn ); - Update( uData, ulRest ); - } - - fclose( fIn ); - fIn = NULL; - - return ( true ); -} - -void CSHA1::Final() -{ - unsigned int i = 0; - unsigned char finalcount[ 8 ] = - { - 0, 0, 0, 0, 0, 0, 0, 0 - }; - - for ( i = 0; i < 8; i++ ) - finalcount[ i ] = (unsigned char) ( ( m_count[ ( i >= 4 ? 0 : 1 ) ] - >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); // Endian independent - - Update( ( unsigned char * ) "\200", 1 ); - - while ( ( m_count[ 0 ] & 504 ) != 448 ) - Update( ( unsigned char * ) "\0", 1 ); - - Update( finalcount, 8 ); // Cause a SHA1Transform() - - for ( i = 0; i < 20; i++ ) - { - m_digest[ i ] = (unsigned char) ( ( m_state[ i >> 2 ] >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); - } - - // Wipe variables for security reasons - i = 0; - -// j = 0; - - memset( m_buffer, 0, 64 ); - - memset( m_state, 0, 20 ); - - memset( m_count, 0, 8 ); - - memset( finalcount, 0, 8 ); - - Transform( m_state, m_buffer ); -} - -// Get the final hash as a pre-formatted string -void CSHA1::ReportHash( char *szReport, unsigned char uReportType ) -{ - unsigned char i = 0; - char szTemp[ 4 ]; - - if ( uReportType == REPORT_HEX ) - { - sprintf( szTemp, "%02X", m_digest[ 0 ] ); - strcat( szReport, szTemp ); - - for ( i = 1; i < 20; i++ ) - { - sprintf( szTemp, " %02X", m_digest[ i ] ); - strcat( szReport, szTemp ); - } - } - - else - if ( uReportType == REPORT_DIGIT ) - { - sprintf( szTemp, "%u", m_digest[ 0 ] ); - strcat( szReport, szTemp ); - - for ( i = 1; i < 20; i++ ) - { - sprintf( szTemp, " %u", m_digest[ i ] ); - strcat( szReport, szTemp ); - } - } - - else - strcpy( szReport, "Error: Unknown report type!" ); -} - -// Get the raw message digest -void CSHA1::GetHash( unsigned char *uDest ) -{ - memcpy( uDest, m_digest, 20 ); -} - -// Get the raw message digest -// Added by Kevin to be quicker -unsigned char * CSHA1::GetHash( void ) const -{ - return ( unsigned char * ) m_digest; -} +/** +* @brief SHA-1 Hash key computation +* +* 100% free public domain implementation of the SHA-1 +* algorithm by Dominik Reichl +* +* +* === Test Vectors (from FIPS PUB 180-1) === +* +* "abc" +* A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +* +* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +* 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +* +* A million repetitions of "a" +* 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + + +#include "SHA1.h" +#include + + +CSHA1::CSHA1() +{ + Reset(); +} + +CSHA1::~CSHA1() +{ + Reset(); +} + + +void CSHA1::Reset() +{ + // SHA1 initialization constants + m_state[ 0 ] = 0x67452301; + m_state[ 1 ] = 0xEFCDAB89; + m_state[ 2 ] = 0x98BADCFE; + m_state[ 3 ] = 0x10325476; + m_state[ 4 ] = 0xC3D2E1F0; + + m_count[ 0 ] = 0; + m_count[ 1 ] = 0; +} + +void CSHA1::Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ) +{ + unsigned int a = 0, b = 0, c = 0, d = 0, e = 0; + + SHA1_WORKSPACE_BLOCK* block; + // static unsigned char workspace[64]; + block = ( SHA1_WORKSPACE_BLOCK * ) workspace; + memcpy( block, buffer, 64 ); + + // Copy state[] to working vars + a = state[ 0 ]; + b = state[ 1 ]; + c = state[ 2 ]; + d = state[ 3 ]; + e = state[ 4 ]; + + // 4 rounds of 20 operations each. Loop unrolled. + R0( a, b, c, d, e, 0 ); + R0( e, a, b, c, d, 1 ); + R0( d, e, a, b, c, 2 ); + R0( c, d, e, a, b, 3 ); + R0( b, c, d, e, a, 4 ); + R0( a, b, c, d, e, 5 ); + R0( e, a, b, c, d, 6 ); + R0( d, e, a, b, c, 7 ); + R0( c, d, e, a, b, 8 ); + R0( b, c, d, e, a, 9 ); + R0( a, b, c, d, e, 10 ); + R0( e, a, b, c, d, 11 ); + R0( d, e, a, b, c, 12 ); + R0( c, d, e, a, b, 13 ); + R0( b, c, d, e, a, 14 ); + R0( a, b, c, d, e, 15 ); + R1( e, a, b, c, d, 16 ); + R1( d, e, a, b, c, 17 ); + R1( c, d, e, a, b, 18 ); + R1( b, c, d, e, a, 19 ); + R2( a, b, c, d, e, 20 ); + R2( e, a, b, c, d, 21 ); + R2( d, e, a, b, c, 22 ); + R2( c, d, e, a, b, 23 ); + R2( b, c, d, e, a, 24 ); + R2( a, b, c, d, e, 25 ); + R2( e, a, b, c, d, 26 ); + R2( d, e, a, b, c, 27 ); + R2( c, d, e, a, b, 28 ); + R2( b, c, d, e, a, 29 ); + R2( a, b, c, d, e, 30 ); + R2( e, a, b, c, d, 31 ); + R2( d, e, a, b, c, 32 ); + R2( c, d, e, a, b, 33 ); + R2( b, c, d, e, a, 34 ); + R2( a, b, c, d, e, 35 ); + R2( e, a, b, c, d, 36 ); + R2( d, e, a, b, c, 37 ); + R2( c, d, e, a, b, 38 ); + R2( b, c, d, e, a, 39 ); + R3( a, b, c, d, e, 40 ); + R3( e, a, b, c, d, 41 ); + R3( d, e, a, b, c, 42 ); + R3( c, d, e, a, b, 43 ); + R3( b, c, d, e, a, 44 ); + R3( a, b, c, d, e, 45 ); + R3( e, a, b, c, d, 46 ); + R3( d, e, a, b, c, 47 ); + R3( c, d, e, a, b, 48 ); + R3( b, c, d, e, a, 49 ); + R3( a, b, c, d, e, 50 ); + R3( e, a, b, c, d, 51 ); + R3( d, e, a, b, c, 52 ); + R3( c, d, e, a, b, 53 ); + R3( b, c, d, e, a, 54 ); + R3( a, b, c, d, e, 55 ); + R3( e, a, b, c, d, 56 ); + R3( d, e, a, b, c, 57 ); + R3( c, d, e, a, b, 58 ); + R3( b, c, d, e, a, 59 ); + R4( a, b, c, d, e, 60 ); + R4( e, a, b, c, d, 61 ); + R4( d, e, a, b, c, 62 ); + R4( c, d, e, a, b, 63 ); + R4( b, c, d, e, a, 64 ); + R4( a, b, c, d, e, 65 ); + R4( e, a, b, c, d, 66 ); + R4( d, e, a, b, c, 67 ); + R4( c, d, e, a, b, 68 ); + R4( b, c, d, e, a, 69 ); + R4( a, b, c, d, e, 70 ); + R4( e, a, b, c, d, 71 ); + R4( d, e, a, b, c, 72 ); + R4( c, d, e, a, b, 73 ); + R4( b, c, d, e, a, 74 ); + R4( a, b, c, d, e, 75 ); + R4( e, a, b, c, d, 76 ); + R4( d, e, a, b, c, 77 ); + R4( c, d, e, a, b, 78 ); + R4( b, c, d, e, a, 79 ); + + // Add the working vars back into state[] + state[ 0 ] += a; + state[ 1 ] += b; + state[ 2 ] += c; + state[ 3 ] += d; + state[ 4 ] += e; + + // Wipe variables + a = 0; + b = 0; + c = 0; + d = 0; + e = 0; +} + +// Use this function to hash in binary data and strings +void CSHA1::Update( unsigned char* data, unsigned int len ) +{ + unsigned int i = 0, j = 0; + + j = ( m_count[ 0 ] >> 3 ) & 63; + + if ( ( m_count[ 0 ] += len << 3 ) < ( len << 3 ) ) + m_count[ 1 ] ++; + + m_count[ 1 ] += ( len >> 29 ); + + if ( ( j + len ) > 63 ) + { + memcpy( &m_buffer[ j ], data, ( i = 64 - j ) ); + Transform( m_state, m_buffer ); + + for ( ; i + 63 < len; i += 64 ) + { + Transform( m_state, &data[ i ] ); + } + + j = 0; + } + + else + i = 0; + + memcpy( &m_buffer[ j ], &data[ i ], len - i ); +} + +// Hash in file contents +bool CSHA1::HashFile( char *szFileName ) +{ + unsigned long ulFileSize = 0, ulRest = 0, ulBlocks = 0; + unsigned long i = 0; + unsigned char uData[ MAX_FILE_READ_BUFFER ]; + FILE *fIn = NULL; + + if ( ( fIn = fopen( szFileName, "rb" ) ) == NULL ) + return ( false ); + + fseek( fIn, 0, SEEK_END ); + + ulFileSize = ftell( fIn ); + + fseek( fIn, 0, SEEK_SET ); + + // This is faster + div_t temp; + + temp = div( ulFileSize, MAX_FILE_READ_BUFFER ); + + ulRest = temp.rem; + + ulBlocks = temp.quot; + + // ulRest = ulFileSize % MAX_FILE_READ_BUFFER; + // ulBlocks = ulFileSize / MAX_FILE_READ_BUFFER; + + for ( i = 0; i < ulBlocks; i++ ) + { + fread( uData, 1, MAX_FILE_READ_BUFFER, fIn ); + Update( uData, MAX_FILE_READ_BUFFER ); + } + + if ( ulRest != 0 ) + { + fread( uData, 1, ulRest, fIn ); + Update( uData, ulRest ); + } + + fclose( fIn ); + fIn = NULL; + + return ( true ); +} + +void CSHA1::Final() +{ + unsigned int i = 0; + unsigned char finalcount[ 8 ] = + { + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + for ( i = 0; i < 8; i++ ) + finalcount[ i ] = (unsigned char) ( ( m_count[ ( i >= 4 ? 0 : 1 ) ] + >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); // Endian independent + + Update( ( unsigned char * ) "\200", 1 ); + + while ( ( m_count[ 0 ] & 504 ) != 448 ) + Update( ( unsigned char * ) "\0", 1 ); + + Update( finalcount, 8 ); // Cause a SHA1Transform() + + for ( i = 0; i < 20; i++ ) + { + m_digest[ i ] = (unsigned char) ( ( m_state[ i >> 2 ] >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); + } + + // Wipe variables for security reasons + i = 0; + +// j = 0; + + memset( m_buffer, 0, 64 ); + + memset( m_state, 0, 20 ); + + memset( m_count, 0, 8 ); + + memset( finalcount, 0, 8 ); + + Transform( m_state, m_buffer ); +} + +// Get the final hash as a pre-formatted string +void CSHA1::ReportHash( char *szReport, unsigned char uReportType ) +{ + unsigned char i = 0; + char szTemp[ 4 ]; + + if ( uReportType == REPORT_HEX ) + { + sprintf( szTemp, "%02X", m_digest[ 0 ] ); + strcat( szReport, szTemp ); + + for ( i = 1; i < 20; i++ ) + { + sprintf( szTemp, " %02X", m_digest[ i ] ); + strcat( szReport, szTemp ); + } + } + + else + if ( uReportType == REPORT_DIGIT ) + { + sprintf( szTemp, "%u", m_digest[ 0 ] ); + strcat( szReport, szTemp ); + + for ( i = 1; i < 20; i++ ) + { + sprintf( szTemp, " %u", m_digest[ i ] ); + strcat( szReport, szTemp ); + } + } + + else + strcpy( szReport, "Error: Unknown report type!" ); +} + +// Get the raw message digest +void CSHA1::GetHash( unsigned char *uDest ) +{ + memcpy( uDest, m_digest, 20 ); +} + +// Get the raw message digest +// Added by Kevin to be quicker +unsigned char * CSHA1::GetHash( void ) const +{ + return ( unsigned char * ) m_digest; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/SHA1.h b/project/lib_projects/raknet/jni/RaknetSources/SHA1.h index 212f490..bac7e70 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SHA1.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SHA1.h @@ -1,87 +1,87 @@ -/// \brief \b [Internal] SHA-1 computation class -/// -/// 100% free public domain implementation of the SHA-1 -/// algorithm by Dominik Reichl -/// -/// -/// === Test Vectors (from FIPS PUB 180-1) === -/// -/// "abc" -/// A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -/// -/// "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" -/// 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -/// -/// A million repetitions of "a" -/// 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F - -#ifndef ___SHA1_H___ -#define ___SHA1_H___ - -#include "RakMemoryOverride.h" -#include // Needed for file access - -#include // Needed for memset and memcpy - -#include // Needed for strcat and strcpy -#include "Export.h" - -#define MAX_FILE_READ_BUFFER 8000 - -#define SHA1_LENGTH 20 - -class RAK_DLL_EXPORT CSHA1 -{ - -public: - // Rotate x bits to the left - #define ROL32(value, bits) (((value)<<(bits))|((value)>>(32-(bits)))) - -#ifdef LITTLE_ENDIAN -#define SHABLK0(i) (block->l[i] = (ROL32(block->l[i],24) & 0xFF00FF00) \ - | (ROL32(block->l[i],8) & 0x00FF00FF)) -#else -#define SHABLK0(i) (block->l[i]) -#endif - -#define SHABLK(i) (block->l[i&15] = ROL32(block->l[(i+13)&15] ^ block->l[(i+8)&15] \ - ^ block->l[(i+2)&15] ^ block->l[i&15],1)) - - // SHA-1 rounds -#define R0(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } -#define R1(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } -#define R2(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5); w=ROL32(w,30); } -#define R3(v,w,x,y,z,i) { z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5); w=ROL32(w,30); } -#define R4(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5); w=ROL32(w,30); } - - typedef union { - unsigned char c[ 64 ]; - unsigned int l[ 16 ]; - } SHA1_WORKSPACE_BLOCK; - /* Two different formats for ReportHash(...) - */ - enum { REPORT_HEX = 0, - REPORT_DIGIT = 1}; - - CSHA1(); - virtual ~CSHA1(); - - unsigned int m_state[ 5 ]; - unsigned int m_count[ 2 ]; - unsigned char m_buffer[ 64 ]; - unsigned char m_digest[ 20 ]; - void Reset(); - void Update( unsigned char* data, unsigned int len ); - bool HashFile( char *szFileName ); - void Final(); - void ReportHash( char *szReport, unsigned char uReportType = REPORT_HEX ); - void GetHash( unsigned char *uDest ); - unsigned char * GetHash( void ) const; - -private: - void Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ); - unsigned char workspace[ 64 ]; -}; - -#endif // ___SHA1_H___ - +/// \brief \b [Internal] SHA-1 computation class +/// +/// 100% free public domain implementation of the SHA-1 +/// algorithm by Dominik Reichl +/// +/// +/// === Test Vectors (from FIPS PUB 180-1) === +/// +/// "abc" +/// A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +/// +/// "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +/// 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +/// +/// A million repetitions of "a" +/// 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + +#ifndef ___SHA1_H___ +#define ___SHA1_H___ + +#include "RakMemoryOverride.h" +#include // Needed for file access + +#include // Needed for memset and memcpy + +#include // Needed for strcat and strcpy +#include "Export.h" + +#define MAX_FILE_READ_BUFFER 8000 + +#define SHA1_LENGTH 20 + +class RAK_DLL_EXPORT CSHA1 +{ + +public: + // Rotate x bits to the left + #define ROL32(value, bits) (((value)<<(bits))|((value)>>(32-(bits)))) + +#ifdef LITTLE_ENDIAN +#define SHABLK0(i) (block->l[i] = (ROL32(block->l[i],24) & 0xFF00FF00) \ + | (ROL32(block->l[i],8) & 0x00FF00FF)) +#else +#define SHABLK0(i) (block->l[i]) +#endif + +#define SHABLK(i) (block->l[i&15] = ROL32(block->l[(i+13)&15] ^ block->l[(i+8)&15] \ + ^ block->l[(i+2)&15] ^ block->l[i&15],1)) + + // SHA-1 rounds +#define R0(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } +#define R1(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } +#define R2(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5); w=ROL32(w,30); } +#define R3(v,w,x,y,z,i) { z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5); w=ROL32(w,30); } +#define R4(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5); w=ROL32(w,30); } + + typedef union { + unsigned char c[ 64 ]; + unsigned int l[ 16 ]; + } SHA1_WORKSPACE_BLOCK; + /* Two different formats for ReportHash(...) + */ + enum { REPORT_HEX = 0, + REPORT_DIGIT = 1}; + + CSHA1(); + virtual ~CSHA1(); + + unsigned int m_state[ 5 ]; + unsigned int m_count[ 2 ]; + unsigned char m_buffer[ 64 ]; + unsigned char m_digest[ 20 ]; + void Reset(); + void Update( unsigned char* data, unsigned int len ); + bool HashFile( char *szFileName ); + void Final(); + void ReportHash( char *szReport, unsigned char uReportType = REPORT_HEX ); + void GetHash( unsigned char *uDest ); + unsigned char * GetHash( void ) const; + +private: + void Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ); + unsigned char workspace[ 64 ]; +}; + +#endif // ___SHA1_H___ + diff --git a/project/lib_projects/raknet/jni/RaknetSources/SecureHandshake.cpp b/project/lib_projects/raknet/jni/RaknetSources/SecureHandshake.cpp index e3b3c71..2c44168 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SecureHandshake.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/SecureHandshake.cpp @@ -1,54 +1,54 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" - -#if LIBCAT_SECURITY==1 - -// If building a RakNet DLL, be sure to tweak the CAT_EXPORT macro meaning -#if !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) -# define CAT_BUILD_DLL -#else -# define CAT_NEUTER_EXPORT -#endif - -#include "cat/src/port/EndianNeutral.cpp" -#include "cat/src/port/AlignedAlloc.cpp" -#include "cat/src/time/Clock.cpp" -#include "cat/src/threads/Mutex.cpp" -#include "cat/src/threads/Thread.cpp" -#include "cat/src/threads/WaitableFlag.cpp" -#include "cat/src/hash/MurmurHash2.cpp" -#include "cat/src/lang/Strings.cpp" - -#include "cat/src/math/BigRTL.cpp" -#include "cat/src/math/BigPseudoMersenne.cpp" -#include "cat/src/math/BigTwistedEdwards.cpp" - -#include "cat/src/crypt/SecureCompare.cpp" -#include "cat/src/crypt/cookie/CookieJar.cpp" -#include "cat/src/crypt/hash/HMAC_MD5.cpp" -#include "cat/src/crypt/privatekey/ChaCha.cpp" -#include "cat/src/crypt/hash/Skein.cpp" -#include "cat/src/crypt/hash/Skein256.cpp" -#include "cat/src/crypt/hash/Skein512.cpp" -#include "cat/src/crypt/pass/Passwords.cpp" - -#include "cat/src/crypt/rand/EntropyWindows.cpp" -#include "cat/src/crypt/rand/EntropyLinux.cpp" -#include "cat/src/crypt/rand/EntropyWindowsCE.cpp" -#include "cat/src/crypt/rand/EntropyGeneric.cpp" -#include "cat/src/crypt/rand/Fortuna.cpp" - -#include "cat/src/crypt/tunnel/KeyAgreement.cpp" -#include "cat/src/crypt/tunnel/AuthenticatedEncryption.cpp" -#include "cat/src/crypt/tunnel/KeyAgreementInitiator.cpp" -#include "cat/src/crypt/tunnel/KeyAgreementResponder.cpp" -#include "cat/src/crypt/tunnel/KeyMaker.cpp" - -#include "cat/src/crypt/tunnel/EasyHandshake.cpp" - -#endif // LIBCAT_SECURITY +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" + +#if LIBCAT_SECURITY==1 + +// If building a RakNet DLL, be sure to tweak the CAT_EXPORT macro meaning +#if !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) +# define CAT_BUILD_DLL +#else +# define CAT_NEUTER_EXPORT +#endif + +#include "cat/src/port/EndianNeutral.cpp" +#include "cat/src/port/AlignedAlloc.cpp" +#include "cat/src/time/Clock.cpp" +#include "cat/src/threads/Mutex.cpp" +#include "cat/src/threads/Thread.cpp" +#include "cat/src/threads/WaitableFlag.cpp" +#include "cat/src/hash/MurmurHash2.cpp" +#include "cat/src/lang/Strings.cpp" + +#include "cat/src/math/BigRTL.cpp" +#include "cat/src/math/BigPseudoMersenne.cpp" +#include "cat/src/math/BigTwistedEdwards.cpp" + +#include "cat/src/crypt/SecureCompare.cpp" +#include "cat/src/crypt/cookie/CookieJar.cpp" +#include "cat/src/crypt/hash/HMAC_MD5.cpp" +#include "cat/src/crypt/privatekey/ChaCha.cpp" +#include "cat/src/crypt/hash/Skein.cpp" +#include "cat/src/crypt/hash/Skein256.cpp" +#include "cat/src/crypt/hash/Skein512.cpp" +#include "cat/src/crypt/pass/Passwords.cpp" + +#include "cat/src/crypt/rand/EntropyWindows.cpp" +#include "cat/src/crypt/rand/EntropyLinux.cpp" +#include "cat/src/crypt/rand/EntropyWindowsCE.cpp" +#include "cat/src/crypt/rand/EntropyGeneric.cpp" +#include "cat/src/crypt/rand/Fortuna.cpp" + +#include "cat/src/crypt/tunnel/KeyAgreement.cpp" +#include "cat/src/crypt/tunnel/AuthenticatedEncryption.cpp" +#include "cat/src/crypt/tunnel/KeyAgreementInitiator.cpp" +#include "cat/src/crypt/tunnel/KeyAgreementResponder.cpp" +#include "cat/src/crypt/tunnel/KeyMaker.cpp" + +#include "cat/src/crypt/tunnel/EasyHandshake.cpp" + +#endif // LIBCAT_SECURITY diff --git a/project/lib_projects/raknet/jni/RaknetSources/SecureHandshake.h b/project/lib_projects/raknet/jni/RaknetSources/SecureHandshake.h index b289ff5..ce1ffe6 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SecureHandshake.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SecureHandshake.h @@ -1,25 +1,25 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef SECURE_HANDSHAKE_H -#define SECURE_HANDSHAKE_H - -#include "NativeFeatureIncludes.h" - -#if LIBCAT_SECURITY==1 - -// If building a RakNet DLL, be sure to tweak the CAT_EXPORT macro meaning -#if !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) -# define CAT_BUILD_DLL -#else -# define CAT_NEUTER_EXPORT -#endif - -#include "cat/AllTunnel.hpp" - -#endif // LIBCAT_SECURITY - -#endif // SECURE_HANDSHAKE_H +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef SECURE_HANDSHAKE_H +#define SECURE_HANDSHAKE_H + +#include "NativeFeatureIncludes.h" + +#if LIBCAT_SECURITY==1 + +// If building a RakNet DLL, be sure to tweak the CAT_EXPORT macro meaning +#if !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) +# define CAT_BUILD_DLL +#else +# define CAT_NEUTER_EXPORT +#endif + +#include "cat/AllTunnel.hpp" + +#endif // LIBCAT_SECURITY + +#endif // SECURE_HANDSHAKE_H diff --git a/project/lib_projects/raknet/jni/RaknetSources/SendToThread.cpp b/project/lib_projects/raknet/jni/RaknetSources/SendToThread.cpp index 0c3d313..127c7b8 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SendToThread.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/SendToThread.cpp @@ -1,77 +1,77 @@ -#include "SendToThread.h" -#ifdef USE_THREADED_SEND -#include "RakThread.h" -#include "InternalPacket.h" -#include "GetTime.h" - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 -#include "CCRakNetUDT.h" -#else -#include "CCRakNetSlidingWindow.h" -#endif - -using namespace RakNet; - -int SendToThread::refCount=0; -DataStructures::ThreadsafeAllocatingQueue SendToThread::objectQueue; -ThreadPool SendToThread::threadPool; - -SendToThread::SendToThreadBlock* SendToWorkerThread(SendToThread::SendToThreadBlock* input, bool *returnOutput, void* perThreadData) -{ - (void) perThreadData; - *returnOutput=false; -// RakNet::TimeUS *mostRecentTime=(RakNet::TimeUS *)input->data; -// *mostRecentTime=RakNet::GetTimeUS(); - SocketLayer::SendTo(input->s, input->data, input->dataWriteOffset, input->systemAddress, input->remotePortRakNetWasStartedOn_PS3, input->extraSocketOptions, _FILE_AND_LINE_); - SendToThread::objectQueue.Push(input); - return 0; -} -SendToThread::SendToThread() -{ -} -SendToThread::~SendToThread() -{ - -} -void SendToThread::AddRef(void) -{ - if (++refCount==1) - { - threadPool.StartThreads(1,0); - } -} -void SendToThread::Deref(void) -{ - if (refCount>0) - { - if (--refCount==0) - { - threadPool.StopThreads(); - RakAssert(threadPool.NumThreadsWorking()==0); - - unsigned i; - SendToThreadBlock* info; - for (i=0; i < threadPool.InputSize(); i++) - { - info = threadPool.GetInputAtIndex(i); - objectQueue.Push(info); - } - threadPool.ClearInput(); - objectQueue.Clear(_FILE_AND_LINE_); - } - } -} -SendToThread::SendToThreadBlock* SendToThread::AllocateBlock(void) -{ - SendToThread::SendToThreadBlock *b; - b=objectQueue.Pop(); - if (b==0) - b=objectQueue.Allocate(_FILE_AND_LINE_); - return b; -} -void SendToThread::ProcessBlock(SendToThread::SendToThreadBlock* threadedSend) -{ - RakAssert(threadedSend->dataWriteOffset>0 && threadedSend->dataWriteOffset<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - threadPool.AddInput(SendToWorkerThread,threadedSend); -} -#endif +#include "SendToThread.h" +#ifdef USE_THREADED_SEND +#include "RakThread.h" +#include "InternalPacket.h" +#include "GetTime.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 +#include "CCRakNetUDT.h" +#else +#include "CCRakNetSlidingWindow.h" +#endif + +using namespace RakNet; + +int SendToThread::refCount=0; +DataStructures::ThreadsafeAllocatingQueue SendToThread::objectQueue; +ThreadPool SendToThread::threadPool; + +SendToThread::SendToThreadBlock* SendToWorkerThread(SendToThread::SendToThreadBlock* input, bool *returnOutput, void* perThreadData) +{ + (void) perThreadData; + *returnOutput=false; +// RakNet::TimeUS *mostRecentTime=(RakNet::TimeUS *)input->data; +// *mostRecentTime=RakNet::GetTimeUS(); + SocketLayer::SendTo(input->s, input->data, input->dataWriteOffset, input->systemAddress, input->remotePortRakNetWasStartedOn_PS3, input->extraSocketOptions, _FILE_AND_LINE_); + SendToThread::objectQueue.Push(input); + return 0; +} +SendToThread::SendToThread() +{ +} +SendToThread::~SendToThread() +{ + +} +void SendToThread::AddRef(void) +{ + if (++refCount==1) + { + threadPool.StartThreads(1,0); + } +} +void SendToThread::Deref(void) +{ + if (refCount>0) + { + if (--refCount==0) + { + threadPool.StopThreads(); + RakAssert(threadPool.NumThreadsWorking()==0); + + unsigned i; + SendToThreadBlock* info; + for (i=0; i < threadPool.InputSize(); i++) + { + info = threadPool.GetInputAtIndex(i); + objectQueue.Push(info); + } + threadPool.ClearInput(); + objectQueue.Clear(_FILE_AND_LINE_); + } + } +} +SendToThread::SendToThreadBlock* SendToThread::AllocateBlock(void) +{ + SendToThread::SendToThreadBlock *b; + b=objectQueue.Pop(); + if (b==0) + b=objectQueue.Allocate(_FILE_AND_LINE_); + return b; +} +void SendToThread::ProcessBlock(SendToThread::SendToThreadBlock* threadedSend) +{ + RakAssert(threadedSend->dataWriteOffset>0 && threadedSend->dataWriteOffset<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + threadPool.AddInput(SendToWorkerThread,threadedSend); +} +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/SendToThread.h b/project/lib_projects/raknet/jni/RaknetSources/SendToThread.h index 676a5f4..df1bb51 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SendToThread.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SendToThread.h @@ -1,47 +1,47 @@ -#ifndef __SENDTO_THREAD -#define __SENDTO_THREAD - -#include "RakNetDefines.h" - -#ifdef USE_THREADED_SEND - -#include "InternalPacket.h" -#include "SocketLayer.h" -#include "DS_ThreadsafeAllocatingQueue.h" -#include "ThreadPool.h" - -namespace RakNet -{ -class SendToThread -{ -public: - SendToThread(); - ~SendToThread(); - - struct SendToThreadBlock - { - SOCKET s; - SystemAddress systemAddress; - unsigned short remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions; - char data[MAXIMUM_MTU_SIZE]; - unsigned short dataWriteOffset; - }; - - static SendToThreadBlock* AllocateBlock(void); - static void ProcessBlock(SendToThreadBlock* threadedSend); - - static void AddRef(void); - static void Deref(void); - static DataStructures::ThreadsafeAllocatingQueue objectQueue; -protected: - static int refCount; - static ThreadPool threadPool; - -}; -} - - -#endif - -#endif +#ifndef __SENDTO_THREAD +#define __SENDTO_THREAD + +#include "RakNetDefines.h" + +#ifdef USE_THREADED_SEND + +#include "InternalPacket.h" +#include "SocketLayer.h" +#include "DS_ThreadsafeAllocatingQueue.h" +#include "ThreadPool.h" + +namespace RakNet +{ +class SendToThread +{ +public: + SendToThread(); + ~SendToThread(); + + struct SendToThreadBlock + { + SOCKET s; + SystemAddress systemAddress; + unsigned short remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions; + char data[MAXIMUM_MTU_SIZE]; + unsigned short dataWriteOffset; + }; + + static SendToThreadBlock* AllocateBlock(void); + static void ProcessBlock(SendToThreadBlock* threadedSend); + + static void AddRef(void); + static void Deref(void); + static DataStructures::ThreadsafeAllocatingQueue objectQueue; +protected: + static int refCount; + static ThreadPool threadPool; + +}; +} + + +#endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/SignaledEvent.cpp b/project/lib_projects/raknet/jni/RaknetSources/SignaledEvent.cpp index 6e0ca07..b22599f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SignaledEvent.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/SignaledEvent.cpp @@ -1,245 +1,245 @@ -#include "SignaledEvent.h" -#include "RakAssert.h" -#include "RakSleep.h" - -#if defined(__GNUC__) -#include -#include -#endif - -using namespace RakNet; - - - - - -SignaledEvent::SignaledEvent() -{ -#ifdef _WIN32 - eventList=INVALID_HANDLE_VALUE; - - -#else - isSignaled=false; -#endif -} -SignaledEvent::~SignaledEvent() -{ - // Intentionally do not close event, so it doesn't close twice on linux -} - -void SignaledEvent::InitEvent(void) -{ -#ifdef _WIN32 - eventList=CreateEvent(0, false, false, 0); - - - - - - - - - -#else - -#if !defined(ANDROID) - pthread_condattr_init( &condAttr ); - pthread_cond_init(&eventList, &condAttr); -#else - pthread_cond_init(&eventList, 0); -#endif - pthread_mutexattr_init( &mutexAttr ); - pthread_mutex_init(&hMutex, &mutexAttr); -#endif -} - -void SignaledEvent::CloseEvent(void) -{ -#ifdef _WIN32 - if (eventList!=INVALID_HANDLE_VALUE) - { - CloseHandle(eventList); - eventList=INVALID_HANDLE_VALUE; - } - - - - - - - - - -#else - pthread_cond_destroy(&eventList); - pthread_mutex_destroy(&hMutex); -#if !defined(ANDROID) - pthread_condattr_destroy( &condAttr ); -#endif - pthread_mutexattr_destroy( &mutexAttr ); -#endif -} - -void SignaledEvent::SetEvent(void) -{ -#ifdef _WIN32 - ::SetEvent(eventList); - - - - - - - - - - -#else - // Different from SetEvent which stays signaled. - // We have to record manually that the event was signaled - isSignaledMutex.Lock(); - isSignaled=true; - isSignaledMutex.Unlock(); - - // Unblock waiting threads - pthread_cond_broadcast(&eventList); -#endif -} - -void SignaledEvent::WaitOnEvent(int timeoutMs) -{ -#ifdef _WIN32 -// WaitForMultipleObjects( -// 2, -// eventList, -// false, -// timeoutMs); - WaitForSingleObject(eventList,timeoutMs); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#else - - // If was previously set signaled, just unset and return - isSignaledMutex.Lock(); - if (isSignaled==true) - { - isSignaled=false; - isSignaledMutex.Unlock(); - return; - } - isSignaledMutex.Unlock(); - - - struct timespec ts; - - // Else wait for SetEvent to be called - - - - - - - - - - - - - - - int rc; - struct timeval tp; - rc = gettimeofday(&tp, NULL); - ts.tv_sec = tp.tv_sec; - ts.tv_nsec = tp.tv_usec * 1000; - - - while (timeoutMs > 30) - { - // Wait 30 milliseconds for the signal, then check again. - // This is in case we missed the signal between the top of this function and pthread_cond_timedwait, or after the end of the loop and pthread_cond_timedwait - ts.tv_nsec += 30*1000000; - if (ts.tv_nsec >= 1000000000) - { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - - // [SBC] added mutex lock/unlock around cond_timedwait. - // this prevents airplay from generating a whole much of errors. - // not sure how this works on other platforms since according to - // the docs you are suppost to hold the lock before you wait - // on the cond. - pthread_mutex_lock(&hMutex); - pthread_cond_timedwait(&eventList, &hMutex, &ts); - pthread_mutex_unlock(&hMutex); - - timeoutMs-=30; - - isSignaledMutex.Lock(); - if (isSignaled==true) - { - isSignaled=false; - isSignaledMutex.Unlock(); - return; - } - isSignaledMutex.Unlock(); - } - - // Wait the remaining time, and turn off the signal in case it was set - ts.tv_nsec += timeoutMs*1000000; - if (ts.tv_nsec >= 1000000000) - { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - - pthread_mutex_lock(&hMutex); - pthread_cond_timedwait(&eventList, &hMutex, &ts); - pthread_mutex_unlock(&hMutex); - - isSignaledMutex.Lock(); - isSignaled=false; - isSignaledMutex.Unlock(); -#endif -} +#include "SignaledEvent.h" +#include "RakAssert.h" +#include "RakSleep.h" + +#if defined(__GNUC__) +#include +#include +#endif + +using namespace RakNet; + + + + + +SignaledEvent::SignaledEvent() +{ +#ifdef _WIN32 + eventList=INVALID_HANDLE_VALUE; + + +#else + isSignaled=false; +#endif +} +SignaledEvent::~SignaledEvent() +{ + // Intentionally do not close event, so it doesn't close twice on linux +} + +void SignaledEvent::InitEvent(void) +{ +#ifdef _WIN32 + eventList=CreateEvent(0, false, false, 0); + + + + + + + + + +#else + +#if !defined(ANDROID) + pthread_condattr_init( &condAttr ); + pthread_cond_init(&eventList, &condAttr); +#else + pthread_cond_init(&eventList, 0); +#endif + pthread_mutexattr_init( &mutexAttr ); + pthread_mutex_init(&hMutex, &mutexAttr); +#endif +} + +void SignaledEvent::CloseEvent(void) +{ +#ifdef _WIN32 + if (eventList!=INVALID_HANDLE_VALUE) + { + CloseHandle(eventList); + eventList=INVALID_HANDLE_VALUE; + } + + + + + + + + + +#else + pthread_cond_destroy(&eventList); + pthread_mutex_destroy(&hMutex); +#if !defined(ANDROID) + pthread_condattr_destroy( &condAttr ); +#endif + pthread_mutexattr_destroy( &mutexAttr ); +#endif +} + +void SignaledEvent::SetEvent(void) +{ +#ifdef _WIN32 + ::SetEvent(eventList); + + + + + + + + + + +#else + // Different from SetEvent which stays signaled. + // We have to record manually that the event was signaled + isSignaledMutex.Lock(); + isSignaled=true; + isSignaledMutex.Unlock(); + + // Unblock waiting threads + pthread_cond_broadcast(&eventList); +#endif +} + +void SignaledEvent::WaitOnEvent(int timeoutMs) +{ +#ifdef _WIN32 +// WaitForMultipleObjects( +// 2, +// eventList, +// false, +// timeoutMs); + WaitForSingleObject(eventList,timeoutMs); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#else + + // If was previously set signaled, just unset and return + isSignaledMutex.Lock(); + if (isSignaled==true) + { + isSignaled=false; + isSignaledMutex.Unlock(); + return; + } + isSignaledMutex.Unlock(); + + + struct timespec ts; + + // Else wait for SetEvent to be called + + + + + + + + + + + + + + + int rc; + struct timeval tp; + rc = gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + + + while (timeoutMs > 30) + { + // Wait 30 milliseconds for the signal, then check again. + // This is in case we missed the signal between the top of this function and pthread_cond_timedwait, or after the end of the loop and pthread_cond_timedwait + ts.tv_nsec += 30*1000000; + if (ts.tv_nsec >= 1000000000) + { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + + // [SBC] added mutex lock/unlock around cond_timedwait. + // this prevents airplay from generating a whole much of errors. + // not sure how this works on other platforms since according to + // the docs you are suppost to hold the lock before you wait + // on the cond. + pthread_mutex_lock(&hMutex); + pthread_cond_timedwait(&eventList, &hMutex, &ts); + pthread_mutex_unlock(&hMutex); + + timeoutMs-=30; + + isSignaledMutex.Lock(); + if (isSignaled==true) + { + isSignaled=false; + isSignaledMutex.Unlock(); + return; + } + isSignaledMutex.Unlock(); + } + + // Wait the remaining time, and turn off the signal in case it was set + ts.tv_nsec += timeoutMs*1000000; + if (ts.tv_nsec >= 1000000000) + { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + + pthread_mutex_lock(&hMutex); + pthread_cond_timedwait(&eventList, &hMutex, &ts); + pthread_mutex_unlock(&hMutex); + + isSignaledMutex.Lock(); + isSignaled=false; + isSignaledMutex.Unlock(); +#endif +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/SignaledEvent.h b/project/lib_projects/raknet/jni/RaknetSources/SignaledEvent.h index b744b40..3d36948 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SignaledEvent.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SignaledEvent.h @@ -1,59 +1,59 @@ -#ifndef __SIGNALED_EVENT_H -#define __SIGNALED_EVENT_H - - - -#if defined(_WIN32) -#include - - - -#else - #include - #include - #include "SimpleMutex.h" - - - - -#endif - -#include "Export.h" - -namespace RakNet -{ - -class RAK_DLL_EXPORT SignaledEvent -{ -public: - SignaledEvent(); - ~SignaledEvent(); - - void InitEvent(void); - void CloseEvent(void); - void SetEvent(void); - void WaitOnEvent(int timeoutMs); - -protected: -#ifdef _WIN32 - HANDLE eventList; - - - - - -#else - SimpleMutex isSignaledMutex; - bool isSignaled; -#if !defined(ANDROID) - pthread_condattr_t condAttr; -#endif - pthread_cond_t eventList; - pthread_mutex_t hMutex; - pthread_mutexattr_t mutexAttr; -#endif -}; - -} // namespace RakNet - -#endif +#ifndef __SIGNALED_EVENT_H +#define __SIGNALED_EVENT_H + + + +#if defined(_WIN32) +#include + + + +#else + #include + #include + #include "SimpleMutex.h" + + + + +#endif + +#include "Export.h" + +namespace RakNet +{ + +class RAK_DLL_EXPORT SignaledEvent +{ +public: + SignaledEvent(); + ~SignaledEvent(); + + void InitEvent(void); + void CloseEvent(void); + void SetEvent(void); + void WaitOnEvent(int timeoutMs); + +protected: +#ifdef _WIN32 + HANDLE eventList; + + + + + +#else + SimpleMutex isSignaledMutex; + bool isSignaled; +#if !defined(ANDROID) + pthread_condattr_t condAttr; +#endif + pthread_cond_t eventList; + pthread_mutex_t hMutex; + pthread_mutexattr_t mutexAttr; +#endif +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/SimpleMutex.cpp b/project/lib_projects/raknet/jni/RaknetSources/SimpleMutex.cpp index 0f5eae7..26e90b5 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SimpleMutex.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/SimpleMutex.cpp @@ -1,181 +1,181 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "SimpleMutex.h" -#include "RakAssert.h" - -using namespace RakNet; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SimpleMutex::SimpleMutex() //: isInitialized(false) -{ - - - - - - - - // Prior implementation of Initializing in Lock() was not threadsafe - Init(); -} - -SimpleMutex::~SimpleMutex() -{ -// if (isInitialized==false) -// return; -#ifdef _WIN32 - // CloseHandle(hMutex); - DeleteCriticalSection(&criticalSection); - - - - - - -#else - pthread_mutex_destroy(&hMutex); -#endif - - - - - - - -} - -#ifdef _WIN32 -#ifdef _DEBUG -#include -#endif -#endif - -void SimpleMutex::Lock(void) -{ -// if (isInitialized==false) -// Init(); - -#ifdef _WIN32 - /* - DWORD d = WaitForSingleObject(hMutex, INFINITE); - #ifdef _DEBUG - if (d==WAIT_FAILED) - { - LPVOID messageBuffer; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPTSTR) &messageBuffer, - 0, - NULL - ); - // Process any inserts in messageBuffer. - // ... - // Display the string. - //MessageBox( NULL, (LPCTSTR)messageBuffer, "Error", MB_OK | MB_ICONINFORMATION ); - RAKNET_DEBUG_PRINTF("SimpleMutex error: %s", messageBuffer); - // Free the buffer. - LocalFree( messageBuffer ); - - } - - RakAssert(d==WAIT_OBJECT_0); - */ - EnterCriticalSection(&criticalSection); - - - - - - -#else - int error = pthread_mutex_lock(&hMutex); - (void) error; - RakAssert(error==0); -#endif -} - -void SimpleMutex::Unlock(void) -{ -// if (isInitialized==false) -// return; -#ifdef _WIN32 - // ReleaseMutex(hMutex); - LeaveCriticalSection(&criticalSection); - - - - - - -#else - int error = pthread_mutex_unlock(&hMutex); - (void) error; - RakAssert(error==0); -#endif -} - -void SimpleMutex::Init(void) -{ -#ifdef _WIN32 - // hMutex = CreateMutex(NULL, FALSE, 0); - // RakAssert(hMutex); - InitializeCriticalSection(&criticalSection); - - - - - - - - -#else - int error = pthread_mutex_init(&hMutex, 0); - (void) error; - RakAssert(error==0); -#endif -// isInitialized=true; -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "SimpleMutex.h" +#include "RakAssert.h" + +using namespace RakNet; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +SimpleMutex::SimpleMutex() //: isInitialized(false) +{ + + + + + + + + // Prior implementation of Initializing in Lock() was not threadsafe + Init(); +} + +SimpleMutex::~SimpleMutex() +{ +// if (isInitialized==false) +// return; +#ifdef _WIN32 + // CloseHandle(hMutex); + DeleteCriticalSection(&criticalSection); + + + + + + +#else + pthread_mutex_destroy(&hMutex); +#endif + + + + + + + +} + +#ifdef _WIN32 +#ifdef _DEBUG +#include +#endif +#endif + +void SimpleMutex::Lock(void) +{ +// if (isInitialized==false) +// Init(); + +#ifdef _WIN32 + /* + DWORD d = WaitForSingleObject(hMutex, INFINITE); + #ifdef _DEBUG + if (d==WAIT_FAILED) + { + LPVOID messageBuffer; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &messageBuffer, + 0, + NULL + ); + // Process any inserts in messageBuffer. + // ... + // Display the string. + //MessageBox( NULL, (LPCTSTR)messageBuffer, "Error", MB_OK | MB_ICONINFORMATION ); + RAKNET_DEBUG_PRINTF("SimpleMutex error: %s", messageBuffer); + // Free the buffer. + LocalFree( messageBuffer ); + + } + + RakAssert(d==WAIT_OBJECT_0); + */ + EnterCriticalSection(&criticalSection); + + + + + + +#else + int error = pthread_mutex_lock(&hMutex); + (void) error; + RakAssert(error==0); +#endif +} + +void SimpleMutex::Unlock(void) +{ +// if (isInitialized==false) +// return; +#ifdef _WIN32 + // ReleaseMutex(hMutex); + LeaveCriticalSection(&criticalSection); + + + + + + +#else + int error = pthread_mutex_unlock(&hMutex); + (void) error; + RakAssert(error==0); +#endif +} + +void SimpleMutex::Init(void) +{ +#ifdef _WIN32 + // hMutex = CreateMutex(NULL, FALSE, 0); + // RakAssert(hMutex); + InitializeCriticalSection(&criticalSection); + + + + + + + + +#else + int error = pthread_mutex_init(&hMutex, 0); + (void) error; + RakAssert(error==0); +#endif +// isInitialized=true; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/SimpleMutex.h b/project/lib_projects/raknet/jni/RaknetSources/SimpleMutex.h index cf28355..8868851 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SimpleMutex.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SimpleMutex.h @@ -1,70 +1,70 @@ -/// \file -/// \brief \b [Internal] Encapsulates a mutex -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __SIMPLE_MUTEX_H -#define __SIMPLE_MUTEX_H - -#include "RakMemoryOverride.h" - - -#if defined(_WIN32) -#include "WindowsIncludes.h" - - -#else -#include -#include -#endif -#include "Export.h" - -namespace RakNet -{ - -/// \brief An easy to use mutex. -/// -/// I wrote this because the version that comes with Windows is too complicated and requires too much code to use. -/// @remark Previously I used this everywhere, and in fact for a year or two RakNet was totally threadsafe. While doing profiling, I saw that this function was incredibly slow compared to the blazing performance of everything else, so switched to single producer / consumer everywhere. Now the user thread of RakNet is not threadsafe, but it's 100X faster than before. -class RAK_DLL_EXPORT SimpleMutex -{ -public: - - // Constructor - SimpleMutex(); - - // Destructor - ~SimpleMutex(); - - // Locks the mutex. Slow! - void Lock(void); - - // Unlocks the mutex. - void Unlock(void); - - - - - - - -private: - void Init(void); -#ifdef _WIN32 - CRITICAL_SECTION criticalSection; /// Docs say this is faster than a mutex for single process access - - -#else - pthread_mutex_t hMutex; -#endif - // Not threadsafe - // bool isInitialized; -}; - -} // namespace RakNet - -#endif - +/// \file +/// \brief \b [Internal] Encapsulates a mutex +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __SIMPLE_MUTEX_H +#define __SIMPLE_MUTEX_H + +#include "RakMemoryOverride.h" + + +#if defined(_WIN32) +#include "WindowsIncludes.h" + + +#else +#include +#include +#endif +#include "Export.h" + +namespace RakNet +{ + +/// \brief An easy to use mutex. +/// +/// I wrote this because the version that comes with Windows is too complicated and requires too much code to use. +/// @remark Previously I used this everywhere, and in fact for a year or two RakNet was totally threadsafe. While doing profiling, I saw that this function was incredibly slow compared to the blazing performance of everything else, so switched to single producer / consumer everywhere. Now the user thread of RakNet is not threadsafe, but it's 100X faster than before. +class RAK_DLL_EXPORT SimpleMutex +{ +public: + + // Constructor + SimpleMutex(); + + // Destructor + ~SimpleMutex(); + + // Locks the mutex. Slow! + void Lock(void); + + // Unlocks the mutex. + void Unlock(void); + + + + + + + +private: + void Init(void); +#ifdef _WIN32 + CRITICAL_SECTION criticalSection; /// Docs say this is faster than a mutex for single process access + + +#else + pthread_mutex_t hMutex; +#endif + // Not threadsafe + // bool isInitialized; +}; + +} // namespace RakNet + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/SingleProducerConsumer.h b/project/lib_projects/raknet/jni/RaknetSources/SingleProducerConsumer.h index 8897e40..7bffbfc 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SingleProducerConsumer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SingleProducerConsumer.h @@ -1,259 +1,259 @@ -/// \file -/// \brief \b [Internal] Passes queued data between threads using a circular buffer with read and write pointers -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __SINGLE_PRODUCER_CONSUMER_H -#define __SINGLE_PRODUCER_CONSUMER_H - -#include "RakAssert.h" - -static const int MINIMUM_LIST_SIZE=8; - -#include "RakMemoryOverride.h" -#include "Export.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// \brief A single producer consumer implementation without critical sections. - template - class RAK_DLL_EXPORT SingleProducerConsumer - { - public: - // Constructor - SingleProducerConsumer(); - - // Destructor - ~SingleProducerConsumer(); - - /// WriteLock must be immediately followed by WriteUnlock. These two functions must be called in the same thread. - /// \return A pointer to a block of data you can write to. - SingleProducerConsumerType* WriteLock(void); - - /// Call if you don't want to write to a block of data from WriteLock() after all. - /// Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored - /// \param[in] cancelToLocation Which WriteLock() to cancel. - void CancelWriteLock(SingleProducerConsumerType* cancelToLocation); - - /// Call when you are done writing to a block of memory returned by WriteLock() - void WriteUnlock(void); - - /// ReadLock must be immediately followed by ReadUnlock. These two functions must be called in the same thread. - /// \retval 0 No data is availble to read - /// \retval Non-zero The data previously written to, in another thread, by WriteLock followed by WriteUnlock. - SingleProducerConsumerType* ReadLock(void); - - // Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored - /// param[in] Which ReadLock() to cancel. - void CancelReadLock(SingleProducerConsumerType* cancelToLocation); - - /// Signals that we are done reading the the data from the least recent call of ReadLock. - /// At this point that pointer is no longer valid, and should no longer be read. - void ReadUnlock(void); - - /// Clear is not thread-safe and none of the lock or unlock functions should be called while it is running. - void Clear(void); - - /// This function will estimate how many elements are waiting to be read. It's threadsafe enough that the value returned is stable, but not threadsafe enough to give accurate results. - /// \return An ESTIMATE of how many data elements are waiting to be read - int Size(void) const; - - /// Make sure that the pointer we done reading for the call to ReadUnlock is the right pointer. - /// param[in] A previous pointer returned by ReadLock() - bool CheckReadUnlockOrder(const SingleProducerConsumerType* data) const; - - /// Returns if ReadUnlock was called before ReadLock - /// \return If the read is locked - bool ReadIsLocked(void) const; - - private: - struct DataPlusPtr - { - DataPlusPtr () {readyToRead=false;} - SingleProducerConsumerType object; - - // Ready to read is so we can use an equality boolean comparison, in case the writePointer var is trashed while context switching. - volatile bool readyToRead; - volatile DataPlusPtr *next; - }; - volatile DataPlusPtr *readAheadPointer; - volatile DataPlusPtr *writeAheadPointer; - volatile DataPlusPtr *readPointer; - volatile DataPlusPtr *writePointer; - unsigned readCount, writeCount; - }; - - template - SingleProducerConsumer::SingleProducerConsumer() - { - // Preallocate - readPointer = RakNet::OP_NEW( _FILE_AND_LINE_ ); - writePointer=readPointer; - readPointer->next = RakNet::OP_NEW( _FILE_AND_LINE_ ); - int listSize; -#ifdef _DEBUG - RakAssert(MINIMUM_LIST_SIZE>=3); -#endif - for (listSize=2; listSize < MINIMUM_LIST_SIZE; listSize++) - { - readPointer=readPointer->next; - readPointer->next = RakNet::OP_NEW( _FILE_AND_LINE_ ); - } - readPointer->next->next=writePointer; // last to next = start - readPointer=writePointer; - readAheadPointer=readPointer; - writeAheadPointer=writePointer; - readCount=writeCount=0; - } - - template - SingleProducerConsumer::~SingleProducerConsumer() - { - volatile DataPlusPtr *next; - readPointer=writeAheadPointer->next; - while (readPointer!=writeAheadPointer) - { - next=readPointer->next; - RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_); - readPointer=next; - } - RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_); - } - - template - SingleProducerConsumerType* SingleProducerConsumer::WriteLock( void ) - { - if (writeAheadPointer->next==readPointer || - writeAheadPointer->next->readyToRead==true) - { - volatile DataPlusPtr *originalNext=writeAheadPointer->next; - writeAheadPointer->next=RakNet::OP_NEW(_FILE_AND_LINE_); - RakAssert(writeAheadPointer->next); - writeAheadPointer->next->next=originalNext; - } - - volatile DataPlusPtr *last; - last=writeAheadPointer; - writeAheadPointer=writeAheadPointer->next; - - return (SingleProducerConsumerType*) last; - } - - template - void SingleProducerConsumer::CancelWriteLock( SingleProducerConsumerType* cancelToLocation ) - { - writeAheadPointer=(DataPlusPtr *)cancelToLocation; - } - - template - void SingleProducerConsumer::WriteUnlock( void ) - { - // DataPlusPtr *dataContainer = (DataPlusPtr *)structure; - -#ifdef _DEBUG - RakAssert(writePointer->next!=readPointer); - RakAssert(writePointer!=writeAheadPointer); -#endif - - writeCount++; - // User is done with the data, allow send by updating the write pointer - writePointer->readyToRead=true; - writePointer=writePointer->next; - } - - template - SingleProducerConsumerType* SingleProducerConsumer::ReadLock( void ) - { - if (readAheadPointer==writePointer || - readAheadPointer->readyToRead==false) - { - return 0; - } - - volatile DataPlusPtr *last; - last=readAheadPointer; - readAheadPointer=readAheadPointer->next; - return (SingleProducerConsumerType*)last; - } - - template - void SingleProducerConsumer::CancelReadLock( SingleProducerConsumerType* cancelToLocation ) - { -#ifdef _DEBUG - RakAssert(readPointer!=writePointer); -#endif - readAheadPointer=(DataPlusPtr *)cancelToLocation; - } - - template - void SingleProducerConsumer::ReadUnlock( void ) - { -#ifdef _DEBUG - RakAssert(readAheadPointer!=readPointer); // If hits, then called ReadUnlock before ReadLock - RakAssert(readPointer!=writePointer); // If hits, then called ReadUnlock when Read returns 0 -#endif - readCount++; - - // Allow writes to this memory block - readPointer->readyToRead=false; - readPointer=readPointer->next; - } - - template - void SingleProducerConsumer::Clear( void ) - { - // Shrink the list down to MINIMUM_LIST_SIZE elements - volatile DataPlusPtr *next; - writePointer=readPointer->next; - - int listSize=1; - next=readPointer->next; - while (next!=readPointer) - { - listSize++; - next=next->next; - } - - while (listSize-- > MINIMUM_LIST_SIZE) - { - next=writePointer->next; -#ifdef _DEBUG - RakAssert(writePointer!=readPointer); -#endif - RakNet::OP_DELETE((char*) writePointer, _FILE_AND_LINE_); - writePointer=next; - } - - readPointer->next=writePointer; - writePointer=readPointer; - readAheadPointer=readPointer; - writeAheadPointer=writePointer; - readCount=writeCount=0; - } - - template - int SingleProducerConsumer::Size( void ) const - { - return writeCount-readCount; - } - - template - bool SingleProducerConsumer::CheckReadUnlockOrder(const SingleProducerConsumerType* data) const - { - return const_cast(&readPointer->object) == data; - } - - - template - bool SingleProducerConsumer::ReadIsLocked(void) const - { - return readAheadPointer!=readPointer; - } -} - -#endif +/// \file +/// \brief \b [Internal] Passes queued data between threads using a circular buffer with read and write pointers +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __SINGLE_PRODUCER_CONSUMER_H +#define __SINGLE_PRODUCER_CONSUMER_H + +#include "RakAssert.h" + +static const int MINIMUM_LIST_SIZE=8; + +#include "RakMemoryOverride.h" +#include "Export.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief A single producer consumer implementation without critical sections. + template + class RAK_DLL_EXPORT SingleProducerConsumer + { + public: + // Constructor + SingleProducerConsumer(); + + // Destructor + ~SingleProducerConsumer(); + + /// WriteLock must be immediately followed by WriteUnlock. These two functions must be called in the same thread. + /// \return A pointer to a block of data you can write to. + SingleProducerConsumerType* WriteLock(void); + + /// Call if you don't want to write to a block of data from WriteLock() after all. + /// Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored + /// \param[in] cancelToLocation Which WriteLock() to cancel. + void CancelWriteLock(SingleProducerConsumerType* cancelToLocation); + + /// Call when you are done writing to a block of memory returned by WriteLock() + void WriteUnlock(void); + + /// ReadLock must be immediately followed by ReadUnlock. These two functions must be called in the same thread. + /// \retval 0 No data is availble to read + /// \retval Non-zero The data previously written to, in another thread, by WriteLock followed by WriteUnlock. + SingleProducerConsumerType* ReadLock(void); + + // Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored + /// param[in] Which ReadLock() to cancel. + void CancelReadLock(SingleProducerConsumerType* cancelToLocation); + + /// Signals that we are done reading the the data from the least recent call of ReadLock. + /// At this point that pointer is no longer valid, and should no longer be read. + void ReadUnlock(void); + + /// Clear is not thread-safe and none of the lock or unlock functions should be called while it is running. + void Clear(void); + + /// This function will estimate how many elements are waiting to be read. It's threadsafe enough that the value returned is stable, but not threadsafe enough to give accurate results. + /// \return An ESTIMATE of how many data elements are waiting to be read + int Size(void) const; + + /// Make sure that the pointer we done reading for the call to ReadUnlock is the right pointer. + /// param[in] A previous pointer returned by ReadLock() + bool CheckReadUnlockOrder(const SingleProducerConsumerType* data) const; + + /// Returns if ReadUnlock was called before ReadLock + /// \return If the read is locked + bool ReadIsLocked(void) const; + + private: + struct DataPlusPtr + { + DataPlusPtr () {readyToRead=false;} + SingleProducerConsumerType object; + + // Ready to read is so we can use an equality boolean comparison, in case the writePointer var is trashed while context switching. + volatile bool readyToRead; + volatile DataPlusPtr *next; + }; + volatile DataPlusPtr *readAheadPointer; + volatile DataPlusPtr *writeAheadPointer; + volatile DataPlusPtr *readPointer; + volatile DataPlusPtr *writePointer; + unsigned readCount, writeCount; + }; + + template + SingleProducerConsumer::SingleProducerConsumer() + { + // Preallocate + readPointer = RakNet::OP_NEW( _FILE_AND_LINE_ ); + writePointer=readPointer; + readPointer->next = RakNet::OP_NEW( _FILE_AND_LINE_ ); + int listSize; +#ifdef _DEBUG + RakAssert(MINIMUM_LIST_SIZE>=3); +#endif + for (listSize=2; listSize < MINIMUM_LIST_SIZE; listSize++) + { + readPointer=readPointer->next; + readPointer->next = RakNet::OP_NEW( _FILE_AND_LINE_ ); + } + readPointer->next->next=writePointer; // last to next = start + readPointer=writePointer; + readAheadPointer=readPointer; + writeAheadPointer=writePointer; + readCount=writeCount=0; + } + + template + SingleProducerConsumer::~SingleProducerConsumer() + { + volatile DataPlusPtr *next; + readPointer=writeAheadPointer->next; + while (readPointer!=writeAheadPointer) + { + next=readPointer->next; + RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_); + readPointer=next; + } + RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_); + } + + template + SingleProducerConsumerType* SingleProducerConsumer::WriteLock( void ) + { + if (writeAheadPointer->next==readPointer || + writeAheadPointer->next->readyToRead==true) + { + volatile DataPlusPtr *originalNext=writeAheadPointer->next; + writeAheadPointer->next=RakNet::OP_NEW(_FILE_AND_LINE_); + RakAssert(writeAheadPointer->next); + writeAheadPointer->next->next=originalNext; + } + + volatile DataPlusPtr *last; + last=writeAheadPointer; + writeAheadPointer=writeAheadPointer->next; + + return (SingleProducerConsumerType*) last; + } + + template + void SingleProducerConsumer::CancelWriteLock( SingleProducerConsumerType* cancelToLocation ) + { + writeAheadPointer=(DataPlusPtr *)cancelToLocation; + } + + template + void SingleProducerConsumer::WriteUnlock( void ) + { + // DataPlusPtr *dataContainer = (DataPlusPtr *)structure; + +#ifdef _DEBUG + RakAssert(writePointer->next!=readPointer); + RakAssert(writePointer!=writeAheadPointer); +#endif + + writeCount++; + // User is done with the data, allow send by updating the write pointer + writePointer->readyToRead=true; + writePointer=writePointer->next; + } + + template + SingleProducerConsumerType* SingleProducerConsumer::ReadLock( void ) + { + if (readAheadPointer==writePointer || + readAheadPointer->readyToRead==false) + { + return 0; + } + + volatile DataPlusPtr *last; + last=readAheadPointer; + readAheadPointer=readAheadPointer->next; + return (SingleProducerConsumerType*)last; + } + + template + void SingleProducerConsumer::CancelReadLock( SingleProducerConsumerType* cancelToLocation ) + { +#ifdef _DEBUG + RakAssert(readPointer!=writePointer); +#endif + readAheadPointer=(DataPlusPtr *)cancelToLocation; + } + + template + void SingleProducerConsumer::ReadUnlock( void ) + { +#ifdef _DEBUG + RakAssert(readAheadPointer!=readPointer); // If hits, then called ReadUnlock before ReadLock + RakAssert(readPointer!=writePointer); // If hits, then called ReadUnlock when Read returns 0 +#endif + readCount++; + + // Allow writes to this memory block + readPointer->readyToRead=false; + readPointer=readPointer->next; + } + + template + void SingleProducerConsumer::Clear( void ) + { + // Shrink the list down to MINIMUM_LIST_SIZE elements + volatile DataPlusPtr *next; + writePointer=readPointer->next; + + int listSize=1; + next=readPointer->next; + while (next!=readPointer) + { + listSize++; + next=next->next; + } + + while (listSize-- > MINIMUM_LIST_SIZE) + { + next=writePointer->next; +#ifdef _DEBUG + RakAssert(writePointer!=readPointer); +#endif + RakNet::OP_DELETE((char*) writePointer, _FILE_AND_LINE_); + writePointer=next; + } + + readPointer->next=writePointer; + writePointer=readPointer; + readAheadPointer=readPointer; + writeAheadPointer=writePointer; + readCount=writeCount=0; + } + + template + int SingleProducerConsumer::Size( void ) const + { + return writeCount-readCount; + } + + template + bool SingleProducerConsumer::CheckReadUnlockOrder(const SingleProducerConsumerType* data) const + { + return const_cast(&readPointer->object) == data; + } + + + template + bool SingleProducerConsumer::ReadIsLocked(void) const + { + return readAheadPointer!=readPointer; + } +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/SocketDefines.h b/project/lib_projects/raknet/jni/RaknetSources/SocketDefines.h index f6548ab..248cf2d 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SocketDefines.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SocketDefines.h @@ -1,69 +1,69 @@ -#ifndef __SOCKET_DEFINES_H -#define __SOCKET_DEFINES_H - -/// Internal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #if defined(_WIN32) - #define closesocket__ closesocket - #define select__ select - #else - #define closesocket__ close - #define select__ select - #endif -#define accept__ accept -#define connect__ connect -#define socket__ socket -#define bind__ bind -#define getsockname__ getsockname -#define getsockopt__ getsockopt -#define inet_addr__ inet_addr -#define ioctlsocket__ ioctlsocket -#define listen__ listen -#define recv__ recv -#define recvfrom__ recvfrom -#define sendto__ sendto -#define send__ send -#define setsockopt__ setsockopt -#define shutdown__ shutdown -#define WSASendTo__ WSASendTo - - -#endif +#ifndef __SOCKET_DEFINES_H +#define __SOCKET_DEFINES_H + +/// Internal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #if defined(_WIN32) + #define closesocket__ closesocket + #define select__ select + #else + #define closesocket__ close + #define select__ select + #endif +#define accept__ accept +#define connect__ connect +#define socket__ socket +#define bind__ bind +#define getsockname__ getsockname +#define getsockopt__ getsockopt +#define inet_addr__ inet_addr +#define ioctlsocket__ ioctlsocket +#define listen__ listen +#define recv__ recv +#define recvfrom__ recvfrom +#define sendto__ sendto +#define send__ send +#define setsockopt__ setsockopt +#define shutdown__ shutdown +#define WSASendTo__ WSASendTo + + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/SocketIncludes.h b/project/lib_projects/raknet/jni/RaknetSources/SocketIncludes.h index 3c530c5..418e074 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SocketIncludes.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SocketIncludes.h @@ -1,31 +1,31 @@ -// All this crap just to include type SOCKET - - - - - - - - - - -#if defined(_WIN32) -typedef int socklen_t; -// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib -// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly -#include -#else -#define closesocket close -#include -#include -#include -#include -#include -#include -#include -#include -#define INVALID_SOCKET -1 -//#include "RakMemoryOverride.h" -/// Unix/Linux uses ints for sockets -typedef int SOCKET; -#endif +// All this crap just to include type SOCKET + + + + + + + + + + +#if defined(_WIN32) +typedef int socklen_t; +// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib +// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly +#include +#else +#define closesocket close +#include +#include +#include +#include +#include +#include +#include +#include +#define INVALID_SOCKET -1 +//#include "RakMemoryOverride.h" +/// Unix/Linux uses ints for sockets +typedef int SOCKET; +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/SocketLayer.cpp b/project/lib_projects/raknet/jni/RaknetSources/SocketLayer.cpp index ae11076..53537fa 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SocketLayer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/SocketLayer.cpp @@ -1,1851 +1,1851 @@ -/// \file -/// \brief SocketLayer class implementation -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "SocketLayer.h" -#include "RakAssert.h" -#include "RakNetTypes.h" -#include "GetTime.h" -#include "LinuxStrings.h" -#include "SocketDefines.h" - -using namespace RakNet; - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 -#include "CCRakNetUDT.h" -#else -#include "CCRakNetSlidingWindow.h" -#endif - -SocketLayerOverride *SocketLayer::slo=0; - -#ifdef _WIN32 -#else -#include // memcpy -#include -#include -#include -#include // error numbers -#include // RAKNET_DEBUG_PRINTF -#if !defined(ANDROID) -#include -#endif -#include -#include -#include -#include -#include - -#endif - - - - - - - - - - - - - -#if defined(_WIN32) -#include "WSAStartupSingleton.h" -#include // 'IP_DONTFRAGMENT' 'IP_TTL' -#elif defined SN_TARGET_PSP2 -#else -#include -#endif - -#include "RakSleep.h" -#include -#include "Itoa.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -namespace RakNet -{ - extern void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead ); - extern void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead ); -} - -#ifdef _DEBUG -#include -#endif - -// http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#ip4to6 -// http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#getaddrinfo - -#if RAKNET_SUPPORT_IPV6==1 -void PrepareAddrInfoHints(addrinfo *hints) -{ - memset(hints, 0, sizeof (addrinfo)); // make sure the struct is empty - hints->ai_socktype = SOCK_DGRAM; // UDP sockets - hints->ai_flags = AI_PASSIVE; // fill in my IP for me -} -#endif - -// Frogwares: Define this -// #define DEBUG_SENDTO_SPIKES - -bool SocketLayer::IsPortInUse_Old(unsigned short port, const char *hostAddress) -{ - SOCKET listenSocket; - sockaddr_in listenerSocketAddress; - memset(&listenerSocketAddress,0,sizeof(sockaddr_in)); - // Listen on our designated Port# - listenerSocketAddress.sin_port = htons( port ); - listenSocket = socket__( AF_INET, SOCK_DGRAM, 0 ); - if ( listenSocket == (SOCKET) -1 ) - return true; - // bind our name to the socket - // Fill in the rest of the address structure - listenerSocketAddress.sin_family = AF_INET; - - if ( hostAddress && hostAddress[0] ) - { - - - - listenerSocketAddress.sin_addr.s_addr = inet_addr__( hostAddress ); - - } - else - listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; - - - - - - - - int ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); - closesocket__(listenSocket); - - - - - // #if defined(_DEBUG) - // if (ret == -1) - // perror("Failed to bind to address:"); - // #endif - return ret <= -1; - -} -bool SocketLayer::IsSocketFamilySupported(const char *hostAddress, unsigned short socketFamily) -{ - (void) hostAddress; - (void) socketFamily; - -#if RAKNET_SUPPORT_IPV6!=1 - return socketFamily==AF_INET; -#else - struct addrinfo hints; -#if RAKNET_SUPPORT_IPV6==1 - PrepareAddrInfoHints(&hints); -#endif - hints.ai_family = socketFamily; - struct addrinfo *servinfo=0; - int error; - // On Ubuntu, "" returns "No address associated with hostname" while 0 works. - if (hostAddress && - (_stricmp(hostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || hostAddress[0]==0)) - { - getaddrinfo(0, "0", &hints, &servinfo); - } - else - { - getaddrinfo(hostAddress, "0", &hints, &servinfo); - } - - - (void) error; - if (servinfo) - { - freeaddrinfo(servinfo); - return true; - } - else - { -#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_WIN32) - printf("IsSocketFamilySupported failed. hostAddress=%s. %s\n", hostAddress, gai_strerror(error)); -#endif - } - return false; -#endif -} -bool SocketLayer::IsPortInUse(unsigned short port, const char *hostAddress, unsigned short socketFamily) -{ -#if RAKNET_SUPPORT_IPV6!=1 - (void) socketFamily; - return IsPortInUse_Old(port, hostAddress); -#else - SOCKET listenSocket; - struct addrinfo hints; - struct addrinfo *servinfo=0, *aip; // will point to the results - PrepareAddrInfoHints(&hints); - hints.ai_family = socketFamily; - char portStr[32]; - Itoa(port,portStr,10); - - // On Ubuntu, "" returns "No address associated with hostname" while 0 works. - if (hostAddress && - (_stricmp(hostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || hostAddress[0]==0)) - { - getaddrinfo(0, portStr, &hints, &servinfo); - } - else - { - getaddrinfo(hostAddress, portStr, &hints, &servinfo); - } - - // Try all returned addresses until one works - for (aip = servinfo; aip != NULL; aip = aip->ai_next) - { - // Open socket. The address type depends on what - // getaddrinfo() gave us. - listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); - if (listenSocket != -1) - { - int ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); - closesocket__(listenSocket); - if (ret>=0) - { - freeaddrinfo(servinfo); // free the linked-list - return false; - } - } - - // If the user didn't specify which host address, then only apply the first - if (hostAddress==0 || hostAddress[0]==0) - break; - } - - freeaddrinfo(servinfo); // free the linked-list - return true; -#endif // #if RAKNET_SUPPORT_IPV6!=1 -} -void SocketLayer::SetDoNotFragment( SOCKET listenSocket, int opt, int IPPROTO ) -{ -#if defined(IP_DONTFRAGMENT ) - -#if defined(_WIN32) && defined(_DEBUG) - // If this assert hit you improperly linked against WSock32.h - RakAssert(IP_DONTFRAGMENT==14); -#endif - - if ( setsockopt__( listenSocket, IPPROTO, IP_DONTFRAGMENT, ( char * ) & opt, sizeof ( opt ) ) == -1 ) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // I see this hit on XP with IPV6 for some reason - RAKNET_DEBUG_PRINTF( "Warning: setsockopt__(IP_DONTFRAGMENT) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - LocalFree( messageBuffer ); -#endif - } -#endif -} - -void SocketLayer::SetNonBlocking( SOCKET listenSocket) -{ -#ifdef _WIN32 - unsigned long nonBlocking = 1; - ioctlsocket__( listenSocket, FIONBIO, &nonBlocking ); - - - -#else - int flags = fcntl(listenSocket, F_GETFL, 0); - fcntl(listenSocket, F_SETFL, flags | O_NONBLOCK); -#endif -} - -void SocketLayer::SetSocketOptions( SOCKET listenSocket) -{ - int sock_opt = 1; - // // On Vista, can get WSAEACCESS (10013) - /* - if ( setsockopt__( listenSocket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & sock_opt, sizeof ( sock_opt ) ) == -1 ) - { - #if defined(_WIN32) && !defined(_XBOX) && defined(_DEBUG) && !defined(X360) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "setsockopt__(SO_REUSEADDR) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); - #endif - } - */ - - // This doubles the max throughput rate - sock_opt=1024*256; - setsockopt__(listenSocket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - // Immediate hard close. Don't linger the socket, or recreating the socket quickly on Vista fails. - // Fail with voice and xbox - - sock_opt=0; - setsockopt__(listenSocket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - - - // This doesn't make much difference: 10% maybe - // Not supported on console 2 - sock_opt=1024*16; - setsockopt__(listenSocket, SOL_SOCKET, SO_SNDBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - -#ifdef __APPLE__ - // Preventing SEGPIPE on "broken" socket - int set = 1; - setsockopt__(listenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); -#endif - - /* - #ifdef _WIN32 - unsigned long nonblocking = 1; - ioctlsocket__( listenSocket, FIONBIO, &nonblocking ); - #elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) - sock_opt=1; - setsockopt__(listenSocket, SOL_SOCKET, SO_NBIO, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - #else - fcntl( listenSocket, F_SETFL, O_NONBLOCK ); - #endif - */ - - // TODO - teston ipv6 - - // Note: Fails with VDP but not xbox - // Set broadcast capable - sock_opt=1; - if ( setsockopt__( listenSocket, SOL_SOCKET, SO_BROADCAST, ( char * ) & sock_opt, sizeof( sock_opt ) ) == -1 ) - { -#if defined(_WIN32) && defined(_DEBUG) - - DWORD dwIOError = GetLastError(); - // On Vista, can get WSAEACCESS (10013) - // See http://support.microsoft.com/kb/819124 - // http://blogs.msdn.com/wndp/archive/2007/03/19/winsock-so-exclusiveaddruse-on-vista.aspx - // http://msdn.microsoft.com/en-us/library/ms740621(VS.85).aspx - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "setsockopt__(SO_BROADCAST) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); - -#endif - - } -} -SOCKET SocketLayer::CreateBoundSocket_PS3Lobby( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ) -{ - (void) port; - (void) blockingSocket; - (void) forceHostAddress; - (void) socketFamily; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - return 0; - -} -SOCKET SocketLayer::CreateBoundSocket_PSP2( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ) -{ - (void) port; - (void) blockingSocket; - (void) forceHostAddress; - (void) socketFamily; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - return 0; - -} -SOCKET SocketLayer::CreateBoundSocket_Old( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions ) -{ - (void) blockingSocket; - - int ret; - SOCKET listenSocket; - sockaddr_in listenerSocketAddress; - memset(&listenerSocketAddress,0,sizeof(sockaddr_in)); - // Listen on our designated Port# - listenerSocketAddress.sin_port = htons( port ); - - listenSocket = socket__( AF_INET, SOCK_DGRAM, extraSocketOptions ); - - if ( listenSocket == (SOCKET) -1 ) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "socket__(...) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - char str[512]; - strcpy(str,(const char*) messageBuffer); - //Free the buffer. - LocalFree( messageBuffer ); -#endif - - return (SOCKET) -1; - } - - SetSocketOptions(listenSocket); - - // Fill in the rest of the address structure - listenerSocketAddress.sin_family = AF_INET; - - if (forceHostAddress && forceHostAddress[0]) - { - // RAKNET_DEBUG_PRINTF("Force binding %s:%i\n", forceHostAddress, port); - - - - listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); - - } - else - { - // RAKNET_DEBUG_PRINTF("Binding any on port %i\n", port); - listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; - } - - - - - - - - - // bind our name to the socket - ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); - - if ( ret <= -1 ) - { - - - - - - - -#if defined(_WIN32) - DWORD dwIOError = GetLastError(); - if (dwIOError==10048) - { - if (sleepOn10048==0) - return (SOCKET) -1; - // Vista has a bug where it returns WSAEADDRINUSE (10048) if you create, shutdown, then rebind the socket port unless you wait a while first. - // Wait, then rebind - RakSleep(100); - - closesocket__(listenSocket); - listenerSocketAddress.sin_port = htons( port ); - listenSocket = socket__( AF_INET, SOCK_DGRAM, 0 ); - if ( listenSocket == (SOCKET) -1 ) - return false; - SetSocketOptions(listenSocket); - - // Fill in the rest of the address structure - listenerSocketAddress.sin_family = AF_INET; - if (forceHostAddress && forceHostAddress[0]) - { - - - - listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); - - } - else - listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; - - // bind our name to the socket - ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); - - if ( ret >= 0 ) - return listenSocket; - } - dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "bind__(...) failed:Error code - %d\n%s", (unsigned int) dwIOError, (char*) messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#elif (defined(__GNUC__) || defined(__GCCXML__) ) && !defined(_WIN32) - switch (ret) - { - case EBADF: - RAKNET_DEBUG_PRINTF("bind__(): sockfd is not a valid descriptor.\n"); break; - - case ENOTSOCK: - RAKNET_DEBUG_PRINTF("bind__(): Argument is a descriptor for a file, not a socket.\n"); break; - - case EINVAL: - RAKNET_DEBUG_PRINTF("bind__(): The addrlen is wrong, or the socket was not in the AF_UNIX family.\n"); break; - case EROFS: - RAKNET_DEBUG_PRINTF("bind__(): The socket inode would reside on a read-only file system.\n"); break; - case EFAULT: - RAKNET_DEBUG_PRINTF("bind__(): my_addr points outside the user's accessible address space.\n"); break; - case ENAMETOOLONG: - RAKNET_DEBUG_PRINTF("bind__(): my_addr is too long.\n"); break; - case ENOENT: - RAKNET_DEBUG_PRINTF("bind__(): The file does not exist.\n"); break; - case ENOMEM: - RAKNET_DEBUG_PRINTF("bind__(): Insufficient kernel memory was available.\n"); break; - case ENOTDIR: - RAKNET_DEBUG_PRINTF("bind__(): A component of the path prefix is not a directory.\n"); break; - case EACCES: - RAKNET_DEBUG_PRINTF("bind__(): Search permission is denied on a component of the path prefix.\n"); break; - - case ELOOP: - RAKNET_DEBUG_PRINTF("bind__(): Too many symbolic links were encountered in resolving my_addr.\n"); break; - - default: - RAKNET_DEBUG_PRINTF("Unknown bind__() error %i.\n", ret); break; - } -#endif - - return (SOCKET) -1; - } - - return listenSocket; -} -SOCKET SocketLayer::CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions, unsigned short socketFamily ) -{ - (void) blockingSocket; - (void) extraSocketOptions; - (void) socketFamily; - -#if RAKNET_SUPPORT_IPV6!=1 - return CreateBoundSocket_Old(port,blockingSocket,forceHostAddress,sleepOn10048,extraSocketOptions); -#else - -#ifdef _WIN32 - // Vista has a bug where it returns WSAEADDRINUSE (10048) if you create, shutdown, then rebind the socket port unless you wait a while first. - if (sleepOn10048==0) - RakSleep(100); -#endif - - int ret=0; - SOCKET listenSocket; - struct addrinfo hints; - struct addrinfo *servinfo=0, *aip; // will point to the results - PrepareAddrInfoHints(&hints); - hints.ai_family=socketFamily; - char portStr[32]; - Itoa(port,portStr,10); - - // On Ubuntu, "" returns "No address associated with hostname" while 0 works. - if (forceHostAddress && - (_stricmp(forceHostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || forceHostAddress[0]==0)) - { - getaddrinfo(0, portStr, &hints, &servinfo); - } - else - { - getaddrinfo(forceHostAddress, portStr, &hints, &servinfo); - } - - // Try all returned addresses until one works - for (aip = servinfo; aip != NULL; aip = aip->ai_next) - { - // Open socket. The address type depends on what - // getaddrinfo() gave us. - listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); - if (listenSocket != -1) - { - ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); - if (ret>=0) - { - // Is this valid? - sockaddr_in6 addr6; - memcpy(&addr6, aip->ai_addr, sizeof(addr6)); - - freeaddrinfo(servinfo); // free the linked-list - - SetSocketOptions(listenSocket); - return listenSocket; - } - } - } - -#if defined(_WIN32) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - - RAKNET_DEBUG_PRINTF( "bind__(...) failed:Error code - %d\n%s", (unsigned int) dwIOError, (char*) messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#elif defined(__GNUC__) || defined(__GCCXML__) && !defined(_WIN32) - switch (ret) - { - case EBADF: - RAKNET_DEBUG_PRINTF("bind__(): sockfd is not a valid descriptor.\n"); break; - - case ENOTSOCK: - RAKNET_DEBUG_PRINTF("bind__(): Argument is a descriptor for a file, not a socket.\n"); break; - - case EINVAL: - RAKNET_DEBUG_PRINTF("bind__(): The addrlen is wrong, or the socket was not in the AF_UNIX family.\n"); break; - case EROFS: - RAKNET_DEBUG_PRINTF("bind__(): The socket inode would reside on a read-only file system.\n"); break; - case EFAULT: - RAKNET_DEBUG_PRINTF("bind__(): my_addr points outside the user's accessible address space.\n"); break; - case ENAMETOOLONG: - RAKNET_DEBUG_PRINTF("bind__(): my_addr is too long.\n"); break; - case ENOENT: - RAKNET_DEBUG_PRINTF("bind__(): The file does not exist.\n"); break; - case ENOMEM: - RAKNET_DEBUG_PRINTF("bind__(): Insufficient kernel memory was available.\n"); break; - case ENOTDIR: - RAKNET_DEBUG_PRINTF("bind__(): A component of the path prefix is not a directory.\n"); break; - case EACCES: - RAKNET_DEBUG_PRINTF("bind__(): Search permission is denied on a component of the path prefix.\n"); break; - - case ELOOP: - RAKNET_DEBUG_PRINTF("bind__(): Too many symbolic links were encountered in resolving my_addr.\n"); break; - - default: - RAKNET_DEBUG_PRINTF("Unknown bind__() error %i.\n", ret); break; - } -#endif - - -#endif - - return (SOCKET) -1; -} -const char* SocketLayer::DomainNameToIP_Old( const char *domainName ) -{ - struct in_addr addr; - memset(&addr,0,sizeof(in_addr)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Use inet_addr instead? What is the difference? - struct hostent * phe = gethostbyname( domainName ); - - if ( phe == 0 || phe->h_addr_list[ 0 ] == 0 ) - { - //cerr << "Yow! Bad host lookup." << endl; - return 0; - } - - if (phe->h_addr_list[ 0 ]==0) - return 0; - - memcpy( &addr, phe->h_addr_list[ 0 ], sizeof( struct in_addr ) ); - return inet_ntoa( addr ); - - - return ""; -} -const char* SocketLayer::DomainNameToIP( const char *domainName ) -{ -#if RAKNET_SUPPORT_IPV6!=1 - return DomainNameToIP_Old(domainName); -#else - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - struct addrinfo hints, *res, *p; - int status; - static char ipstr[INET6_ADDRSTRLEN]; - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version - hints.ai_socktype = SOCK_DGRAM; - - if ((status = getaddrinfo(domainName, NULL, &hints, &res)) != 0) { - return 0; - } - - p=res; -// for(p = res;p != NULL; p = p->ai_next) { - void *addr; -// char *ipver; - - // get the pointer to the address itself, - // different fields in IPv4 and IPv6: - if (p->ai_family == AF_INET) - { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; - addr = &(ipv4->sin_addr); - strcpy(ipstr, inet_ntoa( ipv4->sin_addr )); - } - else - { - // TODO - test - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; - addr = &(ipv6->sin6_addr); - // inet_ntop function does not exist on windows - // http://www.mail-archive.com/users@ipv6.org/msg02107.html - getnameinfo((struct sockaddr *)ipv6, sizeof(struct sockaddr_in6), ipstr, 1, NULL, 0, NI_NUMERICHOST); - } - freeaddrinfo(res); // free the linked list - return ipstr; -// } - - - - return ""; - -#endif // #if RAKNET_SUPPORT_IPV6!=1 -} - - -void SocketLayer::Write( const SOCKET writeSocket, const char* data, const int length ) -{ -#ifdef _DEBUG - RakAssert( writeSocket != (SOCKET) -1 ); -#endif - - send__( writeSocket, data, length, 0 ); -} -void SocketLayer::RecvFromBlocking_Old( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ) -{ - (void) rakPeer; - - sockaddr* sockAddrPtr; - socklen_t sockLen; - socklen_t* socketlenPtr=(socklen_t*) &sockLen; - sockaddr_in sa; - memset(&sa,0,sizeof(sockaddr_in)); - int dataOutSize; - const int flag=0; - - (void) remotePortRakNetWasStartedOn_PS3; - (void) extraSocketOptions; - - - - - - - - - - - - - - - - - - - - - { - sockLen=sizeof(sa); - sa.sin_family = AF_INET; - sa.sin_port=0; - sockAddrPtr=(sockaddr*) &sa; - } - - - - - dataOutSize=MAXIMUM_MTU_SIZE; - - - *bytesReadOut = recvfrom__( s, dataOut, dataOutSize, flag, sockAddrPtr, socketlenPtr ); - - - - - - - - - - if (*bytesReadOut<=0) - return; - *timeRead=RakNet::GetTimeUS(); - - - - - - - - - - { - systemAddressOut->SetPortNetworkOrder( sa.sin_port ); - systemAddressOut->address.addr4.sin_addr.s_addr=sa.sin_addr.s_addr; - } -} - -void SocketLayer::RecvFromBlocking( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ) -{ -#if RAKNET_SUPPORT_IPV6!=1 - RecvFromBlocking_Old(s,rakPeer,remotePortRakNetWasStartedOn_PS3,extraSocketOptions,dataOut,bytesReadOut,systemAddressOut,timeRead); -#else - (void) rakPeer; - sockaddr_storage their_addr; - sockaddr* sockAddrPtr; - socklen_t sockLen; - socklen_t* socketlenPtr=(socklen_t*) &sockLen; - memset(&their_addr,0,sizeof(their_addr)); - int dataOutSize; - const int flag=0; - - (void) remotePortRakNetWasStartedOn_PS3; - (void) extraSocketOptions; - - - - - - - - - - - - { - sockLen=sizeof(their_addr); - sockAddrPtr=(sockaddr*) &their_addr; - } - - - - - dataOutSize=MAXIMUM_MTU_SIZE; - - - *bytesReadOut = recvfrom__( s, dataOut, dataOutSize, flag, sockAddrPtr, socketlenPtr ); - - - - - - - - - - if (*bytesReadOut<=0) - return; - *timeRead=RakNet::GetTimeUS(); - - - - - - - - - - { - if (their_addr.ss_family==AF_INET) - { - memcpy(&systemAddressOut->address.addr4,(sockaddr_in *)&their_addr,sizeof(sockaddr_in)); - systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr4.sin_port); - // systemAddressOut->address.addr4.sin_port=ntohs( systemAddressOut->address.addr4.sin_port ); - } - else - { - memcpy(&systemAddressOut->address.addr6,(sockaddr_in6 *)&their_addr,sizeof(sockaddr_in6)); - systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr6.sin6_port); - // systemAddressOut->address.addr6.sin6_port=ntohs( systemAddressOut->address.addr6.sin6_port ); - } - } - -#endif // defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) -} - -int SocketLayer::SendTo_PS3Lobby( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ) -{ - (void) s; - (void) data; - (void) length; - (void) remotePortRakNetWasStartedOn_PS3; - (void) systemAddress; - - int len=0; - - - - - - - - - - - - - - - return len; -} -int SocketLayer::SendTo_PSP2( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ) -{ - (void) s; - (void) data; - (void) length; - (void) remotePortRakNetWasStartedOn_PS3; - (void) systemAddress; - - int len=0; - - - - - - - - - - - - - - return len; -} -int SocketLayer::SendTo_360( SOCKET s, const char *data, int length, const char *voiceData, int voiceLength, const SystemAddress &systemAddress, unsigned int extraSocketOptions ) -{ - (void) s; - (void) data; - (void) length; - (void) voiceData; - (void) voiceLength; - (void) extraSocketOptions; - (void) systemAddress; - - int len=0; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - return len; -} -int SocketLayer::SendTo_PC( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, const char *file, const long line ) -{ - // TODO - // http://www.linuxquestions.org/questions/linux-software-2/ipv6-linux-sendto-problems-519485/ - -// #if RAKNET_SUPPORT_IPV6==1 -// RakAssert( -// systemAddress.address.addr4.sin_family!=AF_MAX && -// (systemAddress.address.addr4.sin_family==AF_INET || (systemAddress.address.addr6.sin6_scope_id!=0)) -// ); -// #endif - - /* - sockaddr_in sa; - memset(&sa,0,sizeof(sockaddr_in)); - sa.sin_port = htons( port ); // User port - sa.sin_addr.s_addr = binaryAddress; - sa.sin_family = socketFamily; - */ - - int len=0; - do - { -#ifdef DEBUG_SENDTO_SPIKES - RakNetTime start = RakNet::GetTime(); -#else - (void) file; - (void) line; -#endif - if (systemAddress.address.addr4.sin_family==AF_INET) - { - //systemAddress.address.addr4.sin_port=htons(systemAddress.address.addr4.sin_port); - len = sendto__( s, data, length, 0, ( const sockaddr* ) & systemAddress.address.addr4, sizeof( sockaddr_in ) ); - //systemAddress.address.addr4.sin_port=ntohs(systemAddress.address.addr4.sin_port); - } - else - { -#if RAKNET_SUPPORT_IPV6==1 - // systemAddress.address.addr6.sin6_port=htons(systemAddress.address.addr6.sin6_port); - len = sendto__( s, data, length, 0, ( const sockaddr* ) & systemAddress.address.addr6, sizeof( sockaddr_in6 ) ); - //systemAddress.address.addr6.sin6_port=ntohs(systemAddress.address.addr6.sin6_port); -#endif - } - -#ifdef DEBUG_SENDTO_SPIKES - RakNetTime end = RakNet::GetTime(); - static unsigned int callCount=1; - RAKNET_DEBUG_PRINTF("%i. SendTo_PC, time=%llu, elapsed=%llu, length=%i, returned=%i, binaryAddress=%i, port=%i, file=%s, line=%i\n", callCount++, end, end-start, length, len, binaryAddress, port, file, line); -#endif - if (len<0) - { - RAKNET_DEBUG_PRINTF("sendto failed with code %i for char %i and length %i.\n", len, data[0], length); - } - } - while ( len == 0 ); - return len; -} - -#ifdef _MSC_VER -#pragma warning( disable : 4702 ) // warning C4702: unreachable code -#endif -int SocketLayer::SendTo( SOCKET s, const char *data, int length, SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ) -{ - (void) extraSocketOptions; - - int len=0; - RakAssert(length<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); -#if !defined(__S3E__) - RakAssert(systemAddress.address.addr4.sin_family!=AF_MAX); -#endif - - if (slo) - { - len = slo->RakNetSendTo(s,data,length,systemAddress); - if ( len != -1 ) - return 0; - return 1; - } - - if ( s == (SOCKET) -1 ) - { - return -1; - } - - - if (remotePortRakNetWasStartedOn_PS3!=0) - { - - - - - - } - else - { - - - - - - - len = SendTo_PC(s,data,length,systemAddress,file,line); - - } - - if ( len != -1 ) - return 0; - -#if defined(_WIN32) && !defined(_WIN32_WCE) - - DWORD dwIOError = WSAGetLastError(); - - if ( dwIOError == WSAECONNRESET ) - { -#if defined(_DEBUG) - RAKNET_DEBUG_PRINTF( "A previous send operation resulted in an ICMP Port Unreachable message.\n" ); -#endif - - } - else if ( dwIOError != WSAEWOULDBLOCK && dwIOError != WSAEADDRNOTAVAIL) - { -#if defined(_WIN32) && defined(_DEBUG) - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "sendto failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); -#endif - - } - - return dwIOError; -#endif - - return 1; // error -} -// Not enough info for IPV6 -// int SocketLayer::SendTo( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ) -// { -// SystemAddress systemAddress; -// systemAddress.FromStringAndPort(ip,port); -// return SendTo( s, data, length, systemAddress,remotePortRakNetWasStartedOn_PS3, extraSocketOptions, file, line ); -// } -int SocketLayer::SendToTTL( SOCKET s, const char *data, int length, SystemAddress &systemAddress, int ttl ) -{ - if (slo) - return slo->RakNetSendTo(s,data,length,systemAddress); - - - int oldTTL; - socklen_t opLen=sizeof(oldTTL); - // Get the current TTL - if (getsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & oldTTL, &opLen ) == -1) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "getsockopt__(IPPROTO_IP,IP_TTL) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#endif - } - - // Set to TTL - int newTTL=ttl; - if (setsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & newTTL, sizeof ( newTTL ) ) == -1) - { - -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "setsockopt__(IPPROTO_IP,IP_TTL) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#endif - } - - // Send - int res = SendTo(s,data,length,systemAddress,0,0, __FILE__, __LINE__ ); - - // Restore the old TTL - setsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & oldTTL, opLen ); - - return res; - - - -} - - -RakNet::RakString SocketLayer::GetSubNetForSocketAndIp(SOCKET inSock, RakNet::RakString inIpString) -{ - RakNet::RakString netMaskString; - RakNet::RakString ipString; - - - - - -#if defined(_WIN32) - INTERFACE_INFO InterfaceList[20]; - unsigned long nBytesReturned; - if (WSAIoctl(inSock, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList, - sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR) { - return ""; - } - - int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO); - - for (int i = 0; i < nNumInterfaces; ++i) - { - sockaddr_in *pAddress; - pAddress = (sockaddr_in *) & (InterfaceList[i].iiAddress); - ipString=inet_ntoa(pAddress->sin_addr); - - if (inIpString==ipString) - { - pAddress = (sockaddr_in *) & (InterfaceList[i].iiNetmask); - netMaskString=inet_ntoa(pAddress->sin_addr); - return netMaskString; - } - } - return ""; -#else - - int fd,fd2; - fd2 = socket__(AF_INET, SOCK_DGRAM, 0); - - if(fd2 < 0) - { - return ""; - } - - struct ifconf ifc; - char buf[1999]; - ifc.ifc_len = sizeof(buf); - ifc.ifc_buf = buf; - if(ioctl(fd2, SIOCGIFCONF, &ifc) < 0) - { - return ""; - } - - struct ifreq *ifr; - ifr = ifc.ifc_req; - int intNum = ifc.ifc_len / sizeof(struct ifreq); - for(int i = 0; i < intNum; i++) - { - ipString=inet_ntoa(((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr); - - if (inIpString==ipString) - { - struct ifreq ifr2; - fd = socket__(AF_INET, SOCK_DGRAM, 0); - if(fd < 0) - { - return ""; - } - ifr2.ifr_addr.sa_family = AF_INET; - - strncpy(ifr2.ifr_name, ifr[i].ifr_name, IFNAMSIZ-1); - - ioctl(fd, SIOCGIFNETMASK, &ifr2); - - close(fd); - close(fd2); - netMaskString=inet_ntoa(((struct sockaddr_in *)&ifr2.ifr_addr)->sin_addr); - - return netMaskString; - } - } - - close(fd2); - return ""; - -#endif - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void GetMyIP_Win32( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) -{ - char ac[ 80 ]; - if ( gethostname( ac, sizeof( ac ) ) == -1 ) - { - #if defined(_WIN32) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "gethostname failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); - #endif - return ; - } - - -#if RAKNET_SUPPORT_IPV6==1 - struct addrinfo hints; - struct addrinfo *servinfo=0, *aip; // will point to the results - PrepareAddrInfoHints(&hints); - getaddrinfo(ac, "", &hints, &servinfo); - - int idx; - for (idx=0, aip = servinfo; aip != NULL && idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; aip = aip->ai_next, idx++) - { - if (aip->ai_family == AF_INET) - { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)aip->ai_addr; - memcpy(&addresses[idx].address.addr4,ipv4,sizeof(sockaddr_in)); - } - else - { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)aip->ai_addr; - memcpy(&addresses[idx].address.addr4,ipv6,sizeof(sockaddr_in6)); - } - - } - - freeaddrinfo(servinfo); // free the linked-list -#else - struct hostent *phe = gethostbyname( ac ); - - if ( phe == 0 ) - { - #ifdef _WIN32 - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "gethostbyname failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); - #endif - return ; - } - int idx; - for ( idx = 0; idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ++idx ) - { - if (phe->h_addr_list[ idx ] == 0) - break; - - memcpy(&addresses[idx].address.addr4.sin_addr,phe->h_addr_list[ idx ],sizeof(struct in_addr)); - - } -#endif - - - while (idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS) - { - addresses[idx]=UNASSIGNED_SYSTEM_ADDRESS; - idx++; - } -} -// #else -/* -void GetMyIP_Linux( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) -{ - struct ifaddrs *ifaddr, *ifa; - int family, s; - char host[NI_MAXHOST]; - struct in_addr linux_in_addr; - - if (getifaddrs(&ifaddr) == -1) { - printf( "Error getting interface list\n"); - } - - int idx = 0; - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) continue; - family = ifa->ifa_addr->sa_family; - - if (family == AF_INET) { - s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - if (s != 0) { - // printf ("getnameinfo() failed: %s\n", gai_strerror(s)); - } - else if (strcmp(host,"127.0.0.1")!=0) - { - if (inet_aton(host, &addresses[idx].address.addr4.sin_addr)!=0) - idx++; - } - } -#if RAKNET_SUPPORT_IPV6==1 - else - { - s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - if (s != 0) { - // printf ("getnameinfo() failed: %s\n", gai_strerror(s)); - } - else if (strcmp(host,"::1")!=0) - { - if (inet_pton(family, host, &addresses[idx].address.addr6.sin6_addr)!=0) - idx++; - } - } -#endif - } - - for ( ; idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ++idx ) - { - addresses[idx]=UNASSIGNED_SYSTEM_ADDRESS; - } - - freeifaddrs(ifaddr); -} -*/ - - - -void SocketLayer::GetMyIP( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) -{ - - - - -#if defined(_WIN32) - GetMyIP_Win32(addresses); -#else -// GetMyIP_Linux(addresses); - GetMyIP_Win32(addresses); -#endif -} - - -unsigned short SocketLayer::GetLocalPort(SOCKET s) -{ - SystemAddress sa; - GetSystemAddress(s,&sa); - return sa.GetPort(); -} -void SocketLayer::GetSystemAddress_Old ( SOCKET s, SystemAddress *systemAddressOut ) -{ - sockaddr_in sa; - memset(&sa,0,sizeof(sockaddr_in)); - socklen_t len = sizeof(sa); - if (getsockname__(s, (sockaddr*)&sa, &len)!=0) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "getsockname failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); -#endif - *systemAddressOut = UNASSIGNED_SYSTEM_ADDRESS; - return; - } - - systemAddressOut->SetPortNetworkOrder(sa.sin_port); - systemAddressOut->address.addr4.sin_addr.s_addr=sa.sin_addr.s_addr; -} -void SocketLayer::GetSystemAddress ( SOCKET s, SystemAddress *systemAddressOut ) -{ -#if RAKNET_SUPPORT_IPV6!=1 - GetSystemAddress_Old(s,systemAddressOut); -#else - socklen_t slen; - sockaddr_storage ss; - slen = sizeof(ss); - - if (getsockname__(s, (struct sockaddr *)&ss, &slen)!=0) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "getsockname failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); -#endif - systemAddressOut->FromString(0); - return; - } - - if (ss.ss_family==AF_INET) - { - memcpy(&systemAddressOut->address.addr4,(sockaddr_in *)&ss,sizeof(sockaddr_in)); - systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr4.sin_port); - - uint32_t zero = 0; - if (memcmp(&systemAddressOut->address.addr4.sin_addr.s_addr, &zero, sizeof(zero))==0) - systemAddressOut->SetToLoopback(4); - // systemAddressOut->address.addr4.sin_port=ntohs(systemAddressOut->address.addr4.sin_port); - } - else - { - memcpy(&systemAddressOut->address.addr6,(sockaddr_in6 *)&ss,sizeof(sockaddr_in6)); - systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr6.sin6_port); - - char zero[16]; - memset(zero,0,sizeof(zero)); - if (memcmp(&systemAddressOut->address.addr4.sin_addr.s_addr, &zero, sizeof(zero))==0) - systemAddressOut->SetToLoopback(6); - - // systemAddressOut->address.addr6.sin6_port=ntohs(systemAddressOut->address.addr6.sin6_port); - } -#endif // #if RAKNET_SUPPORT_IPV6!=1 -} - -void SocketLayer::SetSocketLayerOverride(SocketLayerOverride *_slo) -{ - slo=_slo; -} - -bool SocketLayer::GetFirstBindableIP(char firstBindable[128], int ipProto) -{ - SystemAddress ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ]; - SocketLayer::GetMyIP( ipList ); - - - if (ipProto==AF_UNSPEC) - - { - ipList[0].ToString(false,firstBindable); - return true; - } - - // Find the first valid host address - unsigned int l; - for (l=0; l < MAXIMUM_NUMBER_OF_INTERNAL_IDS; l++) - { - if (ipList[l]==UNASSIGNED_SYSTEM_ADDRESS) - break; - if (ipList[l].GetIPVersion()==4 && ipProto==AF_INET) - break; - if (ipList[l].GetIPVersion()==6 && ipProto==AF_INET6) - break; - } - - if (ipList[l]==UNASSIGNED_SYSTEM_ADDRESS || l==MAXIMUM_NUMBER_OF_INTERNAL_IDS) - return false; -// RAKNET_DEBUG_PRINTF("%i %i %i %i\n", -// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[0], -// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[1], -// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[2], -// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[3] -// ); - ipList[l].ToString(false,firstBindable); - return true; - -} - - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +/// \file +/// \brief SocketLayer class implementation +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "SocketLayer.h" +#include "RakAssert.h" +#include "RakNetTypes.h" +#include "GetTime.h" +#include "LinuxStrings.h" +#include "SocketDefines.h" + +using namespace RakNet; + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 +#include "CCRakNetUDT.h" +#else +#include "CCRakNetSlidingWindow.h" +#endif + +SocketLayerOverride *SocketLayer::slo=0; + +#ifdef _WIN32 +#else +#include // memcpy +#include +#include +#include +#include // error numbers +#include // RAKNET_DEBUG_PRINTF +#if !defined(ANDROID) +#include +#endif +#include +#include +#include +#include +#include + +#endif + + + + + + + + + + + + + +#if defined(_WIN32) +#include "WSAStartupSingleton.h" +#include // 'IP_DONTFRAGMENT' 'IP_TTL' +#elif defined SN_TARGET_PSP2 +#else +#include +#endif + +#include "RakSleep.h" +#include +#include "Itoa.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +namespace RakNet +{ + extern void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead ); + extern void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead ); +} + +#ifdef _DEBUG +#include +#endif + +// http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#ip4to6 +// http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#getaddrinfo + +#if RAKNET_SUPPORT_IPV6==1 +void PrepareAddrInfoHints(addrinfo *hints) +{ + memset(hints, 0, sizeof (addrinfo)); // make sure the struct is empty + hints->ai_socktype = SOCK_DGRAM; // UDP sockets + hints->ai_flags = AI_PASSIVE; // fill in my IP for me +} +#endif + +// Frogwares: Define this +// #define DEBUG_SENDTO_SPIKES + +bool SocketLayer::IsPortInUse_Old(unsigned short port, const char *hostAddress) +{ + SOCKET listenSocket; + sockaddr_in listenerSocketAddress; + memset(&listenerSocketAddress,0,sizeof(sockaddr_in)); + // Listen on our designated Port# + listenerSocketAddress.sin_port = htons( port ); + listenSocket = socket__( AF_INET, SOCK_DGRAM, 0 ); + if ( listenSocket == (SOCKET) -1 ) + return true; + // bind our name to the socket + // Fill in the rest of the address structure + listenerSocketAddress.sin_family = AF_INET; + + if ( hostAddress && hostAddress[0] ) + { + + + + listenerSocketAddress.sin_addr.s_addr = inet_addr__( hostAddress ); + + } + else + listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; + + + + + + + + int ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); + closesocket__(listenSocket); + + + + + // #if defined(_DEBUG) + // if (ret == -1) + // perror("Failed to bind to address:"); + // #endif + return ret <= -1; + +} +bool SocketLayer::IsSocketFamilySupported(const char *hostAddress, unsigned short socketFamily) +{ + (void) hostAddress; + (void) socketFamily; + +#if RAKNET_SUPPORT_IPV6!=1 + return socketFamily==AF_INET; +#else + struct addrinfo hints; +#if RAKNET_SUPPORT_IPV6==1 + PrepareAddrInfoHints(&hints); +#endif + hints.ai_family = socketFamily; + struct addrinfo *servinfo=0; + int error; + // On Ubuntu, "" returns "No address associated with hostname" while 0 works. + if (hostAddress && + (_stricmp(hostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || hostAddress[0]==0)) + { + getaddrinfo(0, "0", &hints, &servinfo); + } + else + { + getaddrinfo(hostAddress, "0", &hints, &servinfo); + } + + + (void) error; + if (servinfo) + { + freeaddrinfo(servinfo); + return true; + } + else + { +#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_WIN32) + printf("IsSocketFamilySupported failed. hostAddress=%s. %s\n", hostAddress, gai_strerror(error)); +#endif + } + return false; +#endif +} +bool SocketLayer::IsPortInUse(unsigned short port, const char *hostAddress, unsigned short socketFamily) +{ +#if RAKNET_SUPPORT_IPV6!=1 + (void) socketFamily; + return IsPortInUse_Old(port, hostAddress); +#else + SOCKET listenSocket; + struct addrinfo hints; + struct addrinfo *servinfo=0, *aip; // will point to the results + PrepareAddrInfoHints(&hints); + hints.ai_family = socketFamily; + char portStr[32]; + Itoa(port,portStr,10); + + // On Ubuntu, "" returns "No address associated with hostname" while 0 works. + if (hostAddress && + (_stricmp(hostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || hostAddress[0]==0)) + { + getaddrinfo(0, portStr, &hints, &servinfo); + } + else + { + getaddrinfo(hostAddress, portStr, &hints, &servinfo); + } + + // Try all returned addresses until one works + for (aip = servinfo; aip != NULL; aip = aip->ai_next) + { + // Open socket. The address type depends on what + // getaddrinfo() gave us. + listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (listenSocket != -1) + { + int ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); + closesocket__(listenSocket); + if (ret>=0) + { + freeaddrinfo(servinfo); // free the linked-list + return false; + } + } + + // If the user didn't specify which host address, then only apply the first + if (hostAddress==0 || hostAddress[0]==0) + break; + } + + freeaddrinfo(servinfo); // free the linked-list + return true; +#endif // #if RAKNET_SUPPORT_IPV6!=1 +} +void SocketLayer::SetDoNotFragment( SOCKET listenSocket, int opt, int IPPROTO ) +{ +#if defined(IP_DONTFRAGMENT ) + +#if defined(_WIN32) && defined(_DEBUG) + // If this assert hit you improperly linked against WSock32.h + RakAssert(IP_DONTFRAGMENT==14); +#endif + + if ( setsockopt__( listenSocket, IPPROTO, IP_DONTFRAGMENT, ( char * ) & opt, sizeof ( opt ) ) == -1 ) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // I see this hit on XP with IPV6 for some reason + RAKNET_DEBUG_PRINTF( "Warning: setsockopt__(IP_DONTFRAGMENT) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + LocalFree( messageBuffer ); +#endif + } +#endif +} + +void SocketLayer::SetNonBlocking( SOCKET listenSocket) +{ +#ifdef _WIN32 + unsigned long nonBlocking = 1; + ioctlsocket__( listenSocket, FIONBIO, &nonBlocking ); + + + +#else + int flags = fcntl(listenSocket, F_GETFL, 0); + fcntl(listenSocket, F_SETFL, flags | O_NONBLOCK); +#endif +} + +void SocketLayer::SetSocketOptions( SOCKET listenSocket) +{ + int sock_opt = 1; + // // On Vista, can get WSAEACCESS (10013) + /* + if ( setsockopt__( listenSocket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & sock_opt, sizeof ( sock_opt ) ) == -1 ) + { + #if defined(_WIN32) && !defined(_XBOX) && defined(_DEBUG) && !defined(X360) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "setsockopt__(SO_REUSEADDR) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); + #endif + } + */ + + // This doubles the max throughput rate + sock_opt=1024*256; + setsockopt__(listenSocket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + // Immediate hard close. Don't linger the socket, or recreating the socket quickly on Vista fails. + // Fail with voice and xbox + + sock_opt=0; + setsockopt__(listenSocket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + + + // This doesn't make much difference: 10% maybe + // Not supported on console 2 + sock_opt=1024*16; + setsockopt__(listenSocket, SOL_SOCKET, SO_SNDBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + +#ifdef __APPLE__ + // Preventing SEGPIPE on "broken" socket + int set = 1; + setsockopt__(listenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); +#endif + + /* + #ifdef _WIN32 + unsigned long nonblocking = 1; + ioctlsocket__( listenSocket, FIONBIO, &nonblocking ); + #elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) + sock_opt=1; + setsockopt__(listenSocket, SOL_SOCKET, SO_NBIO, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + #else + fcntl( listenSocket, F_SETFL, O_NONBLOCK ); + #endif + */ + + // TODO - teston ipv6 + + // Note: Fails with VDP but not xbox + // Set broadcast capable + sock_opt=1; + if ( setsockopt__( listenSocket, SOL_SOCKET, SO_BROADCAST, ( char * ) & sock_opt, sizeof( sock_opt ) ) == -1 ) + { +#if defined(_WIN32) && defined(_DEBUG) + + DWORD dwIOError = GetLastError(); + // On Vista, can get WSAEACCESS (10013) + // See http://support.microsoft.com/kb/819124 + // http://blogs.msdn.com/wndp/archive/2007/03/19/winsock-so-exclusiveaddruse-on-vista.aspx + // http://msdn.microsoft.com/en-us/library/ms740621(VS.85).aspx + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "setsockopt__(SO_BROADCAST) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); + +#endif + + } +} +SOCKET SocketLayer::CreateBoundSocket_PS3Lobby( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ) +{ + (void) port; + (void) blockingSocket; + (void) forceHostAddress; + (void) socketFamily; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + return 0; + +} +SOCKET SocketLayer::CreateBoundSocket_PSP2( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ) +{ + (void) port; + (void) blockingSocket; + (void) forceHostAddress; + (void) socketFamily; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + return 0; + +} +SOCKET SocketLayer::CreateBoundSocket_Old( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions ) +{ + (void) blockingSocket; + + int ret; + SOCKET listenSocket; + sockaddr_in listenerSocketAddress; + memset(&listenerSocketAddress,0,sizeof(sockaddr_in)); + // Listen on our designated Port# + listenerSocketAddress.sin_port = htons( port ); + + listenSocket = socket__( AF_INET, SOCK_DGRAM, extraSocketOptions ); + + if ( listenSocket == (SOCKET) -1 ) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "socket__(...) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + char str[512]; + strcpy(str,(const char*) messageBuffer); + //Free the buffer. + LocalFree( messageBuffer ); +#endif + + return (SOCKET) -1; + } + + SetSocketOptions(listenSocket); + + // Fill in the rest of the address structure + listenerSocketAddress.sin_family = AF_INET; + + if (forceHostAddress && forceHostAddress[0]) + { + // RAKNET_DEBUG_PRINTF("Force binding %s:%i\n", forceHostAddress, port); + + + + listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); + + } + else + { + // RAKNET_DEBUG_PRINTF("Binding any on port %i\n", port); + listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; + } + + + + + + + + + // bind our name to the socket + ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); + + if ( ret <= -1 ) + { + + + + + + + +#if defined(_WIN32) + DWORD dwIOError = GetLastError(); + if (dwIOError==10048) + { + if (sleepOn10048==0) + return (SOCKET) -1; + // Vista has a bug where it returns WSAEADDRINUSE (10048) if you create, shutdown, then rebind the socket port unless you wait a while first. + // Wait, then rebind + RakSleep(100); + + closesocket__(listenSocket); + listenerSocketAddress.sin_port = htons( port ); + listenSocket = socket__( AF_INET, SOCK_DGRAM, 0 ); + if ( listenSocket == (SOCKET) -1 ) + return false; + SetSocketOptions(listenSocket); + + // Fill in the rest of the address structure + listenerSocketAddress.sin_family = AF_INET; + if (forceHostAddress && forceHostAddress[0]) + { + + + + listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); + + } + else + listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; + + // bind our name to the socket + ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); + + if ( ret >= 0 ) + return listenSocket; + } + dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "bind__(...) failed:Error code - %d\n%s", (unsigned int) dwIOError, (char*) messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#elif (defined(__GNUC__) || defined(__GCCXML__) ) && !defined(_WIN32) + switch (ret) + { + case EBADF: + RAKNET_DEBUG_PRINTF("bind__(): sockfd is not a valid descriptor.\n"); break; + + case ENOTSOCK: + RAKNET_DEBUG_PRINTF("bind__(): Argument is a descriptor for a file, not a socket.\n"); break; + + case EINVAL: + RAKNET_DEBUG_PRINTF("bind__(): The addrlen is wrong, or the socket was not in the AF_UNIX family.\n"); break; + case EROFS: + RAKNET_DEBUG_PRINTF("bind__(): The socket inode would reside on a read-only file system.\n"); break; + case EFAULT: + RAKNET_DEBUG_PRINTF("bind__(): my_addr points outside the user's accessible address space.\n"); break; + case ENAMETOOLONG: + RAKNET_DEBUG_PRINTF("bind__(): my_addr is too long.\n"); break; + case ENOENT: + RAKNET_DEBUG_PRINTF("bind__(): The file does not exist.\n"); break; + case ENOMEM: + RAKNET_DEBUG_PRINTF("bind__(): Insufficient kernel memory was available.\n"); break; + case ENOTDIR: + RAKNET_DEBUG_PRINTF("bind__(): A component of the path prefix is not a directory.\n"); break; + case EACCES: + RAKNET_DEBUG_PRINTF("bind__(): Search permission is denied on a component of the path prefix.\n"); break; + + case ELOOP: + RAKNET_DEBUG_PRINTF("bind__(): Too many symbolic links were encountered in resolving my_addr.\n"); break; + + default: + RAKNET_DEBUG_PRINTF("Unknown bind__() error %i.\n", ret); break; + } +#endif + + return (SOCKET) -1; + } + + return listenSocket; +} +SOCKET SocketLayer::CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions, unsigned short socketFamily ) +{ + (void) blockingSocket; + (void) extraSocketOptions; + (void) socketFamily; + +#if RAKNET_SUPPORT_IPV6!=1 + return CreateBoundSocket_Old(port,blockingSocket,forceHostAddress,sleepOn10048,extraSocketOptions); +#else + +#ifdef _WIN32 + // Vista has a bug where it returns WSAEADDRINUSE (10048) if you create, shutdown, then rebind the socket port unless you wait a while first. + if (sleepOn10048==0) + RakSleep(100); +#endif + + int ret=0; + SOCKET listenSocket; + struct addrinfo hints; + struct addrinfo *servinfo=0, *aip; // will point to the results + PrepareAddrInfoHints(&hints); + hints.ai_family=socketFamily; + char portStr[32]; + Itoa(port,portStr,10); + + // On Ubuntu, "" returns "No address associated with hostname" while 0 works. + if (forceHostAddress && + (_stricmp(forceHostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || forceHostAddress[0]==0)) + { + getaddrinfo(0, portStr, &hints, &servinfo); + } + else + { + getaddrinfo(forceHostAddress, portStr, &hints, &servinfo); + } + + // Try all returned addresses until one works + for (aip = servinfo; aip != NULL; aip = aip->ai_next) + { + // Open socket. The address type depends on what + // getaddrinfo() gave us. + listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (listenSocket != -1) + { + ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); + if (ret>=0) + { + // Is this valid? + sockaddr_in6 addr6; + memcpy(&addr6, aip->ai_addr, sizeof(addr6)); + + freeaddrinfo(servinfo); // free the linked-list + + SetSocketOptions(listenSocket); + return listenSocket; + } + } + } + +#if defined(_WIN32) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + + RAKNET_DEBUG_PRINTF( "bind__(...) failed:Error code - %d\n%s", (unsigned int) dwIOError, (char*) messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#elif defined(__GNUC__) || defined(__GCCXML__) && !defined(_WIN32) + switch (ret) + { + case EBADF: + RAKNET_DEBUG_PRINTF("bind__(): sockfd is not a valid descriptor.\n"); break; + + case ENOTSOCK: + RAKNET_DEBUG_PRINTF("bind__(): Argument is a descriptor for a file, not a socket.\n"); break; + + case EINVAL: + RAKNET_DEBUG_PRINTF("bind__(): The addrlen is wrong, or the socket was not in the AF_UNIX family.\n"); break; + case EROFS: + RAKNET_DEBUG_PRINTF("bind__(): The socket inode would reside on a read-only file system.\n"); break; + case EFAULT: + RAKNET_DEBUG_PRINTF("bind__(): my_addr points outside the user's accessible address space.\n"); break; + case ENAMETOOLONG: + RAKNET_DEBUG_PRINTF("bind__(): my_addr is too long.\n"); break; + case ENOENT: + RAKNET_DEBUG_PRINTF("bind__(): The file does not exist.\n"); break; + case ENOMEM: + RAKNET_DEBUG_PRINTF("bind__(): Insufficient kernel memory was available.\n"); break; + case ENOTDIR: + RAKNET_DEBUG_PRINTF("bind__(): A component of the path prefix is not a directory.\n"); break; + case EACCES: + RAKNET_DEBUG_PRINTF("bind__(): Search permission is denied on a component of the path prefix.\n"); break; + + case ELOOP: + RAKNET_DEBUG_PRINTF("bind__(): Too many symbolic links were encountered in resolving my_addr.\n"); break; + + default: + RAKNET_DEBUG_PRINTF("Unknown bind__() error %i.\n", ret); break; + } +#endif + + +#endif + + return (SOCKET) -1; +} +const char* SocketLayer::DomainNameToIP_Old( const char *domainName ) +{ + struct in_addr addr; + memset(&addr,0,sizeof(in_addr)); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + // Use inet_addr instead? What is the difference? + struct hostent * phe = gethostbyname( domainName ); + + if ( phe == 0 || phe->h_addr_list[ 0 ] == 0 ) + { + //cerr << "Yow! Bad host lookup." << endl; + return 0; + } + + if (phe->h_addr_list[ 0 ]==0) + return 0; + + memcpy( &addr, phe->h_addr_list[ 0 ], sizeof( struct in_addr ) ); + return inet_ntoa( addr ); + + + return ""; +} +const char* SocketLayer::DomainNameToIP( const char *domainName ) +{ +#if RAKNET_SUPPORT_IPV6!=1 + return DomainNameToIP_Old(domainName); +#else + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + struct addrinfo hints, *res, *p; + int status; + static char ipstr[INET6_ADDRSTRLEN]; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version + hints.ai_socktype = SOCK_DGRAM; + + if ((status = getaddrinfo(domainName, NULL, &hints, &res)) != 0) { + return 0; + } + + p=res; +// for(p = res;p != NULL; p = p->ai_next) { + void *addr; +// char *ipver; + + // get the pointer to the address itself, + // different fields in IPv4 and IPv6: + if (p->ai_family == AF_INET) + { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + addr = &(ipv4->sin_addr); + strcpy(ipstr, inet_ntoa( ipv4->sin_addr )); + } + else + { + // TODO - test + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + addr = &(ipv6->sin6_addr); + // inet_ntop function does not exist on windows + // http://www.mail-archive.com/users@ipv6.org/msg02107.html + getnameinfo((struct sockaddr *)ipv6, sizeof(struct sockaddr_in6), ipstr, 1, NULL, 0, NI_NUMERICHOST); + } + freeaddrinfo(res); // free the linked list + return ipstr; +// } + + + + return ""; + +#endif // #if RAKNET_SUPPORT_IPV6!=1 +} + + +void SocketLayer::Write( const SOCKET writeSocket, const char* data, const int length ) +{ +#ifdef _DEBUG + RakAssert( writeSocket != (SOCKET) -1 ); +#endif + + send__( writeSocket, data, length, 0 ); +} +void SocketLayer::RecvFromBlocking_Old( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ) +{ + (void) rakPeer; + + sockaddr* sockAddrPtr; + socklen_t sockLen; + socklen_t* socketlenPtr=(socklen_t*) &sockLen; + sockaddr_in sa; + memset(&sa,0,sizeof(sockaddr_in)); + int dataOutSize; + const int flag=0; + + (void) remotePortRakNetWasStartedOn_PS3; + (void) extraSocketOptions; + + + + + + + + + + + + + + + + + + + + + { + sockLen=sizeof(sa); + sa.sin_family = AF_INET; + sa.sin_port=0; + sockAddrPtr=(sockaddr*) &sa; + } + + + + + dataOutSize=MAXIMUM_MTU_SIZE; + + + *bytesReadOut = recvfrom__( s, dataOut, dataOutSize, flag, sockAddrPtr, socketlenPtr ); + + + + + + + + + + if (*bytesReadOut<=0) + return; + *timeRead=RakNet::GetTimeUS(); + + + + + + + + + + { + systemAddressOut->SetPortNetworkOrder( sa.sin_port ); + systemAddressOut->address.addr4.sin_addr.s_addr=sa.sin_addr.s_addr; + } +} + +void SocketLayer::RecvFromBlocking( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ) +{ +#if RAKNET_SUPPORT_IPV6!=1 + RecvFromBlocking_Old(s,rakPeer,remotePortRakNetWasStartedOn_PS3,extraSocketOptions,dataOut,bytesReadOut,systemAddressOut,timeRead); +#else + (void) rakPeer; + sockaddr_storage their_addr; + sockaddr* sockAddrPtr; + socklen_t sockLen; + socklen_t* socketlenPtr=(socklen_t*) &sockLen; + memset(&their_addr,0,sizeof(their_addr)); + int dataOutSize; + const int flag=0; + + (void) remotePortRakNetWasStartedOn_PS3; + (void) extraSocketOptions; + + + + + + + + + + + + { + sockLen=sizeof(their_addr); + sockAddrPtr=(sockaddr*) &their_addr; + } + + + + + dataOutSize=MAXIMUM_MTU_SIZE; + + + *bytesReadOut = recvfrom__( s, dataOut, dataOutSize, flag, sockAddrPtr, socketlenPtr ); + + + + + + + + + + if (*bytesReadOut<=0) + return; + *timeRead=RakNet::GetTimeUS(); + + + + + + + + + + { + if (their_addr.ss_family==AF_INET) + { + memcpy(&systemAddressOut->address.addr4,(sockaddr_in *)&their_addr,sizeof(sockaddr_in)); + systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr4.sin_port); + // systemAddressOut->address.addr4.sin_port=ntohs( systemAddressOut->address.addr4.sin_port ); + } + else + { + memcpy(&systemAddressOut->address.addr6,(sockaddr_in6 *)&their_addr,sizeof(sockaddr_in6)); + systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr6.sin6_port); + // systemAddressOut->address.addr6.sin6_port=ntohs( systemAddressOut->address.addr6.sin6_port ); + } + } + +#endif // defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) +} + +int SocketLayer::SendTo_PS3Lobby( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ) +{ + (void) s; + (void) data; + (void) length; + (void) remotePortRakNetWasStartedOn_PS3; + (void) systemAddress; + + int len=0; + + + + + + + + + + + + + + + return len; +} +int SocketLayer::SendTo_PSP2( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ) +{ + (void) s; + (void) data; + (void) length; + (void) remotePortRakNetWasStartedOn_PS3; + (void) systemAddress; + + int len=0; + + + + + + + + + + + + + + return len; +} +int SocketLayer::SendTo_360( SOCKET s, const char *data, int length, const char *voiceData, int voiceLength, const SystemAddress &systemAddress, unsigned int extraSocketOptions ) +{ + (void) s; + (void) data; + (void) length; + (void) voiceData; + (void) voiceLength; + (void) extraSocketOptions; + (void) systemAddress; + + int len=0; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + return len; +} +int SocketLayer::SendTo_PC( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, const char *file, const long line ) +{ + // TODO + // http://www.linuxquestions.org/questions/linux-software-2/ipv6-linux-sendto-problems-519485/ + +// #if RAKNET_SUPPORT_IPV6==1 +// RakAssert( +// systemAddress.address.addr4.sin_family!=AF_MAX && +// (systemAddress.address.addr4.sin_family==AF_INET || (systemAddress.address.addr6.sin6_scope_id!=0)) +// ); +// #endif + + /* + sockaddr_in sa; + memset(&sa,0,sizeof(sockaddr_in)); + sa.sin_port = htons( port ); // User port + sa.sin_addr.s_addr = binaryAddress; + sa.sin_family = socketFamily; + */ + + int len=0; + do + { +#ifdef DEBUG_SENDTO_SPIKES + RakNetTime start = RakNet::GetTime(); +#else + (void) file; + (void) line; +#endif + if (systemAddress.address.addr4.sin_family==AF_INET) + { + //systemAddress.address.addr4.sin_port=htons(systemAddress.address.addr4.sin_port); + len = sendto__( s, data, length, 0, ( const sockaddr* ) & systemAddress.address.addr4, sizeof( sockaddr_in ) ); + //systemAddress.address.addr4.sin_port=ntohs(systemAddress.address.addr4.sin_port); + } + else + { +#if RAKNET_SUPPORT_IPV6==1 + // systemAddress.address.addr6.sin6_port=htons(systemAddress.address.addr6.sin6_port); + len = sendto__( s, data, length, 0, ( const sockaddr* ) & systemAddress.address.addr6, sizeof( sockaddr_in6 ) ); + //systemAddress.address.addr6.sin6_port=ntohs(systemAddress.address.addr6.sin6_port); +#endif + } + +#ifdef DEBUG_SENDTO_SPIKES + RakNetTime end = RakNet::GetTime(); + static unsigned int callCount=1; + RAKNET_DEBUG_PRINTF("%i. SendTo_PC, time=%llu, elapsed=%llu, length=%i, returned=%i, binaryAddress=%i, port=%i, file=%s, line=%i\n", callCount++, end, end-start, length, len, binaryAddress, port, file, line); +#endif + if (len<0) + { + RAKNET_DEBUG_PRINTF("sendto failed with code %i for char %i and length %i.\n", len, data[0], length); + } + } + while ( len == 0 ); + return len; +} + +#ifdef _MSC_VER +#pragma warning( disable : 4702 ) // warning C4702: unreachable code +#endif +int SocketLayer::SendTo( SOCKET s, const char *data, int length, SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ) +{ + (void) extraSocketOptions; + + int len=0; + RakAssert(length<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); +#if !defined(__S3E__) + RakAssert(systemAddress.address.addr4.sin_family!=AF_MAX); +#endif + + if (slo) + { + len = slo->RakNetSendTo(s,data,length,systemAddress); + if ( len != -1 ) + return 0; + return 1; + } + + if ( s == (SOCKET) -1 ) + { + return -1; + } + + + if (remotePortRakNetWasStartedOn_PS3!=0) + { + + + + + + } + else + { + + + + + + + len = SendTo_PC(s,data,length,systemAddress,file,line); + + } + + if ( len != -1 ) + return 0; + +#if defined(_WIN32) && !defined(_WIN32_WCE) + + DWORD dwIOError = WSAGetLastError(); + + if ( dwIOError == WSAECONNRESET ) + { +#if defined(_DEBUG) + RAKNET_DEBUG_PRINTF( "A previous send operation resulted in an ICMP Port Unreachable message.\n" ); +#endif + + } + else if ( dwIOError != WSAEWOULDBLOCK && dwIOError != WSAEADDRNOTAVAIL) + { +#if defined(_WIN32) && defined(_DEBUG) + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "sendto failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); +#endif + + } + + return dwIOError; +#endif + + return 1; // error +} +// Not enough info for IPV6 +// int SocketLayer::SendTo( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ) +// { +// SystemAddress systemAddress; +// systemAddress.FromStringAndPort(ip,port); +// return SendTo( s, data, length, systemAddress,remotePortRakNetWasStartedOn_PS3, extraSocketOptions, file, line ); +// } +int SocketLayer::SendToTTL( SOCKET s, const char *data, int length, SystemAddress &systemAddress, int ttl ) +{ + if (slo) + return slo->RakNetSendTo(s,data,length,systemAddress); + + + int oldTTL; + socklen_t opLen=sizeof(oldTTL); + // Get the current TTL + if (getsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & oldTTL, &opLen ) == -1) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "getsockopt__(IPPROTO_IP,IP_TTL) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#endif + } + + // Set to TTL + int newTTL=ttl; + if (setsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & newTTL, sizeof ( newTTL ) ) == -1) + { + +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "setsockopt__(IPPROTO_IP,IP_TTL) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#endif + } + + // Send + int res = SendTo(s,data,length,systemAddress,0,0, __FILE__, __LINE__ ); + + // Restore the old TTL + setsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & oldTTL, opLen ); + + return res; + + + +} + + +RakNet::RakString SocketLayer::GetSubNetForSocketAndIp(SOCKET inSock, RakNet::RakString inIpString) +{ + RakNet::RakString netMaskString; + RakNet::RakString ipString; + + + + + +#if defined(_WIN32) + INTERFACE_INFO InterfaceList[20]; + unsigned long nBytesReturned; + if (WSAIoctl(inSock, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList, + sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR) { + return ""; + } + + int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO); + + for (int i = 0; i < nNumInterfaces; ++i) + { + sockaddr_in *pAddress; + pAddress = (sockaddr_in *) & (InterfaceList[i].iiAddress); + ipString=inet_ntoa(pAddress->sin_addr); + + if (inIpString==ipString) + { + pAddress = (sockaddr_in *) & (InterfaceList[i].iiNetmask); + netMaskString=inet_ntoa(pAddress->sin_addr); + return netMaskString; + } + } + return ""; +#else + + int fd,fd2; + fd2 = socket__(AF_INET, SOCK_DGRAM, 0); + + if(fd2 < 0) + { + return ""; + } + + struct ifconf ifc; + char buf[1999]; + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if(ioctl(fd2, SIOCGIFCONF, &ifc) < 0) + { + return ""; + } + + struct ifreq *ifr; + ifr = ifc.ifc_req; + int intNum = ifc.ifc_len / sizeof(struct ifreq); + for(int i = 0; i < intNum; i++) + { + ipString=inet_ntoa(((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr); + + if (inIpString==ipString) + { + struct ifreq ifr2; + fd = socket__(AF_INET, SOCK_DGRAM, 0); + if(fd < 0) + { + return ""; + } + ifr2.ifr_addr.sa_family = AF_INET; + + strncpy(ifr2.ifr_name, ifr[i].ifr_name, IFNAMSIZ-1); + + ioctl(fd, SIOCGIFNETMASK, &ifr2); + + close(fd); + close(fd2); + netMaskString=inet_ntoa(((struct sockaddr_in *)&ifr2.ifr_addr)->sin_addr); + + return netMaskString; + } + } + + close(fd2); + return ""; + +#endif + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void GetMyIP_Win32( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) +{ + char ac[ 80 ]; + if ( gethostname( ac, sizeof( ac ) ) == -1 ) + { + #if defined(_WIN32) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "gethostname failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); + #endif + return ; + } + + +#if RAKNET_SUPPORT_IPV6==1 + struct addrinfo hints; + struct addrinfo *servinfo=0, *aip; // will point to the results + PrepareAddrInfoHints(&hints); + getaddrinfo(ac, "", &hints, &servinfo); + + int idx; + for (idx=0, aip = servinfo; aip != NULL && idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; aip = aip->ai_next, idx++) + { + if (aip->ai_family == AF_INET) + { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)aip->ai_addr; + memcpy(&addresses[idx].address.addr4,ipv4,sizeof(sockaddr_in)); + } + else + { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)aip->ai_addr; + memcpy(&addresses[idx].address.addr4,ipv6,sizeof(sockaddr_in6)); + } + + } + + freeaddrinfo(servinfo); // free the linked-list +#else + struct hostent *phe = gethostbyname( ac ); + + if ( phe == 0 ) + { + #ifdef _WIN32 + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "gethostbyname failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); + #endif + return ; + } + int idx; + for ( idx = 0; idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ++idx ) + { + if (phe->h_addr_list[ idx ] == 0) + break; + + memcpy(&addresses[idx].address.addr4.sin_addr,phe->h_addr_list[ idx ],sizeof(struct in_addr)); + + } +#endif + + + while (idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS) + { + addresses[idx]=UNASSIGNED_SYSTEM_ADDRESS; + idx++; + } +} +// #else +/* +void GetMyIP_Linux( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) +{ + struct ifaddrs *ifaddr, *ifa; + int family, s; + char host[NI_MAXHOST]; + struct in_addr linux_in_addr; + + if (getifaddrs(&ifaddr) == -1) { + printf( "Error getting interface list\n"); + } + + int idx = 0; + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) continue; + family = ifa->ifa_addr->sa_family; + + if (family == AF_INET) { + s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (s != 0) { + // printf ("getnameinfo() failed: %s\n", gai_strerror(s)); + } + else if (strcmp(host,"127.0.0.1")!=0) + { + if (inet_aton(host, &addresses[idx].address.addr4.sin_addr)!=0) + idx++; + } + } +#if RAKNET_SUPPORT_IPV6==1 + else + { + s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (s != 0) { + // printf ("getnameinfo() failed: %s\n", gai_strerror(s)); + } + else if (strcmp(host,"::1")!=0) + { + if (inet_pton(family, host, &addresses[idx].address.addr6.sin6_addr)!=0) + idx++; + } + } +#endif + } + + for ( ; idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ++idx ) + { + addresses[idx]=UNASSIGNED_SYSTEM_ADDRESS; + } + + freeifaddrs(ifaddr); +} +*/ + + + +void SocketLayer::GetMyIP( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) +{ + + + + +#if defined(_WIN32) + GetMyIP_Win32(addresses); +#else +// GetMyIP_Linux(addresses); + GetMyIP_Win32(addresses); +#endif +} + + +unsigned short SocketLayer::GetLocalPort(SOCKET s) +{ + SystemAddress sa; + GetSystemAddress(s,&sa); + return sa.GetPort(); +} +void SocketLayer::GetSystemAddress_Old ( SOCKET s, SystemAddress *systemAddressOut ) +{ + sockaddr_in sa; + memset(&sa,0,sizeof(sockaddr_in)); + socklen_t len = sizeof(sa); + if (getsockname__(s, (sockaddr*)&sa, &len)!=0) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "getsockname failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); +#endif + *systemAddressOut = UNASSIGNED_SYSTEM_ADDRESS; + return; + } + + systemAddressOut->SetPortNetworkOrder(sa.sin_port); + systemAddressOut->address.addr4.sin_addr.s_addr=sa.sin_addr.s_addr; +} +void SocketLayer::GetSystemAddress ( SOCKET s, SystemAddress *systemAddressOut ) +{ +#if RAKNET_SUPPORT_IPV6!=1 + GetSystemAddress_Old(s,systemAddressOut); +#else + socklen_t slen; + sockaddr_storage ss; + slen = sizeof(ss); + + if (getsockname__(s, (struct sockaddr *)&ss, &slen)!=0) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "getsockname failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); +#endif + systemAddressOut->FromString(0); + return; + } + + if (ss.ss_family==AF_INET) + { + memcpy(&systemAddressOut->address.addr4,(sockaddr_in *)&ss,sizeof(sockaddr_in)); + systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr4.sin_port); + + uint32_t zero = 0; + if (memcmp(&systemAddressOut->address.addr4.sin_addr.s_addr, &zero, sizeof(zero))==0) + systemAddressOut->SetToLoopback(4); + // systemAddressOut->address.addr4.sin_port=ntohs(systemAddressOut->address.addr4.sin_port); + } + else + { + memcpy(&systemAddressOut->address.addr6,(sockaddr_in6 *)&ss,sizeof(sockaddr_in6)); + systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr6.sin6_port); + + char zero[16]; + memset(zero,0,sizeof(zero)); + if (memcmp(&systemAddressOut->address.addr4.sin_addr.s_addr, &zero, sizeof(zero))==0) + systemAddressOut->SetToLoopback(6); + + // systemAddressOut->address.addr6.sin6_port=ntohs(systemAddressOut->address.addr6.sin6_port); + } +#endif // #if RAKNET_SUPPORT_IPV6!=1 +} + +void SocketLayer::SetSocketLayerOverride(SocketLayerOverride *_slo) +{ + slo=_slo; +} + +bool SocketLayer::GetFirstBindableIP(char firstBindable[128], int ipProto) +{ + SystemAddress ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ]; + SocketLayer::GetMyIP( ipList ); + + + if (ipProto==AF_UNSPEC) + + { + ipList[0].ToString(false,firstBindable); + return true; + } + + // Find the first valid host address + unsigned int l; + for (l=0; l < MAXIMUM_NUMBER_OF_INTERNAL_IDS; l++) + { + if (ipList[l]==UNASSIGNED_SYSTEM_ADDRESS) + break; + if (ipList[l].GetIPVersion()==4 && ipProto==AF_INET) + break; + if (ipList[l].GetIPVersion()==6 && ipProto==AF_INET6) + break; + } + + if (ipList[l]==UNASSIGNED_SYSTEM_ADDRESS || l==MAXIMUM_NUMBER_OF_INTERNAL_IDS) + return false; +// RAKNET_DEBUG_PRINTF("%i %i %i %i\n", +// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[0], +// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[1], +// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[2], +// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[3] +// ); + ipList[l].ToString(false,firstBindable); + return true; + +} + + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/SocketLayer.h b/project/lib_projects/raknet/jni/RaknetSources/SocketLayer.h index 01265d5..ba089fa 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SocketLayer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SocketLayer.h @@ -1,164 +1,164 @@ -/// \file -/// \brief SocketLayer class implementation -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - - -#ifndef __SOCKET_LAYER_H -#define __SOCKET_LAYER_H - -#include "RakMemoryOverride.h" -#include "SocketIncludes.h" -#include "RakNetTypes.h" -#include "RakNetSmartPtr.h" -#include "RakNetSocket.h" -#include "Export.h" -#include "MTUSize.h" -#include "RakString.h" - -//#include "ClientContextStruct.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeer; - -class RAK_DLL_EXPORT SocketLayerOverride -{ -public: - SocketLayerOverride() {} - virtual ~SocketLayerOverride() {} - - /// Called when SendTo would otherwise occur. - virtual int RakNetSendTo( SOCKET s, const char *data, int length, const SystemAddress &systemAddress )=0; - - /// Called when RecvFrom would otherwise occur. Return number of bytes read. Write data into dataOut - // Return -1 to use RakNet's normal recvfrom, 0 to abort RakNet's normal recvfrom, and positive to return data - virtual int RakNetRecvFrom( const SOCKET sIn, RakPeer *rakPeerIn, char dataOut[ MAXIMUM_MTU_SIZE ], SystemAddress *senderOut, bool calledFromMainThread )=0; -}; - - -// A platform independent implementation of Berkeley sockets, with settings used by RakNet -class RAK_DLL_EXPORT SocketLayer -{ - -public: - - /// Default Constructor - SocketLayer(); - - // Destructor - ~SocketLayer(); - - /// Creates a bound socket to listen for incoming connections on the specified port - /// \param[in] port the port number - /// \param[in] blockingSocket - /// \return A new socket used for accepting clients - static SOCKET CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions, unsigned short socketFamily ); - static SOCKET CreateBoundSocket_Old( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions ); - static SOCKET CreateBoundSocket_PS3Lobby( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ); - static SOCKET CreateBoundSocket_PSP2( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ); - - /// Returns if this specified port is in use, for UDP - /// \param[in] port the port number - /// \return If this port is already in use - static bool IsPortInUse_Old(unsigned short port, const char *hostAddress); - static bool IsPortInUse(unsigned short port, const char *hostAddress, unsigned short socketFamily ); - static bool IsSocketFamilySupported(const char *hostAddress, unsigned short socketFamily); - - static const char* DomainNameToIP_Old( const char *domainName ); - static const char* DomainNameToIP( const char *domainName ); - - /// Write \a data of length \a length to \a writeSocket - /// \param[in] writeSocket The socket to write to - /// \param[in] data The data to write - /// \param[in] length The length of \a data - static void Write( const SOCKET writeSocket, const char* data, const int length ); - - /// Read data from a socket - /// \param[in] s the socket - /// \param[in] rakPeer The instance of rakPeer containing the recvFrom C callback - /// \param[in] errorCode An error code if an error occured . - /// \param[in] connectionSocketIndex Which of the sockets in RakPeer we are using - /// \return Returns true if you successfully read data, false on error. - static void RecvFromBlocking_Old( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ); - static void RecvFromBlocking( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ); - - /// Given a socket and IP, retrieves the subnet mask, on linux the socket is unused - /// \param[in] inSock the socket - /// \param[in] inIpString The ip of the interface you wish to retrieve the subnet mask from - /// \return Returns the ip dotted subnet mask if successful, otherwise returns empty string ("") - static RakNet::RakString GetSubNetForSocketAndIp(SOCKET inSock, RakNet::RakString inIpString); - - - /// Sets the socket flags to nonblocking - /// \param[in] listenSocket the socket to set - static void SetNonBlocking( SOCKET listenSocket); - - - /// Retrieve all local IP address in a string format. - /// \param[in] s The socket whose port we are referring to - /// \param[in] ipList An array of ip address in dotted notation. - static void GetMyIP( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ); - - - /// Call sendto (UDP obviously) - /// \param[in] s the socket - /// \param[in] data The byte buffer to send - /// \param[in] length The length of the \a data in bytes - /// \param[in] ip The address of the remote host in dotted notation. - /// \param[in] port The port number to send to. - /// \return 0 on success, nonzero on failure. -// static int SendTo( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ); - - /// Call sendto (UDP obviously) - /// It won't reach the recipient, except on a LAN - /// However, this is good for opening routers / firewalls - /// \param[in] s the socket - /// \param[in] data The byte buffer to send - /// \param[in] length The length of the \a data in bytes - /// \param[in] ip The address of the remote host in dotted notation. - /// \param[in] port The port number to send to. - /// \param[in] ttl Max hops of datagram - /// \return 0 on success, nonzero on failure. - static int SendToTTL( SOCKET s, const char *data, int length, SystemAddress &systemAddress, int ttl ); - - /// Call sendto (UDP obviously) - /// \param[in] s the socket - /// \param[in] data The byte buffer to send - /// \param[in] length The length of the \a data in bytes - /// \param[in] binaryAddress The address of the remote host in binary format. - /// \param[in] port The port number to send to. - /// \return 0 on success, nonzero on failure. - static int SendTo( SOCKET s, const char *data, int length, SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ); - - static unsigned short GetLocalPort(SOCKET s); - static void GetSystemAddress_Old ( SOCKET s, SystemAddress *systemAddressOut ); - static void GetSystemAddress ( SOCKET s, SystemAddress *systemAddressOut ); - - static void SetSocketLayerOverride(SocketLayerOverride *_slo); - static SocketLayerOverride* GetSocketLayerOverride(void) {return slo;} - - static int SendTo_PS3Lobby( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ); - static int SendTo_PSP2( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ); - static int SendTo_360( SOCKET s, const char *data, int length, const char *voiceData, int voiceLength, const SystemAddress &systemAddress, unsigned int extraSocketOptions ); - static int SendTo_PC( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, const char *file, const long line ); - - static void SetDoNotFragment( SOCKET listenSocket, int opt, int IPPROTO ); - - - // AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - static bool GetFirstBindableIP(char firstBindable[128], int ipProto); - -private: - - static void SetSocketOptions( SOCKET listenSocket); - static SocketLayerOverride *slo; -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief SocketLayer class implementation +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + + +#ifndef __SOCKET_LAYER_H +#define __SOCKET_LAYER_H + +#include "RakMemoryOverride.h" +#include "SocketIncludes.h" +#include "RakNetTypes.h" +#include "RakNetSmartPtr.h" +#include "RakNetSocket.h" +#include "Export.h" +#include "MTUSize.h" +#include "RakString.h" + +//#include "ClientContextStruct.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeer; + +class RAK_DLL_EXPORT SocketLayerOverride +{ +public: + SocketLayerOverride() {} + virtual ~SocketLayerOverride() {} + + /// Called when SendTo would otherwise occur. + virtual int RakNetSendTo( SOCKET s, const char *data, int length, const SystemAddress &systemAddress )=0; + + /// Called when RecvFrom would otherwise occur. Return number of bytes read. Write data into dataOut + // Return -1 to use RakNet's normal recvfrom, 0 to abort RakNet's normal recvfrom, and positive to return data + virtual int RakNetRecvFrom( const SOCKET sIn, RakPeer *rakPeerIn, char dataOut[ MAXIMUM_MTU_SIZE ], SystemAddress *senderOut, bool calledFromMainThread )=0; +}; + + +// A platform independent implementation of Berkeley sockets, with settings used by RakNet +class RAK_DLL_EXPORT SocketLayer +{ + +public: + + /// Default Constructor + SocketLayer(); + + // Destructor + ~SocketLayer(); + + /// Creates a bound socket to listen for incoming connections on the specified port + /// \param[in] port the port number + /// \param[in] blockingSocket + /// \return A new socket used for accepting clients + static SOCKET CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions, unsigned short socketFamily ); + static SOCKET CreateBoundSocket_Old( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions ); + static SOCKET CreateBoundSocket_PS3Lobby( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ); + static SOCKET CreateBoundSocket_PSP2( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ); + + /// Returns if this specified port is in use, for UDP + /// \param[in] port the port number + /// \return If this port is already in use + static bool IsPortInUse_Old(unsigned short port, const char *hostAddress); + static bool IsPortInUse(unsigned short port, const char *hostAddress, unsigned short socketFamily ); + static bool IsSocketFamilySupported(const char *hostAddress, unsigned short socketFamily); + + static const char* DomainNameToIP_Old( const char *domainName ); + static const char* DomainNameToIP( const char *domainName ); + + /// Write \a data of length \a length to \a writeSocket + /// \param[in] writeSocket The socket to write to + /// \param[in] data The data to write + /// \param[in] length The length of \a data + static void Write( const SOCKET writeSocket, const char* data, const int length ); + + /// Read data from a socket + /// \param[in] s the socket + /// \param[in] rakPeer The instance of rakPeer containing the recvFrom C callback + /// \param[in] errorCode An error code if an error occured . + /// \param[in] connectionSocketIndex Which of the sockets in RakPeer we are using + /// \return Returns true if you successfully read data, false on error. + static void RecvFromBlocking_Old( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ); + static void RecvFromBlocking( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ); + + /// Given a socket and IP, retrieves the subnet mask, on linux the socket is unused + /// \param[in] inSock the socket + /// \param[in] inIpString The ip of the interface you wish to retrieve the subnet mask from + /// \return Returns the ip dotted subnet mask if successful, otherwise returns empty string ("") + static RakNet::RakString GetSubNetForSocketAndIp(SOCKET inSock, RakNet::RakString inIpString); + + + /// Sets the socket flags to nonblocking + /// \param[in] listenSocket the socket to set + static void SetNonBlocking( SOCKET listenSocket); + + + /// Retrieve all local IP address in a string format. + /// \param[in] s The socket whose port we are referring to + /// \param[in] ipList An array of ip address in dotted notation. + static void GetMyIP( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ); + + + /// Call sendto (UDP obviously) + /// \param[in] s the socket + /// \param[in] data The byte buffer to send + /// \param[in] length The length of the \a data in bytes + /// \param[in] ip The address of the remote host in dotted notation. + /// \param[in] port The port number to send to. + /// \return 0 on success, nonzero on failure. +// static int SendTo( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ); + + /// Call sendto (UDP obviously) + /// It won't reach the recipient, except on a LAN + /// However, this is good for opening routers / firewalls + /// \param[in] s the socket + /// \param[in] data The byte buffer to send + /// \param[in] length The length of the \a data in bytes + /// \param[in] ip The address of the remote host in dotted notation. + /// \param[in] port The port number to send to. + /// \param[in] ttl Max hops of datagram + /// \return 0 on success, nonzero on failure. + static int SendToTTL( SOCKET s, const char *data, int length, SystemAddress &systemAddress, int ttl ); + + /// Call sendto (UDP obviously) + /// \param[in] s the socket + /// \param[in] data The byte buffer to send + /// \param[in] length The length of the \a data in bytes + /// \param[in] binaryAddress The address of the remote host in binary format. + /// \param[in] port The port number to send to. + /// \return 0 on success, nonzero on failure. + static int SendTo( SOCKET s, const char *data, int length, SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ); + + static unsigned short GetLocalPort(SOCKET s); + static void GetSystemAddress_Old ( SOCKET s, SystemAddress *systemAddressOut ); + static void GetSystemAddress ( SOCKET s, SystemAddress *systemAddressOut ); + + static void SetSocketLayerOverride(SocketLayerOverride *_slo); + static SocketLayerOverride* GetSocketLayerOverride(void) {return slo;} + + static int SendTo_PS3Lobby( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ); + static int SendTo_PSP2( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ); + static int SendTo_360( SOCKET s, const char *data, int length, const char *voiceData, int voiceLength, const SystemAddress &systemAddress, unsigned int extraSocketOptions ); + static int SendTo_PC( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, const char *file, const long line ); + + static void SetDoNotFragment( SOCKET listenSocket, int opt, int IPPROTO ); + + + // AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + static bool GetFirstBindableIP(char firstBindable[128], int ipProto); + +private: + + static void SetSocketOptions( SOCKET listenSocket); + static SocketLayerOverride *slo; +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/StringCompressor.cpp b/project/lib_projects/raknet/jni/RaknetSources/StringCompressor.cpp index 8516a33..dd63f84 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/StringCompressor.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/StringCompressor.cpp @@ -1,499 +1,499 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "StringCompressor.h" -#include "DS_HuffmanEncodingTree.h" -#include "BitStream.h" -#include "RakString.h" -#include "RakAssert.h" -#include - -#include - - - - - -using namespace RakNet; - -StringCompressor* StringCompressor::instance=0; -int StringCompressor::referenceCount=0; - -void StringCompressor::AddReference(void) -{ - if (++referenceCount==1) - { - instance = RakNet::OP_NEW( _FILE_AND_LINE_ ); - } -} -void StringCompressor::RemoveReference(void) -{ - RakAssert(referenceCount > 0); - - if (referenceCount > 0) - { - if (--referenceCount==0) - { - RakNet::OP_DELETE(instance, _FILE_AND_LINE_); - instance=0; - } - } -} - -StringCompressor* StringCompressor::Instance(void) -{ - return instance; -} - -unsigned int englishCharacterFrequencies[ 256 ] = -{ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 722, - 0, - 0, - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 11084, - 58, - 63, - 1, - 0, - 31, - 0, - 317, - 64, - 64, - 44, - 0, - 695, - 62, - 980, - 266, - 69, - 67, - 56, - 7, - 73, - 3, - 14, - 2, - 69, - 1, - 167, - 9, - 1, - 2, - 25, - 94, - 0, - 195, - 139, - 34, - 96, - 48, - 103, - 56, - 125, - 653, - 21, - 5, - 23, - 64, - 85, - 44, - 34, - 7, - 92, - 76, - 147, - 12, - 14, - 57, - 15, - 39, - 15, - 1, - 1, - 1, - 2, - 3, - 0, - 3611, - 845, - 1077, - 1884, - 5870, - 841, - 1057, - 2501, - 3212, - 164, - 531, - 2019, - 1330, - 3056, - 4037, - 848, - 47, - 2586, - 2919, - 4771, - 1707, - 535, - 1106, - 152, - 1243, - 100, - 0, - 2, - 0, - 10, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 -}; - -StringCompressor::StringCompressor() -{ - DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON(); - - // Make a default tree immediately, since this is used for RPC possibly from multiple threads at the same time - HuffmanEncodingTree *huffmanEncodingTree = RakNet::OP_NEW( _FILE_AND_LINE_ ); - huffmanEncodingTree->GenerateFromFrequencyTable( englishCharacterFrequencies ); - - huffmanEncodingTrees.Set(0, huffmanEncodingTree); -} -void StringCompressor::GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, uint8_t languageId ) -{ - HuffmanEncodingTree *huffmanEncodingTree; - if (huffmanEncodingTrees.Has(languageId)) - { - huffmanEncodingTree = huffmanEncodingTrees.Get(languageId); - RakNet::OP_DELETE(huffmanEncodingTree, _FILE_AND_LINE_); - } - - unsigned index; - unsigned int frequencyTable[ 256 ]; - - if ( inputLength == 0 ) - return ; - - // Zero out the frequency table - memset( frequencyTable, 0, sizeof( frequencyTable ) ); - - // Generate the frequency table from the strings - for ( index = 0; index < inputLength; index++ ) - frequencyTable[ input[ index ] ] ++; - - // Build the tree - huffmanEncodingTree = RakNet::OP_NEW( _FILE_AND_LINE_ ); - huffmanEncodingTree->GenerateFromFrequencyTable( frequencyTable ); - huffmanEncodingTrees.Set(languageId, huffmanEncodingTree); -} - -StringCompressor::~StringCompressor() -{ - for (unsigned i=0; i < huffmanEncodingTrees.Size(); i++) - RakNet::OP_DELETE(huffmanEncodingTrees[i], _FILE_AND_LINE_); -} - -void StringCompressor::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) -{ - HuffmanEncodingTree *huffmanEncodingTree; - if (huffmanEncodingTrees.Has(languageId)==false) - return; - huffmanEncodingTree=huffmanEncodingTrees.Get(languageId); - - if ( input == 0 ) - { - output->WriteCompressed( (uint32_t) 0 ); - return ; - } - - RakNet::BitStream encodedBitStream; - - uint32_t stringBitLength; - - int charsToWrite; - - if ( maxCharsToWrite<=0 || ( int ) strlen( input ) < maxCharsToWrite ) - charsToWrite = ( int ) strlen( input ); - else - charsToWrite = maxCharsToWrite - 1; - - huffmanEncodingTree->EncodeArray( ( unsigned char* ) input, charsToWrite, &encodedBitStream ); - - stringBitLength = (uint32_t) encodedBitStream.GetNumberOfBitsUsed(); - - output->WriteCompressed( stringBitLength ); - - output->WriteBits( encodedBitStream.GetData(), stringBitLength ); -} - -bool StringCompressor::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) -{ - HuffmanEncodingTree *huffmanEncodingTree; - if (huffmanEncodingTrees.Has(languageId)==false) - return false; - if (maxCharsToWrite<=0) - return false; - huffmanEncodingTree=huffmanEncodingTrees.Get(languageId); - - uint32_t stringBitLength; - int bytesInStream; - - output[ 0 ] = 0; - - if ( input->ReadCompressed( stringBitLength ) == false ) - return false; - - if ( (unsigned) input->GetNumberOfUnreadBits() < stringBitLength ) - return false; - - bytesInStream = huffmanEncodingTree->DecodeArray( input, stringBitLength, maxCharsToWrite, ( unsigned char* ) output ); - - if ( bytesInStream < maxCharsToWrite ) - output[ bytesInStream ] = 0; - else - output[ maxCharsToWrite - 1 ] = 0; - - return true; -} -#ifdef _CSTRING_COMPRESSOR -void StringCompressor::EncodeString( const CString &input, int maxCharsToWrite, RakNet::BitStream *output ) -{ - LPTSTR p = input; - EncodeString(p, maxCharsToWrite*sizeof(TCHAR), output, languageID); -} -bool StringCompressor::DecodeString( CString &output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) -{ - LPSTR p = output.GetBuffer(maxCharsToWrite*sizeof(TCHAR)); - DecodeString(p,maxCharsToWrite*sizeof(TCHAR), input, languageID); - output.ReleaseBuffer(0) - -} -#endif -#ifdef _STD_STRING_COMPRESSOR -void StringCompressor::EncodeString( const std::string &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) -{ - EncodeString(input.c_str(), maxCharsToWrite, output, languageID); -} -bool StringCompressor::DecodeString( std::string *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) -{ - if (maxCharsToWrite <= 0) - { - output->clear(); - return true; - } - - char *destinationBlock; - bool out; - - - if (maxCharsToWrite < MAX_ALLOCA_STACK_ALLOCATION) - { - destinationBlock = (char*) alloca(maxCharsToWrite); - out=DecodeString(destinationBlock, maxCharsToWrite, input, languageID); - *output=destinationBlock; - } - else - - { - destinationBlock = (char*) rakMalloc_Ex( maxCharsToWrite, _FILE_AND_LINE_ ); - out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); - *output=destinationBlock; - rakFree_Ex(destinationBlock, _FILE_AND_LINE_ ); - } - - return out; -} -#endif -void StringCompressor::EncodeString( const RakString *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) -{ - EncodeString(input->C_String(), maxCharsToWrite, output, languageId); -} -bool StringCompressor::DecodeString( RakString *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) -{ - if (maxCharsToWrite <= 0) - { - output->Clear(); - return true; - } - - char *destinationBlock; - bool out; - - - if (maxCharsToWrite < MAX_ALLOCA_STACK_ALLOCATION) - { - destinationBlock = (char*) alloca(maxCharsToWrite); - out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); - *output=destinationBlock; - } - else - - { - destinationBlock = (char*) rakMalloc_Ex( maxCharsToWrite, _FILE_AND_LINE_ ); - out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); - *output=destinationBlock; - rakFree_Ex(destinationBlock, _FILE_AND_LINE_ ); - } - - return out; -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "StringCompressor.h" +#include "DS_HuffmanEncodingTree.h" +#include "BitStream.h" +#include "RakString.h" +#include "RakAssert.h" +#include + +#include + + + + + +using namespace RakNet; + +StringCompressor* StringCompressor::instance=0; +int StringCompressor::referenceCount=0; + +void StringCompressor::AddReference(void) +{ + if (++referenceCount==1) + { + instance = RakNet::OP_NEW( _FILE_AND_LINE_ ); + } +} +void StringCompressor::RemoveReference(void) +{ + RakAssert(referenceCount > 0); + + if (referenceCount > 0) + { + if (--referenceCount==0) + { + RakNet::OP_DELETE(instance, _FILE_AND_LINE_); + instance=0; + } + } +} + +StringCompressor* StringCompressor::Instance(void) +{ + return instance; +} + +unsigned int englishCharacterFrequencies[ 256 ] = +{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 722, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11084, + 58, + 63, + 1, + 0, + 31, + 0, + 317, + 64, + 64, + 44, + 0, + 695, + 62, + 980, + 266, + 69, + 67, + 56, + 7, + 73, + 3, + 14, + 2, + 69, + 1, + 167, + 9, + 1, + 2, + 25, + 94, + 0, + 195, + 139, + 34, + 96, + 48, + 103, + 56, + 125, + 653, + 21, + 5, + 23, + 64, + 85, + 44, + 34, + 7, + 92, + 76, + 147, + 12, + 14, + 57, + 15, + 39, + 15, + 1, + 1, + 1, + 2, + 3, + 0, + 3611, + 845, + 1077, + 1884, + 5870, + 841, + 1057, + 2501, + 3212, + 164, + 531, + 2019, + 1330, + 3056, + 4037, + 848, + 47, + 2586, + 2919, + 4771, + 1707, + 535, + 1106, + 152, + 1243, + 100, + 0, + 2, + 0, + 10, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +StringCompressor::StringCompressor() +{ + DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON(); + + // Make a default tree immediately, since this is used for RPC possibly from multiple threads at the same time + HuffmanEncodingTree *huffmanEncodingTree = RakNet::OP_NEW( _FILE_AND_LINE_ ); + huffmanEncodingTree->GenerateFromFrequencyTable( englishCharacterFrequencies ); + + huffmanEncodingTrees.Set(0, huffmanEncodingTree); +} +void StringCompressor::GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, uint8_t languageId ) +{ + HuffmanEncodingTree *huffmanEncodingTree; + if (huffmanEncodingTrees.Has(languageId)) + { + huffmanEncodingTree = huffmanEncodingTrees.Get(languageId); + RakNet::OP_DELETE(huffmanEncodingTree, _FILE_AND_LINE_); + } + + unsigned index; + unsigned int frequencyTable[ 256 ]; + + if ( inputLength == 0 ) + return ; + + // Zero out the frequency table + memset( frequencyTable, 0, sizeof( frequencyTable ) ); + + // Generate the frequency table from the strings + for ( index = 0; index < inputLength; index++ ) + frequencyTable[ input[ index ] ] ++; + + // Build the tree + huffmanEncodingTree = RakNet::OP_NEW( _FILE_AND_LINE_ ); + huffmanEncodingTree->GenerateFromFrequencyTable( frequencyTable ); + huffmanEncodingTrees.Set(languageId, huffmanEncodingTree); +} + +StringCompressor::~StringCompressor() +{ + for (unsigned i=0; i < huffmanEncodingTrees.Size(); i++) + RakNet::OP_DELETE(huffmanEncodingTrees[i], _FILE_AND_LINE_); +} + +void StringCompressor::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) +{ + HuffmanEncodingTree *huffmanEncodingTree; + if (huffmanEncodingTrees.Has(languageId)==false) + return; + huffmanEncodingTree=huffmanEncodingTrees.Get(languageId); + + if ( input == 0 ) + { + output->WriteCompressed( (uint32_t) 0 ); + return ; + } + + RakNet::BitStream encodedBitStream; + + uint32_t stringBitLength; + + int charsToWrite; + + if ( maxCharsToWrite<=0 || ( int ) strlen( input ) < maxCharsToWrite ) + charsToWrite = ( int ) strlen( input ); + else + charsToWrite = maxCharsToWrite - 1; + + huffmanEncodingTree->EncodeArray( ( unsigned char* ) input, charsToWrite, &encodedBitStream ); + + stringBitLength = (uint32_t) encodedBitStream.GetNumberOfBitsUsed(); + + output->WriteCompressed( stringBitLength ); + + output->WriteBits( encodedBitStream.GetData(), stringBitLength ); +} + +bool StringCompressor::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) +{ + HuffmanEncodingTree *huffmanEncodingTree; + if (huffmanEncodingTrees.Has(languageId)==false) + return false; + if (maxCharsToWrite<=0) + return false; + huffmanEncodingTree=huffmanEncodingTrees.Get(languageId); + + uint32_t stringBitLength; + int bytesInStream; + + output[ 0 ] = 0; + + if ( input->ReadCompressed( stringBitLength ) == false ) + return false; + + if ( (unsigned) input->GetNumberOfUnreadBits() < stringBitLength ) + return false; + + bytesInStream = huffmanEncodingTree->DecodeArray( input, stringBitLength, maxCharsToWrite, ( unsigned char* ) output ); + + if ( bytesInStream < maxCharsToWrite ) + output[ bytesInStream ] = 0; + else + output[ maxCharsToWrite - 1 ] = 0; + + return true; +} +#ifdef _CSTRING_COMPRESSOR +void StringCompressor::EncodeString( const CString &input, int maxCharsToWrite, RakNet::BitStream *output ) +{ + LPTSTR p = input; + EncodeString(p, maxCharsToWrite*sizeof(TCHAR), output, languageID); +} +bool StringCompressor::DecodeString( CString &output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) +{ + LPSTR p = output.GetBuffer(maxCharsToWrite*sizeof(TCHAR)); + DecodeString(p,maxCharsToWrite*sizeof(TCHAR), input, languageID); + output.ReleaseBuffer(0) + +} +#endif +#ifdef _STD_STRING_COMPRESSOR +void StringCompressor::EncodeString( const std::string &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) +{ + EncodeString(input.c_str(), maxCharsToWrite, output, languageID); +} +bool StringCompressor::DecodeString( std::string *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) +{ + if (maxCharsToWrite <= 0) + { + output->clear(); + return true; + } + + char *destinationBlock; + bool out; + + + if (maxCharsToWrite < MAX_ALLOCA_STACK_ALLOCATION) + { + destinationBlock = (char*) alloca(maxCharsToWrite); + out=DecodeString(destinationBlock, maxCharsToWrite, input, languageID); + *output=destinationBlock; + } + else + + { + destinationBlock = (char*) rakMalloc_Ex( maxCharsToWrite, _FILE_AND_LINE_ ); + out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); + *output=destinationBlock; + rakFree_Ex(destinationBlock, _FILE_AND_LINE_ ); + } + + return out; +} +#endif +void StringCompressor::EncodeString( const RakString *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) +{ + EncodeString(input->C_String(), maxCharsToWrite, output, languageId); +} +bool StringCompressor::DecodeString( RakString *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) +{ + if (maxCharsToWrite <= 0) + { + output->Clear(); + return true; + } + + char *destinationBlock; + bool out; + + + if (maxCharsToWrite < MAX_ALLOCA_STACK_ALLOCATION) + { + destinationBlock = (char*) alloca(maxCharsToWrite); + out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); + *output=destinationBlock; + } + else + + { + destinationBlock = (char*) rakMalloc_Ex( maxCharsToWrite, _FILE_AND_LINE_ ); + out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); + *output=destinationBlock; + rakFree_Ex(destinationBlock, _FILE_AND_LINE_ ); + } + + return out; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/StringCompressor.h b/project/lib_projects/raknet/jni/RaknetSources/StringCompressor.h index 6852ce8..ddb2405 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/StringCompressor.h +++ b/project/lib_projects/raknet/jni/RaknetSources/StringCompressor.h @@ -1,105 +1,105 @@ -/// \file -/// \brief \b Compresses/Decompresses ASCII strings and writes/reads them to BitStream class instances. You can use this to easily serialize and deserialize your own strings. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __STRING_COMPRESSOR_H -#define __STRING_COMPRESSOR_H - -#include "Export.h" -#include "DS_Map.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" - -#ifdef _STD_STRING_COMPRESSOR -#include -#endif - -/// Forward declaration -namespace RakNet -{ - class BitStream; - class RakString; -}; - - -namespace RakNet -{ -/// Forward declarations -class HuffmanEncodingTree; - -/// \brief Writes and reads strings to and from bitstreams. -/// -/// Only works with ASCII strings. The default compression is for English. -/// You can call GenerateTreeFromStrings to compress and decompress other languages efficiently as well. -class RAK_DLL_EXPORT StringCompressor -{ -public: - - // Destructor - ~StringCompressor(); - - /// static function because only static functions can access static members - /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. - /// \return the unique instance of the StringCompressor - static StringCompressor* Instance(void); - - /// Given an array of strings, such as a chat log, generate the optimal encoding tree for it. - /// This function is optional and if it is not called a default tree will be used instead. - /// \param[in] input An array of bytes which should point to text. - /// \param[in] inputLength Length of \a input - /// \param[in] languageID An identifier for the language / string table to generate the tree for. English is automatically created with ID 0 in the constructor. - void GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, uint8_t languageId ); - - /// Writes input to output, compressed. Takes care of the null terminator for you. - /// \param[in] input Pointer to an ASCII string - /// \param[in] maxCharsToWrite The max number of bytes to write of \a input. Use 0 to mean no limit. - /// \param[out] output The bitstream to write the compressed string to - /// \param[in] languageID Which language to use - void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); - - /// Writes input to output, uncompressed. Takes care of the null terminator for you. - /// \param[out] output A block of bytes to receive the output - /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. - /// \param[in] input The bitstream containing the compressed string - /// \param[in] languageID Which language to use - bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); - -#ifdef _CSTRING_COMPRESSOR - void EncodeString( const CString &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); - bool DecodeString( CString &output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); -#endif - -#ifdef _STD_STRING_COMPRESSOR - void EncodeString( const std::string &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); - bool DecodeString( std::string *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); -#endif - - void EncodeString( const RakNet::RakString *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); - bool DecodeString( RakNet::RakString *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); - - /// Used so I can allocate and deallocate this singleton at runtime - static void AddReference(void); - - /// Used so I can allocate and deallocate this singleton at runtime - static void RemoveReference(void); - - StringCompressor(); - -private: - - /// Singleton instance - static StringCompressor *instance; - - /// Pointer to the huffman encoding trees. - DataStructures::Map huffmanEncodingTrees; - - static int referenceCount; -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief \b Compresses/Decompresses ASCII strings and writes/reads them to BitStream class instances. You can use this to easily serialize and deserialize your own strings. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __STRING_COMPRESSOR_H +#define __STRING_COMPRESSOR_H + +#include "Export.h" +#include "DS_Map.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" + +#ifdef _STD_STRING_COMPRESSOR +#include +#endif + +/// Forward declaration +namespace RakNet +{ + class BitStream; + class RakString; +}; + + +namespace RakNet +{ +/// Forward declarations +class HuffmanEncodingTree; + +/// \brief Writes and reads strings to and from bitstreams. +/// +/// Only works with ASCII strings. The default compression is for English. +/// You can call GenerateTreeFromStrings to compress and decompress other languages efficiently as well. +class RAK_DLL_EXPORT StringCompressor +{ +public: + + // Destructor + ~StringCompressor(); + + /// static function because only static functions can access static members + /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. + /// \return the unique instance of the StringCompressor + static StringCompressor* Instance(void); + + /// Given an array of strings, such as a chat log, generate the optimal encoding tree for it. + /// This function is optional and if it is not called a default tree will be used instead. + /// \param[in] input An array of bytes which should point to text. + /// \param[in] inputLength Length of \a input + /// \param[in] languageID An identifier for the language / string table to generate the tree for. English is automatically created with ID 0 in the constructor. + void GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, uint8_t languageId ); + + /// Writes input to output, compressed. Takes care of the null terminator for you. + /// \param[in] input Pointer to an ASCII string + /// \param[in] maxCharsToWrite The max number of bytes to write of \a input. Use 0 to mean no limit. + /// \param[out] output The bitstream to write the compressed string to + /// \param[in] languageID Which language to use + void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); + + /// Writes input to output, uncompressed. Takes care of the null terminator for you. + /// \param[out] output A block of bytes to receive the output + /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. + /// \param[in] input The bitstream containing the compressed string + /// \param[in] languageID Which language to use + bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); + +#ifdef _CSTRING_COMPRESSOR + void EncodeString( const CString &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); + bool DecodeString( CString &output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); +#endif + +#ifdef _STD_STRING_COMPRESSOR + void EncodeString( const std::string &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); + bool DecodeString( std::string *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); +#endif + + void EncodeString( const RakNet::RakString *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); + bool DecodeString( RakNet::RakString *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); + + /// Used so I can allocate and deallocate this singleton at runtime + static void AddReference(void); + + /// Used so I can allocate and deallocate this singleton at runtime + static void RemoveReference(void); + + StringCompressor(); + +private: + + /// Singleton instance + static StringCompressor *instance; + + /// Pointer to the huffman encoding trees. + DataStructures::Map huffmanEncodingTrees; + + static int referenceCount; +}; + +} // namespace RakNet + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/StringTable.cpp b/project/lib_projects/raknet/jni/RaknetSources/StringTable.cpp index 48665d8..41a18c4 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/StringTable.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/StringTable.cpp @@ -1,140 +1,140 @@ -#include "StringTable.h" -#include -#include "RakAssert.h" -#include -#include "BitStream.h" -#include "StringCompressor.h" -using namespace RakNet; - -StringTable* StringTable::instance=0; -int StringTable::referenceCount=0; - - -int RakNet::StrAndBoolComp( char *const &key, const StrAndBool &data ) -{ - return strcmp(key,(const char*)data.str); -} - -StringTable::StringTable() -{ - -} - -StringTable::~StringTable() -{ - unsigned i; - for (i=0; i < orderedStringList.Size(); i++) - { - if (orderedStringList[i].b) - rakFree_Ex(orderedStringList[i].str, _FILE_AND_LINE_ ); - } -} - -void StringTable::AddReference(void) -{ - if (++referenceCount==1) - { - instance = RakNet::OP_NEW( _FILE_AND_LINE_ ); - } -} -void StringTable::RemoveReference(void) -{ - RakAssert(referenceCount > 0); - - if (referenceCount > 0) - { - if (--referenceCount==0) - { - RakNet::OP_DELETE(instance, _FILE_AND_LINE_); - instance=0; - } - } -} - -StringTable* StringTable::Instance(void) -{ - return instance; -} - -void StringTable::AddString(const char *str, bool copyString) -{ - StrAndBool sab; - sab.b=copyString; - if (copyString) - { - sab.str = (char*) rakMalloc_Ex( strlen(str)+1, _FILE_AND_LINE_ ); - strcpy(sab.str, str); - } - else - { - sab.str=(char*)str; - } - - // If it asserts inside here you are adding duplicate strings. - orderedStringList.Insert(sab.str,sab, true, _FILE_AND_LINE_); - - // If this assert hits you need to increase the range of StringTableType - RakAssert(orderedStringList.Size() < (StringTableType)-1); - -} -void StringTable::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output ) -{ - unsigned index; - bool objectExists; - // This is fast because the list is kept ordered. - index=orderedStringList.GetIndexFromKey((char*)input, &objectExists); - if (objectExists) - { - output->Write(true); - output->Write((StringTableType)index); - } - else - { - LogStringNotFound(input); - output->Write(false); - StringCompressor::Instance()->EncodeString(input, maxCharsToWrite, output); - } -} - -bool StringTable::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input ) -{ - bool hasIndex=false; - RakAssert(maxCharsToWrite>0); - - if (maxCharsToWrite==0) - return false; - if (!input->Read(hasIndex)) - return false; - if (hasIndex==false) - { - StringCompressor::Instance()->DecodeString(output, maxCharsToWrite, input); - } - else - { - StringTableType index; - if (!input->Read(index)) - return false; - if (index >= orderedStringList.Size()) - { -#ifdef _DEBUG - // Critical error - got a string index out of range, which means AddString was called more times on the remote system than on this system. - // All systems must call AddString the same number of types, with the same strings in the same order. - RakAssert(0); -#endif - return false; - } - - strncpy(output, orderedStringList[index].str, maxCharsToWrite); - output[maxCharsToWrite-1]=0; - } - - return true; -} -void StringTable::LogStringNotFound(const char *strName) -{ - (void) strName; - -#ifdef _DEBUG - RAKNET_DEBUG_PRINTF("Efficiency Warning! Unregistered String %s sent to StringTable.\n", strName); -#endif -} +#include "StringTable.h" +#include +#include "RakAssert.h" +#include +#include "BitStream.h" +#include "StringCompressor.h" +using namespace RakNet; + +StringTable* StringTable::instance=0; +int StringTable::referenceCount=0; + + +int RakNet::StrAndBoolComp( char *const &key, const StrAndBool &data ) +{ + return strcmp(key,(const char*)data.str); +} + +StringTable::StringTable() +{ + +} + +StringTable::~StringTable() +{ + unsigned i; + for (i=0; i < orderedStringList.Size(); i++) + { + if (orderedStringList[i].b) + rakFree_Ex(orderedStringList[i].str, _FILE_AND_LINE_ ); + } +} + +void StringTable::AddReference(void) +{ + if (++referenceCount==1) + { + instance = RakNet::OP_NEW( _FILE_AND_LINE_ ); + } +} +void StringTable::RemoveReference(void) +{ + RakAssert(referenceCount > 0); + + if (referenceCount > 0) + { + if (--referenceCount==0) + { + RakNet::OP_DELETE(instance, _FILE_AND_LINE_); + instance=0; + } + } +} + +StringTable* StringTable::Instance(void) +{ + return instance; +} + +void StringTable::AddString(const char *str, bool copyString) +{ + StrAndBool sab; + sab.b=copyString; + if (copyString) + { + sab.str = (char*) rakMalloc_Ex( strlen(str)+1, _FILE_AND_LINE_ ); + strcpy(sab.str, str); + } + else + { + sab.str=(char*)str; + } + + // If it asserts inside here you are adding duplicate strings. + orderedStringList.Insert(sab.str,sab, true, _FILE_AND_LINE_); + + // If this assert hits you need to increase the range of StringTableType + RakAssert(orderedStringList.Size() < (StringTableType)-1); + +} +void StringTable::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output ) +{ + unsigned index; + bool objectExists; + // This is fast because the list is kept ordered. + index=orderedStringList.GetIndexFromKey((char*)input, &objectExists); + if (objectExists) + { + output->Write(true); + output->Write((StringTableType)index); + } + else + { + LogStringNotFound(input); + output->Write(false); + StringCompressor::Instance()->EncodeString(input, maxCharsToWrite, output); + } +} + +bool StringTable::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input ) +{ + bool hasIndex=false; + RakAssert(maxCharsToWrite>0); + + if (maxCharsToWrite==0) + return false; + if (!input->Read(hasIndex)) + return false; + if (hasIndex==false) + { + StringCompressor::Instance()->DecodeString(output, maxCharsToWrite, input); + } + else + { + StringTableType index; + if (!input->Read(index)) + return false; + if (index >= orderedStringList.Size()) + { +#ifdef _DEBUG + // Critical error - got a string index out of range, which means AddString was called more times on the remote system than on this system. + // All systems must call AddString the same number of types, with the same strings in the same order. + RakAssert(0); +#endif + return false; + } + + strncpy(output, orderedStringList[index].str, maxCharsToWrite); + output[maxCharsToWrite-1]=0; + } + + return true; +} +void StringTable::LogStringNotFound(const char *strName) +{ + (void) strName; + +#ifdef _DEBUG + RAKNET_DEBUG_PRINTF("Efficiency Warning! Unregistered String %s sent to StringTable.\n", strName); +#endif +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/StringTable.h b/project/lib_projects/raknet/jni/RaknetSources/StringTable.h index f886bb7..8e7b3b9 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/StringTable.h +++ b/project/lib_projects/raknet/jni/RaknetSources/StringTable.h @@ -1,96 +1,96 @@ -/// \file -/// \brief A simple class to encode and decode known strings based on a lookup table. Similar to the StringCompressor class. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __STRING_TABLE_H -#define __STRING_TABLE_H - -#include "DS_OrderedList.h" -#include "Export.h" -#include "RakMemoryOverride.h" - -/// Forward declaration -namespace RakNet -{ - class BitStream; -}; - -/// StringTableType should be the smallest type possible, or else it defeats the purpose of the StringTable class, which is to save bandwidth. -typedef unsigned char StringTableType; - -/// The string plus a bool telling us if this string was copied or not. -struct StrAndBool -{ - char *str; - bool b; -}; - -namespace RakNet -{ - int RAK_DLL_EXPORT StrAndBoolComp( char *const &key, const StrAndBool &data ); - - /// \details This is an even more efficient alternative to StringCompressor in that it writes a single byte from a lookup table and only does compression.
      - /// if the string does not already exist in the table.
      - /// All string tables must match on all systems - hence you must add all the strings in the same order on all systems.
      - /// Furthermore, this must be done before sending packets that use this class, since the strings are ordered for fast lookup. Adding after that time would mess up all the indices so don't do it.
      - /// Don't use this class to write strings which were not previously registered with AddString, since you just waste bandwidth then. Use StringCompressor instead. - /// \brief Writes a string index, instead of the whole string - class RAK_DLL_EXPORT StringTable - { - public: - - // Destructor - ~StringTable(); - - /// static function because only static functions can access static members - /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. - /// \return the unique instance of the StringTable - static StringTable* Instance(void); - - /// Add a string to the string table. - /// \param[in] str The string to add to the string table - /// \param[in] copyString true to make a copy of the passed string (takes more memory), false to not do so (if your string is in static memory). - void AddString(const char *str, bool copyString); - - /// Writes input to output, compressed. Takes care of the null terminator for you. - /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. - /// \param[in] input Pointer to an ASCII string - /// \param[in] maxCharsToWrite The size of \a input - /// \param[out] output The bitstream to write the compressed string to - void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output ); - - /// Writes input to output, uncompressed. Takes care of the null terminator for you. - /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. - /// \param[out] output A block of bytes to receive the output - /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. - /// \param[in] input The bitstream containing the compressed string - bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input ); - - /// Used so I can allocate and deallocate this singleton at runtime - static void AddReference(void); - - /// Used so I can allocate and deallocate this singleton at runtime - static void RemoveReference(void); - - /// Private Constructor - StringTable(); - - protected: - /// Called when you mess up and send a string using this class that was not registered with AddString - /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. - void LogStringNotFound(const char *strName); - - /// Singleton instance - static StringTable *instance; - static int referenceCount; - - DataStructures::OrderedList orderedStringList; - }; -} - - -#endif +/// \file +/// \brief A simple class to encode and decode known strings based on a lookup table. Similar to the StringCompressor class. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __STRING_TABLE_H +#define __STRING_TABLE_H + +#include "DS_OrderedList.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +/// Forward declaration +namespace RakNet +{ + class BitStream; +}; + +/// StringTableType should be the smallest type possible, or else it defeats the purpose of the StringTable class, which is to save bandwidth. +typedef unsigned char StringTableType; + +/// The string plus a bool telling us if this string was copied or not. +struct StrAndBool +{ + char *str; + bool b; +}; + +namespace RakNet +{ + int RAK_DLL_EXPORT StrAndBoolComp( char *const &key, const StrAndBool &data ); + + /// \details This is an even more efficient alternative to StringCompressor in that it writes a single byte from a lookup table and only does compression.
      + /// if the string does not already exist in the table.
      + /// All string tables must match on all systems - hence you must add all the strings in the same order on all systems.
      + /// Furthermore, this must be done before sending packets that use this class, since the strings are ordered for fast lookup. Adding after that time would mess up all the indices so don't do it.
      + /// Don't use this class to write strings which were not previously registered with AddString, since you just waste bandwidth then. Use StringCompressor instead. + /// \brief Writes a string index, instead of the whole string + class RAK_DLL_EXPORT StringTable + { + public: + + // Destructor + ~StringTable(); + + /// static function because only static functions can access static members + /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. + /// \return the unique instance of the StringTable + static StringTable* Instance(void); + + /// Add a string to the string table. + /// \param[in] str The string to add to the string table + /// \param[in] copyString true to make a copy of the passed string (takes more memory), false to not do so (if your string is in static memory). + void AddString(const char *str, bool copyString); + + /// Writes input to output, compressed. Takes care of the null terminator for you. + /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. + /// \param[in] input Pointer to an ASCII string + /// \param[in] maxCharsToWrite The size of \a input + /// \param[out] output The bitstream to write the compressed string to + void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output ); + + /// Writes input to output, uncompressed. Takes care of the null terminator for you. + /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. + /// \param[out] output A block of bytes to receive the output + /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. + /// \param[in] input The bitstream containing the compressed string + bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input ); + + /// Used so I can allocate and deallocate this singleton at runtime + static void AddReference(void); + + /// Used so I can allocate and deallocate this singleton at runtime + static void RemoveReference(void); + + /// Private Constructor + StringTable(); + + protected: + /// Called when you mess up and send a string using this class that was not registered with AddString + /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. + void LogStringNotFound(const char *strName); + + /// Singleton instance + static StringTable *instance; + static int referenceCount; + + DataStructures::OrderedList orderedStringList; + }; +} + + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/SuperFastHash.cpp b/project/lib_projects/raknet/jni/RaknetSources/SuperFastHash.cpp index f5626e9..f9c22b6 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SuperFastHash.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/SuperFastHash.cpp @@ -1,119 +1,119 @@ -#include "SuperFastHash.h" -#include "NativeTypes.h" -#include - -#if !defined(_WIN32) -#include -#endif - -#undef get16bits - -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#else -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif - -static const int INCREMENTAL_READ_BLOCK=65536; - -uint32_t SuperFastHash (const char * data, int length) -{ - // All this is necessary or the hash does not match SuperFastHashIncremental - int bytesRemaining=length; - unsigned int lastHash = length; - int offset=0; - while (bytesRemaining>=INCREMENTAL_READ_BLOCK) - { - lastHash=SuperFastHashIncremental (data+offset, INCREMENTAL_READ_BLOCK, lastHash ); - bytesRemaining-=INCREMENTAL_READ_BLOCK; - offset+=INCREMENTAL_READ_BLOCK; - } - if (bytesRemaining>0) - { - lastHash=SuperFastHashIncremental (data+offset, bytesRemaining, lastHash ); - } - return lastHash; - -// return SuperFastHashIncremental(data,len,len); -} -uint32_t SuperFastHashIncremental (const char * data, int len, unsigned int lastHash ) -{ - uint32_t hash = (uint32_t) lastHash; - uint32_t tmp; - int rem; - - if (len <= 0 || data == NULL) return 0; - - rem = len & 3; - len >>= 2; - - /* Main loop */ - for (;len > 0; len--) { - hash += get16bits (data); - tmp = (get16bits (data+2) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - data += 2*sizeof (uint16_t); - hash += hash >> 11; - } - - /* Handle end cases */ - switch (rem) { - case 3: hash += get16bits (data); - hash ^= hash << 16; - hash ^= data[sizeof (uint16_t)] << 18; - hash += hash >> 11; - break; - case 2: hash += get16bits (data); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: hash += *data; - hash ^= hash << 10; - hash += hash >> 1; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return (uint32_t) hash; - -} - -uint32_t SuperFastHashFile (const char * filename) -{ - FILE *fp = fopen(filename, "rb"); - if (fp==0) - return 0; - uint32_t hash = SuperFastHashFilePtr(fp); - fclose(fp); - return hash; -} - -uint32_t SuperFastHashFilePtr (FILE *fp) -{ - fseek(fp, 0, SEEK_END); - int length = ftell(fp); - fseek(fp, 0, SEEK_SET); - int bytesRemaining=length; - unsigned int lastHash = length; - char readBlock[INCREMENTAL_READ_BLOCK]; - while (bytesRemaining>=(int) sizeof(readBlock)) - { - fread(readBlock, sizeof(readBlock), 1, fp); - lastHash=SuperFastHashIncremental (readBlock, (int) sizeof(readBlock), lastHash ); - bytesRemaining-=(int) sizeof(readBlock); - } - if (bytesRemaining>0) - { - fread(readBlock, bytesRemaining, 1, fp); - lastHash=SuperFastHashIncremental (readBlock, bytesRemaining, lastHash ); - } - return lastHash; -} +#include "SuperFastHash.h" +#include "NativeTypes.h" +#include + +#if !defined(_WIN32) +#include +#endif + +#undef get16bits + +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#else +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif + +static const int INCREMENTAL_READ_BLOCK=65536; + +uint32_t SuperFastHash (const char * data, int length) +{ + // All this is necessary or the hash does not match SuperFastHashIncremental + int bytesRemaining=length; + unsigned int lastHash = length; + int offset=0; + while (bytesRemaining>=INCREMENTAL_READ_BLOCK) + { + lastHash=SuperFastHashIncremental (data+offset, INCREMENTAL_READ_BLOCK, lastHash ); + bytesRemaining-=INCREMENTAL_READ_BLOCK; + offset+=INCREMENTAL_READ_BLOCK; + } + if (bytesRemaining>0) + { + lastHash=SuperFastHashIncremental (data+offset, bytesRemaining, lastHash ); + } + return lastHash; + +// return SuperFastHashIncremental(data,len,len); +} +uint32_t SuperFastHashIncremental (const char * data, int len, unsigned int lastHash ) +{ + uint32_t hash = (uint32_t) lastHash; + uint32_t tmp; + int rem; + + if (len <= 0 || data == NULL) return 0; + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (;len > 0; len--) { + hash += get16bits (data); + tmp = (get16bits (data+2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2*sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: hash += get16bits (data); + hash ^= hash << 16; + hash ^= data[sizeof (uint16_t)] << 18; + hash += hash >> 11; + break; + case 2: hash += get16bits (data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += *data; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return (uint32_t) hash; + +} + +uint32_t SuperFastHashFile (const char * filename) +{ + FILE *fp = fopen(filename, "rb"); + if (fp==0) + return 0; + uint32_t hash = SuperFastHashFilePtr(fp); + fclose(fp); + return hash; +} + +uint32_t SuperFastHashFilePtr (FILE *fp) +{ + fseek(fp, 0, SEEK_END); + int length = ftell(fp); + fseek(fp, 0, SEEK_SET); + int bytesRemaining=length; + unsigned int lastHash = length; + char readBlock[INCREMENTAL_READ_BLOCK]; + while (bytesRemaining>=(int) sizeof(readBlock)) + { + fread(readBlock, sizeof(readBlock), 1, fp); + lastHash=SuperFastHashIncremental (readBlock, (int) sizeof(readBlock), lastHash ); + bytesRemaining-=(int) sizeof(readBlock); + } + if (bytesRemaining>0) + { + fread(readBlock, bytesRemaining, 1, fp); + lastHash=SuperFastHashIncremental (readBlock, bytesRemaining, lastHash ); + } + return lastHash; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/SuperFastHash.h b/project/lib_projects/raknet/jni/RaknetSources/SuperFastHash.h index fd8463c..acf91c4 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/SuperFastHash.h +++ b/project/lib_projects/raknet/jni/RaknetSources/SuperFastHash.h @@ -1,17 +1,17 @@ -#ifndef __SUPER_FAST_HASH_H -#define __SUPER_FAST_HASH_H - -#include -#include "NativeTypes.h" - -// From http://www.azillionmonkeys.com/qed/hash.html -// Author of main code is Paul Hsieh -// I just added some convenience functions -// Also note http://burtleburtle.net/bob/hash/doobs.html, which shows that this is 20% faster than the one on that page but has more collisions - -uint32_t SuperFastHash (const char * data, int length); -uint32_t SuperFastHashIncremental (const char * data, int len, unsigned int lastHash ); -uint32_t SuperFastHashFile (const char * filename); -uint32_t SuperFastHashFilePtr (FILE *fp); - -#endif +#ifndef __SUPER_FAST_HASH_H +#define __SUPER_FAST_HASH_H + +#include +#include "NativeTypes.h" + +// From http://www.azillionmonkeys.com/qed/hash.html +// Author of main code is Paul Hsieh +// I just added some convenience functions +// Also note http://burtleburtle.net/bob/hash/doobs.html, which shows that this is 20% faster than the one on that page but has more collisions + +uint32_t SuperFastHash (const char * data, int length); +uint32_t SuperFastHashIncremental (const char * data, int len, unsigned int lastHash ); +uint32_t SuperFastHashFile (const char * filename); +uint32_t SuperFastHashFilePtr (FILE *fp); + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/TCPInterface.cpp b/project/lib_projects/raknet/jni/RaknetSources/TCPInterface.cpp index 78826af..bb4a847 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TCPInterface.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/TCPInterface.cpp @@ -1,1218 +1,1218 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TCPInterface==1 - -/// \file -/// \brief A simple TCP based server allowing sends and receives. Can be connected to by a telnet client. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "TCPInterface.h" -#ifdef _WIN32 -typedef int socklen_t; - - -#else -#include -#include -#include -#endif -#include -#include "RakAssert.h" -#include -#include "RakAssert.h" -#include "RakSleep.h" -#include "StringCompressor.h" -#include "StringTable.h" -#include "Itoa.h" -#include "SocketLayer.h" -#include "SocketDefines.h" - -#ifdef _DO_PRINTF -#endif - -#ifdef _WIN32 -#include "WSAStartupSingleton.h" -#endif -namespace RakNet -{ -RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop); -RAK_THREAD_DECLARATION(ConnectionAttemptLoop); -} -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(TCPInterface,TCPInterface); - -TCPInterface::TCPInterface() -{ - isStarted=false; - threadRunning=false; - listenSocket=(SOCKET) -1; - remoteClients=0; - remoteClientsLength=0; - - StringCompressor::AddReference(); - RakNet::StringTable::AddReference(); - -#if OPEN_SSL_CLIENT_SUPPORT==1 - ctx=0; - meth=0; -#endif - -#ifdef _WIN32 - WSAStartupSingleton::AddRef(); -#endif -} -TCPInterface::~TCPInterface() -{ - Stop(); -#ifdef _WIN32 - WSAStartupSingleton::Deref(); -#endif - - RakNet::OP_DELETE_ARRAY(remoteClients,_FILE_AND_LINE_); - - StringCompressor::RemoveReference(); - RakNet::StringTable::RemoveReference(); -} -bool TCPInterface::Start(unsigned short port, unsigned short maxIncomingConnections, unsigned short maxConnections, int _threadPriority, unsigned short socketFamily) -{ - (void) socketFamily; - - if (isStarted) - return false; - - threadPriority=_threadPriority; - - if (threadPriority==-99999) - { - - -#if defined(_WIN32) - threadPriority=0; - - -#else - threadPriority=1000; -#endif - } - - isStarted=true; - if (maxConnections==0) - maxConnections=maxIncomingConnections; - if (maxConnections==0) - maxConnections=1; - remoteClientsLength=maxConnections; - remoteClients=RakNet::OP_NEW_ARRAY(maxConnections,_FILE_AND_LINE_); - - -#if RAKNET_SUPPORT_IPV6!=1 - if (maxIncomingConnections>0) - { - listenSocket = socket__(AF_INET, SOCK_STREAM, 0); - if ((int)listenSocket ==-1) - return false; - - struct sockaddr_in serverAddress; - memset(&serverAddress,0,sizeof(sockaddr_in)); - serverAddress.sin_family = AF_INET; - - serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); - - serverAddress.sin_port = htons(port); - - if (bind__(listenSocket,(struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0) - return false; - - listen__(listenSocket, maxIncomingConnections); - } -#else - listenSocket=INVALID_SOCKET; - if (maxIncomingConnections>0) - { - struct addrinfo hints; - memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty - hints.ai_family = socketFamily; // don't care IPv4 or IPv6 - hints.ai_socktype = SOCK_STREAM; // TCP sockets - hints.ai_flags = AI_PASSIVE; // fill in my IP for me - struct addrinfo *servinfo=0, *aip; // will point to the results - char portStr[32]; - Itoa(port,portStr,10); - - getaddrinfo(0, portStr, &hints, &servinfo); - for (aip = servinfo; aip != NULL; aip = aip->ai_next) - { - // Open socket. The address type depends on what - // getaddrinfo() gave us. - listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); - if (listenSocket != INVALID_SOCKET) - { - int ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); - if (ret>=0) - { - break; - } - else - { - closesocket__(listenSocket); - listenSocket=INVALID_SOCKET; - } - } - } - - if (listenSocket==INVALID_SOCKET) - return false; - - listen__(listenSocket, maxIncomingConnections); - } -#endif // #if RAKNET_SUPPORT_IPV6!=1 - - - // Start the update thread - int errorCode; - - - - - - errorCode = RakNet::RakThread::Create(UpdateTCPInterfaceLoop, this, threadPriority); - - - if (errorCode!=0) - return false; - - while (threadRunning==false) - RakSleep(0); - - return true; -} -void TCPInterface::Stop(void) -{ - if (isStarted==false) - return; - - unsigned i; -#if OPEN_SSL_CLIENT_SUPPORT==1 - for (i=0; i < remoteClientsLength; i++) - remoteClients[i].DisconnectSSL(); -#endif - - isStarted=false; - - if (listenSocket!=(SOCKET) -1) - { -#ifdef _WIN32 - shutdown__(listenSocket, SD_BOTH); - -#else - shutdown__(listenSocket, SHUT_RDWR); -#endif - closesocket__(listenSocket); - listenSocket=(SOCKET) -1; - } - - // Abort waiting connect calls - blockingSocketListMutex.Lock(); - for (i=0; i < blockingSocketList.Size(); i++) - { - closesocket__(blockingSocketList[i]); - } - blockingSocketListMutex.Unlock(); - - // Wait for the thread to stop - while ( threadRunning ) - RakSleep(15); - - RakSleep(100); - - // Stuff from here on to the end of the function is not threadsafe - for (i=0; i < (unsigned int) remoteClientsLength; i++) - { - closesocket__(remoteClients[i].socket); -#if OPEN_SSL_CLIENT_SUPPORT==1 - remoteClients[i].FreeSSL(); -#endif - } - remoteClientsLength=0; - RakNet::OP_DELETE_ARRAY(remoteClients,_FILE_AND_LINE_); - remoteClients=0; - - incomingMessages.Clear(_FILE_AND_LINE_); - newIncomingConnections.Clear(_FILE_AND_LINE_); - newRemoteClients.Clear(_FILE_AND_LINE_); - lostConnections.Clear(_FILE_AND_LINE_); - requestedCloseConnections.Clear(_FILE_AND_LINE_); - failedConnectionAttempts.Clear(_FILE_AND_LINE_); - completedConnectionAttempts.Clear(_FILE_AND_LINE_); - failedConnectionAttempts.Clear(_FILE_AND_LINE_); - for (i=0; i < headPush.Size(); i++) - DeallocatePacket(headPush[i]); - headPush.Clear(_FILE_AND_LINE_); - for (i=0; i < tailPush.Size(); i++) - DeallocatePacket(tailPush[i]); - tailPush.Clear(_FILE_AND_LINE_); - -#if OPEN_SSL_CLIENT_SUPPORT==1 - SSL_CTX_free (ctx); - startSSL.Clear(_FILE_AND_LINE_); - activeSSLConnections.Clear(false, _FILE_AND_LINE_); -#endif - - - - - -} -SystemAddress TCPInterface::Connect(const char* host, unsigned short remotePort, bool block, unsigned short socketFamily) -{ - if (threadRunning==false) - return UNASSIGNED_SYSTEM_ADDRESS; - - int newRemoteClientIndex=-1; - for (newRemoteClientIndex=0; newRemoteClientIndex < remoteClientsLength; newRemoteClientIndex++) - { - remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); - if (remoteClients[newRemoteClientIndex].isActive==false) - { - remoteClients[newRemoteClientIndex].SetActive(true); - remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - break; - } - remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - } - if (newRemoteClientIndex==-1) - return UNASSIGNED_SYSTEM_ADDRESS; - - if (block) - { - SystemAddress systemAddress; - systemAddress.FromString(host); - systemAddress.SetPort(remotePort); - systemAddress.systemIndex=(SystemIndex) newRemoteClientIndex; - char buffout[128]; - systemAddress.ToString(false,buffout); - - SOCKET sockfd = SocketConnect(buffout, remotePort, socketFamily); - if (sockfd==(SOCKET)-1) - { - remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); - remoteClients[newRemoteClientIndex].SetActive(false); - remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - - failedConnectionAttemptMutex.Lock(); - failedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); - failedConnectionAttemptMutex.Unlock(); - - return UNASSIGNED_SYSTEM_ADDRESS; - } - - remoteClients[newRemoteClientIndex].socket=sockfd; - remoteClients[newRemoteClientIndex].systemAddress=systemAddress; - - completedConnectionAttemptMutex.Lock(); - completedConnectionAttempts.Push(remoteClients[newRemoteClientIndex].systemAddress, _FILE_AND_LINE_ ); - completedConnectionAttemptMutex.Unlock(); - - return remoteClients[newRemoteClientIndex].systemAddress; - } - else - { - ThisPtrPlusSysAddr *s = RakNet::OP_NEW( _FILE_AND_LINE_ ); - s->systemAddress.FromStringExplicitPort(host,remotePort); - s->systemAddress.systemIndex=(SystemIndex) newRemoteClientIndex; - s->tcpInterface=this; - s->socketFamily=socketFamily; - - // Start the connection thread - int errorCode; - - - - - errorCode = RakNet::RakThread::Create(ConnectionAttemptLoop, s, threadPriority); - - if (errorCode!=0) - { - RakNet::OP_DELETE(s, _FILE_AND_LINE_); - failedConnectionAttempts.Push(s->systemAddress, _FILE_AND_LINE_ ); - } - return UNASSIGNED_SYSTEM_ADDRESS; - } -} -#if OPEN_SSL_CLIENT_SUPPORT==1 -void TCPInterface::StartSSLClient(SystemAddress systemAddress) -{ - if (ctx==0) - { - sharedSslMutex.Lock(); - SSLeay_add_ssl_algorithms(); - meth = (SSL_METHOD*) SSLv2_client_method(); - SSL_load_error_strings(); - ctx = SSL_CTX_new (meth); - RakAssert(ctx!=0); - sharedSslMutex.Unlock(); - } - - SystemAddress *id = startSSL.Allocate( _FILE_AND_LINE_ ); - *id=systemAddress; - startSSL.Push(id); - unsigned index = activeSSLConnections.GetIndexOf(systemAddress); - if (index==(unsigned)-1) - activeSSLConnections.Insert(systemAddress,_FILE_AND_LINE_); -} -bool TCPInterface::IsSSLActive(SystemAddress systemAddress) -{ - return activeSSLConnections.GetIndexOf(systemAddress)!=-1; -} -#endif -void TCPInterface::Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ) -{ - SendList( &data, &length, 1, systemAddress,broadcast ); -} -bool TCPInterface::SendList( const char **data, const unsigned int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ) -{ - if (isStarted==false) - return false; - if (data==0) - return false; - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS && broadcast==false) - return false; - unsigned int totalLength=0; - int i; - for (i=0; i < numParameters; i++) - { - if (lengths[i]>0) - totalLength+=lengths[i]; - } - if (totalLength==0) - return false; - - if (broadcast) - { - // Send to all, possible exception system - for (i=0; i < remoteClientsLength; i++) - { - if (remoteClients[i].systemAddress!=systemAddress) - { - remoteClients[i].SendOrBuffer(data, lengths, numParameters); - } - } - } - else - { - // Send to this player - if (systemAddress.systemIndexdeleteData) - { - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - incomingMessages.Deallocate(packet, _FILE_AND_LINE_); - } - else - { - // Came from userspace AllocatePacket - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(packet, _FILE_AND_LINE_); - } -} -Packet* TCPInterface::AllocatePacket(unsigned dataSize) -{ - Packet*p = RakNet::OP_NEW(_FILE_AND_LINE_); - p->data=(unsigned char*) rakMalloc_Ex(dataSize,_FILE_AND_LINE_); - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=false; - p->guid=UNASSIGNED_RAKNET_GUID; - p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - p->systemAddress.systemIndex=(SystemIndex)-1; - return p; -} -void TCPInterface::PushBackPacket( Packet *packet, bool pushAtHead ) -{ - if (pushAtHead) - headPush.Push(packet, _FILE_AND_LINE_ ); - else - tailPush.Push(packet, _FILE_AND_LINE_ ); -} -bool TCPInterface::WasStarted(void) const -{ - return threadRunning==true; -} -int TCPInterface::Base64Encoding(const char *inputData, int dataLength, char *outputData) -{ - // http://en.wikipedia.org/wiki/Base64 - - int outputOffset, charCount; - int write3Count; - outputOffset=0; - charCount=0; - int j; - - write3Count=dataLength/3; - for (j=0; j < write3Count; j++) - { - // 6 leftmost bits from first byte, shifted to bits 7,8 are 0 - outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // 4 low bits from the second byte and the two high bits from the third byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[((inputData[j*3+1] << 2) | (inputData[j*3+2] >> 6)) & 63]; // Third 6 bits - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Last 6 bits from the third byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[inputData[j*3+2] & 63]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - } - - if (dataLength % 3==1) - { - // One input byte remaining - outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Pad with two equals - outputData[outputOffset++]='='; - outputData[outputOffset++]='='; - } - else if (dataLength % 3==2) - { - // Two input bytes remaining - - // 6 leftmost bits from first byte, shifted to bits 7,8 are 0 - outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // 4 low bits from the second byte, followed by 00 - outputData[outputOffset++]=Base64Map()[(inputData[j*3+1] << 2) & 63]; // Third 6 bits - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Pad with one equal - outputData[outputOffset++]='='; - //outputData[outputOffset++]='='; - } - - // Append \r\n - outputData[outputOffset++]='\r'; - outputData[outputOffset++]='\n'; - outputData[outputOffset]=0; - - return outputOffset; -} -SystemAddress TCPInterface::HasCompletedConnectionAttempt(void) -{ - SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS; - completedConnectionAttemptMutex.Lock(); - if (completedConnectionAttempts.IsEmpty()==false) - sysAddr=completedConnectionAttempts.Pop(); - completedConnectionAttemptMutex.Unlock(); - return sysAddr; -} -SystemAddress TCPInterface::HasFailedConnectionAttempt(void) -{ - SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS; - failedConnectionAttemptMutex.Lock(); - if (failedConnectionAttempts.IsEmpty()==false) - sysAddr=failedConnectionAttempts.Pop(); - failedConnectionAttemptMutex.Unlock(); - return sysAddr; -} -SystemAddress TCPInterface::HasNewIncomingConnection(void) -{ - SystemAddress *out, out2; - out = newIncomingConnections.PopInaccurate(); - if (out) - { - out2=*out; - newIncomingConnections.Deallocate(out, _FILE_AND_LINE_); - return *out; - } - else - { - return UNASSIGNED_SYSTEM_ADDRESS; - } -} -SystemAddress TCPInterface::HasLostConnection(void) -{ - SystemAddress *out, out2; - out = lostConnections.PopInaccurate(); - if (out) - { - out2=*out; - lostConnections.Deallocate(out, _FILE_AND_LINE_); - return *out; - } - else - { - return UNASSIGNED_SYSTEM_ADDRESS; - } -} -void TCPInterface::GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const -{ - unsigned short systemCount=0; - unsigned short maxToWrite=*numberOfSystems; - for (int i=0; i < remoteClientsLength; i++) - { - if (remoteClients[i].isActive) - { - if (systemCount < maxToWrite) - remoteSystems[systemCount]=remoteClients[i].systemAddress; - systemCount++; - } - } - *numberOfSystems=systemCount; -} -unsigned short TCPInterface::GetConnectionCount(void) const -{ - unsigned short systemCount=0; - for (int i=0; i < remoteClientsLength; i++) - { - if (remoteClients[i].isActive) - systemCount++; - } - return systemCount; -} - -unsigned int TCPInterface::GetOutgoingDataBufferSize(SystemAddress systemAddress) const -{ - unsigned bytesWritten=0; - if (systemAddress.systemIndexh_addr, server->h_length); - - - - - - - blockingSocketListMutex.Lock(); - blockingSocketList.Insert(sockfd, _FILE_AND_LINE_); - blockingSocketListMutex.Unlock(); - - // This is blocking - connectResult = connect__( sockfd, ( struct sockaddr * ) &serverAddress, sizeof( struct sockaddr ) ); - -#else - - - struct addrinfo hints, *res; - int sockfd; - memset(&hints, 0, sizeof hints); - hints.ai_family = socketFamily; - hints.ai_socktype = SOCK_STREAM; - char portStr[32]; - Itoa(remotePort,portStr,10); - getaddrinfo(host, portStr, &hints, &res); - sockfd = socket__(res->ai_family, res->ai_socktype, res->ai_protocol); - blockingSocketListMutex.Lock(); - blockingSocketList.Insert(sockfd, _FILE_AND_LINE_); - blockingSocketListMutex.Unlock(); - connectResult=connect__(sockfd, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); // free the linked-list - -#endif // #if RAKNET_SUPPORT_IPV6!=1 - - if (connectResult==-1) - { - unsigned sockfdIndex; - blockingSocketListMutex.Lock(); - sockfdIndex=blockingSocketList.GetIndexOf(sockfd); - if (sockfdIndex!=(unsigned)-1) - blockingSocketList.RemoveAtIndexFast(sockfdIndex); - blockingSocketListMutex.Unlock(); - - closesocket__(sockfd); - return (SOCKET) -1; - } - - return sockfd; -} - -RAK_THREAD_DECLARATION(RakNet::ConnectionAttemptLoop) -{ - - - - TCPInterface::ThisPtrPlusSysAddr *s = (TCPInterface::ThisPtrPlusSysAddr *) arguments; - - - - SystemAddress systemAddress = s->systemAddress; - TCPInterface *tcpInterface = s->tcpInterface; - int newRemoteClientIndex=systemAddress.systemIndex; - unsigned short socketFamily = s->socketFamily; - RakNet::OP_DELETE(s, _FILE_AND_LINE_); - - char str1[64]; - systemAddress.ToString(false, str1); - SOCKET sockfd = tcpInterface->SocketConnect(str1, systemAddress.GetPort(), socketFamily); - if (sockfd==(SOCKET)-1) - { - tcpInterface->remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); - tcpInterface->remoteClients[newRemoteClientIndex].SetActive(false); - tcpInterface->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - - tcpInterface->failedConnectionAttemptMutex.Lock(); - tcpInterface->failedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); - tcpInterface->failedConnectionAttemptMutex.Unlock(); - return 0; - } - - tcpInterface->remoteClients[newRemoteClientIndex].socket=sockfd; - tcpInterface->remoteClients[newRemoteClientIndex].systemAddress=systemAddress; - - // Notify user that the connection attempt has completed. - if (tcpInterface->threadRunning) - { - tcpInterface->completedConnectionAttemptMutex.Lock(); - tcpInterface->completedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); - tcpInterface->completedConnectionAttemptMutex.Unlock(); - } - - - - - return 0; - -} - -RAK_THREAD_DECLARATION(RakNet::UpdateTCPInterfaceLoop) -{ - - - - TCPInterface * sts = ( TCPInterface * ) arguments; - - -// const int BUFF_SIZE=8096; - const unsigned int BUFF_SIZE=1048576; - //char data[ BUFF_SIZE ]; - char * data = (char*) rakMalloc_Ex(BUFF_SIZE,_FILE_AND_LINE_); - Packet *incomingMessage; - fd_set readFD, exceptionFD, writeFD; - sts->threadRunning=true; - -#if RAKNET_SUPPORT_IPV6!=1 - sockaddr_in sockAddr; - int sockAddrSize = sizeof(sockAddr); -#else - struct sockaddr_storage sockAddr; - socklen_t sockAddrSize = sizeof(sockAddr); -#endif - - int len; - SOCKET newSock; - int selectResult; - - - timeval tv; - tv.tv_sec=0; - tv.tv_usec=30000; - - - while (sts->isStarted) - { -#if OPEN_SSL_CLIENT_SUPPORT==1 - SystemAddress *sslSystemAddress; - sslSystemAddress = sts->startSSL.PopInaccurate(); - if (sslSystemAddress) - { - if (sslSystemAddress->systemIndex>=0 && - sslSystemAddress->systemIndexremoteClientsLength && - sts->remoteClients[sslSystemAddress->systemIndex].systemAddress==*sslSystemAddress) - { - sts->remoteClients[sslSystemAddress->systemIndex].InitSSL(sts->ctx,sts->meth); - } - else - { - for (int i=0; i < sts->remoteClientsLength; i++) - { - sts->remoteClients[i].isActiveMutex.Lock(); - if (sts->remoteClients[i].isActive && sts->remoteClients[i].systemAddress==*sslSystemAddress) - { - if (sts->remoteClients[i].ssl==0) - sts->remoteClients[i].InitSSL(sts->ctx,sts->meth); - } - sts->remoteClients[i].isActiveMutex.Unlock(); - } - } - sts->startSSL.Deallocate(sslSystemAddress,_FILE_AND_LINE_); - } -#endif - - - SOCKET largestDescriptor=0; // see select__()'s first parameter's documentation under linux - - - // Linux' select__() implementation changes the timeout - - tv.tv_sec=0; - tv.tv_usec=30000; - - - while (1) - { - // Reset readFD, writeFD, and exceptionFD since select seems to clear it - FD_ZERO(&readFD); - FD_ZERO(&exceptionFD); - FD_ZERO(&writeFD); -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - largestDescriptor=0; - if (sts->listenSocket!=(SOCKET) -1) - { - FD_SET(sts->listenSocket, &readFD); - FD_SET(sts->listenSocket, &exceptionFD); - largestDescriptor = sts->listenSocket; // @see largestDescriptor def - } - - unsigned i; - for (i=0; i < (unsigned int) sts->remoteClientsLength; i++) - { - sts->remoteClients[i].isActiveMutex.Lock(); - if (sts->remoteClients[i].isActive && sts->remoteClients[i].socket!=INVALID_SOCKET) - { - FD_SET(sts->remoteClients[i].socket, &readFD); - FD_SET(sts->remoteClients[i].socket, &exceptionFD); - if (sts->remoteClients[i].outgoingData.GetBytesWritten()>0) - FD_SET(sts->remoteClients[i].socket, &writeFD); - if(sts->remoteClients[i].socket > largestDescriptor) // @see largestDescriptorDef - largestDescriptor = sts->remoteClients[i].socket; - } - sts->remoteClients[i].isActiveMutex.Unlock(); - } - -#ifdef _MSC_VER -#pragma warning( disable : 4244 ) // warning C4127: conditional expression is constant -#endif - - - selectResult=(int) select__(largestDescriptor+1, &readFD, &writeFD, &exceptionFD, &tv); - - - - - if (selectResult<=0) - break; - - if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &readFD)) - { - newSock = accept__(sts->listenSocket, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize); - - if (newSock != (SOCKET) -1) - { - int newRemoteClientIndex=-1; - for (newRemoteClientIndex=0; newRemoteClientIndex < sts->remoteClientsLength; newRemoteClientIndex++) - { - sts->remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); - if (sts->remoteClients[newRemoteClientIndex].isActive==false) - { - sts->remoteClients[newRemoteClientIndex].socket=newSock; - -#if RAKNET_SUPPORT_IPV6!=1 - sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_addr.s_addr=sockAddr.sin_addr.s_addr; - sts->remoteClients[newRemoteClientIndex].systemAddress.SetPortNetworkOrder( sockAddr.sin_port); - sts->remoteClients[newRemoteClientIndex].systemAddress.systemIndex=newRemoteClientIndex; -#else - if (sockAddr.ss_family==AF_INET) - { - memcpy(&sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4,(sockaddr_in *)&sockAddr,sizeof(sockaddr_in)); - // sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_port=ntohs( sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_port ); - } - else - { - memcpy(&sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6,(sockaddr_in6 *)&sockAddr,sizeof(sockaddr_in6)); - // sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6.sin6_port=ntohs( sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6.sin6_port ); - } - -#endif // #if RAKNET_SUPPORT_IPV6!=1 - sts->remoteClients[newRemoteClientIndex].SetActive(true); - sts->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - - - SystemAddress *newConnectionSystemAddress=sts->newIncomingConnections.Allocate( _FILE_AND_LINE_ ); - *newConnectionSystemAddress=sts->remoteClients[newRemoteClientIndex].systemAddress; - sts->newIncomingConnections.Push(newConnectionSystemAddress); - - break; - } - sts->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - } - if (newRemoteClientIndex==-1) - { - closesocket__(sts->listenSocket); - } - } - else - { -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Error: connection failed\n"); -#endif - } - } - else if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &exceptionFD)) - { -#ifdef _DO_PRINTF - int err; - int errlen = sizeof(err); - getsockopt__(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen); - RAKNET_DEBUG_PRINTF("Socket error %s on listening socket\n", err); -#endif - } - - { - i=0; - while (i < (unsigned int) sts->remoteClientsLength) - { - if (sts->remoteClients[i].isActive==false) - { - i++; - continue; - } - - if (FD_ISSET(sts->remoteClients[i].socket, &exceptionFD)) - { -// #ifdef _DO_PRINTF -// if (sts->listenSocket!=-1) -// { -// int err; -// int errlen = sizeof(err); -// getsockopt__(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen); -// in_addr in; -// in.s_addr = sts->remoteClients[i].systemAddress.binaryAddress; -// RAKNET_DEBUG_PRINTF("Socket error %i on %s:%i\n", err,inet_ntoa( in ), sts->remoteClients[i].systemAddress.GetPort() ); -// } -// -// #endif - // Connection lost abruptly - SystemAddress *lostConnectionSystemAddress=sts->lostConnections.Allocate( _FILE_AND_LINE_ ); - *lostConnectionSystemAddress=sts->remoteClients[i].systemAddress; - sts->lostConnections.Push(lostConnectionSystemAddress); - sts->remoteClients[i].isActiveMutex.Lock(); - sts->remoteClients[i].SetActive(false); - sts->remoteClients[i].isActiveMutex.Unlock(); - } - else - { - if (FD_ISSET(sts->remoteClients[i].socket, &readFD)) - { - // if recv returns 0 this was a graceful close - len = sts->remoteClients[i].Recv(data,BUFF_SIZE); - - - // removeme -// data[len]=0; -// printf(data); - - if (len>0) - { - incomingMessage=sts->incomingMessages.Allocate( _FILE_AND_LINE_ ); - incomingMessage->data = (unsigned char*) rakMalloc_Ex( len+1, _FILE_AND_LINE_ ); - memcpy(incomingMessage->data, data, len); - incomingMessage->data[len]=0; // Null terminate this so we can print it out as regular strings. This is different from RakNet which does not do this. -// printf("RECV: %s\n",incomingMessage->data); - incomingMessage->length=len; - incomingMessage->deleteData=true; // actually means came from SPSC, rather than AllocatePacket - incomingMessage->systemAddress=sts->remoteClients[i].systemAddress; - sts->incomingMessages.Push(incomingMessage); - } - else - { - // Connection lost gracefully - SystemAddress *lostConnectionSystemAddress=sts->lostConnections.Allocate( _FILE_AND_LINE_ ); - *lostConnectionSystemAddress=sts->remoteClients[i].systemAddress; - sts->lostConnections.Push(lostConnectionSystemAddress); - sts->remoteClients[i].isActiveMutex.Lock(); - sts->remoteClients[i].SetActive(false); - sts->remoteClients[i].isActiveMutex.Unlock(); - continue; - } - } - if (FD_ISSET(sts->remoteClients[i].socket, &writeFD)) - { - RemoteClient *rc = &sts->remoteClients[i]; - unsigned int bytesInBuffer; - int bytesAvailable; - int bytesSent; - rc->outgoingDataMutex.Lock(); - bytesInBuffer=rc->outgoingData.GetBytesWritten(); - if (bytesInBuffer>0) - { - unsigned int contiguousLength; - char* contiguousBytesPointer = rc->outgoingData.PeekContiguousBytes(&contiguousLength); - if (contiguousLength < (unsigned int) BUFF_SIZE && contiguousLength BUFF_SIZE) - bytesAvailable=BUFF_SIZE; - else - bytesAvailable=bytesInBuffer; - rc->outgoingData.ReadBytes(data,bytesAvailable,true); - bytesSent=rc->Send(data,bytesAvailable); - } - else - { - bytesSent=rc->Send(contiguousBytesPointer,contiguousLength); - } - - rc->outgoingData.IncrementReadOffset(bytesSent); - bytesInBuffer=rc->outgoingData.GetBytesWritten(); - } - rc->outgoingDataMutex.Unlock(); - } - - i++; // Nothing deleted so increment the index - } - } - } - } - - // Sleep 0 on Linux monopolizes the CPU - RakSleep(30); - } - sts->threadRunning=false; - - rakFree_Ex(data,_FILE_AND_LINE_); - - - - - return 0; - -} - -void RemoteClient::SetActive(bool a) -{ - if (isActive != a) - { - isActive=a; - Reset(); - if (isActive==false && socket!=INVALID_SOCKET) - { - closesocket__(socket); - socket=INVALID_SOCKET; - } - } -} -void RemoteClient::SendOrBuffer(const char **data, const unsigned int *lengths, const int numParameters) -{ - // True can save memory and buffer copies, but gives worse performance overall - // Do not use true for the XBOX, as it just locks up - const bool ALLOW_SEND_FROM_USER_THREAD=false; - - int parameterIndex; - if (isActive==false) - return; - parameterIndex=0; - for (; parameterIndex < numParameters; parameterIndex++) - { - outgoingDataMutex.Lock(); - if (ALLOW_SEND_FROM_USER_THREAD && outgoingData.GetBytesWritten()==0) - { - outgoingDataMutex.Unlock(); - int bytesSent = Send(data[parameterIndex],lengths[parameterIndex]); - if (bytesSent<(int) lengths[parameterIndex]) - { - // Push remainder - outgoingDataMutex.Lock(); - outgoingData.WriteBytes(data[parameterIndex]+bytesSent,lengths[parameterIndex]-bytesSent,_FILE_AND_LINE_); - outgoingDataMutex.Unlock(); - } - } - else - { - outgoingData.WriteBytes(data[parameterIndex],lengths[parameterIndex],_FILE_AND_LINE_); - outgoingDataMutex.Unlock(); - } - } -} -#if OPEN_SSL_CLIENT_SUPPORT==1 -void RemoteClient::InitSSL(SSL_CTX* ctx, SSL_METHOD *meth) -{ - (void) meth; - - ssl = SSL_new (ctx); - RakAssert(ssl); - SSL_set_fd (ssl, socket); - SSL_connect (ssl); -} -void RemoteClient::DisconnectSSL(void) -{ - if (ssl) - SSL_shutdown (ssl); /* send SSL/TLS close_notify */ -} -void RemoteClient::FreeSSL(void) -{ - if (ssl) - SSL_free (ssl); -} -int RemoteClient::Send(const char *data, unsigned int length) -{ - int err; - if (ssl) - { - return SSL_write (ssl, data, length); - } - else - return send__(socket, data, length, 0); -} -int RemoteClient::Recv(char *data, const int dataSize) -{ - if (ssl) - return SSL_read (ssl, data, dataSize); - else - return recv__(socket, data, dataSize, 0); -} -#else -int RemoteClient::Send(const char *data, unsigned int length) -{ - return send__(socket, data, length, 0); -} -int RemoteClient::Recv(char *data, const int dataSize) -{ - return recv__(socket, data, dataSize, 0); -} -#endif - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TCPInterface==1 + +/// \file +/// \brief A simple TCP based server allowing sends and receives. Can be connected to by a telnet client. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "TCPInterface.h" +#ifdef _WIN32 +typedef int socklen_t; + + +#else +#include +#include +#include +#endif +#include +#include "RakAssert.h" +#include +#include "RakAssert.h" +#include "RakSleep.h" +#include "StringCompressor.h" +#include "StringTable.h" +#include "Itoa.h" +#include "SocketLayer.h" +#include "SocketDefines.h" + +#ifdef _DO_PRINTF +#endif + +#ifdef _WIN32 +#include "WSAStartupSingleton.h" +#endif +namespace RakNet +{ +RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop); +RAK_THREAD_DECLARATION(ConnectionAttemptLoop); +} +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(TCPInterface,TCPInterface); + +TCPInterface::TCPInterface() +{ + isStarted=false; + threadRunning=false; + listenSocket=(SOCKET) -1; + remoteClients=0; + remoteClientsLength=0; + + StringCompressor::AddReference(); + RakNet::StringTable::AddReference(); + +#if OPEN_SSL_CLIENT_SUPPORT==1 + ctx=0; + meth=0; +#endif + +#ifdef _WIN32 + WSAStartupSingleton::AddRef(); +#endif +} +TCPInterface::~TCPInterface() +{ + Stop(); +#ifdef _WIN32 + WSAStartupSingleton::Deref(); +#endif + + RakNet::OP_DELETE_ARRAY(remoteClients,_FILE_AND_LINE_); + + StringCompressor::RemoveReference(); + RakNet::StringTable::RemoveReference(); +} +bool TCPInterface::Start(unsigned short port, unsigned short maxIncomingConnections, unsigned short maxConnections, int _threadPriority, unsigned short socketFamily) +{ + (void) socketFamily; + + if (isStarted) + return false; + + threadPriority=_threadPriority; + + if (threadPriority==-99999) + { + + +#if defined(_WIN32) + threadPriority=0; + + +#else + threadPriority=1000; +#endif + } + + isStarted=true; + if (maxConnections==0) + maxConnections=maxIncomingConnections; + if (maxConnections==0) + maxConnections=1; + remoteClientsLength=maxConnections; + remoteClients=RakNet::OP_NEW_ARRAY(maxConnections,_FILE_AND_LINE_); + + +#if RAKNET_SUPPORT_IPV6!=1 + if (maxIncomingConnections>0) + { + listenSocket = socket__(AF_INET, SOCK_STREAM, 0); + if ((int)listenSocket ==-1) + return false; + + struct sockaddr_in serverAddress; + memset(&serverAddress,0,sizeof(sockaddr_in)); + serverAddress.sin_family = AF_INET; + + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + + serverAddress.sin_port = htons(port); + + if (bind__(listenSocket,(struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0) + return false; + + listen__(listenSocket, maxIncomingConnections); + } +#else + listenSocket=INVALID_SOCKET; + if (maxIncomingConnections>0) + { + struct addrinfo hints; + memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty + hints.ai_family = socketFamily; // don't care IPv4 or IPv6 + hints.ai_socktype = SOCK_STREAM; // TCP sockets + hints.ai_flags = AI_PASSIVE; // fill in my IP for me + struct addrinfo *servinfo=0, *aip; // will point to the results + char portStr[32]; + Itoa(port,portStr,10); + + getaddrinfo(0, portStr, &hints, &servinfo); + for (aip = servinfo; aip != NULL; aip = aip->ai_next) + { + // Open socket. The address type depends on what + // getaddrinfo() gave us. + listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (listenSocket != INVALID_SOCKET) + { + int ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); + if (ret>=0) + { + break; + } + else + { + closesocket__(listenSocket); + listenSocket=INVALID_SOCKET; + } + } + } + + if (listenSocket==INVALID_SOCKET) + return false; + + listen__(listenSocket, maxIncomingConnections); + } +#endif // #if RAKNET_SUPPORT_IPV6!=1 + + + // Start the update thread + int errorCode; + + + + + + errorCode = RakNet::RakThread::Create(UpdateTCPInterfaceLoop, this, threadPriority); + + + if (errorCode!=0) + return false; + + while (threadRunning==false) + RakSleep(0); + + return true; +} +void TCPInterface::Stop(void) +{ + if (isStarted==false) + return; + + unsigned i; +#if OPEN_SSL_CLIENT_SUPPORT==1 + for (i=0; i < remoteClientsLength; i++) + remoteClients[i].DisconnectSSL(); +#endif + + isStarted=false; + + if (listenSocket!=(SOCKET) -1) + { +#ifdef _WIN32 + shutdown__(listenSocket, SD_BOTH); + +#else + shutdown__(listenSocket, SHUT_RDWR); +#endif + closesocket__(listenSocket); + listenSocket=(SOCKET) -1; + } + + // Abort waiting connect calls + blockingSocketListMutex.Lock(); + for (i=0; i < blockingSocketList.Size(); i++) + { + closesocket__(blockingSocketList[i]); + } + blockingSocketListMutex.Unlock(); + + // Wait for the thread to stop + while ( threadRunning ) + RakSleep(15); + + RakSleep(100); + + // Stuff from here on to the end of the function is not threadsafe + for (i=0; i < (unsigned int) remoteClientsLength; i++) + { + closesocket__(remoteClients[i].socket); +#if OPEN_SSL_CLIENT_SUPPORT==1 + remoteClients[i].FreeSSL(); +#endif + } + remoteClientsLength=0; + RakNet::OP_DELETE_ARRAY(remoteClients,_FILE_AND_LINE_); + remoteClients=0; + + incomingMessages.Clear(_FILE_AND_LINE_); + newIncomingConnections.Clear(_FILE_AND_LINE_); + newRemoteClients.Clear(_FILE_AND_LINE_); + lostConnections.Clear(_FILE_AND_LINE_); + requestedCloseConnections.Clear(_FILE_AND_LINE_); + failedConnectionAttempts.Clear(_FILE_AND_LINE_); + completedConnectionAttempts.Clear(_FILE_AND_LINE_); + failedConnectionAttempts.Clear(_FILE_AND_LINE_); + for (i=0; i < headPush.Size(); i++) + DeallocatePacket(headPush[i]); + headPush.Clear(_FILE_AND_LINE_); + for (i=0; i < tailPush.Size(); i++) + DeallocatePacket(tailPush[i]); + tailPush.Clear(_FILE_AND_LINE_); + +#if OPEN_SSL_CLIENT_SUPPORT==1 + SSL_CTX_free (ctx); + startSSL.Clear(_FILE_AND_LINE_); + activeSSLConnections.Clear(false, _FILE_AND_LINE_); +#endif + + + + + +} +SystemAddress TCPInterface::Connect(const char* host, unsigned short remotePort, bool block, unsigned short socketFamily) +{ + if (threadRunning==false) + return UNASSIGNED_SYSTEM_ADDRESS; + + int newRemoteClientIndex=-1; + for (newRemoteClientIndex=0; newRemoteClientIndex < remoteClientsLength; newRemoteClientIndex++) + { + remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); + if (remoteClients[newRemoteClientIndex].isActive==false) + { + remoteClients[newRemoteClientIndex].SetActive(true); + remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + break; + } + remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + } + if (newRemoteClientIndex==-1) + return UNASSIGNED_SYSTEM_ADDRESS; + + if (block) + { + SystemAddress systemAddress; + systemAddress.FromString(host); + systemAddress.SetPort(remotePort); + systemAddress.systemIndex=(SystemIndex) newRemoteClientIndex; + char buffout[128]; + systemAddress.ToString(false,buffout); + + SOCKET sockfd = SocketConnect(buffout, remotePort, socketFamily); + if (sockfd==(SOCKET)-1) + { + remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); + remoteClients[newRemoteClientIndex].SetActive(false); + remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + + failedConnectionAttemptMutex.Lock(); + failedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); + failedConnectionAttemptMutex.Unlock(); + + return UNASSIGNED_SYSTEM_ADDRESS; + } + + remoteClients[newRemoteClientIndex].socket=sockfd; + remoteClients[newRemoteClientIndex].systemAddress=systemAddress; + + completedConnectionAttemptMutex.Lock(); + completedConnectionAttempts.Push(remoteClients[newRemoteClientIndex].systemAddress, _FILE_AND_LINE_ ); + completedConnectionAttemptMutex.Unlock(); + + return remoteClients[newRemoteClientIndex].systemAddress; + } + else + { + ThisPtrPlusSysAddr *s = RakNet::OP_NEW( _FILE_AND_LINE_ ); + s->systemAddress.FromStringExplicitPort(host,remotePort); + s->systemAddress.systemIndex=(SystemIndex) newRemoteClientIndex; + s->tcpInterface=this; + s->socketFamily=socketFamily; + + // Start the connection thread + int errorCode; + + + + + errorCode = RakNet::RakThread::Create(ConnectionAttemptLoop, s, threadPriority); + + if (errorCode!=0) + { + RakNet::OP_DELETE(s, _FILE_AND_LINE_); + failedConnectionAttempts.Push(s->systemAddress, _FILE_AND_LINE_ ); + } + return UNASSIGNED_SYSTEM_ADDRESS; + } +} +#if OPEN_SSL_CLIENT_SUPPORT==1 +void TCPInterface::StartSSLClient(SystemAddress systemAddress) +{ + if (ctx==0) + { + sharedSslMutex.Lock(); + SSLeay_add_ssl_algorithms(); + meth = (SSL_METHOD*) SSLv2_client_method(); + SSL_load_error_strings(); + ctx = SSL_CTX_new (meth); + RakAssert(ctx!=0); + sharedSslMutex.Unlock(); + } + + SystemAddress *id = startSSL.Allocate( _FILE_AND_LINE_ ); + *id=systemAddress; + startSSL.Push(id); + unsigned index = activeSSLConnections.GetIndexOf(systemAddress); + if (index==(unsigned)-1) + activeSSLConnections.Insert(systemAddress,_FILE_AND_LINE_); +} +bool TCPInterface::IsSSLActive(SystemAddress systemAddress) +{ + return activeSSLConnections.GetIndexOf(systemAddress)!=-1; +} +#endif +void TCPInterface::Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ) +{ + SendList( &data, &length, 1, systemAddress,broadcast ); +} +bool TCPInterface::SendList( const char **data, const unsigned int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ) +{ + if (isStarted==false) + return false; + if (data==0) + return false; + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS && broadcast==false) + return false; + unsigned int totalLength=0; + int i; + for (i=0; i < numParameters; i++) + { + if (lengths[i]>0) + totalLength+=lengths[i]; + } + if (totalLength==0) + return false; + + if (broadcast) + { + // Send to all, possible exception system + for (i=0; i < remoteClientsLength; i++) + { + if (remoteClients[i].systemAddress!=systemAddress) + { + remoteClients[i].SendOrBuffer(data, lengths, numParameters); + } + } + } + else + { + // Send to this player + if (systemAddress.systemIndexdeleteData) + { + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + incomingMessages.Deallocate(packet, _FILE_AND_LINE_); + } + else + { + // Came from userspace AllocatePacket + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(packet, _FILE_AND_LINE_); + } +} +Packet* TCPInterface::AllocatePacket(unsigned dataSize) +{ + Packet*p = RakNet::OP_NEW(_FILE_AND_LINE_); + p->data=(unsigned char*) rakMalloc_Ex(dataSize,_FILE_AND_LINE_); + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=false; + p->guid=UNASSIGNED_RAKNET_GUID; + p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + p->systemAddress.systemIndex=(SystemIndex)-1; + return p; +} +void TCPInterface::PushBackPacket( Packet *packet, bool pushAtHead ) +{ + if (pushAtHead) + headPush.Push(packet, _FILE_AND_LINE_ ); + else + tailPush.Push(packet, _FILE_AND_LINE_ ); +} +bool TCPInterface::WasStarted(void) const +{ + return threadRunning==true; +} +int TCPInterface::Base64Encoding(const char *inputData, int dataLength, char *outputData) +{ + // http://en.wikipedia.org/wiki/Base64 + + int outputOffset, charCount; + int write3Count; + outputOffset=0; + charCount=0; + int j; + + write3Count=dataLength/3; + for (j=0; j < write3Count; j++) + { + // 6 leftmost bits from first byte, shifted to bits 7,8 are 0 + outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // 4 low bits from the second byte and the two high bits from the third byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[((inputData[j*3+1] << 2) | (inputData[j*3+2] >> 6)) & 63]; // Third 6 bits + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Last 6 bits from the third byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[inputData[j*3+2] & 63]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + } + + if (dataLength % 3==1) + { + // One input byte remaining + outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Pad with two equals + outputData[outputOffset++]='='; + outputData[outputOffset++]='='; + } + else if (dataLength % 3==2) + { + // Two input bytes remaining + + // 6 leftmost bits from first byte, shifted to bits 7,8 are 0 + outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // 4 low bits from the second byte, followed by 00 + outputData[outputOffset++]=Base64Map()[(inputData[j*3+1] << 2) & 63]; // Third 6 bits + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Pad with one equal + outputData[outputOffset++]='='; + //outputData[outputOffset++]='='; + } + + // Append \r\n + outputData[outputOffset++]='\r'; + outputData[outputOffset++]='\n'; + outputData[outputOffset]=0; + + return outputOffset; +} +SystemAddress TCPInterface::HasCompletedConnectionAttempt(void) +{ + SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS; + completedConnectionAttemptMutex.Lock(); + if (completedConnectionAttempts.IsEmpty()==false) + sysAddr=completedConnectionAttempts.Pop(); + completedConnectionAttemptMutex.Unlock(); + return sysAddr; +} +SystemAddress TCPInterface::HasFailedConnectionAttempt(void) +{ + SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS; + failedConnectionAttemptMutex.Lock(); + if (failedConnectionAttempts.IsEmpty()==false) + sysAddr=failedConnectionAttempts.Pop(); + failedConnectionAttemptMutex.Unlock(); + return sysAddr; +} +SystemAddress TCPInterface::HasNewIncomingConnection(void) +{ + SystemAddress *out, out2; + out = newIncomingConnections.PopInaccurate(); + if (out) + { + out2=*out; + newIncomingConnections.Deallocate(out, _FILE_AND_LINE_); + return *out; + } + else + { + return UNASSIGNED_SYSTEM_ADDRESS; + } +} +SystemAddress TCPInterface::HasLostConnection(void) +{ + SystemAddress *out, out2; + out = lostConnections.PopInaccurate(); + if (out) + { + out2=*out; + lostConnections.Deallocate(out, _FILE_AND_LINE_); + return *out; + } + else + { + return UNASSIGNED_SYSTEM_ADDRESS; + } +} +void TCPInterface::GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const +{ + unsigned short systemCount=0; + unsigned short maxToWrite=*numberOfSystems; + for (int i=0; i < remoteClientsLength; i++) + { + if (remoteClients[i].isActive) + { + if (systemCount < maxToWrite) + remoteSystems[systemCount]=remoteClients[i].systemAddress; + systemCount++; + } + } + *numberOfSystems=systemCount; +} +unsigned short TCPInterface::GetConnectionCount(void) const +{ + unsigned short systemCount=0; + for (int i=0; i < remoteClientsLength; i++) + { + if (remoteClients[i].isActive) + systemCount++; + } + return systemCount; +} + +unsigned int TCPInterface::GetOutgoingDataBufferSize(SystemAddress systemAddress) const +{ + unsigned bytesWritten=0; + if (systemAddress.systemIndexh_addr, server->h_length); + + + + + + + blockingSocketListMutex.Lock(); + blockingSocketList.Insert(sockfd, _FILE_AND_LINE_); + blockingSocketListMutex.Unlock(); + + // This is blocking + connectResult = connect__( sockfd, ( struct sockaddr * ) &serverAddress, sizeof( struct sockaddr ) ); + +#else + + + struct addrinfo hints, *res; + int sockfd; + memset(&hints, 0, sizeof hints); + hints.ai_family = socketFamily; + hints.ai_socktype = SOCK_STREAM; + char portStr[32]; + Itoa(remotePort,portStr,10); + getaddrinfo(host, portStr, &hints, &res); + sockfd = socket__(res->ai_family, res->ai_socktype, res->ai_protocol); + blockingSocketListMutex.Lock(); + blockingSocketList.Insert(sockfd, _FILE_AND_LINE_); + blockingSocketListMutex.Unlock(); + connectResult=connect__(sockfd, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); // free the linked-list + +#endif // #if RAKNET_SUPPORT_IPV6!=1 + + if (connectResult==-1) + { + unsigned sockfdIndex; + blockingSocketListMutex.Lock(); + sockfdIndex=blockingSocketList.GetIndexOf(sockfd); + if (sockfdIndex!=(unsigned)-1) + blockingSocketList.RemoveAtIndexFast(sockfdIndex); + blockingSocketListMutex.Unlock(); + + closesocket__(sockfd); + return (SOCKET) -1; + } + + return sockfd; +} + +RAK_THREAD_DECLARATION(RakNet::ConnectionAttemptLoop) +{ + + + + TCPInterface::ThisPtrPlusSysAddr *s = (TCPInterface::ThisPtrPlusSysAddr *) arguments; + + + + SystemAddress systemAddress = s->systemAddress; + TCPInterface *tcpInterface = s->tcpInterface; + int newRemoteClientIndex=systemAddress.systemIndex; + unsigned short socketFamily = s->socketFamily; + RakNet::OP_DELETE(s, _FILE_AND_LINE_); + + char str1[64]; + systemAddress.ToString(false, str1); + SOCKET sockfd = tcpInterface->SocketConnect(str1, systemAddress.GetPort(), socketFamily); + if (sockfd==(SOCKET)-1) + { + tcpInterface->remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); + tcpInterface->remoteClients[newRemoteClientIndex].SetActive(false); + tcpInterface->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + + tcpInterface->failedConnectionAttemptMutex.Lock(); + tcpInterface->failedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); + tcpInterface->failedConnectionAttemptMutex.Unlock(); + return 0; + } + + tcpInterface->remoteClients[newRemoteClientIndex].socket=sockfd; + tcpInterface->remoteClients[newRemoteClientIndex].systemAddress=systemAddress; + + // Notify user that the connection attempt has completed. + if (tcpInterface->threadRunning) + { + tcpInterface->completedConnectionAttemptMutex.Lock(); + tcpInterface->completedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); + tcpInterface->completedConnectionAttemptMutex.Unlock(); + } + + + + + return 0; + +} + +RAK_THREAD_DECLARATION(RakNet::UpdateTCPInterfaceLoop) +{ + + + + TCPInterface * sts = ( TCPInterface * ) arguments; + + +// const int BUFF_SIZE=8096; + const unsigned int BUFF_SIZE=1048576; + //char data[ BUFF_SIZE ]; + char * data = (char*) rakMalloc_Ex(BUFF_SIZE,_FILE_AND_LINE_); + Packet *incomingMessage; + fd_set readFD, exceptionFD, writeFD; + sts->threadRunning=true; + +#if RAKNET_SUPPORT_IPV6!=1 + sockaddr_in sockAddr; + int sockAddrSize = sizeof(sockAddr); +#else + struct sockaddr_storage sockAddr; + socklen_t sockAddrSize = sizeof(sockAddr); +#endif + + int len; + SOCKET newSock; + int selectResult; + + + timeval tv; + tv.tv_sec=0; + tv.tv_usec=30000; + + + while (sts->isStarted) + { +#if OPEN_SSL_CLIENT_SUPPORT==1 + SystemAddress *sslSystemAddress; + sslSystemAddress = sts->startSSL.PopInaccurate(); + if (sslSystemAddress) + { + if (sslSystemAddress->systemIndex>=0 && + sslSystemAddress->systemIndexremoteClientsLength && + sts->remoteClients[sslSystemAddress->systemIndex].systemAddress==*sslSystemAddress) + { + sts->remoteClients[sslSystemAddress->systemIndex].InitSSL(sts->ctx,sts->meth); + } + else + { + for (int i=0; i < sts->remoteClientsLength; i++) + { + sts->remoteClients[i].isActiveMutex.Lock(); + if (sts->remoteClients[i].isActive && sts->remoteClients[i].systemAddress==*sslSystemAddress) + { + if (sts->remoteClients[i].ssl==0) + sts->remoteClients[i].InitSSL(sts->ctx,sts->meth); + } + sts->remoteClients[i].isActiveMutex.Unlock(); + } + } + sts->startSSL.Deallocate(sslSystemAddress,_FILE_AND_LINE_); + } +#endif + + + SOCKET largestDescriptor=0; // see select__()'s first parameter's documentation under linux + + + // Linux' select__() implementation changes the timeout + + tv.tv_sec=0; + tv.tv_usec=30000; + + + while (1) + { + // Reset readFD, writeFD, and exceptionFD since select seems to clear it + FD_ZERO(&readFD); + FD_ZERO(&exceptionFD); + FD_ZERO(&writeFD); +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + largestDescriptor=0; + if (sts->listenSocket!=(SOCKET) -1) + { + FD_SET(sts->listenSocket, &readFD); + FD_SET(sts->listenSocket, &exceptionFD); + largestDescriptor = sts->listenSocket; // @see largestDescriptor def + } + + unsigned i; + for (i=0; i < (unsigned int) sts->remoteClientsLength; i++) + { + sts->remoteClients[i].isActiveMutex.Lock(); + if (sts->remoteClients[i].isActive && sts->remoteClients[i].socket!=INVALID_SOCKET) + { + FD_SET(sts->remoteClients[i].socket, &readFD); + FD_SET(sts->remoteClients[i].socket, &exceptionFD); + if (sts->remoteClients[i].outgoingData.GetBytesWritten()>0) + FD_SET(sts->remoteClients[i].socket, &writeFD); + if(sts->remoteClients[i].socket > largestDescriptor) // @see largestDescriptorDef + largestDescriptor = sts->remoteClients[i].socket; + } + sts->remoteClients[i].isActiveMutex.Unlock(); + } + +#ifdef _MSC_VER +#pragma warning( disable : 4244 ) // warning C4127: conditional expression is constant +#endif + + + selectResult=(int) select__(largestDescriptor+1, &readFD, &writeFD, &exceptionFD, &tv); + + + + + if (selectResult<=0) + break; + + if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &readFD)) + { + newSock = accept__(sts->listenSocket, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize); + + if (newSock != (SOCKET) -1) + { + int newRemoteClientIndex=-1; + for (newRemoteClientIndex=0; newRemoteClientIndex < sts->remoteClientsLength; newRemoteClientIndex++) + { + sts->remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); + if (sts->remoteClients[newRemoteClientIndex].isActive==false) + { + sts->remoteClients[newRemoteClientIndex].socket=newSock; + +#if RAKNET_SUPPORT_IPV6!=1 + sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_addr.s_addr=sockAddr.sin_addr.s_addr; + sts->remoteClients[newRemoteClientIndex].systemAddress.SetPortNetworkOrder( sockAddr.sin_port); + sts->remoteClients[newRemoteClientIndex].systemAddress.systemIndex=newRemoteClientIndex; +#else + if (sockAddr.ss_family==AF_INET) + { + memcpy(&sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4,(sockaddr_in *)&sockAddr,sizeof(sockaddr_in)); + // sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_port=ntohs( sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_port ); + } + else + { + memcpy(&sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6,(sockaddr_in6 *)&sockAddr,sizeof(sockaddr_in6)); + // sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6.sin6_port=ntohs( sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6.sin6_port ); + } + +#endif // #if RAKNET_SUPPORT_IPV6!=1 + sts->remoteClients[newRemoteClientIndex].SetActive(true); + sts->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + + + SystemAddress *newConnectionSystemAddress=sts->newIncomingConnections.Allocate( _FILE_AND_LINE_ ); + *newConnectionSystemAddress=sts->remoteClients[newRemoteClientIndex].systemAddress; + sts->newIncomingConnections.Push(newConnectionSystemAddress); + + break; + } + sts->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + } + if (newRemoteClientIndex==-1) + { + closesocket__(sts->listenSocket); + } + } + else + { +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Error: connection failed\n"); +#endif + } + } + else if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &exceptionFD)) + { +#ifdef _DO_PRINTF + int err; + int errlen = sizeof(err); + getsockopt__(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen); + RAKNET_DEBUG_PRINTF("Socket error %s on listening socket\n", err); +#endif + } + + { + i=0; + while (i < (unsigned int) sts->remoteClientsLength) + { + if (sts->remoteClients[i].isActive==false) + { + i++; + continue; + } + + if (FD_ISSET(sts->remoteClients[i].socket, &exceptionFD)) + { +// #ifdef _DO_PRINTF +// if (sts->listenSocket!=-1) +// { +// int err; +// int errlen = sizeof(err); +// getsockopt__(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen); +// in_addr in; +// in.s_addr = sts->remoteClients[i].systemAddress.binaryAddress; +// RAKNET_DEBUG_PRINTF("Socket error %i on %s:%i\n", err,inet_ntoa( in ), sts->remoteClients[i].systemAddress.GetPort() ); +// } +// +// #endif + // Connection lost abruptly + SystemAddress *lostConnectionSystemAddress=sts->lostConnections.Allocate( _FILE_AND_LINE_ ); + *lostConnectionSystemAddress=sts->remoteClients[i].systemAddress; + sts->lostConnections.Push(lostConnectionSystemAddress); + sts->remoteClients[i].isActiveMutex.Lock(); + sts->remoteClients[i].SetActive(false); + sts->remoteClients[i].isActiveMutex.Unlock(); + } + else + { + if (FD_ISSET(sts->remoteClients[i].socket, &readFD)) + { + // if recv returns 0 this was a graceful close + len = sts->remoteClients[i].Recv(data,BUFF_SIZE); + + + // removeme +// data[len]=0; +// printf(data); + + if (len>0) + { + incomingMessage=sts->incomingMessages.Allocate( _FILE_AND_LINE_ ); + incomingMessage->data = (unsigned char*) rakMalloc_Ex( len+1, _FILE_AND_LINE_ ); + memcpy(incomingMessage->data, data, len); + incomingMessage->data[len]=0; // Null terminate this so we can print it out as regular strings. This is different from RakNet which does not do this. +// printf("RECV: %s\n",incomingMessage->data); + incomingMessage->length=len; + incomingMessage->deleteData=true; // actually means came from SPSC, rather than AllocatePacket + incomingMessage->systemAddress=sts->remoteClients[i].systemAddress; + sts->incomingMessages.Push(incomingMessage); + } + else + { + // Connection lost gracefully + SystemAddress *lostConnectionSystemAddress=sts->lostConnections.Allocate( _FILE_AND_LINE_ ); + *lostConnectionSystemAddress=sts->remoteClients[i].systemAddress; + sts->lostConnections.Push(lostConnectionSystemAddress); + sts->remoteClients[i].isActiveMutex.Lock(); + sts->remoteClients[i].SetActive(false); + sts->remoteClients[i].isActiveMutex.Unlock(); + continue; + } + } + if (FD_ISSET(sts->remoteClients[i].socket, &writeFD)) + { + RemoteClient *rc = &sts->remoteClients[i]; + unsigned int bytesInBuffer; + int bytesAvailable; + int bytesSent; + rc->outgoingDataMutex.Lock(); + bytesInBuffer=rc->outgoingData.GetBytesWritten(); + if (bytesInBuffer>0) + { + unsigned int contiguousLength; + char* contiguousBytesPointer = rc->outgoingData.PeekContiguousBytes(&contiguousLength); + if (contiguousLength < (unsigned int) BUFF_SIZE && contiguousLength BUFF_SIZE) + bytesAvailable=BUFF_SIZE; + else + bytesAvailable=bytesInBuffer; + rc->outgoingData.ReadBytes(data,bytesAvailable,true); + bytesSent=rc->Send(data,bytesAvailable); + } + else + { + bytesSent=rc->Send(contiguousBytesPointer,contiguousLength); + } + + rc->outgoingData.IncrementReadOffset(bytesSent); + bytesInBuffer=rc->outgoingData.GetBytesWritten(); + } + rc->outgoingDataMutex.Unlock(); + } + + i++; // Nothing deleted so increment the index + } + } + } + } + + // Sleep 0 on Linux monopolizes the CPU + RakSleep(30); + } + sts->threadRunning=false; + + rakFree_Ex(data,_FILE_AND_LINE_); + + + + + return 0; + +} + +void RemoteClient::SetActive(bool a) +{ + if (isActive != a) + { + isActive=a; + Reset(); + if (isActive==false && socket!=INVALID_SOCKET) + { + closesocket__(socket); + socket=INVALID_SOCKET; + } + } +} +void RemoteClient::SendOrBuffer(const char **data, const unsigned int *lengths, const int numParameters) +{ + // True can save memory and buffer copies, but gives worse performance overall + // Do not use true for the XBOX, as it just locks up + const bool ALLOW_SEND_FROM_USER_THREAD=false; + + int parameterIndex; + if (isActive==false) + return; + parameterIndex=0; + for (; parameterIndex < numParameters; parameterIndex++) + { + outgoingDataMutex.Lock(); + if (ALLOW_SEND_FROM_USER_THREAD && outgoingData.GetBytesWritten()==0) + { + outgoingDataMutex.Unlock(); + int bytesSent = Send(data[parameterIndex],lengths[parameterIndex]); + if (bytesSent<(int) lengths[parameterIndex]) + { + // Push remainder + outgoingDataMutex.Lock(); + outgoingData.WriteBytes(data[parameterIndex]+bytesSent,lengths[parameterIndex]-bytesSent,_FILE_AND_LINE_); + outgoingDataMutex.Unlock(); + } + } + else + { + outgoingData.WriteBytes(data[parameterIndex],lengths[parameterIndex],_FILE_AND_LINE_); + outgoingDataMutex.Unlock(); + } + } +} +#if OPEN_SSL_CLIENT_SUPPORT==1 +void RemoteClient::InitSSL(SSL_CTX* ctx, SSL_METHOD *meth) +{ + (void) meth; + + ssl = SSL_new (ctx); + RakAssert(ssl); + SSL_set_fd (ssl, socket); + SSL_connect (ssl); +} +void RemoteClient::DisconnectSSL(void) +{ + if (ssl) + SSL_shutdown (ssl); /* send SSL/TLS close_notify */ +} +void RemoteClient::FreeSSL(void) +{ + if (ssl) + SSL_free (ssl); +} +int RemoteClient::Send(const char *data, unsigned int length) +{ + int err; + if (ssl) + { + return SSL_write (ssl, data, length); + } + else + return send__(socket, data, length, 0); +} +int RemoteClient::Recv(char *data, const int dataSize) +{ + if (ssl) + return SSL_read (ssl, data, dataSize); + else + return recv__(socket, data, dataSize, 0); +} +#else +int RemoteClient::Send(const char *data, unsigned int length) +{ + return send__(socket, data, length, 0); +} +int RemoteClient::Recv(char *data, const int dataSize) +{ + return recv__(socket, data, dataSize, 0); +} +#endif + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/TCPInterface.h b/project/lib_projects/raknet/jni/RaknetSources/TCPInterface.h index e7bc8a0..3d76d51 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TCPInterface.h +++ b/project/lib_projects/raknet/jni/RaknetSources/TCPInterface.h @@ -1,240 +1,240 @@ -/// \file -/// \brief A simple TCP based server allowing sends and receives. Can be connected by any TCP client, including telnet. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TCPInterface==1 - -#ifndef __SIMPLE_TCP_SERVER -#define __SIMPLE_TCP_SERVER - -#include "RakMemoryOverride.h" -#include "DS_List.h" -#include "RakNetTypes.h" -#include "Export.h" -#include "RakThread.h" -#include "DS_Queue.h" -#include "SimpleMutex.h" -#include "RakNetDefines.h" -#include "SocketIncludes.h" -#include "DS_ByteQueue.h" -#include "DS_ThreadsafeAllocatingQueue.h" - -#if OPEN_SSL_CLIENT_SUPPORT==1 -#include -#include -#include -#include -#include -#endif - -namespace RakNet -{ -/// Forward declarations -struct RemoteClient; - -/// \internal -/// \brief As the name says, a simple multithreaded TCP server. Used by TelnetTransport -class RAK_DLL_EXPORT TCPInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(TCPInterface) - - TCPInterface(); - virtual ~TCPInterface(); - - /// Starts the TCP server on the indicated port - /// \param[in] port Which port to listen on. - /// \param[in] maxIncomingConnections Max incoming connections we will accept - /// \param[in] maxConnections Max total connections, which should be >= maxIncomingConnections - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - bool Start(unsigned short port, unsigned short maxIncomingConnections, unsigned short maxConnections=0, int _threadPriority=-99999, unsigned short socketFamily=AF_INET); - - /// Stops the TCP server - void Stop(void); - - /// Connect to the specified host on the specified port - SystemAddress Connect(const char* host, unsigned short remotePort, bool block=true, unsigned short socketFamily=AF_INET); - -#if OPEN_SSL_CLIENT_SUPPORT==1 - /// Start SSL on an existing connection, notified with HasCompletedConnectionAttempt - void StartSSLClient(SystemAddress systemAddress); - - /// Was SSL started on this socket? - bool IsSSLActive(SystemAddress systemAddress); -#endif - - /// Sends a byte stream - void Send( const char *data, unsigned int length, const SystemAddress &systemAddress, bool broadcast ); - - // Sends a concatenated list of byte streams - bool SendList( const char **data, const unsigned int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ); - - // Get how many bytes are waiting to be sent. If too many, you may want to skip sending - unsigned int GetOutgoingDataBufferSize(SystemAddress systemAddress) const; - - /// Returns if Receive() will return data - bool ReceiveHasPackets( void ); - - /// Returns data received - Packet* Receive( void ); - - /// Disconnects a player/address - void CloseConnection( SystemAddress systemAddress ); - - /// Deallocates a packet returned by Receive - void DeallocatePacket( Packet *packet ); - - /// Fills the array remoteSystems with the SystemAddress of all the systems we are connected to - /// \param[out] remoteSystems An array of SystemAddress structures to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to only get the number of systems we are connected to - /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array - void GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const; - - /// Returns just the number of connections we have - unsigned short GetConnectionCount(void) const; - - /// Has a previous call to connect succeeded? - /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. - SystemAddress HasCompletedConnectionAttempt(void); - - /// Has a previous call to connect failed? - /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. - SystemAddress HasFailedConnectionAttempt(void); - - /// Queued events of new incoming connections - SystemAddress HasNewIncomingConnection(void); - - /// Queued events of lost connections - SystemAddress HasLostConnection(void); - - /// Return an allocated but empty packet, for custom use - Packet* AllocatePacket(unsigned dataSize); - - // Push a packet back to the queue - virtual void PushBackPacket( Packet *packet, bool pushAtHead ); - - static const char *Base64Map(void) {return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";} - - /// \brief Returns how many bytes were written. - static int Base64Encoding(const char *inputData, int dataLength, char *outputData); - - /// Returns if Start() was called successfully - bool WasStarted(void) const; - -protected: - - bool isStarted, threadRunning; - SOCKET listenSocket; - - DataStructures::Queue headPush, tailPush; - RemoteClient* remoteClients; - int remoteClientsLength; - - // Assuming remoteClients is only used by one thread! - // DataStructures::List remoteClients; - // Use this thread-safe queue to add to remoteClients - // DataStructures::Queue remoteClientsInsertionQueue; - // SimpleMutex remoteClientsInsertionQueueMutex; - - /* - struct OutgoingMessage - { - unsigned char* data; - SystemAddress systemAddress; - bool broadcast; - unsigned int length; - }; - */ -// DataStructures::SingleProducerConsumer outgoingMessages; -// DataStructures::SingleProducerConsumer incomingMessages; -// DataStructures::SingleProducerConsumer newIncomingConnections, lostConnections, requestedCloseConnections; -// DataStructures::SingleProducerConsumer newRemoteClients; -// DataStructures::ThreadsafeAllocatingQueue outgoingMessages; - DataStructures::ThreadsafeAllocatingQueue incomingMessages; - DataStructures::ThreadsafeAllocatingQueue newIncomingConnections, lostConnections, requestedCloseConnections; - DataStructures::ThreadsafeAllocatingQueue newRemoteClients; - SimpleMutex completedConnectionAttemptMutex, failedConnectionAttemptMutex; - DataStructures::Queue completedConnectionAttempts, failedConnectionAttempts; - - int threadPriority; - - DataStructures::List blockingSocketList; - SimpleMutex blockingSocketListMutex; - - - - - - friend RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop); - friend RAK_THREAD_DECLARATION(ConnectionAttemptLoop); - -// void DeleteRemoteClient(RemoteClient *remoteClient, fd_set *exceptionFD); -// void InsertRemoteClient(RemoteClient* remoteClient); - SOCKET SocketConnect(const char* host, unsigned short remotePort, unsigned short socketFamily); - - struct ThisPtrPlusSysAddr - { - TCPInterface *tcpInterface; - SystemAddress systemAddress; - bool useSSL; - unsigned short socketFamily; - }; - -#if OPEN_SSL_CLIENT_SUPPORT==1 - SSL_CTX* ctx; - SSL_METHOD *meth; - DataStructures::ThreadsafeAllocatingQueue startSSL; - DataStructures::List activeSSLConnections; - SimpleMutex sharedSslMutex; -#endif -}; - -/// Stores information about a remote client. -struct RemoteClient -{ - RemoteClient() { -#if OPEN_SSL_CLIENT_SUPPORT==1 - ssl=0; -#endif - isActive=false; - socket=INVALID_SOCKET; - } - SOCKET socket; - SystemAddress systemAddress; - DataStructures::ByteQueue outgoingData; - bool isActive; - SimpleMutex outgoingDataMutex; - SimpleMutex isActiveMutex; - -#if OPEN_SSL_CLIENT_SUPPORT==1 - SSL* ssl; - void InitSSL(SSL_CTX* ctx, SSL_METHOD *meth); - void DisconnectSSL(void); - void FreeSSL(void); - int Send(const char *data, unsigned int length); - int Recv(char *data, const int dataSize); -#else - int Send(const char *data, unsigned int length); - int Recv(char *data, const int dataSize); -#endif - void Reset(void) - { - outgoingDataMutex.Lock(); - outgoingData.Clear(_FILE_AND_LINE_); - outgoingDataMutex.Unlock(); - } - void SetActive(bool a); - void SendOrBuffer(const char **data, const unsigned int *lengths, const int numParameters); -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* - +/// \file +/// \brief A simple TCP based server allowing sends and receives. Can be connected by any TCP client, including telnet. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TCPInterface==1 + +#ifndef __SIMPLE_TCP_SERVER +#define __SIMPLE_TCP_SERVER + +#include "RakMemoryOverride.h" +#include "DS_List.h" +#include "RakNetTypes.h" +#include "Export.h" +#include "RakThread.h" +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "RakNetDefines.h" +#include "SocketIncludes.h" +#include "DS_ByteQueue.h" +#include "DS_ThreadsafeAllocatingQueue.h" + +#if OPEN_SSL_CLIENT_SUPPORT==1 +#include +#include +#include +#include +#include +#endif + +namespace RakNet +{ +/// Forward declarations +struct RemoteClient; + +/// \internal +/// \brief As the name says, a simple multithreaded TCP server. Used by TelnetTransport +class RAK_DLL_EXPORT TCPInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(TCPInterface) + + TCPInterface(); + virtual ~TCPInterface(); + + /// Starts the TCP server on the indicated port + /// \param[in] port Which port to listen on. + /// \param[in] maxIncomingConnections Max incoming connections we will accept + /// \param[in] maxConnections Max total connections, which should be >= maxIncomingConnections + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + bool Start(unsigned short port, unsigned short maxIncomingConnections, unsigned short maxConnections=0, int _threadPriority=-99999, unsigned short socketFamily=AF_INET); + + /// Stops the TCP server + void Stop(void); + + /// Connect to the specified host on the specified port + SystemAddress Connect(const char* host, unsigned short remotePort, bool block=true, unsigned short socketFamily=AF_INET); + +#if OPEN_SSL_CLIENT_SUPPORT==1 + /// Start SSL on an existing connection, notified with HasCompletedConnectionAttempt + void StartSSLClient(SystemAddress systemAddress); + + /// Was SSL started on this socket? + bool IsSSLActive(SystemAddress systemAddress); +#endif + + /// Sends a byte stream + void Send( const char *data, unsigned int length, const SystemAddress &systemAddress, bool broadcast ); + + // Sends a concatenated list of byte streams + bool SendList( const char **data, const unsigned int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ); + + // Get how many bytes are waiting to be sent. If too many, you may want to skip sending + unsigned int GetOutgoingDataBufferSize(SystemAddress systemAddress) const; + + /// Returns if Receive() will return data + bool ReceiveHasPackets( void ); + + /// Returns data received + Packet* Receive( void ); + + /// Disconnects a player/address + void CloseConnection( SystemAddress systemAddress ); + + /// Deallocates a packet returned by Receive + void DeallocatePacket( Packet *packet ); + + /// Fills the array remoteSystems with the SystemAddress of all the systems we are connected to + /// \param[out] remoteSystems An array of SystemAddress structures to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to only get the number of systems we are connected to + /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array + void GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const; + + /// Returns just the number of connections we have + unsigned short GetConnectionCount(void) const; + + /// Has a previous call to connect succeeded? + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasCompletedConnectionAttempt(void); + + /// Has a previous call to connect failed? + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasFailedConnectionAttempt(void); + + /// Queued events of new incoming connections + SystemAddress HasNewIncomingConnection(void); + + /// Queued events of lost connections + SystemAddress HasLostConnection(void); + + /// Return an allocated but empty packet, for custom use + Packet* AllocatePacket(unsigned dataSize); + + // Push a packet back to the queue + virtual void PushBackPacket( Packet *packet, bool pushAtHead ); + + static const char *Base64Map(void) {return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";} + + /// \brief Returns how many bytes were written. + static int Base64Encoding(const char *inputData, int dataLength, char *outputData); + + /// Returns if Start() was called successfully + bool WasStarted(void) const; + +protected: + + bool isStarted, threadRunning; + SOCKET listenSocket; + + DataStructures::Queue headPush, tailPush; + RemoteClient* remoteClients; + int remoteClientsLength; + + // Assuming remoteClients is only used by one thread! + // DataStructures::List remoteClients; + // Use this thread-safe queue to add to remoteClients + // DataStructures::Queue remoteClientsInsertionQueue; + // SimpleMutex remoteClientsInsertionQueueMutex; + + /* + struct OutgoingMessage + { + unsigned char* data; + SystemAddress systemAddress; + bool broadcast; + unsigned int length; + }; + */ +// DataStructures::SingleProducerConsumer outgoingMessages; +// DataStructures::SingleProducerConsumer incomingMessages; +// DataStructures::SingleProducerConsumer newIncomingConnections, lostConnections, requestedCloseConnections; +// DataStructures::SingleProducerConsumer newRemoteClients; +// DataStructures::ThreadsafeAllocatingQueue outgoingMessages; + DataStructures::ThreadsafeAllocatingQueue incomingMessages; + DataStructures::ThreadsafeAllocatingQueue newIncomingConnections, lostConnections, requestedCloseConnections; + DataStructures::ThreadsafeAllocatingQueue newRemoteClients; + SimpleMutex completedConnectionAttemptMutex, failedConnectionAttemptMutex; + DataStructures::Queue completedConnectionAttempts, failedConnectionAttempts; + + int threadPriority; + + DataStructures::List blockingSocketList; + SimpleMutex blockingSocketListMutex; + + + + + + friend RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop); + friend RAK_THREAD_DECLARATION(ConnectionAttemptLoop); + +// void DeleteRemoteClient(RemoteClient *remoteClient, fd_set *exceptionFD); +// void InsertRemoteClient(RemoteClient* remoteClient); + SOCKET SocketConnect(const char* host, unsigned short remotePort, unsigned short socketFamily); + + struct ThisPtrPlusSysAddr + { + TCPInterface *tcpInterface; + SystemAddress systemAddress; + bool useSSL; + unsigned short socketFamily; + }; + +#if OPEN_SSL_CLIENT_SUPPORT==1 + SSL_CTX* ctx; + SSL_METHOD *meth; + DataStructures::ThreadsafeAllocatingQueue startSSL; + DataStructures::List activeSSLConnections; + SimpleMutex sharedSslMutex; +#endif +}; + +/// Stores information about a remote client. +struct RemoteClient +{ + RemoteClient() { +#if OPEN_SSL_CLIENT_SUPPORT==1 + ssl=0; +#endif + isActive=false; + socket=INVALID_SOCKET; + } + SOCKET socket; + SystemAddress systemAddress; + DataStructures::ByteQueue outgoingData; + bool isActive; + SimpleMutex outgoingDataMutex; + SimpleMutex isActiveMutex; + +#if OPEN_SSL_CLIENT_SUPPORT==1 + SSL* ssl; + void InitSSL(SSL_CTX* ctx, SSL_METHOD *meth); + void DisconnectSSL(void); + void FreeSSL(void); + int Send(const char *data, unsigned int length); + int Recv(char *data, const int dataSize); +#else + int Send(const char *data, unsigned int length); + int Recv(char *data, const int dataSize); +#endif + void Reset(void) + { + outgoingDataMutex.Lock(); + outgoingData.Clear(_FILE_AND_LINE_); + outgoingDataMutex.Unlock(); + } + void SetActive(bool a); + void SendOrBuffer(const char **data, const unsigned int *lengths, const int numParameters); +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* + diff --git a/project/lib_projects/raknet/jni/RaknetSources/TableSerializer.cpp b/project/lib_projects/raknet/jni/RaknetSources/TableSerializer.cpp index 17b5373..d639607 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TableSerializer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/TableSerializer.cpp @@ -1,320 +1,320 @@ -#include "TableSerializer.h" -#include "DS_Table.h" -#include "BitStream.h" -#include "StringCompressor.h" -#include "RakAssert.h" - -using namespace RakNet; - -void TableSerializer::SerializeTable(DataStructures::Table *in, RakNet::BitStream *out) -{ - DataStructures::Page *cur = in->GetRows().GetListHead(); - const DataStructures::List &columns=in->GetColumns(); - SerializeColumns(in, out); - out->Write((unsigned)in->GetRows().Size()); - unsigned rowIndex; - while (cur) - { - for (rowIndex=0; rowIndex < (unsigned)cur->size; rowIndex++) - { - SerializeRow(cur->data[rowIndex], cur->keys[rowIndex], columns, out); - } - cur=cur->next; - } -} -void TableSerializer::SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out) -{ - const DataStructures::List &columns=in->GetColumns(); - out->Write((unsigned)columns.Size()); - unsigned i; - for (i=0; iEncodeString(columns[i].columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out); - out->Write((unsigned char)columns[i].columnType); - } -} -void TableSerializer::SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out, DataStructures::List &skipColumnIndices) -{ - const DataStructures::List &columns=in->GetColumns(); - out->Write((unsigned)columns.Size()-skipColumnIndices.Size()); - unsigned i; - for (i=0; iEncodeString(columns[i].columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out); - out->Write((unsigned char)columns[i].columnType); - } - } -} -bool TableSerializer::DeserializeTable(unsigned char *serializedTable, unsigned int dataLength, DataStructures::Table *out) -{ - RakNet::BitStream in((unsigned char*) serializedTable, dataLength, false); - return DeserializeTable(&in, out); -} -bool TableSerializer::DeserializeTable(RakNet::BitStream *in, DataStructures::Table *out) -{ - unsigned rowSize; - DeserializeColumns(in,out); - if (in->Read(rowSize)==false || rowSize>100000) - { - RakAssert(0); - return false; // Hacker crash prevention - } - - unsigned rowIndex; - for (rowIndex=0; rowIndex < rowSize; rowIndex++) - { - if (DeserializeRow(in, out)==false) - return false; - } - return true; -} -bool TableSerializer::DeserializeColumns(RakNet::BitStream *in, DataStructures::Table *out) -{ - unsigned columnSize; - unsigned char columnType; - char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; - if (in->Read(columnSize)==false || columnSize > 10000) - return false; // Hacker crash prevention - - out->Clear(); - unsigned i; - for (i=0; iDecodeString(columnName, 32, in); - in->Read(columnType); - out->AddColumn(columnName, (DataStructures::Table::ColumnType)columnType); - } - return true; -} -void TableSerializer::SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out) -{ - unsigned cellIndex; - out->Write(keyIn); - unsigned int columnsSize = columns.Size(); - out->Write(columnsSize); - for (cellIndex=0; cellIndexWrite(cellIndex); - SerializeCell(out, in->cells[cellIndex], columns[cellIndex].columnType); - } -} -void TableSerializer::SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out, DataStructures::List &skipColumnIndices) -{ - unsigned cellIndex; - out->Write(keyIn); - unsigned int numEntries=0; - for (cellIndex=0; cellIndexWrite(numEntries); - - for (cellIndex=0; cellIndexWrite(cellIndex); - SerializeCell(out, in->cells[cellIndex], columns[cellIndex].columnType); - } - } -} -bool TableSerializer::DeserializeRow(RakNet::BitStream *in, DataStructures::Table *out) -{ - const DataStructures::List &columns=out->GetColumns(); - unsigned numEntries; - DataStructures::Table::Row *row; - unsigned key; - if (in->Read(key)==false) - return false; - row=out->AddRow(key); - unsigned int cnt; - in->Read(numEntries); - for (cnt=0; cntRead(cellIndex); - if (DeserializeCell(in, row->cells[cellIndex], columns[cellIndex].columnType)==false) - { - out->RemoveRow(key); - return false; - } - } - return true; -} -void TableSerializer::SerializeCell(RakNet::BitStream *out, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType) -{ - out->Write(cell->isEmpty); - if (cell->isEmpty==false) - { - if (columnType==DataStructures::Table::NUMERIC) - { - out->Write(cell->i); - } - else if (columnType==DataStructures::Table::STRING) - { - StringCompressor::Instance()->EncodeString(cell->c, 65535, out); - } - else if (columnType==DataStructures::Table::POINTER) - { - out->Write(cell->ptr); - } - else - { - // Binary - RakAssert(columnType==DataStructures::Table::BINARY); - RakAssert(cell->i>0); - unsigned binaryLength; - binaryLength=(unsigned)cell->i; - out->Write(binaryLength); - out->WriteAlignedBytes((const unsigned char*) cell->c, (const unsigned int) cell->i); - } - } -} -bool TableSerializer::DeserializeCell(RakNet::BitStream *in, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType) -{ - bool isEmpty=false; - double value; - void *ptr; - char tempString[65535]; - cell->Clear(); - - if (in->Read(isEmpty)==false) - return false; - if (isEmpty==false) - { - if (columnType==DataStructures::Table::NUMERIC) - { - if (in->Read(value)==false) - return false; - cell->Set(value); - } - else if (columnType==DataStructures::Table::STRING) - { - if (StringCompressor::Instance()->DecodeString(tempString, 65535, in)==false) - return false; - cell->Set(tempString); - } - else if (columnType==DataStructures::Table::POINTER) - { - if (in->Read(ptr)==false) - return false; - cell->SetPtr(ptr); - } - else - { - unsigned binaryLength; - // Binary - RakAssert(columnType==DataStructures::Table::BINARY); - if (in->Read(binaryLength)==false || binaryLength > 10000000) - return false; // Sanity check to max binary cell of 10 megabytes - in->AlignReadToByteBoundary(); - if (BITS_TO_BYTES(in->GetNumberOfUnreadBits())<(BitSize_t)binaryLength) - return false; - cell->Set((char*) in->GetData()+BITS_TO_BYTES(in->GetReadOffset()), (int) binaryLength); - in->IgnoreBits(BYTES_TO_BITS((int) binaryLength)); - } - } - return true; -} -void TableSerializer::SerializeFilterQuery(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query) -{ - StringCompressor::Instance()->EncodeString(query->columnName,_TABLE_MAX_COLUMN_NAME_LENGTH,in,0); - in->WriteCompressed(query->columnIndex); - in->Write((unsigned char) query->operation); - in->Write(query->cellValue->isEmpty); - if (query->cellValue->isEmpty==false) - { - in->Write(query->cellValue->i); - in->WriteAlignedBytesSafe((const char*)query->cellValue->c,(const unsigned int)query->cellValue->i,10000000); // Sanity check to max binary cell of 10 megabytes - in->Write(query->cellValue->ptr); - - } -} -bool TableSerializer::DeserializeFilterQuery(RakNet::BitStream *out, DataStructures::Table::FilterQuery *query) -{ - bool b; - RakAssert(query->cellValue); - StringCompressor::Instance()->DecodeString(query->columnName,_TABLE_MAX_COLUMN_NAME_LENGTH,out,0); - out->ReadCompressed(query->columnIndex); - unsigned char op; - out->Read(op); - query->operation=(DataStructures::Table::FilterQueryType) op; - query->cellValue->Clear(); - b=out->Read(query->cellValue->isEmpty); - if (query->cellValue->isEmpty==false) - { - // HACK - cellValue->i is used for integer, character, and binary data. However, for character and binary c will be 0. So use that to determine if the data was integer or not. - out->Read(query->cellValue->i); - unsigned int inputLength; - out->ReadAlignedBytesSafeAlloc(&query->cellValue->c,inputLength,10000000); // Sanity check to max binary cell of 10 megabytes - if (query->cellValue->c) - query->cellValue->i=inputLength; - b=out->Read(query->cellValue->ptr); - } - return b; -} -void TableSerializer::SerializeFilterQueryList(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query, unsigned int numQueries, unsigned int maxQueries) -{ - (void) maxQueries; - in->Write((bool)(query && numQueries>0)); - if (query==0 || numQueries<=0) - return; - - RakAssert(numQueries<=maxQueries); - in->WriteCompressed(numQueries); - unsigned i; - for (i=0; i < numQueries; i++) - { - SerializeFilterQuery(in, query); - } -} -bool TableSerializer::DeserializeFilterQueryList(RakNet::BitStream *out, DataStructures::Table::FilterQuery **query, unsigned int *numQueries, unsigned int maxQueries, int allocateExtraQueries) -{ - bool b, anyQueries=false; - out->Read(anyQueries); - if (anyQueries==false) - { - if (allocateExtraQueries<=0) - *query=0; - else - *query=new DataStructures::Table::FilterQuery[allocateExtraQueries]; - - *numQueries=0; - return true; - } - b=out->ReadCompressed(*numQueries); - if (*numQueries>maxQueries) - { - RakAssert(0); - *numQueries=maxQueries; - } - if (*numQueries==0) - return b; - - *query=new DataStructures::Table::FilterQuery[*numQueries+allocateExtraQueries]; - DataStructures::Table::FilterQuery *queryPtr = *query; - - unsigned i; - for (i=0; i < *numQueries; i++) - { - queryPtr[i].cellValue=new DataStructures::Table::Cell; - b=DeserializeFilterQuery(out, queryPtr+i); - } - - return b; -} -void TableSerializer::DeallocateQueryList(DataStructures::Table::FilterQuery *query, unsigned int numQueries) -{ - if (query==0 || numQueries==0) - return; - - unsigned i; - for (i=0; i < numQueries; i++) - RakNet::OP_DELETE(query[i].cellValue, _FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(query, _FILE_AND_LINE_); -} +#include "TableSerializer.h" +#include "DS_Table.h" +#include "BitStream.h" +#include "StringCompressor.h" +#include "RakAssert.h" + +using namespace RakNet; + +void TableSerializer::SerializeTable(DataStructures::Table *in, RakNet::BitStream *out) +{ + DataStructures::Page *cur = in->GetRows().GetListHead(); + const DataStructures::List &columns=in->GetColumns(); + SerializeColumns(in, out); + out->Write((unsigned)in->GetRows().Size()); + unsigned rowIndex; + while (cur) + { + for (rowIndex=0; rowIndex < (unsigned)cur->size; rowIndex++) + { + SerializeRow(cur->data[rowIndex], cur->keys[rowIndex], columns, out); + } + cur=cur->next; + } +} +void TableSerializer::SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out) +{ + const DataStructures::List &columns=in->GetColumns(); + out->Write((unsigned)columns.Size()); + unsigned i; + for (i=0; iEncodeString(columns[i].columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out); + out->Write((unsigned char)columns[i].columnType); + } +} +void TableSerializer::SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out, DataStructures::List &skipColumnIndices) +{ + const DataStructures::List &columns=in->GetColumns(); + out->Write((unsigned)columns.Size()-skipColumnIndices.Size()); + unsigned i; + for (i=0; iEncodeString(columns[i].columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out); + out->Write((unsigned char)columns[i].columnType); + } + } +} +bool TableSerializer::DeserializeTable(unsigned char *serializedTable, unsigned int dataLength, DataStructures::Table *out) +{ + RakNet::BitStream in((unsigned char*) serializedTable, dataLength, false); + return DeserializeTable(&in, out); +} +bool TableSerializer::DeserializeTable(RakNet::BitStream *in, DataStructures::Table *out) +{ + unsigned rowSize; + DeserializeColumns(in,out); + if (in->Read(rowSize)==false || rowSize>100000) + { + RakAssert(0); + return false; // Hacker crash prevention + } + + unsigned rowIndex; + for (rowIndex=0; rowIndex < rowSize; rowIndex++) + { + if (DeserializeRow(in, out)==false) + return false; + } + return true; +} +bool TableSerializer::DeserializeColumns(RakNet::BitStream *in, DataStructures::Table *out) +{ + unsigned columnSize; + unsigned char columnType; + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; + if (in->Read(columnSize)==false || columnSize > 10000) + return false; // Hacker crash prevention + + out->Clear(); + unsigned i; + for (i=0; iDecodeString(columnName, 32, in); + in->Read(columnType); + out->AddColumn(columnName, (DataStructures::Table::ColumnType)columnType); + } + return true; +} +void TableSerializer::SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out) +{ + unsigned cellIndex; + out->Write(keyIn); + unsigned int columnsSize = columns.Size(); + out->Write(columnsSize); + for (cellIndex=0; cellIndexWrite(cellIndex); + SerializeCell(out, in->cells[cellIndex], columns[cellIndex].columnType); + } +} +void TableSerializer::SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out, DataStructures::List &skipColumnIndices) +{ + unsigned cellIndex; + out->Write(keyIn); + unsigned int numEntries=0; + for (cellIndex=0; cellIndexWrite(numEntries); + + for (cellIndex=0; cellIndexWrite(cellIndex); + SerializeCell(out, in->cells[cellIndex], columns[cellIndex].columnType); + } + } +} +bool TableSerializer::DeserializeRow(RakNet::BitStream *in, DataStructures::Table *out) +{ + const DataStructures::List &columns=out->GetColumns(); + unsigned numEntries; + DataStructures::Table::Row *row; + unsigned key; + if (in->Read(key)==false) + return false; + row=out->AddRow(key); + unsigned int cnt; + in->Read(numEntries); + for (cnt=0; cntRead(cellIndex); + if (DeserializeCell(in, row->cells[cellIndex], columns[cellIndex].columnType)==false) + { + out->RemoveRow(key); + return false; + } + } + return true; +} +void TableSerializer::SerializeCell(RakNet::BitStream *out, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType) +{ + out->Write(cell->isEmpty); + if (cell->isEmpty==false) + { + if (columnType==DataStructures::Table::NUMERIC) + { + out->Write(cell->i); + } + else if (columnType==DataStructures::Table::STRING) + { + StringCompressor::Instance()->EncodeString(cell->c, 65535, out); + } + else if (columnType==DataStructures::Table::POINTER) + { + out->Write(cell->ptr); + } + else + { + // Binary + RakAssert(columnType==DataStructures::Table::BINARY); + RakAssert(cell->i>0); + unsigned binaryLength; + binaryLength=(unsigned)cell->i; + out->Write(binaryLength); + out->WriteAlignedBytes((const unsigned char*) cell->c, (const unsigned int) cell->i); + } + } +} +bool TableSerializer::DeserializeCell(RakNet::BitStream *in, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType) +{ + bool isEmpty=false; + double value; + void *ptr; + char tempString[65535]; + cell->Clear(); + + if (in->Read(isEmpty)==false) + return false; + if (isEmpty==false) + { + if (columnType==DataStructures::Table::NUMERIC) + { + if (in->Read(value)==false) + return false; + cell->Set(value); + } + else if (columnType==DataStructures::Table::STRING) + { + if (StringCompressor::Instance()->DecodeString(tempString, 65535, in)==false) + return false; + cell->Set(tempString); + } + else if (columnType==DataStructures::Table::POINTER) + { + if (in->Read(ptr)==false) + return false; + cell->SetPtr(ptr); + } + else + { + unsigned binaryLength; + // Binary + RakAssert(columnType==DataStructures::Table::BINARY); + if (in->Read(binaryLength)==false || binaryLength > 10000000) + return false; // Sanity check to max binary cell of 10 megabytes + in->AlignReadToByteBoundary(); + if (BITS_TO_BYTES(in->GetNumberOfUnreadBits())<(BitSize_t)binaryLength) + return false; + cell->Set((char*) in->GetData()+BITS_TO_BYTES(in->GetReadOffset()), (int) binaryLength); + in->IgnoreBits(BYTES_TO_BITS((int) binaryLength)); + } + } + return true; +} +void TableSerializer::SerializeFilterQuery(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query) +{ + StringCompressor::Instance()->EncodeString(query->columnName,_TABLE_MAX_COLUMN_NAME_LENGTH,in,0); + in->WriteCompressed(query->columnIndex); + in->Write((unsigned char) query->operation); + in->Write(query->cellValue->isEmpty); + if (query->cellValue->isEmpty==false) + { + in->Write(query->cellValue->i); + in->WriteAlignedBytesSafe((const char*)query->cellValue->c,(const unsigned int)query->cellValue->i,10000000); // Sanity check to max binary cell of 10 megabytes + in->Write(query->cellValue->ptr); + + } +} +bool TableSerializer::DeserializeFilterQuery(RakNet::BitStream *out, DataStructures::Table::FilterQuery *query) +{ + bool b; + RakAssert(query->cellValue); + StringCompressor::Instance()->DecodeString(query->columnName,_TABLE_MAX_COLUMN_NAME_LENGTH,out,0); + out->ReadCompressed(query->columnIndex); + unsigned char op; + out->Read(op); + query->operation=(DataStructures::Table::FilterQueryType) op; + query->cellValue->Clear(); + b=out->Read(query->cellValue->isEmpty); + if (query->cellValue->isEmpty==false) + { + // HACK - cellValue->i is used for integer, character, and binary data. However, for character and binary c will be 0. So use that to determine if the data was integer or not. + out->Read(query->cellValue->i); + unsigned int inputLength; + out->ReadAlignedBytesSafeAlloc(&query->cellValue->c,inputLength,10000000); // Sanity check to max binary cell of 10 megabytes + if (query->cellValue->c) + query->cellValue->i=inputLength; + b=out->Read(query->cellValue->ptr); + } + return b; +} +void TableSerializer::SerializeFilterQueryList(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query, unsigned int numQueries, unsigned int maxQueries) +{ + (void) maxQueries; + in->Write((bool)(query && numQueries>0)); + if (query==0 || numQueries<=0) + return; + + RakAssert(numQueries<=maxQueries); + in->WriteCompressed(numQueries); + unsigned i; + for (i=0; i < numQueries; i++) + { + SerializeFilterQuery(in, query); + } +} +bool TableSerializer::DeserializeFilterQueryList(RakNet::BitStream *out, DataStructures::Table::FilterQuery **query, unsigned int *numQueries, unsigned int maxQueries, int allocateExtraQueries) +{ + bool b, anyQueries=false; + out->Read(anyQueries); + if (anyQueries==false) + { + if (allocateExtraQueries<=0) + *query=0; + else + *query=new DataStructures::Table::FilterQuery[allocateExtraQueries]; + + *numQueries=0; + return true; + } + b=out->ReadCompressed(*numQueries); + if (*numQueries>maxQueries) + { + RakAssert(0); + *numQueries=maxQueries; + } + if (*numQueries==0) + return b; + + *query=new DataStructures::Table::FilterQuery[*numQueries+allocateExtraQueries]; + DataStructures::Table::FilterQuery *queryPtr = *query; + + unsigned i; + for (i=0; i < *numQueries; i++) + { + queryPtr[i].cellValue=new DataStructures::Table::Cell; + b=DeserializeFilterQuery(out, queryPtr+i); + } + + return b; +} +void TableSerializer::DeallocateQueryList(DataStructures::Table::FilterQuery *query, unsigned int numQueries) +{ + if (query==0 || numQueries==0) + return; + + unsigned i; + for (i=0; i < numQueries; i++) + RakNet::OP_DELETE(query[i].cellValue, _FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(query, _FILE_AND_LINE_); +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/TableSerializer.h b/project/lib_projects/raknet/jni/RaknetSources/TableSerializer.h index fb15bf8..77ca30b 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TableSerializer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/TableSerializer.h @@ -1,208 +1,208 @@ -#ifndef __TABLE_SERIALIZER_H -#define __TABLE_SERIALIZER_H - -#include "RakMemoryOverride.h" -#include "DS_Table.h" -#include "Export.h" - -namespace RakNet -{ - class BitStream; -} - -namespace RakNet -{ - -class RAK_DLL_EXPORT TableSerializer -{ -public: - static void SerializeTable(DataStructures::Table *in, RakNet::BitStream *out); - static bool DeserializeTable(unsigned char *serializedTable, unsigned int dataLength, DataStructures::Table *out); - static bool DeserializeTable(RakNet::BitStream *in, DataStructures::Table *out); - static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out); - static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); - static bool DeserializeColumns(RakNet::BitStream *in, DataStructures::Table *out); - static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out); - static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); - static bool DeserializeRow(RakNet::BitStream *in, DataStructures::Table *out); - static void SerializeCell(RakNet::BitStream *out, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); - static bool DeserializeCell(RakNet::BitStream *in, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); - static void SerializeFilterQuery(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query); - // Note that this allocates query->cell->c! - static bool DeserializeFilterQuery(RakNet::BitStream *out, DataStructures::Table::FilterQuery *query); - static void SerializeFilterQueryList(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query, unsigned int numQueries, unsigned int maxQueries); - // Note that this allocates queries, cells, and query->cell->c!. Use DeallocateQueryList to free. - static bool DeserializeFilterQueryList(RakNet::BitStream *out, DataStructures::Table::FilterQuery **query, unsigned int *numQueries, unsigned int maxQueries, int allocateExtraQueries=0); - static void DeallocateQueryList(DataStructures::Table::FilterQuery *query, unsigned int numQueries); -}; - -} // namespace RakNet - -#endif - -// Test code for the table -/* -#include "LightweightDatabaseServer.h" -#include "LightweightDatabaseClient.h" -#include "TableSerializer.h" -#include "BitStream.h" -#include "StringCompressor.h" -#include "DS_Table.h" -void main(void) -{ - DataStructures::Table table; - DataStructures::Table::Row *row; - unsigned int dummydata=12345; - - // Add columns Name (string), IP (binary), score (int), and players (int). - table.AddColumn("Name", DataStructures::Table::STRING); - table.AddColumn("IP", DataStructures::Table::BINARY); - table.AddColumn("Score", DataStructures::Table::NUMERIC); - table.AddColumn("Players", DataStructures::Table::NUMERIC); - table.AddColumn("Empty Test Column", DataStructures::Table::STRING); - RakAssert(table.GetColumnCount()==5); - row=table.AddRow(0); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,5); - row->UpdateCell(3,10); - //row->UpdateCell(4,"should be unique"); - - row=table.AddRow(1); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,5); - row->UpdateCell(3,15); - - row=table.AddRow(2); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,5); - row->UpdateCell(3,20); - - row=table.AddRow(3); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,15); - row->UpdateCell(3,5); - row->UpdateCell(4,"col index 4"); - - row=table.AddRow(4); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - //row->UpdateCell(2,25); - row->UpdateCell(3,30); - //row->UpdateCell(4,"should be unique"); - - row=table.AddRow(5); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - //row->UpdateCell(2,25); - row->UpdateCell(3,5); - //row->UpdateCell(4,"should be unique"); - - row=table.AddRow(6); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,35); - //row->UpdateCell(3,40); - //row->UpdateCell(4,"should be unique"); - - row=table.AddRow(7); - RakAssert(row); - row->UpdateCell(0,"Bob Jenkins"); - - row=table.AddRow(8); - RakAssert(row); - row->UpdateCell(0,"Zack Jenkins"); - - // Test multi-column sorting - DataStructures::Table::Row *rows[30]; - DataStructures::Table::SortQuery queries[4]; - queries[0].columnIndex=0; - queries[0].operation=DataStructures::Table::QS_INCREASING_ORDER; - queries[1].columnIndex=1; - queries[1].operation=DataStructures::Table::QS_INCREASING_ORDER; - queries[2].columnIndex=2; - queries[2].operation=DataStructures::Table::QS_INCREASING_ORDER; - queries[3].columnIndex=3; - queries[3].operation=DataStructures::Table::QS_DECREASING_ORDER; - table.SortTable(queries, 4, rows); - unsigned i; - char out[256]; - RAKNET_DEBUG_PRINTF("Sort: Ascending except for column index 3\n"); - for (i=0; i < table.GetRowCount(); i++) - { - table.PrintRow(out,256,',',true, rows[i]); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - - // Test query: - // Don't return column 3, and swap columns 0 and 2 - unsigned columnsToReturn[4]; - columnsToReturn[0]=2; - columnsToReturn[1]=1; - columnsToReturn[2]=0; - columnsToReturn[3]=4; - DataStructures::Table resultsTable; - table.QueryTable(columnsToReturn,4,0,0,&resultsTable); - RAKNET_DEBUG_PRINTF("Query: Don't return column 3, and swap columns 0 and 2:\n"); - for (i=0; i < resultsTable.GetRowCount(); i++) - { - resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - - // Test filter: - // Only return rows with column index 4 empty - DataStructures::Table::FilterQuery inclusionFilters[3]; - inclusionFilters[0].columnIndex=4; - inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; - // inclusionFilters[0].cellValue; // Unused for IS_EMPTY - table.QueryTable(0,0,inclusionFilters,1,&resultsTable); - RAKNET_DEBUG_PRINTF("Filter: Only return rows with column index 4 empty:\n"); - for (i=0; i < resultsTable.GetRowCount(); i++) - { - resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - - // Column 5 empty and column 0 == Kevin Jenkins - inclusionFilters[0].columnIndex=4; - inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; - inclusionFilters[1].columnIndex=0; - inclusionFilters[1].operation=DataStructures::Table::QF_EQUAL; - inclusionFilters[1].cellValue.Set("Kevin Jenkins"); - table.QueryTable(0,0,inclusionFilters,2,&resultsTable); - RAKNET_DEBUG_PRINTF("Filter: Column 5 empty and column 0 == Kevin Jenkins:\n"); - for (i=0; i < resultsTable.GetRowCount(); i++) - { - resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - - RakNet::BitStream bs; - RAKNET_DEBUG_PRINTF("PreSerialize:\n"); - for (i=0; i < table.GetRowCount(); i++) - { - table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - StringCompressor::AddReference(); - TableSerializer::Serialize(&table, &bs); - TableSerializer::Deserialize(&bs, &table); - StringCompressor::RemoveReference(); - RAKNET_DEBUG_PRINTF("PostDeserialize:\n"); - for (i=0; i < table.GetRowCount(); i++) - { - table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - int a=5; -} -*/ +#ifndef __TABLE_SERIALIZER_H +#define __TABLE_SERIALIZER_H + +#include "RakMemoryOverride.h" +#include "DS_Table.h" +#include "Export.h" + +namespace RakNet +{ + class BitStream; +} + +namespace RakNet +{ + +class RAK_DLL_EXPORT TableSerializer +{ +public: + static void SerializeTable(DataStructures::Table *in, RakNet::BitStream *out); + static bool DeserializeTable(unsigned char *serializedTable, unsigned int dataLength, DataStructures::Table *out); + static bool DeserializeTable(RakNet::BitStream *in, DataStructures::Table *out); + static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out); + static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); + static bool DeserializeColumns(RakNet::BitStream *in, DataStructures::Table *out); + static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out); + static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); + static bool DeserializeRow(RakNet::BitStream *in, DataStructures::Table *out); + static void SerializeCell(RakNet::BitStream *out, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); + static bool DeserializeCell(RakNet::BitStream *in, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); + static void SerializeFilterQuery(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query); + // Note that this allocates query->cell->c! + static bool DeserializeFilterQuery(RakNet::BitStream *out, DataStructures::Table::FilterQuery *query); + static void SerializeFilterQueryList(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query, unsigned int numQueries, unsigned int maxQueries); + // Note that this allocates queries, cells, and query->cell->c!. Use DeallocateQueryList to free. + static bool DeserializeFilterQueryList(RakNet::BitStream *out, DataStructures::Table::FilterQuery **query, unsigned int *numQueries, unsigned int maxQueries, int allocateExtraQueries=0); + static void DeallocateQueryList(DataStructures::Table::FilterQuery *query, unsigned int numQueries); +}; + +} // namespace RakNet + +#endif + +// Test code for the table +/* +#include "LightweightDatabaseServer.h" +#include "LightweightDatabaseClient.h" +#include "TableSerializer.h" +#include "BitStream.h" +#include "StringCompressor.h" +#include "DS_Table.h" +void main(void) +{ + DataStructures::Table table; + DataStructures::Table::Row *row; + unsigned int dummydata=12345; + + // Add columns Name (string), IP (binary), score (int), and players (int). + table.AddColumn("Name", DataStructures::Table::STRING); + table.AddColumn("IP", DataStructures::Table::BINARY); + table.AddColumn("Score", DataStructures::Table::NUMERIC); + table.AddColumn("Players", DataStructures::Table::NUMERIC); + table.AddColumn("Empty Test Column", DataStructures::Table::STRING); + RakAssert(table.GetColumnCount()==5); + row=table.AddRow(0); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,5); + row->UpdateCell(3,10); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(1); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,5); + row->UpdateCell(3,15); + + row=table.AddRow(2); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,5); + row->UpdateCell(3,20); + + row=table.AddRow(3); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,15); + row->UpdateCell(3,5); + row->UpdateCell(4,"col index 4"); + + row=table.AddRow(4); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + //row->UpdateCell(2,25); + row->UpdateCell(3,30); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(5); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + //row->UpdateCell(2,25); + row->UpdateCell(3,5); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(6); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,35); + //row->UpdateCell(3,40); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(7); + RakAssert(row); + row->UpdateCell(0,"Bob Jenkins"); + + row=table.AddRow(8); + RakAssert(row); + row->UpdateCell(0,"Zack Jenkins"); + + // Test multi-column sorting + DataStructures::Table::Row *rows[30]; + DataStructures::Table::SortQuery queries[4]; + queries[0].columnIndex=0; + queries[0].operation=DataStructures::Table::QS_INCREASING_ORDER; + queries[1].columnIndex=1; + queries[1].operation=DataStructures::Table::QS_INCREASING_ORDER; + queries[2].columnIndex=2; + queries[2].operation=DataStructures::Table::QS_INCREASING_ORDER; + queries[3].columnIndex=3; + queries[3].operation=DataStructures::Table::QS_DECREASING_ORDER; + table.SortTable(queries, 4, rows); + unsigned i; + char out[256]; + RAKNET_DEBUG_PRINTF("Sort: Ascending except for column index 3\n"); + for (i=0; i < table.GetRowCount(); i++) + { + table.PrintRow(out,256,',',true, rows[i]); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + // Test query: + // Don't return column 3, and swap columns 0 and 2 + unsigned columnsToReturn[4]; + columnsToReturn[0]=2; + columnsToReturn[1]=1; + columnsToReturn[2]=0; + columnsToReturn[3]=4; + DataStructures::Table resultsTable; + table.QueryTable(columnsToReturn,4,0,0,&resultsTable); + RAKNET_DEBUG_PRINTF("Query: Don't return column 3, and swap columns 0 and 2:\n"); + for (i=0; i < resultsTable.GetRowCount(); i++) + { + resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + // Test filter: + // Only return rows with column index 4 empty + DataStructures::Table::FilterQuery inclusionFilters[3]; + inclusionFilters[0].columnIndex=4; + inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; + // inclusionFilters[0].cellValue; // Unused for IS_EMPTY + table.QueryTable(0,0,inclusionFilters,1,&resultsTable); + RAKNET_DEBUG_PRINTF("Filter: Only return rows with column index 4 empty:\n"); + for (i=0; i < resultsTable.GetRowCount(); i++) + { + resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + // Column 5 empty and column 0 == Kevin Jenkins + inclusionFilters[0].columnIndex=4; + inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; + inclusionFilters[1].columnIndex=0; + inclusionFilters[1].operation=DataStructures::Table::QF_EQUAL; + inclusionFilters[1].cellValue.Set("Kevin Jenkins"); + table.QueryTable(0,0,inclusionFilters,2,&resultsTable); + RAKNET_DEBUG_PRINTF("Filter: Column 5 empty and column 0 == Kevin Jenkins:\n"); + for (i=0; i < resultsTable.GetRowCount(); i++) + { + resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + RakNet::BitStream bs; + RAKNET_DEBUG_PRINTF("PreSerialize:\n"); + for (i=0; i < table.GetRowCount(); i++) + { + table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + StringCompressor::AddReference(); + TableSerializer::Serialize(&table, &bs); + TableSerializer::Deserialize(&bs, &table); + StringCompressor::RemoveReference(); + RAKNET_DEBUG_PRINTF("PostDeserialize:\n"); + for (i=0; i < table.GetRowCount(); i++) + { + table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + int a=5; +} +*/ diff --git a/project/lib_projects/raknet/jni/RaknetSources/TeamBalancer.cpp b/project/lib_projects/raknet/jni/RaknetSources/TeamBalancer.cpp index 1c1eb0d..90cc9e0 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TeamBalancer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/TeamBalancer.cpp @@ -1,874 +1,874 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TeamBalancer==1 - -#include "TeamBalancer.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "Rand.h" - -using namespace RakNet; - -enum TeamBalancerOperations -{ - ID_STATUS_UPDATE_TO_NEW_HOST, - ID_CANCEL_TEAM_REQUEST, - ID_REQUEST_ANY_TEAM, - ID_REQUEST_SPECIFIC_TEAM -}; - -STATIC_FACTORY_DEFINITIONS(TeamBalancer,TeamBalancer); - -TeamBalancer::TeamBalancer() -{ - defaultAssigmentAlgorithm=SMALLEST_TEAM; - forceTeamsToBeEven=false; - lockTeams=false; - hostGuid=UNASSIGNED_RAKNET_GUID; -} -TeamBalancer::~TeamBalancer() -{ - -} -void TeamBalancer::SetTeamSizeLimit(TeamId team, unsigned short limit) -{ - teamLimits.Replace(limit,0,team,_FILE_AND_LINE_); - if (teamLimits.Size() > teamMemberCounts.Size()) - teamMemberCounts.Replace(0,0,teamLimits.Size()-1,_FILE_AND_LINE_); -} -void TeamBalancer::SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa) -{ - // Just update the default. Currently active teams are not affected. - defaultAssigmentAlgorithm=daa; -} -void TeamBalancer::SetForceEvenTeams(bool force) -{ - // Set flag to indicate that teams should be even. - forceTeamsToBeEven=force; - - // If teams are locked, just return. - if (lockTeams==true) - return; - - if (forceTeamsToBeEven==true) - { - // Run the even team algorithm - EvenTeams(); - } -} -void TeamBalancer::SetLockTeams(bool lock) -{ - if (lock==lockTeams) - return; - - // Set flag to indicate that teams can no longer be changed. - lockTeams=lock; - - // If lock is false, and teams were set to be forced as even, then run through the even team algorithm - if (lockTeams==false) - { - // Process even swaps - TeamId i,j; - for (i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].requestedTeam!=UNASSIGNED_TEAM_ID) - { - for (j=i+1; j < teamMembers.Size(); j++) - { - if (teamMembers[j].requestedTeam==teamMembers[i].currentTeam && - teamMembers[i].requestedTeam==teamMembers[j].currentTeam) - { - SwapTeamMembersByRequest(i,j); - NotifyTeamAssigment(i); - NotifyTeamAssigment(j); - } - } - } - } - - if (forceTeamsToBeEven==true) - { - EvenTeams(); - } - else - { - // Process requested team changes - // Process movement while not full - for (i=0; i < teamMembers.Size(); i++) - { - TeamId requestedTeam = teamMembers[i].requestedTeam; - if (requestedTeam!=UNASSIGNED_TEAM_ID) - { - if (teamMemberCounts[requestedTeam]Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); -} -void TeamBalancer::CancelRequestSpecificTeam(NetworkID memberId) -{ - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==memberId) - { - myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; - - // Send packet to the host to remove our request flag. - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); - bsOut.Write((MessageID)ID_CANCEL_TEAM_REQUEST); - bsOut.Write(memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); - - return; - } - } -} -void TeamBalancer::RequestAnyTeam(NetworkID memberId) -{ - bool foundMatch=false; - - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==memberId) - { - foundMatch=true; - if (myTeamMembers[i].currentTeam!=UNASSIGNED_TEAM_ID) - return; - else - myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; - break; - } - } - - if (foundMatch==false) - { - MyTeamMembers mtm; - mtm.currentTeam=UNASSIGNED_TEAM_ID; - mtm.memberId=memberId; - mtm.requestedTeam=UNASSIGNED_TEAM_ID; - myTeamMembers.Push(mtm, _FILE_AND_LINE_); - } - - // Else send to the current host that we need a team. - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); - bsOut.Write((MessageID)ID_REQUEST_ANY_TEAM); - bsOut.Write(memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); -} -TeamId TeamBalancer::GetMyTeam(NetworkID memberId) const -{ - // Return team returned by last ID_TEAM_BALANCER_TEAM_ASSIGNED packet - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==memberId) - { - return myTeamMembers[i].currentTeam; - } - } - - return UNASSIGNED_TEAM_ID; -} -void TeamBalancer::DeleteMember(NetworkID memberId) -{ - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==memberId) - { - myTeamMembers.RemoveAtIndexFast(i); - break; - } - } - - for (unsigned int i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].memberId==memberId) - { - RemoveTeamMember(i); - break; - } - } -} -PluginReceiveResult TeamBalancer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_FCM2_NEW_HOST: - { - hostGuid=packet->guid; - - if (myTeamMembers.Size()>0) - { - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); - bsOut.Write((MessageID)ID_STATUS_UPDATE_TO_NEW_HOST); - - bsOut.WriteCasted(myTeamMembers.Size()); - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - bsOut.Write(myTeamMembers[i].memberId); - bsOut.Write(myTeamMembers[i].currentTeam); - bsOut.Write(myTeamMembers[i].requestedTeam); - } - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); - } - } - break; - case ID_TEAM_BALANCER_INTERNAL: - { - if (packet->length>=2) - { - switch (packet->data[1]) - { - case ID_STATUS_UPDATE_TO_NEW_HOST: - OnStatusUpdateToNewHost(packet); - break; - case ID_CANCEL_TEAM_REQUEST: - OnCancelTeamRequest(packet); - break; - case ID_REQUEST_ANY_TEAM: - OnRequestAnyTeam(packet); - break; - case ID_REQUEST_SPECIFIC_TEAM: - OnRequestSpecificTeam(packet); - break; - } - } - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - case ID_TEAM_BALANCER_TEAM_ASSIGNED: - { - return OnTeamAssigned(packet); - } - - case ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING: - { - return OnRequestedTeamChangePending(packet); - } - - case ID_TEAM_BALANCER_TEAMS_LOCKED: - { - return OnTeamsLocked(packet); - } - } - - // Got RequestSpecificTeam - // If teams are locked - // - If this user already has a team, return ID_TEAM_BALANCER_TEAMS_LOCKED - // - This user does not already have a team. Assign a team as if the user called RequestAnyTeam(), with a preference for the requested team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED once the team has been assigned. - // If teams are not locked - // - If even team balancing is on, only assign this user if this would not cause teams to be unbalanced. If teams WOULD be unbalanced, then flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING - // - If the destination team is full, flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING - // - Else, join this team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED - - // Got RequestAnyTeam - // Put user on a team following the algorithm. No team is set as preferred. - - return RR_CONTINUE_PROCESSING; -} -void TeamBalancer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) systemAddress; - (void) lostConnectionReason; - - RemoveByGuid(rakNetGUID); -} -void TeamBalancer::OnAttach(void) -{ - hostGuid = rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -} -void TeamBalancer::RemoveByGuid(RakNetGUID rakNetGUID) -{ - // If we are the host, and the closed connection has a team, and teams are not locked: - if (WeAreHost()) - { - unsigned int droppedMemberIndex=0; - while (droppedMemberIndex < teamMembers.Size()) - { - if (teamMembers[droppedMemberIndex].memberGuid==rakNetGUID) - { - TeamId droppedTeam = teamMembers[droppedMemberIndex].currentTeam; - RemoveTeamMember(droppedMemberIndex); - if (lockTeams==false) - { - if (forceTeamsToBeEven) - { - // - teams were forced to be even, then run the even team algorithm - EvenTeams(); - } - else - { - // - teams were NOT forced to be even, and the team the dropped player on was full, then move users wanting to join that team (if any) - if (teamMemberCounts[ droppedTeam ]==teamLimits[ droppedTeam ]-1) - { - MoveMemberThatWantsToJoinTeam(droppedTeam); - } - } - } - } - else - { - droppedMemberIndex++; - } - } - } -} -void TeamBalancer::OnStatusUpdateToNewHost(Packet *packet) -{ - if (WeAreHost()==false) - return; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - uint8_t requestedTeamChangeListSize; - bsIn.Read(requestedTeamChangeListSize); - TeamMember tm; - tm.memberGuid=packet->guid; - for (uint8_t i=0; i < requestedTeamChangeListSize; i++) - { - bsIn.Read(tm.memberId); - bsIn.Read(tm.currentTeam); - bsIn.Read(tm.requestedTeam); - - if (tm.currentTeam!=UNASSIGNED_TEAM_ID && tm.currentTeam>teamLimits.Size()) - { - RakAssert("Current team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0); - return; - } - - if (tm.requestedTeam!=UNASSIGNED_TEAM_ID && tm.requestedTeam>teamLimits.Size()) - { - RakAssert("Requested team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0); - return; - } - - if (tm.currentTeam==UNASSIGNED_TEAM_ID && tm.requestedTeam==UNASSIGNED_TEAM_ID) - return; - - unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid); - if (memberIndex==(unsigned int) -1) - { - // Add this system (by GUID) to the list of members if he is not already there - // Also update his requested team flag. - // Do not process balancing on requested teams, since we don't necessarily have all data from all systems yet and hopefully the state during the host migration was stable. - if (tm.currentTeam==UNASSIGNED_TEAM_ID) - { - // Assign a default team, then add team member - if (tm.requestedTeam==UNASSIGNED_TEAM_ID) - { - // Assign a default team - tm.currentTeam=GetNextDefaultTeam(); - } - else - { - // Assign to requested team if possible. Otherwise, assign to a default team - if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false) - { - tm.currentTeam=tm.requestedTeam; - } - else - { - tm.currentTeam=GetNextDefaultTeam(); - } - } - } - - if (tm.currentTeam==UNASSIGNED_TEAM_ID) - { - RakAssert("Too many members asking for teams!" && 0); - return; - } - NotifyTeamAssigment(AddTeamMember(tm)); - } - } -} -void TeamBalancer::OnCancelTeamRequest(Packet *packet) -{ - if (WeAreHost()==false) - return; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - NetworkID memberId; - bsIn.Read(memberId); - - unsigned int memberIndex = GetMemberIndex(memberId, packet->guid); - if (memberIndex!=(unsigned int)-1) - teamMembers[memberIndex].requestedTeam=UNASSIGNED_TEAM_ID; -} -void TeamBalancer::OnRequestAnyTeam(Packet *packet) -{ - if (WeAreHost()==false) - return; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - NetworkID memberId; - bsIn.Read(memberId); - - unsigned int memberIndex = GetMemberIndex(memberId, packet->guid); - if (memberIndex==(unsigned int)-1) - { - TeamMember tm; - tm.currentTeam=GetNextDefaultTeam(); - tm.requestedTeam=UNASSIGNED_TEAM_ID; - tm.memberGuid=packet->guid; - tm.memberId=memberId; - if (tm.currentTeam==UNASSIGNED_TEAM_ID) - { - RakAssert("Too many members asking for teams!" && 0); - return; - } - NotifyTeamAssigment(AddTeamMember(tm)); - } -} -void TeamBalancer::OnRequestSpecificTeam(Packet *packet) -{ - if (WeAreHost()==false) - return; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - TeamMember tm; - bsIn.Read(tm.memberId); - bsIn.Read(tm.requestedTeam); - - unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid); - if (tm.requestedTeam==UNASSIGNED_TEAM_ID) - { - NotifyNoTeam(tm.memberId, packet->guid); - RemoveTeamMember(memberIndex); - return; - } - - if (tm.requestedTeam>teamLimits.Size()) - { - RakAssert("Requested team out of range in TeamBalancer::OnRequestSpecificTeam" && 0); - return; - } - if (memberIndex==(unsigned int) -1) - { - tm.memberGuid=packet->guid; - - // Assign to requested team if possible. Otherwise, assign to a default team - if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false) - { - tm.currentTeam=tm.requestedTeam; - tm.requestedTeam=UNASSIGNED_TEAM_ID; - } - else - { - tm.currentTeam=GetNextDefaultTeam(); - } - if (tm.currentTeam==UNASSIGNED_TEAM_ID) - { - RakAssert("Too many members asking for teams!" && 0); - return; - } - NotifyTeamAssigment(AddTeamMember(tm)); - } - else - { - teamMembers[memberIndex].requestedTeam=tm.requestedTeam; - TeamId oldTeamThisUserWasOn = teamMembers[memberIndex].currentTeam; - - if (lockTeams) - { - NotifyTeamsLocked(packet->guid, tm.requestedTeam); - return; - } - - // Assign to requested team if possible. Otherwise, assign to a default team - if (TeamsWouldBeEvenOnSwitch(tm.requestedTeam,oldTeamThisUserWasOn)==true) - { - SwitchMemberTeam(memberIndex,tm.requestedTeam); - NotifyTeamAssigment(memberIndex); - } - else - { - // If someone wants to join this user's old team, and we want to join their team, they can swap - unsigned int swappableMemberIndex; - for (swappableMemberIndex=0; swappableMemberIndex < teamMembers.Size(); swappableMemberIndex++) - { - if (teamMembers[swappableMemberIndex].currentTeam==tm.requestedTeam && teamMembers[swappableMemberIndex].requestedTeam==oldTeamThisUserWasOn) - break; - } - - if (swappableMemberIndex!=teamMembers.Size()) - { - SwapTeamMembersByRequest(memberIndex,swappableMemberIndex); - NotifyTeamAssigment(memberIndex); - NotifyTeamAssigment(swappableMemberIndex); - } - else - { - // Full or would not be even - NotifyTeamSwitchPending(packet->guid, tm.requestedTeam, tm.memberId); - } - } - } -} -unsigned int TeamBalancer::GetMemberIndex(NetworkID memberId, RakNetGUID guid) const -{ - for (unsigned int i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].memberGuid==guid && teamMembers[i].memberId==memberId) - return i; - } - return (unsigned int) -1; -} -unsigned int TeamBalancer::AddTeamMember(const TeamMember &tm) -{ - if (tm.currentTeam>teamLimits.Size()) - { - RakAssert("TeamBalancer::AddTeamMember team index out of bounds" && 0); - return (unsigned int) -1; - } - - RakAssert(tm.currentTeam!=UNASSIGNED_TEAM_ID); - - teamMembers.Push(tm,_FILE_AND_LINE_); - if (teamMemberCounts.Size() overpopulatedTeams; - TeamId teamMemberCountsIndex; - unsigned int memberIndexToSwitch; - for (teamMemberCountsIndex=0; teamMemberCountsIndex0); - memberIndexToSwitch=GetMemberIndexToSwitchTeams(overpopulatedTeams,teamMemberCountsIndex); - RakAssert(memberIndexToSwitch!=(unsigned int)-1); - SwitchMemberTeam(memberIndexToSwitch,teamMemberCountsIndex); - // Tell this member he switched teams - NotifyTeamAssigment(memberIndexToSwitch); - } - } -} -unsigned int TeamBalancer::GetMemberIndexToSwitchTeams(const DataStructures::List &sourceTeamNumbers, TeamId targetTeamNumber) -{ - DataStructures::List preferredSwapIndices; - DataStructures::List potentialSwapIndices; - unsigned int i,j; - for (j=0; j < sourceTeamNumbers.Size(); j++) - { - RakAssert(sourceTeamNumbers[j]!=targetTeamNumber); - for (i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].currentTeam==sourceTeamNumbers[j]) - { - if (teamMembers[i].requestedTeam==targetTeamNumber) - preferredSwapIndices.Push(i,_FILE_AND_LINE_); - else - potentialSwapIndices.Push(i,_FILE_AND_LINE_); - } - } - } - - if (preferredSwapIndices.Size()>0) - { - return preferredSwapIndices[ randomMT() % preferredSwapIndices.Size() ]; - } - else if (potentialSwapIndices.Size()>0) - { - return potentialSwapIndices[ randomMT() % potentialSwapIndices.Size() ]; - } - else - { - return (unsigned int) -1; - } -} -void TeamBalancer::SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam) -{ - teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]=teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]-1; - teamMemberCounts[ destinationTeam ]=teamMemberCounts[ destinationTeam ]+1; - teamMembers[teamMemberIndex].currentTeam=destinationTeam; - if (teamMembers[teamMemberIndex].requestedTeam==destinationTeam) - teamMembers[teamMemberIndex].requestedTeam=UNASSIGNED_TEAM_ID; -} -void TeamBalancer::GetOverpopulatedTeams(DataStructures::List &overpopulatedTeams, int maxTeamSize) -{ - overpopulatedTeams.Clear(true,_FILE_AND_LINE_); - for (TeamId i=0; i < teamMemberCounts.Size(); i++) - { - if (teamMemberCounts[i]>=maxTeamSize) - overpopulatedTeams.Push(i,_FILE_AND_LINE_); - } -} -void TeamBalancer::NotifyTeamAssigment(unsigned int teamMemberIndex) -{ - RakAssert(teamMemberIndex < teamMembers.Size()); - if (teamMemberIndex>=teamMembers.Size()) - return; - - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED); - bsOut.Write(teamMembers[teamMemberIndex].currentTeam); - bsOut.Write(teamMembers[teamMemberIndex].memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,teamMembers[teamMemberIndex].memberGuid,false); -} -bool TeamBalancer::WeAreHost(void) const -{ - return hostGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -} -PluginReceiveResult TeamBalancer::OnTeamAssigned(Packet *packet) -{ - if (packet->guid!=hostGuid) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(1); - - MyTeamMembers mtm; - bsIn.Read(mtm.currentTeam); - bsIn.Read(mtm.memberId); - mtm.requestedTeam=UNASSIGNED_TEAM_ID; - - bool foundMatch=false; - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==mtm.memberId) - { - foundMatch=true; - if (myTeamMembers[i].requestedTeam==mtm.currentTeam) - myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; - myTeamMembers[i].currentTeam=mtm.currentTeam; - break; - } - } - - if (foundMatch==false) - { - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - return RR_CONTINUE_PROCESSING; -} -PluginReceiveResult TeamBalancer::OnRequestedTeamChangePending(Packet *packet) -{ - if (packet->guid!=hostGuid) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - return RR_CONTINUE_PROCESSING; -} -PluginReceiveResult TeamBalancer::OnTeamsLocked(Packet *packet) -{ - if (packet->guid!=hostGuid) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - return RR_CONTINUE_PROCESSING; -} -TeamId TeamBalancer::GetNextDefaultTeam(void) -{ - // Accounting for team balancing and team limits, get the team a player should be placed on - switch (defaultAssigmentAlgorithm) - { - case SMALLEST_TEAM: - { - return GetSmallestNonFullTeam(); - } - - case FILL_IN_ORDER: - { - return GetFirstNonFullTeam(); - } - - default: - { - RakAssert("TeamBalancer::GetNextDefaultTeam unknown algorithm enumeration" && 0); - return UNASSIGNED_TEAM_ID; - } - } -} -bool TeamBalancer::TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize) -{ - // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it? - if (teamMemberCounts[teamId]>=teamLimits[teamId]) - { - return true; - } - - if (forceTeamsToBeEven) - { - int allowedLimit = teamMemberSize/teamLimits.Size() + 1; - return teamMemberCounts[teamId]>=allowedLimit; - } - - return false; -} -bool TeamBalancer::TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize) -{ - if (forceTeamsToBeEven) - { - unsigned int minMembersOnASingleTeam = (teamMemberSize-1)/teamLimits.Size(); - return teamMemberCounts[teamId]<=minMembersOnASingleTeam; - } - return false; -} -TeamId TeamBalancer::GetSmallestNonFullTeam(void) const -{ - TeamId idx; - unsigned long smallestTeamCount=MAX_UNSIGNED_LONG; - TeamId smallestTeamIndex = UNASSIGNED_TEAM_ID; - for (idx=0; idx < teamMemberCounts.Size(); idx++) - { - if (teamMemberCounts[idx] membersThatWantToJoinTheTeam; - for (TeamId i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].requestedTeam==teamId) - membersThatWantToJoinTheTeam.Push(i,_FILE_AND_LINE_); - } - - if (membersThatWantToJoinTheTeam.Size()>0) - { - TeamId oldTeam; - unsigned int swappedMemberIndex = membersThatWantToJoinTheTeam[ randomMT() % membersThatWantToJoinTheTeam.Size() ]; - oldTeam=teamMembers[swappedMemberIndex].currentTeam; - SwitchMemberTeam(swappedMemberIndex,teamId); - NotifyTeamAssigment(swappedMemberIndex); - return oldTeam; - } - return UNASSIGNED_TEAM_ID; -} -void TeamBalancer::NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam) -{ - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAMS_LOCKED); - bsOut.Write(requestedTeam); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); -} -void TeamBalancer::NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam, NetworkID memberId) -{ - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING); - bsOut.Write(requestedTeam); - bsOut.Write(memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); -} -void TeamBalancer::SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2) -{ - TeamId index1Team = teamMembers[memberIndex1].currentTeam; - teamMembers[memberIndex1].currentTeam=teamMembers[memberIndex2].currentTeam; - teamMembers[memberIndex2].currentTeam=index1Team; - teamMembers[memberIndex1].requestedTeam=UNASSIGNED_TEAM_ID; - teamMembers[memberIndex2].requestedTeam=UNASSIGNED_TEAM_ID; -} -void TeamBalancer::NotifyNoTeam(NetworkID memberId, RakNetGUID target) -{ - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED); - bsOut.Write((unsigned char)UNASSIGNED_TEAM_ID); - bsOut.Write(memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); -} -bool TeamBalancer::TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2) -{ - RakAssert(teamMembers.Size()!=0); - return TeamWouldBeOverpopulatedOnAddition(t1, teamMembers.Size()-1)==false && - TeamWouldBeUnderpopulatedOnLeave(t2, teamMembers.Size()-1)==false; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TeamBalancer==1 + +#include "TeamBalancer.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "Rand.h" + +using namespace RakNet; + +enum TeamBalancerOperations +{ + ID_STATUS_UPDATE_TO_NEW_HOST, + ID_CANCEL_TEAM_REQUEST, + ID_REQUEST_ANY_TEAM, + ID_REQUEST_SPECIFIC_TEAM +}; + +STATIC_FACTORY_DEFINITIONS(TeamBalancer,TeamBalancer); + +TeamBalancer::TeamBalancer() +{ + defaultAssigmentAlgorithm=SMALLEST_TEAM; + forceTeamsToBeEven=false; + lockTeams=false; + hostGuid=UNASSIGNED_RAKNET_GUID; +} +TeamBalancer::~TeamBalancer() +{ + +} +void TeamBalancer::SetTeamSizeLimit(TeamId team, unsigned short limit) +{ + teamLimits.Replace(limit,0,team,_FILE_AND_LINE_); + if (teamLimits.Size() > teamMemberCounts.Size()) + teamMemberCounts.Replace(0,0,teamLimits.Size()-1,_FILE_AND_LINE_); +} +void TeamBalancer::SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa) +{ + // Just update the default. Currently active teams are not affected. + defaultAssigmentAlgorithm=daa; +} +void TeamBalancer::SetForceEvenTeams(bool force) +{ + // Set flag to indicate that teams should be even. + forceTeamsToBeEven=force; + + // If teams are locked, just return. + if (lockTeams==true) + return; + + if (forceTeamsToBeEven==true) + { + // Run the even team algorithm + EvenTeams(); + } +} +void TeamBalancer::SetLockTeams(bool lock) +{ + if (lock==lockTeams) + return; + + // Set flag to indicate that teams can no longer be changed. + lockTeams=lock; + + // If lock is false, and teams were set to be forced as even, then run through the even team algorithm + if (lockTeams==false) + { + // Process even swaps + TeamId i,j; + for (i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].requestedTeam!=UNASSIGNED_TEAM_ID) + { + for (j=i+1; j < teamMembers.Size(); j++) + { + if (teamMembers[j].requestedTeam==teamMembers[i].currentTeam && + teamMembers[i].requestedTeam==teamMembers[j].currentTeam) + { + SwapTeamMembersByRequest(i,j); + NotifyTeamAssigment(i); + NotifyTeamAssigment(j); + } + } + } + } + + if (forceTeamsToBeEven==true) + { + EvenTeams(); + } + else + { + // Process requested team changes + // Process movement while not full + for (i=0; i < teamMembers.Size(); i++) + { + TeamId requestedTeam = teamMembers[i].requestedTeam; + if (requestedTeam!=UNASSIGNED_TEAM_ID) + { + if (teamMemberCounts[requestedTeam]Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); +} +void TeamBalancer::CancelRequestSpecificTeam(NetworkID memberId) +{ + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==memberId) + { + myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; + + // Send packet to the host to remove our request flag. + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); + bsOut.Write((MessageID)ID_CANCEL_TEAM_REQUEST); + bsOut.Write(memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); + + return; + } + } +} +void TeamBalancer::RequestAnyTeam(NetworkID memberId) +{ + bool foundMatch=false; + + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==memberId) + { + foundMatch=true; + if (myTeamMembers[i].currentTeam!=UNASSIGNED_TEAM_ID) + return; + else + myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; + break; + } + } + + if (foundMatch==false) + { + MyTeamMembers mtm; + mtm.currentTeam=UNASSIGNED_TEAM_ID; + mtm.memberId=memberId; + mtm.requestedTeam=UNASSIGNED_TEAM_ID; + myTeamMembers.Push(mtm, _FILE_AND_LINE_); + } + + // Else send to the current host that we need a team. + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); + bsOut.Write((MessageID)ID_REQUEST_ANY_TEAM); + bsOut.Write(memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); +} +TeamId TeamBalancer::GetMyTeam(NetworkID memberId) const +{ + // Return team returned by last ID_TEAM_BALANCER_TEAM_ASSIGNED packet + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==memberId) + { + return myTeamMembers[i].currentTeam; + } + } + + return UNASSIGNED_TEAM_ID; +} +void TeamBalancer::DeleteMember(NetworkID memberId) +{ + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==memberId) + { + myTeamMembers.RemoveAtIndexFast(i); + break; + } + } + + for (unsigned int i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].memberId==memberId) + { + RemoveTeamMember(i); + break; + } + } +} +PluginReceiveResult TeamBalancer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_FCM2_NEW_HOST: + { + hostGuid=packet->guid; + + if (myTeamMembers.Size()>0) + { + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); + bsOut.Write((MessageID)ID_STATUS_UPDATE_TO_NEW_HOST); + + bsOut.WriteCasted(myTeamMembers.Size()); + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + bsOut.Write(myTeamMembers[i].memberId); + bsOut.Write(myTeamMembers[i].currentTeam); + bsOut.Write(myTeamMembers[i].requestedTeam); + } + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); + } + } + break; + case ID_TEAM_BALANCER_INTERNAL: + { + if (packet->length>=2) + { + switch (packet->data[1]) + { + case ID_STATUS_UPDATE_TO_NEW_HOST: + OnStatusUpdateToNewHost(packet); + break; + case ID_CANCEL_TEAM_REQUEST: + OnCancelTeamRequest(packet); + break; + case ID_REQUEST_ANY_TEAM: + OnRequestAnyTeam(packet); + break; + case ID_REQUEST_SPECIFIC_TEAM: + OnRequestSpecificTeam(packet); + break; + } + } + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + case ID_TEAM_BALANCER_TEAM_ASSIGNED: + { + return OnTeamAssigned(packet); + } + + case ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING: + { + return OnRequestedTeamChangePending(packet); + } + + case ID_TEAM_BALANCER_TEAMS_LOCKED: + { + return OnTeamsLocked(packet); + } + } + + // Got RequestSpecificTeam + // If teams are locked + // - If this user already has a team, return ID_TEAM_BALANCER_TEAMS_LOCKED + // - This user does not already have a team. Assign a team as if the user called RequestAnyTeam(), with a preference for the requested team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED once the team has been assigned. + // If teams are not locked + // - If even team balancing is on, only assign this user if this would not cause teams to be unbalanced. If teams WOULD be unbalanced, then flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING + // - If the destination team is full, flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING + // - Else, join this team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED + + // Got RequestAnyTeam + // Put user on a team following the algorithm. No team is set as preferred. + + return RR_CONTINUE_PROCESSING; +} +void TeamBalancer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) systemAddress; + (void) lostConnectionReason; + + RemoveByGuid(rakNetGUID); +} +void TeamBalancer::OnAttach(void) +{ + hostGuid = rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +} +void TeamBalancer::RemoveByGuid(RakNetGUID rakNetGUID) +{ + // If we are the host, and the closed connection has a team, and teams are not locked: + if (WeAreHost()) + { + unsigned int droppedMemberIndex=0; + while (droppedMemberIndex < teamMembers.Size()) + { + if (teamMembers[droppedMemberIndex].memberGuid==rakNetGUID) + { + TeamId droppedTeam = teamMembers[droppedMemberIndex].currentTeam; + RemoveTeamMember(droppedMemberIndex); + if (lockTeams==false) + { + if (forceTeamsToBeEven) + { + // - teams were forced to be even, then run the even team algorithm + EvenTeams(); + } + else + { + // - teams were NOT forced to be even, and the team the dropped player on was full, then move users wanting to join that team (if any) + if (teamMemberCounts[ droppedTeam ]==teamLimits[ droppedTeam ]-1) + { + MoveMemberThatWantsToJoinTeam(droppedTeam); + } + } + } + } + else + { + droppedMemberIndex++; + } + } + } +} +void TeamBalancer::OnStatusUpdateToNewHost(Packet *packet) +{ + if (WeAreHost()==false) + return; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + uint8_t requestedTeamChangeListSize; + bsIn.Read(requestedTeamChangeListSize); + TeamMember tm; + tm.memberGuid=packet->guid; + for (uint8_t i=0; i < requestedTeamChangeListSize; i++) + { + bsIn.Read(tm.memberId); + bsIn.Read(tm.currentTeam); + bsIn.Read(tm.requestedTeam); + + if (tm.currentTeam!=UNASSIGNED_TEAM_ID && tm.currentTeam>teamLimits.Size()) + { + RakAssert("Current team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0); + return; + } + + if (tm.requestedTeam!=UNASSIGNED_TEAM_ID && tm.requestedTeam>teamLimits.Size()) + { + RakAssert("Requested team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0); + return; + } + + if (tm.currentTeam==UNASSIGNED_TEAM_ID && tm.requestedTeam==UNASSIGNED_TEAM_ID) + return; + + unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid); + if (memberIndex==(unsigned int) -1) + { + // Add this system (by GUID) to the list of members if he is not already there + // Also update his requested team flag. + // Do not process balancing on requested teams, since we don't necessarily have all data from all systems yet and hopefully the state during the host migration was stable. + if (tm.currentTeam==UNASSIGNED_TEAM_ID) + { + // Assign a default team, then add team member + if (tm.requestedTeam==UNASSIGNED_TEAM_ID) + { + // Assign a default team + tm.currentTeam=GetNextDefaultTeam(); + } + else + { + // Assign to requested team if possible. Otherwise, assign to a default team + if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false) + { + tm.currentTeam=tm.requestedTeam; + } + else + { + tm.currentTeam=GetNextDefaultTeam(); + } + } + } + + if (tm.currentTeam==UNASSIGNED_TEAM_ID) + { + RakAssert("Too many members asking for teams!" && 0); + return; + } + NotifyTeamAssigment(AddTeamMember(tm)); + } + } +} +void TeamBalancer::OnCancelTeamRequest(Packet *packet) +{ + if (WeAreHost()==false) + return; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + NetworkID memberId; + bsIn.Read(memberId); + + unsigned int memberIndex = GetMemberIndex(memberId, packet->guid); + if (memberIndex!=(unsigned int)-1) + teamMembers[memberIndex].requestedTeam=UNASSIGNED_TEAM_ID; +} +void TeamBalancer::OnRequestAnyTeam(Packet *packet) +{ + if (WeAreHost()==false) + return; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + NetworkID memberId; + bsIn.Read(memberId); + + unsigned int memberIndex = GetMemberIndex(memberId, packet->guid); + if (memberIndex==(unsigned int)-1) + { + TeamMember tm; + tm.currentTeam=GetNextDefaultTeam(); + tm.requestedTeam=UNASSIGNED_TEAM_ID; + tm.memberGuid=packet->guid; + tm.memberId=memberId; + if (tm.currentTeam==UNASSIGNED_TEAM_ID) + { + RakAssert("Too many members asking for teams!" && 0); + return; + } + NotifyTeamAssigment(AddTeamMember(tm)); + } +} +void TeamBalancer::OnRequestSpecificTeam(Packet *packet) +{ + if (WeAreHost()==false) + return; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + TeamMember tm; + bsIn.Read(tm.memberId); + bsIn.Read(tm.requestedTeam); + + unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid); + if (tm.requestedTeam==UNASSIGNED_TEAM_ID) + { + NotifyNoTeam(tm.memberId, packet->guid); + RemoveTeamMember(memberIndex); + return; + } + + if (tm.requestedTeam>teamLimits.Size()) + { + RakAssert("Requested team out of range in TeamBalancer::OnRequestSpecificTeam" && 0); + return; + } + if (memberIndex==(unsigned int) -1) + { + tm.memberGuid=packet->guid; + + // Assign to requested team if possible. Otherwise, assign to a default team + if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false) + { + tm.currentTeam=tm.requestedTeam; + tm.requestedTeam=UNASSIGNED_TEAM_ID; + } + else + { + tm.currentTeam=GetNextDefaultTeam(); + } + if (tm.currentTeam==UNASSIGNED_TEAM_ID) + { + RakAssert("Too many members asking for teams!" && 0); + return; + } + NotifyTeamAssigment(AddTeamMember(tm)); + } + else + { + teamMembers[memberIndex].requestedTeam=tm.requestedTeam; + TeamId oldTeamThisUserWasOn = teamMembers[memberIndex].currentTeam; + + if (lockTeams) + { + NotifyTeamsLocked(packet->guid, tm.requestedTeam); + return; + } + + // Assign to requested team if possible. Otherwise, assign to a default team + if (TeamsWouldBeEvenOnSwitch(tm.requestedTeam,oldTeamThisUserWasOn)==true) + { + SwitchMemberTeam(memberIndex,tm.requestedTeam); + NotifyTeamAssigment(memberIndex); + } + else + { + // If someone wants to join this user's old team, and we want to join their team, they can swap + unsigned int swappableMemberIndex; + for (swappableMemberIndex=0; swappableMemberIndex < teamMembers.Size(); swappableMemberIndex++) + { + if (teamMembers[swappableMemberIndex].currentTeam==tm.requestedTeam && teamMembers[swappableMemberIndex].requestedTeam==oldTeamThisUserWasOn) + break; + } + + if (swappableMemberIndex!=teamMembers.Size()) + { + SwapTeamMembersByRequest(memberIndex,swappableMemberIndex); + NotifyTeamAssigment(memberIndex); + NotifyTeamAssigment(swappableMemberIndex); + } + else + { + // Full or would not be even + NotifyTeamSwitchPending(packet->guid, tm.requestedTeam, tm.memberId); + } + } + } +} +unsigned int TeamBalancer::GetMemberIndex(NetworkID memberId, RakNetGUID guid) const +{ + for (unsigned int i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].memberGuid==guid && teamMembers[i].memberId==memberId) + return i; + } + return (unsigned int) -1; +} +unsigned int TeamBalancer::AddTeamMember(const TeamMember &tm) +{ + if (tm.currentTeam>teamLimits.Size()) + { + RakAssert("TeamBalancer::AddTeamMember team index out of bounds" && 0); + return (unsigned int) -1; + } + + RakAssert(tm.currentTeam!=UNASSIGNED_TEAM_ID); + + teamMembers.Push(tm,_FILE_AND_LINE_); + if (teamMemberCounts.Size() overpopulatedTeams; + TeamId teamMemberCountsIndex; + unsigned int memberIndexToSwitch; + for (teamMemberCountsIndex=0; teamMemberCountsIndex0); + memberIndexToSwitch=GetMemberIndexToSwitchTeams(overpopulatedTeams,teamMemberCountsIndex); + RakAssert(memberIndexToSwitch!=(unsigned int)-1); + SwitchMemberTeam(memberIndexToSwitch,teamMemberCountsIndex); + // Tell this member he switched teams + NotifyTeamAssigment(memberIndexToSwitch); + } + } +} +unsigned int TeamBalancer::GetMemberIndexToSwitchTeams(const DataStructures::List &sourceTeamNumbers, TeamId targetTeamNumber) +{ + DataStructures::List preferredSwapIndices; + DataStructures::List potentialSwapIndices; + unsigned int i,j; + for (j=0; j < sourceTeamNumbers.Size(); j++) + { + RakAssert(sourceTeamNumbers[j]!=targetTeamNumber); + for (i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].currentTeam==sourceTeamNumbers[j]) + { + if (teamMembers[i].requestedTeam==targetTeamNumber) + preferredSwapIndices.Push(i,_FILE_AND_LINE_); + else + potentialSwapIndices.Push(i,_FILE_AND_LINE_); + } + } + } + + if (preferredSwapIndices.Size()>0) + { + return preferredSwapIndices[ randomMT() % preferredSwapIndices.Size() ]; + } + else if (potentialSwapIndices.Size()>0) + { + return potentialSwapIndices[ randomMT() % potentialSwapIndices.Size() ]; + } + else + { + return (unsigned int) -1; + } +} +void TeamBalancer::SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam) +{ + teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]=teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]-1; + teamMemberCounts[ destinationTeam ]=teamMemberCounts[ destinationTeam ]+1; + teamMembers[teamMemberIndex].currentTeam=destinationTeam; + if (teamMembers[teamMemberIndex].requestedTeam==destinationTeam) + teamMembers[teamMemberIndex].requestedTeam=UNASSIGNED_TEAM_ID; +} +void TeamBalancer::GetOverpopulatedTeams(DataStructures::List &overpopulatedTeams, int maxTeamSize) +{ + overpopulatedTeams.Clear(true,_FILE_AND_LINE_); + for (TeamId i=0; i < teamMemberCounts.Size(); i++) + { + if (teamMemberCounts[i]>=maxTeamSize) + overpopulatedTeams.Push(i,_FILE_AND_LINE_); + } +} +void TeamBalancer::NotifyTeamAssigment(unsigned int teamMemberIndex) +{ + RakAssert(teamMemberIndex < teamMembers.Size()); + if (teamMemberIndex>=teamMembers.Size()) + return; + + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED); + bsOut.Write(teamMembers[teamMemberIndex].currentTeam); + bsOut.Write(teamMembers[teamMemberIndex].memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,teamMembers[teamMemberIndex].memberGuid,false); +} +bool TeamBalancer::WeAreHost(void) const +{ + return hostGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +} +PluginReceiveResult TeamBalancer::OnTeamAssigned(Packet *packet) +{ + if (packet->guid!=hostGuid) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(1); + + MyTeamMembers mtm; + bsIn.Read(mtm.currentTeam); + bsIn.Read(mtm.memberId); + mtm.requestedTeam=UNASSIGNED_TEAM_ID; + + bool foundMatch=false; + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==mtm.memberId) + { + foundMatch=true; + if (myTeamMembers[i].requestedTeam==mtm.currentTeam) + myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; + myTeamMembers[i].currentTeam=mtm.currentTeam; + break; + } + } + + if (foundMatch==false) + { + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + return RR_CONTINUE_PROCESSING; +} +PluginReceiveResult TeamBalancer::OnRequestedTeamChangePending(Packet *packet) +{ + if (packet->guid!=hostGuid) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + return RR_CONTINUE_PROCESSING; +} +PluginReceiveResult TeamBalancer::OnTeamsLocked(Packet *packet) +{ + if (packet->guid!=hostGuid) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + return RR_CONTINUE_PROCESSING; +} +TeamId TeamBalancer::GetNextDefaultTeam(void) +{ + // Accounting for team balancing and team limits, get the team a player should be placed on + switch (defaultAssigmentAlgorithm) + { + case SMALLEST_TEAM: + { + return GetSmallestNonFullTeam(); + } + + case FILL_IN_ORDER: + { + return GetFirstNonFullTeam(); + } + + default: + { + RakAssert("TeamBalancer::GetNextDefaultTeam unknown algorithm enumeration" && 0); + return UNASSIGNED_TEAM_ID; + } + } +} +bool TeamBalancer::TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize) +{ + // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it? + if (teamMemberCounts[teamId]>=teamLimits[teamId]) + { + return true; + } + + if (forceTeamsToBeEven) + { + int allowedLimit = teamMemberSize/teamLimits.Size() + 1; + return teamMemberCounts[teamId]>=allowedLimit; + } + + return false; +} +bool TeamBalancer::TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize) +{ + if (forceTeamsToBeEven) + { + unsigned int minMembersOnASingleTeam = (teamMemberSize-1)/teamLimits.Size(); + return teamMemberCounts[teamId]<=minMembersOnASingleTeam; + } + return false; +} +TeamId TeamBalancer::GetSmallestNonFullTeam(void) const +{ + TeamId idx; + unsigned long smallestTeamCount=MAX_UNSIGNED_LONG; + TeamId smallestTeamIndex = UNASSIGNED_TEAM_ID; + for (idx=0; idx < teamMemberCounts.Size(); idx++) + { + if (teamMemberCounts[idx] membersThatWantToJoinTheTeam; + for (TeamId i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].requestedTeam==teamId) + membersThatWantToJoinTheTeam.Push(i,_FILE_AND_LINE_); + } + + if (membersThatWantToJoinTheTeam.Size()>0) + { + TeamId oldTeam; + unsigned int swappedMemberIndex = membersThatWantToJoinTheTeam[ randomMT() % membersThatWantToJoinTheTeam.Size() ]; + oldTeam=teamMembers[swappedMemberIndex].currentTeam; + SwitchMemberTeam(swappedMemberIndex,teamId); + NotifyTeamAssigment(swappedMemberIndex); + return oldTeam; + } + return UNASSIGNED_TEAM_ID; +} +void TeamBalancer::NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam) +{ + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAMS_LOCKED); + bsOut.Write(requestedTeam); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); +} +void TeamBalancer::NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam, NetworkID memberId) +{ + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING); + bsOut.Write(requestedTeam); + bsOut.Write(memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); +} +void TeamBalancer::SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2) +{ + TeamId index1Team = teamMembers[memberIndex1].currentTeam; + teamMembers[memberIndex1].currentTeam=teamMembers[memberIndex2].currentTeam; + teamMembers[memberIndex2].currentTeam=index1Team; + teamMembers[memberIndex1].requestedTeam=UNASSIGNED_TEAM_ID; + teamMembers[memberIndex2].requestedTeam=UNASSIGNED_TEAM_ID; +} +void TeamBalancer::NotifyNoTeam(NetworkID memberId, RakNetGUID target) +{ + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED); + bsOut.Write((unsigned char)UNASSIGNED_TEAM_ID); + bsOut.Write(memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); +} +bool TeamBalancer::TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2) +{ + RakAssert(teamMembers.Size()!=0); + return TeamWouldBeOverpopulatedOnAddition(t1, teamMembers.Size()-1)==false && + TeamWouldBeUnderpopulatedOnLeave(t2, teamMembers.Size()-1)==false; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/TeamBalancer.h b/project/lib_projects/raknet/jni/RaknetSources/TeamBalancer.h index 1776aca..502cb35 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TeamBalancer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/TeamBalancer.h @@ -1,194 +1,194 @@ -/// \file TeamBalancer.h -/// \brief Set and network team selection (supports peer to peer or client/server) -/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TeamBalancer==1 - -#ifndef __TEAM_BALANCER_H -#define __TEAM_BALANCER_H - -#include "PluginInterface2.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" -#include "DS_List.h" -#include "RakString.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \defgroup TEAM_BALANCER_GROUP TeamBalancer -/// \brief Set and network team selection (supports peer to peer or client/server) -/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing -/// \ingroup PLUGINS_GROUP - -/// 0...254 for your team number identifiers. 255 is reserved as undefined. -/// \ingroup TEAM_BALANCER_GROUP -typedef unsigned char TeamId; - -#define UNASSIGNED_TEAM_ID 255 - -/// \brief Set and network team selection (supports peer to peer or client/server) -/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing.
      -/// Usage: TODO -/// \ingroup TEAM_BALANCER_GROUP -class RAK_DLL_EXPORT TeamBalancer : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(TeamBalancer) - - TeamBalancer(); - virtual ~TeamBalancer(); - - /// \brief Set the limit to the number of players on the specified team - /// \details SetTeamSizeLimit() must be called on the host, so the host can enforce the maximum number of players on each team. - /// SetTeamSizeLimit() can be called on all systems if desired - for example, in a P2P environment you may wish to call it on all systems in advanced in case you become host. - /// \param[in] team Which team to set the limit for - /// \param[in] limit The maximum number of people on this team - void SetTeamSizeLimit(TeamId team, unsigned short limit); - - enum DefaultAssigmentAlgorithm - { - /// Among all the teams, join the team with the smallest number of players - SMALLEST_TEAM, - /// Join the team with the lowest index that has open slots. - FILL_IN_ORDER - }; - /// \brief Determine how players' teams will be set when they call RequestAnyTeam() - /// \details Based on the specified enumeration, a player will join a team automatically - /// Defaults to SMALLEST_TEAM - /// This function is only used by the host - /// \param[in] daa Enumeration describing the algorithm to use - void SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa); - - /// \brief By default, teams can be unbalanced up to the team size limit defined by SetTeamSizeLimits() - /// \details If SetForceEvenTeams(true) is called on the host, then teams cannot be unbalanced by more than 1 player - /// If teams are uneven at the time that SetForceEvenTeams(true) is called, players at randomly will be switched, and will be notified of ID_TEAM_BALANCER_TEAM_ASSIGNED - /// If players disconnect from the host such that teams would not be even, and teams are not locked, then a player from the largest team is randomly moved to even the teams. - /// Defaults to false - /// \note SetLockTeams(true) takes priority over SetForceEvenTeams(), so if teams are currently locked, this function will have no effect until teams become unlocked. - /// \param[in] force True to force even teams. False to allow teams to not be evenly matched - void SetForceEvenTeams(bool force); - - /// \brief If set, calls to RequestSpecificTeam() and RequestAnyTeam() will return the team you are currently on. - /// \details However, if those functions are called and you do not have a team, then you will be assigned to a default team according to SetDefaultAssignmentAlgorithm() and possibly SetForceEvenTeams(true) - /// If \a lock is false, and SetForceEvenTeams() was called with \a force as true, and teams are currently uneven, they will be made even, and those players randomly moved will get ID_TEAM_BALANCER_TEAM_ASSIGNED - /// Defaults to false - /// \param[in] lock True to lock teams, false to unlock - void SetLockTeams(bool lock); - - /// Set your requested team. UNASSIGNED_TEAM_ID means no team. - /// After enough time for network communication, ID_TEAM_BALANCER_SET_TEAM will be returned with your current team, or - /// If team switch is not possible, ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING or ID_TEAM_BALANCER_TEAMS_LOCKED will be returned. - /// In the case of ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING the request will stay in memory. ID_TEAM_BALANCER_SET_TEAM will be returned when someone on the desired team leaves or wants to switch to your team. - /// If SetLockTeams(true) is called while you have a request pending, you will get ID_TEAM_BALANCER_TEAMS_LOCKED - /// \pre Call SetTeamSizeLimits() on the host and call SetHostGuid() on this system. If the host is not running the TeamBalancer plugin or did not have SetTeamSizeLimits() called, then you will not get any response. - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - /// \param[in] desiredTeam An index representing your team number. The index should range from 0 to one less than the size of the list passed to SetTeamSizeLimits() on the host. You can also pass UNASSIGNED_TEAM_ID to not be on any team (such as if spectating) - void RequestSpecificTeam(NetworkID memberId, TeamId desiredTeam); - - /// If ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING is returned after a call to RequestSpecificTeam(), the request will stay in memory on the host and execute when available, or until the teams become locked. - /// You can cancel the request by calling CancelRequestSpecificTeam(), in which case you will stay on your existing team. - /// \note Due to latency, even after calling CancelRequestSpecificTeam() you may still get ID_TEAM_BALANCER_SET_TEAM if the packet was already in transmission. - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - void CancelRequestSpecificTeam(NetworkID memberId); - - /// Allow host to pick your team, based on whatever algorithm it uses for default team assignments. - /// This only has an effect if you are not currently on a team (GetMyTeam() returns UNASSIGNED_TEAM_ID) - /// \pre Call SetTeamSizeLimits() on the host and call SetHostGuid() on this system - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - void RequestAnyTeam(NetworkID memberId); - - /// Returns your team. - /// As your team changes, you are notified through the ID_TEAM_BALANCER_TEAM_ASSIGNED packet in byte 1. - /// Returns UNASSIGNED_TEAM_ID initially - /// \pre For this to return anything other than UNASSIGNED_TEAM_ID, connect to a properly initialized host and RequestSpecificTeam() or RequestAnyTeam() first - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - /// \return UNASSIGNED_TEAM_ID for no team. Otherwise, the index should range from 0 to one less than the size of the list passed to SetTeamSizeLimits() on the host - TeamId GetMyTeam(NetworkID memberId) const; - - /// If you called RequestSpecificTeam() or RequestAnyTeam() with a value for \a memberId that - /// Has since been deleted, call DeleteMember(). to notify this plugin of that event. - /// Not necessary with only one team member per system - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - void DeleteMember(NetworkID memberId); - - struct TeamMember - { - RakNetGUID memberGuid; - NetworkID memberId; - TeamId currentTeam; - TeamId requestedTeam; - }; - struct MyTeamMembers - { - NetworkID memberId; - TeamId currentTeam; - TeamId requestedTeam; - }; - -protected: - - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal - void OnAttach(void); - - void OnStatusUpdateToNewHost(Packet *packet); - void OnCancelTeamRequest(Packet *packet); - void OnRequestAnyTeam(Packet *packet); - void OnRequestSpecificTeam(Packet *packet); - - RakNetGUID hostGuid; - DefaultAssigmentAlgorithm defaultAssigmentAlgorithm; - bool forceTeamsToBeEven; - bool lockTeams; - // So if we lose the connection while processing, we request the same info of the new host - DataStructures::List myTeamMembers; - - DataStructures::List teamLimits; - DataStructures::List teamMemberCounts; - DataStructures::List teamMembers; - unsigned int GetMemberIndex(NetworkID memberId, RakNetGUID guid) const; - unsigned int AddTeamMember(const TeamMember &tm); // Returns index of new member - void RemoveTeamMember(unsigned int index); - void EvenTeams(void); - unsigned int GetMemberIndexToSwitchTeams(const DataStructures::List &sourceTeamNumbers, TeamId targetTeamNumber); - void GetOverpopulatedTeams(DataStructures::List &overpopulatedTeams, int maxTeamSize); - void SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam); - void NotifyTeamAssigment(unsigned int teamMemberIndex); - bool WeAreHost(void) const; - PluginReceiveResult OnTeamAssigned(Packet *packet); - PluginReceiveResult OnRequestedTeamChangePending(Packet *packet); - PluginReceiveResult OnTeamsLocked(Packet *packet); - void GetMinMaxTeamMembers(int &minMembersOnASingleTeam, int &maxMembersOnASingleTeam); - TeamId GetNextDefaultTeam(void); // Accounting for team balancing and team limits, get the team a player should be placed on - bool TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize); // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it? - bool TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize); - TeamId GetSmallestNonFullTeam(void) const; - TeamId GetFirstNonFullTeam(void) const; - void MoveMemberThatWantsToJoinTeam(TeamId teamId); - TeamId MoveMemberThatWantsToJoinTeamInternal(TeamId teamId); - void NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam); - void NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam, NetworkID memberId); - void NotifyNoTeam(NetworkID memberId, RakNetGUID target); - void SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2); - void RemoveByGuid(RakNetGUID rakNetGUID); - bool TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2); - -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file TeamBalancer.h +/// \brief Set and network team selection (supports peer to peer or client/server) +/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TeamBalancer==1 + +#ifndef __TEAM_BALANCER_H +#define __TEAM_BALANCER_H + +#include "PluginInterface2.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" +#include "DS_List.h" +#include "RakString.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \defgroup TEAM_BALANCER_GROUP TeamBalancer +/// \brief Set and network team selection (supports peer to peer or client/server) +/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing +/// \ingroup PLUGINS_GROUP + +/// 0...254 for your team number identifiers. 255 is reserved as undefined. +/// \ingroup TEAM_BALANCER_GROUP +typedef unsigned char TeamId; + +#define UNASSIGNED_TEAM_ID 255 + +/// \brief Set and network team selection (supports peer to peer or client/server) +/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing.
      +/// Usage: TODO +/// \ingroup TEAM_BALANCER_GROUP +class RAK_DLL_EXPORT TeamBalancer : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(TeamBalancer) + + TeamBalancer(); + virtual ~TeamBalancer(); + + /// \brief Set the limit to the number of players on the specified team + /// \details SetTeamSizeLimit() must be called on the host, so the host can enforce the maximum number of players on each team. + /// SetTeamSizeLimit() can be called on all systems if desired - for example, in a P2P environment you may wish to call it on all systems in advanced in case you become host. + /// \param[in] team Which team to set the limit for + /// \param[in] limit The maximum number of people on this team + void SetTeamSizeLimit(TeamId team, unsigned short limit); + + enum DefaultAssigmentAlgorithm + { + /// Among all the teams, join the team with the smallest number of players + SMALLEST_TEAM, + /// Join the team with the lowest index that has open slots. + FILL_IN_ORDER + }; + /// \brief Determine how players' teams will be set when they call RequestAnyTeam() + /// \details Based on the specified enumeration, a player will join a team automatically + /// Defaults to SMALLEST_TEAM + /// This function is only used by the host + /// \param[in] daa Enumeration describing the algorithm to use + void SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa); + + /// \brief By default, teams can be unbalanced up to the team size limit defined by SetTeamSizeLimits() + /// \details If SetForceEvenTeams(true) is called on the host, then teams cannot be unbalanced by more than 1 player + /// If teams are uneven at the time that SetForceEvenTeams(true) is called, players at randomly will be switched, and will be notified of ID_TEAM_BALANCER_TEAM_ASSIGNED + /// If players disconnect from the host such that teams would not be even, and teams are not locked, then a player from the largest team is randomly moved to even the teams. + /// Defaults to false + /// \note SetLockTeams(true) takes priority over SetForceEvenTeams(), so if teams are currently locked, this function will have no effect until teams become unlocked. + /// \param[in] force True to force even teams. False to allow teams to not be evenly matched + void SetForceEvenTeams(bool force); + + /// \brief If set, calls to RequestSpecificTeam() and RequestAnyTeam() will return the team you are currently on. + /// \details However, if those functions are called and you do not have a team, then you will be assigned to a default team according to SetDefaultAssignmentAlgorithm() and possibly SetForceEvenTeams(true) + /// If \a lock is false, and SetForceEvenTeams() was called with \a force as true, and teams are currently uneven, they will be made even, and those players randomly moved will get ID_TEAM_BALANCER_TEAM_ASSIGNED + /// Defaults to false + /// \param[in] lock True to lock teams, false to unlock + void SetLockTeams(bool lock); + + /// Set your requested team. UNASSIGNED_TEAM_ID means no team. + /// After enough time for network communication, ID_TEAM_BALANCER_SET_TEAM will be returned with your current team, or + /// If team switch is not possible, ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING or ID_TEAM_BALANCER_TEAMS_LOCKED will be returned. + /// In the case of ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING the request will stay in memory. ID_TEAM_BALANCER_SET_TEAM will be returned when someone on the desired team leaves or wants to switch to your team. + /// If SetLockTeams(true) is called while you have a request pending, you will get ID_TEAM_BALANCER_TEAMS_LOCKED + /// \pre Call SetTeamSizeLimits() on the host and call SetHostGuid() on this system. If the host is not running the TeamBalancer plugin or did not have SetTeamSizeLimits() called, then you will not get any response. + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + /// \param[in] desiredTeam An index representing your team number. The index should range from 0 to one less than the size of the list passed to SetTeamSizeLimits() on the host. You can also pass UNASSIGNED_TEAM_ID to not be on any team (such as if spectating) + void RequestSpecificTeam(NetworkID memberId, TeamId desiredTeam); + + /// If ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING is returned after a call to RequestSpecificTeam(), the request will stay in memory on the host and execute when available, or until the teams become locked. + /// You can cancel the request by calling CancelRequestSpecificTeam(), in which case you will stay on your existing team. + /// \note Due to latency, even after calling CancelRequestSpecificTeam() you may still get ID_TEAM_BALANCER_SET_TEAM if the packet was already in transmission. + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + void CancelRequestSpecificTeam(NetworkID memberId); + + /// Allow host to pick your team, based on whatever algorithm it uses for default team assignments. + /// This only has an effect if you are not currently on a team (GetMyTeam() returns UNASSIGNED_TEAM_ID) + /// \pre Call SetTeamSizeLimits() on the host and call SetHostGuid() on this system + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + void RequestAnyTeam(NetworkID memberId); + + /// Returns your team. + /// As your team changes, you are notified through the ID_TEAM_BALANCER_TEAM_ASSIGNED packet in byte 1. + /// Returns UNASSIGNED_TEAM_ID initially + /// \pre For this to return anything other than UNASSIGNED_TEAM_ID, connect to a properly initialized host and RequestSpecificTeam() or RequestAnyTeam() first + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + /// \return UNASSIGNED_TEAM_ID for no team. Otherwise, the index should range from 0 to one less than the size of the list passed to SetTeamSizeLimits() on the host + TeamId GetMyTeam(NetworkID memberId) const; + + /// If you called RequestSpecificTeam() or RequestAnyTeam() with a value for \a memberId that + /// Has since been deleted, call DeleteMember(). to notify this plugin of that event. + /// Not necessary with only one team member per system + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + void DeleteMember(NetworkID memberId); + + struct TeamMember + { + RakNetGUID memberGuid; + NetworkID memberId; + TeamId currentTeam; + TeamId requestedTeam; + }; + struct MyTeamMembers + { + NetworkID memberId; + TeamId currentTeam; + TeamId requestedTeam; + }; + +protected: + + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal + void OnAttach(void); + + void OnStatusUpdateToNewHost(Packet *packet); + void OnCancelTeamRequest(Packet *packet); + void OnRequestAnyTeam(Packet *packet); + void OnRequestSpecificTeam(Packet *packet); + + RakNetGUID hostGuid; + DefaultAssigmentAlgorithm defaultAssigmentAlgorithm; + bool forceTeamsToBeEven; + bool lockTeams; + // So if we lose the connection while processing, we request the same info of the new host + DataStructures::List myTeamMembers; + + DataStructures::List teamLimits; + DataStructures::List teamMemberCounts; + DataStructures::List teamMembers; + unsigned int GetMemberIndex(NetworkID memberId, RakNetGUID guid) const; + unsigned int AddTeamMember(const TeamMember &tm); // Returns index of new member + void RemoveTeamMember(unsigned int index); + void EvenTeams(void); + unsigned int GetMemberIndexToSwitchTeams(const DataStructures::List &sourceTeamNumbers, TeamId targetTeamNumber); + void GetOverpopulatedTeams(DataStructures::List &overpopulatedTeams, int maxTeamSize); + void SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam); + void NotifyTeamAssigment(unsigned int teamMemberIndex); + bool WeAreHost(void) const; + PluginReceiveResult OnTeamAssigned(Packet *packet); + PluginReceiveResult OnRequestedTeamChangePending(Packet *packet); + PluginReceiveResult OnTeamsLocked(Packet *packet); + void GetMinMaxTeamMembers(int &minMembersOnASingleTeam, int &maxMembersOnASingleTeam); + TeamId GetNextDefaultTeam(void); // Accounting for team balancing and team limits, get the team a player should be placed on + bool TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize); // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it? + bool TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize); + TeamId GetSmallestNonFullTeam(void) const; + TeamId GetFirstNonFullTeam(void) const; + void MoveMemberThatWantsToJoinTeam(TeamId teamId); + TeamId MoveMemberThatWantsToJoinTeamInternal(TeamId teamId); + void NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam); + void NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam, NetworkID memberId); + void NotifyNoTeam(NetworkID memberId, RakNetGUID target); + void SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2); + void RemoveByGuid(RakNetGUID rakNetGUID); + bool TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2); + +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/TelnetTransport.cpp b/project/lib_projects/raknet/jni/RaknetSources/TelnetTransport.cpp index cc20897..cf77463 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TelnetTransport.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/TelnetTransport.cpp @@ -1,364 +1,364 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TelnetTransport==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "TelnetTransport.h" -#include "TCPInterface.h" -#include -#include -#include -#include "LinuxStrings.h" - -// #define _PRINTF_DEBUG - -#define ECHO_INPUT - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(TelnetTransport,TelnetTransport); - -TelnetTransport::TelnetTransport() -{ - tcpInterface=0; - sendSuffix=0; - sendPrefix=0; -} -TelnetTransport::~TelnetTransport() -{ - Stop(); - if (sendSuffix) - rakFree_Ex(sendSuffix, _FILE_AND_LINE_ ); - if (sendPrefix) - rakFree_Ex(sendPrefix, _FILE_AND_LINE_ ); -} -bool TelnetTransport::Start(unsigned short port, bool serverMode) -{ - (void) serverMode; - AutoAllocate(); - RakAssert(serverMode); - return tcpInterface->Start(port, 64); -} -void TelnetTransport::Stop(void) -{ - if (tcpInterface==0) return; - tcpInterface->Stop(); - unsigned i; - for (i=0; i < remoteClients.Size(); i++) - RakNet::OP_DELETE(remoteClients[i], _FILE_AND_LINE_); - remoteClients.Clear(false, _FILE_AND_LINE_); - RakNet::OP_DELETE(tcpInterface, _FILE_AND_LINE_); - tcpInterface=0; -} -void TelnetTransport::Send( SystemAddress systemAddress, const char *data,... ) -{ - if (tcpInterface==0) return; - - if (data==0 || data[0]==0) - return; - - char text[REMOTE_MAX_TEXT_INPUT]; - size_t prefixLength; - if (sendPrefix) - { - strcpy(text, sendPrefix); - prefixLength = strlen(sendPrefix); - } - else - { - text[0]=0; - prefixLength=0; - } - va_list ap; - va_start(ap, data); - _vsnprintf(text+prefixLength, REMOTE_MAX_TEXT_INPUT-prefixLength, data, ap); - va_end(ap); - text[REMOTE_MAX_TEXT_INPUT-1]=0; - - if (sendSuffix) - { - size_t length = strlen(text); - size_t availableChars = REMOTE_MAX_TEXT_INPUT-length-1; - strncat(text, sendSuffix, availableChars); - } - - tcpInterface->Send(text, (unsigned int) strlen(text), systemAddress, false); -} -void TelnetTransport::CloseConnection( SystemAddress systemAddress ) -{ - tcpInterface->CloseConnection(systemAddress); -} -Packet* TelnetTransport::Receive( void ) -{ - if (tcpInterface==0) return 0; - Packet *p = tcpInterface->Receive(); - if (p==0) - return 0; - - /* - if (p->data[0]==255) - { - unsigned i; - for (i=0; i < p->length; i++) - { - RAKNET_DEBUG_PRINTF("%i ", p->data[i]); - } - RAKNET_DEBUG_PRINTF("\n"); - tcpInterface->DeallocatePacket(p); - return 0; - } - */ - - // Get this guy's cursor buffer. This is real bullcrap that I have to do this. - unsigned i; - TelnetClient *remoteClient=0; - for (i=0; i < remoteClients.Size(); i++) - { - if (remoteClients[i]->systemAddress==p->systemAddress) - remoteClient=remoteClients[i]; - } - //RakAssert(remoteClient); - if (remoteClient==0) - { - tcpInterface->DeallocatePacket(p); - return 0; - } - - - if (p->length==3 && p->data[0]==27 && p->data[1]==91 && p->data[2]==65) - { - if (remoteClient->lastSentTextInput[0]) - { - // Up arrow, return last string - for (int i=0; remoteClient->textInput[i]; i++) - remoteClient->textInput[i]=8; - strcat(remoteClient->textInput, remoteClient->lastSentTextInput); - tcpInterface->Send((const char *)remoteClient->textInput, (unsigned int) strlen(remoteClient->textInput), p->systemAddress, false); - strcpy(remoteClient->textInput,remoteClient->lastSentTextInput); - remoteClient->cursorPosition=(unsigned int) strlen(remoteClient->textInput); - } - - return 0; - } - - - // 127 is delete - ignore that - // 9 is tab - // 27 is escape - if (p->data[0]>=127 || p->data[0]==9 || p->data[0]==27) - { - tcpInterface->DeallocatePacket(p); - return 0; - } - - // Hack - I don't know what the hell this is about but cursor keys send 3 characters at a time. I can block these - //Up=27,91,65 - //Down=27,91,66 - //Right=27,91,67 - //Left=27,91,68 - if (p->length==3 && p->data[0]==27 && p->data[1]==91 && p->data[2]>=65 && p->data[2]<=68) - { - tcpInterface->DeallocatePacket(p); - return 0; - } - - - - // Echo - - tcpInterface->Send((const char *)p->data, p->length, p->systemAddress, false); - - - bool gotLine; - // Process each character in turn - for (i=0; i < p->length; i++) - { - - - if (p->data[i]==8) - { - char spaceThenBack[2]; - spaceThenBack[0]=' '; - spaceThenBack[1]=8; - tcpInterface->Send((const char *)spaceThenBack, 2, p->systemAddress, false); - } - - - gotLine=ReassembleLine(remoteClient, p->data[i]); - if (gotLine && remoteClient->textInput[0]) - { - - Packet *reassembledLine = (Packet*) rakMalloc_Ex(sizeof(Packet), _FILE_AND_LINE_); - reassembledLine->length=(unsigned int) strlen(remoteClient->textInput); - memcpy(remoteClient->lastSentTextInput, remoteClient->textInput, reassembledLine->length+1); - RakAssert(reassembledLine->length < REMOTE_MAX_TEXT_INPUT); - reassembledLine->data= (unsigned char*) rakMalloc_Ex( reassembledLine->length+1, _FILE_AND_LINE_ ); - memcpy(reassembledLine->data, remoteClient->textInput, reassembledLine->length); -#ifdef _PRINTF_DEBUG - memset(remoteClient->textInput, 0, REMOTE_MAX_TEXT_INPUT); -#endif - reassembledLine->data[reassembledLine->length]=0; - reassembledLine->systemAddress=p->systemAddress; - tcpInterface->DeallocatePacket(p); - return reassembledLine; - } - } - - tcpInterface->DeallocatePacket(p); - return 0; -} -void TelnetTransport::DeallocatePacket( Packet *packet ) -{ - if (tcpInterface==0) return; - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - rakFree_Ex(packet, _FILE_AND_LINE_ ); -} -SystemAddress TelnetTransport::HasNewIncomingConnection(void) -{ - unsigned i; - SystemAddress newConnection; - newConnection = tcpInterface->HasNewIncomingConnection(); - // 03/16/06 Can't force the stupid windows telnet to use line mode or local echo so now I have to track all the remote players and their - // input buffer - if (newConnection != UNASSIGNED_SYSTEM_ADDRESS) - { - unsigned char command[10]; - // http://www.pcmicro.com/netfoss/RFC857.html - // IAC WON'T ECHO - command[0]=255; // IAC - //command[1]=253; // WON'T - command[1]=251; // WILL - command[2]=1; // ECHO - tcpInterface->Send((const char*)command, 3, newConnection, false); - - /* - // Tell the other side to use line mode - // http://www.faqs.org/rfcs/rfc1184.html - // IAC DO LINEMODE - // command[0]=255; // IAC - // command[1]=252; // DO - // command[2]=34; // LINEMODE - // tcpInterface->Send((const char*)command, 3, newConnection); - - */ - - TelnetClient *remoteClient=0; - for (i=0; i < remoteClients.Size(); i++) - { - if (remoteClients[i]->systemAddress==newConnection) - { - remoteClient=remoteClients[i]; - remoteClient->cursorPosition=0; - } - } - - if (remoteClient==0) - { - remoteClient=new TelnetClient; - remoteClient->lastSentTextInput[0]=0; - remoteClient->cursorPosition=0; - remoteClient->systemAddress=newConnection; -#ifdef _PRINTF_DEBUG - memset(remoteClient->textInput, 0, REMOTE_MAX_TEXT_INPUT); -#endif - } - - remoteClients.Insert(remoteClient, _FILE_AND_LINE_); - } - return newConnection; -} -SystemAddress TelnetTransport::HasLostConnection(void) -{ - SystemAddress systemAddress; - unsigned i; - systemAddress=tcpInterface->HasLostConnection(); - if (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < remoteClients.Size(); i++) - { - if (remoteClients[i]->systemAddress==systemAddress) - { - RakNet::OP_DELETE(remoteClients[i], _FILE_AND_LINE_); - remoteClients[i]=remoteClients[remoteClients.Size()-1]; - remoteClients.RemoveFromEnd(); - } - } - } - return systemAddress; -} -CommandParserInterface* TelnetTransport::GetCommandParser(void) -{ - return 0; -} -void TelnetTransport::SetSendSuffix(const char *suffix) -{ - if (sendSuffix) - { - rakFree_Ex(sendSuffix, _FILE_AND_LINE_ ); - sendSuffix=0; - } - if (suffix) - { - sendSuffix = (char*) rakMalloc_Ex(strlen(suffix)+1, _FILE_AND_LINE_); - strcpy(sendSuffix, suffix); - } -} -void TelnetTransport::SetSendPrefix(const char *prefix) -{ - if (sendPrefix) - { - rakFree_Ex(sendPrefix, _FILE_AND_LINE_ ); - sendPrefix=0; - } - if (prefix) - { - sendPrefix = (char*) rakMalloc_Ex(strlen(prefix)+1, _FILE_AND_LINE_); - strcpy(sendPrefix, prefix); - } -} -void TelnetTransport::AutoAllocate(void) -{ - if (tcpInterface==0) - tcpInterface=new TCPInterface; -} -bool TelnetTransport::ReassembleLine(TelnetTransport::TelnetClient* remoteClient, unsigned char c) -{ - if (c=='\n') - { - remoteClient->textInput[remoteClient->cursorPosition]=0; - remoteClient->cursorPosition=0; -#ifdef _PRINTF_DEBUG - RAKNET_DEBUG_PRINTF("[Done] %s\n", remoteClient->textInput); -#endif - return true; - } - else if (c==8) // backspace - { - if (remoteClient->cursorPosition>0) - { - remoteClient->textInput[--remoteClient->cursorPosition]=0; -#ifdef _PRINTF_DEBUG - RAKNET_DEBUG_PRINTF("[Back] %s\n", remoteClient->textInput); -#endif - } - } - else if (c>=32 && c <127) - { - if (remoteClient->cursorPosition < REMOTE_MAX_TEXT_INPUT) - { - remoteClient->textInput[remoteClient->cursorPosition++]=c; -#ifdef _PRINTF_DEBUG - RAKNET_DEBUG_PRINTF("[Norm] %s\n", remoteClient->textInput); -#endif - } - } - return false; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TelnetTransport==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "TelnetTransport.h" +#include "TCPInterface.h" +#include +#include +#include +#include "LinuxStrings.h" + +// #define _PRINTF_DEBUG + +#define ECHO_INPUT + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(TelnetTransport,TelnetTransport); + +TelnetTransport::TelnetTransport() +{ + tcpInterface=0; + sendSuffix=0; + sendPrefix=0; +} +TelnetTransport::~TelnetTransport() +{ + Stop(); + if (sendSuffix) + rakFree_Ex(sendSuffix, _FILE_AND_LINE_ ); + if (sendPrefix) + rakFree_Ex(sendPrefix, _FILE_AND_LINE_ ); +} +bool TelnetTransport::Start(unsigned short port, bool serverMode) +{ + (void) serverMode; + AutoAllocate(); + RakAssert(serverMode); + return tcpInterface->Start(port, 64); +} +void TelnetTransport::Stop(void) +{ + if (tcpInterface==0) return; + tcpInterface->Stop(); + unsigned i; + for (i=0; i < remoteClients.Size(); i++) + RakNet::OP_DELETE(remoteClients[i], _FILE_AND_LINE_); + remoteClients.Clear(false, _FILE_AND_LINE_); + RakNet::OP_DELETE(tcpInterface, _FILE_AND_LINE_); + tcpInterface=0; +} +void TelnetTransport::Send( SystemAddress systemAddress, const char *data,... ) +{ + if (tcpInterface==0) return; + + if (data==0 || data[0]==0) + return; + + char text[REMOTE_MAX_TEXT_INPUT]; + size_t prefixLength; + if (sendPrefix) + { + strcpy(text, sendPrefix); + prefixLength = strlen(sendPrefix); + } + else + { + text[0]=0; + prefixLength=0; + } + va_list ap; + va_start(ap, data); + _vsnprintf(text+prefixLength, REMOTE_MAX_TEXT_INPUT-prefixLength, data, ap); + va_end(ap); + text[REMOTE_MAX_TEXT_INPUT-1]=0; + + if (sendSuffix) + { + size_t length = strlen(text); + size_t availableChars = REMOTE_MAX_TEXT_INPUT-length-1; + strncat(text, sendSuffix, availableChars); + } + + tcpInterface->Send(text, (unsigned int) strlen(text), systemAddress, false); +} +void TelnetTransport::CloseConnection( SystemAddress systemAddress ) +{ + tcpInterface->CloseConnection(systemAddress); +} +Packet* TelnetTransport::Receive( void ) +{ + if (tcpInterface==0) return 0; + Packet *p = tcpInterface->Receive(); + if (p==0) + return 0; + + /* + if (p->data[0]==255) + { + unsigned i; + for (i=0; i < p->length; i++) + { + RAKNET_DEBUG_PRINTF("%i ", p->data[i]); + } + RAKNET_DEBUG_PRINTF("\n"); + tcpInterface->DeallocatePacket(p); + return 0; + } + */ + + // Get this guy's cursor buffer. This is real bullcrap that I have to do this. + unsigned i; + TelnetClient *remoteClient=0; + for (i=0; i < remoteClients.Size(); i++) + { + if (remoteClients[i]->systemAddress==p->systemAddress) + remoteClient=remoteClients[i]; + } + //RakAssert(remoteClient); + if (remoteClient==0) + { + tcpInterface->DeallocatePacket(p); + return 0; + } + + + if (p->length==3 && p->data[0]==27 && p->data[1]==91 && p->data[2]==65) + { + if (remoteClient->lastSentTextInput[0]) + { + // Up arrow, return last string + for (int i=0; remoteClient->textInput[i]; i++) + remoteClient->textInput[i]=8; + strcat(remoteClient->textInput, remoteClient->lastSentTextInput); + tcpInterface->Send((const char *)remoteClient->textInput, (unsigned int) strlen(remoteClient->textInput), p->systemAddress, false); + strcpy(remoteClient->textInput,remoteClient->lastSentTextInput); + remoteClient->cursorPosition=(unsigned int) strlen(remoteClient->textInput); + } + + return 0; + } + + + // 127 is delete - ignore that + // 9 is tab + // 27 is escape + if (p->data[0]>=127 || p->data[0]==9 || p->data[0]==27) + { + tcpInterface->DeallocatePacket(p); + return 0; + } + + // Hack - I don't know what the hell this is about but cursor keys send 3 characters at a time. I can block these + //Up=27,91,65 + //Down=27,91,66 + //Right=27,91,67 + //Left=27,91,68 + if (p->length==3 && p->data[0]==27 && p->data[1]==91 && p->data[2]>=65 && p->data[2]<=68) + { + tcpInterface->DeallocatePacket(p); + return 0; + } + + + + // Echo + + tcpInterface->Send((const char *)p->data, p->length, p->systemAddress, false); + + + bool gotLine; + // Process each character in turn + for (i=0; i < p->length; i++) + { + + + if (p->data[i]==8) + { + char spaceThenBack[2]; + spaceThenBack[0]=' '; + spaceThenBack[1]=8; + tcpInterface->Send((const char *)spaceThenBack, 2, p->systemAddress, false); + } + + + gotLine=ReassembleLine(remoteClient, p->data[i]); + if (gotLine && remoteClient->textInput[0]) + { + + Packet *reassembledLine = (Packet*) rakMalloc_Ex(sizeof(Packet), _FILE_AND_LINE_); + reassembledLine->length=(unsigned int) strlen(remoteClient->textInput); + memcpy(remoteClient->lastSentTextInput, remoteClient->textInput, reassembledLine->length+1); + RakAssert(reassembledLine->length < REMOTE_MAX_TEXT_INPUT); + reassembledLine->data= (unsigned char*) rakMalloc_Ex( reassembledLine->length+1, _FILE_AND_LINE_ ); + memcpy(reassembledLine->data, remoteClient->textInput, reassembledLine->length); +#ifdef _PRINTF_DEBUG + memset(remoteClient->textInput, 0, REMOTE_MAX_TEXT_INPUT); +#endif + reassembledLine->data[reassembledLine->length]=0; + reassembledLine->systemAddress=p->systemAddress; + tcpInterface->DeallocatePacket(p); + return reassembledLine; + } + } + + tcpInterface->DeallocatePacket(p); + return 0; +} +void TelnetTransport::DeallocatePacket( Packet *packet ) +{ + if (tcpInterface==0) return; + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + rakFree_Ex(packet, _FILE_AND_LINE_ ); +} +SystemAddress TelnetTransport::HasNewIncomingConnection(void) +{ + unsigned i; + SystemAddress newConnection; + newConnection = tcpInterface->HasNewIncomingConnection(); + // 03/16/06 Can't force the stupid windows telnet to use line mode or local echo so now I have to track all the remote players and their + // input buffer + if (newConnection != UNASSIGNED_SYSTEM_ADDRESS) + { + unsigned char command[10]; + // http://www.pcmicro.com/netfoss/RFC857.html + // IAC WON'T ECHO + command[0]=255; // IAC + //command[1]=253; // WON'T + command[1]=251; // WILL + command[2]=1; // ECHO + tcpInterface->Send((const char*)command, 3, newConnection, false); + + /* + // Tell the other side to use line mode + // http://www.faqs.org/rfcs/rfc1184.html + // IAC DO LINEMODE + // command[0]=255; // IAC + // command[1]=252; // DO + // command[2]=34; // LINEMODE + // tcpInterface->Send((const char*)command, 3, newConnection); + + */ + + TelnetClient *remoteClient=0; + for (i=0; i < remoteClients.Size(); i++) + { + if (remoteClients[i]->systemAddress==newConnection) + { + remoteClient=remoteClients[i]; + remoteClient->cursorPosition=0; + } + } + + if (remoteClient==0) + { + remoteClient=new TelnetClient; + remoteClient->lastSentTextInput[0]=0; + remoteClient->cursorPosition=0; + remoteClient->systemAddress=newConnection; +#ifdef _PRINTF_DEBUG + memset(remoteClient->textInput, 0, REMOTE_MAX_TEXT_INPUT); +#endif + } + + remoteClients.Insert(remoteClient, _FILE_AND_LINE_); + } + return newConnection; +} +SystemAddress TelnetTransport::HasLostConnection(void) +{ + SystemAddress systemAddress; + unsigned i; + systemAddress=tcpInterface->HasLostConnection(); + if (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < remoteClients.Size(); i++) + { + if (remoteClients[i]->systemAddress==systemAddress) + { + RakNet::OP_DELETE(remoteClients[i], _FILE_AND_LINE_); + remoteClients[i]=remoteClients[remoteClients.Size()-1]; + remoteClients.RemoveFromEnd(); + } + } + } + return systemAddress; +} +CommandParserInterface* TelnetTransport::GetCommandParser(void) +{ + return 0; +} +void TelnetTransport::SetSendSuffix(const char *suffix) +{ + if (sendSuffix) + { + rakFree_Ex(sendSuffix, _FILE_AND_LINE_ ); + sendSuffix=0; + } + if (suffix) + { + sendSuffix = (char*) rakMalloc_Ex(strlen(suffix)+1, _FILE_AND_LINE_); + strcpy(sendSuffix, suffix); + } +} +void TelnetTransport::SetSendPrefix(const char *prefix) +{ + if (sendPrefix) + { + rakFree_Ex(sendPrefix, _FILE_AND_LINE_ ); + sendPrefix=0; + } + if (prefix) + { + sendPrefix = (char*) rakMalloc_Ex(strlen(prefix)+1, _FILE_AND_LINE_); + strcpy(sendPrefix, prefix); + } +} +void TelnetTransport::AutoAllocate(void) +{ + if (tcpInterface==0) + tcpInterface=new TCPInterface; +} +bool TelnetTransport::ReassembleLine(TelnetTransport::TelnetClient* remoteClient, unsigned char c) +{ + if (c=='\n') + { + remoteClient->textInput[remoteClient->cursorPosition]=0; + remoteClient->cursorPosition=0; +#ifdef _PRINTF_DEBUG + RAKNET_DEBUG_PRINTF("[Done] %s\n", remoteClient->textInput); +#endif + return true; + } + else if (c==8) // backspace + { + if (remoteClient->cursorPosition>0) + { + remoteClient->textInput[--remoteClient->cursorPosition]=0; +#ifdef _PRINTF_DEBUG + RAKNET_DEBUG_PRINTF("[Back] %s\n", remoteClient->textInput); +#endif + } + } + else if (c>=32 && c <127) + { + if (remoteClient->cursorPosition < REMOTE_MAX_TEXT_INPUT) + { + remoteClient->textInput[remoteClient->cursorPosition++]=c; +#ifdef _PRINTF_DEBUG + RAKNET_DEBUG_PRINTF("[Norm] %s\n", remoteClient->textInput); +#endif + } + } + return false; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/TelnetTransport.h b/project/lib_projects/raknet/jni/RaknetSources/TelnetTransport.h index e5e515b..ed2836f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TelnetTransport.h +++ b/project/lib_projects/raknet/jni/RaknetSources/TelnetTransport.h @@ -1,72 +1,72 @@ -/// \file -/// \brief Contains TelnetTransport , used to supports the telnet transport protocol. Insecure -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TelnetTransport==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#ifndef __TELNET_TRANSPORT -#define __TELNET_TRANSPORT - -#include "TransportInterface.h" -#include "DS_List.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class TCPInterface; -struct TelnetClient; - -/// \brief Use TelnetTransport to easily allow windows telnet to connect to your ConsoleServer -/// \details To run Windows telnet, go to your start menu, click run, and in the edit box type "telnet " where is the ip address.
      -/// of your ConsoleServer (most likely the same IP as your game).
      -/// This implementation always echos commands. -class RAK_DLL_EXPORT TelnetTransport : public TransportInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(TelnetTransport) - - TelnetTransport(); - virtual ~TelnetTransport(); - bool Start(unsigned short port, bool serverMode); - void Stop(void); - void Send( SystemAddress systemAddress, const char *data, ... ); - void CloseConnection( SystemAddress systemAddress ); - Packet* Receive( void ); - void DeallocatePacket( Packet *packet ); - SystemAddress HasNewIncomingConnection(void); - SystemAddress HasLostConnection(void); - CommandParserInterface* GetCommandParser(void); - void SetSendSuffix(const char *suffix); - void SetSendPrefix(const char *prefix); -protected: - - struct TelnetClient - { - SystemAddress systemAddress; - char textInput[REMOTE_MAX_TEXT_INPUT]; - char lastSentTextInput[REMOTE_MAX_TEXT_INPUT]; - unsigned cursorPosition; - }; - - TCPInterface *tcpInterface; - void AutoAllocate(void); - bool ReassembleLine(TelnetTransport::TelnetClient* telnetClient, unsigned char c); - - // Crap this sucks but because windows telnet won't send line at a time, I have to reconstruct the lines at the server per player - DataStructures::List remoteClients; - - char *sendSuffix, *sendPrefix; - -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains TelnetTransport , used to supports the telnet transport protocol. Insecure +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TelnetTransport==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#ifndef __TELNET_TRANSPORT +#define __TELNET_TRANSPORT + +#include "TransportInterface.h" +#include "DS_List.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class TCPInterface; +struct TelnetClient; + +/// \brief Use TelnetTransport to easily allow windows telnet to connect to your ConsoleServer +/// \details To run Windows telnet, go to your start menu, click run, and in the edit box type "telnet " where is the ip address.
      +/// of your ConsoleServer (most likely the same IP as your game).
      +/// This implementation always echos commands. +class RAK_DLL_EXPORT TelnetTransport : public TransportInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(TelnetTransport) + + TelnetTransport(); + virtual ~TelnetTransport(); + bool Start(unsigned short port, bool serverMode); + void Stop(void); + void Send( SystemAddress systemAddress, const char *data, ... ); + void CloseConnection( SystemAddress systemAddress ); + Packet* Receive( void ); + void DeallocatePacket( Packet *packet ); + SystemAddress HasNewIncomingConnection(void); + SystemAddress HasLostConnection(void); + CommandParserInterface* GetCommandParser(void); + void SetSendSuffix(const char *suffix); + void SetSendPrefix(const char *prefix); +protected: + + struct TelnetClient + { + SystemAddress systemAddress; + char textInput[REMOTE_MAX_TEXT_INPUT]; + char lastSentTextInput[REMOTE_MAX_TEXT_INPUT]; + unsigned cursorPosition; + }; + + TCPInterface *tcpInterface; + void AutoAllocate(void); + bool ReassembleLine(TelnetTransport::TelnetClient* telnetClient, unsigned char c); + + // Crap this sucks but because windows telnet won't send line at a time, I have to reconstruct the lines at the server per player + DataStructures::List remoteClients; + + char *sendSuffix, *sendPrefix; + +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/ThreadPool.h b/project/lib_projects/raknet/jni/RaknetSources/ThreadPool.h index 087460e..29eb8e8 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ThreadPool.h +++ b/project/lib_projects/raknet/jni/RaknetSources/ThreadPool.h @@ -1,620 +1,620 @@ -#ifndef __THREAD_POOL_H -#define __THREAD_POOL_H - -#include "RakMemoryOverride.h" -#include "DS_Queue.h" -#include "SimpleMutex.h" -#include "Export.h" -#include "RakThread.h" -#include "SignaledEvent.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -class ThreadDataInterface -{ -public: - ThreadDataInterface() {} - virtual ~ThreadDataInterface() {} - - virtual void* PerThreadFactory(void *context)=0; - virtual void PerThreadDestructor(void* factoryResult, void *context)=0; -}; -/// A simple class to create worker threads that processes a queue of functions with data. -/// This class does not allocate or deallocate memory. It is up to the user to handle memory management. -/// InputType and OutputType are stored directly in a queue. For large structures, if you plan to delete from the middle of the queue, -/// you might wish to store pointers rather than the structures themselves so the array can shift efficiently. -template -struct RAK_DLL_EXPORT ThreadPool -{ - ThreadPool(); - ~ThreadPool(); - - /// Start the specified number of threads. - /// \param[in] numThreads The number of threads to start - /// \param[in] stackSize 0 for default (except on consoles). - /// \param[in] _perThreadInit User callback to return data stored per thread. Pass 0 if not needed. - /// \param[in] _perThreadDeinit User callback to destroy data stored per thread, created by _perThreadInit. Pass 0 if not needed. - /// \return True on success, false on failure. - bool StartThreads(int numThreads, int stackSize, void* (*_perThreadInit)()=0, void (*_perThreadDeinit)(void*)=0); - - // Alternate form of _perThreadDataFactory, _perThreadDataDestructor - void SetThreadDataInterface(ThreadDataInterface *tdi, void *context); - - /// Stops all threads - void StopThreads(void); - - /// Adds a function to a queue with data to pass to that function. This function will be called from the thread - /// Memory management is your responsibility! This class does not allocate or deallocate memory. - /// The best way to deallocate \a inputData is in userCallback. If you call EndThreads such that callbacks were not called, you - /// can iterate through the inputQueue and deallocate all pending input data there - /// The best way to deallocate output is as it is returned to you from GetOutput. Similarly, if you end the threads such that - /// not all output was returned, you can iterate through outputQueue and deallocate it there. - /// \param[in] workerThreadCallback The function to call from the thread - /// \param[in] inputData The parameter to pass to \a userCallback - void AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData); - - /// Adds to the output queue - /// Use it if you want to inject output into the same queue that the system uses. Normally you would not use this. Consider it a convenience function. - /// \param[in] outputData The output to inject - void AddOutput(OutputType outputData); - - /// Returns true if output from GetOutput is waiting. - /// \return true if output is waiting, false otherwise - bool HasOutput(void); - - /// Inaccurate but fast version of HasOutput. If this returns true, you should still check HasOutput for the real value. - /// \return true if output is probably waiting, false otherwise - bool HasOutputFast(void); - - /// Returns true if input from GetInput is waiting. - /// \return true if input is waiting, false otherwise - bool HasInput(void); - - /// Inaccurate but fast version of HasInput. If this returns true, you should still check HasInput for the real value. - /// \return true if input is probably waiting, false otherwise - bool HasInputFast(void); - - /// Gets the output of a call to \a userCallback - /// HasOutput must return true before you call this function. Otherwise it will assert. - /// \return The output of \a userCallback. If you have different output signatures, it is up to you to encode the data to indicate this - OutputType GetOutput(void); - - /// Clears internal buffers - void Clear(void); - - /// Lock the input buffer before calling the functions InputSize, InputAtIndex, and RemoveInputAtIndex - /// It is only necessary to lock the input or output while the threads are running - void LockInput(void); - - /// Unlock the input buffer after you are done with the functions InputSize, GetInputAtIndex, and RemoveInputAtIndex - void UnlockInput(void); - - /// Length of the input queue - unsigned InputSize(void); - - /// Get the input at a specified index - InputType GetInputAtIndex(unsigned index); - - /// Remove input from a specific index. This does NOT do memory deallocation - it only removes the item from the queue - void RemoveInputAtIndex(unsigned index); - - /// Lock the output buffer before calling the functions OutputSize, OutputAtIndex, and RemoveOutputAtIndex - /// It is only necessary to lock the input or output while the threads are running - void LockOutput(void); - - /// Unlock the output buffer after you are done with the functions OutputSize, GetOutputAtIndex, and RemoveOutputAtIndex - void UnlockOutput(void); - - /// Length of the output queue - unsigned OutputSize(void); - - /// Get the output at a specified index - OutputType GetOutputAtIndex(unsigned index); - - /// Remove output from a specific index. This does NOT do memory deallocation - it only removes the item from the queue - void RemoveOutputAtIndex(unsigned index); - - /// Removes all items from the input queue - void ClearInput(void); - - /// Removes all items from the output queue - void ClearOutput(void); - - /// Are any of the threads working, or is input or output available? - bool IsWorking(void); - - /// The number of currently active threads. - int NumThreadsWorking(void); - - /// Did we call Start? - bool WasStarted(void); - - // Block until all threads are stopped. - bool Pause(void); - - // Continue running - void Resume(void); - -protected: - // It is valid to cancel input before it is processed. To do so, lock the inputQueue with inputQueueMutex, - // Scan the list, and remove the item you don't want. - RakNet::SimpleMutex inputQueueMutex, outputQueueMutex, workingThreadCountMutex, runThreadsMutex; - - void* (*perThreadDataFactory)(); - void (*perThreadDataDestructor)(void*); - - // inputFunctionQueue & inputQueue are paired arrays so if you delete from one at a particular index you must delete from the other - // at the same index - DataStructures::Queue inputFunctionQueue; - DataStructures::Queue inputQueue; - DataStructures::Queue outputQueue; - - ThreadDataInterface *threadDataInterface; - void *tdiContext; - - - template - friend RAK_THREAD_DECLARATION(WorkerThread); - - /* -#ifdef _WIN32 - friend unsigned __stdcall WorkerThread( LPVOID arguments ); -#else - friend void* WorkerThread( void* arguments ); -#endif - */ - - /// \internal - bool runThreads; - /// \internal - int numThreadsRunning; - /// \internal - int numThreadsWorking; - /// \internal - RakNet::SimpleMutex numThreadsRunningMutex; - - RakNet::SignaledEvent quitAndIncomingDataEvents; - -// #if defined(SN_TARGET_PSP2) -// RakNet::RakThread::UltUlThreadRuntime *runtime; -// #endif -}; - -#include "ThreadPool.h" -#include "RakSleep.h" -#ifdef _WIN32 - -#else -#include -#endif - -#ifdef _MSC_VER -#pragma warning(disable:4127) -#pragma warning( disable : 4701 ) // potentially uninitialized local variable 'inputData' used -#endif - -template -RAK_THREAD_DECLARATION(WorkerThread) -/* -#ifdef _WIN32 -unsigned __stdcall WorkerThread( LPVOID arguments ) -#else -void* WorkerThread( void* arguments ) -#endif -*/ -{ - - - - ThreadPool *threadPool = (ThreadPool*) arguments; - - - bool returnOutput; - ThreadOutputType (*userCallback)(ThreadInputType, bool *, void*); - ThreadInputType inputData; - ThreadOutputType callbackOutput; - - userCallback=0; - - void *perThreadData; - if (threadPool->perThreadDataFactory) - perThreadData=threadPool->perThreadDataFactory(); - else if (threadPool->threadDataInterface) - perThreadData=threadPool->threadDataInterface->PerThreadFactory(threadPool->tdiContext); - else - perThreadData=0; - - // Increase numThreadsRunning - threadPool->numThreadsRunningMutex.Lock(); - ++threadPool->numThreadsRunning; - threadPool->numThreadsRunningMutex.Unlock(); - - while (1) - { -#ifdef _WIN32 - if (userCallback==0) - { - threadPool->quitAndIncomingDataEvents.WaitOnEvent(INFINITE); - } -#endif - - threadPool->runThreadsMutex.Lock(); - if (threadPool->runThreads==false) - { - threadPool->runThreadsMutex.Unlock(); - break; - } - threadPool->runThreadsMutex.Unlock(); - - threadPool->workingThreadCountMutex.Lock(); - ++threadPool->numThreadsWorking; - threadPool->workingThreadCountMutex.Unlock(); - - // Read input data - userCallback=0; - threadPool->inputQueueMutex.Lock(); - if (threadPool->inputFunctionQueue.Size()) - { - userCallback=threadPool->inputFunctionQueue.Pop(); - inputData=threadPool->inputQueue.Pop(); - } - threadPool->inputQueueMutex.Unlock(); - - if (userCallback) - { - callbackOutput=userCallback(inputData, &returnOutput,perThreadData); - if (returnOutput) - { - threadPool->outputQueueMutex.Lock(); - threadPool->outputQueue.Push(callbackOutput, _FILE_AND_LINE_ ); - threadPool->outputQueueMutex.Unlock(); - } - } - - threadPool->workingThreadCountMutex.Lock(); - --threadPool->numThreadsWorking; - threadPool->workingThreadCountMutex.Unlock(); - } - - // Decrease numThreadsRunning - threadPool->numThreadsRunningMutex.Lock(); - --threadPool->numThreadsRunning; - threadPool->numThreadsRunningMutex.Unlock(); - - if (threadPool->perThreadDataDestructor) - threadPool->perThreadDataDestructor(perThreadData); - else if (threadPool->threadDataInterface) - threadPool->threadDataInterface->PerThreadDestructor(perThreadData, threadPool->tdiContext); - - - - - return 0; - -} -template -ThreadPool::ThreadPool() -{ - runThreads=false; - numThreadsRunning=0; - threadDataInterface=0; - tdiContext=0; - numThreadsWorking=0; - -} -template -ThreadPool::~ThreadPool() -{ - StopThreads(); - Clear(); -} -template -bool ThreadPool::StartThreads(int numThreads, int stackSize, void* (*_perThreadDataFactory)(), void (*_perThreadDataDestructor)(void *)) -{ - (void) stackSize; - -// #if defined(SN_TARGET_PSP2) -// runtime = RakNet::RakThread::AllocRuntime(numThreads); -// #endif - - runThreadsMutex.Lock(); - if (runThreads==true) - { - // Already running - runThreadsMutex.Unlock(); - return false; - } - runThreadsMutex.Unlock(); - - quitAndIncomingDataEvents.InitEvent(); - - perThreadDataFactory=_perThreadDataFactory; - perThreadDataDestructor=_perThreadDataDestructor; - - runThreadsMutex.Lock(); - runThreads=true; - runThreadsMutex.Unlock(); - - numThreadsWorking=0; - unsigned threadId = 0; - (void) threadId; - int i; - for (i=0; i < numThreads; i++) - { - int errorCode; - - - - - errorCode = RakNet::RakThread::Create(WorkerThread, this); - - if (errorCode!=0) - { - StopThreads(); - return false; - } - } - // Wait for number of threads running to increase to numThreads - bool done=false; - while (done==false) - { - RakSleep(50); - numThreadsRunningMutex.Lock(); - if (numThreadsRunning==numThreads) - done=true; - numThreadsRunningMutex.Unlock(); - } - - return true; -} -template -void ThreadPool::SetThreadDataInterface(ThreadDataInterface *tdi, void *context) -{ - threadDataInterface=tdi; - tdiContext=context; -} -template -void ThreadPool::StopThreads(void) -{ - runThreadsMutex.Lock(); - if (runThreads==false) - { - runThreadsMutex.Unlock(); - return; - } - - runThreads=false; - runThreadsMutex.Unlock(); - - // Wait for number of threads running to decrease to 0 - bool done=false; - while (done==false) - { - quitAndIncomingDataEvents.SetEvent(); - - RakSleep(50); - numThreadsRunningMutex.Lock(); - if (numThreadsRunning==0) - done=true; - numThreadsRunningMutex.Unlock(); - } - - quitAndIncomingDataEvents.CloseEvent(); - -// #if defined(SN_TARGET_PSP2) -// RakNet::RakThread::DeallocRuntime(runtime); -// runtime=0; -// #endif - -} -template -void ThreadPool::AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData) -{ - inputQueueMutex.Lock(); - inputQueue.Push(inputData, _FILE_AND_LINE_ ); - inputFunctionQueue.Push(workerThreadCallback, _FILE_AND_LINE_ ); - inputQueueMutex.Unlock(); - - quitAndIncomingDataEvents.SetEvent(); -} -template -void ThreadPool::AddOutput(OutputType outputData) -{ - outputQueueMutex.Lock(); - outputQueue.Push(outputData, _FILE_AND_LINE_ ); - outputQueueMutex.Unlock(); -} -template -bool ThreadPool::HasOutputFast(void) -{ - return outputQueue.IsEmpty()==false; -} -template -bool ThreadPool::HasOutput(void) -{ - bool res; - outputQueueMutex.Lock(); - res=outputQueue.IsEmpty()==false; - outputQueueMutex.Unlock(); - return res; -} -template -bool ThreadPool::HasInputFast(void) -{ - return inputQueue.IsEmpty()==false; -} -template -bool ThreadPool::HasInput(void) -{ - bool res; - inputQueueMutex.Lock(); - res=inputQueue.IsEmpty()==false; - inputQueueMutex.Unlock(); - return res; -} -template -OutputType ThreadPool::GetOutput(void) -{ - // Real output check - OutputType output; - outputQueueMutex.Lock(); - output=outputQueue.Pop(); - outputQueueMutex.Unlock(); - return output; -} -template -void ThreadPool::Clear(void) -{ - runThreadsMutex.Lock(); - if (runThreads) - { - runThreadsMutex.Unlock(); - inputQueueMutex.Lock(); - inputFunctionQueue.Clear(_FILE_AND_LINE_); - inputQueue.Clear(_FILE_AND_LINE_); - inputQueueMutex.Unlock(); - - outputQueueMutex.Lock(); - outputQueue.Clear(_FILE_AND_LINE_); - outputQueueMutex.Unlock(); - } - else - { - inputFunctionQueue.Clear(_FILE_AND_LINE_); - inputQueue.Clear(_FILE_AND_LINE_); - outputQueue.Clear(_FILE_AND_LINE_); - } -} -template -void ThreadPool::LockInput(void) -{ - inputQueueMutex.Lock(); -} -template -void ThreadPool::UnlockInput(void) -{ - inputQueueMutex.Unlock(); -} -template -unsigned ThreadPool::InputSize(void) -{ - return inputQueue.Size(); -} -template -InputType ThreadPool::GetInputAtIndex(unsigned index) -{ - return inputQueue[index]; -} -template -void ThreadPool::RemoveInputAtIndex(unsigned index) -{ - inputQueue.RemoveAtIndex(index); - inputFunctionQueue.RemoveAtIndex(index); -} -template -void ThreadPool::LockOutput(void) -{ - outputQueueMutex.Lock(); -} -template -void ThreadPool::UnlockOutput(void) -{ - outputQueueMutex.Unlock(); -} -template -unsigned ThreadPool::OutputSize(void) -{ - return outputQueue.Size(); -} -template -OutputType ThreadPool::GetOutputAtIndex(unsigned index) -{ - return outputQueue[index]; -} -template -void ThreadPool::RemoveOutputAtIndex(unsigned index) -{ - outputQueue.RemoveAtIndex(index); -} -template -void ThreadPool::ClearInput(void) -{ - inputQueue.Clear(_FILE_AND_LINE_); - inputFunctionQueue.Clear(_FILE_AND_LINE_); -} - -template -void ThreadPool::ClearOutput(void) -{ - outputQueue.Clear(_FILE_AND_LINE_); -} -template -bool ThreadPool::IsWorking(void) -{ - bool isWorking; -// workingThreadCountMutex.Lock(); -// isWorking=numThreadsWorking!=0; -// workingThreadCountMutex.Unlock(); - -// if (isWorking) -// return true; - - // Bug fix: Originally the order of these two was reversed. - // It's possible with the thread timing that working could have been false, then it picks up the data in the other thread, then it checks - // here and sees there is no data. So it thinks the thread is not working when it was. - if (HasOutputFast() && HasOutput()) - return true; - - if (HasInputFast() && HasInput()) - return true; - - // Need to check is working again, in case the thread was between the first and second checks - workingThreadCountMutex.Lock(); - isWorking=numThreadsWorking!=0; - workingThreadCountMutex.Unlock(); - - return isWorking; -} - -template -int ThreadPool::NumThreadsWorking(void) -{ - return numThreadsWorking; -} - -template -bool ThreadPool::WasStarted(void) -{ - bool b; - runThreadsMutex.Lock(); - b = runThreads; - runThreadsMutex.Unlock(); - return b; -} -template -bool ThreadPool::Pause(void) -{ - if (WasStarted()==false) - return false; - - workingThreadCountMutex.Lock(); - while (numThreadsWorking>0) - { - RakSleep(30); - } - return true; -} -template -void ThreadPool::Resume(void) -{ - workingThreadCountMutex.Unlock(); -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - +#ifndef __THREAD_POOL_H +#define __THREAD_POOL_H + +#include "RakMemoryOverride.h" +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "Export.h" +#include "RakThread.h" +#include "SignaledEvent.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +class ThreadDataInterface +{ +public: + ThreadDataInterface() {} + virtual ~ThreadDataInterface() {} + + virtual void* PerThreadFactory(void *context)=0; + virtual void PerThreadDestructor(void* factoryResult, void *context)=0; +}; +/// A simple class to create worker threads that processes a queue of functions with data. +/// This class does not allocate or deallocate memory. It is up to the user to handle memory management. +/// InputType and OutputType are stored directly in a queue. For large structures, if you plan to delete from the middle of the queue, +/// you might wish to store pointers rather than the structures themselves so the array can shift efficiently. +template +struct RAK_DLL_EXPORT ThreadPool +{ + ThreadPool(); + ~ThreadPool(); + + /// Start the specified number of threads. + /// \param[in] numThreads The number of threads to start + /// \param[in] stackSize 0 for default (except on consoles). + /// \param[in] _perThreadInit User callback to return data stored per thread. Pass 0 if not needed. + /// \param[in] _perThreadDeinit User callback to destroy data stored per thread, created by _perThreadInit. Pass 0 if not needed. + /// \return True on success, false on failure. + bool StartThreads(int numThreads, int stackSize, void* (*_perThreadInit)()=0, void (*_perThreadDeinit)(void*)=0); + + // Alternate form of _perThreadDataFactory, _perThreadDataDestructor + void SetThreadDataInterface(ThreadDataInterface *tdi, void *context); + + /// Stops all threads + void StopThreads(void); + + /// Adds a function to a queue with data to pass to that function. This function will be called from the thread + /// Memory management is your responsibility! This class does not allocate or deallocate memory. + /// The best way to deallocate \a inputData is in userCallback. If you call EndThreads such that callbacks were not called, you + /// can iterate through the inputQueue and deallocate all pending input data there + /// The best way to deallocate output is as it is returned to you from GetOutput. Similarly, if you end the threads such that + /// not all output was returned, you can iterate through outputQueue and deallocate it there. + /// \param[in] workerThreadCallback The function to call from the thread + /// \param[in] inputData The parameter to pass to \a userCallback + void AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData); + + /// Adds to the output queue + /// Use it if you want to inject output into the same queue that the system uses. Normally you would not use this. Consider it a convenience function. + /// \param[in] outputData The output to inject + void AddOutput(OutputType outputData); + + /// Returns true if output from GetOutput is waiting. + /// \return true if output is waiting, false otherwise + bool HasOutput(void); + + /// Inaccurate but fast version of HasOutput. If this returns true, you should still check HasOutput for the real value. + /// \return true if output is probably waiting, false otherwise + bool HasOutputFast(void); + + /// Returns true if input from GetInput is waiting. + /// \return true if input is waiting, false otherwise + bool HasInput(void); + + /// Inaccurate but fast version of HasInput. If this returns true, you should still check HasInput for the real value. + /// \return true if input is probably waiting, false otherwise + bool HasInputFast(void); + + /// Gets the output of a call to \a userCallback + /// HasOutput must return true before you call this function. Otherwise it will assert. + /// \return The output of \a userCallback. If you have different output signatures, it is up to you to encode the data to indicate this + OutputType GetOutput(void); + + /// Clears internal buffers + void Clear(void); + + /// Lock the input buffer before calling the functions InputSize, InputAtIndex, and RemoveInputAtIndex + /// It is only necessary to lock the input or output while the threads are running + void LockInput(void); + + /// Unlock the input buffer after you are done with the functions InputSize, GetInputAtIndex, and RemoveInputAtIndex + void UnlockInput(void); + + /// Length of the input queue + unsigned InputSize(void); + + /// Get the input at a specified index + InputType GetInputAtIndex(unsigned index); + + /// Remove input from a specific index. This does NOT do memory deallocation - it only removes the item from the queue + void RemoveInputAtIndex(unsigned index); + + /// Lock the output buffer before calling the functions OutputSize, OutputAtIndex, and RemoveOutputAtIndex + /// It is only necessary to lock the input or output while the threads are running + void LockOutput(void); + + /// Unlock the output buffer after you are done with the functions OutputSize, GetOutputAtIndex, and RemoveOutputAtIndex + void UnlockOutput(void); + + /// Length of the output queue + unsigned OutputSize(void); + + /// Get the output at a specified index + OutputType GetOutputAtIndex(unsigned index); + + /// Remove output from a specific index. This does NOT do memory deallocation - it only removes the item from the queue + void RemoveOutputAtIndex(unsigned index); + + /// Removes all items from the input queue + void ClearInput(void); + + /// Removes all items from the output queue + void ClearOutput(void); + + /// Are any of the threads working, or is input or output available? + bool IsWorking(void); + + /// The number of currently active threads. + int NumThreadsWorking(void); + + /// Did we call Start? + bool WasStarted(void); + + // Block until all threads are stopped. + bool Pause(void); + + // Continue running + void Resume(void); + +protected: + // It is valid to cancel input before it is processed. To do so, lock the inputQueue with inputQueueMutex, + // Scan the list, and remove the item you don't want. + RakNet::SimpleMutex inputQueueMutex, outputQueueMutex, workingThreadCountMutex, runThreadsMutex; + + void* (*perThreadDataFactory)(); + void (*perThreadDataDestructor)(void*); + + // inputFunctionQueue & inputQueue are paired arrays so if you delete from one at a particular index you must delete from the other + // at the same index + DataStructures::Queue inputFunctionQueue; + DataStructures::Queue inputQueue; + DataStructures::Queue outputQueue; + + ThreadDataInterface *threadDataInterface; + void *tdiContext; + + + template + friend RAK_THREAD_DECLARATION(WorkerThread); + + /* +#ifdef _WIN32 + friend unsigned __stdcall WorkerThread( LPVOID arguments ); +#else + friend void* WorkerThread( void* arguments ); +#endif + */ + + /// \internal + bool runThreads; + /// \internal + int numThreadsRunning; + /// \internal + int numThreadsWorking; + /// \internal + RakNet::SimpleMutex numThreadsRunningMutex; + + RakNet::SignaledEvent quitAndIncomingDataEvents; + +// #if defined(SN_TARGET_PSP2) +// RakNet::RakThread::UltUlThreadRuntime *runtime; +// #endif +}; + +#include "ThreadPool.h" +#include "RakSleep.h" +#ifdef _WIN32 + +#else +#include +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4127) +#pragma warning( disable : 4701 ) // potentially uninitialized local variable 'inputData' used +#endif + +template +RAK_THREAD_DECLARATION(WorkerThread) +/* +#ifdef _WIN32 +unsigned __stdcall WorkerThread( LPVOID arguments ) +#else +void* WorkerThread( void* arguments ) +#endif +*/ +{ + + + + ThreadPool *threadPool = (ThreadPool*) arguments; + + + bool returnOutput; + ThreadOutputType (*userCallback)(ThreadInputType, bool *, void*); + ThreadInputType inputData; + ThreadOutputType callbackOutput; + + userCallback=0; + + void *perThreadData; + if (threadPool->perThreadDataFactory) + perThreadData=threadPool->perThreadDataFactory(); + else if (threadPool->threadDataInterface) + perThreadData=threadPool->threadDataInterface->PerThreadFactory(threadPool->tdiContext); + else + perThreadData=0; + + // Increase numThreadsRunning + threadPool->numThreadsRunningMutex.Lock(); + ++threadPool->numThreadsRunning; + threadPool->numThreadsRunningMutex.Unlock(); + + while (1) + { +#ifdef _WIN32 + if (userCallback==0) + { + threadPool->quitAndIncomingDataEvents.WaitOnEvent(INFINITE); + } +#endif + + threadPool->runThreadsMutex.Lock(); + if (threadPool->runThreads==false) + { + threadPool->runThreadsMutex.Unlock(); + break; + } + threadPool->runThreadsMutex.Unlock(); + + threadPool->workingThreadCountMutex.Lock(); + ++threadPool->numThreadsWorking; + threadPool->workingThreadCountMutex.Unlock(); + + // Read input data + userCallback=0; + threadPool->inputQueueMutex.Lock(); + if (threadPool->inputFunctionQueue.Size()) + { + userCallback=threadPool->inputFunctionQueue.Pop(); + inputData=threadPool->inputQueue.Pop(); + } + threadPool->inputQueueMutex.Unlock(); + + if (userCallback) + { + callbackOutput=userCallback(inputData, &returnOutput,perThreadData); + if (returnOutput) + { + threadPool->outputQueueMutex.Lock(); + threadPool->outputQueue.Push(callbackOutput, _FILE_AND_LINE_ ); + threadPool->outputQueueMutex.Unlock(); + } + } + + threadPool->workingThreadCountMutex.Lock(); + --threadPool->numThreadsWorking; + threadPool->workingThreadCountMutex.Unlock(); + } + + // Decrease numThreadsRunning + threadPool->numThreadsRunningMutex.Lock(); + --threadPool->numThreadsRunning; + threadPool->numThreadsRunningMutex.Unlock(); + + if (threadPool->perThreadDataDestructor) + threadPool->perThreadDataDestructor(perThreadData); + else if (threadPool->threadDataInterface) + threadPool->threadDataInterface->PerThreadDestructor(perThreadData, threadPool->tdiContext); + + + + + return 0; + +} +template +ThreadPool::ThreadPool() +{ + runThreads=false; + numThreadsRunning=0; + threadDataInterface=0; + tdiContext=0; + numThreadsWorking=0; + +} +template +ThreadPool::~ThreadPool() +{ + StopThreads(); + Clear(); +} +template +bool ThreadPool::StartThreads(int numThreads, int stackSize, void* (*_perThreadDataFactory)(), void (*_perThreadDataDestructor)(void *)) +{ + (void) stackSize; + +// #if defined(SN_TARGET_PSP2) +// runtime = RakNet::RakThread::AllocRuntime(numThreads); +// #endif + + runThreadsMutex.Lock(); + if (runThreads==true) + { + // Already running + runThreadsMutex.Unlock(); + return false; + } + runThreadsMutex.Unlock(); + + quitAndIncomingDataEvents.InitEvent(); + + perThreadDataFactory=_perThreadDataFactory; + perThreadDataDestructor=_perThreadDataDestructor; + + runThreadsMutex.Lock(); + runThreads=true; + runThreadsMutex.Unlock(); + + numThreadsWorking=0; + unsigned threadId = 0; + (void) threadId; + int i; + for (i=0; i < numThreads; i++) + { + int errorCode; + + + + + errorCode = RakNet::RakThread::Create(WorkerThread, this); + + if (errorCode!=0) + { + StopThreads(); + return false; + } + } + // Wait for number of threads running to increase to numThreads + bool done=false; + while (done==false) + { + RakSleep(50); + numThreadsRunningMutex.Lock(); + if (numThreadsRunning==numThreads) + done=true; + numThreadsRunningMutex.Unlock(); + } + + return true; +} +template +void ThreadPool::SetThreadDataInterface(ThreadDataInterface *tdi, void *context) +{ + threadDataInterface=tdi; + tdiContext=context; +} +template +void ThreadPool::StopThreads(void) +{ + runThreadsMutex.Lock(); + if (runThreads==false) + { + runThreadsMutex.Unlock(); + return; + } + + runThreads=false; + runThreadsMutex.Unlock(); + + // Wait for number of threads running to decrease to 0 + bool done=false; + while (done==false) + { + quitAndIncomingDataEvents.SetEvent(); + + RakSleep(50); + numThreadsRunningMutex.Lock(); + if (numThreadsRunning==0) + done=true; + numThreadsRunningMutex.Unlock(); + } + + quitAndIncomingDataEvents.CloseEvent(); + +// #if defined(SN_TARGET_PSP2) +// RakNet::RakThread::DeallocRuntime(runtime); +// runtime=0; +// #endif + +} +template +void ThreadPool::AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData) +{ + inputQueueMutex.Lock(); + inputQueue.Push(inputData, _FILE_AND_LINE_ ); + inputFunctionQueue.Push(workerThreadCallback, _FILE_AND_LINE_ ); + inputQueueMutex.Unlock(); + + quitAndIncomingDataEvents.SetEvent(); +} +template +void ThreadPool::AddOutput(OutputType outputData) +{ + outputQueueMutex.Lock(); + outputQueue.Push(outputData, _FILE_AND_LINE_ ); + outputQueueMutex.Unlock(); +} +template +bool ThreadPool::HasOutputFast(void) +{ + return outputQueue.IsEmpty()==false; +} +template +bool ThreadPool::HasOutput(void) +{ + bool res; + outputQueueMutex.Lock(); + res=outputQueue.IsEmpty()==false; + outputQueueMutex.Unlock(); + return res; +} +template +bool ThreadPool::HasInputFast(void) +{ + return inputQueue.IsEmpty()==false; +} +template +bool ThreadPool::HasInput(void) +{ + bool res; + inputQueueMutex.Lock(); + res=inputQueue.IsEmpty()==false; + inputQueueMutex.Unlock(); + return res; +} +template +OutputType ThreadPool::GetOutput(void) +{ + // Real output check + OutputType output; + outputQueueMutex.Lock(); + output=outputQueue.Pop(); + outputQueueMutex.Unlock(); + return output; +} +template +void ThreadPool::Clear(void) +{ + runThreadsMutex.Lock(); + if (runThreads) + { + runThreadsMutex.Unlock(); + inputQueueMutex.Lock(); + inputFunctionQueue.Clear(_FILE_AND_LINE_); + inputQueue.Clear(_FILE_AND_LINE_); + inputQueueMutex.Unlock(); + + outputQueueMutex.Lock(); + outputQueue.Clear(_FILE_AND_LINE_); + outputQueueMutex.Unlock(); + } + else + { + inputFunctionQueue.Clear(_FILE_AND_LINE_); + inputQueue.Clear(_FILE_AND_LINE_); + outputQueue.Clear(_FILE_AND_LINE_); + } +} +template +void ThreadPool::LockInput(void) +{ + inputQueueMutex.Lock(); +} +template +void ThreadPool::UnlockInput(void) +{ + inputQueueMutex.Unlock(); +} +template +unsigned ThreadPool::InputSize(void) +{ + return inputQueue.Size(); +} +template +InputType ThreadPool::GetInputAtIndex(unsigned index) +{ + return inputQueue[index]; +} +template +void ThreadPool::RemoveInputAtIndex(unsigned index) +{ + inputQueue.RemoveAtIndex(index); + inputFunctionQueue.RemoveAtIndex(index); +} +template +void ThreadPool::LockOutput(void) +{ + outputQueueMutex.Lock(); +} +template +void ThreadPool::UnlockOutput(void) +{ + outputQueueMutex.Unlock(); +} +template +unsigned ThreadPool::OutputSize(void) +{ + return outputQueue.Size(); +} +template +OutputType ThreadPool::GetOutputAtIndex(unsigned index) +{ + return outputQueue[index]; +} +template +void ThreadPool::RemoveOutputAtIndex(unsigned index) +{ + outputQueue.RemoveAtIndex(index); +} +template +void ThreadPool::ClearInput(void) +{ + inputQueue.Clear(_FILE_AND_LINE_); + inputFunctionQueue.Clear(_FILE_AND_LINE_); +} + +template +void ThreadPool::ClearOutput(void) +{ + outputQueue.Clear(_FILE_AND_LINE_); +} +template +bool ThreadPool::IsWorking(void) +{ + bool isWorking; +// workingThreadCountMutex.Lock(); +// isWorking=numThreadsWorking!=0; +// workingThreadCountMutex.Unlock(); + +// if (isWorking) +// return true; + + // Bug fix: Originally the order of these two was reversed. + // It's possible with the thread timing that working could have been false, then it picks up the data in the other thread, then it checks + // here and sees there is no data. So it thinks the thread is not working when it was. + if (HasOutputFast() && HasOutput()) + return true; + + if (HasInputFast() && HasInput()) + return true; + + // Need to check is working again, in case the thread was between the first and second checks + workingThreadCountMutex.Lock(); + isWorking=numThreadsWorking!=0; + workingThreadCountMutex.Unlock(); + + return isWorking; +} + +template +int ThreadPool::NumThreadsWorking(void) +{ + return numThreadsWorking; +} + +template +bool ThreadPool::WasStarted(void) +{ + bool b; + runThreadsMutex.Lock(); + b = runThreads; + runThreadsMutex.Unlock(); + return b; +} +template +bool ThreadPool::Pause(void) +{ + if (WasStarted()==false) + return false; + + workingThreadCountMutex.Lock(); + while (numThreadsWorking>0) + { + RakSleep(30); + } + return true; +} +template +void ThreadPool::Resume(void) +{ + workingThreadCountMutex.Unlock(); +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/ThreadsafePacketLogger.cpp b/project/lib_projects/raknet/jni/RaknetSources/ThreadsafePacketLogger.cpp index a749da0..cb50fef 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ThreadsafePacketLogger.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/ThreadsafePacketLogger.cpp @@ -1,38 +1,38 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#include "ThreadsafePacketLogger.h" -#include - -using namespace RakNet; - -ThreadsafePacketLogger::ThreadsafePacketLogger() -{ - -} -ThreadsafePacketLogger::~ThreadsafePacketLogger() -{ - char **msg; - while ((msg = logMessages.ReadLock()) != 0) - { - rakFree_Ex((*msg), _FILE_AND_LINE_ ); - } -} -void ThreadsafePacketLogger::Update(void) -{ - char **msg; - while ((msg = logMessages.ReadLock()) != 0) - { - WriteLog(*msg); - rakFree_Ex((*msg), _FILE_AND_LINE_ ); - } -} -void ThreadsafePacketLogger::AddToLog(const char *str) -{ - char **msg = logMessages.WriteLock(); - *msg = (char*) rakMalloc_Ex( strlen(str)+1, _FILE_AND_LINE_ ); - strcpy(*msg, str); - logMessages.WriteUnlock(); -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#include "ThreadsafePacketLogger.h" +#include + +using namespace RakNet; + +ThreadsafePacketLogger::ThreadsafePacketLogger() +{ + +} +ThreadsafePacketLogger::~ThreadsafePacketLogger() +{ + char **msg; + while ((msg = logMessages.ReadLock()) != 0) + { + rakFree_Ex((*msg), _FILE_AND_LINE_ ); + } +} +void ThreadsafePacketLogger::Update(void) +{ + char **msg; + while ((msg = logMessages.ReadLock()) != 0) + { + WriteLog(*msg); + rakFree_Ex((*msg), _FILE_AND_LINE_ ); + } +} +void ThreadsafePacketLogger::AddToLog(const char *str) +{ + char **msg = logMessages.WriteLock(); + *msg = (char*) rakMalloc_Ex( strlen(str)+1, _FILE_AND_LINE_ ); + strcpy(*msg, str); + logMessages.WriteUnlock(); +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/ThreadsafePacketLogger.h b/project/lib_projects/raknet/jni/RaknetSources/ThreadsafePacketLogger.h index bca5cf2..542e0cc 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/ThreadsafePacketLogger.h +++ b/project/lib_projects/raknet/jni/RaknetSources/ThreadsafePacketLogger.h @@ -1,40 +1,40 @@ -/// \file -/// \brief Derivation of the packet logger to defer the call to WriteLog until the user thread. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __THREADSAFE_PACKET_LOGGER_H -#define __THREADSAFE_PACKET_LOGGER_H - -#include "PacketLogger.h" -#include "SingleProducerConsumer.h" - -namespace RakNet -{ - -/// \ingroup PACKETLOGGER_GROUP -/// \brief Same as PacketLogger, but writes output in the user thread. -class RAK_DLL_EXPORT ThreadsafePacketLogger : public PacketLogger -{ -public: - ThreadsafePacketLogger(); - virtual ~ThreadsafePacketLogger(); - - virtual void Update(void); - -protected: - virtual void AddToLog(const char *str); - - DataStructures::SingleProducerConsumer logMessages; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Derivation of the packet logger to defer the call to WriteLog until the user thread. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __THREADSAFE_PACKET_LOGGER_H +#define __THREADSAFE_PACKET_LOGGER_H + +#include "PacketLogger.h" +#include "SingleProducerConsumer.h" + +namespace RakNet +{ + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Same as PacketLogger, but writes output in the user thread. +class RAK_DLL_EXPORT ThreadsafePacketLogger : public PacketLogger +{ +public: + ThreadsafePacketLogger(); + virtual ~ThreadsafePacketLogger(); + + virtual void Update(void); + +protected: + virtual void AddToLog(const char *str); + + DataStructures::SingleProducerConsumer logMessages; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/TransportInterface.h b/project/lib_projects/raknet/jni/RaknetSources/TransportInterface.h index f4f8076..fa073ee 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TransportInterface.h +++ b/project/lib_projects/raknet/jni/RaknetSources/TransportInterface.h @@ -1,83 +1,83 @@ -/// \file -/// \brief Contains TransportInterface from which you can derive custom transport providers for ConsoleServer. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __TRANSPORT_INTERFACE_H -#define __TRANSPORT_INTERFACE_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "RakMemoryOverride.h" - -#define REMOTE_MAX_TEXT_INPUT 2048 - -namespace RakNet -{ - -class CommandParserInterface; - - -/// \brief Defines an interface that is used to send and receive null-terminated strings. -/// \details In practice this is only used by the CommandParser system for for servers. -class RAK_DLL_EXPORT TransportInterface -{ -public: - TransportInterface() {} - virtual ~TransportInterface() {} - - /// Start the transport provider on the indicated port. - /// \param[in] port The port to start the transport provider on - /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) - /// \return Return true on success, false on failure. - virtual bool Start(unsigned short port, bool serverMode)=0; - - /// Stop the transport provider. You can clear memory and shutdown threads here. - virtual void Stop(void)=0; - - /// Send a null-terminated string to \a systemAddress - /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here - /// and parse it out in Receive(). - /// \param[in] systemAddress The player to send the string to - /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF - /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF - virtual void Send( SystemAddress systemAddress, const char *data, ... )=0; - - /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. - /// \param[in] systemAddress The player/address to disconnect - virtual void CloseConnection( SystemAddress systemAddress )=0; - - /// Return a string. The string should be allocated and written to Packet::data . - /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress - /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet - /// and thus only return a string in Packet::data - /// \return The packet structure containing the result of Receive, or 0 if no data is available - virtual Packet* Receive( void )=0; - - /// Deallocate the Packet structure returned by Receive - /// \param[in] The packet to deallocate - virtual void DeallocatePacket( Packet *packet )=0; - - /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. - /// \return The SystemAddress/address of the system - virtual SystemAddress HasNewIncomingConnection(void)=0; - - /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. - /// \return The SystemAddress/address of the system - virtual SystemAddress HasLostConnection(void)=0; - - /// Your transport provider can itself have command parsers if the transport layer has user-modifiable features - /// For example, your transport layer may have a password which you want remote users to be able to set or you may want - /// to allow remote users to turn on or off command echo - /// \return 0 if you do not need a command parser - otherwise the desired derivation of CommandParserInterface - virtual CommandParserInterface* GetCommandParser(void)=0; -protected: -}; - -} // namespace RakNet - -#endif - +/// \file +/// \brief Contains TransportInterface from which you can derive custom transport providers for ConsoleServer. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __TRANSPORT_INTERFACE_H +#define __TRANSPORT_INTERFACE_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +#define REMOTE_MAX_TEXT_INPUT 2048 + +namespace RakNet +{ + +class CommandParserInterface; + + +/// \brief Defines an interface that is used to send and receive null-terminated strings. +/// \details In practice this is only used by the CommandParser system for for servers. +class RAK_DLL_EXPORT TransportInterface +{ +public: + TransportInterface() {} + virtual ~TransportInterface() {} + + /// Start the transport provider on the indicated port. + /// \param[in] port The port to start the transport provider on + /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) + /// \return Return true on success, false on failure. + virtual bool Start(unsigned short port, bool serverMode)=0; + + /// Stop the transport provider. You can clear memory and shutdown threads here. + virtual void Stop(void)=0; + + /// Send a null-terminated string to \a systemAddress + /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here + /// and parse it out in Receive(). + /// \param[in] systemAddress The player to send the string to + /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF + /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF + virtual void Send( SystemAddress systemAddress, const char *data, ... )=0; + + /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. + /// \param[in] systemAddress The player/address to disconnect + virtual void CloseConnection( SystemAddress systemAddress )=0; + + /// Return a string. The string should be allocated and written to Packet::data . + /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress + /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet + /// and thus only return a string in Packet::data + /// \return The packet structure containing the result of Receive, or 0 if no data is available + virtual Packet* Receive( void )=0; + + /// Deallocate the Packet structure returned by Receive + /// \param[in] The packet to deallocate + virtual void DeallocatePacket( Packet *packet )=0; + + /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + virtual SystemAddress HasNewIncomingConnection(void)=0; + + /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + virtual SystemAddress HasLostConnection(void)=0; + + /// Your transport provider can itself have command parsers if the transport layer has user-modifiable features + /// For example, your transport layer may have a password which you want remote users to be able to set or you may want + /// to allow remote users to turn on or off command echo + /// \return 0 if you do not need a command parser - otherwise the desired derivation of CommandParserInterface + virtual CommandParserInterface* GetCommandParser(void)=0; +protected: +}; + +} // namespace RakNet + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/TwoWayAuthentication.cpp b/project/lib_projects/raknet/jni/RaknetSources/TwoWayAuthentication.cpp index 17648e7..95435ab 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TwoWayAuthentication.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/TwoWayAuthentication.cpp @@ -1,431 +1,431 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TwoWayAuthentication==1 - -#include "TwoWayAuthentication.h" -#include "Rand.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" -#include "RakPeerInterface.h" - -#if LIBCAT_SECURITY==1 -static const int HASH_BITS = 256; -static const int HASH_BYTES = HASH_BITS / 8; -static const int STRENGTHENING_FACTOR = 256; -#include -#endif - -using namespace RakNet; - -enum NegotiationIdentifiers -{ - ID_NONCE_REQUEST, - ID_NONCE_REPLY, - ID_HASHED_NONCE_AND_PASSWORD, -}; - -TwoWayAuthentication::NonceGenerator::NonceGenerator() {nextRequestId=0;} -TwoWayAuthentication::NonceGenerator::~NonceGenerator() -{ - Clear(); -} -void TwoWayAuthentication::NonceGenerator::GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, RakNet::AddressOrGUID remoteSystem) -{ - TwoWayAuthentication::NonceAndRemoteSystemRequest *narsr = RakNet::OP_NEW(_FILE_AND_LINE_); - narsr->remoteSystem=remoteSystem; - GenerateNonce(narsr->nonce); - narsr->requestId=nextRequestId++; - *requestId=narsr->requestId; - memcpy(nonce,narsr->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - narsr->whenGenerated=RakNet::GetTime(); - generatedNonces.Push(narsr,_FILE_AND_LINE_); -} -void TwoWayAuthentication::NonceGenerator::GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]) -{ - fillBufferMT(nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); -} -bool TwoWayAuthentication::NonceGenerator::GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, RakNet::AddressOrGUID remoteSystem, bool popIfFound) -{ - unsigned int i; - for (i=0; i < generatedNonces.Size(); i++) - { - if (generatedNonces[i]->requestId==requestId) - { - if (remoteSystem==generatedNonces[i]->remoteSystem) - { - memcpy(nonce,generatedNonces[i]->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - if (popIfFound) - { - RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); - generatedNonces.RemoveAtIndex(i); - } - return true; - } - else - { - return false; - } - } - } - return false; -} -void TwoWayAuthentication::NonceGenerator::Clear(void) -{ - unsigned int i; - for (i=0; i < generatedNonces.Size(); i++) - RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); - generatedNonces.Clear(true,_FILE_AND_LINE_); -} -void TwoWayAuthentication::NonceGenerator::ClearByAddress(RakNet::AddressOrGUID remoteSystem) -{ - unsigned int i=0; - while (i < generatedNonces.Size()) - { - if (generatedNonces[i]->remoteSystem==remoteSystem) - { - RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); - generatedNonces.RemoveAtIndex(i); - } - else - { - i++; - } - } -} -void TwoWayAuthentication::NonceGenerator::Update(RakNet::Time curTime) -{ - if (generatedNonces.Size()>0 && GreaterThan(curTime-5000, generatedNonces[0]->whenGenerated)) - { - RakNet::OP_DELETE(generatedNonces[0], _FILE_AND_LINE_); - generatedNonces.RemoveAtIndex(0); - } -} -TwoWayAuthentication::TwoWayAuthentication() -{ - whenLastTimeoutCheck=RakNet::GetTime(); - seedMT(RakNet::GetTimeMS()); -} -TwoWayAuthentication::~TwoWayAuthentication() -{ - Clear(); -} -bool TwoWayAuthentication::AddPassword(RakNet::RakString identifier, RakNet::RakString password) -{ - if (password.IsEmpty()) - return false; - - if (identifier.IsEmpty()) - return false; - - if (password==identifier) - return false; // Insecure - - if (passwords.GetIndexOf(identifier.C_String()).IsInvalid()==false) - return false; // This identifier already in use - - passwords.Push(identifier, password,_FILE_AND_LINE_); - return true; -} -bool TwoWayAuthentication::Challenge(RakNet::RakString identifier, AddressOrGUID remoteSystem) -{ - DataStructures::HashIndex skhi = passwords.GetIndexOf(identifier.C_String()); - if (skhi.IsInvalid()) - return false; - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); - bsOut.Write((MessageID)ID_NONCE_REQUEST); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,remoteSystem,false); - - PendingChallenge pc; - pc.identifier=identifier; - pc.remoteSystem=remoteSystem; - pc.time=RakNet::GetTime(); - pc.sentHash=false; - outgoingChallenges.Push(pc,_FILE_AND_LINE_); - - return true; -} -void TwoWayAuthentication::Update(void) -{ - RakNet::Time curTime = RakNet::GetTime(); - nonceGenerator.Update(curTime); - if (GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, whenLastTimeoutCheck)) - { - while (outgoingChallenges.Size() && GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, outgoingChallenges.Peek().time)) - { - PendingChallenge pc = outgoingChallenges.Pop(); - - // Tell the user about the timeout - PushToUser(ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, pc.identifier, pc.remoteSystem); - } - - whenLastTimeoutCheck=curTime+CHALLENGE_MINIMUM_TIMEOUT; - } -} -PluginReceiveResult TwoWayAuthentication::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_TWO_WAY_AUTHENTICATION_NEGOTIATION: - { - if (packet->length>=sizeof(MessageID)*2) - { - switch (packet->data[sizeof(MessageID)]) - { - case ID_NONCE_REQUEST: - { - OnNonceRequest(packet); - } - break; - case ID_NONCE_REPLY: - { - OnNonceReply(packet); - } - break; - case ID_HASHED_NONCE_AND_PASSWORD: - { - return OnHashedNonceAndPassword(packet); - } - break; - } - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE: - case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS: - { - if (packet->wasGeneratedLocally==false) - { - OnPasswordResult(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - else - break; - } - break; - // These should only be generated locally - case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS: - case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE: - case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT: - if (packet->wasGeneratedLocally==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - break; - } - - return RR_CONTINUE_PROCESSING; -} -void TwoWayAuthentication::OnRakPeerShutdown(void) -{ - Clear(); -} -void TwoWayAuthentication::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - - // Remove from pending challenges - unsigned int i=0; - while (i < outgoingChallenges.Size()) - { - if ((rakNetGUID!=UNASSIGNED_RAKNET_GUID && outgoingChallenges[i].remoteSystem.rakNetGuid==rakNetGUID) || - (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && outgoingChallenges[i].remoteSystem.systemAddress==systemAddress)) - { - outgoingChallenges.RemoveAtIndex(i); - } - else - { - i++; - } - } - - if (rakNetGUID!=UNASSIGNED_RAKNET_GUID) - nonceGenerator.ClearByAddress(rakNetGUID); - else - nonceGenerator.ClearByAddress(systemAddress); -} -void TwoWayAuthentication::Clear(void) -{ - outgoingChallenges.Clear(_FILE_AND_LINE_); - passwords.Clear(_FILE_AND_LINE_); - nonceGenerator.Clear(); -} -void TwoWayAuthentication::PushToUser(MessageID messageId, RakNet::RakString password, RakNet::AddressOrGUID remoteSystem) -{ - RakNet::BitStream output; - output.Write(messageId); - if (password.IsEmpty()==false) - output.Write(password); - Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed()); - p->systemAddress=remoteSystem.systemAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=remoteSystem.rakNetGuid; - p->wasGeneratedLocally=true; - memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed()); - rakPeerInterface->PushBackPacket(p, true); -} -void TwoWayAuthentication::OnNonceRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - unsigned short requestId; - nonceGenerator.GetNonce(nonce,&requestId,packet); - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); - bsOut.Write((MessageID)ID_NONCE_REPLY); - bsOut.Write(requestId); - bsOut.WriteAlignedBytes((const unsigned char*) nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); -} -void TwoWayAuthentication::OnNonceReply(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - unsigned short requestId; - bsIn.Read(requestId); - bsIn.ReadAlignedBytes((unsigned char *) thierNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - - // Lookup one of the negotiations for this guid/system address - AddressOrGUID aog(packet); - unsigned int i; - for (i=0; i < outgoingChallenges.Size(); i++) - { - if (outgoingChallenges[i].remoteSystem==aog && outgoingChallenges[i].sentHash==false) - { - outgoingChallenges[i].sentHash=true; - - // Get the password for this identifier - DataStructures::HashIndex skhi = passwords.GetIndexOf(outgoingChallenges[i].identifier.C_String()); - if (skhi.IsInvalid()==false) - { - RakNet::RakString password = passwords.ItemAtIndex(skhi); - - // Hash their nonce with password and reply - char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; - Hash(thierNonce, password, hashedNonceAndPw); - - // Send - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); - bsOut.Write((MessageID)ID_HASHED_NONCE_AND_PASSWORD); - bsOut.Write(requestId); - bsOut.Write(outgoingChallenges[i].identifier); // Identifier helps the other system lookup the password quickly. - bsOut.WriteAlignedBytes((const unsigned char*) hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); - } - - return; - } - } -} -PluginReceiveResult TwoWayAuthentication::OnHashedNonceAndPassword(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - char remoteHashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; - unsigned short requestId; - bsIn.Read(requestId); - RakNet::RakString passwordIdentifier; - bsIn.Read(passwordIdentifier); - bsIn.ReadAlignedBytes((unsigned char *) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - - // Look up used nonce from requestId - char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - if (nonceGenerator.GetNonceById(usedNonce, requestId, packet, true)==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String()); - if (skhi.IsInvalid()==false) - { - char hashedThisNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; - Hash(usedNonce, passwords.ItemAtIndex(skhi), hashedThisNonceAndPw); - if (memcmp(hashedThisNonceAndPw, remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0) - { - // Pass - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS); - bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - bsOut.Write(passwordIdentifier); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); - - // Incoming success, modify packet header to tell user - PushToUser(ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, passwordIdentifier, packet); - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - - // Incoming failure, modify arrived packet header to tell user - packet->data[0]=(MessageID) ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE; - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE); - bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - bsOut.Write(passwordIdentifier); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); - - return RR_CONTINUE_PROCESSING; -} -void TwoWayAuthentication::OnPasswordResult(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*1); - char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - bsIn.ReadAlignedBytes((unsigned char *)usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; - bsIn.ReadAlignedBytes((unsigned char *)hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - RakNet::RakString passwordIdentifier; - bsIn.Read(passwordIdentifier); - - DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String()); - if (skhi.IsInvalid()==false) - { - RakNet::RakString password = passwords.ItemAtIndex(skhi); - char testHash[HASHED_NONCE_AND_PW_LENGTH]; - Hash(usedNonce, password, testHash); - if (memcmp(testHash,hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0) - { - // Lookup the outgoing challenge and remove it from the list - unsigned int i; - AddressOrGUID aog(packet); - for (i=0; i < outgoingChallenges.Size(); i++) - { - if (outgoingChallenges[i].identifier==passwordIdentifier && - outgoingChallenges[i].remoteSystem==aog && - outgoingChallenges[i].sentHash==true) - { - outgoingChallenges.RemoveAtIndex(i); - - PushToUser(packet->data[0], passwordIdentifier, packet); - return; - } - } - } - } -} -void TwoWayAuthentication::Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], RakNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH]) -{ -#if LIBCAT_SECURITY==1 - cat::Skein hash; - if (!hash.BeginKey(HASH_BITS)) return; - hash.Crunch(thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - hash.Crunch(password.C_String(), (int) password.GetLength()); - hash.End(); - hash.Generate(out, HASH_BYTES, STRENGTHENING_FACTOR); -#else - CSHA1 sha1; - sha1.Update((unsigned char *) thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - sha1.Update((unsigned char *) password.C_String(), (unsigned int) password.GetLength()); - sha1.Final(); - sha1.GetHash((unsigned char *) out); -#endif -} - -#endif +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TwoWayAuthentication==1 + +#include "TwoWayAuthentication.h" +#include "Rand.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" +#include "RakPeerInterface.h" + +#if LIBCAT_SECURITY==1 +static const int HASH_BITS = 256; +static const int HASH_BYTES = HASH_BITS / 8; +static const int STRENGTHENING_FACTOR = 256; +#include +#endif + +using namespace RakNet; + +enum NegotiationIdentifiers +{ + ID_NONCE_REQUEST, + ID_NONCE_REPLY, + ID_HASHED_NONCE_AND_PASSWORD, +}; + +TwoWayAuthentication::NonceGenerator::NonceGenerator() {nextRequestId=0;} +TwoWayAuthentication::NonceGenerator::~NonceGenerator() +{ + Clear(); +} +void TwoWayAuthentication::NonceGenerator::GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, RakNet::AddressOrGUID remoteSystem) +{ + TwoWayAuthentication::NonceAndRemoteSystemRequest *narsr = RakNet::OP_NEW(_FILE_AND_LINE_); + narsr->remoteSystem=remoteSystem; + GenerateNonce(narsr->nonce); + narsr->requestId=nextRequestId++; + *requestId=narsr->requestId; + memcpy(nonce,narsr->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + narsr->whenGenerated=RakNet::GetTime(); + generatedNonces.Push(narsr,_FILE_AND_LINE_); +} +void TwoWayAuthentication::NonceGenerator::GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]) +{ + fillBufferMT(nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); +} +bool TwoWayAuthentication::NonceGenerator::GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, RakNet::AddressOrGUID remoteSystem, bool popIfFound) +{ + unsigned int i; + for (i=0; i < generatedNonces.Size(); i++) + { + if (generatedNonces[i]->requestId==requestId) + { + if (remoteSystem==generatedNonces[i]->remoteSystem) + { + memcpy(nonce,generatedNonces[i]->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + if (popIfFound) + { + RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); + generatedNonces.RemoveAtIndex(i); + } + return true; + } + else + { + return false; + } + } + } + return false; +} +void TwoWayAuthentication::NonceGenerator::Clear(void) +{ + unsigned int i; + for (i=0; i < generatedNonces.Size(); i++) + RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); + generatedNonces.Clear(true,_FILE_AND_LINE_); +} +void TwoWayAuthentication::NonceGenerator::ClearByAddress(RakNet::AddressOrGUID remoteSystem) +{ + unsigned int i=0; + while (i < generatedNonces.Size()) + { + if (generatedNonces[i]->remoteSystem==remoteSystem) + { + RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); + generatedNonces.RemoveAtIndex(i); + } + else + { + i++; + } + } +} +void TwoWayAuthentication::NonceGenerator::Update(RakNet::Time curTime) +{ + if (generatedNonces.Size()>0 && GreaterThan(curTime-5000, generatedNonces[0]->whenGenerated)) + { + RakNet::OP_DELETE(generatedNonces[0], _FILE_AND_LINE_); + generatedNonces.RemoveAtIndex(0); + } +} +TwoWayAuthentication::TwoWayAuthentication() +{ + whenLastTimeoutCheck=RakNet::GetTime(); + seedMT(RakNet::GetTimeMS()); +} +TwoWayAuthentication::~TwoWayAuthentication() +{ + Clear(); +} +bool TwoWayAuthentication::AddPassword(RakNet::RakString identifier, RakNet::RakString password) +{ + if (password.IsEmpty()) + return false; + + if (identifier.IsEmpty()) + return false; + + if (password==identifier) + return false; // Insecure + + if (passwords.GetIndexOf(identifier.C_String()).IsInvalid()==false) + return false; // This identifier already in use + + passwords.Push(identifier, password,_FILE_AND_LINE_); + return true; +} +bool TwoWayAuthentication::Challenge(RakNet::RakString identifier, AddressOrGUID remoteSystem) +{ + DataStructures::HashIndex skhi = passwords.GetIndexOf(identifier.C_String()); + if (skhi.IsInvalid()) + return false; + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); + bsOut.Write((MessageID)ID_NONCE_REQUEST); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,remoteSystem,false); + + PendingChallenge pc; + pc.identifier=identifier; + pc.remoteSystem=remoteSystem; + pc.time=RakNet::GetTime(); + pc.sentHash=false; + outgoingChallenges.Push(pc,_FILE_AND_LINE_); + + return true; +} +void TwoWayAuthentication::Update(void) +{ + RakNet::Time curTime = RakNet::GetTime(); + nonceGenerator.Update(curTime); + if (GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, whenLastTimeoutCheck)) + { + while (outgoingChallenges.Size() && GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, outgoingChallenges.Peek().time)) + { + PendingChallenge pc = outgoingChallenges.Pop(); + + // Tell the user about the timeout + PushToUser(ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, pc.identifier, pc.remoteSystem); + } + + whenLastTimeoutCheck=curTime+CHALLENGE_MINIMUM_TIMEOUT; + } +} +PluginReceiveResult TwoWayAuthentication::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_TWO_WAY_AUTHENTICATION_NEGOTIATION: + { + if (packet->length>=sizeof(MessageID)*2) + { + switch (packet->data[sizeof(MessageID)]) + { + case ID_NONCE_REQUEST: + { + OnNonceRequest(packet); + } + break; + case ID_NONCE_REPLY: + { + OnNonceReply(packet); + } + break; + case ID_HASHED_NONCE_AND_PASSWORD: + { + return OnHashedNonceAndPassword(packet); + } + break; + } + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE: + case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS: + { + if (packet->wasGeneratedLocally==false) + { + OnPasswordResult(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + else + break; + } + break; + // These should only be generated locally + case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS: + case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE: + case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT: + if (packet->wasGeneratedLocally==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + break; + } + + return RR_CONTINUE_PROCESSING; +} +void TwoWayAuthentication::OnRakPeerShutdown(void) +{ + Clear(); +} +void TwoWayAuthentication::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + + // Remove from pending challenges + unsigned int i=0; + while (i < outgoingChallenges.Size()) + { + if ((rakNetGUID!=UNASSIGNED_RAKNET_GUID && outgoingChallenges[i].remoteSystem.rakNetGuid==rakNetGUID) || + (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && outgoingChallenges[i].remoteSystem.systemAddress==systemAddress)) + { + outgoingChallenges.RemoveAtIndex(i); + } + else + { + i++; + } + } + + if (rakNetGUID!=UNASSIGNED_RAKNET_GUID) + nonceGenerator.ClearByAddress(rakNetGUID); + else + nonceGenerator.ClearByAddress(systemAddress); +} +void TwoWayAuthentication::Clear(void) +{ + outgoingChallenges.Clear(_FILE_AND_LINE_); + passwords.Clear(_FILE_AND_LINE_); + nonceGenerator.Clear(); +} +void TwoWayAuthentication::PushToUser(MessageID messageId, RakNet::RakString password, RakNet::AddressOrGUID remoteSystem) +{ + RakNet::BitStream output; + output.Write(messageId); + if (password.IsEmpty()==false) + output.Write(password); + Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed()); + p->systemAddress=remoteSystem.systemAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=remoteSystem.rakNetGuid; + p->wasGeneratedLocally=true; + memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed()); + rakPeerInterface->PushBackPacket(p, true); +} +void TwoWayAuthentication::OnNonceRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + unsigned short requestId; + nonceGenerator.GetNonce(nonce,&requestId,packet); + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); + bsOut.Write((MessageID)ID_NONCE_REPLY); + bsOut.Write(requestId); + bsOut.WriteAlignedBytes((const unsigned char*) nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); +} +void TwoWayAuthentication::OnNonceReply(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + unsigned short requestId; + bsIn.Read(requestId); + bsIn.ReadAlignedBytes((unsigned char *) thierNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + + // Lookup one of the negotiations for this guid/system address + AddressOrGUID aog(packet); + unsigned int i; + for (i=0; i < outgoingChallenges.Size(); i++) + { + if (outgoingChallenges[i].remoteSystem==aog && outgoingChallenges[i].sentHash==false) + { + outgoingChallenges[i].sentHash=true; + + // Get the password for this identifier + DataStructures::HashIndex skhi = passwords.GetIndexOf(outgoingChallenges[i].identifier.C_String()); + if (skhi.IsInvalid()==false) + { + RakNet::RakString password = passwords.ItemAtIndex(skhi); + + // Hash their nonce with password and reply + char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; + Hash(thierNonce, password, hashedNonceAndPw); + + // Send + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); + bsOut.Write((MessageID)ID_HASHED_NONCE_AND_PASSWORD); + bsOut.Write(requestId); + bsOut.Write(outgoingChallenges[i].identifier); // Identifier helps the other system lookup the password quickly. + bsOut.WriteAlignedBytes((const unsigned char*) hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); + } + + return; + } + } +} +PluginReceiveResult TwoWayAuthentication::OnHashedNonceAndPassword(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + char remoteHashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; + unsigned short requestId; + bsIn.Read(requestId); + RakNet::RakString passwordIdentifier; + bsIn.Read(passwordIdentifier); + bsIn.ReadAlignedBytes((unsigned char *) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + + // Look up used nonce from requestId + char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + if (nonceGenerator.GetNonceById(usedNonce, requestId, packet, true)==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String()); + if (skhi.IsInvalid()==false) + { + char hashedThisNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; + Hash(usedNonce, passwords.ItemAtIndex(skhi), hashedThisNonceAndPw); + if (memcmp(hashedThisNonceAndPw, remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0) + { + // Pass + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS); + bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + bsOut.Write(passwordIdentifier); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); + + // Incoming success, modify packet header to tell user + PushToUser(ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, passwordIdentifier, packet); + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + + // Incoming failure, modify arrived packet header to tell user + packet->data[0]=(MessageID) ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE; + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE); + bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + bsOut.Write(passwordIdentifier); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); + + return RR_CONTINUE_PROCESSING; +} +void TwoWayAuthentication::OnPasswordResult(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*1); + char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + bsIn.ReadAlignedBytes((unsigned char *)usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; + bsIn.ReadAlignedBytes((unsigned char *)hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + RakNet::RakString passwordIdentifier; + bsIn.Read(passwordIdentifier); + + DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String()); + if (skhi.IsInvalid()==false) + { + RakNet::RakString password = passwords.ItemAtIndex(skhi); + char testHash[HASHED_NONCE_AND_PW_LENGTH]; + Hash(usedNonce, password, testHash); + if (memcmp(testHash,hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0) + { + // Lookup the outgoing challenge and remove it from the list + unsigned int i; + AddressOrGUID aog(packet); + for (i=0; i < outgoingChallenges.Size(); i++) + { + if (outgoingChallenges[i].identifier==passwordIdentifier && + outgoingChallenges[i].remoteSystem==aog && + outgoingChallenges[i].sentHash==true) + { + outgoingChallenges.RemoveAtIndex(i); + + PushToUser(packet->data[0], passwordIdentifier, packet); + return; + } + } + } + } +} +void TwoWayAuthentication::Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], RakNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH]) +{ +#if LIBCAT_SECURITY==1 + cat::Skein hash; + if (!hash.BeginKey(HASH_BITS)) return; + hash.Crunch(thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + hash.Crunch(password.C_String(), (int) password.GetLength()); + hash.End(); + hash.Generate(out, HASH_BYTES, STRENGTHENING_FACTOR); +#else + CSHA1 sha1; + sha1.Update((unsigned char *) thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + sha1.Update((unsigned char *) password.C_String(), (unsigned int) password.GetLength()); + sha1.Final(); + sha1.GetHash((unsigned char *) out); +#endif +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/TwoWayAuthentication.h b/project/lib_projects/raknet/jni/RaknetSources/TwoWayAuthentication.h index 7993f8e..3a3b850 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/TwoWayAuthentication.h +++ b/project/lib_projects/raknet/jni/RaknetSources/TwoWayAuthentication.h @@ -1,146 +1,146 @@ -/// \file TwoWayAuthentication.h -/// \brief Implements two way authentication -/// \details Given two systems, each of whom known a common password, verify the password without transmitting it -/// This can be used to determine what permissions are should be allowed to the other system -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TwoWayAuthentication==1 - -#ifndef __TWO_WAY_AUTHENTICATION_H -#define __TWO_WAY_AUTHENTICATION_H - -// How often to change the nonce. -#define NONCE_TIMEOUT_MS 10000 -// How often to check for ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, and the minimum timeout time. Maximum is double this value. -#define CHALLENGE_MINIMUM_TIMEOUT 3000 - -#if LIBCAT_SECURITY==1 -// From CPP FILE: -// static const int HASH_BITS = 256; -// static const int HASH_BYTES = HASH_BITS / 8; -// static const int STRENGTHENING_FACTOR = 1000; -#define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 32 -#define HASHED_NONCE_AND_PW_LENGTH 32 -#else -#include "SHA1.h" -#define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 20 -#define HASHED_NONCE_AND_PW_LENGTH SHA1_LENGTH -#endif - -#include "PluginInterface2.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" -#include "RakString.h" -#include "DS_Hash.h" -#include "DS_Queue.h" - -typedef int64_t FCM2Guid; - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \brief Implements two way authentication -/// \details Given two systems, each of whom known a common password / identifier pair, verify the password without transmitting it -/// This can be used to determine what permissions are should be allowed to the other system -/// If the other system should not send any data until authentication passes, you can use the MessageFilter plugin for this. Call MessageFilter::SetAllowMessageID() including ID_TWO_WAY_AUTHENTICATION_NEGOTIATION when doing so. Also attach MessageFilter first in the list of plugins -/// \note If other systems challenges us, and fails, you will get ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILED. -/// \ingroup PLUGINS_GROUP -class RAK_DLL_EXPORT TwoWayAuthentication : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(TwoWayAuthentication) - - TwoWayAuthentication(); - virtual ~TwoWayAuthentication(); - - /// \brief Adds a password to the list of passwords the system will accept - /// \details Each password, which is secret and not transmitted, is identified by \a identifier. - /// \a identifier is transmitted in plaintext with the request. It is only needed because the system supports multiple password. - /// It is used to only hash against once password on the remote system, rather than having to hash against every known password. - /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure - /// \param[in] password The password to add - /// \return True on success, false on identifier==password, either identifier or password is blank, or identifier is already in use - bool AddPassword(RakNet::RakString identifier, RakNet::RakString password); - - /// \brief Challenge another system for the specified identifier - /// \details After calling Challenge, you will get back ID_TWO_WAY_AUTHENTICATION_SUCCESS, ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, or ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILED - /// ID_TWO_WAY_AUTHENTICATION_SUCCESS will be returned if and only if the other system has called AddPassword() with the same identifier\password pair as this system. - /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure - /// \return True on success, false on remote system not connected, or identifier not previously added with AddPassword() - bool Challenge(RakNet::RakString identifier, AddressOrGUID remoteSystem); - - /// \brief Free all memory - void Clear(void); - - /// \internal - virtual void Update(void); - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal - virtual void OnRakPeerShutdown(void); - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - /// \internal - struct PendingChallenge - { - RakNet::RakString identifier; - AddressOrGUID remoteSystem; - RakNet::Time time; - bool sentHash; - }; - - DataStructures::Queue outgoingChallenges; - - /// \internal - struct NonceAndRemoteSystemRequest - { - char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - RakNet::AddressOrGUID remoteSystem; - unsigned short requestId; - RakNet::Time whenGenerated; - }; - /// \internal - struct RAK_DLL_EXPORT NonceGenerator - { - NonceGenerator(); - ~NonceGenerator(); - void GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, RakNet::AddressOrGUID remoteSystem); - void GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]); - bool GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, RakNet::AddressOrGUID remoteSystem, bool popIfFound); - void Clear(void); - void ClearByAddress(RakNet::AddressOrGUID remoteSystem); - void Update(RakNet::Time curTime); - - DataStructures::List generatedNonces; - unsigned short nextRequestId; - }; - -protected: - void PushToUser(MessageID messageId, RakNet::RakString password, RakNet::AddressOrGUID remoteSystem); - // Key is identifier, data is password - DataStructures::Hash passwords; - - RakNet::Time whenLastTimeoutCheck; - - NonceGenerator nonceGenerator; - - void OnNonceRequest(Packet *packet); - void OnNonceReply(Packet *packet); - PluginReceiveResult OnHashedNonceAndPassword(Packet *packet); - void OnPasswordResult(Packet *packet); - void Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], RakNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH]); -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file TwoWayAuthentication.h +/// \brief Implements two way authentication +/// \details Given two systems, each of whom known a common password, verify the password without transmitting it +/// This can be used to determine what permissions are should be allowed to the other system +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TwoWayAuthentication==1 + +#ifndef __TWO_WAY_AUTHENTICATION_H +#define __TWO_WAY_AUTHENTICATION_H + +// How often to change the nonce. +#define NONCE_TIMEOUT_MS 10000 +// How often to check for ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, and the minimum timeout time. Maximum is double this value. +#define CHALLENGE_MINIMUM_TIMEOUT 3000 + +#if LIBCAT_SECURITY==1 +// From CPP FILE: +// static const int HASH_BITS = 256; +// static const int HASH_BYTES = HASH_BITS / 8; +// static const int STRENGTHENING_FACTOR = 1000; +#define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 32 +#define HASHED_NONCE_AND_PW_LENGTH 32 +#else +#include "SHA1.h" +#define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 20 +#define HASHED_NONCE_AND_PW_LENGTH SHA1_LENGTH +#endif + +#include "PluginInterface2.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" +#include "RakString.h" +#include "DS_Hash.h" +#include "DS_Queue.h" + +typedef int64_t FCM2Guid; + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief Implements two way authentication +/// \details Given two systems, each of whom known a common password / identifier pair, verify the password without transmitting it +/// This can be used to determine what permissions are should be allowed to the other system +/// If the other system should not send any data until authentication passes, you can use the MessageFilter plugin for this. Call MessageFilter::SetAllowMessageID() including ID_TWO_WAY_AUTHENTICATION_NEGOTIATION when doing so. Also attach MessageFilter first in the list of plugins +/// \note If other systems challenges us, and fails, you will get ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILED. +/// \ingroup PLUGINS_GROUP +class RAK_DLL_EXPORT TwoWayAuthentication : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(TwoWayAuthentication) + + TwoWayAuthentication(); + virtual ~TwoWayAuthentication(); + + /// \brief Adds a password to the list of passwords the system will accept + /// \details Each password, which is secret and not transmitted, is identified by \a identifier. + /// \a identifier is transmitted in plaintext with the request. It is only needed because the system supports multiple password. + /// It is used to only hash against once password on the remote system, rather than having to hash against every known password. + /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure + /// \param[in] password The password to add + /// \return True on success, false on identifier==password, either identifier or password is blank, or identifier is already in use + bool AddPassword(RakNet::RakString identifier, RakNet::RakString password); + + /// \brief Challenge another system for the specified identifier + /// \details After calling Challenge, you will get back ID_TWO_WAY_AUTHENTICATION_SUCCESS, ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, or ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILED + /// ID_TWO_WAY_AUTHENTICATION_SUCCESS will be returned if and only if the other system has called AddPassword() with the same identifier\password pair as this system. + /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure + /// \return True on success, false on remote system not connected, or identifier not previously added with AddPassword() + bool Challenge(RakNet::RakString identifier, AddressOrGUID remoteSystem); + + /// \brief Free all memory + void Clear(void); + + /// \internal + virtual void Update(void); + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal + virtual void OnRakPeerShutdown(void); + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + /// \internal + struct PendingChallenge + { + RakNet::RakString identifier; + AddressOrGUID remoteSystem; + RakNet::Time time; + bool sentHash; + }; + + DataStructures::Queue outgoingChallenges; + + /// \internal + struct NonceAndRemoteSystemRequest + { + char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + RakNet::AddressOrGUID remoteSystem; + unsigned short requestId; + RakNet::Time whenGenerated; + }; + /// \internal + struct RAK_DLL_EXPORT NonceGenerator + { + NonceGenerator(); + ~NonceGenerator(); + void GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, RakNet::AddressOrGUID remoteSystem); + void GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]); + bool GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, RakNet::AddressOrGUID remoteSystem, bool popIfFound); + void Clear(void); + void ClearByAddress(RakNet::AddressOrGUID remoteSystem); + void Update(RakNet::Time curTime); + + DataStructures::List generatedNonces; + unsigned short nextRequestId; + }; + +protected: + void PushToUser(MessageID messageId, RakNet::RakString password, RakNet::AddressOrGUID remoteSystem); + // Key is identifier, data is password + DataStructures::Hash passwords; + + RakNet::Time whenLastTimeoutCheck; + + NonceGenerator nonceGenerator; + + void OnNonceRequest(Packet *packet); + void OnNonceReply(Packet *packet); + PluginReceiveResult OnHashedNonceAndPassword(Packet *packet); + void OnPasswordResult(Packet *packet); + void Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], RakNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH]); +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/UDPForwarder.cpp b/project/lib_projects/raknet/jni/RaknetSources/UDPForwarder.cpp index c043e5e..e1038c5 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/UDPForwarder.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/UDPForwarder.cpp @@ -1,816 +1,816 @@ -#include "UDPForwarder.h" - -#if _RAKNET_SUPPORT_UDPForwarder==1 - -#include "GetTime.h" -#include "MTUSize.h" -#include "SocketLayer.h" -#include "WSAStartupSingleton.h" -#include "RakSleep.h" -#include "DS_OrderedList.h" -#include "LinuxStrings.h" -#include "SocketDefines.h" -#include "VitaIncludes.h" - -using namespace RakNet; -static const unsigned short DEFAULT_MAX_FORWARD_ENTRIES=64; - -#ifdef UDP_FORWARDER_EXECUTE_THREADED -namespace RakNet -{ - RAK_THREAD_DECLARATION(UpdateUDPForwarder); -} -#endif - -int UDPForwarder::SrcAndDestForwardEntryComp( const UDPForwarder::SrcAndDest &inputKey, UDPForwarder::ForwardEntry * const &cls ) -{ - if (inputKey.source < cls->srcAndDest.source) - return -1; - - if (inputKey.source > cls->srcAndDest.source) - return 1; - - if (inputKey.dest < cls->srcAndDest.dest) - return -1; - - if (inputKey.dest > cls->srcAndDest.dest) - return 1; - - return 0; -} -/* -bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) -{ - return inputKey.Get().source < cls->srcAndDest.source || - (inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest < cls->srcAndDest.dest); -} -bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) -{ - return inputKey.Get().source > cls->srcAndDest.source || - (inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest > cls->srcAndDest.dest); -} -bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) -{ - return inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest == cls->srcAndDest.dest; -} -*/ - - -UDPForwarder::ForwardEntry::ForwardEntry() {socket=INVALID_SOCKET; timeLastDatagramForwarded=RakNet::GetTimeMS(); updatedSourcePort=false; updatedDestPort=false;} -UDPForwarder::ForwardEntry::~ForwardEntry() { - if (socket!=INVALID_SOCKET) - closesocket__(socket); -} - -UDPForwarder::UDPForwarder() -{ -#ifdef _WIN32 - WSAStartupSingleton::AddRef(); -#endif - - maxForwardEntries=DEFAULT_MAX_FORWARD_ENTRIES; - isRunning=false; - threadRunning=false; - - - - -} -UDPForwarder::~UDPForwarder() -{ - Shutdown(); - -#ifdef _WIN32 - WSAStartupSingleton::Deref(); -#endif -} -void UDPForwarder::Startup(void) -{ - if (isRunning==true) - return; - - isRunning=true; - threadRunning=false; - -#ifdef UDP_FORWARDER_EXECUTE_THREADED - - - - - - int errorCode; - - - - - errorCode = RakNet::RakThread::Create(UpdateUDPForwarder, this); - - if ( errorCode != 0 ) - { - RakAssert(0); - return; - } - - while (threadRunning==false) - RakSleep(30); -#endif -} -void UDPForwarder::Shutdown(void) -{ - if (isRunning==false) - return; - - isRunning=false; - -#ifdef UDP_FORWARDER_EXECUTE_THREADED - while (threadRunning==true) - RakSleep(30); -#endif - - unsigned int j; - for (j=0; j < forwardList.Size(); j++) - RakNet::OP_DELETE(forwardList[j],_FILE_AND_LINE_); - forwardList.Clear(false, _FILE_AND_LINE_); - - - - - - -} -void UDPForwarder::Update(void) -{ -#ifndef UDP_FORWARDER_EXECUTE_THREADED - #if RAKNET_SUPPORT_IPV6!=1 - UpdateThreaded_Old(); - #else - UpdateThreaded(); - #endif -#endif -} -void UDPForwarder::UpdateThreaded_Old(void) -{ - fd_set readFD; - //fd_set exceptionFD; - FD_ZERO(&readFD); - // FD_ZERO(&exceptionFD); - int selectResult; - - - timeval tv; - tv.tv_sec=0; - tv.tv_usec=0; - - - RakNet::TimeMS curTime = RakNet::GetTimeMS(); - - SOCKET largestDescriptor=0; - unsigned int i; - - // Remove unused entries - i=0; - while (i < forwardList.Size()) - { - if (curTime > forwardList[i]->timeLastDatagramForwarded && // Account for timestamp wrap - curTime > forwardList[i]->timeLastDatagramForwarded+forwardList[i]->timeoutOnNoDataMS) - { - RakNet::OP_DELETE(forwardList[i],_FILE_AND_LINE_); - forwardList.RemoveAtIndex(i); - } - else - i++; - } - - if (forwardList.Size()==0) - return; - - for (i=0; i < forwardList.Size(); i++) - { -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - FD_SET(forwardList[i]->socket, &readFD); - // FD_SET(forwardList[i]->readSocket, &exceptionFD); - - if (forwardList[i]->socket > largestDescriptor) - largestDescriptor = forwardList[i]->socket; - } - - - selectResult=(int) select__((int) largestDescriptor+1, &readFD, 0, 0, &tv); - - - - - char data[ MAXIMUM_MTU_SIZE ]; - sockaddr_in sa; - socklen_t len2; - - if (selectResult > 0) - { - DataStructures::Queue entriesToRead; - ForwardEntry *forwardEntry; - - for (i=0; i < forwardList.Size(); i++) - { - forwardEntry = forwardList[i]; - // I do this because I'm updating the forwardList, and don't want to lose FD_ISSET as the list is no longer in order - if (FD_ISSET(forwardEntry->socket, &readFD)) - entriesToRead.Push(forwardEntry,_FILE_AND_LINE_); - } - - while (entriesToRead.IsEmpty()==false) - { - forwardEntry=entriesToRead.Pop(); - - const int flag=0; - int receivedDataLen, len=0; - unsigned short portnum=0; - len2 = sizeof( sa ); - sa.sin_family = AF_INET; - receivedDataLen = recvfrom__( forwardEntry->socket, data, MAXIMUM_MTU_SIZE, flag, ( sockaddr* ) & sa, ( socklen_t* ) & len2 ); - - if (receivedDataLen<0) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = WSAGetLastError(); - - if (dwIOError!=WSAECONNRESET && dwIOError!=WSAEINTR && dwIOError!=WSAETIMEDOUT) - { - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "recvfrom failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); - } -#endif - continue; - } - - portnum = ntohs( sa.sin_port ); - if (forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->updatedSourcePort==false && forwardEntry->srcAndDest.dest.GetPort()!=portnum) - { - forwardEntry->updatedSourcePort=true; - - if (forwardEntry->srcAndDest.source.GetPort()!=portnum) - { - unsigned int index; - SrcAndDest srcAndDest(forwardEntry->srcAndDest.dest, forwardEntry->srcAndDest.source); - bool objectExists; - index = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - if (objectExists) - { - forwardList.RemoveAtIndex(index); - } - forwardEntry->srcAndDest.source.SetPort(portnum); - forwardList.Insert(forwardEntry->srcAndDest,forwardEntry,true,_FILE_AND_LINE_); - } - } - - if (forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->srcAndDest.source.GetPort()==portnum) - { - // Forward to dest - len=0; - sockaddr_in saOut; - saOut.sin_port = forwardEntry->srcAndDest.dest.GetPortNetworkOrder(); // User port - saOut.sin_addr.s_addr = forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr; - saOut.sin_family = AF_INET; - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & saOut, sizeof( saOut ) ); - } - while ( len == 0 ); - - // printf("1. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); - - forwardEntry->timeLastDatagramForwarded=curTime; - } - - if (forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->updatedDestPort==false && forwardEntry->srcAndDest.source.GetPort()!=portnum) - { - forwardEntry->updatedDestPort=true; - - if (forwardEntry->srcAndDest.dest.GetPort()!=portnum) - { - unsigned int index; - SrcAndDest srcAndDest(forwardEntry->srcAndDest.source, forwardEntry->srcAndDest.dest); - bool objectExists; - index = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - if (objectExists) - { - forwardList.RemoveAtIndex(index); - } - forwardEntry->srcAndDest.dest.SetPort(portnum); - forwardList.Insert(forwardEntry->srcAndDest,forwardEntry,true,_FILE_AND_LINE_); - } - } - - if (forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->srcAndDest.dest.GetPort()==portnum) - { - // Forward to source - len=0; - sockaddr_in saOut; - saOut.sin_port = forwardEntry->srcAndDest.source.GetPortNetworkOrder(); // User port - saOut.sin_addr.s_addr = forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr; - saOut.sin_family = AF_INET; - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & saOut, sizeof( saOut ) ); - } - while ( len == 0 ); - - // printf("2. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); - - forwardEntry->timeLastDatagramForwarded=curTime; - } - } - } -} -#if RAKNET_SUPPORT_IPV6==1 -void UDPForwarder::UpdateThreaded(void) -{ - fd_set readFD; - //fd_set exceptionFD; - FD_ZERO(&readFD); -// FD_ZERO(&exceptionFD); - timeval tv; - int selectResult; - tv.tv_sec=0; - tv.tv_usec=0; - - RakNet::TimeMS curTime = RakNet::GetTimeMS(); - - SOCKET largestDescriptor=0; - unsigned int i; - - // Remove unused entries - i=0; - while (i < forwardList.Size()) - { - if (curTime > forwardList[i]->timeLastDatagramForwarded && // Account for timestamp wrap - curTime > forwardList[i]->timeLastDatagramForwarded+forwardList[i]->timeoutOnNoDataMS) - { - RakNet::OP_DELETE(forwardList[i],_FILE_AND_LINE_); - forwardList.RemoveAtIndex(i); - } - else - i++; - } - - if (forwardList.Size()==0) - return; - - for (i=0; i < forwardList.Size(); i++) - { -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - FD_SET(forwardList[i]->socket, &readFD); -// FD_SET(forwardList[i]->readSocket, &exceptionFD); - - if (forwardList[i]->socket > largestDescriptor) - largestDescriptor = forwardList[i]->socket; - } - - selectResult=(int) select__((int) largestDescriptor+1, &readFD, 0, 0, &tv); - - char data[ MAXIMUM_MTU_SIZE ]; - sockaddr_storage their_addr; - sockaddr* sockAddrPtr; - socklen_t sockLen; - socklen_t* socketlenPtr=(socklen_t*) &sockLen; - sockaddr_in *sockAddrIn; - sockaddr_in6 *sockAddrIn6; - SystemAddress receivedAddr; - sockLen=sizeof(their_addr); - sockAddrPtr=(sockaddr*) &their_addr; - - if (selectResult > 0) - { - DataStructures::Queue entriesToRead; - ForwardEntry *forwardEntry; - - for (i=0; i < forwardList.Size(); i++) - { - forwardEntry = forwardList[i]; - // I do this because I'm updating the forwardList, and don't want to lose FD_ISSET as the list is no longer in order - if (FD_ISSET(forwardEntry->socket, &readFD)) - entriesToRead.Push(forwardEntry,_FILE_AND_LINE_); - } - - while (entriesToRead.IsEmpty()==false) - { - forwardEntry=entriesToRead.Pop(); - - const int flag=0; - int receivedDataLen, len=0; - receivedDataLen = recvfrom__( forwardEntry->socket, data, MAXIMUM_MTU_SIZE, flag, sockAddrPtr, socketlenPtr ); - - if (receivedDataLen<0) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = WSAGetLastError(); - - if (dwIOError!=WSAECONNRESET && dwIOError!=WSAEINTR && dwIOError!=WSAETIMEDOUT) - { - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "recvfrom failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); - } -#endif - continue; - } - - if (their_addr.ss_family==AF_INET) - { - sockAddrIn=(sockaddr_in *)&their_addr; - sockAddrIn6=0; - memcpy(&receivedAddr.address.addr4,sockAddrIn,sizeof(sockaddr_in)); - // receivedAddr.address.addr4.sin_port=ntohs( sockAddrIn->sin_port ); - } - else - { - sockAddrIn=0; - sockAddrIn6=(sockaddr_in6 *)&their_addr; - memcpy(&receivedAddr.address.addr6,sockAddrIn6,sizeof(sockaddr_in6)); - // receivedAddr.address.addr6.sin6_port=ntohs( sockAddrIn6->sin6_port ); - } - - if (forwardEntry->srcAndDest.source.EqualsExcludingPort(receivedAddr) && forwardEntry->updatedSourcePort==false && forwardEntry->srcAndDest.dest.GetPort()!=receivedAddr.GetPort()) - { - forwardEntry->updatedSourcePort=true; - - if (forwardEntry->srcAndDest.source.GetPort()!=receivedAddr.GetPort()) - { - unsigned int index; - SrcAndDest srcAndDest(forwardEntry->srcAndDest.dest, forwardEntry->srcAndDest.source); - bool objectExists; - index=forwardList.GetIndexFromKey(srcAndDest, &objectExists); - forwardList.RemoveAtIndex(index); - forwardEntry->srcAndDest.source.SetPort(receivedAddr.GetPort()); - forwardList.Push(forwardEntry,forwardEntry->srcAndDest,_FILE_AND_LINE_); - } - } - - if (forwardEntry->srcAndDest.source.EqualsExcludingPort(receivedAddr) && forwardEntry->srcAndDest.source.GetPort()==receivedAddr.GetPort()) - { - // Forward to dest - len=0; - - if (forwardEntry->srcAndDest.dest.address.addr4.sin_family==AF_INET) - { - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.dest.address.addr4, sizeof( sockaddr_in ) ); - } - while ( len == 0 ); - } - else - { - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.dest.address.addr6, sizeof( sockaddr_in ) ); - } - while ( len == 0 ); - } - - - // printf("1. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); - - forwardEntry->timeLastDatagramForwarded=curTime; - } - - if (forwardEntry->srcAndDest.dest.EqualsExcludingPort(receivedAddr) && forwardEntry->updatedDestPort==false && forwardEntry->srcAndDest.source.GetPort()!=receivedAddr.GetPort()) - { - forwardEntry->updatedDestPort=true; - - if (forwardEntry->srcAndDest.dest.GetPort()!=receivedAddr.GetPort()) - { - unsigned int index; - SrcAndDest srcAndDest(forwardEntry->srcAndDest.source, forwardEntry->srcAndDest.dest); - index=forwardList.GetIndexOf(srcAndDest); - forwardList.RemoveAtIndex(index); - forwardEntry->srcAndDest.dest.SetPort(receivedAddr.GetPort()); - forwardList.Push(forwardEntry,forwardEntry->srcAndDest,_FILE_AND_LINE_); - } - } - - if (forwardEntry->srcAndDest.dest.EqualsExcludingPort(receivedAddr) && forwardEntry->srcAndDest.dest.GetPort()==receivedAddr.GetPort()) - { - // Forward to source - len=0; - if (forwardEntry->srcAndDest.source.address.addr4.sin_family==AF_INET) - { - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.source.address.addr4, sizeof( sockaddr_in ) ); - } - while ( len == 0 ); - } - else - { - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.source.address.addr6, sizeof( sockaddr_in ) ); - } - while ( len == 0 ); - } - - // printf("2. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); - - forwardEntry->timeLastDatagramForwarded=curTime; - } - } - } -} -#endif // #if RAKNET_SUPPORT_IPV6!=1 -void UDPForwarder::SetMaxForwardEntries(unsigned short maxEntries) -{ - RakAssert(maxEntries>0 && maxEntries<65535/2); - maxForwardEntries=maxEntries; -} -int UDPForwarder::GetMaxForwardEntries(void) const -{ - return maxForwardEntries; -} -int UDPForwarder::GetUsedForwardEntries(void) const -{ - return (int) forwardList.Size(); -} -UDPForwarderResult UDPForwarder::AddForwardingEntry(SrcAndDest srcAndDest, RakNet::TimeMS timeoutOnNoDataMS, unsigned short *port, const char *forceHostAddress, short socketFamily) -{ - (void) socketFamily; - - unsigned int insertionIndex; - bool objectExists; - insertionIndex = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - if (objectExists==false) - { -#if RAKNET_SUPPORT_IPV6!=1 - int sock_opt; - sockaddr_in listenerSocketAddress; - listenerSocketAddress.sin_port = 0; - ForwardEntry *fe = RakNet::OP_NEW(_FILE_AND_LINE_); - fe->srcAndDest=srcAndDest; - fe->timeoutOnNoDataMS=timeoutOnNoDataMS; - fe->socket = socket__( AF_INET, SOCK_DGRAM, 0 ); - - //printf("Made socket %i\n", fe->readSocket); - - // This doubles the max throughput rate - sock_opt=1024*256; - setsockopt__(fe->socket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - // Immediate hard close. Don't linger the readSocket, or recreating the readSocket quickly on Vista fails. - sock_opt=0; - setsockopt__(fe->socket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - listenerSocketAddress.sin_family = AF_INET; - - if (forceHostAddress && forceHostAddress[0]) - { - - - - listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); - - } - else - { - listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; - } - - int ret = bind__( fe->socket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); - if (ret==-1) - { - RakNet::OP_DELETE(fe,_FILE_AND_LINE_); - return UDPFORWARDER_BIND_FAILED; - } -#else - ForwardEntry *fe = RakNet::OP_NEW(_FILE_AND_LINE_); - fe->srcAndDest=srcAndDest; - fe->timeoutOnNoDataMS=timeoutOnNoDataMS; - fe->socket=INVALID_SOCKET; - - struct addrinfo hints; - memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty - hints.ai_family = socketFamily; - hints.ai_socktype = SOCK_DGRAM; // UDP sockets - hints.ai_flags = AI_PASSIVE; // fill in my IP for me - struct addrinfo *servinfo=0, *aip; // will point to the results - - - RakAssert(forceHostAddress==0 || forceHostAddress[0]!=0); - if (_stricmp(forceHostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0) - { - getaddrinfo(0, "0", &hints, &servinfo); - } - else - { - getaddrinfo(forceHostAddress, "0", &hints, &servinfo); - } - - for (aip = servinfo; aip != NULL; aip = aip->ai_next) - { - // Open socket. The address type depends on what - // getaddrinfo() gave us. - fe->socket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); - if (fe->socket != INVALID_SOCKET) - { - int ret = bind__( fe->socket, aip->ai_addr, (int) aip->ai_addrlen ); - if (ret>=0) - { - break; - } - else - { - closesocket__(fe->socket); - fe->socket=INVALID_SOCKET; - } - } - } - - if (fe->socket==INVALID_SOCKET) - return UDPFORWARDER_BIND_FAILED; - - //printf("Made socket %i\n", fe->readSocket); - - // This doubles the max throughput rate - int sock_opt; - sock_opt=1024*256; - setsockopt__(fe->socket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - // Immediate hard close. Don't linger the readSocket, or recreating the readSocket quickly on Vista fails. - sock_opt=0; - setsockopt__(fe->socket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); -#endif // #if RAKNET_SUPPORT_IPV6!=1 - -// unsigned int oldSize = forwardList.Size(); - forwardList.InsertAtIndex(fe,insertionIndex,_FILE_AND_LINE_); - // RakAssert(forwardList.GetIndexOf(fe->srcAndDest)!=(unsigned int) -1); - *port = SocketLayer::GetLocalPort ( fe->socket ); - return UDPFORWARDER_SUCCESS; - } - - return UDPFORWARDER_FORWARDING_ALREADY_EXISTS; -} -UDPForwarderResult UDPForwarder::StartForwarding(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, - unsigned short *forwardingPort, SOCKET *forwardingSocket) -{ - // Invalid parameters? - if (timeoutOnNoDataMS == 0 || timeoutOnNoDataMS > UDP_FORWARDER_MAXIMUM_TIMEOUT || source==UNASSIGNED_SYSTEM_ADDRESS || destination==UNASSIGNED_SYSTEM_ADDRESS) - return UDPFORWARDER_INVALID_PARAMETERS; - -#ifdef UDP_FORWARDER_EXECUTE_THREADED - ThreadOperation threadOperation; - threadOperation.source=source; - threadOperation.destination=destination; - threadOperation.timeoutOnNoDataMS=timeoutOnNoDataMS; - threadOperation.forceHostAddress=forceHostAddress; - threadOperation.socketFamily=socketFamily; - threadOperation.operation=ThreadOperation::TO_START_FORWARDING; - threadOperationIncomingMutex.Lock(); - threadOperationIncomingQueue.Push(threadOperation, _FILE_AND_LINE_ ); - threadOperationIncomingMutex.Unlock(); - - while (1) - { - RakSleep(0); - threadOperationOutgoingMutex.Lock(); - if (threadOperationOutgoingQueue.Size()!=0) - { - threadOperation=threadOperationOutgoingQueue.Pop(); - threadOperationOutgoingMutex.Unlock(); - if (forwardingPort) - *forwardingPort=threadOperation.forwardingPort; - if (forwardingSocket) - *forwardingSocket=threadOperation.forwardingSocket; - return threadOperation.result; - } - threadOperationOutgoingMutex.Unlock(); - - } -#else - return StartForwardingThreaded(source, destination, timeoutOnNoDataMS, forceHostAddress, socketFamily, srcToDestPort, destToSourcePort, srcToDestSocket, destToSourceSocket); -#endif - -} -UDPForwarderResult UDPForwarder::StartForwardingThreaded(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, - unsigned short *forwardingPort, SOCKET *forwardingSocket) -{ - SrcAndDest srcAndDest(source, destination); - - UDPForwarderResult result = AddForwardingEntry(srcAndDest, timeoutOnNoDataMS, forwardingPort, forceHostAddress, socketFamily); - - if (result!=UDPFORWARDER_SUCCESS) - return result; - - if (*forwardingSocket) - { - unsigned int idx; - bool objectExists; - idx = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - RakAssert(objectExists); - *forwardingSocket=forwardList[idx]->socket; - } - - return UDPFORWARDER_SUCCESS; -} -void UDPForwarder::StopForwarding(SystemAddress source, SystemAddress destination) -{ -#ifdef UDP_FORWARDER_EXECUTE_THREADED - ThreadOperation threadOperation; - threadOperation.source=source; - threadOperation.destination=destination; - threadOperation.operation=ThreadOperation::TO_STOP_FORWARDING; - threadOperationIncomingMutex.Lock(); - threadOperationIncomingQueue.Push(threadOperation, _FILE_AND_LINE_ ); - threadOperationIncomingMutex.Unlock(); -#else - StopForwardingThreaded(source, destination); -#endif -} -void UDPForwarder::StopForwardingThreaded(SystemAddress source, SystemAddress destination) -{ - SrcAndDest srcAndDest(destination,source); - - bool objectExists; - unsigned int idx = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - if (objectExists) - { - RakNet::OP_DELETE(forwardList[idx],_FILE_AND_LINE_); - forwardList.RemoveAtIndex(idx); - } -} -namespace RakNet { -#ifdef UDP_FORWARDER_EXECUTE_THREADED -RAK_THREAD_DECLARATION(UpdateUDPForwarder) -{ - - - - UDPForwarder * udpForwarder = ( UDPForwarder * ) arguments; - - - udpForwarder->threadRunning=true; - UDPForwarder::ThreadOperation threadOperation; - while (udpForwarder->isRunning) - { - udpForwarder->threadOperationIncomingMutex.Lock(); - while (udpForwarder->threadOperationIncomingQueue.Size()) - { - threadOperation=udpForwarder->threadOperationIncomingQueue.Pop(); - udpForwarder->threadOperationIncomingMutex.Unlock(); - if (threadOperation.operation==UDPForwarder::ThreadOperation::TO_START_FORWARDING) - { - threadOperation.result=udpForwarder->StartForwardingThreaded(threadOperation.source, threadOperation.destination, threadOperation.timeoutOnNoDataMS, - threadOperation.forceHostAddress, threadOperation.socketFamily, &threadOperation.forwardingPort, &threadOperation.forwardingSocket); - udpForwarder->threadOperationOutgoingMutex.Lock(); - udpForwarder->threadOperationOutgoingQueue.Push(threadOperation, _FILE_AND_LINE_ ); - udpForwarder->threadOperationOutgoingMutex.Unlock(); - } - else - { - udpForwarder->StopForwardingThreaded(threadOperation.source, threadOperation.destination); - } - - - udpForwarder->threadOperationIncomingMutex.Lock(); - } - udpForwarder->threadOperationIncomingMutex.Unlock(); - -#if RAKNET_SUPPORT_IPV6!=1 - udpForwarder->UpdateThreaded_Old(); -#else - udpForwarder->UpdateThreaded(); -#endif - - - // 12/1/2010 Do not change from 0 - // See http://www.jenkinssoftware.com/forum/index.php?topic=4033.0;topicseen - // Avoid 100% reported CPU usage - RakSleep(0); - } - udpForwarder->threadRunning=false; - - - - - return 0; - - -} -} // namespace RakNet -#endif - -#endif // #if _RAKNET_SUPPORT_FileOperations==1 +#include "UDPForwarder.h" + +#if _RAKNET_SUPPORT_UDPForwarder==1 + +#include "GetTime.h" +#include "MTUSize.h" +#include "SocketLayer.h" +#include "WSAStartupSingleton.h" +#include "RakSleep.h" +#include "DS_OrderedList.h" +#include "LinuxStrings.h" +#include "SocketDefines.h" +#include "VitaIncludes.h" + +using namespace RakNet; +static const unsigned short DEFAULT_MAX_FORWARD_ENTRIES=64; + +#ifdef UDP_FORWARDER_EXECUTE_THREADED +namespace RakNet +{ + RAK_THREAD_DECLARATION(UpdateUDPForwarder); +} +#endif + +int UDPForwarder::SrcAndDestForwardEntryComp( const UDPForwarder::SrcAndDest &inputKey, UDPForwarder::ForwardEntry * const &cls ) +{ + if (inputKey.source < cls->srcAndDest.source) + return -1; + + if (inputKey.source > cls->srcAndDest.source) + return 1; + + if (inputKey.dest < cls->srcAndDest.dest) + return -1; + + if (inputKey.dest > cls->srcAndDest.dest) + return 1; + + return 0; +} +/* +bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) +{ + return inputKey.Get().source < cls->srcAndDest.source || + (inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest < cls->srcAndDest.dest); +} +bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) +{ + return inputKey.Get().source > cls->srcAndDest.source || + (inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest > cls->srcAndDest.dest); +} +bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) +{ + return inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest == cls->srcAndDest.dest; +} +*/ + + +UDPForwarder::ForwardEntry::ForwardEntry() {socket=INVALID_SOCKET; timeLastDatagramForwarded=RakNet::GetTimeMS(); updatedSourcePort=false; updatedDestPort=false;} +UDPForwarder::ForwardEntry::~ForwardEntry() { + if (socket!=INVALID_SOCKET) + closesocket__(socket); +} + +UDPForwarder::UDPForwarder() +{ +#ifdef _WIN32 + WSAStartupSingleton::AddRef(); +#endif + + maxForwardEntries=DEFAULT_MAX_FORWARD_ENTRIES; + isRunning=false; + threadRunning=false; + + + + +} +UDPForwarder::~UDPForwarder() +{ + Shutdown(); + +#ifdef _WIN32 + WSAStartupSingleton::Deref(); +#endif +} +void UDPForwarder::Startup(void) +{ + if (isRunning==true) + return; + + isRunning=true; + threadRunning=false; + +#ifdef UDP_FORWARDER_EXECUTE_THREADED + + + + + + int errorCode; + + + + + errorCode = RakNet::RakThread::Create(UpdateUDPForwarder, this); + + if ( errorCode != 0 ) + { + RakAssert(0); + return; + } + + while (threadRunning==false) + RakSleep(30); +#endif +} +void UDPForwarder::Shutdown(void) +{ + if (isRunning==false) + return; + + isRunning=false; + +#ifdef UDP_FORWARDER_EXECUTE_THREADED + while (threadRunning==true) + RakSleep(30); +#endif + + unsigned int j; + for (j=0; j < forwardList.Size(); j++) + RakNet::OP_DELETE(forwardList[j],_FILE_AND_LINE_); + forwardList.Clear(false, _FILE_AND_LINE_); + + + + + + +} +void UDPForwarder::Update(void) +{ +#ifndef UDP_FORWARDER_EXECUTE_THREADED + #if RAKNET_SUPPORT_IPV6!=1 + UpdateThreaded_Old(); + #else + UpdateThreaded(); + #endif +#endif +} +void UDPForwarder::UpdateThreaded_Old(void) +{ + fd_set readFD; + //fd_set exceptionFD; + FD_ZERO(&readFD); + // FD_ZERO(&exceptionFD); + int selectResult; + + + timeval tv; + tv.tv_sec=0; + tv.tv_usec=0; + + + RakNet::TimeMS curTime = RakNet::GetTimeMS(); + + SOCKET largestDescriptor=0; + unsigned int i; + + // Remove unused entries + i=0; + while (i < forwardList.Size()) + { + if (curTime > forwardList[i]->timeLastDatagramForwarded && // Account for timestamp wrap + curTime > forwardList[i]->timeLastDatagramForwarded+forwardList[i]->timeoutOnNoDataMS) + { + RakNet::OP_DELETE(forwardList[i],_FILE_AND_LINE_); + forwardList.RemoveAtIndex(i); + } + else + i++; + } + + if (forwardList.Size()==0) + return; + + for (i=0; i < forwardList.Size(); i++) + { +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + FD_SET(forwardList[i]->socket, &readFD); + // FD_SET(forwardList[i]->readSocket, &exceptionFD); + + if (forwardList[i]->socket > largestDescriptor) + largestDescriptor = forwardList[i]->socket; + } + + + selectResult=(int) select__((int) largestDescriptor+1, &readFD, 0, 0, &tv); + + + + + char data[ MAXIMUM_MTU_SIZE ]; + sockaddr_in sa; + socklen_t len2; + + if (selectResult > 0) + { + DataStructures::Queue entriesToRead; + ForwardEntry *forwardEntry; + + for (i=0; i < forwardList.Size(); i++) + { + forwardEntry = forwardList[i]; + // I do this because I'm updating the forwardList, and don't want to lose FD_ISSET as the list is no longer in order + if (FD_ISSET(forwardEntry->socket, &readFD)) + entriesToRead.Push(forwardEntry,_FILE_AND_LINE_); + } + + while (entriesToRead.IsEmpty()==false) + { + forwardEntry=entriesToRead.Pop(); + + const int flag=0; + int receivedDataLen, len=0; + unsigned short portnum=0; + len2 = sizeof( sa ); + sa.sin_family = AF_INET; + receivedDataLen = recvfrom__( forwardEntry->socket, data, MAXIMUM_MTU_SIZE, flag, ( sockaddr* ) & sa, ( socklen_t* ) & len2 ); + + if (receivedDataLen<0) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = WSAGetLastError(); + + if (dwIOError!=WSAECONNRESET && dwIOError!=WSAEINTR && dwIOError!=WSAETIMEDOUT) + { + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "recvfrom failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); + } +#endif + continue; + } + + portnum = ntohs( sa.sin_port ); + if (forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->updatedSourcePort==false && forwardEntry->srcAndDest.dest.GetPort()!=portnum) + { + forwardEntry->updatedSourcePort=true; + + if (forwardEntry->srcAndDest.source.GetPort()!=portnum) + { + unsigned int index; + SrcAndDest srcAndDest(forwardEntry->srcAndDest.dest, forwardEntry->srcAndDest.source); + bool objectExists; + index = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + if (objectExists) + { + forwardList.RemoveAtIndex(index); + } + forwardEntry->srcAndDest.source.SetPort(portnum); + forwardList.Insert(forwardEntry->srcAndDest,forwardEntry,true,_FILE_AND_LINE_); + } + } + + if (forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->srcAndDest.source.GetPort()==portnum) + { + // Forward to dest + len=0; + sockaddr_in saOut; + saOut.sin_port = forwardEntry->srcAndDest.dest.GetPortNetworkOrder(); // User port + saOut.sin_addr.s_addr = forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr; + saOut.sin_family = AF_INET; + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & saOut, sizeof( saOut ) ); + } + while ( len == 0 ); + + // printf("1. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); + + forwardEntry->timeLastDatagramForwarded=curTime; + } + + if (forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->updatedDestPort==false && forwardEntry->srcAndDest.source.GetPort()!=portnum) + { + forwardEntry->updatedDestPort=true; + + if (forwardEntry->srcAndDest.dest.GetPort()!=portnum) + { + unsigned int index; + SrcAndDest srcAndDest(forwardEntry->srcAndDest.source, forwardEntry->srcAndDest.dest); + bool objectExists; + index = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + if (objectExists) + { + forwardList.RemoveAtIndex(index); + } + forwardEntry->srcAndDest.dest.SetPort(portnum); + forwardList.Insert(forwardEntry->srcAndDest,forwardEntry,true,_FILE_AND_LINE_); + } + } + + if (forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->srcAndDest.dest.GetPort()==portnum) + { + // Forward to source + len=0; + sockaddr_in saOut; + saOut.sin_port = forwardEntry->srcAndDest.source.GetPortNetworkOrder(); // User port + saOut.sin_addr.s_addr = forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr; + saOut.sin_family = AF_INET; + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & saOut, sizeof( saOut ) ); + } + while ( len == 0 ); + + // printf("2. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); + + forwardEntry->timeLastDatagramForwarded=curTime; + } + } + } +} +#if RAKNET_SUPPORT_IPV6==1 +void UDPForwarder::UpdateThreaded(void) +{ + fd_set readFD; + //fd_set exceptionFD; + FD_ZERO(&readFD); +// FD_ZERO(&exceptionFD); + timeval tv; + int selectResult; + tv.tv_sec=0; + tv.tv_usec=0; + + RakNet::TimeMS curTime = RakNet::GetTimeMS(); + + SOCKET largestDescriptor=0; + unsigned int i; + + // Remove unused entries + i=0; + while (i < forwardList.Size()) + { + if (curTime > forwardList[i]->timeLastDatagramForwarded && // Account for timestamp wrap + curTime > forwardList[i]->timeLastDatagramForwarded+forwardList[i]->timeoutOnNoDataMS) + { + RakNet::OP_DELETE(forwardList[i],_FILE_AND_LINE_); + forwardList.RemoveAtIndex(i); + } + else + i++; + } + + if (forwardList.Size()==0) + return; + + for (i=0; i < forwardList.Size(); i++) + { +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + FD_SET(forwardList[i]->socket, &readFD); +// FD_SET(forwardList[i]->readSocket, &exceptionFD); + + if (forwardList[i]->socket > largestDescriptor) + largestDescriptor = forwardList[i]->socket; + } + + selectResult=(int) select__((int) largestDescriptor+1, &readFD, 0, 0, &tv); + + char data[ MAXIMUM_MTU_SIZE ]; + sockaddr_storage their_addr; + sockaddr* sockAddrPtr; + socklen_t sockLen; + socklen_t* socketlenPtr=(socklen_t*) &sockLen; + sockaddr_in *sockAddrIn; + sockaddr_in6 *sockAddrIn6; + SystemAddress receivedAddr; + sockLen=sizeof(their_addr); + sockAddrPtr=(sockaddr*) &their_addr; + + if (selectResult > 0) + { + DataStructures::Queue entriesToRead; + ForwardEntry *forwardEntry; + + for (i=0; i < forwardList.Size(); i++) + { + forwardEntry = forwardList[i]; + // I do this because I'm updating the forwardList, and don't want to lose FD_ISSET as the list is no longer in order + if (FD_ISSET(forwardEntry->socket, &readFD)) + entriesToRead.Push(forwardEntry,_FILE_AND_LINE_); + } + + while (entriesToRead.IsEmpty()==false) + { + forwardEntry=entriesToRead.Pop(); + + const int flag=0; + int receivedDataLen, len=0; + receivedDataLen = recvfrom__( forwardEntry->socket, data, MAXIMUM_MTU_SIZE, flag, sockAddrPtr, socketlenPtr ); + + if (receivedDataLen<0) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = WSAGetLastError(); + + if (dwIOError!=WSAECONNRESET && dwIOError!=WSAEINTR && dwIOError!=WSAETIMEDOUT) + { + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "recvfrom failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); + } +#endif + continue; + } + + if (their_addr.ss_family==AF_INET) + { + sockAddrIn=(sockaddr_in *)&their_addr; + sockAddrIn6=0; + memcpy(&receivedAddr.address.addr4,sockAddrIn,sizeof(sockaddr_in)); + // receivedAddr.address.addr4.sin_port=ntohs( sockAddrIn->sin_port ); + } + else + { + sockAddrIn=0; + sockAddrIn6=(sockaddr_in6 *)&their_addr; + memcpy(&receivedAddr.address.addr6,sockAddrIn6,sizeof(sockaddr_in6)); + // receivedAddr.address.addr6.sin6_port=ntohs( sockAddrIn6->sin6_port ); + } + + if (forwardEntry->srcAndDest.source.EqualsExcludingPort(receivedAddr) && forwardEntry->updatedSourcePort==false && forwardEntry->srcAndDest.dest.GetPort()!=receivedAddr.GetPort()) + { + forwardEntry->updatedSourcePort=true; + + if (forwardEntry->srcAndDest.source.GetPort()!=receivedAddr.GetPort()) + { + unsigned int index; + SrcAndDest srcAndDest(forwardEntry->srcAndDest.dest, forwardEntry->srcAndDest.source); + bool objectExists; + index=forwardList.GetIndexFromKey(srcAndDest, &objectExists); + forwardList.RemoveAtIndex(index); + forwardEntry->srcAndDest.source.SetPort(receivedAddr.GetPort()); + forwardList.Push(forwardEntry,forwardEntry->srcAndDest,_FILE_AND_LINE_); + } + } + + if (forwardEntry->srcAndDest.source.EqualsExcludingPort(receivedAddr) && forwardEntry->srcAndDest.source.GetPort()==receivedAddr.GetPort()) + { + // Forward to dest + len=0; + + if (forwardEntry->srcAndDest.dest.address.addr4.sin_family==AF_INET) + { + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.dest.address.addr4, sizeof( sockaddr_in ) ); + } + while ( len == 0 ); + } + else + { + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.dest.address.addr6, sizeof( sockaddr_in ) ); + } + while ( len == 0 ); + } + + + // printf("1. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); + + forwardEntry->timeLastDatagramForwarded=curTime; + } + + if (forwardEntry->srcAndDest.dest.EqualsExcludingPort(receivedAddr) && forwardEntry->updatedDestPort==false && forwardEntry->srcAndDest.source.GetPort()!=receivedAddr.GetPort()) + { + forwardEntry->updatedDestPort=true; + + if (forwardEntry->srcAndDest.dest.GetPort()!=receivedAddr.GetPort()) + { + unsigned int index; + SrcAndDest srcAndDest(forwardEntry->srcAndDest.source, forwardEntry->srcAndDest.dest); + index=forwardList.GetIndexOf(srcAndDest); + forwardList.RemoveAtIndex(index); + forwardEntry->srcAndDest.dest.SetPort(receivedAddr.GetPort()); + forwardList.Push(forwardEntry,forwardEntry->srcAndDest,_FILE_AND_LINE_); + } + } + + if (forwardEntry->srcAndDest.dest.EqualsExcludingPort(receivedAddr) && forwardEntry->srcAndDest.dest.GetPort()==receivedAddr.GetPort()) + { + // Forward to source + len=0; + if (forwardEntry->srcAndDest.source.address.addr4.sin_family==AF_INET) + { + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.source.address.addr4, sizeof( sockaddr_in ) ); + } + while ( len == 0 ); + } + else + { + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.source.address.addr6, sizeof( sockaddr_in ) ); + } + while ( len == 0 ); + } + + // printf("2. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); + + forwardEntry->timeLastDatagramForwarded=curTime; + } + } + } +} +#endif // #if RAKNET_SUPPORT_IPV6!=1 +void UDPForwarder::SetMaxForwardEntries(unsigned short maxEntries) +{ + RakAssert(maxEntries>0 && maxEntries<65535/2); + maxForwardEntries=maxEntries; +} +int UDPForwarder::GetMaxForwardEntries(void) const +{ + return maxForwardEntries; +} +int UDPForwarder::GetUsedForwardEntries(void) const +{ + return (int) forwardList.Size(); +} +UDPForwarderResult UDPForwarder::AddForwardingEntry(SrcAndDest srcAndDest, RakNet::TimeMS timeoutOnNoDataMS, unsigned short *port, const char *forceHostAddress, short socketFamily) +{ + (void) socketFamily; + + unsigned int insertionIndex; + bool objectExists; + insertionIndex = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + if (objectExists==false) + { +#if RAKNET_SUPPORT_IPV6!=1 + int sock_opt; + sockaddr_in listenerSocketAddress; + listenerSocketAddress.sin_port = 0; + ForwardEntry *fe = RakNet::OP_NEW(_FILE_AND_LINE_); + fe->srcAndDest=srcAndDest; + fe->timeoutOnNoDataMS=timeoutOnNoDataMS; + fe->socket = socket__( AF_INET, SOCK_DGRAM, 0 ); + + //printf("Made socket %i\n", fe->readSocket); + + // This doubles the max throughput rate + sock_opt=1024*256; + setsockopt__(fe->socket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + // Immediate hard close. Don't linger the readSocket, or recreating the readSocket quickly on Vista fails. + sock_opt=0; + setsockopt__(fe->socket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + listenerSocketAddress.sin_family = AF_INET; + + if (forceHostAddress && forceHostAddress[0]) + { + + + + listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); + + } + else + { + listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; + } + + int ret = bind__( fe->socket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); + if (ret==-1) + { + RakNet::OP_DELETE(fe,_FILE_AND_LINE_); + return UDPFORWARDER_BIND_FAILED; + } +#else + ForwardEntry *fe = RakNet::OP_NEW(_FILE_AND_LINE_); + fe->srcAndDest=srcAndDest; + fe->timeoutOnNoDataMS=timeoutOnNoDataMS; + fe->socket=INVALID_SOCKET; + + struct addrinfo hints; + memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty + hints.ai_family = socketFamily; + hints.ai_socktype = SOCK_DGRAM; // UDP sockets + hints.ai_flags = AI_PASSIVE; // fill in my IP for me + struct addrinfo *servinfo=0, *aip; // will point to the results + + + RakAssert(forceHostAddress==0 || forceHostAddress[0]!=0); + if (_stricmp(forceHostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0) + { + getaddrinfo(0, "0", &hints, &servinfo); + } + else + { + getaddrinfo(forceHostAddress, "0", &hints, &servinfo); + } + + for (aip = servinfo; aip != NULL; aip = aip->ai_next) + { + // Open socket. The address type depends on what + // getaddrinfo() gave us. + fe->socket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (fe->socket != INVALID_SOCKET) + { + int ret = bind__( fe->socket, aip->ai_addr, (int) aip->ai_addrlen ); + if (ret>=0) + { + break; + } + else + { + closesocket__(fe->socket); + fe->socket=INVALID_SOCKET; + } + } + } + + if (fe->socket==INVALID_SOCKET) + return UDPFORWARDER_BIND_FAILED; + + //printf("Made socket %i\n", fe->readSocket); + + // This doubles the max throughput rate + int sock_opt; + sock_opt=1024*256; + setsockopt__(fe->socket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + // Immediate hard close. Don't linger the readSocket, or recreating the readSocket quickly on Vista fails. + sock_opt=0; + setsockopt__(fe->socket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); +#endif // #if RAKNET_SUPPORT_IPV6!=1 + +// unsigned int oldSize = forwardList.Size(); + forwardList.InsertAtIndex(fe,insertionIndex,_FILE_AND_LINE_); + // RakAssert(forwardList.GetIndexOf(fe->srcAndDest)!=(unsigned int) -1); + *port = SocketLayer::GetLocalPort ( fe->socket ); + return UDPFORWARDER_SUCCESS; + } + + return UDPFORWARDER_FORWARDING_ALREADY_EXISTS; +} +UDPForwarderResult UDPForwarder::StartForwarding(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, + unsigned short *forwardingPort, SOCKET *forwardingSocket) +{ + // Invalid parameters? + if (timeoutOnNoDataMS == 0 || timeoutOnNoDataMS > UDP_FORWARDER_MAXIMUM_TIMEOUT || source==UNASSIGNED_SYSTEM_ADDRESS || destination==UNASSIGNED_SYSTEM_ADDRESS) + return UDPFORWARDER_INVALID_PARAMETERS; + +#ifdef UDP_FORWARDER_EXECUTE_THREADED + ThreadOperation threadOperation; + threadOperation.source=source; + threadOperation.destination=destination; + threadOperation.timeoutOnNoDataMS=timeoutOnNoDataMS; + threadOperation.forceHostAddress=forceHostAddress; + threadOperation.socketFamily=socketFamily; + threadOperation.operation=ThreadOperation::TO_START_FORWARDING; + threadOperationIncomingMutex.Lock(); + threadOperationIncomingQueue.Push(threadOperation, _FILE_AND_LINE_ ); + threadOperationIncomingMutex.Unlock(); + + while (1) + { + RakSleep(0); + threadOperationOutgoingMutex.Lock(); + if (threadOperationOutgoingQueue.Size()!=0) + { + threadOperation=threadOperationOutgoingQueue.Pop(); + threadOperationOutgoingMutex.Unlock(); + if (forwardingPort) + *forwardingPort=threadOperation.forwardingPort; + if (forwardingSocket) + *forwardingSocket=threadOperation.forwardingSocket; + return threadOperation.result; + } + threadOperationOutgoingMutex.Unlock(); + + } +#else + return StartForwardingThreaded(source, destination, timeoutOnNoDataMS, forceHostAddress, socketFamily, srcToDestPort, destToSourcePort, srcToDestSocket, destToSourceSocket); +#endif + +} +UDPForwarderResult UDPForwarder::StartForwardingThreaded(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, + unsigned short *forwardingPort, SOCKET *forwardingSocket) +{ + SrcAndDest srcAndDest(source, destination); + + UDPForwarderResult result = AddForwardingEntry(srcAndDest, timeoutOnNoDataMS, forwardingPort, forceHostAddress, socketFamily); + + if (result!=UDPFORWARDER_SUCCESS) + return result; + + if (*forwardingSocket) + { + unsigned int idx; + bool objectExists; + idx = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + RakAssert(objectExists); + *forwardingSocket=forwardList[idx]->socket; + } + + return UDPFORWARDER_SUCCESS; +} +void UDPForwarder::StopForwarding(SystemAddress source, SystemAddress destination) +{ +#ifdef UDP_FORWARDER_EXECUTE_THREADED + ThreadOperation threadOperation; + threadOperation.source=source; + threadOperation.destination=destination; + threadOperation.operation=ThreadOperation::TO_STOP_FORWARDING; + threadOperationIncomingMutex.Lock(); + threadOperationIncomingQueue.Push(threadOperation, _FILE_AND_LINE_ ); + threadOperationIncomingMutex.Unlock(); +#else + StopForwardingThreaded(source, destination); +#endif +} +void UDPForwarder::StopForwardingThreaded(SystemAddress source, SystemAddress destination) +{ + SrcAndDest srcAndDest(destination,source); + + bool objectExists; + unsigned int idx = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + if (objectExists) + { + RakNet::OP_DELETE(forwardList[idx],_FILE_AND_LINE_); + forwardList.RemoveAtIndex(idx); + } +} +namespace RakNet { +#ifdef UDP_FORWARDER_EXECUTE_THREADED +RAK_THREAD_DECLARATION(UpdateUDPForwarder) +{ + + + + UDPForwarder * udpForwarder = ( UDPForwarder * ) arguments; + + + udpForwarder->threadRunning=true; + UDPForwarder::ThreadOperation threadOperation; + while (udpForwarder->isRunning) + { + udpForwarder->threadOperationIncomingMutex.Lock(); + while (udpForwarder->threadOperationIncomingQueue.Size()) + { + threadOperation=udpForwarder->threadOperationIncomingQueue.Pop(); + udpForwarder->threadOperationIncomingMutex.Unlock(); + if (threadOperation.operation==UDPForwarder::ThreadOperation::TO_START_FORWARDING) + { + threadOperation.result=udpForwarder->StartForwardingThreaded(threadOperation.source, threadOperation.destination, threadOperation.timeoutOnNoDataMS, + threadOperation.forceHostAddress, threadOperation.socketFamily, &threadOperation.forwardingPort, &threadOperation.forwardingSocket); + udpForwarder->threadOperationOutgoingMutex.Lock(); + udpForwarder->threadOperationOutgoingQueue.Push(threadOperation, _FILE_AND_LINE_ ); + udpForwarder->threadOperationOutgoingMutex.Unlock(); + } + else + { + udpForwarder->StopForwardingThreaded(threadOperation.source, threadOperation.destination); + } + + + udpForwarder->threadOperationIncomingMutex.Lock(); + } + udpForwarder->threadOperationIncomingMutex.Unlock(); + +#if RAKNET_SUPPORT_IPV6!=1 + udpForwarder->UpdateThreaded_Old(); +#else + udpForwarder->UpdateThreaded(); +#endif + + + // 12/1/2010 Do not change from 0 + // See http://www.jenkinssoftware.com/forum/index.php?topic=4033.0;topicseen + // Avoid 100% reported CPU usage + RakSleep(0); + } + udpForwarder->threadRunning=false; + + + + + return 0; + + +} +} // namespace RakNet +#endif + +#endif // #if _RAKNET_SUPPORT_FileOperations==1 diff --git a/project/lib_projects/raknet/jni/RaknetSources/UDPForwarder.h b/project/lib_projects/raknet/jni/RaknetSources/UDPForwarder.h index 372352e..2b22373 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/UDPForwarder.h +++ b/project/lib_projects/raknet/jni/RaknetSources/UDPForwarder.h @@ -1,177 +1,177 @@ -/// \file -/// \brief Forwards UDP datagrams. Independent of RakNet's protocol. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPForwarder==1 - -#ifndef __UDP_FORWARDER_H -#define __UDP_FORWARDER_H - -#include "Export.h" -#include "RakNetTypes.h" -#include "SocketIncludes.h" -#include "UDPProxyCommon.h" -#include "SimpleMutex.h" -#include "RakString.h" -#include "RakThread.h" -#include "DS_Queue.h" -#include "DS_OrderedList.h" - -#define UDP_FORWARDER_EXECUTE_THREADED - -namespace RakNet -{ - -enum UDPForwarderResult -{ - UDPFORWARDER_FORWARDING_ALREADY_EXISTS, - UDPFORWARDER_NO_SOCKETS, - UDPFORWARDER_BIND_FAILED, - UDPFORWARDER_INVALID_PARAMETERS, - UDPFORWARDER_SUCCESS, - -}; - -/// \brief Forwards UDP datagrams. Independent of RakNet's protocol. -/// \ingroup NAT_PUNCHTHROUGH_GROUP -class RAK_DLL_EXPORT UDPForwarder -{ -public: - UDPForwarder(); - ~UDPForwarder(); - - /// Starts the system. - /// Required to call before StartForwarding - void Startup(void); - - /// Stops the system, and frees all sockets - void Shutdown(void); - - /// Call on a regular basis, unless using UDP_FORWARDER_EXECUTE_THREADED. - /// Will call select__() on all sockets and forward messages. - void Update(void); - - /// Sets the maximum number of forwarding entries allowed - /// Set according to your available bandwidth and the estimated average bandwidth per forwarded address. - /// A single connection requires 2 entries, as connections are bi-directional. - /// \param[in] maxEntries The maximum number of simultaneous forwarding entries. Defaults to 64 (32 connections) - void SetMaxForwardEntries(unsigned short maxEntries); - - /// \return The \a maxEntries parameter passed to SetMaxForwardEntries(), or the default if it was never called - int GetMaxForwardEntries(void) const; - - /// \note Each call to StartForwarding uses up two forwarding entries, since communications are bidirectional - /// \return How many entries have been used - int GetUsedForwardEntries(void) const; - - /// Forwards datagrams from source to destination, and vice-versa - /// Does nothing if this forward entry already exists via a previous call - /// \pre Call Startup() - /// \note RakNet's protocol will ensure a message is sent at least every 15 seconds, so if routing RakNet messages, it is a reasonable value for timeoutOnNoDataMS, plus an some extra seconds for latency - /// \param[in] source The source IP and port - /// \param[in] destination Where to forward to (and vice-versa) - /// \param[in] timeoutOnNoDataMS If no messages are forwarded for this many MS, then automatically remove this entry. Currently hardcoded to UDP_FORWARDER_MAXIMUM_TIMEOUT (else the call fails) - /// \param[in] forceHostAddress Force binding on a particular address. 0 to use any. - /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - short socketFamily; - /// \param[out] forwardingPort New opened port for forwarding - /// \param[out] forwardingSocket New opened socket for forwarding - /// \return UDPForwarderResult - UDPForwarderResult StartForwarding(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, - unsigned short *forwardingPort, SOCKET *forwardingSocket); - - /// No longer forward datagrams from source to destination - /// \param[in] source The source IP and port - /// \param[in] destination Where to forward to - void StopForwarding(SystemAddress source, SystemAddress destination); - - struct SrcAndDest - { - SrcAndDest() {} - SrcAndDest(SystemAddress sa1, SystemAddress sa2) - { - if (sa1 < sa2) - { - source=sa1; - dest=sa2; - } - else - { - source=sa2; - dest=sa1; - } - } - SystemAddress source; - SystemAddress dest; - }; - - struct ForwardEntry - { - ForwardEntry(); - ~ForwardEntry(); - SrcAndDest srcAndDest; - RakNet::TimeMS timeLastDatagramForwarded; - SOCKET socket; - RakNet::TimeMS timeoutOnNoDataMS; - bool updatedSourcePort, updatedDestPort; - short socketFamily; - }; - - -protected: - - static int SrcAndDestForwardEntryComp( const SrcAndDest &inputKey, ForwardEntry * const &cls ); - - - friend RAK_THREAD_DECLARATION(UpdateUDPForwarder); - struct ThreadOperation - { - enum { - TO_NONE, - TO_START_FORWARDING, - TO_STOP_FORWARDING, - } operation; - - SystemAddress source; - SystemAddress destination; - RakNet::TimeMS timeoutOnNoDataMS; - RakNet::RakString forceHostAddress; - unsigned short forwardingPort; - SOCKET forwardingSocket; - UDPForwarderResult result; - unsigned short socketFamily; - }; - SimpleMutex threadOperationIncomingMutex,threadOperationOutgoingMutex; - DataStructures::Queue threadOperationIncomingQueue; - DataStructures::Queue threadOperationOutgoingQueue; - -#if RAKNET_SUPPORT_IPV6==1 - void UpdateThreaded(void); -#endif - void UpdateThreaded_Old(void); - UDPForwarderResult StartForwardingThreaded(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, - unsigned short *forwardingPort, SOCKET *forwardingSocket); - void StopForwardingThreaded(SystemAddress source, SystemAddress destination); - - DataStructures::OrderedList forwardList; - unsigned short maxForwardEntries; - - UDPForwarderResult AddForwardingEntry(SrcAndDest srcAndDest, RakNet::TimeMS timeoutOnNoDataMS, unsigned short *port, const char *forceHostAddress, short socketFamily); - - - bool isRunning, threadRunning; - - - -}; - -} // End namespace - -#endif - -#endif // #if _RAKNET_SUPPORT_UDPForwarder==1 +/// \file +/// \brief Forwards UDP datagrams. Independent of RakNet's protocol. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPForwarder==1 + +#ifndef __UDP_FORWARDER_H +#define __UDP_FORWARDER_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "SocketIncludes.h" +#include "UDPProxyCommon.h" +#include "SimpleMutex.h" +#include "RakString.h" +#include "RakThread.h" +#include "DS_Queue.h" +#include "DS_OrderedList.h" + +#define UDP_FORWARDER_EXECUTE_THREADED + +namespace RakNet +{ + +enum UDPForwarderResult +{ + UDPFORWARDER_FORWARDING_ALREADY_EXISTS, + UDPFORWARDER_NO_SOCKETS, + UDPFORWARDER_BIND_FAILED, + UDPFORWARDER_INVALID_PARAMETERS, + UDPFORWARDER_SUCCESS, + +}; + +/// \brief Forwards UDP datagrams. Independent of RakNet's protocol. +/// \ingroup NAT_PUNCHTHROUGH_GROUP +class RAK_DLL_EXPORT UDPForwarder +{ +public: + UDPForwarder(); + ~UDPForwarder(); + + /// Starts the system. + /// Required to call before StartForwarding + void Startup(void); + + /// Stops the system, and frees all sockets + void Shutdown(void); + + /// Call on a regular basis, unless using UDP_FORWARDER_EXECUTE_THREADED. + /// Will call select__() on all sockets and forward messages. + void Update(void); + + /// Sets the maximum number of forwarding entries allowed + /// Set according to your available bandwidth and the estimated average bandwidth per forwarded address. + /// A single connection requires 2 entries, as connections are bi-directional. + /// \param[in] maxEntries The maximum number of simultaneous forwarding entries. Defaults to 64 (32 connections) + void SetMaxForwardEntries(unsigned short maxEntries); + + /// \return The \a maxEntries parameter passed to SetMaxForwardEntries(), or the default if it was never called + int GetMaxForwardEntries(void) const; + + /// \note Each call to StartForwarding uses up two forwarding entries, since communications are bidirectional + /// \return How many entries have been used + int GetUsedForwardEntries(void) const; + + /// Forwards datagrams from source to destination, and vice-versa + /// Does nothing if this forward entry already exists via a previous call + /// \pre Call Startup() + /// \note RakNet's protocol will ensure a message is sent at least every 15 seconds, so if routing RakNet messages, it is a reasonable value for timeoutOnNoDataMS, plus an some extra seconds for latency + /// \param[in] source The source IP and port + /// \param[in] destination Where to forward to (and vice-versa) + /// \param[in] timeoutOnNoDataMS If no messages are forwarded for this many MS, then automatically remove this entry. Currently hardcoded to UDP_FORWARDER_MAXIMUM_TIMEOUT (else the call fails) + /// \param[in] forceHostAddress Force binding on a particular address. 0 to use any. + /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + short socketFamily; + /// \param[out] forwardingPort New opened port for forwarding + /// \param[out] forwardingSocket New opened socket for forwarding + /// \return UDPForwarderResult + UDPForwarderResult StartForwarding(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, + unsigned short *forwardingPort, SOCKET *forwardingSocket); + + /// No longer forward datagrams from source to destination + /// \param[in] source The source IP and port + /// \param[in] destination Where to forward to + void StopForwarding(SystemAddress source, SystemAddress destination); + + struct SrcAndDest + { + SrcAndDest() {} + SrcAndDest(SystemAddress sa1, SystemAddress sa2) + { + if (sa1 < sa2) + { + source=sa1; + dest=sa2; + } + else + { + source=sa2; + dest=sa1; + } + } + SystemAddress source; + SystemAddress dest; + }; + + struct ForwardEntry + { + ForwardEntry(); + ~ForwardEntry(); + SrcAndDest srcAndDest; + RakNet::TimeMS timeLastDatagramForwarded; + SOCKET socket; + RakNet::TimeMS timeoutOnNoDataMS; + bool updatedSourcePort, updatedDestPort; + short socketFamily; + }; + + +protected: + + static int SrcAndDestForwardEntryComp( const SrcAndDest &inputKey, ForwardEntry * const &cls ); + + + friend RAK_THREAD_DECLARATION(UpdateUDPForwarder); + struct ThreadOperation + { + enum { + TO_NONE, + TO_START_FORWARDING, + TO_STOP_FORWARDING, + } operation; + + SystemAddress source; + SystemAddress destination; + RakNet::TimeMS timeoutOnNoDataMS; + RakNet::RakString forceHostAddress; + unsigned short forwardingPort; + SOCKET forwardingSocket; + UDPForwarderResult result; + unsigned short socketFamily; + }; + SimpleMutex threadOperationIncomingMutex,threadOperationOutgoingMutex; + DataStructures::Queue threadOperationIncomingQueue; + DataStructures::Queue threadOperationOutgoingQueue; + +#if RAKNET_SUPPORT_IPV6==1 + void UpdateThreaded(void); +#endif + void UpdateThreaded_Old(void); + UDPForwarderResult StartForwardingThreaded(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, + unsigned short *forwardingPort, SOCKET *forwardingSocket); + void StopForwardingThreaded(SystemAddress source, SystemAddress destination); + + DataStructures::OrderedList forwardList; + unsigned short maxForwardEntries; + + UDPForwarderResult AddForwardingEntry(SrcAndDest srcAndDest, RakNet::TimeMS timeoutOnNoDataMS, unsigned short *port, const char *forceHostAddress, short socketFamily); + + + bool isRunning, threadRunning; + + + +}; + +} // End namespace + +#endif + +#endif // #if _RAKNET_SUPPORT_UDPForwarder==1 diff --git a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyClient.cpp b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyClient.cpp index 3ce9a2d..bbe5bb9 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyClient.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyClient.cpp @@ -1,301 +1,301 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyClient==1 - -#include "UDPProxyClient.h" -#include "BitStream.h" -#include "UDPProxyCommon.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "GetTime.h" - -using namespace RakNet; -static const int DEFAULT_UNRESPONSIVE_PING_TIME=1000; - -// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress < cls.serverAddress;} -// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress > cls.serverAddress;} -// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress == cls.serverAddress;} - -STATIC_FACTORY_DEFINITIONS(UDPProxyClient,UDPProxyClient); - -UDPProxyClient::UDPProxyClient() -{ - resultHandler=0; -} -UDPProxyClient::~UDPProxyClient() -{ - Clear(); -} -void UDPProxyClient::SetResultHandler(UDPProxyClientResultHandler *rh) -{ - resultHandler=rh; -} -bool UDPProxyClient::RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, RakNetGUID targetGuid, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream) -{ - // Return false if not connected - ConnectionState cs = rakPeerInterface->GetConnectionState(proxyCoordinator); - if (cs!=IS_CONNECTED) - return false; - - // Pretty much a bug not to set the result handler, as otherwise you won't know if the operation succeeed or not - RakAssert(resultHandler!=0); - if (resultHandler==0) - return false; - - BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(false); - outgoingBs.Write(targetGuid); - outgoingBs.Write(timeoutOnNoDataMS); - if (serverSelectionBitstream && serverSelectionBitstream->GetNumberOfBitsUsed()>0) - { - outgoingBs.Write(true); - outgoingBs.Write(serverSelectionBitstream); - } - else - { - outgoingBs.Write(false); - } - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, proxyCoordinator, false); - - return true; -} -bool UDPProxyClient::RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddressAsSeenFromCoordinator, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream) -{ - // Return false if not connected - ConnectionState cs = rakPeerInterface->GetConnectionState(proxyCoordinator); - if (cs!=IS_CONNECTED) - return false; - - // Pretty much a bug not to set the result handler, as otherwise you won't know if the operation succeeed or not - RakAssert(resultHandler!=0); - if (resultHandler==0) - return false; - - BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(true); - outgoingBs.Write(targetAddressAsSeenFromCoordinator); - outgoingBs.Write(timeoutOnNoDataMS); - if (serverSelectionBitstream && serverSelectionBitstream->GetNumberOfBitsUsed()>0) - { - outgoingBs.Write(true); - outgoingBs.Write(serverSelectionBitstream); - } - else - { - outgoingBs.Write(false); - } - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, proxyCoordinator, false); - - return true; -} -void UDPProxyClient::Update(void) -{ - unsigned int idx1=0; - while (idx1 < pingServerGroups.Size()) - { - PingServerGroup *psg = pingServerGroups[idx1]; - - if (psg->serversToPing.Size() > 0 && - RakNet::GetTimeMS() > psg->startPingTime+DEFAULT_UNRESPONSIVE_PING_TIME) - { - // If they didn't reply within DEFAULT_UNRESPONSIVE_PING_TIME, just give up on them - psg->SendPingedServersToCoordinator(rakPeerInterface); - - RakNet::OP_DELETE(psg,_FILE_AND_LINE_); - pingServerGroups.RemoveAtIndex(idx1); - } - else - idx1++; - } - -} -PluginReceiveResult UDPProxyClient::OnReceive(Packet *packet) -{ - if (packet->data[0]==ID_UNCONNECTED_PONG) - { - unsigned int idx1, idx2; - PingServerGroup *psg; - for (idx1=0; idx1 < pingServerGroups.Size(); idx1++) - { - psg = pingServerGroups[idx1]; - for (idx2=0; idx2 < psg->serversToPing.Size(); idx2++) - { - if (psg->serversToPing[idx2].serverAddress==packet->systemAddress) - { - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - RakNet::TimeMS sentTime; - bsIn.Read(sentTime); - RakNet::TimeMS curTime=RakNet::GetTimeMS(); - int ping; - if (curTime>sentTime) - ping=(int) (curTime-sentTime); - else - ping=0; - psg->serversToPing[idx2].ping=(unsigned short) ping; - - // If all servers to ping are now pinged, reply to coordinator - if (psg->AreAllServersPinged()) - { - psg->SendPingedServersToCoordinator(rakPeerInterface); - RakNet::OP_DELETE(psg,_FILE_AND_LINE_); - pingServerGroups.RemoveAtIndex(idx1); - } - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - - } - } - else if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) - { - switch (packet->data[1]) - { - case ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT: - { - OnPingServers(packet); - } - break; - case ID_UDP_PROXY_FORWARDING_SUCCEEDED: - case ID_UDP_PROXY_ALL_SERVERS_BUSY: - case ID_UDP_PROXY_IN_PROGRESS: - case ID_UDP_PROXY_NO_SERVERS_ONLINE: - case ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR: - case ID_UDP_PROXY_FORWARDING_NOTIFICATION: - { - RakNetGUID targetGuid; - SystemAddress senderAddress, targetAddress; - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(sizeof(MessageID)*2); - incomingBs.Read(senderAddress); - incomingBs.Read(targetAddress); - incomingBs.Read(targetGuid); - - switch (packet->data[1]) - { - case ID_UDP_PROXY_FORWARDING_NOTIFICATION: - case ID_UDP_PROXY_FORWARDING_SUCCEEDED: - { - unsigned short forwardingPort; - RakNet::RakString serverIP; - incomingBs.Read(serverIP); - incomingBs.Read(forwardingPort); - if (packet->data[1]==ID_UDP_PROXY_FORWARDING_SUCCEEDED) - { - if (resultHandler) - resultHandler->OnForwardingSuccess(serverIP.C_String(), forwardingPort, packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - } - else - { - // Send a datagram to the proxy, so if we are behind a router, that router adds an entry to the routing table. - // Otherwise the router would block the incoming datagrams from source - // It doesn't matter if the message actually arrives as long as it goes through the router - rakPeerInterface->Ping(serverIP.C_String(), forwardingPort, false); - - if (resultHandler) - resultHandler->OnForwardingNotification(serverIP.C_String(), forwardingPort, packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - } - } - break; - case ID_UDP_PROXY_ALL_SERVERS_BUSY: - if (resultHandler) - resultHandler->OnAllServersBusy(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - break; - case ID_UDP_PROXY_IN_PROGRESS: - if (resultHandler) - resultHandler->OnForwardingInProgress(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - break; - case ID_UDP_PROXY_NO_SERVERS_ONLINE: - if (resultHandler) - resultHandler->OnNoServersOnline(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - break; - case ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR: - { - if (resultHandler) - resultHandler->OnRecipientNotConnected(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - break; - } - } - - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - return RR_CONTINUE_PROCESSING; -} -void UDPProxyClient::OnRakPeerShutdown(void) -{ - Clear(); -} -void UDPProxyClient::OnPingServers(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - - PingServerGroup *psg = RakNet::OP_NEW(_FILE_AND_LINE_); - - ServerWithPing swp; - incomingBs.Read(psg->sata.senderClientAddress); - incomingBs.Read(psg->sata.targetClientAddress); - psg->startPingTime=RakNet::GetTimeMS(); - psg->coordinatorAddressForPings=packet->systemAddress; - unsigned short serverListSize; - incomingBs.Read(serverListSize); - SystemAddress serverAddress; - unsigned short serverListIndex; - char ipStr[64]; - for (serverListIndex=0; serverListIndexserversToPing.Push(swp, _FILE_AND_LINE_ ); - swp.serverAddress.ToString(false,ipStr); - rakPeerInterface->Ping(ipStr,swp.serverAddress.GetPort(),false,0); - } - pingServerGroups.Push(psg,_FILE_AND_LINE_); -} - -bool UDPProxyClient::PingServerGroup::AreAllServersPinged(void) const -{ - unsigned int serversToPingIndex; - for (serversToPingIndex=0; serversToPingIndex < serversToPing.Size(); serversToPingIndex++) - { - if (serversToPing[serversToPingIndex].ping==DEFAULT_UNRESPONSIVE_PING_TIME) - return false; - } - return true; -} - -void UDPProxyClient::PingServerGroup::SendPingedServersToCoordinator(RakPeerInterface *rakPeerInterface) -{ - BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(sata.targetClientAddress); - unsigned short serversToPingSize = (unsigned short) serversToPing.Size(); - outgoingBs.Write(serversToPingSize); - unsigned int serversToPingIndex; - for (serversToPingIndex=0; serversToPingIndex < serversToPingSize; serversToPingIndex++) - { - outgoingBs.Write(serversToPing[serversToPingIndex].serverAddress); - outgoingBs.Write(serversToPing[serversToPingIndex].ping); - } - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, coordinatorAddressForPings, false); -} -void UDPProxyClient::Clear(void) -{ - for (unsigned int i=0; i < pingServerGroups.Size(); i++) - RakNet::OP_DELETE(pingServerGroups[i],_FILE_AND_LINE_); - pingServerGroups.Clear(false, _FILE_AND_LINE_); -} - - -#endif // _RAKNET_SUPPORT_* - +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyClient==1 + +#include "UDPProxyClient.h" +#include "BitStream.h" +#include "UDPProxyCommon.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "GetTime.h" + +using namespace RakNet; +static const int DEFAULT_UNRESPONSIVE_PING_TIME=1000; + +// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress < cls.serverAddress;} +// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress > cls.serverAddress;} +// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress == cls.serverAddress;} + +STATIC_FACTORY_DEFINITIONS(UDPProxyClient,UDPProxyClient); + +UDPProxyClient::UDPProxyClient() +{ + resultHandler=0; +} +UDPProxyClient::~UDPProxyClient() +{ + Clear(); +} +void UDPProxyClient::SetResultHandler(UDPProxyClientResultHandler *rh) +{ + resultHandler=rh; +} +bool UDPProxyClient::RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, RakNetGUID targetGuid, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream) +{ + // Return false if not connected + ConnectionState cs = rakPeerInterface->GetConnectionState(proxyCoordinator); + if (cs!=IS_CONNECTED) + return false; + + // Pretty much a bug not to set the result handler, as otherwise you won't know if the operation succeeed or not + RakAssert(resultHandler!=0); + if (resultHandler==0) + return false; + + BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(false); + outgoingBs.Write(targetGuid); + outgoingBs.Write(timeoutOnNoDataMS); + if (serverSelectionBitstream && serverSelectionBitstream->GetNumberOfBitsUsed()>0) + { + outgoingBs.Write(true); + outgoingBs.Write(serverSelectionBitstream); + } + else + { + outgoingBs.Write(false); + } + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, proxyCoordinator, false); + + return true; +} +bool UDPProxyClient::RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddressAsSeenFromCoordinator, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream) +{ + // Return false if not connected + ConnectionState cs = rakPeerInterface->GetConnectionState(proxyCoordinator); + if (cs!=IS_CONNECTED) + return false; + + // Pretty much a bug not to set the result handler, as otherwise you won't know if the operation succeeed or not + RakAssert(resultHandler!=0); + if (resultHandler==0) + return false; + + BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(true); + outgoingBs.Write(targetAddressAsSeenFromCoordinator); + outgoingBs.Write(timeoutOnNoDataMS); + if (serverSelectionBitstream && serverSelectionBitstream->GetNumberOfBitsUsed()>0) + { + outgoingBs.Write(true); + outgoingBs.Write(serverSelectionBitstream); + } + else + { + outgoingBs.Write(false); + } + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, proxyCoordinator, false); + + return true; +} +void UDPProxyClient::Update(void) +{ + unsigned int idx1=0; + while (idx1 < pingServerGroups.Size()) + { + PingServerGroup *psg = pingServerGroups[idx1]; + + if (psg->serversToPing.Size() > 0 && + RakNet::GetTimeMS() > psg->startPingTime+DEFAULT_UNRESPONSIVE_PING_TIME) + { + // If they didn't reply within DEFAULT_UNRESPONSIVE_PING_TIME, just give up on them + psg->SendPingedServersToCoordinator(rakPeerInterface); + + RakNet::OP_DELETE(psg,_FILE_AND_LINE_); + pingServerGroups.RemoveAtIndex(idx1); + } + else + idx1++; + } + +} +PluginReceiveResult UDPProxyClient::OnReceive(Packet *packet) +{ + if (packet->data[0]==ID_UNCONNECTED_PONG) + { + unsigned int idx1, idx2; + PingServerGroup *psg; + for (idx1=0; idx1 < pingServerGroups.Size(); idx1++) + { + psg = pingServerGroups[idx1]; + for (idx2=0; idx2 < psg->serversToPing.Size(); idx2++) + { + if (psg->serversToPing[idx2].serverAddress==packet->systemAddress) + { + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + RakNet::TimeMS sentTime; + bsIn.Read(sentTime); + RakNet::TimeMS curTime=RakNet::GetTimeMS(); + int ping; + if (curTime>sentTime) + ping=(int) (curTime-sentTime); + else + ping=0; + psg->serversToPing[idx2].ping=(unsigned short) ping; + + // If all servers to ping are now pinged, reply to coordinator + if (psg->AreAllServersPinged()) + { + psg->SendPingedServersToCoordinator(rakPeerInterface); + RakNet::OP_DELETE(psg,_FILE_AND_LINE_); + pingServerGroups.RemoveAtIndex(idx1); + } + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + + } + } + else if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) + { + switch (packet->data[1]) + { + case ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT: + { + OnPingServers(packet); + } + break; + case ID_UDP_PROXY_FORWARDING_SUCCEEDED: + case ID_UDP_PROXY_ALL_SERVERS_BUSY: + case ID_UDP_PROXY_IN_PROGRESS: + case ID_UDP_PROXY_NO_SERVERS_ONLINE: + case ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR: + case ID_UDP_PROXY_FORWARDING_NOTIFICATION: + { + RakNetGUID targetGuid; + SystemAddress senderAddress, targetAddress; + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(sizeof(MessageID)*2); + incomingBs.Read(senderAddress); + incomingBs.Read(targetAddress); + incomingBs.Read(targetGuid); + + switch (packet->data[1]) + { + case ID_UDP_PROXY_FORWARDING_NOTIFICATION: + case ID_UDP_PROXY_FORWARDING_SUCCEEDED: + { + unsigned short forwardingPort; + RakNet::RakString serverIP; + incomingBs.Read(serverIP); + incomingBs.Read(forwardingPort); + if (packet->data[1]==ID_UDP_PROXY_FORWARDING_SUCCEEDED) + { + if (resultHandler) + resultHandler->OnForwardingSuccess(serverIP.C_String(), forwardingPort, packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + } + else + { + // Send a datagram to the proxy, so if we are behind a router, that router adds an entry to the routing table. + // Otherwise the router would block the incoming datagrams from source + // It doesn't matter if the message actually arrives as long as it goes through the router + rakPeerInterface->Ping(serverIP.C_String(), forwardingPort, false); + + if (resultHandler) + resultHandler->OnForwardingNotification(serverIP.C_String(), forwardingPort, packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + } + } + break; + case ID_UDP_PROXY_ALL_SERVERS_BUSY: + if (resultHandler) + resultHandler->OnAllServersBusy(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + break; + case ID_UDP_PROXY_IN_PROGRESS: + if (resultHandler) + resultHandler->OnForwardingInProgress(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + break; + case ID_UDP_PROXY_NO_SERVERS_ONLINE: + if (resultHandler) + resultHandler->OnNoServersOnline(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + break; + case ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR: + { + if (resultHandler) + resultHandler->OnRecipientNotConnected(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + break; + } + } + + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + return RR_CONTINUE_PROCESSING; +} +void UDPProxyClient::OnRakPeerShutdown(void) +{ + Clear(); +} +void UDPProxyClient::OnPingServers(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + + PingServerGroup *psg = RakNet::OP_NEW(_FILE_AND_LINE_); + + ServerWithPing swp; + incomingBs.Read(psg->sata.senderClientAddress); + incomingBs.Read(psg->sata.targetClientAddress); + psg->startPingTime=RakNet::GetTimeMS(); + psg->coordinatorAddressForPings=packet->systemAddress; + unsigned short serverListSize; + incomingBs.Read(serverListSize); + SystemAddress serverAddress; + unsigned short serverListIndex; + char ipStr[64]; + for (serverListIndex=0; serverListIndexserversToPing.Push(swp, _FILE_AND_LINE_ ); + swp.serverAddress.ToString(false,ipStr); + rakPeerInterface->Ping(ipStr,swp.serverAddress.GetPort(),false,0); + } + pingServerGroups.Push(psg,_FILE_AND_LINE_); +} + +bool UDPProxyClient::PingServerGroup::AreAllServersPinged(void) const +{ + unsigned int serversToPingIndex; + for (serversToPingIndex=0; serversToPingIndex < serversToPing.Size(); serversToPingIndex++) + { + if (serversToPing[serversToPingIndex].ping==DEFAULT_UNRESPONSIVE_PING_TIME) + return false; + } + return true; +} + +void UDPProxyClient::PingServerGroup::SendPingedServersToCoordinator(RakPeerInterface *rakPeerInterface) +{ + BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(sata.targetClientAddress); + unsigned short serversToPingSize = (unsigned short) serversToPing.Size(); + outgoingBs.Write(serversToPingSize); + unsigned int serversToPingIndex; + for (serversToPingIndex=0; serversToPingIndex < serversToPingSize; serversToPingIndex++) + { + outgoingBs.Write(serversToPing[serversToPingIndex].serverAddress); + outgoingBs.Write(serversToPing[serversToPingIndex].ping); + } + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, coordinatorAddressForPings, false); +} +void UDPProxyClient::Clear(void) +{ + for (unsigned int i=0; i < pingServerGroups.Size(); i++) + RakNet::OP_DELETE(pingServerGroups[i],_FILE_AND_LINE_); + pingServerGroups.Clear(false, _FILE_AND_LINE_); +} + + +#endif // _RAKNET_SUPPORT_* + diff --git a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyClient.h b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyClient.h index fe49847..eb3fb68 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyClient.h +++ b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyClient.h @@ -1,178 +1,178 @@ -/// \file -/// \brief A RakNet plugin performing networking to communicate with UDPProxyCoordinator. Ultimately used to tell UDPProxyServer to forward UDP packets. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyClient==1 - -#ifndef __UDP_PROXY_CLIENT_H -#define __UDP_PROXY_CLIENT_H - -#include "Export.h" -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "DS_List.h" - -/// \defgroup UDP_PROXY_GROUP UDPProxy -/// \brief Forwards UDP datagrams from one system to another. Protocol independent -/// \details Used when NatPunchthroughClient fails -/// \ingroup PLUGINS_GROUP - -namespace RakNet -{ -class UDPProxyClient; - -/// Callback to handle results of calling UDPProxyClient::RequestForwarding() -/// \ingroup UDP_PROXY_GROUP -struct UDPProxyClientResultHandler -{ - UDPProxyClientResultHandler() {} - virtual ~UDPProxyClientResultHandler() {} - - /// Called when our forwarding request was completed. We can now connect to \a targetAddress by using \a proxyAddress instead - /// \param[out] proxyIPAddress IP Address of the proxy server, which will forward messages to targetAddress - /// \param[out] proxyPort Remote port to use on the proxy server, which will forward messages to targetAddress - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnForwardingSuccess(const char *proxyIPAddress, unsigned short proxyPort, - SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when another system has setup forwarding, with our system as the target address. - /// Plugin automatically sends a datagram to proxyIPAddress before this callback, to open our router if necessary. - /// \param[out] proxyIPAddress IP Address of the proxy server, which will forward messages to targetAddress - /// \param[out] proxyPort Remote port to use on the proxy server, which will forward messages to targetAddress - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. This is originating source IP address of the remote system that will be sending to us. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding. This is our external IP address. - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnForwardingNotification(const char *proxyIPAddress, unsigned short proxyPort, - SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when our forwarding request failed, because no UDPProxyServers are connected to UDPProxyCoordinator - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnNoServersOnline(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when our forwarding request failed, because no UDPProxyServers are connected to UDPProxyCoordinator - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnRecipientNotConnected(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when our forwarding request failed, because all UDPProxyServers that are connected to UDPProxyCoordinator are at their capacity - /// Either add more servers, or increase capacity via UDPForwarder::SetMaxForwardEntries() - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnAllServersBusy(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when our forwarding request is already in progress on the \a proxyCoordinator. - /// This can be ignored, but indicates an unneeded second request - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnForwardingInProgress(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; -}; - - -/// \brief Communicates with UDPProxyCoordinator, in order to find a UDPProxyServer to forward our datagrams. -/// \details When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa.
      -/// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin.
      -/// The UDPProxyClient connects to UDPProxyCoordinator to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request -/// \sa NatPunchthroughServer -/// \sa NatPunchthroughClient -/// \ingroup UDP_PROXY_GROUP -class RAK_DLL_EXPORT UDPProxyClient : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(UDPProxyClient) - - UDPProxyClient(); - ~UDPProxyClient(); - - /// Receives the results of calling RequestForwarding() - /// Set before calling RequestForwarding or you won't know what happened - /// \param[in] resultHandler - void SetResultHandler(UDPProxyClientResultHandler *rh); - - /// Sends a request to proxyCoordinator to find a server and have that server setup UDPForwarder::StartForwarding() on our address to \a targetAddressAsSeenFromCoordinator - /// The forwarded datagrams can be from any UDP source, not just RakNet - /// \pre Must be connected to \a proxyCoordinator - /// \pre Systems running UDPProxyServer must be connected to \a proxyCoordinator and logged in via UDPProxyCoordinator::LoginServer() or UDPProxyServer::LoginToCoordinator() - /// \note May still fail, if all proxy servers have no open connections. - /// \note RakNet's protocol will ensure a message is sent at least every 5 seconds, so if routing RakNet messages, it is a reasonable value for timeoutOnNoDataMS, plus an extra few seconds for latency. - /// \param[in] proxyCoordinator System we are connected to that is running the UDPProxyCoordinator plugin - /// \param[in] sourceAddress External IP address of the system we want to forward messages from. This does not have to be our own system. To specify our own system, you can pass UNASSIGNED_SYSTEM_ADDRESS which the coordinator will treat as our external IP address. - /// \param[in] targetAddressAsSeenFromCoordinator External IP address of the system we want to forward messages to. If this system is connected to UDPProxyCoordinator at this address using RakNet, that system will ping the server and thus open the router for incoming communication. In any other case, you are responsible for doing your own network communication to have that system ping the server. See also targetGuid in the other version of RequestForwarding(), to avoid the need to know the IP address to the coordinator of the destination. - /// \param[in] timeoutOnNoData If no data is sent by the forwarded systems, how long before removing the forward entry from UDPForwarder? UDP_FORWARDER_MAXIMUM_TIMEOUT is the maximum value. Recommended high enough to not drop valid connections, but low enough to not waste forwarding slots on the proxy server. - /// \param[in] serverSelectionBitstream If you want to send data to UDPProxyCoordinator::GetBestServer(), write it here - /// \return true if the request was sent, false if we are not connected to proxyCoordinator - bool RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddressAsSeenFromCoordinator, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream=0); - - /// Same as above, but specify the target with a GUID, in case you don't know what its address is to the coordinator - /// If requesting forwarding to a RakNet enabled system, then it is easier to use targetGuid instead of targetAddressAsSeenFromCoordinator - bool RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, RakNetGUID targetGuid, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream=0); - - /// \internal - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnRakPeerShutdown(void); - - struct ServerWithPing - { - unsigned short ping; - SystemAddress serverAddress; - }; - struct SenderAndTargetAddress - { - SystemAddress senderClientAddress; - SystemAddress targetClientAddress; - }; - struct PingServerGroup - { - SenderAndTargetAddress sata; - RakNet::TimeMS startPingTime; - SystemAddress coordinatorAddressForPings; - //DataStructures::Multilist serversToPing; - DataStructures::List serversToPing; - bool AreAllServersPinged(void) const; - void SendPingedServersToCoordinator(RakPeerInterface *rakPeerInterface); - }; - //DataStructures::Multilist pingServerGroups; - DataStructures::List pingServerGroups; -protected: - - void OnPingServers(Packet *packet); - void Clear(void); - UDPProxyClientResultHandler *resultHandler; - -}; - -} // End namespace - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief A RakNet plugin performing networking to communicate with UDPProxyCoordinator. Ultimately used to tell UDPProxyServer to forward UDP packets. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyClient==1 + +#ifndef __UDP_PROXY_CLIENT_H +#define __UDP_PROXY_CLIENT_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "DS_List.h" + +/// \defgroup UDP_PROXY_GROUP UDPProxy +/// \brief Forwards UDP datagrams from one system to another. Protocol independent +/// \details Used when NatPunchthroughClient fails +/// \ingroup PLUGINS_GROUP + +namespace RakNet +{ +class UDPProxyClient; + +/// Callback to handle results of calling UDPProxyClient::RequestForwarding() +/// \ingroup UDP_PROXY_GROUP +struct UDPProxyClientResultHandler +{ + UDPProxyClientResultHandler() {} + virtual ~UDPProxyClientResultHandler() {} + + /// Called when our forwarding request was completed. We can now connect to \a targetAddress by using \a proxyAddress instead + /// \param[out] proxyIPAddress IP Address of the proxy server, which will forward messages to targetAddress + /// \param[out] proxyPort Remote port to use on the proxy server, which will forward messages to targetAddress + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnForwardingSuccess(const char *proxyIPAddress, unsigned short proxyPort, + SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when another system has setup forwarding, with our system as the target address. + /// Plugin automatically sends a datagram to proxyIPAddress before this callback, to open our router if necessary. + /// \param[out] proxyIPAddress IP Address of the proxy server, which will forward messages to targetAddress + /// \param[out] proxyPort Remote port to use on the proxy server, which will forward messages to targetAddress + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. This is originating source IP address of the remote system that will be sending to us. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding. This is our external IP address. + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnForwardingNotification(const char *proxyIPAddress, unsigned short proxyPort, + SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when our forwarding request failed, because no UDPProxyServers are connected to UDPProxyCoordinator + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnNoServersOnline(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when our forwarding request failed, because no UDPProxyServers are connected to UDPProxyCoordinator + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnRecipientNotConnected(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when our forwarding request failed, because all UDPProxyServers that are connected to UDPProxyCoordinator are at their capacity + /// Either add more servers, or increase capacity via UDPForwarder::SetMaxForwardEntries() + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnAllServersBusy(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when our forwarding request is already in progress on the \a proxyCoordinator. + /// This can be ignored, but indicates an unneeded second request + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnForwardingInProgress(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; +}; + + +/// \brief Communicates with UDPProxyCoordinator, in order to find a UDPProxyServer to forward our datagrams. +/// \details When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa.
      +/// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin.
      +/// The UDPProxyClient connects to UDPProxyCoordinator to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request +/// \sa NatPunchthroughServer +/// \sa NatPunchthroughClient +/// \ingroup UDP_PROXY_GROUP +class RAK_DLL_EXPORT UDPProxyClient : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(UDPProxyClient) + + UDPProxyClient(); + ~UDPProxyClient(); + + /// Receives the results of calling RequestForwarding() + /// Set before calling RequestForwarding or you won't know what happened + /// \param[in] resultHandler + void SetResultHandler(UDPProxyClientResultHandler *rh); + + /// Sends a request to proxyCoordinator to find a server and have that server setup UDPForwarder::StartForwarding() on our address to \a targetAddressAsSeenFromCoordinator + /// The forwarded datagrams can be from any UDP source, not just RakNet + /// \pre Must be connected to \a proxyCoordinator + /// \pre Systems running UDPProxyServer must be connected to \a proxyCoordinator and logged in via UDPProxyCoordinator::LoginServer() or UDPProxyServer::LoginToCoordinator() + /// \note May still fail, if all proxy servers have no open connections. + /// \note RakNet's protocol will ensure a message is sent at least every 5 seconds, so if routing RakNet messages, it is a reasonable value for timeoutOnNoDataMS, plus an extra few seconds for latency. + /// \param[in] proxyCoordinator System we are connected to that is running the UDPProxyCoordinator plugin + /// \param[in] sourceAddress External IP address of the system we want to forward messages from. This does not have to be our own system. To specify our own system, you can pass UNASSIGNED_SYSTEM_ADDRESS which the coordinator will treat as our external IP address. + /// \param[in] targetAddressAsSeenFromCoordinator External IP address of the system we want to forward messages to. If this system is connected to UDPProxyCoordinator at this address using RakNet, that system will ping the server and thus open the router for incoming communication. In any other case, you are responsible for doing your own network communication to have that system ping the server. See also targetGuid in the other version of RequestForwarding(), to avoid the need to know the IP address to the coordinator of the destination. + /// \param[in] timeoutOnNoData If no data is sent by the forwarded systems, how long before removing the forward entry from UDPForwarder? UDP_FORWARDER_MAXIMUM_TIMEOUT is the maximum value. Recommended high enough to not drop valid connections, but low enough to not waste forwarding slots on the proxy server. + /// \param[in] serverSelectionBitstream If you want to send data to UDPProxyCoordinator::GetBestServer(), write it here + /// \return true if the request was sent, false if we are not connected to proxyCoordinator + bool RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddressAsSeenFromCoordinator, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream=0); + + /// Same as above, but specify the target with a GUID, in case you don't know what its address is to the coordinator + /// If requesting forwarding to a RakNet enabled system, then it is easier to use targetGuid instead of targetAddressAsSeenFromCoordinator + bool RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, RakNetGUID targetGuid, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream=0); + + /// \internal + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnRakPeerShutdown(void); + + struct ServerWithPing + { + unsigned short ping; + SystemAddress serverAddress; + }; + struct SenderAndTargetAddress + { + SystemAddress senderClientAddress; + SystemAddress targetClientAddress; + }; + struct PingServerGroup + { + SenderAndTargetAddress sata; + RakNet::TimeMS startPingTime; + SystemAddress coordinatorAddressForPings; + //DataStructures::Multilist serversToPing; + DataStructures::List serversToPing; + bool AreAllServersPinged(void) const; + void SendPingedServersToCoordinator(RakPeerInterface *rakPeerInterface); + }; + //DataStructures::Multilist pingServerGroups; + DataStructures::List pingServerGroups; +protected: + + void OnPingServers(Packet *packet); + void Clear(void); + UDPProxyClientResultHandler *resultHandler; + +}; + +} // End namespace + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCommon.h b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCommon.h index 29699f3..14593e3 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCommon.h +++ b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCommon.h @@ -1,57 +1,57 @@ -#ifndef __UDP_PROXY_COMMON_H -#define __UDP_PROXY_COMMON_H - -// System flow: -/* -UDPProxyClient: End user -UDPProxyServer: open server, to route messages from end users that can't connect to each other using UDPForwarder class. -UDPProxyCoordinator: Server somewhere, connected to by RakNet, to maintain a list of UDPProxyServer - -UDPProxyServer - On startup, log into UDPProxyCoordinator and register self - -UDPProxyClient - Wish to open route to X - Send message to UDPProxyCoordinator containing X, desired timeout - Wait for success or failure - -UDPProxyCoordinator: -* Get openRouteRequest - If no servers registered, return failure - Add entry to memory - chooseBestUDPProxyServer() (overridable, chooses at random by default) - Query this server to StartForwarding(). Return success or failure - If failure, choose another server from the remaining list. If none remaining, return failure. Else return success. -* Disconnect: - If disconnected system is pending client on openRouteRequest, delete that request - If disconnected system is UDPProxyServer, remove from list. For each pending client for this server, choose from remaining servers. -* Login: - Add to UDPProxyServer list, validating password if set -*/ - -// Stored in the second byte after ID_UDP_PROXY_GENERAL -// Otherwise MessageIdentifiers.h is too cluttered and will hit the limit on enumerations in a single byte -enum UDPProxyMessages -{ - ID_UDP_PROXY_FORWARDING_SUCCEEDED, - ID_UDP_PROXY_FORWARDING_NOTIFICATION, - ID_UDP_PROXY_NO_SERVERS_ONLINE, - ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR, - ID_UDP_PROXY_ALL_SERVERS_BUSY, - ID_UDP_PROXY_IN_PROGRESS, - ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR, - ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT, - ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR, - ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER, - ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR, - ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR, - ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER, - ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER, - ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER, - ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER -}; - - -#define UDP_FORWARDER_MAXIMUM_TIMEOUT (60000 * 10) - -#endif +#ifndef __UDP_PROXY_COMMON_H +#define __UDP_PROXY_COMMON_H + +// System flow: +/* +UDPProxyClient: End user +UDPProxyServer: open server, to route messages from end users that can't connect to each other using UDPForwarder class. +UDPProxyCoordinator: Server somewhere, connected to by RakNet, to maintain a list of UDPProxyServer + +UDPProxyServer + On startup, log into UDPProxyCoordinator and register self + +UDPProxyClient + Wish to open route to X + Send message to UDPProxyCoordinator containing X, desired timeout + Wait for success or failure + +UDPProxyCoordinator: +* Get openRouteRequest + If no servers registered, return failure + Add entry to memory + chooseBestUDPProxyServer() (overridable, chooses at random by default) + Query this server to StartForwarding(). Return success or failure + If failure, choose another server from the remaining list. If none remaining, return failure. Else return success. +* Disconnect: + If disconnected system is pending client on openRouteRequest, delete that request + If disconnected system is UDPProxyServer, remove from list. For each pending client for this server, choose from remaining servers. +* Login: + Add to UDPProxyServer list, validating password if set +*/ + +// Stored in the second byte after ID_UDP_PROXY_GENERAL +// Otherwise MessageIdentifiers.h is too cluttered and will hit the limit on enumerations in a single byte +enum UDPProxyMessages +{ + ID_UDP_PROXY_FORWARDING_SUCCEEDED, + ID_UDP_PROXY_FORWARDING_NOTIFICATION, + ID_UDP_PROXY_NO_SERVERS_ONLINE, + ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR, + ID_UDP_PROXY_ALL_SERVERS_BUSY, + ID_UDP_PROXY_IN_PROGRESS, + ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR, + ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT, + ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR, + ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER, + ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR, + ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR, + ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER, + ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER, + ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER, + ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER +}; + + +#define UDP_FORWARDER_MAXIMUM_TIMEOUT (60000 * 10) + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCoordinator.cpp b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCoordinator.cpp index 9e063cd..f2ff1a0 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCoordinator.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCoordinator.cpp @@ -1,543 +1,543 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyCoordinator==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#include "UDPProxyCoordinator.h" -#include "BitStream.h" -#include "UDPProxyCommon.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "Rand.h" -#include "GetTime.h" -#include "UDPForwarder.h" - -// Larger than the client version -static const int DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME=2000; -static const int DEFAULT_UNRESPONSIVE_PING_TIME=DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME+1000; - -using namespace RakNet; - -// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() < cls.ping;} -// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() > cls.ping;} -// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() == cls.ping;} - -int UDPProxyCoordinator::ServerWithPingComp( const unsigned short &key, const UDPProxyCoordinator::ServerWithPing &data ) -{ - if (key < data.ping) - return -1; - if (key > data.ping) - return 1; - return 0; -} - -int UDPProxyCoordinator::ForwardingRequestComp( const SenderAndTargetAddress &key, ForwardingRequest* const &data) -{ - if (key.senderClientAddress < data->sata.senderClientAddress ) - return -1; - if (key.senderClientAddress > data->sata.senderClientAddress ) - return -1; - if (key.targetClientAddress < data->sata.targetClientAddress ) - return -1; - if (key.targetClientAddress > data->sata.targetClientAddress ) - return 1; - return 0; -} -// -// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) -// { -// return inputKey.Get().senderClientAddress < cls->sata.senderClientAddress || -// (inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress < cls->sata.targetClientAddress); -// } -// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) -// { -// return inputKey.Get().senderClientAddress > cls->sata.senderClientAddress || -// (inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress > cls->sata.targetClientAddress); -// } -// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) -// { -// return inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress == cls->sata.targetClientAddress; -// } - -STATIC_FACTORY_DEFINITIONS(UDPProxyCoordinator,UDPProxyCoordinator); - -UDPProxyCoordinator::UDPProxyCoordinator() -{ - -} -UDPProxyCoordinator::~UDPProxyCoordinator() -{ - Clear(); -} -void UDPProxyCoordinator::SetRemoteLoginPassword(RakNet::RakString password) -{ - remoteLoginPassword=password; -} -void UDPProxyCoordinator::Update(void) -{ - unsigned int idx; - RakNet::TimeMS curTime = RakNet::GetTimeMS(); - ForwardingRequest *fw; - idx=0; - while (idx < forwardingRequestList.Size()) - { - fw=forwardingRequestList[idx]; - if (fw->timeRequestedPings!=0 && - curTime > fw->timeRequestedPings + DEFAULT_UNRESPONSIVE_PING_TIME) - { - fw->OrderRemainingServersToTry(); - fw->timeRequestedPings=0; - TryNextServer(fw->sata, fw); - idx++; - } - else if (fw->timeoutAfterSuccess!=0 && - curTime > fw->timeoutAfterSuccess) - { - // Forwarding request succeeded, we waited a bit to prevent duplicates. Can forget about the entry now. - RakNet::OP_DELETE(fw,_FILE_AND_LINE_); - forwardingRequestList.RemoveAtIndex(idx); - } - else - idx++; - } -} -PluginReceiveResult UDPProxyCoordinator::OnReceive(Packet *packet) -{ - if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) - { - switch (packet->data[1]) - { - case ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR: - OnForwardingRequestFromClientToCoordinator(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR: - OnLoginRequestFromServerToCoordinator(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR: - OnForwardingReplyFromServerToCoordinator(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR: - OnPingServersReplyFromClientToCoordinator(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - return RR_CONTINUE_PROCESSING; -} -void UDPProxyCoordinator::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - unsigned int idx, idx2; - - idx=0; - while (idx < forwardingRequestList.Size()) - { - if (forwardingRequestList[idx]->requestingAddress==systemAddress) - { - // Guy disconnected before the attempt completed - RakNet::OP_DELETE(forwardingRequestList[idx], _FILE_AND_LINE_); - forwardingRequestList.RemoveAtIndex(idx ); - } - else - idx++; - } - - idx = serverList.GetIndexOf(systemAddress); - if (idx!=(unsigned int)-1) - { - ForwardingRequest *fw; - // For each pending client for this server, choose from remaining servers. - for (idx2=0; idx2 < forwardingRequestList.Size(); idx2++) - { - fw = forwardingRequestList[idx2]; - if (fw->currentlyAttemptedServerAddress==systemAddress) - { - // Try the next server - TryNextServer(fw->sata, fw); - } - } - - // Remove dead server - serverList.RemoveAtIndexFast(idx); - } -} -void UDPProxyCoordinator::OnForwardingRequestFromClientToCoordinator(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - SystemAddress sourceAddress; - incomingBs.Read(sourceAddress); - if (sourceAddress==UNASSIGNED_SYSTEM_ADDRESS) - sourceAddress=packet->systemAddress; - SystemAddress targetAddress; - RakNetGUID targetGuid; - bool usesAddress=false; - incomingBs.Read(usesAddress); - if (usesAddress) - { - incomingBs.Read(targetAddress); - targetGuid=rakPeerInterface->GetGuidFromSystemAddress(targetAddress); - } - else - { - incomingBs.Read(targetGuid); - targetAddress=rakPeerInterface->GetSystemAddressFromGuid(targetGuid); - } - ForwardingRequest *fw = RakNet::OP_NEW(_FILE_AND_LINE_); - fw->timeoutAfterSuccess=0; - incomingBs.Read(fw->timeoutOnNoDataMS); - bool hasServerSelectionBitstream=false; - incomingBs.Read(hasServerSelectionBitstream); - if (hasServerSelectionBitstream) - incomingBs.Read(&(fw->serverSelectionBitstream)); - - RakNet::BitStream outgoingBs; - SenderAndTargetAddress sata; - sata.senderClientAddress=sourceAddress; - sata.targetClientAddress=targetAddress; - sata.targetClientGuid=targetGuid; - sata.senderClientGuid=rakPeerInterface->GetGuidFromSystemAddress(sourceAddress); - SenderAndTargetAddress sataReversed; - sataReversed.senderClientAddress=targetAddress; - sataReversed.targetClientAddress=sourceAddress; - sataReversed.senderClientGuid=sata.targetClientGuid; - sataReversed.targetClientGuid=sata.senderClientGuid; - - unsigned int insertionIndex; - bool objectExists1, objectExists2; - insertionIndex=forwardingRequestList.GetIndexFromKey(sata, &objectExists1); - forwardingRequestList.GetIndexFromKey(sataReversed, &objectExists2); - - if (objectExists1 || objectExists2) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_IN_PROGRESS); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(targetGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - RakNet::OP_DELETE(fw, _FILE_AND_LINE_); - return; - } - - if (serverList.Size()==0) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_NO_SERVERS_ONLINE); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(targetGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - RakNet::OP_DELETE(fw, _FILE_AND_LINE_); - return; - } - - if (rakPeerInterface->GetConnectionState(targetAddress)!=IS_CONNECTED && usesAddress==false) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(targetGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - RakNet::OP_DELETE(fw, _FILE_AND_LINE_); - return; - } - - fw->sata=sata; - fw->requestingAddress=packet->systemAddress; - - if (serverList.Size()>1) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(targetGuid); - unsigned short serverListSize = (unsigned short) serverList.Size(); - outgoingBs.Write(serverListSize); - unsigned int idx; - for (idx=0; idx < serverList.Size(); idx++) - outgoingBs.Write(serverList[idx]); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, sourceAddress, false); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, targetAddress, false); - fw->timeRequestedPings=RakNet::GetTimeMS(); - unsigned int copyIndex; - for (copyIndex=0; copyIndex < serverList.Size(); copyIndex++) - fw->remainingServersToTry.Push(serverList[copyIndex], _FILE_AND_LINE_ ); - forwardingRequestList.InsertAtIndex(fw, insertionIndex, _FILE_AND_LINE_ ); - } - else - { - fw->timeRequestedPings=0; - fw->currentlyAttemptedServerAddress=serverList[0]; - forwardingRequestList.InsertAtIndex(fw, insertionIndex, _FILE_AND_LINE_ ); - SendForwardingRequest(sourceAddress, targetAddress, fw->currentlyAttemptedServerAddress, fw->timeoutOnNoDataMS); - } -} - -void UDPProxyCoordinator::SendForwardingRequest(SystemAddress sourceAddress, SystemAddress targetAddress, SystemAddress serverAddress, RakNet::TimeMS timeoutOnNoDataMS) -{ - RakNet::BitStream outgoingBs; - // Send request to desired server - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(timeoutOnNoDataMS); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, serverAddress, false); -} -void UDPProxyCoordinator::OnLoginRequestFromServerToCoordinator(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - RakNet::RakString password; - incomingBs.Read(password); - RakNet::BitStream outgoingBs; - - if (remoteLoginPassword.IsEmpty()) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - return; - } - - if (remoteLoginPassword!=password) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - return; - } - - unsigned int insertionIndex; - insertionIndex=serverList.GetIndexOf(packet->systemAddress); - if (insertionIndex!=(unsigned int)-1) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - return; - } - serverList.Push(packet->systemAddress, _FILE_AND_LINE_ ); - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); -} -void UDPProxyCoordinator::OnForwardingReplyFromServerToCoordinator(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - SenderAndTargetAddress sata; - incomingBs.Read(sata.senderClientAddress); - incomingBs.Read(sata.targetClientAddress); - bool objectExists; - unsigned int index = forwardingRequestList.GetIndexFromKey(sata, &objectExists); - if (objectExists==false) - { - // The guy disconnected before the request finished - return; - } - ForwardingRequest *fw = forwardingRequestList[index]; - sata.senderClientGuid = fw->sata.senderClientGuid; - sata.targetClientGuid = fw->sata.targetClientGuid; - - UDPForwarderResult success; - unsigned char c; - incomingBs.Read(c); - success=(UDPForwarderResult)c; - - RakNet::BitStream outgoingBs; - if (success==UDPFORWARDER_SUCCESS) - { - char serverIP[64]; - packet->systemAddress.ToString(false,serverIP); - unsigned short forwardingPort; - incomingBs.Read(forwardingPort); - - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_SUCCEEDED); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(sata.targetClientAddress); - outgoingBs.Write(sata.targetClientGuid); - outgoingBs.Write(RakNet::RakString(serverIP)); - outgoingBs.Write(forwardingPort); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, fw->requestingAddress, false); - - outgoingBs.Reset(); - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_NOTIFICATION); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(sata.targetClientAddress); - outgoingBs.Write(sata.targetClientGuid); - outgoingBs.Write(RakNet::RakString(serverIP)); - outgoingBs.Write(forwardingPort); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, sata.targetClientAddress, false); - - // 05/18/09 Keep the entry around for some time after success, so duplicates are reported if attempting forwarding from the target system before notification of success - fw->timeoutAfterSuccess=RakNet::GetTimeMS()+fw->timeoutOnNoDataMS; - // forwardingRequestList.RemoveAtIndex(index); - // RakNet::OP_DELETE(fw,_FILE_AND_LINE_); - - return; - } - else if (success==UDPFORWARDER_NO_SOCKETS) - { - // Try next server - TryNextServer(sata, fw); - } - else - { - RakAssert(success==UDPFORWARDER_FORWARDING_ALREADY_EXISTS); - - // Return in progress - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_IN_PROGRESS); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(sata.targetClientAddress); - outgoingBs.Write(sata.targetClientGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, fw->requestingAddress, false); - forwardingRequestList.RemoveAtIndex(index); - RakNet::OP_DELETE(fw,_FILE_AND_LINE_); - } -} -void UDPProxyCoordinator::OnPingServersReplyFromClientToCoordinator(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - unsigned short serversToPingSize; - SystemAddress serverAddress; - SenderAndTargetAddress sata; - incomingBs.Read(sata.senderClientAddress); - incomingBs.Read(sata.targetClientAddress); - bool objectExists; - unsigned int index = forwardingRequestList.GetIndexFromKey(sata, &objectExists); - if (objectExists==false) - return; - unsigned short idx; - ServerWithPing swp; - ForwardingRequest *fw = forwardingRequestList[index]; - if (fw->timeRequestedPings==0) - return; - - incomingBs.Read(serversToPingSize); - if (packet->systemAddress==sata.senderClientAddress) - { - for (idx=0; idx < serversToPingSize; idx++) - { - incomingBs.Read(swp.serverAddress); - incomingBs.Read(swp.ping); - unsigned int index2; - for (index2=0; index2 < fw->sourceServerPings.Size(); index2++) - { - if (fw->sourceServerPings[index2].ping >= swp.ping ) - break; - } - fw->sourceServerPings.Insert(swp, index2, _FILE_AND_LINE_); - } - } - else - { - for (idx=0; idx < serversToPingSize; idx++) - { - incomingBs.Read(swp.serverAddress); - incomingBs.Read(swp.ping); - - unsigned int index2; - for (index2=0; index2 < fw->targetServerPings.Size(); index2++) - { - if (fw->targetServerPings[index2].ping >= swp.ping ) - break; - } - fw->sourceServerPings.Insert(swp, index2, _FILE_AND_LINE_); - } - } - - // Both systems have to give us pings to progress here. Otherwise will timeout in Update() - if (fw->sourceServerPings.Size()>0 && - fw->targetServerPings.Size()>0) - { - fw->OrderRemainingServersToTry(); - fw->timeRequestedPings=0; - TryNextServer(fw->sata, fw); - } -} -void UDPProxyCoordinator::TryNextServer(SenderAndTargetAddress sata, ForwardingRequest *fw) -{ - bool pickedGoodServer=false; - while(fw->remainingServersToTry.Size()>0) - { - fw->currentlyAttemptedServerAddress=fw->remainingServersToTry.Pop(); - if (serverList.GetIndexOf(fw->currentlyAttemptedServerAddress)!=(unsigned int)-1) - { - pickedGoodServer=true; - break; - } - } - - if (pickedGoodServer==false) - { - SendAllBusy(sata.senderClientAddress, sata.targetClientAddress, sata.targetClientGuid, fw->requestingAddress); - forwardingRequestList.Remove(sata); - RakNet::OP_DELETE(fw,_FILE_AND_LINE_); - return; - } - - SendForwardingRequest(sata.senderClientAddress, sata.targetClientAddress, fw->currentlyAttemptedServerAddress, fw->timeoutOnNoDataMS); -} -void UDPProxyCoordinator::SendAllBusy(SystemAddress senderClientAddress, SystemAddress targetClientAddress, RakNetGUID targetClientGuid, SystemAddress requestingAddress) -{ - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_ALL_SERVERS_BUSY); - outgoingBs.Write(senderClientAddress); - outgoingBs.Write(targetClientAddress); - outgoingBs.Write(targetClientGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, requestingAddress, false); -} -void UDPProxyCoordinator::Clear(void) -{ - serverList.Clear(true, _FILE_AND_LINE_); - for (unsigned int i=0; i < forwardingRequestList.Size(); i++) - { - RakNet::OP_DELETE(forwardingRequestList[i],_FILE_AND_LINE_); - } - forwardingRequestList.Clear(false, _FILE_AND_LINE_); -} -void UDPProxyCoordinator::ForwardingRequest::OrderRemainingServersToTry(void) -{ - //DataStructures::Multilist swpList; - DataStructures::OrderedList swpList; - // swpList.SetSortOrder(true); - - if (sourceServerPings.Size()==0 && targetServerPings.Size()==0) - return; - - unsigned int idx; - UDPProxyCoordinator::ServerWithPing swp; - for (idx=0; idx < remainingServersToTry.Size(); idx++) - { - swp.serverAddress=remainingServersToTry[idx]; - swp.ping=0; - if (sourceServerPings.Size()) - swp.ping+=(unsigned short) (sourceServerPings[idx].ping); - else - swp.ping+=(unsigned short) (DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME); - if (targetServerPings.Size()) - swp.ping+=(unsigned short) (targetServerPings[idx].ping); - else - swp.ping+=(unsigned short) (DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME); - swpList.Insert(swp.ping, swp, false, _FILE_AND_LINE_); - } - remainingServersToTry.Clear(_FILE_AND_LINE_ ); - for (idx=0; idx < swpList.Size(); idx++) - { - remainingServersToTry.Push(swpList[idx].serverAddress, _FILE_AND_LINE_ ); - } -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyCoordinator==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#include "UDPProxyCoordinator.h" +#include "BitStream.h" +#include "UDPProxyCommon.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "Rand.h" +#include "GetTime.h" +#include "UDPForwarder.h" + +// Larger than the client version +static const int DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME=2000; +static const int DEFAULT_UNRESPONSIVE_PING_TIME=DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME+1000; + +using namespace RakNet; + +// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() < cls.ping;} +// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() > cls.ping;} +// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() == cls.ping;} + +int UDPProxyCoordinator::ServerWithPingComp( const unsigned short &key, const UDPProxyCoordinator::ServerWithPing &data ) +{ + if (key < data.ping) + return -1; + if (key > data.ping) + return 1; + return 0; +} + +int UDPProxyCoordinator::ForwardingRequestComp( const SenderAndTargetAddress &key, ForwardingRequest* const &data) +{ + if (key.senderClientAddress < data->sata.senderClientAddress ) + return -1; + if (key.senderClientAddress > data->sata.senderClientAddress ) + return -1; + if (key.targetClientAddress < data->sata.targetClientAddress ) + return -1; + if (key.targetClientAddress > data->sata.targetClientAddress ) + return 1; + return 0; +} +// +// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) +// { +// return inputKey.Get().senderClientAddress < cls->sata.senderClientAddress || +// (inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress < cls->sata.targetClientAddress); +// } +// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) +// { +// return inputKey.Get().senderClientAddress > cls->sata.senderClientAddress || +// (inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress > cls->sata.targetClientAddress); +// } +// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) +// { +// return inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress == cls->sata.targetClientAddress; +// } + +STATIC_FACTORY_DEFINITIONS(UDPProxyCoordinator,UDPProxyCoordinator); + +UDPProxyCoordinator::UDPProxyCoordinator() +{ + +} +UDPProxyCoordinator::~UDPProxyCoordinator() +{ + Clear(); +} +void UDPProxyCoordinator::SetRemoteLoginPassword(RakNet::RakString password) +{ + remoteLoginPassword=password; +} +void UDPProxyCoordinator::Update(void) +{ + unsigned int idx; + RakNet::TimeMS curTime = RakNet::GetTimeMS(); + ForwardingRequest *fw; + idx=0; + while (idx < forwardingRequestList.Size()) + { + fw=forwardingRequestList[idx]; + if (fw->timeRequestedPings!=0 && + curTime > fw->timeRequestedPings + DEFAULT_UNRESPONSIVE_PING_TIME) + { + fw->OrderRemainingServersToTry(); + fw->timeRequestedPings=0; + TryNextServer(fw->sata, fw); + idx++; + } + else if (fw->timeoutAfterSuccess!=0 && + curTime > fw->timeoutAfterSuccess) + { + // Forwarding request succeeded, we waited a bit to prevent duplicates. Can forget about the entry now. + RakNet::OP_DELETE(fw,_FILE_AND_LINE_); + forwardingRequestList.RemoveAtIndex(idx); + } + else + idx++; + } +} +PluginReceiveResult UDPProxyCoordinator::OnReceive(Packet *packet) +{ + if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) + { + switch (packet->data[1]) + { + case ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR: + OnForwardingRequestFromClientToCoordinator(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR: + OnLoginRequestFromServerToCoordinator(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR: + OnForwardingReplyFromServerToCoordinator(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR: + OnPingServersReplyFromClientToCoordinator(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + return RR_CONTINUE_PROCESSING; +} +void UDPProxyCoordinator::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + unsigned int idx, idx2; + + idx=0; + while (idx < forwardingRequestList.Size()) + { + if (forwardingRequestList[idx]->requestingAddress==systemAddress) + { + // Guy disconnected before the attempt completed + RakNet::OP_DELETE(forwardingRequestList[idx], _FILE_AND_LINE_); + forwardingRequestList.RemoveAtIndex(idx ); + } + else + idx++; + } + + idx = serverList.GetIndexOf(systemAddress); + if (idx!=(unsigned int)-1) + { + ForwardingRequest *fw; + // For each pending client for this server, choose from remaining servers. + for (idx2=0; idx2 < forwardingRequestList.Size(); idx2++) + { + fw = forwardingRequestList[idx2]; + if (fw->currentlyAttemptedServerAddress==systemAddress) + { + // Try the next server + TryNextServer(fw->sata, fw); + } + } + + // Remove dead server + serverList.RemoveAtIndexFast(idx); + } +} +void UDPProxyCoordinator::OnForwardingRequestFromClientToCoordinator(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + SystemAddress sourceAddress; + incomingBs.Read(sourceAddress); + if (sourceAddress==UNASSIGNED_SYSTEM_ADDRESS) + sourceAddress=packet->systemAddress; + SystemAddress targetAddress; + RakNetGUID targetGuid; + bool usesAddress=false; + incomingBs.Read(usesAddress); + if (usesAddress) + { + incomingBs.Read(targetAddress); + targetGuid=rakPeerInterface->GetGuidFromSystemAddress(targetAddress); + } + else + { + incomingBs.Read(targetGuid); + targetAddress=rakPeerInterface->GetSystemAddressFromGuid(targetGuid); + } + ForwardingRequest *fw = RakNet::OP_NEW(_FILE_AND_LINE_); + fw->timeoutAfterSuccess=0; + incomingBs.Read(fw->timeoutOnNoDataMS); + bool hasServerSelectionBitstream=false; + incomingBs.Read(hasServerSelectionBitstream); + if (hasServerSelectionBitstream) + incomingBs.Read(&(fw->serverSelectionBitstream)); + + RakNet::BitStream outgoingBs; + SenderAndTargetAddress sata; + sata.senderClientAddress=sourceAddress; + sata.targetClientAddress=targetAddress; + sata.targetClientGuid=targetGuid; + sata.senderClientGuid=rakPeerInterface->GetGuidFromSystemAddress(sourceAddress); + SenderAndTargetAddress sataReversed; + sataReversed.senderClientAddress=targetAddress; + sataReversed.targetClientAddress=sourceAddress; + sataReversed.senderClientGuid=sata.targetClientGuid; + sataReversed.targetClientGuid=sata.senderClientGuid; + + unsigned int insertionIndex; + bool objectExists1, objectExists2; + insertionIndex=forwardingRequestList.GetIndexFromKey(sata, &objectExists1); + forwardingRequestList.GetIndexFromKey(sataReversed, &objectExists2); + + if (objectExists1 || objectExists2) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_IN_PROGRESS); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(targetGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + RakNet::OP_DELETE(fw, _FILE_AND_LINE_); + return; + } + + if (serverList.Size()==0) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_NO_SERVERS_ONLINE); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(targetGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + RakNet::OP_DELETE(fw, _FILE_AND_LINE_); + return; + } + + if (rakPeerInterface->GetConnectionState(targetAddress)!=IS_CONNECTED && usesAddress==false) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(targetGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + RakNet::OP_DELETE(fw, _FILE_AND_LINE_); + return; + } + + fw->sata=sata; + fw->requestingAddress=packet->systemAddress; + + if (serverList.Size()>1) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(targetGuid); + unsigned short serverListSize = (unsigned short) serverList.Size(); + outgoingBs.Write(serverListSize); + unsigned int idx; + for (idx=0; idx < serverList.Size(); idx++) + outgoingBs.Write(serverList[idx]); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, sourceAddress, false); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, targetAddress, false); + fw->timeRequestedPings=RakNet::GetTimeMS(); + unsigned int copyIndex; + for (copyIndex=0; copyIndex < serverList.Size(); copyIndex++) + fw->remainingServersToTry.Push(serverList[copyIndex], _FILE_AND_LINE_ ); + forwardingRequestList.InsertAtIndex(fw, insertionIndex, _FILE_AND_LINE_ ); + } + else + { + fw->timeRequestedPings=0; + fw->currentlyAttemptedServerAddress=serverList[0]; + forwardingRequestList.InsertAtIndex(fw, insertionIndex, _FILE_AND_LINE_ ); + SendForwardingRequest(sourceAddress, targetAddress, fw->currentlyAttemptedServerAddress, fw->timeoutOnNoDataMS); + } +} + +void UDPProxyCoordinator::SendForwardingRequest(SystemAddress sourceAddress, SystemAddress targetAddress, SystemAddress serverAddress, RakNet::TimeMS timeoutOnNoDataMS) +{ + RakNet::BitStream outgoingBs; + // Send request to desired server + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(timeoutOnNoDataMS); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, serverAddress, false); +} +void UDPProxyCoordinator::OnLoginRequestFromServerToCoordinator(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + RakNet::RakString password; + incomingBs.Read(password); + RakNet::BitStream outgoingBs; + + if (remoteLoginPassword.IsEmpty()) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + return; + } + + if (remoteLoginPassword!=password) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + return; + } + + unsigned int insertionIndex; + insertionIndex=serverList.GetIndexOf(packet->systemAddress); + if (insertionIndex!=(unsigned int)-1) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + return; + } + serverList.Push(packet->systemAddress, _FILE_AND_LINE_ ); + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); +} +void UDPProxyCoordinator::OnForwardingReplyFromServerToCoordinator(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + SenderAndTargetAddress sata; + incomingBs.Read(sata.senderClientAddress); + incomingBs.Read(sata.targetClientAddress); + bool objectExists; + unsigned int index = forwardingRequestList.GetIndexFromKey(sata, &objectExists); + if (objectExists==false) + { + // The guy disconnected before the request finished + return; + } + ForwardingRequest *fw = forwardingRequestList[index]; + sata.senderClientGuid = fw->sata.senderClientGuid; + sata.targetClientGuid = fw->sata.targetClientGuid; + + UDPForwarderResult success; + unsigned char c; + incomingBs.Read(c); + success=(UDPForwarderResult)c; + + RakNet::BitStream outgoingBs; + if (success==UDPFORWARDER_SUCCESS) + { + char serverIP[64]; + packet->systemAddress.ToString(false,serverIP); + unsigned short forwardingPort; + incomingBs.Read(forwardingPort); + + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_SUCCEEDED); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(sata.targetClientAddress); + outgoingBs.Write(sata.targetClientGuid); + outgoingBs.Write(RakNet::RakString(serverIP)); + outgoingBs.Write(forwardingPort); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, fw->requestingAddress, false); + + outgoingBs.Reset(); + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_NOTIFICATION); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(sata.targetClientAddress); + outgoingBs.Write(sata.targetClientGuid); + outgoingBs.Write(RakNet::RakString(serverIP)); + outgoingBs.Write(forwardingPort); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, sata.targetClientAddress, false); + + // 05/18/09 Keep the entry around for some time after success, so duplicates are reported if attempting forwarding from the target system before notification of success + fw->timeoutAfterSuccess=RakNet::GetTimeMS()+fw->timeoutOnNoDataMS; + // forwardingRequestList.RemoveAtIndex(index); + // RakNet::OP_DELETE(fw,_FILE_AND_LINE_); + + return; + } + else if (success==UDPFORWARDER_NO_SOCKETS) + { + // Try next server + TryNextServer(sata, fw); + } + else + { + RakAssert(success==UDPFORWARDER_FORWARDING_ALREADY_EXISTS); + + // Return in progress + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_IN_PROGRESS); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(sata.targetClientAddress); + outgoingBs.Write(sata.targetClientGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, fw->requestingAddress, false); + forwardingRequestList.RemoveAtIndex(index); + RakNet::OP_DELETE(fw,_FILE_AND_LINE_); + } +} +void UDPProxyCoordinator::OnPingServersReplyFromClientToCoordinator(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + unsigned short serversToPingSize; + SystemAddress serverAddress; + SenderAndTargetAddress sata; + incomingBs.Read(sata.senderClientAddress); + incomingBs.Read(sata.targetClientAddress); + bool objectExists; + unsigned int index = forwardingRequestList.GetIndexFromKey(sata, &objectExists); + if (objectExists==false) + return; + unsigned short idx; + ServerWithPing swp; + ForwardingRequest *fw = forwardingRequestList[index]; + if (fw->timeRequestedPings==0) + return; + + incomingBs.Read(serversToPingSize); + if (packet->systemAddress==sata.senderClientAddress) + { + for (idx=0; idx < serversToPingSize; idx++) + { + incomingBs.Read(swp.serverAddress); + incomingBs.Read(swp.ping); + unsigned int index2; + for (index2=0; index2 < fw->sourceServerPings.Size(); index2++) + { + if (fw->sourceServerPings[index2].ping >= swp.ping ) + break; + } + fw->sourceServerPings.Insert(swp, index2, _FILE_AND_LINE_); + } + } + else + { + for (idx=0; idx < serversToPingSize; idx++) + { + incomingBs.Read(swp.serverAddress); + incomingBs.Read(swp.ping); + + unsigned int index2; + for (index2=0; index2 < fw->targetServerPings.Size(); index2++) + { + if (fw->targetServerPings[index2].ping >= swp.ping ) + break; + } + fw->sourceServerPings.Insert(swp, index2, _FILE_AND_LINE_); + } + } + + // Both systems have to give us pings to progress here. Otherwise will timeout in Update() + if (fw->sourceServerPings.Size()>0 && + fw->targetServerPings.Size()>0) + { + fw->OrderRemainingServersToTry(); + fw->timeRequestedPings=0; + TryNextServer(fw->sata, fw); + } +} +void UDPProxyCoordinator::TryNextServer(SenderAndTargetAddress sata, ForwardingRequest *fw) +{ + bool pickedGoodServer=false; + while(fw->remainingServersToTry.Size()>0) + { + fw->currentlyAttemptedServerAddress=fw->remainingServersToTry.Pop(); + if (serverList.GetIndexOf(fw->currentlyAttemptedServerAddress)!=(unsigned int)-1) + { + pickedGoodServer=true; + break; + } + } + + if (pickedGoodServer==false) + { + SendAllBusy(sata.senderClientAddress, sata.targetClientAddress, sata.targetClientGuid, fw->requestingAddress); + forwardingRequestList.Remove(sata); + RakNet::OP_DELETE(fw,_FILE_AND_LINE_); + return; + } + + SendForwardingRequest(sata.senderClientAddress, sata.targetClientAddress, fw->currentlyAttemptedServerAddress, fw->timeoutOnNoDataMS); +} +void UDPProxyCoordinator::SendAllBusy(SystemAddress senderClientAddress, SystemAddress targetClientAddress, RakNetGUID targetClientGuid, SystemAddress requestingAddress) +{ + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_ALL_SERVERS_BUSY); + outgoingBs.Write(senderClientAddress); + outgoingBs.Write(targetClientAddress); + outgoingBs.Write(targetClientGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, requestingAddress, false); +} +void UDPProxyCoordinator::Clear(void) +{ + serverList.Clear(true, _FILE_AND_LINE_); + for (unsigned int i=0; i < forwardingRequestList.Size(); i++) + { + RakNet::OP_DELETE(forwardingRequestList[i],_FILE_AND_LINE_); + } + forwardingRequestList.Clear(false, _FILE_AND_LINE_); +} +void UDPProxyCoordinator::ForwardingRequest::OrderRemainingServersToTry(void) +{ + //DataStructures::Multilist swpList; + DataStructures::OrderedList swpList; + // swpList.SetSortOrder(true); + + if (sourceServerPings.Size()==0 && targetServerPings.Size()==0) + return; + + unsigned int idx; + UDPProxyCoordinator::ServerWithPing swp; + for (idx=0; idx < remainingServersToTry.Size(); idx++) + { + swp.serverAddress=remainingServersToTry[idx]; + swp.ping=0; + if (sourceServerPings.Size()) + swp.ping+=(unsigned short) (sourceServerPings[idx].ping); + else + swp.ping+=(unsigned short) (DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME); + if (targetServerPings.Size()) + swp.ping+=(unsigned short) (targetServerPings[idx].ping); + else + swp.ping+=(unsigned short) (DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME); + swpList.Insert(swp.ping, swp, false, _FILE_AND_LINE_); + } + remainingServersToTry.Clear(_FILE_AND_LINE_ ); + for (idx=0; idx < swpList.Size(); idx++) + { + remainingServersToTry.Push(swpList[idx].serverAddress, _FILE_AND_LINE_ ); + } +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCoordinator.h b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCoordinator.h index 783e061..0107346 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCoordinator.h +++ b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyCoordinator.h @@ -1,115 +1,115 @@ -/// \file -/// \brief Essentially maintains a list of servers running UDPProxyServer, and some state management for UDPProxyClient to find a free server to forward datagrams -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyCoordinator==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#ifndef __UDP_PROXY_COORDINATOR_H -#define __UDP_PROXY_COORDINATOR_H - -#include "Export.h" -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "RakString.h" -#include "BitStream.h" -#include "DS_Queue.h" -#include "DS_OrderedList.h" - -namespace RakNet -{ - /// When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa - /// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin. - /// The UDPProxyClient connects to UDPProxyCoordinator to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request - /// \brief Middleman between UDPProxyServer and UDPProxyClient, maintaining a list of UDPProxyServer, and managing state for clients to find an available forwarding server. - /// \ingroup NAT_PUNCHTHROUGH_GROUP - class RAK_DLL_EXPORT UDPProxyCoordinator : public PluginInterface2 - { - public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(UDPProxyCoordinator) - - UDPProxyCoordinator(); - virtual ~UDPProxyCoordinator(); - - /// For UDPProxyServers logging in remotely, they must pass a password to UDPProxyServer::LoginToCoordinator(). It must match the password set here. - /// If no password is set, they cannot login remotely. - /// By default, no password is set - void SetRemoteLoginPassword(RakNet::RakString password); - - /// \internal - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - struct SenderAndTargetAddress - { - SystemAddress senderClientAddress; - RakNetGUID senderClientGuid; - SystemAddress targetClientAddress; - RakNetGUID targetClientGuid; - }; - - struct ServerWithPing - { - unsigned short ping; - SystemAddress serverAddress; - }; - - struct ForwardingRequest - { - RakNet::TimeMS timeoutOnNoDataMS; - RakNet::TimeMS timeoutAfterSuccess; - SenderAndTargetAddress sata; - SystemAddress requestingAddress; // Which system originally sent the network message to start forwarding - SystemAddress currentlyAttemptedServerAddress; - DataStructures::Queue remainingServersToTry; - RakNet::BitStream serverSelectionBitstream; - - DataStructures::List sourceServerPings, targetServerPings; - RakNet::TimeMS timeRequestedPings; - // Order based on sourceServerPings and targetServerPings - void OrderRemainingServersToTry(void); - - }; - protected: - - static int ServerWithPingComp( const unsigned short &key, const UDPProxyCoordinator::ServerWithPing &data ); - static int ForwardingRequestComp( const SenderAndTargetAddress &key, ForwardingRequest* const &data); - - void OnForwardingRequestFromClientToCoordinator(Packet *packet); - void OnLoginRequestFromServerToCoordinator(Packet *packet); - void OnForwardingReplyFromServerToCoordinator(Packet *packet); - void OnPingServersReplyFromClientToCoordinator(Packet *packet); - void TryNextServer(SenderAndTargetAddress sata, ForwardingRequest *fw); - void SendAllBusy(SystemAddress senderClientAddress, SystemAddress targetClientAddress, RakNetGUID targetClientGuid, SystemAddress requestingAddress); - void Clear(void); - - void SendForwardingRequest(SystemAddress sourceAddress, SystemAddress targetAddress, SystemAddress serverAddress, RakNet::TimeMS timeoutOnNoDataMS); - - // Logged in servers - //DataStructures::Multilist serverList; - DataStructures::List serverList; - - // Forwarding requests in progress - //DataStructures::Multilist forwardingRequestList; - DataStructures::OrderedList forwardingRequestList; - - RakNet::RakString remoteLoginPassword; - - }; - -} // End namespace - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Essentially maintains a list of servers running UDPProxyServer, and some state management for UDPProxyClient to find a free server to forward datagrams +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyCoordinator==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#ifndef __UDP_PROXY_COORDINATOR_H +#define __UDP_PROXY_COORDINATOR_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "RakString.h" +#include "BitStream.h" +#include "DS_Queue.h" +#include "DS_OrderedList.h" + +namespace RakNet +{ + /// When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa + /// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin. + /// The UDPProxyClient connects to UDPProxyCoordinator to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request + /// \brief Middleman between UDPProxyServer and UDPProxyClient, maintaining a list of UDPProxyServer, and managing state for clients to find an available forwarding server. + /// \ingroup NAT_PUNCHTHROUGH_GROUP + class RAK_DLL_EXPORT UDPProxyCoordinator : public PluginInterface2 + { + public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(UDPProxyCoordinator) + + UDPProxyCoordinator(); + virtual ~UDPProxyCoordinator(); + + /// For UDPProxyServers logging in remotely, they must pass a password to UDPProxyServer::LoginToCoordinator(). It must match the password set here. + /// If no password is set, they cannot login remotely. + /// By default, no password is set + void SetRemoteLoginPassword(RakNet::RakString password); + + /// \internal + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + struct SenderAndTargetAddress + { + SystemAddress senderClientAddress; + RakNetGUID senderClientGuid; + SystemAddress targetClientAddress; + RakNetGUID targetClientGuid; + }; + + struct ServerWithPing + { + unsigned short ping; + SystemAddress serverAddress; + }; + + struct ForwardingRequest + { + RakNet::TimeMS timeoutOnNoDataMS; + RakNet::TimeMS timeoutAfterSuccess; + SenderAndTargetAddress sata; + SystemAddress requestingAddress; // Which system originally sent the network message to start forwarding + SystemAddress currentlyAttemptedServerAddress; + DataStructures::Queue remainingServersToTry; + RakNet::BitStream serverSelectionBitstream; + + DataStructures::List sourceServerPings, targetServerPings; + RakNet::TimeMS timeRequestedPings; + // Order based on sourceServerPings and targetServerPings + void OrderRemainingServersToTry(void); + + }; + protected: + + static int ServerWithPingComp( const unsigned short &key, const UDPProxyCoordinator::ServerWithPing &data ); + static int ForwardingRequestComp( const SenderAndTargetAddress &key, ForwardingRequest* const &data); + + void OnForwardingRequestFromClientToCoordinator(Packet *packet); + void OnLoginRequestFromServerToCoordinator(Packet *packet); + void OnForwardingReplyFromServerToCoordinator(Packet *packet); + void OnPingServersReplyFromClientToCoordinator(Packet *packet); + void TryNextServer(SenderAndTargetAddress sata, ForwardingRequest *fw); + void SendAllBusy(SystemAddress senderClientAddress, SystemAddress targetClientAddress, RakNetGUID targetClientGuid, SystemAddress requestingAddress); + void Clear(void); + + void SendForwardingRequest(SystemAddress sourceAddress, SystemAddress targetAddress, SystemAddress serverAddress, RakNet::TimeMS timeoutOnNoDataMS); + + // Logged in servers + //DataStructures::Multilist serverList; + DataStructures::List serverList; + + // Forwarding requests in progress + //DataStructures::Multilist forwardingRequestList; + DataStructures::OrderedList forwardingRequestList; + + RakNet::RakString remoteLoginPassword; + + }; + +} // End namespace + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyServer.cpp b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyServer.cpp index f999627..ce65db9 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyServer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyServer.cpp @@ -1,167 +1,167 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyServer==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#include "UDPProxyServer.h" -#include "BitStream.h" -#include "UDPProxyCommon.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(UDPProxyServer,UDPProxyServer); - -UDPProxyServer::UDPProxyServer() -{ - resultHandler=0; - socketFamily=AF_INET; -} -UDPProxyServer::~UDPProxyServer() -{ - -} -void UDPProxyServer::SetSocketFamily(unsigned short _socketFamily) -{ - socketFamily=_socketFamily; -} -void UDPProxyServer::SetResultHandler(UDPProxyServerResultHandler *rh) -{ - resultHandler=rh; -} -bool UDPProxyServer::LoginToCoordinator(RakNet::RakString password, SystemAddress coordinatorAddress) -{ - unsigned int insertionIndex; - bool objectExists; - insertionIndex=loggingInCoordinators.GetIndexFromKey(coordinatorAddress,&objectExists); - if (objectExists==true) - return false; - loggedInCoordinators.GetIndexFromKey(coordinatorAddress,&objectExists); - if (objectExists==true) - return false; - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, coordinatorAddress, false); - loggingInCoordinators.InsertAtIndex(coordinatorAddress, insertionIndex, _FILE_AND_LINE_ ); - return true; -} -void UDPProxyServer::Update(void) -{ - udpForwarder.Update(); -} -PluginReceiveResult UDPProxyServer::OnReceive(Packet *packet) -{ - // Make sure incoming messages from from UDPProxyCoordinator - - if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) - { - bool objectExists; - - switch (packet->data[1]) - { - case ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER: - if (loggedInCoordinators.GetIndexFromKey(packet->systemAddress, &objectExists)!=(unsigned int)-1) - { - OnForwardingRequestFromCoordinatorToServer(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - break; - case ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER: - case ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER: - case ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER: - case ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER: - { - unsigned int removalIndex = loggingInCoordinators.GetIndexFromKey(packet->systemAddress, &objectExists); - if (objectExists) - { - loggingInCoordinators.RemoveAtIndex(removalIndex); - - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - RakNet::RakString password; - incomingBs.Read(password); - switch (packet->data[1]) - { - case ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER: - if (resultHandler) - resultHandler->OnNoPasswordSet(password, this); - break; - case ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER: - if (resultHandler) - resultHandler->OnWrongPassword(password, this); - break; - case ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER: - if (resultHandler) - resultHandler->OnAlreadyLoggedIn(password, this); - break; - case ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER: - // RakAssert(loggedInCoordinators.GetIndexOf(packet->systemAddress)==(unsigned int)-1); - loggedInCoordinators.Insert(packet->systemAddress, packet->systemAddress, true, _FILE_AND_LINE_); - if (resultHandler) - resultHandler->OnLoginSuccess(password, this); - break; - } - } - - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - } - return RR_CONTINUE_PROCESSING; -} -void UDPProxyServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - loggingInCoordinators.RemoveIfExists(systemAddress); - loggedInCoordinators.RemoveIfExists(systemAddress); -} -void UDPProxyServer::OnRakPeerStartup(void) -{ - udpForwarder.Startup(); -} -void UDPProxyServer::OnRakPeerShutdown(void) -{ - udpForwarder.Shutdown(); - loggingInCoordinators.Clear(true,_FILE_AND_LINE_); - loggedInCoordinators.Clear(true,_FILE_AND_LINE_); -} -void UDPProxyServer::OnAttach(void) -{ - if (rakPeerInterface->IsActive()) - OnRakPeerStartup(); -} -void UDPProxyServer::OnDetach(void) -{ - OnRakPeerShutdown(); -} -void UDPProxyServer::OnForwardingRequestFromCoordinatorToServer(Packet *packet) -{ - SystemAddress sourceAddress, targetAddress; - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - incomingBs.Read(sourceAddress); - incomingBs.Read(targetAddress); - RakNet::TimeMS timeoutOnNoDataMS; - incomingBs.Read(timeoutOnNoDataMS); - RakAssert(timeoutOnNoDataMS > 0 && timeoutOnNoDataMS <= UDP_FORWARDER_MAXIMUM_TIMEOUT); - - unsigned short forwardingPort; - UDPForwarderResult success = udpForwarder.StartForwarding(sourceAddress, targetAddress, timeoutOnNoDataMS, 0, socketFamily, &forwardingPort, 0); - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write((unsigned char) success); - if (success==UDPFORWARDER_SUCCESS) - { - outgoingBs.Write(forwardingPort); - } - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyServer==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#include "UDPProxyServer.h" +#include "BitStream.h" +#include "UDPProxyCommon.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(UDPProxyServer,UDPProxyServer); + +UDPProxyServer::UDPProxyServer() +{ + resultHandler=0; + socketFamily=AF_INET; +} +UDPProxyServer::~UDPProxyServer() +{ + +} +void UDPProxyServer::SetSocketFamily(unsigned short _socketFamily) +{ + socketFamily=_socketFamily; +} +void UDPProxyServer::SetResultHandler(UDPProxyServerResultHandler *rh) +{ + resultHandler=rh; +} +bool UDPProxyServer::LoginToCoordinator(RakNet::RakString password, SystemAddress coordinatorAddress) +{ + unsigned int insertionIndex; + bool objectExists; + insertionIndex=loggingInCoordinators.GetIndexFromKey(coordinatorAddress,&objectExists); + if (objectExists==true) + return false; + loggedInCoordinators.GetIndexFromKey(coordinatorAddress,&objectExists); + if (objectExists==true) + return false; + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, coordinatorAddress, false); + loggingInCoordinators.InsertAtIndex(coordinatorAddress, insertionIndex, _FILE_AND_LINE_ ); + return true; +} +void UDPProxyServer::Update(void) +{ + udpForwarder.Update(); +} +PluginReceiveResult UDPProxyServer::OnReceive(Packet *packet) +{ + // Make sure incoming messages from from UDPProxyCoordinator + + if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) + { + bool objectExists; + + switch (packet->data[1]) + { + case ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER: + if (loggedInCoordinators.GetIndexFromKey(packet->systemAddress, &objectExists)!=(unsigned int)-1) + { + OnForwardingRequestFromCoordinatorToServer(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + break; + case ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER: + case ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER: + case ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER: + case ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER: + { + unsigned int removalIndex = loggingInCoordinators.GetIndexFromKey(packet->systemAddress, &objectExists); + if (objectExists) + { + loggingInCoordinators.RemoveAtIndex(removalIndex); + + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + RakNet::RakString password; + incomingBs.Read(password); + switch (packet->data[1]) + { + case ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER: + if (resultHandler) + resultHandler->OnNoPasswordSet(password, this); + break; + case ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER: + if (resultHandler) + resultHandler->OnWrongPassword(password, this); + break; + case ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER: + if (resultHandler) + resultHandler->OnAlreadyLoggedIn(password, this); + break; + case ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER: + // RakAssert(loggedInCoordinators.GetIndexOf(packet->systemAddress)==(unsigned int)-1); + loggedInCoordinators.Insert(packet->systemAddress, packet->systemAddress, true, _FILE_AND_LINE_); + if (resultHandler) + resultHandler->OnLoginSuccess(password, this); + break; + } + } + + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + } + return RR_CONTINUE_PROCESSING; +} +void UDPProxyServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + loggingInCoordinators.RemoveIfExists(systemAddress); + loggedInCoordinators.RemoveIfExists(systemAddress); +} +void UDPProxyServer::OnRakPeerStartup(void) +{ + udpForwarder.Startup(); +} +void UDPProxyServer::OnRakPeerShutdown(void) +{ + udpForwarder.Shutdown(); + loggingInCoordinators.Clear(true,_FILE_AND_LINE_); + loggedInCoordinators.Clear(true,_FILE_AND_LINE_); +} +void UDPProxyServer::OnAttach(void) +{ + if (rakPeerInterface->IsActive()) + OnRakPeerStartup(); +} +void UDPProxyServer::OnDetach(void) +{ + OnRakPeerShutdown(); +} +void UDPProxyServer::OnForwardingRequestFromCoordinatorToServer(Packet *packet) +{ + SystemAddress sourceAddress, targetAddress; + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + incomingBs.Read(sourceAddress); + incomingBs.Read(targetAddress); + RakNet::TimeMS timeoutOnNoDataMS; + incomingBs.Read(timeoutOnNoDataMS); + RakAssert(timeoutOnNoDataMS > 0 && timeoutOnNoDataMS <= UDP_FORWARDER_MAXIMUM_TIMEOUT); + + unsigned short forwardingPort; + UDPForwarderResult success = udpForwarder.StartForwarding(sourceAddress, targetAddress, timeoutOnNoDataMS, 0, socketFamily, &forwardingPort, 0); + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write((unsigned char) success); + if (success==UDPFORWARDER_SUCCESS) + { + outgoingBs.Write(forwardingPort); + } + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); +} + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyServer.h b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyServer.h index d3b776e..1678e6c 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/UDPProxyServer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/UDPProxyServer.h @@ -1,118 +1,118 @@ -/// \file -/// \brief A RakNet plugin performing networking to communicate with UDPProxyServer. It allows UDPProxyServer to control our instance of UDPForwarder. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyServer==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#ifndef __UDP_PROXY_SERVER_H -#define __UDP_PROXY_SERVER_H - -#include "Export.h" -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "UDPForwarder.h" -#include "RakString.h" - -namespace RakNet -{ -class UDPProxyServer; - -/// Callback to handle results of calling UDPProxyServer::LoginToCoordinator() -/// \ingroup UDP_PROXY_GROUP -struct UDPProxyServerResultHandler -{ - UDPProxyServerResultHandler() {} - virtual ~UDPProxyServerResultHandler() {} - - /// Called when our login succeeds - /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() - /// \param[out] proxyServer The plugin calling this callback - virtual void OnLoginSuccess(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; - - /// We are already logged in. - /// This login failed, but the system is operational as if it succeeded - /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() - /// \param[out] proxyServer The plugin calling this callback - virtual void OnAlreadyLoggedIn(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; - - /// The coordinator operator forgot to call UDPProxyCoordinator::SetRemoteLoginPassword() - /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() - /// \param[out] proxyServer The plugin calling this callback - virtual void OnNoPasswordSet(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; - - /// The coordinator operator set a different password in UDPProxyCoordinator::SetRemoteLoginPassword() than what we passed - /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() - /// \param[out] proxyServer The plugin calling this callback - virtual void OnWrongPassword(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; -}; - -/// \brief UDPProxyServer to control our instance of UDPForwarder -/// \details When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa.
      -/// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin.
      -/// The UDPProxyServer connects to UDPProxyServer to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request. -/// \ingroup UDP_PROXY_GROUP -class RAK_DLL_EXPORT UDPProxyServer : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(UDPProxyServer) - - UDPProxyServer(); - ~UDPProxyServer(); - - /// Sets the socket family to use, either IPV4 or IPV6 - /// \param[in] socketFamily For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - void SetSocketFamily(unsigned short _socketFamily); - - /// Receives the results of calling LoginToCoordinator() - /// Set before calling LoginToCoordinator or you won't know what happened - /// \param[in] resultHandler - void SetResultHandler(UDPProxyServerResultHandler *rh); - - /// Before the coordinator will register the UDPProxyServer, you must login - /// \pre Must be connected to the coordinator - /// \pre Coordinator must have set a password with UDPProxyCoordinator::SetRemoteLoginPassword() - /// \returns false if already logged in, or logging in. Returns true otherwise - bool LoginToCoordinator(RakNet::RakString password, SystemAddress coordinatorAddress); - - /// Operative class that performs the forwarding - /// Exposed so you can call UDPForwarder::SetMaxForwardEntries() if you want to change away from the default - /// UDPForwarder::Startup(), UDPForwarder::Shutdown(), and UDPForwarder::Update() are called automatically by the plugin - UDPForwarder udpForwarder; - - virtual void OnAttach(void); - virtual void OnDetach(void); - - /// \internal - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnRakPeerStartup(void); - virtual void OnRakPeerShutdown(void); - -protected: - void OnForwardingRequestFromCoordinatorToServer(Packet *packet); - - DataStructures::OrderedList loggingInCoordinators; - DataStructures::OrderedList loggedInCoordinators; - - UDPProxyServerResultHandler *resultHandler; - unsigned short socketFamily; - -}; - -} // End namespace - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief A RakNet plugin performing networking to communicate with UDPProxyServer. It allows UDPProxyServer to control our instance of UDPForwarder. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyServer==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#ifndef __UDP_PROXY_SERVER_H +#define __UDP_PROXY_SERVER_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "UDPForwarder.h" +#include "RakString.h" + +namespace RakNet +{ +class UDPProxyServer; + +/// Callback to handle results of calling UDPProxyServer::LoginToCoordinator() +/// \ingroup UDP_PROXY_GROUP +struct UDPProxyServerResultHandler +{ + UDPProxyServerResultHandler() {} + virtual ~UDPProxyServerResultHandler() {} + + /// Called when our login succeeds + /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() + /// \param[out] proxyServer The plugin calling this callback + virtual void OnLoginSuccess(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; + + /// We are already logged in. + /// This login failed, but the system is operational as if it succeeded + /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() + /// \param[out] proxyServer The plugin calling this callback + virtual void OnAlreadyLoggedIn(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; + + /// The coordinator operator forgot to call UDPProxyCoordinator::SetRemoteLoginPassword() + /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() + /// \param[out] proxyServer The plugin calling this callback + virtual void OnNoPasswordSet(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; + + /// The coordinator operator set a different password in UDPProxyCoordinator::SetRemoteLoginPassword() than what we passed + /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() + /// \param[out] proxyServer The plugin calling this callback + virtual void OnWrongPassword(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; +}; + +/// \brief UDPProxyServer to control our instance of UDPForwarder +/// \details When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa.
      +/// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin.
      +/// The UDPProxyServer connects to UDPProxyServer to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request. +/// \ingroup UDP_PROXY_GROUP +class RAK_DLL_EXPORT UDPProxyServer : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(UDPProxyServer) + + UDPProxyServer(); + ~UDPProxyServer(); + + /// Sets the socket family to use, either IPV4 or IPV6 + /// \param[in] socketFamily For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + void SetSocketFamily(unsigned short _socketFamily); + + /// Receives the results of calling LoginToCoordinator() + /// Set before calling LoginToCoordinator or you won't know what happened + /// \param[in] resultHandler + void SetResultHandler(UDPProxyServerResultHandler *rh); + + /// Before the coordinator will register the UDPProxyServer, you must login + /// \pre Must be connected to the coordinator + /// \pre Coordinator must have set a password with UDPProxyCoordinator::SetRemoteLoginPassword() + /// \returns false if already logged in, or logging in. Returns true otherwise + bool LoginToCoordinator(RakNet::RakString password, SystemAddress coordinatorAddress); + + /// Operative class that performs the forwarding + /// Exposed so you can call UDPForwarder::SetMaxForwardEntries() if you want to change away from the default + /// UDPForwarder::Startup(), UDPForwarder::Shutdown(), and UDPForwarder::Update() are called automatically by the plugin + UDPForwarder udpForwarder; + + virtual void OnAttach(void); + virtual void OnDetach(void); + + /// \internal + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnRakPeerStartup(void); + virtual void OnRakPeerShutdown(void); + +protected: + void OnForwardingRequestFromCoordinatorToServer(Packet *packet); + + DataStructures::OrderedList loggingInCoordinators; + DataStructures::OrderedList loggedInCoordinators; + + UDPProxyServerResultHandler *resultHandler; + unsigned short socketFamily; + +}; + +} // End namespace + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/project/lib_projects/raknet/jni/RaknetSources/VariableDeltaSerializer.cpp b/project/lib_projects/raknet/jni/RaknetSources/VariableDeltaSerializer.cpp index 92ff24c..044819f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/VariableDeltaSerializer.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/VariableDeltaSerializer.cpp @@ -1,275 +1,275 @@ -#include "VariableDeltaSerializer.h" - -using namespace RakNet; - -VariableDeltaSerializer::VariableDeltaSerializer() {didComparisonThisTick=false;} -VariableDeltaSerializer::~VariableDeltaSerializer() {RemoveRemoteSystemVariableHistory();} - -VariableDeltaSerializer::SerializationContext::SerializationContext() {variableHistoryIdentical=0; variableHistoryUnique=0;} -VariableDeltaSerializer::SerializationContext::~SerializationContext() {} - -void VariableDeltaSerializer::OnMessageReceipt(RakNetGUID guid, uint32_t receiptId, bool messageArrived) -{ - // Module? - if (messageArrived) - FreeVarsAssociatedWithReceipt(guid, receiptId); - else - DirtyAndFreeVarsAssociatedWithReceipt(guid, receiptId); - -} - -void VariableDeltaSerializer::BeginUnreliableAckedSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream, uint32_t _sendReceipt) -{ - RakAssert(_guid!=UNASSIGNED_RAKNET_GUID); - context->anyVariablesWritten=false; - context->guid=_guid; - context->bitStream=_bitStream; - if (context->variableHistoryUnique==0) - context->variableHistoryUnique=StartVariableHistoryWrite(_guid); - context->variableHistory=context->variableHistoryUnique; - context->sendReceipt=_sendReceipt; - context->changedVariables = AllocChangedVariablesList(); - context->newSystemSend=false; - context->serializationMode=UNRELIABLE_WITH_ACK_RECEIPT; -} - -void VariableDeltaSerializer::BeginUniqueSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream) -{ - RakAssert(_guid!=UNASSIGNED_RAKNET_GUID); - context->anyVariablesWritten=false; - context->guid=_guid; - context->bitStream=_bitStream; - if (context->variableHistoryUnique==0) - context->variableHistoryUnique=StartVariableHistoryWrite(_guid); - context->variableHistory=context->variableHistoryUnique; - context->newSystemSend=false; - - context->serializationMode=RELIABLE; -} - - -void VariableDeltaSerializer::BeginIdenticalSerialize(SerializationContext *context, bool _isFirstSendToRemoteSystem, BitStream *_bitStream) -{ - context->anyVariablesWritten=false; - context->guid=UNASSIGNED_RAKNET_GUID; - context->bitStream=_bitStream; - context->serializationMode=RELIABLE; - if (context->variableHistoryIdentical==0) - context->variableHistoryIdentical=StartVariableHistoryWrite(UNASSIGNED_RAKNET_GUID); - context->variableHistory=context->variableHistoryIdentical; - context->newSystemSend=_isFirstSendToRemoteSystem; -} - -void VariableDeltaSerializer::EndSerialize(SerializationContext *context) -{ - if (context->serializationMode==UNRELIABLE_WITH_ACK_RECEIPT) - { - if (context->anyVariablesWritten==false) - { - context->bitStream->Reset(); - FreeChangedVariablesList(context->changedVariables); - return; - } - - StoreChangedVariablesList(context->variableHistory, context->changedVariables, context->sendReceipt); - } - else - { - if (context->variableHistoryIdentical) - { - if (didComparisonThisTick==false) - { - didComparisonThisTick=true; - identicalSerializationBs.Reset(); - - if (context->anyVariablesWritten==false) - { - context->bitStream->Reset(); - return; - } - - identicalSerializationBs.Write(context->bitStream); - context->bitStream->ResetReadPointer(); - } - else - { - context->bitStream->Write(&identicalSerializationBs); - identicalSerializationBs.ResetReadPointer(); - } - } - else if (context->anyVariablesWritten==false) - { - context->bitStream->Reset(); - return; - } - } -} - -void VariableDeltaSerializer::BeginDeserialize(DeserializationContext *context, BitStream *_bitStream) -{ - context->bitStream=_bitStream; -} - -void VariableDeltaSerializer::EndDeserialize(DeserializationContext *context) -{ - (void) context; -} - -void VariableDeltaSerializer::AddRemoteSystemVariableHistory(RakNetGUID guid) -{ - (void) guid; -} - -void VariableDeltaSerializer::RemoveRemoteSystemVariableHistory(RakNetGUID guid) -{ - unsigned int idx,idx2; - idx = GetVarsWrittenPerRemoteSystemListIndex(guid); - if (idx==(unsigned int)-1) - return; - - if (remoteSystemVariableHistoryList[idx]->guid==guid) - { - // Memory pool doesn't call destructor - for (idx2=0; idx2 < remoteSystemVariableHistoryList[idx]->updatedVariablesHistory.Size(); idx2++) - { - FreeChangedVariablesList(remoteSystemVariableHistoryList[idx]->updatedVariablesHistory[idx2]); - } - - delete remoteSystemVariableHistoryList[idx]; - remoteSystemVariableHistoryList.RemoveAtIndexFast(idx); - return; - } -} - -int RakNet::VariableDeltaSerializer::UpdatedVariablesListPtrComp( const uint32_t &key, ChangedVariablesList* const &data ) -{ - if (keysendReceipt) - return -1; - if (key==data->sendReceipt) - return 0; - return 1; -} - -void VariableDeltaSerializer::FreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId) -{ - unsigned int idx, idx2; - idx = GetVarsWrittenPerRemoteSystemListIndex(guid); - if (idx==(unsigned int)-1) - return; - - RemoteSystemVariableHistory* vprs = remoteSystemVariableHistoryList[idx]; - bool objectExists; - idx2=vprs->updatedVariablesHistory.GetIndexFromKey(receiptId,&objectExists); - if (objectExists) - { - // Free this history node - FreeChangedVariablesList(vprs->updatedVariablesHistory[idx2]); - vprs->updatedVariablesHistory.RemoveAtIndex(idx2); - } -} - -void VariableDeltaSerializer::DirtyAndFreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId) -{ - unsigned int idx, idx2; - idx = GetVarsWrittenPerRemoteSystemListIndex(guid); - if (idx==(unsigned int)-1) - return; - - RemoteSystemVariableHistory* vprs = remoteSystemVariableHistoryList[idx]; - bool objectExists; - idx2=vprs->updatedVariablesHistory.GetIndexFromKey(receiptId,&objectExists); - if (objectExists) - { - // 'Dirty' all variables sent this update, meaning they will be resent the next time Serialize() is called - vprs->variableListDeltaTracker.FlagDirtyFromBitArray(vprs->updatedVariablesHistory[idx2]->bitField); - - // Free this history node - FreeChangedVariablesList(vprs->updatedVariablesHistory[idx2]); - vprs->updatedVariablesHistory.RemoveAtIndex(idx2); - } -} -unsigned int VariableDeltaSerializer::GetVarsWrittenPerRemoteSystemListIndex(RakNetGUID guid) -{ - unsigned int idx; - for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) - { - if (remoteSystemVariableHistoryList[idx]->guid==guid) - return idx; - } - return (unsigned int) -1; -} -void VariableDeltaSerializer::RemoveRemoteSystemVariableHistory(void) -{ - unsigned int idx,idx2; - for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) - { - for (idx2=0; idx2 < remoteSystemVariableHistoryList[idx]->updatedVariablesHistory.Size(); idx2++) - { - FreeChangedVariablesList(remoteSystemVariableHistoryList[idx]->updatedVariablesHistory[idx2]); - } - - delete remoteSystemVariableHistoryList[idx]; - } - remoteSystemVariableHistoryList.Clear(false,_FILE_AND_LINE_); -} - -VariableDeltaSerializer::RemoteSystemVariableHistory* VariableDeltaSerializer::GetRemoteSystemVariableHistory(RakNetGUID guid) -{ - unsigned int rshli = GetRemoteSystemHistoryListIndex(guid); - return remoteSystemVariableHistoryList[rshli]; -} - -VariableDeltaSerializer::ChangedVariablesList *VariableDeltaSerializer::AllocChangedVariablesList(void) -{ - VariableDeltaSerializer::ChangedVariablesList *p = updatedVariablesMemoryPool.Allocate(_FILE_AND_LINE_); - p->bitWriteIndex=0; - p->bitField[0]=0; - return p; -} -void VariableDeltaSerializer::FreeChangedVariablesList(ChangedVariablesList *changedVariables) -{ - updatedVariablesMemoryPool.Release(changedVariables, _FILE_AND_LINE_); -} -void VariableDeltaSerializer::StoreChangedVariablesList(RemoteSystemVariableHistory *variableHistory, ChangedVariablesList *changedVariables, uint32_t sendReceipt) -{ - changedVariables->sendReceipt=sendReceipt; - variableHistory->updatedVariablesHistory.Insert(changedVariables->sendReceipt,changedVariables,true,_FILE_AND_LINE_); -} - -VariableDeltaSerializer::RemoteSystemVariableHistory *VariableDeltaSerializer::StartVariableHistoryWrite(RakNetGUID guid) -{ - RemoteSystemVariableHistory *variableHistory; - - unsigned int rshli = GetRemoteSystemHistoryListIndex(guid); - if (rshli==(unsigned int) -1) - { - variableHistory = new RemoteSystemVariableHistory; - variableHistory->guid=guid; - remoteSystemVariableHistoryList.Push(variableHistory,_FILE_AND_LINE_); - } - else - { - variableHistory=remoteSystemVariableHistoryList[rshli]; - } - - variableHistory->variableListDeltaTracker.StartWrite(); - return variableHistory; -} -unsigned int VariableDeltaSerializer::GetRemoteSystemHistoryListIndex(RakNetGUID guid) -{ - // Find the variable tracker for the target system - unsigned int idx; - for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) - { - if (remoteSystemVariableHistoryList[idx]->guid==guid) - { - return idx; - } - } - return (unsigned int) -1; -} - -void VariableDeltaSerializer::OnPreSerializeTick(void) -{ - didComparisonThisTick=false; -} +#include "VariableDeltaSerializer.h" + +using namespace RakNet; + +VariableDeltaSerializer::VariableDeltaSerializer() {didComparisonThisTick=false;} +VariableDeltaSerializer::~VariableDeltaSerializer() {RemoveRemoteSystemVariableHistory();} + +VariableDeltaSerializer::SerializationContext::SerializationContext() {variableHistoryIdentical=0; variableHistoryUnique=0;} +VariableDeltaSerializer::SerializationContext::~SerializationContext() {} + +void VariableDeltaSerializer::OnMessageReceipt(RakNetGUID guid, uint32_t receiptId, bool messageArrived) +{ + // Module? + if (messageArrived) + FreeVarsAssociatedWithReceipt(guid, receiptId); + else + DirtyAndFreeVarsAssociatedWithReceipt(guid, receiptId); + +} + +void VariableDeltaSerializer::BeginUnreliableAckedSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream, uint32_t _sendReceipt) +{ + RakAssert(_guid!=UNASSIGNED_RAKNET_GUID); + context->anyVariablesWritten=false; + context->guid=_guid; + context->bitStream=_bitStream; + if (context->variableHistoryUnique==0) + context->variableHistoryUnique=StartVariableHistoryWrite(_guid); + context->variableHistory=context->variableHistoryUnique; + context->sendReceipt=_sendReceipt; + context->changedVariables = AllocChangedVariablesList(); + context->newSystemSend=false; + context->serializationMode=UNRELIABLE_WITH_ACK_RECEIPT; +} + +void VariableDeltaSerializer::BeginUniqueSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream) +{ + RakAssert(_guid!=UNASSIGNED_RAKNET_GUID); + context->anyVariablesWritten=false; + context->guid=_guid; + context->bitStream=_bitStream; + if (context->variableHistoryUnique==0) + context->variableHistoryUnique=StartVariableHistoryWrite(_guid); + context->variableHistory=context->variableHistoryUnique; + context->newSystemSend=false; + + context->serializationMode=RELIABLE; +} + + +void VariableDeltaSerializer::BeginIdenticalSerialize(SerializationContext *context, bool _isFirstSendToRemoteSystem, BitStream *_bitStream) +{ + context->anyVariablesWritten=false; + context->guid=UNASSIGNED_RAKNET_GUID; + context->bitStream=_bitStream; + context->serializationMode=RELIABLE; + if (context->variableHistoryIdentical==0) + context->variableHistoryIdentical=StartVariableHistoryWrite(UNASSIGNED_RAKNET_GUID); + context->variableHistory=context->variableHistoryIdentical; + context->newSystemSend=_isFirstSendToRemoteSystem; +} + +void VariableDeltaSerializer::EndSerialize(SerializationContext *context) +{ + if (context->serializationMode==UNRELIABLE_WITH_ACK_RECEIPT) + { + if (context->anyVariablesWritten==false) + { + context->bitStream->Reset(); + FreeChangedVariablesList(context->changedVariables); + return; + } + + StoreChangedVariablesList(context->variableHistory, context->changedVariables, context->sendReceipt); + } + else + { + if (context->variableHistoryIdentical) + { + if (didComparisonThisTick==false) + { + didComparisonThisTick=true; + identicalSerializationBs.Reset(); + + if (context->anyVariablesWritten==false) + { + context->bitStream->Reset(); + return; + } + + identicalSerializationBs.Write(context->bitStream); + context->bitStream->ResetReadPointer(); + } + else + { + context->bitStream->Write(&identicalSerializationBs); + identicalSerializationBs.ResetReadPointer(); + } + } + else if (context->anyVariablesWritten==false) + { + context->bitStream->Reset(); + return; + } + } +} + +void VariableDeltaSerializer::BeginDeserialize(DeserializationContext *context, BitStream *_bitStream) +{ + context->bitStream=_bitStream; +} + +void VariableDeltaSerializer::EndDeserialize(DeserializationContext *context) +{ + (void) context; +} + +void VariableDeltaSerializer::AddRemoteSystemVariableHistory(RakNetGUID guid) +{ + (void) guid; +} + +void VariableDeltaSerializer::RemoveRemoteSystemVariableHistory(RakNetGUID guid) +{ + unsigned int idx,idx2; + idx = GetVarsWrittenPerRemoteSystemListIndex(guid); + if (idx==(unsigned int)-1) + return; + + if (remoteSystemVariableHistoryList[idx]->guid==guid) + { + // Memory pool doesn't call destructor + for (idx2=0; idx2 < remoteSystemVariableHistoryList[idx]->updatedVariablesHistory.Size(); idx2++) + { + FreeChangedVariablesList(remoteSystemVariableHistoryList[idx]->updatedVariablesHistory[idx2]); + } + + delete remoteSystemVariableHistoryList[idx]; + remoteSystemVariableHistoryList.RemoveAtIndexFast(idx); + return; + } +} + +int RakNet::VariableDeltaSerializer::UpdatedVariablesListPtrComp( const uint32_t &key, ChangedVariablesList* const &data ) +{ + if (keysendReceipt) + return -1; + if (key==data->sendReceipt) + return 0; + return 1; +} + +void VariableDeltaSerializer::FreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId) +{ + unsigned int idx, idx2; + idx = GetVarsWrittenPerRemoteSystemListIndex(guid); + if (idx==(unsigned int)-1) + return; + + RemoteSystemVariableHistory* vprs = remoteSystemVariableHistoryList[idx]; + bool objectExists; + idx2=vprs->updatedVariablesHistory.GetIndexFromKey(receiptId,&objectExists); + if (objectExists) + { + // Free this history node + FreeChangedVariablesList(vprs->updatedVariablesHistory[idx2]); + vprs->updatedVariablesHistory.RemoveAtIndex(idx2); + } +} + +void VariableDeltaSerializer::DirtyAndFreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId) +{ + unsigned int idx, idx2; + idx = GetVarsWrittenPerRemoteSystemListIndex(guid); + if (idx==(unsigned int)-1) + return; + + RemoteSystemVariableHistory* vprs = remoteSystemVariableHistoryList[idx]; + bool objectExists; + idx2=vprs->updatedVariablesHistory.GetIndexFromKey(receiptId,&objectExists); + if (objectExists) + { + // 'Dirty' all variables sent this update, meaning they will be resent the next time Serialize() is called + vprs->variableListDeltaTracker.FlagDirtyFromBitArray(vprs->updatedVariablesHistory[idx2]->bitField); + + // Free this history node + FreeChangedVariablesList(vprs->updatedVariablesHistory[idx2]); + vprs->updatedVariablesHistory.RemoveAtIndex(idx2); + } +} +unsigned int VariableDeltaSerializer::GetVarsWrittenPerRemoteSystemListIndex(RakNetGUID guid) +{ + unsigned int idx; + for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) + { + if (remoteSystemVariableHistoryList[idx]->guid==guid) + return idx; + } + return (unsigned int) -1; +} +void VariableDeltaSerializer::RemoveRemoteSystemVariableHistory(void) +{ + unsigned int idx,idx2; + for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) + { + for (idx2=0; idx2 < remoteSystemVariableHistoryList[idx]->updatedVariablesHistory.Size(); idx2++) + { + FreeChangedVariablesList(remoteSystemVariableHistoryList[idx]->updatedVariablesHistory[idx2]); + } + + delete remoteSystemVariableHistoryList[idx]; + } + remoteSystemVariableHistoryList.Clear(false,_FILE_AND_LINE_); +} + +VariableDeltaSerializer::RemoteSystemVariableHistory* VariableDeltaSerializer::GetRemoteSystemVariableHistory(RakNetGUID guid) +{ + unsigned int rshli = GetRemoteSystemHistoryListIndex(guid); + return remoteSystemVariableHistoryList[rshli]; +} + +VariableDeltaSerializer::ChangedVariablesList *VariableDeltaSerializer::AllocChangedVariablesList(void) +{ + VariableDeltaSerializer::ChangedVariablesList *p = updatedVariablesMemoryPool.Allocate(_FILE_AND_LINE_); + p->bitWriteIndex=0; + p->bitField[0]=0; + return p; +} +void VariableDeltaSerializer::FreeChangedVariablesList(ChangedVariablesList *changedVariables) +{ + updatedVariablesMemoryPool.Release(changedVariables, _FILE_AND_LINE_); +} +void VariableDeltaSerializer::StoreChangedVariablesList(RemoteSystemVariableHistory *variableHistory, ChangedVariablesList *changedVariables, uint32_t sendReceipt) +{ + changedVariables->sendReceipt=sendReceipt; + variableHistory->updatedVariablesHistory.Insert(changedVariables->sendReceipt,changedVariables,true,_FILE_AND_LINE_); +} + +VariableDeltaSerializer::RemoteSystemVariableHistory *VariableDeltaSerializer::StartVariableHistoryWrite(RakNetGUID guid) +{ + RemoteSystemVariableHistory *variableHistory; + + unsigned int rshli = GetRemoteSystemHistoryListIndex(guid); + if (rshli==(unsigned int) -1) + { + variableHistory = new RemoteSystemVariableHistory; + variableHistory->guid=guid; + remoteSystemVariableHistoryList.Push(variableHistory,_FILE_AND_LINE_); + } + else + { + variableHistory=remoteSystemVariableHistoryList[rshli]; + } + + variableHistory->variableListDeltaTracker.StartWrite(); + return variableHistory; +} +unsigned int VariableDeltaSerializer::GetRemoteSystemHistoryListIndex(RakNetGUID guid) +{ + // Find the variable tracker for the target system + unsigned int idx; + for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) + { + if (remoteSystemVariableHistoryList[idx]->guid==guid) + { + return idx; + } + } + return (unsigned int) -1; +} + +void VariableDeltaSerializer::OnPreSerializeTick(void) +{ + didComparisonThisTick=false; +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/VariableDeltaSerializer.h b/project/lib_projects/raknet/jni/RaknetSources/VariableDeltaSerializer.h index 257426d..a05653a 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/VariableDeltaSerializer.h +++ b/project/lib_projects/raknet/jni/RaknetSources/VariableDeltaSerializer.h @@ -1,257 +1,257 @@ -#ifndef __VARIABLE_DELTA_SERIALIZER_H -#define __VARIABLE_DELTA_SERIALIZER_H - -#include "VariableListDeltaTracker.h" -#include "DS_MemoryPool.h" -#include "NativeTypes.h" -#include "BitStream.h" -#include "PacketPriority.h" -#include "DS_OrderedList.h" - -namespace RakNet -{ - -/// \brief Class to compare memory values of variables in a current state to a prior state -/// Results of the comparisons will be written to a bitStream, such that only changed variables get written
      -/// Can be used with ReplicaManager3 to Serialize a Replica3 per-variable, rather than comparing the entire object against itself
      -/// Usage:
      -///
      -/// 1. Call BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(). In the case of Replica3, this would be in the Serialize() call
      -/// 2. For each variable of the type in step 1, call Serialize(). The same variables must be serialized every tick()
      -/// 3. Call EndSerialize()
      -/// 4. Repeat step 1 for each of the other categories of how to send varaibles -/// -/// On the receiver: -/// -/// 1. Call BeginDeserialize(). In the case of Replica3, this would be in the Deserialize() call -/// 2. Call DeserializeVariable() for each variable, in the same order as was Serialized() -/// 3. Call EndSerialize() -/// \sa The ReplicaManager3 sample -class VariableDeltaSerializer -{ -protected: - struct RemoteSystemVariableHistory; - struct ChangedVariablesList; - -public: - VariableDeltaSerializer(); - ~VariableDeltaSerializer(); - - struct SerializationContext - { - SerializationContext(); - ~SerializationContext(); - - RakNetGUID guid; - BitStream *bitStream; - uint32_t rakPeerSendReceipt; - RemoteSystemVariableHistory *variableHistory; - RemoteSystemVariableHistory *variableHistoryIdentical; - RemoteSystemVariableHistory *variableHistoryUnique; - ChangedVariablesList *changedVariables; - uint32_t sendReceipt; - PacketReliability serializationMode; - bool anyVariablesWritten; - bool newSystemSend; // Force send all, do not record - }; - - struct DeserializationContext - { - BitStream *bitStream; - }; - - /// \brief Call before doing one or more SerializeVariable calls when the data will be sent UNRELIABLE_WITH_ACK_RECEIPT - /// The last value of each variable will be saved per remote system. Additionally, a history of \a _sendReceipts is stored to determine what to resend on packetloss. - /// When variables are lost, they will be flagged dirty and always resent to the system that lost it - /// Disadvantages: Every variable for every remote system is copied internally, in addition to a history list of what variables changed for which \a _sendReceipt. Very memory and CPU intensive for multiple connections. - /// Advantages: When data needs to be resent by RakNet, RakNet can only resend the value it currently has. This allows the application to control the resend, sending the most recent value of the variable. The end result is that bandwidth is used more efficiently because old data is never sent. - /// \pre Upon getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED call OnMessageReceipt() - /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections - /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. - /// \param[in] _guid Which system we are sending to - /// \param[in] _bitSteam Which bitStream to write to - /// \param[in] _sendReceipt Returned from RakPeer::IncrementNextSendReceipt() and passed to the Send() or SendLists() function. Identifies this update for ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED - void BeginUnreliableAckedSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream, uint32_t _sendReceipt); - - /// \brief Call before doing one or more SerializeVariable calls for data that may be sent differently to every remote system (such as an invisibility flag that only teammates can see) - /// The last value of each variable will be saved per remote system. - /// Unlike BeginUnreliableAckedSerialize(), send receipts are not necessary - /// Disadvantages: Every variable for every remote system is copied internally. Very memory and CPU intensive for multiple connections. - /// Advantages: When data is sent differently depending on the recipient, this system can make things easier to use and is as efficient as it can be. - /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections - /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. - /// \param[in] _guid Which system we are sending to - /// \param[in] _bitSteam Which bitStream to write to - void BeginUniqueSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream); - - /// \brief Call before doing one or more SerializeVariable calls for data that is sent with the same value to every remote system (such as health, position, etc.) - /// This is the most common type of serialization, and also the most efficient - /// Disadvantages: A copy of every variable still needs to be held, although only once - /// Advantages: After the first serialization, the last serialized bitStream will be used for subsequent sends - /// \pre Call OnPreSerializeTick() before doing any calls to BeginIdenticalSerialize() for each of your objects, once per game tick - /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. - /// \param[in] _isFirstSerializeToThisSystem Pass true if this is the first time ever serializing to this system (the initial download). This way all variables will be written, rather than checking against prior sent values. - /// \param[in] _bitSteam Which bitStream to write to - void BeginIdenticalSerialize(SerializationContext *context, bool _isFirstSerializeToThisSystem, BitStream *_bitStream); - - /// \brief Call after BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(), then after calling SerializeVariable() one or more times - /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() - void EndSerialize(SerializationContext *context); - - /// \brief Call when you receive the BitStream written by SerializeVariable(), before calling DeserializeVariable() - /// \param[in] context Holds the context of this group of deserialize calls. This can be a stack object just passed to the function. - /// \param[in] _bitStream Pass the bitStream originally passed to and written to by serialize calls - void BeginDeserialize(DeserializationContext *context, BitStream *_bitStream); - - /// \param[in] context Same context pointer passed to BeginDeserialize() - void EndDeserialize(DeserializationContext *context); - - /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped - /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events - /// \param[in] _guid Which system we are sending to - void AddRemoteSystemVariableHistory(RakNetGUID guid); - - /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped - /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events - /// \param[in] _guid Which system we are sending to - void RemoveRemoteSystemVariableHistory(RakNetGUID guid); - - /// BeginIdenticalSerialize() requires knowledge of when serialization has started for an object across multiple systems - /// This way it can setup the flag to do new comparisons against the last sent values, rather than just resending the last sent bitStream - /// For Replica3, overload and call this from Replica3::OnUserReplicaPreSerializeTick() - void OnPreSerializeTick(void); - - /// Call when getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED for a particular system - /// Example: - /// - /// uint32_t msgNumber; - /// memcpy(&msgNumber, packet->data+1, 4); - /// DataStructures::List replicaListOut; - /// replicaManager.GetReplicasCreatedByMe(replicaListOut); - /// unsigned int idx; - /// for (idx=0; idx < replicaListOut.GetSize(); idx++) - /// { - /// ((SampleReplica*)replicaListOut[idx])->NotifyReplicaOfMessageDeliveryStatus(packet->guid,msgNumber, packet->data[0]==ID_SND_RECEIPT_ACKED); - /// } - /// - /// \param[in] guid Which system we are sending to - /// \param[in] receiptId Encoded in bytes 1-4 inclusive of ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED - /// \param[in] messageArrived True for ID_SND_RECEIPT_ACKED, false otherwise - void OnMessageReceipt(RakNetGUID guid, uint32_t receiptId, bool messageArrived); - - /// Call to Serialize a variable - /// Will write to the bitSteam passed to \a context true, variableValue if the variable has changed or has never been written. Otherwise will write false. - /// \pre You have called BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() - /// \pre Will also require calling OnPreSerializeTick() if using BeginIdenticalSerialize() - /// \note Be sure to call EndSerialize() after finishing all serializations - /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() - /// \param[in] variable A variable to write to the bitStream passed to \a context - template - void SerializeVariable(SerializationContext *context, const VarType &variable) - { - if (context->newSystemSend) - { - if (context->variableHistory->variableListDeltaTracker.IsPastEndOfList()==false) - { - // previously sent data to another system - context->bitStream->Write(true); - context->bitStream->Write(variable); - context->anyVariablesWritten=true; - } - else - { - // never sent data to another system - context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); - context->anyVariablesWritten=true; - } - } - else if (context->serializationMode==UNRELIABLE_WITH_ACK_RECEIPT) - { - context->anyVariablesWritten|= - context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream, context->changedVariables->bitField, context->changedVariables->bitWriteIndex++); - } - else - { - if (context->variableHistoryIdentical) - { - // Identical serialization to a number of systems - if (didComparisonThisTick==false) - context->anyVariablesWritten|= - context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); - // Else bitstream is written to at the end - } - else - { - // Per-system serialization - context->anyVariablesWritten|= - context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); - } - } - } - - /// Call to deserialize into a variable - /// \pre You have called BeginDeserialize() - /// \note Be sure to call EndDeserialize() after finishing all deserializations - /// \param[in] context Same context pointer passed to BeginDeserialize() - /// \param[in] variable A variable to write to the bitStream passed to \a context - template - bool DeserializeVariable(DeserializationContext *context, VarType &variable) - { - return VariableListDeltaTracker::ReadVarFromBitstream(variable, context->bitStream); - } - - - -protected: - - // For a given send receipt from RakPeer::Send() track which variables we updated - // That way if that send does not arrive (ID_SND_RECEIPT_LOSS) we can mark those variables as dirty to resend them with current values - struct ChangedVariablesList - { - uint32_t sendReceipt; - unsigned short bitWriteIndex; - unsigned char bitField[56]; - }; - - // static int Replica2ObjectComp( const uint32_t &key, ChangedVariablesList* const &data ); - - static int UpdatedVariablesListPtrComp( const uint32_t &key, ChangedVariablesList* const &data ); - - // For each remote system, track the last values of variables we sent to them, and the history of what values changed per call to Send() - // Every serialize if a variable changes from its last value, send it out again - // Also if a send does not arrive (ID_SND_RECEIPT_LOSS) we use updatedVariablesHistory to mark those variables as dirty, to resend them unreliably with the current values - struct RemoteSystemVariableHistory - { - RakNetGUID guid; - VariableListDeltaTracker variableListDeltaTracker; - DataStructures::OrderedList updatedVariablesHistory; - }; - /// A list of RemoteSystemVariableHistory indexed by guid, one per connection that we serialize to - /// List is added to when SerializeConstruction is called, and removed from when SerializeDestruction is called, or when a given connection is dropped - DataStructures::List remoteSystemVariableHistoryList; - - // Because the ChangedVariablesList is created every serialize and destroyed every receipt I use a pool to avoid fragmentation - DataStructures::MemoryPool updatedVariablesMemoryPool; - - bool didComparisonThisTick; - RakNet::BitStream identicalSerializationBs; - - void FreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId); - void DirtyAndFreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId); - unsigned int GetVarsWrittenPerRemoteSystemListIndex(RakNetGUID guid); - void RemoveRemoteSystemVariableHistory(void); - - RemoteSystemVariableHistory* GetRemoteSystemVariableHistory(RakNetGUID guid); - - ChangedVariablesList *AllocChangedVariablesList(void); - void FreeChangedVariablesList(ChangedVariablesList *changedVariables); - void StoreChangedVariablesList(RemoteSystemVariableHistory *variableHistory, ChangedVariablesList *changedVariables, uint32_t sendReceipt); - - RemoteSystemVariableHistory *StartVariableHistoryWrite(RakNetGUID guid); - unsigned int GetRemoteSystemHistoryListIndex(RakNetGUID guid); - -}; - -} - -#endif +#ifndef __VARIABLE_DELTA_SERIALIZER_H +#define __VARIABLE_DELTA_SERIALIZER_H + +#include "VariableListDeltaTracker.h" +#include "DS_MemoryPool.h" +#include "NativeTypes.h" +#include "BitStream.h" +#include "PacketPriority.h" +#include "DS_OrderedList.h" + +namespace RakNet +{ + +/// \brief Class to compare memory values of variables in a current state to a prior state +/// Results of the comparisons will be written to a bitStream, such that only changed variables get written
      +/// Can be used with ReplicaManager3 to Serialize a Replica3 per-variable, rather than comparing the entire object against itself
      +/// Usage:
      +///
      +/// 1. Call BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(). In the case of Replica3, this would be in the Serialize() call
      +/// 2. For each variable of the type in step 1, call Serialize(). The same variables must be serialized every tick()
      +/// 3. Call EndSerialize()
      +/// 4. Repeat step 1 for each of the other categories of how to send varaibles +/// +/// On the receiver: +/// +/// 1. Call BeginDeserialize(). In the case of Replica3, this would be in the Deserialize() call +/// 2. Call DeserializeVariable() for each variable, in the same order as was Serialized() +/// 3. Call EndSerialize() +/// \sa The ReplicaManager3 sample +class VariableDeltaSerializer +{ +protected: + struct RemoteSystemVariableHistory; + struct ChangedVariablesList; + +public: + VariableDeltaSerializer(); + ~VariableDeltaSerializer(); + + struct SerializationContext + { + SerializationContext(); + ~SerializationContext(); + + RakNetGUID guid; + BitStream *bitStream; + uint32_t rakPeerSendReceipt; + RemoteSystemVariableHistory *variableHistory; + RemoteSystemVariableHistory *variableHistoryIdentical; + RemoteSystemVariableHistory *variableHistoryUnique; + ChangedVariablesList *changedVariables; + uint32_t sendReceipt; + PacketReliability serializationMode; + bool anyVariablesWritten; + bool newSystemSend; // Force send all, do not record + }; + + struct DeserializationContext + { + BitStream *bitStream; + }; + + /// \brief Call before doing one or more SerializeVariable calls when the data will be sent UNRELIABLE_WITH_ACK_RECEIPT + /// The last value of each variable will be saved per remote system. Additionally, a history of \a _sendReceipts is stored to determine what to resend on packetloss. + /// When variables are lost, they will be flagged dirty and always resent to the system that lost it + /// Disadvantages: Every variable for every remote system is copied internally, in addition to a history list of what variables changed for which \a _sendReceipt. Very memory and CPU intensive for multiple connections. + /// Advantages: When data needs to be resent by RakNet, RakNet can only resend the value it currently has. This allows the application to control the resend, sending the most recent value of the variable. The end result is that bandwidth is used more efficiently because old data is never sent. + /// \pre Upon getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED call OnMessageReceipt() + /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections + /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. + /// \param[in] _guid Which system we are sending to + /// \param[in] _bitSteam Which bitStream to write to + /// \param[in] _sendReceipt Returned from RakPeer::IncrementNextSendReceipt() and passed to the Send() or SendLists() function. Identifies this update for ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED + void BeginUnreliableAckedSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream, uint32_t _sendReceipt); + + /// \brief Call before doing one or more SerializeVariable calls for data that may be sent differently to every remote system (such as an invisibility flag that only teammates can see) + /// The last value of each variable will be saved per remote system. + /// Unlike BeginUnreliableAckedSerialize(), send receipts are not necessary + /// Disadvantages: Every variable for every remote system is copied internally. Very memory and CPU intensive for multiple connections. + /// Advantages: When data is sent differently depending on the recipient, this system can make things easier to use and is as efficient as it can be. + /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections + /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. + /// \param[in] _guid Which system we are sending to + /// \param[in] _bitSteam Which bitStream to write to + void BeginUniqueSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream); + + /// \brief Call before doing one or more SerializeVariable calls for data that is sent with the same value to every remote system (such as health, position, etc.) + /// This is the most common type of serialization, and also the most efficient + /// Disadvantages: A copy of every variable still needs to be held, although only once + /// Advantages: After the first serialization, the last serialized bitStream will be used for subsequent sends + /// \pre Call OnPreSerializeTick() before doing any calls to BeginIdenticalSerialize() for each of your objects, once per game tick + /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. + /// \param[in] _isFirstSerializeToThisSystem Pass true if this is the first time ever serializing to this system (the initial download). This way all variables will be written, rather than checking against prior sent values. + /// \param[in] _bitSteam Which bitStream to write to + void BeginIdenticalSerialize(SerializationContext *context, bool _isFirstSerializeToThisSystem, BitStream *_bitStream); + + /// \brief Call after BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(), then after calling SerializeVariable() one or more times + /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() + void EndSerialize(SerializationContext *context); + + /// \brief Call when you receive the BitStream written by SerializeVariable(), before calling DeserializeVariable() + /// \param[in] context Holds the context of this group of deserialize calls. This can be a stack object just passed to the function. + /// \param[in] _bitStream Pass the bitStream originally passed to and written to by serialize calls + void BeginDeserialize(DeserializationContext *context, BitStream *_bitStream); + + /// \param[in] context Same context pointer passed to BeginDeserialize() + void EndDeserialize(DeserializationContext *context); + + /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped + /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events + /// \param[in] _guid Which system we are sending to + void AddRemoteSystemVariableHistory(RakNetGUID guid); + + /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped + /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events + /// \param[in] _guid Which system we are sending to + void RemoveRemoteSystemVariableHistory(RakNetGUID guid); + + /// BeginIdenticalSerialize() requires knowledge of when serialization has started for an object across multiple systems + /// This way it can setup the flag to do new comparisons against the last sent values, rather than just resending the last sent bitStream + /// For Replica3, overload and call this from Replica3::OnUserReplicaPreSerializeTick() + void OnPreSerializeTick(void); + + /// Call when getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED for a particular system + /// Example: + /// + /// uint32_t msgNumber; + /// memcpy(&msgNumber, packet->data+1, 4); + /// DataStructures::List replicaListOut; + /// replicaManager.GetReplicasCreatedByMe(replicaListOut); + /// unsigned int idx; + /// for (idx=0; idx < replicaListOut.GetSize(); idx++) + /// { + /// ((SampleReplica*)replicaListOut[idx])->NotifyReplicaOfMessageDeliveryStatus(packet->guid,msgNumber, packet->data[0]==ID_SND_RECEIPT_ACKED); + /// } + /// + /// \param[in] guid Which system we are sending to + /// \param[in] receiptId Encoded in bytes 1-4 inclusive of ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED + /// \param[in] messageArrived True for ID_SND_RECEIPT_ACKED, false otherwise + void OnMessageReceipt(RakNetGUID guid, uint32_t receiptId, bool messageArrived); + + /// Call to Serialize a variable + /// Will write to the bitSteam passed to \a context true, variableValue if the variable has changed or has never been written. Otherwise will write false. + /// \pre You have called BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() + /// \pre Will also require calling OnPreSerializeTick() if using BeginIdenticalSerialize() + /// \note Be sure to call EndSerialize() after finishing all serializations + /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() + /// \param[in] variable A variable to write to the bitStream passed to \a context + template + void SerializeVariable(SerializationContext *context, const VarType &variable) + { + if (context->newSystemSend) + { + if (context->variableHistory->variableListDeltaTracker.IsPastEndOfList()==false) + { + // previously sent data to another system + context->bitStream->Write(true); + context->bitStream->Write(variable); + context->anyVariablesWritten=true; + } + else + { + // never sent data to another system + context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); + context->anyVariablesWritten=true; + } + } + else if (context->serializationMode==UNRELIABLE_WITH_ACK_RECEIPT) + { + context->anyVariablesWritten|= + context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream, context->changedVariables->bitField, context->changedVariables->bitWriteIndex++); + } + else + { + if (context->variableHistoryIdentical) + { + // Identical serialization to a number of systems + if (didComparisonThisTick==false) + context->anyVariablesWritten|= + context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); + // Else bitstream is written to at the end + } + else + { + // Per-system serialization + context->anyVariablesWritten|= + context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); + } + } + } + + /// Call to deserialize into a variable + /// \pre You have called BeginDeserialize() + /// \note Be sure to call EndDeserialize() after finishing all deserializations + /// \param[in] context Same context pointer passed to BeginDeserialize() + /// \param[in] variable A variable to write to the bitStream passed to \a context + template + bool DeserializeVariable(DeserializationContext *context, VarType &variable) + { + return VariableListDeltaTracker::ReadVarFromBitstream(variable, context->bitStream); + } + + + +protected: + + // For a given send receipt from RakPeer::Send() track which variables we updated + // That way if that send does not arrive (ID_SND_RECEIPT_LOSS) we can mark those variables as dirty to resend them with current values + struct ChangedVariablesList + { + uint32_t sendReceipt; + unsigned short bitWriteIndex; + unsigned char bitField[56]; + }; + + // static int Replica2ObjectComp( const uint32_t &key, ChangedVariablesList* const &data ); + + static int UpdatedVariablesListPtrComp( const uint32_t &key, ChangedVariablesList* const &data ); + + // For each remote system, track the last values of variables we sent to them, and the history of what values changed per call to Send() + // Every serialize if a variable changes from its last value, send it out again + // Also if a send does not arrive (ID_SND_RECEIPT_LOSS) we use updatedVariablesHistory to mark those variables as dirty, to resend them unreliably with the current values + struct RemoteSystemVariableHistory + { + RakNetGUID guid; + VariableListDeltaTracker variableListDeltaTracker; + DataStructures::OrderedList updatedVariablesHistory; + }; + /// A list of RemoteSystemVariableHistory indexed by guid, one per connection that we serialize to + /// List is added to when SerializeConstruction is called, and removed from when SerializeDestruction is called, or when a given connection is dropped + DataStructures::List remoteSystemVariableHistoryList; + + // Because the ChangedVariablesList is created every serialize and destroyed every receipt I use a pool to avoid fragmentation + DataStructures::MemoryPool updatedVariablesMemoryPool; + + bool didComparisonThisTick; + RakNet::BitStream identicalSerializationBs; + + void FreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId); + void DirtyAndFreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId); + unsigned int GetVarsWrittenPerRemoteSystemListIndex(RakNetGUID guid); + void RemoveRemoteSystemVariableHistory(void); + + RemoteSystemVariableHistory* GetRemoteSystemVariableHistory(RakNetGUID guid); + + ChangedVariablesList *AllocChangedVariablesList(void); + void FreeChangedVariablesList(ChangedVariablesList *changedVariables); + void StoreChangedVariablesList(RemoteSystemVariableHistory *variableHistory, ChangedVariablesList *changedVariables, uint32_t sendReceipt); + + RemoteSystemVariableHistory *StartVariableHistoryWrite(RakNetGUID guid); + unsigned int GetRemoteSystemHistoryListIndex(RakNetGUID guid); + +}; + +} + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/VariableListDeltaTracker.cpp b/project/lib_projects/raknet/jni/RaknetSources/VariableListDeltaTracker.cpp index 5523a99..cbbcb2f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/VariableListDeltaTracker.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/VariableListDeltaTracker.cpp @@ -1,40 +1,40 @@ -#include "VariableListDeltaTracker.h" - -using namespace RakNet; - -VariableListDeltaTracker::VariableListDeltaTracker() {nextWriteIndex=0;} -VariableListDeltaTracker::~VariableListDeltaTracker() -{ - unsigned int i; - for (i=0; i < variableList.Size(); i++) - rakFree_Ex(variableList[i].lastData,_FILE_AND_LINE_); -} - -// Call before using a series of WriteVar -void VariableListDeltaTracker::StartWrite(void) {nextWriteIndex=0;} - -void VariableListDeltaTracker::FlagDirtyFromBitArray(unsigned char *bArray) -{ - unsigned short readOffset=0; - for (readOffset=0; readOffset < variableList.Size(); readOffset++) - { - bool result = ( bArray[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0; - - if (result==true) - variableList[readOffset].isDirty=true; - } -} -VariableListDeltaTracker::VariableLastValueNode::VariableLastValueNode() -{ - lastData=0; -} -VariableListDeltaTracker::VariableLastValueNode::VariableLastValueNode(const unsigned char *data, int _byteLength) -{ - lastData=(char*) rakMalloc_Ex(_byteLength,_FILE_AND_LINE_); - memcpy(lastData,data,_byteLength); - byteLength=_byteLength; - isDirty=false; -} -VariableListDeltaTracker::VariableLastValueNode::~VariableLastValueNode() -{ -} +#include "VariableListDeltaTracker.h" + +using namespace RakNet; + +VariableListDeltaTracker::VariableListDeltaTracker() {nextWriteIndex=0;} +VariableListDeltaTracker::~VariableListDeltaTracker() +{ + unsigned int i; + for (i=0; i < variableList.Size(); i++) + rakFree_Ex(variableList[i].lastData,_FILE_AND_LINE_); +} + +// Call before using a series of WriteVar +void VariableListDeltaTracker::StartWrite(void) {nextWriteIndex=0;} + +void VariableListDeltaTracker::FlagDirtyFromBitArray(unsigned char *bArray) +{ + unsigned short readOffset=0; + for (readOffset=0; readOffset < variableList.Size(); readOffset++) + { + bool result = ( bArray[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0; + + if (result==true) + variableList[readOffset].isDirty=true; + } +} +VariableListDeltaTracker::VariableLastValueNode::VariableLastValueNode() +{ + lastData=0; +} +VariableListDeltaTracker::VariableLastValueNode::VariableLastValueNode(const unsigned char *data, int _byteLength) +{ + lastData=(char*) rakMalloc_Ex(_byteLength,_FILE_AND_LINE_); + memcpy(lastData,data,_byteLength); + byteLength=_byteLength; + isDirty=false; +} +VariableListDeltaTracker::VariableLastValueNode::~VariableLastValueNode() +{ +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/VariableListDeltaTracker.h b/project/lib_projects/raknet/jni/RaknetSources/VariableListDeltaTracker.h index 5b55d3e..e449cd7 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/VariableListDeltaTracker.h +++ b/project/lib_projects/raknet/jni/RaknetSources/VariableListDeltaTracker.h @@ -1,131 +1,131 @@ -#include "NativeTypes.h" -#include "DS_List.h" -#include "RakMemoryOverride.h" -#include "BitStream.h" - -namespace RakNet -{ -/// Class to write a series of variables, copy the contents to memory, and return if the newly written value is different than what was last written -/// Can also encode the reads, writes, and results directly to/from a bitstream -class VariableListDeltaTracker -{ -public: - VariableListDeltaTracker(); - ~VariableListDeltaTracker(); - - // Call before using a series of WriteVar - void StartWrite(void); - - bool IsPastEndOfList(void) const {return nextWriteIndex>=variableList.Size();} - - /// Records the passed value of the variable to memory, and returns true if the value is different from the write before that (or if it is the first write) - /// \pre Call StartWrite() before doing the first of a series of calls to WriteVar or other functions that call WriteVar - /// \note Variables must be of the same type, written in the same order, each time - template - bool WriteVar(const VarType &varData) - { - RakNet::BitStream temp; - temp.Write(varData); - if (nextWriteIndex>=variableList.Size()) - { - variableList.Push(VariableLastValueNode(temp.GetData(),temp.GetNumberOfBytesUsed()),_FILE_AND_LINE_); - nextWriteIndex++; - return true; // Different because it's new - } - - if (temp.GetNumberOfBytesUsed()!=variableList[nextWriteIndex].byteLength) - { - variableList[nextWriteIndex].lastData=(char*) rakRealloc_Ex(variableList[nextWriteIndex].lastData, temp.GetNumberOfBytesUsed(),_FILE_AND_LINE_); - variableList[nextWriteIndex].byteLength=temp.GetNumberOfBytesUsed(); - memcpy(variableList[nextWriteIndex].lastData,temp.GetData(),temp.GetNumberOfBytesUsed()); - nextWriteIndex++; - variableList[nextWriteIndex].isDirty=false; - return true; // Different because the serialized size is different - } - if (variableList[nextWriteIndex].isDirty==false && memcmp(temp.GetData(),variableList[nextWriteIndex].lastData, variableList[nextWriteIndex].byteLength)==0) - { - nextWriteIndex++; - return false; // Same because not dirty and memcmp is the same - } - - variableList[nextWriteIndex].isDirty=false; - memcpy(variableList[nextWriteIndex].lastData,temp.GetData(),temp.GetNumberOfBytesUsed()); - nextWriteIndex++; - return true; // Different because dirty or memcmp was different - } - /// Calls WriteVar. If the variable has changed, writes true, and writes the variable. Otherwise writes false. - template - bool WriteVarToBitstream(const VarType &varData, RakNet::BitStream *bitStream) - { - bool wasDifferent = WriteVar(varData); - bitStream->Write(wasDifferent); - if (wasDifferent) - { - bitStream->Write(varData); - return true; - } - return false; - } - /// Calls WriteVarToBitstream(). Additionally, adds the boolean result of WriteVar() to boolean bit array - template - bool WriteVarToBitstream(const VarType &varData, RakNet::BitStream *bitStream, unsigned char *bArray, unsigned short writeOffset) - { - if (WriteVarToBitstream(varData,bitStream)==true) - { - BitSize_t numberOfBitsMod8 = writeOffset & 7; - - if ( numberOfBitsMod8 == 0 ) - bArray[ writeOffset >> 3 ] = 0x80; - else - bArray[ writeOffset >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 - - return true; - } - else - { - if ( ( writeOffset & 7 ) == 0 ) - bArray[ writeOffset >> 3 ] = 0; - - return false; - } - } - - /// Paired with a call to WriteVarToBitstream(), will read a variable if it had changed. Otherwise the values remains the same. - template - static bool ReadVarFromBitstream(const VarType &varData, RakNet::BitStream *bitStream) - { - bool wasWritten; - if (bitStream->Read(wasWritten)==false) - return false; - if (wasWritten) - { - if (bitStream->Read(varData)==false) - return false; - } - return wasWritten; - } - - /// Variables flagged dirty will cause WriteVar() to return true, even if the variable had not otherwise changed - /// This updates all the variables in the list, where in each index \a varsWritten is true, so will the variable at the corresponding index be flagged dirty - void FlagDirtyFromBitArray(unsigned char *bArray); - - /// \internal - struct VariableLastValueNode - { - VariableLastValueNode(); - VariableLastValueNode(const unsigned char *data, int _byteLength); - ~VariableLastValueNode(); - char *lastData; - unsigned int byteLength; - bool isDirty; - }; - -protected: - /// \internal - DataStructures::List variableList; - /// \internal - unsigned int nextWriteIndex; -}; - - -} +#include "NativeTypes.h" +#include "DS_List.h" +#include "RakMemoryOverride.h" +#include "BitStream.h" + +namespace RakNet +{ +/// Class to write a series of variables, copy the contents to memory, and return if the newly written value is different than what was last written +/// Can also encode the reads, writes, and results directly to/from a bitstream +class VariableListDeltaTracker +{ +public: + VariableListDeltaTracker(); + ~VariableListDeltaTracker(); + + // Call before using a series of WriteVar + void StartWrite(void); + + bool IsPastEndOfList(void) const {return nextWriteIndex>=variableList.Size();} + + /// Records the passed value of the variable to memory, and returns true if the value is different from the write before that (or if it is the first write) + /// \pre Call StartWrite() before doing the first of a series of calls to WriteVar or other functions that call WriteVar + /// \note Variables must be of the same type, written in the same order, each time + template + bool WriteVar(const VarType &varData) + { + RakNet::BitStream temp; + temp.Write(varData); + if (nextWriteIndex>=variableList.Size()) + { + variableList.Push(VariableLastValueNode(temp.GetData(),temp.GetNumberOfBytesUsed()),_FILE_AND_LINE_); + nextWriteIndex++; + return true; // Different because it's new + } + + if (temp.GetNumberOfBytesUsed()!=variableList[nextWriteIndex].byteLength) + { + variableList[nextWriteIndex].lastData=(char*) rakRealloc_Ex(variableList[nextWriteIndex].lastData, temp.GetNumberOfBytesUsed(),_FILE_AND_LINE_); + variableList[nextWriteIndex].byteLength=temp.GetNumberOfBytesUsed(); + memcpy(variableList[nextWriteIndex].lastData,temp.GetData(),temp.GetNumberOfBytesUsed()); + nextWriteIndex++; + variableList[nextWriteIndex].isDirty=false; + return true; // Different because the serialized size is different + } + if (variableList[nextWriteIndex].isDirty==false && memcmp(temp.GetData(),variableList[nextWriteIndex].lastData, variableList[nextWriteIndex].byteLength)==0) + { + nextWriteIndex++; + return false; // Same because not dirty and memcmp is the same + } + + variableList[nextWriteIndex].isDirty=false; + memcpy(variableList[nextWriteIndex].lastData,temp.GetData(),temp.GetNumberOfBytesUsed()); + nextWriteIndex++; + return true; // Different because dirty or memcmp was different + } + /// Calls WriteVar. If the variable has changed, writes true, and writes the variable. Otherwise writes false. + template + bool WriteVarToBitstream(const VarType &varData, RakNet::BitStream *bitStream) + { + bool wasDifferent = WriteVar(varData); + bitStream->Write(wasDifferent); + if (wasDifferent) + { + bitStream->Write(varData); + return true; + } + return false; + } + /// Calls WriteVarToBitstream(). Additionally, adds the boolean result of WriteVar() to boolean bit array + template + bool WriteVarToBitstream(const VarType &varData, RakNet::BitStream *bitStream, unsigned char *bArray, unsigned short writeOffset) + { + if (WriteVarToBitstream(varData,bitStream)==true) + { + BitSize_t numberOfBitsMod8 = writeOffset & 7; + + if ( numberOfBitsMod8 == 0 ) + bArray[ writeOffset >> 3 ] = 0x80; + else + bArray[ writeOffset >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 + + return true; + } + else + { + if ( ( writeOffset & 7 ) == 0 ) + bArray[ writeOffset >> 3 ] = 0; + + return false; + } + } + + /// Paired with a call to WriteVarToBitstream(), will read a variable if it had changed. Otherwise the values remains the same. + template + static bool ReadVarFromBitstream(const VarType &varData, RakNet::BitStream *bitStream) + { + bool wasWritten; + if (bitStream->Read(wasWritten)==false) + return false; + if (wasWritten) + { + if (bitStream->Read(varData)==false) + return false; + } + return wasWritten; + } + + /// Variables flagged dirty will cause WriteVar() to return true, even if the variable had not otherwise changed + /// This updates all the variables in the list, where in each index \a varsWritten is true, so will the variable at the corresponding index be flagged dirty + void FlagDirtyFromBitArray(unsigned char *bArray); + + /// \internal + struct VariableLastValueNode + { + VariableLastValueNode(); + VariableLastValueNode(const unsigned char *data, int _byteLength); + ~VariableLastValueNode(); + char *lastData; + unsigned int byteLength; + bool isDirty; + }; + +protected: + /// \internal + DataStructures::List variableList; + /// \internal + unsigned int nextWriteIndex; +}; + + +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/VariadicSQLParser.cpp b/project/lib_projects/raknet/jni/RaknetSources/VariadicSQLParser.cpp index bc8e3fc..c0b9204 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/VariadicSQLParser.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/VariadicSQLParser.cpp @@ -1,150 +1,150 @@ -#include "VariadicSQLParser.h" -#include "BitStream.h" -#include - -using namespace VariadicSQLParser; - -struct TypeMapping -{ - char inputType; - const char *type; -}; -const int NUM_TYPE_MAPPINGS=7; -TypeMapping typeMappings[NUM_TYPE_MAPPINGS] = -{ - {'i', "int"}, - {'d', "int"}, - {'s', "text"}, - {'b', "bool"}, - {'f', "numeric"}, - {'g', "double precision"}, - {'a', "bytea"}, -}; -unsigned int GetTypeMappingIndex(char c) -{ - unsigned int i; - for (i=0; i < (unsigned int) NUM_TYPE_MAPPINGS; i++ ) - if (typeMappings[i].inputType==c) - return i; - return (unsigned int)-1; -} -const char* VariadicSQLParser::GetTypeMappingAtIndex(int i) -{ - return typeMappings[i].type; -} -void VariadicSQLParser::GetTypeMappingIndices( const char *format, DataStructures::List &indices ) -{ - bool previousCharWasPercentSign; - unsigned int i; - unsigned int typeMappingIndex; - indices.Clear(false, _FILE_AND_LINE_); - unsigned int len = (unsigned int) strlen(format); - previousCharWasPercentSign=false; - for (i=0; i < len; i++) - { - if (previousCharWasPercentSign==true ) - { - typeMappingIndex = GetTypeMappingIndex(format[i]); - if (typeMappingIndex!=(unsigned int) -1) - { - IndexAndType iat; - iat.strIndex=i-1; - iat.typeMappingIndex=typeMappingIndex; - indices.Insert(iat, _FILE_AND_LINE_ ); - } - } - - previousCharWasPercentSign=format[i]=='%'; - } -} -void VariadicSQLParser::ExtractArguments( va_list argptr, const DataStructures::List &indices, char ***argumentBinary, int **argumentLengths ) -{ - if (indices.Size()==0) - return; - - unsigned int i; - *argumentBinary=RakNet::OP_NEW_ARRAY(indices.Size(), _FILE_AND_LINE_); - *argumentLengths=RakNet::OP_NEW_ARRAY(indices.Size(), _FILE_AND_LINE_); - - char **paramData=*argumentBinary; - int *paramLength=*argumentLengths; - - int variadicArgIndex; - for (variadicArgIndex=0, i=0; i < indices.Size(); i++, variadicArgIndex++) - { - switch (typeMappings[indices[i].typeMappingIndex].inputType) - { - case 'i': - case 'd': - { - int val = va_arg( argptr, int ); - paramLength[i]=sizeof(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], &val, paramLength[i]); - if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); - } - break; - case 's': - { - char* val = va_arg( argptr, char* ); - paramLength[i]=(int) strlen(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i]+1, _FILE_AND_LINE_); - memcpy(paramData[i], val, paramLength[i]+1); - } - break; - case 'b': - { - bool val = (va_arg( argptr, int )!=0); - paramLength[i]=sizeof(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], &val, paramLength[i]); - if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); - } - break; - /* - case 'f': - { - // On MSVC at least, this only works with double as the 2nd param - float val = (float) va_arg( argptr, double ); - //float val = va_arg( argptr, float ); - paramLength[i]=sizeof(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], &val, paramLength[i]); - if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); - } - break; - */ - // On MSVC at least, this only works with double as the 2nd param - case 'f': - case 'g': - { - double val = va_arg( argptr, double ); - paramLength[i]=sizeof(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], &val, paramLength[i]); - if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); - } - break; - case 'a': - { - char* val = va_arg( argptr, char* ); - paramLength[i]=va_arg( argptr, unsigned int ); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], val, paramLength[i]); - } - break; - } - } - -} -void VariadicSQLParser::FreeArguments(const DataStructures::List &indices, char **argumentBinary, int *argumentLengths) -{ - if (indices.Size()==0) - return; - - unsigned int i; - for (i=0; i < indices.Size(); i++) - rakFree_Ex(argumentBinary[i],_FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(argumentBinary,_FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(argumentLengths,_FILE_AND_LINE_); -} +#include "VariadicSQLParser.h" +#include "BitStream.h" +#include + +using namespace VariadicSQLParser; + +struct TypeMapping +{ + char inputType; + const char *type; +}; +const int NUM_TYPE_MAPPINGS=7; +TypeMapping typeMappings[NUM_TYPE_MAPPINGS] = +{ + {'i', "int"}, + {'d', "int"}, + {'s', "text"}, + {'b', "bool"}, + {'f', "numeric"}, + {'g', "double precision"}, + {'a', "bytea"}, +}; +unsigned int GetTypeMappingIndex(char c) +{ + unsigned int i; + for (i=0; i < (unsigned int) NUM_TYPE_MAPPINGS; i++ ) + if (typeMappings[i].inputType==c) + return i; + return (unsigned int)-1; +} +const char* VariadicSQLParser::GetTypeMappingAtIndex(int i) +{ + return typeMappings[i].type; +} +void VariadicSQLParser::GetTypeMappingIndices( const char *format, DataStructures::List &indices ) +{ + bool previousCharWasPercentSign; + unsigned int i; + unsigned int typeMappingIndex; + indices.Clear(false, _FILE_AND_LINE_); + unsigned int len = (unsigned int) strlen(format); + previousCharWasPercentSign=false; + for (i=0; i < len; i++) + { + if (previousCharWasPercentSign==true ) + { + typeMappingIndex = GetTypeMappingIndex(format[i]); + if (typeMappingIndex!=(unsigned int) -1) + { + IndexAndType iat; + iat.strIndex=i-1; + iat.typeMappingIndex=typeMappingIndex; + indices.Insert(iat, _FILE_AND_LINE_ ); + } + } + + previousCharWasPercentSign=format[i]=='%'; + } +} +void VariadicSQLParser::ExtractArguments( va_list argptr, const DataStructures::List &indices, char ***argumentBinary, int **argumentLengths ) +{ + if (indices.Size()==0) + return; + + unsigned int i; + *argumentBinary=RakNet::OP_NEW_ARRAY(indices.Size(), _FILE_AND_LINE_); + *argumentLengths=RakNet::OP_NEW_ARRAY(indices.Size(), _FILE_AND_LINE_); + + char **paramData=*argumentBinary; + int *paramLength=*argumentLengths; + + int variadicArgIndex; + for (variadicArgIndex=0, i=0; i < indices.Size(); i++, variadicArgIndex++) + { + switch (typeMappings[indices[i].typeMappingIndex].inputType) + { + case 'i': + case 'd': + { + int val = va_arg( argptr, int ); + paramLength[i]=sizeof(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], &val, paramLength[i]); + if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); + } + break; + case 's': + { + char* val = va_arg( argptr, char* ); + paramLength[i]=(int) strlen(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i]+1, _FILE_AND_LINE_); + memcpy(paramData[i], val, paramLength[i]+1); + } + break; + case 'b': + { + bool val = (va_arg( argptr, int )!=0); + paramLength[i]=sizeof(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], &val, paramLength[i]); + if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); + } + break; + /* + case 'f': + { + // On MSVC at least, this only works with double as the 2nd param + float val = (float) va_arg( argptr, double ); + //float val = va_arg( argptr, float ); + paramLength[i]=sizeof(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], &val, paramLength[i]); + if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); + } + break; + */ + // On MSVC at least, this only works with double as the 2nd param + case 'f': + case 'g': + { + double val = va_arg( argptr, double ); + paramLength[i]=sizeof(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], &val, paramLength[i]); + if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); + } + break; + case 'a': + { + char* val = va_arg( argptr, char* ); + paramLength[i]=va_arg( argptr, unsigned int ); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], val, paramLength[i]); + } + break; + } + } + +} +void VariadicSQLParser::FreeArguments(const DataStructures::List &indices, char **argumentBinary, int *argumentLengths) +{ + if (indices.Size()==0) + return; + + unsigned int i; + for (i=0; i < indices.Size(); i++) + rakFree_Ex(argumentBinary[i],_FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(argumentBinary,_FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(argumentLengths,_FILE_AND_LINE_); +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/VariadicSQLParser.h b/project/lib_projects/raknet/jni/RaknetSources/VariadicSQLParser.h index a176751..96b1c36 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/VariadicSQLParser.h +++ b/project/lib_projects/raknet/jni/RaknetSources/VariadicSQLParser.h @@ -1,24 +1,24 @@ -#ifndef __VARIADIC_SQL_PARSER_H -#define __VARIADIC_SQL_PARSER_H - -#include "DS_List.h" - -#include - -namespace VariadicSQLParser -{ - struct IndexAndType - { - unsigned int strIndex; - unsigned int typeMappingIndex; - }; - const char* GetTypeMappingAtIndex(int i); - void GetTypeMappingIndices( const char *format, DataStructures::List &indices ); - // Given an SQL string with variadic arguments, allocate argumentBinary and argumentLengths, and hold the parameters in binary format - // Last 2 parameters are out parameters - void ExtractArguments( va_list argptr, const DataStructures::List &indices, char ***argumentBinary, int **argumentLengths ); - void FreeArguments(const DataStructures::List &indices, char **argumentBinary, int *argumentLengths); -} - - -#endif +#ifndef __VARIADIC_SQL_PARSER_H +#define __VARIADIC_SQL_PARSER_H + +#include "DS_List.h" + +#include + +namespace VariadicSQLParser +{ + struct IndexAndType + { + unsigned int strIndex; + unsigned int typeMappingIndex; + }; + const char* GetTypeMappingAtIndex(int i); + void GetTypeMappingIndices( const char *format, DataStructures::List &indices ); + // Given an SQL string with variadic arguments, allocate argumentBinary and argumentLengths, and hold the parameters in binary format + // Last 2 parameters are out parameters + void ExtractArguments( va_list argptr, const DataStructures::List &indices, char ***argumentBinary, int **argumentLengths ); + void FreeArguments(const DataStructures::List &indices, char **argumentBinary, int *argumentLengths); +} + + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/VitaIncludes.cpp b/project/lib_projects/raknet/jni/RaknetSources/VitaIncludes.cpp index fa0e62d..5e567bf 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/VitaIncludes.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/VitaIncludes.cpp @@ -1,163 +1,163 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project/lib_projects/raknet/jni/RaknetSources/VitaIncludes.h b/project/lib_projects/raknet/jni/RaknetSources/VitaIncludes.h index 062dad9..bd70cd1 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/VitaIncludes.h +++ b/project/lib_projects/raknet/jni/RaknetSources/VitaIncludes.h @@ -1,57 +1,57 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project/lib_projects/raknet/jni/RaknetSources/WSAStartupSingleton.cpp b/project/lib_projects/raknet/jni/RaknetSources/WSAStartupSingleton.cpp index 17e848b..f808214 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/WSAStartupSingleton.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/WSAStartupSingleton.cpp @@ -1,75 +1,75 @@ -#include "WSAStartupSingleton.h" - - - - - -#if defined(_WIN32) -#include -#include - - - - - -#endif -#include "RakNetDefines.h" -#include - -int WSAStartupSingleton::refCount=0; - -WSAStartupSingleton::WSAStartupSingleton() {} -WSAStartupSingleton::~WSAStartupSingleton() {} -void WSAStartupSingleton::AddRef(void) -{ -#ifdef _WIN32 - - refCount++; - - if (refCount!=1) - return; - - - - - - WSADATA winsockInfo; - if ( WSAStartup( MAKEWORD( 2, 2 ), &winsockInfo ) != 0 ) - { -#if defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "WSAStartup failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#endif - } - -#endif -} -void WSAStartupSingleton::Deref(void) -{ -#ifdef _WIN32 - if (refCount==0) - return; - - if (refCount>1) - { - refCount--; - return; - } - - WSACleanup(); - - - - - - - refCount=0; -#endif -} +#include "WSAStartupSingleton.h" + + + + + +#if defined(_WIN32) +#include +#include + + + + + +#endif +#include "RakNetDefines.h" +#include + +int WSAStartupSingleton::refCount=0; + +WSAStartupSingleton::WSAStartupSingleton() {} +WSAStartupSingleton::~WSAStartupSingleton() {} +void WSAStartupSingleton::AddRef(void) +{ +#ifdef _WIN32 + + refCount++; + + if (refCount!=1) + return; + + + + + + WSADATA winsockInfo; + if ( WSAStartup( MAKEWORD( 2, 2 ), &winsockInfo ) != 0 ) + { +#if defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "WSAStartup failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#endif + } + +#endif +} +void WSAStartupSingleton::Deref(void) +{ +#ifdef _WIN32 + if (refCount==0) + return; + + if (refCount>1) + { + refCount--; + return; + } + + WSACleanup(); + + + + + + + refCount=0; +#endif +} diff --git a/project/lib_projects/raknet/jni/RaknetSources/WSAStartupSingleton.h b/project/lib_projects/raknet/jni/RaknetSources/WSAStartupSingleton.h index d2930fe..2ac9042 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/WSAStartupSingleton.h +++ b/project/lib_projects/raknet/jni/RaknetSources/WSAStartupSingleton.h @@ -1,16 +1,16 @@ -#ifndef __WSA_STARTUP_SINGLETON_H -#define __WSA_STARTUP_SINGLETON_H - -class WSAStartupSingleton -{ -public: - WSAStartupSingleton(); - ~WSAStartupSingleton(); - static void AddRef(void); - static void Deref(void); - -protected: - static int refCount; -}; - -#endif +#ifndef __WSA_STARTUP_SINGLETON_H +#define __WSA_STARTUP_SINGLETON_H + +class WSAStartupSingleton +{ +public: + WSAStartupSingleton(); + ~WSAStartupSingleton(); + static void AddRef(void); + static void Deref(void); + +protected: + static int refCount; +}; + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/WindowsIncludes.h b/project/lib_projects/raknet/jni/RaknetSources/WindowsIncludes.h index 828614c..9bec7bf 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/WindowsIncludes.h +++ b/project/lib_projects/raknet/jni/RaknetSources/WindowsIncludes.h @@ -1,17 +1,17 @@ -#if defined(X360__) -#elif defined (_WIN32) -#include -#include -#include - -// Must always include Winsock2.h before windows.h -// or else: -// winsock2.h(99) : error C2011: 'fd_set' : 'struct' type redefinition -// winsock2.h(134) : warning C4005: 'FD_SET' : macro redefinition -// winsock.h(83) : see previous definition of 'FD_SET' -// winsock2.h(143) : error C2011: 'timeval' : 'struct' type redefinition -// winsock2.h(199) : error C2011: 'hostent' : 'struct' type redefinition -// winsock2.h(212) : error C2011: 'netent' : 'struct' type redefinition -// winsock2.h(219) : error C2011: 'servent' : 'struct' type redefinition - -#endif +#if defined(X360__) +#elif defined (_WIN32) +#include +#include +#include + +// Must always include Winsock2.h before windows.h +// or else: +// winsock2.h(99) : error C2011: 'fd_set' : 'struct' type redefinition +// winsock2.h(134) : warning C4005: 'FD_SET' : macro redefinition +// winsock.h(83) : see previous definition of 'FD_SET' +// winsock2.h(143) : error C2011: 'timeval' : 'struct' type redefinition +// winsock2.h(199) : error C2011: 'hostent' : 'struct' type redefinition +// winsock2.h(212) : error C2011: 'netent' : 'struct' type redefinition +// winsock2.h(219) : error C2011: 'servent' : 'struct' type redefinition + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/XBox360Includes.h b/project/lib_projects/raknet/jni/RaknetSources/XBox360Includes.h index 7896bec..ad3f39d 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/XBox360Includes.h +++ b/project/lib_projects/raknet/jni/RaknetSources/XBox360Includes.h @@ -1,55 +1,55 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project/lib_projects/raknet/jni/RaknetSources/_FindFirst.cpp b/project/lib_projects/raknet/jni/RaknetSources/_FindFirst.cpp index 6db92ac..8a697c1 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/_FindFirst.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/_FindFirst.cpp @@ -1,159 +1,159 @@ -/** -* Original file by the_viking, fixed by R√¥mulo Fernandes, fixed by Emmanuel Nars -* Should emulate windows finddata structure -*/ -#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_WIN32) -#include "_FindFirst.h" -#include "DS_List.h" - -#include - -#include - - -static DataStructures::List< _findinfo_t* > fileInfo; - -#include "RakMemoryOverride.h" -#include "RakAssert.h" - -/** -* _findfirst - equivalent -*/ -long _findfirst(const char *name, _finddata_t *f) -{ - RakNet::RakString nameCopy = name; - RakNet::RakString filter; - - // This is linux only, so don't bother with '\' - const char* lastSep = strrchr(name,'/'); - if(!lastSep) - { - // filter pattern only is given, search current directory. - filter = nameCopy; - nameCopy = "."; - } else - { - // strip filter pattern from directory name, leave - // trailing '/' intact. - filter = lastSep+1; - unsigned sepIndex = lastSep - name; - nameCopy.Erase(sepIndex+1, nameCopy.GetLength() - sepIndex-1); - } - - DIR* dir = opendir(nameCopy); - - if(!dir) return -1; - - _findinfo_t* fi = RakNet::OP_NEW<_findinfo_t>( _FILE_AND_LINE_ ); - fi->filter = filter; - fi->dirName = nameCopy; // we need to remember this for stat() - fi->openedDir = dir; - fileInfo.Insert(fi, _FILE_AND_LINE_); - - long ret = fileInfo.Size()-1; - - // Retrieve the first file. We cannot rely on the first item - // being '.' - if (_findnext(ret, f) == -1) return -1; - else return ret; -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int _findnext(long h, _finddata_t *f) -{ - RakAssert(h >= 0 && h < (long)fileInfo.Size()); - if (h < 0 || h >= (long)fileInfo.Size()) return -1; - - _findinfo_t* fi = fileInfo[h]; - - while(true) - { - dirent* entry = readdir(fi->openedDir); - if(entry == 0) return -1; - - // Only report stuff matching our filter - if (fnmatch(fi->filter, entry->d_name, FNM_PATHNAME) != 0) continue; - - // To reliably determine the entry's type, we must do - // a stat... don't rely on entry->d_type, as this - // might be unavailable! - struct stat filestat; - RakNet::RakString fullPath = fi->dirName + entry->d_name; - if (stat(fullPath, &filestat) != 0) - { - RAKNET_DEBUG_PRINTF("Cannot stat %s\n", fullPath.C_String()); - continue; - } - - if (S_ISREG(filestat.st_mode)) - { - f->attrib = _A_NORMAL; - } else if (S_ISDIR(filestat.st_mode)) - { - f->attrib = _A_SUBDIR; - } else continue; // We are interested in files and - // directories only. Links currently - // are not supported. - - f->size = filestat.st_size; - strncpy(f->name, entry->d_name, STRING_BUFFER_SIZE); - - return 0; - } - - return -1; -} - - - - - -/** - * _findclose - equivalent - */ -int _findclose(long h) -{ - if (h==-1) return 0; - - if (h < 0 || h >= (long)fileInfo.Size()) - { - RakAssert(false); - return -1; - } - - _findinfo_t* fi = fileInfo[h]; - closedir(fi->openedDir); - fileInfo.RemoveAtIndex(h); - RakNet::OP_DELETE(fi, _FILE_AND_LINE_); - return 0; -} -#endif +/** +* Original file by the_viking, fixed by R√¥mulo Fernandes, fixed by Emmanuel Nars +* Should emulate windows finddata structure +*/ +#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_WIN32) +#include "_FindFirst.h" +#include "DS_List.h" + +#include + +#include + + +static DataStructures::List< _findinfo_t* > fileInfo; + +#include "RakMemoryOverride.h" +#include "RakAssert.h" + +/** +* _findfirst - equivalent +*/ +long _findfirst(const char *name, _finddata_t *f) +{ + RakNet::RakString nameCopy = name; + RakNet::RakString filter; + + // This is linux only, so don't bother with '\' + const char* lastSep = strrchr(name,'/'); + if(!lastSep) + { + // filter pattern only is given, search current directory. + filter = nameCopy; + nameCopy = "."; + } else + { + // strip filter pattern from directory name, leave + // trailing '/' intact. + filter = lastSep+1; + unsigned sepIndex = lastSep - name; + nameCopy.Erase(sepIndex+1, nameCopy.GetLength() - sepIndex-1); + } + + DIR* dir = opendir(nameCopy); + + if(!dir) return -1; + + _findinfo_t* fi = RakNet::OP_NEW<_findinfo_t>( _FILE_AND_LINE_ ); + fi->filter = filter; + fi->dirName = nameCopy; // we need to remember this for stat() + fi->openedDir = dir; + fileInfo.Insert(fi, _FILE_AND_LINE_); + + long ret = fileInfo.Size()-1; + + // Retrieve the first file. We cannot rely on the first item + // being '.' + if (_findnext(ret, f) == -1) return -1; + else return ret; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +int _findnext(long h, _finddata_t *f) +{ + RakAssert(h >= 0 && h < (long)fileInfo.Size()); + if (h < 0 || h >= (long)fileInfo.Size()) return -1; + + _findinfo_t* fi = fileInfo[h]; + + while(true) + { + dirent* entry = readdir(fi->openedDir); + if(entry == 0) return -1; + + // Only report stuff matching our filter + if (fnmatch(fi->filter, entry->d_name, FNM_PATHNAME) != 0) continue; + + // To reliably determine the entry's type, we must do + // a stat... don't rely on entry->d_type, as this + // might be unavailable! + struct stat filestat; + RakNet::RakString fullPath = fi->dirName + entry->d_name; + if (stat(fullPath, &filestat) != 0) + { + RAKNET_DEBUG_PRINTF("Cannot stat %s\n", fullPath.C_String()); + continue; + } + + if (S_ISREG(filestat.st_mode)) + { + f->attrib = _A_NORMAL; + } else if (S_ISDIR(filestat.st_mode)) + { + f->attrib = _A_SUBDIR; + } else continue; // We are interested in files and + // directories only. Links currently + // are not supported. + + f->size = filestat.st_size; + strncpy(f->name, entry->d_name, STRING_BUFFER_SIZE); + + return 0; + } + + return -1; +} + + + + + +/** + * _findclose - equivalent + */ +int _findclose(long h) +{ + if (h==-1) return 0; + + if (h < 0 || h >= (long)fileInfo.Size()) + { + RakAssert(false); + return -1; + } + + _findinfo_t* fi = fileInfo[h]; + closedir(fi->openedDir); + fileInfo.RemoveAtIndex(h); + RakNet::OP_DELETE(fi, _FILE_AND_LINE_); + return 0; +} +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/_FindFirst.h b/project/lib_projects/raknet/jni/RaknetSources/_FindFirst.h index a664222..6305af7 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/_FindFirst.h +++ b/project/lib_projects/raknet/jni/RaknetSources/_FindFirst.h @@ -1,56 +1,56 @@ -/// -/// Original file by the_viking, fixed by Rômulo Fernandes -/// Should emulate windows finddata structure -/// - -#ifndef GCC_FINDFIRST_H -#define GCC_FINDFIRST_H - -#if (defined(__GNUC__) || defined(__ARMCC_VERSION) || defined(__GCCXML__) || defined(__S3E__) ) && !defined(__WIN32) - -#include - -#include "RakString.h" - -#define _A_NORMAL 0x00 // Normal file -#define _A_RDONLY 0x01 // Read-only file -#define _A_HIDDEN 0x02 // Hidden file -#define _A_SYSTEM 0x04 // System file -#define _A_VOLID 0x08 // Volume ID -#define _A_SUBDIR 0x10 // Subdirectory -#define _A_ARCH 0x20 // File changed since last archive -#define FA_NORMAL 0x00 // Synonym of _A_NORMAL -#define FA_RDONLY 0x01 // Synonym of _A_RDONLY -#define FA_HIDDEN 0x02 // Synonym of _A_HIDDEN -#define FA_SYSTEM 0x04 // Synonym of _A_SYSTEM -#define FA_LABEL 0x08 // Synonym of _A_VOLID -#define FA_DIREC 0x10 // Synonym of _A_SUBDIR -#define FA_ARCH 0x20 // Synonym of _A_ARCH - - -const unsigned STRING_BUFFER_SIZE = 512; - -typedef struct _finddata_t -{ - char name[STRING_BUFFER_SIZE]; - int attrib; - unsigned long size; -} _finddata; - -/** - * Hold information about the current search - */ -typedef struct _findinfo_t -{ - DIR* openedDir; - RakNet::RakString filter; - RakNet::RakString dirName; -} _findinfo; - -long _findfirst(const char *name, _finddata_t *f); -int _findnext(long h, _finddata_t *f); -int _findclose(long h); - -#endif -#endif - +/// +/// Original file by the_viking, fixed by Rômulo Fernandes +/// Should emulate windows finddata structure +/// + +#ifndef GCC_FINDFIRST_H +#define GCC_FINDFIRST_H + +#if (defined(__GNUC__) || defined(__ARMCC_VERSION) || defined(__GCCXML__) || defined(__S3E__) ) && !defined(__WIN32) + +#include + +#include "RakString.h" + +#define _A_NORMAL 0x00 // Normal file +#define _A_RDONLY 0x01 // Read-only file +#define _A_HIDDEN 0x02 // Hidden file +#define _A_SYSTEM 0x04 // System file +#define _A_VOLID 0x08 // Volume ID +#define _A_SUBDIR 0x10 // Subdirectory +#define _A_ARCH 0x20 // File changed since last archive +#define FA_NORMAL 0x00 // Synonym of _A_NORMAL +#define FA_RDONLY 0x01 // Synonym of _A_RDONLY +#define FA_HIDDEN 0x02 // Synonym of _A_HIDDEN +#define FA_SYSTEM 0x04 // Synonym of _A_SYSTEM +#define FA_LABEL 0x08 // Synonym of _A_VOLID +#define FA_DIREC 0x10 // Synonym of _A_SUBDIR +#define FA_ARCH 0x20 // Synonym of _A_ARCH + + +const unsigned STRING_BUFFER_SIZE = 512; + +typedef struct _finddata_t +{ + char name[STRING_BUFFER_SIZE]; + int attrib; + unsigned long size; +} _finddata; + +/** + * Hold information about the current search + */ +typedef struct _findinfo_t +{ + DIR* openedDir; + RakNet::RakString filter; + RakNet::RakString dirName; +} _findinfo; + +long _findfirst(const char *name, _finddata_t *f); +int _findnext(long h, _finddata_t *f); +int _findclose(long h); + +#endif +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/gettimeofday.cpp b/project/lib_projects/raknet/jni/RaknetSources/gettimeofday.cpp index 6b2e1c0..917330a 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/gettimeofday.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/gettimeofday.cpp @@ -1,55 +1,55 @@ - - - - -#if defined(_WIN32) && !defined(__GNUC__) &&!defined(__GCCXML__) - -#include "gettimeofday.h" - -// From http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/ - -#include "WindowsIncludes.h" - -#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) - #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 -#else - #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL -#endif - -int gettimeofday(struct timeval *tv, struct timezone *tz) -{ - FILETIME ft; - unsigned __int64 tmpres = 0; - static int tzflag; - - if (NULL != tv) - { - GetSystemTimeAsFileTime(&ft); - - tmpres |= ft.dwHighDateTime; - tmpres <<= 32; - tmpres |= ft.dwLowDateTime; - - /*converting file time to unix epoch*/ - tmpres /= 10; /*convert into microseconds*/ - tmpres -= DELTA_EPOCH_IN_MICROSECS; - tv->tv_sec = (long)(tmpres / 1000000UL); - tv->tv_usec = (long)(tmpres % 1000000UL); - } - - if (NULL != tz) - { - if (!tzflag) - { - _tzset(); - tzflag++; - } - tz->tz_minuteswest = _timezone / 60; - tz->tz_dsttime = _daylight; - } - - return 0; -} - -#endif - + + + + +#if defined(_WIN32) && !defined(__GNUC__) &&!defined(__GCCXML__) + +#include "gettimeofday.h" + +// From http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/ + +#include "WindowsIncludes.h" + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch*/ + tmpres /= 10; /*convert into microseconds*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) + { + if (!tzflag) + { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} + +#endif + diff --git a/project/lib_projects/raknet/jni/RaknetSources/gettimeofday.h b/project/lib_projects/raknet/jni/RaknetSources/gettimeofday.h index 52c4207..5e3b639 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/gettimeofday.h +++ b/project/lib_projects/raknet/jni/RaknetSources/gettimeofday.h @@ -1,56 +1,56 @@ -#ifndef __GET_TIME_OF_DAY_H -#define __GET_TIME_OF_DAY_H - -#if defined(_WIN32) && !defined(__GNUC__) &&!defined(__GCCXML__) -#include < time.h > -struct timezone -{ - int tz_minuteswest; /* minutes W of Greenwich */ - int tz_dsttime; /* type of dst correction */ -}; -int gettimeofday(struct timeval *tv, struct timezone *tz); - - -#else - - - - -#include - -#include - -// Uncomment this if you need to -/* -// http://www.halcode.com/archives/2008/08/26/retrieving-system-time-gettimeofday/ -struct timezone -{ - int tz_minuteswest; - int tz_dsttime; -}; - -#ifdef __cplusplus - -void GetSystemTimeAsFileTime(FILETIME*); - -inline int gettimeofday(struct timeval* p, void* tz ) -{ - union { - long long ns100; // time since 1 Jan 1601 in 100ns units - FILETIME ft; - } now; - - GetSystemTimeAsFileTime( &(now.ft) ); - p->tv_usec=(long)((now.ns100 / 10LL) % 1000000LL ); - p->tv_sec= (long)((now.ns100-(116444736000000000LL))/10000000LL); - return 0; -} - -#else - int gettimeofday(struct timeval* p, void* tz ); -#endif -*/ - -#endif - -#endif +#ifndef __GET_TIME_OF_DAY_H +#define __GET_TIME_OF_DAY_H + +#if defined(_WIN32) && !defined(__GNUC__) &&!defined(__GCCXML__) +#include < time.h > +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; +int gettimeofday(struct timeval *tv, struct timezone *tz); + + +#else + + + + +#include + +#include + +// Uncomment this if you need to +/* +// http://www.halcode.com/archives/2008/08/26/retrieving-system-time-gettimeofday/ +struct timezone +{ + int tz_minuteswest; + int tz_dsttime; +}; + +#ifdef __cplusplus + +void GetSystemTimeAsFileTime(FILETIME*); + +inline int gettimeofday(struct timeval* p, void* tz ) +{ + union { + long long ns100; // time since 1 Jan 1601 in 100ns units + FILETIME ft; + } now; + + GetSystemTimeAsFileTime( &(now.ft) ); + p->tv_usec=(long)((now.ns100 / 10LL) % 1000000LL ); + p->tv_sec= (long)((now.ns100-(116444736000000000LL))/10000000LL); + return 0; +} + +#else + int gettimeofday(struct timeval* p, void* tz ); +#endif +*/ + +#endif + +#endif diff --git a/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc-options.h b/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc-options.h index 37e9887..9f2e091 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc-options.h +++ b/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc-options.h @@ -1,28 +1,28 @@ -#ifdef _RAKNET_SUPPORT_DL_MALLOC - -#ifndef __DLMALLOC_OPTIONS_H -#define __DLMALLOC_OPTIONS_H - -#include "NativeTypes.h" -#include "Export.h" -#include -#include - -#define MSPACES 1 -#define ONLY_MSPACES 1 -#define USE_DL_PREFIX 1 -#define NO_MALLINFO 1 -// Make it threadsafe -#define USE_LOCKS 1 - -extern RAK_DLL_EXPORT void * (*dlMallocMMap) (size_t size); -extern RAK_DLL_EXPORT void * (*dlMallocDirectMMap) (size_t size); -extern RAK_DLL_EXPORT int (*dlMallocMUnmap) (void* ptr, size_t size); - -#define MMAP(s) dlMallocMMap(s) -#define MUNMAP(a, s) dlMallocMUnmap((a), (s)) -#define DIRECT_MMAP(s) dlMallocDirectMMap(s) - -#endif - +#ifdef _RAKNET_SUPPORT_DL_MALLOC + +#ifndef __DLMALLOC_OPTIONS_H +#define __DLMALLOC_OPTIONS_H + +#include "NativeTypes.h" +#include "Export.h" +#include +#include + +#define MSPACES 1 +#define ONLY_MSPACES 1 +#define USE_DL_PREFIX 1 +#define NO_MALLINFO 1 +// Make it threadsafe +#define USE_LOCKS 1 + +extern RAK_DLL_EXPORT void * (*dlMallocMMap) (size_t size); +extern RAK_DLL_EXPORT void * (*dlMallocDirectMMap) (size_t size); +extern RAK_DLL_EXPORT int (*dlMallocMUnmap) (void* ptr, size_t size); + +#define MMAP(s) dlMallocMMap(s) +#define MUNMAP(a, s) dlMallocMUnmap((a), (s)) +#define DIRECT_MMAP(s) dlMallocDirectMMap(s) + +#endif + #endif // _RAKNET_SUPPORT_DL_MALLOC \ No newline at end of file diff --git a/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc.cpp b/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc.cpp index 2eb71f0..420ba6f 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc.cpp +++ b/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc.cpp @@ -1,4161 +1,4161 @@ -#ifdef _RAKNET_SUPPORT_DL_MALLOC - -#include "rdlmalloc.h" - -/* --------------------------- Lock preliminaries ------------------------ */ - -/* -When locks are defined, there is one global lock, plus -one per-mspace lock. - -The global lock_ensures that mparams.magic and other unique -mparams values are initialized only once. It also protects -sequences of calls to MORECORE. In many cases sys_alloc requires -two calls, that should not be interleaved with calls by other -threads. This does not protect against direct calls to MORECORE -by other threads not using this lock, so there is still code to -cope the best we can on interference. - -Per-mspace locks surround calls to malloc, free, etc. To enable use -in layered extensions, per-mspace locks are reentrant. - -Because lock-protected regions generally have bounded times, it is -OK to use the supplied simple spinlocks in the custom versions for -x86. Spinlocks are likely to improve performance for lightly -contended applications, but worsen performance under heavy -contention. - -If USE_LOCKS is > 1, the definitions of lock routines here are -bypassed, in which case you will need to define the type MLOCK_T, -and at least INITIAL_LOCK, ACQUIRE_LOCK, RELEASE_LOCK and possibly -TRY_LOCK (which is not used in this malloc, but commonly needed in -extensions.) You must also declare a -static MLOCK_T malloc_global_mutex = { initialization values };. - -*/ - -#if USE_LOCKS == 1 - -#if USE_SPIN_LOCKS && SPIN_LOCKS_AVAILABLE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#if !defined(DL_PLATFORM_WIN32) - -/* Custom pthread-style spin locks on x86 and x64 for gcc */ -struct pthread_mlock_t { - volatile unsigned int l; - unsigned int c; - pthread_t threadid; -}; -#define MLOCK_T struct pthread_mlock_t -#define CURRENT_THREAD pthread_self() -#define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) -#define ACQUIRE_LOCK(sl) pthread_acquire_lock(sl) -#define RELEASE_LOCK(sl) pthread_release_lock(sl) -#define TRY_LOCK(sl) pthread_try_lock(sl) -#define SPINS_PER_YIELD 63 - -static MLOCK_T malloc_global_mutex = { 0, 0, 0}; - -static FORCEINLINE int pthread_acquire_lock (MLOCK_T *sl) { - int spins = 0; - volatile unsigned int* lp = &sl->l; - for (;;) { - if (*lp != 0) { - if (sl->threadid == CURRENT_THREAD) { - ++sl->c; - return 0; - } - } - else { - /* place args to cmpxchgl in locals to evade oddities in some gccs */ - int cmp = 0; - int val = 1; - int ret; - __asm__ __volatile__ ("lock; cmpxchgl %1, %2" - : "=a" (ret) - : "r" (val), "m" (*(lp)), "0"(cmp) - : "memory", "cc"); - if (!ret) { - assert(!sl->threadid); - sl->threadid = CURRENT_THREAD; - sl->c = 1; - return 0; - } - } - if ((++spins & SPINS_PER_YIELD) == 0) { -#if defined (__SVR4) && defined (__sun) /* solaris */ - thr_yield(); -#else -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) - sched_yield(); -#else /* no-op yield on unknown systems */ - ; -#endif /* __linux__ || __FreeBSD__ || __APPLE__ */ -#endif /* solaris */ - } - } -} - -static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) { - volatile unsigned int* lp = &sl->l; - assert(*lp != 0); - assert(sl->threadid == CURRENT_THREAD); - if (--sl->c == 0) { - sl->threadid = 0; - int prev = 0; - int ret; - __asm__ __volatile__ ("lock; xchgl %0, %1" - : "=r" (ret) - : "m" (*(lp)), "0"(prev) - : "memory"); - } -} - -static FORCEINLINE int pthread_try_lock (MLOCK_T *sl) { - volatile unsigned int* lp = &sl->l; - if (*lp != 0) { - if (sl->threadid == CURRENT_THREAD) { - ++sl->c; - return 1; - } - } - else { - int cmp = 0; - int val = 1; - int ret; - __asm__ __volatile__ ("lock; cmpxchgl %1, %2" - : "=a" (ret) - : "r" (val), "m" (*(lp)), "0"(cmp) - : "memory", "cc"); - if (!ret) { - assert(!sl->threadid); - sl->threadid = CURRENT_THREAD; - sl->c = 1; - return 1; - } - } - return 0; -} - - -#else /* DL_PLATFORM_WIN32 */ -/* Custom win32-style spin locks on x86 and x64 for MSC */ -struct win32_mlock_t { - volatile long l; - unsigned int c; - long threadid; -}; - -#define MLOCK_T struct win32_mlock_t -#define CURRENT_THREAD GetCurrentThreadId() -#define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) -#define ACQUIRE_LOCK(sl) win32_acquire_lock(sl) -#define RELEASE_LOCK(sl) win32_release_lock(sl) -#define TRY_LOCK(sl) win32_try_lock(sl) -#define SPINS_PER_YIELD 63 - -static MLOCK_T malloc_global_mutex = { 0, 0, 0}; - -static FORCEINLINE int win32_acquire_lock (MLOCK_T *sl) { - int spins = 0; - for (;;) { - if (sl->l != 0) { - if (sl->threadid == CURRENT_THREAD) { - ++sl->c; - return 0; - } - } - else { - if (!interlockedexchange(&sl->l, 1)) { - assert(!sl->threadid); - sl->threadid = CURRENT_THREAD; - sl->c = 1; - return 0; - } - } - if ((++spins & SPINS_PER_YIELD) == 0) - SleepEx(0, FALSE); - } -} - -static FORCEINLINE void win32_release_lock (MLOCK_T *sl) { - assert(sl->threadid == CURRENT_THREAD); - assert(sl->l != 0); - if (--sl->c == 0) { - sl->threadid = 0; - interlockedexchange (&sl->l, 0); - } -} - -static FORCEINLINE int win32_try_lock (MLOCK_T *sl) { - if (sl->l != 0) { - if (sl->threadid == CURRENT_THREAD) { - ++sl->c; - return 1; - } - } - else { - if (!interlockedexchange(&sl->l, 1)){ - assert(!sl->threadid); - sl->threadid = CURRENT_THREAD; - sl->c = 1; - return 1; - } - } - return 0; -} - -#endif /* DL_PLATFORM_WIN32 */ -#else /* USE_SPIN_LOCKS */ - -#ifndef DL_PLATFORM_WIN32 -/* pthreads-based locks */ - -#define MLOCK_T pthread_mutex_t -#define CURRENT_THREAD pthread_self() -#define INITIAL_LOCK(sl) pthread_init_lock(sl) -#define ACQUIRE_LOCK(sl) pthread_mutex_lock(sl) -#define RELEASE_LOCK(sl) pthread_mutex_unlock(sl) -#define TRY_LOCK(sl) (!pthread_mutex_trylock(sl)) - -static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; - -/* Cope with old-style linux recursive lock initialization by adding */ -/* skipped internal declaration from pthread.h */ -#ifdef linux -#ifndef PTHREAD_MUTEX_RECURSIVE -extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, - int __kind)); -#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP -#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) -#endif -#endif - -static int pthread_init_lock (MLOCK_T *sl) { - pthread_mutexattr_t attr; - if (pthread_mutexattr_init(&attr)) return 1; - if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; - if (pthread_mutex_init(sl, &attr)) return 1; - if (pthread_mutexattr_destroy(&attr)) return 1; - return 0; -} - -#else /* DL_PLATFORM_WIN32 */ -/* Win32 critical sections */ -#define MLOCK_T CRITICAL_SECTION -#define CURRENT_THREAD GetCurrentThreadId() -#define INITIAL_LOCK(s) (!InitializeCriticalSectionAndSpinCount((s), 0x80000000|4000)) -#define ACQUIRE_LOCK(s) (EnterCriticalSection(sl), 0) -#define RELEASE_LOCK(s) LeaveCriticalSection(sl) -#define TRY_LOCK(s) TryEnterCriticalSection(sl) -#define NEED_GLOBAL_LOCK_INIT - -static MLOCK_T malloc_global_mutex; -static volatile long malloc_global_mutex_status; - -/* Use spin loop to initialize global lock */ -static void init_malloc_global_mutex() { - for (;;) { - long stat = malloc_global_mutex_status; - if (stat > 0) - return; - /* transition to < 0 while initializing, then to > 0) */ - if (stat == 0 && - interlockedcompareexchange(&malloc_global_mutex_status, -1, 0) == 0) { - InitializeCriticalSection(&malloc_global_mutex); - interlockedexchange(&malloc_global_mutex_status,1); - return; - } - SleepEx(0, FALSE); - } -} - -#endif /* DL_PLATFORM_WIN32 */ -#endif /* USE_SPIN_LOCKS */ -#endif /* USE_LOCKS == 1 */ - -/* ----------------------- User-defined locks ------------------------ */ - -#if USE_LOCKS > 1 -/* Define your own lock implementation here */ -/* #define INITIAL_LOCK(sl) ... */ -/* #define ACQUIRE_LOCK(sl) ... */ -/* #define RELEASE_LOCK(sl) ... */ -/* #define TRY_LOCK(sl) ... */ -/* static MLOCK_T malloc_global_mutex = ... */ -#endif /* USE_LOCKS > 1 */ - -/* ----------------------- Lock-based state ------------------------ */ - -#if USE_LOCKS -#define USE_LOCK_BIT (2U) -#else /* USE_LOCKS */ -#define USE_LOCK_BIT (0U) -#define INITIAL_LOCK(l) -#endif /* USE_LOCKS */ - -#if USE_LOCKS -#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK -#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); -#endif -#ifndef RELEASE_MALLOC_GLOBAL_LOCK -#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); -#endif -#else /* USE_LOCKS */ -#define ACQUIRE_MALLOC_GLOBAL_LOCK() -#define RELEASE_MALLOC_GLOBAL_LOCK() -#endif /* USE_LOCKS */ - - -/* ----------------------- Chunk representations ------------------------ */ - -/* -(The following includes lightly edited explanations by Colin Plumb.) - -The malloc_chunk declaration below is misleading (but accurate and -necessary). It declares a "view" into memory allowing access to -necessary fields at known offsets from a given base. - -Chunks of memory are maintained using a `boundary tag' method as -originally described by Knuth. (See the paper by Paul Wilson -ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such -techniques.) Sizes of free chunks are stored both in the front of -each chunk and at the end. This makes consolidating fragmented -chunks into bigger chunks fast. The head fields also hold bits -representing whether chunks are free or in use. - -Here are some pictures to make it clearer. They are "exploded" to -show that the state of a chunk can be thought of as extending from -the high 31 bits of the head field of its header through the -prev_foot and PINUSE_BIT bit of the following chunk header. - -A chunk that's in use looks like: - -chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size of previous chunk (if P = 0) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| -| Size of this chunk 1| +-+ -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -+- -+ -| | -+- -+ -| : -+- size - sizeof(size_t) available payload bytes -+ -: | -chunk-> +- -+ -| | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| -| Size of next chunk (may or may not be in use) | +-+ -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -And if it's free, it looks like this: - -chunk-> +- -+ -| User payload (must be in use, or we would have merged!) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| -| Size of this chunk 0| +-+ -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Next pointer | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Prev pointer | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| : -+- size - sizeof(struct chunk) unused bytes -+ -: | -chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size of this chunk | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| -| Size of next chunk (must be in use, or we would have merged)| +-+ -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| : -+- User payload -+ -: | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -|0| -+-+ -Note that since we always merge adjacent free chunks, the chunks -adjacent to a free chunk must be in use. - -Given a pointer to a chunk (which can be derived trivially from the -payload pointer) we can, in O(1) time, find out whether the adjacent -chunks are free, and if so, unlink them from the lists that they -are on and merge them with the current chunk. - -Chunks always begin on even word boundaries, so the mem portion -(which is returned to the user) is also on an even word boundary, and -thus at least double-word aligned. - -The P (PINUSE_BIT) bit, stored in the unused low-order bit of the -chunk size (which is always a multiple of two words), is an in-use -bit for the *previous* chunk. If that bit is *clear*, then the -word before the current chunk size contains the previous chunk -size, and can be used to find the front of the previous chunk. -The very first chunk allocated always has this bit set, preventing -access to non-existent (or non-owned) memory. If pinuse is set for -any given chunk, then you CANNOT determine the size of the -previous chunk, and might even get a memory addressing fault when -trying to do so. - -The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of -the chunk size redundantly records whether the current chunk is -inuse (unless the chunk is mmapped). This redundancy enables usage -checks within free and realloc, and reduces indirection when freeing -and consolidating chunks. - -Each freshly allocated chunk must have both cinuse and pinuse set. -That is, each allocated chunk borders either a previously allocated -and still in-use chunk, or the base of its memory arena. This is -ensured by making all allocations from the the `lowest' part of any -found chunk. Further, no free chunk physically borders another one, -so each free chunk is known to be preceded and followed by either -inuse chunks or the ends of memory. - -Note that the `foot' of the current chunk is actually represented -as the prev_foot of the NEXT chunk. This makes it easier to -deal with alignments etc but can be very confusing when trying -to extend or adapt this code. - -The exceptions to all this are - -1. The special chunk `top' is the top-most available chunk (i.e., -the one bordering the end of available memory). It is treated -specially. Top is never included in any bin, is used only if -no other chunk is available, and is released back to the -system if it is very large (see M_TRIM_THRESHOLD). In effect, -the top chunk is treated as larger (and thus less well -fitting) than any other available chunk. The top chunk -doesn't update its trailing size field since there is no next -contiguous chunk that would have to index off it. However, -space is still allocated for it (TOP_FOOT_SIZE) to enable -separation or merging when space is extended. - -3. Chunks allocated via mmap, have both cinuse and pinuse bits -cleared in their head fields. Because they are allocated -one-by-one, each must carry its own prev_foot field, which is -also used to hold the offset this chunk has within its mmapped -region, which is needed to preserve alignment. Each mmapped -chunk is trailed by the first two fields of a fake next-chunk -for sake of usage checks. - -*/ - -struct malloc_chunk { - size_t prev_foot; /* Size of previous chunk (if free). */ - size_t head; /* Size and inuse bits. */ - struct malloc_chunk* fd; /* double links -- used only if free. */ - struct malloc_chunk* bk; -}; - -typedef struct malloc_chunk mchunk; -typedef struct malloc_chunk* mchunkptr; -typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ -typedef unsigned int bindex_t; /* Described below */ -typedef unsigned int binmap_t; /* Described below */ -typedef unsigned int flag_t; /* The type of various bit flag sets */ - -/* ------------------- Chunks sizes and alignments ----------------------- */ - -#define MCHUNK_SIZE (sizeof(mchunk)) - -#if FOOTERS -#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) -#else /* FOOTERS */ -#define CHUNK_OVERHEAD (SIZE_T_SIZE) -#endif /* FOOTERS */ - -/* MMapped chunks need a second word of overhead ... */ -#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) -/* ... and additional padding for fake next-chunk at foot */ -#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) - -/* The smallest size we can malloc is an aligned minimal chunk */ -#define MIN_CHUNK_SIZE\ - ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) - -/* conversion from malloc headers to user pointers, and back */ -#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) -#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) -/* chunk associated with aligned address A */ -#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) - -/* Bounds on request (not chunk) sizes. */ -#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) -#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) - -/* pad request bytes into a usable size */ -#define pad_request(req) \ - (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) - -/* pad request, checking for minimum (but not maximum) */ -#define request2size(req) \ - (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) - - -/* ------------------ Operations on head and foot fields ----------------- */ - -/* -The head field of a chunk is or'ed with PINUSE_BIT when previous -adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in -use, unless mmapped, in which case both bits are cleared. - -FLAG4_BIT is not used by this malloc, but might be useful in extensions. -*/ - -#define PINUSE_BIT (SIZE_T_ONE) -#define CINUSE_BIT (SIZE_T_TWO) -#define FLAG4_BIT (SIZE_T_FOUR) -#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) -#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) - -/* Head value for fenceposts */ -#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) - -/* extraction of fields from head words */ -#define cinuse(p) ((p)->head & CINUSE_BIT) -#define pinuse(p) ((p)->head & PINUSE_BIT) -#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) -#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) - -#define chunksize(p) ((p)->head & ~(FLAG_BITS)) - -#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) - -/* Treat space at ptr +/- offset as a chunk */ -#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) -#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) - -/* Ptr to next or previous physical malloc_chunk. */ -#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) -#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) - -/* extract next chunk's pinuse bit */ -#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) - -/* Get/set size at footer */ -#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) -#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) - -/* Set size, pinuse bit, and foot */ -#define set_size_and_pinuse_of_free_chunk(p, s)\ - ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) - -/* Set size, pinuse bit, foot, and clear next pinuse */ -#define set_free_with_pinuse(p, s, n)\ - (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) - -/* Get the internal overhead associated with chunk p */ -#define overhead_for(p)\ - (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) - -/* Return true if malloced space is not necessarily cleared */ -#if MMAP_CLEARS -#define calloc_must_clear(p) (!is_mmapped(p)) -#else /* MMAP_CLEARS */ -#define calloc_must_clear(p) (1) -#endif /* MMAP_CLEARS */ - -/* ---------------------- Overlaid data structures ----------------------- */ - -/* -When chunks are not in use, they are treated as nodes of either -lists or trees. - -"Small" chunks are stored in circular doubly-linked lists, and look -like this: - -chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size of previous chunk | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -`head:' | Size of chunk, in bytes |P| -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Forward pointer to next chunk in list | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Back pointer to previous chunk in list | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Unused space (may be 0 bytes long) . -. . -. | -nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -`foot:' | Size of chunk, in bytes | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -Larger chunks are kept in a form of bitwise digital trees (aka -tries) keyed on chunksizes. Because malloc_tree_chunks are only for -free chunks greater than 256 bytes, their size doesn't impose any -constraints on user chunk sizes. Each node looks like: - -chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size of previous chunk | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -`head:' | Size of chunk, in bytes |P| -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Forward pointer to next chunk of same size | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Back pointer to previous chunk of same size | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Pointer to left child (child[0]) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Pointer to right child (child[1]) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Pointer to parent | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| bin index of this chunk | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Unused space . -. | -nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -`foot:' | Size of chunk, in bytes | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -Each tree holding treenodes is a tree of unique chunk sizes. Chunks -of the same size are arranged in a circularly-linked list, with only -the oldest chunk (the next to be used, in our FIFO ordering) -actually in the tree. (Tree members are distinguished by a non-null -parent pointer.) If a chunk with the same size an an existing node -is inserted, it is linked off the existing node using pointers that -work in the same way as fd/bk pointers of small chunks. - -Each tree contains a power of 2 sized range of chunk sizes (the -smallest is 0x100 <= x < 0x180), which is is divided in half at each -tree level, with the chunks in the smaller half of the range (0x100 -<= x < 0x140 for the top nose) in the left subtree and the larger -half (0x140 <= x < 0x180) in the right subtree. This is, of course, -done by inspecting individual bits. - -Using these rules, each node's left subtree contains all smaller -sizes than its right subtree. However, the node at the root of each -subtree has no particular ordering relationship to either. (The -dividing line between the subtree sizes is based on trie relation.) -If we remove the last chunk of a given size from the interior of the -tree, we need to replace it with a leaf node. The tree ordering -rules permit a node to be replaced by any leaf below it. - -The smallest chunk in a tree (a common operation in a best-fit -allocator) can be found by walking a path to the leftmost leaf in -the tree. Unlike a usual binary tree, where we follow left child -pointers until we reach a null, here we follow the right child -pointer any time the left one is null, until we reach a leaf with -both child pointers null. The smallest chunk in the tree will be -somewhere along that path. - -The worst case number of steps to add, find, or remove a node is -bounded by the number of bits differentiating chunks within -bins. Under current bin calculations, this ranges from 6 up to 21 -(for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case -is of course much better. -*/ - -struct malloc_tree_chunk { - /* The first four fields must be compatible with malloc_chunk */ - size_t prev_foot; - size_t head; - struct malloc_tree_chunk* fd; - struct malloc_tree_chunk* bk; - - struct malloc_tree_chunk* child[2]; - struct malloc_tree_chunk* parent; - bindex_t index; -}; - -typedef struct malloc_tree_chunk tchunk; -typedef struct malloc_tree_chunk* tchunkptr; -typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ - -/* A little helper macro for trees */ -#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) - -/* ----------------------------- Segments -------------------------------- */ - -/* -Each malloc space may include non-contiguous segments, held in a -list headed by an embedded malloc_segment record representing the -top-most space. Segments also include flags holding properties of -the space. Large chunks that are directly allocated by mmap are not -included in this list. They are instead independently created and -destroyed without otherwise keeping track of them. - -Segment management mainly comes into play for spaces allocated by -MMAP. Any call to MMAP might or might not return memory that is -adjacent to an existing segment. MORECORE normally contiguously -extends the current space, so this space is almost always adjacent, -which is simpler and faster to deal with. (This is why MORECORE is -used preferentially to MMAP when both are available -- see -sys_alloc.) When allocating using MMAP, we don't use any of the -hinting mechanisms (inconsistently) supported in various -implementations of unix mmap, or distinguish reserving from -committing memory. Instead, we just ask for space, and exploit -contiguity when we get it. It is probably possible to do -better than this on some systems, but no general scheme seems -to be significantly better. - -Management entails a simpler variant of the consolidation scheme -used for chunks to reduce fragmentation -- new adjacent memory is -normally prepended or appended to an existing segment. However, -there are limitations compared to chunk consolidation that mostly -reflect the fact that segment processing is relatively infrequent -(occurring only when getting memory from system) and that we -don't expect to have huge numbers of segments: - -* Segments are not indexed, so traversal requires linear scans. (It -would be possible to index these, but is not worth the extra -overhead and complexity for most programs on most platforms.) -* New segments are only appended to old ones when holding top-most -memory; if they cannot be prepended to others, they are held in -different segments. - -Except for the top-most segment of an mstate, each segment record -is kept at the tail of its segment. Segments are added by pushing -segment records onto the list headed by &mstate.seg for the -containing mstate. - -Segment flags control allocation/merge/deallocation policies: -* If EXTERN_BIT set, then we did not allocate this segment, -and so should not try to deallocate or merge with others. -(This currently holds only for the initial segment passed -into rak_create_mspace_with_base.) -* If USE_MMAP_BIT set, the segment may be merged with -other surrounding mmapped segments and trimmed/de-allocated -using munmap. -* If neither bit is set, then the segment was obtained using -MORECORE so can be merged with surrounding MORECORE'd segments -and deallocated/trimmed using MORECORE with negative arguments. -*/ - -struct malloc_segment { - char* base; /* base address */ - size_t size; /* allocated size */ - struct malloc_segment* next; /* ptr to next segment */ - flag_t sflags; /* mmap and extern flag */ -}; - -#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) -#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) - -typedef struct malloc_segment msegment; -typedef struct malloc_segment* msegmentptr; - -/* ---------------------------- malloc_state ----------------------------- */ - -/* -A malloc_state holds all of the bookkeeping for a space. -The main fields are: - -Top -The topmost chunk of the currently active segment. Its size is -cached in topsize. The actual size of topmost space is -topsize+TOP_FOOT_SIZE, which includes space reserved for adding -fenceposts and segment records if necessary when getting more -space from the system. The size at which to autotrim top is -cached from mparams in trim_check, except that it is disabled if -an autotrim fails. - -Designated victim (dv) -This is the preferred chunk for servicing small requests that -don't have exact fits. It is normally the chunk split off most -recently to service another small request. Its size is cached in -dvsize. The link fields of this chunk are not maintained since it -is not kept in a bin. - -SmallBins -An array of bin headers for free chunks. These bins hold chunks -with sizes less than MIN_LARGE_SIZE bytes. Each bin contains -chunks of all the same size, spaced 8 bytes apart. To simplify -use in double-linked lists, each bin header acts as a malloc_chunk -pointing to the real first node, if it exists (else pointing to -itself). This avoids special-casing for headers. But to avoid -waste, we allocate only the fd/bk pointers of bins, and then use -repositioning tricks to treat these as the fields of a chunk. - -TreeBins -Treebins are pointers to the roots of trees holding a range of -sizes. There are 2 equally spaced treebins for each power of two -from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything -larger. - -Bin maps -There is one bit map for small bins ("smallmap") and one for -treebins ("treemap). Each bin sets its bit when non-empty, and -clears the bit when empty. Bit operations are then used to avoid -bin-by-bin searching -- nearly all "search" is done without ever -looking at bins that won't be selected. The bit maps -conservatively use 32 bits per map word, even if on 64bit system. -For a good description of some of the bit-based techniques used -here, see Henry S. Warren Jr's book "Hacker's Delight" (and -supplement at http://hackersdelight.org/). Many of these are -intended to reduce the branchiness of paths through malloc etc, as -well as to reduce the number of memory locations read or written. - -Segments -A list of segments headed by an embedded malloc_segment record -representing the initial space. - -Address check support -The least_addr field is the least address ever obtained from -MORECORE or MMAP. Attempted frees and reallocs of any address less -than this are trapped (unless INSECURE is defined). - -Magic tag -A cross-check field that should always hold same value as mparams.magic. - -Flags -Bits recording whether to use MMAP, locks, or contiguous MORECORE - -Statistics -Each space keeps track of current and maximum system memory -obtained via MORECORE or MMAP. - -Trim support -Fields holding the amount of unused topmost memory that should trigger -timming, and a counter to force periodic scanning to release unused -non-topmost segments. - -Locking -If USE_LOCKS is defined, the "mutex" lock is acquired and released -around every public call using this mspace. - -Extension support -A void* pointer and a size_t field that can be used to help implement -extensions to this malloc. -*/ - -/* Bin types, widths and sizes */ -#define NSMALLBINS (32U) -#define NTREEBINS (32U) -#define SMALLBIN_SHIFT (3U) -#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) -#define TREEBIN_SHIFT (8U) -#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) -#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) -#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) - -struct malloc_state { - binmap_t smallmap; - binmap_t treemap; - size_t dvsize; - size_t topsize; - char* least_addr; - mchunkptr dv; - mchunkptr top; - size_t trim_check; - size_t release_checks; - size_t magic; - mchunkptr smallbins[(NSMALLBINS+1)*2]; - tbinptr treebins[NTREEBINS]; - size_t footprint; - size_t max_footprint; - flag_t mflags; -#if USE_LOCKS - MLOCK_T mutex; /* locate lock among fields that rarely change */ -#endif /* USE_LOCKS */ - msegment seg; - void* extp; /* Unused but available for extensions */ - size_t exts; -}; - -typedef struct malloc_state* mstate; - -/* ------------- Global malloc_state and malloc_params ------------------- */ - -/* -malloc_params holds global properties, including those that can be -dynamically set using mallopt. There is a single instance, mparams, -initialized in init_mparams. Note that the non-zeroness of "magic" -also serves as an initialization flag. -*/ - -struct malloc_params { - volatile size_t magic; - size_t page_size; - size_t granularity; - size_t mmap_threshold; - size_t trim_threshold; - flag_t default_mflags; -}; - -static struct malloc_params mparams; - -/* Ensure mparams initialized */ -#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) - -#if !ONLY_MSPACES - -/* The global malloc_state used for all non-"mspace" calls */ -static struct malloc_state _gm_; -#define gm (&_gm_) -#define is_global(M) ((M) == &_gm_) - -#endif /* !ONLY_MSPACES */ - -#define is_initialized(M) ((M)->top != 0) - -/* -------------------------- system alloc setup ------------------------- */ - -/* Operations on mflags */ - -#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) -#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) -#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) - -#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) -#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) -#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) - -#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) -#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) - -#define set_lock(M,L)\ - ((M)->mflags = (L)?\ - ((M)->mflags | USE_LOCK_BIT) :\ - ((M)->mflags & ~USE_LOCK_BIT)) - -/* page-align a size */ -#define page_align(S)\ - (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) - -/* granularity-align a size */ -#define granularity_align(S)\ - (((S) + (mparams.granularity - SIZE_T_ONE))\ - & ~(mparams.granularity - SIZE_T_ONE)) - - -/* For mmap, use granularity alignment on windows, else page-align */ -#ifdef DL_PLATFORM_WIN32 -#define mmap_align(S) granularity_align(S) -#else -#define mmap_align(S) page_align(S) -#endif - -/* For sys_alloc, enough padding to ensure can malloc request on success */ -#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) - -#define is_page_aligned(S)\ - (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) -#define is_granularity_aligned(S)\ - (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) - -/* True if segment S holds address A */ -#define segment_holds(S, A)\ - ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) - -/* Return segment holding given address */ -static msegmentptr segment_holding(mstate m, char* addr) { - msegmentptr sp = &m->seg; - for (;;) { - if (addr >= sp->base && addr < sp->base + sp->size) - return sp; - if ((sp = sp->next) == 0) - return 0; - } -} - -/* Return true if segment contains a segment link */ -static int has_segment_link(mstate m, msegmentptr ss) { - msegmentptr sp = &m->seg; - for (;;) { - if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) - return 1; - if ((sp = sp->next) == 0) - return 0; - } -} - -#ifndef MORECORE_CANNOT_TRIM -#define should_trim(M,s) ((s) > (M)->trim_check) -#else /* MORECORE_CANNOT_TRIM */ -#define should_trim(M,s) (0) -#endif /* MORECORE_CANNOT_TRIM */ - -/* -TOP_FOOT_SIZE is padding at the end of a segment, including space -that may be needed to place segment records and fenceposts when new -noncontiguous segments are added. -*/ -#define TOP_FOOT_SIZE\ - (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) - - -/* ------------------------------- Hooks -------------------------------- */ - -/* -PREACTION should be defined to return 0 on success, and nonzero on -failure. If you are not using locking, you can redefine these to do -anything you like. -*/ - -#if USE_LOCKS - -#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) -#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } -#else /* USE_LOCKS */ - -#ifndef PREACTION -#define PREACTION(M) (0) -#endif /* PREACTION */ - -#ifndef POSTACTION -#define POSTACTION(M) -#endif /* POSTACTION */ - -#endif /* USE_LOCKS */ - -/* -CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. -USAGE_ERROR_ACTION is triggered on detected bad frees and -reallocs. The argument p is an address that might have triggered the -fault. It is ignored by the two predefined actions, but might be -useful in custom actions that try to help diagnose errors. -*/ - -#if PROCEED_ON_ERROR - -/* A count of the number of corruption errors causing resets */ -int malloc_corruption_error_count; - -/* default corruption action */ -static void reset_on_error(mstate m); - -#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) -#define USAGE_ERROR_ACTION(m, p) - -#else /* PROCEED_ON_ERROR */ - -#ifndef CORRUPTION_ERROR_ACTION -#define CORRUPTION_ERROR_ACTION(m) ABORT -#endif /* CORRUPTION_ERROR_ACTION */ - -#ifndef USAGE_ERROR_ACTION -#define USAGE_ERROR_ACTION(m,p) ABORT -#endif /* USAGE_ERROR_ACTION */ - -#endif /* PROCEED_ON_ERROR */ - -/* -------------------------- Debugging setup ---------------------------- */ - -#if ! DEBUG - -#define check_free_chunk(M,P) -#define check_inuse_chunk(M,P) -#define check_malloced_chunk(M,P,N) -#define check_mmapped_chunk(M,P) -#define check_malloc_state(M) -#define check_top_chunk(M,P) - -#else /* DEBUG */ -#define check_free_chunk(M,P) do_check_free_chunk(M,P) -#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) -#define check_top_chunk(M,P) do_check_top_chunk(M,P) -#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) -#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) -#define check_malloc_state(M) do_check_malloc_state(M) - -static void do_check_any_chunk(mstate m, mchunkptr p); -static void do_check_top_chunk(mstate m, mchunkptr p); -static void do_check_mmapped_chunk(mstate m, mchunkptr p); -static void do_check_inuse_chunk(mstate m, mchunkptr p); -static void do_check_free_chunk(mstate m, mchunkptr p); -static void do_check_malloced_chunk(mstate m, void* mem, size_t s); -static void do_check_tree(mstate m, tchunkptr t); -static void do_check_treebin(mstate m, bindex_t i); -static void do_check_smallbin(mstate m, bindex_t i); -static void do_check_malloc_state(mstate m); -static int bin_find(mstate m, mchunkptr x); -static size_t traverse_and_check(mstate m); -#endif /* DEBUG */ - -/* ---------------------------- Indexing Bins ---------------------------- */ - -#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) -#define small_index(s) ((s) >> SMALLBIN_SHIFT) -#define small_index2size(i) ((i) << SMALLBIN_SHIFT) -#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) - -/* addressing by index. See above about smallbin repositioning */ -#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) -#define treebin_at(M,i) (&((M)->treebins[i])) - -/* assign tree index for size S to variable I. Use x86 asm if possible */ -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define compute_tree_index(S, I)\ -{\ - unsigned int X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K;\ - __asm__("bsrl\t%1, %0\n\t" : "=r" (K) : "g" (X));\ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ -}\ -} - -#elif defined (__INTEL_COMPILER) -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K = _bit_scan_reverse (X); \ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ -}\ -} - -#elif defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K;\ - _BitScanReverse((DWORD *) &K, X);\ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ -}\ -} - -#else /* GNUC */ -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int Y = (unsigned int)X;\ - unsigned int N = ((Y - 0x100) >> 16) & 8;\ - unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ - N += K;\ - N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ - K = 14 - N + ((Y <<= K) >> 15);\ - I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ -}\ -} -#endif /* GNUC */ - -/* Bit representing maximum resolved size in a treebin at i */ -#define bit_for_tree_index(i) \ - (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) - -/* Shift placing maximum resolved bit in a treebin at i as sign bit */ -#define leftshift_for_tree_index(i) \ - ((i == NTREEBINS-1)? 0 : \ - ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) - -/* The size of the smallest chunk held in bin with index i */ -#define minsize_for_tree_index(i) \ - ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ - (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) - - -/* ------------------------ Operations on bin maps ----------------------- */ - -/* bit corresponding to given index */ -#define idx2bit(i) ((binmap_t)(1) << (i)) - -/* Mark/Clear bits with given index */ -#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) -#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) -#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) - -#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) -#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) -#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) - -/* isolate the least set bit of a bitmap */ -#define least_bit(x) ((x) & -(x)) - -/* mask with all bits to left of least bit of x on */ -#define left_bits(x) ((x<<1) | -(x<<1)) - -/* mask with all bits to left of or equal to least bit of x on */ -#define same_or_left_bits(x) ((x) | -(x)) - -/* index corresponding to given bit. Use x86 asm if possible */ - -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - __asm__("bsfl\t%1, %0\n\t" : "=r" (J) : "g" (X));\ - I = (bindex_t)J;\ -} - -#elif defined (__INTEL_COMPILER) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - J = _bit_scan_forward (X); \ - I = (bindex_t)J;\ -} - -#elif defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - _BitScanForward((DWORD *) &J, X);\ - I = (bindex_t)J;\ -} - -#elif USE_BUILTIN_FFS -#define compute_bit2idx(X, I) I = ffs(X)-1 - -#else -#define compute_bit2idx(X, I)\ -{\ - unsigned int Y = X - 1;\ - unsigned int K = Y >> (16-4) & 16;\ - unsigned int N = K; Y >>= K;\ - N += K = Y >> (8-3) & 8; Y >>= K;\ - N += K = Y >> (4-2) & 4; Y >>= K;\ - N += K = Y >> (2-1) & 2; Y >>= K;\ - N += K = Y >> (1-0) & 1; Y >>= K;\ - I = (bindex_t)(N + Y);\ -} -#endif /* GNUC */ - - -/* ----------------------- Runtime Check Support ------------------------- */ - -/* -For security, the main invariant is that malloc/free/etc never -writes to a static address other than malloc_state, unless static -malloc_state itself has been corrupted, which cannot occur via -malloc (because of these checks). In essence this means that we -believe all pointers, sizes, maps etc held in malloc_state, but -check all of those linked or offsetted from other embedded data -structures. These checks are interspersed with main code in a way -that tends to minimize their run-time cost. - -When FOOTERS is defined, in addition to range checking, we also -verify footer fields of inuse chunks, which can be used guarantee -that the mstate controlling malloc/free is intact. This is a -streamlined version of the approach described by William Robertson -et al in "Run-time Detection of Heap-based Overflows" LISA'03 -http://www.usenix.org/events/lisa03/tech/robertson.html The footer -of an inuse chunk holds the xor of its mstate and a random seed, -that is checked upon calls to free() and realloc(). This is -(probablistically) unguessable from outside the program, but can be -computed by any code successfully malloc'ing any chunk, so does not -itself provide protection against code that has already broken -security through some other means. Unlike Robertson et al, we -always dynamically check addresses of all offset chunks (previous, -next, etc). This turns out to be cheaper than relying on hashes. -*/ - -#if !INSECURE -/* Check if address a is at least as high as any from MORECORE or MMAP */ -#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) -/* Check if address of next chunk n is higher than base chunk p */ -#define ok_next(p, n) ((char*)(p) < (char*)(n)) -/* Check if p has inuse status */ -#define ok_inuse(p) is_inuse(p) -/* Check if p has its pinuse bit on */ -#define ok_pinuse(p) pinuse(p) - -#else /* !INSECURE */ -#define ok_address(M, a) (1) -#define ok_next(b, n) (1) -#define ok_inuse(p) (1) -#define ok_pinuse(p) (1) -#endif /* !INSECURE */ - -#if (FOOTERS && !INSECURE) -/* Check if (alleged) mstate m has expected magic field */ -#define ok_magic(M) ((M)->magic == mparams.magic) -#else /* (FOOTERS && !INSECURE) */ -#define ok_magic(M) (1) -#endif /* (FOOTERS && !INSECURE) */ - - -/* In gcc, use __builtin_expect to minimize impact of checks */ -#if !INSECURE -#if defined(__GNUC__) && __GNUC__ >= 3 -#define RTCHECK(e) __builtin_expect(e, 1) -#else /* GNUC */ -#define RTCHECK(e) (e) -#endif /* GNUC */ -#else /* !INSECURE */ -#define RTCHECK(e) (1) -#endif /* !INSECURE */ - -/* macros to set up inuse chunks with or without footers */ - -#if !FOOTERS - -#define mark_inuse_foot(M,p,s) - -/* Macros for setting head/foot of non-mmapped chunks */ - -/* Set cinuse bit and pinuse bit of next chunk */ -#define set_inuse(M,p,s)\ - ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ - ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) - -/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ -#define set_inuse_and_pinuse(M,p,s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) - -/* Set size, cinuse and pinuse bit of this chunk */ -#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) - -#else /* FOOTERS */ - -/* Set foot of inuse chunk to be xor of mstate and seed */ -#define mark_inuse_foot(M,p,s)\ - (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) - -#define get_mstate_for(p)\ - ((mstate)(((mchunkptr)((char*)(p) +\ - (chunksize(p))))->prev_foot ^ mparams.magic)) - -#define set_inuse(M,p,s)\ - ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ - (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ - mark_inuse_foot(M,p,s)) - -#define set_inuse_and_pinuse(M,p,s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ - mark_inuse_foot(M,p,s)) - -#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - mark_inuse_foot(M, p, s)) - -#endif /* !FOOTERS */ - -/* ---------------------------- setting mparams -------------------------- */ - -/* Initialize mparams */ -static int init_mparams(void) { - - if (malloc_global_mutex_status <= 0) - init_malloc_global_mutex(); - - - ACQUIRE_MALLOC_GLOBAL_LOCK(); - if (mparams.magic == 0) { - size_t magic; - size_t psize; - size_t gsize; - -#ifndef DL_PLATFORM_WIN32 - psize = malloc_getpagesize; - gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); -#else /* DL_PLATFORM_WIN32 */ - { - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - psize = system_info.dwPageSize; - gsize = ((DEFAULT_GRANULARITY != 0)? -DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); - } -#endif /* DL_PLATFORM_WIN32 */ - - /* Sanity-check configuration: - size_t must be unsigned and as wide as pointer type. - ints must be at least 4 bytes. - alignment must be at least 8. - Alignment, min chunk size, and page size must all be powers of 2. - */ - if ((sizeof(size_t) != sizeof(char*)) || - (MAX_SIZE_T < MIN_CHUNK_SIZE) || - (sizeof(int) < 4) || - (MALLOC_ALIGNMENT < (size_t)8U) || - ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || - ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || - ((gsize & (gsize-SIZE_T_ONE)) != 0) || - ((psize & (psize-SIZE_T_ONE)) != 0)) - ABORT; - - mparams.granularity = gsize; - mparams.page_size = psize; - mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; - mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; -#if MORECORE_CONTIGUOUS - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; -#else /* MORECORE_CONTIGUOUS */ - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; -#endif /* MORECORE_CONTIGUOUS */ - -#if !ONLY_MSPACES - /* Set up lock for main malloc area */ - gm->mflags = mparams.default_mflags; - INITIAL_LOCK(&gm->mutex); -#endif - - { -#if USE_DEV_RANDOM - int fd; - unsigned char buf[sizeof(size_t)]; - /* Try to use /dev/urandom, else fall back on using time */ - if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && - read(fd, buf, sizeof(buf)) == sizeof(buf)) { - magic = *((size_t *) buf); - close(fd); - } - else -#endif /* USE_DEV_RANDOM */ - - - -#if defined(DL_PLATFORM_WIN32) - magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); -#else - magic = (size_t)(time(0) ^ (size_t)0x55555555U); -#endif - magic |= (size_t)8U; /* ensure nonzero */ - magic &= ~(size_t)7U; /* improve chances of fault for bad values */ - mparams.magic = magic; - } - } - - RELEASE_MALLOC_GLOBAL_LOCK(); - return 1; -} - -/* support for mallopt */ -static int change_mparam(int param_number, int value) { - size_t val; - ensure_initialization(); - val = (value == -1)? MAX_SIZE_T : (size_t)value; - switch(param_number) { - case M_TRIM_THRESHOLD: - mparams.trim_threshold = val; - return 1; - case M_GRANULARITY: - if (val >= mparams.page_size && ((val & (val-1)) == 0)) { - mparams.granularity = val; - return 1; - } - else - return 0; - case M_MMAP_THRESHOLD: - mparams.mmap_threshold = val; - return 1; - default: - return 0; - } -} - -#if DEBUG -/* ------------------------- Debugging Support --------------------------- */ - -/* Check properties of any chunk, whether free, inuse, mmapped etc */ -static void do_check_any_chunk(mstate m, mchunkptr p) { - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); - assert(ok_address(m, p)); -} - -/* Check properties of top chunk */ -static void do_check_top_chunk(mstate m, mchunkptr p) { - msegmentptr sp = segment_holding(m, (char*)p); - size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ - assert(sp != 0); - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); - assert(ok_address(m, p)); - assert(sz == m->topsize); - assert(sz > 0); - assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); - assert(pinuse(p)); - assert(!pinuse(chunk_plus_offset(p, sz))); -} - -/* Check properties of (inuse) mmapped chunks */ -static void do_check_mmapped_chunk(mstate m, mchunkptr p) { - size_t sz = chunksize(p); - size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); - assert(is_mmapped(p)); - assert(use_mmap(m)); - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); - assert(ok_address(m, p)); - assert(!is_small(sz)); - assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); - assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); - assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); -} - -/* Check properties of inuse chunks */ -static void do_check_inuse_chunk(mstate m, mchunkptr p) { - do_check_any_chunk(m, p); - assert(is_inuse(p)); - assert(next_pinuse(p)); - /* If not pinuse and not mmapped, previous chunk has OK offset */ - assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); - if (is_mmapped(p)) - do_check_mmapped_chunk(m, p); -} - -/* Check properties of free chunks */ -static void do_check_free_chunk(mstate m, mchunkptr p) { - size_t sz = chunksize(p); - mchunkptr next = chunk_plus_offset(p, sz); - do_check_any_chunk(m, p); - assert(!is_inuse(p)); - assert(!next_pinuse(p)); - assert (!is_mmapped(p)); - if (p != m->dv && p != m->top) { - if (sz >= MIN_CHUNK_SIZE) { - assert((sz & CHUNK_ALIGN_MASK) == 0); - assert(is_aligned(chunk2mem(p))); - assert(next->prev_foot == sz); - assert(pinuse(p)); - assert (next == m->top || is_inuse(next)); - assert(p->fd->bk == p); - assert(p->bk->fd == p); - } - else /* markers are always of size SIZE_T_SIZE */ - assert(sz == SIZE_T_SIZE); - } -} - -/* Check properties of malloced chunks at the point they are malloced */ -static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - size_t sz = p->head & ~INUSE_BITS; - do_check_inuse_chunk(m, p); - assert((sz & CHUNK_ALIGN_MASK) == 0); - assert(sz >= MIN_CHUNK_SIZE); - assert(sz >= s); - /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ - assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); - } -} - -/* Check a tree and its subtrees. */ -static void do_check_tree(mstate m, tchunkptr t) { - tchunkptr head = 0; - tchunkptr u = t; - bindex_t tindex = t->index; - size_t tsize = chunksize(t); - bindex_t idx; - compute_tree_index(tsize, idx); - assert(tindex == idx); - assert(tsize >= MIN_LARGE_SIZE); - assert(tsize >= minsize_for_tree_index(idx)); - assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); - - do { /* traverse through chain of same-sized nodes */ - do_check_any_chunk(m, ((mchunkptr)u)); - assert(u->index == tindex); - assert(chunksize(u) == tsize); - assert(!is_inuse(u)); - assert(!next_pinuse(u)); - assert(u->fd->bk == u); - assert(u->bk->fd == u); - if (u->parent == 0) { - assert(u->child[0] == 0); - assert(u->child[1] == 0); - } - else { - assert(head == 0); /* only one node on chain has parent */ - head = u; - assert(u->parent != u); - assert (u->parent->child[0] == u || - u->parent->child[1] == u || - *((tbinptr*)(u->parent)) == u); - if (u->child[0] != 0) { - assert(u->child[0]->parent == u); - assert(u->child[0] != u); - do_check_tree(m, u->child[0]); - } - if (u->child[1] != 0) { - assert(u->child[1]->parent == u); - assert(u->child[1] != u); - do_check_tree(m, u->child[1]); - } - if (u->child[0] != 0 && u->child[1] != 0) { - assert(chunksize(u->child[0]) < chunksize(u->child[1])); - } - } - u = u->fd; - } while (u != t); - assert(head != 0); -} - -/* Check all the chunks in a treebin. */ -static void do_check_treebin(mstate m, bindex_t i) { - tbinptr* tb = treebin_at(m, i); - tchunkptr t = *tb; - int empty = (m->treemap & (1U << i)) == 0; - if (t == 0) - assert(empty); - if (!empty) - do_check_tree(m, t); -} - -/* Check all the chunks in a smallbin. */ -static void do_check_smallbin(mstate m, bindex_t i) { - sbinptr b = smallbin_at(m, i); - mchunkptr p = b->bk; - unsigned int empty = (m->smallmap & (1U << i)) == 0; - if (p == b) - assert(empty); - if (!empty) { - for (; p != b; p = p->bk) { - size_t size = chunksize(p); - mchunkptr q; - /* each chunk claims to be free */ - do_check_free_chunk(m, p); - /* chunk belongs in bin */ - assert(small_index(size) == i); - assert(p->bk == b || chunksize(p->bk) == chunksize(p)); - /* chunk is followed by an inuse chunk */ - q = next_chunk(p); - if (q->head != FENCEPOST_HEAD) - do_check_inuse_chunk(m, q); - } - } -} - -/* Find x in a bin. Used in other check functions. */ -static int bin_find(mstate m, mchunkptr x) { - size_t size = chunksize(x); - if (is_small(size)) { - bindex_t sidx = small_index(size); - sbinptr b = smallbin_at(m, sidx); - if (smallmap_is_marked(m, sidx)) { - mchunkptr p = b; - do { - if (p == x) - return 1; - } while ((p = p->fd) != b); - } - } - else { - bindex_t tidx; - compute_tree_index(size, tidx); - if (treemap_is_marked(m, tidx)) { - tchunkptr t = *treebin_at(m, tidx); - size_t sizebits = size << leftshift_for_tree_index(tidx); - while (t != 0 && chunksize(t) != size) { - t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; - sizebits <<= 1; - } - if (t != 0) { - tchunkptr u = t; - do { - if (u == (tchunkptr)x) - return 1; - } while ((u = u->fd) != t); - } - } - } - return 0; -} - -/* Traverse each chunk and check it; return total */ -static size_t traverse_and_check(mstate m) { - size_t sum = 0; - if (is_initialized(m)) { - msegmentptr s = &m->seg; - sum += m->topsize + TOP_FOOT_SIZE; - while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - mchunkptr lastq = 0; - assert(pinuse(q)); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - sum += chunksize(q); - if (is_inuse(q)) { - assert(!bin_find(m, q)); - do_check_inuse_chunk(m, q); - } - else { - assert(q == m->dv || bin_find(m, q)); - assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ - do_check_free_chunk(m, q); - } - lastq = q; - q = next_chunk(q); - } - s = s->next; - } - } - return sum; -} - -/* Check all properties of malloc_state. */ -static void do_check_malloc_state(mstate m) { - bindex_t i; - size_t total; - /* check bins */ - for (i = 0; i < NSMALLBINS; ++i) - do_check_smallbin(m, i); - for (i = 0; i < NTREEBINS; ++i) - do_check_treebin(m, i); - - if (m->dvsize != 0) { /* check dv chunk */ - do_check_any_chunk(m, m->dv); - assert(m->dvsize == chunksize(m->dv)); - assert(m->dvsize >= MIN_CHUNK_SIZE); - assert(bin_find(m, m->dv) == 0); - } - - if (m->top != 0) { /* check top chunk */ - do_check_top_chunk(m, m->top); - /*assert(m->topsize == chunksize(m->top)); redundant */ - assert(m->topsize > 0); - assert(bin_find(m, m->top) == 0); - } - - total = traverse_and_check(m); - assert(total <= m->footprint); - assert(m->footprint <= m->max_footprint); -} -#endif /* DEBUG */ - -/* ----------------------------- statistics ------------------------------ */ - -#if !NO_MALLINFO -static struct mallinfo internal_mallinfo(mstate m) { - struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - ensure_initialization(); - if (!PREACTION(m)) { - check_malloc_state(m); - if (is_initialized(m)) { - size_t nfree = SIZE_T_ONE; /* top always free */ - size_t mfree = m->topsize + TOP_FOOT_SIZE; - size_t sum = mfree; - msegmentptr s = &m->seg; - while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - size_t sz = chunksize(q); - sum += sz; - if (!is_inuse(q)) { - mfree += sz; - ++nfree; - } - q = next_chunk(q); - } - s = s->next; - } - - nm.arena = sum; - nm.ordblks = nfree; - nm.hblkhd = m->footprint - sum; - nm.usmblks = m->max_footprint; - nm.uordblks = m->footprint - mfree; - nm.fordblks = mfree; - nm.keepcost = m->topsize; - } - - POSTACTION(m); - } - return nm; -} -#endif /* !NO_MALLINFO */ - -static void internal_malloc_stats(mstate m) { - ensure_initialization(); - if (!PREACTION(m)) { - size_t maxfp = 0; - size_t fp = 0; - size_t used = 0; - check_malloc_state(m); - if (is_initialized(m)) { - msegmentptr s = &m->seg; - maxfp = m->max_footprint; - fp = m->footprint; - used = fp - (m->topsize + TOP_FOOT_SIZE); - - while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - if (!is_inuse(q)) - used -= chunksize(q); - q = next_chunk(q); - } - s = s->next; - } - } - - fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); - fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); - fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); - - POSTACTION(m); - } -} - -/* ----------------------- Operations on smallbins ----------------------- */ - -/* -Various forms of linking and unlinking are defined as macros. Even -the ones for trees, which are very long but have very short typical -paths. This is ugly but reduces reliance on inlining support of -compilers. -*/ - -/* Link a free chunk into a smallbin */ -#define insert_small_chunk(M, P, S) {\ - bindex_t I = small_index(S);\ - mchunkptr B = smallbin_at(M, I);\ - mchunkptr F = B;\ - assert(S >= MIN_CHUNK_SIZE);\ - if (!smallmap_is_marked(M, I))\ - mark_smallmap(M, I);\ - else if (RTCHECK(ok_address(M, B->fd)))\ - F = B->fd;\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ - B->fd = P;\ - F->bk = P;\ - P->fd = F;\ - P->bk = B;\ -} - -/* Unlink a chunk from a smallbin */ -#define unlink_small_chunk(M, P, S) {\ - mchunkptr F = P->fd;\ - mchunkptr B = P->bk;\ - bindex_t I = small_index(S);\ - assert(P != B);\ - assert(P != F);\ - assert(chunksize(P) == small_index2size(I));\ - if (F == B)\ - clear_smallmap(M, I);\ - else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ - (B == smallbin_at(M,I) || ok_address(M, B)))) {\ - F->bk = B;\ - B->fd = F;\ -}\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ -} - -/* Unlink the first chunk from a smallbin */ -#define unlink_first_small_chunk(M, B, P, I) {\ - mchunkptr F = P->fd;\ - assert(P != B);\ - assert(P != F);\ - assert(chunksize(P) == small_index2size(I));\ - if (B == F)\ - clear_smallmap(M, I);\ - else if (RTCHECK(ok_address(M, F))) {\ - B->fd = F;\ - F->bk = B;\ -}\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ -} - - - -/* Replace dv node, binning the old one */ -/* Used only when dvsize known to be small */ -#define replace_dv(M, P, S) {\ - size_t DVS = M->dvsize;\ - if (DVS != 0) {\ - mchunkptr DV = M->dv;\ - assert(is_small(DVS));\ - insert_small_chunk(M, DV, DVS);\ - }\ - M->dvsize = S;\ - M->dv = P;\ -} - -/* ------------------------- Operations on trees ------------------------- */ - -/* Insert chunk into tree */ -#define insert_large_chunk(M, X, S) {\ - tbinptr* H;\ - bindex_t I;\ - compute_tree_index(S, I);\ - H = treebin_at(M, I);\ - X->index = I;\ - X->child[0] = X->child[1] = 0;\ - if (!treemap_is_marked(M, I)) {\ - mark_treemap(M, I);\ - *H = X;\ - X->parent = (tchunkptr)H;\ - X->fd = X->bk = X;\ - }\ - else {\ - tchunkptr T = *H;\ - size_t K = S << leftshift_for_tree_index(I);\ - for (;;) {\ - if (chunksize(T) != S) {\ - tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ - K <<= 1;\ - if (*C != 0)\ - T = *C;\ - else if (RTCHECK(ok_address(M, C))) {\ - *C = X;\ - X->parent = T;\ - X->fd = X->bk = X;\ - break;\ -}\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ -}\ - }\ - else {\ - tchunkptr F = T->fd;\ - if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ - T->fd = F->bk = X;\ - X->fd = F;\ - X->bk = T;\ - X->parent = 0;\ - break;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ -}\ -}\ - }\ -}\ -} - -/* -Unlink steps: - -1. If x is a chained node, unlink it from its same-sized fd/bk links -and choose its bk node as its replacement. -2. If x was the last node of its size, but not a leaf node, it must -be replaced with a leaf node (not merely one with an open left or -right), to make sure that lefts and rights of descendents -correspond properly to bit masks. We use the rightmost descendent -of x. We could use any other leaf, but this is easy to locate and -tends to counteract removal of leftmosts elsewhere, and so keeps -paths shorter than minimally guaranteed. This doesn't loop much -because on average a node in a tree is near the bottom. -3. If x is the base of a chain (i.e., has parent links) relink -x's parent and children to x's replacement (or null if none). -*/ - -#define unlink_large_chunk(M, X) {\ - tchunkptr XP = X->parent;\ - tchunkptr R;\ - if (X->bk != X) {\ - tchunkptr F = X->fd;\ - R = X->bk;\ - if (RTCHECK(ok_address(M, F))) {\ - F->bk = R;\ - R->fd = F;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ - }\ - else {\ - tchunkptr* RP;\ - if (((R = *(RP = &(X->child[1]))) != 0) ||\ - ((R = *(RP = &(X->child[0]))) != 0)) {\ - tchunkptr* CP;\ - while ((*(CP = &(R->child[1])) != 0) ||\ - (*(CP = &(R->child[0])) != 0)) {\ - R = *(RP = CP);\ -}\ - if (RTCHECK(ok_address(M, RP)))\ - *RP = 0;\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ -}\ -}\ - if (XP != 0) {\ - tbinptr* H = treebin_at(M, X->index);\ - if (X == *H) {\ - if ((*H = R) == 0) \ - clear_treemap(M, X->index);\ - }\ - else if (RTCHECK(ok_address(M, XP))) {\ - if (XP->child[0] == X) \ - XP->child[0] = R;\ - else \ - XP->child[1] = R;\ -}\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - if (R != 0) {\ - if (RTCHECK(ok_address(M, R))) {\ - tchunkptr C0, C1;\ - R->parent = XP;\ - if ((C0 = X->child[0]) != 0) {\ - if (RTCHECK(ok_address(M, C0))) {\ - R->child[0] = C0;\ - C0->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - if ((C1 = X->child[1]) != 0) {\ - if (RTCHECK(ok_address(M, C1))) {\ - R->child[1] = C1;\ - C1->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ -} - -/* Relays to large vs small bin operations */ - -#define insert_chunk(M, P, S)\ - if (is_small(S)) insert_small_chunk(M, P, S)\ - else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } - -#define unlink_chunk(M, P, S)\ - if (is_small(S)) unlink_small_chunk(M, P, S)\ - else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } - - -/* Relays to internal calls to malloc/free from realloc, memalign etc */ - -#if ONLY_MSPACES -#define internal_malloc(m, b) rak_mspace_malloc(m, b) -#define internal_free(m, mem) rak_mspace_free(m,mem); -#else /* ONLY_MSPACES */ -#if MSPACES -#define internal_malloc(m, b)\ - (m == gm)? rdlmalloc(b) : rak_mspace_malloc(m, b) -#define internal_free(m, mem)\ - if (m == gm) rdlfree(mem); else rak_mspace_free(m,mem); -#else /* MSPACES */ -#define internal_malloc(m, b) rdlmalloc(b) -#define internal_free(m, mem) rdlfree(mem) -#endif /* MSPACES */ -#endif /* ONLY_MSPACES */ - -/* ----------------------- Direct-mmapping chunks ----------------------- */ - -/* -Directly mmapped chunks are set up with an offset to the start of -the mmapped region stored in the prev_foot field of the chunk. This -allows reconstruction of the required argument to MUNMAP when freed, -and also allows adjustment of the returned chunk to meet alignment -requirements (especially in memalign). -*/ - -/* Malloc using mmap */ -static void* mmap_alloc(mstate m, size_t nb) { - size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - if (mmsize > nb) { /* Check for wrap around 0 */ - char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); - if (mm != CMFAIL) { - size_t offset = align_offset(chunk2mem(mm)); - size_t psize = mmsize - offset - MMAP_FOOT_PAD; - mchunkptr p = (mchunkptr)(mm + offset); - p->prev_foot = offset; - p->head = psize; - mark_inuse_foot(m, p, psize); - chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; - - if (m->least_addr == 0 || mm < m->least_addr) - m->least_addr = mm; - if ((m->footprint += mmsize) > m->max_footprint) - m->max_footprint = m->footprint; - assert(is_aligned(chunk2mem(p))); - check_mmapped_chunk(m, p); - return chunk2mem(p); - } - } - return 0; -} - -/* Realloc using mmap */ -static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { - size_t oldsize = chunksize(oldp); - if (is_small(nb)) /* Can't shrink mmap regions below small size */ - return 0; - /* Keep old chunk if big enough but not too big */ - if (oldsize >= nb + SIZE_T_SIZE && - (oldsize - nb) <= (mparams.granularity << 1)) - return oldp; - else { - size_t offset = oldp->prev_foot; - size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; - size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - char* cp = (char*)CALL_MREMAP((char*)oldp - offset, - oldmmsize, newmmsize, 1); - if (cp != CMFAIL) { - mchunkptr newp = (mchunkptr)(cp + offset); - size_t psize = newmmsize - offset - MMAP_FOOT_PAD; - newp->head = psize; - mark_inuse_foot(m, newp, psize); - chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; - - if (cp < m->least_addr) - m->least_addr = cp; - if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) - m->max_footprint = m->footprint; - check_mmapped_chunk(m, newp); - return newp; - } - } - return 0; -} - -/* -------------------------- mspace management -------------------------- */ - -/* Initialize top chunk and its size */ -static void init_top(mstate m, mchunkptr p, size_t psize) { - /* Ensure alignment */ - size_t offset = align_offset(chunk2mem(p)); - p = (mchunkptr)((char*)p + offset); - psize -= offset; - - m->top = p; - m->topsize = psize; - p->head = psize | PINUSE_BIT; - /* set size of fake trailing chunk holding overhead space only once */ - chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; - m->trim_check = mparams.trim_threshold; /* reset on each update */ -} - -/* Initialize bins for a new mstate that is otherwise zeroed out */ -static void init_bins(mstate m) { - /* Establish circular links for smallbins */ - bindex_t i; - for (i = 0; i < NSMALLBINS; ++i) { - sbinptr bin = smallbin_at(m,i); - bin->fd = bin->bk = bin; - } -} - -#if PROCEED_ON_ERROR - -/* default corruption action */ -static void reset_on_error(mstate m) { - int i; - ++malloc_corruption_error_count; - /* Reinitialize fields to forget about all memory */ - m->smallbins = m->treebins = 0; - m->dvsize = m->topsize = 0; - m->seg.base = 0; - m->seg.size = 0; - m->seg.next = 0; - m->top = m->dv = 0; - for (i = 0; i < NTREEBINS; ++i) - *treebin_at(m, i) = 0; - init_bins(m); -} -#endif /* PROCEED_ON_ERROR */ - -/* Allocate chunk and prepend remainder with chunk in successor base. */ -static void* prepend_alloc(mstate m, char* newbase, char* oldbase, - size_t nb) { - mchunkptr p = align_as_chunk(newbase); - mchunkptr oldfirst = align_as_chunk(oldbase); - size_t psize = (char*)oldfirst - (char*)p; - mchunkptr q = chunk_plus_offset(p, nb); - size_t qsize = psize - nb; - set_size_and_pinuse_of_inuse_chunk(m, p, nb); - - assert((char*)oldfirst > (char*)q); - assert(pinuse(oldfirst)); - assert(qsize >= MIN_CHUNK_SIZE); - - /* consolidate remainder with first chunk of old base */ - if (oldfirst == m->top) { - size_t tsize = m->topsize += qsize; - m->top = q; - q->head = tsize | PINUSE_BIT; - check_top_chunk(m, q); - } - else if (oldfirst == m->dv) { - size_t dsize = m->dvsize += qsize; - m->dv = q; - set_size_and_pinuse_of_free_chunk(q, dsize); - } - else { - if (!is_inuse(oldfirst)) { - size_t nsize = chunksize(oldfirst); - unlink_chunk(m, oldfirst, nsize); - oldfirst = chunk_plus_offset(oldfirst, nsize); - qsize += nsize; - } - set_free_with_pinuse(q, qsize, oldfirst); - insert_chunk(m, q, qsize); - check_free_chunk(m, q); - } - - check_malloced_chunk(m, chunk2mem(p), nb); - return chunk2mem(p); -} - -/* Add a segment to hold a new noncontiguous region */ -static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { - /* Determine locations and sizes of segment, fenceposts, old top */ - char* old_top = (char*)m->top; - msegmentptr oldsp = segment_holding(m, old_top); - char* old_end = oldsp->base + oldsp->size; - size_t ssize = pad_request(sizeof(struct malloc_segment)); - char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - size_t offset = align_offset(chunk2mem(rawsp)); - char* asp = rawsp + offset; - char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; - mchunkptr sp = (mchunkptr)csp; - msegmentptr ss = (msegmentptr)(chunk2mem(sp)); - mchunkptr tnext = chunk_plus_offset(sp, ssize); - mchunkptr p = tnext; - int nfences = 0; - - /* reset top to new space */ - init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); - - /* Set up segment record */ - assert(is_aligned(ss)); - set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); - *ss = m->seg; /* Push current record */ - m->seg.base = tbase; - m->seg.size = tsize; - m->seg.sflags = mmapped; - m->seg.next = ss; - - /* Insert trailing fenceposts */ - for (;;) { - mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); - p->head = FENCEPOST_HEAD; - ++nfences; - if ((char*)(&(nextp->head)) < old_end) - p = nextp; - else - break; - } - assert(nfences >= 2); - - /* Insert the rest of old top into a bin as an ordinary free chunk */ - if (csp != old_top) { - mchunkptr q = (mchunkptr)old_top; - size_t psize = csp - old_top; - mchunkptr tn = chunk_plus_offset(q, psize); - set_free_with_pinuse(q, psize, tn); - insert_chunk(m, q, psize); - } - - check_top_chunk(m, m->top); -} - -/* -------------------------- System allocation -------------------------- */ - -/* Get memory from system using MORECORE or MMAP */ -static void* sys_alloc(mstate m, size_t nb) { - char* tbase = CMFAIL; - size_t tsize = 0; - flag_t mmap_flag = 0; - - ensure_initialization(); - - /* Directly map large chunks, but only if already initialized */ - if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { - void* mem = mmap_alloc(m, nb); - if (mem != 0) - return mem; - } - - /* - Try getting memory in any of three ways (in most-preferred to - least-preferred order): - 1. A call to MORECORE that can normally contiguously extend memory. - (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or - or main space is mmapped or a previous contiguous call failed) - 2. A call to MMAP new space (disabled if not HAVE_MMAP). - Note that under the default settings, if MORECORE is unable to - fulfill a request, and HAVE_MMAP is true, then mmap is - used as a noncontiguous system allocator. This is a useful backup - strategy for systems with holes in address spaces -- in this case - sbrk cannot contiguously expand the heap, but mmap may be able to - find space. - 3. A call to MORECORE that cannot usually contiguously extend memory. - (disabled if not HAVE_MORECORE) - - In all cases, we need to request enough bytes from system to ensure - we can malloc nb bytes upon success, so pad with enough space for - top_foot, plus alignment-pad to make sure we don't lose bytes if - not on boundary, and round this up to a granularity unit. - */ - - if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { - char* br = CMFAIL; - msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); - size_t asize = 0; - ACQUIRE_MALLOC_GLOBAL_LOCK(); - - if (ss == 0) { /* First time through or recovery */ - char* base = (char*)CALL_MORECORE(0); - if (base != CMFAIL) { - asize = granularity_align(nb + SYS_ALLOC_PADDING); - /* Adjust to end on a page boundary */ - if (!is_page_aligned(base)) - asize += (page_align((size_t)base) - (size_t)base); - /* Can't call MORECORE if size is negative when treated as signed */ - if (asize < HALF_MAX_SIZE_T && - (br = (char*)(CALL_MORECORE(asize))) == base) { - tbase = base; - tsize = asize; - } - } - } - else { - /* Subtract out existing available top space from MORECORE request. */ - asize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); - /* Use mem here only if it did continuously extend old space */ - if (asize < HALF_MAX_SIZE_T && - (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) { - tbase = br; - tsize = asize; - } - } - - if (tbase == CMFAIL) { /* Cope with partial failure */ - if (br != CMFAIL) { /* Try to use/extend the space we did get */ - if (asize < HALF_MAX_SIZE_T && - asize < nb + SYS_ALLOC_PADDING) { - size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - asize); - if (esize < HALF_MAX_SIZE_T) { - char* end = (char*)CALL_MORECORE(esize); - if (end != CMFAIL) - asize += esize; - else { /* Can't use; try to release */ - (void) CALL_MORECORE(-asize); - br = CMFAIL; - } - } - } - } - if (br != CMFAIL) { /* Use the space we did get */ - tbase = br; - tsize = asize; - } - else - disable_contiguous(m); /* Don't try contiguous path in the future */ - } - - RELEASE_MALLOC_GLOBAL_LOCK(); - } - - if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ - size_t rsize = granularity_align(nb + SYS_ALLOC_PADDING); - if (rsize > nb) { /* Fail if wraps around zero */ - char* mp = (char*)(CALL_MMAP(rsize)); - if (mp != CMFAIL) { - tbase = mp; - tsize = rsize; - mmap_flag = USE_MMAP_BIT; - } - } - } - - if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ - size_t asize = granularity_align(nb + SYS_ALLOC_PADDING); - if (asize < HALF_MAX_SIZE_T) { - char* br = CMFAIL; - char* end = CMFAIL; - ACQUIRE_MALLOC_GLOBAL_LOCK(); - br = (char*)(CALL_MORECORE(asize)); - end = (char*)(CALL_MORECORE(0)); - RELEASE_MALLOC_GLOBAL_LOCK(); - if (br != CMFAIL && end != CMFAIL && br < end) { - size_t ssize = end - br; - if (ssize > nb + TOP_FOOT_SIZE) { - tbase = br; - tsize = ssize; - } - } - } - } - - if (tbase != CMFAIL) { - - if ((m->footprint += tsize) > m->max_footprint) - m->max_footprint = m->footprint; - - if (!is_initialized(m)) { /* first-time initialization */ - if (m->least_addr == 0 || tbase < m->least_addr) - m->least_addr = tbase; - m->seg.base = tbase; - m->seg.size = tsize; - m->seg.sflags = mmap_flag; - m->magic = mparams.magic; - m->release_checks = MAX_RELEASE_CHECK_RATE; - init_bins(m); -#if !ONLY_MSPACES - if (is_global(m)) - init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); - else -#endif - { - /* Offset top by embedded malloc_state */ - mchunkptr mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); - } - } - - else { - /* Try to merge with an existing segment */ - msegmentptr sp = &m->seg; - /* Only consider most recent segment if traversal suppressed */ - while (sp != 0 && tbase != sp->base + sp->size) - sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && - !is_extern_segment(sp) && - (sp->sflags & USE_MMAP_BIT) == mmap_flag && - segment_holds(sp, m->top)) { /* append */ - sp->size += tsize; - init_top(m, m->top, m->topsize + tsize); - } - else { - if (tbase < m->least_addr) - m->least_addr = tbase; - sp = &m->seg; - while (sp != 0 && sp->base != tbase + tsize) - sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && - !is_extern_segment(sp) && - (sp->sflags & USE_MMAP_BIT) == mmap_flag) { - char* oldbase = sp->base; - sp->base = tbase; - sp->size += tsize; - return prepend_alloc(m, tbase, oldbase, nb); - } - else - add_segment(m, tbase, tsize, mmap_flag); - } - } - - if (nb < m->topsize) { /* Allocate from new or extended top space */ - size_t rsize = m->topsize -= nb; - mchunkptr p = m->top; - mchunkptr r = m->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(m, p, nb); - check_top_chunk(m, m->top); - check_malloced_chunk(m, chunk2mem(p), nb); - return chunk2mem(p); - } - } - - MALLOC_FAILURE_ACTION; - return 0; -} - -/* ----------------------- system deallocation -------------------------- */ - -/* Unmap and unlink any mmapped segments that don't contain used chunks */ -static size_t release_unused_segments(mstate m) { - size_t released = 0; - int nsegs = 0; - msegmentptr pred = &m->seg; - msegmentptr sp = pred->next; - while (sp != 0) { - char* base = sp->base; - size_t size = sp->size; - msegmentptr next = sp->next; - ++nsegs; - if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { - mchunkptr p = align_as_chunk(base); - size_t psize = chunksize(p); - /* Can unmap if first chunk holds entire segment and not pinned */ - if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { - tchunkptr tp = (tchunkptr)p; - assert(segment_holds(sp, (char*)sp)); - if (p == m->dv) { - m->dv = 0; - m->dvsize = 0; - } - else { - unlink_large_chunk(m, tp); - } - if (CALL_MUNMAP(base, size) == 0) { - released += size; - m->footprint -= size; - /* unlink obsoleted record */ - sp = pred; - sp->next = next; - } - else { /* back out if cannot unmap */ - insert_large_chunk(m, tp, psize); - } - } - } - if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ - break; - pred = sp; - sp = next; - } - /* Reset check counter */ - m->release_checks = ((nsegs > MAX_RELEASE_CHECK_RATE)? -nsegs : MAX_RELEASE_CHECK_RATE); - return released; -} - -static int sys_trim(mstate m, size_t pad) { - size_t released = 0; - ensure_initialization(); - if (pad < MAX_REQUEST && is_initialized(m)) { - pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ - - if (m->topsize > pad) { - /* Shrink top space in granularity-size units, keeping at least one */ - size_t unit = mparams.granularity; - size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - - SIZE_T_ONE) * unit; - msegmentptr sp = segment_holding(m, (char*)m->top); - - if (!is_extern_segment(sp)) { - if (is_mmapped_segment(sp)) { - if (HAVE_MMAP && - sp->size >= extra && - !has_segment_link(m, sp)) { /* can't shrink if pinned */ - size_t newsize = sp->size - extra; - /* Prefer mremap, fall back to munmap */ - if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || - (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { - released = extra; - } - } - } - else if (HAVE_MORECORE) { - if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ - extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; - ACQUIRE_MALLOC_GLOBAL_LOCK(); - { - /* Make sure end of memory is where we last set it. */ - char* old_br = (char*)(CALL_MORECORE(0)); - if (old_br == sp->base + sp->size) { - char* rel_br = (char*)(CALL_MORECORE(-extra)); - char* new_br = (char*)(CALL_MORECORE(0)); - if (rel_br != CMFAIL && new_br < old_br) - released = old_br - new_br; - } - } - RELEASE_MALLOC_GLOBAL_LOCK(); - } - } - - if (released != 0) { - sp->size -= released; - m->footprint -= released; - init_top(m, m->top, m->topsize - released); - check_top_chunk(m, m->top); - } - } - - /* Unmap any unused mmapped segments */ - if (HAVE_MMAP) - released += release_unused_segments(m); - - /* On failure, disable autotrim to avoid repeated failed future calls */ - if (released == 0 && m->topsize > m->trim_check) - m->trim_check = MAX_SIZE_T; - } - - return (released != 0)? 1 : 0; -} - - -/* ---------------------------- malloc support --------------------------- */ - -/* allocate a large request from the best fitting chunk in a treebin */ -static void* tmalloc_large(mstate m, size_t nb) { - tchunkptr v = 0; - size_t rsize = -nb; /* Unsigned negation */ - tchunkptr t; - bindex_t idx; - compute_tree_index(nb, idx); - if ((t = *treebin_at(m, idx)) != 0) { - /* Traverse tree for this bin looking for node with size == nb */ - size_t sizebits = nb << leftshift_for_tree_index(idx); - tchunkptr rst = 0; /* The deepest untaken right subtree */ - for (;;) { - tchunkptr rt; - size_t trem = chunksize(t) - nb; - if (trem < rsize) { - v = t; - if ((rsize = trem) == 0) - break; - } - rt = t->child[1]; - t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; - if (rt != 0 && rt != t) - rst = rt; - if (t == 0) { - t = rst; /* set t to least subtree holding sizes > nb */ - break; - } - sizebits <<= 1; - } - } - if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ - binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; - if (leftbits != 0) { - bindex_t i; - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - t = *treebin_at(m, i); - } - } - - while (t != 0) { /* find smallest of tree or subtree */ - size_t trem = chunksize(t) - nb; - if (trem < rsize) { - rsize = trem; - v = t; - } - t = leftmost_child(t); - } - - /* If dv is a better fit, return 0 so malloc will use it */ - if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { - if (RTCHECK(ok_address(m, v))) { /* split */ - mchunkptr r = chunk_plus_offset(v, nb); - assert(chunksize(v) == rsize + nb); - if (RTCHECK(ok_next(v, r))) { - unlink_large_chunk(m, v); - if (rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(m, v, (rsize + nb)); - else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - insert_chunk(m, r, rsize); - } - return chunk2mem(v); - } - } - CORRUPTION_ERROR_ACTION(m); - } - return 0; -} - -/* allocate a small request from the best fitting chunk in a treebin */ -static void* tmalloc_small(mstate m, size_t nb) { - tchunkptr t, v; - size_t rsize; - bindex_t i; - binmap_t leastbit = least_bit(m->treemap); - compute_bit2idx(leastbit, i); - v = t = *treebin_at(m, i); - rsize = chunksize(t) - nb; - - while ((t = leftmost_child(t)) != 0) { - size_t trem = chunksize(t) - nb; - if (trem < rsize) { - rsize = trem; - v = t; - } - } - - if (RTCHECK(ok_address(m, v))) { - mchunkptr r = chunk_plus_offset(v, nb); - assert(chunksize(v) == rsize + nb); - if (RTCHECK(ok_next(v, r))) { - unlink_large_chunk(m, v); - if (rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(m, v, (rsize + nb)); - else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(m, r, rsize); - } - return chunk2mem(v); - } - } - - CORRUPTION_ERROR_ACTION(m); - return 0; -} - -/* --------------------------- realloc support --------------------------- */ - -static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { - if (bytes >= MAX_REQUEST) { - MALLOC_FAILURE_ACTION; - return 0; - } - if (!PREACTION(m)) { - mchunkptr oldp = mem2chunk(oldmem); - size_t oldsize = chunksize(oldp); - mchunkptr next = chunk_plus_offset(oldp, oldsize); - mchunkptr newp = 0; - void* extra = 0; - - /* Try to either shrink or extend into top. Else malloc-copy-free */ - - if (RTCHECK(ok_address(m, oldp) && ok_inuse(oldp) && - ok_next(oldp, next) && ok_pinuse(next))) { - size_t nb = request2size(bytes); - if (is_mmapped(oldp)) - newp = mmap_resize(m, oldp, nb); - else if (oldsize >= nb) { /* already big enough */ - size_t rsize = oldsize - nb; - newp = oldp; - if (rsize >= MIN_CHUNK_SIZE) { - mchunkptr remainder = chunk_plus_offset(newp, nb); - set_inuse(m, newp, nb); - set_inuse_and_pinuse(m, remainder, rsize); - extra = chunk2mem(remainder); - } - } - else if (next == m->top && oldsize + m->topsize > nb) { - /* Expand into top */ - size_t newsize = oldsize + m->topsize; - size_t newtopsize = newsize - nb; - mchunkptr newtop = chunk_plus_offset(oldp, nb); - set_inuse(m, oldp, nb); - newtop->head = newtopsize |PINUSE_BIT; - m->top = newtop; - m->topsize = newtopsize; - newp = oldp; - } - } - else { - USAGE_ERROR_ACTION(m, oldmem); - POSTACTION(m); - return 0; - } -#if DEBUG - if (newp != 0) { - check_inuse_chunk(m, newp); /* Check requires lock */ - } -#endif - - POSTACTION(m); - - if (newp != 0) { - if (extra != 0) { - internal_free(m, extra); - } - return chunk2mem(newp); - } - else { - void* newmem = internal_malloc(m, bytes); - if (newmem != 0) { - size_t oc = oldsize - overhead_for(oldp); - memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); - internal_free(m, oldmem); - } - return newmem; - } - } - return 0; -} - -/* --------------------------- memalign support -------------------------- */ - -static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { - if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ - return internal_malloc(m, bytes); - if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ - alignment = MIN_CHUNK_SIZE; - if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ - size_t a = MALLOC_ALIGNMENT << 1; - while (a < alignment) a <<= 1; - alignment = a; - } - - if (bytes >= MAX_REQUEST - alignment) { - if (m != 0) { /* Test isn't needed but avoids compiler warning */ - MALLOC_FAILURE_ACTION; - } - } - else { - size_t nb = request2size(bytes); - size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; - char* mem = (char*)internal_malloc(m, req); - if (mem != 0) { - void* leader = 0; - void* trailer = 0; - mchunkptr p = mem2chunk(mem); - - if (PREACTION(m)) return 0; - if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ - /* - Find an aligned spot inside chunk. Since we need to give - back leading space in a chunk of at least MIN_CHUNK_SIZE, if - the first calculation places us at a spot with less than - MIN_CHUNK_SIZE leader, we can move to the next aligned spot. - We've allocated enough total room so that this is always - possible. - */ - char* br = (char*)mem2chunk((size_t)(((size_t)(mem + - alignment - - SIZE_T_ONE)) & - -alignment)); - char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? -br : br+alignment; - mchunkptr newp = (mchunkptr)pos; - size_t leadsize = pos - (char*)(p); - size_t newsize = chunksize(p) - leadsize; - - if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ - newp->prev_foot = p->prev_foot + leadsize; - newp->head = newsize; - } - else { /* Otherwise, give back leader, use the rest */ - set_inuse(m, newp, newsize); - set_inuse(m, p, leadsize); - leader = chunk2mem(p); - } - p = newp; - } - - /* Give back spare room at the end */ - if (!is_mmapped(p)) { - size_t size = chunksize(p); - if (size > nb + MIN_CHUNK_SIZE) { - size_t remainder_size = size - nb; - mchunkptr remainder = chunk_plus_offset(p, nb); - set_inuse(m, p, nb); - set_inuse(m, remainder, remainder_size); - trailer = chunk2mem(remainder); - } - } - - assert (chunksize(p) >= nb); - assert((((size_t)(chunk2mem(p))) % alignment) == 0); - check_inuse_chunk(m, p); - POSTACTION(m); - if (leader != 0) { - internal_free(m, leader); - } - if (trailer != 0) { - internal_free(m, trailer); - } - return chunk2mem(p); - } - } - return 0; -} - -/* ------------------------ comalloc/coalloc support --------------------- */ - -static void** ialloc(mstate m, - size_t n_elements, - size_t* sizes, - int opts, - void* chunks[]) { - /* - This provides common support for independent_X routines, handling - all of the combinations that can result. - - The opts arg has: - bit 0 set if all elements are same size (using sizes[0]) - bit 1 set if elements should be zeroed - */ - - size_t element_size; /* chunksize of each element, if all same */ - size_t contents_size; /* total size of elements */ - size_t array_size; /* request size of pointer array */ - void* mem; /* malloced aggregate space */ - mchunkptr p; /* corresponding chunk */ - size_t remainder_size; /* remaining bytes while splitting */ - void** marray; /* either "chunks" or malloced ptr array */ - mchunkptr array_chunk; /* chunk for malloced ptr array */ - flag_t was_enabled; /* to disable mmap */ - size_t size; - size_t i; - - ensure_initialization(); - /* compute array length, if needed */ - if (chunks != 0) { - if (n_elements == 0) - return chunks; /* nothing to do */ - marray = chunks; - array_size = 0; - } - else { - /* if empty req, must still return chunk representing empty array */ - if (n_elements == 0) - return (void**)internal_malloc(m, 0); - marray = 0; - array_size = request2size(n_elements * (sizeof(void*))); - } - - /* compute total element size */ - if (opts & 0x1) { /* all-same-size */ - element_size = request2size(*sizes); - contents_size = n_elements * element_size; - } - else { /* add up all the sizes */ - element_size = 0; - contents_size = 0; - for (i = 0; i != n_elements; ++i) - contents_size += request2size(sizes[i]); - } - - size = contents_size + array_size; - - /* - Allocate the aggregate chunk. First disable direct-mmapping so - malloc won't use it, since we would not be able to later - free/realloc space internal to a segregated mmap region. - */ - was_enabled = use_mmap(m); - disable_mmap(m); - mem = internal_malloc(m, size - CHUNK_OVERHEAD); - if (was_enabled) - enable_mmap(m); - if (mem == 0) - return 0; - - if (PREACTION(m)) return 0; - p = mem2chunk(mem); - remainder_size = chunksize(p); - - assert(!is_mmapped(p)); - - if (opts & 0x2) { /* optionally clear the elements */ - memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); - } - - /* If not provided, allocate the pointer array as final part of chunk */ - if (marray == 0) { - size_t array_chunk_size; - array_chunk = chunk_plus_offset(p, contents_size); - array_chunk_size = remainder_size - contents_size; - marray = (void**) (chunk2mem(array_chunk)); - set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); - remainder_size = contents_size; - } - - /* split out elements */ - for (i = 0; ; ++i) { - marray[i] = chunk2mem(p); - if (i != n_elements-1) { - if (element_size != 0) - size = element_size; - else - size = request2size(sizes[i]); - remainder_size -= size; - set_size_and_pinuse_of_inuse_chunk(m, p, size); - p = chunk_plus_offset(p, size); - } - else { /* the final element absorbs any overallocation slop */ - set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); - break; - } - } - -#if DEBUG - if (marray != chunks) { - /* final element must have exactly exhausted chunk */ - if (element_size != 0) { - assert(remainder_size == element_size); - } - else { - assert(remainder_size == request2size(sizes[i])); - } - check_inuse_chunk(m, mem2chunk(marray)); - } - for (i = 0; i != n_elements; ++i) - check_inuse_chunk(m, mem2chunk(marray[i])); - -#endif /* DEBUG */ - - POSTACTION(m); - return marray; -} - - -/* -------------------------- public routines ---------------------------- */ - -#if !ONLY_MSPACES - -void* rdlmalloc(size_t bytes) { - /* - Basic algorithm: - If a small request (< 256 bytes minus per-chunk overhead): - 1. If one exists, use a remainderless chunk in associated smallbin. - (Remainderless means that there are too few excess bytes to - represent as a chunk.) - 2. If it is big enough, use the dv chunk, which is normally the - chunk adjacent to the one used for the most recent small request. - 3. If one exists, split the smallest available chunk in a bin, - saving remainder in dv. - 4. If it is big enough, use the top chunk. - 5. If available, get memory from system and use it - Otherwise, for a large request: - 1. Find the smallest available binned chunk that fits, and use it - if it is better fitting than dv chunk, splitting if necessary. - 2. If better fitting than any binned chunk, use the dv chunk. - 3. If it is big enough, use the top chunk. - 4. If request size >= mmap threshold, try to directly mmap this chunk. - 5. If available, get memory from system and use it - - The ugly goto's here ensure that postaction occurs along all paths. - */ - -#if USE_LOCKS - ensure_initialization(); /* initialize in sys_alloc if not using locks */ -#endif - - if (!PREACTION(gm)) { - void* mem; - size_t nb; - if (bytes <= MAX_SMALL_REQUEST) { - bindex_t idx; - binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); - idx = small_index(nb); - smallbits = gm->smallmap >> idx; - - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ - mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ - b = smallbin_at(gm, idx); - p = b->fd; - assert(chunksize(p) == small_index2size(idx)); - unlink_first_small_chunk(gm, b, p, idx); - set_inuse_and_pinuse(gm, p, small_index2size(idx)); - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (nb > gm->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ - mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - b = smallbin_at(gm, i); - p = b->fd; - assert(chunksize(p) == small_index2size(i)); - unlink_first_small_chunk(gm, b, p, i); - rsize = small_index2size(i) - nb; - /* Fit here cannot be remainderless if 4byte sizes */ - if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(gm, p, small_index2size(i)); - else { - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - r = chunk_plus_offset(p, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(gm, r, rsize); - } - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - } - } - else if (bytes >= MAX_REQUEST) - nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ - else { - nb = pad_request(bytes); - if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - } - - if (nb <= gm->dvsize) { - size_t rsize = gm->dvsize - nb; - mchunkptr p = gm->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ - mchunkptr r = gm->dv = chunk_plus_offset(p, nb); - gm->dvsize = rsize; - set_size_and_pinuse_of_free_chunk(r, rsize); - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - } - else { /* exhaust dv */ - size_t dvs = gm->dvsize; - gm->dvsize = 0; - gm->dv = 0; - set_inuse_and_pinuse(gm, p, dvs); - } - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (nb < gm->topsize) { /* Split top */ - size_t rsize = gm->topsize -= nb; - mchunkptr p = gm->top; - mchunkptr r = gm->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - mem = chunk2mem(p); - check_top_chunk(gm, gm->top); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - mem = sys_alloc(gm, nb); - -postaction: - POSTACTION(gm); - return mem; - } - - return 0; -} - -void rdlfree(void* mem) { - /* - Consolidate freed chunks with preceeding or succeeding bordering - free chunks, if they exist, and then place in a bin. Intermixed - with special cases for top, dv, mmapped chunks, and usage errors. - */ - - if (mem != 0) { - mchunkptr p = mem2chunk(mem); -#if FOOTERS - mstate fm = get_mstate_for(p); - if (!ok_magic(fm)) { - USAGE_ERROR_ACTION(fm, p); - return; - } -#else /* FOOTERS */ -#define fm gm -#endif /* FOOTERS */ - if (!PREACTION(fm)) { - check_inuse_chunk(fm, p); - if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - size_t psize = chunksize(p); - mchunkptr next = chunk_plus_offset(p, psize); - if (!pinuse(p)) { - size_t prevsize = p->prev_foot; - if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - fm->footprint -= psize; - goto postaction; - } - else { - mchunkptr prev = chunk_minus_offset(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ - if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { - fm->dvsize = psize; - set_free_with_pinuse(p, psize, next); - goto postaction; - } - } - else - goto erroraction; - } - } - - if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ - if (next == fm->top) { - size_t tsize = fm->topsize += psize; - fm->top = p; - p->head = tsize | PINUSE_BIT; - if (p == fm->dv) { - fm->dv = 0; - fm->dvsize = 0; - } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); - goto postaction; - } - else if (next == fm->dv) { - size_t dsize = fm->dvsize += psize; - fm->dv = p; - set_size_and_pinuse_of_free_chunk(p, dsize); - goto postaction; - } - else { - size_t nsize = chunksize(next); - psize += nsize; - unlink_chunk(fm, next, nsize); - set_size_and_pinuse_of_free_chunk(p, psize); - if (p == fm->dv) { - fm->dvsize = psize; - goto postaction; - } - } - } - else - set_free_with_pinuse(p, psize, next); - - if (is_small(psize)) { - insert_small_chunk(fm, p, psize); - check_free_chunk(fm, p); - } - else { - tchunkptr tp = (tchunkptr)p; - insert_large_chunk(fm, tp, psize); - check_free_chunk(fm, p); - if (--fm->release_checks == 0) - release_unused_segments(fm); - } - goto postaction; - } - } -erroraction: - USAGE_ERROR_ACTION(fm, p); -postaction: - POSTACTION(fm); - } - } -#if !FOOTERS -#undef fm -#endif /* FOOTERS */ -} - -void* rdlcalloc(size_t n_elements, size_t elem_size) { - void* mem; - size_t req = 0; - if (n_elements != 0) { - req = n_elements * elem_size; - if (((n_elements | elem_size) & ~(size_t)0xffff) && - (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ - } - mem = rdlmalloc(req); - if (mem != 0 && calloc_must_clear(mem2chunk(mem))) - memset(mem, 0, req); - return mem; -} - -void* rdlrealloc(void* oldmem, size_t bytes) { - if (oldmem == 0) - return rdlmalloc(bytes); -#ifdef REALLOC_ZERO_BYTES_FREES - if (bytes == 0) { - rdlfree(oldmem); - return 0; - } -#endif /* REALLOC_ZERO_BYTES_FREES */ - else { -#if ! FOOTERS - mstate m = gm; -#else /* FOOTERS */ - mstate m = get_mstate_for(mem2chunk(oldmem)); - if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); - return 0; - } -#endif /* FOOTERS */ - return internal_realloc(m, oldmem, bytes); - } -} - -void* rdlmemalign(size_t alignment, size_t bytes) { - return internal_memalign(gm, alignment, bytes); -} - -void** rdlindependent_calloc(size_t n_elements, size_t elem_size, - void* chunks[]) { - size_t sz = elem_size; /* serves as 1-element array */ - return ialloc(gm, n_elements, &sz, 3, chunks); -} - -void** rdlindependent_comalloc(size_t n_elements, size_t sizes[], - void* chunks[]) { - return ialloc(gm, n_elements, sizes, 0, chunks); -} - -void* rdlvalloc(size_t bytes) { - size_t pagesz; - ensure_initialization(); - pagesz = mparams.page_size; - return rdlmemalign(pagesz, bytes); -} - -void* rdlpvalloc(size_t bytes) { - size_t pagesz; - ensure_initialization(); - pagesz = mparams.page_size; - return rdlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); -} - -int rdlmalloc_trim(size_t pad) { - int result = 0; - ensure_initialization(); - if (!PREACTION(gm)) { - result = sys_trim(gm, pad); - POSTACTION(gm); - } - return result; -} - -size_t rdlmalloc_footprint(void) { - return gm->footprint; -} - -size_t dlmalloc_max_footprint(void) { - return gm->max_footprint; -} - -#if !NO_MALLINFO -struct mallinfo rdlmallinfo(void) { - return internal_mallinfo(gm); -} -#endif /* NO_MALLINFO */ - -void rdlmalloc_stats() { - internal_malloc_stats(gm); -} - -int rdlmallopt(int param_number, int value) { - return change_mparam(param_number, value); -} - -#endif /* !ONLY_MSPACES */ - -size_t rdlmalloc_usable_size(void* mem) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) - return chunksize(p) - overhead_for(p); - } - return 0; -} - -/* ----------------------------- user mspaces ---------------------------- */ - -#if MSPACES - -static mstate init_user_mstate(char* tbase, size_t tsize) { - size_t msize = pad_request(sizeof(struct malloc_state)); - mchunkptr mn; - mchunkptr msp = align_as_chunk(tbase); - mstate m = (mstate)(chunk2mem(msp)); - memset(m, 0, msize); - INITIAL_LOCK(&m->mutex); - msp->head = (msize|INUSE_BITS); - m->seg.base = m->least_addr = tbase; - m->seg.size = m->footprint = m->max_footprint = tsize; - m->magic = mparams.magic; - m->release_checks = MAX_RELEASE_CHECK_RATE; - m->mflags = mparams.default_mflags; - m->extp = 0; - m->exts = 0; - disable_contiguous(m); - init_bins(m); - mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); - check_top_chunk(m, m->top); - return m; -} - -mspace rak_create_mspace(size_t capacity, int locked) { - mstate m = 0; - size_t msize; - ensure_initialization(); - msize = pad_request(sizeof(struct malloc_state)); - if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { - size_t rs = ((capacity == 0)? mparams.granularity : - (capacity + TOP_FOOT_SIZE + msize)); - size_t tsize = granularity_align(rs); - char* tbase = (char*)(CALL_MMAP(tsize)); - if (tbase != CMFAIL) { - m = init_user_mstate(tbase, tsize); - m->seg.sflags = USE_MMAP_BIT; - set_lock(m, locked); - } - } - return (mspace)m; -} - -mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked) { - mstate m = 0; - size_t msize; - ensure_initialization(); - msize = pad_request(sizeof(struct malloc_state)); - if (capacity > msize + TOP_FOOT_SIZE && - capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { - m = init_user_mstate((char*)base, capacity); - m->seg.sflags = EXTERN_BIT; - set_lock(m, locked); - } - return (mspace)m; -} - -int rak_mspace_track_large_chunks(mspace msp, int enable) { - int ret = 0; - mstate ms = (mstate)msp; - if (!PREACTION(ms)) { - if (!use_mmap(ms)) - ret = 1; - if (!enable) - enable_mmap(ms); - else - disable_mmap(ms); - POSTACTION(ms); - } - return ret; -} - -size_t rak_destroy_mspace(mspace msp) { - size_t freed = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - msegmentptr sp = &ms->seg; - while (sp != 0) { - char* base = sp->base; - size_t size = sp->size; - flag_t flag = sp->sflags; - sp = sp->next; - if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && - CALL_MUNMAP(base, size) == 0) - freed += size; - } - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return freed; -} - -/* -mspace versions of routines are near-clones of the global -versions. This is not so nice but better than the alternatives. -*/ - - -void* rak_mspace_malloc(mspace msp, size_t bytes) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - if (!PREACTION(ms)) { - void* mem; - size_t nb; - if (bytes <= MAX_SMALL_REQUEST) { - bindex_t idx; - binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); - idx = small_index(nb); - smallbits = ms->smallmap >> idx; - - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ - mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ - b = smallbin_at(ms, idx); - p = b->fd; - assert(chunksize(p) == small_index2size(idx)); - unlink_first_small_chunk(ms, b, p, idx); - set_inuse_and_pinuse(ms, p, small_index2size(idx)); - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - else if (nb > ms->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ - mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - b = smallbin_at(ms, i); - p = b->fd; - assert(chunksize(p) == small_index2size(i)); - unlink_first_small_chunk(ms, b, p, i); - rsize = small_index2size(i) - nb; - /* Fit here cannot be remainderless if 4byte sizes */ - if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(ms, p, small_index2size(i)); - else { - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - r = chunk_plus_offset(p, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(ms, r, rsize); - } - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - } - } - else if (bytes >= MAX_REQUEST) - nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ - else { - nb = pad_request(bytes); - if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - } - - if (nb <= ms->dvsize) { - size_t rsize = ms->dvsize - nb; - mchunkptr p = ms->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ - mchunkptr r = ms->dv = chunk_plus_offset(p, nb); - ms->dvsize = rsize; - set_size_and_pinuse_of_free_chunk(r, rsize); - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - } - else { /* exhaust dv */ - size_t dvs = ms->dvsize; - ms->dvsize = 0; - ms->dv = 0; - set_inuse_and_pinuse(ms, p, dvs); - } - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - else if (nb < ms->topsize) { /* Split top */ - size_t rsize = ms->topsize -= nb; - mchunkptr p = ms->top; - mchunkptr r = ms->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - mem = chunk2mem(p); - check_top_chunk(ms, ms->top); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - mem = sys_alloc(ms, nb); - -postaction: - POSTACTION(ms); - return mem; - } - - return 0; -} - -void rak_mspace_free(mspace msp, void* mem) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); -#if FOOTERS - mstate fm = get_mstate_for(p); - msp = msp; /* placate people compiling -Wunused */ -#else /* FOOTERS */ - mstate fm = (mstate)msp; -#endif /* FOOTERS */ - if (!ok_magic(fm)) { - USAGE_ERROR_ACTION(fm, p); - return; - } - if (!PREACTION(fm)) { - check_inuse_chunk(fm, p); - if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - size_t psize = chunksize(p); - mchunkptr next = chunk_plus_offset(p, psize); - if (!pinuse(p)) { - size_t prevsize = p->prev_foot; - if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - fm->footprint -= psize; - goto postaction; - } - else { - mchunkptr prev = chunk_minus_offset(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ - if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { - fm->dvsize = psize; - set_free_with_pinuse(p, psize, next); - goto postaction; - } - } - else - goto erroraction; - } - } - - if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ - if (next == fm->top) { - size_t tsize = fm->topsize += psize; - fm->top = p; - p->head = tsize | PINUSE_BIT; - if (p == fm->dv) { - fm->dv = 0; - fm->dvsize = 0; - } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); - goto postaction; - } - else if (next == fm->dv) { - size_t dsize = fm->dvsize += psize; - fm->dv = p; - set_size_and_pinuse_of_free_chunk(p, dsize); - goto postaction; - } - else { - size_t nsize = chunksize(next); - psize += nsize; - unlink_chunk(fm, next, nsize); - set_size_and_pinuse_of_free_chunk(p, psize); - if (p == fm->dv) { - fm->dvsize = psize; - goto postaction; - } - } - } - else - set_free_with_pinuse(p, psize, next); - - if (is_small(psize)) { - insert_small_chunk(fm, p, psize); - check_free_chunk(fm, p); - } - else { - tchunkptr tp = (tchunkptr)p; - insert_large_chunk(fm, tp, psize); - check_free_chunk(fm, p); - if (--fm->release_checks == 0) - release_unused_segments(fm); - } - goto postaction; - } - } -erroraction: - USAGE_ERROR_ACTION(fm, p); -postaction: - POSTACTION(fm); - } - } -} - -void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { - void* mem; - size_t req = 0; - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - if (n_elements != 0) { - req = n_elements * elem_size; - if (((n_elements | elem_size) & ~(size_t)0xffff) && - (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ - } - mem = internal_malloc(ms, req); - if (mem != 0 && calloc_must_clear(mem2chunk(mem))) - memset(mem, 0, req); - return mem; -} - -void* rak_mspace_realloc(mspace msp, void* oldmem, size_t bytes) { - if (oldmem == 0) - return rak_mspace_malloc(msp, bytes); -#ifdef REALLOC_ZERO_BYTES_FREES - if (bytes == 0) { - rak_mspace_free(msp, oldmem); - return 0; - } -#endif /* REALLOC_ZERO_BYTES_FREES */ - else { -#if FOOTERS - mchunkptr p = mem2chunk(oldmem); - mstate ms = get_mstate_for(p); -#else /* FOOTERS */ - mstate ms = (mstate)msp; -#endif /* FOOTERS */ - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return internal_realloc(ms, oldmem, bytes); - } -} - -void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return internal_memalign(ms, alignment, bytes); -} - -void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]) { - size_t sz = elem_size; /* serves as 1-element array */ - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return ialloc(ms, n_elements, &sz, 3, chunks); -} - -void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return ialloc(ms, n_elements, sizes, 0, chunks); -} - -int rak_mspace_trim(mspace msp, size_t pad) { - int result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - if (!PREACTION(ms)) { - result = sys_trim(ms, pad); - POSTACTION(ms); - } - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - -void rak_mspace_malloc_stats(mspace msp) { - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - internal_malloc_stats(ms); - } - else { - USAGE_ERROR_ACTION(ms,ms); - } -} - -size_t rak_mspace_footprint(mspace msp) { - size_t result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - result = ms->footprint; - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - - -size_t mspace_max_footprint(mspace msp) { - size_t result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - result = ms->max_footprint; - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - - -#if !NO_MALLINFO -struct mallinfo rak_mspace_mallinfo(mspace msp) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - } - return internal_mallinfo(ms); -} -#endif /* NO_MALLINFO */ - -size_t rak_mspace_usable_size(void* mem) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) - return chunksize(p) - overhead_for(p); - } - return 0; -} - -int rak_mspace_mallopt(int param_number, int value) { - return change_mparam(param_number, value); -} - -#endif /* MSPACES */ - - -/* -------------------- Alternative MORECORE functions ------------------- */ - -/* -Guidelines for creating a custom version of MORECORE: - -* For best performance, MORECORE should allocate in multiples of pagesize. -* MORECORE may allocate more memory than requested. (Or even less, -but this will usually result in a malloc failure.) -* MORECORE must not allocate memory when given argument zero, but -instead return one past the end address of memory from previous -nonzero call. -* For best performance, consecutive calls to MORECORE with positive -arguments should return increasing addresses, indicating that -space has been contiguously extended. -* Even though consecutive calls to MORECORE need not return contiguous -addresses, it must be OK for malloc'ed chunks to span multiple -regions in those cases where they do happen to be contiguous. -* MORECORE need not handle negative arguments -- it may instead -just return MFAIL when given negative arguments. -Negative arguments are always multiples of pagesize. MORECORE -must not misinterpret negative args as large positive unsigned -args. You can suppress all such calls from even occurring by defining -MORECORE_CANNOT_TRIM, - -As an example alternative MORECORE, here is a custom allocator -kindly contributed for pre-OSX macOS. It uses virtually but not -necessarily physically contiguous non-paged memory (locked in, -present and won't get swapped out). You can use it by uncommenting -this section, adding some #includes, and setting up the appropriate -defines above: - -#define MORECORE osMoreCore - -There is also a shutdown routine that should somehow be called for -cleanup upon program exit. - -#define MAX_POOL_ENTRIES 100 -#define MINIMUM_MORECORE_SIZE (64 * 1024U) -static int next_os_pool; -void *our_os_pools[MAX_POOL_ENTRIES]; - -void *osMoreCore(int size) -{ -void *ptr = 0; -static void *sbrk_top = 0; - -if (size > 0) -{ -if (size < MINIMUM_MORECORE_SIZE) -size = MINIMUM_MORECORE_SIZE; -if (CurrentExecutionLevel() == kTaskLevel) -ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); -if (ptr == 0) -{ -return (void *) MFAIL; -} -// save ptrs so they can be freed during cleanup -our_os_pools[next_os_pool] = ptr; -next_os_pool++; -ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); -sbrk_top = (char *) ptr + size; -return ptr; -} -else if (size < 0) -{ -// we don't currently support shrink behavior -return (void *) MFAIL; -} -else -{ -return sbrk_top; -} -} - -// cleanup any allocated memory pools -// called as last thing before shutting down driver - -void osCleanupMem(void) -{ -void **ptr; - -for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) -if (*ptr) -{ -PoolDeallocate(*ptr); -*ptr = 0; -} -} - -*/ - - -/* ----------------------------------------------------------------------- -History: -V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) -* Use zeros instead of prev foot for is_mmapped -* Add rak_mspace_track_large_chunks; thanks to Jean Brouwers -* Fix set_inuse in internal_realloc; thanks to Jean Brouwers -* Fix insufficient sys_alloc padding when using 16byte alignment -* Fix bad error check in rak_mspace_footprint -* Adaptations for ptmalloc; thanks to Wolfram Gloger. -* Reentrant spin locks; thanks to Earl Chew and others -* Win32 improvements; thanks to Niall Douglas and Earl Chew -* Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options -* Extension hook in malloc_state -* Various small adjustments to reduce warnings on some compilers -* Various configuration extensions/changes for more platforms. Thanks -to all who contributed these. - -V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) -* Add max_footprint functions -* Ensure all appropriate literals are size_t -* Fix conditional compilation problem for some #define settings -* Avoid concatenating segments with the one provided -in rak_create_mspace_with_base -* Rename some variables to avoid compiler shadowing warnings -* Use explicit lock initialization. -* Better handling of sbrk interference. -* Simplify and fix segment insertion, trimming and mspace_destroy -* Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x -* Thanks especially to Dennis Flanagan for help on these. - -V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) -* Fix memalign brace error. - -V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) -* Fix improper #endif nesting in C++ -* Add explicit casts needed for C++ - -V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) -* Use trees for large bins -* Support mspaces -* Use segments to unify sbrk-based and mmap-based system allocation, -removing need for emulation on most platforms without sbrk. -* Default safety checks -* Optional footer checks. Thanks to William Robertson for the idea. -* Internal code refactoring -* Incorporate suggestions and platform-specific changes. -Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, -Aaron Bachmann, Emery Berger, and others. -* Speed up non-fastbin processing enough to remove fastbins. -* Remove useless cfree() to avoid conflicts with other apps. -* Remove internal memcpy, memset. Compilers handle builtins better. -* Remove some options that no one ever used and rename others. - -V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) -* Fix malloc_state bitmap array misdeclaration - -V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) -* Allow tuning of FIRST_SORTED_BIN_SIZE -* Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. -* Better detection and support for non-contiguousness of MORECORE. -Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger -* Bypass most of malloc if no frees. Thanks To Emery Berger. -* Fix freeing of old top non-contiguous chunk im sysmalloc. -* Raised default trim and map thresholds to 256K. -* Fix mmap-related #defines. Thanks to Lubos Lunak. -* Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. -* Branch-free bin calculation -* Default trim and mmap thresholds now 256K. - -V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) -* Introduce independent_comalloc and independent_calloc. -Thanks to Michael Pachos for motivation and help. -* Make optional .h file available -* Allow > 2GB requests on 32bit systems. -* new DL_PLATFORM_WIN32 sbrk, mmap, munmap, lock code from . -Thanks also to Andreas Mueller , -and Anonymous. -* Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for -helping test this.) -* memalign: check alignment arg -* realloc: don't try to shift chunks backwards, since this -leads to more fragmentation in some programs and doesn't -seem to help in any others. -* Collect all cases in malloc requiring system memory into sysmalloc -* Use mmap as backup to sbrk -* Place all internal state in malloc_state -* Introduce fastbins (although similar to 2.5.1) -* Many minor tunings and cosmetic improvements -* Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK -* Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS -Thanks to Tony E. Bennett and others. -* Include errno.h to support default failure action. - -V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) -* return null for negative arguments -* Added Several DL_PLATFORM_WIN32 cleanups from Martin C. Fong -* Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' -(e.g. DL_PLATFORM_WIN32 platforms) -* Cleanup header file inclusion for DL_PLATFORM_WIN32 platforms -* Cleanup code to avoid Microsoft Visual C++ compiler complaints -* Add 'USE_DL_PREFIX' to quickly allow co-existence with existing -memory allocation routines -* Set 'malloc_getpagesize' for DL_PLATFORM_WIN32 platforms (needs more work) -* Use 'assert' rather than 'ASSERT' in DL_PLATFORM_WIN32 code to conform to -usage of 'assert' in non-DL_PLATFORM_WIN32 code -* Improve DL_PLATFORM_WIN32 'sbrk()' emulation's 'findRegion()' routine to -avoid infinite loop -* Always call 'fREe()' rather than 'free()' - -V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) -* Fixed ordering problem with boundary-stamping - -V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) -* Added pvalloc, as recommended by H.J. Liu -* Added 64bit pointer support mainly from Wolfram Gloger -* Added anonymously donated DL_PLATFORM_WIN32 sbrk emulation -* Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen -* malloc_extend_top: fix mask error that caused wastage after -foreign sbrks -* Add linux mremap support code from HJ Liu - -V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) -* Integrated most documentation with the code. -* Add support for mmap, with help from -Wolfram Gloger (Gloger@lrz.uni-muenchen.de). -* Use last_remainder in more cases. -* Pack bins using idea from colin@nyx10.cs.du.edu -* Use ordered bins instead of best-fit threshhold -* Eliminate block-local decls to simplify tracing and debugging. -* Support another case of realloc via move into top -* Fix error occuring when initial sbrk_base not word-aligned. -* Rely on page size for units instead of SBRK_UNIT to -avoid surprises about sbrk alignment conventions. -* Add mallinfo, mallopt. Thanks to Raymond Nijssen -(raymond@es.ele.tue.nl) for the suggestion. -* Add `pad' argument to malloc_trim and top_pad mallopt parameter. -* More precautions for cases where other routines call sbrk, -courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). -* Added macros etc., allowing use in linux libc from -H.J. Lu (hjl@gnu.ai.mit.edu) -* Inverted this history list - -V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) -* Re-tuned and fixed to behave more nicely with V2.6.0 changes. -* Removed all preallocation code since under current scheme -the work required to undo bad preallocations exceeds -the work saved in good cases for most test programs. -* No longer use return list or unconsolidated bins since -no scheme using them consistently outperforms those that don't -given above changes. -* Use best fit for very large chunks to prevent some worst-cases. -* Added some support for debugging - -V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) -* Removed footers when chunks are in use. Thanks to -Paul Wilson (wilson@cs.texas.edu) for the suggestion. - -V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) -* Added malloc_trim, with help from Wolfram Gloger -(wmglo@Dent.MED.Uni-Muenchen.DE). - -V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) - -V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) -* realloc: try to expand in both directions -* malloc: swap order of clean-bin strategy; -* realloc: only conditionally expand backwards -* Try not to scavenge used bins -* Use bin counts as a guide to preallocation -* Occasionally bin return list chunks in first scan -* Add a few optimizations from colin@nyx10.cs.du.edu - -V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) -* faster bin computation & slightly different binning -* merged all consolidations to one part of malloc proper -(eliminating old malloc_find_space & malloc_clean_bin) -* Scan 2 returns chunks (not just 1) -* Propagate failure in realloc if malloc returns 0 -* Add stuff to allow compilation on non-ANSI compilers -from kpv@research.att.com - -V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) -* removed potential for odd address access in prev_chunk -* removed dependency on getpagesize.h -* misc cosmetics and a bit more internal documentation -* anticosmetics: mangled names in macros to evade debugger strangeness -* tested on sparc, hp-700, dec-mips, rs6000 -with gcc & native cc (hp, dec only) allowing -Detlefs & Zorn comparison study (in SIGPLAN Notices.) - -Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) -* Based loosely on libg++-1.2X malloc. (It retains some of the overall -structure of old version, but most details differ.) - -*/ - -#endif // _RAKNET_SUPPORT_DL_MALLOC +#ifdef _RAKNET_SUPPORT_DL_MALLOC + +#include "rdlmalloc.h" + +/* --------------------------- Lock preliminaries ------------------------ */ + +/* +When locks are defined, there is one global lock, plus +one per-mspace lock. + +The global lock_ensures that mparams.magic and other unique +mparams values are initialized only once. It also protects +sequences of calls to MORECORE. In many cases sys_alloc requires +two calls, that should not be interleaved with calls by other +threads. This does not protect against direct calls to MORECORE +by other threads not using this lock, so there is still code to +cope the best we can on interference. + +Per-mspace locks surround calls to malloc, free, etc. To enable use +in layered extensions, per-mspace locks are reentrant. + +Because lock-protected regions generally have bounded times, it is +OK to use the supplied simple spinlocks in the custom versions for +x86. Spinlocks are likely to improve performance for lightly +contended applications, but worsen performance under heavy +contention. + +If USE_LOCKS is > 1, the definitions of lock routines here are +bypassed, in which case you will need to define the type MLOCK_T, +and at least INITIAL_LOCK, ACQUIRE_LOCK, RELEASE_LOCK and possibly +TRY_LOCK (which is not used in this malloc, but commonly needed in +extensions.) You must also declare a +static MLOCK_T malloc_global_mutex = { initialization values };. + +*/ + +#if USE_LOCKS == 1 + +#if USE_SPIN_LOCKS && SPIN_LOCKS_AVAILABLE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if !defined(DL_PLATFORM_WIN32) + +/* Custom pthread-style spin locks on x86 and x64 for gcc */ +struct pthread_mlock_t { + volatile unsigned int l; + unsigned int c; + pthread_t threadid; +}; +#define MLOCK_T struct pthread_mlock_t +#define CURRENT_THREAD pthread_self() +#define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) +#define ACQUIRE_LOCK(sl) pthread_acquire_lock(sl) +#define RELEASE_LOCK(sl) pthread_release_lock(sl) +#define TRY_LOCK(sl) pthread_try_lock(sl) +#define SPINS_PER_YIELD 63 + +static MLOCK_T malloc_global_mutex = { 0, 0, 0}; + +static FORCEINLINE int pthread_acquire_lock (MLOCK_T *sl) { + int spins = 0; + volatile unsigned int* lp = &sl->l; + for (;;) { + if (*lp != 0) { + if (sl->threadid == CURRENT_THREAD) { + ++sl->c; + return 0; + } + } + else { + /* place args to cmpxchgl in locals to evade oddities in some gccs */ + int cmp = 0; + int val = 1; + int ret; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(lp)), "0"(cmp) + : "memory", "cc"); + if (!ret) { + assert(!sl->threadid); + sl->threadid = CURRENT_THREAD; + sl->c = 1; + return 0; + } + } + if ((++spins & SPINS_PER_YIELD) == 0) { +#if defined (__SVR4) && defined (__sun) /* solaris */ + thr_yield(); +#else +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) + sched_yield(); +#else /* no-op yield on unknown systems */ + ; +#endif /* __linux__ || __FreeBSD__ || __APPLE__ */ +#endif /* solaris */ + } + } +} + +static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) { + volatile unsigned int* lp = &sl->l; + assert(*lp != 0); + assert(sl->threadid == CURRENT_THREAD); + if (--sl->c == 0) { + sl->threadid = 0; + int prev = 0; + int ret; + __asm__ __volatile__ ("lock; xchgl %0, %1" + : "=r" (ret) + : "m" (*(lp)), "0"(prev) + : "memory"); + } +} + +static FORCEINLINE int pthread_try_lock (MLOCK_T *sl) { + volatile unsigned int* lp = &sl->l; + if (*lp != 0) { + if (sl->threadid == CURRENT_THREAD) { + ++sl->c; + return 1; + } + } + else { + int cmp = 0; + int val = 1; + int ret; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(lp)), "0"(cmp) + : "memory", "cc"); + if (!ret) { + assert(!sl->threadid); + sl->threadid = CURRENT_THREAD; + sl->c = 1; + return 1; + } + } + return 0; +} + + +#else /* DL_PLATFORM_WIN32 */ +/* Custom win32-style spin locks on x86 and x64 for MSC */ +struct win32_mlock_t { + volatile long l; + unsigned int c; + long threadid; +}; + +#define MLOCK_T struct win32_mlock_t +#define CURRENT_THREAD GetCurrentThreadId() +#define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) +#define ACQUIRE_LOCK(sl) win32_acquire_lock(sl) +#define RELEASE_LOCK(sl) win32_release_lock(sl) +#define TRY_LOCK(sl) win32_try_lock(sl) +#define SPINS_PER_YIELD 63 + +static MLOCK_T malloc_global_mutex = { 0, 0, 0}; + +static FORCEINLINE int win32_acquire_lock (MLOCK_T *sl) { + int spins = 0; + for (;;) { + if (sl->l != 0) { + if (sl->threadid == CURRENT_THREAD) { + ++sl->c; + return 0; + } + } + else { + if (!interlockedexchange(&sl->l, 1)) { + assert(!sl->threadid); + sl->threadid = CURRENT_THREAD; + sl->c = 1; + return 0; + } + } + if ((++spins & SPINS_PER_YIELD) == 0) + SleepEx(0, FALSE); + } +} + +static FORCEINLINE void win32_release_lock (MLOCK_T *sl) { + assert(sl->threadid == CURRENT_THREAD); + assert(sl->l != 0); + if (--sl->c == 0) { + sl->threadid = 0; + interlockedexchange (&sl->l, 0); + } +} + +static FORCEINLINE int win32_try_lock (MLOCK_T *sl) { + if (sl->l != 0) { + if (sl->threadid == CURRENT_THREAD) { + ++sl->c; + return 1; + } + } + else { + if (!interlockedexchange(&sl->l, 1)){ + assert(!sl->threadid); + sl->threadid = CURRENT_THREAD; + sl->c = 1; + return 1; + } + } + return 0; +} + +#endif /* DL_PLATFORM_WIN32 */ +#else /* USE_SPIN_LOCKS */ + +#ifndef DL_PLATFORM_WIN32 +/* pthreads-based locks */ + +#define MLOCK_T pthread_mutex_t +#define CURRENT_THREAD pthread_self() +#define INITIAL_LOCK(sl) pthread_init_lock(sl) +#define ACQUIRE_LOCK(sl) pthread_mutex_lock(sl) +#define RELEASE_LOCK(sl) pthread_mutex_unlock(sl) +#define TRY_LOCK(sl) (!pthread_mutex_trylock(sl)) + +static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Cope with old-style linux recursive lock initialization by adding */ +/* skipped internal declaration from pthread.h */ +#ifdef linux +#ifndef PTHREAD_MUTEX_RECURSIVE +extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, + int __kind)); +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) +#endif +#endif + +static int pthread_init_lock (MLOCK_T *sl) { + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr)) return 1; + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; + if (pthread_mutex_init(sl, &attr)) return 1; + if (pthread_mutexattr_destroy(&attr)) return 1; + return 0; +} + +#else /* DL_PLATFORM_WIN32 */ +/* Win32 critical sections */ +#define MLOCK_T CRITICAL_SECTION +#define CURRENT_THREAD GetCurrentThreadId() +#define INITIAL_LOCK(s) (!InitializeCriticalSectionAndSpinCount((s), 0x80000000|4000)) +#define ACQUIRE_LOCK(s) (EnterCriticalSection(sl), 0) +#define RELEASE_LOCK(s) LeaveCriticalSection(sl) +#define TRY_LOCK(s) TryEnterCriticalSection(sl) +#define NEED_GLOBAL_LOCK_INIT + +static MLOCK_T malloc_global_mutex; +static volatile long malloc_global_mutex_status; + +/* Use spin loop to initialize global lock */ +static void init_malloc_global_mutex() { + for (;;) { + long stat = malloc_global_mutex_status; + if (stat > 0) + return; + /* transition to < 0 while initializing, then to > 0) */ + if (stat == 0 && + interlockedcompareexchange(&malloc_global_mutex_status, -1, 0) == 0) { + InitializeCriticalSection(&malloc_global_mutex); + interlockedexchange(&malloc_global_mutex_status,1); + return; + } + SleepEx(0, FALSE); + } +} + +#endif /* DL_PLATFORM_WIN32 */ +#endif /* USE_SPIN_LOCKS */ +#endif /* USE_LOCKS == 1 */ + +/* ----------------------- User-defined locks ------------------------ */ + +#if USE_LOCKS > 1 +/* Define your own lock implementation here */ +/* #define INITIAL_LOCK(sl) ... */ +/* #define ACQUIRE_LOCK(sl) ... */ +/* #define RELEASE_LOCK(sl) ... */ +/* #define TRY_LOCK(sl) ... */ +/* static MLOCK_T malloc_global_mutex = ... */ +#endif /* USE_LOCKS > 1 */ + +/* ----------------------- Lock-based state ------------------------ */ + +#if USE_LOCKS +#define USE_LOCK_BIT (2U) +#else /* USE_LOCKS */ +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) +#endif /* USE_LOCKS */ + +#if USE_LOCKS +#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK +#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); +#endif +#ifndef RELEASE_MALLOC_GLOBAL_LOCK +#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); +#endif +#else /* USE_LOCKS */ +#define ACQUIRE_MALLOC_GLOBAL_LOCK() +#define RELEASE_MALLOC_GLOBAL_LOCK() +#endif /* USE_LOCKS */ + + +/* ----------------------- Chunk representations ------------------------ */ + +/* +(The following includes lightly edited explanations by Colin Plumb.) + +The malloc_chunk declaration below is misleading (but accurate and +necessary). It declares a "view" into memory allowing access to +necessary fields at known offsets from a given base. + +Chunks of memory are maintained using a `boundary tag' method as +originally described by Knuth. (See the paper by Paul Wilson +ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such +techniques.) Sizes of free chunks are stored both in the front of +each chunk and at the end. This makes consolidating fragmented +chunks into bigger chunks fast. The head fields also hold bits +representing whether chunks are free or in use. + +Here are some pictures to make it clearer. They are "exploded" to +show that the state of a chunk can be thought of as extending from +the high 31 bits of the head field of its header through the +prev_foot and PINUSE_BIT bit of the following chunk header. + +A chunk that's in use looks like: + +chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size of previous chunk (if P = 0) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| +| Size of this chunk 1| +-+ +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++- -+ +| | ++- -+ +| : ++- size - sizeof(size_t) available payload bytes -+ +: | +chunk-> +- -+ +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| +| Size of next chunk (may or may not be in use) | +-+ +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +And if it's free, it looks like this: + +chunk-> +- -+ +| User payload (must be in use, or we would have merged!) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| +| Size of this chunk 0| +-+ +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Next pointer | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Prev pointer | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| : ++- size - sizeof(struct chunk) unused bytes -+ +: | +chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size of this chunk | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| +| Size of next chunk (must be in use, or we would have merged)| +-+ +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| : ++- User payload -+ +: | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +|0| ++-+ +Note that since we always merge adjacent free chunks, the chunks +adjacent to a free chunk must be in use. + +Given a pointer to a chunk (which can be derived trivially from the +payload pointer) we can, in O(1) time, find out whether the adjacent +chunks are free, and if so, unlink them from the lists that they +are on and merge them with the current chunk. + +Chunks always begin on even word boundaries, so the mem portion +(which is returned to the user) is also on an even word boundary, and +thus at least double-word aligned. + +The P (PINUSE_BIT) bit, stored in the unused low-order bit of the +chunk size (which is always a multiple of two words), is an in-use +bit for the *previous* chunk. If that bit is *clear*, then the +word before the current chunk size contains the previous chunk +size, and can be used to find the front of the previous chunk. +The very first chunk allocated always has this bit set, preventing +access to non-existent (or non-owned) memory. If pinuse is set for +any given chunk, then you CANNOT determine the size of the +previous chunk, and might even get a memory addressing fault when +trying to do so. + +The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of +the chunk size redundantly records whether the current chunk is +inuse (unless the chunk is mmapped). This redundancy enables usage +checks within free and realloc, and reduces indirection when freeing +and consolidating chunks. + +Each freshly allocated chunk must have both cinuse and pinuse set. +That is, each allocated chunk borders either a previously allocated +and still in-use chunk, or the base of its memory arena. This is +ensured by making all allocations from the the `lowest' part of any +found chunk. Further, no free chunk physically borders another one, +so each free chunk is known to be preceded and followed by either +inuse chunks or the ends of memory. + +Note that the `foot' of the current chunk is actually represented +as the prev_foot of the NEXT chunk. This makes it easier to +deal with alignments etc but can be very confusing when trying +to extend or adapt this code. + +The exceptions to all this are + +1. The special chunk `top' is the top-most available chunk (i.e., +the one bordering the end of available memory). It is treated +specially. Top is never included in any bin, is used only if +no other chunk is available, and is released back to the +system if it is very large (see M_TRIM_THRESHOLD). In effect, +the top chunk is treated as larger (and thus less well +fitting) than any other available chunk. The top chunk +doesn't update its trailing size field since there is no next +contiguous chunk that would have to index off it. However, +space is still allocated for it (TOP_FOOT_SIZE) to enable +separation or merging when space is extended. + +3. Chunks allocated via mmap, have both cinuse and pinuse bits +cleared in their head fields. Because they are allocated +one-by-one, each must carry its own prev_foot field, which is +also used to hold the offset this chunk has within its mmapped +region, which is needed to preserve alignment. Each mmapped +chunk is trailed by the first two fields of a fake next-chunk +for sake of usage checks. + +*/ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk* mchunkptr; +typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#if FOOTERS +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE) +#endif /* FOOTERS */ + +/* MMapped chunks need a second word of overhead ... */ +#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +/* ... and additional padding for fake next-chunk at foot */ +#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + + +/* ------------------ Operations on head and foot fields ----------------- */ + +/* +The head field of a chunk is or'ed with PINUSE_BIT when previous +adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in +use, unless mmapped, in which case both bits are cleared. + +FLAG4_BIT is not used by this malloc, but might be useful in extensions. +*/ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define FLAG4_BIT (SIZE_T_FOUR) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) +#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) +#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) + +#define chunksize(p) ((p)->head & ~(FLAG_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) +#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p)\ + (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +/* Return true if malloced space is not necessarily cleared */ +#if MMAP_CLEARS +#define calloc_must_clear(p) (!is_mmapped(p)) +#else /* MMAP_CLEARS */ +#define calloc_must_clear(p) (1) +#endif /* MMAP_CLEARS */ + +/* ---------------------- Overlaid data structures ----------------------- */ + +/* +When chunks are not in use, they are treated as nodes of either +lists or trees. + +"Small" chunks are stored in circular doubly-linked lists, and look +like this: + +chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size of previous chunk | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +`head:' | Size of chunk, in bytes |P| +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Forward pointer to next chunk in list | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Back pointer to previous chunk in list | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Unused space (may be 0 bytes long) . +. . +. | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +`foot:' | Size of chunk, in bytes | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Larger chunks are kept in a form of bitwise digital trees (aka +tries) keyed on chunksizes. Because malloc_tree_chunks are only for +free chunks greater than 256 bytes, their size doesn't impose any +constraints on user chunk sizes. Each node looks like: + +chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size of previous chunk | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +`head:' | Size of chunk, in bytes |P| +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Forward pointer to next chunk of same size | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Back pointer to previous chunk of same size | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Pointer to left child (child[0]) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Pointer to right child (child[1]) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Pointer to parent | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| bin index of this chunk | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Unused space . +. | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +`foot:' | Size of chunk, in bytes | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Each tree holding treenodes is a tree of unique chunk sizes. Chunks +of the same size are arranged in a circularly-linked list, with only +the oldest chunk (the next to be used, in our FIFO ordering) +actually in the tree. (Tree members are distinguished by a non-null +parent pointer.) If a chunk with the same size an an existing node +is inserted, it is linked off the existing node using pointers that +work in the same way as fd/bk pointers of small chunks. + +Each tree contains a power of 2 sized range of chunk sizes (the +smallest is 0x100 <= x < 0x180), which is is divided in half at each +tree level, with the chunks in the smaller half of the range (0x100 +<= x < 0x140 for the top nose) in the left subtree and the larger +half (0x140 <= x < 0x180) in the right subtree. This is, of course, +done by inspecting individual bits. + +Using these rules, each node's left subtree contains all smaller +sizes than its right subtree. However, the node at the root of each +subtree has no particular ordering relationship to either. (The +dividing line between the subtree sizes is based on trie relation.) +If we remove the last chunk of a given size from the interior of the +tree, we need to replace it with a leaf node. The tree ordering +rules permit a node to be replaced by any leaf below it. + +The smallest chunk in a tree (a common operation in a best-fit +allocator) can be found by walking a path to the leftmost leaf in +the tree. Unlike a usual binary tree, where we follow left child +pointers until we reach a null, here we follow the right child +pointer any time the left one is null, until we reach a leaf with +both child pointers null. The smallest chunk in the tree will be +somewhere along that path. + +The worst case number of steps to add, find, or remove a node is +bounded by the number of bits differentiating chunks within +bins. Under current bin calculations, this ranges from 6 up to 21 +(for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case +is of course much better. +*/ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; + + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk* tchunkptr; +typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +/* +Each malloc space may include non-contiguous segments, held in a +list headed by an embedded malloc_segment record representing the +top-most space. Segments also include flags holding properties of +the space. Large chunks that are directly allocated by mmap are not +included in this list. They are instead independently created and +destroyed without otherwise keeping track of them. + +Segment management mainly comes into play for spaces allocated by +MMAP. Any call to MMAP might or might not return memory that is +adjacent to an existing segment. MORECORE normally contiguously +extends the current space, so this space is almost always adjacent, +which is simpler and faster to deal with. (This is why MORECORE is +used preferentially to MMAP when both are available -- see +sys_alloc.) When allocating using MMAP, we don't use any of the +hinting mechanisms (inconsistently) supported in various +implementations of unix mmap, or distinguish reserving from +committing memory. Instead, we just ask for space, and exploit +contiguity when we get it. It is probably possible to do +better than this on some systems, but no general scheme seems +to be significantly better. + +Management entails a simpler variant of the consolidation scheme +used for chunks to reduce fragmentation -- new adjacent memory is +normally prepended or appended to an existing segment. However, +there are limitations compared to chunk consolidation that mostly +reflect the fact that segment processing is relatively infrequent +(occurring only when getting memory from system) and that we +don't expect to have huge numbers of segments: + +* Segments are not indexed, so traversal requires linear scans. (It +would be possible to index these, but is not worth the extra +overhead and complexity for most programs on most platforms.) +* New segments are only appended to old ones when holding top-most +memory; if they cannot be prepended to others, they are held in +different segments. + +Except for the top-most segment of an mstate, each segment record +is kept at the tail of its segment. Segments are added by pushing +segment records onto the list headed by &mstate.seg for the +containing mstate. + +Segment flags control allocation/merge/deallocation policies: +* If EXTERN_BIT set, then we did not allocate this segment, +and so should not try to deallocate or merge with others. +(This currently holds only for the initial segment passed +into rak_create_mspace_with_base.) +* If USE_MMAP_BIT set, the segment may be merged with +other surrounding mmapped segments and trimmed/de-allocated +using munmap. +* If neither bit is set, then the segment was obtained using +MORECORE so can be merged with surrounding MORECORE'd segments +and deallocated/trimmed using MORECORE with negative arguments. +*/ + +struct malloc_segment { + char* base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment* next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ +}; + +#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) +#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) + +typedef struct malloc_segment msegment; +typedef struct malloc_segment* msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* +A malloc_state holds all of the bookkeeping for a space. +The main fields are: + +Top +The topmost chunk of the currently active segment. Its size is +cached in topsize. The actual size of topmost space is +topsize+TOP_FOOT_SIZE, which includes space reserved for adding +fenceposts and segment records if necessary when getting more +space from the system. The size at which to autotrim top is +cached from mparams in trim_check, except that it is disabled if +an autotrim fails. + +Designated victim (dv) +This is the preferred chunk for servicing small requests that +don't have exact fits. It is normally the chunk split off most +recently to service another small request. Its size is cached in +dvsize. The link fields of this chunk are not maintained since it +is not kept in a bin. + +SmallBins +An array of bin headers for free chunks. These bins hold chunks +with sizes less than MIN_LARGE_SIZE bytes. Each bin contains +chunks of all the same size, spaced 8 bytes apart. To simplify +use in double-linked lists, each bin header acts as a malloc_chunk +pointing to the real first node, if it exists (else pointing to +itself). This avoids special-casing for headers. But to avoid +waste, we allocate only the fd/bk pointers of bins, and then use +repositioning tricks to treat these as the fields of a chunk. + +TreeBins +Treebins are pointers to the roots of trees holding a range of +sizes. There are 2 equally spaced treebins for each power of two +from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything +larger. + +Bin maps +There is one bit map for small bins ("smallmap") and one for +treebins ("treemap). Each bin sets its bit when non-empty, and +clears the bit when empty. Bit operations are then used to avoid +bin-by-bin searching -- nearly all "search" is done without ever +looking at bins that won't be selected. The bit maps +conservatively use 32 bits per map word, even if on 64bit system. +For a good description of some of the bit-based techniques used +here, see Henry S. Warren Jr's book "Hacker's Delight" (and +supplement at http://hackersdelight.org/). Many of these are +intended to reduce the branchiness of paths through malloc etc, as +well as to reduce the number of memory locations read or written. + +Segments +A list of segments headed by an embedded malloc_segment record +representing the initial space. + +Address check support +The least_addr field is the least address ever obtained from +MORECORE or MMAP. Attempted frees and reallocs of any address less +than this are trapped (unless INSECURE is defined). + +Magic tag +A cross-check field that should always hold same value as mparams.magic. + +Flags +Bits recording whether to use MMAP, locks, or contiguous MORECORE + +Statistics +Each space keeps track of current and maximum system memory +obtained via MORECORE or MMAP. + +Trim support +Fields holding the amount of unused topmost memory that should trigger +timming, and a counter to force periodic scanning to release unused +non-topmost segments. + +Locking +If USE_LOCKS is defined, the "mutex" lock is acquired and released +around every public call using this mspace. + +Extension support +A void* pointer and a size_t field that can be used to help implement +extensions to this malloc. +*/ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t release_checks; + size_t magic; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + flag_t mflags; +#if USE_LOCKS + MLOCK_T mutex; /* locate lock among fields that rarely change */ +#endif /* USE_LOCKS */ + msegment seg; + void* extp; /* Unused but available for extensions */ + size_t exts; +}; + +typedef struct malloc_state* mstate; + +/* ------------- Global malloc_state and malloc_params ------------------- */ + +/* +malloc_params holds global properties, including those that can be +dynamically set using mallopt. There is a single instance, mparams, +initialized in init_mparams. Note that the non-zeroness of "magic" +also serves as an initialization flag. +*/ + +struct malloc_params { + volatile size_t magic; + size_t page_size; + size_t granularity; + size_t mmap_threshold; + size_t trim_threshold; + flag_t default_mflags; +}; + +static struct malloc_params mparams; + +/* Ensure mparams initialized */ +#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) + +#if !ONLY_MSPACES + +/* The global malloc_state used for all non-"mspace" calls */ +static struct malloc_state _gm_; +#define gm (&_gm_) +#define is_global(M) ((M) == &_gm_) + +#endif /* !ONLY_MSPACES */ + +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* Operations on mflags */ + +#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) +#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) + +#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) +#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) +#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) + +#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) +#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) + +#define set_lock(M,L)\ + ((M)->mflags = (L)?\ + ((M)->mflags | USE_LOCK_BIT) :\ + ((M)->mflags & ~USE_LOCK_BIT)) + +/* page-align a size */ +#define page_align(S)\ + (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (mparams.granularity - SIZE_T_ONE))\ + & ~(mparams.granularity - SIZE_T_ONE)) + + +/* For mmap, use granularity alignment on windows, else page-align */ +#ifdef DL_PLATFORM_WIN32 +#define mmap_align(S) granularity_align(S) +#else +#define mmap_align(S) page_align(S) +#endif + +/* For sys_alloc, enough padding to ensure can malloc request on success */ +#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) + +#define is_page_aligned(S)\ + (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) +#define is_granularity_aligned(S)\ + (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char* addr) { + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) { + msegmentptr sp = &m->seg; + for (;;) { + if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + +#ifndef MORECORE_CANNOT_TRIM +#define should_trim(M,s) ((s) > (M)->trim_check) +#else /* MORECORE_CANNOT_TRIM */ +#define should_trim(M,s) (0) +#endif /* MORECORE_CANNOT_TRIM */ + +/* +TOP_FOOT_SIZE is padding at the end of a segment, including space +that may be needed to place segment records and fenceposts when new +noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + + +/* ------------------------------- Hooks -------------------------------- */ + +/* +PREACTION should be defined to return 0 on success, and nonzero on +failure. If you are not using locking, you can redefine these to do +anything you like. +*/ + +#if USE_LOCKS + +#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) +#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } +#else /* USE_LOCKS */ + +#ifndef PREACTION +#define PREACTION(M) (0) +#endif /* PREACTION */ + +#ifndef POSTACTION +#define POSTACTION(M) +#endif /* POSTACTION */ + +#endif /* USE_LOCKS */ + +/* +CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. +USAGE_ERROR_ACTION is triggered on detected bad frees and +reallocs. The argument p is an address that might have triggered the +fault. It is ignored by the two predefined actions, but might be +useful in custom actions that try to help diagnose errors. +*/ + +#if PROCEED_ON_ERROR + +/* A count of the number of corruption errors causing resets */ +int malloc_corruption_error_count; + +/* default corruption action */ +static void reset_on_error(mstate m); + +#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) +#define USAGE_ERROR_ACTION(m, p) + +#else /* PROCEED_ON_ERROR */ + +#ifndef CORRUPTION_ERROR_ACTION +#define CORRUPTION_ERROR_ACTION(m) ABORT +#endif /* CORRUPTION_ERROR_ACTION */ + +#ifndef USAGE_ERROR_ACTION +#define USAGE_ERROR_ACTION(m,p) ABORT +#endif /* USAGE_ERROR_ACTION */ + +#endif /* PROCEED_ON_ERROR */ + +/* -------------------------- Debugging setup ---------------------------- */ + +#if ! DEBUG + +#define check_free_chunk(M,P) +#define check_inuse_chunk(M,P) +#define check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) +#define check_malloc_state(M) +#define check_top_chunk(M,P) + +#else /* DEBUG */ +#define check_free_chunk(M,P) do_check_free_chunk(M,P) +#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) +#define check_top_chunk(M,P) do_check_top_chunk(M,P) +#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) +#define check_malloc_state(M) do_check_malloc_state(M) + +static void do_check_any_chunk(mstate m, mchunkptr p); +static void do_check_top_chunk(mstate m, mchunkptr p); +static void do_check_mmapped_chunk(mstate m, mchunkptr p); +static void do_check_inuse_chunk(mstate m, mchunkptr p); +static void do_check_free_chunk(mstate m, mchunkptr p); +static void do_check_malloced_chunk(mstate m, void* mem, size_t s); +static void do_check_tree(mstate m, tchunkptr t); +static void do_check_treebin(mstate m, bindex_t i); +static void do_check_smallbin(mstate m, bindex_t i); +static void do_check_malloc_state(mstate m); +static int bin_find(mstate m, mchunkptr x); +static size_t traverse_and_check(mstate m); +#endif /* DEBUG */ + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) ((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I. Use x86 asm if possible */ +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_tree_index(S, I)\ +{\ + unsigned int X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + __asm__("bsrl\t%1, %0\n\t" : "=r" (K) : "g" (X));\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ +}\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = _bit_scan_reverse (X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ +}\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + _BitScanReverse((DWORD *) &K, X);\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ +}\ +} + +#else /* GNUC */ +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int Y = (unsigned int)X;\ + unsigned int N = ((Y - 0x100) >> 16) & 8;\ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ + N += K;\ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ + K = 14 - N + ((Y <<= K) >> 15);\ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ +}\ +} +#endif /* GNUC */ + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* isolate the least set bit of a bitmap */ +#define least_bit(x) ((x) & -(x)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | -(x<<1)) + +/* mask with all bits to left of or equal to least bit of x on */ +#define same_or_left_bits(x) ((x) | -(x)) + +/* index corresponding to given bit. Use x86 asm if possible */ + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + __asm__("bsfl\t%1, %0\n\t" : "=r" (J) : "g" (X));\ + I = (bindex_t)J;\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = _bit_scan_forward (X); \ + I = (bindex_t)J;\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + _BitScanForward((DWORD *) &J, X);\ + I = (bindex_t)J;\ +} + +#elif USE_BUILTIN_FFS +#define compute_bit2idx(X, I) I = ffs(X)-1 + +#else +#define compute_bit2idx(X, I)\ +{\ + unsigned int Y = X - 1;\ + unsigned int K = Y >> (16-4) & 16;\ + unsigned int N = K; Y >>= K;\ + N += K = Y >> (8-3) & 8; Y >>= K;\ + N += K = Y >> (4-2) & 4; Y >>= K;\ + N += K = Y >> (2-1) & 2; Y >>= K;\ + N += K = Y >> (1-0) & 1; Y >>= K;\ + I = (bindex_t)(N + Y);\ +} +#endif /* GNUC */ + + +/* ----------------------- Runtime Check Support ------------------------- */ + +/* +For security, the main invariant is that malloc/free/etc never +writes to a static address other than malloc_state, unless static +malloc_state itself has been corrupted, which cannot occur via +malloc (because of these checks). In essence this means that we +believe all pointers, sizes, maps etc held in malloc_state, but +check all of those linked or offsetted from other embedded data +structures. These checks are interspersed with main code in a way +that tends to minimize their run-time cost. + +When FOOTERS is defined, in addition to range checking, we also +verify footer fields of inuse chunks, which can be used guarantee +that the mstate controlling malloc/free is intact. This is a +streamlined version of the approach described by William Robertson +et al in "Run-time Detection of Heap-based Overflows" LISA'03 +http://www.usenix.org/events/lisa03/tech/robertson.html The footer +of an inuse chunk holds the xor of its mstate and a random seed, +that is checked upon calls to free() and realloc(). This is +(probablistically) unguessable from outside the program, but can be +computed by any code successfully malloc'ing any chunk, so does not +itself provide protection against code that has already broken +security through some other means. Unlike Robertson et al, we +always dynamically check addresses of all offset chunks (previous, +next, etc). This turns out to be cheaper than relying on hashes. +*/ + +#if !INSECURE +/* Check if address a is at least as high as any from MORECORE or MMAP */ +#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) +/* Check if address of next chunk n is higher than base chunk p */ +#define ok_next(p, n) ((char*)(p) < (char*)(n)) +/* Check if p has inuse status */ +#define ok_inuse(p) is_inuse(p) +/* Check if p has its pinuse bit on */ +#define ok_pinuse(p) pinuse(p) + +#else /* !INSECURE */ +#define ok_address(M, a) (1) +#define ok_next(b, n) (1) +#define ok_inuse(p) (1) +#define ok_pinuse(p) (1) +#endif /* !INSECURE */ + +#if (FOOTERS && !INSECURE) +/* Check if (alleged) mstate m has expected magic field */ +#define ok_magic(M) ((M)->magic == mparams.magic) +#else /* (FOOTERS && !INSECURE) */ +#define ok_magic(M) (1) +#endif /* (FOOTERS && !INSECURE) */ + + +/* In gcc, use __builtin_expect to minimize impact of checks */ +#if !INSECURE +#if defined(__GNUC__) && __GNUC__ >= 3 +#define RTCHECK(e) __builtin_expect(e, 1) +#else /* GNUC */ +#define RTCHECK(e) (e) +#endif /* GNUC */ +#else /* !INSECURE */ +#define RTCHECK(e) (1) +#endif /* !INSECURE */ + +/* macros to set up inuse chunks with or without footers */ + +#if !FOOTERS + +#define mark_inuse_foot(M,p,s) + +/* Macros for setting head/foot of non-mmapped chunks */ + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +#else /* FOOTERS */ + +/* Set foot of inuse chunk to be xor of mstate and seed */ +#define mark_inuse_foot(M,p,s)\ + (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) + +#define get_mstate_for(p)\ + ((mstate)(((mchunkptr)((char*)(p) +\ + (chunksize(p))))->prev_foot ^ mparams.magic)) + +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M,p,s)) + +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ + mark_inuse_foot(M,p,s)) + +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + mark_inuse_foot(M, p, s)) + +#endif /* !FOOTERS */ + +/* ---------------------------- setting mparams -------------------------- */ + +/* Initialize mparams */ +static int init_mparams(void) { + + if (malloc_global_mutex_status <= 0) + init_malloc_global_mutex(); + + + ACQUIRE_MALLOC_GLOBAL_LOCK(); + if (mparams.magic == 0) { + size_t magic; + size_t psize; + size_t gsize; + +#ifndef DL_PLATFORM_WIN32 + psize = malloc_getpagesize; + gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); +#else /* DL_PLATFORM_WIN32 */ + { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + psize = system_info.dwPageSize; + gsize = ((DEFAULT_GRANULARITY != 0)? +DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); + } +#endif /* DL_PLATFORM_WIN32 */ + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + if ((sizeof(size_t) != sizeof(char*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((gsize & (gsize-SIZE_T_ONE)) != 0) || + ((psize & (psize-SIZE_T_ONE)) != 0)) + ABORT; + + mparams.granularity = gsize; + mparams.page_size = psize; + mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; + mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; +#if MORECORE_CONTIGUOUS + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; +#else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; +#endif /* MORECORE_CONTIGUOUS */ + +#if !ONLY_MSPACES + /* Set up lock for main malloc area */ + gm->mflags = mparams.default_mflags; + INITIAL_LOCK(&gm->mutex); +#endif + + { +#if USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + /* Try to use /dev/urandom, else fall back on using time */ + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) { + magic = *((size_t *) buf); + close(fd); + } + else +#endif /* USE_DEV_RANDOM */ + + + +#if defined(DL_PLATFORM_WIN32) + magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); +#else + magic = (size_t)(time(0) ^ (size_t)0x55555555U); +#endif + magic |= (size_t)8U; /* ensure nonzero */ + magic &= ~(size_t)7U; /* improve chances of fault for bad values */ + mparams.magic = magic; + } + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + return 1; +} + +/* support for mallopt */ +static int change_mparam(int param_number, int value) { + size_t val; + ensure_initialization(); + val = (value == -1)? MAX_SIZE_T : (size_t)value; + switch(param_number) { + case M_TRIM_THRESHOLD: + mparams.trim_threshold = val; + return 1; + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val-1)) == 0)) { + mparams.granularity = val; + return 1; + } + else + return 0; + case M_MMAP_THRESHOLD: + mparams.mmap_threshold = val; + return 1; + default: + return 0; + } +} + +#if DEBUG +/* ------------------------- Debugging Support --------------------------- */ + +/* Check properties of any chunk, whether free, inuse, mmapped etc */ +static void do_check_any_chunk(mstate m, mchunkptr p) { + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); +} + +/* Check properties of top chunk */ +static void do_check_top_chunk(mstate m, mchunkptr p) { + msegmentptr sp = segment_holding(m, (char*)p); + size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ + assert(sp != 0); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(sz == m->topsize); + assert(sz > 0); + assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); + assert(pinuse(p)); + assert(!pinuse(chunk_plus_offset(p, sz))); +} + +/* Check properties of (inuse) mmapped chunks */ +static void do_check_mmapped_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); + assert(is_mmapped(p)); + assert(use_mmap(m)); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(!is_small(sz)); + assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); + assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); + assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); +} + +/* Check properties of inuse chunks */ +static void do_check_inuse_chunk(mstate m, mchunkptr p) { + do_check_any_chunk(m, p); + assert(is_inuse(p)); + assert(next_pinuse(p)); + /* If not pinuse and not mmapped, previous chunk has OK offset */ + assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); + if (is_mmapped(p)) + do_check_mmapped_chunk(m, p); +} + +/* Check properties of free chunks */ +static void do_check_free_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + mchunkptr next = chunk_plus_offset(p, sz); + do_check_any_chunk(m, p); + assert(!is_inuse(p)); + assert(!next_pinuse(p)); + assert (!is_mmapped(p)); + if (p != m->dv && p != m->top) { + if (sz >= MIN_CHUNK_SIZE) { + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(is_aligned(chunk2mem(p))); + assert(next->prev_foot == sz); + assert(pinuse(p)); + assert (next == m->top || is_inuse(next)); + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_T_SIZE */ + assert(sz == SIZE_T_SIZE); + } +} + +/* Check properties of malloced chunks at the point they are malloced */ +static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t sz = p->head & ~INUSE_BITS; + do_check_inuse_chunk(m, p); + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(sz >= MIN_CHUNK_SIZE); + assert(sz >= s); + /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ + assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); + } +} + +/* Check a tree and its subtrees. */ +static void do_check_tree(mstate m, tchunkptr t) { + tchunkptr head = 0; + tchunkptr u = t; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; + compute_tree_index(tsize, idx); + assert(tindex == idx); + assert(tsize >= MIN_LARGE_SIZE); + assert(tsize >= minsize_for_tree_index(idx)); + assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); + + do { /* traverse through chain of same-sized nodes */ + do_check_any_chunk(m, ((mchunkptr)u)); + assert(u->index == tindex); + assert(chunksize(u) == tsize); + assert(!is_inuse(u)); + assert(!next_pinuse(u)); + assert(u->fd->bk == u); + assert(u->bk->fd == u); + if (u->parent == 0) { + assert(u->child[0] == 0); + assert(u->child[1] == 0); + } + else { + assert(head == 0); /* only one node on chain has parent */ + head = u; + assert(u->parent != u); + assert (u->parent->child[0] == u || + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); + if (u->child[0] != 0) { + assert(u->child[0]->parent == u); + assert(u->child[0] != u); + do_check_tree(m, u->child[0]); + } + if (u->child[1] != 0) { + assert(u->child[1]->parent == u); + assert(u->child[1] != u); + do_check_tree(m, u->child[1]); + } + if (u->child[0] != 0 && u->child[1] != 0) { + assert(chunksize(u->child[0]) < chunksize(u->child[1])); + } + } + u = u->fd; + } while (u != t); + assert(head != 0); +} + +/* Check all the chunks in a treebin. */ +static void do_check_treebin(mstate m, bindex_t i) { + tbinptr* tb = treebin_at(m, i); + tchunkptr t = *tb; + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) + assert(empty); + if (!empty) + do_check_tree(m, t); +} + +/* Check all the chunks in a smallbin. */ +static void do_check_smallbin(mstate m, bindex_t i) { + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; + unsigned int empty = (m->smallmap & (1U << i)) == 0; + if (p == b) + assert(empty); + if (!empty) { + for (; p != b; p = p->bk) { + size_t size = chunksize(p); + mchunkptr q; + /* each chunk claims to be free */ + do_check_free_chunk(m, p); + /* chunk belongs in bin */ + assert(small_index(size) == i); + assert(p->bk == b || chunksize(p->bk) == chunksize(p)); + /* chunk is followed by an inuse chunk */ + q = next_chunk(p); + if (q->head != FENCEPOST_HEAD) + do_check_inuse_chunk(m, q); + } + } +} + +/* Find x in a bin. Used in other check functions. */ +static int bin_find(mstate m, mchunkptr x) { + size_t size = chunksize(x); + if (is_small(size)) { + bindex_t sidx = small_index(size); + sbinptr b = smallbin_at(m, sidx); + if (smallmap_is_marked(m, sidx)) { + mchunkptr p = b; + do { + if (p == x) + return 1; + } while ((p = p->fd) != b); + } + } + else { + bindex_t tidx; + compute_tree_index(size, tidx); + if (treemap_is_marked(m, tidx)) { + tchunkptr t = *treebin_at(m, tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); + while (t != 0 && chunksize(t) != size) { + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + sizebits <<= 1; + } + if (t != 0) { + tchunkptr u = t; + do { + if (u == (tchunkptr)x) + return 1; + } while ((u = u->fd) != t); + } + } + } + return 0; +} + +/* Traverse each chunk and check it; return total */ +static size_t traverse_and_check(mstate m) { + size_t sum = 0; + if (is_initialized(m)) { + msegmentptr s = &m->seg; + sum += m->topsize + TOP_FOOT_SIZE; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + mchunkptr lastq = 0; + assert(pinuse(q)); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); + if (is_inuse(q)) { + assert(!bin_find(m, q)); + do_check_inuse_chunk(m, q); + } + else { + assert(q == m->dv || bin_find(m, q)); + assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ + do_check_free_chunk(m, q); + } + lastq = q; + q = next_chunk(q); + } + s = s->next; + } + } + return sum; +} + +/* Check all properties of malloc_state. */ +static void do_check_malloc_state(mstate m) { + bindex_t i; + size_t total; + /* check bins */ + for (i = 0; i < NSMALLBINS; ++i) + do_check_smallbin(m, i); + for (i = 0; i < NTREEBINS; ++i) + do_check_treebin(m, i); + + if (m->dvsize != 0) { /* check dv chunk */ + do_check_any_chunk(m, m->dv); + assert(m->dvsize == chunksize(m->dv)); + assert(m->dvsize >= MIN_CHUNK_SIZE); + assert(bin_find(m, m->dv) == 0); + } + + if (m->top != 0) { /* check top chunk */ + do_check_top_chunk(m, m->top); + /*assert(m->topsize == chunksize(m->top)); redundant */ + assert(m->topsize > 0); + assert(bin_find(m, m->top) == 0); + } + + total = traverse_and_check(m); + assert(total <= m->footprint); + assert(m->footprint <= m->max_footprint); +} +#endif /* DEBUG */ + +/* ----------------------------- statistics ------------------------------ */ + +#if !NO_MALLINFO +static struct mallinfo internal_mallinfo(mstate m) { + struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ensure_initialization(); + if (!PREACTION(m)) { + check_malloc_state(m); + if (is_initialized(m)) { + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; + msegmentptr s = &m->seg; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!is_inuse(q)) { + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } + + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; + nm.uordblks = m->footprint - mfree; + nm.fordblks = mfree; + nm.keepcost = m->topsize; + } + + POSTACTION(m); + } + return nm; +} +#endif /* !NO_MALLINFO */ + +static void internal_malloc_stats(mstate m) { + ensure_initialization(); + if (!PREACTION(m)) { + size_t maxfp = 0; + size_t fp = 0; + size_t used = 0; + check_malloc_state(m); + if (is_initialized(m)) { + msegmentptr s = &m->seg; + maxfp = m->max_footprint; + fp = m->footprint; + used = fp - (m->topsize + TOP_FOOT_SIZE); + + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!is_inuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; + } + } + + fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); + fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); + fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); + + POSTACTION(m); + } +} + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* +Various forms of linking and unlinking are defined as macros. Even +the ones for trees, which are very long but have very short typical +paths. This is ugly but reduces reliance on inlining support of +compilers. +*/ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + assert(S >= MIN_CHUNK_SIZE);\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, B->fd)))\ + F = B->fd;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (F == B)\ + clear_smallmap(M, I);\ + else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ + (B == smallbin_at(M,I) || ok_address(M, B)))) {\ + F->bk = B;\ + B->fd = F;\ +}\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (B == F)\ + clear_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, F))) {\ + B->fd = F;\ + F->bk = B;\ +}\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ +} + + + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + assert(is_small(DVS));\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr* H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + }\ + else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ +}\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ +}\ + }\ + else {\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ +}\ +}\ + }\ +}\ +} + +/* +Unlink steps: + +1. If x is a chained node, unlink it from its same-sized fd/bk links +and choose its bk node as its replacement. +2. If x was the last node of its size, but not a leaf node, it must +be replaced with a leaf node (not merely one with an open left or +right), to make sure that lefts and rights of descendents +correspond properly to bit masks. We use the rightmost descendent +of x. We could use any other leaf, but this is easy to locate and +tends to counteract removal of leftmosts elsewhere, and so keeps +paths shorter than minimally guaranteed. This doesn't loop much +because on average a node in a tree is near the bottom. +3. If x is the base of a chain (i.e., has parent links) relink +x's parent and children to x's replacement (or null if none). +*/ + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + if (RTCHECK(ok_address(M, F))) {\ + F->bk = R;\ + R->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ + }\ + else {\ + tchunkptr* RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr* CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ +}\ + if (RTCHECK(ok_address(M, RP)))\ + *RP = 0;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ +}\ +}\ + if (XP != 0) {\ + tbinptr* H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + }\ + else if (RTCHECK(ok_address(M, XP))) {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ +}\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + if (R != 0) {\ + if (RTCHECK(ok_address(M, R))) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) insert_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) unlink_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + + +/* Relays to internal calls to malloc/free from realloc, memalign etc */ + +#if ONLY_MSPACES +#define internal_malloc(m, b) rak_mspace_malloc(m, b) +#define internal_free(m, mem) rak_mspace_free(m,mem); +#else /* ONLY_MSPACES */ +#if MSPACES +#define internal_malloc(m, b)\ + (m == gm)? rdlmalloc(b) : rak_mspace_malloc(m, b) +#define internal_free(m, mem)\ + if (m == gm) rdlfree(mem); else rak_mspace_free(m,mem); +#else /* MSPACES */ +#define internal_malloc(m, b) rdlmalloc(b) +#define internal_free(m, mem) rdlfree(mem) +#endif /* MSPACES */ +#endif /* ONLY_MSPACES */ + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +/* +Directly mmapped chunks are set up with an offset to the start of +the mmapped region stored in the prev_foot field of the chunk. This +allows reconstruction of the required argument to MUNMAP when freed, +and also allows adjustment of the returned chunk to meet alignment +requirements (especially in memalign). +*/ + +/* Malloc using mmap */ +static void* mmap_alloc(mstate m, size_t nb) { + size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (mmsize > nb) { /* Check for wrap around 0 */ + char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset; + p->head = psize; + mark_inuse_foot(m, p, psize); + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + + if (m->least_addr == 0 || mm < m->least_addr) + m->least_addr = mm; + if ((m->footprint += mmsize) > m->max_footprint) + m->max_footprint = m->footprint; + assert(is_aligned(chunk2mem(p))); + check_mmapped_chunk(m, p); + return chunk2mem(p); + } + } + return 0; +} + +/* Realloc using mmap */ +static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { + size_t oldsize = chunksize(oldp); + if (is_small(nb)) /* Can't shrink mmap regions below small size */ + return 0; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (mparams.granularity << 1)) + return oldp; + else { + size_t offset = oldp->prev_foot; + size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; + size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + char* cp = (char*)CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, 1); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + newp->head = psize; + mark_inuse_foot(m, newp, psize); + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + + if (cp < m->least_addr) + m->least_addr = cp; + if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) + m->max_footprint = m->footprint; + check_mmapped_chunk(m, newp); + return newp; + } + } + return 0; +} + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) { + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char*)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; + m->trim_check = mparams.trim_threshold; /* reset on each update */ +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) { + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +#if PROCEED_ON_ERROR + +/* default corruption action */ +static void reset_on_error(mstate m) { + int i; + ++malloc_corruption_error_count; + /* Reinitialize fields to forget about all memory */ + m->smallbins = m->treebins = 0; + m->dvsize = m->topsize = 0; + m->seg.base = 0; + m->seg.size = 0; + m->seg.next = 0; + m->top = m->dv = 0; + for (i = 0; i < NTREEBINS; ++i) + *treebin_at(m, i) = 0; + init_bins(m); +} +#endif /* PROCEED_ON_ERROR */ + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void* prepend_alloc(mstate m, char* newbase, char* oldbase, + size_t nb) { + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (char*)oldfirst - (char*)p; + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + assert((char*)oldfirst > (char*)q); + assert(pinuse(oldfirst)); + assert(qsize >= MIN_CHUNK_SIZE); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + check_top_chunk(m, q); + } + else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } + else { + if (!is_inuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + check_free_chunk(m, q); + } + + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); +} + +/* Add a segment to hold a new noncontiguous region */ +static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { + /* Determine locations and sizes of segment, fenceposts, old top */ + char* old_top = (char*)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + char* old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char* asp = rawsp + offset; + char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + int nfences = 0; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + assert(is_aligned(ss)); + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmapped; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; + ++nfences; + if ((char*)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } + assert(nfences >= 2); + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = csp - old_top; + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize); + } + + check_top_chunk(m, m->top); +} + +/* -------------------------- System allocation -------------------------- */ + +/* Get memory from system using MORECORE or MMAP */ +static void* sys_alloc(mstate m, size_t nb) { + char* tbase = CMFAIL; + size_t tsize = 0; + flag_t mmap_flag = 0; + + ensure_initialization(); + + /* Directly map large chunks, but only if already initialized */ + if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { + void* mem = mmap_alloc(m, nb); + if (mem != 0) + return mem; + } + + /* + Try getting memory in any of three ways (in most-preferred to + least-preferred order): + 1. A call to MORECORE that can normally contiguously extend memory. + (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or + or main space is mmapped or a previous contiguous call failed) + 2. A call to MMAP new space (disabled if not HAVE_MMAP). + Note that under the default settings, if MORECORE is unable to + fulfill a request, and HAVE_MMAP is true, then mmap is + used as a noncontiguous system allocator. This is a useful backup + strategy for systems with holes in address spaces -- in this case + sbrk cannot contiguously expand the heap, but mmap may be able to + find space. + 3. A call to MORECORE that cannot usually contiguously extend memory. + (disabled if not HAVE_MORECORE) + + In all cases, we need to request enough bytes from system to ensure + we can malloc nb bytes upon success, so pad with enough space for + top_foot, plus alignment-pad to make sure we don't lose bytes if + not on boundary, and round this up to a granularity unit. + */ + + if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { + char* br = CMFAIL; + msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); + size_t asize = 0; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + + if (ss == 0) { /* First time through or recovery */ + char* base = (char*)CALL_MORECORE(0); + if (base != CMFAIL) { + asize = granularity_align(nb + SYS_ALLOC_PADDING); + /* Adjust to end on a page boundary */ + if (!is_page_aligned(base)) + asize += (page_align((size_t)base) - (size_t)base); + /* Can't call MORECORE if size is negative when treated as signed */ + if (asize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(asize))) == base) { + tbase = base; + tsize = asize; + } + } + } + else { + /* Subtract out existing available top space from MORECORE request. */ + asize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); + /* Use mem here only if it did continuously extend old space */ + if (asize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) { + tbase = br; + tsize = asize; + } + } + + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (asize < HALF_MAX_SIZE_T && + asize < nb + SYS_ALLOC_PADDING) { + size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - asize); + if (esize < HALF_MAX_SIZE_T) { + char* end = (char*)CALL_MORECORE(esize); + if (end != CMFAIL) + asize += esize; + else { /* Can't use; try to release */ + (void) CALL_MORECORE(-asize); + br = CMFAIL; + } + } + } + } + if (br != CMFAIL) { /* Use the space we did get */ + tbase = br; + tsize = asize; + } + else + disable_contiguous(m); /* Don't try contiguous path in the future */ + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + } + + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + size_t rsize = granularity_align(nb + SYS_ALLOC_PADDING); + if (rsize > nb) { /* Fail if wraps around zero */ + char* mp = (char*)(CALL_MMAP(rsize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = rsize; + mmap_flag = USE_MMAP_BIT; + } + } + } + + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + size_t asize = granularity_align(nb + SYS_ALLOC_PADDING); + if (asize < HALF_MAX_SIZE_T) { + char* br = CMFAIL; + char* end = CMFAIL; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + br = (char*)(CALL_MORECORE(asize)); + end = (char*)(CALL_MORECORE(0)); + RELEASE_MALLOC_GLOBAL_LOCK(); + if (br != CMFAIL && end != CMFAIL && br < end) { + size_t ssize = end - br; + if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; + tsize = ssize; + } + } + } + } + + if (tbase != CMFAIL) { + + if ((m->footprint += tsize) > m->max_footprint) + m->max_footprint = m->footprint; + + if (!is_initialized(m)) { /* first-time initialization */ + if (m->least_addr == 0 || tbase < m->least_addr) + m->least_addr = tbase; + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmap_flag; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + init_bins(m); +#if !ONLY_MSPACES + if (is_global(m)) + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + else +#endif + { + /* Offset top by embedded malloc_state */ + mchunkptr mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); + } + } + + else { + /* Try to merge with an existing segment */ + msegmentptr sp = &m->seg; + /* Only consider most recent segment if traversal suppressed */ + while (sp != 0 && tbase != sp->base + sp->size) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag && + segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } + else { + if (tbase < m->least_addr) + m->least_addr = tbase; + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag) { + char* oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } + else + add_segment(m, tbase, tsize, mmap_flag); + } + } + + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + check_top_chunk(m, m->top); + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); + } + } + + MALLOC_FAILURE_ACTION; + return 0; +} + +/* ----------------------- system deallocation -------------------------- */ + +/* Unmap and unlink any mmapped segments that don't contain used chunks */ +static size_t release_unused_segments(mstate m) { + size_t released = 0; + int nsegs = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + ++nsegs; + if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + assert(segment_holds(sp, (char*)sp)); + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + m->footprint -= size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } + else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } + if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ + break; + pred = sp; + sp = next; + } + /* Reset check counter */ + m->release_checks = ((nsegs > MAX_RELEASE_CHECK_RATE)? +nsegs : MAX_RELEASE_CHECK_RATE); + return released; +} + +static int sys_trim(mstate m, size_t pad) { + size_t released = 0; + ensure_initialization(); + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = mparams.granularity; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char*)m->top); + + if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + size_t newsize = sp->size - extra; + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + { + /* Make sure end of memory is where we last set it. */ + char* old_br = (char*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + char* rel_br = (char*)(CALL_MORECORE(-extra)); + char* new_br = (char*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MALLOC_GLOBAL_LOCK(); + } + } + + if (released != 0) { + sp->size -= released; + m->footprint -= released; + init_top(m, m->top, m->topsize - released); + check_top_chunk(m, m->top); + } + } + + /* Unmap any unused mmapped segments */ + if (HAVE_MMAP) + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0 && m->topsize > m->trim_check) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; +} + + +/* ---------------------------- malloc support --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void* tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; + size_t rsize = -nb; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) { + bindex_t i; + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + t = *treebin_at(m, i); + } + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return 0 so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + if (RTCHECK(ok_address(m, v))) { /* split */ + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + } + CORRUPTION_ERROR_ACTION(m); + } + return 0; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void* tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); + compute_bit2idx(leastbit, i); + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); + } + } + + CORRUPTION_ERROR_ACTION(m); + return 0; +} + +/* --------------------------- realloc support --------------------------- */ + +static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + return 0; + } + if (!PREACTION(m)) { + mchunkptr oldp = mem2chunk(oldmem); + size_t oldsize = chunksize(oldp); + mchunkptr next = chunk_plus_offset(oldp, oldsize); + mchunkptr newp = 0; + void* extra = 0; + + /* Try to either shrink or extend into top. Else malloc-copy-free */ + + if (RTCHECK(ok_address(m, oldp) && ok_inuse(oldp) && + ok_next(oldp, next) && ok_pinuse(next))) { + size_t nb = request2size(bytes); + if (is_mmapped(oldp)) + newp = mmap_resize(m, oldp, nb); + else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + newp = oldp; + if (rsize >= MIN_CHUNK_SIZE) { + mchunkptr remainder = chunk_plus_offset(newp, nb); + set_inuse(m, newp, nb); + set_inuse_and_pinuse(m, remainder, rsize); + extra = chunk2mem(remainder); + } + } + else if (next == m->top && oldsize + m->topsize > nb) { + /* Expand into top */ + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(oldp, nb); + set_inuse(m, oldp, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = oldp; + } + } + else { + USAGE_ERROR_ACTION(m, oldmem); + POSTACTION(m); + return 0; + } +#if DEBUG + if (newp != 0) { + check_inuse_chunk(m, newp); /* Check requires lock */ + } +#endif + + POSTACTION(m); + + if (newp != 0) { + if (extra != 0) { + internal_free(m, extra); + } + return chunk2mem(newp); + } + else { + void* newmem = internal_malloc(m, bytes); + if (newmem != 0) { + size_t oc = oldsize - overhead_for(oldp); + memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + return newmem; + } + } + return 0; +} + +/* --------------------------- memalign support -------------------------- */ + +static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ + return internal_malloc(m, bytes); + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ + alignment = MIN_CHUNK_SIZE; + if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ + size_t a = MALLOC_ALIGNMENT << 1; + while (a < alignment) a <<= 1; + alignment = a; + } + + if (bytes >= MAX_REQUEST - alignment) { + if (m != 0) { /* Test isn't needed but avoids compiler warning */ + MALLOC_FAILURE_ACTION; + } + } + else { + size_t nb = request2size(bytes); + size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + char* mem = (char*)internal_malloc(m, req); + if (mem != 0) { + void* leader = 0; + void* trailer = 0; + mchunkptr p = mem2chunk(mem); + + if (PREACTION(m)) return 0; + if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((size_t)(((size_t)(mem + + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? +br : br+alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; + + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + newp->prev_foot = p->prev_foot + leadsize; + newp->head = newsize; + } + else { /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); + set_inuse(m, p, leadsize); + leader = chunk2mem(p); + } + p = newp; + } + + /* Give back spare room at the end */ + if (!is_mmapped(p)) { + size_t size = chunksize(p); + if (size > nb + MIN_CHUNK_SIZE) { + size_t remainder_size = size - nb; + mchunkptr remainder = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, remainder, remainder_size); + trailer = chunk2mem(remainder); + } + } + + assert (chunksize(p) >= nb); + assert((((size_t)(chunk2mem(p))) % alignment) == 0); + check_inuse_chunk(m, p); + POSTACTION(m); + if (leader != 0) { + internal_free(m, leader); + } + if (trailer != 0) { + internal_free(m, trailer); + } + return chunk2mem(p); + } + } + return 0; +} + +/* ------------------------ comalloc/coalloc support --------------------- */ + +static void** ialloc(mstate m, + size_t n_elements, + size_t* sizes, + int opts, + void* chunks[]) { + /* + This provides common support for independent_X routines, handling + all of the combinations that can result. + + The opts arg has: + bit 0 set if all elements are same size (using sizes[0]) + bit 1 set if elements should be zeroed + */ + + size_t element_size; /* chunksize of each element, if all same */ + size_t contents_size; /* total size of elements */ + size_t array_size; /* request size of pointer array */ + void* mem; /* malloced aggregate space */ + mchunkptr p; /* corresponding chunk */ + size_t remainder_size; /* remaining bytes while splitting */ + void** marray; /* either "chunks" or malloced ptr array */ + mchunkptr array_chunk; /* chunk for malloced ptr array */ + flag_t was_enabled; /* to disable mmap */ + size_t size; + size_t i; + + ensure_initialization(); + /* compute array length, if needed */ + if (chunks != 0) { + if (n_elements == 0) + return chunks; /* nothing to do */ + marray = chunks; + array_size = 0; + } + else { + /* if empty req, must still return chunk representing empty array */ + if (n_elements == 0) + return (void**)internal_malloc(m, 0); + marray = 0; + array_size = request2size(n_elements * (sizeof(void*))); + } + + /* compute total element size */ + if (opts & 0x1) { /* all-same-size */ + element_size = request2size(*sizes); + contents_size = n_elements * element_size; + } + else { /* add up all the sizes */ + element_size = 0; + contents_size = 0; + for (i = 0; i != n_elements; ++i) + contents_size += request2size(sizes[i]); + } + + size = contents_size + array_size; + + /* + Allocate the aggregate chunk. First disable direct-mmapping so + malloc won't use it, since we would not be able to later + free/realloc space internal to a segregated mmap region. + */ + was_enabled = use_mmap(m); + disable_mmap(m); + mem = internal_malloc(m, size - CHUNK_OVERHEAD); + if (was_enabled) + enable_mmap(m); + if (mem == 0) + return 0; + + if (PREACTION(m)) return 0; + p = mem2chunk(mem); + remainder_size = chunksize(p); + + assert(!is_mmapped(p)); + + if (opts & 0x2) { /* optionally clear the elements */ + memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); + } + + /* If not provided, allocate the pointer array as final part of chunk */ + if (marray == 0) { + size_t array_chunk_size; + array_chunk = chunk_plus_offset(p, contents_size); + array_chunk_size = remainder_size - contents_size; + marray = (void**) (chunk2mem(array_chunk)); + set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); + remainder_size = contents_size; + } + + /* split out elements */ + for (i = 0; ; ++i) { + marray[i] = chunk2mem(p); + if (i != n_elements-1) { + if (element_size != 0) + size = element_size; + else + size = request2size(sizes[i]); + remainder_size -= size; + set_size_and_pinuse_of_inuse_chunk(m, p, size); + p = chunk_plus_offset(p, size); + } + else { /* the final element absorbs any overallocation slop */ + set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); + break; + } + } + +#if DEBUG + if (marray != chunks) { + /* final element must have exactly exhausted chunk */ + if (element_size != 0) { + assert(remainder_size == element_size); + } + else { + assert(remainder_size == request2size(sizes[i])); + } + check_inuse_chunk(m, mem2chunk(marray)); + } + for (i = 0; i != n_elements; ++i) + check_inuse_chunk(m, mem2chunk(marray[i])); + +#endif /* DEBUG */ + + POSTACTION(m); + return marray; +} + + +/* -------------------------- public routines ---------------------------- */ + +#if !ONLY_MSPACES + +void* rdlmalloc(size_t bytes) { + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to + represent as a chunk.) + 2. If it is big enough, use the dv chunk, which is normally the + chunk adjacent to the one used for the most recent small request. + 3. If one exists, split the smallest available chunk in a bin, + saving remainder in dv. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, and use it + if it is better fitting than dv chunk, splitting if necessary. + 2. If better fitting than any binned chunk, use the dv chunk. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it + + The ugly goto's here ensure that postaction occurs along all paths. + */ + +#if USE_LOCKS + ensure_initialization(); /* initialize in sys_alloc if not using locks */ +#endif + + if (!PREACTION(gm)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = gm->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(gm, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(gm, b, p, idx); + set_inuse_and_pinuse(gm, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb > gm->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(gm, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(gm, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(gm, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(gm, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + + if (nb <= gm->dvsize) { + size_t rsize = gm->dvsize - nb; + mchunkptr p = gm->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = gm->dv = chunk_plus_offset(p, nb); + gm->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + } + else { /* exhaust dv */ + size_t dvs = gm->dvsize; + gm->dvsize = 0; + gm->dv = 0; + set_inuse_and_pinuse(gm, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; + mchunkptr p = gm->top; + mchunkptr r = gm->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + mem = chunk2mem(p); + check_top_chunk(gm, gm->top); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + mem = sys_alloc(gm, nb); + +postaction: + POSTACTION(gm); + return mem; + } + + return 0; +} + +void rdlfree(void* mem) { + /* + Consolidate freed chunks with preceeding or succeeding bordering + free chunks, if they exist, and then place in a bin. Intermixed + with special cases for top, dv, mmapped chunks, and usage errors. + */ + + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } +erroraction: + USAGE_ERROR_ACTION(fm, p); +postaction: + POSTACTION(fm); + } + } +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ +} + +void* rdlcalloc(size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = rdlmalloc(req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +void* rdlrealloc(void* oldmem, size_t bytes) { + if (oldmem == 0) + return rdlmalloc(bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + rdlfree(oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(mem2chunk(oldmem)); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + return internal_realloc(m, oldmem, bytes); + } +} + +void* rdlmemalign(size_t alignment, size_t bytes) { + return internal_memalign(gm, alignment, bytes); +} + +void** rdlindependent_calloc(size_t n_elements, size_t elem_size, + void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + return ialloc(gm, n_elements, &sz, 3, chunks); +} + +void** rdlindependent_comalloc(size_t n_elements, size_t sizes[], + void* chunks[]) { + return ialloc(gm, n_elements, sizes, 0, chunks); +} + +void* rdlvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return rdlmemalign(pagesz, bytes); +} + +void* rdlpvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return rdlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); +} + +int rdlmalloc_trim(size_t pad) { + int result = 0; + ensure_initialization(); + if (!PREACTION(gm)) { + result = sys_trim(gm, pad); + POSTACTION(gm); + } + return result; +} + +size_t rdlmalloc_footprint(void) { + return gm->footprint; +} + +size_t dlmalloc_max_footprint(void) { + return gm->max_footprint; +} + +#if !NO_MALLINFO +struct mallinfo rdlmallinfo(void) { + return internal_mallinfo(gm); +} +#endif /* NO_MALLINFO */ + +void rdlmalloc_stats() { + internal_malloc_stats(gm); +} + +int rdlmallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* !ONLY_MSPACES */ + +size_t rdlmalloc_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +/* ----------------------------- user mspaces ---------------------------- */ + +#if MSPACES + +static mstate init_user_mstate(char* tbase, size_t tsize) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + memset(m, 0, msize); + INITIAL_LOCK(&m->mutex); + msp->head = (msize|INUSE_BITS); + m->seg.base = m->least_addr = tbase; + m->seg.size = m->footprint = m->max_footprint = tsize; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + m->mflags = mparams.default_mflags; + m->extp = 0; + m->exts = 0; + disable_contiguous(m); + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); + check_top_chunk(m, m->top); + return m; +} + +mspace rak_create_mspace(size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + size_t rs = ((capacity == 0)? mparams.granularity : + (capacity + TOP_FOOT_SIZE + msize)); + size_t tsize = granularity_align(rs); + char* tbase = (char*)(CALL_MMAP(tsize)); + if (tbase != CMFAIL) { + m = init_user_mstate(tbase, tsize); + m->seg.sflags = USE_MMAP_BIT; + set_lock(m, locked); + } + } + return (mspace)m; +} + +mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity > msize + TOP_FOOT_SIZE && + capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + m = init_user_mstate((char*)base, capacity); + m->seg.sflags = EXTERN_BIT; + set_lock(m, locked); + } + return (mspace)m; +} + +int rak_mspace_track_large_chunks(mspace msp, int enable) { + int ret = 0; + mstate ms = (mstate)msp; + if (!PREACTION(ms)) { + if (!use_mmap(ms)) + ret = 1; + if (!enable) + enable_mmap(ms); + else + disable_mmap(ms); + POSTACTION(ms); + } + return ret; +} + +size_t rak_destroy_mspace(mspace msp) { + size_t freed = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + msegmentptr sp = &ms->seg; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + flag_t flag = sp->sflags; + sp = sp->next; + if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && + CALL_MUNMAP(base, size) == 0) + freed += size; + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return freed; +} + +/* +mspace versions of routines are near-clones of the global +versions. This is not so nice but better than the alternatives. +*/ + + +void* rak_mspace_malloc(mspace msp, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (!PREACTION(ms)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(ms, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(ms, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } + else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + check_top_chunk(ms, ms->top); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + mem = sys_alloc(ms, nb); + +postaction: + POSTACTION(ms); + return mem; + } + + return 0; +} + +void rak_mspace_free(mspace msp, void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + msp = msp; /* placate people compiling -Wunused */ +#else /* FOOTERS */ + mstate fm = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } +erroraction: + USAGE_ERROR_ACTION(fm, p); +postaction: + POSTACTION(fm); + } + } +} + +void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = internal_malloc(ms, req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +void* rak_mspace_realloc(mspace msp, void* oldmem, size_t bytes) { + if (oldmem == 0) + return rak_mspace_malloc(msp, bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + rak_mspace_free(msp, oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if FOOTERS + mchunkptr p = mem2chunk(oldmem); + mstate ms = get_mstate_for(p); +#else /* FOOTERS */ + mstate ms = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_realloc(ms, oldmem, bytes); + } +} + +void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_memalign(ms, alignment, bytes); +} + +void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, &sz, 3, chunks); +} + +void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, sizes, 0, chunks); +} + +int rak_mspace_trim(mspace msp, size_t pad) { + int result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + result = sys_trim(ms, pad); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +void rak_mspace_malloc_stats(mspace msp) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + internal_malloc_stats(ms); + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} + +size_t rak_mspace_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + + +size_t mspace_max_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->max_footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + + +#if !NO_MALLINFO +struct mallinfo rak_mspace_mallinfo(mspace msp) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + } + return internal_mallinfo(ms); +} +#endif /* NO_MALLINFO */ + +size_t rak_mspace_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +int rak_mspace_mallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* MSPACES */ + + +/* -------------------- Alternative MORECORE functions ------------------- */ + +/* +Guidelines for creating a custom version of MORECORE: + +* For best performance, MORECORE should allocate in multiples of pagesize. +* MORECORE may allocate more memory than requested. (Or even less, +but this will usually result in a malloc failure.) +* MORECORE must not allocate memory when given argument zero, but +instead return one past the end address of memory from previous +nonzero call. +* For best performance, consecutive calls to MORECORE with positive +arguments should return increasing addresses, indicating that +space has been contiguously extended. +* Even though consecutive calls to MORECORE need not return contiguous +addresses, it must be OK for malloc'ed chunks to span multiple +regions in those cases where they do happen to be contiguous. +* MORECORE need not handle negative arguments -- it may instead +just return MFAIL when given negative arguments. +Negative arguments are always multiples of pagesize. MORECORE +must not misinterpret negative args as large positive unsigned +args. You can suppress all such calls from even occurring by defining +MORECORE_CANNOT_TRIM, + +As an example alternative MORECORE, here is a custom allocator +kindly contributed for pre-OSX macOS. It uses virtually but not +necessarily physically contiguous non-paged memory (locked in, +present and won't get swapped out). You can use it by uncommenting +this section, adding some #includes, and setting up the appropriate +defines above: + +#define MORECORE osMoreCore + +There is also a shutdown routine that should somehow be called for +cleanup upon program exit. + +#define MAX_POOL_ENTRIES 100 +#define MINIMUM_MORECORE_SIZE (64 * 1024U) +static int next_os_pool; +void *our_os_pools[MAX_POOL_ENTRIES]; + +void *osMoreCore(int size) +{ +void *ptr = 0; +static void *sbrk_top = 0; + +if (size > 0) +{ +if (size < MINIMUM_MORECORE_SIZE) +size = MINIMUM_MORECORE_SIZE; +if (CurrentExecutionLevel() == kTaskLevel) +ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); +if (ptr == 0) +{ +return (void *) MFAIL; +} +// save ptrs so they can be freed during cleanup +our_os_pools[next_os_pool] = ptr; +next_os_pool++; +ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); +sbrk_top = (char *) ptr + size; +return ptr; +} +else if (size < 0) +{ +// we don't currently support shrink behavior +return (void *) MFAIL; +} +else +{ +return sbrk_top; +} +} + +// cleanup any allocated memory pools +// called as last thing before shutting down driver + +void osCleanupMem(void) +{ +void **ptr; + +for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) +if (*ptr) +{ +PoolDeallocate(*ptr); +*ptr = 0; +} +} + +*/ + + +/* ----------------------------------------------------------------------- +History: +V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) +* Use zeros instead of prev foot for is_mmapped +* Add rak_mspace_track_large_chunks; thanks to Jean Brouwers +* Fix set_inuse in internal_realloc; thanks to Jean Brouwers +* Fix insufficient sys_alloc padding when using 16byte alignment +* Fix bad error check in rak_mspace_footprint +* Adaptations for ptmalloc; thanks to Wolfram Gloger. +* Reentrant spin locks; thanks to Earl Chew and others +* Win32 improvements; thanks to Niall Douglas and Earl Chew +* Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options +* Extension hook in malloc_state +* Various small adjustments to reduce warnings on some compilers +* Various configuration extensions/changes for more platforms. Thanks +to all who contributed these. + +V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) +* Add max_footprint functions +* Ensure all appropriate literals are size_t +* Fix conditional compilation problem for some #define settings +* Avoid concatenating segments with the one provided +in rak_create_mspace_with_base +* Rename some variables to avoid compiler shadowing warnings +* Use explicit lock initialization. +* Better handling of sbrk interference. +* Simplify and fix segment insertion, trimming and mspace_destroy +* Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x +* Thanks especially to Dennis Flanagan for help on these. + +V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) +* Fix memalign brace error. + +V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) +* Fix improper #endif nesting in C++ +* Add explicit casts needed for C++ + +V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) +* Use trees for large bins +* Support mspaces +* Use segments to unify sbrk-based and mmap-based system allocation, +removing need for emulation on most platforms without sbrk. +* Default safety checks +* Optional footer checks. Thanks to William Robertson for the idea. +* Internal code refactoring +* Incorporate suggestions and platform-specific changes. +Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, +Aaron Bachmann, Emery Berger, and others. +* Speed up non-fastbin processing enough to remove fastbins. +* Remove useless cfree() to avoid conflicts with other apps. +* Remove internal memcpy, memset. Compilers handle builtins better. +* Remove some options that no one ever used and rename others. + +V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) +* Fix malloc_state bitmap array misdeclaration + +V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) +* Allow tuning of FIRST_SORTED_BIN_SIZE +* Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. +* Better detection and support for non-contiguousness of MORECORE. +Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger +* Bypass most of malloc if no frees. Thanks To Emery Berger. +* Fix freeing of old top non-contiguous chunk im sysmalloc. +* Raised default trim and map thresholds to 256K. +* Fix mmap-related #defines. Thanks to Lubos Lunak. +* Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. +* Branch-free bin calculation +* Default trim and mmap thresholds now 256K. + +V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) +* Introduce independent_comalloc and independent_calloc. +Thanks to Michael Pachos for motivation and help. +* Make optional .h file available +* Allow > 2GB requests on 32bit systems. +* new DL_PLATFORM_WIN32 sbrk, mmap, munmap, lock code from . +Thanks also to Andreas Mueller , +and Anonymous. +* Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for +helping test this.) +* memalign: check alignment arg +* realloc: don't try to shift chunks backwards, since this +leads to more fragmentation in some programs and doesn't +seem to help in any others. +* Collect all cases in malloc requiring system memory into sysmalloc +* Use mmap as backup to sbrk +* Place all internal state in malloc_state +* Introduce fastbins (although similar to 2.5.1) +* Many minor tunings and cosmetic improvements +* Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK +* Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS +Thanks to Tony E. Bennett and others. +* Include errno.h to support default failure action. + +V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) +* return null for negative arguments +* Added Several DL_PLATFORM_WIN32 cleanups from Martin C. Fong +* Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' +(e.g. DL_PLATFORM_WIN32 platforms) +* Cleanup header file inclusion for DL_PLATFORM_WIN32 platforms +* Cleanup code to avoid Microsoft Visual C++ compiler complaints +* Add 'USE_DL_PREFIX' to quickly allow co-existence with existing +memory allocation routines +* Set 'malloc_getpagesize' for DL_PLATFORM_WIN32 platforms (needs more work) +* Use 'assert' rather than 'ASSERT' in DL_PLATFORM_WIN32 code to conform to +usage of 'assert' in non-DL_PLATFORM_WIN32 code +* Improve DL_PLATFORM_WIN32 'sbrk()' emulation's 'findRegion()' routine to +avoid infinite loop +* Always call 'fREe()' rather than 'free()' + +V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) +* Fixed ordering problem with boundary-stamping + +V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) +* Added pvalloc, as recommended by H.J. Liu +* Added 64bit pointer support mainly from Wolfram Gloger +* Added anonymously donated DL_PLATFORM_WIN32 sbrk emulation +* Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen +* malloc_extend_top: fix mask error that caused wastage after +foreign sbrks +* Add linux mremap support code from HJ Liu + +V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) +* Integrated most documentation with the code. +* Add support for mmap, with help from +Wolfram Gloger (Gloger@lrz.uni-muenchen.de). +* Use last_remainder in more cases. +* Pack bins using idea from colin@nyx10.cs.du.edu +* Use ordered bins instead of best-fit threshhold +* Eliminate block-local decls to simplify tracing and debugging. +* Support another case of realloc via move into top +* Fix error occuring when initial sbrk_base not word-aligned. +* Rely on page size for units instead of SBRK_UNIT to +avoid surprises about sbrk alignment conventions. +* Add mallinfo, mallopt. Thanks to Raymond Nijssen +(raymond@es.ele.tue.nl) for the suggestion. +* Add `pad' argument to malloc_trim and top_pad mallopt parameter. +* More precautions for cases where other routines call sbrk, +courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). +* Added macros etc., allowing use in linux libc from +H.J. Lu (hjl@gnu.ai.mit.edu) +* Inverted this history list + +V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) +* Re-tuned and fixed to behave more nicely with V2.6.0 changes. +* Removed all preallocation code since under current scheme +the work required to undo bad preallocations exceeds +the work saved in good cases for most test programs. +* No longer use return list or unconsolidated bins since +no scheme using them consistently outperforms those that don't +given above changes. +* Use best fit for very large chunks to prevent some worst-cases. +* Added some support for debugging + +V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) +* Removed footers when chunks are in use. Thanks to +Paul Wilson (wilson@cs.texas.edu) for the suggestion. + +V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) +* Added malloc_trim, with help from Wolfram Gloger +(wmglo@Dent.MED.Uni-Muenchen.DE). + +V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + +V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) +* realloc: try to expand in both directions +* malloc: swap order of clean-bin strategy; +* realloc: only conditionally expand backwards +* Try not to scavenge used bins +* Use bin counts as a guide to preallocation +* Occasionally bin return list chunks in first scan +* Add a few optimizations from colin@nyx10.cs.du.edu + +V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) +* faster bin computation & slightly different binning +* merged all consolidations to one part of malloc proper +(eliminating old malloc_find_space & malloc_clean_bin) +* Scan 2 returns chunks (not just 1) +* Propagate failure in realloc if malloc returns 0 +* Add stuff to allow compilation on non-ANSI compilers +from kpv@research.att.com + +V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) +* removed potential for odd address access in prev_chunk +* removed dependency on getpagesize.h +* misc cosmetics and a bit more internal documentation +* anticosmetics: mangled names in macros to evade debugger strangeness +* tested on sparc, hp-700, dec-mips, rs6000 +with gcc & native cc (hp, dec only) allowing +Detlefs & Zorn comparison study (in SIGPLAN Notices.) + +Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) +* Based loosely on libg++-1.2X malloc. (It retains some of the overall +structure of old version, but most details differ.) + +*/ + +#endif // _RAKNET_SUPPORT_DL_MALLOC diff --git a/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc.h b/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc.h index 9180496..28c67ec 100755 --- a/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc.h +++ b/project/lib_projects/raknet/jni/RaknetSources/rdlmalloc.h @@ -1,2261 +1,2261 @@ -#ifdef _RAKNET_SUPPORT_DL_MALLOC - -/* -Default header file for malloc-2.8.x, written by Doug Lea -and released to the public domain, as explained at -http://creativecommons.org/licenses/publicdomain. - -last update: Wed May 27 14:25:17 2009 Doug Lea (dl at gee) - -This header is for ANSI C/C++ only. You can set any of -the following #defines before including: - -* If USE_DL_PREFIX is defined, it is assumed that malloc.c -was also compiled with this option, so all routines -have names starting with "dl". - -* If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this -file will be #included AFTER . This is needed only if -your system defines a struct mallinfo that is incompatible with the -standard one declared here. Otherwise, you can include this file -INSTEAD of your system system . At least on ANSI, all -declarations should be compatible with system versions - -* If MSPACES is defined, declarations for mspace versions are included. -*/ - -#ifndef MALLOC_280_H -#define MALLOC_280_H - -#include "rdlmalloc-options.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#include /* for size_t */ - -#ifndef ONLY_MSPACES -#define ONLY_MSPACES 0 /* define to a value */ -#endif /* ONLY_MSPACES */ -#ifndef NO_MALLINFO -#define NO_MALLINFO 0 -#endif /* NO_MALLINFO */ - - -#if !ONLY_MSPACES - -#ifndef USE_DL_PREFIX -#define rdlcalloc calloc -#define rdlfree free -#define rdlmalloc malloc -#define rdlmemalign memalign -#define rdlrealloc realloc -#define rdlvalloc valloc -#define rdlpvalloc pvalloc -#define rdlmallinfo mallinfo -#define rdlmallopt mallopt -#define rdlmalloc_trim malloc_trim -#define rdlmalloc_stats malloc_stats -#define rdlmalloc_usable_size malloc_usable_size -#define rdlmalloc_footprint malloc_footprint -#define rdlindependent_calloc independent_calloc -#define rdlindependent_comalloc independent_comalloc -#endif /* USE_DL_PREFIX */ -#if !NO_MALLINFO -#ifndef HAVE_USR_INCLUDE_MALLOC_H -#ifndef _MALLOC_H -#ifndef MALLINFO_FIELD_TYPE -#define MALLINFO_FIELD_TYPE size_t -#endif /* MALLINFO_FIELD_TYPE */ -#ifndef STRUCT_MALLINFO_DECLARED -#define STRUCT_MALLINFO_DECLARED 1 - struct mallinfo { - MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ - MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ - MALLINFO_FIELD_TYPE smblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ - MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ - MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ - MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ - MALLINFO_FIELD_TYPE fordblks; /* total free space */ - MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ - }; -#endif /* STRUCT_MALLINFO_DECLARED */ -#endif /* _MALLOC_H */ -#endif /* HAVE_USR_INCLUDE_MALLOC_H */ -#endif /* !NO_MALLINFO */ - - /* - malloc(size_t n) - Returns a pointer to a newly allocated chunk of at least n bytes, or - null if no space is available, in which case errno is set to ENOMEM - on ANSI C systems. - - If n is zero, malloc returns a minimum-sized chunk. (The minimum - size is 16 bytes on most 32bit systems, and 32 bytes on 64bit - systems.) Note that size_t is an unsigned type, so calls with - arguments that would be negative if signed are interpreted as - requests for huge amounts of space, which will often fail. The - maximum supported value of n differs across systems, but is in all - cases less than the maximum representable value of a size_t. - */ - void* rdlmalloc(size_t); - - /* - free(void* p) - Releases the chunk of memory pointed to by p, that had been previously - allocated using malloc or a related routine such as realloc. - It has no effect if p is null. If p was not malloced or already - freed, free(p) will by default cuase the current program to abort. - */ - void rdlfree(void*); - - /* - calloc(size_t n_elements, size_t element_size); - Returns a pointer to n_elements * element_size bytes, with all locations - set to zero. - */ - void* rdlcalloc(size_t, size_t); - - /* - realloc(void* p, size_t n) - Returns a pointer to a chunk of size n that contains the same data - as does chunk p up to the minimum of (n, p's size) bytes, or null - if no space is available. - - The returned pointer may or may not be the same as p. The algorithm - prefers extending p in most cases when possible, otherwise it - employs the equivalent of a malloc-copy-free sequence. - - If p is null, realloc is equivalent to malloc. - - If space is not available, realloc returns null, errno is set (if on - ANSI) and p is NOT freed. - - if n is for fewer bytes than already held by p, the newly unused - space is lopped off and freed if possible. realloc with a size - argument of zero (re)allocates a minimum-sized chunk. - - The old unix realloc convention of allowing the last-free'd chunk - to be used as an argument to realloc is not supported. - */ - - void* rdlrealloc(void*, size_t); - - /* - memalign(size_t alignment, size_t n); - Returns a pointer to a newly allocated chunk of n bytes, aligned - in accord with the alignment argument. - - The alignment argument should be a power of two. If the argument is - not a power of two, the nearest greater power is used. - 8-byte alignment is guaranteed by normal malloc calls, so don't - bother calling memalign with an argument of 8 or less. - - Overreliance on memalign is a sure way to fragment space. - */ - void* rdlmemalign(size_t, size_t); - - /* - valloc(size_t n); - Equivalent to memalign(pagesize, n), where pagesize is the page - size of the system. If the pagesize is unknown, 4096 is used. - */ - void* rdlvalloc(size_t); - - /* - mallopt(int parameter_number, int parameter_value) - Sets tunable parameters The format is to provide a - (parameter-number, parameter-value) pair. mallopt then sets the - corresponding parameter to the argument value if it can (i.e., so - long as the value is meaningful), and returns 1 if successful else - 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, - normally defined in malloc.h. None of these are use in this malloc, - so setting them has no effect. But this malloc also supports other - options in mallopt: - - Symbol param # default allowed param values - M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) - M_GRANULARITY -2 page size any power of 2 >= page size - M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) - */ - int rdlmallopt(int, int); - -#define M_TRIM_THRESHOLD (-1) -#define M_GRANULARITY (-2) -#define M_MMAP_THRESHOLD (-3) - - - /* - malloc_footprint(); - Returns the number of bytes obtained from the system. The total - number of bytes allocated by malloc, realloc etc., is less than this - value. Unlike mallinfo, this function returns only a precomputed - result, so can be called frequently to monitor memory consumption. - Even if locks are otherwise defined, this function does not use them, - so results might not be up to date. - */ - size_t rdlmalloc_footprint(); - -#if !NO_MALLINFO - /* - mallinfo() - Returns (by copy) a struct containing various summary statistics: - - arena: current total non-mmapped bytes allocated from system - ordblks: the number of free chunks - smblks: always zero. - hblks: current number of mmapped regions - hblkhd: total bytes held in mmapped regions - usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. - fsmblks: always zero - uordblks: current total allocated space (normal or mmapped) - fordblks: total free space - keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) - - Because these fields are ints, but internal bookkeeping may - be kept as longs, the reported values may wrap around zero and - thus be inaccurate. - */ - - struct mallinfo rdlmallinfo(void); -#endif /* NO_MALLINFO */ - - /* - independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); - - independent_calloc is similar to calloc, but instead of returning a - single cleared space, it returns an array of pointers to n_elements - independent elements that can hold contents of size elem_size, each - of which starts out cleared, and can be independently freed, - realloc'ed etc. The elements are guaranteed to be adjacently - allocated (this is not guaranteed to occur with multiple callocs or - mallocs), which may also improve cache locality in some - applications. - - The "chunks" argument is optional (i.e., may be null, which is - probably the most typical usage). If it is null, the returned array - is itself dynamically allocated and should also be freed when it is - no longer needed. Otherwise, the chunks array must be of at least - n_elements in length. It is filled in with the pointers to the - chunks. - - In either case, independent_calloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and "chunks" - is null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use regular calloc and assign pointers into this - space to represent elements. (In this case though, you cannot - independently free elements.) - - independent_calloc simplifies and speeds up implementations of many - kinds of pools. It may also be useful when constructing large data - structures that initially have a fixed number of fixed-sized nodes, - but the number is not known at compile time, and some of the nodes - may later need to be freed. For example: - - struct Node { int item; struct Node* next; }; - - struct Node* build_list() { - struct Node** pool; - int n = read_number_of_nodes_needed(); - if (n <= 0) return 0; - pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); - if (pool == 0) die(); - // organize into a linked list... - struct Node* first = pool[0]; - for (i = 0; i < n-1; ++i) - pool[i]->next = pool[i+1]; - free(pool); // Can now free the array (or not, if it is needed later) - return first; - } - */ - void** rdlindependent_calloc(size_t, size_t, void**); - - /* - independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); - - independent_comalloc allocates, all at once, a set of n_elements - chunks with sizes indicated in the "sizes" array. It returns - an array of pointers to these elements, each of which can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null). If it is null - the returned array is itself dynamically allocated and should also - be freed when it is no longer needed. Otherwise, the chunks array - must be of at least n_elements in length. It is filled in with the - pointers to the chunks. - - In either case, independent_comalloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and chunks is - null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use a single regular malloc, and assign pointers at - particular offsets in the aggregate space. (In this case though, you - cannot independently free elements.) - - independent_comallac differs from independent_calloc in that each - element may have a different size, and also that it does not - automatically clear elements. - - independent_comalloc can be used to speed up allocation in cases - where several structs or objects must always be allocated at the - same time. For example: - - struct Head { ... } - struct Foot { ... } - - void send_message(char* msg) { - int msglen = strlen(msg); - size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; - void* chunks[3]; - if (independent_comalloc(3, sizes, chunks) == 0) - die(); - struct Head* head = (struct Head*)(chunks[0]); - char* body = (char*)(chunks[1]); - struct Foot* foot = (struct Foot*)(chunks[2]); - // ... - } - - In general though, independent_comalloc is worth using only for - larger values of n_elements. For small values, you probably won't - detect enough difference from series of malloc calls to bother. - - Overuse of independent_comalloc can increase overall memory usage, - since it cannot reuse existing noncontiguous small chunks that - might be available for some of the elements. - */ - void** rdlindependent_comalloc(size_t, size_t*, void**); - - - /* - pvalloc(size_t n); - Equivalent to valloc(minimum-page-that-holds(n)), that is, - round up n to nearest pagesize. - */ - void* rdlpvalloc(size_t); - - /* - malloc_trim(size_t pad); - - If possible, gives memory back to the system (via negative arguments - to sbrk) if there is unused memory at the `high' end of the malloc - pool or in unused MMAP segments. You can call this after freeing - large blocks of memory to potentially reduce the system-level memory - requirements of a program. However, it cannot guarantee to reduce - memory. Under some allocation patterns, some large free blocks of - memory will be locked between two used chunks, so they cannot be - given back to the system. - - The `pad' argument to malloc_trim represents the amount of free - trailing space to leave untrimmed. If this argument is zero, only - the minimum amount of memory to maintain internal data structures - will be left. Non-zero arguments can be supplied to maintain enough - trailing space to service future expected allocations without having - to re-obtain memory from the system. - - Malloc_trim returns 1 if it actually released any memory, else 0. - */ - int rdlmalloc_trim(size_t); - - /* - malloc_stats(); - Prints on stderr the amount of space obtained from the system (both - via sbrk and mmap), the maximum amount (which may be more than - current if malloc_trim and/or munmap got called), and the current - number of bytes allocated via malloc (or realloc, etc) but not yet - freed. Note that this is the number of bytes allocated, not the - number requested. It will be larger than the number requested - because of alignment and bookkeeping overhead. Because it includes - alignment wastage as being in use, this figure may be greater than - zero even when no user-level chunks are allocated. - - The reported current and maximum system memory can be inaccurate if - a program makes other calls to system memory allocation functions - (normally sbrk) outside of malloc. - - malloc_stats prints only the most commonly interesting statistics. - More information can be obtained by calling mallinfo. - */ - void rdlmalloc_stats(); - -#endif /* !ONLY_MSPACES */ - - /* - malloc_usable_size(void* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. malloc_usable_size can be more useful in - debugging and assertions, for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); - */ - size_t rdlmalloc_usable_size(void*); - - -#if MSPACES - - /* - mspace is an opaque type representing an independent - region of space that supports rak_mspace_malloc, etc. - */ - typedef void* mspace; - - /* - rak_create_mspace creates and returns a new independent space with the - given initial capacity, or, if 0, the default granularity size. It - returns null if there is no system memory available to create the - space. If argument locked is non-zero, the space uses a separate - lock to control access. The capacity of the space will grow - dynamically as needed to service rak_mspace_malloc requests. You can - control the sizes of incremental increases of this space by - compiling with a different DEFAULT_GRANULARITY or dynamically - setting with mallopt(M_GRANULARITY, value). - */ - mspace rak_create_mspace(size_t capacity, int locked); - - /* - rak_destroy_mspace destroys the given space, and attempts to return all - of its memory back to the system, returning the total number of - bytes freed. After destruction, the results of access to all memory - used by the space become undefined. - */ - size_t rak_destroy_mspace(mspace msp); - - /* - rak_create_mspace_with_base uses the memory supplied as the initial base - of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this - space is used for bookkeeping, so the capacity must be at least this - large. (Otherwise 0 is returned.) When this initial space is - exhausted, additional memory will be obtained from the system. - Destroying this space will deallocate all additionally allocated - space (if possible) but not the initial base. - */ - mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked); - - /* - rak_mspace_track_large_chunks controls whether requests for large chunks - are allocated in their own untracked mmapped regions, separate from - others in this mspace. By default large chunks are not tracked, - which reduces fragmentation. However, such chunks are not - necessarily released to the system upon rak_destroy_mspace. Enabling - tracking by setting to true may increase fragmentation, but avoids - leakage when relying on rak_destroy_mspace to release all memory - allocated using this space. The function returns the previous - setting. - */ - int rak_mspace_track_large_chunks(mspace msp, int enable); - - /* - rak_mspace_malloc behaves as malloc, but operates within - the given space. - */ - void* rak_mspace_malloc(mspace msp, size_t bytes); - - /* - rak_mspace_free behaves as free, but operates within - the given space. - - If compiled with FOOTERS==1, rak_mspace_free is not actually needed. - free may be called instead of rak_mspace_free because freed chunks from - any space are handled by their originating spaces. - */ - void rak_mspace_free(mspace msp, void* mem); - - /* - rak_mspace_realloc behaves as realloc, but operates within - the given space. - - If compiled with FOOTERS==1, rak_mspace_realloc is not actually - needed. realloc may be called instead of rak_mspace_realloc because - realloced chunks from any space are handled by their originating - spaces. - */ - void* rak_mspace_realloc(mspace msp, void* mem, size_t newsize); - - /* - rak_mspace_calloc behaves as calloc, but operates within - the given space. - */ - void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); - - /* - rak_mspace_memalign behaves as memalign, but operates within - the given space. - */ - void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes); - - /* - rak_mspace_independent_calloc behaves as independent_calloc, but - operates within the given space. - */ - void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]); - - /* - rak_mspace_independent_comalloc behaves as independent_comalloc, but - operates within the given space. - */ - void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]); - - /* - rak_mspace_footprint() returns the number of bytes obtained from the - system for this space. - */ - size_t rak_mspace_footprint(mspace msp); - - -#if !NO_MALLINFO - /* - rak_mspace_mallinfo behaves as mallinfo, but reports properties of - the given space. - */ - struct mallinfo rak_mspace_mallinfo(mspace msp); -#endif /* NO_MALLINFO */ - - /* - malloc_usable_size(void* p) behaves the same as malloc_usable_size; - */ - size_t rak_mspace_usable_size(void* mem); - - /* - rak_mspace_malloc_stats behaves as malloc_stats, but reports - properties of the given space. - */ - void rak_mspace_malloc_stats(mspace msp); - - /* - rak_mspace_trim behaves as malloc_trim, but - operates within the given space. - */ - int rak_mspace_trim(mspace msp, size_t pad); - - /* - An alias for mallopt. - */ - int rak_mspace_mallopt(int, int); - -#endif /* MSPACES */ - -#ifdef __cplusplus -}; /* end of extern "C" */ -#endif - -/* -This is a version (aka rdlmalloc) of malloc/free/realloc written by -Doug Lea and released to the public domain, as explained at -http://creativecommons.org/licenses/publicdomain. Send questions, -comments, complaints, performance data, etc to dl@cs.oswego.edu - -* Version 2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) - -Note: There may be an updated version of this malloc obtainable at -ftp://gee.cs.oswego.edu/pub/misc/malloc.c -Check before installing! - -* Quickstart - -This library is all in one file to simplify the most common usage: -ftp it, compile it (-O3), and link it into another program. All of -the compile-time options default to reasonable values for use on -most platforms. You might later want to step through various -compile-time and dynamic tuning options. - -For convenience, an include file for code using this malloc is at: -ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.4.h -You don't really need this .h file unless you call functions not -defined in your system include files. The .h file contains only the -excerpts from this file needed for using this malloc on ANSI C/C++ -systems, so long as you haven't changed compile-time options about -naming and tuning parameters. If you do, then you can create your -own malloc.h that does include all settings by cutting at the point -indicated below. Note that you may already by default be using a C -library containing a malloc that is based on some version of this -malloc (for example in linux). You might still want to use the one -in this file to customize settings or to avoid overheads associated -with library versions. - -* Vital statistics: - -Supported pointer/size_t representation: 4 or 8 bytes -size_t MUST be an unsigned type of the same width as -pointers. (If you are using an ancient system that declares -size_t as a signed type, or need it to be a different width -than pointers, you can use a previous release of this malloc -(e.g. 2.7.2) supporting these.) - -Alignment: 8 bytes (default) -This suffices for nearly all current machines and C compilers. -However, you can define MALLOC_ALIGNMENT to be wider than this -if necessary (up to 128bytes), at the expense of using more space. - -Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) -8 or 16 bytes (if 8byte sizes) -Each malloced chunk has a hidden word of overhead holding size -and status information, and additional cross-check word -if FOOTERS is defined. - -Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) -8-byte ptrs: 32 bytes (including overhead) - -Even a request for zero bytes (i.e., malloc(0)) returns a -pointer to something of the minimum allocatable size. -The maximum overhead wastage (i.e., number of extra bytes -allocated than were requested in malloc) is less than or equal -to the minimum size, except for requests >= mmap_threshold that -are serviced via mmap(), where the worst case wastage is about -32 bytes plus the remainder from a system page (the minimal -mmap unit); typically 4096 or 8192 bytes. - -Security: static-safe; optionally more or less -The "security" of malloc refers to the ability of malicious -code to accentuate the effects of errors (for example, freeing -space that is not currently malloc'ed or overwriting past the -ends of chunks) in code that calls malloc. This malloc -guarantees not to modify any memory locations below the base of -heap, i.e., static variables, even in the presence of usage -errors. The routines additionally detect most improper frees -and reallocs. All this holds as long as the static bookkeeping -for malloc itself is not corrupted by some other means. This -is only one aspect of security -- these checks do not, and -cannot, detect all possible programming errors. - -If FOOTERS is defined nonzero, then each allocated chunk -carries an additional check word to verify that it was malloced -from its space. These check words are the same within each -execution of a program using malloc, but differ across -executions, so externally crafted fake chunks cannot be -freed. This improves security by rejecting frees/reallocs that -could corrupt heap memory, in addition to the checks preventing -writes to statics that are always on. This may further improve -security at the expense of time and space overhead. (Note that -FOOTERS may also be worth using with MSPACES.) - -By default detected errors cause the program to abort (calling -"abort()"). You can override this to instead proceed past -errors by defining PROCEED_ON_ERROR. In this case, a bad free -has no effect, and a malloc that encounters a bad address -caused by user overwrites will ignore the bad address by -dropping pointers and indices to all known memory. This may -be appropriate for programs that should continue if at all -possible in the face of programming errors, although they may -run out of memory because dropped memory is never reclaimed. - -If you don't like either of these options, you can define -CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything -else. And if if you are sure that your program using malloc has -no errors or vulnerabilities, you can define INSECURE to 1, -which might (or might not) provide a small performance improvement. - -Thread-safety: NOT thread-safe unless USE_LOCKS defined -When USE_LOCKS is defined, each public call to malloc, free, -etc is surrounded with either a pthread mutex or a win32 -spinlock (depending on DL_PLATFORM_WIN32). This is not especially fast, and -can be a major bottleneck. It is designed only to provide -minimal protection in concurrent environments, and to provide a -basis for extensions. If you are using malloc in a concurrent -program, consider instead using nedmalloc -(http://www.nedprod.com/programs/portable/nedmalloc/) or -ptmalloc (See http://www.malloc.de), which are derived -from versions of this malloc. - -System requirements: Any combination of MORECORE and/or MMAP/MUNMAP -This malloc can use unix sbrk or any emulation (invoked using -the CALL_MORECORE macro) and/or mmap/munmap or any emulation -(invoked using CALL_MMAP/CALL_MUNMAP) to get and release system -memory. On most unix systems, it tends to work best if both -MORECORE and MMAP are enabled. On Win32, it uses emulations -based on VirtualAlloc. It also uses common C library functions -like memset. - -Compliance: I believe it is compliant with the Single Unix Specification -(See http://www.unix.org). Also SVID/XPG, ANSI C, and probably -others as well. - -* Overview of algorithms - -This is not the fastest, most space-conserving, most portable, or -most tunable malloc ever written. However it is among the fastest -while also being among the most space-conserving, portable and -tunable. Consistent balance across these factors results in a good -general-purpose allocator for malloc-intensive programs. - -In most ways, this malloc is a best-fit allocator. Generally, it -chooses the best-fitting existing chunk for a request, with ties -broken in approximately least-recently-used order. (This strategy -normally maintains low fragmentation.) However, for requests less -than 256bytes, it deviates from best-fit when there is not an -exactly fitting available chunk by preferring to use space adjacent -to that used for the previous small request, as well as by breaking -ties in approximately most-recently-used order. (These enhance -locality of series of small allocations.) And for very large requests -(>= 256Kb by default), it relies on system memory mapping -facilities, if supported. (This helps avoid carrying around and -possibly fragmenting memory used only for large chunks.) - -All operations (except malloc_stats and mallinfo) have execution -times that are bounded by a constant factor of the number of bits in -a size_t, not counting any clearing in calloc or copying in realloc, -or actions surrounding MORECORE and MMAP that have times -proportional to the number of non-contiguous regions returned by -system allocation routines, which is often just 1. In real-time -applications, you can optionally suppress segment traversals using -NO_SEGMENT_TRAVERSAL, which assures bounded execution even when -system allocators return non-contiguous spaces, at the typical -expense of carrying around more memory and increased fragmentation. - -The implementation is not very modular and seriously overuses -macros. Perhaps someday all C compilers will do as good a job -inlining modular code as can now be done by brute-force expansion, -but now, enough of them seem not to. - -Some compilers issue a lot of warnings about code that is -dead/unreachable only on some platforms, and also about intentional -uses of negation on unsigned types. All known cases of each can be -ignored. - -For a longer but out of date high-level description, see -http://gee.cs.oswego.edu/dl/html/malloc.html - -* MSPACES -If MSPACES is defined, then in addition to malloc, free, etc., -this file also defines rak_mspace_malloc, rak_mspace_free, etc. These -are versions of malloc routines that take an "mspace" argument -obtained using rak_create_mspace, to control all internal bookkeeping. -If ONLY_MSPACES is defined, only these versions are compiled. -So if you would like to use this allocator for only some allocations, -and your system malloc for others, you can compile with -ONLY_MSPACES and then do something like... -static mspace mymspace = rak_create_mspace(0,0); // for example -#define mymalloc(bytes) rak_mspace_malloc(mymspace, bytes) - -(Note: If you only need one instance of an mspace, you can instead -use "USE_DL_PREFIX" to relabel the global malloc.) - -You can similarly create thread-local allocators by storing -mspaces as thread-locals. For example: -static __thread mspace tlms = 0; -void* tlmalloc(size_t bytes) { -if (tlms == 0) tlms = rak_create_mspace(0, 0); -return rak_mspace_malloc(tlms, bytes); -} -void tlfree(void* mem) { rak_mspace_free(tlms, mem); } - -Unless FOOTERS is defined, each mspace is completely independent. -You cannot allocate from one and free to another (although -conformance is only weakly checked, so usage errors are not always -caught). If FOOTERS is defined, then each chunk carries around a tag -indicating its originating mspace, and frees are directed to their -originating spaces. - -------------------------- Compile-time options --------------------------- - -Be careful in setting #define values for numerical constants of type -size_t. On some systems, literal values are not automatically extended -to size_t precision unless they are explicitly casted. You can also -use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. - -DL_PLATFORM_WIN32 default: defined if _WIN32 defined -Defining DL_PLATFORM_WIN32 sets up defaults for MS environment and compilers. -Otherwise defaults are for unix. Beware that there seem to be some -cases where this malloc might not be a pure drop-in replacement for -Win32 malloc: Random-looking failures from Win32 GDI API's (eg; -SetDIBits()) may be due to bugs in some video driver implementations -when pixel buffers are malloc()ed, and the region spans more than -one VirtualAlloc()ed region. Because rdlmalloc uses a small (64Kb) -default granularity, pixel buffers may straddle virtual allocation -regions more often than when using the Microsoft allocator. You can -avoid this by using VirtualAlloc() and VirtualFree() for all pixel -buffers rather than using malloc(). If this is not possible, -recompile this malloc with a larger DEFAULT_GRANULARITY. - -MALLOC_ALIGNMENT default: (size_t)8 -Controls the minimum alignment for malloc'ed chunks. It must be a -power of two and at least 8, even on machines for which smaller -alignments would suffice. It may be defined as larger than this -though. Note however that code and data structures are optimized for -the case of 8-byte alignment. - -MSPACES default: 0 (false) -If true, compile in support for independent allocation spaces. -This is only supported if HAVE_MMAP is true. - -ONLY_MSPACES default: 0 (false) -If true, only compile in mspace versions, not regular versions. - -USE_LOCKS default: 0 (false) -Causes each call to each public routine to be surrounded with -pthread or DL_PLATFORM_WIN32 mutex lock/unlock. (If set true, this can be -overridden on a per-mspace basis for mspace versions.) If set to a -non-zero value other than 1, locks are used, but their -implementation is left out, so lock functions must be supplied manually, -as described below. - -USE_SPIN_LOCKS default: 1 iff USE_LOCKS and on x86 using gcc or MSC -If true, uses custom spin locks for locking. This is currently -supported only for x86 platforms using gcc or recent MS compilers. -Otherwise, posix locks or win32 critical sections are used. - -FOOTERS default: 0 -If true, provide extra checking and dispatching by placing -information in the footers of allocated chunks. This adds -space and time overhead. - -INSECURE default: 0 -If true, omit checks for usage errors and heap space overwrites. - -USE_DL_PREFIX default: NOT defined -Causes compiler to prefix all public routines with the string 'dl'. -This can be useful when you only want to use this malloc in one part -of a program, using your regular system malloc elsewhere. - -ABORT default: defined as abort() -Defines how to abort on failed checks. On most systems, a failed -check cannot die with an "assert" or even print an informative -message, because the underlying print routines in turn call malloc, -which will fail again. Generally, the best policy is to simply call -abort(). It's not very useful to do more than this because many -errors due to overwriting will show up as address faults (null, odd -addresses etc) rather than malloc-triggered checks, so will also -abort. Also, most compilers know that abort() does not return, so -can better optimize code conditionally calling it. - -PROCEED_ON_ERROR default: defined as 0 (false) -Controls whether detected bad addresses cause them to bypassed -rather than aborting. If set, detected bad arguments to free and -realloc are ignored. And all bookkeeping information is zeroed out -upon a detected overwrite of freed heap space, thus losing the -ability to ever return it from malloc again, but enabling the -application to proceed. If PROCEED_ON_ERROR is defined, the -static variable malloc_corruption_error_count is compiled in -and can be examined to see if errors have occurred. This option -generates slower code than the default abort policy. - -DEBUG default: NOT defined -The DEBUG setting is mainly intended for people trying to modify -this code or diagnose problems when porting to new platforms. -However, it may also be able to better isolate user errors than just -using runtime checks. The assertions in the check routines spell -out in more detail the assumptions and invariants underlying the -algorithms. The checking is fairly extensive, and will slow down -execution noticeably. Calling malloc_stats or mallinfo with DEBUG -set will attempt to check every non-mmapped allocated and free chunk -in the course of computing the summaries. - -ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) -Debugging assertion failures can be nearly impossible if your -version of the assert macro causes malloc to be called, which will -lead to a cascade of further failures, blowing the runtime stack. -ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), -which will usually make debugging easier. - -MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 -The action to take before "return 0" when malloc fails to be able to -return memory because there is none available. - -HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES -True if this system supports sbrk or an emulation of it. - -MORECORE default: sbrk -The name of the sbrk-style system routine to call to obtain more -memory. See below for guidance on writing custom MORECORE -functions. The type of the argument to sbrk/MORECORE varies across -systems. It cannot be size_t, because it supports negative -arguments, so it is normally the signed type of the same width as -size_t (sometimes declared as "intptr_t"). It doesn't much matter -though. Internally, we only call it with arguments less than half -the max value of a size_t, which should work across all reasonable -possibilities, although sometimes generating compiler warnings. - -MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE -If true, take advantage of fact that consecutive calls to MORECORE -with positive arguments always return contiguous increasing -addresses. This is true of unix sbrk. It does not hurt too much to -set it true anyway, since malloc copes with non-contiguities. -Setting it false when definitely non-contiguous saves time -and possibly wasted space it would take to discover this though. - -MORECORE_CANNOT_TRIM default: NOT defined -True if MORECORE cannot release space back to the system when given -negative arguments. This is generally necessary only if you are -using a hand-crafted MORECORE function that cannot handle negative -arguments. - -NO_SEGMENT_TRAVERSAL default: 0 -If non-zero, suppresses traversals of memory segments -returned by either MORECORE or CALL_MMAP. This disables -merging of segments that are contiguous, and selectively -releasing them to the OS if unused, but bounds execution times. - -HAVE_MMAP default: 1 (true) -True if this system supports mmap or an emulation of it. If so, and -HAVE_MORECORE is not true, MMAP is used for all system -allocation. If set and HAVE_MORECORE is true as well, MMAP is -primarily used to directly allocate very large blocks. It is also -used as a backup strategy in cases where MORECORE fails to provide -space from system. Note: A single call to MUNMAP is assumed to be -able to unmap memory that may have be allocated using multiple calls -to MMAP, so long as they are adjacent. - -HAVE_MREMAP default: 1 on linux, else 0 -If true realloc() uses mremap() to re-allocate large blocks and -extend or shrink allocation spaces. - -MMAP_CLEARS default: 1 except on WINCE. -True if mmap clears memory so calloc doesn't need to. This is true -for standard unix mmap using /dev/zero and on DL_PLATFORM_WIN32 except for WINCE. - -USE_BUILTIN_FFS default: 0 (i.e., not used) -Causes malloc to use the builtin ffs() function to compute indices. -Some compilers may recognize and intrinsify ffs to be faster than the -supplied C version. Also, the case of x86 using gcc is special-cased -to an asm instruction, so is already as fast as it can be, and so -this setting has no effect. Similarly for Win32 under recent MS compilers. -(On most x86s, the asm version is only slightly faster than the C version.) - -malloc_getpagesize default: derive from system includes, or 4096. -The system page size. To the extent possible, this malloc manages -memory from the system in page-size units. This may be (and -usually is) a function rather than a constant. This is ignored -if DL_PLATFORM_WIN32, where page size is determined using getSystemInfo during -initialization. - -USE_DEV_RANDOM default: 0 (i.e., not used) -Causes malloc to use /dev/random to initialize secure magic seed for -stamping footers. Otherwise, the current time is used. - -NO_MALLINFO default: 0 -If defined, don't compile "mallinfo". This can be a simple way -of dealing with mismatches between system declarations and -those in this file. - -MALLINFO_FIELD_TYPE default: size_t -The type of the fields in the mallinfo struct. This was originally -defined as "int" in SVID etc, but is more usefully defined as -size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set - -REALLOC_ZERO_BYTES_FREES default: not defined -This should be set if a call to realloc with zero bytes should -be the same as a call to free. Some people think it should. Otherwise, -since this malloc returns a unique pointer for malloc(0), so does -realloc(p, 0). - -LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H -LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H -LACKS_STDLIB_H default: NOT defined unless on DL_PLATFORM_WIN32 -Define these if your system does not have these header files. -You might need to manually insert some of the declarations they provide. - -DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, -system_info.dwAllocationGranularity in DL_PLATFORM_WIN32, -otherwise 64K. -Also settable using mallopt(M_GRANULARITY, x) -The unit for allocating and deallocating memory from the system. On -most systems with contiguous MORECORE, there is no reason to -make this more than a page. However, systems with MMAP tend to -either require or encourage larger granularities. You can increase -this value to prevent system allocation functions to be called so -often, especially if they are slow. The value must be at least one -page and must be a power of two. Setting to 0 causes initialization -to either page size or win32 region size. (Note: In previous -versions of malloc, the equivalent of this option was called -"TOP_PAD") - -DEFAULT_TRIM_THRESHOLD default: 2MB -Also settable using mallopt(M_TRIM_THRESHOLD, x) -The maximum amount of unused top-most memory to keep before -releasing via malloc_trim in free(). Automatic trimming is mainly -useful in long-lived programs using contiguous MORECORE. Because -trimming via sbrk can be slow on some systems, and can sometimes be -wasteful (in cases where programs immediately afterward allocate -more large chunks) the value should be high enough so that your -overall system performance would improve by releasing this much -memory. As a rough guide, you might set to a value close to the -average size of a process (program) running on your system. -Releasing this much memory would allow such a process to run in -memory. Generally, it is worth tuning trim thresholds when a -program undergoes phases where several large chunks are allocated -and released in ways that can reuse each other's storage, perhaps -mixed with phases where there are no such chunks at all. The trim -value must be greater than page size to have any useful effect. To -disable trimming completely, you can set to MAX_SIZE_T. Note that the trick -some people use of mallocing a huge space and then freeing it at -program startup, in an attempt to reserve system memory, doesn't -have the intended effect under automatic trimming, since that memory -will immediately be returned to the system. - -DEFAULT_MMAP_THRESHOLD default: 256K -Also settable using mallopt(M_MMAP_THRESHOLD, x) -The request size threshold for using MMAP to directly service a -request. Requests of at least this size that cannot be allocated -using already-existing space will be serviced via mmap. (If enough -normal freed space already exists it is used instead.) Using mmap -segregates relatively large chunks of memory so that they can be -individually obtained and released from the host system. A request -serviced through mmap is never reused by any other request (at least -not directly; the system may just so happen to remap successive -requests to the same locations). Segregating space in this way has -the benefits that: Mmapped space can always be individually released -back to the system, which helps keep the system level memory demands -of a long-lived program low. Also, mapped memory doesn't become -`locked' between other chunks, as can happen with normally allocated -chunks, which means that even trimming via malloc_trim would not -release them. However, it has the disadvantage that the space -cannot be reclaimed, consolidated, and then used to service later -requests, as happens with normal chunks. The advantages of mmap -nearly always outweigh disadvantages for "large" chunks, but the -value of "large" may vary across systems. The default is an -empirically derived value that works well in most systems. You can -disable mmap by setting to MAX_SIZE_T. - -MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP -The number of consolidated frees between checks to release -unused segments when freeing. When using non-contiguous segments, -especially with multiple mspaces, checking only for topmost space -doesn't always suffice to trigger trimming. To compensate for this, -free() will, with a period of MAX_RELEASE_CHECK_RATE (or the -current number of segments, if greater) try to release unused -segments to the OS when freeing chunks that result in -consolidation. The best value for this parameter is a compromise -between slowing down frees with relatively costly checks that -rarely trigger versus holding on to unused memory. To effectively -disable, set to MAX_SIZE_T. This may lead to a very slight speed -improvement at the expense of carrying around more memory. -*/ - -/* Version identifier to allow people to support multiple versions */ -#ifndef DLMALLOC_VERSION -#define DLMALLOC_VERSION 20804 -#endif /* DLMALLOC_VERSION */ - -#include "rdlmalloc-options.h" - -#ifndef WIN32 -#if defined(_XBOX) || defined(X360) -#else -#if defined(_WIN32) -#define DL_PLATFORM_WIN32 1 -#endif /* _WIN32 */ -#ifdef _WIN32_WCE -#define LACKS_FCNTL_H -#define DL_PLATFORM_WIN32 1 -#endif /* _WIN32_WCE */ -#endif -#else -#define DL_PLATFORM_WIN32 1 -#endif /* DL_PLATFORM_WIN32 */ - -#if defined(_XBOX) || defined(X360) -#define HAVE_MMAP 1 -#define HAVE_MORECORE 0 -#define LACKS_UNISTD_H -#define LACKS_SYS_PARAM_H -#define LACKS_SYS_MMAN_H -#define LACKS_STRING_H -#define LACKS_STRINGS_H -#define LACKS_SYS_TYPES_H -#define LACKS_ERRNO_H -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION -#endif -#define MMAP_CLEARS 1 -#endif - -#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) -#define LACKS_SYS_PARAM_H -#include "sysutil\sysutil_sysparam.h" -#define LACKS_SYS_MMAN_H -#endif - - -#ifdef DL_PLATFORM_WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#define HAVE_MMAP 1 -#define HAVE_MORECORE 0 -#define LACKS_UNISTD_H -#define LACKS_SYS_PARAM_H -#define LACKS_SYS_MMAN_H -#define LACKS_STRING_H -#define LACKS_STRINGS_H -#define LACKS_SYS_TYPES_H -#define LACKS_ERRNO_H -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION -#endif /* MALLOC_FAILURE_ACTION */ -#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ -#define MMAP_CLEARS 0 -#else -#define MMAP_CLEARS 1 -#endif /* _WIN32_WCE */ -#endif /* DL_PLATFORM_WIN32 */ - -#if defined(DARWIN) || defined(_DARWIN) -/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ -#ifndef HAVE_MORECORE -#define HAVE_MORECORE 0 -#define HAVE_MMAP 1 -/* OSX allocators provide 16 byte alignment */ -#ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT ((size_t)16U) -#endif -#endif /* HAVE_MORECORE */ -#endif /* DARWIN */ - -#ifndef LACKS_SYS_TYPES_H -#include /* For size_t */ -#endif /* LACKS_SYS_TYPES_H */ - -#if (defined(__GNUC__) && ((defined(__i386__) || defined(__x86_64__)))) || (defined(_MSC_VER) && _MSC_VER>=1310) -#define SPIN_LOCKS_AVAILABLE 1 -#else -#define SPIN_LOCKS_AVAILABLE 0 -#endif - -/* The maximum possible size_t value has all bits set */ -#define MAX_SIZE_T (~(size_t)0) - -#ifndef ONLY_MSPACES -#define ONLY_MSPACES 0 /* define to a value */ -#else -#define ONLY_MSPACES 1 -#endif /* ONLY_MSPACES */ -#ifndef MSPACES -#if ONLY_MSPACES -#define MSPACES 1 -#else /* ONLY_MSPACES */ -#define MSPACES 0 -#endif /* ONLY_MSPACES */ -#endif /* MSPACES */ -#ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT ((size_t)8U) -#endif /* MALLOC_ALIGNMENT */ -#ifndef FOOTERS -#define FOOTERS 0 -#endif /* FOOTERS */ -#ifndef ABORT -#define ABORT abort() -#endif /* ABORT */ -#ifndef ABORT_ON_ASSERT_FAILURE -#define ABORT_ON_ASSERT_FAILURE 1 -#endif /* ABORT_ON_ASSERT_FAILURE */ -#ifndef PROCEED_ON_ERROR -#define PROCEED_ON_ERROR 0 -#endif /* PROCEED_ON_ERROR */ -#ifndef USE_LOCKS -#define USE_LOCKS 0 -#endif /* USE_LOCKS */ -#ifndef USE_SPIN_LOCKS -#if USE_LOCKS && SPIN_LOCKS_AVAILABLE -#define USE_SPIN_LOCKS 1 -#else -#define USE_SPIN_LOCKS 0 -#endif /* USE_LOCKS && SPIN_LOCKS_AVAILABLE. */ -#endif /* USE_SPIN_LOCKS */ -#ifndef INSECURE -#define INSECURE 0 -#endif /* INSECURE */ -#ifndef HAVE_MMAP -#define HAVE_MMAP 1 -#endif /* HAVE_MMAP */ -#ifndef MMAP_CLEARS -#define MMAP_CLEARS 1 -#endif /* MMAP_CLEARS */ -#ifndef HAVE_MREMAP -#ifdef linux -#define HAVE_MREMAP 1 -#else /* linux */ -#define HAVE_MREMAP 0 -#endif /* linux */ -#endif /* HAVE_MREMAP */ -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION errno = ENOMEM; -#endif /* MALLOC_FAILURE_ACTION */ -#ifndef HAVE_MORECORE -#if ONLY_MSPACES -#define HAVE_MORECORE 0 -#else /* ONLY_MSPACES */ -#define HAVE_MORECORE 1 -#endif /* ONLY_MSPACES */ -#endif /* HAVE_MORECORE */ -#if !HAVE_MORECORE -#define MORECORE_CONTIGUOUS 0 -#else /* !HAVE_MORECORE */ -#define MORECORE_DEFAULT sbrk -#ifndef MORECORE_CONTIGUOUS -#define MORECORE_CONTIGUOUS 1 -#endif /* MORECORE_CONTIGUOUS */ -#endif /* HAVE_MORECORE */ -#ifndef DEFAULT_GRANULARITY -#if (MORECORE_CONTIGUOUS || defined(DL_PLATFORM_WIN32)) -#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ -#else /* MORECORE_CONTIGUOUS */ -#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) -#endif /* MORECORE_CONTIGUOUS */ -#endif /* DEFAULT_GRANULARITY */ -#ifndef DEFAULT_TRIM_THRESHOLD -#ifndef MORECORE_CANNOT_TRIM -#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) -#else /* MORECORE_CANNOT_TRIM */ -#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T -#endif /* MORECORE_CANNOT_TRIM */ -#endif /* DEFAULT_TRIM_THRESHOLD */ -#ifndef DEFAULT_MMAP_THRESHOLD -#if HAVE_MMAP -#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) -#else /* HAVE_MMAP */ -#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T -#endif /* HAVE_MMAP */ -#endif /* DEFAULT_MMAP_THRESHOLD */ -#ifndef MAX_RELEASE_CHECK_RATE -#if HAVE_MMAP -#define MAX_RELEASE_CHECK_RATE 4095 -#else -#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T -#endif /* HAVE_MMAP */ -#endif /* MAX_RELEASE_CHECK_RATE */ -#ifndef USE_BUILTIN_FFS -#define USE_BUILTIN_FFS 0 -#endif /* USE_BUILTIN_FFS */ -#ifndef USE_DEV_RANDOM -#define USE_DEV_RANDOM 0 -#endif /* USE_DEV_RANDOM */ -#ifndef NO_MALLINFO -#define NO_MALLINFO 0 -#endif /* NO_MALLINFO */ -#ifndef MALLINFO_FIELD_TYPE -#define MALLINFO_FIELD_TYPE size_t -#endif /* MALLINFO_FIELD_TYPE */ -#ifndef NO_SEGMENT_TRAVERSAL -#define NO_SEGMENT_TRAVERSAL 0 -#endif /* NO_SEGMENT_TRAVERSAL */ - -/* -mallopt tuning options. SVID/XPG defines four standard parameter -numbers for mallopt, normally defined in malloc.h. None of these -are used in this malloc, so setting them has no effect. But this -malloc does support the following options. -*/ - -#define M_TRIM_THRESHOLD (-1) -#define M_GRANULARITY (-2) -#define M_MMAP_THRESHOLD (-3) - -/* ------------------------ Mallinfo declarations ------------------------ */ - -#if !NO_MALLINFO -/* -This version of malloc supports the standard SVID/XPG mallinfo -routine that returns a struct containing usage properties and -statistics. It should work on any system that has a -/usr/include/malloc.h defining struct mallinfo. The main -declaration needed is the mallinfo struct that is returned (by-copy) -by mallinfo(). The malloinfo struct contains a bunch of fields that -are not even meaningful in this version of malloc. These fields are -are instead filled by mallinfo() with other numbers that might be of -interest. - -HAVE_USR_INCLUDE_MALLOC_H should be set if you have a -/usr/include/malloc.h file that includes a declaration of struct -mallinfo. If so, it is included; else a compliant version is -declared below. These must be precisely the same for mallinfo() to -work. The original SVID version of this struct, defined on most -systems with mallinfo, declares all fields as ints. But some others -define as unsigned long. If your system defines the fields using a -type of different width than listed here, you MUST #include your -system version and #define HAVE_USR_INCLUDE_MALLOC_H. -*/ - -/* #define HAVE_USR_INCLUDE_MALLOC_H */ - -#ifdef HAVE_USR_INCLUDE_MALLOC_H -#include "/usr/include/malloc.h" -#else /* HAVE_USR_INCLUDE_MALLOC_H */ -#ifndef STRUCT_MALLINFO_DECLARED -#define STRUCT_MALLINFO_DECLARED 1 -struct mallinfo { - MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ - MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ - MALLINFO_FIELD_TYPE smblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ - MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ - MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ - MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ - MALLINFO_FIELD_TYPE fordblks; /* total free space */ - MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ -}; -#endif /* STRUCT_MALLINFO_DECLARED */ -#endif /* HAVE_USR_INCLUDE_MALLOC_H */ -#endif /* NO_MALLINFO */ - -/* -Try to persuade compilers to inline. The most critical functions for -inlining are defined as macros, so these aren't used for them. -*/ - -#ifndef FORCEINLINE -#if defined(__GNUC__) -#define FORCEINLINE __inline __attribute__ ((always_inline)) -#elif defined(_MSC_VER) -#define FORCEINLINE __forceinline -#endif -#endif -#ifndef NOINLINE -#if defined(__GNUC__) -#define NOINLINE __attribute__ ((noinline)) -#elif defined(_MSC_VER) -#define NOINLINE __declspec(noinline) -#else -#define NOINLINE -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#ifndef FORCEINLINE -#define FORCEINLINE inline -#endif -#endif /* __cplusplus */ -#ifndef FORCEINLINE -#define FORCEINLINE -#endif - -#if !ONLY_MSPACES - - /* ------------------- Declarations of public routines ------------------- */ - -#ifndef USE_DL_PREFIX -#define rdlcalloc calloc -#define rdlfree free -#define rdlmalloc malloc -#define rdlmemalign memalign -#define rdlrealloc realloc -#define rdlvalloc valloc -#define rdlpvalloc pvalloc -#define rdlmallinfo mallinfo -#define rdlmallopt mallopt -#define rdlmalloc_trim malloc_trim -#define rdlmalloc_stats malloc_stats -#define rdlmalloc_usable_size malloc_usable_size -#define rdlmalloc_footprint malloc_footprint -#define dlmalloc_max_footprint malloc_max_footprint -#define rdlindependent_calloc independent_calloc -#define rdlindependent_comalloc independent_comalloc -#endif /* USE_DL_PREFIX */ - - - /* - malloc(size_t n) - Returns a pointer to a newly allocated chunk of at least n bytes, or - null if no space is available, in which case errno is set to ENOMEM - on ANSI C systems. - - If n is zero, malloc returns a minimum-sized chunk. (The minimum - size is 16 bytes on most 32bit systems, and 32 bytes on 64bit - systems.) Note that size_t is an unsigned type, so calls with - arguments that would be negative if signed are interpreted as - requests for huge amounts of space, which will often fail. The - maximum supported value of n differs across systems, but is in all - cases less than the maximum representable value of a size_t. - */ - void* rdlmalloc(size_t); - - /* - free(void* p) - Releases the chunk of memory pointed to by p, that had been previously - allocated using malloc or a related routine such as realloc. - It has no effect if p is null. If p was not malloced or already - freed, free(p) will by default cause the current program to abort. - */ - void rdlfree(void*); - - /* - calloc(size_t n_elements, size_t element_size); - Returns a pointer to n_elements * element_size bytes, with all locations - set to zero. - */ - void* rdlcalloc(size_t, size_t); - - /* - realloc(void* p, size_t n) - Returns a pointer to a chunk of size n that contains the same data - as does chunk p up to the minimum of (n, p's size) bytes, or null - if no space is available. - - The returned pointer may or may not be the same as p. The algorithm - prefers extending p in most cases when possible, otherwise it - employs the equivalent of a malloc-copy-free sequence. - - If p is null, realloc is equivalent to malloc. - - If space is not available, realloc returns null, errno is set (if on - ANSI) and p is NOT freed. - - if n is for fewer bytes than already held by p, the newly unused - space is lopped off and freed if possible. realloc with a size - argument of zero (re)allocates a minimum-sized chunk. - - The old unix realloc convention of allowing the last-free'd chunk - to be used as an argument to realloc is not supported. - */ - - void* rdlrealloc(void*, size_t); - - /* - memalign(size_t alignment, size_t n); - Returns a pointer to a newly allocated chunk of n bytes, aligned - in accord with the alignment argument. - - The alignment argument should be a power of two. If the argument is - not a power of two, the nearest greater power is used. - 8-byte alignment is guaranteed by normal malloc calls, so don't - bother calling memalign with an argument of 8 or less. - - Overreliance on memalign is a sure way to fragment space. - */ - void* rdlmemalign(size_t, size_t); - - /* - valloc(size_t n); - Equivalent to memalign(pagesize, n), where pagesize is the page - size of the system. If the pagesize is unknown, 4096 is used. - */ - void* rdlvalloc(size_t); - - /* - mallopt(int parameter_number, int parameter_value) - Sets tunable parameters The format is to provide a - (parameter-number, parameter-value) pair. mallopt then sets the - corresponding parameter to the argument value if it can (i.e., so - long as the value is meaningful), and returns 1 if successful else - 0. To workaround the fact that mallopt is specified to use int, - not size_t parameters, the value -1 is specially treated as the - maximum unsigned size_t value. - - SVID/XPG/ANSI defines four standard param numbers for mallopt, - normally defined in malloc.h. None of these are use in this malloc, - so setting them has no effect. But this malloc also supports other - options in mallopt. See below for details. Briefly, supported - parameters are as follows (listed defaults are for "typical" - configurations). - - Symbol param # default allowed param values - M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) - M_GRANULARITY -2 page size any power of 2 >= page size - M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) - */ - int rdlmallopt(int, int); - - /* - malloc_footprint(); - Returns the number of bytes obtained from the system. The total - number of bytes allocated by malloc, realloc etc., is less than this - value. Unlike mallinfo, this function returns only a precomputed - result, so can be called frequently to monitor memory consumption. - Even if locks are otherwise defined, this function does not use them, - so results might not be up to date. - */ - size_t rdlmalloc_footprint(void); - - /* - malloc_max_footprint(); - Returns the maximum number of bytes obtained from the system. This - value will be greater than current footprint if deallocated space - has been reclaimed by the system. The peak number of bytes allocated - by malloc, realloc etc., is less than this value. Unlike mallinfo, - this function returns only a precomputed result, so can be called - frequently to monitor memory consumption. Even if locks are - otherwise defined, this function does not use them, so results might - not be up to date. - */ - size_t dlmalloc_max_footprint(void); - -#if !NO_MALLINFO - /* - mallinfo() - Returns (by copy) a struct containing various summary statistics: - - arena: current total non-mmapped bytes allocated from system - ordblks: the number of free chunks - smblks: always zero. - hblks: current number of mmapped regions - hblkhd: total bytes held in mmapped regions - usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. - fsmblks: always zero - uordblks: current total allocated space (normal or mmapped) - fordblks: total free space - keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) - - Because these fields are ints, but internal bookkeeping may - be kept as longs, the reported values may wrap around zero and - thus be inaccurate. - */ - struct mallinfo rdlmallinfo(void); -#endif /* NO_MALLINFO */ - - /* - independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); - - independent_calloc is similar to calloc, but instead of returning a - single cleared space, it returns an array of pointers to n_elements - independent elements that can hold contents of size elem_size, each - of which starts out cleared, and can be independently freed, - realloc'ed etc. The elements are guaranteed to be adjacently - allocated (this is not guaranteed to occur with multiple callocs or - mallocs), which may also improve cache locality in some - applications. - - The "chunks" argument is optional (i.e., may be null, which is - probably the most typical usage). If it is null, the returned array - is itself dynamically allocated and should also be freed when it is - no longer needed. Otherwise, the chunks array must be of at least - n_elements in length. It is filled in with the pointers to the - chunks. - - In either case, independent_calloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and "chunks" - is null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use regular calloc and assign pointers into this - space to represent elements. (In this case though, you cannot - independently free elements.) - - independent_calloc simplifies and speeds up implementations of many - kinds of pools. It may also be useful when constructing large data - structures that initially have a fixed number of fixed-sized nodes, - but the number is not known at compile time, and some of the nodes - may later need to be freed. For example: - - struct Node { int item; struct Node* next; }; - - struct Node* build_list() { - struct Node** pool; - int n = read_number_of_nodes_needed(); - if (n <= 0) return 0; - pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); - if (pool == 0) die(); - // organize into a linked list... - struct Node* first = pool[0]; - for (i = 0; i < n-1; ++i) - pool[i]->next = pool[i+1]; - free(pool); // Can now free the array (or not, if it is needed later) - return first; - } - */ - void** rdlindependent_calloc(size_t, size_t, void**); - - /* - independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); - - independent_comalloc allocates, all at once, a set of n_elements - chunks with sizes indicated in the "sizes" array. It returns - an array of pointers to these elements, each of which can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null). If it is null - the returned array is itself dynamically allocated and should also - be freed when it is no longer needed. Otherwise, the chunks array - must be of at least n_elements in length. It is filled in with the - pointers to the chunks. - - In either case, independent_comalloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and chunks is - null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use a single regular malloc, and assign pointers at - particular offsets in the aggregate space. (In this case though, you - cannot independently free elements.) - - independent_comallac differs from independent_calloc in that each - element may have a different size, and also that it does not - automatically clear elements. - - independent_comalloc can be used to speed up allocation in cases - where several structs or objects must always be allocated at the - same time. For example: - - struct Head { ... } - struct Foot { ... } - - void send_message(char* msg) { - int msglen = strlen(msg); - size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; - void* chunks[3]; - if (independent_comalloc(3, sizes, chunks) == 0) - die(); - struct Head* head = (struct Head*)(chunks[0]); - char* body = (char*)(chunks[1]); - struct Foot* foot = (struct Foot*)(chunks[2]); - // ... - } - - In general though, independent_comalloc is worth using only for - larger values of n_elements. For small values, you probably won't - detect enough difference from series of malloc calls to bother. - - Overuse of independent_comalloc can increase overall memory usage, - since it cannot reuse existing noncontiguous small chunks that - might be available for some of the elements. - */ - void** rdlindependent_comalloc(size_t, size_t*, void**); - - - /* - pvalloc(size_t n); - Equivalent to valloc(minimum-page-that-holds(n)), that is, - round up n to nearest pagesize. - */ - void* rdlpvalloc(size_t); - - /* - malloc_trim(size_t pad); - - If possible, gives memory back to the system (via negative arguments - to sbrk) if there is unused memory at the `high' end of the malloc - pool or in unused MMAP segments. You can call this after freeing - large blocks of memory to potentially reduce the system-level memory - requirements of a program. However, it cannot guarantee to reduce - memory. Under some allocation patterns, some large free blocks of - memory will be locked between two used chunks, so they cannot be - given back to the system. - - The `pad' argument to malloc_trim represents the amount of free - trailing space to leave untrimmed. If this argument is zero, only - the minimum amount of memory to maintain internal data structures - will be left. Non-zero arguments can be supplied to maintain enough - trailing space to service future expected allocations without having - to re-obtain memory from the system. - - Malloc_trim returns 1 if it actually released any memory, else 0. - */ - int rdlmalloc_trim(size_t); - - /* - malloc_stats(); - Prints on stderr the amount of space obtained from the system (both - via sbrk and mmap), the maximum amount (which may be more than - current if malloc_trim and/or munmap got called), and the current - number of bytes allocated via malloc (or realloc, etc) but not yet - freed. Note that this is the number of bytes allocated, not the - number requested. It will be larger than the number requested - because of alignment and bookkeeping overhead. Because it includes - alignment wastage as being in use, this figure may be greater than - zero even when no user-level chunks are allocated. - - The reported current and maximum system memory can be inaccurate if - a program makes other calls to system memory allocation functions - (normally sbrk) outside of malloc. - - malloc_stats prints only the most commonly interesting statistics. - More information can be obtained by calling mallinfo. - */ - void rdlmalloc_stats(void); - -#endif /* ONLY_MSPACES */ - - /* - malloc_usable_size(void* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. malloc_usable_size can be more useful in - debugging and assertions, for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); - */ - size_t rdlmalloc_usable_size(void*); - - -#if MSPACES - - /* - mspace is an opaque type representing an independent - region of space that supports rak_mspace_malloc, etc. - */ - typedef void* mspace; - - /* - rak_create_mspace creates and returns a new independent space with the - given initial capacity, or, if 0, the default granularity size. It - returns null if there is no system memory available to create the - space. If argument locked is non-zero, the space uses a separate - lock to control access. The capacity of the space will grow - dynamically as needed to service rak_mspace_malloc requests. You can - control the sizes of incremental increases of this space by - compiling with a different DEFAULT_GRANULARITY or dynamically - setting with mallopt(M_GRANULARITY, value). - */ - mspace rak_create_mspace(size_t capacity, int locked); - - /* - rak_destroy_mspace destroys the given space, and attempts to return all - of its memory back to the system, returning the total number of - bytes freed. After destruction, the results of access to all memory - used by the space become undefined. - */ - size_t rak_destroy_mspace(mspace msp); - - /* - rak_create_mspace_with_base uses the memory supplied as the initial base - of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this - space is used for bookkeeping, so the capacity must be at least this - large. (Otherwise 0 is returned.) When this initial space is - exhausted, additional memory will be obtained from the system. - Destroying this space will deallocate all additionally allocated - space (if possible) but not the initial base. - */ - mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked); - - /* - rak_mspace_track_large_chunks controls whether requests for large chunks - are allocated in their own untracked mmapped regions, separate from - others in this mspace. By default large chunks are not tracked, - which reduces fragmentation. However, such chunks are not - necessarily released to the system upon rak_destroy_mspace. Enabling - tracking by setting to true may increase fragmentation, but avoids - leakage when relying on rak_destroy_mspace to release all memory - allocated using this space. The function returns the previous - setting. - */ - int rak_mspace_track_large_chunks(mspace msp, int enable); - - - /* - rak_mspace_malloc behaves as malloc, but operates within - the given space. - */ - void* rak_mspace_malloc(mspace msp, size_t bytes); - - /* - rak_mspace_free behaves as free, but operates within - the given space. - - If compiled with FOOTERS==1, rak_mspace_free is not actually needed. - free may be called instead of rak_mspace_free because freed chunks from - any space are handled by their originating spaces. - */ - void rak_mspace_free(mspace msp, void* mem); - - /* - rak_mspace_realloc behaves as realloc, but operates within - the given space. - - If compiled with FOOTERS==1, rak_mspace_realloc is not actually - needed. realloc may be called instead of rak_mspace_realloc because - realloced chunks from any space are handled by their originating - spaces. - */ - void* rak_mspace_realloc(mspace msp, void* mem, size_t newsize); - - /* - rak_mspace_calloc behaves as calloc, but operates within - the given space. - */ - void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); - - /* - rak_mspace_memalign behaves as memalign, but operates within - the given space. - */ - void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes); - - /* - rak_mspace_independent_calloc behaves as independent_calloc, but - operates within the given space. - */ - void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]); - - /* - rak_mspace_independent_comalloc behaves as independent_comalloc, but - operates within the given space. - */ - void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]); - - /* - rak_mspace_footprint() returns the number of bytes obtained from the - system for this space. - */ - size_t rak_mspace_footprint(mspace msp); - - /* - mspace_max_footprint() returns the peak number of bytes obtained from the - system for this space. - */ - size_t mspace_max_footprint(mspace msp); - - -#if !NO_MALLINFO - /* - rak_mspace_mallinfo behaves as mallinfo, but reports properties of - the given space. - */ - struct mallinfo rak_mspace_mallinfo(mspace msp); -#endif /* NO_MALLINFO */ - - /* - malloc_usable_size(void* p) behaves the same as malloc_usable_size; - */ - size_t rak_mspace_usable_size(void* mem); - - /* - rak_mspace_malloc_stats behaves as malloc_stats, but reports - properties of the given space. - */ - void rak_mspace_malloc_stats(mspace msp); - - /* - rak_mspace_trim behaves as malloc_trim, but - operates within the given space. - */ - int rak_mspace_trim(mspace msp, size_t pad); - - /* - An alias for mallopt. - */ - int rak_mspace_mallopt(int, int); - -#endif /* MSPACES */ - -#ifdef __cplusplus -}; /* end of extern "C" */ -#endif /* __cplusplus */ - -/* -======================================================================== -To make a fully customizable malloc.h header file, cut everything -above this line, put into file malloc.h, edit to suit, and #include it -on the next line, as well as in programs that use this malloc. -======================================================================== -*/ - -/* #include "malloc.h" */ - -/*------------------------------ internal #includes ---------------------- */ - -#ifdef DL_PLATFORM_WIN32 -#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ -#endif /* DL_PLATFORM_WIN32 */ - -#include /* for printing in malloc_stats */ - -#ifndef LACKS_ERRNO_H -#include /* for MALLOC_FAILURE_ACTION */ -#endif /* LACKS_ERRNO_H */ - -#if FOOTERS || DEBUG -#include /* for magic initialization */ -#endif /* FOOTERS */ - -#ifndef LACKS_STDLIB_H -#include /* for abort() */ -#endif /* LACKS_STDLIB_H */ - -#ifdef DEBUG -#if ABORT_ON_ASSERT_FAILURE -#undef assert -#define assert(x) if(!(x)) ABORT -#else /* ABORT_ON_ASSERT_FAILURE */ -#include -#endif /* ABORT_ON_ASSERT_FAILURE */ -#else /* DEBUG */ -#ifndef assert -#define assert(x) -#endif -#define DEBUG 0 -#endif /* DEBUG */ - -#ifndef LACKS_STRING_H -#include /* for memset etc */ -#endif /* LACKS_STRING_H */ - -#if USE_BUILTIN_FFS -#ifndef LACKS_STRINGS_H -#include /* for ffs */ -#endif /* LACKS_STRINGS_H */ -#endif /* USE_BUILTIN_FFS */ - -#if HAVE_MMAP -#ifndef LACKS_SYS_MMAN_H -/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ -#if (defined(linux) && !defined(__USE_GNU)) -#define __USE_GNU 1 -#include /* for mmap */ -#undef __USE_GNU -#else -#include /* for mmap */ -#endif /* linux */ -#endif /* LACKS_SYS_MMAN_H */ -#ifndef LACKS_FCNTL_H -#include -#endif /* LACKS_FCNTL_H */ -#endif /* HAVE_MMAP */ - -#ifndef LACKS_UNISTD_H -#include /* for sbrk, sysconf */ -#else /* LACKS_UNISTD_H */ -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) -extern void* sbrk(ptrdiff_t); -#endif /* FreeBSD etc */ -#endif /* LACKS_UNISTD_H */ - -/* Declarations for locking */ -#if USE_LOCKS -#if defined(_XBOX) || defined(X360) -#pragma intrinsic (_InterlockedCompareExchange) -#pragma intrinsic (_InterlockedExchange) -#define interlockedcompareexchange _InterlockedCompareExchange -#define interlockedexchange _InterlockedExchange -#elif !defined(DL_PLATFORM_WIN32) -#include -#if defined (__SVR4) && defined (__sun) /* solaris */ -#include -#endif /* solaris */ -#else -#ifndef _M_AMD64 -/* These are already defined on AMD64 builds */ -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); - LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* _M_AMD64 */ -#pragma intrinsic (_InterlockedCompareExchange) -#pragma intrinsic (_InterlockedExchange) -#define interlockedcompareexchange _InterlockedCompareExchange -#define interlockedexchange _InterlockedExchange -#endif /* Win32 */ -#endif /* USE_LOCKS */ - -/* Declarations for bit scanning on win32 */ -#if defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) -#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - unsigned char _BitScanForward(unsigned long *index, unsigned long mask); - unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#define BitScanForward _BitScanForward -#define BitScanReverse _BitScanReverse -#pragma intrinsic(_BitScanForward) -#pragma intrinsic(_BitScanReverse) -#endif /* BitScanForward */ -#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ - -#ifndef DL_PLATFORM_WIN32 -#ifndef malloc_getpagesize -# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ -# ifndef _SC_PAGE_SIZE -# define _SC_PAGE_SIZE _SC_PAGESIZE -# endif -# endif -# ifdef _SC_PAGE_SIZE -# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) -# else -# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) -extern size_t getpagesize(); -# define malloc_getpagesize getpagesize() -# else -# ifdef DL_PLATFORM_WIN32 /* use supplied emulation of getpagesize */ -# define malloc_getpagesize getpagesize() -# else -# ifndef LACKS_SYS_PARAM_H -# include -# endif -# ifdef EXEC_PAGESIZE -# define malloc_getpagesize EXEC_PAGESIZE -# else -# ifdef NBPG -# ifndef CLSIZE -# define malloc_getpagesize NBPG -# else -# define malloc_getpagesize (NBPG * CLSIZE) -# endif -# else -# ifdef NBPC -# define malloc_getpagesize NBPC -# else -# ifdef PAGESIZE -# define malloc_getpagesize PAGESIZE -# else /* just guess */ -# define malloc_getpagesize ((size_t)4096U) -# endif -# endif -# endif -# endif -# endif -# endif -# endif -#endif -#endif - - - -/* ------------------- size_t and alignment properties -------------------- */ - -/* The byte and bit size of a size_t */ -#define SIZE_T_SIZE (sizeof(size_t)) -#define SIZE_T_BITSIZE (sizeof(size_t) << 3) - -/* Some constants coerced to size_t */ -/* Annoying but necessary to avoid errors on some platforms */ -#define SIZE_T_ZERO ((size_t)0) -#define SIZE_T_ONE ((size_t)1) -#define SIZE_T_TWO ((size_t)2) -#define SIZE_T_FOUR ((size_t)4) -#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) -#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) -#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) -#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) - -/* The bit mask value corresponding to MALLOC_ALIGNMENT */ -#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) - -/* True if address a has acceptable alignment */ -#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) - -/* the number of bytes to offset an address to align it */ -#define align_offset(A)\ - ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ - ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) - -/* -------------------------- MMAP preliminaries ------------------------- */ - -/* -If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and -checks to fail so compiler optimizer can delete code rather than -using so many "#if"s. -*/ - - -/* MORECORE and MMAP must return MFAIL on failure */ -#define MFAIL ((void*)(MAX_SIZE_T)) -#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ - -#if HAVE_MMAP - -#if defined(_XBOX) || defined(X360) - /* Win32 MMAP via VirtualAlloc */ - static void* win32mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; - } - - /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ - static void* win32direct_mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, - PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; - } - - /* This function supports releasing coalesed segments */ - static int win32munmap(void* ptr, size_t size) { - MEMORY_BASIC_INFORMATION minfo; - char* cptr = (char*)ptr; - while (size) { - if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) - return -1; - if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || - minfo.State != MEM_COMMIT || minfo.RegionSize > size) - return -1; - if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) - return -1; - cptr += minfo.RegionSize; - size -= minfo.RegionSize; - } - return 0; - } - - #define RAK_MMAP_DEFAULT(s) win32mmap(s) - #define RAK_MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) - #define RAK_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) -#elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) - -inline int ___freeit_dlmalloc_default__(void* s) {free(s); return 0;} -#define RAK_MMAP_DEFAULT(s) malloc(s); -#define RAK_MUNMAP_DEFAULT(a, s) ___freeit_dlmalloc_default__(a); -#define RAK_DIRECT_MMAP_DEFAULT(s) malloc(s); - -#elif !defined(DL_PLATFORM_WIN32) - #define RAK_MUNMAP_DEFAULT(a, s) munmap((a), (s)) - #define MMAP_PROT (PROT_READ|PROT_WRITE) - #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) - #define MAP_ANONYMOUS MAP_ANON - #endif /* MAP_ANON */ - #ifdef MAP_ANONYMOUS - #define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) - #define RAK_MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) - #else /* MAP_ANONYMOUS */ - /* - Nearly all versions of mmap support MAP_ANONYMOUS, so the following - is unlikely to be needed, but is supplied just in case. - */ - #define MMAP_FLAGS (MAP_PRIVATE) - static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ - #define RAK_MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ - (dev_zero_fd = open("/dev/zero", O_RDWR), \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) - #endif /* MAP_ANONYMOUS */ - - #define RAK_DIRECT_MMAP_DEFAULT(s) RAK_MMAP_DEFAULT(s) - -#else /* DL_PLATFORM_WIN32 */ - - /* Win32 MMAP via VirtualAlloc */ - static FORCEINLINE void* win32mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; - } - - /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ - static FORCEINLINE void* win32direct_mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, - PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; - } - - /* This function supports releasing coalesed segments */ - static FORCEINLINE int win32munmap(void* ptr, size_t size) { - MEMORY_BASIC_INFORMATION minfo; - char* cptr = (char*)ptr; - while (size) { - if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) - return -1; - if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || - minfo.State != MEM_COMMIT || minfo.RegionSize > size) - return -1; - if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) - return -1; - cptr += minfo.RegionSize; - size -= minfo.RegionSize; - } - return 0; - } - - #define RAK_MMAP_DEFAULT(s) win32mmap(s) - #define RAK_MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) - #define RAK_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) -#endif /* DL_PLATFORM_WIN32 */ -#endif /* HAVE_MMAP */ - -#if HAVE_MREMAP -#ifndef DL_PLATFORM_WIN32 -#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) -#endif /* DL_PLATFORM_WIN32 */ -#endif /* HAVE_MREMAP */ - - -/** -* Define CALL_MORECORE -*/ -#if HAVE_MORECORE -#ifdef MORECORE -#define CALL_MORECORE(S) MORECORE(S) -#else /* MORECORE */ -#define CALL_MORECORE(S) MORECORE_DEFAULT(S) -#endif /* MORECORE */ -#else /* HAVE_MORECORE */ -#define CALL_MORECORE(S) MFAIL -#endif /* HAVE_MORECORE */ - -/** -* Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP -*/ -#if HAVE_MMAP -#define USE_MMAP_BIT (SIZE_T_ONE) - -#ifdef MMAP -#define CALL_MMAP(s) MMAP(s) -#else /* MMAP */ -#define CALL_MMAP(s) RAK_MMAP_DEFAULT(s) -#endif /* MMAP */ -#ifdef MUNMAP -#define CALL_MUNMAP(a, s) MUNMAP((a), (s)) -#else /* MUNMAP */ -#define CALL_MUNMAP(a, s) RAK_MUNMAP_DEFAULT((a), (s)) -#endif /* MUNMAP */ -#ifdef DIRECT_MMAP -#define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) -#else /* DIRECT_MMAP */ -#define CALL_DIRECT_MMAP(s) RAK_DIRECT_MMAP_DEFAULT(s) -#endif /* DIRECT_MMAP */ -#else /* HAVE_MMAP */ -#define USE_MMAP_BIT (SIZE_T_ZERO) - -#define MMAP(s) MFAIL -#define MUNMAP(a, s) (-1) -#define DIRECT_MMAP(s) MFAIL -#define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) -#define CALL_MMAP(s) MMAP(s) -#define CALL_MUNMAP(a, s) MUNMAP((a), (s)) -#endif /* HAVE_MMAP */ - -/** -* Define CALL_MREMAP -*/ -#if HAVE_MMAP && HAVE_MREMAP -#ifdef MREMAP -#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) -#else /* MREMAP */ -#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) -#endif /* MREMAP */ -#else /* HAVE_MMAP && HAVE_MREMAP */ -#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL -#endif /* HAVE_MMAP && HAVE_MREMAP */ - -/* mstate bit set if continguous morecore disabled or failed */ -#define USE_NONCONTIGUOUS_BIT (4U) - -/* segment bit set in rak_create_mspace_with_base */ -#define EXTERN_BIT (8U) - - -#endif /* MALLOC_280_H */ - +#ifdef _RAKNET_SUPPORT_DL_MALLOC + +/* +Default header file for malloc-2.8.x, written by Doug Lea +and released to the public domain, as explained at +http://creativecommons.org/licenses/publicdomain. + +last update: Wed May 27 14:25:17 2009 Doug Lea (dl at gee) + +This header is for ANSI C/C++ only. You can set any of +the following #defines before including: + +* If USE_DL_PREFIX is defined, it is assumed that malloc.c +was also compiled with this option, so all routines +have names starting with "dl". + +* If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this +file will be #included AFTER . This is needed only if +your system defines a struct mallinfo that is incompatible with the +standard one declared here. Otherwise, you can include this file +INSTEAD of your system system . At least on ANSI, all +declarations should be compatible with system versions + +* If MSPACES is defined, declarations for mspace versions are included. +*/ + +#ifndef MALLOC_280_H +#define MALLOC_280_H + +#include "rdlmalloc-options.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* for size_t */ + +#ifndef ONLY_MSPACES +#define ONLY_MSPACES 0 /* define to a value */ +#endif /* ONLY_MSPACES */ +#ifndef NO_MALLINFO +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ + + +#if !ONLY_MSPACES + +#ifndef USE_DL_PREFIX +#define rdlcalloc calloc +#define rdlfree free +#define rdlmalloc malloc +#define rdlmemalign memalign +#define rdlrealloc realloc +#define rdlvalloc valloc +#define rdlpvalloc pvalloc +#define rdlmallinfo mallinfo +#define rdlmallopt mallopt +#define rdlmalloc_trim malloc_trim +#define rdlmalloc_stats malloc_stats +#define rdlmalloc_usable_size malloc_usable_size +#define rdlmalloc_footprint malloc_footprint +#define rdlindependent_calloc independent_calloc +#define rdlindependent_comalloc independent_comalloc +#endif /* USE_DL_PREFIX */ +#if !NO_MALLINFO +#ifndef HAVE_USR_INCLUDE_MALLOC_H +#ifndef _MALLOC_H +#ifndef MALLINFO_FIELD_TYPE +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ +#ifndef STRUCT_MALLINFO_DECLARED +#define STRUCT_MALLINFO_DECLARED 1 + struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ + }; +#endif /* STRUCT_MALLINFO_DECLARED */ +#endif /* _MALLOC_H */ +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* !NO_MALLINFO */ + + /* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or + null if no space is available, in which case errno is set to ENOMEM + on ANSI C systems. + + If n is zero, malloc returns a minimum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 32 bytes on 64bit + systems.) Note that size_t is an unsigned type, so calls with + arguments that would be negative if signed are interpreted as + requests for huge amounts of space, which will often fail. The + maximum supported value of n differs across systems, but is in all + cases less than the maximum representable value of a size_t. + */ + void* rdlmalloc(size_t); + + /* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. If p was not malloced or already + freed, free(p) will by default cuase the current program to abort. + */ + void rdlfree(void*); + + /* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. + */ + void* rdlcalloc(size_t, size_t); + + /* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p in most cases when possible, otherwise it + employs the equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. realloc with a size + argument of zero (re)allocates a minimum-sized chunk. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. + */ + + void* rdlrealloc(void*, size_t); + + /* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + */ + void* rdlmemalign(size_t, size_t); + + /* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. + */ + void* rdlvalloc(size_t); + + /* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. None of these are use in this malloc, + so setting them has no effect. But this malloc also supports other + options in mallopt: + + Symbol param # default allowed param values + M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) + M_GRANULARITY -2 page size any power of 2 >= page size + M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) + */ + int rdlmallopt(int, int); + +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) + + + /* + malloc_footprint(); + Returns the number of bytes obtained from the system. The total + number of bytes allocated by malloc, realloc etc., is less than this + value. Unlike mallinfo, this function returns only a precomputed + result, so can be called frequently to monitor memory consumption. + Even if locks are otherwise defined, this function does not use them, + so results might not be up to date. + */ + size_t rdlmalloc_footprint(); + +#if !NO_MALLINFO + /* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: always zero. + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: the maximum total allocated space. This will be greater + than current total if trimming has occurred. + fsmblks: always zero + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. + */ + + struct mallinfo rdlmallinfo(void); +#endif /* NO_MALLINFO */ + + /* + independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); + + independent_calloc is similar to calloc, but instead of returning a + single cleared space, it returns an array of pointers to n_elements + independent elements that can hold contents of size elem_size, each + of which starts out cleared, and can be independently freed, + realloc'ed etc. The elements are guaranteed to be adjacently + allocated (this is not guaranteed to occur with multiple callocs or + mallocs), which may also improve cache locality in some + applications. + + The "chunks" argument is optional (i.e., may be null, which is + probably the most typical usage). If it is null, the returned array + is itself dynamically allocated and should also be freed when it is + no longer needed. Otherwise, the chunks array must be of at least + n_elements in length. It is filled in with the pointers to the + chunks. + + In either case, independent_calloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and "chunks" + is null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use regular calloc and assign pointers into this + space to represent elements. (In this case though, you cannot + independently free elements.) + + independent_calloc simplifies and speeds up implementations of many + kinds of pools. It may also be useful when constructing large data + structures that initially have a fixed number of fixed-sized nodes, + but the number is not known at compile time, and some of the nodes + may later need to be freed. For example: + + struct Node { int item; struct Node* next; }; + + struct Node* build_list() { + struct Node** pool; + int n = read_number_of_nodes_needed(); + if (n <= 0) return 0; + pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); + if (pool == 0) die(); + // organize into a linked list... + struct Node* first = pool[0]; + for (i = 0; i < n-1; ++i) + pool[i]->next = pool[i+1]; + free(pool); // Can now free the array (or not, if it is needed later) + return first; + } + */ + void** rdlindependent_calloc(size_t, size_t, void**); + + /* + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + + independent_comalloc allocates, all at once, a set of n_elements + chunks with sizes indicated in the "sizes" array. It returns + an array of pointers to these elements, each of which can be + independently freed, realloc'ed etc. The elements are guaranteed to + be adjacently allocated (this is not guaranteed to occur with + multiple callocs or mallocs), which may also improve cache locality + in some applications. + + The "chunks" argument is optional (i.e., may be null). If it is null + the returned array is itself dynamically allocated and should also + be freed when it is no longer needed. Otherwise, the chunks array + must be of at least n_elements in length. It is filled in with the + pointers to the chunks. + + In either case, independent_comalloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and chunks is + null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use a single regular malloc, and assign pointers at + particular offsets in the aggregate space. (In this case though, you + cannot independently free elements.) + + independent_comallac differs from independent_calloc in that each + element may have a different size, and also that it does not + automatically clear elements. + + independent_comalloc can be used to speed up allocation in cases + where several structs or objects must always be allocated at the + same time. For example: + + struct Head { ... } + struct Foot { ... } + + void send_message(char* msg) { + int msglen = strlen(msg); + size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; + void* chunks[3]; + if (independent_comalloc(3, sizes, chunks) == 0) + die(); + struct Head* head = (struct Head*)(chunks[0]); + char* body = (char*)(chunks[1]); + struct Foot* foot = (struct Foot*)(chunks[2]); + // ... + } + + In general though, independent_comalloc is worth using only for + larger values of n_elements. For small values, you probably won't + detect enough difference from series of malloc calls to bother. + + Overuse of independent_comalloc can increase overall memory usage, + since it cannot reuse existing noncontiguous small chunks that + might be available for some of the elements. + */ + void** rdlindependent_comalloc(size_t, size_t*, void**); + + + /* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ + void* rdlpvalloc(size_t); + + /* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative arguments + to sbrk) if there is unused memory at the `high' end of the malloc + pool or in unused MMAP segments. You can call this after freeing + large blocks of memory to potentially reduce the system-level memory + requirements of a program. However, it cannot guarantee to reduce + memory. Under some allocation patterns, some large free blocks of + memory will be locked between two used chunks, so they cannot be + given back to the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, only + the minimum amount of memory to maintain internal data structures + will be left. Non-zero arguments can be supplied to maintain enough + trailing space to service future expected allocations without having + to re-obtain memory from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + */ + int rdlmalloc_trim(size_t); + + /* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. + */ + void rdlmalloc_stats(); + +#endif /* !ONLY_MSPACES */ + + /* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); + */ + size_t rdlmalloc_usable_size(void*); + + +#if MSPACES + + /* + mspace is an opaque type representing an independent + region of space that supports rak_mspace_malloc, etc. + */ + typedef void* mspace; + + /* + rak_create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service rak_mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). + */ + mspace rak_create_mspace(size_t capacity, int locked); + + /* + rak_destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. + */ + size_t rak_destroy_mspace(mspace msp); + + /* + rak_create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. + */ + mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked); + + /* + rak_mspace_track_large_chunks controls whether requests for large chunks + are allocated in their own untracked mmapped regions, separate from + others in this mspace. By default large chunks are not tracked, + which reduces fragmentation. However, such chunks are not + necessarily released to the system upon rak_destroy_mspace. Enabling + tracking by setting to true may increase fragmentation, but avoids + leakage when relying on rak_destroy_mspace to release all memory + allocated using this space. The function returns the previous + setting. + */ + int rak_mspace_track_large_chunks(mspace msp, int enable); + + /* + rak_mspace_malloc behaves as malloc, but operates within + the given space. + */ + void* rak_mspace_malloc(mspace msp, size_t bytes); + + /* + rak_mspace_free behaves as free, but operates within + the given space. + + If compiled with FOOTERS==1, rak_mspace_free is not actually needed. + free may be called instead of rak_mspace_free because freed chunks from + any space are handled by their originating spaces. + */ + void rak_mspace_free(mspace msp, void* mem); + + /* + rak_mspace_realloc behaves as realloc, but operates within + the given space. + + If compiled with FOOTERS==1, rak_mspace_realloc is not actually + needed. realloc may be called instead of rak_mspace_realloc because + realloced chunks from any space are handled by their originating + spaces. + */ + void* rak_mspace_realloc(mspace msp, void* mem, size_t newsize); + + /* + rak_mspace_calloc behaves as calloc, but operates within + the given space. + */ + void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + + /* + rak_mspace_memalign behaves as memalign, but operates within + the given space. + */ + void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes); + + /* + rak_mspace_independent_calloc behaves as independent_calloc, but + operates within the given space. + */ + void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); + + /* + rak_mspace_independent_comalloc behaves as independent_comalloc, but + operates within the given space. + */ + void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); + + /* + rak_mspace_footprint() returns the number of bytes obtained from the + system for this space. + */ + size_t rak_mspace_footprint(mspace msp); + + +#if !NO_MALLINFO + /* + rak_mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. + */ + struct mallinfo rak_mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + + /* + malloc_usable_size(void* p) behaves the same as malloc_usable_size; + */ + size_t rak_mspace_usable_size(void* mem); + + /* + rak_mspace_malloc_stats behaves as malloc_stats, but reports + properties of the given space. + */ + void rak_mspace_malloc_stats(mspace msp); + + /* + rak_mspace_trim behaves as malloc_trim, but + operates within the given space. + */ + int rak_mspace_trim(mspace msp, size_t pad); + + /* + An alias for mallopt. + */ + int rak_mspace_mallopt(int, int); + +#endif /* MSPACES */ + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +/* +This is a version (aka rdlmalloc) of malloc/free/realloc written by +Doug Lea and released to the public domain, as explained at +http://creativecommons.org/licenses/publicdomain. Send questions, +comments, complaints, performance data, etc to dl@cs.oswego.edu + +* Version 2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) + +Note: There may be an updated version of this malloc obtainable at +ftp://gee.cs.oswego.edu/pub/misc/malloc.c +Check before installing! + +* Quickstart + +This library is all in one file to simplify the most common usage: +ftp it, compile it (-O3), and link it into another program. All of +the compile-time options default to reasonable values for use on +most platforms. You might later want to step through various +compile-time and dynamic tuning options. + +For convenience, an include file for code using this malloc is at: +ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.4.h +You don't really need this .h file unless you call functions not +defined in your system include files. The .h file contains only the +excerpts from this file needed for using this malloc on ANSI C/C++ +systems, so long as you haven't changed compile-time options about +naming and tuning parameters. If you do, then you can create your +own malloc.h that does include all settings by cutting at the point +indicated below. Note that you may already by default be using a C +library containing a malloc that is based on some version of this +malloc (for example in linux). You might still want to use the one +in this file to customize settings or to avoid overheads associated +with library versions. + +* Vital statistics: + +Supported pointer/size_t representation: 4 or 8 bytes +size_t MUST be an unsigned type of the same width as +pointers. (If you are using an ancient system that declares +size_t as a signed type, or need it to be a different width +than pointers, you can use a previous release of this malloc +(e.g. 2.7.2) supporting these.) + +Alignment: 8 bytes (default) +This suffices for nearly all current machines and C compilers. +However, you can define MALLOC_ALIGNMENT to be wider than this +if necessary (up to 128bytes), at the expense of using more space. + +Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) +8 or 16 bytes (if 8byte sizes) +Each malloced chunk has a hidden word of overhead holding size +and status information, and additional cross-check word +if FOOTERS is defined. + +Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) +8-byte ptrs: 32 bytes (including overhead) + +Even a request for zero bytes (i.e., malloc(0)) returns a +pointer to something of the minimum allocatable size. +The maximum overhead wastage (i.e., number of extra bytes +allocated than were requested in malloc) is less than or equal +to the minimum size, except for requests >= mmap_threshold that +are serviced via mmap(), where the worst case wastage is about +32 bytes plus the remainder from a system page (the minimal +mmap unit); typically 4096 or 8192 bytes. + +Security: static-safe; optionally more or less +The "security" of malloc refers to the ability of malicious +code to accentuate the effects of errors (for example, freeing +space that is not currently malloc'ed or overwriting past the +ends of chunks) in code that calls malloc. This malloc +guarantees not to modify any memory locations below the base of +heap, i.e., static variables, even in the presence of usage +errors. The routines additionally detect most improper frees +and reallocs. All this holds as long as the static bookkeeping +for malloc itself is not corrupted by some other means. This +is only one aspect of security -- these checks do not, and +cannot, detect all possible programming errors. + +If FOOTERS is defined nonzero, then each allocated chunk +carries an additional check word to verify that it was malloced +from its space. These check words are the same within each +execution of a program using malloc, but differ across +executions, so externally crafted fake chunks cannot be +freed. This improves security by rejecting frees/reallocs that +could corrupt heap memory, in addition to the checks preventing +writes to statics that are always on. This may further improve +security at the expense of time and space overhead. (Note that +FOOTERS may also be worth using with MSPACES.) + +By default detected errors cause the program to abort (calling +"abort()"). You can override this to instead proceed past +errors by defining PROCEED_ON_ERROR. In this case, a bad free +has no effect, and a malloc that encounters a bad address +caused by user overwrites will ignore the bad address by +dropping pointers and indices to all known memory. This may +be appropriate for programs that should continue if at all +possible in the face of programming errors, although they may +run out of memory because dropped memory is never reclaimed. + +If you don't like either of these options, you can define +CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything +else. And if if you are sure that your program using malloc has +no errors or vulnerabilities, you can define INSECURE to 1, +which might (or might not) provide a small performance improvement. + +Thread-safety: NOT thread-safe unless USE_LOCKS defined +When USE_LOCKS is defined, each public call to malloc, free, +etc is surrounded with either a pthread mutex or a win32 +spinlock (depending on DL_PLATFORM_WIN32). This is not especially fast, and +can be a major bottleneck. It is designed only to provide +minimal protection in concurrent environments, and to provide a +basis for extensions. If you are using malloc in a concurrent +program, consider instead using nedmalloc +(http://www.nedprod.com/programs/portable/nedmalloc/) or +ptmalloc (See http://www.malloc.de), which are derived +from versions of this malloc. + +System requirements: Any combination of MORECORE and/or MMAP/MUNMAP +This malloc can use unix sbrk or any emulation (invoked using +the CALL_MORECORE macro) and/or mmap/munmap or any emulation +(invoked using CALL_MMAP/CALL_MUNMAP) to get and release system +memory. On most unix systems, it tends to work best if both +MORECORE and MMAP are enabled. On Win32, it uses emulations +based on VirtualAlloc. It also uses common C library functions +like memset. + +Compliance: I believe it is compliant with the Single Unix Specification +(See http://www.unix.org). Also SVID/XPG, ANSI C, and probably +others as well. + +* Overview of algorithms + +This is not the fastest, most space-conserving, most portable, or +most tunable malloc ever written. However it is among the fastest +while also being among the most space-conserving, portable and +tunable. Consistent balance across these factors results in a good +general-purpose allocator for malloc-intensive programs. + +In most ways, this malloc is a best-fit allocator. Generally, it +chooses the best-fitting existing chunk for a request, with ties +broken in approximately least-recently-used order. (This strategy +normally maintains low fragmentation.) However, for requests less +than 256bytes, it deviates from best-fit when there is not an +exactly fitting available chunk by preferring to use space adjacent +to that used for the previous small request, as well as by breaking +ties in approximately most-recently-used order. (These enhance +locality of series of small allocations.) And for very large requests +(>= 256Kb by default), it relies on system memory mapping +facilities, if supported. (This helps avoid carrying around and +possibly fragmenting memory used only for large chunks.) + +All operations (except malloc_stats and mallinfo) have execution +times that are bounded by a constant factor of the number of bits in +a size_t, not counting any clearing in calloc or copying in realloc, +or actions surrounding MORECORE and MMAP that have times +proportional to the number of non-contiguous regions returned by +system allocation routines, which is often just 1. In real-time +applications, you can optionally suppress segment traversals using +NO_SEGMENT_TRAVERSAL, which assures bounded execution even when +system allocators return non-contiguous spaces, at the typical +expense of carrying around more memory and increased fragmentation. + +The implementation is not very modular and seriously overuses +macros. Perhaps someday all C compilers will do as good a job +inlining modular code as can now be done by brute-force expansion, +but now, enough of them seem not to. + +Some compilers issue a lot of warnings about code that is +dead/unreachable only on some platforms, and also about intentional +uses of negation on unsigned types. All known cases of each can be +ignored. + +For a longer but out of date high-level description, see +http://gee.cs.oswego.edu/dl/html/malloc.html + +* MSPACES +If MSPACES is defined, then in addition to malloc, free, etc., +this file also defines rak_mspace_malloc, rak_mspace_free, etc. These +are versions of malloc routines that take an "mspace" argument +obtained using rak_create_mspace, to control all internal bookkeeping. +If ONLY_MSPACES is defined, only these versions are compiled. +So if you would like to use this allocator for only some allocations, +and your system malloc for others, you can compile with +ONLY_MSPACES and then do something like... +static mspace mymspace = rak_create_mspace(0,0); // for example +#define mymalloc(bytes) rak_mspace_malloc(mymspace, bytes) + +(Note: If you only need one instance of an mspace, you can instead +use "USE_DL_PREFIX" to relabel the global malloc.) + +You can similarly create thread-local allocators by storing +mspaces as thread-locals. For example: +static __thread mspace tlms = 0; +void* tlmalloc(size_t bytes) { +if (tlms == 0) tlms = rak_create_mspace(0, 0); +return rak_mspace_malloc(tlms, bytes); +} +void tlfree(void* mem) { rak_mspace_free(tlms, mem); } + +Unless FOOTERS is defined, each mspace is completely independent. +You cannot allocate from one and free to another (although +conformance is only weakly checked, so usage errors are not always +caught). If FOOTERS is defined, then each chunk carries around a tag +indicating its originating mspace, and frees are directed to their +originating spaces. + +------------------------- Compile-time options --------------------------- + +Be careful in setting #define values for numerical constants of type +size_t. On some systems, literal values are not automatically extended +to size_t precision unless they are explicitly casted. You can also +use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. + +DL_PLATFORM_WIN32 default: defined if _WIN32 defined +Defining DL_PLATFORM_WIN32 sets up defaults for MS environment and compilers. +Otherwise defaults are for unix. Beware that there seem to be some +cases where this malloc might not be a pure drop-in replacement for +Win32 malloc: Random-looking failures from Win32 GDI API's (eg; +SetDIBits()) may be due to bugs in some video driver implementations +when pixel buffers are malloc()ed, and the region spans more than +one VirtualAlloc()ed region. Because rdlmalloc uses a small (64Kb) +default granularity, pixel buffers may straddle virtual allocation +regions more often than when using the Microsoft allocator. You can +avoid this by using VirtualAlloc() and VirtualFree() for all pixel +buffers rather than using malloc(). If this is not possible, +recompile this malloc with a larger DEFAULT_GRANULARITY. + +MALLOC_ALIGNMENT default: (size_t)8 +Controls the minimum alignment for malloc'ed chunks. It must be a +power of two and at least 8, even on machines for which smaller +alignments would suffice. It may be defined as larger than this +though. Note however that code and data structures are optimized for +the case of 8-byte alignment. + +MSPACES default: 0 (false) +If true, compile in support for independent allocation spaces. +This is only supported if HAVE_MMAP is true. + +ONLY_MSPACES default: 0 (false) +If true, only compile in mspace versions, not regular versions. + +USE_LOCKS default: 0 (false) +Causes each call to each public routine to be surrounded with +pthread or DL_PLATFORM_WIN32 mutex lock/unlock. (If set true, this can be +overridden on a per-mspace basis for mspace versions.) If set to a +non-zero value other than 1, locks are used, but their +implementation is left out, so lock functions must be supplied manually, +as described below. + +USE_SPIN_LOCKS default: 1 iff USE_LOCKS and on x86 using gcc or MSC +If true, uses custom spin locks for locking. This is currently +supported only for x86 platforms using gcc or recent MS compilers. +Otherwise, posix locks or win32 critical sections are used. + +FOOTERS default: 0 +If true, provide extra checking and dispatching by placing +information in the footers of allocated chunks. This adds +space and time overhead. + +INSECURE default: 0 +If true, omit checks for usage errors and heap space overwrites. + +USE_DL_PREFIX default: NOT defined +Causes compiler to prefix all public routines with the string 'dl'. +This can be useful when you only want to use this malloc in one part +of a program, using your regular system malloc elsewhere. + +ABORT default: defined as abort() +Defines how to abort on failed checks. On most systems, a failed +check cannot die with an "assert" or even print an informative +message, because the underlying print routines in turn call malloc, +which will fail again. Generally, the best policy is to simply call +abort(). It's not very useful to do more than this because many +errors due to overwriting will show up as address faults (null, odd +addresses etc) rather than malloc-triggered checks, so will also +abort. Also, most compilers know that abort() does not return, so +can better optimize code conditionally calling it. + +PROCEED_ON_ERROR default: defined as 0 (false) +Controls whether detected bad addresses cause them to bypassed +rather than aborting. If set, detected bad arguments to free and +realloc are ignored. And all bookkeeping information is zeroed out +upon a detected overwrite of freed heap space, thus losing the +ability to ever return it from malloc again, but enabling the +application to proceed. If PROCEED_ON_ERROR is defined, the +static variable malloc_corruption_error_count is compiled in +and can be examined to see if errors have occurred. This option +generates slower code than the default abort policy. + +DEBUG default: NOT defined +The DEBUG setting is mainly intended for people trying to modify +this code or diagnose problems when porting to new platforms. +However, it may also be able to better isolate user errors than just +using runtime checks. The assertions in the check routines spell +out in more detail the assumptions and invariants underlying the +algorithms. The checking is fairly extensive, and will slow down +execution noticeably. Calling malloc_stats or mallinfo with DEBUG +set will attempt to check every non-mmapped allocated and free chunk +in the course of computing the summaries. + +ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) +Debugging assertion failures can be nearly impossible if your +version of the assert macro causes malloc to be called, which will +lead to a cascade of further failures, blowing the runtime stack. +ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), +which will usually make debugging easier. + +MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 +The action to take before "return 0" when malloc fails to be able to +return memory because there is none available. + +HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES +True if this system supports sbrk or an emulation of it. + +MORECORE default: sbrk +The name of the sbrk-style system routine to call to obtain more +memory. See below for guidance on writing custom MORECORE +functions. The type of the argument to sbrk/MORECORE varies across +systems. It cannot be size_t, because it supports negative +arguments, so it is normally the signed type of the same width as +size_t (sometimes declared as "intptr_t"). It doesn't much matter +though. Internally, we only call it with arguments less than half +the max value of a size_t, which should work across all reasonable +possibilities, although sometimes generating compiler warnings. + +MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE +If true, take advantage of fact that consecutive calls to MORECORE +with positive arguments always return contiguous increasing +addresses. This is true of unix sbrk. It does not hurt too much to +set it true anyway, since malloc copes with non-contiguities. +Setting it false when definitely non-contiguous saves time +and possibly wasted space it would take to discover this though. + +MORECORE_CANNOT_TRIM default: NOT defined +True if MORECORE cannot release space back to the system when given +negative arguments. This is generally necessary only if you are +using a hand-crafted MORECORE function that cannot handle negative +arguments. + +NO_SEGMENT_TRAVERSAL default: 0 +If non-zero, suppresses traversals of memory segments +returned by either MORECORE or CALL_MMAP. This disables +merging of segments that are contiguous, and selectively +releasing them to the OS if unused, but bounds execution times. + +HAVE_MMAP default: 1 (true) +True if this system supports mmap or an emulation of it. If so, and +HAVE_MORECORE is not true, MMAP is used for all system +allocation. If set and HAVE_MORECORE is true as well, MMAP is +primarily used to directly allocate very large blocks. It is also +used as a backup strategy in cases where MORECORE fails to provide +space from system. Note: A single call to MUNMAP is assumed to be +able to unmap memory that may have be allocated using multiple calls +to MMAP, so long as they are adjacent. + +HAVE_MREMAP default: 1 on linux, else 0 +If true realloc() uses mremap() to re-allocate large blocks and +extend or shrink allocation spaces. + +MMAP_CLEARS default: 1 except on WINCE. +True if mmap clears memory so calloc doesn't need to. This is true +for standard unix mmap using /dev/zero and on DL_PLATFORM_WIN32 except for WINCE. + +USE_BUILTIN_FFS default: 0 (i.e., not used) +Causes malloc to use the builtin ffs() function to compute indices. +Some compilers may recognize and intrinsify ffs to be faster than the +supplied C version. Also, the case of x86 using gcc is special-cased +to an asm instruction, so is already as fast as it can be, and so +this setting has no effect. Similarly for Win32 under recent MS compilers. +(On most x86s, the asm version is only slightly faster than the C version.) + +malloc_getpagesize default: derive from system includes, or 4096. +The system page size. To the extent possible, this malloc manages +memory from the system in page-size units. This may be (and +usually is) a function rather than a constant. This is ignored +if DL_PLATFORM_WIN32, where page size is determined using getSystemInfo during +initialization. + +USE_DEV_RANDOM default: 0 (i.e., not used) +Causes malloc to use /dev/random to initialize secure magic seed for +stamping footers. Otherwise, the current time is used. + +NO_MALLINFO default: 0 +If defined, don't compile "mallinfo". This can be a simple way +of dealing with mismatches between system declarations and +those in this file. + +MALLINFO_FIELD_TYPE default: size_t +The type of the fields in the mallinfo struct. This was originally +defined as "int" in SVID etc, but is more usefully defined as +size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set + +REALLOC_ZERO_BYTES_FREES default: not defined +This should be set if a call to realloc with zero bytes should +be the same as a call to free. Some people think it should. Otherwise, +since this malloc returns a unique pointer for malloc(0), so does +realloc(p, 0). + +LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H +LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H +LACKS_STDLIB_H default: NOT defined unless on DL_PLATFORM_WIN32 +Define these if your system does not have these header files. +You might need to manually insert some of the declarations they provide. + +DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, +system_info.dwAllocationGranularity in DL_PLATFORM_WIN32, +otherwise 64K. +Also settable using mallopt(M_GRANULARITY, x) +The unit for allocating and deallocating memory from the system. On +most systems with contiguous MORECORE, there is no reason to +make this more than a page. However, systems with MMAP tend to +either require or encourage larger granularities. You can increase +this value to prevent system allocation functions to be called so +often, especially if they are slow. The value must be at least one +page and must be a power of two. Setting to 0 causes initialization +to either page size or win32 region size. (Note: In previous +versions of malloc, the equivalent of this option was called +"TOP_PAD") + +DEFAULT_TRIM_THRESHOLD default: 2MB +Also settable using mallopt(M_TRIM_THRESHOLD, x) +The maximum amount of unused top-most memory to keep before +releasing via malloc_trim in free(). Automatic trimming is mainly +useful in long-lived programs using contiguous MORECORE. Because +trimming via sbrk can be slow on some systems, and can sometimes be +wasteful (in cases where programs immediately afterward allocate +more large chunks) the value should be high enough so that your +overall system performance would improve by releasing this much +memory. As a rough guide, you might set to a value close to the +average size of a process (program) running on your system. +Releasing this much memory would allow such a process to run in +memory. Generally, it is worth tuning trim thresholds when a +program undergoes phases where several large chunks are allocated +and released in ways that can reuse each other's storage, perhaps +mixed with phases where there are no such chunks at all. The trim +value must be greater than page size to have any useful effect. To +disable trimming completely, you can set to MAX_SIZE_T. Note that the trick +some people use of mallocing a huge space and then freeing it at +program startup, in an attempt to reserve system memory, doesn't +have the intended effect under automatic trimming, since that memory +will immediately be returned to the system. + +DEFAULT_MMAP_THRESHOLD default: 256K +Also settable using mallopt(M_MMAP_THRESHOLD, x) +The request size threshold for using MMAP to directly service a +request. Requests of at least this size that cannot be allocated +using already-existing space will be serviced via mmap. (If enough +normal freed space already exists it is used instead.) Using mmap +segregates relatively large chunks of memory so that they can be +individually obtained and released from the host system. A request +serviced through mmap is never reused by any other request (at least +not directly; the system may just so happen to remap successive +requests to the same locations). Segregating space in this way has +the benefits that: Mmapped space can always be individually released +back to the system, which helps keep the system level memory demands +of a long-lived program low. Also, mapped memory doesn't become +`locked' between other chunks, as can happen with normally allocated +chunks, which means that even trimming via malloc_trim would not +release them. However, it has the disadvantage that the space +cannot be reclaimed, consolidated, and then used to service later +requests, as happens with normal chunks. The advantages of mmap +nearly always outweigh disadvantages for "large" chunks, but the +value of "large" may vary across systems. The default is an +empirically derived value that works well in most systems. You can +disable mmap by setting to MAX_SIZE_T. + +MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP +The number of consolidated frees between checks to release +unused segments when freeing. When using non-contiguous segments, +especially with multiple mspaces, checking only for topmost space +doesn't always suffice to trigger trimming. To compensate for this, +free() will, with a period of MAX_RELEASE_CHECK_RATE (or the +current number of segments, if greater) try to release unused +segments to the OS when freeing chunks that result in +consolidation. The best value for this parameter is a compromise +between slowing down frees with relatively costly checks that +rarely trigger versus holding on to unused memory. To effectively +disable, set to MAX_SIZE_T. This may lead to a very slight speed +improvement at the expense of carrying around more memory. +*/ + +/* Version identifier to allow people to support multiple versions */ +#ifndef DLMALLOC_VERSION +#define DLMALLOC_VERSION 20804 +#endif /* DLMALLOC_VERSION */ + +#include "rdlmalloc-options.h" + +#ifndef WIN32 +#if defined(_XBOX) || defined(X360) +#else +#if defined(_WIN32) +#define DL_PLATFORM_WIN32 1 +#endif /* _WIN32 */ +#ifdef _WIN32_WCE +#define LACKS_FCNTL_H +#define DL_PLATFORM_WIN32 1 +#endif /* _WIN32_WCE */ +#endif +#else +#define DL_PLATFORM_WIN32 1 +#endif /* DL_PLATFORM_WIN32 */ + +#if defined(_XBOX) || defined(X360) +#define HAVE_MMAP 1 +#define HAVE_MORECORE 0 +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H +#define LACKS_SYS_MMAN_H +#define LACKS_STRING_H +#define LACKS_STRINGS_H +#define LACKS_SYS_TYPES_H +#define LACKS_ERRNO_H +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION +#endif +#define MMAP_CLEARS 1 +#endif + +#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) +#define LACKS_SYS_PARAM_H +#include "sysutil\sysutil_sysparam.h" +#define LACKS_SYS_MMAN_H +#endif + + +#ifdef DL_PLATFORM_WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define HAVE_MMAP 1 +#define HAVE_MORECORE 0 +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H +#define LACKS_SYS_MMAN_H +#define LACKS_STRING_H +#define LACKS_STRINGS_H +#define LACKS_SYS_TYPES_H +#define LACKS_ERRNO_H +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION +#endif /* MALLOC_FAILURE_ACTION */ +#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ +#define MMAP_CLEARS 0 +#else +#define MMAP_CLEARS 1 +#endif /* _WIN32_WCE */ +#endif /* DL_PLATFORM_WIN32 */ + +#if defined(DARWIN) || defined(_DARWIN) +/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ +#ifndef HAVE_MORECORE +#define HAVE_MORECORE 0 +#define HAVE_MMAP 1 +/* OSX allocators provide 16 byte alignment */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)16U) +#endif +#endif /* HAVE_MORECORE */ +#endif /* DARWIN */ + +#ifndef LACKS_SYS_TYPES_H +#include /* For size_t */ +#endif /* LACKS_SYS_TYPES_H */ + +#if (defined(__GNUC__) && ((defined(__i386__) || defined(__x86_64__)))) || (defined(_MSC_VER) && _MSC_VER>=1310) +#define SPIN_LOCKS_AVAILABLE 1 +#else +#define SPIN_LOCKS_AVAILABLE 0 +#endif + +/* The maximum possible size_t value has all bits set */ +#define MAX_SIZE_T (~(size_t)0) + +#ifndef ONLY_MSPACES +#define ONLY_MSPACES 0 /* define to a value */ +#else +#define ONLY_MSPACES 1 +#endif /* ONLY_MSPACES */ +#ifndef MSPACES +#if ONLY_MSPACES +#define MSPACES 1 +#else /* ONLY_MSPACES */ +#define MSPACES 0 +#endif /* ONLY_MSPACES */ +#endif /* MSPACES */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)8U) +#endif /* MALLOC_ALIGNMENT */ +#ifndef FOOTERS +#define FOOTERS 0 +#endif /* FOOTERS */ +#ifndef ABORT +#define ABORT abort() +#endif /* ABORT */ +#ifndef ABORT_ON_ASSERT_FAILURE +#define ABORT_ON_ASSERT_FAILURE 1 +#endif /* ABORT_ON_ASSERT_FAILURE */ +#ifndef PROCEED_ON_ERROR +#define PROCEED_ON_ERROR 0 +#endif /* PROCEED_ON_ERROR */ +#ifndef USE_LOCKS +#define USE_LOCKS 0 +#endif /* USE_LOCKS */ +#ifndef USE_SPIN_LOCKS +#if USE_LOCKS && SPIN_LOCKS_AVAILABLE +#define USE_SPIN_LOCKS 1 +#else +#define USE_SPIN_LOCKS 0 +#endif /* USE_LOCKS && SPIN_LOCKS_AVAILABLE. */ +#endif /* USE_SPIN_LOCKS */ +#ifndef INSECURE +#define INSECURE 0 +#endif /* INSECURE */ +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif /* HAVE_MMAP */ +#ifndef MMAP_CLEARS +#define MMAP_CLEARS 1 +#endif /* MMAP_CLEARS */ +#ifndef HAVE_MREMAP +#ifdef linux +#define HAVE_MREMAP 1 +#else /* linux */ +#define HAVE_MREMAP 0 +#endif /* linux */ +#endif /* HAVE_MREMAP */ +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION errno = ENOMEM; +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef HAVE_MORECORE +#if ONLY_MSPACES +#define HAVE_MORECORE 0 +#else /* ONLY_MSPACES */ +#define HAVE_MORECORE 1 +#endif /* ONLY_MSPACES */ +#endif /* HAVE_MORECORE */ +#if !HAVE_MORECORE +#define MORECORE_CONTIGUOUS 0 +#else /* !HAVE_MORECORE */ +#define MORECORE_DEFAULT sbrk +#ifndef MORECORE_CONTIGUOUS +#define MORECORE_CONTIGUOUS 1 +#endif /* MORECORE_CONTIGUOUS */ +#endif /* HAVE_MORECORE */ +#ifndef DEFAULT_GRANULARITY +#if (MORECORE_CONTIGUOUS || defined(DL_PLATFORM_WIN32)) +#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ +#else /* MORECORE_CONTIGUOUS */ +#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) +#endif /* MORECORE_CONTIGUOUS */ +#endif /* DEFAULT_GRANULARITY */ +#ifndef DEFAULT_TRIM_THRESHOLD +#ifndef MORECORE_CANNOT_TRIM +#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#else /* MORECORE_CANNOT_TRIM */ +#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T +#endif /* MORECORE_CANNOT_TRIM */ +#endif /* DEFAULT_TRIM_THRESHOLD */ +#ifndef DEFAULT_MMAP_THRESHOLD +#if HAVE_MMAP +#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) +#else /* HAVE_MMAP */ +#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* DEFAULT_MMAP_THRESHOLD */ +#ifndef MAX_RELEASE_CHECK_RATE +#if HAVE_MMAP +#define MAX_RELEASE_CHECK_RATE 4095 +#else +#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* MAX_RELEASE_CHECK_RATE */ +#ifndef USE_BUILTIN_FFS +#define USE_BUILTIN_FFS 0 +#endif /* USE_BUILTIN_FFS */ +#ifndef USE_DEV_RANDOM +#define USE_DEV_RANDOM 0 +#endif /* USE_DEV_RANDOM */ +#ifndef NO_MALLINFO +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ +#ifndef MALLINFO_FIELD_TYPE +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ +#ifndef NO_SEGMENT_TRAVERSAL +#define NO_SEGMENT_TRAVERSAL 0 +#endif /* NO_SEGMENT_TRAVERSAL */ + +/* +mallopt tuning options. SVID/XPG defines four standard parameter +numbers for mallopt, normally defined in malloc.h. None of these +are used in this malloc, so setting them has no effect. But this +malloc does support the following options. +*/ + +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) + +/* ------------------------ Mallinfo declarations ------------------------ */ + +#if !NO_MALLINFO +/* +This version of malloc supports the standard SVID/XPG mallinfo +routine that returns a struct containing usage properties and +statistics. It should work on any system that has a +/usr/include/malloc.h defining struct mallinfo. The main +declaration needed is the mallinfo struct that is returned (by-copy) +by mallinfo(). The malloinfo struct contains a bunch of fields that +are not even meaningful in this version of malloc. These fields are +are instead filled by mallinfo() with other numbers that might be of +interest. + +HAVE_USR_INCLUDE_MALLOC_H should be set if you have a +/usr/include/malloc.h file that includes a declaration of struct +mallinfo. If so, it is included; else a compliant version is +declared below. These must be precisely the same for mallinfo() to +work. The original SVID version of this struct, defined on most +systems with mallinfo, declares all fields as ints. But some others +define as unsigned long. If your system defines the fields using a +type of different width than listed here, you MUST #include your +system version and #define HAVE_USR_INCLUDE_MALLOC_H. +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#ifdef HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else /* HAVE_USR_INCLUDE_MALLOC_H */ +#ifndef STRUCT_MALLINFO_DECLARED +#define STRUCT_MALLINFO_DECLARED 1 +struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ +}; +#endif /* STRUCT_MALLINFO_DECLARED */ +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* NO_MALLINFO */ + +/* +Try to persuade compilers to inline. The most critical functions for +inlining are defined as macros, so these aren't used for them. +*/ + +#ifndef FORCEINLINE +#if defined(__GNUC__) +#define FORCEINLINE __inline __attribute__ ((always_inline)) +#elif defined(_MSC_VER) +#define FORCEINLINE __forceinline +#endif +#endif +#ifndef NOINLINE +#if defined(__GNUC__) +#define NOINLINE __attribute__ ((noinline)) +#elif defined(_MSC_VER) +#define NOINLINE __declspec(noinline) +#else +#define NOINLINE +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#ifndef FORCEINLINE +#define FORCEINLINE inline +#endif +#endif /* __cplusplus */ +#ifndef FORCEINLINE +#define FORCEINLINE +#endif + +#if !ONLY_MSPACES + + /* ------------------- Declarations of public routines ------------------- */ + +#ifndef USE_DL_PREFIX +#define rdlcalloc calloc +#define rdlfree free +#define rdlmalloc malloc +#define rdlmemalign memalign +#define rdlrealloc realloc +#define rdlvalloc valloc +#define rdlpvalloc pvalloc +#define rdlmallinfo mallinfo +#define rdlmallopt mallopt +#define rdlmalloc_trim malloc_trim +#define rdlmalloc_stats malloc_stats +#define rdlmalloc_usable_size malloc_usable_size +#define rdlmalloc_footprint malloc_footprint +#define dlmalloc_max_footprint malloc_max_footprint +#define rdlindependent_calloc independent_calloc +#define rdlindependent_comalloc independent_comalloc +#endif /* USE_DL_PREFIX */ + + + /* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or + null if no space is available, in which case errno is set to ENOMEM + on ANSI C systems. + + If n is zero, malloc returns a minimum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 32 bytes on 64bit + systems.) Note that size_t is an unsigned type, so calls with + arguments that would be negative if signed are interpreted as + requests for huge amounts of space, which will often fail. The + maximum supported value of n differs across systems, but is in all + cases less than the maximum representable value of a size_t. + */ + void* rdlmalloc(size_t); + + /* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. If p was not malloced or already + freed, free(p) will by default cause the current program to abort. + */ + void rdlfree(void*); + + /* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. + */ + void* rdlcalloc(size_t, size_t); + + /* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p in most cases when possible, otherwise it + employs the equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. realloc with a size + argument of zero (re)allocates a minimum-sized chunk. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. + */ + + void* rdlrealloc(void*, size_t); + + /* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + */ + void* rdlmemalign(size_t, size_t); + + /* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. + */ + void* rdlvalloc(size_t); + + /* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. To workaround the fact that mallopt is specified to use int, + not size_t parameters, the value -1 is specially treated as the + maximum unsigned size_t value. + + SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. None of these are use in this malloc, + so setting them has no effect. But this malloc also supports other + options in mallopt. See below for details. Briefly, supported + parameters are as follows (listed defaults are for "typical" + configurations). + + Symbol param # default allowed param values + M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) + M_GRANULARITY -2 page size any power of 2 >= page size + M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) + */ + int rdlmallopt(int, int); + + /* + malloc_footprint(); + Returns the number of bytes obtained from the system. The total + number of bytes allocated by malloc, realloc etc., is less than this + value. Unlike mallinfo, this function returns only a precomputed + result, so can be called frequently to monitor memory consumption. + Even if locks are otherwise defined, this function does not use them, + so results might not be up to date. + */ + size_t rdlmalloc_footprint(void); + + /* + malloc_max_footprint(); + Returns the maximum number of bytes obtained from the system. This + value will be greater than current footprint if deallocated space + has been reclaimed by the system. The peak number of bytes allocated + by malloc, realloc etc., is less than this value. Unlike mallinfo, + this function returns only a precomputed result, so can be called + frequently to monitor memory consumption. Even if locks are + otherwise defined, this function does not use them, so results might + not be up to date. + */ + size_t dlmalloc_max_footprint(void); + +#if !NO_MALLINFO + /* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: always zero. + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: the maximum total allocated space. This will be greater + than current total if trimming has occurred. + fsmblks: always zero + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. + */ + struct mallinfo rdlmallinfo(void); +#endif /* NO_MALLINFO */ + + /* + independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); + + independent_calloc is similar to calloc, but instead of returning a + single cleared space, it returns an array of pointers to n_elements + independent elements that can hold contents of size elem_size, each + of which starts out cleared, and can be independently freed, + realloc'ed etc. The elements are guaranteed to be adjacently + allocated (this is not guaranteed to occur with multiple callocs or + mallocs), which may also improve cache locality in some + applications. + + The "chunks" argument is optional (i.e., may be null, which is + probably the most typical usage). If it is null, the returned array + is itself dynamically allocated and should also be freed when it is + no longer needed. Otherwise, the chunks array must be of at least + n_elements in length. It is filled in with the pointers to the + chunks. + + In either case, independent_calloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and "chunks" + is null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use regular calloc and assign pointers into this + space to represent elements. (In this case though, you cannot + independently free elements.) + + independent_calloc simplifies and speeds up implementations of many + kinds of pools. It may also be useful when constructing large data + structures that initially have a fixed number of fixed-sized nodes, + but the number is not known at compile time, and some of the nodes + may later need to be freed. For example: + + struct Node { int item; struct Node* next; }; + + struct Node* build_list() { + struct Node** pool; + int n = read_number_of_nodes_needed(); + if (n <= 0) return 0; + pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); + if (pool == 0) die(); + // organize into a linked list... + struct Node* first = pool[0]; + for (i = 0; i < n-1; ++i) + pool[i]->next = pool[i+1]; + free(pool); // Can now free the array (or not, if it is needed later) + return first; + } + */ + void** rdlindependent_calloc(size_t, size_t, void**); + + /* + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + + independent_comalloc allocates, all at once, a set of n_elements + chunks with sizes indicated in the "sizes" array. It returns + an array of pointers to these elements, each of which can be + independently freed, realloc'ed etc. The elements are guaranteed to + be adjacently allocated (this is not guaranteed to occur with + multiple callocs or mallocs), which may also improve cache locality + in some applications. + + The "chunks" argument is optional (i.e., may be null). If it is null + the returned array is itself dynamically allocated and should also + be freed when it is no longer needed. Otherwise, the chunks array + must be of at least n_elements in length. It is filled in with the + pointers to the chunks. + + In either case, independent_comalloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and chunks is + null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use a single regular malloc, and assign pointers at + particular offsets in the aggregate space. (In this case though, you + cannot independently free elements.) + + independent_comallac differs from independent_calloc in that each + element may have a different size, and also that it does not + automatically clear elements. + + independent_comalloc can be used to speed up allocation in cases + where several structs or objects must always be allocated at the + same time. For example: + + struct Head { ... } + struct Foot { ... } + + void send_message(char* msg) { + int msglen = strlen(msg); + size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; + void* chunks[3]; + if (independent_comalloc(3, sizes, chunks) == 0) + die(); + struct Head* head = (struct Head*)(chunks[0]); + char* body = (char*)(chunks[1]); + struct Foot* foot = (struct Foot*)(chunks[2]); + // ... + } + + In general though, independent_comalloc is worth using only for + larger values of n_elements. For small values, you probably won't + detect enough difference from series of malloc calls to bother. + + Overuse of independent_comalloc can increase overall memory usage, + since it cannot reuse existing noncontiguous small chunks that + might be available for some of the elements. + */ + void** rdlindependent_comalloc(size_t, size_t*, void**); + + + /* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ + void* rdlpvalloc(size_t); + + /* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative arguments + to sbrk) if there is unused memory at the `high' end of the malloc + pool or in unused MMAP segments. You can call this after freeing + large blocks of memory to potentially reduce the system-level memory + requirements of a program. However, it cannot guarantee to reduce + memory. Under some allocation patterns, some large free blocks of + memory will be locked between two used chunks, so they cannot be + given back to the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, only + the minimum amount of memory to maintain internal data structures + will be left. Non-zero arguments can be supplied to maintain enough + trailing space to service future expected allocations without having + to re-obtain memory from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + */ + int rdlmalloc_trim(size_t); + + /* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. + */ + void rdlmalloc_stats(void); + +#endif /* ONLY_MSPACES */ + + /* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); + */ + size_t rdlmalloc_usable_size(void*); + + +#if MSPACES + + /* + mspace is an opaque type representing an independent + region of space that supports rak_mspace_malloc, etc. + */ + typedef void* mspace; + + /* + rak_create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service rak_mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). + */ + mspace rak_create_mspace(size_t capacity, int locked); + + /* + rak_destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. + */ + size_t rak_destroy_mspace(mspace msp); + + /* + rak_create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. + */ + mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked); + + /* + rak_mspace_track_large_chunks controls whether requests for large chunks + are allocated in their own untracked mmapped regions, separate from + others in this mspace. By default large chunks are not tracked, + which reduces fragmentation. However, such chunks are not + necessarily released to the system upon rak_destroy_mspace. Enabling + tracking by setting to true may increase fragmentation, but avoids + leakage when relying on rak_destroy_mspace to release all memory + allocated using this space. The function returns the previous + setting. + */ + int rak_mspace_track_large_chunks(mspace msp, int enable); + + + /* + rak_mspace_malloc behaves as malloc, but operates within + the given space. + */ + void* rak_mspace_malloc(mspace msp, size_t bytes); + + /* + rak_mspace_free behaves as free, but operates within + the given space. + + If compiled with FOOTERS==1, rak_mspace_free is not actually needed. + free may be called instead of rak_mspace_free because freed chunks from + any space are handled by their originating spaces. + */ + void rak_mspace_free(mspace msp, void* mem); + + /* + rak_mspace_realloc behaves as realloc, but operates within + the given space. + + If compiled with FOOTERS==1, rak_mspace_realloc is not actually + needed. realloc may be called instead of rak_mspace_realloc because + realloced chunks from any space are handled by their originating + spaces. + */ + void* rak_mspace_realloc(mspace msp, void* mem, size_t newsize); + + /* + rak_mspace_calloc behaves as calloc, but operates within + the given space. + */ + void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + + /* + rak_mspace_memalign behaves as memalign, but operates within + the given space. + */ + void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes); + + /* + rak_mspace_independent_calloc behaves as independent_calloc, but + operates within the given space. + */ + void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); + + /* + rak_mspace_independent_comalloc behaves as independent_comalloc, but + operates within the given space. + */ + void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); + + /* + rak_mspace_footprint() returns the number of bytes obtained from the + system for this space. + */ + size_t rak_mspace_footprint(mspace msp); + + /* + mspace_max_footprint() returns the peak number of bytes obtained from the + system for this space. + */ + size_t mspace_max_footprint(mspace msp); + + +#if !NO_MALLINFO + /* + rak_mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. + */ + struct mallinfo rak_mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + + /* + malloc_usable_size(void* p) behaves the same as malloc_usable_size; + */ + size_t rak_mspace_usable_size(void* mem); + + /* + rak_mspace_malloc_stats behaves as malloc_stats, but reports + properties of the given space. + */ + void rak_mspace_malloc_stats(mspace msp); + + /* + rak_mspace_trim behaves as malloc_trim, but + operates within the given space. + */ + int rak_mspace_trim(mspace msp, size_t pad); + + /* + An alias for mallopt. + */ + int rak_mspace_mallopt(int, int); + +#endif /* MSPACES */ + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif /* __cplusplus */ + +/* +======================================================================== +To make a fully customizable malloc.h header file, cut everything +above this line, put into file malloc.h, edit to suit, and #include it +on the next line, as well as in programs that use this malloc. +======================================================================== +*/ + +/* #include "malloc.h" */ + +/*------------------------------ internal #includes ---------------------- */ + +#ifdef DL_PLATFORM_WIN32 +#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ +#endif /* DL_PLATFORM_WIN32 */ + +#include /* for printing in malloc_stats */ + +#ifndef LACKS_ERRNO_H +#include /* for MALLOC_FAILURE_ACTION */ +#endif /* LACKS_ERRNO_H */ + +#if FOOTERS || DEBUG +#include /* for magic initialization */ +#endif /* FOOTERS */ + +#ifndef LACKS_STDLIB_H +#include /* for abort() */ +#endif /* LACKS_STDLIB_H */ + +#ifdef DEBUG +#if ABORT_ON_ASSERT_FAILURE +#undef assert +#define assert(x) if(!(x)) ABORT +#else /* ABORT_ON_ASSERT_FAILURE */ +#include +#endif /* ABORT_ON_ASSERT_FAILURE */ +#else /* DEBUG */ +#ifndef assert +#define assert(x) +#endif +#define DEBUG 0 +#endif /* DEBUG */ + +#ifndef LACKS_STRING_H +#include /* for memset etc */ +#endif /* LACKS_STRING_H */ + +#if USE_BUILTIN_FFS +#ifndef LACKS_STRINGS_H +#include /* for ffs */ +#endif /* LACKS_STRINGS_H */ +#endif /* USE_BUILTIN_FFS */ + +#if HAVE_MMAP +#ifndef LACKS_SYS_MMAN_H +/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ +#if (defined(linux) && !defined(__USE_GNU)) +#define __USE_GNU 1 +#include /* for mmap */ +#undef __USE_GNU +#else +#include /* for mmap */ +#endif /* linux */ +#endif /* LACKS_SYS_MMAN_H */ +#ifndef LACKS_FCNTL_H +#include +#endif /* LACKS_FCNTL_H */ +#endif /* HAVE_MMAP */ + +#ifndef LACKS_UNISTD_H +#include /* for sbrk, sysconf */ +#else /* LACKS_UNISTD_H */ +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +extern void* sbrk(ptrdiff_t); +#endif /* FreeBSD etc */ +#endif /* LACKS_UNISTD_H */ + +/* Declarations for locking */ +#if USE_LOCKS +#if defined(_XBOX) || defined(X360) +#pragma intrinsic (_InterlockedCompareExchange) +#pragma intrinsic (_InterlockedExchange) +#define interlockedcompareexchange _InterlockedCompareExchange +#define interlockedexchange _InterlockedExchange +#elif !defined(DL_PLATFORM_WIN32) +#include +#if defined (__SVR4) && defined (__sun) /* solaris */ +#include +#endif /* solaris */ +#else +#ifndef _M_AMD64 +/* These are already defined on AMD64 builds */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); + LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _M_AMD64 */ +#pragma intrinsic (_InterlockedCompareExchange) +#pragma intrinsic (_InterlockedExchange) +#define interlockedcompareexchange _InterlockedCompareExchange +#define interlockedexchange _InterlockedExchange +#endif /* Win32 */ +#endif /* USE_LOCKS */ + +/* Declarations for bit scanning on win32 */ +#if defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) +#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + unsigned char _BitScanForward(unsigned long *index, unsigned long mask); + unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define BitScanForward _BitScanForward +#define BitScanReverse _BitScanReverse +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) +#endif /* BitScanForward */ +#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ + +#ifndef DL_PLATFORM_WIN32 +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) +extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef DL_PLATFORM_WIN32 /* use supplied emulation of getpagesize */ +# define malloc_getpagesize getpagesize() +# else +# ifndef LACKS_SYS_PARAM_H +# include +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else /* just guess */ +# define malloc_getpagesize ((size_t)4096U) +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif +#endif + + + +/* ------------------- size_t and alignment properties -------------------- */ + +/* The byte and bit size of a size_t */ +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +/* Some constants coerced to size_t */ +/* Annoying but necessary to avoid errors on some platforms */ +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define SIZE_T_FOUR ((size_t)4) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* True if address a has acceptable alignment */ +#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) + +/* the number of bytes to offset an address to align it */ +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) + +/* -------------------------- MMAP preliminaries ------------------------- */ + +/* +If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and +checks to fail so compiler optimizer can delete code rather than +using so many "#if"s. +*/ + + +/* MORECORE and MMAP must return MFAIL on failure */ +#define MFAIL ((void*)(MAX_SIZE_T)) +#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ + +#if HAVE_MMAP + +#if defined(_XBOX) || defined(X360) + /* Win32 MMAP via VirtualAlloc */ + static void* win32mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; + } + + /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ + static void* win32direct_mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; + } + + /* This function supports releasing coalesed segments */ + static int win32munmap(void* ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = (char*)ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; + } + + #define RAK_MMAP_DEFAULT(s) win32mmap(s) + #define RAK_MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) + #define RAK_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) +#elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) + +inline int ___freeit_dlmalloc_default__(void* s) {free(s); return 0;} +#define RAK_MMAP_DEFAULT(s) malloc(s); +#define RAK_MUNMAP_DEFAULT(a, s) ___freeit_dlmalloc_default__(a); +#define RAK_DIRECT_MMAP_DEFAULT(s) malloc(s); + +#elif !defined(DL_PLATFORM_WIN32) + #define RAK_MUNMAP_DEFAULT(a, s) munmap((a), (s)) + #define MMAP_PROT (PROT_READ|PROT_WRITE) + #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) + #define MAP_ANONYMOUS MAP_ANON + #endif /* MAP_ANON */ + #ifdef MAP_ANONYMOUS + #define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) + #define RAK_MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) + #else /* MAP_ANONYMOUS */ + /* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. + */ + #define MMAP_FLAGS (MAP_PRIVATE) + static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ + #define RAK_MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ + (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) + #endif /* MAP_ANONYMOUS */ + + #define RAK_DIRECT_MMAP_DEFAULT(s) RAK_MMAP_DEFAULT(s) + +#else /* DL_PLATFORM_WIN32 */ + + /* Win32 MMAP via VirtualAlloc */ + static FORCEINLINE void* win32mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; + } + + /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ + static FORCEINLINE void* win32direct_mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; + } + + /* This function supports releasing coalesed segments */ + static FORCEINLINE int win32munmap(void* ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = (char*)ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; + } + + #define RAK_MMAP_DEFAULT(s) win32mmap(s) + #define RAK_MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) + #define RAK_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) +#endif /* DL_PLATFORM_WIN32 */ +#endif /* HAVE_MMAP */ + +#if HAVE_MREMAP +#ifndef DL_PLATFORM_WIN32 +#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) +#endif /* DL_PLATFORM_WIN32 */ +#endif /* HAVE_MREMAP */ + + +/** +* Define CALL_MORECORE +*/ +#if HAVE_MORECORE +#ifdef MORECORE +#define CALL_MORECORE(S) MORECORE(S) +#else /* MORECORE */ +#define CALL_MORECORE(S) MORECORE_DEFAULT(S) +#endif /* MORECORE */ +#else /* HAVE_MORECORE */ +#define CALL_MORECORE(S) MFAIL +#endif /* HAVE_MORECORE */ + +/** +* Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP +*/ +#if HAVE_MMAP +#define USE_MMAP_BIT (SIZE_T_ONE) + +#ifdef MMAP +#define CALL_MMAP(s) MMAP(s) +#else /* MMAP */ +#define CALL_MMAP(s) RAK_MMAP_DEFAULT(s) +#endif /* MMAP */ +#ifdef MUNMAP +#define CALL_MUNMAP(a, s) MUNMAP((a), (s)) +#else /* MUNMAP */ +#define CALL_MUNMAP(a, s) RAK_MUNMAP_DEFAULT((a), (s)) +#endif /* MUNMAP */ +#ifdef DIRECT_MMAP +#define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) +#else /* DIRECT_MMAP */ +#define CALL_DIRECT_MMAP(s) RAK_DIRECT_MMAP_DEFAULT(s) +#endif /* DIRECT_MMAP */ +#else /* HAVE_MMAP */ +#define USE_MMAP_BIT (SIZE_T_ZERO) + +#define MMAP(s) MFAIL +#define MUNMAP(a, s) (-1) +#define DIRECT_MMAP(s) MFAIL +#define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) +#define CALL_MMAP(s) MMAP(s) +#define CALL_MUNMAP(a, s) MUNMAP((a), (s)) +#endif /* HAVE_MMAP */ + +/** +* Define CALL_MREMAP +*/ +#if HAVE_MMAP && HAVE_MREMAP +#ifdef MREMAP +#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) +#else /* MREMAP */ +#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) +#endif /* MREMAP */ +#else /* HAVE_MMAP && HAVE_MREMAP */ +#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL +#endif /* HAVE_MMAP && HAVE_MREMAP */ + +/* mstate bit set if continguous morecore disabled or failed */ +#define USE_NONCONTIGUOUS_BIT (4U) + +/* segment bit set in rak_create_mspace_with_base */ +#define EXTERN_BIT (8U) + + +#endif /* MALLOC_280_H */ + #endif // _RAKNET_SUPPORT_DL_MALLOC \ No newline at end of file diff --git a/project/raspberry/py/api.py b/project/raspberry/py/api.py index 84dfc83..095b740 100755 --- a/project/raspberry/py/api.py +++ b/project/raspberry/py/api.py @@ -1,100 +1,100 @@ -import socket, select -import apiutil -from vec3 import Vec3 -from event import TileEvent - -comm = None -def connect(address, port=4711): - global comm - comm = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - comm.connect((address, 4711)) - -connect("localhost") - -def drainSocket(s): - while True: - read, w, x = select.select([s], [], [], 0.0) - if len(read) == 0: break - s.recv(1500) - -def generateFunc(name, unpack=None): - def _inner(*l): - s = apiutil.call(name,*l) - drainSocket(comm) - comm.send(s) - if not unpack: return - - r = comm.recv(1024) - if r != "Fail\n": - return unpack(r) - return _inner - -def unpackArrayTo(func, f=lambda x:x): - """Calling func with unpacked array element arguments""" - def _inner(s): return [func(*map(f, e.split(","))) for e in s.split("|") if e.strip()] - return _inner - -def unpackTo(func, f=lambda x:x): - """Calling func with unpacked arguments""" - def _inner(s): return func(*map(f, s.split(","))) - return _inner - -def postMessageLoop(): - n = 0 - while 1: - postMessage(n) - n += 1 - -def Print(s): - print(s) - -def readV3(s): return unpackTo(Vec3, float)(s) -def readV3ints(s): return unpackTo(Vec3, lambda x:int(float(x)))(s) - -class Holder: - pass - -# Block, world -world = Holder() -world.setBlock = generateFunc("world.setBlock") -world.getBlock = generateFunc("world.getBlock", int) -world.setBlocks = generateFunc("world.setBlocks") -world.getHeight = generateFunc("world.getHeight", int) -world.getPlayerIds = generateFunc("world.getPlayerIds", unpackArrayTo(int)) -world.setting = generateFunc("world.setting") - -# Checkpoint -world.checkpoint = Holder() -world.checkpoint.save = generateFunc("world.checkpoint.save") -world.checkpoint.restore= generateFunc("world.checkpoint.restore") -# = generateFunc("entity.getType - -# Player -player = Holder() -player.setTile = generateFunc("player.setTile") -player.getTile = generateFunc("player.getTile", readV3ints) -player.setPos = generateFunc("player.setPos") -player.getPos = generateFunc("player.getPos", readV3) -player.setting = generateFunc("player.setting") - -# Camera -camera = Holder() -camera.setNormal = generateFunc("camera.mode.setNormal") -#camera.setThirdPerson = generateFunc("camera.mode.setThirdPerson") -camera.setFixed = generateFunc("camera.mode.setFixed") -camera.setFollow = generateFunc("camera.mode.setFollow") -camera.setPos = generateFunc("camera.setPos") -#setCameraFollow1 = generateFunc("camera.mode.setFollowWithPosition") -#setCameraFollow2 = generateFunc("camera.mode.setFollowWithRotation") - -# Events -events = Holder() -events.block = Holder() -events.block.hits = generateFunc("events.block.hits", unpackArrayTo(TileEvent.Hit, int)) -#pollChat = generateFunc("events.chat", unpackArrayTo(TileEvent.Hit, int)) -# Chat -chat = Holder() -chat.post = generateFunc("chat.post") - -from functools import partial +import socket, select +import apiutil +from vec3 import Vec3 +from event import TileEvent + +comm = None +def connect(address, port=4711): + global comm + comm = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + comm.connect((address, 4711)) + +connect("localhost") + +def drainSocket(s): + while True: + read, w, x = select.select([s], [], [], 0.0) + if len(read) == 0: break + s.recv(1500) + +def generateFunc(name, unpack=None): + def _inner(*l): + s = apiutil.call(name,*l) + drainSocket(comm) + comm.send(s) + if not unpack: return + + r = comm.recv(1024) + if r != "Fail\n": + return unpack(r) + return _inner + +def unpackArrayTo(func, f=lambda x:x): + """Calling func with unpacked array element arguments""" + def _inner(s): return [func(*map(f, e.split(","))) for e in s.split("|") if e.strip()] + return _inner + +def unpackTo(func, f=lambda x:x): + """Calling func with unpacked arguments""" + def _inner(s): return func(*map(f, s.split(","))) + return _inner + +def postMessageLoop(): + n = 0 + while 1: + postMessage(n) + n += 1 + +def Print(s): + print(s) + +def readV3(s): return unpackTo(Vec3, float)(s) +def readV3ints(s): return unpackTo(Vec3, lambda x:int(float(x)))(s) + +class Holder: + pass + +# Block, world +world = Holder() +world.setBlock = generateFunc("world.setBlock") +world.getBlock = generateFunc("world.getBlock", int) +world.setBlocks = generateFunc("world.setBlocks") +world.getHeight = generateFunc("world.getHeight", int) +world.getPlayerIds = generateFunc("world.getPlayerIds", unpackArrayTo(int)) +world.setting = generateFunc("world.setting") + +# Checkpoint +world.checkpoint = Holder() +world.checkpoint.save = generateFunc("world.checkpoint.save") +world.checkpoint.restore= generateFunc("world.checkpoint.restore") +# = generateFunc("entity.getType + +# Player +player = Holder() +player.setTile = generateFunc("player.setTile") +player.getTile = generateFunc("player.getTile", readV3ints) +player.setPos = generateFunc("player.setPos") +player.getPos = generateFunc("player.getPos", readV3) +player.setting = generateFunc("player.setting") + +# Camera +camera = Holder() +camera.setNormal = generateFunc("camera.mode.setNormal") +#camera.setThirdPerson = generateFunc("camera.mode.setThirdPerson") +camera.setFixed = generateFunc("camera.mode.setFixed") +camera.setFollow = generateFunc("camera.mode.setFollow") +camera.setPos = generateFunc("camera.setPos") +#setCameraFollow1 = generateFunc("camera.mode.setFollowWithPosition") +#setCameraFollow2 = generateFunc("camera.mode.setFollowWithRotation") + +# Events +events = Holder() +events.block = Holder() +events.block.hits = generateFunc("events.block.hits", unpackArrayTo(TileEvent.Hit, int)) +#pollChat = generateFunc("events.chat", unpackArrayTo(TileEvent.Hit, int)) +# Chat +chat = Holder() +chat.post = generateFunc("chat.post") + +from functools import partial setAutojump = partial(player.setting, "autojump") \ No newline at end of file diff --git a/project/raspberry/py/apiutil.py b/project/raspberry/py/apiutil.py index 3888c98..836b9c0 100755 --- a/project/raspberry/py/apiutil.py +++ b/project/raspberry/py/apiutil.py @@ -1,13 +1,13 @@ -import collections - -def flatten(l): - for e in l: - if isinstance(e, collections.Iterable) and not isinstance(e, basestring): - for ee in flatten(e): yield ee - else: yield e - -def toParameters(l): - return ",".join(map(str, flatten(l))) - -def call(name, *l): - return "%s(%s)\n"%(name, toParameters(l)) +import collections + +def flatten(l): + for e in l: + if isinstance(e, collections.Iterable) and not isinstance(e, basestring): + for ee in flatten(e): yield ee + else: yield e + +def toParameters(l): + return ",".join(map(str, flatten(l))) + +def call(name, *l): + return "%s(%s)\n"%(name, toParameters(l)) diff --git a/project/raspberry/py/event.py b/project/raspberry/py/event.py index d4ae4a0..1a0a2c4 100755 --- a/project/raspberry/py/event.py +++ b/project/raspberry/py/event.py @@ -1,19 +1,19 @@ -from vec3 import Vec3 - -class TileEvent: - HIT = 0 - - def __init__(self, type, x, y, z, face, entityId): - self.type = type - self.pos = Vec3(x, y, z) - self.face = face - self.entityId = entityId - - def __str__(self): - types = {TileEvent.HIT: "Hit"} - return "TileEvent(%s @ %s:%d by %d)"%(types.get(self.type, 0), - self.pos, self.face, self.entityId); - - @staticmethod - def Hit(x, y, z, face, entityId): - return TileEvent(TileEvent.HIT, x, y, z, face, entityId) +from vec3 import Vec3 + +class TileEvent: + HIT = 0 + + def __init__(self, type, x, y, z, face, entityId): + self.type = type + self.pos = Vec3(x, y, z) + self.face = face + self.entityId = entityId + + def __str__(self): + types = {TileEvent.HIT: "Hit"} + return "TileEvent(%s @ %s:%d by %d)"%(types.get(self.type, 0), + self.pos, self.face, self.entityId); + + @staticmethod + def Hit(x, y, z, face, entityId): + return TileEvent(TileEvent.HIT, x, y, z, face, entityId) diff --git a/project/raspberry/py/game_snake.py b/project/raspberry/py/game_snake.py index da656d5..3918728 100755 --- a/project/raspberry/py/game_snake.py +++ b/project/raspberry/py/game_snake.py @@ -1,199 +1,199 @@ -import time, random -import api, kbhelp -from vec3 import Vec3 - -class BlockType: - AIR = 0 - ROCK = 1 - DIRT = 3 - SANDSTONE = 24 - CLOTH = 35 - CACTUS = 81 - -class ClothColor: - WHITE = 0 - RED = 14 - - @staticmethod - def dark(x): return x + 8 if x < 8 else x - def bright(x): return x - 8 if x >= 8 else x - -class Level: - Wall = BlockType.DIRT - Food = BlockType.SANDSTONE - - def __init__(self, w, h, pos=Vec3(0,0,0)): - self.w = w - self.h = h - self.pos = pos - self.m = [0] * (w * h) - - def setup(self, numApples): - size = Vec3(self.w-1, 0, self.h-1) - margin = Vec3(1, 0, 1) - api.world.setBlocks(self.pos - margin + Vec3.down(3), self.pos + size + margin, BlockType.ROCK) - api.world.setBlocks(self.pos, self.pos + size + Vec3.up(8), BlockType.AIR) - - for i in random.sample(range(self.w * self.h), numApples): - xx = i % self.w - yy = i / self.w - self.m[i] = Level.Food - api.world.setBlock(xx, self.pos.y, yy, Level.Food) - - def getTile(self, x, y): - return self.m[y * self.w + x] if self.inRange(x, y) else None - - def clearTile(self, x, y): - if self.inRange(x, y): self.m[y * self.w + x] = 0 - - def isFood(self, x, y): - return self.inRange(x, y) and Level.Food == self.getTile(x, y) - - def isWall(self, x, y): - return (not self.inRange(x, y)) or Level.Wall == self.getTile(x, y) - - def inRange(self, x, y): - return x >= 0 and y >= 0 and x < self.w and y < self.h - - def countApples(self): - return self.m.count(Level.Food) - -class Snake: - def __init__(self, pos=Vec3(0,0,0), length=5): - self.dir = Vec3(1, 0, 0) - self.seg = [pos] - self.dead = False - self.growTicks = 0 - - for i in range(length-1): - self._add() - - def left(self): self.dir.rotateLeft() - def right(self): self.dir.rotateRight() - - def next(self): - return self.head() + self.dir - - def head(self): - return self.seg[-1] - - def tick(self): - self._add() - if self.growTicks == 0: self._remove() - if self.growTicks > 0: self.growTicks -= 1 - - def _add(self): - n = self.next() - if n in self.seg: - self.kill() - return - self.seg.append( n ) - api.world.setBlock(n.x, n.y, n.z, 45) - - def _remove(self): - h, self.seg = self.seg[0], self.seg[1:] - api.world.setBlock(h.x, h.y, h.z, 0) - - def grow(self, ticks): - self.growTicks = ticks - - def kill(self): - for p in self.seg: - api.world.setBlock(p.x, p.y, p.z, BlockType.CLOTH, 14) - self.dead = True - -class SnakeGame: - def __init__(self, numSnakes): - self.numSnakes = numSnakes - self.snakes = [] - self.level = None - self.noActionTicks = 10 - - def startLevel(self, level, numApples=8): - self.level = level - self.level.setup(numApples) - self.snakes = [Snake(Vec3(3, 0, 3)) for x in range(self.numSnakes)] - self.noActionTicks = 10 - for s in self.snakes: - for t in s.seg: self.level.clearTile(t.x, t.z) - api.camera.setFixed() - api.camera.setPos(Vec3((level.w-1)/2.0, 10, level.h/2.0 - 1)) - - def tick(self): - if self.noActionTicks > 0: - self.noActionTicks -= 1 - return - - for s in self.snakes: - self.tickSnake(s) - - def tickSnake(self, snake): - if snake.dead: return - self.onTile(snake, snake.next()) - if not snake.dead: snake.tick() - - def isFinished(self): - return self.isWon() or self.allSnakesDead() - - def isWon(self): - #print self.level.countApples() - return self.level.countApples() == 0 - - def allSnakesDead(self): - for s in self.snakes: - if not s.dead: return False - return True - - def onTile(self, snake, pos): - x, y = pos.x, pos.z - if self.level.isWall(x, y): snake.kill() - if self.level.isFood(x, y): snake.grow(2) - self.level.clearTile(x, y) - -def run(): - kb = kbhelp.KeyboardInput() - keys = (('a', 'd'), - ('4', '6')) - - numSnakes = 1 - - def play(level, numApples, tickDelay): - g = SnakeGame(numSnakes) - g.startLevel(Level(16, 12), numApples) - api.chat.post("Snake - level %d"%level) - - while 1: - # Update input - if kb.hasChar(): - key = kb.readChar() - for i in range(min(len(g.snakes), len(keys))): - LeftKey, RightKey = keys[i] - s = g.snakes[i] - if key == LeftKey: s.left() - if key == RightKey: s.right() - # Update game - g.tick() - time.sleep(tickDelay) - - if g.isFinished(): - return g.isWon() - - delay = 0.15 - level = 0 - apples = 5 - - while 1: - level += 1 - apples += 1 - won = play(level, apples, delay) - if not won: - api.chat.post("Snake - Game over! [ level %d ]!"%level) - return - - delay -= 0.01 - if delay < 0.04: - return - - -if __name__ == "__main__": - run() +import time, random +import api, kbhelp +from vec3 import Vec3 + +class BlockType: + AIR = 0 + ROCK = 1 + DIRT = 3 + SANDSTONE = 24 + CLOTH = 35 + CACTUS = 81 + +class ClothColor: + WHITE = 0 + RED = 14 + + @staticmethod + def dark(x): return x + 8 if x < 8 else x + def bright(x): return x - 8 if x >= 8 else x + +class Level: + Wall = BlockType.DIRT + Food = BlockType.SANDSTONE + + def __init__(self, w, h, pos=Vec3(0,0,0)): + self.w = w + self.h = h + self.pos = pos + self.m = [0] * (w * h) + + def setup(self, numApples): + size = Vec3(self.w-1, 0, self.h-1) + margin = Vec3(1, 0, 1) + api.world.setBlocks(self.pos - margin + Vec3.down(3), self.pos + size + margin, BlockType.ROCK) + api.world.setBlocks(self.pos, self.pos + size + Vec3.up(8), BlockType.AIR) + + for i in random.sample(range(self.w * self.h), numApples): + xx = i % self.w + yy = i / self.w + self.m[i] = Level.Food + api.world.setBlock(xx, self.pos.y, yy, Level.Food) + + def getTile(self, x, y): + return self.m[y * self.w + x] if self.inRange(x, y) else None + + def clearTile(self, x, y): + if self.inRange(x, y): self.m[y * self.w + x] = 0 + + def isFood(self, x, y): + return self.inRange(x, y) and Level.Food == self.getTile(x, y) + + def isWall(self, x, y): + return (not self.inRange(x, y)) or Level.Wall == self.getTile(x, y) + + def inRange(self, x, y): + return x >= 0 and y >= 0 and x < self.w and y < self.h + + def countApples(self): + return self.m.count(Level.Food) + +class Snake: + def __init__(self, pos=Vec3(0,0,0), length=5): + self.dir = Vec3(1, 0, 0) + self.seg = [pos] + self.dead = False + self.growTicks = 0 + + for i in range(length-1): + self._add() + + def left(self): self.dir.rotateLeft() + def right(self): self.dir.rotateRight() + + def next(self): + return self.head() + self.dir + + def head(self): + return self.seg[-1] + + def tick(self): + self._add() + if self.growTicks == 0: self._remove() + if self.growTicks > 0: self.growTicks -= 1 + + def _add(self): + n = self.next() + if n in self.seg: + self.kill() + return + self.seg.append( n ) + api.world.setBlock(n.x, n.y, n.z, 45) + + def _remove(self): + h, self.seg = self.seg[0], self.seg[1:] + api.world.setBlock(h.x, h.y, h.z, 0) + + def grow(self, ticks): + self.growTicks = ticks + + def kill(self): + for p in self.seg: + api.world.setBlock(p.x, p.y, p.z, BlockType.CLOTH, 14) + self.dead = True + +class SnakeGame: + def __init__(self, numSnakes): + self.numSnakes = numSnakes + self.snakes = [] + self.level = None + self.noActionTicks = 10 + + def startLevel(self, level, numApples=8): + self.level = level + self.level.setup(numApples) + self.snakes = [Snake(Vec3(3, 0, 3)) for x in range(self.numSnakes)] + self.noActionTicks = 10 + for s in self.snakes: + for t in s.seg: self.level.clearTile(t.x, t.z) + api.camera.setFixed() + api.camera.setPos(Vec3((level.w-1)/2.0, 10, level.h/2.0 - 1)) + + def tick(self): + if self.noActionTicks > 0: + self.noActionTicks -= 1 + return + + for s in self.snakes: + self.tickSnake(s) + + def tickSnake(self, snake): + if snake.dead: return + self.onTile(snake, snake.next()) + if not snake.dead: snake.tick() + + def isFinished(self): + return self.isWon() or self.allSnakesDead() + + def isWon(self): + #print self.level.countApples() + return self.level.countApples() == 0 + + def allSnakesDead(self): + for s in self.snakes: + if not s.dead: return False + return True + + def onTile(self, snake, pos): + x, y = pos.x, pos.z + if self.level.isWall(x, y): snake.kill() + if self.level.isFood(x, y): snake.grow(2) + self.level.clearTile(x, y) + +def run(): + kb = kbhelp.KeyboardInput() + keys = (('a', 'd'), + ('4', '6')) + + numSnakes = 1 + + def play(level, numApples, tickDelay): + g = SnakeGame(numSnakes) + g.startLevel(Level(16, 12), numApples) + api.chat.post("Snake - level %d"%level) + + while 1: + # Update input + if kb.hasChar(): + key = kb.readChar() + for i in range(min(len(g.snakes), len(keys))): + LeftKey, RightKey = keys[i] + s = g.snakes[i] + if key == LeftKey: s.left() + if key == RightKey: s.right() + # Update game + g.tick() + time.sleep(tickDelay) + + if g.isFinished(): + return g.isWon() + + delay = 0.15 + level = 0 + apples = 5 + + while 1: + level += 1 + apples += 1 + won = play(level, apples, delay) + if not won: + api.chat.post("Snake - Game over! [ level %d ]!"%level) + return + + delay -= 0.01 + if delay < 0.04: + return + + +if __name__ == "__main__": + run() diff --git a/project/raspberry/py/image.py b/project/raspberry/py/image.py index 41ebd53..7dadeab 100755 --- a/project/raspberry/py/image.py +++ b/project/raspberry/py/image.py @@ -1,38 +1,38 @@ -import Image -import apiutil - -def hexToRgb(hex): - return (hex >> 16) & 255, (hex >> 8) & 255, hex & 255 - -cgaColors = map(hexToRgb, ( - 0x000000, - 0x0000A8, - 0x00A800, - 0x00A8A8, - 0xFF0000, - 0xA800A8, - 0xA85400, - 0xA8A8A8, - 0x545454, - 0x5454FE, - 0x54FE54, - 0x54FEFE, - 0xFE5454, - 0xFE54FE, - 0xFEFE54, - 0xFEFEFE)) - -cgaPalette = apiutil.flatten(cgaColors) - -def _padPalette(values, count): - v = tuple(values) - return v + (0, 0, 0) * (count - len(v)) - -def imageToCga(im): - p = Image.new("P", (1,1)) - p.putpalette(_padPalette(cgaPalette, 256)) - return im.convert("RGB").quantize(palette=p, kmeans=255) - -def imageToWool(im, (w, h)): - cga = imageToCga(im.resize((w, h))) - +import Image +import apiutil + +def hexToRgb(hex): + return (hex >> 16) & 255, (hex >> 8) & 255, hex & 255 + +cgaColors = map(hexToRgb, ( + 0x000000, + 0x0000A8, + 0x00A800, + 0x00A8A8, + 0xFF0000, + 0xA800A8, + 0xA85400, + 0xA8A8A8, + 0x545454, + 0x5454FE, + 0x54FE54, + 0x54FEFE, + 0xFE5454, + 0xFE54FE, + 0xFEFE54, + 0xFEFEFE)) + +cgaPalette = apiutil.flatten(cgaColors) + +def _padPalette(values, count): + v = tuple(values) + return v + (0, 0, 0) * (count - len(v)) + +def imageToCga(im): + p = Image.new("P", (1,1)) + p.putpalette(_padPalette(cgaPalette, 256)) + return im.convert("RGB").quantize(palette=p, kmeans=255) + +def imageToWool(im, (w, h)): + cga = imageToCga(im.resize((w, h))) + diff --git a/project/raspberry/py/kbhelp.py b/project/raspberry/py/kbhelp.py index fed1412..0c72ca9 100755 --- a/project/raspberry/py/kbhelp.py +++ b/project/raspberry/py/kbhelp.py @@ -1,60 +1,60 @@ -try: - import msvcrt - - """Win32 implementation of a keyboard input reader""" - class KeyboardInput_win32: - def __init__(self): - self.setBlocking(True) - - def clear(self): - while 1: - if self.readChar_nonblocking() is None: - break - - def setBlocking(self, block): - self.readChar = self.readChar_blocking if block else \ - self.readChar_nonblocking - - def hasChar(self): - return msvcrt.kbhit() - - def readChar_blocking(self): - return msvcrt.getch() - - def readChar_nonblocking(self): - if not msvcrt.kbhit(): return None - return msvcrt.getch() - - KeyboardInput = KeyboardInput_win32 - -except: - try: - class KeyboardInput_unix: - def hasChar(self): - import sys, tty, termios - from select import select - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) - try: - tty.setraw(sys.stdin.fileno()) - [i, o, e] = select([sys.stdin.fileno()], [], [], 5) - if i: ch = True - else: ch = False - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - return ch - - def readChar(self): - import sys, tty, termios - from select import select - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) - try: - tty.setraw(sys.stdin.fileno()) - ch = sys.stdin.read(1) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - return ch - - KeyboardInput = KeyboardInput_unix - except: pass +try: + import msvcrt + + """Win32 implementation of a keyboard input reader""" + class KeyboardInput_win32: + def __init__(self): + self.setBlocking(True) + + def clear(self): + while 1: + if self.readChar_nonblocking() is None: + break + + def setBlocking(self, block): + self.readChar = self.readChar_blocking if block else \ + self.readChar_nonblocking + + def hasChar(self): + return msvcrt.kbhit() + + def readChar_blocking(self): + return msvcrt.getch() + + def readChar_nonblocking(self): + if not msvcrt.kbhit(): return None + return msvcrt.getch() + + KeyboardInput = KeyboardInput_win32 + +except: + try: + class KeyboardInput_unix: + def hasChar(self): + import sys, tty, termios + from select import select + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + [i, o, e] = select([sys.stdin.fileno()], [], [], 5) + if i: ch = True + else: ch = False + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + def readChar(self): + import sys, tty, termios + from select import select + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + KeyboardInput = KeyboardInput_unix + except: pass diff --git a/project/raspberry/py/vec3.py b/project/raspberry/py/vec3.py index 0bef495..45ed368 100755 --- a/project/raspberry/py/vec3.py +++ b/project/raspberry/py/vec3.py @@ -1,80 +1,80 @@ -class Vec3: - def __init__(self, x=0, y=0, z=0): - self.x = x - self.y = y - self.z = z - - def __add__(self, rhs): - c = self.clone() - c += rhs - return c - - def __iadd__(self, rhs): - self.x += rhs.x - self.y += rhs.y - self.z += rhs.z - return self - - def clone(self): - return Vec3(self.x, self.y, self.z) - - def __neg__(self): - return Vec3(-self.x, -self.y, -self.z) - - def __sub__(self, rhs): - return self.__add__(-rhs) - - def __isub__(self, rhs): - return self.__iadd__(-rhs) - - def __repr__(self): - return "Vec3(%s,%s,%s)"%(self.x,self.y,self.z) - - def __iter__(self): - return iter((self.x, self.y, self.z)) - - def _map(self, func): - self.x = func(self.x) - self.y = func(self.y) - self.z = func(self.z) - - def __cmp__(self, rhs): - dx = self.x - rhs.x - if dx != 0: return dx - dy = self.y - rhs.y - if dy != 0: return dy - dz = self.z - rhs.z - if dz != 0: return dz - return 0 - - def iround(self): self._map(lambda v:int(v+0.5)) - def ifloor(self): self._map(int) - - def rotateLeft(self): self.x, self.z = self.z, -self.x - def rotateRight(self): self.x, self.z = -self.z, self.x - - @staticmethod - def y(n=1): return Vec3(0, n, 0) - @staticmethod - def up(n=1): return Vec3.y(n) - @staticmethod - def down(n=1): return Vec3.y(-n) - -def testVec3(): - # Note: It's not testing everything - - # 1.1 Test initialization - it = Vec3(1, -2, 3) - assert it.x == 1 - assert it.y == -2 - assert it.z == 3 - - # 2.1 Test cloning and equality - clone = it.clone() - assert it == clone - it.x += 1 - assert it != clone - - # Test repr - e = eval(repr(it)) - assert e == it +class Vec3: + def __init__(self, x=0, y=0, z=0): + self.x = x + self.y = y + self.z = z + + def __add__(self, rhs): + c = self.clone() + c += rhs + return c + + def __iadd__(self, rhs): + self.x += rhs.x + self.y += rhs.y + self.z += rhs.z + return self + + def clone(self): + return Vec3(self.x, self.y, self.z) + + def __neg__(self): + return Vec3(-self.x, -self.y, -self.z) + + def __sub__(self, rhs): + return self.__add__(-rhs) + + def __isub__(self, rhs): + return self.__iadd__(-rhs) + + def __repr__(self): + return "Vec3(%s,%s,%s)"%(self.x,self.y,self.z) + + def __iter__(self): + return iter((self.x, self.y, self.z)) + + def _map(self, func): + self.x = func(self.x) + self.y = func(self.y) + self.z = func(self.z) + + def __cmp__(self, rhs): + dx = self.x - rhs.x + if dx != 0: return dx + dy = self.y - rhs.y + if dy != 0: return dy + dz = self.z - rhs.z + if dz != 0: return dz + return 0 + + def iround(self): self._map(lambda v:int(v+0.5)) + def ifloor(self): self._map(int) + + def rotateLeft(self): self.x, self.z = self.z, -self.x + def rotateRight(self): self.x, self.z = -self.z, self.x + + @staticmethod + def y(n=1): return Vec3(0, n, 0) + @staticmethod + def up(n=1): return Vec3.y(n) + @staticmethod + def down(n=1): return Vec3.y(-n) + +def testVec3(): + # Note: It's not testing everything + + # 1.1 Test initialization + it = Vec3(1, -2, 3) + assert it.x == 1 + assert it.y == -2 + assert it.z == 3 + + # 2.1 Test cloning and equality + clone = it.clone() + assert it == clone + it.x += 1 + assert it != clone + + # Test repr + e = eval(repr(it)) + assert e == it diff --git a/project/win32/MinecraftWin32.sln b/project/win32/MinecraftWin32.sln index d1bdd36..a5deb17 100755 --- a/project/win32/MinecraftWin32.sln +++ b/project/win32/MinecraftWin32.sln @@ -1,32 +1,32 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinecraftWin32", "MinecraftWin32.vcxproj", "{61D12CAD-CC16-4024-9C45-DE97AB0465AF}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{57B259AC-F044-44F2-A67D-DA49970BED5D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinecraftWin32_GL", "..\win32_gl\MinecraftWin32_GL.vcxproj", "{19B34E47-B11C-E628-ED97-698E93339EA3}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug DEMO|Win32 = Debug DEMO|Win32 - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Debug DEMO|Win32.ActiveCfg = Debug DEMO|Win32 - {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Debug DEMO|Win32.Build.0 = Debug DEMO|Win32 - {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Debug|Win32.ActiveCfg = Debug|Win32 - {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Debug|Win32.Build.0 = Debug|Win32 - {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Release|Win32.ActiveCfg = Release|Win32 - {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Release|Win32.Build.0 = Release|Win32 - {19B34E47-B11C-E628-ED97-698E93339EA3}.Debug DEMO|Win32.ActiveCfg = Debug|Win32 - {19B34E47-B11C-E628-ED97-698E93339EA3}.Debug|Win32.ActiveCfg = Debug|Win32 - {19B34E47-B11C-E628-ED97-698E93339EA3}.Debug|Win32.Build.0 = Debug|Win32 - {19B34E47-B11C-E628-ED97-698E93339EA3}.Release|Win32.ActiveCfg = Release|Win32 - {19B34E47-B11C-E628-ED97-698E93339EA3}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinecraftWin32", "MinecraftWin32.vcxproj", "{61D12CAD-CC16-4024-9C45-DE97AB0465AF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{57B259AC-F044-44F2-A67D-DA49970BED5D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinecraftWin32_GL", "..\win32_gl\MinecraftWin32_GL.vcxproj", "{19B34E47-B11C-E628-ED97-698E93339EA3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug DEMO|Win32 = Debug DEMO|Win32 + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Debug DEMO|Win32.ActiveCfg = Debug DEMO|Win32 + {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Debug DEMO|Win32.Build.0 = Debug DEMO|Win32 + {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Debug|Win32.ActiveCfg = Debug|Win32 + {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Debug|Win32.Build.0 = Debug|Win32 + {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Release|Win32.ActiveCfg = Release|Win32 + {61D12CAD-CC16-4024-9C45-DE97AB0465AF}.Release|Win32.Build.0 = Release|Win32 + {19B34E47-B11C-E628-ED97-698E93339EA3}.Debug DEMO|Win32.ActiveCfg = Debug|Win32 + {19B34E47-B11C-E628-ED97-698E93339EA3}.Debug|Win32.ActiveCfg = Debug|Win32 + {19B34E47-B11C-E628-ED97-698E93339EA3}.Debug|Win32.Build.0 = Debug|Win32 + {19B34E47-B11C-E628-ED97-698E93339EA3}.Release|Win32.ActiveCfg = Release|Win32 + {19B34E47-B11C-E628-ED97-698E93339EA3}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/project/win32/MinecraftWin32.vcxproj b/project/win32/MinecraftWin32.vcxproj index 04646a7..26495d6 100755 --- a/project/win32/MinecraftWin32.vcxproj +++ b/project/win32/MinecraftWin32.vcxproj @@ -1,1126 +1,1126 @@ - - - - - Debug DEMO - Win32 - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {61D12CAD-CC16-4024-9C45-DE97AB0465AF} - Win32Proj - MinecraftWin32 - - - - Application - true - NotSet - v110 - - - Application - true - NotSet - v110 - - - Application - false - true - NotSet - v110 - - - - - - - - - - - - - - - - true - ..\..\lib\include;$(IncludePath) - ..\..\lib\lib;$(LibraryPath) - - - true - ..\..\lib\include;$(IncludePath) - ..\..\lib\lib;$(LibraryPath) - - - false - ..\..\lib\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; - ..\..\lib\lib;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib - - - - - - Level3 - Disabled - GLDEBUG;OPENGL_ES;WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions) - true - false - - - Console - true - libEGL.lib;libGLES_CM.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) - - - - - - - - - Level3 - Disabled - DEMO_MODE;OPENGL_ES;WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - libEGL.lib;libGLES_CM.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - OPENGL_ES;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true - $(WindowsSdkDir)\include\um;$(WindowsSdkDir)\include\shared;%(AdditionalIncludeDirectories) - - - Console - true - true - true - libEGL.lib;libGLES_CM.lib;WS2_32.Lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) - $(WindowsSdkDir)lib\win8\um\x86;%(AdditionalLibraryDirectories) - - - - - + + + + + Debug DEMO + Win32 + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {61D12CAD-CC16-4024-9C45-DE97AB0465AF} + Win32Proj + MinecraftWin32 + + + + Application + true + NotSet + v110 + + + Application + true + NotSet + v110 + + + Application + false + true + NotSet + v110 + + + + + + + + + + + + + + + + true + ..\..\lib\include;$(IncludePath) + ..\..\lib\lib;$(LibraryPath) + + + true + ..\..\lib\include;$(IncludePath) + ..\..\lib\lib;$(LibraryPath) + + + false + ..\..\lib\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; + ..\..\lib\lib;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib + + + + + + Level3 + Disabled + GLDEBUG;OPENGL_ES;WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions) + true + false + + + Console + true + libEGL.lib;libGLES_CM.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + Disabled + DEMO_MODE;OPENGL_ES;WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + libEGL.lib;libGLES_CM.lib;ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + OPENGL_ES;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + $(WindowsSdkDir)\include\um;$(WindowsSdkDir)\include\shared;%(AdditionalIncludeDirectories) + + + Console + true + true + true + libEGL.lib;libGLES_CM.lib;WS2_32.Lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) + $(WindowsSdkDir)lib\win8\um\x86;%(AdditionalLibraryDirectories) + + + + + \ No newline at end of file diff --git a/project/win32/MinecraftWin32.vcxproj.filters b/project/win32/MinecraftWin32.vcxproj.filters index 0c9ec12..37e4d51 100755 --- a/project/win32/MinecraftWin32.vcxproj.filters +++ b/project/win32/MinecraftWin32.vcxproj.filters @@ -1,3191 +1,3191 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - {315d8115-3fca-4a3f-9374-57f41665e3a0} - - - {84771a0f-b6af-479c-a26d-fbf611d14fcb} - - - {93f057aa-83e6-442e-a126-9d244453eb38} - - - {82f6d408-1efb-43bc-99fd-f6029eabe32b} - - - {01996801-7e5f-476e-b41c-e7d3bad443b0} - - - {4398bfb3-fb0a-498c-9772-3c815e8ad360} - - - {7887f605-f5e2-4bca-86b9-ccaf7228184d} - - - {9b2956dc-ea58-4192-8bb8-3a7c97e92141} - - - {c13ce830-5bad-4622-b008-4198a50fd760} - - - {d0112033-5536-42af-9a19-d188bb7b4a29} - - - {ee8fd90c-82e9-4f37-b468-fa264e20843d} - - - {cbcb2ae1-8eee-4d76-b137-e68c8edc55c0} - - - {81419bca-bcb4-46bd-bfd5-d05ec83adece} - - - {5094f961-132c-4d23-9077-353cf989adc5} - - - {71ab63f9-7c1d-4cdc-bbe1-67a69e54fcf9} - - - {e9e550bf-9345-4cb4-bd3d-a6d0f70136f6} - - - {94682265-c50a-4307-84b3-a2e2488f56aa} - - - {57e82abe-b138-40da-be47-75c872682126} - - - {3053195e-e3a9-4481-813f-2c63c7d4fc3b} - - - {4f6589cb-0d2a-4a0d-b94f-a15cc5af30db} - - - {711b9f4f-6a59-4c97-a37d-50f5e6171b08} - - - {7f18df66-372a-4d60-9e34-ddaed7b522e3} - - - {eed0a973-fbec-4dd6-96ef-308b80850ab1} - - - {ef4ebf01-6f5f-4b01-990d-feb9885d9f11} - - - {3c1f4573-74cc-4b76-b4ce-fc01f1e9ff6f} - - - {2360ae39-413c-4514-8898-fbed347600bd} - - - {e9f52cee-84ea-43b1-814d-822cb70805fa} - - - {e536d920-0d4b-4c39-97de-2b83a6ca3cf4} - - - {7c33753b-7228-4c43-a0db-e50621e60be8} - - - {00188837-a1b6-446f-94aa-c7cbd7da8291} - - - {bdda49a8-dce0-4e2c-bd52-a08949611547} - - - {fe0157fa-e486-47be-9a78-ea4abe58a75a} - - - {3f7b68ba-24bf-4a04-a072-3c63d061c6d3} - - - {7abec229-7c62-4f8c-be0c-b0541ebe6a2b} - - - {9a993a4b-5523-4ce6-8fc5-d45b6c098655} - - - {5db94373-ca02-4a85-8029-da71c6bb7b9c} - - - {db6877ab-b3ee-4c4e-a4db-a934d366ccf2} - - - {55eb3700-6e83-4ddd-acf1-8007be34240c} - - - {d75d193d-81f9-4fbd-8c1e-0688149b829b} - - - {64ffbf67-3499-4004-8bb1-124d6ef1c10c} - - - {0f4d45e8-d068-489e-a66e-d42d36067fbd} - - - {e2511587-18cc-4635-9921-513debf878df} - - - {99bc510f-d187-40f7-befc-1c5a02571110} - - - {76b518bc-f17f-4bf5-bd69-15c3945d8d98} - - - {89c4918c-8ff9-4290-81c8-e6115afb6e0a} - - - {8faf4949-1190-46ba-97ba-8d854cb8f104} - - - {491d98ea-686d-417c-9e42-ed3a4e66781d} - - - {d400197c-231e-4e27-a176-9e91bcc92934} - - - {d0aee588-4f58-44f9-8a82-0d1998cba02f} - - - {9b9a6b73-102d-40b8-bd32-cb552c9f83e4} - - - {85c02cb0-6cdf-4ec8-9d94-92b7c7b82e0c} - - - {1a46cf05-e5e8-4b61-af27-291435577b37} - - - {57b58ea4-2f23-4f79-ac82-6c40619f3ded} - - - {cab0cd1e-1e3c-4f6b-af63-a8b90c20e86e} - - - {fe32771a-8f08-471c-8696-28951441b3a2} - - - {7c895144-ec49-4465-be5c-e59960757bb3} - - - {3860d844-27c3-441e-adfa-e5993010a24a} - - - {8d46e789-d2ac-4888-adc7-499c25659dd8} - - - {d73bff72-4294-4b54-acf1-e4da7c159895} - - - {908ab587-566a-4177-94a0-67e66c25fac1} - - - {633779f8-e638-42a9-a5e4-98c3c8d810b0} - - - {160d7e15-8f93-46c7-8aec-a13299e9d090} - - - {d9a5faa2-fae5-4072-950e-89ca5a5f86fd} - - - - - Source Files - - - Source Files\client - - - Source Files\client - - - Source Files\client\gamemode - - - Source Files\client\player - - - Platform - - - Source Files\util - - - Source Files\world - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\world\phys - - - Source Files\world\level - - - Source Files\world\level - - - Source Files\world\level\chunk - - - Source Files\world\level\dimension - - - Source Files\world\level\levelgen - - - Source Files\world\level\material - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\world\level - - - Source Files\client\renderer\culling - - - Source Files\client\renderer - - - Source Files\world\level - - - Source Files\world\entity\player - - - Platform\input - - - Platform\input - - - Platform\input - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\level - - - Source Files\client\renderer - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\renderer - - - Platform - - - Source Files\client\gui - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Source Files\client\renderer\entity - - - Source Files\client\renderer\entity - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\client\sound - - - Source Files\client\renderer\entity - - - Source Files\world\level\biome - - - Source Files\world\level\biome - - - Source Files\client\renderer\entity - - - Source Files\client\renderer\entity - - - Source Files - - - Source Files\world\level\storage - - - Source Files\world\level\storage - - - Source Files - - - Source Files\world\level\tile - - - Source Files\client\renderer\ptexture - - - Source Files\client\renderer\entity - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\world\level - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen\synth - - - Source Files\world\level\levelgen\synth - - - Source Files\world\level\levelgen\synth - - - Source Files\world\entity\item - - - Source Files\world\entity\item - - - Source Files\world\entity\item - - - Source Files\world\entity\item - - - Source Files\client\renderer\entity - - - Source Files\client\gui - - - Source Files\client\gui - - - Source Files\client\gui - - - Source Files\client\renderer\entity - - - Source Files\client\model - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\util - - - Source Files\client\gui\screens - - - Source Files\world\level\storage - - - Source Files\client\gui\screens - - - Source Files\client\player\input - - - Platform\input - - - Source Files\client\player\input\touchscreen - - - Source Files\client\gui\screens\touch - - - Source Files\client\gui\components - - - Source Files - - - Source Files\client\gui\screens\touch - - - Source Files\client\gui\screens\touch - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client\gui\screens - - - Source Files\client\gui\components - - - Source Files\client\gui\screens\touch - - - Source Files\client - - - Source Files\nbt - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\entity - - - Source Files\world\level - - - Source Files\client\model\geom - - - Source Files\client\model\geom - - - Source Files\client\model\geom - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\world\entity\animal - - - Source Files\world\entity\animal - - - Source Files\world\entity\animal - - - Source Files\world\entity\animal - - - Source Files\world\entity\animal - - - Source Files\util - - - Source Files\util - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\world\entity\monster - - - Source Files\network - - - Source Files\client\renderer\entity - - - Source Files\world\item\crafting - - - Source Files\world\item\crafting - - - Source Files\world\entity\projectile - - - Source Files\world\item\crafting - - - Source Files\world\entity\animal - - - Source Files\client\gamemode - - - Source Files\util - - - Source Files\world\entity - - - Source Files\client\gui\screens - - - Source Files\world\entity\player - - - Source Files\world\item - - - Source Files\world\level\storage - - - Source Files\world\entity\ai\control - - - Source Files\client - - - Source Files - - - Source Files\world\level\pathfinder - - - Source Files\world\item\crafting - - - Source Files\locale - - - Libs\raknet - - - Source Files\client\gui\components - - - Source Files\world\item\crafting - - - Source Files\client\sound - - - Source Files\world\level\tile - - - Source Files\world\level\tile\entity - - - Source Files\world\level\tile\entity - - - Source Files\world\item\crafting - - - Source Files\world\item\crafting - - - Source Files\client\gui\screens - - - Source Files\client\gui\components - - - Source Files\world\level\levelgen\feature - - - Source Files\world\inventory - - - Source Files\world\inventory - - - Source Files\client\gui\components - - - Source Files\world\level\tile\entity - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\inventory - - - Source Files\client\gui\screens - - - Source Files\client\renderer - - - Source Files\client\renderer\tileentity - - - Source Files\client\renderer\tileentity - - - Source Files\client\renderer\tileentity - - - Source Files\world\inventory - - - Source Files\client\renderer\entity - - - Source Files\world\entity - - - Source Files\world\entity\monster - - - Source Files\world\entity\monster - - - Source Files\world\entity\monster - - - Source Files\world\entity\monster - - - Source Files\world\level\tile - - - Source Files\world\item - - - Source Files\world\level\storage - - - Source Files\world\level\storage - - - Source Files\client\renderer\entity - - - Source Files\world\food - - - Source Files\client\player - - - Source Files\server - - - Source Files\server - - - Source Files\world\level\tile - - - Source Files\world\item - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files - - - Source Files\client\gamemode - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\world\item - - - Source Files\client\renderer\entity - - - Source Files\world\level\tile - - - Source Files\world\entity\monster - - - Source Files\world\level\tile\entity - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile\entity - - - Source Files\client\renderer\tileentity - - - Source Files\client\gui\screens - - - Source Files\world\level\tile - - - Source Files\network - - - Source Files\network\command - - - Source Files\network - - - Source Files\client\gamemode\creator - - - Source Files\client\gui\components - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens\crafting - - - Source Files\client\gui\screens\crafting - - - Source Files\client\renderer\entity - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\world\entity - - - Source Files\client - - - Source Files\client\renderer\entity - - - Source Files\world\level\tile - - - Source Files\world\item - - - Source Files\world\item\crafting - - - Source Files\world\level\tile - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens\crafting - - - Source Files\client\gui\screens\crafting - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files - - - Source Files\client\gui\components - - - Source Files\client - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files\client - - - Source Files\client - - - Source Files\client - - - Source Files\client - - - Source Files\client\gamemode - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\client\player - - - Platform - - - Source Files\util - - - Source Files\util - - - Source Files\util - - - Source Files\world - - - Source Files\world - - - Source Files\world - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\world\entity\item - - - Source Files\world\entity\player - - - Source Files\world\entity\player - - - Source Files\world\entity\player - - - Source Files\world\phys - - - Source Files\world\phys - - - Source Files\world\phys - - - Source Files\world\level - - - Source Files\world\level - - - Source Files\world\level - - - Source Files\world\level - - - Source Files\world\level\chunk - - - Source Files\world\level\chunk - - - Source Files\world\level\chunk - - - Source Files\world\level\chunk - - - Source Files\world\level\chunk - - - Source Files\world\level\chunk\storage - - - Source Files\world\level\chunk\storage - - - Source Files\world\level\dimension - - - Source Files\world\level\dimension - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen - - - Source Files\world\level\levelgen\synth - - - Source Files\world\level\levelgen\synth - - - Source Files\world\level\levelgen\synth - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\material - - - Source Files\world\level\material - - - Source Files\world\level\material - - - Source Files\world\level\material - - - Source Files\world\level\storage - - - Source Files\world\level\storage - - - Source Files\world\level\storage - - - Source Files\world\level\storage - - - Source Files\world\level\storage - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\world\level - - - Source Files\client\renderer\culling - - - Source Files\client\renderer\culling - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\world\level - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\client\renderer - - - Source Files\client - - - Source Files\world\entity - - - Source Files\world\level\pathfinder - - - Source Files\world\level\pathfinder - - - Source Files\world\level\tile - - - Source Files\world\level - - - Source Files\client - - - Source Files\client\renderer\culling - - - Source Files\client\renderer\culling - - - Source Files\client\renderer\culling - - - Platform\input - - - Platform\input - - - Platform\input - - - Source Files\world\level - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\locale - - - Source Files\world\item - - - Source Files\world\entity\item - - - Source Files\world\level\tile - - - Source Files\world\entity\item - - - Source Files\world\level - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\client\renderer - - - Platform - - - Source Files\client\gui - - - Source Files\client\gui - - - Source Files\world\level - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Libs\raknet - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\client\renderer\entity - - - Source Files\client\renderer\entity - - - Source Files\client\renderer\entity - - - Source Files\client\renderer\entity - - - Source Files\client\renderer\entity - - - Source Files\client\renderer\entity - - - Source Files\network - - - Source Files\network - - - Source Files\client\renderer - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\client\renderer - - - Source Files\client\sound - - - Source Files\client\sound - - - Source Files\client\sound - - - Platform\audio - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\client\gui - - - Source Files\client\gui - - - Platform\audio - - - Source Files\client\renderer\entity - - - Source Files\client\renderer - - - Source Files\world\level\biome - - - Source Files\world\level\biome - - - Source Files\world\level\biome - - - Source Files\world\level\biome - - - Source Files\world\level\biome - - - Source Files\world\level\biome - - - Source Files\world\level\biome - - - Source Files\world\level\biome - - - Source Files\world\level\levelgen\feature - - - Source Files\world\level\levelgen\feature - - - Platform - - - Source Files\world\level\storage - - - Source Files\world\level\storage - - - Source Files\world\level\storage - - - Source Files - - - Source Files - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\client\renderer\ptexture - - - Source Files\world\item - - - Source Files - - - Source Files\client\renderer\entity - - - Source Files\world\item - - - Source Files\world\entity\item - - - Source Files\client\renderer - - - Source Files - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Platform - - - Source Files\client\gui\screens - - - Source Files\util - - - Source Files\client\gui\screens - - - Source Files\world\level\storage - - - Source Files\world\level\storage - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\player\input - - - Source Files\client\player\input - - - Source Files\client\player\input - - - Source Files\client\gui\screens - - - Source Files - - - Source Files\client\gui\screens - - - Platform\input - - - Source Files\client\player\input\touchscreen - - - Source Files\client\player\input\touchscreen - - - Source Files\client\player\input\touchscreen - - - Source Files\client\player\input - - - Source Files\client\player\input - - - Source Files\client\player\input - - - Source Files\client\player\input - - - Source Files\client\player\input\touchscreen - - - Source Files\client\player\input - - - Source Files\client\player\input\touchscreen - - - Source Files\client\gui\screens\touch - - - Source Files\client\gui\components - - - Source Files\client\gui\screens\touch - - - Source Files\client\gui - - - Source Files\client\gui\screens\touch - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client\gui\screens - - - Source Files\client\gui\components - - - Source Files\client\gui\screens\touch - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\nbt - - - Source Files\util - - - Source Files\util - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\entity\animal - - - Source Files\world\entity\animal - - - Source Files\world\entity\animal - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\world\level - - - Source Files\client\renderer\entity - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\client\model\geom - - - Source Files\client\model\geom - - - Source Files\client\model\geom - - - Source Files\client\model\geom - - - Source Files\world\entity\animal - - - Source Files\world\entity\animal - - - Source Files\world\entity\animal - - - Source Files\world\level\pathfinder - - - Source Files\world\level\pathfinder - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\client\model - - - Source Files - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\util - - - Source Files\util - - - Source Files\util - - - Source Files\util - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\client\multiplayer - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\world\entity\monster - - - Source Files\world\entity\monster - - - Source Files\world\entity\monster - - - Source Files\world\entity\monster - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\client\renderer\entity - - - Source Files\world\item\crafting - - - Source Files\world\item\crafting - - - Source Files\world\item\crafting - - - Source Files\world\item\crafting - - - Source Files\world\item\crafting - - - Source Files\world\item\crafting - - - Source Files\world\item - - - Source Files\world\entity\monster - - - Source Files\world\entity\monster - - - Source Files\world\entity\projectile - - - Source Files\client\particle - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\client\renderer\entity - - - Source Files\client\renderer\entity - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\client\gui\screens - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\item - - - Source Files\world\entity\animal - - - Source Files\client\model - - - Source Files\client\model - - - Source Files\client\renderer\entity - - - Source Files\world\item - - - Source Files\client\gamemode - - - Source Files\client\gamemode - - - Source Files\util - - - Source Files\world\level - - - Source Files\world\entity - - - Source Files\network\packet - - - Source Files\client\gui\screens - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\util - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\client\model - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\world\entity\player - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\world\entity\ai - - - Source Files\world\entity\ai\control - - - Source Files\world\entity\ai\control - - - Source Files\world\entity\ai\control - - - Source Files\world\entity\ai\goal - - - Source Files\world\entity\ai\goal - - - Source Files\world\entity\ai\goal - - - Source Files\world\entity\ai\goal - - - Source Files\world\entity\ai\goal\target - - - Source Files\world\entity\ai\goal\target - - - Source Files\world\entity\ai\util - - - Source Files\world\entity\ai\goal\target - - - Source Files\world\entity\ai\goal - - - Source Files\world\entity\ai\goal - - - Source Files\world\item\crafting - - - Source Files\world\item\crafting - - - Source Files\world\item - - - Source Files\world\item - - - Libs\raknet - - - Source Files\client\gui\components - - - Source Files\network\packet - - - Source Files\world\level\tile - - - Source Files\world\item - - - Source Files\client\gui\components - - - Source Files\world\item\crafting - - - Source Files\network\packet - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile\entity - - - Source Files\world\level\tile\entity - - - Source Files\world\item\crafting - - - Source Files\world\item\crafting - - - Source Files\world\level\tile - - - Source Files\client\gui\screens - - - Source Files\client\gui\components - - - Source Files\world\level - - - Source Files\world\level\tile - - - Source Files\world\item - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\client\gui\screens - - - Source Files\network\packet - - - Source Files\world\inventory - - - Source Files\world\inventory - - - Source Files\client\gui\components - - - Source Files\world\level\tile\entity - - - Source Files\world\level\tile - - - Source Files\client\model - - - Source Files\client\renderer - - - Source Files\network\packet - - - Source Files\world\inventory - - - Source Files\client\gui\screens - - - Source Files\network\packet - - - Source Files\client\renderer\tileentity - - - Source Files\client\renderer\tileentity - - - Source Files\client\renderer\tileentity - - - Source Files\world\inventory - - - Source Files - - - Source Files\world\item - - - Source Files\world\entity - - - Source Files\client\renderer\entity - - - Source Files\world\item - - - Source Files\network\packet - - - Source Files\world\entity - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\item - - - Source Files\client\renderer\entity - - - Source Files\world\food - - - Source Files\network\packet - - - Source Files\world\food - - - Source Files\world - - - Source Files\world - - - Source Files\client\player - - - Source Files\client\particle - - - Source Files\server - - - Source Files\server - - - Source Files\world\level\tile - - - Source Files\world\item - - - Source Files\world\item - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\entity\projectile - - - Source Files\world\entity\projectile - - - Source Files\world\entity\projectile - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\world\entity - - - Source Files\world\item - - - Source Files\client\renderer\entity - - - Source Files\network\packet - - - Source Files\world\level\tile - - - Source Files\world\entity\monster - - - Source Files\world\level\tile\entity - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\world\level\tile\entity - - - Source Files\world\item - - - Source Files\world\level\tile - - - Source Files\client\model - - - Source Files\client\renderer\tileentity - - - Source Files\network\packet - - - Source Files\client\gui\screens - - - Source Files\network\packet - - - Source Files\world\level\tile - - - Source Files\world\level\material - - - Source Files\network\packet - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\network - - - Source Files\network\command - - - Source Files\network - - - Source Files\client\gamemode\creator - - - Source Files\client\gui\components - - - Source Files\world - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens\crafting - - - Source Files\client\gui\screens\crafting - - - Source Files\world\entity - - - Source Files\client\renderer\entity - - - Source Files\client\particle - - - Source Files\client\particle - - - Source Files\world\level\tile - - - Source Files\world\item - - - Source Files\world\item\crafting - - - Source Files\world\level\tile - - - Source Files\network\packet - - - Source Files\network\packet - - - Source Files\world\level\tile - - - Source Files\world\level\tile - - - Source Files\client\gui\screens - - - Source Files\client\gui\screens\crafting - - - Source Files\client\gui\screens\crafting - - - Source Files\world\level\tile - - - Source Files\network\packet - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - - Source Files\client\gui\components - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {315d8115-3fca-4a3f-9374-57f41665e3a0} + + + {84771a0f-b6af-479c-a26d-fbf611d14fcb} + + + {93f057aa-83e6-442e-a126-9d244453eb38} + + + {82f6d408-1efb-43bc-99fd-f6029eabe32b} + + + {01996801-7e5f-476e-b41c-e7d3bad443b0} + + + {4398bfb3-fb0a-498c-9772-3c815e8ad360} + + + {7887f605-f5e2-4bca-86b9-ccaf7228184d} + + + {9b2956dc-ea58-4192-8bb8-3a7c97e92141} + + + {c13ce830-5bad-4622-b008-4198a50fd760} + + + {d0112033-5536-42af-9a19-d188bb7b4a29} + + + {ee8fd90c-82e9-4f37-b468-fa264e20843d} + + + {cbcb2ae1-8eee-4d76-b137-e68c8edc55c0} + + + {81419bca-bcb4-46bd-bfd5-d05ec83adece} + + + {5094f961-132c-4d23-9077-353cf989adc5} + + + {71ab63f9-7c1d-4cdc-bbe1-67a69e54fcf9} + + + {e9e550bf-9345-4cb4-bd3d-a6d0f70136f6} + + + {94682265-c50a-4307-84b3-a2e2488f56aa} + + + {57e82abe-b138-40da-be47-75c872682126} + + + {3053195e-e3a9-4481-813f-2c63c7d4fc3b} + + + {4f6589cb-0d2a-4a0d-b94f-a15cc5af30db} + + + {711b9f4f-6a59-4c97-a37d-50f5e6171b08} + + + {7f18df66-372a-4d60-9e34-ddaed7b522e3} + + + {eed0a973-fbec-4dd6-96ef-308b80850ab1} + + + {ef4ebf01-6f5f-4b01-990d-feb9885d9f11} + + + {3c1f4573-74cc-4b76-b4ce-fc01f1e9ff6f} + + + {2360ae39-413c-4514-8898-fbed347600bd} + + + {e9f52cee-84ea-43b1-814d-822cb70805fa} + + + {e536d920-0d4b-4c39-97de-2b83a6ca3cf4} + + + {7c33753b-7228-4c43-a0db-e50621e60be8} + + + {00188837-a1b6-446f-94aa-c7cbd7da8291} + + + {bdda49a8-dce0-4e2c-bd52-a08949611547} + + + {fe0157fa-e486-47be-9a78-ea4abe58a75a} + + + {3f7b68ba-24bf-4a04-a072-3c63d061c6d3} + + + {7abec229-7c62-4f8c-be0c-b0541ebe6a2b} + + + {9a993a4b-5523-4ce6-8fc5-d45b6c098655} + + + {5db94373-ca02-4a85-8029-da71c6bb7b9c} + + + {db6877ab-b3ee-4c4e-a4db-a934d366ccf2} + + + {55eb3700-6e83-4ddd-acf1-8007be34240c} + + + {d75d193d-81f9-4fbd-8c1e-0688149b829b} + + + {64ffbf67-3499-4004-8bb1-124d6ef1c10c} + + + {0f4d45e8-d068-489e-a66e-d42d36067fbd} + + + {e2511587-18cc-4635-9921-513debf878df} + + + {99bc510f-d187-40f7-befc-1c5a02571110} + + + {76b518bc-f17f-4bf5-bd69-15c3945d8d98} + + + {89c4918c-8ff9-4290-81c8-e6115afb6e0a} + + + {8faf4949-1190-46ba-97ba-8d854cb8f104} + + + {491d98ea-686d-417c-9e42-ed3a4e66781d} + + + {d400197c-231e-4e27-a176-9e91bcc92934} + + + {d0aee588-4f58-44f9-8a82-0d1998cba02f} + + + {9b9a6b73-102d-40b8-bd32-cb552c9f83e4} + + + {85c02cb0-6cdf-4ec8-9d94-92b7c7b82e0c} + + + {1a46cf05-e5e8-4b61-af27-291435577b37} + + + {57b58ea4-2f23-4f79-ac82-6c40619f3ded} + + + {cab0cd1e-1e3c-4f6b-af63-a8b90c20e86e} + + + {fe32771a-8f08-471c-8696-28951441b3a2} + + + {7c895144-ec49-4465-be5c-e59960757bb3} + + + {3860d844-27c3-441e-adfa-e5993010a24a} + + + {8d46e789-d2ac-4888-adc7-499c25659dd8} + + + {d73bff72-4294-4b54-acf1-e4da7c159895} + + + {908ab587-566a-4177-94a0-67e66c25fac1} + + + {633779f8-e638-42a9-a5e4-98c3c8d810b0} + + + {160d7e15-8f93-46c7-8aec-a13299e9d090} + + + {d9a5faa2-fae5-4072-950e-89ca5a5f86fd} + + + + + Source Files + + + Source Files\client + + + Source Files\client + + + Source Files\client\gamemode + + + Source Files\client\player + + + Platform + + + Source Files\util + + + Source Files\world + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\world\phys + + + Source Files\world\level + + + Source Files\world\level + + + Source Files\world\level\chunk + + + Source Files\world\level\dimension + + + Source Files\world\level\levelgen + + + Source Files\world\level\material + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\world\level + + + Source Files\client\renderer\culling + + + Source Files\client\renderer + + + Source Files\world\level + + + Source Files\world\entity\player + + + Platform\input + + + Platform\input + + + Platform\input + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\level + + + Source Files\client\renderer + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\renderer + + + Platform + + + Source Files\client\gui + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Source Files\client\renderer\entity + + + Source Files\client\renderer\entity + + + Source Files\network + + + Source Files\network + + + Source Files\network + + + Source Files\network + + + Source Files\client\sound + + + Source Files\client\renderer\entity + + + Source Files\world\level\biome + + + Source Files\world\level\biome + + + Source Files\client\renderer\entity + + + Source Files\client\renderer\entity + + + Source Files + + + Source Files\world\level\storage + + + Source Files\world\level\storage + + + Source Files + + + Source Files\world\level\tile + + + Source Files\client\renderer\ptexture + + + Source Files\client\renderer\entity + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\world\level + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen\synth + + + Source Files\world\level\levelgen\synth + + + Source Files\world\level\levelgen\synth + + + Source Files\world\entity\item + + + Source Files\world\entity\item + + + Source Files\world\entity\item + + + Source Files\world\entity\item + + + Source Files\client\renderer\entity + + + Source Files\client\gui + + + Source Files\client\gui + + + Source Files\client\gui + + + Source Files\client\renderer\entity + + + Source Files\client\model + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\util + + + Source Files\client\gui\screens + + + Source Files\world\level\storage + + + Source Files\client\gui\screens + + + Source Files\client\player\input + + + Platform\input + + + Source Files\client\player\input\touchscreen + + + Source Files\client\gui\screens\touch + + + Source Files\client\gui\components + + + Source Files + + + Source Files\client\gui\screens\touch + + + Source Files\client\gui\screens\touch + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client\gui\screens + + + Source Files\client\gui\components + + + Source Files\client\gui\screens\touch + + + Source Files\client + + + Source Files\nbt + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\entity + + + Source Files\world\level + + + Source Files\client\model\geom + + + Source Files\client\model\geom + + + Source Files\client\model\geom + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\world\entity\animal + + + Source Files\world\entity\animal + + + Source Files\world\entity\animal + + + Source Files\world\entity\animal + + + Source Files\world\entity\animal + + + Source Files\util + + + Source Files\util + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\world\entity\monster + + + Source Files\network + + + Source Files\client\renderer\entity + + + Source Files\world\item\crafting + + + Source Files\world\item\crafting + + + Source Files\world\entity\projectile + + + Source Files\world\item\crafting + + + Source Files\world\entity\animal + + + Source Files\client\gamemode + + + Source Files\util + + + Source Files\world\entity + + + Source Files\client\gui\screens + + + Source Files\world\entity\player + + + Source Files\world\item + + + Source Files\world\level\storage + + + Source Files\world\entity\ai\control + + + Source Files\client + + + Source Files + + + Source Files\world\level\pathfinder + + + Source Files\world\item\crafting + + + Source Files\locale + + + Libs\raknet + + + Source Files\client\gui\components + + + Source Files\world\item\crafting + + + Source Files\client\sound + + + Source Files\world\level\tile + + + Source Files\world\level\tile\entity + + + Source Files\world\level\tile\entity + + + Source Files\world\item\crafting + + + Source Files\world\item\crafting + + + Source Files\client\gui\screens + + + Source Files\client\gui\components + + + Source Files\world\level\levelgen\feature + + + Source Files\world\inventory + + + Source Files\world\inventory + + + Source Files\client\gui\components + + + Source Files\world\level\tile\entity + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\inventory + + + Source Files\client\gui\screens + + + Source Files\client\renderer + + + Source Files\client\renderer\tileentity + + + Source Files\client\renderer\tileentity + + + Source Files\client\renderer\tileentity + + + Source Files\world\inventory + + + Source Files\client\renderer\entity + + + Source Files\world\entity + + + Source Files\world\entity\monster + + + Source Files\world\entity\monster + + + Source Files\world\entity\monster + + + Source Files\world\entity\monster + + + Source Files\world\level\tile + + + Source Files\world\item + + + Source Files\world\level\storage + + + Source Files\world\level\storage + + + Source Files\client\renderer\entity + + + Source Files\world\food + + + Source Files\client\player + + + Source Files\server + + + Source Files\server + + + Source Files\world\level\tile + + + Source Files\world\item + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files + + + Source Files\client\gamemode + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\world\item + + + Source Files\client\renderer\entity + + + Source Files\world\level\tile + + + Source Files\world\entity\monster + + + Source Files\world\level\tile\entity + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile\entity + + + Source Files\client\renderer\tileentity + + + Source Files\client\gui\screens + + + Source Files\world\level\tile + + + Source Files\network + + + Source Files\network\command + + + Source Files\network + + + Source Files\client\gamemode\creator + + + Source Files\client\gui\components + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens\crafting + + + Source Files\client\gui\screens\crafting + + + Source Files\client\renderer\entity + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\world\entity + + + Source Files\client + + + Source Files\client\renderer\entity + + + Source Files\world\level\tile + + + Source Files\world\item + + + Source Files\world\item\crafting + + + Source Files\world\level\tile + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens\crafting + + + Source Files\client\gui\screens\crafting + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files + + + Source Files\client\gui\components + + + Source Files\client + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\client + + + Source Files\client + + + Source Files\client + + + Source Files\client + + + Source Files\client\gamemode + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\client\player + + + Platform + + + Source Files\util + + + Source Files\util + + + Source Files\util + + + Source Files\world + + + Source Files\world + + + Source Files\world + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\world\entity\item + + + Source Files\world\entity\player + + + Source Files\world\entity\player + + + Source Files\world\entity\player + + + Source Files\world\phys + + + Source Files\world\phys + + + Source Files\world\phys + + + Source Files\world\level + + + Source Files\world\level + + + Source Files\world\level + + + Source Files\world\level + + + Source Files\world\level\chunk + + + Source Files\world\level\chunk + + + Source Files\world\level\chunk + + + Source Files\world\level\chunk + + + Source Files\world\level\chunk + + + Source Files\world\level\chunk\storage + + + Source Files\world\level\chunk\storage + + + Source Files\world\level\dimension + + + Source Files\world\level\dimension + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen + + + Source Files\world\level\levelgen\synth + + + Source Files\world\level\levelgen\synth + + + Source Files\world\level\levelgen\synth + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\material + + + Source Files\world\level\material + + + Source Files\world\level\material + + + Source Files\world\level\material + + + Source Files\world\level\storage + + + Source Files\world\level\storage + + + Source Files\world\level\storage + + + Source Files\world\level\storage + + + Source Files\world\level\storage + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\world\level + + + Source Files\client\renderer\culling + + + Source Files\client\renderer\culling + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\world\level + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\client\renderer + + + Source Files\client + + + Source Files\world\entity + + + Source Files\world\level\pathfinder + + + Source Files\world\level\pathfinder + + + Source Files\world\level\tile + + + Source Files\world\level + + + Source Files\client + + + Source Files\client\renderer\culling + + + Source Files\client\renderer\culling + + + Source Files\client\renderer\culling + + + Platform\input + + + Platform\input + + + Platform\input + + + Source Files\world\level + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\locale + + + Source Files\world\item + + + Source Files\world\entity\item + + + Source Files\world\level\tile + + + Source Files\world\entity\item + + + Source Files\world\level + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\client\renderer + + + Platform + + + Source Files\client\gui + + + Source Files\client\gui + + + Source Files\world\level + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Libs\raknet + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\client\renderer\entity + + + Source Files\client\renderer\entity + + + Source Files\client\renderer\entity + + + Source Files\client\renderer\entity + + + Source Files\client\renderer\entity + + + Source Files\client\renderer\entity + + + Source Files\network + + + Source Files\network + + + Source Files\client\renderer + + + Source Files\network + + + Source Files\network + + + Source Files\network + + + Source Files\client\renderer + + + Source Files\client\sound + + + Source Files\client\sound + + + Source Files\client\sound + + + Platform\audio + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\client\gui + + + Source Files\client\gui + + + Platform\audio + + + Source Files\client\renderer\entity + + + Source Files\client\renderer + + + Source Files\world\level\biome + + + Source Files\world\level\biome + + + Source Files\world\level\biome + + + Source Files\world\level\biome + + + Source Files\world\level\biome + + + Source Files\world\level\biome + + + Source Files\world\level\biome + + + Source Files\world\level\biome + + + Source Files\world\level\levelgen\feature + + + Source Files\world\level\levelgen\feature + + + Platform + + + Source Files\world\level\storage + + + Source Files\world\level\storage + + + Source Files\world\level\storage + + + Source Files + + + Source Files + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\client\renderer\ptexture + + + Source Files\world\item + + + Source Files + + + Source Files\client\renderer\entity + + + Source Files\world\item + + + Source Files\world\entity\item + + + Source Files\client\renderer + + + Source Files + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Platform + + + Source Files\client\gui\screens + + + Source Files\util + + + Source Files\client\gui\screens + + + Source Files\world\level\storage + + + Source Files\world\level\storage + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\player\input + + + Source Files\client\player\input + + + Source Files\client\player\input + + + Source Files\client\gui\screens + + + Source Files + + + Source Files\client\gui\screens + + + Platform\input + + + Source Files\client\player\input\touchscreen + + + Source Files\client\player\input\touchscreen + + + Source Files\client\player\input\touchscreen + + + Source Files\client\player\input + + + Source Files\client\player\input + + + Source Files\client\player\input + + + Source Files\client\player\input + + + Source Files\client\player\input\touchscreen + + + Source Files\client\player\input + + + Source Files\client\player\input\touchscreen + + + Source Files\client\gui\screens\touch + + + Source Files\client\gui\components + + + Source Files\client\gui\screens\touch + + + Source Files\client\gui + + + Source Files\client\gui\screens\touch + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client\gui\screens + + + Source Files\client\gui\components + + + Source Files\client\gui\screens\touch + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\nbt + + + Source Files\util + + + Source Files\util + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\entity\animal + + + Source Files\world\entity\animal + + + Source Files\world\entity\animal + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\world\level + + + Source Files\client\renderer\entity + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\client\model\geom + + + Source Files\client\model\geom + + + Source Files\client\model\geom + + + Source Files\client\model\geom + + + Source Files\world\entity\animal + + + Source Files\world\entity\animal + + + Source Files\world\entity\animal + + + Source Files\world\level\pathfinder + + + Source Files\world\level\pathfinder + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\client\model + + + Source Files + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\util + + + Source Files\util + + + Source Files\util + + + Source Files\util + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\client\multiplayer + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\world\entity\monster + + + Source Files\world\entity\monster + + + Source Files\world\entity\monster + + + Source Files\world\entity\monster + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\client\renderer\entity + + + Source Files\world\item\crafting + + + Source Files\world\item\crafting + + + Source Files\world\item\crafting + + + Source Files\world\item\crafting + + + Source Files\world\item\crafting + + + Source Files\world\item\crafting + + + Source Files\world\item + + + Source Files\world\entity\monster + + + Source Files\world\entity\monster + + + Source Files\world\entity\projectile + + + Source Files\client\particle + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\client\renderer\entity + + + Source Files\client\renderer\entity + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\client\gui\screens + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\item + + + Source Files\world\entity\animal + + + Source Files\client\model + + + Source Files\client\model + + + Source Files\client\renderer\entity + + + Source Files\world\item + + + Source Files\client\gamemode + + + Source Files\client\gamemode + + + Source Files\util + + + Source Files\world\level + + + Source Files\world\entity + + + Source Files\network\packet + + + Source Files\client\gui\screens + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\util + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\client\model + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\world\entity\player + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\world\entity\ai + + + Source Files\world\entity\ai\control + + + Source Files\world\entity\ai\control + + + Source Files\world\entity\ai\control + + + Source Files\world\entity\ai\goal + + + Source Files\world\entity\ai\goal + + + Source Files\world\entity\ai\goal + + + Source Files\world\entity\ai\goal + + + Source Files\world\entity\ai\goal\target + + + Source Files\world\entity\ai\goal\target + + + Source Files\world\entity\ai\util + + + Source Files\world\entity\ai\goal\target + + + Source Files\world\entity\ai\goal + + + Source Files\world\entity\ai\goal + + + Source Files\world\item\crafting + + + Source Files\world\item\crafting + + + Source Files\world\item + + + Source Files\world\item + + + Libs\raknet + + + Source Files\client\gui\components + + + Source Files\network\packet + + + Source Files\world\level\tile + + + Source Files\world\item + + + Source Files\client\gui\components + + + Source Files\world\item\crafting + + + Source Files\network\packet + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile\entity + + + Source Files\world\level\tile\entity + + + Source Files\world\item\crafting + + + Source Files\world\item\crafting + + + Source Files\world\level\tile + + + Source Files\client\gui\screens + + + Source Files\client\gui\components + + + Source Files\world\level + + + Source Files\world\level\tile + + + Source Files\world\item + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\client\gui\screens + + + Source Files\network\packet + + + Source Files\world\inventory + + + Source Files\world\inventory + + + Source Files\client\gui\components + + + Source Files\world\level\tile\entity + + + Source Files\world\level\tile + + + Source Files\client\model + + + Source Files\client\renderer + + + Source Files\network\packet + + + Source Files\world\inventory + + + Source Files\client\gui\screens + + + Source Files\network\packet + + + Source Files\client\renderer\tileentity + + + Source Files\client\renderer\tileentity + + + Source Files\client\renderer\tileentity + + + Source Files\world\inventory + + + Source Files + + + Source Files\world\item + + + Source Files\world\entity + + + Source Files\client\renderer\entity + + + Source Files\world\item + + + Source Files\network\packet + + + Source Files\world\entity + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\item + + + Source Files\client\renderer\entity + + + Source Files\world\food + + + Source Files\network\packet + + + Source Files\world\food + + + Source Files\world + + + Source Files\world + + + Source Files\client\player + + + Source Files\client\particle + + + Source Files\server + + + Source Files\server + + + Source Files\world\level\tile + + + Source Files\world\item + + + Source Files\world\item + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\entity\projectile + + + Source Files\world\entity\projectile + + + Source Files\world\entity\projectile + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\world\entity + + + Source Files\world\item + + + Source Files\client\renderer\entity + + + Source Files\network\packet + + + Source Files\world\level\tile + + + Source Files\world\entity\monster + + + Source Files\world\level\tile\entity + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\world\level\tile\entity + + + Source Files\world\item + + + Source Files\world\level\tile + + + Source Files\client\model + + + Source Files\client\renderer\tileentity + + + Source Files\network\packet + + + Source Files\client\gui\screens + + + Source Files\network\packet + + + Source Files\world\level\tile + + + Source Files\world\level\material + + + Source Files\network\packet + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\network + + + Source Files\network\command + + + Source Files\network + + + Source Files\client\gamemode\creator + + + Source Files\client\gui\components + + + Source Files\world + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens\crafting + + + Source Files\client\gui\screens\crafting + + + Source Files\world\entity + + + Source Files\client\renderer\entity + + + Source Files\client\particle + + + Source Files\client\particle + + + Source Files\world\level\tile + + + Source Files\world\item + + + Source Files\world\item\crafting + + + Source Files\world\level\tile + + + Source Files\network\packet + + + Source Files\network\packet + + + Source Files\world\level\tile + + + Source Files\world\level\tile + + + Source Files\client\gui\screens + + + Source Files\client\gui\screens\crafting + + + Source Files\client\gui\screens\crafting + + + Source Files\world\level\tile + + + Source Files\network\packet + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + + Source Files\client\gui\components + + \ No newline at end of file diff --git a/project/win32/MinecraftWin32.vcxproj.user b/project/win32/MinecraftWin32.vcxproj.user index f5e1823..3f03091 100755 --- a/project/win32/MinecraftWin32.vcxproj.user +++ b/project/win32/MinecraftWin32.vcxproj.user @@ -1,6 +1,6 @@ - - - - false - + + + + false + \ No newline at end of file diff --git a/project/win32_gl/MinecraftWin32_GL.sln b/project/win32_gl/MinecraftWin32_GL.sln index f9b310c..8e2e9ec 100755 --- a/project/win32_gl/MinecraftWin32_GL.sln +++ b/project/win32_gl/MinecraftWin32_GL.sln @@ -1,20 +1,20 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinecraftWin32_GL", "MinecraftWin32_GL.vcxproj", "{7DB16490-D454-6E18-6820-3219A119CD8E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7DB16490-D454-6E18-6820-3219A119CD8E}.Debug|Win32.ActiveCfg = Debug|Win32 - {7DB16490-D454-6E18-6820-3219A119CD8E}.Debug|Win32.Build.0 = Debug|Win32 - {7DB16490-D454-6E18-6820-3219A119CD8E}.Release|Win32.ActiveCfg = Release|Win32 - {7DB16490-D454-6E18-6820-3219A119CD8E}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinecraftWin32_GL", "MinecraftWin32_GL.vcxproj", "{7DB16490-D454-6E18-6820-3219A119CD8E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7DB16490-D454-6E18-6820-3219A119CD8E}.Debug|Win32.ActiveCfg = Debug|Win32 + {7DB16490-D454-6E18-6820-3219A119CD8E}.Debug|Win32.Build.0 = Debug|Win32 + {7DB16490-D454-6E18-6820-3219A119CD8E}.Release|Win32.ActiveCfg = Release|Win32 + {7DB16490-D454-6E18-6820-3219A119CD8E}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/project/win32_gl/MinecraftWin32_GL.vcxproj b/project/win32_gl/MinecraftWin32_GL.vcxproj index 1e8a3c7..2af6335 100755 --- a/project/win32_gl/MinecraftWin32_GL.vcxproj +++ b/project/win32_gl/MinecraftWin32_GL.vcxproj @@ -1,525 +1,525 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - Win32Proj - {19B34E47-B11C-E628-ED97-698E93339EA3} - - - - Application - true - NotSet - v110 - - - Application - false - NotSet - v110 - - - - - - - - - - - - - true - ..\..\lib\include;$(IncludePath) - ..\..\lib\lib;$(LibraryPath) - - - true - ..\..\lib\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; - ..\..\lib\lib;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib - ExtendedCorrectnessRules.ruleset - - - - WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - Level3 - ProgramDatabase - Disabled - true - false - - - MachineX86 - true - Console - ws2_32.lib;libegl.lib;glew32.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) - - - - - WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDLL - Level3 - ProgramDatabase - true - false - - - MachineX86 - true - Console - true - true - ws2_32.lib;libegl.lib;glew32.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + Win32Proj + {19B34E47-B11C-E628-ED97-698E93339EA3} + + + + Application + true + NotSet + v110 + + + Application + false + NotSet + v110 + + + + + + + + + + + + + true + ..\..\lib\include;$(IncludePath) + ..\..\lib\lib;$(LibraryPath) + + + true + ..\..\lib\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; + ..\..\lib\lib;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib + ExtendedCorrectnessRules.ruleset + + + + WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Disabled + true + false + + + MachineX86 + true + Console + ws2_32.lib;libegl.lib;glew32.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) + + + + + WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + true + false + + + MachineX86 + true + Console + true + true + ws2_32.lib;libegl.lib;glew32.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libpng.lib;%(AdditionalDependencies) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/project/win32_gl/MinecraftWin32_GL.vcxproj.filters b/project/win32_gl/MinecraftWin32_GL.vcxproj.filters index bf08185..b4519a2 100755 --- a/project/win32_gl/MinecraftWin32_GL.vcxproj.filters +++ b/project/win32_gl/MinecraftWin32_GL.vcxproj.filters @@ -1,1329 +1,1329 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - {a641bbc8-1cce-483d-b0b8-76464a379015} - - - {7703ad7e-3c90-464d-a54e-d30cb1b7daa1} - - - {70a5efe2-2f9f-40bc-8663-c2e37b5767cf} - - - {d6713e84-c5a8-4169-a161-121d4b4de306} - - - {b9684a1f-db09-43fa-83be-903dcc316369} - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files\raknet - - - Source Files - - - Source Files - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files\world\level\storage - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + {a641bbc8-1cce-483d-b0b8-76464a379015} + + + {7703ad7e-3c90-464d-a54e-d30cb1b7daa1} + + + {70a5efe2-2f9f-40bc-8663-c2e37b5767cf} + + + {d6713e84-c5a8-4169-a161-121d4b4de306} + + + {b9684a1f-db09-43fa-83be-903dcc316369} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files\raknet + + + Source Files + + + Source Files + + + Source Files\network + + + Source Files\network + + + Source Files\network + + + Source Files\network + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\world\level\storage + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + \ No newline at end of file diff --git a/project/win32_gl/MinecraftWin32_GL.vcxproj.user b/project/win32_gl/MinecraftWin32_GL.vcxproj.user index 695b5c7..ace9a86 100755 --- a/project/win32_gl/MinecraftWin32_GL.vcxproj.user +++ b/project/win32_gl/MinecraftWin32_GL.vcxproj.user @@ -1,3 +1,3 @@ - - + + \ No newline at end of file diff --git a/src/App.h b/src/App.h index 3e5152e..5fb7d85 100755 --- a/src/App.h +++ b/src/App.h @@ -1,87 +1,87 @@ -#ifndef APP_H__ -#define APP_H__ - -#ifdef __APPLE__ -#define NO_EGL -#endif -#ifdef STANDALONE_SERVER -#define NO_EGL -#endif - -#include "AppPlatform.h" -#ifndef NO_EGL - #include -#endif -#include "platform/log.h" - -typedef struct AppContext { -#ifndef NO_EGL - EGLDisplay display; - EGLContext context; - EGLSurface surface; -#endif - AppPlatform* platform; - bool doRender; -} AppContext; - - -class App -{ -public: - App() - : _finished(false), - _inited(false) - { - _context.platform = 0; - } - virtual ~App() {} - - void init(AppContext& c) { - _context = c; - init(); - _inited = true; - } - bool isInited() { return _inited; } - - virtual AppPlatform* platform() { return _context.platform; } - - void onGraphicsReset(AppContext& c) { - _context = c; - onGraphicsReset(); - } - - virtual void audioEngineOn () {} - virtual void audioEngineOff() {} - - virtual void destroy() {} - - virtual void loadState(void* state, int stateSize) {} - virtual bool saveState(void** state, int* stateSize) { return false; } - - void swapBuffers() { -#ifndef NO_EGL - if (_context.doRender) - eglSwapBuffers(_context.display, _context.surface); -#endif - } - - virtual void draw() {} - virtual void update() {};// = 0; - virtual void setSize(int width, int height) {} - - virtual void quit() { _finished = true; } - virtual bool wantToQuit() { return _finished; } - virtual bool handleBack(bool isDown) { return false; } - -protected: - virtual void init() {} - //virtual void onGraphicsLost() = 0; - virtual void onGraphicsReset() = 0; - -private: - bool _inited; - bool _finished; - AppContext _context; -}; - -#endif//APP_H__ +#ifndef APP_H__ +#define APP_H__ + +#ifdef __APPLE__ +#define NO_EGL +#endif +#ifdef STANDALONE_SERVER +#define NO_EGL +#endif + +#include "AppPlatform.h" +#ifndef NO_EGL + #include +#endif +#include "platform/log.h" + +typedef struct AppContext { +#ifndef NO_EGL + EGLDisplay display; + EGLContext context; + EGLSurface surface; +#endif + AppPlatform* platform; + bool doRender; +} AppContext; + + +class App +{ +public: + App() + : _finished(false), + _inited(false) + { + _context.platform = 0; + } + virtual ~App() {} + + void init(AppContext& c) { + _context = c; + init(); + _inited = true; + } + bool isInited() { return _inited; } + + virtual AppPlatform* platform() { return _context.platform; } + + void onGraphicsReset(AppContext& c) { + _context = c; + onGraphicsReset(); + } + + virtual void audioEngineOn () {} + virtual void audioEngineOff() {} + + virtual void destroy() {} + + virtual void loadState(void* state, int stateSize) {} + virtual bool saveState(void** state, int* stateSize) { return false; } + + void swapBuffers() { +#ifndef NO_EGL + if (_context.doRender) + eglSwapBuffers(_context.display, _context.surface); +#endif + } + + virtual void draw() {} + virtual void update() {};// = 0; + virtual void setSize(int width, int height) {} + + virtual void quit() { _finished = true; } + virtual bool wantToQuit() { return _finished; } + virtual bool handleBack(bool isDown) { return false; } + +protected: + virtual void init() {} + //virtual void onGraphicsLost() = 0; + virtual void onGraphicsReset() = 0; + +private: + bool _inited; + bool _finished; + AppContext _context; +}; + +#endif//APP_H__ diff --git a/src/AppConstants.h b/src/AppConstants.h index 3311284..092a68c 100755 --- a/src/AppConstants.h +++ b/src/AppConstants.h @@ -1,10 +1,10 @@ -#ifndef _MINECRAFT_APPCONSTANTS_H_ -#define _MINECRAFT_APPCONSTANTS_H_ - - -#define APP_VERSION_STRING "Demo" -#define APP_NAME "Minecraft - Pocket Edition " APP_VERSION_STRING - - - -#endif +#ifndef _MINECRAFT_APPCONSTANTS_H_ +#define _MINECRAFT_APPCONSTANTS_H_ + + +#define APP_VERSION_STRING "Demo" +#define APP_NAME "Minecraft - Pocket Edition " APP_VERSION_STRING + + + +#endif diff --git a/src/AppPlatform.h b/src/AppPlatform.h index d52118a..1a432a1 100755 --- a/src/AppPlatform.h +++ b/src/AppPlatform.h @@ -1,143 +1,143 @@ -#ifndef APPPLATFORM_H__ -#define APPPLATFORM_H__ - -#include -#include -#include -#include "client/renderer/TextureData.h" - -typedef std::vector StringVector; - -/* -typedef struct UserInput -{ - static const int STATUS_INVALID = -1; - static const int STATUS_NOTINITED = -2; - static const int STATUS_OK = 1; - static const int STATUS_CANCEL = 0; - - UserInput(int id) - : _id(id), - status(STATUS_NOTINITED) - {} - UserInput(int id, int status) - : _id(id), - status(status) - {} - int getId() { return _id; } - - int status; -private: - int _id; -} UserInput; - - -class UserInputStatus { - int _status; -public: - UserInputStatus(int status) - : _status(status) - {} - bool isAnswered() { return _status >= 0; } - bool isOk() { return _status == UserInput::STATUS_OK; } - bool isCancel() { return _status == UserInput::STATUS_CANCEL; } -}; -*/ - -class BinaryBlob { -public: - BinaryBlob() - : data(NULL), - size(-1) {} - - BinaryBlob(unsigned char* data, unsigned int size) - : data(data), - size(size) {} - - unsigned char* data; - int size; -}; - -class PlatformStringVars { -public: - static const int DEVICE_BUILD_MODEL = 0; -}; - -class AppPlatform -{ -public: - AppPlatform() : keyboardVisible(false) {} - virtual ~AppPlatform() {} - - virtual void saveScreenshot(const std::string& filename, int glWidth, int glHeight) {} - virtual TextureData loadTexture(const std::string& filename_, bool textureFolder) { return TextureData(); } - virtual TextureData loadTextureFromMemory(const unsigned char* data, size_t size) { return TextureData(); } - - virtual void playSound(const std::string& fn, float volume, float pitch) {} - - virtual void hideCursor(bool hide) {} - - virtual void showDialog(int dialogId) {} - virtual void createUserInput() {} - - bool is_big_endian(void) { - union { - unsigned int i; - char c[4]; - } bint = {0x01020304}; - return bint.c[0] == 1; - } - - void createUserInput(int dialogId) - { - showDialog(dialogId); - createUserInput(); - } - virtual int getUserInputStatus() { return 0; } - virtual StringVector getUserInput() { return StringVector(); } - - virtual std::string getDateString(int s) { return ""; } - //virtual void createUserInputScreen(const char* types) {} - - virtual void uploadPlatformDependentData(int id, void* data) {} - virtual BinaryBlob readAssetFile(const std::string& filename) { return BinaryBlob(); } - virtual void _tick() {} - - virtual int getScreenWidth() { return 854; } - virtual int getScreenHeight() { return 480; } - virtual float getPixelsPerMillimeter() { return 10; } - - virtual bool isNetworkEnabled(bool onlyWifiAllowed) { return true; } - - virtual bool isPowerVR() { - return false; - } - virtual int getKeyFromKeyCode(int keyCode, int metaState, int deviceId) {return 0;} -#ifdef __APPLE__ - virtual bool isSuperFast() = 0; -#endif - - virtual void openURL(const std::string& url) {} - - virtual void finish() {} - - virtual bool supportsTouchscreen() { return false; } - - virtual void vibrate(int milliSeconds) {} - - virtual std::string getPlatformStringVar(int stringId) { - return ""; - } - - virtual void showKeyboard() { - keyboardVisible = true; - } - virtual void hideKeyboard() { - keyboardVisible = false; - } - virtual bool isKeyboardVisible() {return keyboardVisible;} -protected: - bool keyboardVisible; -}; - -#endif /*APPPLATFORM_H__*/ +#ifndef APPPLATFORM_H__ +#define APPPLATFORM_H__ + +#include +#include +#include +#include "client/renderer/TextureData.h" + +typedef std::vector StringVector; + +/* +typedef struct UserInput +{ + static const int STATUS_INVALID = -1; + static const int STATUS_NOTINITED = -2; + static const int STATUS_OK = 1; + static const int STATUS_CANCEL = 0; + + UserInput(int id) + : _id(id), + status(STATUS_NOTINITED) + {} + UserInput(int id, int status) + : _id(id), + status(status) + {} + int getId() { return _id; } + + int status; +private: + int _id; +} UserInput; + + +class UserInputStatus { + int _status; +public: + UserInputStatus(int status) + : _status(status) + {} + bool isAnswered() { return _status >= 0; } + bool isOk() { return _status == UserInput::STATUS_OK; } + bool isCancel() { return _status == UserInput::STATUS_CANCEL; } +}; +*/ + +class BinaryBlob { +public: + BinaryBlob() + : data(NULL), + size(-1) {} + + BinaryBlob(unsigned char* data, unsigned int size) + : data(data), + size(size) {} + + unsigned char* data; + int size; +}; + +class PlatformStringVars { +public: + static const int DEVICE_BUILD_MODEL = 0; +}; + +class AppPlatform +{ +public: + AppPlatform() : keyboardVisible(false) {} + virtual ~AppPlatform() {} + + virtual void saveScreenshot(const std::string& filename, int glWidth, int glHeight) {} + virtual TextureData loadTexture(const std::string& filename_, bool textureFolder) { return TextureData(); } + virtual TextureData loadTextureFromMemory(const unsigned char* data, size_t size) { return TextureData(); } + + virtual void playSound(const std::string& fn, float volume, float pitch) {} + + virtual void hideCursor(bool hide) {} + + virtual void showDialog(int dialogId) {} + virtual void createUserInput() {} + + bool is_big_endian(void) { + union { + unsigned int i; + char c[4]; + } bint = {0x01020304}; + return bint.c[0] == 1; + } + + void createUserInput(int dialogId) + { + showDialog(dialogId); + createUserInput(); + } + virtual int getUserInputStatus() { return 0; } + virtual StringVector getUserInput() { return StringVector(); } + + virtual std::string getDateString(int s) { return ""; } + //virtual void createUserInputScreen(const char* types) {} + + virtual void uploadPlatformDependentData(int id, void* data) {} + virtual BinaryBlob readAssetFile(const std::string& filename) { return BinaryBlob(); } + virtual void _tick() {} + + virtual int getScreenWidth() { return 854; } + virtual int getScreenHeight() { return 480; } + virtual float getPixelsPerMillimeter() { return 10; } + + virtual bool isNetworkEnabled(bool onlyWifiAllowed) { return true; } + + virtual bool isPowerVR() { + return false; + } + virtual int getKeyFromKeyCode(int keyCode, int metaState, int deviceId) {return 0;} +#ifdef __APPLE__ + virtual bool isSuperFast() = 0; +#endif + + virtual void openURL(const std::string& url) {} + + virtual void finish() {} + + virtual bool supportsTouchscreen() { return false; } + + virtual void vibrate(int milliSeconds) {} + + virtual std::string getPlatformStringVar(int stringId) { + return ""; + } + + virtual void showKeyboard() { + keyboardVisible = true; + } + virtual void hideKeyboard() { + keyboardVisible = false; + } + virtual bool isKeyboardVisible() {return keyboardVisible;} +protected: + bool keyboardVisible; +}; + +#endif /*APPPLATFORM_H__*/ diff --git a/src/AppPlatform_android.h b/src/AppPlatform_android.h index 9f3a7cf..cfec11c 100755 --- a/src/AppPlatform_android.h +++ b/src/AppPlatform_android.h @@ -1,654 +1,654 @@ -#ifndef APPPLATFORM_ANDROID_H__ -#define APPPLATFORM_ANDROID_H__ - -#include "AppPlatform.h" -#include "client/renderer/gles.h" -#include "platform/log.h" -#include "platform/time.h" -#include -#include -#include -#include -class JVMAttacher { -public: - JVMAttacher(JavaVM* vm) - : _vm(vm), - _isAttached(false), - _env(NULL) - { - if( _vm->GetEnv((void**)&_env, JNI_VERSION_1_4 ) != JNI_OK ) { - _vm->AttachCurrentThread(&_env, NULL); - _isAttached = _env != NULL; - } - } - - ~JVMAttacher() { - forceDetach(); - } - - JNIEnv* getEnv() { - return _env; - } - - void forceDetach() { - if (!_isAttached) return; - _vm->DetachCurrentThread(); - _isAttached = false; - } - -private: - JavaVM* _vm; - JNIEnv* _env; - bool _isAttached; -}; - -class AppPlatform_android: public AppPlatform -{ - typedef AppPlatform super; -public: - AppPlatform_android() - : _vm(NULL), - _isInited(false), - - instance(0), - _activityClass(0), - - _methodSaveScreenshot(0), - _postScreenshotToFacebook(0), - _getImageData(0), - _readAssetFile(0), - _methodPlaySound(0), - _showDialog(0), - _methodTick(0), - _methodFinish(0), - - _methodUserInputInitiate(0), - _methodUserInputStatus(0), - _methodUserInputString(0), - - _methodGetDateString(0), - - _methodVibrate(0), - _methodSupportsTouchscreen(0), - _methodSetIsPowerVR(0), - _methodIsNetworkEnabled(0), - - _getScreenWidth(0), - _getScreenHeight(0), - _methodGetPixelsPerMillimeter(0), - _methodGetPlatformStringVar(0), - - _classWindow(0), - _classContext(0), - _fieldINPUT_METHOD_SERVICE(0), - _classInputManager(0), - _methodGetSystemService(0), - _methodGetWindow(0), - _methodGetDecorView(0), - _methodShowSoftInput(0), - _classView(0), - _methodGetWindowToken(0), - _methodHideSoftInput(0), - - _screenWidth(854), - _screenHeight(480), - _hasSetPowerVR(false), - _nativeActivity(0), - - _methodGetKeyFromKeyCode(0) - { - } - - int getScreenWidth() { return _screenWidth; } - int getScreenHeight() { return _screenHeight; } - void setScreenDimensions(int w, int h) { _screenWidth = w; _screenHeight = h; } - - // Note, this has to be called from the main thread (e.g. do it from JNI_onLoad) - // Somewhere between calling this, and calling the AppPlatform methods, - // this->instance will be assigned by its creator - int init(JavaVM* vm) { - if (_isInited) - return -1; - - JVMAttacher ta(vm); - JNIEnv* env = ta.getEnv(); - - if(!env) { - LOGI("%s - Failed to get the environment using JVMAttacher::getEnv()", __FUNCTION__); - return -2; - } - - // Get Class reference - const char* interface_path = "com/mojang/minecraftpe/MainActivity"; - jclass clazz = NULL; - if( (clazz = env->FindClass( interface_path )) == 0 ) { - return -3; - } - _activityClass = (jclass)env->NewGlobalRef(clazz); - - // Save all the method IDs - _methodSaveScreenshot = env->GetStaticMethodID( _activityClass, "saveScreenshot", "(Ljava/lang/String;II[I)V"); - _postScreenshotToFacebook = env->GetMethodID( _activityClass, "postScreenshotToFacebook", "(Ljava/lang/String;II[I)V"); - _getImageData = env->GetMethodID( _activityClass, "getImageData", "(Ljava/lang/String;)[I"); - _readAssetFile = env->GetMethodID( _activityClass, "getFileDataBytes", "(Ljava/lang/String;)[B"); - #if defined(PRE_ANDROID23) - _methodPlaySound = env->GetMethodID( _activityClass, "playSound", "(Ljava/lang/String;FF)V"); - #endif - _showDialog = env->GetMethodID( _activityClass, "displayDialog", "(I)V"); - _methodTick = env->GetMethodID( _activityClass, "tick", "()V"); - _methodFinish = env->GetMethodID( _activityClass, "quit", "()V"); - - _methodUserInputInitiate = env->GetMethodID( _activityClass, "initiateUserInput", "(I)V"); - _methodUserInputStatus = env->GetMethodID( _activityClass, "getUserInputStatus", "()I"); - _methodUserInputString = env->GetMethodID( _activityClass, "getUserInputString", "()[Ljava/lang/String;"); - - _methodGetDateString = env->GetMethodID( _activityClass, "getDateString", "(I)Ljava/lang/String;"); - - _methodVibrate = env->GetMethodID( _activityClass, "vibrate", "(I)V"); - _methodSupportsTouchscreen = env->GetMethodID( _activityClass, "supportsTouchscreen", "()Z"); - _methodSetIsPowerVR = env->GetMethodID( _activityClass, "setIsPowerVR", "(Z)V"); - _methodIsNetworkEnabled = env->GetMethodID( _activityClass, "isNetworkEnabled", "(Z)Z"); - - _methodGetPixelsPerMillimeter = env->GetMethodID( _activityClass, "getPixelsPerMillimeter", "()F"); - _methodGetPlatformStringVar = env->GetMethodID( _activityClass, "getPlatformStringVar", "(I)Ljava/lang/String;"); - // custom helper to launch external URLs - _methodOpenURL = env->GetMethodID(_activityClass, "openURL", "(Ljava/lang/String;)V"); - - _classWindow = (jclass)env->NewGlobalRef(env->FindClass("android/view/Window")); - _classContext = (jclass)env->NewGlobalRef(env->FindClass("android/content/Context")); - _classView = (jclass)env->NewGlobalRef(env->FindClass( "android/view/View")); - _classInputManager = (jclass)env->NewGlobalRef(env->FindClass("android/view/inputmethod/InputMethodManager")); - - _fieldINPUT_METHOD_SERVICE = env->GetStaticFieldID(_classContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;"); - _methodGetSystemService = env->GetMethodID(_activityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); - _methodGetWindow = env->GetMethodID( _activityClass, "getWindow", "()Landroid/view/Window;"); - _methodGetDecorView = env->GetMethodID(_classWindow, "getDecorView", "()Landroid/view/View;"); - _methodShowSoftInput = env->GetMethodID(_classInputManager, "showSoftInput", "(Landroid/view/View;I)Z"); - _methodGetWindowToken = env->GetMethodID(_classView, "getWindowToken", "()Landroid/os/IBinder;"); - _methodHideSoftInput = env->GetMethodID(_classInputManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"); - - _methodGetKeyFromKeyCode = env->GetMethodID( _activityClass, "getKeyFromKeyCode", "(III)I"); - - if (env->ExceptionOccurred()) { - env->ExceptionDescribe(); - } - - // LOGI("Class: %d\n. Methods: %d,%d\n", _activityClass, - // _methodSaveScreenshot, - // _postScreenshotToFacebook); - - _vm = vm; - _isInited = true; - - return JNI_VERSION_1_4; - } - - // Stuff that should only be written once, e.g. device specs - // @note: This is called after instance is set, BUT this will - // be rewritten later on anyway - int initConsts() { - - LOGI("initConsts: start\n"); - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - LOGI("initConsts: getting method IDs\n"); - jmethodID fWidth = env->GetMethodID( _activityClass, "getScreenWidth", "()I"); - LOGI("initConsts: got fWidth=%p\n", fWidth); - jmethodID fHeight = env->GetMethodID( _activityClass, "getScreenHeight", "()I"); - LOGI("initConsts: got fHeight=%p, calling getScreenWidth\n", fHeight); - - _screenWidth = env->CallIntMethod(instance, fWidth); - LOGI("initConsts: screenWidth=%d, calling getScreenHeight\n", _screenWidth); - _screenHeight = env->CallIntMethod(instance, fHeight); - LOGI("initConsts: screenHeight=%d, done\n", _screenHeight); - - return 1; - } - - void tick() { - if (!_isInited) return; - if (!_methodTick) return; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - env->CallVoidMethod(instance, _methodTick); - } - - void showDialog(int dialogId) { - if (!_isInited) return; - if (!_showDialog) return; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - env->CallVoidMethod(instance, _showDialog, dialogId); - } - - void createUserInput() { - if (!_isInited) return; - if (!_methodUserInputInitiate) return; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - env->CallVoidMethod(instance, _methodUserInputInitiate, 1); - } - - int getUserInputStatus() { - if (!_isInited) return -2; - if (!_methodUserInputStatus) return -2; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - return env->CallIntMethod(instance, _methodUserInputStatus); - } - - float getPixelsPerMillimeter() { - if (!_isInited) return 10; - if (!_methodGetPixelsPerMillimeter) return 10; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - return env->CallFloatMethod(instance, _methodGetPixelsPerMillimeter); - } - - StringVector getUserInput() { - if (!_isInited) return std::vector(); - if (!_methodUserInputString) return std::vector(); - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - jobjectArray sa = (jobjectArray) env->CallObjectMethod(instance, _methodUserInputString); - jsize len = env->GetArrayLength(sa); - - StringVector out; - for (int i = 0; i < len; ++i) { - - jstring s = (jstring)env->GetObjectArrayElement(sa, i); - int length = env->GetStringLength (s); - if (length > 1023) - length = 1023; - static char buf[1024]; - - // Only supporting 7 bit chars, right now - const char* str = env->GetStringUTFChars(s, 0); - strncpy(buf, str, 1023); - buf[1023] = 0; - - out.push_back(std::string(buf)); - } - return out; - } - - void saveScreenshot(const std::string& filename, int glWidth, int glHeight) { - if (!_isInited) return; - if (!_methodSaveScreenshot) return; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - const int numPixels = glWidth * glHeight; - unsigned int* pixels = new unsigned int[numPixels]; - - if (!pixels) - return; - - // Create the OpenGL "screenshot" - glReadPixels(0, 0, glWidth, glHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - convertOpenGlToAndroidPixels(glWidth, glHeight, pixels); - - // Tell Android to save the screenshot - jintArray jPixels = env->NewIntArray(numPixels); - if (jPixels != NULL) - { - jobject gpixRef = env->NewGlobalRef(jPixels); - env->SetIntArrayRegion(jPixels, 0, numPixels, (int*)pixels); -// env->CallVoidMethod( instance, -// _postScreenshotToFacebook, -// env->NewStringUTF(filename.c_str()), -// glWidth, glHeight, jPixels); - env->CallStaticVoidMethod( _activityClass, - _methodSaveScreenshot, - env->NewStringUTF(filename.c_str()), - glWidth, glHeight, jPixels); - // Teardown - env->DeleteGlobalRef(gpixRef); - delete[] pixels; - } - } - - __inline unsigned int rgbToBgr(unsigned int p) { - return (p & 0xff00ff00) | ((p >> 16) & 0xff) | ((p << 16) & 0xff0000); - } - - void convertOpenGlToAndroidPixels(int w, int h, unsigned int* pixels) { - // Making the conversion in-place - for (int y = 0; y < h/2; ++y) - for (int x = 0; x < w; ++x) { - const int i0 = y * w + x; - const int i1 = (h - y - 1) * w + x; - const unsigned int p1 = pixels[i1]; - const unsigned int p0 = pixels[i0]; - pixels[i0] = rgbToBgr(p1); - pixels[i1] = rgbToBgr(p0); - } - // Handle the middle row, if any - if (h & 1) { - const int start = w * h/2; - const int stop = start + w; - for (int i = start; i < stop; ++i) { - const unsigned int p = pixels[i]; - pixels[i] = rgbToBgr(p); - } - } - } - - virtual void playSound(const std::string& filename, float volume, float pitch) { - if (!_isInited || !_methodPlaySound) return; - - //static Stopwatch w; - //w.start(); - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - env->CallVoidMethod(instance, _methodPlaySound, env->NewStringUTF(filename.c_str()), volume, pitch); - //w.stop(); - //w.printEvery(1, "playSound-java"); - } - - virtual void uploadPlatformDependentData(int id, void* data) { - if (!_isInited) return; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - if (id == 1) { - } - if (id == 2) { - } - } - - TextureData loadTexture(const std::string& filename, bool textureFolder) { - if (!_isInited) return TextureData(); - if (!_getImageData) return TextureData(); - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - std::string path = textureFolder ? "images/" + filename : filename; - jintArray arr = (jintArray)env->CallObjectMethod( - instance, _getImageData, env->NewStringUTF(path.c_str())); - - if (!arr) - return TextureData(); - - jsize len = env->GetArrayLength(arr); - int numPixels = len-2; - - // pixels = [width, height, p_0, p_1, ..., p_numPixels-1] - int* pixels = new int[numPixels]; - jint* body = env->GetIntArrayElements(arr, 0); - - int w = body[0]; - int h = body[1]; - memcpy(pixels, &body[2], numPixels * sizeof(int)); - - // Convert pixels - for (int i = 0; i < numPixels; ++i) - pixels[i] = rgbToBgr(pixels[i]); - - //LOGI("Read-Image-Data: Decided file size to be: %d x %d (len %d)\n", w, h, len); - - env->ReleaseIntArrayElements(arr, body, 0); - // LOGI("loadtexture: 9\n"); - - // Set the result - TextureData out; - out.w = w; - out.h = h; - out.data = (unsigned char*)pixels; - out.memoryHandledExternally = false; - return out; - } - - BinaryBlob readAssetFile(const std::string& filename) { - if (!_isInited) return BinaryBlob(); - if (!_readAssetFile) return BinaryBlob(); - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - jbyteArray arr = (jbyteArray)env->CallObjectMethod( - instance, _readAssetFile, env->NewStringUTF(filename.c_str())); - - if (!arr) - return BinaryBlob(); - - jsize len = env->GetArrayLength(arr); - BinaryBlob blob(new unsigned char[len], len); - jbyte* body = env->GetByteArrayElements(arr, 0); - memcpy(blob.data, body, len); - - env->ReleaseByteArrayElements(arr, body, 0); - return blob; - } - - std::string getDateString(int s) { - if (!_isInited) return ""; - if (!_methodGetDateString) return ""; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - std::string out; - jstring dateString = (jstring)env->CallObjectMethod(instance, _methodGetDateString, s); - const char* str = env->GetStringUTFChars(dateString, NULL); - if (str) - out = str; - env->ReleaseStringUTFChars(dateString, str); - return out; - } - - std::string getPlatformStringVar(int s) { - if (!_isInited) return ""; - if (!_methodGetPlatformStringVar) return ""; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - std::string out; - jstring stringVar = (jstring)env->CallObjectMethod(instance, _methodGetPlatformStringVar, s); - const char* str = env->GetStringUTFChars(stringVar, NULL); - if (str) - out = str; - env->ReleaseStringUTFChars(stringVar, str); - return out; - } - // Opens a webpage using an Android intent. Called from native code. - virtual void openURL(const std::string& url) { - if (!_isInited || !_methodOpenURL) return; - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - jstring jurl = env->NewStringUTF(url.c_str()); - env->CallVoidMethod(instance, _methodOpenURL, jurl); - env->DeleteLocalRef(jurl); - } - virtual void finish() { - if (!_isInited) return; - if (!_methodFinish) return; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - env->CallVoidMethod(instance, _methodFinish); - env->DeleteGlobalRef( _activityClass ); - - env->DeleteGlobalRef( _classWindow ); - env->DeleteGlobalRef( _classContext ); - env->DeleteGlobalRef( _classView ); - env->DeleteGlobalRef( _classInputManager ); - } - - virtual bool supportsTouchscreen() { - if (!_isInited) return true; - if (!_methodSupportsTouchscreen) return true; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - return env->CallBooleanMethod(instance, _methodSupportsTouchscreen); - } - - virtual void vibrate(int milliSeconds) { - if (!_isInited) return; - if (!_methodVibrate) return; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - return env->CallVoidMethod(instance, _methodVibrate, milliSeconds); - } - - virtual bool isPowerVR() { - bool is = super::isPowerVR(); - - if (_methodSetIsPowerVR && _isInited && !_hasSetPowerVR ) { - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - _hasSetPowerVR = true; - env->CallVoidMethod(instance, _methodSetIsPowerVR, is); - } - return is; - } - - virtual bool isNetworkEnabled(bool onlyWifiAllowed) { - if (!_isInited) return false; - if (!_methodIsNetworkEnabled) return false; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - return env->CallBooleanMethod(instance, _methodIsNetworkEnabled, onlyWifiAllowed); - } - - static __inline bool isSquare(int n) { - int L = n & 0xf; - if (((1 << L) & 0x213) == 0) return false; - - int t = (int) sqrt((double) n) + 0.5; - return t*t == n; - } - - virtual void showKeyboard() { - showKeyboard(true); - super::showKeyboard(); - } - virtual void hideKeyboard() { - showKeyboard(false); - super::hideKeyboard(); - } - virtual void showKeyboard(bool bShow) { - if (!_isInited) return; - - jint lResult = 0; - jint lFlags = 0; - - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - jobject INPUT_METHOD_SERVICE = - env->GetStaticObjectField(_classContext, - _fieldINPUT_METHOD_SERVICE); - - jobject lInputMethodManager = env->CallObjectMethod( - instance, _methodGetSystemService, - INPUT_METHOD_SERVICE); - - jobject lWindow = env->CallObjectMethod(instance, - _methodGetWindow); - - jobject lDecorView = env->CallObjectMethod(lWindow, - _methodGetDecorView); - - if (bShow) { - // Runs lInputMethodManager.showSoftInput(...). - jboolean lResult = env->CallBooleanMethod( - lInputMethodManager, _methodShowSoftInput, - lDecorView, lFlags); - } else { - // Runs lWindow.getViewToken() - jobject lBinder = env->CallObjectMethod(lDecorView, - _methodGetWindowToken); - jboolean lRes = env->CallBooleanMethod( - lInputMethodManager, _methodHideSoftInput, - lBinder, lFlags); - } - } - virtual int getKeyFromKeyCode(int keyCode, int metaState, int deviceId) { - JVMAttacher ta(_vm); - JNIEnv* env = ta.getEnv(); - - return (int) env->CallIntMethod(instance, _methodGetKeyFromKeyCode, keyCode, metaState, deviceId); - } -public: - jobject instance; - -private: - bool _isInited; - JavaVM* _vm; - - jclass _activityClass; - - jmethodID _methodSaveScreenshot; - jmethodID _postScreenshotToFacebook; - jmethodID _getImageData; - jmethodID _readAssetFile; - jmethodID _methodPlaySound; - jmethodID _showDialog; - jmethodID _methodTick; - jmethodID _methodFinish; - - jmethodID _methodUserInputInitiate; - jmethodID _methodUserInputStatus; - jmethodID _methodUserInputString; - - jmethodID _methodGetDateString; - - jmethodID _getScreenWidth; - jmethodID _getScreenHeight; - jmethodID _methodGetPixelsPerMillimeter; - jmethodID _methodVibrate; - jmethodID _methodSupportsTouchscreen; - jmethodID _methodSetIsPowerVR; - jmethodID _methodIsNetworkEnabled; - - jmethodID _methodGetPlatformStringVar; - jmethodID _methodOpenURL; // new JNI method for launching browser - - jclass _classWindow; - jclass _classContext; - jfieldID _fieldINPUT_METHOD_SERVICE; - jclass _classInputManager; - jmethodID _methodGetSystemService; - jmethodID _methodGetWindow; - jmethodID _methodGetDecorView; - jmethodID _methodShowSoftInput; - jclass _classView; - jmethodID _methodGetWindowToken; - jmethodID _methodHideSoftInput; - - jmethodID _methodGetKeyFromKeyCode; - - int _screenWidth; - int _screenHeight; - bool _hasSetPowerVR; -public: - ANativeActivity* _nativeActivity; -}; - -#endif /*APPPLATFORM_ANDROID_H__*/ +#ifndef APPPLATFORM_ANDROID_H__ +#define APPPLATFORM_ANDROID_H__ + +#include "AppPlatform.h" +#include "client/renderer/gles.h" +#include "platform/log.h" +#include "platform/time.h" +#include +#include +#include +#include +class JVMAttacher { +public: + JVMAttacher(JavaVM* vm) + : _vm(vm), + _isAttached(false), + _env(NULL) + { + if( _vm->GetEnv((void**)&_env, JNI_VERSION_1_4 ) != JNI_OK ) { + _vm->AttachCurrentThread(&_env, NULL); + _isAttached = _env != NULL; + } + } + + ~JVMAttacher() { + forceDetach(); + } + + JNIEnv* getEnv() { + return _env; + } + + void forceDetach() { + if (!_isAttached) return; + _vm->DetachCurrentThread(); + _isAttached = false; + } + +private: + JavaVM* _vm; + JNIEnv* _env; + bool _isAttached; +}; + +class AppPlatform_android: public AppPlatform +{ + typedef AppPlatform super; +public: + AppPlatform_android() + : _vm(NULL), + _isInited(false), + + instance(0), + _activityClass(0), + + _methodSaveScreenshot(0), + _postScreenshotToFacebook(0), + _getImageData(0), + _readAssetFile(0), + _methodPlaySound(0), + _showDialog(0), + _methodTick(0), + _methodFinish(0), + + _methodUserInputInitiate(0), + _methodUserInputStatus(0), + _methodUserInputString(0), + + _methodGetDateString(0), + + _methodVibrate(0), + _methodSupportsTouchscreen(0), + _methodSetIsPowerVR(0), + _methodIsNetworkEnabled(0), + + _getScreenWidth(0), + _getScreenHeight(0), + _methodGetPixelsPerMillimeter(0), + _methodGetPlatformStringVar(0), + + _classWindow(0), + _classContext(0), + _fieldINPUT_METHOD_SERVICE(0), + _classInputManager(0), + _methodGetSystemService(0), + _methodGetWindow(0), + _methodGetDecorView(0), + _methodShowSoftInput(0), + _classView(0), + _methodGetWindowToken(0), + _methodHideSoftInput(0), + + _screenWidth(854), + _screenHeight(480), + _hasSetPowerVR(false), + _nativeActivity(0), + + _methodGetKeyFromKeyCode(0) + { + } + + int getScreenWidth() { return _screenWidth; } + int getScreenHeight() { return _screenHeight; } + void setScreenDimensions(int w, int h) { _screenWidth = w; _screenHeight = h; } + + // Note, this has to be called from the main thread (e.g. do it from JNI_onLoad) + // Somewhere between calling this, and calling the AppPlatform methods, + // this->instance will be assigned by its creator + int init(JavaVM* vm) { + if (_isInited) + return -1; + + JVMAttacher ta(vm); + JNIEnv* env = ta.getEnv(); + + if(!env) { + LOGI("%s - Failed to get the environment using JVMAttacher::getEnv()", __FUNCTION__); + return -2; + } + + // Get Class reference + const char* interface_path = "com/mojang/minecraftpe/MainActivity"; + jclass clazz = NULL; + if( (clazz = env->FindClass( interface_path )) == 0 ) { + return -3; + } + _activityClass = (jclass)env->NewGlobalRef(clazz); + + // Save all the method IDs + _methodSaveScreenshot = env->GetStaticMethodID( _activityClass, "saveScreenshot", "(Ljava/lang/String;II[I)V"); + _postScreenshotToFacebook = env->GetMethodID( _activityClass, "postScreenshotToFacebook", "(Ljava/lang/String;II[I)V"); + _getImageData = env->GetMethodID( _activityClass, "getImageData", "(Ljava/lang/String;)[I"); + _readAssetFile = env->GetMethodID( _activityClass, "getFileDataBytes", "(Ljava/lang/String;)[B"); + #if defined(PRE_ANDROID23) + _methodPlaySound = env->GetMethodID( _activityClass, "playSound", "(Ljava/lang/String;FF)V"); + #endif + _showDialog = env->GetMethodID( _activityClass, "displayDialog", "(I)V"); + _methodTick = env->GetMethodID( _activityClass, "tick", "()V"); + _methodFinish = env->GetMethodID( _activityClass, "quit", "()V"); + + _methodUserInputInitiate = env->GetMethodID( _activityClass, "initiateUserInput", "(I)V"); + _methodUserInputStatus = env->GetMethodID( _activityClass, "getUserInputStatus", "()I"); + _methodUserInputString = env->GetMethodID( _activityClass, "getUserInputString", "()[Ljava/lang/String;"); + + _methodGetDateString = env->GetMethodID( _activityClass, "getDateString", "(I)Ljava/lang/String;"); + + _methodVibrate = env->GetMethodID( _activityClass, "vibrate", "(I)V"); + _methodSupportsTouchscreen = env->GetMethodID( _activityClass, "supportsTouchscreen", "()Z"); + _methodSetIsPowerVR = env->GetMethodID( _activityClass, "setIsPowerVR", "(Z)V"); + _methodIsNetworkEnabled = env->GetMethodID( _activityClass, "isNetworkEnabled", "(Z)Z"); + + _methodGetPixelsPerMillimeter = env->GetMethodID( _activityClass, "getPixelsPerMillimeter", "()F"); + _methodGetPlatformStringVar = env->GetMethodID( _activityClass, "getPlatformStringVar", "(I)Ljava/lang/String;"); + // custom helper to launch external URLs + _methodOpenURL = env->GetMethodID(_activityClass, "openURL", "(Ljava/lang/String;)V"); + + _classWindow = (jclass)env->NewGlobalRef(env->FindClass("android/view/Window")); + _classContext = (jclass)env->NewGlobalRef(env->FindClass("android/content/Context")); + _classView = (jclass)env->NewGlobalRef(env->FindClass( "android/view/View")); + _classInputManager = (jclass)env->NewGlobalRef(env->FindClass("android/view/inputmethod/InputMethodManager")); + + _fieldINPUT_METHOD_SERVICE = env->GetStaticFieldID(_classContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;"); + _methodGetSystemService = env->GetMethodID(_activityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + _methodGetWindow = env->GetMethodID( _activityClass, "getWindow", "()Landroid/view/Window;"); + _methodGetDecorView = env->GetMethodID(_classWindow, "getDecorView", "()Landroid/view/View;"); + _methodShowSoftInput = env->GetMethodID(_classInputManager, "showSoftInput", "(Landroid/view/View;I)Z"); + _methodGetWindowToken = env->GetMethodID(_classView, "getWindowToken", "()Landroid/os/IBinder;"); + _methodHideSoftInput = env->GetMethodID(_classInputManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"); + + _methodGetKeyFromKeyCode = env->GetMethodID( _activityClass, "getKeyFromKeyCode", "(III)I"); + + if (env->ExceptionOccurred()) { + env->ExceptionDescribe(); + } + + // LOGI("Class: %d\n. Methods: %d,%d\n", _activityClass, + // _methodSaveScreenshot, + // _postScreenshotToFacebook); + + _vm = vm; + _isInited = true; + + return JNI_VERSION_1_4; + } + + // Stuff that should only be written once, e.g. device specs + // @note: This is called after instance is set, BUT this will + // be rewritten later on anyway + int initConsts() { + + LOGI("initConsts: start\n"); + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + LOGI("initConsts: getting method IDs\n"); + jmethodID fWidth = env->GetMethodID( _activityClass, "getScreenWidth", "()I"); + LOGI("initConsts: got fWidth=%p\n", fWidth); + jmethodID fHeight = env->GetMethodID( _activityClass, "getScreenHeight", "()I"); + LOGI("initConsts: got fHeight=%p, calling getScreenWidth\n", fHeight); + + _screenWidth = env->CallIntMethod(instance, fWidth); + LOGI("initConsts: screenWidth=%d, calling getScreenHeight\n", _screenWidth); + _screenHeight = env->CallIntMethod(instance, fHeight); + LOGI("initConsts: screenHeight=%d, done\n", _screenHeight); + + return 1; + } + + void tick() { + if (!_isInited) return; + if (!_methodTick) return; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + env->CallVoidMethod(instance, _methodTick); + } + + void showDialog(int dialogId) { + if (!_isInited) return; + if (!_showDialog) return; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + env->CallVoidMethod(instance, _showDialog, dialogId); + } + + void createUserInput() { + if (!_isInited) return; + if (!_methodUserInputInitiate) return; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + env->CallVoidMethod(instance, _methodUserInputInitiate, 1); + } + + int getUserInputStatus() { + if (!_isInited) return -2; + if (!_methodUserInputStatus) return -2; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + return env->CallIntMethod(instance, _methodUserInputStatus); + } + + float getPixelsPerMillimeter() { + if (!_isInited) return 10; + if (!_methodGetPixelsPerMillimeter) return 10; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + return env->CallFloatMethod(instance, _methodGetPixelsPerMillimeter); + } + + StringVector getUserInput() { + if (!_isInited) return std::vector(); + if (!_methodUserInputString) return std::vector(); + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + jobjectArray sa = (jobjectArray) env->CallObjectMethod(instance, _methodUserInputString); + jsize len = env->GetArrayLength(sa); + + StringVector out; + for (int i = 0; i < len; ++i) { + + jstring s = (jstring)env->GetObjectArrayElement(sa, i); + int length = env->GetStringLength (s); + if (length > 1023) + length = 1023; + static char buf[1024]; + + // Only supporting 7 bit chars, right now + const char* str = env->GetStringUTFChars(s, 0); + strncpy(buf, str, 1023); + buf[1023] = 0; + + out.push_back(std::string(buf)); + } + return out; + } + + void saveScreenshot(const std::string& filename, int glWidth, int glHeight) { + if (!_isInited) return; + if (!_methodSaveScreenshot) return; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + const int numPixels = glWidth * glHeight; + unsigned int* pixels = new unsigned int[numPixels]; + + if (!pixels) + return; + + // Create the OpenGL "screenshot" + glReadPixels(0, 0, glWidth, glHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + convertOpenGlToAndroidPixels(glWidth, glHeight, pixels); + + // Tell Android to save the screenshot + jintArray jPixels = env->NewIntArray(numPixels); + if (jPixels != NULL) + { + jobject gpixRef = env->NewGlobalRef(jPixels); + env->SetIntArrayRegion(jPixels, 0, numPixels, (int*)pixels); +// env->CallVoidMethod( instance, +// _postScreenshotToFacebook, +// env->NewStringUTF(filename.c_str()), +// glWidth, glHeight, jPixels); + env->CallStaticVoidMethod( _activityClass, + _methodSaveScreenshot, + env->NewStringUTF(filename.c_str()), + glWidth, glHeight, jPixels); + // Teardown + env->DeleteGlobalRef(gpixRef); + delete[] pixels; + } + } + + __inline unsigned int rgbToBgr(unsigned int p) { + return (p & 0xff00ff00) | ((p >> 16) & 0xff) | ((p << 16) & 0xff0000); + } + + void convertOpenGlToAndroidPixels(int w, int h, unsigned int* pixels) { + // Making the conversion in-place + for (int y = 0; y < h/2; ++y) + for (int x = 0; x < w; ++x) { + const int i0 = y * w + x; + const int i1 = (h - y - 1) * w + x; + const unsigned int p1 = pixels[i1]; + const unsigned int p0 = pixels[i0]; + pixels[i0] = rgbToBgr(p1); + pixels[i1] = rgbToBgr(p0); + } + // Handle the middle row, if any + if (h & 1) { + const int start = w * h/2; + const int stop = start + w; + for (int i = start; i < stop; ++i) { + const unsigned int p = pixels[i]; + pixels[i] = rgbToBgr(p); + } + } + } + + virtual void playSound(const std::string& filename, float volume, float pitch) { + if (!_isInited || !_methodPlaySound) return; + + //static Stopwatch w; + //w.start(); + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + env->CallVoidMethod(instance, _methodPlaySound, env->NewStringUTF(filename.c_str()), volume, pitch); + //w.stop(); + //w.printEvery(1, "playSound-java"); + } + + virtual void uploadPlatformDependentData(int id, void* data) { + if (!_isInited) return; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + if (id == 1) { + } + if (id == 2) { + } + } + + TextureData loadTexture(const std::string& filename, bool textureFolder) { + if (!_isInited) return TextureData(); + if (!_getImageData) return TextureData(); + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + std::string path = textureFolder ? "images/" + filename : filename; + jintArray arr = (jintArray)env->CallObjectMethod( + instance, _getImageData, env->NewStringUTF(path.c_str())); + + if (!arr) + return TextureData(); + + jsize len = env->GetArrayLength(arr); + int numPixels = len-2; + + // pixels = [width, height, p_0, p_1, ..., p_numPixels-1] + int* pixels = new int[numPixels]; + jint* body = env->GetIntArrayElements(arr, 0); + + int w = body[0]; + int h = body[1]; + memcpy(pixels, &body[2], numPixels * sizeof(int)); + + // Convert pixels + for (int i = 0; i < numPixels; ++i) + pixels[i] = rgbToBgr(pixels[i]); + + //LOGI("Read-Image-Data: Decided file size to be: %d x %d (len %d)\n", w, h, len); + + env->ReleaseIntArrayElements(arr, body, 0); + // LOGI("loadtexture: 9\n"); + + // Set the result + TextureData out; + out.w = w; + out.h = h; + out.data = (unsigned char*)pixels; + out.memoryHandledExternally = false; + return out; + } + + BinaryBlob readAssetFile(const std::string& filename) { + if (!_isInited) return BinaryBlob(); + if (!_readAssetFile) return BinaryBlob(); + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + jbyteArray arr = (jbyteArray)env->CallObjectMethod( + instance, _readAssetFile, env->NewStringUTF(filename.c_str())); + + if (!arr) + return BinaryBlob(); + + jsize len = env->GetArrayLength(arr); + BinaryBlob blob(new unsigned char[len], len); + jbyte* body = env->GetByteArrayElements(arr, 0); + memcpy(blob.data, body, len); + + env->ReleaseByteArrayElements(arr, body, 0); + return blob; + } + + std::string getDateString(int s) { + if (!_isInited) return ""; + if (!_methodGetDateString) return ""; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + std::string out; + jstring dateString = (jstring)env->CallObjectMethod(instance, _methodGetDateString, s); + const char* str = env->GetStringUTFChars(dateString, NULL); + if (str) + out = str; + env->ReleaseStringUTFChars(dateString, str); + return out; + } + + std::string getPlatformStringVar(int s) { + if (!_isInited) return ""; + if (!_methodGetPlatformStringVar) return ""; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + std::string out; + jstring stringVar = (jstring)env->CallObjectMethod(instance, _methodGetPlatformStringVar, s); + const char* str = env->GetStringUTFChars(stringVar, NULL); + if (str) + out = str; + env->ReleaseStringUTFChars(stringVar, str); + return out; + } + // Opens a webpage using an Android intent. Called from native code. + virtual void openURL(const std::string& url) { + if (!_isInited || !_methodOpenURL) return; + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + jstring jurl = env->NewStringUTF(url.c_str()); + env->CallVoidMethod(instance, _methodOpenURL, jurl); + env->DeleteLocalRef(jurl); + } + virtual void finish() { + if (!_isInited) return; + if (!_methodFinish) return; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + env->CallVoidMethod(instance, _methodFinish); + env->DeleteGlobalRef( _activityClass ); + + env->DeleteGlobalRef( _classWindow ); + env->DeleteGlobalRef( _classContext ); + env->DeleteGlobalRef( _classView ); + env->DeleteGlobalRef( _classInputManager ); + } + + virtual bool supportsTouchscreen() { + if (!_isInited) return true; + if (!_methodSupportsTouchscreen) return true; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + return env->CallBooleanMethod(instance, _methodSupportsTouchscreen); + } + + virtual void vibrate(int milliSeconds) { + if (!_isInited) return; + if (!_methodVibrate) return; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + return env->CallVoidMethod(instance, _methodVibrate, milliSeconds); + } + + virtual bool isPowerVR() { + bool is = super::isPowerVR(); + + if (_methodSetIsPowerVR && _isInited && !_hasSetPowerVR ) { + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + _hasSetPowerVR = true; + env->CallVoidMethod(instance, _methodSetIsPowerVR, is); + } + return is; + } + + virtual bool isNetworkEnabled(bool onlyWifiAllowed) { + if (!_isInited) return false; + if (!_methodIsNetworkEnabled) return false; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + return env->CallBooleanMethod(instance, _methodIsNetworkEnabled, onlyWifiAllowed); + } + + static __inline bool isSquare(int n) { + int L = n & 0xf; + if (((1 << L) & 0x213) == 0) return false; + + int t = (int) sqrt((double) n) + 0.5; + return t*t == n; + } + + virtual void showKeyboard() { + showKeyboard(true); + super::showKeyboard(); + } + virtual void hideKeyboard() { + showKeyboard(false); + super::hideKeyboard(); + } + virtual void showKeyboard(bool bShow) { + if (!_isInited) return; + + jint lResult = 0; + jint lFlags = 0; + + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + jobject INPUT_METHOD_SERVICE = + env->GetStaticObjectField(_classContext, + _fieldINPUT_METHOD_SERVICE); + + jobject lInputMethodManager = env->CallObjectMethod( + instance, _methodGetSystemService, + INPUT_METHOD_SERVICE); + + jobject lWindow = env->CallObjectMethod(instance, + _methodGetWindow); + + jobject lDecorView = env->CallObjectMethod(lWindow, + _methodGetDecorView); + + if (bShow) { + // Runs lInputMethodManager.showSoftInput(...). + jboolean lResult = env->CallBooleanMethod( + lInputMethodManager, _methodShowSoftInput, + lDecorView, lFlags); + } else { + // Runs lWindow.getViewToken() + jobject lBinder = env->CallObjectMethod(lDecorView, + _methodGetWindowToken); + jboolean lRes = env->CallBooleanMethod( + lInputMethodManager, _methodHideSoftInput, + lBinder, lFlags); + } + } + virtual int getKeyFromKeyCode(int keyCode, int metaState, int deviceId) { + JVMAttacher ta(_vm); + JNIEnv* env = ta.getEnv(); + + return (int) env->CallIntMethod(instance, _methodGetKeyFromKeyCode, keyCode, metaState, deviceId); + } +public: + jobject instance; + +private: + bool _isInited; + JavaVM* _vm; + + jclass _activityClass; + + jmethodID _methodSaveScreenshot; + jmethodID _postScreenshotToFacebook; + jmethodID _getImageData; + jmethodID _readAssetFile; + jmethodID _methodPlaySound; + jmethodID _showDialog; + jmethodID _methodTick; + jmethodID _methodFinish; + + jmethodID _methodUserInputInitiate; + jmethodID _methodUserInputStatus; + jmethodID _methodUserInputString; + + jmethodID _methodGetDateString; + + jmethodID _getScreenWidth; + jmethodID _getScreenHeight; + jmethodID _methodGetPixelsPerMillimeter; + jmethodID _methodVibrate; + jmethodID _methodSupportsTouchscreen; + jmethodID _methodSetIsPowerVR; + jmethodID _methodIsNetworkEnabled; + + jmethodID _methodGetPlatformStringVar; + jmethodID _methodOpenURL; // new JNI method for launching browser + + jclass _classWindow; + jclass _classContext; + jfieldID _fieldINPUT_METHOD_SERVICE; + jclass _classInputManager; + jmethodID _methodGetSystemService; + jmethodID _methodGetWindow; + jmethodID _methodGetDecorView; + jmethodID _methodShowSoftInput; + jclass _classView; + jmethodID _methodGetWindowToken; + jmethodID _methodHideSoftInput; + + jmethodID _methodGetKeyFromKeyCode; + + int _screenWidth; + int _screenHeight; + bool _hasSetPowerVR; +public: + ANativeActivity* _nativeActivity; +}; + +#endif /*APPPLATFORM_ANDROID_H__*/ diff --git a/src/AppPlatform_android23.h b/src/AppPlatform_android23.h index 1e69bc7..d7a5e6b 100755 --- a/src/AppPlatform_android23.h +++ b/src/AppPlatform_android23.h @@ -1,49 +1,49 @@ -#include "AppPlatform_android.h" -#include -#include - -class AppPlatform_android23 : public AppPlatform_android -{ - typedef AppPlatform_android super; -public: - AppPlatform_android23() - : _assetManager(NULL) - { - } - - // If we're using Android 2.3+, try reading assets from NDK at first. - // If that doesn't work, read through java/JNI as usual. - BinaryBlob readAssetFile(const std::string& filename) { - if (!_isInited) - return BinaryBlob(); - - if (_assetManager != NULL) { - AAsset* asset = AAssetManager_open(_assetManager, filename.c_str(), AASSET_MODE_BUFFER); - if (asset != NULL) { - const int len = AAsset_getLength(asset); - const void* buf = len > 0? AAsset_getBuffer(asset) : NULL; - - BinaryBlob blob; - if (buf != NULL) { - blob = BinaryBlob(new unsigned char[len], len); - memcpy(blob.data, buf, len); - } - AAsset_close(asset); - if (blob.data) - return blob; - } - } - return super::readAssetFile(filename); - } - - - // Another init method... added to read data from the activity, and setup constants - // @note: This is called after instance is set from the outside, BUT this - // will be rewritten later on anyway - void initWithActivity(struct ANativeActivity* activity) { - _assetManager = activity->assetManager; - } - -private: - AAssetManager* _assetManager; -}; +#include "AppPlatform_android.h" +#include +#include + +class AppPlatform_android23 : public AppPlatform_android +{ + typedef AppPlatform_android super; +public: + AppPlatform_android23() + : _assetManager(NULL) + { + } + + // If we're using Android 2.3+, try reading assets from NDK at first. + // If that doesn't work, read through java/JNI as usual. + BinaryBlob readAssetFile(const std::string& filename) { + if (!_isInited) + return BinaryBlob(); + + if (_assetManager != NULL) { + AAsset* asset = AAssetManager_open(_assetManager, filename.c_str(), AASSET_MODE_BUFFER); + if (asset != NULL) { + const int len = AAsset_getLength(asset); + const void* buf = len > 0? AAsset_getBuffer(asset) : NULL; + + BinaryBlob blob; + if (buf != NULL) { + blob = BinaryBlob(new unsigned char[len], len); + memcpy(blob.data, buf, len); + } + AAsset_close(asset); + if (blob.data) + return blob; + } + } + return super::readAssetFile(filename); + } + + + // Another init method... added to read data from the activity, and setup constants + // @note: This is called after instance is set from the outside, BUT this + // will be rewritten later on anyway + void initWithActivity(struct ANativeActivity* activity) { + _assetManager = activity->assetManager; + } + +private: + AAssetManager* _assetManager; +}; diff --git a/src/AppPlatform_glfw.cpp b/src/AppPlatform_glfw.cpp index 01fd7b4..adf3f80 100644 --- a/src/AppPlatform_glfw.cpp +++ b/src/AppPlatform_glfw.cpp @@ -1,12 +1,12 @@ -#include "AppPlatform_glfw.h" - -float AppPlatform_glfw::getPixelsPerMillimeter() { - GLFWmonitor* monitor = glfwGetPrimaryMonitor(); - - int width_mm, height_mm; - glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm); - - const GLFWvidmode* mode = glfwGetVideoMode(monitor); - - return (float)mode->width / (float)width_mm; +#include "AppPlatform_glfw.h" + +float AppPlatform_glfw::getPixelsPerMillimeter() { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + + int width_mm, height_mm; + glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm); + + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + + return (float)mode->width / (float)width_mm; } \ No newline at end of file diff --git a/src/AppPlatform_glfw.h b/src/AppPlatform_glfw.h index d996860..febb9dc 100755 --- a/src/AppPlatform_glfw.h +++ b/src/AppPlatform_glfw.h @@ -1,155 +1,155 @@ -#ifndef APPPLATFORM_GLFW_H__ -#define APPPLATFORM_GLFW_H__ - -#include "AppPlatform.h" -#include "platform/log.h" -#include "platform/HttpClient.h" -#include "platform/PngLoader.h" -#include "client/renderer/gles.h" -#include "world/level/storage/FolderMethods.h" -#include -#include -#include -#include -#include -#include -#include "util/StringUtils.h" - -#ifdef _WIN32 -#include -#include -#endif - -#ifdef __EMSCRIPTEN__ - #include -#endif - -static void png_funcReadFile(png_structp pngPtr, png_bytep data, png_size_t length) { - ((std::istream*)png_get_io_ptr(pngPtr))->read((char*)data, length); -} - -class AppPlatform_glfw: public AppPlatform -{ -public: - AppPlatform_glfw() - { - } - - BinaryBlob readAssetFile(const std::string& filename) override { - FILE* fp = fopen(("data/" + filename).c_str(), "r"); - if (!fp) - return BinaryBlob(); - - int size = getRemainingFileSize(fp); - - BinaryBlob blob; - blob.size = size; - blob.data = new unsigned char[size]; - - fread(blob.data, 1, size, fp); - fclose(fp); - - return blob; - } - - void saveScreenshot(const std::string& filename, int glWidth, int glHeight) override { - //@todo - } - - __inline unsigned int rgbToBgr(unsigned int p) { - return (p & 0xff00ff00) | ((p >> 16) & 0xff) | ((p << 16) & 0xff0000); - } - - TextureData loadTexture(const std::string& filename_, bool textureFolder) override - { - // Support fetching PNG textures via HTTP/HTTPS (for skins, etc) - if (Util::startsWith(filename_, "http://") || Util::startsWith(filename_, "https://")) { - std::vector body; - if (HttpClient::download(filename_, body) && !body.empty()) { - return loadTextureFromMemory(body.data(), body.size()); - } - return TextureData(); - } - - TextureData out; - - std::string filename = textureFolder? "data/images/" + filename_ - : filename_; - std::ifstream source(filename.c_str(), std::ios::binary); - - if (!source) { - LOGI("Couldn't find file: %s\n", filename.c_str()); - return out; - } - - std::vector fileData((std::istreambuf_iterator(source)), std::istreambuf_iterator()); - source.close(); - - if (fileData.empty()) { - LOGI("Couldn't read file: %s\n", filename.c_str()); - return out; - } - - return loadTextureFromMemory(fileData.data(), fileData.size()); - } - - TextureData loadTextureFromMemory(const unsigned char* data, size_t size) override { - return loadPngFromMemory(data, size); - } - - virtual std::string getDateString(int s) override { - time_t tm = s; - - char mbstr[100]; - std::strftime(mbstr, sizeof(mbstr), "%F %T", std::localtime(&tm)); - - return std::string(mbstr); - } - - virtual int getScreenWidth() override { - #ifdef __EMSCRIPTEN__ - int w, h; - emscripten_get_canvas_element_size("canvas", &w, &h); - - return w; - #endif - - return 854; - }; - - virtual int getScreenHeight() override { - #ifdef __EMSCRIPTEN__ - int w, h; - emscripten_get_canvas_element_size("canvas", &w, &h); - - return h; - #endif - - return 480; - }; - - virtual float getPixelsPerMillimeter() override; - - virtual bool supportsTouchscreen() override { return false; /* glfw supports only mouse and keyboard */ } - - virtual void hideCursor(bool hide) override { - int isHide = hide ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN; - glfwSetInputMode(window, GLFW_CURSOR, isHide); - } - - virtual void openURL(const std::string& url) override { -#ifdef _WIN32 - ShellExecuteA(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); -#elif __linux__ - std::string command = "xdg-open " + url; - system(command.c_str()); -#elif __EMSCRIPTEN__ - emscripten_run_script(std::string("window.open('" + url + "', '_blank')").c_str()); -#endif - } - - GLFWwindow* window; - -private: -}; -#endif /*APPPLATFORM_GLFW_H__*/ +#ifndef APPPLATFORM_GLFW_H__ +#define APPPLATFORM_GLFW_H__ + +#include "AppPlatform.h" +#include "platform/log.h" +#include "platform/HttpClient.h" +#include "platform/PngLoader.h" +#include "client/renderer/gles.h" +#include "world/level/storage/FolderMethods.h" +#include +#include +#include +#include +#include +#include +#include "util/StringUtils.h" + +#ifdef _WIN32 +#include +#include +#endif + +#ifdef __EMSCRIPTEN__ + #include +#endif + +static void png_funcReadFile(png_structp pngPtr, png_bytep data, png_size_t length) { + ((std::istream*)png_get_io_ptr(pngPtr))->read((char*)data, length); +} + +class AppPlatform_glfw: public AppPlatform +{ +public: + AppPlatform_glfw() + { + } + + BinaryBlob readAssetFile(const std::string& filename) override { + FILE* fp = fopen(("data/" + filename).c_str(), "r"); + if (!fp) + return BinaryBlob(); + + int size = getRemainingFileSize(fp); + + BinaryBlob blob; + blob.size = size; + blob.data = new unsigned char[size]; + + fread(blob.data, 1, size, fp); + fclose(fp); + + return blob; + } + + void saveScreenshot(const std::string& filename, int glWidth, int glHeight) override { + //@todo + } + + __inline unsigned int rgbToBgr(unsigned int p) { + return (p & 0xff00ff00) | ((p >> 16) & 0xff) | ((p << 16) & 0xff0000); + } + + TextureData loadTexture(const std::string& filename_, bool textureFolder) override + { + // Support fetching PNG textures via HTTP/HTTPS (for skins, etc) + if (Util::startsWith(filename_, "http://") || Util::startsWith(filename_, "https://")) { + std::vector body; + if (HttpClient::download(filename_, body) && !body.empty()) { + return loadTextureFromMemory(body.data(), body.size()); + } + return TextureData(); + } + + TextureData out; + + std::string filename = textureFolder? "data/images/" + filename_ + : filename_; + std::ifstream source(filename.c_str(), std::ios::binary); + + if (!source) { + LOGI("Couldn't find file: %s\n", filename.c_str()); + return out; + } + + std::vector fileData((std::istreambuf_iterator(source)), std::istreambuf_iterator()); + source.close(); + + if (fileData.empty()) { + LOGI("Couldn't read file: %s\n", filename.c_str()); + return out; + } + + return loadTextureFromMemory(fileData.data(), fileData.size()); + } + + TextureData loadTextureFromMemory(const unsigned char* data, size_t size) override { + return loadPngFromMemory(data, size); + } + + virtual std::string getDateString(int s) override { + time_t tm = s; + + char mbstr[100]; + std::strftime(mbstr, sizeof(mbstr), "%F %T", std::localtime(&tm)); + + return std::string(mbstr); + } + + virtual int getScreenWidth() override { + #ifdef __EMSCRIPTEN__ + int w, h; + emscripten_get_canvas_element_size("canvas", &w, &h); + + return w; + #endif + + return 854; + }; + + virtual int getScreenHeight() override { + #ifdef __EMSCRIPTEN__ + int w, h; + emscripten_get_canvas_element_size("canvas", &w, &h); + + return h; + #endif + + return 480; + }; + + virtual float getPixelsPerMillimeter() override; + + virtual bool supportsTouchscreen() override { return false; /* glfw supports only mouse and keyboard */ } + + virtual void hideCursor(bool hide) override { + int isHide = hide ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN; + glfwSetInputMode(window, GLFW_CURSOR, isHide); + } + + virtual void openURL(const std::string& url) override { +#ifdef _WIN32 + ShellExecuteA(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); +#elif __linux__ + std::string command = "xdg-open " + url; + system(command.c_str()); +#elif __EMSCRIPTEN__ + emscripten_run_script(std::string("window.open('" + url + "', '_blank')").c_str()); +#endif + } + + GLFWwindow* window; + +private: +}; +#endif /*APPPLATFORM_GLFW_H__*/ diff --git a/src/AppPlatform_iOS.h b/src/AppPlatform_iOS.h index a2206b6..32c3aaf 100755 --- a/src/AppPlatform_iOS.h +++ b/src/AppPlatform_iOS.h @@ -1,65 +1,65 @@ -#ifndef APPPLATFORM_IOS_H__ -#define APPPLATFORM_IOS_H__ - -#include "AppPlatform.h" -#include "client/renderer/gles.h" -#include "platform/log.h" -#include -#include -#include - -@class minecraftpeViewController; - -class AppPlatform_iOS: public AppPlatform -{ - typedef AppPlatform super; -public: - AppPlatform_iOS(minecraftpeViewController* vc) { - _viewController = vc; - srand(time(0)); - - LOGI("ViewController in AppPlatform: %p\n", _viewController); - } - - void setBasePath(const std::string& bp) { _basePath = bp; } - - void saveScreenshot(const std::string& filename, int glWidth, int glHeight) { - //@todo - } - - __inline unsigned int rgbToBgr(unsigned int p) { - return (p & 0xff00ff00) | ((p >> 16) & 0xff) | ((p << 16) & 0xff0000); - } - - virtual void showDialog(int dialogId); - virtual int getUserInputStatus(); - virtual StringVector getUserInput(); - - TextureData loadTexture(const std::string& filename_, bool textureFolder); - - virtual BinaryBlob readAssetFile(const std::string& filename); - - std::string getDateString(int s); - - virtual int getScreenWidth(); - virtual int getScreenHeight(); - virtual float getPixelsPerMillimeter(); - - virtual bool isTouchscreen(); - virtual void vibrate(int milliSeconds); - - virtual bool isNetworkEnabled(bool onlyWifiAllowed); - - virtual StringVector getOptionStrings(); - - virtual bool isPowerVR(); - virtual bool isSuperFast(); - virtual void showKeyboard(); - virtual void hideKeyboard(); -private: - - std::string _basePath; - minecraftpeViewController* _viewController; -}; - -#endif /*APPPLATFORM_IOS_H__*/ +#ifndef APPPLATFORM_IOS_H__ +#define APPPLATFORM_IOS_H__ + +#include "AppPlatform.h" +#include "client/renderer/gles.h" +#include "platform/log.h" +#include +#include +#include + +@class minecraftpeViewController; + +class AppPlatform_iOS: public AppPlatform +{ + typedef AppPlatform super; +public: + AppPlatform_iOS(minecraftpeViewController* vc) { + _viewController = vc; + srand(time(0)); + + LOGI("ViewController in AppPlatform: %p\n", _viewController); + } + + void setBasePath(const std::string& bp) { _basePath = bp; } + + void saveScreenshot(const std::string& filename, int glWidth, int glHeight) { + //@todo + } + + __inline unsigned int rgbToBgr(unsigned int p) { + return (p & 0xff00ff00) | ((p >> 16) & 0xff) | ((p << 16) & 0xff0000); + } + + virtual void showDialog(int dialogId); + virtual int getUserInputStatus(); + virtual StringVector getUserInput(); + + TextureData loadTexture(const std::string& filename_, bool textureFolder); + + virtual BinaryBlob readAssetFile(const std::string& filename); + + std::string getDateString(int s); + + virtual int getScreenWidth(); + virtual int getScreenHeight(); + virtual float getPixelsPerMillimeter(); + + virtual bool isTouchscreen(); + virtual void vibrate(int milliSeconds); + + virtual bool isNetworkEnabled(bool onlyWifiAllowed); + + virtual StringVector getOptionStrings(); + + virtual bool isPowerVR(); + virtual bool isSuperFast(); + virtual void showKeyboard(); + virtual void hideKeyboard(); +private: + + std::string _basePath; + minecraftpeViewController* _viewController; +}; + +#endif /*APPPLATFORM_IOS_H__*/ diff --git a/src/AppPlatform_iOS.mm b/src/AppPlatform_iOS.mm index 68f0d12..5350fbb 100755 --- a/src/AppPlatform_iOS.mm +++ b/src/AppPlatform_iOS.mm @@ -1,282 +1,282 @@ -#include "AppPlatform_iOS.h" -#import -#import - -#import "../project/iosproj/minecraftpe/minecraftpeViewController.h" -#import "../project/iosproj/minecraftpe/dialogs/BaseDialogController.h" -#import "../project/iosproj/minecraftpe/PVRTexture.h" -#import "client/gui/screens/DialogDefinitions.h" - -#import "terrain_565.h" - -typedef unsigned int PVRTuint32; - -struct PVR_Texture_Header -{ - PVRTuint32 dwHeaderSize; /*!< size of the structure */ - PVRTuint32 dwHeight; /*!< height of surface to be created */ - PVRTuint32 dwWidth; /*!< width of input surface */ - PVRTuint32 dwMipMapCount; /*!< number of mip-map levels requested */ - PVRTuint32 dwpfFlags; /*!< pixel format flags */ - PVRTuint32 dwTextureDataSize; /*!< Total size in bytes */ - PVRTuint32 dwBitCount; /*!< number of bits per pixel */ - PVRTuint32 dwRBitMask; /*!< mask for red bit */ - PVRTuint32 dwGBitMask; /*!< mask for green bits */ - PVRTuint32 dwBBitMask; /*!< mask for blue bits */ - PVRTuint32 dwAlphaBitMask; /*!< mask for alpha channel */ - PVRTuint32 dwPVR; /*!< magic number identifying pvr file */ - PVRTuint32 dwNumSurfs; /*!< the number of surfaces present in the pvr */ -} ; - -void AppPlatform_iOS::showDialog(int dialogId) { - if (dialogId == DialogDefinitions::DIALOG_CREATE_NEW_WORLD) { - [_viewController showDialog_CreateWorld]; - } - if (dialogId == DialogDefinitions::DIALOG_MAINMENU_OPTIONS) { - [_viewController showDialog_MainMenuOptions]; - } - if (dialogId == DialogDefinitions::DIALOG_RENAME_MP_WORLD) { - [_viewController showDialog_RenameMPWorld]; - } - if (dialogId == DialogDefinitions::DIALOG_DEMO_FEATURE_DISABLED) { - UIAlertView *a = [[UIAlertView alloc] - initWithTitle:@"" - message:@"Feature not enabled for this demo" - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil]; - [a show]; - [a release]; - } -} - -TextureData AppPlatform_iOS::loadTexture(const std::string& filename_, bool textureFolder) -{ - TextureData out; - out.memoryHandledExternally = false; - - std::string filename = filename_; - size_t dotp = filename.rfind("."); - size_t slashp = filename.rfind("/"); - if (dotp != std::string::npos || slashp != std::string::npos) { - if (slashp == std::string::npos) slashp = -1; - filename = filename.substr(slashp+1, dotp-(slashp+1)); - } - -// if (filename == "terrain" || filename_[dotp+2] == 'v') { // @fix -// //NSString *path = [[NSBundle mainBundle] pathForResource:[[NSString alloc] initWithUTF8String:filename.c_str()] ofType:@"pvr4"]; -// -// //FILE* fp = fopen([path UTF8String], "rb"); -// int fp = 1; -// if (fp) { -// PVR_Texture_Header header; -// header = *((PVR_Texture_Header*)terrain_565); -// //fread(&header, 1, sizeof(PVR_Texture_Header), fp); -// int numBytes = header.dwTextureDataSize; -// //out.data = new unsigned char[numBytes]; -// out.data = (unsigned char*)&terrain_565[header.dwHeaderSize]; -// out.memoryHandledExternally = true; -// out.numBytes = numBytes; -// out.transparent = (header.dwAlphaBitMask != 0); -// //fread(out.data, 1, numBytes, fp); -// out.w = header.dwWidth; -// out.h = header.dwHeight; -// LOGI("Size of file: %d (%d, %d) - %x,%x,%x,%x\n", out.numBytes, out.w, out.h, -// header.dwRBitMask, -// header.dwGBitMask, -// header.dwBBitMask, -// header.dwAlphaBitMask); -// out.format = TEXF_UNCOMPRESSED_565;// TEXF_COMPRESSED_PVRTC_5551; -// //fclose(fp); -// } -// -//// PVRTexture* tex = [PVRTexture pvrTextureWithContentsOfFile:path]; -//// //NSLog(@"path: %@, tex: %p, name: %d\n", path, tex, [tex name]); -//// out.identifier = [tex name]; -//// out.w = [tex width]; -//// out.h = [tex height]; -//// GLuint texId; -//// //PVRTTextureLoadFromPVR([path UTF8String], &texId); -//// out.identifier = texId; -// return out; -// } - - NSString *p = [[NSString alloc] initWithUTF8String:filename.c_str()]; - NSString *path = [[NSBundle mainBundle] pathForResource:p ofType:@"png"]; - [p release]; - NSData *texData = [[NSData alloc] initWithContentsOfFile:path]; - UIImage *image = [[UIImage alloc] initWithData:texData]; - - if (image != nil) { - // Get Image size - out.w = CGImageGetWidth(image.CGImage); - out.h = CGImageGetHeight(image.CGImage); - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - // Allocate memory for image - out.data = new unsigned char[4 * out.w * out.h]; - CGContextRef imgcontext = CGBitmapContextCreate( out.data, out.w, out.h, 8, 4 * out.w, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big ); - CGColorSpaceRelease( colorSpace ); - CGContextClearRect( imgcontext, CGRectMake( 0, 0, out.w, out.h ) ); - CGContextTranslateCTM( imgcontext, 0, 0);//height - height ); - CGContextDrawImage( imgcontext, CGRectMake( 0, 0, out.w, out.h ), image.CGImage ); - CGContextRelease(imgcontext); - } else { - LOGI("Couldn't find file: %s\n", filename.c_str()); - - if ("this is idiotic but temporary") { - out.w = 16; - out.h = 16; - bool isTerrain = (filename.find("terrain") != std::string::npos); - int numPixels = out.w * out.h; - out.data = new unsigned char[4 * numPixels]; - if (isTerrain) { - for (int i = 0; i < numPixels; ++i) { - unsigned int color = 0xff000000 | ((rand() & 0xff) << 16) | (rand() & 0xffff); - *((int*)(&out.data[4*i])) = color; - } - } else { - unsigned int color = 0xff000000 | ((rand() & 0xff) << 16) | (rand() & 0xffff); - for (int i = 0; i < numPixels; ++i) { - *((int*)(&out.data[4*i])) = color; - } - } - } - } - [image release]; - [texData release]; - - return out; -} - -BinaryBlob AppPlatform_iOS::readAssetFile(const std::string& filename_) { - std::string filename = filename_; - size_t dotp = filename.rfind("."); - size_t slashp = filename.rfind("/"); - std::string ext; - if (dotp != std::string::npos || slashp != std::string::npos) { - // Get file extension - if (dotp != std::string::npos) { - ext = filename.substr(dotp+1); - } - if (slashp == std::string::npos) slashp = -1; - filename = filename.substr(slashp+1, dotp-(slashp+1)); - } - NSString *rext = [NSString stringWithUTF8String:ext.c_str()]; - NSString *p = [[NSString alloc] initWithUTF8String:filename.c_str()]; - NSString *path = [[NSBundle mainBundle] pathForResource:p ofType:rext]; - [p release]; - NSData *data = [NSData dataWithContentsOfFile:path]; - if (!data) - return BinaryBlob(); - - unsigned int numBytes = [data length]; - unsigned char* bytes = new unsigned char[numBytes]; - memcpy(bytes, [data bytes], numBytes); - return BinaryBlob(bytes, numBytes); -} - -std::string AppPlatform_iOS::getDateString(int s) { - - NSDate* date = [NSDate dateWithTimeIntervalSince1970:s]; - - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - //dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm"; - - [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; - [dateFormatter setTimeStyle:NSDateFormatterShortStyle]; - - //NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; - //[dateFormatter setTimeZone:gmt]; - NSString *timeStamp = [dateFormatter stringFromDate:date]; - [dateFormatter release]; - - return std::string( [timeStamp UTF8String] ); -} - -int AppPlatform_iOS::getScreenWidth() { return 480; } -int AppPlatform_iOS::getScreenHeight() { return 320; } - -float AppPlatform_iOS::getPixelsPerMillimeter() { - // @note: @retina has a much higher density, however, - // we use 480x320 for the OpenGL context size - BOOL isIpad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad); - if (isIpad) { - return 5.1968503937007879f * _viewController->viewScale; - } else { - return 6.4173228346456694f * _viewController->viewScale; - } -} - -bool AppPlatform_iOS::isTouchscreen() { return true; } - -void AppPlatform_iOS::vibrate(int ms) { - // Note: In iOS 4, there's no way to set length of the vibration, so it's useless - //AudioServicesPlaySystemSound (kSystemSoundID_Vibrate); -} - -int AppPlatform_iOS::getUserInputStatus() { - return [_viewController getUserInputStatus]; -} - -StringVector AppPlatform_iOS::getUserInput() { - return [_viewController getUserInput]; -} - -StringVector AppPlatform_iOS::getOptionStrings() { - //@options - StringVector options; - NSDictionary* d = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]; - - for( NSString *key in d ) - { - if ([key hasPrefix:@"mp_"] - || [key hasPrefix:@"gfx_"] - || [key hasPrefix:@"ctrl_"] - || [key hasPrefix:@"feedback_"] - || [key hasPrefix:@"game_"] ) { - id value = [d objectForKey: key]; - options.push_back([key UTF8String]); - options.push_back([[value description] UTF8String]); - //LOGI("Added strings: %s\n", options[options.size()-1].c_str()); - } - } - return options; -} - -bool AppPlatform_iOS::isSuperFast() { - const char* s = (const char*)glGetString(GL_RENDERER); - if (!s) return false; - - return (strstr(s, "SGX") != NULL) && (strstr(s, "543") != NULL); -} - -bool AppPlatform_iOS::isNetworkEnabled(bool onlyWifiAllowed) { - return true; - /* - Reachability *reachability = [Reachability reachabilityForInternetConnection]; - [reachability startNotifier]; - - NetworkStatus status = [reachability currentReachabilityStatus]; - bool success = (status == ReachableViaWiFiNetwork); - if (!onlyWifiAllowed && !success) - success = (status == ReachableViaWWAN); - - [reachability stopNotifier]; - return success; - */ -} - -void AppPlatform_iOS::showKeyboard() { - [_viewController showKeyboard]; - super::showKeyboard(); -} - -void AppPlatform_iOS::hideKeyboard() { - [_viewController hideKeyboard]; - super::hideKeyboard(); -} -void AppPlatform_iOS::isPowerVR() { - const char* s = (const char*)glGetString(GL_RENDERER); - if (!s) return false; - return strstr(s, "SGX") != NULL; +#include "AppPlatform_iOS.h" +#import +#import + +#import "../project/iosproj/minecraftpe/minecraftpeViewController.h" +#import "../project/iosproj/minecraftpe/dialogs/BaseDialogController.h" +#import "../project/iosproj/minecraftpe/PVRTexture.h" +#import "client/gui/screens/DialogDefinitions.h" + +#import "terrain_565.h" + +typedef unsigned int PVRTuint32; + +struct PVR_Texture_Header +{ + PVRTuint32 dwHeaderSize; /*!< size of the structure */ + PVRTuint32 dwHeight; /*!< height of surface to be created */ + PVRTuint32 dwWidth; /*!< width of input surface */ + PVRTuint32 dwMipMapCount; /*!< number of mip-map levels requested */ + PVRTuint32 dwpfFlags; /*!< pixel format flags */ + PVRTuint32 dwTextureDataSize; /*!< Total size in bytes */ + PVRTuint32 dwBitCount; /*!< number of bits per pixel */ + PVRTuint32 dwRBitMask; /*!< mask for red bit */ + PVRTuint32 dwGBitMask; /*!< mask for green bits */ + PVRTuint32 dwBBitMask; /*!< mask for blue bits */ + PVRTuint32 dwAlphaBitMask; /*!< mask for alpha channel */ + PVRTuint32 dwPVR; /*!< magic number identifying pvr file */ + PVRTuint32 dwNumSurfs; /*!< the number of surfaces present in the pvr */ +} ; + +void AppPlatform_iOS::showDialog(int dialogId) { + if (dialogId == DialogDefinitions::DIALOG_CREATE_NEW_WORLD) { + [_viewController showDialog_CreateWorld]; + } + if (dialogId == DialogDefinitions::DIALOG_MAINMENU_OPTIONS) { + [_viewController showDialog_MainMenuOptions]; + } + if (dialogId == DialogDefinitions::DIALOG_RENAME_MP_WORLD) { + [_viewController showDialog_RenameMPWorld]; + } + if (dialogId == DialogDefinitions::DIALOG_DEMO_FEATURE_DISABLED) { + UIAlertView *a = [[UIAlertView alloc] + initWithTitle:@"" + message:@"Feature not enabled for this demo" + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [a show]; + [a release]; + } +} + +TextureData AppPlatform_iOS::loadTexture(const std::string& filename_, bool textureFolder) +{ + TextureData out; + out.memoryHandledExternally = false; + + std::string filename = filename_; + size_t dotp = filename.rfind("."); + size_t slashp = filename.rfind("/"); + if (dotp != std::string::npos || slashp != std::string::npos) { + if (slashp == std::string::npos) slashp = -1; + filename = filename.substr(slashp+1, dotp-(slashp+1)); + } + +// if (filename == "terrain" || filename_[dotp+2] == 'v') { // @fix +// //NSString *path = [[NSBundle mainBundle] pathForResource:[[NSString alloc] initWithUTF8String:filename.c_str()] ofType:@"pvr4"]; +// +// //FILE* fp = fopen([path UTF8String], "rb"); +// int fp = 1; +// if (fp) { +// PVR_Texture_Header header; +// header = *((PVR_Texture_Header*)terrain_565); +// //fread(&header, 1, sizeof(PVR_Texture_Header), fp); +// int numBytes = header.dwTextureDataSize; +// //out.data = new unsigned char[numBytes]; +// out.data = (unsigned char*)&terrain_565[header.dwHeaderSize]; +// out.memoryHandledExternally = true; +// out.numBytes = numBytes; +// out.transparent = (header.dwAlphaBitMask != 0); +// //fread(out.data, 1, numBytes, fp); +// out.w = header.dwWidth; +// out.h = header.dwHeight; +// LOGI("Size of file: %d (%d, %d) - %x,%x,%x,%x\n", out.numBytes, out.w, out.h, +// header.dwRBitMask, +// header.dwGBitMask, +// header.dwBBitMask, +// header.dwAlphaBitMask); +// out.format = TEXF_UNCOMPRESSED_565;// TEXF_COMPRESSED_PVRTC_5551; +// //fclose(fp); +// } +// +//// PVRTexture* tex = [PVRTexture pvrTextureWithContentsOfFile:path]; +//// //NSLog(@"path: %@, tex: %p, name: %d\n", path, tex, [tex name]); +//// out.identifier = [tex name]; +//// out.w = [tex width]; +//// out.h = [tex height]; +//// GLuint texId; +//// //PVRTTextureLoadFromPVR([path UTF8String], &texId); +//// out.identifier = texId; +// return out; +// } + + NSString *p = [[NSString alloc] initWithUTF8String:filename.c_str()]; + NSString *path = [[NSBundle mainBundle] pathForResource:p ofType:@"png"]; + [p release]; + NSData *texData = [[NSData alloc] initWithContentsOfFile:path]; + UIImage *image = [[UIImage alloc] initWithData:texData]; + + if (image != nil) { + // Get Image size + out.w = CGImageGetWidth(image.CGImage); + out.h = CGImageGetHeight(image.CGImage); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + // Allocate memory for image + out.data = new unsigned char[4 * out.w * out.h]; + CGContextRef imgcontext = CGBitmapContextCreate( out.data, out.w, out.h, 8, 4 * out.w, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big ); + CGColorSpaceRelease( colorSpace ); + CGContextClearRect( imgcontext, CGRectMake( 0, 0, out.w, out.h ) ); + CGContextTranslateCTM( imgcontext, 0, 0);//height - height ); + CGContextDrawImage( imgcontext, CGRectMake( 0, 0, out.w, out.h ), image.CGImage ); + CGContextRelease(imgcontext); + } else { + LOGI("Couldn't find file: %s\n", filename.c_str()); + + if ("this is idiotic but temporary") { + out.w = 16; + out.h = 16; + bool isTerrain = (filename.find("terrain") != std::string::npos); + int numPixels = out.w * out.h; + out.data = new unsigned char[4 * numPixels]; + if (isTerrain) { + for (int i = 0; i < numPixels; ++i) { + unsigned int color = 0xff000000 | ((rand() & 0xff) << 16) | (rand() & 0xffff); + *((int*)(&out.data[4*i])) = color; + } + } else { + unsigned int color = 0xff000000 | ((rand() & 0xff) << 16) | (rand() & 0xffff); + for (int i = 0; i < numPixels; ++i) { + *((int*)(&out.data[4*i])) = color; + } + } + } + } + [image release]; + [texData release]; + + return out; +} + +BinaryBlob AppPlatform_iOS::readAssetFile(const std::string& filename_) { + std::string filename = filename_; + size_t dotp = filename.rfind("."); + size_t slashp = filename.rfind("/"); + std::string ext; + if (dotp != std::string::npos || slashp != std::string::npos) { + // Get file extension + if (dotp != std::string::npos) { + ext = filename.substr(dotp+1); + } + if (slashp == std::string::npos) slashp = -1; + filename = filename.substr(slashp+1, dotp-(slashp+1)); + } + NSString *rext = [NSString stringWithUTF8String:ext.c_str()]; + NSString *p = [[NSString alloc] initWithUTF8String:filename.c_str()]; + NSString *path = [[NSBundle mainBundle] pathForResource:p ofType:rext]; + [p release]; + NSData *data = [NSData dataWithContentsOfFile:path]; + if (!data) + return BinaryBlob(); + + unsigned int numBytes = [data length]; + unsigned char* bytes = new unsigned char[numBytes]; + memcpy(bytes, [data bytes], numBytes); + return BinaryBlob(bytes, numBytes); +} + +std::string AppPlatform_iOS::getDateString(int s) { + + NSDate* date = [NSDate dateWithTimeIntervalSince1970:s]; + + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + //dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm"; + + [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; + [dateFormatter setTimeStyle:NSDateFormatterShortStyle]; + + //NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; + //[dateFormatter setTimeZone:gmt]; + NSString *timeStamp = [dateFormatter stringFromDate:date]; + [dateFormatter release]; + + return std::string( [timeStamp UTF8String] ); +} + +int AppPlatform_iOS::getScreenWidth() { return 480; } +int AppPlatform_iOS::getScreenHeight() { return 320; } + +float AppPlatform_iOS::getPixelsPerMillimeter() { + // @note: @retina has a much higher density, however, + // we use 480x320 for the OpenGL context size + BOOL isIpad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad); + if (isIpad) { + return 5.1968503937007879f * _viewController->viewScale; + } else { + return 6.4173228346456694f * _viewController->viewScale; + } +} + +bool AppPlatform_iOS::isTouchscreen() { return true; } + +void AppPlatform_iOS::vibrate(int ms) { + // Note: In iOS 4, there's no way to set length of the vibration, so it's useless + //AudioServicesPlaySystemSound (kSystemSoundID_Vibrate); +} + +int AppPlatform_iOS::getUserInputStatus() { + return [_viewController getUserInputStatus]; +} + +StringVector AppPlatform_iOS::getUserInput() { + return [_viewController getUserInput]; +} + +StringVector AppPlatform_iOS::getOptionStrings() { + //@options + StringVector options; + NSDictionary* d = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]; + + for( NSString *key in d ) + { + if ([key hasPrefix:@"mp_"] + || [key hasPrefix:@"gfx_"] + || [key hasPrefix:@"ctrl_"] + || [key hasPrefix:@"feedback_"] + || [key hasPrefix:@"game_"] ) { + id value = [d objectForKey: key]; + options.push_back([key UTF8String]); + options.push_back([[value description] UTF8String]); + //LOGI("Added strings: %s\n", options[options.size()-1].c_str()); + } + } + return options; +} + +bool AppPlatform_iOS::isSuperFast() { + const char* s = (const char*)glGetString(GL_RENDERER); + if (!s) return false; + + return (strstr(s, "SGX") != NULL) && (strstr(s, "543") != NULL); +} + +bool AppPlatform_iOS::isNetworkEnabled(bool onlyWifiAllowed) { + return true; + /* + Reachability *reachability = [Reachability reachabilityForInternetConnection]; + [reachability startNotifier]; + + NetworkStatus status = [reachability currentReachabilityStatus]; + bool success = (status == ReachableViaWiFiNetwork); + if (!onlyWifiAllowed && !success) + success = (status == ReachableViaWWAN); + + [reachability stopNotifier]; + return success; + */ +} + +void AppPlatform_iOS::showKeyboard() { + [_viewController showKeyboard]; + super::showKeyboard(); +} + +void AppPlatform_iOS::hideKeyboard() { + [_viewController hideKeyboard]; + super::hideKeyboard(); +} +void AppPlatform_iOS::isPowerVR() { + const char* s = (const char*)glGetString(GL_RENDERER); + if (!s) return false; + return strstr(s, "SGX") != NULL; } \ No newline at end of file diff --git a/src/AppPlatform_win32.cpp b/src/AppPlatform_win32.cpp index f193e7c..c1596e2 100755 --- a/src/AppPlatform_win32.cpp +++ b/src/AppPlatform_win32.cpp @@ -1,16 +1,16 @@ -#include "AppPlatform_win32.h" -#include "util/Mth.h" - -int AppPlatform_win32::getScreenWidth() { return 854; } -int AppPlatform_win32::getScreenHeight() { return 480; } - -float AppPlatform_win32::getPixelsPerMillimeter() { - // assuming 24" @ 1920x1200 - const int w = 1920; - const int h = 1200; - const float pixels = Mth::sqrt(w*w + h*h); - const float mm = 24 * 25.4f; - return pixels / mm; -} - -bool AppPlatform_win32::supportsTouchscreen() { return true; } +#include "AppPlatform_win32.h" +#include "util/Mth.h" + +int AppPlatform_win32::getScreenWidth() { return 854; } +int AppPlatform_win32::getScreenHeight() { return 480; } + +float AppPlatform_win32::getPixelsPerMillimeter() { + // assuming 24" @ 1920x1200 + const int w = 1920; + const int h = 1200; + const float pixels = Mth::sqrt(w*w + h*h); + const float mm = 24 * 25.4f; + return pixels / mm; +} + +bool AppPlatform_win32::supportsTouchscreen() { return true; } diff --git a/src/AppPlatform_win32.h b/src/AppPlatform_win32.h index c3623b3..f56dc64 100755 --- a/src/AppPlatform_win32.h +++ b/src/AppPlatform_win32.h @@ -1,142 +1,142 @@ -#ifndef APPPLATFORM_WIN32_H__ -#define APPPLATFORM_WIN32_H__ - -#include "AppPlatform.h" -#include "platform/log.h" -#include "platform/HttpClient.h" -#include "platform/PngLoader.h" -#include "client/renderer/gles.h" -#include "world/level/storage/FolderMethods.h" -#include "util/StringUtils.h" -#include -#include -#include -#include -#include -#include - -static void png_funcReadFile(png_structp pngPtr, png_bytep data, png_size_t length) { - ((std::istream*)png_get_io_ptr(pngPtr))->read((char*)data, length); -} - -class AppPlatform_win32: public AppPlatform -{ -public: - AppPlatform_win32() - { - } - - BinaryBlob readAssetFile(const std::string& filename) { - FILE* fp = fopen(("data/" + filename).c_str(), "r"); - if (!fp) - return BinaryBlob(); - - int size = getRemainingFileSize(fp); - - BinaryBlob blob; - blob.size = size; - blob.data = new unsigned char[size]; - - fread(blob.data, 1, size, fp); - fclose(fp); - - return blob; - } - - void saveScreenshot(const std::string& filename, int glWidth, int glHeight) { - //@todo - } - - __inline unsigned int rgbToBgr(unsigned int p) { - return (p & 0xff00ff00) | ((p >> 16) & 0xff) | ((p << 16) & 0xff0000); - } - - TextureData loadTexture(const std::string& filename_, bool textureFolder) - { - // Support fetching PNG textures via HTTP/HTTPS (for skins, etc). - if (Util::startsWith(filename_, "http://") || Util::startsWith(filename_, "https://")) { - std::vector body; - if (HttpClient::download(filename_, body) && !body.empty()) { - return loadTextureFromMemory(body.data(), body.size()); - } - return TextureData(); - } - - TextureData out; - - std::string filename = textureFolder? "data/images/" + filename_ - : filename_; - std::ifstream source(filename.c_str(), std::ios::binary); - - if (source) { - png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - - if (!pngPtr) - return out; - - png_infop infoPtr = png_create_info_struct(pngPtr); - - if (!infoPtr) { - png_destroy_read_struct(&pngPtr, NULL, NULL); - return out; - } - - // Hack to get around the broken libpng for windows - png_set_read_fn(pngPtr,(void*)&source, png_funcReadFile); - - png_read_info(pngPtr, infoPtr); - - // Set up the texdata properties - out.w = png_get_image_width(pngPtr, infoPtr); - out.h = png_get_image_height(pngPtr, infoPtr); - - png_bytep* rowPtrs = new png_bytep[out.h]; - out.data = new unsigned char[4 * out.w * out.h]; - out.memoryHandledExternally = false; - - int rowStrideBytes = 4 * out.w; - for (int i = 0; i < out.h; i++) { - rowPtrs[i] = (png_bytep)&out.data[i*rowStrideBytes]; - } - png_read_image(pngPtr, rowPtrs); - - // Teardown and return - png_destroy_read_struct(&pngPtr, &infoPtr,(png_infopp)0); - delete[] (png_bytep)rowPtrs; - source.close(); - - return out; - } - else - { - LOGI("Couldn't find file: %s\n", filename.c_str()); - return out; - } - } - - TextureData loadTextureFromMemory(const unsigned char* data, size_t size) override { - return loadPngFromMemory(data, size); - } - time_t tm = s; - - char mbstr[100]; - std::strftime(mbstr, sizeof(mbstr), "%F %T", std::localtime(&tm)); - - return std::string(mbstr); - } - - virtual int getScreenWidth(); - virtual int getScreenHeight(); - - virtual float getPixelsPerMillimeter(); - - virtual bool supportsTouchscreen(); - - virtual void openURL(const std::string& url) { - ShellExecuteA(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); - } - -private: -}; - -#endif /*APPPLATFORM_WIN32_H__*/ +#ifndef APPPLATFORM_WIN32_H__ +#define APPPLATFORM_WIN32_H__ + +#include "AppPlatform.h" +#include "platform/log.h" +#include "platform/HttpClient.h" +#include "platform/PngLoader.h" +#include "client/renderer/gles.h" +#include "world/level/storage/FolderMethods.h" +#include "util/StringUtils.h" +#include +#include +#include +#include +#include +#include + +static void png_funcReadFile(png_structp pngPtr, png_bytep data, png_size_t length) { + ((std::istream*)png_get_io_ptr(pngPtr))->read((char*)data, length); +} + +class AppPlatform_win32: public AppPlatform +{ +public: + AppPlatform_win32() + { + } + + BinaryBlob readAssetFile(const std::string& filename) { + FILE* fp = fopen(("data/" + filename).c_str(), "r"); + if (!fp) + return BinaryBlob(); + + int size = getRemainingFileSize(fp); + + BinaryBlob blob; + blob.size = size; + blob.data = new unsigned char[size]; + + fread(blob.data, 1, size, fp); + fclose(fp); + + return blob; + } + + void saveScreenshot(const std::string& filename, int glWidth, int glHeight) { + //@todo + } + + __inline unsigned int rgbToBgr(unsigned int p) { + return (p & 0xff00ff00) | ((p >> 16) & 0xff) | ((p << 16) & 0xff0000); + } + + TextureData loadTexture(const std::string& filename_, bool textureFolder) + { + // Support fetching PNG textures via HTTP/HTTPS (for skins, etc). + if (Util::startsWith(filename_, "http://") || Util::startsWith(filename_, "https://")) { + std::vector body; + if (HttpClient::download(filename_, body) && !body.empty()) { + return loadTextureFromMemory(body.data(), body.size()); + } + return TextureData(); + } + + TextureData out; + + std::string filename = textureFolder? "data/images/" + filename_ + : filename_; + std::ifstream source(filename.c_str(), std::ios::binary); + + if (source) { + png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!pngPtr) + return out; + + png_infop infoPtr = png_create_info_struct(pngPtr); + + if (!infoPtr) { + png_destroy_read_struct(&pngPtr, NULL, NULL); + return out; + } + + // Hack to get around the broken libpng for windows + png_set_read_fn(pngPtr,(void*)&source, png_funcReadFile); + + png_read_info(pngPtr, infoPtr); + + // Set up the texdata properties + out.w = png_get_image_width(pngPtr, infoPtr); + out.h = png_get_image_height(pngPtr, infoPtr); + + png_bytep* rowPtrs = new png_bytep[out.h]; + out.data = new unsigned char[4 * out.w * out.h]; + out.memoryHandledExternally = false; + + int rowStrideBytes = 4 * out.w; + for (int i = 0; i < out.h; i++) { + rowPtrs[i] = (png_bytep)&out.data[i*rowStrideBytes]; + } + png_read_image(pngPtr, rowPtrs); + + // Teardown and return + png_destroy_read_struct(&pngPtr, &infoPtr,(png_infopp)0); + delete[] (png_bytep)rowPtrs; + source.close(); + + return out; + } + else + { + LOGI("Couldn't find file: %s\n", filename.c_str()); + return out; + } + } + + TextureData loadTextureFromMemory(const unsigned char* data, size_t size) override { + return loadPngFromMemory(data, size); + } + time_t tm = s; + + char mbstr[100]; + std::strftime(mbstr, sizeof(mbstr), "%F %T", std::localtime(&tm)); + + return std::string(mbstr); + } + + virtual int getScreenWidth(); + virtual int getScreenHeight(); + + virtual float getPixelsPerMillimeter(); + + virtual bool supportsTouchscreen(); + + virtual void openURL(const std::string& url) { + ShellExecuteA(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); + } + +private: +}; + +#endif /*APPPLATFORM_WIN32_H__*/ diff --git a/src/ErrorCodes.h b/src/ErrorCodes.h index 0820847..8893ed7 100755 --- a/src/ErrorCodes.h +++ b/src/ErrorCodes.h @@ -1,13 +1,13 @@ -#ifndef ERRORCODES_H__ -#define ERRORCODES_H__ - -namespace ErrorCodes { - -enum Enum { - Unknown, - ContainerRefStillExistsAfterDestruction -}; - -} - -#endif /*ERRORCODES_H__*/ +#ifndef ERRORCODES_H__ +#define ERRORCODES_H__ + +namespace ErrorCodes { + +enum Enum { + Unknown, + ContainerRefStillExistsAfterDestruction +}; + +} + +#endif /*ERRORCODES_H__*/ diff --git a/src/LicenseCodes.h b/src/LicenseCodes.h index f62f42b..dddd5a0 100755 --- a/src/LicenseCodes.h +++ b/src/LicenseCodes.h @@ -1,40 +1,40 @@ -#ifndef LICENSECODES_H__ -#define LICENSECODES_H__ - -class LicenseCodes -{ -public: - // Something's not ready, call again later - static const int WAIT_PLATFORM_NOT_READY = -2; - static const int WAIT_SERVER_NOT_READY = -1; - - // License is ok - static const int LICENSE_OK = 0; - static const int LICENSE_TRIAL_OK = 1; - - // License is not working in one way or another - static const int LICENSE_VALIDATION_FAILED = 50; - static const int ITEM_NOT_FOUND = 51; - static const int LICENSE_NOT_FOUND = 52; - static const int ERROR_CONTENT_HANDLER = 100; - static const int ERROR_ILLEGAL_ARGUMENT = 101; - static const int ERROR_SECURITY = 102; - static const int ERROR_INPUT_OUTPUT = 103; - static const int ERROR_ILLEGAL_STATE = 104; - static const int ERROR_NULL_POINTER = 105; - static const int ERROR_GENERAL = 106; - static const int ERROR_UNABLE_TO_CONNECT_TO_CDS = 107; - - // The call went wrong so we didn't get a license value at all - static const int ERROR_EXCEPTION = 200; - - static bool isOk(int i) { - return (i == 0) || (i == 1); - } - static bool isReady(int i) { - return (i >= 0); - } -}; - -#endif /*LICENSECODES_H__ */ - +#ifndef LICENSECODES_H__ +#define LICENSECODES_H__ + +class LicenseCodes +{ +public: + // Something's not ready, call again later + static const int WAIT_PLATFORM_NOT_READY = -2; + static const int WAIT_SERVER_NOT_READY = -1; + + // License is ok + static const int LICENSE_OK = 0; + static const int LICENSE_TRIAL_OK = 1; + + // License is not working in one way or another + static const int LICENSE_VALIDATION_FAILED = 50; + static const int ITEM_NOT_FOUND = 51; + static const int LICENSE_NOT_FOUND = 52; + static const int ERROR_CONTENT_HANDLER = 100; + static const int ERROR_ILLEGAL_ARGUMENT = 101; + static const int ERROR_SECURITY = 102; + static const int ERROR_INPUT_OUTPUT = 103; + static const int ERROR_ILLEGAL_STATE = 104; + static const int ERROR_NULL_POINTER = 105; + static const int ERROR_GENERAL = 106; + static const int ERROR_UNABLE_TO_CONNECT_TO_CDS = 107; + + // The call went wrong so we didn't get a license value at all + static const int ERROR_EXCEPTION = 200; + + static bool isOk(int i) { + return (i == 0) || (i == 1); + } + static bool isReady(int i) { + return (i >= 0); + } +}; + +#endif /*LICENSECODES_H__ */ + diff --git a/src/NinecraftApp.cpp b/src/NinecraftApp.cpp index d2feea6..f7a7b57 100755 --- a/src/NinecraftApp.cpp +++ b/src/NinecraftApp.cpp @@ -1,428 +1,428 @@ -#include "NinecraftApp.h" -//#include - -#ifdef RPI -//#define NO_STORAGE -#endif - -#include - -#include "platform/input/Mouse.h" -#include "platform/input/Multitouch.h" - -#include "world/item/Item.h" -#include "world/level/Level.h" -#include "world/level/biome/Biome.h" -#include "world/level/material/Material.h" -#include "world/entity/MobCategory.h" -//#include "world/level/storage/FolderMethods.h" -#ifndef STANDALONE_SERVER -#include "client/gui/screens/StartMenuScreen.h" -#include "client/gui/screens/UsernameScreen.h" -#endif -#include "client/player/LocalPlayer.h" -#ifndef STANDALONE_SERVER -#include "client/renderer/gles.h" -#include "client/renderer/Chunk.h" -#include "client/renderer/LevelRenderer.h" -#include "client/renderer/Tesselator.h" -#endif -// sorry for raknet dependency, but I'm too lazy to find another getTime method -#include "raknet/GetTime.h" -#include "network/RakNetInstance.h" -#include "network/ClientSideNetworkHandler.h" -#include "client/gui/screens/ProgressScreen.h" - -//#include "world/entity/player/Inventory2.h" - -#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION) && !defined(NO_STORAGE) - #include "world/level/storage/ExternalFileLevelStorageSource.h" -#else - #include "world/level/storage/MemoryLevelStorageSource.h" -#endif -#ifndef STANDALONE_SERVER -#include "client/renderer/Textures.h" -#include "client/renderer/entity/ItemRenderer.h" -#endif -#include "world/item/crafting/Recipes.h" -#include "world/level/tile/entity/TileEntity.h" -#ifndef STANDALONE_SERVER -#include "client/renderer/EntityTileRenderer.h" -#endif - -bool NinecraftApp::_hasInitedStatics = false; - -NinecraftApp::NinecraftApp() -: _verbose(true), - _lastTickMs(0), - _frames(0) -{ - #if defined(ANDROID) || defined(__APPLE__) || defined(RPI) - signal(SIGPIPE, SIG_IGN); - #endif -} - -NinecraftApp::~NinecraftApp() -{ - teardown(); -} - -void NinecraftApp::init() -{ - // Global initialization goes here - Mth::initMth(); - - //#ifdef DEMO_MODE - //writeDemoFile(); - //#endif - - // If we're running Android, only initialize - // the first time class is instanced - #ifdef ANDROID - if (!_hasInitedStatics) { - _hasInitedStatics = true; - #endif - Material::initMaterials(); - MobCategory::initMobCategories(); - Tile::initTiles(); - Item::initItems(); - Biome::initBiomes(); - TileEntity::initTileEntities(); - - #ifdef ANDROID - } - #endif - -#ifndef STANDALONE_SERVER - initGLStates(); - Tesselator::instance.init(); - I18n::loadLanguage(platform(), "en_US"); -#endif - - if (!externalStoragePath.empty()) { - options.setOptionsFilePath(externalStoragePath); - } - Minecraft::init(); - -#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION) && !defined(NO_STORAGE) - storageSource = new ExternalFileLevelStorageSource(externalStoragePath, externalCacheStoragePath); -#else - storageSource = new MemoryLevelStorageSource(); -#endif - _running = false; - -#ifndef STANDALONE_SERVER - LOGI("This: %p\n", this); - screenChooser.setScreen(SCREEN_STARTMENU); - - if (options.getBooleanValue(OPTIONS_FIRST_LAUNCH)) { - options.toggle(OPTIONS_FIRST_LAUNCH); - setScreen(new UsernameScreen()); - } -#else - hostMultiplayer(); -#endif -} - -void NinecraftApp::teardown() -{ - // Note: Don't tear down statics if we run on Android - // (we might change this in the future) -#ifndef ANDROID - Biome::teardownBiomes(); - Item ::teardownItems(); - Tile ::teardownTiles(); - Material::teardownMaterials(); - Recipes ::teardownRecipes(); - TileEntity::teardownTileEntities(); -#endif -#ifdef WIN32 - ItemRenderer::teardown_static(); - if (EntityTileRenderer::instance != NULL) { - delete EntityTileRenderer::instance; - EntityTileRenderer::instance = NULL; - } - TileEntityRenderDispatcher::destroy(); -#endif -} - -void NinecraftApp::update() -{ - ++_frames; - - // Generate Multitouch active pointer list - Multitouch::commit(); - -#ifndef ANDROID_PUBLISH - //testCreationAndDestruction(); - //testJoiningAndDestruction(); -#endif /*ANDROID_PUBLISH*/ - - Minecraft::update(); - - swapBuffers(); - Mouse::reset2(); - - // Restart the server if (our modded) RakNet reports an error - if (level && raknetInstance->isProbablyBroken() && raknetInstance->isServer()) { - restartServer(); - } - -#ifndef WIN32 - updateStats(); -#endif -} - -void NinecraftApp::updateStats() -{ -#ifndef STANDALONE_SERVER - if (Options::debugGl) - LOGI("--------------------------------------------\n"); - - //* - int now = getTimeMs(); - //int since = now - _lastTickMs; - - if (now >= lastTime + 1000) - { - if (player) { - LOGI("%d fps \t%3d chunk updates. (%.2f, %.2f, %.2f)\n", - _frames, Chunk::updates, player->x, player->y, player->z); - Chunk::resetUpdates(); - - //static int _n = 0; - //if (++_n % 5 == -1) { // @note: -1 - // static char filename[256]; - // sprintf(filename, "%s/games/com.mojang/img_%.4d.jpg", externalStoragePath.c_str(), _n/5); - // _context.platform->saveScreenshot(filename, width, height); - //} - - LOGI("%s", levelRenderer->gatherStats1().c_str()); - //printf("Texture swaps (this frame): %d\n", Textures::textureChanges); - } else { - LOGI("%d fps\n", _frames); - } - - //const int* pointerIds; - //int pointerCount = Multitouch::getActivePointerIds(&pointerIds); - //if (pointerCount) { - // std::string s = "Pointers ("; - // s += (char)(48 + pointerCount); - // s += ": "; - // for (int i = 0; i < pointerCount; ++i) { - // s += (char)(48 + pointerIds[i]); - // s += ", "; - // } - // LOGI("%s\n", s.c_str()); - //} - - lastTime = now; - _frames = 0; -#ifdef GLDEBUG - while (1) { - int error = glGetError(); - if (error == GL_NO_ERROR) break; - - LOGI("#################### GL-ERROR: %d\t#####################\n", error); - LOGI("#################### GL-ERROR: %d\t#####################\n", error); - LOGI("#################### GL-ERROR: %d\t#####################\n", error); - } -#endif - } - Textures::textureChanges = 0; - /**/ -#endif /* STANDALONE_SERVER */ -} - -void NinecraftApp::initGLStates() -{ -#ifndef STANDALONE_SERVER - //glShadeModel2(GL_SMOOTH); - //glClearDepthf(1.0f); - glEnable2(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glDepthRangef(0, 1); - glEnable2(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.1f); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - glEnable2(GL_TEXTURE_2D); -#ifndef _EMSCRIPTEN_ - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); -#endif - // Both updates isPowerVR flag in java and returns if the graphics chip is PowerVR SGX or not - _powerVr = platform()->isPowerVR(); -#ifdef __APPLE__ - _isSuperFast = platform()->isSuperFast(); -#endif - //glLineWidth(4); -#endif /* STANDALONE_SERVER */ -} - -void NinecraftApp::restartServer() { - if (!level) return; - - for (int i = level->players.size()-1; i >= 0; --i) { - Player* p = level->players[i]; - if (p != player) - level->removeEntity(p); - } - - raknetInstance->resetIsBroken(); -#ifndef STANDALONE_SERVER - gui.addMessage("This server has restarted!"); -#endif - hostMultiplayer(); - if (netCallback) netCallback->levelGenerated(level); -} - -bool NinecraftApp::handleBack(bool isDown) -{ - if (isGeneratingLevel) - { - return true; - } - if (level) - { - if (!isDown) - { - if (screen) - { - if (!screen->handleBackEvent(isDown)) - { - if (player->containerMenu) player->closeContainer(); - setScreen(NULL); - } - return true; - } else { - pauseGame(true); - } - //leaveGame(); - return false; - } - return true; - } - else if (screen) - { - return screen->handleBackEvent(isDown); - } - return false; -} - -void NinecraftApp::onGraphicsReset() -{ -#ifndef STANDALONE_SERVER - initGLStates(); - Tesselator::instance.init(); - - Minecraft::onGraphicsReset(); -#endif -} - -#ifndef ANDROID_PUBLISH - -static int _state = -1; -static int _stateTicksLeft = 0; -void NinecraftApp::testCreationAndDestruction() -{ - if (_state == -1) { - _stateTicksLeft = 100; - _state = 0; - } - if (_state == 0) { - if (--_stateTicksLeft <= 0) - _state = 1; - } - else if (_state == 1) { - getLevelSource()->deleteLevel("perf"); - int seed = getEpochTimeS(); - LOGI(">seed %d\n", seed); - selectLevel("perf", "perf", LevelSettings(seed, GameType::Creative)); - hostMultiplayer(); -#ifndef STANDALONE_SERVER - setScreen(new ProgressScreen()); -#endif - _state = 2; - _stateTicksLeft = 1000;//25000;//00; - } - else if (_state == 2) { - if (isLevelGenerated()) { - if (--_stateTicksLeft <= 0) { - _state = 3; - } - } - } else if (_state == 3) { - leaveGame(); - _state = 1; - } -} - - -void NinecraftApp::testJoiningAndDestruction() -{ - if (_state == -1) { - //LightUpdate sz[2] = { LightUpdate(LightLayer::Block, 0, 0, 0, 1, 1, 1), - // LightUpdate(LightLayer::Sky, 0, 0, 0, 1, 1, 1) }; - //LOGI("size of lightupdate: %lu == %d\n", sizeof(LightUpdate), (const char*)&sz[1] - (const char*)&sz[0]); - _stateTicksLeft = 100; - _state = 0; - } - if (_state == 0) { - if (--_stateTicksLeft <= 0) { - raknetInstance->clearServerList(); - locateMultiplayer(); - _state = 1; - } - } - else if (_state == 1) { - if (!raknetInstance->getServerList().empty()) { - PingedCompatibleServer s = raknetInstance->getServerList().at(0); - if (s.name.GetLength() > 0) { - joinMultiplayer(s); -#ifndef STANDALONE_SERVER - setScreen(new ProgressScreen()); -#endif - _state = 2; - _stateTicksLeft = 80;//1000; - } - } - } - else if (_state == 2) { - if (isLevelGenerated()) { - if (--_stateTicksLeft <= 0) { - _state = 3; - } - } - } else if (_state == 3) { - leaveGame(); - _stateTicksLeft = 50; - _state = 0; - } -} - -#endif /*ANDROID_PUBLISH*/ - -/* -void NinecraftApp::writeDemoFile() { - std::string path = externalStoragePath + "/games"; - - if (createFolderIfNotExists(path.c_str())) { - path += "/com.mojang"; - if (createFolderIfNotExists(path.c_str())) { - path += "/minecraftpe"; - if (createFolderIfNotExists(path.c_str())) { - path += "/played_demo"; - FILE* fp = fopen(path.c_str(), "w"); - if (fp) fclose(fp); - }}} -} - -bool NinecraftApp::hasPlayedDemo() { - std::string filename = externalStoragePath + "/games/com.mojang/minecraftpe/played_demo"; - FILE* fp = fopen(filename.c_str(), "r"); - if (!fp) return false; - fclose(fp); - return true; -} -*/ +#include "NinecraftApp.h" +//#include + +#ifdef RPI +//#define NO_STORAGE +#endif + +#include + +#include "platform/input/Mouse.h" +#include "platform/input/Multitouch.h" + +#include "world/item/Item.h" +#include "world/level/Level.h" +#include "world/level/biome/Biome.h" +#include "world/level/material/Material.h" +#include "world/entity/MobCategory.h" +//#include "world/level/storage/FolderMethods.h" +#ifndef STANDALONE_SERVER +#include "client/gui/screens/StartMenuScreen.h" +#include "client/gui/screens/UsernameScreen.h" +#endif +#include "client/player/LocalPlayer.h" +#ifndef STANDALONE_SERVER +#include "client/renderer/gles.h" +#include "client/renderer/Chunk.h" +#include "client/renderer/LevelRenderer.h" +#include "client/renderer/Tesselator.h" +#endif +// sorry for raknet dependency, but I'm too lazy to find another getTime method +#include "raknet/GetTime.h" +#include "network/RakNetInstance.h" +#include "network/ClientSideNetworkHandler.h" +#include "client/gui/screens/ProgressScreen.h" + +//#include "world/entity/player/Inventory2.h" + +#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION) && !defined(NO_STORAGE) + #include "world/level/storage/ExternalFileLevelStorageSource.h" +#else + #include "world/level/storage/MemoryLevelStorageSource.h" +#endif +#ifndef STANDALONE_SERVER +#include "client/renderer/Textures.h" +#include "client/renderer/entity/ItemRenderer.h" +#endif +#include "world/item/crafting/Recipes.h" +#include "world/level/tile/entity/TileEntity.h" +#ifndef STANDALONE_SERVER +#include "client/renderer/EntityTileRenderer.h" +#endif + +bool NinecraftApp::_hasInitedStatics = false; + +NinecraftApp::NinecraftApp() +: _verbose(true), + _lastTickMs(0), + _frames(0) +{ + #if defined(ANDROID) || defined(__APPLE__) || defined(RPI) + signal(SIGPIPE, SIG_IGN); + #endif +} + +NinecraftApp::~NinecraftApp() +{ + teardown(); +} + +void NinecraftApp::init() +{ + // Global initialization goes here + Mth::initMth(); + + //#ifdef DEMO_MODE + //writeDemoFile(); + //#endif + + // If we're running Android, only initialize + // the first time class is instanced + #ifdef ANDROID + if (!_hasInitedStatics) { + _hasInitedStatics = true; + #endif + Material::initMaterials(); + MobCategory::initMobCategories(); + Tile::initTiles(); + Item::initItems(); + Biome::initBiomes(); + TileEntity::initTileEntities(); + + #ifdef ANDROID + } + #endif + +#ifndef STANDALONE_SERVER + initGLStates(); + Tesselator::instance.init(); + I18n::loadLanguage(platform(), "en_US"); +#endif + + if (!externalStoragePath.empty()) { + options.setOptionsFilePath(externalStoragePath); + } + Minecraft::init(); + +#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION) && !defined(NO_STORAGE) + storageSource = new ExternalFileLevelStorageSource(externalStoragePath, externalCacheStoragePath); +#else + storageSource = new MemoryLevelStorageSource(); +#endif + _running = false; + +#ifndef STANDALONE_SERVER + LOGI("This: %p\n", this); + screenChooser.setScreen(SCREEN_STARTMENU); + + if (options.getBooleanValue(OPTIONS_FIRST_LAUNCH)) { + options.toggle(OPTIONS_FIRST_LAUNCH); + setScreen(new UsernameScreen()); + } +#else + hostMultiplayer(); +#endif +} + +void NinecraftApp::teardown() +{ + // Note: Don't tear down statics if we run on Android + // (we might change this in the future) +#ifndef ANDROID + Biome::teardownBiomes(); + Item ::teardownItems(); + Tile ::teardownTiles(); + Material::teardownMaterials(); + Recipes ::teardownRecipes(); + TileEntity::teardownTileEntities(); +#endif +#ifdef WIN32 + ItemRenderer::teardown_static(); + if (EntityTileRenderer::instance != NULL) { + delete EntityTileRenderer::instance; + EntityTileRenderer::instance = NULL; + } + TileEntityRenderDispatcher::destroy(); +#endif +} + +void NinecraftApp::update() +{ + ++_frames; + + // Generate Multitouch active pointer list + Multitouch::commit(); + +#ifndef ANDROID_PUBLISH + //testCreationAndDestruction(); + //testJoiningAndDestruction(); +#endif /*ANDROID_PUBLISH*/ + + Minecraft::update(); + + swapBuffers(); + Mouse::reset2(); + + // Restart the server if (our modded) RakNet reports an error + if (level && raknetInstance->isProbablyBroken() && raknetInstance->isServer()) { + restartServer(); + } + +#ifndef WIN32 + updateStats(); +#endif +} + +void NinecraftApp::updateStats() +{ +#ifndef STANDALONE_SERVER + if (Options::debugGl) + LOGI("--------------------------------------------\n"); + + //* + int now = getTimeMs(); + //int since = now - _lastTickMs; + + if (now >= lastTime + 1000) + { + if (player) { + LOGI("%d fps \t%3d chunk updates. (%.2f, %.2f, %.2f)\n", + _frames, Chunk::updates, player->x, player->y, player->z); + Chunk::resetUpdates(); + + //static int _n = 0; + //if (++_n % 5 == -1) { // @note: -1 + // static char filename[256]; + // sprintf(filename, "%s/games/com.mojang/img_%.4d.jpg", externalStoragePath.c_str(), _n/5); + // _context.platform->saveScreenshot(filename, width, height); + //} + + LOGI("%s", levelRenderer->gatherStats1().c_str()); + //printf("Texture swaps (this frame): %d\n", Textures::textureChanges); + } else { + LOGI("%d fps\n", _frames); + } + + //const int* pointerIds; + //int pointerCount = Multitouch::getActivePointerIds(&pointerIds); + //if (pointerCount) { + // std::string s = "Pointers ("; + // s += (char)(48 + pointerCount); + // s += ": "; + // for (int i = 0; i < pointerCount; ++i) { + // s += (char)(48 + pointerIds[i]); + // s += ", "; + // } + // LOGI("%s\n", s.c_str()); + //} + + lastTime = now; + _frames = 0; +#ifdef GLDEBUG + while (1) { + int error = glGetError(); + if (error == GL_NO_ERROR) break; + + LOGI("#################### GL-ERROR: %d\t#####################\n", error); + LOGI("#################### GL-ERROR: %d\t#####################\n", error); + LOGI("#################### GL-ERROR: %d\t#####################\n", error); + } +#endif + } + Textures::textureChanges = 0; + /**/ +#endif /* STANDALONE_SERVER */ +} + +void NinecraftApp::initGLStates() +{ +#ifndef STANDALONE_SERVER + //glShadeModel2(GL_SMOOTH); + //glClearDepthf(1.0f); + glEnable2(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthRangef(0, 1); + glEnable2(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.1f); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + glEnable2(GL_TEXTURE_2D); +#ifndef _EMSCRIPTEN_ + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); +#endif + // Both updates isPowerVR flag in java and returns if the graphics chip is PowerVR SGX or not + _powerVr = platform()->isPowerVR(); +#ifdef __APPLE__ + _isSuperFast = platform()->isSuperFast(); +#endif + //glLineWidth(4); +#endif /* STANDALONE_SERVER */ +} + +void NinecraftApp::restartServer() { + if (!level) return; + + for (int i = level->players.size()-1; i >= 0; --i) { + Player* p = level->players[i]; + if (p != player) + level->removeEntity(p); + } + + raknetInstance->resetIsBroken(); +#ifndef STANDALONE_SERVER + gui.addMessage("This server has restarted!"); +#endif + hostMultiplayer(); + if (netCallback) netCallback->levelGenerated(level); +} + +bool NinecraftApp::handleBack(bool isDown) +{ + if (isGeneratingLevel) + { + return true; + } + if (level) + { + if (!isDown) + { + if (screen) + { + if (!screen->handleBackEvent(isDown)) + { + if (player->containerMenu) player->closeContainer(); + setScreen(NULL); + } + return true; + } else { + pauseGame(true); + } + //leaveGame(); + return false; + } + return true; + } + else if (screen) + { + return screen->handleBackEvent(isDown); + } + return false; +} + +void NinecraftApp::onGraphicsReset() +{ +#ifndef STANDALONE_SERVER + initGLStates(); + Tesselator::instance.init(); + + Minecraft::onGraphicsReset(); +#endif +} + +#ifndef ANDROID_PUBLISH + +static int _state = -1; +static int _stateTicksLeft = 0; +void NinecraftApp::testCreationAndDestruction() +{ + if (_state == -1) { + _stateTicksLeft = 100; + _state = 0; + } + if (_state == 0) { + if (--_stateTicksLeft <= 0) + _state = 1; + } + else if (_state == 1) { + getLevelSource()->deleteLevel("perf"); + int seed = getEpochTimeS(); + LOGI(">seed %d\n", seed); + selectLevel("perf", "perf", LevelSettings(seed, GameType::Creative)); + hostMultiplayer(); +#ifndef STANDALONE_SERVER + setScreen(new ProgressScreen()); +#endif + _state = 2; + _stateTicksLeft = 1000;//25000;//00; + } + else if (_state == 2) { + if (isLevelGenerated()) { + if (--_stateTicksLeft <= 0) { + _state = 3; + } + } + } else if (_state == 3) { + leaveGame(); + _state = 1; + } +} + + +void NinecraftApp::testJoiningAndDestruction() +{ + if (_state == -1) { + //LightUpdate sz[2] = { LightUpdate(LightLayer::Block, 0, 0, 0, 1, 1, 1), + // LightUpdate(LightLayer::Sky, 0, 0, 0, 1, 1, 1) }; + //LOGI("size of lightupdate: %lu == %d\n", sizeof(LightUpdate), (const char*)&sz[1] - (const char*)&sz[0]); + _stateTicksLeft = 100; + _state = 0; + } + if (_state == 0) { + if (--_stateTicksLeft <= 0) { + raknetInstance->clearServerList(); + locateMultiplayer(); + _state = 1; + } + } + else if (_state == 1) { + if (!raknetInstance->getServerList().empty()) { + PingedCompatibleServer s = raknetInstance->getServerList().at(0); + if (s.name.GetLength() > 0) { + joinMultiplayer(s); +#ifndef STANDALONE_SERVER + setScreen(new ProgressScreen()); +#endif + _state = 2; + _stateTicksLeft = 80;//1000; + } + } + } + else if (_state == 2) { + if (isLevelGenerated()) { + if (--_stateTicksLeft <= 0) { + _state = 3; + } + } + } else if (_state == 3) { + leaveGame(); + _stateTicksLeft = 50; + _state = 0; + } +} + +#endif /*ANDROID_PUBLISH*/ + +/* +void NinecraftApp::writeDemoFile() { + std::string path = externalStoragePath + "/games"; + + if (createFolderIfNotExists(path.c_str())) { + path += "/com.mojang"; + if (createFolderIfNotExists(path.c_str())) { + path += "/minecraftpe"; + if (createFolderIfNotExists(path.c_str())) { + path += "/played_demo"; + FILE* fp = fopen(path.c_str(), "w"); + if (fp) fclose(fp); + }}} +} + +bool NinecraftApp::hasPlayedDemo() { + std::string filename = externalStoragePath + "/games/com.mojang/minecraftpe/played_demo"; + FILE* fp = fopen(filename.c_str(), "r"); + if (!fp) return false; + fclose(fp); + return true; +} +*/ diff --git a/src/NinecraftApp.h b/src/NinecraftApp.h index 1c90bb7..9b3491c 100755 --- a/src/NinecraftApp.h +++ b/src/NinecraftApp.h @@ -1,52 +1,52 @@ -#ifndef NINECRAFTAPP_H__ -#define NINECRAFTAPP_H__ - -#include "world/Pos.h" -#include "App.h" -#include "client/Minecraft.h" -#include "world/level/storage/MemoryLevelStorage.h" -#include - - -class Level; -class LocalPlayer; -class ExternalFileLevelStorageSource; - -class NinecraftApp: public Minecraft -{ -public: - NinecraftApp(); - ~NinecraftApp(); - - void init(); - void teardown(); - - void update(); - - virtual bool handleBack(bool isDown); - -protected: - void onGraphicsReset(); - -private: - void initGLStates(); - void restartServer(); - - void updateStats(); - - //void writeDemoFile(); - //bool hasPlayedDemo(); - -#ifndef ANDROID_PUBLISH - void testCreationAndDestruction(); - void testJoiningAndDestruction(); -#endif /*ANDROID_PUBLISH*/ - - bool _verbose; - int _frames; - int _lastTickMs; - - static bool _hasInitedStatics; -}; - -#endif//NINECRAFTAPP_H__ +#ifndef NINECRAFTAPP_H__ +#define NINECRAFTAPP_H__ + +#include "world/Pos.h" +#include "App.h" +#include "client/Minecraft.h" +#include "world/level/storage/MemoryLevelStorage.h" +#include + + +class Level; +class LocalPlayer; +class ExternalFileLevelStorageSource; + +class NinecraftApp: public Minecraft +{ +public: + NinecraftApp(); + ~NinecraftApp(); + + void init(); + void teardown(); + + void update(); + + virtual bool handleBack(bool isDown); + +protected: + void onGraphicsReset(); + +private: + void initGLStates(); + void restartServer(); + + void updateStats(); + + //void writeDemoFile(); + //bool hasPlayedDemo(); + +#ifndef ANDROID_PUBLISH + void testCreationAndDestruction(); + void testJoiningAndDestruction(); +#endif /*ANDROID_PUBLISH*/ + + bool _verbose; + int _frames; + int _lastTickMs; + + static bool _hasInitedStatics; +}; + +#endif//NINECRAFTAPP_H__ diff --git a/src/Performance.cpp b/src/Performance.cpp index 82c1343..9ad2faa 100755 --- a/src/Performance.cpp +++ b/src/Performance.cpp @@ -1,4 +1,4 @@ -#include "Performance.h" - -/*static*/ -StopwatchHandler Performance::watches; +#include "Performance.h" + +/*static*/ +StopwatchHandler Performance::watches; diff --git a/src/Performance.h b/src/Performance.h index c30d0d5..4eb85b9 100755 --- a/src/Performance.h +++ b/src/Performance.h @@ -1,12 +1,12 @@ -#ifndef PERFORMANCE_H__ -#define PERFORMANCE_H__ - -#include "platform/time.h" - -class Performance -{ -public: - static StopwatchHandler watches; -}; - -#endif /*PERFORMANCE_H__*/ +#ifndef PERFORMANCE_H__ +#define PERFORMANCE_H__ + +#include "platform/time.h" + +class Performance +{ +public: + static StopwatchHandler watches; +}; + +#endif /*PERFORMANCE_H__*/ diff --git a/src/SharedConstants.cpp b/src/SharedConstants.cpp index 4a31175..46011c1 100755 --- a/src/SharedConstants.cpp +++ b/src/SharedConstants.cpp @@ -1,16 +1,16 @@ -#include "SharedConstants.h" - -namespace Common { - -std::string getGameVersionString(const std::string& versionSuffix /* = "" */) -{ - std::string result = std::string("v0.6.1") + versionSuffix; - // append 64-bit port marker only on Android 64‑bit targets - #if defined(ANDROID) && (defined(__aarch64__) || defined(__x86_64__)) - result += " (64-bit port)"; - #endif - result += " alpha"; - return result; -} - -}; +#include "SharedConstants.h" + +namespace Common { + +std::string getGameVersionString(const std::string& versionSuffix /* = "" */) +{ + std::string result = std::string("v0.6.1") + versionSuffix; + // append 64-bit port marker only on Android 64‑bit targets + #if defined(ANDROID) && (defined(__aarch64__) || defined(__x86_64__)) + result += " (64-bit port)"; + #endif + result += " alpha"; + return result; +} + +}; diff --git a/src/SharedConstants.h b/src/SharedConstants.h index f97ea36..fb49103 100755 --- a/src/SharedConstants.h +++ b/src/SharedConstants.h @@ -1,34 +1,34 @@ -#ifndef NET_MINECRAFT_SharedConstants_H__ -#define NET_MINECRAFT_SharedConstants_H__ - -#include - -enum LevelGeneratorVersion -{ - LGV_ORIGINAL = 0, -}; - -namespace Common { - std::string getGameVersionString(const std::string& versionSuffix = ""); -} - -namespace SharedConstants -{ - // 0.5.0 uses NPv8 - // 0.6.0 uses NPv9 - const int NetworkProtocolVersion = 9; - const int NetworkProtocolLowestSupportedVersion = 9; - const int GameProtocolVersion = 1; - const int GameProtocolLowestSupportedVersion = 1; - - const int StorageVersion = 3; - - const int MaxChatLength = 100; - - const int TicksPerSecond = 20; - - const int GeneratorVersion = (int)LGV_ORIGINAL; - //int FULLBRIGHT_LIGHTVALUE = 15 << 20 | 15 << 4; -} - -#endif /*NET_MINECRAFT_SharedConstants_H__*/ +#ifndef NET_MINECRAFT_SharedConstants_H__ +#define NET_MINECRAFT_SharedConstants_H__ + +#include + +enum LevelGeneratorVersion +{ + LGV_ORIGINAL = 0, +}; + +namespace Common { + std::string getGameVersionString(const std::string& versionSuffix = ""); +} + +namespace SharedConstants +{ + // 0.5.0 uses NPv8 + // 0.6.0 uses NPv9 + const int NetworkProtocolVersion = 9; + const int NetworkProtocolLowestSupportedVersion = 9; + const int GameProtocolVersion = 1; + const int GameProtocolLowestSupportedVersion = 1; + + const int StorageVersion = 3; + + const int MaxChatLength = 100; + + const int TicksPerSecond = 20; + + const int GeneratorVersion = (int)LGV_ORIGINAL; + //int FULLBRIGHT_LIGHTVALUE = 15 << 20 | 15 << 4; +} + +#endif /*NET_MINECRAFT_SharedConstants_H__*/ diff --git a/src/client/IConfigListener.cpp b/src/client/IConfigListener.cpp index 2c312e7..7123e81 100755 --- a/src/client/IConfigListener.cpp +++ b/src/client/IConfigListener.cpp @@ -1,16 +1,16 @@ -#include "IConfigListener.h" -#include "Minecraft.h" -#ifndef STANDALONE_SERVER -#include "gui/Gui.h" -#endif /* STANDALONE_SERVER */ -Config createConfig(Minecraft* mc) { - Config c; - #ifndef STANDALONE_SERVER - c.setScreenSize(mc->width, mc->height, Gui::GuiScale); - #endif - c.pixelCalc = mc->pixelCalc; - c.pixelCalcUi = mc->pixelCalcUi; - c.minecraft = mc; - c.options = &mc->options; - return c; -} +#include "IConfigListener.h" +#include "Minecraft.h" +#ifndef STANDALONE_SERVER +#include "gui/Gui.h" +#endif /* STANDALONE_SERVER */ +Config createConfig(Minecraft* mc) { + Config c; + #ifndef STANDALONE_SERVER + c.setScreenSize(mc->width, mc->height, Gui::GuiScale); + #endif + c.pixelCalc = mc->pixelCalc; + c.pixelCalcUi = mc->pixelCalcUi; + c.minecraft = mc; + c.options = &mc->options; + return c; +} diff --git a/src/client/IConfigListener.h b/src/client/IConfigListener.h index 6e96a2b..67b4404 100755 --- a/src/client/IConfigListener.h +++ b/src/client/IConfigListener.h @@ -1,48 +1,48 @@ -#ifndef CONFIGLISTENER_H__ -#define CONFIGLISTENER_H__ - -#include "PixelCalc.h" -class Minecraft; -class Options; - -class Config { -public: - // Screen dimensions and world-to-screen conversion - void setScreenSize(int width, int height, float scale) { - this->width = width; - this->height = height; - this->guiScale = scale; - this->invGuiScale = 1.0f / scale; - this->guiWidth = (int)(width * invGuiScale); - this->guiHeight = (int)(height * invGuiScale); - } - - int width; - int height; - - float guiScale; - float invGuiScale; - int guiWidth; - int guiHeight; - - PixelCalc pixelCalc; - PixelCalc pixelCalcUi; - - Minecraft* minecraft; - Options* options; -}; - -Config createConfig(Minecraft* mc); - -// Interface for Configuration-Changed listener -// This can mean (for instance); -// - Screen has changed dimensions, or rotation if rotations are enabled -// - New input device or control mechanism -class IConfigListener -{ -public: - virtual ~IConfigListener() {} - virtual void onConfigChanged(const Config& config) = 0; -}; - -#endif /*CONFIGLISTENER_H__*/ +#ifndef CONFIGLISTENER_H__ +#define CONFIGLISTENER_H__ + +#include "PixelCalc.h" +class Minecraft; +class Options; + +class Config { +public: + // Screen dimensions and world-to-screen conversion + void setScreenSize(int width, int height, float scale) { + this->width = width; + this->height = height; + this->guiScale = scale; + this->invGuiScale = 1.0f / scale; + this->guiWidth = (int)(width * invGuiScale); + this->guiHeight = (int)(height * invGuiScale); + } + + int width; + int height; + + float guiScale; + float invGuiScale; + int guiWidth; + int guiHeight; + + PixelCalc pixelCalc; + PixelCalc pixelCalcUi; + + Minecraft* minecraft; + Options* options; +}; + +Config createConfig(Minecraft* mc); + +// Interface for Configuration-Changed listener +// This can mean (for instance); +// - Screen has changed dimensions, or rotation if rotations are enabled +// - New input device or control mechanism +class IConfigListener +{ +public: + virtual ~IConfigListener() {} + virtual void onConfigChanged(const Config& config) = 0; +}; + +#endif /*CONFIGLISTENER_H__*/ diff --git a/src/client/KeyMapping.h b/src/client/KeyMapping.h index 06740c8..dde1c0d 100644 --- a/src/client/KeyMapping.h +++ b/src/client/KeyMapping.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT__KeyMapping_H__ -#define NET_MINECRAFT_CLIENT__KeyMapping_H__ - -//package net.minecraft.client; -#include - -class KeyMapping -{ -public: - std::string name; - int key; - - KeyMapping() {} - - KeyMapping(const std::string& name_, int key_) - : name(name_), - key(key_) - {} -}; - -#endif /*NET_MINECRAFT_CLIENT__KeyMapping_H__*/ +#ifndef NET_MINECRAFT_CLIENT__KeyMapping_H__ +#define NET_MINECRAFT_CLIENT__KeyMapping_H__ + +//package net.minecraft.client; +#include + +class KeyMapping +{ +public: + std::string name; + int key; + + KeyMapping() {} + + KeyMapping(const std::string& name_, int key_) + : name(name_), + key(key_) + {} +}; + +#endif /*NET_MINECRAFT_CLIENT__KeyMapping_H__*/ diff --git a/src/client/Minecraft.cpp b/src/client/Minecraft.cpp index ef9b134..cf27e20 100755 --- a/src/client/Minecraft.cpp +++ b/src/client/Minecraft.cpp @@ -1,1589 +1,1584 @@ -#include "Minecraft.h" -#include "Options.h" -#include "IBuildInput.h" -#include "Keyboard.h" -#include "Item.h" -#include "ItemInstance.h" -#include "XperiaPlayInput.h" -#include "ControllerTurnInput.h" -#include -#include - -#if defined(APPLE_DEMO_PROMOTION) - #define NO_NETWORK -#endif - -#if defined(RPI) - #define CREATORMODE -#endif -#include "../network/RakNetInstance.h" -#include "../network/ClientSideNetworkHandler.h" -#include "../network/ServerSideNetworkHandler.h" -//#include "../network/Packet.h" -#include "../world/entity/player/Inventory.h" -#include "../world/level/tile/Tile.h" -#include "../world/level/storage/LevelStorageSource.h" -#include "../world/level/storage/LevelStorage.h" -#include "player/input/KeyboardInput.h" -<<<<<<< HEAD -#include "player/input/ControllerTurnInput.h" -#include "player/input/XperiaPlayInput.h" -#include "world/level/chunk/ChunkSource.h" -======= -#include "ChunkSource.h" ->>>>>>> abfb9e0 (more refactoring for iOS) - -#ifndef STANDALONE_SERVER -#include "player/input/touchscreen/TouchInputHolder.h" -#include "particle/ParticleEngine.h" -#include "gui/Screen.h" -#include "gui/Font.h" -#include "gui/screens/RenameMPLevelScreen.h" -#include "gui/screens/ConsoleScreen.h" -#include "gui/screens/ChatScreen.h" -#include "sound/SoundEngine.h" -#include "player/input/touchscreen/TouchscreenInput.h" -#include "renderer/Chunk.h" -#include "gui/screens/PrerenderTilesScreen.h" -#include "renderer/Textures.h" -#include "gui/screens/DeathScreen.h" -#include "gui/screens/FurnaceScreen.h" -#include "gui/screens/ArmorScreen.h" -#include "renderer/tileentity/TileEntityRenderDispatcher.h" -#include "renderer/ptexture/DynamicTexture.h" -#include "renderer/GameRenderer.h" -#include "renderer/ItemInHandRenderer.h" -#include "renderer/LevelRenderer.h" -#include "renderer/entity/EntityRenderDispatcher.h" -#include "gui/Screen.h" -#include "gui/Font.h" -#include "gui/screens/RenameMPLevelScreen.h" -#include "sound/SoundEngine.h" -#endif // STANDALONE_SERVER - -#include "player/LocalPlayer.h" -#include "gamemode/CreativeMode.h" -#include "gamemode/SurvivalMode.h" -#include "player/LocalPlayer.h" -#include "../platform/CThread.h" -#include "../platform/input/Mouse.h" -#include "../AppPlatform.h" -#include "../LicenseCodes.h" -#include "../util/PerfTimer.h" -#include "../util/PerfRenderer.h" -#include "player/input/MouseBuildInput.h" - -#include "player/input/IInputHolder.h" - -#include "player/input/MouseTurnInput.h" -#include "../world/entity/MobFactory.h" -#include "../world/level/MobSpawner.h" -#include "../util/Mth.h" -#include "../network/packet/InteractPacket.h" -#include "../network/packet/RespawnPacket.h" -#include "../network/packet/AdventureSettingsPacket.h" -#include "../network/packet/SetSpawnPositionPacket.h" -#include "IConfigListener.h" -#include "../world/entity/MobCategory.h" -#include "../world/Difficulty.h" -#include "../server/ServerLevel.h" - -#ifdef CREATORMODE -#include "../server/CreatorLevel.h" -#endif - -#include "../network/command/CommandServer.h" -#include "gamemode/CreatorMode.h" - -static void checkGlError(const char* tag) { -#ifdef GLDEBUG - while (1) { - const int errCode = glGetError(); - if (errCode == GL_NO_ERROR) break; - - LOGE("################\nOpenGL-error @ %s : #%d\n", tag, errCode); - } -#endif /*GLDEBUG*/ -} - -/*static*/ -const char* Minecraft::progressMessages[] = { - "Locating server", - "Building terrain", - "Preparing", - "Saving chunks" -}; - -int Minecraft::customDebugId = Minecraft::CDI_NONE; - -#if defined(_MSC_VER) - #pragma warning( disable : 4355 ) // 'this' pointer in initialization list which is perfectly legal -#endif - -bool Minecraft::useAmbientOcclusion = false; - -Minecraft::Minecraft() : - level(NULL), - player(NULL), - cameraTargetPlayer(NULL), - levelRenderer(NULL), - gameRenderer(NULL), -#ifndef STANDALONE_SERVER - particleEngine(NULL), - _perfRenderer(NULL), -#endif - _commandServer(NULL), -#ifndef STANDALONE_SERVER - textures(NULL), -#endif - lastTickTime(-1), - lastTime(0), - ticksSinceLastUpdate(0), - gameMode(NULL), - mouseGrabbed(true), - missTime(0), - pause(false), - _running(false), - timer(20), -#ifndef STANDALONE_SERVER - gui(this), -#endif - netCallback(NULL), -#ifndef STANDALONE_SERVER - screen(NULL), - font(NULL), -#endif - screenMutex(false), -#ifndef STANDALONE_SERVER - scheduledScreen(NULL), - hasScheduledScreen(false), - soundEngine(NULL), -#endif - ticks(0), - isGeneratingLevel(false), - _hasSignaledGeneratingLevelFinished(true), - generateLevelThread(NULL), - progressStagePercentage(0), - progressStageStatusId(0), - isLookingForMultiplayer(false), - _licenseId(LicenseCodes::WAIT_PLATFORM_NOT_READY), - inputHolder(0), - _supportsNonTouchscreen(false), -#ifndef STANDALONE_SERVER - screenChooser(this), -#endif - width(1), height(1), - //_respawnPlayerTicks(-1), -#ifdef __APPLE__ - _isSuperFast(false), -#endif - _powerVr(false), - commandPort(4711), - reserved_d1(0),reserved_d2(0), - reserved_f1(0),reserved_f2(0), options(this) -{ -//#ifdef ANDROID - -#if defined(NO_NETWORK) - raknetInstance = new IRakNetInstance(); -#else - raknetInstance = new RakNetInstance(); -#endif -#ifndef STANDALONE_SERVER - soundEngine = new SoundEngine(20.0f); - soundEngine->init(this, &options); -#endif - //setupPieces(); -} - -Minecraft::~Minecraft() -{ - delete netCallback; - delete raknetInstance; -#ifndef STANDALONE_SERVER - delete levelRenderer; - delete gameRenderer; - delete particleEngine; - - delete soundEngine; -#endif - delete gameMode; -#ifndef STANDALONE_SERVER - delete font; - delete textures; - - if (screen != NULL) { - delete screen; - screen = NULL; - } -#endif - if (level != NULL) { - level->saveGame(); - if (level->getChunkSource()) - level->getChunkSource()->saveAll(true); - delete level->getLevelStorage(); - delete level; - level = NULL; - } - - //delete player; - delete inputHolder; - - delete storageSource; - delete _perfRenderer; - delete _commandServer; - - MobFactory::clearStaticTestMobs(); -#ifndef STANDALONE_SERVER - EntityRenderDispatcher::destroy(); -#endif -} - -// Only called by server -void Minecraft::selectLevel( const std::string& levelId, const std::string& levelName, const LevelSettings& settings ) -{ -#if defined(CREATORMODE) - level = new CreatorLevel( -#else - level = new ServerLevel( -#endif - storageSource->selectLevel(levelId, false), - levelName, - settings, - SharedConstants::GeneratorVersion); - - // note: settings is useless beyond this point, since it's - // either copied to LevelData (or LevelData read from file) - setLevel(level, "Generating level"); - setIsCreativeMode(level->getLevelData()->getGameType() == GameType::Creative); - _running = true; -} - -void Minecraft::setLevel(Level* level, const std::string& message /* ="" */, LocalPlayer* forceInsertPlayer /* = NULL */) { - cameraTargetPlayer = NULL; - LOGI("Seed is %ld\n", level->getSeed()); - - if (level != NULL) { - level->raknetInstance = raknetInstance; - gameMode->initLevel(level); - - if (!player && forceInsertPlayer) - { - player = forceInsertPlayer; - player->resetPos(false); - //level->addEntity(forceInsertPlayer); - } - else if (player != NULL) { - player->resetPos(false); - if (level != NULL) { - level->addEntity(player); - } - } - this->level = level; - _hasSignaledGeneratingLevelFinished = false; -#ifdef STANDALONE_SERVER - const bool threadedLevelCreation = false; -#else - const bool threadedLevelCreation = true; -#endif - - if (threadedLevelCreation) { - // Threaded - // "Lock" - isGeneratingLevel = true; - generateLevelThread = new CThread(Minecraft::prepareLevel_tspawn, this); - } else { - // Non-threaded - generateLevel("Currently not used", level); - } - } else { - player = NULL; - } - - this->lastTickTime = 0; - this->_running = true; -} - -void Minecraft::leaveGame(bool renameLevel /*=false*/) -{ - if (isGeneratingLevel || !_hasSignaledGeneratingLevelFinished) - return; - - isGeneratingLevel = false; - bool saveLevel = level && (!level->isClientSide || renameLevel); - - raknetInstance->disconnect(); - if (saveLevel) { - // If server or wanting to save level as client, save all unsaved chunks! - level->getChunkSource()->saveAll(true); - } - - LOGI("Clearing levels\n"); - - cameraTargetPlayer = NULL; -#ifndef STANDALONE_SERVER - levelRenderer->setLevel(NULL); - particleEngine->setLevel(NULL); -#endif - LOGI("Erasing callback\n"); - delete netCallback; - netCallback = NULL; - - LOGI("Erasing level\n"); - if (level != NULL) { - delete level->getLevelStorage(); - delete level; - level = NULL; - } - //delete player; - player = NULL; - cameraTargetPlayer = NULL; - - _running = false; -#ifndef STANDALONE_SERVER - gui.clearMessages(); - if (renameLevel) { - setScreen(new RenameMPLevelScreen(LevelStorageSource::TempLevelId)); - } - else - screenChooser.setScreen(SCREEN_STARTMENU); -#endif -} - -void Minecraft::prepareLevel(const std::string& title) { - LOGI("status: 1\n"); - progressStageStatusId = 1; - - Stopwatch A, B, C, D; - A.start(); - - Stopwatch L; - - // Dont update lights if we load the level (ok, actually just with leveldata version=1.+(?)) - if (!level->isNew()) - level->setUpdateLights(false); - - int Max = CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH; - int pp = 0; - for (int x = 8; x < (CHUNK_CACHE_WIDTH * CHUNK_WIDTH); x += CHUNK_WIDTH) { - for (int z = 8; z < (CHUNK_CACHE_WIDTH * CHUNK_WIDTH); z += CHUNK_WIDTH) { - progressStagePercentage = 100 * pp++ / Max; - //printf("level generation progress %d\n", progressStagePercentage); - B.start(); - level->getTile(x, 64, z); - B.stop(); - L.start(); - if (level->isNew()) - while (level->updateLights()) - ; - L.stop(); - } - } - A.stop(); - level->setUpdateLights(true); - - C.start(); - for (int x = 0; x < CHUNK_CACHE_WIDTH; x++) - { - for (int z = 0; z < CHUNK_CACHE_WIDTH; z++) - { - LevelChunk* chunk = level->getChunk(x, z); - if (chunk && !chunk->createdFromSave) - { - chunk->unsaved = false; - chunk->clearUpdateMap(); - } - } - } - C.stop(); - - LOGI("status: 3\n"); - progressStageStatusId = 3; - if (level->isNew()) { - level->setInitialSpawn(); // @note: should obviously be called from Level itself - level->saveLevelData(); - level->getChunkSource()->saveAll(false); - level->saveGame(); - } else { - level->saveLevelData(); - level->loadEntities(); - } - - progressStagePercentage = -1; - progressStageStatusId = 2; - LOGI("status: 2\n"); - - D.start(); - level->prepare(); - D.stop(); - - A.print("Generate level: "); - L.print(" - light: "); - B.print(" - getTl: "); - C.print(" - clear: "); - D.print(" - prepr: "); - progressStageStatusId = 0; -} - -void Minecraft::update() { - //LOGI("Enter Update\n"); - - if (Options::debugGl) - LOGI(">>>>>>>>>>\n"); - - TIMER_PUSH("root"); - - //if (level) { - // LOGI("numplayers: %d\n", level->players.size()); - // for (int i = 0; i < level->players.size(); ++i) { - // Player* p = level->players[i]; - // bool inEnt = std::find(level->entities.begin(), level->entities.end(), p) != level->entities.end(); - // LOGI(" %p, %d, %d - in? %d\n", p, p->entityId, p->owner.ToUint32(p->owner), inEnt); - // } - //} - - // If we're paused (local world / invisible server), freeze gameplay and - // networking and only keep UI responsive. - bool freezeGame = pause; - - if (!freezeGame) { - timer.advanceTime(); - } - - if (raknetInstance && !freezeGame) { - raknetInstance->runEvents(netCallback); - } - - TIMER_PUSH("tick"); - int toTick = freezeGame ? 1 : timer.ticks; - if (!freezeGame) timer.ticks = 0; - for (int i = 0; i < toTick; ++i, ++ticks) - tick(i, toTick-1); - - TIMER_POP_PUSH("updatelights"); - if (level && !isGeneratingLevel) { - level->updateLights(); - } - TIMER_POP(); - - #ifndef STANDALONE_SERVER - if (gameMode != NULL) gameMode->render(timer.a); - TIMER_PUSH("sound"); - soundEngine->update(player, timer.a); - TIMER_POP_PUSH("render"); - gameRenderer->render(timer.a); - TIMER_POP(); - #else - CThread::sleep(1); - #endif -#ifndef STANDALONE_SERVER - Multitouch::resetThisUpdate(); -#endif - TIMER_POP(); -#ifndef STANDALONE_SERVER - checkGlError("Update finished"); - - if (options.getBooleanValue(OPTIONS_RENDER_DEBUG)) { -#ifndef PLATFORM_DESKTOP - if (!PerfTimer::enabled) { - PerfTimer::reset(); - PerfTimer::enabled = true; - } - _perfRenderer->renderFpsMeter(1); - checkGlError("render debug"); -#endif - } else { - PerfTimer::enabled = false; - } -#endif - //LOGI("Exit Update\n"); -} - -void Minecraft::tick(int nTick, int maxTick) { - if (missTime > 0) missTime--; -#ifndef STANDALONE_SERVER - if (!screen && player) { - if (player->health <= 0) { - setScreen(new DeathScreen()); - } - } -#endif - TIMER_PUSH("gameMode"); - if (level && !pause) { - gameMode->tick(); - } - - TIMER_POP_PUSH("commandServer"); - if (level && _commandServer) { - _commandServer->tick(); - } - - TIMER_POP_PUSH("input"); - tickInput(); -#ifndef STANDALONE_SERVER - TIMER_POP_PUSH("gui"); - gui.tick(); -#endif - // - // Ongoing level generation in a (perhaps) different thread. When it's - // ready, _levelGenerated() is called once and any threads are deleted. - // - if (isGeneratingLevel) { - return; - } else if (!_hasSignaledGeneratingLevelFinished) { - if (generateLevelThread) { - delete generateLevelThread; - generateLevelThread = NULL; - } - _levelGenerated(); - } - - // - // Normal game loop, run before or efter level generation - // - if (level != NULL) - { - if (!pause) { -#ifndef STANDALONE_SERVER - TIMER_POP_PUSH("gameRenderer"); - gameRenderer->tick(nTick, maxTick); - - TIMER_POP_PUSH("levelRenderer"); - levelRenderer->tick(); -#endif - level->difficulty = options.getIntValue(OPTIONS_DIFFICULTY); - if (level->isClientSide) level->difficulty = Difficulty::EASY; - - TIMER_POP_PUSH("level"); - level->tickEntities(); - level->tick(); -#ifndef STANDALONE_SERVER - TIMER_POP_PUSH("animateTick"); - if (player) { - level->animateTick(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); - } -#endif - } - } -#ifndef STANDALONE_SERVER - textures->loadAndBindTexture("terrain.png"); - if (!pause && !(screen && !screen->renderGameBehind())) { - #if !defined(RPI) - #ifdef __APPLE__ - if (isSuperFast()) - #endif - { - if (nTick == maxTick) { - TIMER_POP_PUSH("textures"); - textures->tick(true); - } - } - #endif - } - TIMER_POP_PUSH("particles"); - if (!pause) { - particleEngine->tick(); - } - if (screen) { - screenMutex = true; - screen->tick(); - screenMutex = false; - } - - // @note: fix to keep "isPressed" and "isReleased" as long as necessary. - // Most likely keyboard and mouse could/should be reset here as well. - Multitouch::reset(); -#endif - TIMER_POP(); -} - -class InputRAII { -public: - ~InputRAII() { -#ifndef STANDALONE_SERVER - Mouse::reset(); - Keyboard::reset(); -#endif - } -}; - -void Minecraft::tickInput() { -#ifndef STANDALONE_SERVER - InputRAII raiiInput; - - if (screen && !screen->passEvents) { - screenMutex = true; - screen->updateEvents(); - //screen->updateSetScreen(); - screenMutex = false; - if (hasScheduledScreen) { - setScreen(scheduledScreen); - scheduledScreen = NULL; - hasScheduledScreen = false; - } - return; - } - - if (!player) { - return; - } - -#ifdef RPI - bool mouseDiggable = true; - bool allowGuiClicks = !mouseGrabbed; -#else - bool mouseDiggable = !gui.isInside(Mouse::getX(), Mouse::getY()); - bool allowGuiClicks = true; -#endif - - TIMER_PUSH("mouse"); - while (Mouse::next()) { - //if (Mouse::getButtonState(MouseAction::ACTION_LEFT)) - // LOGI("mouse-down-at: %d, %d\n", Mouse::getX(), Mouse::getY()); - int passedTime = getTimeMs() - lastTickTime; - if (passedTime > 200) continue; // @note: As long Mouse::clear CLEARS the whole buffer, it's safe to break here - // But since it might be rewritten anyway (and hopefully there aren't a lot of messages, we just continue. - - const MouseAction& e = Mouse::getEvent(); - - if (!useTouchscreen() && !mouseGrabbed) { - if (!screen && e.data == MouseAction::DATA_DOWN) { - grabMouse(); - } - } - - if (allowGuiClicks && e.action == MouseAction::ACTION_LEFT && e.data == MouseAction::DATA_DOWN) { - gui.handleClick(MouseAction::ACTION_LEFT, Mouse::getX(), Mouse::getY()); - } - - if (e.action == MouseAction::ACTION_WHEEL) { - // If chat/console is open, use the wheel to scroll through chat history. - if (screen && (dynamic_cast(screen) || dynamic_cast(screen))) { - gui.scrollChat(e.dy); - } else { - Inventory* v = player->inventory; - - int numSlots = gui.getNumSlots(); - if (!useTouchscreen()) numSlots--; - - int slot = (v->selected - e.dy + numSlots) % numSlots; - v->selectSlot(slot); - } - } - /* - if (mouseDiggable && options.useMouseForDigging) { - if (Mouse::getEventButton() == MouseAction::ACTION_LEFT && Mouse::getEventButtonState()) { - handleMouseClick(MouseAction::ACTION_LEFT); - lastClickTick = ticks; - } - if (Mouse::getEventButton() == MouseAction::ACTION_RIGHT && Mouse::getEventButtonState()) { - handleMouseClick(MouseAction::ACTION_RIGHT); - lastClickTick = ticks; - } - } - */ - } - - TIMER_POP_PUSH("keyboard"); - while (Keyboard::next()) { - int key = Keyboard::getEventKey(); - bool isPressed = (Keyboard::getEventKeyState() == KeyboardAction::KEYDOWN); - player->setKey(key, isPressed); - - if (isPressed) { - gui.handleKeyPressed(key); - - if (key >= '0' && key <= '9') { - int digit = key - '0'; - int slot = digit - 1; - - if (slot >= 0 && slot < gui.getNumSlots()) - player->inventory->selectSlot(slot); - - #if defined(WIN32) - if (digit >= 1 && GetAsyncKeyState(VK_CONTROL) < 0) { - // Set adventure settings here! - AdventureSettingsPacket p(level->adventureSettings); - p.toggle((AdventureSettingsPacket::Flags)(1 << slot)); - p.fillIn(level->adventureSettings); - raknetInstance->send(p); - } - if (digit == 0) { - Pos pos((int)player->x, (int)player->y-1, (int)player->z); - SetSpawnPositionPacket p(pos); - raknetInstance->send(p); - } - #endif - } - - if (key == Keyboard::KEY_LEFT_CTRL) { - player->setSprinting(true); - } - - if (key == Keyboard::KEY_E) { - screenChooser.setScreen(SCREEN_BLOCKSELECTION); - } - - if (!screen && key == Keyboard::KEY_T && level) { - setScreen(new ConsoleScreen()); - } - - if (key == Keyboard::KEY_F3) { - options.toggle(OPTIONS_RENDER_DEBUG); - } - - if (key == Keyboard::KEY_F5) { - options.toggle(OPTIONS_THIRD_PERSON_VIEW); - /* - ImprovedNoise noise; - for (int i = 0; i < 16; ++i) - printf("%d\t%f\n", i, noise.grad2(i, 3, 8)); - */ - } - - if (!screen && key == Keyboard::KEY_O || key == 250) { - releaseMouse(); - } - - if (key == Keyboard::KEY_F) { - int dst = options.getIntValue(OPTIONS_VIEW_DISTANCE); - options.set(OPTIONS_VIEW_DISTANCE, (dst + 1) % 4); - } - #ifdef CHEATS - if (key == Keyboard::KEY_U) { - onGraphicsReset(); - player->heal(100); - } - - if (key == Keyboard::KEY_B || key == 108) // Toggle the game mode - setIsCreativeMode(!isCreativeMode()); - - if (key == Keyboard::KEY_P) // Step forward in time - level->setTime( level->getTime() + 1000); - - if (key == Keyboard::KEY_G) { - setScreen(new ArmorScreen()); - /* - std::vector& boxs = level->getCubes(NULL, AABB(128.1f, 73, 128.1f, 128.9f, 74.9f, 128.9f)); - LOGI("boxes: %d\n", (int)boxs.size()); - */ - } - - if (key == Keyboard::KEY_Y) { - textures->reloadAll(); - player->hurtTo(2); - } - if (key == Keyboard::KEY_Z || key == 108) { - for (int i = 0; i < 1; ++i) { - Mob* mob = NULL; - int forceId = 0;//MobTypes::Sheep; - - int types[] = { - MobTypes::Sheep, - MobTypes::Pig, - MobTypes::Chicken, - MobTypes::Cow, - }; - - int mobType = (forceId > 0)? forceId : types[Mth::random(sizeof(types) / sizeof(int))]; - mob = MobFactory::CreateMob(mobType, level); - - //((Animal*)mob)->setAge(-1000); - float dx = 4 - 8 * Mth::random() + 4 * Mth::sin(Mth::DEGRAD * player->yRot); - float dz = 4 - 8 * Mth::random() + 4 * Mth::cos(Mth::DEGRAD * player->yRot); - if (mob && !MobSpawner::addMob(level, mob, player->x + dx, player->y, player->z + dz, Mth::random()*360, 0, true)) - delete mob; - } - } - - if (key == Keyboard::KEY_X) { - const EntityList& entities = level->getAllEntities(); - for (int i = entities.size()-1; i >= 0; --i) { - Entity* e = entities[i]; - if (!e->isPlayer()) - level->removeEntity(e); - } - } - - if (key == Keyboard::KEY_C /*|| key == 4*/) { - player->inventory->clearInventoryWithDefault(); - // @todo: Add saving here for benchmarking - } - if (key == Keyboard::KEY_H) { - setScreen( new PrerenderTilesScreen() ); - } - - if (key == Keyboard::KEY_O) { - for (int i = Inventory::MAX_SELECTION_SIZE; i < player->inventory->getContainerSize(); ++i) - if (player->inventory->getItem(i)) - player->inventory->dropSlot(i, false); - } - if (key == Keyboard::KEY_M) { - Difficulty difficulty = (Difficulty)options.getIntValue(OPTIONS_DIFFICULTY); - options.set(OPTIONS_DIFFICULTY, (difficulty == Difficulty::PEACEFUL)? - Difficulty::NORMAL : Difficulty::PEACEFUL); - //setIsCreativeMode( !isCreativeMode() ); - } - - if (options.getBooleanValue(OPTIONS_RENDER_DEBUG)) { - if (key >= '0' && key <= '9') { - _perfRenderer->debugFpsMeterKeyPress(key - '0'); - } - } - #endif - - if (key == Keyboard::KEY_ESCAPE) - pauseGame(false); - - #ifndef OPENGL_ES - if (key == Keyboard::KEY_P) { - static bool isWireFrame = false; - isWireFrame = !isWireFrame; - glPolygonMode(GL_FRONT, isWireFrame? GL_LINE : GL_FILL); - //glPolygonMode(GL_BACK, isWireFrame? GL_LINE : GL_FILL); - } - #endif - } - #ifdef WIN32 - if (key == Keyboard::KEY_M) { - for (int i = 0; i < 5 * SharedConstants::TicksPerSecond; ++i) - level->tick(); - } - #endif - } - - TIMER_POP_PUSH("handlemouse"); - - static bool prevMouseDownLeft = false; - - if (Mouse::getButtonState(MouseAction::ACTION_LEFT) == 0) { - gameMode->stopDestroyBlock(); - } - - if (!Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) { - gameMode->releaseUsingItem(player); - } - - if (useTouchscreen()) { - // Touch: gesture recognizer classifies the action type (turn/destroy/build) - BuildActionIntention bai; - - if (inputHolder && inputHolder->getBuildInput()->tickBuild(player, &bai)) { - handleBuildAction(&bai); - } else { - gameMode->stopDestroyBlock(); - } - } else { - // Desktop: left mouse = destroy/attack - if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) { - auto baiFlags = BuildActionIntention::BAI_REMOVE | BuildActionIntention::BAI_ATTACK; - - if (!prevMouseDownLeft) baiFlags |= BuildActionIntention::BAI_FIRSTREMOVE; - - BuildActionIntention bai(baiFlags); - handleBuildAction(&bai); - } - - prevMouseDownLeft = Mouse::isButtonDown(MouseAction::ACTION_LEFT); - - // Build and use/interact is on same button - // USPESHNO spizheno - static int buildHoldTicks = 0; - if (Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) { - if (buildHoldTicks >= 5) buildHoldTicks = 0; - - if (++buildHoldTicks == 1) { - BuildActionIntention bai(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_INTERACT); - handleBuildAction(&bai); - } - } else { - buildHoldTicks = 0; - gameMode->releaseUsingItem(player); - } - } - - lastTickTime = getTimeMs(); - - // we have (hopefully) handled the keyboard & mouse queue and it - // can now be emptied. If wanted, the reset could be changed to: - // index -= numRead; // then this code doesn't have to be placed here - // + it prepares for tick not handling all or any events. - // update: RAII'ing instead, see above - //Keyboard::reset(); - //Mouse::reset(); - - TIMER_POP(); -#endif -} - -void Minecraft::handleBuildAction(BuildActionIntention* action) { -#ifndef STANDALONE_SERVER - if (action->isRemove()) { - if (missTime > 0) return; - player->swing(); - } - if(player->isUsingItem()) - return; - bool mayUse = true; - - if (!hitResult.isHit()) { - if (action->isRemove() && !gameMode->isCreativeType()) { - missTime = 10; - } - } else if (hitResult.type == ENTITY) { - if (action->isAttack()) { - player->swing(); - //LOGI("attacking!\n"); - InteractPacket packet(InteractPacket::Attack, player->entityId, hitResult.entity->entityId); - raknetInstance->send(packet); - gameMode->attack(player, hitResult.entity); - } else if (action->isInteract()) { - if (hitResult.entity->interactPreventDefault()) - mayUse = false; - //LOGI("interacting!\n"); - InteractPacket packet(InteractPacket::Interact, player->entityId, hitResult.entity->entityId); - raknetInstance->send(packet); - gameMode->interact(player, hitResult.entity); - } - } else if (hitResult.type == TILE) { - int x = hitResult.x; - int y = hitResult.y; - int z = hitResult.z; - int face = hitResult.f; - - int oldTileId = level->getTile(x, y, z); - Tile* oldTile = Tile::tiles[oldTileId]; - - //bool tryDestroyBlock = false; - - if (action->isRemove()) { - if (!oldTile) - return; - - //LOGI("tile: %s - %d, %d, %d. b: %f - %f\n", oldTile->getDescriptionId().c_str(), x, y, z, oldTile->getBrightness(level, x, y, z), oldTile->getBrightness(level, x, y+1, z)); - level->extinguishFire(x, y, z, hitResult.f); - - if (action->isFirstRemove()) { - gameMode->startDestroyBlock(x, y, z, hitResult.f); - } else { - gameMode->continueDestroyBlock(x, y, z, hitResult.f); - } - - particleEngine->crack(x, y, z, hitResult.f); - player->swing(); - } - else { - ItemInstance* item = player->inventory->getSelected(); - if (gameMode->useItemOn(player, level, item, x, y, z, face, hitResult.pos)) { - mayUse = false; - player->swing(); - #ifdef RPI - } else if (item && item->id == ((Item*)Item::sword_iron)->id) { - player->swing(); - #endif - } - if (item && item->count <= 0) { - player->inventory->clearSlot(player->inventory->selected); - } - //} else if (item && item->count != oldCount) { - // gameRenderer->itemInHandRenderer->itemPlaced(); - //} - } - } - if (mayUse && action->isInteract()) { - ItemInstance* item = player->inventory->getSelected(); - if (item && !player->isUsingItem()) { - if (gameMode->useItem(player, level, item)) { - gameRenderer->itemInHandRenderer->itemUsed(); - } - if (item && item->count <= 0) { - player->inventory->clearSlot(player->inventory->selected); - } - } - } -#endif -} - -bool Minecraft::isOnlineClient() -{ - return (level != NULL && level->isClientSide); -} - -bool Minecraft::isOnline() -{ - return netCallback != NULL; -} - -void Minecraft::pauseGame(bool isBackPaused) { - // Only freeze gameplay when running a local server and it is not accepting - // incoming connections (invisible server), which includes typical single- - // player/lobby mode. If the server is visible, the game should keep ticking. - bool canFreeze = false; - if (raknetInstance && raknetInstance->isServer() && netCallback) { - ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) netCallback; - if (!ss->allowsIncomingConnections()) - canFreeze = true; - } - pause = canFreeze; - -#ifndef STANDALONE_SERVER - if (screen != NULL) return; - screenChooser.setScreen(isBackPaused? SCREEN_PAUSEPREV : SCREEN_PAUSE); -#endif -} -void Minecraft::gameLostFocus() { -#ifndef STANDALONE_SERVER - if(screen != NULL) { - screen->lostFocus(); - } -#endif -} - - -void Minecraft::setScreen( Screen* screen ) -{ -#ifndef STANDALONE_SERVER - Mouse::reset(); - Multitouch::reset(); - Multitouch::resetThisUpdate(); - - if (screenMutex) { - hasScheduledScreen = true; - scheduledScreen = screen; - return; - } - - if (screen != NULL && screen->isErrorScreen()) - return; - if (screen == NULL && level == NULL) - screen = screenChooser.createScreen(SCREEN_STARTMENU); - - if (this->screen != NULL) { - this->screen->removed(); - delete this->screen; - } - - this->screen = screen; - if (screen != NULL) { - releaseMouse(); - //ScreenSizeCalculator ssc = new ScreenSizeCalculator(options, width, height); - int screenWidth = (int)(width * Gui::InvGuiScale); //ssc.getWidth(); - int screenHeight = (int)(height * Gui::InvGuiScale); //ssc.getHeight(); - screen->init(this, screenWidth, screenHeight); - - if (screen->isInGameScreen() && level) { - level->saveLevelData(); - level->saveGame(); - } - - //noRender = false; - } else { - // Closing a screen and returning to the game should unpause. - pause = false; - grabMouse(); - } -#endif -} - -void Minecraft::grabMouse() -{ -#ifndef STANDALONE_SERVER - if (mouseGrabbed) return; - mouseGrabbed = true; - mouseHandler.grab(); - //setScreen(NULL); -#endif -} - -void Minecraft::releaseMouse() -{ -#ifndef STANDALONE_SERVER - if (!mouseGrabbed) { - return; - } - if (player) { - player->releaseAllKeys(); - } - mouseGrabbed = false; - mouseHandler.release(); -#endif -} - -bool Minecraft::useTouchscreen() { -#ifdef RPI - return false; -#endif - return options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN) && !_supportsNonTouchscreen; -} -bool Minecraft::supportNonTouchScreen() { - return _supportsNonTouchscreen; -} -void Minecraft::init() -{ -#ifndef STANDALONE_SERVER - checkGlError("Init enter"); - - _supportsNonTouchscreen = !platform()->supportsTouchscreen(); - - LOGI("IS TOUCHSCREEN? %d\n", options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN)); - - textures = new Textures(&options, platform()); - textures->addDynamicTexture(new WaterTexture()); - textures->addDynamicTexture(new WaterSideTexture()); - gui.texturesLoaded(textures); - - levelRenderer = new LevelRenderer(this); - gameRenderer = new GameRenderer(this); - particleEngine = new ParticleEngine(level, textures); - - // Platform specific initialization here - font = new Font(&options, "font/default8.png", textures); - - _perfRenderer = new PerfRenderer(this, font); - - checkGlError("Init complete"); -#endif - - options.load(); - - setIsCreativeMode(false); // false means it's Survival Mode - reloadOptions(); -} - -void Minecraft::setSize(int w, int h) { -#ifndef STANDALONE_SERVER - transformResolution(&w, &h); - - width = w; - height = h; - - int guiScale = options.getIntValue(OPTIONS_GUI_SCALE); - - // determine gui scale, optionally overriding auto - if (guiScale != 0) { - // manual selection: 1->small, 2->medium, 3->large, 4->larger, 5->largest - switch (guiScale) { - case 1: Gui::GuiScale = 2.0f; break; - case 2: Gui::GuiScale = 3.0f; break; - case 3: Gui::GuiScale = 4.0f; break; - case 4: Gui::GuiScale = 5.0f; break; - case 5: Gui::GuiScale = 6.0f; break; - default: Gui::GuiScale = 1.0f; break; // auto - } - } else { - // auto compute from resolution - if (width >= 1000) { - #ifdef __APPLE__ - Gui::GuiScale = (width > 2000)? 8.0f : 4.0f; - #else - Gui::GuiScale = 4.0f; - #endif - } - else if (width >= 800) { -#ifdef __APPLE__ - Gui::GuiScale = 4.0f; -#else - Gui::GuiScale = 3.0f; -#endif - } - else if (width >= 400) - Gui::GuiScale = 2.0f; - else - Gui::GuiScale = 1.0f; - } - - Gui::InvGuiScale = 1.0f / Gui::GuiScale; - int screenWidth = (int)(width * Gui::InvGuiScale); - int screenHeight = (int)(height * Gui::InvGuiScale); - - // if (platform()) { - // float pixelsPerMillimeter = options.getProgressValue(&Option::PIXELS_PER_MILLIMETER); - // pixelCalc.setPixelsPerMillimeter(pixelsPerMillimeter); - // pixelCalcUi.setPixelsPerMillimeter(pixelsPerMillimeter * Gui::InvGuiScale); - // } - - Config config = createConfig(this); - gui.onConfigChanged(config); - - if (screen) - screen->setSize(screenWidth, screenHeight); - - if (inputHolder) - inputHolder->onConfigChanged(config); - //LOGI("Setting size: %d, %d: %f\n", width, height, Gui::InvGuiScale); - -#ifdef WIN32 - char resbuf[128]; - sprintf(resbuf, " %d x %d @ scale %.2f", width, height, Gui::GuiScale); - //gui.addMessage(resbuf); -#endif -#endif /* STANDALONE_SERVER */ -} - -void Minecraft::reloadOptions() { - options.save(); - bool wasTouchscreen = options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN); - options.set(OPTIONS_USE_TOUCHSCREEN, useTouchscreen()); - options.save(); - - if ((wasTouchscreen != useTouchscreen()) || (inputHolder == 0)) - _reloadInput(); - - // user->name = options.username; - - LOGI("Reloading-options\n"); - - // @todo @fix Android and iOS behaves a bit differently when leaving - // an options screen (Android recreates OpenGL surface) - setSize(width, height); -} - -void Minecraft::_reloadInput() { -#ifndef STANDALONE_SERVER - delete inputHolder; - - const bool useTouchHolder = useTouchscreen(); - if (useTouchHolder) { - inputHolder = new TouchInputHolder(this, &options); - } else { - #if defined(ANDROID) || defined(__APPLE__) - inputHolder = new CustomInputHolder( - new XperiaPlayInput(&options), - new ControllerTurnInput(2, ControllerTurnInput::MODE_DELTA), - new IBuildInput()); - #else - inputHolder = new CustomInputHolder( - new KeyboardInput(&options), - new MouseTurnInput(MouseTurnInput::MODE_DELTA, width/2, height/2), - new MouseBuildInput()); - #endif - } - - mouseHandler.setTurnInput(inputHolder->getTurnInput()); - if (level && player) { - player->input = inputHolder->getMoveInput(); - } -#endif -} - - -// -// Multiplayer -// -void Minecraft::locateMultiplayer() { - isLookingForMultiplayer = true; - - raknetInstance->pingForHosts(19132); - netCallback = new ClientSideNetworkHandler(this, raknetInstance); -} - -void Minecraft::cancelLocateMultiplayer() { - isLookingForMultiplayer = false; - - raknetInstance->stopPingForHosts(); - - delete netCallback; - netCallback = NULL; -} - -bool Minecraft::joinMultiplayer( const PingedCompatibleServer& server ) -{ - if (isLookingForMultiplayer && netCallback) { - isLookingForMultiplayer = false; - return raknetInstance->connect(server.address.ToString(false), server.address.GetPort()); - } - return false; -} - -bool Minecraft::joinMultiplayerFromString( const std::string& server ) -{ - std::string ip = ""; - std::string port = "19132"; - - size_t pos = server.find(":"); - - if (pos != std::string::npos) { - ip = server.substr(0, pos); - port = server.substr(pos + 1); - } else { - ip = server; - } - - printf("%s \n", port.c_str()); - - if (isLookingForMultiplayer && netCallback) { - isLookingForMultiplayer = false; - printf("test"); - int portNum = atoi(port.c_str()); - return raknetInstance->connect(ip.c_str(), portNum); - } - return false; -} - -void Minecraft::hostMultiplayer(int port) { - // Tear down last instance - raknetInstance->disconnect(); - delete netCallback; - netCallback = NULL; - -#if !defined(NO_NETWORK) - netCallback = new ServerSideNetworkHandler(this, raknetInstance); - #ifdef STANDALONE_SERVER - raknetInstance->host("Server", port, 16); - #else - raknetInstance->host(options.getStringValue(OPTIONS_USERNAME), port); - #endif -#endif -} - -// -// Level generation -// -/*static*/ - -void* Minecraft::prepareLevel_tspawn(void *p_param) -{ - Minecraft* mc = (Minecraft*) p_param; - mc->generateLevel("Currently not used", mc->level); - return 0; -} - -void Minecraft::generateLevel( const std::string& message, Level* level ) -{ - Stopwatch s; - s.start(); - prepareLevel(message); - s.stop(); - s.print("Level generated: "); - - // "Unlock" - isGeneratingLevel = false; -} - -void Minecraft::_levelGenerated() -{ -#ifndef STANDALONE_SERVER - if (player == NULL) { - player = (LocalPlayer*) gameMode->createPlayer(level); - gameMode->initPlayer(player); - } - - if (player) { - player->input = inputHolder->getMoveInput(); - } - - if (levelRenderer != NULL) levelRenderer->setLevel(level); - if (particleEngine != NULL) particleEngine->setLevel(level); - - gameMode->adjustPlayer(player); - gui.onLevelGenerated(); -#endif - - level->validateSpawn(); - level->loadPlayer(player, true); - // if we are client side, we trust the server to have given us a correct position - if (player && !level->isClientSide) { - player->resetPos(false); - } - - this->cameraTargetPlayer = player; - - std::string serverName = options.getStringValue(OPTIONS_USERNAME) + " - " + level->getLevelData()->levelName; - - if (raknetInstance->isServer()) - raknetInstance->announceServer(serverName); - - if (netCallback) { - netCallback->levelGenerated(level); - } - -#if defined(WIN32) || defined(RPI) - if (_commandServer) { - delete _commandServer; - } - _commandServer = new CommandServer(this); - _commandServer->init(commandPort); -#endif - - // Hack to (hopefully) get the players to show (note: in LevelListener - // instead, since adding yourself always generates a entityAdded) - //EntityRenderDispatcher::getInstance()->onGraphicsReset(); - _hasSignaledGeneratingLevelFinished = true; -} - -Player* Minecraft::respawnPlayer(int playerId) { - for (unsigned int i = 0; i < level->players.size(); ++i) { - if (level->players[i]->entityId == playerId) { - resetPlayer(level->players[i]); - return level->players[i]; - } - } - return NULL; -} - -void Minecraft::resetPlayer(Player* player) { - level->validateSpawn(); - player->reset(); - - Pos p; - if(player->hasRespawnPosition()) { - p = player->getRespawnPosition(); - } - else { - p = level->getSharedSpawnPos(); - } - player->setPos((float)p.x + 0.5f, (float)p.y + 1.0f, (float)p.z + 0.5f); - player->resetPos(true); - - if (isCreativeMode()) - player->inventory->clearInventoryWithDefault(); -} - -void Minecraft::respawnPlayer() { - // RESET THE FRACKING PLAYER HERE - //bool slowCheck = false; - //for (int i = 0; i < level->entities.size(); ++i) - // if (level->entities[i] == player) slowCheck = true; - // - //LOGI("Has entity? %d, %d\n", level->getEntity(player->entityId), slowCheck); - - resetPlayer(player); - - // tell server (or other client) that we re-spawned - RespawnPacket packet(player); - raknetInstance->send(packet); -} - -void Minecraft::onGraphicsReset() -{ -#ifndef STANDALONE_SERVER - textures->clear(); - - font->onGraphicsReset(); - gui.onGraphicsReset(); - - if (levelRenderer) levelRenderer->onGraphicsReset(); - if (gameRenderer) gameRenderer->onGraphicsReset(); - - EntityRenderDispatcher::getInstance()->onGraphicsReset(); - TileEntityRenderDispatcher::getInstance()->onGraphicsReset(); -#endif -} - -int Minecraft::getProgressStatusId() { - return progressStageStatusId; -} - -const char* Minecraft::getProgressMessage() -{ - return progressMessages[progressStageStatusId]; -} - -bool Minecraft::isLevelGenerated() -{ - return level != NULL && !isGeneratingLevel; -} - -LevelStorageSource* Minecraft::getLevelSource() -{ - return storageSource; -} - -// int Minecraft::getLicenseId() { -// if (!LicenseCodes::isReady(_licenseId)) -// _licenseId = platform()->checkLicense(); -// return _licenseId; -// } - -void Minecraft::audioEngineOn() { -#ifndef STANDALONE_SERVER - soundEngine->enable(true); -#endif -} -void Minecraft::audioEngineOff() { -#ifndef STANDALONE_SERVER - soundEngine->enable(false); -#endif -} - -void Minecraft::setIsCreativeMode(bool isCreative) -{ -#ifdef CREATORMODE - delete gameMode; - gameMode = new CreatorMode(this); - _isCreativeMode = true; -#else - if (!gameMode || isCreative != _isCreativeMode) - { - delete gameMode; - if (isCreative) gameMode = new CreativeMode(this); - else gameMode = new SurvivalMode(this); - _isCreativeMode = isCreative; - } -#endif - if (player) - gameMode->initAbilities(player->abilities); -} - -bool Minecraft::isCreativeMode() { - return _isCreativeMode; -} - -bool Minecraft::isKindleFire(int kindleVersion) { - if (kindleVersion != 1) - return false; - - std::string model = platform()->getPlatformStringVar(PlatformStringVars::DEVICE_BUILD_MODEL); - std::string modelLower(model); - std::transform(modelLower.begin(), modelLower.end(), modelLower.begin(), tolower); - - return (modelLower.find("kindle") != std::string::npos) && (modelLower.find("fire") != std::string::npos); -} - -bool Minecraft::transformResolution(int* w, int* h) -{ - bool changed = false; - - // Kindle Fire 1: reporting wrong height in - // certain cases (e.g. after screen lock) - if (isKindleFire(1) && *h >= 560 && *h <= 620) { - *h = 580; - changed = true; - } - - return changed; -} - -ICreator* Minecraft::getCreator() -{ -#ifdef CREATORMODE - return ((CreatorMode*)gameMode)->getCreator(); -#else - return NULL; -#endif -} - -void Minecraft::optionUpdated(OptionId option, bool value ) { - if(netCallback != NULL && option == OPTIONS_SERVER_VISIBLE) { - ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) netCallback; - ss->allowIncomingConnections(value); - } else if (option == OPTIONS_USE_TOUCHSCREEN) { - _reloadInput(); - } -} - -void Minecraft::optionUpdated(OptionId option, float value ) { -// #ifndef STANDALONE_SERVER -// if(option == OPTIONS_PIXELS_PER_MILLIMETER) { -// pixelCalcUi.setPixelsPerMillimeter(value * Gui::InvGuiScale); -// pixelCalc.setPixelsPerMillimeter(value); -// } -// #endif -} - -void Minecraft::optionUpdated(OptionId option, int value ) { - if(option == OPTIONS_GUI_SCALE) { - // reapply screen scaling using current window size - setSize(width, height); - } -} +#include "Minecraft.h" +#include "Options.h" +#include "client/Options.h" +#include "client/player/input/IBuildInput.h" +#include "platform/input/Keyboard.h" +#include "world/item/Item.h" +#include "world/item/ItemInstance.h" +#include +#include + +#if defined(APPLE_DEMO_PROMOTION) + #define NO_NETWORK +#endif + +#if defined(RPI) + #define CREATORMODE +#endif +#include "../network/RakNetInstance.h" +#include "../network/ClientSideNetworkHandler.h" +#include "../network/ServerSideNetworkHandler.h" +//#include "../network/Packet.h" +#include "../world/entity/player/Inventory.h" +#include "../world/level/tile/Tile.h" +#include "../world/level/storage/LevelStorageSource.h" +#include "../world/level/storage/LevelStorage.h" +#include "player/input/KeyboardInput.h" +#include "player/input/ControllerTurnInput.h" +#include "player/input/XperiaPlayInput.h" +#include "world/level/chunk/ChunkSource.h" + +#ifndef STANDALONE_SERVER +#include "player/input/touchscreen/TouchInputHolder.h" +#include "particle/ParticleEngine.h" +#include "gui/Screen.h" +#include "gui/Font.h" +#include "gui/screens/RenameMPLevelScreen.h" +#include "gui/screens/ConsoleScreen.h" +#include "gui/screens/ChatScreen.h" +#include "sound/SoundEngine.h" +#include "player/input/touchscreen/TouchscreenInput.h" +#include "renderer/Chunk.h" +#include "gui/screens/PrerenderTilesScreen.h" +#include "renderer/Textures.h" +#include "gui/screens/DeathScreen.h" +#include "gui/screens/FurnaceScreen.h" +#include "gui/screens/ArmorScreen.h" +#include "renderer/tileentity/TileEntityRenderDispatcher.h" +#include "renderer/ptexture/DynamicTexture.h" +#include "renderer/GameRenderer.h" +#include "renderer/ItemInHandRenderer.h" +#include "renderer/LevelRenderer.h" +#include "renderer/entity/EntityRenderDispatcher.h" +#include "gui/Screen.h" +#include "gui/Font.h" +#include "gui/screens/RenameMPLevelScreen.h" +#include "sound/SoundEngine.h" +#endif // STANDALONE_SERVER + +#include "player/LocalPlayer.h" +#include "gamemode/CreativeMode.h" +#include "gamemode/SurvivalMode.h" +#include "player/LocalPlayer.h" +#include "../platform/CThread.h" +#include "../platform/input/Mouse.h" +#include "../AppPlatform.h" +#include "../LicenseCodes.h" +#include "../util/PerfTimer.h" +#include "../util/PerfRenderer.h" +#include "player/input/MouseBuildInput.h" + +#include "player/input/IInputHolder.h" + +#include "player/input/MouseTurnInput.h" +#include "../world/entity/MobFactory.h" +#include "../world/level/MobSpawner.h" +#include "../util/Mth.h" +#include "../network/packet/InteractPacket.h" +#include "../network/packet/RespawnPacket.h" +#include "../network/packet/AdventureSettingsPacket.h" +#include "../network/packet/SetSpawnPositionPacket.h" +#include "IConfigListener.h" +#include "../world/entity/MobCategory.h" +#include "../world/Difficulty.h" +#include "../server/ServerLevel.h" + +#ifdef CREATORMODE +#include "../server/CreatorLevel.h" +#endif + +#include "../network/command/CommandServer.h" +#include "gamemode/CreatorMode.h" + +static void checkGlError(const char* tag) { +#ifdef GLDEBUG + while (1) { + const int errCode = glGetError(); + if (errCode == GL_NO_ERROR) break; + + LOGE("################\nOpenGL-error @ %s : #%d\n", tag, errCode); + } +#endif /*GLDEBUG*/ +} + +/*static*/ +const char* Minecraft::progressMessages[] = { + "Locating server", + "Building terrain", + "Preparing", + "Saving chunks" +}; + +int Minecraft::customDebugId = Minecraft::CDI_NONE; + +#if defined(_MSC_VER) + #pragma warning( disable : 4355 ) // 'this' pointer in initialization list which is perfectly legal +#endif + +bool Minecraft::useAmbientOcclusion = false; + +Minecraft::Minecraft() : + level(NULL), + player(NULL), + cameraTargetPlayer(NULL), + levelRenderer(NULL), + gameRenderer(NULL), +#ifndef STANDALONE_SERVER + particleEngine(NULL), + _perfRenderer(NULL), +#endif + _commandServer(NULL), +#ifndef STANDALONE_SERVER + textures(NULL), +#endif + lastTickTime(-1), + lastTime(0), + ticksSinceLastUpdate(0), + gameMode(NULL), + mouseGrabbed(true), + missTime(0), + pause(false), + _running(false), + timer(20), +#ifndef STANDALONE_SERVER + gui(this), +#endif + netCallback(NULL), +#ifndef STANDALONE_SERVER + screen(NULL), + font(NULL), +#endif + screenMutex(false), +#ifndef STANDALONE_SERVER + scheduledScreen(NULL), + hasScheduledScreen(false), + soundEngine(NULL), +#endif + ticks(0), + isGeneratingLevel(false), + _hasSignaledGeneratingLevelFinished(true), + generateLevelThread(NULL), + progressStagePercentage(0), + progressStageStatusId(0), + isLookingForMultiplayer(false), + _licenseId(LicenseCodes::WAIT_PLATFORM_NOT_READY), + inputHolder(0), + _supportsNonTouchscreen(false), +#ifndef STANDALONE_SERVER + screenChooser(this), +#endif + width(1), height(1), + //_respawnPlayerTicks(-1), +#ifdef __APPLE__ + _isSuperFast(false), +#endif + _powerVr(false), + commandPort(4711), + reserved_d1(0),reserved_d2(0), + reserved_f1(0),reserved_f2(0), options(this) +{ +//#ifdef ANDROID + +#if defined(NO_NETWORK) + raknetInstance = new IRakNetInstance(); +#else + raknetInstance = new RakNetInstance(); +#endif +#ifndef STANDALONE_SERVER + soundEngine = new SoundEngine(20.0f); + soundEngine->init(this, &options); +#endif + //setupPieces(); +} + +Minecraft::~Minecraft() +{ + delete netCallback; + delete raknetInstance; +#ifndef STANDALONE_SERVER + delete levelRenderer; + delete gameRenderer; + delete particleEngine; + + delete soundEngine; +#endif + delete gameMode; +#ifndef STANDALONE_SERVER + delete font; + delete textures; + + if (screen != NULL) { + delete screen; + screen = NULL; + } +#endif + if (level != NULL) { + level->saveGame(); + if (level->getChunkSource()) + level->getChunkSource()->saveAll(true); + delete level->getLevelStorage(); + delete level; + level = NULL; + } + + //delete player; + delete inputHolder; + + delete storageSource; + delete _perfRenderer; + delete _commandServer; + + MobFactory::clearStaticTestMobs(); +#ifndef STANDALONE_SERVER + EntityRenderDispatcher::destroy(); +#endif +} + +// Only called by server +void Minecraft::selectLevel( const std::string& levelId, const std::string& levelName, const LevelSettings& settings ) +{ +#if defined(CREATORMODE) + level = new CreatorLevel( +#else + level = new ServerLevel( +#endif + storageSource->selectLevel(levelId, false), + levelName, + settings, + SharedConstants::GeneratorVersion); + + // note: settings is useless beyond this point, since it's + // either copied to LevelData (or LevelData read from file) + setLevel(level, "Generating level"); + setIsCreativeMode(level->getLevelData()->getGameType() == GameType::Creative); + _running = true; +} + +void Minecraft::setLevel(Level* level, const std::string& message /* ="" */, LocalPlayer* forceInsertPlayer /* = NULL */) { + cameraTargetPlayer = NULL; + LOGI("Seed is %ld\n", level->getSeed()); + + if (level != NULL) { + level->raknetInstance = raknetInstance; + gameMode->initLevel(level); + + if (!player && forceInsertPlayer) + { + player = forceInsertPlayer; + player->resetPos(false); + //level->addEntity(forceInsertPlayer); + } + else if (player != NULL) { + player->resetPos(false); + if (level != NULL) { + level->addEntity(player); + } + } + this->level = level; + _hasSignaledGeneratingLevelFinished = false; +#ifdef STANDALONE_SERVER + const bool threadedLevelCreation = false; +#else + const bool threadedLevelCreation = true; +#endif + + if (threadedLevelCreation) { + // Threaded + // "Lock" + isGeneratingLevel = true; + generateLevelThread = new CThread(Minecraft::prepareLevel_tspawn, this); + } else { + // Non-threaded + generateLevel("Currently not used", level); + } + } else { + player = NULL; + } + + this->lastTickTime = 0; + this->_running = true; +} + +void Minecraft::leaveGame(bool renameLevel /*=false*/) +{ + if (isGeneratingLevel || !_hasSignaledGeneratingLevelFinished) + return; + + isGeneratingLevel = false; + bool saveLevel = level && (!level->isClientSide || renameLevel); + + raknetInstance->disconnect(); + if (saveLevel) { + // If server or wanting to save level as client, save all unsaved chunks! + level->getChunkSource()->saveAll(true); + } + + LOGI("Clearing levels\n"); + + cameraTargetPlayer = NULL; +#ifndef STANDALONE_SERVER + levelRenderer->setLevel(NULL); + particleEngine->setLevel(NULL); +#endif + LOGI("Erasing callback\n"); + delete netCallback; + netCallback = NULL; + + LOGI("Erasing level\n"); + if (level != NULL) { + delete level->getLevelStorage(); + delete level; + level = NULL; + } + //delete player; + player = NULL; + cameraTargetPlayer = NULL; + + _running = false; +#ifndef STANDALONE_SERVER + gui.clearMessages(); + if (renameLevel) { + setScreen(new RenameMPLevelScreen(LevelStorageSource::TempLevelId)); + } + else + screenChooser.setScreen(SCREEN_STARTMENU); +#endif +} + +void Minecraft::prepareLevel(const std::string& title) { + LOGI("status: 1\n"); + progressStageStatusId = 1; + + Stopwatch A, B, C, D; + A.start(); + + Stopwatch L; + + // Dont update lights if we load the level (ok, actually just with leveldata version=1.+(?)) + if (!level->isNew()) + level->setUpdateLights(false); + + int Max = CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH; + int pp = 0; + for (int x = 8; x < (CHUNK_CACHE_WIDTH * CHUNK_WIDTH); x += CHUNK_WIDTH) { + for (int z = 8; z < (CHUNK_CACHE_WIDTH * CHUNK_WIDTH); z += CHUNK_WIDTH) { + progressStagePercentage = 100 * pp++ / Max; + //printf("level generation progress %d\n", progressStagePercentage); + B.start(); + level->getTile(x, 64, z); + B.stop(); + L.start(); + if (level->isNew()) + while (level->updateLights()) + ; + L.stop(); + } + } + A.stop(); + level->setUpdateLights(true); + + C.start(); + for (int x = 0; x < CHUNK_CACHE_WIDTH; x++) + { + for (int z = 0; z < CHUNK_CACHE_WIDTH; z++) + { + LevelChunk* chunk = level->getChunk(x, z); + if (chunk && !chunk->createdFromSave) + { + chunk->unsaved = false; + chunk->clearUpdateMap(); + } + } + } + C.stop(); + + LOGI("status: 3\n"); + progressStageStatusId = 3; + if (level->isNew()) { + level->setInitialSpawn(); // @note: should obviously be called from Level itself + level->saveLevelData(); + level->getChunkSource()->saveAll(false); + level->saveGame(); + } else { + level->saveLevelData(); + level->loadEntities(); + } + + progressStagePercentage = -1; + progressStageStatusId = 2; + LOGI("status: 2\n"); + + D.start(); + level->prepare(); + D.stop(); + + A.print("Generate level: "); + L.print(" - light: "); + B.print(" - getTl: "); + C.print(" - clear: "); + D.print(" - prepr: "); + progressStageStatusId = 0; +} + +void Minecraft::update() { + //LOGI("Enter Update\n"); + + if (Options::debugGl) + LOGI(">>>>>>>>>>\n"); + + TIMER_PUSH("root"); + + //if (level) { + // LOGI("numplayers: %d\n", level->players.size()); + // for (int i = 0; i < level->players.size(); ++i) { + // Player* p = level->players[i]; + // bool inEnt = std::find(level->entities.begin(), level->entities.end(), p) != level->entities.end(); + // LOGI(" %p, %d, %d - in? %d\n", p, p->entityId, p->owner.ToUint32(p->owner), inEnt); + // } + //} + + // If we're paused (local world / invisible server), freeze gameplay and + // networking and only keep UI responsive. + bool freezeGame = pause; + + if (!freezeGame) { + timer.advanceTime(); + } + + if (raknetInstance && !freezeGame) { + raknetInstance->runEvents(netCallback); + } + + TIMER_PUSH("tick"); + int toTick = freezeGame ? 1 : timer.ticks; + if (!freezeGame) timer.ticks = 0; + for (int i = 0; i < toTick; ++i, ++ticks) + tick(i, toTick-1); + + TIMER_POP_PUSH("updatelights"); + if (level && !isGeneratingLevel) { + level->updateLights(); + } + TIMER_POP(); + + #ifndef STANDALONE_SERVER + if (gameMode != NULL) gameMode->render(timer.a); + TIMER_PUSH("sound"); + soundEngine->update(player, timer.a); + TIMER_POP_PUSH("render"); + gameRenderer->render(timer.a); + TIMER_POP(); + #else + CThread::sleep(1); + #endif +#ifndef STANDALONE_SERVER + Multitouch::resetThisUpdate(); +#endif + TIMER_POP(); +#ifndef STANDALONE_SERVER + checkGlError("Update finished"); + + if (options.getBooleanValue(OPTIONS_RENDER_DEBUG)) { +#ifndef PLATFORM_DESKTOP + if (!PerfTimer::enabled) { + PerfTimer::reset(); + PerfTimer::enabled = true; + } + _perfRenderer->renderFpsMeter(1); + checkGlError("render debug"); +#endif + } else { + PerfTimer::enabled = false; + } +#endif + //LOGI("Exit Update\n"); +} + +void Minecraft::tick(int nTick, int maxTick) { + if (missTime > 0) missTime--; +#ifndef STANDALONE_SERVER + if (!screen && player) { + if (player->health <= 0) { + setScreen(new DeathScreen()); + } + } +#endif + TIMER_PUSH("gameMode"); + if (level && !pause) { + gameMode->tick(); + } + + TIMER_POP_PUSH("commandServer"); + if (level && _commandServer) { + _commandServer->tick(); + } + + TIMER_POP_PUSH("input"); + tickInput(); +#ifndef STANDALONE_SERVER + TIMER_POP_PUSH("gui"); + gui.tick(); +#endif + // + // Ongoing level generation in a (perhaps) different thread. When it's + // ready, _levelGenerated() is called once and any threads are deleted. + // + if (isGeneratingLevel) { + return; + } else if (!_hasSignaledGeneratingLevelFinished) { + if (generateLevelThread) { + delete generateLevelThread; + generateLevelThread = NULL; + } + _levelGenerated(); + } + + // + // Normal game loop, run before or efter level generation + // + if (level != NULL) + { + if (!pause) { +#ifndef STANDALONE_SERVER + TIMER_POP_PUSH("gameRenderer"); + gameRenderer->tick(nTick, maxTick); + + TIMER_POP_PUSH("levelRenderer"); + levelRenderer->tick(); +#endif + level->difficulty = options.getIntValue(OPTIONS_DIFFICULTY); + if (level->isClientSide) level->difficulty = Difficulty::EASY; + + TIMER_POP_PUSH("level"); + level->tickEntities(); + level->tick(); +#ifndef STANDALONE_SERVER + TIMER_POP_PUSH("animateTick"); + if (player) { + level->animateTick(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + } +#endif + } + } +#ifndef STANDALONE_SERVER + textures->loadAndBindTexture("terrain.png"); + if (!pause && !(screen && !screen->renderGameBehind())) { + #if !defined(RPI) + #ifdef __APPLE__ + if (isSuperFast()) + #endif + { + if (nTick == maxTick) { + TIMER_POP_PUSH("textures"); + textures->tick(true); + } + } + #endif + } + TIMER_POP_PUSH("particles"); + if (!pause) { + particleEngine->tick(); + } + if (screen) { + screenMutex = true; + screen->tick(); + screenMutex = false; + } + + // @note: fix to keep "isPressed" and "isReleased" as long as necessary. + // Most likely keyboard and mouse could/should be reset here as well. + Multitouch::reset(); +#endif + TIMER_POP(); +} + +class InputRAII { +public: + ~InputRAII() { +#ifndef STANDALONE_SERVER + Mouse::reset(); + Keyboard::reset(); +#endif + } +}; + +void Minecraft::tickInput() { +#ifndef STANDALONE_SERVER + InputRAII raiiInput; + + if (screen && !screen->passEvents) { + screenMutex = true; + screen->updateEvents(); + //screen->updateSetScreen(); + screenMutex = false; + if (hasScheduledScreen) { + setScreen(scheduledScreen); + scheduledScreen = NULL; + hasScheduledScreen = false; + } + return; + } + + if (!player) { + return; + } + +#ifdef RPI + bool mouseDiggable = true; + bool allowGuiClicks = !mouseGrabbed; +#else + bool mouseDiggable = !gui.isInside(Mouse::getX(), Mouse::getY()); + bool allowGuiClicks = true; +#endif + + TIMER_PUSH("mouse"); + while (Mouse::next()) { + //if (Mouse::getButtonState(MouseAction::ACTION_LEFT)) + // LOGI("mouse-down-at: %d, %d\n", Mouse::getX(), Mouse::getY()); + int passedTime = getTimeMs() - lastTickTime; + if (passedTime > 200) continue; // @note: As long Mouse::clear CLEARS the whole buffer, it's safe to break here + // But since it might be rewritten anyway (and hopefully there aren't a lot of messages, we just continue. + + const MouseAction& e = Mouse::getEvent(); + + if (!useTouchscreen() && !mouseGrabbed) { + if (!screen && e.data == MouseAction::DATA_DOWN) { + grabMouse(); + } + } + + if (allowGuiClicks && e.action == MouseAction::ACTION_LEFT && e.data == MouseAction::DATA_DOWN) { + gui.handleClick(MouseAction::ACTION_LEFT, Mouse::getX(), Mouse::getY()); + } + + if (e.action == MouseAction::ACTION_WHEEL) { + // If chat/console is open, use the wheel to scroll through chat history. + if (screen && (dynamic_cast(screen) || dynamic_cast(screen))) { + gui.scrollChat(e.dy); + } else { + Inventory* v = player->inventory; + + int numSlots = gui.getNumSlots(); + if (!useTouchscreen()) numSlots--; + + int slot = (v->selected - e.dy + numSlots) % numSlots; + v->selectSlot(slot); + } + } + /* + if (mouseDiggable && options.useMouseForDigging) { + if (Mouse::getEventButton() == MouseAction::ACTION_LEFT && Mouse::getEventButtonState()) { + handleMouseClick(MouseAction::ACTION_LEFT); + lastClickTick = ticks; + } + if (Mouse::getEventButton() == MouseAction::ACTION_RIGHT && Mouse::getEventButtonState()) { + handleMouseClick(MouseAction::ACTION_RIGHT); + lastClickTick = ticks; + } + } + */ + } + + TIMER_POP_PUSH("keyboard"); + while (Keyboard::next()) { + int key = Keyboard::getEventKey(); + bool isPressed = (Keyboard::getEventKeyState() == KeyboardAction::KEYDOWN); + player->setKey(key, isPressed); + + if (isPressed) { + gui.handleKeyPressed(key); + + if (key >= '0' && key <= '9') { + int digit = key - '0'; + int slot = digit - 1; + + if (slot >= 0 && slot < gui.getNumSlots()) + player->inventory->selectSlot(slot); + + #if defined(WIN32) + if (digit >= 1 && GetAsyncKeyState(VK_CONTROL) < 0) { + // Set adventure settings here! + AdventureSettingsPacket p(level->adventureSettings); + p.toggle((AdventureSettingsPacket::Flags)(1 << slot)); + p.fillIn(level->adventureSettings); + raknetInstance->send(p); + } + if (digit == 0) { + Pos pos((int)player->x, (int)player->y-1, (int)player->z); + SetSpawnPositionPacket p(pos); + raknetInstance->send(p); + } + #endif + } + + if (key == Keyboard::KEY_LEFT_CTRL) { + player->setSprinting(true); + } + + if (key == Keyboard::KEY_E) { + screenChooser.setScreen(SCREEN_BLOCKSELECTION); + } + + if (!screen && key == Keyboard::KEY_T && level) { + setScreen(new ConsoleScreen()); + } + + if (key == Keyboard::KEY_F3) { + options.toggle(OPTIONS_RENDER_DEBUG); + } + + if (key == Keyboard::KEY_F5) { + options.toggle(OPTIONS_THIRD_PERSON_VIEW); + /* + ImprovedNoise noise; + for (int i = 0; i < 16; ++i) + printf("%d\t%f\n", i, noise.grad2(i, 3, 8)); + */ + } + + if (!screen && key == Keyboard::KEY_O || key == 250) { + releaseMouse(); + } + + if (key == Keyboard::KEY_F) { + int dst = options.getIntValue(OPTIONS_VIEW_DISTANCE); + options.set(OPTIONS_VIEW_DISTANCE, (dst + 1) % 4); + } + #ifdef CHEATS + if (key == Keyboard::KEY_U) { + onGraphicsReset(); + player->heal(100); + } + + if (key == Keyboard::KEY_B || key == 108) // Toggle the game mode + setIsCreativeMode(!isCreativeMode()); + + if (key == Keyboard::KEY_P) // Step forward in time + level->setTime( level->getTime() + 1000); + + if (key == Keyboard::KEY_G) { + setScreen(new ArmorScreen()); + /* + std::vector& boxs = level->getCubes(NULL, AABB(128.1f, 73, 128.1f, 128.9f, 74.9f, 128.9f)); + LOGI("boxes: %d\n", (int)boxs.size()); + */ + } + + if (key == Keyboard::KEY_Y) { + textures->reloadAll(); + player->hurtTo(2); + } + if (key == Keyboard::KEY_Z || key == 108) { + for (int i = 0; i < 1; ++i) { + Mob* mob = NULL; + int forceId = 0;//MobTypes::Sheep; + + int types[] = { + MobTypes::Sheep, + MobTypes::Pig, + MobTypes::Chicken, + MobTypes::Cow, + }; + + int mobType = (forceId > 0)? forceId : types[Mth::random(sizeof(types) / sizeof(int))]; + mob = MobFactory::CreateMob(mobType, level); + + //((Animal*)mob)->setAge(-1000); + float dx = 4 - 8 * Mth::random() + 4 * Mth::sin(Mth::DEGRAD * player->yRot); + float dz = 4 - 8 * Mth::random() + 4 * Mth::cos(Mth::DEGRAD * player->yRot); + if (mob && !MobSpawner::addMob(level, mob, player->x + dx, player->y, player->z + dz, Mth::random()*360, 0, true)) + delete mob; + } + } + + if (key == Keyboard::KEY_X) { + const EntityList& entities = level->getAllEntities(); + for (int i = entities.size()-1; i >= 0; --i) { + Entity* e = entities[i]; + if (!e->isPlayer()) + level->removeEntity(e); + } + } + + if (key == Keyboard::KEY_C /*|| key == 4*/) { + player->inventory->clearInventoryWithDefault(); + // @todo: Add saving here for benchmarking + } + if (key == Keyboard::KEY_H) { + setScreen( new PrerenderTilesScreen() ); + } + + if (key == Keyboard::KEY_O) { + for (int i = Inventory::MAX_SELECTION_SIZE; i < player->inventory->getContainerSize(); ++i) + if (player->inventory->getItem(i)) + player->inventory->dropSlot(i, false); + } + if (key == Keyboard::KEY_M) { + Difficulty difficulty = (Difficulty)options.getIntValue(OPTIONS_DIFFICULTY); + options.set(OPTIONS_DIFFICULTY, (difficulty == Difficulty::PEACEFUL)? + Difficulty::NORMAL : Difficulty::PEACEFUL); + //setIsCreativeMode( !isCreativeMode() ); + } + + if (options.getBooleanValue(OPTIONS_RENDER_DEBUG)) { + if (key >= '0' && key <= '9') { + _perfRenderer->debugFpsMeterKeyPress(key - '0'); + } + } + #endif + + if (key == Keyboard::KEY_ESCAPE) + pauseGame(false); + + #ifndef OPENGL_ES + if (key == Keyboard::KEY_P) { + static bool isWireFrame = false; + isWireFrame = !isWireFrame; + glPolygonMode(GL_FRONT, isWireFrame? GL_LINE : GL_FILL); + //glPolygonMode(GL_BACK, isWireFrame? GL_LINE : GL_FILL); + } + #endif + } + #ifdef WIN32 + if (key == Keyboard::KEY_M) { + for (int i = 0; i < 5 * SharedConstants::TicksPerSecond; ++i) + level->tick(); + } + #endif + } + + TIMER_POP_PUSH("handlemouse"); + + static bool prevMouseDownLeft = false; + + if (Mouse::getButtonState(MouseAction::ACTION_LEFT) == 0) { + gameMode->stopDestroyBlock(); + } + + if (!Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) { + gameMode->releaseUsingItem(player); + } + + if (useTouchscreen()) { + // Touch: gesture recognizer classifies the action type (turn/destroy/build) + BuildActionIntention bai; + + if (inputHolder && inputHolder->getBuildInput()->tickBuild(player, &bai)) { + handleBuildAction(&bai); + } else { + gameMode->stopDestroyBlock(); + } + } else { + // Desktop: left mouse = destroy/attack + if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) { + auto baiFlags = BuildActionIntention::BAI_REMOVE | BuildActionIntention::BAI_ATTACK; + + if (!prevMouseDownLeft) baiFlags |= BuildActionIntention::BAI_FIRSTREMOVE; + + BuildActionIntention bai(baiFlags); + handleBuildAction(&bai); + } + + prevMouseDownLeft = Mouse::isButtonDown(MouseAction::ACTION_LEFT); + + // Build and use/interact is on same button + // USPESHNO spizheno + static int buildHoldTicks = 0; + if (Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) { + if (buildHoldTicks >= 5) buildHoldTicks = 0; + + if (++buildHoldTicks == 1) { + BuildActionIntention bai(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_INTERACT); + handleBuildAction(&bai); + } + } else { + buildHoldTicks = 0; + gameMode->releaseUsingItem(player); + } + } + + lastTickTime = getTimeMs(); + + // we have (hopefully) handled the keyboard & mouse queue and it + // can now be emptied. If wanted, the reset could be changed to: + // index -= numRead; // then this code doesn't have to be placed here + // + it prepares for tick not handling all or any events. + // update: RAII'ing instead, see above + //Keyboard::reset(); + //Mouse::reset(); + + TIMER_POP(); +#endif +} + +void Minecraft::handleBuildAction(BuildActionIntention* action) { +#ifndef STANDALONE_SERVER + if (action->isRemove()) { + if (missTime > 0) return; + player->swing(); + } + if(player->isUsingItem()) + return; + bool mayUse = true; + + if (!hitResult.isHit()) { + if (action->isRemove() && !gameMode->isCreativeType()) { + missTime = 10; + } + } else if (hitResult.type == ENTITY) { + if (action->isAttack()) { + player->swing(); + //LOGI("attacking!\n"); + InteractPacket packet(InteractPacket::Attack, player->entityId, hitResult.entity->entityId); + raknetInstance->send(packet); + gameMode->attack(player, hitResult.entity); + } else if (action->isInteract()) { + if (hitResult.entity->interactPreventDefault()) + mayUse = false; + //LOGI("interacting!\n"); + InteractPacket packet(InteractPacket::Interact, player->entityId, hitResult.entity->entityId); + raknetInstance->send(packet); + gameMode->interact(player, hitResult.entity); + } + } else if (hitResult.type == TILE) { + int x = hitResult.x; + int y = hitResult.y; + int z = hitResult.z; + int face = hitResult.f; + + int oldTileId = level->getTile(x, y, z); + Tile* oldTile = Tile::tiles[oldTileId]; + + //bool tryDestroyBlock = false; + + if (action->isRemove()) { + if (!oldTile) + return; + + //LOGI("tile: %s - %d, %d, %d. b: %f - %f\n", oldTile->getDescriptionId().c_str(), x, y, z, oldTile->getBrightness(level, x, y, z), oldTile->getBrightness(level, x, y+1, z)); + level->extinguishFire(x, y, z, hitResult.f); + + if (action->isFirstRemove()) { + gameMode->startDestroyBlock(x, y, z, hitResult.f); + } else { + gameMode->continueDestroyBlock(x, y, z, hitResult.f); + } + + particleEngine->crack(x, y, z, hitResult.f); + player->swing(); + } + else { + ItemInstance* item = player->inventory->getSelected(); + if (gameMode->useItemOn(player, level, item, x, y, z, face, hitResult.pos)) { + mayUse = false; + player->swing(); + #ifdef RPI + } else if (item && item->id == ((Item*)Item::sword_iron)->id) { + player->swing(); + #endif + } + if (item && item->count <= 0) { + player->inventory->clearSlot(player->inventory->selected); + } + //} else if (item && item->count != oldCount) { + // gameRenderer->itemInHandRenderer->itemPlaced(); + //} + } + } + if (mayUse && action->isInteract()) { + ItemInstance* item = player->inventory->getSelected(); + if (item && !player->isUsingItem()) { + if (gameMode->useItem(player, level, item)) { + gameRenderer->itemInHandRenderer->itemUsed(); + } + if (item && item->count <= 0) { + player->inventory->clearSlot(player->inventory->selected); + } + } + } +#endif +} + +bool Minecraft::isOnlineClient() +{ + return (level != NULL && level->isClientSide); +} + +bool Minecraft::isOnline() +{ + return netCallback != NULL; +} + +void Minecraft::pauseGame(bool isBackPaused) { + // Only freeze gameplay when running a local server and it is not accepting + // incoming connections (invisible server), which includes typical single- + // player/lobby mode. If the server is visible, the game should keep ticking. + bool canFreeze = false; + if (raknetInstance && raknetInstance->isServer() && netCallback) { + ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) netCallback; + if (!ss->allowsIncomingConnections()) + canFreeze = true; + } + pause = canFreeze; + +#ifndef STANDALONE_SERVER + if (screen != NULL) return; + screenChooser.setScreen(isBackPaused? SCREEN_PAUSEPREV : SCREEN_PAUSE); +#endif +} +void Minecraft::gameLostFocus() { +#ifndef STANDALONE_SERVER + if(screen != NULL) { + screen->lostFocus(); + } +#endif +} + + +void Minecraft::setScreen( Screen* screen ) +{ +#ifndef STANDALONE_SERVER + Mouse::reset(); + Multitouch::reset(); + Multitouch::resetThisUpdate(); + + if (screenMutex) { + hasScheduledScreen = true; + scheduledScreen = screen; + return; + } + + if (screen != NULL && screen->isErrorScreen()) + return; + if (screen == NULL && level == NULL) + screen = screenChooser.createScreen(SCREEN_STARTMENU); + + if (this->screen != NULL) { + this->screen->removed(); + delete this->screen; + } + + this->screen = screen; + if (screen != NULL) { + releaseMouse(); + //ScreenSizeCalculator ssc = new ScreenSizeCalculator(options, width, height); + int screenWidth = (int)(width * Gui::InvGuiScale); //ssc.getWidth(); + int screenHeight = (int)(height * Gui::InvGuiScale); //ssc.getHeight(); + screen->init(this, screenWidth, screenHeight); + + if (screen->isInGameScreen() && level) { + level->saveLevelData(); + level->saveGame(); + } + + //noRender = false; + } else { + // Closing a screen and returning to the game should unpause. + pause = false; + grabMouse(); + } +#endif +} + +void Minecraft::grabMouse() +{ +#ifndef STANDALONE_SERVER + if (mouseGrabbed) return; + mouseGrabbed = true; + mouseHandler.grab(); + //setScreen(NULL); +#endif +} + +void Minecraft::releaseMouse() +{ +#ifndef STANDALONE_SERVER + if (!mouseGrabbed) { + return; + } + if (player) { + player->releaseAllKeys(); + } + mouseGrabbed = false; + mouseHandler.release(); +#endif +} + +bool Minecraft::useTouchscreen() { +#ifdef RPI + return false; +#endif + return options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN) && !_supportsNonTouchscreen; +} +bool Minecraft::supportNonTouchScreen() { + return _supportsNonTouchscreen; +} +void Minecraft::init() +{ +#ifndef STANDALONE_SERVER + checkGlError("Init enter"); + + _supportsNonTouchscreen = !platform()->supportsTouchscreen(); + + LOGI("IS TOUCHSCREEN? %d\n", options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN)); + + textures = new Textures(&options, platform()); + textures->addDynamicTexture(new WaterTexture()); + textures->addDynamicTexture(new WaterSideTexture()); + gui.texturesLoaded(textures); + + levelRenderer = new LevelRenderer(this); + gameRenderer = new GameRenderer(this); + particleEngine = new ParticleEngine(level, textures); + + // Platform specific initialization here + font = new Font(&options, "font/default8.png", textures); + + _perfRenderer = new PerfRenderer(this, font); + + checkGlError("Init complete"); +#endif + + options.load(); + + setIsCreativeMode(false); // false means it's Survival Mode + reloadOptions(); +} + +void Minecraft::setSize(int w, int h) { +#ifndef STANDALONE_SERVER + transformResolution(&w, &h); + + width = w; + height = h; + + int guiScale = options.getIntValue(OPTIONS_GUI_SCALE); + + // determine gui scale, optionally overriding auto + if (guiScale != 0) { + // manual selection: 1->small, 2->medium, 3->large, 4->larger, 5->largest + switch (guiScale) { + case 1: Gui::GuiScale = 2.0f; break; + case 2: Gui::GuiScale = 3.0f; break; + case 3: Gui::GuiScale = 4.0f; break; + case 4: Gui::GuiScale = 5.0f; break; + case 5: Gui::GuiScale = 6.0f; break; + default: Gui::GuiScale = 1.0f; break; // auto + } + } else { + // auto compute from resolution + if (width >= 1000) { + #ifdef __APPLE__ + Gui::GuiScale = (width > 2000)? 8.0f : 4.0f; + #else + Gui::GuiScale = 4.0f; + #endif + } + else if (width >= 800) { +#ifdef __APPLE__ + Gui::GuiScale = 4.0f; +#else + Gui::GuiScale = 3.0f; +#endif + } + else if (width >= 400) + Gui::GuiScale = 2.0f; + else + Gui::GuiScale = 1.0f; + } + + Gui::InvGuiScale = 1.0f / Gui::GuiScale; + int screenWidth = (int)(width * Gui::InvGuiScale); + int screenHeight = (int)(height * Gui::InvGuiScale); + + // if (platform()) { + // float pixelsPerMillimeter = options.getProgressValue(&Option::PIXELS_PER_MILLIMETER); + // pixelCalc.setPixelsPerMillimeter(pixelsPerMillimeter); + // pixelCalcUi.setPixelsPerMillimeter(pixelsPerMillimeter * Gui::InvGuiScale); + // } + + Config config = createConfig(this); + gui.onConfigChanged(config); + + if (screen) + screen->setSize(screenWidth, screenHeight); + + if (inputHolder) + inputHolder->onConfigChanged(config); + //LOGI("Setting size: %d, %d: %f\n", width, height, Gui::InvGuiScale); + +#ifdef WIN32 + char resbuf[128]; + sprintf(resbuf, " %d x %d @ scale %.2f", width, height, Gui::GuiScale); + //gui.addMessage(resbuf); +#endif +#endif /* STANDALONE_SERVER */ +} + +void Minecraft::reloadOptions() { + options.save(); + bool wasTouchscreen = options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN); + options.set(OPTIONS_USE_TOUCHSCREEN, useTouchscreen()); + options.save(); + + if ((wasTouchscreen != useTouchscreen()) || (inputHolder == 0)) + _reloadInput(); + + // user->name = options.username; + + LOGI("Reloading-options\n"); + + // @todo @fix Android and iOS behaves a bit differently when leaving + // an options screen (Android recreates OpenGL surface) + setSize(width, height); +} + +void Minecraft::_reloadInput() { +#ifndef STANDALONE_SERVER + delete inputHolder; + + const bool useTouchHolder = useTouchscreen(); + if (useTouchHolder) { + inputHolder = new TouchInputHolder(this, &options); + } else { + #if defined(ANDROID) || defined(__APPLE__) + inputHolder = new CustomInputHolder( + new XperiaPlayInput(&options), + new ControllerTurnInput(2, ControllerTurnInput::MODE_DELTA), + new IBuildInput()); + #else + inputHolder = new CustomInputHolder( + new KeyboardInput(&options), + new MouseTurnInput(MouseTurnInput::MODE_DELTA, width/2, height/2), + new MouseBuildInput()); + #endif + } + + mouseHandler.setTurnInput(inputHolder->getTurnInput()); + if (level && player) { + player->input = inputHolder->getMoveInput(); + } +#endif +} + + +// +// Multiplayer +// +void Minecraft::locateMultiplayer() { + isLookingForMultiplayer = true; + + raknetInstance->pingForHosts(19132); + netCallback = new ClientSideNetworkHandler(this, raknetInstance); +} + +void Minecraft::cancelLocateMultiplayer() { + isLookingForMultiplayer = false; + + raknetInstance->stopPingForHosts(); + + delete netCallback; + netCallback = NULL; +} + +bool Minecraft::joinMultiplayer( const PingedCompatibleServer& server ) +{ + if (isLookingForMultiplayer && netCallback) { + isLookingForMultiplayer = false; + return raknetInstance->connect(server.address.ToString(false), server.address.GetPort()); + } + return false; +} + +bool Minecraft::joinMultiplayerFromString( const std::string& server ) +{ + std::string ip = ""; + std::string port = "19132"; + + size_t pos = server.find(":"); + + if (pos != std::string::npos) { + ip = server.substr(0, pos); + port = server.substr(pos + 1); + } else { + ip = server; + } + + printf("%s \n", port.c_str()); + + if (isLookingForMultiplayer && netCallback) { + isLookingForMultiplayer = false; + printf("test"); + int portNum = atoi(port.c_str()); + return raknetInstance->connect(ip.c_str(), portNum); + } + return false; +} + +void Minecraft::hostMultiplayer(int port) { + // Tear down last instance + raknetInstance->disconnect(); + delete netCallback; + netCallback = NULL; + +#if !defined(NO_NETWORK) + netCallback = new ServerSideNetworkHandler(this, raknetInstance); + #ifdef STANDALONE_SERVER + raknetInstance->host("Server", port, 16); + #else + raknetInstance->host(options.getStringValue(OPTIONS_USERNAME), port); + #endif +#endif +} + +// +// Level generation +// +/*static*/ + +void* Minecraft::prepareLevel_tspawn(void *p_param) +{ + Minecraft* mc = (Minecraft*) p_param; + mc->generateLevel("Currently not used", mc->level); + return 0; +} + +void Minecraft::generateLevel( const std::string& message, Level* level ) +{ + Stopwatch s; + s.start(); + prepareLevel(message); + s.stop(); + s.print("Level generated: "); + + // "Unlock" + isGeneratingLevel = false; +} + +void Minecraft::_levelGenerated() +{ +#ifndef STANDALONE_SERVER + if (player == NULL) { + player = (LocalPlayer*) gameMode->createPlayer(level); + gameMode->initPlayer(player); + } + + if (player) { + player->input = inputHolder->getMoveInput(); + } + + if (levelRenderer != NULL) levelRenderer->setLevel(level); + if (particleEngine != NULL) particleEngine->setLevel(level); + + gameMode->adjustPlayer(player); + gui.onLevelGenerated(); +#endif + + level->validateSpawn(); + level->loadPlayer(player, true); + // if we are client side, we trust the server to have given us a correct position + if (player && !level->isClientSide) { + player->resetPos(false); + } + + this->cameraTargetPlayer = player; + + std::string serverName = options.getStringValue(OPTIONS_USERNAME) + " - " + level->getLevelData()->levelName; + + if (raknetInstance->isServer()) + raknetInstance->announceServer(serverName); + + if (netCallback) { + netCallback->levelGenerated(level); + } + +#if defined(WIN32) || defined(RPI) + if (_commandServer) { + delete _commandServer; + } + _commandServer = new CommandServer(this); + _commandServer->init(commandPort); +#endif + + // Hack to (hopefully) get the players to show (note: in LevelListener + // instead, since adding yourself always generates a entityAdded) + //EntityRenderDispatcher::getInstance()->onGraphicsReset(); + _hasSignaledGeneratingLevelFinished = true; +} + +Player* Minecraft::respawnPlayer(int playerId) { + for (unsigned int i = 0; i < level->players.size(); ++i) { + if (level->players[i]->entityId == playerId) { + resetPlayer(level->players[i]); + return level->players[i]; + } + } + return NULL; +} + +void Minecraft::resetPlayer(Player* player) { + level->validateSpawn(); + player->reset(); + + Pos p; + if(player->hasRespawnPosition()) { + p = player->getRespawnPosition(); + } + else { + p = level->getSharedSpawnPos(); + } + player->setPos((float)p.x + 0.5f, (float)p.y + 1.0f, (float)p.z + 0.5f); + player->resetPos(true); + + if (isCreativeMode()) + player->inventory->clearInventoryWithDefault(); +} + +void Minecraft::respawnPlayer() { + // RESET THE FRACKING PLAYER HERE + //bool slowCheck = false; + //for (int i = 0; i < level->entities.size(); ++i) + // if (level->entities[i] == player) slowCheck = true; + // + //LOGI("Has entity? %d, %d\n", level->getEntity(player->entityId), slowCheck); + + resetPlayer(player); + + // tell server (or other client) that we re-spawned + RespawnPacket packet(player); + raknetInstance->send(packet); +} + +void Minecraft::onGraphicsReset() +{ +#ifndef STANDALONE_SERVER + textures->clear(); + + font->onGraphicsReset(); + gui.onGraphicsReset(); + + if (levelRenderer) levelRenderer->onGraphicsReset(); + if (gameRenderer) gameRenderer->onGraphicsReset(); + + EntityRenderDispatcher::getInstance()->onGraphicsReset(); + TileEntityRenderDispatcher::getInstance()->onGraphicsReset(); +#endif +} + +int Minecraft::getProgressStatusId() { + return progressStageStatusId; +} + +const char* Minecraft::getProgressMessage() +{ + return progressMessages[progressStageStatusId]; +} + +bool Minecraft::isLevelGenerated() +{ + return level != NULL && !isGeneratingLevel; +} + +LevelStorageSource* Minecraft::getLevelSource() +{ + return storageSource; +} + +// int Minecraft::getLicenseId() { +// if (!LicenseCodes::isReady(_licenseId)) +// _licenseId = platform()->checkLicense(); +// return _licenseId; +// } + +void Minecraft::audioEngineOn() { +#ifndef STANDALONE_SERVER + soundEngine->enable(true); +#endif +} +void Minecraft::audioEngineOff() { +#ifndef STANDALONE_SERVER + soundEngine->enable(false); +#endif +} + +void Minecraft::setIsCreativeMode(bool isCreative) +{ +#ifdef CREATORMODE + delete gameMode; + gameMode = new CreatorMode(this); + _isCreativeMode = true; +#else + if (!gameMode || isCreative != _isCreativeMode) + { + delete gameMode; + if (isCreative) gameMode = new CreativeMode(this); + else gameMode = new SurvivalMode(this); + _isCreativeMode = isCreative; + } +#endif + if (player) + gameMode->initAbilities(player->abilities); +} + +bool Minecraft::isCreativeMode() { + return _isCreativeMode; +} + +bool Minecraft::isKindleFire(int kindleVersion) { + if (kindleVersion != 1) + return false; + + std::string model = platform()->getPlatformStringVar(PlatformStringVars::DEVICE_BUILD_MODEL); + std::string modelLower(model); + std::transform(modelLower.begin(), modelLower.end(), modelLower.begin(), tolower); + + return (modelLower.find("kindle") != std::string::npos) && (modelLower.find("fire") != std::string::npos); +} + +bool Minecraft::transformResolution(int* w, int* h) +{ + bool changed = false; + + // Kindle Fire 1: reporting wrong height in + // certain cases (e.g. after screen lock) + if (isKindleFire(1) && *h >= 560 && *h <= 620) { + *h = 580; + changed = true; + } + + return changed; +} + +ICreator* Minecraft::getCreator() +{ +#ifdef CREATORMODE + return ((CreatorMode*)gameMode)->getCreator(); +#else + return NULL; +#endif +} + +void Minecraft::optionUpdated(OptionId option, bool value ) { + if(netCallback != NULL && option == OPTIONS_SERVER_VISIBLE) { + ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) netCallback; + ss->allowIncomingConnections(value); + } else if (option == OPTIONS_USE_TOUCHSCREEN) { + _reloadInput(); + } +} + +void Minecraft::optionUpdated(OptionId option, float value ) { +// #ifndef STANDALONE_SERVER +// if(option == OPTIONS_PIXELS_PER_MILLIMETER) { +// pixelCalcUi.setPixelsPerMillimeter(value * Gui::InvGuiScale); +// pixelCalc.setPixelsPerMillimeter(value); +// } +// #endif +} + +void Minecraft::optionUpdated(OptionId option, int value ) { + if(option == OPTIONS_GUI_SCALE) { + // reapply screen scaling using current window size + setSize(width, height); + } +} diff --git a/src/client/Minecraft.h b/src/client/Minecraft.h index 22b7ec7..1fafd46 100755 --- a/src/client/Minecraft.h +++ b/src/client/Minecraft.h @@ -1,230 +1,230 @@ -#ifndef NET_MINECRAFT_CLIENT__Minecraft_H__ -#define NET_MINECRAFT_CLIENT__Minecraft_H__ - -#include "Options.h" -#ifndef STANDALONE_SERVER -#include "MouseHandler.h" -#include "gui/Gui.h" -#include "gui/screens/ScreenChooser.h" -#endif - -#include "Timer.h" - -//#include "../network/RakNetInstance.h" -#include "../world/phys/HitResult.h" - -class Level; -class LocalPlayer; -class IInputHolder; -class Mob; -class Player; -class LevelRenderer; -class GameRenderer; -class ParticleEngine; -class Entity; -class ICreator; -class GameMode; -class Textures; -class CThread; -class SoundEngine; -class Screen; -class Font; -class LevelStorageSource; -class BuildActionIntention; -class PerfRenderer; -class LevelSettings; -class IRakNetInstance; -class NetEventCallback; -class CommandServer; -struct PingedCompatibleServer; -//class ExternalFileLevelStorageSource; - - -#include "../App.h" -#include "PixelCalc.h" -class AppPlatform; -class AppPlatform_android; - -class Minecraft: public App -{ -protected: - Minecraft(); -public: - virtual ~Minecraft(); - - void init(); - void setSize(int width, int height); - void reloadOptions(); - - bool supportNonTouchScreen(); - bool useTouchscreen(); - void grabMouse(); - void releaseMouse(); - - void handleBuildAction(BuildActionIntention*); - - void toggleDimension(){} - bool isCreativeMode(); - void setIsCreativeMode(bool isCreative); - void setScreen(Screen*); - - virtual void selectLevel(const std::string& levelId, const std::string& levelName, const LevelSettings& settings); - virtual void setLevel(Level* level, const std::string& message = "", LocalPlayer* forceInsertPlayer = NULL); - - void generateLevel( const std::string& message, Level* level ); - LevelStorageSource* getLevelSource(); - - bool isLookingForMultiplayer; - void locateMultiplayer(); - void cancelLocateMultiplayer(); - bool joinMultiplayer(const PingedCompatibleServer& server); - bool joinMultiplayerFromString(const std::string& server); - void hostMultiplayer(int port=19132); - Player* respawnPlayer(int playerId); - void respawnPlayer(); - void resetPlayer(Player* player); - void doActuallyRespawnPlayer(); - - void update(); - - void tick(int nTick, int maxTick); - void tickInput(); - - bool isOnlineClient(); - bool isOnline(); - void pauseGame(bool isBackPaused); - void gameLostFocus(); - - void prepareLevel(const std::string& message); - - void leaveGame(bool renameLevel = false); - - int getProgressStatusId(); - const char* getProgressMessage(); - - ICreator* getCreator(); - - // void onGraphicsLost() {} - void onGraphicsReset(); - - bool isLevelGenerated(); - - void handleMouseDown(int button, bool down); - - void audioEngineOn(); - void audioEngineOff(); - - bool isPowerVR() { return _powerVr; } - bool isKindleFire(int kindleVersion); - bool transformResolution(int* w, int* h); - void optionUpdated(OptionId option, bool value); - void optionUpdated(OptionId option, float value); - void optionUpdated(OptionId option, int value); -#ifdef __APPLE__ - bool _isSuperFast; - bool isSuperFast() { return _isSuperFast; } -#endif - -protected: - void _levelGenerated(); - -private: - static void* prepareLevel_tspawn(void *p_param); - - void _reloadInput(); -public: - int width; - int height; - - // Vars that the platform is allowed to use in the future - int commandPort; - int reserved_d1, reserved_d2; - float reserved_f1, reserved_f2; - - Options options; - - static bool useAmbientOcclusion; - //static bool threadInterrupt; - - volatile bool pause; - - LevelRenderer* levelRenderer; - GameRenderer* gameRenderer; - ParticleEngine* particleEngine; - SoundEngine* soundEngine; - - GameMode* gameMode; -#ifndef STANDALONE_SERVER - Textures* textures; - ScreenChooser screenChooser; - Font* font; -#endif - IRakNetInstance* raknetInstance; - NetEventCallback* netCallback; - - int lastTime; - int lastTickTime; - float ticksSinceLastUpdate; - - Level* level; - - LocalPlayer* player; - IInputHolder* inputHolder; - Mob* cameraTargetPlayer; -#ifndef STANDALONE_SERVER - Gui gui; -#endif - CThread* generateLevelThread; - Screen* screen; - static int customDebugId; - - static const int CDI_NONE = 0; - static const int CDI_GRAPHICS = 1; -#ifndef STANDALONE_SERVER - MouseHandler mouseHandler; -#endif - bool mouseGrabbed; - - PixelCalc pixelCalc; - PixelCalc pixelCalcUi; - - HitResult hitResult; - volatile int progressStagePercentage; - - // This field is initialized in main() - // It sets the base path to where worlds can be written (sdcard on android) - std::string externalStoragePath; - std::string externalCacheStoragePath; -protected: - Timer timer; - // @note @attn @warn: this is dangerous as fuck! - volatile bool isGeneratingLevel; - bool _hasSignaledGeneratingLevelFinished; - - LevelStorageSource* storageSource; - bool _running; - bool _powerVr; - -private: - volatile int progressStageStatusId; - static const char* progressMessages[]; - - int missTime; - int ticks; - bool screenMutex; - bool hasScheduledScreen; - Screen* scheduledScreen; - - int _licenseId; - bool _supportsNonTouchscreen; - - bool _isCreativeMode; - //int _respawnPlayerTicks; - Player* _pendingRemovePlayer; // @attn @todo @fix: remove this shait and fix the respawn behaviour - // shit* lmao - - PerfRenderer* _perfRenderer; - CommandServer* _commandServer; -}; - -#endif /*NET_MINECRAFT_CLIENT__Minecraft_H__*/ +#ifndef NET_MINECRAFT_CLIENT__Minecraft_H__ +#define NET_MINECRAFT_CLIENT__Minecraft_H__ + +#include "Options.h" +#ifndef STANDALONE_SERVER +#include "MouseHandler.h" +#include "gui/Gui.h" +#include "gui/screens/ScreenChooser.h" +#endif + +#include "Timer.h" + +//#include "../network/RakNetInstance.h" +#include "../world/phys/HitResult.h" + +class Level; +class LocalPlayer; +class IInputHolder; +class Mob; +class Player; +class LevelRenderer; +class GameRenderer; +class ParticleEngine; +class Entity; +class ICreator; +class GameMode; +class Textures; +class CThread; +class SoundEngine; +class Screen; +class Font; +class LevelStorageSource; +class BuildActionIntention; +class PerfRenderer; +class LevelSettings; +class IRakNetInstance; +class NetEventCallback; +class CommandServer; +struct PingedCompatibleServer; +//class ExternalFileLevelStorageSource; + + +#include "../App.h" +#include "PixelCalc.h" +class AppPlatform; +class AppPlatform_android; + +class Minecraft: public App +{ +protected: + Minecraft(); +public: + virtual ~Minecraft(); + + void init(); + void setSize(int width, int height); + void reloadOptions(); + + bool supportNonTouchScreen(); + bool useTouchscreen(); + void grabMouse(); + void releaseMouse(); + + void handleBuildAction(BuildActionIntention*); + + void toggleDimension(){} + bool isCreativeMode(); + void setIsCreativeMode(bool isCreative); + void setScreen(Screen*); + + virtual void selectLevel(const std::string& levelId, const std::string& levelName, const LevelSettings& settings); + virtual void setLevel(Level* level, const std::string& message = "", LocalPlayer* forceInsertPlayer = NULL); + + void generateLevel( const std::string& message, Level* level ); + LevelStorageSource* getLevelSource(); + + bool isLookingForMultiplayer; + void locateMultiplayer(); + void cancelLocateMultiplayer(); + bool joinMultiplayer(const PingedCompatibleServer& server); + bool joinMultiplayerFromString(const std::string& server); + void hostMultiplayer(int port=19132); + Player* respawnPlayer(int playerId); + void respawnPlayer(); + void resetPlayer(Player* player); + void doActuallyRespawnPlayer(); + + void update(); + + void tick(int nTick, int maxTick); + void tickInput(); + + bool isOnlineClient(); + bool isOnline(); + void pauseGame(bool isBackPaused); + void gameLostFocus(); + + void prepareLevel(const std::string& message); + + void leaveGame(bool renameLevel = false); + + int getProgressStatusId(); + const char* getProgressMessage(); + + ICreator* getCreator(); + + // void onGraphicsLost() {} + void onGraphicsReset(); + + bool isLevelGenerated(); + + void handleMouseDown(int button, bool down); + + void audioEngineOn(); + void audioEngineOff(); + + bool isPowerVR() { return _powerVr; } + bool isKindleFire(int kindleVersion); + bool transformResolution(int* w, int* h); + void optionUpdated(OptionId option, bool value); + void optionUpdated(OptionId option, float value); + void optionUpdated(OptionId option, int value); +#ifdef __APPLE__ + bool _isSuperFast; + bool isSuperFast() { return _isSuperFast; } +#endif + +protected: + void _levelGenerated(); + +private: + static void* prepareLevel_tspawn(void *p_param); + + void _reloadInput(); +public: + int width; + int height; + + // Vars that the platform is allowed to use in the future + int commandPort; + int reserved_d1, reserved_d2; + float reserved_f1, reserved_f2; + + Options options; + + static bool useAmbientOcclusion; + //static bool threadInterrupt; + + volatile bool pause; + + LevelRenderer* levelRenderer; + GameRenderer* gameRenderer; + ParticleEngine* particleEngine; + SoundEngine* soundEngine; + + GameMode* gameMode; +#ifndef STANDALONE_SERVER + Textures* textures; + ScreenChooser screenChooser; + Font* font; +#endif + IRakNetInstance* raknetInstance; + NetEventCallback* netCallback; + + int lastTime; + int lastTickTime; + float ticksSinceLastUpdate; + + Level* level; + + LocalPlayer* player; + IInputHolder* inputHolder; + Mob* cameraTargetPlayer; +#ifndef STANDALONE_SERVER + Gui gui; +#endif + CThread* generateLevelThread; + Screen* screen; + static int customDebugId; + + static const int CDI_NONE = 0; + static const int CDI_GRAPHICS = 1; +#ifndef STANDALONE_SERVER + MouseHandler mouseHandler; +#endif + bool mouseGrabbed; + + PixelCalc pixelCalc; + PixelCalc pixelCalcUi; + + HitResult hitResult; + volatile int progressStagePercentage; + + // This field is initialized in main() + // It sets the base path to where worlds can be written (sdcard on android) + std::string externalStoragePath; + std::string externalCacheStoragePath; +protected: + Timer timer; + // @note @attn @warn: this is dangerous as fuck! + volatile bool isGeneratingLevel; + bool _hasSignaledGeneratingLevelFinished; + + LevelStorageSource* storageSource; + bool _running; + bool _powerVr; + +private: + volatile int progressStageStatusId; + static const char* progressMessages[]; + + int missTime; + int ticks; + bool screenMutex; + bool hasScheduledScreen; + Screen* scheduledScreen; + + int _licenseId; + bool _supportsNonTouchscreen; + + bool _isCreativeMode; + //int _respawnPlayerTicks; + Player* _pendingRemovePlayer; // @attn @todo @fix: remove this shait and fix the respawn behaviour + // shit* lmao + + PerfRenderer* _perfRenderer; + CommandServer* _commandServer; +}; + +#endif /*NET_MINECRAFT_CLIENT__Minecraft_H__*/ diff --git a/src/client/MouseHandler.cpp b/src/client/MouseHandler.cpp index d77e28b..8129a4f 100755 --- a/src/client/MouseHandler.cpp +++ b/src/client/MouseHandler.cpp @@ -1,60 +1,60 @@ -#include "MouseHandler.h" -#include "player/input/ITurnInput.h" - -#ifdef RPI -#include -#endif - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) -#include -#endif - -MouseHandler::MouseHandler( ITurnInput* turnInput ) -: _turnInput(turnInput) -{} - -MouseHandler::MouseHandler() -: _turnInput(0) -{} - -MouseHandler::~MouseHandler() { -} - -void MouseHandler::setTurnInput( ITurnInput* turnInput ) { - _turnInput = turnInput; -} - -void MouseHandler::grab() { - xd = 0; - yd = 0; - -#if defined(RPI) - //LOGI("Grabbing input!\n"); - SDL_WM_GrabInput(SDL_GRAB_ON); - SDL_ShowCursor(0); -#endif - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSetInputMode(glfwGetCurrentContext(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); -#endif -} - -void MouseHandler::release() { -#if defined(RPI) - //LOGI("Releasing input!\n"); - SDL_WM_GrabInput(SDL_GRAB_OFF); - SDL_ShowCursor(1); -#endif - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSetInputMode(glfwGetCurrentContext(), GLFW_CURSOR, GLFW_CURSOR_NORMAL); -#endif -} - -void MouseHandler::poll() { - if (_turnInput != 0) { - TurnDelta td = _turnInput->getTurnDelta(); - xd = td.x; - yd = td.y; - } -} +#include "MouseHandler.h" +#include "player/input/ITurnInput.h" + +#ifdef RPI +#include +#endif + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) +#include +#endif + +MouseHandler::MouseHandler( ITurnInput* turnInput ) +: _turnInput(turnInput) +{} + +MouseHandler::MouseHandler() +: _turnInput(0) +{} + +MouseHandler::~MouseHandler() { +} + +void MouseHandler::setTurnInput( ITurnInput* turnInput ) { + _turnInput = turnInput; +} + +void MouseHandler::grab() { + xd = 0; + yd = 0; + +#if defined(RPI) + //LOGI("Grabbing input!\n"); + SDL_WM_GrabInput(SDL_GRAB_ON); + SDL_ShowCursor(0); +#endif + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + glfwSetInputMode(glfwGetCurrentContext(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); +#endif +} + +void MouseHandler::release() { +#if defined(RPI) + //LOGI("Releasing input!\n"); + SDL_WM_GrabInput(SDL_GRAB_OFF); + SDL_ShowCursor(1); +#endif + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + glfwSetInputMode(glfwGetCurrentContext(), GLFW_CURSOR, GLFW_CURSOR_NORMAL); +#endif +} + +void MouseHandler::poll() { + if (_turnInput != 0) { + TurnDelta td = _turnInput->getTurnDelta(); + xd = td.x; + yd = td.y; + } +} diff --git a/src/client/MouseHandler.h b/src/client/MouseHandler.h index 32ff60c..563459b 100755 --- a/src/client/MouseHandler.h +++ b/src/client/MouseHandler.h @@ -1,28 +1,28 @@ -#ifndef NET_MINECRAFT_CLIENT__MouseHandler_H__ -#define NET_MINECRAFT_CLIENT__MouseHandler_H__ - -//package net.minecraft.client; - -class ITurnInput; - -class MouseHandler -{ -public: - MouseHandler(ITurnInput* turnInput); - MouseHandler(); - ~MouseHandler(); - - void setTurnInput(ITurnInput* turnInput); - - void grab(); - void release(); - - void poll(); - - float xd, yd; -private: - int toSkip; - ITurnInput* _turnInput; -}; - -#endif /*NET_MINECRAFT_CLIENT__MouseHandler_H__*/ +#ifndef NET_MINECRAFT_CLIENT__MouseHandler_H__ +#define NET_MINECRAFT_CLIENT__MouseHandler_H__ + +//package net.minecraft.client; + +class ITurnInput; + +class MouseHandler +{ +public: + MouseHandler(ITurnInput* turnInput); + MouseHandler(); + ~MouseHandler(); + + void setTurnInput(ITurnInput* turnInput); + + void grab(); + void release(); + + void poll(); + + float xd, yd; +private: + int toSkip; + ITurnInput* _turnInput; +}; + +#endif /*NET_MINECRAFT_CLIENT__MouseHandler_H__*/ diff --git a/src/client/Options.cpp b/src/client/Options.cpp index 2cacca0..ad9967e 100755 --- a/src/client/Options.cpp +++ b/src/client/Options.cpp @@ -1,302 +1,302 @@ -#include "Options.h" -#include "OptionStrings.h" -#include "Minecraft.h" -#include "../platform/log.h" -#include "../world/Difficulty.h" -#include - -#include - -bool Options::debugGl = false; - -// OPTIONS TABLE - -OptionInt difficulty("difficulty", Difficulty::NORMAL, 0, Difficulty::COUNT); -OptionBool hidegui("hidegui", false); -OptionBool thirdPersonView("thirdperson", false); -OptionBool renderDebug("renderDebug", false); -OptionBool smoothCamera("smoothCamera", false); -OptionBool fixedCamera("fixedCamera", false); -OptionBool isFlying("isflying", false); -OptionBool barOnTop("barOnTop", false); -OptionBool allowSprint("allowSprint", true); -OptionBool rpiCursor("rpiCursor", false); -OptionBool autoJump("autoJump", true); - - -OptionFloat flySpeed("flySpeed", 1.f); -OptionFloat cameraSpeed("cameraSpeed", 1.f); - -OptionInt guiScale("guiScale", 0, 0, 5); - -OptionString skin("skin", "Default"); - -#ifdef RPI -OptionString username("username", "StevePi"); -#else -OptionString username("username", "Steve"); -#endif - -OptionBool destroyVibration("destroyVibration", true); -OptionBool isLeftHanded("isLeftHanded", false); -OptionBool isJoyTouchArea("isJoyTouchArea", false); - -OptionFloat musicVolume("music", 1.f, MUSIC_MIN_VALUE, MUSIC_MAX_VALUE); -OptionFloat soundVolume("sound", 1.f, SOUND_MIN_VALUE, SOUND_MAX_VALUE); - -OptionFloat sensitivityOpt("sensitivity", 0.5f, SENSITIVITY_MIN_VALUE, SENSITIVITY_MAX_VALUE); - -OptionBool invertYMouse("invertMouse", false); -OptionInt viewDistance("renderDistance", 2, 0, 4); - -OptionBool anaglyph3d("anaglyph3d", false); -OptionBool limitFramerate("limitFramerate", false); -OptionBool vsync("vsync", true); -OptionBool fancyGraphics("fancyGraphics", true); -OptionBool viewBobbing("viewBobbing", true); -OptionBool ambientOcclusion("ao", false); - -OptionBool useTouchscreen("useTouchscreen", true); - -OptionBool serverVisible("servervisible", true); - -OptionInt keyForward("key.forward", Keyboard::KEY_W); -OptionInt keyLeft("key.left", Keyboard::KEY_A); -OptionInt keyBack("key.back", Keyboard::KEY_S); -OptionInt keyRight("key.right", Keyboard::KEY_D); -OptionInt keyJump("key.jump", Keyboard::KEY_SPACE); -OptionInt keyInventory("key.inventory", Keyboard::KEY_E); -OptionInt keySneak("key.sneak", Keyboard::KEY_LSHIFT); -OptionInt keyDrop("key.drop", Keyboard::KEY_Q); -OptionInt keyChat("key.chat", Keyboard::KEY_T); -OptionInt keyFog("key.fog", Keyboard::KEY_F); -OptionInt keyUse("key.use", Keyboard::KEY_U); - -// TODO: make human readable keycodes here -OptionInt keyMenuNext("key.menu.next", 40); -OptionInt keyMenuPrev("key.menu.previous", 38); -OptionInt keyMenuOk("key.menu.ok", 13); -OptionInt keyMenuCancel("key.menu.cancel", 8); - -OptionBool firstLaunch("firstLaunch", true); - -OptionString lastIp("lastip"); - -void Options::initTable() { - m_options[OPTIONS_DIFFICULTY] = &difficulty; - m_options[OPTIONS_HIDEGUI] = &hidegui; - m_options[OPTIONS_THIRD_PERSON_VIEW] = &thirdPersonView; - m_options[OPTIONS_RENDER_DEBUG] = &renderDebug; - m_options[OPTIONS_SMOOTH_CAMERA] = &smoothCamera; - m_options[OPTIONS_FIXED_CAMERA] = &fixedCamera; - m_options[OPTIONS_IS_FLYING] = &isFlying; - - m_options[OPTIONS_FLY_SPEED] = &flySpeed; - m_options[OPTIONS_CAMERA_SPEED] = &cameraSpeed; - - m_options[OPTIONS_GUI_SCALE] = &guiScale; - - m_options[OPTIONS_DESTROY_VIBRATION] = &destroyVibration; - - m_options[OPTIONS_IS_LEFT_HANDED] = &isLeftHanded; - m_options[OPTIONS_IS_JOY_TOUCH_AREA] = &isJoyTouchArea; - - m_options[OPTIONS_MUSIC_VOLUME] = &musicVolume; - m_options[OPTIONS_SOUND_VOLUME] = &soundVolume; - - #if defined(PLATFORM_DESKTOP) || defined(RPI) - float sensitivity = sensitivityOpt.get(); - sensitivity *= 0.4f; - sensitivityOpt.set(sensitivity); - #endif - - - m_options[OPTIONS_GUI_SCALE] = &guiScale; - - m_options[OPTIONS_SKIN] = &skin; - m_options[OPTIONS_USERNAME] = &username; - - m_options[OPTIONS_DESTROY_VIBRATION] = &destroyVibration; - m_options[OPTIONS_IS_LEFT_HANDED] = &isLeftHanded; - - m_options[OPTIONS_MUSIC_VOLUME] = &musicVolume; - m_options[OPTIONS_SOUND_VOLUME] = &soundVolume; - - m_options[OPTIONS_SENSITIVITY] = &sensitivityOpt; - - m_options[OPTIONS_INVERT_Y_MOUSE] = &invertYMouse; - m_options[OPTIONS_VIEW_DISTANCE] = &viewDistance; - - m_options[OPTIONS_ANAGLYPH_3D] = &anaglyph3d; - m_options[OPTIONS_LIMIT_FRAMERATE] = &limitFramerate; - m_options[OPTIONS_VSYNC] = &vsync; - m_options[OPTIONS_FANCY_GRAPHICS] = &fancyGraphics; - m_options[OPTIONS_VIEW_BOBBING] = &viewBobbing; - m_options[OPTIONS_AMBIENT_OCCLUSION] = &ambientOcclusion; - - m_options[OPTIONS_USE_TOUCHSCREEN] = &useTouchscreen; - - m_options[OPTIONS_SERVER_VISIBLE] = &serverVisible; - - m_options[OPTIONS_KEY_FORWARD] = &keyForward; - m_options[OPTIONS_KEY_LEFT] = &keyLeft; - m_options[OPTIONS_KEY_BACK] = &keyBack; - m_options[OPTIONS_KEY_RIGHT] = &keyRight; - m_options[OPTIONS_KEY_JUMP] = &keyJump; - m_options[OPTIONS_KEY_INVENTORY] = &keyInventory; - m_options[OPTIONS_KEY_SNEAK] = &keySneak; - m_options[OPTIONS_KEY_DROP] = &keyDrop; - m_options[OPTIONS_KEY_CHAT] = &keyChat; - m_options[OPTIONS_KEY_FOG] = &keyFog; - m_options[OPTIONS_KEY_USE] = &keyUse; - - m_options[OPTIONS_KEY_MENU_NEXT] = &keyMenuNext; - m_options[OPTIONS_KEY_MENU_PREV] = &keyMenuPrev; - m_options[OPTIONS_KEY_MENU_OK] = &keyMenuOk; - m_options[OPTIONS_KEY_MENU_CANCEL] = &keyMenuCancel; - - m_options[OPTIONS_FIRST_LAUNCH] = &firstLaunch; - - m_options[OPTIONS_BAR_ON_TOP] = &barOnTop; - m_options[OPTIONS_ALLOW_SPRINT] = &allowSprint; - m_options[OPTIONS_RPI_CURSOR] = &rpiCursor; - - m_options[OPTIONS_AUTOJUMP] = &autoJump; - m_options[OPTIONS_LAST_IP] = &lastIp; -} - -void Options::set(OptionId key, const std::string& value) { - auto option = opt(key); - - if (option) { - option->set(value); - notifyOptionUpdate(key, value); - } -} - -void Options::set(OptionId key, float value) { - auto option = opt(key); - - if (option) { - option->set(value); - notifyOptionUpdate(key, value); - } -} - -void Options::set(OptionId key, int value) { - auto option = opt(key); - - if (option) { - option->set(value); - notifyOptionUpdate(key, value); - } -} - -void Options::toggle(OptionId key) { - auto option = opt(key); - - if (option) { - option->toggle(); - notifyOptionUpdate(key, option->get()); - } -} - -void Options::load() { - StringVector optionStrings = optionsFile.getOptionStrings(); - - for (auto i = 0; i < optionStrings.size(); i += 2) { - const std::string& key = optionStrings[i]; - const std::string& value = optionStrings[i+1]; - - // FIXME: woah this is so slow - auto opt = std::find_if(m_options.begin(), m_options.end(), [&](auto& it) { - return it != nullptr && it->getStringId() == key; - }); - - if (opt == m_options.end()) continue; - - (*opt)->parse(value); -/* - // //LOGI("reading key: %s (%s)\n", key.c_str(), value.c_str()); - - // // Multiplayer - // // if (key == OptionStrings::Multiplayer_Username) username = value; - // if (key == OptionStrings::Multiplayer_ServerVisible) { - // m_options[OPTIONS_SERVER_VISIBLE] = readBool(value); - // } - - // // Controls - // if (key == OptionStrings::Controls_Sensitivity) { - // float sens = readFloat(value); - - // // sens is in range [0,1] with default/center at 0.5 (for aesthetics) - // // We wanna map it to something like [0.3, 0.9] BUT keep 0.5 @ ~0.5... - // m_options[OPTIONS_SENSITIVITY] = 0.3f + std::pow(1.1f * sens, 1.3f) * 0.42f; - // } - - // if (key == OptionStrings::Controls_InvertMouse) { - // m_options[OPTIONS_INVERT_Y_MOUSE] = readBool(value); - // } - - // if (key == OptionStrings::Controls_IsLefthanded) { - // m_options[OPTIONS_IS_LEFT_HANDED] = readBool(value); - // } - - // if (key == OptionStrings::Controls_UseTouchJoypad) { - // m_options[OPTIONS_IS_JOY_TOUCH_AREA] = readBool(value) && minecraft->useTouchscreen(); - // } - - // // Feedback - // if (key == OptionStrings::Controls_FeedbackVibration) { - // m_options[OPTIONS_DESTROY_VIBRATION] = readBool(value); - // } - - // // Graphics - // if (key == OptionStrings::Graphics_Fancy) { - // m_options[OPTIONS_FANCY_GRAPHICS] = readBool(value); - // } - - // // Graphics extras - // if (key == OptionStrings::Graphics_Vsync) { - // m_options[OPTIONS_VSYNC] = readBool(value); - // } - - // if (key == OptionStrings::Graphics_GUIScale) { - // m_options[OPTIONS_GUI_SCALE] = readInt(value) % 5; - // } - - // // Game - // if (key == OptionStrings::Game_DifficultyLevel) { - // readInt(value, difficulty); - // // Only support peaceful and normal right now - // if (difficulty != Difficulty::PEACEFUL && difficulty != Difficulty::NORMAL) - // difficulty = Difficulty::NORMAL; - // }*/ - } -} - -void Options::save() { - StringVector stringVec; - - for (auto& it : m_options) { - if (it) stringVec.push_back(it->serialize()); - } - - optionsFile.save(stringVec); -} - -void Options::setOptionsFilePath(const std::string& path) { - optionsFile.setOptionsPath(path + "/options.txt"); -} - -void Options::notifyOptionUpdate(OptionId key, bool value) { - minecraft->optionUpdated(key, value); -} - -void Options::notifyOptionUpdate(OptionId key, float value) { - minecraft->optionUpdated(key, value); -} - -void Options::notifyOptionUpdate(OptionId key, int value) { - minecraft->optionUpdated(key, value); -} +#include "Options.h" +#include "OptionStrings.h" +#include "Minecraft.h" +#include "../platform/log.h" +#include "../world/Difficulty.h" +#include + +#include + +bool Options::debugGl = false; + +// OPTIONS TABLE + +OptionInt difficulty("difficulty", Difficulty::NORMAL, 0, Difficulty::COUNT); +OptionBool hidegui("hidegui", false); +OptionBool thirdPersonView("thirdperson", false); +OptionBool renderDebug("renderDebug", false); +OptionBool smoothCamera("smoothCamera", false); +OptionBool fixedCamera("fixedCamera", false); +OptionBool isFlying("isflying", false); +OptionBool barOnTop("barOnTop", false); +OptionBool allowSprint("allowSprint", true); +OptionBool rpiCursor("rpiCursor", false); +OptionBool autoJump("autoJump", true); + + +OptionFloat flySpeed("flySpeed", 1.f); +OptionFloat cameraSpeed("cameraSpeed", 1.f); + +OptionInt guiScale("guiScale", 0, 0, 5); + +OptionString skin("skin", "Default"); + +#ifdef RPI +OptionString username("username", "StevePi"); +#else +OptionString username("username", "Steve"); +#endif + +OptionBool destroyVibration("destroyVibration", true); +OptionBool isLeftHanded("isLeftHanded", false); +OptionBool isJoyTouchArea("isJoyTouchArea", false); + +OptionFloat musicVolume("music", 1.f, MUSIC_MIN_VALUE, MUSIC_MAX_VALUE); +OptionFloat soundVolume("sound", 1.f, SOUND_MIN_VALUE, SOUND_MAX_VALUE); + +OptionFloat sensitivityOpt("sensitivity", 0.5f, SENSITIVITY_MIN_VALUE, SENSITIVITY_MAX_VALUE); + +OptionBool invertYMouse("invertMouse", false); +OptionInt viewDistance("renderDistance", 2, 0, 4); + +OptionBool anaglyph3d("anaglyph3d", false); +OptionBool limitFramerate("limitFramerate", false); +OptionBool vsync("vsync", true); +OptionBool fancyGraphics("fancyGraphics", true); +OptionBool viewBobbing("viewBobbing", true); +OptionBool ambientOcclusion("ao", false); + +OptionBool useTouchscreen("useTouchscreen", true); + +OptionBool serverVisible("servervisible", true); + +OptionInt keyForward("key.forward", Keyboard::KEY_W); +OptionInt keyLeft("key.left", Keyboard::KEY_A); +OptionInt keyBack("key.back", Keyboard::KEY_S); +OptionInt keyRight("key.right", Keyboard::KEY_D); +OptionInt keyJump("key.jump", Keyboard::KEY_SPACE); +OptionInt keyInventory("key.inventory", Keyboard::KEY_E); +OptionInt keySneak("key.sneak", Keyboard::KEY_LSHIFT); +OptionInt keyDrop("key.drop", Keyboard::KEY_Q); +OptionInt keyChat("key.chat", Keyboard::KEY_T); +OptionInt keyFog("key.fog", Keyboard::KEY_F); +OptionInt keyUse("key.use", Keyboard::KEY_U); + +// TODO: make human readable keycodes here +OptionInt keyMenuNext("key.menu.next", 40); +OptionInt keyMenuPrev("key.menu.previous", 38); +OptionInt keyMenuOk("key.menu.ok", 13); +OptionInt keyMenuCancel("key.menu.cancel", 8); + +OptionBool firstLaunch("firstLaunch", true); + +OptionString lastIp("lastip"); + +void Options::initTable() { + m_options[OPTIONS_DIFFICULTY] = &difficulty; + m_options[OPTIONS_HIDEGUI] = &hidegui; + m_options[OPTIONS_THIRD_PERSON_VIEW] = &thirdPersonView; + m_options[OPTIONS_RENDER_DEBUG] = &renderDebug; + m_options[OPTIONS_SMOOTH_CAMERA] = &smoothCamera; + m_options[OPTIONS_FIXED_CAMERA] = &fixedCamera; + m_options[OPTIONS_IS_FLYING] = &isFlying; + + m_options[OPTIONS_FLY_SPEED] = &flySpeed; + m_options[OPTIONS_CAMERA_SPEED] = &cameraSpeed; + + m_options[OPTIONS_GUI_SCALE] = &guiScale; + + m_options[OPTIONS_DESTROY_VIBRATION] = &destroyVibration; + + m_options[OPTIONS_IS_LEFT_HANDED] = &isLeftHanded; + m_options[OPTIONS_IS_JOY_TOUCH_AREA] = &isJoyTouchArea; + + m_options[OPTIONS_MUSIC_VOLUME] = &musicVolume; + m_options[OPTIONS_SOUND_VOLUME] = &soundVolume; + + #if defined(PLATFORM_DESKTOP) || defined(RPI) + float sensitivity = sensitivityOpt.get(); + sensitivity *= 0.4f; + sensitivityOpt.set(sensitivity); + #endif + + + m_options[OPTIONS_GUI_SCALE] = &guiScale; + + m_options[OPTIONS_SKIN] = &skin; + m_options[OPTIONS_USERNAME] = &username; + + m_options[OPTIONS_DESTROY_VIBRATION] = &destroyVibration; + m_options[OPTIONS_IS_LEFT_HANDED] = &isLeftHanded; + + m_options[OPTIONS_MUSIC_VOLUME] = &musicVolume; + m_options[OPTIONS_SOUND_VOLUME] = &soundVolume; + + m_options[OPTIONS_SENSITIVITY] = &sensitivityOpt; + + m_options[OPTIONS_INVERT_Y_MOUSE] = &invertYMouse; + m_options[OPTIONS_VIEW_DISTANCE] = &viewDistance; + + m_options[OPTIONS_ANAGLYPH_3D] = &anaglyph3d; + m_options[OPTIONS_LIMIT_FRAMERATE] = &limitFramerate; + m_options[OPTIONS_VSYNC] = &vsync; + m_options[OPTIONS_FANCY_GRAPHICS] = &fancyGraphics; + m_options[OPTIONS_VIEW_BOBBING] = &viewBobbing; + m_options[OPTIONS_AMBIENT_OCCLUSION] = &ambientOcclusion; + + m_options[OPTIONS_USE_TOUCHSCREEN] = &useTouchscreen; + + m_options[OPTIONS_SERVER_VISIBLE] = &serverVisible; + + m_options[OPTIONS_KEY_FORWARD] = &keyForward; + m_options[OPTIONS_KEY_LEFT] = &keyLeft; + m_options[OPTIONS_KEY_BACK] = &keyBack; + m_options[OPTIONS_KEY_RIGHT] = &keyRight; + m_options[OPTIONS_KEY_JUMP] = &keyJump; + m_options[OPTIONS_KEY_INVENTORY] = &keyInventory; + m_options[OPTIONS_KEY_SNEAK] = &keySneak; + m_options[OPTIONS_KEY_DROP] = &keyDrop; + m_options[OPTIONS_KEY_CHAT] = &keyChat; + m_options[OPTIONS_KEY_FOG] = &keyFog; + m_options[OPTIONS_KEY_USE] = &keyUse; + + m_options[OPTIONS_KEY_MENU_NEXT] = &keyMenuNext; + m_options[OPTIONS_KEY_MENU_PREV] = &keyMenuPrev; + m_options[OPTIONS_KEY_MENU_OK] = &keyMenuOk; + m_options[OPTIONS_KEY_MENU_CANCEL] = &keyMenuCancel; + + m_options[OPTIONS_FIRST_LAUNCH] = &firstLaunch; + + m_options[OPTIONS_BAR_ON_TOP] = &barOnTop; + m_options[OPTIONS_ALLOW_SPRINT] = &allowSprint; + m_options[OPTIONS_RPI_CURSOR] = &rpiCursor; + + m_options[OPTIONS_AUTOJUMP] = &autoJump; + m_options[OPTIONS_LAST_IP] = &lastIp; +} + +void Options::set(OptionId key, const std::string& value) { + auto option = opt(key); + + if (option) { + option->set(value); + notifyOptionUpdate(key, value); + } +} + +void Options::set(OptionId key, float value) { + auto option = opt(key); + + if (option) { + option->set(value); + notifyOptionUpdate(key, value); + } +} + +void Options::set(OptionId key, int value) { + auto option = opt(key); + + if (option) { + option->set(value); + notifyOptionUpdate(key, value); + } +} + +void Options::toggle(OptionId key) { + auto option = opt(key); + + if (option) { + option->toggle(); + notifyOptionUpdate(key, option->get()); + } +} + +void Options::load() { + StringVector optionStrings = optionsFile.getOptionStrings(); + + for (auto i = 0; i < optionStrings.size(); i += 2) { + const std::string& key = optionStrings[i]; + const std::string& value = optionStrings[i+1]; + + // FIXME: woah this is so slow + auto opt = std::find_if(m_options.begin(), m_options.end(), [&](auto& it) { + return it != nullptr && it->getStringId() == key; + }); + + if (opt == m_options.end()) continue; + + (*opt)->parse(value); +/* + // //LOGI("reading key: %s (%s)\n", key.c_str(), value.c_str()); + + // // Multiplayer + // // if (key == OptionStrings::Multiplayer_Username) username = value; + // if (key == OptionStrings::Multiplayer_ServerVisible) { + // m_options[OPTIONS_SERVER_VISIBLE] = readBool(value); + // } + + // // Controls + // if (key == OptionStrings::Controls_Sensitivity) { + // float sens = readFloat(value); + + // // sens is in range [0,1] with default/center at 0.5 (for aesthetics) + // // We wanna map it to something like [0.3, 0.9] BUT keep 0.5 @ ~0.5... + // m_options[OPTIONS_SENSITIVITY] = 0.3f + std::pow(1.1f * sens, 1.3f) * 0.42f; + // } + + // if (key == OptionStrings::Controls_InvertMouse) { + // m_options[OPTIONS_INVERT_Y_MOUSE] = readBool(value); + // } + + // if (key == OptionStrings::Controls_IsLefthanded) { + // m_options[OPTIONS_IS_LEFT_HANDED] = readBool(value); + // } + + // if (key == OptionStrings::Controls_UseTouchJoypad) { + // m_options[OPTIONS_IS_JOY_TOUCH_AREA] = readBool(value) && minecraft->useTouchscreen(); + // } + + // // Feedback + // if (key == OptionStrings::Controls_FeedbackVibration) { + // m_options[OPTIONS_DESTROY_VIBRATION] = readBool(value); + // } + + // // Graphics + // if (key == OptionStrings::Graphics_Fancy) { + // m_options[OPTIONS_FANCY_GRAPHICS] = readBool(value); + // } + + // // Graphics extras + // if (key == OptionStrings::Graphics_Vsync) { + // m_options[OPTIONS_VSYNC] = readBool(value); + // } + + // if (key == OptionStrings::Graphics_GUIScale) { + // m_options[OPTIONS_GUI_SCALE] = readInt(value) % 5; + // } + + // // Game + // if (key == OptionStrings::Game_DifficultyLevel) { + // readInt(value, difficulty); + // // Only support peaceful and normal right now + // if (difficulty != Difficulty::PEACEFUL && difficulty != Difficulty::NORMAL) + // difficulty = Difficulty::NORMAL; + // }*/ + } +} + +void Options::save() { + StringVector stringVec; + + for (auto& it : m_options) { + if (it) stringVec.push_back(it->serialize()); + } + + optionsFile.save(stringVec); +} + +void Options::setOptionsFilePath(const std::string& path) { + optionsFile.setOptionsPath(path + "/options.txt"); +} + +void Options::notifyOptionUpdate(OptionId key, bool value) { + minecraft->optionUpdated(key, value); +} + +void Options::notifyOptionUpdate(OptionId key, float value) { + minecraft->optionUpdated(key, value); +} + +void Options::notifyOptionUpdate(OptionId key, int value) { + minecraft->optionUpdated(key, value); +} diff --git a/src/client/Options.h b/src/client/Options.h index 8671b1a..dc1dfd1 100755 --- a/src/client/Options.h +++ b/src/client/Options.h @@ -1,170 +1,171 @@ -#ifndef NET_MINECRAFT_CLIENT__Options_H__ -#define NET_MINECRAFT_CLIENT__Options_H__ - -#define SOUND_MIN_VALUE 0.0f -#define SOUND_MAX_VALUE 1.0f -#define MUSIC_MIN_VALUE 0.0f -#define MUSIC_MAX_VALUE 1.0f -#define SENSITIVITY_MIN_VALUE 0.0f -#define SENSITIVITY_MAX_VALUE 1.0f -#define PIXELS_PER_MILLIMETER_MIN_VALUE 3.0f -#define PIXELS_PER_MILLIMETER_MAX_VALUE 4.0f - -//package net.minecraft.client; - -//#include "locale/Language.h" - -#include -#include -#include "../platform/input/Keyboard.h" -#include "../util/StringUtils.h" -#include "OptionsFile.h" -#include "Option.h" -#include - -enum OptionId { - // General - OPTIONS_DIFFICULTY, - OPTIONS_HIDEGUI, - OPTIONS_THIRD_PERSON_VIEW, - OPTIONS_GUI_SCALE, - OPTIONS_DESTROY_VIBRATION, - OPTIONS_MUSIC_VOLUME, - OPTIONS_SOUND_VOLUME, - OPTIONS_SKIN, - OPTIONS_USERNAME, - OPTIONS_SERVER_VISIBLE, - OPTIONS_BAR_ON_TOP, - OPTIONS_ALLOW_SPRINT, - OPTIONS_AUTOJUMP, - - // Graphics - OPTIONS_RENDER_DEBUG, - OPTIONS_SMOOTH_CAMERA, - OPTIONS_FIXED_CAMERA, - OPTIONS_VIEW_DISTANCE, - OPTIONS_VIEW_BOBBING, - OPTIONS_AMBIENT_OCCLUSION, - OPTIONS_ANAGLYPH_3D, - OPTIONS_LIMIT_FRAMERATE, - OPTIONS_VSYNC, - OPTIONS_FANCY_GRAPHICS, - - // Cheats / debug - OPTIONS_FLY_SPEED, - OPTIONS_CAMERA_SPEED, - OPTIONS_IS_FLYING, - - // Control - OPTIONS_USE_MOUSE_FOR_DIGGING, - OPTIONS_IS_LEFT_HANDED, - OPTIONS_IS_JOY_TOUCH_AREA, - OPTIONS_SENSITIVITY, - OPTIONS_INVERT_Y_MOUSE, - OPTIONS_USE_TOUCHSCREEN, - - OPTIONS_KEY_FORWARD, - OPTIONS_KEY_LEFT, - OPTIONS_KEY_BACK, - OPTIONS_KEY_RIGHT, - OPTIONS_KEY_JUMP, - OPTIONS_KEY_INVENTORY, - OPTIONS_KEY_SNEAK, - OPTIONS_KEY_DROP, - OPTIONS_KEY_CHAT, - OPTIONS_KEY_FOG, - OPTIONS_KEY_USE, - - OPTIONS_KEY_MENU_NEXT, - OPTIONS_KEY_MENU_PREV, - OPTIONS_KEY_MENU_OK, - OPTIONS_KEY_MENU_CANCEL, - - OPTIONS_FIRST_LAUNCH, - OPTIONS_LAST_IP, - - OPTIONS_RPI_CURSOR, - // Should be last! - OPTIONS_COUNT -}; - -class Minecraft; -typedef std::vector StringVector; - -class Options -{ -public: - - static bool debugGl; - - Options(Minecraft* minecraft, const std::string& workingDirectory = "") - : minecraft(minecraft) { - // elements werent initialized so i was getting a garbage pointer and a crash - m_options.fill(nullptr); - initTable(); - // load() is deferred to init() where path is configured correctly - } - - void initTable(); - - int getIntValue(OptionId key) { - auto option = opt(key); - return (option)? option->get() : 0; - } - - std::string getStringValue(OptionId key) { - auto option = opt(key); - return (option)? option->get() : ""; - } - - float getProgressValue(OptionId key) { - auto option = opt(key); - return (option)? option->get() : 0.f; - } - - bool getBooleanValue(OptionId key) { - auto option = opt(key); - return (option)? option->get() : false; - } - - float getProgrssMin(OptionId key) { - auto option = opt(key); - return (option)? option->getMin() : 0.f; - } - - float getProgrssMax(OptionId key) { - auto option = opt(key); - return (option)? option->getMax() : 0.f; - } - - Option* getOpt(OptionId id) { return m_options[id]; } - - void load(); - void save(); - void set(OptionId key, int value); - void set(OptionId key, float value); - void set(OptionId key, const std::string& value); - void setOptionsFilePath(const std::string& path); - void toggle(OptionId key); - - void notifyOptionUpdate(OptionId key, bool value); - void notifyOptionUpdate(OptionId key, float value); - void notifyOptionUpdate(OptionId key, int value); - void notifyOptionUpdate(OptionId key, const std::string& value) {} - -private: - template - T* opt(OptionId key) { - if (m_options[key] == nullptr) return nullptr; - return dynamic_cast(m_options[key]); - } -// apple stuff - float sound; - float music; - std::array m_options; - OptionsFile optionsFile; - - Minecraft* minecraft; -}; - -#endif /*NET_MINECRAFT_CLIENT__Options_H__*/ +#ifndef NET_MINECRAFT_CLIENT__Options_H__ +#define NET_MINECRAFT_CLIENT__Options_H__ + +#define SOUND_MIN_VALUE 0.0f +#define SOUND_MAX_VALUE 1.0f +#define MUSIC_MIN_VALUE 0.0f +#define MUSIC_MAX_VALUE 1.0f +#define SENSITIVITY_MIN_VALUE 0.0f +#define SENSITIVITY_MAX_VALUE 1.0f +#define PIXELS_PER_MILLIMETER_MIN_VALUE 3.0f +#define PIXELS_PER_MILLIMETER_MAX_VALUE 4.0f + +//package net.minecraft.client; + +//#include "locale/Language.h" + +#include +#include +#include "../platform/input/Keyboard.h" +#include "../util/StringUtils.h" +#include "OptionsFile.h" +#include "Option.h" +#include + +enum OptionId { + // General + OPTIONS_DIFFICULTY, + OPTIONS_HIDEGUI, + OPTIONS_THIRD_PERSON_VIEW, + OPTIONS_GUI_SCALE, + OPTIONS_DESTROY_VIBRATION, + OPTIONS_MUSIC_VOLUME, + OPTIONS_SOUND_VOLUME, + OPTIONS_SKIN, + OPTIONS_USERNAME, + OPTIONS_SERVER_VISIBLE, + OPTIONS_BAR_ON_TOP, + OPTIONS_ALLOW_SPRINT, + OPTIONS_AUTOJUMP, + + // Graphics + OPTIONS_RENDER_DEBUG, + OPTIONS_SMOOTH_CAMERA, + OPTIONS_FIXED_CAMERA, + OPTIONS_VIEW_DISTANCE, + OPTIONS_VIEW_BOBBING, + OPTIONS_AMBIENT_OCCLUSION, + OPTIONS_ANAGLYPH_3D, + OPTIONS_LIMIT_FRAMERATE, + OPTIONS_VSYNC, + OPTIONS_FANCY_GRAPHICS, + + // Cheats / debug + OPTIONS_FLY_SPEED, + OPTIONS_CAMERA_SPEED, + OPTIONS_IS_FLYING, + + // Control + OPTIONS_USE_MOUSE_FOR_DIGGING, + OPTIONS_IS_LEFT_HANDED, + OPTIONS_IS_JOY_TOUCH_AREA, + OPTIONS_SENSITIVITY, + OPTIONS_INVERT_Y_MOUSE, + OPTIONS_USE_TOUCHSCREEN, + + OPTIONS_KEY_FORWARD, + OPTIONS_KEY_LEFT, + OPTIONS_KEY_BACK, + OPTIONS_KEY_RIGHT, + OPTIONS_KEY_JUMP, + OPTIONS_KEY_INVENTORY, + OPTIONS_KEY_SNEAK, + OPTIONS_KEY_DROP, + OPTIONS_KEY_CHAT, + OPTIONS_KEY_FOG, + OPTIONS_KEY_USE, + + OPTIONS_KEY_MENU_NEXT, + OPTIONS_KEY_MENU_PREV, + OPTIONS_KEY_MENU_OK, + OPTIONS_KEY_MENU_CANCEL, + + OPTIONS_FIRST_LAUNCH, + OPTIONS_LAST_IP, + + OPTIONS_RPI_CURSOR, + // Should be last! + OPTIONS_COUNT +}; + +class Minecraft; +typedef std::vector StringVector; + +class Options +{ +public: + // deepfriedwaffles: for iOS, was getting compile errors saying: No member named 'sound' in 'Options' and No member named 'music' in 'Options' so I floated them here. 1.0f means full volume out of the box, but if everything is too loud, you might want to try adjusting this + float sound = 1.0f; + float music = 1.0f; + + static bool debugGl; + + Options(Minecraft* minecraft, const std::string& workingDirectory = "") + : minecraft(minecraft) { + // elements werent initialized so i was getting a garbage pointer and a crash + m_options.fill(nullptr); + initTable(); + // load() is deferred to init() where path is configured correctly + } + + void initTable(); + + int getIntValue(OptionId key) { + auto option = opt(key); + return (option)? option->get() : 0; + } + + std::string getStringValue(OptionId key) { + auto option = opt(key); + return (option)? option->get() : ""; + } + + float getProgressValue(OptionId key) { + auto option = opt(key); + return (option)? option->get() : 0.f; + } + + bool getBooleanValue(OptionId key) { + auto option = opt(key); + return (option)? option->get() : false; + } + + float getProgrssMin(OptionId key) { + auto option = opt(key); + return (option)? option->getMin() : 0.f; + } + + float getProgrssMax(OptionId key) { + auto option = opt(key); + return (option)? option->getMax() : 0.f; + } + + Option* getOpt(OptionId id) { return m_options[id]; } + + void load(); + void save(); + void set(OptionId key, int value); + void set(OptionId key, float value); + void set(OptionId key, const std::string& value); + void setOptionsFilePath(const std::string& path); + void toggle(OptionId key); + + void notifyOptionUpdate(OptionId key, bool value); + void notifyOptionUpdate(OptionId key, float value); + void notifyOptionUpdate(OptionId key, int value); + void notifyOptionUpdate(OptionId key, const std::string& value) {} + +private: + template + T* opt(OptionId key) { + if (m_options[key] == nullptr) return nullptr; + return dynamic_cast(m_options[key]); + } + + std::array m_options; + OptionsFile optionsFile; + + Minecraft* minecraft; +}; + +#endif /*NET_MINECRAFT_CLIENT__Options_H__*/ diff --git a/src/client/OptionsFile.cpp b/src/client/OptionsFile.cpp index ef2a0dc..15d8bca 100755 --- a/src/client/OptionsFile.cpp +++ b/src/client/OptionsFile.cpp @@ -1,98 +1,98 @@ -#include "OptionsFile.h" -#include -#include -#include -#include - -#if defined(_WIN32) - #include -#else - #include - #include -#endif - -OptionsFile::OptionsFile() { -#ifdef __APPLE__ - settingsPath = "./Documents/options.txt"; -#elif defined(ANDROID) - settingsPath = "options.txt"; -#elif defined(__EMSCRIPTEN__) - settingsPath = "/games/com.mojang/options.txt"; -#else - settingsPath = "options.txt"; -#endif -} - -void OptionsFile::setOptionsPath(const std::string& path) { - settingsPath = path; -} - -std::string OptionsFile::getOptionsPath() const { - return settingsPath; -} -void OptionsFile::save(const StringVector& settings) { - FILE* pFile = fopen(settingsPath.c_str(), "w"); - - if (!pFile && errno == ENOENT) { - std::string dir = settingsPath; - size_t fpos = dir.find_last_of("/\\"); - if (fpos != std::string::npos) { - dir.resize(fpos); - - std::string toCreate; - for (size_t i = 0; i <= dir.size(); ++i) { - if (i == dir.size() || dir[i] == '/' || dir[i] == '\\') { - if (!toCreate.empty()) { -#if defined(_WIN32) - _mkdir(toCreate.c_str()); -#else - mkdir(toCreate.c_str(), 0755); -#endif - } - } - if (i < dir.size()) - toCreate.push_back(dir[i]); - } - } - - pFile = fopen(settingsPath.c_str(), "w"); - } - - if (!pFile) { - LOGI("OptionsFile::save failed: %s", strerror(errno)); - return; - } - - for (const auto& s : settings) { - fprintf(pFile, "%s\n", s.c_str()); - } - - fclose(pFile); -} - - -StringVector OptionsFile::getOptionStrings() { - StringVector returnVector; - FILE* pFile = fopen(settingsPath.c_str(), "r"); - if(pFile != NULL) { - char lineBuff[128]; - while(fgets(lineBuff, sizeof lineBuff, pFile)) { - // Strip trailing newline - size_t len = strlen(lineBuff); - while(len > 0 && (lineBuff[len-1] == '\n' || lineBuff[len-1] == '\r')) - lineBuff[--len] = '\0'; - if(len < 3) continue; - // Split "key:value" into two separate entries to match update() pairing - char* colon = strchr(lineBuff, ':'); - if(colon) { - returnVector.push_back(std::string(lineBuff, colon - lineBuff)); - returnVector.push_back(std::string(colon + 1)); - } - } - fclose(pFile); - } else { - if (errno != ENOENT) - LOGI("OptionsFile::getOptionStrings failed to open '%s' for reading: %s", settingsPath.c_str(), strerror(errno)); - } - return returnVector; -} +#include "OptionsFile.h" +#include +#include +#include +#include + +#if defined(_WIN32) + #include +#else + #include + #include +#endif + +OptionsFile::OptionsFile() { +#ifdef __APPLE__ + settingsPath = "./Documents/options.txt"; +#elif defined(ANDROID) + settingsPath = "options.txt"; +#elif defined(__EMSCRIPTEN__) + settingsPath = "/games/com.mojang/options.txt"; +#else + settingsPath = "options.txt"; +#endif +} + +void OptionsFile::setOptionsPath(const std::string& path) { + settingsPath = path; +} + +std::string OptionsFile::getOptionsPath() const { + return settingsPath; +} +void OptionsFile::save(const StringVector& settings) { + FILE* pFile = fopen(settingsPath.c_str(), "w"); + + if (!pFile && errno == ENOENT) { + std::string dir = settingsPath; + size_t fpos = dir.find_last_of("/\\"); + if (fpos != std::string::npos) { + dir.resize(fpos); + + std::string toCreate; + for (size_t i = 0; i <= dir.size(); ++i) { + if (i == dir.size() || dir[i] == '/' || dir[i] == '\\') { + if (!toCreate.empty()) { +#if defined(_WIN32) + _mkdir(toCreate.c_str()); +#else + mkdir(toCreate.c_str(), 0755); +#endif + } + } + if (i < dir.size()) + toCreate.push_back(dir[i]); + } + } + + pFile = fopen(settingsPath.c_str(), "w"); + } + + if (!pFile) { + LOGI("OptionsFile::save failed: %s", strerror(errno)); + return; + } + + for (const auto& s : settings) { + fprintf(pFile, "%s\n", s.c_str()); + } + + fclose(pFile); +} + + +StringVector OptionsFile::getOptionStrings() { + StringVector returnVector; + FILE* pFile = fopen(settingsPath.c_str(), "r"); + if(pFile != NULL) { + char lineBuff[128]; + while(fgets(lineBuff, sizeof lineBuff, pFile)) { + // Strip trailing newline + size_t len = strlen(lineBuff); + while(len > 0 && (lineBuff[len-1] == '\n' || lineBuff[len-1] == '\r')) + lineBuff[--len] = '\0'; + if(len < 3) continue; + // Split "key:value" into two separate entries to match update() pairing + char* colon = strchr(lineBuff, ':'); + if(colon) { + returnVector.push_back(std::string(lineBuff, colon - lineBuff)); + returnVector.push_back(std::string(colon + 1)); + } + } + fclose(pFile); + } else { + if (errno != ENOENT) + LOGI("OptionsFile::getOptionStrings failed to open '%s' for reading: %s", settingsPath.c_str(), strerror(errno)); + } + return returnVector; +} diff --git a/src/client/OptionsFile.h b/src/client/OptionsFile.h index b2464a0..ff18cde 100755 --- a/src/client/OptionsFile.h +++ b/src/client/OptionsFile.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT__OptionsFile_H__ -#define NET_MINECRAFT_CLIENT__OptionsFile_H__ - -//package net.minecraft.client; -#include -#include -typedef std::vector StringVector; -class OptionsFile -{ -public: - OptionsFile(); - void save(const StringVector& settings); - StringVector getOptionStrings(); - void setOptionsPath(const std::string& path); - std::string getOptionsPath() const; - -private: - std::string settingsPath; -}; - -#endif /* NET_MINECRAFT_CLIENT__OptionsFile_H__ */ +#ifndef NET_MINECRAFT_CLIENT__OptionsFile_H__ +#define NET_MINECRAFT_CLIENT__OptionsFile_H__ + +//package net.minecraft.client; +#include +#include +typedef std::vector StringVector; +class OptionsFile +{ +public: + OptionsFile(); + void save(const StringVector& settings); + StringVector getOptionStrings(); + void setOptionsPath(const std::string& path); + std::string getOptionsPath() const; + +private: + std::string settingsPath; +}; + +#endif /* NET_MINECRAFT_CLIENT__OptionsFile_H__ */ diff --git a/src/client/Timer.h b/src/client/Timer.h index cb7a928..4caa15d 100755 --- a/src/client/Timer.h +++ b/src/client/Timer.h @@ -1,124 +1,124 @@ -#ifndef NET_MINECRAFT_CLIENT__Timer_H__ -#define NET_MINECRAFT_CLIENT__Timer_H__ - -//package net.minecraft.client; -#include "../platform/time.h" - -class Timer -{ -public: - Timer(float ticksPerSecond) - : ticksPerSecond(ticksPerSecond), - adjustTime(1.0f), - timeScale(1.0f), - passedTime(0) - { - lastMs = getTimeMs(); - lastMsSysTime = lastMs; - lastTime = lastMs / 1000.0f; - } - - void advanceTime() { - long nowMs = getTimeMs(); - long passedMs = nowMs - lastMs; - long msSysTime = nowMs;//System.nanoTime() / 1000000; - - if (passedMs > 1000) { - long passedMsSysTime = msSysTime - lastMsSysTime; - if (passedMsSysTime == 0) - passedMs = passedMsSysTime = 1; - - float adjustTimeT = passedMs / (float) passedMsSysTime; - adjustTime += (adjustTimeT - adjustTime) * 0.2f; - - lastMs = nowMs; - lastMsSysTime = msSysTime; - } - if (passedMs < 0) { - lastMs = nowMs; - lastMsSysTime = msSysTime; - } - - float now = msSysTime / 1000.0f; - float passedSeconds = (now - lastTime) * adjustTime; - lastTime = now; - - if (passedSeconds < 0) passedSeconds = 0; - if (passedSeconds > 1) passedSeconds = 1; - //LOGI("passed s: %f\n", passedSeconds); - - passedTime += passedSeconds * timeScale * ticksPerSecond; - - ticks = (int) passedTime; - passedTime -= ticks; - if (ticks > MAX_TICKS_PER_UPDATE) ticks = MAX_TICKS_PER_UPDATE; - a = passedTime; - } - - /** - * Advances time the max number of ticks per second. - */ - void advanceTimeQuickly() { - - float passedSeconds = (float) MAX_TICKS_PER_UPDATE / (float) ticksPerSecond; - - passedTime += passedSeconds * timeScale * ticksPerSecond; - ticks = (int) passedTime; - passedTime -= ticks; - a = passedTime; - - lastMs = getTimeMs();//System.currentTimeMillis(); - lastMsSysTime = lastMs; - } - - void skipTime() { - long nowMs = getTimeMs();//System.currentTimeMillis(); - long passedMs = nowMs - lastMs; - long msSysTime = nowMs;//System.nanoTime() / 1000000; - - if (passedMs > 1000) { - long passedMsSysTime = msSysTime - lastMsSysTime; - if (passedMsSysTime == 0) - passedMs = passedMsSysTime = 1; - - float adjustTimeT = passedMs / (float) passedMsSysTime; - adjustTime += (adjustTimeT - adjustTime) * 0.2f; - - lastMs = nowMs; - lastMsSysTime = msSysTime; - } - if (passedMs < 0) { - lastMs = nowMs; - lastMsSysTime = msSysTime; - } - - float now = msSysTime / 1000.0f; - float passedSeconds = (now - lastTime) * adjustTime; - lastTime = now; - - if (passedSeconds < 0) passedSeconds = 0; - if (passedSeconds > 1) passedSeconds = 1; - - passedTime += passedSeconds * timeScale * ticksPerSecond; - - ticks = (int) 0; - if (ticks > MAX_TICKS_PER_UPDATE) ticks = MAX_TICKS_PER_UPDATE; - passedTime -= ticks; - } - -public: - float ticksPerSecond; - int ticks; - float a; - float timeScale; - float passedTime; -private: - static const int MAX_TICKS_PER_UPDATE = 10; - - float lastTime; - long lastMs; - long lastMsSysTime; - float adjustTime; -}; - -#endif /*NET_MINECRAFT_CLIENT__Timer_H__*/ +#ifndef NET_MINECRAFT_CLIENT__Timer_H__ +#define NET_MINECRAFT_CLIENT__Timer_H__ + +//package net.minecraft.client; +#include "../platform/time.h" + +class Timer +{ +public: + Timer(float ticksPerSecond) + : ticksPerSecond(ticksPerSecond), + adjustTime(1.0f), + timeScale(1.0f), + passedTime(0) + { + lastMs = getTimeMs(); + lastMsSysTime = lastMs; + lastTime = lastMs / 1000.0f; + } + + void advanceTime() { + long nowMs = getTimeMs(); + long passedMs = nowMs - lastMs; + long msSysTime = nowMs;//System.nanoTime() / 1000000; + + if (passedMs > 1000) { + long passedMsSysTime = msSysTime - lastMsSysTime; + if (passedMsSysTime == 0) + passedMs = passedMsSysTime = 1; + + float adjustTimeT = passedMs / (float) passedMsSysTime; + adjustTime += (adjustTimeT - adjustTime) * 0.2f; + + lastMs = nowMs; + lastMsSysTime = msSysTime; + } + if (passedMs < 0) { + lastMs = nowMs; + lastMsSysTime = msSysTime; + } + + float now = msSysTime / 1000.0f; + float passedSeconds = (now - lastTime) * adjustTime; + lastTime = now; + + if (passedSeconds < 0) passedSeconds = 0; + if (passedSeconds > 1) passedSeconds = 1; + //LOGI("passed s: %f\n", passedSeconds); + + passedTime += passedSeconds * timeScale * ticksPerSecond; + + ticks = (int) passedTime; + passedTime -= ticks; + if (ticks > MAX_TICKS_PER_UPDATE) ticks = MAX_TICKS_PER_UPDATE; + a = passedTime; + } + + /** + * Advances time the max number of ticks per second. + */ + void advanceTimeQuickly() { + + float passedSeconds = (float) MAX_TICKS_PER_UPDATE / (float) ticksPerSecond; + + passedTime += passedSeconds * timeScale * ticksPerSecond; + ticks = (int) passedTime; + passedTime -= ticks; + a = passedTime; + + lastMs = getTimeMs();//System.currentTimeMillis(); + lastMsSysTime = lastMs; + } + + void skipTime() { + long nowMs = getTimeMs();//System.currentTimeMillis(); + long passedMs = nowMs - lastMs; + long msSysTime = nowMs;//System.nanoTime() / 1000000; + + if (passedMs > 1000) { + long passedMsSysTime = msSysTime - lastMsSysTime; + if (passedMsSysTime == 0) + passedMs = passedMsSysTime = 1; + + float adjustTimeT = passedMs / (float) passedMsSysTime; + adjustTime += (adjustTimeT - adjustTime) * 0.2f; + + lastMs = nowMs; + lastMsSysTime = msSysTime; + } + if (passedMs < 0) { + lastMs = nowMs; + lastMsSysTime = msSysTime; + } + + float now = msSysTime / 1000.0f; + float passedSeconds = (now - lastTime) * adjustTime; + lastTime = now; + + if (passedSeconds < 0) passedSeconds = 0; + if (passedSeconds > 1) passedSeconds = 1; + + passedTime += passedSeconds * timeScale * ticksPerSecond; + + ticks = (int) 0; + if (ticks > MAX_TICKS_PER_UPDATE) ticks = MAX_TICKS_PER_UPDATE; + passedTime -= ticks; + } + +public: + float ticksPerSecond; + int ticks; + float a; + float timeScale; + float passedTime; +private: + static const int MAX_TICKS_PER_UPDATE = 10; + + float lastTime; + long lastMs; + long lastMsSysTime; + float adjustTime; +}; + +#endif /*NET_MINECRAFT_CLIENT__Timer_H__*/ diff --git a/src/client/gamemode/GameMode.h b/src/client/gamemode/GameMode.h index 83e7f7d..d2cb0b8 100755 --- a/src/client/gamemode/GameMode.h +++ b/src/client/gamemode/GameMode.h @@ -1,63 +1,63 @@ -#ifndef NET_MINECRAFT_CLIENT_GAMEMODE__GameMode_H__ -#define NET_MINECRAFT_CLIENT_GAMEMODE__GameMode_H__ - -//package net.minecraft.client.gamemode; - -#include "../../world/level/tile/Tile.h" - -class ItemInstance; -class Minecraft; -class Level; -class Player; -class Abilities; - -class GameMode -{ -protected: - Minecraft* minecraft; -public: - GameMode(Minecraft* minecraft); - virtual ~GameMode() {} - - virtual void initLevel(Level* level) {} - - virtual void startDestroyBlock(int x, int y, int z, int face); - virtual bool destroyBlock(int x, int y, int z, int face); - virtual void continueDestroyBlock(int x, int y, int z, int face) = 0; - virtual void stopDestroyBlock() {} - - virtual void tick(); - virtual void render(float a); - - virtual float getPickRange(); - /* void postLevelGen(LevelGen levelGen, Level level) {} */ - - virtual bool useItem(Player* player, Level* level, ItemInstance* item); - virtual bool useItemOn(Player* player, Level* level, ItemInstance* item, int x, int y, int z, int face, const Vec3& hit); - - virtual Player* createPlayer(Level* level); - virtual void initPlayer(Player* player); - virtual void adjustPlayer(Player* player) {} - virtual bool canHurtPlayer() { return false; } - - virtual void interact(Player* player, Entity* entity); - virtual void attack(Player* player, Entity* entity); - - virtual ItemInstance* handleInventoryMouseClick(int containerId, int slotNum, int buttonNum, Player* player); - virtual void handleCloseInventory(int containerId, Player* player); - - virtual bool isCreativeType() { return false; } - virtual bool isSurvivalType() { return false; } - - virtual void initAbilities(Abilities& abilities) {} - - virtual void releaseUsingItem(Player* player); - - float oDestroyProgress; - float destroyProgress; -protected: - int destroyTicks; - int destroyDelay; -}; - -#endif /*NET_MINECRAFT_CLIENT_GAMEMODE__GameMode_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GAMEMODE__GameMode_H__ +#define NET_MINECRAFT_CLIENT_GAMEMODE__GameMode_H__ + +//package net.minecraft.client.gamemode; + +#include "../../world/level/tile/Tile.h" + +class ItemInstance; +class Minecraft; +class Level; +class Player; +class Abilities; + +class GameMode +{ +protected: + Minecraft* minecraft; +public: + GameMode(Minecraft* minecraft); + virtual ~GameMode() {} + + virtual void initLevel(Level* level) {} + + virtual void startDestroyBlock(int x, int y, int z, int face); + virtual bool destroyBlock(int x, int y, int z, int face); + virtual void continueDestroyBlock(int x, int y, int z, int face) = 0; + virtual void stopDestroyBlock() {} + + virtual void tick(); + virtual void render(float a); + + virtual float getPickRange(); + /* void postLevelGen(LevelGen levelGen, Level level) {} */ + + virtual bool useItem(Player* player, Level* level, ItemInstance* item); + virtual bool useItemOn(Player* player, Level* level, ItemInstance* item, int x, int y, int z, int face, const Vec3& hit); + + virtual Player* createPlayer(Level* level); + virtual void initPlayer(Player* player); + virtual void adjustPlayer(Player* player) {} + virtual bool canHurtPlayer() { return false; } + + virtual void interact(Player* player, Entity* entity); + virtual void attack(Player* player, Entity* entity); + + virtual ItemInstance* handleInventoryMouseClick(int containerId, int slotNum, int buttonNum, Player* player); + virtual void handleCloseInventory(int containerId, Player* player); + + virtual bool isCreativeType() { return false; } + virtual bool isSurvivalType() { return false; } + + virtual void initAbilities(Abilities& abilities) {} + + virtual void releaseUsingItem(Player* player); + + float oDestroyProgress; + float destroyProgress; +protected: + int destroyTicks; + int destroyDelay; +}; + +#endif /*NET_MINECRAFT_CLIENT_GAMEMODE__GameMode_H__*/ diff --git a/src/client/gamemode/SurvivalMode.cpp b/src/client/gamemode/SurvivalMode.cpp index ac49fda..cb085e1 100755 --- a/src/client/gamemode/SurvivalMode.cpp +++ b/src/client/gamemode/SurvivalMode.cpp @@ -1,98 +1,98 @@ -#include "SurvivalMode.h" -#include "../Minecraft.h" -#include "../player/LocalPlayer.h" -#ifndef STANDALONE_SERVER -#include "../particle/ParticleEngine.h" -#include "../sound/SoundEngine.h" -#endif -#include "../../world/level/Level.h" -#include "../../world/entity/player/Abilities.h" - -SurvivalMode::SurvivalMode( Minecraft* minecraft ) -: super(minecraft), - xDestroyBlock(-1), - yDestroyBlock(-1), - zDestroyBlock(-1) -{ -} - -void SurvivalMode::continueDestroyBlock( int x, int y, int z, int face ) { - if (destroyDelay > 0) { - destroyDelay--; - return; - } - - if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock) { - int t = minecraft->level->getTile(x, y, z); - if (t == 0) return; - Tile* tile = Tile::tiles[t]; - - destroyProgress += tile->getDestroyProgress(minecraft->player); - - if ((++destroyTicks & 3) == 1) { -#ifndef STANDALONE_SERVER - if (tile != NULL) { - minecraft->soundEngine->play(tile->soundType->getStepSound(), x + 0.5f, y + 0.5f, z + 0.5f, (tile->soundType->getVolume() + 1) / 8, tile->soundType->getPitch() * 0.5f); - } -#endif - } - - if (destroyProgress >= 1) { - destroyBlock(x, y, z, face); - destroyProgress = 0; - oDestroyProgress = 0; - destroyTicks = 0; - destroyDelay = 5; - } - } else { - destroyProgress = 0; - oDestroyProgress = 0; - destroyTicks = 0; - xDestroyBlock = x; - yDestroyBlock = y; - zDestroyBlock = z; - } -} - -bool SurvivalMode::destroyBlock( int x, int y, int z, int face ) { - int t = minecraft->level->getTile(x, y, z); - int data = minecraft->level->getData(x, y, z); - bool changed = GameMode::destroyBlock(x, y, z, face); - bool couldDestroy = minecraft->player->canDestroy(Tile::tiles[t]); - - ItemInstance* item = minecraft->player->inventory->getSelected(); - if (item != NULL) { - item->mineBlock(t, x, y, z); - if (item->count == 0) { - //item->snap(minecraft->player); - minecraft->player->inventory->clearSlot(minecraft->player->inventory->selected); - } - } - if (changed && couldDestroy) { - ItemInstance instance(t, 1, data); - Tile::tiles[t]->playerDestroy(minecraft->level, minecraft->player, x, y, z, data); - } - return changed; -} - -void SurvivalMode::stopDestroyBlock() { - destroyProgress = 0; - destroyDelay = 0; -} - -void SurvivalMode::initAbilities( Abilities& abilities ) { - abilities.flying = false; - abilities.mayfly = false; - abilities.instabuild = false; - abilities.invulnerable = false; -} - -void SurvivalMode::startDestroyBlock( int x, int y, int z, int face ) { - if(minecraft->player->getCarriedItem() != NULL && minecraft->player->getCarriedItem()->id == Item::bow->id) - return; - - int t = minecraft->level->getTile(x, y, z); - if (t > 0 && destroyProgress == 0) Tile::tiles[t]->attack(minecraft->level, x, y, z, minecraft->player); - if (t > 0 && Tile::tiles[t]->getDestroyProgress(minecraft->player) >= 1) - destroyBlock(x, y, z, face); -} +#include "SurvivalMode.h" +#include "../Minecraft.h" +#include "../player/LocalPlayer.h" +#ifndef STANDALONE_SERVER +#include "../particle/ParticleEngine.h" +#include "../sound/SoundEngine.h" +#endif +#include "../../world/level/Level.h" +#include "../../world/entity/player/Abilities.h" + +SurvivalMode::SurvivalMode( Minecraft* minecraft ) +: super(minecraft), + xDestroyBlock(-1), + yDestroyBlock(-1), + zDestroyBlock(-1) +{ +} + +void SurvivalMode::continueDestroyBlock( int x, int y, int z, int face ) { + if (destroyDelay > 0) { + destroyDelay--; + return; + } + + if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock) { + int t = minecraft->level->getTile(x, y, z); + if (t == 0) return; + Tile* tile = Tile::tiles[t]; + + destroyProgress += tile->getDestroyProgress(minecraft->player); + + if ((++destroyTicks & 3) == 1) { +#ifndef STANDALONE_SERVER + if (tile != NULL) { + minecraft->soundEngine->play(tile->soundType->getStepSound(), x + 0.5f, y + 0.5f, z + 0.5f, (tile->soundType->getVolume() + 1) / 8, tile->soundType->getPitch() * 0.5f); + } +#endif + } + + if (destroyProgress >= 1) { + destroyBlock(x, y, z, face); + destroyProgress = 0; + oDestroyProgress = 0; + destroyTicks = 0; + destroyDelay = 5; + } + } else { + destroyProgress = 0; + oDestroyProgress = 0; + destroyTicks = 0; + xDestroyBlock = x; + yDestroyBlock = y; + zDestroyBlock = z; + } +} + +bool SurvivalMode::destroyBlock( int x, int y, int z, int face ) { + int t = minecraft->level->getTile(x, y, z); + int data = minecraft->level->getData(x, y, z); + bool changed = GameMode::destroyBlock(x, y, z, face); + bool couldDestroy = minecraft->player->canDestroy(Tile::tiles[t]); + + ItemInstance* item = minecraft->player->inventory->getSelected(); + if (item != NULL) { + item->mineBlock(t, x, y, z); + if (item->count == 0) { + //item->snap(minecraft->player); + minecraft->player->inventory->clearSlot(minecraft->player->inventory->selected); + } + } + if (changed && couldDestroy) { + ItemInstance instance(t, 1, data); + Tile::tiles[t]->playerDestroy(minecraft->level, minecraft->player, x, y, z, data); + } + return changed; +} + +void SurvivalMode::stopDestroyBlock() { + destroyProgress = 0; + destroyDelay = 0; +} + +void SurvivalMode::initAbilities( Abilities& abilities ) { + abilities.flying = false; + abilities.mayfly = false; + abilities.instabuild = false; + abilities.invulnerable = false; +} + +void SurvivalMode::startDestroyBlock( int x, int y, int z, int face ) { + if(minecraft->player->getCarriedItem() != NULL && minecraft->player->getCarriedItem()->id == Item::bow->id) + return; + + int t = minecraft->level->getTile(x, y, z); + if (t > 0 && destroyProgress == 0) Tile::tiles[t]->attack(minecraft->level, x, y, z, minecraft->player); + if (t > 0 && Tile::tiles[t]->getDestroyProgress(minecraft->player) >= 1) + destroyBlock(x, y, z, face); +} diff --git a/src/client/gamemode/SurvivalMode.h b/src/client/gamemode/SurvivalMode.h index 3057103..58c4a7c 100755 --- a/src/client/gamemode/SurvivalMode.h +++ b/src/client/gamemode/SurvivalMode.h @@ -1,31 +1,31 @@ -#ifndef NET_MINECRAFT_CLIENT_GAMEMODE__SurvivalMode_H__ -#define NET_MINECRAFT_CLIENT_GAMEMODE__SurvivalMode_H__ - -#include "GameMode.h" - -class Abilities; -class Minecraft; - -class SurvivalMode: public GameMode -{ - typedef GameMode super; -public: - SurvivalMode(Minecraft* minecraft); - - bool destroyBlock(int x, int y, int z, int face); - void startDestroyBlock(int x, int y, int z, int face); - void continueDestroyBlock(int x, int y, int z, int face); - void stopDestroyBlock(); - - bool canHurtPlayer() { return true; } - - bool isSurvivalType() { return true; } - - void initAbilities( Abilities& abilities ); -private: - int xDestroyBlock; - int yDestroyBlock; - int zDestroyBlock; -}; - -#endif /*NET_MINECRAFT_CLIENT_GAMEMODE__SurvivalMode_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GAMEMODE__SurvivalMode_H__ +#define NET_MINECRAFT_CLIENT_GAMEMODE__SurvivalMode_H__ + +#include "GameMode.h" + +class Abilities; +class Minecraft; + +class SurvivalMode: public GameMode +{ + typedef GameMode super; +public: + SurvivalMode(Minecraft* minecraft); + + bool destroyBlock(int x, int y, int z, int face); + void startDestroyBlock(int x, int y, int z, int face); + void continueDestroyBlock(int x, int y, int z, int face); + void stopDestroyBlock(); + + bool canHurtPlayer() { return true; } + + bool isSurvivalType() { return true; } + + void initAbilities( Abilities& abilities ); +private: + int xDestroyBlock; + int yDestroyBlock; + int zDestroyBlock; +}; + +#endif /*NET_MINECRAFT_CLIENT_GAMEMODE__SurvivalMode_H__*/ diff --git a/src/client/gui/Font.cpp b/src/client/gui/Font.cpp index 3f88fc6..2e0521f 100755 --- a/src/client/gui/Font.cpp +++ b/src/client/gui/Font.cpp @@ -1,372 +1,372 @@ -#include "Font.h" - -//#include "SharedConstants.h" -#include "../Options.h" -#include "../renderer/Textures.h" -#include "../renderer/Tesselator.h" -#include "../../util/Mth.h" -#include - -Font::Font( Options* options, const std::string& name, Textures* textures ) -: options(options), - fontTexture(0), - fontName(name), - index(0), - count(0), - _textures(textures), - _x(0), _y(0), - _cols(16), _rows(16), - _charOffset(0), - lineHeight(DefaultLineHeight) -{ - init(options); -} - - -//Font::Font( Options* options, const std::string& name, Textures* textures, int imgW, int imgH, int x, int y, int cols, int rows, unsigned char charOffset ) -//: options(options), -// fontTexture(0), -// fontName(name), -// index(0), -// count(0), -// _textures(textures), -// _x(x), _y(y), -// _cols(cols), _rows(rows), -// _charOffset(charOffset) -//{ -// init(options); -//} - -void Font::onGraphicsReset() -{ - init(options); -} - -void Font::init( Options* options ) -{ - TextureId fontTexture = _textures->loadTexture(fontName); - const TextureData* tex = _textures->getTemporaryTextureData(fontTexture); - - if (!tex) - return; - - unsigned char* rawPixels = tex->data; - - const int numChars = _rows * _cols; - for (int i = 0; i < numChars; i++) { - int xt = i % _cols; - int yt = i / _cols; - - int x = 7; - for (; x >= 0; x--) { - int xPixel = _x + xt * 8 + x; - bool emptyColumn = true; - for (int y = 0; y < 8 && emptyColumn; y++) { - int yPixel = _y + (yt * 8 + y) * tex->w; - unsigned char pixelalpha = rawPixels[(xPixel + yPixel) << 2]; - if (pixelalpha > 0) emptyColumn = false; - } - if (!emptyColumn) { - break; - } - } - - if (i == ' ') x = 4 - 2; - charWidths[i] = x + 2; - fcharWidths[i] = (float) charWidths[i]; - } - -#ifdef USE_VBO - return; // this <1 -#endif - -#ifndef USE_VBO - listPos = glGenLists(256 + 32); - - Tesselator& t = Tesselator::instance; - for (int i = 0; i < 256; i++) { - glNewList(listPos + i, GL_COMPILE); - // @attn @huge @note: This is some dangerous code right here / Aron, added ^1 - t.begin(); - buildChar(i); - t.end(false, -1); - - glTranslatef2((GLfloat)charWidths[i], 0.0f, 0.0f); - glEndList(); - } - - for (int i = 0; i < 32; i++) { - int br = ((i >> 3) & 1) * 0x55; - int r = ((i >> 2) & 1) * 0xaa + br; - int g = ((i >> 1) & 1) * 0xaa + br; - int b = ((i >> 0) & 1) * 0xaa + br; - if (i == 6) { - r += 0x55; - } - bool darken = i >= 16; - - if (options->anaglyph3d) { - int cr = (r * 30 + g * 59 + b * 11) / 100; - int cg = (r * 30 + g * 70) / (100); - int cb = (r * 30 + b * 70) / (100); - - r = cr; - g = cg; - b = cb; - } - - // color = r << 16 | g << 8 | b; - if (darken) { - r /= 4; - g /= 4; - b /= 4; - } - - glNewList(listPos + 256 + i, GL_COMPILE); - glColor3f(r / 255.0f, g / 255.0f, b / 255.0f); - glEndList(); - } -#endif -} - -void Font::drawShadow( const std::string& str, float x, float y, int color ) -{ - draw(str, x + 1, y + 1, color, true); - draw(str, x, y, color); -} -void Font::drawShadow( const char* str, float x, float y, int color ) -{ - draw(str, x + 1, y + 1, color, true); - draw(str, x, y, color); -} - -void Font::draw( const std::string& str, float x, float y, int color ) -{ - draw(str, x, y, color, false); -} - -void Font::draw( const char* str, float x, float y, int color ) -{ - draw(str, x, y, color, false); -} - -void Font::draw( const char* str, float x, float y, int color, bool darken ) -{ -#ifdef USE_VBO - drawSlow(str, x, y, color, darken); -#endif -} - -void Font::draw( const std::string& str, float x, float y, int color, bool darken ) -{ -#ifdef USE_VBO - drawSlow(str, x, y, color, darken); - return; -#endif - - if (str.empty()) return; - - if (darken) { - int oldAlpha = color & 0xff000000; - color = (color & 0xfcfcfc) >> 2; - color += oldAlpha; - } - - _textures->loadAndBindTexture(fontName); - float r = ((color >> 16) & 0xff) / 255.0f; - float g = ((color >> 8) & 0xff) / 255.0f; - float b = ((color) & 0xff) / 255.0f; - float a = ((color >> 24) & 0xff) / 255.0f; - if (a == 0) a = 1; - glColor4f2(r, g, b, a); - - static const std::string hex("0123456789abcdef"); - - index = 0; - glPushMatrix2(); - glTranslatef2((GLfloat)x, (GLfloat)y, 0.0f); - for (unsigned int i = 0; i < str.length(); i++) { - while (str.length() > i + 1 && str[i] == '\xa7') { - int cc = hex.find((char)tolower(str[i + 1])); - if (cc < 0 || cc > 15) cc = 15; - lists[index++] = listPos + 256 + cc + (darken ? 16 : 0); - - if (index == 1024) { - count = index; - index = 0; -#ifndef USE_VBO - glCallLists(count, GL_UNSIGNED_INT, lists); -#endif - count = 1024; - } - - i += 2; - } - - if (i < str.length()) { - //int ch = SharedConstants.acceptableLetters.indexOf(str.charAt(i)); - char ch = str[i]; - if (ch >= 0) { - //ib.put(listPos + ch + 32); - lists[index++] = listPos + ch; - } - } - - if (index == 1024) { - count = index; - index = 0; -#ifndef USE_VBO - glCallLists(count, GL_UNSIGNED_INT, lists); -#endif - count = 1024; - } - } - count = index; - index = 0; -#ifndef USE_VBO - glCallLists(count, GL_UNSIGNED_INT, lists); -#endif - glPopMatrix2(); -} - -int Font::width( const std::string& str ) -{ - int maxLen = 0; - int len = 0; - - for (unsigned int i = 0; i < str.length(); i++) { - if (str[i] == '\xa7') { - i++; - } else { - //int ch = SharedConstants.acceptableLetters.indexOf(str.charAt(i)); - //if (ch >= 0) { - // len += charWidths[ch + 32]; - //} - if (str[i] == '\n') { - if (len > maxLen) maxLen = len; - len = 0; - } - else { - int charWidth = charWidths[ (unsigned char) str[i] ]; - len += charWidth; - } - } - } - return maxLen>len? maxLen : len; -} - -int Font::height( const std::string& str ) { - int h = 0; - bool hasLine = false; - for (unsigned int i = 0; i < str.length(); ++i) { - if (str[i] == '\n') hasLine = true; - else { - if (hasLine) h += lineHeight; - hasLine = false; - } - } - return h; -} - -std::string Font::sanitize( const std::string& str ) -{ - std::string sanitized(str.length() + 1, 0); - int j = 0; - - for (unsigned int i = 0; i < str.length(); i++) { - if (str[i] == '\xa7') { - i++; - //} else if (SharedConstants.acceptableLetters.indexOf(str.charAt(i)) >= 0) { - } else { - sanitized[j++] = str[i]; - } - } - return sanitized.erase(j); -} - -void Font::drawWordWrap( const std::string& str, float x, float y, float w, int col ) -{ - char* cstr = new char[str.length() + 1]; - strncpy(cstr, str.c_str(), str.length()); - cstr[str.length()] = 0; - - const char* lims = " \n\t\r"; - char* ptok = strtok(cstr, lims); - - std::vector words; - while (ptok != NULL) { - words.push_back( ptok ); - ptok = strtok(NULL, lims); - } - - delete[] cstr; - - int pos = 0; - while (pos < (int)words.size()) { - std::string line = words[pos++] + " "; - while (pos < (int)words.size() && width(line + words[pos]) < w) { - line += words[pos++] + " "; - } - drawShadow(line, x, y, col); - y += lineHeight; - } -} - -void Font::drawSlow( const std::string& str, float x, float y, int color, bool darken /*= false*/ ) { - drawSlow(str.c_str(), x, y, color, darken); -} -void Font::drawSlow( const char* str, float x, float y, int color, bool darken /*= false*/ ) -{ - if (!str) return; - - if (darken) { - int oldAlpha = color & 0xff000000; - color = (color & 0xfcfcfc) >> 2; - color += oldAlpha; - } - - _textures->loadAndBindTexture(fontName); - - Tesselator& t = Tesselator::instance; - t.begin(); - int alpha = (0xff000000 & color) >> 24; - if (!alpha) alpha = 0xff; - t.color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, alpha); - - t.addOffset((float)x, (float)y, 0); - float xOffset = 0; - float yOffset = 0; - - while (unsigned char ch = *(str++)) { - if (ch == '\n') { - xOffset = 0; - yOffset += lineHeight; - } else { - buildChar(ch, xOffset, yOffset); - xOffset += fcharWidths[ch]; - } - } - t.draw(); - t.addOffset(-(float)x, -(float)y, 0); -} - -void Font::buildChar( unsigned char i, float x /*= 0*/, float y /*=0*/ ) -{ - Tesselator& t = Tesselator::instance; - - //i -= _charOffset; - //int ix = (i % _cols) * 8 + _x; - //int iy = (i / _cols) * 8 + _y; - float ix = (float)((i & 15) * 8); - float iy = (float)((i >> 4) * 8); - float s = 7.99f; - - float uo = (0.0f) / 128.0f; - float vo = (0.0f) / 128.0f; - - t.vertexUV(x, y + s, 0, ix / 128.0f + uo, (iy + s) / 128.0f + vo); - t.vertexUV(x + s, y + s, 0, (ix + s) / 128.0f + uo, (iy + s) / 128.0f + vo); - t.vertexUV(x + s, y, 0, (ix + s) / 128.0f + uo, iy / 128.0f + vo); - t.vertexUV(x, y, 0, ix / 128.0f + uo, iy / 128.0f + vo); -} - +#include "Font.h" + +//#include "SharedConstants.h" +#include "../Options.h" +#include "../renderer/Textures.h" +#include "../renderer/Tesselator.h" +#include "../../util/Mth.h" +#include + +Font::Font( Options* options, const std::string& name, Textures* textures ) +: options(options), + fontTexture(0), + fontName(name), + index(0), + count(0), + _textures(textures), + _x(0), _y(0), + _cols(16), _rows(16), + _charOffset(0), + lineHeight(DefaultLineHeight) +{ + init(options); +} + + +//Font::Font( Options* options, const std::string& name, Textures* textures, int imgW, int imgH, int x, int y, int cols, int rows, unsigned char charOffset ) +//: options(options), +// fontTexture(0), +// fontName(name), +// index(0), +// count(0), +// _textures(textures), +// _x(x), _y(y), +// _cols(cols), _rows(rows), +// _charOffset(charOffset) +//{ +// init(options); +//} + +void Font::onGraphicsReset() +{ + init(options); +} + +void Font::init( Options* options ) +{ + TextureId fontTexture = _textures->loadTexture(fontName); + const TextureData* tex = _textures->getTemporaryTextureData(fontTexture); + + if (!tex) + return; + + unsigned char* rawPixels = tex->data; + + const int numChars = _rows * _cols; + for (int i = 0; i < numChars; i++) { + int xt = i % _cols; + int yt = i / _cols; + + int x = 7; + for (; x >= 0; x--) { + int xPixel = _x + xt * 8 + x; + bool emptyColumn = true; + for (int y = 0; y < 8 && emptyColumn; y++) { + int yPixel = _y + (yt * 8 + y) * tex->w; + unsigned char pixelalpha = rawPixels[(xPixel + yPixel) << 2]; + if (pixelalpha > 0) emptyColumn = false; + } + if (!emptyColumn) { + break; + } + } + + if (i == ' ') x = 4 - 2; + charWidths[i] = x + 2; + fcharWidths[i] = (float) charWidths[i]; + } + +#ifdef USE_VBO + return; // this <1 +#endif + +#ifndef USE_VBO + listPos = glGenLists(256 + 32); + + Tesselator& t = Tesselator::instance; + for (int i = 0; i < 256; i++) { + glNewList(listPos + i, GL_COMPILE); + // @attn @huge @note: This is some dangerous code right here / Aron, added ^1 + t.begin(); + buildChar(i); + t.end(false, -1); + + glTranslatef2((GLfloat)charWidths[i], 0.0f, 0.0f); + glEndList(); + } + + for (int i = 0; i < 32; i++) { + int br = ((i >> 3) & 1) * 0x55; + int r = ((i >> 2) & 1) * 0xaa + br; + int g = ((i >> 1) & 1) * 0xaa + br; + int b = ((i >> 0) & 1) * 0xaa + br; + if (i == 6) { + r += 0x55; + } + bool darken = i >= 16; + + if (options->anaglyph3d) { + int cr = (r * 30 + g * 59 + b * 11) / 100; + int cg = (r * 30 + g * 70) / (100); + int cb = (r * 30 + b * 70) / (100); + + r = cr; + g = cg; + b = cb; + } + + // color = r << 16 | g << 8 | b; + if (darken) { + r /= 4; + g /= 4; + b /= 4; + } + + glNewList(listPos + 256 + i, GL_COMPILE); + glColor3f(r / 255.0f, g / 255.0f, b / 255.0f); + glEndList(); + } +#endif +} + +void Font::drawShadow( const std::string& str, float x, float y, int color ) +{ + draw(str, x + 1, y + 1, color, true); + draw(str, x, y, color); +} +void Font::drawShadow( const char* str, float x, float y, int color ) +{ + draw(str, x + 1, y + 1, color, true); + draw(str, x, y, color); +} + +void Font::draw( const std::string& str, float x, float y, int color ) +{ + draw(str, x, y, color, false); +} + +void Font::draw( const char* str, float x, float y, int color ) +{ + draw(str, x, y, color, false); +} + +void Font::draw( const char* str, float x, float y, int color, bool darken ) +{ +#ifdef USE_VBO + drawSlow(str, x, y, color, darken); +#endif +} + +void Font::draw( const std::string& str, float x, float y, int color, bool darken ) +{ +#ifdef USE_VBO + drawSlow(str, x, y, color, darken); + return; +#endif + + if (str.empty()) return; + + if (darken) { + int oldAlpha = color & 0xff000000; + color = (color & 0xfcfcfc) >> 2; + color += oldAlpha; + } + + _textures->loadAndBindTexture(fontName); + float r = ((color >> 16) & 0xff) / 255.0f; + float g = ((color >> 8) & 0xff) / 255.0f; + float b = ((color) & 0xff) / 255.0f; + float a = ((color >> 24) & 0xff) / 255.0f; + if (a == 0) a = 1; + glColor4f2(r, g, b, a); + + static const std::string hex("0123456789abcdef"); + + index = 0; + glPushMatrix2(); + glTranslatef2((GLfloat)x, (GLfloat)y, 0.0f); + for (unsigned int i = 0; i < str.length(); i++) { + while (str.length() > i + 1 && str[i] == '\xa7') { + int cc = hex.find((char)tolower(str[i + 1])); + if (cc < 0 || cc > 15) cc = 15; + lists[index++] = listPos + 256 + cc + (darken ? 16 : 0); + + if (index == 1024) { + count = index; + index = 0; +#ifndef USE_VBO + glCallLists(count, GL_UNSIGNED_INT, lists); +#endif + count = 1024; + } + + i += 2; + } + + if (i < str.length()) { + //int ch = SharedConstants.acceptableLetters.indexOf(str.charAt(i)); + char ch = str[i]; + if (ch >= 0) { + //ib.put(listPos + ch + 32); + lists[index++] = listPos + ch; + } + } + + if (index == 1024) { + count = index; + index = 0; +#ifndef USE_VBO + glCallLists(count, GL_UNSIGNED_INT, lists); +#endif + count = 1024; + } + } + count = index; + index = 0; +#ifndef USE_VBO + glCallLists(count, GL_UNSIGNED_INT, lists); +#endif + glPopMatrix2(); +} + +int Font::width( const std::string& str ) +{ + int maxLen = 0; + int len = 0; + + for (unsigned int i = 0; i < str.length(); i++) { + if (str[i] == '\xa7') { + i++; + } else { + //int ch = SharedConstants.acceptableLetters.indexOf(str.charAt(i)); + //if (ch >= 0) { + // len += charWidths[ch + 32]; + //} + if (str[i] == '\n') { + if (len > maxLen) maxLen = len; + len = 0; + } + else { + int charWidth = charWidths[ (unsigned char) str[i] ]; + len += charWidth; + } + } + } + return maxLen>len? maxLen : len; +} + +int Font::height( const std::string& str ) { + int h = 0; + bool hasLine = false; + for (unsigned int i = 0; i < str.length(); ++i) { + if (str[i] == '\n') hasLine = true; + else { + if (hasLine) h += lineHeight; + hasLine = false; + } + } + return h; +} + +std::string Font::sanitize( const std::string& str ) +{ + std::string sanitized(str.length() + 1, 0); + int j = 0; + + for (unsigned int i = 0; i < str.length(); i++) { + if (str[i] == '\xa7') { + i++; + //} else if (SharedConstants.acceptableLetters.indexOf(str.charAt(i)) >= 0) { + } else { + sanitized[j++] = str[i]; + } + } + return sanitized.erase(j); +} + +void Font::drawWordWrap( const std::string& str, float x, float y, float w, int col ) +{ + char* cstr = new char[str.length() + 1]; + strncpy(cstr, str.c_str(), str.length()); + cstr[str.length()] = 0; + + const char* lims = " \n\t\r"; + char* ptok = strtok(cstr, lims); + + std::vector words; + while (ptok != NULL) { + words.push_back( ptok ); + ptok = strtok(NULL, lims); + } + + delete[] cstr; + + int pos = 0; + while (pos < (int)words.size()) { + std::string line = words[pos++] + " "; + while (pos < (int)words.size() && width(line + words[pos]) < w) { + line += words[pos++] + " "; + } + drawShadow(line, x, y, col); + y += lineHeight; + } +} + +void Font::drawSlow( const std::string& str, float x, float y, int color, bool darken /*= false*/ ) { + drawSlow(str.c_str(), x, y, color, darken); +} +void Font::drawSlow( const char* str, float x, float y, int color, bool darken /*= false*/ ) +{ + if (!str) return; + + if (darken) { + int oldAlpha = color & 0xff000000; + color = (color & 0xfcfcfc) >> 2; + color += oldAlpha; + } + + _textures->loadAndBindTexture(fontName); + + Tesselator& t = Tesselator::instance; + t.begin(); + int alpha = (0xff000000 & color) >> 24; + if (!alpha) alpha = 0xff; + t.color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, alpha); + + t.addOffset((float)x, (float)y, 0); + float xOffset = 0; + float yOffset = 0; + + while (unsigned char ch = *(str++)) { + if (ch == '\n') { + xOffset = 0; + yOffset += lineHeight; + } else { + buildChar(ch, xOffset, yOffset); + xOffset += fcharWidths[ch]; + } + } + t.draw(); + t.addOffset(-(float)x, -(float)y, 0); +} + +void Font::buildChar( unsigned char i, float x /*= 0*/, float y /*=0*/ ) +{ + Tesselator& t = Tesselator::instance; + + //i -= _charOffset; + //int ix = (i % _cols) * 8 + _x; + //int iy = (i / _cols) * 8 + _y; + float ix = (float)((i & 15) * 8); + float iy = (float)((i >> 4) * 8); + float s = 7.99f; + + float uo = (0.0f) / 128.0f; + float vo = (0.0f) / 128.0f; + + t.vertexUV(x, y + s, 0, ix / 128.0f + uo, (iy + s) / 128.0f + vo); + t.vertexUV(x + s, y + s, 0, (ix + s) / 128.0f + uo, (iy + s) / 128.0f + vo); + t.vertexUV(x + s, y, 0, (ix + s) / 128.0f + uo, iy / 128.0f + vo); + t.vertexUV(x, y, 0, ix / 128.0f + uo, iy / 128.0f + vo); +} + diff --git a/src/client/gui/Font.h b/src/client/gui/Font.h index 69a0106..620b4b4 100755 --- a/src/client/gui/Font.h +++ b/src/client/gui/Font.h @@ -1,63 +1,63 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__Font_H__ -#define NET_MINECRAFT_CLIENT_GUI__Font_H__ - -//package net.minecraft.client.gui; - -#include -#include - -#include "../renderer/gles.h" - -class Textures; -class Options; - -class Font -{ -public: - Font(Options* options, const std::string& name, Textures* textures); - //Font(Options* options, const std::string& name, Textures* textures, int imgW, int imgH, int x, int y, int cols, int rows, unsigned char charOffset); - - void init(Options* options); - void onGraphicsReset(); - - void draw(const char* str, float x, float y, int color); - void draw(const std::string& str, float x, float y, int color); - void draw(const char* str, float x, float y, int color, bool darken); - void draw(const std::string& str, float x, float y, int color, bool darken); - void drawShadow(const std::string& str, float x, float y, int color); - void drawShadow(const char* str, float x, float y, int color); - void drawWordWrap(const std::string& str, float x, float y, float w, int col); - - int width(const std::string& str); - int height(const std::string& str); - - static std::string sanitize(const std::string& str); -private: - void buildChar(unsigned char i, float x = 0, float y = 0); - void drawSlow(const std::string& str, float x, float y, int color, bool darken = false); - void drawSlow(const char* str, float x, float y, int color, bool darken = false); -public: - int fontTexture; - int lineHeight; - static const int DefaultLineHeight = 10; -private: - int charWidths[256]; - float fcharWidths[256]; - int listPos; - - int index; - int count; - GLuint lists[1024]; - - std::string fontName; - Textures* _textures; - - Options* options; - - int _x, _y; - int _cols; - int _rows; - unsigned char _charOffset; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI__Font_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__Font_H__ +#define NET_MINECRAFT_CLIENT_GUI__Font_H__ + +//package net.minecraft.client.gui; + +#include +#include + +#include "../renderer/gles.h" + +class Textures; +class Options; + +class Font +{ +public: + Font(Options* options, const std::string& name, Textures* textures); + //Font(Options* options, const std::string& name, Textures* textures, int imgW, int imgH, int x, int y, int cols, int rows, unsigned char charOffset); + + void init(Options* options); + void onGraphicsReset(); + + void draw(const char* str, float x, float y, int color); + void draw(const std::string& str, float x, float y, int color); + void draw(const char* str, float x, float y, int color, bool darken); + void draw(const std::string& str, float x, float y, int color, bool darken); + void drawShadow(const std::string& str, float x, float y, int color); + void drawShadow(const char* str, float x, float y, int color); + void drawWordWrap(const std::string& str, float x, float y, float w, int col); + + int width(const std::string& str); + int height(const std::string& str); + + static std::string sanitize(const std::string& str); +private: + void buildChar(unsigned char i, float x = 0, float y = 0); + void drawSlow(const std::string& str, float x, float y, int color, bool darken = false); + void drawSlow(const char* str, float x, float y, int color, bool darken = false); +public: + int fontTexture; + int lineHeight; + static const int DefaultLineHeight = 10; +private: + int charWidths[256]; + float fcharWidths[256]; + int listPos; + + int index; + int count; + GLuint lists[1024]; + + std::string fontName; + Textures* _textures; + + Options* options; + + int _x, _y; + int _cols; + int _rows; + unsigned char _charOffset; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI__Font_H__*/ diff --git a/src/client/gui/Gui.cpp b/src/client/gui/Gui.cpp index df5284a..ccc9c26 100755 --- a/src/client/gui/Gui.cpp +++ b/src/client/gui/Gui.cpp @@ -1,7 +1,7 @@ #include "Gui.h" #include "Font.h" -#include "Options.h" -#include "Keyboard.h" +#include "client/Options.h" +#include "platform/input/Keyboard.h" #include "screens/IngameBlockSelectionScreen.h" #include "screens/ChatScreen.h" #include "screens/ConsoleScreen.h" diff --git a/src/client/gui/Gui.h b/src/client/gui/Gui.h index 1335035..b8beb6a 100755 --- a/src/client/gui/Gui.h +++ b/src/client/gui/Gui.h @@ -1,134 +1,134 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__Gui_H__ -#define NET_MINECRAFT_CLIENT_GUI__Gui_H__ - -//package net.minecraft.client.gui; - -#include "GuiComponent.h" -#include "Font.h" -#include "../player/input/touchscreen/TouchAreaModel.h" -#include "../renderer/RenderChunk.h" -#include "../../util/Random.h" -#include "../IConfigListener.h" - -class Minecraft; -class ItemInstance; -class Textures; -class Tesselator; -struct IntRectangle; - -struct GuiMessage -{ - std::string message; - int ticks; -}; - -typedef std::vector GuiMessageList; - -class Gui: public GuiComponent, IConfigListener -{ -public: - Gui(Minecraft* minecraft); - ~Gui(); - - int getSlotIdAt(int x, int y); - void flashSlot(int slotId); - bool isInside(int x, int y); - RectangleArea getRectangleArea(int extendSide); - void getSlotPos(int slot, int& posX, int& posY); - int getNumSlots(); - - void handleClick(int button, int x, int y); - void handleKeyPressed( int key ); - void scrollChat(int delta); - - void tick(); - void render(float a, bool mouseFree, int xMouse, int yMouse); - - void renderToolBar( float a, int ySlot, const int screenWidth ); - - void renderChatMessages( const int screenHeight, unsigned int max, bool isChatting, Font* font ); - - // draw a string containing simple [color]...[/color] tags; color names are matched - // case-insensitively and default to white. alpha is applied to each segment. - // draw tagged string (ignores simple [color]…[/color] tags) - static void drawColoredString(Font* font, const std::string& text, float x, float y, int alpha); - static float getColoredWidth(Font* font, const std::string& text); - - void renderOnSelectItemNameText( const int screenWidth, Font* font, int ySlot ); - - void renderSleepAnimation( const int screenWidth, const int screenHeight ); - - void renderBubbles(); - void renderHearts(); - void renderDebugInfo(); - void renderPlayerList(Font* font, int screenWidth, int screenHeight); - - void renderProgressIndicator( const bool isTouchInterface, const int screenWidth, const int screenHeight, float a ); - - void addMessage(const std::string& string); - void clearMessages(); - void postError(int errCode); - - void onGraphicsReset(); - void inventoryUpdated(); - - void setNowPlaying(const std::string& string); - void displayClientMessage(const std::string& messageId); - void renderSlotText(const ItemInstance* item, float x, float y, bool hasFinite, bool shadow); - void texturesLoaded( Textures* textures ); - - void onConfigChanged(const Config& config); - void onLevelGenerated(); - - void setScissorRect(const IntRectangle& rect); - - static float floorAlignToScreenPixel(float); - static int itemCountItoa(char* buf, int count); -private: - void renderVignette(float br, int w, int h); - void renderSlot(int slot, int x, int y, float a); - void tickItemDrop(); - float cubeSmoothStep(float percentage, float min, float max); -public: - float progress; - std::string selectedName; - static float InvGuiScale; - static float GuiScale; - -private: - int MAX_MESSAGE_WIDTH; - //ItemRenderer itemRenderer; - GuiMessageList guiMessages; - int chatScrollOffset; - Random random; - - Minecraft* minecraft; - int tickCount; - float itemNameOverlayTime; - std::string overlayMessageString; - int overlayMessageTime; - bool animateOverlayMessageColor; - - float tbr; - - RenderChunk _inventoryRc; - bool _inventoryNeedsUpdate; - - int _flashSlotId; - float _flashSlotStartTime; - - Font* _slotFont; - int _numSlots; - - RenderChunk rcFeedbackOuter; - RenderChunk rcFeedbackInner; - - // For dropping - static const float DropTicks; - float _currentDropTicks; - int _currentDropSlot; - - bool _openInventorySlot; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI__Gui_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__Gui_H__ +#define NET_MINECRAFT_CLIENT_GUI__Gui_H__ + +//package net.minecraft.client.gui; + +#include "GuiComponent.h" +#include "Font.h" +#include "../player/input/touchscreen/TouchAreaModel.h" +#include "../renderer/RenderChunk.h" +#include "../../util/Random.h" +#include "../IConfigListener.h" + +class Minecraft; +class ItemInstance; +class Textures; +class Tesselator; +struct IntRectangle; + +struct GuiMessage +{ + std::string message; + int ticks; +}; + +typedef std::vector GuiMessageList; + +class Gui: public GuiComponent, IConfigListener +{ +public: + Gui(Minecraft* minecraft); + ~Gui(); + + int getSlotIdAt(int x, int y); + void flashSlot(int slotId); + bool isInside(int x, int y); + RectangleArea getRectangleArea(int extendSide); + void getSlotPos(int slot, int& posX, int& posY); + int getNumSlots(); + + void handleClick(int button, int x, int y); + void handleKeyPressed( int key ); + void scrollChat(int delta); + + void tick(); + void render(float a, bool mouseFree, int xMouse, int yMouse); + + void renderToolBar( float a, int ySlot, const int screenWidth ); + + void renderChatMessages( const int screenHeight, unsigned int max, bool isChatting, Font* font ); + + // draw a string containing simple [color]...[/color] tags; color names are matched + // case-insensitively and default to white. alpha is applied to each segment. + // draw tagged string (ignores simple [color]…[/color] tags) + static void drawColoredString(Font* font, const std::string& text, float x, float y, int alpha); + static float getColoredWidth(Font* font, const std::string& text); + + void renderOnSelectItemNameText( const int screenWidth, Font* font, int ySlot ); + + void renderSleepAnimation( const int screenWidth, const int screenHeight ); + + void renderBubbles(); + void renderHearts(); + void renderDebugInfo(); + void renderPlayerList(Font* font, int screenWidth, int screenHeight); + + void renderProgressIndicator( const bool isTouchInterface, const int screenWidth, const int screenHeight, float a ); + + void addMessage(const std::string& string); + void clearMessages(); + void postError(int errCode); + + void onGraphicsReset(); + void inventoryUpdated(); + + void setNowPlaying(const std::string& string); + void displayClientMessage(const std::string& messageId); + void renderSlotText(const ItemInstance* item, float x, float y, bool hasFinite, bool shadow); + void texturesLoaded( Textures* textures ); + + void onConfigChanged(const Config& config); + void onLevelGenerated(); + + void setScissorRect(const IntRectangle& rect); + + static float floorAlignToScreenPixel(float); + static int itemCountItoa(char* buf, int count); +private: + void renderVignette(float br, int w, int h); + void renderSlot(int slot, int x, int y, float a); + void tickItemDrop(); + float cubeSmoothStep(float percentage, float min, float max); +public: + float progress; + std::string selectedName; + static float InvGuiScale; + static float GuiScale; + +private: + int MAX_MESSAGE_WIDTH; + //ItemRenderer itemRenderer; + GuiMessageList guiMessages; + int chatScrollOffset; + Random random; + + Minecraft* minecraft; + int tickCount; + float itemNameOverlayTime; + std::string overlayMessageString; + int overlayMessageTime; + bool animateOverlayMessageColor; + + float tbr; + + RenderChunk _inventoryRc; + bool _inventoryNeedsUpdate; + + int _flashSlotId; + float _flashSlotStartTime; + + Font* _slotFont; + int _numSlots; + + RenderChunk rcFeedbackOuter; + RenderChunk rcFeedbackInner; + + // For dropping + static const float DropTicks; + float _currentDropTicks; + int _currentDropSlot; + + bool _openInventorySlot; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI__Gui_H__*/ diff --git a/src/client/gui/GuiComponent.cpp b/src/client/gui/GuiComponent.cpp index 884c96a..925407c 100755 --- a/src/client/gui/GuiComponent.cpp +++ b/src/client/gui/GuiComponent.cpp @@ -1,156 +1,156 @@ -#include "GuiComponent.h" - -#include "../renderer/Tesselator.h" -#include "../renderer/gles.h" -#include "Font.h" - - -GuiComponent::GuiComponent() -: blitOffset(0) -{ -} - -GuiComponent::~GuiComponent() -{ -} - -void GuiComponent::drawCenteredString( Font* font, const std::string& str, int x, int y, int color ) -{ - font->drawShadow(str, (float)(x - font->width(str) / 2), (float)(y - font->height(str) / 2), color); -} - -void GuiComponent::drawString( Font* font, const std::string& str, int x, int y, int color ) -{ - font->drawShadow(str, (float)x, (float)y /*- font->height(str)/2*/, color); -} - -void GuiComponent::blit( int x, int y, int sx, int sy, int w, int h, int sw/*=0*/, int sh/*=0*/ ) -{ - if (!sw) sw = w; - if (!sh) sh = h; - float us = 1 / 256.0f; - float vs = 1 / 256.0f; - Tesselator& t = Tesselator::instance; - t.begin(); - t.vertexUV((float)(x) , (float)(y + h), blitOffset, (float)(sx ) * us, (float)(sy + sh) * vs); - t.vertexUV((float)(x + w), (float)(y + h), blitOffset, (float)(sx + sw) * us, (float)(sy + sh) * vs); - t.vertexUV((float)(x + w), (float)(y) , blitOffset, (float)(sx + sw) * us, (float)(sy ) * vs); - t.vertexUV((float)(x) , (float)(y) , blitOffset, (float)(sx ) * us, (float)(sy ) * vs); - t.draw(); -} -void GuiComponent::blit( float x, float y, int sx, int sy, float w, float h, int sw/*=0*/, int sh/*=0*/ ) -{ - if (!sw) sw = (int)w; - if (!sh) sh = (int)h; - float us = 1 / 256.0f; - float vs = 1 / 256.0f; - Tesselator& t = Tesselator::instance; - t.begin(); - t.vertexUV(x , y + h, blitOffset, (float)(sx ) * us, (float)(sy + sh) * vs); - t.vertexUV(x + w, y + h, blitOffset, (float)(sx + sw) * us, (float)(sy + sh) * vs); - t.vertexUV(x + w, y , blitOffset, (float)(sx + sw) * us, (float)(sy ) * vs); - t.vertexUV(x , y , blitOffset, (float)(sx ) * us, (float)(sy ) * vs); - t.draw(); -} - -void GuiComponent::fill( int x0, int y0, int x1, int y1, int col ) { - fill((float)x0, (float)y0, (float)x1, (float)y1, col); -} -void GuiComponent::fill( float x0, float y0, float x1, float y1, int col ) -{ - //float a = ((col >> 24) & 0xff) / 255.0f; - //float r = ((col >> 16) & 0xff) / 255.0f; - //float g = ((col >> 8) & 0xff) / 255.0f; - //float b = ((col) & 0xff) / 255.0f; - //glColor4f2(r, g, b, a); - - Tesselator& t = Tesselator::instance; - glEnable2(GL_BLEND); - glDisable2(GL_TEXTURE_2D); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - //LOGI("col: %f, %f, %f, %f\n", r, g, b, a); - t.begin(); - const int color = (col&0xff00ff00) | ((col&0xff0000) >> 16) | ((col&0xff) << 16); - t.colorABGR(color); - t.vertex(x0, y1, 0); - t.vertex(x1, y1, 0); - t.vertex(x1, y0, 0); - t.vertex(x0, y0, 0); - t.draw(); - glEnable2(GL_TEXTURE_2D); - glDisable2(GL_BLEND); -} - -void GuiComponent::fillGradient( int x0, int y0, int x1, int y1, int col1, int col2 ) { - fillGradient((float)x0, (float)y0, (float)x1, (float)y1, col1, col2); -} -void GuiComponent::fillGradient( float x0, float y0, float x1, float y1, int col1, int col2 ) -{ - float a1 = ((col1 >> 24) & 0xff) / 255.0f; - float r1 = ((col1 >> 16) & 0xff) / 255.0f; - float g1 = ((col1 >> 8) & 0xff) / 255.0f; - float b1 = ((col1) & 0xff) / 255.0f; - - float a2 = ((col2 >> 24) & 0xff) / 255.0f; - float r2 = ((col2 >> 16) & 0xff) / 255.0f; - float g2 = ((col2 >> 8) & 0xff) / 255.0f; - float b2 = ((col2) & 0xff) / 255.0f; - glDisable2(GL_TEXTURE_2D); - glEnable2(GL_BLEND); - glDisable2(GL_ALPHA_TEST); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glShadeModel2(GL_SMOOTH); - - Tesselator& t = Tesselator::instance; - t.begin(); - t.color(r1, g1, b1, a1); - t.vertex(x1, y0, 0); - t.vertex(x0, y0, 0); - t.color(r2, g2, b2, a2); - t.vertex(x0, y1, 0); - t.vertex(x1, y1, 0); - t.draw(); - - glShadeModel2(GL_FLAT); - glDisable2(GL_BLEND); - glEnable2(GL_ALPHA_TEST); - glEnable2(GL_TEXTURE_2D); -} -void GuiComponent::fillHorizontalGradient( int x0, int y0, int x1, int y1, int col1, int col2 ) { - fillHorizontalGradient((float)x0, (float)y0, (float)x1, (float)y1, col1, col2); -} -void GuiComponent::fillHorizontalGradient( float x0, float y0, float x1, float y1, int col1, int col2 ) -{ - float a1 = ((col1 >> 24) & 0xff) / 255.0f; - float r1 = ((col1 >> 16) & 0xff) / 255.0f; - float g1 = ((col1 >> 8) & 0xff) / 255.0f; - float b1 = ((col1) & 0xff) / 255.0f; - - float a2 = ((col2 >> 24) & 0xff) / 255.0f; - float r2 = ((col2 >> 16) & 0xff) / 255.0f; - float g2 = ((col2 >> 8) & 0xff) / 255.0f; - float b2 = ((col2) & 0xff) / 255.0f; - glDisable2(GL_TEXTURE_2D); - glEnable2(GL_BLEND); - glDisable2(GL_ALPHA_TEST); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glShadeModel2(GL_SMOOTH); - - Tesselator& t = Tesselator::instance; - t.begin(); - t.color(r2, g2, b2, a2); - t.vertex(x1, y0, 0); - t.color(r1, g1, b1, a1); - t.vertex(x0, y0, 0); - t.color(r1, g1, b1, a1); - t.vertex(x0, y1, 0); - t.color(r2, g2, b2, a2); - t.vertex(x1, y1, 0); - t.draw(); - - glShadeModel2(GL_FLAT); - glDisable2(GL_BLEND); - glEnable2(GL_ALPHA_TEST); - glEnable2(GL_TEXTURE_2D); -} +#include "GuiComponent.h" + +#include "../renderer/Tesselator.h" +#include "../renderer/gles.h" +#include "Font.h" + + +GuiComponent::GuiComponent() +: blitOffset(0) +{ +} + +GuiComponent::~GuiComponent() +{ +} + +void GuiComponent::drawCenteredString( Font* font, const std::string& str, int x, int y, int color ) +{ + font->drawShadow(str, (float)(x - font->width(str) / 2), (float)(y - font->height(str) / 2), color); +} + +void GuiComponent::drawString( Font* font, const std::string& str, int x, int y, int color ) +{ + font->drawShadow(str, (float)x, (float)y /*- font->height(str)/2*/, color); +} + +void GuiComponent::blit( int x, int y, int sx, int sy, int w, int h, int sw/*=0*/, int sh/*=0*/ ) +{ + if (!sw) sw = w; + if (!sh) sh = h; + float us = 1 / 256.0f; + float vs = 1 / 256.0f; + Tesselator& t = Tesselator::instance; + t.begin(); + t.vertexUV((float)(x) , (float)(y + h), blitOffset, (float)(sx ) * us, (float)(sy + sh) * vs); + t.vertexUV((float)(x + w), (float)(y + h), blitOffset, (float)(sx + sw) * us, (float)(sy + sh) * vs); + t.vertexUV((float)(x + w), (float)(y) , blitOffset, (float)(sx + sw) * us, (float)(sy ) * vs); + t.vertexUV((float)(x) , (float)(y) , blitOffset, (float)(sx ) * us, (float)(sy ) * vs); + t.draw(); +} +void GuiComponent::blit( float x, float y, int sx, int sy, float w, float h, int sw/*=0*/, int sh/*=0*/ ) +{ + if (!sw) sw = (int)w; + if (!sh) sh = (int)h; + float us = 1 / 256.0f; + float vs = 1 / 256.0f; + Tesselator& t = Tesselator::instance; + t.begin(); + t.vertexUV(x , y + h, blitOffset, (float)(sx ) * us, (float)(sy + sh) * vs); + t.vertexUV(x + w, y + h, blitOffset, (float)(sx + sw) * us, (float)(sy + sh) * vs); + t.vertexUV(x + w, y , blitOffset, (float)(sx + sw) * us, (float)(sy ) * vs); + t.vertexUV(x , y , blitOffset, (float)(sx ) * us, (float)(sy ) * vs); + t.draw(); +} + +void GuiComponent::fill( int x0, int y0, int x1, int y1, int col ) { + fill((float)x0, (float)y0, (float)x1, (float)y1, col); +} +void GuiComponent::fill( float x0, float y0, float x1, float y1, int col ) +{ + //float a = ((col >> 24) & 0xff) / 255.0f; + //float r = ((col >> 16) & 0xff) / 255.0f; + //float g = ((col >> 8) & 0xff) / 255.0f; + //float b = ((col) & 0xff) / 255.0f; + //glColor4f2(r, g, b, a); + + Tesselator& t = Tesselator::instance; + glEnable2(GL_BLEND); + glDisable2(GL_TEXTURE_2D); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //LOGI("col: %f, %f, %f, %f\n", r, g, b, a); + t.begin(); + const int color = (col&0xff00ff00) | ((col&0xff0000) >> 16) | ((col&0xff) << 16); + t.colorABGR(color); + t.vertex(x0, y1, 0); + t.vertex(x1, y1, 0); + t.vertex(x1, y0, 0); + t.vertex(x0, y0, 0); + t.draw(); + glEnable2(GL_TEXTURE_2D); + glDisable2(GL_BLEND); +} + +void GuiComponent::fillGradient( int x0, int y0, int x1, int y1, int col1, int col2 ) { + fillGradient((float)x0, (float)y0, (float)x1, (float)y1, col1, col2); +} +void GuiComponent::fillGradient( float x0, float y0, float x1, float y1, int col1, int col2 ) +{ + float a1 = ((col1 >> 24) & 0xff) / 255.0f; + float r1 = ((col1 >> 16) & 0xff) / 255.0f; + float g1 = ((col1 >> 8) & 0xff) / 255.0f; + float b1 = ((col1) & 0xff) / 255.0f; + + float a2 = ((col2 >> 24) & 0xff) / 255.0f; + float r2 = ((col2 >> 16) & 0xff) / 255.0f; + float g2 = ((col2 >> 8) & 0xff) / 255.0f; + float b2 = ((col2) & 0xff) / 255.0f; + glDisable2(GL_TEXTURE_2D); + glEnable2(GL_BLEND); + glDisable2(GL_ALPHA_TEST); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glShadeModel2(GL_SMOOTH); + + Tesselator& t = Tesselator::instance; + t.begin(); + t.color(r1, g1, b1, a1); + t.vertex(x1, y0, 0); + t.vertex(x0, y0, 0); + t.color(r2, g2, b2, a2); + t.vertex(x0, y1, 0); + t.vertex(x1, y1, 0); + t.draw(); + + glShadeModel2(GL_FLAT); + glDisable2(GL_BLEND); + glEnable2(GL_ALPHA_TEST); + glEnable2(GL_TEXTURE_2D); +} +void GuiComponent::fillHorizontalGradient( int x0, int y0, int x1, int y1, int col1, int col2 ) { + fillHorizontalGradient((float)x0, (float)y0, (float)x1, (float)y1, col1, col2); +} +void GuiComponent::fillHorizontalGradient( float x0, float y0, float x1, float y1, int col1, int col2 ) +{ + float a1 = ((col1 >> 24) & 0xff) / 255.0f; + float r1 = ((col1 >> 16) & 0xff) / 255.0f; + float g1 = ((col1 >> 8) & 0xff) / 255.0f; + float b1 = ((col1) & 0xff) / 255.0f; + + float a2 = ((col2 >> 24) & 0xff) / 255.0f; + float r2 = ((col2 >> 16) & 0xff) / 255.0f; + float g2 = ((col2 >> 8) & 0xff) / 255.0f; + float b2 = ((col2) & 0xff) / 255.0f; + glDisable2(GL_TEXTURE_2D); + glEnable2(GL_BLEND); + glDisable2(GL_ALPHA_TEST); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glShadeModel2(GL_SMOOTH); + + Tesselator& t = Tesselator::instance; + t.begin(); + t.color(r2, g2, b2, a2); + t.vertex(x1, y0, 0); + t.color(r1, g1, b1, a1); + t.vertex(x0, y0, 0); + t.color(r1, g1, b1, a1); + t.vertex(x0, y1, 0); + t.color(r2, g2, b2, a2); + t.vertex(x1, y1, 0); + t.draw(); + + glShadeModel2(GL_FLAT); + glDisable2(GL_BLEND); + glEnable2(GL_ALPHA_TEST); + glEnable2(GL_TEXTURE_2D); +} diff --git a/src/client/gui/GuiComponent.h b/src/client/gui/GuiComponent.h index 1b8c468..90bba93 100755 --- a/src/client/gui/GuiComponent.h +++ b/src/client/gui/GuiComponent.h @@ -1,34 +1,34 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__GuiComponent_H__ -#define NET_MINECRAFT_CLIENT_GUI__GuiComponent_H__ - -//package net.minecraft.client.gui; - -#include -class Font; -class Minecraft; - -class GuiComponent -{ -public: - GuiComponent(); - virtual ~GuiComponent(); - - void drawString(Font* font, const std::string& str, int x, int y, int color); - void drawCenteredString(Font* font, const std::string& str, int x, int y, int color); - - void blit(int x, int y, int sx, int sy, int w, int h, int sw=0, int sh=0); - void blit(float x, float y, int sx, int sy, float w, float h, int sw=0, int sh=0); - -protected: - void fill(int x0, int y0, int x1, int y1, int col); - void fill(float x0, float y0, float x1, float y1, int col); - void fillGradient(int x0, int y0, int x1, int y1, int col1, int col2); - void fillGradient(float x0, float y0, float x1, float y1, int col1, int col2); - void fillHorizontalGradient(int x0, int y0, int x1, int y1, int col1, int col2); - void fillHorizontalGradient(float x0, float y0, float x1, float y1, int col1, int col2); - - float blitOffset; - -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI__GuiComponent_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__GuiComponent_H__ +#define NET_MINECRAFT_CLIENT_GUI__GuiComponent_H__ + +//package net.minecraft.client.gui; + +#include +class Font; +class Minecraft; + +class GuiComponent +{ +public: + GuiComponent(); + virtual ~GuiComponent(); + + void drawString(Font* font, const std::string& str, int x, int y, int color); + void drawCenteredString(Font* font, const std::string& str, int x, int y, int color); + + void blit(int x, int y, int sx, int sy, int w, int h, int sw=0, int sh=0); + void blit(float x, float y, int sx, int sy, float w, float h, int sw=0, int sh=0); + +protected: + void fill(int x0, int y0, int x1, int y1, int col); + void fill(float x0, float y0, float x1, float y1, int col); + void fillGradient(int x0, int y0, int x1, int y1, int col1, int col2); + void fillGradient(float x0, float y0, float x1, float y1, int col1, int col2); + void fillHorizontalGradient(int x0, int y0, int x1, int y1, int col1, int col2); + void fillHorizontalGradient(float x0, float y0, float x1, float y1, int col1, int col2); + + float blitOffset; + +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI__GuiComponent_H__*/ diff --git a/src/client/gui/Screen.cpp b/src/client/gui/Screen.cpp index a6bcc6f..fc0ae28 100755 --- a/src/client/gui/Screen.cpp +++ b/src/client/gui/Screen.cpp @@ -1,291 +1,291 @@ -#include "Screen.h" -#include "components/Button.h" -#include "components/TextBox.h" -#include "../Minecraft.h" -#include "../renderer/Tesselator.h" -#include "../sound/SoundEngine.h" -#include "../../platform/input/Keyboard.h" -#include "../../platform/input/Mouse.h" -#include "../renderer/Textures.h" - -Screen::Screen() -: passEvents(false), - clickedButton(NULL), - tabButtonIndex(0), - width(1), - height(1), - minecraft(NULL), - font(NULL) -{ -} - -void Screen::render( int xm, int ym, float a ) -{ - for (unsigned int i = 0; i < buttons.size(); i++) { - Button* button = buttons[i]; - button->render(minecraft, xm, ym); - } - - // render any text boxes after buttons - for (unsigned int i = 0; i < textBoxes.size(); i++) { - TextBox* textbox = textBoxes[i]; - textbox->render(minecraft, xm, ym); - } -} - -void Screen::init( Minecraft* minecraft, int width, int height ) -{ - //particles = /*new*/ GuiParticles(minecraft); - this->minecraft = minecraft; - this->font = minecraft->font; - this->width = width; - this->height = height; - init(); - setupPositions(); - updateTabButtonSelection(); -} - -void Screen::init() -{ -} - -void Screen::setSize( int width, int height ) -{ - this->width = width; - this->height = height; - setupPositions(); -} - -bool Screen::handleBackEvent( bool isDown ) -{ - return false; -} - -void Screen::updateEvents() -{ - if (passEvents) - return; - - while (Mouse::next()) - mouseEvent(); - - while (Keyboard::next()) - keyboardEvent(); - while (Keyboard::nextTextChar()) - keyboardTextEvent(); -} - -void Screen::mouseEvent() -{ - const MouseAction& e = Mouse::getEvent(); - // forward wheel events to subclasses - if (e.action == MouseAction::ACTION_WHEEL) { - int xm = e.x * width / minecraft->width; - int ym = e.y * height / minecraft->height - 1; - mouseWheel(e.dx, e.dy, xm, ym); - return; - } - - if (!e.isButton()) - return; - - if (Mouse::getEventButtonState()) { - int xm = e.x * width / minecraft->width; - int ym = e.y * height / minecraft->height - 1; - mouseClicked(xm, ym, Mouse::getEventButton()); - } else { - int xm = e.x * width / minecraft->width; - int ym = e.y * height / minecraft->height - 1; - mouseReleased(xm, ym, Mouse::getEventButton()); - } -} - -void Screen::keyboardEvent() -{ - if (Keyboard::getEventKeyState()) { - //if (Keyboard.getEventKey() == Keyboard.KEY_F11) { - // minecraft->toggleFullScreen(); - // return; - //} - keyPressed(Keyboard::getEventKey()); - } -} -void Screen::keyboardTextEvent() -{ - charPressed(Keyboard::getChar()); -} -void Screen::renderBackground() -{ - renderBackground(0); -} - -void Screen::renderBackground( int vo ) -{ - if (minecraft->isLevelGenerated()) { - fillGradient(0, 0, width, height, 0xc0101010, 0xd0101010); - } else { - renderDirtBackground(vo); - } -} - -void Screen::renderDirtBackground( int vo ) -{ - //glDisable2(GL_LIGHTING); - glDisable2(GL_FOG); - Tesselator& t = Tesselator::instance; - minecraft->textures->loadAndBindTexture("gui/background.png"); - glColor4f2(1, 1, 1, 1); - float s = 32; - float fvo = (float) vo; - t.begin(); - t.color(0x404040); - t.vertexUV(0, (float)height, 0, 0, height / s + fvo); - t.vertexUV((float)width, (float)height, 0, width / s, (float)height / s + fvo); - t.vertexUV((float)width, 0, 0, (float)width / s, 0 + fvo); - t.vertexUV(0, 0, 0, 0, 0 + fvo); - t.draw(); -} - -bool Screen::isPauseScreen() -{ - return true; -} - -bool Screen::isErrorScreen() -{ - return false; -} - -bool Screen::isInGameScreen() -{ - return true; -} - -bool Screen::closeOnPlayerHurt() { - return false; -} - -void Screen::keyPressed( int eventKey ) -{ - if (eventKey == Keyboard::KEY_ESCAPE) { - minecraft->setScreen(NULL); - //minecraft->grabMouse(); - } - - // pass key events to any text boxes first - for (auto& textbox : textBoxes) { - textbox->keyPressed(minecraft, eventKey); - } - -#ifdef TABBING - if (minecraft->useTouchscreen()) - return; - - - // "Tabbing" the buttons (walking with keys) - const int tabButtonCount = tabButtons.size(); - if (!tabButtonCount) - return; - - Options& o = minecraft->options; - if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_NEXT)) - if (++tabButtonIndex == tabButtonCount) tabButtonIndex = 0; - if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_PREV)) - if (--tabButtonIndex == -1) tabButtonIndex = tabButtonCount-1; - if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_OK)) { - Button* button = tabButtons[tabButtonIndex]; - if (button->active) { - minecraft->soundEngine->playUI("random.click", 1, 1); - buttonClicked(button); - } - } - - updateTabButtonSelection(); -#endif -} - -void Screen::charPressed(char inputChar) { - for (auto& textbox : textBoxes) { - textbox->charPressed(minecraft, inputChar); - } -} - -void Screen::updateTabButtonSelection() -{ -#ifdef TABBING - if (minecraft->useTouchscreen()) - return; - - for (unsigned int i = 0; i < tabButtons.size(); ++i) - tabButtons[i]->selected = (i == tabButtonIndex); -#endif -} - -void Screen::mouseClicked( int x, int y, int buttonNum ) -{ - if (buttonNum == MouseAction::ACTION_LEFT) { - for (unsigned int i = 0; i < buttons.size(); ++i) { - Button* button = buttons[i]; - //LOGI("Hit-testing button: %p\n", button); - if (button->clicked(minecraft, x, y)) { - button->setPressed(); - - //LOGI("Hit-test successful: %p\n", button); - clickedButton = button; -/* -#if !defined(ANDROID) && !defined(__APPLE__) //if (!minecraft->isTouchscreen()) { - minecraft->soundEngine->playUI("random.click", 1, 1); - buttonClicked(button); -#endif } -*/ - } - } - } - - // let textboxes see the click regardless - for (auto& textbox : textBoxes) { - textbox->mouseClicked(minecraft, x, y, buttonNum); - } -} - -void Screen::mouseReleased( int x, int y, int buttonNum ) -{ - //LOGI("b_id: %d, (%p), text: %s\n", buttonNum, clickedButton, clickedButton?clickedButton->msg.c_str():""); - if (!clickedButton || buttonNum != MouseAction::ACTION_LEFT) return; - -#if 1 -//#if defined(ANDROID) || defined(__APPLE__) //if (minecraft->isTouchscreen()) { - for (unsigned int i = 0; i < buttons.size(); ++i) { - Button* button = buttons[i]; - if (clickedButton == button && button->clicked(minecraft, x, y)) { - buttonClicked(button); - minecraft->soundEngine->playUI("random.click", 1, 1); - clickedButton->released(x, y); - } - } -# else // } else { - clickedButton->released(x, y); -#endif // } - clickedButton = NULL; -} - -bool Screen::renderGameBehind() { - return true; -} - -bool Screen::hasClippingArea( IntRectangle& out ) -{ - return false; -} - -void Screen::lostFocus() { - for(std::vector::iterator it = textBoxes.begin(); it != textBoxes.end(); ++it) { - TextBox* tb = *it; - tb->loseFocus(minecraft); - } -} - -void Screen::toGUICoordinate( int& x, int& y ) { - x = x * width / minecraft->width; - y = y * height / minecraft->height - 1; -} +#include "Screen.h" +#include "components/Button.h" +#include "components/TextBox.h" +#include "../Minecraft.h" +#include "../renderer/Tesselator.h" +#include "../sound/SoundEngine.h" +#include "../../platform/input/Keyboard.h" +#include "../../platform/input/Mouse.h" +#include "../renderer/Textures.h" + +Screen::Screen() +: passEvents(false), + clickedButton(NULL), + tabButtonIndex(0), + width(1), + height(1), + minecraft(NULL), + font(NULL) +{ +} + +void Screen::render( int xm, int ym, float a ) +{ + for (unsigned int i = 0; i < buttons.size(); i++) { + Button* button = buttons[i]; + button->render(minecraft, xm, ym); + } + + // render any text boxes after buttons + for (unsigned int i = 0; i < textBoxes.size(); i++) { + TextBox* textbox = textBoxes[i]; + textbox->render(minecraft, xm, ym); + } +} + +void Screen::init( Minecraft* minecraft, int width, int height ) +{ + //particles = /*new*/ GuiParticles(minecraft); + this->minecraft = minecraft; + this->font = minecraft->font; + this->width = width; + this->height = height; + init(); + setupPositions(); + updateTabButtonSelection(); +} + +void Screen::init() +{ +} + +void Screen::setSize( int width, int height ) +{ + this->width = width; + this->height = height; + setupPositions(); +} + +bool Screen::handleBackEvent( bool isDown ) +{ + return false; +} + +void Screen::updateEvents() +{ + if (passEvents) + return; + + while (Mouse::next()) + mouseEvent(); + + while (Keyboard::next()) + keyboardEvent(); + while (Keyboard::nextTextChar()) + keyboardTextEvent(); +} + +void Screen::mouseEvent() +{ + const MouseAction& e = Mouse::getEvent(); + // forward wheel events to subclasses + if (e.action == MouseAction::ACTION_WHEEL) { + int xm = e.x * width / minecraft->width; + int ym = e.y * height / minecraft->height - 1; + mouseWheel(e.dx, e.dy, xm, ym); + return; + } + + if (!e.isButton()) + return; + + if (Mouse::getEventButtonState()) { + int xm = e.x * width / minecraft->width; + int ym = e.y * height / minecraft->height - 1; + mouseClicked(xm, ym, Mouse::getEventButton()); + } else { + int xm = e.x * width / minecraft->width; + int ym = e.y * height / minecraft->height - 1; + mouseReleased(xm, ym, Mouse::getEventButton()); + } +} + +void Screen::keyboardEvent() +{ + if (Keyboard::getEventKeyState()) { + //if (Keyboard.getEventKey() == Keyboard.KEY_F11) { + // minecraft->toggleFullScreen(); + // return; + //} + keyPressed(Keyboard::getEventKey()); + } +} +void Screen::keyboardTextEvent() +{ + charPressed(Keyboard::getChar()); +} +void Screen::renderBackground() +{ + renderBackground(0); +} + +void Screen::renderBackground( int vo ) +{ + if (minecraft->isLevelGenerated()) { + fillGradient(0, 0, width, height, 0xc0101010, 0xd0101010); + } else { + renderDirtBackground(vo); + } +} + +void Screen::renderDirtBackground( int vo ) +{ + //glDisable2(GL_LIGHTING); + glDisable2(GL_FOG); + Tesselator& t = Tesselator::instance; + minecraft->textures->loadAndBindTexture("gui/background.png"); + glColor4f2(1, 1, 1, 1); + float s = 32; + float fvo = (float) vo; + t.begin(); + t.color(0x404040); + t.vertexUV(0, (float)height, 0, 0, height / s + fvo); + t.vertexUV((float)width, (float)height, 0, width / s, (float)height / s + fvo); + t.vertexUV((float)width, 0, 0, (float)width / s, 0 + fvo); + t.vertexUV(0, 0, 0, 0, 0 + fvo); + t.draw(); +} + +bool Screen::isPauseScreen() +{ + return true; +} + +bool Screen::isErrorScreen() +{ + return false; +} + +bool Screen::isInGameScreen() +{ + return true; +} + +bool Screen::closeOnPlayerHurt() { + return false; +} + +void Screen::keyPressed( int eventKey ) +{ + if (eventKey == Keyboard::KEY_ESCAPE) { + minecraft->setScreen(NULL); + //minecraft->grabMouse(); + } + + // pass key events to any text boxes first + for (auto& textbox : textBoxes) { + textbox->keyPressed(minecraft, eventKey); + } + +#ifdef TABBING + if (minecraft->useTouchscreen()) + return; + + + // "Tabbing" the buttons (walking with keys) + const int tabButtonCount = tabButtons.size(); + if (!tabButtonCount) + return; + + Options& o = minecraft->options; + if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_NEXT)) + if (++tabButtonIndex == tabButtonCount) tabButtonIndex = 0; + if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_PREV)) + if (--tabButtonIndex == -1) tabButtonIndex = tabButtonCount-1; + if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_OK)) { + Button* button = tabButtons[tabButtonIndex]; + if (button->active) { + minecraft->soundEngine->playUI("random.click", 1, 1); + buttonClicked(button); + } + } + + updateTabButtonSelection(); +#endif +} + +void Screen::charPressed(char inputChar) { + for (auto& textbox : textBoxes) { + textbox->charPressed(minecraft, inputChar); + } +} + +void Screen::updateTabButtonSelection() +{ +#ifdef TABBING + if (minecraft->useTouchscreen()) + return; + + for (unsigned int i = 0; i < tabButtons.size(); ++i) + tabButtons[i]->selected = (i == tabButtonIndex); +#endif +} + +void Screen::mouseClicked( int x, int y, int buttonNum ) +{ + if (buttonNum == MouseAction::ACTION_LEFT) { + for (unsigned int i = 0; i < buttons.size(); ++i) { + Button* button = buttons[i]; + //LOGI("Hit-testing button: %p\n", button); + if (button->clicked(minecraft, x, y)) { + button->setPressed(); + + //LOGI("Hit-test successful: %p\n", button); + clickedButton = button; +/* +#if !defined(ANDROID) && !defined(__APPLE__) //if (!minecraft->isTouchscreen()) { + minecraft->soundEngine->playUI("random.click", 1, 1); + buttonClicked(button); +#endif } +*/ + } + } + } + + // let textboxes see the click regardless + for (auto& textbox : textBoxes) { + textbox->mouseClicked(minecraft, x, y, buttonNum); + } +} + +void Screen::mouseReleased( int x, int y, int buttonNum ) +{ + //LOGI("b_id: %d, (%p), text: %s\n", buttonNum, clickedButton, clickedButton?clickedButton->msg.c_str():""); + if (!clickedButton || buttonNum != MouseAction::ACTION_LEFT) return; + +#if 1 +//#if defined(ANDROID) || defined(__APPLE__) //if (minecraft->isTouchscreen()) { + for (unsigned int i = 0; i < buttons.size(); ++i) { + Button* button = buttons[i]; + if (clickedButton == button && button->clicked(minecraft, x, y)) { + buttonClicked(button); + minecraft->soundEngine->playUI("random.click", 1, 1); + clickedButton->released(x, y); + } + } +# else // } else { + clickedButton->released(x, y); +#endif // } + clickedButton = NULL; +} + +bool Screen::renderGameBehind() { + return true; +} + +bool Screen::hasClippingArea( IntRectangle& out ) +{ + return false; +} + +void Screen::lostFocus() { + for(std::vector::iterator it = textBoxes.begin(); it != textBoxes.end(); ++it) { + TextBox* tb = *it; + tb->loseFocus(minecraft); + } +} + +void Screen::toGUICoordinate( int& x, int& y ) { + x = x * width / minecraft->width; + y = y * height / minecraft->height - 1; +} diff --git a/src/client/gui/Screen.h b/src/client/gui/Screen.h index be44d5c..0fa6430 100755 --- a/src/client/gui/Screen.h +++ b/src/client/gui/Screen.h @@ -1,83 +1,83 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__Screen_H__ -#define NET_MINECRAFT_CLIENT_GUI__Screen_H__ - -//package net.minecraft.client.gui; - -#include -#include "GuiComponent.h" - -class Font; -class Minecraft; -class Button; -class TextBox; -struct IntRectangle; - -class Screen: public GuiComponent -{ -public: - Screen(); - - virtual void render(int xm, int ym, float a); - - void init(Minecraft* minecraft, int width, int height); - virtual void init(); - - void setSize(int width, int height); - virtual void setupPositions() {}; - - virtual void updateEvents(); - virtual void mouseEvent(); - virtual void keyboardEvent(); - virtual void keyboardTextEvent(); - virtual bool handleBackEvent(bool isDown); - - virtual void tick() {} - - virtual void removed() {} - - virtual void renderBackground(); - virtual void renderBackground(int vo); - virtual void renderDirtBackground(int vo); - // query - virtual bool renderGameBehind(); - virtual bool hasClippingArea(IntRectangle& out); - - virtual bool isPauseScreen(); - virtual bool isErrorScreen(); - virtual bool isInGameScreen(); - virtual bool closeOnPlayerHurt(); - - virtual void confirmResult(bool result, int id) {} - virtual void lostFocus(); - virtual void toGUICoordinate(int& x, int& y); -protected: - void updateTabButtonSelection(); - - virtual void buttonClicked(Button* button) {} - virtual void mouseClicked(int x, int y, int buttonNum); - virtual void mouseReleased(int x, int y, int buttonNum); - - // mouse wheel movement (dx/dy are wheel deltas, xm/ym are GUI coords) - virtual void mouseWheel(int dx, int dy, int xm, int ym) {} - - virtual void keyPressed(int eventKey); - virtual void charPressed(char inputChar); -public: - int width; - int height; - bool passEvents; - //GuiParticles* particles; -protected: - Minecraft* minecraft; - std::vector buttons; - std::vector textBoxes; - - std::vector tabButtons; - int tabButtonIndex; - - Font* font; -private: - Button* clickedButton; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI__Screen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__Screen_H__ +#define NET_MINECRAFT_CLIENT_GUI__Screen_H__ + +//package net.minecraft.client.gui; + +#include +#include "GuiComponent.h" + +class Font; +class Minecraft; +class Button; +class TextBox; +struct IntRectangle; + +class Screen: public GuiComponent +{ +public: + Screen(); + + virtual void render(int xm, int ym, float a); + + void init(Minecraft* minecraft, int width, int height); + virtual void init(); + + void setSize(int width, int height); + virtual void setupPositions() {}; + + virtual void updateEvents(); + virtual void mouseEvent(); + virtual void keyboardEvent(); + virtual void keyboardTextEvent(); + virtual bool handleBackEvent(bool isDown); + + virtual void tick() {} + + virtual void removed() {} + + virtual void renderBackground(); + virtual void renderBackground(int vo); + virtual void renderDirtBackground(int vo); + // query + virtual bool renderGameBehind(); + virtual bool hasClippingArea(IntRectangle& out); + + virtual bool isPauseScreen(); + virtual bool isErrorScreen(); + virtual bool isInGameScreen(); + virtual bool closeOnPlayerHurt(); + + virtual void confirmResult(bool result, int id) {} + virtual void lostFocus(); + virtual void toGUICoordinate(int& x, int& y); +protected: + void updateTabButtonSelection(); + + virtual void buttonClicked(Button* button) {} + virtual void mouseClicked(int x, int y, int buttonNum); + virtual void mouseReleased(int x, int y, int buttonNum); + + // mouse wheel movement (dx/dy are wheel deltas, xm/ym are GUI coords) + virtual void mouseWheel(int dx, int dy, int xm, int ym) {} + + virtual void keyPressed(int eventKey); + virtual void charPressed(char inputChar); +public: + int width; + int height; + bool passEvents; + //GuiParticles* particles; +protected: + Minecraft* minecraft; + std::vector buttons; + std::vector textBoxes; + + std::vector tabButtons; + int tabButtonIndex; + + Font* font; +private: + Button* clickedButton; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI__Screen_H__*/ diff --git a/src/client/gui/TweenData.h b/src/client/gui/TweenData.h index 0d63b8e..b2563a2 100755 --- a/src/client/gui/TweenData.h +++ b/src/client/gui/TweenData.h @@ -1,11 +1,11 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__TweenData_H__ -#define NET_MINECRAFT_CLIENT_GUI__TweenData_H__ - -typedef struct TweenData { - float cur; - float dur; - float start; - float stop; -} TweenData; - -#endif /*NET_MINECRAFT_CLIENT_GUI__TweenData_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__TweenData_H__ +#define NET_MINECRAFT_CLIENT_GUI__TweenData_H__ + +typedef struct TweenData { + float cur; + float dur; + float start; + float stop; +} TweenData; + +#endif /*NET_MINECRAFT_CLIENT_GUI__TweenData_H__*/ diff --git a/src/client/gui/components/Button.cpp b/src/client/gui/components/Button.cpp index 39022a7..6f69311 100755 --- a/src/client/gui/components/Button.cpp +++ b/src/client/gui/components/Button.cpp @@ -1,219 +1,219 @@ -#include "Button.h" -#include "../../Minecraft.h" -#include "../../renderer/Textures.h" - -Button::Button(int id, const std::string& msg) -: GuiElement(true, true, 0, 0, 200, 24), - id(id), - msg(msg), - selected(false), - _currentlyDown(false) -{ -} - -Button::Button( int id, int x, int y, const std::string& msg ) -: GuiElement(true, true, x, y, 200, 24), - id(id), - msg(msg), - selected(false), - _currentlyDown(false) -{ -} - -Button::Button( int id, int x, int y, int w, int h, const std::string& msg ) -: GuiElement(true, true, x, y, w, h), - id(id), - msg(msg), - selected(false), - _currentlyDown(false) -{ -} - -void Button::render( Minecraft* minecraft, int xm, int ym ) -{ - if (!visible) return; - - /* - minecraft->textures->loadAndBindTexture("gui/gui.png"); - glColor4f2(1, 1, 1, 1); - - //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); - int yImage = getYImage(hovered || selected); - - blit(x, y, 0, 46 + yImage * 20, w / 2, h, 0, 20); - blit(x + w / 2, y, 200 - w / 2, 46 + yImage * 20, w / 2, h, 0, 20); - */ - - renderBg(minecraft, xm, ym); - renderFace(minecraft, xm , ym); -} - -void Button::released( int mx, int my ) { - _currentlyDown = false; -} - -bool Button::clicked( Minecraft* minecraft, int mx, int my ) -{ - return active && mx >= x && my >= y && mx < x + width && my < y + height; -} - -void Button::setPressed() { - _currentlyDown = true; -} - -int Button::getYImage( bool hovered ) -{ - int res = 1; - if (!active) res = 0; - else if (hovered) res = 2; - return res; -} - -void Button::renderFace(Minecraft* mc, int xm, int ym) { - Font* font = mc->font; - if (!active) { - drawCenteredString(font, msg, x + width / 2, y + (height - 8) / 2, 0xffa0a0a0); - } else { - if (hovered(mc, xm, ym) || selected) { - drawCenteredString(font, msg, x + width / 2, y + (height - 8) / 2, 0xffffa0); - } else { - drawCenteredString(font, msg, x + width / 2, y + (height - 8) / 2, 0xe0e0e0); - } - } -} - -void Button::renderBg( Minecraft* minecraft, int xm, int ym ) -{ - minecraft->textures->loadAndBindTexture("gui/gui.png"); - glColor4f2(1, 1, 1, 1); - - //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); - int yImage = getYImage(selected || hovered(minecraft, xm, ym));; - - blit(x, y, 0, 46 + yImage * 20, width / 2, height, 0, 20); - blit(x + width / 2, y, 200 - width / 2, 46 + yImage * 20, width / 2, height, 0, 20); -} - -bool Button::hovered(Minecraft* minecraft, int xm , int ym) { - return minecraft->useTouchscreen()? (_currentlyDown && isInside(xm, ym)) : isInside(xm, ym); -} - -bool Button::isInside( int xm, int ym ) { - return xm >= x && ym >= y && xm < x + width && ym < y + height; -} - -// -// BlankButton -// -BlankButton::BlankButton(int id) -: super(id, "") -{ - visible = false; -} - -BlankButton::BlankButton(int id, int x, int y, int w, int h) -: super(id, x, y, w, h, "") -{ - visible = false; -} - -// -// The Touch-interface button -// -namespace Touch { - -TButton::TButton(int id, const std::string& msg) -: super(id, msg) -{ - width = 66; - height = 26; -} - -TButton::TButton( int id, int x, int y, const std::string& msg ) -: super(id, x, y, msg) -{ - width = 66; - height = 26; -} - -TButton::TButton( int id, int x, int y, int w, int h, const std::string& msg ) -: super(id, x, y, w, h, msg) -{ -} - -void TButton::renderBg( Minecraft* minecraft, int xm, int ym ) -{ - bool hovered = active && (minecraft->useTouchscreen()? (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); - // bool hovered = active && (_currentlyDown && isInside(xm, ym)); - - minecraft->textures->loadAndBindTexture("gui/touchgui.png"); - - //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); - if (active) - glColor4f2(1, 1, 1, 1); - else - glColor4f2(0.5f, 0.5f, 0.5f, 1); - - blit(x, y, hovered?66:0, 0, width, height, 66, 26); - //blit(x + w / 2, y, 200 - w / 2, 46 + yImage * 20, w / 2, h, 0, 20); -} - - -// -// Header spacing in Touchscreen mode -// -THeader::THeader(int id, const std::string& msg) -: super(id, msg), - xText(-99999) -{ - active = false; - width = 66; - height = 26; -} - -THeader::THeader( int id, int x, int y, const std::string& msg ) -: super(id, x, y, msg), - xText(-99999) -{ - active = false; - width = 66; - height = 26; -} - -THeader::THeader( int id, int x, int y, int w, int h, const std::string& msg ) -: super(id, x, y, w, h, msg), - xText(-99999) -{ - active = false; -} - -void THeader::render( Minecraft* minecraft, int xm, int ym ) { - Font* font = minecraft->font; - renderBg(minecraft, xm, ym); - - int xx = x + width/2; - if (xText != -99999) - xx = xText; - drawCenteredString(font, msg, xx, y + (height - 8) / 2, 0xe0e0e0); -} - -void THeader::renderBg( Minecraft* minecraft, int xm, int ym ) -{ - minecraft->textures->loadAndBindTexture("gui/touchgui.png"); - - //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); - glColor4f2(1, 1, 1, 1); - - // Left cap - blit(x, y, 150, 26, 2, height-1, 2, 25); - // Middle - blit(x+2, y, 153, 26, width-3, height-1, 8, 25); - // Right cap - blit(x+width-2, y, 162, 26, 2, height-1, 2, 25); - // Shadow - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - blit(x, y+height-1, 153, 52, width, 3, 8, 3); -} - -}; +#include "Button.h" +#include "../../Minecraft.h" +#include "../../renderer/Textures.h" + +Button::Button(int id, const std::string& msg) +: GuiElement(true, true, 0, 0, 200, 24), + id(id), + msg(msg), + selected(false), + _currentlyDown(false) +{ +} + +Button::Button( int id, int x, int y, const std::string& msg ) +: GuiElement(true, true, x, y, 200, 24), + id(id), + msg(msg), + selected(false), + _currentlyDown(false) +{ +} + +Button::Button( int id, int x, int y, int w, int h, const std::string& msg ) +: GuiElement(true, true, x, y, w, h), + id(id), + msg(msg), + selected(false), + _currentlyDown(false) +{ +} + +void Button::render( Minecraft* minecraft, int xm, int ym ) +{ + if (!visible) return; + + /* + minecraft->textures->loadAndBindTexture("gui/gui.png"); + glColor4f2(1, 1, 1, 1); + + //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); + int yImage = getYImage(hovered || selected); + + blit(x, y, 0, 46 + yImage * 20, w / 2, h, 0, 20); + blit(x + w / 2, y, 200 - w / 2, 46 + yImage * 20, w / 2, h, 0, 20); + */ + + renderBg(minecraft, xm, ym); + renderFace(minecraft, xm , ym); +} + +void Button::released( int mx, int my ) { + _currentlyDown = false; +} + +bool Button::clicked( Minecraft* minecraft, int mx, int my ) +{ + return active && mx >= x && my >= y && mx < x + width && my < y + height; +} + +void Button::setPressed() { + _currentlyDown = true; +} + +int Button::getYImage( bool hovered ) +{ + int res = 1; + if (!active) res = 0; + else if (hovered) res = 2; + return res; +} + +void Button::renderFace(Minecraft* mc, int xm, int ym) { + Font* font = mc->font; + if (!active) { + drawCenteredString(font, msg, x + width / 2, y + (height - 8) / 2, 0xffa0a0a0); + } else { + if (hovered(mc, xm, ym) || selected) { + drawCenteredString(font, msg, x + width / 2, y + (height - 8) / 2, 0xffffa0); + } else { + drawCenteredString(font, msg, x + width / 2, y + (height - 8) / 2, 0xe0e0e0); + } + } +} + +void Button::renderBg( Minecraft* minecraft, int xm, int ym ) +{ + minecraft->textures->loadAndBindTexture("gui/gui.png"); + glColor4f2(1, 1, 1, 1); + + //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); + int yImage = getYImage(selected || hovered(minecraft, xm, ym));; + + blit(x, y, 0, 46 + yImage * 20, width / 2, height, 0, 20); + blit(x + width / 2, y, 200 - width / 2, 46 + yImage * 20, width / 2, height, 0, 20); +} + +bool Button::hovered(Minecraft* minecraft, int xm , int ym) { + return minecraft->useTouchscreen()? (_currentlyDown && isInside(xm, ym)) : isInside(xm, ym); +} + +bool Button::isInside( int xm, int ym ) { + return xm >= x && ym >= y && xm < x + width && ym < y + height; +} + +// +// BlankButton +// +BlankButton::BlankButton(int id) +: super(id, "") +{ + visible = false; +} + +BlankButton::BlankButton(int id, int x, int y, int w, int h) +: super(id, x, y, w, h, "") +{ + visible = false; +} + +// +// The Touch-interface button +// +namespace Touch { + +TButton::TButton(int id, const std::string& msg) +: super(id, msg) +{ + width = 66; + height = 26; +} + +TButton::TButton( int id, int x, int y, const std::string& msg ) +: super(id, x, y, msg) +{ + width = 66; + height = 26; +} + +TButton::TButton( int id, int x, int y, int w, int h, const std::string& msg ) +: super(id, x, y, w, h, msg) +{ +} + +void TButton::renderBg( Minecraft* minecraft, int xm, int ym ) +{ + bool hovered = active && (minecraft->useTouchscreen()? (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); + // bool hovered = active && (_currentlyDown && isInside(xm, ym)); + + minecraft->textures->loadAndBindTexture("gui/touchgui.png"); + + //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); + if (active) + glColor4f2(1, 1, 1, 1); + else + glColor4f2(0.5f, 0.5f, 0.5f, 1); + + blit(x, y, hovered?66:0, 0, width, height, 66, 26); + //blit(x + w / 2, y, 200 - w / 2, 46 + yImage * 20, w / 2, h, 0, 20); +} + + +// +// Header spacing in Touchscreen mode +// +THeader::THeader(int id, const std::string& msg) +: super(id, msg), + xText(-99999) +{ + active = false; + width = 66; + height = 26; +} + +THeader::THeader( int id, int x, int y, const std::string& msg ) +: super(id, x, y, msg), + xText(-99999) +{ + active = false; + width = 66; + height = 26; +} + +THeader::THeader( int id, int x, int y, int w, int h, const std::string& msg ) +: super(id, x, y, w, h, msg), + xText(-99999) +{ + active = false; +} + +void THeader::render( Minecraft* minecraft, int xm, int ym ) { + Font* font = minecraft->font; + renderBg(minecraft, xm, ym); + + int xx = x + width/2; + if (xText != -99999) + xx = xText; + drawCenteredString(font, msg, xx, y + (height - 8) / 2, 0xe0e0e0); +} + +void THeader::renderBg( Minecraft* minecraft, int xm, int ym ) +{ + minecraft->textures->loadAndBindTexture("gui/touchgui.png"); + + //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); + glColor4f2(1, 1, 1, 1); + + // Left cap + blit(x, y, 150, 26, 2, height-1, 2, 25); + // Middle + blit(x+2, y, 153, 26, width-3, height-1, 8, 25); + // Right cap + blit(x+width-2, y, 162, 26, 2, height-1, 2, 25); + // Shadow + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + blit(x, y+height-1, 153, 52, width, 3, 8, 3); +} + +}; diff --git a/src/client/gui/components/Button.h b/src/client/gui/components/Button.h index 1b93ddc..2cfde05 100755 --- a/src/client/gui/components/Button.h +++ b/src/client/gui/components/Button.h @@ -1,80 +1,80 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Button_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Button_H__ - -//package net.minecraft.client.gui; - -#include -#include "GuiElement.h" -#include "../../Options.h" - -class Font; -class Minecraft; - -class Button: public GuiElement -{ -public: - Button(int id, const std::string& msg); - Button(int id, int x, int y, const std::string& msg); - Button(int id, int x, int y, int w, int h, const std::string& msg); - virtual ~Button() {} - virtual void render(Minecraft* minecraft, int xm, int ym); - - virtual bool clicked(Minecraft* minecraft, int mx, int my); - virtual void released(int mx, int my); - virtual void setPressed(); - - bool isInside(int xm, int ym); -protected: - virtual int getYImage(bool hovered); - virtual void renderBg(Minecraft* minecraft, int xm, int ym); - - virtual void renderFace(Minecraft* minecraft, int xm, int ym); - bool hovered(Minecraft* minecraft, int xm, int ym); -public: - std::string msg; - int id; - - bool selected; -protected: - bool _currentlyDown; -}; - -// @note: A bit backwards, but this is a button that -// only reacts to clicks, but isn't rendered. -class BlankButton: public Button -{ - typedef Button super; -public: - BlankButton(int id); - BlankButton(int id, int x, int y, int w, int h); -}; - - -namespace Touch { -class TButton: public Button -{ - typedef Button super; -public: - TButton(int id, const std::string& msg); - TButton(int id, int x, int y, const std::string& msg); - TButton(int id, int x, int y, int w, int h, const std::string& msg); -protected: - virtual void renderBg(Minecraft* minecraft, int xm, int ym); -}; - -// "Header" in Touchscreen mode -class THeader: public Button { - typedef Button super; -public: - THeader(int id, const std::string& msg); - THeader(int id, int x, int y, const std::string& msg); - THeader(int id, int x, int y, int w, int h, const std::string& msg); -protected: - virtual void renderBg(Minecraft* minecraft, int xm, int ym); - void render( Minecraft* minecraft, int xm, int ym ); -public: - int xText; -}; -} - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Button_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Button_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Button_H__ + +//package net.minecraft.client.gui; + +#include +#include "GuiElement.h" +#include "../../Options.h" + +class Font; +class Minecraft; + +class Button: public GuiElement +{ +public: + Button(int id, const std::string& msg); + Button(int id, int x, int y, const std::string& msg); + Button(int id, int x, int y, int w, int h, const std::string& msg); + virtual ~Button() {} + virtual void render(Minecraft* minecraft, int xm, int ym); + + virtual bool clicked(Minecraft* minecraft, int mx, int my); + virtual void released(int mx, int my); + virtual void setPressed(); + + bool isInside(int xm, int ym); +protected: + virtual int getYImage(bool hovered); + virtual void renderBg(Minecraft* minecraft, int xm, int ym); + + virtual void renderFace(Minecraft* minecraft, int xm, int ym); + bool hovered(Minecraft* minecraft, int xm, int ym); +public: + std::string msg; + int id; + + bool selected; +protected: + bool _currentlyDown; +}; + +// @note: A bit backwards, but this is a button that +// only reacts to clicks, but isn't rendered. +class BlankButton: public Button +{ + typedef Button super; +public: + BlankButton(int id); + BlankButton(int id, int x, int y, int w, int h); +}; + + +namespace Touch { +class TButton: public Button +{ + typedef Button super; +public: + TButton(int id, const std::string& msg); + TButton(int id, int x, int y, const std::string& msg); + TButton(int id, int x, int y, int w, int h, const std::string& msg); +protected: + virtual void renderBg(Minecraft* minecraft, int xm, int ym); +}; + +// "Header" in Touchscreen mode +class THeader: public Button { + typedef Button super; +public: + THeader(int id, const std::string& msg); + THeader(int id, int x, int y, const std::string& msg); + THeader(int id, int x, int y, int w, int h, const std::string& msg); +protected: + virtual void renderBg(Minecraft* minecraft, int xm, int ym); + void render( Minecraft* minecraft, int xm, int ym ); +public: + int xText; +}; +} + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Button_H__*/ diff --git a/src/client/gui/components/GButton.cpp b/src/client/gui/components/GButton.cpp index 2ac8ade..7598b2c 100755 --- a/src/client/gui/components/GButton.cpp +++ b/src/client/gui/components/GButton.cpp @@ -1 +1 @@ -#include "GuiElement.h" +#include "GuiElement.h" diff --git a/src/client/gui/components/GButton.h b/src/client/gui/components/GButton.h index 8e9474e..89e517c 100755 --- a/src/client/gui/components/GButton.h +++ b/src/client/gui/components/GButton.h @@ -1,55 +1,55 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__GButton_H__ -#define NET_MINECRAFT_CLIENT_GUI__GButton_H__ -#include "Button.h" - -class GButton: public Button { - typedef Button super; -public: - static const int LayerDefault = 1; - static const int LayerSelected = 2; - static const int LayerMax = 4; - - GButton(int id) - : super(id, "") - {} - ~GButton() { - for (unsigned int i = 0; i < layers.size(); ++i) { - delete layers[i].first; - } - } - - void addElement(int layerId, GuiElement* e) { - if (!e || layerId < 0 || layerId >= LayerMax) { - LOGE("Error @ GButton::element : Trying to add element %p at layer: %d\n", e, layerId); - return; - } - layers.push_back(std::make_pair(e, layerId)); - } - - void render( Minecraft* minecraft, int xm, int ym ) - { - if (!visible) return; - - bool isHovered = minecraft->isTouchscreen()? - (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height): false; - - int layer = isHovered? LayerSelected : LayerDefault; - if (layer < 0) return; - - Tesselator& t = Tesselator::instance; - t.addOffset((float)x, (float)y, 0); - - for (unsigned int i = 0; i < layers.size(); ++i) { - if ((layers[i].second & layer) != 0) - layers[i].first->render(minecraft, 0, 0); - } - - t.addOffset((float)-x, (float)-y, 0); - } - -private: - std::vector > layers; -}; - - -#endif /*NET_MINECRAFT_CLIENT_GUI__GButton_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__GButton_H__ +#define NET_MINECRAFT_CLIENT_GUI__GButton_H__ +#include "Button.h" + +class GButton: public Button { + typedef Button super; +public: + static const int LayerDefault = 1; + static const int LayerSelected = 2; + static const int LayerMax = 4; + + GButton(int id) + : super(id, "") + {} + ~GButton() { + for (unsigned int i = 0; i < layers.size(); ++i) { + delete layers[i].first; + } + } + + void addElement(int layerId, GuiElement* e) { + if (!e || layerId < 0 || layerId >= LayerMax) { + LOGE("Error @ GButton::element : Trying to add element %p at layer: %d\n", e, layerId); + return; + } + layers.push_back(std::make_pair(e, layerId)); + } + + void render( Minecraft* minecraft, int xm, int ym ) + { + if (!visible) return; + + bool isHovered = minecraft->isTouchscreen()? + (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height): false; + + int layer = isHovered? LayerSelected : LayerDefault; + if (layer < 0) return; + + Tesselator& t = Tesselator::instance; + t.addOffset((float)x, (float)y, 0); + + for (unsigned int i = 0; i < layers.size(); ++i) { + if ((layers[i].second & layer) != 0) + layers[i].first->render(minecraft, 0, 0); + } + + t.addOffset((float)-x, (float)-y, 0); + } + +private: + std::vector > layers; +}; + + +#endif /*NET_MINECRAFT_CLIENT_GUI__GButton_H__*/ diff --git a/src/client/gui/components/GuiElement.cpp b/src/client/gui/components/GuiElement.cpp index 5f4804e..36606b8 100755 --- a/src/client/gui/components/GuiElement.cpp +++ b/src/client/gui/components/GuiElement.cpp @@ -1,20 +1,20 @@ -#include "GuiElement.h" - -GuiElement::GuiElement( bool active/*=false*/, bool visible/*=true*/, int x /*= 0*/, int y /*= 0*/, int width/*=24*/, int height/*=24*/ ) -: active(active), - visible(visible), - x(x), - y(y), - width(width), - height(height) { - -} - -bool GuiElement::pointInside( int x, int y ) { - if(x >= this->x && x < this->x + this->width) { - if(y >= this->y && y < this->y + this->height) { - return true; - } - } - return false; -} +#include "GuiElement.h" + +GuiElement::GuiElement( bool active/*=false*/, bool visible/*=true*/, int x /*= 0*/, int y /*= 0*/, int width/*=24*/, int height/*=24*/ ) +: active(active), + visible(visible), + x(x), + y(y), + width(width), + height(height) { + +} + +bool GuiElement::pointInside( int x, int y ) { + if(x >= this->x && x < this->x + this->width) { + if(y >= this->y && y < this->y + this->height) { + return true; + } + } + return false; +} diff --git a/src/client/gui/components/GuiElement.h b/src/client/gui/components/GuiElement.h index f0a0a33..c284095 100755 --- a/src/client/gui/components/GuiElement.h +++ b/src/client/gui/components/GuiElement.h @@ -1,34 +1,34 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__GuiElement_H__ -#define NET_MINECRAFT_CLIENT_GUI__GuiElement_H__ -#include "../GuiComponent.h" - -class Tesselator; -class Minecraft; - -class GuiElement : public GuiComponent { -public: - GuiElement(bool active=false, bool visible=true, int x = 0, int y = 0, int width=24, int height=24); - virtual ~GuiElement() {} - - virtual void tick(Minecraft* minecraft) {} - virtual void render(Minecraft* minecraft, int xm, int ym) { } - virtual void setupPositions() {} - - virtual void mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum) {} - virtual void mouseReleased(Minecraft* minecraft, int x, int y, int buttonNum) {} - virtual void keyPressed(Minecraft* minecraft, int key) {} - virtual void charPressed(Minecraft* minecraft, char key) {} - - virtual bool pointInside(int x, int y); - - void setVisible(bool visible); - - bool active; - bool visible; - int x; - int y; - int width; - int height; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI__GuiElement_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__GuiElement_H__ +#define NET_MINECRAFT_CLIENT_GUI__GuiElement_H__ +#include "../GuiComponent.h" + +class Tesselator; +class Minecraft; + +class GuiElement : public GuiComponent { +public: + GuiElement(bool active=false, bool visible=true, int x = 0, int y = 0, int width=24, int height=24); + virtual ~GuiElement() {} + + virtual void tick(Minecraft* minecraft) {} + virtual void render(Minecraft* minecraft, int xm, int ym) { } + virtual void setupPositions() {} + + virtual void mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum) {} + virtual void mouseReleased(Minecraft* minecraft, int x, int y, int buttonNum) {} + virtual void keyPressed(Minecraft* minecraft, int key) {} + virtual void charPressed(Minecraft* minecraft, char key) {} + + virtual bool pointInside(int x, int y); + + void setVisible(bool visible); + + bool active; + bool visible; + int x; + int y; + int width; + int height; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI__GuiElement_H__*/ diff --git a/src/client/gui/components/GuiElementContainer.cpp b/src/client/gui/components/GuiElementContainer.cpp index 9cfbb09..035c7f5 100755 --- a/src/client/gui/components/GuiElementContainer.cpp +++ b/src/client/gui/components/GuiElementContainer.cpp @@ -1,66 +1,66 @@ -#include "GuiElementContainer.h" -#include -GuiElementContainer::GuiElementContainer( bool active/*=false*/, bool visible/*=true*/, int x /*= 0*/, int y /*= 0*/, int width/*=24*/, int height/*=24*/ ) -: GuiElement(active, visible, x, y, width, height) { - -} - -GuiElementContainer::~GuiElementContainer() { - while(!children.empty()) { - GuiElement* element = children.back(); - children.pop_back(); - delete element; - } -} - -void GuiElementContainer::render( Minecraft* minecraft, int xm, int ym ) { - for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { - (*it)->render(minecraft, xm, ym); - } -} - -void GuiElementContainer::setupPositions() { - for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { - (*it)->setupPositions(); - } -} - -void GuiElementContainer::addChild( GuiElement* element ) { - children.push_back(element); -} - -void GuiElementContainer::removeChild( GuiElement* element ) { - std::vector::iterator it = std::find(children.begin(), children.end(), element); - if(it != children.end()) - children.erase(it); -} - -void GuiElementContainer::tick( Minecraft* minecraft ) { - for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { - (*it)->tick(minecraft); - } -} - -void GuiElementContainer::mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ) { - for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { - (*it)->mouseClicked(minecraft, x, y, buttonNum); - } -} - -void GuiElementContainer::mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) { - for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { - (*it)->mouseReleased(minecraft, x, y, buttonNum); - } -} - -void GuiElementContainer::keyPressed(Minecraft* minecraft, int key) { - for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { - (*it)->keyPressed(minecraft, key); - } -} - -void GuiElementContainer::charPressed(Minecraft* minecraft, char key) { - for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { - (*it)->charPressed(minecraft, key); - } +#include "GuiElementContainer.h" +#include +GuiElementContainer::GuiElementContainer( bool active/*=false*/, bool visible/*=true*/, int x /*= 0*/, int y /*= 0*/, int width/*=24*/, int height/*=24*/ ) +: GuiElement(active, visible, x, y, width, height) { + +} + +GuiElementContainer::~GuiElementContainer() { + while(!children.empty()) { + GuiElement* element = children.back(); + children.pop_back(); + delete element; + } +} + +void GuiElementContainer::render( Minecraft* minecraft, int xm, int ym ) { + for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->render(minecraft, xm, ym); + } +} + +void GuiElementContainer::setupPositions() { + for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->setupPositions(); + } +} + +void GuiElementContainer::addChild( GuiElement* element ) { + children.push_back(element); +} + +void GuiElementContainer::removeChild( GuiElement* element ) { + std::vector::iterator it = std::find(children.begin(), children.end(), element); + if(it != children.end()) + children.erase(it); +} + +void GuiElementContainer::tick( Minecraft* minecraft ) { + for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->tick(minecraft); + } +} + +void GuiElementContainer::mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ) { + for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->mouseClicked(minecraft, x, y, buttonNum); + } +} + +void GuiElementContainer::mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) { + for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->mouseReleased(minecraft, x, y, buttonNum); + } +} + +void GuiElementContainer::keyPressed(Minecraft* minecraft, int key) { + for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->keyPressed(minecraft, key); + } +} + +void GuiElementContainer::charPressed(Minecraft* minecraft, char key) { + for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->charPressed(minecraft, key); + } } \ No newline at end of file diff --git a/src/client/gui/components/GuiElementContainer.h b/src/client/gui/components/GuiElementContainer.h index d20f9d3..096c6b5 100755 --- a/src/client/gui/components/GuiElementContainer.h +++ b/src/client/gui/components/GuiElementContainer.h @@ -1,28 +1,28 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__GuiElementContainer_H__ -#define NET_MINECRAFT_CLIENT_GUI__GuiElementContainer_H__ -#include "GuiElement.h" -#include -class Tesselator; -class Minecraft; - -class GuiElementContainer : public GuiElement { -public: - GuiElementContainer(bool active=false, bool visible=true, int x = 0, int y = 0, int width=24, int height=24); - virtual ~GuiElementContainer(); - virtual void render(Minecraft* minecraft, int xm, int ym); - virtual void setupPositions(); - virtual void addChild(GuiElement* element); - virtual void removeChild(GuiElement* element); - - virtual void tick( Minecraft* minecraft ); - - virtual void mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ); - virtual void mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ); - virtual void keyPressed(Minecraft* minecraft, int key); - virtual void charPressed(Minecraft* minecraft, char key); - -protected: - std::vector children; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI__GuiElementContainer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__GuiElementContainer_H__ +#define NET_MINECRAFT_CLIENT_GUI__GuiElementContainer_H__ +#include "GuiElement.h" +#include +class Tesselator; +class Minecraft; + +class GuiElementContainer : public GuiElement { +public: + GuiElementContainer(bool active=false, bool visible=true, int x = 0, int y = 0, int width=24, int height=24); + virtual ~GuiElementContainer(); + virtual void render(Minecraft* minecraft, int xm, int ym); + virtual void setupPositions(); + virtual void addChild(GuiElement* element); + virtual void removeChild(GuiElement* element); + + virtual void tick( Minecraft* minecraft ); + + virtual void mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ); + virtual void mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ); + virtual void keyPressed(Minecraft* minecraft, int key); + virtual void charPressed(Minecraft* minecraft, char key); + +protected: + std::vector children; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI__GuiElementContainer_H__*/ diff --git a/src/client/gui/components/ImageButton.cpp b/src/client/gui/components/ImageButton.cpp index f686dcd..d9fd195 100755 --- a/src/client/gui/components/ImageButton.cpp +++ b/src/client/gui/components/ImageButton.cpp @@ -1,135 +1,135 @@ -#include "ImageButton.h" -#include "../../renderer/Tesselator.h" -#include "../../Minecraft.h" -#include "../../../platform/log.h" -#include "../../../util/Mth.h" -#include "../../renderer/Textures.h" -#include "../../Option.h" - - -ImageButton::ImageButton(int id, const std::string& msg) -: super(id, msg) -{ - setupDefault(); -} - -ImageButton::ImageButton(int id, const std::string& msg, const ImageDef& imagedef) -: super(id, msg), - _imageDef(imagedef) -{ - setupDefault(); -} - -void ImageButton::setupDefault() { - width = 48; - height = 48; - scaleWhenPressed = true; -} - -void ImageButton::setImageDef(const ImageDef& imageDef, bool setButtonSize) { - _imageDef = imageDef; - if (setButtonSize) { - width = (int)_imageDef.width; - height = (int)_imageDef.height; - } -} - -void ImageButton::render(Minecraft* minecraft, int xm, int ym) { - if (!visible) return; - - Font* font = minecraft->font; - - //minecraft->textures->loadAndBindTexture("gui/gui.png"); - glColor4f2(1, 1, 1, 1); - - bool hovered = active && (minecraft->useTouchscreen()? (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); - bool IsSecondImage = isSecondImage(hovered); - - //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); - //int yImage = getYImage(hovered || selected); - - //blit(x, y, 0, 46 + yImage * 20, w / 2, h, 0, 20); - //blit(x + w / 2, y, 200 - w / 2, 46 + yImage * 20, w / 2, h, 0, 20); - - renderBg(minecraft, xm, ym); - - TextureId texId = (_imageDef.name.length() > 0)? minecraft->textures->loadAndBindTexture(_imageDef.name) : Textures::InvalidId; - if ( Textures::isTextureIdValid(texId) ) { - const ImageDef& d = _imageDef; - Tesselator& t = Tesselator::instance; - - t.begin(); - if (!active) t.color(0xff808080); - //else if (hovered||selected) t.color(0xffffffff); - //else t.color(0xffe0e0e0); - else t.color(0xffffffff); - - float hx = ((float) d.width) * 0.5f; - float hy = ((float) d.height) * 0.5f; - const float cx = ((float)x+d.x) + hx; - const float cy = ((float)y+d.y) + hy; - if (scaleWhenPressed && hovered) { - hx *= 0.95f; - hy *= 0.95f; - } - - const IntRectangle* src = _imageDef.getSrc(); - if (src) { - const TextureData* d = minecraft->textures->getTemporaryTextureData(texId); - if (d != NULL) { - float u0 = (src->x+(IsSecondImage?src->w:0)) / (float)d->w; - float u1 = (src->x+(IsSecondImage?2*src->w:src->w)) / (float)d->w; - float v0 = src->y / (float)d->h; - float v1 = (src->y+src->h) / (float)d->h; - t.vertexUV(cx-hx, cy-hy, blitOffset, u0, v0); - t.vertexUV(cx-hx, cy+hy, blitOffset, u0, v1); - t.vertexUV(cx+hx, cy+hy, blitOffset, u1, v1); - t.vertexUV(cx+hx, cy-hy, blitOffset, u1, v0); - } - } else { - t.vertexUV(cx-hx, cy-hy, blitOffset, 0, 0); - t.vertexUV(cx-hx, cy+hy, blitOffset, 0, 1); - t.vertexUV(cx+hx, cy+hy, blitOffset, 1, 1); - t.vertexUV(cx+hx, cy-hy, blitOffset, 1, 0); - } - t.draw(); - } - //blit(0, 0, 0, 0, 64, 64, 256, 256); - - //LOGI("%d %d\n", x+d.x, x+d.x+d.w); - - if (!active) { - drawCenteredString(font, msg, x + width / 2, y + 16/*(h - 16)*/, 0xffa0a0a0); - } else { - if (hovered || selected) { - drawCenteredString(font, msg, x + width / 2, y + 17/*(h - 16)*/, 0xffffa0); - } else { - drawCenteredString(font, msg, x + width / 2, y + 16/*(h - 48)*/, 0xe0e0e0); - } - } -} - - -// -// A toggleable Button -// -OptionButton::OptionButton(OptionId option) : m_optId(option), super(ButtonId, "") {} - -void OptionButton::toggle(Options* options) { - options->toggle(m_optId); - - // Update graphics here - updateImage(options); -} - -void OptionButton::updateImage(Options* options) { - _secondImage = options->getBooleanValue(m_optId); -} - -void OptionButton::mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ) { - if(buttonNum == MouseAction::ACTION_LEFT) { - if(clicked(minecraft, x, y)) { - toggle(&minecraft->options); - } - } -} +#include "ImageButton.h" +#include "../../renderer/Tesselator.h" +#include "../../Minecraft.h" +#include "../../../platform/log.h" +#include "../../../util/Mth.h" +#include "../../renderer/Textures.h" +#include + + +ImageButton::ImageButton(int id, const std::string& msg) +: super(id, msg) +{ + setupDefault(); +} + +ImageButton::ImageButton(int id, const std::string& msg, const ImageDef& imagedef) +: super(id, msg), + _imageDef(imagedef) +{ + setupDefault(); +} + +void ImageButton::setupDefault() { + width = 48; + height = 48; + scaleWhenPressed = true; +} + +void ImageButton::setImageDef(const ImageDef& imageDef, bool setButtonSize) { + _imageDef = imageDef; + if (setButtonSize) { + width = (int)_imageDef.width; + height = (int)_imageDef.height; + } +} + +void ImageButton::render(Minecraft* minecraft, int xm, int ym) { + if (!visible) return; + + Font* font = minecraft->font; + + //minecraft->textures->loadAndBindTexture("gui/gui.png"); + glColor4f2(1, 1, 1, 1); + + bool hovered = active && (minecraft->useTouchscreen()? (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); + bool IsSecondImage = isSecondImage(hovered); + + //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); + //int yImage = getYImage(hovered || selected); + + //blit(x, y, 0, 46 + yImage * 20, w / 2, h, 0, 20); + //blit(x + w / 2, y, 200 - w / 2, 46 + yImage * 20, w / 2, h, 0, 20); + + renderBg(minecraft, xm, ym); + + TextureId texId = (_imageDef.name.length() > 0)? minecraft->textures->loadAndBindTexture(_imageDef.name) : Textures::InvalidId; + if ( Textures::isTextureIdValid(texId) ) { + const ImageDef& d = _imageDef; + Tesselator& t = Tesselator::instance; + + t.begin(); + if (!active) t.color(0xff808080); + //else if (hovered||selected) t.color(0xffffffff); + //else t.color(0xffe0e0e0); + else t.color(0xffffffff); + + float hx = ((float) d.width) * 0.5f; + float hy = ((float) d.height) * 0.5f; + const float cx = ((float)x+d.x) + hx; + const float cy = ((float)y+d.y) + hy; + if (scaleWhenPressed && hovered) { + hx *= 0.95f; + hy *= 0.95f; + } + + const IntRectangle* src = _imageDef.getSrc(); + if (src) { + const TextureData* d = minecraft->textures->getTemporaryTextureData(texId); + if (d != NULL) { + float u0 = (src->x+(IsSecondImage?src->w:0)) / (float)d->w; + float u1 = (src->x+(IsSecondImage?2*src->w:src->w)) / (float)d->w; + float v0 = src->y / (float)d->h; + float v1 = (src->y+src->h) / (float)d->h; + t.vertexUV(cx-hx, cy-hy, blitOffset, u0, v0); + t.vertexUV(cx-hx, cy+hy, blitOffset, u0, v1); + t.vertexUV(cx+hx, cy+hy, blitOffset, u1, v1); + t.vertexUV(cx+hx, cy-hy, blitOffset, u1, v0); + } + } else { + t.vertexUV(cx-hx, cy-hy, blitOffset, 0, 0); + t.vertexUV(cx-hx, cy+hy, blitOffset, 0, 1); + t.vertexUV(cx+hx, cy+hy, blitOffset, 1, 1); + t.vertexUV(cx+hx, cy-hy, blitOffset, 1, 0); + } + t.draw(); + } + //blit(0, 0, 0, 0, 64, 64, 256, 256); + + //LOGI("%d %d\n", x+d.x, x+d.x+d.w); + + if (!active) { + drawCenteredString(font, msg, x + width / 2, y + 16/*(h - 16)*/, 0xffa0a0a0); + } else { + if (hovered || selected) { + drawCenteredString(font, msg, x + width / 2, y + 17/*(h - 16)*/, 0xffffa0); + } else { + drawCenteredString(font, msg, x + width / 2, y + 16/*(h - 48)*/, 0xe0e0e0); + } + } +} + + +// +// A toggleable Button +// +OptionButton::OptionButton(OptionId option) : m_optId(option), super(ButtonId, "") {} + +void OptionButton::toggle(Options* options) { + options->toggle(m_optId); + + // Update graphics here + updateImage(options); +} + +void OptionButton::updateImage(Options* options) { + _secondImage = options->getBooleanValue(m_optId); +} + +void OptionButton::mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ) { + if(buttonNum == MouseAction::ACTION_LEFT) { + if(clicked(minecraft, x, y)) { + toggle(&minecraft->options); + } + } +} diff --git a/src/client/gui/components/ImageButton.h b/src/client/gui/components/ImageButton.h index a1bd777..2901875 100755 --- a/src/client/gui/components/ImageButton.h +++ b/src/client/gui/components/ImageButton.h @@ -1,97 +1,97 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ImageButton_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ImageButton_H__ - -#include "Button.h" - -typedef struct IntRectangle { - IntRectangle() - : x(0), - y(0), - w(1), - h(1) - {} - IntRectangle(int x, int y, int w, int h) - : x(x), - y(y), - w(w), - h(h) - {} - - int x, y; - int w, h; -} IntRectangle; - -typedef struct ImageDef { - ImageDef() - : hasSrc(false), - x(0), - y(0), - width(16), - height(16) - {} - - std::string name; - int x; - int y; - float width; - float height; - - ImageDef& setSrc(const IntRectangle& srcRect) { - hasSrc = true; - src = srcRect; - return *this; - } - IntRectangle* getSrc() { - return hasSrc? &src : NULL; - } -protected: - IntRectangle src; - bool hasSrc; -} ImageDef; - - -class ImageButton: public Button -{ - typedef Button super; -public: - ImageButton(int id, const std::string& msg); - ImageButton(int id, const std::string& msg, const ImageDef& imageDef); - void setImageDef(const ImageDef& imageDef, bool setButtonSize); - - void render(Minecraft* minecraft, int xm, int ym); - void renderBg(Minecraft* minecraft, int xm, int ym) {} - -protected: - virtual void setupDefault(); - virtual bool isSecondImage(bool hovered) { return hovered; } - - ImageDef _imageDef; -public: - bool scaleWhenPressed; -}; - -// -// A toggleable Button -// -class OptionButton: public ImageButton -{ - typedef ImageButton super; -public: - OptionButton(OptionId optId); - - void toggle(Options* options); - void updateImage(Options* options); - - static const int ButtonId = 9999999; -protected: - bool isSecondImage(bool hovered) { return _secondImage; } - - virtual void mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ); - -private: - OptionId m_optId; - bool _secondImage; -}; - - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ImageButton_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ImageButton_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ImageButton_H__ + +#include "Button.h" + +typedef struct IntRectangle { + IntRectangle() + : x(0), + y(0), + w(1), + h(1) + {} + IntRectangle(int x, int y, int w, int h) + : x(x), + y(y), + w(w), + h(h) + {} + + int x, y; + int w, h; +} IntRectangle; + +typedef struct ImageDef { + ImageDef() + : hasSrc(false), + x(0), + y(0), + width(16), + height(16) + {} + + std::string name; + int x; + int y; + float width; + float height; + + ImageDef& setSrc(const IntRectangle& srcRect) { + hasSrc = true; + src = srcRect; + return *this; + } + IntRectangle* getSrc() { + return hasSrc? &src : NULL; + } +protected: + IntRectangle src; + bool hasSrc; +} ImageDef; + + +class ImageButton: public Button +{ + typedef Button super; +public: + ImageButton(int id, const std::string& msg); + ImageButton(int id, const std::string& msg, const ImageDef& imageDef); + void setImageDef(const ImageDef& imageDef, bool setButtonSize); + + void render(Minecraft* minecraft, int xm, int ym); + void renderBg(Minecraft* minecraft, int xm, int ym) {} + +protected: + virtual void setupDefault(); + virtual bool isSecondImage(bool hovered) { return hovered; } + + ImageDef _imageDef; +public: + bool scaleWhenPressed; +}; + +// +// A toggleable Button +// +class OptionButton: public ImageButton +{ + typedef ImageButton super; +public: + OptionButton(OptionId optId); + + void toggle(Options* options); + void updateImage(Options* options); + + static const int ButtonId = 9999999; +protected: + bool isSecondImage(bool hovered) { return _secondImage; } + + virtual void mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ); + +private: + OptionId m_optId; + bool _secondImage; +}; + + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ImageButton_H__*/ diff --git a/src/client/gui/components/InventoryPane.cpp b/src/client/gui/components/InventoryPane.cpp index 1ee9334..7d22b73 100755 --- a/src/client/gui/components/InventoryPane.cpp +++ b/src/client/gui/components/InventoryPane.cpp @@ -1,206 +1,206 @@ -#include "InventoryPane.h" -#include "../Gui.h" -#include "../../Minecraft.h" -#include "../../player/input/touchscreen/TouchAreaModel.h" -#include "../../renderer/entity/ItemRenderer.h" -#include "../../renderer/Tesselator.h" -#include "../../renderer/Textures.h" -#include "../../../world/item/ItemInstance.h" -#include "../../../world/entity/player/Inventory.h" - -namespace Touch { - -static const int By = 6; // Border Frame height - -InventoryPane::InventoryPane( IInventoryPaneCallback* screen, Minecraft* mc, const IntRectangle& rect, int paneWidth, float clickMarginH, int numItems, int itemSize, int itemBorderSize) -: screen(screen), - mc(mc), - paneWidth(paneWidth), - rect(rect), - super( - SF_LockX|/*SF_Scissor|*/SF_ShowScrollbar|SF_NoHoldSelect, - rect, // Pane rect - IntRectangle(0, 0, itemSize, itemSize), // Item rect - 0, numItems, Gui::GuiScale), - BorderPixels(itemBorderSize), - lastItemIndex(-1), - lastItemTicks(-1), - fillMarginX(2), - fillMarginY(4), - markerType(1), - markerIndex(-1), - markerShare(0), - renderDecorations(true) -{ - _clickArea = new RectangleArea(0, 0, 0, 0); - area._x0 = rect.x - clickMarginH; - area._x1 = rect.x + rect.w + clickMarginH; - area._y0 -= By; - area._y1 += By; - - /* - const int left = bbox.x + (bbox.w - paneWidth) / 2; - bg.x = left; - bg.w = left + paneWidth; // @note: read as x1, not width - bg.y = bbox.y - fillMarginY; - bg.h = bbox.y + bbox.h + fillMarginY; // @note: read as y1, not width - */ -} - -InventoryPane::~InventoryPane() { - delete _clickArea; -} - -void InventoryPane::renderBatch( std::vector& items, float alpha ) -{ - //fill(bg.x, bg.y, bg.w, bg.h, 0xff333333); - fill((float)(bbox.x-fillMarginX-1), (float)(bbox.y-fillMarginY), (float)(bbox.x + bbox.w + fillMarginX+1), (float)(bbox.y + bbox.h + fillMarginY), 0xff333333); - //fill(0.0f, (float)(bbox.y-fillMarginY), 400.0f, (float)(bbox.y + bbox.h + fillMarginY), 0xff333333);//(float)(bbox.x-fillMarginX), (float)(bbox.y-fillMarginY), (float)(bbox.x + bbox.w + fillMarginX), (float)(bbox.y + bbox.h + fillMarginY), 0xff333333); - glEnable2(GL_BLEND); - glDisable2(GL_ALPHA_TEST); - std::vector inventoryItems = screen->getItems(this); - - glEnable2(GL_SCISSOR_TEST); - GLuint x = (GLuint)(screenScale * bbox.x); - GLuint y = mc->height - (GLuint)(screenScale * (bbox.y + bbox.h)); - GLuint w = (GLuint)(screenScale * bbox.w); - GLuint h = (GLuint)(screenScale * bbox.h); - glScissor(x, y, w, h); - - Tesselator& t = Tesselator::instance; - - t.beginOverride(); - t.colorABGR(0xffffffff); - for (unsigned int i = 0; i < items.size(); ++i) { - GridItem& item = items[i]; - blit(item.xf, item.yf, 200, 46, (float)itemBbox.w, (float)itemBbox.h, 16, 16); - } - mc->textures->loadAndBindTexture("gui/gui.png"); - t.endOverrideAndDraw(); - - GridItem* marked = NULL; - float mxx, myy; - - t.beginOverride(); - for (unsigned int i = 0; i < items.size(); ++i) { - GridItem& item = items[i]; - int j = item.id; - const ItemInstance* citem = inventoryItems[j]; - if (!citem) continue; - - bool allowed = true; - - t.enableColor(); - //#ifdef DEMO_MODE //@huge @attn - if (!screen->isAllowed(j)) { allowed = false; t.color( 64, 64, 64); } - else - //#endif - if (lastItemTicks > 0 && lastItemIndex == j) { - int gv = 255 - lastItemTicks * 15; - t.color(gv, gv, gv, (allowed && citem->count <= 0)?0x60:0xff); - } else { - t.color(255, 255, 255, (allowed && citem->count <= 0)?0x60:0xff); - } - t.noColor(); - float xx = Gui::floorAlignToScreenPixel(item.xf + BorderPixels + 4); - float yy = Gui::floorAlignToScreenPixel(item.yf + BorderPixels + 4); - ItemRenderer::renderGuiItem(NULL, mc->textures, citem, xx, yy, 16, 16, false); - - if (j == markerIndex && markerShare >= 0) - marked = &item, mxx = xx, myy = yy; - - } - t.endOverrideAndDraw(); - - if (marked) { - glDisable2(GL_TEXTURE_2D); - const float yy0 = myy - 5.0f; - const float yy1 = yy0 + 2; - fill(mxx, yy0, mxx + 16.0f, yy1, 0xff606060); - fill(mxx, yy0, mxx + markerShare * 16.0f, yy1, markerType==1?0xff00ff00:0xff476543); - glEnable2(GL_BLEND); - glEnable2(GL_TEXTURE_2D); - } - - - if (!mc->isCreativeMode()) { - const float ikText = Gui::InvGuiScale + Gui::InvGuiScale; - const float kText = 0.5f * Gui::GuiScale; - t.beginOverride(); - t.scale2d(ikText, ikText); - for (unsigned int i = 0; i < items.size(); ++i) { - GridItem& item = items[i]; - const ItemInstance* citem = inventoryItems[item.id]; - if (!citem) continue; - - char buf[64] = {0}; - /*int c = */ Gui::itemCountItoa(buf, citem->count); - - float tx = Gui::floorAlignToScreenPixel(kText * (item.xf + BorderPixels + 3)); - float ty = Gui::floorAlignToScreenPixel(kText * (item.yf + BorderPixels + 3)); - mc->gui.renderSlotText(citem, tx, ty, true, true); - } - t.resetScale(); - glEnable2(GL_BLEND); - t.endOverrideAndDraw(); - } - - if (renderDecorations) { - t.beginOverride(); - for (unsigned int i = 0; i < items.size(); ++i) { - GridItem& item = items[i]; - const ItemInstance* citem = inventoryItems[item.id]; - if (!citem || citem->isNull()) continue; - - if (citem->isDamaged()) { - ItemRenderer::renderGuiItemDecorations(citem, item.xf + 8, item.yf + 12); - } - } - - glDisable2(GL_TEXTURE_2D); - t.endOverrideAndDraw(); - glEnable2(GL_TEXTURE_2D); - } - glDisable2(GL_SCISSOR_TEST); - - //fillGradient(bbox.x - 1, bbox.y, bbox.x + bbox.w + 1, bbox.y + 20, 0x99000000, 0x00000000); - //fillGradient(bbox.x - 1, bbox.y + bbox.h - 20, bbox.x + bbox.w + 1, bbox.y + bbox.h, 0x00000000, 0x99000000); - fillGradient(bg.x - fillMarginX, bbox.y, bg.w + fillMarginX, bbox.y + 20, 0x99000000, 0x00000000); - fillGradient(bg.x - fillMarginX, bbox.y + bbox.h - 20, bg.w + fillMarginX, bbox.y + bbox.h, 0x00000000, 0x99000000); - - drawScrollBar(hScroll); - drawScrollBar(vScroll); -} - -bool InventoryPane::onSelect( int gridId, bool selected ) -{ - //screen->onItemSelected(gridId); - if (screen->isAllowed(gridId)) - if (screen->addItem(this, gridId)) { - lastItemIndex = gridId; - lastItemTicks = 7; - } - - return false; -} - -void InventoryPane::drawScrollBar( ScrollBar& sb ) { - if (sb.alpha <= 0) - return; - - const int color = ((int)(255.0f * sb.alpha) << 24) | 0xaaaaaa; - const float xx = (float)(bbox.x + bbox.w); - fill(xx - sb.w, sb.y, xx, sb.y + sb.h, color); -} - -void InventoryPane::tick() -{ - --lastItemTicks; - super::tick(); -} - -void InventoryPane::setRenderDecorations( bool value ) { - renderDecorations = value; -} - -} +#include "InventoryPane.h" +#include "../Gui.h" +#include "../../Minecraft.h" +#include "../../player/input/touchscreen/TouchAreaModel.h" +#include "../../renderer/entity/ItemRenderer.h" +#include "../../renderer/Tesselator.h" +#include "../../renderer/Textures.h" +#include "../../../world/item/ItemInstance.h" +#include "../../../world/entity/player/Inventory.h" + +namespace Touch { + +static const int By = 6; // Border Frame height + +InventoryPane::InventoryPane( IInventoryPaneCallback* screen, Minecraft* mc, const IntRectangle& rect, int paneWidth, float clickMarginH, int numItems, int itemSize, int itemBorderSize) +: screen(screen), + mc(mc), + paneWidth(paneWidth), + rect(rect), + super( + SF_LockX|/*SF_Scissor|*/SF_ShowScrollbar|SF_NoHoldSelect, + rect, // Pane rect + IntRectangle(0, 0, itemSize, itemSize), // Item rect + 0, numItems, Gui::GuiScale), + BorderPixels(itemBorderSize), + lastItemIndex(-1), + lastItemTicks(-1), + fillMarginX(2), + fillMarginY(4), + markerType(1), + markerIndex(-1), + markerShare(0), + renderDecorations(true) +{ + _clickArea = new RectangleArea(0, 0, 0, 0); + area._x0 = rect.x - clickMarginH; + area._x1 = rect.x + rect.w + clickMarginH; + area._y0 -= By; + area._y1 += By; + + /* + const int left = bbox.x + (bbox.w - paneWidth) / 2; + bg.x = left; + bg.w = left + paneWidth; // @note: read as x1, not width + bg.y = bbox.y - fillMarginY; + bg.h = bbox.y + bbox.h + fillMarginY; // @note: read as y1, not width + */ +} + +InventoryPane::~InventoryPane() { + delete _clickArea; +} + +void InventoryPane::renderBatch( std::vector& items, float alpha ) +{ + //fill(bg.x, bg.y, bg.w, bg.h, 0xff333333); + fill((float)(bbox.x-fillMarginX-1), (float)(bbox.y-fillMarginY), (float)(bbox.x + bbox.w + fillMarginX+1), (float)(bbox.y + bbox.h + fillMarginY), 0xff333333); + //fill(0.0f, (float)(bbox.y-fillMarginY), 400.0f, (float)(bbox.y + bbox.h + fillMarginY), 0xff333333);//(float)(bbox.x-fillMarginX), (float)(bbox.y-fillMarginY), (float)(bbox.x + bbox.w + fillMarginX), (float)(bbox.y + bbox.h + fillMarginY), 0xff333333); + glEnable2(GL_BLEND); + glDisable2(GL_ALPHA_TEST); + std::vector inventoryItems = screen->getItems(this); + + glEnable2(GL_SCISSOR_TEST); + GLuint x = (GLuint)(screenScale * bbox.x); + GLuint y = mc->height - (GLuint)(screenScale * (bbox.y + bbox.h)); + GLuint w = (GLuint)(screenScale * bbox.w); + GLuint h = (GLuint)(screenScale * bbox.h); + glScissor(x, y, w, h); + + Tesselator& t = Tesselator::instance; + + t.beginOverride(); + t.colorABGR(0xffffffff); + for (unsigned int i = 0; i < items.size(); ++i) { + GridItem& item = items[i]; + blit(item.xf, item.yf, 200, 46, (float)itemBbox.w, (float)itemBbox.h, 16, 16); + } + mc->textures->loadAndBindTexture("gui/gui.png"); + t.endOverrideAndDraw(); + + GridItem* marked = NULL; + float mxx, myy; + + t.beginOverride(); + for (unsigned int i = 0; i < items.size(); ++i) { + GridItem& item = items[i]; + int j = item.id; + const ItemInstance* citem = inventoryItems[j]; + if (!citem) continue; + + bool allowed = true; + + t.enableColor(); + //#ifdef DEMO_MODE //@huge @attn + if (!screen->isAllowed(j)) { allowed = false; t.color( 64, 64, 64); } + else + //#endif + if (lastItemTicks > 0 && lastItemIndex == j) { + int gv = 255 - lastItemTicks * 15; + t.color(gv, gv, gv, (allowed && citem->count <= 0)?0x60:0xff); + } else { + t.color(255, 255, 255, (allowed && citem->count <= 0)?0x60:0xff); + } + t.noColor(); + float xx = Gui::floorAlignToScreenPixel(item.xf + BorderPixels + 4); + float yy = Gui::floorAlignToScreenPixel(item.yf + BorderPixels + 4); + ItemRenderer::renderGuiItem(NULL, mc->textures, citem, xx, yy, 16, 16, false); + + if (j == markerIndex && markerShare >= 0) + marked = &item, mxx = xx, myy = yy; + + } + t.endOverrideAndDraw(); + + if (marked) { + glDisable2(GL_TEXTURE_2D); + const float yy0 = myy - 5.0f; + const float yy1 = yy0 + 2; + fill(mxx, yy0, mxx + 16.0f, yy1, 0xff606060); + fill(mxx, yy0, mxx + markerShare * 16.0f, yy1, markerType==1?0xff00ff00:0xff476543); + glEnable2(GL_BLEND); + glEnable2(GL_TEXTURE_2D); + } + + + if (!mc->isCreativeMode()) { + const float ikText = Gui::InvGuiScale + Gui::InvGuiScale; + const float kText = 0.5f * Gui::GuiScale; + t.beginOverride(); + t.scale2d(ikText, ikText); + for (unsigned int i = 0; i < items.size(); ++i) { + GridItem& item = items[i]; + const ItemInstance* citem = inventoryItems[item.id]; + if (!citem) continue; + + char buf[64] = {0}; + /*int c = */ Gui::itemCountItoa(buf, citem->count); + + float tx = Gui::floorAlignToScreenPixel(kText * (item.xf + BorderPixels + 3)); + float ty = Gui::floorAlignToScreenPixel(kText * (item.yf + BorderPixels + 3)); + mc->gui.renderSlotText(citem, tx, ty, true, true); + } + t.resetScale(); + glEnable2(GL_BLEND); + t.endOverrideAndDraw(); + } + + if (renderDecorations) { + t.beginOverride(); + for (unsigned int i = 0; i < items.size(); ++i) { + GridItem& item = items[i]; + const ItemInstance* citem = inventoryItems[item.id]; + if (!citem || citem->isNull()) continue; + + if (citem->isDamaged()) { + ItemRenderer::renderGuiItemDecorations(citem, item.xf + 8, item.yf + 12); + } + } + + glDisable2(GL_TEXTURE_2D); + t.endOverrideAndDraw(); + glEnable2(GL_TEXTURE_2D); + } + glDisable2(GL_SCISSOR_TEST); + + //fillGradient(bbox.x - 1, bbox.y, bbox.x + bbox.w + 1, bbox.y + 20, 0x99000000, 0x00000000); + //fillGradient(bbox.x - 1, bbox.y + bbox.h - 20, bbox.x + bbox.w + 1, bbox.y + bbox.h, 0x00000000, 0x99000000); + fillGradient(bg.x - fillMarginX, bbox.y, bg.w + fillMarginX, bbox.y + 20, 0x99000000, 0x00000000); + fillGradient(bg.x - fillMarginX, bbox.y + bbox.h - 20, bg.w + fillMarginX, bbox.y + bbox.h, 0x00000000, 0x99000000); + + drawScrollBar(hScroll); + drawScrollBar(vScroll); +} + +bool InventoryPane::onSelect( int gridId, bool selected ) +{ + //screen->onItemSelected(gridId); + if (screen->isAllowed(gridId)) + if (screen->addItem(this, gridId)) { + lastItemIndex = gridId; + lastItemTicks = 7; + } + + return false; +} + +void InventoryPane::drawScrollBar( ScrollBar& sb ) { + if (sb.alpha <= 0) + return; + + const int color = ((int)(255.0f * sb.alpha) << 24) | 0xaaaaaa; + const float xx = (float)(bbox.x + bbox.w); + fill(xx - sb.w, sb.y, xx, sb.y + sb.h, color); +} + +void InventoryPane::tick() +{ + --lastItemTicks; + super::tick(); +} + +void InventoryPane::setRenderDecorations( bool value ) { + renderDecorations = value; +} + +} diff --git a/src/client/gui/components/InventoryPane.h b/src/client/gui/components/InventoryPane.h index c80f754..fd1e5ac 100755 --- a/src/client/gui/components/InventoryPane.h +++ b/src/client/gui/components/InventoryPane.h @@ -1,62 +1,62 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__InventoryPane_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__InventoryPane_H__ - -#include "ScrollingPane.h" -#include "ImageButton.h" - -class Minecraft; -class ItemInstance; -class Font; -class IArea; - -namespace Touch { - -class IInventoryPaneCallback; - -class InventoryPane: public ScrollingPane -{ - typedef ScrollingPane super; -public: - InventoryPane(IInventoryPaneCallback* screen, Minecraft* mc, const IntRectangle& rect, int paneWidth, float clickMarginH, int numItems, int itemSize, int itemBorderSize); - ~InventoryPane(); - - void tick(); - void renderBatch( std::vector& item, float alpha ); - bool onSelect( int gridId, bool selected ); - void drawScrollBar( ScrollBar& hScroll ); - - void setRenderDecorations(bool value); - - IntRectangle rect; - int paneWidth; - IArea* _clickArea; - IInventoryPaneCallback* screen; - Minecraft* mc; - - int fillMarginX; - int fillMarginY; - - int markerType; - int markerIndex; - float markerShare; -private: - int lastItemIndex; - int lastItemTicks; - int BorderPixels; - bool renderDecorations; - - IntRectangle bg; -}; - -class IInventoryPaneCallback -{ -public: - virtual ~IInventoryPaneCallback() {} - virtual bool addItem(const InventoryPane* forPane, int index) = 0; - virtual bool isAllowed( int slot ) = 0; - virtual std::vector getItems(const InventoryPane* forPane) = 0; -}; - -} - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__InventoryPane_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__InventoryPane_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__InventoryPane_H__ + +#include "ScrollingPane.h" +#include "ImageButton.h" + +class Minecraft; +class ItemInstance; +class Font; +class IArea; + +namespace Touch { + +class IInventoryPaneCallback; + +class InventoryPane: public ScrollingPane +{ + typedef ScrollingPane super; +public: + InventoryPane(IInventoryPaneCallback* screen, Minecraft* mc, const IntRectangle& rect, int paneWidth, float clickMarginH, int numItems, int itemSize, int itemBorderSize); + ~InventoryPane(); + + void tick(); + void renderBatch( std::vector& item, float alpha ); + bool onSelect( int gridId, bool selected ); + void drawScrollBar( ScrollBar& hScroll ); + + void setRenderDecorations(bool value); + + IntRectangle rect; + int paneWidth; + IArea* _clickArea; + IInventoryPaneCallback* screen; + Minecraft* mc; + + int fillMarginX; + int fillMarginY; + + int markerType; + int markerIndex; + float markerShare; +private: + int lastItemIndex; + int lastItemTicks; + int BorderPixels; + bool renderDecorations; + + IntRectangle bg; +}; + +class IInventoryPaneCallback +{ +public: + virtual ~IInventoryPaneCallback() {} + virtual bool addItem(const InventoryPane* forPane, int index) = 0; + virtual bool isAllowed( int slot ) = 0; + virtual std::vector getItems(const InventoryPane* forPane) = 0; +}; + +} + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__InventoryPane_H__*/ diff --git a/src/client/gui/components/ItemPane.cpp b/src/client/gui/components/ItemPane.cpp index e0ae8d2..41c32df 100755 --- a/src/client/gui/components/ItemPane.cpp +++ b/src/client/gui/components/ItemPane.cpp @@ -1,148 +1,148 @@ -#include "ItemPane.h" -#include "../Gui.h" -#include "../../renderer/gles.h" -#include "../../renderer/Tesselator.h" -#include "NinePatch.h" -#include "../../renderer/entity/ItemRenderer.h" - -const int rgbActive = 0xfff0f0f0; -const int rgbInactive = 0xc0635558; -const int rgbInactiveShadow = 0xc0aaaaaa; - -ItemPane::ItemPane( IItemPaneCallback* screen, - Textures* textures, - const IntRectangle& rect, - int numItems, - int guiHeight, - int physicalScreenHeight, - bool isVertical /*= true*/) -: super( - (isVertical?SF_LockX:SF_LockY)/*|SF_Scissor*/|SF_ShowScrollbar, - rect, // Pane rect - isVertical?IntRectangle(0, 0, rect.w, 22) // Item rect if vertical - :IntRectangle(0, 0, 32, rect.h), // Item rect if horizontal - isVertical?1:numItems, numItems, Gui::GuiScale), - screen(screen), - textures(textures), - physicalScreenHeight(physicalScreenHeight), - guiSlotItem(NULL), - guiSlotItemSelected(NULL), - isVertical(isVertical) -{ - // Expand the area to make it easier to scroll - area._x0 -= 4; - area._x1 += 4; - area._y0 = 0; - area._y1 = (float)guiHeight; - - // GUI - NinePatchFactory builder(textures, "gui/spritesheet.png"); - guiSlotItem = builder.createSymmetrical(IntRectangle(20, 32, 8, 8), 2, 2); - guiSlotItemSelected = builder.createSymmetrical(IntRectangle(28, 32, 8, 8), 2, 2); - guiSlotItem->setSize((float)rect.w + 4, 22); - guiSlotItemSelected->setSize((float)rect.w + 4, 22); -} - -ItemPane::~ItemPane() { - delete guiSlotItem; - delete guiSlotItemSelected; -} - -void ItemPane::renderBatch( std::vector& items, float alpha ) -{ - //fill(bbox.x, bbox.y, bbox.x + bbox.w, bbox.y + bbox.h, 0xff666666); - const std::vector& cat = screen->getItems(this); - if (cat.empty()) return; - - glEnable2(GL_SCISSOR_TEST); - GLuint x = (GLuint)(screenScale * bbox.x); - GLuint y = physicalScreenHeight - (GLuint)(screenScale * (bbox.y + bbox.h)); - GLuint w = (GLuint)(screenScale * bbox.w); - GLuint h = (GLuint)(screenScale * bbox.h); - glScissor(x, y, w, h); - - Tesselator& t = Tesselator::instance; - - t.beginOverride(); - for (unsigned int i = 0; i < items.size(); ++i) { - GridItem& item = items[i]; - (item.selected? guiSlotItemSelected : guiSlotItem)->draw(t, Gui::floorAlignToScreenPixel(item.xf-1), Gui::floorAlignToScreenPixel(item.yf)); - } - t.endOverrideAndDraw(); - - t.beginOverride(); - for (unsigned int i = 0; i < items.size(); ++i) { - GridItem& item = items[i]; - CItem* citem = cat[item.id]; - - ItemRenderer::renderGuiItem(NULL, textures, &citem->item, - Gui::floorAlignToScreenPixel(item.xf + itemBbox.w - 16), - Gui::floorAlignToScreenPixel(2 + item.yf), 16, 16, false); - } - t.endOverrideAndDraw(); - - t.beginOverride(); - for (unsigned int i = 0; i < items.size(); ++i) { - GridItem& item = items[i]; - CItem* citem = cat[item.id]; - - char buf[64] = {0}; - int c = Gui::itemCountItoa(buf, citem->inventoryCount); - - float xf = item.xf - 1; - if (citem->canCraft()) { - f->drawShadow(citem->text, - Gui::floorAlignToScreenPixel(xf + 2), - Gui::floorAlignToScreenPixel(item.yf + 6), rgbActive); - t.scale2d(0.6667f, 0.6667f); - f->drawShadow(buf, - Gui::floorAlignToScreenPixel(1.5f * (xf + itemBbox.w - c*4)), - Gui::floorAlignToScreenPixel(1.5f * (item.yf + itemBbox.h - 8)), rgbActive); - t.resetScale(); - } else { - f->draw(citem->text, - Gui::floorAlignToScreenPixel(xf + 3), - Gui::floorAlignToScreenPixel(item.yf + 7), rgbInactiveShadow); - f->draw(citem->text, - Gui::floorAlignToScreenPixel(xf + 2), - Gui::floorAlignToScreenPixel(item.yf + 6), rgbInactive); - t.scale2d(0.6667f, 0.6667f); - f->draw(buf, - Gui::floorAlignToScreenPixel(1.5f * (xf + itemBbox.w - c*4)), - Gui::floorAlignToScreenPixel(1.5f * (item.yf + itemBbox.h - 8)), rgbInactive); - t.resetScale(); - } - } - t.endOverrideAndDraw(); - - //fillGradient(bbox.x, bbox.y, bbox.x + bbox.w, 20, 0x00000000, 0x80ff0000) - if (isVertical) { - fillGradient(bbox.x, bbox.y, bbox.x + bbox.w, bbox.y + 28, 0xbb000000, 0x00000000); - fillGradient(bbox.x, bbox.y + bbox.h - 28, bbox.x + bbox.w, bbox.y + bbox.h, 0x00000000, 0xbb000000);//0xbb2A272B); - } else { - fillHorizontalGradient(bbox.x, bbox.y, bbox.x + 28, bbox.y + bbox.h, 0xbb000000, 0x00000000); - fillHorizontalGradient(bbox.x + bbox.w - 28, bbox.y, bbox.x + bbox.w, bbox.y + bbox.h, 0x00000000, 0xbb000000);//0xbb2A272B); - } - - //LOGI("scroll: %f - %f, %f :: %f, %f\n", hScroll.alpha, hScroll.x, hScroll.y, hScroll.w, hScroll.h); - glDisable2(GL_SCISSOR_TEST); - - drawScrollBar(hScroll); - drawScrollBar(vScroll); -} - -bool ItemPane::onSelect( int gridId, bool selected ) -{ - if (selected) - screen->onItemSelected(this, gridId); - - return selected; -} - -void ItemPane::drawScrollBar( ScrollBar& sb ) { - if (sb.alpha <= 0) - return; - - int color = ((int)(255.0f * sb.alpha) << 24) | 0xffffff; - fill(2 + sb.x, sb.y, 2 + sb.x + sb.w, sb.y + sb.h, color); -} +#include "ItemPane.h" +#include "../Gui.h" +#include "../../renderer/gles.h" +#include "../../renderer/Tesselator.h" +#include "NinePatch.h" +#include "../../renderer/entity/ItemRenderer.h" + +const int rgbActive = 0xfff0f0f0; +const int rgbInactive = 0xc0635558; +const int rgbInactiveShadow = 0xc0aaaaaa; + +ItemPane::ItemPane( IItemPaneCallback* screen, + Textures* textures, + const IntRectangle& rect, + int numItems, + int guiHeight, + int physicalScreenHeight, + bool isVertical /*= true*/) +: super( + (isVertical?SF_LockX:SF_LockY)/*|SF_Scissor*/|SF_ShowScrollbar, + rect, // Pane rect + isVertical?IntRectangle(0, 0, rect.w, 22) // Item rect if vertical + :IntRectangle(0, 0, 32, rect.h), // Item rect if horizontal + isVertical?1:numItems, numItems, Gui::GuiScale), + screen(screen), + textures(textures), + physicalScreenHeight(physicalScreenHeight), + guiSlotItem(NULL), + guiSlotItemSelected(NULL), + isVertical(isVertical) +{ + // Expand the area to make it easier to scroll + area._x0 -= 4; + area._x1 += 4; + area._y0 = 0; + area._y1 = (float)guiHeight; + + // GUI + NinePatchFactory builder(textures, "gui/spritesheet.png"); + guiSlotItem = builder.createSymmetrical(IntRectangle(20, 32, 8, 8), 2, 2); + guiSlotItemSelected = builder.createSymmetrical(IntRectangle(28, 32, 8, 8), 2, 2); + guiSlotItem->setSize((float)rect.w + 4, 22); + guiSlotItemSelected->setSize((float)rect.w + 4, 22); +} + +ItemPane::~ItemPane() { + delete guiSlotItem; + delete guiSlotItemSelected; +} + +void ItemPane::renderBatch( std::vector& items, float alpha ) +{ + //fill(bbox.x, bbox.y, bbox.x + bbox.w, bbox.y + bbox.h, 0xff666666); + const std::vector& cat = screen->getItems(this); + if (cat.empty()) return; + + glEnable2(GL_SCISSOR_TEST); + GLuint x = (GLuint)(screenScale * bbox.x); + GLuint y = physicalScreenHeight - (GLuint)(screenScale * (bbox.y + bbox.h)); + GLuint w = (GLuint)(screenScale * bbox.w); + GLuint h = (GLuint)(screenScale * bbox.h); + glScissor(x, y, w, h); + + Tesselator& t = Tesselator::instance; + + t.beginOverride(); + for (unsigned int i = 0; i < items.size(); ++i) { + GridItem& item = items[i]; + (item.selected? guiSlotItemSelected : guiSlotItem)->draw(t, Gui::floorAlignToScreenPixel(item.xf-1), Gui::floorAlignToScreenPixel(item.yf)); + } + t.endOverrideAndDraw(); + + t.beginOverride(); + for (unsigned int i = 0; i < items.size(); ++i) { + GridItem& item = items[i]; + CItem* citem = cat[item.id]; + + ItemRenderer::renderGuiItem(NULL, textures, &citem->item, + Gui::floorAlignToScreenPixel(item.xf + itemBbox.w - 16), + Gui::floorAlignToScreenPixel(2 + item.yf), 16, 16, false); + } + t.endOverrideAndDraw(); + + t.beginOverride(); + for (unsigned int i = 0; i < items.size(); ++i) { + GridItem& item = items[i]; + CItem* citem = cat[item.id]; + + char buf[64] = {0}; + int c = Gui::itemCountItoa(buf, citem->inventoryCount); + + float xf = item.xf - 1; + if (citem->canCraft()) { + f->drawShadow(citem->text, + Gui::floorAlignToScreenPixel(xf + 2), + Gui::floorAlignToScreenPixel(item.yf + 6), rgbActive); + t.scale2d(0.6667f, 0.6667f); + f->drawShadow(buf, + Gui::floorAlignToScreenPixel(1.5f * (xf + itemBbox.w - c*4)), + Gui::floorAlignToScreenPixel(1.5f * (item.yf + itemBbox.h - 8)), rgbActive); + t.resetScale(); + } else { + f->draw(citem->text, + Gui::floorAlignToScreenPixel(xf + 3), + Gui::floorAlignToScreenPixel(item.yf + 7), rgbInactiveShadow); + f->draw(citem->text, + Gui::floorAlignToScreenPixel(xf + 2), + Gui::floorAlignToScreenPixel(item.yf + 6), rgbInactive); + t.scale2d(0.6667f, 0.6667f); + f->draw(buf, + Gui::floorAlignToScreenPixel(1.5f * (xf + itemBbox.w - c*4)), + Gui::floorAlignToScreenPixel(1.5f * (item.yf + itemBbox.h - 8)), rgbInactive); + t.resetScale(); + } + } + t.endOverrideAndDraw(); + + //fillGradient(bbox.x, bbox.y, bbox.x + bbox.w, 20, 0x00000000, 0x80ff0000) + if (isVertical) { + fillGradient(bbox.x, bbox.y, bbox.x + bbox.w, bbox.y + 28, 0xbb000000, 0x00000000); + fillGradient(bbox.x, bbox.y + bbox.h - 28, bbox.x + bbox.w, bbox.y + bbox.h, 0x00000000, 0xbb000000);//0xbb2A272B); + } else { + fillHorizontalGradient(bbox.x, bbox.y, bbox.x + 28, bbox.y + bbox.h, 0xbb000000, 0x00000000); + fillHorizontalGradient(bbox.x + bbox.w - 28, bbox.y, bbox.x + bbox.w, bbox.y + bbox.h, 0x00000000, 0xbb000000);//0xbb2A272B); + } + + //LOGI("scroll: %f - %f, %f :: %f, %f\n", hScroll.alpha, hScroll.x, hScroll.y, hScroll.w, hScroll.h); + glDisable2(GL_SCISSOR_TEST); + + drawScrollBar(hScroll); + drawScrollBar(vScroll); +} + +bool ItemPane::onSelect( int gridId, bool selected ) +{ + if (selected) + screen->onItemSelected(this, gridId); + + return selected; +} + +void ItemPane::drawScrollBar( ScrollBar& sb ) { + if (sb.alpha <= 0) + return; + + int color = ((int)(255.0f * sb.alpha) << 24) | 0xffffff; + fill(2 + sb.x, sb.y, 2 + sb.x + sb.w, sb.y + sb.h, color); +} diff --git a/src/client/gui/components/ItemPane.h b/src/client/gui/components/ItemPane.h index ebb0f31..4428f59 100755 --- a/src/client/gui/components/ItemPane.h +++ b/src/client/gui/components/ItemPane.h @@ -1,95 +1,95 @@ -#ifndef ITEMPANE_H__ -#define ITEMPANE_H__ - -#include -#include -#include "ScrollingPane.h" -#include "../../../world/item/ItemInstance.h" - -class Font; -class Textures; -class NinePatchLayer; -class Recipe; -class ItemPane; - -class CItem -{ -public: - CItem(const ItemInstance& ins, Recipe* recipe, const std::string& text) - : item(ins), - recipe(recipe), - text(text), - sortText(text), - //maxBuildCount(0), - numBuilt(0), - inventoryCount(0), - _canCraft(false) - { - } - - typedef struct ReqItem { - ReqItem() {} - ReqItem(const ItemInstance& needItem, int has) - : item(needItem), has(has) {} - ItemInstance item; - int has; - bool enough() { return has >= item.count; } - } ReqItem; - - bool canCraft() { - return _canCraft;// || maxBuildCount > 0; - } - void setCanCraft(bool status) { - _canCraft = status; - } - - ItemInstance item; - Recipe* recipe; - std::string text; - std::string sortText; - //int maxBuildCount; - int numBuilt; - int inventoryCount; - std::vector neededItems; -private: - bool _canCraft; -}; - -class IItemPaneCallback -{ -public: - virtual ~IItemPaneCallback() {} - virtual void onItemSelected(const ItemPane* forPane, int index) = 0; - virtual const std::vector& getItems(const ItemPane* forPane) = 0; -}; - -class ItemPane: public ScrollingPane -{ - typedef ScrollingPane super; -public: - ItemPane( IItemPaneCallback* screen, - Textures* textures, - const IntRectangle& rect, - int numItems, - int guiHeight, - int physicalScreenHeight, - bool isVertical = true); - ~ItemPane(); - - void renderBatch( std::vector& item, float alpha ); - bool onSelect( int gridId, bool selected ); - void drawScrollBar( ScrollBar& hScroll ); - //void setSize() - - Font* f; - Textures* textures; - IItemPaneCallback* screen; - - int physicalScreenHeight; // Needed for glScissor - bool isVertical; - - NinePatchLayer* guiSlotItem; - NinePatchLayer* guiSlotItemSelected; -}; - -#endif /*ITEMPANE_H__*/ +#ifndef ITEMPANE_H__ +#define ITEMPANE_H__ + +#include +#include +#include "ScrollingPane.h" +#include "../../../world/item/ItemInstance.h" + +class Font; +class Textures; +class NinePatchLayer; +class Recipe; +class ItemPane; + +class CItem +{ +public: + CItem(const ItemInstance& ins, Recipe* recipe, const std::string& text) + : item(ins), + recipe(recipe), + text(text), + sortText(text), + //maxBuildCount(0), + numBuilt(0), + inventoryCount(0), + _canCraft(false) + { + } + + typedef struct ReqItem { + ReqItem() {} + ReqItem(const ItemInstance& needItem, int has) + : item(needItem), has(has) {} + ItemInstance item; + int has; + bool enough() { return has >= item.count; } + } ReqItem; + + bool canCraft() { + return _canCraft;// || maxBuildCount > 0; + } + void setCanCraft(bool status) { + _canCraft = status; + } + + ItemInstance item; + Recipe* recipe; + std::string text; + std::string sortText; + //int maxBuildCount; + int numBuilt; + int inventoryCount; + std::vector neededItems; +private: + bool _canCraft; +}; + +class IItemPaneCallback +{ +public: + virtual ~IItemPaneCallback() {} + virtual void onItemSelected(const ItemPane* forPane, int index) = 0; + virtual const std::vector& getItems(const ItemPane* forPane) = 0; +}; + +class ItemPane: public ScrollingPane +{ + typedef ScrollingPane super; +public: + ItemPane( IItemPaneCallback* screen, + Textures* textures, + const IntRectangle& rect, + int numItems, + int guiHeight, + int physicalScreenHeight, + bool isVertical = true); + ~ItemPane(); + + void renderBatch( std::vector& item, float alpha ); + bool onSelect( int gridId, bool selected ); + void drawScrollBar( ScrollBar& hScroll ); + //void setSize() + + Font* f; + Textures* textures; + IItemPaneCallback* screen; + + int physicalScreenHeight; // Needed for glScissor + bool isVertical; + + NinePatchLayer* guiSlotItem; + NinePatchLayer* guiSlotItemSelected; +}; + +#endif /*ITEMPANE_H__*/ diff --git a/src/client/gui/components/KeyOption.h b/src/client/gui/components/KeyOption.h index cc86958..fabdc7a 100644 --- a/src/client/gui/components/KeyOption.h +++ b/src/client/gui/components/KeyOption.h @@ -1,6 +1,6 @@ #pragma once #include "Button.h" -#include "Options.h" +#include class KeyOption : public Touch::TButton { public: @@ -11,4 +11,4 @@ public: virtual void keyPressed(Minecraft* minecraft, int key); protected: bool m_captureMode; -}; +}; \ No newline at end of file diff --git a/src/client/gui/components/LargeImageButton.cpp b/src/client/gui/components/LargeImageButton.cpp index db1f599..31211d1 100755 --- a/src/client/gui/components/LargeImageButton.cpp +++ b/src/client/gui/components/LargeImageButton.cpp @@ -1,104 +1,104 @@ -#include "LargeImageButton.h" -#include "../../renderer/Tesselator.h" -#include "../../Minecraft.h" -#include "../../../util/Mth.h" -#include "../../../platform/log.h" -#include "../../../util/Mth.h" -#include "../../renderer/Textures.h" - - -LargeImageButton::LargeImageButton(int id, const std::string& msg) -: super(id, msg) -{ - setupDefault(); -} - -LargeImageButton::LargeImageButton(int id, const std::string& msg, ImageDef& imagedef) -: super(id, msg) -{ - _imageDef = imagedef; - setupDefault(); -} - -void LargeImageButton::setupDefault() { - _buttonScale = 1; - width = 72; - height = 72; -} - -void LargeImageButton::render(Minecraft* minecraft, int xm, int ym) { - if (!visible) return; - - Font* font = minecraft->font; - - //minecraft->textures->loadAndBindTexture("gui/gui.png"); - glColor4f2(1, 1, 1, 1); - bool hovered = active && (minecraft->useTouchscreen()? (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); - - //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); - //int yImage = getYImage(hovered || selected); - - //blit(x, y, 0, 46 + yImage * 20, w / 2, h, 0, 20); - //blit(x + w / 2, y, 200 - w / 2, 46 + yImage * 20, w / 2, h, 0, 20); - - renderBg(minecraft, xm, ym); - - TextureId texId = (_imageDef.name.length() > 0)? minecraft->textures->loadAndBindTexture(_imageDef.name) : Textures::InvalidId; - if ( Textures::isTextureIdValid(texId) ) { - const ImageDef& d = _imageDef; - Tesselator& t = Tesselator::instance; - - t.begin(); - if (!active) t.color(0xff808080); - //else if (hovered||selected) t.color(0xffffffff); - //else t.color(0xffe0e0e0); - else t.color(0xffffffff); - - float hx = ((float) d.width) * 0.5f; - float hy = ((float) d.height) * 0.5f; - const float cx = ((float)x+d.x) + hx; - const float cy = ((float)y+d.y) + hy; - - if (hovered) - _buttonScale = Mth::Max(0.95f, _buttonScale-0.025f); - else - _buttonScale = Mth::Min(1.00f, _buttonScale+0.025f); - - hx *= _buttonScale; - hy *= _buttonScale; - - const IntRectangle* src = _imageDef.getSrc(); - if (src) { - const TextureData* d = minecraft->textures->getTemporaryTextureData(texId); - if (d != NULL) { - float u0 = (src->x+(hovered?src->w:0)) / (float)d->w; - float u1 = (src->x+(hovered?2*src->w:src->w)) / (float)d->w; - float v0 = src->y / (float)d->h; - float v1 = (src->y+src->h) / (float)d->h; - t.vertexUV(cx-hx, cy-hy, blitOffset, u0, v0); - t.vertexUV(cx-hx, cy+hy, blitOffset, u0, v1); - t.vertexUV(cx+hx, cy+hy, blitOffset, u1, v1); - t.vertexUV(cx+hx, cy-hy, blitOffset, u1, v0); - } - } else { - t.vertexUV(cx-hx, cy-hy, blitOffset, 0, 0); - t.vertexUV(cx-hx, cy+hy, blitOffset, 0, 1); - t.vertexUV(cx+hx, cy+hy, blitOffset, 1, 1); - t.vertexUV(cx+hx, cy-hy, blitOffset, 1, 0); - } - t.draw(); - } - //blit(0, 0, 0, 0, 64, 64, 256, 256); - - //LOGI("%d %d\n", x+d.x, x+d.x+d.w); - - if (!active) { - drawCenteredString(font, msg, x + width / 2, y + 11/*(h - 16)*/, 0xffa0a0a0); - } else { - if (hovered || selected) { - drawCenteredString(font, msg, x + width / 2, y + 11/*(h - 16)*/, 0xffffa0); - } else { - drawCenteredString(font, msg, x + width / 2, y + 11/*(h - 48)*/, 0xe0e0e0); - } - } -} +#include "LargeImageButton.h" +#include "../../renderer/Tesselator.h" +#include "../../Minecraft.h" +#include "../../../util/Mth.h" +#include "../../../platform/log.h" +#include "../../../util/Mth.h" +#include "../../renderer/Textures.h" + + +LargeImageButton::LargeImageButton(int id, const std::string& msg) +: super(id, msg) +{ + setupDefault(); +} + +LargeImageButton::LargeImageButton(int id, const std::string& msg, ImageDef& imagedef) +: super(id, msg) +{ + _imageDef = imagedef; + setupDefault(); +} + +void LargeImageButton::setupDefault() { + _buttonScale = 1; + width = 72; + height = 72; +} + +void LargeImageButton::render(Minecraft* minecraft, int xm, int ym) { + if (!visible) return; + + Font* font = minecraft->font; + + //minecraft->textures->loadAndBindTexture("gui/gui.png"); + glColor4f2(1, 1, 1, 1); + bool hovered = active && (minecraft->useTouchscreen()? (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); + + //printf("ButtonId: %d - Hovered? %d (cause: %d, %d, %d, %d, <> %d, %d)\n", id, hovered, x, y, x+w, y+h, xm, ym); + //int yImage = getYImage(hovered || selected); + + //blit(x, y, 0, 46 + yImage * 20, w / 2, h, 0, 20); + //blit(x + w / 2, y, 200 - w / 2, 46 + yImage * 20, w / 2, h, 0, 20); + + renderBg(minecraft, xm, ym); + + TextureId texId = (_imageDef.name.length() > 0)? minecraft->textures->loadAndBindTexture(_imageDef.name) : Textures::InvalidId; + if ( Textures::isTextureIdValid(texId) ) { + const ImageDef& d = _imageDef; + Tesselator& t = Tesselator::instance; + + t.begin(); + if (!active) t.color(0xff808080); + //else if (hovered||selected) t.color(0xffffffff); + //else t.color(0xffe0e0e0); + else t.color(0xffffffff); + + float hx = ((float) d.width) * 0.5f; + float hy = ((float) d.height) * 0.5f; + const float cx = ((float)x+d.x) + hx; + const float cy = ((float)y+d.y) + hy; + + if (hovered) + _buttonScale = Mth::Max(0.95f, _buttonScale-0.025f); + else + _buttonScale = Mth::Min(1.00f, _buttonScale+0.025f); + + hx *= _buttonScale; + hy *= _buttonScale; + + const IntRectangle* src = _imageDef.getSrc(); + if (src) { + const TextureData* d = minecraft->textures->getTemporaryTextureData(texId); + if (d != NULL) { + float u0 = (src->x+(hovered?src->w:0)) / (float)d->w; + float u1 = (src->x+(hovered?2*src->w:src->w)) / (float)d->w; + float v0 = src->y / (float)d->h; + float v1 = (src->y+src->h) / (float)d->h; + t.vertexUV(cx-hx, cy-hy, blitOffset, u0, v0); + t.vertexUV(cx-hx, cy+hy, blitOffset, u0, v1); + t.vertexUV(cx+hx, cy+hy, blitOffset, u1, v1); + t.vertexUV(cx+hx, cy-hy, blitOffset, u1, v0); + } + } else { + t.vertexUV(cx-hx, cy-hy, blitOffset, 0, 0); + t.vertexUV(cx-hx, cy+hy, blitOffset, 0, 1); + t.vertexUV(cx+hx, cy+hy, blitOffset, 1, 1); + t.vertexUV(cx+hx, cy-hy, blitOffset, 1, 0); + } + t.draw(); + } + //blit(0, 0, 0, 0, 64, 64, 256, 256); + + //LOGI("%d %d\n", x+d.x, x+d.x+d.w); + + if (!active) { + drawCenteredString(font, msg, x + width / 2, y + 11/*(h - 16)*/, 0xffa0a0a0); + } else { + if (hovered || selected) { + drawCenteredString(font, msg, x + width / 2, y + 11/*(h - 16)*/, 0xffffa0); + } else { + drawCenteredString(font, msg, x + width / 2, y + 11/*(h - 48)*/, 0xe0e0e0); + } + } +} diff --git a/src/client/gui/components/LargeImageButton.h b/src/client/gui/components/LargeImageButton.h index 9a06d53..d7579f9 100755 --- a/src/client/gui/components/LargeImageButton.h +++ b/src/client/gui/components/LargeImageButton.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__LargeImageButton_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__LargeImageButton_H__ - -#include "ImageButton.h" - -class LargeImageButton: public ImageButton -{ - typedef ImageButton super; -public: - LargeImageButton(int id, const std::string& msg); - LargeImageButton(int id, const std::string& msg, ImageDef& imageDef); - - void render(Minecraft* minecraft, int xm, int ym); - -private: - void setupDefault(); - - float _buttonScale; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__LargeImageButton_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__LargeImageButton_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__LargeImageButton_H__ + +#include "ImageButton.h" + +class LargeImageButton: public ImageButton +{ + typedef ImageButton super; +public: + LargeImageButton(int id, const std::string& msg); + LargeImageButton(int id, const std::string& msg, ImageDef& imageDef); + + void render(Minecraft* minecraft, int xm, int ym); + +private: + void setupDefault(); + + float _buttonScale; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__LargeImageButton_H__*/ diff --git a/src/client/gui/components/NinePatch.cpp b/src/client/gui/components/NinePatch.cpp index a82b937..a2ea1e3 100755 --- a/src/client/gui/components/NinePatch.cpp +++ b/src/client/gui/components/NinePatch.cpp @@ -1,141 +1,141 @@ -#include "NinePatch.h" - -NinePatchDescription::NinePatchDescription( float x, float y, float x1, float x2, float x3, float y1, float y2, float y3, float w, float e, float n, float s ) : u0(x), u1(x + x1), u2(x + x2), u3(x + x3), - v0(y), v1(y + y1), v2(y + y2), v3(y + y3), - w(w), e(e), n(n), s(s), - imgW(-1), - imgH(-1) { - -} - -NinePatchDescription& NinePatchDescription::transformUVForImage( const TextureData& d ) { - return transformUVForImageSize(d.w, d.h); -} - -NinePatchDescription& NinePatchDescription::transformUVForImageSize( int w, int h ) { - if (imgW < 0) - imgW = imgH = 1; - - const float us = (float) imgW / w; // @todo: prepare for normal blit? (e.g. mult by 256) - const float vs = (float) imgH / h; - u0 *= us; u1 *= us; u2 *= us; u3 *= us; - v0 *= vs; v1 *= vs; v2 *= vs; v3 *= vs; - - imgW = w; - imgH = h; - - return *this; -} - -NinePatchDescription NinePatchDescription::createSymmetrical( int texWidth, int texHeight, const IntRectangle& src, int xCutAt, int yCutAt ) { - NinePatchDescription patch((float)src.x, (float)src.y,// width and height of src - (float)xCutAt, (float)(src.w-xCutAt), (float)src.w, // u tex coordinates - (float)yCutAt, (float)(src.h-yCutAt), (float)src.h, // v tex coordinates - (float)xCutAt, (float)xCutAt, (float)yCutAt, (float)yCutAt); // border width and heights - if (texWidth > 0) patch.transformUVForImageSize(texWidth, texHeight); - return patch; -} - -NinePatchLayer::NinePatchLayer(const NinePatchDescription& desc, const std::string& imageName, Textures* textures, float w, float h) - : desc(desc), - imageName(imageName), - textures(textures), - w(-1), h(-1), - excluded(0) -{ - setSize(w, h); -} - -void NinePatchLayer::setSize( float w, float h ) { - if (w == this->w && h == this->h) - return; - - this->w = w; - this->h = h; - - for (int i = 0; i < 9; ++i) - buildQuad(i); -} - -void NinePatchLayer::draw( Tesselator& t, float x, float y ) { - textures->loadAndBindTexture(imageName); - t.begin(); - t.addOffset(x, y, 0); - for (int i = 0, b = 1; i < 9; ++i, b += b) - if ((b & excluded) == 0) - d(t, quads[i]); - t.addOffset(-x, -y, 0); - t.draw(); -} - -NinePatchLayer* NinePatchLayer::exclude( int excludeId ) { - return setExcluded(excluded | (1 << excludeId)); -} - -NinePatchLayer* NinePatchLayer::setExcluded( int exludeBits ) { - excluded = exludeBits; - return this; -} - -void NinePatchLayer::buildQuad( int qid ) { - //@attn; fix - CachedQuad& q = quads[qid]; - const int yid = qid / 3; - const int xid = qid - 3 * yid; - q.u0 = (&desc.u0)[xid]; - q.u1 = (&desc.u0)[xid + 1]; - q.v0 = (&desc.v0)[yid]; - q.v1 = (&desc.v0)[yid + 1]; - q.z = 0; - getPatchInfo(xid, yid, q.x0, q.x1, q.y0, q.y1); - /* q.x0 = w * (q.u0 - desc.u0); - q.y0 = h * (q.v0 - desc.v0); - q.x1 = w * (q.u1 - desc.u0); - q.y1 = h * (q.v1 - desc.v0); - */ -} - -void NinePatchLayer::getPatchInfo( int xc, int yc, float& x0, float& x1, float& y0, float& y1 ) { - if (xc == 0) { x0 = 0; x1 = desc.w; } - else if (xc == 1) { x0 = desc.w; x1 = w - desc.e; } - else if (xc == 2) { x0 = w-desc.e; x1 = w; } - if (yc == 0) { y0 = 0; y1 = desc.n; } - else if (yc == 1) { y0 = desc.n; y1 = h - desc.s; } - else if (yc == 2) { y0 = h-desc.s; y1 = h; } -} - -void NinePatchLayer::d( Tesselator& t, const CachedQuad& q ) { - /* - t.vertexUV(x , y + h, blitOffset, (float)(sx ), (float)(sy + sh)); - t.vertexUV(x + w, y + h, blitOffset, (float)(sx + sw), (float)(sy + sh)); - t.vertexUV(x + w, y , blitOffset, (float)(sx + sw), (float)(sy )); - t.vertexUV(x , y , blitOffset, (float)(sx ), (float)(sy )); - */ - - t.vertexUV(q.x0, q.y1, q.z, q.u0, q.v1); - t.vertexUV(q.x1, q.y1, q.z, q.u1, q.v1); - t.vertexUV(q.x1, q.y0, q.z, q.u1, q.v0); - t.vertexUV(q.x0, q.y0, q.z, q.u0, q.v0); -} - -NinePatchFactory::NinePatchFactory( Textures* textures, const std::string& imageName ) : textures(textures), - imageName(imageName), - width(1), - height(1) { - TextureId id = textures->loadTexture(imageName); - if (id != Textures::InvalidId) { - const TextureData* data = textures->getTemporaryTextureData(id); - if (data) { // This should never be false - width = data->w; - height = data->h; - } - } else { - LOGE("Error @ NinePatchFactory::ctor - Couldn't find texture: %s\n", imageName.c_str()); - } -} - -NinePatchLayer* NinePatchFactory::createSymmetrical( const IntRectangle& src, int xCutAt, int yCutAt, float w /*= 32.0f*/, float h /*= 32.0f*/ ) { - return new NinePatchLayer( - NinePatchDescription::createSymmetrical(width, height, src, xCutAt, yCutAt), - imageName, textures, w, h); -} +#include "NinePatch.h" + +NinePatchDescription::NinePatchDescription( float x, float y, float x1, float x2, float x3, float y1, float y2, float y3, float w, float e, float n, float s ) : u0(x), u1(x + x1), u2(x + x2), u3(x + x3), + v0(y), v1(y + y1), v2(y + y2), v3(y + y3), + w(w), e(e), n(n), s(s), + imgW(-1), + imgH(-1) { + +} + +NinePatchDescription& NinePatchDescription::transformUVForImage( const TextureData& d ) { + return transformUVForImageSize(d.w, d.h); +} + +NinePatchDescription& NinePatchDescription::transformUVForImageSize( int w, int h ) { + if (imgW < 0) + imgW = imgH = 1; + + const float us = (float) imgW / w; // @todo: prepare for normal blit? (e.g. mult by 256) + const float vs = (float) imgH / h; + u0 *= us; u1 *= us; u2 *= us; u3 *= us; + v0 *= vs; v1 *= vs; v2 *= vs; v3 *= vs; + + imgW = w; + imgH = h; + + return *this; +} + +NinePatchDescription NinePatchDescription::createSymmetrical( int texWidth, int texHeight, const IntRectangle& src, int xCutAt, int yCutAt ) { + NinePatchDescription patch((float)src.x, (float)src.y,// width and height of src + (float)xCutAt, (float)(src.w-xCutAt), (float)src.w, // u tex coordinates + (float)yCutAt, (float)(src.h-yCutAt), (float)src.h, // v tex coordinates + (float)xCutAt, (float)xCutAt, (float)yCutAt, (float)yCutAt); // border width and heights + if (texWidth > 0) patch.transformUVForImageSize(texWidth, texHeight); + return patch; +} + +NinePatchLayer::NinePatchLayer(const NinePatchDescription& desc, const std::string& imageName, Textures* textures, float w, float h) + : desc(desc), + imageName(imageName), + textures(textures), + w(-1), h(-1), + excluded(0) +{ + setSize(w, h); +} + +void NinePatchLayer::setSize( float w, float h ) { + if (w == this->w && h == this->h) + return; + + this->w = w; + this->h = h; + + for (int i = 0; i < 9; ++i) + buildQuad(i); +} + +void NinePatchLayer::draw( Tesselator& t, float x, float y ) { + textures->loadAndBindTexture(imageName); + t.begin(); + t.addOffset(x, y, 0); + for (int i = 0, b = 1; i < 9; ++i, b += b) + if ((b & excluded) == 0) + d(t, quads[i]); + t.addOffset(-x, -y, 0); + t.draw(); +} + +NinePatchLayer* NinePatchLayer::exclude( int excludeId ) { + return setExcluded(excluded | (1 << excludeId)); +} + +NinePatchLayer* NinePatchLayer::setExcluded( int exludeBits ) { + excluded = exludeBits; + return this; +} + +void NinePatchLayer::buildQuad( int qid ) { + //@attn; fix + CachedQuad& q = quads[qid]; + const int yid = qid / 3; + const int xid = qid - 3 * yid; + q.u0 = (&desc.u0)[xid]; + q.u1 = (&desc.u0)[xid + 1]; + q.v0 = (&desc.v0)[yid]; + q.v1 = (&desc.v0)[yid + 1]; + q.z = 0; + getPatchInfo(xid, yid, q.x0, q.x1, q.y0, q.y1); + /* q.x0 = w * (q.u0 - desc.u0); + q.y0 = h * (q.v0 - desc.v0); + q.x1 = w * (q.u1 - desc.u0); + q.y1 = h * (q.v1 - desc.v0); + */ +} + +void NinePatchLayer::getPatchInfo( int xc, int yc, float& x0, float& x1, float& y0, float& y1 ) { + if (xc == 0) { x0 = 0; x1 = desc.w; } + else if (xc == 1) { x0 = desc.w; x1 = w - desc.e; } + else if (xc == 2) { x0 = w-desc.e; x1 = w; } + if (yc == 0) { y0 = 0; y1 = desc.n; } + else if (yc == 1) { y0 = desc.n; y1 = h - desc.s; } + else if (yc == 2) { y0 = h-desc.s; y1 = h; } +} + +void NinePatchLayer::d( Tesselator& t, const CachedQuad& q ) { + /* + t.vertexUV(x , y + h, blitOffset, (float)(sx ), (float)(sy + sh)); + t.vertexUV(x + w, y + h, blitOffset, (float)(sx + sw), (float)(sy + sh)); + t.vertexUV(x + w, y , blitOffset, (float)(sx + sw), (float)(sy )); + t.vertexUV(x , y , blitOffset, (float)(sx ), (float)(sy )); + */ + + t.vertexUV(q.x0, q.y1, q.z, q.u0, q.v1); + t.vertexUV(q.x1, q.y1, q.z, q.u1, q.v1); + t.vertexUV(q.x1, q.y0, q.z, q.u1, q.v0); + t.vertexUV(q.x0, q.y0, q.z, q.u0, q.v0); +} + +NinePatchFactory::NinePatchFactory( Textures* textures, const std::string& imageName ) : textures(textures), + imageName(imageName), + width(1), + height(1) { + TextureId id = textures->loadTexture(imageName); + if (id != Textures::InvalidId) { + const TextureData* data = textures->getTemporaryTextureData(id); + if (data) { // This should never be false + width = data->w; + height = data->h; + } + } else { + LOGE("Error @ NinePatchFactory::ctor - Couldn't find texture: %s\n", imageName.c_str()); + } +} + +NinePatchLayer* NinePatchFactory::createSymmetrical( const IntRectangle& src, int xCutAt, int yCutAt, float w /*= 32.0f*/, float h /*= 32.0f*/ ) { + return new NinePatchLayer( + NinePatchDescription::createSymmetrical(width, height, src, xCutAt, yCutAt), + imageName, textures, w, h); +} diff --git a/src/client/gui/components/NinePatch.h b/src/client/gui/components/NinePatch.h index 3403f2b..16f2211 100755 --- a/src/client/gui/components/NinePatch.h +++ b/src/client/gui/components/NinePatch.h @@ -1,78 +1,78 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__NinePatch_H__ -#define NET_MINECRAFT_CLIENT_GUI__NinePatch_H__ - -#include "ImageButton.h" -#include "../../renderer/TextureData.h" -#include "../../renderer/Textures.h" -#include "../../renderer/Tesselator.h" -#include "../../Minecraft.h" - -class Tesselator; - -class NinePatchDescription { -public: - NinePatchDescription& transformUVForImage(const TextureData& d); - NinePatchDescription& transformUVForImageSize(int w, int h); - - float u0, u1, u2, u3; - float v0, v1, v2, v3; - float w, e, n, s; - - static NinePatchDescription createSymmetrical(int texWidth, int texHeight, const IntRectangle& src, int xCutAt, int yCutAt); -private: - NinePatchDescription( float x, float y, float x1, float x2, float x3, float y1, float y2, float y3, - float w, float e, float n, float s); - - int imgW; - int imgH; -}; - -class NinePatchLayer: public GuiElement -{ - struct CachedQuad; -public: - NinePatchLayer(const NinePatchDescription& desc, const std::string& imageName, Textures* textures, float w = 32, float h = 32); - virtual ~NinePatchLayer() {}; - void setSize(float w, float h); - - void draw(Tesselator& t, float x, float y); - - NinePatchLayer* exclude(int excludeId); - NinePatchLayer* setExcluded(int exludeBits); - - float getWidth() { return w; } - float getHeight() { return h; } - -private: - void buildQuad(int qid); - void getPatchInfo(int xc, int yc, float& x0, float& x1, float& y0, float& y1); - - void d(Tesselator& t, const CachedQuad& q); - - float w, h; - NinePatchDescription desc; - std::string imageName; - Textures* textures; - int excluded; - - typedef struct CachedQuad { - float x0, x1, y0, y1, z; - float u0, u1, v0, v1; - } CachedQuad; - CachedQuad quads[9]; -}; - -class NinePatchFactory { -public: - NinePatchFactory(Textures* textures, const std::string& imageName ); - - NinePatchLayer* createSymmetrical(const IntRectangle& src, int xCutAt, int yCutAt, float w = 32.0f, float h = 32.0f); - -private: - Textures* textures; - std::string imageName; - int width; - int height; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI__NinePatch_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__NinePatch_H__ +#define NET_MINECRAFT_CLIENT_GUI__NinePatch_H__ + +#include "ImageButton.h" +#include "../../renderer/TextureData.h" +#include "../../renderer/Textures.h" +#include "../../renderer/Tesselator.h" +#include "../../Minecraft.h" + +class Tesselator; + +class NinePatchDescription { +public: + NinePatchDescription& transformUVForImage(const TextureData& d); + NinePatchDescription& transformUVForImageSize(int w, int h); + + float u0, u1, u2, u3; + float v0, v1, v2, v3; + float w, e, n, s; + + static NinePatchDescription createSymmetrical(int texWidth, int texHeight, const IntRectangle& src, int xCutAt, int yCutAt); +private: + NinePatchDescription( float x, float y, float x1, float x2, float x3, float y1, float y2, float y3, + float w, float e, float n, float s); + + int imgW; + int imgH; +}; + +class NinePatchLayer: public GuiElement +{ + struct CachedQuad; +public: + NinePatchLayer(const NinePatchDescription& desc, const std::string& imageName, Textures* textures, float w = 32, float h = 32); + virtual ~NinePatchLayer() {}; + void setSize(float w, float h); + + void draw(Tesselator& t, float x, float y); + + NinePatchLayer* exclude(int excludeId); + NinePatchLayer* setExcluded(int exludeBits); + + float getWidth() { return w; } + float getHeight() { return h; } + +private: + void buildQuad(int qid); + void getPatchInfo(int xc, int yc, float& x0, float& x1, float& y0, float& y1); + + void d(Tesselator& t, const CachedQuad& q); + + float w, h; + NinePatchDescription desc; + std::string imageName; + Textures* textures; + int excluded; + + typedef struct CachedQuad { + float x0, x1, y0, y1, z; + float u0, u1, v0, v1; + } CachedQuad; + CachedQuad quads[9]; +}; + +class NinePatchFactory { +public: + NinePatchFactory(Textures* textures, const std::string& imageName ); + + NinePatchLayer* createSymmetrical(const IntRectangle& src, int xCutAt, int yCutAt, float w = 32.0f, float h = 32.0f); + +private: + Textures* textures; + std::string imageName; + int width; + int height; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI__NinePatch_H__*/ diff --git a/src/client/gui/components/OptionsGroup.cpp b/src/client/gui/components/OptionsGroup.cpp index 54212d5..fdfd8cb 100755 --- a/src/client/gui/components/OptionsGroup.cpp +++ b/src/client/gui/components/OptionsGroup.cpp @@ -1,115 +1,115 @@ -#include "OptionsGroup.h" -#include "../../Minecraft.h" -#include "ImageButton.h" -#include "OptionsItem.h" -#include "Slider.h" -#include "../../../locale/I18n.h" -#include "TextOption.h" -#include "KeyOption.h" - -OptionsGroup::OptionsGroup( std::string labelID ) { - label = I18n::get(labelID); -} - -void OptionsGroup::setupPositions() { - // First we write the header and then we add the items - int curY = y + 18; - for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { - (*it)->width = width - 5; - - (*it)->y = curY; - (*it)->x = x + 10; - (*it)->setupPositions(); - curY += (*it)->height + 3; - } - height = curY; -} - -void OptionsGroup::render( Minecraft* minecraft, int xm, int ym ) { - float padX = 10.0f; - float padY = 5.0f; - - minecraft->font->draw(label, (float)x + padX, (float)y + padY, 0xffffffff, false); - - super::render(minecraft, xm, ym); -} - -OptionsGroup& OptionsGroup::addOptionItem(OptionId optId, Minecraft* minecraft ) { - auto option = minecraft->options.getOpt(optId); - - if (option == nullptr) return *this; - - // TODO: do a options key class to check it faster via dynamic_cast - if (option->getStringId().find("options.key") != std::string::npos) createKey(optId, minecraft); - else if (dynamic_cast(option)) createToggle(optId, minecraft); - else if (dynamic_cast(option)) createProgressSlider(optId, minecraft); - else if (dynamic_cast(option)) createStepSlider(optId, minecraft); - else if (dynamic_cast(option)) createTextbox(optId, minecraft); - - return *this; -} - -// TODO: wrap this copypaste shit into templates - -void OptionsGroup::createToggle(OptionId optId, Minecraft* minecraft ) { - ImageDef def; - - def.setSrc(IntRectangle(160, 206, 39, 20)); - def.name = "gui/touchgui.png"; - def.width = 39 * 0.7f; - def.height = 20 * 0.7f; - - OptionButton* element = new OptionButton(optId); - element->setImageDef(def, true); - element->updateImage(&minecraft->options); - - std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); - - OptionsItem* item = new OptionsItem(optId, itemLabel, element); - - addChild(item); - setupPositions(); -} - -void OptionsGroup::createProgressSlider(OptionId optId, Minecraft* minecraft ) { - Slider* element = new SliderFloat(minecraft, optId); - element->width = 100; - element->height = 20; - - std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); - OptionsItem* item = new OptionsItem(optId, itemLabel, element); - addChild(item); - setupPositions(); -} - -void OptionsGroup::createStepSlider(OptionId optId, Minecraft* minecraft ) { - Slider* element = new SliderInt(minecraft, optId); - element->width = 100; - element->height = 20; - std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); - OptionsItem* item = new OptionsItem(optId, itemLabel, element); - addChild(item); - setupPositions(); -} - -void OptionsGroup::createTextbox(OptionId optId, Minecraft* minecraft) { - TextBox* element = new TextOption(minecraft, optId); - element->width = 100; - element->height = 20; - - std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); - OptionsItem* item = new OptionsItem(optId, itemLabel, element); - addChild(item); - setupPositions(); -} - -void OptionsGroup::createKey(OptionId optId, Minecraft* minecraft) { - KeyOption* element = new KeyOption(minecraft, optId); - element->width = 50; - element->height = 20; - - std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); - OptionsItem* item = new OptionsItem(optId, itemLabel, element); - addChild(item); - setupPositions(); +#include "OptionsGroup.h" +#include "../../Minecraft.h" +#include "ImageButton.h" +#include "OptionsItem.h" +#include "Slider.h" +#include "../../../locale/I18n.h" +#include "TextOption.h" +#include "KeyOption.h" + +OptionsGroup::OptionsGroup( std::string labelID ) { + label = I18n::get(labelID); +} + +void OptionsGroup::setupPositions() { + // First we write the header and then we add the items + int curY = y + 18; + for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->width = width - 5; + + (*it)->y = curY; + (*it)->x = x + 10; + (*it)->setupPositions(); + curY += (*it)->height + 3; + } + height = curY; +} + +void OptionsGroup::render( Minecraft* minecraft, int xm, int ym ) { + float padX = 10.0f; + float padY = 5.0f; + + minecraft->font->draw(label, (float)x + padX, (float)y + padY, 0xffffffff, false); + + super::render(minecraft, xm, ym); +} + +OptionsGroup& OptionsGroup::addOptionItem(OptionId optId, Minecraft* minecraft ) { + auto option = minecraft->options.getOpt(optId); + + if (option == nullptr) return *this; + + // TODO: do a options key class to check it faster via dynamic_cast + if (option->getStringId().find("options.key") != std::string::npos) createKey(optId, minecraft); + else if (dynamic_cast(option)) createToggle(optId, minecraft); + else if (dynamic_cast(option)) createProgressSlider(optId, minecraft); + else if (dynamic_cast(option)) createStepSlider(optId, minecraft); + else if (dynamic_cast(option)) createTextbox(optId, minecraft); + + return *this; +} + +// TODO: wrap this copypaste shit into templates + +void OptionsGroup::createToggle(OptionId optId, Minecraft* minecraft ) { + ImageDef def; + + def.setSrc(IntRectangle(160, 206, 39, 20)); + def.name = "gui/touchgui.png"; + def.width = 39 * 0.7f; + def.height = 20 * 0.7f; + + OptionButton* element = new OptionButton(optId); + element->setImageDef(def, true); + element->updateImage(&minecraft->options); + + std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); + + OptionsItem* item = new OptionsItem(optId, itemLabel, element); + + addChild(item); + setupPositions(); +} + +void OptionsGroup::createProgressSlider(OptionId optId, Minecraft* minecraft ) { + Slider* element = new SliderFloat(minecraft, optId); + element->width = 100; + element->height = 20; + + std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); + OptionsItem* item = new OptionsItem(optId, itemLabel, element); + addChild(item); + setupPositions(); +} + +void OptionsGroup::createStepSlider(OptionId optId, Minecraft* minecraft ) { + Slider* element = new SliderInt(minecraft, optId); + element->width = 100; + element->height = 20; + std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); + OptionsItem* item = new OptionsItem(optId, itemLabel, element); + addChild(item); + setupPositions(); +} + +void OptionsGroup::createTextbox(OptionId optId, Minecraft* minecraft) { + TextBox* element = new TextOption(minecraft, optId); + element->width = 100; + element->height = 20; + + std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); + OptionsItem* item = new OptionsItem(optId, itemLabel, element); + addChild(item); + setupPositions(); +} + +void OptionsGroup::createKey(OptionId optId, Minecraft* minecraft) { + KeyOption* element = new KeyOption(minecraft, optId); + element->width = 50; + element->height = 20; + + std::string itemLabel = I18n::get(minecraft->options.getOpt(optId)->getStringId()); + OptionsItem* item = new OptionsItem(optId, itemLabel, element); + addChild(item); + setupPositions(); } \ No newline at end of file diff --git a/src/client/gui/components/OptionsGroup.h b/src/client/gui/components/OptionsGroup.h index 24d28bd..cfeeca2 100755 --- a/src/client/gui/components/OptionsGroup.h +++ b/src/client/gui/components/OptionsGroup.h @@ -1,32 +1,32 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsGroup_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsGroup_H__ - -//package net.minecraft.client.gui; - -#include -#include "GuiElementContainer.h" -#include "ScrollingPane.h" -#include "../../Options.h" - -class Font; -class Minecraft; - -class OptionsGroup: public GuiElementContainer { - typedef GuiElementContainer super; -public: - OptionsGroup(std::string labelID); - virtual void setupPositions(); - virtual void render(Minecraft* minecraft, int xm, int ym); - OptionsGroup& addOptionItem(OptionId optId, Minecraft* minecraft); -protected: - - void createToggle(OptionId optId, Minecraft* minecraft); - void createProgressSlider(OptionId optId, Minecraft* minecraft); - void createStepSlider(OptionId optId, Minecraft* minecraft); - void createTextbox(OptionId optId, Minecraft* minecraft); - void createKey(OptionId optId, Minecraft* minecraft); - - std::string label; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsGroup_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsGroup_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsGroup_H__ + +//package net.minecraft.client.gui; + +#include +#include "GuiElementContainer.h" +#include "ScrollingPane.h" +#include "../../Options.h" + +class Font; +class Minecraft; + +class OptionsGroup: public GuiElementContainer { + typedef GuiElementContainer super; +public: + OptionsGroup(std::string labelID); + virtual void setupPositions(); + virtual void render(Minecraft* minecraft, int xm, int ym); + OptionsGroup& addOptionItem(OptionId optId, Minecraft* minecraft); +protected: + + void createToggle(OptionId optId, Minecraft* minecraft); + void createProgressSlider(OptionId optId, Minecraft* minecraft); + void createStepSlider(OptionId optId, Minecraft* minecraft); + void createTextbox(OptionId optId, Minecraft* minecraft); + void createKey(OptionId optId, Minecraft* minecraft); + + std::string label; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsGroup_H__*/ diff --git a/src/client/gui/components/OptionsItem.cpp b/src/client/gui/components/OptionsItem.cpp index 1ee48e3..7ffad40 100755 --- a/src/client/gui/components/OptionsItem.cpp +++ b/src/client/gui/components/OptionsItem.cpp @@ -1,42 +1,42 @@ -#include "OptionsItem.h" -#include "../../Minecraft.h" -#include "../../../locale/I18n.h" -#include "../../../util/Mth.h" -OptionsItem::OptionsItem( OptionId optionId, std::string label, GuiElement* element ) -: GuiElementContainer(false, true, 0, 0, 24, 12), - m_optionId(optionId), - m_label(label) { - addChild(element); -} - -void OptionsItem::setupPositions() { - int currentHeight = 0; - for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { - (*it)->x = x + width - (*it)->width - 15; - (*it)->y = y + currentHeight; - currentHeight += (*it)->height; - } - height = currentHeight; -} - -void OptionsItem::render( Minecraft* minecraft, int xm, int ym ) { - int yOffset = (height - 8) / 2; - std::string text = m_label; - if (m_optionId == OPTIONS_GUI_SCALE) { - int value = minecraft->options.getIntValue(OPTIONS_GUI_SCALE); - std::string scaleText; - switch (value) { - case 0: scaleText = I18n::get("options.guiScale.auto"); break; - case 1: scaleText = I18n::get("options.guiScale.small"); break; - case 2: scaleText = I18n::get("options.guiScale.medium"); break; - case 3: scaleText = I18n::get("options.guiScale.large"); break; - case 4: scaleText = I18n::get("options.guiScale.larger"); break; - case 5: scaleText = I18n::get("options.guiScale.largest"); break; - default: scaleText = I18n::get("options.guiScale.auto"); break; - } - text += ": " + scaleText; - } - - minecraft->font->draw(text, (float)x, (float)y + yOffset, 0x909090, false); - super::render(minecraft, xm, ym); +#include "OptionsItem.h" +#include "../../Minecraft.h" +#include "../../../locale/I18n.h" +#include "../../../util/Mth.h" +OptionsItem::OptionsItem( OptionId optionId, std::string label, GuiElement* element ) +: GuiElementContainer(false, true, 0, 0, 24, 12), + m_optionId(optionId), + m_label(label) { + addChild(element); +} + +void OptionsItem::setupPositions() { + int currentHeight = 0; + for(std::vector::iterator it = children.begin(); it != children.end(); ++it) { + (*it)->x = x + width - (*it)->width - 15; + (*it)->y = y + currentHeight; + currentHeight += (*it)->height; + } + height = currentHeight; +} + +void OptionsItem::render( Minecraft* minecraft, int xm, int ym ) { + int yOffset = (height - 8) / 2; + std::string text = m_label; + if (m_optionId == OPTIONS_GUI_SCALE) { + int value = minecraft->options.getIntValue(OPTIONS_GUI_SCALE); + std::string scaleText; + switch (value) { + case 0: scaleText = I18n::get("options.guiScale.auto"); break; + case 1: scaleText = I18n::get("options.guiScale.small"); break; + case 2: scaleText = I18n::get("options.guiScale.medium"); break; + case 3: scaleText = I18n::get("options.guiScale.large"); break; + case 4: scaleText = I18n::get("options.guiScale.larger"); break; + case 5: scaleText = I18n::get("options.guiScale.largest"); break; + default: scaleText = I18n::get("options.guiScale.auto"); break; + } + text += ": " + scaleText; + } + + minecraft->font->draw(text, (float)x, (float)y + yOffset, 0x909090, false); + super::render(minecraft, xm, ym); } \ No newline at end of file diff --git a/src/client/gui/components/OptionsItem.h b/src/client/gui/components/OptionsItem.h index 5add1ef..97bc267 100755 --- a/src/client/gui/components/OptionsItem.h +++ b/src/client/gui/components/OptionsItem.h @@ -1,27 +1,27 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsItem_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsItem_H__ - -#include -#include -#include "GuiElementContainer.h" -#include "../../../world/item/ItemInstance.h" -#include "../../../client/Options.h" -class Font; -class Textures; -class NinePatchLayer; -class ItemPane; - -class OptionsItem: public GuiElementContainer -{ - typedef GuiElementContainer super; -public: - OptionsItem(OptionId optionId, std::string label, GuiElement* element); - virtual void render(Minecraft* minecraft, int xm, int ym); - void setupPositions(); - -private: - OptionId m_optionId; - std::string m_label; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsItem_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsItem_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsItem_H__ + +#include +#include +#include "GuiElementContainer.h" +#include "../../../world/item/ItemInstance.h" +#include "../../../client/Options.h" +class Font; +class Textures; +class NinePatchLayer; +class ItemPane; + +class OptionsItem: public GuiElementContainer +{ + typedef GuiElementContainer super; +public: + OptionsItem(OptionId optionId, std::string label, GuiElement* element); + virtual void render(Minecraft* minecraft, int xm, int ym); + void setupPositions(); + +private: + OptionId m_optionId; + std::string m_label; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__OptionsItem_H__*/ diff --git a/src/client/gui/components/OptionsPane.cpp b/src/client/gui/components/OptionsPane.cpp index e7e6aa2..068f2be 100644 --- a/src/client/gui/components/OptionsPane.cpp +++ b/src/client/gui/components/OptionsPane.cpp @@ -1,63 +1,63 @@ -#include "OptionsPane.h" -#include "OptionsGroup.h" -#include "OptionsItem.h" -#include "ImageButton.h" -#include "Slider.h" -#include "../../Minecraft.h" - -OptionsPane::OptionsPane() { - -} - -void OptionsPane::setupPositions() { - int currentHeight = y + 1; - for(std::vector::iterator it = children.begin(); it != children.end(); ++it ) { - (*it)->width = width; - (*it)->y = currentHeight; - (*it)->x = x; - currentHeight += (*it)->height + 1; - } - height = currentHeight; - super::setupPositions(); -} - -OptionsGroup& OptionsPane::createOptionsGroup( std::string label ) { - OptionsGroup* newGroup = new OptionsGroup(label); - children.push_back(newGroup); - // create and return a new group index - return *newGroup; -} - -void OptionsPane::createToggle( unsigned int group, std::string label, const Option* option ) { - if(group > children.size()) return; - ImageDef def; - def.setSrc(IntRectangle(160, 206, 39, 20)); - def.name = "gui/touchgui.png"; - def.width = 39 * 0.7f; - def.height = 20 * 0.7f; - OptionButton* element = new OptionButton(option); - element->setImageDef(def, true); - OptionsItem* item = new OptionsItem(label, element); - ((OptionsGroup*)children[group])->addChild(item); - setupPositions(); -} - -void OptionsPane::createProgressSlider( Minecraft* minecraft, unsigned int group, std::string label, const Option* option, float progressMin/*=1.0f*/, float progressMax/*=1.0f */ ) { - if(group > children.size()) return; - Slider* element = new Slider(minecraft, option, progressMin, progressMax); - element->width = 100; - element->height = 20; - OptionsItem* item = new OptionsItem(label, element); - ((OptionsGroup*)children[group])->addChild(item); - setupPositions(); -} - -void OptionsPane::createStepSlider( Minecraft* minecraft, unsigned int group, std::string label, const Option* option, const std::vector& stepVec ) { - if(group > children.size()) return; - Slider* element = new Slider(minecraft, option, stepVec); - element->width = 100; - element->height = 20; - OptionsItem* item = new OptionsItem(label, element); - ((OptionsGroup*)children[group])->addChild(item); - setupPositions(); -} +#include "OptionsPane.h" +#include "OptionsGroup.h" +#include "OptionsItem.h" +#include "ImageButton.h" +#include "Slider.h" +#include "../../Minecraft.h" + +OptionsPane::OptionsPane() { + +} + +void OptionsPane::setupPositions() { + int currentHeight = y + 1; + for(std::vector::iterator it = children.begin(); it != children.end(); ++it ) { + (*it)->width = width; + (*it)->y = currentHeight; + (*it)->x = x; + currentHeight += (*it)->height + 1; + } + height = currentHeight; + super::setupPositions(); +} + +OptionsGroup& OptionsPane::createOptionsGroup( std::string label ) { + OptionsGroup* newGroup = new OptionsGroup(label); + children.push_back(newGroup); + // create and return a new group index + return *newGroup; +} + +void OptionsPane::createToggle( unsigned int group, std::string label, const Option* option ) { + if(group > children.size()) return; + ImageDef def; + def.setSrc(IntRectangle(160, 206, 39, 20)); + def.name = "gui/touchgui.png"; + def.width = 39 * 0.7f; + def.height = 20 * 0.7f; + OptionButton* element = new OptionButton(option); + element->setImageDef(def, true); + OptionsItem* item = new OptionsItem(label, element); + ((OptionsGroup*)children[group])->addChild(item); + setupPositions(); +} + +void OptionsPane::createProgressSlider( Minecraft* minecraft, unsigned int group, std::string label, const Option* option, float progressMin/*=1.0f*/, float progressMax/*=1.0f */ ) { + if(group > children.size()) return; + Slider* element = new Slider(minecraft, option, progressMin, progressMax); + element->width = 100; + element->height = 20; + OptionsItem* item = new OptionsItem(label, element); + ((OptionsGroup*)children[group])->addChild(item); + setupPositions(); +} + +void OptionsPane::createStepSlider( Minecraft* minecraft, unsigned int group, std::string label, const Option* option, const std::vector& stepVec ) { + if(group > children.size()) return; + Slider* element = new Slider(minecraft, option, stepVec); + element->width = 100; + element->height = 20; + OptionsItem* item = new OptionsItem(label, element); + ((OptionsGroup*)children[group])->addChild(item); + setupPositions(); +} diff --git a/src/client/gui/components/OptionsPane.h b/src/client/gui/components/OptionsPane.h index cf16b26..559c2a0 100644 --- a/src/client/gui/components/OptionsPane.h +++ b/src/client/gui/components/OptionsPane.h @@ -1,30 +1,30 @@ -#ifndef ITEMPANE_H__ -#define ITEMPANE_H__ - -#include -#include -#include "GuiElementContainer.h" -#include "../../../world/item/ItemInstance.h" -#include "../../../client/Options.h" -class Font; -class Textures; -class NinePatchLayer; -class ItemPane; -class OptionButton; -class Button; -class OptionsGroup; -class Slider; -class Minecraft; -class OptionsPane: public GuiElementContainer -{ - typedef GuiElementContainer super; -public: - OptionsPane(); - OptionsGroup& createOptionsGroup( std::string label ); - void createToggle( unsigned int group, std::string label, const Option* option ); - void createProgressSlider(Minecraft* minecraft, unsigned int group, std::string label, const Option* option, float progressMin=1.0f, float progressMax=1.0f ); - void createStepSlider(Minecraft* minecraft, unsigned int group, std::string label, const Option* option, const std::vector& stepVec ); - void setupPositions(); -}; - -#endif /*ITEMPANE_H__*/ +#ifndef ITEMPANE_H__ +#define ITEMPANE_H__ + +#include +#include +#include "GuiElementContainer.h" +#include "../../../world/item/ItemInstance.h" +#include "../../../client/Options.h" +class Font; +class Textures; +class NinePatchLayer; +class ItemPane; +class OptionButton; +class Button; +class OptionsGroup; +class Slider; +class Minecraft; +class OptionsPane: public GuiElementContainer +{ + typedef GuiElementContainer super; +public: + OptionsPane(); + OptionsGroup& createOptionsGroup( std::string label ); + void createToggle( unsigned int group, std::string label, const Option* option ); + void createProgressSlider(Minecraft* minecraft, unsigned int group, std::string label, const Option* option, float progressMin=1.0f, float progressMax=1.0f ); + void createStepSlider(Minecraft* minecraft, unsigned int group, std::string label, const Option* option, const std::vector& stepVec ); + void setupPositions(); +}; + +#endif /*ITEMPANE_H__*/ diff --git a/src/client/gui/components/RolledSelectionListH.cpp b/src/client/gui/components/RolledSelectionListH.cpp index 625896d..cb63a58 100755 --- a/src/client/gui/components/RolledSelectionListH.cpp +++ b/src/client/gui/components/RolledSelectionListH.cpp @@ -1,299 +1,299 @@ -#include "RolledSelectionListH.h" -#include "../../Minecraft.h" -#include "../../renderer/Tesselator.h" -#include "../../renderer/gles.h" -#include "../../../platform/input/Mouse.h" -#include "../../../platform/input/Multitouch.h" -#include "../../../util/Mth.h" -#include "../../renderer/Textures.h" - - -RolledSelectionListH::RolledSelectionListH( Minecraft* minecraft, int width, int height, int x0, int x1, int y0, int y1, int itemWidth ) -: minecraft(minecraft), - width(width), - height(height), - x0((float)x0), - x1((float)x1), - y0((float)y0), - y1((float)y1), - itemWidth(itemWidth), - selectionX(-1), - lastSelectionTime(0), - lastSelection(-1), - renderSelection(true), - doRenderHeader(false), - headerWidth(0), - dragState(DRAG_OUTSIDE), - xDrag(0.0f), - xo(0.0f), - xoo(0.0f), - xInertia(0.0f), - _componentSelected(false), - _renderTopBorder(true), - _renderBottomBorder(true), - _lastxoo(0), - _xinertia(0) -{ - xo = xoo = (float)(itemWidth-width) * 0.5f; - _lastxoo = xoo; -} - -void RolledSelectionListH::setRenderSelection( bool _renderSelection ) -{ - renderSelection = _renderSelection; -} - -void RolledSelectionListH::setComponentSelected(bool selected) { - _componentSelected = selected; -} - -void RolledSelectionListH::setRenderHeader( bool _renderHeader, int _headerHeight ) -{ - doRenderHeader = _renderHeader; - headerWidth = _headerHeight; - - if (!doRenderHeader) { - headerWidth = 0; - } -} - -int RolledSelectionListH::getMaxPosition() -{ - return getNumberOfItems() * itemWidth + headerWidth; -} - -int RolledSelectionListH::getItemAtPosition( int x, int y ) -{ - int clickSlotPos = (int)(x - x0 - headerWidth + (int) xo - 4); - int isInsideY = y >= y0 && y <= y1; - return isInsideY? getItemAtXPositionRaw(clickSlotPos) : -1; -} - -int RolledSelectionListH::getItemAtXPositionRaw(int x) { - int slot = x / itemWidth; - bool isInsideX = slot >= 0 && x >= 0 && slot < getNumberOfItems(); - return isInsideX? slot : -1; -} - -bool RolledSelectionListH::capXPosition() -{ - const float MinX = (float)(itemWidth-width)/2; - const float MaxX = MinX + (getNumberOfItems()-1) * itemWidth; - if (xo < MinX) { xo = MinX; xInertia = 0; return true; } - if (xo > MaxX) { xo = MaxX; xInertia = 0; return true; } - return false; -} - -void RolledSelectionListH::tick() { - - //if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) - { - _xinertia = _lastxoo - xoo; - } - _lastxoo = xoo; - xoo = xo - xInertia; -} - -float RolledSelectionListH::getPos(float alpha) { - return xoo - xInertia * alpha; -} - -void RolledSelectionListH::render( int xm, int ym, float a ) -{ - renderBackground(); - - int itemCount = getNumberOfItems(); - - //float yy0 = height / 2.0f + 124; - //float yy1 = yy0 + 6; - - if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) { - touched(); - //LOGI("DOWN ym: %d\n", ym); - if (ym >= y0 && ym <= y1) { - if (dragState == NO_DRAG) { - lastSelectionTime = getTimeMs(); - lastSelection = getItemAtPosition(xm, height/2); - //float localX = (float)(xm*Gui::InvGuiScale - x0 - xo + lastSelection * itemWidth + headerWidth); - selectStart(lastSelection, 0, 0);//localX, ym-y0); - selectionX = xm; - } - else if (dragState >= 0) { - xo -= (xm - xDrag); - xoo = xo; - } - dragState = DRAG_NORMAL; - //const int* ids; - //LOGI("mtouch: %d\n", Multitouch::getActivePointerIds(&ids)); - } - } else { - if (dragState >= 0) { - if (dragState >= 0) { - xInertia = _xinertia < 0? Mth::Max(-20.0f, _xinertia) : Mth::Min(20.0f, _xinertia); - } - //LOGI("Inertia: %f. Time: %d, delta-x: %d, (xm, sel: %d, %d)\n", xInertia, getTimeMs() - lastSelectionTime, std::abs(selectionX - xm), xm, selectionX); - // kill small inertia values when releasing scrollist - if (std::abs(xInertia) <= 2.0001f) { - xInertia = 0.0f; - } - - if (std::abs(xInertia) <= 10 && getTimeMs() - lastSelectionTime < 300) - { - int slot = getItemAtPosition(xm, height/2); - //LOGI("slot: %d, lt: %d. diff: %d - %d\n", slot, lastSelection, selectionX, xm); - if (slot >= 0 && slot == lastSelection && std::abs(selectionX - xm) < 10) - selectItem(slot, false); - else - selectCancel(); - } else { - selectCancel(); - } - } - - // if (slot >= 0 && std::abs(selectionX - xm) < itemWidth) - // { - // bool doubleClick = false; - // selectItem(slot, doubleClick); - // //xInertia = 0.0f; - // } - //} - dragState = NO_DRAG; - - xo = getPos(a); - } - xDrag = (float)xm; - - capXPosition(); - - Tesselator& t = Tesselator::instance; - - float by0 = _renderTopBorder? y0 : 0; - float by1 = _renderBottomBorder? y1 : height; - - //LOGI("x: %f\n", xo); - - minecraft->textures->loadAndBindTexture("gui/background.png"); - glColor4f2(1.0f, 1, 1, 1); - float s = 32; - t.begin(); - t.color(0x202020); - t.vertexUV(x0, by1, 0, (x0 + (int) xo) / s, by1 / s); - t.vertexUV(x1, by1, 0, (x1 + (int) xo) / s, by1 / s); - t.vertexUV(x1, by0, 0, (x1 + (int) xo) / s, by0 / s); - t.vertexUV(x0, by0, 0, (x0 + (int) xo) / s, by0 / s); - t.draw(); - - const int HalfHeight = 48; - - if (getNumberOfItems() == 0) xo = 0; - - int rowY = (int)(height / 2 - HalfHeight + 8); - int rowBaseX = (int)(x0 /*+ 4*/ - (int) xo); - - if (doRenderHeader) { - renderHeader(rowBaseX, rowY, t); - } - - for (int i = 0; i < itemCount; i++) { - - float x = (float)(rowBaseX + (i) * itemWidth + headerWidth); - float h = (float)itemWidth; - - if (x > x1 || (x + h) < x0) { - continue; - } - - if (renderSelection && isSelectedItem(i)) { - float y0 = height / 2.0f - HalfHeight - 4; //@kindle-res:+2 - float y1 = height / 2.0f + HalfHeight - 4; //@kindle-res:-6 - glColor4f2(1, 1, 1, 1); - glDisable2(GL_TEXTURE_2D); - - int ew = 0; - int color = 0x808080; - if (_componentSelected) { - ew = 0; - color = 0x7F89BF; - } - t.begin(); - t.color(color); - t.vertex(x - 1 - ew, y0 - ew, 0); - t.vertex(x - 1 - ew, y1 + ew, 0); - t.vertex(x + h + 1 + ew, y1 + ew, 0); - t.vertex(x + h + 1 + ew, y0 - ew, 0); - - t.color(0x000000); - t.vertex(x, y0 + 1, 0); - t.vertex(x, y1 - 1, 0); - t.vertex(x + h, y1 - 1, 0); - t.vertex(x + h, y0 + 1, 0); - - t.draw(); - glEnable2(GL_TEXTURE_2D); - } - renderItem(i, (int)x, rowY, (int)h, t); - } - - glDisable2(GL_DEPTH_TEST); - - if (_renderTopBorder) - renderHoleBackground(0, y0, 255, 255); - if (_renderBottomBorder) - renderHoleBackground(y1, (float)height, 255, 255); - - //glEnable2(GL_BLEND); - //glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - //glDisable2(GL_ALPHA_TEST); - //glShadeModel2(GL_SMOOTH); - - //glDisable2(GL_TEXTURE_2D); - - //const int d = 4; - //t.begin(); - //t.color(0x000000, 0); - //t.vertexUV(y0, x0 + d, 0, 0, 1); - //t.vertexUV(y1, x0 + d, 0, 1, 1); - //t.color(0x000000, 255); - //t.vertexUV(y1, x0, 0, 1, 0); - //t.vertexUV(y0, x0, 0, 0, 0); - //t.draw(); - - //t.begin(); - //t.color(0x000000, 255); - //t.vertexUV(y0, x1, 0, 0, 1); - //t.vertexUV(y1, x1, 0, 1, 1); - //t.color(0x000000, 0); - //t.vertexUV(y1, x1 - d, 0, 1, 0); - //t.vertexUV(y0, x1 - d, 0, 0, 0); - //t.draw(); - - //renderDecorations(xm, ym); - - //glEnable2(GL_TEXTURE_2D); - glEnable2(GL_DEPTH_TEST); - - //glShadeModel2(GL_FLAT); - //glEnable2(GL_ALPHA_TEST); - //glDisable2(GL_BLEND); -} - -void RolledSelectionListH::renderHoleBackground( /*float x0, float x1,*/ float y0, float y1, int a0, int a1 ) -{ - Tesselator& t = Tesselator::instance; - minecraft->textures->loadAndBindTexture("gui/background.png"); - glColor4f2(1.0f, 1, 1, 1); - float s = 32; - t.begin(); - t.color(0x505050, a1); - t.vertexUV(0, y1, 0, 0, y1 / s); - t.vertexUV((float)width, y1, 0, width / s, y1 / s); - t.color(0x505050, a0); - t.vertexUV((float)width, y0, 0, width / s, y0 / s); - t.vertexUV(0, y0, 0, 0, y0 / s); - t.draw(); - //printf("x, y, x1, y1: %d, %d, %d, %d\n", 0, (int)y0, width, (int)y1); -} - -void RolledSelectionListH::touched() -{ -} +#include "RolledSelectionListH.h" +#include "../../Minecraft.h" +#include "../../renderer/Tesselator.h" +#include "../../renderer/gles.h" +#include "../../../platform/input/Mouse.h" +#include "../../../platform/input/Multitouch.h" +#include "../../../util/Mth.h" +#include "../../renderer/Textures.h" + + +RolledSelectionListH::RolledSelectionListH( Minecraft* minecraft, int width, int height, int x0, int x1, int y0, int y1, int itemWidth ) +: minecraft(minecraft), + width(width), + height(height), + x0((float)x0), + x1((float)x1), + y0((float)y0), + y1((float)y1), + itemWidth(itemWidth), + selectionX(-1), + lastSelectionTime(0), + lastSelection(-1), + renderSelection(true), + doRenderHeader(false), + headerWidth(0), + dragState(DRAG_OUTSIDE), + xDrag(0.0f), + xo(0.0f), + xoo(0.0f), + xInertia(0.0f), + _componentSelected(false), + _renderTopBorder(true), + _renderBottomBorder(true), + _lastxoo(0), + _xinertia(0) +{ + xo = xoo = (float)(itemWidth-width) * 0.5f; + _lastxoo = xoo; +} + +void RolledSelectionListH::setRenderSelection( bool _renderSelection ) +{ + renderSelection = _renderSelection; +} + +void RolledSelectionListH::setComponentSelected(bool selected) { + _componentSelected = selected; +} + +void RolledSelectionListH::setRenderHeader( bool _renderHeader, int _headerHeight ) +{ + doRenderHeader = _renderHeader; + headerWidth = _headerHeight; + + if (!doRenderHeader) { + headerWidth = 0; + } +} + +int RolledSelectionListH::getMaxPosition() +{ + return getNumberOfItems() * itemWidth + headerWidth; +} + +int RolledSelectionListH::getItemAtPosition( int x, int y ) +{ + int clickSlotPos = (int)(x - x0 - headerWidth + (int) xo - 4); + int isInsideY = y >= y0 && y <= y1; + return isInsideY? getItemAtXPositionRaw(clickSlotPos) : -1; +} + +int RolledSelectionListH::getItemAtXPositionRaw(int x) { + int slot = x / itemWidth; + bool isInsideX = slot >= 0 && x >= 0 && slot < getNumberOfItems(); + return isInsideX? slot : -1; +} + +bool RolledSelectionListH::capXPosition() +{ + const float MinX = (float)(itemWidth-width)/2; + const float MaxX = MinX + (getNumberOfItems()-1) * itemWidth; + if (xo < MinX) { xo = MinX; xInertia = 0; return true; } + if (xo > MaxX) { xo = MaxX; xInertia = 0; return true; } + return false; +} + +void RolledSelectionListH::tick() { + + //if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) + { + _xinertia = _lastxoo - xoo; + } + _lastxoo = xoo; + xoo = xo - xInertia; +} + +float RolledSelectionListH::getPos(float alpha) { + return xoo - xInertia * alpha; +} + +void RolledSelectionListH::render( int xm, int ym, float a ) +{ + renderBackground(); + + int itemCount = getNumberOfItems(); + + //float yy0 = height / 2.0f + 124; + //float yy1 = yy0 + 6; + + if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) { + touched(); + //LOGI("DOWN ym: %d\n", ym); + if (ym >= y0 && ym <= y1) { + if (dragState == NO_DRAG) { + lastSelectionTime = getTimeMs(); + lastSelection = getItemAtPosition(xm, height/2); + //float localX = (float)(xm*Gui::InvGuiScale - x0 - xo + lastSelection * itemWidth + headerWidth); + selectStart(lastSelection, 0, 0);//localX, ym-y0); + selectionX = xm; + } + else if (dragState >= 0) { + xo -= (xm - xDrag); + xoo = xo; + } + dragState = DRAG_NORMAL; + //const int* ids; + //LOGI("mtouch: %d\n", Multitouch::getActivePointerIds(&ids)); + } + } else { + if (dragState >= 0) { + if (dragState >= 0) { + xInertia = _xinertia < 0? Mth::Max(-20.0f, _xinertia) : Mth::Min(20.0f, _xinertia); + } + //LOGI("Inertia: %f. Time: %d, delta-x: %d, (xm, sel: %d, %d)\n", xInertia, getTimeMs() - lastSelectionTime, std::abs(selectionX - xm), xm, selectionX); + // kill small inertia values when releasing scrollist + if (std::abs(xInertia) <= 2.0001f) { + xInertia = 0.0f; + } + + if (std::abs(xInertia) <= 10 && getTimeMs() - lastSelectionTime < 300) + { + int slot = getItemAtPosition(xm, height/2); + //LOGI("slot: %d, lt: %d. diff: %d - %d\n", slot, lastSelection, selectionX, xm); + if (slot >= 0 && slot == lastSelection && std::abs(selectionX - xm) < 10) + selectItem(slot, false); + else + selectCancel(); + } else { + selectCancel(); + } + } + + // if (slot >= 0 && std::abs(selectionX - xm) < itemWidth) + // { + // bool doubleClick = false; + // selectItem(slot, doubleClick); + // //xInertia = 0.0f; + // } + //} + dragState = NO_DRAG; + + xo = getPos(a); + } + xDrag = (float)xm; + + capXPosition(); + + Tesselator& t = Tesselator::instance; + + float by0 = _renderTopBorder? y0 : 0; + float by1 = _renderBottomBorder? y1 : height; + + //LOGI("x: %f\n", xo); + + minecraft->textures->loadAndBindTexture("gui/background.png"); + glColor4f2(1.0f, 1, 1, 1); + float s = 32; + t.begin(); + t.color(0x202020); + t.vertexUV(x0, by1, 0, (x0 + (int) xo) / s, by1 / s); + t.vertexUV(x1, by1, 0, (x1 + (int) xo) / s, by1 / s); + t.vertexUV(x1, by0, 0, (x1 + (int) xo) / s, by0 / s); + t.vertexUV(x0, by0, 0, (x0 + (int) xo) / s, by0 / s); + t.draw(); + + const int HalfHeight = 48; + + if (getNumberOfItems() == 0) xo = 0; + + int rowY = (int)(height / 2 - HalfHeight + 8); + int rowBaseX = (int)(x0 /*+ 4*/ - (int) xo); + + if (doRenderHeader) { + renderHeader(rowBaseX, rowY, t); + } + + for (int i = 0; i < itemCount; i++) { + + float x = (float)(rowBaseX + (i) * itemWidth + headerWidth); + float h = (float)itemWidth; + + if (x > x1 || (x + h) < x0) { + continue; + } + + if (renderSelection && isSelectedItem(i)) { + float y0 = height / 2.0f - HalfHeight - 4; //@kindle-res:+2 + float y1 = height / 2.0f + HalfHeight - 4; //@kindle-res:-6 + glColor4f2(1, 1, 1, 1); + glDisable2(GL_TEXTURE_2D); + + int ew = 0; + int color = 0x808080; + if (_componentSelected) { + ew = 0; + color = 0x7F89BF; + } + t.begin(); + t.color(color); + t.vertex(x - 1 - ew, y0 - ew, 0); + t.vertex(x - 1 - ew, y1 + ew, 0); + t.vertex(x + h + 1 + ew, y1 + ew, 0); + t.vertex(x + h + 1 + ew, y0 - ew, 0); + + t.color(0x000000); + t.vertex(x, y0 + 1, 0); + t.vertex(x, y1 - 1, 0); + t.vertex(x + h, y1 - 1, 0); + t.vertex(x + h, y0 + 1, 0); + + t.draw(); + glEnable2(GL_TEXTURE_2D); + } + renderItem(i, (int)x, rowY, (int)h, t); + } + + glDisable2(GL_DEPTH_TEST); + + if (_renderTopBorder) + renderHoleBackground(0, y0, 255, 255); + if (_renderBottomBorder) + renderHoleBackground(y1, (float)height, 255, 255); + + //glEnable2(GL_BLEND); + //glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glDisable2(GL_ALPHA_TEST); + //glShadeModel2(GL_SMOOTH); + + //glDisable2(GL_TEXTURE_2D); + + //const int d = 4; + //t.begin(); + //t.color(0x000000, 0); + //t.vertexUV(y0, x0 + d, 0, 0, 1); + //t.vertexUV(y1, x0 + d, 0, 1, 1); + //t.color(0x000000, 255); + //t.vertexUV(y1, x0, 0, 1, 0); + //t.vertexUV(y0, x0, 0, 0, 0); + //t.draw(); + + //t.begin(); + //t.color(0x000000, 255); + //t.vertexUV(y0, x1, 0, 0, 1); + //t.vertexUV(y1, x1, 0, 1, 1); + //t.color(0x000000, 0); + //t.vertexUV(y1, x1 - d, 0, 1, 0); + //t.vertexUV(y0, x1 - d, 0, 0, 0); + //t.draw(); + + //renderDecorations(xm, ym); + + //glEnable2(GL_TEXTURE_2D); + glEnable2(GL_DEPTH_TEST); + + //glShadeModel2(GL_FLAT); + //glEnable2(GL_ALPHA_TEST); + //glDisable2(GL_BLEND); +} + +void RolledSelectionListH::renderHoleBackground( /*float x0, float x1,*/ float y0, float y1, int a0, int a1 ) +{ + Tesselator& t = Tesselator::instance; + minecraft->textures->loadAndBindTexture("gui/background.png"); + glColor4f2(1.0f, 1, 1, 1); + float s = 32; + t.begin(); + t.color(0x505050, a1); + t.vertexUV(0, y1, 0, 0, y1 / s); + t.vertexUV((float)width, y1, 0, width / s, y1 / s); + t.color(0x505050, a0); + t.vertexUV((float)width, y0, 0, width / s, y0 / s); + t.vertexUV(0, y0, 0, 0, y0 / s); + t.draw(); + //printf("x, y, x1, y1: %d, %d, %d, %d\n", 0, (int)y0, width, (int)y1); +} + +void RolledSelectionListH::touched() +{ +} diff --git a/src/client/gui/components/RolledSelectionListH.h b/src/client/gui/components/RolledSelectionListH.h index 14592cd..54eb0f5 100755 --- a/src/client/gui/components/RolledSelectionListH.h +++ b/src/client/gui/components/RolledSelectionListH.h @@ -1,82 +1,82 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListH_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListH_H__ - -#include "../GuiComponent.h" -class Minecraft; -class Tesselator; - - -class RolledSelectionListH : public GuiComponent -{ - static const int NO_DRAG = -1; - static const int DRAG_OUTSIDE = -2; - static const int DRAG_NORMAL = 0; -public: - RolledSelectionListH(Minecraft* minecraft, int width, int height, int x0, int x1, int y0, int y1, int itemWidth); - - virtual int getItemAtPosition(int x, int y); - - virtual bool capXPosition(); - - virtual void tick(); - virtual void render(int xm, int ym, float a); - virtual void renderHoleBackground(/*float x0, float x1,*/ float y0, float y1, int a0, int a1); - virtual void setRenderSelection(bool _renderSelection); - virtual void setComponentSelected(bool selected); -protected: - void setRenderHeader(bool _renderHeader, int _headerHeight); - - virtual int getNumberOfItems() = 0; - - virtual void selectStart(int item, int localX, int localY) {} - virtual void selectCancel() {} - virtual void selectItem(int item, bool doubleClick) = 0; - virtual bool isSelectedItem(int item) = 0; - - virtual int getMaxPosition(); - virtual float getPos(float alpha); - virtual void touched(); - - virtual void renderItem(int i, int x, int y, int h, Tesselator& t) = 0; - virtual void renderHeader(int x, int y, Tesselator& t) {} - virtual void renderBackground() = 0; - virtual void renderDecorations(int mouseX, int mouseY) {} - - virtual void clickedHeader(int headerMouseX, int headerMouseY) {} - int getItemAtXPositionRaw(int x); -protected: - Minecraft* minecraft; - - float x0; - float x1; - int itemWidth; - int width; - int height; -//private: - float y0; - float y1; - - int dragState; - float xDrag; - float xo; - float xoo; - float xInertia; - float _xinertia; - - int selectionX; - bool renderSelection; - bool _componentSelected; - - bool _renderTopBorder; - bool _renderBottomBorder; - -private: - int headerWidth; - bool doRenderHeader; - long lastSelectionTime; - int lastSelection; - - float _lastxoo; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListH_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListH_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListH_H__ + +#include "../GuiComponent.h" +class Minecraft; +class Tesselator; + + +class RolledSelectionListH : public GuiComponent +{ + static const int NO_DRAG = -1; + static const int DRAG_OUTSIDE = -2; + static const int DRAG_NORMAL = 0; +public: + RolledSelectionListH(Minecraft* minecraft, int width, int height, int x0, int x1, int y0, int y1, int itemWidth); + + virtual int getItemAtPosition(int x, int y); + + virtual bool capXPosition(); + + virtual void tick(); + virtual void render(int xm, int ym, float a); + virtual void renderHoleBackground(/*float x0, float x1,*/ float y0, float y1, int a0, int a1); + virtual void setRenderSelection(bool _renderSelection); + virtual void setComponentSelected(bool selected); +protected: + void setRenderHeader(bool _renderHeader, int _headerHeight); + + virtual int getNumberOfItems() = 0; + + virtual void selectStart(int item, int localX, int localY) {} + virtual void selectCancel() {} + virtual void selectItem(int item, bool doubleClick) = 0; + virtual bool isSelectedItem(int item) = 0; + + virtual int getMaxPosition(); + virtual float getPos(float alpha); + virtual void touched(); + + virtual void renderItem(int i, int x, int y, int h, Tesselator& t) = 0; + virtual void renderHeader(int x, int y, Tesselator& t) {} + virtual void renderBackground() = 0; + virtual void renderDecorations(int mouseX, int mouseY) {} + + virtual void clickedHeader(int headerMouseX, int headerMouseY) {} + int getItemAtXPositionRaw(int x); +protected: + Minecraft* minecraft; + + float x0; + float x1; + int itemWidth; + int width; + int height; +//private: + float y0; + float y1; + + int dragState; + float xDrag; + float xo; + float xoo; + float xInertia; + float _xinertia; + + int selectionX; + bool renderSelection; + bool _componentSelected; + + bool _renderTopBorder; + bool _renderBottomBorder; + +private: + int headerWidth; + bool doRenderHeader; + long lastSelectionTime; + int lastSelection; + + float _lastxoo; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListH_H__*/ diff --git a/src/client/gui/components/RolledSelectionListV.cpp b/src/client/gui/components/RolledSelectionListV.cpp index e42d9bd..1b1bb39 100755 --- a/src/client/gui/components/RolledSelectionListV.cpp +++ b/src/client/gui/components/RolledSelectionListV.cpp @@ -1,352 +1,352 @@ -#include "RolledSelectionListV.h" -#include "../../Minecraft.h" -#include "../../renderer/Tesselator.h" -#include "../../renderer/gles.h" -#include "../../../platform/input/Mouse.h" -#include "../../../util/Mth.h" -#include "../../renderer/Textures.h" - - -RolledSelectionListV::RolledSelectionListV( Minecraft* minecraft_, int width_, int height_, int x0_, int x1_, int y0_, int y1_, int itemHeight_ ) -: minecraft(minecraft_), - width(width_), - height(height_), - x0((float)x0_), - x1((float)x1_), - y0((float)y0_), - y1((float)y1_), - itemHeight(itemHeight_), - selectionY(-1), - lastSelectionTime(0), - lastSelection(-1), - renderSelection(true), - doRenderHeader(false), - headerHeight(0), - dragState(DRAG_OUTSIDE), - yDrag(0.0f), - yo(0.0f), - yoo(0.0f), - yInertia(0.0f), - _componentSelected(false), - _renderDirtBackground(true), - _renderTopBorder(true), - _renderBottomBorder(true), - _lastyoo(0), - _yinertia(0), - _stickPixels(0), - _lastxm(0), - _lastym(0) -{ - yo = yoo = 0;//(float)(-itemHeight) * 0.5f; - _lastyoo = yoo; -} - -void RolledSelectionListV::setRenderSelection( bool _renderSelection ) -{ - renderSelection = _renderSelection; -} - -void RolledSelectionListV::setComponentSelected(bool selected) { - _componentSelected = selected; -} - -void RolledSelectionListV::setRenderHeader( bool _renderHeader, int _headerHeight ) -{ - doRenderHeader = _renderHeader; - headerHeight = _headerHeight; - - if (!doRenderHeader) { - headerHeight = 0; - } -} - -int RolledSelectionListV::getMaxPosition() -{ - return getNumberOfItems() * itemHeight + headerHeight; -} - -int RolledSelectionListV::getItemAtPosition( int x, int y ) -{ - int clickSlotPos = (int)(y - y0 - headerHeight + (int) yo - 4); - int isInsideX = x >= x0 && x <= x1; - return isInsideX? getItemAtYPositionRaw(clickSlotPos) : -1; -} - -int RolledSelectionListV::getItemAtYPositionRaw(int y) { - int slot = y / itemHeight; - bool isInsideX = slot >= 0 && y >= 0 && slot < getNumberOfItems(); - return isInsideX? slot : -1; -} - -bool RolledSelectionListV::capYPosition() -{ - float max = getMaxPosition() - (y1 - y0 - 4); - if (max < 0) max /= 2; - if (yo < 0) yo = 0; - if (yo > max) yo = max; - return false; -/* - const float MinY = -itemHeight/2;//(float)(itemHeight-height)/2; - const float MaxY = MinY + (getNumberOfItems()-1) * itemHeight; - if (yo < MinY) { yo = MinY; yInertia = 0; return true; } - if (yo > MaxY) { yo = MaxY; yInertia = 0; return true; } - return false; - */ -} - -void RolledSelectionListV::tick() { - - if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) - { - _yinertia = _lastyoo - yoo; - } - _lastyoo = yoo; - - //yInertia = Mth::absDecrease(yInertia, 1.0f, 0); - - yoo = yo - yInertia; - - //LOGI("tick: %f, %f, %f\n", yo, yInertia, _yinertia); -} - -float RolledSelectionListV::getPos(float alpha) { - return yoo - yInertia * alpha; -} - -void RolledSelectionListV::render( int xm, int ym, float a ) -{ - _lastxm = xm; - _lastym = ym; - renderBackground(); - - int itemCount = getNumberOfItems(); - - //float yy0 = height / 2.0f + 124; - //float yy1 = yy0 + 6; - - if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) { - touched(); - //LOGI("DOWN ym: %d\n", ym); - if (ym >= y0 && ym <= y1) { - if (dragState == NO_DRAG) { - lastSelectionTime = getTimeMs(); - lastSelection = convertSelection( getItemAtPosition(width/2, ym), xm, ym ); - selectStart(lastSelection); - //LOGI("Sel : %d\n", lastSelection); - selectionY = ym; - _stickPixels = 10; - } - else if (dragState >= 0) { - float delta = (ym - yDrag); - float absDelta = Mth::abs(delta); - if (absDelta > _stickPixels) { - _stickPixels = 0; - delta -= delta>0? _stickPixels : -_stickPixels; - } else { - delta = 0; - _stickPixels -= absDelta; - } - yo -= delta; - yoo = yo; - } - dragState = DRAG_NORMAL; - } - } else { - if (dragState >= 0) { - if (dragState >= 0) { - yInertia = _yinertia < 0? Mth::Max(-10.0f, _yinertia) : Mth::Min(10.0f, _yinertia); - } - // kill small inertia values when releasing scrollist - if (std::abs(yInertia) <= 2.0001f) { - yInertia = 0.0f; - } - - if (std::abs(yInertia) <= 10 /*&& getTimeMs() - lastSelectionTime < 300 */) - { - //float clickSlotPos = (ym - x0 - headerHeight + (int) yo - 4); - int slot = convertSelection( getItemAtPosition(width/2, ym), xm, ym); - //LOGI("slot: %d, lt: %d. diff: %d - %d\n", slot, lastSelection, selectionX, xm); - if (xm >= x0 && xm <= x1 && slot >= 0 && slot == lastSelection && std::abs(selectionY - ym) < 10) - selectItem(slot, false); - } else { - selectCancel(); - } - } - - // if (slot >= 0 && std::abs(selectionX - xm) < itemWidth) - // { - // bool doubleClick = false; - // selectItem(slot, doubleClick); - // //xInertia = 0.0f; - // } - //} - dragState = NO_DRAG; - - yo = getPos(a); - } - yDrag = (float)ym; - - evaluate(xm, ym); - capYPosition(); - - Tesselator& t = Tesselator::instance; - - const int HalfWidth = 48; - int rowX = (int)(width / 2 - HalfWidth + 8); - int rowBaseY = (int)(y0 + 4 - (int) yo); - - if (_renderDirtBackground) - renderDirtBackground(); - - if (getNumberOfItems() == 0) yo = 0; - - //int rowY = (int)(height / 2 - HalfHeight + 8); - if (doRenderHeader) { - const int HalfWidth = 48; - int rowX = (int)(width / 2 - HalfWidth + 8); - int rowBaseY = (int)(y0 + 4 - (int) yo); - renderHeader(rowX, rowBaseY, t); - } - - onPreRender(); - for (int i = 0; i < itemCount; i++) { - - float y = (float)(rowBaseY + (i) * itemHeight + headerHeight); - float h = itemHeight - 4.0f; - - if (y > y1 || (y + h) < y0) { - continue; - } - - if (renderSelection && isSelectedItem(i)) { - //float y0 = height / 2.0f - HalfHeight - 4; - //float y1 = height / 2.0f + HalfHeight - 4; - //glColor4f2(1, 1, 1, 1); - //glDisable2(GL_TEXTURE_2D); - - //int ew = 0; - //int color = 0x808080; - //if (_componentSelected) { - // ew = 0; - // color = 0x7F89BF; - //} - //t.begin(); - //t.color(color); - //t.vertex(x - 2 - ew, y0 - ew, 0); - //t.vertex(x - 2 - ew, y1 + ew, 0); - //t.vertex(x + h + 2 + ew, y1 + ew, 0); - //t.vertex(x + h + 2 + ew, y0 - ew, 0); - - //t.color(0x000000); - //t.vertex(x - 1, y0 + 1, 0); - //t.vertex(x - 1, y1 - 1, 0); - //t.vertex(x + h + 1, y1 - 1, 0); - //t.vertex(x + h + 1, y0 + 1, 0); - - //t.draw(); - //glEnable2(GL_TEXTURE_2D); - } - renderItem(i, rowX, (int)y, (int)h, t); - } - onPostRender(); - - glDisable2(GL_DEPTH_TEST); - - if (_renderTopBorder) - renderHoleBackground(0, y0, 255, 255); - if (_renderBottomBorder) - renderHoleBackground(y1, (float)height, 255, 255); - renderForeground(); - - //glEnable2(GL_BLEND); - //glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - //glDisable2(GL_ALPHA_TEST); - //glShadeModel2(GL_SMOOTH); - - //glDisable2(GL_TEXTURE_2D); - - //const int d = 4; - //t.begin(); - //t.color(0x000000, 0); - //t.vertexUV(y0, x0 + d, 0, 0, 1); - //t.vertexUV(y1, x0 + d, 0, 1, 1); - //t.color(0x000000, 255); - //t.vertexUV(y1, x0, 0, 1, 0); - //t.vertexUV(y0, x0, 0, 0, 0); - //t.draw(); - - //t.begin(); - //t.color(0x000000, 255); - //t.vertexUV(y0, x1, 0, 0, 1); - //t.vertexUV(y1, x1, 0, 1, 1); - //t.color(0x000000, 0); - //t.vertexUV(y1, x1 - d, 0, 1, 0); - //t.vertexUV(y0, x1 - d, 0, 0, 0); - //t.draw(); - - //renderDecorations(xm, ym); - - //glEnable2(GL_TEXTURE_2D); - //glEnable2(GL_DEPTH_TEST); - - //glShadeModel2(GL_FLAT); - //glEnable2(GL_ALPHA_TEST); - //glDisable2(GL_BLEND); -} - -void RolledSelectionListV::renderHoleBackground( /*float x0, float x1,*/ float y0, float y1, int a0, int a1 ) -{ - Tesselator& t = Tesselator::instance; - minecraft->textures->loadAndBindTexture("gui/background.png"); - glColor4f2(1.0f, 1, 1, 1); - float s = 32; - t.begin(); - t.color(0x505050, a1); - t.vertexUV(0, y1, 0, 0, y1 / s); - t.vertexUV((float)width, y1, 0, width / s, y1 / s); - t.color(0x505050, a0); - t.vertexUV((float)width, y0, 0, width / s, y0 / s); - t.vertexUV(0, y0, 0, 0, y0 / s); - t.draw(); - //printf("x, y, x1, y1: %d, %d, %d, %d\n", 0, (int)y0, width, (int)y1); -} - -void RolledSelectionListV::touched() -{ -} - -void RolledSelectionListV::evaluate(int xm, int ym) -{ - if (std::abs(selectionY - ym) >= 10) { - lastSelection = -1; - selectCancel(); - } -} - -void RolledSelectionListV::onPreRender() -{ -} - -void RolledSelectionListV::onPostRender() -{ -} - -void RolledSelectionListV::renderDirtBackground() -{ - float by0 = _renderTopBorder? y0 : 0; - float by1 = _renderBottomBorder? y1 : height; - - minecraft->textures->loadAndBindTexture("gui/background.png"); - glColor4f2(1.0f, 1, 1, 1); - float s = 32; - const float uvy = (float)((int) yo); - Tesselator& t = Tesselator::instance; - t.begin(); - t.color(0x202020); - t.vertexUV(x0, by1, 0, x0 / s, (by1+uvy) / s); - t.vertexUV(x1, by1, 0, x1 / s, (by1+uvy) / s); - t.vertexUV(x1, by0, 0, x1 / s, (by0+uvy) / s); - t.vertexUV(x0, by0, 0, x0 / s, (by0+uvy) / s); - t.draw(); - //LOGI("%f, %f - %f, %f\n", x0, by0, x1, by1); -} +#include "RolledSelectionListV.h" +#include "../../Minecraft.h" +#include "../../renderer/Tesselator.h" +#include "../../renderer/gles.h" +#include "../../../platform/input/Mouse.h" +#include "../../../util/Mth.h" +#include "../../renderer/Textures.h" + + +RolledSelectionListV::RolledSelectionListV( Minecraft* minecraft_, int width_, int height_, int x0_, int x1_, int y0_, int y1_, int itemHeight_ ) +: minecraft(minecraft_), + width(width_), + height(height_), + x0((float)x0_), + x1((float)x1_), + y0((float)y0_), + y1((float)y1_), + itemHeight(itemHeight_), + selectionY(-1), + lastSelectionTime(0), + lastSelection(-1), + renderSelection(true), + doRenderHeader(false), + headerHeight(0), + dragState(DRAG_OUTSIDE), + yDrag(0.0f), + yo(0.0f), + yoo(0.0f), + yInertia(0.0f), + _componentSelected(false), + _renderDirtBackground(true), + _renderTopBorder(true), + _renderBottomBorder(true), + _lastyoo(0), + _yinertia(0), + _stickPixels(0), + _lastxm(0), + _lastym(0) +{ + yo = yoo = 0;//(float)(-itemHeight) * 0.5f; + _lastyoo = yoo; +} + +void RolledSelectionListV::setRenderSelection( bool _renderSelection ) +{ + renderSelection = _renderSelection; +} + +void RolledSelectionListV::setComponentSelected(bool selected) { + _componentSelected = selected; +} + +void RolledSelectionListV::setRenderHeader( bool _renderHeader, int _headerHeight ) +{ + doRenderHeader = _renderHeader; + headerHeight = _headerHeight; + + if (!doRenderHeader) { + headerHeight = 0; + } +} + +int RolledSelectionListV::getMaxPosition() +{ + return getNumberOfItems() * itemHeight + headerHeight; +} + +int RolledSelectionListV::getItemAtPosition( int x, int y ) +{ + int clickSlotPos = (int)(y - y0 - headerHeight + (int) yo - 4); + int isInsideX = x >= x0 && x <= x1; + return isInsideX? getItemAtYPositionRaw(clickSlotPos) : -1; +} + +int RolledSelectionListV::getItemAtYPositionRaw(int y) { + int slot = y / itemHeight; + bool isInsideX = slot >= 0 && y >= 0 && slot < getNumberOfItems(); + return isInsideX? slot : -1; +} + +bool RolledSelectionListV::capYPosition() +{ + float max = getMaxPosition() - (y1 - y0 - 4); + if (max < 0) max /= 2; + if (yo < 0) yo = 0; + if (yo > max) yo = max; + return false; +/* + const float MinY = -itemHeight/2;//(float)(itemHeight-height)/2; + const float MaxY = MinY + (getNumberOfItems()-1) * itemHeight; + if (yo < MinY) { yo = MinY; yInertia = 0; return true; } + if (yo > MaxY) { yo = MaxY; yInertia = 0; return true; } + return false; + */ +} + +void RolledSelectionListV::tick() { + + if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) + { + _yinertia = _lastyoo - yoo; + } + _lastyoo = yoo; + + //yInertia = Mth::absDecrease(yInertia, 1.0f, 0); + + yoo = yo - yInertia; + + //LOGI("tick: %f, %f, %f\n", yo, yInertia, _yinertia); +} + +float RolledSelectionListV::getPos(float alpha) { + return yoo - yInertia * alpha; +} + +void RolledSelectionListV::render( int xm, int ym, float a ) +{ + _lastxm = xm; + _lastym = ym; + renderBackground(); + + int itemCount = getNumberOfItems(); + + //float yy0 = height / 2.0f + 124; + //float yy1 = yy0 + 6; + + if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) { + touched(); + //LOGI("DOWN ym: %d\n", ym); + if (ym >= y0 && ym <= y1) { + if (dragState == NO_DRAG) { + lastSelectionTime = getTimeMs(); + lastSelection = convertSelection( getItemAtPosition(width/2, ym), xm, ym ); + selectStart(lastSelection); + //LOGI("Sel : %d\n", lastSelection); + selectionY = ym; + _stickPixels = 10; + } + else if (dragState >= 0) { + float delta = (ym - yDrag); + float absDelta = Mth::abs(delta); + if (absDelta > _stickPixels) { + _stickPixels = 0; + delta -= delta>0? _stickPixels : -_stickPixels; + } else { + delta = 0; + _stickPixels -= absDelta; + } + yo -= delta; + yoo = yo; + } + dragState = DRAG_NORMAL; + } + } else { + if (dragState >= 0) { + if (dragState >= 0) { + yInertia = _yinertia < 0? Mth::Max(-10.0f, _yinertia) : Mth::Min(10.0f, _yinertia); + } + // kill small inertia values when releasing scrollist + if (std::abs(yInertia) <= 2.0001f) { + yInertia = 0.0f; + } + + if (std::abs(yInertia) <= 10 /*&& getTimeMs() - lastSelectionTime < 300 */) + { + //float clickSlotPos = (ym - x0 - headerHeight + (int) yo - 4); + int slot = convertSelection( getItemAtPosition(width/2, ym), xm, ym); + //LOGI("slot: %d, lt: %d. diff: %d - %d\n", slot, lastSelection, selectionX, xm); + if (xm >= x0 && xm <= x1 && slot >= 0 && slot == lastSelection && std::abs(selectionY - ym) < 10) + selectItem(slot, false); + } else { + selectCancel(); + } + } + + // if (slot >= 0 && std::abs(selectionX - xm) < itemWidth) + // { + // bool doubleClick = false; + // selectItem(slot, doubleClick); + // //xInertia = 0.0f; + // } + //} + dragState = NO_DRAG; + + yo = getPos(a); + } + yDrag = (float)ym; + + evaluate(xm, ym); + capYPosition(); + + Tesselator& t = Tesselator::instance; + + const int HalfWidth = 48; + int rowX = (int)(width / 2 - HalfWidth + 8); + int rowBaseY = (int)(y0 + 4 - (int) yo); + + if (_renderDirtBackground) + renderDirtBackground(); + + if (getNumberOfItems() == 0) yo = 0; + + //int rowY = (int)(height / 2 - HalfHeight + 8); + if (doRenderHeader) { + const int HalfWidth = 48; + int rowX = (int)(width / 2 - HalfWidth + 8); + int rowBaseY = (int)(y0 + 4 - (int) yo); + renderHeader(rowX, rowBaseY, t); + } + + onPreRender(); + for (int i = 0; i < itemCount; i++) { + + float y = (float)(rowBaseY + (i) * itemHeight + headerHeight); + float h = itemHeight - 4.0f; + + if (y > y1 || (y + h) < y0) { + continue; + } + + if (renderSelection && isSelectedItem(i)) { + //float y0 = height / 2.0f - HalfHeight - 4; + //float y1 = height / 2.0f + HalfHeight - 4; + //glColor4f2(1, 1, 1, 1); + //glDisable2(GL_TEXTURE_2D); + + //int ew = 0; + //int color = 0x808080; + //if (_componentSelected) { + // ew = 0; + // color = 0x7F89BF; + //} + //t.begin(); + //t.color(color); + //t.vertex(x - 2 - ew, y0 - ew, 0); + //t.vertex(x - 2 - ew, y1 + ew, 0); + //t.vertex(x + h + 2 + ew, y1 + ew, 0); + //t.vertex(x + h + 2 + ew, y0 - ew, 0); + + //t.color(0x000000); + //t.vertex(x - 1, y0 + 1, 0); + //t.vertex(x - 1, y1 - 1, 0); + //t.vertex(x + h + 1, y1 - 1, 0); + //t.vertex(x + h + 1, y0 + 1, 0); + + //t.draw(); + //glEnable2(GL_TEXTURE_2D); + } + renderItem(i, rowX, (int)y, (int)h, t); + } + onPostRender(); + + glDisable2(GL_DEPTH_TEST); + + if (_renderTopBorder) + renderHoleBackground(0, y0, 255, 255); + if (_renderBottomBorder) + renderHoleBackground(y1, (float)height, 255, 255); + renderForeground(); + + //glEnable2(GL_BLEND); + //glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glDisable2(GL_ALPHA_TEST); + //glShadeModel2(GL_SMOOTH); + + //glDisable2(GL_TEXTURE_2D); + + //const int d = 4; + //t.begin(); + //t.color(0x000000, 0); + //t.vertexUV(y0, x0 + d, 0, 0, 1); + //t.vertexUV(y1, x0 + d, 0, 1, 1); + //t.color(0x000000, 255); + //t.vertexUV(y1, x0, 0, 1, 0); + //t.vertexUV(y0, x0, 0, 0, 0); + //t.draw(); + + //t.begin(); + //t.color(0x000000, 255); + //t.vertexUV(y0, x1, 0, 0, 1); + //t.vertexUV(y1, x1, 0, 1, 1); + //t.color(0x000000, 0); + //t.vertexUV(y1, x1 - d, 0, 1, 0); + //t.vertexUV(y0, x1 - d, 0, 0, 0); + //t.draw(); + + //renderDecorations(xm, ym); + + //glEnable2(GL_TEXTURE_2D); + //glEnable2(GL_DEPTH_TEST); + + //glShadeModel2(GL_FLAT); + //glEnable2(GL_ALPHA_TEST); + //glDisable2(GL_BLEND); +} + +void RolledSelectionListV::renderHoleBackground( /*float x0, float x1,*/ float y0, float y1, int a0, int a1 ) +{ + Tesselator& t = Tesselator::instance; + minecraft->textures->loadAndBindTexture("gui/background.png"); + glColor4f2(1.0f, 1, 1, 1); + float s = 32; + t.begin(); + t.color(0x505050, a1); + t.vertexUV(0, y1, 0, 0, y1 / s); + t.vertexUV((float)width, y1, 0, width / s, y1 / s); + t.color(0x505050, a0); + t.vertexUV((float)width, y0, 0, width / s, y0 / s); + t.vertexUV(0, y0, 0, 0, y0 / s); + t.draw(); + //printf("x, y, x1, y1: %d, %d, %d, %d\n", 0, (int)y0, width, (int)y1); +} + +void RolledSelectionListV::touched() +{ +} + +void RolledSelectionListV::evaluate(int xm, int ym) +{ + if (std::abs(selectionY - ym) >= 10) { + lastSelection = -1; + selectCancel(); + } +} + +void RolledSelectionListV::onPreRender() +{ +} + +void RolledSelectionListV::onPostRender() +{ +} + +void RolledSelectionListV::renderDirtBackground() +{ + float by0 = _renderTopBorder? y0 : 0; + float by1 = _renderBottomBorder? y1 : height; + + minecraft->textures->loadAndBindTexture("gui/background.png"); + glColor4f2(1.0f, 1, 1, 1); + float s = 32; + const float uvy = (float)((int) yo); + Tesselator& t = Tesselator::instance; + t.begin(); + t.color(0x202020); + t.vertexUV(x0, by1, 0, x0 / s, (by1+uvy) / s); + t.vertexUV(x1, by1, 0, x1 / s, (by1+uvy) / s); + t.vertexUV(x1, by0, 0, x1 / s, (by0+uvy) / s); + t.vertexUV(x0, by0, 0, x0 / s, (by0+uvy) / s); + t.draw(); + //LOGI("%f, %f - %f, %f\n", x0, by0, x1, by1); +} diff --git a/src/client/gui/components/RolledSelectionListV.h b/src/client/gui/components/RolledSelectionListV.h index 09fb85f..f10728e 100755 --- a/src/client/gui/components/RolledSelectionListV.h +++ b/src/client/gui/components/RolledSelectionListV.h @@ -1,94 +1,94 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListV_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListV_H__ - -#include "../GuiComponent.h" -class Minecraft; -class Tesselator; - - -class RolledSelectionListV : public GuiComponent -{ - static const int NO_DRAG = -1; - static const int DRAG_OUTSIDE = -2; - static const int DRAG_NORMAL = 0; -public: - RolledSelectionListV(Minecraft* minecraft, int width, int height, int x0, int x1, int y0, int y1, int itemHeight); - - virtual int getItemAtPosition(int x, int y); - - virtual bool capYPosition(); - - virtual void tick(); - virtual void render(int xm, int ym, float a); - virtual void renderHoleBackground(/*float x0, float x1,*/ float y0, float y1, int a0, int a1); - virtual void setRenderSelection(bool _renderSelection); - virtual void setComponentSelected(bool selected); -protected: - void setRenderHeader(bool _renderHeader, int _headerHeight); - - virtual int getNumberOfItems() = 0; - - virtual void selectStart(int item) {} - virtual void selectCancel() {} - virtual void selectItem(int item, bool doubleClick) = 0; - virtual bool isSelectedItem(int item) = 0; - - virtual int getMaxPosition(); - virtual float getPos(float alpha); - virtual void touched(); - - virtual void renderItem(int i, int x, int y, int h, Tesselator& t) = 0; - virtual void renderHeader(int x, int y, Tesselator& t) {} - virtual void renderBackground() = 0; - virtual void renderForeground() {} - virtual void renderDecorations(int mouseX, int mouseY) {} - - virtual void clickedHeader(int headerMouseX, int headerMouseY) {} - virtual int convertSelection(int item, int xm, int ym) { return item; } - - int getItemAtYPositionRaw(int y); - void evaluate(int xm, int ym); - virtual void onPreRender(); - virtual void onPostRender(); - void renderDirtBackground(); -protected: - Minecraft* minecraft; - - float x0; - float x1; - int itemHeight; - int width; - int height; -//private: - float y0; - float y1; - - int dragState; - float yDrag; - float yo; - float yoo; - float yInertia; - float _yinertia; - - int selectionY; - bool renderSelection; - bool _componentSelected; - - bool _renderDirtBackground; - bool _renderTopBorder; - bool _renderBottomBorder; - - int _lastxm; - int _lastym; -private: - int headerHeight; - bool doRenderHeader; - long lastSelectionTime; - int lastSelection; - - float _lastyoo; - - float _stickPixels; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListV_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListV_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListV_H__ + +#include "../GuiComponent.h" +class Minecraft; +class Tesselator; + + +class RolledSelectionListV : public GuiComponent +{ + static const int NO_DRAG = -1; + static const int DRAG_OUTSIDE = -2; + static const int DRAG_NORMAL = 0; +public: + RolledSelectionListV(Minecraft* minecraft, int width, int height, int x0, int x1, int y0, int y1, int itemHeight); + + virtual int getItemAtPosition(int x, int y); + + virtual bool capYPosition(); + + virtual void tick(); + virtual void render(int xm, int ym, float a); + virtual void renderHoleBackground(/*float x0, float x1,*/ float y0, float y1, int a0, int a1); + virtual void setRenderSelection(bool _renderSelection); + virtual void setComponentSelected(bool selected); +protected: + void setRenderHeader(bool _renderHeader, int _headerHeight); + + virtual int getNumberOfItems() = 0; + + virtual void selectStart(int item) {} + virtual void selectCancel() {} + virtual void selectItem(int item, bool doubleClick) = 0; + virtual bool isSelectedItem(int item) = 0; + + virtual int getMaxPosition(); + virtual float getPos(float alpha); + virtual void touched(); + + virtual void renderItem(int i, int x, int y, int h, Tesselator& t) = 0; + virtual void renderHeader(int x, int y, Tesselator& t) {} + virtual void renderBackground() = 0; + virtual void renderForeground() {} + virtual void renderDecorations(int mouseX, int mouseY) {} + + virtual void clickedHeader(int headerMouseX, int headerMouseY) {} + virtual int convertSelection(int item, int xm, int ym) { return item; } + + int getItemAtYPositionRaw(int y); + void evaluate(int xm, int ym); + virtual void onPreRender(); + virtual void onPostRender(); + void renderDirtBackground(); +protected: + Minecraft* minecraft; + + float x0; + float x1; + int itemHeight; + int width; + int height; +//private: + float y0; + float y1; + + int dragState; + float yDrag; + float yo; + float yoo; + float yInertia; + float _yinertia; + + int selectionY; + bool renderSelection; + bool _componentSelected; + + bool _renderDirtBackground; + bool _renderTopBorder; + bool _renderBottomBorder; + + int _lastxm; + int _lastym; +private: + int headerHeight; + bool doRenderHeader; + long lastSelectionTime; + int lastSelection; + + float _lastyoo; + + float _stickPixels; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__RolledSelectionListV_H__*/ diff --git a/src/client/gui/components/ScrolledSelectionList.cpp b/src/client/gui/components/ScrolledSelectionList.cpp index 55a643f..659bf9d 100755 --- a/src/client/gui/components/ScrolledSelectionList.cpp +++ b/src/client/gui/components/ScrolledSelectionList.cpp @@ -1,296 +1,296 @@ -#include "ScrolledSelectionList.h" -#include "../../Minecraft.h" -#include "../../renderer/Tesselator.h" -#include "../../renderer/gles.h" -#include "../../../platform/input/Mouse.h" -#include "../../renderer/Textures.h" - -static int Abs(int d) { - return d >= 0? d : -d; -} - -ScrolledSelectionList::ScrolledSelectionList( Minecraft* _minecraft, int _width, int _height, int _y0, int _y1, int _itemHeight ) -: minecraft(_minecraft), - width(_width), - height(_height), - y0((float)_y0), - y1((float)_y1), - itemHeight(_itemHeight), - x0(0.0f), - x1((float)_width), - selectionY(-1), - lastSelectionTime(0), - renderSelection(true), - doRenderHeader(false), - headerHeight(0), - dragState(DRAG_OUTSIDE), - yDrag(0.0f), - yo(0.0f), - yInertia(0.0f) -{ -} - -void ScrolledSelectionList::setRenderSelection( bool _renderSelection ) -{ - renderSelection = _renderSelection; -} - -void ScrolledSelectionList::setRenderHeader( bool _renderHeader, int _headerHeight ) -{ - doRenderHeader = _renderHeader; - headerHeight = _headerHeight; - - if (!doRenderHeader) { - headerHeight = 0; - } -} - -int ScrolledSelectionList::getMaxPosition() -{ - return getNumberOfItems() * itemHeight + headerHeight; -} - -int ScrolledSelectionList::getItemAtPosition( int x, int y ) -{ - int x0 = width / 2 - (92 + 16 + 2); - int x1 = width / 2 + (92 + 16 + 2); - - int clickSlotPos = (int)(y - y0 - headerHeight + (int) yo - 4); - int slot = clickSlotPos / itemHeight; - if (x >= x0 && x <= x1 && slot >= 0 && clickSlotPos >= 0 && slot < getNumberOfItems()) { - return slot; - } - return -1; -} - -void ScrolledSelectionList::capYPosition() -{ - float max = getMaxPosition() - (y1 - y0 - 4); - if (max < 0) max /= 2; - if (yo < 0) yo = 0; - if (yo > max) yo = max; -} - -void ScrolledSelectionList::render( int xm, int ym, float a ) -{ - renderBackground(); - - int itemCount = getNumberOfItems(); - - //float xx0 = width / 2.0f + 124; - //float xx1 = xx0 + 6; - - - if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) { - //LOGI("DOWN ym: %d\n", ym); - if (ym >= y0 && ym <= y1 && ym != ignoreY) { - if (dragState == NO_DRAG) { - dragState = DRAG_SKIP; - } - else if (dragState >= 0) - { - if (dragState == DRAG_SKIP) - { - lastSelectionTime = getTimeMs(); - selectionY = ym; - } - else if (dragState == DRAG_NORMAL) - { - yo -= (ym - yDrag); - yInertia += (float)(ym - yDrag); - } - dragState = DRAG_NORMAL; - } - ignoreY = -1; - } - - } else { - if (dragState != NO_DRAG) - { - //LOGI("UP ym: %d\n", ym); - } - //ignoreY = ym; - - // kill small inertia values when releasing scrollist - if (dragState >= 0 && std::abs(yInertia) < 2) - { - yInertia = 0.0f; - } - - if (dragState >= 0 && getTimeMs() - lastSelectionTime < 300) - { - float clickSlotPos = (ym - y0 - headerHeight + (int) yo - 4); - int slot = (int)clickSlotPos / itemHeight; - - if (slot >= 0 && Abs(selectionY - ym) < itemHeight) - { - bool doubleClick = false; - selectItem(slot, doubleClick); - yInertia = 0.0f; - } - } - dragState = NO_DRAG; - - yo -= yInertia; - } - yInertia = yInertia * .75f; - yDrag = (float)ym; - - capYPosition(); - - Tesselator& t = Tesselator::instance; - - renderDirtBackground(); - - int rowX = (int)(width / 2 - 92 - 16); - int rowBaseY = (int)(y0 + 4 - (int) yo); - - if (doRenderHeader) { - renderHeader(rowX, rowBaseY, t); - } - - for (int i = 0; i < itemCount; i++) { - - float y = (float)(rowBaseY + (i) * itemHeight + headerHeight); - float h = itemHeight - 4.0f; - - if (y > y1 || (y + h) < y0) { - continue; - } - - if (renderSelection && isSelectedItem(i)) { - float x0 = width / 2.0f - (92 + 16 + 2); - float x1 = width / 2.0f + (92 + 16 + 2); - glColor4f2(1, 1, 1, 1); - glDisable2(GL_TEXTURE_2D); - t.begin(); - t.color(0x808080); - t.vertexUV(x0, y + h + 2, 0, 0, 1); - t.vertexUV(x1, y + h + 2, 0, 1, 1); - t.vertexUV(x1, y - 2, 0, 1, 0); - t.vertexUV(x0, y - 2, 0, 0, 0); - - t.color(0x000000); - t.vertexUV(x0 + 1, y + h + 1, 0, 0, 1); - t.vertexUV(x1 - 1, y + h + 1, 0, 1, 1); - t.vertexUV(x1 - 1, y - 1, 0, 1, 0); - t.vertexUV(x0 + 1, y - 1, 0, 0, 0); - - t.draw(); - glEnable2(GL_TEXTURE_2D); - } - - renderItem(i, rowX, (int)y, (int)h, t); - - } - - glDisable2(GL_DEPTH_TEST); - - - int d = 4; - - renderHoleBackground(0, y0, 255, 255); - renderHoleBackground(y1, (float)height, 255, 255); - - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable2(GL_ALPHA_TEST); - glShadeModel2(GL_SMOOTH); - - glDisable2(GL_TEXTURE_2D); - - t.begin(); - t.color(0x000000, 0); - t.vertexUV(x0, y0 + d, 0, 0, 1); - t.vertexUV(x1, y0 + d, 0, 1, 1); - t.color(0x000000, 255); - t.vertexUV(x1, y0, 0, 1, 0); - t.vertexUV(x0, y0, 0, 0, 0); - t.draw(); - - t.begin(); - t.color(0x000000, 255); - t.vertexUV(x0, y1, 0, 0, 1); - t.vertexUV(x1, y1, 0, 1, 1); - t.color(0x000000, 0); - t.vertexUV(x1, y1 - d, 0, 1, 0); - t.vertexUV(x0, y1 - d, 0, 0, 0); - t.draw(); - - // { - // float max = getMaxPosition() - (y1 - y0 - 4); - // if (max > 0) { - // float barHeight = (y1 - y0) * (y1 - y0) / (getMaxPosition()); - // if (barHeight < 32) barHeight = 32; - // if (barHeight > (y1 - y0 - 8)) barHeight = (y1 - y0 - 8); - // - // float yp = (int) yo * (y1 - y0 - barHeight) / max + y0; - // if (yp < y0) yp = y0; - // - // t.begin(); - // t.color(0x000000, 255); - // t.vertexUV(xx0, y1, 0.0f, 0.0f, 1.0f); - // t.vertexUV(xx1, y1, 0.0f, 1.0f, 1.0f); - // t.vertexUV(xx1, y0, 0.0f, 1.0f, 0.0f); - // t.vertexUV(xx0, y0, 0.0f, 0.0f, 0.0f); - // t.draw(); - // - // t.begin(); - // t.color(0x808080, 255); - // t.vertexUV(xx0, yp + barHeight, 0, 0, 1); - // t.vertexUV(xx1, yp + barHeight, 0, 1, 1); - // t.vertexUV(xx1, yp, 0, 1, 0); - // t.vertexUV(xx0, yp, 0, 0, 0); - // t.draw(); - // - // t.begin(); - // t.color(0xc0c0c0, 255); - // t.vertexUV(xx0, yp + barHeight - 1, 0, 0, 1); - // t.vertexUV(xx1 - 1, yp + barHeight - 1, 0, 1, 1); - // t.vertexUV(xx1 - 1, yp, 0, 1, 0); - // t.vertexUV(xx0, yp, 0, 0, 0); - // t.draw(); - // } - // } - - renderDecorations(xm, ym); - - - glEnable2(GL_TEXTURE_2D); - glEnable2(GL_DEPTH_TEST); - - glShadeModel2(GL_FLAT); - glEnable2(GL_ALPHA_TEST); - glDisable2(GL_BLEND); -} - -void ScrolledSelectionList::renderHoleBackground( float y0, float y1, int a0, int a1 ) -{ - Tesselator& t = Tesselator::instance; - minecraft->textures->loadAndBindTexture("gui/background.png"); - glColor4f2(1.0f, 1, 1, 1); - float s = 32; - t.begin(); - t.color(0x505050, a1); - t.vertexUV(0, y1, 0, 0, y1 / s); - t.vertexUV((float)width, y1, 0, width / s, y1 / s); - t.color(0x505050, a0); - t.vertexUV((float)width, y0, 0, width / s, y0 / s); - t.vertexUV(0, y0, 0, 0, y0 / s); - t.draw(); -} - -void ScrolledSelectionList::renderDirtBackground() -{ - Tesselator& t = Tesselator::instance; - minecraft->textures->loadAndBindTexture("gui/background.png"); - glColor4f2(1.0f, 1, 1, 1); - float s = 32; - t.begin(); - t.color(0x202020); - t.vertexUV(x0, y1, 0, x0 / s, (y1 + (int) yo) / s); - t.vertexUV(x1, y1, 0, x1 / s, (y1 + (int) yo) / s); - t.vertexUV(x1, y0, 0, x1 / s, (y0 + (int) yo) / s); - t.vertexUV(x0, y0, 0, x0 / s, (y0 + (int) yo) / s); - t.draw(); -} +#include "ScrolledSelectionList.h" +#include "../../Minecraft.h" +#include "../../renderer/Tesselator.h" +#include "../../renderer/gles.h" +#include "../../../platform/input/Mouse.h" +#include "../../renderer/Textures.h" + +static int Abs(int d) { + return d >= 0? d : -d; +} + +ScrolledSelectionList::ScrolledSelectionList( Minecraft* _minecraft, int _width, int _height, int _y0, int _y1, int _itemHeight ) +: minecraft(_minecraft), + width(_width), + height(_height), + y0((float)_y0), + y1((float)_y1), + itemHeight(_itemHeight), + x0(0.0f), + x1((float)_width), + selectionY(-1), + lastSelectionTime(0), + renderSelection(true), + doRenderHeader(false), + headerHeight(0), + dragState(DRAG_OUTSIDE), + yDrag(0.0f), + yo(0.0f), + yInertia(0.0f) +{ +} + +void ScrolledSelectionList::setRenderSelection( bool _renderSelection ) +{ + renderSelection = _renderSelection; +} + +void ScrolledSelectionList::setRenderHeader( bool _renderHeader, int _headerHeight ) +{ + doRenderHeader = _renderHeader; + headerHeight = _headerHeight; + + if (!doRenderHeader) { + headerHeight = 0; + } +} + +int ScrolledSelectionList::getMaxPosition() +{ + return getNumberOfItems() * itemHeight + headerHeight; +} + +int ScrolledSelectionList::getItemAtPosition( int x, int y ) +{ + int x0 = width / 2 - (92 + 16 + 2); + int x1 = width / 2 + (92 + 16 + 2); + + int clickSlotPos = (int)(y - y0 - headerHeight + (int) yo - 4); + int slot = clickSlotPos / itemHeight; + if (x >= x0 && x <= x1 && slot >= 0 && clickSlotPos >= 0 && slot < getNumberOfItems()) { + return slot; + } + return -1; +} + +void ScrolledSelectionList::capYPosition() +{ + float max = getMaxPosition() - (y1 - y0 - 4); + if (max < 0) max /= 2; + if (yo < 0) yo = 0; + if (yo > max) yo = max; +} + +void ScrolledSelectionList::render( int xm, int ym, float a ) +{ + renderBackground(); + + int itemCount = getNumberOfItems(); + + //float xx0 = width / 2.0f + 124; + //float xx1 = xx0 + 6; + + + if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) { + //LOGI("DOWN ym: %d\n", ym); + if (ym >= y0 && ym <= y1 && ym != ignoreY) { + if (dragState == NO_DRAG) { + dragState = DRAG_SKIP; + } + else if (dragState >= 0) + { + if (dragState == DRAG_SKIP) + { + lastSelectionTime = getTimeMs(); + selectionY = ym; + } + else if (dragState == DRAG_NORMAL) + { + yo -= (ym - yDrag); + yInertia += (float)(ym - yDrag); + } + dragState = DRAG_NORMAL; + } + ignoreY = -1; + } + + } else { + if (dragState != NO_DRAG) + { + //LOGI("UP ym: %d\n", ym); + } + //ignoreY = ym; + + // kill small inertia values when releasing scrollist + if (dragState >= 0 && std::abs(yInertia) < 2) + { + yInertia = 0.0f; + } + + if (dragState >= 0 && getTimeMs() - lastSelectionTime < 300) + { + float clickSlotPos = (ym - y0 - headerHeight + (int) yo - 4); + int slot = (int)clickSlotPos / itemHeight; + + if (slot >= 0 && Abs(selectionY - ym) < itemHeight) + { + bool doubleClick = false; + selectItem(slot, doubleClick); + yInertia = 0.0f; + } + } + dragState = NO_DRAG; + + yo -= yInertia; + } + yInertia = yInertia * .75f; + yDrag = (float)ym; + + capYPosition(); + + Tesselator& t = Tesselator::instance; + + renderDirtBackground(); + + int rowX = (int)(width / 2 - 92 - 16); + int rowBaseY = (int)(y0 + 4 - (int) yo); + + if (doRenderHeader) { + renderHeader(rowX, rowBaseY, t); + } + + for (int i = 0; i < itemCount; i++) { + + float y = (float)(rowBaseY + (i) * itemHeight + headerHeight); + float h = itemHeight - 4.0f; + + if (y > y1 || (y + h) < y0) { + continue; + } + + if (renderSelection && isSelectedItem(i)) { + float x0 = width / 2.0f - (92 + 16 + 2); + float x1 = width / 2.0f + (92 + 16 + 2); + glColor4f2(1, 1, 1, 1); + glDisable2(GL_TEXTURE_2D); + t.begin(); + t.color(0x808080); + t.vertexUV(x0, y + h + 2, 0, 0, 1); + t.vertexUV(x1, y + h + 2, 0, 1, 1); + t.vertexUV(x1, y - 2, 0, 1, 0); + t.vertexUV(x0, y - 2, 0, 0, 0); + + t.color(0x000000); + t.vertexUV(x0 + 1, y + h + 1, 0, 0, 1); + t.vertexUV(x1 - 1, y + h + 1, 0, 1, 1); + t.vertexUV(x1 - 1, y - 1, 0, 1, 0); + t.vertexUV(x0 + 1, y - 1, 0, 0, 0); + + t.draw(); + glEnable2(GL_TEXTURE_2D); + } + + renderItem(i, rowX, (int)y, (int)h, t); + + } + + glDisable2(GL_DEPTH_TEST); + + + int d = 4; + + renderHoleBackground(0, y0, 255, 255); + renderHoleBackground(y1, (float)height, 255, 255); + + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable2(GL_ALPHA_TEST); + glShadeModel2(GL_SMOOTH); + + glDisable2(GL_TEXTURE_2D); + + t.begin(); + t.color(0x000000, 0); + t.vertexUV(x0, y0 + d, 0, 0, 1); + t.vertexUV(x1, y0 + d, 0, 1, 1); + t.color(0x000000, 255); + t.vertexUV(x1, y0, 0, 1, 0); + t.vertexUV(x0, y0, 0, 0, 0); + t.draw(); + + t.begin(); + t.color(0x000000, 255); + t.vertexUV(x0, y1, 0, 0, 1); + t.vertexUV(x1, y1, 0, 1, 1); + t.color(0x000000, 0); + t.vertexUV(x1, y1 - d, 0, 1, 0); + t.vertexUV(x0, y1 - d, 0, 0, 0); + t.draw(); + + // { + // float max = getMaxPosition() - (y1 - y0 - 4); + // if (max > 0) { + // float barHeight = (y1 - y0) * (y1 - y0) / (getMaxPosition()); + // if (barHeight < 32) barHeight = 32; + // if (barHeight > (y1 - y0 - 8)) barHeight = (y1 - y0 - 8); + // + // float yp = (int) yo * (y1 - y0 - barHeight) / max + y0; + // if (yp < y0) yp = y0; + // + // t.begin(); + // t.color(0x000000, 255); + // t.vertexUV(xx0, y1, 0.0f, 0.0f, 1.0f); + // t.vertexUV(xx1, y1, 0.0f, 1.0f, 1.0f); + // t.vertexUV(xx1, y0, 0.0f, 1.0f, 0.0f); + // t.vertexUV(xx0, y0, 0.0f, 0.0f, 0.0f); + // t.draw(); + // + // t.begin(); + // t.color(0x808080, 255); + // t.vertexUV(xx0, yp + barHeight, 0, 0, 1); + // t.vertexUV(xx1, yp + barHeight, 0, 1, 1); + // t.vertexUV(xx1, yp, 0, 1, 0); + // t.vertexUV(xx0, yp, 0, 0, 0); + // t.draw(); + // + // t.begin(); + // t.color(0xc0c0c0, 255); + // t.vertexUV(xx0, yp + barHeight - 1, 0, 0, 1); + // t.vertexUV(xx1 - 1, yp + barHeight - 1, 0, 1, 1); + // t.vertexUV(xx1 - 1, yp, 0, 1, 0); + // t.vertexUV(xx0, yp, 0, 0, 0); + // t.draw(); + // } + // } + + renderDecorations(xm, ym); + + + glEnable2(GL_TEXTURE_2D); + glEnable2(GL_DEPTH_TEST); + + glShadeModel2(GL_FLAT); + glEnable2(GL_ALPHA_TEST); + glDisable2(GL_BLEND); +} + +void ScrolledSelectionList::renderHoleBackground( float y0, float y1, int a0, int a1 ) +{ + Tesselator& t = Tesselator::instance; + minecraft->textures->loadAndBindTexture("gui/background.png"); + glColor4f2(1.0f, 1, 1, 1); + float s = 32; + t.begin(); + t.color(0x505050, a1); + t.vertexUV(0, y1, 0, 0, y1 / s); + t.vertexUV((float)width, y1, 0, width / s, y1 / s); + t.color(0x505050, a0); + t.vertexUV((float)width, y0, 0, width / s, y0 / s); + t.vertexUV(0, y0, 0, 0, y0 / s); + t.draw(); +} + +void ScrolledSelectionList::renderDirtBackground() +{ + Tesselator& t = Tesselator::instance; + minecraft->textures->loadAndBindTexture("gui/background.png"); + glColor4f2(1.0f, 1, 1, 1); + float s = 32; + t.begin(); + t.color(0x202020); + t.vertexUV(x0, y1, 0, x0 / s, (y1 + (int) yo) / s); + t.vertexUV(x1, y1, 0, x1 / s, (y1 + (int) yo) / s); + t.vertexUV(x1, y0, 0, x1 / s, (y0 + (int) yo) / s); + t.vertexUV(x0, y0, 0, x0 / s, (y0 + (int) yo) / s); + t.draw(); +} diff --git a/src/client/gui/components/ScrolledSelectionList.h b/src/client/gui/components/ScrolledSelectionList.h index bb6e9e2..3ae3ccd 100755 --- a/src/client/gui/components/ScrolledSelectionList.h +++ b/src/client/gui/components/ScrolledSelectionList.h @@ -1,69 +1,69 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrolledSelectionList_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrolledSelectionList_H__ - -#include "../GuiComponent.h" -class Minecraft; -class Tesselator; - - -class ScrolledSelectionList : public GuiComponent -{ - static const int NO_DRAG = -1; - static const int DRAG_OUTSIDE = -2; - static const int DRAG_NORMAL = 0; - static const int DRAG_SKIP = 1; // special case to fix android jump bug -public: - ScrolledSelectionList(Minecraft* _minecraft, int _width, int _height, int _y0, int _y1, int _itemHeight); - - virtual void setRenderSelection(bool _renderSelection); -protected: - void setRenderHeader(bool _renderHeader, int _headerHeight); - - virtual int getNumberOfItems() = 0; - - virtual void selectItem(int item, bool doubleClick) = 0; - virtual bool isSelectedItem(int item) = 0; - - virtual int getMaxPosition(); - - virtual void renderItem(int i, int x, int y, int h, Tesselator& t) = 0; - virtual void renderHeader(int x, int y, Tesselator& t) {} - virtual void renderBackground() = 0; - virtual void renderDecorations(int mouseX, int mouseY) {} - - virtual void clickedHeader(int headerMouseX, int headerMouseY) {} -public: - virtual int getItemAtPosition(int x, int y); - - virtual void capYPosition(); - - virtual void render(int xm, int ym, float a); - virtual void renderHoleBackground(float y0, float y1, int a0, int a1); - void renderDirtBackground(); -protected: - Minecraft* minecraft; - - float y0; - float y1; - int itemHeight; -private: - int width; - int height; - float x1; - float x0; - - int ignoreY; // new attempt to fix android jump bug - int dragState; - float yDrag; - float yo; - float yInertia; - - int selectionY; - long lastSelectionTime; - - bool renderSelection; - bool doRenderHeader; - int headerHeight; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrolledSelectionList_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrolledSelectionList_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrolledSelectionList_H__ + +#include "../GuiComponent.h" +class Minecraft; +class Tesselator; + + +class ScrolledSelectionList : public GuiComponent +{ + static const int NO_DRAG = -1; + static const int DRAG_OUTSIDE = -2; + static const int DRAG_NORMAL = 0; + static const int DRAG_SKIP = 1; // special case to fix android jump bug +public: + ScrolledSelectionList(Minecraft* _minecraft, int _width, int _height, int _y0, int _y1, int _itemHeight); + + virtual void setRenderSelection(bool _renderSelection); +protected: + void setRenderHeader(bool _renderHeader, int _headerHeight); + + virtual int getNumberOfItems() = 0; + + virtual void selectItem(int item, bool doubleClick) = 0; + virtual bool isSelectedItem(int item) = 0; + + virtual int getMaxPosition(); + + virtual void renderItem(int i, int x, int y, int h, Tesselator& t) = 0; + virtual void renderHeader(int x, int y, Tesselator& t) {} + virtual void renderBackground() = 0; + virtual void renderDecorations(int mouseX, int mouseY) {} + + virtual void clickedHeader(int headerMouseX, int headerMouseY) {} +public: + virtual int getItemAtPosition(int x, int y); + + virtual void capYPosition(); + + virtual void render(int xm, int ym, float a); + virtual void renderHoleBackground(float y0, float y1, int a0, int a1); + void renderDirtBackground(); +protected: + Minecraft* minecraft; + + float y0; + float y1; + int itemHeight; +private: + int width; + int height; + float x1; + float x0; + + int ignoreY; // new attempt to fix android jump bug + int dragState; + float yDrag; + float yo; + float yInertia; + + int selectionY; + long lastSelectionTime; + + bool renderSelection; + bool doRenderHeader; + int headerHeight; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrolledSelectionList_H__*/ diff --git a/src/client/gui/components/ScrollingPane.cpp b/src/client/gui/components/ScrollingPane.cpp index 090bc3a..e646ddf 100755 --- a/src/client/gui/components/ScrollingPane.cpp +++ b/src/client/gui/components/ScrollingPane.cpp @@ -1,805 +1,805 @@ -#include "ScrollingPane.h" -#include "../../renderer/gles.h" -#include "../Gui.h" -#include "../../../util/Mth.h" -#include "../../../SharedConstants.h" - -#define STR(x) (x.toString().c_str()) - -static const float kPenetrationDeceleration = 0.03f; -static const float kPenetrationAcceleration = 0.08f; -static const float kMaxTrackingTime = 100.0f; -static const float kAcceleration = 15; -static const float kMinVelocityForDecelerationWithPaging = 4 / 3.0f; -static const float kMinVelocityForDeceleration = 1 / 3.0f; -static const float kDesiredAnimationFrameRate = 1000.0f / 60; -static const float kDecelerationFrictionFactor = 0.95f; -static const float kMinimumVelocityToHideScrollIndicators = 0.05f; -static const float kMinimumVelocity = 0.01f; -static const float kMinimumTrackingForDrag = 5; -static const float kMinIndicatorLength = 34.0f / 3; -static const float PKScrollIndicatorEndSize = 3; -static const float PKScrollIndicatorThickness = 7.0f /3; -static const float kWheelOverscrollMax = 80.0f; -static const float kWheelOverscrollDamping = 0.6f; -static const float kWheelOverscrollRestoreAlpha = 0.18f; - -ScrollingPane::ScrollingPane( - int optionFlags, - const IntRectangle& boundingBox, - const IntRectangle& itemRect, - int columns, - int numItems, - float screenScale /* = 1.0f */, - const IntRectangle& itemBoundingRect /*= IntRectangle(0,0,0,0)*/ ) -: flags(optionFlags), - bbox(boundingBox), - size(boundingBox), - area((float)bbox.x, (float)bbox.y, (float)(bbox.x + bbox.w), (float)(bbox.y + bbox.h)), - bboxArea(area), - itemRect(itemRect), - numItems(numItems), - screenScale(screenScale), - invScreenScale(1.0f / screenScale), - //hasItemBounding(), - px(0), py(0), fpx(0), fpy(0), - dx(0), dy(0), - dragState(-1), - dragLastDeltaTimeStamp(-1.0f), - friction(0.95f), - highlightTimer(-1), - highlightStarted(-1), - selectedId(-1), - decelerating(false), - tracking(false), - dragging(false), - pagingEnabled(false), - _scrollEnabled(true), - _timer(60), - _doStepTimer(false), - _wasDown(false),//Mouse::isButtonDown(MouseAction::ACTION_LEFT)), - _lx(-9999), _ly(-9999) -{ - if (itemBoundingRect.w > 0) - itemBbox = itemBoundingRect; - else - itemBbox = itemRect; - - this->columns = (columns>0)? columns : bbox.w / itemBbox.w; - - if (this->columns <= 0) { - LOGW("Columns are 0! Area width is smaller than item width. Setting columns to 1.\n"); - this->columns = 1; - } - //LOGI("%d, %d :: %d\n", bbox.w, itemBbox.w, this->columns); - - rows = 1 + (numItems-1) / this->columns; - - /* - if (columns * itemBbox.w <= bbox.w) flags |= SF_LockX; - if (rows * itemBbox.h <= bbox.h) flags |= SF_LockY; - */ - - // initialize content bounds immediately - adjustContentSize(); - minPoint.set((float)(this->size.w - this->adjustedContentSize.w), (float)(this->size.h - this->adjustedContentSize.h), 0); - this->snapContentOffsetToBounds(false); - - - dragDeltas.reserve(128); - - te_moved = 0; - te_ended = 1; - - selected = new bool[numItems]; - for (int i = 0; i < numItems; ++i) - selected[i] = false; - - // Setup the scroll bars - vScroll.w = vScroll.h = PKScrollIndicatorThickness; - hScroll.w = hScroll.h = PKScrollIndicatorThickness; - vScroll.x = bbox.x + bbox.w - vScroll.w; - vScroll.y = 0; - hScroll.x = 0; - hScroll.y = bbox.y + bbox.h - hScroll.h; - -// vScroll.alpha -// vScroll.fading = hScroll.fading = -1; -} - -ScrollingPane::~ScrollingPane() { - delete[] selected; -} - -//void ScrollingPane::init(Minecraft* mc, int width, int height) { -// this->mc = mc; -// this->width = width; -// this->height = height; -//} - -void ScrollingPane::tick() { - - if (isSet(SF_ShowScrollbar)) { - updateScrollFade(vScroll); - updateScrollFade(hScroll); - } - - if (isNotSet(SF_HardLimits) && !Mouse::isButtonDown(MouseAction::ACTION_LEFT) && !dragging && !tracking && !decelerating) { - float targetX = _contentOffset.x; - float targetY = _contentOffset.y; - bool corrected = false; - - if (targetX > 0.0f) { - targetX = Mth::lerp(targetX, 0.0f, kWheelOverscrollRestoreAlpha); - corrected = true; - } else if (targetX < minPoint.x) { - targetX = Mth::lerp(targetX, minPoint.x, kWheelOverscrollRestoreAlpha); - corrected = true; - } - - if (targetY > 0.0f) { - targetY = Mth::lerp(targetY, 0.0f, kWheelOverscrollRestoreAlpha); - corrected = true; - } else if (targetY < minPoint.y) { - targetY = Mth::lerp(targetY, minPoint.y, kWheelOverscrollRestoreAlpha); - corrected = true; - } - - if (corrected) { - if (Mth::abs(targetX - _contentOffset.x) < 0.25f) targetX = (targetX > 0.0f ? 0.0f : (targetX < minPoint.x ? minPoint.x : targetX)); - if (Mth::abs(targetY - _contentOffset.y) < 0.25f) targetY = (targetY > 0.0f ? 0.0f : (targetY < minPoint.y ? minPoint.y : targetY)); - setContentOffset(Vec3(targetX, targetY, 0)); - } - } -} - -bool ScrollingPane::getGridItemFor_slow(int itemIndex, GridItem& out) { - GridItem nw = getItemForPos(0, 0, false); - GridItem se = getItemForPos((float)bbox.w - 1, (float)bbox.h - 1, false); - const float bxx = bbox.x - (fpx /*+ dx*alpha*/) + (nw.xf - nw.x); - const float byy = bbox.y - (fpy /*+ dy*alpha*/) + (nw.yf - nw.y); - - int y = itemIndex / columns; - int x = itemIndex - (y * columns); - - out.id = itemIndex; - out.xf = bxx + x * itemBbox.w; - out.yf = byy + y * itemBbox.h; - out.x = x; - out.y = y; - - return x >= nw.x && x <= se.x - && y >= nw.y && y <= se.y; -} - - -void ScrollingPane::render( int xm, int ym, float alpha ) { - // Handle user interaction first - handleUserInput(); - - _timer.advanceTime(); - - if (_doStepTimer && !te_moved) { - for (int i = 0; i < _timer.ticks; ++i) - stepThroughDecelerationAnimation(false); - this->lastFrame = getTimeMs(); - } - - // Render - //if (isSet(SF_Scissor)) { - // glEnable2(GL_SCISSOR_TEST); - // GLuint x = (GLuint)(screenScale * bbox.x); - // GLuint y = 480 - (GLuint)(screenScale * (bbox.y + bbox.h)); - // GLuint w = (GLuint)(screenScale * bbox.w); - // GLuint h = (GLuint)(screenScale * bbox.h); - // glScissor(x, y, w, h); - // LOGI("x, y, w, h: %d, %d, %d, %d\n", x, y, w, h); - //} - - std::vector itemsToRender; - GridItem nw = getItemForPos(0, 0, false); - GridItem se = getItemForPos((float)bbox.w - 1, (float)bbox.h - 1, false); - - //LOGI("getItem: %d, %d - %d, %d\n", nw.x, nw.y, se.x, se.y); - - const float bxx = bbox.x - (fpx /*+ dx*alpha*/) + (nw.xf - nw.x); - const float byy = bbox.y - (fpy /*+ dy*alpha*/) + (nw.yf - nw.y); - for (int y = nw.y; y <= se.y; ++y) - for (int x = nw.x; x <= se.x; ++x) { - int id = y * columns + x; - if (y <0 || id < 0 || id >= numItems) continue; // @todo: break rather - if (isNotSet(SF_WrapX) && (x < 0 || x >= columns)) continue; // @todo: break rather - GridItem item; //@todo: v- Does not support SF_Wrapping - item.id = id; - item.xf = bxx + x * itemBbox.w; - item.yf = byy + y * itemBbox.h; - item.x = (int)item.xf; - item.y = (int)item.yf; - //LOGI("i: %d (%.1f, %.1f)\t", id, item.xf, item.yf); - if (isSet(SF_MultiSelect)) item.selected = selected[id]; - else item.selected = (id == selectedId); - - itemsToRender.push_back(item); - } - renderBatch(itemsToRender, alpha); - - //if (isSet(SF_Scissor)) - // glDisable2(GL_SCISSOR_TEST); -} - -void ScrollingPane::renderBatch(std::vector& items, float alpha) { - for (unsigned int i = 0; i < items.size(); ++i) - renderItem(items[i], alpha); -} - -void ScrollingPane::renderItem(GridItem& item, float alpha) { -} - -ScrollingPane::GridItem ScrollingPane::getItemForPos( float x, float y, bool isScreenPos ) { - // Screen relative pos (rather than ScrollingPane relative pos) - if (isScreenPos) { - x -= bbox.x; - y -= bbox.y; - } - - // Add the scrolled offset - x += fpx; - y += fpy; - - // Does the grid SF_Wrap around? - if (isSet(SF_WrapX)) x = fmod(x, (float)(itemBbox.w * columns)); - if (isSet(SF_WrapY)) y = fmod(y, (float)(itemBbox.h * rows)); - - GridItem out; - out.xf = x / itemBbox.w; - out.yf = y / itemBbox.h; - out.x = (int) out.xf; - out.y = (int) out.yf; - out.id = out.y * columns + out.x; - return out; -} - -void ScrollingPane::addDeltaPos(float x, float y, float dt, int a) { - if (dt <= 0) - return; - - Vec3 delta = (dragLastPos - Vec3(x, y, 0)) * (1.0f / dt); - dragDeltas.push_back(delta.x); - dragDeltas.push_back(delta.y); - dragLastPos.set(x, y, 0); - dragLastDeltaTimeStamp += dt; // @attn @fix: This relies on user to be correct - //LOGI(">> delta %d: %s\n", a, STR(delta)); -} - -static const int PKTableViewMinTouchDurationForCellSelection = 150; - -void ScrollingPane::handleUserInput() { - - bool isDown = Mouse::isButtonDown(MouseAction::ACTION_LEFT); - float x = Mouse::getX() * invScreenScale; - float y = Mouse::getY() * invScreenScale; - int t = getTimeMs(); - bool isInside = area.isInside(x, y); - //LOGI("inside? %d\n", isInside); - - bool moved = (x != _lx || y != _ly); - - _lx = x; - _ly = y; - - if (te_ended > 0 && _wasDown && !isDown) - touchesEnded(x, y, t); - else if (isDown && !_wasDown && isInside) - touchesBegan(x, y, t); - else if (te_moved > 0 && moved && isDown) - touchesMoved(x, y, t); - - if (highlightTimer >= 0 && isNotSet(SF_NoHoldSelect)) { - if (getTimeMs() - highlightTimer >= PKTableViewMinTouchDurationForCellSelection) - onHoldItem(); - } - - _wasDown = isDown; -} - -Vec3& ScrollingPane::contentOffset() { - return _contentOffset; -} - -void ScrollingPane::beginTracking(float x, float y, int t) { //@param 1: MouseEvent a - if (this->tracking) { - return; - } - //a.preventDefault(); - this->stopDecelerationAnimation(); - //this->hostingLayer.style.webkitTransitionDuration = 0; - this->adjustContentSize(); //@todo @? - this->minPoint.set((float)(this->size.w - this->adjustedContentSize.w), (float)(this->size.h - this->adjustedContentSize.h), 0); //@todo - this->snapContentOffsetToBounds(false); - this->startPosition = this->_contentOffset; - this->startTouchPosition.set(x, y, 0); - this->startTime = (float)t; - this->startTimePosition = this->contentOffset(); - this->tracking = true; - this->dragging = false; - this->touchesHaveMoved = false; - //window.addEventListener(PKMoveEvent, this, true); //@todo - //window.addEventListener(PKEndEvent, this, true); - //window.addEventListener("touchcancel", this, true); - //window.addEventListener(PKEndEvent, this, false) -}; - -void ScrollingPane::touchesBegan(float x, float y, int t) { //@param 1: MouseEvent a - if (!this->_scrollEnabled) { - return; - } - - te_ended = 1; - //if (a.eventPhase == Event.CAPTURING_PHASE) { - // if (a._manufactured) { - // return - // } - //this->highlightItem = getItemForPos(x, y, true); - // if (this.delaysContentTouches) { - // a.stopPropagation(); - // this.callMethodNameAfterDelay("beginTouchesInContent", kContentTouchesDelay, a); - this->beginTracking(x, y, t); - // } - //} else { - // this.beginTracking(a) - //} - te_moved = 2; - - GridItem gi = getItemForPos(x, y, true); - if (gi.id >= 0 && gi.id < numItems) { - //LOGI("Pressed down at %d (%d, %d)\n", gi.id, gi.x, gi.y); - highlightItem.id = bboxArea.isInside(x, y)? gi.id : -1; - highlightStarted = highlightTimer = bboxArea.isInside(x, y)? getTimeMs() : -1; - } else { - highlightItem.id = -1; - highlightStarted = highlightTimer = -1; - } -} - -void ScrollingPane::touchesMoved(float x, float y, int t) -{ - this->touchesHaveMoved = true; - //this->callSuper(d); - Vec3 e(x, y, 0); - float b = e.x - this->startTouchPosition.x; - float c = e.y - this->startTouchPosition.y; - if (!this->dragging) { - if ((Mth::abs(b) >= kMinimumTrackingForDrag && isNotSet(SF_LockX)) || (Mth::abs(c) >= kMinimumTrackingForDrag && isNotSet(SF_LockY))) { - willBeginDragging(); - this->dragging = true; - this->firstDrag = true; - - if (isSet(SF_ShowScrollbar)) { - if (isNotSet(SF_LockX) && (this->adjustedContentSize.w > this->size.w)) - //this->hScroll.visible = true; - this->hScroll.fading = 1; - if (isNotSet(SF_LockY) && (this->adjustedContentSize.h > this->size.h)) - //this->vScroll.visible = true; - this->vScroll.fading = 1; - } - } - } - if (this->dragging) { - //d.stopPropagation(); - float f = isNotSet(SF_LockX) ? (this->startPosition.x + b) : this->_contentOffset.x; - float a = isNotSet(SF_LockY) ? (this->startPosition.y + c) : this->_contentOffset.y; - if (isNotSet(SF_HardLimits)) { - f -= ((f < this->minPoint.x) ? (f - this->minPoint.x) : ((f > 0) ? f : 0)) / 2; - a -= ((a < this->minPoint.y) ? (a - this->minPoint.y) : ((a > 0) ? a : 0)) / 2; - } else { - f = Mth::Min(Mth::Max(this->minPoint.x, f), 0.0f); - a = Mth::Min(Mth::Max(this->minPoint.y, a), 0.0f); - } - if (this->firstDrag) { - this->firstDrag = false; - this->startTouchPosition = e; - return; - } - this->setContentOffset(f, a); - this->lastEventTime = t;//d.timeStamp; - } -} - -void ScrollingPane::touchesEnded(float x, float y, int t) { - te_ended = 0; - highlightStarted = -1; - //te_moved = 0; - - //this.callSuper(a); - this->tracking = false; - if (this->dragging) { - this->dragging = false; - //a.stopPropagation(); - if (t - this->lastEventTime <= kMaxTrackingTime) { - this->_contentOffsetBeforeDeceleration = this->_contentOffset; - this->startDecelerationAnimation(false); - } - if (!this->decelerating) {} - //window.removeEventListener(PKEndEvent, this, false); - didEndDragging(); - } - if (!this->decelerating) { - if (fpy < 0 || fpy > bbox.h) { //@todo: for x as well (or rather, x^y) - this->_contentOffsetBeforeDeceleration = this->_contentOffset; - this->startDecelerationAnimation(true); - } else { - this->snapContentOffsetToBounds(true); //@fix - this->hideScrollIndicators(); - } - } - //if (a.eventPhase == Event.BUBBLING_PHASE) { //@? @todo - // window.removeEventListener(PKEndEvent, this, false); - - //// old and shaky, doesn't work good with Xperia Play (and presumably lots of others) - //if (!this->touchesHaveMoved && this->highlightItem.id >= 0) { - // _onSelect(this->highlightItem.id); - //} - if (Vec3(x, y, 0).distanceToSqr(startTouchPosition) <= 6.0f * 6.0f && this->highlightItem.id >= 0) { - _onSelect(this->highlightItem.id); - } - //} - te_moved = 0; -}; - - -void ScrollingPane::touchesCancelled(float x, float y, int a) { - touchesEnded(x, y, a); -} - -void ScrollingPane::startDecelerationAnimation( bool force ) -{ - Vec3 a(this->_contentOffset.x - this->startTimePosition.x, this->_contentOffset.y - this->startTimePosition.y, 0); - float b = (getTimeMs()/*event.timeStamp*/ - this->startTime) / kAcceleration; - //LOGI("starting deceleration! %s, %f\n", STR(a), b); - - this->decelerationVelocity = Vec3(a.x / b, a.y / b, 0); - this->minDecelerationPoint = this->minPoint; - this->maxDecelerationPoint = Vec3(0, 0, 0); - if (this->pagingEnabled) { - this->minDecelerationPoint.x = Mth::Max(this->minPoint.x, std::floor(this->_contentOffsetBeforeDeceleration.x / this->size.w) * this->size.w); - this->minDecelerationPoint.y = Mth::Max(this->minPoint.y, std::floor(this->_contentOffsetBeforeDeceleration.y / this->size.h) * this->size.h); - this->maxDecelerationPoint.x = Mth::Min(0.0f, std::ceil(this->_contentOffsetBeforeDeceleration.x / this->size.w) * this->size.w); - this->maxDecelerationPoint.y = Mth::Min(0.0f, std::ceil(this->_contentOffsetBeforeDeceleration.y / this->size.h) * this->size.h); - } - this->penetrationDeceleration = kPenetrationDeceleration; - this->penetrationAcceleration = kPenetrationAcceleration; - if (this->pagingEnabled) { - this->penetrationDeceleration *= 5; - } - float c = this->pagingEnabled ? kMinVelocityForDecelerationWithPaging : kMinVelocityForDeceleration; - if (force || (Mth::abs(this->decelerationVelocity.x) > c || Mth::abs(this->decelerationVelocity.y) > c)) { - this->decelerating = true; - //LOGI("accelerating True - A\n"); - _doStepTimer = true; - //this->decelerationTimer = this->callMethodNameAfterDelay("stepThroughDecelerationAnimation", kDesiredAnimationFrameRate); //@? - this->lastFrame = getTimeMs(); - willBeginDecelerating(); - } -} - -void ScrollingPane::hideScrollIndicators() { - //hScroll.visible = vScroll.visible = false; - hScroll.fading = vScroll.fading = 0; -} - - -void ScrollingPane::stopDecelerationAnimation() -{ - //LOGI("decelerating False - A\n"); - this->decelerating = false; - _doStepTimer = false; - //clearTimeout(this.decelerationTimer) //@? -} - -void ScrollingPane::stepThroughDecelerationAnimation(bool noAnimation) { - if (!this->decelerating) { - return; - } - int d = getTimeMs(); - int k = d - this->lastFrame; - - int l = noAnimation ? 0 : (int)(std::floor(0.5f + ((float)k / kDesiredAnimationFrameRate) - 1)); - //LOGI("k: %d, %d %d : %d\n", d, this->lastFrame, k, l); - for (int j = 0; j < l; j++) - this->stepThroughDecelerationAnimation(true); - - float g = this->contentOffset().x + this->decelerationVelocity.x; - float h = this->contentOffset().y + this->decelerationVelocity.y; - if (isSet(SF_HardLimits)) { - float a = Mth::Min(Mth::Max(this->minPoint.x, g), 0.0f); - if (a != g) { - g = a; - this->decelerationVelocity.x = 0; - } - float c = Mth::Min(Mth::Max(this->minPoint.y, h), 0.0f); - if (c != h) { - h = c; - this->decelerationVelocity.y = 0; - } - } - if (noAnimation) { - this->contentOffset().x = g; - this->contentOffset().y = h; - } else { - this->setContentOffset(g, h); - } - if (!this->pagingEnabled) { - this->decelerationVelocity.x *= kDecelerationFrictionFactor; - this->decelerationVelocity.y *= kDecelerationFrictionFactor; - } - float b = Mth::abs(this->decelerationVelocity.x); - float i = Mth::abs(this->decelerationVelocity.y); - if (!noAnimation && b <= kMinimumVelocityToHideScrollIndicators && i <= kMinimumVelocityToHideScrollIndicators) { - this->hideScrollIndicators(); - if (b <= kMinimumVelocity && i <= kMinimumVelocity) { - //LOGI("decelerating False - B\n"); - this->decelerating = false; - didEndDecelerating(); - return; - } - } - if (!noAnimation) { - //this->decelerationTimer = this->callMethodNameAfterDelay("stepThroughDecelerationAnimation", kDesiredAnimationFrameRate) - } - //if (noAnimation) doStepTimer = false; - - - if (isNotSet(SF_HardLimits)) { - Vec3 e; - if (g < this->minDecelerationPoint.x) { - e.x = this->minDecelerationPoint.x - g; - } else { - if (g > this->maxDecelerationPoint.x) { - e.x = this->maxDecelerationPoint.x - g; - } - } - if (h < this->minDecelerationPoint.y) { - e.y = this->minDecelerationPoint.y - h; - } else { - if (h > this->maxDecelerationPoint.y) { - e.y = this->maxDecelerationPoint.y - h; - } - } - if (e.x != 0) { - if (e.x * this->decelerationVelocity.x <= 0) { - this->decelerationVelocity.x += e.x * this->penetrationDeceleration; - } else { - this->decelerationVelocity.x = e.x * this->penetrationAcceleration; - } - } - if (e.y != 0) { - if (e.y * this->decelerationVelocity.y <= 0) { - this->decelerationVelocity.y += e.y * this->penetrationDeceleration; - } else { - this->decelerationVelocity.y = e.y * this->penetrationAcceleration; - } - } - } - if (!noAnimation) { - this->lastFrame = d; - } -} - -void ScrollingPane::scrollBy(float dx, float dy) { - // compute target content offset from wheel delta (in screen-space w/ opposite sign) - float targetX = _contentOffset.x - dx; - float targetY = _contentOffset.y - dy; - - if (isSet(SF_LockX)) targetX = _contentOffset.x; - if (isSet(SF_LockY)) targetY = _contentOffset.y; - - if (isSet(SF_HardLimits)) { - targetX = Mth::clamp(targetX, minPoint.x, 0.0f); - targetY = Mth::clamp(targetY, minPoint.y, 0.0f); - } else { - if (targetX > 0.0f) { - float overshoot = targetX; - overshoot = Mth::Min(overshoot, kWheelOverscrollMax); - targetX = overshoot * kWheelOverscrollDamping; - } else if (targetX < minPoint.x) { - float overshoot = targetX - minPoint.x; - overshoot = Mth::Max(overshoot, -kWheelOverscrollMax); - targetX = minPoint.x + overshoot * kWheelOverscrollDamping; - } - - if (targetY > 0.0f) { - float overshoot = targetY; - overshoot = Mth::Min(overshoot, kWheelOverscrollMax); - targetY = overshoot * kWheelOverscrollDamping; - } else if (targetY < minPoint.y) { - float overshoot = targetY - minPoint.y; - overshoot = Mth::Max(overshoot, -kWheelOverscrollMax); - targetY = minPoint.y + overshoot * kWheelOverscrollDamping; - } - } - - setContentOffset(Vec3(targetX, targetY, 0)); -} - -void ScrollingPane::setContentOffset(float x, float y) { - this->setContentOffsetWithAnimation(Vec3(x, y, 0), false); -} -void ScrollingPane::setContentOffset(Vec3 a) { - this->setContentOffsetWithAnimation(a, false); -}; - -void ScrollingPane::setContentOffsetWithAnimation(Vec3 b, bool doScroll) { - this->_contentOffset = b; - fpx = -this->_contentOffset.x; - fpy = -this->_contentOffset.y; - /* //@todo //@? - this->hostingLayer.style.webkitTransform = PKUtils.t(this->_contentOffset.x, this->_contentOffset.y); - if (a) { - this->scrollTransitionsNeedRemoval = true; - this->hostingLayer.style.webkitTransitionDuration = kPagingTransitionDuration - } else { - this->didScroll(false) - } - */ - if (!doScroll) { - // @todo: for scroll indicator //@? - if (isSet(SF_ShowScrollbar)) { - if (isNotSet(SF_LockX)) this->updateHorizontalScrollIndicator(); - if (isNotSet(SF_LockY)) this->updateVerticalScrollIndicator(); - } - } - //this->notifyPropertyChange("contentOffset") -} - -void ScrollingPane::snapContentOffsetToBounds(bool a) { - bool b = false; - Vec3 c; - if (this->pagingEnabled) { - c.x = std::floor(0.5f + this->_contentOffset.x / this->size.w) * this->size.w; - c.y = std::floor(0.5f + this->_contentOffset.y / this->size.h) * this->size.h; - b = true; - } else { - if (isNotSet(SF_HardLimits)) { - c.x = Mth::Min(Mth::Max(this->minPoint.x, this->_contentOffset.x), 0.0f); - c.y = Mth::Min(Mth::Max(this->minPoint.y, this->_contentOffset.y), 0.0f); - b = (c.x != this->_contentOffset.x || c.y != this->_contentOffset.y); - } - } - if (b) { - this->setContentOffsetWithAnimation(c, a); - } -} - -void ScrollingPane::adjustContentSize() -{ - this->adjustedContentSize.w = Mth::Max(itemBbox.w * columns, bbox.w); - this->adjustedContentSize.h = Mth::Max(itemBbox.h * rows, bbox.h); -} - -void ScrollingPane::onHoldItem() { - //LOGI("dragging, tracking %d, %d\n", !this->dragging, this->tracking); - int id = highlightItem.id; - if (id != -1 && !this->dragging && this->tracking) - _onSelect(id); - highlightTimer = -1; - //highlightItem.id = -1; -} - -bool ScrollingPane::onSelect( int gridId, bool selected ) -{ - return selected; -} - -void ScrollingPane::updateHorizontalScrollIndicator() -{ - float c = (isNotSet(SF_LockX) && isSet(SF_ShowScrollbar)) ? PKScrollIndicatorEndSize * 2 : 1; - float d = Mth::Max(kMinIndicatorLength, std::floor(0.5f + (this->size.w / this->adjustedContentSize.w) * (this->size.w - c))); - float a = (-this->_contentOffset.x / (this->adjustedContentSize.w - this->size.w)) * (this->size.w - c - d); - //float b = this->size.h - PKScrollIndicatorThickness - 1; - if (this->_contentOffset.x > 0) { - d = std::floor(0.5f + Mth::Max(d - this->_contentOffset.x, PKScrollIndicatorThickness)); - a = 1; - } else { - if (this->_contentOffset.x < -(this->adjustedContentSize.w - this->size.w)) { - d = std::floor(0.5f + Mth::Max(d + this->adjustedContentSize.w - this->size.w + this->contentOffset().x, PKScrollIndicatorThickness)); - a = this->size.w - d - c; - } - } - this->hScroll.x = a + bbox.x; - //this->hScroll.y = b; - this->hScroll.w = d; //@property -}; - - -void ScrollingPane::updateVerticalScrollIndicator() -{ - float c = (isNotSet(SF_LockY) && isSet(SF_ShowScrollbar)) ? PKScrollIndicatorEndSize * 2 : 1; - float d = Mth::Max(kMinIndicatorLength, std::floor(0.5f + (this->size.h / this->adjustedContentSize.h) * (this->size.h - c))); - //float a = this->size.w - PKScrollIndicatorThickness - 1; - float b = (-this->_contentOffset.y / (this->adjustedContentSize.h - this->size.h)) * (this->size.h - c - d); - if (this->_contentOffset.y > 0) { - d = std::floor(0.5f + Mth::Max(d - this->_contentOffset.y, PKScrollIndicatorThickness)); - b = 1; - } else { - if (this->_contentOffset.y < -(this->adjustedContentSize.h - this->size.h)) { - d = std::floor(0.5f + Mth::Max(d + this->adjustedContentSize.h - this->size.h + this->contentOffset().y, PKScrollIndicatorThickness)); - b = this->size.h - d - c; - } - } - //this->vScroll.x = a; - this->vScroll.y = b + bbox.y; - this->vScroll.h = d; //@property -}; - -void ScrollingPane::_onSelect( int id ) -{ - if (isSet(SF_MultiSelect)) { - selected[id] = onSelect(id, !selected[id]); - } else { - // Change the selection, if the user wants it - // @note: There's currently no way to clear a selection - bool doSelect = onSelect(id, true); - if (id != selectedId && doSelect) { - onSelect(selectedId, false); - selectedId = id; - } - } -} - -void ScrollingPane::updateScrollFade( ScrollBar& sb ) { - if (sb.fading == 1 && ((sb.alpha += 0.33f) >= 1)) { - sb.alpha = 1; - sb.fading = -1; - } - if (sb.fading == 0 && ((sb.alpha -= 0.10f) <= 0)) { - sb.alpha = 0; - sb.fading = -1; - } -} - -void ScrollingPane::setSelected( int id, bool selected ) -{ - if (isSet(SF_MultiSelect)) - this->selected[id] = selected; - else { - if (selected) selectedId = selected? id : -1; - else if (id == selectedId) - selectedId = -1; - } -} - -void ScrollingPane::translate( float xo, float yo ) -{ - bbox.x += (int)xo; - bbox.y += (int)yo; - area._x0 += xo; - area._x1 += xo; - area._y0 += yo; - area._y1 += yo; - bboxArea._x0 += xo; - bboxArea._x1 += xo; - bboxArea._y0 += yo; - bboxArea._y1 += yo; - hScroll.x += xo; - hScroll.y += yo; - vScroll.x += xo; - vScroll.y += yo; -} - -bool ScrollingPane::queryHoldTime(int* gridItem, int* heldMs) { - *gridItem = -1; - *heldMs = -1; - - if (!dragging && highlightStarted >= 0) { - GridItem item = getItemForPos(_lx, _ly, true); - if (item.id == highlightItem.id) { - *gridItem = highlightItem.id; - *heldMs = getTimeMs() - highlightStarted; - return true; - } else { - highlightStarted = -1; - } - } - - return false; -} +#include "ScrollingPane.h" +#include "../../renderer/gles.h" +#include "../Gui.h" +#include "../../../util/Mth.h" +#include "../../../SharedConstants.h" + +#define STR(x) (x.toString().c_str()) + +static const float kPenetrationDeceleration = 0.03f; +static const float kPenetrationAcceleration = 0.08f; +static const float kMaxTrackingTime = 100.0f; +static const float kAcceleration = 15; +static const float kMinVelocityForDecelerationWithPaging = 4 / 3.0f; +static const float kMinVelocityForDeceleration = 1 / 3.0f; +static const float kDesiredAnimationFrameRate = 1000.0f / 60; +static const float kDecelerationFrictionFactor = 0.95f; +static const float kMinimumVelocityToHideScrollIndicators = 0.05f; +static const float kMinimumVelocity = 0.01f; +static const float kMinimumTrackingForDrag = 5; +static const float kMinIndicatorLength = 34.0f / 3; +static const float PKScrollIndicatorEndSize = 3; +static const float PKScrollIndicatorThickness = 7.0f /3; +static const float kWheelOverscrollMax = 80.0f; +static const float kWheelOverscrollDamping = 0.6f; +static const float kWheelOverscrollRestoreAlpha = 0.18f; + +ScrollingPane::ScrollingPane( + int optionFlags, + const IntRectangle& boundingBox, + const IntRectangle& itemRect, + int columns, + int numItems, + float screenScale /* = 1.0f */, + const IntRectangle& itemBoundingRect /*= IntRectangle(0,0,0,0)*/ ) +: flags(optionFlags), + bbox(boundingBox), + size(boundingBox), + area((float)bbox.x, (float)bbox.y, (float)(bbox.x + bbox.w), (float)(bbox.y + bbox.h)), + bboxArea(area), + itemRect(itemRect), + numItems(numItems), + screenScale(screenScale), + invScreenScale(1.0f / screenScale), + //hasItemBounding(), + px(0), py(0), fpx(0), fpy(0), + dx(0), dy(0), + dragState(-1), + dragLastDeltaTimeStamp(-1.0f), + friction(0.95f), + highlightTimer(-1), + highlightStarted(-1), + selectedId(-1), + decelerating(false), + tracking(false), + dragging(false), + pagingEnabled(false), + _scrollEnabled(true), + _timer(60), + _doStepTimer(false), + _wasDown(false),//Mouse::isButtonDown(MouseAction::ACTION_LEFT)), + _lx(-9999), _ly(-9999) +{ + if (itemBoundingRect.w > 0) + itemBbox = itemBoundingRect; + else + itemBbox = itemRect; + + this->columns = (columns>0)? columns : bbox.w / itemBbox.w; + + if (this->columns <= 0) { + LOGW("Columns are 0! Area width is smaller than item width. Setting columns to 1.\n"); + this->columns = 1; + } + //LOGI("%d, %d :: %d\n", bbox.w, itemBbox.w, this->columns); + + rows = 1 + (numItems-1) / this->columns; + + /* + if (columns * itemBbox.w <= bbox.w) flags |= SF_LockX; + if (rows * itemBbox.h <= bbox.h) flags |= SF_LockY; + */ + + // initialize content bounds immediately + adjustContentSize(); + minPoint.set((float)(this->size.w - this->adjustedContentSize.w), (float)(this->size.h - this->adjustedContentSize.h), 0); + this->snapContentOffsetToBounds(false); + + + dragDeltas.reserve(128); + + te_moved = 0; + te_ended = 1; + + selected = new bool[numItems]; + for (int i = 0; i < numItems; ++i) + selected[i] = false; + + // Setup the scroll bars + vScroll.w = vScroll.h = PKScrollIndicatorThickness; + hScroll.w = hScroll.h = PKScrollIndicatorThickness; + vScroll.x = bbox.x + bbox.w - vScroll.w; + vScroll.y = 0; + hScroll.x = 0; + hScroll.y = bbox.y + bbox.h - hScroll.h; + +// vScroll.alpha +// vScroll.fading = hScroll.fading = -1; +} + +ScrollingPane::~ScrollingPane() { + delete[] selected; +} + +//void ScrollingPane::init(Minecraft* mc, int width, int height) { +// this->mc = mc; +// this->width = width; +// this->height = height; +//} + +void ScrollingPane::tick() { + + if (isSet(SF_ShowScrollbar)) { + updateScrollFade(vScroll); + updateScrollFade(hScroll); + } + + if (isNotSet(SF_HardLimits) && !Mouse::isButtonDown(MouseAction::ACTION_LEFT) && !dragging && !tracking && !decelerating) { + float targetX = _contentOffset.x; + float targetY = _contentOffset.y; + bool corrected = false; + + if (targetX > 0.0f) { + targetX = Mth::lerp(targetX, 0.0f, kWheelOverscrollRestoreAlpha); + corrected = true; + } else if (targetX < minPoint.x) { + targetX = Mth::lerp(targetX, minPoint.x, kWheelOverscrollRestoreAlpha); + corrected = true; + } + + if (targetY > 0.0f) { + targetY = Mth::lerp(targetY, 0.0f, kWheelOverscrollRestoreAlpha); + corrected = true; + } else if (targetY < minPoint.y) { + targetY = Mth::lerp(targetY, minPoint.y, kWheelOverscrollRestoreAlpha); + corrected = true; + } + + if (corrected) { + if (Mth::abs(targetX - _contentOffset.x) < 0.25f) targetX = (targetX > 0.0f ? 0.0f : (targetX < minPoint.x ? minPoint.x : targetX)); + if (Mth::abs(targetY - _contentOffset.y) < 0.25f) targetY = (targetY > 0.0f ? 0.0f : (targetY < minPoint.y ? minPoint.y : targetY)); + setContentOffset(Vec3(targetX, targetY, 0)); + } + } +} + +bool ScrollingPane::getGridItemFor_slow(int itemIndex, GridItem& out) { + GridItem nw = getItemForPos(0, 0, false); + GridItem se = getItemForPos((float)bbox.w - 1, (float)bbox.h - 1, false); + const float bxx = bbox.x - (fpx /*+ dx*alpha*/) + (nw.xf - nw.x); + const float byy = bbox.y - (fpy /*+ dy*alpha*/) + (nw.yf - nw.y); + + int y = itemIndex / columns; + int x = itemIndex - (y * columns); + + out.id = itemIndex; + out.xf = bxx + x * itemBbox.w; + out.yf = byy + y * itemBbox.h; + out.x = x; + out.y = y; + + return x >= nw.x && x <= se.x + && y >= nw.y && y <= se.y; +} + + +void ScrollingPane::render( int xm, int ym, float alpha ) { + // Handle user interaction first + handleUserInput(); + + _timer.advanceTime(); + + if (_doStepTimer && !te_moved) { + for (int i = 0; i < _timer.ticks; ++i) + stepThroughDecelerationAnimation(false); + this->lastFrame = getTimeMs(); + } + + // Render + //if (isSet(SF_Scissor)) { + // glEnable2(GL_SCISSOR_TEST); + // GLuint x = (GLuint)(screenScale * bbox.x); + // GLuint y = 480 - (GLuint)(screenScale * (bbox.y + bbox.h)); + // GLuint w = (GLuint)(screenScale * bbox.w); + // GLuint h = (GLuint)(screenScale * bbox.h); + // glScissor(x, y, w, h); + // LOGI("x, y, w, h: %d, %d, %d, %d\n", x, y, w, h); + //} + + std::vector itemsToRender; + GridItem nw = getItemForPos(0, 0, false); + GridItem se = getItemForPos((float)bbox.w - 1, (float)bbox.h - 1, false); + + //LOGI("getItem: %d, %d - %d, %d\n", nw.x, nw.y, se.x, se.y); + + const float bxx = bbox.x - (fpx /*+ dx*alpha*/) + (nw.xf - nw.x); + const float byy = bbox.y - (fpy /*+ dy*alpha*/) + (nw.yf - nw.y); + for (int y = nw.y; y <= se.y; ++y) + for (int x = nw.x; x <= se.x; ++x) { + int id = y * columns + x; + if (y <0 || id < 0 || id >= numItems) continue; // @todo: break rather + if (isNotSet(SF_WrapX) && (x < 0 || x >= columns)) continue; // @todo: break rather + GridItem item; //@todo: v- Does not support SF_Wrapping + item.id = id; + item.xf = bxx + x * itemBbox.w; + item.yf = byy + y * itemBbox.h; + item.x = (int)item.xf; + item.y = (int)item.yf; + //LOGI("i: %d (%.1f, %.1f)\t", id, item.xf, item.yf); + if (isSet(SF_MultiSelect)) item.selected = selected[id]; + else item.selected = (id == selectedId); + + itemsToRender.push_back(item); + } + renderBatch(itemsToRender, alpha); + + //if (isSet(SF_Scissor)) + // glDisable2(GL_SCISSOR_TEST); +} + +void ScrollingPane::renderBatch(std::vector& items, float alpha) { + for (unsigned int i = 0; i < items.size(); ++i) + renderItem(items[i], alpha); +} + +void ScrollingPane::renderItem(GridItem& item, float alpha) { +} + +ScrollingPane::GridItem ScrollingPane::getItemForPos( float x, float y, bool isScreenPos ) { + // Screen relative pos (rather than ScrollingPane relative pos) + if (isScreenPos) { + x -= bbox.x; + y -= bbox.y; + } + + // Add the scrolled offset + x += fpx; + y += fpy; + + // Does the grid SF_Wrap around? + if (isSet(SF_WrapX)) x = fmod(x, (float)(itemBbox.w * columns)); + if (isSet(SF_WrapY)) y = fmod(y, (float)(itemBbox.h * rows)); + + GridItem out; + out.xf = x / itemBbox.w; + out.yf = y / itemBbox.h; + out.x = (int) out.xf; + out.y = (int) out.yf; + out.id = out.y * columns + out.x; + return out; +} + +void ScrollingPane::addDeltaPos(float x, float y, float dt, int a) { + if (dt <= 0) + return; + + Vec3 delta = (dragLastPos - Vec3(x, y, 0)) * (1.0f / dt); + dragDeltas.push_back(delta.x); + dragDeltas.push_back(delta.y); + dragLastPos.set(x, y, 0); + dragLastDeltaTimeStamp += dt; // @attn @fix: This relies on user to be correct + //LOGI(">> delta %d: %s\n", a, STR(delta)); +} + +static const int PKTableViewMinTouchDurationForCellSelection = 150; + +void ScrollingPane::handleUserInput() { + + bool isDown = Mouse::isButtonDown(MouseAction::ACTION_LEFT); + float x = Mouse::getX() * invScreenScale; + float y = Mouse::getY() * invScreenScale; + int t = getTimeMs(); + bool isInside = area.isInside(x, y); + //LOGI("inside? %d\n", isInside); + + bool moved = (x != _lx || y != _ly); + + _lx = x; + _ly = y; + + if (te_ended > 0 && _wasDown && !isDown) + touchesEnded(x, y, t); + else if (isDown && !_wasDown && isInside) + touchesBegan(x, y, t); + else if (te_moved > 0 && moved && isDown) + touchesMoved(x, y, t); + + if (highlightTimer >= 0 && isNotSet(SF_NoHoldSelect)) { + if (getTimeMs() - highlightTimer >= PKTableViewMinTouchDurationForCellSelection) + onHoldItem(); + } + + _wasDown = isDown; +} + +Vec3& ScrollingPane::contentOffset() { + return _contentOffset; +} + +void ScrollingPane::beginTracking(float x, float y, int t) { //@param 1: MouseEvent a + if (this->tracking) { + return; + } + //a.preventDefault(); + this->stopDecelerationAnimation(); + //this->hostingLayer.style.webkitTransitionDuration = 0; + this->adjustContentSize(); //@todo @? + this->minPoint.set((float)(this->size.w - this->adjustedContentSize.w), (float)(this->size.h - this->adjustedContentSize.h), 0); //@todo + this->snapContentOffsetToBounds(false); + this->startPosition = this->_contentOffset; + this->startTouchPosition.set(x, y, 0); + this->startTime = (float)t; + this->startTimePosition = this->contentOffset(); + this->tracking = true; + this->dragging = false; + this->touchesHaveMoved = false; + //window.addEventListener(PKMoveEvent, this, true); //@todo + //window.addEventListener(PKEndEvent, this, true); + //window.addEventListener("touchcancel", this, true); + //window.addEventListener(PKEndEvent, this, false) +}; + +void ScrollingPane::touchesBegan(float x, float y, int t) { //@param 1: MouseEvent a + if (!this->_scrollEnabled) { + return; + } + + te_ended = 1; + //if (a.eventPhase == Event.CAPTURING_PHASE) { + // if (a._manufactured) { + // return + // } + //this->highlightItem = getItemForPos(x, y, true); + // if (this.delaysContentTouches) { + // a.stopPropagation(); + // this.callMethodNameAfterDelay("beginTouchesInContent", kContentTouchesDelay, a); + this->beginTracking(x, y, t); + // } + //} else { + // this.beginTracking(a) + //} + te_moved = 2; + + GridItem gi = getItemForPos(x, y, true); + if (gi.id >= 0 && gi.id < numItems) { + //LOGI("Pressed down at %d (%d, %d)\n", gi.id, gi.x, gi.y); + highlightItem.id = bboxArea.isInside(x, y)? gi.id : -1; + highlightStarted = highlightTimer = bboxArea.isInside(x, y)? getTimeMs() : -1; + } else { + highlightItem.id = -1; + highlightStarted = highlightTimer = -1; + } +} + +void ScrollingPane::touchesMoved(float x, float y, int t) +{ + this->touchesHaveMoved = true; + //this->callSuper(d); + Vec3 e(x, y, 0); + float b = e.x - this->startTouchPosition.x; + float c = e.y - this->startTouchPosition.y; + if (!this->dragging) { + if ((Mth::abs(b) >= kMinimumTrackingForDrag && isNotSet(SF_LockX)) || (Mth::abs(c) >= kMinimumTrackingForDrag && isNotSet(SF_LockY))) { + willBeginDragging(); + this->dragging = true; + this->firstDrag = true; + + if (isSet(SF_ShowScrollbar)) { + if (isNotSet(SF_LockX) && (this->adjustedContentSize.w > this->size.w)) + //this->hScroll.visible = true; + this->hScroll.fading = 1; + if (isNotSet(SF_LockY) && (this->adjustedContentSize.h > this->size.h)) + //this->vScroll.visible = true; + this->vScroll.fading = 1; + } + } + } + if (this->dragging) { + //d.stopPropagation(); + float f = isNotSet(SF_LockX) ? (this->startPosition.x + b) : this->_contentOffset.x; + float a = isNotSet(SF_LockY) ? (this->startPosition.y + c) : this->_contentOffset.y; + if (isNotSet(SF_HardLimits)) { + f -= ((f < this->minPoint.x) ? (f - this->minPoint.x) : ((f > 0) ? f : 0)) / 2; + a -= ((a < this->minPoint.y) ? (a - this->minPoint.y) : ((a > 0) ? a : 0)) / 2; + } else { + f = Mth::Min(Mth::Max(this->minPoint.x, f), 0.0f); + a = Mth::Min(Mth::Max(this->minPoint.y, a), 0.0f); + } + if (this->firstDrag) { + this->firstDrag = false; + this->startTouchPosition = e; + return; + } + this->setContentOffset(f, a); + this->lastEventTime = t;//d.timeStamp; + } +} + +void ScrollingPane::touchesEnded(float x, float y, int t) { + te_ended = 0; + highlightStarted = -1; + //te_moved = 0; + + //this.callSuper(a); + this->tracking = false; + if (this->dragging) { + this->dragging = false; + //a.stopPropagation(); + if (t - this->lastEventTime <= kMaxTrackingTime) { + this->_contentOffsetBeforeDeceleration = this->_contentOffset; + this->startDecelerationAnimation(false); + } + if (!this->decelerating) {} + //window.removeEventListener(PKEndEvent, this, false); + didEndDragging(); + } + if (!this->decelerating) { + if (fpy < 0 || fpy > bbox.h) { //@todo: for x as well (or rather, x^y) + this->_contentOffsetBeforeDeceleration = this->_contentOffset; + this->startDecelerationAnimation(true); + } else { + this->snapContentOffsetToBounds(true); //@fix + this->hideScrollIndicators(); + } + } + //if (a.eventPhase == Event.BUBBLING_PHASE) { //@? @todo + // window.removeEventListener(PKEndEvent, this, false); + + //// old and shaky, doesn't work good with Xperia Play (and presumably lots of others) + //if (!this->touchesHaveMoved && this->highlightItem.id >= 0) { + // _onSelect(this->highlightItem.id); + //} + if (Vec3(x, y, 0).distanceToSqr(startTouchPosition) <= 6.0f * 6.0f && this->highlightItem.id >= 0) { + _onSelect(this->highlightItem.id); + } + //} + te_moved = 0; +}; + + +void ScrollingPane::touchesCancelled(float x, float y, int a) { + touchesEnded(x, y, a); +} + +void ScrollingPane::startDecelerationAnimation( bool force ) +{ + Vec3 a(this->_contentOffset.x - this->startTimePosition.x, this->_contentOffset.y - this->startTimePosition.y, 0); + float b = (getTimeMs()/*event.timeStamp*/ - this->startTime) / kAcceleration; + //LOGI("starting deceleration! %s, %f\n", STR(a), b); + + this->decelerationVelocity = Vec3(a.x / b, a.y / b, 0); + this->minDecelerationPoint = this->minPoint; + this->maxDecelerationPoint = Vec3(0, 0, 0); + if (this->pagingEnabled) { + this->minDecelerationPoint.x = Mth::Max(this->minPoint.x, std::floor(this->_contentOffsetBeforeDeceleration.x / this->size.w) * this->size.w); + this->minDecelerationPoint.y = Mth::Max(this->minPoint.y, std::floor(this->_contentOffsetBeforeDeceleration.y / this->size.h) * this->size.h); + this->maxDecelerationPoint.x = Mth::Min(0.0f, std::ceil(this->_contentOffsetBeforeDeceleration.x / this->size.w) * this->size.w); + this->maxDecelerationPoint.y = Mth::Min(0.0f, std::ceil(this->_contentOffsetBeforeDeceleration.y / this->size.h) * this->size.h); + } + this->penetrationDeceleration = kPenetrationDeceleration; + this->penetrationAcceleration = kPenetrationAcceleration; + if (this->pagingEnabled) { + this->penetrationDeceleration *= 5; + } + float c = this->pagingEnabled ? kMinVelocityForDecelerationWithPaging : kMinVelocityForDeceleration; + if (force || (Mth::abs(this->decelerationVelocity.x) > c || Mth::abs(this->decelerationVelocity.y) > c)) { + this->decelerating = true; + //LOGI("accelerating True - A\n"); + _doStepTimer = true; + //this->decelerationTimer = this->callMethodNameAfterDelay("stepThroughDecelerationAnimation", kDesiredAnimationFrameRate); //@? + this->lastFrame = getTimeMs(); + willBeginDecelerating(); + } +} + +void ScrollingPane::hideScrollIndicators() { + //hScroll.visible = vScroll.visible = false; + hScroll.fading = vScroll.fading = 0; +} + + +void ScrollingPane::stopDecelerationAnimation() +{ + //LOGI("decelerating False - A\n"); + this->decelerating = false; + _doStepTimer = false; + //clearTimeout(this.decelerationTimer) //@? +} + +void ScrollingPane::stepThroughDecelerationAnimation(bool noAnimation) { + if (!this->decelerating) { + return; + } + int d = getTimeMs(); + int k = d - this->lastFrame; + + int l = noAnimation ? 0 : (int)(std::floor(0.5f + ((float)k / kDesiredAnimationFrameRate) - 1)); + //LOGI("k: %d, %d %d : %d\n", d, this->lastFrame, k, l); + for (int j = 0; j < l; j++) + this->stepThroughDecelerationAnimation(true); + + float g = this->contentOffset().x + this->decelerationVelocity.x; + float h = this->contentOffset().y + this->decelerationVelocity.y; + if (isSet(SF_HardLimits)) { + float a = Mth::Min(Mth::Max(this->minPoint.x, g), 0.0f); + if (a != g) { + g = a; + this->decelerationVelocity.x = 0; + } + float c = Mth::Min(Mth::Max(this->minPoint.y, h), 0.0f); + if (c != h) { + h = c; + this->decelerationVelocity.y = 0; + } + } + if (noAnimation) { + this->contentOffset().x = g; + this->contentOffset().y = h; + } else { + this->setContentOffset(g, h); + } + if (!this->pagingEnabled) { + this->decelerationVelocity.x *= kDecelerationFrictionFactor; + this->decelerationVelocity.y *= kDecelerationFrictionFactor; + } + float b = Mth::abs(this->decelerationVelocity.x); + float i = Mth::abs(this->decelerationVelocity.y); + if (!noAnimation && b <= kMinimumVelocityToHideScrollIndicators && i <= kMinimumVelocityToHideScrollIndicators) { + this->hideScrollIndicators(); + if (b <= kMinimumVelocity && i <= kMinimumVelocity) { + //LOGI("decelerating False - B\n"); + this->decelerating = false; + didEndDecelerating(); + return; + } + } + if (!noAnimation) { + //this->decelerationTimer = this->callMethodNameAfterDelay("stepThroughDecelerationAnimation", kDesiredAnimationFrameRate) + } + //if (noAnimation) doStepTimer = false; + + + if (isNotSet(SF_HardLimits)) { + Vec3 e; + if (g < this->minDecelerationPoint.x) { + e.x = this->minDecelerationPoint.x - g; + } else { + if (g > this->maxDecelerationPoint.x) { + e.x = this->maxDecelerationPoint.x - g; + } + } + if (h < this->minDecelerationPoint.y) { + e.y = this->minDecelerationPoint.y - h; + } else { + if (h > this->maxDecelerationPoint.y) { + e.y = this->maxDecelerationPoint.y - h; + } + } + if (e.x != 0) { + if (e.x * this->decelerationVelocity.x <= 0) { + this->decelerationVelocity.x += e.x * this->penetrationDeceleration; + } else { + this->decelerationVelocity.x = e.x * this->penetrationAcceleration; + } + } + if (e.y != 0) { + if (e.y * this->decelerationVelocity.y <= 0) { + this->decelerationVelocity.y += e.y * this->penetrationDeceleration; + } else { + this->decelerationVelocity.y = e.y * this->penetrationAcceleration; + } + } + } + if (!noAnimation) { + this->lastFrame = d; + } +} + +void ScrollingPane::scrollBy(float dx, float dy) { + // compute target content offset from wheel delta (in screen-space w/ opposite sign) + float targetX = _contentOffset.x - dx; + float targetY = _contentOffset.y - dy; + + if (isSet(SF_LockX)) targetX = _contentOffset.x; + if (isSet(SF_LockY)) targetY = _contentOffset.y; + + if (isSet(SF_HardLimits)) { + targetX = Mth::clamp(targetX, minPoint.x, 0.0f); + targetY = Mth::clamp(targetY, minPoint.y, 0.0f); + } else { + if (targetX > 0.0f) { + float overshoot = targetX; + overshoot = Mth::Min(overshoot, kWheelOverscrollMax); + targetX = overshoot * kWheelOverscrollDamping; + } else if (targetX < minPoint.x) { + float overshoot = targetX - minPoint.x; + overshoot = Mth::Max(overshoot, -kWheelOverscrollMax); + targetX = minPoint.x + overshoot * kWheelOverscrollDamping; + } + + if (targetY > 0.0f) { + float overshoot = targetY; + overshoot = Mth::Min(overshoot, kWheelOverscrollMax); + targetY = overshoot * kWheelOverscrollDamping; + } else if (targetY < minPoint.y) { + float overshoot = targetY - minPoint.y; + overshoot = Mth::Max(overshoot, -kWheelOverscrollMax); + targetY = minPoint.y + overshoot * kWheelOverscrollDamping; + } + } + + setContentOffset(Vec3(targetX, targetY, 0)); +} + +void ScrollingPane::setContentOffset(float x, float y) { + this->setContentOffsetWithAnimation(Vec3(x, y, 0), false); +} +void ScrollingPane::setContentOffset(Vec3 a) { + this->setContentOffsetWithAnimation(a, false); +}; + +void ScrollingPane::setContentOffsetWithAnimation(Vec3 b, bool doScroll) { + this->_contentOffset = b; + fpx = -this->_contentOffset.x; + fpy = -this->_contentOffset.y; + /* //@todo //@? + this->hostingLayer.style.webkitTransform = PKUtils.t(this->_contentOffset.x, this->_contentOffset.y); + if (a) { + this->scrollTransitionsNeedRemoval = true; + this->hostingLayer.style.webkitTransitionDuration = kPagingTransitionDuration + } else { + this->didScroll(false) + } + */ + if (!doScroll) { + // @todo: for scroll indicator //@? + if (isSet(SF_ShowScrollbar)) { + if (isNotSet(SF_LockX)) this->updateHorizontalScrollIndicator(); + if (isNotSet(SF_LockY)) this->updateVerticalScrollIndicator(); + } + } + //this->notifyPropertyChange("contentOffset") +} + +void ScrollingPane::snapContentOffsetToBounds(bool a) { + bool b = false; + Vec3 c; + if (this->pagingEnabled) { + c.x = std::floor(0.5f + this->_contentOffset.x / this->size.w) * this->size.w; + c.y = std::floor(0.5f + this->_contentOffset.y / this->size.h) * this->size.h; + b = true; + } else { + if (isNotSet(SF_HardLimits)) { + c.x = Mth::Min(Mth::Max(this->minPoint.x, this->_contentOffset.x), 0.0f); + c.y = Mth::Min(Mth::Max(this->minPoint.y, this->_contentOffset.y), 0.0f); + b = (c.x != this->_contentOffset.x || c.y != this->_contentOffset.y); + } + } + if (b) { + this->setContentOffsetWithAnimation(c, a); + } +} + +void ScrollingPane::adjustContentSize() +{ + this->adjustedContentSize.w = Mth::Max(itemBbox.w * columns, bbox.w); + this->adjustedContentSize.h = Mth::Max(itemBbox.h * rows, bbox.h); +} + +void ScrollingPane::onHoldItem() { + //LOGI("dragging, tracking %d, %d\n", !this->dragging, this->tracking); + int id = highlightItem.id; + if (id != -1 && !this->dragging && this->tracking) + _onSelect(id); + highlightTimer = -1; + //highlightItem.id = -1; +} + +bool ScrollingPane::onSelect( int gridId, bool selected ) +{ + return selected; +} + +void ScrollingPane::updateHorizontalScrollIndicator() +{ + float c = (isNotSet(SF_LockX) && isSet(SF_ShowScrollbar)) ? PKScrollIndicatorEndSize * 2 : 1; + float d = Mth::Max(kMinIndicatorLength, std::floor(0.5f + (this->size.w / this->adjustedContentSize.w) * (this->size.w - c))); + float a = (-this->_contentOffset.x / (this->adjustedContentSize.w - this->size.w)) * (this->size.w - c - d); + //float b = this->size.h - PKScrollIndicatorThickness - 1; + if (this->_contentOffset.x > 0) { + d = std::floor(0.5f + Mth::Max(d - this->_contentOffset.x, PKScrollIndicatorThickness)); + a = 1; + } else { + if (this->_contentOffset.x < -(this->adjustedContentSize.w - this->size.w)) { + d = std::floor(0.5f + Mth::Max(d + this->adjustedContentSize.w - this->size.w + this->contentOffset().x, PKScrollIndicatorThickness)); + a = this->size.w - d - c; + } + } + this->hScroll.x = a + bbox.x; + //this->hScroll.y = b; + this->hScroll.w = d; //@property +}; + + +void ScrollingPane::updateVerticalScrollIndicator() +{ + float c = (isNotSet(SF_LockY) && isSet(SF_ShowScrollbar)) ? PKScrollIndicatorEndSize * 2 : 1; + float d = Mth::Max(kMinIndicatorLength, std::floor(0.5f + (this->size.h / this->adjustedContentSize.h) * (this->size.h - c))); + //float a = this->size.w - PKScrollIndicatorThickness - 1; + float b = (-this->_contentOffset.y / (this->adjustedContentSize.h - this->size.h)) * (this->size.h - c - d); + if (this->_contentOffset.y > 0) { + d = std::floor(0.5f + Mth::Max(d - this->_contentOffset.y, PKScrollIndicatorThickness)); + b = 1; + } else { + if (this->_contentOffset.y < -(this->adjustedContentSize.h - this->size.h)) { + d = std::floor(0.5f + Mth::Max(d + this->adjustedContentSize.h - this->size.h + this->contentOffset().y, PKScrollIndicatorThickness)); + b = this->size.h - d - c; + } + } + //this->vScroll.x = a; + this->vScroll.y = b + bbox.y; + this->vScroll.h = d; //@property +}; + +void ScrollingPane::_onSelect( int id ) +{ + if (isSet(SF_MultiSelect)) { + selected[id] = onSelect(id, !selected[id]); + } else { + // Change the selection, if the user wants it + // @note: There's currently no way to clear a selection + bool doSelect = onSelect(id, true); + if (id != selectedId && doSelect) { + onSelect(selectedId, false); + selectedId = id; + } + } +} + +void ScrollingPane::updateScrollFade( ScrollBar& sb ) { + if (sb.fading == 1 && ((sb.alpha += 0.33f) >= 1)) { + sb.alpha = 1; + sb.fading = -1; + } + if (sb.fading == 0 && ((sb.alpha -= 0.10f) <= 0)) { + sb.alpha = 0; + sb.fading = -1; + } +} + +void ScrollingPane::setSelected( int id, bool selected ) +{ + if (isSet(SF_MultiSelect)) + this->selected[id] = selected; + else { + if (selected) selectedId = selected? id : -1; + else if (id == selectedId) + selectedId = -1; + } +} + +void ScrollingPane::translate( float xo, float yo ) +{ + bbox.x += (int)xo; + bbox.y += (int)yo; + area._x0 += xo; + area._x1 += xo; + area._y0 += yo; + area._y1 += yo; + bboxArea._x0 += xo; + bboxArea._x1 += xo; + bboxArea._y0 += yo; + bboxArea._y1 += yo; + hScroll.x += xo; + hScroll.y += yo; + vScroll.x += xo; + vScroll.y += yo; +} + +bool ScrollingPane::queryHoldTime(int* gridItem, int* heldMs) { + *gridItem = -1; + *heldMs = -1; + + if (!dragging && highlightStarted >= 0) { + GridItem item = getItemForPos(_lx, _ly, true); + if (item.id == highlightItem.id) { + *gridItem = highlightItem.id; + *heldMs = getTimeMs() - highlightStarted; + return true; + } else { + highlightStarted = -1; + } + } + + return false; +} diff --git a/src/client/gui/components/ScrollingPane.h b/src/client/gui/components/ScrollingPane.h index 0a2c321..fca1797 100755 --- a/src/client/gui/components/ScrollingPane.h +++ b/src/client/gui/components/ScrollingPane.h @@ -1,203 +1,203 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrollingPane_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrollingPane_H__ - -#include "../GuiComponent.h" -#include "ImageButton.h" -#include "../../player/input/touchscreen/TouchAreaModel.h" -#include "../../../world/phys/Vec3.h" -#include "../../Timer.h" - -enum ScrollingPaneFlags { - SF_LockX = 1 << 0, - SF_LockY = 1 << 1, - SF_WrapX = 1 << 2, - SF_WrapY = 1 << 3, - SF_HardLimits = 1 << 4, - SF_MultiSelect = 1 << 5, - //SF_Snap = 1 << 6, - //SF_CustomSnap = 1 << 7, - //SF_Scissor = 1 << 8, - SF_ShowScrollbar= 1 << 9, - SF_NoHoldSelect = 1 << 10 -}; - -typedef struct ScrollBar { - ScrollBar() - : alpha(0), - fading(-1) - {} - float x; - float y; - float w; - float h; - //bool visible; - float alpha; - int fading; -} ScrollBar; - -class ScrollingPane: public GuiComponent { -public: - typedef struct GridItem { - int id; - int x, y; - // The GUI coordinates comes in (xf, yf) - float xf, yf; - bool selected; - } GridItem; - - ScrollingPane(int flags, const IntRectangle& boundingBox, const IntRectangle& itemRect, int columns, int numItems, float screenScale = 1.0f, const IntRectangle& itemBoundingRect = IntRectangle(0,0,0,0)); - ~ScrollingPane(); - //void init(Minecraft*, int width, int height); - void tick(); - void render(int xm, int ym, float alpha); - - // scroll the content by the given amount (dx horizontal, dy vertical) - // positive values move content downward/rightward - void scrollBy(float dx, float dy); - - bool getGridItemFor_slow(int itemIndex, GridItem& out); - - void setSelected(int id, bool selected); - - // This function is called with all visible GridItems. The base - // implementation just dispatches each item to renderItem in y,x order - virtual void renderBatch(std::vector& items, float alpha); - virtual void renderItem(GridItem& item, float alpha); - - //void render(int xx, int yy); - bool queryHoldTime(int* gridId, int* heldMs); - -protected: - GridItem getItemForPos(float x, float y, bool isScreenPos); - void handleUserInput(); - void addDeltaPos(float x, float y, float dt, int z); - - void translate(float xo, float yo); - - int flags; - - int columns; - int rows; - int numItems; - - int px, py; - float fpx, fpy; - - float screenScale; - float invScreenScale; - //bool hasItemBounding; - - IntRectangle bbox; - IntRectangle itemRect; - IntRectangle itemBbox; - RectangleArea area; - RectangleArea bboxArea; - - // Dragging info - std::vector dragDeltas; - int dragState; - Vec3 dragBeginPos; - Vec3 dragBeginScreenPos; - int dragTicks; - float dragLastDeltaTimeStamp; - Vec3 dragLastPos; - - float dx, dy; - float friction; - - float dstx, dsty; - - // Rewrite - bool dragging; //! - bool decelerating; - bool tracking; //! - - bool pagingEnabled; //! - - Vec3 _contentOffset; //! - Vec3 _contentOffsetBeforeDeceleration; //* - - int lastEventTime; //< - - Vec3 decelerationVelocity; //* - Vec3 minDecelerationPoint; //* - Vec3 maxDecelerationPoint; //* - - float penetrationDeceleration; //< - float penetrationAcceleration; //< - - Vec3 minPoint; //* - Vec3 startPosition; //* - Vec3 startTouchPosition; //* - Vec3 startTimePosition; //* - - bool wasDeceleratingWhenTouchesBegan; //* - bool firstDrag; //< - - float startTime; //< - //float startTime - - IntRectangle size; - int lastFrame; - - bool _scrollEnabled; //! - bool touchesHaveMoved; //* - - virtual void didEndDragging() {} - virtual void didEndDecelerating() {} - virtual void willBeginDecelerating() {} - virtual void willBeginDragging() {} - - int te_moved, - te_ended, - te_highlight; - int highlightTimer; - int highlightStarted; - GridItem highlightItem; - - bool* selected; - int selectedId; - - ScrollBar vScroll, hScroll; - - IntRectangle adjustedContentSize; - void touchesBegan(float x, float y, int t); - void touchesMoved(float x, float y, int t); - void touchesEnded(float x, float y, int t); - void touchesCancelled(float x, float y, int t); - void beginTracking(float x, float y, int t); - void onHoldItem(); - - void _onSelect( int id ); - virtual bool onSelect(int gridId, bool selected); - - Vec3& contentOffset(); - - void startDecelerationAnimation(bool force); - void stopDecelerationAnimation(); - void stepThroughDecelerationAnimation(bool f); - - void setContentOffset(float x, float y); - void setContentOffset(Vec3 a); - void setContentOffsetWithAnimation(Vec3 b, bool doScroll); - void snapContentOffsetToBounds(bool snap); //* - void adjustContentSize(); - //TouchAreaModel _areaModel; - - bool isAllSet(int flag) { return (flags & flag) == flag; } - bool isSet(int flag) { return (flags & flag) != 0; } - bool isNotSet(int flag) { return !isSet(flag); } - - void updateHorizontalScrollIndicator(); - void updateVerticalScrollIndicator(); - void hideScrollIndicators(); //* - void updateScrollFade( ScrollBar& vScroll ); -private: - Timer _timer; - bool _doStepTimer; - bool _wasDown; - float _lx; - float _ly; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrollingPane_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrollingPane_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrollingPane_H__ + +#include "../GuiComponent.h" +#include "ImageButton.h" +#include "../../player/input/touchscreen/TouchAreaModel.h" +#include "../../../world/phys/Vec3.h" +#include "../../Timer.h" + +enum ScrollingPaneFlags { + SF_LockX = 1 << 0, + SF_LockY = 1 << 1, + SF_WrapX = 1 << 2, + SF_WrapY = 1 << 3, + SF_HardLimits = 1 << 4, + SF_MultiSelect = 1 << 5, + //SF_Snap = 1 << 6, + //SF_CustomSnap = 1 << 7, + //SF_Scissor = 1 << 8, + SF_ShowScrollbar= 1 << 9, + SF_NoHoldSelect = 1 << 10 +}; + +typedef struct ScrollBar { + ScrollBar() + : alpha(0), + fading(-1) + {} + float x; + float y; + float w; + float h; + //bool visible; + float alpha; + int fading; +} ScrollBar; + +class ScrollingPane: public GuiComponent { +public: + typedef struct GridItem { + int id; + int x, y; + // The GUI coordinates comes in (xf, yf) + float xf, yf; + bool selected; + } GridItem; + + ScrollingPane(int flags, const IntRectangle& boundingBox, const IntRectangle& itemRect, int columns, int numItems, float screenScale = 1.0f, const IntRectangle& itemBoundingRect = IntRectangle(0,0,0,0)); + ~ScrollingPane(); + //void init(Minecraft*, int width, int height); + void tick(); + void render(int xm, int ym, float alpha); + + // scroll the content by the given amount (dx horizontal, dy vertical) + // positive values move content downward/rightward + void scrollBy(float dx, float dy); + + bool getGridItemFor_slow(int itemIndex, GridItem& out); + + void setSelected(int id, bool selected); + + // This function is called with all visible GridItems. The base + // implementation just dispatches each item to renderItem in y,x order + virtual void renderBatch(std::vector& items, float alpha); + virtual void renderItem(GridItem& item, float alpha); + + //void render(int xx, int yy); + bool queryHoldTime(int* gridId, int* heldMs); + +protected: + GridItem getItemForPos(float x, float y, bool isScreenPos); + void handleUserInput(); + void addDeltaPos(float x, float y, float dt, int z); + + void translate(float xo, float yo); + + int flags; + + int columns; + int rows; + int numItems; + + int px, py; + float fpx, fpy; + + float screenScale; + float invScreenScale; + //bool hasItemBounding; + + IntRectangle bbox; + IntRectangle itemRect; + IntRectangle itemBbox; + RectangleArea area; + RectangleArea bboxArea; + + // Dragging info + std::vector dragDeltas; + int dragState; + Vec3 dragBeginPos; + Vec3 dragBeginScreenPos; + int dragTicks; + float dragLastDeltaTimeStamp; + Vec3 dragLastPos; + + float dx, dy; + float friction; + + float dstx, dsty; + + // Rewrite + bool dragging; //! + bool decelerating; + bool tracking; //! + + bool pagingEnabled; //! + + Vec3 _contentOffset; //! + Vec3 _contentOffsetBeforeDeceleration; //* + + int lastEventTime; //< + + Vec3 decelerationVelocity; //* + Vec3 minDecelerationPoint; //* + Vec3 maxDecelerationPoint; //* + + float penetrationDeceleration; //< + float penetrationAcceleration; //< + + Vec3 minPoint; //* + Vec3 startPosition; //* + Vec3 startTouchPosition; //* + Vec3 startTimePosition; //* + + bool wasDeceleratingWhenTouchesBegan; //* + bool firstDrag; //< + + float startTime; //< + //float startTime + + IntRectangle size; + int lastFrame; + + bool _scrollEnabled; //! + bool touchesHaveMoved; //* + + virtual void didEndDragging() {} + virtual void didEndDecelerating() {} + virtual void willBeginDecelerating() {} + virtual void willBeginDragging() {} + + int te_moved, + te_ended, + te_highlight; + int highlightTimer; + int highlightStarted; + GridItem highlightItem; + + bool* selected; + int selectedId; + + ScrollBar vScroll, hScroll; + + IntRectangle adjustedContentSize; + void touchesBegan(float x, float y, int t); + void touchesMoved(float x, float y, int t); + void touchesEnded(float x, float y, int t); + void touchesCancelled(float x, float y, int t); + void beginTracking(float x, float y, int t); + void onHoldItem(); + + void _onSelect( int id ); + virtual bool onSelect(int gridId, bool selected); + + Vec3& contentOffset(); + + void startDecelerationAnimation(bool force); + void stopDecelerationAnimation(); + void stepThroughDecelerationAnimation(bool f); + + void setContentOffset(float x, float y); + void setContentOffset(Vec3 a); + void setContentOffsetWithAnimation(Vec3 b, bool doScroll); + void snapContentOffsetToBounds(bool snap); //* + void adjustContentSize(); + //TouchAreaModel _areaModel; + + bool isAllSet(int flag) { return (flags & flag) == flag; } + bool isSet(int flag) { return (flags & flag) != 0; } + bool isNotSet(int flag) { return !isSet(flag); } + + void updateHorizontalScrollIndicator(); + void updateVerticalScrollIndicator(); + void hideScrollIndicators(); //* + void updateScrollFade( ScrollBar& vScroll ); +private: + Timer _timer; + bool _doStepTimer; + bool _wasDown; + float _lx; + float _ly; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__ScrollingPane_H__*/ diff --git a/src/client/gui/components/Slider.cpp b/src/client/gui/components/Slider.cpp index 27a3b36..07d90f7 100755 --- a/src/client/gui/components/Slider.cpp +++ b/src/client/gui/components/Slider.cpp @@ -1,94 +1,94 @@ -#include "Slider.h" -#include "../../Minecraft.h" -#include "../../renderer/Textures.h" -#include "../Screen.h" -#include "../../../locale/I18n.h" -#include "../../../util/Mth.h" -#include -#include - -Slider::Slider(OptionId optId) : m_mouseDownOnElement(false), m_optId(optId), m_numSteps(0) {} - -void Slider::render( Minecraft* minecraft, int xm, int ym ) { - int xSliderStart = x + 5; - int xSliderEnd = x + width - 5; - int ySliderStart = y + 6; - int ySliderEnd = y + 9; - int handleSizeX = 9; - int handleSizeY = 15; - int barWidth = xSliderEnd - xSliderStart; - //fill(x, y + 8, x + (int)(width * percentage), y + height, 0xffff00ff); - fill(xSliderStart, ySliderStart, xSliderEnd, ySliderEnd, 0xff606060); - - if (m_numSteps > 2) { - int stepDistance = barWidth / (m_numSteps-1); - for(int a = 0; a < m_numSteps; ++a) { - int renderSliderStepPosX = xSliderStart + a * stepDistance + 1; - fill(renderSliderStepPosX - 1, ySliderStart - 2, renderSliderStepPosX + 1, ySliderEnd + 2, 0xff606060); - } - } - - minecraft->textures->loadAndBindTexture("gui/touchgui.png"); - blit(xSliderStart + (int)(m_percentage * barWidth) - handleSizeX / 2, y, 226, 126, handleSizeX, handleSizeY, handleSizeX, handleSizeY); -} - -void Slider::mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ) { - if(pointInside(x, y)) { - m_mouseDownOnElement = true; - } -} - -void Slider::mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) { - m_mouseDownOnElement = false; -} - -void Slider::tick(Minecraft* minecraft) { - if(minecraft->screen != NULL) { - int xm = Mouse::getX(); - int ym = Mouse::getY(); - - minecraft->screen->toGUICoordinate(xm, ym); - - if(m_mouseDownOnElement) { - m_percentage = float(xm - x) / float(width); - m_percentage = Mth::clamp(m_percentage, 0.0f, 1.0f); - } - } -} - -SliderFloat::SliderFloat(Minecraft* minecraft, OptionId option) -: Slider(option), m_option(dynamic_cast(minecraft->options.getOpt(option))) -{ - m_percentage = Mth::clamp((m_option->get() - m_option->getMin()) / (m_option->getMax() - m_option->getMin()), 0.f, 1.f); -} - -SliderInt::SliderInt(Minecraft* minecraft, OptionId option) -: Slider(option), m_option(dynamic_cast(minecraft->options.getOpt(option))) -{ - m_numSteps = m_option->getMax() - m_option->getMin() + 1; - m_percentage = float(m_option->get() - m_option->getMin()) / (m_numSteps-1); -} - -void SliderInt::render( Minecraft* minecraft, int xm, int ym ) { - Slider::render(minecraft, xm, ym); -} - -void SliderInt::mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) { - Slider::mouseReleased(minecraft, x, y, buttonNum); - - if (pointInside(x, y)) { - int curStep = int(m_percentage * (m_numSteps-1) + 0.5f); - curStep = Mth::clamp(curStep + m_option->getMin(), m_option->getMin(), m_option->getMax()); - m_percentage = float(curStep - m_option->getMin()) / (m_numSteps-1); - - minecraft->options.set(m_optId, curStep); - } -} - -void SliderFloat::mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) { - Slider::mouseReleased(minecraft, x, y, buttonNum); - - if (pointInside(x, y)) { - minecraft->options.set(m_optId, m_percentage * (m_option->getMax() - m_option->getMin()) + m_option->getMin()); - } +#include "Slider.h" +#include "../../Minecraft.h" +#include "../../renderer/Textures.h" +#include "../Screen.h" +#include "../../../locale/I18n.h" +#include "../../../util/Mth.h" +#include +#include + +Slider::Slider(OptionId optId) : m_mouseDownOnElement(false), m_optId(optId), m_numSteps(0) {} + +void Slider::render( Minecraft* minecraft, int xm, int ym ) { + int xSliderStart = x + 5; + int xSliderEnd = x + width - 5; + int ySliderStart = y + 6; + int ySliderEnd = y + 9; + int handleSizeX = 9; + int handleSizeY = 15; + int barWidth = xSliderEnd - xSliderStart; + //fill(x, y + 8, x + (int)(width * percentage), y + height, 0xffff00ff); + fill(xSliderStart, ySliderStart, xSliderEnd, ySliderEnd, 0xff606060); + + if (m_numSteps > 2) { + int stepDistance = barWidth / (m_numSteps-1); + for(int a = 0; a < m_numSteps; ++a) { + int renderSliderStepPosX = xSliderStart + a * stepDistance + 1; + fill(renderSliderStepPosX - 1, ySliderStart - 2, renderSliderStepPosX + 1, ySliderEnd + 2, 0xff606060); + } + } + + minecraft->textures->loadAndBindTexture("gui/touchgui.png"); + blit(xSliderStart + (int)(m_percentage * barWidth) - handleSizeX / 2, y, 226, 126, handleSizeX, handleSizeY, handleSizeX, handleSizeY); +} + +void Slider::mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ) { + if(pointInside(x, y)) { + m_mouseDownOnElement = true; + } +} + +void Slider::mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) { + m_mouseDownOnElement = false; +} + +void Slider::tick(Minecraft* minecraft) { + if(minecraft->screen != NULL) { + int xm = Mouse::getX(); + int ym = Mouse::getY(); + + minecraft->screen->toGUICoordinate(xm, ym); + + if(m_mouseDownOnElement) { + m_percentage = float(xm - x) / float(width); + m_percentage = Mth::clamp(m_percentage, 0.0f, 1.0f); + } + } +} + +SliderFloat::SliderFloat(Minecraft* minecraft, OptionId option) +: Slider(option), m_option(dynamic_cast(minecraft->options.getOpt(option))) +{ + m_percentage = Mth::clamp((m_option->get() - m_option->getMin()) / (m_option->getMax() - m_option->getMin()), 0.f, 1.f); +} + +SliderInt::SliderInt(Minecraft* minecraft, OptionId option) +: Slider(option), m_option(dynamic_cast(minecraft->options.getOpt(option))) +{ + m_numSteps = m_option->getMax() - m_option->getMin() + 1; + m_percentage = float(m_option->get() - m_option->getMin()) / (m_numSteps-1); +} + +void SliderInt::render( Minecraft* minecraft, int xm, int ym ) { + Slider::render(minecraft, xm, ym); +} + +void SliderInt::mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) { + Slider::mouseReleased(minecraft, x, y, buttonNum); + + if (pointInside(x, y)) { + int curStep = int(m_percentage * (m_numSteps-1) + 0.5f); + curStep = Mth::clamp(curStep + m_option->getMin(), m_option->getMin(), m_option->getMax()); + m_percentage = float(curStep - m_option->getMin()) / (m_numSteps-1); + + minecraft->options.set(m_optId, curStep); + } +} + +void SliderFloat::mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) { + Slider::mouseReleased(minecraft, x, y, buttonNum); + + if (pointInside(x, y)) { + minecraft->options.set(m_optId, m_percentage * (m_option->getMax() - m_option->getMin()) + m_option->getMin()); + } } \ No newline at end of file diff --git a/src/client/gui/components/Slider.h b/src/client/gui/components/Slider.h index 8168741..d3e302d 100755 --- a/src/client/gui/components/Slider.h +++ b/src/client/gui/components/Slider.h @@ -1,47 +1,47 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Slider_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Slider_H__ - -#include "GuiElement.h" -#include "../../../client/Options.h" - -class Slider : public GuiElement { - typedef GuiElement super; -public: - virtual void render( Minecraft* minecraft, int xm, int ym ); - virtual void mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ); - virtual void mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ); - virtual void tick(Minecraft* minecraft); - -protected: - Slider(OptionId optId); - - OptionId m_optId; - - bool m_mouseDownOnElement; - float m_percentage; - int m_numSteps; -}; - -class SliderFloat : public Slider { -public: - SliderFloat(Minecraft* minecraft, OptionId option); - - virtual void mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) override; - -protected: - OptionFloat* m_option; -}; - - -class SliderInt : public Slider { -public: - SliderInt(Minecraft* minecraft, OptionId option); - - virtual void render( Minecraft* minecraft, int xm, int ym ) override; - virtual void mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) override; - -protected: - OptionInt* m_option; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Slider_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Slider_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Slider_H__ + +#include "GuiElement.h" +#include "../../../client/Options.h" + +class Slider : public GuiElement { + typedef GuiElement super; +public: + virtual void render( Minecraft* minecraft, int xm, int ym ); + virtual void mouseClicked( Minecraft* minecraft, int x, int y, int buttonNum ); + virtual void mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ); + virtual void tick(Minecraft* minecraft); + +protected: + Slider(OptionId optId); + + OptionId m_optId; + + bool m_mouseDownOnElement; + float m_percentage; + int m_numSteps; +}; + +class SliderFloat : public Slider { +public: + SliderFloat(Minecraft* minecraft, OptionId option); + + virtual void mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) override; + +protected: + OptionFloat* m_option; +}; + + +class SliderInt : public Slider { +public: + SliderInt(Minecraft* minecraft, OptionId option); + + virtual void render( Minecraft* minecraft, int xm, int ym ) override; + virtual void mouseReleased( Minecraft* minecraft, int x, int y, int buttonNum ) override; + +protected: + OptionInt* m_option; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__Slider_H__*/ diff --git a/src/client/gui/components/SmallButton.cpp b/src/client/gui/components/SmallButton.cpp index e154736..01c1674 100644 --- a/src/client/gui/components/SmallButton.cpp +++ b/src/client/gui/components/SmallButton.cpp @@ -1,24 +1,24 @@ -#include "SmallButton.h" - -SmallButton::SmallButton( int id, int x, int y, const std::string& msg ) -: super(id, x, y, 150, 20, msg), - option(NULL) -{ -} - -SmallButton::SmallButton( int id, int x, int y, int width, int height, const std::string& msg ) -: super(id, x, y, width, height, msg), - option(NULL) -{ -} - -SmallButton::SmallButton( int id, int x, int y, Option* item, const std::string& msg ) -: super(id, x, y, 150, 20, msg), - option(item) -{ -} - -Option* SmallButton::getOption() -{ - return option; -} +#include "SmallButton.h" + +SmallButton::SmallButton( int id, int x, int y, const std::string& msg ) +: super(id, x, y, 150, 20, msg), + option(NULL) +{ +} + +SmallButton::SmallButton( int id, int x, int y, int width, int height, const std::string& msg ) +: super(id, x, y, width, height, msg), + option(NULL) +{ +} + +SmallButton::SmallButton( int id, int x, int y, Option* item, const std::string& msg ) +: super(id, x, y, 150, 20, msg), + option(item) +{ +} + +Option* SmallButton::getOption() +{ + return option; +} diff --git a/src/client/gui/components/SmallButton.h b/src/client/gui/components/SmallButton.h index 2fd6372..5e1617e 100644 --- a/src/client/gui/components/SmallButton.h +++ b/src/client/gui/components/SmallButton.h @@ -1,23 +1,23 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__SmallButton_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__SmallButton_H__ - -//package net.minecraft.client.gui; - -#include -#include "Button.h" -#include "../../Options.h" - -class SmallButton: public Button -{ - typedef Button super; -public: - SmallButton(int id, int x, int y, const std::string& msg); - SmallButton(int id, int x, int y, int width, int height, const std::string& msg); - SmallButton(int id, int x, int y, Option* item, const std::string& msg); - - Option* getOption(); -private: - Option* option; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__SmallButton_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__SmallButton_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__SmallButton_H__ + +//package net.minecraft.client.gui; + +#include +#include "Button.h" +#include "../../Options.h" + +class SmallButton: public Button +{ + typedef Button super; +public: + SmallButton(int id, int x, int y, const std::string& msg); + SmallButton(int id, int x, int y, int width, int height, const std::string& msg); + SmallButton(int id, int x, int y, Option* item, const std::string& msg); + + Option* getOption(); +private: + Option* option; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__SmallButton_H__*/ diff --git a/src/client/gui/components/TextBox.cpp b/src/client/gui/components/TextBox.cpp index 16bf8a6..f7f68bf 100755 --- a/src/client/gui/components/TextBox.cpp +++ b/src/client/gui/components/TextBox.cpp @@ -1,102 +1,102 @@ -#include "TextBox.h" -#include "../Gui.h" -#include "../../Minecraft.h" -#include "../../../AppPlatform.h" -#include "../../../platform/input/Mouse.h" - -// delegate constructors -TextBox::TextBox(int id, const std::string& msg) - : TextBox(id, 0, 0, msg) -{ -} - -TextBox::TextBox(int id, int x, int y, const std::string& msg) - : TextBox(id, x, y, 24, Font::DefaultLineHeight + 4, msg) -{ -} - -TextBox::TextBox(int id, int x, int y, int w, int h, const std::string& msg) - : GuiElement(true, true, x, y, w, h), - id(id), hint(msg), focused(false), blink(false), blinkTicks(0) -{ -} - -void TextBox::setFocus(Minecraft* minecraft) { - if (!focused) { - minecraft->platform()->showKeyboard(); - focused = true; - blinkTicks = 0; - blink = false; - } -} - -bool TextBox::loseFocus(Minecraft* minecraft) { - if (focused) { - minecraft->platform()->hideKeyboard(); - focused = false; - return true; - } - return false; -} - -void TextBox::mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum) { - if (buttonNum == MouseAction::ACTION_LEFT) { - if (pointInside(x, y)) { - setFocus(minecraft); - } else { - loseFocus(minecraft); - } - } -} - -void TextBox::charPressed(Minecraft* minecraft, char c) { - if (focused && c >= 32 && c < 127 && (int)text.size() < 256) { - text.push_back(c); - } -} - -void TextBox::keyPressed(Minecraft* minecraft, int key) { - if (focused && key == Keyboard::KEY_BACKSPACE && !text.empty()) { - text.pop_back(); - } -} - -void TextBox::tick(Minecraft* minecraft) { - blinkTicks++; - if (blinkTicks >= 5) { - blink = !blink; - blinkTicks = 0; - } -} - -void TextBox::render(Minecraft* minecraft, int xm, int ym) { - // textbox like in beta 1.7.3 - // change appearance when focused so the user can tell it's active - // active background darker gray with a subtle border - uint32_t bgColor = focused ? 0xffa0a0a0 : 0xffa0a0a0; - uint32_t borderColor = focused ? 0xff000000 : 0xff000000; - fill(x, y, x + width, y + height, bgColor); - fill(x + 1, y + 1, x + width - 1, y + height - 1, borderColor); - - glEnable2(GL_SCISSOR_TEST); - glScissor( - Gui::GuiScale * (x + 2), - minecraft->height - Gui::GuiScale * (y + height - 2), - Gui::GuiScale * (width - 2), - Gui::GuiScale * (height - 2) - ); - - int _y = y + (height - Font::DefaultLineHeight) / 2; - - if (text.empty() && !focused) { - drawString(minecraft->font, hint, x + 2, _y, 0xff5e5e5e); - } - - if (focused && blink) text.push_back('_'); - - drawString(minecraft->font, text, x + 2, _y, 0xffffffff); - - if (focused && blink) text.pop_back(); - - glDisable2(GL_SCISSOR_TEST); -} +#include "TextBox.h" +#include "../Gui.h" +#include "../../Minecraft.h" +#include "../../../AppPlatform.h" +#include "../../../platform/input/Mouse.h" + +// delegate constructors +TextBox::TextBox(int id, const std::string& msg) + : TextBox(id, 0, 0, msg) +{ +} + +TextBox::TextBox(int id, int x, int y, const std::string& msg) + : TextBox(id, x, y, 24, Font::DefaultLineHeight + 4, msg) +{ +} + +TextBox::TextBox(int id, int x, int y, int w, int h, const std::string& msg) + : GuiElement(true, true, x, y, w, h), + id(id), hint(msg), focused(false), blink(false), blinkTicks(0) +{ +} + +void TextBox::setFocus(Minecraft* minecraft) { + if (!focused) { + minecraft->platform()->showKeyboard(); + focused = true; + blinkTicks = 0; + blink = false; + } +} + +bool TextBox::loseFocus(Minecraft* minecraft) { + if (focused) { + minecraft->platform()->hideKeyboard(); + focused = false; + return true; + } + return false; +} + +void TextBox::mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum) { + if (buttonNum == MouseAction::ACTION_LEFT) { + if (pointInside(x, y)) { + setFocus(minecraft); + } else { + loseFocus(minecraft); + } + } +} + +void TextBox::charPressed(Minecraft* minecraft, char c) { + if (focused && c >= 32 && c < 127 && (int)text.size() < 256) { + text.push_back(c); + } +} + +void TextBox::keyPressed(Minecraft* minecraft, int key) { + if (focused && key == Keyboard::KEY_BACKSPACE && !text.empty()) { + text.pop_back(); + } +} + +void TextBox::tick(Minecraft* minecraft) { + blinkTicks++; + if (blinkTicks >= 5) { + blink = !blink; + blinkTicks = 0; + } +} + +void TextBox::render(Minecraft* minecraft, int xm, int ym) { + // textbox like in beta 1.7.3 + // change appearance when focused so the user can tell it's active + // active background darker gray with a subtle border + uint32_t bgColor = focused ? 0xffa0a0a0 : 0xffa0a0a0; + uint32_t borderColor = focused ? 0xff000000 : 0xff000000; + fill(x, y, x + width, y + height, bgColor); + fill(x + 1, y + 1, x + width - 1, y + height - 1, borderColor); + + glEnable2(GL_SCISSOR_TEST); + glScissor( + Gui::GuiScale * (x + 2), + minecraft->height - Gui::GuiScale * (y + height - 2), + Gui::GuiScale * (width - 2), + Gui::GuiScale * (height - 2) + ); + + int _y = y + (height - Font::DefaultLineHeight) / 2; + + if (text.empty() && !focused) { + drawString(minecraft->font, hint, x + 2, _y, 0xff5e5e5e); + } + + if (focused && blink) text.push_back('_'); + + drawString(minecraft->font, text, x + 2, _y, 0xffffffff); + + if (focused && blink) text.pop_back(); + + glDisable2(GL_SCISSOR_TEST); +} diff --git a/src/client/gui/components/TextBox.h b/src/client/gui/components/TextBox.h index 34ea5a7..7b8e7d1 100755 --- a/src/client/gui/components/TextBox.h +++ b/src/client/gui/components/TextBox.h @@ -1,44 +1,44 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__TextBox_H__ -#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__TextBox_H__ - -//package net.minecraft.client.gui; - -#include -#include "GuiElement.h" -#include "../../Options.h" -#include "../../../platform/input/Mouse.h" -#include "../../../platform/input/Keyboard.h" - -class Font; -class Minecraft; - -class TextBox: public GuiElement -{ -public: - TextBox(int id, const std::string& msg); - TextBox(int id, int x, int y, const std::string& msg); - TextBox(int id, int x, int y, int w, int h, const std::string& msg); - - virtual void mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum); - - virtual void setFocus(Minecraft* minecraft); - virtual bool loseFocus(Minecraft* minecraft); - - virtual void render(Minecraft* minecraft, int xm, int ym); - - virtual void keyPressed(Minecraft* minecraft, int key); - virtual void charPressed(Minecraft* minecraft, char c); - virtual void tick(Minecraft* minecraft); - -public: - std::string hint; - std::string text; - int id; - - int blinkTicks; - - bool focused; - bool blink; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__TextBox_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_COMPONENTS__TextBox_H__ +#define NET_MINECRAFT_CLIENT_GUI_COMPONENTS__TextBox_H__ + +//package net.minecraft.client.gui; + +#include +#include "GuiElement.h" +#include "../../Options.h" +#include "../../../platform/input/Mouse.h" +#include "../../../platform/input/Keyboard.h" + +class Font; +class Minecraft; + +class TextBox: public GuiElement +{ +public: + TextBox(int id, const std::string& msg); + TextBox(int id, int x, int y, const std::string& msg); + TextBox(int id, int x, int y, int w, int h, const std::string& msg); + + virtual void mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum); + + virtual void setFocus(Minecraft* minecraft); + virtual bool loseFocus(Minecraft* minecraft); + + virtual void render(Minecraft* minecraft, int xm, int ym); + + virtual void keyPressed(Minecraft* minecraft, int key); + virtual void charPressed(Minecraft* minecraft, char c); + virtual void tick(Minecraft* minecraft); + +public: + std::string hint; + std::string text; + int id; + + int blinkTicks; + + bool focused; + bool blink; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_COMPONENTS__TextBox_H__*/ diff --git a/src/client/gui/components/TextOption.h b/src/client/gui/components/TextOption.h index b93a79c..89c545c 100644 --- a/src/client/gui/components/TextOption.h +++ b/src/client/gui/components/TextOption.h @@ -1,10 +1,10 @@ #pragma once #include "TextBox.h" -#include "Options.h" +#include class TextOption : public TextBox { public: TextOption(Minecraft* minecraft, OptionId optId); virtual bool loseFocus(Minecraft* minecraft); -}; +}; \ No newline at end of file diff --git a/src/client/gui/screens/ArmorScreen.cpp b/src/client/gui/screens/ArmorScreen.cpp index 593a892..c4249f7 100755 --- a/src/client/gui/screens/ArmorScreen.cpp +++ b/src/client/gui/screens/ArmorScreen.cpp @@ -1,370 +1,370 @@ -#include "ArmorScreen.h" -#include "../Screen.h" -#include "../components/NinePatch.h" -#include "../../Minecraft.h" -#include "../../player/LocalPlayer.h" -#include "../../renderer/Tesselator.h" -#include "../../renderer/entity/ItemRenderer.h" -#include "../../../world/item/Item.h" -#include "../../../world/item/ItemCategory.h" -#include "../../../world/entity/player/Inventory.h" -#include "../../../world/entity/item/ItemEntity.h" -#include "../../../world/level/Level.h" -#include "../../../network/RakNetInstance.h" -#include "../../renderer/entity/EntityRenderDispatcher.h" -#include "../../../world/item/ArmorItem.h" - -static void setIfNotSet(bool& ref, bool condition) { - ref = (ref || condition); -} - -const int descFrameWidth = 100; - -const int rgbActive = 0xfff0f0f0; -const int rgbInactive = 0xc0635558; -const int rgbInactiveShadow = 0xc0aaaaaa; - -#ifdef __APPLE__ - static const float BorderPixels = 4; - #ifdef DEMO_MODE - static const float BlockPixels = 22; - #else - static const float BlockPixels = 22; - #endif -#else - static const float BorderPixels = 4; - static const float BlockPixels = 24; -#endif -static const int ItemSize = (int)(BlockPixels + 2*BorderPixels); - -static const int Bx = 10; // Border Frame width -static const int By = 6; // Border Frame height - - -ArmorScreen::ArmorScreen(): - inventoryPane(NULL), - btnArmor0(0), - btnArmor1(1), - btnArmor2(2), - btnArmor3(3), - btnClose(4, ""), - bHeader (5, "Armor"), - guiBackground(NULL), - guiSlot(NULL), - guiPaneFrame(NULL), - guiPlayerBg(NULL), - doRecreatePane(false), - descWidth(90) - //guiSlotItem(NULL), - //guiSlotItemSelected(NULL) -{ - //LOGI("Creating ArmorScreen with %p, %d\n", furnace, furnace->runningId); -} - -ArmorScreen::~ArmorScreen() { - delete inventoryPane; - - delete guiBackground; - delete guiSlot; - delete guiPaneFrame; - delete guiPlayerBg; -} - -void ArmorScreen::init() { - super::init(); - - player = minecraft->player; - - ImageDef def; - def.name = "gui/spritesheet.png"; - def.x = 0; - def.y = 1; - def.width = def.height = 18; - def.setSrc(IntRectangle(60, 0, 18, 18)); - btnClose.setImageDef(def, true); - btnClose.scaleWhenPressed = false; - - buttons.push_back(&bHeader); - buttons.push_back(&btnClose); - - armorButtons[0] = &btnArmor0; - armorButtons[1] = &btnArmor1; - armorButtons[2] = &btnArmor2; - armorButtons[3] = &btnArmor3; - for (int i = 0; i < NUM_ARMORBUTTONS; ++i) - buttons.push_back(armorButtons[i]); - - // GUI - nine patches - NinePatchFactory builder(minecraft->textures, "gui/spritesheet.png"); - - guiBackground = builder.createSymmetrical(IntRectangle(0, 0, 16, 16), 4, 4); - guiSlot = builder.createSymmetrical(IntRectangle(0, 32, 8, 8), 3, 3, 20, 20); - guiPaneFrame = builder.createSymmetrical(IntRectangle(28, 42, 4, 4), 1, 1)->setExcluded(1 << 4); - guiPlayerBg = builder.createSymmetrical(IntRectangle(0, 20, 8, 8), 3, 3); - - updateItems(); -} - -void ArmorScreen::setupPositions() { - // Left - Categories - bHeader.x = bHeader.y = 0; - bHeader.width = width;// - bDone.w; - - btnClose.width = btnClose.height = 19; - btnClose.x = width - btnClose.width; - btnClose.y = 0; - - // Inventory pane - const int maxWidth = (int)(width/1.8f) - Bx - Bx; - const int InventoryColumns = maxWidth / ItemSize; - const int realWidth = InventoryColumns * ItemSize; - const int paneWidth = realWidth + Bx + Bx; - const int realBx = (paneWidth - realWidth) / 2; - - inventoryPaneRect = IntRectangle(realBx, -#ifdef __APPLE__ - 26 + By - ((width==240)?1:0), realWidth, ((width==240)?1:0) + height-By-By-28); -#else - 26 + By, realWidth, height-By-By-28); -#endif - - for (int i = 0; i < NUM_ARMORBUTTONS; ++i) { - Button& b = *armorButtons[i]; - b.x = paneWidth; - b.y = inventoryPaneRect.y + 24 * i; - b.width = 20; - b.height = 20; - } - - guiPlayerBgRect.y = inventoryPaneRect.y; - int xx = armorButtons[0]->x + armorButtons[0]->width; - int xw = width - xx; - guiPlayerBgRect.x = xx + xw / 10; - guiPlayerBgRect.w = xw - (xw / 10) * 2; - guiPlayerBgRect.h = inventoryPaneRect.h; - - guiPaneFrame->setSize((float)inventoryPaneRect.w + 2, (float)inventoryPaneRect.h + 2); - guiPlayerBg->setSize((float)guiPlayerBgRect.w, (float)guiPlayerBgRect.h); - guiBackground->setSize((float)width, (float)height); - - updateItems(); - setupInventoryPane(); -} - -void ArmorScreen::tick() { - if (inventoryPane) - inventoryPane->tick(); - - if (doRecreatePane) { - updateItems(); - setupInventoryPane(); - doRecreatePane = false; - } -} - -void ArmorScreen::handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a) { - if (pane) { - pane->render(xm, ym, a); - guiPaneFrame->draw(t, (float)(pane->rect.x - 1), (float)(pane->rect.y - 1)); - } -} - -void ArmorScreen::render(int xm, int ym, float a) { - //renderBackground(); - - Tesselator& t = Tesselator::instance; - - t.addOffset(0, 0, -500); - guiBackground->draw(t, 0, 0); - t.addOffset(0, 0, 500); - glEnable2(GL_ALPHA_TEST); - - // Buttons (Left side + crafting) - super::render(xm, ym, a); - - handleRenderPane(inventoryPane, t, xm, ym, a); - - t.colorABGR(0xffffffff); - glColor4f2(1, 1, 1, 1); - - t.addOffset(0, 0, -490); - guiPlayerBg->draw(t, (float)guiPlayerBgRect.x, (float)guiPlayerBgRect.y); - t.addOffset(0, 0, 490); - renderPlayer((float)(guiPlayerBgRect.x + guiPlayerBgRect.w / 2), 0.85f * height); - - for (int i = 0; i < NUM_ARMORBUTTONS; ++i) { - drawSlotItemAt(t, i, player->getArmor(i), armorButtons[i]->x, armorButtons[i]->y); - } - glDisable2(GL_ALPHA_TEST); -} - -void ArmorScreen::buttonClicked(Button* button) { - if (button == &btnClose) { - minecraft->setScreen(NULL); - } - - if (button->id >= 0 && button->id <= 3) { - takeAndClearSlot(button->id); - } -} - -bool ArmorScreen::addItem(const Touch::InventoryPane* forPane, int itemIndex) { - const ItemInstance* instance = armorItems[itemIndex]; - if (!ItemInstance::isArmorItem(instance)) - return false; - - ArmorItem* item = (ArmorItem*) instance->getItem(); - ItemInstance* old = player->getArmor(item->slot); - ItemInstance oldArmor; - - if (ItemInstance::isArmorItem(old)) { - oldArmor = *old; - } - - player->setArmor(item->slot, instance); - - player->inventory->removeItem(instance); - //@attn: this is hugely important - armorItems[itemIndex] = NULL; - - if (!oldArmor.isNull()) { - if (!player->inventory->add(&oldArmor)) { - player->drop(new ItemInstance(oldArmor), false); - } - } - - doRecreatePane = true; - return true; -} - -bool ArmorScreen::isAllowed( int slot ) { - return true; -} - -bool ArmorScreen::renderGameBehind() { - return false; -} - -std::vector ArmorScreen::getItems( const Touch::InventoryPane* forPane ) { - return armorItems; -} - -void ArmorScreen::updateItems() { - armorItems.clear(); - - for (int i = Inventory::MAX_SELECTION_SIZE; i < minecraft->player->inventory->getContainerSize(); ++i) { - ItemInstance* item = minecraft->player->inventory->getItem(i); - if (ItemInstance::isArmorItem(item)) - armorItems.push_back(item); - } -} - -bool ArmorScreen::canMoveToSlot(int slot, const ItemInstance* item) { - return ItemInstance::isArmorItem(item) - && ((ArmorItem*)item)->slot == slot; -} - -void ArmorScreen::setupInventoryPane() { - // IntRectangle(0, 0, 100, 100) - if (inventoryPane) delete inventoryPane; - inventoryPane = new Touch::InventoryPane(this, minecraft, inventoryPaneRect, inventoryPaneRect.w, BorderPixels, armorItems.size(), ItemSize, (int)BorderPixels); - inventoryPane->fillMarginX = 0; - inventoryPane->fillMarginY = 0; - //LOGI("Creating new pane: %d %p\n", inventoryItems.size(), inventoryPane); -} - -void ArmorScreen::drawSlotItemAt( Tesselator& t, int slot, const ItemInstance* item, int x, int y) -{ - float xx = (float)x; - float yy = (float)y; - - guiSlot->draw(t, xx, yy); - - if (item && !item->isNull()) { - ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, xx + 2, yy, true); - glDisable2(GL_TEXTURE_2D); - ItemRenderer::renderGuiItemDecorations(item, xx + 2, yy + 3); - glEnable2(GL_TEXTURE_2D); - //minecraft->gui.renderSlotText(item, xx + 3, yy + 3, true, true); - } else { - minecraft->textures->loadAndBindTexture("gui/items.png"); - blit(x + 2, y, 15 * 16, slot * 16, 16, 16, 16, 16); - } -} - -void ArmorScreen::takeAndClearSlot( int slot ) { - ItemInstance* item = player->getArmor(slot); - if (!item) - return; - - int oldSize = minecraft->player->inventory->getNumEmptySlots(); - - if (!minecraft->player->inventory->add(item)) - minecraft->player->drop(new ItemInstance(*item), false); - - player->setArmor(slot, NULL); - - int newSize = minecraft->player->inventory->getNumEmptySlots(); - setIfNotSet(doRecreatePane, newSize != oldSize); -} - -void ArmorScreen::renderPlayer(float xo, float yo) { - // Push GL and player state - glPushMatrix(); - - glTranslatef(xo, yo, -200); - float ss = 45; - glScalef(-ss, ss, ss); - - glRotatef(180, 0, 0, 1); - //glDisable(GL_DEPTH_TEST); - - Player* player = (Player*) minecraft->player; - float oybr = player->yBodyRot; - float oyr = player->yRot; - float oxr = player->xRot; - - float t = getTimeS(); - - float xd = 10 * Mth::sin(t);//(xo + 51) - xm; - float yd = 10 * Mth::cos(t * 0.05f);//(yo + 75 - 50) - ym; - - glRotatef(45 + 90, 0, 1, 0); - glRotatef(-45 - 90, 0, 1, 0); - - const float xtan = Mth::atan(xd / 40.0f) * +20; - const float ytan = Mth::atan(yd / 40.0f) * -20; - - glRotatef(ytan, 1, 0, 0); - - player->yBodyRot = xtan; - player->yRot = xtan + xtan; - player->xRot = ytan; - glTranslatef(0, player->heightOffset, 0); - - // Push walking anim - float oldWAP = player->walkAnimPos; - float oldWAS = player->walkAnimSpeed; - float oldWASO = player->walkAnimSpeedO; - - // Set new walking anim - player->walkAnimSpeedO = player->walkAnimSpeed = 0.25f; - player->walkAnimPos = getTimeS() * player->walkAnimSpeed * SharedConstants::TicksPerSecond; - - EntityRenderDispatcher* rd = EntityRenderDispatcher::getInstance(); - rd->playerRotY = 180; - rd->render(player, 0, 0, 0, 0, 1); - - // Pop walking anim - player->walkAnimPos = oldWAP; - player->walkAnimSpeed = oldWAS; - player->walkAnimSpeedO = oldWASO; - - //glEnable(GL_DEPTH_TEST); - // Pop GL and player state - player->yBodyRot = oybr; - player->yRot = oyr; - player->xRot = oxr; - - glPopMatrix(); -} +#include "ArmorScreen.h" +#include "../Screen.h" +#include "../components/NinePatch.h" +#include "../../Minecraft.h" +#include "../../player/LocalPlayer.h" +#include "../../renderer/Tesselator.h" +#include "../../renderer/entity/ItemRenderer.h" +#include "../../../world/item/Item.h" +#include "../../../world/item/ItemCategory.h" +#include "../../../world/entity/player/Inventory.h" +#include "../../../world/entity/item/ItemEntity.h" +#include "../../../world/level/Level.h" +#include "../../../network/RakNetInstance.h" +#include "../../renderer/entity/EntityRenderDispatcher.h" +#include "../../../world/item/ArmorItem.h" + +static void setIfNotSet(bool& ref, bool condition) { + ref = (ref || condition); +} + +const int descFrameWidth = 100; + +const int rgbActive = 0xfff0f0f0; +const int rgbInactive = 0xc0635558; +const int rgbInactiveShadow = 0xc0aaaaaa; + +#ifdef __APPLE__ + static const float BorderPixels = 4; + #ifdef DEMO_MODE + static const float BlockPixels = 22; + #else + static const float BlockPixels = 22; + #endif +#else + static const float BorderPixels = 4; + static const float BlockPixels = 24; +#endif +static const int ItemSize = (int)(BlockPixels + 2*BorderPixels); + +static const int Bx = 10; // Border Frame width +static const int By = 6; // Border Frame height + + +ArmorScreen::ArmorScreen(): + inventoryPane(NULL), + btnArmor0(0), + btnArmor1(1), + btnArmor2(2), + btnArmor3(3), + btnClose(4, ""), + bHeader (5, "Armor"), + guiBackground(NULL), + guiSlot(NULL), + guiPaneFrame(NULL), + guiPlayerBg(NULL), + doRecreatePane(false), + descWidth(90) + //guiSlotItem(NULL), + //guiSlotItemSelected(NULL) +{ + //LOGI("Creating ArmorScreen with %p, %d\n", furnace, furnace->runningId); +} + +ArmorScreen::~ArmorScreen() { + delete inventoryPane; + + delete guiBackground; + delete guiSlot; + delete guiPaneFrame; + delete guiPlayerBg; +} + +void ArmorScreen::init() { + super::init(); + + player = minecraft->player; + + ImageDef def; + def.name = "gui/spritesheet.png"; + def.x = 0; + def.y = 1; + def.width = def.height = 18; + def.setSrc(IntRectangle(60, 0, 18, 18)); + btnClose.setImageDef(def, true); + btnClose.scaleWhenPressed = false; + + buttons.push_back(&bHeader); + buttons.push_back(&btnClose); + + armorButtons[0] = &btnArmor0; + armorButtons[1] = &btnArmor1; + armorButtons[2] = &btnArmor2; + armorButtons[3] = &btnArmor3; + for (int i = 0; i < NUM_ARMORBUTTONS; ++i) + buttons.push_back(armorButtons[i]); + + // GUI - nine patches + NinePatchFactory builder(minecraft->textures, "gui/spritesheet.png"); + + guiBackground = builder.createSymmetrical(IntRectangle(0, 0, 16, 16), 4, 4); + guiSlot = builder.createSymmetrical(IntRectangle(0, 32, 8, 8), 3, 3, 20, 20); + guiPaneFrame = builder.createSymmetrical(IntRectangle(28, 42, 4, 4), 1, 1)->setExcluded(1 << 4); + guiPlayerBg = builder.createSymmetrical(IntRectangle(0, 20, 8, 8), 3, 3); + + updateItems(); +} + +void ArmorScreen::setupPositions() { + // Left - Categories + bHeader.x = bHeader.y = 0; + bHeader.width = width;// - bDone.w; + + btnClose.width = btnClose.height = 19; + btnClose.x = width - btnClose.width; + btnClose.y = 0; + + // Inventory pane + const int maxWidth = (int)(width/1.8f) - Bx - Bx; + const int InventoryColumns = maxWidth / ItemSize; + const int realWidth = InventoryColumns * ItemSize; + const int paneWidth = realWidth + Bx + Bx; + const int realBx = (paneWidth - realWidth) / 2; + + inventoryPaneRect = IntRectangle(realBx, +#ifdef __APPLE__ + 26 + By - ((width==240)?1:0), realWidth, ((width==240)?1:0) + height-By-By-28); +#else + 26 + By, realWidth, height-By-By-28); +#endif + + for (int i = 0; i < NUM_ARMORBUTTONS; ++i) { + Button& b = *armorButtons[i]; + b.x = paneWidth; + b.y = inventoryPaneRect.y + 24 * i; + b.width = 20; + b.height = 20; + } + + guiPlayerBgRect.y = inventoryPaneRect.y; + int xx = armorButtons[0]->x + armorButtons[0]->width; + int xw = width - xx; + guiPlayerBgRect.x = xx + xw / 10; + guiPlayerBgRect.w = xw - (xw / 10) * 2; + guiPlayerBgRect.h = inventoryPaneRect.h; + + guiPaneFrame->setSize((float)inventoryPaneRect.w + 2, (float)inventoryPaneRect.h + 2); + guiPlayerBg->setSize((float)guiPlayerBgRect.w, (float)guiPlayerBgRect.h); + guiBackground->setSize((float)width, (float)height); + + updateItems(); + setupInventoryPane(); +} + +void ArmorScreen::tick() { + if (inventoryPane) + inventoryPane->tick(); + + if (doRecreatePane) { + updateItems(); + setupInventoryPane(); + doRecreatePane = false; + } +} + +void ArmorScreen::handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a) { + if (pane) { + pane->render(xm, ym, a); + guiPaneFrame->draw(t, (float)(pane->rect.x - 1), (float)(pane->rect.y - 1)); + } +} + +void ArmorScreen::render(int xm, int ym, float a) { + //renderBackground(); + + Tesselator& t = Tesselator::instance; + + t.addOffset(0, 0, -500); + guiBackground->draw(t, 0, 0); + t.addOffset(0, 0, 500); + glEnable2(GL_ALPHA_TEST); + + // Buttons (Left side + crafting) + super::render(xm, ym, a); + + handleRenderPane(inventoryPane, t, xm, ym, a); + + t.colorABGR(0xffffffff); + glColor4f2(1, 1, 1, 1); + + t.addOffset(0, 0, -490); + guiPlayerBg->draw(t, (float)guiPlayerBgRect.x, (float)guiPlayerBgRect.y); + t.addOffset(0, 0, 490); + renderPlayer((float)(guiPlayerBgRect.x + guiPlayerBgRect.w / 2), 0.85f * height); + + for (int i = 0; i < NUM_ARMORBUTTONS; ++i) { + drawSlotItemAt(t, i, player->getArmor(i), armorButtons[i]->x, armorButtons[i]->y); + } + glDisable2(GL_ALPHA_TEST); +} + +void ArmorScreen::buttonClicked(Button* button) { + if (button == &btnClose) { + minecraft->setScreen(NULL); + } + + if (button->id >= 0 && button->id <= 3) { + takeAndClearSlot(button->id); + } +} + +bool ArmorScreen::addItem(const Touch::InventoryPane* forPane, int itemIndex) { + const ItemInstance* instance = armorItems[itemIndex]; + if (!ItemInstance::isArmorItem(instance)) + return false; + + ArmorItem* item = (ArmorItem*) instance->getItem(); + ItemInstance* old = player->getArmor(item->slot); + ItemInstance oldArmor; + + if (ItemInstance::isArmorItem(old)) { + oldArmor = *old; + } + + player->setArmor(item->slot, instance); + + player->inventory->removeItem(instance); + //@attn: this is hugely important + armorItems[itemIndex] = NULL; + + if (!oldArmor.isNull()) { + if (!player->inventory->add(&oldArmor)) { + player->drop(new ItemInstance(oldArmor), false); + } + } + + doRecreatePane = true; + return true; +} + +bool ArmorScreen::isAllowed( int slot ) { + return true; +} + +bool ArmorScreen::renderGameBehind() { + return false; +} + +std::vector ArmorScreen::getItems( const Touch::InventoryPane* forPane ) { + return armorItems; +} + +void ArmorScreen::updateItems() { + armorItems.clear(); + + for (int i = Inventory::MAX_SELECTION_SIZE; i < minecraft->player->inventory->getContainerSize(); ++i) { + ItemInstance* item = minecraft->player->inventory->getItem(i); + if (ItemInstance::isArmorItem(item)) + armorItems.push_back(item); + } +} + +bool ArmorScreen::canMoveToSlot(int slot, const ItemInstance* item) { + return ItemInstance::isArmorItem(item) + && ((ArmorItem*)item)->slot == slot; +} + +void ArmorScreen::setupInventoryPane() { + // IntRectangle(0, 0, 100, 100) + if (inventoryPane) delete inventoryPane; + inventoryPane = new Touch::InventoryPane(this, minecraft, inventoryPaneRect, inventoryPaneRect.w, BorderPixels, armorItems.size(), ItemSize, (int)BorderPixels); + inventoryPane->fillMarginX = 0; + inventoryPane->fillMarginY = 0; + //LOGI("Creating new pane: %d %p\n", inventoryItems.size(), inventoryPane); +} + +void ArmorScreen::drawSlotItemAt( Tesselator& t, int slot, const ItemInstance* item, int x, int y) +{ + float xx = (float)x; + float yy = (float)y; + + guiSlot->draw(t, xx, yy); + + if (item && !item->isNull()) { + ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, xx + 2, yy, true); + glDisable2(GL_TEXTURE_2D); + ItemRenderer::renderGuiItemDecorations(item, xx + 2, yy + 3); + glEnable2(GL_TEXTURE_2D); + //minecraft->gui.renderSlotText(item, xx + 3, yy + 3, true, true); + } else { + minecraft->textures->loadAndBindTexture("gui/items.png"); + blit(x + 2, y, 15 * 16, slot * 16, 16, 16, 16, 16); + } +} + +void ArmorScreen::takeAndClearSlot( int slot ) { + ItemInstance* item = player->getArmor(slot); + if (!item) + return; + + int oldSize = minecraft->player->inventory->getNumEmptySlots(); + + if (!minecraft->player->inventory->add(item)) + minecraft->player->drop(new ItemInstance(*item), false); + + player->setArmor(slot, NULL); + + int newSize = minecraft->player->inventory->getNumEmptySlots(); + setIfNotSet(doRecreatePane, newSize != oldSize); +} + +void ArmorScreen::renderPlayer(float xo, float yo) { + // Push GL and player state + glPushMatrix(); + + glTranslatef(xo, yo, -200); + float ss = 45; + glScalef(-ss, ss, ss); + + glRotatef(180, 0, 0, 1); + //glDisable(GL_DEPTH_TEST); + + Player* player = (Player*) minecraft->player; + float oybr = player->yBodyRot; + float oyr = player->yRot; + float oxr = player->xRot; + + float t = getTimeS(); + + float xd = 10 * Mth::sin(t);//(xo + 51) - xm; + float yd = 10 * Mth::cos(t * 0.05f);//(yo + 75 - 50) - ym; + + glRotatef(45 + 90, 0, 1, 0); + glRotatef(-45 - 90, 0, 1, 0); + + const float xtan = Mth::atan(xd / 40.0f) * +20; + const float ytan = Mth::atan(yd / 40.0f) * -20; + + glRotatef(ytan, 1, 0, 0); + + player->yBodyRot = xtan; + player->yRot = xtan + xtan; + player->xRot = ytan; + glTranslatef(0, player->heightOffset, 0); + + // Push walking anim + float oldWAP = player->walkAnimPos; + float oldWAS = player->walkAnimSpeed; + float oldWASO = player->walkAnimSpeedO; + + // Set new walking anim + player->walkAnimSpeedO = player->walkAnimSpeed = 0.25f; + player->walkAnimPos = getTimeS() * player->walkAnimSpeed * SharedConstants::TicksPerSecond; + + EntityRenderDispatcher* rd = EntityRenderDispatcher::getInstance(); + rd->playerRotY = 180; + rd->render(player, 0, 0, 0, 0, 1); + + // Pop walking anim + player->walkAnimPos = oldWAP; + player->walkAnimSpeed = oldWAS; + player->walkAnimSpeedO = oldWASO; + + //glEnable(GL_DEPTH_TEST); + // Pop GL and player state + player->yBodyRot = oybr; + player->yRot = oyr; + player->xRot = oxr; + + glPopMatrix(); +} diff --git a/src/client/gui/screens/ArmorScreen.h b/src/client/gui/screens/ArmorScreen.h index 46c1d1f..a402338 100755 --- a/src/client/gui/screens/ArmorScreen.h +++ b/src/client/gui/screens/ArmorScreen.h @@ -1,79 +1,79 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ArmorScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ArmorScreen_H__ - -#include "BaseContainerScreen.h" - -#include "../components/InventoryPane.h" -#include "../components/Button.h" - -class Font; -class CItem; -class Textures; -class NinePatchLayer; -class Tesselator; - -class ArmorScreen: public Screen, - public Touch::IInventoryPaneCallback -{ - typedef Screen super; - typedef std::vector ItemList; - - static const int NUM_ARMORBUTTONS = 4; -public: - ArmorScreen(); - ~ArmorScreen(); - - void init(); - void setupPositions(); - - void tick(); - void render(int xm, int ym, float a); - bool renderGameBehind(); - void buttonClicked(Button* button); - - // IInventoryPaneCallback - bool addItem(const Touch::InventoryPane* pane, int itemId); - bool isAllowed( int slot ); - std::vector getItems( const Touch::InventoryPane* forPane ); -private: - void renderPlayer(float xo, float yo); - - void setupInventoryPane(); - void updateItems(); - - void drawSlotItemAt(Tesselator& t, int slot, const ItemInstance* item, int x, int y); - ItemInstance moveOver(const ItemInstance* item, int maxCount); - void takeAndClearSlot( int slot ); - void handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a); - bool canMoveToSlot(int slot, const ItemInstance* item); - ItemList _items; - - std::string currentItemDesc; - ItemInstance burnResult; - float descWidth; - ImageButton btnClose; - - BlankButton btnArmor0; - BlankButton btnArmor1; - BlankButton btnArmor2; - BlankButton btnArmor3; - BlankButton* armorButtons[4]; - - Touch::THeader bHeader; - - Touch::InventoryPane* inventoryPane; - IntRectangle inventoryPaneRect; - IntRectangle guiPlayerBgRect; - - std::vector armorItems; - bool doRecreatePane; - - // GUI elements such as 9-Patches - NinePatchLayer* guiBackground; - NinePatchLayer* guiSlot; - NinePatchLayer* guiPaneFrame; - NinePatchLayer* guiPlayerBg; - Player* player; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ArmorScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ArmorScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ArmorScreen_H__ + +#include "BaseContainerScreen.h" + +#include "../components/InventoryPane.h" +#include "../components/Button.h" + +class Font; +class CItem; +class Textures; +class NinePatchLayer; +class Tesselator; + +class ArmorScreen: public Screen, + public Touch::IInventoryPaneCallback +{ + typedef Screen super; + typedef std::vector ItemList; + + static const int NUM_ARMORBUTTONS = 4; +public: + ArmorScreen(); + ~ArmorScreen(); + + void init(); + void setupPositions(); + + void tick(); + void render(int xm, int ym, float a); + bool renderGameBehind(); + void buttonClicked(Button* button); + + // IInventoryPaneCallback + bool addItem(const Touch::InventoryPane* pane, int itemId); + bool isAllowed( int slot ); + std::vector getItems( const Touch::InventoryPane* forPane ); +private: + void renderPlayer(float xo, float yo); + + void setupInventoryPane(); + void updateItems(); + + void drawSlotItemAt(Tesselator& t, int slot, const ItemInstance* item, int x, int y); + ItemInstance moveOver(const ItemInstance* item, int maxCount); + void takeAndClearSlot( int slot ); + void handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a); + bool canMoveToSlot(int slot, const ItemInstance* item); + ItemList _items; + + std::string currentItemDesc; + ItemInstance burnResult; + float descWidth; + ImageButton btnClose; + + BlankButton btnArmor0; + BlankButton btnArmor1; + BlankButton btnArmor2; + BlankButton btnArmor3; + BlankButton* armorButtons[4]; + + Touch::THeader bHeader; + + Touch::InventoryPane* inventoryPane; + IntRectangle inventoryPaneRect; + IntRectangle guiPlayerBgRect; + + std::vector armorItems; + bool doRecreatePane; + + // GUI elements such as 9-Patches + NinePatchLayer* guiBackground; + NinePatchLayer* guiSlot; + NinePatchLayer* guiPaneFrame; + NinePatchLayer* guiPlayerBg; + Player* player; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ArmorScreen_H__*/ diff --git a/src/client/gui/screens/BaseContainerScreen.h b/src/client/gui/screens/BaseContainerScreen.h index 55ce1e0..da0714a 100755 --- a/src/client/gui/screens/BaseContainerScreen.h +++ b/src/client/gui/screens/BaseContainerScreen.h @@ -1,50 +1,50 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__BaseContainerScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI__BaseContainerScreen_H__ - -//package net.minecraft.client.gui.screens; - -#include -#include "../Screen.h" -#include "../../Minecraft.h" -#include "../../player/LocalPlayer.h" - -class BaseContainerMenu; - -class BaseContainerScreen: public Screen -{ - typedef Screen super; -public: - BaseContainerScreen(BaseContainerMenu* menu) - : menu(menu) - { - } - - virtual void init() { - super::init(); - minecraft->player->containerMenu = menu; - } - - virtual void tick() { - super::tick(); - if (!minecraft->player->isAlive() || minecraft->player->removed) - minecraft->player->closeContainer(); - } - - virtual void keyPressed( int eventKey ) - { - if (eventKey == Keyboard::KEY_ESCAPE) { - minecraft->player->closeContainer(); - } else { - super::keyPressed(eventKey); - } - } - - virtual bool closeOnPlayerHurt() { - return true; - } - -protected: - BaseContainerMenu* menu; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI__BaseContainerScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__BaseContainerScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI__BaseContainerScreen_H__ + +//package net.minecraft.client.gui.screens; + +#include +#include "../Screen.h" +#include "../../Minecraft.h" +#include "../../player/LocalPlayer.h" + +class BaseContainerMenu; + +class BaseContainerScreen: public Screen +{ + typedef Screen super; +public: + BaseContainerScreen(BaseContainerMenu* menu) + : menu(menu) + { + } + + virtual void init() { + super::init(); + minecraft->player->containerMenu = menu; + } + + virtual void tick() { + super::tick(); + if (!minecraft->player->isAlive() || minecraft->player->removed) + minecraft->player->closeContainer(); + } + + virtual void keyPressed( int eventKey ) + { + if (eventKey == Keyboard::KEY_ESCAPE) { + minecraft->player->closeContainer(); + } else { + super::keyPressed(eventKey); + } + } + + virtual bool closeOnPlayerHurt() { + return true; + } + +protected: + BaseContainerMenu* menu; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI__BaseContainerScreen_H__*/ diff --git a/src/client/gui/screens/BuyGameScreen.h b/src/client/gui/screens/BuyGameScreen.h index 228cdba..ed1354d 100644 --- a/src/client/gui/screens/BuyGameScreen.h +++ b/src/client/gui/screens/BuyGameScreen.h @@ -1,26 +1,26 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__BuyGameScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__BuyGameScreen_H__ - -#include "../Screen.h" -#include "../components/Button.h" - -class BuyGameScreen: public Screen -{ -public: - BuyGameScreen() {} - virtual ~BuyGameScreen() {} - - void init(); - - void render(int xm, int ym, float a); - - void buttonClicked(Button* button) { - //if (button->id == bQuit.id) - } - -private: - //Button bQuit; - //Button bBuyGame; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__BuyGameScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__BuyGameScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__BuyGameScreen_H__ + +#include "../Screen.h" +#include "../components/Button.h" + +class BuyGameScreen: public Screen +{ +public: + BuyGameScreen() {} + virtual ~BuyGameScreen() {} + + void init(); + + void render(int xm, int ym, float a); + + void buttonClicked(Button* button) { + //if (button->id == bQuit.id) + } + +private: + //Button bQuit; + //Button bBuyGame; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__BuyGameScreen_H__*/ diff --git a/src/client/gui/screens/ChatScreen.cpp b/src/client/gui/screens/ChatScreen.cpp index 925444f..b2a6095 100755 --- a/src/client/gui/screens/ChatScreen.cpp +++ b/src/client/gui/screens/ChatScreen.cpp @@ -1,24 +1,24 @@ -#include "ChatScreen.h" -#include "DialogDefinitions.h" -#include "../Gui.h" -#include "../../Minecraft.h" -#include "../../../AppPlatform.h" -#include "../../../platform/log.h" - -void ChatScreen::init() { - minecraft->platform()->createUserInput(DialogDefinitions::DIALOG_NEW_CHAT_MESSAGE); -} - -void ChatScreen::render(int xm, int ym, float a) -{ - int status = minecraft->platform()->getUserInputStatus(); - if (status > -1) { - if (status == 1) { - std::vector v = minecraft->platform()->getUserInput(); - if (v.size() && v[0].length() > 0) - minecraft->gui.addMessage(v[0]); - } - - minecraft->setScreen(NULL); - } -} +#include "ChatScreen.h" +#include "DialogDefinitions.h" +#include "../Gui.h" +#include "../../Minecraft.h" +#include "../../../AppPlatform.h" +#include "../../../platform/log.h" + +void ChatScreen::init() { + minecraft->platform()->createUserInput(DialogDefinitions::DIALOG_NEW_CHAT_MESSAGE); +} + +void ChatScreen::render(int xm, int ym, float a) +{ + int status = minecraft->platform()->getUserInputStatus(); + if (status > -1) { + if (status == 1) { + std::vector v = minecraft->platform()->getUserInput(); + if (v.size() && v[0].length() > 0) + minecraft->gui.addMessage(v[0]); + } + + minecraft->setScreen(NULL); + } +} diff --git a/src/client/gui/screens/ChatScreen.h b/src/client/gui/screens/ChatScreen.h index 0d6444b..2c98184 100755 --- a/src/client/gui/screens/ChatScreen.h +++ b/src/client/gui/screens/ChatScreen.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ChatScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ChatScreen_H__ - -#include "../Screen.h" - -class ChatScreen: public Screen -{ -public: - ChatScreen() {} - virtual ~ChatScreen() {} - - void init(); - - void render(int xm, int ym, float a); - - void buttonClicked(Button* button) {}; - -private: -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ChatScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ChatScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ChatScreen_H__ + +#include "../Screen.h" + +class ChatScreen: public Screen +{ +public: + ChatScreen() {} + virtual ~ChatScreen() {} + + void init(); + + void render(int xm, int ym, float a); + + void buttonClicked(Button* button) {}; + +private: +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ChatScreen_H__*/ diff --git a/src/client/gui/screens/ChestScreen.cpp b/src/client/gui/screens/ChestScreen.cpp index c9383c9..a397f8b 100755 --- a/src/client/gui/screens/ChestScreen.cpp +++ b/src/client/gui/screens/ChestScreen.cpp @@ -1,474 +1,474 @@ -#include "ChestScreen.h" -#include "touch/TouchStartMenuScreen.h" -#include "../Screen.h" -#include "../components/NinePatch.h" -#include "../../Minecraft.h" -#include "../../player/LocalPlayer.h" -#include "../../renderer/Tesselator.h" -#include "../../renderer/entity/ItemRenderer.h" -#include "../../../world/item/Item.h" -#include "../../../world/item/ItemCategory.h" -#include "../../../world/entity/player/Inventory.h" -#include "../../../world/entity/item/ItemEntity.h" -#include "../../../world/level/Level.h" -#include "../../../locale/I18n.h" -#include "../../../util/StringUtils.h" -#include "../../../network/packet/ContainerSetSlotPacket.h" -#include "../../../network/RakNetInstance.h" -#include "../../../world/level/tile/entity/TileEntity.h" -#include "../../../world/level/tile/entity/ChestTileEntity.h" -#include "../../../world/inventory/ContainerMenu.h" -#include "../../../util/Mth.h" - -//static NinePatchLayer* guiPaneFrame = NULL; - -static __inline void setIfNotSet(bool& ref, bool condition) { - ref = (ref || condition); -} - -template -T* upcast(V* x) { return x; } - -static int heldMs = -1; -static int percent = -1; -static const float MaxHoldMs = 500.0f; -static const int MinChargeMs = 200; - -class ItemDiffer { -public: - ItemDiffer(int size) - : size(size), - count(0) - { - base = new ItemInstance[size]; - } - ItemDiffer(const std::vector& v) - : size(v.size()), - count(0) - { - base = new ItemInstance[size]; - init(v); - } - - ~ItemDiffer() { - delete[] base; - } - - void init(const std::vector& v) { - for (int i = 0; i < size; ++i) { - if (v[i]) base[i] = *v[i]; - else base[i].setNull(); - } - } - - int getDiff(const std::vector& v, std::vector& outIndices) { - int diffLen = v.size() - size; - int minLen = Mth::Max((int)v.size(), size); - for (int i = 0; i < minLen; ++i) { - //LOGI("%s, %s\n", base[i].toString().c_str(), v[i]?v[i]->toString().c_str() : "null"); - if (!ItemInstance::matchesNulls(&base[i], v[i])) - outIndices.push_back(i); - } - return diffLen; - } - -private: - int size; - int count; - ItemInstance* base; -}; - -const int descFrameWidth = 100; - -const int rgbActive = 0xfff0f0f0; -const int rgbInactive = 0xc0635558; -const int rgbInactiveShadow = 0xc0aaaaaa; - -#ifdef __APPLE__ - static const float BorderPixels = 3; - #ifdef DEMO_MODE - static const float BlockPixels = 22; - #else - static const float BlockPixels = 22; - #endif -#else - static const float BorderPixels = 4; - static const float BlockPixels = 24; -#endif -static const int ItemSize = (int)(BlockPixels + 2*BorderPixels); - -static const int Bx = 10; // Border Frame width -static const int By = 6; // Border Frame height - -typedef struct FlyingItem { - ItemInstance item; - float startTime; - float sx, sy; - float dx, dy; -} FlyingItem ; - -static std::vector flyingItems; - -ChestScreen::ChestScreen(Player* player, ChestTileEntity* chest) -: super(new ContainerMenu(chest, chest->runningId)), //@huge @attn - inventoryPane(NULL), - chestPane(NULL), - btnClose(4, ""), - bHeader (5, "Inventory"), - bHeaderChest (6, "Chest"), - guiBackground(NULL), - guiSlot(NULL), - guiSlotMarked(NULL), - guiSlotMarker(NULL), - player(player), - chest(chest), - selectedSlot(-1), - doRecreatePane(false) - //guiSlotItem(NULL), - //guiSlotItemSelected(NULL) -{ -} - -ChestScreen::~ChestScreen() { - delete inventoryPane; - delete chestPane; - - delete guiBackground; - delete guiSlot; - delete guiSlotMarked; - delete guiSlotMarker; - delete guiPaneFrame; - - delete menu; - - if (chest->clientSideOnly) - delete chest; -} - -void ChestScreen::init() { - super::init(); - //printf("-> %d\n", width/2); - - ImageDef def; - def.name = "gui/spritesheet.png"; - def.x = 0; - def.y = 1; - def.width = def.height = 18; - def.setSrc(IntRectangle(60, 0, 18, 18)); - btnClose.setImageDef(def, true); - btnClose.scaleWhenPressed = false; - - buttons.push_back(&bHeader); - buttons.push_back(&bHeaderChest); - buttons.push_back(&btnClose); - - // GUI - nine patches - NinePatchFactory builder(minecraft->textures, "gui/spritesheet.png"); - - guiBackground = builder.createSymmetrical(IntRectangle(0, 0, 16, 16), 4, 4); - guiSlot = builder.createSymmetrical(IntRectangle(0, 32, 8, 8), 3, 3); - guiSlotMarked = builder.createSymmetrical(IntRectangle(0, 44, 8, 8), 3, 3); - guiSlotMarker = builder.createSymmetrical(IntRectangle(10, 42, 16, 16), 5, 5); - guiPaneFrame = builder.createSymmetrical(IntRectangle(28, 42, 4, 4), 1, 1)->exclude(4); -} - -void ChestScreen::setupPositions() { - // Left - Categories - bHeader.x = 0; - bHeader.y = bHeaderChest.y = 0; - bHeader.width = bHeaderChest.width = width / 2;// - bDone.w; - bHeaderChest.x = bHeader.x + bHeader.width; - - // Right - Description - btnClose.width = btnClose.height = 19; - btnClose.x = width - btnClose.width; - btnClose.y = 0; - - //guiPaneFrame->setSize((float)paneFuelRect.w + 2, (float)paneFuelRect.h + 4); - guiBackground->setSize((float)width, (float)height); - //guiSlotItem->setSize((float)width, 22); //@todo - //guiSlotItemSelected->setSize((float)width, 22); - - setupPane(); -} - -void ChestScreen::tick() { - if (inventoryPane) - inventoryPane->tick(); - - if (chestPane) - chestPane->tick(); - - if (doRecreatePane) { - setupPane(); - doRecreatePane = false; - } -} - -void ChestScreen::handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a) { - if (pane) { - int ms, id; - pane->markerIndex = -1; - if (pane->queryHoldTime(&id, &ms)) { - heldMs = ms; - - FillingContainer* c = (pane == inventoryPane)? - upcast(minecraft->player->inventory) - : upcast(chest); - - const int slotIndex = id + c->getNumLinkedSlots(); - ItemInstance* item = c->getItem(slotIndex); - int count = (item && !item->isNull())? item->count : 0; - float maxHoldMs = item? 700 + 10 * item->count: MaxHoldMs; - - if (count > 1) { - float share = (heldMs-MinChargeMs) / maxHoldMs; - pane->markerType = 1;//(heldMs >= MinChargeMs)? 1 : 0; - pane->markerIndex = id; - pane->markerShare = Mth::Max(share, 0.0f); - - percent = (int)Mth::clamp(100.0f * share, 0.0f, 100.0f); - if (percent >= 100) { - addItem(pane, id); - } - } - } - - pane->render(xm, ym, a); - guiPaneFrame->draw(t, (float)(pane->rect.x - 1), (float)(pane->rect.y - 1)); - //LOGI("query-iv: %d, %d\n", gridId, heldMs); - - } -} - -void ChestScreen::render(int xm, int ym, float a) { - const int N = 5; - static StopwatchNLast r(N); - //renderBackground(); - - Tesselator& t = Tesselator::instance; - guiBackground->draw(t, 0, 0); - glEnable2(GL_ALPHA_TEST); - - // Buttons (Left side + crafting) - super::render(xm, ym, a); - - heldMs = -1; - - handleRenderPane(inventoryPane, t, xm, ym, a); - handleRenderPane(chestPane, t, xm, ym, a); - - float now = getTimeS(); - float MaxTime = 0.3f; - std::vector flyingToSave; - - glEnable2(GL_BLEND); - glColor4f(1, 1, 1, 0.2f); - t.beginOverride(); - //t.color(1.0f, 0.0f, 0.0f, 0.2f); - //t.noColor(); - - glEnable2(GL_SCISSOR_TEST); - //LOGI("panesBox: %d, %d - %d, %d\n", panesBbox.x, panesBbox.y, panesBbox.w, panesBbox.h); - minecraft->gui.setScissorRect(panesBbox); - for (unsigned int i = 0; i < flyingItems.size(); ++i) { - FlyingItem& fi = flyingItems[i]; - float since = (now - fi.startTime); - if (since > MaxTime) continue; - float t = since / MaxTime; - t *= t; - //float xx = fi.sx + t * 100.0f; - - float xx = Mth::lerp(fi.sx, fi.dx, t); - float yy = Mth::lerp(fi.sy, fi.dy, t); - ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, &fi.item, xx + 7, yy + 8, true); - //minecraft->gui.renderSlotText(&fi.item, xx + 3, yy + 3, true, true); - - flyingToSave.push_back(fi); - } - t.enableColor(); - t.endOverrideAndDraw(); - glDisable2(GL_SCISSOR_TEST); - - flyingItems = flyingToSave; - - t.colorABGR(0xffffffff); - glDisable2(GL_BLEND); - - minecraft->textures->loadAndBindTexture("gui/spritesheet.png"); -} - -void ChestScreen::buttonClicked(Button* button) { - if (button == &btnClose) { - minecraft->player->closeContainer(); - } -} - -bool ChestScreen::handleAddItem(FillingContainer* from, FillingContainer* to, int itemIndex) { - const int itemOffset = from->getNumLinkedSlots(); - const int slotIndex = itemIndex + itemOffset; - ItemInstance* item = from->getItem(slotIndex); - - bool added = false; - bool fromChest = (from == chest); - Touch::InventoryPane* pane = fromChest? chestPane : inventoryPane; - Touch::InventoryPane* toPane = fromChest? inventoryPane : chestPane; - - int wantedCount = (item && !item->isNull())? item->count * percent / 100 : 0; - if ((item && !item->isNull()) && (!wantedCount || heldMs < MinChargeMs)) { - wantedCount = 1; - } - - if (wantedCount > 0) { - ItemInstance takenItem(*item); - takenItem.count = wantedCount; - - ItemDiffer differ(getItems(toPane)); - to->add(&takenItem); - - added = (takenItem.count != wantedCount); - - if (added) { - item->count -= (wantedCount - takenItem.count); - std::vector changed; - std::vector items = getItems(toPane); - differ.getDiff(items, changed); - - ScrollingPane::GridItem g, gTo; - pane->getGridItemFor_slow(itemIndex, g); - - //LOGI("Changed: %d\n", changed.size()); - for (unsigned int i = 0; i < changed.size(); ++i) { - FlyingItem fi; - fi.startTime = getTimeS(); - fi.item = *item; - - fi.sx = g.xf; - fi.sy = g.yf; - - int toIndex = changed[i]; - toPane->getGridItemFor_slow(toIndex, gTo); - - fi.dx = gTo.xf; - fi.dy = gTo.yf; - flyingItems.push_back(fi); - - if (!fromChest && minecraft->level->isClientSide) { - int j = toIndex; - ItemInstance item = items[j]? *items[j] : ItemInstance(); - ContainerSetSlotPacket p(menu->containerId, j, item); - minecraft->raknetInstance->send(p); - } - } - } - - // Send to server, needs a bit special handling - if (fromChest) { - ItemInstance ins(item->count <= 0? ItemInstance() : *item); - ContainerSetSlotPacket p(menu->containerId, slotIndex, ins); - minecraft->raknetInstance->send(p); - } - if (item->count <= 0) - from->clearSlot(slotIndex); - } - // Clear the marker indices - pane->markerIndex = toPane->markerIndex = -1; - - return added; -} - -bool ChestScreen::addItem(const Touch::InventoryPane* forPane, int itemIndex) { - //LOGI("items.size, index: %d, %d\n", inventoryItems.size(), itemIndex); - bool l2r = (forPane == inventoryPane); - return handleAddItem( l2r? upcast(minecraft->player->inventory) : upcast(chest), - l2r? upcast(chest) : upcast(minecraft->player->inventory), - itemIndex); -} - -bool ChestScreen::isAllowed( int slot ) -{ - return true; -} - -bool ChestScreen::renderGameBehind() -{ - return false; -} - -std::vector ChestScreen::getItems( const Touch::InventoryPane* forPane ) -{ - if (forPane == inventoryPane) { - for (int i = Inventory::MAX_SELECTION_SIZE, j = 0; i < minecraft->player->inventory->getContainerSize(); ++i, ++j) - inventoryItems[j] = minecraft->player->inventory->getItem(i); - return inventoryItems; - } - else { - for (int i = 0; i < chest->getContainerSize(); ++i) - chestItems[i] = chest->getItem(i); - return chestItems; - } -} - - -void ChestScreen::setupPane() -{ - inventoryItems.clear(); - for (int i = Inventory::MAX_SELECTION_SIZE; i < minecraft->player->inventory->getContainerSize(); ++i) { - ItemInstance* item = minecraft->player->inventory->getItem(i); - /*if (!item || item->isNull()) continue;*/ - inventoryItems.push_back(item); - } - chestItems.clear(); - for (int i = 0; i < chest->getContainerSize(); ++i) { - ItemInstance* item = chest->getItem(i); - /*if (!item || item->isNull()) continue;*/ - chestItems.push_back(item); - } - - int maxWidth = width/2 - Bx/2;//- Bx - Bx/*- Bx*/; - int InventoryColumns = maxWidth / ItemSize; - const int realWidth = InventoryColumns * ItemSize; - int paneWidth = realWidth;// + Bx + Bx; - const int realBx = (width/2 - realWidth) / 2; - - IntRectangle rect(realBx, -#ifdef __APPLE__ - 24 + By - ((width==240)?1:0), realWidth, ((width==240)?1:0) + height-By-By-24); -#else - 24 + By, realWidth, height-By-By-24); -#endif - // IntRectangle(0, 0, 100, 100) - if (inventoryPane) delete inventoryPane; - inventoryPane = new Touch::InventoryPane(this, minecraft, rect, paneWidth, BorderPixels, minecraft->player->inventory->getContainerSize() - Inventory::MAX_SELECTION_SIZE, ItemSize, (int)BorderPixels); - inventoryPane->fillMarginX = 0; - inventoryPane->fillMarginY = 0; - guiPaneFrame->setSize((float)rect.w + 2, (float)rect.h + 2); - - panesBbox = rect; - rect.x += width/2;// - rect.w - Bx; - panesBbox.w += (rect.x - panesBbox.x); - - if (chestPane) delete chestPane; - chestPane = new Touch::InventoryPane(this, minecraft, rect, paneWidth, BorderPixels, chest->getContainerSize(), ItemSize, (int)BorderPixels); - chestPane->fillMarginX = 0; - chestPane->fillMarginY = 0; - LOGI("Creating new panes\n:" - " Inventory %d %p\n" - " Chest %d %p\n", (int)inventoryItems.size(), inventoryPane, (int)chestItems.size(), chestPane); -} - -void ChestScreen::drawSlotItemAt( Tesselator& t, const ItemInstance* item, int x, int y, bool selected) -{ - float xx = (float)x; - float yy = (float)y; - - (selected? guiSlot/*Marked*/ : guiSlot)->draw(t, xx, yy); - - if (selected) - guiSlotMarker->draw(t, xx - 2, yy - 2); - - if (item && !item->isNull()) { - ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, xx + 7, yy + 8, true); - minecraft->gui.renderSlotText(item, xx + 3, yy + 3, true, true); - } -} +#include "ChestScreen.h" +#include "touch/TouchStartMenuScreen.h" +#include "../Screen.h" +#include "../components/NinePatch.h" +#include "../../Minecraft.h" +#include "../../player/LocalPlayer.h" +#include "../../renderer/Tesselator.h" +#include "../../renderer/entity/ItemRenderer.h" +#include "../../../world/item/Item.h" +#include "../../../world/item/ItemCategory.h" +#include "../../../world/entity/player/Inventory.h" +#include "../../../world/entity/item/ItemEntity.h" +#include "../../../world/level/Level.h" +#include "../../../locale/I18n.h" +#include "../../../util/StringUtils.h" +#include "../../../network/packet/ContainerSetSlotPacket.h" +#include "../../../network/RakNetInstance.h" +#include "../../../world/level/tile/entity/TileEntity.h" +#include "../../../world/level/tile/entity/ChestTileEntity.h" +#include "../../../world/inventory/ContainerMenu.h" +#include "../../../util/Mth.h" + +//static NinePatchLayer* guiPaneFrame = NULL; + +static __inline void setIfNotSet(bool& ref, bool condition) { + ref = (ref || condition); +} + +template +T* upcast(V* x) { return x; } + +static int heldMs = -1; +static int percent = -1; +static const float MaxHoldMs = 500.0f; +static const int MinChargeMs = 200; + +class ItemDiffer { +public: + ItemDiffer(int size) + : size(size), + count(0) + { + base = new ItemInstance[size]; + } + ItemDiffer(const std::vector& v) + : size(v.size()), + count(0) + { + base = new ItemInstance[size]; + init(v); + } + + ~ItemDiffer() { + delete[] base; + } + + void init(const std::vector& v) { + for (int i = 0; i < size; ++i) { + if (v[i]) base[i] = *v[i]; + else base[i].setNull(); + } + } + + int getDiff(const std::vector& v, std::vector& outIndices) { + int diffLen = v.size() - size; + int minLen = Mth::Max((int)v.size(), size); + for (int i = 0; i < minLen; ++i) { + //LOGI("%s, %s\n", base[i].toString().c_str(), v[i]?v[i]->toString().c_str() : "null"); + if (!ItemInstance::matchesNulls(&base[i], v[i])) + outIndices.push_back(i); + } + return diffLen; + } + +private: + int size; + int count; + ItemInstance* base; +}; + +const int descFrameWidth = 100; + +const int rgbActive = 0xfff0f0f0; +const int rgbInactive = 0xc0635558; +const int rgbInactiveShadow = 0xc0aaaaaa; + +#ifdef __APPLE__ + static const float BorderPixels = 3; + #ifdef DEMO_MODE + static const float BlockPixels = 22; + #else + static const float BlockPixels = 22; + #endif +#else + static const float BorderPixels = 4; + static const float BlockPixels = 24; +#endif +static const int ItemSize = (int)(BlockPixels + 2*BorderPixels); + +static const int Bx = 10; // Border Frame width +static const int By = 6; // Border Frame height + +typedef struct FlyingItem { + ItemInstance item; + float startTime; + float sx, sy; + float dx, dy; +} FlyingItem ; + +static std::vector flyingItems; + +ChestScreen::ChestScreen(Player* player, ChestTileEntity* chest) +: super(new ContainerMenu(chest, chest->runningId)), //@huge @attn + inventoryPane(NULL), + chestPane(NULL), + btnClose(4, ""), + bHeader (5, "Inventory"), + bHeaderChest (6, "Chest"), + guiBackground(NULL), + guiSlot(NULL), + guiSlotMarked(NULL), + guiSlotMarker(NULL), + player(player), + chest(chest), + selectedSlot(-1), + doRecreatePane(false) + //guiSlotItem(NULL), + //guiSlotItemSelected(NULL) +{ +} + +ChestScreen::~ChestScreen() { + delete inventoryPane; + delete chestPane; + + delete guiBackground; + delete guiSlot; + delete guiSlotMarked; + delete guiSlotMarker; + delete guiPaneFrame; + + delete menu; + + if (chest->clientSideOnly) + delete chest; +} + +void ChestScreen::init() { + super::init(); + //printf("-> %d\n", width/2); + + ImageDef def; + def.name = "gui/spritesheet.png"; + def.x = 0; + def.y = 1; + def.width = def.height = 18; + def.setSrc(IntRectangle(60, 0, 18, 18)); + btnClose.setImageDef(def, true); + btnClose.scaleWhenPressed = false; + + buttons.push_back(&bHeader); + buttons.push_back(&bHeaderChest); + buttons.push_back(&btnClose); + + // GUI - nine patches + NinePatchFactory builder(minecraft->textures, "gui/spritesheet.png"); + + guiBackground = builder.createSymmetrical(IntRectangle(0, 0, 16, 16), 4, 4); + guiSlot = builder.createSymmetrical(IntRectangle(0, 32, 8, 8), 3, 3); + guiSlotMarked = builder.createSymmetrical(IntRectangle(0, 44, 8, 8), 3, 3); + guiSlotMarker = builder.createSymmetrical(IntRectangle(10, 42, 16, 16), 5, 5); + guiPaneFrame = builder.createSymmetrical(IntRectangle(28, 42, 4, 4), 1, 1)->exclude(4); +} + +void ChestScreen::setupPositions() { + // Left - Categories + bHeader.x = 0; + bHeader.y = bHeaderChest.y = 0; + bHeader.width = bHeaderChest.width = width / 2;// - bDone.w; + bHeaderChest.x = bHeader.x + bHeader.width; + + // Right - Description + btnClose.width = btnClose.height = 19; + btnClose.x = width - btnClose.width; + btnClose.y = 0; + + //guiPaneFrame->setSize((float)paneFuelRect.w + 2, (float)paneFuelRect.h + 4); + guiBackground->setSize((float)width, (float)height); + //guiSlotItem->setSize((float)width, 22); //@todo + //guiSlotItemSelected->setSize((float)width, 22); + + setupPane(); +} + +void ChestScreen::tick() { + if (inventoryPane) + inventoryPane->tick(); + + if (chestPane) + chestPane->tick(); + + if (doRecreatePane) { + setupPane(); + doRecreatePane = false; + } +} + +void ChestScreen::handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a) { + if (pane) { + int ms, id; + pane->markerIndex = -1; + if (pane->queryHoldTime(&id, &ms)) { + heldMs = ms; + + FillingContainer* c = (pane == inventoryPane)? + upcast(minecraft->player->inventory) + : upcast(chest); + + const int slotIndex = id + c->getNumLinkedSlots(); + ItemInstance* item = c->getItem(slotIndex); + int count = (item && !item->isNull())? item->count : 0; + float maxHoldMs = item? 700 + 10 * item->count: MaxHoldMs; + + if (count > 1) { + float share = (heldMs-MinChargeMs) / maxHoldMs; + pane->markerType = 1;//(heldMs >= MinChargeMs)? 1 : 0; + pane->markerIndex = id; + pane->markerShare = Mth::Max(share, 0.0f); + + percent = (int)Mth::clamp(100.0f * share, 0.0f, 100.0f); + if (percent >= 100) { + addItem(pane, id); + } + } + } + + pane->render(xm, ym, a); + guiPaneFrame->draw(t, (float)(pane->rect.x - 1), (float)(pane->rect.y - 1)); + //LOGI("query-iv: %d, %d\n", gridId, heldMs); + + } +} + +void ChestScreen::render(int xm, int ym, float a) { + const int N = 5; + static StopwatchNLast r(N); + //renderBackground(); + + Tesselator& t = Tesselator::instance; + guiBackground->draw(t, 0, 0); + glEnable2(GL_ALPHA_TEST); + + // Buttons (Left side + crafting) + super::render(xm, ym, a); + + heldMs = -1; + + handleRenderPane(inventoryPane, t, xm, ym, a); + handleRenderPane(chestPane, t, xm, ym, a); + + float now = getTimeS(); + float MaxTime = 0.3f; + std::vector flyingToSave; + + glEnable2(GL_BLEND); + glColor4f(1, 1, 1, 0.2f); + t.beginOverride(); + //t.color(1.0f, 0.0f, 0.0f, 0.2f); + //t.noColor(); + + glEnable2(GL_SCISSOR_TEST); + //LOGI("panesBox: %d, %d - %d, %d\n", panesBbox.x, panesBbox.y, panesBbox.w, panesBbox.h); + minecraft->gui.setScissorRect(panesBbox); + for (unsigned int i = 0; i < flyingItems.size(); ++i) { + FlyingItem& fi = flyingItems[i]; + float since = (now - fi.startTime); + if (since > MaxTime) continue; + float t = since / MaxTime; + t *= t; + //float xx = fi.sx + t * 100.0f; + + float xx = Mth::lerp(fi.sx, fi.dx, t); + float yy = Mth::lerp(fi.sy, fi.dy, t); + ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, &fi.item, xx + 7, yy + 8, true); + //minecraft->gui.renderSlotText(&fi.item, xx + 3, yy + 3, true, true); + + flyingToSave.push_back(fi); + } + t.enableColor(); + t.endOverrideAndDraw(); + glDisable2(GL_SCISSOR_TEST); + + flyingItems = flyingToSave; + + t.colorABGR(0xffffffff); + glDisable2(GL_BLEND); + + minecraft->textures->loadAndBindTexture("gui/spritesheet.png"); +} + +void ChestScreen::buttonClicked(Button* button) { + if (button == &btnClose) { + minecraft->player->closeContainer(); + } +} + +bool ChestScreen::handleAddItem(FillingContainer* from, FillingContainer* to, int itemIndex) { + const int itemOffset = from->getNumLinkedSlots(); + const int slotIndex = itemIndex + itemOffset; + ItemInstance* item = from->getItem(slotIndex); + + bool added = false; + bool fromChest = (from == chest); + Touch::InventoryPane* pane = fromChest? chestPane : inventoryPane; + Touch::InventoryPane* toPane = fromChest? inventoryPane : chestPane; + + int wantedCount = (item && !item->isNull())? item->count * percent / 100 : 0; + if ((item && !item->isNull()) && (!wantedCount || heldMs < MinChargeMs)) { + wantedCount = 1; + } + + if (wantedCount > 0) { + ItemInstance takenItem(*item); + takenItem.count = wantedCount; + + ItemDiffer differ(getItems(toPane)); + to->add(&takenItem); + + added = (takenItem.count != wantedCount); + + if (added) { + item->count -= (wantedCount - takenItem.count); + std::vector changed; + std::vector items = getItems(toPane); + differ.getDiff(items, changed); + + ScrollingPane::GridItem g, gTo; + pane->getGridItemFor_slow(itemIndex, g); + + //LOGI("Changed: %d\n", changed.size()); + for (unsigned int i = 0; i < changed.size(); ++i) { + FlyingItem fi; + fi.startTime = getTimeS(); + fi.item = *item; + + fi.sx = g.xf; + fi.sy = g.yf; + + int toIndex = changed[i]; + toPane->getGridItemFor_slow(toIndex, gTo); + + fi.dx = gTo.xf; + fi.dy = gTo.yf; + flyingItems.push_back(fi); + + if (!fromChest && minecraft->level->isClientSide) { + int j = toIndex; + ItemInstance item = items[j]? *items[j] : ItemInstance(); + ContainerSetSlotPacket p(menu->containerId, j, item); + minecraft->raknetInstance->send(p); + } + } + } + + // Send to server, needs a bit special handling + if (fromChest) { + ItemInstance ins(item->count <= 0? ItemInstance() : *item); + ContainerSetSlotPacket p(menu->containerId, slotIndex, ins); + minecraft->raknetInstance->send(p); + } + if (item->count <= 0) + from->clearSlot(slotIndex); + } + // Clear the marker indices + pane->markerIndex = toPane->markerIndex = -1; + + return added; +} + +bool ChestScreen::addItem(const Touch::InventoryPane* forPane, int itemIndex) { + //LOGI("items.size, index: %d, %d\n", inventoryItems.size(), itemIndex); + bool l2r = (forPane == inventoryPane); + return handleAddItem( l2r? upcast(minecraft->player->inventory) : upcast(chest), + l2r? upcast(chest) : upcast(minecraft->player->inventory), + itemIndex); +} + +bool ChestScreen::isAllowed( int slot ) +{ + return true; +} + +bool ChestScreen::renderGameBehind() +{ + return false; +} + +std::vector ChestScreen::getItems( const Touch::InventoryPane* forPane ) +{ + if (forPane == inventoryPane) { + for (int i = Inventory::MAX_SELECTION_SIZE, j = 0; i < minecraft->player->inventory->getContainerSize(); ++i, ++j) + inventoryItems[j] = minecraft->player->inventory->getItem(i); + return inventoryItems; + } + else { + for (int i = 0; i < chest->getContainerSize(); ++i) + chestItems[i] = chest->getItem(i); + return chestItems; + } +} + + +void ChestScreen::setupPane() +{ + inventoryItems.clear(); + for (int i = Inventory::MAX_SELECTION_SIZE; i < minecraft->player->inventory->getContainerSize(); ++i) { + ItemInstance* item = minecraft->player->inventory->getItem(i); + /*if (!item || item->isNull()) continue;*/ + inventoryItems.push_back(item); + } + chestItems.clear(); + for (int i = 0; i < chest->getContainerSize(); ++i) { + ItemInstance* item = chest->getItem(i); + /*if (!item || item->isNull()) continue;*/ + chestItems.push_back(item); + } + + int maxWidth = width/2 - Bx/2;//- Bx - Bx/*- Bx*/; + int InventoryColumns = maxWidth / ItemSize; + const int realWidth = InventoryColumns * ItemSize; + int paneWidth = realWidth;// + Bx + Bx; + const int realBx = (width/2 - realWidth) / 2; + + IntRectangle rect(realBx, +#ifdef __APPLE__ + 24 + By - ((width==240)?1:0), realWidth, ((width==240)?1:0) + height-By-By-24); +#else + 24 + By, realWidth, height-By-By-24); +#endif + // IntRectangle(0, 0, 100, 100) + if (inventoryPane) delete inventoryPane; + inventoryPane = new Touch::InventoryPane(this, minecraft, rect, paneWidth, BorderPixels, minecraft->player->inventory->getContainerSize() - Inventory::MAX_SELECTION_SIZE, ItemSize, (int)BorderPixels); + inventoryPane->fillMarginX = 0; + inventoryPane->fillMarginY = 0; + guiPaneFrame->setSize((float)rect.w + 2, (float)rect.h + 2); + + panesBbox = rect; + rect.x += width/2;// - rect.w - Bx; + panesBbox.w += (rect.x - panesBbox.x); + + if (chestPane) delete chestPane; + chestPane = new Touch::InventoryPane(this, minecraft, rect, paneWidth, BorderPixels, chest->getContainerSize(), ItemSize, (int)BorderPixels); + chestPane->fillMarginX = 0; + chestPane->fillMarginY = 0; + LOGI("Creating new panes\n:" + " Inventory %d %p\n" + " Chest %d %p\n", (int)inventoryItems.size(), inventoryPane, (int)chestItems.size(), chestPane); +} + +void ChestScreen::drawSlotItemAt( Tesselator& t, const ItemInstance* item, int x, int y, bool selected) +{ + float xx = (float)x; + float yy = (float)y; + + (selected? guiSlot/*Marked*/ : guiSlot)->draw(t, xx, yy); + + if (selected) + guiSlotMarker->draw(t, xx - 2, yy - 2); + + if (item && !item->isNull()) { + ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, xx + 7, yy + 8, true); + minecraft->gui.renderSlotText(item, xx + 3, yy + 3, true, true); + } +} diff --git a/src/client/gui/screens/ChestScreen.h b/src/client/gui/screens/ChestScreen.h index 7a072ef..61a895d 100755 --- a/src/client/gui/screens/ChestScreen.h +++ b/src/client/gui/screens/ChestScreen.h @@ -1,72 +1,72 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ChestScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ChestScreen_H__ - -#include "BaseContainerScreen.h" - -#include "../components/InventoryPane.h" -#include "../components/Button.h" - -class Font; -class CItem; -class Textures; -class NinePatchLayer; -class FillingContainer; -class ChestTileEntity; -class Tesselator; - -class ChestScreen: public BaseContainerScreen, - public Touch::IInventoryPaneCallback -{ - typedef BaseContainerScreen super; - typedef std::vector ItemList; - friend class ItemPane; -public: - ChestScreen(Player* player, ChestTileEntity* chest); - ~ChestScreen(); - - void init(); - void setupPositions(); - - void tick(); - void render(int xm, int ym, float a); - bool renderGameBehind(); - void buttonClicked(Button* button); - - // IInventoryPaneCallback - bool addItem(const Touch::InventoryPane* pane, int itemId); - bool isAllowed( int slot ); - std::vector getItems( const Touch::InventoryPane* forPane ); - //const ItemList& getItems(const ItemPane* forPane); -private: - void setupPane(); - - void drawSlotItemAt(Tesselator& t, const ItemInstance* item, int x, int y, bool selected); - bool handleAddItem(FillingContainer* from, FillingContainer* to, int itemIndex); - void handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a); - - std::string currentItemDesc; - ImageButton btnClose; - Touch::THeader bHeader; - Touch::THeader bHeaderChest; - - Touch::InventoryPane* inventoryPane; - Touch::InventoryPane* chestPane; - IntRectangle panesBbox; - - std::vector inventoryItems; - std::vector chestItems; - bool doRecreatePane; - - int selectedSlot; - - // GUI elements such as 9-Patches - NinePatchLayer* guiBackground; - NinePatchLayer* guiSlot; - NinePatchLayer* guiSlotMarked; - NinePatchLayer* guiSlotMarker; - NinePatchLayer* guiPaneFrame; - Player* player; - ChestTileEntity* chest; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ChestScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ChestScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ChestScreen_H__ + +#include "BaseContainerScreen.h" + +#include "../components/InventoryPane.h" +#include "../components/Button.h" + +class Font; +class CItem; +class Textures; +class NinePatchLayer; +class FillingContainer; +class ChestTileEntity; +class Tesselator; + +class ChestScreen: public BaseContainerScreen, + public Touch::IInventoryPaneCallback +{ + typedef BaseContainerScreen super; + typedef std::vector ItemList; + friend class ItemPane; +public: + ChestScreen(Player* player, ChestTileEntity* chest); + ~ChestScreen(); + + void init(); + void setupPositions(); + + void tick(); + void render(int xm, int ym, float a); + bool renderGameBehind(); + void buttonClicked(Button* button); + + // IInventoryPaneCallback + bool addItem(const Touch::InventoryPane* pane, int itemId); + bool isAllowed( int slot ); + std::vector getItems( const Touch::InventoryPane* forPane ); + //const ItemList& getItems(const ItemPane* forPane); +private: + void setupPane(); + + void drawSlotItemAt(Tesselator& t, const ItemInstance* item, int x, int y, bool selected); + bool handleAddItem(FillingContainer* from, FillingContainer* to, int itemIndex); + void handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a); + + std::string currentItemDesc; + ImageButton btnClose; + Touch::THeader bHeader; + Touch::THeader bHeaderChest; + + Touch::InventoryPane* inventoryPane; + Touch::InventoryPane* chestPane; + IntRectangle panesBbox; + + std::vector inventoryItems; + std::vector chestItems; + bool doRecreatePane; + + int selectedSlot; + + // GUI elements such as 9-Patches + NinePatchLayer* guiBackground; + NinePatchLayer* guiSlot; + NinePatchLayer* guiSlotMarked; + NinePatchLayer* guiSlotMarker; + NinePatchLayer* guiPaneFrame; + Player* player; + ChestTileEntity* chest; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ChestScreen_H__*/ diff --git a/src/client/gui/screens/ChooseLevelScreen.cpp b/src/client/gui/screens/ChooseLevelScreen.cpp index 1cef1c5..11e9aea 100755 --- a/src/client/gui/screens/ChooseLevelScreen.cpp +++ b/src/client/gui/screens/ChooseLevelScreen.cpp @@ -1,26 +1,26 @@ -#include "ChooseLevelScreen.h" -#include -#include -#include "../../Minecraft.h" - -void ChooseLevelScreen::init() { - loadLevelSource(); -} - -void ChooseLevelScreen::loadLevelSource() -{ - LevelStorageSource* levelSource = minecraft->getLevelSource(); - levelSource->getLevelList(levels); - std::sort(levels.begin(), levels.end()); -} - -std::string ChooseLevelScreen::getUniqueLevelName( const std::string& level ) { - std::set Set; - for (unsigned int i = 0; i < levels.size(); ++i) - Set.insert(levels[i].id); - - std::string s = level; - while ( Set.find(s) != Set.end() ) - s += "-"; - return s; -} +#include "ChooseLevelScreen.h" +#include +#include +#include "../../Minecraft.h" + +void ChooseLevelScreen::init() { + loadLevelSource(); +} + +void ChooseLevelScreen::loadLevelSource() +{ + LevelStorageSource* levelSource = minecraft->getLevelSource(); + levelSource->getLevelList(levels); + std::sort(levels.begin(), levels.end()); +} + +std::string ChooseLevelScreen::getUniqueLevelName( const std::string& level ) { + std::set Set; + for (unsigned int i = 0; i < levels.size(); ++i) + Set.insert(levels[i].id); + + std::string s = level; + while ( Set.find(s) != Set.end() ) + s += "-"; + return s; +} diff --git a/src/client/gui/screens/ChooseLevelScreen.h b/src/client/gui/screens/ChooseLevelScreen.h index 74d5341..3032866 100755 --- a/src/client/gui/screens/ChooseLevelScreen.h +++ b/src/client/gui/screens/ChooseLevelScreen.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ChooseLevelScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ChooseLevelScreen_H__ - -#include "../Screen.h" -#include "../../../world/level/storage/LevelStorageSource.h" - -class ChooseLevelScreen: public Screen -{ -public: - void init(); - -protected: - std::string getUniqueLevelName(const std::string& level); - -private: - void loadLevelSource(); - - LevelSummaryList levels; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ChooseLevelScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ChooseLevelScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ChooseLevelScreen_H__ + +#include "../Screen.h" +#include "../../../world/level/storage/LevelStorageSource.h" + +class ChooseLevelScreen: public Screen +{ +public: + void init(); + +protected: + std::string getUniqueLevelName(const std::string& level); + +private: + void loadLevelSource(); + + LevelSummaryList levels; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ChooseLevelScreen_H__*/ diff --git a/src/client/gui/screens/ConfirmScreen.cpp b/src/client/gui/screens/ConfirmScreen.cpp index 2529549..6e53045 100755 --- a/src/client/gui/screens/ConfirmScreen.cpp +++ b/src/client/gui/screens/ConfirmScreen.cpp @@ -1,84 +1,84 @@ -#include "ConfirmScreen.h" -#include "../components/Button.h" -#include "../../Minecraft.h" - -ConfirmScreen::ConfirmScreen(Screen* parent_, const std::string& title1_, const std::string& title2_, int id_) -: parent(parent_), - title1(title1_), - title2(title2_), - id(id_), - yesButtonText("Ok"), - noButtonText("Cancel"), - yesButton(0), - noButton(0) -{ -} - -ConfirmScreen::ConfirmScreen(Screen* parent_, const std::string& title1_, const std::string& title2_, const std::string& yesButton_, const std::string& noButton_, int id_ ) -: parent(parent_), - title1(title1_), - title2(title2_), - id(id_), - yesButtonText(yesButton_), - noButtonText(noButton_) -{ -} - -ConfirmScreen::~ConfirmScreen() { - delete yesButton; - delete noButton; -} - -void ConfirmScreen::init() -{ - if (/* minecraft->useTouchscreen() */ true) { - yesButton = new Touch::TButton(0, 0, 0, yesButtonText), - noButton = new Touch::TButton(1, 0, 0, noButtonText); - } else { - yesButton = new Button(0, 0, 0, yesButtonText), - noButton = new Button(1, 0, 0, noButtonText); - } - - buttons.push_back(yesButton); - buttons.push_back(noButton); - - tabButtons.push_back(yesButton); - tabButtons.push_back(noButton); -} - -void ConfirmScreen::setupPositions() { - const int ButtonWidth = 120; - const int ButtonHeight = 24; - yesButton->x = width / 2 - ButtonWidth - 4; - yesButton->y = height / 6 + 72; - noButton->x = width / 2 + 4; - noButton->y = height / 6 + 72; - yesButton->width = noButton->width = ButtonWidth; - yesButton->height = noButton->height = ButtonHeight; -} - -bool ConfirmScreen::handleBackEvent(bool isDown) { - if (!isDown) - postResult(false); - return true; -} - -void ConfirmScreen::render( int xm, int ym, float a ) -{ - renderBackground(); - - drawCenteredString(font, title1, width / 2, 50, 0xffffff); - drawCenteredString(font, title2, width / 2, 70, 0xffffff); - - super::render(xm, ym, a); -} - -void ConfirmScreen::buttonClicked( Button* button ) -{ - postResult(button->id == 0); -} - -void ConfirmScreen::postResult(bool isOk) -{ - parent->confirmResult(isOk, id); -} +#include "ConfirmScreen.h" +#include "../components/Button.h" +#include "../../Minecraft.h" + +ConfirmScreen::ConfirmScreen(Screen* parent_, const std::string& title1_, const std::string& title2_, int id_) +: parent(parent_), + title1(title1_), + title2(title2_), + id(id_), + yesButtonText("Ok"), + noButtonText("Cancel"), + yesButton(0), + noButton(0) +{ +} + +ConfirmScreen::ConfirmScreen(Screen* parent_, const std::string& title1_, const std::string& title2_, const std::string& yesButton_, const std::string& noButton_, int id_ ) +: parent(parent_), + title1(title1_), + title2(title2_), + id(id_), + yesButtonText(yesButton_), + noButtonText(noButton_) +{ +} + +ConfirmScreen::~ConfirmScreen() { + delete yesButton; + delete noButton; +} + +void ConfirmScreen::init() +{ + if (/* minecraft->useTouchscreen() */ true) { + yesButton = new Touch::TButton(0, 0, 0, yesButtonText), + noButton = new Touch::TButton(1, 0, 0, noButtonText); + } else { + yesButton = new Button(0, 0, 0, yesButtonText), + noButton = new Button(1, 0, 0, noButtonText); + } + + buttons.push_back(yesButton); + buttons.push_back(noButton); + + tabButtons.push_back(yesButton); + tabButtons.push_back(noButton); +} + +void ConfirmScreen::setupPositions() { + const int ButtonWidth = 120; + const int ButtonHeight = 24; + yesButton->x = width / 2 - ButtonWidth - 4; + yesButton->y = height / 6 + 72; + noButton->x = width / 2 + 4; + noButton->y = height / 6 + 72; + yesButton->width = noButton->width = ButtonWidth; + yesButton->height = noButton->height = ButtonHeight; +} + +bool ConfirmScreen::handleBackEvent(bool isDown) { + if (!isDown) + postResult(false); + return true; +} + +void ConfirmScreen::render( int xm, int ym, float a ) +{ + renderBackground(); + + drawCenteredString(font, title1, width / 2, 50, 0xffffff); + drawCenteredString(font, title2, width / 2, 70, 0xffffff); + + super::render(xm, ym, a); +} + +void ConfirmScreen::buttonClicked( Button* button ) +{ + postResult(button->id == 0); +} + +void ConfirmScreen::postResult(bool isOk) +{ + parent->confirmResult(isOk, id); +} diff --git a/src/client/gui/screens/ConfirmScreen.h b/src/client/gui/screens/ConfirmScreen.h index 0fedbe9..19245b9 100755 --- a/src/client/gui/screens/ConfirmScreen.h +++ b/src/client/gui/screens/ConfirmScreen.h @@ -1,40 +1,40 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ConfirmScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ConfirmScreen_H__ - -//package net.minecraft.client.gui; - -#include "../Screen.h" -#include - -class ConfirmScreen: public Screen -{ - typedef Screen super; -public: - ConfirmScreen(Screen* parent_, const std::string& title1_, const std::string& title2_, int id_); - ConfirmScreen(Screen* parent_, const std::string& title1_, const std::string& title2_, const std::string& yesButton, const std::string& noButton, int id_); - ~ConfirmScreen(); - - void init(); - void setupPositions(); - - bool handleBackEvent(bool isDown); - void render(int xm, int ym, float a); -protected: - void buttonClicked(Button* button); - - virtual void postResult(bool isOk); - - Screen* parent; - int id; -private: - std::string title1; - std::string title2; - - std::string yesButtonText; - std::string noButtonText; - - Button* yesButton; // 0 - Button* noButton; // 1 -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ConfirmScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ConfirmScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ConfirmScreen_H__ + +//package net.minecraft.client.gui; + +#include "../Screen.h" +#include + +class ConfirmScreen: public Screen +{ + typedef Screen super; +public: + ConfirmScreen(Screen* parent_, const std::string& title1_, const std::string& title2_, int id_); + ConfirmScreen(Screen* parent_, const std::string& title1_, const std::string& title2_, const std::string& yesButton, const std::string& noButton, int id_); + ~ConfirmScreen(); + + void init(); + void setupPositions(); + + bool handleBackEvent(bool isDown); + void render(int xm, int ym, float a); +protected: + void buttonClicked(Button* button); + + virtual void postResult(bool isOk); + + Screen* parent; + int id; +private: + std::string title1; + std::string title2; + + std::string yesButtonText; + std::string noButtonText; + + Button* yesButton; // 0 + Button* noButton; // 1 +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ConfirmScreen_H__*/ diff --git a/src/client/gui/screens/DeathScreen.cpp b/src/client/gui/screens/DeathScreen.cpp index 411b148..62fe0e3 100755 --- a/src/client/gui/screens/DeathScreen.cpp +++ b/src/client/gui/screens/DeathScreen.cpp @@ -1,83 +1,83 @@ -#include "DeathScreen.h" -#include "ScreenChooser.h" -#include "../components/Button.h" -#include "../../Minecraft.h" -#include "../../player/LocalPlayer.h" -#include "../../../platform/time.h" - -static const int WAIT_TICKS = 30; - -DeathScreen::DeathScreen() -: bRespawn(0), - bTitle(0), - _hasChosen(false), - _tick(0) -{ -} - -DeathScreen::~DeathScreen() -{ - delete bRespawn; - delete bTitle; -} - -void DeathScreen::init() -{ - if (/* minecraft->useTouchscreen() */ true) { - bRespawn = new Touch::TButton(1, "Respawn!"); - bTitle = new Touch::TButton(2, "Main menu"); - } else { - bRespawn = new Button(1, "Respawn!"); - bTitle = new Button(2, "Main menu"); - } - buttons.push_back(bRespawn); - buttons.push_back(bTitle); - - tabButtons.push_back(bRespawn); - tabButtons.push_back(bTitle); -} - -void DeathScreen::setupPositions() -{ - bRespawn->width = bTitle->width = width / 4; - - bRespawn->y = bTitle->y = height / 2; - bRespawn->x = width/2 - bRespawn->width - 10; - bTitle->x = width/2 + 10; - - LOGI("xyz: %d, %d (%d, %d)\n", bTitle->x, bTitle->y, width, height); -} - -void DeathScreen::tick() { - ++_tick; -} - -void DeathScreen::render( int xm, int ym, float a ) -{ - fillGradient(0, 0, width, height, 0x60500000, 0xa0803030); - - glPushMatrix2(); - glScalef2(2, 2, 2); - drawCenteredString(font, "You died!", width / 2 / 2, height / 8, 0xffffff); - glPopMatrix2(); - - if (_tick >= WAIT_TICKS) - Screen::render(xm, ym, a); -} - -void DeathScreen::buttonClicked( Button* button ) -{ - if (_tick < WAIT_TICKS) return; - - if (button == bRespawn) { - //RespawnPacket packet(); - //minecraft->raknetInstance->send(packet); - - minecraft->player->respawn(); - //minecraft->raknetInstance->send(); - minecraft->setScreen(NULL); - } - - if (button == bTitle) - minecraft->leaveGame(); -} +#include "DeathScreen.h" +#include "ScreenChooser.h" +#include "../components/Button.h" +#include "../../Minecraft.h" +#include "../../player/LocalPlayer.h" +#include "../../../platform/time.h" + +static const int WAIT_TICKS = 30; + +DeathScreen::DeathScreen() +: bRespawn(0), + bTitle(0), + _hasChosen(false), + _tick(0) +{ +} + +DeathScreen::~DeathScreen() +{ + delete bRespawn; + delete bTitle; +} + +void DeathScreen::init() +{ + if (/* minecraft->useTouchscreen() */ true) { + bRespawn = new Touch::TButton(1, "Respawn!"); + bTitle = new Touch::TButton(2, "Main menu"); + } else { + bRespawn = new Button(1, "Respawn!"); + bTitle = new Button(2, "Main menu"); + } + buttons.push_back(bRespawn); + buttons.push_back(bTitle); + + tabButtons.push_back(bRespawn); + tabButtons.push_back(bTitle); +} + +void DeathScreen::setupPositions() +{ + bRespawn->width = bTitle->width = width / 4; + + bRespawn->y = bTitle->y = height / 2; + bRespawn->x = width/2 - bRespawn->width - 10; + bTitle->x = width/2 + 10; + + LOGI("xyz: %d, %d (%d, %d)\n", bTitle->x, bTitle->y, width, height); +} + +void DeathScreen::tick() { + ++_tick; +} + +void DeathScreen::render( int xm, int ym, float a ) +{ + fillGradient(0, 0, width, height, 0x60500000, 0xa0803030); + + glPushMatrix2(); + glScalef2(2, 2, 2); + drawCenteredString(font, "You died!", width / 2 / 2, height / 8, 0xffffff); + glPopMatrix2(); + + if (_tick >= WAIT_TICKS) + Screen::render(xm, ym, a); +} + +void DeathScreen::buttonClicked( Button* button ) +{ + if (_tick < WAIT_TICKS) return; + + if (button == bRespawn) { + //RespawnPacket packet(); + //minecraft->raknetInstance->send(packet); + + minecraft->player->respawn(); + //minecraft->raknetInstance->send(); + minecraft->setScreen(NULL); + } + + if (button == bTitle) + minecraft->leaveGame(); +} diff --git a/src/client/gui/screens/DeathScreen.h b/src/client/gui/screens/DeathScreen.h index 8a728ce..0475a22 100755 --- a/src/client/gui/screens/DeathScreen.h +++ b/src/client/gui/screens/DeathScreen.h @@ -1,30 +1,30 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DeathScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__DeathScreen_H__ - -#include "../Screen.h" -class Button; - -class DeathScreen: public Screen -{ -public: - DeathScreen(); - - virtual ~DeathScreen(); - - void init(); - - void setupPositions(); - - void tick(); - void render(int xm, int ym, float a); - - void buttonClicked(Button* button); - -private: - Button* bRespawn; - Button* bTitle; - bool _hasChosen; - int _tick; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__DeathScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DeathScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__DeathScreen_H__ + +#include "../Screen.h" +class Button; + +class DeathScreen: public Screen +{ +public: + DeathScreen(); + + virtual ~DeathScreen(); + + void init(); + + void setupPositions(); + + void tick(); + void render(int xm, int ym, float a); + + void buttonClicked(Button* button); + +private: + Button* bRespawn; + Button* bTitle; + bool _hasChosen; + int _tick; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__DeathScreen_H__*/ diff --git a/src/client/gui/screens/DialogDefinitions.h b/src/client/gui/screens/DialogDefinitions.h index 886e6a2..1f6bc85 100755 --- a/src/client/gui/screens/DialogDefinitions.h +++ b/src/client/gui/screens/DialogDefinitions.h @@ -1,13 +1,13 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DialogDefinitions_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__DialogDefinitions_H__ - -class DialogDefinitions { -public: - static const int DIALOG_CREATE_NEW_WORLD = 1; - static const int DIALOG_NEW_CHAT_MESSAGE = 2; - static const int DIALOG_MAINMENU_OPTIONS = 3; - static const int DIALOG_RENAME_MP_WORLD = 4; - static const int DIALOG_DEMO_FEATURE_DISABLED = 98; -}; - -#endif /*#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DialogDefinitions_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DialogDefinitions_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__DialogDefinitions_H__ + +class DialogDefinitions { +public: + static const int DIALOG_CREATE_NEW_WORLD = 1; + static const int DIALOG_NEW_CHAT_MESSAGE = 2; + static const int DIALOG_MAINMENU_OPTIONS = 3; + static const int DIALOG_RENAME_MP_WORLD = 4; + static const int DIALOG_DEMO_FEATURE_DISABLED = 98; +}; + +#endif /*#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DialogDefinitions_H__*/ diff --git a/src/client/gui/screens/DisconnectionScreen.h b/src/client/gui/screens/DisconnectionScreen.h index f32530f..c6c7f96 100755 --- a/src/client/gui/screens/DisconnectionScreen.h +++ b/src/client/gui/screens/DisconnectionScreen.h @@ -1,57 +1,57 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DisconnectionScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__DisconnectionScreen_H__ - -#include "../Screen.h" -#include "../Font.h" -#include "../components/Button.h" -#include "../../Minecraft.h" -#include - -class DisconnectionScreen: public Screen -{ - typedef Screen super; -public: - DisconnectionScreen(const std::string& msg) - : _msg(msg), - _back(NULL) - {} - - ~DisconnectionScreen() { - delete _back; - } - - void init() { - if (/* minecraft->useTouchscreen() */ true) - _back = new Touch::TButton(1, "Ok"); - else - _back = new Button(1, "Ok"); - - buttons.push_back(_back); - tabButtons.push_back(_back); - - _back->width = 128; - _back->x = (width - _back->width) / 2; - _back->y = height / 2; - } - - void render( int xm, int ym, float a ) { - renderBackground(); - super::render(xm, ym, a); - - int center = (width - minecraft->font->width(_msg)) / 2; - minecraft->font->drawShadow(_msg, (float)center, (float)(height / 2 - 32), 0xffffffff); - } - - void buttonClicked(Button* button) { - if (button->id == _back->id) { - minecraft->leaveGame(); - } - } - bool isInGameScreen() { return false; } - -private: - std::string _msg; - Button* _back; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__DisconnectionScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DisconnectionScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__DisconnectionScreen_H__ + +#include "../Screen.h" +#include "../Font.h" +#include "../components/Button.h" +#include "../../Minecraft.h" +#include + +class DisconnectionScreen: public Screen +{ + typedef Screen super; +public: + DisconnectionScreen(const std::string& msg) + : _msg(msg), + _back(NULL) + {} + + ~DisconnectionScreen() { + delete _back; + } + + void init() { + if (/* minecraft->useTouchscreen() */ true) + _back = new Touch::TButton(1, "Ok"); + else + _back = new Button(1, "Ok"); + + buttons.push_back(_back); + tabButtons.push_back(_back); + + _back->width = 128; + _back->x = (width - _back->width) / 2; + _back->y = height / 2; + } + + void render( int xm, int ym, float a ) { + renderBackground(); + super::render(xm, ym, a); + + int center = (width - minecraft->font->width(_msg)) / 2; + minecraft->font->drawShadow(_msg, (float)center, (float)(height / 2 - 32), 0xffffffff); + } + + void buttonClicked(Button* button) { + if (button->id == _back->id) { + minecraft->leaveGame(); + } + } + bool isInGameScreen() { return false; } + +private: + std::string _msg; + Button* _back; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__DisconnectionScreen_H__*/ diff --git a/src/client/gui/screens/FurnaceScreen.cpp b/src/client/gui/screens/FurnaceScreen.cpp index 58d6df2..8b4b3e9 100755 --- a/src/client/gui/screens/FurnaceScreen.cpp +++ b/src/client/gui/screens/FurnaceScreen.cpp @@ -1,555 +1,555 @@ -#include "FurnaceScreen.h" -#include "crafting/PaneCraftingScreen.h" -#include "../Screen.h" -#include "../components/NinePatch.h" -#include "../../Minecraft.h" -#include "../../player/LocalPlayer.h" -#include "../../renderer/Tesselator.h" -#include "../../renderer/entity/ItemRenderer.h" -#include "../../../world/item/Item.h" -#include "../../../world/item/crafting/Recipes.h" -#include "../../../world/item/ItemCategory.h" -#include "../../../world/entity/player/Inventory.h" -#include "../../../world/entity/item/ItemEntity.h" -#include "../../../world/level/Level.h" -#include "../../../world/item/DyePowderItem.h" -#include "../../../world/item/crafting/FurnaceRecipes.h" -#include "../../../world/level/tile/entity/FurnaceTileEntity.h" -#include "../../../locale/I18n.h" -#include "../../../util/StringUtils.h" -#include "../../../world/inventory/FurnaceMenu.h" -#include "../../../network/packet/ContainerSetSlotPacket.h" -#include "../../../network/RakNetInstance.h" - -static int heldMs = -1; -static int percent = -1; -static const float MaxHoldMs = 500.0f; -static const int MinChargeMs = 200; - -static void setIfNotSet(bool& ref, bool condition) { - ref = (ref || condition); -} - -const int descFrameWidth = 100; - -const int rgbActive = 0xfff0f0f0; -const int rgbInactive = 0xc0635558; -const int rgbInactiveShadow = 0xc0aaaaaa; - -#ifdef __APPLE__ - static const float BorderPixels = 4; - #ifdef DEMO_MODE - static const float BlockPixels = 22; - #else - static const float BlockPixels = 22; - #endif -#else - static const float BorderPixels = 4; - static const float BlockPixels = 24; -#endif -static const int ItemSize = (int)(BlockPixels + 2*BorderPixels); - -static const int Bx = 10; // Border Frame width -static const int By = 6; // Border Frame height - - -FurnaceScreen::FurnaceScreen(Player* player, FurnaceTileEntity* furnace) -: super(new FurnaceMenu(furnace)), //@huge @attn - inventoryPane(NULL), - btnFuel(FurnaceTileEntity::SLOT_FUEL), - btnIngredient(FurnaceTileEntity::SLOT_INGREDIENT), - btnResult(FurnaceTileEntity::SLOT_RESULT), - btnClose(4, ""), - bHeader (5, "Furnace"), - guiBackground(NULL), - guiSlot(NULL), - guiSlotMarked(NULL), - guiSlotMarker(NULL), - guiPaneFrame(NULL), - player(player), - furnace(furnace), - selectedSlot(FurnaceTileEntity::SLOT_INGREDIENT), - doRecreatePane(false), - lastBurnTypeId(0), - descWidth(90) - //guiSlotItem(NULL), - //guiSlotItemSelected(NULL) -{ - //LOGI("Creating FurnaceScreen with %p, %d\n", furnace, furnace->runningId); -} - -FurnaceScreen::~FurnaceScreen() { - clearItems(); - - delete inventoryPane; - - delete guiBackground; - delete guiSlot; - delete guiSlotMarked; - delete guiSlotMarker; - delete guiPaneFrame; - - delete menu; - - if (furnace->clientSideOnly) - delete furnace; -} - -void FurnaceScreen::init() { - super::init(); - //printf("-> %d\n", width/2); - - ImageDef def; - def.name = "gui/spritesheet.png"; - def.x = 0; - def.y = 1; - def.width = def.height = 18; - def.setSrc(IntRectangle(60, 0, 18, 18)); - btnClose.setImageDef(def, true); - btnClose.scaleWhenPressed = false; - - buttons.push_back(&bHeader); - buttons.push_back(&btnIngredient); - buttons.push_back(&btnFuel); - buttons.push_back(&btnResult); - buttons.push_back(&btnClose); - - // GUI - nine patches - NinePatchFactory builder(minecraft->textures, "gui/spritesheet.png"); - - guiBackground = builder.createSymmetrical(IntRectangle(0, 0, 16, 16), 4, 4); - guiSlot = builder.createSymmetrical(IntRectangle(0, 32, 8, 8), 3, 3); - guiSlotMarked = builder.createSymmetrical(IntRectangle(0, 44, 8, 8), 3, 3); - guiSlotMarker = builder.createSymmetrical(IntRectangle(10, 42, 16, 16), 5, 5); - guiPaneFrame = builder.createSymmetrical(IntRectangle(28, 42, 4, 4), 1, 1)->setExcluded(1 << 4); - - recheckRecipes(); -} - -void FurnaceScreen::setupPositions() { - // Left - Categories - bHeader.x = bHeader.y = 0; - bHeader.width = width;// - bDone.w; - - btnClose.width = btnClose.height = 19; - btnClose.x = width - btnClose.width; - btnClose.y = 0; - - // Inventory pane - const int maxWidth = width/2 - Bx - Bx; - const int InventoryColumns = maxWidth / ItemSize; - const int realWidth = InventoryColumns * ItemSize; - const int paneWidth = realWidth + Bx + Bx; - const int realBx = (paneWidth - realWidth) / 2; - - inventoryPaneRect = IntRectangle(realBx, -#ifdef __APPLE__ - 26 + By - ((width==240)?1:0), realWidth, ((width==240)?1:0) + height-By-By-28); -#else - 26 + By, realWidth, height-By-By-28); -#endif - - // Right - Slots, description etc - { - int cx = (inventoryPaneRect.x + inventoryPaneRect.w); - int rightWidth = width - cx; - - btnIngredient.width = btnFuel.width = btnResult.width = (int)guiSlot->getWidth(); - btnIngredient.height = btnFuel.height = btnResult.height = (int)guiSlot->getHeight(); - int space = rightWidth - (2 * btnFuel.width + 40); - int margin = space/2; - int bx0 = cx + margin; - int bx1 = width - margin - btnFuel.width; - btnIngredient.x = btnFuel.x = bx0; - descWidth = (float)Mth::Min(90, width - bx1 + 24 - 4); - - int by = 36;// + (height-20) / 6; - btnIngredient.y = by; - btnFuel.y = by + 20 + btnFuel.height; - - btnResult.x = bx1; - btnResult.y = (btnIngredient.y + btnFuel.y) / 2; - - guiBackground->setSize((float)width, (float)height); - guiSlotMarker->setSize((float)btnFuel.width + 4, (float)btnFuel.height + 4); - - recheckRecipes(); - setupInventoryPane(); - } -} - -void FurnaceScreen::tick() { - if (inventoryPane) - inventoryPane->tick(); - - if (doRecreatePane) { - recheckRecipes(); - setupInventoryPane(); - doRecreatePane = false; - } -} - -void FurnaceScreen::handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a) { - if (pane) { - int ms, id; - pane->markerIndex = -1; - if (pane->queryHoldTime(&id, &ms)) { - heldMs = ms; - - const ItemInstance* item = inventoryItems[id]; - int count = (item && !item->isNull())? item->count : 0; - float maxHoldMs = item? 700 + 10 * item->count: MaxHoldMs; - - if (count > 1 && canMoveToFurnace(id, item)) { - float share = (heldMs-MinChargeMs) / maxHoldMs; - pane->markerType = 1;//(heldMs >= MinChargeMs)? 1 : 0; - pane->markerIndex = id; - pane->markerShare = Mth::Max(share, 0.0f); - - percent = (int)Mth::clamp(100.0f * share, 0.0f, 100.0f); - if (percent >= 100) { - addItem(pane, id); - } - } - } - - pane->render(xm, ym, a); - guiPaneFrame->draw(t, (float)(pane->rect.x - 1), (float)(pane->rect.y - 1)); - //LOGI("query-iv: %d, %d\n", gridId, heldMs); - - } -} - -void FurnaceScreen::render(int xm, int ym, float a) { - const int N = 5; - static StopwatchNLast r(N); - //renderBackground(); - - updateResult(furnace->getItem(FurnaceTileEntity::SLOT_INGREDIENT)); - - Tesselator& t = Tesselator::instance; - guiBackground->draw(t, 0, 0); - glEnable2(GL_ALPHA_TEST); - - // Buttons (Left side + crafting) - super::render(xm, ym, a); - - handleRenderPane(inventoryPane, t, xm, ym, a); - - t.colorABGR(0xffffffff); - - drawSlotItemAt(t, furnace->getItem(btnIngredient.id), btnIngredient.x, btnIngredient.y, btnIngredient.id == selectedSlot); - drawSlotItemAt(t, furnace->getItem(btnFuel.id), btnFuel.x, btnFuel.y, btnFuel.id == selectedSlot); - - const ItemInstance* resultSlotItem = furnace->getItem(btnResult.id); - drawSlotItemAt(t, resultSlotItem, btnResult.x, btnResult.y, btnResult.id == selectedSlot); - - if (!burnResult.isNull()) { - if (!resultSlotItem || resultSlotItem->isNull()) { - glEnable2(GL_BLEND); - t.beginOverride(); - t.colorABGR(0x33ffffff); - t.noColor(); - ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, &burnResult, (float)(btnResult.x + 7), (float)(btnResult.y + 8), true); - t.endOverrideAndDraw(); - glDisable2(GL_BLEND); - } - minecraft->font->drawWordWrap(currentItemDesc, (float)btnResult.x - 24, (float)(btnResult.y + btnResult.height + 6), descWidth, rgbActive); - } - - minecraft->textures->loadAndBindTexture("gui/spritesheet.png"); - int yy = btnResult.y + 8; - int fpx = furnace->getLitProgress(14) + 2; - int xx0 = btnIngredient.x + 8; - blit(xx0, yy, 80, 40, 16, 16, 32, 32); - blit(xx0, yy + 16 - fpx, 112, 40 + 32-fpx-fpx, 16, fpx, 32, fpx+fpx); - - int bpx = furnace->getBurnProgress(24); - int xx1 = btnIngredient.x + 40; - blit(xx1, yy, 144, 40, 24, 16, 48, 32); - blit(xx1, yy, 144, 72, bpx, 16, bpx+bpx, 32); -} - -void FurnaceScreen::buttonClicked(Button* button) { - int slot = button->id; - - if (button == &btnClose) { - minecraft->player->closeContainer(); - } - - if (slot >= FurnaceTileEntity::SLOT_INGREDIENT - && slot <= FurnaceTileEntity::SLOT_RESULT) { - // Can't highlight the Result slot - int oldSlot = selectedSlot; - if (slot != FurnaceTileEntity::SLOT_RESULT) - selectedSlot = slot; - - if (oldSlot == selectedSlot) - takeAndClearSlot(slot); - } -} - -static bool sortCanCraftPredicate(const CItem* a, const CItem* b) { - //if (a->maxBuildCount == 0 && b->maxBuildCount > 0) return false; - //if (b->maxBuildCount == 0 && a->maxBuildCount > 0) return true; - return a->sortText < b->sortText; -} - -void FurnaceScreen::recheckRecipes() -{ - clearItems(); - - Stopwatch w; - w.start(); - - const FurnaceRecipes* recipes = FurnaceRecipes::getInstance(); - ItemPack ip; - // Check for fuel, and items to burn - if (minecraft->player && minecraft->player->inventory) { - Inventory* inv = (minecraft->player)->inventory; - - for (int i = Inventory::MAX_SELECTION_SIZE; i < inv->getContainerSize(); ++i) { - if (ItemInstance* item = inv->getItem(i)) { - // Fuel material - if (FurnaceTileEntity::isFuel(*item)) { - CItem* ci = new CItem(*item, NULL, "");//item->getName()); - //LOGI("Adding fuel: %s\n", item->getName()); - listFuel.push_back(ci); - } - // Ingredient/burn material - if (recipes->isFurnaceItem(item->id)) { - CItem* ci = new CItem(*item, NULL, "");//item->getName()); - //LOGI("Adding item to burn: %s\n", item->getName()); - listIngredient.push_back(ci); - } - //ip.add(ItemPack::getIdForItemInstance(item), item->count); - } - } - } - - ip.print(); - - w.stop(); - w.printEvery(1, "> craft "); - - updateItems(); - //std::stable_sort(_categories[c].begin(), _categories[c].end(), sortCanCraftPredicate); -} - -bool FurnaceScreen::addItem(const Touch::InventoryPane* forPane, int itemIndex) { - //LOGI("items.size, index: %d, %d\n", inventoryItems.size(), itemIndex); - const ItemInstance* item = inventoryItems[itemIndex]; - if (!item || item->isNull()) return false; - - setIfNotSet(doRecreatePane, handleAddItem(selectedSlot, item)); - - if (doRecreatePane) { - int slot = inventorySlots[itemIndex]; - if (player->inventory->getItem(slot)) { - // fix: if we deplete a slot we didn't click on (but had same type), - // we need to NULLify the slot that actually was depleted - for (unsigned int i = 0; i < inventorySlots.size(); ++i) { - slot = inventorySlots[i]; - if (!player->inventory->getItem(slot)) { - LOGI("Changed! removing slot %d (was: %d)\n", i, itemIndex); - itemIndex = i; - break; - } - } - } - inventoryItems[itemIndex] = NULL; - } - //LOGI("Pressed button: %d\n", itemIndexInCurrentCategory); - return true; -} - -bool FurnaceScreen::isAllowed( int slot ) -{ //LOGI("items.size, index: %d, %d\n", inventoryItems.size(), slot); - if (slot >= (int)inventoryItems.size()) return false; - if (!inventoryItems[slot]) return false; - const ItemInstance& item = *inventoryItems[slot]; - - if (selectedSlot == btnFuel.id) - return (FurnaceTileEntity::getBurnDuration(item) > 0); - else - if (selectedSlot == btnIngredient.id) - return !FurnaceRecipes::getInstance()->getResult(item.id).isNull(); - return false; -} - -bool FurnaceScreen::renderGameBehind() -{ - return false; -} - -std::vector FurnaceScreen::getItems( const Touch::InventoryPane* forPane ) -{ - return inventoryItems; -} - -void FurnaceScreen::clearItems() -{ - for (unsigned int i = 0; i < listFuel.size(); ++i) delete listFuel[i]; - for (unsigned int i = 0; i < listIngredient.size(); ++i) delete listIngredient[i]; - listFuel.clear(); - listIngredient.clear(); -} - -void FurnaceScreen::updateItems() { - inventoryItems.clear(); - inventorySlots.clear(); - - ItemList all(listFuel.begin(), listFuel.end()); - all.insert(all.end(), listIngredient.begin(), listIngredient.end()); - for (int i = Inventory::MAX_SELECTION_SIZE; i < minecraft->player->inventory->getContainerSize(); ++i) { - ItemInstance* item = minecraft->player->inventory->getItem(i); - if (!item) continue; - //LOGI("ItemInstance (%p) Id/aux/count: %d, %d, %d\n", item, item->id, item->getAuxValue(), item->count); - bool added = false; - for (unsigned int j = 0; j < listFuel.size(); ++j) { - if (ItemInstance::matches(item, &listFuel[j]->item)) { - inventorySlots.push_back(i); - inventoryItems.push_back(item); - added = true; - break; - } - } - if (added) continue; - for (unsigned int j = 0; j < listIngredient.size(); ++j) { - if (ItemInstance::matches(item, &listIngredient[j]->item)) { - inventorySlots.push_back(i); - inventoryItems.push_back(item); - added = true; - break; - } - } - } -} - -bool FurnaceScreen::canMoveToFurnace(int inventorySlot, const ItemInstance* item) { - if (!isAllowed(inventorySlot)) return false; - ItemInstance* jitem = furnace->getItem(selectedSlot); - if (!jitem || jitem->isNull()) return true; - if (ItemInstance::isStackable(item, jitem) && jitem->count < jitem->getMaxStackSize()) - return true; - - return false; -} - -void FurnaceScreen::updateResult( const ItemInstance* item ) -{ - const ItemInstance* result = furnace->getItem(FurnaceTileEntity::SLOT_RESULT); - if (!result->isNull()) { - int id = result->id; - if (id == lastBurnTypeId) return; - currentItemDesc = I18n::getDescriptionString(*result); - lastBurnTypeId = id; - this->burnResult = *result; - } else { - int id = (item? item->id : 0); - if (id == lastBurnTypeId) return; - - ItemInstance burnResult = FurnaceRecipes::getInstance()->getResult(id); - if (!burnResult.isNull()) - currentItemDesc = I18n::getDescriptionString(burnResult); - else - currentItemDesc = ""; - lastBurnTypeId = id; - this->burnResult = burnResult; - } -} - -void FurnaceScreen::setupInventoryPane() -{ - // IntRectangle(0, 0, 100, 100) - if (inventoryPane) delete inventoryPane; - inventoryPane = new Touch::InventoryPane(this, minecraft, inventoryPaneRect, inventoryPaneRect.w, BorderPixels, inventoryItems.size(), ItemSize, (int)BorderPixels); - inventoryPane->fillMarginX = 0; - inventoryPane->fillMarginY = 0; - guiPaneFrame->setSize((float)inventoryPaneRect.w + 2, (float)inventoryPaneRect.h + 2); - //LOGI("Creating new pane: %d %p\n", inventoryItems.size(), inventoryPane); -} - -void FurnaceScreen::drawSlotItemAt( Tesselator& t, const ItemInstance* item, int x, int y, bool selected) -{ - float xx = (float)x; - float yy = (float)y; - - (selected? guiSlot/*Marked*/ : guiSlot)->draw(t, xx, yy); - - if (selected) - guiSlotMarker->draw(t, xx - 2, yy - 2); - - if (item && !item->isNull()) { - ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, xx + 7, yy + 8, true); - minecraft->gui.renderSlotText(item, xx + 3, yy + 3, true, true); - } -} - -ItemInstance FurnaceScreen::moveOver(const ItemInstance* item, int maxCount) { - int wantedCount = item->count * percent / 100; - if (!wantedCount || heldMs < MinChargeMs) - wantedCount = 1; - - wantedCount = Mth::Min(wantedCount, maxCount); - - ItemInstance removed(item->id, wantedCount, item->getAuxValue()); - int oldSize = minecraft->player->inventory->getNumEmptySlots(); - if (minecraft->player->inventory->removeResource(removed)) { - int newSize = minecraft->player->inventory->getNumEmptySlots(); - setIfNotSet(doRecreatePane, newSize != oldSize); - return removed; - } - return ItemInstance(); -} - -void FurnaceScreen::takeAndClearSlot( int slot ) -{ - //if (selectedSlot == btnBurn.id && !furnace->isSlotEmpty(btnBurn.id)) - ItemInstance oldItem = *furnace->getItem(slot); - ItemInstance blank; - - furnace->setItem(slot, &blank); - if (minecraft->level->isClientSide) { - ContainerSetSlotPacket p(menu->containerId, slot, blank); - minecraft->raknetInstance->send(p); - } - - int oldSize = minecraft->player->inventory->getNumEmptySlots(); - - if (!minecraft->player->inventory->add(&oldItem)) - minecraft->player->drop(new ItemInstance(oldItem), false); - - int newSize = minecraft->player->inventory->getNumEmptySlots(); - setIfNotSet(doRecreatePane, newSize != oldSize); -} - -bool FurnaceScreen::handleAddItem( int slot, const ItemInstance* item ) -{ - ItemInstance* furnaceItem = furnace->getItem(slot); - int oldSize = minecraft->player->inventory->getNumEmptySlots(); - - if (item->id == furnaceItem->id) { - // If stackable, stack them! Else deny the addition - const int maxMovedCount = furnaceItem->getMaxStackSize() - furnaceItem->count; - if (maxMovedCount <= 0) - return false; - - ItemInstance added = moveOver(item, maxMovedCount); - furnaceItem->count += added.count; - } else { - if (!furnace->isSlotEmpty(slot)) - return false;//takeAndClearSlot(slot); - - ItemInstance moved = moveOver(item, item->getMaxStackSize()); - player->containerMenu->setSlot(slot, &moved); - } - - if (minecraft->level->isClientSide) { - ContainerSetSlotPacket p(menu->containerId, slot, *furnaceItem); - minecraft->raknetInstance->send(p); - } - - int newSize = minecraft->player->inventory->getNumEmptySlots(); - return (newSize != oldSize); -} +#include "FurnaceScreen.h" +#include "crafting/PaneCraftingScreen.h" +#include "../Screen.h" +#include "../components/NinePatch.h" +#include "../../Minecraft.h" +#include "../../player/LocalPlayer.h" +#include "../../renderer/Tesselator.h" +#include "../../renderer/entity/ItemRenderer.h" +#include "../../../world/item/Item.h" +#include "../../../world/item/crafting/Recipes.h" +#include "../../../world/item/ItemCategory.h" +#include "../../../world/entity/player/Inventory.h" +#include "../../../world/entity/item/ItemEntity.h" +#include "../../../world/level/Level.h" +#include "../../../world/item/DyePowderItem.h" +#include "../../../world/item/crafting/FurnaceRecipes.h" +#include "../../../world/level/tile/entity/FurnaceTileEntity.h" +#include "../../../locale/I18n.h" +#include "../../../util/StringUtils.h" +#include "../../../world/inventory/FurnaceMenu.h" +#include "../../../network/packet/ContainerSetSlotPacket.h" +#include "../../../network/RakNetInstance.h" + +static int heldMs = -1; +static int percent = -1; +static const float MaxHoldMs = 500.0f; +static const int MinChargeMs = 200; + +static void setIfNotSet(bool& ref, bool condition) { + ref = (ref || condition); +} + +const int descFrameWidth = 100; + +const int rgbActive = 0xfff0f0f0; +const int rgbInactive = 0xc0635558; +const int rgbInactiveShadow = 0xc0aaaaaa; + +#ifdef __APPLE__ + static const float BorderPixels = 4; + #ifdef DEMO_MODE + static const float BlockPixels = 22; + #else + static const float BlockPixels = 22; + #endif +#else + static const float BorderPixels = 4; + static const float BlockPixels = 24; +#endif +static const int ItemSize = (int)(BlockPixels + 2*BorderPixels); + +static const int Bx = 10; // Border Frame width +static const int By = 6; // Border Frame height + + +FurnaceScreen::FurnaceScreen(Player* player, FurnaceTileEntity* furnace) +: super(new FurnaceMenu(furnace)), //@huge @attn + inventoryPane(NULL), + btnFuel(FurnaceTileEntity::SLOT_FUEL), + btnIngredient(FurnaceTileEntity::SLOT_INGREDIENT), + btnResult(FurnaceTileEntity::SLOT_RESULT), + btnClose(4, ""), + bHeader (5, "Furnace"), + guiBackground(NULL), + guiSlot(NULL), + guiSlotMarked(NULL), + guiSlotMarker(NULL), + guiPaneFrame(NULL), + player(player), + furnace(furnace), + selectedSlot(FurnaceTileEntity::SLOT_INGREDIENT), + doRecreatePane(false), + lastBurnTypeId(0), + descWidth(90) + //guiSlotItem(NULL), + //guiSlotItemSelected(NULL) +{ + //LOGI("Creating FurnaceScreen with %p, %d\n", furnace, furnace->runningId); +} + +FurnaceScreen::~FurnaceScreen() { + clearItems(); + + delete inventoryPane; + + delete guiBackground; + delete guiSlot; + delete guiSlotMarked; + delete guiSlotMarker; + delete guiPaneFrame; + + delete menu; + + if (furnace->clientSideOnly) + delete furnace; +} + +void FurnaceScreen::init() { + super::init(); + //printf("-> %d\n", width/2); + + ImageDef def; + def.name = "gui/spritesheet.png"; + def.x = 0; + def.y = 1; + def.width = def.height = 18; + def.setSrc(IntRectangle(60, 0, 18, 18)); + btnClose.setImageDef(def, true); + btnClose.scaleWhenPressed = false; + + buttons.push_back(&bHeader); + buttons.push_back(&btnIngredient); + buttons.push_back(&btnFuel); + buttons.push_back(&btnResult); + buttons.push_back(&btnClose); + + // GUI - nine patches + NinePatchFactory builder(minecraft->textures, "gui/spritesheet.png"); + + guiBackground = builder.createSymmetrical(IntRectangle(0, 0, 16, 16), 4, 4); + guiSlot = builder.createSymmetrical(IntRectangle(0, 32, 8, 8), 3, 3); + guiSlotMarked = builder.createSymmetrical(IntRectangle(0, 44, 8, 8), 3, 3); + guiSlotMarker = builder.createSymmetrical(IntRectangle(10, 42, 16, 16), 5, 5); + guiPaneFrame = builder.createSymmetrical(IntRectangle(28, 42, 4, 4), 1, 1)->setExcluded(1 << 4); + + recheckRecipes(); +} + +void FurnaceScreen::setupPositions() { + // Left - Categories + bHeader.x = bHeader.y = 0; + bHeader.width = width;// - bDone.w; + + btnClose.width = btnClose.height = 19; + btnClose.x = width - btnClose.width; + btnClose.y = 0; + + // Inventory pane + const int maxWidth = width/2 - Bx - Bx; + const int InventoryColumns = maxWidth / ItemSize; + const int realWidth = InventoryColumns * ItemSize; + const int paneWidth = realWidth + Bx + Bx; + const int realBx = (paneWidth - realWidth) / 2; + + inventoryPaneRect = IntRectangle(realBx, +#ifdef __APPLE__ + 26 + By - ((width==240)?1:0), realWidth, ((width==240)?1:0) + height-By-By-28); +#else + 26 + By, realWidth, height-By-By-28); +#endif + + // Right - Slots, description etc + { + int cx = (inventoryPaneRect.x + inventoryPaneRect.w); + int rightWidth = width - cx; + + btnIngredient.width = btnFuel.width = btnResult.width = (int)guiSlot->getWidth(); + btnIngredient.height = btnFuel.height = btnResult.height = (int)guiSlot->getHeight(); + int space = rightWidth - (2 * btnFuel.width + 40); + int margin = space/2; + int bx0 = cx + margin; + int bx1 = width - margin - btnFuel.width; + btnIngredient.x = btnFuel.x = bx0; + descWidth = (float)Mth::Min(90, width - bx1 + 24 - 4); + + int by = 36;// + (height-20) / 6; + btnIngredient.y = by; + btnFuel.y = by + 20 + btnFuel.height; + + btnResult.x = bx1; + btnResult.y = (btnIngredient.y + btnFuel.y) / 2; + + guiBackground->setSize((float)width, (float)height); + guiSlotMarker->setSize((float)btnFuel.width + 4, (float)btnFuel.height + 4); + + recheckRecipes(); + setupInventoryPane(); + } +} + +void FurnaceScreen::tick() { + if (inventoryPane) + inventoryPane->tick(); + + if (doRecreatePane) { + recheckRecipes(); + setupInventoryPane(); + doRecreatePane = false; + } +} + +void FurnaceScreen::handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a) { + if (pane) { + int ms, id; + pane->markerIndex = -1; + if (pane->queryHoldTime(&id, &ms)) { + heldMs = ms; + + const ItemInstance* item = inventoryItems[id]; + int count = (item && !item->isNull())? item->count : 0; + float maxHoldMs = item? 700 + 10 * item->count: MaxHoldMs; + + if (count > 1 && canMoveToFurnace(id, item)) { + float share = (heldMs-MinChargeMs) / maxHoldMs; + pane->markerType = 1;//(heldMs >= MinChargeMs)? 1 : 0; + pane->markerIndex = id; + pane->markerShare = Mth::Max(share, 0.0f); + + percent = (int)Mth::clamp(100.0f * share, 0.0f, 100.0f); + if (percent >= 100) { + addItem(pane, id); + } + } + } + + pane->render(xm, ym, a); + guiPaneFrame->draw(t, (float)(pane->rect.x - 1), (float)(pane->rect.y - 1)); + //LOGI("query-iv: %d, %d\n", gridId, heldMs); + + } +} + +void FurnaceScreen::render(int xm, int ym, float a) { + const int N = 5; + static StopwatchNLast r(N); + //renderBackground(); + + updateResult(furnace->getItem(FurnaceTileEntity::SLOT_INGREDIENT)); + + Tesselator& t = Tesselator::instance; + guiBackground->draw(t, 0, 0); + glEnable2(GL_ALPHA_TEST); + + // Buttons (Left side + crafting) + super::render(xm, ym, a); + + handleRenderPane(inventoryPane, t, xm, ym, a); + + t.colorABGR(0xffffffff); + + drawSlotItemAt(t, furnace->getItem(btnIngredient.id), btnIngredient.x, btnIngredient.y, btnIngredient.id == selectedSlot); + drawSlotItemAt(t, furnace->getItem(btnFuel.id), btnFuel.x, btnFuel.y, btnFuel.id == selectedSlot); + + const ItemInstance* resultSlotItem = furnace->getItem(btnResult.id); + drawSlotItemAt(t, resultSlotItem, btnResult.x, btnResult.y, btnResult.id == selectedSlot); + + if (!burnResult.isNull()) { + if (!resultSlotItem || resultSlotItem->isNull()) { + glEnable2(GL_BLEND); + t.beginOverride(); + t.colorABGR(0x33ffffff); + t.noColor(); + ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, &burnResult, (float)(btnResult.x + 7), (float)(btnResult.y + 8), true); + t.endOverrideAndDraw(); + glDisable2(GL_BLEND); + } + minecraft->font->drawWordWrap(currentItemDesc, (float)btnResult.x - 24, (float)(btnResult.y + btnResult.height + 6), descWidth, rgbActive); + } + + minecraft->textures->loadAndBindTexture("gui/spritesheet.png"); + int yy = btnResult.y + 8; + int fpx = furnace->getLitProgress(14) + 2; + int xx0 = btnIngredient.x + 8; + blit(xx0, yy, 80, 40, 16, 16, 32, 32); + blit(xx0, yy + 16 - fpx, 112, 40 + 32-fpx-fpx, 16, fpx, 32, fpx+fpx); + + int bpx = furnace->getBurnProgress(24); + int xx1 = btnIngredient.x + 40; + blit(xx1, yy, 144, 40, 24, 16, 48, 32); + blit(xx1, yy, 144, 72, bpx, 16, bpx+bpx, 32); +} + +void FurnaceScreen::buttonClicked(Button* button) { + int slot = button->id; + + if (button == &btnClose) { + minecraft->player->closeContainer(); + } + + if (slot >= FurnaceTileEntity::SLOT_INGREDIENT + && slot <= FurnaceTileEntity::SLOT_RESULT) { + // Can't highlight the Result slot + int oldSlot = selectedSlot; + if (slot != FurnaceTileEntity::SLOT_RESULT) + selectedSlot = slot; + + if (oldSlot == selectedSlot) + takeAndClearSlot(slot); + } +} + +static bool sortCanCraftPredicate(const CItem* a, const CItem* b) { + //if (a->maxBuildCount == 0 && b->maxBuildCount > 0) return false; + //if (b->maxBuildCount == 0 && a->maxBuildCount > 0) return true; + return a->sortText < b->sortText; +} + +void FurnaceScreen::recheckRecipes() +{ + clearItems(); + + Stopwatch w; + w.start(); + + const FurnaceRecipes* recipes = FurnaceRecipes::getInstance(); + ItemPack ip; + // Check for fuel, and items to burn + if (minecraft->player && minecraft->player->inventory) { + Inventory* inv = (minecraft->player)->inventory; + + for (int i = Inventory::MAX_SELECTION_SIZE; i < inv->getContainerSize(); ++i) { + if (ItemInstance* item = inv->getItem(i)) { + // Fuel material + if (FurnaceTileEntity::isFuel(*item)) { + CItem* ci = new CItem(*item, NULL, "");//item->getName()); + //LOGI("Adding fuel: %s\n", item->getName()); + listFuel.push_back(ci); + } + // Ingredient/burn material + if (recipes->isFurnaceItem(item->id)) { + CItem* ci = new CItem(*item, NULL, "");//item->getName()); + //LOGI("Adding item to burn: %s\n", item->getName()); + listIngredient.push_back(ci); + } + //ip.add(ItemPack::getIdForItemInstance(item), item->count); + } + } + } + + ip.print(); + + w.stop(); + w.printEvery(1, "> craft "); + + updateItems(); + //std::stable_sort(_categories[c].begin(), _categories[c].end(), sortCanCraftPredicate); +} + +bool FurnaceScreen::addItem(const Touch::InventoryPane* forPane, int itemIndex) { + //LOGI("items.size, index: %d, %d\n", inventoryItems.size(), itemIndex); + const ItemInstance* item = inventoryItems[itemIndex]; + if (!item || item->isNull()) return false; + + setIfNotSet(doRecreatePane, handleAddItem(selectedSlot, item)); + + if (doRecreatePane) { + int slot = inventorySlots[itemIndex]; + if (player->inventory->getItem(slot)) { + // fix: if we deplete a slot we didn't click on (but had same type), + // we need to NULLify the slot that actually was depleted + for (unsigned int i = 0; i < inventorySlots.size(); ++i) { + slot = inventorySlots[i]; + if (!player->inventory->getItem(slot)) { + LOGI("Changed! removing slot %d (was: %d)\n", i, itemIndex); + itemIndex = i; + break; + } + } + } + inventoryItems[itemIndex] = NULL; + } + //LOGI("Pressed button: %d\n", itemIndexInCurrentCategory); + return true; +} + +bool FurnaceScreen::isAllowed( int slot ) +{ //LOGI("items.size, index: %d, %d\n", inventoryItems.size(), slot); + if (slot >= (int)inventoryItems.size()) return false; + if (!inventoryItems[slot]) return false; + const ItemInstance& item = *inventoryItems[slot]; + + if (selectedSlot == btnFuel.id) + return (FurnaceTileEntity::getBurnDuration(item) > 0); + else + if (selectedSlot == btnIngredient.id) + return !FurnaceRecipes::getInstance()->getResult(item.id).isNull(); + return false; +} + +bool FurnaceScreen::renderGameBehind() +{ + return false; +} + +std::vector FurnaceScreen::getItems( const Touch::InventoryPane* forPane ) +{ + return inventoryItems; +} + +void FurnaceScreen::clearItems() +{ + for (unsigned int i = 0; i < listFuel.size(); ++i) delete listFuel[i]; + for (unsigned int i = 0; i < listIngredient.size(); ++i) delete listIngredient[i]; + listFuel.clear(); + listIngredient.clear(); +} + +void FurnaceScreen::updateItems() { + inventoryItems.clear(); + inventorySlots.clear(); + + ItemList all(listFuel.begin(), listFuel.end()); + all.insert(all.end(), listIngredient.begin(), listIngredient.end()); + for (int i = Inventory::MAX_SELECTION_SIZE; i < minecraft->player->inventory->getContainerSize(); ++i) { + ItemInstance* item = minecraft->player->inventory->getItem(i); + if (!item) continue; + //LOGI("ItemInstance (%p) Id/aux/count: %d, %d, %d\n", item, item->id, item->getAuxValue(), item->count); + bool added = false; + for (unsigned int j = 0; j < listFuel.size(); ++j) { + if (ItemInstance::matches(item, &listFuel[j]->item)) { + inventorySlots.push_back(i); + inventoryItems.push_back(item); + added = true; + break; + } + } + if (added) continue; + for (unsigned int j = 0; j < listIngredient.size(); ++j) { + if (ItemInstance::matches(item, &listIngredient[j]->item)) { + inventorySlots.push_back(i); + inventoryItems.push_back(item); + added = true; + break; + } + } + } +} + +bool FurnaceScreen::canMoveToFurnace(int inventorySlot, const ItemInstance* item) { + if (!isAllowed(inventorySlot)) return false; + ItemInstance* jitem = furnace->getItem(selectedSlot); + if (!jitem || jitem->isNull()) return true; + if (ItemInstance::isStackable(item, jitem) && jitem->count < jitem->getMaxStackSize()) + return true; + + return false; +} + +void FurnaceScreen::updateResult( const ItemInstance* item ) +{ + const ItemInstance* result = furnace->getItem(FurnaceTileEntity::SLOT_RESULT); + if (!result->isNull()) { + int id = result->id; + if (id == lastBurnTypeId) return; + currentItemDesc = I18n::getDescriptionString(*result); + lastBurnTypeId = id; + this->burnResult = *result; + } else { + int id = (item? item->id : 0); + if (id == lastBurnTypeId) return; + + ItemInstance burnResult = FurnaceRecipes::getInstance()->getResult(id); + if (!burnResult.isNull()) + currentItemDesc = I18n::getDescriptionString(burnResult); + else + currentItemDesc = ""; + lastBurnTypeId = id; + this->burnResult = burnResult; + } +} + +void FurnaceScreen::setupInventoryPane() +{ + // IntRectangle(0, 0, 100, 100) + if (inventoryPane) delete inventoryPane; + inventoryPane = new Touch::InventoryPane(this, minecraft, inventoryPaneRect, inventoryPaneRect.w, BorderPixels, inventoryItems.size(), ItemSize, (int)BorderPixels); + inventoryPane->fillMarginX = 0; + inventoryPane->fillMarginY = 0; + guiPaneFrame->setSize((float)inventoryPaneRect.w + 2, (float)inventoryPaneRect.h + 2); + //LOGI("Creating new pane: %d %p\n", inventoryItems.size(), inventoryPane); +} + +void FurnaceScreen::drawSlotItemAt( Tesselator& t, const ItemInstance* item, int x, int y, bool selected) +{ + float xx = (float)x; + float yy = (float)y; + + (selected? guiSlot/*Marked*/ : guiSlot)->draw(t, xx, yy); + + if (selected) + guiSlotMarker->draw(t, xx - 2, yy - 2); + + if (item && !item->isNull()) { + ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, xx + 7, yy + 8, true); + minecraft->gui.renderSlotText(item, xx + 3, yy + 3, true, true); + } +} + +ItemInstance FurnaceScreen::moveOver(const ItemInstance* item, int maxCount) { + int wantedCount = item->count * percent / 100; + if (!wantedCount || heldMs < MinChargeMs) + wantedCount = 1; + + wantedCount = Mth::Min(wantedCount, maxCount); + + ItemInstance removed(item->id, wantedCount, item->getAuxValue()); + int oldSize = minecraft->player->inventory->getNumEmptySlots(); + if (minecraft->player->inventory->removeResource(removed)) { + int newSize = minecraft->player->inventory->getNumEmptySlots(); + setIfNotSet(doRecreatePane, newSize != oldSize); + return removed; + } + return ItemInstance(); +} + +void FurnaceScreen::takeAndClearSlot( int slot ) +{ + //if (selectedSlot == btnBurn.id && !furnace->isSlotEmpty(btnBurn.id)) + ItemInstance oldItem = *furnace->getItem(slot); + ItemInstance blank; + + furnace->setItem(slot, &blank); + if (minecraft->level->isClientSide) { + ContainerSetSlotPacket p(menu->containerId, slot, blank); + minecraft->raknetInstance->send(p); + } + + int oldSize = minecraft->player->inventory->getNumEmptySlots(); + + if (!minecraft->player->inventory->add(&oldItem)) + minecraft->player->drop(new ItemInstance(oldItem), false); + + int newSize = minecraft->player->inventory->getNumEmptySlots(); + setIfNotSet(doRecreatePane, newSize != oldSize); +} + +bool FurnaceScreen::handleAddItem( int slot, const ItemInstance* item ) +{ + ItemInstance* furnaceItem = furnace->getItem(slot); + int oldSize = minecraft->player->inventory->getNumEmptySlots(); + + if (item->id == furnaceItem->id) { + // If stackable, stack them! Else deny the addition + const int maxMovedCount = furnaceItem->getMaxStackSize() - furnaceItem->count; + if (maxMovedCount <= 0) + return false; + + ItemInstance added = moveOver(item, maxMovedCount); + furnaceItem->count += added.count; + } else { + if (!furnace->isSlotEmpty(slot)) + return false;//takeAndClearSlot(slot); + + ItemInstance moved = moveOver(item, item->getMaxStackSize()); + player->containerMenu->setSlot(slot, &moved); + } + + if (minecraft->level->isClientSide) { + ContainerSetSlotPacket p(menu->containerId, slot, *furnaceItem); + minecraft->raknetInstance->send(p); + } + + int newSize = minecraft->player->inventory->getNumEmptySlots(); + return (newSize != oldSize); +} diff --git a/src/client/gui/screens/FurnaceScreen.h b/src/client/gui/screens/FurnaceScreen.h index d359abd..6d2ff65 100755 --- a/src/client/gui/screens/FurnaceScreen.h +++ b/src/client/gui/screens/FurnaceScreen.h @@ -1,84 +1,84 @@ -#ifndef _FURNACESCREEN_H__ -#define _FURNACESCREEN_H__ - -#include "BaseContainerScreen.h" - -#include "../components/InventoryPane.h" -#include "../components/Button.h" - -class Font; -class CItem; -class Textures; -class NinePatchLayer; -class Tesselator; - -class FurnaceScreen: public BaseContainerScreen, - public Touch::IInventoryPaneCallback -{ - typedef BaseContainerScreen super; - typedef std::vector ItemList; -public: - FurnaceScreen(Player* player, FurnaceTileEntity* furnace); - ~FurnaceScreen(); - - void init(); - void setupPositions(); - - void tick(); - void render(int xm, int ym, float a); - bool renderGameBehind(); - void buttonClicked(Button* button); - - // IInventoryPaneCallback - bool addItem(const Touch::InventoryPane* pane, int itemId); - bool isAllowed( int slot ); - std::vector getItems( const Touch::InventoryPane* forPane ); -private: - //void addItem(Recipe* recipe); - void recheckRecipes(); - - void clearItems(); - void updateResult(const ItemInstance* item); - void setupInventoryPane(); - void updateItems(); - - void drawSlotItemAt(Tesselator& t, const ItemInstance* item, int x, int y, bool selected); - ItemInstance moveOver(const ItemInstance* item, int maxCount); - void takeAndClearSlot( int slot ); - bool handleAddItem( int slot, const ItemInstance* item ); - void handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a); - bool canMoveToFurnace(int inventorySlot, const ItemInstance* item); - ItemList _items; - - std::string currentItemDesc; - ItemInstance burnResult; - float descWidth; - ImageButton btnClose; - BlankButton btnIngredient; - BlankButton btnFuel; - BlankButton btnResult; - Touch::THeader bHeader; - - Touch::InventoryPane* inventoryPane; - IntRectangle inventoryPaneRect; - - ItemList listFuel; - ItemList listIngredient; - std::vector inventorySlots; - std::vector inventoryItems; - bool doRecreatePane; - - int selectedSlot; - int lastBurnTypeId; - - // GUI elements such as 9-Patches - NinePatchLayer* guiBackground; - NinePatchLayer* guiSlot; - NinePatchLayer* guiSlotMarked; - NinePatchLayer* guiSlotMarker; - NinePatchLayer* guiPaneFrame; - Player* player; - FurnaceTileEntity* furnace; -}; - -#endif /*_FURNACESCREEN_H__*/ +#ifndef _FURNACESCREEN_H__ +#define _FURNACESCREEN_H__ + +#include "BaseContainerScreen.h" + +#include "../components/InventoryPane.h" +#include "../components/Button.h" + +class Font; +class CItem; +class Textures; +class NinePatchLayer; +class Tesselator; + +class FurnaceScreen: public BaseContainerScreen, + public Touch::IInventoryPaneCallback +{ + typedef BaseContainerScreen super; + typedef std::vector ItemList; +public: + FurnaceScreen(Player* player, FurnaceTileEntity* furnace); + ~FurnaceScreen(); + + void init(); + void setupPositions(); + + void tick(); + void render(int xm, int ym, float a); + bool renderGameBehind(); + void buttonClicked(Button* button); + + // IInventoryPaneCallback + bool addItem(const Touch::InventoryPane* pane, int itemId); + bool isAllowed( int slot ); + std::vector getItems( const Touch::InventoryPane* forPane ); +private: + //void addItem(Recipe* recipe); + void recheckRecipes(); + + void clearItems(); + void updateResult(const ItemInstance* item); + void setupInventoryPane(); + void updateItems(); + + void drawSlotItemAt(Tesselator& t, const ItemInstance* item, int x, int y, bool selected); + ItemInstance moveOver(const ItemInstance* item, int maxCount); + void takeAndClearSlot( int slot ); + bool handleAddItem( int slot, const ItemInstance* item ); + void handleRenderPane(Touch::InventoryPane* pane, Tesselator& t, int xm, int ym, float a); + bool canMoveToFurnace(int inventorySlot, const ItemInstance* item); + ItemList _items; + + std::string currentItemDesc; + ItemInstance burnResult; + float descWidth; + ImageButton btnClose; + BlankButton btnIngredient; + BlankButton btnFuel; + BlankButton btnResult; + Touch::THeader bHeader; + + Touch::InventoryPane* inventoryPane; + IntRectangle inventoryPaneRect; + + ItemList listFuel; + ItemList listIngredient; + std::vector inventorySlots; + std::vector inventoryItems; + bool doRecreatePane; + + int selectedSlot; + int lastBurnTypeId; + + // GUI elements such as 9-Patches + NinePatchLayer* guiBackground; + NinePatchLayer* guiSlot; + NinePatchLayer* guiSlotMarked; + NinePatchLayer* guiSlotMarker; + NinePatchLayer* guiPaneFrame; + Player* player; + FurnaceTileEntity* furnace; +}; + +#endif /*_FURNACESCREEN_H__*/ diff --git a/src/client/gui/screens/InBedScreen.cpp b/src/client/gui/screens/InBedScreen.cpp index 3adadc7..543da22 100755 --- a/src/client/gui/screens/InBedScreen.cpp +++ b/src/client/gui/screens/InBedScreen.cpp @@ -1,49 +1,49 @@ -#include "InBedScreen.h" -#include "ScreenChooser.h" -#include "../components/Button.h" -#include "../../Minecraft.h" -#include "../../player/LocalPlayer.h" -#include "../../../platform/time.h" - -static const int WAIT_TICKS = 30; - -InBedScreen::InBedScreen() - : bWakeUp(0) -{ -} - -InBedScreen::~InBedScreen() { - delete bWakeUp; -} - -void InBedScreen::init() { - if (/* minecraft->useTouchscreen() */ true) { - bWakeUp = new Touch::TButton(1, "Leave Bed"); - } else { - bWakeUp = new Button(1, "Leave Bed"); - } - buttons.push_back(bWakeUp); - - tabButtons.push_back(bWakeUp); -} - -void InBedScreen::setupPositions() { - bWakeUp->width = width / 2; - bWakeUp->height = int(height * 0.2f); - bWakeUp->y = height - int(bWakeUp->height * 1.5); - bWakeUp->x = width/2 - bWakeUp->width/2; -} - -void InBedScreen::render( int xm, int ym, float a ) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Screen::render(xm, ym, a); - glDisable(GL_BLEND); -} - -void InBedScreen::buttonClicked( Button* button ) { - if (button == bWakeUp) { - minecraft->player->stopSleepInBed(true, true, true); - minecraft->setScreen(NULL); - } -} +#include "InBedScreen.h" +#include "ScreenChooser.h" +#include "../components/Button.h" +#include "../../Minecraft.h" +#include "../../player/LocalPlayer.h" +#include "../../../platform/time.h" + +static const int WAIT_TICKS = 30; + +InBedScreen::InBedScreen() + : bWakeUp(0) +{ +} + +InBedScreen::~InBedScreen() { + delete bWakeUp; +} + +void InBedScreen::init() { + if (/* minecraft->useTouchscreen() */ true) { + bWakeUp = new Touch::TButton(1, "Leave Bed"); + } else { + bWakeUp = new Button(1, "Leave Bed"); + } + buttons.push_back(bWakeUp); + + tabButtons.push_back(bWakeUp); +} + +void InBedScreen::setupPositions() { + bWakeUp->width = width / 2; + bWakeUp->height = int(height * 0.2f); + bWakeUp->y = height - int(bWakeUp->height * 1.5); + bWakeUp->x = width/2 - bWakeUp->width/2; +} + +void InBedScreen::render( int xm, int ym, float a ) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Screen::render(xm, ym, a); + glDisable(GL_BLEND); +} + +void InBedScreen::buttonClicked( Button* button ) { + if (button == bWakeUp) { + minecraft->player->stopSleepInBed(true, true, true); + minecraft->setScreen(NULL); + } +} diff --git a/src/client/gui/screens/InBedScreen.h b/src/client/gui/screens/InBedScreen.h index 1004088..b577a12 100755 --- a/src/client/gui/screens/InBedScreen.h +++ b/src/client/gui/screens/InBedScreen.h @@ -1,26 +1,26 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__InBedScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__InBedScreen_H__ - -#include "../Screen.h" -class Button; - -class InBedScreen: public Screen -{ -public: - InBedScreen(); - - virtual ~InBedScreen(); - - void init(); - - void setupPositions(); - - void render(int xm, int ym, float a); - - void buttonClicked(Button* button); - -private: - Button* bWakeUp; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__InBedScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__InBedScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__InBedScreen_H__ + +#include "../Screen.h" +class Button; + +class InBedScreen: public Screen +{ +public: + InBedScreen(); + + virtual ~InBedScreen(); + + void init(); + + void setupPositions(); + + void render(int xm, int ym, float a); + + void buttonClicked(Button* button); + +private: + Button* bWakeUp; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__InBedScreen_H__*/ diff --git a/src/client/gui/screens/IngameBlockSelectionScreen.cpp b/src/client/gui/screens/IngameBlockSelectionScreen.cpp index 863b1f8..85a3302 100755 --- a/src/client/gui/screens/IngameBlockSelectionScreen.cpp +++ b/src/client/gui/screens/IngameBlockSelectionScreen.cpp @@ -1,341 +1,341 @@ -#include "IngameBlockSelectionScreen.h" -#include "../../renderer/TileRenderer.h" -#include "../../player/LocalPlayer.h" -#include "../../renderer/gles.h" -#include "../../Minecraft.h" -#include "../../sound/SoundEngine.h" -#include "../../../world/entity/player/Inventory.h" -#include "../../../platform/input/Mouse.h" - -#include "../Gui.h" -#include "../../renderer/Textures.h" -#include "../../gamemode/GameMode.h" -#include "ArmorScreen.h" -#include "../components/Button.h" - -#if defined(__APPLE__) - static const std::string demoVersionString("Not available in the Lite version"); -#else - static const std::string demoVersionString("Not available in the demo version"); -#endif - -IngameBlockSelectionScreen::IngameBlockSelectionScreen() -: selectedItem(0), - _area(0,0,0,0), - _pendingQuit(false), - InventoryRows(1), - InventoryCols(1), - InventorySize(1), - bArmor(1, "Armor") -{ -} - -void IngameBlockSelectionScreen::init() -{ - Inventory* inventory = minecraft->player->inventory; - InventoryCols = minecraft->isCreativeMode()? 13 : 9; - InventorySize = inventory->getContainerSize() - Inventory::MAX_SELECTION_SIZE; - InventoryRows = 1 + (InventorySize - 1) / InventoryCols; - - _area = RectangleArea( (float)getSlotPosX(0) - 4, - (float)getSlotPosY(0) - 4, - (float)getSlotPosX(InventoryCols) + 4, - (float)getSlotPosY(InventoryRows) + 4); - - ItemInstance* selected = inventory->getSelected(); - if (!selected || selected->isNull()) { - selectedItem = 0; - return; - } - - for (int i = Inventory::MAX_SELECTION_SIZE; i < InventorySize; i++) { - if (selected == minecraft->player->inventory->getItem(i)) - { - selectedItem = i - Inventory::MAX_SELECTION_SIZE; - break; - } - } - if (!isAllowed(selectedItem)) - selectedItem = 0; - - if (!minecraft->isCreativeMode()) { - bArmor.width = 42; - bArmor.x = 0; - bArmor.y = height - bArmor.height; - buttons.push_back(&bArmor); - } -} - -void IngameBlockSelectionScreen::removed() -{ - minecraft->gui.inventoryUpdated(); -} - -void IngameBlockSelectionScreen::renderSlots() -{ - //static Stopwatch w; - //w.start(); - - glColor4f2(1, 1, 1, 1); - - blitOffset = -90; - - //glEnable2(GL_RESCALE_NORMAL); - //glPushMatrix2(); - //glRotatef2(180, 1, 0, 0); - //Lighting::turnOn(); - //glPopMatrix2(); - - minecraft->textures->loadAndBindTexture("gui/gui.png"); - for (int r = 0; r < InventoryRows; r++) - { - int x = getSlotPosX(0) - 3; - int y = getSlotPosY(r) - 3; - - if (InventoryCols == 9) { - blit(x, y, 0, 0, 182, 22); - } else { - // first 8 slots - blit(x, y, 0, 0, 182-20, 22); - // last k slots - const int k = 5; - const int w = k * 20; - blit(x + 162, y, 182-w, 0, w, 22); - } - } - if (selectedItem >= 0) - { - int x = getSlotPosX(selectedItem % InventoryCols) - 4;// width / 2 - 182 / 2 - 1 + () * 20; - int y = getSlotPosY(selectedItem / InventoryCols) - 4;// height - 22 * 3 - 1 - (selectedItem / InventoryCols) * 22; - blit(x, y, 0, 22, 24, 22); - } - - for (int r = 0; r < InventoryRows; r++) - { - int y = getSlotPosY(r); - for (int i = 0; i < InventoryCols; i++) { - int x = getSlotPosX(i); - renderSlot(r * InventoryCols + i + Inventory::MAX_SELECTION_SIZE, x, y, 0); - } - } - - //w.stop(); - //w.printEvery(1000, "render-blocksel"); - - //glDisable2(GL_RESCALE_NORMAL); - //Lighting::turnOn(); -} - -int IngameBlockSelectionScreen::getSlotPosX(int slotX) { - return width / 2 - InventoryCols * 10 + slotX * 20 + 2; -} - -int IngameBlockSelectionScreen::getSlotPosY(int slotY) { - //return height - 63 - 22 * (3 - slotY); - int yy = InventoryCols==9? 8 : 3; - return yy + slotY * getSlotHeight(); -} - -//int IngameBlockSelectionScreen::getLinearSlotId(int x, int y) { -// return -//} - - -#include "../../../world/item/ItemInstance.h" -#include "../../renderer/entity/ItemRenderer.h" - -void IngameBlockSelectionScreen::renderSlot(int slot, int x, int y, float a) -{ - ItemInstance* item = minecraft->player->inventory->getItem(slot); - if (!item) return; - - ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, (float)x, (float)y, true); - - if (minecraft->gameMode->isCreativeType()) return; - if (!isAllowed(slot - Inventory::MAX_SELECTION_SIZE)) return; - - glPushMatrix2(); - glScalef2(Gui::InvGuiScale + Gui::InvGuiScale, Gui::InvGuiScale + Gui::InvGuiScale, 1); - const float k = 0.5f * Gui::GuiScale; - minecraft->gui.renderSlotText(item, k*x, k*y, true, true); - glPopMatrix2(); -} - -void IngameBlockSelectionScreen::keyPressed(int eventKey) -{ - int selX = selectedItem % InventoryCols; - int selY = selectedItem / InventoryCols; - - int tmpSelectedSlot = selectedItem; - - Options& o = minecraft->options; - if (eventKey == o.getIntValue(OPTIONS_KEY_LEFT) && selX > 0) - { - tmpSelectedSlot -= 1; - } - else if (eventKey == o.getIntValue(OPTIONS_KEY_RIGHT) && selX < (InventoryCols - 1)) - { - tmpSelectedSlot += 1; - } - else if (eventKey == o.getIntValue(OPTIONS_KEY_BACK) && selY < (InventoryRows - 1)) - { - tmpSelectedSlot += InventoryCols; - } - else if (eventKey == o.getIntValue(OPTIONS_KEY_FORWARD) && selY > 0) - { - tmpSelectedSlot -= InventoryCols; - } - - if (isAllowed(tmpSelectedSlot)) - selectedItem = tmpSelectedSlot; - - if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_OK)) - selectSlotAndClose(); - -#ifdef RPI - if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_CANCEL) - || eventKey == Keyboard::KEY_ESCAPE) - minecraft->setScreen(NULL); -#else - if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_CANCEL)) - minecraft->setScreen(NULL); -#endif -} - -//------------------------------------------------------------------------------ -// wheel support for creative inventory; scroll moves selection vertically -void IngameBlockSelectionScreen::mouseWheel(int dx, int dy, int xm, int ym) -{ - if (dy == 0) return; - // just move selection up/down one row; desktop UI doesn't have a pane - int cols = InventoryCols; - int maxIndex = InventorySize - 1; - int idx = selectedItem; - if (dy > 0) { - // wheel up -> previous row - if (idx >= cols) idx -= cols; - } else { - // wheel down -> next row - if (idx + cols <= maxIndex) idx += cols; - } - selectedItem = idx; -} - -int IngameBlockSelectionScreen::getSelectedSlot(int x, int y) -{ - int left = width / 2 - InventoryCols * 10; - int top = -4 + getSlotPosY(0); - - if (x >= left && y >= top) - { - int xSlot = (x - left) / 20; - if (xSlot < InventoryCols) { - int row = ((y-top) / getSlotHeight()); - return row * InventoryCols + xSlot; - } - } - return -1; -} - -void IngameBlockSelectionScreen::mouseClicked(int x, int y, int buttonNum) -{ - if (buttonNum == MouseAction::ACTION_LEFT) { - - int slot = getSelectedSlot(x, y); - if (isAllowed(slot)) - { - selectedItem = slot; - //minecraft->soundEngine->playUI("random.click", 1, 1); - } else { - _pendingQuit = !_area.isInside((float)x, (float)y) - && !bArmor.isInside(x, y); - } - } - if (!_pendingQuit) - super::mouseClicked(x, y, buttonNum); -} - -void IngameBlockSelectionScreen::mouseReleased(int x, int y, int buttonNum) -{ - if (buttonNum == MouseAction::ACTION_LEFT) { - - int slot = getSelectedSlot(x, y); - if (isAllowed(slot) && slot == selectedItem) - { - selectSlotAndClose(); - } else { - if (_pendingQuit && !_area.isInside((float)x, (float)y)) - minecraft->setScreen(NULL); - } - } - if (!_pendingQuit) - super::mouseReleased(x, y, buttonNum); -} - -void IngameBlockSelectionScreen::selectSlotAndClose() -{ - Inventory* inventory = minecraft->player->inventory; - // Flash the selected gui item - //inventory->moveToSelectedSlot(selectedItem + Inventory::MAX_SELECTION_SIZE, true); - inventory->moveToSelectionSlot(0, selectedItem + Inventory::MAX_SELECTION_SIZE, true); - inventory->selectSlot(0); - minecraft->gui.flashSlot(inventory->selected); - - minecraft->soundEngine->playUI("random.click", 1, 1); - minecraft->setScreen(NULL); -} - -void IngameBlockSelectionScreen::render( int xm, int ym, float a ) -{ - glDisable2(GL_DEPTH_TEST); - fill(0, 0, width, height, (0x80) << 24); - glEnable2(GL_BLEND); - - glDisable2(GL_ALPHA_TEST); - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - renderSlots(); - renderDemoOverlay(); - - glEnable2(GL_ALPHA_TEST); - glDisable2(GL_BLEND); - - glEnable2(GL_DEPTH_TEST); - - Screen::render(xm, ym, a); -} - -void IngameBlockSelectionScreen::renderDemoOverlay() { -#ifdef DEMO_MODE - fill( getSlotPosX(0) - 3, getSlotPosY(3) - 3, - getSlotPosX(InventoryCols) - 3, getSlotPosY(InventoryRows) - 3, 0xa0 << 24); - - const int centerX = (getSlotPosX(4) + getSlotPosX(5)) / 2; - const int centerY = (getSlotPosY(3) + getSlotPosY(InventoryRows-1)) / 2 + 5; - drawCenteredString(minecraft->font, demoVersionString, centerX, centerY, 0xffffffff); -#endif /*DEMO_MODE*/ -} - -bool IngameBlockSelectionScreen::isAllowed(int slot) { - if (slot < 0 || slot >= InventorySize) - return false; - - #ifdef DEMO_MODE - return slot < (minecraft->isCreativeMode()? 28 : 27); - #endif /*DEMO_MODE*/ - - return true; -} - -int IngameBlockSelectionScreen::getSlotHeight() { - return InventoryCols==9? 22 : 20; -} - -void IngameBlockSelectionScreen::buttonClicked( Button* button ) -{ - if (button == &bArmor) { - minecraft->setScreen(new ArmorScreen()); - } - super::buttonClicked(button); -} +#include "IngameBlockSelectionScreen.h" +#include "../../renderer/TileRenderer.h" +#include "../../player/LocalPlayer.h" +#include "../../renderer/gles.h" +#include "../../Minecraft.h" +#include "../../sound/SoundEngine.h" +#include "../../../world/entity/player/Inventory.h" +#include "../../../platform/input/Mouse.h" + +#include "../Gui.h" +#include "../../renderer/Textures.h" +#include "../../gamemode/GameMode.h" +#include "ArmorScreen.h" +#include "../components/Button.h" + +#if defined(__APPLE__) + static const std::string demoVersionString("Not available in the Lite version"); +#else + static const std::string demoVersionString("Not available in the demo version"); +#endif + +IngameBlockSelectionScreen::IngameBlockSelectionScreen() +: selectedItem(0), + _area(0,0,0,0), + _pendingQuit(false), + InventoryRows(1), + InventoryCols(1), + InventorySize(1), + bArmor(1, "Armor") +{ +} + +void IngameBlockSelectionScreen::init() +{ + Inventory* inventory = minecraft->player->inventory; + InventoryCols = minecraft->isCreativeMode()? 13 : 9; + InventorySize = inventory->getContainerSize() - Inventory::MAX_SELECTION_SIZE; + InventoryRows = 1 + (InventorySize - 1) / InventoryCols; + + _area = RectangleArea( (float)getSlotPosX(0) - 4, + (float)getSlotPosY(0) - 4, + (float)getSlotPosX(InventoryCols) + 4, + (float)getSlotPosY(InventoryRows) + 4); + + ItemInstance* selected = inventory->getSelected(); + if (!selected || selected->isNull()) { + selectedItem = 0; + return; + } + + for (int i = Inventory::MAX_SELECTION_SIZE; i < InventorySize; i++) { + if (selected == minecraft->player->inventory->getItem(i)) + { + selectedItem = i - Inventory::MAX_SELECTION_SIZE; + break; + } + } + if (!isAllowed(selectedItem)) + selectedItem = 0; + + if (!minecraft->isCreativeMode()) { + bArmor.width = 42; + bArmor.x = 0; + bArmor.y = height - bArmor.height; + buttons.push_back(&bArmor); + } +} + +void IngameBlockSelectionScreen::removed() +{ + minecraft->gui.inventoryUpdated(); +} + +void IngameBlockSelectionScreen::renderSlots() +{ + //static Stopwatch w; + //w.start(); + + glColor4f2(1, 1, 1, 1); + + blitOffset = -90; + + //glEnable2(GL_RESCALE_NORMAL); + //glPushMatrix2(); + //glRotatef2(180, 1, 0, 0); + //Lighting::turnOn(); + //glPopMatrix2(); + + minecraft->textures->loadAndBindTexture("gui/gui.png"); + for (int r = 0; r < InventoryRows; r++) + { + int x = getSlotPosX(0) - 3; + int y = getSlotPosY(r) - 3; + + if (InventoryCols == 9) { + blit(x, y, 0, 0, 182, 22); + } else { + // first 8 slots + blit(x, y, 0, 0, 182-20, 22); + // last k slots + const int k = 5; + const int w = k * 20; + blit(x + 162, y, 182-w, 0, w, 22); + } + } + if (selectedItem >= 0) + { + int x = getSlotPosX(selectedItem % InventoryCols) - 4;// width / 2 - 182 / 2 - 1 + () * 20; + int y = getSlotPosY(selectedItem / InventoryCols) - 4;// height - 22 * 3 - 1 - (selectedItem / InventoryCols) * 22; + blit(x, y, 0, 22, 24, 22); + } + + for (int r = 0; r < InventoryRows; r++) + { + int y = getSlotPosY(r); + for (int i = 0; i < InventoryCols; i++) { + int x = getSlotPosX(i); + renderSlot(r * InventoryCols + i + Inventory::MAX_SELECTION_SIZE, x, y, 0); + } + } + + //w.stop(); + //w.printEvery(1000, "render-blocksel"); + + //glDisable2(GL_RESCALE_NORMAL); + //Lighting::turnOn(); +} + +int IngameBlockSelectionScreen::getSlotPosX(int slotX) { + return width / 2 - InventoryCols * 10 + slotX * 20 + 2; +} + +int IngameBlockSelectionScreen::getSlotPosY(int slotY) { + //return height - 63 - 22 * (3 - slotY); + int yy = InventoryCols==9? 8 : 3; + return yy + slotY * getSlotHeight(); +} + +//int IngameBlockSelectionScreen::getLinearSlotId(int x, int y) { +// return +//} + + +#include "../../../world/item/ItemInstance.h" +#include "../../renderer/entity/ItemRenderer.h" + +void IngameBlockSelectionScreen::renderSlot(int slot, int x, int y, float a) +{ + ItemInstance* item = minecraft->player->inventory->getItem(slot); + if (!item) return; + + ItemRenderer::renderGuiItem(minecraft->font, minecraft->textures, item, (float)x, (float)y, true); + + if (minecraft->gameMode->isCreativeType()) return; + if (!isAllowed(slot - Inventory::MAX_SELECTION_SIZE)) return; + + glPushMatrix2(); + glScalef2(Gui::InvGuiScale + Gui::InvGuiScale, Gui::InvGuiScale + Gui::InvGuiScale, 1); + const float k = 0.5f * Gui::GuiScale; + minecraft->gui.renderSlotText(item, k*x, k*y, true, true); + glPopMatrix2(); +} + +void IngameBlockSelectionScreen::keyPressed(int eventKey) +{ + int selX = selectedItem % InventoryCols; + int selY = selectedItem / InventoryCols; + + int tmpSelectedSlot = selectedItem; + + Options& o = minecraft->options; + if (eventKey == o.getIntValue(OPTIONS_KEY_LEFT) && selX > 0) + { + tmpSelectedSlot -= 1; + } + else if (eventKey == o.getIntValue(OPTIONS_KEY_RIGHT) && selX < (InventoryCols - 1)) + { + tmpSelectedSlot += 1; + } + else if (eventKey == o.getIntValue(OPTIONS_KEY_BACK) && selY < (InventoryRows - 1)) + { + tmpSelectedSlot += InventoryCols; + } + else if (eventKey == o.getIntValue(OPTIONS_KEY_FORWARD) && selY > 0) + { + tmpSelectedSlot -= InventoryCols; + } + + if (isAllowed(tmpSelectedSlot)) + selectedItem = tmpSelectedSlot; + + if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_OK)) + selectSlotAndClose(); + +#ifdef RPI + if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_CANCEL) + || eventKey == Keyboard::KEY_ESCAPE) + minecraft->setScreen(NULL); +#else + if (eventKey == o.getIntValue(OPTIONS_KEY_MENU_CANCEL)) + minecraft->setScreen(NULL); +#endif +} + +//------------------------------------------------------------------------------ +// wheel support for creative inventory; scroll moves selection vertically +void IngameBlockSelectionScreen::mouseWheel(int dx, int dy, int xm, int ym) +{ + if (dy == 0) return; + // just move selection up/down one row; desktop UI doesn't have a pane + int cols = InventoryCols; + int maxIndex = InventorySize - 1; + int idx = selectedItem; + if (dy > 0) { + // wheel up -> previous row + if (idx >= cols) idx -= cols; + } else { + // wheel down -> next row + if (idx + cols <= maxIndex) idx += cols; + } + selectedItem = idx; +} + +int IngameBlockSelectionScreen::getSelectedSlot(int x, int y) +{ + int left = width / 2 - InventoryCols * 10; + int top = -4 + getSlotPosY(0); + + if (x >= left && y >= top) + { + int xSlot = (x - left) / 20; + if (xSlot < InventoryCols) { + int row = ((y-top) / getSlotHeight()); + return row * InventoryCols + xSlot; + } + } + return -1; +} + +void IngameBlockSelectionScreen::mouseClicked(int x, int y, int buttonNum) +{ + if (buttonNum == MouseAction::ACTION_LEFT) { + + int slot = getSelectedSlot(x, y); + if (isAllowed(slot)) + { + selectedItem = slot; + //minecraft->soundEngine->playUI("random.click", 1, 1); + } else { + _pendingQuit = !_area.isInside((float)x, (float)y) + && !bArmor.isInside(x, y); + } + } + if (!_pendingQuit) + super::mouseClicked(x, y, buttonNum); +} + +void IngameBlockSelectionScreen::mouseReleased(int x, int y, int buttonNum) +{ + if (buttonNum == MouseAction::ACTION_LEFT) { + + int slot = getSelectedSlot(x, y); + if (isAllowed(slot) && slot == selectedItem) + { + selectSlotAndClose(); + } else { + if (_pendingQuit && !_area.isInside((float)x, (float)y)) + minecraft->setScreen(NULL); + } + } + if (!_pendingQuit) + super::mouseReleased(x, y, buttonNum); +} + +void IngameBlockSelectionScreen::selectSlotAndClose() +{ + Inventory* inventory = minecraft->player->inventory; + // Flash the selected gui item + //inventory->moveToSelectedSlot(selectedItem + Inventory::MAX_SELECTION_SIZE, true); + inventory->moveToSelectionSlot(0, selectedItem + Inventory::MAX_SELECTION_SIZE, true); + inventory->selectSlot(0); + minecraft->gui.flashSlot(inventory->selected); + + minecraft->soundEngine->playUI("random.click", 1, 1); + minecraft->setScreen(NULL); +} + +void IngameBlockSelectionScreen::render( int xm, int ym, float a ) +{ + glDisable2(GL_DEPTH_TEST); + fill(0, 0, width, height, (0x80) << 24); + glEnable2(GL_BLEND); + + glDisable2(GL_ALPHA_TEST); + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + renderSlots(); + renderDemoOverlay(); + + glEnable2(GL_ALPHA_TEST); + glDisable2(GL_BLEND); + + glEnable2(GL_DEPTH_TEST); + + Screen::render(xm, ym, a); +} + +void IngameBlockSelectionScreen::renderDemoOverlay() { +#ifdef DEMO_MODE + fill( getSlotPosX(0) - 3, getSlotPosY(3) - 3, + getSlotPosX(InventoryCols) - 3, getSlotPosY(InventoryRows) - 3, 0xa0 << 24); + + const int centerX = (getSlotPosX(4) + getSlotPosX(5)) / 2; + const int centerY = (getSlotPosY(3) + getSlotPosY(InventoryRows-1)) / 2 + 5; + drawCenteredString(minecraft->font, demoVersionString, centerX, centerY, 0xffffffff); +#endif /*DEMO_MODE*/ +} + +bool IngameBlockSelectionScreen::isAllowed(int slot) { + if (slot < 0 || slot >= InventorySize) + return false; + + #ifdef DEMO_MODE + return slot < (minecraft->isCreativeMode()? 28 : 27); + #endif /*DEMO_MODE*/ + + return true; +} + +int IngameBlockSelectionScreen::getSlotHeight() { + return InventoryCols==9? 22 : 20; +} + +void IngameBlockSelectionScreen::buttonClicked( Button* button ) +{ + if (button == &bArmor) { + minecraft->setScreen(new ArmorScreen()); + } + super::buttonClicked(button); +} diff --git a/src/client/gui/screens/IngameBlockSelectionScreen.h b/src/client/gui/screens/IngameBlockSelectionScreen.h index affd6af..d3eb810 100755 --- a/src/client/gui/screens/IngameBlockSelectionScreen.h +++ b/src/client/gui/screens/IngameBlockSelectionScreen.h @@ -1,59 +1,59 @@ -#ifndef _MINECRAFT_INGAMEBLOCKSELECTIONSCREEN_H_ -#define _MINECRAFT_INGAMEBLOCKSELECTIONSCREEN_H_ - -#include "../Screen.h" -#include "../../player/input/touchscreen/TouchAreaModel.h" -#include "../components/Button.h" - -class IngameBlockSelectionScreen : public Screen -{ - typedef Screen super; -public: - IngameBlockSelectionScreen(); - virtual ~IngameBlockSelectionScreen() {} - - virtual void init() override; - virtual void removed() override; - - void render(int xm, int ym, float a) override; - -protected: - virtual void mouseClicked(int x, int y, int buttonNum) override; - virtual void mouseReleased(int x, int y, int buttonNum) override; - - virtual void buttonClicked(Button* button) override; - - // wheel input for creative inventory scrolling - virtual void mouseWheel(int dx, int dy, int xm, int ym) override; - - virtual void keyPressed(int eventKey) override; -private: - void renderSlots(); - void renderSlot(int slot, int x, int y, float a); - void renderDemoOverlay(); - - int getSelectedSlot(int x, int y); - void selectSlotAndClose(); - - //int getLinearSlotId(int x, int y); - int getSlotPosX(int slotX); - int getSlotPosY(int slotY); - - int getSlotHeight(); - - bool isAllowed(int slot); - -private: - int InventoryCols; - int InventoryRows; - int InventorySize; - - int selectedItem; - bool _pendingQuit; - - Button bArmor; - - RectangleArea _area; -}; - -#endif +#ifndef _MINECRAFT_INGAMEBLOCKSELECTIONSCREEN_H_ +#define _MINECRAFT_INGAMEBLOCKSELECTIONSCREEN_H_ + +#include "../Screen.h" +#include "../../player/input/touchscreen/TouchAreaModel.h" +#include "../components/Button.h" + +class IngameBlockSelectionScreen : public Screen +{ + typedef Screen super; +public: + IngameBlockSelectionScreen(); + virtual ~IngameBlockSelectionScreen() {} + + virtual void init() override; + virtual void removed() override; + + void render(int xm, int ym, float a) override; + +protected: + virtual void mouseClicked(int x, int y, int buttonNum) override; + virtual void mouseReleased(int x, int y, int buttonNum) override; + + virtual void buttonClicked(Button* button) override; + + // wheel input for creative inventory scrolling + virtual void mouseWheel(int dx, int dy, int xm, int ym) override; + + virtual void keyPressed(int eventKey) override; +private: + void renderSlots(); + void renderSlot(int slot, int x, int y, float a); + void renderDemoOverlay(); + + int getSelectedSlot(int x, int y); + void selectSlotAndClose(); + + //int getLinearSlotId(int x, int y); + int getSlotPosX(int slotX); + int getSlotPosY(int slotY); + + int getSlotHeight(); + + bool isAllowed(int slot); + +private: + int InventoryCols; + int InventoryRows; + int InventorySize; + + int selectedItem; + bool _pendingQuit; + + Button bArmor; + + RectangleArea _area; +}; + +#endif diff --git a/src/client/gui/screens/InvalidLicenseScreen.h b/src/client/gui/screens/InvalidLicenseScreen.h index 2801521..e32f0eb 100644 --- a/src/client/gui/screens/InvalidLicenseScreen.h +++ b/src/client/gui/screens/InvalidLicenseScreen.h @@ -1,107 +1,107 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__InvalidLicenseScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__InvalidLicenseScreen_H__ - -#include "../Screen.h" -#include "../components/Button.h" -#include "../../Minecraft.h" -#include "../../../LicenseCodes.h" - -class InvalidLicenseScreen: public Screen -{ -public: - InvalidLicenseScreen(int id, bool hasBuyButton) - : _id(id), - _hasBuyButton(hasBuyButton), - _baseY(0), - bOk(0), - bBuy(0) - { - } - - virtual ~InvalidLicenseScreen() { - delete bOk; - delete bBuy; - } - - void init() { - if (minecraft->useTouchscreen()) { - bOk = new Touch::TButton(1, "Ok"); - bBuy = new Touch::TButton(2, "Buy"); - } else { - bOk = new Button(1, "Ok"); - bBuy = new Button(2, "Buy"); - } - - if (_hasBuyButton) - bOk->msg = "Quit"; - - if (!LicenseCodes::isOk(_id)) { - char buf[20] = {0}; - sprintf(buf, "%d", _id); - - desc1 = "License verification failed (error "; - desc1 += buf; - desc1 += ")"; - desc2 = "Try again later."; - hint = "You need to be connected to the internet\n"; - hint += "once while you start the game."; - } - - buttons.push_back(bOk); - tabButtons.push_back(bOk); - - if (_hasBuyButton) { - buttons.push_back(bBuy); - tabButtons.push_back(bBuy); - } - } - - void setupPositions() { - _baseY = height/5 + 6; - //if (_hasBuyButton) - _baseY -= 24; - - bOk->width = bBuy->width = 200; - bOk->x = bBuy->x = (width - bOk->width) / 2; - bBuy->y = _baseY + 84; - bOk->y = bBuy->y + bBuy->height + 4; - - if (!_hasBuyButton) - bOk->y -= 24; - } - - void tick() {} - - //void keyPressed(int eventKey) {} - - void render(int xm, int ym, float a) { - renderDirtBackground(0); - drawCenteredString(minecraft->font, desc1, width/2, _baseY, 0xffffff); - drawCenteredString(minecraft->font, desc2, width/2, _baseY + 24, 0xffffff); - - drawCenteredString(minecraft->font, hint, width/2, _baseY + 60, 0xffffff); - - Screen::render(xm, ym, a); - } - - void buttonClicked(Button* button) { - if (button->id == bOk->id) { - minecraft->quit(); - } - if (button->id == bBuy->id) { - minecraft->platform()->buyGame(); - } - }; -private: - int _id; - std::string desc1; - std::string desc2; - std::string hint; - - Button* bOk; - Button* bBuy; - bool _hasBuyButton; - int _baseY; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__InvalidLicenseScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__InvalidLicenseScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__InvalidLicenseScreen_H__ + +#include "../Screen.h" +#include "../components/Button.h" +#include "../../Minecraft.h" +#include "../../../LicenseCodes.h" + +class InvalidLicenseScreen: public Screen +{ +public: + InvalidLicenseScreen(int id, bool hasBuyButton) + : _id(id), + _hasBuyButton(hasBuyButton), + _baseY(0), + bOk(0), + bBuy(0) + { + } + + virtual ~InvalidLicenseScreen() { + delete bOk; + delete bBuy; + } + + void init() { + if (minecraft->useTouchscreen()) { + bOk = new Touch::TButton(1, "Ok"); + bBuy = new Touch::TButton(2, "Buy"); + } else { + bOk = new Button(1, "Ok"); + bBuy = new Button(2, "Buy"); + } + + if (_hasBuyButton) + bOk->msg = "Quit"; + + if (!LicenseCodes::isOk(_id)) { + char buf[20] = {0}; + sprintf(buf, "%d", _id); + + desc1 = "License verification failed (error "; + desc1 += buf; + desc1 += ")"; + desc2 = "Try again later."; + hint = "You need to be connected to the internet\n"; + hint += "once while you start the game."; + } + + buttons.push_back(bOk); + tabButtons.push_back(bOk); + + if (_hasBuyButton) { + buttons.push_back(bBuy); + tabButtons.push_back(bBuy); + } + } + + void setupPositions() { + _baseY = height/5 + 6; + //if (_hasBuyButton) + _baseY -= 24; + + bOk->width = bBuy->width = 200; + bOk->x = bBuy->x = (width - bOk->width) / 2; + bBuy->y = _baseY + 84; + bOk->y = bBuy->y + bBuy->height + 4; + + if (!_hasBuyButton) + bOk->y -= 24; + } + + void tick() {} + + //void keyPressed(int eventKey) {} + + void render(int xm, int ym, float a) { + renderDirtBackground(0); + drawCenteredString(minecraft->font, desc1, width/2, _baseY, 0xffffff); + drawCenteredString(minecraft->font, desc2, width/2, _baseY + 24, 0xffffff); + + drawCenteredString(minecraft->font, hint, width/2, _baseY + 60, 0xffffff); + + Screen::render(xm, ym, a); + } + + void buttonClicked(Button* button) { + if (button->id == bOk->id) { + minecraft->quit(); + } + if (button->id == bBuy->id) { + minecraft->platform()->buyGame(); + } + }; +private: + int _id; + std::string desc1; + std::string desc2; + std::string hint; + + Button* bOk; + Button* bBuy; + bool _hasBuyButton; + int _baseY; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__InvalidLicenseScreen_H__*/ diff --git a/src/client/gui/screens/JoinGameScreen.cpp b/src/client/gui/screens/JoinGameScreen.cpp index 8626103..bd622bf 100755 --- a/src/client/gui/screens/JoinGameScreen.cpp +++ b/src/client/gui/screens/JoinGameScreen.cpp @@ -1,167 +1,167 @@ -#include "JoinGameScreen.h" -#include "StartMenuScreen.h" -#include "ProgressScreen.h" -#include "../Font.h" -#include "../../../network/RakNetInstance.h" - -JoinGameScreen::JoinGameScreen() -: bJoin( 2, "Join Game"), - bBack( 3, "Back"), - gamesList(NULL) -{ - bJoin.active = false; - //gamesList->yInertia = 0.5f; -} - -JoinGameScreen::~JoinGameScreen() -{ - delete gamesList; -} - -void JoinGameScreen::buttonClicked(Button* button) -{ - if (button->id == bJoin.id) - { - if (isIndexValid(gamesList->selectedItem)) - { - PingedCompatibleServer selectedServer = gamesList->copiedServerList[gamesList->selectedItem]; - minecraft->joinMultiplayer(selectedServer); - { - bJoin.active = false; - bBack.active = false; - minecraft->setScreen(new ProgressScreen()); - } - } - //minecraft->locateMultiplayer(); - //minecraft->setScreen(new JoinGameScreen()); - } - if (button->id == bBack.id) - { - minecraft->cancelLocateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } -} - -bool JoinGameScreen::handleBackEvent(bool isDown) -{ - if (!isDown) - { - minecraft->cancelLocateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } - return true; -} - - -bool JoinGameScreen::isIndexValid( int index ) -{ - return gamesList && index >= 0 && index < gamesList->getNumberOfItems(); -} - -void JoinGameScreen::tick() -{ - const ServerList& orgServerList = minecraft->raknetInstance->getServerList(); - ServerList serverList; - for (unsigned int i = 0; i < orgServerList.size(); ++i) - if (orgServerList[i].name.GetLength() > 0) - serverList.push_back(orgServerList[i]); - - if (serverList.size() != gamesList->copiedServerList.size()) - { - // copy the currently selected item - PingedCompatibleServer selectedServer; - bool hasSelection = false; - if (isIndexValid(gamesList->selectedItem)) - { - selectedServer = gamesList->copiedServerList[gamesList->selectedItem]; - hasSelection = true; - } - - gamesList->copiedServerList = serverList; - gamesList->selectItem(-1, false); - - // re-select previous item if it still exists - if (hasSelection) - { - for (unsigned int i = 0; i < gamesList->copiedServerList.size(); i++) - { - if (gamesList->copiedServerList[i].address == selectedServer.address) - { - gamesList->selectItem(i, false); - break; - } - } - } - } else { - for (int i = (int)gamesList->copiedServerList.size()-1; i >= 0 ; --i) { - for (int j = 0; j < (int) serverList.size(); ++j) - if (serverList[j].address == gamesList->copiedServerList[i].address) - gamesList->copiedServerList[i].name = serverList[j].name; - } - } - - bJoin.active = isIndexValid(gamesList->selectedItem); -} - -void JoinGameScreen::init() -{ - buttons.push_back(&bJoin); - buttons.push_back(&bBack); - - minecraft->raknetInstance->clearServerList(); - gamesList = new AvailableGamesList(minecraft, width, height); - -#ifdef ANDROID - tabButtons.push_back(&bJoin); - tabButtons.push_back(&bBack); -#endif -} - -void JoinGameScreen::setupPositions() { - int yBase = height - 26; - - //#ifdef ANDROID - bJoin.y = yBase; - bBack.y = yBase; - - bBack.width = bJoin.width = 120; - //#endif - - // Center buttons - bJoin.x = width / 2 - 4 - bJoin.width; - bBack.x = width / 2 + 4; -} - -void JoinGameScreen::render( int xm, int ym, float a ) -{ - bool hasNetwork = minecraft->platform()->isNetworkEnabled(true); -#ifdef WIN32 - hasNetwork = hasNetwork && !GetAsyncKeyState(VK_TAB); -#endif - - renderBackground(); - if (hasNetwork) gamesList->render(xm, ym, a); - Screen::render(xm, ym, a); - - if (hasNetwork) { -#ifdef RPI - std::string s = "Scanning for Local Network Games..."; -#else - std::string s = "Scanning for WiFi Games..."; -#endif - drawCenteredString(minecraft->font, s, width / 2, 8, 0xffffffff); - - const int textWidth = minecraft->font->width(s); - const int spinnerX = width/2 + textWidth / 2 + 6; - - static const char* spinnerTexts[] = {"-", "\\", "|", "/"}; - int n = ((int)(5.5f * getTimeS()) % 4); - drawCenteredString(minecraft->font, spinnerTexts[n], spinnerX, 8, 0xffffffff); - } else { - std::string s = "WiFi is disabled"; - const int yy = height / 2 - 8; - drawCenteredString(minecraft->font, s, width / 2, yy, 0xffffffff); - } -} - -bool JoinGameScreen::isInGameScreen() { return false; } +#include "JoinGameScreen.h" +#include "StartMenuScreen.h" +#include "ProgressScreen.h" +#include "../Font.h" +#include "../../../network/RakNetInstance.h" + +JoinGameScreen::JoinGameScreen() +: bJoin( 2, "Join Game"), + bBack( 3, "Back"), + gamesList(NULL) +{ + bJoin.active = false; + //gamesList->yInertia = 0.5f; +} + +JoinGameScreen::~JoinGameScreen() +{ + delete gamesList; +} + +void JoinGameScreen::buttonClicked(Button* button) +{ + if (button->id == bJoin.id) + { + if (isIndexValid(gamesList->selectedItem)) + { + PingedCompatibleServer selectedServer = gamesList->copiedServerList[gamesList->selectedItem]; + minecraft->joinMultiplayer(selectedServer); + { + bJoin.active = false; + bBack.active = false; + minecraft->setScreen(new ProgressScreen()); + } + } + //minecraft->locateMultiplayer(); + //minecraft->setScreen(new JoinGameScreen()); + } + if (button->id == bBack.id) + { + minecraft->cancelLocateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } +} + +bool JoinGameScreen::handleBackEvent(bool isDown) +{ + if (!isDown) + { + minecraft->cancelLocateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } + return true; +} + + +bool JoinGameScreen::isIndexValid( int index ) +{ + return gamesList && index >= 0 && index < gamesList->getNumberOfItems(); +} + +void JoinGameScreen::tick() +{ + const ServerList& orgServerList = minecraft->raknetInstance->getServerList(); + ServerList serverList; + for (unsigned int i = 0; i < orgServerList.size(); ++i) + if (orgServerList[i].name.GetLength() > 0) + serverList.push_back(orgServerList[i]); + + if (serverList.size() != gamesList->copiedServerList.size()) + { + // copy the currently selected item + PingedCompatibleServer selectedServer; + bool hasSelection = false; + if (isIndexValid(gamesList->selectedItem)) + { + selectedServer = gamesList->copiedServerList[gamesList->selectedItem]; + hasSelection = true; + } + + gamesList->copiedServerList = serverList; + gamesList->selectItem(-1, false); + + // re-select previous item if it still exists + if (hasSelection) + { + for (unsigned int i = 0; i < gamesList->copiedServerList.size(); i++) + { + if (gamesList->copiedServerList[i].address == selectedServer.address) + { + gamesList->selectItem(i, false); + break; + } + } + } + } else { + for (int i = (int)gamesList->copiedServerList.size()-1; i >= 0 ; --i) { + for (int j = 0; j < (int) serverList.size(); ++j) + if (serverList[j].address == gamesList->copiedServerList[i].address) + gamesList->copiedServerList[i].name = serverList[j].name; + } + } + + bJoin.active = isIndexValid(gamesList->selectedItem); +} + +void JoinGameScreen::init() +{ + buttons.push_back(&bJoin); + buttons.push_back(&bBack); + + minecraft->raknetInstance->clearServerList(); + gamesList = new AvailableGamesList(minecraft, width, height); + +#ifdef ANDROID + tabButtons.push_back(&bJoin); + tabButtons.push_back(&bBack); +#endif +} + +void JoinGameScreen::setupPositions() { + int yBase = height - 26; + + //#ifdef ANDROID + bJoin.y = yBase; + bBack.y = yBase; + + bBack.width = bJoin.width = 120; + //#endif + + // Center buttons + bJoin.x = width / 2 - 4 - bJoin.width; + bBack.x = width / 2 + 4; +} + +void JoinGameScreen::render( int xm, int ym, float a ) +{ + bool hasNetwork = minecraft->platform()->isNetworkEnabled(true); +#ifdef WIN32 + hasNetwork = hasNetwork && !GetAsyncKeyState(VK_TAB); +#endif + + renderBackground(); + if (hasNetwork) gamesList->render(xm, ym, a); + Screen::render(xm, ym, a); + + if (hasNetwork) { +#ifdef RPI + std::string s = "Scanning for Local Network Games..."; +#else + std::string s = "Scanning for WiFi Games..."; +#endif + drawCenteredString(minecraft->font, s, width / 2, 8, 0xffffffff); + + const int textWidth = minecraft->font->width(s); + const int spinnerX = width/2 + textWidth / 2 + 6; + + static const char* spinnerTexts[] = {"-", "\\", "|", "/"}; + int n = ((int)(5.5f * getTimeS()) % 4); + drawCenteredString(minecraft->font, spinnerTexts[n], spinnerX, 8, 0xffffffff); + } else { + std::string s = "WiFi is disabled"; + const int yy = height / 2 - 8; + drawCenteredString(minecraft->font, s, width / 2, yy, 0xffffffff); + } +} + +bool JoinGameScreen::isInGameScreen() { return false; } diff --git a/src/client/gui/screens/JoinGameScreen.h b/src/client/gui/screens/JoinGameScreen.h index 4c6fc20..100d6a6 100755 --- a/src/client/gui/screens/JoinGameScreen.h +++ b/src/client/gui/screens/JoinGameScreen.h @@ -1,70 +1,70 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__JoinGameScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__JoinGameScreen_H__ - -#include "../Screen.h" -#include "../components/Button.h" -#include "../components/ScrolledSelectionList.h" -#include "../../Minecraft.h" -#include "../../../network/RakNetInstance.h" - - -class JoinGameScreen; - -class AvailableGamesList : public ScrolledSelectionList -{ - int selectedItem; - ServerList copiedServerList; - - friend class JoinGameScreen; - -public: - - AvailableGamesList(Minecraft* _minecraft, int _width, int _height) - : ScrolledSelectionList(_minecraft, _width, _height, 24, _height - 30, 28) - { - } - -protected: - - virtual int getNumberOfItems() { return (int)copiedServerList.size(); } - - virtual void selectItem(int item, bool doubleClick) { selectedItem = item; } - virtual bool isSelectedItem(int item) { return item == selectedItem; } - - virtual void renderBackground() {} - virtual void renderItem(int i, int x, int y, int h, Tesselator& t) - { - const PingedCompatibleServer& s = copiedServerList[i]; - unsigned int color = s.isSpecial? 0xff00b0 : 0xffffa0; - drawString(minecraft->font, s.name.C_String(), x, y + 2, color); - drawString(minecraft->font, s.address.ToString(false), x, y + 16, 0xffffa0); - } -}; - -class JoinGameScreen: public Screen -{ -public: - JoinGameScreen(); - virtual ~JoinGameScreen(); - - void init(); - void setupPositions(); - - virtual bool handleBackEvent(bool isDown); - - virtual bool isIndexValid(int index); - - virtual void tick(); - - void render(int xm, int ym, float a); - - void buttonClicked(Button* button); - - bool isInGameScreen(); -private: - Button bJoin; - Button bBack; - AvailableGamesList* gamesList; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__JoinGameScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__JoinGameScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__JoinGameScreen_H__ + +#include "../Screen.h" +#include "../components/Button.h" +#include "../components/ScrolledSelectionList.h" +#include "../../Minecraft.h" +#include "../../../network/RakNetInstance.h" + + +class JoinGameScreen; + +class AvailableGamesList : public ScrolledSelectionList +{ + int selectedItem; + ServerList copiedServerList; + + friend class JoinGameScreen; + +public: + + AvailableGamesList(Minecraft* _minecraft, int _width, int _height) + : ScrolledSelectionList(_minecraft, _width, _height, 24, _height - 30, 28) + { + } + +protected: + + virtual int getNumberOfItems() { return (int)copiedServerList.size(); } + + virtual void selectItem(int item, bool doubleClick) { selectedItem = item; } + virtual bool isSelectedItem(int item) { return item == selectedItem; } + + virtual void renderBackground() {} + virtual void renderItem(int i, int x, int y, int h, Tesselator& t) + { + const PingedCompatibleServer& s = copiedServerList[i]; + unsigned int color = s.isSpecial? 0xff00b0 : 0xffffa0; + drawString(minecraft->font, s.name.C_String(), x, y + 2, color); + drawString(minecraft->font, s.address.ToString(false), x, y + 16, 0xffffa0); + } +}; + +class JoinGameScreen: public Screen +{ +public: + JoinGameScreen(); + virtual ~JoinGameScreen(); + + void init(); + void setupPositions(); + + virtual bool handleBackEvent(bool isDown); + + virtual bool isIndexValid(int index); + + virtual void tick(); + + void render(int xm, int ym, float a); + + void buttonClicked(Button* button); + + bool isInGameScreen(); +private: + Button bJoin; + Button bBack; + AvailableGamesList* gamesList; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__JoinGameScreen_H__*/ diff --git a/src/client/gui/screens/OptionsScreen.cpp b/src/client/gui/screens/OptionsScreen.cpp index 97a1696..311a103 100755 --- a/src/client/gui/screens/OptionsScreen.cpp +++ b/src/client/gui/screens/OptionsScreen.cpp @@ -1,270 +1,270 @@ -#include "OptionsScreen.h" - -#include "StartMenuScreen.h" -#include "UsernameScreen.h" -#include "DialogDefinitions.h" -#include "../../Minecraft.h" -#include "../../../AppPlatform.h" -#include "CreditsScreen.h" - -#include "../components/ImageButton.h" -#include "../components/OptionsGroup.h" -#include "platform/input/Keyboard.h" - -OptionsScreen::OptionsScreen() - : btnClose(NULL), - bHeader(NULL), - btnCredits(NULL), - selectedCategory(0) { -} - -OptionsScreen::~OptionsScreen() { - if (btnClose != NULL) { - delete btnClose; - btnClose = NULL; - } - - if (bHeader != NULL) { - delete bHeader; - bHeader = NULL; - } - - if (btnCredits != NULL) { - delete btnCredits; - btnCredits = NULL; - } - - for (std::vector::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) { - if (*it != NULL) { - delete* it; - *it = NULL; - } - } - - for (std::vector::iterator it = optionPanes.begin(); it != optionPanes.end(); ++it) { - if (*it != NULL) { - delete* it; - *it = NULL; - } - } - - categoryButtons.clear(); -} - -void OptionsScreen::init() { - bHeader = new Touch::THeader(0, "Options"); - - btnClose = new ImageButton(1, ""); - - ImageDef def; - def.name = "gui/touchgui.png"; - def.width = 34; - def.height = 26; - - def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); - btnClose->setImageDef(def, true); - - categoryButtons.push_back(new Touch::TButton(2, "General")); - categoryButtons.push_back(new Touch::TButton(3, "Game")); - categoryButtons.push_back(new Touch::TButton(4, "Controls")); - categoryButtons.push_back(new Touch::TButton(5, "Graphics")); - categoryButtons.push_back(new Touch::TButton(6, "Tweaks")); - - btnCredits = new Touch::TButton(11, "Credits"); - - buttons.push_back(bHeader); - buttons.push_back(btnClose); - buttons.push_back(btnCredits); - - for (std::vector::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) { - buttons.push_back(*it); - tabButtons.push_back(*it); - } - - generateOptionScreens(); - // start with first category selected - selectCategory(0); -} - -void OptionsScreen::setupPositions() { - int buttonHeight = btnClose->height; - - btnClose->x = width - btnClose->width; - btnClose->y = 0; - - int offsetNum = 1; - - for (std::vector::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) { - - (*it)->x = 0; - (*it)->y = offsetNum * buttonHeight; - (*it)->selected = false; - - offsetNum++; - } - - bHeader->x = 0; - bHeader->y = 0; - bHeader->width = width - btnClose->width; - bHeader->height = btnClose->height; - - // Credits button (bottom-right) - if (btnCredits != NULL) { - btnCredits->x = width - btnCredits->width; - btnCredits->y = height - btnCredits->height; - } - - for (std::vector::iterator it = optionPanes.begin(); it != optionPanes.end(); ++it) { - - if (categoryButtons.size() > 0 && categoryButtons[0] != NULL) { - - (*it)->x = categoryButtons[0]->width; - (*it)->y = bHeader->height; - (*it)->width = width - categoryButtons[0]->width; - - (*it)->setupPositions(); - } - } - - // don't override user selection on resize -} - - -void OptionsScreen::render(int xm, int ym, float a) { - renderBackground(); - - int xmm = xm * width / minecraft->width; - int ymm = ym * height / minecraft->height - 1; - - if (currentOptionsGroup != NULL) - currentOptionsGroup->render(minecraft, xmm, ymm); - - super::render(xm, ym, a); -} - -void OptionsScreen::removed() { -} - -void OptionsScreen::buttonClicked(Button* button) { - if (button == btnClose) { - minecraft->options.save(); - if (minecraft->screen != NULL) { - minecraft->setScreen(NULL); - } else { - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } - } - else if (button->id > 1 && button->id < 7) { - int categoryButton = button->id - categoryButtons[0]->id; - selectCategory(categoryButton); - } - else if (button == btnCredits) { - minecraft->setScreen(new CreditsScreen()); - } -} - -void OptionsScreen::selectCategory(int index) { - int currentIndex = 0; - - for (std::vector::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) { - - if (index == currentIndex) - (*it)->selected = true; - else - (*it)->selected = false; - - currentIndex++; - } - - if (index < (int)optionPanes.size()) - currentOptionsGroup = optionPanes[index]; -} - -void OptionsScreen::generateOptionScreens() { - // how the fuck it works - - optionPanes.push_back(new OptionsGroup("options.group.general")); - optionPanes.push_back(new OptionsGroup("options.group.game")); - optionPanes.push_back(new OptionsGroup("options.group.controls")); - optionPanes.push_back(new OptionsGroup("options.group.graphics")); - optionPanes.push_back(new OptionsGroup("options.group.tweaks")); - - // General Pane - optionPanes[0]->addOptionItem(OPTIONS_USERNAME, minecraft) - .addOptionItem(OPTIONS_SENSITIVITY, minecraft); - - // Game Pane - optionPanes[1]->addOptionItem(OPTIONS_DIFFICULTY, minecraft) - .addOptionItem(OPTIONS_SERVER_VISIBLE, minecraft) - .addOptionItem(OPTIONS_THIRD_PERSON_VIEW, minecraft) - .addOptionItem(OPTIONS_GUI_SCALE, minecraft) - .addOptionItem(OPTIONS_SENSITIVITY, minecraft) - .addOptionItem(OPTIONS_MUSIC_VOLUME, minecraft) - .addOptionItem(OPTIONS_SOUND_VOLUME, minecraft) - .addOptionItem(OPTIONS_SMOOTH_CAMERA, minecraft) - .addOptionItem(OPTIONS_DESTROY_VIBRATION, minecraft) - .addOptionItem(OPTIONS_IS_LEFT_HANDED, minecraft); - - // // Controls Pane - optionPanes[2]->addOptionItem(OPTIONS_INVERT_Y_MOUSE, minecraft) - .addOptionItem(OPTIONS_USE_TOUCHSCREEN, minecraft) - .addOptionItem(OPTIONS_AUTOJUMP, minecraft); - - for (int i = OPTIONS_KEY_FORWARD; i <= OPTIONS_KEY_USE; i++) { - optionPanes[2]->addOptionItem((OptionId)i, minecraft); - } - - // // Graphics Pane - optionPanes[3]->addOptionItem(OPTIONS_FANCY_GRAPHICS, minecraft) - // .addOptionItem(&Option::VIEW_BOBBING, minecraft) - // .addOptionItem(&Option::AMBIENT_OCCLUSION, minecraft) - // .addOptionItem(&Option::ANAGLYPH, minecraft) - .addOptionItem(OPTIONS_LIMIT_FRAMERATE, minecraft) - .addOptionItem(OPTIONS_VSYNC, minecraft) - .addOptionItem(OPTIONS_RENDER_DEBUG, minecraft) - .addOptionItem(OPTIONS_ANAGLYPH_3D, minecraft) - .addOptionItem(OPTIONS_VIEW_BOBBING, minecraft) - .addOptionItem(OPTIONS_AMBIENT_OCCLUSION, minecraft); - - optionPanes[4]->addOptionItem(OPTIONS_ALLOW_SPRINT, minecraft) - .addOptionItem(OPTIONS_BAR_ON_TOP, minecraft) - .addOptionItem(OPTIONS_RPI_CURSOR, minecraft); -} - -void OptionsScreen::mouseClicked(int x, int y, int buttonNum) { - if (currentOptionsGroup != NULL) - currentOptionsGroup->mouseClicked(minecraft, x, y, buttonNum); - - super::mouseClicked(x, y, buttonNum); -} - -void OptionsScreen::mouseReleased(int x, int y, int buttonNum) { - if (currentOptionsGroup != NULL) - currentOptionsGroup->mouseReleased(minecraft, x, y, buttonNum); - - super::mouseReleased(x, y, buttonNum); -} - -void OptionsScreen::keyPressed(int eventKey) { - if (currentOptionsGroup != NULL) - currentOptionsGroup->keyPressed(minecraft, eventKey); - if (eventKey == Keyboard::KEY_ESCAPE) - minecraft->options.save(); - - super::keyPressed(eventKey); -} - -void OptionsScreen::charPressed(char inputChar) { - if (currentOptionsGroup != NULL) - currentOptionsGroup->charPressed(minecraft, inputChar); - - super::keyPressed(inputChar); -} - -void OptionsScreen::tick() { - - if (currentOptionsGroup != NULL) - currentOptionsGroup->tick(minecraft); - - super::tick(); -} +#include "OptionsScreen.h" + +#include "StartMenuScreen.h" +#include "UsernameScreen.h" +#include "DialogDefinitions.h" +#include "../../Minecraft.h" +#include "../../../AppPlatform.h" +#include "CreditsScreen.h" + +#include "../components/ImageButton.h" +#include "../components/OptionsGroup.h" +#include "platform/input/Keyboard.h" + +OptionsScreen::OptionsScreen() + : btnClose(NULL), + bHeader(NULL), + btnCredits(NULL), + selectedCategory(0) { +} + +OptionsScreen::~OptionsScreen() { + if (btnClose != NULL) { + delete btnClose; + btnClose = NULL; + } + + if (bHeader != NULL) { + delete bHeader; + bHeader = NULL; + } + + if (btnCredits != NULL) { + delete btnCredits; + btnCredits = NULL; + } + + for (std::vector::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) { + if (*it != NULL) { + delete* it; + *it = NULL; + } + } + + for (std::vector::iterator it = optionPanes.begin(); it != optionPanes.end(); ++it) { + if (*it != NULL) { + delete* it; + *it = NULL; + } + } + + categoryButtons.clear(); +} + +void OptionsScreen::init() { + bHeader = new Touch::THeader(0, "Options"); + + btnClose = new ImageButton(1, ""); + + ImageDef def; + def.name = "gui/touchgui.png"; + def.width = 34; + def.height = 26; + + def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); + btnClose->setImageDef(def, true); + + categoryButtons.push_back(new Touch::TButton(2, "General")); + categoryButtons.push_back(new Touch::TButton(3, "Game")); + categoryButtons.push_back(new Touch::TButton(4, "Controls")); + categoryButtons.push_back(new Touch::TButton(5, "Graphics")); + categoryButtons.push_back(new Touch::TButton(6, "Tweaks")); + + btnCredits = new Touch::TButton(11, "Credits"); + + buttons.push_back(bHeader); + buttons.push_back(btnClose); + buttons.push_back(btnCredits); + + for (std::vector::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) { + buttons.push_back(*it); + tabButtons.push_back(*it); + } + + generateOptionScreens(); + // start with first category selected + selectCategory(0); +} + +void OptionsScreen::setupPositions() { + int buttonHeight = btnClose->height; + + btnClose->x = width - btnClose->width; + btnClose->y = 0; + + int offsetNum = 1; + + for (std::vector::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) { + + (*it)->x = 0; + (*it)->y = offsetNum * buttonHeight; + (*it)->selected = false; + + offsetNum++; + } + + bHeader->x = 0; + bHeader->y = 0; + bHeader->width = width - btnClose->width; + bHeader->height = btnClose->height; + + // Credits button (bottom-right) + if (btnCredits != NULL) { + btnCredits->x = width - btnCredits->width; + btnCredits->y = height - btnCredits->height; + } + + for (std::vector::iterator it = optionPanes.begin(); it != optionPanes.end(); ++it) { + + if (categoryButtons.size() > 0 && categoryButtons[0] != NULL) { + + (*it)->x = categoryButtons[0]->width; + (*it)->y = bHeader->height; + (*it)->width = width - categoryButtons[0]->width; + + (*it)->setupPositions(); + } + } + + // don't override user selection on resize +} + + +void OptionsScreen::render(int xm, int ym, float a) { + renderBackground(); + + int xmm = xm * width / minecraft->width; + int ymm = ym * height / minecraft->height - 1; + + if (currentOptionsGroup != NULL) + currentOptionsGroup->render(minecraft, xmm, ymm); + + super::render(xm, ym, a); +} + +void OptionsScreen::removed() { +} + +void OptionsScreen::buttonClicked(Button* button) { + if (button == btnClose) { + minecraft->options.save(); + if (minecraft->screen != NULL) { + minecraft->setScreen(NULL); + } else { + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } + } + else if (button->id > 1 && button->id < 7) { + int categoryButton = button->id - categoryButtons[0]->id; + selectCategory(categoryButton); + } + else if (button == btnCredits) { + minecraft->setScreen(new CreditsScreen()); + } +} + +void OptionsScreen::selectCategory(int index) { + int currentIndex = 0; + + for (std::vector::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) { + + if (index == currentIndex) + (*it)->selected = true; + else + (*it)->selected = false; + + currentIndex++; + } + + if (index < (int)optionPanes.size()) + currentOptionsGroup = optionPanes[index]; +} + +void OptionsScreen::generateOptionScreens() { + // how the fuck it works + + optionPanes.push_back(new OptionsGroup("options.group.general")); + optionPanes.push_back(new OptionsGroup("options.group.game")); + optionPanes.push_back(new OptionsGroup("options.group.controls")); + optionPanes.push_back(new OptionsGroup("options.group.graphics")); + optionPanes.push_back(new OptionsGroup("options.group.tweaks")); + + // General Pane + optionPanes[0]->addOptionItem(OPTIONS_USERNAME, minecraft) + .addOptionItem(OPTIONS_SENSITIVITY, minecraft); + + // Game Pane + optionPanes[1]->addOptionItem(OPTIONS_DIFFICULTY, minecraft) + .addOptionItem(OPTIONS_SERVER_VISIBLE, minecraft) + .addOptionItem(OPTIONS_THIRD_PERSON_VIEW, minecraft) + .addOptionItem(OPTIONS_GUI_SCALE, minecraft) + .addOptionItem(OPTIONS_SENSITIVITY, minecraft) + .addOptionItem(OPTIONS_MUSIC_VOLUME, minecraft) + .addOptionItem(OPTIONS_SOUND_VOLUME, minecraft) + .addOptionItem(OPTIONS_SMOOTH_CAMERA, minecraft) + .addOptionItem(OPTIONS_DESTROY_VIBRATION, minecraft) + .addOptionItem(OPTIONS_IS_LEFT_HANDED, minecraft); + + // // Controls Pane + optionPanes[2]->addOptionItem(OPTIONS_INVERT_Y_MOUSE, minecraft) + .addOptionItem(OPTIONS_USE_TOUCHSCREEN, minecraft) + .addOptionItem(OPTIONS_AUTOJUMP, minecraft); + + for (int i = OPTIONS_KEY_FORWARD; i <= OPTIONS_KEY_USE; i++) { + optionPanes[2]->addOptionItem((OptionId)i, minecraft); + } + + // // Graphics Pane + optionPanes[3]->addOptionItem(OPTIONS_FANCY_GRAPHICS, minecraft) + // .addOptionItem(&Option::VIEW_BOBBING, minecraft) + // .addOptionItem(&Option::AMBIENT_OCCLUSION, minecraft) + // .addOptionItem(&Option::ANAGLYPH, minecraft) + .addOptionItem(OPTIONS_LIMIT_FRAMERATE, minecraft) + .addOptionItem(OPTIONS_VSYNC, minecraft) + .addOptionItem(OPTIONS_RENDER_DEBUG, minecraft) + .addOptionItem(OPTIONS_ANAGLYPH_3D, minecraft) + .addOptionItem(OPTIONS_VIEW_BOBBING, minecraft) + .addOptionItem(OPTIONS_AMBIENT_OCCLUSION, minecraft); + + optionPanes[4]->addOptionItem(OPTIONS_ALLOW_SPRINT, minecraft) + .addOptionItem(OPTIONS_BAR_ON_TOP, minecraft) + .addOptionItem(OPTIONS_RPI_CURSOR, minecraft); +} + +void OptionsScreen::mouseClicked(int x, int y, int buttonNum) { + if (currentOptionsGroup != NULL) + currentOptionsGroup->mouseClicked(minecraft, x, y, buttonNum); + + super::mouseClicked(x, y, buttonNum); +} + +void OptionsScreen::mouseReleased(int x, int y, int buttonNum) { + if (currentOptionsGroup != NULL) + currentOptionsGroup->mouseReleased(minecraft, x, y, buttonNum); + + super::mouseReleased(x, y, buttonNum); +} + +void OptionsScreen::keyPressed(int eventKey) { + if (currentOptionsGroup != NULL) + currentOptionsGroup->keyPressed(minecraft, eventKey); + if (eventKey == Keyboard::KEY_ESCAPE) + minecraft->options.save(); + + super::keyPressed(eventKey); +} + +void OptionsScreen::charPressed(char inputChar) { + if (currentOptionsGroup != NULL) + currentOptionsGroup->charPressed(minecraft, inputChar); + + super::keyPressed(inputChar); +} + +void OptionsScreen::tick() { + + if (currentOptionsGroup != NULL) + currentOptionsGroup->tick(minecraft); + + super::tick(); +} diff --git a/src/client/gui/screens/OptionsScreen.h b/src/client/gui/screens/OptionsScreen.h index b02c7b8..aacc386 100755 --- a/src/client/gui/screens/OptionsScreen.h +++ b/src/client/gui/screens/OptionsScreen.h @@ -1,49 +1,49 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__OptionsScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__OptionsScreen_H__ - -#include "../Screen.h" -#include "../components/Button.h" -#include "../components/OptionsGroup.h" - -class ImageButton; -class OptionsPane; - -class OptionsScreen: public Screen -{ - typedef Screen super; - - void init(); - void generateOptionScreens(); - -public: - OptionsScreen(); - ~OptionsScreen(); - - void setupPositions(); - void buttonClicked(Button* button); - void render(int xm, int ym, float a); - void removed(); - void selectCategory(int index); - - virtual void mouseClicked(int x, int y, int buttonNum); - virtual void mouseReleased(int x, int y, int buttonNum); - virtual void keyPressed(int eventKey); - virtual void charPressed(char inputChar); - - virtual void tick(); - -private: - Touch::THeader* bHeader; - ImageButton* btnClose; - - Button* btnCredits; // <-- ADD THIS - - std::vector categoryButtons; - std::vector optionPanes; - - OptionsGroup* currentOptionsGroup; - - int selectedCategory; -}; - +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__OptionsScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__OptionsScreen_H__ + +#include "../Screen.h" +#include "../components/Button.h" +#include "../components/OptionsGroup.h" + +class ImageButton; +class OptionsPane; + +class OptionsScreen: public Screen +{ + typedef Screen super; + + void init(); + void generateOptionScreens(); + +public: + OptionsScreen(); + ~OptionsScreen(); + + void setupPositions(); + void buttonClicked(Button* button); + void render(int xm, int ym, float a); + void removed(); + void selectCategory(int index); + + virtual void mouseClicked(int x, int y, int buttonNum); + virtual void mouseReleased(int x, int y, int buttonNum); + virtual void keyPressed(int eventKey); + virtual void charPressed(char inputChar); + + virtual void tick(); + +private: + Touch::THeader* bHeader; + ImageButton* btnClose; + + Button* btnCredits; // <-- ADD THIS + + std::vector categoryButtons; + std::vector optionPanes; + + OptionsGroup* currentOptionsGroup; + + int selectedCategory; +}; + #endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__OptionsScreen_H__*/ \ No newline at end of file diff --git a/src/client/gui/screens/PauseScreen.cpp b/src/client/gui/screens/PauseScreen.cpp index ab30ecb..2206ef2 100755 --- a/src/client/gui/screens/PauseScreen.cpp +++ b/src/client/gui/screens/PauseScreen.cpp @@ -1,201 +1,201 @@ -#include "PauseScreen.h" -#include "StartMenuScreen.h" -#include "../components/ImageButton.h" -#include "../../Minecraft.h" -#include "../../../util/Mth.h" -#include "../../../network/RakNetInstance.h" -#include "../../../network/ServerSideNetworkHandler.h" -#include "client/Options.h" -#include "client/gui/components/Button.h" -#include "client/gui/screens/OptionsScreen.h" - -PauseScreen::PauseScreen(bool wasBackPaused) -: saveStep(0), - visibleTime(0), - bContinue(0), - bQuit(0), - bOptions(0), - bQuitAndSaveLocally(0), - bServerVisibility(0), -// bThirdPerson(0), - wasBackPaused(wasBackPaused), - // bSound(OPTIONS_SOUND_VOLUME, 1, 0), - bThirdPerson(OPTIONS_THIRD_PERSON_VIEW), - bHideGui(OPTIONS_HIDEGUI) -{ - ImageDef def; - def.setSrc(IntRectangle(160, 144, 39, 31)); - def.name = "gui/touchgui.png"; - IntRectangle& defSrc = *def.getSrc(); - - def.width = defSrc.w * 0.666667f; - def.height = defSrc.h * 0.666667f; - - // bSound.setImageDef(def, true); - defSrc.y += defSrc.h; - bThirdPerson.setImageDef(def, true); - bHideGui.setImageDef(def, true); - //void setImageDef(ImageDef& imageDef, bool setButtonSize); -} - -PauseScreen::~PauseScreen() { - delete bContinue; - delete bQuit; - delete bQuitAndSaveLocally; - delete bServerVisibility; - delete bOptions; -// delete bThirdPerson; -} - -void PauseScreen::init() { - if (/* minecraft->useTouchscreen() */ true) { - bContinue = new Touch::TButton(1, "Back to game"); - bOptions = new Touch::TButton(5, "Options"); - bQuit = new Touch::TButton(2, "Quit to title"); - bQuitAndSaveLocally = new Touch::TButton(3, "Quit and copy map"); - bServerVisibility = new Touch::TButton(4, ""); -// bThirdPerson = new Touch::TButton(5, "Toggle 3:rd person view"); - } else { - bContinue = new Button(1, "Back to game"); - bOptions = new Button(5, "Options"); - bQuit = new Button(2, "Quit to title"); - bQuitAndSaveLocally = new Button(3, "Quit and copy map"); - bServerVisibility = new Button(4, ""); -// bThirdPerson = new Button(5, "Toggle 3:rd person view"); - } - - buttons.push_back(bContinue); - buttons.push_back(bQuit); - buttons.push_back(bOptions); - // bSound.updateImage(&minecraft->options); - bThirdPerson.updateImage(&minecraft->options); - bHideGui.updateImage(&minecraft->options); - // buttons.push_back(&bSound); - buttons.push_back(&bThirdPerson); - //buttons.push_back(&bHideGui); - - // If Back wasn't pressed, set up additional items (more than Quit to menu - // and Back to game) here - - #if !defined(APPLE_DEMO_PROMOTION) && !defined(RPI) - if (true || !wasBackPaused) { - if (minecraft->raknetInstance) { - if (minecraft->raknetInstance->isServer()) { - updateServerVisibilityText(); - buttons.push_back(bServerVisibility); - } - else { - #if !defined(DEMO_MODE) - buttons.push_back(bQuitAndSaveLocally); - #endif - } - } - } - #endif -// buttons.push_back(bThirdPerson); - - for (unsigned int i = 0; i < buttons.size(); ++i) { - // if (buttons[i] == &bSound) continue; - if (buttons[i] == &bThirdPerson) continue; - if (buttons[i] == &bHideGui) continue; - tabButtons.push_back(buttons[i]); - } -} - -void PauseScreen::setupPositions() { - saveStep = 0; - int yBase = 16; - - bContinue->width = bOptions->width = bQuit->width = /*bThirdPerson->w =*/ 160; - bQuitAndSaveLocally->width = bServerVisibility->width = 160; - - bContinue->x = (width - bContinue->width) / 2; - bContinue->y = yBase + 32 * 1; - - bOptions->x = (width - bOptions->width) / 2; - bOptions->y = yBase + 32 * 2; - - bQuit->x = (width - bQuit->width) / 2; - bQuit->y = yBase + 32 * 3; - -#if APPLE_DEMO_PROMOTION - bQuit->y += 16; -#endif - - bQuitAndSaveLocally->x = bServerVisibility->x = (width - bQuitAndSaveLocally->width) / 2; - bQuitAndSaveLocally->y = bServerVisibility->y = yBase + 32 * 4; - - // bSound.y = bThirdPerson.y = 8; - // bSound.x = 4; - // bThirdPerson.x = bSound.x + 4 + bSound.width; - // bHideGui.x = bThirdPerson.x + 4 + bThirdPerson.width; - - //bThirdPerson->x = (width - bThirdPerson->w) / 2; - //bThirdPerson->y = yBase + 32 * 4; -} - -void PauseScreen::tick() { - super::tick(); - visibleTime++; -} - -void PauseScreen::render(int xm, int ym, float a) { - renderBackground(); - - //bool isSaving = !minecraft->level.pauseSave(saveStep++); - //if (isSaving || visibleTime < 20) { - // float col = ((visibleTime % 10) + a) / 10.0f; - // col = Mth::sin(col * Mth::PI * 2) * 0.2f + 0.8f; - // int br = (int) (255 * col); - - // drawString(font, "Saving level..", 8, height - 16, br << 16 | br << 8 | br); - //} - - drawCenteredString(font, "Game menu", width / 2, 24, 0xffffff); - - super::render(xm, ym, a); -} - -void PauseScreen::buttonClicked(Button* button) { - if (button->id == bContinue->id) { - minecraft->setScreen(NULL); - //minecraft->grabMouse(); - } - if (button->id == bQuit->id) { - minecraft->leaveGame(); - } - if (button->id == bQuitAndSaveLocally->id) { - minecraft->leaveGame(true); - } - if (button->id == bOptions->id) { - minecraft->setScreen(new OptionsScreen()); - } - if (button->id == bServerVisibility->id) { - if (minecraft->raknetInstance && minecraft->netCallback && minecraft->raknetInstance->isServer()) { - ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) minecraft->netCallback; - bool allows = !ss->allowsIncomingConnections(); - ss->allowIncomingConnections(allows); - - updateServerVisibilityText(); - } - } - - if (button->id == OptionButton::ButtonId) { - ((OptionButton*)button)->toggle(&minecraft->options); - } - - //if (button->id == bThirdPerson->id) { - // minecraft->options.thirdPersonView = !minecraft->options.thirdPersonView; - //} -} - -void PauseScreen::updateServerVisibilityText() -{ - if (!minecraft->raknetInstance || !minecraft->raknetInstance->isServer()) - return; - - ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) minecraft->netCallback; - bServerVisibility->msg = ss->allowsIncomingConnections()? - "Server is visible" - : "Server is invisible"; -} +#include "PauseScreen.h" +#include "StartMenuScreen.h" +#include "../components/ImageButton.h" +#include "../../Minecraft.h" +#include "../../../util/Mth.h" +#include "../../../network/RakNetInstance.h" +#include "../../../network/ServerSideNetworkHandler.h" +#include "client/Options.h" +#include "client/gui/components/Button.h" +#include "client/gui/screens/OptionsScreen.h" + +PauseScreen::PauseScreen(bool wasBackPaused) +: saveStep(0), + visibleTime(0), + bContinue(0), + bQuit(0), + bOptions(0), + bQuitAndSaveLocally(0), + bServerVisibility(0), +// bThirdPerson(0), + wasBackPaused(wasBackPaused), + // bSound(OPTIONS_SOUND_VOLUME, 1, 0), + bThirdPerson(OPTIONS_THIRD_PERSON_VIEW), + bHideGui(OPTIONS_HIDEGUI) +{ + ImageDef def; + def.setSrc(IntRectangle(160, 144, 39, 31)); + def.name = "gui/touchgui.png"; + IntRectangle& defSrc = *def.getSrc(); + + def.width = defSrc.w * 0.666667f; + def.height = defSrc.h * 0.666667f; + + // bSound.setImageDef(def, true); + defSrc.y += defSrc.h; + bThirdPerson.setImageDef(def, true); + bHideGui.setImageDef(def, true); + //void setImageDef(ImageDef& imageDef, bool setButtonSize); +} + +PauseScreen::~PauseScreen() { + delete bContinue; + delete bQuit; + delete bQuitAndSaveLocally; + delete bServerVisibility; + delete bOptions; +// delete bThirdPerson; +} + +void PauseScreen::init() { + if (/* minecraft->useTouchscreen() */ true) { + bContinue = new Touch::TButton(1, "Back to game"); + bOptions = new Touch::TButton(5, "Options"); + bQuit = new Touch::TButton(2, "Quit to title"); + bQuitAndSaveLocally = new Touch::TButton(3, "Quit and copy map"); + bServerVisibility = new Touch::TButton(4, ""); +// bThirdPerson = new Touch::TButton(5, "Toggle 3:rd person view"); + } else { + bContinue = new Button(1, "Back to game"); + bOptions = new Button(5, "Options"); + bQuit = new Button(2, "Quit to title"); + bQuitAndSaveLocally = new Button(3, "Quit and copy map"); + bServerVisibility = new Button(4, ""); +// bThirdPerson = new Button(5, "Toggle 3:rd person view"); + } + + buttons.push_back(bContinue); + buttons.push_back(bQuit); + buttons.push_back(bOptions); + // bSound.updateImage(&minecraft->options); + bThirdPerson.updateImage(&minecraft->options); + bHideGui.updateImage(&minecraft->options); + // buttons.push_back(&bSound); + buttons.push_back(&bThirdPerson); + //buttons.push_back(&bHideGui); + + // If Back wasn't pressed, set up additional items (more than Quit to menu + // and Back to game) here + + #if !defined(APPLE_DEMO_PROMOTION) && !defined(RPI) + if (true || !wasBackPaused) { + if (minecraft->raknetInstance) { + if (minecraft->raknetInstance->isServer()) { + updateServerVisibilityText(); + buttons.push_back(bServerVisibility); + } + else { + #if !defined(DEMO_MODE) + buttons.push_back(bQuitAndSaveLocally); + #endif + } + } + } + #endif +// buttons.push_back(bThirdPerson); + + for (unsigned int i = 0; i < buttons.size(); ++i) { + // if (buttons[i] == &bSound) continue; + if (buttons[i] == &bThirdPerson) continue; + if (buttons[i] == &bHideGui) continue; + tabButtons.push_back(buttons[i]); + } +} + +void PauseScreen::setupPositions() { + saveStep = 0; + int yBase = 16; + + bContinue->width = bOptions->width = bQuit->width = /*bThirdPerson->w =*/ 160; + bQuitAndSaveLocally->width = bServerVisibility->width = 160; + + bContinue->x = (width - bContinue->width) / 2; + bContinue->y = yBase + 32 * 1; + + bOptions->x = (width - bOptions->width) / 2; + bOptions->y = yBase + 32 * 2; + + bQuit->x = (width - bQuit->width) / 2; + bQuit->y = yBase + 32 * 3; + +#if APPLE_DEMO_PROMOTION + bQuit->y += 16; +#endif + + bQuitAndSaveLocally->x = bServerVisibility->x = (width - bQuitAndSaveLocally->width) / 2; + bQuitAndSaveLocally->y = bServerVisibility->y = yBase + 32 * 4; + + // bSound.y = bThirdPerson.y = 8; + // bSound.x = 4; + // bThirdPerson.x = bSound.x + 4 + bSound.width; + // bHideGui.x = bThirdPerson.x + 4 + bThirdPerson.width; + + //bThirdPerson->x = (width - bThirdPerson->w) / 2; + //bThirdPerson->y = yBase + 32 * 4; +} + +void PauseScreen::tick() { + super::tick(); + visibleTime++; +} + +void PauseScreen::render(int xm, int ym, float a) { + renderBackground(); + + //bool isSaving = !minecraft->level.pauseSave(saveStep++); + //if (isSaving || visibleTime < 20) { + // float col = ((visibleTime % 10) + a) / 10.0f; + // col = Mth::sin(col * Mth::PI * 2) * 0.2f + 0.8f; + // int br = (int) (255 * col); + + // drawString(font, "Saving level..", 8, height - 16, br << 16 | br << 8 | br); + //} + + drawCenteredString(font, "Game menu", width / 2, 24, 0xffffff); + + super::render(xm, ym, a); +} + +void PauseScreen::buttonClicked(Button* button) { + if (button->id == bContinue->id) { + minecraft->setScreen(NULL); + //minecraft->grabMouse(); + } + if (button->id == bQuit->id) { + minecraft->leaveGame(); + } + if (button->id == bQuitAndSaveLocally->id) { + minecraft->leaveGame(true); + } + if (button->id == bOptions->id) { + minecraft->setScreen(new OptionsScreen()); + } + if (button->id == bServerVisibility->id) { + if (minecraft->raknetInstance && minecraft->netCallback && minecraft->raknetInstance->isServer()) { + ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) minecraft->netCallback; + bool allows = !ss->allowsIncomingConnections(); + ss->allowIncomingConnections(allows); + + updateServerVisibilityText(); + } + } + + if (button->id == OptionButton::ButtonId) { + ((OptionButton*)button)->toggle(&minecraft->options); + } + + //if (button->id == bThirdPerson->id) { + // minecraft->options.thirdPersonView = !minecraft->options.thirdPersonView; + //} +} + +void PauseScreen::updateServerVisibilityText() +{ + if (!minecraft->raknetInstance || !minecraft->raknetInstance->isServer()) + return; + + ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) minecraft->netCallback; + bServerVisibility->msg = ss->allowsIncomingConnections()? + "Server is visible" + : "Server is invisible"; +} diff --git a/src/client/gui/screens/PauseScreen.h b/src/client/gui/screens/PauseScreen.h index 4098511..b187833 100755 --- a/src/client/gui/screens/PauseScreen.h +++ b/src/client/gui/screens/PauseScreen.h @@ -1,45 +1,45 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI__PauseScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI__PauseScreen_H__ - -//package net.minecraft.client.gui; - -#include "../Screen.h" -#include "../components/ImageButton.h" - -class Button; - -class PauseScreen: public Screen -{ - typedef Screen super; -public: - PauseScreen(bool wasBackPaused); - ~PauseScreen(); - - void init(); - void setupPositions(); - - void tick(); - void render(int xm, int ym, float a); -protected: - void buttonClicked(Button* button); -private: - void updateServerVisibilityText(); - - int saveStep; - int visibleTime; - bool wasBackPaused; - - Button* bContinue; - Button* bQuit; - Button* bQuitAndSaveLocally; - Button* bServerVisibility; - Button* bOptions; - - // Button* bThirdPerson; - - // OptionButton bSound; - OptionButton bThirdPerson; - OptionButton bHideGui; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI__PauseScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI__PauseScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI__PauseScreen_H__ + +//package net.minecraft.client.gui; + +#include "../Screen.h" +#include "../components/ImageButton.h" + +class Button; + +class PauseScreen: public Screen +{ + typedef Screen super; +public: + PauseScreen(bool wasBackPaused); + ~PauseScreen(); + + void init(); + void setupPositions(); + + void tick(); + void render(int xm, int ym, float a); +protected: + void buttonClicked(Button* button); +private: + void updateServerVisibilityText(); + + int saveStep; + int visibleTime; + bool wasBackPaused; + + Button* bContinue; + Button* bQuit; + Button* bQuitAndSaveLocally; + Button* bServerVisibility; + Button* bOptions; + + // Button* bThirdPerson; + + // OptionButton bSound; + OptionButton bThirdPerson; + OptionButton bHideGui; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI__PauseScreen_H__*/ diff --git a/src/client/gui/screens/PrerenderTilesScreen.h b/src/client/gui/screens/PrerenderTilesScreen.h index faa405d..9bb1e52 100755 --- a/src/client/gui/screens/PrerenderTilesScreen.h +++ b/src/client/gui/screens/PrerenderTilesScreen.h @@ -1,162 +1,162 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__PrerenderTilesScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__PrerenderTilesScreen_H__ - -#include "../Screen.h" - -#include "../../renderer/GameRenderer.h" -#include "../../renderer/entity/ItemRenderer.h" -#include "../../../world/item/ItemInstance.h" -#include "../../../world/level/tile/Tile.h" - -#include "../../../world/entity/player/Inventory.h" -#include "../../renderer/Tesselator.h" -#include "../../../world/item/crafting/Recipes.h" -#include "../../../world/item/crafting/FurnaceRecipes.h" -#include "../../../world/level/tile/LeafTile.h" -#include "../../renderer/TileRenderer.h" - -class PrerenderTilesScreen: public Screen -{ -public: - void init() { - Player p(minecraft->level, true); - Inventory _inventory(&p, true); - Inventory* inventory = &_inventory; - - // Copy over the inventory items - for (int i = Inventory::MAX_SELECTION_SIZE; i < inventory->getContainerSize(); ++i) - addItem(inventory->getItem(i)); - - // Fill the inventory with all the recipe items we don't already have: furnace - const FurnaceRecipes::Map& furnaceRecipes = FurnaceRecipes::getInstance()->getRecipes(); - for (FurnaceRecipes::Map::const_iterator cit = furnaceRecipes.begin(); cit != furnaceRecipes.end(); ++cit) { - ItemInstance ingredient(cit->first, 1, 0); - addItem(&ingredient); - ItemInstance result = cit->second; - addItem(&result); - } - - // Fill the inventory with all the recipe items we don't already have: crafting - const RecipeList& recipes = Recipes::getInstance()->getRecipes(); - for (unsigned int i = 0; i < recipes.size(); ++i) { - - std::vector items; - std::vector required = recipes[i]->getItemPack().getItemInstances(); - items.push_back(recipes[i]->getResultItem()); - items.insert(items.end(), required.begin(), required.end()); - - for (unsigned int i = 0; i < items.size(); ++i) { - ItemInstance& item = items[i]; - addItem(&item); - } - } - - // Manually added stuff - // Example: the one that's spawned from tiles when destroyed - int items[] = { - Tile::sapling->id, LeafTile::BIRCH_LEAF, - Tile::sapling->id, LeafTile::EVERGREEN_LEAF, - Tile::sapling->id, LeafTile::NORMAL_LEAF, - Tile::dirt->id, 0, - Tile::reeds->id, 0, - Tile::gravel->id, 0, - Item::apple->id, 0, - Tile::grass_carried->id, 0, - Tile::web->id, 0, - Item::sign->id, 0, - }; - for (int i = 0; i < sizeof(items)/sizeof(int); i += 2) { - ItemInstance item(items[i], 1, items[i+1]); - addItem(&item); - } - } - - void render( int xm, int ym, float a ) { - static Stopwatch w; - w.start(); - - glDisable2(GL_DEPTH_TEST); - fill(0, 0, width, height, 0xffff00ff); - //fill(0, 0, width, height, 0xff333333); - glColor4f2(1, 1, 1, 1); - glEnable2(GL_BLEND); - - LOGI("--------------------\n"); - /*int j = 0; - for (int i = Inventory::MAX_SELECTION_SIZE; i < inventory->getContainerSize(); ++i) { - - ItemInstance* item = inventory->getItem(i); - if (!item) continue; - - //LOGI("desc: %d - %s. %d\n", i, item->toString().c_str()); - - int x = j%16 * 16; - int y = j/16 * 16; - - //Tesselator::instance.color(0xffffffff); - //minecraft->textures->loadAndBindTexture("gui/gui2.png"); - //glColor4f2(0.2f, 0.5f, 0.2f, 1); - //blit(x, y, 4 + 20 * (i%9), 4, 16, 16, 15, 15); - //glColor4f2(1, 1, 1, 1); - - if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { - LOGI("0, %d, %d, %d, 0\n", j, item->id, item->getAuxValue()); - ItemRenderer::renderGuiItemCorrect(minecraft->font, minecraft->textures, item, x, y); - } else if (item->getIcon() >= 0) { - LOGI("1, %d, %d, %d, %d\n", j, item->id, item->getAuxValue(), item->getIcon()); - } - ++j; - }*/ - int j = 0; - for(std::vector::iterator i = mItems.begin(); i != mItems.end(); ++i) { - ItemInstance* item = &(*i); - - //LOGI("desc: %d - %s. %d\n", i, item->toString().c_str()); - - int x = j%16 * 16; - int y = j/16 * 16; - if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { - LOGI("0, %d, %d, %d, 0\n", j, item->id, item->getAuxValue()); - ItemRenderer::renderGuiItemCorrect(minecraft->font, minecraft->textures, item, x, y); - } else if (item->getIcon() >= 0) { - LOGI("1, %d, %d, %d, %d\n", j, item->id, item->getAuxValue(), item->getIcon()); - } - j++; - } - //@todo: blit out something famous here - - //glRotatef2(-180, 1, 0, 0); - glEnable2(GL_DEPTH_TEST); - glDisable2(GL_BLEND); - - w.stop(); - w.printEvery(100, "render-blocksel"); - } - void removed(){} - - void addItem(ItemInstance* item) { - if(item == NULL) - return; - if (item->getAuxValue() < 0) return; - - bool found = false; - for(std::vector::iterator i = mItems.begin(); i != mItems.end(); ++i) { - ItemInstance *jitem = &*i; - if(jitem->id != item->id) continue; - if(jitem->isStackedByData() && jitem->getAuxValue() != item->getAuxValue()) continue; - - found = true; - break; - } - - if (!found) { - LOGI("Adding item: %s\n", item->getDescriptionId().c_str()); - mItems.push_back(*item); - } - } - -private: - std::vector mItems; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__PrerenderTilesScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__PrerenderTilesScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__PrerenderTilesScreen_H__ + +#include "../Screen.h" + +#include "../../renderer/GameRenderer.h" +#include "../../renderer/entity/ItemRenderer.h" +#include "../../../world/item/ItemInstance.h" +#include "../../../world/level/tile/Tile.h" + +#include "../../../world/entity/player/Inventory.h" +#include "../../renderer/Tesselator.h" +#include "../../../world/item/crafting/Recipes.h" +#include "../../../world/item/crafting/FurnaceRecipes.h" +#include "../../../world/level/tile/LeafTile.h" +#include "../../renderer/TileRenderer.h" + +class PrerenderTilesScreen: public Screen +{ +public: + void init() { + Player p(minecraft->level, true); + Inventory _inventory(&p, true); + Inventory* inventory = &_inventory; + + // Copy over the inventory items + for (int i = Inventory::MAX_SELECTION_SIZE; i < inventory->getContainerSize(); ++i) + addItem(inventory->getItem(i)); + + // Fill the inventory with all the recipe items we don't already have: furnace + const FurnaceRecipes::Map& furnaceRecipes = FurnaceRecipes::getInstance()->getRecipes(); + for (FurnaceRecipes::Map::const_iterator cit = furnaceRecipes.begin(); cit != furnaceRecipes.end(); ++cit) { + ItemInstance ingredient(cit->first, 1, 0); + addItem(&ingredient); + ItemInstance result = cit->second; + addItem(&result); + } + + // Fill the inventory with all the recipe items we don't already have: crafting + const RecipeList& recipes = Recipes::getInstance()->getRecipes(); + for (unsigned int i = 0; i < recipes.size(); ++i) { + + std::vector items; + std::vector required = recipes[i]->getItemPack().getItemInstances(); + items.push_back(recipes[i]->getResultItem()); + items.insert(items.end(), required.begin(), required.end()); + + for (unsigned int i = 0; i < items.size(); ++i) { + ItemInstance& item = items[i]; + addItem(&item); + } + } + + // Manually added stuff + // Example: the one that's spawned from tiles when destroyed + int items[] = { + Tile::sapling->id, LeafTile::BIRCH_LEAF, + Tile::sapling->id, LeafTile::EVERGREEN_LEAF, + Tile::sapling->id, LeafTile::NORMAL_LEAF, + Tile::dirt->id, 0, + Tile::reeds->id, 0, + Tile::gravel->id, 0, + Item::apple->id, 0, + Tile::grass_carried->id, 0, + Tile::web->id, 0, + Item::sign->id, 0, + }; + for (int i = 0; i < sizeof(items)/sizeof(int); i += 2) { + ItemInstance item(items[i], 1, items[i+1]); + addItem(&item); + } + } + + void render( int xm, int ym, float a ) { + static Stopwatch w; + w.start(); + + glDisable2(GL_DEPTH_TEST); + fill(0, 0, width, height, 0xffff00ff); + //fill(0, 0, width, height, 0xff333333); + glColor4f2(1, 1, 1, 1); + glEnable2(GL_BLEND); + + LOGI("--------------------\n"); + /*int j = 0; + for (int i = Inventory::MAX_SELECTION_SIZE; i < inventory->getContainerSize(); ++i) { + + ItemInstance* item = inventory->getItem(i); + if (!item) continue; + + //LOGI("desc: %d - %s. %d\n", i, item->toString().c_str()); + + int x = j%16 * 16; + int y = j/16 * 16; + + //Tesselator::instance.color(0xffffffff); + //minecraft->textures->loadAndBindTexture("gui/gui2.png"); + //glColor4f2(0.2f, 0.5f, 0.2f, 1); + //blit(x, y, 4 + 20 * (i%9), 4, 16, 16, 15, 15); + //glColor4f2(1, 1, 1, 1); + + if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { + LOGI("0, %d, %d, %d, 0\n", j, item->id, item->getAuxValue()); + ItemRenderer::renderGuiItemCorrect(minecraft->font, minecraft->textures, item, x, y); + } else if (item->getIcon() >= 0) { + LOGI("1, %d, %d, %d, %d\n", j, item->id, item->getAuxValue(), item->getIcon()); + } + ++j; + }*/ + int j = 0; + for(std::vector::iterator i = mItems.begin(); i != mItems.end(); ++i) { + ItemInstance* item = &(*i); + + //LOGI("desc: %d - %s. %d\n", i, item->toString().c_str()); + + int x = j%16 * 16; + int y = j/16 * 16; + if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { + LOGI("0, %d, %d, %d, 0\n", j, item->id, item->getAuxValue()); + ItemRenderer::renderGuiItemCorrect(minecraft->font, minecraft->textures, item, x, y); + } else if (item->getIcon() >= 0) { + LOGI("1, %d, %d, %d, %d\n", j, item->id, item->getAuxValue(), item->getIcon()); + } + j++; + } + //@todo: blit out something famous here + + //glRotatef2(-180, 1, 0, 0); + glEnable2(GL_DEPTH_TEST); + glDisable2(GL_BLEND); + + w.stop(); + w.printEvery(100, "render-blocksel"); + } + void removed(){} + + void addItem(ItemInstance* item) { + if(item == NULL) + return; + if (item->getAuxValue() < 0) return; + + bool found = false; + for(std::vector::iterator i = mItems.begin(); i != mItems.end(); ++i) { + ItemInstance *jitem = &*i; + if(jitem->id != item->id) continue; + if(jitem->isStackedByData() && jitem->getAuxValue() != item->getAuxValue()) continue; + + found = true; + break; + } + + if (!found) { + LOGI("Adding item: %s\n", item->getDescriptionId().c_str()); + mItems.push_back(*item); + } + } + +private: + std::vector mItems; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__PrerenderTilesScreen_H__*/ diff --git a/src/client/gui/screens/ProgressScreen.cpp b/src/client/gui/screens/ProgressScreen.cpp index 1bab3e5..1ec5ee6 100755 --- a/src/client/gui/screens/ProgressScreen.cpp +++ b/src/client/gui/screens/ProgressScreen.cpp @@ -1,99 +1,99 @@ -#include "ProgressScreen.h" -#include "DisconnectionScreen.h" -#include "../Gui.h" -#include "../Font.h" -#include "../../Minecraft.h" -#include "../../renderer/Tesselator.h" -#include "../../../SharedConstants.h" -#include "../../renderer/Textures.h" - -ProgressScreen::ProgressScreen() -: ticks(0) -{ -} - -void ProgressScreen::render( int xm, int ym, float a ) -{ - if (minecraft->isLevelGenerated()) { - minecraft->setScreen(NULL); - return; - } - - Tesselator& t = Tesselator::instance; - renderBackground(); - - minecraft->textures->loadAndBindTexture("gui/background.png"); - - const float s = 32; - t.begin(); - t.color(0x404040); - t.vertexUV(0, (float)height, 0, 0, height / s); - t.vertexUV((float)width, (float)height, 0, width / s, height / s); - t.vertexUV((float)width, 0, 0, width / s, 0); - t.vertexUV(0, 0, 0, 0, 0); - t.draw(); - - int i = minecraft->progressStagePercentage; - - if (i >= 0) { - int w = 100; - int h = 2; - int x = width / 2 - w / 2; - int y = height / 2 + 16; - - //printf("%d, %d - %d, %d\n", x, y, x + w, y + h); - - glDisable2(GL_TEXTURE_2D); - t.begin(); - t.color(0x808080); - t.vertex((float)x, (float)y, 0); - t.vertex((float)x, (float)(y + h), 0); - t.vertex((float)(x + w), (float)(y + h), 0); - t.vertex((float)(x + w), (float)y, 0); - - t.color(0x80ff80); - t.vertex((float)x, (float)y, 0); - t.vertex((float)x, (float)(y + h), 0); - t.vertex((float)(x + i), (float)(y + h), 0); - t.vertex((float)(x + i), (float)y, 0); - t.draw(); - glEnable2(GL_TEXTURE_2D); - } - - glEnable2(GL_BLEND); - - const char* title = "Generating world"; - minecraft->font->drawShadow(title, (float)((width - minecraft->font->width(title)) / 2), (float)(height / 2 - 4 - 16), 0xffffff); - - const char* status = minecraft->getProgressMessage(); - const int progressWidth = minecraft->font->width(status); - const int progressLeft = (width - progressWidth) / 2; - const int progressY = height / 2 - 4 + 8; - minecraft->font->drawShadow(status, (float)progressLeft, (float)progressY, 0xffffff); - -#if APPLE_DEMO_PROMOTION - drawCenteredString(minecraft->font, "This demonstration version", width/2, progressY + 36, 0xffffff); - drawCenteredString(minecraft->font, "does not allow saving games", width/2, progressY + 46, 0xffffff); -#endif - - // If we're locating the server, show our famous spinner! - bool isLocating = (minecraft->getProgressStatusId() == 0); - if (isLocating) { - const int spinnerX = progressLeft + progressWidth + 6; - static const char* spinnerTexts[] = {"-", "\\", "|", "/"}; - int n = ((int)(5.5f * getTimeS()) % 4); - drawCenteredString(minecraft->font, spinnerTexts[n], spinnerX, progressY, 0xffffffff); - } - - glDisable2(GL_BLEND); - sleepMs(50); -} - -bool ProgressScreen::isInGameScreen() { return false; } - -void ProgressScreen::tick() { - // After 10 seconds of not connecting -> write an error message and go back - if (++ticks == 10 * SharedConstants::TicksPerSecond && minecraft->getProgressStatusId() == 0) { - minecraft->setScreen( new DisconnectionScreen("Could not connect to server. Try again.") ); - } -} +#include "ProgressScreen.h" +#include "DisconnectionScreen.h" +#include "../Gui.h" +#include "../Font.h" +#include "../../Minecraft.h" +#include "../../renderer/Tesselator.h" +#include "../../../SharedConstants.h" +#include "../../renderer/Textures.h" + +ProgressScreen::ProgressScreen() +: ticks(0) +{ +} + +void ProgressScreen::render( int xm, int ym, float a ) +{ + if (minecraft->isLevelGenerated()) { + minecraft->setScreen(NULL); + return; + } + + Tesselator& t = Tesselator::instance; + renderBackground(); + + minecraft->textures->loadAndBindTexture("gui/background.png"); + + const float s = 32; + t.begin(); + t.color(0x404040); + t.vertexUV(0, (float)height, 0, 0, height / s); + t.vertexUV((float)width, (float)height, 0, width / s, height / s); + t.vertexUV((float)width, 0, 0, width / s, 0); + t.vertexUV(0, 0, 0, 0, 0); + t.draw(); + + int i = minecraft->progressStagePercentage; + + if (i >= 0) { + int w = 100; + int h = 2; + int x = width / 2 - w / 2; + int y = height / 2 + 16; + + //printf("%d, %d - %d, %d\n", x, y, x + w, y + h); + + glDisable2(GL_TEXTURE_2D); + t.begin(); + t.color(0x808080); + t.vertex((float)x, (float)y, 0); + t.vertex((float)x, (float)(y + h), 0); + t.vertex((float)(x + w), (float)(y + h), 0); + t.vertex((float)(x + w), (float)y, 0); + + t.color(0x80ff80); + t.vertex((float)x, (float)y, 0); + t.vertex((float)x, (float)(y + h), 0); + t.vertex((float)(x + i), (float)(y + h), 0); + t.vertex((float)(x + i), (float)y, 0); + t.draw(); + glEnable2(GL_TEXTURE_2D); + } + + glEnable2(GL_BLEND); + + const char* title = "Generating world"; + minecraft->font->drawShadow(title, (float)((width - minecraft->font->width(title)) / 2), (float)(height / 2 - 4 - 16), 0xffffff); + + const char* status = minecraft->getProgressMessage(); + const int progressWidth = minecraft->font->width(status); + const int progressLeft = (width - progressWidth) / 2; + const int progressY = height / 2 - 4 + 8; + minecraft->font->drawShadow(status, (float)progressLeft, (float)progressY, 0xffffff); + +#if APPLE_DEMO_PROMOTION + drawCenteredString(minecraft->font, "This demonstration version", width/2, progressY + 36, 0xffffff); + drawCenteredString(minecraft->font, "does not allow saving games", width/2, progressY + 46, 0xffffff); +#endif + + // If we're locating the server, show our famous spinner! + bool isLocating = (minecraft->getProgressStatusId() == 0); + if (isLocating) { + const int spinnerX = progressLeft + progressWidth + 6; + static const char* spinnerTexts[] = {"-", "\\", "|", "/"}; + int n = ((int)(5.5f * getTimeS()) % 4); + drawCenteredString(minecraft->font, spinnerTexts[n], spinnerX, progressY, 0xffffffff); + } + + glDisable2(GL_BLEND); + sleepMs(50); +} + +bool ProgressScreen::isInGameScreen() { return false; } + +void ProgressScreen::tick() { + // After 10 seconds of not connecting -> write an error message and go back + if (++ticks == 10 * SharedConstants::TicksPerSecond && minecraft->getProgressStatusId() == 0) { + minecraft->setScreen( new DisconnectionScreen("Could not connect to server. Try again.") ); + } +} diff --git a/src/client/gui/screens/ProgressScreen.h b/src/client/gui/screens/ProgressScreen.h index 762d4fa..882b21a 100755 --- a/src/client/gui/screens/ProgressScreen.h +++ b/src/client/gui/screens/ProgressScreen.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ProgressScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ProgressScreen_H__ - -#include "../Screen.h" - -class ProgressScreen: public Screen -{ -public: - ProgressScreen(); - - void render(int xm, int ym, float a); - bool isInGameScreen(); - - virtual void keyPressed(int eventKey) {} - - void tick(); -private: - int ticks; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ProgressScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ProgressScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ProgressScreen_H__ + +#include "../Screen.h" + +class ProgressScreen: public Screen +{ +public: + ProgressScreen(); + + void render(int xm, int ym, float a); + bool isInGameScreen(); + + virtual void keyPressed(int eventKey) {} + + void tick(); +private: + int ticks; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ProgressScreen_H__*/ diff --git a/src/client/gui/screens/RenameMPLevelScreen.cpp b/src/client/gui/screens/RenameMPLevelScreen.cpp index 7a5a7c3..10921c3 100755 --- a/src/client/gui/screens/RenameMPLevelScreen.cpp +++ b/src/client/gui/screens/RenameMPLevelScreen.cpp @@ -1,55 +1,55 @@ -#include "RenameMPLevelScreen.h" -#include "StartMenuScreen.h" -#include "DialogDefinitions.h" -#include "../Gui.h" -#include "../../Minecraft.h" -#include "../../../AppPlatform.h" -#include "../../../platform/log.h" -#include "../../../world/level/storage/LevelStorageSource.h" - - -static char ILLEGAL_FILE_CHARACTERS[] = { - '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' -}; - -RenameMPLevelScreen::RenameMPLevelScreen( const std::string& levelId ) -: _levelId(levelId) -{ -} - -void RenameMPLevelScreen::init() { - minecraft->platform()->createUserInput(DialogDefinitions::DIALOG_RENAME_MP_WORLD); -} - -void RenameMPLevelScreen::render(int xm, int ym, float a) -{ - renderBackground(); - - #ifdef WIN32 - minecraft->getLevelSource()->renameLevel(_levelId, "Save?Level"); - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - #else - int status = minecraft->platform()->getUserInputStatus(); - if (status > -1) { - if (status == 1) { - std::vector v = minecraft->platform()->getUserInput(); - - if (!v.empty()) { - // Read the level name. - // 1) Trim name 2) Remove all bad chars -) We don't have to getUniqueLevelName, since renameLevel will do that - std::string levelId = v[0]; - - for (int i = 0; i < sizeof(ILLEGAL_FILE_CHARACTERS) / sizeof(char); ++i) - levelId = Util::stringReplace(levelId, std::string(1, ILLEGAL_FILE_CHARACTERS[i]), ""); - if ((int)levelId.length() == 0) { - levelId = "saved_world"; - } - - minecraft->getLevelSource()->renameLevel(_levelId, levelId); - } - } - - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } - #endif -} +#include "RenameMPLevelScreen.h" +#include "StartMenuScreen.h" +#include "DialogDefinitions.h" +#include "../Gui.h" +#include "../../Minecraft.h" +#include "../../../AppPlatform.h" +#include "../../../platform/log.h" +#include "../../../world/level/storage/LevelStorageSource.h" + + +static char ILLEGAL_FILE_CHARACTERS[] = { + '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' +}; + +RenameMPLevelScreen::RenameMPLevelScreen( const std::string& levelId ) +: _levelId(levelId) +{ +} + +void RenameMPLevelScreen::init() { + minecraft->platform()->createUserInput(DialogDefinitions::DIALOG_RENAME_MP_WORLD); +} + +void RenameMPLevelScreen::render(int xm, int ym, float a) +{ + renderBackground(); + + #ifdef WIN32 + minecraft->getLevelSource()->renameLevel(_levelId, "Save?Level"); + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + #else + int status = minecraft->platform()->getUserInputStatus(); + if (status > -1) { + if (status == 1) { + std::vector v = minecraft->platform()->getUserInput(); + + if (!v.empty()) { + // Read the level name. + // 1) Trim name 2) Remove all bad chars -) We don't have to getUniqueLevelName, since renameLevel will do that + std::string levelId = v[0]; + + for (int i = 0; i < sizeof(ILLEGAL_FILE_CHARACTERS) / sizeof(char); ++i) + levelId = Util::stringReplace(levelId, std::string(1, ILLEGAL_FILE_CHARACTERS[i]), ""); + if ((int)levelId.length() == 0) { + levelId = "saved_world"; + } + + minecraft->getLevelSource()->renameLevel(_levelId, levelId); + } + } + + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } + #endif +} diff --git a/src/client/gui/screens/RenameMPLevelScreen.h b/src/client/gui/screens/RenameMPLevelScreen.h index 02d5fa4..22c857e 100755 --- a/src/client/gui/screens/RenameMPLevelScreen.h +++ b/src/client/gui/screens/RenameMPLevelScreen.h @@ -1,18 +1,18 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__RenameMPLevelScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__RenameMPLevelScreen_H__ - -#include "../Screen.h" - -class RenameMPLevelScreen: public Screen -{ -public: - RenameMPLevelScreen(const std::string& levelId); - - virtual void init(); - virtual void render(int xm, int ym, float a); - -private: - std::string _levelId; -}; - +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__RenameMPLevelScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__RenameMPLevelScreen_H__ + +#include "../Screen.h" + +class RenameMPLevelScreen: public Screen +{ +public: + RenameMPLevelScreen(const std::string& levelId); + + virtual void init(); + virtual void render(int xm, int ym, float a); + +private: + std::string _levelId; +}; + #endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__RenameMPLevelScreen_H__*/ \ No newline at end of file diff --git a/src/client/gui/screens/ScreenChooser.cpp b/src/client/gui/screens/ScreenChooser.cpp index 610a209..1702596 100755 --- a/src/client/gui/screens/ScreenChooser.cpp +++ b/src/client/gui/screens/ScreenChooser.cpp @@ -1,63 +1,63 @@ -#include "ScreenChooser.h" -#include "StartMenuScreen.h" -#include "SelectWorldScreen.h" -#include "JoinGameScreen.h" -#include "PauseScreen.h" -#include "RenameMPLevelScreen.h" -#include "ConsoleScreen.h" -#include "IngameBlockSelectionScreen.h" -#include "JoinByIPScreen.h" -#include "touch/TouchStartMenuScreen.h" -#include "touch/TouchSelectWorldScreen.h" -#include "touch/TouchJoinGameScreen.h" -#include "touch/TouchIngameBlockSelectionScreen.h" - -#include "../../Minecraft.h" - -#include "UsernameScreen.h" - -Screen* ScreenChooser::createScreen( ScreenId id ) -{ - Screen* screen = NULL; - - // :sob: - if (/* _mc->useTouchscreen() */ true) { - switch (id) { - case SCREEN_STARTMENU: screen = new Touch::StartMenuScreen(); break; - case SCREEN_SELECTWORLD: screen = new Touch::SelectWorldScreen();break; - case SCREEN_JOINGAME: screen = new Touch::JoinGameScreen(); break; - case SCREEN_PAUSE: screen = new PauseScreen(false); break; - case SCREEN_PAUSEPREV: screen = new PauseScreen(true); break; - case SCREEN_BLOCKSELECTION: screen = new Touch::IngameBlockSelectionScreen(); break; - case SCREEN_JOINBYIP: screen = new JoinByIPScreen(); break; - case SCREEN_CONSOLE: screen = new ConsoleScreen(); break; - case SCREEN_NONE: - default: - // Do nothing - break; - } - } else { - switch (id) { - case SCREEN_STARTMENU: screen = new StartMenuScreen(); break; - case SCREEN_SELECTWORLD: screen = new SelectWorldScreen();break; - case SCREEN_JOINGAME: screen = new JoinGameScreen(); break; - case SCREEN_PAUSE: screen = new PauseScreen(false); break; - case SCREEN_PAUSEPREV: screen = new PauseScreen(true); break; - case SCREEN_BLOCKSELECTION: screen = new IngameBlockSelectionScreen(); break; - case SCREEN_JOINBYIP: screen = new JoinByIPScreen(); break; - case SCREEN_CONSOLE: screen = new ConsoleScreen(); break; - case SCREEN_NONE: - default: - // Do nothing - break; - } - } - return screen; -} - -Screen* ScreenChooser::setScreen(ScreenId id) -{ - Screen* screen = createScreen(id); - _mc->setScreen(screen); - return screen; -} +#include "ScreenChooser.h" +#include "StartMenuScreen.h" +#include "SelectWorldScreen.h" +#include "JoinGameScreen.h" +#include "PauseScreen.h" +#include "RenameMPLevelScreen.h" +#include "ConsoleScreen.h" +#include "IngameBlockSelectionScreen.h" +#include "JoinByIPScreen.h" +#include "touch/TouchStartMenuScreen.h" +#include "touch/TouchSelectWorldScreen.h" +#include "touch/TouchJoinGameScreen.h" +#include "touch/TouchIngameBlockSelectionScreen.h" + +#include "../../Minecraft.h" + +#include "UsernameScreen.h" + +Screen* ScreenChooser::createScreen( ScreenId id ) +{ + Screen* screen = NULL; + + // :sob: + if (/* _mc->useTouchscreen() */ true) { + switch (id) { + case SCREEN_STARTMENU: screen = new Touch::StartMenuScreen(); break; + case SCREEN_SELECTWORLD: screen = new Touch::SelectWorldScreen();break; + case SCREEN_JOINGAME: screen = new Touch::JoinGameScreen(); break; + case SCREEN_PAUSE: screen = new PauseScreen(false); break; + case SCREEN_PAUSEPREV: screen = new PauseScreen(true); break; + case SCREEN_BLOCKSELECTION: screen = new Touch::IngameBlockSelectionScreen(); break; + case SCREEN_JOINBYIP: screen = new JoinByIPScreen(); break; + case SCREEN_CONSOLE: screen = new ConsoleScreen(); break; + case SCREEN_NONE: + default: + // Do nothing + break; + } + } else { + switch (id) { + case SCREEN_STARTMENU: screen = new StartMenuScreen(); break; + case SCREEN_SELECTWORLD: screen = new SelectWorldScreen();break; + case SCREEN_JOINGAME: screen = new JoinGameScreen(); break; + case SCREEN_PAUSE: screen = new PauseScreen(false); break; + case SCREEN_PAUSEPREV: screen = new PauseScreen(true); break; + case SCREEN_BLOCKSELECTION: screen = new IngameBlockSelectionScreen(); break; + case SCREEN_JOINBYIP: screen = new JoinByIPScreen(); break; + case SCREEN_CONSOLE: screen = new ConsoleScreen(); break; + case SCREEN_NONE: + default: + // Do nothing + break; + } + } + return screen; +} + +Screen* ScreenChooser::setScreen(ScreenId id) +{ + Screen* screen = createScreen(id); + _mc->setScreen(screen); + return screen; +} diff --git a/src/client/gui/screens/ScreenChooser.h b/src/client/gui/screens/ScreenChooser.h index 21d19e5..0a21aeb 100755 --- a/src/client/gui/screens/ScreenChooser.h +++ b/src/client/gui/screens/ScreenChooser.h @@ -1,32 +1,32 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ScreenChooser_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ScreenChooser_H__ - -enum ScreenId { - SCREEN_NONE, - SCREEN_STARTMENU, - SCREEN_JOINGAME, - SCREEN_PAUSE, - SCREEN_PAUSEPREV, - SCREEN_SELECTWORLD, - SCREEN_BLOCKSELECTION, - SCREEN_JOINBYIP, - SCREEN_CONSOLE -}; - -class Screen; -class Minecraft; - -class ScreenChooser -{ -public: - ScreenChooser(Minecraft* mc) - : _mc(mc) - {} - - Screen* createScreen(ScreenId id); - Screen* setScreen(ScreenId id); -private: - Minecraft* _mc; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ScreenChooser_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ScreenChooser_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ScreenChooser_H__ + +enum ScreenId { + SCREEN_NONE, + SCREEN_STARTMENU, + SCREEN_JOINGAME, + SCREEN_PAUSE, + SCREEN_PAUSEPREV, + SCREEN_SELECTWORLD, + SCREEN_BLOCKSELECTION, + SCREEN_JOINBYIP, + SCREEN_CONSOLE +}; + +class Screen; +class Minecraft; + +class ScreenChooser +{ +public: + ScreenChooser(Minecraft* mc) + : _mc(mc) + {} + + Screen* createScreen(ScreenId id); + Screen* setScreen(ScreenId id); +private: + Minecraft* _mc; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ScreenChooser_H__*/ diff --git a/src/client/gui/screens/SelectWorldScreen.cpp b/src/client/gui/screens/SelectWorldScreen.cpp index 596ea1a..a7cf110 100755 --- a/src/client/gui/screens/SelectWorldScreen.cpp +++ b/src/client/gui/screens/SelectWorldScreen.cpp @@ -1,468 +1,468 @@ -#include "SelectWorldScreen.h" -#include "StartMenuScreen.h" -#include "ProgressScreen.h" -#include "DialogDefinitions.h" -#include "../../renderer/Tesselator.h" -#include "../../../AppPlatform.h" -#include "../../../util/StringUtils.h" -#include "../../../util/Mth.h" -#include "../../../platform/input/Mouse.h" -#include "../../../Performance.h" -#include "../../../world/level/LevelSettings.h" - -#include -#include -#include "../../renderer/Textures.h" -#include "SimpleChooseLevelScreen.h" - -static float Max(float a, float b) { - return a>b? a : b; -} - -// -// World Selection List -// -WorldSelectionList::WorldSelectionList( Minecraft* minecraft, int width, int height ) -: _height(height), - hasPickedLevel(false), - currentTick(0), - stoppedTick(-1), - mode(0), - RolledSelectionListH(minecraft, width, height, 0, width, 26, height-32, 120) -{ -} - -int WorldSelectionList::getNumberOfItems() { - return (int)levels.size(); -} - -void WorldSelectionList::selectItem( int item, bool doubleClick ) { - //LOGI("sel: %d, item %d\n", selectedItem, item); - if (selectedItem < 0 || (selectedItem != item)) - return; - - if (!hasPickedLevel) { - hasPickedLevel = true; - pickedLevel = levels[item]; - } -} - -bool WorldSelectionList::isSelectedItem( int item ) { - return item == selectedItem; -} - -void WorldSelectionList::renderItem( int i, int x, int y, int h, Tesselator& t ) { - - int centerx = x + itemWidth/2; - - float a0 = Max(1.1f - std::abs( width / 2 - centerx ) * 0.0055f, 0.2f); - if (a0 > 1) a0 = 1; - int textColor = (int)(255.0f * a0) * 0x010101; - int textColor2 = (int)(140.0f * a0) * 0x010101; - - const int TY = y + 42; - const int TX = centerx - itemWidth / 2 + 5; - - StringVector v = _descriptions[i]; - drawString(minecraft->font, v[0].c_str(), TX, TY + 0, textColor); - drawString(minecraft->font, v[1].c_str(), TX, TY + 10, textColor2); - drawString(minecraft->font, v[2].c_str(), TX, TY + 20, textColor2); - drawString(minecraft->font, v[3].c_str(), TX, TY + 30, textColor2); - - minecraft->textures->loadAndBindTexture(_imageNames[i]); - t.color(0.3f, 1.0f, 0.2f); - - //float x0 = (float)x; - //float x1 = (float)x + (float)itemWidth; - - const float IY = (float)y - 8; - t.begin(); - t.color(textColor); - t.vertexUV((float)(centerx-32), IY, blitOffset, 0, 0); - t.vertexUV((float)(centerx-32), IY + 48, blitOffset, 0, 1); - t.vertexUV((float)(centerx+32), IY + 48, blitOffset, 1, 1); - t.vertexUV((float)(centerx+32), IY, blitOffset, 1, 0); - t.draw(); -} - -void WorldSelectionList::stepLeft() { - if (selectedItem > 0) { - td.start = xo; - td.stop = xo - itemWidth; - td.cur = 0; - td.dur = 8; - mode = 1; - tweenInited(); - } -} - -void WorldSelectionList::stepRight() { - if (selectedItem >= 0 && selectedItem < getNumberOfItems()-1) { - td.start = xo; - td.stop = xo + itemWidth; - td.cur = 0; - td.dur = 8; - mode = 1; - tweenInited(); - } -} - -void WorldSelectionList::commit() { - for (unsigned int i = 0; i < levels.size(); ++i) { - LevelSummary& level = levels[i]; - - std::stringstream ss; - ss << level.name << "/preview.png"; - TextureId id = Textures::InvalidId;//minecraft->textures->loadTexture(ss.str(), false); - - if (id != Textures::InvalidId) { - _imageNames.push_back( ss.str() ); - } else { - _imageNames.push_back("gui/default_world.png"); - } - - StringVector lines; - lines.push_back(level.name); - lines.push_back(minecraft->platform()->getDateString(level.lastPlayed)); - lines.push_back(level.id); - lines.push_back(LevelSettings::gameTypeToString(level.gameType)); - _descriptions.push_back(lines); - - selectedItem = 0; - } -} - -static float quadraticInOut(float t, float dur, float start, float stop) { - const float delta = stop - start; - const float T = (t / dur) * 2.0f; - if (T < 1) return 0.5f*delta*T*T + start; - return -0.5f*delta * ((T-1)*(T-3) - 1) + start; -} - -void WorldSelectionList::tick() -{ - RolledSelectionListH::tick(); - - ++currentTick; - - if (Mouse::isButtonDown(MouseAction::ACTION_LEFT) || dragState == 0) - return; - - // Handle the tween (when in "mode 1") - selectedItem = -1; - if (mode == 1) { - if (++td.cur == td.dur) { - mode = 0; - xInertia = 0; - xoo = xo = td.stop; - selectedItem = getItemAtPosition(width/2, height/2); - } else { - tweenInited(); - } - return; - } - - // It's still going fast, let it run - float speed = Mth::abs(xInertia); - bool slowEnoughToBeBothered = speed < 5.0f; - if (!slowEnoughToBeBothered) { - xInertia = xInertia * .9f; - return; - } - - xInertia *= 0.8f; - - if (speed < 1 && dragState < 0) { - const int offsetx = (width-itemWidth) / 2; - const float pxo = xo + offsetx; - int index = getItemAtXPositionRaw((int)(pxo - 10*xInertia)); - int indexPos = index*itemWidth; - - // Pick closest - float diff = (float)indexPos - pxo; - if (diff < -itemWidth/2) { - diff += itemWidth; - index++; - //indexPos += itemWidth; - } - if (Mth::abs(diff) < 1 && speed < 0.1f) { - selectedItem = getItemAtPosition(width/2, height/2); - return; - } - - td.start = xo; - td.stop = xo + diff; - td.cur = 0; - td.dur = (float) Mth::Min(7, 1 + (int)(Mth::abs(diff) * 0.25f)); - mode = 1; - //LOGI("inited-t %d\n", dragState); - tweenInited(); - } -} - -float WorldSelectionList::getPos( float alpha ) -{ - if (mode != 1) return RolledSelectionListH::getPos(alpha); - - float x0 = quadraticInOut(td.cur, td.dur, td.start, td.stop); - float x1 = quadraticInOut(td.cur+1, td.dur, td.start, td.stop); - return x0 + (x1-x0)*alpha; -} - -bool WorldSelectionList::capXPosition() { - bool capped = RolledSelectionListH::capXPosition(); - if (capped) mode = 0; - return capped; -} - -void WorldSelectionList::tweenInited() { - float x0 = quadraticInOut(td.cur, td.dur, td.start, td.stop); - float x1 = quadraticInOut(td.cur+1, td.dur, td.start, td.stop); - xInertia = x0-x1; // yes, it's all backwards and messed up.. -} - -// -// Select World Screen -// -SelectWorldScreen::SelectWorldScreen() -: bDelete (1, "Delete"), - bCreate (2, "Create new"), - bBack (3, "Back"), - bWorldView(4, ""), - worldsList(NULL), - _hasStartedLevel(false) -{ - bDelete.active = false; -} - -SelectWorldScreen::~SelectWorldScreen() -{ - delete worldsList; -} - -void SelectWorldScreen::buttonClicked(Button* button) -{ - if (button->id == bCreate.id) { - // open in-game world-creation screen instead of using platform dialog - if (!_hasStartedLevel) { - std::string name = getUniqueLevelName("world"); - minecraft->setScreen(new SimpleChooseLevelScreen(name)); - } - } - if (button->id == bDelete.id) { - if (isIndexValid(worldsList->selectedItem)) { - LevelSummary level = worldsList->levels[worldsList->selectedItem]; - LOGI("level: %s, %s\n", level.id.c_str(), level.name.c_str()); - minecraft->setScreen( new DeleteWorldScreen(level) ); - } - } - if (button->id == bBack.id) { - minecraft->cancelLocateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } - if (button->id == bWorldView.id) { - // Try to "click" the item in the middle - worldsList->selectItem( worldsList->getItemAtPosition(width/2, height/2), false ); - } -} - -bool SelectWorldScreen::handleBackEvent(bool isDown) -{ - if (!isDown) - { - minecraft->cancelLocateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } - return true; -} - -bool SelectWorldScreen::isIndexValid( int index ) -{ - return worldsList && index >= 0 && index < worldsList->getNumberOfItems(); -} - -static char ILLEGAL_FILE_CHARACTERS[] = { - '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' -}; - -void SelectWorldScreen::tick() -{ - worldsList->tick(); - - if (worldsList->hasPickedLevel) { - minecraft->selectLevel(worldsList->pickedLevel.id, worldsList->pickedLevel.name, LevelSettings::None()); - minecraft->hostMultiplayer(); - minecraft->setScreen(new ProgressScreen()); - _hasStartedLevel = true; - return; - } - - // copy the currently selected item - LevelSummary selectedWorld; - //bool hasSelection = false; - if (isIndexValid(worldsList->selectedItem)) - { - selectedWorld = worldsList->levels[worldsList->selectedItem]; - //hasSelection = true; - } - - bDelete.active = isIndexValid(worldsList->selectedItem); -} - -void SelectWorldScreen::init() -{ - worldsList = new WorldSelectionList(minecraft, width, height); - loadLevelSource(); - worldsList->commit(); - - buttons.push_back(&bDelete); - buttons.push_back(&bCreate); - buttons.push_back(&bBack); - - _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); - - tabButtons.push_back(&bWorldView); - tabButtons.push_back(&bDelete); - tabButtons.push_back(&bCreate); - tabButtons.push_back(&bBack); -} - -void SelectWorldScreen::setupPositions() { - int yBase = height - 28; - - //#ifdef ANDROID - bCreate.y = yBase; - bBack.y = yBase; - bDelete.y = yBase; - - bBack.width = bDelete.width = bCreate.width = 84; - //bDelete.h = bCreate.h = bBack.h = 24; - //#endif - - // Center buttons - bDelete.x = width / 2 - 4 - bDelete.width - bDelete.width / 2; - bCreate.x = width / 2 - bCreate.width / 2; - bBack.x = width / 2 + 4 + bCreate.width - bBack.width / 2; -} - -void SelectWorldScreen::render( int xm, int ym, float a ) -{ - //Performance::watches.get("sws-full").start(); - //Performance::watches.get("sws-renderbg").start(); - renderBackground(); - //Performance::watches.get("sws-renderbg").stop(); - //Performance::watches.get("sws-worlds").start(); - - worldsList->setComponentSelected(bWorldView.selected); - // #ifdef PLATFORM_DESKTOP - - // desktop: render the list normally (mouse wheel handled separately below) - if (_mouseHasBeenUp) - worldsList->render(xm, ym, a); - else { - worldsList->render(0, 0, a); - _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); - } - // #else - // if (_mouseHasBeenUp) - // worldsList->render(xm, ym, a); - // else { - // worldsList->render(0, 0, a); - // _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); - // } - // #endif - - //Performance::watches.get("sws-worlds").stop(); - //Performance::watches.get("sws-screen").start(); - Screen::render(xm, ym, a); - //Performance::watches.get("sws-screen").stop(); - - //Performance::watches.get("sws-string").start(); - drawCenteredString(minecraft->font, "Select world", width / 2, 8, 0xffffffff); - //Performance::watches.get("sws-string").stop(); - - //Performance::watches.get("sws-full").stop(); - //Performance::watches.printEvery(128); -} - -void SelectWorldScreen::loadLevelSource() -{ - LevelStorageSource* levelSource = minecraft->getLevelSource(); - levelSource->getLevelList(levels); - std::sort(levels.begin(), levels.end()); - - for (unsigned int i = 0; i < levels.size(); ++i) { - if (levels[i].id != LevelStorageSource::TempLevelId) - worldsList->levels.push_back( levels[i] ); - } -} - - -std::string SelectWorldScreen::getUniqueLevelName( const std::string& level ) -{ - std::set Set; - for (unsigned int i = 0; i < levels.size(); ++i) - Set.insert(levels[i].id); - - std::string s = level; - while ( Set.find(s) != Set.end() ) - s += "-"; - return s; -} - -bool SelectWorldScreen::isInGameScreen() { return true; } - -void SelectWorldScreen::mouseWheel(int dx, int dy, int xm, int ym) -{ - if (!worldsList) - return; - if (dy == 0) - return; - int num = worldsList->getNumberOfItems(); - int idx = worldsList->selectedItem; - if (dy > 0) { - if (idx > 0) { - idx--; - worldsList->stepLeft(); - } - } else { - if (idx < num - 1) { - idx++; - worldsList->stepRight(); - } - } - worldsList->selectedItem = idx; -} - -void SelectWorldScreen::keyPressed( int eventKey ) -{ - if (bWorldView.selected) { - if (eventKey == minecraft->options.getIntValue(OPTIONS_KEY_RIGHT)) - worldsList->stepLeft(); - if (eventKey == minecraft->options.getIntValue(OPTIONS_KEY_LEFT)) - worldsList->stepRight(); - } - - Screen::keyPressed(eventKey); -} - -// -// Delete World Screen -// -DeleteWorldScreen::DeleteWorldScreen(const LevelSummary& level) -: ConfirmScreen(NULL, "Are you sure you want to delete this world?", - "'" + level.name + "' will be lost forever!", - "Delete", "Cancel", 0), - _level(level) -{ - tabButtonIndex = 1; -} - -void DeleteWorldScreen::postResult( bool isOk ) -{ - if (isOk) { - LevelStorageSource* storageSource = minecraft->getLevelSource(); - storageSource->deleteLevel(_level.id); - } - minecraft->screenChooser.setScreen(SCREEN_SELECTWORLD); -} +#include "SelectWorldScreen.h" +#include "StartMenuScreen.h" +#include "ProgressScreen.h" +#include "DialogDefinitions.h" +#include "../../renderer/Tesselator.h" +#include "../../../AppPlatform.h" +#include "../../../util/StringUtils.h" +#include "../../../util/Mth.h" +#include "../../../platform/input/Mouse.h" +#include "../../../Performance.h" +#include "../../../world/level/LevelSettings.h" + +#include +#include +#include "../../renderer/Textures.h" +#include "SimpleChooseLevelScreen.h" + +static float Max(float a, float b) { + return a>b? a : b; +} + +// +// World Selection List +// +WorldSelectionList::WorldSelectionList( Minecraft* minecraft, int width, int height ) +: _height(height), + hasPickedLevel(false), + currentTick(0), + stoppedTick(-1), + mode(0), + RolledSelectionListH(minecraft, width, height, 0, width, 26, height-32, 120) +{ +} + +int WorldSelectionList::getNumberOfItems() { + return (int)levels.size(); +} + +void WorldSelectionList::selectItem( int item, bool doubleClick ) { + //LOGI("sel: %d, item %d\n", selectedItem, item); + if (selectedItem < 0 || (selectedItem != item)) + return; + + if (!hasPickedLevel) { + hasPickedLevel = true; + pickedLevel = levels[item]; + } +} + +bool WorldSelectionList::isSelectedItem( int item ) { + return item == selectedItem; +} + +void WorldSelectionList::renderItem( int i, int x, int y, int h, Tesselator& t ) { + + int centerx = x + itemWidth/2; + + float a0 = Max(1.1f - std::abs( width / 2 - centerx ) * 0.0055f, 0.2f); + if (a0 > 1) a0 = 1; + int textColor = (int)(255.0f * a0) * 0x010101; + int textColor2 = (int)(140.0f * a0) * 0x010101; + + const int TY = y + 42; + const int TX = centerx - itemWidth / 2 + 5; + + StringVector v = _descriptions[i]; + drawString(minecraft->font, v[0].c_str(), TX, TY + 0, textColor); + drawString(minecraft->font, v[1].c_str(), TX, TY + 10, textColor2); + drawString(minecraft->font, v[2].c_str(), TX, TY + 20, textColor2); + drawString(minecraft->font, v[3].c_str(), TX, TY + 30, textColor2); + + minecraft->textures->loadAndBindTexture(_imageNames[i]); + t.color(0.3f, 1.0f, 0.2f); + + //float x0 = (float)x; + //float x1 = (float)x + (float)itemWidth; + + const float IY = (float)y - 8; + t.begin(); + t.color(textColor); + t.vertexUV((float)(centerx-32), IY, blitOffset, 0, 0); + t.vertexUV((float)(centerx-32), IY + 48, blitOffset, 0, 1); + t.vertexUV((float)(centerx+32), IY + 48, blitOffset, 1, 1); + t.vertexUV((float)(centerx+32), IY, blitOffset, 1, 0); + t.draw(); +} + +void WorldSelectionList::stepLeft() { + if (selectedItem > 0) { + td.start = xo; + td.stop = xo - itemWidth; + td.cur = 0; + td.dur = 8; + mode = 1; + tweenInited(); + } +} + +void WorldSelectionList::stepRight() { + if (selectedItem >= 0 && selectedItem < getNumberOfItems()-1) { + td.start = xo; + td.stop = xo + itemWidth; + td.cur = 0; + td.dur = 8; + mode = 1; + tweenInited(); + } +} + +void WorldSelectionList::commit() { + for (unsigned int i = 0; i < levels.size(); ++i) { + LevelSummary& level = levels[i]; + + std::stringstream ss; + ss << level.name << "/preview.png"; + TextureId id = Textures::InvalidId;//minecraft->textures->loadTexture(ss.str(), false); + + if (id != Textures::InvalidId) { + _imageNames.push_back( ss.str() ); + } else { + _imageNames.push_back("gui/default_world.png"); + } + + StringVector lines; + lines.push_back(level.name); + lines.push_back(minecraft->platform()->getDateString(level.lastPlayed)); + lines.push_back(level.id); + lines.push_back(LevelSettings::gameTypeToString(level.gameType)); + _descriptions.push_back(lines); + + selectedItem = 0; + } +} + +static float quadraticInOut(float t, float dur, float start, float stop) { + const float delta = stop - start; + const float T = (t / dur) * 2.0f; + if (T < 1) return 0.5f*delta*T*T + start; + return -0.5f*delta * ((T-1)*(T-3) - 1) + start; +} + +void WorldSelectionList::tick() +{ + RolledSelectionListH::tick(); + + ++currentTick; + + if (Mouse::isButtonDown(MouseAction::ACTION_LEFT) || dragState == 0) + return; + + // Handle the tween (when in "mode 1") + selectedItem = -1; + if (mode == 1) { + if (++td.cur == td.dur) { + mode = 0; + xInertia = 0; + xoo = xo = td.stop; + selectedItem = getItemAtPosition(width/2, height/2); + } else { + tweenInited(); + } + return; + } + + // It's still going fast, let it run + float speed = Mth::abs(xInertia); + bool slowEnoughToBeBothered = speed < 5.0f; + if (!slowEnoughToBeBothered) { + xInertia = xInertia * .9f; + return; + } + + xInertia *= 0.8f; + + if (speed < 1 && dragState < 0) { + const int offsetx = (width-itemWidth) / 2; + const float pxo = xo + offsetx; + int index = getItemAtXPositionRaw((int)(pxo - 10*xInertia)); + int indexPos = index*itemWidth; + + // Pick closest + float diff = (float)indexPos - pxo; + if (diff < -itemWidth/2) { + diff += itemWidth; + index++; + //indexPos += itemWidth; + } + if (Mth::abs(diff) < 1 && speed < 0.1f) { + selectedItem = getItemAtPosition(width/2, height/2); + return; + } + + td.start = xo; + td.stop = xo + diff; + td.cur = 0; + td.dur = (float) Mth::Min(7, 1 + (int)(Mth::abs(diff) * 0.25f)); + mode = 1; + //LOGI("inited-t %d\n", dragState); + tweenInited(); + } +} + +float WorldSelectionList::getPos( float alpha ) +{ + if (mode != 1) return RolledSelectionListH::getPos(alpha); + + float x0 = quadraticInOut(td.cur, td.dur, td.start, td.stop); + float x1 = quadraticInOut(td.cur+1, td.dur, td.start, td.stop); + return x0 + (x1-x0)*alpha; +} + +bool WorldSelectionList::capXPosition() { + bool capped = RolledSelectionListH::capXPosition(); + if (capped) mode = 0; + return capped; +} + +void WorldSelectionList::tweenInited() { + float x0 = quadraticInOut(td.cur, td.dur, td.start, td.stop); + float x1 = quadraticInOut(td.cur+1, td.dur, td.start, td.stop); + xInertia = x0-x1; // yes, it's all backwards and messed up.. +} + +// +// Select World Screen +// +SelectWorldScreen::SelectWorldScreen() +: bDelete (1, "Delete"), + bCreate (2, "Create new"), + bBack (3, "Back"), + bWorldView(4, ""), + worldsList(NULL), + _hasStartedLevel(false) +{ + bDelete.active = false; +} + +SelectWorldScreen::~SelectWorldScreen() +{ + delete worldsList; +} + +void SelectWorldScreen::buttonClicked(Button* button) +{ + if (button->id == bCreate.id) { + // open in-game world-creation screen instead of using platform dialog + if (!_hasStartedLevel) { + std::string name = getUniqueLevelName("world"); + minecraft->setScreen(new SimpleChooseLevelScreen(name)); + } + } + if (button->id == bDelete.id) { + if (isIndexValid(worldsList->selectedItem)) { + LevelSummary level = worldsList->levels[worldsList->selectedItem]; + LOGI("level: %s, %s\n", level.id.c_str(), level.name.c_str()); + minecraft->setScreen( new DeleteWorldScreen(level) ); + } + } + if (button->id == bBack.id) { + minecraft->cancelLocateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } + if (button->id == bWorldView.id) { + // Try to "click" the item in the middle + worldsList->selectItem( worldsList->getItemAtPosition(width/2, height/2), false ); + } +} + +bool SelectWorldScreen::handleBackEvent(bool isDown) +{ + if (!isDown) + { + minecraft->cancelLocateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } + return true; +} + +bool SelectWorldScreen::isIndexValid( int index ) +{ + return worldsList && index >= 0 && index < worldsList->getNumberOfItems(); +} + +static char ILLEGAL_FILE_CHARACTERS[] = { + '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' +}; + +void SelectWorldScreen::tick() +{ + worldsList->tick(); + + if (worldsList->hasPickedLevel) { + minecraft->selectLevel(worldsList->pickedLevel.id, worldsList->pickedLevel.name, LevelSettings::None()); + minecraft->hostMultiplayer(); + minecraft->setScreen(new ProgressScreen()); + _hasStartedLevel = true; + return; + } + + // copy the currently selected item + LevelSummary selectedWorld; + //bool hasSelection = false; + if (isIndexValid(worldsList->selectedItem)) + { + selectedWorld = worldsList->levels[worldsList->selectedItem]; + //hasSelection = true; + } + + bDelete.active = isIndexValid(worldsList->selectedItem); +} + +void SelectWorldScreen::init() +{ + worldsList = new WorldSelectionList(minecraft, width, height); + loadLevelSource(); + worldsList->commit(); + + buttons.push_back(&bDelete); + buttons.push_back(&bCreate); + buttons.push_back(&bBack); + + _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); + + tabButtons.push_back(&bWorldView); + tabButtons.push_back(&bDelete); + tabButtons.push_back(&bCreate); + tabButtons.push_back(&bBack); +} + +void SelectWorldScreen::setupPositions() { + int yBase = height - 28; + + //#ifdef ANDROID + bCreate.y = yBase; + bBack.y = yBase; + bDelete.y = yBase; + + bBack.width = bDelete.width = bCreate.width = 84; + //bDelete.h = bCreate.h = bBack.h = 24; + //#endif + + // Center buttons + bDelete.x = width / 2 - 4 - bDelete.width - bDelete.width / 2; + bCreate.x = width / 2 - bCreate.width / 2; + bBack.x = width / 2 + 4 + bCreate.width - bBack.width / 2; +} + +void SelectWorldScreen::render( int xm, int ym, float a ) +{ + //Performance::watches.get("sws-full").start(); + //Performance::watches.get("sws-renderbg").start(); + renderBackground(); + //Performance::watches.get("sws-renderbg").stop(); + //Performance::watches.get("sws-worlds").start(); + + worldsList->setComponentSelected(bWorldView.selected); + // #ifdef PLATFORM_DESKTOP + + // desktop: render the list normally (mouse wheel handled separately below) + if (_mouseHasBeenUp) + worldsList->render(xm, ym, a); + else { + worldsList->render(0, 0, a); + _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); + } + // #else + // if (_mouseHasBeenUp) + // worldsList->render(xm, ym, a); + // else { + // worldsList->render(0, 0, a); + // _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); + // } + // #endif + + //Performance::watches.get("sws-worlds").stop(); + //Performance::watches.get("sws-screen").start(); + Screen::render(xm, ym, a); + //Performance::watches.get("sws-screen").stop(); + + //Performance::watches.get("sws-string").start(); + drawCenteredString(minecraft->font, "Select world", width / 2, 8, 0xffffffff); + //Performance::watches.get("sws-string").stop(); + + //Performance::watches.get("sws-full").stop(); + //Performance::watches.printEvery(128); +} + +void SelectWorldScreen::loadLevelSource() +{ + LevelStorageSource* levelSource = minecraft->getLevelSource(); + levelSource->getLevelList(levels); + std::sort(levels.begin(), levels.end()); + + for (unsigned int i = 0; i < levels.size(); ++i) { + if (levels[i].id != LevelStorageSource::TempLevelId) + worldsList->levels.push_back( levels[i] ); + } +} + + +std::string SelectWorldScreen::getUniqueLevelName( const std::string& level ) +{ + std::set Set; + for (unsigned int i = 0; i < levels.size(); ++i) + Set.insert(levels[i].id); + + std::string s = level; + while ( Set.find(s) != Set.end() ) + s += "-"; + return s; +} + +bool SelectWorldScreen::isInGameScreen() { return true; } + +void SelectWorldScreen::mouseWheel(int dx, int dy, int xm, int ym) +{ + if (!worldsList) + return; + if (dy == 0) + return; + int num = worldsList->getNumberOfItems(); + int idx = worldsList->selectedItem; + if (dy > 0) { + if (idx > 0) { + idx--; + worldsList->stepLeft(); + } + } else { + if (idx < num - 1) { + idx++; + worldsList->stepRight(); + } + } + worldsList->selectedItem = idx; +} + +void SelectWorldScreen::keyPressed( int eventKey ) +{ + if (bWorldView.selected) { + if (eventKey == minecraft->options.getIntValue(OPTIONS_KEY_RIGHT)) + worldsList->stepLeft(); + if (eventKey == minecraft->options.getIntValue(OPTIONS_KEY_LEFT)) + worldsList->stepRight(); + } + + Screen::keyPressed(eventKey); +} + +// +// Delete World Screen +// +DeleteWorldScreen::DeleteWorldScreen(const LevelSummary& level) +: ConfirmScreen(NULL, "Are you sure you want to delete this world?", + "'" + level.name + "' will be lost forever!", + "Delete", "Cancel", 0), + _level(level) +{ + tabButtonIndex = 1; +} + +void DeleteWorldScreen::postResult( bool isOk ) +{ + if (isOk) { + LevelStorageSource* storageSource = minecraft->getLevelSource(); + storageSource->deleteLevel(_level.id); + } + minecraft->screenChooser.setScreen(SCREEN_SELECTWORLD); +} diff --git a/src/client/gui/screens/SelectWorldScreen.h b/src/client/gui/screens/SelectWorldScreen.h index 12c6c7e..677422f 100755 --- a/src/client/gui/screens/SelectWorldScreen.h +++ b/src/client/gui/screens/SelectWorldScreen.h @@ -1,112 +1,112 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__SelectWorldScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__SelectWorldScreen_H__ - -#include "../Screen.h" -#include "../TweenData.h" -#include "../components/Button.h" -#include "../components/RolledSelectionListH.h" -#include "../../Minecraft.h" -#include "../../../world/level/storage/LevelStorageSource.h" - - -class SelectWorldScreen; - -// -// Scrolling World selection list -// -class WorldSelectionList : public RolledSelectionListH -{ -public: - WorldSelectionList(Minecraft* _minecraft, int _width, int _height); - virtual void tick(); - void stepLeft(); - void stepRight(); - - void commit(); -protected: - virtual int getNumberOfItems(); - virtual void selectItem(int item, bool doubleClick); - virtual bool isSelectedItem(int item); - - virtual void renderBackground() {} - virtual void renderItem(int i, int x, int y, int h, Tesselator& t); - virtual float getPos(float alpha); - virtual void touched() { mode = 0; } - virtual bool capXPosition(); -private: - TweenData td; - void tweenInited(); - - int selectedItem; - int _height; - LevelSummaryList levels; - std::vector _descriptions; - StringVector _imageNames; - - bool hasPickedLevel; - LevelSummary pickedLevel; - - int stoppedTick; - int currentTick; - float accRatio; - int mode; - - friend class SelectWorldScreen; -}; - -// -// Delete World screen -// -#include "ConfirmScreen.h" -class DeleteWorldScreen: public ConfirmScreen -{ -public: - DeleteWorldScreen(const LevelSummary& levelId); -protected: - virtual void postResult(bool isOk); -private: - LevelSummary _level; -}; - - -// -// Select world screen -// -class SelectWorldScreen: public Screen -{ -public: - SelectWorldScreen(); - virtual ~SelectWorldScreen(); - - virtual void init(); - virtual void setupPositions(); - virtual void tick(); - - virtual bool isIndexValid(int index); - virtual bool handleBackEvent(bool isDown); - virtual void buttonClicked(Button* button); - virtual void keyPressed(int eventKey); - - void render(int xm, int ym, float a); - - // mouse wheel scroll (new in desktop implementation) - virtual void mouseWheel(int dx, int dy, int xm, int ym); - - bool isInGameScreen(); -private: - void loadLevelSource(); - std::string getUniqueLevelName(const std::string& level); - - Button bDelete; - Button bCreate; - Button bBack; - Button bWorldView; - WorldSelectionList* worldsList; - LevelSummaryList levels; - - bool _mouseHasBeenUp; - bool _hasStartedLevel; - //LevelStorageSource* levels; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__SelectWorldScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__SelectWorldScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__SelectWorldScreen_H__ + +#include "../Screen.h" +#include "../TweenData.h" +#include "../components/Button.h" +#include "../components/RolledSelectionListH.h" +#include "../../Minecraft.h" +#include "../../../world/level/storage/LevelStorageSource.h" + + +class SelectWorldScreen; + +// +// Scrolling World selection list +// +class WorldSelectionList : public RolledSelectionListH +{ +public: + WorldSelectionList(Minecraft* _minecraft, int _width, int _height); + virtual void tick(); + void stepLeft(); + void stepRight(); + + void commit(); +protected: + virtual int getNumberOfItems(); + virtual void selectItem(int item, bool doubleClick); + virtual bool isSelectedItem(int item); + + virtual void renderBackground() {} + virtual void renderItem(int i, int x, int y, int h, Tesselator& t); + virtual float getPos(float alpha); + virtual void touched() { mode = 0; } + virtual bool capXPosition(); +private: + TweenData td; + void tweenInited(); + + int selectedItem; + int _height; + LevelSummaryList levels; + std::vector _descriptions; + StringVector _imageNames; + + bool hasPickedLevel; + LevelSummary pickedLevel; + + int stoppedTick; + int currentTick; + float accRatio; + int mode; + + friend class SelectWorldScreen; +}; + +// +// Delete World screen +// +#include "ConfirmScreen.h" +class DeleteWorldScreen: public ConfirmScreen +{ +public: + DeleteWorldScreen(const LevelSummary& levelId); +protected: + virtual void postResult(bool isOk); +private: + LevelSummary _level; +}; + + +// +// Select world screen +// +class SelectWorldScreen: public Screen +{ +public: + SelectWorldScreen(); + virtual ~SelectWorldScreen(); + + virtual void init(); + virtual void setupPositions(); + virtual void tick(); + + virtual bool isIndexValid(int index); + virtual bool handleBackEvent(bool isDown); + virtual void buttonClicked(Button* button); + virtual void keyPressed(int eventKey); + + void render(int xm, int ym, float a); + + // mouse wheel scroll (new in desktop implementation) + virtual void mouseWheel(int dx, int dy, int xm, int ym); + + bool isInGameScreen(); +private: + void loadLevelSource(); + std::string getUniqueLevelName(const std::string& level); + + Button bDelete; + Button bCreate; + Button bBack; + Button bWorldView; + WorldSelectionList* worldsList; + LevelSummaryList levels; + + bool _mouseHasBeenUp; + bool _hasStartedLevel; + //LevelStorageSource* levels; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__SelectWorldScreen_H__*/ diff --git a/src/client/gui/screens/SimpleChooseLevelScreen.cpp b/src/client/gui/screens/SimpleChooseLevelScreen.cpp index c5a1278..c11a7c7 100755 --- a/src/client/gui/screens/SimpleChooseLevelScreen.cpp +++ b/src/client/gui/screens/SimpleChooseLevelScreen.cpp @@ -1,260 +1,260 @@ -#include "SimpleChooseLevelScreen.h" -#include "ProgressScreen.h" -#include "ScreenChooser.h" -#include "../components/Button.h" -#include "../components/ImageButton.h" -#include "../../Minecraft.h" -#include "../../../world/level/LevelSettings.h" -#include "../../../platform/time.h" -#include "../../../platform/input/Keyboard.h" -#include "../../../platform/log.h" - -SimpleChooseLevelScreen::SimpleChooseLevelScreen(const std::string& levelName) -: bHeader(0), - bGamemode(0), - bCheats(0), - bBack(0), - bCreate(0), - levelName(levelName), - hasChosen(false), - gamemode(GameType::Survival), - cheatsEnabled(false), - tLevelName(0, "World name"), - tSeed(1, "World seed") -{ -} - -SimpleChooseLevelScreen::~SimpleChooseLevelScreen() -{ - if (bHeader) delete bHeader; - delete bGamemode; - delete bCheats; - delete bBack; - delete bCreate; -} - -void SimpleChooseLevelScreen::init() -{ - // make sure the base class loads the existing level list; the - // derived screen uses ChooseLevelScreen::getUniqueLevelName(), which - // depends on `levels` being populated. omitting this used to result - // in duplicate IDs ("creating the second world would load the - // first") when the name already existed. - ChooseLevelScreen::init(); - - tLevelName.text = "New world"; - - // header + close button - bHeader = new Touch::THeader(0, "Create World"); - // create the back/X button as ImageButton like CreditsScreen - bBack = new ImageButton(2, ""); - { - ImageDef def; - def.name = "gui/touchgui.png"; - def.width = 34; - def.height = 26; - def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); - bBack->setImageDef(def, true); - } - if (/* minecraft->useTouchscreen() */ true) { - bGamemode = new Touch::TButton(1, "Survival mode"); - bCheats = new Touch::TButton(4, "Cheats: Off"); - bCreate = new Touch::TButton(3, "Create"); - } else { - bGamemode = new Button(1, "Survival mode"); - bCheats = new Button(4, "Cheats: Off"); - bCreate = new Button(3, "Create"); - } - - buttons.push_back(bHeader); - buttons.push_back(bBack); - buttons.push_back(bGamemode); - buttons.push_back(bCheats); - buttons.push_back(bCreate); - - tabButtons.push_back(bGamemode); - tabButtons.push_back(bCheats); - tabButtons.push_back(bBack); - tabButtons.push_back(bCreate); - - textBoxes.push_back(&tLevelName); - textBoxes.push_back(&tSeed); -} - -void SimpleChooseLevelScreen::setupPositions() -{ - int buttonHeight = bBack->height; - - // position back button in upper-right - bBack->x = width - bBack->width; - bBack->y = 0; - - // header occupies remaining top bar - if (bHeader) { - bHeader->x = 0; - bHeader->y = 0; - bHeader->width = width - bBack->width; - bHeader->height = buttonHeight; - } - - // layout the form elements below the header - int centerX = width / 2; - const int padding = 5; - - tLevelName.width = tSeed.width = 200; - tLevelName.x = centerX - tLevelName.width / 2; - tLevelName.y = buttonHeight + 20; - - tSeed.x = tLevelName.x; - tSeed.y = tLevelName.y + 30; - - const int buttonWidth = 120; - const int buttonSpacing = 10; - const int totalButtonWidth = buttonWidth * 2 + buttonSpacing; - - bGamemode->width = buttonWidth; - bCheats->width = buttonWidth; - - bGamemode->x = centerX - totalButtonWidth / 2; - bCheats->x = bGamemode->x + buttonWidth + buttonSpacing; - - // compute vertical centre for buttons in remaining space - { - int bottomPad = 20; - int availTop = buttonHeight + 20 + 30 + 10; // just below seed - int availBottom = height - bottomPad - bCreate->height - 10; // leave some gap before create - int availHeight = availBottom - availTop; - if (availHeight < 0) availHeight = 0; - int y = availTop + (availHeight - bGamemode->height) / 2; - bGamemode->y = y; - bCheats->y = y; - } - - bCreate->width = 100; - bCreate->x = centerX - bCreate->width / 2; - int bottomPadding = 20; - bCreate->y = height - bottomPadding - bCreate->height; -} - -void SimpleChooseLevelScreen::tick() -{ - // let any textboxes handle their own blinking/input - for (auto* tb : textBoxes) - tb->tick(minecraft); -} - -void SimpleChooseLevelScreen::render( int xm, int ym, float a ) -{ - renderDirtBackground(0); - glEnable2(GL_BLEND); - - const char* modeDesc = NULL; - if (gamemode == GameType::Survival) { - modeDesc = "Mobs, health and gather resources"; - } else if (gamemode == GameType::Creative) { - modeDesc = "Unlimited resources and flying"; - } - if (modeDesc) { - drawCenteredString(minecraft->font, modeDesc, width / 2, bGamemode->y + bGamemode->height + 4, 0xffcccccc); - } - - drawString(minecraft->font, "World name:", tLevelName.x, tLevelName.y - Font::DefaultLineHeight - 2, 0xffcccccc); - drawString(minecraft->font, "World seed:", tSeed.x, tSeed.y - Font::DefaultLineHeight - 2, 0xffcccccc); - - Screen::render(xm, ym, a); - glDisable2(GL_BLEND); -} - -// mouse clicks should also manage textbox focus explicitly -void SimpleChooseLevelScreen::mouseClicked(int x, int y, int buttonNum) -{ - if (buttonNum == MouseAction::ACTION_LEFT) { - // determine if the click landed on either textbox or its label above - int lvlTop = tLevelName.y - (Font::DefaultLineHeight + 4); - int lvlBottom = tLevelName.y + tLevelName.height; - int lvlLeft = tLevelName.x; - int lvlRight = tLevelName.x + tLevelName.width; - bool clickedLevel = x >= lvlLeft && x < lvlRight && y >= lvlTop && y < lvlBottom; - - int seedTop = tSeed.y - (Font::DefaultLineHeight + 4); - int seedBottom = tSeed.y + tSeed.height; - int seedLeft = tSeed.x; - int seedRight = tSeed.x + tSeed.width; - bool clickedSeed = x >= seedLeft && x < seedRight && y >= seedTop && y < seedBottom; - - if (clickedLevel) { - LOGI("SimpleChooseLevelScreen: level textbox clicked (%d,%d)\n", x, y); - tLevelName.setFocus(minecraft); - tSeed.loseFocus(minecraft); - } else if (clickedSeed) { - LOGI("SimpleChooseLevelScreen: seed textbox clicked (%d,%d)\n", x, y); - tSeed.setFocus(minecraft); - tLevelName.loseFocus(minecraft); - } else { - // click outside both fields -> blur both - tLevelName.loseFocus(minecraft); - tSeed.loseFocus(minecraft); - } - } - - // allow normal button and textbox handling too - Screen::mouseClicked(x, y, buttonNum); -} - -void SimpleChooseLevelScreen::buttonClicked( Button* button ) -{ - if (hasChosen) - return; - - if (button == bGamemode) { - gamemode ^= 1; - bGamemode->msg = (gamemode == GameType::Survival) ? "Survival mode" : "Creative mode"; - return; - } - - if (button == bCheats) { - cheatsEnabled = !cheatsEnabled; - bCheats->msg = cheatsEnabled ? "Cheats: On" : "Cheats: Off"; - return; - } - - if (button == bCreate && !tLevelName.text.empty()) { - int seed = getEpochTimeS(); - if (!tSeed.text.empty()) { - std::string seedString = Util::stringTrim(tSeed.text); - int tmpSeed; - if (sscanf(seedString.c_str(), "%d", &tmpSeed) > 0) { - seed = tmpSeed; - } else { - seed = Util::hashCode(seedString); - } - } - std::string levelId = getUniqueLevelName(tLevelName.text); - LevelSettings settings(seed, gamemode, cheatsEnabled); - minecraft->selectLevel(levelId, levelId, settings); - minecraft->hostMultiplayer(); - minecraft->setScreen(new ProgressScreen()); - hasChosen = true; - return; - } - - if (button == bBack) { - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } -} - -void SimpleChooseLevelScreen::keyPressed(int eventKey) -{ - if (eventKey == Keyboard::KEY_ESCAPE) { - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - return; - } - // let base class handle navigation and text box keys - Screen::keyPressed(eventKey); -} - -bool SimpleChooseLevelScreen::handleBackEvent(bool isDown) { - if (!isDown) - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - return true; -} +#include "SimpleChooseLevelScreen.h" +#include "ProgressScreen.h" +#include "ScreenChooser.h" +#include "../components/Button.h" +#include "../components/ImageButton.h" +#include "../../Minecraft.h" +#include "../../../world/level/LevelSettings.h" +#include "../../../platform/time.h" +#include "../../../platform/input/Keyboard.h" +#include "../../../platform/log.h" + +SimpleChooseLevelScreen::SimpleChooseLevelScreen(const std::string& levelName) +: bHeader(0), + bGamemode(0), + bCheats(0), + bBack(0), + bCreate(0), + levelName(levelName), + hasChosen(false), + gamemode(GameType::Survival), + cheatsEnabled(false), + tLevelName(0, "World name"), + tSeed(1, "World seed") +{ +} + +SimpleChooseLevelScreen::~SimpleChooseLevelScreen() +{ + if (bHeader) delete bHeader; + delete bGamemode; + delete bCheats; + delete bBack; + delete bCreate; +} + +void SimpleChooseLevelScreen::init() +{ + // make sure the base class loads the existing level list; the + // derived screen uses ChooseLevelScreen::getUniqueLevelName(), which + // depends on `levels` being populated. omitting this used to result + // in duplicate IDs ("creating the second world would load the + // first") when the name already existed. + ChooseLevelScreen::init(); + + tLevelName.text = "New world"; + + // header + close button + bHeader = new Touch::THeader(0, "Create World"); + // create the back/X button as ImageButton like CreditsScreen + bBack = new ImageButton(2, ""); + { + ImageDef def; + def.name = "gui/touchgui.png"; + def.width = 34; + def.height = 26; + def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); + bBack->setImageDef(def, true); + } + if (/* minecraft->useTouchscreen() */ true) { + bGamemode = new Touch::TButton(1, "Survival mode"); + bCheats = new Touch::TButton(4, "Cheats: Off"); + bCreate = new Touch::TButton(3, "Create"); + } else { + bGamemode = new Button(1, "Survival mode"); + bCheats = new Button(4, "Cheats: Off"); + bCreate = new Button(3, "Create"); + } + + buttons.push_back(bHeader); + buttons.push_back(bBack); + buttons.push_back(bGamemode); + buttons.push_back(bCheats); + buttons.push_back(bCreate); + + tabButtons.push_back(bGamemode); + tabButtons.push_back(bCheats); + tabButtons.push_back(bBack); + tabButtons.push_back(bCreate); + + textBoxes.push_back(&tLevelName); + textBoxes.push_back(&tSeed); +} + +void SimpleChooseLevelScreen::setupPositions() +{ + int buttonHeight = bBack->height; + + // position back button in upper-right + bBack->x = width - bBack->width; + bBack->y = 0; + + // header occupies remaining top bar + if (bHeader) { + bHeader->x = 0; + bHeader->y = 0; + bHeader->width = width - bBack->width; + bHeader->height = buttonHeight; + } + + // layout the form elements below the header + int centerX = width / 2; + const int padding = 5; + + tLevelName.width = tSeed.width = 200; + tLevelName.x = centerX - tLevelName.width / 2; + tLevelName.y = buttonHeight + 20; + + tSeed.x = tLevelName.x; + tSeed.y = tLevelName.y + 30; + + const int buttonWidth = 120; + const int buttonSpacing = 10; + const int totalButtonWidth = buttonWidth * 2 + buttonSpacing; + + bGamemode->width = buttonWidth; + bCheats->width = buttonWidth; + + bGamemode->x = centerX - totalButtonWidth / 2; + bCheats->x = bGamemode->x + buttonWidth + buttonSpacing; + + // compute vertical centre for buttons in remaining space + { + int bottomPad = 20; + int availTop = buttonHeight + 20 + 30 + 10; // just below seed + int availBottom = height - bottomPad - bCreate->height - 10; // leave some gap before create + int availHeight = availBottom - availTop; + if (availHeight < 0) availHeight = 0; + int y = availTop + (availHeight - bGamemode->height) / 2; + bGamemode->y = y; + bCheats->y = y; + } + + bCreate->width = 100; + bCreate->x = centerX - bCreate->width / 2; + int bottomPadding = 20; + bCreate->y = height - bottomPadding - bCreate->height; +} + +void SimpleChooseLevelScreen::tick() +{ + // let any textboxes handle their own blinking/input + for (auto* tb : textBoxes) + tb->tick(minecraft); +} + +void SimpleChooseLevelScreen::render( int xm, int ym, float a ) +{ + renderDirtBackground(0); + glEnable2(GL_BLEND); + + const char* modeDesc = NULL; + if (gamemode == GameType::Survival) { + modeDesc = "Mobs, health and gather resources"; + } else if (gamemode == GameType::Creative) { + modeDesc = "Unlimited resources and flying"; + } + if (modeDesc) { + drawCenteredString(minecraft->font, modeDesc, width / 2, bGamemode->y + bGamemode->height + 4, 0xffcccccc); + } + + drawString(minecraft->font, "World name:", tLevelName.x, tLevelName.y - Font::DefaultLineHeight - 2, 0xffcccccc); + drawString(minecraft->font, "World seed:", tSeed.x, tSeed.y - Font::DefaultLineHeight - 2, 0xffcccccc); + + Screen::render(xm, ym, a); + glDisable2(GL_BLEND); +} + +// mouse clicks should also manage textbox focus explicitly +void SimpleChooseLevelScreen::mouseClicked(int x, int y, int buttonNum) +{ + if (buttonNum == MouseAction::ACTION_LEFT) { + // determine if the click landed on either textbox or its label above + int lvlTop = tLevelName.y - (Font::DefaultLineHeight + 4); + int lvlBottom = tLevelName.y + tLevelName.height; + int lvlLeft = tLevelName.x; + int lvlRight = tLevelName.x + tLevelName.width; + bool clickedLevel = x >= lvlLeft && x < lvlRight && y >= lvlTop && y < lvlBottom; + + int seedTop = tSeed.y - (Font::DefaultLineHeight + 4); + int seedBottom = tSeed.y + tSeed.height; + int seedLeft = tSeed.x; + int seedRight = tSeed.x + tSeed.width; + bool clickedSeed = x >= seedLeft && x < seedRight && y >= seedTop && y < seedBottom; + + if (clickedLevel) { + LOGI("SimpleChooseLevelScreen: level textbox clicked (%d,%d)\n", x, y); + tLevelName.setFocus(minecraft); + tSeed.loseFocus(minecraft); + } else if (clickedSeed) { + LOGI("SimpleChooseLevelScreen: seed textbox clicked (%d,%d)\n", x, y); + tSeed.setFocus(minecraft); + tLevelName.loseFocus(minecraft); + } else { + // click outside both fields -> blur both + tLevelName.loseFocus(minecraft); + tSeed.loseFocus(minecraft); + } + } + + // allow normal button and textbox handling too + Screen::mouseClicked(x, y, buttonNum); +} + +void SimpleChooseLevelScreen::buttonClicked( Button* button ) +{ + if (hasChosen) + return; + + if (button == bGamemode) { + gamemode ^= 1; + bGamemode->msg = (gamemode == GameType::Survival) ? "Survival mode" : "Creative mode"; + return; + } + + if (button == bCheats) { + cheatsEnabled = !cheatsEnabled; + bCheats->msg = cheatsEnabled ? "Cheats: On" : "Cheats: Off"; + return; + } + + if (button == bCreate && !tLevelName.text.empty()) { + int seed = getEpochTimeS(); + if (!tSeed.text.empty()) { + std::string seedString = Util::stringTrim(tSeed.text); + int tmpSeed; + if (sscanf(seedString.c_str(), "%d", &tmpSeed) > 0) { + seed = tmpSeed; + } else { + seed = Util::hashCode(seedString); + } + } + std::string levelId = getUniqueLevelName(tLevelName.text); + LevelSettings settings(seed, gamemode, cheatsEnabled); + minecraft->selectLevel(levelId, levelId, settings); + minecraft->hostMultiplayer(); + minecraft->setScreen(new ProgressScreen()); + hasChosen = true; + return; + } + + if (button == bBack) { + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } +} + +void SimpleChooseLevelScreen::keyPressed(int eventKey) +{ + if (eventKey == Keyboard::KEY_ESCAPE) { + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + return; + } + // let base class handle navigation and text box keys + Screen::keyPressed(eventKey); +} + +bool SimpleChooseLevelScreen::handleBackEvent(bool isDown) { + if (!isDown) + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + return true; +} diff --git a/src/client/gui/screens/SimpleChooseLevelScreen.h b/src/client/gui/screens/SimpleChooseLevelScreen.h index 1c88d06..bd5dda1 100755 --- a/src/client/gui/screens/SimpleChooseLevelScreen.h +++ b/src/client/gui/screens/SimpleChooseLevelScreen.h @@ -1,44 +1,44 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DemoChooseLevelScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__DemoChooseLevelScreen_H__ - -#include "ChooseLevelScreen.h" -#include "../components/TextBox.h" -#include "../components/Button.h" // for Touch::THeader -class Button; -class ImageButton; - -class SimpleChooseLevelScreen: public ChooseLevelScreen -{ -public: - SimpleChooseLevelScreen(const std::string& levelName); - - virtual ~SimpleChooseLevelScreen(); - - void init(); - void setupPositions(); - void tick(); - - void render(int xm, int ym, float a); - - void buttonClicked(Button* button); - bool handleBackEvent(bool isDown); - virtual void keyPressed(int eventKey); - virtual void mouseClicked(int x, int y, int buttonNum); - -private: - Touch::THeader* bHeader; - Button* bGamemode; - Button* bCheats; - ImageButton* bBack; - Button* bCreate; - bool hasChosen; - - std::string levelName; - int gamemode; - bool cheatsEnabled; - - TextBox tLevelName; - TextBox tSeed; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__DemoChooseLevelScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__DemoChooseLevelScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__DemoChooseLevelScreen_H__ + +#include "ChooseLevelScreen.h" +#include "../components/TextBox.h" +#include "../components/Button.h" // for Touch::THeader +class Button; +class ImageButton; + +class SimpleChooseLevelScreen: public ChooseLevelScreen +{ +public: + SimpleChooseLevelScreen(const std::string& levelName); + + virtual ~SimpleChooseLevelScreen(); + + void init(); + void setupPositions(); + void tick(); + + void render(int xm, int ym, float a); + + void buttonClicked(Button* button); + bool handleBackEvent(bool isDown); + virtual void keyPressed(int eventKey); + virtual void mouseClicked(int x, int y, int buttonNum); + +private: + Touch::THeader* bHeader; + Button* bGamemode; + Button* bCheats; + ImageButton* bBack; + Button* bCreate; + bool hasChosen; + + std::string levelName; + int gamemode; + bool cheatsEnabled; + + TextBox tLevelName; + TextBox tSeed; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__DemoChooseLevelScreen_H__*/ diff --git a/src/client/gui/screens/StartMenuScreen.cpp b/src/client/gui/screens/StartMenuScreen.cpp index 3fede7e..b68b68d 100755 --- a/src/client/gui/screens/StartMenuScreen.cpp +++ b/src/client/gui/screens/StartMenuScreen.cpp @@ -1,216 +1,216 @@ -#include "StartMenuScreen.h" -#include "UsernameScreen.h" -#include "SelectWorldScreen.h" -#include "ProgressScreen.h" -#include "JoinGameScreen.h" -#include "OptionsScreen.h" -#include "PauseScreen.h" -#include "PrerenderTilesScreen.h" // test button -#include "../components/ImageButton.h" - -#include "../../../util/Mth.h" - -#include "../Font.h" -#include "../components/ScrolledSelectionList.h" - -#include "../../Minecraft.h" -#include "../../renderer/Tesselator.h" -#include "../../../AppPlatform.h" -#include "../../../LicenseCodes.h" -#include "SimpleChooseLevelScreen.h" -#include "../../renderer/Textures.h" -#include "../../../SharedConstants.h" - -// Some kind of default settings, might be overridden in ::init -StartMenuScreen::StartMenuScreen() -: bHost( 2, 0, 0, 160, 24, "Start Game"), - bJoin( 3, 0, 0, 160, 24, "Join Game"), - bOptions( 4, 0, 0, 160, 24, "Options"), - bQuit( 5, "") -{ -} - -StartMenuScreen::~StartMenuScreen() -{ -} - -void StartMenuScreen::init() -{ - bJoin.active = bHost.active = bOptions.active = true; - - if (minecraft->options.getStringValue(OPTIONS_USERNAME).empty()) { - return; // tick() will redirect to UsernameScreen - } - - buttons.push_back(&bHost); - buttons.push_back(&bJoin); - //buttons.push_back(&bTest); - - tabButtons.push_back(&bHost); - tabButtons.push_back(&bJoin); - - #ifndef RPI - buttons.push_back(&bOptions); - tabButtons.push_back(&bOptions); - #endif - - // add quit button (top right X icon) – match OptionsScreen style - { - ImageDef def; - def.name = "gui/touchgui.png"; - def.width = 34; - def.height = 26; - def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); - bQuit.setImageDef(def, true); - bQuit.scaleWhenPressed = false; - buttons.push_back(&bQuit); - // don't include in tab navigation - } - - copyright = "\xffMojang AB";//. Do not distribute!"; - - // always show base version string, suffix was previously added for Android builds - std::string versionString = Common::getGameVersionString(); - - std::string _username = minecraft->options.getStringValue(OPTIONS_USERNAME); - if (_username.empty()) _username = "unknown"; - - username = "Username: " + _username; - - #ifdef DEMO_MODE - #ifdef __APPLE__ - version = versionString + " (Lite)"; - #else - version = versionString + " (Demo)"; - #endif - #else - #ifdef RPI - version = "v0.1.1 alpha";//(MCPE " + versionString + " compatible)"; - #else - version = versionString; - #endif - #endif -} - -void StartMenuScreen::setupPositions() { - int yBase = height / 2; - - bHost.y = yBase; - bJoin.y = bHost.y + 24 + 4; - bOptions.y = bJoin.y + 24 + 4; - - // Center buttons - bHost.x = (width - bHost.width) / 2; - bJoin.x = (width - bJoin.width) / 2; - bOptions.x = (width - bOptions.width) / 2; - - // position quit icon at top-right (use image-defined size) - bQuit.x = width - bQuit.width; - bQuit.y = 0; -} - -void StartMenuScreen::tick() { -} - -void StartMenuScreen::buttonClicked(Button* button) { - - if (button->id == bHost.id) - { - #if defined(DEMO_MODE) || defined(APPLE_DEMO_PROMOTION) - minecraft->setScreen( new SimpleChooseLevelScreen("_DemoLevel") ); - #else - minecraft->screenChooser.setScreen(SCREEN_SELECTWORLD); - #endif - } - if (button->id == bJoin.id) - { - minecraft->locateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_JOINGAME); - } - if (button->id == bOptions.id) - { - minecraft->setScreen(new OptionsScreen()); - } - if (button == &bQuit) - { - minecraft->quit(); - } -} - -bool StartMenuScreen::isInGameScreen() { return false; } - -void StartMenuScreen::render( int xm, int ym, float a ) -{ - renderBackground(); - - // Show current username in the top-left corner - drawString(font, username, 2, 2, 0xffffffff); - -#if defined(RPI) - TextureId id = minecraft->textures->loadTexture("gui/pi_title.png"); -#else - TextureId id = minecraft->textures->loadTexture("gui/title.png"); -#endif - const TextureData* data = minecraft->textures->getTemporaryTextureData(id); - - if (data) { - minecraft->textures->bind(id); - - const float x = (float)width / 2; - const float y = height/16; - //const float scale = Mth::Min( - const float wh = Mth::Min((float)width/2.0f, (float)data->w / 2); - const float scale = 2.0f * wh / (float)data->w; - const float h = scale * (float)data->h; - - // Render title text - Tesselator& t = Tesselator::instance; - glColor4f2(1, 1, 1, 1); - t.begin(); - t.vertexUV(x-wh, y+h, blitOffset, 0, 1); - t.vertexUV(x+wh, y+h, blitOffset, 1, 1); - t.vertexUV(x+wh, y+0, blitOffset, 1, 0); - t.vertexUV(x-wh, y+0, blitOffset, 0, 0); - t.draw(); - } - -#if defined(RPI) - if (Textures::isTextureIdValid(minecraft->textures->loadAndBindTexture("gui/logo/raknet_high_72.png"))) - blit(0, height - 12, 0, 0, 43, 12, 256, 72+72); -#endif - - drawString(font, version, width - font->width(version) - 2, height - 10, 0xffcccccc);//0x666666); - drawString(font, copyright, 2, height - 20, 0xffffff); - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f2(1, 1, 1, 1); - if (Textures::isTextureIdValid(minecraft->textures->loadAndBindTexture("gui/logo/github.png"))) - blit(2, height - 10, 0, 0, 8, 8, 256, 256); - { - std::string txt = "Kolyah35/minecraft-pe-0.6.1"; - float wtxt = font->width(txt); - Gui::drawColoredString(font, txt, 12, height - 10, 255); - // underline link - float y0 = height - 10 + font->lineHeight - 1; - this->fill(12, (int)y0, 12 + (int)wtxt, (int)(y0 + 1), 0xffffffff); - } - - - Screen::render(xm, ym, a); -} - -void StartMenuScreen::mouseClicked(int x, int y, int buttonNum) { - const int logoX = 2; - const int logoW = 8 + 2 + font->width("Kolyah35/minecraft-pe-0.6.1"); - const int logoY = height - 10; - const int logoH = 10; - if (x >= logoX && x <= logoX + logoW && y >= logoY && y <= logoY + logoH) - minecraft->platform()->openURL("https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1"); - else - Screen::mouseClicked(x, y, buttonNum); -} - -bool StartMenuScreen::handleBackEvent( bool isDown ) { - minecraft->quit(); - return true; -} +#include "StartMenuScreen.h" +#include "UsernameScreen.h" +#include "SelectWorldScreen.h" +#include "ProgressScreen.h" +#include "JoinGameScreen.h" +#include "OptionsScreen.h" +#include "PauseScreen.h" +#include "PrerenderTilesScreen.h" // test button +#include "../components/ImageButton.h" + +#include "../../../util/Mth.h" + +#include "../Font.h" +#include "../components/ScrolledSelectionList.h" + +#include "../../Minecraft.h" +#include "../../renderer/Tesselator.h" +#include "../../../AppPlatform.h" +#include "../../../LicenseCodes.h" +#include "SimpleChooseLevelScreen.h" +#include "../../renderer/Textures.h" +#include "../../../SharedConstants.h" + +// Some kind of default settings, might be overridden in ::init +StartMenuScreen::StartMenuScreen() +: bHost( 2, 0, 0, 160, 24, "Start Game"), + bJoin( 3, 0, 0, 160, 24, "Join Game"), + bOptions( 4, 0, 0, 160, 24, "Options"), + bQuit( 5, "") +{ +} + +StartMenuScreen::~StartMenuScreen() +{ +} + +void StartMenuScreen::init() +{ + bJoin.active = bHost.active = bOptions.active = true; + + if (minecraft->options.getStringValue(OPTIONS_USERNAME).empty()) { + return; // tick() will redirect to UsernameScreen + } + + buttons.push_back(&bHost); + buttons.push_back(&bJoin); + //buttons.push_back(&bTest); + + tabButtons.push_back(&bHost); + tabButtons.push_back(&bJoin); + + #ifndef RPI + buttons.push_back(&bOptions); + tabButtons.push_back(&bOptions); + #endif + + // add quit button (top right X icon) – match OptionsScreen style + { + ImageDef def; + def.name = "gui/touchgui.png"; + def.width = 34; + def.height = 26; + def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); + bQuit.setImageDef(def, true); + bQuit.scaleWhenPressed = false; + buttons.push_back(&bQuit); + // don't include in tab navigation + } + + copyright = "\xffMojang AB";//. Do not distribute!"; + + // always show base version string, suffix was previously added for Android builds + std::string versionString = Common::getGameVersionString(); + + std::string _username = minecraft->options.getStringValue(OPTIONS_USERNAME); + if (_username.empty()) _username = "unknown"; + + username = "Username: " + _username; + + #ifdef DEMO_MODE + #ifdef __APPLE__ + version = versionString + " (Lite)"; + #else + version = versionString + " (Demo)"; + #endif + #else + #ifdef RPI + version = "v0.1.1 alpha";//(MCPE " + versionString + " compatible)"; + #else + version = versionString; + #endif + #endif +} + +void StartMenuScreen::setupPositions() { + int yBase = height / 2; + + bHost.y = yBase; + bJoin.y = bHost.y + 24 + 4; + bOptions.y = bJoin.y + 24 + 4; + + // Center buttons + bHost.x = (width - bHost.width) / 2; + bJoin.x = (width - bJoin.width) / 2; + bOptions.x = (width - bOptions.width) / 2; + + // position quit icon at top-right (use image-defined size) + bQuit.x = width - bQuit.width; + bQuit.y = 0; +} + +void StartMenuScreen::tick() { +} + +void StartMenuScreen::buttonClicked(Button* button) { + + if (button->id == bHost.id) + { + #if defined(DEMO_MODE) || defined(APPLE_DEMO_PROMOTION) + minecraft->setScreen( new SimpleChooseLevelScreen("_DemoLevel") ); + #else + minecraft->screenChooser.setScreen(SCREEN_SELECTWORLD); + #endif + } + if (button->id == bJoin.id) + { + minecraft->locateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_JOINGAME); + } + if (button->id == bOptions.id) + { + minecraft->setScreen(new OptionsScreen()); + } + if (button == &bQuit) + { + minecraft->quit(); + } +} + +bool StartMenuScreen::isInGameScreen() { return false; } + +void StartMenuScreen::render( int xm, int ym, float a ) +{ + renderBackground(); + + // Show current username in the top-left corner + drawString(font, username, 2, 2, 0xffffffff); + +#if defined(RPI) + TextureId id = minecraft->textures->loadTexture("gui/pi_title.png"); +#else + TextureId id = minecraft->textures->loadTexture("gui/title.png"); +#endif + const TextureData* data = minecraft->textures->getTemporaryTextureData(id); + + if (data) { + minecraft->textures->bind(id); + + const float x = (float)width / 2; + const float y = height/16; + //const float scale = Mth::Min( + const float wh = Mth::Min((float)width/2.0f, (float)data->w / 2); + const float scale = 2.0f * wh / (float)data->w; + const float h = scale * (float)data->h; + + // Render title text + Tesselator& t = Tesselator::instance; + glColor4f2(1, 1, 1, 1); + t.begin(); + t.vertexUV(x-wh, y+h, blitOffset, 0, 1); + t.vertexUV(x+wh, y+h, blitOffset, 1, 1); + t.vertexUV(x+wh, y+0, blitOffset, 1, 0); + t.vertexUV(x-wh, y+0, blitOffset, 0, 0); + t.draw(); + } + +#if defined(RPI) + if (Textures::isTextureIdValid(minecraft->textures->loadAndBindTexture("gui/logo/raknet_high_72.png"))) + blit(0, height - 12, 0, 0, 43, 12, 256, 72+72); +#endif + + drawString(font, version, width - font->width(version) - 2, height - 10, 0xffcccccc);//0x666666); + drawString(font, copyright, 2, height - 20, 0xffffff); + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f2(1, 1, 1, 1); + if (Textures::isTextureIdValid(minecraft->textures->loadAndBindTexture("gui/logo/github.png"))) + blit(2, height - 10, 0, 0, 8, 8, 256, 256); + { + std::string txt = "Kolyah35/minecraft-pe-0.6.1"; + float wtxt = font->width(txt); + Gui::drawColoredString(font, txt, 12, height - 10, 255); + // underline link + float y0 = height - 10 + font->lineHeight - 1; + this->fill(12, (int)y0, 12 + (int)wtxt, (int)(y0 + 1), 0xffffffff); + } + + + Screen::render(xm, ym, a); +} + +void StartMenuScreen::mouseClicked(int x, int y, int buttonNum) { + const int logoX = 2; + const int logoW = 8 + 2 + font->width("Kolyah35/minecraft-pe-0.6.1"); + const int logoY = height - 10; + const int logoH = 10; + if (x >= logoX && x <= logoX + logoW && y >= logoY && y <= logoY + logoH) + minecraft->platform()->openURL("https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1"); + else + Screen::mouseClicked(x, y, buttonNum); +} + +bool StartMenuScreen::handleBackEvent( bool isDown ) { + minecraft->quit(); + return true; +} diff --git a/src/client/gui/screens/StartMenuScreen.h b/src/client/gui/screens/StartMenuScreen.h index e2801fe..8ba2e96 100755 --- a/src/client/gui/screens/StartMenuScreen.h +++ b/src/client/gui/screens/StartMenuScreen.h @@ -1,40 +1,40 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__StartMenuScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__StartMenuScreen_H__ - -#include "../Screen.h" -#include "../components/Button.h" -#include "../components/ImageButton.h" - -class StartMenuScreen: public Screen -{ -public: - StartMenuScreen(); - virtual ~StartMenuScreen(); - - void init(); - void setupPositions(); - - void tick(); - void render(int xm, int ym, float a); - - void buttonClicked(Button* button); - virtual void mouseClicked(int x, int y, int buttonNum); - bool handleBackEvent(bool isDown); - bool isInGameScreen(); -private: - - Button bHost; - Button bJoin; - Button bOptions; - ImageButton bQuit; // X button in top-right corner - - std::string copyright; - int copyrightPosX; - - std::string version; - int versionPosX; - - std::string username; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__StartMenuScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__StartMenuScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__StartMenuScreen_H__ + +#include "../Screen.h" +#include "../components/Button.h" +#include "../components/ImageButton.h" + +class StartMenuScreen: public Screen +{ +public: + StartMenuScreen(); + virtual ~StartMenuScreen(); + + void init(); + void setupPositions(); + + void tick(); + void render(int xm, int ym, float a); + + void buttonClicked(Button* button); + virtual void mouseClicked(int x, int y, int buttonNum); + bool handleBackEvent(bool isDown); + bool isInGameScreen(); +private: + + Button bHost; + Button bJoin; + Button bOptions; + ImageButton bQuit; // X button in top-right corner + + std::string copyright; + int copyrightPosX; + + std::string version; + int versionPosX; + + std::string username; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__StartMenuScreen_H__*/ diff --git a/src/client/gui/screens/TextEditScreen.cpp b/src/client/gui/screens/TextEditScreen.cpp index 1159e67..189ad72 100755 --- a/src/client/gui/screens/TextEditScreen.cpp +++ b/src/client/gui/screens/TextEditScreen.cpp @@ -1,146 +1,146 @@ -#include "TextEditScreen.h" -#include "../../../world/level/tile/entity/SignTileEntity.h" -#include "../../../AppPlatform.h" -#include "../../Minecraft.h" -#include "../../renderer/tileentity/TileEntityRenderDispatcher.h" -#include "../../renderer/Tesselator.h" -#include "../../renderer/Textures.h" -#include "../../renderer/GameRenderer.h" -#include "../components/Button.h" -#include "../../../network/Packet.h" -#include "../../../network/RakNetInstance.h" -TextEditScreen::TextEditScreen( SignTileEntity* signEntity ) - : sign(signEntity), isShowingKeyboard(false), frame(0), line(0), btnClose(1, "") { - -} -TextEditScreen::~TextEditScreen() { - -} -void TextEditScreen::init() { - super::init(); - minecraft->platform()->showKeyboard(); - isShowingKeyboard = true; - ImageDef def; - def.name = "gui/spritesheet.png"; - def.x = 0; - def.y = 1; - def.width = def.height = 18; - def.setSrc(IntRectangle(60, 0, 18, 18)); - btnClose.setImageDef(def, true); - btnClose.scaleWhenPressed = false; - buttons.push_back(&btnClose); -} - -void TextEditScreen::setupPositions() { - btnClose.width = btnClose.height = 19; - btnClose.x = width - btnClose.width; - btnClose.y = 0; -} - - -bool TextEditScreen::handleBackEvent( bool isDown ) { - sign->setChanged(); - Packet* signUpdatePacket = sign->getUpdatePacket(); - minecraft->raknetInstance->send(signUpdatePacket); - minecraft->platform()->hideKeyboard(); - minecraft->setScreen(NULL); - return true; -} - -void TextEditScreen::render( int xm, int ym, float a ) { - glDepthMask(GL_FALSE); - renderBackground(); - glPushMatrix(); - glDepthMask(GL_TRUE); - glDisable(GL_CULL_FACE); - glLoadIdentity(); - Tesselator& t = Tesselator::instance; - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrthof(0.0f, (float)minecraft->width, (float)minecraft->height, 0, -1, 1); - glMatrixMode(GL_MODELVIEW); - - minecraft->textures->loadAndBindTexture("item/sign.png"); - glColor4f2(1, 1, 1, 1); - - static float minUV[] = {0.03126f, 0.06249f}; - static float maxUV[] = {0.39063f, 0.4374f}; - float scale = ((minecraft->height / 2) / 32) * 0.9f; - - glTranslatef(minecraft->width / 2.0f, 5.0f, 0.0f); - glScalef2(scale,scale,1); - t.begin(GL_QUADS); - t.vertexUV(-32, 0, 0.0f,minUV[0],minUV[1]); - t.vertexUV(32, 0, 0.0f, maxUV[0], minUV[1]); - t.vertexUV(32, 0 + 32, 0.0f, maxUV[0], maxUV[1]); - t.vertexUV(-32, 0 + 32, 0.0f, minUV[0], maxUV[1]); - t.draw(); - - sign->selectedLine = line; - float textScale = 8.0f / 11.0f; - - glTranslatef(0, 2 ,0); - glScalef2(textScale, textScale, 1); - for(int i = 0; i < 4; ++i) { - //drawCenteredString(font, sign->messages[a], 32.0f, 10 * a, 0xFF000000); - std::string msg = sign->messages[i]; - if (i == sign->selectedLine && msg.length() < 14) { - std::string s = "> " + msg + " <"; - font->draw(s, -(float)font->width(s) / 2.0f, 10.0f * i, 0xFF000000, false); - } else { - font->draw(msg, -(float)font->width(msg) / 2.0f, 10.0f * i, 0xFF000000, false); - } - } - sign->selectedLine = -1; - //font->draw("Hej", minecraft->width / 2, 100, 0xFFFFFFFF, false); - - glPopMatrix(); - glEnable(GL_CULL_FACE); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - - //glEnable(GL_DEPTH_TEST); - super::render(xm, ym, a); -} - -void TextEditScreen::lostFocus() { - -} - -void TextEditScreen::tick() { - frame++; -} - -void TextEditScreen::keyPressed( int eventKey ) { - LOGW("Key pressed! [%d]", eventKey); - if(eventKey == Keyboard::KEY_BACKSPACE) { - if(sign->messages[line].length() > 0) { - sign->messages[line].erase(sign->messages[line].size() - 1, 1); - } else { - line--; - if(line < 0) { - line = 3; - } - } - } else if(eventKey == Keyboard::KEY_RETURN) { - line = (line + 1) % 4; - } else { - super::keyPressed(eventKey); - } -} - -void TextEditScreen::charPressed( char inputChar ) { - std::string fullstring = sign->messages[line] + inputChar; - if(fullstring.length() < 16) { - sign->messages[line] = fullstring; - //LOGW("Line text updated: %s\n", fullstring.c_str()); - } -} - -void TextEditScreen::buttonClicked( Button* button ) { - if(button == &btnClose) - handleBackEvent(true); -} +#include "TextEditScreen.h" +#include "../../../world/level/tile/entity/SignTileEntity.h" +#include "../../../AppPlatform.h" +#include "../../Minecraft.h" +#include "../../renderer/tileentity/TileEntityRenderDispatcher.h" +#include "../../renderer/Tesselator.h" +#include "../../renderer/Textures.h" +#include "../../renderer/GameRenderer.h" +#include "../components/Button.h" +#include "../../../network/Packet.h" +#include "../../../network/RakNetInstance.h" +TextEditScreen::TextEditScreen( SignTileEntity* signEntity ) + : sign(signEntity), isShowingKeyboard(false), frame(0), line(0), btnClose(1, "") { + +} +TextEditScreen::~TextEditScreen() { + +} +void TextEditScreen::init() { + super::init(); + minecraft->platform()->showKeyboard(); + isShowingKeyboard = true; + ImageDef def; + def.name = "gui/spritesheet.png"; + def.x = 0; + def.y = 1; + def.width = def.height = 18; + def.setSrc(IntRectangle(60, 0, 18, 18)); + btnClose.setImageDef(def, true); + btnClose.scaleWhenPressed = false; + buttons.push_back(&btnClose); +} + +void TextEditScreen::setupPositions() { + btnClose.width = btnClose.height = 19; + btnClose.x = width - btnClose.width; + btnClose.y = 0; +} + + +bool TextEditScreen::handleBackEvent( bool isDown ) { + sign->setChanged(); + Packet* signUpdatePacket = sign->getUpdatePacket(); + minecraft->raknetInstance->send(signUpdatePacket); + minecraft->platform()->hideKeyboard(); + minecraft->setScreen(NULL); + return true; +} + +void TextEditScreen::render( int xm, int ym, float a ) { + glDepthMask(GL_FALSE); + renderBackground(); + glPushMatrix(); + glDepthMask(GL_TRUE); + glDisable(GL_CULL_FACE); + glLoadIdentity(); + Tesselator& t = Tesselator::instance; + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrthof(0.0f, (float)minecraft->width, (float)minecraft->height, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + + minecraft->textures->loadAndBindTexture("item/sign.png"); + glColor4f2(1, 1, 1, 1); + + static float minUV[] = {0.03126f, 0.06249f}; + static float maxUV[] = {0.39063f, 0.4374f}; + float scale = ((minecraft->height / 2) / 32) * 0.9f; + + glTranslatef(minecraft->width / 2.0f, 5.0f, 0.0f); + glScalef2(scale,scale,1); + t.begin(GL_QUADS); + t.vertexUV(-32, 0, 0.0f,minUV[0],minUV[1]); + t.vertexUV(32, 0, 0.0f, maxUV[0], minUV[1]); + t.vertexUV(32, 0 + 32, 0.0f, maxUV[0], maxUV[1]); + t.vertexUV(-32, 0 + 32, 0.0f, minUV[0], maxUV[1]); + t.draw(); + + sign->selectedLine = line; + float textScale = 8.0f / 11.0f; + + glTranslatef(0, 2 ,0); + glScalef2(textScale, textScale, 1); + for(int i = 0; i < 4; ++i) { + //drawCenteredString(font, sign->messages[a], 32.0f, 10 * a, 0xFF000000); + std::string msg = sign->messages[i]; + if (i == sign->selectedLine && msg.length() < 14) { + std::string s = "> " + msg + " <"; + font->draw(s, -(float)font->width(s) / 2.0f, 10.0f * i, 0xFF000000, false); + } else { + font->draw(msg, -(float)font->width(msg) / 2.0f, 10.0f * i, 0xFF000000, false); + } + } + sign->selectedLine = -1; + //font->draw("Hej", minecraft->width / 2, 100, 0xFFFFFFFF, false); + + glPopMatrix(); + glEnable(GL_CULL_FACE); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + //glEnable(GL_DEPTH_TEST); + super::render(xm, ym, a); +} + +void TextEditScreen::lostFocus() { + +} + +void TextEditScreen::tick() { + frame++; +} + +void TextEditScreen::keyPressed( int eventKey ) { + LOGW("Key pressed! [%d]", eventKey); + if(eventKey == Keyboard::KEY_BACKSPACE) { + if(sign->messages[line].length() > 0) { + sign->messages[line].erase(sign->messages[line].size() - 1, 1); + } else { + line--; + if(line < 0) { + line = 3; + } + } + } else if(eventKey == Keyboard::KEY_RETURN) { + line = (line + 1) % 4; + } else { + super::keyPressed(eventKey); + } +} + +void TextEditScreen::charPressed( char inputChar ) { + std::string fullstring = sign->messages[line] + inputChar; + if(fullstring.length() < 16) { + sign->messages[line] = fullstring; + //LOGW("Line text updated: %s\n", fullstring.c_str()); + } +} + +void TextEditScreen::buttonClicked( Button* button ) { + if(button == &btnClose) + handleBackEvent(true); +} diff --git a/src/client/gui/screens/TextEditScreen.h b/src/client/gui/screens/TextEditScreen.h index 2fea9fc..e5351a3 100755 --- a/src/client/gui/screens/TextEditScreen.h +++ b/src/client/gui/screens/TextEditScreen.h @@ -1,35 +1,35 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__TextEditScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__TextEditScreen_H__ - -//package net.minecraft.client.gui; - -#include "../Screen.h" -#include -#include "../components/ImageButton.h" -class SignTileEntity; -class Button; -class TextEditScreen: public Screen -{ - typedef Screen super; -public: - TextEditScreen(SignTileEntity* signEntity); - ~TextEditScreen(); - void init(); - void tick(); - bool handleBackEvent(bool isDown); - void render(int xm, int ym, float a); - virtual void lostFocus(); - virtual void keyPressed(int eventKey); - virtual void charPressed(char inputChar); - void setupPositions(); - void buttonClicked(Button* button); -protected: - bool isShowingKeyboard; - SignTileEntity* sign; - int frame; - int line; -private: - ImageButton btnClose; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__TextEditScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__TextEditScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__TextEditScreen_H__ + +//package net.minecraft.client.gui; + +#include "../Screen.h" +#include +#include "../components/ImageButton.h" +class SignTileEntity; +class Button; +class TextEditScreen: public Screen +{ + typedef Screen super; +public: + TextEditScreen(SignTileEntity* signEntity); + ~TextEditScreen(); + void init(); + void tick(); + bool handleBackEvent(bool isDown); + void render(int xm, int ym, float a); + virtual void lostFocus(); + virtual void keyPressed(int eventKey); + virtual void charPressed(char inputChar); + void setupPositions(); + void buttonClicked(Button* button); +protected: + bool isShowingKeyboard; + SignTileEntity* sign; + int frame; + int line; +private: + ImageButton btnClose; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__TextEditScreen_H__*/ diff --git a/src/client/gui/screens/UploadPhotoScreen.cpp b/src/client/gui/screens/UploadPhotoScreen.cpp index ccf38f8..4e9ab53 100755 --- a/src/client/gui/screens/UploadPhotoScreen.cpp +++ b/src/client/gui/screens/UploadPhotoScreen.cpp @@ -1,177 +1,177 @@ -#if 0 - -#include "UploadPhotoScreen.h" -#include "../renderer/TileRenderer.h" -#include "../player/LocalPlayer.h" -#include "../../world/entity/player/Inventory.h" - -UploadPhotoScreen::UploadPhotoScreen() - : -selectedItem(0) -{ -} - -void UploadPhotoScreen::init() -{ - int currentSelection = minecraft->player->inventory->getSelectedItemId(); - for (int i = 0; i < Inventory::INVENTORY_SIZE; i++) - { - if (currentSelection == minecraft->player->inventory->getSelectionSlotItemId(i + Inventory::SELECTION_SIZE)) - { - selectedItem = i; - break; - } - } - -} - -void UploadPhotoScreen::renderSlots() -{ - glColor4f2(1, 1, 1, 1); - - blitOffset = -90; - - minecraft->textures->loadAndBindTexture("gui/gui.png"); - for (int r = 0; r < Inventory::INVENTORY_ROWS; r++) - { - blit(width / 2 - 182 / 2, height - 22 * 3 - 22 * r, 0, 0, 182, 22); - } - if (selectedItem >= 0) - { - int x = width / 2 - 182 / 2 - 1 + (selectedItem % Inventory::SELECTION_SIZE) * 20; - int y = height - 22 * 3 - 1 - (selectedItem / Inventory::SELECTION_SIZE) * 22; - blit(x, y, 0, 22, 24, 22); - } - - for (int r = 0; r < Inventory::INVENTORY_ROWS; r++) - { - for (int i = 0; i < 9; i++) { - int x = width / 2 - 9 * 10 + i * 20 + 2; - int y = height - 16 - 3 - 22 * 2 - 22 * r; - renderSlot(r * 9 + i + Inventory::SELECTION_SIZE, x, y, 0); - } - } - -} - -void UploadPhotoScreen::renderSlot(int slot, int x, int y, float a) -{ - int itemId = minecraft->player->inventory->getSelectionSlotItemId(slot); - if (itemId < 0) return; - - const bool fancy = false; - - if (fancy && itemId < 256 && TileRenderer::canRender(Tile::tiles[itemId]->getRenderShape())) { - - } else { - if (itemId < 256) { - Tile* tile = Tile::tiles[itemId]; - if (tile == NULL) return; - - minecraft->textures->loadAndBindTexture("terrain.png"); - - int textureId = tile->getTexture(2, 0); - blit(x, y, textureId % 16 * 16, textureId / 16 * 16, 16, 16); - } - } - -} - -void UploadPhotoScreen::keyPressed(int eventKey) -{ - int selX = selectedItem % Inventory::SELECTION_SIZE; - int selY = selectedItem / Inventory::SELECTION_SIZE; - - Options& o = minecraft->options; - if (eventKey == o.keyLeft.key && selX > 0) - { - selectedItem -= 1; - } - else if (eventKey == o.keyRight.key && selX < (Inventory::SELECTION_SIZE - 1)) - { - selectedItem += 1; - } - else if (eventKey == o.keyDown.key && selY > 0) - { - selectedItem -= Inventory::SELECTION_SIZE; - } - else if (eventKey == o.keyUp.key && selY < (Inventory::INVENTORY_ROWS - 1)) - { - selectedItem += Inventory::SELECTION_SIZE; - } - - if (eventKey == o.keyMenuOk.key) - { - selectSlotAndClose(); - } -} - -int UploadPhotoScreen::getSelectedSlot(int x, int y) -{ - int left = 3 + width / 2 - Inventory::SELECTION_SIZE * 10; - int top = height - 16 - 3 - 22 * 2 - 22 * Inventory::INVENTORY_ROWS; - - if (x >= left && y >= top) - { - int xSlot = (x - left) / 20; - if (xSlot < Inventory::SELECTION_SIZE) - { - // rows are rendered upsidedown - return xSlot + Inventory::INVENTORY_SIZE - ((y - top) / 22) * Inventory::SELECTION_SIZE; - } - } - return -1; -} - -void UploadPhotoScreen::mouseClicked(int x, int y, int buttonNum) { - if (buttonNum == MouseAction::ACTION_LEFT) { - - int slot = getSelectedSlot(x, y); - if (slot >= 0 && slot < Inventory::INVENTORY_SIZE) - { - selectedItem = slot; - //minecraft->soundEngine->playUI("random.click", 1, 1); - } - } -} - -void UploadPhotoScreen::mouseReleased(int x, int y, int buttonNum) -{ - if (buttonNum == MouseAction::ACTION_LEFT) { - - int slot = getSelectedSlot(x, y); - if (slot >= 0 && slot < Inventory::INVENTORY_SIZE && slot == selectedItem) - { - selectSlotAndClose(); - } - } -} - -void UploadPhotoScreen::selectSlotAndClose() -{ - Inventory* inventory = minecraft->player->inventory; - - int itemId = inventory->getSelectionSlotItemId(selectedItem + Inventory::SELECTION_SIZE); - int i = 0; - - for (; i < Inventory::SELECTION_SIZE - 2; i++) - { - if (itemId == inventory->getSelectionSlotItemId(i)) - { - break; - } - } - - // update selection list - for (; i >= 1; i--) - { - inventory->setSelectionSlotItemId(i, inventory->getSelectionSlotItemId(i - 1)); - } - inventory->setSelectionSlotItemId(0, itemId); - inventory->selectSlot(0); - - minecraft->soundEngine->playUI("random.click", 1, 1); - minecraft->setScreen(NULL); -} - -#endif +#if 0 + +#include "UploadPhotoScreen.h" +#include "../renderer/TileRenderer.h" +#include "../player/LocalPlayer.h" +#include "../../world/entity/player/Inventory.h" + +UploadPhotoScreen::UploadPhotoScreen() + : +selectedItem(0) +{ +} + +void UploadPhotoScreen::init() +{ + int currentSelection = minecraft->player->inventory->getSelectedItemId(); + for (int i = 0; i < Inventory::INVENTORY_SIZE; i++) + { + if (currentSelection == minecraft->player->inventory->getSelectionSlotItemId(i + Inventory::SELECTION_SIZE)) + { + selectedItem = i; + break; + } + } + +} + +void UploadPhotoScreen::renderSlots() +{ + glColor4f2(1, 1, 1, 1); + + blitOffset = -90; + + minecraft->textures->loadAndBindTexture("gui/gui.png"); + for (int r = 0; r < Inventory::INVENTORY_ROWS; r++) + { + blit(width / 2 - 182 / 2, height - 22 * 3 - 22 * r, 0, 0, 182, 22); + } + if (selectedItem >= 0) + { + int x = width / 2 - 182 / 2 - 1 + (selectedItem % Inventory::SELECTION_SIZE) * 20; + int y = height - 22 * 3 - 1 - (selectedItem / Inventory::SELECTION_SIZE) * 22; + blit(x, y, 0, 22, 24, 22); + } + + for (int r = 0; r < Inventory::INVENTORY_ROWS; r++) + { + for (int i = 0; i < 9; i++) { + int x = width / 2 - 9 * 10 + i * 20 + 2; + int y = height - 16 - 3 - 22 * 2 - 22 * r; + renderSlot(r * 9 + i + Inventory::SELECTION_SIZE, x, y, 0); + } + } + +} + +void UploadPhotoScreen::renderSlot(int slot, int x, int y, float a) +{ + int itemId = minecraft->player->inventory->getSelectionSlotItemId(slot); + if (itemId < 0) return; + + const bool fancy = false; + + if (fancy && itemId < 256 && TileRenderer::canRender(Tile::tiles[itemId]->getRenderShape())) { + + } else { + if (itemId < 256) { + Tile* tile = Tile::tiles[itemId]; + if (tile == NULL) return; + + minecraft->textures->loadAndBindTexture("terrain.png"); + + int textureId = tile->getTexture(2, 0); + blit(x, y, textureId % 16 * 16, textureId / 16 * 16, 16, 16); + } + } + +} + +void UploadPhotoScreen::keyPressed(int eventKey) +{ + int selX = selectedItem % Inventory::SELECTION_SIZE; + int selY = selectedItem / Inventory::SELECTION_SIZE; + + Options& o = minecraft->options; + if (eventKey == o.keyLeft.key && selX > 0) + { + selectedItem -= 1; + } + else if (eventKey == o.keyRight.key && selX < (Inventory::SELECTION_SIZE - 1)) + { + selectedItem += 1; + } + else if (eventKey == o.keyDown.key && selY > 0) + { + selectedItem -= Inventory::SELECTION_SIZE; + } + else if (eventKey == o.keyUp.key && selY < (Inventory::INVENTORY_ROWS - 1)) + { + selectedItem += Inventory::SELECTION_SIZE; + } + + if (eventKey == o.keyMenuOk.key) + { + selectSlotAndClose(); + } +} + +int UploadPhotoScreen::getSelectedSlot(int x, int y) +{ + int left = 3 + width / 2 - Inventory::SELECTION_SIZE * 10; + int top = height - 16 - 3 - 22 * 2 - 22 * Inventory::INVENTORY_ROWS; + + if (x >= left && y >= top) + { + int xSlot = (x - left) / 20; + if (xSlot < Inventory::SELECTION_SIZE) + { + // rows are rendered upsidedown + return xSlot + Inventory::INVENTORY_SIZE - ((y - top) / 22) * Inventory::SELECTION_SIZE; + } + } + return -1; +} + +void UploadPhotoScreen::mouseClicked(int x, int y, int buttonNum) { + if (buttonNum == MouseAction::ACTION_LEFT) { + + int slot = getSelectedSlot(x, y); + if (slot >= 0 && slot < Inventory::INVENTORY_SIZE) + { + selectedItem = slot; + //minecraft->soundEngine->playUI("random.click", 1, 1); + } + } +} + +void UploadPhotoScreen::mouseReleased(int x, int y, int buttonNum) +{ + if (buttonNum == MouseAction::ACTION_LEFT) { + + int slot = getSelectedSlot(x, y); + if (slot >= 0 && slot < Inventory::INVENTORY_SIZE && slot == selectedItem) + { + selectSlotAndClose(); + } + } +} + +void UploadPhotoScreen::selectSlotAndClose() +{ + Inventory* inventory = minecraft->player->inventory; + + int itemId = inventory->getSelectionSlotItemId(selectedItem + Inventory::SELECTION_SIZE); + int i = 0; + + for (; i < Inventory::SELECTION_SIZE - 2; i++) + { + if (itemId == inventory->getSelectionSlotItemId(i)) + { + break; + } + } + + // update selection list + for (; i >= 1; i--) + { + inventory->setSelectionSlotItemId(i, inventory->getSelectionSlotItemId(i - 1)); + } + inventory->setSelectionSlotItemId(0, itemId); + inventory->selectSlot(0); + + minecraft->soundEngine->playUI("random.click", 1, 1); + minecraft->setScreen(NULL); +} + +#endif diff --git a/src/client/gui/screens/UploadPhotoScreen.h b/src/client/gui/screens/UploadPhotoScreen.h index 773f5bb..97d7e6c 100755 --- a/src/client/gui/screens/UploadPhotoScreen.h +++ b/src/client/gui/screens/UploadPhotoScreen.h @@ -1,36 +1,36 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__UploadPhotoScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS__UploadPhotoScreen_H__ - -#include "Screen.h" - -class UploadPhotoScreen : public Screen -{ -public: - - UploadPhotoScreen(); - virtual ~UploadPhotoScreen() {} - - virtual void init(); - - void render(int xm, int ym, float a) { - Screen::render(xm, ym, a); - } - -protected: - - virtual void mouseClicked(int x, int y, int buttonNum); - virtual void mouseReleased(int x, int y, int buttonNum); - virtual void keyPressed(int eventKey); - -private: - - int selectedItem; - - void renderSlots(); - void renderSlot(int slot, int x, int y, float a); - - int getSelectedSlot(int x, int y); - void selectSlotAndClose(); -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__UploadPhotoScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__UploadPhotoScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS__UploadPhotoScreen_H__ + +#include "Screen.h" + +class UploadPhotoScreen : public Screen +{ +public: + + UploadPhotoScreen(); + virtual ~UploadPhotoScreen() {} + + virtual void init(); + + void render(int xm, int ym, float a) { + Screen::render(xm, ym, a); + } + +protected: + + virtual void mouseClicked(int x, int y, int buttonNum); + virtual void mouseReleased(int x, int y, int buttonNum); + virtual void keyPressed(int eventKey); + +private: + + int selectedItem; + + void renderSlots(); + void renderSlot(int slot, int x, int y, float a); + + int getSelectedSlot(int x, int y); + void selectSlotAndClose(); +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__UploadPhotoScreen_H__*/ diff --git a/src/client/gui/screens/crafting/CraftingFilters.cpp b/src/client/gui/screens/crafting/CraftingFilters.cpp index 5820b13..8190a21 100755 --- a/src/client/gui/screens/crafting/CraftingFilters.cpp +++ b/src/client/gui/screens/crafting/CraftingFilters.cpp @@ -1,32 +1,32 @@ -#include "CraftingFilters.h" -#include "../../../../world/item/ItemInstance.h" -#include "../../../../world/item/Item.h" -#include "../../../../world/level/tile/Tile.h" -#include "../../../../world/level/material/Material.h" -#include "../../../../world/level/tile/StoneSlabTile.h" - -namespace CraftingFilters { - -bool isStonecutterItem(const ItemInstance& ins) { - Item* const item = ins.getItem(); - if (item->id < 0 || item->id >= 256) - return false; - - Tile* const tile = Tile::tiles[item->id]; - if (!tile) - return false; - - // Special stone/sand cases - if ( tile == Tile::lapisBlock - || tile == Tile::furnace - || tile == Tile::stonecutterBench) - return false; - - if (tile == Tile::stoneSlabHalf && ins.getAuxValue() == StoneSlabTile::WOOD_SLAB) - return false; - - // Return everything stone or sand - return (tile->material == Material::stone || tile->material == Material::sand); -} - +#include "CraftingFilters.h" +#include "../../../../world/item/ItemInstance.h" +#include "../../../../world/item/Item.h" +#include "../../../../world/level/tile/Tile.h" +#include "../../../../world/level/material/Material.h" +#include "../../../../world/level/tile/StoneSlabTile.h" + +namespace CraftingFilters { + +bool isStonecutterItem(const ItemInstance& ins) { + Item* const item = ins.getItem(); + if (item->id < 0 || item->id >= 256) + return false; + + Tile* const tile = Tile::tiles[item->id]; + if (!tile) + return false; + + // Special stone/sand cases + if ( tile == Tile::lapisBlock + || tile == Tile::furnace + || tile == Tile::stonecutterBench) + return false; + + if (tile == Tile::stoneSlabHalf && ins.getAuxValue() == StoneSlabTile::WOOD_SLAB) + return false; + + // Return everything stone or sand + return (tile->material == Material::stone || tile->material == Material::sand); +} + } \ No newline at end of file diff --git a/src/client/gui/screens/crafting/CraftingFilters.h b/src/client/gui/screens/crafting/CraftingFilters.h index 8ad3bc3..e3ea78c 100755 --- a/src/client/gui/screens/crafting/CraftingFilters.h +++ b/src/client/gui/screens/crafting/CraftingFilters.h @@ -1,10 +1,10 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_CraftingFilters_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_CraftingFilters_H__ - -class ItemInstance; - -namespace CraftingFilters { - bool isStonecutterItem(const ItemInstance& item); -} - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_CraftingFilters_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_CraftingFilters_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_CraftingFilters_H__ + +class ItemInstance; + +namespace CraftingFilters { + bool isStonecutterItem(const ItemInstance& item); +} + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_CraftingFilters_H__*/ diff --git a/src/client/gui/screens/crafting/PaneCraftingScreen.cpp b/src/client/gui/screens/crafting/PaneCraftingScreen.cpp index c0ec6a9..42953c2 100755 --- a/src/client/gui/screens/crafting/PaneCraftingScreen.cpp +++ b/src/client/gui/screens/crafting/PaneCraftingScreen.cpp @@ -1,556 +1,556 @@ -#include "PaneCraftingScreen.h" -#include "../touch/TouchStartMenuScreen.h" -#include "../../Screen.h" -#include "../../components/NinePatch.h" -#include "../../../Minecraft.h" -#include "../../../player/LocalPlayer.h" -#include "../../../renderer/Tesselator.h" -#include "../../../renderer/entity/ItemRenderer.h" -#include "../../../../world/item/Item.h" -#include "../../../../world/item/crafting/Recipes.h" -#include "../../../../world/item/ItemCategory.h" -#include "../../../../world/entity/player/Inventory.h" -#include "../../../../util/StringUtils.h" -#include "../../../../locale/I18n.h" -#include "../../../../world/entity/item/ItemEntity.h" -#include "../../../../world/level/Level.h" -#include "../../../../world/item/DyePowderItem.h" -#include "../../../../world/item/crafting/Recipe.h" -#include "Keyboard.h" - -static NinePatchLayer* guiPaneFrame = NULL; - -const float BorderPixels = 6.0f; -const int descFrameWidth = 100; - -const int rgbActive = 0xfff0f0f0; -const int rgbInactive = 0xc0635558; -const int rgbInactiveShadow = 0xc0aaaaaa; - -class CategoryButton: public ImageButton { - typedef ImageButton super; -public: - CategoryButton(int id, const ImageButton* const* selectedPtr, NinePatchLayer* stateNormal, NinePatchLayer* statePressed) - : super(id, ""), - selectedPtr(selectedPtr), - stateNormal(stateNormal), - statePressed(statePressed) - {} - - void renderBg(Minecraft* minecraft, int xm, int ym) { - //fill(x+1, y+1, x+w-1, y+h-1, 0xff999999); - - bool hovered = active && (minecraft->useTouchscreen()? - (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); - - if (hovered || *selectedPtr == this) - statePressed->draw(Tesselator::instance, (float)x, (float)y); - else - stateNormal->draw(Tesselator::instance, (float)x, (float)y); - } - bool isSecondImage(bool hovered) { return false; } - -private: - const ImageButton* const* selectedPtr; - NinePatchLayer* stateNormal; - NinePatchLayer* statePressed; -}; - -PaneCraftingScreen::PaneCraftingScreen(int craftingSize) -: craftingSize(craftingSize), - currentCategory(-1), - currentItem(NULL), - pane(NULL), - btnCraft(1), - btnClose(2, ""), - selectedCategoryButton(NULL), - guiBackground(NULL), - guiSlotCategory(NULL), - guiSlotCategorySelected(NULL), - numCategories(4) -{ - for (int i = 0; i < numCategories; ++i) { - categoryBitmasks.push_back(1 << i); - categoryIcons.push_back(i); - } -} - -PaneCraftingScreen::~PaneCraftingScreen() { - for (unsigned int i = 0; i < _items.size(); ++i) - delete _items[i]; - - for (unsigned int i = 0; i < _categoryButtons.size(); ++i) - delete _categoryButtons[i]; - - clearCategoryItems(); - - delete pane; - delete guiBackground; - - // statics - delete guiSlotCategory; - delete guiSlotCategorySelected; - delete guiPaneFrame; -} - -void PaneCraftingScreen::init() { - ImageDef def; - def.name = "gui/spritesheet.png"; - def.x = 0; - def.y = 1; - def.width = def.height = 18; - def.setSrc(IntRectangle(60, 0, 18, 18)); - btnClose.setImageDef(def, true); - btnClose.scaleWhenPressed = false; - - btnCraft.init(minecraft->textures); - - buttons.push_back(&btnCraft); - buttons.push_back(&btnClose); - - // GUI patches - NinePatchFactory builder(minecraft->textures, "gui/spritesheet.png"); - - guiBackground = builder.createSymmetrical(IntRectangle(0, 0, 16, 16), 4, 4); - guiPaneFrame = builder.createSymmetrical(IntRectangle(0, 20, 8, 8), 1, 2)->setExcluded(1 << 4); - guiSlotCategory = builder.createSymmetrical(IntRectangle(8, 32, 8, 8), 2, 2); - guiSlotCategorySelected = builder.createSymmetrical(IntRectangle(0, 32, 8, 8), 2, 2); - - initCategories(); -} - -void PaneCraftingScreen::initCategories() { - _categories.resize(numCategories); - - // Category buttons - for (int i = 0; i < numCategories; ++i) { - ImageButton* button = new CategoryButton(100 + i, &selectedCategoryButton, guiSlotCategory, guiSlotCategorySelected); - _categoryButtons.push_back( button ); - buttons.push_back( button ); - } - - const RecipeList& all = Recipes::getInstance()->getRecipes(); - RecipeList filtered; - filtered.reserve(all.size()); - - // Apply size filter - for (unsigned int i = 0; i < all.size(); ++i) { - if (craftingSize >= all[i]->getCraftingSize()) - filtered.push_back(all[i]); - } - // Filter by subclass impl - filterRecipes(filtered); - - // Add items from filtered recipes - for (unsigned int i = 0; i < filtered.size(); ++i) - addItem(filtered[i]); - - recheckRecipes(); -} - -void PaneCraftingScreen::setupPositions() { - // Left - Categories - const int buttonHeight = (height - 16) / (Mth::Max(numCategories, 4)); - for (unsigned c = 0; c < _categoryButtons.size(); ++c) { - ImageButton* button = _categoryButtons[c]; - button->x = (int)BorderPixels; - button->y = (int)BorderPixels + c * (1 + buttonHeight); - button->width = (int)buttonHeight; - button->height = (int)buttonHeight; - - int icon = categoryIcons[c]; - ImageDef def; - def.x = 0; - def.width = def.height = (float)buttonHeight; - def.name = "gui/spritesheet.png"; - def.setSrc(IntRectangle(32 * (icon/2), 64 + (icon&1) * 32, 32, 32)); - button->setImageDef(def, false); - } - // Right - Description - const int craftW = (int)(100 - 2 * BorderPixels - 0); - btnCraft.x = width - descFrameWidth + (descFrameWidth-craftW)/2 - 1;// width - descFrameWidth + (int)BorderPixels + 4; - btnCraft.y = 20; - btnCraft.setSize((float)craftW, 62); - - btnClose.width = btnClose.height = 19; - btnClose.x = width - btnClose.width; - btnClose.y = 0; - - // Middle - Scrolling pane - paneRect.x = buttonHeight + 2 * (int)BorderPixels; - paneRect.y = (int)BorderPixels + 2; - paneRect.w = width - paneRect.x - descFrameWidth; - paneRect.h = height - 2 * (int)BorderPixels - 4; - - guiPaneFrame->setSize((float)paneRect.w + 2, (float)paneRect.h + 4); - guiBackground->setSize((float)width, (float)height); - guiSlotCategory->setSize((float)buttonHeight, (float)buttonHeight); - guiSlotCategorySelected->setSize((float)buttonHeight, (float)buttonHeight); - - int oldCategory = currentCategory; - currentCategory = -1; - buttonClicked(_categoryButtons[pane?oldCategory:0]); -} - -void PaneCraftingScreen::tick() { - if (pane) pane->tick(); -} - -void PaneCraftingScreen::render(int xm, int ym, float a) { - const int N = 5; - static StopwatchNLast r(N); - //renderBackground(); - Tesselator& t = Tesselator::instance; - guiBackground->draw(t, 0, 0); - glEnable2(GL_ALPHA_TEST); - - // Buttons (Left side + crafting) - super::render(xm, ym, a); - - // Mid - r.start(); - // Blit frame - guiPaneFrame->draw(t, (float)paneRect.x - 1, (float)paneRect.y - 2); - if (pane) pane->render(xm, ym, a); - r.stop(); - //r.printEvery(N, "test"); - - const float slotWidth = (float)btnCraft.width / 2.0f; - const float slotHeight = (float)btnCraft.height / 2.0f; - const float slotBx = (float)btnCraft.x + slotWidth/2 - 8; - const float slotBy = (float)btnCraft.y + slotHeight/2 - 9; - - ItemInstance reqItem; - // Right side - if (currentItem) { - t.beginOverride(); - for (unsigned int i = 0; i < currentItem->neededItems.size(); ++i) { - const float xx = slotBx + slotWidth * (float)(i % 2); - const float yy = slotBy + slotHeight * (float)(i / 2); - CItem::ReqItem& req = currentItem->neededItems[i]; - reqItem = req.item; - if (reqItem.getAuxValue() == -1) reqItem.setAuxValue(0); - ItemRenderer::renderGuiItem(NULL, minecraft->textures, &reqItem, xx, yy, 16, 16, true); - } - t.endOverrideAndDraw(); - - char buf[16]; - const float scale = 2.0f / 3.0f; - const float invScale = 1.0f / scale; - t.beginOverride(); - t.scale2d(scale, scale); - for (unsigned int i = 0; i < currentItem->neededItems.size(); ++i) { - const float xx = 4 + invScale * (slotBx + slotWidth * (float)(i % 2)); - const float yy = 23 + invScale * (slotBy + slotHeight * (float)(i / 2)); - CItem::ReqItem& req = currentItem->neededItems[i]; - - int bufIndex = 0; - bufIndex += Gui::itemCountItoa(&buf[bufIndex], req.has); - strcpy(&buf[bufIndex], "/"); bufIndex += 1; - bufIndex += Gui::itemCountItoa(&buf[bufIndex], req.item.count); - - buf[bufIndex] = 0; - if (req.enough()) - minecraft->font->drawShadow(buf, xx, yy, rgbActive); - else { - minecraft->font->draw(buf, xx+1, yy+1, rgbInactiveShadow); - minecraft->font->draw(buf, xx, yy, rgbInactive); - } - } - t.resetScale(); - t.endOverrideAndDraw(); - - //minecraft->font->drawWordWrap(currentItemDesc, rightBx + 2, (float)btnCraft.y + btnCraft.h + 6, descFrameWidth-4, rgbActive); - minecraft->font->drawWordWrap(currentItemDesc, (float)btnCraft.x, (float)(btnCraft.y + btnCraft.height + 6), (float)btnCraft.width, rgbActive); - } - //glDisable2(GL_ALPHA_TEST); -} - -void PaneCraftingScreen::buttonClicked(Button* button) { - if (button == &btnCraft) - craftSelectedItem(); - - if (button == &btnClose) - minecraft->setScreen(NULL); - - // Did we click a category? - if (button->id >= 100 && button->id < 200) { - int categoryId = button->id - 100; - ItemList& cat = _categories[categoryId]; - if (!cat.empty()) { - onItemSelected(categoryId, cat[0]); - pane->setSelected(0, true); - } - currentCategory = categoryId; - selectedCategoryButton = (CategoryButton*)button; - } -} - -static void randomlyFillItemPack(ItemPack* ip, int numItems) { - int added = 0; - ItemInstance item(0, 1, 0); - while (added < numItems) { - int t = Mth::random(512); - if (!Item::items[t]) continue; - - item.id = t; - int id = ItemPack::getIdForItemInstance(&item); - int count = Mth::random(10); - for (int i = 0; i < count; ++i) - ip->add(id); - ++added; - } -} - -static bool sortCanCraftPredicate(const CItem* a, const CItem* b) { - //if (a->maxBuildCount == 0 && b->maxBuildCount > 0) return false; - //if (b->maxBuildCount == 0 && a->maxBuildCount > 0) return true; - return a->sortText < b->sortText; -} - -void PaneCraftingScreen::recheckRecipes() { - ItemPack ip; - - if (minecraft->player && minecraft->player->inventory) { - Inventory* inv = (minecraft->player)->inventory; - - for (int i = Inventory::MAX_SELECTION_SIZE; i < inv->getContainerSize(); ++i) { - if (ItemInstance* item = inv->getItem(i)) - ip.add(ItemPack::getIdForItemInstance(item), item->count); - } - } else { - randomlyFillItemPack(&ip, 50); - } - - ip.print(); - - Stopwatch w; - w.start(); - - for (unsigned int i = 0; i < _items.size(); ++i) { - CItem* item = _items[i]; - item->neededItems.clear(); - item->setCanCraft(true); - - Recipe* recipe = item->recipe; - item->inventoryCount = ip.getCount(ItemPack::getIdForItemInstance(&item->item)); - //item->maxBuildCount = recipe->getMaxCraftCount(ip); - // Override the canCraft thing, since I'm too lazy - // to fix the above (commented out) function - std::vector items = recipe->getItemPack().getItemInstances(); - for (unsigned int j = 0; j < items.size(); ++j) { - ItemInstance& jtem = items[j]; - int has = 0; - if (!Recipe::isAnyAuxValue(&jtem) && (jtem.getAuxValue() == Recipe::ANY_AUX_VALUE)) { - // If the aux value on the item matters, but the recipe says it doesn't, - // use this override (by fetching all items with aux-ids 0-15) - ItemInstance aux(jtem); - for (int i = 0; i < 16; ++i) { - aux.setAuxValue(i); - has += ip.getCount(ItemPack::getIdForItemInstance(&aux)); - } - } else { - // Else just use the normal aux-value rules - has = ip.getCount(ItemPack::getIdForItemInstance(&jtem)); - } - CItem::ReqItem req(jtem, has); - item->neededItems.push_back(req); - item->setCanCraft(item->canCraft() && req.enough()); - } - } - w.stop(); - w.printEvery(1, "> craft "); - - for (unsigned int c = 0; c < _categories.size(); ++c) - std::stable_sort(_categories[c].begin(), _categories[c].end(), sortCanCraftPredicate); -} - -void PaneCraftingScreen::addItem( Recipe* recipe ) -{ - ItemInstance instance = recipe->getResultItem(); - Item* item = instance.getItem(); - CItem* ci = new CItem(instance, recipe, instance.getName());//item->getDescriptionId()); - if (item->id == Tile::cloth->id) - ci->sortText = "Wool " + ci->text; - if (item->id == Item::dye_powder->id) - ci->sortText = "ZDye " + ci->text; - _items.push_back(ci); - - if (item->category < 0) - return; - - for (int i = 0; i < (int)categoryBitmasks.size(); ++i) { - int bitmask = categoryBitmasks[i]; - if ((bitmask & item->category) != 0) - _categories[i].push_back( ci ); - } -} - -void PaneCraftingScreen::onItemSelected(const ItemPane* forPane, int itemIndexInCurrentCategory) { - if (currentCategory >= (int)_categories.size()) return; - if (itemIndexInCurrentCategory >= (int)_categories[currentCategory].size()) return; - onItemSelected(currentCategory, _categories[currentCategory][itemIndexInCurrentCategory]); -} - -void PaneCraftingScreen::onItemSelected(int buttonIndex, CItem* item) { - currentItem = item; - currentItemDesc = I18n::getDescriptionString(currentItem->item); - - if (buttonIndex != currentCategory) { - // Clear item buttons for this category - clearCategoryItems(); - - // Setup new buttons for the items in this category - const int NumCategoryItems = _categories[buttonIndex].size(); - - if (pane) delete pane; - pane = new ItemPane(this, minecraft->textures, paneRect, NumCategoryItems, height, minecraft->height); - pane->f = minecraft->font; - - currentCategory = buttonIndex; - } -} - -void PaneCraftingScreen::clearCategoryItems() -{ - for (unsigned int i = 0; i < currentCategoryButtons.size(); ++i) { - delete currentCategoryButtons[i]; - } - currentCategoryButtons.clear(); -} - -void PaneCraftingScreen::keyPressed( int eventKey ) -{ - if (eventKey == Keyboard::KEY_ESCAPE || eventKey == Keyboard::KEY_E) { - minecraft->setScreen(NULL); - //minecraft->grabMouse(); - } else { - super::keyPressed(eventKey); - } -} - -void PaneCraftingScreen::craftSelectedItem() -{ - if (!currentItem) - return; - if (!currentItem->canCraft()) - return; - - ItemInstance resultItem = currentItem->item; - - if (minecraft->player) { - // Remove all items required for the recipe and ... - for (unsigned int i = 0; i < currentItem->neededItems.size(); ++i) { - CItem::ReqItem& req = currentItem->neededItems[i]; - - // If the recipe allows any aux-value as ingredients, first deplete - // aux == 0 from inventory. Since I'm not sure if this always is - // correct, let's only do it for ingredient sandstone for now. - ItemInstance toRemove = req.item; - - if (Tile::sandStone->id == req.item.id - && Recipe::ANY_AUX_VALUE == req.item.getAuxValue()) { - toRemove.setAuxValue(0); - toRemove.count = minecraft->player->inventory->removeResource(toRemove, true); - toRemove.setAuxValue(Recipe::ANY_AUX_VALUE); - } - - if (toRemove.count > 0) { - minecraft->player->inventory->removeResource(toRemove); - } - } - // ... add the new one! (in this order, to fill empty slots better) - // if it doesn't fit, throw it on the ground! - if (!minecraft->player->inventory->add(&resultItem)) { - minecraft->player->drop(new ItemInstance(resultItem), false); - } - - recheckRecipes(); - } -} - -bool PaneCraftingScreen::renderGameBehind() -{ - return false; -} - -bool PaneCraftingScreen::closeOnPlayerHurt() { - return true; -} - -void PaneCraftingScreen::filterRecipes(RecipeList& recipes) { - for (int i = recipes.size() - 1; i >= 0; --i) { - if (!filterRecipe(*recipes[i])) - recipes.erase(recipes.begin() + i); - } -} - -const std::vector& PaneCraftingScreen::getItems(const ItemPane* forPane) -{ - return _categories[currentCategory]; -} - -void PaneCraftingScreen::setSingleCategoryAndIcon(int categoryBitmask, int categoryIcon) { - assert(!minecraft && "setSingleCategoryAndIcon needs to be called from subclass constructor!\n"); - - numCategories = 1; - - categoryIcons.clear(); - categoryIcons.push_back(categoryIcon); - - categoryBitmasks.clear(); - categoryBitmasks.push_back(categoryBitmask); -} - -// -// Craft button -// -CraftButton::CraftButton( int id) -: super(id, ""), - bg(NULL), - bgSelected(NULL), - numItems(0) -{ -} - -CraftButton::~CraftButton() -{ - delete bg; - delete bgSelected; -} - -void CraftButton::setSize(float w, float h ) { - this->width = (int)w; - this->height = (int)h; - - if (bg && bgSelected) { - bg->setSize(w, h); - bgSelected->setSize(w, h); - } -} - -void CraftButton::init( Textures* textures) -{ - NinePatchFactory builder(textures, "gui/spritesheet.png"); - bg = builder.createSymmetrical(IntRectangle(112, 0, 8, 67), 2, 2); - bgSelected = builder.createSymmetrical(IntRectangle(120, 0, 8, 67), 2, 2); -} - -IntRectangle CraftButton::getItemPos( int i ) -{ - return IntRectangle(); -} - -void CraftButton::renderBg(Minecraft* minecraft, int xm, int ym) { - if (!bg || !bgSelected) - return; - //fill(x+1, y+1, x+w-1, y+h-1, 0xff999999); - - bool hovered = active && (minecraft->useTouchscreen()? - (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); - - if (hovered || selected) - bgSelected->draw(Tesselator::instance, (float)x, (float)y); - else - bg->draw(Tesselator::instance, (float)x, (float)y); -} +#include "PaneCraftingScreen.h" +#include "../touch/TouchStartMenuScreen.h" +#include "../../Screen.h" +#include "../../components/NinePatch.h" +#include "../../../Minecraft.h" +#include "../../../player/LocalPlayer.h" +#include "../../../renderer/Tesselator.h" +#include "../../../renderer/entity/ItemRenderer.h" +#include "../../../../world/item/Item.h" +#include "../../../../world/item/crafting/Recipes.h" +#include "../../../../world/item/ItemCategory.h" +#include "../../../../world/entity/player/Inventory.h" +#include "../../../../util/StringUtils.h" +#include "../../../../locale/I18n.h" +#include "../../../../world/entity/item/ItemEntity.h" +#include "../../../../world/level/Level.h" +#include "../../../../world/item/DyePowderItem.h" +#include "../../../../world/item/crafting/Recipe.h" +#include "Keyboard.h" + +static NinePatchLayer* guiPaneFrame = NULL; + +const float BorderPixels = 6.0f; +const int descFrameWidth = 100; + +const int rgbActive = 0xfff0f0f0; +const int rgbInactive = 0xc0635558; +const int rgbInactiveShadow = 0xc0aaaaaa; + +class CategoryButton: public ImageButton { + typedef ImageButton super; +public: + CategoryButton(int id, const ImageButton* const* selectedPtr, NinePatchLayer* stateNormal, NinePatchLayer* statePressed) + : super(id, ""), + selectedPtr(selectedPtr), + stateNormal(stateNormal), + statePressed(statePressed) + {} + + void renderBg(Minecraft* minecraft, int xm, int ym) { + //fill(x+1, y+1, x+w-1, y+h-1, 0xff999999); + + bool hovered = active && (minecraft->useTouchscreen()? + (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); + + if (hovered || *selectedPtr == this) + statePressed->draw(Tesselator::instance, (float)x, (float)y); + else + stateNormal->draw(Tesselator::instance, (float)x, (float)y); + } + bool isSecondImage(bool hovered) { return false; } + +private: + const ImageButton* const* selectedPtr; + NinePatchLayer* stateNormal; + NinePatchLayer* statePressed; +}; + +PaneCraftingScreen::PaneCraftingScreen(int craftingSize) +: craftingSize(craftingSize), + currentCategory(-1), + currentItem(NULL), + pane(NULL), + btnCraft(1), + btnClose(2, ""), + selectedCategoryButton(NULL), + guiBackground(NULL), + guiSlotCategory(NULL), + guiSlotCategorySelected(NULL), + numCategories(4) +{ + for (int i = 0; i < numCategories; ++i) { + categoryBitmasks.push_back(1 << i); + categoryIcons.push_back(i); + } +} + +PaneCraftingScreen::~PaneCraftingScreen() { + for (unsigned int i = 0; i < _items.size(); ++i) + delete _items[i]; + + for (unsigned int i = 0; i < _categoryButtons.size(); ++i) + delete _categoryButtons[i]; + + clearCategoryItems(); + + delete pane; + delete guiBackground; + + // statics + delete guiSlotCategory; + delete guiSlotCategorySelected; + delete guiPaneFrame; +} + +void PaneCraftingScreen::init() { + ImageDef def; + def.name = "gui/spritesheet.png"; + def.x = 0; + def.y = 1; + def.width = def.height = 18; + def.setSrc(IntRectangle(60, 0, 18, 18)); + btnClose.setImageDef(def, true); + btnClose.scaleWhenPressed = false; + + btnCraft.init(minecraft->textures); + + buttons.push_back(&btnCraft); + buttons.push_back(&btnClose); + + // GUI patches + NinePatchFactory builder(minecraft->textures, "gui/spritesheet.png"); + + guiBackground = builder.createSymmetrical(IntRectangle(0, 0, 16, 16), 4, 4); + guiPaneFrame = builder.createSymmetrical(IntRectangle(0, 20, 8, 8), 1, 2)->setExcluded(1 << 4); + guiSlotCategory = builder.createSymmetrical(IntRectangle(8, 32, 8, 8), 2, 2); + guiSlotCategorySelected = builder.createSymmetrical(IntRectangle(0, 32, 8, 8), 2, 2); + + initCategories(); +} + +void PaneCraftingScreen::initCategories() { + _categories.resize(numCategories); + + // Category buttons + for (int i = 0; i < numCategories; ++i) { + ImageButton* button = new CategoryButton(100 + i, &selectedCategoryButton, guiSlotCategory, guiSlotCategorySelected); + _categoryButtons.push_back( button ); + buttons.push_back( button ); + } + + const RecipeList& all = Recipes::getInstance()->getRecipes(); + RecipeList filtered; + filtered.reserve(all.size()); + + // Apply size filter + for (unsigned int i = 0; i < all.size(); ++i) { + if (craftingSize >= all[i]->getCraftingSize()) + filtered.push_back(all[i]); + } + // Filter by subclass impl + filterRecipes(filtered); + + // Add items from filtered recipes + for (unsigned int i = 0; i < filtered.size(); ++i) + addItem(filtered[i]); + + recheckRecipes(); +} + +void PaneCraftingScreen::setupPositions() { + // Left - Categories + const int buttonHeight = (height - 16) / (Mth::Max(numCategories, 4)); + for (unsigned c = 0; c < _categoryButtons.size(); ++c) { + ImageButton* button = _categoryButtons[c]; + button->x = (int)BorderPixels; + button->y = (int)BorderPixels + c * (1 + buttonHeight); + button->width = (int)buttonHeight; + button->height = (int)buttonHeight; + + int icon = categoryIcons[c]; + ImageDef def; + def.x = 0; + def.width = def.height = (float)buttonHeight; + def.name = "gui/spritesheet.png"; + def.setSrc(IntRectangle(32 * (icon/2), 64 + (icon&1) * 32, 32, 32)); + button->setImageDef(def, false); + } + // Right - Description + const int craftW = (int)(100 - 2 * BorderPixels - 0); + btnCraft.x = width - descFrameWidth + (descFrameWidth-craftW)/2 - 1;// width - descFrameWidth + (int)BorderPixels + 4; + btnCraft.y = 20; + btnCraft.setSize((float)craftW, 62); + + btnClose.width = btnClose.height = 19; + btnClose.x = width - btnClose.width; + btnClose.y = 0; + + // Middle - Scrolling pane + paneRect.x = buttonHeight + 2 * (int)BorderPixels; + paneRect.y = (int)BorderPixels + 2; + paneRect.w = width - paneRect.x - descFrameWidth; + paneRect.h = height - 2 * (int)BorderPixels - 4; + + guiPaneFrame->setSize((float)paneRect.w + 2, (float)paneRect.h + 4); + guiBackground->setSize((float)width, (float)height); + guiSlotCategory->setSize((float)buttonHeight, (float)buttonHeight); + guiSlotCategorySelected->setSize((float)buttonHeight, (float)buttonHeight); + + int oldCategory = currentCategory; + currentCategory = -1; + buttonClicked(_categoryButtons[pane?oldCategory:0]); +} + +void PaneCraftingScreen::tick() { + if (pane) pane->tick(); +} + +void PaneCraftingScreen::render(int xm, int ym, float a) { + const int N = 5; + static StopwatchNLast r(N); + //renderBackground(); + Tesselator& t = Tesselator::instance; + guiBackground->draw(t, 0, 0); + glEnable2(GL_ALPHA_TEST); + + // Buttons (Left side + crafting) + super::render(xm, ym, a); + + // Mid + r.start(); + // Blit frame + guiPaneFrame->draw(t, (float)paneRect.x - 1, (float)paneRect.y - 2); + if (pane) pane->render(xm, ym, a); + r.stop(); + //r.printEvery(N, "test"); + + const float slotWidth = (float)btnCraft.width / 2.0f; + const float slotHeight = (float)btnCraft.height / 2.0f; + const float slotBx = (float)btnCraft.x + slotWidth/2 - 8; + const float slotBy = (float)btnCraft.y + slotHeight/2 - 9; + + ItemInstance reqItem; + // Right side + if (currentItem) { + t.beginOverride(); + for (unsigned int i = 0; i < currentItem->neededItems.size(); ++i) { + const float xx = slotBx + slotWidth * (float)(i % 2); + const float yy = slotBy + slotHeight * (float)(i / 2); + CItem::ReqItem& req = currentItem->neededItems[i]; + reqItem = req.item; + if (reqItem.getAuxValue() == -1) reqItem.setAuxValue(0); + ItemRenderer::renderGuiItem(NULL, minecraft->textures, &reqItem, xx, yy, 16, 16, true); + } + t.endOverrideAndDraw(); + + char buf[16]; + const float scale = 2.0f / 3.0f; + const float invScale = 1.0f / scale; + t.beginOverride(); + t.scale2d(scale, scale); + for (unsigned int i = 0; i < currentItem->neededItems.size(); ++i) { + const float xx = 4 + invScale * (slotBx + slotWidth * (float)(i % 2)); + const float yy = 23 + invScale * (slotBy + slotHeight * (float)(i / 2)); + CItem::ReqItem& req = currentItem->neededItems[i]; + + int bufIndex = 0; + bufIndex += Gui::itemCountItoa(&buf[bufIndex], req.has); + strcpy(&buf[bufIndex], "/"); bufIndex += 1; + bufIndex += Gui::itemCountItoa(&buf[bufIndex], req.item.count); + + buf[bufIndex] = 0; + if (req.enough()) + minecraft->font->drawShadow(buf, xx, yy, rgbActive); + else { + minecraft->font->draw(buf, xx+1, yy+1, rgbInactiveShadow); + minecraft->font->draw(buf, xx, yy, rgbInactive); + } + } + t.resetScale(); + t.endOverrideAndDraw(); + + //minecraft->font->drawWordWrap(currentItemDesc, rightBx + 2, (float)btnCraft.y + btnCraft.h + 6, descFrameWidth-4, rgbActive); + minecraft->font->drawWordWrap(currentItemDesc, (float)btnCraft.x, (float)(btnCraft.y + btnCraft.height + 6), (float)btnCraft.width, rgbActive); + } + //glDisable2(GL_ALPHA_TEST); +} + +void PaneCraftingScreen::buttonClicked(Button* button) { + if (button == &btnCraft) + craftSelectedItem(); + + if (button == &btnClose) + minecraft->setScreen(NULL); + + // Did we click a category? + if (button->id >= 100 && button->id < 200) { + int categoryId = button->id - 100; + ItemList& cat = _categories[categoryId]; + if (!cat.empty()) { + onItemSelected(categoryId, cat[0]); + pane->setSelected(0, true); + } + currentCategory = categoryId; + selectedCategoryButton = (CategoryButton*)button; + } +} + +static void randomlyFillItemPack(ItemPack* ip, int numItems) { + int added = 0; + ItemInstance item(0, 1, 0); + while (added < numItems) { + int t = Mth::random(512); + if (!Item::items[t]) continue; + + item.id = t; + int id = ItemPack::getIdForItemInstance(&item); + int count = Mth::random(10); + for (int i = 0; i < count; ++i) + ip->add(id); + ++added; + } +} + +static bool sortCanCraftPredicate(const CItem* a, const CItem* b) { + //if (a->maxBuildCount == 0 && b->maxBuildCount > 0) return false; + //if (b->maxBuildCount == 0 && a->maxBuildCount > 0) return true; + return a->sortText < b->sortText; +} + +void PaneCraftingScreen::recheckRecipes() { + ItemPack ip; + + if (minecraft->player && minecraft->player->inventory) { + Inventory* inv = (minecraft->player)->inventory; + + for (int i = Inventory::MAX_SELECTION_SIZE; i < inv->getContainerSize(); ++i) { + if (ItemInstance* item = inv->getItem(i)) + ip.add(ItemPack::getIdForItemInstance(item), item->count); + } + } else { + randomlyFillItemPack(&ip, 50); + } + + ip.print(); + + Stopwatch w; + w.start(); + + for (unsigned int i = 0; i < _items.size(); ++i) { + CItem* item = _items[i]; + item->neededItems.clear(); + item->setCanCraft(true); + + Recipe* recipe = item->recipe; + item->inventoryCount = ip.getCount(ItemPack::getIdForItemInstance(&item->item)); + //item->maxBuildCount = recipe->getMaxCraftCount(ip); + // Override the canCraft thing, since I'm too lazy + // to fix the above (commented out) function + std::vector items = recipe->getItemPack().getItemInstances(); + for (unsigned int j = 0; j < items.size(); ++j) { + ItemInstance& jtem = items[j]; + int has = 0; + if (!Recipe::isAnyAuxValue(&jtem) && (jtem.getAuxValue() == Recipe::ANY_AUX_VALUE)) { + // If the aux value on the item matters, but the recipe says it doesn't, + // use this override (by fetching all items with aux-ids 0-15) + ItemInstance aux(jtem); + for (int i = 0; i < 16; ++i) { + aux.setAuxValue(i); + has += ip.getCount(ItemPack::getIdForItemInstance(&aux)); + } + } else { + // Else just use the normal aux-value rules + has = ip.getCount(ItemPack::getIdForItemInstance(&jtem)); + } + CItem::ReqItem req(jtem, has); + item->neededItems.push_back(req); + item->setCanCraft(item->canCraft() && req.enough()); + } + } + w.stop(); + w.printEvery(1, "> craft "); + + for (unsigned int c = 0; c < _categories.size(); ++c) + std::stable_sort(_categories[c].begin(), _categories[c].end(), sortCanCraftPredicate); +} + +void PaneCraftingScreen::addItem( Recipe* recipe ) +{ + ItemInstance instance = recipe->getResultItem(); + Item* item = instance.getItem(); + CItem* ci = new CItem(instance, recipe, instance.getName());//item->getDescriptionId()); + if (item->id == Tile::cloth->id) + ci->sortText = "Wool " + ci->text; + if (item->id == Item::dye_powder->id) + ci->sortText = "ZDye " + ci->text; + _items.push_back(ci); + + if (item->category < 0) + return; + + for (int i = 0; i < (int)categoryBitmasks.size(); ++i) { + int bitmask = categoryBitmasks[i]; + if ((bitmask & item->category) != 0) + _categories[i].push_back( ci ); + } +} + +void PaneCraftingScreen::onItemSelected(const ItemPane* forPane, int itemIndexInCurrentCategory) { + if (currentCategory >= (int)_categories.size()) return; + if (itemIndexInCurrentCategory >= (int)_categories[currentCategory].size()) return; + onItemSelected(currentCategory, _categories[currentCategory][itemIndexInCurrentCategory]); +} + +void PaneCraftingScreen::onItemSelected(int buttonIndex, CItem* item) { + currentItem = item; + currentItemDesc = I18n::getDescriptionString(currentItem->item); + + if (buttonIndex != currentCategory) { + // Clear item buttons for this category + clearCategoryItems(); + + // Setup new buttons for the items in this category + const int NumCategoryItems = _categories[buttonIndex].size(); + + if (pane) delete pane; + pane = new ItemPane(this, minecraft->textures, paneRect, NumCategoryItems, height, minecraft->height); + pane->f = minecraft->font; + + currentCategory = buttonIndex; + } +} + +void PaneCraftingScreen::clearCategoryItems() +{ + for (unsigned int i = 0; i < currentCategoryButtons.size(); ++i) { + delete currentCategoryButtons[i]; + } + currentCategoryButtons.clear(); +} + +void PaneCraftingScreen::keyPressed( int eventKey ) +{ + if (eventKey == Keyboard::KEY_ESCAPE || eventKey == Keyboard::KEY_E) { + minecraft->setScreen(NULL); + //minecraft->grabMouse(); + } else { + super::keyPressed(eventKey); + } +} + +void PaneCraftingScreen::craftSelectedItem() +{ + if (!currentItem) + return; + if (!currentItem->canCraft()) + return; + + ItemInstance resultItem = currentItem->item; + + if (minecraft->player) { + // Remove all items required for the recipe and ... + for (unsigned int i = 0; i < currentItem->neededItems.size(); ++i) { + CItem::ReqItem& req = currentItem->neededItems[i]; + + // If the recipe allows any aux-value as ingredients, first deplete + // aux == 0 from inventory. Since I'm not sure if this always is + // correct, let's only do it for ingredient sandstone for now. + ItemInstance toRemove = req.item; + + if (Tile::sandStone->id == req.item.id + && Recipe::ANY_AUX_VALUE == req.item.getAuxValue()) { + toRemove.setAuxValue(0); + toRemove.count = minecraft->player->inventory->removeResource(toRemove, true); + toRemove.setAuxValue(Recipe::ANY_AUX_VALUE); + } + + if (toRemove.count > 0) { + minecraft->player->inventory->removeResource(toRemove); + } + } + // ... add the new one! (in this order, to fill empty slots better) + // if it doesn't fit, throw it on the ground! + if (!minecraft->player->inventory->add(&resultItem)) { + minecraft->player->drop(new ItemInstance(resultItem), false); + } + + recheckRecipes(); + } +} + +bool PaneCraftingScreen::renderGameBehind() +{ + return false; +} + +bool PaneCraftingScreen::closeOnPlayerHurt() { + return true; +} + +void PaneCraftingScreen::filterRecipes(RecipeList& recipes) { + for (int i = recipes.size() - 1; i >= 0; --i) { + if (!filterRecipe(*recipes[i])) + recipes.erase(recipes.begin() + i); + } +} + +const std::vector& PaneCraftingScreen::getItems(const ItemPane* forPane) +{ + return _categories[currentCategory]; +} + +void PaneCraftingScreen::setSingleCategoryAndIcon(int categoryBitmask, int categoryIcon) { + assert(!minecraft && "setSingleCategoryAndIcon needs to be called from subclass constructor!\n"); + + numCategories = 1; + + categoryIcons.clear(); + categoryIcons.push_back(categoryIcon); + + categoryBitmasks.clear(); + categoryBitmasks.push_back(categoryBitmask); +} + +// +// Craft button +// +CraftButton::CraftButton( int id) +: super(id, ""), + bg(NULL), + bgSelected(NULL), + numItems(0) +{ +} + +CraftButton::~CraftButton() +{ + delete bg; + delete bgSelected; +} + +void CraftButton::setSize(float w, float h ) { + this->width = (int)w; + this->height = (int)h; + + if (bg && bgSelected) { + bg->setSize(w, h); + bgSelected->setSize(w, h); + } +} + +void CraftButton::init( Textures* textures) +{ + NinePatchFactory builder(textures, "gui/spritesheet.png"); + bg = builder.createSymmetrical(IntRectangle(112, 0, 8, 67), 2, 2); + bgSelected = builder.createSymmetrical(IntRectangle(120, 0, 8, 67), 2, 2); +} + +IntRectangle CraftButton::getItemPos( int i ) +{ + return IntRectangle(); +} + +void CraftButton::renderBg(Minecraft* minecraft, int xm, int ym) { + if (!bg || !bgSelected) + return; + //fill(x+1, y+1, x+w-1, y+h-1, 0xff999999); + + bool hovered = active && (minecraft->useTouchscreen()? + (_currentlyDown && xm >= x && ym >= y && xm < x + width && ym < y + height) : isInside(xm, ym)); + + if (hovered || selected) + bgSelected->draw(Tesselator::instance, (float)x, (float)y); + else + bg->draw(Tesselator::instance, (float)x, (float)y); +} diff --git a/src/client/gui/screens/crafting/PaneCraftingScreen.h b/src/client/gui/screens/crafting/PaneCraftingScreen.h index d20303b..429aec3 100755 --- a/src/client/gui/screens/crafting/PaneCraftingScreen.h +++ b/src/client/gui/screens/crafting/PaneCraftingScreen.h @@ -1,106 +1,106 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_PaneCraftingScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_PaneCraftingScreen_H__ - -#include "../../Screen.h" -#include "../../../../world/item/crafting/Recipes.h" -#include "../../../../world/item/ItemInstance.h" - -#include "../../components/ScrollingPane.h" -#include "../../components/ImageButton.h" -#include "../../components/ItemPane.h" - -class Font; -class CItem; -class Textures; -class NinePatchLayer; - -class CraftButton: public ImageButton -{ - typedef ImageButton super; -public: - CraftButton(int id); - ~CraftButton(); - - void init(Textures*); - void setSize(float w, float h); - - void setNumItems(int i) { numItems = i; } - IntRectangle getItemPos(int i); - void renderBg(Minecraft* minecraft, int xm, int ym); -private: - NinePatchLayer* bg; - NinePatchLayer* bgSelected; - int numItems; -}; - -class PaneCraftingScreen: public Screen, - public IItemPaneCallback -{ - typedef Screen super; - typedef std::vector ItemList; -public: - PaneCraftingScreen(int craftingSize); - ~PaneCraftingScreen(); - - void init(); - void setupPositions(); - - void tick(); - void render(int xm, int ym, float a); - bool renderGameBehind(); - bool closeOnPlayerHurt(); - void buttonClicked(Button* button); - void keyPressed( int eventKey ); - - // IItemPaneCallback - void onItemSelected(const ItemPane* forPane, int itemIndexInCurrentCategory); - const std::vector& getItems(const ItemPane* forPane); -protected: - void setSingleCategoryAndIcon(int categoryBitmask, int categoryIcon); -private: - /// Filter out non craftable recipes. - /// The default implementation calls bool filterRecipe(r) for every - /// Recipe r and keeps the ones that returned true. - /// A crafting size filter has already been applied. - virtual void filterRecipes(RecipeList& recipes); - virtual bool filterRecipe(const Recipe& recipe) = 0; - - void initCategories(); - - void addItem(Recipe* recipe); - void recheckRecipes(); - void onItemSelected(int buttonIndex, CItem* item); - void clearCategoryItems(); - - void craftSelectedItem(); - std::vector _categoryButtons; - - ItemList _items; - std::vector _categories; - - int currentCategory; - CItem* currentItem; - std::string currentItemDesc; - std::vector currentCategoryButtons; - ImageButton btnClose; - CraftButton btnCraft; - - int craftingSize; - - ItemPane* pane; - IntRectangle paneRect; - //int paneX; - //int paneW; - - int numCategories; - std::vector categoryBitmasks; - std::vector categoryIcons; - ImageButton* selectedCategoryButton; - - // GUI elements such as 9-Patches - NinePatchLayer* guiBackground; - NinePatchLayer* guiSlotCategory; - NinePatchLayer* guiSlotCategorySelected; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_PaneCraftingScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_PaneCraftingScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_PaneCraftingScreen_H__ + +#include "../../Screen.h" +#include "../../../../world/item/crafting/Recipes.h" +#include "../../../../world/item/ItemInstance.h" + +#include "../../components/ScrollingPane.h" +#include "../../components/ImageButton.h" +#include "../../components/ItemPane.h" + +class Font; +class CItem; +class Textures; +class NinePatchLayer; + +class CraftButton: public ImageButton +{ + typedef ImageButton super; +public: + CraftButton(int id); + ~CraftButton(); + + void init(Textures*); + void setSize(float w, float h); + + void setNumItems(int i) { numItems = i; } + IntRectangle getItemPos(int i); + void renderBg(Minecraft* minecraft, int xm, int ym); +private: + NinePatchLayer* bg; + NinePatchLayer* bgSelected; + int numItems; +}; + +class PaneCraftingScreen: public Screen, + public IItemPaneCallback +{ + typedef Screen super; + typedef std::vector ItemList; +public: + PaneCraftingScreen(int craftingSize); + ~PaneCraftingScreen(); + + void init(); + void setupPositions(); + + void tick(); + void render(int xm, int ym, float a); + bool renderGameBehind(); + bool closeOnPlayerHurt(); + void buttonClicked(Button* button); + void keyPressed( int eventKey ); + + // IItemPaneCallback + void onItemSelected(const ItemPane* forPane, int itemIndexInCurrentCategory); + const std::vector& getItems(const ItemPane* forPane); +protected: + void setSingleCategoryAndIcon(int categoryBitmask, int categoryIcon); +private: + /// Filter out non craftable recipes. + /// The default implementation calls bool filterRecipe(r) for every + /// Recipe r and keeps the ones that returned true. + /// A crafting size filter has already been applied. + virtual void filterRecipes(RecipeList& recipes); + virtual bool filterRecipe(const Recipe& recipe) = 0; + + void initCategories(); + + void addItem(Recipe* recipe); + void recheckRecipes(); + void onItemSelected(int buttonIndex, CItem* item); + void clearCategoryItems(); + + void craftSelectedItem(); + std::vector _categoryButtons; + + ItemList _items; + std::vector _categories; + + int currentCategory; + CItem* currentItem; + std::string currentItemDesc; + std::vector currentCategoryButtons; + ImageButton btnClose; + CraftButton btnCraft; + + int craftingSize; + + ItemPane* pane; + IntRectangle paneRect; + //int paneX; + //int paneW; + + int numCategories; + std::vector categoryBitmasks; + std::vector categoryIcons; + ImageButton* selectedCategoryButton; + + // GUI elements such as 9-Patches + NinePatchLayer* guiBackground; + NinePatchLayer* guiSlotCategory; + NinePatchLayer* guiSlotCategorySelected; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_PaneCraftingScreen_H__*/ diff --git a/src/client/gui/screens/crafting/StonecutterScreen.cpp b/src/client/gui/screens/crafting/StonecutterScreen.cpp index 08a0fdd..4ec6451 100755 --- a/src/client/gui/screens/crafting/StonecutterScreen.cpp +++ b/src/client/gui/screens/crafting/StonecutterScreen.cpp @@ -1,17 +1,17 @@ -#include "StonecutterScreen.h" -#include "CraftingFilters.h" -#include "../../../../world/level/material/Material.h" -#include "../../../../world/item/ItemCategory.h" - -StonecutterScreen::StonecutterScreen() -: super(Recipe::SIZE_3X3) -{ - setSingleCategoryAndIcon(ItemCategory::Structures, 5); -} - -StonecutterScreen::~StonecutterScreen() { -} - -bool StonecutterScreen::filterRecipe(const Recipe& r) { - return CraftingFilters::isStonecutterItem(r.getResultItem()); -} +#include "StonecutterScreen.h" +#include "CraftingFilters.h" +#include "../../../../world/level/material/Material.h" +#include "../../../../world/item/ItemCategory.h" + +StonecutterScreen::StonecutterScreen() +: super(Recipe::SIZE_3X3) +{ + setSingleCategoryAndIcon(ItemCategory::Structures, 5); +} + +StonecutterScreen::~StonecutterScreen() { +} + +bool StonecutterScreen::filterRecipe(const Recipe& r) { + return CraftingFilters::isStonecutterItem(r.getResultItem()); +} diff --git a/src/client/gui/screens/crafting/StonecutterScreen.h b/src/client/gui/screens/crafting/StonecutterScreen.h index 4baf0df..f4e6c13 100755 --- a/src/client/gui/screens/crafting/StonecutterScreen.h +++ b/src/client/gui/screens/crafting/StonecutterScreen.h @@ -1,17 +1,17 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_StonecutterScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_StonecutterScreen_H__ - -#include "PaneCraftingScreen.h" - -class StonecutterScreen: public PaneCraftingScreen -{ - typedef PaneCraftingScreen super; -public: - StonecutterScreen(); - ~StonecutterScreen(); - -private: - bool filterRecipe(const Recipe& r); -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_StonecutterScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_StonecutterScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_StonecutterScreen_H__ + +#include "PaneCraftingScreen.h" + +class StonecutterScreen: public PaneCraftingScreen +{ + typedef PaneCraftingScreen super; +public: + StonecutterScreen(); + ~StonecutterScreen(); + +private: + bool filterRecipe(const Recipe& r); +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_StonecutterScreen_H__*/ diff --git a/src/client/gui/screens/crafting/WorkbenchScreen.cpp b/src/client/gui/screens/crafting/WorkbenchScreen.cpp index 3c29dfc..45e23e5 100755 --- a/src/client/gui/screens/crafting/WorkbenchScreen.cpp +++ b/src/client/gui/screens/crafting/WorkbenchScreen.cpp @@ -1,15 +1,15 @@ -#include "WorkbenchScreen.h" -#include "CraftingFilters.h" -#include "../../../../world/level/material/Material.h" - -WorkbenchScreen::WorkbenchScreen(int craftingSize) -: super(craftingSize) -{ -} - -WorkbenchScreen::~WorkbenchScreen() { -} - -bool WorkbenchScreen::filterRecipe(const Recipe& r) { - return !CraftingFilters::isStonecutterItem(r.getResultItem()); -} +#include "WorkbenchScreen.h" +#include "CraftingFilters.h" +#include "../../../../world/level/material/Material.h" + +WorkbenchScreen::WorkbenchScreen(int craftingSize) +: super(craftingSize) +{ +} + +WorkbenchScreen::~WorkbenchScreen() { +} + +bool WorkbenchScreen::filterRecipe(const Recipe& r) { + return !CraftingFilters::isStonecutterItem(r.getResultItem()); +} diff --git a/src/client/gui/screens/crafting/WorkbenchScreen.h b/src/client/gui/screens/crafting/WorkbenchScreen.h index e4e5c24..abf8472 100755 --- a/src/client/gui/screens/crafting/WorkbenchScreen.h +++ b/src/client/gui/screens/crafting/WorkbenchScreen.h @@ -1,17 +1,17 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_WorkbenchScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_WorkbenchScreen_H__ - -#include "PaneCraftingScreen.h" - -class WorkbenchScreen: public PaneCraftingScreen -{ - typedef PaneCraftingScreen super; -public: - WorkbenchScreen(int craftingSize); - ~WorkbenchScreen(); - -private: - bool filterRecipe(const Recipe& r); -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_WorkbenchScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_WorkbenchScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_WorkbenchScreen_H__ + +#include "PaneCraftingScreen.h" + +class WorkbenchScreen: public PaneCraftingScreen +{ + typedef PaneCraftingScreen super; +public: + WorkbenchScreen(int craftingSize); + ~WorkbenchScreen(); + +private: + bool filterRecipe(const Recipe& r); +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_CRAFT_WorkbenchScreen_H__*/ diff --git a/src/client/gui/screens/touch/TouchIngameBlockSelectionScreen.h b/src/client/gui/screens/touch/TouchIngameBlockSelectionScreen.h index ee8cbb1..7a75f2e 100755 --- a/src/client/gui/screens/touch/TouchIngameBlockSelectionScreen.h +++ b/src/client/gui/screens/touch/TouchIngameBlockSelectionScreen.h @@ -1,73 +1,73 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchIngameBlockSelectionScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchIngameBlockSelectionScreen_H__ - -#include "../../Screen.h" -#include "../../components/InventoryPane.h" -#include "../../components/Button.h" -#include "../../components/ScrollingPane.h" -#include "../../components/ItemPane.h" -#include "../../TweenData.h" -#include "../../../player/input/touchscreen/TouchAreaModel.h" -#include "../../../../AppPlatform.h" - -namespace Touch { - -class IngameBlockSelectionScreen : public Screen, - public IInventoryPaneCallback -{ - typedef Screen super; - -public: - IngameBlockSelectionScreen(); - virtual ~IngameBlockSelectionScreen(); - - virtual void init() override; - virtual void setupPositions() override; - virtual void removed() override; - - void tick() override; - void render(int xm, int ym, float a) override; - - bool hasClippingArea(IntRectangle& out) override; - - // IInventoryPaneCallback - bool addItem(const InventoryPane* pane, int itemId) override; - bool isAllowed(int slot) override; - std::vector getItems(const InventoryPane* forPane) override; - - void buttonClicked(Button* button) override; -protected: - virtual void mouseClicked(int x, int y, int buttonNum) override; - virtual void mouseReleased(int x, int y, int buttonNum) override; - - // also support wheel scrolling - virtual void mouseWheel(int dx, int dy, int xm, int ym) override; -private: - void renderDemoOverlay(); - - //int getLinearSlotId(int x, int y); - int getSlotPosX(int slotX); - int getSlotPosY(int slotY); - int getSlotHeight(); - -private: - int selectedItem; - bool _pendingClose; - InventoryPane* _blockList; - - THeader bHeader; - ImageButton bDone; - TButton bCraft; - TButton bArmor; - TButton bMenu; - - IntRectangle clippingArea; - - int InventoryRows; - int InventorySize; - int InventoryColumns; -}; - -} - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchIngameBlockSelectionScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchIngameBlockSelectionScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchIngameBlockSelectionScreen_H__ + +#include "../../Screen.h" +#include "../../components/InventoryPane.h" +#include "../../components/Button.h" +#include "../../components/ScrollingPane.h" +#include "../../components/ItemPane.h" +#include "../../TweenData.h" +#include "../../../player/input/touchscreen/TouchAreaModel.h" +#include "../../../../AppPlatform.h" + +namespace Touch { + +class IngameBlockSelectionScreen : public Screen, + public IInventoryPaneCallback +{ + typedef Screen super; + +public: + IngameBlockSelectionScreen(); + virtual ~IngameBlockSelectionScreen(); + + virtual void init() override; + virtual void setupPositions() override; + virtual void removed() override; + + void tick() override; + void render(int xm, int ym, float a) override; + + bool hasClippingArea(IntRectangle& out) override; + + // IInventoryPaneCallback + bool addItem(const InventoryPane* pane, int itemId) override; + bool isAllowed(int slot) override; + std::vector getItems(const InventoryPane* forPane) override; + + void buttonClicked(Button* button) override; +protected: + virtual void mouseClicked(int x, int y, int buttonNum) override; + virtual void mouseReleased(int x, int y, int buttonNum) override; + + // also support wheel scrolling + virtual void mouseWheel(int dx, int dy, int xm, int ym) override; +private: + void renderDemoOverlay(); + + //int getLinearSlotId(int x, int y); + int getSlotPosX(int slotX); + int getSlotPosY(int slotY); + int getSlotHeight(); + +private: + int selectedItem; + bool _pendingClose; + InventoryPane* _blockList; + + THeader bHeader; + ImageButton bDone; + TButton bCraft; + TButton bArmor; + TButton bMenu; + + IntRectangle clippingArea; + + int InventoryRows; + int InventorySize; + int InventoryColumns; +}; + +} + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchIngameBlockSelectionScreen_H__*/ diff --git a/src/client/gui/screens/touch/TouchJoinGameScreen.cpp b/src/client/gui/screens/touch/TouchJoinGameScreen.cpp index f8ad00a..6d98ea3 100755 --- a/src/client/gui/screens/touch/TouchJoinGameScreen.cpp +++ b/src/client/gui/screens/touch/TouchJoinGameScreen.cpp @@ -1,243 +1,243 @@ -#include "TouchJoinGameScreen.h" -#include "../StartMenuScreen.h" -#include "../ProgressScreen.h" -#include "../../Font.h" -#include "../../../Minecraft.h" -#include "../../../renderer/Textures.h" - -namespace Touch { - -// -// Games list -// - -void AvailableGamesList::selectStart( int item) { - startSelected = item; -} - -void AvailableGamesList::selectCancel() { - startSelected = -1; -} - -void AvailableGamesList::selectItem( int item, bool doubleClick ) { - LOGI("selected an item! %d\n", item); - selectedItem = item; -} - -void AvailableGamesList::renderItem( int i, int x, int y, int h, Tesselator& t ) -{ - if (startSelected == i && Multitouch::getFirstActivePointerIdEx() >= 0) { - fill((int)x0, y, (int)x1, y+h, 0x809E684F); - } - - //static int colors[2] = {0xffffb0, 0xcccc90}; - const PingedCompatibleServer& s = copiedServerList[i]; - unsigned int color = s.isSpecial? 0x6090a0 : 0xffffb0; - unsigned int color2 = 0xffffa0;//colors[i&1]; - - int xx1 = (int)x0 + 24; - int xx2 = xx1; - - if (s.isSpecial) { - xx1 += 50; - - glEnable2(GL_TEXTURE_2D); - glColor4f2(1,1,1,1); - glEnable2(GL_BLEND); - minecraft->textures->loadAndBindTexture("gui/badge/minecon140.png"); - blit(xx2, y + 6, 0, 0, 37, 8, 140, 240); - } - - drawString(minecraft->font, s.name.C_String(), xx1, y + 4 + 2, color); - drawString(minecraft->font, s.address.ToString(false), xx2, y + 18, color2); - - /* - drawString(minecraft->font, copiedServerList[i].name.C_String(), (int)x0 + 24, y + 4, color); - drawString(minecraft->font, copiedServerList[i].address.ToString(false), (int)x0 + 24, y + 18, color); - */ -} - - -// -// Join Game screen -// -JoinGameScreen::JoinGameScreen() -: bJoin( 2, "Join Game"), - bBack( 3, "Back"), - bJoinByIp(4, "Join By IP"), - bHeader(0, ""), - gamesList(NULL) -{ - bJoin.active = false; - //gamesList->yInertia = 0.5f; -} - -JoinGameScreen::~JoinGameScreen() -{ - delete gamesList; -} - -void JoinGameScreen::init() -{ - //buttons.push_back(&bJoin); - buttons.push_back(&bBack); - buttons.push_back(&bJoinByIp); - buttons.push_back(&bHeader); - - minecraft->raknetInstance->clearServerList(); - gamesList = new AvailableGamesList(minecraft, width, height); - -#ifdef ANDROID - //tabButtons.push_back(&bJoin); - tabButtons.push_back(&bBack); - tabButtons.push_back(&bJoinByIp); -#endif -} - -void JoinGameScreen::setupPositions() { - //int yBase = height - 26; - - //#ifdef ANDROID - bJoin.y = 0; - bBack.y = 0; - bJoinByIp.y = 0; - bHeader.y = 0; - //#endif - - // Center buttons - //bJoin.x = width / 2 - 4 - bJoin.w; - bBack.x = 0;//width / 2 + 4; - bJoinByIp.x = width - bJoinByIp.width;; - bHeader.x = bJoinByIp.width; - bHeader.width = width - (bBack.width + bJoinByIp.width); -} - -void JoinGameScreen::buttonClicked(Button* button) -{ - if (button->id == bJoin.id) - { - if (isIndexValid(gamesList->selectedItem)) - { - PingedCompatibleServer selectedServer = gamesList->copiedServerList[gamesList->selectedItem]; - minecraft->joinMultiplayer(selectedServer); - { - bJoin.active = false; - bBack.active = false; - minecraft->setScreen(new ProgressScreen()); - } - } - //minecraft->locateMultiplayer(); - //minecraft->setScreen(new JoinGameScreen()); - } - if(button->id == bJoinByIp.id) { - minecraft->cancelLocateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_JOINBYIP); - } - - if (button->id == bBack.id) - { - minecraft->cancelLocateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } -} - -bool JoinGameScreen::handleBackEvent(bool isDown) -{ - if (!isDown) - { - minecraft->cancelLocateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } - return true; -} - - -bool JoinGameScreen::isIndexValid( int index ) -{ - return gamesList && index >= 0 && index < gamesList->getNumberOfItems(); -} - -void JoinGameScreen::tick() -{ - if (isIndexValid(gamesList->selectedItem)) { - buttonClicked(&bJoin); - return; - } - - //gamesList->tick(); - - const ServerList& orgServerList = minecraft->raknetInstance->getServerList(); - ServerList serverList; - for (unsigned int i = 0; i < orgServerList.size(); ++i) - if (orgServerList[i].name.GetLength() > 0) - serverList.push_back(orgServerList[i]); - - if (serverList.size() != gamesList->copiedServerList.size()) - { - // copy the currently selected item - PingedCompatibleServer selectedServer; - bool hasSelection = false; - if (isIndexValid(gamesList->selectedItem)) - { - selectedServer = gamesList->copiedServerList[gamesList->selectedItem]; - hasSelection = true; - } - - gamesList->copiedServerList = serverList; - gamesList->selectItem(-1, false); - - // re-select previous item if it still exists - if (hasSelection) - { - for (unsigned int i = 0; i < gamesList->copiedServerList.size(); i++) - { - if (gamesList->copiedServerList[i].address == selectedServer.address) - { - gamesList->selectItem(i, false); - break; - } - } - } - } else { - for (int i = (int)gamesList->copiedServerList.size()-1; i >= 0 ; --i) { - for (int j = 0; j < (int) serverList.size(); ++j) - if (serverList[j].address == gamesList->copiedServerList[i].address) - gamesList->copiedServerList[i].name = serverList[j].name; - } - } - - bJoin.active = isIndexValid(gamesList->selectedItem); -} - -void JoinGameScreen::render( int xm, int ym, float a ) -{ - bool hasNetwork = minecraft->platform()->isNetworkEnabled(true); -#ifdef WIN32 - hasNetwork = hasNetwork && !GetAsyncKeyState(VK_TAB); -#endif - - renderBackground(); - if (hasNetwork) gamesList->render(xm, ym, a); - else gamesList->renderDirtBackground(); - Screen::render(xm, ym, a); - - const int baseX = bHeader.x + bHeader.width / 2; - - if (hasNetwork) { - std::string s = "Scanning for WiFi Games..."; - drawCenteredString(minecraft->font, s, baseX, 8, 0xffffffff); - - const int textWidth = minecraft->font->width(s); - const int spinnerX = baseX + textWidth / 2 + 6; - - static const char* spinnerTexts[] = {"-", "\\", "|", "/"}; - int n = ((int)(5.5f * getTimeS()) % 4); - drawCenteredString(minecraft->font, spinnerTexts[n], spinnerX, 8, 0xffffffff); - } else { - drawCenteredString(minecraft->font, "WiFi is disabled", baseX, 8, 0xffffffff); - } -} - -bool JoinGameScreen::isInGameScreen() { return false; } - -}; +#include "TouchJoinGameScreen.h" +#include "../StartMenuScreen.h" +#include "../ProgressScreen.h" +#include "../../Font.h" +#include "../../../Minecraft.h" +#include "../../../renderer/Textures.h" + +namespace Touch { + +// +// Games list +// + +void AvailableGamesList::selectStart( int item) { + startSelected = item; +} + +void AvailableGamesList::selectCancel() { + startSelected = -1; +} + +void AvailableGamesList::selectItem( int item, bool doubleClick ) { + LOGI("selected an item! %d\n", item); + selectedItem = item; +} + +void AvailableGamesList::renderItem( int i, int x, int y, int h, Tesselator& t ) +{ + if (startSelected == i && Multitouch::getFirstActivePointerIdEx() >= 0) { + fill((int)x0, y, (int)x1, y+h, 0x809E684F); + } + + //static int colors[2] = {0xffffb0, 0xcccc90}; + const PingedCompatibleServer& s = copiedServerList[i]; + unsigned int color = s.isSpecial? 0x6090a0 : 0xffffb0; + unsigned int color2 = 0xffffa0;//colors[i&1]; + + int xx1 = (int)x0 + 24; + int xx2 = xx1; + + if (s.isSpecial) { + xx1 += 50; + + glEnable2(GL_TEXTURE_2D); + glColor4f2(1,1,1,1); + glEnable2(GL_BLEND); + minecraft->textures->loadAndBindTexture("gui/badge/minecon140.png"); + blit(xx2, y + 6, 0, 0, 37, 8, 140, 240); + } + + drawString(minecraft->font, s.name.C_String(), xx1, y + 4 + 2, color); + drawString(minecraft->font, s.address.ToString(false), xx2, y + 18, color2); + + /* + drawString(minecraft->font, copiedServerList[i].name.C_String(), (int)x0 + 24, y + 4, color); + drawString(minecraft->font, copiedServerList[i].address.ToString(false), (int)x0 + 24, y + 18, color); + */ +} + + +// +// Join Game screen +// +JoinGameScreen::JoinGameScreen() +: bJoin( 2, "Join Game"), + bBack( 3, "Back"), + bJoinByIp(4, "Join By IP"), + bHeader(0, ""), + gamesList(NULL) +{ + bJoin.active = false; + //gamesList->yInertia = 0.5f; +} + +JoinGameScreen::~JoinGameScreen() +{ + delete gamesList; +} + +void JoinGameScreen::init() +{ + //buttons.push_back(&bJoin); + buttons.push_back(&bBack); + buttons.push_back(&bJoinByIp); + buttons.push_back(&bHeader); + + minecraft->raknetInstance->clearServerList(); + gamesList = new AvailableGamesList(minecraft, width, height); + +#ifdef ANDROID + //tabButtons.push_back(&bJoin); + tabButtons.push_back(&bBack); + tabButtons.push_back(&bJoinByIp); +#endif +} + +void JoinGameScreen::setupPositions() { + //int yBase = height - 26; + + //#ifdef ANDROID + bJoin.y = 0; + bBack.y = 0; + bJoinByIp.y = 0; + bHeader.y = 0; + //#endif + + // Center buttons + //bJoin.x = width / 2 - 4 - bJoin.w; + bBack.x = 0;//width / 2 + 4; + bJoinByIp.x = width - bJoinByIp.width;; + bHeader.x = bJoinByIp.width; + bHeader.width = width - (bBack.width + bJoinByIp.width); +} + +void JoinGameScreen::buttonClicked(Button* button) +{ + if (button->id == bJoin.id) + { + if (isIndexValid(gamesList->selectedItem)) + { + PingedCompatibleServer selectedServer = gamesList->copiedServerList[gamesList->selectedItem]; + minecraft->joinMultiplayer(selectedServer); + { + bJoin.active = false; + bBack.active = false; + minecraft->setScreen(new ProgressScreen()); + } + } + //minecraft->locateMultiplayer(); + //minecraft->setScreen(new JoinGameScreen()); + } + if(button->id == bJoinByIp.id) { + minecraft->cancelLocateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_JOINBYIP); + } + + if (button->id == bBack.id) + { + minecraft->cancelLocateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } +} + +bool JoinGameScreen::handleBackEvent(bool isDown) +{ + if (!isDown) + { + minecraft->cancelLocateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } + return true; +} + + +bool JoinGameScreen::isIndexValid( int index ) +{ + return gamesList && index >= 0 && index < gamesList->getNumberOfItems(); +} + +void JoinGameScreen::tick() +{ + if (isIndexValid(gamesList->selectedItem)) { + buttonClicked(&bJoin); + return; + } + + //gamesList->tick(); + + const ServerList& orgServerList = minecraft->raknetInstance->getServerList(); + ServerList serverList; + for (unsigned int i = 0; i < orgServerList.size(); ++i) + if (orgServerList[i].name.GetLength() > 0) + serverList.push_back(orgServerList[i]); + + if (serverList.size() != gamesList->copiedServerList.size()) + { + // copy the currently selected item + PingedCompatibleServer selectedServer; + bool hasSelection = false; + if (isIndexValid(gamesList->selectedItem)) + { + selectedServer = gamesList->copiedServerList[gamesList->selectedItem]; + hasSelection = true; + } + + gamesList->copiedServerList = serverList; + gamesList->selectItem(-1, false); + + // re-select previous item if it still exists + if (hasSelection) + { + for (unsigned int i = 0; i < gamesList->copiedServerList.size(); i++) + { + if (gamesList->copiedServerList[i].address == selectedServer.address) + { + gamesList->selectItem(i, false); + break; + } + } + } + } else { + for (int i = (int)gamesList->copiedServerList.size()-1; i >= 0 ; --i) { + for (int j = 0; j < (int) serverList.size(); ++j) + if (serverList[j].address == gamesList->copiedServerList[i].address) + gamesList->copiedServerList[i].name = serverList[j].name; + } + } + + bJoin.active = isIndexValid(gamesList->selectedItem); +} + +void JoinGameScreen::render( int xm, int ym, float a ) +{ + bool hasNetwork = minecraft->platform()->isNetworkEnabled(true); +#ifdef WIN32 + hasNetwork = hasNetwork && !GetAsyncKeyState(VK_TAB); +#endif + + renderBackground(); + if (hasNetwork) gamesList->render(xm, ym, a); + else gamesList->renderDirtBackground(); + Screen::render(xm, ym, a); + + const int baseX = bHeader.x + bHeader.width / 2; + + if (hasNetwork) { + std::string s = "Scanning for WiFi Games..."; + drawCenteredString(minecraft->font, s, baseX, 8, 0xffffffff); + + const int textWidth = minecraft->font->width(s); + const int spinnerX = baseX + textWidth / 2 + 6; + + static const char* spinnerTexts[] = {"-", "\\", "|", "/"}; + int n = ((int)(5.5f * getTimeS()) % 4); + drawCenteredString(minecraft->font, spinnerTexts[n], spinnerX, 8, 0xffffffff); + } else { + drawCenteredString(minecraft->font, "WiFi is disabled", baseX, 8, 0xffffffff); + } +} + +bool JoinGameScreen::isInGameScreen() { return false; } + +}; diff --git a/src/client/gui/screens/touch/TouchJoinGameScreen.h b/src/client/gui/screens/touch/TouchJoinGameScreen.h index 2205bf6..bfb99ef 100755 --- a/src/client/gui/screens/touch/TouchJoinGameScreen.h +++ b/src/client/gui/screens/touch/TouchJoinGameScreen.h @@ -1,74 +1,74 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchJoinGameScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchJoinGameScreen_H__ - -#include "../../Screen.h" -#include "../../components/Button.h" -#include "../../components/RolledSelectionListV.h" -#include "../../../Minecraft.h" -#include "../../../../platform/input/Multitouch.h" -#include "../../../../network/RakNetInstance.h" - -namespace Touch { - -class JoinGameScreen; - -class AvailableGamesList : public RolledSelectionListV -{ - int startSelected; - int selectedItem; - ServerList copiedServerList; - - friend class JoinGameScreen; - -public: - AvailableGamesList(Minecraft* _minecraft, int _width, int _height) - : RolledSelectionListV(_minecraft, _width, _height, 0, _width, 24, _height, 34), - selectedItem(-1), - startSelected(-1) - { - } - -protected: - - virtual int getNumberOfItems() { return (int)copiedServerList.size(); } - - virtual void selectCancel(); - virtual void selectStart(int item); - virtual void selectItem(int item, bool doubleClick); - - virtual bool isSelectedItem(int item) { return item == selectedItem; } - - virtual void renderBackground() {} - virtual void renderItem(int i, int x, int y, int h, Tesselator& t); -}; - -class JoinGameScreen: public Screen -{ -public: - JoinGameScreen(); - virtual ~JoinGameScreen(); - - void init(); - void setupPositions(); - - virtual bool handleBackEvent(bool isDown); - - virtual bool isIndexValid(int index); - - virtual void tick(); - void render(int xm, int ym, float a); - - void buttonClicked(Button* button); - - bool isInGameScreen(); -private: - Button bJoin; - TButton bBack; - TButton bJoinByIp; - THeader bHeader; - AvailableGamesList* gamesList; -}; - -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchJoinGameScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchJoinGameScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchJoinGameScreen_H__ + +#include "../../Screen.h" +#include "../../components/Button.h" +#include "../../components/RolledSelectionListV.h" +#include "../../../Minecraft.h" +#include "../../../../platform/input/Multitouch.h" +#include "../../../../network/RakNetInstance.h" + +namespace Touch { + +class JoinGameScreen; + +class AvailableGamesList : public RolledSelectionListV +{ + int startSelected; + int selectedItem; + ServerList copiedServerList; + + friend class JoinGameScreen; + +public: + AvailableGamesList(Minecraft* _minecraft, int _width, int _height) + : RolledSelectionListV(_minecraft, _width, _height, 0, _width, 24, _height, 34), + selectedItem(-1), + startSelected(-1) + { + } + +protected: + + virtual int getNumberOfItems() { return (int)copiedServerList.size(); } + + virtual void selectCancel(); + virtual void selectStart(int item); + virtual void selectItem(int item, bool doubleClick); + + virtual bool isSelectedItem(int item) { return item == selectedItem; } + + virtual void renderBackground() {} + virtual void renderItem(int i, int x, int y, int h, Tesselator& t); +}; + +class JoinGameScreen: public Screen +{ +public: + JoinGameScreen(); + virtual ~JoinGameScreen(); + + void init(); + void setupPositions(); + + virtual bool handleBackEvent(bool isDown); + + virtual bool isIndexValid(int index); + + virtual void tick(); + void render(int xm, int ym, float a); + + void buttonClicked(Button* button); + + bool isInGameScreen(); +private: + Button bJoin; + TButton bBack; + TButton bJoinByIp; + THeader bHeader; + AvailableGamesList* gamesList; +}; + +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchJoinGameScreen_H__*/ diff --git a/src/client/gui/screens/touch/TouchSelectWorldScreen.cpp b/src/client/gui/screens/touch/TouchSelectWorldScreen.cpp index 0ed793d..9e0d439 100755 --- a/src/client/gui/screens/touch/TouchSelectWorldScreen.cpp +++ b/src/client/gui/screens/touch/TouchSelectWorldScreen.cpp @@ -1,605 +1,605 @@ -#include "TouchSelectWorldScreen.h" -#include "../StartMenuScreen.h" -#include "../ProgressScreen.h" -#include "../DialogDefinitions.h" -#include "../../components/ImageButton.h" //weird! -#include "../../../renderer/Textures.h" -#include "../../../renderer/Tesselator.h" -#include "../../../../world/level/LevelSettings.h" -#include "../../../../AppPlatform.h" -#include "../../../../util/StringUtils.h" -#include "../../../../util/Mth.h" -#include "../../../../platform/input/Mouse.h" -#include "../../../../Performance.h" - -#include -#include -#include "../SimpleChooseLevelScreen.h" - -namespace Touch { - -// -// World Selection List -// -TouchWorldSelectionList::TouchWorldSelectionList( Minecraft* minecraft, int width, int height ) -: _height(height), - hasPickedLevel(false), - pickedIndex(-1), - currentTick(0), - stoppedTick(-1), - mode(0), - _newWorldSelected(false), - RolledSelectionListH(minecraft, width, height, 0, width, 24, height-32, 120) -{ - _renderBottomBorder = false; - //setRenderSelection(false); -} - -int TouchWorldSelectionList::getNumberOfItems() { - return (int)levels.size() + 1; -} - -void TouchWorldSelectionList::selectItem( int item, bool doubleClick ) { - if (selectedItem < 0) - return; - - const int delta = item - selectedItem; - - if (delta == -1) - stepLeft(); - if (delta == +1) - stepRight(); - if (delta == 0 ) { - if (!hasPickedLevel) { - hasPickedLevel = true; - pickedIndex = item; - if (item < (int)levels.size()) - pickedLevel = levels[item]; - } - } -} - -bool TouchWorldSelectionList::isSelectedItem( int item ) { - return item == selectedItem; -} - -void TouchWorldSelectionList::selectStart(int item, int localX, int localY) { - if (selectedItem != (int) levels.size() || item != selectedItem) - return; - _newWorldSelected = true; -} - -void TouchWorldSelectionList::selectCancel() { - _newWorldSelected = false; -} - -void TouchWorldSelectionList::renderItem( int i, int x, int y, int h, Tesselator& t ) { - int centerx = x + itemWidth/2; - float a0 = Mth::Max(1.1f - std::abs( width / 2 - centerx ) * 0.0055f, 0.2f); - if (a0 > 1) a0 = 1; - int textColor = (int)(255.0f * a0) * 0x010101; - int textColor2 = (int)(140.0f * a0) * 0x010101; - const int TX = centerx - itemWidth / 2 + 5; - const int TY = y + 44; //@kindle-res:42 - - if (i < (int)levels.size()) { - // Draw the worlds - StringVector v = _descriptions[i]; - drawString(minecraft->font, v[0].c_str(), TX, TY + 0, textColor); - drawString(minecraft->font, v[1].c_str(), TX, TY + 10, textColor2); - drawString(minecraft->font, v[2].c_str(), TX, TY + 20, textColor2); - drawString(minecraft->font, v[3].c_str(), TX, TY + 30, textColor2); - - minecraft->textures->loadAndBindTexture(_imageNames[i]); - t.color(0.3f, 1.0f, 0.2f); - - //float x0 = (float)x; - //float x1 = (float)x + (float)itemWidth; - - const float IY = (float)y - 8; // @kindle-res: -3 - t.begin(); - t.color(textColor); - t.vertexUV((float)(centerx-32), IY, blitOffset, 0, 0.125f); - t.vertexUV((float)(centerx-32), IY + 48, blitOffset, 0, 0.875f); //@kindle-res: +44 - t.vertexUV((float)(centerx+32), IY + 48, blitOffset, 1, 0.875f); //@kindle-res: +44 - t.vertexUV((float)(centerx+32), IY, blitOffset, 1, 0.125f); - t.draw(); - } else { - // Draw the "Create new world" icon - drawCenteredString(minecraft->font, "Create new", centerx, TY + 12, textColor); - - minecraft->textures->loadAndBindTexture("gui/touchgui.png"); - - const bool selected = _newWorldSelected; - - const float W = 54.0f; - const float H = 54.0f; - const float IY = (float)y; - const float u0 = (168.0f ) / 256.0f; - const float u1 = (168.0f + W) / 256.0f; - float v0 = (32.0f ) / 256.0f; - float v1 = (32.0f + H) / 256.0f; - if (selected) { - v0 += H / 256.0f; - v1 += H / 256.0f; - } - - t.begin(); - t.color(textColor); - t.vertexUV((float)centerx - W*0.5f, IY, blitOffset, u0, v0); - t.vertexUV((float)centerx - W*0.5f, IY + H, blitOffset, u0, v1); - t.vertexUV((float)centerx + W*0.5f, IY + H, blitOffset, u1, v1); - t.vertexUV((float)centerx + W*0.5f, IY, blitOffset, u1, v0); - t.draw(); - } -} - -void TouchWorldSelectionList::stepLeft() { - if (selectedItem > 0) { - int xoffset = (int)(xo - ((float)(selectedItem * itemWidth) + ((float)(itemWidth-width)) * 0.5f)); - td.start = xo; - td.stop = xo - itemWidth - xoffset; - td.cur = 0; - td.dur = 8; - mode = 1; - tweenInited(); - } -} - -void TouchWorldSelectionList::stepRight() { - if (selectedItem >= 0 && selectedItem < getNumberOfItems()-1) { - int xoffset = (int)(xo - ((float)(selectedItem * itemWidth) + ((float)(itemWidth-width)) * 0.5f)); - td.start = xo; - td.stop = xo + itemWidth - xoffset; - td.cur = 0; - td.dur = 8; - mode = 1; - tweenInited(); - } -} - -void TouchWorldSelectionList::commit() { - for (unsigned int i = 0; i < levels.size(); ++i) { - LevelSummary& level = levels[i]; - - std::stringstream ss; - ss << level.name << "/preview.png"; - TextureId id = Textures::InvalidId;//minecraft->textures->loadTexture(ss.str(), false); - - if (id != Textures::InvalidId) { - _imageNames.push_back( ss.str() ); - } else { - _imageNames.push_back("gui/default_world.png"); - } - - StringVector lines; - lines.push_back(levels[i].name); - lines.push_back(minecraft->platform()->getDateString(levels[i].lastPlayed)); - lines.push_back(levels[i].id); - lines.push_back(LevelSettings::gameTypeToString(level.gameType)); - _descriptions.push_back(lines); - - selectedItem = 0; - } -} - -static float quadraticInOut(float t, float dur, float start, float stop) { - const float delta = stop - start; - const float T = (t / dur) * 2.0f; - if (T < 1) return 0.5f*delta*T*T + start; - return -0.5f*delta * ((T-1)*(T-3) - 1) + start; -} - -void TouchWorldSelectionList::tick() -{ - RolledSelectionListH::tick(); - - ++currentTick; - - if (Mouse::isButtonDown(MouseAction::ACTION_LEFT) || dragState == 0) - return; - - // Handle the tween (when in "mode 1") - selectedItem = -1; - if (mode == 1) { - if (++td.cur == td.dur) { - mode = 0; - xInertia = 0; - xoo = xo = td.stop; - selectedItem = getItemAtPosition(width/2, height/2); - } else { - tweenInited(); - } - return; - } - - // It's still going fast, let it run - float speed = Mth::abs(xInertia); - bool slowEnoughToBeBothered = speed < 5.0f; - if (!slowEnoughToBeBothered) { - xInertia = xInertia * .9f; - return; - } - - xInertia *= 0.8f; - - if (speed < 1 && dragState < 0) { - const int offsetx = (width-itemWidth) / 2; - const float pxo = xo + offsetx; - int index = getItemAtXPositionRaw((int)(pxo - 10*xInertia)); - int indexPos = index*itemWidth; - - // Pick closest - float diff = (float)indexPos - pxo; - if (diff < -itemWidth/2) { - diff += itemWidth; - index++; - //indexPos += itemWidth; - } - if (Mth::abs(diff) < 1 && speed < 0.1f) { - selectedItem = getItemAtPosition(width/2, height/2); - return; - } - - td.start = xo; - td.stop = xo + diff; - td.cur = 0; - td.dur = (float) Mth::Min(7, 1 + (int)(Mth::abs(diff) * 0.25f)); - mode = 1; - //LOGI("inited-t %d\n", dragState); - tweenInited(); - } -} - -float TouchWorldSelectionList::getPos( float alpha ) -{ - if (mode != 1) return RolledSelectionListH::getPos(alpha); - - float x0 = quadraticInOut(td.cur, td.dur, td.start, td.stop); - float x1 = quadraticInOut(td.cur+1, td.dur, td.start, td.stop); - return x0 + (x1-x0)*alpha; -} - -bool TouchWorldSelectionList::capXPosition() { - bool capped = RolledSelectionListH::capXPosition(); - if (capped) mode = 0; - return capped; -} - -void TouchWorldSelectionList::tweenInited() { - float x0 = quadraticInOut(td.cur, td.dur, td.start, td.stop); - float x1 = quadraticInOut(td.cur+1, td.dur, td.start, td.stop); - _xinertia = 0; - xInertia = x0-x1; // yes, it's all backwards and messed up.. -} - -// -// Select World Screen -// -SelectWorldScreen::SelectWorldScreen() -: bDelete (1, ""), - bCreate (2, "Create new"), - bBack (3, "Back"), - bHeader (0, "Select world"), - bWorldView(4, ""), - worldsList(NULL), - _hasStartedLevel(false) -{ - bDelete.active = false; - - // Delete button - ImageDef def; - def.name = "gui/touchgui.png"; - def.width = 34; - def.height = 26; - - def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); - bDelete.setImageDef(def, true); - - // Create new, and Back button -/* - def.src.y = 26; // @ 0, 26 - def.src.w = def.w = 66; // 66, 26 size - bBack.setImageDef(def, true); - bCreate.setImageDef(def, true); -*/ -} - -SelectWorldScreen::~SelectWorldScreen() -{ - delete worldsList; -} - -void SelectWorldScreen::init() -{ - worldsList = new TouchWorldSelectionList(minecraft, width, height); - loadLevelSource(); - worldsList->commit(); - - buttons.push_back(&bDelete); - buttons.push_back(&bCreate); - buttons.push_back(&bBack); - buttons.push_back(&bHeader); - - _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); - - tabButtons.push_back(&bWorldView); - tabButtons.push_back(&bDelete); - tabButtons.push_back(&bCreate); - tabButtons.push_back(&bBack); -} - -void SelectWorldScreen::setupPositions() { - //#ifdef ANDROID - bCreate.y = 0; - bBack.y = 0; - bHeader.y = 0; - bDelete.y = height - 30; - - // Center buttons - bDelete.x = (width - bDelete.width) / 2; - bCreate.x = width - bCreate.width;//width / 2 - bCreate.w / 2; - bBack.x = 0;//width / 2 + 4 + bCreate.w - bBack.w / 2; - bHeader.x = bBack.width; - bHeader.width = width - (bBack.width + bCreate.width); - bHeader.height = bCreate.height; -} - -void SelectWorldScreen::buttonClicked(Button* button) -{ - if (button->id == bCreate.id) { - if (!_hasStartedLevel) { - std::string name = getUniqueLevelName("World"); - minecraft->setScreen(new SimpleChooseLevelScreen(name)); - } - } - if (button->id == bDelete.id) { - if (isIndexValid(worldsList->selectedItem)) { - LevelSummary level = worldsList->levels[worldsList->selectedItem]; - LOGI("level: %s, %s\n", level.id.c_str(), level.name.c_str()); - minecraft->setScreen( new TouchDeleteWorldScreen(level) ); - } - } - if (button->id == bBack.id) { - minecraft->cancelLocateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } - if (button->id == bWorldView.id) { - // Try to "click" the item in the middle - worldsList->selectItem( worldsList->getItemAtPosition(width/2, height/2), false ); - } -} - -bool SelectWorldScreen::handleBackEvent(bool isDown) -{ - if (!isDown) - { - minecraft->cancelLocateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_STARTMENU); - } - return true; -} - -bool SelectWorldScreen::isIndexValid( int index ) -{ - return worldsList && index >= 0 && index < worldsList->getNumberOfItems() - 1; -} - -static char ILLEGAL_FILE_CHARACTERS[] = { - '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' -}; - -void SelectWorldScreen::mouseWheel(int dx, int dy, int xm, int ym) -{ - if (!worldsList) return; - if (dy == 0) return; - int num = worldsList->getNumberOfItems(); - int idx = worldsList->selectedItem; - if (dy > 0) { - if (idx > 0) { - idx--; - worldsList->stepLeft(); - } - } else { - if (idx < num - 1) { - idx++; - worldsList->stepRight(); - } - } - worldsList->selectedItem = idx; -} - -void SelectWorldScreen::tick() -{ -#if 0 - if (_state == _STATE_CREATEWORLD) { - #if defined(RPI) - std::string levelId = getUniqueLevelName("World"); - //int seed = Util::hashCode("/r/Minecraft"); - LevelSettings settings(getEpochTimeS(), GameType::Creative); - minecraft->selectLevel(levelId, levelId, settings); - minecraft->hostMultiplayer(); - minecraft->setScreen(new ProgressScreen()); - _hasStartedLevel = true; - #elif defined(PLATFORM_DESKTOP) - std::string name = getUniqueLevelName("World"); - minecraft->setScreen(new SimpleChooseLevelScreen(name)); - #else - int status = minecraft->platform()->getUserInputStatus(); - //LOGI("Status is: %d\n", status); - if (status > -1) { - if (status == 1) { - StringVector sv = minecraft->platform()->getUserInput(); - - // Read the level name. - // 1) Trim name 2) Remove all bad chars 3) Append '-' chars 'til the name is unique - std::string levelName = Util::stringTrim(sv[0]); - std::string levelId = levelName; - - for (int i = 0; i < sizeof(ILLEGAL_FILE_CHARACTERS) / sizeof(char); ++i) - levelId = Util::stringReplace(levelId, std::string(1, ILLEGAL_FILE_CHARACTERS[i]), ""); - if ((int)levelId.length() == 0) { - levelId = "no_name"; - } - levelId = getUniqueLevelName(levelId); - - // Read the seed - int seed = getEpochTimeS(); - if (sv.size() >= 2) { - std::string seedString = Util::stringTrim(sv[1]); - if (seedString.length() > 0) { - int tmpSeed; - // Try to read it as an integer - if (sscanf(seedString.c_str(), "%d", &tmpSeed) > 0) { - seed = tmpSeed; - } // Hash the "seed" - else { - seed = Util::hashCode(seedString); - } - } - } - // Read the game mode - bool isCreative = true; - if (sv.size() >= 3 && sv[2] == "survival") - isCreative = false; - - // Start a new level with the given name and seed - LOGI("Creating a level with id '%s', name '%s' and seed '%d'\n", levelId.c_str(), levelName.c_str(), seed); - LevelSettings settings(seed, isCreative? GameType::Creative : GameType::Survival); - minecraft->selectLevel(levelId, levelName, settings); - minecraft->hostMultiplayer(); - minecraft->setScreen(new ProgressScreen()); - _hasStartedLevel = true; - } - _state = _STATE_DEFAULT; - // Reset the world list - worldsList->hasPickedLevel = false; - worldsList->pickedIndex = -1; - } - #endif - - worldsList->hasPickedLevel = false; - return; - } -#endif - - worldsList->tick(); - - if (worldsList->hasPickedLevel) { - if (worldsList->pickedIndex == worldsList->levels.size()) { - worldsList->hasPickedLevel = false; - std::string name = getUniqueLevelName("World"); - minecraft->setScreen(new SimpleChooseLevelScreen(name)); - } else { - minecraft->selectLevel(worldsList->pickedLevel.id, worldsList->pickedLevel.name, LevelSettings::None()); - minecraft->hostMultiplayer(); - minecraft->setScreen(new ProgressScreen()); - _hasStartedLevel = true; - return; - } - } - - // copy the currently selected item - LevelSummary selectedWorld; - //bool hasSelection = false; - if (isIndexValid(worldsList->selectedItem)) - { - selectedWorld = worldsList->levels[worldsList->selectedItem]; - //hasSelection = true; - } - - bDelete.active = isIndexValid(worldsList->selectedItem); -} - -void SelectWorldScreen::render( int xm, int ym, float a ) -{ - //Performance::watches.get("sws-full").start(); - //Performance::watches.get("sws-renderbg").start(); - renderBackground(); - //Performance::watches.get("sws-renderbg").stop(); - //Performance::watches.get("sws-worlds").start(); - - worldsList->setComponentSelected(bWorldView.selected); - - if (_mouseHasBeenUp) - worldsList->render(xm, ym, a); - else { - worldsList->render(0, 0, a); - _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); - } - - //Performance::watches.get("sws-worlds").stop(); - //Performance::watches.get("sws-screen").start(); - Screen::render(xm, ym, a); - //Performance::watches.get("sws-screen").stop(); - - //minecraft->textures->loadAndBindTexture("gui/selectworld/trash.png"); - - //Performance::watches.get("sws-string").start(); - //Performance::watches.get("sws-string").stop(); - - //Performance::watches.get("sws-full").stop(); - //Performance::watches.printEvery(128); -} - -void SelectWorldScreen::loadLevelSource() -{ - LevelStorageSource* levelSource = minecraft->getLevelSource(); - levelSource->getLevelList(levels); - std::sort(levels.begin(), levels.end()); - - for (unsigned int i = 0; i < levels.size(); ++i) { - if (levels[i].id != LevelStorageSource::TempLevelId) - worldsList->levels.push_back( levels[i] ); - } -} - - -std::string SelectWorldScreen::getUniqueLevelName( const std::string& level ) -{ - std::set Set; - for (unsigned int i = 0; i < levels.size(); ++i) - Set.insert(levels[i].id); - - std::string s = level; - while ( Set.find(s) != Set.end() ) - s += "-"; - return s; -} - -bool SelectWorldScreen::isInGameScreen() { return true; } - -void SelectWorldScreen::keyPressed( int eventKey ) -{ - if (bWorldView.selected) { - if (eventKey == minecraft->options.getIntValue(OPTIONS_KEY_LEFT)) - worldsList->stepLeft(); - if (eventKey == minecraft->options.getIntValue(OPTIONS_KEY_RIGHT)) - worldsList->stepRight(); - } - - Screen::keyPressed(eventKey); -} - -// -// Delete World Screen -// -TouchDeleteWorldScreen::TouchDeleteWorldScreen(const LevelSummary& level) -: ConfirmScreen(NULL, "Are you sure you want to delete this world?", - "'" + level.name + "' will be lost forever!", - "Delete", "Cancel", 0), - _level(level) -{ - tabButtonIndex = 1; -} - -void TouchDeleteWorldScreen::postResult( bool isOk ) -{ - if (isOk) { - LevelStorageSource* storageSource = minecraft->getLevelSource(); - storageSource->deleteLevel(_level.id); - } - minecraft->screenChooser.setScreen(SCREEN_SELECTWORLD); -} - -}; +#include "TouchSelectWorldScreen.h" +#include "../StartMenuScreen.h" +#include "../ProgressScreen.h" +#include "../DialogDefinitions.h" +#include "../../components/ImageButton.h" //weird! +#include "../../../renderer/Textures.h" +#include "../../../renderer/Tesselator.h" +#include "../../../../world/level/LevelSettings.h" +#include "../../../../AppPlatform.h" +#include "../../../../util/StringUtils.h" +#include "../../../../util/Mth.h" +#include "../../../../platform/input/Mouse.h" +#include "../../../../Performance.h" + +#include +#include +#include "../SimpleChooseLevelScreen.h" + +namespace Touch { + +// +// World Selection List +// +TouchWorldSelectionList::TouchWorldSelectionList( Minecraft* minecraft, int width, int height ) +: _height(height), + hasPickedLevel(false), + pickedIndex(-1), + currentTick(0), + stoppedTick(-1), + mode(0), + _newWorldSelected(false), + RolledSelectionListH(minecraft, width, height, 0, width, 24, height-32, 120) +{ + _renderBottomBorder = false; + //setRenderSelection(false); +} + +int TouchWorldSelectionList::getNumberOfItems() { + return (int)levels.size() + 1; +} + +void TouchWorldSelectionList::selectItem( int item, bool doubleClick ) { + if (selectedItem < 0) + return; + + const int delta = item - selectedItem; + + if (delta == -1) + stepLeft(); + if (delta == +1) + stepRight(); + if (delta == 0 ) { + if (!hasPickedLevel) { + hasPickedLevel = true; + pickedIndex = item; + if (item < (int)levels.size()) + pickedLevel = levels[item]; + } + } +} + +bool TouchWorldSelectionList::isSelectedItem( int item ) { + return item == selectedItem; +} + +void TouchWorldSelectionList::selectStart(int item, int localX, int localY) { + if (selectedItem != (int) levels.size() || item != selectedItem) + return; + _newWorldSelected = true; +} + +void TouchWorldSelectionList::selectCancel() { + _newWorldSelected = false; +} + +void TouchWorldSelectionList::renderItem( int i, int x, int y, int h, Tesselator& t ) { + int centerx = x + itemWidth/2; + float a0 = Mth::Max(1.1f - std::abs( width / 2 - centerx ) * 0.0055f, 0.2f); + if (a0 > 1) a0 = 1; + int textColor = (int)(255.0f * a0) * 0x010101; + int textColor2 = (int)(140.0f * a0) * 0x010101; + const int TX = centerx - itemWidth / 2 + 5; + const int TY = y + 44; //@kindle-res:42 + + if (i < (int)levels.size()) { + // Draw the worlds + StringVector v = _descriptions[i]; + drawString(minecraft->font, v[0].c_str(), TX, TY + 0, textColor); + drawString(minecraft->font, v[1].c_str(), TX, TY + 10, textColor2); + drawString(minecraft->font, v[2].c_str(), TX, TY + 20, textColor2); + drawString(minecraft->font, v[3].c_str(), TX, TY + 30, textColor2); + + minecraft->textures->loadAndBindTexture(_imageNames[i]); + t.color(0.3f, 1.0f, 0.2f); + + //float x0 = (float)x; + //float x1 = (float)x + (float)itemWidth; + + const float IY = (float)y - 8; // @kindle-res: -3 + t.begin(); + t.color(textColor); + t.vertexUV((float)(centerx-32), IY, blitOffset, 0, 0.125f); + t.vertexUV((float)(centerx-32), IY + 48, blitOffset, 0, 0.875f); //@kindle-res: +44 + t.vertexUV((float)(centerx+32), IY + 48, blitOffset, 1, 0.875f); //@kindle-res: +44 + t.vertexUV((float)(centerx+32), IY, blitOffset, 1, 0.125f); + t.draw(); + } else { + // Draw the "Create new world" icon + drawCenteredString(minecraft->font, "Create new", centerx, TY + 12, textColor); + + minecraft->textures->loadAndBindTexture("gui/touchgui.png"); + + const bool selected = _newWorldSelected; + + const float W = 54.0f; + const float H = 54.0f; + const float IY = (float)y; + const float u0 = (168.0f ) / 256.0f; + const float u1 = (168.0f + W) / 256.0f; + float v0 = (32.0f ) / 256.0f; + float v1 = (32.0f + H) / 256.0f; + if (selected) { + v0 += H / 256.0f; + v1 += H / 256.0f; + } + + t.begin(); + t.color(textColor); + t.vertexUV((float)centerx - W*0.5f, IY, blitOffset, u0, v0); + t.vertexUV((float)centerx - W*0.5f, IY + H, blitOffset, u0, v1); + t.vertexUV((float)centerx + W*0.5f, IY + H, blitOffset, u1, v1); + t.vertexUV((float)centerx + W*0.5f, IY, blitOffset, u1, v0); + t.draw(); + } +} + +void TouchWorldSelectionList::stepLeft() { + if (selectedItem > 0) { + int xoffset = (int)(xo - ((float)(selectedItem * itemWidth) + ((float)(itemWidth-width)) * 0.5f)); + td.start = xo; + td.stop = xo - itemWidth - xoffset; + td.cur = 0; + td.dur = 8; + mode = 1; + tweenInited(); + } +} + +void TouchWorldSelectionList::stepRight() { + if (selectedItem >= 0 && selectedItem < getNumberOfItems()-1) { + int xoffset = (int)(xo - ((float)(selectedItem * itemWidth) + ((float)(itemWidth-width)) * 0.5f)); + td.start = xo; + td.stop = xo + itemWidth - xoffset; + td.cur = 0; + td.dur = 8; + mode = 1; + tweenInited(); + } +} + +void TouchWorldSelectionList::commit() { + for (unsigned int i = 0; i < levels.size(); ++i) { + LevelSummary& level = levels[i]; + + std::stringstream ss; + ss << level.name << "/preview.png"; + TextureId id = Textures::InvalidId;//minecraft->textures->loadTexture(ss.str(), false); + + if (id != Textures::InvalidId) { + _imageNames.push_back( ss.str() ); + } else { + _imageNames.push_back("gui/default_world.png"); + } + + StringVector lines; + lines.push_back(levels[i].name); + lines.push_back(minecraft->platform()->getDateString(levels[i].lastPlayed)); + lines.push_back(levels[i].id); + lines.push_back(LevelSettings::gameTypeToString(level.gameType)); + _descriptions.push_back(lines); + + selectedItem = 0; + } +} + +static float quadraticInOut(float t, float dur, float start, float stop) { + const float delta = stop - start; + const float T = (t / dur) * 2.0f; + if (T < 1) return 0.5f*delta*T*T + start; + return -0.5f*delta * ((T-1)*(T-3) - 1) + start; +} + +void TouchWorldSelectionList::tick() +{ + RolledSelectionListH::tick(); + + ++currentTick; + + if (Mouse::isButtonDown(MouseAction::ACTION_LEFT) || dragState == 0) + return; + + // Handle the tween (when in "mode 1") + selectedItem = -1; + if (mode == 1) { + if (++td.cur == td.dur) { + mode = 0; + xInertia = 0; + xoo = xo = td.stop; + selectedItem = getItemAtPosition(width/2, height/2); + } else { + tweenInited(); + } + return; + } + + // It's still going fast, let it run + float speed = Mth::abs(xInertia); + bool slowEnoughToBeBothered = speed < 5.0f; + if (!slowEnoughToBeBothered) { + xInertia = xInertia * .9f; + return; + } + + xInertia *= 0.8f; + + if (speed < 1 && dragState < 0) { + const int offsetx = (width-itemWidth) / 2; + const float pxo = xo + offsetx; + int index = getItemAtXPositionRaw((int)(pxo - 10*xInertia)); + int indexPos = index*itemWidth; + + // Pick closest + float diff = (float)indexPos - pxo; + if (diff < -itemWidth/2) { + diff += itemWidth; + index++; + //indexPos += itemWidth; + } + if (Mth::abs(diff) < 1 && speed < 0.1f) { + selectedItem = getItemAtPosition(width/2, height/2); + return; + } + + td.start = xo; + td.stop = xo + diff; + td.cur = 0; + td.dur = (float) Mth::Min(7, 1 + (int)(Mth::abs(diff) * 0.25f)); + mode = 1; + //LOGI("inited-t %d\n", dragState); + tweenInited(); + } +} + +float TouchWorldSelectionList::getPos( float alpha ) +{ + if (mode != 1) return RolledSelectionListH::getPos(alpha); + + float x0 = quadraticInOut(td.cur, td.dur, td.start, td.stop); + float x1 = quadraticInOut(td.cur+1, td.dur, td.start, td.stop); + return x0 + (x1-x0)*alpha; +} + +bool TouchWorldSelectionList::capXPosition() { + bool capped = RolledSelectionListH::capXPosition(); + if (capped) mode = 0; + return capped; +} + +void TouchWorldSelectionList::tweenInited() { + float x0 = quadraticInOut(td.cur, td.dur, td.start, td.stop); + float x1 = quadraticInOut(td.cur+1, td.dur, td.start, td.stop); + _xinertia = 0; + xInertia = x0-x1; // yes, it's all backwards and messed up.. +} + +// +// Select World Screen +// +SelectWorldScreen::SelectWorldScreen() +: bDelete (1, ""), + bCreate (2, "Create new"), + bBack (3, "Back"), + bHeader (0, "Select world"), + bWorldView(4, ""), + worldsList(NULL), + _hasStartedLevel(false) +{ + bDelete.active = false; + + // Delete button + ImageDef def; + def.name = "gui/touchgui.png"; + def.width = 34; + def.height = 26; + + def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); + bDelete.setImageDef(def, true); + + // Create new, and Back button +/* + def.src.y = 26; // @ 0, 26 + def.src.w = def.w = 66; // 66, 26 size + bBack.setImageDef(def, true); + bCreate.setImageDef(def, true); +*/ +} + +SelectWorldScreen::~SelectWorldScreen() +{ + delete worldsList; +} + +void SelectWorldScreen::init() +{ + worldsList = new TouchWorldSelectionList(minecraft, width, height); + loadLevelSource(); + worldsList->commit(); + + buttons.push_back(&bDelete); + buttons.push_back(&bCreate); + buttons.push_back(&bBack); + buttons.push_back(&bHeader); + + _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); + + tabButtons.push_back(&bWorldView); + tabButtons.push_back(&bDelete); + tabButtons.push_back(&bCreate); + tabButtons.push_back(&bBack); +} + +void SelectWorldScreen::setupPositions() { + //#ifdef ANDROID + bCreate.y = 0; + bBack.y = 0; + bHeader.y = 0; + bDelete.y = height - 30; + + // Center buttons + bDelete.x = (width - bDelete.width) / 2; + bCreate.x = width - bCreate.width;//width / 2 - bCreate.w / 2; + bBack.x = 0;//width / 2 + 4 + bCreate.w - bBack.w / 2; + bHeader.x = bBack.width; + bHeader.width = width - (bBack.width + bCreate.width); + bHeader.height = bCreate.height; +} + +void SelectWorldScreen::buttonClicked(Button* button) +{ + if (button->id == bCreate.id) { + if (!_hasStartedLevel) { + std::string name = getUniqueLevelName("World"); + minecraft->setScreen(new SimpleChooseLevelScreen(name)); + } + } + if (button->id == bDelete.id) { + if (isIndexValid(worldsList->selectedItem)) { + LevelSummary level = worldsList->levels[worldsList->selectedItem]; + LOGI("level: %s, %s\n", level.id.c_str(), level.name.c_str()); + minecraft->setScreen( new TouchDeleteWorldScreen(level) ); + } + } + if (button->id == bBack.id) { + minecraft->cancelLocateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } + if (button->id == bWorldView.id) { + // Try to "click" the item in the middle + worldsList->selectItem( worldsList->getItemAtPosition(width/2, height/2), false ); + } +} + +bool SelectWorldScreen::handleBackEvent(bool isDown) +{ + if (!isDown) + { + minecraft->cancelLocateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_STARTMENU); + } + return true; +} + +bool SelectWorldScreen::isIndexValid( int index ) +{ + return worldsList && index >= 0 && index < worldsList->getNumberOfItems() - 1; +} + +static char ILLEGAL_FILE_CHARACTERS[] = { + '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' +}; + +void SelectWorldScreen::mouseWheel(int dx, int dy, int xm, int ym) +{ + if (!worldsList) return; + if (dy == 0) return; + int num = worldsList->getNumberOfItems(); + int idx = worldsList->selectedItem; + if (dy > 0) { + if (idx > 0) { + idx--; + worldsList->stepLeft(); + } + } else { + if (idx < num - 1) { + idx++; + worldsList->stepRight(); + } + } + worldsList->selectedItem = idx; +} + +void SelectWorldScreen::tick() +{ +#if 0 + if (_state == _STATE_CREATEWORLD) { + #if defined(RPI) + std::string levelId = getUniqueLevelName("World"); + //int seed = Util::hashCode("/r/Minecraft"); + LevelSettings settings(getEpochTimeS(), GameType::Creative); + minecraft->selectLevel(levelId, levelId, settings); + minecraft->hostMultiplayer(); + minecraft->setScreen(new ProgressScreen()); + _hasStartedLevel = true; + #elif defined(PLATFORM_DESKTOP) + std::string name = getUniqueLevelName("World"); + minecraft->setScreen(new SimpleChooseLevelScreen(name)); + #else + int status = minecraft->platform()->getUserInputStatus(); + //LOGI("Status is: %d\n", status); + if (status > -1) { + if (status == 1) { + StringVector sv = minecraft->platform()->getUserInput(); + + // Read the level name. + // 1) Trim name 2) Remove all bad chars 3) Append '-' chars 'til the name is unique + std::string levelName = Util::stringTrim(sv[0]); + std::string levelId = levelName; + + for (int i = 0; i < sizeof(ILLEGAL_FILE_CHARACTERS) / sizeof(char); ++i) + levelId = Util::stringReplace(levelId, std::string(1, ILLEGAL_FILE_CHARACTERS[i]), ""); + if ((int)levelId.length() == 0) { + levelId = "no_name"; + } + levelId = getUniqueLevelName(levelId); + + // Read the seed + int seed = getEpochTimeS(); + if (sv.size() >= 2) { + std::string seedString = Util::stringTrim(sv[1]); + if (seedString.length() > 0) { + int tmpSeed; + // Try to read it as an integer + if (sscanf(seedString.c_str(), "%d", &tmpSeed) > 0) { + seed = tmpSeed; + } // Hash the "seed" + else { + seed = Util::hashCode(seedString); + } + } + } + // Read the game mode + bool isCreative = true; + if (sv.size() >= 3 && sv[2] == "survival") + isCreative = false; + + // Start a new level with the given name and seed + LOGI("Creating a level with id '%s', name '%s' and seed '%d'\n", levelId.c_str(), levelName.c_str(), seed); + LevelSettings settings(seed, isCreative? GameType::Creative : GameType::Survival); + minecraft->selectLevel(levelId, levelName, settings); + minecraft->hostMultiplayer(); + minecraft->setScreen(new ProgressScreen()); + _hasStartedLevel = true; + } + _state = _STATE_DEFAULT; + // Reset the world list + worldsList->hasPickedLevel = false; + worldsList->pickedIndex = -1; + } + #endif + + worldsList->hasPickedLevel = false; + return; + } +#endif + + worldsList->tick(); + + if (worldsList->hasPickedLevel) { + if (worldsList->pickedIndex == worldsList->levels.size()) { + worldsList->hasPickedLevel = false; + std::string name = getUniqueLevelName("World"); + minecraft->setScreen(new SimpleChooseLevelScreen(name)); + } else { + minecraft->selectLevel(worldsList->pickedLevel.id, worldsList->pickedLevel.name, LevelSettings::None()); + minecraft->hostMultiplayer(); + minecraft->setScreen(new ProgressScreen()); + _hasStartedLevel = true; + return; + } + } + + // copy the currently selected item + LevelSummary selectedWorld; + //bool hasSelection = false; + if (isIndexValid(worldsList->selectedItem)) + { + selectedWorld = worldsList->levels[worldsList->selectedItem]; + //hasSelection = true; + } + + bDelete.active = isIndexValid(worldsList->selectedItem); +} + +void SelectWorldScreen::render( int xm, int ym, float a ) +{ + //Performance::watches.get("sws-full").start(); + //Performance::watches.get("sws-renderbg").start(); + renderBackground(); + //Performance::watches.get("sws-renderbg").stop(); + //Performance::watches.get("sws-worlds").start(); + + worldsList->setComponentSelected(bWorldView.selected); + + if (_mouseHasBeenUp) + worldsList->render(xm, ym, a); + else { + worldsList->render(0, 0, a); + _mouseHasBeenUp = !Mouse::getButtonState(MouseAction::ACTION_LEFT); + } + + //Performance::watches.get("sws-worlds").stop(); + //Performance::watches.get("sws-screen").start(); + Screen::render(xm, ym, a); + //Performance::watches.get("sws-screen").stop(); + + //minecraft->textures->loadAndBindTexture("gui/selectworld/trash.png"); + + //Performance::watches.get("sws-string").start(); + //Performance::watches.get("sws-string").stop(); + + //Performance::watches.get("sws-full").stop(); + //Performance::watches.printEvery(128); +} + +void SelectWorldScreen::loadLevelSource() +{ + LevelStorageSource* levelSource = minecraft->getLevelSource(); + levelSource->getLevelList(levels); + std::sort(levels.begin(), levels.end()); + + for (unsigned int i = 0; i < levels.size(); ++i) { + if (levels[i].id != LevelStorageSource::TempLevelId) + worldsList->levels.push_back( levels[i] ); + } +} + + +std::string SelectWorldScreen::getUniqueLevelName( const std::string& level ) +{ + std::set Set; + for (unsigned int i = 0; i < levels.size(); ++i) + Set.insert(levels[i].id); + + std::string s = level; + while ( Set.find(s) != Set.end() ) + s += "-"; + return s; +} + +bool SelectWorldScreen::isInGameScreen() { return true; } + +void SelectWorldScreen::keyPressed( int eventKey ) +{ + if (bWorldView.selected) { + if (eventKey == minecraft->options.getIntValue(OPTIONS_KEY_LEFT)) + worldsList->stepLeft(); + if (eventKey == minecraft->options.getIntValue(OPTIONS_KEY_RIGHT)) + worldsList->stepRight(); + } + + Screen::keyPressed(eventKey); +} + +// +// Delete World Screen +// +TouchDeleteWorldScreen::TouchDeleteWorldScreen(const LevelSummary& level) +: ConfirmScreen(NULL, "Are you sure you want to delete this world?", + "'" + level.name + "' will be lost forever!", + "Delete", "Cancel", 0), + _level(level) +{ + tabButtonIndex = 1; +} + +void TouchDeleteWorldScreen::postResult( bool isOk ) +{ + if (isOk) { + LevelStorageSource* storageSource = minecraft->getLevelSource(); + storageSource->deleteLevel(_level.id); + } + minecraft->screenChooser.setScreen(SCREEN_SELECTWORLD); +} + +}; diff --git a/src/client/gui/screens/touch/TouchSelectWorldScreen.h b/src/client/gui/screens/touch/TouchSelectWorldScreen.h index 525116b..820e022 100755 --- a/src/client/gui/screens/touch/TouchSelectWorldScreen.h +++ b/src/client/gui/screens/touch/TouchSelectWorldScreen.h @@ -1,122 +1,122 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchSelectWorldScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchSelectWorldScreen_H__ - -#include "../ConfirmScreen.h" -#include "../../Screen.h" -#include "../../TweenData.h" -#include "../../components/ImageButton.h" -#include "../../components/Button.h" -#include "../../components/RolledSelectionListH.h" -#include "../../../Minecraft.h" -#include "../../../../world/level/storage/LevelStorageSource.h" - - -namespace Touch { - -class SelectWorldScreen; - -// -// Scrolling World selection list -// -class TouchWorldSelectionList : public RolledSelectionListH -{ -public: - TouchWorldSelectionList(Minecraft* _minecraft, int _width, int _height); - virtual void tick(); - void stepLeft(); - void stepRight(); - - void commit(); -protected: - virtual int getNumberOfItems(); - virtual void selectItem(int item, bool doubleClick); - virtual bool isSelectedItem(int item); - - virtual void renderBackground() {} - virtual void renderItem(int i, int x, int y, int h, Tesselator& t); - virtual float getPos(float alpha); - virtual void touched() { mode = 0; } - virtual bool capXPosition(); - - virtual void selectStart(int item, int localX, int localY); - virtual void selectCancel(); -private: - TweenData td; - void tweenInited(); - - int selectedItem; - bool _newWorldSelected; // Is the PLUS button pressed? - int _height; - LevelSummaryList levels; - std::vector _descriptions; - StringVector _imageNames; - - bool hasPickedLevel; - LevelSummary pickedLevel; - int pickedIndex; - - int stoppedTick; - int currentTick; - float accRatio; - int mode; - - friend class SelectWorldScreen; -}; - -// -// Delete World screen -// -class TouchDeleteWorldScreen: public ConfirmScreen -{ -public: - TouchDeleteWorldScreen(const LevelSummary& levelId); -protected: - virtual void postResult(bool isOk); -private: - LevelSummary _level; -}; - - -// -// Select world screen -// -class SelectWorldScreen: public Screen -{ -public: - SelectWorldScreen(); - virtual ~SelectWorldScreen(); - - virtual void init() override; - virtual void setupPositions() override; - - virtual void tick() override; - virtual void render(int xm, int ym, float a) override; - - virtual bool isIndexValid(int index); - virtual bool handleBackEvent(bool isDown) override; - virtual void buttonClicked(Button* button) override; - virtual void keyPressed(int eventKey) override; - - // support for mouse wheel when desktop code uses touch variant - virtual void mouseWheel(int dx, int dy, int xm, int ym) override; - - bool isInGameScreen() override; -private: - void loadLevelSource(); - std::string getUniqueLevelName(const std::string& level); - - ImageButton bDelete; - TButton bCreate; - THeader bHeader; - TButton bBack; - Button bWorldView; - TouchWorldSelectionList* worldsList; - LevelSummaryList levels; - - bool _mouseHasBeenUp; - bool _hasStartedLevel; - //LevelStorageSource* levels; -}; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchSelectWorldScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchSelectWorldScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchSelectWorldScreen_H__ + +#include "../ConfirmScreen.h" +#include "../../Screen.h" +#include "../../TweenData.h" +#include "../../components/ImageButton.h" +#include "../../components/Button.h" +#include "../../components/RolledSelectionListH.h" +#include "../../../Minecraft.h" +#include "../../../../world/level/storage/LevelStorageSource.h" + + +namespace Touch { + +class SelectWorldScreen; + +// +// Scrolling World selection list +// +class TouchWorldSelectionList : public RolledSelectionListH +{ +public: + TouchWorldSelectionList(Minecraft* _minecraft, int _width, int _height); + virtual void tick(); + void stepLeft(); + void stepRight(); + + void commit(); +protected: + virtual int getNumberOfItems(); + virtual void selectItem(int item, bool doubleClick); + virtual bool isSelectedItem(int item); + + virtual void renderBackground() {} + virtual void renderItem(int i, int x, int y, int h, Tesselator& t); + virtual float getPos(float alpha); + virtual void touched() { mode = 0; } + virtual bool capXPosition(); + + virtual void selectStart(int item, int localX, int localY); + virtual void selectCancel(); +private: + TweenData td; + void tweenInited(); + + int selectedItem; + bool _newWorldSelected; // Is the PLUS button pressed? + int _height; + LevelSummaryList levels; + std::vector _descriptions; + StringVector _imageNames; + + bool hasPickedLevel; + LevelSummary pickedLevel; + int pickedIndex; + + int stoppedTick; + int currentTick; + float accRatio; + int mode; + + friend class SelectWorldScreen; +}; + +// +// Delete World screen +// +class TouchDeleteWorldScreen: public ConfirmScreen +{ +public: + TouchDeleteWorldScreen(const LevelSummary& levelId); +protected: + virtual void postResult(bool isOk); +private: + LevelSummary _level; +}; + + +// +// Select world screen +// +class SelectWorldScreen: public Screen +{ +public: + SelectWorldScreen(); + virtual ~SelectWorldScreen(); + + virtual void init() override; + virtual void setupPositions() override; + + virtual void tick() override; + virtual void render(int xm, int ym, float a) override; + + virtual bool isIndexValid(int index); + virtual bool handleBackEvent(bool isDown) override; + virtual void buttonClicked(Button* button) override; + virtual void keyPressed(int eventKey) override; + + // support for mouse wheel when desktop code uses touch variant + virtual void mouseWheel(int dx, int dy, int xm, int ym) override; + + bool isInGameScreen() override; +private: + void loadLevelSource(); + std::string getUniqueLevelName(const std::string& level); + + ImageButton bDelete; + TButton bCreate; + THeader bHeader; + TButton bBack; + Button bWorldView; + TouchWorldSelectionList* worldsList; + LevelSummaryList levels; + + bool _mouseHasBeenUp; + bool _hasStartedLevel; + //LevelStorageSource* levels; +}; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchSelectWorldScreen_H__*/ diff --git a/src/client/gui/screens/touch/TouchStartMenuScreen.cpp b/src/client/gui/screens/touch/TouchStartMenuScreen.cpp index c80300e..9be4269 100755 --- a/src/client/gui/screens/touch/TouchStartMenuScreen.cpp +++ b/src/client/gui/screens/touch/TouchStartMenuScreen.cpp @@ -1,230 +1,230 @@ -#include "TouchStartMenuScreen.h" -#include "../ProgressScreen.h" -#include "../OptionsScreen.h" -#include "../PauseScreen.h" - -#include "../../Font.h" -#include "../../components/ScrolledSelectionList.h" -#include "../../components/GuiElement.h" - -#include "../../../Minecraft.h" -#include "../../../renderer/Tesselator.h" -#include "../../../renderer/Textures.h" -#include "../../../renderer/TextureData.h" -#include "../../../../SharedConstants.h" -#include "../../../../AppPlatform.h" -#include "../../../../LicenseCodes.h" -#include "../../../../util/Mth.h" - -#include "../DialogDefinitions.h" -#include "../SimpleChooseLevelScreen.h" - -namespace Touch { - -// -// Start menu screen implementation -// - -// Some kind of default settings, might be overridden in ::init -StartMenuScreen::StartMenuScreen() -: bHost( 2, "Start Game"), - bJoin( 3, "Join Game"), - bOptions( 4, "Options"), - bQuit( 5, "") -{ - ImageDef def; - bJoin.width = 75; - def.width = def.height = (float) bJoin.width; - - def.setSrc(IntRectangle(0, 26, (int)def.width, (int)def.width)); - def.name = "gui/touchgui.png"; - IntRectangle& defSrc = *def.getSrc(); - - bOptions.setImageDef(def, true); - - defSrc.y += defSrc.h; - bHost.setImageDef(def, true); - - defSrc.y += defSrc.h; - bJoin.setImageDef(def, true); -} - -StartMenuScreen::~StartMenuScreen() -{ -} - -void StartMenuScreen::init() -{ - buttons.push_back(&bHost); - buttons.push_back(&bJoin); - buttons.push_back(&bOptions); - - // add quit icon (same look as options header) - { - ImageDef def; - def.name = "gui/touchgui.png"; - def.width = 34; - def.height = 26; - def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); - bQuit.setImageDef(def, true); - bQuit.scaleWhenPressed = false; - buttons.push_back(&bQuit); - } - - tabButtons.push_back(&bHost); - tabButtons.push_back(&bJoin); - tabButtons.push_back(&bOptions); - - #ifdef DEMO_MODE - buttons.push_back(&bBuy); - tabButtons.push_back(&bBuy); - #endif - - copyright = "\xffMojang AB";//. Do not distribute!"; - - // always show base version string - std::string versionString = Common::getGameVersionString(); - - std::string _username = minecraft->options.getStringValue(OPTIONS_USERNAME); - if (_username.empty()) _username = "unknown"; - - username = "Username: " + _username; - - #ifdef DEMO_MODE - #ifdef __APPLE__ - version = versionString + " (Lite)"; - #else - version = versionString + " (Demo)"; - #endif - #else - version = versionString; - #endif - - #ifdef APPLE_DEMO_PROMOTION - version = versionString + " (Demo)"; - #endif - - bJoin.active = bHost.active = bOptions.active = true; -} - -void StartMenuScreen::setupPositions() { - int yBase = 2 + height / 3; - int buttonWidth = bHost.width; - float spacing = (width - (3.0f * buttonWidth)) / 4; - - //#ifdef ANDROID - bHost.y = yBase; - bJoin.y = yBase; - bOptions.y = yBase; - //#endif - - // Center buttons - bJoin.x = 0*buttonWidth + (int)(1*spacing); - bHost.x = 1*buttonWidth + (int)(2*spacing); - bOptions.x = 2*buttonWidth + (int)(3*spacing); - - // quit icon top-right (use size assigned in init) - bQuit.x = width - bQuit.width; - bQuit.y = 0; - - copyrightPosX = width - minecraft->font->width(copyright) - 1; - versionPosX = (width - minecraft->font->width(version)) / 2;// - minecraft->font->width(version) - 2; -} - -void StartMenuScreen::buttonClicked(::Button* button) { - - if (button->id == bHost.id) - { - #if defined(DEMO_MODE) || defined(APPLE_DEMO_PROMOTION) - minecraft->setScreen( new SimpleChooseLevelScreen("_DemoLevel") ); - #else - minecraft->screenChooser.setScreen(SCREEN_SELECTWORLD); - #endif - } - if (button->id == bJoin.id) - { - #ifdef APPLE_DEMO_PROMOTION - minecraft->platform()->createUserInput(DialogDefinitions::DIALOG_DEMO_FEATURE_DISABLED); - #else - minecraft->locateMultiplayer(); - minecraft->screenChooser.setScreen(SCREEN_JOINGAME); - #endif - } - if (button->id == bOptions.id) - { - minecraft->setScreen(new OptionsScreen()); - } - if (button == &bQuit) - { - minecraft->quit(); - } -} - -bool StartMenuScreen::isInGameScreen() { return false; } - -void StartMenuScreen::render( int xm, int ym, float a ) -{ - renderBackground(); - - // Show current username in the top-left corner - drawString(font, username, 2, 2, 0xffffffff); - - glEnable2(GL_BLEND); - -#if defined(RPI) - TextureId id = minecraft->textures->loadTexture("gui/pi_title.png"); -#else - TextureId id = minecraft->textures->loadTexture("gui/title.png"); -#endif - const TextureData* data = minecraft->textures->getTemporaryTextureData(id); - - if (data) { - minecraft->textures->bind(id); - - const float x = (float)width / 2; - const float y = 4; - const float wh = 0.5f * Mth::Min((float)width/2.0f, (float)data->w / 2); - const float scale = 2.0f * wh / (float)data->w; - const float h = scale * (float)data->h; - - // Render title text - Tesselator& t = Tesselator::instance; - glColor4f2(1, 1, 1, 1); - t.begin(); - t.vertexUV(x-wh, y+h, blitOffset, 0, 1); - t.vertexUV(x+wh, y+h, blitOffset, 1, 1); - t.vertexUV(x+wh, y+0, blitOffset, 1, 0); - t.vertexUV(x-wh, y+0, blitOffset, 0, 0); - t.draw(); - - drawString(font, version, versionPosX, (int)(y+h)+2, /*50,*/ 0xffcccccc);//0x666666); - drawString(font, copyright, copyrightPosX, height - 10, 0xffffff); - glColor4f2(1, 1, 1, 1); - if (Textures::isTextureIdValid(minecraft->textures->loadAndBindTexture("gui/logo/github.png"))) - blit(2, height - 10, 0, 0, 8, 8, 256, 256); - drawString(font, "Kolyah35/minecraft-pe-0.6.1", 12, height - 10, 0xffcccccc); - //patch->draw(t, 0, 20); - } - Screen::render(xm, ym, a); - glDisable2(GL_BLEND); -} - - -void StartMenuScreen::mouseClicked(int x, int y, int buttonNum) { - const int logoX = 2; - const int logoW = 8 + 2 + font->width("Kolyah35/minecraft-pe-0.6.1"); - const int logoY = height - 10; - const int logoH = 10; - if (x >= logoX && x <= logoX + logoW && y >= logoY && y <= logoY + logoH) - minecraft->platform()->openURL("https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1"); - else - Screen::mouseClicked(x, y, buttonNum); -} - -bool StartMenuScreen::handleBackEvent( bool isDown ) { - minecraft->quit(); - return true; -} - -} // namespace Touch - +#include "TouchStartMenuScreen.h" +#include "../ProgressScreen.h" +#include "../OptionsScreen.h" +#include "../PauseScreen.h" + +#include "../../Font.h" +#include "../../components/ScrolledSelectionList.h" +#include "../../components/GuiElement.h" + +#include "../../../Minecraft.h" +#include "../../../renderer/Tesselator.h" +#include "../../../renderer/Textures.h" +#include "../../../renderer/TextureData.h" +#include "../../../../SharedConstants.h" +#include "../../../../AppPlatform.h" +#include "../../../../LicenseCodes.h" +#include "../../../../util/Mth.h" + +#include "../DialogDefinitions.h" +#include "../SimpleChooseLevelScreen.h" + +namespace Touch { + +// +// Start menu screen implementation +// + +// Some kind of default settings, might be overridden in ::init +StartMenuScreen::StartMenuScreen() +: bHost( 2, "Start Game"), + bJoin( 3, "Join Game"), + bOptions( 4, "Options"), + bQuit( 5, "") +{ + ImageDef def; + bJoin.width = 75; + def.width = def.height = (float) bJoin.width; + + def.setSrc(IntRectangle(0, 26, (int)def.width, (int)def.width)); + def.name = "gui/touchgui.png"; + IntRectangle& defSrc = *def.getSrc(); + + bOptions.setImageDef(def, true); + + defSrc.y += defSrc.h; + bHost.setImageDef(def, true); + + defSrc.y += defSrc.h; + bJoin.setImageDef(def, true); +} + +StartMenuScreen::~StartMenuScreen() +{ +} + +void StartMenuScreen::init() +{ + buttons.push_back(&bHost); + buttons.push_back(&bJoin); + buttons.push_back(&bOptions); + + // add quit icon (same look as options header) + { + ImageDef def; + def.name = "gui/touchgui.png"; + def.width = 34; + def.height = 26; + def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height)); + bQuit.setImageDef(def, true); + bQuit.scaleWhenPressed = false; + buttons.push_back(&bQuit); + } + + tabButtons.push_back(&bHost); + tabButtons.push_back(&bJoin); + tabButtons.push_back(&bOptions); + + #ifdef DEMO_MODE + buttons.push_back(&bBuy); + tabButtons.push_back(&bBuy); + #endif + + copyright = "\xffMojang AB";//. Do not distribute!"; + + // always show base version string + std::string versionString = Common::getGameVersionString(); + + std::string _username = minecraft->options.getStringValue(OPTIONS_USERNAME); + if (_username.empty()) _username = "unknown"; + + username = "Username: " + _username; + + #ifdef DEMO_MODE + #ifdef __APPLE__ + version = versionString + " (Lite)"; + #else + version = versionString + " (Demo)"; + #endif + #else + version = versionString; + #endif + + #ifdef APPLE_DEMO_PROMOTION + version = versionString + " (Demo)"; + #endif + + bJoin.active = bHost.active = bOptions.active = true; +} + +void StartMenuScreen::setupPositions() { + int yBase = 2 + height / 3; + int buttonWidth = bHost.width; + float spacing = (width - (3.0f * buttonWidth)) / 4; + + //#ifdef ANDROID + bHost.y = yBase; + bJoin.y = yBase; + bOptions.y = yBase; + //#endif + + // Center buttons + bJoin.x = 0*buttonWidth + (int)(1*spacing); + bHost.x = 1*buttonWidth + (int)(2*spacing); + bOptions.x = 2*buttonWidth + (int)(3*spacing); + + // quit icon top-right (use size assigned in init) + bQuit.x = width - bQuit.width; + bQuit.y = 0; + + copyrightPosX = width - minecraft->font->width(copyright) - 1; + versionPosX = (width - minecraft->font->width(version)) / 2;// - minecraft->font->width(version) - 2; +} + +void StartMenuScreen::buttonClicked(::Button* button) { + + if (button->id == bHost.id) + { + #if defined(DEMO_MODE) || defined(APPLE_DEMO_PROMOTION) + minecraft->setScreen( new SimpleChooseLevelScreen("_DemoLevel") ); + #else + minecraft->screenChooser.setScreen(SCREEN_SELECTWORLD); + #endif + } + if (button->id == bJoin.id) + { + #ifdef APPLE_DEMO_PROMOTION + minecraft->platform()->createUserInput(DialogDefinitions::DIALOG_DEMO_FEATURE_DISABLED); + #else + minecraft->locateMultiplayer(); + minecraft->screenChooser.setScreen(SCREEN_JOINGAME); + #endif + } + if (button->id == bOptions.id) + { + minecraft->setScreen(new OptionsScreen()); + } + if (button == &bQuit) + { + minecraft->quit(); + } +} + +bool StartMenuScreen::isInGameScreen() { return false; } + +void StartMenuScreen::render( int xm, int ym, float a ) +{ + renderBackground(); + + // Show current username in the top-left corner + drawString(font, username, 2, 2, 0xffffffff); + + glEnable2(GL_BLEND); + +#if defined(RPI) + TextureId id = minecraft->textures->loadTexture("gui/pi_title.png"); +#else + TextureId id = minecraft->textures->loadTexture("gui/title.png"); +#endif + const TextureData* data = minecraft->textures->getTemporaryTextureData(id); + + if (data) { + minecraft->textures->bind(id); + + const float x = (float)width / 2; + const float y = 4; + const float wh = 0.5f * Mth::Min((float)width/2.0f, (float)data->w / 2); + const float scale = 2.0f * wh / (float)data->w; + const float h = scale * (float)data->h; + + // Render title text + Tesselator& t = Tesselator::instance; + glColor4f2(1, 1, 1, 1); + t.begin(); + t.vertexUV(x-wh, y+h, blitOffset, 0, 1); + t.vertexUV(x+wh, y+h, blitOffset, 1, 1); + t.vertexUV(x+wh, y+0, blitOffset, 1, 0); + t.vertexUV(x-wh, y+0, blitOffset, 0, 0); + t.draw(); + + drawString(font, version, versionPosX, (int)(y+h)+2, /*50,*/ 0xffcccccc);//0x666666); + drawString(font, copyright, copyrightPosX, height - 10, 0xffffff); + glColor4f2(1, 1, 1, 1); + if (Textures::isTextureIdValid(minecraft->textures->loadAndBindTexture("gui/logo/github.png"))) + blit(2, height - 10, 0, 0, 8, 8, 256, 256); + drawString(font, "Kolyah35/minecraft-pe-0.6.1", 12, height - 10, 0xffcccccc); + //patch->draw(t, 0, 20); + } + Screen::render(xm, ym, a); + glDisable2(GL_BLEND); +} + + +void StartMenuScreen::mouseClicked(int x, int y, int buttonNum) { + const int logoX = 2; + const int logoW = 8 + 2 + font->width("Kolyah35/minecraft-pe-0.6.1"); + const int logoY = height - 10; + const int logoH = 10; + if (x >= logoX && x <= logoX + logoW && y >= logoY && y <= logoY + logoH) + minecraft->platform()->openURL("https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1"); + else + Screen::mouseClicked(x, y, buttonNum); +} + +bool StartMenuScreen::handleBackEvent( bool isDown ) { + minecraft->quit(); + return true; +} + +} // namespace Touch + diff --git a/src/client/gui/screens/touch/TouchStartMenuScreen.h b/src/client/gui/screens/touch/TouchStartMenuScreen.h index b4c8755..7816c58 100755 --- a/src/client/gui/screens/touch/TouchStartMenuScreen.h +++ b/src/client/gui/screens/touch/TouchStartMenuScreen.h @@ -1,43 +1,43 @@ -#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchStartMenuScreen_H__ -#define NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchStartMenuScreen_H__ - -#include "../../Screen.h" -#include "../../components/LargeImageButton.h" -#include "../../components/ImageButton.h" -#include "../../components/TextBox.h" - -namespace Touch { - -class StartMenuScreen: public Screen -{ -public: - StartMenuScreen(); - virtual ~StartMenuScreen(); - - void init(); - void setupPositions(); - - void render(int xm, int ym, float a); - - void buttonClicked(Button* button); - virtual void mouseClicked(int x, int y, int buttonNum); - bool handleBackEvent(bool isDown); - bool isInGameScreen(); -private: - - LargeImageButton bHost; - LargeImageButton bJoin; - LargeImageButton bOptions; - ImageButton bQuit; // X close icon - - std::string copyright; - int copyrightPosX; - - std::string version; - int versionPosX; - - std::string username; -}; -}; - -#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchStartMenuScreen_H__*/ +#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchStartMenuScreen_H__ +#define NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchStartMenuScreen_H__ + +#include "../../Screen.h" +#include "../../components/LargeImageButton.h" +#include "../../components/ImageButton.h" +#include "../../components/TextBox.h" + +namespace Touch { + +class StartMenuScreen: public Screen +{ +public: + StartMenuScreen(); + virtual ~StartMenuScreen(); + + void init(); + void setupPositions(); + + void render(int xm, int ym, float a); + + void buttonClicked(Button* button); + virtual void mouseClicked(int x, int y, int buttonNum); + bool handleBackEvent(bool isDown); + bool isInGameScreen(); +private: + + LargeImageButton bHost; + LargeImageButton bJoin; + LargeImageButton bOptions; + ImageButton bQuit; // X close icon + + std::string copyright; + int copyrightPosX; + + std::string version; + int versionPosX; + + std::string username; +}; +}; + +#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS_TOUCH__TouchStartMenuScreen_H__*/ diff --git a/src/client/model/ChestModel.h b/src/client/model/ChestModel.h index 4a91504..b089583 100755 --- a/src/client/model/ChestModel.h +++ b/src/client/model/ChestModel.h @@ -1,52 +1,52 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__ChestModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__ChestModel_H__ - -//package net.minecraft.client.model; - -#include "Model.h" -#include "geom/ModelPart.h" - -class ChestModel: public Model -{ -public: - ChestModel() - : lid(0, 0), - lock(0, 0), - bottom(0, 0) - { - lid.setModel(this); - lid.setTexSize(64, 64); - lid.addBox(0.0f, -5, -14, 14, 5, 14, 0); - lid.x = 1; - lid.y = 7; - lid.z = 15; - - lock.setModel(this); - lock.setTexSize(64, 64); - lock.addBox(-1, -2, -15, 2, 4, 1, 0); - lock.x = 8; - lock.y = 7; - lock.z = 15; - - bottom.setModel(this); - bottom.setTexSize(64, 64); - bottom.addBox(0.0f, 0, 0, 14, 10, 14, 0); - bottom.x = 1; - bottom.y = 6; - bottom.z = 1; - } - - void render() { - lock.xRot = lid.xRot; - - lid.render(1 / 16.0f); - lock.render(1 / 16.0f); - bottom.render(1 / 16.0f); - } - - ModelPart lid; - ModelPart bottom; - ModelPart lock; -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__ChestModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__ChestModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__ChestModel_H__ + +//package net.minecraft.client.model; + +#include "Model.h" +#include "geom/ModelPart.h" + +class ChestModel: public Model +{ +public: + ChestModel() + : lid(0, 0), + lock(0, 0), + bottom(0, 0) + { + lid.setModel(this); + lid.setTexSize(64, 64); + lid.addBox(0.0f, -5, -14, 14, 5, 14, 0); + lid.x = 1; + lid.y = 7; + lid.z = 15; + + lock.setModel(this); + lock.setTexSize(64, 64); + lock.addBox(-1, -2, -15, 2, 4, 1, 0); + lock.x = 8; + lock.y = 7; + lock.z = 15; + + bottom.setModel(this); + bottom.setTexSize(64, 64); + bottom.addBox(0.0f, 0, 0, 14, 10, 14, 0); + bottom.x = 1; + bottom.y = 6; + bottom.z = 1; + } + + void render() { + lock.xRot = lid.xRot; + + lid.render(1 / 16.0f); + lock.render(1 / 16.0f); + bottom.render(1 / 16.0f); + } + + ModelPart lid; + ModelPart bottom; + ModelPart lock; +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__ChestModel_H__*/ diff --git a/src/client/model/ChickenModel.cpp b/src/client/model/ChickenModel.cpp index 4c23498..ac6b2e9 100755 --- a/src/client/model/ChickenModel.cpp +++ b/src/client/model/ChickenModel.cpp @@ -1,101 +1,101 @@ -#include "ChickenModel.h" -#include "../../world/entity/Entity.h" - -ChickenModel::ChickenModel() -: head(0, 0), - beak(14, 0), - redThing(14, 4), - body(0, 9), - leg0(26, 0), - leg1(26, 0), - wing0(24, 13), - wing1(24, 13) -{ - head.setModel(this); - beak.setModel(this); - redThing.setModel(this); - body.setModel(this); - leg0.setModel(this); - leg1.setModel(this); - wing0.setModel(this); - wing1.setModel(this); - - int yo = 16; - head.addBox(-2, -6, -2, 4, 6, 3, 0); // Head - head.setPos(0, -1 + (float)yo, -4); - - beak.addBox(-2, -4, -4, 4, 2, 2, 0); // Beak - beak.setPos(0, -1 + (float)yo, -4); - - redThing.addBox(-1, -2, -3, 2, 2, 2, 0); // Beak - redThing.setPos(0, -1 + (float)yo, -4); - - body.addBox(-3, -4, -3, 6, 8, 6, 0); // Body - body.setPos(0, 0 + (float)yo, 0); - - leg0.addBox(-1, 0, -3, 3, 5, 3); // Leg0 - leg0.setPos(-2, 3 + (float)yo, 1); - - leg1.addBox(-1, 0, -3, 3, 5, 3); // Leg1 - leg1.setPos(1, 3 + (float)yo, 1); - - wing0.addBox(0, 0, -3, 1, 4, 6); // Wing0 - wing0.setPos(-4, -3 + (float)yo, 0); - - wing1.addBox(-1, 0, -3, 1, 4, 6); // Wing1 - wing1.setPos(4, -3 + (float)yo, 0); -} - -void ChickenModel::render( Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale ) -{ - setupAnim(time, r, bob, yRot, xRot, scale); - - if (young) { - float ss = 2; - glPushMatrix(); - glTranslatef(0, 5 * scale, 2 * scale); - head.render(scale); - beak.render(scale); - redThing.render(scale); - glPopMatrix(); - glPushMatrix(); - glScalef(1 / ss, 1 / ss, 1 / ss); - glTranslatef(0, 24 * scale, 0); - body.render(scale); - leg0.render(scale); - leg1.render(scale); - wing0.render(scale); - wing1.render(scale); - glPopMatrix(); - } else { - head.render(scale); - beak.render(scale); - redThing.render(scale); - body.render(scale); - leg0.render(scale); - leg1.render(scale); - wing0.render(scale); - wing1.render(scale); - } -} - -void ChickenModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) -{ - head.xRot = -((xRot / (float) (180 / Mth::PI))); - head.yRot = yRot / (float) (180 / Mth::PI); - - beak.xRot = head.xRot; - beak.yRot = head.yRot; - - redThing.xRot = head.xRot; - redThing.yRot = head.yRot; - - body.xRot = 90 / (float) (180 / Mth::PI); - - const float pend = Mth::cos(time * 0.6662f) * 1.4f * r; - leg0.xRot = pend; - leg1.xRot = -pend; - wing0.zRot = bob; - wing1.zRot = -bob; -} - +#include "ChickenModel.h" +#include "../../world/entity/Entity.h" + +ChickenModel::ChickenModel() +: head(0, 0), + beak(14, 0), + redThing(14, 4), + body(0, 9), + leg0(26, 0), + leg1(26, 0), + wing0(24, 13), + wing1(24, 13) +{ + head.setModel(this); + beak.setModel(this); + redThing.setModel(this); + body.setModel(this); + leg0.setModel(this); + leg1.setModel(this); + wing0.setModel(this); + wing1.setModel(this); + + int yo = 16; + head.addBox(-2, -6, -2, 4, 6, 3, 0); // Head + head.setPos(0, -1 + (float)yo, -4); + + beak.addBox(-2, -4, -4, 4, 2, 2, 0); // Beak + beak.setPos(0, -1 + (float)yo, -4); + + redThing.addBox(-1, -2, -3, 2, 2, 2, 0); // Beak + redThing.setPos(0, -1 + (float)yo, -4); + + body.addBox(-3, -4, -3, 6, 8, 6, 0); // Body + body.setPos(0, 0 + (float)yo, 0); + + leg0.addBox(-1, 0, -3, 3, 5, 3); // Leg0 + leg0.setPos(-2, 3 + (float)yo, 1); + + leg1.addBox(-1, 0, -3, 3, 5, 3); // Leg1 + leg1.setPos(1, 3 + (float)yo, 1); + + wing0.addBox(0, 0, -3, 1, 4, 6); // Wing0 + wing0.setPos(-4, -3 + (float)yo, 0); + + wing1.addBox(-1, 0, -3, 1, 4, 6); // Wing1 + wing1.setPos(4, -3 + (float)yo, 0); +} + +void ChickenModel::render( Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale ) +{ + setupAnim(time, r, bob, yRot, xRot, scale); + + if (young) { + float ss = 2; + glPushMatrix(); + glTranslatef(0, 5 * scale, 2 * scale); + head.render(scale); + beak.render(scale); + redThing.render(scale); + glPopMatrix(); + glPushMatrix(); + glScalef(1 / ss, 1 / ss, 1 / ss); + glTranslatef(0, 24 * scale, 0); + body.render(scale); + leg0.render(scale); + leg1.render(scale); + wing0.render(scale); + wing1.render(scale); + glPopMatrix(); + } else { + head.render(scale); + beak.render(scale); + redThing.render(scale); + body.render(scale); + leg0.render(scale); + leg1.render(scale); + wing0.render(scale); + wing1.render(scale); + } +} + +void ChickenModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) +{ + head.xRot = -((xRot / (float) (180 / Mth::PI))); + head.yRot = yRot / (float) (180 / Mth::PI); + + beak.xRot = head.xRot; + beak.yRot = head.yRot; + + redThing.xRot = head.xRot; + redThing.yRot = head.yRot; + + body.xRot = 90 / (float) (180 / Mth::PI); + + const float pend = Mth::cos(time * 0.6662f) * 1.4f * r; + leg0.xRot = pend; + leg1.xRot = -pend; + wing0.zRot = bob; + wing1.zRot = -bob; +} + diff --git a/src/client/model/ChickenModel.h b/src/client/model/ChickenModel.h index 7e9ff7c..4bd53a9 100755 --- a/src/client/model/ChickenModel.h +++ b/src/client/model/ChickenModel.h @@ -1,23 +1,23 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__ChickenModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__ChickenModel_H__ - -#include "Model.h" -#include "geom/ModelPart.h" - -class Entity; - -class ChickenModel: public Model -{ - typedef Model super; -public: - ModelPart head, hair, body, leg0, leg1, wing0, wing1, beak, redThing; - - ChickenModel(); - - /*@Override*/ - void render(Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale); - - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__ChickenModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__ChickenModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__ChickenModel_H__ + +#include "Model.h" +#include "geom/ModelPart.h" + +class Entity; + +class ChickenModel: public Model +{ + typedef Model super; +public: + ModelPart head, hair, body, leg0, leg1, wing0, wing1, beak, redThing; + + ChickenModel(); + + /*@Override*/ + void render(Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale); + + void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__ChickenModel_H__*/ diff --git a/src/client/model/CowModel.cpp b/src/client/model/CowModel.cpp index 78154ca..054945c 100755 --- a/src/client/model/CowModel.cpp +++ b/src/client/model/CowModel.cpp @@ -1,30 +1,30 @@ -#include "CowModel.h" - -CowModel::CowModel() -: super(12, 0) -{ - head = ModelPart(0, 0); - head.setModel(this); - head.addBox(-4, -4, -6, 8, 8, 6, 0); // Head - head.setPos(0, 12 - 6 - 2, -8); - - head.texOffs(22, 0).addBox(-5, -5, -4, 1, 3, 1, 0); // Horn1 - head.texOffs(22, 0).addBox(+4, -5, -4, 1, 3, 1, 0); // Horn1 - - body = ModelPart(18, 4); - body.setModel(this); - body.addBox(-6, -10, -7, 12, 18, 10, 0); // Body - body.setPos(0, 11 + 6 - 12, 2); - body.texOffs(52, 0).addBox(-2, 2, -8, 4, 6, 1); - - leg0.x -= 1; - leg1.x += 1; - leg0.z += 0; - leg1.z += 0; - leg2.x -= 1; - leg3.x += 1; - leg2.z -= 1; - leg3.z -= 1; - - this->zHeadOffs += 2; -} +#include "CowModel.h" + +CowModel::CowModel() +: super(12, 0) +{ + head = ModelPart(0, 0); + head.setModel(this); + head.addBox(-4, -4, -6, 8, 8, 6, 0); // Head + head.setPos(0, 12 - 6 - 2, -8); + + head.texOffs(22, 0).addBox(-5, -5, -4, 1, 3, 1, 0); // Horn1 + head.texOffs(22, 0).addBox(+4, -5, -4, 1, 3, 1, 0); // Horn1 + + body = ModelPart(18, 4); + body.setModel(this); + body.addBox(-6, -10, -7, 12, 18, 10, 0); // Body + body.setPos(0, 11 + 6 - 12, 2); + body.texOffs(52, 0).addBox(-2, 2, -8, 4, 6, 1); + + leg0.x -= 1; + leg1.x += 1; + leg0.z += 0; + leg1.z += 0; + leg2.x -= 1; + leg3.x += 1; + leg2.z -= 1; + leg3.z -= 1; + + this->zHeadOffs += 2; +} diff --git a/src/client/model/CowModel.h b/src/client/model/CowModel.h index 3840639..28a28a6 100755 --- a/src/client/model/CowModel.h +++ b/src/client/model/CowModel.h @@ -1,15 +1,15 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__CowModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__CowModel_H__ - -//package net.minecraft.client.model; - -#include "QuadrupedModel.h" - -class CowModel: public QuadrupedModel -{ - typedef QuadrupedModel super; -public: - CowModel(); -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__CowModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__CowModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__CowModel_H__ + +//package net.minecraft.client.model; + +#include "QuadrupedModel.h" + +class CowModel: public QuadrupedModel +{ + typedef QuadrupedModel super; +public: + CowModel(); +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__CowModel_H__*/ diff --git a/src/client/model/CreeperModel.h b/src/client/model/CreeperModel.h index 2909afe..4528958 100755 --- a/src/client/model/CreeperModel.h +++ b/src/client/model/CreeperModel.h @@ -1,82 +1,82 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__CreeperModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__CreeperModel_H__ - -//package net.minecraft.client.model; - -#include "Model.h" - -#include "geom/ModelPart.h" -#include "../../world/entity/Entity.h" - -class CreeperModel: public Model -{ - typedef Model super; -public: - ModelPart head, /*hair,*/ body, leg0, leg1, leg2, leg3; - - CreeperModel(float g = 0) - : head(0, 0), - //hair(32, 0), - body(16, 16), - leg0(0, 16), - leg1(0, 16), - leg2(0, 16), - leg3(0, 16) - { - float yo = 4; - - head.setModel(this); - //hair.setModel(this); - body.setModel(this); - leg0.setModel(this); - leg1.setModel(this); - leg2.setModel(this); - leg3.setModel(this); - - head.addBox(-4, -8, -4, 8, 8, 8, g); // Head - head.setPos(0, yo, 0); - -// hair.addBox(-4, -8, -4, 8, 8, 8, g + 0.5f); // Head -// hair.setPos(0, yo, 0); - - body.addBox(-4, 0, -2, 8, 12, 4, g); // Body - body.setPos(0, yo, 0); - - leg0.addBox(-2, 0, -2, 4, 6, 4, g); // Leg0 - leg0.setPos(-2, 12 + yo, 4); - - leg1.addBox(-2, 0, -2, 4, 6, 4, g); // Leg1 - leg1.setPos(2, 12 + yo, 4); - - leg2.addBox(-2, 0, -2, 4, 6, 4, g); // Leg2 - leg2.setPos(-2, 12 + yo, -4); - - leg3.addBox(-2, 0, -2, 4, 6, 4, g); // Leg3 - leg3.setPos(2, 12 + yo, -4); - } - - /*@Override*/ - void render(Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale) { - setupAnim(time, r, bob, yRot, xRot, scale); - - head.render(scale); - body.render(scale); - leg0.render(scale); - leg1.render(scale); - leg2.render(scale); - leg3.render(scale); - } - - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { - head.yRot = yRot / (float) (180 / Mth::PI); - head.xRot = xRot / (float) (180 / Mth::PI); - - const float pend = (Mth::cos(time * 0.6662f) * 1.4f) * r; - leg0.xRot = pend; - leg1.xRot = -pend; - leg2.xRot = -pend; - leg3.xRot = pend; - } -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__CreeperModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__CreeperModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__CreeperModel_H__ + +//package net.minecraft.client.model; + +#include "Model.h" + +#include "geom/ModelPart.h" +#include "../../world/entity/Entity.h" + +class CreeperModel: public Model +{ + typedef Model super; +public: + ModelPart head, /*hair,*/ body, leg0, leg1, leg2, leg3; + + CreeperModel(float g = 0) + : head(0, 0), + //hair(32, 0), + body(16, 16), + leg0(0, 16), + leg1(0, 16), + leg2(0, 16), + leg3(0, 16) + { + float yo = 4; + + head.setModel(this); + //hair.setModel(this); + body.setModel(this); + leg0.setModel(this); + leg1.setModel(this); + leg2.setModel(this); + leg3.setModel(this); + + head.addBox(-4, -8, -4, 8, 8, 8, g); // Head + head.setPos(0, yo, 0); + +// hair.addBox(-4, -8, -4, 8, 8, 8, g + 0.5f); // Head +// hair.setPos(0, yo, 0); + + body.addBox(-4, 0, -2, 8, 12, 4, g); // Body + body.setPos(0, yo, 0); + + leg0.addBox(-2, 0, -2, 4, 6, 4, g); // Leg0 + leg0.setPos(-2, 12 + yo, 4); + + leg1.addBox(-2, 0, -2, 4, 6, 4, g); // Leg1 + leg1.setPos(2, 12 + yo, 4); + + leg2.addBox(-2, 0, -2, 4, 6, 4, g); // Leg2 + leg2.setPos(-2, 12 + yo, -4); + + leg3.addBox(-2, 0, -2, 4, 6, 4, g); // Leg3 + leg3.setPos(2, 12 + yo, -4); + } + + /*@Override*/ + void render(Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale) { + setupAnim(time, r, bob, yRot, xRot, scale); + + head.render(scale); + body.render(scale); + leg0.render(scale); + leg1.render(scale); + leg2.render(scale); + leg3.render(scale); + } + + void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { + head.yRot = yRot / (float) (180 / Mth::PI); + head.xRot = xRot / (float) (180 / Mth::PI); + + const float pend = (Mth::cos(time * 0.6662f) * 1.4f) * r; + leg0.xRot = pend; + leg1.xRot = -pend; + leg2.xRot = -pend; + leg3.xRot = pend; + } +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__CreeperModel_H__*/ diff --git a/src/client/model/HumanoidModel.cpp b/src/client/model/HumanoidModel.cpp index 5a64657..79cacc1 100755 --- a/src/client/model/HumanoidModel.cpp +++ b/src/client/model/HumanoidModel.cpp @@ -1,292 +1,292 @@ -#include "HumanoidModel.h" -#include "../Minecraft.h" -#include "../../util/Mth.h" -#include "../../world/entity/player/Player.h" -#include "../../world/entity/player/Inventory.h" - -HumanoidModel::HumanoidModel( float g /*= 0*/, float yOffset /*= 0*/, int texW /*= 64*/, int texH /*= 32*/ ) -: holdingLeftHand(false), - holdingRightHand(false), - sneaking(false), - bowAndArrow(false), - head(0, 0), - hair(32, 0), - //ear (24, 0), - body(16, 16), - arm0(24 + 16, 16), - arm1(24 + 16, 16), - leg0(0, 16), - leg1(0, 16) -{ - texWidth = texW; - texHeight = texH; - - head.setModel(this); - hair.setModel(this); - body.setModel(this); - arm0.setModel(this); - arm1.setModel(this); - leg0.setModel(this); - leg1.setModel(this); - - // If the texture is 64x64, use the modern skin layout for arms/legs and add overlay layers. - bool modernSkin = (texWidth == 64 && texHeight == 64); - if (modernSkin) { - // Left arm and left leg are located in the bottom half of a 64x64 skin. - arm1.texOffs(32, 48); - leg1.texOffs(16, 48); - } - - head.addBox(-4, -8, -4, 8, 8, 8, g); // Head - head.setPos(0, 0 + yOffset, 0); - - if (modernSkin) { - hair.addBox(-4, -8, -4, 8, 8, 8, g + 0.5f); // Outer head layer (hat) - hair.setPos(0, 0 + yOffset, 0); - } - - body.addBox(-4, 0, -2, 8, 12, 4, g); // Body - body.setPos(0, 0 + yOffset, 0); - - arm0.addBox(-3, -2, -2, 4, 12, 4, g); // Arm0 - arm0.setPos(-5, 2 + yOffset, 0); - - arm1.mirror = true; - arm1.addBox(-1, -2, -2, 4, 12, 4, g); // Arm1 - arm1.setPos(5, 2 + yOffset, 0); - - leg0.addBox(-2, 0, -2, 4, 12, 4, g); // Leg0 - leg0.setPos(-2, 12 + yOffset, 0); - - leg1.mirror = true; - leg1.addBox(-2, 0, -2, 4, 12, 4, g); // Leg1 - leg1.setPos(2, 12 + yOffset, 0); - - if (modernSkin) { - // Overlay layers for 64x64 skins (same geometry, different texture regions) - body.texOffs(16, 32); - body.addBox(-4, 0, -2, 8, 12, 4, g + 0.5f); - - arm0.texOffs(40, 32); - arm0.addBox(-3, -2, -2, 4, 12, 4, g + 0.5f); - - arm1.texOffs(48, 48); - arm1.addBox(-1, -2, -2, 4, 12, 4, g + 0.5f); - - leg0.texOffs(0, 32); - leg0.addBox(-2, 0, -2, 4, 12, 4, g + 0.5f); - - leg1.texOffs(0, 48); - leg1.addBox(-2, 0, -2, 4, 12, 4, g + 0.5f); - } -} - -void HumanoidModel::render(Entity* e, float time, float r, float bob, float yRot, float xRot, float scale ) -{ - if(e != NULL && e->isMob()) { - Mob* mob = (Mob*)(e); - ItemInstance* item = mob->getCarriedItem(); - if(item != NULL) { - if(mob->getUseItemDuration() > 0) { - UseAnim::UseAnimation anim = item->getUseAnimation(); - if(anim == UseAnim::bow) { - bowAndArrow = true; - } - } - } - } - - setupAnim(time, r, bob, yRot, xRot, scale); - - // Sync overlay with head rotation/position - if (texWidth == 64 && texHeight == 64) { - hair.xRot = head.xRot; - hair.yRot = head.yRot; - hair.zRot = head.zRot; - hair.y = head.y; - } - - head.render(scale); - if (texWidth == 64 && texHeight == 64) { - hair.render(scale); - } - body.render(scale); - arm0.render(scale); - arm1.render(scale); - leg0.render(scale); - leg1.render(scale); - bowAndArrow = false; -} - -void HumanoidModel::render( HumanoidModel* model, float scale ) -{ - head.yRot = model->head.yRot; - head.y = model->head.y; - head.xRot = model->head.xRot; - if (texWidth == 64 && texHeight == 64) { - hair.yRot = head.yRot; - hair.xRot = head.xRot; - hair.zRot = head.zRot; - hair.y = head.y; - } - - arm0.xRot = model->arm0.xRot; - arm0.zRot = model->arm0.zRot; - - arm1.xRot = model->arm1.xRot; - arm1.zRot = model->arm1.zRot; - - leg0.xRot = model->leg0.xRot; - leg1.xRot = model->leg1.xRot; - - head.render(scale); - if (texWidth == 64 && texHeight == 64) { - hair.render(scale); - } - body.render(scale); - arm0.render(scale); - arm1.render(scale); - leg0.render(scale); - leg1.render(scale); -} - -void HumanoidModel::renderHorrible( float time, float r, float bob, float yRot, float xRot, float scale ) -{ - setupAnim(time, r, bob, yRot, xRot, scale); - head.renderHorrible(scale); - body.renderHorrible(scale); - arm0.renderHorrible(scale); - arm1.renderHorrible(scale); - leg0.renderHorrible(scale); - leg1.renderHorrible(scale); - //hair.renderHorrible(scale); -} -// Updated to match Minecraft Java, all except hair. -void HumanoidModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) -{ - head.yRot = yRot / (180 / Mth::PI); - head.xRot = xRot / (180 / Mth::PI); - const float tcos0 = Mth::cos(time * 0.6662f) * r; - const float tcos1 = Mth::cos(time * 0.6662f + Mth::PI) * r; - - arm0.xRot = tcos1; - arm1.xRot = tcos0; - arm0.zRot = 0; - arm1.zRot = 0; - - leg0.xRot = tcos0 * 1.4f; - leg1.xRot = tcos1 * 1.4f; - leg0.yRot = 0; - leg1.yRot = 0; - - if (riding) { - arm0.xRot += -Mth::PI / 2 * 0.4f; - arm1.xRot += -Mth::PI / 2 * 0.4f; - leg0.xRot = -Mth::PI / 2 * 0.8f; - leg1.xRot = -Mth::PI / 2 * 0.8f; - leg0.yRot = Mth::PI / 2 * 0.2f; - leg1.yRot = -Mth::PI / 2 * 0.2f; - } - - if (holdingLeftHand != 0) { - arm1.xRot = arm1.xRot * 0.5f - Mth::PI / 2.0f * 0.2f * holdingLeftHand; - } - if (holdingRightHand != 0) { - arm0.xRot = arm0.xRot * 0.5f - Mth::PI / 2.0f * 0.2f * holdingRightHand; - } - arm0.yRot = 0; - arm1.yRot = 0; - - if (attackTime > -9990) { - float swing = attackTime; - body.yRot = Mth::sin(Mth::sqrt(swing) * Mth::PI * 2) * 0.2f; - arm0.z = Mth::sin(body.yRot) * 5; - arm0.x = -Mth::cos(body.yRot) * 5; - arm1.z = -Mth::sin(body.yRot) * 5; - arm1.x = Mth::cos(body.yRot) * 5; - arm0.yRot += body.yRot; - arm1.yRot += body.yRot; - arm1.xRot += body.yRot; - - swing = 1 - attackTime; - swing *= swing; - swing *= swing; - swing = 1 - swing; - float aa = Mth::sin(swing * Mth::PI); - float bb = Mth::sin(attackTime * Mth::PI) * -(head.xRot - 0.7f) * 0.75f; - arm0.xRot -= aa * 1.2f + bb; - arm0.yRot += body.yRot * 2; - arm0.zRot = Mth::sin(attackTime * Mth::PI) * -0.4f; - } - - if (sneaking) { - body.xRot = 0.5f; - arm0.xRot += 0.4f; - arm1.xRot += 0.4f; - leg0.z = +4.0f; - leg1.z = +4.0f; - leg0.y = +9.0f; - leg1.y = +9.0f; - head.y = +1; - } else { - body.xRot = 0.0f; - leg0.z = +0.0f; - leg1.z = +0.0f; - leg0.y = +12.0f; - leg1.y = +12.0f; - head.y = 0; - } - - const float bcos = Mth::cos(bob * 0.09f) * 0.05f + 0.05f; - const float bsin = Mth::sin(bob * 0.067f) * 0.05f; - - arm0.zRot += bcos; - arm1.zRot -= bcos; - arm0.xRot += bsin; - arm1.xRot -= bsin; - - if (bowAndArrow) { - float attack2 = 0; - float attack = 0; - - arm0.zRot = 0; - arm1.zRot = 0; - arm0.yRot = -(0.1f - attack2 * 0.6f) + head.yRot; - arm1.yRot = +(0.1f - attack2 * 0.6f) + head.yRot + 0.4f; - arm0.xRot = -Mth::PI / 2.0f + head.xRot; - arm1.xRot = -Mth::PI / 2.0f + head.xRot; - arm0.xRot -= attack2 * 1.2f - attack * 0.4f; - arm1.xRot -= attack2 * 1.2f - attack * 0.4f; - - arm0.zRot += bcos; - arm1.zRot -= bcos; - arm0.xRot += bsin; - arm1.xRot -= bsin; - } -} - -void HumanoidModel::onGraphicsReset() -{ - head.onGraphicsReset(); - body.onGraphicsReset(); - arm0.onGraphicsReset(); - arm1.onGraphicsReset(); - leg0.onGraphicsReset(); - leg1.onGraphicsReset(); - //hair.onGraphicsReset(); -} - -//void renderHair(float scale) { -// hair.yRot = head.yRot; -// hair.xRot = head.xRot; -// hair.render(scale); -//} -// -//void renderEars(float scale) { -// ear.yRot = head.yRot; -// ear.xRot = head.xRot; -// ear.x=0; -// ear.y=0; -// ear.render(scale); -//} -// +#include "HumanoidModel.h" +#include "../Minecraft.h" +#include "../../util/Mth.h" +#include "../../world/entity/player/Player.h" +#include "../../world/entity/player/Inventory.h" + +HumanoidModel::HumanoidModel( float g /*= 0*/, float yOffset /*= 0*/, int texW /*= 64*/, int texH /*= 32*/ ) +: holdingLeftHand(false), + holdingRightHand(false), + sneaking(false), + bowAndArrow(false), + head(0, 0), + hair(32, 0), + //ear (24, 0), + body(16, 16), + arm0(24 + 16, 16), + arm1(24 + 16, 16), + leg0(0, 16), + leg1(0, 16) +{ + texWidth = texW; + texHeight = texH; + + head.setModel(this); + hair.setModel(this); + body.setModel(this); + arm0.setModel(this); + arm1.setModel(this); + leg0.setModel(this); + leg1.setModel(this); + + // If the texture is 64x64, use the modern skin layout for arms/legs and add overlay layers. + bool modernSkin = (texWidth == 64 && texHeight == 64); + if (modernSkin) { + // Left arm and left leg are located in the bottom half of a 64x64 skin. + arm1.texOffs(32, 48); + leg1.texOffs(16, 48); + } + + head.addBox(-4, -8, -4, 8, 8, 8, g); // Head + head.setPos(0, 0 + yOffset, 0); + + if (modernSkin) { + hair.addBox(-4, -8, -4, 8, 8, 8, g + 0.5f); // Outer head layer (hat) + hair.setPos(0, 0 + yOffset, 0); + } + + body.addBox(-4, 0, -2, 8, 12, 4, g); // Body + body.setPos(0, 0 + yOffset, 0); + + arm0.addBox(-3, -2, -2, 4, 12, 4, g); // Arm0 + arm0.setPos(-5, 2 + yOffset, 0); + + arm1.mirror = true; + arm1.addBox(-1, -2, -2, 4, 12, 4, g); // Arm1 + arm1.setPos(5, 2 + yOffset, 0); + + leg0.addBox(-2, 0, -2, 4, 12, 4, g); // Leg0 + leg0.setPos(-2, 12 + yOffset, 0); + + leg1.mirror = true; + leg1.addBox(-2, 0, -2, 4, 12, 4, g); // Leg1 + leg1.setPos(2, 12 + yOffset, 0); + + if (modernSkin) { + // Overlay layers for 64x64 skins (same geometry, different texture regions) + body.texOffs(16, 32); + body.addBox(-4, 0, -2, 8, 12, 4, g + 0.5f); + + arm0.texOffs(40, 32); + arm0.addBox(-3, -2, -2, 4, 12, 4, g + 0.5f); + + arm1.texOffs(48, 48); + arm1.addBox(-1, -2, -2, 4, 12, 4, g + 0.5f); + + leg0.texOffs(0, 32); + leg0.addBox(-2, 0, -2, 4, 12, 4, g + 0.5f); + + leg1.texOffs(0, 48); + leg1.addBox(-2, 0, -2, 4, 12, 4, g + 0.5f); + } +} + +void HumanoidModel::render(Entity* e, float time, float r, float bob, float yRot, float xRot, float scale ) +{ + if(e != NULL && e->isMob()) { + Mob* mob = (Mob*)(e); + ItemInstance* item = mob->getCarriedItem(); + if(item != NULL) { + if(mob->getUseItemDuration() > 0) { + UseAnim::UseAnimation anim = item->getUseAnimation(); + if(anim == UseAnim::bow) { + bowAndArrow = true; + } + } + } + } + + setupAnim(time, r, bob, yRot, xRot, scale); + + // Sync overlay with head rotation/position + if (texWidth == 64 && texHeight == 64) { + hair.xRot = head.xRot; + hair.yRot = head.yRot; + hair.zRot = head.zRot; + hair.y = head.y; + } + + head.render(scale); + if (texWidth == 64 && texHeight == 64) { + hair.render(scale); + } + body.render(scale); + arm0.render(scale); + arm1.render(scale); + leg0.render(scale); + leg1.render(scale); + bowAndArrow = false; +} + +void HumanoidModel::render( HumanoidModel* model, float scale ) +{ + head.yRot = model->head.yRot; + head.y = model->head.y; + head.xRot = model->head.xRot; + if (texWidth == 64 && texHeight == 64) { + hair.yRot = head.yRot; + hair.xRot = head.xRot; + hair.zRot = head.zRot; + hair.y = head.y; + } + + arm0.xRot = model->arm0.xRot; + arm0.zRot = model->arm0.zRot; + + arm1.xRot = model->arm1.xRot; + arm1.zRot = model->arm1.zRot; + + leg0.xRot = model->leg0.xRot; + leg1.xRot = model->leg1.xRot; + + head.render(scale); + if (texWidth == 64 && texHeight == 64) { + hair.render(scale); + } + body.render(scale); + arm0.render(scale); + arm1.render(scale); + leg0.render(scale); + leg1.render(scale); +} + +void HumanoidModel::renderHorrible( float time, float r, float bob, float yRot, float xRot, float scale ) +{ + setupAnim(time, r, bob, yRot, xRot, scale); + head.renderHorrible(scale); + body.renderHorrible(scale); + arm0.renderHorrible(scale); + arm1.renderHorrible(scale); + leg0.renderHorrible(scale); + leg1.renderHorrible(scale); + //hair.renderHorrible(scale); +} +// Updated to match Minecraft Java, all except hair. +void HumanoidModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) +{ + head.yRot = yRot / (180 / Mth::PI); + head.xRot = xRot / (180 / Mth::PI); + const float tcos0 = Mth::cos(time * 0.6662f) * r; + const float tcos1 = Mth::cos(time * 0.6662f + Mth::PI) * r; + + arm0.xRot = tcos1; + arm1.xRot = tcos0; + arm0.zRot = 0; + arm1.zRot = 0; + + leg0.xRot = tcos0 * 1.4f; + leg1.xRot = tcos1 * 1.4f; + leg0.yRot = 0; + leg1.yRot = 0; + + if (riding) { + arm0.xRot += -Mth::PI / 2 * 0.4f; + arm1.xRot += -Mth::PI / 2 * 0.4f; + leg0.xRot = -Mth::PI / 2 * 0.8f; + leg1.xRot = -Mth::PI / 2 * 0.8f; + leg0.yRot = Mth::PI / 2 * 0.2f; + leg1.yRot = -Mth::PI / 2 * 0.2f; + } + + if (holdingLeftHand != 0) { + arm1.xRot = arm1.xRot * 0.5f - Mth::PI / 2.0f * 0.2f * holdingLeftHand; + } + if (holdingRightHand != 0) { + arm0.xRot = arm0.xRot * 0.5f - Mth::PI / 2.0f * 0.2f * holdingRightHand; + } + arm0.yRot = 0; + arm1.yRot = 0; + + if (attackTime > -9990) { + float swing = attackTime; + body.yRot = Mth::sin(Mth::sqrt(swing) * Mth::PI * 2) * 0.2f; + arm0.z = Mth::sin(body.yRot) * 5; + arm0.x = -Mth::cos(body.yRot) * 5; + arm1.z = -Mth::sin(body.yRot) * 5; + arm1.x = Mth::cos(body.yRot) * 5; + arm0.yRot += body.yRot; + arm1.yRot += body.yRot; + arm1.xRot += body.yRot; + + swing = 1 - attackTime; + swing *= swing; + swing *= swing; + swing = 1 - swing; + float aa = Mth::sin(swing * Mth::PI); + float bb = Mth::sin(attackTime * Mth::PI) * -(head.xRot - 0.7f) * 0.75f; + arm0.xRot -= aa * 1.2f + bb; + arm0.yRot += body.yRot * 2; + arm0.zRot = Mth::sin(attackTime * Mth::PI) * -0.4f; + } + + if (sneaking) { + body.xRot = 0.5f; + arm0.xRot += 0.4f; + arm1.xRot += 0.4f; + leg0.z = +4.0f; + leg1.z = +4.0f; + leg0.y = +9.0f; + leg1.y = +9.0f; + head.y = +1; + } else { + body.xRot = 0.0f; + leg0.z = +0.0f; + leg1.z = +0.0f; + leg0.y = +12.0f; + leg1.y = +12.0f; + head.y = 0; + } + + const float bcos = Mth::cos(bob * 0.09f) * 0.05f + 0.05f; + const float bsin = Mth::sin(bob * 0.067f) * 0.05f; + + arm0.zRot += bcos; + arm1.zRot -= bcos; + arm0.xRot += bsin; + arm1.xRot -= bsin; + + if (bowAndArrow) { + float attack2 = 0; + float attack = 0; + + arm0.zRot = 0; + arm1.zRot = 0; + arm0.yRot = -(0.1f - attack2 * 0.6f) + head.yRot; + arm1.yRot = +(0.1f - attack2 * 0.6f) + head.yRot + 0.4f; + arm0.xRot = -Mth::PI / 2.0f + head.xRot; + arm1.xRot = -Mth::PI / 2.0f + head.xRot; + arm0.xRot -= attack2 * 1.2f - attack * 0.4f; + arm1.xRot -= attack2 * 1.2f - attack * 0.4f; + + arm0.zRot += bcos; + arm1.zRot -= bcos; + arm0.xRot += bsin; + arm1.xRot -= bsin; + } +} + +void HumanoidModel::onGraphicsReset() +{ + head.onGraphicsReset(); + body.onGraphicsReset(); + arm0.onGraphicsReset(); + arm1.onGraphicsReset(); + leg0.onGraphicsReset(); + leg1.onGraphicsReset(); + //hair.onGraphicsReset(); +} + +//void renderHair(float scale) { +// hair.yRot = head.yRot; +// hair.xRot = head.xRot; +// hair.render(scale); +//} +// +//void renderEars(float scale) { +// ear.yRot = head.yRot; +// ear.xRot = head.xRot; +// ear.x=0; +// ear.y=0; +// ear.render(scale); +//} +// diff --git a/src/client/model/HumanoidModel.h b/src/client/model/HumanoidModel.h index d97a7be..f67d04d 100755 --- a/src/client/model/HumanoidModel.h +++ b/src/client/model/HumanoidModel.h @@ -1,28 +1,28 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__HumanoidModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__HumanoidModel_H__ - -//package net.minecraft.client.model; - -#include "Model.h" -#include "geom/ModelPart.h" -class ItemInstance; -class HumanoidModel: public Model -{ -public: -HumanoidModel(float g = 0, float yOffset = 0, int texW = 64, int texH = 32); - - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); - - void render(HumanoidModel* model, float scale); - void render(Entity* e, float time, float r, float bob, float yRot, float xRot, float scale); - void renderHorrible(float time, float r, float bob, float yRot, float xRot, float scale); - void onGraphicsReset(); - - ModelPart head, hair, body, arm0, arm1, leg0, leg1;//, ear; - bool holdingLeftHand; - bool holdingRightHand; - bool sneaking; - bool bowAndArrow; -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__HumanoidModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__HumanoidModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__HumanoidModel_H__ + +//package net.minecraft.client.model; + +#include "Model.h" +#include "geom/ModelPart.h" +class ItemInstance; +class HumanoidModel: public Model +{ +public: +HumanoidModel(float g = 0, float yOffset = 0, int texW = 64, int texH = 32); + + void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); + + void render(HumanoidModel* model, float scale); + void render(Entity* e, float time, float r, float bob, float yRot, float xRot, float scale); + void renderHorrible(float time, float r, float bob, float yRot, float xRot, float scale); + void onGraphicsReset(); + + ModelPart head, hair, body, arm0, arm1, leg0, leg1;//, ear; + bool holdingLeftHand; + bool holdingRightHand; + bool sneaking; + bool bowAndArrow; +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__HumanoidModel_H__*/ diff --git a/src/client/model/Model.h b/src/client/model/Model.h index 176ca5e..450dac6 100755 --- a/src/client/model/Model.h +++ b/src/client/model/Model.h @@ -1,48 +1,48 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__Model_H__ -#define NET_MINECRAFT_CLIENT_MODEL__Model_H__ - -//package net.minecraft.client.model; - -#include -#include "geom/ModelPart.h" - -class Mob; -class Entity; - -class Model -{ -protected: - Model() - : riding(false), - attackTime(0), - texWidth(64), - texHeight(32), - young(true) - {} - -public: - virtual ~Model() {} - - virtual void onGraphicsReset() { - for (unsigned int i = 0; i < cubes.size(); ++i) - cubes[i]->onGraphicsReset(); - } - - virtual void render(Entity* e, float time, float r, float bob, float yRot, float xRot, float scale) {} - virtual void renderHorrible(float time, float r, float bob, float yRot, float xRot, float scale) {} - virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) {} - virtual void prepareMobModel(Mob* mob, float time, float r, float a) {} - - float attackTime; - bool riding; - - int texWidth; - int texHeight; - - std::vector cubes; - bool young; -private: - //Map mappedTexOffs = new HashMap(); -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__Model_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__Model_H__ +#define NET_MINECRAFT_CLIENT_MODEL__Model_H__ + +//package net.minecraft.client.model; + +#include +#include "geom/ModelPart.h" + +class Mob; +class Entity; + +class Model +{ +protected: + Model() + : riding(false), + attackTime(0), + texWidth(64), + texHeight(32), + young(true) + {} + +public: + virtual ~Model() {} + + virtual void onGraphicsReset() { + for (unsigned int i = 0; i < cubes.size(); ++i) + cubes[i]->onGraphicsReset(); + } + + virtual void render(Entity* e, float time, float r, float bob, float yRot, float xRot, float scale) {} + virtual void renderHorrible(float time, float r, float bob, float yRot, float xRot, float scale) {} + virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) {} + virtual void prepareMobModel(Mob* mob, float time, float r, float a) {} + + float attackTime; + bool riding; + + int texWidth; + int texHeight; + + std::vector cubes; + bool young; +private: + //Map mappedTexOffs = new HashMap(); +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__Model_H__*/ diff --git a/src/client/model/ModelInclude.h b/src/client/model/ModelInclude.h index 0fb4a51..f742ace 100755 --- a/src/client/model/ModelInclude.h +++ b/src/client/model/ModelInclude.h @@ -1,17 +1,17 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__ModelInclude_H__ -#define NET_MINECRAFT_CLIENT_MODEL__ModelInclude_H__ - -#include "HumanoidModel.h" - -#include "ChickenModel.h" -#include "CowModel.h" -#include "PigModel.h" -#include "SheepModel.h" -#include "SheepFurModel.h" - -#include "ZombieModel.h" -#include "SkeletonModel.h" - -#include "SignModel.h" - -#endif /*NET_MINECRAFT_CLIENT_MODEL__ModelInclude_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__ModelInclude_H__ +#define NET_MINECRAFT_CLIENT_MODEL__ModelInclude_H__ + +#include "HumanoidModel.h" + +#include "ChickenModel.h" +#include "CowModel.h" +#include "PigModel.h" +#include "SheepModel.h" +#include "SheepFurModel.h" + +#include "ZombieModel.h" +#include "SkeletonModel.h" + +#include "SignModel.h" + +#endif /*NET_MINECRAFT_CLIENT_MODEL__ModelInclude_H__*/ diff --git a/src/client/model/PigModel.cpp b/src/client/model/PigModel.cpp index 9adc8eb..09ecc49 100755 --- a/src/client/model/PigModel.cpp +++ b/src/client/model/PigModel.cpp @@ -1,8 +1,8 @@ -#include "PigModel.h" - -PigModel::PigModel( float g /*= 0*/ ) -: super(6, g) -{ - head.texOffs(16, 16).addBox(-2, 0, -9, 4, 3, 1, g); - yHeadOffs = 4; -} +#include "PigModel.h" + +PigModel::PigModel( float g /*= 0*/ ) +: super(6, g) +{ + head.texOffs(16, 16).addBox(-2, 0, -9, 4, 3, 1, g); + yHeadOffs = 4; +} diff --git a/src/client/model/PigModel.h b/src/client/model/PigModel.h index c4bcc69..96e874f 100755 --- a/src/client/model/PigModel.h +++ b/src/client/model/PigModel.h @@ -1,15 +1,15 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__PigModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__PigModel_H__ - -//package net.minecraft.client.model; - -#include "QuadrupedModel.h" - -class PigModel: public QuadrupedModel -{ - typedef QuadrupedModel super; -public: - PigModel(float g = 0); -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__PigModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__PigModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__PigModel_H__ + +//package net.minecraft.client.model; + +#include "QuadrupedModel.h" + +class PigModel: public QuadrupedModel +{ + typedef QuadrupedModel super; +public: + PigModel(float g = 0); +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__PigModel_H__*/ diff --git a/src/client/model/QuadrupedModel.cpp b/src/client/model/QuadrupedModel.cpp index 7f83c6e..e470283 100755 --- a/src/client/model/QuadrupedModel.cpp +++ b/src/client/model/QuadrupedModel.cpp @@ -1,122 +1,122 @@ -#include "QuadrupedModel.h" -#include "geom/ModelPart.h" -#include "../../util/Mth.h" - -QuadrupedModel::QuadrupedModel( int legSize, float g ) -: yHeadOffs(8), - zHeadOffs(4), - head(0, 0), - body(28, 8), - leg0(0, 16), - leg1(0, 16), - leg2(0, 16), - leg3(0, 16) -{ - head.setModel(this); - body.setModel(this); - leg0.setModel(this); - leg1.setModel(this); - leg2.setModel(this); - leg3.setModel(this); - - head.addBox(-4, -4, -8, 8, 8, 8, g); // Head - head.setPos(0, 12 + 6 - (float)legSize, -6); - - body.addBox(-5, -10, -7, 10, 16, 8, g); // Body - body.setPos(0, 11 + 6 - (float)legSize, 2); - - leg0.addBox(-2, 0, -2, 4, legSize, 4, g); // Leg0 - leg0.setPos(-3, 18 + 6 - (float)legSize, 7); - - leg1.addBox(-2, 0, -2, 4, legSize, 4, g); // Leg1 - leg1.setPos(3, 18 + 6 - (float)legSize, 7); - - leg2.addBox(-2, 0, -2, 4, legSize, 4, g); // Leg2 - leg2.setPos(-3, 18 + 6 - (float)legSize, -5); - - leg3.addBox(-2, 0, -2, 4, legSize, 4, g); // Leg3 - leg3.setPos(3, 18 + 6 - (float)legSize, -5); -} - -void QuadrupedModel::render( Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale ) -{ - setupAnim(time, r, bob, yRot, xRot, scale); - - if (young) { - float ss = 2.0f; - glPushMatrix(); - glTranslatef(0, yHeadOffs * scale, zHeadOffs * scale); - head.render(scale); - glPopMatrix(); - glPushMatrix(); - glScalef(1 / ss, 1 / ss, 1 / ss); - glTranslatef(0, 24 * scale, 0); - body.render(scale); - leg0.render(scale); - leg1.render(scale); - leg2.render(scale); - leg3.render(scale); - glPopMatrix(); - } else { - head.render(scale); - body.render(scale); - leg0.render(scale); - leg1.render(scale); - leg2.render(scale); - leg3.render(scale); - } -} - -void QuadrupedModel::render( QuadrupedModel* model, float scale ) -{ - head.yRot = model->head.yRot; - head.xRot = model->head.xRot; - - head.y = model->head.y; - head.x = model->head.x; - - body.yRot = model->body.yRot; - body.xRot = model->body.xRot; - - leg0.xRot = model->leg0.xRot; - leg1.xRot = model->leg1.xRot; - leg2.xRot = model->leg2.xRot; - leg3.xRot = model->leg3.xRot; - - if (young) { - float ss = 2.0f; - glPushMatrix(); - glTranslatef(0, 8 * scale, 4 * scale); - head.render(scale); - glPopMatrix(); - glPushMatrix(); - glScalef(1 / ss, 1 / ss, 1 / ss); - glTranslatef(0, 24 * scale, 0); - body.render(scale); - leg0.render(scale); - leg1.render(scale); - leg2.render(scale); - leg3.render(scale); - glPopMatrix(); - } else { - head.render(scale); - body.render(scale); - leg0.render(scale); - leg1.render(scale); - leg2.render(scale); - leg3.render(scale); - } -} - -void QuadrupedModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) -{ - head.xRot = xRot / (180.f / Mth::PI); - head.yRot = yRot / (180.f / Mth::PI); - body.xRot = 90 / (180.f / Mth::PI); - - const float pend = Mth::cos(time * 0.6662f) * 1.4f * r; - leg0.xRot = pend; - leg1.xRot = -pend; - leg2.xRot = -pend; - leg3.xRot = pend; -} +#include "QuadrupedModel.h" +#include "geom/ModelPart.h" +#include "../../util/Mth.h" + +QuadrupedModel::QuadrupedModel( int legSize, float g ) +: yHeadOffs(8), + zHeadOffs(4), + head(0, 0), + body(28, 8), + leg0(0, 16), + leg1(0, 16), + leg2(0, 16), + leg3(0, 16) +{ + head.setModel(this); + body.setModel(this); + leg0.setModel(this); + leg1.setModel(this); + leg2.setModel(this); + leg3.setModel(this); + + head.addBox(-4, -4, -8, 8, 8, 8, g); // Head + head.setPos(0, 12 + 6 - (float)legSize, -6); + + body.addBox(-5, -10, -7, 10, 16, 8, g); // Body + body.setPos(0, 11 + 6 - (float)legSize, 2); + + leg0.addBox(-2, 0, -2, 4, legSize, 4, g); // Leg0 + leg0.setPos(-3, 18 + 6 - (float)legSize, 7); + + leg1.addBox(-2, 0, -2, 4, legSize, 4, g); // Leg1 + leg1.setPos(3, 18 + 6 - (float)legSize, 7); + + leg2.addBox(-2, 0, -2, 4, legSize, 4, g); // Leg2 + leg2.setPos(-3, 18 + 6 - (float)legSize, -5); + + leg3.addBox(-2, 0, -2, 4, legSize, 4, g); // Leg3 + leg3.setPos(3, 18 + 6 - (float)legSize, -5); +} + +void QuadrupedModel::render( Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale ) +{ + setupAnim(time, r, bob, yRot, xRot, scale); + + if (young) { + float ss = 2.0f; + glPushMatrix(); + glTranslatef(0, yHeadOffs * scale, zHeadOffs * scale); + head.render(scale); + glPopMatrix(); + glPushMatrix(); + glScalef(1 / ss, 1 / ss, 1 / ss); + glTranslatef(0, 24 * scale, 0); + body.render(scale); + leg0.render(scale); + leg1.render(scale); + leg2.render(scale); + leg3.render(scale); + glPopMatrix(); + } else { + head.render(scale); + body.render(scale); + leg0.render(scale); + leg1.render(scale); + leg2.render(scale); + leg3.render(scale); + } +} + +void QuadrupedModel::render( QuadrupedModel* model, float scale ) +{ + head.yRot = model->head.yRot; + head.xRot = model->head.xRot; + + head.y = model->head.y; + head.x = model->head.x; + + body.yRot = model->body.yRot; + body.xRot = model->body.xRot; + + leg0.xRot = model->leg0.xRot; + leg1.xRot = model->leg1.xRot; + leg2.xRot = model->leg2.xRot; + leg3.xRot = model->leg3.xRot; + + if (young) { + float ss = 2.0f; + glPushMatrix(); + glTranslatef(0, 8 * scale, 4 * scale); + head.render(scale); + glPopMatrix(); + glPushMatrix(); + glScalef(1 / ss, 1 / ss, 1 / ss); + glTranslatef(0, 24 * scale, 0); + body.render(scale); + leg0.render(scale); + leg1.render(scale); + leg2.render(scale); + leg3.render(scale); + glPopMatrix(); + } else { + head.render(scale); + body.render(scale); + leg0.render(scale); + leg1.render(scale); + leg2.render(scale); + leg3.render(scale); + } +} + +void QuadrupedModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) +{ + head.xRot = xRot / (180.f / Mth::PI); + head.yRot = yRot / (180.f / Mth::PI); + body.xRot = 90 / (180.f / Mth::PI); + + const float pend = Mth::cos(time * 0.6662f) * 1.4f * r; + leg0.xRot = pend; + leg1.xRot = -pend; + leg2.xRot = -pend; + leg3.xRot = pend; +} diff --git a/src/client/model/QuadrupedModel.h b/src/client/model/QuadrupedModel.h index 0cce639..6758e4b 100755 --- a/src/client/model/QuadrupedModel.h +++ b/src/client/model/QuadrupedModel.h @@ -1,28 +1,28 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__QuadrupedModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__QuadrupedModel_H__ - -//package net.minecraft.client.model; - -#include "Model.h" -#include "geom/ModelPart.h" - -class Entity; - -class QuadrupedModel: public Model -{ -public: - QuadrupedModel(int legSize, float g); - - //@Override - void render(QuadrupedModel* model, float scale); - void render(Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale); - - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); - - ModelPart head, hair, body, leg0, leg1, leg2, leg3; -protected: - float yHeadOffs; - float zHeadOffs; -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__QuadrupedModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__QuadrupedModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__QuadrupedModel_H__ + +//package net.minecraft.client.model; + +#include "Model.h" +#include "geom/ModelPart.h" + +class Entity; + +class QuadrupedModel: public Model +{ +public: + QuadrupedModel(int legSize, float g); + + //@Override + void render(QuadrupedModel* model, float scale); + void render(Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale); + + void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); + + ModelPart head, hair, body, leg0, leg1, leg2, leg3; +protected: + float yHeadOffs; + float zHeadOffs; +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__QuadrupedModel_H__*/ diff --git a/src/client/model/SheepFurModel.cpp b/src/client/model/SheepFurModel.cpp index 0dd5368..146308a 100755 --- a/src/client/model/SheepFurModel.cpp +++ b/src/client/model/SheepFurModel.cpp @@ -1,49 +1,49 @@ -#include "SheepFurModel.h" -#include "geom/ModelPart.h" -#include "../../world/entity/Mob.h" -#include "../../world/entity/animal/Sheep.h" - -SheepFurModel::SheepFurModel() -: super(12, 0) -{ - head = ModelPart(this, 0, 0); - head.addBox(-3, -4, -4, 6, 6, 6, 0.6f); // Head - head.setPos(0, 12 - 6, -8); - - body = ModelPart(this, 28, 8); - body.addBox(-4, -10, -7, 8, 16, 6, 1.75f); // Body - body.setPos(0, 11 + 6 - 12, 2); - - float g = 0.5f; - leg0 = ModelPart(this, 0, 16); - leg0.addBox(-2, 0, -2, 4, 6, 4, g); // Leg0 - leg0.setPos(-3, 18 + 6 - 12, 7); - - leg1 = ModelPart(this, 0, 16); - leg1.addBox(-2, 0, -2, 4, 6, 4, g); // Leg1 - leg1.setPos(3, 18 + 6 - 12, 7); - - leg2 = ModelPart(this, 0, 16); - leg2.addBox(-2, 0, -2, 4, 6, 4, g); // Leg2 - leg2.setPos(-3, 18 + 6 - 12, -5); - - leg3 = ModelPart(this, 0, 16); - leg3.addBox(-2, 0, -2, 4, 6, 4, g); // Leg3 - leg3.setPos(3, 18 + 6 - 12, -5); -} - -void SheepFurModel::prepareMobModel( Mob* mob, float time, float r, float a ) -{ - super::prepareMobModel(mob, time, r, a); - - Sheep* sheep = (Sheep*) mob; - head.y = 6 + sheep->getHeadEatPositionScale(a) * 9.f; - headXRot = sheep->getHeadEatAngleScale(a); -} - -void SheepFurModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) -{ - super::setupAnim(time, r, bob, yRot, xRot, scale); - - head.xRot = headXRot; -} +#include "SheepFurModel.h" +#include "geom/ModelPart.h" +#include "../../world/entity/Mob.h" +#include "../../world/entity/animal/Sheep.h" + +SheepFurModel::SheepFurModel() +: super(12, 0) +{ + head = ModelPart(this, 0, 0); + head.addBox(-3, -4, -4, 6, 6, 6, 0.6f); // Head + head.setPos(0, 12 - 6, -8); + + body = ModelPart(this, 28, 8); + body.addBox(-4, -10, -7, 8, 16, 6, 1.75f); // Body + body.setPos(0, 11 + 6 - 12, 2); + + float g = 0.5f; + leg0 = ModelPart(this, 0, 16); + leg0.addBox(-2, 0, -2, 4, 6, 4, g); // Leg0 + leg0.setPos(-3, 18 + 6 - 12, 7); + + leg1 = ModelPart(this, 0, 16); + leg1.addBox(-2, 0, -2, 4, 6, 4, g); // Leg1 + leg1.setPos(3, 18 + 6 - 12, 7); + + leg2 = ModelPart(this, 0, 16); + leg2.addBox(-2, 0, -2, 4, 6, 4, g); // Leg2 + leg2.setPos(-3, 18 + 6 - 12, -5); + + leg3 = ModelPart(this, 0, 16); + leg3.addBox(-2, 0, -2, 4, 6, 4, g); // Leg3 + leg3.setPos(3, 18 + 6 - 12, -5); +} + +void SheepFurModel::prepareMobModel( Mob* mob, float time, float r, float a ) +{ + super::prepareMobModel(mob, time, r, a); + + Sheep* sheep = (Sheep*) mob; + head.y = 6 + sheep->getHeadEatPositionScale(a) * 9.f; + headXRot = sheep->getHeadEatAngleScale(a); +} + +void SheepFurModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) +{ + super::setupAnim(time, r, bob, yRot, xRot, scale); + + head.xRot = headXRot; +} diff --git a/src/client/model/SheepFurModel.h b/src/client/model/SheepFurModel.h index afc0b0f..f97b64d 100755 --- a/src/client/model/SheepFurModel.h +++ b/src/client/model/SheepFurModel.h @@ -1,24 +1,24 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__SheepFurModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__SheepFurModel_H__ - -//package net.minecraft.client.model; - -#include "QuadrupedModel.h" - -class SheepFurModel: public QuadrupedModel -{ - typedef QuadrupedModel super; - -public: - SheepFurModel(); - - /*@Override*/ - void prepareMobModel(Mob* mob, float time, float r, float a); - - /*@Override*/ - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); -private: - float headXRot; -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__SheepFurModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__SheepFurModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__SheepFurModel_H__ + +//package net.minecraft.client.model; + +#include "QuadrupedModel.h" + +class SheepFurModel: public QuadrupedModel +{ + typedef QuadrupedModel super; + +public: + SheepFurModel(); + + /*@Override*/ + void prepareMobModel(Mob* mob, float time, float r, float a); + + /*@Override*/ + void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); +private: + float headXRot; +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__SheepFurModel_H__*/ diff --git a/src/client/model/SheepModel.cpp b/src/client/model/SheepModel.cpp index 2dac992..d17f91e 100755 --- a/src/client/model/SheepModel.cpp +++ b/src/client/model/SheepModel.cpp @@ -1,32 +1,32 @@ -#include "SheepModel.h" -#include "geom/ModelPart.h" -#include "../../world/entity/Mob.h" -#include "../../world/entity/animal/Sheep.h" - -SheepModel::SheepModel() -: super(12, 0) -{ - head = ModelPart(this, 0, 0); - head.addBox(-3, -4, -6, 6, 6, 8, 0); // Head - head.setPos(0, 12 - 6, -8); - - body = ModelPart(this, 28, 8); - body.addBox(-4, -10, -7, 8, 16, 6, 0); // Body - body.setPos(0, 11 + 6 - 12, 2); -} - -void SheepModel::prepareMobModel( Mob* mob, float time, float r, float a ) -{ - super::prepareMobModel(mob, time, r, a); - - Sheep* sheep = (Sheep*) mob; - head.y = 6 + sheep->getHeadEatPositionScale(a) * 9.f; - headXRot = sheep->getHeadEatAngleScale(a); -} - -void SheepModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) -{ - super::setupAnim(time, r, bob, yRot, xRot, scale); - - head.xRot = headXRot; -} +#include "SheepModel.h" +#include "geom/ModelPart.h" +#include "../../world/entity/Mob.h" +#include "../../world/entity/animal/Sheep.h" + +SheepModel::SheepModel() +: super(12, 0) +{ + head = ModelPart(this, 0, 0); + head.addBox(-3, -4, -6, 6, 6, 8, 0); // Head + head.setPos(0, 12 - 6, -8); + + body = ModelPart(this, 28, 8); + body.addBox(-4, -10, -7, 8, 16, 6, 0); // Body + body.setPos(0, 11 + 6 - 12, 2); +} + +void SheepModel::prepareMobModel( Mob* mob, float time, float r, float a ) +{ + super::prepareMobModel(mob, time, r, a); + + Sheep* sheep = (Sheep*) mob; + head.y = 6 + sheep->getHeadEatPositionScale(a) * 9.f; + headXRot = sheep->getHeadEatAngleScale(a); +} + +void SheepModel::setupAnim( float time, float r, float bob, float yRot, float xRot, float scale ) +{ + super::setupAnim(time, r, bob, yRot, xRot, scale); + + head.xRot = headXRot; +} diff --git a/src/client/model/SheepModel.h b/src/client/model/SheepModel.h index 7b2bc63..56da46d 100755 --- a/src/client/model/SheepModel.h +++ b/src/client/model/SheepModel.h @@ -1,23 +1,23 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__SheepModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__SheepModel_H__ - -//package net.minecraft.client.model; - -#include "QuadrupedModel.h" - -class SheepModel: public QuadrupedModel -{ - typedef QuadrupedModel super; -public: - SheepModel(); - - /*@Override*/ - void prepareMobModel(Mob* mob, float time, float r, float a); - - /*@Override*/ - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); -private: - float headXRot; -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__SheepModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__SheepModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__SheepModel_H__ + +//package net.minecraft.client.model; + +#include "QuadrupedModel.h" + +class SheepModel: public QuadrupedModel +{ + typedef QuadrupedModel super; +public: + SheepModel(); + + /*@Override*/ + void prepareMobModel(Mob* mob, float time, float r, float a); + + /*@Override*/ + void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); +private: + float headXRot; +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__SheepModel_H__*/ diff --git a/src/client/model/SignModel.h b/src/client/model/SignModel.h index b2985c1..3b102f5 100755 --- a/src/client/model/SignModel.h +++ b/src/client/model/SignModel.h @@ -1,32 +1,32 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__SignModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__SignModel_H__ - -//package net.minecraft.client.model; - -#include "Model.h" -#include "geom/ModelPart.h" - -class SignModel: public Model -{ -public: - SignModel() - : cube(0, 0), - cube2(0, 14) - { - cube.addBox(-12, -14, -1, 24, 12, 2, 0); - cube2.addBox(-1, -2, -1, 2, 14, 2, 0); - - cube.setModel(this); - cube2.setModel(this); - } - - void render() { - cube.render(1 / 16.0f); - cube2.render(1 / 16.0f); - } - - ModelPart cube; - ModelPart cube2; -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__SignModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__SignModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__SignModel_H__ + +//package net.minecraft.client.model; + +#include "Model.h" +#include "geom/ModelPart.h" + +class SignModel: public Model +{ +public: + SignModel() + : cube(0, 0), + cube2(0, 14) + { + cube.addBox(-12, -14, -1, 24, 12, 2, 0); + cube2.addBox(-1, -2, -1, 2, 14, 2, 0); + + cube.setModel(this); + cube2.setModel(this); + } + + void render() { + cube.render(1 / 16.0f); + cube2.render(1 / 16.0f); + } + + ModelPart cube; + ModelPart cube2; +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__SignModel_H__*/ diff --git a/src/client/model/SkeletonModel.h b/src/client/model/SkeletonModel.h index 86162c9..c919db2 100755 --- a/src/client/model/SkeletonModel.h +++ b/src/client/model/SkeletonModel.h @@ -1,46 +1,46 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__SkeletonModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__SkeletonModel_H__ - -//package net.minecraft.client.model; - -#include "ZombieModel.h" -#include "geom/ModelPart.h" - -class SkeletonModel: public ZombieModel -{ - typedef ZombieModel super; -public: - SkeletonModel() - { - float g = 0; - - arm0 = ModelPart(24 + 16, 16); - arm0.setModel(this); - arm0.addBox(-1, -2, -1, 2, 12, 2, g); // Arm0 - arm0.setPos(-5, 2, 0); - - arm1 = ModelPart(24 + 16, 16); - arm1.setModel(this); - arm1.mirror = true; - arm1.addBox(-1, -2, -1, 2, 12, 2, g); // Arm1 - arm1.setPos(5, 2, 0); - - leg0 = ModelPart(0, 16); - leg0.setModel(this); - leg0.addBox(-1, 0, -1, 2, 12, 2, g); // Leg0 - leg0.setPos(-2, 12, 0); - - leg1 = ModelPart(0, 16); - leg1.setModel(this); - leg1.mirror = true; - leg1.addBox(-1, 0, -1, 2, 12, 2, g); // Leg1 - leg1.setPos(2, 12, 0); - } - - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { - bowAndArrow = true; - super::setupAnim(time, r, bob, yRot, xRot, scale); - } -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__SkeletonModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__SkeletonModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__SkeletonModel_H__ + +//package net.minecraft.client.model; + +#include "ZombieModel.h" +#include "geom/ModelPart.h" + +class SkeletonModel: public ZombieModel +{ + typedef ZombieModel super; +public: + SkeletonModel() + { + float g = 0; + + arm0 = ModelPart(24 + 16, 16); + arm0.setModel(this); + arm0.addBox(-1, -2, -1, 2, 12, 2, g); // Arm0 + arm0.setPos(-5, 2, 0); + + arm1 = ModelPart(24 + 16, 16); + arm1.setModel(this); + arm1.mirror = true; + arm1.addBox(-1, -2, -1, 2, 12, 2, g); // Arm1 + arm1.setPos(5, 2, 0); + + leg0 = ModelPart(0, 16); + leg0.setModel(this); + leg0.addBox(-1, 0, -1, 2, 12, 2, g); // Leg0 + leg0.setPos(-2, 12, 0); + + leg1 = ModelPart(0, 16); + leg1.setModel(this); + leg1.mirror = true; + leg1.addBox(-1, 0, -1, 2, 12, 2, g); // Leg1 + leg1.setPos(2, 12, 0); + } + + void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { + bowAndArrow = true; + super::setupAnim(time, r, bob, yRot, xRot, scale); + } +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__SkeletonModel_H__*/ diff --git a/src/client/model/SpiderModel.h b/src/client/model/SpiderModel.h index f02c0ce..1f3a8e6 100755 --- a/src/client/model/SpiderModel.h +++ b/src/client/model/SpiderModel.h @@ -1,168 +1,168 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__SpiderModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__SpiderModel_H__ - -//package net.minecraft.client.model; - -#include "Model.h" - -#include "geom/ModelPart.h" -#include "../../world/entity/Entity.h" -#include "../../util/Mth.h" - -class SpiderModel: public Model -{ - typedef Model super; -public: - ModelPart head, body0, body1, leg0, leg1, leg2, leg3, leg4, leg5, leg6, leg7; - - SpiderModel() - : head(32, 4), - body0(0, 0), - body1(0, 12), - leg0(18, 0), - leg1(18, 0), - leg2(18, 0), - leg3(18, 0), - leg4(18, 0), - leg5(18, 0), - leg6(18, 0), - leg7(18, 0) - { - float g = 0; - - head.setModel(this); - body0.setModel(this); - body0.setModel(this); - leg0.setModel(this); - leg1.setModel(this); - leg2.setModel(this); - leg3.setModel(this); - leg4.setModel(this); - leg5.setModel(this); - leg6.setModel(this); - leg7.setModel(this); - - float yo = 18 + 6 - 9; - - head.addBox(-4, -4, -8, 8, 8, 8, g); // Head - head.setPos(0, 0 + yo, -3); - - body0.addBox(-3, -3, -3, 6, 6, 6, g); // Body - body0.setPos(0, yo, 0); - - body1.addBox(-5, -4, -6, 10, 8, 12, g); // Body - body1.setPos(0, 0 + yo, 3 + 6); - - leg0.addBox(-15, -1, -1, 16, 2, 2, g); // Leg0 - leg0.setPos(-4, 0 + yo, 2); - - leg1.addBox(-1, -1, -1, 16, 2, 2, g); // Leg1 - leg1.setPos(4, 0 + yo, 2); - - leg2.addBox(-15, -1, -1, 16, 2, 2, g); // Leg2 - leg2.setPos(-4, 0 + yo, 1); - - leg3.addBox(-1, -1, -1, 16, 2, 2, g); // Leg3 - leg3.setPos(4, 0 + yo, 1); - - leg4.addBox(-15, -1, -1, 16, 2, 2, g); // Leg0 - leg4.setPos(-4, 0 + yo, 0); - - leg5.addBox(-1, -1, -1, 16, 2, 2, g); // Leg1 - leg5.setPos(4, 0 + yo, 0); - - leg6.addBox(-15, -1, -1, 16, 2, 2, g); // Leg2 - leg6.setPos(-4, 0 + yo, -1); - - leg7.addBox(-1, -1, -1, 16, 2, 2, g); // Leg3 - leg7.setPos(4, 0 + yo, -1); - } - - /*@Override*/ - void render(Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale) { - setupAnim(time, r, bob, yRot, xRot, scale); - - head.render(scale); - body0.render(scale); - body1.render(scale); - leg0.render(scale); - leg1.render(scale); - leg2.render(scale); - leg3.render(scale); - leg4.render(scale); - leg5.render(scale); - leg6.render(scale); - leg7.render(scale); - } - - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { - head.yRot = yRot / (float) (180 / Mth::PI); - head.xRot = xRot / (float) (180 / Mth::PI); - - const float sr = (float) Mth::PI / 4.0f; - leg0.zRot = -sr; - leg1.zRot = sr; - - leg2.zRot = -sr * 0.74f; - leg3.zRot = sr * 0.74f; - - leg4.zRot = -sr * 0.74f; - leg5.zRot = sr * 0.74f; - - leg6.zRot = -sr; - leg7.zRot = sr; - - //float ro = -(float) Mth::PI / 2.0f * 0; - const float ur = (float) Mth::PI / 8.0f; - const float ur2 = sr; - - leg0.yRot = ur2; - leg1.yRot = -ur2; - leg2.yRot = ur; - leg3.yRot = -ur; - leg4.yRot = -ur; - leg5.yRot = +ur; - leg6.yRot = -ur2; - leg7.yRot = +ur2; - - //static Stopwatch w; - //w.start(); - - float c0 = -((float) Mth::cos(time * 0.6662f * 2 + Mth::PI * 2 * 0 / 4.0f) * 0.4f) * r; - float c1 = -((float) Mth::cos(time * 0.6662f * 2 + Mth::PI * 2 * 2 / 4.0f) * 0.4f) * r; - float c2 = -((float) Mth::cos(time * 0.6662f * 2 + Mth::PI * 2 * 1 / 4.0f) * 0.4f) * r; - float c3 = -((float) Mth::cos(time * 0.6662f * 2 + Mth::PI * 2 * 3 / 4.0f) * 0.4f) * r; - - //LOGI("spider: %f, %f, %f, %f\n", c0, c1, c2, c3); - - float s0 = std::abs((float) Mth::sin(time * 0.6662f + Mth::PI * 2 * 0 / 4.0f) * 0.4f) * r; - float s1 = std::abs((float) Mth::sin(time * 0.6662f + Mth::PI * 2 * 2 / 4.0f) * 0.4f) * r; - float s2 = std::abs((float) Mth::sin(time * 0.6662f + Mth::PI * 2 * 1 / 4.0f) * 0.4f) * r; - float s3 = std::abs((float) Mth::sin(time * 0.6662f + Mth::PI * 2 * 3 / 4.0f) * 0.4f) * r; - - //w.stop(); - //w.printEvery(100); - - //LOGI("spiddy: %f, %f, %f, %f\n", s0, s1, s2, s3); - - leg0.yRot += c0; - leg1.yRot -= c0; - leg2.yRot += c1; - leg3.yRot -= c1; - leg4.yRot += c2; - leg5.yRot -= c2; - leg6.yRot += c3; - leg7.yRot -= c3; - - leg0.zRot += s0; - leg1.zRot -= s0; - leg2.zRot += s1; - leg3.zRot -= s1; - leg4.zRot += s2; - leg5.zRot -= s2; - leg6.zRot += s3; - leg7.zRot -= s3; - } -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__SpiderModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__SpiderModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__SpiderModel_H__ + +//package net.minecraft.client.model; + +#include "Model.h" + +#include "geom/ModelPart.h" +#include "../../world/entity/Entity.h" +#include "../../util/Mth.h" + +class SpiderModel: public Model +{ + typedef Model super; +public: + ModelPart head, body0, body1, leg0, leg1, leg2, leg3, leg4, leg5, leg6, leg7; + + SpiderModel() + : head(32, 4), + body0(0, 0), + body1(0, 12), + leg0(18, 0), + leg1(18, 0), + leg2(18, 0), + leg3(18, 0), + leg4(18, 0), + leg5(18, 0), + leg6(18, 0), + leg7(18, 0) + { + float g = 0; + + head.setModel(this); + body0.setModel(this); + body0.setModel(this); + leg0.setModel(this); + leg1.setModel(this); + leg2.setModel(this); + leg3.setModel(this); + leg4.setModel(this); + leg5.setModel(this); + leg6.setModel(this); + leg7.setModel(this); + + float yo = 18 + 6 - 9; + + head.addBox(-4, -4, -8, 8, 8, 8, g); // Head + head.setPos(0, 0 + yo, -3); + + body0.addBox(-3, -3, -3, 6, 6, 6, g); // Body + body0.setPos(0, yo, 0); + + body1.addBox(-5, -4, -6, 10, 8, 12, g); // Body + body1.setPos(0, 0 + yo, 3 + 6); + + leg0.addBox(-15, -1, -1, 16, 2, 2, g); // Leg0 + leg0.setPos(-4, 0 + yo, 2); + + leg1.addBox(-1, -1, -1, 16, 2, 2, g); // Leg1 + leg1.setPos(4, 0 + yo, 2); + + leg2.addBox(-15, -1, -1, 16, 2, 2, g); // Leg2 + leg2.setPos(-4, 0 + yo, 1); + + leg3.addBox(-1, -1, -1, 16, 2, 2, g); // Leg3 + leg3.setPos(4, 0 + yo, 1); + + leg4.addBox(-15, -1, -1, 16, 2, 2, g); // Leg0 + leg4.setPos(-4, 0 + yo, 0); + + leg5.addBox(-1, -1, -1, 16, 2, 2, g); // Leg1 + leg5.setPos(4, 0 + yo, 0); + + leg6.addBox(-15, -1, -1, 16, 2, 2, g); // Leg2 + leg6.setPos(-4, 0 + yo, -1); + + leg7.addBox(-1, -1, -1, 16, 2, 2, g); // Leg3 + leg7.setPos(4, 0 + yo, -1); + } + + /*@Override*/ + void render(Entity* entity, float time, float r, float bob, float yRot, float xRot, float scale) { + setupAnim(time, r, bob, yRot, xRot, scale); + + head.render(scale); + body0.render(scale); + body1.render(scale); + leg0.render(scale); + leg1.render(scale); + leg2.render(scale); + leg3.render(scale); + leg4.render(scale); + leg5.render(scale); + leg6.render(scale); + leg7.render(scale); + } + + void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { + head.yRot = yRot / (float) (180 / Mth::PI); + head.xRot = xRot / (float) (180 / Mth::PI); + + const float sr = (float) Mth::PI / 4.0f; + leg0.zRot = -sr; + leg1.zRot = sr; + + leg2.zRot = -sr * 0.74f; + leg3.zRot = sr * 0.74f; + + leg4.zRot = -sr * 0.74f; + leg5.zRot = sr * 0.74f; + + leg6.zRot = -sr; + leg7.zRot = sr; + + //float ro = -(float) Mth::PI / 2.0f * 0; + const float ur = (float) Mth::PI / 8.0f; + const float ur2 = sr; + + leg0.yRot = ur2; + leg1.yRot = -ur2; + leg2.yRot = ur; + leg3.yRot = -ur; + leg4.yRot = -ur; + leg5.yRot = +ur; + leg6.yRot = -ur2; + leg7.yRot = +ur2; + + //static Stopwatch w; + //w.start(); + + float c0 = -((float) Mth::cos(time * 0.6662f * 2 + Mth::PI * 2 * 0 / 4.0f) * 0.4f) * r; + float c1 = -((float) Mth::cos(time * 0.6662f * 2 + Mth::PI * 2 * 2 / 4.0f) * 0.4f) * r; + float c2 = -((float) Mth::cos(time * 0.6662f * 2 + Mth::PI * 2 * 1 / 4.0f) * 0.4f) * r; + float c3 = -((float) Mth::cos(time * 0.6662f * 2 + Mth::PI * 2 * 3 / 4.0f) * 0.4f) * r; + + //LOGI("spider: %f, %f, %f, %f\n", c0, c1, c2, c3); + + float s0 = std::abs((float) Mth::sin(time * 0.6662f + Mth::PI * 2 * 0 / 4.0f) * 0.4f) * r; + float s1 = std::abs((float) Mth::sin(time * 0.6662f + Mth::PI * 2 * 2 / 4.0f) * 0.4f) * r; + float s2 = std::abs((float) Mth::sin(time * 0.6662f + Mth::PI * 2 * 1 / 4.0f) * 0.4f) * r; + float s3 = std::abs((float) Mth::sin(time * 0.6662f + Mth::PI * 2 * 3 / 4.0f) * 0.4f) * r; + + //w.stop(); + //w.printEvery(100); + + //LOGI("spiddy: %f, %f, %f, %f\n", s0, s1, s2, s3); + + leg0.yRot += c0; + leg1.yRot -= c0; + leg2.yRot += c1; + leg3.yRot -= c1; + leg4.yRot += c2; + leg5.yRot -= c2; + leg6.yRot += c3; + leg7.yRot -= c3; + + leg0.zRot += s0; + leg1.zRot -= s0; + leg2.zRot += s1; + leg3.zRot -= s1; + leg4.zRot += s2; + leg5.zRot -= s2; + leg6.zRot += s3; + leg7.zRot -= s3; + } +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__SpiderModel_H__*/ diff --git a/src/client/model/ZombieModel.h b/src/client/model/ZombieModel.h index e0f404b..6de67a9 100755 --- a/src/client/model/ZombieModel.h +++ b/src/client/model/ZombieModel.h @@ -1,37 +1,37 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__ZombieModel_H__ -#define NET_MINECRAFT_CLIENT_MODEL__ZombieModel_H__ - -//package net.minecraft.client.model; - -#include "HumanoidModel.h" - -class ZombieModel: public HumanoidModel -{ - typedef HumanoidModel super; -public: - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { - super::setupAnim(time, r, bob, yRot, xRot, scale); - if(!holdingLeftHand && !holdingRightHand) { - float attack2 = (float) Mth::sin(attackTime * Mth::PI); - float attack = (float) Mth::sin((1 - (1 - attackTime) * (1 - attackTime)) * Mth::PI); - arm0.zRot = 0;// -attack; - arm1.zRot = 0;// +attack; - arm0.yRot = -(0.1f - attack2 * 0.6f); - arm1.yRot = +(0.1f - attack2 * 0.6f); - arm0.xRot = -Mth::PI / 2.0f; - arm1.xRot = -Mth::PI / 2.0f; - arm0.xRot -= attack2 * 1.2f - attack * 0.4f; - arm1.xRot -= attack2 * 1.2f - attack * 0.4f; - - // body.yRot = attack; - const float zBob = Mth::cos(bob * 0.09f) * 0.05f + 0.05f; - const float xBob = Mth::sin(bob * 0.067f) * 0.05f; - arm0.zRot += zBob; - arm1.zRot -= zBob; - arm0.xRot += xBob; - arm1.xRot -= xBob; - } - } -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__ZombieModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__ZombieModel_H__ +#define NET_MINECRAFT_CLIENT_MODEL__ZombieModel_H__ + +//package net.minecraft.client.model; + +#include "HumanoidModel.h" + +class ZombieModel: public HumanoidModel +{ + typedef HumanoidModel super; +public: + void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { + super::setupAnim(time, r, bob, yRot, xRot, scale); + if(!holdingLeftHand && !holdingRightHand) { + float attack2 = (float) Mth::sin(attackTime * Mth::PI); + float attack = (float) Mth::sin((1 - (1 - attackTime) * (1 - attackTime)) * Mth::PI); + arm0.zRot = 0;// -attack; + arm1.zRot = 0;// +attack; + arm0.yRot = -(0.1f - attack2 * 0.6f); + arm1.yRot = +(0.1f - attack2 * 0.6f); + arm0.xRot = -Mth::PI / 2.0f; + arm1.xRot = -Mth::PI / 2.0f; + arm0.xRot -= attack2 * 1.2f - attack * 0.4f; + arm1.xRot -= attack2 * 1.2f - attack * 0.4f; + + // body.yRot = attack; + const float zBob = Mth::cos(bob * 0.09f) * 0.05f + 0.05f; + const float xBob = Mth::sin(bob * 0.067f) * 0.05f; + arm0.zRot += zBob; + arm1.zRot -= zBob; + arm0.xRot += xBob; + arm1.xRot -= xBob; + } + } +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__ZombieModel_H__*/ diff --git a/src/client/model/geom/Cube.cpp b/src/client/model/geom/Cube.cpp index bb3c030..cbd04f2 100755 --- a/src/client/model/geom/Cube.cpp +++ b/src/client/model/geom/Cube.cpp @@ -1,75 +1,75 @@ -#include "Cube.h" -#include "ModelPart.h" -#include "../../renderer/Tesselator.h" -#include "../../renderer/gles.h" -#include "../../../util/Mth.h" - -Cube::Cube(ModelPart* modelPart, int xTexOffs, int yTexOffs, float x0, float y0, float z0, int w, int h, int d, float g) -: x0(x0), - y0(y0), - z0(z0), - x1(x0 + w), - y1(y0 + h), - z1(z0 + d) -{ - float x1 = this->x1; - float y1 = this->y1; - float z1 = this->z1; - - x0 -= g; - y0 -= g; - z0 -= g; - x1 += g; - y1 += g; - z1 += g; - - if (modelPart->mirror) { - float tmp = x1; - x1 = x0; - x0 = tmp; - } - - vertices[0] = VertexPT(x0, y0, z0, 0, 0); - vertices[1] = VertexPT(x1, y0, z0, 0, 8); - vertices[2] = VertexPT(x1, y1, z0, 8, 8); - vertices[3] = VertexPT(x0, y1, z0, 8, 0); - - vertices[4] = VertexPT(x0, y0, z1, 0, 0); - vertices[5] = VertexPT(x1, y0, z1, 0, 8); - vertices[6] = VertexPT(x1, y1, z1, 8, 8); - vertices[7] = VertexPT(x0, y1, z1, 8, 0); - - VertexPT* ptr = vertices - 1; - VertexPT* u0 = ++ptr; - VertexPT* u1 = ++ptr; - VertexPT* u2 = ++ptr; - VertexPT* u3 = ++ptr; - - VertexPT* l0 = ++ptr; - VertexPT* l1 = ++ptr; - VertexPT* l2 = ++ptr; - VertexPT* l3 = ++ptr; - - polygons[0] = PolygonQuad(l1, u1, u2, l2, xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + d, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Right - polygons[1] = PolygonQuad(u0, l0, l3, u3, xTexOffs + 0, yTexOffs + d, xTexOffs + d, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Left - polygons[2] = PolygonQuad(l1, l0, u0, u1, xTexOffs + d, yTexOffs + 0, xTexOffs + d + w, yTexOffs + d, modelPart->xTexSize, modelPart->yTexSize); // Up - polygons[3] = PolygonQuad(u2, u3, l3, l2, xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + w, yTexOffs, modelPart->xTexSize, modelPart->yTexSize); // Down - polygons[4] = PolygonQuad(u1, u0, u3, u2, xTexOffs + d, yTexOffs + d, xTexOffs + d + w, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Front - polygons[5] = PolygonQuad(l0, l1, l2, l3, xTexOffs + d + w + d, yTexOffs + d, xTexOffs + d + w + d + w, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Back - - if (modelPart->mirror) { - for (int i = 0; i < 6; i++) - polygons[i].mirror(); - } -} - -void Cube::compile(Tesselator& t, float scale) { - for (int i = 0; i < 6; i++) { - polygons[i].render(t, scale); - } -} - -Cube* Cube::setId(const std::string& id) { - this->id = id; - return this; +#include "Cube.h" +#include "ModelPart.h" +#include "../../renderer/Tesselator.h" +#include "../../renderer/gles.h" +#include "../../../util/Mth.h" + +Cube::Cube(ModelPart* modelPart, int xTexOffs, int yTexOffs, float x0, float y0, float z0, int w, int h, int d, float g) +: x0(x0), + y0(y0), + z0(z0), + x1(x0 + w), + y1(y0 + h), + z1(z0 + d) +{ + float x1 = this->x1; + float y1 = this->y1; + float z1 = this->z1; + + x0 -= g; + y0 -= g; + z0 -= g; + x1 += g; + y1 += g; + z1 += g; + + if (modelPart->mirror) { + float tmp = x1; + x1 = x0; + x0 = tmp; + } + + vertices[0] = VertexPT(x0, y0, z0, 0, 0); + vertices[1] = VertexPT(x1, y0, z0, 0, 8); + vertices[2] = VertexPT(x1, y1, z0, 8, 8); + vertices[3] = VertexPT(x0, y1, z0, 8, 0); + + vertices[4] = VertexPT(x0, y0, z1, 0, 0); + vertices[5] = VertexPT(x1, y0, z1, 0, 8); + vertices[6] = VertexPT(x1, y1, z1, 8, 8); + vertices[7] = VertexPT(x0, y1, z1, 8, 0); + + VertexPT* ptr = vertices - 1; + VertexPT* u0 = ++ptr; + VertexPT* u1 = ++ptr; + VertexPT* u2 = ++ptr; + VertexPT* u3 = ++ptr; + + VertexPT* l0 = ++ptr; + VertexPT* l1 = ++ptr; + VertexPT* l2 = ++ptr; + VertexPT* l3 = ++ptr; + + polygons[0] = PolygonQuad(l1, u1, u2, l2, xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + d, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Right + polygons[1] = PolygonQuad(u0, l0, l3, u3, xTexOffs + 0, yTexOffs + d, xTexOffs + d, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Left + polygons[2] = PolygonQuad(l1, l0, u0, u1, xTexOffs + d, yTexOffs + 0, xTexOffs + d + w, yTexOffs + d, modelPart->xTexSize, modelPart->yTexSize); // Up + polygons[3] = PolygonQuad(u2, u3, l3, l2, xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + w, yTexOffs, modelPart->xTexSize, modelPart->yTexSize); // Down + polygons[4] = PolygonQuad(u1, u0, u3, u2, xTexOffs + d, yTexOffs + d, xTexOffs + d + w, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Front + polygons[5] = PolygonQuad(l0, l1, l2, l3, xTexOffs + d + w + d, yTexOffs + d, xTexOffs + d + w + d + w, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Back + + if (modelPart->mirror) { + for (int i = 0; i < 6; i++) + polygons[i].mirror(); + } +} + +void Cube::compile(Tesselator& t, float scale) { + for (int i = 0; i < 6; i++) { + polygons[i].render(t, scale); + } +} + +Cube* Cube::setId(const std::string& id) { + this->id = id; + return this; } \ No newline at end of file diff --git a/src/client/model/geom/Cube.h b/src/client/model/geom/Cube.h index 8638006..5b5cf0d 100755 --- a/src/client/model/geom/Cube.h +++ b/src/client/model/geom/Cube.h @@ -1,29 +1,29 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL_GEOM__Cube_H__ -#define NET_MINECRAFT_CLIENT_MODEL_GEOM__Cube_H__ - -//package net.minecraft.client.model; - -#include -#include "Polygon.h" - -class Tesselator; -class ModelPart; - -class Cube -{ -public: - Cube(ModelPart* modelPart, int xTexOffs, int yTexOffs, float x0, float y0, float z0, int w, int h, int d, float g); - - void compile(Tesselator& t, float scale); - Cube* setId(const std::string& id); -private: - - VertexPT vertices[8]; - PolygonQuad polygons[6]; - std::string id; - const float x0, y0, z0, x1, y1, z1; - - friend class ModelPart; -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL_GEOM__Cube_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL_GEOM__Cube_H__ +#define NET_MINECRAFT_CLIENT_MODEL_GEOM__Cube_H__ + +//package net.minecraft.client.model; + +#include +#include "Polygon.h" + +class Tesselator; +class ModelPart; + +class Cube +{ +public: + Cube(ModelPart* modelPart, int xTexOffs, int yTexOffs, float x0, float y0, float z0, int w, int h, int d, float g); + + void compile(Tesselator& t, float scale); + Cube* setId(const std::string& id); +private: + + VertexPT vertices[8]; + PolygonQuad polygons[6]; + std::string id; + const float x0, y0, z0, x1, y1, z1; + + friend class ModelPart; +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL_GEOM__Cube_H__*/ diff --git a/src/client/model/geom/ModelPart.cpp b/src/client/model/geom/ModelPart.cpp index 7fa8f70..6dbbef6 100755 --- a/src/client/model/geom/ModelPart.cpp +++ b/src/client/model/geom/ModelPart.cpp @@ -1,303 +1,303 @@ -#include "ModelPart.h" -#include "Cube.h" -#include "../Model.h" -#include "../../renderer/Tesselator.h" -#include "../../../util/Mth.h" - - -ModelPart::ModelPart( const std::string& id ) -: id(id), - model(NULL), - xTexOffs(0), - yTexOffs(0) -{ - _init(); -} - -ModelPart::ModelPart( int xTexOffs /*= 0*/, int yTexOffs /*= 0*/ ) -: xTexOffs(xTexOffs), - yTexOffs(yTexOffs), - model(NULL) -{ - _init(); -} - -ModelPart::ModelPart( Model* model, int xTexOffs /*= 0*/, int yTexOffs /*= 0*/ ) -: model(model), - xTexOffs(xTexOffs), - yTexOffs(yTexOffs) -{ - _init(); -} - -ModelPart::~ModelPart() { - clear(); -} - -void ModelPart::_init() { - x = y = z = 0; - xRot = yRot = zRot = 0; - list = 0; - mirror = false; - visible = true; - neverRender = false; - compiled = false; - xTexSize = 64; - yTexSize = 32; - - vboId = 0; - glGenBuffers2(1, &vboId); -} - -void ModelPart::setModel(Model* model) { - if (this->model) { - Util::remove(this->model->cubes, this); - } - if (model) { - model->cubes.push_back(this); - setTexSize(model->texWidth, model->texHeight); - } - this->model = model; -} - -void ModelPart::addChild(ModelPart* child) { - children.push_back(child); -} - -ModelPart& ModelPart::addBox(const std::string& id, float x0, float y0, float z0, int w, int h, int d) { - std::string newid = this->id + "." + id; - //TexOffs offs = model.getMapTex(id); //@todo @diff - //texOffs(offs.x, offs.y); - cubes.push_back((new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, 0))->setId(newid)); - return *this; -} - -ModelPart& ModelPart::addBox(float x0, float y0, float z0, int w, int h, int d) { - cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, 0)); - return *this; -} - -void ModelPart::addBox(float x0, float y0, float z0, int w, int h, int d, float g) { - cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, g)); -} - -//This Cube constructor is commented out -//void ModelPart::addTexBox(float x0, float y0, float z0, int w, int h, int d, int tex) { -// cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, tex)); -//} - - -void ModelPart::setPos( float x, float y, float z ) -{ - this->x = x; - this->y = y; - this->z = z; -} - -void ModelPart::render( float scale ) -{ - if (neverRender) return; - if (!visible) return; - if (!compiled) compile(scale); - - if (xRot != 0 || yRot != 0 || zRot != 0) { - glPushMatrix2(); - glTranslatef2(x * scale, y * scale, z * scale); - - const float c = Mth::RADDEG; - if (zRot != 0) glRotatef2(zRot * c, 0.0f, 0.0f, 1.0f); - if (yRot != 0) glRotatef2(yRot * c, 0.0f, 1.0f, 0.0f); - if (xRot != 0) glRotatef2(xRot * c, 1.0f, 0.0f, 0.0f); - - //LOGI("A"); - draw(); - if (!children.empty()) { - for (unsigned int i = 0; i < children.size(); i++) { - children[i]->render(scale); - } - } - - glPopMatrix2(); - } else if (x != 0 || y != 0 || z != 0) { - glTranslatef2(x * scale, y * scale, z * scale); - //LOGI("B"); - draw(); - if (!children.empty()) { - for (unsigned int i = 0; i < children.size(); i++) { - children[i]->render(scale); - } - } - - glTranslatef2(-x * scale, -y * scale, -z * scale); - } else { - //LOGI("C"); - draw(); - if (!children.empty()) { - for (unsigned int i = 0; i < children.size(); i++) { - children[i]->render(scale); - } - } - } -} - - -void ModelPart::renderRollable( float scale ) -{ - if (neverRender) return; - if (!visible) return; - if (!compiled) compile(scale); - - glPushMatrix2(); - glTranslatef2(x * scale, y * scale, z * scale); - - const float c = Mth::RADDEG; - if (yRot != 0) glRotatef2(yRot * c, 0.0f, 1.0f, 0.0f); - if (xRot != 0) glRotatef2(xRot * c, 1.0f, 0.0f, 0.0f); - if (zRot != 0) glRotatef2(zRot * c, 0.0f, 0.0f, 1.0f); - - draw(); - glPopMatrix2(); -} - - -void ModelPart::translateTo( float scale ) -{ - if (neverRender) return; - if (!visible) return; - if (!compiled) compile(scale); - - if (xRot != 0 || yRot != 0 || zRot != 0) { - const float c = Mth::RADDEG; - glTranslatef2(x * scale, y * scale, z * scale); - if (zRot != 0) glRotatef2(zRot * c, 0.0f, 0.0f, 1.0f); - if (yRot != 0) glRotatef2(yRot * c, 0.0f, 1.0f, 0.0f); - if (xRot != 0) glRotatef2(xRot * c, 1.0f, 0.0f, 0.0f); - } else if (x != 0 || y != 0 || z != 0) { - glTranslatef2(x * scale, y * scale, z * scale); - } else { - } -} - - -void ModelPart::compile( float scale ) -{ -#ifndef OPENGL_ES - list = glGenLists(1); - // FIX NORMAL BUG HERE - glNewList(list, GL_COMPILE); -#endif - Tesselator& t = Tesselator::instance; - t.begin(); - t.color(255, 255, 255, 255); - for (int i = 0; i < 6; i++) { - for (unsigned int i = 0; i < cubes.size(); ++i) - cubes[i]->compile(t, scale); - } - t.end(true, vboId); -#ifndef OPENGL_ES - glEndList(); -#endif - compiled = true; -} - -void ModelPart::draw() -{ -#ifdef OPENGL_ES - drawArrayVT_NoState(vboId, cubes.size() * 2 * 3 * 6, 24); -#else - glCallList(list); -#endif -} - -ModelPart& ModelPart::setTexSize(int xs, int ys) { - xTexSize = (float)xs; - yTexSize = (float)ys; - return *this; -} - -ModelPart& ModelPart::texOffs(int xTexOffs, int yTexOffs) { - this->xTexOffs = xTexOffs; - this->yTexOffs = yTexOffs; - return *this; -} - -void ModelPart::mimic(const ModelPart* o) { - x = o->x; - y = o->y; - z = o->z; - - xRot = o->xRot; - yRot = o->yRot; - zRot = o->zRot; -} - -void ModelPart::renderHorrible( float scale ) -{ - if (neverRender) { - return; - } - if (!visible) { - return; - } - //if (!compiled) compile(scale); - - if (xRot != 0 || yRot != 0 || zRot != 0) { - glPushMatrix2(); - glTranslatef2(x * scale, y * scale, z * scale); - if (zRot != 0) glRotatef2(zRot * Mth::RADDEG, 0.0f, 0.0f, 1.0f); - if (yRot != 0) glRotatef2(yRot * Mth::RADDEG, 0.0f, 1.0f, 0.0f); - if (xRot != 0) glRotatef2(xRot * Mth::RADDEG, 1.0f, 0.0f, 0.0f); - - drawSlow(scale); - glPopMatrix2(); - } else if (x != 0 || y != 0 || z != 0) { - glTranslatef2(x * scale, y * scale, z * scale); - drawSlow(scale); - glTranslatef2(-x * scale, -y * scale, -z * scale); - } else { - drawSlow(scale); - } -} - -void ModelPart::drawSlow( float scale ) -{ - Tesselator& t = Tesselator::instance; - t.begin(); - for (int j = 0; j < (int)cubes.size(); ++j) { - Cube* c = cubes[j]; - for (int i = 0; i < 6; i++) { - c->polygons[i].render(t, scale, vboId); - } - } - t.draw(); -} - -void ModelPart::clear() -{ - for (unsigned int i = 0; i < cubes.size(); ++i) - delete cubes[i]; - cubes.clear(); - - setModel(NULL); -} - -ModelPart& ModelPart::operator=( const ModelPart& rhs ) -{ - clear(); - - if (!id.empty() || !rhs.id.empty()) { - id = rhs.id; - } - xTexOffs = rhs.xTexOffs; - yTexOffs = rhs.yTexOffs; - - compiled = false; - mirror = rhs.mirror; - - setModel(rhs.model); - cubes.assign(rhs.cubes.begin(), rhs.cubes.end()); - - mimic(&rhs); - - return *this; -} +#include "ModelPart.h" +#include "Cube.h" +#include "../Model.h" +#include "../../renderer/Tesselator.h" +#include "../../../util/Mth.h" + + +ModelPart::ModelPart( const std::string& id ) +: id(id), + model(NULL), + xTexOffs(0), + yTexOffs(0) +{ + _init(); +} + +ModelPart::ModelPart( int xTexOffs /*= 0*/, int yTexOffs /*= 0*/ ) +: xTexOffs(xTexOffs), + yTexOffs(yTexOffs), + model(NULL) +{ + _init(); +} + +ModelPart::ModelPart( Model* model, int xTexOffs /*= 0*/, int yTexOffs /*= 0*/ ) +: model(model), + xTexOffs(xTexOffs), + yTexOffs(yTexOffs) +{ + _init(); +} + +ModelPart::~ModelPart() { + clear(); +} + +void ModelPart::_init() { + x = y = z = 0; + xRot = yRot = zRot = 0; + list = 0; + mirror = false; + visible = true; + neverRender = false; + compiled = false; + xTexSize = 64; + yTexSize = 32; + + vboId = 0; + glGenBuffers2(1, &vboId); +} + +void ModelPart::setModel(Model* model) { + if (this->model) { + Util::remove(this->model->cubes, this); + } + if (model) { + model->cubes.push_back(this); + setTexSize(model->texWidth, model->texHeight); + } + this->model = model; +} + +void ModelPart::addChild(ModelPart* child) { + children.push_back(child); +} + +ModelPart& ModelPart::addBox(const std::string& id, float x0, float y0, float z0, int w, int h, int d) { + std::string newid = this->id + "." + id; + //TexOffs offs = model.getMapTex(id); //@todo @diff + //texOffs(offs.x, offs.y); + cubes.push_back((new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, 0))->setId(newid)); + return *this; +} + +ModelPart& ModelPart::addBox(float x0, float y0, float z0, int w, int h, int d) { + cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, 0)); + return *this; +} + +void ModelPart::addBox(float x0, float y0, float z0, int w, int h, int d, float g) { + cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, g)); +} + +//This Cube constructor is commented out +//void ModelPart::addTexBox(float x0, float y0, float z0, int w, int h, int d, int tex) { +// cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, tex)); +//} + + +void ModelPart::setPos( float x, float y, float z ) +{ + this->x = x; + this->y = y; + this->z = z; +} + +void ModelPart::render( float scale ) +{ + if (neverRender) return; + if (!visible) return; + if (!compiled) compile(scale); + + if (xRot != 0 || yRot != 0 || zRot != 0) { + glPushMatrix2(); + glTranslatef2(x * scale, y * scale, z * scale); + + const float c = Mth::RADDEG; + if (zRot != 0) glRotatef2(zRot * c, 0.0f, 0.0f, 1.0f); + if (yRot != 0) glRotatef2(yRot * c, 0.0f, 1.0f, 0.0f); + if (xRot != 0) glRotatef2(xRot * c, 1.0f, 0.0f, 0.0f); + + //LOGI("A"); + draw(); + if (!children.empty()) { + for (unsigned int i = 0; i < children.size(); i++) { + children[i]->render(scale); + } + } + + glPopMatrix2(); + } else if (x != 0 || y != 0 || z != 0) { + glTranslatef2(x * scale, y * scale, z * scale); + //LOGI("B"); + draw(); + if (!children.empty()) { + for (unsigned int i = 0; i < children.size(); i++) { + children[i]->render(scale); + } + } + + glTranslatef2(-x * scale, -y * scale, -z * scale); + } else { + //LOGI("C"); + draw(); + if (!children.empty()) { + for (unsigned int i = 0; i < children.size(); i++) { + children[i]->render(scale); + } + } + } +} + + +void ModelPart::renderRollable( float scale ) +{ + if (neverRender) return; + if (!visible) return; + if (!compiled) compile(scale); + + glPushMatrix2(); + glTranslatef2(x * scale, y * scale, z * scale); + + const float c = Mth::RADDEG; + if (yRot != 0) glRotatef2(yRot * c, 0.0f, 1.0f, 0.0f); + if (xRot != 0) glRotatef2(xRot * c, 1.0f, 0.0f, 0.0f); + if (zRot != 0) glRotatef2(zRot * c, 0.0f, 0.0f, 1.0f); + + draw(); + glPopMatrix2(); +} + + +void ModelPart::translateTo( float scale ) +{ + if (neverRender) return; + if (!visible) return; + if (!compiled) compile(scale); + + if (xRot != 0 || yRot != 0 || zRot != 0) { + const float c = Mth::RADDEG; + glTranslatef2(x * scale, y * scale, z * scale); + if (zRot != 0) glRotatef2(zRot * c, 0.0f, 0.0f, 1.0f); + if (yRot != 0) glRotatef2(yRot * c, 0.0f, 1.0f, 0.0f); + if (xRot != 0) glRotatef2(xRot * c, 1.0f, 0.0f, 0.0f); + } else if (x != 0 || y != 0 || z != 0) { + glTranslatef2(x * scale, y * scale, z * scale); + } else { + } +} + + +void ModelPart::compile( float scale ) +{ +#ifndef OPENGL_ES + list = glGenLists(1); + // FIX NORMAL BUG HERE + glNewList(list, GL_COMPILE); +#endif + Tesselator& t = Tesselator::instance; + t.begin(); + t.color(255, 255, 255, 255); + for (int i = 0; i < 6; i++) { + for (unsigned int i = 0; i < cubes.size(); ++i) + cubes[i]->compile(t, scale); + } + t.end(true, vboId); +#ifndef OPENGL_ES + glEndList(); +#endif + compiled = true; +} + +void ModelPart::draw() +{ +#ifdef OPENGL_ES + drawArrayVT_NoState(vboId, cubes.size() * 2 * 3 * 6, 24); +#else + glCallList(list); +#endif +} + +ModelPart& ModelPart::setTexSize(int xs, int ys) { + xTexSize = (float)xs; + yTexSize = (float)ys; + return *this; +} + +ModelPart& ModelPart::texOffs(int xTexOffs, int yTexOffs) { + this->xTexOffs = xTexOffs; + this->yTexOffs = yTexOffs; + return *this; +} + +void ModelPart::mimic(const ModelPart* o) { + x = o->x; + y = o->y; + z = o->z; + + xRot = o->xRot; + yRot = o->yRot; + zRot = o->zRot; +} + +void ModelPart::renderHorrible( float scale ) +{ + if (neverRender) { + return; + } + if (!visible) { + return; + } + //if (!compiled) compile(scale); + + if (xRot != 0 || yRot != 0 || zRot != 0) { + glPushMatrix2(); + glTranslatef2(x * scale, y * scale, z * scale); + if (zRot != 0) glRotatef2(zRot * Mth::RADDEG, 0.0f, 0.0f, 1.0f); + if (yRot != 0) glRotatef2(yRot * Mth::RADDEG, 0.0f, 1.0f, 0.0f); + if (xRot != 0) glRotatef2(xRot * Mth::RADDEG, 1.0f, 0.0f, 0.0f); + + drawSlow(scale); + glPopMatrix2(); + } else if (x != 0 || y != 0 || z != 0) { + glTranslatef2(x * scale, y * scale, z * scale); + drawSlow(scale); + glTranslatef2(-x * scale, -y * scale, -z * scale); + } else { + drawSlow(scale); + } +} + +void ModelPart::drawSlow( float scale ) +{ + Tesselator& t = Tesselator::instance; + t.begin(); + for (int j = 0; j < (int)cubes.size(); ++j) { + Cube* c = cubes[j]; + for (int i = 0; i < 6; i++) { + c->polygons[i].render(t, scale, vboId); + } + } + t.draw(); +} + +void ModelPart::clear() +{ + for (unsigned int i = 0; i < cubes.size(); ++i) + delete cubes[i]; + cubes.clear(); + + setModel(NULL); +} + +ModelPart& ModelPart::operator=( const ModelPart& rhs ) +{ + clear(); + + if (!id.empty() || !rhs.id.empty()) { + id = rhs.id; + } + xTexOffs = rhs.xTexOffs; + yTexOffs = rhs.yTexOffs; + + compiled = false; + mirror = rhs.mirror; + + setModel(rhs.model); + cubes.assign(rhs.cubes.begin(), rhs.cubes.end()); + + mimic(&rhs); + + return *this; +} diff --git a/src/client/model/geom/ModelPart.h b/src/client/model/geom/ModelPart.h index e666542..f29a62c 100755 --- a/src/client/model/geom/ModelPart.h +++ b/src/client/model/geom/ModelPart.h @@ -1,74 +1,74 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL_GEOM__ModelPart_H__ -#define NET_MINECRAFT_CLIENT_MODEL_GEOM__ModelPart_H__ - -#include "../../renderer/gles.h" - -class Model; -class Cube; - -class ModelPart -{ -public: - ModelPart(const std::string& id); - ModelPart(int xTexOffs = 0, int yTexOffs = 0); - ModelPart(Model* model, int xTexOffs = 0, int yTexOffs = 0); - ~ModelPart(); - - void _init(); - void clear();//const ModelPart& rhs); - ModelPart& operator=(const ModelPart& rhs); - - void setModel(Model* model); - - void setPos( float x, float y, float z ); - void translateTo( float scale ); - - ModelPart& setTexSize(int xs, int ys); - ModelPart& texOffs(int xTexOffs, int yTexOffs); - - void mimic(const ModelPart* o); - - // Render normally - void render( float scale ); - void renderRollable( float scale ); - void draw(); - // Bad, immediate version... //@fix @todo: remove this - void renderHorrible(float scale); - void drawSlow( float scale ); - - void onGraphicsReset() { compiled = false; } - void compile( float scale ); - - void addChild(ModelPart* child); - ModelPart& addBox(const std::string& id, float x0, float y0, float z0, int w, int h, int d); - ModelPart& addBox(float x0, float y0, float z0, int w, int h, int d); - void addBox(float x0, float y0, float z0, int w, int h, int d, float g); - void addTexBox(float x0, float y0, float z0, int w, int h, int d, int tex); - - float x, y, z; - float xRot, yRot, zRot; - - bool mirror; - bool visible; - - std::vector cubes; - std::vector children; - - std::string id; - - float xTexSize; - float yTexSize; - -private: - int xTexOffs, yTexOffs; - - bool neverRender; - - bool compiled; - int list; - GLuint vboId; - - Model* model; -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL_GEOM__ModelPart_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL_GEOM__ModelPart_H__ +#define NET_MINECRAFT_CLIENT_MODEL_GEOM__ModelPart_H__ + +#include "../../renderer/gles.h" + +class Model; +class Cube; + +class ModelPart +{ +public: + ModelPart(const std::string& id); + ModelPart(int xTexOffs = 0, int yTexOffs = 0); + ModelPart(Model* model, int xTexOffs = 0, int yTexOffs = 0); + ~ModelPart(); + + void _init(); + void clear();//const ModelPart& rhs); + ModelPart& operator=(const ModelPart& rhs); + + void setModel(Model* model); + + void setPos( float x, float y, float z ); + void translateTo( float scale ); + + ModelPart& setTexSize(int xs, int ys); + ModelPart& texOffs(int xTexOffs, int yTexOffs); + + void mimic(const ModelPart* o); + + // Render normally + void render( float scale ); + void renderRollable( float scale ); + void draw(); + // Bad, immediate version... //@fix @todo: remove this + void renderHorrible(float scale); + void drawSlow( float scale ); + + void onGraphicsReset() { compiled = false; } + void compile( float scale ); + + void addChild(ModelPart* child); + ModelPart& addBox(const std::string& id, float x0, float y0, float z0, int w, int h, int d); + ModelPart& addBox(float x0, float y0, float z0, int w, int h, int d); + void addBox(float x0, float y0, float z0, int w, int h, int d, float g); + void addTexBox(float x0, float y0, float z0, int w, int h, int d, int tex); + + float x, y, z; + float xRot, yRot, zRot; + + bool mirror; + bool visible; + + std::vector cubes; + std::vector children; + + std::string id; + + float xTexSize; + float yTexSize; + +private: + int xTexOffs, yTexOffs; + + bool neverRender; + + bool compiled; + int list; + GLuint vboId; + + Model* model; +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL_GEOM__ModelPart_H__*/ diff --git a/src/client/model/geom/Polygon.cpp b/src/client/model/geom/Polygon.cpp index bfb1467..9fbe026 100755 --- a/src/client/model/geom/Polygon.cpp +++ b/src/client/model/geom/Polygon.cpp @@ -1,55 +1,55 @@ -#include "Polygon.h" -#include "../../renderer/Tesselator.h" -#include "../../../world/phys/Vec3.h" - -PolygonQuad::PolygonQuad(VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3) -: _flipNormal(false) -{ - vertices[0] = *v0; - vertices[1] = *v1; - vertices[2] = *v2; - vertices[3] = *v3; -} - -PolygonQuad::PolygonQuad( VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3, - int uu0, int vv0, int uu1, int vv1, float texW, float texH) -: _flipNormal(false) -{ - const float us = -0.002f / texW; - const float vs = -0.002f / texH; - vertices[0] = v0->remap(uu1 / texW - us, vv0 / texH + vs); - vertices[1] = v1->remap(uu0 / texW + us, vv0 / texH + vs); - vertices[2] = v2->remap(uu0 / texW + us, vv1 / texH - vs); - vertices[3] = v3->remap(uu1 / texW - us, vv1 / texH - vs); -} - -PolygonQuad::PolygonQuad( VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3, - float uu0, float vv0, float uu1, float vv1) -: _flipNormal(false) -{ - vertices[0] = v0->remap(uu1, vv0); - vertices[1] = v1->remap(uu0, vv0); - vertices[2] = v2->remap(uu0, vv1); - vertices[3] = v3->remap(uu1, vv1); -} - -void PolygonQuad::mirror() { - for (int i = 0; i < VERTEX_COUNT / 2; ++i) { - const int j = VERTEX_COUNT - i - 1; - VertexPT tmp = vertices[i]; - vertices[i] = vertices[j]; - vertices[j] = tmp; - } -} - -void PolygonQuad::render(Tesselator& t, float scale, int vboId /* = -1 */) { - for (int i = 0; i < 4; i++) { - VertexPT& v = vertices[i]; - t.vertexUV(v.pos.x * scale, v.pos.y * scale, v.pos.z * scale, v.u, v.v); - } -} - -PolygonQuad* PolygonQuad::flipNormal() { - _flipNormal = true; - return this; -} +#include "Polygon.h" +#include "../../renderer/Tesselator.h" +#include "../../../world/phys/Vec3.h" + +PolygonQuad::PolygonQuad(VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3) +: _flipNormal(false) +{ + vertices[0] = *v0; + vertices[1] = *v1; + vertices[2] = *v2; + vertices[3] = *v3; +} + +PolygonQuad::PolygonQuad( VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3, + int uu0, int vv0, int uu1, int vv1, float texW, float texH) +: _flipNormal(false) +{ + const float us = -0.002f / texW; + const float vs = -0.002f / texH; + vertices[0] = v0->remap(uu1 / texW - us, vv0 / texH + vs); + vertices[1] = v1->remap(uu0 / texW + us, vv0 / texH + vs); + vertices[2] = v2->remap(uu0 / texW + us, vv1 / texH - vs); + vertices[3] = v3->remap(uu1 / texW - us, vv1 / texH - vs); +} + +PolygonQuad::PolygonQuad( VertexPT* v0, VertexPT* v1, VertexPT* v2, VertexPT* v3, + float uu0, float vv0, float uu1, float vv1) +: _flipNormal(false) +{ + vertices[0] = v0->remap(uu1, vv0); + vertices[1] = v1->remap(uu0, vv0); + vertices[2] = v2->remap(uu0, vv1); + vertices[3] = v3->remap(uu1, vv1); +} + +void PolygonQuad::mirror() { + for (int i = 0; i < VERTEX_COUNT / 2; ++i) { + const int j = VERTEX_COUNT - i - 1; + VertexPT tmp = vertices[i]; + vertices[i] = vertices[j]; + vertices[j] = tmp; + } +} + +void PolygonQuad::render(Tesselator& t, float scale, int vboId /* = -1 */) { + for (int i = 0; i < 4; i++) { + VertexPT& v = vertices[i]; + t.vertexUV(v.pos.x * scale, v.pos.y * scale, v.pos.z * scale, v.u, v.v); + } +} + +PolygonQuad* PolygonQuad::flipNormal() { + _flipNormal = true; + return this; +} diff --git a/src/client/model/geom/Polygon.h b/src/client/model/geom/Polygon.h index 7731f9c..3febc61 100755 --- a/src/client/model/geom/Polygon.h +++ b/src/client/model/geom/Polygon.h @@ -1,29 +1,29 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__Polygon_H__ -#define NET_MINECRAFT_CLIENT_MODEL__Polygon_H__ - -//package net.minecraft.client.model; -#include "Vertex.h" - -class Tesselator; - -class PolygonQuad -{ -public: - PolygonQuad() {} - PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*); - PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*, int u0, int v0, int u1, int v1, float texW = 64.0f, float texH = 32.0f); - PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*, float u0, float v0, float u1, float v1); - - void mirror(); - void render(Tesselator& t, float scale, int vboId = -1); - PolygonQuad* flipNormal(); - - VertexPT vertices[4]; - //int vertexCount; - -private: - static const int VERTEX_COUNT = 4; - bool _flipNormal; -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__Polygon_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__Polygon_H__ +#define NET_MINECRAFT_CLIENT_MODEL__Polygon_H__ + +//package net.minecraft.client.model; +#include "Vertex.h" + +class Tesselator; + +class PolygonQuad +{ +public: + PolygonQuad() {} + PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*); + PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*, int u0, int v0, int u1, int v1, float texW = 64.0f, float texH = 32.0f); + PolygonQuad(VertexPT*,VertexPT*,VertexPT*,VertexPT*, float u0, float v0, float u1, float v1); + + void mirror(); + void render(Tesselator& t, float scale, int vboId = -1); + PolygonQuad* flipNormal(); + + VertexPT vertices[4]; + //int vertexCount; + +private: + static const int VERTEX_COUNT = 4; + bool _flipNormal; +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__Polygon_H__*/ diff --git a/src/client/model/geom/Vertex.h b/src/client/model/geom/Vertex.h index b7babbb..48d3ef4 100755 --- a/src/client/model/geom/Vertex.h +++ b/src/client/model/geom/Vertex.h @@ -1,40 +1,40 @@ -#ifndef NET_MINECRAFT_CLIENT_MODEL__Vertex_H__ -#define NET_MINECRAFT_CLIENT_MODEL__Vertex_H__ - -//package net.minecraft.client.model; - -#include "../../../world/phys/Vec3.h" - -class VertexPT -{ -public: - Vec3 pos; - - float u, v; - - VertexPT() {} - - VertexPT(float x, float y, float z, float u_, float v_) - : pos(x, y, z), - u(u_), - v(v_) - {} - - VertexPT remap(float u, float v) { - return VertexPT(*this, u, v); - } - - VertexPT(const VertexPT& vertex, float u_, float v_) { - pos = vertex.pos; - u = u_; - v = v_; - } - - VertexPT(const Vec3& pos_, float u_, float v_) { - pos = pos_; - u = u_; - v = v_; - } -}; - -#endif /*NET_MINECRAFT_CLIENT_MODEL__Vertex_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MODEL__Vertex_H__ +#define NET_MINECRAFT_CLIENT_MODEL__Vertex_H__ + +//package net.minecraft.client.model; + +#include "../../../world/phys/Vec3.h" + +class VertexPT +{ +public: + Vec3 pos; + + float u, v; + + VertexPT() {} + + VertexPT(float x, float y, float z, float u_, float v_) + : pos(x, y, z), + u(u_), + v(v_) + {} + + VertexPT remap(float u, float v) { + return VertexPT(*this, u, v); + } + + VertexPT(const VertexPT& vertex, float u_, float v_) { + pos = vertex.pos; + u = u_; + v = v_; + } + + VertexPT(const Vec3& pos_, float u_, float v_) { + pos = pos_; + u = u_; + v = v_; + } +}; + +#endif /*NET_MINECRAFT_CLIENT_MODEL__Vertex_H__*/ diff --git a/src/client/multiplayer/MultiPlayerLevel.h b/src/client/multiplayer/MultiPlayerLevel.h index 3ed5737..305e41c 100755 --- a/src/client/multiplayer/MultiPlayerLevel.h +++ b/src/client/multiplayer/MultiPlayerLevel.h @@ -1,265 +1,265 @@ -#ifndef NET_MINECRAFT_CLIENT_MULTIPLAYER__MultiPlayerLevel_H__ -#define NET_MINECRAFT_CLIENT_MULTIPLAYER__MultiPlayerLevel_H__ - -#include "../../world/Pos.h" -#include "../../world/level/Level.h" -//#include "../../network/packet/DisconnectPacket.h" -#include "../../world/entity/Entity.h" -#include "../../world/level/chunk/ChunkSource.h" -#include "../../world/level/dimension/Dimension.h" -//#include "../../world/level/storage/MockedLevelStorage.h" - -#include -#include -#include - -class MultiPlayerLevel: public Level -{ - typedef Level super; - typedef std::map EntityIdMap; - typedef std::set EntitySet; - - static const int TICKS_BEFORE_RESET = SharedConstants::TicksPerSecond * 4; - - class ResetInfo { - public: - int x, y, z, ticks, tile, data; - - ResetInfo(int x, int y, int z, int tile, int data) - : x(x), - y(y), - z(z), - ticks(TICKS_BEFORE_RESET), - tile(tile), - data(data) - {} - }; - typedef std::list ResetInfoList; - -public: - MultiPlayerLevel(LevelStorage* levelStorage, const std::string& levelName, const LevelSettings& settings, int levelVersion) - : super(levelStorage, levelName, settings, levelVersion) - { - //super(new MockedLevelStorage(), "MpServer", Dimension.getNew(dimension), levelSettings); - //this->difficulty = difficulty; - //setSpawnPos(new Pos(8, 64, 8)); - //this.savedDataStorage = connection.savedDataStorage; - } - - //@todo: destructor (to Entities) - - void tick() { - if (adventureSettings.doTickTime) { - setTime(getTime() + 1); - } - updateSkyDarken(); - - for (int i = 0; i < 10 && !reEntries.empty(); i++) { - EntitySet::iterator it = reEntries.begin(); - Entity* e = *it;//reEntries.iterator().next(); - reEntries.erase(it); - - //if (!entities.contains(e)) addEntity(e); - EntityList::iterator jt = std::find(entities.begin(), entities.end(), e); - if (jt == entities.end()) - addEntity(e); - } - - for (ResetInfoList::iterator it = updatesToReset.begin(); it != updatesToReset.end();) { - ResetInfo& r = *it; - if (--r.ticks == 0) { - super::setTileAndDataNoUpdate(r.x, r.y, r.z, r.tile, r.data); - super::sendTileUpdated(r.x, r.y, r.z); - it = updatesToReset.erase(it); - } else - ++it; - } - } - - void clearResetRegion(int x0, int y0, int z0, int x1, int y1, int z1) { - for (ResetInfoList::iterator it = updatesToReset.begin(); it != updatesToReset.end();) { - ResetInfo& r = *it; - if (r.x >= x0 && r.y >= y0 && r.z >= z0 && r.x <= x1 && r.y <= y1 && r.z <= z1) { - it = updatesToReset.erase(it); - } else - ++it; - } - } - - void validateSpawn() { - setSpawnPos(Pos(8, 64, 8)); - } - - void addToTickNextTick(int x, int y, int z, int tileId, int tickDelay) { - } - - bool tickPendingTicks(bool force) { - return false; - } - - //@todo - //void setChunkVisible(int x, int z, bool visible) { - // if (visible) chunkCache.create(x, z); - // else chunkCache.drop(x, z); - // if (!visible) { - // this->setTilesDirty(x * 16, 0, z * 16, x * 16 + 15, depth, z * 16 + 15); - // } - //} - - bool addEntity(Entity* e) { - bool ok = super::addEntity(e); - forced.insert(e); - - if (!ok) { - reEntries.insert(e); - } - - return ok; - } - - void removeEntity(Entity* e) { - super::removeEntity(e); - forced.erase(e); - } - - void putEntity(int id, Entity* e) { - Entity* old = getEntity(id); - if (old != NULL) { - removeEntity(old); - } - - forced.insert(e); - e->entityId = id; - if (!addEntity(e)) { - reEntries.insert(e); - } - entitiesById.insert(std::make_pair(id, e)); - } - - Entity* getEntity(int id) { - EntityIdMap::const_iterator cit = entitiesById.find(id); - return cit != entitiesById.end()? cit->second : NULL; - } - - Entity* removeEntity(int id) { - EntityIdMap::iterator it = entitiesById.find(id); - if (it != entitiesById.end()) { - Entity* e = it->second; - entitiesById.erase(it); - removeEntity(e); - return it->second; - } - return NULL; - } - - bool setDataNoUpdate(int x, int y, int z, int data) { - int t = getTile(x, y, z); - int d = getData(x, y, z); - if (super::setDataNoUpdate(x, y, z, data)) { - updatesToReset.push_back(/*new*/ ResetInfo(x, y, z, t, d)); - return true; - } - return false; - } - - bool setTileAndDataNoUpdate(int x, int y, int z, int tile, int data) { - int t = getTile(x, y, z); - int d = getData(x, y, z); - if (super::setTileAndDataNoUpdate(x, y, z, tile, data)) { - updatesToReset.push_back(/*new*/ ResetInfo(x, y, z, t, d)); - return true; - } - return false; - } - - bool setTileNoUpdate(int x, int y, int z, int tile) { - int t = getTile(x, y, z); - int d = getData(x, y, z); - if (super::setTileNoUpdate(x, y, z, tile)) { - updatesToReset.push_back(/*new*/ ResetInfo(x, y, z, t, d)); - return true; - } - return false; - } - - bool doSetTileAndData(int x, int y, int z, int tile, int data) { - clearResetRegion(x, y, z, x, y, z); - if (super::setTileAndDataNoUpdate(x, y, z, tile, data)) { - // if (setTile(x, y, z, tile)) { - // setData(x, y, z, data); - tileUpdated(x, y, z, tile); - return true; - } - return false; - } - - void disconnect() { - //connection.sendAndDisconnect(/*new*/ DisconnectPacket("Quitting")); - } - -protected: - void tickTiles() { - } - /*@Override*/ - ChunkSource* createChunkSource() { - //chunkCache = /*new*/ MultiPlayerChunkCache(this); - //return chunkCache; - return NULL; - } - - void entityAdded(Entity* e) { - super::entityAdded(e); - EntitySet::iterator it = reEntries.find(e); - if (it != reEntries.end()) { - reEntries.erase(it); - } - } - - void entityRemoved(Entity* e) { - super::entityRemoved(e); - EntitySet::iterator it = forced.find(e); - if (it != forced.end()) { - if (e->isAlive()) { - reEntries.insert(e); - } else { - forced.erase(it); - } - } - } - -// void tickWeather() { -// if (dimension.hasCeiling) return; -// -// if (lightningTime > 0) { -// lightningTime--; -// } -// -// oRainLevel = rainLevel; -// if (levelData.isRaining()) { -// rainLevel += 0.01; -// } else { -// rainLevel -= 0.01; -// } -// if (rainLevel < 0) rainLevel = 0; -// if (rainLevel > 1) rainLevel = 1; -// -// oThunderLevel = thunderLevel; -// if (levelData.isThundering()) { -// thunderLevel += 0.01; -// } else { -// thunderLevel -= 0.01; -// } -// if (thunderLevel < 0) thunderLevel = 0; -// if (thunderLevel > 1) thunderLevel = 1; -// } -private: - ResetInfoList updatesToReset; - //ClientConnection connection; - //MultiPlayerChunkCache chunkCache; - - EntityIdMap entitiesById; - EntitySet forced; - EntitySet reEntries; -}; - -#endif /*NET_MINECRAFT_CLIENT_MULTIPLAYER__MultiPlayerLevel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_MULTIPLAYER__MultiPlayerLevel_H__ +#define NET_MINECRAFT_CLIENT_MULTIPLAYER__MultiPlayerLevel_H__ + +#include "../../world/Pos.h" +#include "../../world/level/Level.h" +//#include "../../network/packet/DisconnectPacket.h" +#include "../../world/entity/Entity.h" +#include "../../world/level/chunk/ChunkSource.h" +#include "../../world/level/dimension/Dimension.h" +//#include "../../world/level/storage/MockedLevelStorage.h" + +#include +#include +#include + +class MultiPlayerLevel: public Level +{ + typedef Level super; + typedef std::map EntityIdMap; + typedef std::set EntitySet; + + static const int TICKS_BEFORE_RESET = SharedConstants::TicksPerSecond * 4; + + class ResetInfo { + public: + int x, y, z, ticks, tile, data; + + ResetInfo(int x, int y, int z, int tile, int data) + : x(x), + y(y), + z(z), + ticks(TICKS_BEFORE_RESET), + tile(tile), + data(data) + {} + }; + typedef std::list ResetInfoList; + +public: + MultiPlayerLevel(LevelStorage* levelStorage, const std::string& levelName, const LevelSettings& settings, int levelVersion) + : super(levelStorage, levelName, settings, levelVersion) + { + //super(new MockedLevelStorage(), "MpServer", Dimension.getNew(dimension), levelSettings); + //this->difficulty = difficulty; + //setSpawnPos(new Pos(8, 64, 8)); + //this.savedDataStorage = connection.savedDataStorage; + } + + //@todo: destructor (to Entities) + + void tick() { + if (adventureSettings.doTickTime) { + setTime(getTime() + 1); + } + updateSkyDarken(); + + for (int i = 0; i < 10 && !reEntries.empty(); i++) { + EntitySet::iterator it = reEntries.begin(); + Entity* e = *it;//reEntries.iterator().next(); + reEntries.erase(it); + + //if (!entities.contains(e)) addEntity(e); + EntityList::iterator jt = std::find(entities.begin(), entities.end(), e); + if (jt == entities.end()) + addEntity(e); + } + + for (ResetInfoList::iterator it = updatesToReset.begin(); it != updatesToReset.end();) { + ResetInfo& r = *it; + if (--r.ticks == 0) { + super::setTileAndDataNoUpdate(r.x, r.y, r.z, r.tile, r.data); + super::sendTileUpdated(r.x, r.y, r.z); + it = updatesToReset.erase(it); + } else + ++it; + } + } + + void clearResetRegion(int x0, int y0, int z0, int x1, int y1, int z1) { + for (ResetInfoList::iterator it = updatesToReset.begin(); it != updatesToReset.end();) { + ResetInfo& r = *it; + if (r.x >= x0 && r.y >= y0 && r.z >= z0 && r.x <= x1 && r.y <= y1 && r.z <= z1) { + it = updatesToReset.erase(it); + } else + ++it; + } + } + + void validateSpawn() { + setSpawnPos(Pos(8, 64, 8)); + } + + void addToTickNextTick(int x, int y, int z, int tileId, int tickDelay) { + } + + bool tickPendingTicks(bool force) { + return false; + } + + //@todo + //void setChunkVisible(int x, int z, bool visible) { + // if (visible) chunkCache.create(x, z); + // else chunkCache.drop(x, z); + // if (!visible) { + // this->setTilesDirty(x * 16, 0, z * 16, x * 16 + 15, depth, z * 16 + 15); + // } + //} + + bool addEntity(Entity* e) { + bool ok = super::addEntity(e); + forced.insert(e); + + if (!ok) { + reEntries.insert(e); + } + + return ok; + } + + void removeEntity(Entity* e) { + super::removeEntity(e); + forced.erase(e); + } + + void putEntity(int id, Entity* e) { + Entity* old = getEntity(id); + if (old != NULL) { + removeEntity(old); + } + + forced.insert(e); + e->entityId = id; + if (!addEntity(e)) { + reEntries.insert(e); + } + entitiesById.insert(std::make_pair(id, e)); + } + + Entity* getEntity(int id) { + EntityIdMap::const_iterator cit = entitiesById.find(id); + return cit != entitiesById.end()? cit->second : NULL; + } + + Entity* removeEntity(int id) { + EntityIdMap::iterator it = entitiesById.find(id); + if (it != entitiesById.end()) { + Entity* e = it->second; + entitiesById.erase(it); + removeEntity(e); + return it->second; + } + return NULL; + } + + bool setDataNoUpdate(int x, int y, int z, int data) { + int t = getTile(x, y, z); + int d = getData(x, y, z); + if (super::setDataNoUpdate(x, y, z, data)) { + updatesToReset.push_back(/*new*/ ResetInfo(x, y, z, t, d)); + return true; + } + return false; + } + + bool setTileAndDataNoUpdate(int x, int y, int z, int tile, int data) { + int t = getTile(x, y, z); + int d = getData(x, y, z); + if (super::setTileAndDataNoUpdate(x, y, z, tile, data)) { + updatesToReset.push_back(/*new*/ ResetInfo(x, y, z, t, d)); + return true; + } + return false; + } + + bool setTileNoUpdate(int x, int y, int z, int tile) { + int t = getTile(x, y, z); + int d = getData(x, y, z); + if (super::setTileNoUpdate(x, y, z, tile)) { + updatesToReset.push_back(/*new*/ ResetInfo(x, y, z, t, d)); + return true; + } + return false; + } + + bool doSetTileAndData(int x, int y, int z, int tile, int data) { + clearResetRegion(x, y, z, x, y, z); + if (super::setTileAndDataNoUpdate(x, y, z, tile, data)) { + // if (setTile(x, y, z, tile)) { + // setData(x, y, z, data); + tileUpdated(x, y, z, tile); + return true; + } + return false; + } + + void disconnect() { + //connection.sendAndDisconnect(/*new*/ DisconnectPacket("Quitting")); + } + +protected: + void tickTiles() { + } + /*@Override*/ + ChunkSource* createChunkSource() { + //chunkCache = /*new*/ MultiPlayerChunkCache(this); + //return chunkCache; + return NULL; + } + + void entityAdded(Entity* e) { + super::entityAdded(e); + EntitySet::iterator it = reEntries.find(e); + if (it != reEntries.end()) { + reEntries.erase(it); + } + } + + void entityRemoved(Entity* e) { + super::entityRemoved(e); + EntitySet::iterator it = forced.find(e); + if (it != forced.end()) { + if (e->isAlive()) { + reEntries.insert(e); + } else { + forced.erase(it); + } + } + } + +// void tickWeather() { +// if (dimension.hasCeiling) return; +// +// if (lightningTime > 0) { +// lightningTime--; +// } +// +// oRainLevel = rainLevel; +// if (levelData.isRaining()) { +// rainLevel += 0.01; +// } else { +// rainLevel -= 0.01; +// } +// if (rainLevel < 0) rainLevel = 0; +// if (rainLevel > 1) rainLevel = 1; +// +// oThunderLevel = thunderLevel; +// if (levelData.isThundering()) { +// thunderLevel += 0.01; +// } else { +// thunderLevel -= 0.01; +// } +// if (thunderLevel < 0) thunderLevel = 0; +// if (thunderLevel > 1) thunderLevel = 1; +// } +private: + ResetInfoList updatesToReset; + //ClientConnection connection; + //MultiPlayerChunkCache chunkCache; + + EntityIdMap entitiesById; + EntitySet forced; + EntitySet reEntries; +}; + +#endif /*NET_MINECRAFT_CLIENT_MULTIPLAYER__MultiPlayerLevel_H__*/ diff --git a/src/client/particle/BreakingItemParticle.h b/src/client/particle/BreakingItemParticle.h index b92e2dd..8e7f412 100755 --- a/src/client/particle/BreakingItemParticle.h +++ b/src/client/particle/BreakingItemParticle.h @@ -1,66 +1,66 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__BreakingItemParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__BreakingItemParticle_H__ - -//package net.minecraft.client.particle; - -#include "Particle.h" -#include "../renderer/Tesselator.h" -#include "../../world/item/Item.h" -#include "../../world/level/Level.h" -#include "../../world/level/tile/Tile.h" - -class BreakingItemParticle: public Particle -{ - typedef Particle super; -public: - BreakingItemParticle(Level* level, float x, float y, float z, Item* item) - : super(level, x, y, z, 0, 0, 0) - { - _init(item); - } - - BreakingItemParticle(Level* level, float x, float y, float z, float xa, float ya, float za, Item* item) - : super(level, x, y, z, 0, 0, 0) - { - _init(item); - - xd *= 0.1f; - yd *= 0.1f; - zd *= 0.1f; - xd += xa; - yd += ya; - zd += za; - } - - void _init(Item* item) { - tex = item->getIcon(0); - rCol = gCol = bCol = 1.0f; - gravity = Tile::snow->gravity; - size /= 2; - } - - int getParticleTexture() { - return ParticleEngine::ITEM_TEXTURE; - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { - float u0 = (tex % 16 + uo / 4.0f) / 16.0f; - float u1 = u0 + 0.999f / 16.0f / 4; - float v0 = (tex / 16 + vo / 4.0f) / 16.0f; - float v1 = v0 + 0.999f / 16.0f / 4; - float r = 0.1f * size; - - float x = (float) (xo + (this->x - xo) * a - xOff); - float y = (float) (yo + (this->y - yo) * a - yOff); - float z = (float) (zo + (this->z - zo) * a - zOff); - float br = getBrightness(a); - t.color(br * rCol, br * gCol, br * bCol); - - t.vertexUV(x - xa * r - xa2 * r, y - ya * r, z - za * r - za2 * r, u0, v1); - t.vertexUV(x - xa * r + xa2 * r, y + ya * r, z - za * r + za2 * r, u0, v0); - t.vertexUV(x + xa * r + xa2 * r, y + ya * r, z + za * r + za2 * r, u1, v0); - t.vertexUV(x + xa * r - xa2 * r, y - ya * r, z + za * r - za2 * r, u1, v1); - } -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__BreakingItemParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__BreakingItemParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__BreakingItemParticle_H__ + +//package net.minecraft.client.particle; + +#include "Particle.h" +#include "../renderer/Tesselator.h" +#include "../../world/item/Item.h" +#include "../../world/level/Level.h" +#include "../../world/level/tile/Tile.h" + +class BreakingItemParticle: public Particle +{ + typedef Particle super; +public: + BreakingItemParticle(Level* level, float x, float y, float z, Item* item) + : super(level, x, y, z, 0, 0, 0) + { + _init(item); + } + + BreakingItemParticle(Level* level, float x, float y, float z, float xa, float ya, float za, Item* item) + : super(level, x, y, z, 0, 0, 0) + { + _init(item); + + xd *= 0.1f; + yd *= 0.1f; + zd *= 0.1f; + xd += xa; + yd += ya; + zd += za; + } + + void _init(Item* item) { + tex = item->getIcon(0); + rCol = gCol = bCol = 1.0f; + gravity = Tile::snow->gravity; + size /= 2; + } + + int getParticleTexture() { + return ParticleEngine::ITEM_TEXTURE; + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { + float u0 = (tex % 16 + uo / 4.0f) / 16.0f; + float u1 = u0 + 0.999f / 16.0f / 4; + float v0 = (tex / 16 + vo / 4.0f) / 16.0f; + float v1 = v0 + 0.999f / 16.0f / 4; + float r = 0.1f * size; + + float x = (float) (xo + (this->x - xo) * a - xOff); + float y = (float) (yo + (this->y - yo) * a - yOff); + float z = (float) (zo + (this->z - zo) * a - zOff); + float br = getBrightness(a); + t.color(br * rCol, br * gCol, br * bCol); + + t.vertexUV(x - xa * r - xa2 * r, y - ya * r, z - za * r - za2 * r, u0, v1); + t.vertexUV(x - xa * r + xa2 * r, y + ya * r, z - za * r + za2 * r, u0, v0); + t.vertexUV(x + xa * r + xa2 * r, y + ya * r, z + za * r + za2 * r, u1, v0); + t.vertexUV(x + xa * r - xa2 * r, y - ya * r, z + za * r - za2 * r, u1, v1); + } +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__BreakingItemParticle_H__*/ diff --git a/src/client/particle/BubbleParticle.h b/src/client/particle/BubbleParticle.h index 4e2ee48..463cfe4 100755 --- a/src/client/particle/BubbleParticle.h +++ b/src/client/particle/BubbleParticle.h @@ -1,51 +1,51 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__BubbleParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__BubbleParticle_H__ - -//package net.minecraft.client.particle; - -#include "../../util/Mth.h" -#include "../../world/level/Level.h" -#include "../../world/level/material/Material.h" -#include "Particle.h" - -class BubbleParticle: public Particle -{ - typedef Particle super; - -public: - BubbleParticle(Level* level, float x, float y, float z, float xa, float ya, float za) - : super(level, x, y, z, xa, ya, za) - { - rCol = 1.0f; - gCol = 1.0f; - bCol = 1.0f; - tex = 32; - this->setSize(0.02f, 0.02f); - - size = size*(sharedRandom.nextFloat()*0.6f+0.2f); - - xd = xa*0.2f+(float)(Mth::random()*2-1)*0.02f; - yd = ya*0.2f+(float)(Mth::random()*2-1)*0.02f; - zd = za*0.2f+(float)(Mth::random()*2-1)*0.02f; - - lifetime = (int) (8 / (Mth::random() * 0.8 + 0.2)); - } - - void tick() { - xo = x; - yo = y; - zo = z; - - yd += 0.002f; - move(xd, yd, zd); - xd *= 0.85f; - yd *= 0.85f; - zd *= 0.85f; - - if (level->getMaterial(Mth::floor(x), Mth::floor(y), Mth::floor(z)) != Material::water) remove(); - - if (lifetime-- <= 0) remove(); - } -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__BubbleParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__BubbleParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__BubbleParticle_H__ + +//package net.minecraft.client.particle; + +#include "../../util/Mth.h" +#include "../../world/level/Level.h" +#include "../../world/level/material/Material.h" +#include "Particle.h" + +class BubbleParticle: public Particle +{ + typedef Particle super; + +public: + BubbleParticle(Level* level, float x, float y, float z, float xa, float ya, float za) + : super(level, x, y, z, xa, ya, za) + { + rCol = 1.0f; + gCol = 1.0f; + bCol = 1.0f; + tex = 32; + this->setSize(0.02f, 0.02f); + + size = size*(sharedRandom.nextFloat()*0.6f+0.2f); + + xd = xa*0.2f+(float)(Mth::random()*2-1)*0.02f; + yd = ya*0.2f+(float)(Mth::random()*2-1)*0.02f; + zd = za*0.2f+(float)(Mth::random()*2-1)*0.02f; + + lifetime = (int) (8 / (Mth::random() * 0.8 + 0.2)); + } + + void tick() { + xo = x; + yo = y; + zo = z; + + yd += 0.002f; + move(xd, yd, zd); + xd *= 0.85f; + yd *= 0.85f; + zd *= 0.85f; + + if (level->getMaterial(Mth::floor(x), Mth::floor(y), Mth::floor(z)) != Material::water) remove(); + + if (lifetime-- <= 0) remove(); + } +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__BubbleParticle_H__*/ diff --git a/src/client/particle/CritParticle2.h b/src/client/particle/CritParticle2.h index 229e386..fbb3f7c 100755 --- a/src/client/particle/CritParticle2.h +++ b/src/client/particle/CritParticle2.h @@ -1,74 +1,74 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__CritParticle2_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__CritParticle2_H__ - -//package net.minecraft.client.particle; - -#include "Particle.h" -#include "../../util/Mth.h" -#include "../../world/level/Level.h" -#include "../../world/level/material/Material.h" - -class CritParticle2: public Particle -{ - typedef Particle super; -public: - CritParticle2(Level* level, float x, float y, float z, float xa, float ya, float za, float scale = 1.0f) - : super(level, x, y, z, 0, 0, 0), - visible(true) - { - xd *= 0.1f; - yd *= 0.1f; - zd *= 0.1f; - xd += xa * 0.4f; - yd += ya * 0.4f; - zd += za * 0.4f; - - rCol = gCol = bCol = (float) (Mth::random() * 0.3f + 0.6f); - size *= 0.75f; - size *= scale; - oSize = size; - - lifetime = (int)(6 / (Mth::random() * 0.8f + 0.6f) * scale); - noPhysics = false; - - tex = 16 * 4 + 1; - tick(); - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { - if (!visible) return; - float l = ((age + a) / lifetime) * 32; - if (l < 0) l = 0; - if (l > 1) l = 1; - - size = oSize * l; - super::render(t, a, xa, ya, za, xa2, za2); - } - - void tick() { - xo = x; - yo = y; - zo = z; - - if (age++ >= lifetime) remove(); - - move(xd, yd, zd); - gCol *= 0.96f; - bCol *= 0.9f; - - xd *= 0.70f; - yd *= 0.70f; - zd *= 0.70f; - yd -= 0.02f; - - if (onGround) { - xd *= 0.7f; - zd *= 0.7f; - } - } -protected: - bool visible; - float oSize; -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__CritParticle2_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__CritParticle2_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__CritParticle2_H__ + +//package net.minecraft.client.particle; + +#include "Particle.h" +#include "../../util/Mth.h" +#include "../../world/level/Level.h" +#include "../../world/level/material/Material.h" + +class CritParticle2: public Particle +{ + typedef Particle super; +public: + CritParticle2(Level* level, float x, float y, float z, float xa, float ya, float za, float scale = 1.0f) + : super(level, x, y, z, 0, 0, 0), + visible(true) + { + xd *= 0.1f; + yd *= 0.1f; + zd *= 0.1f; + xd += xa * 0.4f; + yd += ya * 0.4f; + zd += za * 0.4f; + + rCol = gCol = bCol = (float) (Mth::random() * 0.3f + 0.6f); + size *= 0.75f; + size *= scale; + oSize = size; + + lifetime = (int)(6 / (Mth::random() * 0.8f + 0.6f) * scale); + noPhysics = false; + + tex = 16 * 4 + 1; + tick(); + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { + if (!visible) return; + float l = ((age + a) / lifetime) * 32; + if (l < 0) l = 0; + if (l > 1) l = 1; + + size = oSize * l; + super::render(t, a, xa, ya, za, xa2, za2); + } + + void tick() { + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + move(xd, yd, zd); + gCol *= 0.96f; + bCol *= 0.9f; + + xd *= 0.70f; + yd *= 0.70f; + zd *= 0.70f; + yd -= 0.02f; + + if (onGround) { + xd *= 0.7f; + zd *= 0.7f; + } + } +protected: + bool visible; + float oSize; +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__CritParticle2_H__*/ diff --git a/src/client/particle/ExplodeParticle.h b/src/client/particle/ExplodeParticle.h index 0eeb305..afe4c0d 100755 --- a/src/client/particle/ExplodeParticle.h +++ b/src/client/particle/ExplodeParticle.h @@ -1,51 +1,51 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__ExplodeParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__ExplodeParticle_H__ - -//package net.minecraft.client.particle; - -#include "Particle.h" -#include "../renderer/Tesselator.h" -#include "../../world/level/Level.h" - -class ExplodeParticle: public Particle -{ - typedef Particle super; -public: - ExplodeParticle(Level* level, float x, float y, float z, float xa, float ya, float za) - : super(level, x, y, z, xa, ya, za) - { - xd = xa + (Mth::random()*2.0f-1.0f) * 0.05f; - yd = ya + (Mth::random()*2.0f-1.0f) * 0.05f; - zd = za + (Mth::random()*2.0f-1.0f) * 0.05f; - - rCol = gCol = bCol = sharedRandom.nextFloat() * 0.3f + 0.7f; - size = sharedRandom.nextFloat() * sharedRandom.nextFloat() * 6.0f + 1.0f; - - lifetime = (int)(16.0f/(sharedRandom.nextFloat() * 0.8f + 0.2f)) + 2; -// noPhysics = true; - } - - void tick() - { - xo = x; - yo = y; - zo = z; - - if (age++ >= lifetime) remove(); - - tex = 7 - age * 8 / lifetime; - - yd += 0.004f; - move(xd, yd, zd); - xd *= 0.90f; - yd *= 0.90f; - zd *= 0.90f; - - if (onGround) { - xd *= 0.7f; - zd *= 0.7f; - } - } -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__ExplodeParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__ExplodeParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__ExplodeParticle_H__ + +//package net.minecraft.client.particle; + +#include "Particle.h" +#include "../renderer/Tesselator.h" +#include "../../world/level/Level.h" + +class ExplodeParticle: public Particle +{ + typedef Particle super; +public: + ExplodeParticle(Level* level, float x, float y, float z, float xa, float ya, float za) + : super(level, x, y, z, xa, ya, za) + { + xd = xa + (Mth::random()*2.0f-1.0f) * 0.05f; + yd = ya + (Mth::random()*2.0f-1.0f) * 0.05f; + zd = za + (Mth::random()*2.0f-1.0f) * 0.05f; + + rCol = gCol = bCol = sharedRandom.nextFloat() * 0.3f + 0.7f; + size = sharedRandom.nextFloat() * sharedRandom.nextFloat() * 6.0f + 1.0f; + + lifetime = (int)(16.0f/(sharedRandom.nextFloat() * 0.8f + 0.2f)) + 2; +// noPhysics = true; + } + + void tick() + { + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + tex = 7 - age * 8 / lifetime; + + yd += 0.004f; + move(xd, yd, zd); + xd *= 0.90f; + yd *= 0.90f; + zd *= 0.90f; + + if (onGround) { + xd *= 0.7f; + zd *= 0.7f; + } + } +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__ExplodeParticle_H__*/ diff --git a/src/client/particle/FlameParticle.h b/src/client/particle/FlameParticle.h index 56fe208..451f2c4 100755 --- a/src/client/particle/FlameParticle.h +++ b/src/client/particle/FlameParticle.h @@ -1,73 +1,73 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__FlameParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__FlameParticle_H__ - -//package net.minecraft.client.particle; - -#include "Particle.h" -#include "../renderer/Tesselator.h" -#include "../../world/level/Level.h" - -class FlameParticle: public Particle -{ - typedef Particle super; - float oSize; -public: - FlameParticle(Level* level, float x, float y, float z, float xa, float ya, float za) - : super(level, x, y, z, xa, ya, za) - { - xd = xd * 0.01f + xa; - yd = yd * 0.01f + ya; - zd = zd * 0.01f + za; - x += (sharedRandom.nextFloat()-sharedRandom.nextFloat()) * 0.05f; - y += (sharedRandom.nextFloat()-sharedRandom.nextFloat()) * 0.05f; - z += (sharedRandom.nextFloat()-sharedRandom.nextFloat()) * 0.05f; - - //printf("xd, yd, zd: %f, %f, %f\n", xd, yd, zd); - - oSize = size; - rCol = gCol = bCol = 1.0f; - - lifetime = (int)(8.0f/(Mth::random()*0.8f+0.2f))+4; - noPhysics = true; - tex = 48; - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { - float s = (age + a) / (float) lifetime; - size = oSize * (1 - s*s*0.5f); - super::render(t, a, xa, ya, za, xa2, za2); - } - - float getBrightness(float a) { - return 1.0f; -/* - float l = (age+a)/lifetime; - if (l<0) l = 0; - if (l>1) l = 1; - float br = super::getBrightness(a); - - return br*l+(1-l); -*/ - } - - void tick() - { - xo = x; - yo = y; - zo = z; - - if (age++ >= lifetime) remove(); - - move(xd, yd, zd); - xd *= 0.96f; - yd *= 0.96f; - zd *= 0.96f; - - if (onGround) - { - xd *= 0.7f; - zd *= 0.7f; - } - } -}; -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__FlameParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__FlameParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__FlameParticle_H__ + +//package net.minecraft.client.particle; + +#include "Particle.h" +#include "../renderer/Tesselator.h" +#include "../../world/level/Level.h" + +class FlameParticle: public Particle +{ + typedef Particle super; + float oSize; +public: + FlameParticle(Level* level, float x, float y, float z, float xa, float ya, float za) + : super(level, x, y, z, xa, ya, za) + { + xd = xd * 0.01f + xa; + yd = yd * 0.01f + ya; + zd = zd * 0.01f + za; + x += (sharedRandom.nextFloat()-sharedRandom.nextFloat()) * 0.05f; + y += (sharedRandom.nextFloat()-sharedRandom.nextFloat()) * 0.05f; + z += (sharedRandom.nextFloat()-sharedRandom.nextFloat()) * 0.05f; + + //printf("xd, yd, zd: %f, %f, %f\n", xd, yd, zd); + + oSize = size; + rCol = gCol = bCol = 1.0f; + + lifetime = (int)(8.0f/(Mth::random()*0.8f+0.2f))+4; + noPhysics = true; + tex = 48; + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { + float s = (age + a) / (float) lifetime; + size = oSize * (1 - s*s*0.5f); + super::render(t, a, xa, ya, za, xa2, za2); + } + + float getBrightness(float a) { + return 1.0f; +/* + float l = (age+a)/lifetime; + if (l<0) l = 0; + if (l>1) l = 1; + float br = super::getBrightness(a); + + return br*l+(1-l); +*/ + } + + void tick() + { + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + move(xd, yd, zd); + xd *= 0.96f; + yd *= 0.96f; + zd *= 0.96f; + + if (onGround) + { + xd *= 0.7f; + zd *= 0.7f; + } + } +}; +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__FlameParticle_H__*/ diff --git a/src/client/particle/HugeExplosionParticle.h b/src/client/particle/HugeExplosionParticle.h index 4bcd297..7da2281 100755 --- a/src/client/particle/HugeExplosionParticle.h +++ b/src/client/particle/HugeExplosionParticle.h @@ -1,72 +1,72 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionParticle_H__ - -//package net.minecraft.client.particle; - -#include "../renderer/Tesselator.h" -#include "../renderer/Textures.h" -#include "../../world/level/Level.h" - -class HugeExplosionParticle: public Particle -{ - typedef Particle super; -public: - HugeExplosionParticle(Textures* textures, Level* level, float x, float y, float z, float xa, float ya, float za) - : super(level, x, y, z, 0, 0, 0), - life(0), - lifeTime(6 + sharedRandom.nextInt(4)), - textures(textures), - size(1 - xa * 0.5f) - { - rCol = gCol = bCol = sharedRandom.nextFloat() * 0.6f + 0.4f; - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { - int tex = (int) ((life + a) * 15 / lifeTime); - if (tex > 15) return; - textures->loadAndBindTexture("misc/explosion.png"); - - float u0 = (tex % 4) / 4.0f; - float u1 = u0 + 0.999f / 4.0f; - float v0 = (tex / 4) / 4.0f; - float v1 = v0 + 0.999f / 4.0f; - -// xa2 = 0; -// za2 = 1; - - float r = size + size; - - float x = xo + (this->x - xo) * a - xOff; - float y = yo + (this->y - yo) * a - yOff; - float z = zo + (this->z - zo) * a - zOff; - const float xar = xa * r, yar = ya * r, zar = za * r; - const float xa2r = xa2 * r, za2r = za2 * r; - t.begin(); - t.color(rCol, gCol, bCol, 1.0f); - t.vertexUV(x - xar - xa2r, y - yar, z - zar - za2r, u1, v1); - t.vertexUV(x - xar + xa2r, y + yar, z - zar + za2r, u1, v0); - t.vertexUV(x + xar + xa2r, y + yar, z + zar + za2r, u0, v0); - t.vertexUV(x + xar - xa2r, y - yar, z + zar - za2r, u0, v1); - t.draw(); - //glPolygonOffset(0, 0.0f); - } - - void tick() { - xo = x; - yo = y; - zo = z; - life++; - if (life == lifeTime) remove(); - } - - int getParticleTexture() { - return ParticleEngine::ENTITY_PARTICLE_TEXTURE; - } -private: - int life; - int lifeTime; - float size; - Textures* textures; -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionParticle_H__ + +//package net.minecraft.client.particle; + +#include "../renderer/Tesselator.h" +#include "../renderer/Textures.h" +#include "../../world/level/Level.h" + +class HugeExplosionParticle: public Particle +{ + typedef Particle super; +public: + HugeExplosionParticle(Textures* textures, Level* level, float x, float y, float z, float xa, float ya, float za) + : super(level, x, y, z, 0, 0, 0), + life(0), + lifeTime(6 + sharedRandom.nextInt(4)), + textures(textures), + size(1 - xa * 0.5f) + { + rCol = gCol = bCol = sharedRandom.nextFloat() * 0.6f + 0.4f; + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { + int tex = (int) ((life + a) * 15 / lifeTime); + if (tex > 15) return; + textures->loadAndBindTexture("misc/explosion.png"); + + float u0 = (tex % 4) / 4.0f; + float u1 = u0 + 0.999f / 4.0f; + float v0 = (tex / 4) / 4.0f; + float v1 = v0 + 0.999f / 4.0f; + +// xa2 = 0; +// za2 = 1; + + float r = size + size; + + float x = xo + (this->x - xo) * a - xOff; + float y = yo + (this->y - yo) * a - yOff; + float z = zo + (this->z - zo) * a - zOff; + const float xar = xa * r, yar = ya * r, zar = za * r; + const float xa2r = xa2 * r, za2r = za2 * r; + t.begin(); + t.color(rCol, gCol, bCol, 1.0f); + t.vertexUV(x - xar - xa2r, y - yar, z - zar - za2r, u1, v1); + t.vertexUV(x - xar + xa2r, y + yar, z - zar + za2r, u1, v0); + t.vertexUV(x + xar + xa2r, y + yar, z + zar + za2r, u0, v0); + t.vertexUV(x + xar - xa2r, y - yar, z + zar - za2r, u0, v1); + t.draw(); + //glPolygonOffset(0, 0.0f); + } + + void tick() { + xo = x; + yo = y; + zo = z; + life++; + if (life == lifeTime) remove(); + } + + int getParticleTexture() { + return ParticleEngine::ENTITY_PARTICLE_TEXTURE; + } +private: + int life; + int lifeTime; + float size; + Textures* textures; +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionParticle_H__*/ diff --git a/src/client/particle/HugeExplosionSeedParticle.h b/src/client/particle/HugeExplosionSeedParticle.h index 6b06e8c..4186e68 100755 --- a/src/client/particle/HugeExplosionSeedParticle.h +++ b/src/client/particle/HugeExplosionSeedParticle.h @@ -1,40 +1,40 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionSeedParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionSeedParticle_H__ - -//package net.minecraft.client.particle; - -#include "Particle.h" - -class HugeExplosionSeedParticle: public Particle -{ - typedef Particle super; -public: - HugeExplosionSeedParticle(Level* level, float x, float y, float z, float xa, float ya, float za) - : super(level, x, y, z, 0, 0, 0), - life(0), - lifeTime(8) - { - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) {} - - void tick() { - for (int i = 0; i < 6; i++) { - float xx = x + (sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 4; - float yy = y + (sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 4; - float zz = z + (sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 4; - level->addParticle(PARTICLETYPE(largeexplode), xx, yy, zz, life / (float) lifeTime, 0, 0); - } - life++; - if (life == lifeTime) remove(); - } - - int getParticleTexture() { - return ParticleEngine::TERRAIN_TEXTURE; - } -private: - int life; - int lifeTime; -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionSeedParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionSeedParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionSeedParticle_H__ + +//package net.minecraft.client.particle; + +#include "Particle.h" + +class HugeExplosionSeedParticle: public Particle +{ + typedef Particle super; +public: + HugeExplosionSeedParticle(Level* level, float x, float y, float z, float xa, float ya, float za) + : super(level, x, y, z, 0, 0, 0), + life(0), + lifeTime(8) + { + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) {} + + void tick() { + for (int i = 0; i < 6; i++) { + float xx = x + (sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 4; + float yy = y + (sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 4; + float zz = z + (sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 4; + level->addParticle(PARTICLETYPE(largeexplode), xx, yy, zz, life / (float) lifeTime, 0, 0); + } + life++; + if (life == lifeTime) remove(); + } + + int getParticleTexture() { + return ParticleEngine::TERRAIN_TEXTURE; + } +private: + int life; + int lifeTime; +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__HugeExplosionSeedParticle_H__*/ diff --git a/src/client/particle/LavaParticle.h b/src/client/particle/LavaParticle.h index 86b6fe0..b984550 100755 --- a/src/client/particle/LavaParticle.h +++ b/src/client/particle/LavaParticle.h @@ -1,63 +1,63 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__LavaParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__LavaParticle_H__ - -//package net.minecraft.client.particle; - -#include "../renderer/Tesselator.h" -#include "../../world/level/Level.h" - -class LavaParticle: public Particle -{ - typedef Particle super; - float oSize; -public: - LavaParticle(Level* level, float x, float y, float z) - : super(level, x, y, z, 0, 0, 0) - { - xd *= 0.8f; - yd *= 0.8f; - zd *= 0.8f; - yd = sharedRandom.nextFloat() * 0.4f + 0.05f; - - rCol = gCol = bCol = 1; - size *= (sharedRandom.nextFloat() * 2 + 0.2f); - oSize = size; - - lifetime = (int) (16 / (Mth::random() * 0.8 + 0.2)); - noPhysics = false; - tex = 49; - } - - float getBrightness(float a) { - return 1; - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { - float s = (age + a) / (float) lifetime; - size = oSize * (1 - s*s); - super::render(t, a, xa, ya, za, xa2, za2); - } - - void tick() { - xo = x; - yo = y; - zo = z; - - if (age++ >= lifetime) remove(); - float odds = age / (float) lifetime; - if (sharedRandom.nextFloat() > odds) level->addParticle(PARTICLETYPE(smoke), x, y, z, xd, yd, zd); - - yd -= 0.03f; - move(xd, yd, zd); - xd *= 0.999f; - yd *= 0.999f; - zd *= 0.999f; - - if (onGround) { - xd *= 0.7f; - zd *= 0.7f; - } - } -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__LavaParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__LavaParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__LavaParticle_H__ + +//package net.minecraft.client.particle; + +#include "../renderer/Tesselator.h" +#include "../../world/level/Level.h" + +class LavaParticle: public Particle +{ + typedef Particle super; + float oSize; +public: + LavaParticle(Level* level, float x, float y, float z) + : super(level, x, y, z, 0, 0, 0) + { + xd *= 0.8f; + yd *= 0.8f; + zd *= 0.8f; + yd = sharedRandom.nextFloat() * 0.4f + 0.05f; + + rCol = gCol = bCol = 1; + size *= (sharedRandom.nextFloat() * 2 + 0.2f); + oSize = size; + + lifetime = (int) (16 / (Mth::random() * 0.8 + 0.2)); + noPhysics = false; + tex = 49; + } + + float getBrightness(float a) { + return 1; + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { + float s = (age + a) / (float) lifetime; + size = oSize * (1 - s*s); + super::render(t, a, xa, ya, za, xa2, za2); + } + + void tick() { + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + float odds = age / (float) lifetime; + if (sharedRandom.nextFloat() > odds) level->addParticle(PARTICLETYPE(smoke), x, y, z, xd, yd, zd); + + yd -= 0.03f; + move(xd, yd, zd); + xd *= 0.999f; + yd *= 0.999f; + zd *= 0.999f; + + if (onGround) { + xd *= 0.7f; + zd *= 0.7f; + } + } +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__LavaParticle_H__*/ diff --git a/src/client/particle/Particle.cpp b/src/client/particle/Particle.cpp index 2a31e2a..ecb3bdf 100755 --- a/src/client/particle/Particle.cpp +++ b/src/client/particle/Particle.cpp @@ -1,99 +1,99 @@ -#include "Particle.h" - -float - Particle::xOff = 0, - Particle::yOff = 0, - Particle::zOff = 0; - -Particle::Particle( Level* level, float x, float y, float z, float xa, float ya, float za ) -: super(level), - age(0), - gravity(0), - rCol(1), gCol(1), bCol(1), - tex(0) -{ - setSize(0.2f, 0.2f); - heightOffset = bbHeight / 2.0f; - setPos(x, y, z); - - xd = xa + (float) (Mth::random() * 2 - 1) * 0.4f; - yd = ya + (float) (Mth::random() * 2 - 1) * 0.4f; - zd = za + (float) (Mth::random() * 2 - 1) * 0.4f; - float speed = (float) (Mth::random() + Mth::random() + 1) * 0.15f; - - float dd = (float) (Mth::sqrt(xd * xd + yd * yd + zd * zd)); - const float mul = 0.4f * speed / dd; - xd = xd * mul; - yd = yd * mul + 0.1f; - zd = zd * mul; - - uo = sharedRandom.nextFloat() * 3; - vo = sharedRandom.nextFloat() * 3; - - size = (sharedRandom.nextFloat() * 0.5f + 0.5f) * 2; - - lifetime = (int) (4.0f / (sharedRandom.nextFloat() * 0.9f + 0.1f)); - makeStepSound = false; -} - -Particle* Particle::setPower( float power ) -{ - xd *= power; - yd = (yd - 0.1f) * power + 0.1f; - zd *= power; - return this; -} - -Particle* Particle::scale( float scale ) -{ - setSize(0.2f * scale, 0.2f * scale); - size *= scale; - return this; -} - -void Particle::tick() -{ - xo = x; - yo = y; - zo = z; - - if (age++ >= lifetime) remove(); - - yd -= 0.04f * gravity; - move(xd, yd, zd); - xd *= 0.98f; - yd *= 0.98f; - zd *= 0.98f; - - if (onGround) { - xd *= 0.7f; - zd *= 0.7f; - } -} - -void Particle::render( Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2 ) -{ - float u0 = (tex % 16) / 16.0f; - float u1 = u0 + 0.999f / 16.0f; - float v0 = (tex / 16) / 16.0f; - float v1 = v0 + 0.999f / 16.0f; - float r = 0.1f * size; - - float x = (float) (xo + (this->x - xo) * a - xOff); - float y = (float) (yo + (this->y - yo) * a - yOff); - float z = (float) (zo + (this->z - zo) * a - zOff); - - float br = getBrightness(a); - t.color(rCol * br, gCol * br, bCol * br); - - t.vertexUV(x - xa * r - xa2 * r, y - ya * r, z - za * r - za2 * r, u1, v1); - t.vertexUV(x - xa * r + xa2 * r, y + ya * r, z - za * r + za2 * r, u1, v0); - t.vertexUV(x + xa * r + xa2 * r, y + ya * r, z + za * r + za2 * r, u0, v0); - t.vertexUV(x + xa * r - xa2 * r, y - ya * r, z + za * r - za2 * r, u0, v1); - //printf("uv: %f, %f, %f, %f\n", u0, v0, u1, v1); -} - -int Particle::getParticleTexture() -{ - return ParticleEngine::MISC_TEXTURE; -} +#include "Particle.h" + +float + Particle::xOff = 0, + Particle::yOff = 0, + Particle::zOff = 0; + +Particle::Particle( Level* level, float x, float y, float z, float xa, float ya, float za ) +: super(level), + age(0), + gravity(0), + rCol(1), gCol(1), bCol(1), + tex(0) +{ + setSize(0.2f, 0.2f); + heightOffset = bbHeight / 2.0f; + setPos(x, y, z); + + xd = xa + (float) (Mth::random() * 2 - 1) * 0.4f; + yd = ya + (float) (Mth::random() * 2 - 1) * 0.4f; + zd = za + (float) (Mth::random() * 2 - 1) * 0.4f; + float speed = (float) (Mth::random() + Mth::random() + 1) * 0.15f; + + float dd = (float) (Mth::sqrt(xd * xd + yd * yd + zd * zd)); + const float mul = 0.4f * speed / dd; + xd = xd * mul; + yd = yd * mul + 0.1f; + zd = zd * mul; + + uo = sharedRandom.nextFloat() * 3; + vo = sharedRandom.nextFloat() * 3; + + size = (sharedRandom.nextFloat() * 0.5f + 0.5f) * 2; + + lifetime = (int) (4.0f / (sharedRandom.nextFloat() * 0.9f + 0.1f)); + makeStepSound = false; +} + +Particle* Particle::setPower( float power ) +{ + xd *= power; + yd = (yd - 0.1f) * power + 0.1f; + zd *= power; + return this; +} + +Particle* Particle::scale( float scale ) +{ + setSize(0.2f * scale, 0.2f * scale); + size *= scale; + return this; +} + +void Particle::tick() +{ + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + yd -= 0.04f * gravity; + move(xd, yd, zd); + xd *= 0.98f; + yd *= 0.98f; + zd *= 0.98f; + + if (onGround) { + xd *= 0.7f; + zd *= 0.7f; + } +} + +void Particle::render( Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2 ) +{ + float u0 = (tex % 16) / 16.0f; + float u1 = u0 + 0.999f / 16.0f; + float v0 = (tex / 16) / 16.0f; + float v1 = v0 + 0.999f / 16.0f; + float r = 0.1f * size; + + float x = (float) (xo + (this->x - xo) * a - xOff); + float y = (float) (yo + (this->y - yo) * a - yOff); + float z = (float) (zo + (this->z - zo) * a - zOff); + + float br = getBrightness(a); + t.color(rCol * br, gCol * br, bCol * br); + + t.vertexUV(x - xa * r - xa2 * r, y - ya * r, z - za * r - za2 * r, u1, v1); + t.vertexUV(x - xa * r + xa2 * r, y + ya * r, z - za * r + za2 * r, u1, v0); + t.vertexUV(x + xa * r + xa2 * r, y + ya * r, z + za * r + za2 * r, u0, v0); + t.vertexUV(x + xa * r - xa2 * r, y - ya * r, z + za * r - za2 * r, u0, v1); + //printf("uv: %f, %f, %f, %f\n", u0, v0, u1, v1); +} + +int Particle::getParticleTexture() +{ + return ParticleEngine::MISC_TEXTURE; +} diff --git a/src/client/particle/Particle.h b/src/client/particle/Particle.h index dd96bb4..c256a22 100755 --- a/src/client/particle/Particle.h +++ b/src/client/particle/Particle.h @@ -1,46 +1,46 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__Particle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__Particle_H__ - -//package net.minecraft.client.particle; - -#include "../renderer/Tesselator.h" -#include "../../world/entity/Entity.h" -#include "../../world/level/Level.h" -#include "ParticleEngine.h" - -class CompoundTag; - -class Particle: public Entity { - typedef Entity super; - -public: - static float xOff, yOff, zOff; - - Particle(Level* level, float x, float y, float z, float xa, float ya, float za); - - virtual ~Particle() {} - - Particle* setPower(float power); - Particle* scale(float scale); - - virtual void tick(); - virtual void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2); - - virtual int getParticleTexture(); - - virtual void addAdditonalSaveData(CompoundTag* entityTag) {} - virtual void readAdditionalSaveData(CompoundTag* tag) {} - - virtual int getEntityTypeId() const { return 0; } - -protected: - int tex; - float uo, vo; - int age; - int lifetime; - float size; - float gravity; - float rCol, gCol, bCol; -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__Particle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__Particle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__Particle_H__ + +//package net.minecraft.client.particle; + +#include "../renderer/Tesselator.h" +#include "../../world/entity/Entity.h" +#include "../../world/level/Level.h" +#include "ParticleEngine.h" + +class CompoundTag; + +class Particle: public Entity { + typedef Entity super; + +public: + static float xOff, yOff, zOff; + + Particle(Level* level, float x, float y, float z, float xa, float ya, float za); + + virtual ~Particle() {} + + Particle* setPower(float power); + Particle* scale(float scale); + + virtual void tick(); + virtual void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2); + + virtual int getParticleTexture(); + + virtual void addAdditonalSaveData(CompoundTag* entityTag) {} + virtual void readAdditionalSaveData(CompoundTag* tag) {} + + virtual int getEntityTypeId() const { return 0; } + +protected: + int tex; + float uo, vo; + int age; + int lifetime; + float size; + float gravity; + float rCol, gCol, bCol; +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__Particle_H__*/ diff --git a/src/client/particle/ParticleEngine.cpp b/src/client/particle/ParticleEngine.cpp index 725b1da..39f51ba 100755 --- a/src/client/particle/ParticleEngine.cpp +++ b/src/client/particle/ParticleEngine.cpp @@ -1,157 +1,157 @@ -#include "ParticleEngine.h" -#include "Particle.h" -#include "TerrainParticle.h" -#include "../renderer/Textures.h" -#include "../../world/level/Level.h" -#include "../../NinecraftApp.h" - -ParticleEngine::ParticleEngine(Level* level, Textures* textures) -: level(level), - textures(textures) -{ - textures->loadTexture("particles.png"); -} - -ParticleEngine::~ParticleEngine() { - clear(); -} - -void ParticleEngine::add(Particle* p) { - int t = p->getParticleTexture(); - particles[t].push_back(p); -} - -void ParticleEngine::tick() { - for (int tt = 0; tt < TEXTURE_COUNT; tt++) { - ParticleList& list = particles[tt]; - unsigned int size = list.size(); - for (unsigned int i = 0; i < size; ++i) { - Particle* p = list[i]; - p->tick(); - if (p->removed) { - list.erase(list.begin() + (i--)); - --size; - delete p; - } - } - } -} - -void ParticleEngine::render(Entity* player, float a) { - float xa = (float) Mth::cos(player->yRot * Mth::PI / 180); - float za = (float) Mth::sin(player->yRot * Mth::PI / 180); - - float xa2 = -za * (float) Mth::sin(player->xRot * Mth::PI / 180); - float za2 = xa * (float) Mth::sin(player->xRot * Mth::PI / 180); - float ya = (float) Mth::cos(player->xRot * Mth::PI / 180); - - Particle::xOff = (player->xOld+(player->x-player->xOld)*a); - Particle::yOff = (player->yOld+(player->y-player->yOld)*a); - Particle::zOff = (player->zOld+(player->z-player->zOld)*a); - for (int tt = 0; tt < 3; tt++) { - if (particles[tt].size() == 0) continue; - - if (tt == MISC_TEXTURE) - textures->loadAndBindTexture("particles.png"); - else if (tt == TERRAIN_TEXTURE) - textures->loadAndBindTexture("terrain.png"); - else if (tt == ITEM_TEXTURE) - textures->loadAndBindTexture("gui/items.png"); - - Tesselator& t = Tesselator::instance; - t.begin(); - for (unsigned int i = 0; i < particles[tt].size(); i++) { - Particle* p = particles[tt][i]; - - p->render(t, a, xa, ya, za, xa2, za2); - } - t.draw(); - } - - renderLit(player, a); -} - -void ParticleEngine::renderLit(Entity* player, float a) { - int tt = ENTITY_PARTICLE_TEXTURE; - ParticleList& pl = particles[tt]; - const int size = pl.size(); - if (size == 0) return; - - float xa = Mth::cos(player->yRot * Mth::DEGRAD); - float za = Mth::sin(player->yRot * Mth::DEGRAD); - - float xs = Mth::sin(player->xRot * Mth::DEGRAD); - float xa2 = -za * xs; - float za2 = xa * xs; - float ya = Mth::cos(player->xRot * Mth::DEGRAD); - - Tesselator& t = Tesselator::instance; - for (int i = 0; i < size; i++) { - Particle* p = pl[i]; - p->render(t, a, xa, ya, za, xa2, za2); - } -} - -void ParticleEngine::setLevel(Level* level) { - this->level = level; - clear(); -} - -void ParticleEngine::destroy(int x, int y, int z) { - int tid = level->getTile(x, y, z); - if (tid == 0) return; - int data = level->getData(x, y, z); - - //static Stopwatch sw; - //sw.start(); - Tile* tile = Tile::tiles[tid]; - const int SD = 3; - for (int xx = 0; xx < SD; xx++) - for (int yy = 1; yy < SD; yy++) - for (int zz = 0; zz < SD; zz++) { - float xp = x + (xx + 0.5f) / SD; - float yp = y + (yy + 0.5f) / SD; - float zp = z + (zz + 0.5f) / SD; - add((new TerrainParticle(level, xp, yp, zp, 2*(xp - x - 0.5f), 2*(yp - y - 0.5f), 2*(zp - z - 0.5f), tile, data))->init(x, y, z)); - } - //sw.stop(); - //sw.print("gen destroy particles: "); -} - -void ParticleEngine::crack(int x, int y, int z, int face) { - int tid = level->getTile(x, y, z); - if (tid == 0) return; - int data = level->getData(x, y, z); - Tile* tile = Tile::tiles[tid]; - float r = 0.10f; - float xp = x + random.nextFloat() * ((tile->xx1 - tile->xx0) - r * 2) + r + tile->xx0; - float yp = y + random.nextFloat() * ((tile->yy1 - tile->yy0) - r * 2) + r + tile->yy0; - float zp = z + random.nextFloat() * ((tile->zz1 - tile->zz0) - r * 2) + r + tile->zz0; - switch (face) { - case 0: yp = y + tile->yy0 - r; break; - case 1: yp = y + tile->yy1 + r; break; - case 2: zp = z + tile->zz0 - r; break; - case 3: zp = z + tile->zz1 + r; break; - case 4: xp = x + tile->xx0 - r; break; - case 5: xp = x + tile->xx1 + r; break; - } - add((new TerrainParticle(level, xp, yp, zp, 0, 0, 0, tile, data))->init(x, y, z)->setPower(0.2f)->scale(0.6f)); -} - -std::string ParticleEngine::countParticles() { - std::stringstream ss; - int count = 0; - for (int i = 0; i < TEXTURE_COUNT; ++i) - count += particles[i].size(); - ss << count; - return ss.str(); -} - -void ParticleEngine::clear() -{ - for (int tt = 0; tt < TEXTURE_COUNT; tt++) { - for (unsigned int i = 0; i < particles[tt].size(); i++) - delete particles[tt][i]; - particles[tt].clear(); - } -} +#include "ParticleEngine.h" +#include "Particle.h" +#include "TerrainParticle.h" +#include "../renderer/Textures.h" +#include "../../world/level/Level.h" +#include "../../NinecraftApp.h" + +ParticleEngine::ParticleEngine(Level* level, Textures* textures) +: level(level), + textures(textures) +{ + textures->loadTexture("particles.png"); +} + +ParticleEngine::~ParticleEngine() { + clear(); +} + +void ParticleEngine::add(Particle* p) { + int t = p->getParticleTexture(); + particles[t].push_back(p); +} + +void ParticleEngine::tick() { + for (int tt = 0; tt < TEXTURE_COUNT; tt++) { + ParticleList& list = particles[tt]; + unsigned int size = list.size(); + for (unsigned int i = 0; i < size; ++i) { + Particle* p = list[i]; + p->tick(); + if (p->removed) { + list.erase(list.begin() + (i--)); + --size; + delete p; + } + } + } +} + +void ParticleEngine::render(Entity* player, float a) { + float xa = (float) Mth::cos(player->yRot * Mth::PI / 180); + float za = (float) Mth::sin(player->yRot * Mth::PI / 180); + + float xa2 = -za * (float) Mth::sin(player->xRot * Mth::PI / 180); + float za2 = xa * (float) Mth::sin(player->xRot * Mth::PI / 180); + float ya = (float) Mth::cos(player->xRot * Mth::PI / 180); + + Particle::xOff = (player->xOld+(player->x-player->xOld)*a); + Particle::yOff = (player->yOld+(player->y-player->yOld)*a); + Particle::zOff = (player->zOld+(player->z-player->zOld)*a); + for (int tt = 0; tt < 3; tt++) { + if (particles[tt].size() == 0) continue; + + if (tt == MISC_TEXTURE) + textures->loadAndBindTexture("particles.png"); + else if (tt == TERRAIN_TEXTURE) + textures->loadAndBindTexture("terrain.png"); + else if (tt == ITEM_TEXTURE) + textures->loadAndBindTexture("gui/items.png"); + + Tesselator& t = Tesselator::instance; + t.begin(); + for (unsigned int i = 0; i < particles[tt].size(); i++) { + Particle* p = particles[tt][i]; + + p->render(t, a, xa, ya, za, xa2, za2); + } + t.draw(); + } + + renderLit(player, a); +} + +void ParticleEngine::renderLit(Entity* player, float a) { + int tt = ENTITY_PARTICLE_TEXTURE; + ParticleList& pl = particles[tt]; + const int size = pl.size(); + if (size == 0) return; + + float xa = Mth::cos(player->yRot * Mth::DEGRAD); + float za = Mth::sin(player->yRot * Mth::DEGRAD); + + float xs = Mth::sin(player->xRot * Mth::DEGRAD); + float xa2 = -za * xs; + float za2 = xa * xs; + float ya = Mth::cos(player->xRot * Mth::DEGRAD); + + Tesselator& t = Tesselator::instance; + for (int i = 0; i < size; i++) { + Particle* p = pl[i]; + p->render(t, a, xa, ya, za, xa2, za2); + } +} + +void ParticleEngine::setLevel(Level* level) { + this->level = level; + clear(); +} + +void ParticleEngine::destroy(int x, int y, int z) { + int tid = level->getTile(x, y, z); + if (tid == 0) return; + int data = level->getData(x, y, z); + + //static Stopwatch sw; + //sw.start(); + Tile* tile = Tile::tiles[tid]; + const int SD = 3; + for (int xx = 0; xx < SD; xx++) + for (int yy = 1; yy < SD; yy++) + for (int zz = 0; zz < SD; zz++) { + float xp = x + (xx + 0.5f) / SD; + float yp = y + (yy + 0.5f) / SD; + float zp = z + (zz + 0.5f) / SD; + add((new TerrainParticle(level, xp, yp, zp, 2*(xp - x - 0.5f), 2*(yp - y - 0.5f), 2*(zp - z - 0.5f), tile, data))->init(x, y, z)); + } + //sw.stop(); + //sw.print("gen destroy particles: "); +} + +void ParticleEngine::crack(int x, int y, int z, int face) { + int tid = level->getTile(x, y, z); + if (tid == 0) return; + int data = level->getData(x, y, z); + Tile* tile = Tile::tiles[tid]; + float r = 0.10f; + float xp = x + random.nextFloat() * ((tile->xx1 - tile->xx0) - r * 2) + r + tile->xx0; + float yp = y + random.nextFloat() * ((tile->yy1 - tile->yy0) - r * 2) + r + tile->yy0; + float zp = z + random.nextFloat() * ((tile->zz1 - tile->zz0) - r * 2) + r + tile->zz0; + switch (face) { + case 0: yp = y + tile->yy0 - r; break; + case 1: yp = y + tile->yy1 + r; break; + case 2: zp = z + tile->zz0 - r; break; + case 3: zp = z + tile->zz1 + r; break; + case 4: xp = x + tile->xx0 - r; break; + case 5: xp = x + tile->xx1 + r; break; + } + add((new TerrainParticle(level, xp, yp, zp, 0, 0, 0, tile, data))->init(x, y, z)->setPower(0.2f)->scale(0.6f)); +} + +std::string ParticleEngine::countParticles() { + std::stringstream ss; + int count = 0; + for (int i = 0; i < TEXTURE_COUNT; ++i) + count += particles[i].size(); + ss << count; + return ss.str(); +} + +void ParticleEngine::clear() +{ + for (int tt = 0; tt < TEXTURE_COUNT; tt++) { + for (unsigned int i = 0; i < particles[tt].size(); i++) + delete particles[tt][i]; + particles[tt].clear(); + } +} diff --git a/src/client/particle/ParticleEngine.h b/src/client/particle/ParticleEngine.h index b75c248..d1d1f41 100755 --- a/src/client/particle/ParticleEngine.h +++ b/src/client/particle/ParticleEngine.h @@ -1,54 +1,54 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__ParticleEngine_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__ParticleEngine_H__ - -//package net.minecraft.client.particle; - -#include -#include "../../world/entity/Entity.h" -#include "../../world/level/tile/Tile.h" -#include "../renderer/gles.h" - -class Textures; -class Level; -class Particle; -typedef std::vector ParticleList; - -class ParticleEngine -{ -public: - static const int MISC_TEXTURE = 0; - static const int TERRAIN_TEXTURE = 1; - static const int ITEM_TEXTURE = 2; - static const int ENTITY_PARTICLE_TEXTURE = 3; - - static const int TEXTURE_COUNT = 4; - - ParticleEngine(Level* level, Textures* textures); - ~ParticleEngine(); - - void add(Particle* p); - void destroy(int x, int y, int z); - - void tick(); - void render(Entity* player, float a); - void renderLit(Entity* player, float a); - - void setLevel(Level* level); - - void crack(int x, int y, int z, int face); - - std::string countParticles(); - -protected: - void clear(); - - Level* level; - GLuint textureIds[4]; - -private: - ParticleList particles[4]; - Textures* textures; - Random random; -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__ParticleEngine_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__ParticleEngine_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__ParticleEngine_H__ + +//package net.minecraft.client.particle; + +#include +#include "../../world/entity/Entity.h" +#include "../../world/level/tile/Tile.h" +#include "../renderer/gles.h" + +class Textures; +class Level; +class Particle; +typedef std::vector ParticleList; + +class ParticleEngine +{ +public: + static const int MISC_TEXTURE = 0; + static const int TERRAIN_TEXTURE = 1; + static const int ITEM_TEXTURE = 2; + static const int ENTITY_PARTICLE_TEXTURE = 3; + + static const int TEXTURE_COUNT = 4; + + ParticleEngine(Level* level, Textures* textures); + ~ParticleEngine(); + + void add(Particle* p); + void destroy(int x, int y, int z); + + void tick(); + void render(Entity* player, float a); + void renderLit(Entity* player, float a); + + void setLevel(Level* level); + + void crack(int x, int y, int z, int face); + + std::string countParticles(); + +protected: + void clear(); + + Level* level; + GLuint textureIds[4]; + +private: + ParticleList particles[4]; + Textures* textures; + Random random; +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__ParticleEngine_H__*/ diff --git a/src/client/particle/ParticleInclude.h b/src/client/particle/ParticleInclude.h index dadcd4b..719729c 100755 --- a/src/client/particle/ParticleInclude.h +++ b/src/client/particle/ParticleInclude.h @@ -1,17 +1,17 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__ParticleInclude_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__ParticleInclude_H__ - -#include "Particle.h" - -#include "BubbleParticle.h" -#include "CritParticle2.h" -#include "ExplodeParticle.h" -#include "FlameParticle.h" -#include "LavaParticle.h" -#include "RedDustParticle.h" -#include "SmokeParticle.h" -#include "TerrainParticle.h" -#include "HugeExplosionSeedParticle.h" -#include "HugeExplosionParticle.h" - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__ParticleInclude_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__ParticleInclude_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__ParticleInclude_H__ + +#include "Particle.h" + +#include "BubbleParticle.h" +#include "CritParticle2.h" +#include "ExplodeParticle.h" +#include "FlameParticle.h" +#include "LavaParticle.h" +#include "RedDustParticle.h" +#include "SmokeParticle.h" +#include "TerrainParticle.h" +#include "HugeExplosionSeedParticle.h" +#include "HugeExplosionParticle.h" + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__ParticleInclude_H__*/ diff --git a/src/client/particle/RedDustParticle.h b/src/client/particle/RedDustParticle.h index 67a6faf..bf19084 100755 --- a/src/client/particle/RedDustParticle.h +++ b/src/client/particle/RedDustParticle.h @@ -1,73 +1,73 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__RedDustParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__RedDustParticle_H__ - -//package net.minecraft.client.particle; - -#include "../renderer/Tesselator.h" -#include "../../world/level/Level.h" - - -class RedDustParticle: public Particle -{ - typedef Particle super; -public: - float oSize; - - RedDustParticle(Level* level, float x, float y, float z, float rCol, float gCol, float bCol, float scale = 1.0f) - : super(level, x, y, z, 0, 0, 0) - { - xd *= 0.1f; - yd *= 0.1f; - zd *= 0.1f; - - if (rCol == 0) { - rCol = 1; - } - float brr = (float) Mth::random() * 0.4f + 0.6f; - this->rCol = ((float) (Mth::random() * 0.2f) + 0.8f) * rCol * brr; - this->gCol = ((float) (Mth::random() * 0.2f) + 0.8f) * gCol * brr; - this->bCol = ((float) (Mth::random() * 0.2f) + 0.8f) * bCol * brr; - size *= 0.75f; - size *= scale; - oSize = size; - - lifetime = (int) (8 / (Mth::random() * 0.8 + 0.2)); - lifetime = (int)(lifetime * scale); - noPhysics = false; - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { - float l = ((age + a) / lifetime) * 32; - if (l < 0) l = 0; - if (l > 1) l = 1; - - size = oSize * l; - super::render(t, a, xa, ya, za, xa2, za2); - } - - void tick() { - xo = x; - yo = y; - zo = z; - - if (age++ >= lifetime) remove(); - - tex = 7 - age * 8 / lifetime; - - move(xd, yd, zd); - if (y == yo) { - xd *= 1.1f; - zd *= 1.1f; - } - xd *= 0.96f; - yd *= 0.96f; - zd *= 0.96f; - - if (onGround) { - xd *= 0.7f; - zd *= 0.7f; - } - } -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__RedDustParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__RedDustParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__RedDustParticle_H__ + +//package net.minecraft.client.particle; + +#include "../renderer/Tesselator.h" +#include "../../world/level/Level.h" + + +class RedDustParticle: public Particle +{ + typedef Particle super; +public: + float oSize; + + RedDustParticle(Level* level, float x, float y, float z, float rCol, float gCol, float bCol, float scale = 1.0f) + : super(level, x, y, z, 0, 0, 0) + { + xd *= 0.1f; + yd *= 0.1f; + zd *= 0.1f; + + if (rCol == 0) { + rCol = 1; + } + float brr = (float) Mth::random() * 0.4f + 0.6f; + this->rCol = ((float) (Mth::random() * 0.2f) + 0.8f) * rCol * brr; + this->gCol = ((float) (Mth::random() * 0.2f) + 0.8f) * gCol * brr; + this->bCol = ((float) (Mth::random() * 0.2f) + 0.8f) * bCol * brr; + size *= 0.75f; + size *= scale; + oSize = size; + + lifetime = (int) (8 / (Mth::random() * 0.8 + 0.2)); + lifetime = (int)(lifetime * scale); + noPhysics = false; + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { + float l = ((age + a) / lifetime) * 32; + if (l < 0) l = 0; + if (l > 1) l = 1; + + size = oSize * l; + super::render(t, a, xa, ya, za, xa2, za2); + } + + void tick() { + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + tex = 7 - age * 8 / lifetime; + + move(xd, yd, zd); + if (y == yo) { + xd *= 1.1f; + zd *= 1.1f; + } + xd *= 0.96f; + yd *= 0.96f; + zd *= 0.96f; + + if (onGround) { + xd *= 0.7f; + zd *= 0.7f; + } + } +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__RedDustParticle_H__*/ diff --git a/src/client/particle/SmokeParticle.h b/src/client/particle/SmokeParticle.h index 7e92f18..8e88176 100755 --- a/src/client/particle/SmokeParticle.h +++ b/src/client/particle/SmokeParticle.h @@ -1,69 +1,69 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__SmokeParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__SmokeParticle_H__ - -//package net.minecraft.client.particle; - -#include "Particle.h" - -class SmokeParticle: public Particle -{ - typedef Particle super; - -public: - float oSize; - - SmokeParticle(Level* level, float x, float y, float z, float xa, float ya, float za, float scale = 1.0f) - : super(level, x, y, z, 0, 0, 0) - { - xd *= 0.1f; - yd *= 0.1f; - zd *= 0.1f; - xd += xa; - yd += ya; - zd += za; - - rCol = gCol = bCol = (float) (Mth::random() * 0.5f); - size *= 0.75f; - size *= scale; - oSize = size; - - lifetime = (int) (scale * 8.0f / (Mth::random() * 0.8f + 0.2f)); - noPhysics = false; - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { - float l = ((age + a) / lifetime) * 32; - if (l < 0) l = 0; - if (l > 1) l = 1; - - size = oSize * l; - super::render(t, a, xa, ya, za, xa2, za2); - } - - void tick() { - xo = x; - yo = y; - zo = z; - - if (age++ >= lifetime) remove(); - - tex = 7 - age * 8 / lifetime; - - yd += 0.004f; - move(xd, yd, zd); - if (y == yo) { - xd *= 1.1f; - zd *= 1.1f; - } - xd *= 0.96f; - yd *= 0.96f; - zd *= 0.96f; - - if (onGround) { - xd *= 0.7f; - zd *= 0.7f; - } - } -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__SmokeParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__SmokeParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__SmokeParticle_H__ + +//package net.minecraft.client.particle; + +#include "Particle.h" + +class SmokeParticle: public Particle +{ + typedef Particle super; + +public: + float oSize; + + SmokeParticle(Level* level, float x, float y, float z, float xa, float ya, float za, float scale = 1.0f) + : super(level, x, y, z, 0, 0, 0) + { + xd *= 0.1f; + yd *= 0.1f; + zd *= 0.1f; + xd += xa; + yd += ya; + zd += za; + + rCol = gCol = bCol = (float) (Mth::random() * 0.5f); + size *= 0.75f; + size *= scale; + oSize = size; + + lifetime = (int) (scale * 8.0f / (Mth::random() * 0.8f + 0.2f)); + noPhysics = false; + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { + float l = ((age + a) / lifetime) * 32; + if (l < 0) l = 0; + if (l > 1) l = 1; + + size = oSize * l; + super::render(t, a, xa, ya, za, xa2, za2); + } + + void tick() { + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + tex = 7 - age * 8 / lifetime; + + yd += 0.004f; + move(xd, yd, zd); + if (y == yo) { + xd *= 1.1f; + zd *= 1.1f; + } + xd *= 0.96f; + yd *= 0.96f; + zd *= 0.96f; + + if (onGround) { + xd *= 0.7f; + zd *= 0.7f; + } + } +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__SmokeParticle_H__*/ diff --git a/src/client/particle/SplashParticle.h b/src/client/particle/SplashParticle.h index b895de1..deaee92 100755 --- a/src/client/particle/SplashParticle.h +++ b/src/client/particle/SplashParticle.h @@ -1,26 +1,26 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__SplashParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__SplashParticle_H__ - -//package net.minecraft.client.particle; - -#include "WaterDropParticle.h" -#include "../../world/level/Level.h" - -class SplashParticle: public WaterDropParticle -{ - typedef WaterDropParticle super; -public: - SplashParticle(Level* level, float x, float y, float z, float xa, float ya, float za) - : super(level, x, y, z) - { - gravity = 0.04f; - tex += 1; - if (ya == 0 && (xa != 0 || za != 0)) { - xd = xa; - yd = ya + 0.1; - zd = za; - } - } -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__SplashParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__SplashParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__SplashParticle_H__ + +//package net.minecraft.client.particle; + +#include "WaterDropParticle.h" +#include "../../world/level/Level.h" + +class SplashParticle: public WaterDropParticle +{ + typedef WaterDropParticle super; +public: + SplashParticle(Level* level, float x, float y, float z, float xa, float ya, float za) + : super(level, x, y, z) + { + gravity = 0.04f; + tex += 1; + if (ya == 0 && (xa != 0 || za != 0)) { + xd = xa; + yd = ya + 0.1; + zd = za; + } + } +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__SplashParticle_H__*/ diff --git a/src/client/particle/TakeAnimationParticle.h b/src/client/particle/TakeAnimationParticle.h index 78bad7f..9985edf 100755 --- a/src/client/particle/TakeAnimationParticle.h +++ b/src/client/particle/TakeAnimationParticle.h @@ -1,82 +1,82 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__TakeAnimationParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__TakeAnimationParticle_H__ - -//package net.minecraft.client.particle; - -#include "Particle.h" - -#include "../renderer/entity/EntityRenderDispatcher.h" -#include "../renderer/Tesselator.h" -#include "../../world/entity/Entity.h" -#include "../../world/level/Level.h" -#include "../../util/Mth.h" -#include "../../world/entity/item/ItemEntity.h" - -class TakeAnimationParticle: public Particle -{ - typedef Particle super; - -public: - //@todo:itementity - TakeAnimationParticle(Level* level, ItemEntity* item, Entity* target, float yOffs) - : super(level, item->x, item->y, item->z, item->xd, item->yd, item->zd), - e(level, item->x, item->y, item->z, item->item), - //bx(item->x), by(item->y), bz(item->z), - target(target), - yOffs(yOffs), - life(0), - lifeTime(3) - { - e.item.count = 1; - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { - float time = (life + a) / lifeTime; - time = time*time; - - float xo = e.x; - float yo = e.y; - float zo = e.z; - - float xt = target->xOld + (target->x - target->xOld) * a; - float yt = target->yOld + (target->y - target->yOld) * a+yOffs; - float zt = target->zOld + (target->z - target->zOld) * a; - - float xx = xo + (xt - xo) * time; - float yy = yo + (yt - yo) * time; - float zz = zo + (zt - zo) * time; - - int xTile = Mth::floor(xx); - int yTile = Mth::floor(yy + heightOffset * 0.5f); - int zTile = Mth::floor(zz); - - float br = level->getBrightness(xTile, yTile, zTile); - glColor4f2(br, br, br, 1); - - xx -= xOff; - yy -= yOff; - zz -= zOff; - - EntityRenderDispatcher::getInstance()->render(&e, xx, yy, zz, e.yRot, a); - } - - void tick() { - life++; - if (life == lifeTime) remove(); - } - - int getParticleTexture() { - return ParticleEngine::ENTITY_PARTICLE_TEXTURE; - } - -private: - //Entity* item; - ItemEntity e; - Entity* target; - //float bx, by, bz; - int life; - int lifeTime; - float yOffs; -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__TakeAnimationParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__TakeAnimationParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__TakeAnimationParticle_H__ + +//package net.minecraft.client.particle; + +#include "Particle.h" + +#include "../renderer/entity/EntityRenderDispatcher.h" +#include "../renderer/Tesselator.h" +#include "../../world/entity/Entity.h" +#include "../../world/level/Level.h" +#include "../../util/Mth.h" +#include "../../world/entity/item/ItemEntity.h" + +class TakeAnimationParticle: public Particle +{ + typedef Particle super; + +public: + //@todo:itementity + TakeAnimationParticle(Level* level, ItemEntity* item, Entity* target, float yOffs) + : super(level, item->x, item->y, item->z, item->xd, item->yd, item->zd), + e(level, item->x, item->y, item->z, item->item), + //bx(item->x), by(item->y), bz(item->z), + target(target), + yOffs(yOffs), + life(0), + lifeTime(3) + { + e.item.count = 1; + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { + float time = (life + a) / lifeTime; + time = time*time; + + float xo = e.x; + float yo = e.y; + float zo = e.z; + + float xt = target->xOld + (target->x - target->xOld) * a; + float yt = target->yOld + (target->y - target->yOld) * a+yOffs; + float zt = target->zOld + (target->z - target->zOld) * a; + + float xx = xo + (xt - xo) * time; + float yy = yo + (yt - yo) * time; + float zz = zo + (zt - zo) * time; + + int xTile = Mth::floor(xx); + int yTile = Mth::floor(yy + heightOffset * 0.5f); + int zTile = Mth::floor(zz); + + float br = level->getBrightness(xTile, yTile, zTile); + glColor4f2(br, br, br, 1); + + xx -= xOff; + yy -= yOff; + zz -= zOff; + + EntityRenderDispatcher::getInstance()->render(&e, xx, yy, zz, e.yRot, a); + } + + void tick() { + life++; + if (life == lifeTime) remove(); + } + + int getParticleTexture() { + return ParticleEngine::ENTITY_PARTICLE_TEXTURE; + } + +private: + //Entity* item; + ItemEntity e; + Entity* target; + //float bx, by, bz; + int life; + int lifeTime; + float yOffs; +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__TakeAnimationParticle_H__*/ diff --git a/src/client/particle/TerrainParticle.h b/src/client/particle/TerrainParticle.h index 663d87b..07ceac3 100755 --- a/src/client/particle/TerrainParticle.h +++ b/src/client/particle/TerrainParticle.h @@ -1,63 +1,63 @@ -#ifndef NET_MINECRAFT_CLIENT_PARTICLE__TerrainParticle_H__ -#define NET_MINECRAFT_CLIENT_PARTICLE__TerrainParticle_H__ - -//package net.minecraft.client.particle; - -#include "../renderer/Tesselator.h" -#include "../../world/level/Level.h" -#include "../../world/level/tile/Tile.h" -#include "../../world/level/tile/GrassTile.h" - -class TerrainParticle: public Particle -{ - typedef Particle super; - -public: - TerrainParticle(Level* level, float x, float y, float z, float xa, float ya, float za, Tile* tile, int data) - : super(level, x, y, z, xa, ya, za), - tile(tile) - { - tex = tile->getTexture(2, data); - gravity = tile->gravity; - rCol = gCol = bCol = 0.6f; - size /= 2; - //noPhysics = true; - } - - TerrainParticle* init(int x, int y, int z) { - if (tile == Tile::grass) return this; - int col = tile->getColor(level, x, y, z); - rCol *= ((col >> 16) & 0xff) / 255.0f; - gCol *= ((col >> 8) & 0xff) / 255.0f; - bCol *= ((col) & 0xff) / 255.0f; - return this; - } - - int getParticleTexture() { - return ParticleEngine::TERRAIN_TEXTURE; - } - - void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { - float u0 = ((tex & 15) + uo / 4.0f) / 16.0f; - float u1 = u0 + 0.999f / 16.0f / 4; - float v0 = ((tex >> 4) + vo / 4.0f) / 16.0f; - float v1 = v0 + 0.999f / 16.0f / 4; - float r = 0.1f * size; - - float x = (float) (xo + (this->x - xo) * a - xOff); - float y = (float) (yo + (this->y - yo) * a - yOff); - float z = (float) (zo + (this->z - zo) * a - zOff); - float br = getBrightness(a); - t.color(br * rCol, br * gCol, br * bCol); - - t.vertexUV(x - xa * r - xa2 * r, y - ya * r, z - za * r - za2 * r, u0, v1); - t.vertexUV(x - xa * r + xa2 * r, y + ya * r, z - za * r + za2 * r, u0, v0); - t.vertexUV(x + xa * r + xa2 * r, y + ya * r, z + za * r + za2 * r, u1, v0); - t.vertexUV(x + xa * r - xa2 * r, y - ya * r, z + za * r - za2 * r, u1, v1); - } - -private: - Tile* tile; -}; - -#endif /*NET_MINECRAFT_CLIENT_PARTICLE__TerrainParticle_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PARTICLE__TerrainParticle_H__ +#define NET_MINECRAFT_CLIENT_PARTICLE__TerrainParticle_H__ + +//package net.minecraft.client.particle; + +#include "../renderer/Tesselator.h" +#include "../../world/level/Level.h" +#include "../../world/level/tile/Tile.h" +#include "../../world/level/tile/GrassTile.h" + +class TerrainParticle: public Particle +{ + typedef Particle super; + +public: + TerrainParticle(Level* level, float x, float y, float z, float xa, float ya, float za, Tile* tile, int data) + : super(level, x, y, z, xa, ya, za), + tile(tile) + { + tex = tile->getTexture(2, data); + gravity = tile->gravity; + rCol = gCol = bCol = 0.6f; + size /= 2; + //noPhysics = true; + } + + TerrainParticle* init(int x, int y, int z) { + if (tile == Tile::grass) return this; + int col = tile->getColor(level, x, y, z); + rCol *= ((col >> 16) & 0xff) / 255.0f; + gCol *= ((col >> 8) & 0xff) / 255.0f; + bCol *= ((col) & 0xff) / 255.0f; + return this; + } + + int getParticleTexture() { + return ParticleEngine::TERRAIN_TEXTURE; + } + + void render(Tesselator& t, float a, float xa, float ya, float za, float xa2, float za2) { + float u0 = ((tex & 15) + uo / 4.0f) / 16.0f; + float u1 = u0 + 0.999f / 16.0f / 4; + float v0 = ((tex >> 4) + vo / 4.0f) / 16.0f; + float v1 = v0 + 0.999f / 16.0f / 4; + float r = 0.1f * size; + + float x = (float) (xo + (this->x - xo) * a - xOff); + float y = (float) (yo + (this->y - yo) * a - yOff); + float z = (float) (zo + (this->z - zo) * a - zOff); + float br = getBrightness(a); + t.color(br * rCol, br * gCol, br * bCol); + + t.vertexUV(x - xa * r - xa2 * r, y - ya * r, z - za * r - za2 * r, u0, v1); + t.vertexUV(x - xa * r + xa2 * r, y + ya * r, z - za * r + za2 * r, u0, v0); + t.vertexUV(x + xa * r + xa2 * r, y + ya * r, z + za * r + za2 * r, u1, v0); + t.vertexUV(x + xa * r - xa2 * r, y - ya * r, z + za * r - za2 * r, u1, v1); + } + +private: + Tile* tile; +}; + +#endif /*NET_MINECRAFT_CLIENT_PARTICLE__TerrainParticle_H__*/ diff --git a/src/client/player/LocalPlayer.cpp b/src/client/player/LocalPlayer.cpp index 398edcd..c7aeaca 100755 --- a/src/client/player/LocalPlayer.cpp +++ b/src/client/player/LocalPlayer.cpp @@ -1,856 +1,856 @@ -#include "LocalPlayer.h" -#include "../Minecraft.h" -#include "../../ErrorCodes.h" -#include "../../world/entity/EntityEvent.h" -#include "../../world/entity/player/Player.h" -#include "../../world/inventory/BaseContainerMenu.h" -#include "../../world/item/BowItem.h" -#include "../../world/level/Level.h" -#include "../../world/level/tile/Tile.h" -#include "../../world/level/tile/entity/TileEntity.h" -#include "../../world/level/material/Material.h" -#include "../../network/packet/ContainerClosePacket.h" -#include "../../network/packet/MovePlayerPacket.h" -#include "../../network/packet/PlayerEquipmentPacket.h" -#include "../../network/RakNetInstance.h" -#include "../../network/packet/DropItemPacket.h" -#include "../../network/packet/SetHealthPacket.h" -#include "../../network/packet/SendInventoryPacket.h" -#include "../../network/packet/EntityEventPacket.h" -#include "../../network/packet/PlayerActionPacket.h" -#include -#include -#include "../../platform/log.h" -#include "../../platform/HttpClient.h" -#include "../../platform/CThread.h" -#include "../../util/StringUtils.h" -#include "client/Options.h" - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#endif - -#ifndef STANDALONE_SERVER -#include "../gui/Screen.h" -#include "../gui/screens/FurnaceScreen.h" -#include "../gui/screens/ChestScreen.h" -#include "../gui/screens/crafting/WorkbenchScreen.h" -#include "../gui/screens/crafting/StonecutterScreen.h" -#include "../gui/screens/InBedScreen.h" -#include "../gui/screens/TextEditScreen.h" -#include "../particle/TakeAnimationParticle.h" -#endif -#include "../../network/packet/AnimatePacket.h" -#include "../../world/item/ArmorItem.h" -#include "../../network/packet/PlayerArmorEquipmentPacket.h" - -namespace { -#ifndef STANDALONE_SERVER - -static bool isBase64(unsigned char c) { - return (std::isalnum(c) || (c == '+') || (c == '/')); -} - -static std::string base64Decode(const std::string& encoded) { - static const std::string base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::string out; - int in_len = (int)encoded.size(); - int i = 0; - int in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - - while (in_len-- && (encoded[in_] != '=') && isBase64(encoded[in_])) { - char_array_4[i++] = encoded[in_]; in_++; - if (i == 4) { - for (i = 0; i < 4; i++) - char_array_4[i] = (unsigned char)base64Chars.find(char_array_4[i]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (i = 0; i < 3; i++) - out += char_array_3[i]; - i = 0; - } - } - - if (i) { - for (int j = i; j < 4; j++) - char_array_4[j] = 0; - for (int j = 0; j < 4; j++) - char_array_4[j] = (unsigned char)base64Chars.find(char_array_4[j]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (int j = 0; (j < i - 1); j++) - out += char_array_3[j]; - } - return out; -} - -static std::string extractJsonString(const std::string& json, const std::string& key) { - std::string search = "\"" + key + "\""; - size_t pos = json.find(search); - if (pos == std::string::npos) return ""; - pos = json.find(':', pos + search.size()); - if (pos == std::string::npos) return ""; - pos++; - while (pos < json.size() && std::isspace((unsigned char)json[pos])) pos++; - if (pos >= json.size() || json[pos] != '"') return ""; - pos++; - size_t end = json.find('"', pos); - if (end == std::string::npos) return ""; - return json.substr(pos, end - pos); -} - -static std::string getTextureUrlForUsername(const std::string& username, const std::string& textureKey) { - if (username.empty()) { - LOGI("[%s] username empty\n", textureKey.c_str()); - return ""; - } - - LOGI("[%s] resolving UUID for user '%s'...\n", textureKey.c_str(), username.c_str()); - std::vector body; - std::string apiUrl = "http://api.mojang.com/users/profiles/minecraft/" + username; - if (!HttpClient::download(apiUrl, body)) { - LOGW("[%s] failed to download UUID for %s\n", textureKey.c_str(), username.c_str()); - return ""; - } - - std::string response(body.begin(), body.end()); - std::string uuid = extractJsonString(response, "id"); - if (uuid.empty()) { - LOGW("[%s] no UUID found in Mojang response for %s\n", textureKey.c_str(), username.c_str()); - return ""; - } - - LOGI("[%s] UUID=%s for user %s\n", textureKey.c_str(), uuid.c_str(), username.c_str()); - - std::string profileUrl = "http://sessionserver.mojang.com/session/minecraft/profile/" + uuid; - if (!HttpClient::download(profileUrl, body)) { - LOGW("[%s] failed to download profile for UUID %s\n", textureKey.c_str(), uuid.c_str()); - return ""; - } - - response.assign(body.begin(), body.end()); - std::string encoded = extractJsonString(response, "value"); - if (encoded.empty()) { - LOGW("[%s] no value field in profile response for UUID %s\n", textureKey.c_str(), uuid.c_str()); - return ""; - } - - std::string decoded = base64Decode(encoded); - - std::string searchKey = "\"" + textureKey + "\""; - size_t texturePos = decoded.find(searchKey); - if (texturePos == std::string::npos) { - LOGW("[%s] no %s entry in decoded profile for UUID %s\n", textureKey.c_str(), textureKey.c_str(), uuid.c_str()); - return ""; - } - size_t urlPos = decoded.find("\"url\"", texturePos); - if (urlPos == std::string::npos) { - LOGW("[%s] no url field under %s for UUID %s\n", textureKey.c_str(), textureKey.c_str(), uuid.c_str()); - return ""; - } - - // extract the URL value from the substring starting at urlPos - std::string urlFragment = decoded.substr(urlPos); - std::string textureUrl = extractJsonString(urlFragment, "url"); - if (textureUrl.empty()) { - LOGW("[%s] failed to parse %s URL for UUID %s\n", textureKey.c_str(), textureKey.c_str(), uuid.c_str()); - return ""; - } - - LOGI("[%s] %s URL for %s: %s\n", textureKey.c_str(), textureKey.c_str(), username.c_str(), textureUrl.c_str()); - return textureUrl; -} - -static std::string getSkinUrlForUsername(const std::string& username) { - return getTextureUrlForUsername(username, "SKIN"); -} - -static std::string getCapeUrlForUsername(const std::string& username) { - return getTextureUrlForUsername(username, "CAPE"); -} - -#endif - -static bool ensureDirectoryExists(const std::string& path) { -#if defined(_WIN32) - return _mkdir(path.c_str()) == 0 || errno == EEXIST; -#else - struct stat st; - if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) - return true; - - std::string subPath; - size_t i = 0; - while (i < path.length()) { - i = path.find_first_of("/\\", i); - if (i == std::string::npos) { - subPath = path; - } else { - subPath = path.substr(0, i); - } - - if (!subPath.empty()) { - if (stat(subPath.c_str(), &st) != 0) { - if (mkdir(subPath.c_str(), 0755) != 0 && errno != EEXIST) - return false; - } else if (!S_ISDIR(st.st_mode)) { - return false; - } - } - - if (i == std::string::npos) - break; - i += 1; - } - - if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) - return true; - return mkdir(path.c_str(), 0755) == 0 || errno == EEXIST; -#endif -} - -static bool fileExists(const std::string& path) { - struct stat st; - if (stat(path.c_str(), &st) != 0) - return false; - -#if defined(_WIN32) - return (st.st_mode & _S_IFREG) != 0; -#else - return S_ISREG(st.st_mode); -#endif -} - -#ifndef STANDALONE_SERVER - -static void* fetchSkinForPlayer(void* param) { - LocalPlayer* player = (LocalPlayer*)param; - if (!player) return NULL; - - LOGI("[Skin] starting skin download for %s\n", player->name.c_str()); - - const std::string cacheDir = "data/images/skins"; - if (!ensureDirectoryExists(cacheDir)) { - LOGW("[Skin] failed to create cache directory %s\n", cacheDir.c_str()); - } - - std::string cacheFile = cacheDir + "/" + player->name + ".png"; - if (fileExists(cacheFile)) { - LOGI("[Skin] using cached skin for %s\n", player->name.c_str()); - player->setTextureName("skins/" + player->name + ".png"); - return NULL; - } - - std::string skinUrl = getSkinUrlForUsername(player->name); - if (skinUrl.empty()) { - LOGW("[Skin] skin URL lookup failed for %s\n", player->name.c_str()); - player->setTextureName("mob/char.png"); - return NULL; - } - - LOGI("[Skin] downloading skin from %s\n", skinUrl.c_str()); - std::vector skinData; - if (!HttpClient::download(skinUrl, skinData) || skinData.empty()) { - LOGW("[Skin] download failed for %s\n", skinUrl.c_str()); - player->setTextureName("mob/char.png"); - return NULL; - } - - // Save to cache - FILE* fp = fopen(cacheFile.c_str(), "wb"); - if (fp) { - fwrite(skinData.data(), 1, skinData.size(), fp); - fclose(fp); - LOGI("[Skin] cached skin to %s\n", cacheFile.c_str()); - - player->setTextureName("skins/" + player->name + ".png"); - } else { - LOGW("[Skin] failed to write skin cache %s\n", cacheFile.c_str()); - player->setTextureName("mob/char.png"); - } - - return NULL; -} - -static void* fetchCapeForPlayer(void* param) { - LocalPlayer* player = (LocalPlayer*)param; - if (!player) return NULL; - - LOGI("[Cape] starting cape download for %s\n", player->name.c_str()); - - const std::string cacheDir = "data/images/capes"; - if (!ensureDirectoryExists(cacheDir)) { - LOGW("[Cape] failed to create cache directory %s\n", cacheDir.c_str()); - } - - std::string cacheFile = cacheDir + "/" + player->name + ".png"; - if (fileExists(cacheFile)) { - LOGI("[Cape] using cached cape for %s\n", player->name.c_str()); - player->setCapeTextureName("capes/" + player->name + ".png"); - return NULL; - } - - std::string capeUrl = getCapeUrlForUsername(player->name); - if (capeUrl.empty()) { - LOGW("[Cape] cape URL lookup failed for %s\n", player->name.c_str()); - return NULL; - } - - LOGI("[Cape] downloading cape from %s\n", capeUrl.c_str()); - std::vector capeData; - if (!HttpClient::download(capeUrl, capeData) || capeData.empty()) { - LOGW("[Cape] download failed for %s\n", capeUrl.c_str()); - return NULL; - } - - // Save to cache - FILE* fp = fopen(cacheFile.c_str(), "wb"); - if (fp) { - fwrite(capeData.data(), 1, capeData.size(), fp); - fclose(fp); - LOGI("[Cape] cached cape to %s\n", cacheFile.c_str()); - - player->setCapeTextureName("capes/" + player->name + ".png"); - } else { - LOGW("[Cape] failed to write cape cache %s\n", cacheFile.c_str()); - } - - return NULL; -} - -#endif - -//@note: doesn't work completely, since it doesn't care about stairs rotation -static bool isJumpable(int tileId) { - return tileId != Tile::fence->id - && tileId != Tile::fenceGate->id - && tileId != Tile::stoneSlabHalf->id - && tileId != Tile::trapdoor->id - && tileId != Tile::sign->id - && tileId != Tile::wallSign->id - && (Tile::tiles[tileId] != NULL && Tile::tiles[tileId]->getRenderShape() != Tile::SHAPE_STAIRS); -} - -} // anonymous namespace - -LocalPlayer::LocalPlayer(Minecraft* minecraft, Level* level, const std::string& username, int dimension, bool isCreative) -: Player(level, isCreative), - minecraft(minecraft), - input(NULL), - sentInventoryItemId(-1), - sentInventoryItemData(-1), - autoJumpEnabled(true), - armorTypeHash(0), - sprinting(false), - sprintDoubleTapTimer(0), - prevForwardHeld(false) -{ - this->dimension = dimension; - _init(); -#ifndef STANDALONE_SERVER - - if (minecraft->options.getStringValue(OPTIONS_USERNAME).size() != 0) { - textureName = "mob/char.png"; - - this->name = minecraft->options.getStringValue(OPTIONS_USERNAME); - printf("test \n"); - // Fetch user skin and cape from Mojang servers in the background (avoids blocking the main thread) - // TODO: Fix this memory leak - new CThread(fetchSkinForPlayer, this); - new CThread(fetchCapeForPlayer, this); - } -#endif -} - -LocalPlayer::~LocalPlayer() { - //delete input; - //input = NULL; -} - -/*private*/ -void LocalPlayer::calculateFlight(float xa, float ya, float za) { - float flySpeed = minecraft->options.getProgressValue(OPTIONS_FLY_SPEED); - float sensivity = minecraft->options.getProgressValue(OPTIONS_SENSITIVITY); - - xa = xa * flySpeed; - ya = 0; - za = za * flySpeed; - - if (sprinting) { - float sprintBoost = getWalkingSpeedModifier(); // 1.3x - xa *= sprintBoost; - za *= sprintBoost; - } - -#ifdef ANDROID - if (Keyboard::isKeyDown(103)) ya = .2f * flySpeed; - if (Keyboard::isKeyDown(102)) ya = -.2f * flySpeed; -#else - if (Keyboard::isKeyDown(Keyboard::KEY_E)) ya = .2f * flySpeed; - if (Keyboard::isKeyDown(Keyboard::KEY_Q)) ya = -.2f * flySpeed; -#endif - - flyX = 10 * smoothFlyX.getNewDeltaValue(xa, .35f * sensivity); - flyY = 10 * smoothFlyY.getNewDeltaValue(ya, .35f * sensivity); - flyZ = 10 * smoothFlyZ.getNewDeltaValue(za, .35f * sensivity); -} - -bool LocalPlayer::isSolidTile(int x, int y, int z) { - int tileId = level->getTile(x, y, z); - return tileId > 0 && Tile::tiles[tileId]->material->isSolid(); -} - -void LocalPlayer::tick() { - - super::tick(); - if(!useItem.isNull()) { - ItemInstance* item = inventory->getSelected(); - if(item != NULL && *item == useItem) { - if (useItemDuration <= 25 && useItemDuration % 4 == 0) { - spawnEatParticles(item, 5); - } - if(--useItemDuration == 0) { - if(!level->isClientSide) { - completeUsingItem(); - } else { - EntityEventPacket p(entityId, EntityEvent::USE_ITEM_COMPLETE); - level->raknetInstance->send(p); - } - } - } - else { - stopUsingItem(); - } - } - if (minecraft->isOnline()) - { - if (std::abs(x - sentX) > .1f || std::abs(y - sentY) > .01f || std::abs(z - sentZ) > .1f || std::abs(sentRotX - xRot) > 1 || std::abs(sentRotY - yRot) > 1) - { - MovePlayerPacket packet(entityId, x, y - heightOffset, z, xRot, yRot); - minecraft->raknetInstance->send(packet); - sentX = x; - sentY = y; - sentZ = z; - sentRotX = xRot; - sentRotY = yRot; - } - - ItemInstance* item = inventory->getSelected(); - int newItemId = (item && item->count > 0) ? item->id : 0; - int newItemData = (item && item->count > 0) ? item->getAuxValue() : 0; - - if (sentInventoryItemId != newItemId - || sentInventoryItemData != newItemData) - { - sentInventoryItemId = newItemId; - sentInventoryItemData = newItemData; - PlayerEquipmentPacket packet(entityId, newItemId, newItemData); - minecraft->raknetInstance->send(packet); - } - } -/* - for (int i = 0; i < 4; ++i) { - ItemInstance* a = getArmor(i); - if (!a) continue; - - ArmorItem* item = (ArmorItem*) a->getItem(); - - printf("armor %d: %d\n", i, a->getAuxValue()); - } -*/ - - updateArmorTypeHash(); -#ifndef STANDALONE_SERVER - if (!minecraft->screen && containerMenu) { - static bool hasPostedError = false; - if (!hasPostedError) { - minecraft->gui.postError( ErrorCodes::ContainerRefStillExistsAfterDestruction ); - hasPostedError = true; - } - } -#endif - //LOGI("biome: %s\n", level->getBiomeSource()->getBiome((int)x >> 4, (int)z >> 4)->name.c_str()); -} - -/*public*/ -void LocalPlayer::aiStep() { - jumpTriggerTime--; - ascendTriggerTime--; - descendTriggerTime--; - - bool wasJumping = input->jumping; -#ifndef STANDALONE_SERVER - bool screenCovering = minecraft->screen && !minecraft->screen->passEvents; - if (!screenCovering) - input->tick(this); - - // Sprint: detect W double-tap - { - bool forwardHeld = (input->ya > 0); - if (forwardHeld && !prevForwardHeld && minecraft->options.getBooleanValue(OPTIONS_ALLOW_SPRINT)) { - // leading edge of W press - if (sprintDoubleTapTimer > 0) - sprinting = true; - else - sprintDoubleTapTimer = 7; - } - if (!forwardHeld) { - sprinting = false; - } - if (sprintDoubleTapTimer > 0) sprintDoubleTapTimer--; - prevForwardHeld = forwardHeld; - } - if (input->sneaking) - sprinting = false; - - if (input->sneaking) { - if (ySlideOffset < 0.2f) ySlideOffset = 0.2f; - } -#endif - if (abilities.mayfly) { - // Check for flight toggle - if (!wasJumping && input->jumping) { - if (jumpTriggerTime <= 0) jumpTriggerTime = 7; - else { - abilities.flying = !abilities.flying; - jumpTriggerTime = 0; - } - } - if (abilities.flying) { - if (input->wantUp) { - yd += 0.15f; - //xd = zd = 0; - } - if (input->wantDown) { - yd -= 0.15f; - } - } - } - - if(isUsingItem()) { - const float k = 0.35f; - input->xa *= k; - input->ya *= k; - } - - Mob::aiStep(); - super::aiStep(); - - //if (onGround && abilities.flying) - // abilities.flying = false; - - if (interpolateOnly()) - updateAi(); -} - -/*public*/ -void LocalPlayer::closeContainer() { - if (level->isClientSide) { - ContainerClosePacket packet(containerMenu->containerId); - minecraft->raknetInstance->send(packet); - } - super::closeContainer(); - minecraft->setScreen(NULL); -} - -//@Override -void LocalPlayer::move(float xa, float ya, float za) { - //@note: why is this == minecraft->player needed? - if (this == minecraft->player && minecraft->options.getBooleanValue(OPTIONS_IS_FLYING)) { - noPhysics = true; - float tmp = walkDist; // update - calculateFlight((float) xa, (float) ya, (float) za); - fallDistance = 0; - yd = 0; - super::move(flyX, flyY, flyZ); - onGround = true; - walkDist = tmp; - } else { - if (autoJumpTime > 0) { - autoJumpTime--; - input->jumping = true; - } - float prevX = x, prevZ = z; - - super::move(xa, ya, za); - - float newX = x, newZ = z; - - if (autoJumpTime <= 0 && minecraft->options.getBooleanValue(OPTIONS_AUTOJUMP)) - { - // auto-jump when crossing the middle of a tile, and the tile in the front is blocked - bool jump = false; - if (Mth::floor(prevX * 2.0f) != Mth::floor(newX * 2.0f) || Mth::floor(prevZ * 2.0f) != Mth::floor(newZ * 2.0f)) - { - float dist = Mth::sqrt(xa * xa + za * za); - const int xx = Mth::floor(x + xa / dist); - const int zz = Mth::floor(z + za / dist); - const int tileId = level->getTile(xx, (int)(y-1), zz); - jump = (isSolidTile(xx, (int)(y-1), zz) // Solid block to jump up on - && !isSolidTile(xx, (int)y, zz) && !isSolidTile(xx, (int)(y+1), zz)) // Enough space - && isJumpable(tileId); - } - if (jump) - { - autoJumpTime = 1; - } - } - } -} - -void LocalPlayer::updateAi() { - super::updateAi(); - this->xxa = input->xa; - this->yya = input->ya; - this->jumping = input->jumping || autoJumpTime > 0; -} - -void LocalPlayer::take( Entity* e, int orgCount ) -{ -#ifndef STANDALONE_SERVER - if (e->isItemEntity()) - minecraft->particleEngine->add(new TakeAnimationParticle(minecraft->level, (ItemEntity*)e, this, -0.5f)); -#endif -} - -void LocalPlayer::setKey( int eventKey, bool eventKeyState ) -{ - input->setKey(eventKey, eventKeyState); -} - -void LocalPlayer::releaseAllKeys() -{ - if (input) input->releaseAllKeys(); -} - -float LocalPlayer::getWalkingSpeedModifier() { - return sprinting ? 1.3f : 1.0f; -} - -float LocalPlayer::getFieldOfViewModifier() { - float targetFov = 1.0f; - if(abilities.flying) targetFov *= 1.1f; - targetFov *= ((walkingSpeed * getWalkingSpeedModifier()) / DEFAULT_WALK_SPEED +1) / 2; - - if(isUsingItem() && getUseItem()->id == Item::bow->id) { - float ticksHeld = (float)getTicksUsingItem(); - float scale = ticksHeld / BowItem::MAX_DRAW_DURATION; - if(scale > 1) { - scale = 1; - } - else { - scale *= scale; - } - targetFov *= 1.0f - scale * 0.15f; - } - return targetFov; -} -void LocalPlayer::addAdditonalSaveData( CompoundTag* entityTag ) -{ - super::addAdditonalSaveData(entityTag); - entityTag->putInt("Score", score); -} - -void LocalPlayer::readAdditionalSaveData( CompoundTag* entityTag ) -{ - super::readAdditionalSaveData(entityTag); - score = entityTag->getInt("Score"); -} - -bool LocalPlayer::isSneaking() -{ - return input->sneaking; -} - -void LocalPlayer::hurtTo( int newHealth ) -{ - int dmg = health - newHealth; - if (dmg <= 0) { - this->health = newHealth; - } else { - lastHurt = dmg; - lastHealth = health; - invulnerableTime = invulnerableDuration; - - minecraft->player->bypassArmor = true; - actuallyHurt(dmg); - minecraft->player->bypassArmor = false; - - hurtTime = hurtDuration = 10; - } -} - -void LocalPlayer::actuallyHurt( int dmg ) -{ -#ifndef STANDALONE_SERVER - if (minecraft->screen && minecraft->screen->closeOnPlayerHurt()) { - if (containerMenu) closeContainer(); - else minecraft->setScreen(NULL); - } -#endif - super::actuallyHurt(dmg); -} - -void LocalPlayer::respawn() -{ - minecraft->respawnPlayer(); -} - -void LocalPlayer::die(Entity* source) -{ - // If we're an online client, send the inventory to be dropped - // If we're the server, drop the inventory immediately - if (level->isClientSide) { - SendInventoryPacket packet(this, true); - minecraft->raknetInstance->send(packet); - } - inventory->dropAll(level->isClientSide); - for (int i = 0; i < NUM_ARMOR; ++i) { - ItemInstance* item = getArmor(i); - if (!ItemInstance::isArmorItem(item)) return; - - drop(new ItemInstance(*item), true); - setArmor(i, NULL); - } - - super::die(source); -} - -void LocalPlayer::swing() { - super::swing(); - - if (swingTime == -1) { - AnimatePacket packet(AnimatePacket::Swing, this); - packet.reliability = UNRELIABLE; - packet.priority = MEDIUM_PRIORITY; - minecraft->raknetInstance->send(packet); - } -} - -void LocalPlayer::reset() { - super::reset(); - this->_init(); -} - -void LocalPlayer::_init() { - autoJumpTime = 0; - jumpTriggerTime = 0; - ascendTriggerTime = 0; - descendTriggerTime = 0; - ascending = false; - descending = false; - - ItemInstance* item = inventory->getSelected(); - sentInventoryItemId = item? item->id : 0; - sentInventoryItemData = item? item->getAuxValue() : 0; -} - -void LocalPlayer::startCrafting(int x, int y, int z, int tableSize) { -#ifndef STANDALONE_SERVER - if (!minecraft->isCreativeMode()) - minecraft->setScreen( new WorkbenchScreen(tableSize) ); -#endif -} - -void LocalPlayer::startStonecutting(int x, int y, int z) { -#ifndef STANDALONE_SERVER - if (!minecraft->isCreativeMode()) - minecraft->setScreen( new StonecutterScreen() ); -#endif -} - -void LocalPlayer::openFurnace( FurnaceTileEntity* e ) { -#ifndef STANDALONE_SERVER - if (!minecraft->isCreativeMode()) - minecraft->setScreen( new FurnaceScreen(this, e) ); -#endif -} - -void LocalPlayer::openContainer( ChestTileEntity* container ) { -#ifndef STANDALONE_SERVER - if (!minecraft->isCreativeMode()) - minecraft->setScreen( new ChestScreen(this, container) ); -#endif -} - -void LocalPlayer::drop( ItemInstance* item, bool randomly ) -{ - if (!item) - return; - - if (level->isClientSide) { - DropItemPacket packet(entityId, *item); - minecraft->raknetInstance->send(packet); - // delete the ItemEntity here, since we don't add it to level - delete item; - } else { - super::drop(item, randomly); - } -} - -void LocalPlayer::causeFallDamage( float distance ) -{ - int dmg = (int) ceil((distance - 3)); - if (dmg > 0) { - if (level->isClientSide) { - SetHealthPacket packet(SetHealthPacket::HEALTH_MODIFY_OFFSET + dmg); - minecraft->raknetInstance->send(packet); - } - } - super::causeFallDamage(distance); - -} - -void LocalPlayer::displayClientMessage( const std::string& messageId ) { -#ifndef STANDALONE_SERVER - minecraft->gui.displayClientMessage(messageId); -#endif -} - -int LocalPlayer::startSleepInBed( int x, int y, int z ) { - int startSleepInBedReturnValue = super::startSleepInBed(x, y, z); -#ifndef STANDALONE_SERVER - if(startSleepInBedReturnValue == BedSleepingResult::OK) - minecraft->setScreen(new InBedScreen()); -#endif - return startSleepInBedReturnValue; -} - -void LocalPlayer::stopSleepInBed( bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint ) { - if(level->isClientSide) { - PlayerActionPacket packet(PlayerActionPacket::STOP_SLEEPING, 0, 0, 0, 0, entityId); - minecraft->raknetInstance->send(packet); - } -#ifndef STANDALONE_SERVER - minecraft->setScreen(NULL); -#endif - super::stopSleepInBed(forcefulWakeUp, updateLevelList, saveRespawnPoint); -} - -void LocalPlayer::openTextEdit( TileEntity* tileEntity ) { -#if !defined(STANDALONE_SERVER) && !defined(RPI) - if(tileEntity->type == TileEntityType::Sign) - minecraft->setScreen(new TextEditScreen((SignTileEntity*) tileEntity)); -#endif -} - -void LocalPlayer::updateArmorTypeHash() { - int hash = getArmorTypeHash(); - if (hash != armorTypeHash) { - PlayerArmorEquipmentPacket p(this); - minecraft->raknetInstance->send(p); - armorTypeHash = hash; - } -} +#include "LocalPlayer.h" +#include "../Minecraft.h" +#include "../../ErrorCodes.h" +#include "../../world/entity/EntityEvent.h" +#include "../../world/entity/player/Player.h" +#include "../../world/inventory/BaseContainerMenu.h" +#include "../../world/item/BowItem.h" +#include "../../world/level/Level.h" +#include "../../world/level/tile/Tile.h" +#include "../../world/level/tile/entity/TileEntity.h" +#include "../../world/level/material/Material.h" +#include "../../network/packet/ContainerClosePacket.h" +#include "../../network/packet/MovePlayerPacket.h" +#include "../../network/packet/PlayerEquipmentPacket.h" +#include "../../network/RakNetInstance.h" +#include "../../network/packet/DropItemPacket.h" +#include "../../network/packet/SetHealthPacket.h" +#include "../../network/packet/SendInventoryPacket.h" +#include "../../network/packet/EntityEventPacket.h" +#include "../../network/packet/PlayerActionPacket.h" +#include +#include +#include "../../platform/log.h" +#include "../../platform/HttpClient.h" +#include "../../platform/CThread.h" +#include "../../util/StringUtils.h" +#include "client/Options.h" + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#include +#endif + +#ifndef STANDALONE_SERVER +#include "../gui/Screen.h" +#include "../gui/screens/FurnaceScreen.h" +#include "../gui/screens/ChestScreen.h" +#include "../gui/screens/crafting/WorkbenchScreen.h" +#include "../gui/screens/crafting/StonecutterScreen.h" +#include "../gui/screens/InBedScreen.h" +#include "../gui/screens/TextEditScreen.h" +#include "../particle/TakeAnimationParticle.h" +#endif +#include "../../network/packet/AnimatePacket.h" +#include "../../world/item/ArmorItem.h" +#include "../../network/packet/PlayerArmorEquipmentPacket.h" + +namespace { +#ifndef STANDALONE_SERVER + +static bool isBase64(unsigned char c) { + return (std::isalnum(c) || (c == '+') || (c == '/')); +} + +static std::string base64Decode(const std::string& encoded) { + static const std::string base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string out; + int in_len = (int)encoded.size(); + int i = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + + while (in_len-- && (encoded[in_] != '=') && isBase64(encoded[in_])) { + char_array_4[i++] = encoded[in_]; in_++; + if (i == 4) { + for (i = 0; i < 4; i++) + char_array_4[i] = (unsigned char)base64Chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; i < 3; i++) + out += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (int j = i; j < 4; j++) + char_array_4[j] = 0; + for (int j = 0; j < 4; j++) + char_array_4[j] = (unsigned char)base64Chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (int j = 0; (j < i - 1); j++) + out += char_array_3[j]; + } + return out; +} + +static std::string extractJsonString(const std::string& json, const std::string& key) { + std::string search = "\"" + key + "\""; + size_t pos = json.find(search); + if (pos == std::string::npos) return ""; + pos = json.find(':', pos + search.size()); + if (pos == std::string::npos) return ""; + pos++; + while (pos < json.size() && std::isspace((unsigned char)json[pos])) pos++; + if (pos >= json.size() || json[pos] != '"') return ""; + pos++; + size_t end = json.find('"', pos); + if (end == std::string::npos) return ""; + return json.substr(pos, end - pos); +} + +static std::string getTextureUrlForUsername(const std::string& username, const std::string& textureKey) { + if (username.empty()) { + LOGI("[%s] username empty\n", textureKey.c_str()); + return ""; + } + + LOGI("[%s] resolving UUID for user '%s'...\n", textureKey.c_str(), username.c_str()); + std::vector body; + std::string apiUrl = "http://api.mojang.com/users/profiles/minecraft/" + username; + if (!HttpClient::download(apiUrl, body)) { + LOGW("[%s] failed to download UUID for %s\n", textureKey.c_str(), username.c_str()); + return ""; + } + + std::string response(body.begin(), body.end()); + std::string uuid = extractJsonString(response, "id"); + if (uuid.empty()) { + LOGW("[%s] no UUID found in Mojang response for %s\n", textureKey.c_str(), username.c_str()); + return ""; + } + + LOGI("[%s] UUID=%s for user %s\n", textureKey.c_str(), uuid.c_str(), username.c_str()); + + std::string profileUrl = "http://sessionserver.mojang.com/session/minecraft/profile/" + uuid; + if (!HttpClient::download(profileUrl, body)) { + LOGW("[%s] failed to download profile for UUID %s\n", textureKey.c_str(), uuid.c_str()); + return ""; + } + + response.assign(body.begin(), body.end()); + std::string encoded = extractJsonString(response, "value"); + if (encoded.empty()) { + LOGW("[%s] no value field in profile response for UUID %s\n", textureKey.c_str(), uuid.c_str()); + return ""; + } + + std::string decoded = base64Decode(encoded); + + std::string searchKey = "\"" + textureKey + "\""; + size_t texturePos = decoded.find(searchKey); + if (texturePos == std::string::npos) { + LOGW("[%s] no %s entry in decoded profile for UUID %s\n", textureKey.c_str(), textureKey.c_str(), uuid.c_str()); + return ""; + } + size_t urlPos = decoded.find("\"url\"", texturePos); + if (urlPos == std::string::npos) { + LOGW("[%s] no url field under %s for UUID %s\n", textureKey.c_str(), textureKey.c_str(), uuid.c_str()); + return ""; + } + + // extract the URL value from the substring starting at urlPos + std::string urlFragment = decoded.substr(urlPos); + std::string textureUrl = extractJsonString(urlFragment, "url"); + if (textureUrl.empty()) { + LOGW("[%s] failed to parse %s URL for UUID %s\n", textureKey.c_str(), textureKey.c_str(), uuid.c_str()); + return ""; + } + + LOGI("[%s] %s URL for %s: %s\n", textureKey.c_str(), textureKey.c_str(), username.c_str(), textureUrl.c_str()); + return textureUrl; +} + +static std::string getSkinUrlForUsername(const std::string& username) { + return getTextureUrlForUsername(username, "SKIN"); +} + +static std::string getCapeUrlForUsername(const std::string& username) { + return getTextureUrlForUsername(username, "CAPE"); +} + +#endif + +static bool ensureDirectoryExists(const std::string& path) { +#if defined(_WIN32) + return _mkdir(path.c_str()) == 0 || errno == EEXIST; +#else + struct stat st; + if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) + return true; + + std::string subPath; + size_t i = 0; + while (i < path.length()) { + i = path.find_first_of("/\\", i); + if (i == std::string::npos) { + subPath = path; + } else { + subPath = path.substr(0, i); + } + + if (!subPath.empty()) { + if (stat(subPath.c_str(), &st) != 0) { + if (mkdir(subPath.c_str(), 0755) != 0 && errno != EEXIST) + return false; + } else if (!S_ISDIR(st.st_mode)) { + return false; + } + } + + if (i == std::string::npos) + break; + i += 1; + } + + if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) + return true; + return mkdir(path.c_str(), 0755) == 0 || errno == EEXIST; +#endif +} + +static bool fileExists(const std::string& path) { + struct stat st; + if (stat(path.c_str(), &st) != 0) + return false; + +#if defined(_WIN32) + return (st.st_mode & _S_IFREG) != 0; +#else + return S_ISREG(st.st_mode); +#endif +} + +#ifndef STANDALONE_SERVER + +static void* fetchSkinForPlayer(void* param) { + LocalPlayer* player = (LocalPlayer*)param; + if (!player) return NULL; + + LOGI("[Skin] starting skin download for %s\n", player->name.c_str()); + + const std::string cacheDir = "data/images/skins"; + if (!ensureDirectoryExists(cacheDir)) { + LOGW("[Skin] failed to create cache directory %s\n", cacheDir.c_str()); + } + + std::string cacheFile = cacheDir + "/" + player->name + ".png"; + if (fileExists(cacheFile)) { + LOGI("[Skin] using cached skin for %s\n", player->name.c_str()); + player->setTextureName("skins/" + player->name + ".png"); + return NULL; + } + + std::string skinUrl = getSkinUrlForUsername(player->name); + if (skinUrl.empty()) { + LOGW("[Skin] skin URL lookup failed for %s\n", player->name.c_str()); + player->setTextureName("mob/char.png"); + return NULL; + } + + LOGI("[Skin] downloading skin from %s\n", skinUrl.c_str()); + std::vector skinData; + if (!HttpClient::download(skinUrl, skinData) || skinData.empty()) { + LOGW("[Skin] download failed for %s\n", skinUrl.c_str()); + player->setTextureName("mob/char.png"); + return NULL; + } + + // Save to cache + FILE* fp = fopen(cacheFile.c_str(), "wb"); + if (fp) { + fwrite(skinData.data(), 1, skinData.size(), fp); + fclose(fp); + LOGI("[Skin] cached skin to %s\n", cacheFile.c_str()); + + player->setTextureName("skins/" + player->name + ".png"); + } else { + LOGW("[Skin] failed to write skin cache %s\n", cacheFile.c_str()); + player->setTextureName("mob/char.png"); + } + + return NULL; +} + +static void* fetchCapeForPlayer(void* param) { + LocalPlayer* player = (LocalPlayer*)param; + if (!player) return NULL; + + LOGI("[Cape] starting cape download for %s\n", player->name.c_str()); + + const std::string cacheDir = "data/images/capes"; + if (!ensureDirectoryExists(cacheDir)) { + LOGW("[Cape] failed to create cache directory %s\n", cacheDir.c_str()); + } + + std::string cacheFile = cacheDir + "/" + player->name + ".png"; + if (fileExists(cacheFile)) { + LOGI("[Cape] using cached cape for %s\n", player->name.c_str()); + player->setCapeTextureName("capes/" + player->name + ".png"); + return NULL; + } + + std::string capeUrl = getCapeUrlForUsername(player->name); + if (capeUrl.empty()) { + LOGW("[Cape] cape URL lookup failed for %s\n", player->name.c_str()); + return NULL; + } + + LOGI("[Cape] downloading cape from %s\n", capeUrl.c_str()); + std::vector capeData; + if (!HttpClient::download(capeUrl, capeData) || capeData.empty()) { + LOGW("[Cape] download failed for %s\n", capeUrl.c_str()); + return NULL; + } + + // Save to cache + FILE* fp = fopen(cacheFile.c_str(), "wb"); + if (fp) { + fwrite(capeData.data(), 1, capeData.size(), fp); + fclose(fp); + LOGI("[Cape] cached cape to %s\n", cacheFile.c_str()); + + player->setCapeTextureName("capes/" + player->name + ".png"); + } else { + LOGW("[Cape] failed to write cape cache %s\n", cacheFile.c_str()); + } + + return NULL; +} + +#endif + +//@note: doesn't work completely, since it doesn't care about stairs rotation +static bool isJumpable(int tileId) { + return tileId != Tile::fence->id + && tileId != Tile::fenceGate->id + && tileId != Tile::stoneSlabHalf->id + && tileId != Tile::trapdoor->id + && tileId != Tile::sign->id + && tileId != Tile::wallSign->id + && (Tile::tiles[tileId] != NULL && Tile::tiles[tileId]->getRenderShape() != Tile::SHAPE_STAIRS); +} + +} // anonymous namespace + +LocalPlayer::LocalPlayer(Minecraft* minecraft, Level* level, const std::string& username, int dimension, bool isCreative) +: Player(level, isCreative), + minecraft(minecraft), + input(NULL), + sentInventoryItemId(-1), + sentInventoryItemData(-1), + autoJumpEnabled(true), + armorTypeHash(0), + sprinting(false), + sprintDoubleTapTimer(0), + prevForwardHeld(false) +{ + this->dimension = dimension; + _init(); +#ifndef STANDALONE_SERVER + + if (minecraft->options.getStringValue(OPTIONS_USERNAME).size() != 0) { + textureName = "mob/char.png"; + + this->name = minecraft->options.getStringValue(OPTIONS_USERNAME); + printf("test \n"); + // Fetch user skin and cape from Mojang servers in the background (avoids blocking the main thread) + // TODO: Fix this memory leak + new CThread(fetchSkinForPlayer, this); + new CThread(fetchCapeForPlayer, this); + } +#endif +} + +LocalPlayer::~LocalPlayer() { + //delete input; + //input = NULL; +} + +/*private*/ +void LocalPlayer::calculateFlight(float xa, float ya, float za) { + float flySpeed = minecraft->options.getProgressValue(OPTIONS_FLY_SPEED); + float sensivity = minecraft->options.getProgressValue(OPTIONS_SENSITIVITY); + + xa = xa * flySpeed; + ya = 0; + za = za * flySpeed; + + if (sprinting) { + float sprintBoost = getWalkingSpeedModifier(); // 1.3x + xa *= sprintBoost; + za *= sprintBoost; + } + +#ifdef ANDROID + if (Keyboard::isKeyDown(103)) ya = .2f * flySpeed; + if (Keyboard::isKeyDown(102)) ya = -.2f * flySpeed; +#else + if (Keyboard::isKeyDown(Keyboard::KEY_E)) ya = .2f * flySpeed; + if (Keyboard::isKeyDown(Keyboard::KEY_Q)) ya = -.2f * flySpeed; +#endif + + flyX = 10 * smoothFlyX.getNewDeltaValue(xa, .35f * sensivity); + flyY = 10 * smoothFlyY.getNewDeltaValue(ya, .35f * sensivity); + flyZ = 10 * smoothFlyZ.getNewDeltaValue(za, .35f * sensivity); +} + +bool LocalPlayer::isSolidTile(int x, int y, int z) { + int tileId = level->getTile(x, y, z); + return tileId > 0 && Tile::tiles[tileId]->material->isSolid(); +} + +void LocalPlayer::tick() { + + super::tick(); + if(!useItem.isNull()) { + ItemInstance* item = inventory->getSelected(); + if(item != NULL && *item == useItem) { + if (useItemDuration <= 25 && useItemDuration % 4 == 0) { + spawnEatParticles(item, 5); + } + if(--useItemDuration == 0) { + if(!level->isClientSide) { + completeUsingItem(); + } else { + EntityEventPacket p(entityId, EntityEvent::USE_ITEM_COMPLETE); + level->raknetInstance->send(p); + } + } + } + else { + stopUsingItem(); + } + } + if (minecraft->isOnline()) + { + if (std::abs(x - sentX) > .1f || std::abs(y - sentY) > .01f || std::abs(z - sentZ) > .1f || std::abs(sentRotX - xRot) > 1 || std::abs(sentRotY - yRot) > 1) + { + MovePlayerPacket packet(entityId, x, y - heightOffset, z, xRot, yRot); + minecraft->raknetInstance->send(packet); + sentX = x; + sentY = y; + sentZ = z; + sentRotX = xRot; + sentRotY = yRot; + } + + ItemInstance* item = inventory->getSelected(); + int newItemId = (item && item->count > 0) ? item->id : 0; + int newItemData = (item && item->count > 0) ? item->getAuxValue() : 0; + + if (sentInventoryItemId != newItemId + || sentInventoryItemData != newItemData) + { + sentInventoryItemId = newItemId; + sentInventoryItemData = newItemData; + PlayerEquipmentPacket packet(entityId, newItemId, newItemData); + minecraft->raknetInstance->send(packet); + } + } +/* + for (int i = 0; i < 4; ++i) { + ItemInstance* a = getArmor(i); + if (!a) continue; + + ArmorItem* item = (ArmorItem*) a->getItem(); + + printf("armor %d: %d\n", i, a->getAuxValue()); + } +*/ + + updateArmorTypeHash(); +#ifndef STANDALONE_SERVER + if (!minecraft->screen && containerMenu) { + static bool hasPostedError = false; + if (!hasPostedError) { + minecraft->gui.postError( ErrorCodes::ContainerRefStillExistsAfterDestruction ); + hasPostedError = true; + } + } +#endif + //LOGI("biome: %s\n", level->getBiomeSource()->getBiome((int)x >> 4, (int)z >> 4)->name.c_str()); +} + +/*public*/ +void LocalPlayer::aiStep() { + jumpTriggerTime--; + ascendTriggerTime--; + descendTriggerTime--; + + bool wasJumping = input->jumping; +#ifndef STANDALONE_SERVER + bool screenCovering = minecraft->screen && !minecraft->screen->passEvents; + if (!screenCovering) + input->tick(this); + + // Sprint: detect W double-tap + { + bool forwardHeld = (input->ya > 0); + if (forwardHeld && !prevForwardHeld && minecraft->options.getBooleanValue(OPTIONS_ALLOW_SPRINT)) { + // leading edge of W press + if (sprintDoubleTapTimer > 0) + sprinting = true; + else + sprintDoubleTapTimer = 7; + } + if (!forwardHeld) { + sprinting = false; + } + if (sprintDoubleTapTimer > 0) sprintDoubleTapTimer--; + prevForwardHeld = forwardHeld; + } + if (input->sneaking) + sprinting = false; + + if (input->sneaking) { + if (ySlideOffset < 0.2f) ySlideOffset = 0.2f; + } +#endif + if (abilities.mayfly) { + // Check for flight toggle + if (!wasJumping && input->jumping) { + if (jumpTriggerTime <= 0) jumpTriggerTime = 7; + else { + abilities.flying = !abilities.flying; + jumpTriggerTime = 0; + } + } + if (abilities.flying) { + if (input->wantUp) { + yd += 0.15f; + //xd = zd = 0; + } + if (input->wantDown) { + yd -= 0.15f; + } + } + } + + if(isUsingItem()) { + const float k = 0.35f; + input->xa *= k; + input->ya *= k; + } + + Mob::aiStep(); + super::aiStep(); + + //if (onGround && abilities.flying) + // abilities.flying = false; + + if (interpolateOnly()) + updateAi(); +} + +/*public*/ +void LocalPlayer::closeContainer() { + if (level->isClientSide) { + ContainerClosePacket packet(containerMenu->containerId); + minecraft->raknetInstance->send(packet); + } + super::closeContainer(); + minecraft->setScreen(NULL); +} + +//@Override +void LocalPlayer::move(float xa, float ya, float za) { + //@note: why is this == minecraft->player needed? + if (this == minecraft->player && minecraft->options.getBooleanValue(OPTIONS_IS_FLYING)) { + noPhysics = true; + float tmp = walkDist; // update + calculateFlight((float) xa, (float) ya, (float) za); + fallDistance = 0; + yd = 0; + super::move(flyX, flyY, flyZ); + onGround = true; + walkDist = tmp; + } else { + if (autoJumpTime > 0) { + autoJumpTime--; + input->jumping = true; + } + float prevX = x, prevZ = z; + + super::move(xa, ya, za); + + float newX = x, newZ = z; + + if (autoJumpTime <= 0 && minecraft->options.getBooleanValue(OPTIONS_AUTOJUMP)) + { + // auto-jump when crossing the middle of a tile, and the tile in the front is blocked + bool jump = false; + if (Mth::floor(prevX * 2.0f) != Mth::floor(newX * 2.0f) || Mth::floor(prevZ * 2.0f) != Mth::floor(newZ * 2.0f)) + { + float dist = Mth::sqrt(xa * xa + za * za); + const int xx = Mth::floor(x + xa / dist); + const int zz = Mth::floor(z + za / dist); + const int tileId = level->getTile(xx, (int)(y-1), zz); + jump = (isSolidTile(xx, (int)(y-1), zz) // Solid block to jump up on + && !isSolidTile(xx, (int)y, zz) && !isSolidTile(xx, (int)(y+1), zz)) // Enough space + && isJumpable(tileId); + } + if (jump) + { + autoJumpTime = 1; + } + } + } +} + +void LocalPlayer::updateAi() { + super::updateAi(); + this->xxa = input->xa; + this->yya = input->ya; + this->jumping = input->jumping || autoJumpTime > 0; +} + +void LocalPlayer::take( Entity* e, int orgCount ) +{ +#ifndef STANDALONE_SERVER + if (e->isItemEntity()) + minecraft->particleEngine->add(new TakeAnimationParticle(minecraft->level, (ItemEntity*)e, this, -0.5f)); +#endif +} + +void LocalPlayer::setKey( int eventKey, bool eventKeyState ) +{ + input->setKey(eventKey, eventKeyState); +} + +void LocalPlayer::releaseAllKeys() +{ + if (input) input->releaseAllKeys(); +} + +float LocalPlayer::getWalkingSpeedModifier() { + return sprinting ? 1.3f : 1.0f; +} + +float LocalPlayer::getFieldOfViewModifier() { + float targetFov = 1.0f; + if(abilities.flying) targetFov *= 1.1f; + targetFov *= ((walkingSpeed * getWalkingSpeedModifier()) / DEFAULT_WALK_SPEED +1) / 2; + + if(isUsingItem() && getUseItem()->id == Item::bow->id) { + float ticksHeld = (float)getTicksUsingItem(); + float scale = ticksHeld / BowItem::MAX_DRAW_DURATION; + if(scale > 1) { + scale = 1; + } + else { + scale *= scale; + } + targetFov *= 1.0f - scale * 0.15f; + } + return targetFov; +} +void LocalPlayer::addAdditonalSaveData( CompoundTag* entityTag ) +{ + super::addAdditonalSaveData(entityTag); + entityTag->putInt("Score", score); +} + +void LocalPlayer::readAdditionalSaveData( CompoundTag* entityTag ) +{ + super::readAdditionalSaveData(entityTag); + score = entityTag->getInt("Score"); +} + +bool LocalPlayer::isSneaking() +{ + return input->sneaking; +} + +void LocalPlayer::hurtTo( int newHealth ) +{ + int dmg = health - newHealth; + if (dmg <= 0) { + this->health = newHealth; + } else { + lastHurt = dmg; + lastHealth = health; + invulnerableTime = invulnerableDuration; + + minecraft->player->bypassArmor = true; + actuallyHurt(dmg); + minecraft->player->bypassArmor = false; + + hurtTime = hurtDuration = 10; + } +} + +void LocalPlayer::actuallyHurt( int dmg ) +{ +#ifndef STANDALONE_SERVER + if (minecraft->screen && minecraft->screen->closeOnPlayerHurt()) { + if (containerMenu) closeContainer(); + else minecraft->setScreen(NULL); + } +#endif + super::actuallyHurt(dmg); +} + +void LocalPlayer::respawn() +{ + minecraft->respawnPlayer(); +} + +void LocalPlayer::die(Entity* source) +{ + // If we're an online client, send the inventory to be dropped + // If we're the server, drop the inventory immediately + if (level->isClientSide) { + SendInventoryPacket packet(this, true); + minecraft->raknetInstance->send(packet); + } + inventory->dropAll(level->isClientSide); + for (int i = 0; i < NUM_ARMOR; ++i) { + ItemInstance* item = getArmor(i); + if (!ItemInstance::isArmorItem(item)) return; + + drop(new ItemInstance(*item), true); + setArmor(i, NULL); + } + + super::die(source); +} + +void LocalPlayer::swing() { + super::swing(); + + if (swingTime == -1) { + AnimatePacket packet(AnimatePacket::Swing, this); + packet.reliability = UNRELIABLE; + packet.priority = MEDIUM_PRIORITY; + minecraft->raknetInstance->send(packet); + } +} + +void LocalPlayer::reset() { + super::reset(); + this->_init(); +} + +void LocalPlayer::_init() { + autoJumpTime = 0; + jumpTriggerTime = 0; + ascendTriggerTime = 0; + descendTriggerTime = 0; + ascending = false; + descending = false; + + ItemInstance* item = inventory->getSelected(); + sentInventoryItemId = item? item->id : 0; + sentInventoryItemData = item? item->getAuxValue() : 0; +} + +void LocalPlayer::startCrafting(int x, int y, int z, int tableSize) { +#ifndef STANDALONE_SERVER + if (!minecraft->isCreativeMode()) + minecraft->setScreen( new WorkbenchScreen(tableSize) ); +#endif +} + +void LocalPlayer::startStonecutting(int x, int y, int z) { +#ifndef STANDALONE_SERVER + if (!minecraft->isCreativeMode()) + minecraft->setScreen( new StonecutterScreen() ); +#endif +} + +void LocalPlayer::openFurnace( FurnaceTileEntity* e ) { +#ifndef STANDALONE_SERVER + if (!minecraft->isCreativeMode()) + minecraft->setScreen( new FurnaceScreen(this, e) ); +#endif +} + +void LocalPlayer::openContainer( ChestTileEntity* container ) { +#ifndef STANDALONE_SERVER + if (!minecraft->isCreativeMode()) + minecraft->setScreen( new ChestScreen(this, container) ); +#endif +} + +void LocalPlayer::drop( ItemInstance* item, bool randomly ) +{ + if (!item) + return; + + if (level->isClientSide) { + DropItemPacket packet(entityId, *item); + minecraft->raknetInstance->send(packet); + // delete the ItemEntity here, since we don't add it to level + delete item; + } else { + super::drop(item, randomly); + } +} + +void LocalPlayer::causeFallDamage( float distance ) +{ + int dmg = (int) ceil((distance - 3)); + if (dmg > 0) { + if (level->isClientSide) { + SetHealthPacket packet(SetHealthPacket::HEALTH_MODIFY_OFFSET + dmg); + minecraft->raknetInstance->send(packet); + } + } + super::causeFallDamage(distance); + +} + +void LocalPlayer::displayClientMessage( const std::string& messageId ) { +#ifndef STANDALONE_SERVER + minecraft->gui.displayClientMessage(messageId); +#endif +} + +int LocalPlayer::startSleepInBed( int x, int y, int z ) { + int startSleepInBedReturnValue = super::startSleepInBed(x, y, z); +#ifndef STANDALONE_SERVER + if(startSleepInBedReturnValue == BedSleepingResult::OK) + minecraft->setScreen(new InBedScreen()); +#endif + return startSleepInBedReturnValue; +} + +void LocalPlayer::stopSleepInBed( bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint ) { + if(level->isClientSide) { + PlayerActionPacket packet(PlayerActionPacket::STOP_SLEEPING, 0, 0, 0, 0, entityId); + minecraft->raknetInstance->send(packet); + } +#ifndef STANDALONE_SERVER + minecraft->setScreen(NULL); +#endif + super::stopSleepInBed(forcefulWakeUp, updateLevelList, saveRespawnPoint); +} + +void LocalPlayer::openTextEdit( TileEntity* tileEntity ) { +#if !defined(STANDALONE_SERVER) && !defined(RPI) + if(tileEntity->type == TileEntityType::Sign) + minecraft->setScreen(new TextEditScreen((SignTileEntity*) tileEntity)); +#endif +} + +void LocalPlayer::updateArmorTypeHash() { + int hash = getArmorTypeHash(); + if (hash != armorTypeHash) { + PlayerArmorEquipmentPacket p(this); + minecraft->raknetInstance->send(p); + armorTypeHash = hash; + } +} diff --git a/src/client/player/LocalPlayer.h b/src/client/player/LocalPlayer.h index e027624..8af3789 100755 --- a/src/client/player/LocalPlayer.h +++ b/src/client/player/LocalPlayer.h @@ -1,110 +1,110 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER__LocalPlayer_H__ -#define NET_MINECRAFT_CLIENT_PLAYER__LocalPlayer_H__ - -//package net.minecraft.client.player; - -#include "input/IMoveInput.h" -#include "../../util/SmoothFloat.h" -#include "../../world/entity/player/Player.h" - -class Minecraft; -class Stat; -class CompoundTag; - -class LocalPlayer: public Player -{ - typedef Player super; -public: - LocalPlayer(Minecraft* minecraft, Level* level, const std::string& username, int dimension, bool isCreative); - ~LocalPlayer(); - - void _init(); - virtual void reset(); - - void tick(); - void move(float xa, float ya, float za); - - void aiStep(); - void updateAi(); - - void setKey(int eventKey, bool eventKeyState); - void releaseAllKeys(); - - void addAdditonalSaveData(CompoundTag* entityTag); - void readAdditionalSaveData(CompoundTag* entityTag); - - void closeContainer(); - - void drop(ItemInstance* item, bool randomly); - void take(Entity* e, int orgCount); - - void startCrafting(int x, int y, int z, int tableSize); - void startStonecutting(int x, int y, int z); - - void openContainer(ChestTileEntity* container); - void openFurnace(FurnaceTileEntity* e); - - bool isSneaking(); - - void actuallyHurt(int dmg); - void hurtTo(int newHealth); - void die(Entity* source); - - void respawn(); - - void animateRespawn() {} - float getFieldOfViewModifier(); - void chat(const std::string& message) {} - void displayClientMessage(const std::string& messageId); - - void awardStat(Stat* stat, int count) { - //minecraft->stats.award(stat, count); - //minecraft->achievementPopup.popup("Achievement get!", stat.name); - } - void causeFallDamage( float distance ); - - virtual int startSleepInBed(int x, int y, int z); - virtual void stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint); - - void swing(); - virtual void openTextEdit( TileEntity* tileEntity ); - virtual float getWalkingSpeedModifier(); -private: - void calculateFlight(float xa, float ya, float za); - bool isSolidTile(int x, int y, int z); - void updateArmorTypeHash(); -public: - IMoveInput* input; - bool autoJumpEnabled; -protected: - Minecraft* minecraft; - int jumpTriggerTime; - int ascendTriggerTime; - int descendTriggerTime; - bool ascending, descending; -private: - // local player fly - // ----------------------- - float flyX, flyY, flyZ; - - // smooth camera settings - SmoothFloat smoothFlyX; - SmoothFloat smoothFlyY; - SmoothFloat smoothFlyZ; - - int autoJumpTime; - - int sentInventoryItemId; - int sentInventoryItemData; - - int armorTypeHash; - - // sprinting - bool sprinting; - int sprintDoubleTapTimer; - bool prevForwardHeld; -public: - void setSprinting(bool sprint) { sprinting = sprint; } -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER__LocalPlayer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER__LocalPlayer_H__ +#define NET_MINECRAFT_CLIENT_PLAYER__LocalPlayer_H__ + +//package net.minecraft.client.player; + +#include "input/IMoveInput.h" +#include "../../util/SmoothFloat.h" +#include "../../world/entity/player/Player.h" + +class Minecraft; +class Stat; +class CompoundTag; + +class LocalPlayer: public Player +{ + typedef Player super; +public: + LocalPlayer(Minecraft* minecraft, Level* level, const std::string& username, int dimension, bool isCreative); + ~LocalPlayer(); + + void _init(); + virtual void reset(); + + void tick(); + void move(float xa, float ya, float za); + + void aiStep(); + void updateAi(); + + void setKey(int eventKey, bool eventKeyState); + void releaseAllKeys(); + + void addAdditonalSaveData(CompoundTag* entityTag); + void readAdditionalSaveData(CompoundTag* entityTag); + + void closeContainer(); + + void drop(ItemInstance* item, bool randomly); + void take(Entity* e, int orgCount); + + void startCrafting(int x, int y, int z, int tableSize); + void startStonecutting(int x, int y, int z); + + void openContainer(ChestTileEntity* container); + void openFurnace(FurnaceTileEntity* e); + + bool isSneaking(); + + void actuallyHurt(int dmg); + void hurtTo(int newHealth); + void die(Entity* source); + + void respawn(); + + void animateRespawn() {} + float getFieldOfViewModifier(); + void chat(const std::string& message) {} + void displayClientMessage(const std::string& messageId); + + void awardStat(Stat* stat, int count) { + //minecraft->stats.award(stat, count); + //minecraft->achievementPopup.popup("Achievement get!", stat.name); + } + void causeFallDamage( float distance ); + + virtual int startSleepInBed(int x, int y, int z); + virtual void stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint); + + void swing(); + virtual void openTextEdit( TileEntity* tileEntity ); + virtual float getWalkingSpeedModifier(); +private: + void calculateFlight(float xa, float ya, float za); + bool isSolidTile(int x, int y, int z); + void updateArmorTypeHash(); +public: + IMoveInput* input; + bool autoJumpEnabled; +protected: + Minecraft* minecraft; + int jumpTriggerTime; + int ascendTriggerTime; + int descendTriggerTime; + bool ascending, descending; +private: + // local player fly + // ----------------------- + float flyX, flyY, flyZ; + + // smooth camera settings + SmoothFloat smoothFlyX; + SmoothFloat smoothFlyY; + SmoothFloat smoothFlyZ; + + int autoJumpTime; + + int sentInventoryItemId; + int sentInventoryItemData; + + int armorTypeHash; + + // sprinting + bool sprinting; + int sprintDoubleTapTimer; + bool prevForwardHeld; +public: + void setSprinting(bool sprint) { sprinting = sprint; } +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER__LocalPlayer_H__*/ diff --git a/src/client/player/RemotePlayer.cpp b/src/client/player/RemotePlayer.cpp index 56b20d7..75a9b30 100755 --- a/src/client/player/RemotePlayer.cpp +++ b/src/client/player/RemotePlayer.cpp @@ -1,35 +1,35 @@ -#include "RemotePlayer.h" -#include "../../world/entity/player/Inventory.h" -#include "../../world/entity/SharedFlags.h" - -RemotePlayer::RemotePlayer(Level* level, bool isCreative) -: Player(level, isCreative), - hasStartedUsingItem(false) -{ - hasFakeInventory = true; -} - -void RemotePlayer::tick() { - super::tick(); - - if (!hasStartedUsingItem && getSharedFlag(SharedFlagsInformation::FLAG_USINGITEM)) { - hasStartedUsingItem = true; - ItemInstance* instance = inventory->getItem(Inventory::MAX_SELECTION_SIZE); - if (instance) { - Item* item = Item::items[instance->id]; - if (item) { - startUsingItem(*instance, item->getUseDuration(instance)); - //LOGI("Start using item! %s\n", instance->getDescriptionId().c_str()); - } - } - } else if (hasStartedUsingItem && !getSharedFlag(SharedFlagsInformation::FLAG_USINGITEM)) { -// LOGI("Stop using item! %s\n", useItem.getDescriptionId().c_str()); - stopUsingItem(); - hasStartedUsingItem = false; - } -} - -void RemotePlayer::aiStep() { - updateAttackAnim(); - super::aiStep(); -} +#include "RemotePlayer.h" +#include "../../world/entity/player/Inventory.h" +#include "../../world/entity/SharedFlags.h" + +RemotePlayer::RemotePlayer(Level* level, bool isCreative) +: Player(level, isCreative), + hasStartedUsingItem(false) +{ + hasFakeInventory = true; +} + +void RemotePlayer::tick() { + super::tick(); + + if (!hasStartedUsingItem && getSharedFlag(SharedFlagsInformation::FLAG_USINGITEM)) { + hasStartedUsingItem = true; + ItemInstance* instance = inventory->getItem(Inventory::MAX_SELECTION_SIZE); + if (instance) { + Item* item = Item::items[instance->id]; + if (item) { + startUsingItem(*instance, item->getUseDuration(instance)); + //LOGI("Start using item! %s\n", instance->getDescriptionId().c_str()); + } + } + } else if (hasStartedUsingItem && !getSharedFlag(SharedFlagsInformation::FLAG_USINGITEM)) { +// LOGI("Stop using item! %s\n", useItem.getDescriptionId().c_str()); + stopUsingItem(); + hasStartedUsingItem = false; + } +} + +void RemotePlayer::aiStep() { + updateAttackAnim(); + super::aiStep(); +} diff --git a/src/client/player/RemotePlayer.h b/src/client/player/RemotePlayer.h index 6cdba9a..9c054d2 100755 --- a/src/client/player/RemotePlayer.h +++ b/src/client/player/RemotePlayer.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER__RemotePlayer_H__ -#define NET_MINECRAFT_CLIENT_PLAYER__RemotePlayer_H__ - -//package net.minecraft.client.player; - -#include "../../world/entity/player/Player.h" - -class Level; - -class RemotePlayer: public Player -{ - typedef Player super; -public: - RemotePlayer(Level* level, bool isCreative); - void aiStep(); - void tick(); -private: - bool hasStartedUsingItem; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER__RemotePlayer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER__RemotePlayer_H__ +#define NET_MINECRAFT_CLIENT_PLAYER__RemotePlayer_H__ + +//package net.minecraft.client.player; + +#include "../../world/entity/player/Player.h" + +class Level; + +class RemotePlayer: public Player +{ + typedef Player super; +public: + RemotePlayer(Level* level, bool isCreative); + void aiStep(); + void tick(); +private: + bool hasStartedUsingItem; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER__RemotePlayer_H__*/ diff --git a/src/client/player/input/ControllerTurnInput.h b/src/client/player/input/ControllerTurnInput.h index d34a42f..07dac69 100755 --- a/src/client/player/input/ControllerTurnInput.h +++ b/src/client/player/input/ControllerTurnInput.h @@ -1,69 +1,69 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_ControllerTurnInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_ControllerTurnInput_H__ - -#include "ITurnInput.h" -#include "../../../platform/input/Controller.h" - -/** A Controller Turn input */ -class ControllerTurnInput : public ITurnInput { -public: - static const int MODE_OFFSET = 1; - static const int MODE_DELTA = 2; - - ControllerTurnInput(int controllerId, int mode_) - : cid(controllerId), - mode(mode_), - cxO(0), cyO(0), - wasActive(false) - {} - - bool isTouched() { return Controller::isTouched(cid); } - - TurnDelta getTurnDelta() { - float dx = 0, dy = 0; - bool isActive = Controller::isTouched(cid); - - if (MODE_OFFSET == mode) { - float dt = getDeltaTime(); - const float MaxTurnX = 250.0f; - const float MaxTurnY = 200.0f; - float cx = isActive? Controller::getX(cid) : cxO * 0.7f; - float cy = isActive? Controller::getY(cid) : cyO * 0.7f; - dx = linearTransform( cx, 0.1f, MaxTurnX ) * dt; - dy = linearTransform( cy, 0.1f, MaxTurnY ) * dt; - cxO = cx; - cyO = cy; - } else - if (MODE_DELTA == mode && (wasActive || isActive)) { - float cx = Controller::getX(cid); - float cy = Controller::getY(cid); - -// const float dt = getDeltaTime(); - const float MaxTurnX = 100.0f; - const float MaxTurnY = 100.0f; - const float DeadZone = 0;//0.25f * dt;//0.02f; - - if (!wasActive) { - cxO = cx; - cyO = cy; - } - if (isActive) { - dx = linearTransform(cx - cxO, DeadZone) * MaxTurnX; - dy = linearTransform(cy - cyO, DeadZone) * MaxTurnY; - cxO = cx; - cyO = cy; - } - } - - wasActive = isActive; - return TurnDelta(dx, -dy); - } - - int mode; -private: - int cid; - float cxO, cyO; - bool wasActive; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_ControllerTurnInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_ControllerTurnInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_ControllerTurnInput_H__ + +#include "ITurnInput.h" +#include "../../../platform/input/Controller.h" + +/** A Controller Turn input */ +class ControllerTurnInput : public ITurnInput { +public: + static const int MODE_OFFSET = 1; + static const int MODE_DELTA = 2; + + ControllerTurnInput(int controllerId, int mode_) + : cid(controllerId), + mode(mode_), + cxO(0), cyO(0), + wasActive(false) + {} + + bool isTouched() { return Controller::isTouched(cid); } + + TurnDelta getTurnDelta() { + float dx = 0, dy = 0; + bool isActive = Controller::isTouched(cid); + + if (MODE_OFFSET == mode) { + float dt = getDeltaTime(); + const float MaxTurnX = 250.0f; + const float MaxTurnY = 200.0f; + float cx = isActive? Controller::getX(cid) : cxO * 0.7f; + float cy = isActive? Controller::getY(cid) : cyO * 0.7f; + dx = linearTransform( cx, 0.1f, MaxTurnX ) * dt; + dy = linearTransform( cy, 0.1f, MaxTurnY ) * dt; + cxO = cx; + cyO = cy; + } else + if (MODE_DELTA == mode && (wasActive || isActive)) { + float cx = Controller::getX(cid); + float cy = Controller::getY(cid); + +// const float dt = getDeltaTime(); + const float MaxTurnX = 100.0f; + const float MaxTurnY = 100.0f; + const float DeadZone = 0;//0.25f * dt;//0.02f; + + if (!wasActive) { + cxO = cx; + cyO = cy; + } + if (isActive) { + dx = linearTransform(cx - cxO, DeadZone) * MaxTurnX; + dy = linearTransform(cy - cyO, DeadZone) * MaxTurnY; + cxO = cx; + cyO = cy; + } + } + + wasActive = isActive; + return TurnDelta(dx, -dy); + } + + int mode; +private: + int cid; + float cxO, cyO; + bool wasActive; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_ControllerTurnInput_H__*/ diff --git a/src/client/player/input/IBuildInput.h b/src/client/player/input/IBuildInput.h index 488e172..47570b4 100755 --- a/src/client/player/input/IBuildInput.h +++ b/src/client/player/input/IBuildInput.h @@ -1,45 +1,45 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT__IBuildInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER_INPUT__IBuildInput_H__ - -#include "../../IConfigListener.h" -class Player; - -class BuildActionIntention { -public: - BuildActionIntention() - : action(0) - {} - BuildActionIntention(int action) - : action(action) - {} - - bool isFirstRemove() { return (action & BAI_FIRSTREMOVE) != 0; } - bool isRemoveContinue() { return (action & BAI_REMOVE) != 0; } - - bool isBuild() { return (action & BAI_BUILD) != 0; } - bool isRemove() { return isFirstRemove() || isRemoveContinue(); } - - bool isAttack() { return (action & BAI_ATTACK) != 0; } - bool isInteract() { return (action & BAI_INTERACT) != 0; } - - int action; - - static const int BAI_BUILD = 1; - static const int BAI_REMOVE = 2; - static const int BAI_FIRSTREMOVE = 4; - - static const int BAI_ATTACK = 8; - static const int BAI_INTERACT = 16; -}; - - -class IBuildInput: public IConfigListener { -public: - virtual ~IBuildInput(){} - - virtual void onConfigChanged(const Config& c) {} - // @return true if user wants to do an action, false if not - virtual bool tickBuild(Player*, BuildActionIntention* bai) { return false; } -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT__IBuildInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT__IBuildInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER_INPUT__IBuildInput_H__ + +#include "../../IConfigListener.h" +class Player; + +class BuildActionIntention { +public: + BuildActionIntention() + : action(0) + {} + BuildActionIntention(int action) + : action(action) + {} + + bool isFirstRemove() { return (action & BAI_FIRSTREMOVE) != 0; } + bool isRemoveContinue() { return (action & BAI_REMOVE) != 0; } + + bool isBuild() { return (action & BAI_BUILD) != 0; } + bool isRemove() { return isFirstRemove() || isRemoveContinue(); } + + bool isAttack() { return (action & BAI_ATTACK) != 0; } + bool isInteract() { return (action & BAI_INTERACT) != 0; } + + int action; + + static const int BAI_BUILD = 1; + static const int BAI_REMOVE = 2; + static const int BAI_FIRSTREMOVE = 4; + + static const int BAI_ATTACK = 8; + static const int BAI_INTERACT = 16; +}; + + +class IBuildInput: public IConfigListener { +public: + virtual ~IBuildInput(){} + + virtual void onConfigChanged(const Config& c) {} + // @return true if user wants to do an action, false if not + virtual bool tickBuild(Player*, BuildActionIntention* bai) { return false; } +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT__IBuildInput_H__*/ diff --git a/src/client/player/input/IInputHolder.h b/src/client/player/input/IInputHolder.h index 4f531c6..9a3464e 100755 --- a/src/client/player/input/IInputHolder.h +++ b/src/client/player/input/IInputHolder.h @@ -1,75 +1,75 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER__IInputHolder_H__ -#define NET_MINECRAFT_CLIENT_PLAYER__IInputHolder_H__ - -#include "IMoveInput.h" -#include "ITurnInput.h" -#include "IBuildInput.h" - -#include "../../../platform/input/Mouse.h" - -class Player; - -class IInputHolder: public IConfigListener -{ -public: - IInputHolder() - : mousex(0), - mousey(0), - alpha(0) - {} - - virtual ~IInputHolder() {} - - virtual void render(float alpha) {} - - virtual bool allowPicking() { - mousex = Mouse::getX(); - mousey = Mouse::getY(); - return Mouse::getButtonState(MouseAction::ACTION_LEFT) == MouseAction::DATA_DOWN; - } - - void onConfigChanged(const Config& c) { - getMoveInput()->onConfigChanged(c); - getTurnInput()->onConfigChanged(c); - getBuildInput()->onConfigChanged(c); - } - - virtual IMoveInput* getMoveInput() = 0; - virtual ITurnInput* getTurnInput() = 0; - virtual IBuildInput* getBuildInput() = 0; - - float mousex, mousey; - float alpha; -}; - - -class CustomInputHolder: public IInputHolder -{ -public: - CustomInputHolder(IMoveInput* move, ITurnInput* turn, IBuildInput* build) - { - setInputs(move, turn, build); - } - - ~CustomInputHolder() { - delete _move; - delete _turn; - delete _build; - } - void setInputs(IMoveInput* move, ITurnInput* turn, IBuildInput* build) { - _move = move; - _turn = turn; - _build = build; - } - - virtual IMoveInput* getMoveInput() { return _move; } - virtual ITurnInput* getTurnInput() { return _turn; } - virtual IBuildInput* getBuildInput() { return _build; } - -private: - IMoveInput* _move; - ITurnInput* _turn; - IBuildInput* _build; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER__IInputHolder_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER__IInputHolder_H__ +#define NET_MINECRAFT_CLIENT_PLAYER__IInputHolder_H__ + +#include "IMoveInput.h" +#include "ITurnInput.h" +#include "IBuildInput.h" + +#include "../../../platform/input/Mouse.h" + +class Player; + +class IInputHolder: public IConfigListener +{ +public: + IInputHolder() + : mousex(0), + mousey(0), + alpha(0) + {} + + virtual ~IInputHolder() {} + + virtual void render(float alpha) {} + + virtual bool allowPicking() { + mousex = Mouse::getX(); + mousey = Mouse::getY(); + return Mouse::getButtonState(MouseAction::ACTION_LEFT) == MouseAction::DATA_DOWN; + } + + void onConfigChanged(const Config& c) { + getMoveInput()->onConfigChanged(c); + getTurnInput()->onConfigChanged(c); + getBuildInput()->onConfigChanged(c); + } + + virtual IMoveInput* getMoveInput() = 0; + virtual ITurnInput* getTurnInput() = 0; + virtual IBuildInput* getBuildInput() = 0; + + float mousex, mousey; + float alpha; +}; + + +class CustomInputHolder: public IInputHolder +{ +public: + CustomInputHolder(IMoveInput* move, ITurnInput* turn, IBuildInput* build) + { + setInputs(move, turn, build); + } + + ~CustomInputHolder() { + delete _move; + delete _turn; + delete _build; + } + void setInputs(IMoveInput* move, ITurnInput* turn, IBuildInput* build) { + _move = move; + _turn = turn; + _build = build; + } + + virtual IMoveInput* getMoveInput() { return _move; } + virtual ITurnInput* getTurnInput() { return _turn; } + virtual IBuildInput* getBuildInput() { return _build; } + +private: + IMoveInput* _move; + ITurnInput* _turn; + IBuildInput* _build; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER__IInputHolder_H__*/ diff --git a/src/client/player/input/IMoveInput.h b/src/client/player/input/IMoveInput.h index e61bac1..2576aa7 100755 --- a/src/client/player/input/IMoveInput.h +++ b/src/client/player/input/IMoveInput.h @@ -1,47 +1,47 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER__IMoveInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER__IMoveInput_H__ - -//package net.minecraft.client.player; - -#include "../../IConfigListener.h" -class Player; -class Minecraft; - -class IMoveInput -{ -protected: - IMoveInput() - : xa(0), - ya(0), - wasJumping(false), - jumping(false), - sneaking(false), - wantUp(false), - wantDown(false), - isChangingFlightHeight(false) - {} -public: - virtual ~IMoveInput() {} - - virtual void tick(Player* player) {} - virtual void render(float a) {} - - virtual void setKey(int eventKey, bool eventKeyState) {} - virtual void releaseAllKeys() {} - - virtual void onConfigChanged(const Config& c) {} - -public: - float xa; - float ya; - - bool wasJumping; - bool jumping; - bool sneaking; - - bool wantUp; - bool wantDown; - bool isChangingFlightHeight; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER__IMoveInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER__IMoveInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER__IMoveInput_H__ + +//package net.minecraft.client.player; + +#include "../../IConfigListener.h" +class Player; +class Minecraft; + +class IMoveInput +{ +protected: + IMoveInput() + : xa(0), + ya(0), + wasJumping(false), + jumping(false), + sneaking(false), + wantUp(false), + wantDown(false), + isChangingFlightHeight(false) + {} +public: + virtual ~IMoveInput() {} + + virtual void tick(Player* player) {} + virtual void render(float a) {} + + virtual void setKey(int eventKey, bool eventKeyState) {} + virtual void releaseAllKeys() {} + + virtual void onConfigChanged(const Config& c) {} + +public: + float xa; + float ya; + + bool wasJumping; + bool jumping; + bool sneaking; + + bool wantUp; + bool wantDown; + bool isChangingFlightHeight; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER__IMoveInput_H__*/ diff --git a/src/client/player/input/ITurnInput.h b/src/client/player/input/ITurnInput.h index 151e807..8f0266d 100755 --- a/src/client/player/input/ITurnInput.h +++ b/src/client/player/input/ITurnInput.h @@ -1,52 +1,52 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER__ITurnInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER__ITurnInput_H__ - -#include "../../../platform/time.h" -#include "../../IConfigListener.h" -#include - -/** Representing a Turn delta value */ -class TurnDelta { -public: - TurnDelta(float dx, float dy) - : x(dx), - y(dy) - {} - - float x, y; -}; - -/** Base class for classes providing TurnDeltas */ -class ITurnInput: public IConfigListener { -public: - virtual ~ITurnInput() {} - virtual void onConfigChanged(const Config& c) {} - - virtual TurnDelta getTurnDelta() = 0; -protected: - ITurnInput() - : _lastTime(-1.0f) - {} - - float getDeltaTime(){ - if (_lastTime == -1.0f) { - _lastTime = getTimeS(); - } - float now = getTimeS(); - float dt = now - _lastTime; - _lastTime = now; - return dt; - } - - static float linearTransform(float value, float deadZone, float scale=1.0f, bool limit1=false) { - float deadSigned = value >= 0? deadZone : -deadZone; - if (std::abs(deadSigned) >= std::abs(value)) return 0; - float ret = (value - deadSigned) * scale; - if (limit1 && std::abs(ret) > 1) ret /= std::abs(ret); - return ret; - } -private: - float _lastTime; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER__ITurnInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER__ITurnInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER__ITurnInput_H__ + +#include "../../../platform/time.h" +#include "../../IConfigListener.h" +#include + +/** Representing a Turn delta value */ +class TurnDelta { +public: + TurnDelta(float dx, float dy) + : x(dx), + y(dy) + {} + + float x, y; +}; + +/** Base class for classes providing TurnDeltas */ +class ITurnInput: public IConfigListener { +public: + virtual ~ITurnInput() {} + virtual void onConfigChanged(const Config& c) {} + + virtual TurnDelta getTurnDelta() = 0; +protected: + ITurnInput() + : _lastTime(-1.0f) + {} + + float getDeltaTime(){ + if (_lastTime == -1.0f) { + _lastTime = getTimeS(); + } + float now = getTimeS(); + float dt = now - _lastTime; + _lastTime = now; + return dt; + } + + static float linearTransform(float value, float deadZone, float scale=1.0f, bool limit1=false) { + float deadSigned = value >= 0? deadZone : -deadZone; + if (std::abs(deadSigned) >= std::abs(value)) return 0; + float ret = (value - deadSigned) * scale; + if (limit1 && std::abs(ret) > 1) ret /= std::abs(ret); + return ret; + } +private: + float _lastTime; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER__ITurnInput_H__*/ diff --git a/src/client/player/input/KeyboardInput.cpp b/src/client/player/input/KeyboardInput.cpp index b84fe75..6915a1c 100755 --- a/src/client/player/input/KeyboardInput.cpp +++ b/src/client/player/input/KeyboardInput.cpp @@ -1,61 +1,61 @@ -#include "KeyboardInput.h" -#include "../../Options.h" -#include "../../../world/entity/player/Player.h" - -KeyboardInput::KeyboardInput( Options* options ) -{ - for (int i = 0; i < NumKeys; ++i) - keys[i] = false; - this->options = options; -} - -void KeyboardInput::setKey( int key, bool state ) -{ - int id = -1; - if (key == options->getIntValue(OPTIONS_KEY_FORWARD)) id = KEY_UP; - if (key == options->getIntValue(OPTIONS_KEY_BACK)) id = KEY_DOWN; - if (key == options->getIntValue(OPTIONS_KEY_LEFT)) id = KEY_LEFT; - if (key == options->getIntValue(OPTIONS_KEY_RIGHT)) id = KEY_RIGHT; - if (key == options->getIntValue(OPTIONS_KEY_JUMP)) id = KEY_JUMP; - if (key == options->getIntValue(OPTIONS_KEY_SNEAK)) id = KEY_SNEAK; - if (id >= 0) { - keys[id] = state; - } -} - -void KeyboardInput::releaseAllKeys() -{ - xa = 0; - ya = 0; - - for (int i = 0; i < NumKeys; i++) { - keys[i] = false; - } - wantUp = wantDown = false; -} - -void KeyboardInput::tick( Player* player ) -{ - xa = 0; - ya = 0; - - if (keys[KEY_UP]) ya++; - if (keys[KEY_DOWN]) ya--; - if (keys[KEY_LEFT]) xa++; - if (keys[KEY_RIGHT]) xa--; - jumping = keys[KEY_JUMP]; - sneaking = keys[KEY_SNEAK]; - if (sneaking) { - xa *= 0.3f; - ya *= 0.3f; - } - - wantUp = jumping; - wantDown = sneaking; - - if (keys[KEY_CRAFT]) - player->startCrafting((int)player->x, (int)player->y, (int)player->z, Recipe::SIZE_2X2); - - //printf("\n>- %f %f\n", xa, ya); -} - +#include "KeyboardInput.h" +#include "../../Options.h" +#include "../../../world/entity/player/Player.h" + +KeyboardInput::KeyboardInput( Options* options ) +{ + for (int i = 0; i < NumKeys; ++i) + keys[i] = false; + this->options = options; +} + +void KeyboardInput::setKey( int key, bool state ) +{ + int id = -1; + if (key == options->getIntValue(OPTIONS_KEY_FORWARD)) id = KEY_UP; + if (key == options->getIntValue(OPTIONS_KEY_BACK)) id = KEY_DOWN; + if (key == options->getIntValue(OPTIONS_KEY_LEFT)) id = KEY_LEFT; + if (key == options->getIntValue(OPTIONS_KEY_RIGHT)) id = KEY_RIGHT; + if (key == options->getIntValue(OPTIONS_KEY_JUMP)) id = KEY_JUMP; + if (key == options->getIntValue(OPTIONS_KEY_SNEAK)) id = KEY_SNEAK; + if (id >= 0) { + keys[id] = state; + } +} + +void KeyboardInput::releaseAllKeys() +{ + xa = 0; + ya = 0; + + for (int i = 0; i < NumKeys; i++) { + keys[i] = false; + } + wantUp = wantDown = false; +} + +void KeyboardInput::tick( Player* player ) +{ + xa = 0; + ya = 0; + + if (keys[KEY_UP]) ya++; + if (keys[KEY_DOWN]) ya--; + if (keys[KEY_LEFT]) xa++; + if (keys[KEY_RIGHT]) xa--; + jumping = keys[KEY_JUMP]; + sneaking = keys[KEY_SNEAK]; + if (sneaking) { + xa *= 0.3f; + ya *= 0.3f; + } + + wantUp = jumping; + wantDown = sneaking; + + if (keys[KEY_CRAFT]) + player->startCrafting((int)player->x, (int)player->y, (int)player->z, Recipe::SIZE_2X2); + + //printf("\n>- %f %f\n", xa, ya); +} + diff --git a/src/client/player/input/KeyboardInput.h b/src/client/player/input/KeyboardInput.h index eb33c31..255d794 100755 --- a/src/client/player/input/KeyboardInput.h +++ b/src/client/player/input/KeyboardInput.h @@ -1,36 +1,36 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER__KeyboardInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER__KeyboardInput_H__ - -//package net.minecraft.client.player; - -#include "IMoveInput.h" - -class Options; -class Player; - -// @todo: extract a separate MoveInput (-> merge XperiaPlayInput) -class KeyboardInput: public IMoveInput -{ - static const int NumKeys = 10; -public: - static const int KEY_UP = 0; - static const int KEY_DOWN = 1; - static const int KEY_LEFT = 2; - static const int KEY_RIGHT = 3; - static const int KEY_JUMP = 4; - static const int KEY_SNEAK = 5; - static const int KEY_CRAFT = 6; - - KeyboardInput(Options* options); - - void tick(Player* player); - - void setKey(int key, bool state); - void releaseAllKeys(); - -protected: - bool keys[NumKeys]; - Options* options; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER__KeyboardInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER__KeyboardInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER__KeyboardInput_H__ + +//package net.minecraft.client.player; + +#include "IMoveInput.h" + +class Options; +class Player; + +// @todo: extract a separate MoveInput (-> merge XperiaPlayInput) +class KeyboardInput: public IMoveInput +{ + static const int NumKeys = 10; +public: + static const int KEY_UP = 0; + static const int KEY_DOWN = 1; + static const int KEY_LEFT = 2; + static const int KEY_RIGHT = 3; + static const int KEY_JUMP = 4; + static const int KEY_SNEAK = 5; + static const int KEY_CRAFT = 6; + + KeyboardInput(Options* options); + + void tick(Player* player); + + void setKey(int key, bool state); + void releaseAllKeys(); + +protected: + bool keys[NumKeys]; + Options* options; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER__KeyboardInput_H__*/ diff --git a/src/client/player/input/MouseBuildInput.h b/src/client/player/input/MouseBuildInput.h index c50b0c4..1636fae 100755 --- a/src/client/player/input/MouseBuildInput.h +++ b/src/client/player/input/MouseBuildInput.h @@ -1,36 +1,36 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseBuildInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseBuildInput_H__ - -#include "IBuildInput.h" -#include "../../../platform/input/Mouse.h" - -/** A Mouse Build input */ -class MouseBuildInput : public IBuildInput { -public: - MouseBuildInput() - : buildDelayTicks(10), - buildHoldTicks(0) - {} - - virtual bool tickBuild(Player* p, BuildActionIntention* bai) { - if (Mouse::getButtonState(MouseAction::ACTION_LEFT) != 0) { - *bai = BuildActionIntention(BuildActionIntention::BAI_REMOVE | BuildActionIntention::BAI_ATTACK); - return true; - } - if (Mouse::getButtonState(MouseAction::ACTION_RIGHT) != 0) { - if (buildHoldTicks >= buildDelayTicks) buildHoldTicks = 0; - if (++buildHoldTicks == 1) { - *bai = BuildActionIntention(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_INTERACT); - return true; - } - } else { - buildHoldTicks = 0; - } - return false; - } -private: - int buildHoldTicks; - int buildDelayTicks; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseBuildInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseBuildInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseBuildInput_H__ + +#include "IBuildInput.h" +#include "../../../platform/input/Mouse.h" + +/** A Mouse Build input */ +class MouseBuildInput : public IBuildInput { +public: + MouseBuildInput() + : buildDelayTicks(10), + buildHoldTicks(0) + {} + + virtual bool tickBuild(Player* p, BuildActionIntention* bai) { + if (Mouse::getButtonState(MouseAction::ACTION_LEFT) != 0) { + *bai = BuildActionIntention(BuildActionIntention::BAI_REMOVE | BuildActionIntention::BAI_ATTACK); + return true; + } + if (Mouse::getButtonState(MouseAction::ACTION_RIGHT) != 0) { + if (buildHoldTicks >= buildDelayTicks) buildHoldTicks = 0; + if (++buildHoldTicks == 1) { + *bai = BuildActionIntention(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_INTERACT); + return true; + } + } else { + buildHoldTicks = 0; + } + return false; + } +private: + int buildHoldTicks; + int buildDelayTicks; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseBuildInput_H__*/ diff --git a/src/client/player/input/MouseTurnInput.h b/src/client/player/input/MouseTurnInput.h index a1cf6b4..9d23161 100755 --- a/src/client/player/input/MouseTurnInput.h +++ b/src/client/player/input/MouseTurnInput.h @@ -1,43 +1,43 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseTurnInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseTurnInput_H__ - -#include "ITurnInput.h" -#include "../../../platform/input/Mouse.h" - -/** A Mouse Turn input */ -class MouseTurnInput : public ITurnInput { -public: - static const int MODE_OFFSET = 1; - static const int MODE_DELTA = 2; - - MouseTurnInput(int mode_, int centerX, int centerY) - : mode(mode_), - _centerX((float)centerX), - _centerY((float)centerY) - {} - - void onConfigChanged(const Config& config) { - _centerX = ((float) config.width ) * 0.5f; - _centerY = ((float) config.height) * 0.5f; - } - - TurnDelta getTurnDelta() { - float dx = 0, dy = 0; - if ( MODE_DELTA == mode ) { - dx = (float)Mouse::getDX(); - dy = (float)Mouse::getDY(); - } else if (MODE_OFFSET == mode) { - float dt = getDeltaTime(); - dx = linearTransform( (float)Mouse::getX() - _centerX, 40, 0.4f ) * dt; - dy = linearTransform( (float)Mouse::getY() - _centerY, 30, 0.6f ) * dt; - } - return TurnDelta(dx, dy); - } - - int mode; -private: - float _centerX; - float _centerY; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseTurnInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseTurnInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseTurnInput_H__ + +#include "ITurnInput.h" +#include "../../../platform/input/Mouse.h" + +/** A Mouse Turn input */ +class MouseTurnInput : public ITurnInput { +public: + static const int MODE_OFFSET = 1; + static const int MODE_DELTA = 2; + + MouseTurnInput(int mode_, int centerX, int centerY) + : mode(mode_), + _centerX((float)centerX), + _centerY((float)centerY) + {} + + void onConfigChanged(const Config& config) { + _centerX = ((float) config.width ) * 0.5f; + _centerY = ((float) config.height) * 0.5f; + } + + TurnDelta getTurnDelta() { + float dx = 0, dy = 0; + if ( MODE_DELTA == mode ) { + dx = (float)Mouse::getDX(); + dy = (float)Mouse::getDY(); + } else if (MODE_OFFSET == mode) { + float dt = getDeltaTime(); + dx = linearTransform( (float)Mouse::getX() - _centerX, 40, 0.4f ) * dt; + dy = linearTransform( (float)Mouse::getY() - _centerY, 30, 0.6f ) * dt; + } + return TurnDelta(dx, dy); + } + + int mode; +private: + float _centerX; + float _centerY; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_MouseTurnInput_H__*/ diff --git a/src/client/player/input/XperiaPlayInput.h b/src/client/player/input/XperiaPlayInput.h index bbebab5..b964ea8 100755 --- a/src/client/player/input/XperiaPlayInput.h +++ b/src/client/player/input/XperiaPlayInput.h @@ -1,53 +1,51 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER__XperiaPlayInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER__XperiaPlayInput_H__ -#include "Player.h" -#include "Controller.h" - -//package net.minecraft.client.player; - -#include "KeyboardInput.h" -#include "platform/input/Controller.h" -#include "world/entity/player/Player.h" - -// @note: This is just copy-pasted from KeyboardInput right now. -class XperiaPlayInput: public KeyboardInput -{ - typedef KeyboardInput super; -public: - XperiaPlayInput(Options* options) - : super(options), - wasSneakTouched(false) - {} - - void tick(Player* player) { - bool _sneaking = sneaking; - super::tick(player); - sneaking = _sneaking; - - wantUp = (jumping && keys[KEY_UP]); - wantDown = (jumping && keys[KEY_DOWN]); - if ((wantUp | wantDown) && (player && player->abilities.flying)) ya = 0; - - bool isSneakTouched = Controller::isTouched(1); - if (isSneakTouched && !wasSneakTouched) { - sneaking = !sneaking; - } - wasSneakTouched = isSneakTouched; - - if (sneaking) { - xa *= 0.3f; - ya *= 0.3f; - } - } - - /* - if (Controller::isTouched(moveStick)) { - xa = -Controller::getTransformedX(moveStick, 0.1f, 1.25f, true); - ya = +Controller::getTransformedY(moveStick, 0.1f, 1.25f, true); - } - */ -private: - bool wasSneakTouched; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER__XperiaPlayInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER__XperiaPlayInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER__XperiaPlayInput_H__ + +//package net.minecraft.client.player; + +#include "KeyboardInput.h" +#include "platform/input/Controller.h" +#include "world/entity/player/Player.h" + +// @note: This is just copy-pasted from KeyboardInput right now. +class XperiaPlayInput: public KeyboardInput +{ + typedef KeyboardInput super; +public: + XperiaPlayInput(Options* options) + : super(options), + wasSneakTouched(false) + {} + + void tick(Player* player) { + bool _sneaking = sneaking; + super::tick(player); + sneaking = _sneaking; + + wantUp = (jumping && keys[KEY_UP]); + wantDown = (jumping && keys[KEY_DOWN]); + if ((wantUp | wantDown) && (player && player->abilities.flying)) ya = 0; + + bool isSneakTouched = Controller::isTouched(1); + if (isSneakTouched && !wasSneakTouched) { + sneaking = !sneaking; + } + wasSneakTouched = isSneakTouched; + + if (sneaking) { + xa *= 0.3f; + ya *= 0.3f; + } + } + + /* + if (Controller::isTouched(moveStick)) { + xa = -Controller::getTransformedX(moveStick, 0.1f, 1.25f, true); + ya = +Controller::getTransformedY(moveStick, 0.1f, 1.25f, true); + } + */ +private: + bool wasSneakTouched; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER__XperiaPlayInput_H__*/ diff --git a/src/client/player/input/touchscreen/ITouchScreenModel.h b/src/client/player/input/touchscreen/ITouchScreenModel.h index be764e2..6f3cb08 100755 --- a/src/client/player/input/touchscreen/ITouchScreenModel.h +++ b/src/client/player/input/touchscreen/ITouchScreenModel.h @@ -1,14 +1,14 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER__ITouchScreenModel_H__ -#define NET_MINECRAFT_CLIENT_PLAYER__ITouchScreenModel_H__ - -#include "../../../../platform/input/Mouse.h" - -class ITouchScreenModel -{ -public: - virtual ~ITouchScreenModel() {} - virtual int getPointerId(const MouseAction& m) { return m.pointerId; } - virtual int getPointerId(int x, int y, int pid) { return pid; } -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER__ITouchScreenModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER__ITouchScreenModel_H__ +#define NET_MINECRAFT_CLIENT_PLAYER__ITouchScreenModel_H__ + +#include "../../../../platform/input/Mouse.h" + +class ITouchScreenModel +{ +public: + virtual ~ITouchScreenModel() {} + virtual int getPointerId(const MouseAction& m) { return m.pointerId; } + virtual int getPointerId(int x, int y, int pid) { return pid; } +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER__ITouchScreenModel_H__*/ diff --git a/src/client/player/input/touchscreen/TouchAreaModel.h b/src/client/player/input/touchscreen/TouchAreaModel.h index 636b2a3..3232f1a 100755 --- a/src/client/player/input/touchscreen/TouchAreaModel.h +++ b/src/client/player/input/touchscreen/TouchAreaModel.h @@ -1,339 +1,339 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER__TouchAreaModel_H__ -#define NET_MINECRAFT_CLIENT_PLAYER__TouchAreaModel_H__ - -#include "ITouchScreenModel.h" -#include -#include "../../../../platform/time.h" - -/// Interface for an area that can be point tested -class IArea{ -public: - IArea() - : deleteMe(true) - {} - - virtual ~IArea() {}; - virtual bool isInside(float x, float y) = 0; - //virtual void expandRectToInclude(float& l, float& t, float& r, float& b) {} - - bool deleteMe; -}; - -/// Holder for multiple pairs -class TouchAreaModel: public ITouchScreenModel -{ -public: - TouchAreaModel() {} - - ~TouchAreaModel() { - clear(); - } - - void clear() { - for (unsigned int i = 0; i < _areas.size(); ++i) { - Area* a = _areas[i]; - if (a->area && a->area->deleteMe) - delete a->area; - delete a; - } - _areas.clear(); - } - - int getPointerId(const MouseAction& m) { - return getPointerId(m.x, m.y, m.pointerId); - } - - int getPointerId(int x, int y, int pid) { - //static Stopwatch w; - //w.printEvery(200, "IArea"); - //w.start(); - float fx = (float)x, fy = (float) y; - for (unsigned int i = 0; i < _areas.size(); ++i) { - const Area* a = _areas[i]; - if (a->area->isInside(fx, fy)) { - //w.stop(); - return a->areaId; - } - } - //w.stop(); - return pid; - } - - void addArea(int areaId, IArea* area) { - Area* a = new Area(); - a->area = area; - a->areaId = areaId; - - _areas.push_back( a ); - } - -private: - struct Area { - IArea* area; - int areaId; - }; - - std::vector _areas; -}; - -/// Area represented by a polygon -class PolygonArea: public IArea -{ -public: - PolygonArea(int numPoints, const float* xp, const float* yp) - : _numPoints(numPoints) - { - _x = new float[numPoints]; - _y = new float[numPoints]; - - for (int i = 0; i < numPoints; ++i) { - _x[i] = xp[i]; - _y[i] = yp[i]; - } - } - - ~PolygonArea() { - delete[] _x; - delete[] _y; - } - - virtual bool isInside(float x, float y) - { - bool c = false; - for (int i = 0, j = _numPoints-1; i < _numPoints; j = i++) { - if ((((_y[i] <= y) && (y < _y[j])) || - ((_y[j] <= y) && (y < _y[i]))) && - (x < (_x[j] - _x[i]) * (y - _y[i]) / (_y[j] - _y[i]) + _x[i])) - c = !c; - } - return c == 1; - } - -//private: - float* _x; - float* _y; - int _numPoints; -}; - -/// Area represented by a rectangle -class RectangleArea: public IArea -{ -public: - RectangleArea(float x0, float y0, float x1, float y1) - : _x0(x0), - _x1(x1), - _y0(y0), - _y1(y1) - {} - - virtual bool isInside(float x, float y) { - return x >= _x0 && x <= _x1 - && y >= _y0 && y <= _y1; - } - - virtual float centerX() - { - return _x0 + (_x1 - _x0) * .5f; - } - - virtual float centerY() - { - return _y0 + (_y1 - _y0) * .5f; - } - -//private: - float _x0, _x1; - float _y0, _y1; -}; - -/// An area represented by a circle -class CircleArea: public IArea -{ -public: - CircleArea(float x, float y, float r) - : _x(x), _y(y), _rr(r*r) - {} - - virtual bool isInside(float x, float y) { - const float dx = x - _x; - const float dy = y - _y; - return (dx*dx + dy*dy) <= _rr; - } - -private: - float _x, _y; - float _rr; -}; - -/// Inside any area in include list but none of exclude list -class IncludeExcludeArea: public IArea -{ -public: - IncludeExcludeArea() {} - - ~IncludeExcludeArea() { - clear(); - } - - void clear() { - if (deleteMe) { - for (unsigned int i = 0; i < _includes.size(); ++i) { - IArea* area = _includes[i]; - if (area->deleteMe) delete area; - } - for (unsigned int i = 0; i < _excludes.size(); ++i) { - IArea* area = _excludes[i]; - if (area->deleteMe) delete area; - } - } - _includes.clear(); - _excludes.clear(); - } - - void include(IArea* area) { _includes.push_back(area); } - void exclude(IArea* area) { _excludes.push_back(area); } - - virtual bool isInside(float x, float y) { - for (unsigned int i = 0; i < _includes.size(); ++i) - if (_includes[i]->isInside(x, y)) { - bool good = true; - //@todo: cache _exclude-test results - for (unsigned int j = 0; j < _excludes.size(); ++j) - if (_excludes[j]->isInside(x, y)) { - good = false; - break; - } - if (good) { - return true; - } - } - return false; - } - -private: - std::vector _includes; - std::vector _excludes; -}; - - -/// At least one of the areas -class OrArea: public IArea -{ -public: - OrArea(IArea* a, IArea* b) - : _a(a), - _b(b) - {} - - ~OrArea() { - if (deleteMe) { - if (_a->deleteMe) delete _a; - if (_b->deleteMe) delete _b; - } - } - - virtual bool isInside(float x, float y) { - return _a->isInside(x, y) || _b->isInside(x, y); - } - -private: - IArea* _a; - IArea* _b; -}; - -/// In both areas -class AndArea: public IArea -{ -public: - AndArea(IArea* a, IArea* b) - : _a(a), - _b(b) - {} - - ~AndArea() { - if (deleteMe) { - if (_a->deleteMe) delete _a; - if (_b->deleteMe) delete _b; - } - } - - virtual bool isInside(float x, float y) { - return _a->isInside(x, y) && _b->isInside(x, y); - } - -private: - IArea* _a; - IArea* _b; -}; - -/// Exactly one of the areas -class XorArea: public IArea -{ -public: - XorArea(IArea* a, IArea* b) - : _a(a), - _b(b) - {} - - ~XorArea() { - if (deleteMe) { - if (_a->deleteMe) delete _a; - if (_b->deleteMe) delete _b; - } - } - - virtual bool isInside(float x, float y) { - return _a->isInside(x, y) ^ _b->isInside(x, y); - } - -private: - IArea* _a; - IArea* _b; -}; - -/// Everything except this area -class NotArea: public IArea -{ -public: - NotArea(IArea* a) - : _a(a) - {} - - ~NotArea() { - if (deleteMe && _a->deleteMe) delete _a; - } - - virtual bool isInside(float x, float y) { - return !_a->isInside(x, y); - } - -private: - IArea* _a; -}; - -/// First area, but not second -/// This is semantically the same as AndArea( a, new NotArea(b) ) -class DifferenceArea: public IArea -{ -public: - DifferenceArea(IArea* a, IArea* b) - : _a(a), - _b(b) - {} - - ~DifferenceArea() { - if (deleteMe) { - if (_a->deleteMe) delete _a; - if (_b->deleteMe) delete _b; - } - } - - virtual bool isInside(float x, float y) { - return _a->isInside(x, y) && !_b->isInside(x, y); - } -private: - IArea* _a; - IArea* _b; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER__TouchAreaModel_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER__TouchAreaModel_H__ +#define NET_MINECRAFT_CLIENT_PLAYER__TouchAreaModel_H__ + +#include "ITouchScreenModel.h" +#include +#include "../../../../platform/time.h" + +/// Interface for an area that can be point tested +class IArea{ +public: + IArea() + : deleteMe(true) + {} + + virtual ~IArea() {}; + virtual bool isInside(float x, float y) = 0; + //virtual void expandRectToInclude(float& l, float& t, float& r, float& b) {} + + bool deleteMe; +}; + +/// Holder for multiple pairs +class TouchAreaModel: public ITouchScreenModel +{ +public: + TouchAreaModel() {} + + ~TouchAreaModel() { + clear(); + } + + void clear() { + for (unsigned int i = 0; i < _areas.size(); ++i) { + Area* a = _areas[i]; + if (a->area && a->area->deleteMe) + delete a->area; + delete a; + } + _areas.clear(); + } + + int getPointerId(const MouseAction& m) { + return getPointerId(m.x, m.y, m.pointerId); + } + + int getPointerId(int x, int y, int pid) { + //static Stopwatch w; + //w.printEvery(200, "IArea"); + //w.start(); + float fx = (float)x, fy = (float) y; + for (unsigned int i = 0; i < _areas.size(); ++i) { + const Area* a = _areas[i]; + if (a->area->isInside(fx, fy)) { + //w.stop(); + return a->areaId; + } + } + //w.stop(); + return pid; + } + + void addArea(int areaId, IArea* area) { + Area* a = new Area(); + a->area = area; + a->areaId = areaId; + + _areas.push_back( a ); + } + +private: + struct Area { + IArea* area; + int areaId; + }; + + std::vector _areas; +}; + +/// Area represented by a polygon +class PolygonArea: public IArea +{ +public: + PolygonArea(int numPoints, const float* xp, const float* yp) + : _numPoints(numPoints) + { + _x = new float[numPoints]; + _y = new float[numPoints]; + + for (int i = 0; i < numPoints; ++i) { + _x[i] = xp[i]; + _y[i] = yp[i]; + } + } + + ~PolygonArea() { + delete[] _x; + delete[] _y; + } + + virtual bool isInside(float x, float y) + { + bool c = false; + for (int i = 0, j = _numPoints-1; i < _numPoints; j = i++) { + if ((((_y[i] <= y) && (y < _y[j])) || + ((_y[j] <= y) && (y < _y[i]))) && + (x < (_x[j] - _x[i]) * (y - _y[i]) / (_y[j] - _y[i]) + _x[i])) + c = !c; + } + return c == 1; + } + +//private: + float* _x; + float* _y; + int _numPoints; +}; + +/// Area represented by a rectangle +class RectangleArea: public IArea +{ +public: + RectangleArea(float x0, float y0, float x1, float y1) + : _x0(x0), + _x1(x1), + _y0(y0), + _y1(y1) + {} + + virtual bool isInside(float x, float y) { + return x >= _x0 && x <= _x1 + && y >= _y0 && y <= _y1; + } + + virtual float centerX() + { + return _x0 + (_x1 - _x0) * .5f; + } + + virtual float centerY() + { + return _y0 + (_y1 - _y0) * .5f; + } + +//private: + float _x0, _x1; + float _y0, _y1; +}; + +/// An area represented by a circle +class CircleArea: public IArea +{ +public: + CircleArea(float x, float y, float r) + : _x(x), _y(y), _rr(r*r) + {} + + virtual bool isInside(float x, float y) { + const float dx = x - _x; + const float dy = y - _y; + return (dx*dx + dy*dy) <= _rr; + } + +private: + float _x, _y; + float _rr; +}; + +/// Inside any area in include list but none of exclude list +class IncludeExcludeArea: public IArea +{ +public: + IncludeExcludeArea() {} + + ~IncludeExcludeArea() { + clear(); + } + + void clear() { + if (deleteMe) { + for (unsigned int i = 0; i < _includes.size(); ++i) { + IArea* area = _includes[i]; + if (area->deleteMe) delete area; + } + for (unsigned int i = 0; i < _excludes.size(); ++i) { + IArea* area = _excludes[i]; + if (area->deleteMe) delete area; + } + } + _includes.clear(); + _excludes.clear(); + } + + void include(IArea* area) { _includes.push_back(area); } + void exclude(IArea* area) { _excludes.push_back(area); } + + virtual bool isInside(float x, float y) { + for (unsigned int i = 0; i < _includes.size(); ++i) + if (_includes[i]->isInside(x, y)) { + bool good = true; + //@todo: cache _exclude-test results + for (unsigned int j = 0; j < _excludes.size(); ++j) + if (_excludes[j]->isInside(x, y)) { + good = false; + break; + } + if (good) { + return true; + } + } + return false; + } + +private: + std::vector _includes; + std::vector _excludes; +}; + + +/// At least one of the areas +class OrArea: public IArea +{ +public: + OrArea(IArea* a, IArea* b) + : _a(a), + _b(b) + {} + + ~OrArea() { + if (deleteMe) { + if (_a->deleteMe) delete _a; + if (_b->deleteMe) delete _b; + } + } + + virtual bool isInside(float x, float y) { + return _a->isInside(x, y) || _b->isInside(x, y); + } + +private: + IArea* _a; + IArea* _b; +}; + +/// In both areas +class AndArea: public IArea +{ +public: + AndArea(IArea* a, IArea* b) + : _a(a), + _b(b) + {} + + ~AndArea() { + if (deleteMe) { + if (_a->deleteMe) delete _a; + if (_b->deleteMe) delete _b; + } + } + + virtual bool isInside(float x, float y) { + return _a->isInside(x, y) && _b->isInside(x, y); + } + +private: + IArea* _a; + IArea* _b; +}; + +/// Exactly one of the areas +class XorArea: public IArea +{ +public: + XorArea(IArea* a, IArea* b) + : _a(a), + _b(b) + {} + + ~XorArea() { + if (deleteMe) { + if (_a->deleteMe) delete _a; + if (_b->deleteMe) delete _b; + } + } + + virtual bool isInside(float x, float y) { + return _a->isInside(x, y) ^ _b->isInside(x, y); + } + +private: + IArea* _a; + IArea* _b; +}; + +/// Everything except this area +class NotArea: public IArea +{ +public: + NotArea(IArea* a) + : _a(a) + {} + + ~NotArea() { + if (deleteMe && _a->deleteMe) delete _a; + } + + virtual bool isInside(float x, float y) { + return !_a->isInside(x, y); + } + +private: + IArea* _a; +}; + +/// First area, but not second +/// This is semantically the same as AndArea( a, new NotArea(b) ) +class DifferenceArea: public IArea +{ +public: + DifferenceArea(IArea* a, IArea* b) + : _a(a), + _b(b) + {} + + ~DifferenceArea() { + if (deleteMe) { + if (_a->deleteMe) delete _a; + if (_b->deleteMe) delete _b; + } + } + + virtual bool isInside(float x, float y) { + return _a->isInside(x, y) && !_b->isInside(x, y); + } +private: + IArea* _a; + IArea* _b; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER__TouchAreaModel_H__*/ diff --git a/src/client/player/input/touchscreen/TouchInputHolder.h b/src/client/player/input/touchscreen/TouchInputHolder.h index afda575..9418a66 100755 --- a/src/client/player/input/touchscreen/TouchInputHolder.h +++ b/src/client/player/input/touchscreen/TouchInputHolder.h @@ -1,471 +1,471 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchInputHolder_H__ -#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchInputHolder_H__ - -#include "../IInputHolder.h" -#include "TouchscreenInput.h" - -#include "../ITurnInput.h" -#include "TouchAreaModel.h" -#include "../../../../platform/input/Multitouch.h" -#include "../../../../platform/time.h" -#include "../../../../util/SmoothFloat.h" - -#include "../../../../world/entity/player/Player.h" -#include "../../../../world/entity/player/Inventory.h" - -template -class ModifyNotify { -public: - ModifyNotify() - : _changed(false) - {} - - T& getOld() { return _old; } - T& getNew() { return _new; } - - bool update(T& val) { - _changed = !equals(val); - if (_changed) { - _old = _new; - _new = val; - } - return _changed; - } - - bool hasChanged() const { return _changed; } - - virtual bool equals(const T& newVal) { - return _old != newVal; - } -protected: - T _old; -private: - T _new; - bool _changed; -}; - -// -// Implementation for unified [Turn & Build Input] -// -class UnifiedTurnBuild: public GuiComponent, - public ITurnInput, - public IBuildInput -{ -public: - static const int MODE_OFFSET = 1; - static const int MODE_DELTA = 2; - - UnifiedTurnBuild(int turnMode, int width, int height, float maxMovementDelta, float sensitivity, IInputHolder* holder, Minecraft* minecraft) - : mode(turnMode), - _holder(holder), - _options(&minecraft->options), - cxO(0), cyO(0), - wasActive(false), - _totalMoveDelta(0), - _maxMovement(maxMovementDelta), - _lastPlayer(0), - screenArea(-1, -1, 0, 0), - allowPicking(false), - state(State_None), - moveArea(-1,-1,0,0), - joyTouchArea(-1, -1, 0, 0), - inventoryArea(-1,-1, 0, 0), - pauseArea(-1, -1, 0, 0), - _buildMovement(0), - _sentFirstRemove(false), - _canDestroy(false), - _forceCanUse(false) - { - _area.deleteMe = false; - setSensitivity(sensitivity); - //((ITurnInput*)this)->onConfigChanged(createConfig(minecraft)); - onConfigChanged(createConfig(minecraft)); - - _lastBuildDownTime = _startTurnTime = getTimeS(); - } - - void setSensitivity(float sensitivity) { - _sensitivity = sensitivity; - } - - virtual void onConfigChanged(const Config& c) { - if (false && _options->getBooleanValue(OPTIONS_IS_JOY_TOUCH_AREA)) { - int touchWidth = c.width - (int)inventoryArea._x1; - if (touchWidth > (int)c.minecraft->pixelCalc.millimetersToPixels(60)) - touchWidth = (int)c.minecraft->pixelCalc.millimetersToPixels(60); - - int touchHeight = (int)(c.height * 0.4f); - if (touchHeight > (int)c.minecraft->pixelCalc.millimetersToPixels(40)) - touchHeight = (int)c.minecraft->pixelCalc.millimetersToPixels(40); - - joyTouchArea._x0 = (float)(c.width - touchWidth); - joyTouchArea._y0 = (float)(c.height - touchHeight); - joyTouchArea._x1 = (float)c.width; - joyTouchArea._y1 = (float)c.height; - - _area.clear(); - _area.include (&joyTouchArea); - _model.clear(); - _model.addArea(AREA_TURN, &_area); - } else { - screenArea = RectangleArea(0, 0, (float)c.width, (float)c.height); - // Expand the move area a bit - const float border = 10; - const float widen = (moveArea._x1-moveArea._x0) * 0.05f + border; // ~5% wider - moveArea._x0 -= widen; - moveArea._x1 += widen; - const float heighten = (moveArea._y1-moveArea._y0) * 0.05f + border; // ~5% taller - moveArea._y0 -= heighten; - moveArea._y1 += heighten; - - pauseArea._x0 -= border; - pauseArea._x1 += border; - pauseArea._y0 -= border; - pauseArea._y1 += border; - - //LOGI("move: %f, %f, %f, %f\n", moveArea._x0, moveArea._y0, moveArea._x1, moveArea._y1); - - _area.clear(); - _area.include(&screenArea); - _area.exclude(&moveArea); - _area.exclude(&inventoryArea); -#ifdef __APPLE__ - _area.exclude(&pauseArea); -#endif /*__APPLE__*/ - //LOGI("Movearea: %f %f %f% f\n", moveArea._x0, moveArea._x1, moveArea._y0, moveArea._y1); - - _model.clear(); - _model.addArea(AREA_TURN, &_area); - } - } - - float calcNewAlpha(float current, float wanted) { - if (wanted > current) - return Mth::clamp(current + 0.02f, 0.0f, wanted); - if (wanted < current) - return Mth::clamp(current - 0.04f, wanted, 1.0f); - return current; - } - - // - // Implementation for the ITurnInput part - // - TurnDelta getTurnDelta() { - float dx = 0, dy = 0; - const float now = getTimeS(); - - float cx = 0; - float cy = 0; - bool isActive = false; - - const int* pointerIds; - bool wasFirstMovement = false; - int pointerCount = Multitouch::getActivePointerIds(&pointerIds); - for (int i = 0; i < pointerCount; ++i) { - int p = pointerIds[i]; - int x = Multitouch::getX(p); - int y = Multitouch::getY(p); - int areaId = _model.getPointerId(x, y, p); - - if (areaId == AREA_TURN) { - isActive = true; - cx = (float)x * 0.5f; - cy = (float)y * -0.5f; - wasFirstMovement = Multitouch::wasFirstMovement(p); - break; - } - } - if (isActive && !wasActive) { - _startTurnTime = now; - _totalMoveDelta = 0; - bool isInMovement = _lastPlayer? getSpeedSquared(_lastPlayer) > 0.01f/*.25f*/ : false; - //state = isInMovement? State_Turn : State_Deciding; - state = State_Deciding; - _canDestroy = !isInMovement; - _forceCanUse = false; - if (!_canDestroy && (_lastPlayer && _lastPlayer->canUseCarriedItemWhileMoving())) { - _forceCanUse = true; - _canDestroy = true; - } - _sentFirstRemove = false; - } else if (wasActive && !isActive) { - _sentFirstRemove = false; - state = State_None; - //_inBuild = false; - } - - if (MODE_DELTA == mode && (wasActive || isActive)) { -// const float dt = getDeltaTime(); -// const float MaxTurnX = 100.0f; -// const float MaxTurnY = 100.0f; - const float DeadZone = 0;//0.25f * dt;//0.02f; - - if (!wasActive) { - cxO = cx; - cyO = cy; - } - if (isActive) { - dx = _sensitivity * linearTransform(cx - cxO, DeadZone);// * MaxTurnX; - dy = _sensitivity * linearTransform(cy - cyO, DeadZone);// * MaxTurnY; - - float moveDelta = ( Mth::abs(dx) + Mth::abs(dy) ); - //LOGI("moveDelta is : %f\n", moveDelta); - - if (moveDelta > _maxMovement) { - dx = 0; - dy = 0; - moveDelta = 0; - } - _totalMoveDelta += moveDelta; - - if (state == State_Deciding && _totalMoveDelta >= MaxBuildMovement/* && !_forceCanUse*/) - state = State_Turn; - - // If a certain time has passed since we pressed button; check if - // we've moved enough to prevent removal state to become activated - const float since = now - _startTurnTime; - if (state == State_Deciding && (since >= (0.001f*RemovalMilliseconds))) { - //LOGI("DECIDED!: %f\n", _totalMoveDelta); - bool isInMovement = _lastPlayer? getSpeedSquared(_lastPlayer) > 0.01f/*.25f*/ : false; - if (!_forceCanUse && (_totalMoveDelta > 20.0f || isInMovement)) { - state = State_Turn; - } else { - state = State_Destroy; - _canDestroy = true; - } - } - - // Removing the pointer ketchup-effect that's built into some devices - if (wasFirstMovement) { - dx = dy = 0; - } - - cxO = cx; - cyO = cy; - //LOGI("move delta: %f (%f, %f), %d\n", moveDelta, dx, dy, state); - } - } else { - // Wasn't active, and isn't active! - state = State_None; - } - - updateFeedbackProgressAlpha(now); - - wasActive = isActive; - return TurnDelta(dx, -dy); - } - - void updateFeedbackProgressAlpha(float now) { - const float MinAlphaValue = -0.05f; - if (_canDestroy) { - // Hack to test out fading in feedback circle - const float since = now - _startTurnTime; - if (state == State_Deciding) { - const float wantedAlpha = since / (0.001f*RemovalMilliseconds); - _holder->alpha = wantedAlpha * wantedAlpha; - } else { - if (state == State_Destroy) { - _holder->alpha = calcNewAlpha(_holder->alpha, 1); - } else if (state == State_Turn || state == State_None) { - _holder->alpha = calcNewAlpha(_holder->alpha, 0); - } - - } - } else { - _holder->alpha = MinAlphaValue; - } - //LOGI("state: %d, canDestroy: %d %d\n", state, _canDestroy, _forceCanUse); - //LOGI("alpha: %f\n", _holder->alpha); - } - - bool isInsideArea(float x, float y) { - return _area.isInside(x, y); - } - - int mode; - - static float getSpeedSquared(Entity* m) { - const float xd = m->x - m->xo; - const float yd = m->y - m->yo; - const float zd = m->z - m->zo; - const float speedSquared = xd*xd + yd*yd + zd*zd; - return speedSquared; - } - - void render(float alpha) { - if (_options->getBooleanValue(OPTIONS_IS_JOY_TOUCH_AREA)) { - fill( (int) (Gui::InvGuiScale * joyTouchArea._x0), - (int) (Gui::InvGuiScale * joyTouchArea._y0), - (int) (Gui::InvGuiScale * joyTouchArea._x1), - (int) (Gui::InvGuiScale * joyTouchArea._y1), 0x40000000);//0x20ffffff); - } - } - - // - // Implementation for the IBuildInput part - // - virtual bool tickBuild(Player* player, BuildActionIntention* bai) { - _lastPlayer = player; - - if (state == State_Destroy) { - if (!_sentFirstRemove) { - *bai = BuildActionIntention((_forceCanUse?0:BuildActionIntention::BAI_FIRSTREMOVE) | BuildActionIntention::BAI_INTERACT); - _sentFirstRemove = true; - } else { - *bai = BuildActionIntention((_forceCanUse?0:BuildActionIntention::BAI_REMOVE) | BuildActionIntention::BAI_INTERACT); - } - return true; - } - - // Rewind the queue, in case someone has used it up before - Multitouch::rewind(); - const float now = getTimeS(); - allowPicking = false; - bool handled = false; - - while (Multitouch::next()) { - MouseAction& m = Multitouch::getEvent(); - if (m.action == MouseAction::ACTION_MOVE) continue; - - int areaId = _model.getPointerId(m.x, m.y, m.pointerId); - if (areaId != AREA_TURN) continue; - - allowPicking = true; - //LOGI("down? %d, up? %d (%d, %d)\n", z && (m.data == MouseAction::DATA_DOWN), z && (m.data == MouseAction::DATA_UP), m.x, m.y); - - if (_totalMoveDelta <= MaxBuildMovement && (m.data == MouseAction::DATA_UP && !handled)) { - const float since = now - _lastBuildDownTime; - //LOGI("move: (%d) %.2f - %f\n", state, _totalMoveDelta, since); - if (state <= State_Deciding) { - if (since >= 0.0f && since < 0.25f) { - // We've pressed and released in haste; Let's build! - *bai = BuildActionIntention(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_ATTACK); - handled = true; - } - } - state = State_None; - } else if (m.data == MouseAction::DATA_DOWN) { - _lastBuildDownTime = now; - _buildMovement = 0; - state = State_Deciding; - } - } - return handled; - } - - bool allowPicking; - float alpha; - SmoothFloat smoothAlpha; - - RectangleArea screenArea; - RectangleArea moveArea; - RectangleArea joyTouchArea; - RectangleArea inventoryArea; - RectangleArea pauseArea; - -private: - IInputHolder* _holder; - - // Turn - int cid; - float cxO, cyO; - bool wasActive; - - TouchAreaModel _model; - IncludeExcludeArea _area; - - bool _decidedTurnMode; - - float _startTurnTime; - float _totalMoveDelta; - float _maxMovement; - float _sensitivity; - - Player* _lastPlayer; - - // Build - float _lastBuildDownTime; - float _buildMovement; - bool _sentFirstRemove; - bool _canDestroy; - bool _forceCanUse; - - int state; - static const int State_None = 0; - static const int State_Deciding = 1; - static const int State_Turn = 2; - static const int State_Destroy = 3; - static const int State_Build = 4; // Will never happen - - static const int MaxBuildMovement = 20; - static const int RemovalMilliseconds = 400; - - static const int AREA_TURN = 100; - Options* _options; -}; - -class Minecraft; - -#if defined(_MSC_VER) - #pragma warning( disable : 4355 ) // 'this' pointer in initialization list which is perfectly legal -#endif - -class TouchInputHolder: public IInputHolder -{ -public: - TouchInputHolder(Minecraft* mc, Options* options) - : _mc(mc), - _move(mc, options), - _turnBuild(UnifiedTurnBuild::MODE_DELTA, mc->width, mc->height, (float)MovementLimit, 1, this, mc) - { - onConfigChanged(createConfig(mc)); - } - ~TouchInputHolder() { - } - - virtual void onConfigChanged(const Config& c) { - _move.onConfigChanged(c); - _turnBuild.moveArea = _move.getRectangleArea(); - _turnBuild.pauseArea = _move.getPauseRectangleArea(); - _turnBuild.inventoryArea = _mc->gui.getRectangleArea( _mc->options.getBooleanValue(OPTIONS_IS_LEFT_HANDED)? 1 : -1 ); - _turnBuild.setSensitivity(c.options->getBooleanValue(OPTIONS_IS_JOY_TOUCH_AREA)? 1.8f : 1.0f); - ((ITurnInput*)&_turnBuild)->onConfigChanged(c); - } - - virtual bool allowPicking() { - const int* pointerIds; - int pointerCount = Multitouch::getActivePointerIds(&pointerIds); - for (int i = 0; i < pointerCount; ++i) { - int p = pointerIds[i]; - const float x = Multitouch::getX(p); - const float y = Multitouch::getY(p); - - if (_turnBuild.isInsideArea(x, y)) { - mousex = x; - mousey = y; - return true; - } - } - - return false; - // return _turnBuild.allowPicking; - } - - virtual void render(float alpha) { - _turnBuild.render(alpha); - } - - virtual IMoveInput* getMoveInput() { return &_move; } - virtual ITurnInput* getTurnInput() { return &_turnBuild; } - virtual IBuildInput* getBuildInput() { return &_turnBuild; } - -private: - TouchscreenInput_TestFps _move; - UnifiedTurnBuild _turnBuild; - - Minecraft* _mc; - - static const int MovementLimit = 200; // per update -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchInputHolder_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchInputHolder_H__ +#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchInputHolder_H__ + +#include "../IInputHolder.h" +#include "TouchscreenInput.h" + +#include "../ITurnInput.h" +#include "TouchAreaModel.h" +#include "../../../../platform/input/Multitouch.h" +#include "../../../../platform/time.h" +#include "../../../../util/SmoothFloat.h" + +#include "../../../../world/entity/player/Player.h" +#include "../../../../world/entity/player/Inventory.h" + +template +class ModifyNotify { +public: + ModifyNotify() + : _changed(false) + {} + + T& getOld() { return _old; } + T& getNew() { return _new; } + + bool update(T& val) { + _changed = !equals(val); + if (_changed) { + _old = _new; + _new = val; + } + return _changed; + } + + bool hasChanged() const { return _changed; } + + virtual bool equals(const T& newVal) { + return _old != newVal; + } +protected: + T _old; +private: + T _new; + bool _changed; +}; + +// +// Implementation for unified [Turn & Build Input] +// +class UnifiedTurnBuild: public GuiComponent, + public ITurnInput, + public IBuildInput +{ +public: + static const int MODE_OFFSET = 1; + static const int MODE_DELTA = 2; + + UnifiedTurnBuild(int turnMode, int width, int height, float maxMovementDelta, float sensitivity, IInputHolder* holder, Minecraft* minecraft) + : mode(turnMode), + _holder(holder), + _options(&minecraft->options), + cxO(0), cyO(0), + wasActive(false), + _totalMoveDelta(0), + _maxMovement(maxMovementDelta), + _lastPlayer(0), + screenArea(-1, -1, 0, 0), + allowPicking(false), + state(State_None), + moveArea(-1,-1,0,0), + joyTouchArea(-1, -1, 0, 0), + inventoryArea(-1,-1, 0, 0), + pauseArea(-1, -1, 0, 0), + _buildMovement(0), + _sentFirstRemove(false), + _canDestroy(false), + _forceCanUse(false) + { + _area.deleteMe = false; + setSensitivity(sensitivity); + //((ITurnInput*)this)->onConfigChanged(createConfig(minecraft)); + onConfigChanged(createConfig(minecraft)); + + _lastBuildDownTime = _startTurnTime = getTimeS(); + } + + void setSensitivity(float sensitivity) { + _sensitivity = sensitivity; + } + + virtual void onConfigChanged(const Config& c) { + if (false && _options->getBooleanValue(OPTIONS_IS_JOY_TOUCH_AREA)) { + int touchWidth = c.width - (int)inventoryArea._x1; + if (touchWidth > (int)c.minecraft->pixelCalc.millimetersToPixels(60)) + touchWidth = (int)c.minecraft->pixelCalc.millimetersToPixels(60); + + int touchHeight = (int)(c.height * 0.4f); + if (touchHeight > (int)c.minecraft->pixelCalc.millimetersToPixels(40)) + touchHeight = (int)c.minecraft->pixelCalc.millimetersToPixels(40); + + joyTouchArea._x0 = (float)(c.width - touchWidth); + joyTouchArea._y0 = (float)(c.height - touchHeight); + joyTouchArea._x1 = (float)c.width; + joyTouchArea._y1 = (float)c.height; + + _area.clear(); + _area.include (&joyTouchArea); + _model.clear(); + _model.addArea(AREA_TURN, &_area); + } else { + screenArea = RectangleArea(0, 0, (float)c.width, (float)c.height); + // Expand the move area a bit + const float border = 10; + const float widen = (moveArea._x1-moveArea._x0) * 0.05f + border; // ~5% wider + moveArea._x0 -= widen; + moveArea._x1 += widen; + const float heighten = (moveArea._y1-moveArea._y0) * 0.05f + border; // ~5% taller + moveArea._y0 -= heighten; + moveArea._y1 += heighten; + + pauseArea._x0 -= border; + pauseArea._x1 += border; + pauseArea._y0 -= border; + pauseArea._y1 += border; + + //LOGI("move: %f, %f, %f, %f\n", moveArea._x0, moveArea._y0, moveArea._x1, moveArea._y1); + + _area.clear(); + _area.include(&screenArea); + _area.exclude(&moveArea); + _area.exclude(&inventoryArea); +#ifdef __APPLE__ + _area.exclude(&pauseArea); +#endif /*__APPLE__*/ + //LOGI("Movearea: %f %f %f% f\n", moveArea._x0, moveArea._x1, moveArea._y0, moveArea._y1); + + _model.clear(); + _model.addArea(AREA_TURN, &_area); + } + } + + float calcNewAlpha(float current, float wanted) { + if (wanted > current) + return Mth::clamp(current + 0.02f, 0.0f, wanted); + if (wanted < current) + return Mth::clamp(current - 0.04f, wanted, 1.0f); + return current; + } + + // + // Implementation for the ITurnInput part + // + TurnDelta getTurnDelta() { + float dx = 0, dy = 0; + const float now = getTimeS(); + + float cx = 0; + float cy = 0; + bool isActive = false; + + const int* pointerIds; + bool wasFirstMovement = false; + int pointerCount = Multitouch::getActivePointerIds(&pointerIds); + for (int i = 0; i < pointerCount; ++i) { + int p = pointerIds[i]; + int x = Multitouch::getX(p); + int y = Multitouch::getY(p); + int areaId = _model.getPointerId(x, y, p); + + if (areaId == AREA_TURN) { + isActive = true; + cx = (float)x * 0.5f; + cy = (float)y * -0.5f; + wasFirstMovement = Multitouch::wasFirstMovement(p); + break; + } + } + if (isActive && !wasActive) { + _startTurnTime = now; + _totalMoveDelta = 0; + bool isInMovement = _lastPlayer? getSpeedSquared(_lastPlayer) > 0.01f/*.25f*/ : false; + //state = isInMovement? State_Turn : State_Deciding; + state = State_Deciding; + _canDestroy = !isInMovement; + _forceCanUse = false; + if (!_canDestroy && (_lastPlayer && _lastPlayer->canUseCarriedItemWhileMoving())) { + _forceCanUse = true; + _canDestroy = true; + } + _sentFirstRemove = false; + } else if (wasActive && !isActive) { + _sentFirstRemove = false; + state = State_None; + //_inBuild = false; + } + + if (MODE_DELTA == mode && (wasActive || isActive)) { +// const float dt = getDeltaTime(); +// const float MaxTurnX = 100.0f; +// const float MaxTurnY = 100.0f; + const float DeadZone = 0;//0.25f * dt;//0.02f; + + if (!wasActive) { + cxO = cx; + cyO = cy; + } + if (isActive) { + dx = _sensitivity * linearTransform(cx - cxO, DeadZone);// * MaxTurnX; + dy = _sensitivity * linearTransform(cy - cyO, DeadZone);// * MaxTurnY; + + float moveDelta = ( Mth::abs(dx) + Mth::abs(dy) ); + //LOGI("moveDelta is : %f\n", moveDelta); + + if (moveDelta > _maxMovement) { + dx = 0; + dy = 0; + moveDelta = 0; + } + _totalMoveDelta += moveDelta; + + if (state == State_Deciding && _totalMoveDelta >= MaxBuildMovement/* && !_forceCanUse*/) + state = State_Turn; + + // If a certain time has passed since we pressed button; check if + // we've moved enough to prevent removal state to become activated + const float since = now - _startTurnTime; + if (state == State_Deciding && (since >= (0.001f*RemovalMilliseconds))) { + //LOGI("DECIDED!: %f\n", _totalMoveDelta); + bool isInMovement = _lastPlayer? getSpeedSquared(_lastPlayer) > 0.01f/*.25f*/ : false; + if (!_forceCanUse && (_totalMoveDelta > 20.0f || isInMovement)) { + state = State_Turn; + } else { + state = State_Destroy; + _canDestroy = true; + } + } + + // Removing the pointer ketchup-effect that's built into some devices + if (wasFirstMovement) { + dx = dy = 0; + } + + cxO = cx; + cyO = cy; + //LOGI("move delta: %f (%f, %f), %d\n", moveDelta, dx, dy, state); + } + } else { + // Wasn't active, and isn't active! + state = State_None; + } + + updateFeedbackProgressAlpha(now); + + wasActive = isActive; + return TurnDelta(dx, -dy); + } + + void updateFeedbackProgressAlpha(float now) { + const float MinAlphaValue = -0.05f; + if (_canDestroy) { + // Hack to test out fading in feedback circle + const float since = now - _startTurnTime; + if (state == State_Deciding) { + const float wantedAlpha = since / (0.001f*RemovalMilliseconds); + _holder->alpha = wantedAlpha * wantedAlpha; + } else { + if (state == State_Destroy) { + _holder->alpha = calcNewAlpha(_holder->alpha, 1); + } else if (state == State_Turn || state == State_None) { + _holder->alpha = calcNewAlpha(_holder->alpha, 0); + } + + } + } else { + _holder->alpha = MinAlphaValue; + } + //LOGI("state: %d, canDestroy: %d %d\n", state, _canDestroy, _forceCanUse); + //LOGI("alpha: %f\n", _holder->alpha); + } + + bool isInsideArea(float x, float y) { + return _area.isInside(x, y); + } + + int mode; + + static float getSpeedSquared(Entity* m) { + const float xd = m->x - m->xo; + const float yd = m->y - m->yo; + const float zd = m->z - m->zo; + const float speedSquared = xd*xd + yd*yd + zd*zd; + return speedSquared; + } + + void render(float alpha) { + if (_options->getBooleanValue(OPTIONS_IS_JOY_TOUCH_AREA)) { + fill( (int) (Gui::InvGuiScale * joyTouchArea._x0), + (int) (Gui::InvGuiScale * joyTouchArea._y0), + (int) (Gui::InvGuiScale * joyTouchArea._x1), + (int) (Gui::InvGuiScale * joyTouchArea._y1), 0x40000000);//0x20ffffff); + } + } + + // + // Implementation for the IBuildInput part + // + virtual bool tickBuild(Player* player, BuildActionIntention* bai) { + _lastPlayer = player; + + if (state == State_Destroy) { + if (!_sentFirstRemove) { + *bai = BuildActionIntention((_forceCanUse?0:BuildActionIntention::BAI_FIRSTREMOVE) | BuildActionIntention::BAI_INTERACT); + _sentFirstRemove = true; + } else { + *bai = BuildActionIntention((_forceCanUse?0:BuildActionIntention::BAI_REMOVE) | BuildActionIntention::BAI_INTERACT); + } + return true; + } + + // Rewind the queue, in case someone has used it up before + Multitouch::rewind(); + const float now = getTimeS(); + allowPicking = false; + bool handled = false; + + while (Multitouch::next()) { + MouseAction& m = Multitouch::getEvent(); + if (m.action == MouseAction::ACTION_MOVE) continue; + + int areaId = _model.getPointerId(m.x, m.y, m.pointerId); + if (areaId != AREA_TURN) continue; + + allowPicking = true; + //LOGI("down? %d, up? %d (%d, %d)\n", z && (m.data == MouseAction::DATA_DOWN), z && (m.data == MouseAction::DATA_UP), m.x, m.y); + + if (_totalMoveDelta <= MaxBuildMovement && (m.data == MouseAction::DATA_UP && !handled)) { + const float since = now - _lastBuildDownTime; + //LOGI("move: (%d) %.2f - %f\n", state, _totalMoveDelta, since); + if (state <= State_Deciding) { + if (since >= 0.0f && since < 0.25f) { + // We've pressed and released in haste; Let's build! + *bai = BuildActionIntention(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_ATTACK); + handled = true; + } + } + state = State_None; + } else if (m.data == MouseAction::DATA_DOWN) { + _lastBuildDownTime = now; + _buildMovement = 0; + state = State_Deciding; + } + } + return handled; + } + + bool allowPicking; + float alpha; + SmoothFloat smoothAlpha; + + RectangleArea screenArea; + RectangleArea moveArea; + RectangleArea joyTouchArea; + RectangleArea inventoryArea; + RectangleArea pauseArea; + +private: + IInputHolder* _holder; + + // Turn + int cid; + float cxO, cyO; + bool wasActive; + + TouchAreaModel _model; + IncludeExcludeArea _area; + + bool _decidedTurnMode; + + float _startTurnTime; + float _totalMoveDelta; + float _maxMovement; + float _sensitivity; + + Player* _lastPlayer; + + // Build + float _lastBuildDownTime; + float _buildMovement; + bool _sentFirstRemove; + bool _canDestroy; + bool _forceCanUse; + + int state; + static const int State_None = 0; + static const int State_Deciding = 1; + static const int State_Turn = 2; + static const int State_Destroy = 3; + static const int State_Build = 4; // Will never happen + + static const int MaxBuildMovement = 20; + static const int RemovalMilliseconds = 400; + + static const int AREA_TURN = 100; + Options* _options; +}; + +class Minecraft; + +#if defined(_MSC_VER) + #pragma warning( disable : 4355 ) // 'this' pointer in initialization list which is perfectly legal +#endif + +class TouchInputHolder: public IInputHolder +{ +public: + TouchInputHolder(Minecraft* mc, Options* options) + : _mc(mc), + _move(mc, options), + _turnBuild(UnifiedTurnBuild::MODE_DELTA, mc->width, mc->height, (float)MovementLimit, 1, this, mc) + { + onConfigChanged(createConfig(mc)); + } + ~TouchInputHolder() { + } + + virtual void onConfigChanged(const Config& c) { + _move.onConfigChanged(c); + _turnBuild.moveArea = _move.getRectangleArea(); + _turnBuild.pauseArea = _move.getPauseRectangleArea(); + _turnBuild.inventoryArea = _mc->gui.getRectangleArea( _mc->options.getBooleanValue(OPTIONS_IS_LEFT_HANDED)? 1 : -1 ); + _turnBuild.setSensitivity(c.options->getBooleanValue(OPTIONS_IS_JOY_TOUCH_AREA)? 1.8f : 1.0f); + ((ITurnInput*)&_turnBuild)->onConfigChanged(c); + } + + virtual bool allowPicking() { + const int* pointerIds; + int pointerCount = Multitouch::getActivePointerIds(&pointerIds); + for (int i = 0; i < pointerCount; ++i) { + int p = pointerIds[i]; + const float x = Multitouch::getX(p); + const float y = Multitouch::getY(p); + + if (_turnBuild.isInsideArea(x, y)) { + mousex = x; + mousey = y; + return true; + } + } + + return false; + // return _turnBuild.allowPicking; + } + + virtual void render(float alpha) { + _turnBuild.render(alpha); + } + + virtual IMoveInput* getMoveInput() { return &_move; } + virtual ITurnInput* getTurnInput() { return &_turnBuild; } + virtual IBuildInput* getBuildInput() { return &_turnBuild; } + +private: + TouchscreenInput_TestFps _move; + UnifiedTurnBuild _turnBuild; + + Minecraft* _mc; + + static const int MovementLimit = 200; // per update +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchInputHolder_H__*/ diff --git a/src/client/player/input/touchscreen/TouchTurnInput.h b/src/client/player/input/touchscreen/TouchTurnInput.h index eaa2e96..4c9a31f 100755 --- a/src/client/player/input/touchscreen/TouchTurnInput.h +++ b/src/client/player/input/touchscreen/TouchTurnInput.h @@ -1,88 +1,88 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchTurnInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchTurnInput_H__ - -#include "../ITurnInput.h" -#include "TouchAreaModel.h" -#include "../../../../platform/input/Multitouch.h" - -class TouchTurnInput: public ITurnInput -{ -public: - static const int MODE_OFFSET = 1; - static const int MODE_DELTA = 2; - - TouchTurnInput(int mode_) - : mode(mode_), - cxO(0), cyO(0), - wasActive(false) - { - } - - TurnDelta getTurnDelta() { - float dx = 0, dy = 0; - - float cx = 0; - float cy = 0; - bool isActive = false; - - const int* pointerIds; - int pointerCount = Multitouch::getActivePointerIds(&pointerIds); - for (int i = 0; i < pointerCount; ++i) { - int p = pointerIds[i]; - int x = Multitouch::getX(p); - int y = Multitouch::getY(p); - int areaId = _model.getPointerId(x, y, p); - - if (areaId == AREA_TURN) { - isActive = true; - cx = (float)x * 0.5f; - cy = (float)y * -0.5f; - break; - } - } - - if (MODE_OFFSET == mode) { - //float dt = getDeltaTime(); - //const float MaxTurnX = 250.0f; - //const float MaxTurnY = 200.0f; - //float cx = isActive? Controller::getX(cid) : cxO * 0.7f; - //float cy = isActive? Controller::getY(cid) : cyO * 0.7f; - //dx = linearTransform( cx, 0.1f, MaxTurnX ) * dt; - //dy = linearTransform( cy, 0.1f, MaxTurnY ) * dt; - //cxO = cx; - //cyO = cy; - } else - if (MODE_DELTA == mode && (wasActive || isActive)) { -// const float dt = getDeltaTime(); -// const float MaxTurnX = 100.0f; -// const float MaxTurnY = 100.0f; - const float DeadZone = 0;//0.25f * dt;//0.02f; - - if (!wasActive) { - cxO = cx; - cyO = cy; - } - if (isActive) { - dx = linearTransform(cx - cxO, DeadZone);// * MaxTurnX; - dy = linearTransform(cy - cyO, DeadZone);// * MaxTurnY; - cxO = cx; - cyO = cy; - } - } - - wasActive = isActive; - return TurnDelta(dx, -dy); - } - - int mode; -private: - int cid; - float cxO, cyO; - bool wasActive; - - TouchAreaModel _model; - - static const int AREA_TURN = 100; -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchTurnInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchTurnInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchTurnInput_H__ + +#include "../ITurnInput.h" +#include "TouchAreaModel.h" +#include "../../../../platform/input/Multitouch.h" + +class TouchTurnInput: public ITurnInput +{ +public: + static const int MODE_OFFSET = 1; + static const int MODE_DELTA = 2; + + TouchTurnInput(int mode_) + : mode(mode_), + cxO(0), cyO(0), + wasActive(false) + { + } + + TurnDelta getTurnDelta() { + float dx = 0, dy = 0; + + float cx = 0; + float cy = 0; + bool isActive = false; + + const int* pointerIds; + int pointerCount = Multitouch::getActivePointerIds(&pointerIds); + for (int i = 0; i < pointerCount; ++i) { + int p = pointerIds[i]; + int x = Multitouch::getX(p); + int y = Multitouch::getY(p); + int areaId = _model.getPointerId(x, y, p); + + if (areaId == AREA_TURN) { + isActive = true; + cx = (float)x * 0.5f; + cy = (float)y * -0.5f; + break; + } + } + + if (MODE_OFFSET == mode) { + //float dt = getDeltaTime(); + //const float MaxTurnX = 250.0f; + //const float MaxTurnY = 200.0f; + //float cx = isActive? Controller::getX(cid) : cxO * 0.7f; + //float cy = isActive? Controller::getY(cid) : cyO * 0.7f; + //dx = linearTransform( cx, 0.1f, MaxTurnX ) * dt; + //dy = linearTransform( cy, 0.1f, MaxTurnY ) * dt; + //cxO = cx; + //cyO = cy; + } else + if (MODE_DELTA == mode && (wasActive || isActive)) { +// const float dt = getDeltaTime(); +// const float MaxTurnX = 100.0f; +// const float MaxTurnY = 100.0f; + const float DeadZone = 0;//0.25f * dt;//0.02f; + + if (!wasActive) { + cxO = cx; + cyO = cy; + } + if (isActive) { + dx = linearTransform(cx - cxO, DeadZone);// * MaxTurnX; + dy = linearTransform(cy - cyO, DeadZone);// * MaxTurnY; + cxO = cx; + cyO = cy; + } + } + + wasActive = isActive; + return TurnDelta(dx, -dy); + } + + int mode; +private: + int cid; + float cxO, cyO; + bool wasActive; + + TouchAreaModel _model; + + static const int AREA_TURN = 100; +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchTurnInput_H__*/ diff --git a/src/client/player/input/touchscreen/TouchscreenInput.cpp b/src/client/player/input/touchscreen/TouchscreenInput.cpp index 37cb6d8..186e307 100755 --- a/src/client/player/input/touchscreen/TouchscreenInput.cpp +++ b/src/client/player/input/touchscreen/TouchscreenInput.cpp @@ -1,524 +1,524 @@ -#include "TouchscreenInput.h" -#include "../../../Options.h" -#include "../../../../platform/input/Multitouch.h" -#include "../../../gui/Gui.h" -#include "../../../renderer/Tesselator.h" -#include "../../../../world/entity/player/Player.h" - -#include "../../../Minecraft.h" -#include "../../../../platform/log.h" -#include "../../../renderer/Textures.h" -#include "../../../sound/SoundEngine.h" -#include "client/gui/screens/ScreenChooser.h" - - -// ARGHHHHHH WHY NOT FUCKING ENUM -static const int AREA_DPAD_FIRST = 100; -static const int AREA_DPAD_N = 100; -static const int AREA_DPAD_S = 101; -static const int AREA_DPAD_W = 102; -static const int AREA_DPAD_E = 103; -static const int AREA_DPAD_C = 104; -static const int AREA_PAUSE = 105; -static const int AREA_CHAT = 106; - -static int cPressed = 0; -static int cReleased = 0; -static int cDiscreet = 0; -static int cPressedPause = 0; -static int cReleasedPause = 0; -//static const int AREA_DPAD_N_JUMP = 105; - -// -// TouchscreenInput_TestFps -// - -static void Copy(int n, float* x, float* y, float* dx, float* dy) { - for (int i = 0; i < n; ++i) { - dx[i] = x[i]; - dy[i] = y[i]; - } -} - -static void Translate(int n, float* x, float* y, float xt, float yt) { - for (int i = 0; i < n; ++i) { - x[i] += xt; - y[i] += yt; - } -} - -static void Scale(int n, float* x, float* y, float xt, float yt) { - for (int i = 0; i < n; ++i) { - x[i] *= xt; - y[i] *= yt; - } -} - -static void Transformed(int n, float* x, float* y, float* dx, float* dy, float xt, float yt, float sx=1.0f, float sy=1.0f) { - Copy(n, x, y, dx, dy); - Scale(n, dx, dy, sx, sy); - Translate(n, dx, dy, xt, yt); - - //for (int i = 0; i < n; ++i) { - // LOGI("%d. (%f, %f)\n", i, dx[i], dy[i]); - //} -} - -TouchscreenInput_TestFps::TouchscreenInput_TestFps( Minecraft* mc, Options* options ) -: _minecraft(mc), - _options(options), - _northJump(false), - _forward(false), - _boundingRectangle(0, 0, 1, 1), - _pressedJump(false), - _pauseIsDown(false), - _sneakTapTime(-999), - aLeft(0), - aRight(0), - aUp(0), - aDown(0), - aJump(0), - aUpLeft(0), - aUpRight(0), - _allowHeightChange(false) -{ - releaseAllKeys(); - onConfigChanged( createConfig(mc) ); - - Tesselator& t = Tesselator::instance; - const int alpha = 128; - t.color( 0xc0c0c0, alpha); cPressed = t.getColor(); - t.color( 0xffffff, alpha); cReleased = t.getColor(); - t.color( 0xffffff, alpha / 4); cDiscreet = t.getColor(); - t.color( 0xc0c0c0, 80); cPressedPause=t.getColor(); - t.color( 0xffffff, 80); cReleasedPause=t.getColor(); -} - -TouchscreenInput_TestFps::~TouchscreenInput_TestFps() { - clear(); -} - -void TouchscreenInput_TestFps::clear() { - _model.clear(); - - delete aUpLeft; aUpLeft = NULL; // @todo: SAFEDEL - delete aUpRight; aUpRight = NULL; -} - -bool TouchscreenInput_TestFps::isButtonDown(int areaId) { - return _buttons[areaId - AREA_DPAD_FIRST]; -} - - -void TouchscreenInput_TestFps::onConfigChanged(const Config& c) { - clear(); - - const float w = (float)c.width; - const float h = (float)c.height; - - /* - // Code for "Move when touching left side of the screen" - float x0[] = { 0, w * 0.3f, w * 0.3f, 0 }; - float y0[] = { 0, 0, h-32, h-32 }; - - _model.addArea(AREA_MOVE, new RectangleArea(0, 0, w*0.3f, h-32)); - */ - - // Code for "D-pad with jump in center" - float Bw = w * 0.11f;//0.08f; - float Bh = Bw;//0.15f; - - // If too large (like playing on Tablet) - PixelCalc& pc = _minecraft->pixelCalc; - if (pc.pixelsToMillimeters(Bw) > 200) { //14 - Bw = Bh = pc.millimetersToPixels(200); //14 - } - // temp data - float xx; - float yy; - - const float BaseY = -8 + h - 3.0f * Bh; - const float BaseX = _options->getBooleanValue(OPTIONS_IS_LEFT_HANDED)? -8 + w - 3 * Bw - : 8 + 0; - // Setup the bounding rectangle - _boundingRectangle = RectangleArea(BaseX, BaseY, BaseX + 3 * Bw, BaseY + 3 * Bh); - - xx = BaseX + Bw; yy = BaseY; - _model.addArea(AREA_DPAD_N, aUp = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); - xx = BaseX; - aUpLeft = new RectangleArea(xx, yy, xx+Bw, yy+Bh); - xx = BaseX + 2 * Bw; - aUpRight = new RectangleArea(xx, yy, xx+Bw, yy+Bh); - - xx = BaseX + Bw; yy = BaseY + Bh; - _model.addArea(AREA_DPAD_C, aJump = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); - - xx = BaseX + Bw; yy = BaseY + 2 * Bh; - _model.addArea(AREA_DPAD_S, aDown = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); - - xx = BaseX; yy = BaseY + Bh; - _model.addArea(AREA_DPAD_W, aLeft = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); - - xx = BaseX + 2 * Bw; yy = BaseY + Bh; - _model.addArea(AREA_DPAD_E, aRight = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); - - float maxPixels = _minecraft->pixelCalc.millimetersToPixels(10); - // float btnSize = Mth::Min(18 * Gui::GuiScale, maxPixels); - float btnSize = pc.millimetersToPixels(18 * Gui::GuiScale); - _model.addArea(AREA_PAUSE, aPause = new RectangleArea(w - 4 - btnSize, 4, w - 4, 4 + btnSize)); - _model.addArea(AREA_CHAT, aChat = new RectangleArea(w - 8 - btnSize * 2, 4, w - 8 - btnSize, 4 + btnSize)); - - //rebuild(); -} - -void TouchscreenInput_TestFps::setKey(int key, bool state) -{ - #ifdef WIN32 - //LOGI("key: %d, %d\n", key, state); - - int id = -1; - // theres no keyUp etc??? - //if (key == _options->keyUp.key) id = KEY_UP; - //if (key == _options->keyDown.key) id = KEY_DOWN; - //if (key == _options->keyLeft.key) id = KEY_LEFT; - //if (key == _options->keyRight.key) id = KEY_RIGHT; - //if (key == _options->keyJump.key) id = KEY_JUMP; - //if (key == _options->keySneak.key) id = KEY_SNEAK; - //if (key == _options->keyCraft.key) id = KEY_CRAFT; - //if (id >= 0) { - // _keys[id] = state; - //} - - if (key == _options->getIntValue(OPTIONS_KEY_FORWARD)) id = KEY_UP; - if (key == _options->getIntValue(OPTIONS_KEY_BACK)) id = KEY_DOWN; - if (key == _options->getIntValue(OPTIONS_KEY_LEFT)) id = KEY_LEFT; - if (key == _options->getIntValue(OPTIONS_KEY_RIGHT)) id = KEY_RIGHT; - if (key == _options->getIntValue(OPTIONS_KEY_JUMP)) id = KEY_JUMP; - if (key == _options->getIntValue(OPTIONS_KEY_SNEAK)) id = KEY_SNEAK; - //if (key == _options->getIntValue(OPTIONS_KEY_CRAFT)) id = KEY_CRAFT; - #endif -} - -void TouchscreenInput_TestFps::releaseAllKeys() -{ - xa = 0; - ya = 0; - - for (int i = 0; i<8; ++i) - _buttons[i] = false; -#ifdef WIN32 - for (int i = 0; iisInWater()) { - jumping = true; - } - else if (Multitouch::isPressed(p)) { - jumping = true; - } // Or if we are walking forward, jump while going forward! - else if (_forward && !player->abilities.flying) { - areaId = AREA_DPAD_N; - tmpNorthJump = true; - //jumping = true; - ya += 1; - } - } - - if (areaId == AREA_DPAD_N) - { - setButton = true; - if (player->isInWater()) - jumping = true; - else if (!isChangingFlightHeight) - tmpForward = true; - ya += 1; - } - else if (areaId == AREA_DPAD_S && !_forward) - { - setButton = true; - ya -= 1; - /* - if (Multitouch::isReleased(p)) { - float now = getTimeS(); - if (now - _sneakTapTime < 0.4f) { - ya += 1; - sneaking = !sneaking; - player->setSneaking(sneaking); - _sneakTapTime = -1; - } else { - _sneakTapTime = now; - } - } - */ - } - else if (areaId == AREA_DPAD_W && !_forward) - { - setButton = true; - xa += 1; - } - else if (areaId == AREA_DPAD_E && !_forward) - { - setButton = true; - xa -= 1; - } - else if (areaId == AREA_PAUSE) { - if (Multitouch::isReleased(p)) { - _minecraft->soundEngine->playUI("random.click", 1, 1); - _minecraft->screenChooser.setScreen(SCREEN_PAUSE); - } - } - else if (areaId == AREA_CHAT) { - if (Multitouch::isReleased(p)) { - _minecraft->soundEngine->playUI("random.click", 1, 1); - _minecraft->screenChooser.setScreen(SCREEN_CONSOLE); - _minecraft->platform()->showKeyboard(); - } - } - - _buttons[areaId - AREA_DPAD_FIRST] = setButton; - } - - _forward = tmpForward; - - // Only jump once at a time - if (tmpNorthJump) { - if (!_northJump) - jumping = true; - _northJump = true; - } - else _northJump = false; - - isChangingFlightHeight = false; - wantUp = isButtonDown(AREA_DPAD_N) && (_allowHeightChange & (_pressedJump | wantUp)); - wantDown = isButtonDown(AREA_DPAD_S) && (_allowHeightChange & (_pressedJump | wantDown)); - if (player->abilities.flying && (wantUp || wantDown || (heldJump && !_forward))) - { - isChangingFlightHeight = true; - ya = 0; - } - _renderFlightImage = player->abilities.flying; - -#ifdef WIN32 - if (_keys[KEY_UP]) ya++; - if (_keys[KEY_DOWN]) ya--; - if (_keys[KEY_LEFT]) xa++; - if (_keys[KEY_RIGHT]) xa--; - if (_keys[KEY_JUMP]) jumping = true; - //sneaking = _keys[KEY_SNEAK]; - if (_keys[KEY_CRAFT]) - player->startCrafting((int)player->x, (int)player->y, (int)player->z, Recipe::SIZE_2X2); -#endif - - if (sneaking) { - xa *= 0.3f; - ya *= 0.3f; - } - //printf("\n>- %f %f\n", xa, ya); - _pressedJump = heldJump; -} - -static void drawRectangleArea(Tesselator& t, RectangleArea* a, int ux, int vy, float ssz = 64.0f) { - const float pm = 1.0f / 256.0f; - const float sz = ssz * pm; - const float uu = (float)(ux) * pm; - const float vv = (float)(vy) * pm; - const float x0 = a->_x0 * Gui::InvGuiScale; - const float x1 = a->_x1 * Gui::InvGuiScale; - const float y0 = a->_y0 * Gui::InvGuiScale; - const float y1 = a->_y1 * Gui::InvGuiScale; - - t.vertexUV(x0, y1, 0, uu, vv+sz); - t.vertexUV(x1, y1, 0, uu+sz,vv+sz); - t.vertexUV(x1, y0, 0, uu+sz,vv); - t.vertexUV(x0, y0, 0, uu, vv); -} - -static void drawPolygonArea(Tesselator& t, PolygonArea* a, int x, int y) { - float pm = 1.0f / 256.0f; - float sz = 64.0f * pm; - float uu = (float)(x) * pm; - float vv = (float)(y) * pm; - - float uvs[] = {uu, vv, uu+sz, vv, uu+sz, vv+sz, uu, vv+sz}; - const int o = 0; - - for (int j = 0; j < a->_numPoints; ++j) { - t.vertexUV(a->_x[j] * Gui::InvGuiScale, a->_y[j] * Gui::InvGuiScale, 0, uvs[(o+j+j)&7], uvs[(o+j+j+1)&7]); - } -} - -void TouchscreenInput_TestFps::render( float a ) { - //return; - - //static Stopwatch sw; - //sw.start(); - - - //glColor4f2(1, 0, 1, 1.0f); - //glDisable2(GL_CULL_FACE); - glDisable2(GL_ALPHA_TEST); - - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - _minecraft->textures->loadAndBindTexture("gui/gui.png"); - - //glDisable2(GL_TEXTURE_2D); - - rebuild(); - //drawArrayVTC(_bufferId, 5 * 2 * 3, 24); - - glDisable2(GL_BLEND); - //glEnable2(GL_TEXTURE_2D); - //glEnable2(GL_CULL_FACE); - - //sw.stop(); - //sw.printEvery(100, "buttons"); -} - -const RectangleArea& TouchscreenInput_TestFps::getRectangleArea() -{ - return _boundingRectangle; -} -const RectangleArea& TouchscreenInput_TestFps::getPauseRectangleArea() -{ - return *aPause; -} - -void TouchscreenInput_TestFps::rebuild() { - if (_options->getBooleanValue(OPTIONS_HIDEGUI)) - return; - - Tesselator& t = Tesselator::instance; - //LOGI("instance is: %p, %p, %p, %p, %p FOR %d\n", &t, aLeft, aRight, aUp, aDown, aJump, _bufferId); - //t.setAccessMode(Tesselator::ACCESS_DYNAMIC); - t.begin(); - - const int imageU = 0; - const int imageV = 107; - const int imageSize = 26; - - bool northDiagonals = !isChangingFlightHeight && (_northJump || _forward); - - // render left button - if (northDiagonals || isChangingFlightHeight) t.colorABGR(cDiscreet); - else if (isButtonDown(AREA_DPAD_W)) t.colorABGR(cPressed); - else t.colorABGR(cReleased); - drawRectangleArea(t, aLeft, imageU + imageSize, imageV, (float)imageSize); - - // render right button - if (northDiagonals || isChangingFlightHeight) t.colorABGR(cDiscreet); - else if (isButtonDown(AREA_DPAD_E)) t.colorABGR(cPressed); - else t.colorABGR(cReleased); - drawRectangleArea(t, aRight, imageU + imageSize * 3, imageV, (float)imageSize); - - // render forward button - if (isButtonDown(AREA_DPAD_N)) t.colorABGR(cPressed); - else t.colorABGR(cReleased); - if (isChangingFlightHeight) - { - drawRectangleArea(t, aUp, imageU + imageSize * 2, imageV + imageSize, (float)imageSize); - } - else - { - drawRectangleArea(t, aUp, imageU, imageV, (float)imageSize); - } - - // render diagonals, if available - if (northDiagonals) - { - t.colorABGR(cReleased); - drawRectangleArea(t, aUpLeft, imageU, imageV + imageSize, (float)imageSize); - drawRectangleArea(t, aUpRight, imageU + imageSize, imageV + imageSize, (float)imageSize); - } - - // render backwards button - if (northDiagonals) t.colorABGR(cDiscreet); - else if (isButtonDown(AREA_DPAD_S)) t.colorABGR(cPressed); - else t.colorABGR(cReleased); - if (isChangingFlightHeight) - { - drawRectangleArea(t, aDown, imageU + imageSize * 3, imageV + imageSize, (float)imageSize); - } - else - { - drawRectangleArea(t, aDown, imageU + imageSize * 2, imageV, (float)imageSize); - } - - // render jump / flight button - if (_renderFlightImage && northDiagonals) t.colorABGR(cDiscreet); - else if (isButtonDown(AREA_DPAD_C)) t.colorABGR(cPressed); - else t.colorABGR(cReleased); - if (_renderFlightImage) - { - drawRectangleArea(t, aJump, imageU + imageSize * 4, imageV + imageSize, (float)imageSize); - } - else - { - drawRectangleArea(t, aJump, imageU + imageSize * 4, imageV, (float)imageSize); - } - - if (!_minecraft->screen) { - t.colorABGR(0xFFFFFFFF); - // if (isButtonDown(AREA_PAUSE)) t.colorABGR(cPressedPause); - // else t.colorABGR(cReleasedPause); - - drawRectangleArea(t, aPause, 200, 64, 18.0f); - drawRectangleArea(t, aChat, 200, 82, 18.0f); - } -//t.end(true, _bufferId); - //return; - - t.draw(); - //RenderChunk _render = t.end(true, _bufferId); - //t.setAccessMode(Tesselator::ACCESS_STATIC); - //_bufferId = _render.vboId; -} +#include "TouchscreenInput.h" +#include "../../../Options.h" +#include "../../../../platform/input/Multitouch.h" +#include "../../../gui/Gui.h" +#include "../../../renderer/Tesselator.h" +#include "../../../../world/entity/player/Player.h" + +#include "../../../Minecraft.h" +#include "../../../../platform/log.h" +#include "../../../renderer/Textures.h" +#include "../../../sound/SoundEngine.h" +#include "client/gui/screens/ScreenChooser.h" + + +// ARGHHHHHH WHY NOT FUCKING ENUM +static const int AREA_DPAD_FIRST = 100; +static const int AREA_DPAD_N = 100; +static const int AREA_DPAD_S = 101; +static const int AREA_DPAD_W = 102; +static const int AREA_DPAD_E = 103; +static const int AREA_DPAD_C = 104; +static const int AREA_PAUSE = 105; +static const int AREA_CHAT = 106; + +static int cPressed = 0; +static int cReleased = 0; +static int cDiscreet = 0; +static int cPressedPause = 0; +static int cReleasedPause = 0; +//static const int AREA_DPAD_N_JUMP = 105; + +// +// TouchscreenInput_TestFps +// + +static void Copy(int n, float* x, float* y, float* dx, float* dy) { + for (int i = 0; i < n; ++i) { + dx[i] = x[i]; + dy[i] = y[i]; + } +} + +static void Translate(int n, float* x, float* y, float xt, float yt) { + for (int i = 0; i < n; ++i) { + x[i] += xt; + y[i] += yt; + } +} + +static void Scale(int n, float* x, float* y, float xt, float yt) { + for (int i = 0; i < n; ++i) { + x[i] *= xt; + y[i] *= yt; + } +} + +static void Transformed(int n, float* x, float* y, float* dx, float* dy, float xt, float yt, float sx=1.0f, float sy=1.0f) { + Copy(n, x, y, dx, dy); + Scale(n, dx, dy, sx, sy); + Translate(n, dx, dy, xt, yt); + + //for (int i = 0; i < n; ++i) { + // LOGI("%d. (%f, %f)\n", i, dx[i], dy[i]); + //} +} + +TouchscreenInput_TestFps::TouchscreenInput_TestFps( Minecraft* mc, Options* options ) +: _minecraft(mc), + _options(options), + _northJump(false), + _forward(false), + _boundingRectangle(0, 0, 1, 1), + _pressedJump(false), + _pauseIsDown(false), + _sneakTapTime(-999), + aLeft(0), + aRight(0), + aUp(0), + aDown(0), + aJump(0), + aUpLeft(0), + aUpRight(0), + _allowHeightChange(false) +{ + releaseAllKeys(); + onConfigChanged( createConfig(mc) ); + + Tesselator& t = Tesselator::instance; + const int alpha = 128; + t.color( 0xc0c0c0, alpha); cPressed = t.getColor(); + t.color( 0xffffff, alpha); cReleased = t.getColor(); + t.color( 0xffffff, alpha / 4); cDiscreet = t.getColor(); + t.color( 0xc0c0c0, 80); cPressedPause=t.getColor(); + t.color( 0xffffff, 80); cReleasedPause=t.getColor(); +} + +TouchscreenInput_TestFps::~TouchscreenInput_TestFps() { + clear(); +} + +void TouchscreenInput_TestFps::clear() { + _model.clear(); + + delete aUpLeft; aUpLeft = NULL; // @todo: SAFEDEL + delete aUpRight; aUpRight = NULL; +} + +bool TouchscreenInput_TestFps::isButtonDown(int areaId) { + return _buttons[areaId - AREA_DPAD_FIRST]; +} + + +void TouchscreenInput_TestFps::onConfigChanged(const Config& c) { + clear(); + + const float w = (float)c.width; + const float h = (float)c.height; + + /* + // Code for "Move when touching left side of the screen" + float x0[] = { 0, w * 0.3f, w * 0.3f, 0 }; + float y0[] = { 0, 0, h-32, h-32 }; + + _model.addArea(AREA_MOVE, new RectangleArea(0, 0, w*0.3f, h-32)); + */ + + // Code for "D-pad with jump in center" + float Bw = w * 0.11f;//0.08f; + float Bh = Bw;//0.15f; + + // If too large (like playing on Tablet) + PixelCalc& pc = _minecraft->pixelCalc; + if (pc.pixelsToMillimeters(Bw) > 200) { //14 + Bw = Bh = pc.millimetersToPixels(200); //14 + } + // temp data + float xx; + float yy; + + const float BaseY = -8 + h - 3.0f * Bh; + const float BaseX = _options->getBooleanValue(OPTIONS_IS_LEFT_HANDED)? -8 + w - 3 * Bw + : 8 + 0; + // Setup the bounding rectangle + _boundingRectangle = RectangleArea(BaseX, BaseY, BaseX + 3 * Bw, BaseY + 3 * Bh); + + xx = BaseX + Bw; yy = BaseY; + _model.addArea(AREA_DPAD_N, aUp = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); + xx = BaseX; + aUpLeft = new RectangleArea(xx, yy, xx+Bw, yy+Bh); + xx = BaseX + 2 * Bw; + aUpRight = new RectangleArea(xx, yy, xx+Bw, yy+Bh); + + xx = BaseX + Bw; yy = BaseY + Bh; + _model.addArea(AREA_DPAD_C, aJump = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); + + xx = BaseX + Bw; yy = BaseY + 2 * Bh; + _model.addArea(AREA_DPAD_S, aDown = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); + + xx = BaseX; yy = BaseY + Bh; + _model.addArea(AREA_DPAD_W, aLeft = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); + + xx = BaseX + 2 * Bw; yy = BaseY + Bh; + _model.addArea(AREA_DPAD_E, aRight = new RectangleArea(xx, yy, xx+Bw, yy+Bh)); + + float maxPixels = _minecraft->pixelCalc.millimetersToPixels(10); + // float btnSize = Mth::Min(18 * Gui::GuiScale, maxPixels); + float btnSize = pc.millimetersToPixels(18 * Gui::GuiScale); + _model.addArea(AREA_PAUSE, aPause = new RectangleArea(w - 4 - btnSize, 4, w - 4, 4 + btnSize)); + _model.addArea(AREA_CHAT, aChat = new RectangleArea(w - 8 - btnSize * 2, 4, w - 8 - btnSize, 4 + btnSize)); + + //rebuild(); +} + +void TouchscreenInput_TestFps::setKey(int key, bool state) +{ + #ifdef WIN32 + //LOGI("key: %d, %d\n", key, state); + + int id = -1; + // theres no keyUp etc??? + //if (key == _options->keyUp.key) id = KEY_UP; + //if (key == _options->keyDown.key) id = KEY_DOWN; + //if (key == _options->keyLeft.key) id = KEY_LEFT; + //if (key == _options->keyRight.key) id = KEY_RIGHT; + //if (key == _options->keyJump.key) id = KEY_JUMP; + //if (key == _options->keySneak.key) id = KEY_SNEAK; + //if (key == _options->keyCraft.key) id = KEY_CRAFT; + //if (id >= 0) { + // _keys[id] = state; + //} + + if (key == _options->getIntValue(OPTIONS_KEY_FORWARD)) id = KEY_UP; + if (key == _options->getIntValue(OPTIONS_KEY_BACK)) id = KEY_DOWN; + if (key == _options->getIntValue(OPTIONS_KEY_LEFT)) id = KEY_LEFT; + if (key == _options->getIntValue(OPTIONS_KEY_RIGHT)) id = KEY_RIGHT; + if (key == _options->getIntValue(OPTIONS_KEY_JUMP)) id = KEY_JUMP; + if (key == _options->getIntValue(OPTIONS_KEY_SNEAK)) id = KEY_SNEAK; + //if (key == _options->getIntValue(OPTIONS_KEY_CRAFT)) id = KEY_CRAFT; + #endif +} + +void TouchscreenInput_TestFps::releaseAllKeys() +{ + xa = 0; + ya = 0; + + for (int i = 0; i<8; ++i) + _buttons[i] = false; +#ifdef WIN32 + for (int i = 0; iisInWater()) { + jumping = true; + } + else if (Multitouch::isPressed(p)) { + jumping = true; + } // Or if we are walking forward, jump while going forward! + else if (_forward && !player->abilities.flying) { + areaId = AREA_DPAD_N; + tmpNorthJump = true; + //jumping = true; + ya += 1; + } + } + + if (areaId == AREA_DPAD_N) + { + setButton = true; + if (player->isInWater()) + jumping = true; + else if (!isChangingFlightHeight) + tmpForward = true; + ya += 1; + } + else if (areaId == AREA_DPAD_S && !_forward) + { + setButton = true; + ya -= 1; + /* + if (Multitouch::isReleased(p)) { + float now = getTimeS(); + if (now - _sneakTapTime < 0.4f) { + ya += 1; + sneaking = !sneaking; + player->setSneaking(sneaking); + _sneakTapTime = -1; + } else { + _sneakTapTime = now; + } + } + */ + } + else if (areaId == AREA_DPAD_W && !_forward) + { + setButton = true; + xa += 1; + } + else if (areaId == AREA_DPAD_E && !_forward) + { + setButton = true; + xa -= 1; + } + else if (areaId == AREA_PAUSE) { + if (Multitouch::isReleased(p)) { + _minecraft->soundEngine->playUI("random.click", 1, 1); + _minecraft->screenChooser.setScreen(SCREEN_PAUSE); + } + } + else if (areaId == AREA_CHAT) { + if (Multitouch::isReleased(p)) { + _minecraft->soundEngine->playUI("random.click", 1, 1); + _minecraft->screenChooser.setScreen(SCREEN_CONSOLE); + _minecraft->platform()->showKeyboard(); + } + } + + _buttons[areaId - AREA_DPAD_FIRST] = setButton; + } + + _forward = tmpForward; + + // Only jump once at a time + if (tmpNorthJump) { + if (!_northJump) + jumping = true; + _northJump = true; + } + else _northJump = false; + + isChangingFlightHeight = false; + wantUp = isButtonDown(AREA_DPAD_N) && (_allowHeightChange & (_pressedJump | wantUp)); + wantDown = isButtonDown(AREA_DPAD_S) && (_allowHeightChange & (_pressedJump | wantDown)); + if (player->abilities.flying && (wantUp || wantDown || (heldJump && !_forward))) + { + isChangingFlightHeight = true; + ya = 0; + } + _renderFlightImage = player->abilities.flying; + +#ifdef WIN32 + if (_keys[KEY_UP]) ya++; + if (_keys[KEY_DOWN]) ya--; + if (_keys[KEY_LEFT]) xa++; + if (_keys[KEY_RIGHT]) xa--; + if (_keys[KEY_JUMP]) jumping = true; + //sneaking = _keys[KEY_SNEAK]; + if (_keys[KEY_CRAFT]) + player->startCrafting((int)player->x, (int)player->y, (int)player->z, Recipe::SIZE_2X2); +#endif + + if (sneaking) { + xa *= 0.3f; + ya *= 0.3f; + } + //printf("\n>- %f %f\n", xa, ya); + _pressedJump = heldJump; +} + +static void drawRectangleArea(Tesselator& t, RectangleArea* a, int ux, int vy, float ssz = 64.0f) { + const float pm = 1.0f / 256.0f; + const float sz = ssz * pm; + const float uu = (float)(ux) * pm; + const float vv = (float)(vy) * pm; + const float x0 = a->_x0 * Gui::InvGuiScale; + const float x1 = a->_x1 * Gui::InvGuiScale; + const float y0 = a->_y0 * Gui::InvGuiScale; + const float y1 = a->_y1 * Gui::InvGuiScale; + + t.vertexUV(x0, y1, 0, uu, vv+sz); + t.vertexUV(x1, y1, 0, uu+sz,vv+sz); + t.vertexUV(x1, y0, 0, uu+sz,vv); + t.vertexUV(x0, y0, 0, uu, vv); +} + +static void drawPolygonArea(Tesselator& t, PolygonArea* a, int x, int y) { + float pm = 1.0f / 256.0f; + float sz = 64.0f * pm; + float uu = (float)(x) * pm; + float vv = (float)(y) * pm; + + float uvs[] = {uu, vv, uu+sz, vv, uu+sz, vv+sz, uu, vv+sz}; + const int o = 0; + + for (int j = 0; j < a->_numPoints; ++j) { + t.vertexUV(a->_x[j] * Gui::InvGuiScale, a->_y[j] * Gui::InvGuiScale, 0, uvs[(o+j+j)&7], uvs[(o+j+j+1)&7]); + } +} + +void TouchscreenInput_TestFps::render( float a ) { + //return; + + //static Stopwatch sw; + //sw.start(); + + + //glColor4f2(1, 0, 1, 1.0f); + //glDisable2(GL_CULL_FACE); + glDisable2(GL_ALPHA_TEST); + + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + _minecraft->textures->loadAndBindTexture("gui/gui.png"); + + //glDisable2(GL_TEXTURE_2D); + + rebuild(); + //drawArrayVTC(_bufferId, 5 * 2 * 3, 24); + + glDisable2(GL_BLEND); + //glEnable2(GL_TEXTURE_2D); + //glEnable2(GL_CULL_FACE); + + //sw.stop(); + //sw.printEvery(100, "buttons"); +} + +const RectangleArea& TouchscreenInput_TestFps::getRectangleArea() +{ + return _boundingRectangle; +} +const RectangleArea& TouchscreenInput_TestFps::getPauseRectangleArea() +{ + return *aPause; +} + +void TouchscreenInput_TestFps::rebuild() { + if (_options->getBooleanValue(OPTIONS_HIDEGUI)) + return; + + Tesselator& t = Tesselator::instance; + //LOGI("instance is: %p, %p, %p, %p, %p FOR %d\n", &t, aLeft, aRight, aUp, aDown, aJump, _bufferId); + //t.setAccessMode(Tesselator::ACCESS_DYNAMIC); + t.begin(); + + const int imageU = 0; + const int imageV = 107; + const int imageSize = 26; + + bool northDiagonals = !isChangingFlightHeight && (_northJump || _forward); + + // render left button + if (northDiagonals || isChangingFlightHeight) t.colorABGR(cDiscreet); + else if (isButtonDown(AREA_DPAD_W)) t.colorABGR(cPressed); + else t.colorABGR(cReleased); + drawRectangleArea(t, aLeft, imageU + imageSize, imageV, (float)imageSize); + + // render right button + if (northDiagonals || isChangingFlightHeight) t.colorABGR(cDiscreet); + else if (isButtonDown(AREA_DPAD_E)) t.colorABGR(cPressed); + else t.colorABGR(cReleased); + drawRectangleArea(t, aRight, imageU + imageSize * 3, imageV, (float)imageSize); + + // render forward button + if (isButtonDown(AREA_DPAD_N)) t.colorABGR(cPressed); + else t.colorABGR(cReleased); + if (isChangingFlightHeight) + { + drawRectangleArea(t, aUp, imageU + imageSize * 2, imageV + imageSize, (float)imageSize); + } + else + { + drawRectangleArea(t, aUp, imageU, imageV, (float)imageSize); + } + + // render diagonals, if available + if (northDiagonals) + { + t.colorABGR(cReleased); + drawRectangleArea(t, aUpLeft, imageU, imageV + imageSize, (float)imageSize); + drawRectangleArea(t, aUpRight, imageU + imageSize, imageV + imageSize, (float)imageSize); + } + + // render backwards button + if (northDiagonals) t.colorABGR(cDiscreet); + else if (isButtonDown(AREA_DPAD_S)) t.colorABGR(cPressed); + else t.colorABGR(cReleased); + if (isChangingFlightHeight) + { + drawRectangleArea(t, aDown, imageU + imageSize * 3, imageV + imageSize, (float)imageSize); + } + else + { + drawRectangleArea(t, aDown, imageU + imageSize * 2, imageV, (float)imageSize); + } + + // render jump / flight button + if (_renderFlightImage && northDiagonals) t.colorABGR(cDiscreet); + else if (isButtonDown(AREA_DPAD_C)) t.colorABGR(cPressed); + else t.colorABGR(cReleased); + if (_renderFlightImage) + { + drawRectangleArea(t, aJump, imageU + imageSize * 4, imageV + imageSize, (float)imageSize); + } + else + { + drawRectangleArea(t, aJump, imageU + imageSize * 4, imageV, (float)imageSize); + } + + if (!_minecraft->screen) { + t.colorABGR(0xFFFFFFFF); + // if (isButtonDown(AREA_PAUSE)) t.colorABGR(cPressedPause); + // else t.colorABGR(cReleasedPause); + + drawRectangleArea(t, aPause, 200, 64, 18.0f); + drawRectangleArea(t, aChat, 200, 82, 18.0f); + } +//t.end(true, _bufferId); + //return; + + t.draw(); + //RenderChunk _render = t.end(true, _bufferId); + //t.setAccessMode(Tesselator::ACCESS_STATIC); + //_bufferId = _render.vboId; +} diff --git a/src/client/player/input/touchscreen/TouchscreenInput.h b/src/client/player/input/touchscreen/TouchscreenInput.h index 515f0d8..b3dc159 100755 --- a/src/client/player/input/touchscreen/TouchscreenInput.h +++ b/src/client/player/input/touchscreen/TouchscreenInput.h @@ -1,80 +1,80 @@ -#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchscreenInput_H__ -#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchscreenInput_H__ - -//package net.minecraft.client.player; - -#include "../IMoveInput.h" -#include "../../../gui/GuiComponent.h" -#include "TouchAreaModel.h" -#include "../../../renderer/RenderChunk.h" - -class Options; -class Player; -class Minecraft; -class PolygonArea; - -// @todo: extract a separate MoveInput (-> merge XperiaPlayInput) -class TouchscreenInput_TestFps: public IMoveInput, - public GuiComponent -{ -public: - static const int KEY_UP = 0; - static const int KEY_DOWN = 1; - static const int KEY_LEFT = 2; - static const int KEY_RIGHT = 3; - static const int KEY_JUMP = 4; - static const int KEY_SNEAK = 5; - static const int KEY_CRAFT = 6; - static const int NumKeys = 7; - - TouchscreenInput_TestFps(Minecraft* mc, Options* options); - ~TouchscreenInput_TestFps(); - - void onConfigChanged(const Config& c); - - void tick(Player* player); - void render(float a); - - void setKey(int key, bool state); - void releaseAllKeys(); - - const RectangleArea& getRectangleArea(); - const RectangleArea& getPauseRectangleArea(); - -private: - void clear(); - - RectangleArea _boundingRectangle; - - bool _keys[NumKeys]; - Options* _options; - - bool _pressedJump; - bool _forward; - bool _northJump; - bool _renderFlightImage; - TouchAreaModel _model; - Minecraft* _minecraft; - - RectangleArea* aLeft; - RectangleArea* aRight; - RectangleArea* aUp; - RectangleArea* aDown; - RectangleArea* aPause; - RectangleArea* aChat; - //RectangleArea* aUpJump; - RectangleArea* aJump; - RectangleArea* aUpLeft; - RectangleArea* aUpRight; - bool _pauseIsDown; - - RenderChunk _render; - bool _allowHeightChange; - float _sneakTapTime; - - bool _buttons[8]; - bool isButtonDown(int areaId); - void rebuild(); -}; - -#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchscreenInput_H__*/ +#ifndef NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchscreenInput_H__ +#define NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchscreenInput_H__ + +//package net.minecraft.client.player; + +#include "../IMoveInput.h" +#include "../../../gui/GuiComponent.h" +#include "TouchAreaModel.h" +#include "../../../renderer/RenderChunk.h" + +class Options; +class Player; +class Minecraft; +class PolygonArea; + +// @todo: extract a separate MoveInput (-> merge XperiaPlayInput) +class TouchscreenInput_TestFps: public IMoveInput, + public GuiComponent +{ +public: + static const int KEY_UP = 0; + static const int KEY_DOWN = 1; + static const int KEY_LEFT = 2; + static const int KEY_RIGHT = 3; + static const int KEY_JUMP = 4; + static const int KEY_SNEAK = 5; + static const int KEY_CRAFT = 6; + static const int NumKeys = 7; + + TouchscreenInput_TestFps(Minecraft* mc, Options* options); + ~TouchscreenInput_TestFps(); + + void onConfigChanged(const Config& c); + + void tick(Player* player); + void render(float a); + + void setKey(int key, bool state); + void releaseAllKeys(); + + const RectangleArea& getRectangleArea(); + const RectangleArea& getPauseRectangleArea(); + +private: + void clear(); + + RectangleArea _boundingRectangle; + + bool _keys[NumKeys]; + Options* _options; + + bool _pressedJump; + bool _forward; + bool _northJump; + bool _renderFlightImage; + TouchAreaModel _model; + Minecraft* _minecraft; + + RectangleArea* aLeft; + RectangleArea* aRight; + RectangleArea* aUp; + RectangleArea* aDown; + RectangleArea* aPause; + RectangleArea* aChat; + //RectangleArea* aUpJump; + RectangleArea* aJump; + RectangleArea* aUpLeft; + RectangleArea* aUpRight; + bool _pauseIsDown; + + RenderChunk _render; + bool _allowHeightChange; + float _sneakTapTime; + + bool _buttons[8]; + bool isButtonDown(int areaId); + void rebuild(); +}; + +#endif /*NET_MINECRAFT_CLIENT_PLAYER_INPUT_TOUCHSCREEN_TouchscreenInput_H__*/ diff --git a/src/client/renderer/Chunk.cpp b/src/client/renderer/Chunk.cpp index 13ac27c..9e9fdd9 100755 --- a/src/client/renderer/Chunk.cpp +++ b/src/client/renderer/Chunk.cpp @@ -1,262 +1,262 @@ -#include "Chunk.h" -#include "Tesselator.h" -#include "TileRenderer.h" -#include "culling/Culler.h" -#include "../../world/entity/Entity.h" -#include "../../world/level/tile/Tile.h" -#include "../../world/level/Region.h" -#include "../../world/level/chunk/LevelChunk.h" -#include "../../util/Mth.h" -//#include "../../platform/time.h" - -/*static*/ int Chunk::updates = 0; -//static Stopwatch swRebuild; -//int* _layerChunks[3] = {0, 0, 0}; //Chunk::NumLayers]; -//int _layerChunkCount[3] = {0, 0, 0}; - -Chunk::Chunk( Level* level_, int x, int y, int z, int size, int lists_, GLuint* ptrBuf/*= NULL*/) -: level(level_), - visible(false), - compiled(false), - _empty(true), - xs(size), ys(size), zs(size), - dirty(false), - occlusion_visible(true), - occlusion_querying(false), - lists(lists_), - vboBuffers(ptrBuf), - bb(0,0,0,1,1,1), - t(Tesselator::instance) -{ - for (int l = 0; l < NumLayers; l++) { - empty[l] = false; - } - - radius = Mth::sqrt((float)(xs * xs + ys * ys + zs * zs)) * 0.5f; - - this->x = -999; - setPos(x, y, z); -} - -void Chunk::setPos( int x, int y, int z ) -{ - if (x == this->x && y == this->y && z == this->z) return; - - reset(); - this->x = x; - this->y = y; - this->z = z; - xm = x + xs / 2; - ym = y + ys / 2; - zm = z + zs / 2; - - const float xzg = 1.0f; - const float yp = 2.0f; - const float yn = 0.0f; - bb.set(x-xzg, y-yn, z-xzg, x + xs+xzg, y + ys+yp, z + zs+xzg); - - //glNewList(lists + 2, GL_COMPILE); - //ItemRenderer.renderFlat(AABB.newTemp(xRenderOffs - g, yRenderOffs - g, zRenderOffs - g, xRenderOffs + xs + g, yRenderOffs + ys + g, zRenderOffs + zs + g)); - //glEndList(); - setDirty(); -} - -void Chunk::translateToPos() -{ - glTranslatef2((float)x, (float)y, (float)z); -} - -void Chunk::rebuild() -{ - if (!dirty) return; - //if (!visible) return; - updates++; - - //if (!_layerChunks[0]) { - // for (int i = 0; i < NumLayers; ++i) - // _layerChunks[i] = new int[xs * ys * zs]; - //} - //for (int i = 0; i < NumLayers; ++i) - // _layerChunkCount[i] = 0; - - //Stopwatch& sw = swRebuild; - //sw.start(); - - int x0 = x; - int y0 = y; - int z0 = z; - int x1 = x + xs; - int y1 = y + ys; - int z1 = z + zs; - for (int l = 0; l < NumLayers; l++) { - empty[l] = true; - } - _empty = true; - - LevelChunk::touchedSky = false; - - int r = 1; - Region region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r); - TileRenderer tileRenderer(®ion); - - bool doRenderLayer[NumLayers] = {true, false, false}; - for (int l = 0; l < NumLayers; l++) { - if (!doRenderLayer[l]) continue; - bool renderNextLayer = false; - bool rendered = false; - - bool started = false; - int cindex = -1; - - for (int y = y0; y < y1; y++) { - for (int z = z0; z < z1; z++) { - for (int x = x0; x < x1; x++) { - ++cindex; - //if (l > 0 && cindex != _layerChunks[_layerChunkCount[l]]) - int tileId = region.getTile(x, y, z); - if (tileId > 0) { - if (!started) { - started = true; - -#ifndef USE_VBO - glNewList(lists + l, GL_COMPILE); - glPushMatrix2(); - translateToPos(); - float ss = 1.000001f; - glTranslatef2(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); - glScalef2(ss, ss, ss); - glTranslatef2(zs / 2.0f, ys / 2.0f, zs / 2.0f); -#endif - t.begin(); - //printf("."); - //printf("Tesselator::offset : %d, %d, %d\n", this->x, this->y, this->z); - t.offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); - //printf("Tesselator::offset : %f, %f, %f\n", this->x, this->y, this->z); - } - - Tile* tile = Tile::tiles[tileId]; - int renderLayer = tile->getRenderLayer(); - - if (renderLayer > l) { - renderNextLayer = true; - doRenderLayer[renderLayer] = true; - } else if (renderLayer == l) { - rendered |= tileRenderer.tesselateInWorld(tile, x, y, z); - } - } - } - } - } - - if (started) { - -#ifdef USE_VBO - renderChunk[l] = t.end(true, vboBuffers[l]); - renderChunk[l].pos.x = (float)this->x; - renderChunk[l].pos.y = (float)this->y; - renderChunk[l].pos.z = (float)this->z; -#else - t.end(false, -1); - glPopMatrix2(); - glEndList(); -#endif - t.offset(0, 0, 0); - } else { - rendered = false; - } - if (rendered) { - empty[l] = false; - _empty = false; - } - if (!renderNextLayer) break; - } - - //sw.stop(); - //sw.printEvery(1, "rebuild-"); - skyLit = LevelChunk::touchedSky; - compiled = true; - return; -} - -float Chunk::distanceToSqr( const Entity* player ) const -{ - float xd = (float) (player->x - xm); - float yd = (float) (player->y - ym); - float zd = (float) (player->z - zm); - return xd * xd + yd * yd + zd * zd; -} - -float Chunk::squishedDistanceToSqr( const Entity* player ) const -{ - float xd = (float) (player->x - xm); - float yd = (float) (player->y - ym) * 2; - float zd = (float) (player->z - zm); - return xd * xd + yd * yd + zd * zd; -} - -void Chunk::reset() -{ - for (int i = 0; i < NumLayers; i++) { - empty[i] = true; - } - visible = false; - compiled = false; - _empty = true; -} - -int Chunk::getList( int layer ) -{ - if (!visible) return -1; - if (!empty[layer]) return lists + layer; - return -1; -} - -RenderChunk& Chunk::getRenderChunk( int layer ) -{ - return renderChunk[layer]; -} - -int Chunk::getAllLists( int displayLists[], int p, int layer ) -{ - if (!visible) return p; - if (!empty[layer]) displayLists[p++] = (lists + layer); - return p; -} - -void Chunk::cull( Culler* culler ) -{ - visible = culler->isVisible(bb); -} - -void Chunk::renderBB() -{ - //glCallList(lists + 2); -} - -bool Chunk::isEmpty() -{ - return compiled && _empty;//empty[0] && empty[1] && empty[2]; -// if (!compiled) return false; -// return empty[0] && empty[1]; -} - -void Chunk::setDirty() -{ - dirty = true; -} - -void Chunk::setClean() -{ - dirty = false; -} - -bool Chunk::isDirty() -{ - return dirty; -} - -void Chunk::resetUpdates() -{ - updates = 0; - //swRebuild.reset(); -} +#include "Chunk.h" +#include "Tesselator.h" +#include "TileRenderer.h" +#include "culling/Culler.h" +#include "../../world/entity/Entity.h" +#include "../../world/level/tile/Tile.h" +#include "../../world/level/Region.h" +#include "../../world/level/chunk/LevelChunk.h" +#include "../../util/Mth.h" +//#include "../../platform/time.h" + +/*static*/ int Chunk::updates = 0; +//static Stopwatch swRebuild; +//int* _layerChunks[3] = {0, 0, 0}; //Chunk::NumLayers]; +//int _layerChunkCount[3] = {0, 0, 0}; + +Chunk::Chunk( Level* level_, int x, int y, int z, int size, int lists_, GLuint* ptrBuf/*= NULL*/) +: level(level_), + visible(false), + compiled(false), + _empty(true), + xs(size), ys(size), zs(size), + dirty(false), + occlusion_visible(true), + occlusion_querying(false), + lists(lists_), + vboBuffers(ptrBuf), + bb(0,0,0,1,1,1), + t(Tesselator::instance) +{ + for (int l = 0; l < NumLayers; l++) { + empty[l] = false; + } + + radius = Mth::sqrt((float)(xs * xs + ys * ys + zs * zs)) * 0.5f; + + this->x = -999; + setPos(x, y, z); +} + +void Chunk::setPos( int x, int y, int z ) +{ + if (x == this->x && y == this->y && z == this->z) return; + + reset(); + this->x = x; + this->y = y; + this->z = z; + xm = x + xs / 2; + ym = y + ys / 2; + zm = z + zs / 2; + + const float xzg = 1.0f; + const float yp = 2.0f; + const float yn = 0.0f; + bb.set(x-xzg, y-yn, z-xzg, x + xs+xzg, y + ys+yp, z + zs+xzg); + + //glNewList(lists + 2, GL_COMPILE); + //ItemRenderer.renderFlat(AABB.newTemp(xRenderOffs - g, yRenderOffs - g, zRenderOffs - g, xRenderOffs + xs + g, yRenderOffs + ys + g, zRenderOffs + zs + g)); + //glEndList(); + setDirty(); +} + +void Chunk::translateToPos() +{ + glTranslatef2((float)x, (float)y, (float)z); +} + +void Chunk::rebuild() +{ + if (!dirty) return; + //if (!visible) return; + updates++; + + //if (!_layerChunks[0]) { + // for (int i = 0; i < NumLayers; ++i) + // _layerChunks[i] = new int[xs * ys * zs]; + //} + //for (int i = 0; i < NumLayers; ++i) + // _layerChunkCount[i] = 0; + + //Stopwatch& sw = swRebuild; + //sw.start(); + + int x0 = x; + int y0 = y; + int z0 = z; + int x1 = x + xs; + int y1 = y + ys; + int z1 = z + zs; + for (int l = 0; l < NumLayers; l++) { + empty[l] = true; + } + _empty = true; + + LevelChunk::touchedSky = false; + + int r = 1; + Region region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r); + TileRenderer tileRenderer(®ion); + + bool doRenderLayer[NumLayers] = {true, false, false}; + for (int l = 0; l < NumLayers; l++) { + if (!doRenderLayer[l]) continue; + bool renderNextLayer = false; + bool rendered = false; + + bool started = false; + int cindex = -1; + + for (int y = y0; y < y1; y++) { + for (int z = z0; z < z1; z++) { + for (int x = x0; x < x1; x++) { + ++cindex; + //if (l > 0 && cindex != _layerChunks[_layerChunkCount[l]]) + int tileId = region.getTile(x, y, z); + if (tileId > 0) { + if (!started) { + started = true; + +#ifndef USE_VBO + glNewList(lists + l, GL_COMPILE); + glPushMatrix2(); + translateToPos(); + float ss = 1.000001f; + glTranslatef2(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); + glScalef2(ss, ss, ss); + glTranslatef2(zs / 2.0f, ys / 2.0f, zs / 2.0f); +#endif + t.begin(); + //printf("."); + //printf("Tesselator::offset : %d, %d, %d\n", this->x, this->y, this->z); + t.offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); + //printf("Tesselator::offset : %f, %f, %f\n", this->x, this->y, this->z); + } + + Tile* tile = Tile::tiles[tileId]; + int renderLayer = tile->getRenderLayer(); + + if (renderLayer > l) { + renderNextLayer = true; + doRenderLayer[renderLayer] = true; + } else if (renderLayer == l) { + rendered |= tileRenderer.tesselateInWorld(tile, x, y, z); + } + } + } + } + } + + if (started) { + +#ifdef USE_VBO + renderChunk[l] = t.end(true, vboBuffers[l]); + renderChunk[l].pos.x = (float)this->x; + renderChunk[l].pos.y = (float)this->y; + renderChunk[l].pos.z = (float)this->z; +#else + t.end(false, -1); + glPopMatrix2(); + glEndList(); +#endif + t.offset(0, 0, 0); + } else { + rendered = false; + } + if (rendered) { + empty[l] = false; + _empty = false; + } + if (!renderNextLayer) break; + } + + //sw.stop(); + //sw.printEvery(1, "rebuild-"); + skyLit = LevelChunk::touchedSky; + compiled = true; + return; +} + +float Chunk::distanceToSqr( const Entity* player ) const +{ + float xd = (float) (player->x - xm); + float yd = (float) (player->y - ym); + float zd = (float) (player->z - zm); + return xd * xd + yd * yd + zd * zd; +} + +float Chunk::squishedDistanceToSqr( const Entity* player ) const +{ + float xd = (float) (player->x - xm); + float yd = (float) (player->y - ym) * 2; + float zd = (float) (player->z - zm); + return xd * xd + yd * yd + zd * zd; +} + +void Chunk::reset() +{ + for (int i = 0; i < NumLayers; i++) { + empty[i] = true; + } + visible = false; + compiled = false; + _empty = true; +} + +int Chunk::getList( int layer ) +{ + if (!visible) return -1; + if (!empty[layer]) return lists + layer; + return -1; +} + +RenderChunk& Chunk::getRenderChunk( int layer ) +{ + return renderChunk[layer]; +} + +int Chunk::getAllLists( int displayLists[], int p, int layer ) +{ + if (!visible) return p; + if (!empty[layer]) displayLists[p++] = (lists + layer); + return p; +} + +void Chunk::cull( Culler* culler ) +{ + visible = culler->isVisible(bb); +} + +void Chunk::renderBB() +{ + //glCallList(lists + 2); +} + +bool Chunk::isEmpty() +{ + return compiled && _empty;//empty[0] && empty[1] && empty[2]; +// if (!compiled) return false; +// return empty[0] && empty[1]; +} + +void Chunk::setDirty() +{ + dirty = true; +} + +void Chunk::setClean() +{ + dirty = false; +} + +bool Chunk::isDirty() +{ + return dirty; +} + +void Chunk::resetUpdates() +{ + updates = 0; + //swRebuild.reset(); +} diff --git a/src/client/renderer/Chunk.h b/src/client/renderer/Chunk.h index 569e078..5e26e0d 100755 --- a/src/client/renderer/Chunk.h +++ b/src/client/renderer/Chunk.h @@ -1,74 +1,74 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__Chunk_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__Chunk_H__ - -//package net.minecraft.client.renderer; - -#include "RenderChunk.h" -#include "../../world/phys/AABB.h" - -class Level; -class Entity; -class Culler; -class Tesselator; - -// @note: TileEntity stuff is stripped away -class Chunk -{ - static const int NumLayers = 3; -public: - Chunk(Level* level_, int x, int y, int z, int size, int lists_, GLuint* ptrBuf = NULL); - - void setPos(int x, int y, int z); - - void rebuild(); - void setDirty(); - void setClean(); - bool isDirty(); - void reset(); - - float distanceToSqr(const Entity* player) const; - float squishedDistanceToSqr(const Entity* player) const; - - //@todo @fix - int getAllLists(int displayLists[], int p, int layer); - int getList(int layer); - - RenderChunk& getRenderChunk(int layer); - - bool isEmpty(); - void cull(Culler* culler); - - void renderBB(); - static void resetUpdates(); - -private: - void translateToPos(); -public: - Level* level; - - static int updates;// = 0; - - int x, y, z, xs, ys, zs; - bool empty[NumLayers]; - int xm, ym, zm; - float radius; - AABB bb; - - int id; - bool visible; - bool occlusion_visible; - bool occlusion_querying; - int occlusion_id; - bool skyLit; - - RenderChunk renderChunk[NumLayers]; -private: - Tesselator& t; - int lists; - GLuint* vboBuffers; - bool compiled; - bool dirty; - bool _empty; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__Chunk_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__Chunk_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__Chunk_H__ + +//package net.minecraft.client.renderer; + +#include "RenderChunk.h" +#include "../../world/phys/AABB.h" + +class Level; +class Entity; +class Culler; +class Tesselator; + +// @note: TileEntity stuff is stripped away +class Chunk +{ + static const int NumLayers = 3; +public: + Chunk(Level* level_, int x, int y, int z, int size, int lists_, GLuint* ptrBuf = NULL); + + void setPos(int x, int y, int z); + + void rebuild(); + void setDirty(); + void setClean(); + bool isDirty(); + void reset(); + + float distanceToSqr(const Entity* player) const; + float squishedDistanceToSqr(const Entity* player) const; + + //@todo @fix + int getAllLists(int displayLists[], int p, int layer); + int getList(int layer); + + RenderChunk& getRenderChunk(int layer); + + bool isEmpty(); + void cull(Culler* culler); + + void renderBB(); + static void resetUpdates(); + +private: + void translateToPos(); +public: + Level* level; + + static int updates;// = 0; + + int x, y, z, xs, ys, zs; + bool empty[NumLayers]; + int xm, ym, zm; + float radius; + AABB bb; + + int id; + bool visible; + bool occlusion_visible; + bool occlusion_querying; + int occlusion_id; + bool skyLit; + + RenderChunk renderChunk[NumLayers]; +private: + Tesselator& t; + int lists; + GLuint* vboBuffers; + bool compiled; + bool dirty; + bool _empty; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__Chunk_H__*/ diff --git a/src/client/renderer/ChunkRenderer.h b/src/client/renderer/ChunkRenderer.h index 8f4cef2..b7ff7ed 100755 --- a/src/client/renderer/ChunkRenderer.h +++ b/src/client/renderer/ChunkRenderer.h @@ -1,22 +1,22 @@ -#ifndef CHUNKRENDERER_H__ -#define CHUNKRENDERER_H__ - -#include "gles.h" -#include "../../world/level/Region.h" - -class ChunkRenderer { -public: - ChunkRenderer(const Region& region) - : r(region) - { - } - - void render() { - //glTranslate(r.xc1, - } - -private: - const Region& r; -}; - -#endif /*CHUNKRENDERER_H__*/ +#ifndef CHUNKRENDERER_H__ +#define CHUNKRENDERER_H__ + +#include "gles.h" +#include "../../world/level/Region.h" + +class ChunkRenderer { +public: + ChunkRenderer(const Region& region) + : r(region) + { + } + + void render() { + //glTranslate(r.xc1, + } + +private: + const Region& r; +}; + +#endif /*CHUNKRENDERER_H__*/ diff --git a/src/client/renderer/Color4.h b/src/client/renderer/Color4.h index a9f63d7..72d5899 100755 --- a/src/client/renderer/Color4.h +++ b/src/client/renderer/Color4.h @@ -1,53 +1,53 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__Color4_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__Color4_H__ - -class Color4 -{ -public: - GLfloat r; - GLfloat g; - GLfloat b; - GLfloat a; - - Color4(GLfloat r_, GLfloat g_, GLfloat b_, GLfloat a_ = 0) - : r(r_), - g(g_), - b(b_), - a(a_) - {} - - Color4(int hex) { - r = ((hex>>16)& 255) / 255.0f; - g = ((hex>>8) & 255) / 255.0f; - b = (hex&255) / 255.0f; - a = (hex >> 24) / 255.0f; - } - - void add(GLfloat val, bool modifyAlpha) { - r += val; - g += val; - b += val; - if (modifyAlpha) a += val; - sanitize(); - } - void mul(GLfloat val, bool modifyAlpha) { - r *= val; - g *= val; - b *= val; - if (modifyAlpha) a *= val; - sanitize(); - } -private: - void sanitize() { - range(r); - range(g); - range(b); - range(a); - } - __inline void range(GLfloat& v) { - if (v < 0) v = 0; - if (v > 1) v = 1; - } -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__Color4_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__Color4_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__Color4_H__ + +class Color4 +{ +public: + GLfloat r; + GLfloat g; + GLfloat b; + GLfloat a; + + Color4(GLfloat r_, GLfloat g_, GLfloat b_, GLfloat a_ = 0) + : r(r_), + g(g_), + b(b_), + a(a_) + {} + + Color4(int hex) { + r = ((hex>>16)& 255) / 255.0f; + g = ((hex>>8) & 255) / 255.0f; + b = (hex&255) / 255.0f; + a = (hex >> 24) / 255.0f; + } + + void add(GLfloat val, bool modifyAlpha) { + r += val; + g += val; + b += val; + if (modifyAlpha) a += val; + sanitize(); + } + void mul(GLfloat val, bool modifyAlpha) { + r *= val; + g *= val; + b *= val; + if (modifyAlpha) a *= val; + sanitize(); + } +private: + void sanitize() { + range(r); + range(g); + range(b); + range(a); + } + __inline void range(GLfloat& v) { + if (v < 0) v = 0; + if (v > 1) v = 1; + } +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__Color4_H__*/ diff --git a/src/client/renderer/DirtyChunkSorter.h b/src/client/renderer/DirtyChunkSorter.h index 2a5a690..57f51ed 100755 --- a/src/client/renderer/DirtyChunkSorter.h +++ b/src/client/renderer/DirtyChunkSorter.h @@ -1,35 +1,35 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__DirtyChunkSorter_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__DirtyChunkSorter_H__ - -//package net.minecraft.client.renderer; - -#include "../../world/entity/Entity.h" -#include "Chunk.h" - -class DirtyChunkSorter -{ - Entity* cameraEntity; - -public: - DirtyChunkSorter(Entity* cameraEntity) { - this->cameraEntity = cameraEntity; - } - - // @attn: c0 > c1 - bool operator() (Chunk* c0, Chunk* c1) { - bool i0 = c0->visible; - bool i1 = c1->visible; - if (i0 && !i1) return false; // 1; - if (i1 && !i0) return true; // -1; - - float d0 = c0->distanceToSqr(cameraEntity); - float d1 = c1->distanceToSqr(cameraEntity); - - if (d0 < d1) return false; // 1; - if (d0 > d1) return true; // -1; - - return c0->id > c1->id;// ? -1 : 1; - } -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__DirtyChunkSorter_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__DirtyChunkSorter_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__DirtyChunkSorter_H__ + +//package net.minecraft.client.renderer; + +#include "../../world/entity/Entity.h" +#include "Chunk.h" + +class DirtyChunkSorter +{ + Entity* cameraEntity; + +public: + DirtyChunkSorter(Entity* cameraEntity) { + this->cameraEntity = cameraEntity; + } + + // @attn: c0 > c1 + bool operator() (Chunk* c0, Chunk* c1) { + bool i0 = c0->visible; + bool i1 = c1->visible; + if (i0 && !i1) return false; // 1; + if (i1 && !i0) return true; // -1; + + float d0 = c0->distanceToSqr(cameraEntity); + float d1 = c1->distanceToSqr(cameraEntity); + + if (d0 < d1) return false; // 1; + if (d0 > d1) return true; // -1; + + return c0->id > c1->id;// ? -1 : 1; + } +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__DirtyChunkSorter_H__*/ diff --git a/src/client/renderer/DistanceChunkSorter.h b/src/client/renderer/DistanceChunkSorter.h index c4bfa9d..ea2b863 100755 --- a/src/client/renderer/DistanceChunkSorter.h +++ b/src/client/renderer/DistanceChunkSorter.h @@ -1,27 +1,27 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__DistanceChunkSorter_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__DistanceChunkSorter_H__ - -//package net.minecraft.client.renderer; - -#include "../../world/entity/Entity.h" -#include "Chunk.h" - -class DistanceChunkSorter -{ - Entity* player; - -public: - DistanceChunkSorter(Entity* player) { - this->player = player; - } - - bool operator() (const Chunk* c0, const Chunk* c1) { - float d0 = c0->distanceToSqr(player); - float d1 = c1->distanceToSqr(player); - if (d0 > 32 * 32 && c0->y < 64) d0 *= 10.0f; - if (d1 > 32 * 32 && c1->y < 64) d1 *= 10.0f; - return d0 < d1;//? -1 : 1; - } -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__DistanceChunkSorter_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__DistanceChunkSorter_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__DistanceChunkSorter_H__ + +//package net.minecraft.client.renderer; + +#include "../../world/entity/Entity.h" +#include "Chunk.h" + +class DistanceChunkSorter +{ + Entity* player; + +public: + DistanceChunkSorter(Entity* player) { + this->player = player; + } + + bool operator() (const Chunk* c0, const Chunk* c1) { + float d0 = c0->distanceToSqr(player); + float d1 = c1->distanceToSqr(player); + if (d0 > 32 * 32 && c0->y < 64) d0 *= 10.0f; + if (d1 > 32 * 32 && c1->y < 64) d1 *= 10.0f; + return d0 < d1;//? -1 : 1; + } +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__DistanceChunkSorter_H__*/ diff --git a/src/client/renderer/EntityTileRenderer.cpp b/src/client/renderer/EntityTileRenderer.cpp index d94f5d0..4d0f66b 100755 --- a/src/client/renderer/EntityTileRenderer.cpp +++ b/src/client/renderer/EntityTileRenderer.cpp @@ -1,8 +1,8 @@ -#include "EntityTileRenderer.h" - -EntityTileRenderer* EntityTileRenderer::instance = new EntityTileRenderer(); - -void EntityTileRenderer::render( Tile* tile, int data, float brightness ) -{ - TileEntityRenderDispatcher::getInstance()->render(&chest, 0, 0, 0, 0); -} +#include "EntityTileRenderer.h" + +EntityTileRenderer* EntityTileRenderer::instance = new EntityTileRenderer(); + +void EntityTileRenderer::render( Tile* tile, int data, float brightness ) +{ + TileEntityRenderDispatcher::getInstance()->render(&chest, 0, 0, 0, 0); +} diff --git a/src/client/renderer/EntityTileRenderer.h b/src/client/renderer/EntityTileRenderer.h index 9902d0f..fcb1396 100755 --- a/src/client/renderer/EntityTileRenderer.h +++ b/src/client/renderer/EntityTileRenderer.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__EntityTileRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__EntityTileRenderer_H__ - -//package net.minecraft.client.renderer; - -#include "../../world/level/tile/entity/ChestTileEntity.h" -#include "tileentity/TileEntityRenderDispatcher.h" - -class Tile; - -class EntityTileRenderer -{ -public: - static EntityTileRenderer* instance; - - void render(Tile* tile, int data, float brightness); - - ChestTileEntity chest; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__EntityTileRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__EntityTileRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__EntityTileRenderer_H__ + +//package net.minecraft.client.renderer; + +#include "../../world/level/tile/entity/ChestTileEntity.h" +#include "tileentity/TileEntityRenderDispatcher.h" + +class Tile; + +class EntityTileRenderer +{ +public: + static EntityTileRenderer* instance; + + void render(Tile* tile, int data, float brightness); + + ChestTileEntity chest; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__EntityTileRenderer_H__*/ diff --git a/src/client/renderer/GameRenderer.cpp b/src/client/renderer/GameRenderer.cpp index 1aa6a15..fee0764 100755 --- a/src/client/renderer/GameRenderer.cpp +++ b/src/client/renderer/GameRenderer.cpp @@ -1,957 +1,957 @@ -#include "GameRenderer.h" -#include "client/Options.h" -#include "gles.h" - -#include "../../util/PerfTimer.h" - -#include "LevelRenderer.h" -#include "ItemInHandRenderer.h" -#include "culling/AllowAllCuller.h" -#include "culling/FrustumCuller.h" -#include "entity/EntityRenderDispatcher.h" -#include "../Minecraft.h" -#include "../gamemode/GameMode.h" -#include "../particle/ParticleEngine.h" -#include "../player/LocalPlayer.h" -#include "../gui/Screen.h" -#include "../../world/level/Level.h" -#include "../../world/entity/Mob.h" -#include "../../world/level/chunk/ChunkCache.h" -#include "../../world/level/material/Material.h" -#include "../../world/Facing.h" -#include "../../platform/input/Controller.h" -#include "../../platform/input/Mouse.h" -#include "../../platform/input/Multitouch.h" -#include "../../NinecraftApp.h" -#include "../../world/level/tile/Tile.h" -#include "../player/input/IInputHolder.h" -#include "Textures.h" -#include "../gui/components/ImageButton.h" -#include "Tesselator.h" - -static int _shTicks = -1; - -GameRenderer::GameRenderer( Minecraft* mc ) -: mc(mc), - renderDistance(0), - _tick(0), - _lastTickT(0), - fovOffset(0), - fovOffsetO(0), - fov(1), oFov(1), - _setupCameraFov(0), - zoom(1), zoom_x(0), zoom_y(0), - cameraRoll(0), cameraRollO(0), - pickDirection(1, 0, 0), - - thirdDistance(4), thirdDistanceO(4), - thirdRotation(0), thirdRotationO(0), - thirdTilt(0), thirdTiltO(0), - - fogBr(0), fogBrO(0), - - fr(0), fg(0), fb(0), - _rotX(0), _rotY(0), - _rotXlast(0), _rotYlast(0), - useScreenScissor(false) -{ - saveMatrices(); - - itemInHandRenderer = new ItemInHandRenderer(mc); - - EntityRenderDispatcher* e = EntityRenderDispatcher::getInstance(); - e->itemInHandRenderer = itemInHandRenderer; - e->textures = mc->textures; -} - -GameRenderer::~GameRenderer() { - delete itemInHandRenderer; -} - -void renderCursor(float x, float y, Minecraft* minecraft) { - Tesselator& t = Tesselator::instance; - - minecraft->textures->loadAndBindTexture("gui/cursor.png"); - glEnable(GL_BLEND); - - const float s = 32; - const float width = 16; - const float height = 16; - t.begin(); - t.color(0xffffffff); - t.vertexUV(x, y + (float)height, 0, 0, 1); - t.vertexUV(x + (float)width, y + (float)height, 0, 1, 1); - t.vertexUV(x + (float)width, y, 0, 1, 0); - t.vertexUV(x, y, 0, 0, 0); - t.draw(); - - glDisable(GL_BLEND); -} - -/*private*/ -void GameRenderer::setupCamera(float a, int eye) { - renderDistance = (float) (16 * 16 >> (mc->options.getIntValue(OPTIONS_VIEW_DISTANCE))); -#if defined(ANDROID) - if (mc->isPowerVR() && mc->options.getIntValue(OPTIONS_VIEW_DISTANCE) <= 2) - renderDistance *= 0.8f; -#endif - - glMatrixMode(GL_PROJECTION); - glLoadIdentity2(); - - float stereoScale = 0.07f; - if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) glTranslatef2(-(eye * 2 - 1) * stereoScale, 0, 0); - if (zoom != 1) { - glTranslatef2((float) zoom_x, (float) -zoom_y, 0); - glScalef2(zoom, zoom, 1); - gluPerspective(_setupCameraFov = getFov(a, true), mc->width / (float) mc->height, 0.05f, renderDistance); - } else { - gluPerspective(_setupCameraFov = getFov(a, true), mc->width / (float) mc->height, 0.05f, renderDistance); - } - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity2(); - if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) glTranslatef2((eye * 2 - 1) * 0.10f, 0, 0); - - bobHurt(a); - if (mc->options.getBooleanValue(OPTIONS_VIEW_BOBBING)) bobView(a); - - moveCameraToPlayer(a); -} - -extern int _t_keepPic; - -/*public*/ -void GameRenderer::render(float a) { - TIMER_PUSH("mouse"); - if (mc->player && mc->mouseGrabbed) { - mc->mouseHandler.poll(); - //printf("Controller.x,y : %f,%f\n", Controller::getX(0), Controller::getY(0)); - - float ss = mc->options.getProgressValue(OPTIONS_SENSITIVITY) * 0.6f + 0.2f; - float sens = (ss * ss * ss) * 8; - float xo = mc->mouseHandler.xd * sens * 4.f; - float yo = mc->mouseHandler.yd * sens * 4.f; - - const float now = _tick + a; - float deltaT = now - _lastTickT; - if (deltaT > 3.0f) deltaT = 3.0f; - _lastTickT = now; - - _rotX += xo; - _rotY += yo; - - int yAxis = -1; - if (mc->options.getBooleanValue(OPTIONS_INVERT_Y_MOUSE)) yAxis = 1; - - bool screenCovering = mc->screen && !mc->screen->passEvents; - if (!screenCovering) - { - mc->player->turn(deltaT * _rotXlast, deltaT * _rotYlast * yAxis); - } - } - - int xMouse = (int)(Mouse::getX() * Gui::InvGuiScale); - int yMouse = (int)(Mouse::getY() * Gui::InvGuiScale); - - if (mc->useTouchscreen()) { - const int pid = Multitouch::getFirstActivePointerIdExThisUpdate(); - if (pid >= 0) { - xMouse = (int)(Multitouch::getX(pid) * Gui::InvGuiScale); - yMouse = (int)(Multitouch::getY(pid) * Gui::InvGuiScale); - } else { - xMouse = -9999; - yMouse = -9999; - } - } - TIMER_POP(); - - bool hasClearedColorBuffer = false; - bool hasSetupGuiScreen = false; - useScreenScissor = false; - if (mc->isLevelGenerated()) { - - TIMER_PUSH("level"); - if (_t_keepPic < 0) { - if (!(mc->screen && !mc->screen->renderGameBehind())) { - - if (mc->screen && mc->screen->hasClippingArea(screenScissorArea)) - useScreenScissor = true; - - renderLevel(a); - hasClearedColorBuffer = true; - - if (!mc->options.getBooleanValue(OPTIONS_HIDEGUI)) { - TIMER_POP_PUSH("gui"); - setupGuiScreen(false); - hasSetupGuiScreen = true; - mc->gui.render(a, mc->screen != NULL, xMouse, yMouse); - } - }} - TIMER_POP(); - - } else { - glViewport(0, 0, mc->width, mc->height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity2(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity2(); - setupGuiScreen(true); - hasSetupGuiScreen = true; - hasClearedColorBuffer = true; - } - //@todo - if (!hasSetupGuiScreen) - setupGuiScreen(!hasClearedColorBuffer); - - if (mc->player && mc->screen == NULL) { - if (mc->inputHolder) mc->inputHolder->render(a); - if (mc->player->input) mc->player->input->render(a); - } - - if (mc->screen != NULL) { - if (useScreenScissor) - glDisable2(GL_SCISSOR_TEST); - - mc->screen->render(xMouse, yMouse, a); - - mc->platform()->hideCursor(!mc->options.getBooleanValue(OPTIONS_RPI_CURSOR)); - if (mc->options.getBooleanValue(OPTIONS_RPI_CURSOR)) - renderCursor(xMouse, yMouse, mc); - - // Screen might have been removed, so check it again - if (mc->screen && !mc->screen->isInGameScreen()) - sleepMs(15); - } -} - -/*public*/ -void GameRenderer::renderLevel(float a) { - - if (mc->cameraTargetPlayer == NULL) { - if (mc->player) - { - mc->cameraTargetPlayer = mc->player; - } - else - { - return; - } - } - - TIMER_PUSH("pick"); - pick(a); - - Mob* cameraEntity = mc->cameraTargetPlayer; - LevelRenderer* levelRenderer = mc->levelRenderer; - ParticleEngine* particleEngine = mc->particleEngine; - float xOff = cameraEntity->xOld + (cameraEntity->x - cameraEntity->xOld) * a; - float yOff = cameraEntity->yOld + (cameraEntity->y - cameraEntity->yOld) * a; - float zOff = cameraEntity->zOld + (cameraEntity->z - cameraEntity->zOld) * a; - - for (int i = 0; i < 2; i++) { - if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) { - if (i == 0) glColorMask(false, true, true, false); - else glColorMask(true, false, false, false); - } - - TIMER_POP_PUSH("clear"); - glViewport(0, 0, mc->width, mc->height); - setupClearColor(a); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable2(GL_CULL_FACE); - - TIMER_POP_PUSH("camera"); - setupCamera(a, i); - saveMatrices(); - - if (useScreenScissor) { - glEnable2(GL_SCISSOR_TEST); - glScissor( screenScissorArea.x, screenScissorArea.y, - screenScissorArea.w, screenScissorArea.h); - } - - if(mc->options.getBooleanValue(OPTIONS_FANCY_GRAPHICS)) { - setupFog(-1); - TIMER_POP_PUSH("sky"); - glFogf(GL_FOG_START, renderDistance * 0.2f); - glFogf(GL_FOG_END, renderDistance *0.75); - levelRenderer->renderSky(a); - glFogf(GL_FOG_START, renderDistance * 0.6f); - glFogf(GL_FOG_END, renderDistance); - } - glEnable2(GL_FOG); - setupFog(1); - - if (mc->options.getBooleanValue(OPTIONS_AMBIENT_OCCLUSION)) { - glShadeModel2(GL_SMOOTH); - } - - TIMER_POP_PUSH("frustrum"); - FrustumCuller frustum; - frustum.prepare(xOff, yOff, zOff); - - TIMER_POP_PUSH("culling"); - mc->levelRenderer->cull(&frustum, a); - mc->levelRenderer->updateDirtyChunks(cameraEntity, false); - - if(mc->options.getBooleanValue(OPTIONS_FANCY_GRAPHICS)) { - prepareAndRenderClouds(levelRenderer, a); - } - - setupFog(0); - glEnable2(GL_FOG); - - mc->textures->loadAndBindTexture("terrain.png"); - glDisable2(GL_ALPHA_TEST); - glDisable2(GL_BLEND); - glEnable2(GL_CULL_FACE); - TIMER_POP_PUSH("terrain-0"); - levelRenderer->render(cameraEntity, 0, a); - - TIMER_POP_PUSH("terrain-1"); - glEnable2(GL_ALPHA_TEST); - levelRenderer->render(cameraEntity, 1, a); - - glShadeModel2(GL_FLAT); - TIMER_POP_PUSH("entities"); - mc->levelRenderer->renderEntities(cameraEntity->getPos(a), &frustum, a); -// setupFog(0); - TIMER_POP_PUSH("particles"); - particleEngine->render(cameraEntity, a); - - glDisable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - setupFog(0); - glEnable2(GL_BLEND); - glDisable2(GL_CULL_FACE); - glDepthMask(GL_FALSE); - glDisable2(GL_ALPHA_TEST); - mc->textures->loadAndBindTexture("terrain.png"); - //if (mc->options.fancyGraphics) { - // glColorMask(false, false, false, false); - // int visibleWaterChunks = levelRenderer->render(cameraEntity, 1, a); - // glColorMask(true, true, true, true); - // if (mc->options.anaglyph3d) { - // if (i == 0) glColorMask(false, true, true, false); - // else glColorMask(true, false, false, false); - // } - // if (visibleWaterChunks > 0) { - // levelRenderer->renderSameAsLast(1, a); - // } - //} else - { - //glDepthRangef(0.1f, 1.0f); - //glDepthMask(GL_FALSE); - TIMER_POP_PUSH("terrain-water"); - glEnable2(GL_DEPTH_TEST); - levelRenderer->render(cameraEntity, 2, a); - //glDepthRangef(0, 1); - - } - - glDepthMask(GL_TRUE); - glEnable2(GL_CULL_FACE); - glDisable2(GL_BLEND); - glEnable2(GL_ALPHA_TEST); - - if (/*!Minecraft::FLYBY_MODE &&*/ zoom == 1 && cameraEntity->isPlayer()) { - if (mc->hitResult.isHit() && !cameraEntity->isUnderLiquid(Material::water)) { - TIMER_POP_PUSH("select"); - Player* player = (Player*) cameraEntity; - // if (mc->useTouchscreen()) { - levelRenderer->renderHitSelect(player, mc->hitResult, 0, NULL, a); //player.inventory->getSelected(), a); - // } - levelRenderer->renderHit(player, mc->hitResult, 0, NULL, a);//player->inventory.getSelected(), a); - } - } - - glDisable2(GL_FOG); -// -// setupFog(0); -// glEnable2(GL_FOG); -//// levelRenderer->renderClouds(a); -// glDisable2(GL_FOG); - setupFog(1); - - if (zoom == 1 && !mc->options.getBooleanValue(OPTIONS_HIDEGUI)) { - TIMER_POP_PUSH("hand"); - glClear(GL_DEPTH_BUFFER_BIT); - renderItemInHand(a, i); - } - - if (!mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) { - TIMER_POP(); - return; - } - } - glColorMask(true, true, true, false); - TIMER_POP(); -} - -void GameRenderer::tickFov() { - if (mc->cameraTargetPlayer != mc->player) - return; - - oFov = fov; - fov += (mc->player->getFieldOfViewModifier() - fov) * 0.5f; -} - -/*private*/ -float GameRenderer::getFov(float a, bool applyEffects) { - Mob* player = mc->cameraTargetPlayer; - float fov = 70; - - if (applyEffects) - fov *= this->oFov + (this->fov - this->oFov) * a; - - if (player->isUnderLiquid(Material::water)) fov = 60; - if (player->health <= 0) { - float duration = player->deathTime + a; - - fov /= ((1 - 500 / (duration + 500)) * 2.0f + 1); - } - return fov + fovOffsetO + (fovOffset - fovOffsetO) * a; -} - -/*private*/ -void GameRenderer::moveCameraToPlayer(float a) { - Entity* player = mc->cameraTargetPlayer; - - float heightOffset = player->heightOffset - 1.62f; - - float x = player->xo + (player->x - player->xo) * a; - float y = player->yo + (player->y - player->yo) * a - heightOffset; - //printf("camera y: %f\n", y); - float z = player->zo + (player->z - player->zo) * a; - - //printf("rot: %f %f\n", cameraRollO, cameraRoll); - glRotatef2(cameraRollO + (cameraRoll - cameraRollO) * a, 0, 0, 1); - - //LOGI("player. alive, removed: %d, %d\n", player->isAlive(), player->removed); - if(player->isPlayer() && ((Player*)player)->isSleeping()) { - heightOffset += 1.0; - glTranslatef(0.0f, 0.3f, 0); - if (!mc->options.getBooleanValue(OPTIONS_FIXED_CAMERA)) { - int t = mc->level->getTile(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); - if (t == Tile::bed->id) { - int data = mc->level->getData(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); - - int direction = data & 3; - glRotatef(float(direction * 90), 0, 1, 0); - } - glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, -1, 0); - glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, -1, 0, 0); - } - } else if (mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW)/* || (player->isPlayer() && !player->isAlive())*/) { - float cameraDist = thirdDistanceO + (thirdDistance - thirdDistanceO) * a; - - if (mc->options.getBooleanValue(OPTIONS_FIXED_CAMERA)) { - - float rotationY = thirdRotationO + (thirdRotation - thirdRotationO) * a; - float xRot = thirdTiltO + (thirdTilt - thirdTiltO) * a; - - glTranslatef2(0, 0, (float) -cameraDist); - glRotatef2(xRot, 1, 0, 0); - glRotatef2(rotationY, 0, 1, 0); - } else { - float yRot = player->yRot; - float xRot = player->xRot/* + 180.0f*/; - float xd = -Mth::sin(yRot / 180 * Mth::PI) * Mth::cos(xRot / 180 * Mth::PI) * cameraDist; - float zd = Mth::cos(yRot / 180 * Mth::PI) * Mth::cos(xRot / 180 * Mth::PI) * cameraDist; - float yd = -Mth::sin(xRot / 180 * Mth::PI) * cameraDist; - - for (int i = 0; i < 8; i++) { - float xo = (float)((i & 1) * 2 - 1); - float yo = (float)(((i >> 1) & 1) * 2 - 1); - float zo = (float)(((i >> 2) & 1) * 2 - 1); - - xo *= 0.1f; - yo *= 0.1f; - zo *= 0.1f; - - HitResult hr = mc->level->clip(Vec3(x + xo, y + yo, z + zo), Vec3(x - xd + xo + zo, y - yd + yo, z - zd + zo)); // newTemp - if (hr.type != NO_HIT) { - float dist = hr.pos.distanceTo(Vec3(x, y, z)); // newTemp - if (dist < cameraDist) cameraDist = dist; - } - } - - //glRotatef2(180, 0, 1, 0); - - glRotatef2(player->xRot - xRot, 1, 0, 0); - glRotatef2(player->yRot - yRot, 0, 1, 0); - glTranslatef2(0, 0, (float) -cameraDist); - glRotatef2(yRot - player->yRot, 0, 1, 0); - glRotatef2(xRot - player->xRot, 1, 0, 0); - } - } else { - glTranslatef2(0, 0, -0.1f); - } - - if (!mc->options.getBooleanValue(OPTIONS_FIXED_CAMERA)) { - glRotatef2(player->xRotO + (player->xRot - player->xRotO) * a, 1.0f, 0.0f, 0.0f); - glRotatef2(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, 1, 0); - //if (_t_keepPic > 0) - } - glTranslatef2(0, heightOffset, 0); -} - -/*private*/ -void GameRenderer::bobHurt(float a) { - Mob* player = mc->cameraTargetPlayer; - - float hurt = player->hurtTime - a; - - if (player->health <= 0) { - float duration = player->deathTime + a; - glRotatef2(40 - (40 * 200) / (duration + 200), 0, 0, 1); - } - - if (player->hurtTime <= 0) return; - - hurt /= player->hurtDuration; - hurt = (float) Mth::sin(hurt * hurt * hurt * hurt * Mth::PI); - - float rr = player->hurtDir; - - glRotatef2(-rr, 0, 1, 0); - glRotatef2(-hurt * 14, 0, 0, 1); - glRotatef2(+rr, 0, 1, 0); -} - -/*private*/ -void GameRenderer::bobView(float a) { - //if (mc->options.thirdPersonView) return; - if (!(mc->cameraTargetPlayer->isPlayer())) { - return; - } - Player* player = (Player*) mc->cameraTargetPlayer; - - float wda = player->walkDist - player->walkDistO; - float b = -(player->walkDist + wda * a); - float bob = player->oBob + (player->bob - player->oBob) * a; - float tilt = player->oTilt + (player->tilt - player->oTilt) * a; - glTranslatef2((float) Mth::sin(b * Mth::PI) * bob * 0.5f, -(float) std::abs(Mth::cos(b * Mth::PI) * bob), 0); - glRotatef2((float) Mth::sin(b * Mth::PI) * bob * 3, 0, 0, 1); - glRotatef2((float) std::abs(Mth::cos(b * Mth::PI - 0.2f) * bob) * 5, 1, 0, 0); - glRotatef2((float) tilt, 1, 0, 0); -} - -/*private*/ -void GameRenderer::setupFog(int i) { - Mob* player = mc->cameraTargetPlayer; - float fogBuffer[4] = {fr, fg, fb, 1}; - - glFogfv(GL_FOG_COLOR, (GLfloat*)fogBuffer); - glColor4f2(1, 1, 1, 1); - - if (player->isUnderLiquid(Material::water)) { - glFogx(GL_FOG_MODE, GL_EXP); - glFogf(GL_FOG_DENSITY, 0.1f); // was 0.06 - -// float rr = 0.4f; -// float gg = 0.4f; -// float bb = 0.9f; -// -// if (mc->options.anaglyph3d) { -// float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; -// float ggg = (rr * 30 + gg * 70) / (100); -// float bbb = (rr * 30 + bb * 70) / (100); -// -// rr = rrr; -// gg = ggg; -// bb = bbb; -// } - } else if (player->isUnderLiquid(Material::lava)) { - glFogx(GL_FOG_MODE, GL_EXP); - glFogf(GL_FOG_DENSITY, 2.f); // was 0.06 -// float rr = 0.4f; -// float gg = 0.3f; -// float bb = 0.3f; -// -// if (mc->options.anaglyph3d) { -// float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; -// float ggg = (rr * 30 + gg * 70) / (100); -// float bbb = (rr * 30 + bb * 70) / (100); -// -// rr = rrr; -// gg = ggg; -// bb = bbb; -// } - } else { - glFogx(GL_FOG_MODE, GL_LINEAR); - glFogf(GL_FOG_START, renderDistance * 0.6f); - glFogf(GL_FOG_END, renderDistance); - if (i < 0) { - glFogf(GL_FOG_START, 0); - glFogf(GL_FOG_END, renderDistance * 1.0f); - } - - if (mc->level->dimension->foggy) { - glFogf(GL_FOG_START, 0); - } - } - - glEnable2(GL_COLOR_MATERIAL); - //glColorMaterial(GL_FRONT, GL_AMBIENT); -} - -void GameRenderer::updateAllChunks() { - mc->levelRenderer->updateDirtyChunks(mc->cameraTargetPlayer, true); -} - -bool GameRenderer::updateFreeformPickDirection(float a, Vec3& outDir) { - - if (!mc->inputHolder->allowPicking()) { - _shTicks = 1; - return false; - } - - Vec3 c = mc->cameraTargetPlayer->getPos(a); - - bool firstPerson = !mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW); - const float PickingDistance = firstPerson? 6.0f : 12.0f; - - _shTicks = -1; - - int vp[4] = {0, 0, mc->width, mc->height}; - float pt[3]; - float x = mc->inputHolder->mousex; - float y = mc->height - mc->inputHolder->mousey; - - //sw.start(); - - if (!glhUnProjectf(x, y, 1, lastModelMatrix, lastProjMatrix, vp, pt)) { - return false; - } - Vec3 p1(pt[0] + c.x, pt[1] + c.y, pt[2] + c.z); - - glhUnProjectf(x, y, 0, lastModelMatrix, lastProjMatrix, vp, pt); - Vec3 p0(pt[0] + c.x, pt[1] + c.y, pt[2] + c.z); - - outDir = (p1 - p0).normalized(); - p1 = p0 + outDir * PickingDistance; - - //sw.stop(); - //sw.printEvery(30, "unproject "); - - const HitResult& hit = mc->hitResult = mc->level->clip(p0, p1, false); - - // If in 3rd person view - verify that the hit target is within range - if (!firstPerson && hit.isHit()) { - const float MaxSqrDist = PickingDistance*PickingDistance; - if (mc->cameraTargetPlayer->distanceToSqr((float)hit.x, (float)hit.y, (float)hit.z) > MaxSqrDist) - mc->hitResult.type = NO_HIT; - } - return true; -} - -/*public*/ -void GameRenderer::pick(float a) { - if (mc->level == NULL) return; - if (mc->cameraTargetPlayer == NULL) return; - if (!mc->cameraTargetPlayer->isAlive()) return; - - float range = mc->gameMode->getPickRange(); - bool isPicking = true; - - bool freeform = mc->useTouchscreen(); //&& !mc->options.getBooleanValue(OPTIONS_IS_JOY_TOUCH_AREA); - - if (freeform) { - isPicking = updateFreeformPickDirection(a, pickDirection); - } else { - mc->hitResult = mc->cameraTargetPlayer->pick(range, a); - pickDirection = mc->cameraTargetPlayer->getViewVector(a); - } - - Vec3 from = mc->cameraTargetPlayer->getPos(a); - float dist = range; - if (mc->hitResult.isHit()) { - dist = mc->hitResult.pos.distanceTo(from); - } - - if (mc->gameMode->isCreativeType()) { - /*dist =*/ range = 12; - } else { - if (dist > 3) dist = 3; - range = dist; - } - - Vec3 pv = (pickDirection * range); - Vec3 to = from + pv; - mc->cameraTargetPlayer->aimDirection = pickDirection; - - Entity* hovered = NULL; - const float g = 1; - AABB aabb = mc->cameraTargetPlayer->bb.expand(pv.x, pv.y, pv.z).grow(g, g, g); - EntityList& objects = mc->level->getEntities(mc->cameraTargetPlayer, aabb); - float nearest = 0; - for (unsigned int i = 0; i < objects.size(); i++) { - Entity* e = objects[i]; - if (!e->isPickable()) continue; - - float rr = e->getPickRadius(); - AABB bb = e->bb.grow(rr, rr, rr); - HitResult p = bb.clip(from, to); - //printf("Clip Hitresult %d (%d)\n", p.type, p.isHit()); - - if (bb.contains(from)) { - //@todo: hovered = e; break; ? - if (nearest >= 0) { - hovered = e; - nearest = 0; - } - } else if (p.isHit()) { - float dd = from.distanceTo(p.pos); - if (dd < nearest || nearest == 0) { - hovered = e; - nearest = dd; - } - } - } - - if (hovered != NULL) { - if(nearest < dist) { - mc->hitResult = HitResult(hovered); - } - } - else if (isPicking && !mc->hitResult.isHit()) { - // if we don't have a hit result, attempt to hit the edge of the block we are standing on - // (this is an pocket edition simplification to help building floors) - //LOGI("hovered : %d (%f)\n", mc->hitResult.type, viewVec.y); - if (pickDirection.y < -.7f) { - // looking down by more than roughly 45 degrees, fetch a hit to the block standing on - Vec3 to = from.add(0, -2.0f, 0); - - HitResult downHitResult = mc->level->clip(from, to); - if (downHitResult.isHit()) { - mc->hitResult = downHitResult; - mc->hitResult.indirectHit = true; - // change face (not up) - if (std::abs(pickDirection.x) > std::abs(pickDirection.z)) { - mc->hitResult.f = (pickDirection.x < 0)? 4 : 5; - } else { - mc->hitResult.f = (pickDirection.z < 0)? 2 : 3; - } - } - } - } -} -/*public*/ -void GameRenderer::tick(int nTick, int maxTick) { - --_t_keepPic; - - if (!mc->player) - { - return; - } - - if (--_shTicks == 0) - mc->hitResult.type = NO_HIT; - - //_rotXlast = _rotX; - //_rotYlast = _rotY; - - //LOGI("x: %f\n", _rotX); - - if (nTick == maxTick) { - const float tickMult = 1.0f / (float)(1 + maxTick); - _rotXlast = 0.4f * std::pow(std::abs(_rotX), 1.2f) * tickMult; - if (_rotX < 0) _rotXlast = -_rotXlast; - - _rotYlast = 0.4f * std::pow(std::abs(_rotY), 1.2f) * tickMult; - if (_rotY < 0) _rotYlast = -_rotYlast; - - _rotX = 0; - _rotY = 0; - } - - fogBrO = fogBr; - thirdDistanceO = thirdDistance; - thirdRotationO = thirdRotation; - thirdTiltO = thirdTilt; - fovOffsetO = fovOffset; - cameraRollO = cameraRoll; - - if (mc->cameraTargetPlayer == NULL) { - mc->cameraTargetPlayer = mc->player; - } - - tickFov(); - - float brr = mc->level->getBrightness( Mth::floor(mc->cameraTargetPlayer->x), - Mth::floor(mc->cameraTargetPlayer->y), - Mth::floor(mc->cameraTargetPlayer->z)); - - float whiteness = (3 - mc->options.getIntValue(OPTIONS_VIEW_DISTANCE)) / 3.0f; - float fogBrT = brr * (1 - whiteness) + whiteness; - fogBr += (fogBrT - fogBr) * 0.1f; - - _tick++; - - itemInHandRenderer->tick(); -// if (mc->isRaining) tickRain(); -} - -/*private*/ -void GameRenderer::setupClearColor(float a) { - Level* level = mc->level; - Mob* player = mc->cameraTargetPlayer; - - float whiteness = 1.0f / (4 - mc->options.getIntValue(OPTIONS_VIEW_DISTANCE)); - whiteness = 1 - (float) pow(whiteness, 0.25f); - - Vec3 skyColor = level->getSkyColor(mc->cameraTargetPlayer, a); - float sr = (float) skyColor.x; - float sg = (float) skyColor.y; - float sb = (float) skyColor.z; - - Vec3 fogColor = level->getFogColor(a); - fr = (float) fogColor.x; - fg = (float) fogColor.y; - fb = (float) fogColor.z; - - fr += (sr - fr) * whiteness; - fg += (sg - fg) * whiteness; - fb += (sb - fb) * whiteness; - - if (player->isUnderLiquid(Material::water)) { - fr = 0.02f; - fg = 0.02f; - fb = 0.2f; - } else if (player->isUnderLiquid(Material::lava)) { - fr = 0.6f; - fg = 0.1f; - fb = 0.00f; - } - - float brr = fogBrO + (fogBr - fogBrO) * a; - fr *= brr; - fg *= brr; - fb *= brr; - - if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) { - float frr = (fr * 30 + fg * 59 + fb * 11) / 100; - float fgg = (fr * 30 + fg * 70) / (100); - float fbb = (fr * 30 + fb * 70) / (100); - - fr = frr; - fg = fgg; - fb = fbb; - } - - glClearColor(fr, fg, fb, 1.0f); -} - -void GameRenderer::zoomRegion( float zoom, float xa, float ya ) -{ - this->zoom = zoom; - this->zoom_x = xa; - this->zoom_y = ya; -} - -void GameRenderer::unZoomRegion() -{ - zoom = 1; -} - -void GameRenderer::setupGuiScreen( bool clearColorBuffer ) -{ - int screenWidth = (int)(mc->width * Gui::InvGuiScale); - int screenHeight = (int)(mc->height * Gui::InvGuiScale); - - // Setup GUI render mode - GLbitfield clearBits = clearColorBuffer? - GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT - : GL_DEPTH_BUFFER_BIT; - glClear(clearBits); - glMatrixMode(GL_PROJECTION); - glLoadIdentity2(); - glOrthof(0, (GLfloat)screenWidth, (GLfloat)screenHeight, 0, 2000, 3000); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity2(); - glTranslatef2(0, 0, -2000); -} - -/*private*/ -void GameRenderer::renderItemInHand(float a, int eye) { - glLoadIdentity2(); - if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) glTranslatef2((eye * 2 - 1) * 0.10f, 0, 0); - - glPushMatrix2(); - bobHurt(a); - if (mc->options.getBooleanValue(OPTIONS_VIEW_BOBBING)) bobView(a); - - if (!mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW) && (mc->cameraTargetPlayer->isPlayer() && !((Player*)mc->cameraTargetPlayer)->isSleeping())) { - if (!mc->options.getBooleanValue(OPTIONS_HIDEGUI)) { - float fov = getFov(a, false); - if (fov != _setupCameraFov) { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(fov, mc->width / (float) mc->height, 0.05f, renderDistance); - glMatrixMode(GL_MODELVIEW); - } - itemInHandRenderer->render(a); - } - } - - glPopMatrix2(); - if (!mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW) && (mc->cameraTargetPlayer->isPlayer() && !((Player*)mc->cameraTargetPlayer)->isSleeping())) { - itemInHandRenderer->renderScreenEffect(a); - bobHurt(a); - } - if (mc->options.getBooleanValue(OPTIONS_VIEW_BOBBING)) bobView(a); -} - -void GameRenderer::onGraphicsReset() -{ - if (itemInHandRenderer) itemInHandRenderer->onGraphicsReset(); -} - -void GameRenderer::saveMatrices() -{ - #if defined(RPI) - return; - #endif - - static bool saved = false; - //if (saved) return; - - saved = true; - - glGetFloatv(GL_PROJECTION_MATRIX, lastProjMatrix); - glGetFloatv(GL_MODELVIEW_MATRIX, lastModelMatrix); -} - -void GameRenderer::prepareAndRenderClouds( LevelRenderer* levelRenderer, float a ) { - //if(mc->options.isCloudsOn()) { - TIMER_PUSH("clouds"); - glMatrixMode(GL_PROJECTION); - glPushMatrix2(); - glLoadIdentity2(); - gluPerspective(_setupCameraFov = getFov(a, true), mc->width / (float) mc->height, 2, renderDistance * 512); - glMatrixMode(GL_MODELVIEW); - glPushMatrix2(); - setupFog(0); - glDepthMask(false); - glEnable2(GL_FOG); - glFogf(GL_FOG_START, renderDistance * 0.2f); - glFogf(GL_FOG_END, renderDistance * 0.75f); - levelRenderer->renderSky(a); - glFogf(GL_FOG_START, renderDistance * 4.2f * 0.6f); - glFogf(GL_FOG_END, renderDistance * 4.2f); - levelRenderer->renderClouds(a); - glFogf(GL_FOG_START, renderDistance * 0.6f); - glFogf(GL_FOG_END, renderDistance); - glDisable2(GL_FOG); - glDepthMask(true); - setupFog(1); - glPopMatrix2(); - glMatrixMode(GL_PROJECTION); - glPopMatrix2(); - glMatrixMode(GL_MODELVIEW); - TIMER_POP(); - //} -} +#include "GameRenderer.h" +#include "client/Options.h" +#include "gles.h" + +#include "../../util/PerfTimer.h" + +#include "LevelRenderer.h" +#include "ItemInHandRenderer.h" +#include "culling/AllowAllCuller.h" +#include "culling/FrustumCuller.h" +#include "entity/EntityRenderDispatcher.h" +#include "../Minecraft.h" +#include "../gamemode/GameMode.h" +#include "../particle/ParticleEngine.h" +#include "../player/LocalPlayer.h" +#include "../gui/Screen.h" +#include "../../world/level/Level.h" +#include "../../world/entity/Mob.h" +#include "../../world/level/chunk/ChunkCache.h" +#include "../../world/level/material/Material.h" +#include "../../world/Facing.h" +#include "../../platform/input/Controller.h" +#include "../../platform/input/Mouse.h" +#include "../../platform/input/Multitouch.h" +#include "../../NinecraftApp.h" +#include "../../world/level/tile/Tile.h" +#include "../player/input/IInputHolder.h" +#include "Textures.h" +#include "../gui/components/ImageButton.h" +#include "Tesselator.h" + +static int _shTicks = -1; + +GameRenderer::GameRenderer( Minecraft* mc ) +: mc(mc), + renderDistance(0), + _tick(0), + _lastTickT(0), + fovOffset(0), + fovOffsetO(0), + fov(1), oFov(1), + _setupCameraFov(0), + zoom(1), zoom_x(0), zoom_y(0), + cameraRoll(0), cameraRollO(0), + pickDirection(1, 0, 0), + + thirdDistance(4), thirdDistanceO(4), + thirdRotation(0), thirdRotationO(0), + thirdTilt(0), thirdTiltO(0), + + fogBr(0), fogBrO(0), + + fr(0), fg(0), fb(0), + _rotX(0), _rotY(0), + _rotXlast(0), _rotYlast(0), + useScreenScissor(false) +{ + saveMatrices(); + + itemInHandRenderer = new ItemInHandRenderer(mc); + + EntityRenderDispatcher* e = EntityRenderDispatcher::getInstance(); + e->itemInHandRenderer = itemInHandRenderer; + e->textures = mc->textures; +} + +GameRenderer::~GameRenderer() { + delete itemInHandRenderer; +} + +void renderCursor(float x, float y, Minecraft* minecraft) { + Tesselator& t = Tesselator::instance; + + minecraft->textures->loadAndBindTexture("gui/cursor.png"); + glEnable(GL_BLEND); + + const float s = 32; + const float width = 16; + const float height = 16; + t.begin(); + t.color(0xffffffff); + t.vertexUV(x, y + (float)height, 0, 0, 1); + t.vertexUV(x + (float)width, y + (float)height, 0, 1, 1); + t.vertexUV(x + (float)width, y, 0, 1, 0); + t.vertexUV(x, y, 0, 0, 0); + t.draw(); + + glDisable(GL_BLEND); +} + +/*private*/ +void GameRenderer::setupCamera(float a, int eye) { + renderDistance = (float) (16 * 16 >> (mc->options.getIntValue(OPTIONS_VIEW_DISTANCE))); +#if defined(ANDROID) + if (mc->isPowerVR() && mc->options.getIntValue(OPTIONS_VIEW_DISTANCE) <= 2) + renderDistance *= 0.8f; +#endif + + glMatrixMode(GL_PROJECTION); + glLoadIdentity2(); + + float stereoScale = 0.07f; + if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) glTranslatef2(-(eye * 2 - 1) * stereoScale, 0, 0); + if (zoom != 1) { + glTranslatef2((float) zoom_x, (float) -zoom_y, 0); + glScalef2(zoom, zoom, 1); + gluPerspective(_setupCameraFov = getFov(a, true), mc->width / (float) mc->height, 0.05f, renderDistance); + } else { + gluPerspective(_setupCameraFov = getFov(a, true), mc->width / (float) mc->height, 0.05f, renderDistance); + } + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity2(); + if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) glTranslatef2((eye * 2 - 1) * 0.10f, 0, 0); + + bobHurt(a); + if (mc->options.getBooleanValue(OPTIONS_VIEW_BOBBING)) bobView(a); + + moveCameraToPlayer(a); +} + +extern int _t_keepPic; + +/*public*/ +void GameRenderer::render(float a) { + TIMER_PUSH("mouse"); + if (mc->player && mc->mouseGrabbed) { + mc->mouseHandler.poll(); + //printf("Controller.x,y : %f,%f\n", Controller::getX(0), Controller::getY(0)); + + float ss = mc->options.getProgressValue(OPTIONS_SENSITIVITY) * 0.6f + 0.2f; + float sens = (ss * ss * ss) * 8; + float xo = mc->mouseHandler.xd * sens * 4.f; + float yo = mc->mouseHandler.yd * sens * 4.f; + + const float now = _tick + a; + float deltaT = now - _lastTickT; + if (deltaT > 3.0f) deltaT = 3.0f; + _lastTickT = now; + + _rotX += xo; + _rotY += yo; + + int yAxis = -1; + if (mc->options.getBooleanValue(OPTIONS_INVERT_Y_MOUSE)) yAxis = 1; + + bool screenCovering = mc->screen && !mc->screen->passEvents; + if (!screenCovering) + { + mc->player->turn(deltaT * _rotXlast, deltaT * _rotYlast * yAxis); + } + } + + int xMouse = (int)(Mouse::getX() * Gui::InvGuiScale); + int yMouse = (int)(Mouse::getY() * Gui::InvGuiScale); + + if (mc->useTouchscreen()) { + const int pid = Multitouch::getFirstActivePointerIdExThisUpdate(); + if (pid >= 0) { + xMouse = (int)(Multitouch::getX(pid) * Gui::InvGuiScale); + yMouse = (int)(Multitouch::getY(pid) * Gui::InvGuiScale); + } else { + xMouse = -9999; + yMouse = -9999; + } + } + TIMER_POP(); + + bool hasClearedColorBuffer = false; + bool hasSetupGuiScreen = false; + useScreenScissor = false; + if (mc->isLevelGenerated()) { + + TIMER_PUSH("level"); + if (_t_keepPic < 0) { + if (!(mc->screen && !mc->screen->renderGameBehind())) { + + if (mc->screen && mc->screen->hasClippingArea(screenScissorArea)) + useScreenScissor = true; + + renderLevel(a); + hasClearedColorBuffer = true; + + if (!mc->options.getBooleanValue(OPTIONS_HIDEGUI)) { + TIMER_POP_PUSH("gui"); + setupGuiScreen(false); + hasSetupGuiScreen = true; + mc->gui.render(a, mc->screen != NULL, xMouse, yMouse); + } + }} + TIMER_POP(); + + } else { + glViewport(0, 0, mc->width, mc->height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity2(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity2(); + setupGuiScreen(true); + hasSetupGuiScreen = true; + hasClearedColorBuffer = true; + } + //@todo + if (!hasSetupGuiScreen) + setupGuiScreen(!hasClearedColorBuffer); + + if (mc->player && mc->screen == NULL) { + if (mc->inputHolder) mc->inputHolder->render(a); + if (mc->player->input) mc->player->input->render(a); + } + + if (mc->screen != NULL) { + if (useScreenScissor) + glDisable2(GL_SCISSOR_TEST); + + mc->screen->render(xMouse, yMouse, a); + + mc->platform()->hideCursor(!mc->options.getBooleanValue(OPTIONS_RPI_CURSOR)); + if (mc->options.getBooleanValue(OPTIONS_RPI_CURSOR)) + renderCursor(xMouse, yMouse, mc); + + // Screen might have been removed, so check it again + if (mc->screen && !mc->screen->isInGameScreen()) + sleepMs(15); + } +} + +/*public*/ +void GameRenderer::renderLevel(float a) { + + if (mc->cameraTargetPlayer == NULL) { + if (mc->player) + { + mc->cameraTargetPlayer = mc->player; + } + else + { + return; + } + } + + TIMER_PUSH("pick"); + pick(a); + + Mob* cameraEntity = mc->cameraTargetPlayer; + LevelRenderer* levelRenderer = mc->levelRenderer; + ParticleEngine* particleEngine = mc->particleEngine; + float xOff = cameraEntity->xOld + (cameraEntity->x - cameraEntity->xOld) * a; + float yOff = cameraEntity->yOld + (cameraEntity->y - cameraEntity->yOld) * a; + float zOff = cameraEntity->zOld + (cameraEntity->z - cameraEntity->zOld) * a; + + for (int i = 0; i < 2; i++) { + if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) { + if (i == 0) glColorMask(false, true, true, false); + else glColorMask(true, false, false, false); + } + + TIMER_POP_PUSH("clear"); + glViewport(0, 0, mc->width, mc->height); + setupClearColor(a); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable2(GL_CULL_FACE); + + TIMER_POP_PUSH("camera"); + setupCamera(a, i); + saveMatrices(); + + if (useScreenScissor) { + glEnable2(GL_SCISSOR_TEST); + glScissor( screenScissorArea.x, screenScissorArea.y, + screenScissorArea.w, screenScissorArea.h); + } + + if(mc->options.getBooleanValue(OPTIONS_FANCY_GRAPHICS)) { + setupFog(-1); + TIMER_POP_PUSH("sky"); + glFogf(GL_FOG_START, renderDistance * 0.2f); + glFogf(GL_FOG_END, renderDistance *0.75); + levelRenderer->renderSky(a); + glFogf(GL_FOG_START, renderDistance * 0.6f); + glFogf(GL_FOG_END, renderDistance); + } + glEnable2(GL_FOG); + setupFog(1); + + if (mc->options.getBooleanValue(OPTIONS_AMBIENT_OCCLUSION)) { + glShadeModel2(GL_SMOOTH); + } + + TIMER_POP_PUSH("frustrum"); + FrustumCuller frustum; + frustum.prepare(xOff, yOff, zOff); + + TIMER_POP_PUSH("culling"); + mc->levelRenderer->cull(&frustum, a); + mc->levelRenderer->updateDirtyChunks(cameraEntity, false); + + if(mc->options.getBooleanValue(OPTIONS_FANCY_GRAPHICS)) { + prepareAndRenderClouds(levelRenderer, a); + } + + setupFog(0); + glEnable2(GL_FOG); + + mc->textures->loadAndBindTexture("terrain.png"); + glDisable2(GL_ALPHA_TEST); + glDisable2(GL_BLEND); + glEnable2(GL_CULL_FACE); + TIMER_POP_PUSH("terrain-0"); + levelRenderer->render(cameraEntity, 0, a); + + TIMER_POP_PUSH("terrain-1"); + glEnable2(GL_ALPHA_TEST); + levelRenderer->render(cameraEntity, 1, a); + + glShadeModel2(GL_FLAT); + TIMER_POP_PUSH("entities"); + mc->levelRenderer->renderEntities(cameraEntity->getPos(a), &frustum, a); +// setupFog(0); + TIMER_POP_PUSH("particles"); + particleEngine->render(cameraEntity, a); + + glDisable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + setupFog(0); + glEnable2(GL_BLEND); + glDisable2(GL_CULL_FACE); + glDepthMask(GL_FALSE); + glDisable2(GL_ALPHA_TEST); + mc->textures->loadAndBindTexture("terrain.png"); + //if (mc->options.fancyGraphics) { + // glColorMask(false, false, false, false); + // int visibleWaterChunks = levelRenderer->render(cameraEntity, 1, a); + // glColorMask(true, true, true, true); + // if (mc->options.anaglyph3d) { + // if (i == 0) glColorMask(false, true, true, false); + // else glColorMask(true, false, false, false); + // } + // if (visibleWaterChunks > 0) { + // levelRenderer->renderSameAsLast(1, a); + // } + //} else + { + //glDepthRangef(0.1f, 1.0f); + //glDepthMask(GL_FALSE); + TIMER_POP_PUSH("terrain-water"); + glEnable2(GL_DEPTH_TEST); + levelRenderer->render(cameraEntity, 2, a); + //glDepthRangef(0, 1); + + } + + glDepthMask(GL_TRUE); + glEnable2(GL_CULL_FACE); + glDisable2(GL_BLEND); + glEnable2(GL_ALPHA_TEST); + + if (/*!Minecraft::FLYBY_MODE &&*/ zoom == 1 && cameraEntity->isPlayer()) { + if (mc->hitResult.isHit() && !cameraEntity->isUnderLiquid(Material::water)) { + TIMER_POP_PUSH("select"); + Player* player = (Player*) cameraEntity; + // if (mc->useTouchscreen()) { + levelRenderer->renderHitSelect(player, mc->hitResult, 0, NULL, a); //player.inventory->getSelected(), a); + // } + levelRenderer->renderHit(player, mc->hitResult, 0, NULL, a);//player->inventory.getSelected(), a); + } + } + + glDisable2(GL_FOG); +// +// setupFog(0); +// glEnable2(GL_FOG); +//// levelRenderer->renderClouds(a); +// glDisable2(GL_FOG); + setupFog(1); + + if (zoom == 1 && !mc->options.getBooleanValue(OPTIONS_HIDEGUI)) { + TIMER_POP_PUSH("hand"); + glClear(GL_DEPTH_BUFFER_BIT); + renderItemInHand(a, i); + } + + if (!mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) { + TIMER_POP(); + return; + } + } + glColorMask(true, true, true, false); + TIMER_POP(); +} + +void GameRenderer::tickFov() { + if (mc->cameraTargetPlayer != mc->player) + return; + + oFov = fov; + fov += (mc->player->getFieldOfViewModifier() - fov) * 0.5f; +} + +/*private*/ +float GameRenderer::getFov(float a, bool applyEffects) { + Mob* player = mc->cameraTargetPlayer; + float fov = 70; + + if (applyEffects) + fov *= this->oFov + (this->fov - this->oFov) * a; + + if (player->isUnderLiquid(Material::water)) fov = 60; + if (player->health <= 0) { + float duration = player->deathTime + a; + + fov /= ((1 - 500 / (duration + 500)) * 2.0f + 1); + } + return fov + fovOffsetO + (fovOffset - fovOffsetO) * a; +} + +/*private*/ +void GameRenderer::moveCameraToPlayer(float a) { + Entity* player = mc->cameraTargetPlayer; + + float heightOffset = player->heightOffset - 1.62f; + + float x = player->xo + (player->x - player->xo) * a; + float y = player->yo + (player->y - player->yo) * a - heightOffset; + //printf("camera y: %f\n", y); + float z = player->zo + (player->z - player->zo) * a; + + //printf("rot: %f %f\n", cameraRollO, cameraRoll); + glRotatef2(cameraRollO + (cameraRoll - cameraRollO) * a, 0, 0, 1); + + //LOGI("player. alive, removed: %d, %d\n", player->isAlive(), player->removed); + if(player->isPlayer() && ((Player*)player)->isSleeping()) { + heightOffset += 1.0; + glTranslatef(0.0f, 0.3f, 0); + if (!mc->options.getBooleanValue(OPTIONS_FIXED_CAMERA)) { + int t = mc->level->getTile(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + if (t == Tile::bed->id) { + int data = mc->level->getData(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + + int direction = data & 3; + glRotatef(float(direction * 90), 0, 1, 0); + } + glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, -1, 0); + glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, -1, 0, 0); + } + } else if (mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW)/* || (player->isPlayer() && !player->isAlive())*/) { + float cameraDist = thirdDistanceO + (thirdDistance - thirdDistanceO) * a; + + if (mc->options.getBooleanValue(OPTIONS_FIXED_CAMERA)) { + + float rotationY = thirdRotationO + (thirdRotation - thirdRotationO) * a; + float xRot = thirdTiltO + (thirdTilt - thirdTiltO) * a; + + glTranslatef2(0, 0, (float) -cameraDist); + glRotatef2(xRot, 1, 0, 0); + glRotatef2(rotationY, 0, 1, 0); + } else { + float yRot = player->yRot; + float xRot = player->xRot/* + 180.0f*/; + float xd = -Mth::sin(yRot / 180 * Mth::PI) * Mth::cos(xRot / 180 * Mth::PI) * cameraDist; + float zd = Mth::cos(yRot / 180 * Mth::PI) * Mth::cos(xRot / 180 * Mth::PI) * cameraDist; + float yd = -Mth::sin(xRot / 180 * Mth::PI) * cameraDist; + + for (int i = 0; i < 8; i++) { + float xo = (float)((i & 1) * 2 - 1); + float yo = (float)(((i >> 1) & 1) * 2 - 1); + float zo = (float)(((i >> 2) & 1) * 2 - 1); + + xo *= 0.1f; + yo *= 0.1f; + zo *= 0.1f; + + HitResult hr = mc->level->clip(Vec3(x + xo, y + yo, z + zo), Vec3(x - xd + xo + zo, y - yd + yo, z - zd + zo)); // newTemp + if (hr.type != NO_HIT) { + float dist = hr.pos.distanceTo(Vec3(x, y, z)); // newTemp + if (dist < cameraDist) cameraDist = dist; + } + } + + //glRotatef2(180, 0, 1, 0); + + glRotatef2(player->xRot - xRot, 1, 0, 0); + glRotatef2(player->yRot - yRot, 0, 1, 0); + glTranslatef2(0, 0, (float) -cameraDist); + glRotatef2(yRot - player->yRot, 0, 1, 0); + glRotatef2(xRot - player->xRot, 1, 0, 0); + } + } else { + glTranslatef2(0, 0, -0.1f); + } + + if (!mc->options.getBooleanValue(OPTIONS_FIXED_CAMERA)) { + glRotatef2(player->xRotO + (player->xRot - player->xRotO) * a, 1.0f, 0.0f, 0.0f); + glRotatef2(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, 1, 0); + //if (_t_keepPic > 0) + } + glTranslatef2(0, heightOffset, 0); +} + +/*private*/ +void GameRenderer::bobHurt(float a) { + Mob* player = mc->cameraTargetPlayer; + + float hurt = player->hurtTime - a; + + if (player->health <= 0) { + float duration = player->deathTime + a; + glRotatef2(40 - (40 * 200) / (duration + 200), 0, 0, 1); + } + + if (player->hurtTime <= 0) return; + + hurt /= player->hurtDuration; + hurt = (float) Mth::sin(hurt * hurt * hurt * hurt * Mth::PI); + + float rr = player->hurtDir; + + glRotatef2(-rr, 0, 1, 0); + glRotatef2(-hurt * 14, 0, 0, 1); + glRotatef2(+rr, 0, 1, 0); +} + +/*private*/ +void GameRenderer::bobView(float a) { + //if (mc->options.thirdPersonView) return; + if (!(mc->cameraTargetPlayer->isPlayer())) { + return; + } + Player* player = (Player*) mc->cameraTargetPlayer; + + float wda = player->walkDist - player->walkDistO; + float b = -(player->walkDist + wda * a); + float bob = player->oBob + (player->bob - player->oBob) * a; + float tilt = player->oTilt + (player->tilt - player->oTilt) * a; + glTranslatef2((float) Mth::sin(b * Mth::PI) * bob * 0.5f, -(float) std::abs(Mth::cos(b * Mth::PI) * bob), 0); + glRotatef2((float) Mth::sin(b * Mth::PI) * bob * 3, 0, 0, 1); + glRotatef2((float) std::abs(Mth::cos(b * Mth::PI - 0.2f) * bob) * 5, 1, 0, 0); + glRotatef2((float) tilt, 1, 0, 0); +} + +/*private*/ +void GameRenderer::setupFog(int i) { + Mob* player = mc->cameraTargetPlayer; + float fogBuffer[4] = {fr, fg, fb, 1}; + + glFogfv(GL_FOG_COLOR, (GLfloat*)fogBuffer); + glColor4f2(1, 1, 1, 1); + + if (player->isUnderLiquid(Material::water)) { + glFogx(GL_FOG_MODE, GL_EXP); + glFogf(GL_FOG_DENSITY, 0.1f); // was 0.06 + +// float rr = 0.4f; +// float gg = 0.4f; +// float bb = 0.9f; +// +// if (mc->options.anaglyph3d) { +// float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; +// float ggg = (rr * 30 + gg * 70) / (100); +// float bbb = (rr * 30 + bb * 70) / (100); +// +// rr = rrr; +// gg = ggg; +// bb = bbb; +// } + } else if (player->isUnderLiquid(Material::lava)) { + glFogx(GL_FOG_MODE, GL_EXP); + glFogf(GL_FOG_DENSITY, 2.f); // was 0.06 +// float rr = 0.4f; +// float gg = 0.3f; +// float bb = 0.3f; +// +// if (mc->options.anaglyph3d) { +// float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; +// float ggg = (rr * 30 + gg * 70) / (100); +// float bbb = (rr * 30 + bb * 70) / (100); +// +// rr = rrr; +// gg = ggg; +// bb = bbb; +// } + } else { + glFogx(GL_FOG_MODE, GL_LINEAR); + glFogf(GL_FOG_START, renderDistance * 0.6f); + glFogf(GL_FOG_END, renderDistance); + if (i < 0) { + glFogf(GL_FOG_START, 0); + glFogf(GL_FOG_END, renderDistance * 1.0f); + } + + if (mc->level->dimension->foggy) { + glFogf(GL_FOG_START, 0); + } + } + + glEnable2(GL_COLOR_MATERIAL); + //glColorMaterial(GL_FRONT, GL_AMBIENT); +} + +void GameRenderer::updateAllChunks() { + mc->levelRenderer->updateDirtyChunks(mc->cameraTargetPlayer, true); +} + +bool GameRenderer::updateFreeformPickDirection(float a, Vec3& outDir) { + + if (!mc->inputHolder->allowPicking()) { + _shTicks = 1; + return false; + } + + Vec3 c = mc->cameraTargetPlayer->getPos(a); + + bool firstPerson = !mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW); + const float PickingDistance = firstPerson? 6.0f : 12.0f; + + _shTicks = -1; + + int vp[4] = {0, 0, mc->width, mc->height}; + float pt[3]; + float x = mc->inputHolder->mousex; + float y = mc->height - mc->inputHolder->mousey; + + //sw.start(); + + if (!glhUnProjectf(x, y, 1, lastModelMatrix, lastProjMatrix, vp, pt)) { + return false; + } + Vec3 p1(pt[0] + c.x, pt[1] + c.y, pt[2] + c.z); + + glhUnProjectf(x, y, 0, lastModelMatrix, lastProjMatrix, vp, pt); + Vec3 p0(pt[0] + c.x, pt[1] + c.y, pt[2] + c.z); + + outDir = (p1 - p0).normalized(); + p1 = p0 + outDir * PickingDistance; + + //sw.stop(); + //sw.printEvery(30, "unproject "); + + const HitResult& hit = mc->hitResult = mc->level->clip(p0, p1, false); + + // If in 3rd person view - verify that the hit target is within range + if (!firstPerson && hit.isHit()) { + const float MaxSqrDist = PickingDistance*PickingDistance; + if (mc->cameraTargetPlayer->distanceToSqr((float)hit.x, (float)hit.y, (float)hit.z) > MaxSqrDist) + mc->hitResult.type = NO_HIT; + } + return true; +} + +/*public*/ +void GameRenderer::pick(float a) { + if (mc->level == NULL) return; + if (mc->cameraTargetPlayer == NULL) return; + if (!mc->cameraTargetPlayer->isAlive()) return; + + float range = mc->gameMode->getPickRange(); + bool isPicking = true; + + bool freeform = mc->useTouchscreen(); //&& !mc->options.getBooleanValue(OPTIONS_IS_JOY_TOUCH_AREA); + + if (freeform) { + isPicking = updateFreeformPickDirection(a, pickDirection); + } else { + mc->hitResult = mc->cameraTargetPlayer->pick(range, a); + pickDirection = mc->cameraTargetPlayer->getViewVector(a); + } + + Vec3 from = mc->cameraTargetPlayer->getPos(a); + float dist = range; + if (mc->hitResult.isHit()) { + dist = mc->hitResult.pos.distanceTo(from); + } + + if (mc->gameMode->isCreativeType()) { + /*dist =*/ range = 12; + } else { + if (dist > 3) dist = 3; + range = dist; + } + + Vec3 pv = (pickDirection * range); + Vec3 to = from + pv; + mc->cameraTargetPlayer->aimDirection = pickDirection; + + Entity* hovered = NULL; + const float g = 1; + AABB aabb = mc->cameraTargetPlayer->bb.expand(pv.x, pv.y, pv.z).grow(g, g, g); + EntityList& objects = mc->level->getEntities(mc->cameraTargetPlayer, aabb); + float nearest = 0; + for (unsigned int i = 0; i < objects.size(); i++) { + Entity* e = objects[i]; + if (!e->isPickable()) continue; + + float rr = e->getPickRadius(); + AABB bb = e->bb.grow(rr, rr, rr); + HitResult p = bb.clip(from, to); + //printf("Clip Hitresult %d (%d)\n", p.type, p.isHit()); + + if (bb.contains(from)) { + //@todo: hovered = e; break; ? + if (nearest >= 0) { + hovered = e; + nearest = 0; + } + } else if (p.isHit()) { + float dd = from.distanceTo(p.pos); + if (dd < nearest || nearest == 0) { + hovered = e; + nearest = dd; + } + } + } + + if (hovered != NULL) { + if(nearest < dist) { + mc->hitResult = HitResult(hovered); + } + } + else if (isPicking && !mc->hitResult.isHit()) { + // if we don't have a hit result, attempt to hit the edge of the block we are standing on + // (this is an pocket edition simplification to help building floors) + //LOGI("hovered : %d (%f)\n", mc->hitResult.type, viewVec.y); + if (pickDirection.y < -.7f) { + // looking down by more than roughly 45 degrees, fetch a hit to the block standing on + Vec3 to = from.add(0, -2.0f, 0); + + HitResult downHitResult = mc->level->clip(from, to); + if (downHitResult.isHit()) { + mc->hitResult = downHitResult; + mc->hitResult.indirectHit = true; + // change face (not up) + if (std::abs(pickDirection.x) > std::abs(pickDirection.z)) { + mc->hitResult.f = (pickDirection.x < 0)? 4 : 5; + } else { + mc->hitResult.f = (pickDirection.z < 0)? 2 : 3; + } + } + } + } +} +/*public*/ +void GameRenderer::tick(int nTick, int maxTick) { + --_t_keepPic; + + if (!mc->player) + { + return; + } + + if (--_shTicks == 0) + mc->hitResult.type = NO_HIT; + + //_rotXlast = _rotX; + //_rotYlast = _rotY; + + //LOGI("x: %f\n", _rotX); + + if (nTick == maxTick) { + const float tickMult = 1.0f / (float)(1 + maxTick); + _rotXlast = 0.4f * std::pow(std::abs(_rotX), 1.2f) * tickMult; + if (_rotX < 0) _rotXlast = -_rotXlast; + + _rotYlast = 0.4f * std::pow(std::abs(_rotY), 1.2f) * tickMult; + if (_rotY < 0) _rotYlast = -_rotYlast; + + _rotX = 0; + _rotY = 0; + } + + fogBrO = fogBr; + thirdDistanceO = thirdDistance; + thirdRotationO = thirdRotation; + thirdTiltO = thirdTilt; + fovOffsetO = fovOffset; + cameraRollO = cameraRoll; + + if (mc->cameraTargetPlayer == NULL) { + mc->cameraTargetPlayer = mc->player; + } + + tickFov(); + + float brr = mc->level->getBrightness( Mth::floor(mc->cameraTargetPlayer->x), + Mth::floor(mc->cameraTargetPlayer->y), + Mth::floor(mc->cameraTargetPlayer->z)); + + float whiteness = (3 - mc->options.getIntValue(OPTIONS_VIEW_DISTANCE)) / 3.0f; + float fogBrT = brr * (1 - whiteness) + whiteness; + fogBr += (fogBrT - fogBr) * 0.1f; + + _tick++; + + itemInHandRenderer->tick(); +// if (mc->isRaining) tickRain(); +} + +/*private*/ +void GameRenderer::setupClearColor(float a) { + Level* level = mc->level; + Mob* player = mc->cameraTargetPlayer; + + float whiteness = 1.0f / (4 - mc->options.getIntValue(OPTIONS_VIEW_DISTANCE)); + whiteness = 1 - (float) pow(whiteness, 0.25f); + + Vec3 skyColor = level->getSkyColor(mc->cameraTargetPlayer, a); + float sr = (float) skyColor.x; + float sg = (float) skyColor.y; + float sb = (float) skyColor.z; + + Vec3 fogColor = level->getFogColor(a); + fr = (float) fogColor.x; + fg = (float) fogColor.y; + fb = (float) fogColor.z; + + fr += (sr - fr) * whiteness; + fg += (sg - fg) * whiteness; + fb += (sb - fb) * whiteness; + + if (player->isUnderLiquid(Material::water)) { + fr = 0.02f; + fg = 0.02f; + fb = 0.2f; + } else if (player->isUnderLiquid(Material::lava)) { + fr = 0.6f; + fg = 0.1f; + fb = 0.00f; + } + + float brr = fogBrO + (fogBr - fogBrO) * a; + fr *= brr; + fg *= brr; + fb *= brr; + + if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) { + float frr = (fr * 30 + fg * 59 + fb * 11) / 100; + float fgg = (fr * 30 + fg * 70) / (100); + float fbb = (fr * 30 + fb * 70) / (100); + + fr = frr; + fg = fgg; + fb = fbb; + } + + glClearColor(fr, fg, fb, 1.0f); +} + +void GameRenderer::zoomRegion( float zoom, float xa, float ya ) +{ + this->zoom = zoom; + this->zoom_x = xa; + this->zoom_y = ya; +} + +void GameRenderer::unZoomRegion() +{ + zoom = 1; +} + +void GameRenderer::setupGuiScreen( bool clearColorBuffer ) +{ + int screenWidth = (int)(mc->width * Gui::InvGuiScale); + int screenHeight = (int)(mc->height * Gui::InvGuiScale); + + // Setup GUI render mode + GLbitfield clearBits = clearColorBuffer? + GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT + : GL_DEPTH_BUFFER_BIT; + glClear(clearBits); + glMatrixMode(GL_PROJECTION); + glLoadIdentity2(); + glOrthof(0, (GLfloat)screenWidth, (GLfloat)screenHeight, 0, 2000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity2(); + glTranslatef2(0, 0, -2000); +} + +/*private*/ +void GameRenderer::renderItemInHand(float a, int eye) { + glLoadIdentity2(); + if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) glTranslatef2((eye * 2 - 1) * 0.10f, 0, 0); + + glPushMatrix2(); + bobHurt(a); + if (mc->options.getBooleanValue(OPTIONS_VIEW_BOBBING)) bobView(a); + + if (!mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW) && (mc->cameraTargetPlayer->isPlayer() && !((Player*)mc->cameraTargetPlayer)->isSleeping())) { + if (!mc->options.getBooleanValue(OPTIONS_HIDEGUI)) { + float fov = getFov(a, false); + if (fov != _setupCameraFov) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(fov, mc->width / (float) mc->height, 0.05f, renderDistance); + glMatrixMode(GL_MODELVIEW); + } + itemInHandRenderer->render(a); + } + } + + glPopMatrix2(); + if (!mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW) && (mc->cameraTargetPlayer->isPlayer() && !((Player*)mc->cameraTargetPlayer)->isSleeping())) { + itemInHandRenderer->renderScreenEffect(a); + bobHurt(a); + } + if (mc->options.getBooleanValue(OPTIONS_VIEW_BOBBING)) bobView(a); +} + +void GameRenderer::onGraphicsReset() +{ + if (itemInHandRenderer) itemInHandRenderer->onGraphicsReset(); +} + +void GameRenderer::saveMatrices() +{ + #if defined(RPI) + return; + #endif + + static bool saved = false; + //if (saved) return; + + saved = true; + + glGetFloatv(GL_PROJECTION_MATRIX, lastProjMatrix); + glGetFloatv(GL_MODELVIEW_MATRIX, lastModelMatrix); +} + +void GameRenderer::prepareAndRenderClouds( LevelRenderer* levelRenderer, float a ) { + //if(mc->options.isCloudsOn()) { + TIMER_PUSH("clouds"); + glMatrixMode(GL_PROJECTION); + glPushMatrix2(); + glLoadIdentity2(); + gluPerspective(_setupCameraFov = getFov(a, true), mc->width / (float) mc->height, 2, renderDistance * 512); + glMatrixMode(GL_MODELVIEW); + glPushMatrix2(); + setupFog(0); + glDepthMask(false); + glEnable2(GL_FOG); + glFogf(GL_FOG_START, renderDistance * 0.2f); + glFogf(GL_FOG_END, renderDistance * 0.75f); + levelRenderer->renderSky(a); + glFogf(GL_FOG_START, renderDistance * 4.2f * 0.6f); + glFogf(GL_FOG_END, renderDistance * 4.2f); + levelRenderer->renderClouds(a); + glFogf(GL_FOG_START, renderDistance * 0.6f); + glFogf(GL_FOG_END, renderDistance); + glDisable2(GL_FOG); + glDepthMask(true); + setupFog(1); + glPopMatrix2(); + glMatrixMode(GL_PROJECTION); + glPopMatrix2(); + glMatrixMode(GL_MODELVIEW); + TIMER_POP(); + //} +} diff --git a/src/client/renderer/GameRenderer.h b/src/client/renderer/GameRenderer.h index ca8d7ec..e82660c 100755 --- a/src/client/renderer/GameRenderer.h +++ b/src/client/renderer/GameRenderer.h @@ -1,117 +1,117 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__GameRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__GameRenderer_H__ - -//package net.minecraft.client.renderer; - -#include "gles.h" -#include -#include "../../util/SmoothFloat.h" -#include "../../world/phys/Vec3.h" -#include "../gui/components/ImageButton.h" - -class Minecraft; -class Entity; -class ItemInHandRenderer; -class LevelRenderer; -class GameRenderer -{ -public: - GameRenderer(Minecraft* mc_); - ~GameRenderer(); - - void pick(float a); - - void updateAllChunks(); - - void zoomRegion(float zoom, float xa, float ya); - void unZoomRegion(); - void setupGuiScreen(bool clearColorBuffer); - - void tick(int nTick, int maxTick); - void render(float a); - void renderLevel(float a); - void renderItemInHand(float a, int eye); - - void onGraphicsReset(); - - void setupCamera(float a, int eye); - void moveCameraToPlayer(float a); - - void setupClearColor(float a); - float getFov(float a, bool applyEffects); -private: - void setupFog(int i); - - void tickFov(); - - - void bobHurt(float a); - void bobView(float a); - - bool updateFreeformPickDirection(float a, Vec3& outDir); - void prepareAndRenderClouds(LevelRenderer* levelRenderer, float a); - -public: - ItemInHandRenderer* itemInHandRenderer; - -private: - Minecraft* mc; - - float renderDistance; - int _tick; - Vec3 pickDirection; - - // smooth camera movement - SmoothFloat smoothTurnX; - SmoothFloat smoothTurnY; - - // // third-person distance etc - // SmoothFloat smoothDistance = /*new*/ SmoothFloat(); - // SmoothFloat smoothRotation = /*new*/ SmoothFloat(); - // SmoothFloat smoothTilt = /*new*/ SmoothFloat(); - // SmoothFloat smoothRoll = /*new*/ SmoothFloat(); - - float thirdDistance; - float thirdDistanceO; - float thirdRotation; - float thirdRotationO; - float thirdTilt; - float thirdTiltO; - - // zoom - float zoom; - float zoom_x; - float zoom_y; - - // fov modification - float fov, oFov; - float fovOffset; - float fovOffsetO; - float _setupCameraFov; - - // roll modification - float cameraRoll; - float cameraRollO; - - float fr; - float fg; - float fb; - - float fogBrO, fogBr; - - float _rotX; - float _rotY; - float _rotXlast; - float _rotYlast; - float _lastTickT; - - void saveMatrices(); - float lastProjMatrix[16]; - float lastModelMatrix[16]; - - // Scissor area that Minecraft::screen defines - bool useScreenScissor; - IntRectangle screenScissorArea; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__GameRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__GameRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__GameRenderer_H__ + +//package net.minecraft.client.renderer; + +#include "gles.h" +#include +#include "../../util/SmoothFloat.h" +#include "../../world/phys/Vec3.h" +#include "../gui/components/ImageButton.h" + +class Minecraft; +class Entity; +class ItemInHandRenderer; +class LevelRenderer; +class GameRenderer +{ +public: + GameRenderer(Minecraft* mc_); + ~GameRenderer(); + + void pick(float a); + + void updateAllChunks(); + + void zoomRegion(float zoom, float xa, float ya); + void unZoomRegion(); + void setupGuiScreen(bool clearColorBuffer); + + void tick(int nTick, int maxTick); + void render(float a); + void renderLevel(float a); + void renderItemInHand(float a, int eye); + + void onGraphicsReset(); + + void setupCamera(float a, int eye); + void moveCameraToPlayer(float a); + + void setupClearColor(float a); + float getFov(float a, bool applyEffects); +private: + void setupFog(int i); + + void tickFov(); + + + void bobHurt(float a); + void bobView(float a); + + bool updateFreeformPickDirection(float a, Vec3& outDir); + void prepareAndRenderClouds(LevelRenderer* levelRenderer, float a); + +public: + ItemInHandRenderer* itemInHandRenderer; + +private: + Minecraft* mc; + + float renderDistance; + int _tick; + Vec3 pickDirection; + + // smooth camera movement + SmoothFloat smoothTurnX; + SmoothFloat smoothTurnY; + + // // third-person distance etc + // SmoothFloat smoothDistance = /*new*/ SmoothFloat(); + // SmoothFloat smoothRotation = /*new*/ SmoothFloat(); + // SmoothFloat smoothTilt = /*new*/ SmoothFloat(); + // SmoothFloat smoothRoll = /*new*/ SmoothFloat(); + + float thirdDistance; + float thirdDistanceO; + float thirdRotation; + float thirdRotationO; + float thirdTilt; + float thirdTiltO; + + // zoom + float zoom; + float zoom_x; + float zoom_y; + + // fov modification + float fov, oFov; + float fovOffset; + float fovOffsetO; + float _setupCameraFov; + + // roll modification + float cameraRoll; + float cameraRollO; + + float fr; + float fg; + float fb; + + float fogBrO, fogBr; + + float _rotX; + float _rotY; + float _rotXlast; + float _rotYlast; + float _lastTickT; + + void saveMatrices(); + float lastProjMatrix[16]; + float lastModelMatrix[16]; + + // Scissor area that Minecraft::screen defines + bool useScreenScissor; + IntRectangle screenScissorArea; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__GameRenderer_H__*/ diff --git a/src/client/renderer/ItemInHandRenderer.cpp b/src/client/renderer/ItemInHandRenderer.cpp index f279a9c..46a305a 100755 --- a/src/client/renderer/ItemInHandRenderer.cpp +++ b/src/client/renderer/ItemInHandRenderer.cpp @@ -1,533 +1,533 @@ -#include "ItemInHandRenderer.h" - -#include "gles.h" -#include "Tesselator.h" -#include "entity/EntityRenderDispatcher.h" -#include "entity/EntityRenderer.h" -#include "entity/MobRenderer.h" -#include "../Minecraft.h" -#include "../player/LocalPlayer.h" -#include "../../world/entity/player/Player.h" -#include "../../world/item/Item.h" -#include "../../world/level/material/Material.h" -#include "../../world/level/tile/Tile.h" -#include "../../world/level/Level.h" -#include "../../util/Mth.h" -#include "../../world/entity/player/Inventory.h" - -#include "../../platform/time.h" -#include "Textures.h" -#include "../../world/item/UseAnim.h" -#include "../../world/item/BowItem.h" -#include "../../world/level/tile/LeafTile.h" -#include "entity/HumanoidMobRenderer.h" - -//static StopwatchHandler handler; - -ItemInHandRenderer::ItemInHandRenderer( Minecraft* mc ) -: mc(mc), - lastSlot(-1), - height(0), - oHeight(0), - lastIconRendered(0), - lastItemRendered(0), - //selectedItem(NULL), - item(0, 1, 0) -{ - GLuint ids[MaxNumRenderObjects]; - glGenBuffers2(MaxNumRenderObjects, ids); - - for (int i = 0; i < MaxNumRenderObjects; ++i) { - renderObjects[i].itemId = -1; - renderObjects[i].chunk.vboId = ids[i]; - //LOGI("IDS: %d\n", ids[i]); - } - - //LOGI("SizeOf :%d (256 * %d)\n", sizeof(renderObjects), sizeof(RenderCall)); -} - -void ItemInHandRenderer::tick() -{ - oHeight = height; - item.id = 0; - - ItemInstance* itemInHand = mc->player->inventory->getSelected(); - if (itemInHand && itemInHand->count > 0) { - item.id = itemInHand->id; - item.setAuxValue(itemInHand->getAuxValue()); - } - - float max = 0.4f; - float tHeight = 1;//matches ? 1 : 0; - float dd = tHeight - height; - if (dd < -max) dd = -max; - if (dd > max) dd = max; - - height += dd; -} - -void ItemInHandRenderer::renderItem(Mob* mob, ItemInstance* item ) -{ - //Stopwatch& w = handler.get("item:" + Tile::tiles[item->id]->getDescriptionId()); - //w.start(); - - int itemId = item->id; - // Ugly hack, but what the heck, I don't have time at the moment - // Use spare slots between 200 and 255 (for now) for items with different - // graphics per data. @note: Check Tile.cpp for needed IDs to override - if (itemId == Tile::cloth->id) { - itemId = 200 + item->getAuxValue(); // 200 to 215 - } else if (itemId == Tile::treeTrunk->id) { - itemId = 216 + item->getAuxValue(); // 216 to 219 @treeTrunk - } else if (itemId == Tile::sapling->id) { - itemId = 220 + item->getAuxValue(); // 220 to 223 @sapling - } else if (itemId == Tile::stoneSlabHalf->id) { - itemId = 224 + item->getAuxValue(); // 224 to 231 @stoneslab - } else if (itemId == ((Tile*)Tile::leaves)->id) { - itemId = 232 + (item->getAuxValue() & LeafTile::LEAF_TYPE_MASK); // 232 to 235 @leaves - } - - RenderCall& renderObject = renderObjects[itemId]; - int itemIcon = item->getIcon(); - if(mob != NULL) { - itemIcon = mob->getItemInHandIcon(item, 0); - } - - bool reTesselate(false); - if(itemIcon != lastIconRendered && lastItemRendered == itemId) - reTesselate = true; - lastItemRendered = itemId; - lastIconRendered = itemIcon; - //const int aux = item->getAuxValue(); - - if (renderObject.itemId == -1 || reTesselate) { - if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { - Tesselator& t = Tesselator::instance; - t.beginOverride(); - - int renderedItemId = item->id; - if (renderedItemId == ((Tile*)Tile::leaves)->id) - renderedItemId = ((Tile*)Tile::leaves_carried)->id; - if (renderedItemId == Tile::grass->id) - renderedItemId = Tile::grass_carried->id; - tileRenderer.renderTile(Tile::tiles[renderedItemId], item->getAuxValue()); - renderObject.chunk = t.endOverride(renderObject.chunk.vboId); - - renderObject.texture = "terrain.png"; - renderObject.itemId = itemId; - renderObject.isFlat = false; - } else { - renderObject.itemId = itemId; - renderObject.isFlat = true; - - if (item->id < 256) { - renderObject.texture = "terrain.png"; - //mc->textures->loadAndBindTexture("terrain.png"); - } else { - renderObject.texture = "gui/items.png"; - //mc->textures->loadAndBindTexture("gui/items.png"); - } - // glDisable2(GL_LIGHTING); - Tesselator& t = Tesselator::instance; - t.beginOverride(); - t.color(0xff, 0xff, 0xff); - const int up = itemIcon & 15; - const int vp = itemIcon >> 4; - float u1 = (up * 16 + 0.00f) / 256.0f; - float u0 = (up * 16 + 15.99f) / 256.0f; - float v0 = (vp * 16 + 0.00f) / 256.0f; - float v1 = (vp * 16 + 15.99f) / 256.0f; - - float r = 1.0f; -// float xo = 0.0f; -// float yo = 0.3f; -/* - //glEnable2(GL_RESCALE_NORMAL); - glTranslatef2(-xo, -yo, 0); - float s = 1.5f; - glScalef2(s, s, s); - - glRotatef2(50, 0, 1, 0); - glRotatef2(45 + 290, 0, 0, 1); - glTranslatef2(-15 / 16.0f, -1 / 16.0f, 0); -*/ - float dd = 1 / 16.0f; - - t.vertexUV(0, 0, 0, u0, v1); - t.vertexUV(r, 0, 0, u1, v1); - t.vertexUV(r, 1, 0, u1, v0); - t.vertexUV(0, 1, 0, u0, v0); - - t.vertexUV(0, 1, 0 - dd, u0, v0); - t.vertexUV(r, 1, 0 - dd, u1, v0); - t.vertexUV(r, 0, 0 - dd, u1, v1); - t.vertexUV(0, 0, 0 - dd, u0, v1); - - for (int i = 0; i < 16; i++) { - float p = i / 16.0f; - float uu = u0 + (u1 - u0) * p - 0.5f / 256.0f; - float xx = r * p; - t.vertexUV(xx, 0, 0 - dd, uu, v1); - t.vertexUV(xx, 0, 0, uu, v1); - t.vertexUV(xx, 1, 0, uu, v0); - t.vertexUV(xx, 1, 0 - dd, uu, v0); - } - for (int i = 0; i < 16; i++) { - float p = i / 16.0f; - float uu = u0 + (u1 - u0) * p - 0.5f / 256.0f; - float xx = r * p + 1 / 16.0f; - t.vertexUV(xx, 1, 0 - dd, uu, v0); - t.vertexUV(xx, 1, 0, uu, v0); - t.vertexUV(xx, 0, 0, uu, v1); - t.vertexUV(xx, 0, 0 - dd, uu, v1); - } - for (int i = 0; i < 16; i++) { - float p = i / 16.0f; - float vv = v1 + (v0 - v1) * p - 0.5f / 256.0f; - float yy = r * p + 1 / 16.0f; - t.vertexUV(0, yy, 0, u0, vv); - t.vertexUV(r, yy, 0, u1, vv); - t.vertexUV(r, yy, 0 - dd, u1, vv); - t.vertexUV(0, yy, 0 - dd, u0, vv); - } - for (int i = 0; i < 16; i++) { - float p = i / 16.0f; - float vv = v1 + (v0 - v1) * p - 0.5f / 256.0f; - float yy = r * p; - t.vertexUV(r, yy, 0, u1, vv); - t.vertexUV(0, yy, 0, u0, vv); - t.vertexUV(0, yy, 0 - dd, u0, vv); - t.vertexUV(r, yy, 0 - dd, u1, vv); - } - renderObject.chunk = t.endOverride(renderObject.chunk.vboId); - } - } - - if (renderObject.itemId >= 0) { - if (renderObject.isFlat) { - float xo = 0.0f; - float yo = 0.3f; - - glPushMatrix2(); - glTranslatef2(-xo, -yo, 0); - float s = 1.5f; - glScalef2(s, s, s); - - glRotatef2(50, 0, 1, 0); - glRotatef2(45 + 290, 0, 0, 1); - glTranslatef2(-15 / 16.0f, -1 / 16.0f, 0); - } - mc->textures->loadAndBindTexture(renderObject.texture); - - drawArrayVT_NoState(renderObject.chunk.vboId, renderObject.chunk.vertexCount); - if (renderObject.isFlat) - glPopMatrix2(); - } - //w.stop(); - //handler.printEvery(100); -} - -void ItemInHandRenderer::render( float a ) -{ - //return; - - //Stopwatch& w = handler.get("render"); - //w.start(); - - float h = oHeight + (height - oHeight) * a; - Player* player = mc->player; - // if (selectedTile==NULL) return; - - glPushMatrix2(); - glRotatef2(player->xRotO + (player->xRot - player->xRotO) * a, 1, 0, 0); - glRotatef2(player->yRotO + (player->yRot - player->yRotO) * a, 0, 1, 0); - glPopMatrix2(); - - float br = mc->level->getBrightness(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); - - ItemInstance* item;// = selectedItem; - //if (player.fishing != NULL) { - // item = /*new*/ ItemInstance(Item.stick); - //} - - if (this->item.id > 0) - item = &this->item; - else - item = NULL; - - if (item != NULL) { - glColor4f2(br, br, br, 1); - bool isUsing = player->getUseItemDuration() > 0; - bool isBow = item->id == Item::bow->id; - bool noSwingAnimation = isUsing && (isBow || (item->getUseAnimation() != UseAnim::none)); - const float swing = noSwingAnimation ? 0 : player->getAttackAnim(a); - const float sqrtSwing = Mth::sqrt(swing); - const float swing3 = Mth::sin(swing * swing * Mth::PI); - const float swing1 = Mth::sin(swing * Mth::PI); - const float swing2 = Mth::sin(sqrtSwing * Mth::PI); - const float d = 0.8f; - glPushMatrix2(); - - if (isUsing) { - UseAnim::UseAnimation anim = item->getUseAnimation(); - if (anim == UseAnim::eat || anim == UseAnim::drink) { - float t = (player->getUseItemDuration() - a + 1); - float useProgress = 1 - (t / item->getUseDuration()); - - float swing = useProgress; - float is = 1 - swing; - is = is * is * is; - is = is * is * is; - is = is * is * is; - float iss = 1 - is; - glTranslatef(0, Mth::abs(Mth::cos(t / 4 * Mth::PI) * 0.1f) * (swing > 0.2 ? 1 : 0), 0); - glTranslatef(iss * 0.6f, -iss * 0.5f, 0); - glRotatef(iss * 90, 0, 1, 0); - glRotatef(iss * 10, 1, 0, 0); - glRotatef(iss * 30, 0, 0, 1); - } - } else { - glTranslatef2(-swing2 * 0.4f, (float) Mth::sin(sqrtSwing * Mth::PI * 2) * 0.2f, -swing1 * 0.2f); - } - - glTranslatef2(0.7f * d, -0.65f * d - (1 - h) * 0.6f, -0.9f * d); - - glRotatef2(45, 0, 1, 0); - //glEnable2(GL_RESCALE_NORMAL); - glRotatef2(-swing3 * 20, 0, 1, 0); - glRotatef2(-swing2 * 20, 0, 0, 1); - glRotatef2(-swing2 * 80, 1, 0, 0); - float ss = 0.4f; - glScalef2(ss, ss, ss); - if (player->getUseItemDuration() > 0) { - UseAnim::UseAnimation anim = item->getUseAnimation(); - if(anim == UseAnim::bow) { - glRotatef(-18, 0, 0, 1); - glRotatef(-12, 0, 1, 0); - glRotatef(-8, 1, 0, 0); - glTranslatef(-0.9f, 0.2f, 0.0f); - float timeHeld = (item->getUseDuration() - (player->getUseItemDuration() - a + 1)); - float pow = timeHeld / (float) (BowItem::MAX_DRAW_DURATION); - pow = ((pow * pow) + pow * 2) / 3; - if (pow > 1) pow = 1; - if (pow > 0.1f) { - glTranslatef(0, Mth::sin((timeHeld - 0.1f) * 1.3f) * 0.01f * (pow - 0.1f), 0); - } - glTranslatef(0, 0, pow * 0.1f); - - glRotatef(-45 - 290, 0, 0, 1); - glRotatef(-50, 0, 1, 0); - glTranslatef(0, 0.5f, 0); - float ys = 1 + pow * 0.2f; - glScalef(1, 1, ys); - glTranslatef(0, -0.5f, 0); - glRotatef(50, 0, 1, 0); - glRotatef(45 + 290, 0, 0, 1); - } - } - if (item->getItem()->isMirroredArt()) { - glRotatef2(180, 0, 1, 0); - } - glEnableClientState2(GL_VERTEX_ARRAY); - glEnableClientState2(GL_TEXTURE_COORD_ARRAY); - renderItem(player, item); - glDisableClientState2(GL_VERTEX_ARRAY); - glDisableClientState2(GL_TEXTURE_COORD_ARRAY); - glPopMatrix2(); - } else { - glColor4f2(br, br, br, 1); - - glPushMatrix2(); - float d = 0.8f; - const float swing = player->getAttackAnim(a); - const float sqrtSwing = Mth::sqrt(swing); - const float swing3 = Mth::sin(swing * swing * Mth::PI); - const float swing1 = Mth::sin(swing * Mth::PI); - const float swing2 = Mth::sin(sqrtSwing * Mth::PI); - - glTranslatef2(-swing2 * 0.3f, (float) Mth::sin(Mth::sqrt(swing) * Mth::PI * 2) * 0.4f, -swing1 * 0.4f); - glTranslatef2(0.8f * d, -0.75f * d - (1 - h) * 0.6f, -0.9f * d); - - glRotatef2(45, 0, 1, 0); - //glEnable2(GL_RESCALE_NORMAL); - glRotatef2(swing2 * 70, 0, 1, 0); - glRotatef2(-swing3 * 20, 0, 0, 1); - // glRotatef2(-swing2 * 80, 1, 0, 0); - - mc->textures->loadAndBindTexture(player->getTexture()); - glTranslatef2(-1.0f, +3.6f, +3.5f); - glRotatef2(120, 0, 0, 1); - glRotatef2(180 + 20, 1, 0, 0); - glRotatef2(-90 - 45, 0, 1, 0); - glScalef2(1.5f / 24.0f * 16, 1.5f / 24.0f * 16, 1.5f / 24.0f * 16); - glTranslatef2(5.6f, 0, 0); - - EntityRenderer* er = EntityRenderDispatcher::getInstance()->getRenderer(mc->player); - HumanoidMobRenderer* playerRenderer = (HumanoidMobRenderer*) er; - float ss = 1; - glScalef2(ss, ss, ss); - playerRenderer->renderHand(); - glPopMatrix2(); - } - //glDisable2(GL_RESCALE_NORMAL); - //Lighting.turnOff(); - //w.stop(); -} - -void ItemInHandRenderer::renderScreenEffect( float a ) -{ - glDisable2(GL_ALPHA_TEST); - if (mc->player->isOnFire()) { - mc->textures->loadAndBindTexture("terrain.png"); - renderFire(a); - } - - if (mc->player->isInWall()) // Inside a tile - { - int x = Mth::floor(mc->player->x); - int y = Mth::floor(mc->player->y); - int z = Mth::floor(mc->player->z); - - mc->textures->loadAndBindTexture("terrain.png"); - int tile = mc->level->getTile(x, y, z); - if (Tile::tiles[tile] != NULL) { - renderTex(a, Tile::tiles[tile]->getTexture(2)); - } - } - - // if (mc->player->isUnderLiquid(Material::water)) { - //mc->textures->loadAndBindTexture("misc/water.png"); - // renderWater(a); - // } - glEnable2(GL_ALPHA_TEST); -} - -void ItemInHandRenderer::itemPlaced() -{ - height = 0; -} - -void ItemInHandRenderer::itemUsed() -{ - height = 0; -} - -void ItemInHandRenderer::renderTex( float a, int tex ) -{ - Tesselator& t = Tesselator::instance; - - float br;// = mc->player->getBrightness(a); - br = 0.1f; - glColor4f2(br, br, br, 0.5f); - - glPushMatrix2(); - - float x0 = -1; - float x1 = +1; - float y0 = -1; - float y1 = +1; - float z0 = -0.5f; - - float r = 2 / 256.0f; - float u0 = (tex % 16) / 256.0f - r; - float u1 = (tex % 16 + 15.99f) / 256.0f + r; - float v0 = (tex / 16) / 256.0f - r; - float v1 = (tex / 16 + 15.99f) / 256.0f + r; - - t.begin(); - t.vertexUV(x0, y0, z0, u1, v1); - t.vertexUV(x1, y0, z0, u0, v1); - t.vertexUV(x1, y1, z0, u0, v0); - t.vertexUV(x0, y1, z0, u1, v0); - //t.end(); - t.draw(); - glPopMatrix2(); - - glColor4f2(1, 1, 1, 1); -} - -void ItemInHandRenderer::renderWater( float a ) -{ - Tesselator& t = Tesselator::instance; - - float br = mc->player->getBrightness(a); - glColor4f2(br, br, br, 0.5f); - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glPushMatrix2(); - - float size = 4; - - float x0 = -1; - float x1 = +1; - float y0 = -1; - float y1 = +1; - float z0 = -0.5f; - - float uo = -mc->player->yRot / 64.0f; - float vo = +mc->player->xRot / 64.0f; - - t.begin(); - t.vertexUV(x0, y0, z0, size + uo, size + vo); - t.vertexUV(x1, y0, z0, 0 + uo, size + vo); - t.vertexUV(x1, y1, z0, 0 + uo, 0 + vo); - t.vertexUV(x0, y1, z0, size + uo, 0 + vo); - //t.end(); - t.draw(); - glPopMatrix2(); - - glColor4f2(1, 1, 1, 1); - glDisable2(GL_BLEND); -} - -void ItemInHandRenderer::renderFire( float a ) -{ - Tesselator& t = Tesselator::instance; - glColor4f2(1, 1, 1, 0.9f); - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - float size = 1; - for (int i = 0; i < 2; i++) { - glPushMatrix2(); - int tex = ((Tile*)Tile::fire)->tex + i * 16; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - - float u0 = (xt) / 256.0f; - float u1 = (xt + 15.99f) / 256.0f; - float v0 = (yt) / 256.0f; - float v1 = (yt + 15.99f) / 256.0f; - - float x0 = (0 - size) / 2; - float x1 = x0 + size; - float y0 = 0 - size / 2; - float y1 = y0 + size; - float z0 = -0.5f; - glTranslatef2(-(i * 2 - 1) * 0.24f, -0.3f, 0); - glRotatef2((i * 2 - 1) * 10.f, 0, 1, 0); - - t.begin(); - t.vertexUV(x0, y0, z0, u1, v1); - t.vertexUV(x1, y0, z0, u0, v1); - t.vertexUV(x1, y1, z0, u0, v0); - t.vertexUV(x0, y1, z0, u1, v0); - //t.end(); - t.draw(); - glPopMatrix2(); - } - glColor4f2(1, 1, 1, 1); - glDisable2(GL_BLEND); -} - -void ItemInHandRenderer::onGraphicsReset() -{ - GLuint ids[MaxNumRenderObjects]; - glGenBuffers2(MaxNumRenderObjects, ids); - - for (int i = 0; i < MaxNumRenderObjects; ++i) { - renderObjects[i].itemId = -1; - renderObjects[i].chunk.vboId = ids[i]; - } -} +#include "ItemInHandRenderer.h" + +#include "gles.h" +#include "Tesselator.h" +#include "entity/EntityRenderDispatcher.h" +#include "entity/EntityRenderer.h" +#include "entity/MobRenderer.h" +#include "../Minecraft.h" +#include "../player/LocalPlayer.h" +#include "../../world/entity/player/Player.h" +#include "../../world/item/Item.h" +#include "../../world/level/material/Material.h" +#include "../../world/level/tile/Tile.h" +#include "../../world/level/Level.h" +#include "../../util/Mth.h" +#include "../../world/entity/player/Inventory.h" + +#include "../../platform/time.h" +#include "Textures.h" +#include "../../world/item/UseAnim.h" +#include "../../world/item/BowItem.h" +#include "../../world/level/tile/LeafTile.h" +#include "entity/HumanoidMobRenderer.h" + +//static StopwatchHandler handler; + +ItemInHandRenderer::ItemInHandRenderer( Minecraft* mc ) +: mc(mc), + lastSlot(-1), + height(0), + oHeight(0), + lastIconRendered(0), + lastItemRendered(0), + //selectedItem(NULL), + item(0, 1, 0) +{ + GLuint ids[MaxNumRenderObjects]; + glGenBuffers2(MaxNumRenderObjects, ids); + + for (int i = 0; i < MaxNumRenderObjects; ++i) { + renderObjects[i].itemId = -1; + renderObjects[i].chunk.vboId = ids[i]; + //LOGI("IDS: %d\n", ids[i]); + } + + //LOGI("SizeOf :%d (256 * %d)\n", sizeof(renderObjects), sizeof(RenderCall)); +} + +void ItemInHandRenderer::tick() +{ + oHeight = height; + item.id = 0; + + ItemInstance* itemInHand = mc->player->inventory->getSelected(); + if (itemInHand && itemInHand->count > 0) { + item.id = itemInHand->id; + item.setAuxValue(itemInHand->getAuxValue()); + } + + float max = 0.4f; + float tHeight = 1;//matches ? 1 : 0; + float dd = tHeight - height; + if (dd < -max) dd = -max; + if (dd > max) dd = max; + + height += dd; +} + +void ItemInHandRenderer::renderItem(Mob* mob, ItemInstance* item ) +{ + //Stopwatch& w = handler.get("item:" + Tile::tiles[item->id]->getDescriptionId()); + //w.start(); + + int itemId = item->id; + // Ugly hack, but what the heck, I don't have time at the moment + // Use spare slots between 200 and 255 (for now) for items with different + // graphics per data. @note: Check Tile.cpp for needed IDs to override + if (itemId == Tile::cloth->id) { + itemId = 200 + item->getAuxValue(); // 200 to 215 + } else if (itemId == Tile::treeTrunk->id) { + itemId = 216 + item->getAuxValue(); // 216 to 219 @treeTrunk + } else if (itemId == Tile::sapling->id) { + itemId = 220 + item->getAuxValue(); // 220 to 223 @sapling + } else if (itemId == Tile::stoneSlabHalf->id) { + itemId = 224 + item->getAuxValue(); // 224 to 231 @stoneslab + } else if (itemId == ((Tile*)Tile::leaves)->id) { + itemId = 232 + (item->getAuxValue() & LeafTile::LEAF_TYPE_MASK); // 232 to 235 @leaves + } + + RenderCall& renderObject = renderObjects[itemId]; + int itemIcon = item->getIcon(); + if(mob != NULL) { + itemIcon = mob->getItemInHandIcon(item, 0); + } + + bool reTesselate(false); + if(itemIcon != lastIconRendered && lastItemRendered == itemId) + reTesselate = true; + lastItemRendered = itemId; + lastIconRendered = itemIcon; + //const int aux = item->getAuxValue(); + + if (renderObject.itemId == -1 || reTesselate) { + if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { + Tesselator& t = Tesselator::instance; + t.beginOverride(); + + int renderedItemId = item->id; + if (renderedItemId == ((Tile*)Tile::leaves)->id) + renderedItemId = ((Tile*)Tile::leaves_carried)->id; + if (renderedItemId == Tile::grass->id) + renderedItemId = Tile::grass_carried->id; + tileRenderer.renderTile(Tile::tiles[renderedItemId], item->getAuxValue()); + renderObject.chunk = t.endOverride(renderObject.chunk.vboId); + + renderObject.texture = "terrain.png"; + renderObject.itemId = itemId; + renderObject.isFlat = false; + } else { + renderObject.itemId = itemId; + renderObject.isFlat = true; + + if (item->id < 256) { + renderObject.texture = "terrain.png"; + //mc->textures->loadAndBindTexture("terrain.png"); + } else { + renderObject.texture = "gui/items.png"; + //mc->textures->loadAndBindTexture("gui/items.png"); + } + // glDisable2(GL_LIGHTING); + Tesselator& t = Tesselator::instance; + t.beginOverride(); + t.color(0xff, 0xff, 0xff); + const int up = itemIcon & 15; + const int vp = itemIcon >> 4; + float u1 = (up * 16 + 0.00f) / 256.0f; + float u0 = (up * 16 + 15.99f) / 256.0f; + float v0 = (vp * 16 + 0.00f) / 256.0f; + float v1 = (vp * 16 + 15.99f) / 256.0f; + + float r = 1.0f; +// float xo = 0.0f; +// float yo = 0.3f; +/* + //glEnable2(GL_RESCALE_NORMAL); + glTranslatef2(-xo, -yo, 0); + float s = 1.5f; + glScalef2(s, s, s); + + glRotatef2(50, 0, 1, 0); + glRotatef2(45 + 290, 0, 0, 1); + glTranslatef2(-15 / 16.0f, -1 / 16.0f, 0); +*/ + float dd = 1 / 16.0f; + + t.vertexUV(0, 0, 0, u0, v1); + t.vertexUV(r, 0, 0, u1, v1); + t.vertexUV(r, 1, 0, u1, v0); + t.vertexUV(0, 1, 0, u0, v0); + + t.vertexUV(0, 1, 0 - dd, u0, v0); + t.vertexUV(r, 1, 0 - dd, u1, v0); + t.vertexUV(r, 0, 0 - dd, u1, v1); + t.vertexUV(0, 0, 0 - dd, u0, v1); + + for (int i = 0; i < 16; i++) { + float p = i / 16.0f; + float uu = u0 + (u1 - u0) * p - 0.5f / 256.0f; + float xx = r * p; + t.vertexUV(xx, 0, 0 - dd, uu, v1); + t.vertexUV(xx, 0, 0, uu, v1); + t.vertexUV(xx, 1, 0, uu, v0); + t.vertexUV(xx, 1, 0 - dd, uu, v0); + } + for (int i = 0; i < 16; i++) { + float p = i / 16.0f; + float uu = u0 + (u1 - u0) * p - 0.5f / 256.0f; + float xx = r * p + 1 / 16.0f; + t.vertexUV(xx, 1, 0 - dd, uu, v0); + t.vertexUV(xx, 1, 0, uu, v0); + t.vertexUV(xx, 0, 0, uu, v1); + t.vertexUV(xx, 0, 0 - dd, uu, v1); + } + for (int i = 0; i < 16; i++) { + float p = i / 16.0f; + float vv = v1 + (v0 - v1) * p - 0.5f / 256.0f; + float yy = r * p + 1 / 16.0f; + t.vertexUV(0, yy, 0, u0, vv); + t.vertexUV(r, yy, 0, u1, vv); + t.vertexUV(r, yy, 0 - dd, u1, vv); + t.vertexUV(0, yy, 0 - dd, u0, vv); + } + for (int i = 0; i < 16; i++) { + float p = i / 16.0f; + float vv = v1 + (v0 - v1) * p - 0.5f / 256.0f; + float yy = r * p; + t.vertexUV(r, yy, 0, u1, vv); + t.vertexUV(0, yy, 0, u0, vv); + t.vertexUV(0, yy, 0 - dd, u0, vv); + t.vertexUV(r, yy, 0 - dd, u1, vv); + } + renderObject.chunk = t.endOverride(renderObject.chunk.vboId); + } + } + + if (renderObject.itemId >= 0) { + if (renderObject.isFlat) { + float xo = 0.0f; + float yo = 0.3f; + + glPushMatrix2(); + glTranslatef2(-xo, -yo, 0); + float s = 1.5f; + glScalef2(s, s, s); + + glRotatef2(50, 0, 1, 0); + glRotatef2(45 + 290, 0, 0, 1); + glTranslatef2(-15 / 16.0f, -1 / 16.0f, 0); + } + mc->textures->loadAndBindTexture(renderObject.texture); + + drawArrayVT_NoState(renderObject.chunk.vboId, renderObject.chunk.vertexCount); + if (renderObject.isFlat) + glPopMatrix2(); + } + //w.stop(); + //handler.printEvery(100); +} + +void ItemInHandRenderer::render( float a ) +{ + //return; + + //Stopwatch& w = handler.get("render"); + //w.start(); + + float h = oHeight + (height - oHeight) * a; + Player* player = mc->player; + // if (selectedTile==NULL) return; + + glPushMatrix2(); + glRotatef2(player->xRotO + (player->xRot - player->xRotO) * a, 1, 0, 0); + glRotatef2(player->yRotO + (player->yRot - player->yRotO) * a, 0, 1, 0); + glPopMatrix2(); + + float br = mc->level->getBrightness(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + + ItemInstance* item;// = selectedItem; + //if (player.fishing != NULL) { + // item = /*new*/ ItemInstance(Item.stick); + //} + + if (this->item.id > 0) + item = &this->item; + else + item = NULL; + + if (item != NULL) { + glColor4f2(br, br, br, 1); + bool isUsing = player->getUseItemDuration() > 0; + bool isBow = item->id == Item::bow->id; + bool noSwingAnimation = isUsing && (isBow || (item->getUseAnimation() != UseAnim::none)); + const float swing = noSwingAnimation ? 0 : player->getAttackAnim(a); + const float sqrtSwing = Mth::sqrt(swing); + const float swing3 = Mth::sin(swing * swing * Mth::PI); + const float swing1 = Mth::sin(swing * Mth::PI); + const float swing2 = Mth::sin(sqrtSwing * Mth::PI); + const float d = 0.8f; + glPushMatrix2(); + + if (isUsing) { + UseAnim::UseAnimation anim = item->getUseAnimation(); + if (anim == UseAnim::eat || anim == UseAnim::drink) { + float t = (player->getUseItemDuration() - a + 1); + float useProgress = 1 - (t / item->getUseDuration()); + + float swing = useProgress; + float is = 1 - swing; + is = is * is * is; + is = is * is * is; + is = is * is * is; + float iss = 1 - is; + glTranslatef(0, Mth::abs(Mth::cos(t / 4 * Mth::PI) * 0.1f) * (swing > 0.2 ? 1 : 0), 0); + glTranslatef(iss * 0.6f, -iss * 0.5f, 0); + glRotatef(iss * 90, 0, 1, 0); + glRotatef(iss * 10, 1, 0, 0); + glRotatef(iss * 30, 0, 0, 1); + } + } else { + glTranslatef2(-swing2 * 0.4f, (float) Mth::sin(sqrtSwing * Mth::PI * 2) * 0.2f, -swing1 * 0.2f); + } + + glTranslatef2(0.7f * d, -0.65f * d - (1 - h) * 0.6f, -0.9f * d); + + glRotatef2(45, 0, 1, 0); + //glEnable2(GL_RESCALE_NORMAL); + glRotatef2(-swing3 * 20, 0, 1, 0); + glRotatef2(-swing2 * 20, 0, 0, 1); + glRotatef2(-swing2 * 80, 1, 0, 0); + float ss = 0.4f; + glScalef2(ss, ss, ss); + if (player->getUseItemDuration() > 0) { + UseAnim::UseAnimation anim = item->getUseAnimation(); + if(anim == UseAnim::bow) { + glRotatef(-18, 0, 0, 1); + glRotatef(-12, 0, 1, 0); + glRotatef(-8, 1, 0, 0); + glTranslatef(-0.9f, 0.2f, 0.0f); + float timeHeld = (item->getUseDuration() - (player->getUseItemDuration() - a + 1)); + float pow = timeHeld / (float) (BowItem::MAX_DRAW_DURATION); + pow = ((pow * pow) + pow * 2) / 3; + if (pow > 1) pow = 1; + if (pow > 0.1f) { + glTranslatef(0, Mth::sin((timeHeld - 0.1f) * 1.3f) * 0.01f * (pow - 0.1f), 0); + } + glTranslatef(0, 0, pow * 0.1f); + + glRotatef(-45 - 290, 0, 0, 1); + glRotatef(-50, 0, 1, 0); + glTranslatef(0, 0.5f, 0); + float ys = 1 + pow * 0.2f; + glScalef(1, 1, ys); + glTranslatef(0, -0.5f, 0); + glRotatef(50, 0, 1, 0); + glRotatef(45 + 290, 0, 0, 1); + } + } + if (item->getItem()->isMirroredArt()) { + glRotatef2(180, 0, 1, 0); + } + glEnableClientState2(GL_VERTEX_ARRAY); + glEnableClientState2(GL_TEXTURE_COORD_ARRAY); + renderItem(player, item); + glDisableClientState2(GL_VERTEX_ARRAY); + glDisableClientState2(GL_TEXTURE_COORD_ARRAY); + glPopMatrix2(); + } else { + glColor4f2(br, br, br, 1); + + glPushMatrix2(); + float d = 0.8f; + const float swing = player->getAttackAnim(a); + const float sqrtSwing = Mth::sqrt(swing); + const float swing3 = Mth::sin(swing * swing * Mth::PI); + const float swing1 = Mth::sin(swing * Mth::PI); + const float swing2 = Mth::sin(sqrtSwing * Mth::PI); + + glTranslatef2(-swing2 * 0.3f, (float) Mth::sin(Mth::sqrt(swing) * Mth::PI * 2) * 0.4f, -swing1 * 0.4f); + glTranslatef2(0.8f * d, -0.75f * d - (1 - h) * 0.6f, -0.9f * d); + + glRotatef2(45, 0, 1, 0); + //glEnable2(GL_RESCALE_NORMAL); + glRotatef2(swing2 * 70, 0, 1, 0); + glRotatef2(-swing3 * 20, 0, 0, 1); + // glRotatef2(-swing2 * 80, 1, 0, 0); + + mc->textures->loadAndBindTexture(player->getTexture()); + glTranslatef2(-1.0f, +3.6f, +3.5f); + glRotatef2(120, 0, 0, 1); + glRotatef2(180 + 20, 1, 0, 0); + glRotatef2(-90 - 45, 0, 1, 0); + glScalef2(1.5f / 24.0f * 16, 1.5f / 24.0f * 16, 1.5f / 24.0f * 16); + glTranslatef2(5.6f, 0, 0); + + EntityRenderer* er = EntityRenderDispatcher::getInstance()->getRenderer(mc->player); + HumanoidMobRenderer* playerRenderer = (HumanoidMobRenderer*) er; + float ss = 1; + glScalef2(ss, ss, ss); + playerRenderer->renderHand(); + glPopMatrix2(); + } + //glDisable2(GL_RESCALE_NORMAL); + //Lighting.turnOff(); + //w.stop(); +} + +void ItemInHandRenderer::renderScreenEffect( float a ) +{ + glDisable2(GL_ALPHA_TEST); + if (mc->player->isOnFire()) { + mc->textures->loadAndBindTexture("terrain.png"); + renderFire(a); + } + + if (mc->player->isInWall()) // Inside a tile + { + int x = Mth::floor(mc->player->x); + int y = Mth::floor(mc->player->y); + int z = Mth::floor(mc->player->z); + + mc->textures->loadAndBindTexture("terrain.png"); + int tile = mc->level->getTile(x, y, z); + if (Tile::tiles[tile] != NULL) { + renderTex(a, Tile::tiles[tile]->getTexture(2)); + } + } + + // if (mc->player->isUnderLiquid(Material::water)) { + //mc->textures->loadAndBindTexture("misc/water.png"); + // renderWater(a); + // } + glEnable2(GL_ALPHA_TEST); +} + +void ItemInHandRenderer::itemPlaced() +{ + height = 0; +} + +void ItemInHandRenderer::itemUsed() +{ + height = 0; +} + +void ItemInHandRenderer::renderTex( float a, int tex ) +{ + Tesselator& t = Tesselator::instance; + + float br;// = mc->player->getBrightness(a); + br = 0.1f; + glColor4f2(br, br, br, 0.5f); + + glPushMatrix2(); + + float x0 = -1; + float x1 = +1; + float y0 = -1; + float y1 = +1; + float z0 = -0.5f; + + float r = 2 / 256.0f; + float u0 = (tex % 16) / 256.0f - r; + float u1 = (tex % 16 + 15.99f) / 256.0f + r; + float v0 = (tex / 16) / 256.0f - r; + float v1 = (tex / 16 + 15.99f) / 256.0f + r; + + t.begin(); + t.vertexUV(x0, y0, z0, u1, v1); + t.vertexUV(x1, y0, z0, u0, v1); + t.vertexUV(x1, y1, z0, u0, v0); + t.vertexUV(x0, y1, z0, u1, v0); + //t.end(); + t.draw(); + glPopMatrix2(); + + glColor4f2(1, 1, 1, 1); +} + +void ItemInHandRenderer::renderWater( float a ) +{ + Tesselator& t = Tesselator::instance; + + float br = mc->player->getBrightness(a); + glColor4f2(br, br, br, 0.5f); + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glPushMatrix2(); + + float size = 4; + + float x0 = -1; + float x1 = +1; + float y0 = -1; + float y1 = +1; + float z0 = -0.5f; + + float uo = -mc->player->yRot / 64.0f; + float vo = +mc->player->xRot / 64.0f; + + t.begin(); + t.vertexUV(x0, y0, z0, size + uo, size + vo); + t.vertexUV(x1, y0, z0, 0 + uo, size + vo); + t.vertexUV(x1, y1, z0, 0 + uo, 0 + vo); + t.vertexUV(x0, y1, z0, size + uo, 0 + vo); + //t.end(); + t.draw(); + glPopMatrix2(); + + glColor4f2(1, 1, 1, 1); + glDisable2(GL_BLEND); +} + +void ItemInHandRenderer::renderFire( float a ) +{ + Tesselator& t = Tesselator::instance; + glColor4f2(1, 1, 1, 0.9f); + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + float size = 1; + for (int i = 0; i < 2; i++) { + glPushMatrix2(); + int tex = ((Tile*)Tile::fire)->tex + i * 16; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + + float u0 = (xt) / 256.0f; + float u1 = (xt + 15.99f) / 256.0f; + float v0 = (yt) / 256.0f; + float v1 = (yt + 15.99f) / 256.0f; + + float x0 = (0 - size) / 2; + float x1 = x0 + size; + float y0 = 0 - size / 2; + float y1 = y0 + size; + float z0 = -0.5f; + glTranslatef2(-(i * 2 - 1) * 0.24f, -0.3f, 0); + glRotatef2((i * 2 - 1) * 10.f, 0, 1, 0); + + t.begin(); + t.vertexUV(x0, y0, z0, u1, v1); + t.vertexUV(x1, y0, z0, u0, v1); + t.vertexUV(x1, y1, z0, u0, v0); + t.vertexUV(x0, y1, z0, u1, v0); + //t.end(); + t.draw(); + glPopMatrix2(); + } + glColor4f2(1, 1, 1, 1); + glDisable2(GL_BLEND); +} + +void ItemInHandRenderer::onGraphicsReset() +{ + GLuint ids[MaxNumRenderObjects]; + glGenBuffers2(MaxNumRenderObjects, ids); + + for (int i = 0; i < MaxNumRenderObjects; ++i) { + renderObjects[i].itemId = -1; + renderObjects[i].chunk.vboId = ids[i]; + } +} diff --git a/src/client/renderer/ItemInHandRenderer.h b/src/client/renderer/ItemInHandRenderer.h index 22e89b3..45e4663 100755 --- a/src/client/renderer/ItemInHandRenderer.h +++ b/src/client/renderer/ItemInHandRenderer.h @@ -1,57 +1,57 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__ItemInHandRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__ItemInHandRenderer_H__ - -//package net.minecraft.client.renderer; - -#include "TileRenderer.h" -#include "../renderer/RenderChunk.h" -#include "../../world/item/ItemInstance.h" - -class Minecraft; - -typedef struct RenderCall { - int itemId; - RenderChunk chunk; - std::string texture; - bool isFlat; -} RenderCall; - - -class ItemInHandRenderer -{ -public: - ItemInHandRenderer(Minecraft* mc); - - void tick(); - - void render(float a); - void renderItem(Mob* player, ItemInstance* item); - void renderScreenEffect(float a); - - void itemPlaced(); - void itemUsed(); - - void onGraphicsReset(); - -private: - void renderTex(float a, int tex); - void renderWater(float a); - void renderFire(float a); - -private: - int lastIconRendered; - int lastItemRendered; - int lastSlot; - ItemInstance item; - - Minecraft* mc; - //ItemInstance* selectedItem; - float height; - float oHeight; - TileRenderer tileRenderer; - - static const int MaxNumRenderObjects = 512; - RenderCall renderObjects[MaxNumRenderObjects]; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__ItemInHandRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__ItemInHandRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__ItemInHandRenderer_H__ + +//package net.minecraft.client.renderer; + +#include "TileRenderer.h" +#include "../renderer/RenderChunk.h" +#include "../../world/item/ItemInstance.h" + +class Minecraft; + +typedef struct RenderCall { + int itemId; + RenderChunk chunk; + std::string texture; + bool isFlat; +} RenderCall; + + +class ItemInHandRenderer +{ +public: + ItemInHandRenderer(Minecraft* mc); + + void tick(); + + void render(float a); + void renderItem(Mob* player, ItemInstance* item); + void renderScreenEffect(float a); + + void itemPlaced(); + void itemUsed(); + + void onGraphicsReset(); + +private: + void renderTex(float a, int tex); + void renderWater(float a); + void renderFire(float a); + +private: + int lastIconRendered; + int lastItemRendered; + int lastSlot; + ItemInstance item; + + Minecraft* mc; + //ItemInstance* selectedItem; + float height; + float oHeight; + TileRenderer tileRenderer; + + static const int MaxNumRenderObjects = 512; + RenderCall renderObjects[MaxNumRenderObjects]; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__ItemInHandRenderer_H__*/ diff --git a/src/client/renderer/LevelRenderer.cpp b/src/client/renderer/LevelRenderer.cpp index 3520732..189f07f 100755 --- a/src/client/renderer/LevelRenderer.cpp +++ b/src/client/renderer/LevelRenderer.cpp @@ -1,1299 +1,1299 @@ -#include "LevelRenderer.h" - -#include "DirtyChunkSorter.h" -#include "DistanceChunkSorter.h" -#include "Chunk.h" -#include "TileRenderer.h" -#include "../Minecraft.h" -#include "../../util/Mth.h" -#include "../../world/entity/player/Player.h" -#include "../../world/level/tile/LevelEvent.h" -#include "../../world/level/tile/LeafTile.h" -#include "../../client/particle/ParticleEngine.h" -#include "../../client/particle/ParticleInclude.h" -#include "../sound/SoundEngine.h" -#include "culling/Culler.h" -#include "entity/EntityRenderDispatcher.h" -#include "../model/HumanoidModel.h" - -#include "GameRenderer.h" -#include "../../AppPlatform.h" -#include "../../util/PerfTimer.h" -#include "Textures.h" -#include "tileentity/TileEntityRenderDispatcher.h" -#include "../particle/BreakingItemParticle.h" - -#include "../../client/player/LocalPlayer.h" - -#ifdef GFX_SMALLER_CHUNKS -/* static */ const int LevelRenderer::CHUNK_SIZE = 8; -#else -/* static */ const int LevelRenderer::CHUNK_SIZE = 16; -#endif - -LevelRenderer::LevelRenderer( Minecraft* mc) -: mc(mc), - textures(mc->textures), - level(NULL), - cullStep(0), - - chunkLists(0), - xChunks(0), yChunks(0), zChunks(0), - - chunks(NULL), - sortedChunks(NULL), - - xMinChunk(0), yMinChunk(0), zMinChunk(0), - xMaxChunk(0), yMaxChunk(0), zMaxChunk(0), - - lastViewDistance(-1), - - noEntityRenderFrames(2), - totalEntities(0), - renderedEntities(0), - culledEntities(0), - - occlusionCheck(false), - totalChunks(0), offscreenChunks(0), renderedChunks(0), occludedChunks(0), emptyChunks(0), - - chunkFixOffs(0), - xOld(-9999), yOld(-9999), zOld(-9999), - - ticks(0), - skyList(0), starList(0), darkList(0), - tileRenderer(NULL), - destroyProgress(0) -{ -#ifdef OPENGL_ES - int maxChunksWidth = 2 * LEVEL_WIDTH / CHUNK_SIZE + 1; - numListsOrBuffers = maxChunksWidth * maxChunksWidth * (128/CHUNK_SIZE) * 3; - chunkBuffers = new GLuint[numListsOrBuffers]; - glGenBuffers2(numListsOrBuffers, chunkBuffers); - LOGI("numBuffers: %d\n", numListsOrBuffers); - //for (int i = 0; i < numListsOrBuffers; ++i) printf("bufId %d: %d\t", i, chunkBuffers[i]); - - glGenBuffers2(1, &skyBuffer); - generateSky(); -#else - int maxChunksWidth = 1024 / CHUNK_SIZE; - numListsOrBuffers = maxChunksWidth * maxChunksWidth * maxChunksWidth * 3; - chunkLists = glGenLists(numListsOrBuffers); -#endif -} - -LevelRenderer::~LevelRenderer() -{ - delete tileRenderer; - tileRenderer = NULL; - - deleteChunks(); - -#ifdef OPENGL_ES - glDeleteBuffers(numListsOrBuffers, chunkBuffers); - glDeleteBuffers(1, &skyBuffer); - delete[] chunkBuffers; -#else - glDeleteLists(numListsOrBuffers, chunkLists); -#endif -} - -void LevelRenderer::generateSky() { - Tesselator& t = Tesselator::instance; - float yy; - int s = 128; - int d = (256 / s) + 2; - yy = (float) (16); - t.begin(); - - skyVertexCount = 0; - for (int xx = -s * d; xx <= s * d; xx += s) { - for (int zz = -s * d; zz <= s * d; zz += s) { - t.vertex((float) xx + 0, yy, (float) zz + 0); - t.vertex((float)(xx + s), yy, (float) zz + 0); - t.vertex((float)(xx + s), yy, (float)(zz + s)); - t.vertex((float) xx + 0, yy, (float)(zz + s)); - //LOGI("x, z: %d, %d\n", xx, zz); - skyVertexCount += 4; - } - } - - t.end(true, skyBuffer); - //LOGI("skyvertexcount: %d\n", skyVertexCount); - //glEndList(); -} - -void LevelRenderer::setLevel( Level* level ) -{ - if (this->level != NULL) { - this->level->removeListener(this); - } - - xOld = -9999; - yOld = -9999; - zOld = -9999; - - EntityRenderDispatcher::getInstance()->setLevel(level); - EntityRenderDispatcher::getInstance()->setMinecraft(mc); - this->level = level; - - delete tileRenderer; - tileRenderer = new TileRenderer(level); - - if (level != NULL) { - level->addListener(this); - allChanged(); - } -} - -void LevelRenderer::allChanged() -{ - deleteChunks(); - - bool fancy = mc->options.getBooleanValue(OPTIONS_FANCY_GRAPHICS); - - Tile::leaves->setFancy(fancy); - Tile::leaves_carried->setFancy(fancy); - lastViewDistance = mc->options.getIntValue(OPTIONS_VIEW_DISTANCE); - - int dist = (512 >> 3) << (3 - lastViewDistance); - if (lastViewDistance <= 2 && mc->isPowerVR()) - dist = (int)((float)dist * 0.8f); - LOGI("last: %d, power: %d\n", lastViewDistance, mc->isPowerVR()); - - #if defined(RPI) - dist *= 0.6f; - #endif - - if (dist > 400) dist = 400; - /* - * if (Minecraft.FLYBY_MODE) { dist = 512 - CHUNK_SIZE * 2; } - */ - xChunks = (dist / LevelRenderer::CHUNK_SIZE) + 1; - yChunks = (128 / LevelRenderer::CHUNK_SIZE); - zChunks = (dist / LevelRenderer::CHUNK_SIZE) + 1; - chunksLength = xChunks * yChunks * zChunks; - LOGI("chunksLength: %d. Distance: %d\n", chunksLength, dist); - - chunks = new Chunk*[chunksLength]; - sortedChunks = new Chunk*[chunksLength]; - - int id = 0; - int count = 0; - - xMinChunk = 0; - yMinChunk = 0; - zMinChunk = 0; - xMaxChunk = xChunks; - yMaxChunk = yChunks; - zMaxChunk = zChunks; - dirtyChunks.clear(); - //renderableTileEntities.clear(); - - for (int x = 0; x < xChunks; x++) { - for (int y = 0; y < yChunks; y++) { - for (int z = 0; z < zChunks; z++) { - const int c = getLinearCoord(x, y, z); - Chunk* chunk = new Chunk(level, x * CHUNK_SIZE, y * CHUNK_SIZE, z * CHUNK_SIZE, CHUNK_SIZE, chunkLists + id, &chunkBuffers[id]); - - if (occlusionCheck) { - chunk->occlusion_id = 0;//occlusionCheckIds.get(count); - } - chunk->occlusion_querying = false; - chunk->occlusion_visible = true; - chunk->visible = true; - chunk->id = count++; - chunk->setDirty(); - - chunks[c] = chunk; - sortedChunks[c] = chunk; - dirtyChunks.push_back(chunk); - - id += 3; - } - } - } - - if (level != NULL) { - Entity* player = mc->cameraTargetPlayer; - if (player != NULL) { - this->resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); - DistanceChunkSorter distanceSorter(player); - std::sort(sortedChunks, sortedChunks + chunksLength, distanceSorter); - } - } - noEntityRenderFrames = 2; -} - -void LevelRenderer::deleteChunks() -{ - for (int z = 0; z < zChunks; ++z) - for (int y = 0; y < yChunks; ++y) - for (int x = 0; x < xChunks; ++x) { - int c = getLinearCoord(x, y, z); - delete chunks[c]; - } - - delete[] chunks; - chunks = NULL; - - delete[] sortedChunks; - sortedChunks = NULL; -} - -void LevelRenderer::resortChunks( int xc, int yc, int zc ) -{ - xc -= CHUNK_SIZE / 2; - //yc -= CHUNK_SIZE / 2; - zc -= CHUNK_SIZE / 2; - xMinChunk = INT_MAX; - yMinChunk = INT_MAX; - zMinChunk = INT_MAX; - xMaxChunk = INT_MIN; - yMaxChunk = INT_MIN; - zMaxChunk = INT_MIN; - - int dirty = 0; - - int s2 = xChunks * CHUNK_SIZE; - int s1 = s2 / 2; - - for (int x = 0; x < xChunks; x++) { - int xx = x * CHUNK_SIZE; - - int xOff = (xx + s1 - xc); - if (xOff < 0) xOff -= (s2 - 1); - xOff /= s2; - xx -= xOff * s2; - - if (xx < xMinChunk) xMinChunk = xx; - if (xx > xMaxChunk) xMaxChunk = xx; - - for (int z = 0; z < zChunks; z++) { - int zz = z * CHUNK_SIZE; - int zOff = (zz + s1 - zc); - if (zOff < 0) zOff -= (s2 - 1); - zOff /= s2; - zz -= zOff * s2; - - if (zz < zMinChunk) zMinChunk = zz; - if (zz > zMaxChunk) zMaxChunk = zz; - - for (int y = 0; y < yChunks; y++) { - int yy = y * CHUNK_SIZE; - if (yy < yMinChunk) yMinChunk = yy; - if (yy > yMaxChunk) yMaxChunk = yy; - - Chunk* chunk = chunks[(z * yChunks + y) * xChunks + x]; - bool wasDirty = chunk->isDirty(); - chunk->setPos(xx, yy, zz); - if (!wasDirty && chunk->isDirty()) { - dirtyChunks.push_back(chunk); - ++dirty; - } - } - } - } -} - -int LevelRenderer::render( Mob* player, int layer, float alpha ) -{ - if (mc->options.getIntValue(OPTIONS_VIEW_DISTANCE) != lastViewDistance) { - allChanged(); - } - - TIMER_PUSH("sortchunks"); - for (int i = 0; i < 10; i++) { - chunkFixOffs = (chunkFixOffs + 1) % chunksLength; - Chunk* c = chunks[chunkFixOffs]; - if (c->isDirty() && std::find(dirtyChunks.begin(), dirtyChunks.end(), c) == dirtyChunks.end()) { - dirtyChunks.push_back(c); - } - } - - if (layer == 0) { - totalChunks = 0; - offscreenChunks = 0; - occludedChunks = 0; - renderedChunks = 0; - emptyChunks = 0; - } - - float xOff = player->xOld + (player->x - player->xOld) * alpha; - float yOff = player->yOld + (player->y - player->yOld) * alpha; - float zOff = player->zOld + (player->z - player->zOld) * alpha; - - float xd = player->x - xOld; - float yd = player->y - yOld; - float zd = player->z - zOld; - if (xd * xd + yd * yd + zd * zd > 4 * 4) { - xOld = player->x; - yOld = player->y; - zOld = player->z; - - resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); - DistanceChunkSorter distanceSorter(player); - std::sort(sortedChunks, sortedChunks + chunksLength, distanceSorter); - } - - int count = 0; - if (occlusionCheck && !mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D) && layer == 0) { - int from = 0; - int to = 16; - //checkQueryResults(from, to); - for (int i = from; i < to; i++) { - sortedChunks[i]->occlusion_visible = true; - } - - count += renderChunks(from, to, layer, alpha); - - do { - from = to; - to = to * 2; - if (to > chunksLength) to = chunksLength; - - glDisable2(GL_TEXTURE_2D); - glDisable2(GL_LIGHTING); - glDisable2(GL_ALPHA_TEST); - glDisable2(GL_FOG); - - glColorMask(false, false, false, false); - glDepthMask(false); - //checkQueryResults(from, to); - glPushMatrix2(); - float xo = 0; - float yo = 0; - float zo = 0; - for (int i = from; i < to; i++) { - if (sortedChunks[i]->isEmpty()) { - sortedChunks[i]->visible = false; - continue; - } - if (!sortedChunks[i]->visible) { - sortedChunks[i]->occlusion_visible = true; - } - - if (sortedChunks[i]->visible && !sortedChunks[i]->occlusion_querying) { - float dist = Mth::sqrt(sortedChunks[i]->distanceToSqr(player)); - - int frequency = (int) (1 + dist / 128); - - if (ticks % frequency == i % frequency) { - Chunk* chunk = sortedChunks[i]; - float xt = (float) (chunk->x - xOff); - float yt = (float) (chunk->y - yOff); - float zt = (float) (chunk->z - zOff); - float xdd = xt - xo; - float ydd = yt - yo; - float zdd = zt - zo; - - if (xdd != 0 || ydd != 0 || zdd != 0) { - glTranslatef2(xdd, ydd, zdd); - xo += xdd; - yo += ydd; - zo += zdd; - } - - sortedChunks[i]->renderBB(); - sortedChunks[i]->occlusion_querying = true; - } - } - } - glPopMatrix2(); - glColorMask(true, true, true, true); - glDepthMask(true); - glEnable2(GL_TEXTURE_2D); - glEnable2(GL_ALPHA_TEST); - glEnable2(GL_FOG); - - count += renderChunks(from, to, layer, alpha); - - } while (to < chunksLength); - - } else { - TIMER_POP_PUSH("render"); - count += renderChunks(0, chunksLength, layer, alpha); - } - - TIMER_POP(); - return count; -} - -void LevelRenderer::renderDebug(const AABB& b, float a) const { - float x0 = b.x0; - float x1 = b.x1; - float y0 = b.y0; - float y1 = b.y1; - float z0 = b.z0; - float z1 = b.z1; - float u0 = 0, v0 = 0; - float u1 = 1, v1 = 1; - - glEnable2(GL_BLEND); - glBlendFunc2(GL_DST_COLOR, GL_SRC_COLOR); - glDisable2(GL_TEXTURE_2D); - glColor4f2(1, 1, 1, 1); - - textures->loadAndBindTexture("terrain.png"); - - Tesselator& t = Tesselator::instance; - t.begin(); - t.color(255, 255, 255, 255); - - t.offset(((Mob*)mc->player)->getPos(a).negated()); - - // up - t.vertexUV(x0, y0, z1, u0, v1); - t.vertexUV(x0, y0, z0, u0, v0); - t.vertexUV(x1, y0, z0, u1, v0); - t.vertexUV(x1, y0, z1, u1, v1); - - // down - t.vertexUV(x1, y1, z1, u1, v1); - t.vertexUV(x1, y1, z0, u1, v0); - t.vertexUV(x0, y1, z0, u0, v0); - t.vertexUV(x0, y1, z1, u0, v1); - - // north - t.vertexUV(x0, y1, z0, u1, v0); - t.vertexUV(x1, y1, z0, u0, v0); - t.vertexUV(x1, y0, z0, u0, v1); - t.vertexUV(x0, y0, z0, u1, v1); - - // south - t.vertexUV(x0, y1, z1, u0, v0); - t.vertexUV(x0, y0, z1, u0, v1); - t.vertexUV(x1, y0, z1, u1, v1); - t.vertexUV(x1, y1, z1, u1, v0); - - // west - t.vertexUV(x0, y1, z1, u1, v0); - t.vertexUV(x0, y1, z0, u0, v0); - t.vertexUV(x0, y0, z0, u0, v1); - t.vertexUV(x0, y0, z1, u1, v1); - - // east - t.vertexUV(x1, y0, z1, u0, v1); - t.vertexUV(x1, y0, z0, u1, v1); - t.vertexUV(x1, y1, z0, u1, v0); - t.vertexUV(x1, y1, z1, u0, v0); - - t.offset(0, 0, 0); - t.draw(); - - glEnable2(GL_TEXTURE_2D); - glDisable2(GL_BLEND); -} - -void LevelRenderer::render(const AABB& b) const -{ - Tesselator& t = Tesselator::instance; - - glColor4f2(1, 1, 1, 1); - - textures->loadAndBindTexture("terrain.png"); - - //t.begin(); - t.color(255, 255, 255, 255); - - t.offset(((Mob*)mc->player)->getPos(0).negated()); - - t.begin(GL_LINE_STRIP); - t.vertex(b.x0, b.y0, b.z0); - t.vertex(b.x1, b.y0, b.z0); - t.vertex(b.x1, b.y0, b.z1); - t.vertex(b.x0, b.y0, b.z1); - t.vertex(b.x0, b.y0, b.z0); - t.draw(); - - t.begin(GL_LINE_STRIP); - t.vertex(b.x0, b.y1, b.z0); - t.vertex(b.x1, b.y1, b.z0); - t.vertex(b.x1, b.y1, b.z1); - t.vertex(b.x0, b.y1, b.z1); - t.vertex(b.x0, b.y1, b.z0); - t.draw(); - - t.begin(GL_LINES); - t.vertex(b.x0, b.y0, b.z0); - t.vertex(b.x0, b.y1, b.z0); - t.vertex(b.x1, b.y0, b.z0); - t.vertex(b.x1, b.y1, b.z0); - t.vertex(b.x1, b.y0, b.z1); - t.vertex(b.x1, b.y1, b.z1); - t.vertex(b.x0, b.y0, b.z1); - t.vertex(b.x0, b.y1, b.z1); - - t.offset(0, 0, 0); - t.draw(); -} - -//void LevelRenderer::checkQueryResults( int from, int to ) -//{ -// for (int i = from; i < to; i++) { -// if (sortedChunks[i]->occlusion_querying) { -// // I wanna do a fast occusion culler here. -// } -// } -//} - -int LevelRenderer::renderChunks( int from, int to, int layer, float alpha ) -{ - _renderChunks.clear(); - int count = 0; - for (int i = from; i < to; i++) { - if (layer == 0) { - totalChunks++; - if (sortedChunks[i]->empty[layer]) emptyChunks++; - else if (!sortedChunks[i]->visible) offscreenChunks++; - else if (occlusionCheck && !sortedChunks[i]->occlusion_visible) occludedChunks++; - else renderedChunks++; - } - - if (!sortedChunks[i]->empty[layer] && sortedChunks[i]->visible && sortedChunks[i]->occlusion_visible) { - int list = sortedChunks[i]->getList(layer); - if (list >= 0) { - _renderChunks.push_back(sortedChunks[i]); - count++; - } - } - } - - Mob* player = mc->cameraTargetPlayer; - float xOff = player->xOld + (player->x - player->xOld) * alpha; - float yOff = player->yOld + (player->y - player->yOld) * alpha; - float zOff = player->zOld + (player->z - player->zOld) * alpha; - - //int lists = 0; - renderList.clear(); - renderList.init(xOff, yOff, zOff); - - for (unsigned int i = 0; i < _renderChunks.size(); ++i) { - Chunk* chunk = _renderChunks[i]; - #ifdef USE_VBO - renderList.addR(chunk->getRenderChunk(layer)); - #else - renderList.add(chunk->getList(layer)); - #endif - renderList.next(); - } - - renderSameAsLast(layer, alpha); - - return count; -} - -void LevelRenderer::renderSameAsLast( int layer, float alpha ) -{ - renderList.render(); -} - -void LevelRenderer::tick() -{ - ticks++; -} - -bool LevelRenderer::updateDirtyChunks( Mob* player, bool force ) -{ - bool slow = false; - - if (slow) { - DirtyChunkSorter dirtySorter(player); - std::sort(dirtyChunks.begin(), dirtyChunks.end(), dirtySorter); - int s = dirtyChunks.size() - 1; - int amount = dirtyChunks.size(); - for (int i = 0; i < amount; i++) { - Chunk* chunk = dirtyChunks[s-i]; - if (!force) { - if (chunk->distanceToSqr(player) > 16 * 16) { - if (chunk->visible) { - if (i >= MAX_VISIBLE_REBUILDS_PER_FRAME) return false; - } else { - if (i >= MAX_INVISIBLE_REBUILDS_PER_FRAME) return false; - } - } - } else { - if (!chunk->visible) continue; - } - chunk->rebuild(); - - dirtyChunks.erase( std::find(dirtyChunks.begin(), dirtyChunks.end(), chunk) ); // @q: s-i? - chunk->setClean(); - } - - return dirtyChunks.size() == 0; - } else { - const int count = 3; - - DirtyChunkSorter dirtyChunkSorter(player); - Chunk* toAdd[count] = {NULL}; - std::vector* nearChunks = NULL; - - int pendingChunkSize = dirtyChunks.size(); - int pendingChunkRemoved = 0; - - for (int i = 0; i < pendingChunkSize; i++) { - Chunk* chunk = dirtyChunks[i]; - - if (!force) { - if (chunk->distanceToSqr(player) > 1024.0f) { - int index; - - // is this chunk in the closest ? - for (index = 0; index < count; index++) { - if (toAdd[index] != NULL && dirtyChunkSorter(toAdd[index], chunk) == false) { - break; - } - } - - index--; - - if (index > 0) { - int x = index; - while (--x != 0) { - toAdd[x - 1] = toAdd[x]; - } - toAdd[index] = chunk; - } - - continue; - } - } else if (!chunk->visible) { - continue; - } - - // chunk is very close -- always render - - if (nearChunks == NULL) { - nearChunks = new std::vector(); - } - - pendingChunkRemoved++; - nearChunks->push_back(chunk); - dirtyChunks[i] = NULL; - } - - // if there are nearby chunks that need to be prepared for - // rendering, sort them and then process them - static const float MaxFrameTime = 1.0f / 100.0f; - Stopwatch chunkWatch; - chunkWatch.start(); - - if (nearChunks != NULL) { - if (nearChunks->size() > 1) { - std::sort(nearChunks->begin(), nearChunks->end(), dirtyChunkSorter); - } - - for (int i = nearChunks->size() - 1; i >= 0; i--) { - Chunk* chunk = (*nearChunks)[i]; - chunk->rebuild(); - chunk->setClean(); - } - delete nearChunks; - } - - // render the nearest chunks (farther than 1024 units away) - int secondaryRemoved = 0; - - for (int i = count - 1; i >= 0; i--) { - Chunk* chunk = toAdd[i]; - if (chunk != NULL) { - - float ttt = chunkWatch.stopContinue(); - if (ttt >= MaxFrameTime) { - //LOGI("Too much work, I quit2!\n"); - break; - } - - if (!chunk->visible && i != count - 1) { - // escape early if chunks aren't ready - toAdd[i] = NULL; - toAdd[0] = NULL; - break; - } - toAdd[i]->rebuild(); - toAdd[i]->setClean(); - secondaryRemoved++; - } - } - - // compact by removing nulls - int cursor = 0; - int target = 0; - int arraySize = dirtyChunks.size(); - while (cursor != arraySize) { - Chunk* chunk = dirtyChunks[cursor]; - if (chunk != NULL) { - bool remove = false; - for (int i = 0; i < count && !remove; i++) - if (chunk == toAdd[i]) { - remove = true; - } - - if (!remove) { - //if (chunk == toAdd[0] || chunk == toAdd[1] || chunk == toAdd[2]) { - // ; // this chunk was rendered and should be removed - //} else { - if (target != cursor) { - dirtyChunks[target] = chunk; - } - target++; - } - } - cursor++; - } - - // trim - if (cursor > target) - dirtyChunks.erase(dirtyChunks.begin() + target, dirtyChunks.end()); - - return pendingChunkSize == (pendingChunkRemoved + secondaryRemoved); - } -} - -void LevelRenderer::renderHit( Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a ) -{ - if (mode == 0) { - if (destroyProgress > 0) { - Tesselator& t = Tesselator::instance; - glEnable2(GL_BLEND); - glBlendFunc2(GL_DST_COLOR, GL_SRC_COLOR); - - textures->loadAndBindTexture("terrain.png"); - glPushMatrix2(); - - int tileId = level->getTile(h.x, h.y, h.z); - Tile* tile = tileId > 0 ? Tile::tiles[tileId] : NULL; - //glDisable2(GL_ALPHA_TEST); - - glPolygonOffset(-3.0f, -3.0f); - glEnable2(GL_POLYGON_OFFSET_FILL); - t.begin(); - t.color(1.0f, 1.0f, 1.0f, 0.5f); - t.noColor(); - float xo = player->xOld + (player->x - player->xOld) * a; - float yo = player->yOld + (player->y - player->yOld) * a; - float zo = player->zOld + (player->z - player->zOld) * a; - - t.offset(-xo, -yo, -zo); - //t.noColor(); - - if (tile == NULL) tile = Tile::rock; - const int progress = (int) (destroyProgress * 10); - tileRenderer->tesselateInWorld(tile, h.x, h.y, h.z, 15 * 16 + progress); - - t.draw(); - t.offset(0, 0, 0); - glPolygonOffset(0.0f, 0.0f); - glDisable2(GL_POLYGON_OFFSET_FILL); - //glDisable2(GL_ALPHA_TEST); - glDisable2(GL_BLEND); - - glDepthMask(true); - glPopMatrix2(); - } - } - //else if (inventoryItem != NULL) { - // glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // float br = ((float) (util.Mth::sin(System.currentTimeMillis() / 100.0f)) * 0.2f + 0.8f); - // glColor4f2(br, br, br, ((float) (util.Mth::sin(System.currentTimeMillis() / 200.0f)) * 0.2f + 0.5f)); - - // int id = textures.loadTexture("terrain.png"); - // glBindTexture2(GL_TEXTURE_2D, id); - // int x = h.x; - // int y = h.y; - // int z = h.z; - // if (h.f == 0) y--; - // if (h.f == 1) y++; - // if (h.f == 2) z--; - // if (h.f == 3) z++; - // if (h.f == 4) x--; - // if (h.f == 5) x++; - // /* - // * t.begin(); t.noColor(); Tile.tiles[tileType].tesselate(level, x, - // * y, z, t); t.end(); - // */ - // } -} - -void LevelRenderer::renderHitOutline( Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a ) -{ - if (mode == 0 && h.type == TILE) { - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f2(0, 0, 0, 0.4f); - glLineWidth(1.0f); - glDisable2(GL_TEXTURE_2D); - glDepthMask(false); - float ss = 0.002f; - int tileId = level->getTile(h.x, h.y, h.z); - - if (tileId > 0) { - Tile::tiles[tileId]->updateShape(level, h.x, h.y, h.z); - float xo = player->xOld + (player->x - player->xOld) * a; - float yo = player->yOld + (player->y - player->yOld) * a; - float zo = player->zOld + (player->z - player->zOld) * a; - render(Tile::tiles[tileId]->getTileAABB(level, h.x, h.y, h.z).grow(ss, ss, ss).cloneMove(-xo, -yo, -zo)); - } - glDepthMask(true); - glEnable2(GL_TEXTURE_2D); - glDisable2(GL_BLEND); - } -} - -void LevelRenderer::setDirty( int x0, int y0, int z0, int x1, int y1, int z1 ) -{ - int _x0 = Mth::intFloorDiv(x0, CHUNK_SIZE); - int _y0 = Mth::intFloorDiv(y0, CHUNK_SIZE); - int _z0 = Mth::intFloorDiv(z0, CHUNK_SIZE); - int _x1 = Mth::intFloorDiv(x1, CHUNK_SIZE); - int _y1 = Mth::intFloorDiv(y1, CHUNK_SIZE); - int _z1 = Mth::intFloorDiv(z1, CHUNK_SIZE); - - for (int x = _x0; x <= _x1; x++) { - int xx = x % xChunks; - if (xx < 0) xx += xChunks; - for (int y = _y0; y <= _y1; y++) { - int yy = y % yChunks; - if (yy < 0) yy += yChunks; - for (int z = _z0; z <= _z1; z++) { - int zz = z % zChunks; - if (zz < 0) zz += zChunks; - - int p = ((zz) * yChunks + (yy)) * xChunks + (xx); - Chunk* chunk = chunks[p]; - if (!chunk->isDirty()) { - dirtyChunks.push_back(chunk); - chunk->setDirty(); - } - } - } - } -} - -void LevelRenderer::tileChanged( int x, int y, int z) -{ - setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1); -} - - -void LevelRenderer::setTilesDirty( int x0, int y0, int z0, int x1, int y1, int z1 ) -{ - setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1); -} - - -void LevelRenderer::cull( Culler* culler, float a ) -{ - for (int i = 0; i < chunksLength; i++) { - if (!chunks[i]->isEmpty()) { - if (!chunks[i]->visible || ((i + cullStep) & 15) == 0) { - chunks[i]->cull(culler); - } - } - } - cullStep++; -} - -void LevelRenderer::skyColorChanged() -{ - for (int i = 0; i < chunksLength; i++) { - if (chunks[i]->skyLit) { - if (!chunks[i]->isDirty()) { - dirtyChunks.push_back(chunks[i]); - chunks[i]->setDirty(); - } - } - } -} - -bool entityRenderPredicate(const Entity* a, const Entity* b) { - return a->entityRendererId < b->entityRendererId; -} - -void LevelRenderer::renderEntities(Vec3 cam, Culler* culler, float a) { - if (noEntityRenderFrames > 0) { - noEntityRenderFrames--; - return; - } - - TIMER_PUSH("prepare"); - TileEntityRenderDispatcher::getInstance()->prepare(level, textures, mc->font, mc->cameraTargetPlayer, a); - EntityRenderDispatcher::getInstance()->prepare(level, mc->font, mc->cameraTargetPlayer, &mc->options, a); - - totalEntities = 0; - renderedEntities = 0; - culledEntities = 0; - - Entity* player = mc->cameraTargetPlayer; - EntityRenderDispatcher::xOff = TileEntityRenderDispatcher::xOff = (player->xOld + (player->x - player->xOld) * a); - EntityRenderDispatcher::yOff = TileEntityRenderDispatcher::yOff = (player->yOld + (player->y - player->yOld) * a); - EntityRenderDispatcher::zOff = TileEntityRenderDispatcher::zOff = (player->zOld + (player->z - player->zOld) * a); - - glEnableClientState2(GL_VERTEX_ARRAY); - glEnableClientState2(GL_TEXTURE_COORD_ARRAY); - - TIMER_POP_PUSH("entities"); - const EntityList& entities = level->getAllEntities(); - totalEntities = entities.size(); - if (totalEntities > 0) { - Entity** toRender = new Entity*[totalEntities]; - for (int i = 0; i < totalEntities; i++) { - Entity* entity = entities[i]; - - bool thirdPerson = mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW); - - if (entity->shouldRender(cam) && culler->isVisible(entity->bb)) - { - if (entity == mc->cameraTargetPlayer && thirdPerson == 0 && mc->cameraTargetPlayer->isPlayer() && !((Player*)mc->cameraTargetPlayer)->isSleeping()) continue; - if (entity == mc->cameraTargetPlayer && !thirdPerson) - continue; - if (!level->hasChunkAt(Mth::floor(entity->x), Mth::floor(entity->y), Mth::floor(entity->z))) - continue; - - toRender[renderedEntities++] = entity; - //EntityRenderDispatcher::getInstance()->render(entity, a); - } - } - - if (renderedEntities > 0) { - std::sort(&toRender[0], &toRender[renderedEntities], entityRenderPredicate); - for (int i = 0; i < renderedEntities; ++i) { - EntityRenderDispatcher* disp = EntityRenderDispatcher::getInstance(); - disp->render(toRender[i], a); - } - } - - delete[] toRender; - } - - TIMER_POP_PUSH("tileentities"); - for (unsigned int i = 0; i < level->tileEntities.size(); i++) { - TileEntityRenderDispatcher::getInstance()->render(level->tileEntities[i], a); - } - - glDisableClientState2(GL_VERTEX_ARRAY); - glDisableClientState2(GL_TEXTURE_COORD_ARRAY); - - TIMER_POP(); -} - -std::string LevelRenderer::gatherStats1() { - std::stringstream ss; - ss << "C: " << renderedChunks << "/" << totalChunks << ". F: " << offscreenChunks << ", O: " << occludedChunks << ", E: " << emptyChunks << "\n"; - return ss.str(); -} - -// -// /*public*/ std::string gatherStats2() { -// return "E: " + renderedEntities + "/" + totalEntities + ". B: " + culledEntities + ", I: " + ((totalEntities - culledEntities) - renderedEntities); -// } -// -// int[] toRender = new int[50000]; -// IntBuffer resultBuffer = MemoryTracker.createIntBuffer(64); - -void LevelRenderer::renderSky(float alpha) { - if (mc->level->dimension->foggy) return; - - glDisable2(GL_TEXTURE_2D); - Vec3 sc = level->getSkyColor(mc->cameraTargetPlayer, alpha); - float sr = (float) sc.x; - float sg = (float) sc.y; - float sb = (float) sc.z;// + 0.5f; - - if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) { - float srr = (sr * 30.0f + sg * 59.0f + sb * 11.0f) / 100.0f; - float sgg = (sr * 30.0f + sg * 70.0f) / (100.0f); - float sbb = (sr * 30.0f + sb * 70.0f) / (100.0f); - - sr = srr; - sg = sgg; - sb = sbb; - } - glColor4f2(sr, sg, Mth::Min(1.0f, sb), 1); - - //Tesselator& t = Tesselator::instance; - - glEnable2(GL_FOG); - glColor4f2(sr, sg, sb, 1.0f); - -#ifdef OPENGL_ES - drawArrayVT(skyBuffer, skyVertexCount); -#endif - glEnable2(GL_TEXTURE_2D); -} - -void LevelRenderer::renderClouds( float alpha ) { - //if (!mc->level->dimension->isNaturalDimension()) return; - glEnable2(GL_TEXTURE_2D); - glDisable(GL_CULL_FACE); - float yOffs = (float) (mc->player->yOld + (mc->player->y - mc->player->yOld) * alpha); - int s = 32; - int d = 256 / s; - Tesselator& t = Tesselator::instance; - - //glBindTexture(GL_TEXTURE_2D, texturesloadTexture("/environment/clouds.png")); - textures->loadAndBindTexture("environment/clouds.png"); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - Vec3 cc = level->getCloudColor(alpha); - float cr = (float) cc.x; - float cg = (float) cc.y; - float cb = (float) cc.z; - - float scale = 1 / 2048.0f; - - float time = (ticks + alpha); - float xo = mc->player->xo + (mc->player->x - mc->player->xo) * alpha + time * 0.03f; - float zo = mc->player->zo + (mc->player->z - mc->player->zo) * alpha; - int xOffs = Mth::floor(xo / 2048); - int zOffs = Mth::floor(zo / 2048); - xo -= xOffs * 2048; - zo -= zOffs * 2048; - - float yy = /*level.dimension.getCloudHeight()*/ 128 - yOffs + 0.33f;//mc->player->y + 1; - float uo = (float) (xo * scale); - float vo = (float) (zo * scale); - t.begin(); - - t.color(cr, cg, cb, 0.8f); - for (int xx = -s * d; xx < +s * d; xx += s) { - for (int zz = -s * d; zz < +s * d; zz += s) { - t.vertexUV((float)xx, yy, (float)zz + s, xx * scale + uo, (zz + s) * scale + vo); - t.vertexUV((float)xx + s, yy, (float)zz + s, (xx + s) * scale + uo, (zz + s) * scale + vo); - t.vertexUV((float)xx + s, yy, (float)zz, (xx + s) * scale + uo, zz * scale + vo); - t.vertexUV((float)xx, yy, (float)zz, xx * scale + uo, zz * scale + vo); - } - } - t.endOverrideAndDraw(); - glColor4f(1, 1, 1, 1.0f); - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); -} - -void LevelRenderer::playSound(const std::string& name, float x, float y, float z, float volume, float pitch) { - // @todo: deny sounds here if sound is off (rather than waiting 'til SoundEngine) - float dd = 16; - - if (volume > 1) dd *= volume; - if (mc->cameraTargetPlayer->distanceToSqr(x, y, z) < dd * dd) { - mc->soundEngine->play(name, x, y, z, volume, pitch); - } -} - -void LevelRenderer::addParticle(const std::string& name, float x, float y, float z, float xa, float ya, float za, int data) { - - float xd = mc->cameraTargetPlayer->x - x; - float yd = mc->cameraTargetPlayer->y - y; - float zd = mc->cameraTargetPlayer->z - z; - float distanceSquared = xd * xd + yd * yd + zd * zd; - - //Particle* p = NULL; - //if (name == "hugeexplosion") p = new HugeExplosionSeedParticle(level, x, y, z, xa, ya, za); - //else if (name == "largeexplode") p = new HugeExplosionParticle(textures, level, x, y, z, xa, ya, za); - - //if (p) { - // if (distanceSquared < 32 * 32) { - // mc->particleEngine->add(p); - // } else { delete p; } - // return; - //} - - const float particleDistance = 16; - if (distanceSquared > particleDistance * particleDistance) return; - - //static Stopwatch sw; - //sw.start(); - - if (name == "bubble") mc->particleEngine->add(new BubbleParticle(level, x, y, z, xa, ya, za)); - else if (name == "crit") mc->particleEngine->add(new CritParticle2(level, x, y, z, xa, ya, za)); - else if (name == "smoke") mc->particleEngine->add(new SmokeParticle(level, x, y, z, xa, ya, za)); - //else if (name == "note") mc->particleEngine->add(new NoteParticle(level, x, y, z, xa, ya, za)); - else if (name == "explode") mc->particleEngine->add(new ExplodeParticle(level, x, y, z, xa, ya, za)); - else if (name == "flame") mc->particleEngine->add(new FlameParticle(level, x, y, z, xa, ya, za)); - else if (name == "lava") mc->particleEngine->add(new LavaParticle(level, x, y, z)); - //else if (name == "splash") mc->particleEngine->add(new SplashParticle(level, x, y, z, xa, ya, za)); - else if (name == "largesmoke") mc->particleEngine->add(new SmokeParticle(level, x, y, z, xa, ya, za, 2.5f)); - else if (name == "reddust") mc->particleEngine->add(new RedDustParticle(level, x, y, z, xa, ya, za)); - else if (name == "iconcrack") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, xa, ya, za, Item::items[data])); - else if (name == "snowballpoof") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, Item::snowBall)); - //else if (name == "snowballpoof") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, Item::snowBall)); - //else if (name == "slime") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, Item::slimeBall)); - //else if (name == "heart") mc->particleEngine->add(new HeartParticle(level, x, y, z, xa, ya, za)); - - //sw.stop(); - //sw.printEvery(50, "add-particle-string"); -} - -/* -void LevelRenderer::addParticle(ParticleType::Id name, float x, float y, float z, float xa, float ya, float za, int data) { - float xd = mc->cameraTargetPlayer->x - x; - float yd = mc->cameraTargetPlayer->y - y; - float zd = mc->cameraTargetPlayer->z - z; - - const float particleDistance = 16; - if (xd * xd + yd * yd + zd * zd > particleDistance * particleDistance) return; - - //static Stopwatch sw; - //sw.start(); - - //Particle* p = NULL; - - if (name == ParticleType::bubble) mc->particleEngine->add( new BubbleParticle(level, x, y, z, xa, ya, za) ); - else if (name == ParticleType::crit) mc->particleEngine->add(new CritParticle2(level, x, y, z, xa, ya, za) ); - else if (name == ParticleType::smoke) mc->particleEngine->add(new SmokeParticle(level, x, y, z, xa, ya, za) ); - else if (name == ParticleType::explode) mc->particleEngine->add( new ExplodeParticle(level, x, y, z, xa, ya, za) ); - else if (name == ParticleType::flame) mc->particleEngine->add( new FlameParticle(level, x, y, z, xa, ya, za) ); - else if (name == ParticleType::lava) mc->particleEngine->add( new LavaParticle(level, x, y, z) ); - else if (name == ParticleType::largesmoke) mc->particleEngine->add( new SmokeParticle(level, x, y, z, xa, ya, za, 2.5f) ); - else if (name == ParticleType::reddust) mc->particleEngine->add( new RedDustParticle(level, x, y, z, xa, ya, za) ); - else if (name == ParticleType::iconcrack) mc->particleEngine->add( new BreakingItemParticle(level, x, y, z, xa, ya, za, Item::items[data]) ); - - //switch (name) { - // case ParticleType::bubble: p = new BubbleParticle(level, x, y, z, xa, ya, za); break; - // case ParticleType::crit: p = new CritParticle2(level, x, y, z, xa, ya, za); break; - // case ParticleType::smoke: p = new SmokeParticle(level, x, y, z, xa, ya, za); break; - // //case ParticleType::note: p = new NoteParticle(level, x, y, z, xa, ya, za); break; - // case ParticleType::explode: p = new ExplodeParticle(level, x, y, z, xa, ya, za); break; - // case ParticleType::flame: p = new FlameParticle(level, x, y, z, xa, ya, za); break; - // case ParticleType::lava: p = new LavaParticle(level, x, y, z); break; - // //case ParticleType::splash: p = new SplashParticle(level, x, y, z, xa, ya, za); break; - // case ParticleType::largesmoke: p = new SmokeParticle(level, x, y, z, xa, ya, za, 2.5f); break; - // case ParticleType::reddust: p = new RedDustParticle(level, x, y, z, xa, ya, za); break; - // case ParticleType::iconcrack: p = new BreakingItemParticle(level, x, y, z, xa, ya, za, Item::items[data]); break; - // //case ParticleType::snowballpoof: p = new BreakingItemParticle(level, x, y, z, Item::snowBall); break; - // //case ParticleType::slime: p = new BreakingItemParticle(level, x, y, z, Item::slimeBall); break; - // //case ParticleType::heart: p = new HeartParticle(level, x, y, z, xa, ya, za); break; - // default: - // LOGW("Couldn't find particle of type: %d\n", name); - // break; - //} - //if (p) { - // mc->particleEngine->add(p); - //} - - //sw.stop(); - //sw.printEvery(50, "add-particle-enum"); -} -*/ - -void LevelRenderer::renderHitSelect( Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a ) -{ - //if (h.type == TILE) LOGI("type: %s @ (%d, %d, %d)\n", Tile::tiles[level->getTile(h.x, h.y, h.z)]->getDescriptionId().c_str(), h.x, h.y, h.z); - - if (mode == 0) { - - Tesselator& t = Tesselator::instance; - glEnable2(GL_BLEND); - glDisable2(GL_TEXTURE_2D); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE); - glBlendFunc2(GL_DST_COLOR, GL_SRC_COLOR); - glEnable2(GL_DEPTH_TEST); - - textures->loadAndBindTexture("terrain.png"); - - int tileId = level->getTile(h.x, h.y, h.z); - Tile* tile = tileId > 0 ? Tile::tiles[tileId] : NULL; - glDisable2(GL_ALPHA_TEST); - - //LOGI("block: %d - %d (%s)\n", tileId, level->getData(h.x, h.y, h.z), tile==NULL?"null" : tile->getDescriptionId().c_str() ); - - const float br = 0.65f; - glColor4f2(br * 1.0f, br * 1.0f, br * 1.0f, br * 1.0f); - glPushMatrix2(); - - //glPolygonOffset(-.3f, -.3f); - glPolygonOffset(-1.f, -1.f); //Implementation dependent units - glEnable2(GL_POLYGON_OFFSET_FILL); - float xo = player->xOld + (player->x - player->xOld) * a; - float yo = player->yOld + (player->y - player->yOld) * a; - float zo = player->zOld + (player->z - player->zOld) * a; - - t.begin(); - t.offset(-xo, -yo, -zo); - t.noColor(); - - if (tile == NULL) tile = Tile::rock; - tileRenderer->tesselateInWorld(tile, h.x, h.y, h.z); - - t.draw(); - t.offset(0, 0, 0); - glPolygonOffset(0.0f, 0.0f); - - glDisable2(GL_POLYGON_OFFSET_FILL); - glEnable2(GL_TEXTURE_2D); - - glDepthMask(true); - glPopMatrix2(); - - glEnable2(GL_ALPHA_TEST); - glDisable2(GL_BLEND); - } -} - -void LevelRenderer::onGraphicsReset() -{ - generateSky(); - - // Get new buffers -#ifdef OPENGL_ES - glGenBuffers2(numListsOrBuffers, chunkBuffers); -#else - chunkLists = glGenLists(numListsOrBuffers); -#endif - - // Rebuild - allChanged(); -} - -void LevelRenderer::entityAdded( Entity* entity ) -{ - if (!entity->isPlayer()) - return; - - // Hack to (hopefully) get the players to show - EntityRenderDispatcher::getInstance()->onGraphicsReset(); -} - -int _t_keepPic = -1; - -void LevelRenderer::takePicture( TripodCamera* cam, Entity* entity ) -{ - // Push old values - Mob* oldCameraEntity = mc->cameraTargetPlayer; - bool hideGui = mc->options.getBooleanValue(OPTIONS_HIDEGUI); - bool thirdPerson = mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW); - - // @huge @attn: This is highly illegal, super temp! - mc->cameraTargetPlayer = (Mob*)cam; - mc->options.set(OPTIONS_HIDEGUI, true); - mc->options.set(OPTIONS_THIRD_PERSON_VIEW, false); - - mc->gameRenderer->renderLevel(0); - - // Pop values back - mc->cameraTargetPlayer = oldCameraEntity; - mc->options.set(OPTIONS_HIDEGUI, hideGui); - mc->options.set(OPTIONS_THIRD_PERSON_VIEW, thirdPerson); - - _t_keepPic = -1; - - // Save image - static char filename[256]; - sprintf(filename, "%s/games/com.mojang/img_%.4d.jpg", mc->externalStoragePath.c_str(), getTimeMs()); - - mc->platform()->saveScreenshot(filename, mc->width, mc->height); -} - -void LevelRenderer::levelEvent(Player* player, int type, int x, int y, int z, int data) { - switch (type) { - case LevelEvent::SOUND_OPEN_DOOR: - if (Mth::random() < 0.5f) { - level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, "random.door_open", 1, level->random.nextFloat() * 0.1f + 0.9f); - } else { - level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, "random.door_close", 1, level->random.nextFloat() * 0.1f + 0.9f); - } - break; - } -} +#include "LevelRenderer.h" + +#include "DirtyChunkSorter.h" +#include "DistanceChunkSorter.h" +#include "Chunk.h" +#include "TileRenderer.h" +#include "../Minecraft.h" +#include "../../util/Mth.h" +#include "../../world/entity/player/Player.h" +#include "../../world/level/tile/LevelEvent.h" +#include "../../world/level/tile/LeafTile.h" +#include "../../client/particle/ParticleEngine.h" +#include "../../client/particle/ParticleInclude.h" +#include "../sound/SoundEngine.h" +#include "culling/Culler.h" +#include "entity/EntityRenderDispatcher.h" +#include "../model/HumanoidModel.h" + +#include "GameRenderer.h" +#include "../../AppPlatform.h" +#include "../../util/PerfTimer.h" +#include "Textures.h" +#include "tileentity/TileEntityRenderDispatcher.h" +#include "../particle/BreakingItemParticle.h" + +#include "../../client/player/LocalPlayer.h" + +#ifdef GFX_SMALLER_CHUNKS +/* static */ const int LevelRenderer::CHUNK_SIZE = 8; +#else +/* static */ const int LevelRenderer::CHUNK_SIZE = 16; +#endif + +LevelRenderer::LevelRenderer( Minecraft* mc) +: mc(mc), + textures(mc->textures), + level(NULL), + cullStep(0), + + chunkLists(0), + xChunks(0), yChunks(0), zChunks(0), + + chunks(NULL), + sortedChunks(NULL), + + xMinChunk(0), yMinChunk(0), zMinChunk(0), + xMaxChunk(0), yMaxChunk(0), zMaxChunk(0), + + lastViewDistance(-1), + + noEntityRenderFrames(2), + totalEntities(0), + renderedEntities(0), + culledEntities(0), + + occlusionCheck(false), + totalChunks(0), offscreenChunks(0), renderedChunks(0), occludedChunks(0), emptyChunks(0), + + chunkFixOffs(0), + xOld(-9999), yOld(-9999), zOld(-9999), + + ticks(0), + skyList(0), starList(0), darkList(0), + tileRenderer(NULL), + destroyProgress(0) +{ +#ifdef OPENGL_ES + int maxChunksWidth = 2 * LEVEL_WIDTH / CHUNK_SIZE + 1; + numListsOrBuffers = maxChunksWidth * maxChunksWidth * (128/CHUNK_SIZE) * 3; + chunkBuffers = new GLuint[numListsOrBuffers]; + glGenBuffers2(numListsOrBuffers, chunkBuffers); + LOGI("numBuffers: %d\n", numListsOrBuffers); + //for (int i = 0; i < numListsOrBuffers; ++i) printf("bufId %d: %d\t", i, chunkBuffers[i]); + + glGenBuffers2(1, &skyBuffer); + generateSky(); +#else + int maxChunksWidth = 1024 / CHUNK_SIZE; + numListsOrBuffers = maxChunksWidth * maxChunksWidth * maxChunksWidth * 3; + chunkLists = glGenLists(numListsOrBuffers); +#endif +} + +LevelRenderer::~LevelRenderer() +{ + delete tileRenderer; + tileRenderer = NULL; + + deleteChunks(); + +#ifdef OPENGL_ES + glDeleteBuffers(numListsOrBuffers, chunkBuffers); + glDeleteBuffers(1, &skyBuffer); + delete[] chunkBuffers; +#else + glDeleteLists(numListsOrBuffers, chunkLists); +#endif +} + +void LevelRenderer::generateSky() { + Tesselator& t = Tesselator::instance; + float yy; + int s = 128; + int d = (256 / s) + 2; + yy = (float) (16); + t.begin(); + + skyVertexCount = 0; + for (int xx = -s * d; xx <= s * d; xx += s) { + for (int zz = -s * d; zz <= s * d; zz += s) { + t.vertex((float) xx + 0, yy, (float) zz + 0); + t.vertex((float)(xx + s), yy, (float) zz + 0); + t.vertex((float)(xx + s), yy, (float)(zz + s)); + t.vertex((float) xx + 0, yy, (float)(zz + s)); + //LOGI("x, z: %d, %d\n", xx, zz); + skyVertexCount += 4; + } + } + + t.end(true, skyBuffer); + //LOGI("skyvertexcount: %d\n", skyVertexCount); + //glEndList(); +} + +void LevelRenderer::setLevel( Level* level ) +{ + if (this->level != NULL) { + this->level->removeListener(this); + } + + xOld = -9999; + yOld = -9999; + zOld = -9999; + + EntityRenderDispatcher::getInstance()->setLevel(level); + EntityRenderDispatcher::getInstance()->setMinecraft(mc); + this->level = level; + + delete tileRenderer; + tileRenderer = new TileRenderer(level); + + if (level != NULL) { + level->addListener(this); + allChanged(); + } +} + +void LevelRenderer::allChanged() +{ + deleteChunks(); + + bool fancy = mc->options.getBooleanValue(OPTIONS_FANCY_GRAPHICS); + + Tile::leaves->setFancy(fancy); + Tile::leaves_carried->setFancy(fancy); + lastViewDistance = mc->options.getIntValue(OPTIONS_VIEW_DISTANCE); + + int dist = (512 >> 3) << (3 - lastViewDistance); + if (lastViewDistance <= 2 && mc->isPowerVR()) + dist = (int)((float)dist * 0.8f); + LOGI("last: %d, power: %d\n", lastViewDistance, mc->isPowerVR()); + + #if defined(RPI) + dist *= 0.6f; + #endif + + if (dist > 400) dist = 400; + /* + * if (Minecraft.FLYBY_MODE) { dist = 512 - CHUNK_SIZE * 2; } + */ + xChunks = (dist / LevelRenderer::CHUNK_SIZE) + 1; + yChunks = (128 / LevelRenderer::CHUNK_SIZE); + zChunks = (dist / LevelRenderer::CHUNK_SIZE) + 1; + chunksLength = xChunks * yChunks * zChunks; + LOGI("chunksLength: %d. Distance: %d\n", chunksLength, dist); + + chunks = new Chunk*[chunksLength]; + sortedChunks = new Chunk*[chunksLength]; + + int id = 0; + int count = 0; + + xMinChunk = 0; + yMinChunk = 0; + zMinChunk = 0; + xMaxChunk = xChunks; + yMaxChunk = yChunks; + zMaxChunk = zChunks; + dirtyChunks.clear(); + //renderableTileEntities.clear(); + + for (int x = 0; x < xChunks; x++) { + for (int y = 0; y < yChunks; y++) { + for (int z = 0; z < zChunks; z++) { + const int c = getLinearCoord(x, y, z); + Chunk* chunk = new Chunk(level, x * CHUNK_SIZE, y * CHUNK_SIZE, z * CHUNK_SIZE, CHUNK_SIZE, chunkLists + id, &chunkBuffers[id]); + + if (occlusionCheck) { + chunk->occlusion_id = 0;//occlusionCheckIds.get(count); + } + chunk->occlusion_querying = false; + chunk->occlusion_visible = true; + chunk->visible = true; + chunk->id = count++; + chunk->setDirty(); + + chunks[c] = chunk; + sortedChunks[c] = chunk; + dirtyChunks.push_back(chunk); + + id += 3; + } + } + } + + if (level != NULL) { + Entity* player = mc->cameraTargetPlayer; + if (player != NULL) { + this->resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + DistanceChunkSorter distanceSorter(player); + std::sort(sortedChunks, sortedChunks + chunksLength, distanceSorter); + } + } + noEntityRenderFrames = 2; +} + +void LevelRenderer::deleteChunks() +{ + for (int z = 0; z < zChunks; ++z) + for (int y = 0; y < yChunks; ++y) + for (int x = 0; x < xChunks; ++x) { + int c = getLinearCoord(x, y, z); + delete chunks[c]; + } + + delete[] chunks; + chunks = NULL; + + delete[] sortedChunks; + sortedChunks = NULL; +} + +void LevelRenderer::resortChunks( int xc, int yc, int zc ) +{ + xc -= CHUNK_SIZE / 2; + //yc -= CHUNK_SIZE / 2; + zc -= CHUNK_SIZE / 2; + xMinChunk = INT_MAX; + yMinChunk = INT_MAX; + zMinChunk = INT_MAX; + xMaxChunk = INT_MIN; + yMaxChunk = INT_MIN; + zMaxChunk = INT_MIN; + + int dirty = 0; + + int s2 = xChunks * CHUNK_SIZE; + int s1 = s2 / 2; + + for (int x = 0; x < xChunks; x++) { + int xx = x * CHUNK_SIZE; + + int xOff = (xx + s1 - xc); + if (xOff < 0) xOff -= (s2 - 1); + xOff /= s2; + xx -= xOff * s2; + + if (xx < xMinChunk) xMinChunk = xx; + if (xx > xMaxChunk) xMaxChunk = xx; + + for (int z = 0; z < zChunks; z++) { + int zz = z * CHUNK_SIZE; + int zOff = (zz + s1 - zc); + if (zOff < 0) zOff -= (s2 - 1); + zOff /= s2; + zz -= zOff * s2; + + if (zz < zMinChunk) zMinChunk = zz; + if (zz > zMaxChunk) zMaxChunk = zz; + + for (int y = 0; y < yChunks; y++) { + int yy = y * CHUNK_SIZE; + if (yy < yMinChunk) yMinChunk = yy; + if (yy > yMaxChunk) yMaxChunk = yy; + + Chunk* chunk = chunks[(z * yChunks + y) * xChunks + x]; + bool wasDirty = chunk->isDirty(); + chunk->setPos(xx, yy, zz); + if (!wasDirty && chunk->isDirty()) { + dirtyChunks.push_back(chunk); + ++dirty; + } + } + } + } +} + +int LevelRenderer::render( Mob* player, int layer, float alpha ) +{ + if (mc->options.getIntValue(OPTIONS_VIEW_DISTANCE) != lastViewDistance) { + allChanged(); + } + + TIMER_PUSH("sortchunks"); + for (int i = 0; i < 10; i++) { + chunkFixOffs = (chunkFixOffs + 1) % chunksLength; + Chunk* c = chunks[chunkFixOffs]; + if (c->isDirty() && std::find(dirtyChunks.begin(), dirtyChunks.end(), c) == dirtyChunks.end()) { + dirtyChunks.push_back(c); + } + } + + if (layer == 0) { + totalChunks = 0; + offscreenChunks = 0; + occludedChunks = 0; + renderedChunks = 0; + emptyChunks = 0; + } + + float xOff = player->xOld + (player->x - player->xOld) * alpha; + float yOff = player->yOld + (player->y - player->yOld) * alpha; + float zOff = player->zOld + (player->z - player->zOld) * alpha; + + float xd = player->x - xOld; + float yd = player->y - yOld; + float zd = player->z - zOld; + if (xd * xd + yd * yd + zd * zd > 4 * 4) { + xOld = player->x; + yOld = player->y; + zOld = player->z; + + resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + DistanceChunkSorter distanceSorter(player); + std::sort(sortedChunks, sortedChunks + chunksLength, distanceSorter); + } + + int count = 0; + if (occlusionCheck && !mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D) && layer == 0) { + int from = 0; + int to = 16; + //checkQueryResults(from, to); + for (int i = from; i < to; i++) { + sortedChunks[i]->occlusion_visible = true; + } + + count += renderChunks(from, to, layer, alpha); + + do { + from = to; + to = to * 2; + if (to > chunksLength) to = chunksLength; + + glDisable2(GL_TEXTURE_2D); + glDisable2(GL_LIGHTING); + glDisable2(GL_ALPHA_TEST); + glDisable2(GL_FOG); + + glColorMask(false, false, false, false); + glDepthMask(false); + //checkQueryResults(from, to); + glPushMatrix2(); + float xo = 0; + float yo = 0; + float zo = 0; + for (int i = from; i < to; i++) { + if (sortedChunks[i]->isEmpty()) { + sortedChunks[i]->visible = false; + continue; + } + if (!sortedChunks[i]->visible) { + sortedChunks[i]->occlusion_visible = true; + } + + if (sortedChunks[i]->visible && !sortedChunks[i]->occlusion_querying) { + float dist = Mth::sqrt(sortedChunks[i]->distanceToSqr(player)); + + int frequency = (int) (1 + dist / 128); + + if (ticks % frequency == i % frequency) { + Chunk* chunk = sortedChunks[i]; + float xt = (float) (chunk->x - xOff); + float yt = (float) (chunk->y - yOff); + float zt = (float) (chunk->z - zOff); + float xdd = xt - xo; + float ydd = yt - yo; + float zdd = zt - zo; + + if (xdd != 0 || ydd != 0 || zdd != 0) { + glTranslatef2(xdd, ydd, zdd); + xo += xdd; + yo += ydd; + zo += zdd; + } + + sortedChunks[i]->renderBB(); + sortedChunks[i]->occlusion_querying = true; + } + } + } + glPopMatrix2(); + glColorMask(true, true, true, true); + glDepthMask(true); + glEnable2(GL_TEXTURE_2D); + glEnable2(GL_ALPHA_TEST); + glEnable2(GL_FOG); + + count += renderChunks(from, to, layer, alpha); + + } while (to < chunksLength); + + } else { + TIMER_POP_PUSH("render"); + count += renderChunks(0, chunksLength, layer, alpha); + } + + TIMER_POP(); + return count; +} + +void LevelRenderer::renderDebug(const AABB& b, float a) const { + float x0 = b.x0; + float x1 = b.x1; + float y0 = b.y0; + float y1 = b.y1; + float z0 = b.z0; + float z1 = b.z1; + float u0 = 0, v0 = 0; + float u1 = 1, v1 = 1; + + glEnable2(GL_BLEND); + glBlendFunc2(GL_DST_COLOR, GL_SRC_COLOR); + glDisable2(GL_TEXTURE_2D); + glColor4f2(1, 1, 1, 1); + + textures->loadAndBindTexture("terrain.png"); + + Tesselator& t = Tesselator::instance; + t.begin(); + t.color(255, 255, 255, 255); + + t.offset(((Mob*)mc->player)->getPos(a).negated()); + + // up + t.vertexUV(x0, y0, z1, u0, v1); + t.vertexUV(x0, y0, z0, u0, v0); + t.vertexUV(x1, y0, z0, u1, v0); + t.vertexUV(x1, y0, z1, u1, v1); + + // down + t.vertexUV(x1, y1, z1, u1, v1); + t.vertexUV(x1, y1, z0, u1, v0); + t.vertexUV(x0, y1, z0, u0, v0); + t.vertexUV(x0, y1, z1, u0, v1); + + // north + t.vertexUV(x0, y1, z0, u1, v0); + t.vertexUV(x1, y1, z0, u0, v0); + t.vertexUV(x1, y0, z0, u0, v1); + t.vertexUV(x0, y0, z0, u1, v1); + + // south + t.vertexUV(x0, y1, z1, u0, v0); + t.vertexUV(x0, y0, z1, u0, v1); + t.vertexUV(x1, y0, z1, u1, v1); + t.vertexUV(x1, y1, z1, u1, v0); + + // west + t.vertexUV(x0, y1, z1, u1, v0); + t.vertexUV(x0, y1, z0, u0, v0); + t.vertexUV(x0, y0, z0, u0, v1); + t.vertexUV(x0, y0, z1, u1, v1); + + // east + t.vertexUV(x1, y0, z1, u0, v1); + t.vertexUV(x1, y0, z0, u1, v1); + t.vertexUV(x1, y1, z0, u1, v0); + t.vertexUV(x1, y1, z1, u0, v0); + + t.offset(0, 0, 0); + t.draw(); + + glEnable2(GL_TEXTURE_2D); + glDisable2(GL_BLEND); +} + +void LevelRenderer::render(const AABB& b) const +{ + Tesselator& t = Tesselator::instance; + + glColor4f2(1, 1, 1, 1); + + textures->loadAndBindTexture("terrain.png"); + + //t.begin(); + t.color(255, 255, 255, 255); + + t.offset(((Mob*)mc->player)->getPos(0).negated()); + + t.begin(GL_LINE_STRIP); + t.vertex(b.x0, b.y0, b.z0); + t.vertex(b.x1, b.y0, b.z0); + t.vertex(b.x1, b.y0, b.z1); + t.vertex(b.x0, b.y0, b.z1); + t.vertex(b.x0, b.y0, b.z0); + t.draw(); + + t.begin(GL_LINE_STRIP); + t.vertex(b.x0, b.y1, b.z0); + t.vertex(b.x1, b.y1, b.z0); + t.vertex(b.x1, b.y1, b.z1); + t.vertex(b.x0, b.y1, b.z1); + t.vertex(b.x0, b.y1, b.z0); + t.draw(); + + t.begin(GL_LINES); + t.vertex(b.x0, b.y0, b.z0); + t.vertex(b.x0, b.y1, b.z0); + t.vertex(b.x1, b.y0, b.z0); + t.vertex(b.x1, b.y1, b.z0); + t.vertex(b.x1, b.y0, b.z1); + t.vertex(b.x1, b.y1, b.z1); + t.vertex(b.x0, b.y0, b.z1); + t.vertex(b.x0, b.y1, b.z1); + + t.offset(0, 0, 0); + t.draw(); +} + +//void LevelRenderer::checkQueryResults( int from, int to ) +//{ +// for (int i = from; i < to; i++) { +// if (sortedChunks[i]->occlusion_querying) { +// // I wanna do a fast occusion culler here. +// } +// } +//} + +int LevelRenderer::renderChunks( int from, int to, int layer, float alpha ) +{ + _renderChunks.clear(); + int count = 0; + for (int i = from; i < to; i++) { + if (layer == 0) { + totalChunks++; + if (sortedChunks[i]->empty[layer]) emptyChunks++; + else if (!sortedChunks[i]->visible) offscreenChunks++; + else if (occlusionCheck && !sortedChunks[i]->occlusion_visible) occludedChunks++; + else renderedChunks++; + } + + if (!sortedChunks[i]->empty[layer] && sortedChunks[i]->visible && sortedChunks[i]->occlusion_visible) { + int list = sortedChunks[i]->getList(layer); + if (list >= 0) { + _renderChunks.push_back(sortedChunks[i]); + count++; + } + } + } + + Mob* player = mc->cameraTargetPlayer; + float xOff = player->xOld + (player->x - player->xOld) * alpha; + float yOff = player->yOld + (player->y - player->yOld) * alpha; + float zOff = player->zOld + (player->z - player->zOld) * alpha; + + //int lists = 0; + renderList.clear(); + renderList.init(xOff, yOff, zOff); + + for (unsigned int i = 0; i < _renderChunks.size(); ++i) { + Chunk* chunk = _renderChunks[i]; + #ifdef USE_VBO + renderList.addR(chunk->getRenderChunk(layer)); + #else + renderList.add(chunk->getList(layer)); + #endif + renderList.next(); + } + + renderSameAsLast(layer, alpha); + + return count; +} + +void LevelRenderer::renderSameAsLast( int layer, float alpha ) +{ + renderList.render(); +} + +void LevelRenderer::tick() +{ + ticks++; +} + +bool LevelRenderer::updateDirtyChunks( Mob* player, bool force ) +{ + bool slow = false; + + if (slow) { + DirtyChunkSorter dirtySorter(player); + std::sort(dirtyChunks.begin(), dirtyChunks.end(), dirtySorter); + int s = dirtyChunks.size() - 1; + int amount = dirtyChunks.size(); + for (int i = 0; i < amount; i++) { + Chunk* chunk = dirtyChunks[s-i]; + if (!force) { + if (chunk->distanceToSqr(player) > 16 * 16) { + if (chunk->visible) { + if (i >= MAX_VISIBLE_REBUILDS_PER_FRAME) return false; + } else { + if (i >= MAX_INVISIBLE_REBUILDS_PER_FRAME) return false; + } + } + } else { + if (!chunk->visible) continue; + } + chunk->rebuild(); + + dirtyChunks.erase( std::find(dirtyChunks.begin(), dirtyChunks.end(), chunk) ); // @q: s-i? + chunk->setClean(); + } + + return dirtyChunks.size() == 0; + } else { + const int count = 3; + + DirtyChunkSorter dirtyChunkSorter(player); + Chunk* toAdd[count] = {NULL}; + std::vector* nearChunks = NULL; + + int pendingChunkSize = dirtyChunks.size(); + int pendingChunkRemoved = 0; + + for (int i = 0; i < pendingChunkSize; i++) { + Chunk* chunk = dirtyChunks[i]; + + if (!force) { + if (chunk->distanceToSqr(player) > 1024.0f) { + int index; + + // is this chunk in the closest ? + for (index = 0; index < count; index++) { + if (toAdd[index] != NULL && dirtyChunkSorter(toAdd[index], chunk) == false) { + break; + } + } + + index--; + + if (index > 0) { + int x = index; + while (--x != 0) { + toAdd[x - 1] = toAdd[x]; + } + toAdd[index] = chunk; + } + + continue; + } + } else if (!chunk->visible) { + continue; + } + + // chunk is very close -- always render + + if (nearChunks == NULL) { + nearChunks = new std::vector(); + } + + pendingChunkRemoved++; + nearChunks->push_back(chunk); + dirtyChunks[i] = NULL; + } + + // if there are nearby chunks that need to be prepared for + // rendering, sort them and then process them + static const float MaxFrameTime = 1.0f / 100.0f; + Stopwatch chunkWatch; + chunkWatch.start(); + + if (nearChunks != NULL) { + if (nearChunks->size() > 1) { + std::sort(nearChunks->begin(), nearChunks->end(), dirtyChunkSorter); + } + + for (int i = nearChunks->size() - 1; i >= 0; i--) { + Chunk* chunk = (*nearChunks)[i]; + chunk->rebuild(); + chunk->setClean(); + } + delete nearChunks; + } + + // render the nearest chunks (farther than 1024 units away) + int secondaryRemoved = 0; + + for (int i = count - 1; i >= 0; i--) { + Chunk* chunk = toAdd[i]; + if (chunk != NULL) { + + float ttt = chunkWatch.stopContinue(); + if (ttt >= MaxFrameTime) { + //LOGI("Too much work, I quit2!\n"); + break; + } + + if (!chunk->visible && i != count - 1) { + // escape early if chunks aren't ready + toAdd[i] = NULL; + toAdd[0] = NULL; + break; + } + toAdd[i]->rebuild(); + toAdd[i]->setClean(); + secondaryRemoved++; + } + } + + // compact by removing nulls + int cursor = 0; + int target = 0; + int arraySize = dirtyChunks.size(); + while (cursor != arraySize) { + Chunk* chunk = dirtyChunks[cursor]; + if (chunk != NULL) { + bool remove = false; + for (int i = 0; i < count && !remove; i++) + if (chunk == toAdd[i]) { + remove = true; + } + + if (!remove) { + //if (chunk == toAdd[0] || chunk == toAdd[1] || chunk == toAdd[2]) { + // ; // this chunk was rendered and should be removed + //} else { + if (target != cursor) { + dirtyChunks[target] = chunk; + } + target++; + } + } + cursor++; + } + + // trim + if (cursor > target) + dirtyChunks.erase(dirtyChunks.begin() + target, dirtyChunks.end()); + + return pendingChunkSize == (pendingChunkRemoved + secondaryRemoved); + } +} + +void LevelRenderer::renderHit( Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a ) +{ + if (mode == 0) { + if (destroyProgress > 0) { + Tesselator& t = Tesselator::instance; + glEnable2(GL_BLEND); + glBlendFunc2(GL_DST_COLOR, GL_SRC_COLOR); + + textures->loadAndBindTexture("terrain.png"); + glPushMatrix2(); + + int tileId = level->getTile(h.x, h.y, h.z); + Tile* tile = tileId > 0 ? Tile::tiles[tileId] : NULL; + //glDisable2(GL_ALPHA_TEST); + + glPolygonOffset(-3.0f, -3.0f); + glEnable2(GL_POLYGON_OFFSET_FILL); + t.begin(); + t.color(1.0f, 1.0f, 1.0f, 0.5f); + t.noColor(); + float xo = player->xOld + (player->x - player->xOld) * a; + float yo = player->yOld + (player->y - player->yOld) * a; + float zo = player->zOld + (player->z - player->zOld) * a; + + t.offset(-xo, -yo, -zo); + //t.noColor(); + + if (tile == NULL) tile = Tile::rock; + const int progress = (int) (destroyProgress * 10); + tileRenderer->tesselateInWorld(tile, h.x, h.y, h.z, 15 * 16 + progress); + + t.draw(); + t.offset(0, 0, 0); + glPolygonOffset(0.0f, 0.0f); + glDisable2(GL_POLYGON_OFFSET_FILL); + //glDisable2(GL_ALPHA_TEST); + glDisable2(GL_BLEND); + + glDepthMask(true); + glPopMatrix2(); + } + } + //else if (inventoryItem != NULL) { + // glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // float br = ((float) (util.Mth::sin(System.currentTimeMillis() / 100.0f)) * 0.2f + 0.8f); + // glColor4f2(br, br, br, ((float) (util.Mth::sin(System.currentTimeMillis() / 200.0f)) * 0.2f + 0.5f)); + + // int id = textures.loadTexture("terrain.png"); + // glBindTexture2(GL_TEXTURE_2D, id); + // int x = h.x; + // int y = h.y; + // int z = h.z; + // if (h.f == 0) y--; + // if (h.f == 1) y++; + // if (h.f == 2) z--; + // if (h.f == 3) z++; + // if (h.f == 4) x--; + // if (h.f == 5) x++; + // /* + // * t.begin(); t.noColor(); Tile.tiles[tileType].tesselate(level, x, + // * y, z, t); t.end(); + // */ + // } +} + +void LevelRenderer::renderHitOutline( Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a ) +{ + if (mode == 0 && h.type == TILE) { + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f2(0, 0, 0, 0.4f); + glLineWidth(1.0f); + glDisable2(GL_TEXTURE_2D); + glDepthMask(false); + float ss = 0.002f; + int tileId = level->getTile(h.x, h.y, h.z); + + if (tileId > 0) { + Tile::tiles[tileId]->updateShape(level, h.x, h.y, h.z); + float xo = player->xOld + (player->x - player->xOld) * a; + float yo = player->yOld + (player->y - player->yOld) * a; + float zo = player->zOld + (player->z - player->zOld) * a; + render(Tile::tiles[tileId]->getTileAABB(level, h.x, h.y, h.z).grow(ss, ss, ss).cloneMove(-xo, -yo, -zo)); + } + glDepthMask(true); + glEnable2(GL_TEXTURE_2D); + glDisable2(GL_BLEND); + } +} + +void LevelRenderer::setDirty( int x0, int y0, int z0, int x1, int y1, int z1 ) +{ + int _x0 = Mth::intFloorDiv(x0, CHUNK_SIZE); + int _y0 = Mth::intFloorDiv(y0, CHUNK_SIZE); + int _z0 = Mth::intFloorDiv(z0, CHUNK_SIZE); + int _x1 = Mth::intFloorDiv(x1, CHUNK_SIZE); + int _y1 = Mth::intFloorDiv(y1, CHUNK_SIZE); + int _z1 = Mth::intFloorDiv(z1, CHUNK_SIZE); + + for (int x = _x0; x <= _x1; x++) { + int xx = x % xChunks; + if (xx < 0) xx += xChunks; + for (int y = _y0; y <= _y1; y++) { + int yy = y % yChunks; + if (yy < 0) yy += yChunks; + for (int z = _z0; z <= _z1; z++) { + int zz = z % zChunks; + if (zz < 0) zz += zChunks; + + int p = ((zz) * yChunks + (yy)) * xChunks + (xx); + Chunk* chunk = chunks[p]; + if (!chunk->isDirty()) { + dirtyChunks.push_back(chunk); + chunk->setDirty(); + } + } + } + } +} + +void LevelRenderer::tileChanged( int x, int y, int z) +{ + setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1); +} + + +void LevelRenderer::setTilesDirty( int x0, int y0, int z0, int x1, int y1, int z1 ) +{ + setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1); +} + + +void LevelRenderer::cull( Culler* culler, float a ) +{ + for (int i = 0; i < chunksLength; i++) { + if (!chunks[i]->isEmpty()) { + if (!chunks[i]->visible || ((i + cullStep) & 15) == 0) { + chunks[i]->cull(culler); + } + } + } + cullStep++; +} + +void LevelRenderer::skyColorChanged() +{ + for (int i = 0; i < chunksLength; i++) { + if (chunks[i]->skyLit) { + if (!chunks[i]->isDirty()) { + dirtyChunks.push_back(chunks[i]); + chunks[i]->setDirty(); + } + } + } +} + +bool entityRenderPredicate(const Entity* a, const Entity* b) { + return a->entityRendererId < b->entityRendererId; +} + +void LevelRenderer::renderEntities(Vec3 cam, Culler* culler, float a) { + if (noEntityRenderFrames > 0) { + noEntityRenderFrames--; + return; + } + + TIMER_PUSH("prepare"); + TileEntityRenderDispatcher::getInstance()->prepare(level, textures, mc->font, mc->cameraTargetPlayer, a); + EntityRenderDispatcher::getInstance()->prepare(level, mc->font, mc->cameraTargetPlayer, &mc->options, a); + + totalEntities = 0; + renderedEntities = 0; + culledEntities = 0; + + Entity* player = mc->cameraTargetPlayer; + EntityRenderDispatcher::xOff = TileEntityRenderDispatcher::xOff = (player->xOld + (player->x - player->xOld) * a); + EntityRenderDispatcher::yOff = TileEntityRenderDispatcher::yOff = (player->yOld + (player->y - player->yOld) * a); + EntityRenderDispatcher::zOff = TileEntityRenderDispatcher::zOff = (player->zOld + (player->z - player->zOld) * a); + + glEnableClientState2(GL_VERTEX_ARRAY); + glEnableClientState2(GL_TEXTURE_COORD_ARRAY); + + TIMER_POP_PUSH("entities"); + const EntityList& entities = level->getAllEntities(); + totalEntities = entities.size(); + if (totalEntities > 0) { + Entity** toRender = new Entity*[totalEntities]; + for (int i = 0; i < totalEntities; i++) { + Entity* entity = entities[i]; + + bool thirdPerson = mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW); + + if (entity->shouldRender(cam) && culler->isVisible(entity->bb)) + { + if (entity == mc->cameraTargetPlayer && thirdPerson == 0 && mc->cameraTargetPlayer->isPlayer() && !((Player*)mc->cameraTargetPlayer)->isSleeping()) continue; + if (entity == mc->cameraTargetPlayer && !thirdPerson) + continue; + if (!level->hasChunkAt(Mth::floor(entity->x), Mth::floor(entity->y), Mth::floor(entity->z))) + continue; + + toRender[renderedEntities++] = entity; + //EntityRenderDispatcher::getInstance()->render(entity, a); + } + } + + if (renderedEntities > 0) { + std::sort(&toRender[0], &toRender[renderedEntities], entityRenderPredicate); + for (int i = 0; i < renderedEntities; ++i) { + EntityRenderDispatcher* disp = EntityRenderDispatcher::getInstance(); + disp->render(toRender[i], a); + } + } + + delete[] toRender; + } + + TIMER_POP_PUSH("tileentities"); + for (unsigned int i = 0; i < level->tileEntities.size(); i++) { + TileEntityRenderDispatcher::getInstance()->render(level->tileEntities[i], a); + } + + glDisableClientState2(GL_VERTEX_ARRAY); + glDisableClientState2(GL_TEXTURE_COORD_ARRAY); + + TIMER_POP(); +} + +std::string LevelRenderer::gatherStats1() { + std::stringstream ss; + ss << "C: " << renderedChunks << "/" << totalChunks << ". F: " << offscreenChunks << ", O: " << occludedChunks << ", E: " << emptyChunks << "\n"; + return ss.str(); +} + +// +// /*public*/ std::string gatherStats2() { +// return "E: " + renderedEntities + "/" + totalEntities + ". B: " + culledEntities + ", I: " + ((totalEntities - culledEntities) - renderedEntities); +// } +// +// int[] toRender = new int[50000]; +// IntBuffer resultBuffer = MemoryTracker.createIntBuffer(64); + +void LevelRenderer::renderSky(float alpha) { + if (mc->level->dimension->foggy) return; + + glDisable2(GL_TEXTURE_2D); + Vec3 sc = level->getSkyColor(mc->cameraTargetPlayer, alpha); + float sr = (float) sc.x; + float sg = (float) sc.y; + float sb = (float) sc.z;// + 0.5f; + + if (mc->options.getBooleanValue(OPTIONS_ANAGLYPH_3D)) { + float srr = (sr * 30.0f + sg * 59.0f + sb * 11.0f) / 100.0f; + float sgg = (sr * 30.0f + sg * 70.0f) / (100.0f); + float sbb = (sr * 30.0f + sb * 70.0f) / (100.0f); + + sr = srr; + sg = sgg; + sb = sbb; + } + glColor4f2(sr, sg, Mth::Min(1.0f, sb), 1); + + //Tesselator& t = Tesselator::instance; + + glEnable2(GL_FOG); + glColor4f2(sr, sg, sb, 1.0f); + +#ifdef OPENGL_ES + drawArrayVT(skyBuffer, skyVertexCount); +#endif + glEnable2(GL_TEXTURE_2D); +} + +void LevelRenderer::renderClouds( float alpha ) { + //if (!mc->level->dimension->isNaturalDimension()) return; + glEnable2(GL_TEXTURE_2D); + glDisable(GL_CULL_FACE); + float yOffs = (float) (mc->player->yOld + (mc->player->y - mc->player->yOld) * alpha); + int s = 32; + int d = 256 / s; + Tesselator& t = Tesselator::instance; + + //glBindTexture(GL_TEXTURE_2D, texturesloadTexture("/environment/clouds.png")); + textures->loadAndBindTexture("environment/clouds.png"); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + Vec3 cc = level->getCloudColor(alpha); + float cr = (float) cc.x; + float cg = (float) cc.y; + float cb = (float) cc.z; + + float scale = 1 / 2048.0f; + + float time = (ticks + alpha); + float xo = mc->player->xo + (mc->player->x - mc->player->xo) * alpha + time * 0.03f; + float zo = mc->player->zo + (mc->player->z - mc->player->zo) * alpha; + int xOffs = Mth::floor(xo / 2048); + int zOffs = Mth::floor(zo / 2048); + xo -= xOffs * 2048; + zo -= zOffs * 2048; + + float yy = /*level.dimension.getCloudHeight()*/ 128 - yOffs + 0.33f;//mc->player->y + 1; + float uo = (float) (xo * scale); + float vo = (float) (zo * scale); + t.begin(); + + t.color(cr, cg, cb, 0.8f); + for (int xx = -s * d; xx < +s * d; xx += s) { + for (int zz = -s * d; zz < +s * d; zz += s) { + t.vertexUV((float)xx, yy, (float)zz + s, xx * scale + uo, (zz + s) * scale + vo); + t.vertexUV((float)xx + s, yy, (float)zz + s, (xx + s) * scale + uo, (zz + s) * scale + vo); + t.vertexUV((float)xx + s, yy, (float)zz, (xx + s) * scale + uo, zz * scale + vo); + t.vertexUV((float)xx, yy, (float)zz, xx * scale + uo, zz * scale + vo); + } + } + t.endOverrideAndDraw(); + glColor4f(1, 1, 1, 1.0f); + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); +} + +void LevelRenderer::playSound(const std::string& name, float x, float y, float z, float volume, float pitch) { + // @todo: deny sounds here if sound is off (rather than waiting 'til SoundEngine) + float dd = 16; + + if (volume > 1) dd *= volume; + if (mc->cameraTargetPlayer->distanceToSqr(x, y, z) < dd * dd) { + mc->soundEngine->play(name, x, y, z, volume, pitch); + } +} + +void LevelRenderer::addParticle(const std::string& name, float x, float y, float z, float xa, float ya, float za, int data) { + + float xd = mc->cameraTargetPlayer->x - x; + float yd = mc->cameraTargetPlayer->y - y; + float zd = mc->cameraTargetPlayer->z - z; + float distanceSquared = xd * xd + yd * yd + zd * zd; + + //Particle* p = NULL; + //if (name == "hugeexplosion") p = new HugeExplosionSeedParticle(level, x, y, z, xa, ya, za); + //else if (name == "largeexplode") p = new HugeExplosionParticle(textures, level, x, y, z, xa, ya, za); + + //if (p) { + // if (distanceSquared < 32 * 32) { + // mc->particleEngine->add(p); + // } else { delete p; } + // return; + //} + + const float particleDistance = 16; + if (distanceSquared > particleDistance * particleDistance) return; + + //static Stopwatch sw; + //sw.start(); + + if (name == "bubble") mc->particleEngine->add(new BubbleParticle(level, x, y, z, xa, ya, za)); + else if (name == "crit") mc->particleEngine->add(new CritParticle2(level, x, y, z, xa, ya, za)); + else if (name == "smoke") mc->particleEngine->add(new SmokeParticle(level, x, y, z, xa, ya, za)); + //else if (name == "note") mc->particleEngine->add(new NoteParticle(level, x, y, z, xa, ya, za)); + else if (name == "explode") mc->particleEngine->add(new ExplodeParticle(level, x, y, z, xa, ya, za)); + else if (name == "flame") mc->particleEngine->add(new FlameParticle(level, x, y, z, xa, ya, za)); + else if (name == "lava") mc->particleEngine->add(new LavaParticle(level, x, y, z)); + //else if (name == "splash") mc->particleEngine->add(new SplashParticle(level, x, y, z, xa, ya, za)); + else if (name == "largesmoke") mc->particleEngine->add(new SmokeParticle(level, x, y, z, xa, ya, za, 2.5f)); + else if (name == "reddust") mc->particleEngine->add(new RedDustParticle(level, x, y, z, xa, ya, za)); + else if (name == "iconcrack") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, xa, ya, za, Item::items[data])); + else if (name == "snowballpoof") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, Item::snowBall)); + //else if (name == "snowballpoof") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, Item::snowBall)); + //else if (name == "slime") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, Item::slimeBall)); + //else if (name == "heart") mc->particleEngine->add(new HeartParticle(level, x, y, z, xa, ya, za)); + + //sw.stop(); + //sw.printEvery(50, "add-particle-string"); +} + +/* +void LevelRenderer::addParticle(ParticleType::Id name, float x, float y, float z, float xa, float ya, float za, int data) { + float xd = mc->cameraTargetPlayer->x - x; + float yd = mc->cameraTargetPlayer->y - y; + float zd = mc->cameraTargetPlayer->z - z; + + const float particleDistance = 16; + if (xd * xd + yd * yd + zd * zd > particleDistance * particleDistance) return; + + //static Stopwatch sw; + //sw.start(); + + //Particle* p = NULL; + + if (name == ParticleType::bubble) mc->particleEngine->add( new BubbleParticle(level, x, y, z, xa, ya, za) ); + else if (name == ParticleType::crit) mc->particleEngine->add(new CritParticle2(level, x, y, z, xa, ya, za) ); + else if (name == ParticleType::smoke) mc->particleEngine->add(new SmokeParticle(level, x, y, z, xa, ya, za) ); + else if (name == ParticleType::explode) mc->particleEngine->add( new ExplodeParticle(level, x, y, z, xa, ya, za) ); + else if (name == ParticleType::flame) mc->particleEngine->add( new FlameParticle(level, x, y, z, xa, ya, za) ); + else if (name == ParticleType::lava) mc->particleEngine->add( new LavaParticle(level, x, y, z) ); + else if (name == ParticleType::largesmoke) mc->particleEngine->add( new SmokeParticle(level, x, y, z, xa, ya, za, 2.5f) ); + else if (name == ParticleType::reddust) mc->particleEngine->add( new RedDustParticle(level, x, y, z, xa, ya, za) ); + else if (name == ParticleType::iconcrack) mc->particleEngine->add( new BreakingItemParticle(level, x, y, z, xa, ya, za, Item::items[data]) ); + + //switch (name) { + // case ParticleType::bubble: p = new BubbleParticle(level, x, y, z, xa, ya, za); break; + // case ParticleType::crit: p = new CritParticle2(level, x, y, z, xa, ya, za); break; + // case ParticleType::smoke: p = new SmokeParticle(level, x, y, z, xa, ya, za); break; + // //case ParticleType::note: p = new NoteParticle(level, x, y, z, xa, ya, za); break; + // case ParticleType::explode: p = new ExplodeParticle(level, x, y, z, xa, ya, za); break; + // case ParticleType::flame: p = new FlameParticle(level, x, y, z, xa, ya, za); break; + // case ParticleType::lava: p = new LavaParticle(level, x, y, z); break; + // //case ParticleType::splash: p = new SplashParticle(level, x, y, z, xa, ya, za); break; + // case ParticleType::largesmoke: p = new SmokeParticle(level, x, y, z, xa, ya, za, 2.5f); break; + // case ParticleType::reddust: p = new RedDustParticle(level, x, y, z, xa, ya, za); break; + // case ParticleType::iconcrack: p = new BreakingItemParticle(level, x, y, z, xa, ya, za, Item::items[data]); break; + // //case ParticleType::snowballpoof: p = new BreakingItemParticle(level, x, y, z, Item::snowBall); break; + // //case ParticleType::slime: p = new BreakingItemParticle(level, x, y, z, Item::slimeBall); break; + // //case ParticleType::heart: p = new HeartParticle(level, x, y, z, xa, ya, za); break; + // default: + // LOGW("Couldn't find particle of type: %d\n", name); + // break; + //} + //if (p) { + // mc->particleEngine->add(p); + //} + + //sw.stop(); + //sw.printEvery(50, "add-particle-enum"); +} +*/ + +void LevelRenderer::renderHitSelect( Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a ) +{ + //if (h.type == TILE) LOGI("type: %s @ (%d, %d, %d)\n", Tile::tiles[level->getTile(h.x, h.y, h.z)]->getDescriptionId().c_str(), h.x, h.y, h.z); + + if (mode == 0) { + + Tesselator& t = Tesselator::instance; + glEnable2(GL_BLEND); + glDisable2(GL_TEXTURE_2D); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE); + glBlendFunc2(GL_DST_COLOR, GL_SRC_COLOR); + glEnable2(GL_DEPTH_TEST); + + textures->loadAndBindTexture("terrain.png"); + + int tileId = level->getTile(h.x, h.y, h.z); + Tile* tile = tileId > 0 ? Tile::tiles[tileId] : NULL; + glDisable2(GL_ALPHA_TEST); + + //LOGI("block: %d - %d (%s)\n", tileId, level->getData(h.x, h.y, h.z), tile==NULL?"null" : tile->getDescriptionId().c_str() ); + + const float br = 0.65f; + glColor4f2(br * 1.0f, br * 1.0f, br * 1.0f, br * 1.0f); + glPushMatrix2(); + + //glPolygonOffset(-.3f, -.3f); + glPolygonOffset(-1.f, -1.f); //Implementation dependent units + glEnable2(GL_POLYGON_OFFSET_FILL); + float xo = player->xOld + (player->x - player->xOld) * a; + float yo = player->yOld + (player->y - player->yOld) * a; + float zo = player->zOld + (player->z - player->zOld) * a; + + t.begin(); + t.offset(-xo, -yo, -zo); + t.noColor(); + + if (tile == NULL) tile = Tile::rock; + tileRenderer->tesselateInWorld(tile, h.x, h.y, h.z); + + t.draw(); + t.offset(0, 0, 0); + glPolygonOffset(0.0f, 0.0f); + + glDisable2(GL_POLYGON_OFFSET_FILL); + glEnable2(GL_TEXTURE_2D); + + glDepthMask(true); + glPopMatrix2(); + + glEnable2(GL_ALPHA_TEST); + glDisable2(GL_BLEND); + } +} + +void LevelRenderer::onGraphicsReset() +{ + generateSky(); + + // Get new buffers +#ifdef OPENGL_ES + glGenBuffers2(numListsOrBuffers, chunkBuffers); +#else + chunkLists = glGenLists(numListsOrBuffers); +#endif + + // Rebuild + allChanged(); +} + +void LevelRenderer::entityAdded( Entity* entity ) +{ + if (!entity->isPlayer()) + return; + + // Hack to (hopefully) get the players to show + EntityRenderDispatcher::getInstance()->onGraphicsReset(); +} + +int _t_keepPic = -1; + +void LevelRenderer::takePicture( TripodCamera* cam, Entity* entity ) +{ + // Push old values + Mob* oldCameraEntity = mc->cameraTargetPlayer; + bool hideGui = mc->options.getBooleanValue(OPTIONS_HIDEGUI); + bool thirdPerson = mc->options.getBooleanValue(OPTIONS_THIRD_PERSON_VIEW); + + // @huge @attn: This is highly illegal, super temp! + mc->cameraTargetPlayer = (Mob*)cam; + mc->options.set(OPTIONS_HIDEGUI, true); + mc->options.set(OPTIONS_THIRD_PERSON_VIEW, false); + + mc->gameRenderer->renderLevel(0); + + // Pop values back + mc->cameraTargetPlayer = oldCameraEntity; + mc->options.set(OPTIONS_HIDEGUI, hideGui); + mc->options.set(OPTIONS_THIRD_PERSON_VIEW, thirdPerson); + + _t_keepPic = -1; + + // Save image + static char filename[256]; + sprintf(filename, "%s/games/com.mojang/img_%.4d.jpg", mc->externalStoragePath.c_str(), getTimeMs()); + + mc->platform()->saveScreenshot(filename, mc->width, mc->height); +} + +void LevelRenderer::levelEvent(Player* player, int type, int x, int y, int z, int data) { + switch (type) { + case LevelEvent::SOUND_OPEN_DOOR: + if (Mth::random() < 0.5f) { + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, "random.door_open", 1, level->random.nextFloat() * 0.1f + 0.9f); + } else { + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, "random.door_close", 1, level->random.nextFloat() * 0.1f + 0.9f); + } + break; + } +} diff --git a/src/client/renderer/LevelRenderer.h b/src/client/renderer/LevelRenderer.h index df43de1..8116d93 100755 --- a/src/client/renderer/LevelRenderer.h +++ b/src/client/renderer/LevelRenderer.h @@ -1,130 +1,130 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__LevelRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__LevelRenderer_H__ - -//package net.minecraft.client.renderer; - -#include "../../world/level/LevelListener.h" -#include "../../world/phys/Vec3.h" -#include "RenderList.h" -#include "gles.h" -#include - -class Minecraft; -class Textures; -class Culler; -class Chunk; -class TileRenderer; -class Level; -class Mob; -class Player; -class HitResult; -class AABB; -class TripodCamera; - -class LevelRenderer: public LevelListener -{ -public: - static const int CHUNK_SIZE; - static const int MAX_VISIBLE_REBUILDS_PER_FRAME = 3; - static const int MAX_INVISIBLE_REBUILDS_PER_FRAME = 1; - - float xOld; - float yOld; - float zOld; - float destroyProgress; - - LevelRenderer(Minecraft* mc); - ~LevelRenderer(); - - void setLevel(Level* level); - void allChanged(); - - int render(Mob* player, int layer, float alpha); - void renderDebug(const AABB& b, float a) const; - - void renderSky(float alpha); - void renderClouds(float alpha); - void renderEntities(Vec3 cam, Culler* culler, float a); - void renderSameAsLast(int layer, float alpha); - void renderHit(Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a); - void renderHitOutline(Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a); - void renderHitSelect(Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a); - void entityAdded(Entity* entity); - - void tick(); - bool updateDirtyChunks(Mob* player, bool force); - void setDirty(int x0, int y0, int z0, int x1, int y1, int z1); - void tileChanged(int x, int y, int z); - void setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1); - void cull(Culler* culler, float a); - void skyColorChanged(); - - //void addParticle(ParticleType::Id name, float x, float y, float z, float xa, float ya, float za, int data); - void addParticle(const std::string& name, float x, float y, float z, float xa, float ya, float za, int data); - void playSound(const std::string& name, float x, float y, float z, float volume, float pitch); - void takePicture(TripodCamera* cam, Entity* entity); - - void levelEvent(Player* source, int type, int x, int y, int z, int data); - - std::string gatherStats1(); - - void render(const AABB& b) const; - void onGraphicsReset(); -private: - void generateSky(); - - int renderChunks(int from, int to, int layer, float alpha); - void resortChunks(int xc, int yc, int zc); - void deleteChunks(); - //void checkQueryResults(int from, int to); - __inline int getLinearCoord(int x, int y, int z) { - return (z * yChunks + y) * xChunks + x; - } - int noEntityRenderFrames; - int totalEntities; - int renderedEntities; - int culledEntities; - - std::vector _renderChunks; - - int cullStep; - //static const int renderListsLength = 4; - RenderList renderList;//[renderListsLength]; - - int totalChunks, offscreenChunks, occludedChunks, renderedChunks, emptyChunks; - int chunkFixOffs; - - int xMinChunk, yMinChunk, zMinChunk; - int xMaxChunk, yMaxChunk, zMaxChunk; - - Level* level; - std::vector dirtyChunks; - - Chunk** chunks; - Chunk** sortedChunks; - int chunksLength; -public: - TileRenderer* tileRenderer; -private: - int xChunks, yChunks, zChunks; - int chunkLists; - Minecraft* mc; - - bool occlusionCheck; - int lastViewDistance; - - int ticks; - int starList, skyList, darkList; - - int numListsOrBuffers; - GLuint* chunkBuffers; - GLuint skyBuffer; - int skyVertexCount; - -// /*public*/ std::vector renderableTileEntities; - Textures* textures; -// /*private*/ TileRenderer tileRenderer; -// /*private*/ IntBuffer occlusionCheckIds; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__LevelRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__LevelRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__LevelRenderer_H__ + +//package net.minecraft.client.renderer; + +#include "../../world/level/LevelListener.h" +#include "../../world/phys/Vec3.h" +#include "RenderList.h" +#include "gles.h" +#include + +class Minecraft; +class Textures; +class Culler; +class Chunk; +class TileRenderer; +class Level; +class Mob; +class Player; +class HitResult; +class AABB; +class TripodCamera; + +class LevelRenderer: public LevelListener +{ +public: + static const int CHUNK_SIZE; + static const int MAX_VISIBLE_REBUILDS_PER_FRAME = 3; + static const int MAX_INVISIBLE_REBUILDS_PER_FRAME = 1; + + float xOld; + float yOld; + float zOld; + float destroyProgress; + + LevelRenderer(Minecraft* mc); + ~LevelRenderer(); + + void setLevel(Level* level); + void allChanged(); + + int render(Mob* player, int layer, float alpha); + void renderDebug(const AABB& b, float a) const; + + void renderSky(float alpha); + void renderClouds(float alpha); + void renderEntities(Vec3 cam, Culler* culler, float a); + void renderSameAsLast(int layer, float alpha); + void renderHit(Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a); + void renderHitOutline(Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a); + void renderHitSelect(Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a); + void entityAdded(Entity* entity); + + void tick(); + bool updateDirtyChunks(Mob* player, bool force); + void setDirty(int x0, int y0, int z0, int x1, int y1, int z1); + void tileChanged(int x, int y, int z); + void setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1); + void cull(Culler* culler, float a); + void skyColorChanged(); + + //void addParticle(ParticleType::Id name, float x, float y, float z, float xa, float ya, float za, int data); + void addParticle(const std::string& name, float x, float y, float z, float xa, float ya, float za, int data); + void playSound(const std::string& name, float x, float y, float z, float volume, float pitch); + void takePicture(TripodCamera* cam, Entity* entity); + + void levelEvent(Player* source, int type, int x, int y, int z, int data); + + std::string gatherStats1(); + + void render(const AABB& b) const; + void onGraphicsReset(); +private: + void generateSky(); + + int renderChunks(int from, int to, int layer, float alpha); + void resortChunks(int xc, int yc, int zc); + void deleteChunks(); + //void checkQueryResults(int from, int to); + __inline int getLinearCoord(int x, int y, int z) { + return (z * yChunks + y) * xChunks + x; + } + int noEntityRenderFrames; + int totalEntities; + int renderedEntities; + int culledEntities; + + std::vector _renderChunks; + + int cullStep; + //static const int renderListsLength = 4; + RenderList renderList;//[renderListsLength]; + + int totalChunks, offscreenChunks, occludedChunks, renderedChunks, emptyChunks; + int chunkFixOffs; + + int xMinChunk, yMinChunk, zMinChunk; + int xMaxChunk, yMaxChunk, zMaxChunk; + + Level* level; + std::vector dirtyChunks; + + Chunk** chunks; + Chunk** sortedChunks; + int chunksLength; +public: + TileRenderer* tileRenderer; +private: + int xChunks, yChunks, zChunks; + int chunkLists; + Minecraft* mc; + + bool occlusionCheck; + int lastViewDistance; + + int ticks; + int starList, skyList, darkList; + + int numListsOrBuffers; + GLuint* chunkBuffers; + GLuint skyBuffer; + int skyVertexCount; + +// /*public*/ std::vector renderableTileEntities; + Textures* textures; +// /*private*/ TileRenderer tileRenderer; +// /*private*/ IntBuffer occlusionCheckIds; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__LevelRenderer_H__*/ diff --git a/src/client/renderer/RenderChunk.cpp b/src/client/renderer/RenderChunk.cpp index a676ea8..7e08a2b 100755 --- a/src/client/renderer/RenderChunk.cpp +++ b/src/client/renderer/RenderChunk.cpp @@ -1,17 +1,17 @@ -#include "RenderChunk.h" - -int RenderChunk::runningId = 0; - -RenderChunk::RenderChunk() : - vboId(-1), - vertexCount(0) -{ - id = ++runningId; -} - -RenderChunk::RenderChunk( GLuint vboId_, int vertexCount_ ) -: vboId(vboId_), - vertexCount(vertexCount_) -{ - id = ++runningId; -} +#include "RenderChunk.h" + +int RenderChunk::runningId = 0; + +RenderChunk::RenderChunk() : + vboId(-1), + vertexCount(0) +{ + id = ++runningId; +} + +RenderChunk::RenderChunk( GLuint vboId_, int vertexCount_ ) +: vboId(vboId_), + vertexCount(vertexCount_) +{ + id = ++runningId; +} diff --git a/src/client/renderer/RenderChunk.h b/src/client/renderer/RenderChunk.h index dc582e4..a9bbe9b 100755 --- a/src/client/renderer/RenderChunk.h +++ b/src/client/renderer/RenderChunk.h @@ -1,24 +1,24 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__RenderChunk_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__RenderChunk_H__ - -//package net.minecraft.client.renderer; - -#include "gles.h" -#include "../../world/phys/Vec3.h" - -class RenderChunk -{ -public: - RenderChunk(); - RenderChunk(GLuint vboId_, int vertexCount_); - - GLuint vboId; - GLsizei vertexCount; - int id; - Vec3 pos; - -private: - static int runningId; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__RenderChunk_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__RenderChunk_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__RenderChunk_H__ + +//package net.minecraft.client.renderer; + +#include "gles.h" +#include "../../world/phys/Vec3.h" + +class RenderChunk +{ +public: + RenderChunk(); + RenderChunk(GLuint vboId_, int vertexCount_); + + GLuint vboId; + GLsizei vertexCount; + int id; + Vec3 pos; + +private: + static int runningId; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__RenderChunk_H__*/ diff --git a/src/client/renderer/RenderChunkHandler.h b/src/client/renderer/RenderChunkHandler.h index f31f345..04638a7 100755 --- a/src/client/renderer/RenderChunkHandler.h +++ b/src/client/renderer/RenderChunkHandler.h @@ -1,23 +1,23 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__RenderChunkHandler_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__RenderChunkHandler_H__ - -#include -#include "RenderChunk.h" - -typedef std::vector ChunkList; - -class RenderChunkHandler -{ -public: - int vboCount; - ChunkList chunks; - - RenderChunkHandler() { - vboCount = Tesselator::getVboCount(); - } - - void render() { - } -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__RenderChunkHandler_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__RenderChunkHandler_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__RenderChunkHandler_H__ + +#include +#include "RenderChunk.h" + +typedef std::vector ChunkList; + +class RenderChunkHandler +{ +public: + int vboCount; + ChunkList chunks; + + RenderChunkHandler() { + vboCount = Tesselator::getVboCount(); + } + + void render() { + } +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__RenderChunkHandler_H__*/ diff --git a/src/client/renderer/RenderList.cpp b/src/client/renderer/RenderList.cpp index 1dfa95f..5a81617 100755 --- a/src/client/renderer/RenderList.cpp +++ b/src/client/renderer/RenderList.cpp @@ -1,96 +1,96 @@ -#include "RenderList.h" - -#include "gles.h" -#include "RenderChunk.h" -#include "Tesselator.h" - - -RenderList::RenderList() - : inited(false), - rendered(false) -{ - lists = new int[MAX_NUM_OBJECTS]; - rlists = new RenderChunk[MAX_NUM_OBJECTS]; - - for (int i = 0; i < MAX_NUM_OBJECTS; ++i) - rlists[i].vboId = -1; -} - -RenderList::~RenderList() { - delete[] lists; - delete[] rlists; -} - -void RenderList::init(float xOff, float yOff, float zOff) { - inited = true; - listIndex = 0; - - this->xOff = (float) xOff; - this->yOff = (float) yOff; - this->zOff = (float) zOff; -} - -void RenderList::add(int list) { - lists[listIndex] = list; - if (listIndex == MAX_NUM_OBJECTS) /*lists.remaining() == 0)*/ render(); -} - -void RenderList::addR(const RenderChunk& chunk) { - rlists[listIndex] = chunk; -} - -void RenderList::render() { - - if (!inited) return; - if (!rendered) { - bufferLimit = listIndex; - listIndex = 0; - rendered = true; - } - if (listIndex < bufferLimit) { - glPushMatrix2(); - glTranslatef2(-xOff, -yOff, -zOff); - - #ifndef USE_VBO - glCallLists(bufferLimit, GL_UNSIGNED_INT, lists); - #else - renderChunks(); - #endif/*!USE_VBO*/ - - glPopMatrix2(); - } -} - -void RenderList::renderChunks() { - //glDisableClientState2(GL_NORMAL_ARRAY); - glEnableClientState2(GL_VERTEX_ARRAY); - glEnableClientState2(GL_COLOR_ARRAY); - glEnableClientState2(GL_TEXTURE_COORD_ARRAY); - - const int Stride = VertexSizeBytes; - - for (int i = 0; i < bufferLimit; ++i) { - RenderChunk& rc = rlists[i]; - - glPushMatrix2(); - glTranslatef2(rc.pos.x, rc.pos.y, rc.pos.z); - glBindBuffer2(GL_ARRAY_BUFFER, rc.vboId); - - glVertexPointer2 (3, GL_FLOAT, Stride, 0); - glTexCoordPointer2 (2, GL_FLOAT, Stride, (GLvoid*) (3 * 4)); - glColorPointer2 (4, GL_UNSIGNED_BYTE, Stride, (GLvoid*) (5 * 4)); - - glDrawArrays2(GL_TRIANGLES, 0, rc.vertexCount); - - glPopMatrix2(); - } - - glDisableClientState2(GL_VERTEX_ARRAY); - glDisableClientState2(GL_COLOR_ARRAY); - glDisableClientState2(GL_TEXTURE_COORD_ARRAY); -} - -void RenderList::clear() { - inited = false; - rendered = false; -} +#include "RenderList.h" + +#include "gles.h" +#include "RenderChunk.h" +#include "Tesselator.h" + + +RenderList::RenderList() + : inited(false), + rendered(false) +{ + lists = new int[MAX_NUM_OBJECTS]; + rlists = new RenderChunk[MAX_NUM_OBJECTS]; + + for (int i = 0; i < MAX_NUM_OBJECTS; ++i) + rlists[i].vboId = -1; +} + +RenderList::~RenderList() { + delete[] lists; + delete[] rlists; +} + +void RenderList::init(float xOff, float yOff, float zOff) { + inited = true; + listIndex = 0; + + this->xOff = (float) xOff; + this->yOff = (float) yOff; + this->zOff = (float) zOff; +} + +void RenderList::add(int list) { + lists[listIndex] = list; + if (listIndex == MAX_NUM_OBJECTS) /*lists.remaining() == 0)*/ render(); +} + +void RenderList::addR(const RenderChunk& chunk) { + rlists[listIndex] = chunk; +} + +void RenderList::render() { + + if (!inited) return; + if (!rendered) { + bufferLimit = listIndex; + listIndex = 0; + rendered = true; + } + if (listIndex < bufferLimit) { + glPushMatrix2(); + glTranslatef2(-xOff, -yOff, -zOff); + + #ifndef USE_VBO + glCallLists(bufferLimit, GL_UNSIGNED_INT, lists); + #else + renderChunks(); + #endif/*!USE_VBO*/ + + glPopMatrix2(); + } +} + +void RenderList::renderChunks() { + //glDisableClientState2(GL_NORMAL_ARRAY); + glEnableClientState2(GL_VERTEX_ARRAY); + glEnableClientState2(GL_COLOR_ARRAY); + glEnableClientState2(GL_TEXTURE_COORD_ARRAY); + + const int Stride = VertexSizeBytes; + + for (int i = 0; i < bufferLimit; ++i) { + RenderChunk& rc = rlists[i]; + + glPushMatrix2(); + glTranslatef2(rc.pos.x, rc.pos.y, rc.pos.z); + glBindBuffer2(GL_ARRAY_BUFFER, rc.vboId); + + glVertexPointer2 (3, GL_FLOAT, Stride, 0); + glTexCoordPointer2 (2, GL_FLOAT, Stride, (GLvoid*) (3 * 4)); + glColorPointer2 (4, GL_UNSIGNED_BYTE, Stride, (GLvoid*) (5 * 4)); + + glDrawArrays2(GL_TRIANGLES, 0, rc.vertexCount); + + glPopMatrix2(); + } + + glDisableClientState2(GL_VERTEX_ARRAY); + glDisableClientState2(GL_COLOR_ARRAY); + glDisableClientState2(GL_TEXTURE_COORD_ARRAY); +} + +void RenderList::clear() { + inited = false; + rendered = false; +} diff --git a/src/client/renderer/RenderList.h b/src/client/renderer/RenderList.h index 9a9d005..d54ce7e 100755 --- a/src/client/renderer/RenderList.h +++ b/src/client/renderer/RenderList.h @@ -1,41 +1,41 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__RenderList_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__RenderList_H__ - -//package net.minecraft.client.renderer; - -class RenderChunk; - -class RenderList -{ - static const int MAX_NUM_OBJECTS = 1024 * 3; - -public: - RenderList(); - ~RenderList(); - - void init(float xOff, float yOff, float zOff); - - void add(int list); - void addR(const RenderChunk& chunk); - - __inline void next() { ++listIndex; } - - void render(); - void renderChunks(); - - void clear(); - - - float xOff, yOff, zOff; - int* lists; - RenderChunk* rlists; - - int listIndex; - bool inited; - bool rendered; - -private: - int bufferLimit; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__RenderList_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__RenderList_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__RenderList_H__ + +//package net.minecraft.client.renderer; + +class RenderChunk; + +class RenderList +{ + static const int MAX_NUM_OBJECTS = 1024 * 3; + +public: + RenderList(); + ~RenderList(); + + void init(float xOff, float yOff, float zOff); + + void add(int list); + void addR(const RenderChunk& chunk); + + __inline void next() { ++listIndex; } + + void render(); + void renderChunks(); + + void clear(); + + + float xOff, yOff, zOff; + int* lists; + RenderChunk* rlists; + + int listIndex; + bool inited; + bool rendered; + +private: + int bufferLimit; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__RenderList_H__*/ diff --git a/src/client/renderer/Tesselator.cpp b/src/client/renderer/Tesselator.cpp index dedaca3..e8be21f 100755 --- a/src/client/renderer/Tesselator.cpp +++ b/src/client/renderer/Tesselator.cpp @@ -1,437 +1,437 @@ -#include "Tesselator.h" -#include -#include -#include - -Tesselator Tesselator::instance(sizeof(GLfloat) * MAX_FLOATS); // max size in bytes - -const int VertexSizeBytes = sizeof(VERTEX); - -Tesselator::Tesselator( int size ) -: size(size), - vertices(0), - u(0), v(0), - _color(0), - hasColor(false), - hasTexture(false), - hasNormal(false), - p(0), - count(0), - _noColor(false), - mode(0), - xo(0), yo(0), zo(0), - _normal(0), - _sx(1), _sy(1), - - tesselating(false), - vboId(-1), - vboCounts(128), - totalSize(0), - accessMode(ACCESS_STATIC), - maxVertices(size / sizeof(VERTEX)), - _voidBeginEnd(false) -{ - vboIds = new GLuint[vboCounts]; - - _varray = new VERTEX[maxVertices]; - - char* a = (char*)&_varray[0]; - char* b = (char*)&_varray[1]; - LOGI("Vsize: %lu, %d\n", sizeof(VERTEX), (b-a)); -} - -Tesselator::~Tesselator() -{ - delete[] vboIds; - delete[] _varray; -} - -void Tesselator::init() -{ -#ifndef STANDALONE_SERVER - glGenBuffers2(vboCounts, vboIds); -#endif -} - -void Tesselator::clear() -{ - accessMode = ACCESS_STATIC; - vertices = 0; - count = 0; - p = 0; - _voidBeginEnd = false; -} - -int Tesselator::getVboCount() { - return vboCounts; -} - -RenderChunk Tesselator::end( bool useMine, int bufferId ) -{ -#ifndef STANDALONE_SERVER - //if (!tesselating) throw /*new*/ IllegalStateException("Not tesselating!"); - if (!tesselating) - LOGI("not tesselating!\n"); - - if (!tesselating || _voidBeginEnd) return RenderChunk(); - - tesselating = false; - const int o_vertices = vertices; - - if (vertices > 0) { - if (p <= 0 || p > maxVertices) { clear(); return RenderChunk(); } - int bytes = p * sizeof(VERTEX); - if (bytes <= 0) return RenderChunk(); - if (++vboId >= vboCounts) - vboId = 0; - -#ifdef USE_VBO - // Using VBO, use default buffer id only if we don't send in any - if (!useMine) { - bufferId = vboIds[vboId]; - } -#else - // Not using VBO - always use the next buffer object - bufferId = vboIds[vboId]; -#endif - int access = GL_STATIC_DRAW;//(accessMode==ACCESS_DYNAMIC) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW; - glBindBuffer2(GL_ARRAY_BUFFER, bufferId); - glBufferData2(GL_ARRAY_BUFFER, bytes, _varray, access); // GL_STREAM_DRAW - - totalSize += bytes; - -#ifndef USE_VBO - // 0 1 2 3 4 5 6 7 - // x y z u v c - if (hasTexture) { - glTexCoordPointer2(2, GL_FLOAT, VertexSizeBytes, (GLvoid*) (3 * 4)); - glEnableClientState2(GL_TEXTURE_COORD_ARRAY); - } - if (hasColor) { - glColorPointer2(4, GL_UNSIGNED_BYTE, VertexSizeBytes, (GLvoid*) (5 * 4)); - glEnableClientState2(GL_COLOR_ARRAY); - } - if (hasNormal) { - glNormalPointer(GL_BYTE, VertexSizeBytes, (GLvoid*) (6 * 4)); - glEnableClientState2(GL_NORMAL_ARRAY); - } - glVertexPointer2(3, GL_FLOAT, VertexSizeBytes, 0); - glEnableClientState2(GL_VERTEX_ARRAY); - - if (mode == GL_QUADS) { - glDrawArrays2(GL_TRIANGLES, 0, vertices); - } else { - glDrawArrays2(mode, 0, vertices); - } - //printf("drawing %d tris, size %d (%d,%d,%d)\n", vertices, p, hasTexture, hasColor, hasNormal); - glDisableClientState2(GL_VERTEX_ARRAY); - if (hasTexture) glDisableClientState2(GL_TEXTURE_COORD_ARRAY); - if (hasColor) glDisableClientState2(GL_COLOR_ARRAY); - if (hasNormal) glDisableClientState2(GL_NORMAL_ARRAY); -#endif /*!USE_VBO*/ - } - - clear(); - RenderChunk out(bufferId, o_vertices); - //map.insert( std::make_pair(bufferId, out.id) ); - return out; -#else - return RenderChunk(); -#endif - -} - -void Tesselator::begin( int mode ) -{ - if (tesselating || _voidBeginEnd) { - if (tesselating && !_voidBeginEnd) - LOGI("already tesselating!\n"); - return; - } - //if (tesselating) { - // throw /*new*/ IllegalStateException("Already tesselating!"); - //} - tesselating = true; - - clear(); - this->mode = mode; - hasNormal = false; - hasColor = false; - hasTexture = false; - _noColor = false; -} - -void Tesselator::begin() -{ - begin(GL_QUADS); -} - -void Tesselator::tex( float u, float v ) -{ - hasTexture = true; - this->u = u; - this->v = v; -} - -int Tesselator::getColor() { - return _color; -} - -void Tesselator::color( float r, float g, float b ) -{ - color((int) (r * 255), (int) (g * 255), (int) (b * 255)); -} - -void Tesselator::color( float r, float g, float b, float a ) -{ - color((int) (r * 255), (int) (g * 255), (int) (b * 255), (int) (a * 255)); -} - -void Tesselator::color( int r, int g, int b ) -{ - color(r, g, b, 255); -} - -void Tesselator::color( int r, int g, int b, int a ) -{ - if (_noColor) return; - - if (r > 255) r = 255; - if (g > 255) g = 255; - if (b > 255) b = 255; - if (a > 255) a = 255; - if (r < 0) r = 0; - if (g < 0) g = 0; - if (b < 0) b = 0; - if (a < 0) a = 0; - - hasColor = true; - //if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) { - if (true) { - _color = (a << 24) | (b << 16) | (g << 8) | (r); - } else { - _color = (r << 24) | (g << 16) | (b << 8) | (a); - } -} - -void Tesselator::color( char r, char g, char b ) -{ - color(r & 0xff, g & 0xff, b & 0xff); -} - -void Tesselator::color( int c ) -{ - int r = ((c >> 16) & 255); - int g = ((c >> 8) & 255); - int b = ((c) & 255); - color(r, g, b); -} - -//@note: doesn't care about endianess -void Tesselator::colorABGR( int c ) -{ - if (_noColor) return; - hasColor = true; - _color = c; -} - -void Tesselator::color( int c, int alpha ) -{ - int r = ((c >> 16) & 255); - int g = ((c >> 8) & 255); - int b = ((c) & 255); - color(r, g, b, alpha); -} - -void Tesselator::vertexUV( float x, float y, float z, float u, float v ) -{ - tex(u, v); - vertex(x, y, z); -} - -void Tesselator::scale2d(float sx, float sy) { - _sx *= sx; - _sy *= sy; -} - -void Tesselator::resetScale() { - _sx = _sy = 1; -} - -void Tesselator::vertex( float x, float y, float z ) -{ -#ifndef STANDALONE_SERVER - count++; - - if (mode == GL_QUADS && (count & 3) == 0) { - for (int i = 0; i < 2; i++) { - - const int offs = 3 - i; - if (p - offs < 0 || p >= maxVertices) { clear(); return; } - VERTEX& src = _varray[p - offs]; - VERTEX& dst = _varray[p]; - - if (hasTexture) { - dst.u = src.u; - dst.v = src.v; - } - if (hasColor) { - dst.color = src.color; - } - //if (hasNormal) { - // dst.normal = src.normal; - //} - - dst.x = src.x; - dst.y = src.y; - dst.z = src.z; - - ++vertices; - ++p; - } - } - - if (p < 0 || p >= maxVertices) { clear(); return; } - VERTEX& vertex = _varray[p]; - - if (hasTexture) { - vertex.u = u; - vertex.v = v; - } - if (hasColor) { - vertex.color = _color; - } - //if (hasNormal) { - // vertex.normal = _normal; - //} - - vertex.x = _sx * (x + xo); - vertex.y = _sy * (y + yo); - vertex.z = z + zo; - - ++p; - ++vertices; - - if ((vertices & 3) == 0 && p >= maxVertices-1) { - for (int i = 0; i < 3; ++i) - printf("Overwriting the vertex buffer! This chunk/entity won't show up\n"); - clear(); - } -#endif -} - -void Tesselator::noColor() -{ - _noColor = true; -} - -void Tesselator::setAccessMode(int mode) -{ - accessMode = mode; -} - -void Tesselator::normal( float x, float y, float z ) -{ - static int _warn_t = 0; - if ((++_warn_t & 32767) == 1) - LOGI("WARNING: Can't use normals (Tesselator::normal)\n"); - return; - - if (!tesselating) printf("But.."); - hasNormal = true; - char xx = (char) (x * 128); - char yy = (char) (y * 127); - char zz = (char) (z * 127); - - _normal = xx | (yy << 8) | (zz << 16); -} - -void Tesselator::offset( float xo, float yo, float zo ) { - this->xo = xo; - this->yo = yo; - this->zo = zo; -} - -void Tesselator::addOffset( float x, float y, float z ) { - xo += x; - yo += y; - zo += z; -} - -void Tesselator::offset( const Vec3& v ) { - xo = v.x; - yo = v.y; - zo = v.z; -} - -void Tesselator::addOffset( const Vec3& v ) { - xo += v.x; - yo += v.y; - zo += v.z; -} - -void Tesselator::draw() -{ -#ifndef STANDALONE_SERVER - if (!tesselating) - LOGI("not (draw) tesselating!\n"); - - if (!tesselating || _voidBeginEnd) - return; - - tesselating = false; - - if (vertices > 0) { - if (p <= 0 || p > maxVertices) { clear(); return; } - int bytes = p * sizeof(VERTEX); - if (bytes <= 0) { clear(); return; } - if (++vboId >= vboCounts) - vboId = 0; - - int bufferId = vboIds[vboId]; - - int access = GL_DYNAMIC_DRAW;//(accessMode==ACCESS_DYNAMIC) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW; - glBindBuffer2(GL_ARRAY_BUFFER, bufferId); - glBufferData2(GL_ARRAY_BUFFER, bytes, _varray, access); // GL_STREAM_DRAW - - if (hasTexture) { - glTexCoordPointer2(2, GL_FLOAT, VertexSizeBytes, (GLvoid*) (3 * 4)); - //glTexCoordPointer2(2, GL_FLOAT, VertexSizeBytes, (GLvoid*) &_varray->u); - glEnableClientState2(GL_TEXTURE_COORD_ARRAY); - } - if (hasColor) { - glColorPointer2(4, GL_UNSIGNED_BYTE, VertexSizeBytes, (GLvoid*) (5 * 4)); - //glColorPointer2(4, GL_UNSIGNED_BYTE, VertexSizeBytes, (GLvoid*) &_varray->color); - glEnableClientState2(GL_COLOR_ARRAY); - } - //if (hasNormal) { - // glNormalPointer(GL_BYTE, VertexSizeBytes, (GLvoid*) (6 * 4)); - // glEnableClientState2(GL_NORMAL_ARRAY); - //} - //glVertexPointer2(3, GL_FLOAT, VertexSizeBytes, (GLvoid*)&_varray); - glVertexPointer2(3, GL_FLOAT, VertexSizeBytes, 0); - glEnableClientState2(GL_VERTEX_ARRAY); - - if (mode == GL_QUADS) { - glDrawArrays2(GL_TRIANGLES, 0, vertices); - } else { - glDrawArrays2(mode, 0, vertices); - } - - glDisableClientState2(GL_VERTEX_ARRAY); - if (hasTexture) glDisableClientState2(GL_TEXTURE_COORD_ARRAY); - if (hasColor) glDisableClientState2(GL_COLOR_ARRAY); - //if (hasNormal) glDisableClientState2(GL_NORMAL_ARRAY); - } - - clear(); -#endif -} - -void Tesselator::voidBeginAndEndCalls(bool doVoid) { - _voidBeginEnd = doVoid; -} - -void Tesselator::enableColor() { - _noColor = false; -} +#include "Tesselator.h" +#include +#include +#include + +Tesselator Tesselator::instance(sizeof(GLfloat) * MAX_FLOATS); // max size in bytes + +const int VertexSizeBytes = sizeof(VERTEX); + +Tesselator::Tesselator( int size ) +: size(size), + vertices(0), + u(0), v(0), + _color(0), + hasColor(false), + hasTexture(false), + hasNormal(false), + p(0), + count(0), + _noColor(false), + mode(0), + xo(0), yo(0), zo(0), + _normal(0), + _sx(1), _sy(1), + + tesselating(false), + vboId(-1), + vboCounts(128), + totalSize(0), + accessMode(ACCESS_STATIC), + maxVertices(size / sizeof(VERTEX)), + _voidBeginEnd(false) +{ + vboIds = new GLuint[vboCounts]; + + _varray = new VERTEX[maxVertices]; + + char* a = (char*)&_varray[0]; + char* b = (char*)&_varray[1]; + LOGI("Vsize: %lu, %d\n", sizeof(VERTEX), (b-a)); +} + +Tesselator::~Tesselator() +{ + delete[] vboIds; + delete[] _varray; +} + +void Tesselator::init() +{ +#ifndef STANDALONE_SERVER + glGenBuffers2(vboCounts, vboIds); +#endif +} + +void Tesselator::clear() +{ + accessMode = ACCESS_STATIC; + vertices = 0; + count = 0; + p = 0; + _voidBeginEnd = false; +} + +int Tesselator::getVboCount() { + return vboCounts; +} + +RenderChunk Tesselator::end( bool useMine, int bufferId ) +{ +#ifndef STANDALONE_SERVER + //if (!tesselating) throw /*new*/ IllegalStateException("Not tesselating!"); + if (!tesselating) + LOGI("not tesselating!\n"); + + if (!tesselating || _voidBeginEnd) return RenderChunk(); + + tesselating = false; + const int o_vertices = vertices; + + if (vertices > 0) { + if (p <= 0 || p > maxVertices) { clear(); return RenderChunk(); } + int bytes = p * sizeof(VERTEX); + if (bytes <= 0) return RenderChunk(); + if (++vboId >= vboCounts) + vboId = 0; + +#ifdef USE_VBO + // Using VBO, use default buffer id only if we don't send in any + if (!useMine) { + bufferId = vboIds[vboId]; + } +#else + // Not using VBO - always use the next buffer object + bufferId = vboIds[vboId]; +#endif + int access = GL_STATIC_DRAW;//(accessMode==ACCESS_DYNAMIC) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW; + glBindBuffer2(GL_ARRAY_BUFFER, bufferId); + glBufferData2(GL_ARRAY_BUFFER, bytes, _varray, access); // GL_STREAM_DRAW + + totalSize += bytes; + +#ifndef USE_VBO + // 0 1 2 3 4 5 6 7 + // x y z u v c + if (hasTexture) { + glTexCoordPointer2(2, GL_FLOAT, VertexSizeBytes, (GLvoid*) (3 * 4)); + glEnableClientState2(GL_TEXTURE_COORD_ARRAY); + } + if (hasColor) { + glColorPointer2(4, GL_UNSIGNED_BYTE, VertexSizeBytes, (GLvoid*) (5 * 4)); + glEnableClientState2(GL_COLOR_ARRAY); + } + if (hasNormal) { + glNormalPointer(GL_BYTE, VertexSizeBytes, (GLvoid*) (6 * 4)); + glEnableClientState2(GL_NORMAL_ARRAY); + } + glVertexPointer2(3, GL_FLOAT, VertexSizeBytes, 0); + glEnableClientState2(GL_VERTEX_ARRAY); + + if (mode == GL_QUADS) { + glDrawArrays2(GL_TRIANGLES, 0, vertices); + } else { + glDrawArrays2(mode, 0, vertices); + } + //printf("drawing %d tris, size %d (%d,%d,%d)\n", vertices, p, hasTexture, hasColor, hasNormal); + glDisableClientState2(GL_VERTEX_ARRAY); + if (hasTexture) glDisableClientState2(GL_TEXTURE_COORD_ARRAY); + if (hasColor) glDisableClientState2(GL_COLOR_ARRAY); + if (hasNormal) glDisableClientState2(GL_NORMAL_ARRAY); +#endif /*!USE_VBO*/ + } + + clear(); + RenderChunk out(bufferId, o_vertices); + //map.insert( std::make_pair(bufferId, out.id) ); + return out; +#else + return RenderChunk(); +#endif + +} + +void Tesselator::begin( int mode ) +{ + if (tesselating || _voidBeginEnd) { + if (tesselating && !_voidBeginEnd) + LOGI("already tesselating!\n"); + return; + } + //if (tesselating) { + // throw /*new*/ IllegalStateException("Already tesselating!"); + //} + tesselating = true; + + clear(); + this->mode = mode; + hasNormal = false; + hasColor = false; + hasTexture = false; + _noColor = false; +} + +void Tesselator::begin() +{ + begin(GL_QUADS); +} + +void Tesselator::tex( float u, float v ) +{ + hasTexture = true; + this->u = u; + this->v = v; +} + +int Tesselator::getColor() { + return _color; +} + +void Tesselator::color( float r, float g, float b ) +{ + color((int) (r * 255), (int) (g * 255), (int) (b * 255)); +} + +void Tesselator::color( float r, float g, float b, float a ) +{ + color((int) (r * 255), (int) (g * 255), (int) (b * 255), (int) (a * 255)); +} + +void Tesselator::color( int r, int g, int b ) +{ + color(r, g, b, 255); +} + +void Tesselator::color( int r, int g, int b, int a ) +{ + if (_noColor) return; + + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + if (a > 255) a = 255; + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (a < 0) a = 0; + + hasColor = true; + //if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) { + if (true) { + _color = (a << 24) | (b << 16) | (g << 8) | (r); + } else { + _color = (r << 24) | (g << 16) | (b << 8) | (a); + } +} + +void Tesselator::color( char r, char g, char b ) +{ + color(r & 0xff, g & 0xff, b & 0xff); +} + +void Tesselator::color( int c ) +{ + int r = ((c >> 16) & 255); + int g = ((c >> 8) & 255); + int b = ((c) & 255); + color(r, g, b); +} + +//@note: doesn't care about endianess +void Tesselator::colorABGR( int c ) +{ + if (_noColor) return; + hasColor = true; + _color = c; +} + +void Tesselator::color( int c, int alpha ) +{ + int r = ((c >> 16) & 255); + int g = ((c >> 8) & 255); + int b = ((c) & 255); + color(r, g, b, alpha); +} + +void Tesselator::vertexUV( float x, float y, float z, float u, float v ) +{ + tex(u, v); + vertex(x, y, z); +} + +void Tesselator::scale2d(float sx, float sy) { + _sx *= sx; + _sy *= sy; +} + +void Tesselator::resetScale() { + _sx = _sy = 1; +} + +void Tesselator::vertex( float x, float y, float z ) +{ +#ifndef STANDALONE_SERVER + count++; + + if (mode == GL_QUADS && (count & 3) == 0) { + for (int i = 0; i < 2; i++) { + + const int offs = 3 - i; + if (p - offs < 0 || p >= maxVertices) { clear(); return; } + VERTEX& src = _varray[p - offs]; + VERTEX& dst = _varray[p]; + + if (hasTexture) { + dst.u = src.u; + dst.v = src.v; + } + if (hasColor) { + dst.color = src.color; + } + //if (hasNormal) { + // dst.normal = src.normal; + //} + + dst.x = src.x; + dst.y = src.y; + dst.z = src.z; + + ++vertices; + ++p; + } + } + + if (p < 0 || p >= maxVertices) { clear(); return; } + VERTEX& vertex = _varray[p]; + + if (hasTexture) { + vertex.u = u; + vertex.v = v; + } + if (hasColor) { + vertex.color = _color; + } + //if (hasNormal) { + // vertex.normal = _normal; + //} + + vertex.x = _sx * (x + xo); + vertex.y = _sy * (y + yo); + vertex.z = z + zo; + + ++p; + ++vertices; + + if ((vertices & 3) == 0 && p >= maxVertices-1) { + for (int i = 0; i < 3; ++i) + printf("Overwriting the vertex buffer! This chunk/entity won't show up\n"); + clear(); + } +#endif +} + +void Tesselator::noColor() +{ + _noColor = true; +} + +void Tesselator::setAccessMode(int mode) +{ + accessMode = mode; +} + +void Tesselator::normal( float x, float y, float z ) +{ + static int _warn_t = 0; + if ((++_warn_t & 32767) == 1) + LOGI("WARNING: Can't use normals (Tesselator::normal)\n"); + return; + + if (!tesselating) printf("But.."); + hasNormal = true; + char xx = (char) (x * 128); + char yy = (char) (y * 127); + char zz = (char) (z * 127); + + _normal = xx | (yy << 8) | (zz << 16); +} + +void Tesselator::offset( float xo, float yo, float zo ) { + this->xo = xo; + this->yo = yo; + this->zo = zo; +} + +void Tesselator::addOffset( float x, float y, float z ) { + xo += x; + yo += y; + zo += z; +} + +void Tesselator::offset( const Vec3& v ) { + xo = v.x; + yo = v.y; + zo = v.z; +} + +void Tesselator::addOffset( const Vec3& v ) { + xo += v.x; + yo += v.y; + zo += v.z; +} + +void Tesselator::draw() +{ +#ifndef STANDALONE_SERVER + if (!tesselating) + LOGI("not (draw) tesselating!\n"); + + if (!tesselating || _voidBeginEnd) + return; + + tesselating = false; + + if (vertices > 0) { + if (p <= 0 || p > maxVertices) { clear(); return; } + int bytes = p * sizeof(VERTEX); + if (bytes <= 0) { clear(); return; } + if (++vboId >= vboCounts) + vboId = 0; + + int bufferId = vboIds[vboId]; + + int access = GL_DYNAMIC_DRAW;//(accessMode==ACCESS_DYNAMIC) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW; + glBindBuffer2(GL_ARRAY_BUFFER, bufferId); + glBufferData2(GL_ARRAY_BUFFER, bytes, _varray, access); // GL_STREAM_DRAW + + if (hasTexture) { + glTexCoordPointer2(2, GL_FLOAT, VertexSizeBytes, (GLvoid*) (3 * 4)); + //glTexCoordPointer2(2, GL_FLOAT, VertexSizeBytes, (GLvoid*) &_varray->u); + glEnableClientState2(GL_TEXTURE_COORD_ARRAY); + } + if (hasColor) { + glColorPointer2(4, GL_UNSIGNED_BYTE, VertexSizeBytes, (GLvoid*) (5 * 4)); + //glColorPointer2(4, GL_UNSIGNED_BYTE, VertexSizeBytes, (GLvoid*) &_varray->color); + glEnableClientState2(GL_COLOR_ARRAY); + } + //if (hasNormal) { + // glNormalPointer(GL_BYTE, VertexSizeBytes, (GLvoid*) (6 * 4)); + // glEnableClientState2(GL_NORMAL_ARRAY); + //} + //glVertexPointer2(3, GL_FLOAT, VertexSizeBytes, (GLvoid*)&_varray); + glVertexPointer2(3, GL_FLOAT, VertexSizeBytes, 0); + glEnableClientState2(GL_VERTEX_ARRAY); + + if (mode == GL_QUADS) { + glDrawArrays2(GL_TRIANGLES, 0, vertices); + } else { + glDrawArrays2(mode, 0, vertices); + } + + glDisableClientState2(GL_VERTEX_ARRAY); + if (hasTexture) glDisableClientState2(GL_TEXTURE_COORD_ARRAY); + if (hasColor) glDisableClientState2(GL_COLOR_ARRAY); + //if (hasNormal) glDisableClientState2(GL_NORMAL_ARRAY); + } + + clear(); +#endif +} + +void Tesselator::voidBeginAndEndCalls(bool doVoid) { + _voidBeginEnd = doVoid; +} + +void Tesselator::enableColor() { + _noColor = false; +} diff --git a/src/client/renderer/Tesselator.h b/src/client/renderer/Tesselator.h index 85bad5f..eac1f77 100755 --- a/src/client/renderer/Tesselator.h +++ b/src/client/renderer/Tesselator.h @@ -1,130 +1,130 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__Tesselator_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__Tesselator_H__ - -//package net.minecraft.client.renderer; - -#include -#include "RenderChunk.h" -#include "gles.h" -#include "VertecDecl.h" - -extern const int VertexSizeBytes; - -typedef VertexDeclPTC VERTEX; -typedef std::map IntGLMap; - - -class Tesselator -{ - static const int MAX_MEMORY_USE = 16 * 1024 * 1024; - static const int MAX_FLOATS = MAX_MEMORY_USE / 4 / 2; - - Tesselator(int size); - -public: - static const int ACCESS_DYNAMIC = 1; - static const int ACCESS_STATIC = 2; - - static Tesselator instance; - - ~Tesselator(); - - void init(); - void clear(); - - void begin(); - void begin(int mode); - void draw(); - RenderChunk end(bool useMine, int bufferId); - - void color(int c); - void color(int c, int alpha); - void color(float r, float g, float b); - void color(float r, float g, float b, float a); - void color(int r, int g, int b); - void color(int r, int g, int b, int a); - void color(char r, char g, char b); - void colorABGR( int c ); - - void normal(float x, float y, float z); - void voidBeginAndEndCalls(bool doVoid); - - void tex(float u, float v); - - void vertex(float x, float y, float z); - void vertexUV(float x, float y, float z, float u, float v); - - void scale2d(float x, float y); - void resetScale(); - - void noColor(); - void enableColor(); -private: - void setAccessMode(int mode); -public: - - void offset(float xo, float yo, float zo); - void offset(const Vec3& v); - void addOffset(float x, float y, float z); - void addOffset(const Vec3& v); - - int getVboCount(); - - int getColor(); - - __inline void beginOverride() { - begin(); - voidBeginAndEndCalls(true); - } - __inline void endOverrideAndDraw() { - voidBeginAndEndCalls(false); - draw(); - } - __inline bool isOverridden() { - return _voidBeginEnd; - } - __inline RenderChunk endOverride(int bufferId) { - voidBeginAndEndCalls(false); - return end(true, bufferId); - } - -private: - Tesselator(const Tesselator& rhs) {} - Tesselator& operator=(const Tesselator& rhs) { return *this; } - VERTEX* _varray; - - int vertices; - - float xo, yo, zo; - float u, v; - unsigned int _color; - int _normal; - float _sx, _sy; - - bool hasColor; - bool hasTexture; - bool hasNormal; - bool _noColor; - bool _voidBeginEnd; - - int p; - int count; - - bool tesselating; - - bool vboMode; - int vboCounts; - int vboId; - GLuint* vboIds; - - int size; - int totalSize; - int maxVertices; - - int mode; - int accessMode; - - IntGLMap map; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__Tesselator_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__Tesselator_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__Tesselator_H__ + +//package net.minecraft.client.renderer; + +#include +#include "RenderChunk.h" +#include "gles.h" +#include "VertecDecl.h" + +extern const int VertexSizeBytes; + +typedef VertexDeclPTC VERTEX; +typedef std::map IntGLMap; + + +class Tesselator +{ + static const int MAX_MEMORY_USE = 16 * 1024 * 1024; + static const int MAX_FLOATS = MAX_MEMORY_USE / 4 / 2; + + Tesselator(int size); + +public: + static const int ACCESS_DYNAMIC = 1; + static const int ACCESS_STATIC = 2; + + static Tesselator instance; + + ~Tesselator(); + + void init(); + void clear(); + + void begin(); + void begin(int mode); + void draw(); + RenderChunk end(bool useMine, int bufferId); + + void color(int c); + void color(int c, int alpha); + void color(float r, float g, float b); + void color(float r, float g, float b, float a); + void color(int r, int g, int b); + void color(int r, int g, int b, int a); + void color(char r, char g, char b); + void colorABGR( int c ); + + void normal(float x, float y, float z); + void voidBeginAndEndCalls(bool doVoid); + + void tex(float u, float v); + + void vertex(float x, float y, float z); + void vertexUV(float x, float y, float z, float u, float v); + + void scale2d(float x, float y); + void resetScale(); + + void noColor(); + void enableColor(); +private: + void setAccessMode(int mode); +public: + + void offset(float xo, float yo, float zo); + void offset(const Vec3& v); + void addOffset(float x, float y, float z); + void addOffset(const Vec3& v); + + int getVboCount(); + + int getColor(); + + __inline void beginOverride() { + begin(); + voidBeginAndEndCalls(true); + } + __inline void endOverrideAndDraw() { + voidBeginAndEndCalls(false); + draw(); + } + __inline bool isOverridden() { + return _voidBeginEnd; + } + __inline RenderChunk endOverride(int bufferId) { + voidBeginAndEndCalls(false); + return end(true, bufferId); + } + +private: + Tesselator(const Tesselator& rhs) {} + Tesselator& operator=(const Tesselator& rhs) { return *this; } + VERTEX* _varray; + + int vertices; + + float xo, yo, zo; + float u, v; + unsigned int _color; + int _normal; + float _sx, _sy; + + bool hasColor; + bool hasTexture; + bool hasNormal; + bool _noColor; + bool _voidBeginEnd; + + int p; + int count; + + bool tesselating; + + bool vboMode; + int vboCounts; + int vboId; + GLuint* vboIds; + + int size; + int totalSize; + int maxVertices; + + int mode; + int accessMode; + + IntGLMap map; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__Tesselator_H__*/ diff --git a/src/client/renderer/TextureData.h b/src/client/renderer/TextureData.h index 2c97797..1801447 100755 --- a/src/client/renderer/TextureData.h +++ b/src/client/renderer/TextureData.h @@ -1,37 +1,37 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__TextureData_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__TextureData_H__ - -enum TextureFormat -{ - TEXF_UNCOMPRESSED_8888, - TEXF_UNCOMPRESSED_565, - TEXF_UNCOMPRESSED_5551, - TEXF_UNCOMPRESSED_4444, - TEXF_COMPRESSED_PVRTC_565, - TEXF_COMPRESSED_PVRTC_5551, - TEXF_COMPRESSED_PVRTC_4444 -}; - -typedef struct TextureData { - TextureData() - : w(0), - h(0), - data(nullptr), - numBytes(0), - transparent(true), - memoryHandledExternally(false), - format(TEXF_UNCOMPRESSED_8888), - identifier(-1) - {} - - int w, h; - unsigned char* data; - int numBytes; - bool transparent; - bool memoryHandledExternally; - - TextureFormat format; - int identifier; -} TextureData; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__Textures_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__TextureData_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__TextureData_H__ + +enum TextureFormat +{ + TEXF_UNCOMPRESSED_8888, + TEXF_UNCOMPRESSED_565, + TEXF_UNCOMPRESSED_5551, + TEXF_UNCOMPRESSED_4444, + TEXF_COMPRESSED_PVRTC_565, + TEXF_COMPRESSED_PVRTC_5551, + TEXF_COMPRESSED_PVRTC_4444 +}; + +typedef struct TextureData { + TextureData() + : w(0), + h(0), + data(nullptr), + numBytes(0), + transparent(true), + memoryHandledExternally(false), + format(TEXF_UNCOMPRESSED_8888), + identifier(-1) + {} + + int w, h; + unsigned char* data; + int numBytes; + bool transparent; + bool memoryHandledExternally; + + TextureFormat format; + int identifier; +} TextureData; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__Textures_H__*/ diff --git a/src/client/renderer/Textures.cpp b/src/client/renderer/Textures.cpp index 9fce411..9192fcd 100755 --- a/src/client/renderer/Textures.cpp +++ b/src/client/renderer/Textures.cpp @@ -1,361 +1,361 @@ -#include "Textures.h" - -#include "TextureData.h" -#include "ptexture/DynamicTexture.h" -#include "../Options.h" -#include "../../platform/time.h" -#include "../../AppPlatform.h" -#include "../../util/StringUtils.h" - -/*static*/ int Textures::textureChanges = 0; -/*static*/ bool Textures::MIPMAP = false; -/*static*/ const TextureId Textures::InvalidId = -1; - -Textures::Textures( Options* options_, AppPlatform* platform_ ) -: clamp(false), - blur(false), - options(options_), - platform(platform_), - lastBoundTexture(Textures::InvalidId) -{ -} - -Textures::~Textures() -{ - clear(); - - for (unsigned int i = 0; i < dynamicTextures.size(); ++i) - delete dynamicTextures[i]; -} - -void Textures::clear() -{ - for (TextureMap::iterator it = idMap.begin(); it != idMap.end(); ++it) { - if (it->second != Textures::InvalidId) - glDeleteTextures(1, &it->second); - } - for (TextureImageMap::iterator it = loadedImages.begin(); it != loadedImages.end(); ++it) { - if (!(it->second).memoryHandledExternally) - delete[] (it->second).data; - } - idMap.clear(); - loadedImages.clear(); - - lastBoundTexture = Textures::InvalidId; -} - -TextureId Textures::loadAndBindTexture( const std::string& resourceName ) -{ - //static Stopwatch t; - - //t.start(); - TextureId id = loadTexture(resourceName); - //t.stop(); - if (id != Textures::InvalidId) - bind(id); - - //t.printEvery(1000); - - return id; -} - -TextureId Textures::loadTexture( const std::string& resourceName, bool inTextureFolder /* = true */ ) -{ - TextureMap::iterator it = idMap.find(resourceName); - if (it != idMap.end()) - return it->second; - - bool isUrl = Util::startsWith(resourceName, "http://") || Util::startsWith(resourceName, "https://"); - TextureData texdata = platform->loadTexture(resourceName, isUrl ? false : inTextureFolder); - if (texdata.data) - return assignTexture(resourceName, texdata); - else if (texdata.identifier != InvalidId) { - //LOGI("Adding id: %d for %s\n", texdata.identifier, resourceName.c_str()); - idMap.insert(std::make_pair(resourceName, texdata.identifier)); - } - else { - idMap.insert(std::make_pair(resourceName, Textures::InvalidId)); - //loadedImages.insert(std::make_pair(InvalidId, texdata)); - } - return Textures::InvalidId; -} - -TextureId Textures::assignTexture( const std::string& resourceName, const TextureData& img ) -{ - TextureId id; - glGenTextures(1, &id); - - bind(id); - - if (MIPMAP) { - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } else { - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - if (blur) { - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - - if (clamp) { - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } else { - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - - switch (img.format) - { - case TEXF_COMPRESSED_PVRTC_4444: - case TEXF_COMPRESSED_PVRTC_565: - case TEXF_COMPRESSED_PVRTC_5551: - { -#if defined(__APPLE__) - int fmt = img.transparent? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - glCompressedTexImage2D(GL_TEXTURE_2D, 0, fmt, img.w, img.h, 0, img.numBytes, img.data); -#endif - break; - } - - default: - const GLint mode = img.transparent? GL_RGBA : GL_RGB; - - if (img.format == TEXF_UNCOMPRESSED_565) { - glTexImage2D2(GL_TEXTURE_2D, 0, mode, img.w, img.h, 0, mode, GL_UNSIGNED_SHORT_5_6_5, img.data); - } - else if (img.format == TEXF_UNCOMPRESSED_4444) { - glTexImage2D2(GL_TEXTURE_2D, 0, mode, img.w, img.h, 0, mode, GL_UNSIGNED_SHORT_4_4_4_4, img.data); - } - else if (img.format == TEXF_UNCOMPRESSED_5551) { - glTexImage2D2(GL_TEXTURE_2D, 0, mode, img.w, img.h, 0, mode, GL_UNSIGNED_SHORT_5_5_5_1, img.data); - } - else { - glTexImage2D2(GL_TEXTURE_2D, 0, mode, img.w, img.h, 0, mode, GL_UNSIGNED_BYTE, img.data); - } - break; - } - - //LOGI("Adding id: %d to map\n", id); - idMap.insert(std::make_pair(resourceName, id)); - loadedImages.insert(std::make_pair(id, img)); - - return id; -} - -const TextureData* Textures::getTemporaryTextureData( TextureId id ) -{ - TextureImageMap::iterator it = loadedImages.find(id); - if (it == loadedImages.end()) - return NULL; - - return &it->second; -} - -void Textures::tick(bool uploadToGraphicsCard) -{ - for (unsigned int i = 0; i < dynamicTextures.size(); ++i ) { - DynamicTexture* tex = dynamicTextures[i]; - tex->tick(); - - if (uploadToGraphicsCard) { - tex->bindTexture(this); - for (int xx = 0; xx < tex->replicate; xx++) - for (int yy = 0; yy < tex->replicate; yy++) { - glTexSubImage2D2(GL_TEXTURE_2D, 0, tex->tex % 16 * 16 + xx * 16, - tex->tex / 16 * 16 + yy * 16, 16, 16, - GL_RGBA, GL_UNSIGNED_BYTE, tex->pixels); - } - } - } -} - -void Textures::addDynamicTexture( DynamicTexture* dynamicTexture ) -{ - dynamicTextures.push_back(dynamicTexture); - dynamicTexture->tick(); -} - -void Textures::reloadAll() -{ - //TexturePack skin = skins.selected; - - //for (int id : loadedImages.keySet()) { - // BufferedImage image = loadedImages.get(id); - // loadTexture(image, id); - //} - - ////for (HttpTexture httpTexture : httpTextures.values()) { - //// httpTexture.isLoaded = false; - ////} - - //for (std::string name : idMap.keySet()) { - // try { - // BufferedImage image; - // if (name.startsWith("##")) { - // image = makeStrip(readImage(skin.getResource(name.substring(2)))); - // } else if (name.startsWith("%clamp%")) { - // clamp = true; - // image = readImage(skin.getResource(name.substring(7))); - // } else if (name.startsWith("%blur%")) { - // blur = true; - // image = readImage(skin.getResource(name.substring(6))); - // } else { - // image = readImage(skin.getResource(name)); - // } - // int id = idMap.get(name); - // loadTexture(image, id); - // blur = false; - // clamp = false; - // } catch (IOException e) { - // e.printStackTrace(); - // } - //} -} - -int Textures::smoothBlend( int c0, int c1 ) -{ - int a0 = (int) (((c0 & 0xff000000) >> 24)) & 0xff; - int a1 = (int) (((c1 & 0xff000000) >> 24)) & 0xff; - return ((a0 + a1) >> 1 << 24) + (((c0 & 0x00fefefe) + (c1 & 0x00fefefe)) >> 1); -} - -int Textures::crispBlend( int c0, int c1 ) -{ - int a0 = (int) (((c0 & 0xff000000) >> 24)) & 0xff; - int a1 = (int) (((c1 & 0xff000000) >> 24)) & 0xff; - - int a = 255; - if (a0 + a1 == 0) { - a0 = 1; - a1 = 1; - a = 0; - } - - int r0 = ((c0 >> 16) & 0xff) * a0; - int g0 = ((c0 >> 8) & 0xff) * a0; - int b0 = ((c0) & 0xff) * a0; - - int r1 = ((c1 >> 16) & 0xff) * a1; - int g1 = ((c1 >> 8) & 0xff) * a1; - int b1 = ((c1) & 0xff) * a1; - - int r = (r0 + r1) / (a0 + a1); - int g = (g0 + g1) / (a0 + a1); - int b = (b0 + b1) / (a0 + a1); - - return (a << 24) | (r << 16) | (g << 8) | b; -} - -///*public*/ int loadHttpTexture(std::string url, std::string backup) { -// HttpTexture texture = httpTextures.get(url); -// if (texture != NULL) { -// if (texture.loadedImage != NULL && !texture.isLoaded) { -// if (texture.id < 0) { -// texture.id = getTexture(texture.loadedImage); -// } else { -// loadTexture(texture.loadedImage, texture.id); -// } -// texture.isLoaded = true; -// } -// } -// if (texture == NULL || texture.id < 0) { -// if (backup == NULL) return -1; -// return loadTexture(backup); -// } -// return texture.id; -//} - -//HttpTexture addHttpTexture(std::string url, HttpTextureProcessor processor) { -// HttpTexture texture = httpTextures.get(url); -// if (texture == NULL) { -// httpTextures.put(url, /*new*/ HttpTexture(url, processor)); -// } else { -// texture.count++; -// } -// return texture; -//} - -//void removeHttpTexture(std::string url) { -// HttpTexture texture = httpTextures.get(url); -// if (texture != NULL) { -// texture.count--; -// if (texture.count == 0) { -// if (texture.id >= 0) releaseTexture(texture.id); -// httpTextures.remove(url); -// } -// } -//} - -//void tick() { -// for (int i = 0; i < dynamicTextures.size(); i++) { -// DynamicTexture dynamicTexture = dynamicTextures.get(i); -// dynamicTexture.anaglyph3d = options.anaglyph3d; -// dynamicTexture.tick(); -// -// pixels.clear(); -// pixels.put(dynamicTexture.pixels); -// pixels.position(0).limit(dynamicTexture.pixels.length); -// -// dynamicTexture.bindTexture(this); -// -// for (int xx = 0; xx < dynamicTexture.replicate; xx++) -// for (int yy = 0; yy < dynamicTexture.replicate; yy++) { -// -// glTexSubImage2D2(GL_TEXTURE_2D, 0, dynamicTexture.tex % 16 * 16 + xx * 16, dynamicTexture.tex / 16 * 16 + yy * 16, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, pixels); -// if (MIPMAP) { -// for (int level = 1; level <= 4; level++) { -// int os = 16 >> (level - 1); -// int s = 16 >> level; -// -// for (int x = 0; x < s; x++) -// for (int y = 0; y < s; y++) { -// int c0 = pixels.getInt(((x * 2 + 0) + (y * 2 + 0) * os) * 4); -// int c1 = pixels.getInt(((x * 2 + 1) + (y * 2 + 0) * os) * 4); -// int c2 = pixels.getInt(((x * 2 + 1) + (y * 2 + 1) * os) * 4); -// int c3 = pixels.getInt(((x * 2 + 0) + (y * 2 + 1) * os) * 4); -// int col = smoothBlend(smoothBlend(c0, c1), smoothBlend(c2, c3)); -// pixels.putInt((x + y * s) * 4, col); -// } -// glTexSubImage2D2(GL_TEXTURE_2D, level, dynamicTexture.tex % 16 * s, dynamicTexture.tex / 16 * s, s, s, GL_RGBA, GL_UNSIGNED_BYTE, pixels); -// } -// } -// } -// } -// -// for (int i = 0; i < dynamicTextures.size(); i++) { -// DynamicTexture dynamicTexture = dynamicTextures.get(i); -// -// if (dynamicTexture.copyTo > 0) { -// pixels.clear(); -// pixels.put(dynamicTexture.pixels); -// pixels.position(0).limit(dynamicTexture.pixels.length); -// glBindTexture2(GL_TEXTURE_2D, dynamicTexture.copyTo); -// glTexSubImage2D2(GL_TEXTURE_2D, 0, 0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, pixels); -// if (MIPMAP) { -// for (int level = 1; level <= 4; level++) { -// int os = 16 >> (level - 1); -// int s = 16 >> level; -// -// for (int x = 0; x < s; x++) -// for (int y = 0; y < s; y++) { -// int c0 = pixels.getInt(((x * 2 + 0) + (y * 2 + 0) * os) * 4); -// int c1 = pixels.getInt(((x * 2 + 1) + (y * 2 + 0) * os) * 4); -// int c2 = pixels.getInt(((x * 2 + 1) + (y * 2 + 1) * os) * 4); -// int c3 = pixels.getInt(((x * 2 + 0) + (y * 2 + 1) * os) * 4); -// int col = smoothBlend(smoothBlend(c0, c1), smoothBlend(c2, c3)); -// pixels.putInt((x + y * s) * 4, col); -// } -// glTexSubImage2D2(GL_TEXTURE_2D, level, 0, 0, s, s, GL_RGBA, GL_UNSIGNED_BYTE, pixels); -// } -// } -// } -// } -//} -// void releaseTexture(int id) { -// loadedImages.erase(id); -// glDeleteTextures(1, (const GLuint*)&id); -// } - +#include "Textures.h" + +#include "TextureData.h" +#include "ptexture/DynamicTexture.h" +#include "../Options.h" +#include "../../platform/time.h" +#include "../../AppPlatform.h" +#include "../../util/StringUtils.h" + +/*static*/ int Textures::textureChanges = 0; +/*static*/ bool Textures::MIPMAP = false; +/*static*/ const TextureId Textures::InvalidId = -1; + +Textures::Textures( Options* options_, AppPlatform* platform_ ) +: clamp(false), + blur(false), + options(options_), + platform(platform_), + lastBoundTexture(Textures::InvalidId) +{ +} + +Textures::~Textures() +{ + clear(); + + for (unsigned int i = 0; i < dynamicTextures.size(); ++i) + delete dynamicTextures[i]; +} + +void Textures::clear() +{ + for (TextureMap::iterator it = idMap.begin(); it != idMap.end(); ++it) { + if (it->second != Textures::InvalidId) + glDeleteTextures(1, &it->second); + } + for (TextureImageMap::iterator it = loadedImages.begin(); it != loadedImages.end(); ++it) { + if (!(it->second).memoryHandledExternally) + delete[] (it->second).data; + } + idMap.clear(); + loadedImages.clear(); + + lastBoundTexture = Textures::InvalidId; +} + +TextureId Textures::loadAndBindTexture( const std::string& resourceName ) +{ + //static Stopwatch t; + + //t.start(); + TextureId id = loadTexture(resourceName); + //t.stop(); + if (id != Textures::InvalidId) + bind(id); + + //t.printEvery(1000); + + return id; +} + +TextureId Textures::loadTexture( const std::string& resourceName, bool inTextureFolder /* = true */ ) +{ + TextureMap::iterator it = idMap.find(resourceName); + if (it != idMap.end()) + return it->second; + + bool isUrl = Util::startsWith(resourceName, "http://") || Util::startsWith(resourceName, "https://"); + TextureData texdata = platform->loadTexture(resourceName, isUrl ? false : inTextureFolder); + if (texdata.data) + return assignTexture(resourceName, texdata); + else if (texdata.identifier != InvalidId) { + //LOGI("Adding id: %d for %s\n", texdata.identifier, resourceName.c_str()); + idMap.insert(std::make_pair(resourceName, texdata.identifier)); + } + else { + idMap.insert(std::make_pair(resourceName, Textures::InvalidId)); + //loadedImages.insert(std::make_pair(InvalidId, texdata)); + } + return Textures::InvalidId; +} + +TextureId Textures::assignTexture( const std::string& resourceName, const TextureData& img ) +{ + TextureId id; + glGenTextures(1, &id); + + bind(id); + + if (MIPMAP) { + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + if (blur) { + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + if (clamp) { + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } else { + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri2(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + switch (img.format) + { + case TEXF_COMPRESSED_PVRTC_4444: + case TEXF_COMPRESSED_PVRTC_565: + case TEXF_COMPRESSED_PVRTC_5551: + { +#if defined(__APPLE__) + int fmt = img.transparent? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + glCompressedTexImage2D(GL_TEXTURE_2D, 0, fmt, img.w, img.h, 0, img.numBytes, img.data); +#endif + break; + } + + default: + const GLint mode = img.transparent? GL_RGBA : GL_RGB; + + if (img.format == TEXF_UNCOMPRESSED_565) { + glTexImage2D2(GL_TEXTURE_2D, 0, mode, img.w, img.h, 0, mode, GL_UNSIGNED_SHORT_5_6_5, img.data); + } + else if (img.format == TEXF_UNCOMPRESSED_4444) { + glTexImage2D2(GL_TEXTURE_2D, 0, mode, img.w, img.h, 0, mode, GL_UNSIGNED_SHORT_4_4_4_4, img.data); + } + else if (img.format == TEXF_UNCOMPRESSED_5551) { + glTexImage2D2(GL_TEXTURE_2D, 0, mode, img.w, img.h, 0, mode, GL_UNSIGNED_SHORT_5_5_5_1, img.data); + } + else { + glTexImage2D2(GL_TEXTURE_2D, 0, mode, img.w, img.h, 0, mode, GL_UNSIGNED_BYTE, img.data); + } + break; + } + + //LOGI("Adding id: %d to map\n", id); + idMap.insert(std::make_pair(resourceName, id)); + loadedImages.insert(std::make_pair(id, img)); + + return id; +} + +const TextureData* Textures::getTemporaryTextureData( TextureId id ) +{ + TextureImageMap::iterator it = loadedImages.find(id); + if (it == loadedImages.end()) + return NULL; + + return &it->second; +} + +void Textures::tick(bool uploadToGraphicsCard) +{ + for (unsigned int i = 0; i < dynamicTextures.size(); ++i ) { + DynamicTexture* tex = dynamicTextures[i]; + tex->tick(); + + if (uploadToGraphicsCard) { + tex->bindTexture(this); + for (int xx = 0; xx < tex->replicate; xx++) + for (int yy = 0; yy < tex->replicate; yy++) { + glTexSubImage2D2(GL_TEXTURE_2D, 0, tex->tex % 16 * 16 + xx * 16, + tex->tex / 16 * 16 + yy * 16, 16, 16, + GL_RGBA, GL_UNSIGNED_BYTE, tex->pixels); + } + } + } +} + +void Textures::addDynamicTexture( DynamicTexture* dynamicTexture ) +{ + dynamicTextures.push_back(dynamicTexture); + dynamicTexture->tick(); +} + +void Textures::reloadAll() +{ + //TexturePack skin = skins.selected; + + //for (int id : loadedImages.keySet()) { + // BufferedImage image = loadedImages.get(id); + // loadTexture(image, id); + //} + + ////for (HttpTexture httpTexture : httpTextures.values()) { + //// httpTexture.isLoaded = false; + ////} + + //for (std::string name : idMap.keySet()) { + // try { + // BufferedImage image; + // if (name.startsWith("##")) { + // image = makeStrip(readImage(skin.getResource(name.substring(2)))); + // } else if (name.startsWith("%clamp%")) { + // clamp = true; + // image = readImage(skin.getResource(name.substring(7))); + // } else if (name.startsWith("%blur%")) { + // blur = true; + // image = readImage(skin.getResource(name.substring(6))); + // } else { + // image = readImage(skin.getResource(name)); + // } + // int id = idMap.get(name); + // loadTexture(image, id); + // blur = false; + // clamp = false; + // } catch (IOException e) { + // e.printStackTrace(); + // } + //} +} + +int Textures::smoothBlend( int c0, int c1 ) +{ + int a0 = (int) (((c0 & 0xff000000) >> 24)) & 0xff; + int a1 = (int) (((c1 & 0xff000000) >> 24)) & 0xff; + return ((a0 + a1) >> 1 << 24) + (((c0 & 0x00fefefe) + (c1 & 0x00fefefe)) >> 1); +} + +int Textures::crispBlend( int c0, int c1 ) +{ + int a0 = (int) (((c0 & 0xff000000) >> 24)) & 0xff; + int a1 = (int) (((c1 & 0xff000000) >> 24)) & 0xff; + + int a = 255; + if (a0 + a1 == 0) { + a0 = 1; + a1 = 1; + a = 0; + } + + int r0 = ((c0 >> 16) & 0xff) * a0; + int g0 = ((c0 >> 8) & 0xff) * a0; + int b0 = ((c0) & 0xff) * a0; + + int r1 = ((c1 >> 16) & 0xff) * a1; + int g1 = ((c1 >> 8) & 0xff) * a1; + int b1 = ((c1) & 0xff) * a1; + + int r = (r0 + r1) / (a0 + a1); + int g = (g0 + g1) / (a0 + a1); + int b = (b0 + b1) / (a0 + a1); + + return (a << 24) | (r << 16) | (g << 8) | b; +} + +///*public*/ int loadHttpTexture(std::string url, std::string backup) { +// HttpTexture texture = httpTextures.get(url); +// if (texture != NULL) { +// if (texture.loadedImage != NULL && !texture.isLoaded) { +// if (texture.id < 0) { +// texture.id = getTexture(texture.loadedImage); +// } else { +// loadTexture(texture.loadedImage, texture.id); +// } +// texture.isLoaded = true; +// } +// } +// if (texture == NULL || texture.id < 0) { +// if (backup == NULL) return -1; +// return loadTexture(backup); +// } +// return texture.id; +//} + +//HttpTexture addHttpTexture(std::string url, HttpTextureProcessor processor) { +// HttpTexture texture = httpTextures.get(url); +// if (texture == NULL) { +// httpTextures.put(url, /*new*/ HttpTexture(url, processor)); +// } else { +// texture.count++; +// } +// return texture; +//} + +//void removeHttpTexture(std::string url) { +// HttpTexture texture = httpTextures.get(url); +// if (texture != NULL) { +// texture.count--; +// if (texture.count == 0) { +// if (texture.id >= 0) releaseTexture(texture.id); +// httpTextures.remove(url); +// } +// } +//} + +//void tick() { +// for (int i = 0; i < dynamicTextures.size(); i++) { +// DynamicTexture dynamicTexture = dynamicTextures.get(i); +// dynamicTexture.anaglyph3d = options.anaglyph3d; +// dynamicTexture.tick(); +// +// pixels.clear(); +// pixels.put(dynamicTexture.pixels); +// pixels.position(0).limit(dynamicTexture.pixels.length); +// +// dynamicTexture.bindTexture(this); +// +// for (int xx = 0; xx < dynamicTexture.replicate; xx++) +// for (int yy = 0; yy < dynamicTexture.replicate; yy++) { +// +// glTexSubImage2D2(GL_TEXTURE_2D, 0, dynamicTexture.tex % 16 * 16 + xx * 16, dynamicTexture.tex / 16 * 16 + yy * 16, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, pixels); +// if (MIPMAP) { +// for (int level = 1; level <= 4; level++) { +// int os = 16 >> (level - 1); +// int s = 16 >> level; +// +// for (int x = 0; x < s; x++) +// for (int y = 0; y < s; y++) { +// int c0 = pixels.getInt(((x * 2 + 0) + (y * 2 + 0) * os) * 4); +// int c1 = pixels.getInt(((x * 2 + 1) + (y * 2 + 0) * os) * 4); +// int c2 = pixels.getInt(((x * 2 + 1) + (y * 2 + 1) * os) * 4); +// int c3 = pixels.getInt(((x * 2 + 0) + (y * 2 + 1) * os) * 4); +// int col = smoothBlend(smoothBlend(c0, c1), smoothBlend(c2, c3)); +// pixels.putInt((x + y * s) * 4, col); +// } +// glTexSubImage2D2(GL_TEXTURE_2D, level, dynamicTexture.tex % 16 * s, dynamicTexture.tex / 16 * s, s, s, GL_RGBA, GL_UNSIGNED_BYTE, pixels); +// } +// } +// } +// } +// +// for (int i = 0; i < dynamicTextures.size(); i++) { +// DynamicTexture dynamicTexture = dynamicTextures.get(i); +// +// if (dynamicTexture.copyTo > 0) { +// pixels.clear(); +// pixels.put(dynamicTexture.pixels); +// pixels.position(0).limit(dynamicTexture.pixels.length); +// glBindTexture2(GL_TEXTURE_2D, dynamicTexture.copyTo); +// glTexSubImage2D2(GL_TEXTURE_2D, 0, 0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, pixels); +// if (MIPMAP) { +// for (int level = 1; level <= 4; level++) { +// int os = 16 >> (level - 1); +// int s = 16 >> level; +// +// for (int x = 0; x < s; x++) +// for (int y = 0; y < s; y++) { +// int c0 = pixels.getInt(((x * 2 + 0) + (y * 2 + 0) * os) * 4); +// int c1 = pixels.getInt(((x * 2 + 1) + (y * 2 + 0) * os) * 4); +// int c2 = pixels.getInt(((x * 2 + 1) + (y * 2 + 1) * os) * 4); +// int c3 = pixels.getInt(((x * 2 + 0) + (y * 2 + 1) * os) * 4); +// int col = smoothBlend(smoothBlend(c0, c1), smoothBlend(c2, c3)); +// pixels.putInt((x + y * s) * 4, col); +// } +// glTexSubImage2D2(GL_TEXTURE_2D, level, 0, 0, s, s, GL_RGBA, GL_UNSIGNED_BYTE, pixels); +// } +// } +// } +// } +//} +// void releaseTexture(int id) { +// loadedImages.erase(id); +// glDeleteTextures(1, (const GLuint*)&id); +// } + diff --git a/src/client/renderer/Textures.h b/src/client/renderer/Textures.h index f5e2113..93bc5f8 100755 --- a/src/client/renderer/Textures.h +++ b/src/client/renderer/Textures.h @@ -1,77 +1,77 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__Textures_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__Textures_H__ - -//package net.minecraft.client.renderer; - -#include -#include -#include -#include "gles.h" -#include "TextureData.h" - -class DynamicTexture; -class Options; -class AppPlatform; - -typedef GLuint TextureId; -typedef std::map TextureMap; -typedef std::map TextureImageMap; - -//@todo: Should probably delete the data buffers with image data -// after we've created an OpenGL-texture, and rewrite the -// getTemporaryTextureData() to actually load from file IF -// it's only read ~once anyway. -class Textures -{ -public: - Textures(Options* options_, AppPlatform* platform_); - ~Textures(); - - void addDynamicTexture(DynamicTexture* dynamicTexture); - - __inline void bind(TextureId id) { - if (id != Textures::InvalidId && lastBoundTexture != id) { - glBindTexture2(GL_TEXTURE_2D, id); - lastBoundTexture = id; - ++textureChanges; - } else if (id == Textures::InvalidId){ - LOGI("invalidId!\n"); - } - } - TextureId loadTexture(const std::string& resourceName, bool inTextureFolder = true); - TextureId loadAndBindTexture(const std::string& resourceName); - - TextureId assignTexture(const std::string& resourceName, const TextureData& img); - const TextureData* getTemporaryTextureData(TextureId id); - - void tick(bool uploadToGraphicsCard); - - void clear(); - void reloadAll(); - - __inline static bool isTextureIdValid(TextureId t) { return t != Textures::InvalidId; } - -private: - int smoothBlend(int c0, int c1); - int crispBlend(int c0, int c1); - -public: - static bool MIPMAP; - static int textureChanges; - static const TextureId InvalidId; - -private: - TextureMap idMap; - TextureImageMap loadedImages; - - Options* options; - AppPlatform* platform; - - bool clamp; - bool blur; - - int lastBoundTexture; - std::vector dynamicTextures; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__Textures_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__Textures_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__Textures_H__ + +//package net.minecraft.client.renderer; + +#include +#include +#include +#include "gles.h" +#include "TextureData.h" + +class DynamicTexture; +class Options; +class AppPlatform; + +typedef GLuint TextureId; +typedef std::map TextureMap; +typedef std::map TextureImageMap; + +//@todo: Should probably delete the data buffers with image data +// after we've created an OpenGL-texture, and rewrite the +// getTemporaryTextureData() to actually load from file IF +// it's only read ~once anyway. +class Textures +{ +public: + Textures(Options* options_, AppPlatform* platform_); + ~Textures(); + + void addDynamicTexture(DynamicTexture* dynamicTexture); + + __inline void bind(TextureId id) { + if (id != Textures::InvalidId && lastBoundTexture != id) { + glBindTexture2(GL_TEXTURE_2D, id); + lastBoundTexture = id; + ++textureChanges; + } else if (id == Textures::InvalidId){ + LOGI("invalidId!\n"); + } + } + TextureId loadTexture(const std::string& resourceName, bool inTextureFolder = true); + TextureId loadAndBindTexture(const std::string& resourceName); + + TextureId assignTexture(const std::string& resourceName, const TextureData& img); + const TextureData* getTemporaryTextureData(TextureId id); + + void tick(bool uploadToGraphicsCard); + + void clear(); + void reloadAll(); + + __inline static bool isTextureIdValid(TextureId t) { return t != Textures::InvalidId; } + +private: + int smoothBlend(int c0, int c1); + int crispBlend(int c0, int c1); + +public: + static bool MIPMAP; + static int textureChanges; + static const TextureId InvalidId; + +private: + TextureMap idMap; + TextureImageMap loadedImages; + + Options* options; + AppPlatform* platform; + + bool clamp; + bool blur; + + int lastBoundTexture; + std::vector dynamicTextures; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__Textures_H__*/ diff --git a/src/client/renderer/TileRenderer.cpp b/src/client/renderer/TileRenderer.cpp index a98e82b..3e472ba 100755 --- a/src/client/renderer/TileRenderer.cpp +++ b/src/client/renderer/TileRenderer.cpp @@ -1,2814 +1,2814 @@ -#include "TileRenderer.h" -#include "Chunk.h" -#include "../Minecraft.h" -#include "Tesselator.h" - -#include "../../world/level/LevelSource.h" -#include "../../world/level/tile/Tile.h" -#include "../../world/level/tile/DoorTile.h" -#include "../../world/level/tile/LiquidTile.h" -#include "../../world/level/tile/FenceTile.h" -#include "../../world/level/tile/FenceGateTile.h" -#include "../../world/level/tile/ThinFenceTile.h" -#include "../../world/level/tile/BedTile.h" -#include "../../world/level/tile/StemTile.h" -#include "../../world/level/tile/StairTile.h" -#include "../../world/Direction.h" -#include "../../world/Facing.h" -#include "tileentity/TileEntityRenderer.h" -#include "EntityTileRenderer.h" - -TileRenderer::TileRenderer( LevelSource* level /* = NULL */ ) -: level(level), - fixedTexture(-1), - xFlipTexture(false), - noCulling(false), - blsmooth(1), - applyAmbienceOcclusion(false) -{ -} - -bool TileRenderer::tesselateBlockInWorld(Tile* tt, int x, int y, int z) { - int col = tt->getColor(level, x, y, z); - float r = ((col >> 16) & 0xff) / 255.0f; - float g = ((col >> 8) & 0xff) / 255.0f; - float b = ((col) & 0xff) / 255.0f; // xFlipTexture = (x & 1) != (y & 1); - - if (Minecraft::useAmbientOcclusion) { - return tesselateBlockInWorldWithAmbienceOcclusion(tt, x, y, z, r, g, b); - } else - { - return tesselateBlockInWorld(tt, x, y, z, r, g, b); - } -} - -bool TileRenderer::tesselateBlockInWorld( Tile* tt, int x, int y, int z, float r, float g, float b ) -{ - applyAmbienceOcclusion = false; - float xf = (float)x; - float yf = (float)y; - float zf = (float)z; - - Tesselator& t = Tesselator::instance; - - bool changed = false; - float c10 = 0.5f; - float c11 = 1; - float c2 = 0.8f; - float c3 = 0.6f; - - - float r11 = c11 * r; - float g11 = c11 * g; - float b11 = c11 * b; - - if (tt == (Tile*)Tile::grass) { - r = g = b = 1.0f; - } - - float r10 = c10 * r; - float r2 = c2 * r; - float r3 = c3 * r; - - float g10 = c10 * g; - float g2 = c2 * g; - float g3 = c3 * g; - - float b10 = c10 * b; - float b2 = c2 * b; - float b3 = c3 * b; - - float centerBrightness = tt->getBrightness(level, x, y, z); - - if (noCulling || tt->shouldRenderFace(level, x, y - 1, z, Facing::DOWN)) { - float br = tt->getBrightness(level, x, y - 1, z); - t.color(r10 * br, g10 * br, b10 * br); - renderFaceDown(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 0)); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x, y + 1, z, Facing::UP)) { - float br = tt->getBrightness(level, x, y + 1, z); - if (tt->yy1 != 1 && !tt->material->isLiquid()) br = centerBrightness; - t.color(r11 * br, g11 * br, b11 * br); - renderFaceUp(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 1)); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x, y, z - 1, Facing::NORTH)) { - float br = tt->getBrightness(level, x, y, z - 1); - if (tt->zz0 > 0) br = centerBrightness; - t.color(r2 * br, g2 * br, b2 * br); - renderNorth(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 2)); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x, y, z + 1, Facing::SOUTH)) { - float br = tt->getBrightness(level, x, y, z + 1); - if (tt->zz1 < 1) br = centerBrightness; - t.color(r2 * br, g2 * br, b2 * br); - renderSouth(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 3)); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x - 1, y, z, Facing::WEST)) { - float br = tt->getBrightness(level, x - 1, y, z); - if (tt->xx0 > 0) br = centerBrightness; - t.color(r3 * br, g3 * br, b3 * br); - renderWest(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 4)); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x + 1, y, z, Facing::EAST)) { - float br = tt->getBrightness(level, x + 1, y, z); - if (tt->xx1 < 1) br = centerBrightness; - t.color(r3 * br, g3 * br, b3 * br); - renderEast(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 5)); - changed = true; - } - - return changed; -} - - -void TileRenderer::tesselateInWorld( Tile* tile, int x, int y, int z, int fixedTexture ) -{ - this->fixedTexture = fixedTexture; - tesselateInWorld(tile, x, y, z); - this->fixedTexture = -1; -} - -bool TileRenderer::tesselateInWorld( Tile* tt, int x, int y, int z ) -{ - int shape = tt->getRenderShape(); - tt->updateShape(level, x, y, z); - - if (shape == Tile::SHAPE_BLOCK) { - return tesselateBlockInWorld(tt, x, y, z); - } else if (shape == Tile::SHAPE_WATER) { - return tesselateWaterInWorld(tt, x, y, z); - } else if (shape == Tile::SHAPE_CACTUS) { - return tesselateCactusInWorld(tt, x, y, z); - } else if (shape == Tile::SHAPE_CROSS_TEXTURE) { - return tesselateCrossInWorld(tt, x, y, z); - } else if(shape == Tile::SHAPE_STEM) { - return tesselateStemInWorld(tt, x, y, z); - } else if (shape == Tile::SHAPE_ROWS) { - return tesselateRowInWorld(tt, x, y, z); - } else if (shape == Tile::SHAPE_TORCH) { - return tesselateTorchInWorld(tt, x, y, z); - //} else if (shape == Tile::SHAPE_FIRE) { - // return tesselateFireInWorld(tt, x, y, z); - //} else if (shape == Tile::SHAPE_RED_DUST) { - // return tesselateDustInWorld(tt, x, y, z); - } else if (shape == Tile::SHAPE_LADDER) { - return tesselateLadderInWorld(tt, x, y, z); - } else if (shape == Tile::SHAPE_DOOR) { - return tesselateDoorInWorld(tt, x, y, z); - //} else if (shape == Tile::SHAPE_RAIL) { - // return tesselateRailInWorld(tt, x, y, z); - } else if (shape == Tile::SHAPE_STAIRS) { - return tesselateStairsInWorld((StairTile*)tt, x, y, z); - } else if (shape == Tile::SHAPE_FENCE) { - return tesselateFenceInWorld((FenceTile*)tt, x, y, z); - } else if (shape == Tile::SHAPE_FENCE_GATE) { - return tesselateFenceGateInWorld((FenceGateTile*) tt, x, y, z); - //} else if (shape == Tile::SHAPE_LEVER) { - // return tesselateLeverInWorld(tt, x, y, z); - //} else if (shape == Tile::SHAPE_BED) { - // return tesselateBedInWorld(tt, x, y, z); - //} else if (shape == Tile::SHAPE_DIODE) { - // return tesselateDiodeInWorld(tt, x, y, z); - } else if (shape == Tile::SHAPE_IRON_FENCE) { - return tesselateThinFenceInWorld((ThinFenceTile*) tt, x, y, z); - } else if(shape == Tile::SHAPE_BED) { - return tesselateBedInWorld(tt, x, y, z); - } else { - return false; - } -} - -void TileRenderer::tesselateInWorldNoCulling( Tile* tile, int x, int y, int z ) -{ - noCulling = true; - tesselateInWorld(tile, x, y, z); - noCulling = false; -} - -bool TileRenderer::tesselateTorchInWorld( Tile* tt, int x, int y, int z ) -{ - int dir = level->getData(x, y, z); - - Tesselator& t = Tesselator::instance; - - float br = tt->getBrightness(level, x, y, z); - if (Tile::lightEmission[tt->id] > 0) br = 1.0f; - t.color(br, br, br); - - float r = 0.40f; - float r2 = 0.5f - r; - float h = 0.20f; - if (dir == 1) { - tesselateTorch(tt, (float)x - r2, (float)y + h, (float)z, -r, 0); - } else if (dir == 2) { - tesselateTorch(tt, (float)x + r2, (float)y + h, (float)z, +r, 0); - } else if (dir == 3) { - tesselateTorch(tt, (float)x, (float)y + h, (float)z - r2, 0, -r); - } else if (dir == 4) { - tesselateTorch(tt, (float)x, (float)y + h, (float)z + r2, 0, +r); - } else { - tesselateTorch(tt, (float)x, (float)y, (float)z, 0, 0); - } - return true; -} - -bool TileRenderer::tesselateLadderInWorld( Tile* tt, int x, int y, int z ) -{ - Tesselator& t = Tesselator::instance; - - int tex = tt->getTexture(0); - - if (fixedTexture >= 0) tex = fixedTexture; - - float br = tt->getBrightness(level, x, y, z); - t.color(br, br, br); - int xt = ((tex & 0xf) << 4); - int yt = tex & 0xf0; - - float u0 = (xt) / 256.0f; - float u1 = (xt + 15.99f) / 256.0f; - float v0 = (yt) / 256.0f; - float v1 = (yt + 15.99f) / 256.0f; - - int face = level->getData(x, y, z); - - float o = 0 / 16.0f; - float r = 0.05f; - if (face == 5) { - t.vertexUV(x + r, y + 1 + o, z + 1 + o, u0, v0); - t.vertexUV(x + r, y + 0 - o, z + 1 + o, u0, v1); - t.vertexUV(x + r, y + 0 - o, z + 0 - o, u1, v1); - t.vertexUV(x + r, y + 1 + o, z + 0 - o, u1, v0); - } - if (face == 4) { - t.vertexUV(x + 1 - r, y + 0 - o, z + 1 + o, u1, v1); - t.vertexUV(x + 1 - r, y + 1 + o, z + 1 + o, u1, v0); - t.vertexUV(x + 1 - r, y + 1 + o, z + 0 - o, u0, v0); - t.vertexUV(x + 1 - r, y + 0 - o, z + 0 - o, u0, v1); - } - if (face == 3) { - t.vertexUV(x + 1 + o, y + 0 - o, z + r, u1, v1); - t.vertexUV(x + 1 + o, y + 1 + o, z + r, u1, v0); - t.vertexUV(x + 0 - o, y + 1 + o, z + r, u0, v0); - t.vertexUV(x + 0 - o, y + 0 - o, z + r, u0, v1); - } - if (face == 2) { - t.vertexUV(x + 1 + o, y + 1 + o, z + 1 - r, u0, v0); - t.vertexUV(x + 1 + o, y + 0 - o, z + 1 - r, u0, v1); - t.vertexUV(x + 0 - o, y + 0 - o, z + 1 - r, u1, v1); - t.vertexUV(x + 0 - o, y + 1 + o, z + 1 - r, u1, v0); - } - - return true; -} - -bool TileRenderer::tesselateCrossInWorld( Tile* tt, int x, int y, int z ) -{ - Tesselator& t = Tesselator::instance; - - float br = tt->getBrightness(level, x, y, z); - int col = tt->getColor(level, x, y, z); - float r = ((col >> 16) & 0xff) / 255.0f; - float g = ((col >> 8) & 0xff) / 255.0f; - float b = ((col) & 0xff) / 255.0f; - t.color(br * r, br * g, br * b); - - float xt = float(x); - float yt = float(y); - float zt = float(z); - - if (tt == Tile::tallgrass) { - long seed = (x * 3129871) ^ (z * 116129781l) ^ (y); - seed = seed * seed * 42317861 + seed * 11; - - xt += ((((seed >> 16) & 0xf) / 15.0f) - 0.5f) * 0.5f; - yt += ((((seed >> 20) & 0xf) / 15.0f) - 1.0f) * 0.2f; - zt += ((((seed >> 24) & 0xf) / 15.0f) - 0.5f) * 0.5f; - } - - tesselateCrossTexture(tt, level->getData(x, y, z), xt, yt, zt); - return true; - //return true; - /*Tesselator& t = Tesselator::instance; - - float br = tt->getBrightness(level, x, y, z); - t.color(br, br, br); - - tesselateCrossTexture(tt, level->getData(x, y, z), (float)x, (float)y, (float)z); - return true;*/ -} -bool TileRenderer::tesselateStemInWorld( Tile* _tt, int x, int y, int z ) { - StemTile* tt = (StemTile*) _tt; - Tesselator& t = Tesselator::instance; - - float br = tt->getBrightness(level, x, y, z); - - int col = tt->getColor(level, x, y, z); - float r = ((col >> 16) & 0xff) / 255.0f; - float g = ((col >> 8) & 0xff) / 255.0f; - float b = ((col) & 0xff) / 255.0f; - - t.color(br * r, br * g, br * b); - - tt->updateShape(level, x, y, z); - int dir = tt->getConnectDir(level, x, y, z); - if (dir < 0) { - tesselateStemTexture(tt, level->getData(x, y, z), tt->yy1, float(x), float(y - 1 / 16.0f), float(z)); - } else { - tesselateStemTexture(tt, level->getData(x, y, z), 0.5f, float(x), float(y - 1 / 16.0f), float(z)); - tesselateStemDirTexture(tt, level->getData(x, y, z), dir, tt->yy1, float(x), float(y - 1 / 16.0f), float(z)); - } - return true; -} -void TileRenderer::tesselateTorch( Tile* tt, float x, float y, float z, float xxa, float zza ) -{ - Tesselator& t = Tesselator::instance; - int tex = tt->getTexture(0); - - if (fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - float u0 = (xt) / 256.0f; - float u1 = (xt + 15.99f) / 256.0f; - float v0 = (yt) / 256.0f; - float v1 = (yt + 15.99f) / 256.0f; - - - float uc0 = u0 + 7 / 256.0f; - float vc0 = v0 + 6 / 256.0f; - float uc1 = u0 + 9 / 256.0f; - float vc1 = v0 + 8 / 256.0f; - x += 0.5f; - z += 0.5f; - - float x0 = x - 0.5f; - float x1 = x + 0.5f; - float z0 = z - 0.5f; - float z1 = z + 0.5f; - float r = 1 / 16.0f; - - float h = 10.0f / 16.0f; - t.vertexUV(x + xxa * (1 - h) - r, y + h, z + zza * (1 - h) - r, uc0, vc0); - t.vertexUV(x + xxa * (1 - h) - r, y + h, z + zza * (1 - h) + r, uc0, vc1); - t.vertexUV(x + xxa * (1 - h) + r, y + h, z + zza * (1 - h) + r, uc1, vc1); - t.vertexUV(x + xxa * (1 - h) + r, y + h, z + zza * (1 - h) - r, uc1, vc0); - - t.vertexUV(x - r, y + 1, z0, u0, v0); - t.vertexUV(x - r + xxa, y + 0, z0 + zza, u0, v1); - t.vertexUV(x - r + xxa, y + 0, z1 + zza, u1, v1); - t.vertexUV(x - r, y + 1, z1, u1, v0); - - t.vertexUV(x + r, y + 1, z1, u0, v0); - t.vertexUV(x + xxa + r, y + 0, z1 + zza, u0, v1); - t.vertexUV(x + xxa + r, y + 0, z0 + zza, u1, v1); - t.vertexUV(x + r, y + 1, z0, u1, v0); - - t.vertexUV(x0, y + 1, z + r, u0, v0); - t.vertexUV(x0 + xxa, y + 0, z + r + zza, u0, v1); - t.vertexUV(x1 + xxa, y + 0, z + r + zza, u1, v1); - t.vertexUV(x1, y + 1, z + r, u1, v0); - - t.vertexUV(x1, y + 1, z - r, u0, v0); - t.vertexUV(x1 + xxa, y + 0, z - r + zza, u0, v1); - t.vertexUV(x0 + xxa, y + 0, z - r + zza, u1, v1); - t.vertexUV(x0, y + 1, z - r, u1, v0); -} - -void TileRenderer::tesselateCrossTexture( Tile* tt, int data, float x, float y, float z ) -{ - Tesselator& t = Tesselator::instance; - - int tex = tt->getTexture(0, data); - - if (fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - float u0 = (xt) / 256.0f; - float u1 = (xt + 15.99f) / 256.0f; - float v0 = (yt) / 256.0f; - float v1 = (yt + 15.99f) / 256.0f; - - float x0 = x + 0.5f - 0.45f; - float x1 = x + 0.5f + 0.45f; - float z0 = z + 0.5f - 0.45f; - float z1 = z + 0.5f + 0.45f; - - t.vertexUV(x0, y + 1, z0, u0, v0); - t.vertexUV(x0, y + 0, z0, u0, v1); - t.vertexUV(x1, y + 0, z1, u1, v1); - t.vertexUV(x1, y + 1, z1, u1, v0); - - t.vertexUV(x1, y + 1, z1, u0, v0); - t.vertexUV(x1, y + 0, z1, u0, v1); - t.vertexUV(x0, y + 0, z0, u1, v1); - t.vertexUV(x0, y + 1, z0, u1, v0); - - t.vertexUV(x0, y + 1, z1, u0, v0); - t.vertexUV(x0, y + 0, z1, u0, v1); - t.vertexUV(x1, y + 0, z0, u1, v1); - t.vertexUV(x1, y + 1, z0, u1, v0); - - t.vertexUV(x1, y + 1, z0, u0, v0); - t.vertexUV(x1, y + 0, z0, u0, v1); - t.vertexUV(x0, y + 0, z1, u1, v1); - t.vertexUV(x0, y + 1, z1, u1, v0); -} -void TileRenderer::tesselateStemTexture( Tile* tt, int data, float h, float x, float y, float z ) { - Tesselator& t = Tesselator::instance; - int tex = tt->getTexture(0, data); - if(fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - float u0 = (xt) / 256.0f; - float u1 = (xt + 15.99f) / 256.0f; - float v0 = (yt) / 256.0f; - float v1 = (yt + 15.99f * h) / 256.0f; - - float x0 = x + 0.5f - 0.45f; - float x1 = x + 0.5f + 0.45f; - float z0 = z + 0.5f - 0.45f; - float z1 = z + 0.5f + 0.45f; - - t.vertexUV(x0, y + h, z0, u0, v0); - t.vertexUV(x0, y + 0, z0, u0, v1); - t.vertexUV(x1, y + 0, z1, u1, v1); - t.vertexUV(x1, y + h, z1, u1, v0); - - t.vertexUV(x1, y + h, z1, u0, v0); - t.vertexUV(x1, y + 0, z1, u0, v1); - t.vertexUV(x0, y + 0, z0, u1, v1); - t.vertexUV(x0, y + h, z0, u1, v0); - - t.vertexUV(x0, y + h, z1, u0, v0); - t.vertexUV(x0, y + 0, z1, u0, v1); - t.vertexUV(x1, y + 0, z0, u1, v1); - t.vertexUV(x1, y + h, z0, u1, v0); - - t.vertexUV(x1, y + h, z0, u0, v0); - t.vertexUV(x1, y + 0, z0, u0, v1); - t.vertexUV(x0, y + 0, z1, u1, v1); - t.vertexUV(x0, y + h, z1, u1, v0); -} -void TileRenderer::tesselateStemDirTexture( Tile* tt, int data, int dir, float h, float x, float y, float z ) { - Tesselator& t = Tesselator::instance; - - int tex = tt->getTexture(0, data) + 16; - - if (fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - float u0 = (xt) / 256.0f; - float u1 = (xt + 15.99f) / 256.0f; - float v0 = (yt) / 256.0f; - float v1 = (yt + 15.99f * h) / 256.0f; - - float x0 = x + 0.5f - 0.5f; - float x1 = x + 0.5f + 0.5f; - float z0 = z + 0.5f - 0.5f; - float z1 = z + 0.5f + 0.5f; - - float xm = x + 0.5f; - float zm = z + 0.5f; - - if ((dir + 1) / 2 % 2 == 1) { - float tmp = u1; - u1 = u0; - u0 = tmp; - } - - if (dir < 2) { - t.vertexUV(x0, y + h, zm, u0, v0); - t.vertexUV(x0, y + 0, zm, u0, v1); - t.vertexUV(x1, y + 0, zm, u1, v1); - t.vertexUV(x1, y + h, zm, u1, v0); - - t.vertexUV(x1, y + h, zm, u1, v0); - t.vertexUV(x1, y + 0, zm, u1, v1); - t.vertexUV(x0, y + 0, zm, u0, v1); - t.vertexUV(x0, y + h, zm, u0, v0); - } else { - - t.vertexUV(xm, y + h, z1, u0, v0); - t.vertexUV(xm, y + 0, z1, u0, v1); - t.vertexUV(xm, y + 0, z0, u1, v1); - t.vertexUV(xm, y + h, z0, u1, v0); - - t.vertexUV(xm, y + h, z0, u1, v0); - t.vertexUV(xm, y + 0, z0, u1, v1); - t.vertexUV(xm, y + 0, z1, u0, v1); - t.vertexUV(xm, y + h, z1, u0, v0); - } -} - -bool TileRenderer::tesselateWaterInWorld( Tile* tt, int x, int y, int z ) -{ - Tesselator& t = Tesselator::instance; - - bool up = tt->shouldRenderFace(level, x, y + 1, z, 1); - bool down = tt->shouldRenderFace(level, x, y - 1, z, 0); - - bool dirs[4]; // static? - dirs[0] = tt->shouldRenderFace(level, x, y, z - 1, 2); - dirs[1] = tt->shouldRenderFace(level, x, y, z + 1, 3); - dirs[2] = tt->shouldRenderFace(level, x - 1, y, z, 4); - dirs[3] = tt->shouldRenderFace(level, x + 1, y, z, 5); - - if (!up && !down && !dirs[0] && !dirs[1] && !dirs[2] && !dirs[3]) return false; - - bool changed = false; - float c10 = 0.5f; - float c11 = 1; - float c2 = 0.8f; - float c3 = 0.6f; - - const float yo0 = 0; - const float yo1 = 1; - - const Material* m = tt->material; - int data = level->getData(x, y, z); - - float h0 = getWaterHeight(x, y, z, m); - float h1 = getWaterHeight(x, y, z + 1, m); - float h2 = getWaterHeight(x + 1, y, z + 1, m); - float h3 = getWaterHeight(x + 1, y, z, m); - - // renderFaceUp(tt, x, y, z, tt->getTexture(0)); - if (noCulling || up) { - changed = true; - int tex = tt->getTexture(1, data); - float angle = (float) LiquidTile::getSlopeAngle(level, x, y, z, m); - if (angle > -999) { - tex = tt->getTexture(2, data); - } - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - - float uc = (xt + 0.5f * 16) / 256.0f; - float vc = (yt + 0.5f * 16) / 256.0f; - if (angle < -999) { - angle = 0; - } else { - uc = (xt + 1 * 16) / 256.0f; - vc = (yt + 1 * 16) / 256.0f; - } - float s = (Mth::sin(angle) * 8) / 256.5f; // @attn: to get rid of "jitter" (caused - float c = (Mth::cos(angle) * 8) / 256.5f; // of fp rounding errors) in big oceans) - - float br = tt->getBrightness(level, x, y, z); - t.color(c11 * br, c11 * br, c11 * br); - t.vertexUV((float)x + 0, (float)y + h0, (float)z + 0, uc - c - s, vc - c + s); - t.vertexUV((float)x + 0, (float)y + h1, (float)z + 1, uc - c + s, vc + c + s); - t.vertexUV((float)x + 1, (float)y + h2, (float)z + 1, uc + c + s, vc + c - s); - t.vertexUV((float)x + 1, (float)y + h3, (float)z + 0, uc + c - s, vc - c - s); - } - - if (noCulling || down) { - float br = tt->getBrightness(level, x, y - 1, z); - t.color(c10 * br, c10 * br, c10 * br); - renderFaceDown(tt, (float)x, (float)y, (float)z, tt->getTexture(0)); - changed = true; - } - - for (int face = 0; face < 4; face++) { - int xt = x; - int yt = y; - int zt = z; - - if (face == 0) zt--; - if (face == 1) zt++; - if (face == 2) xt--; - if (face == 3) xt++; - - int tex = tt->getTexture(face + 2, data); - int xTex = (tex & 0xf) << 4; - int yTex = tex & 0xf0; - - if (noCulling || dirs[face]) { - float hh0; - float hh1; - float x0, z0, x1, z1; - if (face == 0) { - hh0 = h0; - hh1 = h3; - x0 = (float)(x ); - x1 = (float)(x + 1); - z0 = (float)(z ); - z1 = (float)(z ); - } else if (face == 1) { - hh0 = h2; - hh1 = h1; - x0 = (float)(x + 1); - x1 = (float)(x ); - z0 = (float)(z + 1); - z1 = (float)(z + 1); - } else if (face == 2) { - hh0 = h1; - hh1 = h0; - x0 = (float)(x ); - x1 = (float)(x ); - z0 = (float)(z + 1); - z1 = (float)(z ); - } else { - hh0 = h3; - hh1 = h2; - x0 = (float)(x + 1); - x1 = (float)(x + 1); - z0 = (float)(z ); - z1 = (float)(z + 1); - } - - changed = true; - float u0 = (xTex + 0 * 16) / 256.0f; - float u1 = (xTex + 1 * 16 - 0.01f) / 256.0f; - - float v01 = (yTex + (1 - hh0) * 16) / 256.0f; - float v02 = (yTex + (1 - hh1) * 16) / 256.0f; - float v1 = (yTex + 1 * 16 - 0.01f) / 256.0f; - - float br = tt->getBrightness(level, xt, yt, zt); - if (face < 2) br *= c2; - else br *= c3; - - float yf = (float)y; - t.color(c11 * br, c11 * br, c11 * br); - t.vertexUV(x0, yf + hh0, z0, u0, v01); - t.vertexUV(x1, yf + hh1, z1, u1, v02); - t.vertexUV(x1, yf + 0, z1, u1, v1); - t.vertexUV(x0, yf + 0, z0, u0, v1); - } - } - - //printf("w: %d ", (dirs[0] + dirs[1] + dirs[2] + dirs[3] + up + down)); - - tt->yy0 = yo0; - tt->yy1 = yo1; - - return changed; -} - -float TileRenderer::getWaterHeight( int x, int y, int z, const Material* m ) -{ - int count = 0; - float h = 0; - for (int i = 0; i < 4; i++) { - int xx = x - (i & 1); - int yy = y; - int zz = z - ((i >> 1) & 1); - if (level->getMaterial(xx, yy + 1, zz) == m) { - return 1; - } - const Material* tm = level->getMaterial(xx, yy, zz); - if (tm == m) { - int d = level->getData(xx, yy, zz); - if (d >= 8 || d == 0) { - h += (LiquidTile::getHeight(d)) * 10; - count += 10; - } - h += LiquidTile::getHeight(d); - count++; - } else if (!tm->isSolid()) { - h += 1; - count++; - } - } - return 1 - h / count; -} - -void TileRenderer::renderBlock(Tile* tt, LevelSource* level, int x, int y, int z) { - float c10 = 0.5f; - float c11 = 1; - float c2 = 0.8f; - float c3 = 0.6f; - - Tesselator& t = Tesselator::instance; - t.begin(); - - float center = tt->getBrightness(level, x, y, z); - float br = tt->getBrightness(level, x, y - 1, z); - if (br < center) br = center; - - t.color(c10 * br, c10 * br, c10 * br); - renderFaceDown(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(0)); - - br = tt->getBrightness(level, x, y + 1, z); - if (br < center) br = center; - t.color(c11 * br, c11 * br, c11 * br); - renderFaceUp(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(1)); - - br = tt->getBrightness(level, x, y, z - 1); - if (br < center) br = center; - t.color(c2 * br, c2 * br, c2 * br); - renderNorth(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(2)); - - br = tt->getBrightness(level, x, y, z + 1); - if (br < center) br = center; - t.color(c2 * br, c2 * br, c2 * br); - renderSouth(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(3)); - - br = tt->getBrightness(level, x - 1, y, z); - if (br < center) br = center; - t.color(c3 * br, c3 * br, c3 * br); - renderWest(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(4)); - - br = tt->getBrightness(level, x + 1, y, z); - if (br < center) br = center; - t.color(c3 * br, c3 * br, c3 * br); - renderEast(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(5)); - t.draw(); -} - -bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusion( Tile* tt, int pX, int pY, int pZ, float pBaseRed, float pBaseGreen, float pBaseBlue ) -{ - applyAmbienceOcclusion = true; - bool i = false; - float ll1 = ll000; - float ll2 = ll000; - float ll3 = ll000; - float ll4 = ll000; - bool tint0 = true; - bool tint1 = true; - bool tint2 = true; - bool tint3 = true; - bool tint4 = true; - bool tint5 = true; - - ll000 = tt->getBrightness(level, pX, pY, pZ); - llx00 = tt->getBrightness(level, pX - 1, pY, pZ); - ll0y0 = tt->getBrightness(level, pX, pY - 1, pZ); - ll00z = tt->getBrightness(level, pX, pY, pZ - 1); - llX00 = tt->getBrightness(level, pX + 1, pY, pZ); - ll0Y0 = tt->getBrightness(level, pX, pY + 1, pZ); - ll00Z = tt->getBrightness(level, pX, pY, pZ + 1); - - llTransXY0 = Tile::translucent[level->getTile(pX + 1, pY + 1, pZ)]; - llTransXy0 = Tile::translucent[level->getTile(pX + 1, pY - 1, pZ)]; - llTransX0Z = Tile::translucent[level->getTile(pX + 1, pY, pZ + 1)]; - llTransX0z = Tile::translucent[level->getTile(pX + 1, pY, pZ - 1)]; - llTransxY0 = Tile::translucent[level->getTile(pX - 1, pY + 1, pZ)]; - llTransxy0 = Tile::translucent[level->getTile(pX - 1, pY - 1, pZ)]; - llTransx0z = Tile::translucent[level->getTile(pX - 1, pY, pZ - 1)]; - llTransx0Z = Tile::translucent[level->getTile(pX - 1, pY, pZ + 1)]; - llTrans0YZ = Tile::translucent[level->getTile(pX, pY + 1, pZ + 1)]; - llTrans0Yz = Tile::translucent[level->getTile(pX, pY + 1, pZ - 1)]; - llTrans0yZ = Tile::translucent[level->getTile(pX, pY - 1, pZ + 1)]; - llTrans0yz = Tile::translucent[level->getTile(pX, pY - 1, pZ - 1)]; - - if (tt->tex == 3) tint0 = tint2 = tint3 = tint4 = tint5 = false; - - if ((noCulling) || (tt->shouldRenderFace(level, pX, pY - 1, pZ, 0))) { - if (blsmooth > 0) { - pY--; - - llxy0 = tt->getBrightness(level, pX - 1, pY, pZ); - ll0yz = tt->getBrightness(level, pX, pY, pZ - 1); - ll0yZ = tt->getBrightness(level, pX, pY, pZ + 1); - llXy0 = tt->getBrightness(level, pX + 1, pY, pZ); - - if (llTrans0yz || llTransxy0) { - llxyz = tt->getBrightness(level, pX - 1, pY, pZ - 1); - } else { - llxyz = llxy0; - } - if (llTrans0yZ || llTransxy0) { - llxyZ = tt->getBrightness(level, pX - 1, pY, pZ + 1); - } else { - llxyZ = llxy0; - } - if (llTrans0yz || llTransXy0) { - llXyz = tt->getBrightness(level, pX + 1, pY, pZ - 1); - } else { - llXyz = llXy0; - } - if (llTrans0yZ || llTransXy0) { - llXyZ = tt->getBrightness(level, pX + 1, pY, pZ + 1); - } else { - llXyZ = llXy0; - } - - pY++; - ll1 = (llxyZ + llxy0 + ll0yZ + ll0y0) / 4.0f; - ll4 = (ll0yZ + ll0y0 + llXyZ + llXy0) / 4.0f; - ll3 = (ll0y0 + ll0yz + llXy0 + llXyz) / 4.0f; - ll2 = (llxy0 + llxyz + ll0y0 + ll0yz) / 4.0f; - } else ll1 = ll2 = ll3 = ll4 = ll0y0; - c1r = c2r = c3r = c4r = (tint0 ? pBaseRed : 1.0f) * 0.5f; - c1g = c2g = c3g = c4g = (tint0 ? pBaseGreen : 1.0f) * 0.5f; - c1b = c2b = c3b = c4b = (tint0 ? pBaseBlue : 1.0f) * 0.5f; - c1r *= ll1; - c1g *= ll1; - c1b *= ll1; - c2r *= ll2; - c2g *= ll2; - c2b *= ll2; - c3r *= ll3; - c3g *= ll3; - c3b *= ll3; - c4r *= ll4; - c4g *= ll4; - c4b *= ll4; - - renderFaceDown(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 0)); - i = true; - } - if ((noCulling) || (tt->shouldRenderFace(level, pX, pY + 1, pZ, 1))) { - if (blsmooth > 0) { - pY++; - - llxY0 = tt->getBrightness(level, pX - 1, pY, pZ); - llXY0 = tt->getBrightness(level, pX + 1, pY, pZ); - ll0Yz = tt->getBrightness(level, pX, pY, pZ - 1); - ll0YZ = tt->getBrightness(level, pX, pY, pZ + 1); - - if (llTrans0Yz || llTransxY0) { - llxYz = tt->getBrightness(level, pX - 1, pY, pZ - 1); - } else { - llxYz = llxY0; - } - if (llTrans0Yz || llTransXY0) { - llXYz = tt->getBrightness(level, pX + 1, pY, pZ - 1); - } else { - llXYz = llXY0; - } - if (llTrans0YZ || llTransxY0) { - llxYZ = tt->getBrightness(level, pX - 1, pY, pZ + 1); - } else { - llxYZ = llxY0; - } - if (llTrans0YZ || llTransXY0) { - llXYZ = tt->getBrightness(level, pX + 1, pY, pZ + 1); - } else { - llXYZ = llXY0; - } - pY--; - - ll4 = (llxYZ + llxY0 + ll0YZ + ll0Y0) / 4.0f; - ll1 = (ll0YZ + ll0Y0 + llXYZ + llXY0) / 4.0f; - ll2 = (ll0Y0 + ll0Yz + llXY0 + llXYz) / 4.0f; - ll3 = (llxY0 + llxYz + ll0Y0 + ll0Yz) / 4.0f; - } else ll1 = ll2 = ll3 = ll4 = ll0Y0; - c1r = c2r = c3r = c4r = (tint1 ? pBaseRed : 1.0f); - c1g = c2g = c3g = c4g = (tint1 ? pBaseGreen : 1.0f); - c1b = c2b = c3b = c4b = (tint1 ? pBaseBlue : 1.0f); - c1r *= ll1; - c1g *= ll1; - c1b *= ll1; - c2r *= ll2; - c2g *= ll2; - c2b *= ll2; - c3r *= ll3; - c3g *= ll3; - c3b *= ll3; - c4r *= ll4; - c4g *= ll4; - c4b *= ll4; - renderFaceUp(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 1)); - i = true; - } - if ((noCulling) || (tt->shouldRenderFace(level, pX, pY, pZ - 1, 2))) { - if (blsmooth > 0) { - pZ--; - llx0z = tt->getBrightness(level, pX - 1, pY, pZ); - ll0yz = tt->getBrightness(level, pX, pY - 1, pZ); - ll0Yz = tt->getBrightness(level, pX, pY + 1, pZ); - llX0z = tt->getBrightness(level, pX + 1, pY, pZ); - - if (llTransx0z || llTrans0yz) { - llxyz = tt->getBrightness(level, pX - 1, pY - 1, pZ); - } else { - llxyz = llx0z; - } - if (llTransx0z || llTrans0Yz) { - llxYz = tt->getBrightness(level, pX - 1, pY + 1, pZ); - } else { - llxYz = llx0z; - } - if (llTransX0z || llTrans0yz) { - llXyz = tt->getBrightness(level, pX + 1, pY - 1, pZ); - } else { - llXyz = llX0z; - } - if (llTransX0z || llTrans0Yz) { - llXYz = tt->getBrightness(level, pX + 1, pY + 1, pZ); - } else { - llXYz = llX0z; - } - pZ++; - ll1 = (llx0z + llxYz + ll00z + ll0Yz) / 4.0f; - ll2 = (ll00z + ll0Yz + llX0z + llXYz) / 4.0f; - ll3 = (ll0yz + ll00z + llXyz + llX0z) / 4.0f; - ll4 = (llxyz + llx0z + ll0yz + ll00z) / 4.0f; - } else ll1 = ll2 = ll3 = ll4 = ll00z; - c1r = c2r = c3r = c4r = (tint2 ? pBaseRed : 1.0f) * 0.8f; - c1g = c2g = c3g = c4g = (tint2 ? pBaseGreen : 1.0f) * 0.8f; - c1b = c2b = c3b = c4b = (tint2 ? pBaseBlue : 1.0f) * 0.8f; - c1r *= ll1; - c1g *= ll1; - c1b *= ll1; - c2r *= ll2; - c2g *= ll2; - c2b *= ll2; - c3r *= ll3; - c3g *= ll3; - c3b *= ll3; - c4r *= ll4; - c4g *= ll4; - c4b *= ll4; - renderNorth(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 2)); - i = true; - } - if ((noCulling) || (tt->shouldRenderFace(level, pX, pY, pZ + 1, 3))) { - if (blsmooth > 0) { - pZ++; - - llx0Z = tt->getBrightness(level, pX - 1, pY, pZ); - llX0Z = tt->getBrightness(level, pX + 1, pY, pZ); - ll0yZ = tt->getBrightness(level, pX, pY - 1, pZ); - ll0YZ = tt->getBrightness(level, pX, pY + 1, pZ); - - if (llTransx0Z || llTrans0yZ) { - llxyZ = tt->getBrightness(level, pX - 1, pY - 1, pZ); - } else { - llxyZ = llx0Z; - } - if (llTransx0Z || llTrans0YZ) { - llxYZ = tt->getBrightness(level, pX - 1, pY + 1, pZ); - } else { - llxYZ = llx0Z; - } - if (llTransX0Z || llTrans0yZ) { - llXyZ = tt->getBrightness(level, pX + 1, pY - 1, pZ); - } else { - llXyZ = llX0Z; - } - if (llTransX0Z || llTrans0YZ) { - llXYZ = tt->getBrightness(level, pX + 1, pY + 1, pZ); - } else { - llXYZ = llX0Z; - } - pZ--; - ll1 = (llx0Z + llxYZ + ll00Z + ll0YZ) / 4.0f; - ll4 = (ll00Z + ll0YZ + llX0Z + llXYZ) / 4.0f; - ll3 = (ll0yZ + ll00Z + llXyZ + llX0Z) / 4.0f; - ll2 = (llxyZ + llx0Z + ll0yZ + ll00Z) / 4.0f; - } else ll1 = ll2 = ll3 = ll4 = ll00Z; - c1r = c2r = c3r = c4r = (tint3 ? pBaseRed : 1.0f) * 0.8f; - c1g = c2g = c3g = c4g = (tint3 ? pBaseGreen : 1.0f) * 0.8f; - c1b = c2b = c3b = c4b = (tint3 ? pBaseBlue : 1.0f) * 0.8f; - c1r *= ll1; - c1g *= ll1; - c1b *= ll1; - c2r *= ll2; - c2g *= ll2; - c2b *= ll2; - c3r *= ll3; - c3g *= ll3; - c3b *= ll3; - c4r *= ll4; - c4g *= ll4; - c4b *= ll4; - renderSouth(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 3)); - i = true; - } - if ((noCulling) || (tt->shouldRenderFace(level, pX - 1, pY, pZ, 4))) { - if (blsmooth > 0) { - pX--; - llxy0 = tt->getBrightness(level, pX, pY - 1, pZ); - llx0z = tt->getBrightness(level, pX, pY, pZ - 1); - llx0Z = tt->getBrightness(level, pX, pY, pZ + 1); - llxY0 = tt->getBrightness(level, pX, pY + 1, pZ); - - if (llTransx0z || llTransxy0) { - llxyz = tt->getBrightness(level, pX, pY - 1, pZ - 1); - } else { - llxyz = llx0z; - } - if (llTransx0Z || llTransxy0) { - llxyZ = tt->getBrightness(level, pX, pY - 1, pZ + 1); - } else { - llxyZ = llx0Z; - } - if (llTransx0z || llTransxY0) { - llxYz = tt->getBrightness(level, pX, pY + 1, pZ - 1); - } else { - llxYz = llx0z; - } - if (llTransx0Z || llTransxY0) { - llxYZ = tt->getBrightness(level, pX, pY + 1, pZ + 1); - } else { - llxYZ = llx0Z; - } - pX++; - ll4 = (llxy0 + llxyZ + llx00 + llx0Z) / 4.0f; - ll1 = (llx00 + llx0Z + llxY0 + llxYZ) / 4.0f; - ll2 = (llx0z + llx00 + llxYz + llxY0) / 4.0f; - ll3 = (llxyz + llxy0 + llx0z + llx00) / 4.0f; - } else ll1 = ll2 = ll3 = ll4 = llx00; - c1r = c2r = c3r = c4r = (tint4 ? pBaseRed : 1.0f) * 0.6f; - c1g = c2g = c3g = c4g = (tint4 ? pBaseGreen : 1.0f) * 0.6f; - c1b = c2b = c3b = c4b = (tint4 ? pBaseBlue : 1.0f) * 0.6f; - c1r *= ll1; - c1g *= ll1; - c1b *= ll1; - c2r *= ll2; - c2g *= ll2; - c2b *= ll2; - c3r *= ll3; - c3g *= ll3; - c3b *= ll3; - c4r *= ll4; - c4g *= ll4; - c4b *= ll4; - renderWest(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 4)); - i = true; - } - if ((noCulling) || (tt->shouldRenderFace(level, pX + 1, pY, pZ, 5))) { - if (blsmooth > 0) { - pX++; - llXy0 = tt->getBrightness(level, pX, pY - 1, pZ); - llX0z = tt->getBrightness(level, pX, pY, pZ - 1); - llX0Z = tt->getBrightness(level, pX, pY, pZ + 1); - llXY0 = tt->getBrightness(level, pX, pY + 1, pZ); - - if (llTransXy0 || llTransX0z) { - llXyz = tt->getBrightness(level, pX, pY - 1, pZ - 1); - } else { - llXyz = llX0z; - } - if (llTransXy0 || llTransX0Z) { - llXyZ = tt->getBrightness(level, pX, pY - 1, pZ + 1); - } else { - llXyZ = llX0Z; - } - if (llTransXY0 || llTransX0z) { - llXYz = tt->getBrightness(level, pX, pY + 1, pZ - 1); - } else { - llXYz = llX0z; - } - if (llTransXY0 || llTransX0Z) { - llXYZ = tt->getBrightness(level, pX, pY + 1, pZ + 1); - } else { - llXYZ = llX0Z; - } - pX--; - ll1 = (llXy0 + llXyZ + llX00 + llX0Z) / 4.0f; - ll4 = (llX00 + llX0Z + llXY0 + llXYZ) / 4.0f; - ll3 = (llX0z + llX00 + llXYz + llXY0) / 4.0f; - ll2 = (llXyz + llXy0 + llX0z + llX00) / 4.0f; - } else ll1 = ll2 = ll3 = ll4 = llX00; - c1r = c2r = c3r = c4r = (tint5 ? pBaseRed : 1.0f) * 0.6f; - c1g = c2g = c3g = c4g = (tint5 ? pBaseGreen : 1.0f) * 0.6f; - c1b = c2b = c3b = c4b = (tint5 ? pBaseBlue : 1.0f) * 0.6f; - c1r *= ll1; - c1g *= ll1; - c1b *= ll1; - c2r *= ll2; - c2g *= ll2; - c2b *= ll2; - c3r *= ll3; - c3g *= ll3; - c3b *= ll3; - c4r *= ll4; - c4g *= ll4; - c4b *= ll4; - - renderEast(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 5)); - i = true; - } - applyAmbienceOcclusion = false; - return i; -} - -bool TileRenderer::tesselateCactusInWorld(Tile* tt, int x, int y, int z) { - int col = tt->getColor(level, x, y, z); - float r = ((col >> 16) & 0xff) / 255.0f; - float g = ((col >> 8) & 0xff) / 255.0f; - float b = ((col) & 0xff) / 255.0f; - return tesselateCactusInWorld(tt, x, y, z, r, g, b); -} - -bool TileRenderer::tesselateCactusInWorld(Tile* tt, int x, int y, int z, float r, float g, float b) { - Tesselator& t = Tesselator::instance; - - bool changed = false; - float c10 = 0.5f; - float c11 = 1; - float c2 = 0.8f; - float c3 = 0.6f; - - float r10 = c10 * r; - float r11 = c11 * r; - float r2 = c2 * r; - float r3 = c3 * r; - - float g10 = c10 * g; - float g11 = c11 * g; - float g2 = c2 * g; - float g3 = c3 * g; - - float b10 = c10 * b; - float b11 = c11 * b; - float b2 = c2 * b; - float b3 = c3 * b; - - float s = 1 / 16.0f; - const float X = (float)x; - const float Y = (float)y; - const float Z = (float)z; - - float centerBrightness = tt->getBrightness(level, x, y, z); - - if (noCulling || tt->shouldRenderFace(level, x, y - 1, z, 0)) { - float br = tt->getBrightness(level, x, y - 1, z); - // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = -// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; - t.color(r10 * br, g10 * br, b10 * br); - renderFaceDown(tt, X, Y, Z, tt->getTexture(level, x, y, z, 0)); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x, y + 1, z, 1)) { - float br = tt->getBrightness(level, x, y + 1, z); - if (tt->yy1 != 1 && !tt->material->isLiquid()) br = centerBrightness; - // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = -// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; - t.color(r11 * br, g11 * br, b11 * br); - renderFaceUp(tt, X, Y, Z, tt->getTexture(level, x, y, z, 1)); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x, y, z - 1, 2)) { - float br = tt->getBrightness(level, x, y, z - 1); - if (tt->zz0 > 0) br = centerBrightness; - // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = -// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; - t.color(r2 * br, g2 * br, b2 * br); - t.addOffset(0, 0, s); - renderNorth(tt, X, Y, Z, tt->getTexture(level, x, y, z, 2)); - t.addOffset(0, 0, -s); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x, y, z + 1, 3)) { - float br = tt->getBrightness(level, x, y, z + 1); - if (tt->zz1 < 1) br = centerBrightness; - // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = -// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; - t.color(r2 * br, g2 * br, b2 * br); - t.addOffset(0, 0, -s); - renderSouth(tt, X, Y, Z, tt->getTexture(level, x, y, z, 3)); - t.addOffset(0, 0, s); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x - 1, y, z, 4)) { - float br = tt->getBrightness(level, x - 1, y, z); - if (tt->xx0 > 0) br = centerBrightness; - // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = -// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; - t.color(r3 * br, g3 * br, b3 * br); - t.addOffset(s, 0, 0); - renderWest(tt, X, Y, Z, tt->getTexture(level, x, y, z, 4)); - t.addOffset(-s, 0, 0); - changed = true; - } - - if (noCulling || tt->shouldRenderFace(level, x + 1, y, z, 5)) { - float br = tt->getBrightness(level, x + 1, y, z); - if (tt->xx1 < 1) br = centerBrightness; - // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = -// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; - t.color(r3 * br, g3 * br, b3 * br); - t.addOffset(-s, 0, 0); - renderEast(tt, X, Y, Z, tt->getTexture(level, x, y, z, 5)); - t.addOffset(s, 0, 0); - changed = true; - } - - return changed; -} - -bool TileRenderer::tesselateFenceInWorld(FenceTile* tt, int x, int y, int z) { - bool changed = true; - - float a = 6 / 16.0f; - float b = 10 / 16.0f; - tt->setShape(a, 0, a, b, 1, b); - tesselateBlockInWorld(tt, x, y, z); - - bool vertical = false; - bool horizontal = false; - - bool l = tt->connectsTo(level, x - 1, y, z); - bool r = tt->connectsTo(level, x + 1, y, z); - bool u = tt->connectsTo(level, x, y, z - 1); - bool d = tt->connectsTo(level, x, y, z + 1); - - if (l || r) vertical = true; - if (u || d) horizontal = true; - - if (!vertical && !horizontal) vertical = true; - - a = 7 / 16.0f; - b = 9 / 16.0f; - float h0 = 12 / 16.0f; - float h1 = 15 / 16.0f; - - float x0 = l ? 0 : a; - float x1 = r ? 1 : b; - float z0 = u ? 0 : a; - float z1 = d ? 1 : b; - - if (vertical) { - tt->setShape(x0, h0, a, x1, h1, b); - tesselateBlockInWorld(tt, x, y, z); - } - if (horizontal) { - tt->setShape(a, h0, z0, b, h1, z1); - tesselateBlockInWorld(tt, x, y, z); - } - - h0 = 6 / 16.0f; - h1 = 9 / 16.0f; - if (vertical) { - tt->setShape(x0, h0, a, x1, h1, b); - tesselateBlockInWorld(tt, x, y, z); - } - if (horizontal) { - tt->setShape(a, h0, z0, b, h1, z1); - tesselateBlockInWorld(tt, x, y, z); - } - - tt->setShape(0, 0, 0, 1, 1, 1); - return changed; -} - -bool TileRenderer::tesselateFenceGateInWorld(FenceGateTile* tt, int x, int y, int z) { - bool changed = true; - - int data = level->getData(x, y, z); - bool isOpen = FenceGateTile::isOpen(data); - int direction = FenceGateTile::getDirection(data); - - const float h00 = 6 / 16.0f; - const float h01 = 9 / 16.0f; - const float h10 = 12 / 16.0f; - const float h11 = 15 / 16.0f; - const float h20 = 5 / 16.0f; - const float h21 = 16 / 16.0f; - - // edge sticks - if (direction == Direction::EAST || direction == Direction::WEST) { - float x0 = 7 / 16.0f; - float x1 = 9 / 16.0f; - float z0 = 0 / 16.0f; - float z1 = 2 / 16.0f; - tt->setShape(x0, h20, z0, x1, h21, z1); - tesselateBlockInWorld(tt, x, y, z); - - z0 = 14 / 16.0f; - z1 = 16 / 16.0f; - tt->setShape(x0, h20, z0, x1, h21, z1); - tesselateBlockInWorld(tt, x, y, z); - } else { - float x0 = 0 / 16.0f; - float x1 = 2 / 16.0f; - float z0 = 7 / 16.0f; - float z1 = 9 / 16.0f; - tt->setShape(x0, h20, z0, x1, h21, z1); - tesselateBlockInWorld(tt, x, y, z); - - x0 = 14 / 16.0f; - x1 = 16 / 16.0f; - tt->setShape(x0, h20, z0, x1, h21, z1); - tesselateBlockInWorld(tt, x, y, z); - } - if (!isOpen) { - if (direction == Direction::EAST || direction == Direction::WEST) { - float x0 = 7 / 16.0f; - float x1 = 9 / 16.0f; - float z0 = 6 / 16.0f; - float z1 = 8 / 16.0f; - tt->setShape(x0, h00, z0, x1, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - z0 = 8 / 16.0f; - z1 = 10 / 16.0f; - tt->setShape(x0, h00, z0, x1, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - z0 = 10 / 16.0f; - z1 = 14 / 16.0f; - tt->setShape(x0, h00, z0, x1, h01, z1); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x0, h10, z0, x1, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - z0 = 2 / 16.0f; - z1 = 6 / 16.0f; - tt->setShape(x0, h00, z0, x1, h01, z1); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x0, h10, z0, x1, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - } else { - float x0 = 6 / 16.0f; - float x1 = 8 / 16.0f; - float z0 = 7 / 16.0f; - float z1 = 9 / 16.0f; - tt->setShape(x0, h00, z0, x1, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - x0 = 8 / 16.0f; - x1 = 10 / 16.0f; - tt->setShape(x0, h00, z0, x1, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - x0 = 10 / 16.0f; - x1 = 14 / 16.0f; - tt->setShape(x0, h00, z0, x1, h01, z1); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x0, h10, z0, x1, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - x0 = 2 / 16.0f; - x1 = 6 / 16.0f; - tt->setShape(x0, h00, z0, x1, h01, z1); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x0, h10, z0, x1, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - - } - } else { - if (direction == Direction::EAST) { - - const float z00 = 0 / 16.0f; - const float z01 = 2 / 16.0f; - const float z10 = 14 / 16.0f; - const float z11 = 16 / 16.0f; - - const float x0 = 9 / 16.0f; - const float x1 = 13 / 16.0f; - const float x2 = 15 / 16.0f; - - tt->setShape(x1, h00, z00, x2, h11, z01); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x1, h00, z10, x2, h11, z11); - tesselateBlockInWorld(tt, x, y, z); - - tt->setShape(x0, h00, z00, x1, h01, z01); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x0, h00, z10, x1, h01, z11); - tesselateBlockInWorld(tt, x, y, z); - - tt->setShape(x0, h10, z00, x1, h11, z01); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x0, h10, z10, x1, h11, z11); - tesselateBlockInWorld(tt, x, y, z); - } else if (direction == Direction::WEST) { - const float z00 = 0 / 16.0f; - const float z01 = 2 / 16.0f; - const float z10 = 14 / 16.0f; - const float z11 = 16 / 16.0f; - - const float x0 = 1 / 16.0f; - const float x1 = 3 / 16.0f; - const float x2 = 7 / 16.0f; - - tt->setShape(x0, h00, z00, x1, h11, z01); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x0, h00, z10, x1, h11, z11); - tesselateBlockInWorld(tt, x, y, z); - - tt->setShape(x1, h00, z00, x2, h01, z01); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x1, h00, z10, x2, h01, z11); - tesselateBlockInWorld(tt, x, y, z); - - tt->setShape(x1, h10, z00, x2, h11, z01); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x1, h10, z10, x2, h11, z11); - tesselateBlockInWorld(tt, x, y, z); - } else if (direction == Direction::SOUTH) { - - const float x00 = 0 / 16.0f; - const float x01 = 2 / 16.0f; - const float x10 = 14 / 16.0f; - const float x11 = 16 / 16.0f; - - const float z0 = 9 / 16.0f; - const float z1 = 13 / 16.0f; - const float z2 = 15 / 16.0f; - - tt->setShape(x00, h00, z1, x01, h11, z2); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x10, h00, z1, x11, h11, z2); - tesselateBlockInWorld(tt, x, y, z); - - tt->setShape(x00, h00, z0, x01, h01, z1); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x10, h00, z0, x11, h01, z1); - tesselateBlockInWorld(tt, x, y, z); - - tt->setShape(x00, h10, z0, x01, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x10, h10, z0, x11, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - } else if (direction == Direction::NORTH) { - const float x00 = 0 / 16.0f; - const float x01 = 2 / 16.0f; - const float x10 = 14 / 16.0f; - const float x11 = 16 / 16.0f; - - const float z0 = 1 / 16.0f; - const float z1 = 3 / 16.0f; - const float z2 = 7 / 16.0f; - - tt->setShape(x00, h00, z0, x01, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x10, h00, z0, x11, h11, z1); - tesselateBlockInWorld(tt, x, y, z); - - tt->setShape(x00, h00, z1, x01, h01, z2); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x10, h00, z1, x11, h01, z2); - tesselateBlockInWorld(tt, x, y, z); - - tt->setShape(x00, h10, z1, x01, h11, z2); - tesselateBlockInWorld(tt, x, y, z); - tt->setShape(x10, h10, z1, x11, h11, z2); - tesselateBlockInWorld(tt, x, y, z); - } - } - - tt->setShape(0, 0, 0, 1, 1, 1); - return changed; -} - -bool TileRenderer::tesselateBedInWorld(Tile *tt, int x, int y, int z) { - Tesselator& t = Tesselator::instance; - int data = level->getData(x, y, z); - int direction = BedTile::getDirection(data); - bool isHead = BedTile::isHeadPiece(data); - - float c10 = 0.5f; - float c11 = 1; - float c2 = 0.8f; - float c3 = 0.6f; - - float r11 = c11; - float g11 = c11; - float b11 = c11; - - float r10 = c10; - float r2 = c2; - float r3 = c3; - - float g10 = c10; - float g2 = c2; - float g3 = c3; - - float b10 = c10; - float b2 = c2; - float b3 = c3; - - float centerBrightness = tt->getBrightness(level, x, y, z); - // render wooden underside - { - t.color(r10 * centerBrightness, g10 * centerBrightness, b10 * centerBrightness); - int tex = tt->getTexture(level, x, y, z, Facing::DOWN); - - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - - float u0 = (xt) / 256.0f; - float u1 = (xt + 16 - 0.01f) / 256.0f; - float v0 = (yt) / 256.0f; - float v1 = (yt + 16 - 0.01f) / 256.0f; - - float x0 = x + tt->xx0; - float x1 = x + tt->xx1; - float y0 = y + tt->yy0 + 3.0f / 16.0f; - float z0 = z + tt->zz0; - float z1 = z + tt->zz1; - - t.vertexUV(x0, y0, z1, u0, v1); - t.vertexUV(x0, y0, z0, u0, v0); - t.vertexUV(x1, y0, z0, u1, v0); - t.vertexUV(x1, y0, z1, u1, v1); - } - - // render bed top - - float brightness = tt->getBrightness(level, x, y + 1, z); - t.color(r11 * brightness, g11 * brightness, b11 * brightness); - - int tex = tt->getTexture(level, x, y, z, Facing::UP); - - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - - float u0 = (xt) / 256.0f; - float u1 = (xt + 16 ) / 256.0f; - float v0 = (yt) / 256.0f; - float v1 = (yt + 16) / 256.0f; - - // Default is west - float topLeftU = u0; - float topRightU = u1; - float topLeftV = v0; - float topRightV = v0; - float bottomLeftU = u0; - float bottomRightU = u1; - float bottomLeftV = v1; - float bottomRightV = v1; - - if (direction == Direction::SOUTH) { - // rotate 90 degrees clockwise - topRightU = u0; - topLeftV = v1; - bottomLeftU = u1; - bottomRightV = v0; - } else if (direction == Direction::NORTH) { - // rotate 90 degrees counter-clockwise - topLeftU = u1; - topRightV = v1; - bottomRightU = u0; - bottomLeftV = v0; - } else if (direction == Direction::EAST) { - // rotate 180 degrees - topLeftU = u1; - topRightV = v1; - bottomRightU = u0; - bottomLeftV = v0; - topRightU = u0; - topLeftV = v1; - bottomLeftU = u1; - bottomRightV = v0; - } - - float x0 = x + tt->xx0; - float x1 = x + tt->xx1; - float y1 = y + tt->yy1; - float z0 = z + tt->zz0; - float z1 = z + tt->zz1; - - t.vertexUV(x1, y1, z1, bottomLeftU, bottomLeftV); - t.vertexUV(x1, y1, z0, topLeftU, topLeftV); - t.vertexUV(x0, y1, z0, topRightU, topRightV); - t.vertexUV(x0, y1, z1, bottomRightU, bottomRightV); - - // determine which edge to skip (the one between foot and head piece) - int skipEdge = Direction::DIRECTION_FACING[direction]; - if (isHead) { - skipEdge = Direction::DIRECTION_FACING[Direction::DIRECTION_OPPOSITE[direction]]; - } - // and which edge to x-flip - int flipEdge = Facing::WEST; - switch (direction) { - case Direction::NORTH: - break; - case Direction::SOUTH: - flipEdge = Facing::EAST; - break; - case Direction::EAST: - flipEdge = Facing::NORTH; - break; - case Direction::WEST: - flipEdge = Facing::SOUTH; - break; - } - - if ((skipEdge != Facing::NORTH) && (noCulling || tt->shouldRenderFace(level, x, y, z - 1, Facing::NORTH))) { - float br = tt->getBrightness(level, x, y, z - 1); - if (tt->zz0 > 0) br = centerBrightness; - - t.color(r2 * br, g2 * br, b2 * br); - xFlipTexture = flipEdge == Facing::NORTH; - renderNorth(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 2)); - } - - if ((skipEdge != Facing::SOUTH) && (noCulling || tt->shouldRenderFace(level, x, y, z + 1, Facing::SOUTH))) { - float br = tt->getBrightness(level, x, y, z + 1); - if (tt->zz1 < 1) br = centerBrightness; - - t.color(r2 * br, g2 * br, b2 * br); - - xFlipTexture = flipEdge == Facing::SOUTH; - renderSouth(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 3)); - } - - if ((skipEdge != Facing::WEST) && (noCulling || tt->shouldRenderFace(level, x - 1, y, z, Facing::WEST))) { - float br = tt->getBrightness(level, x - 1, y, z); - if (tt->xx0 > 0) br = centerBrightness; - - t.color(r3 * br, g3 * br, b3 * br); - xFlipTexture = flipEdge == Facing::WEST; - renderWest(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 4)); - } - - if ((skipEdge != Facing::EAST) && (noCulling || tt->shouldRenderFace(level, x + 1, y, z, Facing::EAST))) { - float br = tt->getBrightness(level, x + 1, y, z); - if (tt->xx1 < 1) br = centerBrightness; - - t.color(r3 * br, g3 * br, b3 * br); - xFlipTexture = flipEdge == Facing::EAST; - renderEast(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 5)); - } - xFlipTexture = false; - return true; -} - -bool TileRenderer::tesselateStairsInWorld( StairTile* tt, int x, int y, int z ) -{ - - tt->setBaseShape(level, x, y, z); - tesselateBlockInWorld(tt, x, y, z); - - bool checkInnerPiece = tt->setStepShape(level, x, y, z); - tesselateBlockInWorld(tt, x, y, z); - - if (checkInnerPiece) { - if (tt->setInnerPieceShape(level, x, y, z)) { - tesselateBlockInWorld(tt, x, y, z); - } - } - - // setShape(0, 0, 0, 1, 1, 1); - return true; -} - -bool TileRenderer::tesselateDoorInWorld( Tile* tt, int x, int y, int z ) -{ - Tesselator& t = Tesselator::instance; - - DoorTile* dt = (DoorTile*) tt; - - bool changed = false; - float c10 = 0.5f; - float c11 = 1; - float c2 = 0.8f; - float c3 = 0.6f; - - float centerBrightness = tt->getBrightness(level, x, y, z); - - { - float br = tt->getBrightness(level, x, y - 1, z); - if (dt->yy0 > 0) br = centerBrightness; - if (Tile::lightEmission[tt->id] > 0) br = 1.0f; - t.color(c10 * br, c10 * br, c10 * br); - renderFaceDown(tt, (float)x, (float)y, (float)z, tt->getTexture(level, x, y, z, 0)); - changed = true; - } - - { - float br = tt->getBrightness(level, x, y + 1, z); - if (dt->yy1 < 1) br = centerBrightness; - if (Tile::lightEmission[tt->id] > 0) br = 1.0f; - t.color(c11 * br, c11 * br, c11 * br); - renderFaceUp(tt, (float)x, (float)y, (float)z, tt->getTexture(level, x, y, z, 1)); - changed = true; - } - - { - float br = tt->getBrightness(level, x, y, z - 1); - if (dt->zz0 > 0) br = centerBrightness; - if (Tile::lightEmission[tt->id] > 0) br = 1.0f; - t.color(c2 * br, c2 * br, c2 * br); - int tex = tt->getTexture(level, x, y, z, 2); - if (tex < 0) { - xFlipTexture = true; - tex = -tex; - } - renderNorth(tt, (float)x, (float)y, (float)z, tex); - changed = true; - xFlipTexture = false; - } - - { - float br = tt->getBrightness(level, x, y, z + 1); - if (dt->zz1 < 1) br = centerBrightness; - if (Tile::lightEmission[tt->id] > 0) br = 1.0f; - t.color(c2 * br, c2 * br, c2 * br); - int tex = tt->getTexture(level, x, y, z, 3); - if (tex < 0) { - xFlipTexture = true; - tex = -tex; - } - renderSouth(tt, (float)x, (float)y, (float)z, tex); - changed = true; - xFlipTexture = false; - } - - { - float br = tt->getBrightness(level, x - 1, y, z); - if (dt->xx0 > 0) br = centerBrightness; - if (Tile::lightEmission[tt->id] > 0) br = 1.0f; - t.color(c3 * br, c3 * br, c3 * br); - int tex = tt->getTexture(level, x, y, z, 4); - if (tex < 0) { - xFlipTexture = true; - tex = -tex; - } - renderWest(tt, (float)x, (float)y, (float)z, tex); - changed = true; - xFlipTexture = false; - } - - { - float br = tt->getBrightness(level, x + 1, y, z); - if (dt->xx1 < 1) br = centerBrightness; - if (Tile::lightEmission[tt->id] > 0) br = 1.0f; - t.color(c3 * br, c3 * br, c3 * br); - int tex = tt->getTexture(level, x, y, z, 5); - if (tex < 0) { - xFlipTexture = true; - tex = -tex; - } - renderEast(tt, (float)x, (float)y, (float)z, tex); - changed = true; - xFlipTexture = false; - } - - return changed; -} - -bool TileRenderer::tesselateRowInWorld( Tile* tt, int x, int y, int z ) { - Tesselator& t = Tesselator::instance; - float br = tt->getBrightness(level, x, y, z); - t.color(br, br, br); - tesselateRowTexture(tt, level->getData(x, y, z), float(x), y - 1 / 16.0f, float(z)); - return true; -} - -void TileRenderer::renderFaceDown( Tile* tt, float x, float y, float z, int tex ) -{ - Tesselator& t = Tesselator::instance; - - if (fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - - float u0 = (xt + tt->xx0 * 16) / 256.0f; - float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f; - float v0 = (yt + tt->zz0 * 16) / 256.0f; - float v1 = (yt + tt->zz1 * 16 - 0.01f) / 256.0f; - - if (tt->xx0 < 0 || tt->xx1 > 1) { - u0 = (xt + 0 * 15.99f) / 256.0f; - u1 = (xt + 1 * 15.99f) / 256.0f; - } - if (tt->zz0 < 0 || tt->zz1 > 1) { - v0 = (yt + 0 * 15.99f) / 256.0f; - v1 = (yt + 1 * 15.99f) / 256.0f; - } - - float x0 = x + tt->xx0; - float x1 = x + tt->xx1; - float y0 = y + tt->yy0; - float z0 = z + tt->zz0; - float z1 = z + tt->zz1; - - if (applyAmbienceOcclusion) { - t.color(c1r, c1g, c1b); - t.vertexUV(x0, y0, z1, u0, v1); - t.color(c2r, c2g, c2b); - t.vertexUV(x0, y0, z0, u0, v0); - t.color(c3r, c3g, c3b); - t.vertexUV(x1, y0, z0, u1, v0); - t.color(c4r, c4g, c4b); - t.vertexUV(x1, y0, z1, u1, v1); - } else { - t.vertexUV(x0, y0, z1, u0, v1); - t.vertexUV(x0, y0, z0, u0, v0); - t.vertexUV(x1, y0, z0, u1, v0); - t.vertexUV(x1, y0, z1, u1, v1); - } -} - -void TileRenderer::renderFaceUp( Tile* tt, float x, float y, float z, int tex ) -{ - Tesselator& t = Tesselator::instance; - - if (fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - - float u0 = (xt + tt->xx0 * 16) / 256.0f; - float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f; - float v0 = (yt + tt->zz0 * 16) / 256.0f; - float v1 = (yt + tt->zz1 * 16 - 0.01f) / 256.0f; - - if (tt->xx0 < 0 || tt->xx1 > 1) { - u0 = (xt + 0 * 15.99f) / 256.0f; - u1 = (xt + 1 * 15.99f) / 256.0f; - } - if (tt->zz0 < 0 || tt->zz1 > 1) { - v0 = (yt + 0 * 15.99f) / 256.0f; - v1 = (yt + 1 * 15.99f) / 256.0f; - } - - float x0 = x + tt->xx0; - float x1 = x + tt->xx1; - float y1 = y + tt->yy1; - float z0 = z + tt->zz0; - float z1 = z + tt->zz1; - - if (applyAmbienceOcclusion) { - t.color(c1r, c1g, c1b); - t.vertexUV(x1, y1, z1, u1, v1); - t.color(c2r, c2g, c2b); - t.vertexUV(x1, y1, z0, u1, v0); - t.color(c3r, c3g, c3b); - t.vertexUV(x0, y1, z0, u0, v0); - t.color(c4r, c4g, c4b); - t.vertexUV(x0, y1, z1, u0, v1); - } else { - t.vertexUV(x1, y1, z1, u1, v1); - t.vertexUV(x1, y1, z0, u1, v0); - t.vertexUV(x0, y1, z0, u0, v0); - t.vertexUV(x0, y1, z1, u0, v1); - } -} - -void TileRenderer::renderNorth( Tile* tt, float x, float y, float z, int tex ) -{ - Tesselator& t = Tesselator::instance; - - if (fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - float u0 = (xt + tt->xx0 * 16) / 256.0f; - float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f; - float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f; - float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f; - if (xFlipTexture) { - float tmp = u0; - u0 = u1; - u1 = tmp; - } - - if (tt->xx0 < 0 || tt->xx1 > 1) { - u0 = (xt + 0 * 15.99f) / 256.0f; - u1 = (xt + 1 * 15.99f) / 256.0f; - } - if (tt->yy0 < 0 || tt->yy1 > 1) { - v0 = (yt + 0 * 15.99f) / 256.0f; - v1 = (yt + 1 * 15.99f) / 256.0f; - } - - float x0 = x + tt->xx0; - float x1 = x + tt->xx1; - float y0 = y + tt->yy0; - float y1 = y + tt->yy1; - float z0 = z + tt->zz0; - - if (applyAmbienceOcclusion) { - t.color(c1r, c1g, c1b); - t.vertexUV(x0, y1, z0, u1, v0); - t.color(c2r, c2g, c2b); - t.vertexUV(x1, y1, z0, u0, v0); - t.color(c3r, c3g, c3b); - t.vertexUV(x1, y0, z0, u0, v1); - t.color(c4r, c4g, c4b); - t.vertexUV(x0, y0, z0, u1, v1); - } else { - t.vertexUV(x0, y1, z0, u1, v0); - t.vertexUV(x1, y1, z0, u0, v0); - t.vertexUV(x1, y0, z0, u0, v1); - t.vertexUV(x0, y0, z0, u1, v1); - } -} - -void TileRenderer::renderSouth( Tile* tt, float x, float y, float z, int tex ) -{ - Tesselator& t = Tesselator::instance; - - if (fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - - float u0 = (xt + tt->xx0 * 16) / 256.0f; - float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f; - float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f; - float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f; - if (xFlipTexture) { - float tmp = u0; - u0 = u1; - u1 = tmp; - } - - if (tt->xx0 < 0 || tt->xx1 > 1) { - u0 = (xt + 0 * 15.99f) / 256.0f; - u1 = (xt + 1 * 15.99f) / 256.0f; - } - if (tt->yy0 < 0 || tt->yy1 > 1) { - v0 = (yt + 0 * 15.99f) / 256.0f; - v1 = (yt + 1 * 15.99f) / 256.0f; - } - - float x0 = x + tt->xx0; - float x1 = x + tt->xx1; - float y0 = y + tt->yy0; - float y1 = y + tt->yy1; - float z1 = z + tt->zz1; - - if (applyAmbienceOcclusion) { - t.color(c1r, c1g, c1b); - t.vertexUV(x0, y1, z1, u0, v0); - t.color(c2r, c2g, c2b); - t.vertexUV(x0, y0, z1, u0, v1); - t.color(c3r, c3g, c3b); - t.vertexUV(x1, y0, z1, u1, v1); - t.color(c4r, c4g, c4b); - t.vertexUV(x1, y1, z1, u1, v0); - } else { - t.vertexUV(x0, y1, z1, u0, v0); - t.vertexUV(x0, y0, z1, u0, v1); - t.vertexUV(x1, y0, z1, u1, v1); - t.vertexUV(x1, y1, z1, u1, v0); - } -} - -void TileRenderer::renderWest( Tile* tt, float x, float y, float z, int tex ) -{ - Tesselator& t = Tesselator::instance; - - if (fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - - float u0 = (xt + tt->zz0 * 16) / 256.0f; - float u1 = (xt + tt->zz1 * 16 - 0.01f) / 256.0f; - float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f; - float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f; - if (xFlipTexture) { - float tmp = u0; - u0 = u1; - u1 = tmp; - } - - if (tt->zz0 < 0 || tt->zz1 > 1) { - u0 = (xt + 0 * 15.99f) / 256.0f; - u1 = (xt + 1 * 15.99f) / 256.0f; - } - if (tt->yy0 < 0 || tt->yy1 > 1) { - v0 = (yt + 0 * 15.99f) / 256.0f; - v1 = (yt + 1 * 15.99f) / 256.0f; - } - - float x0 = x + tt->xx0; - float y0 = y + tt->yy0; - float y1 = y + tt->yy1; - float z0 = z + tt->zz0; - float z1 = z + tt->zz1; - - if (applyAmbienceOcclusion) { - t.color(c1r, c1g, c1b); - t.vertexUV(x0, y1, z1, u1, v0); - t.color(c2r, c2g, c2b); - t.vertexUV(x0, y1, z0, u0, v0); - t.color(c3r, c3g, c3b); - t.vertexUV(x0, y0, z0, u0, v1); - t.color(c4r, c4g, c4b); - t.vertexUV(x0, y0, z1, u1, v1); - } else { - t.vertexUV(x0, y1, z1, u1, v0); - t.vertexUV(x0, y1, z0, u0, v0); - t.vertexUV(x0, y0, z0, u0, v1); - t.vertexUV(x0, y0, z1, u1, v1); - } -} - -void TileRenderer::renderEast( Tile* tt, float x, float y, float z, int tex ) -{ - Tesselator& t = Tesselator::instance; - - if (fixedTexture >= 0) tex = fixedTexture; - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - - float u0 = (xt + tt->zz0 * 16) / 256.0f; - float u1 = (xt + tt->zz1 * 16 - 0.01f) / 256.0f; - float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f; - float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f; - if (xFlipTexture) { - float tmp = u0; - u0 = u1; - u1 = tmp; - } - - if (tt->zz0 < 0 || tt->zz1 > 1) { - u0 = (xt + 0 * 15.99f) / 256.0f; - u1 = (xt + 1 * 15.99f) / 256.0f; - } - if (tt->yy0 < 0 || tt->yy1 > 1) { - v0 = (yt + 0 * 15.99f) / 256.0f; - v1 = (yt + 1 * 15.99f) / 256.0f; - } - - float x1 = x + tt->xx1; - float y0 = y + tt->yy0; - float y1 = y + tt->yy1; - float z0 = z + tt->zz0; - float z1 = z + tt->zz1; - - if (applyAmbienceOcclusion) { - t.color(c1r, c1g, c1b); - t.vertexUV(x1, y0, z1, u0, v1); - t.color(c2r, c2g, c2b); - t.vertexUV(x1, y0, z0, u1, v1); - t.color(c3r, c3g, c3b); - t.vertexUV(x1, y1, z0, u1, v0); - t.color(c4r, c4g, c4b); - t.vertexUV(x1, y1, z1, u0, v0); - } else { - t.vertexUV(x1, y0, z1, u0, v1); - t.vertexUV(x1, y0, z0, u1, v1); - t.vertexUV(x1, y1, z0, u1, v0); - t.vertexUV(x1, y1, z1, u0, v0); - } -} - -void TileRenderer::renderTile( Tile* tile, int data ) -{ - Tesselator& t = Tesselator::instance; - - t.color(0xff, 0xff, 0xff); - int shape = tile->getRenderShape(); - - if (shape == Tile::SHAPE_BLOCK) { - tile->updateDefaultShape(); - t.addOffset(-0.5f, -0.5f, -0.5f); - t.begin(); - renderFaceDown(tile, 0, 0, 0, tile->getTexture(0, data)); - renderFaceUp(tile, 0, 0, 0, tile->getTexture(1, data)); - renderNorth(tile, 0, 0, 0, tile->getTexture(2, data)); - renderSouth(tile, 0, 0, 0, tile->getTexture(3, data)); - renderWest(tile, 0, 0, 0, tile->getTexture(4, data)); - renderEast(tile, 0, 0, 0, tile->getTexture(5, data)); - t.draw(); - - t.addOffset(0.5f, 0.5f, 0.5f); - - } else if (shape == Tile::SHAPE_CROSS_TEXTURE) { - t.begin(); - tesselateCrossTexture(tile, data, -0.5f, -0.5f, -0.5f); - t.draw(); - } else if(shape == Tile::SHAPE_STEM) { - t.begin(); - tile->updateDefaultShape(); - tesselateStemTexture(tile, data, tile->yy1, -0.5f, -0.5f, -0.5f); - t.draw(); - } else if (shape == Tile::SHAPE_CACTUS) { - tile->updateDefaultShape(); - t.offset(-0.5f, -0.5f, -0.5f); - float s = 1 / 16.0f; - t.begin(); - renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); - renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); - t.addOffset(0, 0, s); - renderNorth(tile, 0, 0, 0, tile->getTexture(2)); - t.addOffset(0, 0, -s); - t.addOffset(0, 0, -s); - renderSouth(tile, 0, 0, 0, tile->getTexture(3)); - t.addOffset(0, 0, s); - t.addOffset(s, 0, 0); - renderWest(tile, 0, 0, 0, tile->getTexture(4)); - t.addOffset(-s, 0, 0); - t.addOffset(-s, 0, 0); - renderEast(tile, 0, 0, 0, tile->getTexture(5)); - t.addOffset(s, 0, 0); - t.draw(); - t.offset(0, 0, 0);//0.5f, 0.5f, 0.5f); - } else if (shape == Tile::SHAPE_ROWS) { - t.begin(); - t.normal(0, -1, 0); - tesselateRowTexture(tile, data, -0.5f, -0.5f, -0.5f); - //} else if (shape == Tile::SHAPE_TORCH) { - //// t.begin(); - //// t.normal(0, -1, 0); - //// tesselateTorch(tile, -0.5f, -0.5f, -0.5f, 0, 0); - //// t.end(); - } else if (shape == Tile::SHAPE_ENTITYTILE_ANIMATED) { - EntityTileRenderer::instance->render(tile, data, 1.0f); - //glEnable(GL_RESCALE_NORMAL); - } else if (shape == Tile::SHAPE_STAIRS) { - t.addOffset(-0.5f, -0.5f, -0.5f); - t.begin(); - for (int i = 0; i < 2; i++) { - if (i == 0) tile->setShape(0, 0, 0, 1, 1, 0.5f); - if (i == 1) tile->setShape(0, 0, 0.5f, 1, 0.5f, 1); - - renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); - renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); - renderNorth(tile, 0, 0, 0, tile->getTexture(2)); - renderSouth(tile, 0, 0, 0, tile->getTexture(3)); - renderWest(tile, 0, 0, 0, tile->getTexture(4)); - renderEast(tile, 0, 0, 0, tile->getTexture(5)); - } - t.draw(); - t.addOffset(0.5f, 0.5f, 0.5f); - } - else if (shape == Tile::SHAPE_FENCE) { - t.addOffset(-0.5f, -0.5f, -0.5f); - t.begin(); - for (int i = 0; i < 4; i++) { - float w = 2 / 16.0f; - if (i == 0) tile->setShape(0.5f - w, 0, 0, 0.5f + w, 1, w * 2); - if (i == 1) tile->setShape(0.5f - w, 0, 1 - w * 2, 0.5f + w, 1, 1); - w = 1 / 16.0f; - if (i == 2) tile->setShape(0.5f - w, 1 - w * 3, -w * 2, 0.5f + w, 1 - w, 1 + w * 2); - if (i == 3) tile->setShape(0.5f - w, 0.5f - w * 3, -w * 2, 0.5f + w, 0.5f - w, 1 + w * 2); - - renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); - renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); - renderNorth(tile, 0, 0, 0, tile->getTexture(2)); - renderSouth(tile, 0, 0, 0, tile->getTexture(3)); - renderWest(tile, 0, 0, 0, tile->getTexture(4)); - renderEast(tile, 0, 0, 0, tile->getTexture(5)); - } - t.draw(); - t.addOffset(0.5f, 0.5f, 0.5f); - tile->setShape(0, 0, 0, 1, 1, 1); - } else if (shape == Tile::SHAPE_FENCE_GATE) { - t.addOffset(-0.5f, -0.5f, -0.5f); - t.begin(); - for (int i = 0; i < 3; i++) { - float w = 1 / 16.0f; - if (i == 0) tile->setShape(0.5f - w, .3f, 0, 0.5f + w, 1, w * 2); - if (i == 1) tile->setShape(0.5f - w, .3f, 1 - w * 2, 0.5f + w, 1, 1); - if (i == 2) tile->setShape(0.5f - w, .5f, w * 2, 0.5f + w, 1 - w, 1 - w * 2); - - renderFaceUp(tile, 0, 0, 0, tile->getTexture(0)); - renderFaceDown(tile, 0, 0, 0, tile->getTexture(1)); - renderNorth(tile, 0, 0, 0, tile->getTexture(2)); - renderSouth(tile, 0, 0, 0, tile->getTexture(3)); - renderWest(tile, 0, 0, 0, tile->getTexture(4)); - renderEast(tile, 0, 0, 0, tile->getTexture(5)); - } - t.draw(); - t.addOffset(0.5f, 0.5f, 0.5f); - tile->setShape(0, 0, 0, 1, 1, 1); - } - -} - -bool TileRenderer::canRender( int renderShape ) -{ - if (renderShape == Tile::SHAPE_BLOCK) return true; - if (renderShape == Tile::SHAPE_CACTUS) return true; - if (renderShape == Tile::SHAPE_STAIRS) return true; - if (renderShape == Tile::SHAPE_FENCE) return true; - if (renderShape == Tile::SHAPE_FENCE_GATE) return true; - //if (renderShape == Tile::SHAPE_CROSS_TEXTURE) return true; - //if (renderShape == Tile::SHAPE_ENTITYTILE_ANIMATED) return true; - - return false; -} - -void TileRenderer::renderGuiTile( Tile* tile, int data ) -{ - Tesselator& t = Tesselator::instance; - - int shape = tile->getRenderShape(); - - if (shape == Tile::SHAPE_BLOCK) { - tile->updateDefaultShape(); - - t.begin(); - t.addOffset(-0.5f, -0.5f, -0.5f); - t.color(0xff, 0xff, 0xff); - renderFaceUp(tile, 0, 0, 0, tile->getTexture(1, data)); - renderFaceDown(tile, 0, 0, 0, tile->getTexture(0, data)); - - t.color(0x80, 0x80, 0x80); - renderNorth(tile, 0, 0, 0, tile->getTexture(2, data)); - renderSouth(tile, 0, 0, 0, tile->getTexture(3, data)); - - t.color(0xbb, 0xbb, 0xbb); - renderEast(tile, 0, 0, 0, tile->getTexture(5, data)); - renderWest(tile, 0, 0, 0, tile->getTexture(4, data)); - t.draw(); - t.addOffset(0.5f, 0.5f, 0.5f); - - } else if (shape == Tile::SHAPE_CROSS_TEXTURE) { - t.begin(); - //t.normal(0, -1, 0); - tesselateCrossTexture(tile, data, -0.5f, -0.5f, -0.5f); - //t.end(); - t.draw(); - } else if (shape == Tile::SHAPE_CACTUS) { - tile->updateDefaultShape(); - t.begin(); - t.offset(-0.5f, -0.5f, -0.5f); - float s = 1 / 16.0f; - t.color(0xff, 0xff, 0xff); - renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); - renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); - - t.color(0x80, 0x80, 0x80); - t.addOffset(0, 0, s); - renderNorth(tile, 0, 0, 0, tile->getTexture(2)); - t.addOffset(0, 0, -s-s); - renderSouth(tile, 0, 0, 0, tile->getTexture(3)); - - t.color(0xbb, 0xbb, 0xbb); - t.addOffset(s, 0, s); - renderWest(tile, 0, 0, 0, tile->getTexture(4)); - t.addOffset(-s-s, 0, 0); - renderEast(tile, 0, 0, 0, tile->getTexture(5)); - - t.draw(); - t.addOffset(s+0.5f, 0.5f, 0.5f); - } else if (shape == Tile::SHAPE_STAIRS) { - t.offset(-0.5f, -0.5f, -0.5f); - t.begin(); - for (int i = 0; i < 2; i++) { - if (i == 0) tile->setShape(0, 0, 0, 1, 1, 0.5f); - if (i == 1) tile->setShape(0, 0, 0.5f, 1, 0.5f, 1); - - t.color(0xff, 0xff, 0xff); - renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); - renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); - - t.color(0x80, 0x80, 0x80); - renderNorth(tile, 0, 0, 0, tile->getTexture(2)); - renderSouth(tile, 0, 0, 0, tile->getTexture(3)); - - t.color(0xbb, 0xbb, 0xbb); - renderWest(tile, 0, 0, 0, tile->getTexture(4)); - renderEast(tile, 0, 0, 0, tile->getTexture(5)); - } - t.draw(); - t.offset(0, 0, 0); - } - else if (shape == Tile::SHAPE_FENCE) { - t.addOffset(-0.5f, -0.5f, -0.5f); - t.begin(); - for (int i = 0; i < 4; i++) { - float w = 2 / 16.0f; - if (i == 0) tile->setShape(0.5f - w, 0, 0, 0.5f + w, 1, w * 2); - if (i == 1) tile->setShape(0.5f - w, 0, 1 - w * 2, 0.5f + w, 1, 1); - w = 1 / 16.0f; - if (i == 2) tile->setShape(0.5f - w, 1 - w * 3, -w * 2, 0.5f + w, 1 - w, 1 + w * 2); - if (i == 3) tile->setShape(0.5f - w, 0.5f - w * 3, -w * 2, 0.5f + w, 0.5f - w, 1 + w * 2); - - t.color(0xff, 0xff, 0xff); - - renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); - renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); - - t.color(0x80, 0x80, 0x80); - renderNorth(tile, 0, 0, 0, tile->getTexture(2)); - renderSouth(tile, 0, 0, 0, tile->getTexture(3)); - - t.color(0xbb, 0xbb, 0xbb); - renderWest(tile, 0, 0, 0, tile->getTexture(4)); - renderEast(tile, 0, 0, 0, tile->getTexture(5)); - } - t.draw(); - t.addOffset(0.5f, 0.5f, 0.5f); - tile->setShape(0, 0, 0, 1, 1, 1); - } - else if (shape == Tile::SHAPE_FENCE_GATE) { - t.addOffset(-0.5f, -0.5f, -0.5f); - t.begin(); - for (int i = 0; i < 3; i++) { - float w = 1 / 16.0f; - if (i == 0) tile->setShape(0.5f - w, .3f, 0, 0.5f + w, 1, w * 2); - if (i == 1) tile->setShape(0.5f - w, .3f, 1 - w * 2, 0.5f + w, 1, 1); - w = 1 / 16.0f; - if (i == 2) tile->setShape(0.5f - w, .5f, 0, 0.5f + w, 1 - w, 1); - - t.color(0xff, 0xff, 0xff); - renderFaceUp(tile, 0, 0, 0, tile->getTexture(0)); - renderFaceDown(tile, 0, 0, 0, tile->getTexture(1)); - - t.color(0x80, 0x80, 0x80); - renderNorth(tile, 0, 0, 0, tile->getTexture(2)); - renderSouth(tile, 0, 0, 0, tile->getTexture(3)); - - t.color(0xbb, 0xbb, 0xbb); - renderWest(tile, 0, 0, 0, tile->getTexture(4)); - renderEast(tile, 0, 0, 0, tile->getTexture(5)); - } - t.draw(); - tile->setShape(0, 0, 0, 1, 1, 1); - t.addOffset(0.5f, 0.5f, 0.5f); - } -} - -bool TileRenderer::tesselateThinFenceInWorld(ThinFenceTile* tt, int x, int y, int z) { - const int depth = 128; - Tesselator& t = Tesselator::instance; - - float br = tt->getBrightness(level, x, y, z); - int col = tt->getColor(level, x, y, z); - float r = ((col >> 16) & 0xff) / 255.0f; - float g = ((col >> 8) & 0xff) / 255.0f; - float b = ((col) & 0xff) / 255.0f; - - //if (GameRenderer::anaglyph3d) { - // float cr = (r * 30 + g * 59 + b * 11) / 100; - // float cg = (r * 30 + g * 70) / (100); - // float cb = (r * 30 + b * 70) / (100); - - // r = cr; - // g = cg; - // b = cb; - //} - t.color(br * r, br * g, br * b); - - int tex = 0; - int edgeTex = 0; - - if (fixedTexture >= 0) { - tex = fixedTexture; - edgeTex = fixedTexture; - } else { - int data = level->getData(x, y, z); - tex = tt->getTexture(0, data); - edgeTex = tt->getEdgeTexture(); - } - - const int xt = (tex & 0xf) << 4; - const int yt = tex & 0xf0; - float u0 = (xt) / 256.0f; - const float u1 = (xt + 7.99f) / 256.0f; - const float u2 = (xt + 15.99f) / 256.0f; - const float v0 = (yt) / 256.0f; - const float v2 = (yt + 15.99f) / 256.0f; - - const int xet = (edgeTex & 0xf) << 4; - const int yet = edgeTex & 0xf0; - - const float iu0 = (xet + 7) / 256.0f; - const float iu1 = (xet + 8.99f) / 256.0f; - const float iv0 = (yet) / 256.0f; - const float iv1 = (yet + 8) / 256.0f; - const float iv2 = (yet + 15.99f) / 256.0f; - - const float x0 = (float)x; - const float x1 = x0 + .5f; - const float x2 = x0 + 1; - const float y0 = (float)y + 0.001f; - const float y1 = y0 + 1 - 0.002f; - const float z0 = (float)z; - const float z1 = z0 + .5f; - const float z2 = z0 + 1; - const float ix0 = x0 + .5f - 1.0f / 16.0f; - const float ix1 = x0 + .5f + 1.0f / 16.0f; - const float iz0 = z0 + .5f - 1.0f / 16.0f; - const float iz1 = z0 + .5f + 1.0f / 16.0f; - - const bool n = tt->attachsTo(level->getTile(x, y, z - 1)); - const bool s = tt->attachsTo(level->getTile(x, y, z + 1)); - const bool w = tt->attachsTo(level->getTile(x - 1, y, z)); - const bool e = tt->attachsTo(level->getTile(x + 1, y, z)); - - const bool up = tt->shouldRenderFace(level, x, y + 1, z, Facing::UP); - const bool down = tt->shouldRenderFace(level, x, y - 1, z, Facing::DOWN); - - const float noZFightingOffset = 0.01f; - - if ((w && e) || (!w && !e && !n && !s)) { - t.vertexUV(x0, y1, z1, u0, v0); - t.vertexUV(x0, y0, z1, u0, v2); - t.vertexUV(x2, y0, z1, u2, v2); - t.vertexUV(x2, y1, z1, u2, v0); - - t.vertexUV(x2, y1, z1, u0, v0); - t.vertexUV(x2, y0, z1, u0, v2); - t.vertexUV(x0, y0, z1, u2, v2); - t.vertexUV(x0, y1, z1, u2, v0); - - if (up) { - // small edge texture - t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv0); - t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv2); - - t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv0); - t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv2); - } else { - if (y < (depth - 1) && level->isEmptyTile(x - 1, y + 1, z)) { - t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv2); - t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv1); - - t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv2); - t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1); - } - if (y < (depth - 1) && level->isEmptyTile(x + 1, y + 1, z)) { - t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv1); - t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv0); - - t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1); - t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv0); - } - } - if (down) { - // small edge texture - t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv0); - t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv2); - - t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv0); - t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv2); - } else { - if (y > 1 && level->isEmptyTile(x - 1, y - 1, z)) { - t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv2); - t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv1); - - t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv2); - t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1); - } - if (y > 1 && level->isEmptyTile(x + 1, y - 1, z)) { - t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv1); - t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv0); - - t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1); - t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv0); - } - } - - } else if (w && !e) { - // half-step towards west - t.vertexUV(x0, y1, z1, u0, v0); - t.vertexUV(x0, y0, z1, u0, v2); - t.vertexUV(x1, y0, z1, u1, v2); - t.vertexUV(x1, y1, z1, u1, v0); - - t.vertexUV(x1, y1, z1, u0, v0); - t.vertexUV(x1, y0, z1, u0, v2); - t.vertexUV(x0, y0, z1, u1, v2); - t.vertexUV(x0, y1, z1, u1, v0); - - // small edge texture - if (!s && !n) { - t.vertexUV(x1, y1, iz1, iu0, iv0); - t.vertexUV(x1, y0, iz1, iu0, iv2); - t.vertexUV(x1, y0, iz0, iu1, iv2); - t.vertexUV(x1, y1, iz0, iu1, iv0); - - t.vertexUV(x1, y1, iz0, iu0, iv0); - t.vertexUV(x1, y0, iz0, iu0, iv2); - t.vertexUV(x1, y0, iz1, iu1, iv2); - t.vertexUV(x1, y1, iz1, iu1, iv0); - } - - if (up || (y < (depth - 1) && level->isEmptyTile(x - 1, y + 1, z))) { - // small edge texture - t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv2); - t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv1); - - t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv2); - t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1); - } - if (down || (y > 1 && level->isEmptyTile(x - 1, y - 1, z))) { - // small edge texture - t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv2); - t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv1); - - t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv2); - t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv2); - t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1); - } - - } else if (!w && e) { - // half-step towards east - t.vertexUV(x1, y1, z1, u1, v0); - t.vertexUV(x1, y0, z1, u1, v2); - t.vertexUV(x2, y0, z1, u2, v2); - t.vertexUV(x2, y1, z1, u2, v0); - - t.vertexUV(x2, y1, z1, u1, v0); - t.vertexUV(x2, y0, z1, u1, v2); - t.vertexUV(x1, y0, z1, u2, v2); - t.vertexUV(x1, y1, z1, u2, v0); - - // small edge texture - if (!s && !n) { - t.vertexUV(x1, y1, iz0, iu0, iv0); - t.vertexUV(x1, y0, iz0, iu0, iv2); - t.vertexUV(x1, y0, iz1, iu1, iv2); - t.vertexUV(x1, y1, iz1, iu1, iv0); - - t.vertexUV(x1, y1, iz1, iu0, iv0); - t.vertexUV(x1, y0, iz1, iu0, iv2); - t.vertexUV(x1, y0, iz0, iu1, iv2); - t.vertexUV(x1, y1, iz0, iu1, iv0); - } - - if (up || (y < (depth - 1) && level->isEmptyTile(x + 1, y + 1, z))) { - // small edge texture - t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv1); - t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv0); - - t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1); - t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv0); - } - if (down || (y > 1 && level->isEmptyTile(x + 1, y - 1, z))) { - // small edge texture - t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv1); - t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv0); - - t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv0); - t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1); - t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1); - t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv0); - } - - } - - if ((n && s) || (!w && !e && !n && !s)) { - // straight north-south - t.vertexUV(x1, y1, z2, u0, v0); - t.vertexUV(x1, y0, z2, u0, v2); - t.vertexUV(x1, y0, z0, u2, v2); - t.vertexUV(x1, y1, z0, u2, v0); - - t.vertexUV(x1, y1, z0, u0, v0); - t.vertexUV(x1, y0, z0, u0, v2); - t.vertexUV(x1, y0, z2, u2, v2); - t.vertexUV(x1, y1, z2, u2, v0); - - if (up) { - // small edge texture - t.vertexUV(ix1, y1, z2, iu1, iv2); - t.vertexUV(ix1, y1, z0, iu1, iv0); - t.vertexUV(ix0, y1, z0, iu0, iv0); - t.vertexUV(ix0, y1, z2, iu0, iv2); - - t.vertexUV(ix1, y1, z0, iu1, iv2); - t.vertexUV(ix1, y1, z2, iu1, iv0); - t.vertexUV(ix0, y1, z2, iu0, iv0); - t.vertexUV(ix0, y1, z0, iu0, iv2); - } else { - if (y < (depth - 1) && level->isEmptyTile(x, y + 1, z - 1)) { - t.vertexUV(ix0, y1, z0, iu1, iv0); - t.vertexUV(ix0, y1, z1, iu1, iv1); - t.vertexUV(ix1, y1, z1, iu0, iv1); - t.vertexUV(ix1, y1, z0, iu0, iv0); - - t.vertexUV(ix0, y1, z1, iu1, iv0); - t.vertexUV(ix0, y1, z0, iu1, iv1); - t.vertexUV(ix1, y1, z0, iu0, iv1); - t.vertexUV(ix1, y1, z1, iu0, iv0); - } - if (y < (depth - 1) && level->isEmptyTile(x, y + 1, z + 1)) { - t.vertexUV(ix0, y1, z1, iu0, iv1); - t.vertexUV(ix0, y1, z2, iu0, iv2); - t.vertexUV(ix1, y1, z2, iu1, iv2); - t.vertexUV(ix1, y1, z1, iu1, iv1); - - t.vertexUV(ix0, y1, z2, iu0, iv1); - t.vertexUV(ix0, y1, z1, iu0, iv2); - t.vertexUV(ix1, y1, z1, iu1, iv2); - t.vertexUV(ix1, y1, z2, iu1, iv1); - } - } - if (down) { - // small edge texture - t.vertexUV(ix1, y0, z2, iu1, iv2); - t.vertexUV(ix1, y0, z0, iu1, iv0); - t.vertexUV(ix0, y0, z0, iu0, iv0); - t.vertexUV(ix0, y0, z2, iu0, iv2); - - t.vertexUV(ix1, y0, z0, iu1, iv2); - t.vertexUV(ix1, y0, z2, iu1, iv0); - t.vertexUV(ix0, y0, z2, iu0, iv0); - t.vertexUV(ix0, y0, z0, iu0, iv2); - } else { - if (y > 1 && level->isEmptyTile(x, y - 1, z - 1)) { - // north half-step - t.vertexUV(ix0, y0, z0, iu1, iv0); - t.vertexUV(ix0, y0, z1, iu1, iv1); - t.vertexUV(ix1, y0, z1, iu0, iv1); - t.vertexUV(ix1, y0, z0, iu0, iv0); - - t.vertexUV(ix0, y0, z1, iu1, iv0); - t.vertexUV(ix0, y0, z0, iu1, iv1); - t.vertexUV(ix1, y0, z0, iu0, iv1); - t.vertexUV(ix1, y0, z1, iu0, iv0); - } - if (y > 1 && level->isEmptyTile(x, y - 1, z + 1)) { - // south half-step - t.vertexUV(ix0, y0, z1, iu0, iv1); - t.vertexUV(ix0, y0, z2, iu0, iv2); - t.vertexUV(ix1, y0, z2, iu1, iv2); - t.vertexUV(ix1, y0, z1, iu1, iv1); - - t.vertexUV(ix0, y0, z2, iu0, iv1); - t.vertexUV(ix0, y0, z1, iu0, iv2); - t.vertexUV(ix1, y0, z1, iu1, iv2); - t.vertexUV(ix1, y0, z2, iu1, iv1); - } - } - - } else if (n && !s) { - // half-step towards north - t.vertexUV(x1, y1, z0, u0, v0); - t.vertexUV(x1, y0, z0, u0, v2); - t.vertexUV(x1, y0, z1, u1, v2); - t.vertexUV(x1, y1, z1, u1, v0); - - t.vertexUV(x1, y1, z1, u0, v0); - t.vertexUV(x1, y0, z1, u0, v2); - t.vertexUV(x1, y0, z0, u1, v2); - t.vertexUV(x1, y1, z0, u1, v0); - - // small edge texture - if (!e && !w) { - t.vertexUV(ix0, y1, z1, iu0, iv0); - t.vertexUV(ix0, y0, z1, iu0, iv2); - t.vertexUV(ix1, y0, z1, iu1, iv2); - t.vertexUV(ix1, y1, z1, iu1, iv0); - - t.vertexUV(ix1, y1, z1, iu0, iv0); - t.vertexUV(ix1, y0, z1, iu0, iv2); - t.vertexUV(ix0, y0, z1, iu1, iv2); - t.vertexUV(ix0, y1, z1, iu1, iv0); - } - - if (up || (y < (depth - 1) && level->isEmptyTile(x, y + 1, z - 1))) { - // small edge texture - t.vertexUV(ix0, y1, z0, iu1, iv0); - t.vertexUV(ix0, y1, z1, iu1, iv1); - t.vertexUV(ix1, y1, z1, iu0, iv1); - t.vertexUV(ix1, y1, z0, iu0, iv0); - - t.vertexUV(ix0, y1, z1, iu1, iv0); - t.vertexUV(ix0, y1, z0, iu1, iv1); - t.vertexUV(ix1, y1, z0, iu0, iv1); - t.vertexUV(ix1, y1, z1, iu0, iv0); - } - - if (down || (y > 1 && level->isEmptyTile(x, y - 1, z - 1))) { - // small edge texture - t.vertexUV(ix0, y0, z0, iu1, iv0); - t.vertexUV(ix0, y0, z1, iu1, iv1); - t.vertexUV(ix1, y0, z1, iu0, iv1); - t.vertexUV(ix1, y0, z0, iu0, iv0); - - t.vertexUV(ix0, y0, z1, iu1, iv0); - t.vertexUV(ix0, y0, z0, iu1, iv1); - t.vertexUV(ix1, y0, z0, iu0, iv1); - t.vertexUV(ix1, y0, z1, iu0, iv0); - } - - } else if (!n && s) { - // half-step towards south - t.vertexUV(x1, y1, z1, u1, v0); - t.vertexUV(x1, y0, z1, u1, v2); - t.vertexUV(x1, y0, z2, u2, v2); - t.vertexUV(x1, y1, z2, u2, v0); - - t.vertexUV(x1, y1, z2, u1, v0); - t.vertexUV(x1, y0, z2, u1, v2); - t.vertexUV(x1, y0, z1, u2, v2); - t.vertexUV(x1, y1, z1, u2, v0); - - // small edge texture - if (!e && !w) { - t.vertexUV(ix1, y1, z1, iu0, iv0); - t.vertexUV(ix1, y0, z1, iu0, iv2); - t.vertexUV(ix0, y0, z1, iu1, iv2); - t.vertexUV(ix0, y1, z1, iu1, iv0); - - t.vertexUV(ix0, y1, z1, iu0, iv0); - t.vertexUV(ix0, y0, z1, iu0, iv2); - t.vertexUV(ix1, y0, z1, iu1, iv2); - t.vertexUV(ix1, y1, z1, iu1, iv0); - } - - if (up || (y < (depth - 1) && level->isEmptyTile(x, y + 1, z + 1))) { - // small edge texture - t.vertexUV(ix0, y1, z1, iu0, iv1); - t.vertexUV(ix0, y1, z2, iu0, iv2); - t.vertexUV(ix1, y1, z2, iu1, iv2); - t.vertexUV(ix1, y1, z1, iu1, iv1); - - t.vertexUV(ix0, y1, z2, iu0, iv1); - t.vertexUV(ix0, y1, z1, iu0, iv2); - t.vertexUV(ix1, y1, z1, iu1, iv2); - t.vertexUV(ix1, y1, z2, iu1, iv1); - } - if (down || (y > 1 && level->isEmptyTile(x, y - 1, z + 1))) { - // small edge texture - t.vertexUV(ix0, y0, z1, iu0, iv1); - t.vertexUV(ix0, y0, z2, iu0, iv2); - t.vertexUV(ix1, y0, z2, iu1, iv2); - t.vertexUV(ix1, y0, z1, iu1, iv1); - - t.vertexUV(ix0, y0, z2, iu0, iv1); - t.vertexUV(ix0, y0, z1, iu0, iv2); - t.vertexUV(ix1, y0, z1, iu1, iv2); - t.vertexUV(ix1, y0, z2, iu1, iv1); - } - - } - - return true; -} - -void TileRenderer::tesselateRowTexture( Tile* tt, int data, float x, float y, float z ) { - Tesselator& t = Tesselator::instance; - - int tex = tt->getTexture(0, data); - if(fixedTexture >= 0) - tex = fixedTexture; - - int xt = (tex & 0xf) << 4; - int yt = tex & 0xf0; - float u0 = (xt) / 256.0f; - float u1 = (xt + 15.99f) / 256.f; - float v0 = (yt) / 256.0f; - float v1 = (yt + 15.99f) / 256.0f; - - float x0 = x + 0.5f - 0.25f; - float x1 = x + 0.5f + 0.25f; - float z0 = z + 0.5f - 0.5f; - float z1 = z + 0.5f + 0.5f; - t.vertexUV(x0, y + 1, z0, u0, v0); - t.vertexUV(x0, y + 0, z0, u0, v1); - t.vertexUV(x0, y + 0, z1, u1, v1); - t.vertexUV(x0, y + 1, z1, u1, v0); - - t.vertexUV(x0, y + 1, z1, u0, v0); - t.vertexUV(x0, y + 0, z1, u0, v1); - t.vertexUV(x0, y + 0, z0, u1, v1); - t.vertexUV(x0, y + 1, z0, u1, v0); - - t.vertexUV(x1, y + 1, z1, u0, v0); - t.vertexUV(x1, y + 0, z1, u0, v1); - t.vertexUV(x1, y + 0, z0, u1, v1); - t.vertexUV(x1, y + 1, z0, u1, v0); - - t.vertexUV(x1, y + 1, z0, u0, v0); - t.vertexUV(x1, y + 0, z0, u0, v1); - t.vertexUV(x1, y + 0, z1, u1, v1); - t.vertexUV(x1, y + 1, z1, u1, v0); - - x0 = x + 0.5f - 0.5f; - x1 = x + 0.5f + 0.5f; - z0 = z + 0.5f - 0.25f; - z1 = z + 0.5f + 0.25f; - - t.vertexUV(x0, y + 1, z0, u0, v0); - t.vertexUV(x0, y + 0, z0, u0, v1); - t.vertexUV(x1, y + 0, z0, u1, v1); - t.vertexUV(x1, y + 1, z0, u1, v0); - - t.vertexUV(x1, y + 1, z0, u0, v0); - t.vertexUV(x1, y + 0, z0, u0, v1); - t.vertexUV(x0, y + 0, z0, u1, v1); - t.vertexUV(x0, y + 1, z0, u1, v0); - - t.vertexUV(x1, y + 1, z1, u0, v0); - t.vertexUV(x1, y + 0, z1, u0, v1); - t.vertexUV(x0, y + 0, z1, u1, v1); - t.vertexUV(x0, y + 1, z1, u1, v0); - - t.vertexUV(x0, y + 1, z1, u0, v0); - t.vertexUV(x0, y + 0, z1, u0, v1); - t.vertexUV(x1, y + 0, z1, u1, v1); - t.vertexUV(x1, y + 1, z1, u1, v0); -} +#include "TileRenderer.h" +#include "Chunk.h" +#include "../Minecraft.h" +#include "Tesselator.h" + +#include "../../world/level/LevelSource.h" +#include "../../world/level/tile/Tile.h" +#include "../../world/level/tile/DoorTile.h" +#include "../../world/level/tile/LiquidTile.h" +#include "../../world/level/tile/FenceTile.h" +#include "../../world/level/tile/FenceGateTile.h" +#include "../../world/level/tile/ThinFenceTile.h" +#include "../../world/level/tile/BedTile.h" +#include "../../world/level/tile/StemTile.h" +#include "../../world/level/tile/StairTile.h" +#include "../../world/Direction.h" +#include "../../world/Facing.h" +#include "tileentity/TileEntityRenderer.h" +#include "EntityTileRenderer.h" + +TileRenderer::TileRenderer( LevelSource* level /* = NULL */ ) +: level(level), + fixedTexture(-1), + xFlipTexture(false), + noCulling(false), + blsmooth(1), + applyAmbienceOcclusion(false) +{ +} + +bool TileRenderer::tesselateBlockInWorld(Tile* tt, int x, int y, int z) { + int col = tt->getColor(level, x, y, z); + float r = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; // xFlipTexture = (x & 1) != (y & 1); + + if (Minecraft::useAmbientOcclusion) { + return tesselateBlockInWorldWithAmbienceOcclusion(tt, x, y, z, r, g, b); + } else + { + return tesselateBlockInWorld(tt, x, y, z, r, g, b); + } +} + +bool TileRenderer::tesselateBlockInWorld( Tile* tt, int x, int y, int z, float r, float g, float b ) +{ + applyAmbienceOcclusion = false; + float xf = (float)x; + float yf = (float)y; + float zf = (float)z; + + Tesselator& t = Tesselator::instance; + + bool changed = false; + float c10 = 0.5f; + float c11 = 1; + float c2 = 0.8f; + float c3 = 0.6f; + + + float r11 = c11 * r; + float g11 = c11 * g; + float b11 = c11 * b; + + if (tt == (Tile*)Tile::grass) { + r = g = b = 1.0f; + } + + float r10 = c10 * r; + float r2 = c2 * r; + float r3 = c3 * r; + + float g10 = c10 * g; + float g2 = c2 * g; + float g3 = c3 * g; + + float b10 = c10 * b; + float b2 = c2 * b; + float b3 = c3 * b; + + float centerBrightness = tt->getBrightness(level, x, y, z); + + if (noCulling || tt->shouldRenderFace(level, x, y - 1, z, Facing::DOWN)) { + float br = tt->getBrightness(level, x, y - 1, z); + t.color(r10 * br, g10 * br, b10 * br); + renderFaceDown(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 0)); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x, y + 1, z, Facing::UP)) { + float br = tt->getBrightness(level, x, y + 1, z); + if (tt->yy1 != 1 && !tt->material->isLiquid()) br = centerBrightness; + t.color(r11 * br, g11 * br, b11 * br); + renderFaceUp(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 1)); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x, y, z - 1, Facing::NORTH)) { + float br = tt->getBrightness(level, x, y, z - 1); + if (tt->zz0 > 0) br = centerBrightness; + t.color(r2 * br, g2 * br, b2 * br); + renderNorth(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 2)); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x, y, z + 1, Facing::SOUTH)) { + float br = tt->getBrightness(level, x, y, z + 1); + if (tt->zz1 < 1) br = centerBrightness; + t.color(r2 * br, g2 * br, b2 * br); + renderSouth(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 3)); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x - 1, y, z, Facing::WEST)) { + float br = tt->getBrightness(level, x - 1, y, z); + if (tt->xx0 > 0) br = centerBrightness; + t.color(r3 * br, g3 * br, b3 * br); + renderWest(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 4)); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x + 1, y, z, Facing::EAST)) { + float br = tt->getBrightness(level, x + 1, y, z); + if (tt->xx1 < 1) br = centerBrightness; + t.color(r3 * br, g3 * br, b3 * br); + renderEast(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 5)); + changed = true; + } + + return changed; +} + + +void TileRenderer::tesselateInWorld( Tile* tile, int x, int y, int z, int fixedTexture ) +{ + this->fixedTexture = fixedTexture; + tesselateInWorld(tile, x, y, z); + this->fixedTexture = -1; +} + +bool TileRenderer::tesselateInWorld( Tile* tt, int x, int y, int z ) +{ + int shape = tt->getRenderShape(); + tt->updateShape(level, x, y, z); + + if (shape == Tile::SHAPE_BLOCK) { + return tesselateBlockInWorld(tt, x, y, z); + } else if (shape == Tile::SHAPE_WATER) { + return tesselateWaterInWorld(tt, x, y, z); + } else if (shape == Tile::SHAPE_CACTUS) { + return tesselateCactusInWorld(tt, x, y, z); + } else if (shape == Tile::SHAPE_CROSS_TEXTURE) { + return tesselateCrossInWorld(tt, x, y, z); + } else if(shape == Tile::SHAPE_STEM) { + return tesselateStemInWorld(tt, x, y, z); + } else if (shape == Tile::SHAPE_ROWS) { + return tesselateRowInWorld(tt, x, y, z); + } else if (shape == Tile::SHAPE_TORCH) { + return tesselateTorchInWorld(tt, x, y, z); + //} else if (shape == Tile::SHAPE_FIRE) { + // return tesselateFireInWorld(tt, x, y, z); + //} else if (shape == Tile::SHAPE_RED_DUST) { + // return tesselateDustInWorld(tt, x, y, z); + } else if (shape == Tile::SHAPE_LADDER) { + return tesselateLadderInWorld(tt, x, y, z); + } else if (shape == Tile::SHAPE_DOOR) { + return tesselateDoorInWorld(tt, x, y, z); + //} else if (shape == Tile::SHAPE_RAIL) { + // return tesselateRailInWorld(tt, x, y, z); + } else if (shape == Tile::SHAPE_STAIRS) { + return tesselateStairsInWorld((StairTile*)tt, x, y, z); + } else if (shape == Tile::SHAPE_FENCE) { + return tesselateFenceInWorld((FenceTile*)tt, x, y, z); + } else if (shape == Tile::SHAPE_FENCE_GATE) { + return tesselateFenceGateInWorld((FenceGateTile*) tt, x, y, z); + //} else if (shape == Tile::SHAPE_LEVER) { + // return tesselateLeverInWorld(tt, x, y, z); + //} else if (shape == Tile::SHAPE_BED) { + // return tesselateBedInWorld(tt, x, y, z); + //} else if (shape == Tile::SHAPE_DIODE) { + // return tesselateDiodeInWorld(tt, x, y, z); + } else if (shape == Tile::SHAPE_IRON_FENCE) { + return tesselateThinFenceInWorld((ThinFenceTile*) tt, x, y, z); + } else if(shape == Tile::SHAPE_BED) { + return tesselateBedInWorld(tt, x, y, z); + } else { + return false; + } +} + +void TileRenderer::tesselateInWorldNoCulling( Tile* tile, int x, int y, int z ) +{ + noCulling = true; + tesselateInWorld(tile, x, y, z); + noCulling = false; +} + +bool TileRenderer::tesselateTorchInWorld( Tile* tt, int x, int y, int z ) +{ + int dir = level->getData(x, y, z); + + Tesselator& t = Tesselator::instance; + + float br = tt->getBrightness(level, x, y, z); + if (Tile::lightEmission[tt->id] > 0) br = 1.0f; + t.color(br, br, br); + + float r = 0.40f; + float r2 = 0.5f - r; + float h = 0.20f; + if (dir == 1) { + tesselateTorch(tt, (float)x - r2, (float)y + h, (float)z, -r, 0); + } else if (dir == 2) { + tesselateTorch(tt, (float)x + r2, (float)y + h, (float)z, +r, 0); + } else if (dir == 3) { + tesselateTorch(tt, (float)x, (float)y + h, (float)z - r2, 0, -r); + } else if (dir == 4) { + tesselateTorch(tt, (float)x, (float)y + h, (float)z + r2, 0, +r); + } else { + tesselateTorch(tt, (float)x, (float)y, (float)z, 0, 0); + } + return true; +} + +bool TileRenderer::tesselateLadderInWorld( Tile* tt, int x, int y, int z ) +{ + Tesselator& t = Tesselator::instance; + + int tex = tt->getTexture(0); + + if (fixedTexture >= 0) tex = fixedTexture; + + float br = tt->getBrightness(level, x, y, z); + t.color(br, br, br); + int xt = ((tex & 0xf) << 4); + int yt = tex & 0xf0; + + float u0 = (xt) / 256.0f; + float u1 = (xt + 15.99f) / 256.0f; + float v0 = (yt) / 256.0f; + float v1 = (yt + 15.99f) / 256.0f; + + int face = level->getData(x, y, z); + + float o = 0 / 16.0f; + float r = 0.05f; + if (face == 5) { + t.vertexUV(x + r, y + 1 + o, z + 1 + o, u0, v0); + t.vertexUV(x + r, y + 0 - o, z + 1 + o, u0, v1); + t.vertexUV(x + r, y + 0 - o, z + 0 - o, u1, v1); + t.vertexUV(x + r, y + 1 + o, z + 0 - o, u1, v0); + } + if (face == 4) { + t.vertexUV(x + 1 - r, y + 0 - o, z + 1 + o, u1, v1); + t.vertexUV(x + 1 - r, y + 1 + o, z + 1 + o, u1, v0); + t.vertexUV(x + 1 - r, y + 1 + o, z + 0 - o, u0, v0); + t.vertexUV(x + 1 - r, y + 0 - o, z + 0 - o, u0, v1); + } + if (face == 3) { + t.vertexUV(x + 1 + o, y + 0 - o, z + r, u1, v1); + t.vertexUV(x + 1 + o, y + 1 + o, z + r, u1, v0); + t.vertexUV(x + 0 - o, y + 1 + o, z + r, u0, v0); + t.vertexUV(x + 0 - o, y + 0 - o, z + r, u0, v1); + } + if (face == 2) { + t.vertexUV(x + 1 + o, y + 1 + o, z + 1 - r, u0, v0); + t.vertexUV(x + 1 + o, y + 0 - o, z + 1 - r, u0, v1); + t.vertexUV(x + 0 - o, y + 0 - o, z + 1 - r, u1, v1); + t.vertexUV(x + 0 - o, y + 1 + o, z + 1 - r, u1, v0); + } + + return true; +} + +bool TileRenderer::tesselateCrossInWorld( Tile* tt, int x, int y, int z ) +{ + Tesselator& t = Tesselator::instance; + + float br = tt->getBrightness(level, x, y, z); + int col = tt->getColor(level, x, y, z); + float r = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + t.color(br * r, br * g, br * b); + + float xt = float(x); + float yt = float(y); + float zt = float(z); + + if (tt == Tile::tallgrass) { + long seed = (x * 3129871) ^ (z * 116129781l) ^ (y); + seed = seed * seed * 42317861 + seed * 11; + + xt += ((((seed >> 16) & 0xf) / 15.0f) - 0.5f) * 0.5f; + yt += ((((seed >> 20) & 0xf) / 15.0f) - 1.0f) * 0.2f; + zt += ((((seed >> 24) & 0xf) / 15.0f) - 0.5f) * 0.5f; + } + + tesselateCrossTexture(tt, level->getData(x, y, z), xt, yt, zt); + return true; + //return true; + /*Tesselator& t = Tesselator::instance; + + float br = tt->getBrightness(level, x, y, z); + t.color(br, br, br); + + tesselateCrossTexture(tt, level->getData(x, y, z), (float)x, (float)y, (float)z); + return true;*/ +} +bool TileRenderer::tesselateStemInWorld( Tile* _tt, int x, int y, int z ) { + StemTile* tt = (StemTile*) _tt; + Tesselator& t = Tesselator::instance; + + float br = tt->getBrightness(level, x, y, z); + + int col = tt->getColor(level, x, y, z); + float r = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + t.color(br * r, br * g, br * b); + + tt->updateShape(level, x, y, z); + int dir = tt->getConnectDir(level, x, y, z); + if (dir < 0) { + tesselateStemTexture(tt, level->getData(x, y, z), tt->yy1, float(x), float(y - 1 / 16.0f), float(z)); + } else { + tesselateStemTexture(tt, level->getData(x, y, z), 0.5f, float(x), float(y - 1 / 16.0f), float(z)); + tesselateStemDirTexture(tt, level->getData(x, y, z), dir, tt->yy1, float(x), float(y - 1 / 16.0f), float(z)); + } + return true; +} +void TileRenderer::tesselateTorch( Tile* tt, float x, float y, float z, float xxa, float zza ) +{ + Tesselator& t = Tesselator::instance; + int tex = tt->getTexture(0); + + if (fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + float u0 = (xt) / 256.0f; + float u1 = (xt + 15.99f) / 256.0f; + float v0 = (yt) / 256.0f; + float v1 = (yt + 15.99f) / 256.0f; + + + float uc0 = u0 + 7 / 256.0f; + float vc0 = v0 + 6 / 256.0f; + float uc1 = u0 + 9 / 256.0f; + float vc1 = v0 + 8 / 256.0f; + x += 0.5f; + z += 0.5f; + + float x0 = x - 0.5f; + float x1 = x + 0.5f; + float z0 = z - 0.5f; + float z1 = z + 0.5f; + float r = 1 / 16.0f; + + float h = 10.0f / 16.0f; + t.vertexUV(x + xxa * (1 - h) - r, y + h, z + zza * (1 - h) - r, uc0, vc0); + t.vertexUV(x + xxa * (1 - h) - r, y + h, z + zza * (1 - h) + r, uc0, vc1); + t.vertexUV(x + xxa * (1 - h) + r, y + h, z + zza * (1 - h) + r, uc1, vc1); + t.vertexUV(x + xxa * (1 - h) + r, y + h, z + zza * (1 - h) - r, uc1, vc0); + + t.vertexUV(x - r, y + 1, z0, u0, v0); + t.vertexUV(x - r + xxa, y + 0, z0 + zza, u0, v1); + t.vertexUV(x - r + xxa, y + 0, z1 + zza, u1, v1); + t.vertexUV(x - r, y + 1, z1, u1, v0); + + t.vertexUV(x + r, y + 1, z1, u0, v0); + t.vertexUV(x + xxa + r, y + 0, z1 + zza, u0, v1); + t.vertexUV(x + xxa + r, y + 0, z0 + zza, u1, v1); + t.vertexUV(x + r, y + 1, z0, u1, v0); + + t.vertexUV(x0, y + 1, z + r, u0, v0); + t.vertexUV(x0 + xxa, y + 0, z + r + zza, u0, v1); + t.vertexUV(x1 + xxa, y + 0, z + r + zza, u1, v1); + t.vertexUV(x1, y + 1, z + r, u1, v0); + + t.vertexUV(x1, y + 1, z - r, u0, v0); + t.vertexUV(x1 + xxa, y + 0, z - r + zza, u0, v1); + t.vertexUV(x0 + xxa, y + 0, z - r + zza, u1, v1); + t.vertexUV(x0, y + 1, z - r, u1, v0); +} + +void TileRenderer::tesselateCrossTexture( Tile* tt, int data, float x, float y, float z ) +{ + Tesselator& t = Tesselator::instance; + + int tex = tt->getTexture(0, data); + + if (fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + float u0 = (xt) / 256.0f; + float u1 = (xt + 15.99f) / 256.0f; + float v0 = (yt) / 256.0f; + float v1 = (yt + 15.99f) / 256.0f; + + float x0 = x + 0.5f - 0.45f; + float x1 = x + 0.5f + 0.45f; + float z0 = z + 0.5f - 0.45f; + float z1 = z + 0.5f + 0.45f; + + t.vertexUV(x0, y + 1, z0, u0, v0); + t.vertexUV(x0, y + 0, z0, u0, v1); + t.vertexUV(x1, y + 0, z1, u1, v1); + t.vertexUV(x1, y + 1, z1, u1, v0); + + t.vertexUV(x1, y + 1, z1, u0, v0); + t.vertexUV(x1, y + 0, z1, u0, v1); + t.vertexUV(x0, y + 0, z0, u1, v1); + t.vertexUV(x0, y + 1, z0, u1, v0); + + t.vertexUV(x0, y + 1, z1, u0, v0); + t.vertexUV(x0, y + 0, z1, u0, v1); + t.vertexUV(x1, y + 0, z0, u1, v1); + t.vertexUV(x1, y + 1, z0, u1, v0); + + t.vertexUV(x1, y + 1, z0, u0, v0); + t.vertexUV(x1, y + 0, z0, u0, v1); + t.vertexUV(x0, y + 0, z1, u1, v1); + t.vertexUV(x0, y + 1, z1, u1, v0); +} +void TileRenderer::tesselateStemTexture( Tile* tt, int data, float h, float x, float y, float z ) { + Tesselator& t = Tesselator::instance; + int tex = tt->getTexture(0, data); + if(fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + float u0 = (xt) / 256.0f; + float u1 = (xt + 15.99f) / 256.0f; + float v0 = (yt) / 256.0f; + float v1 = (yt + 15.99f * h) / 256.0f; + + float x0 = x + 0.5f - 0.45f; + float x1 = x + 0.5f + 0.45f; + float z0 = z + 0.5f - 0.45f; + float z1 = z + 0.5f + 0.45f; + + t.vertexUV(x0, y + h, z0, u0, v0); + t.vertexUV(x0, y + 0, z0, u0, v1); + t.vertexUV(x1, y + 0, z1, u1, v1); + t.vertexUV(x1, y + h, z1, u1, v0); + + t.vertexUV(x1, y + h, z1, u0, v0); + t.vertexUV(x1, y + 0, z1, u0, v1); + t.vertexUV(x0, y + 0, z0, u1, v1); + t.vertexUV(x0, y + h, z0, u1, v0); + + t.vertexUV(x0, y + h, z1, u0, v0); + t.vertexUV(x0, y + 0, z1, u0, v1); + t.vertexUV(x1, y + 0, z0, u1, v1); + t.vertexUV(x1, y + h, z0, u1, v0); + + t.vertexUV(x1, y + h, z0, u0, v0); + t.vertexUV(x1, y + 0, z0, u0, v1); + t.vertexUV(x0, y + 0, z1, u1, v1); + t.vertexUV(x0, y + h, z1, u1, v0); +} +void TileRenderer::tesselateStemDirTexture( Tile* tt, int data, int dir, float h, float x, float y, float z ) { + Tesselator& t = Tesselator::instance; + + int tex = tt->getTexture(0, data) + 16; + + if (fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + float u0 = (xt) / 256.0f; + float u1 = (xt + 15.99f) / 256.0f; + float v0 = (yt) / 256.0f; + float v1 = (yt + 15.99f * h) / 256.0f; + + float x0 = x + 0.5f - 0.5f; + float x1 = x + 0.5f + 0.5f; + float z0 = z + 0.5f - 0.5f; + float z1 = z + 0.5f + 0.5f; + + float xm = x + 0.5f; + float zm = z + 0.5f; + + if ((dir + 1) / 2 % 2 == 1) { + float tmp = u1; + u1 = u0; + u0 = tmp; + } + + if (dir < 2) { + t.vertexUV(x0, y + h, zm, u0, v0); + t.vertexUV(x0, y + 0, zm, u0, v1); + t.vertexUV(x1, y + 0, zm, u1, v1); + t.vertexUV(x1, y + h, zm, u1, v0); + + t.vertexUV(x1, y + h, zm, u1, v0); + t.vertexUV(x1, y + 0, zm, u1, v1); + t.vertexUV(x0, y + 0, zm, u0, v1); + t.vertexUV(x0, y + h, zm, u0, v0); + } else { + + t.vertexUV(xm, y + h, z1, u0, v0); + t.vertexUV(xm, y + 0, z1, u0, v1); + t.vertexUV(xm, y + 0, z0, u1, v1); + t.vertexUV(xm, y + h, z0, u1, v0); + + t.vertexUV(xm, y + h, z0, u1, v0); + t.vertexUV(xm, y + 0, z0, u1, v1); + t.vertexUV(xm, y + 0, z1, u0, v1); + t.vertexUV(xm, y + h, z1, u0, v0); + } +} + +bool TileRenderer::tesselateWaterInWorld( Tile* tt, int x, int y, int z ) +{ + Tesselator& t = Tesselator::instance; + + bool up = tt->shouldRenderFace(level, x, y + 1, z, 1); + bool down = tt->shouldRenderFace(level, x, y - 1, z, 0); + + bool dirs[4]; // static? + dirs[0] = tt->shouldRenderFace(level, x, y, z - 1, 2); + dirs[1] = tt->shouldRenderFace(level, x, y, z + 1, 3); + dirs[2] = tt->shouldRenderFace(level, x - 1, y, z, 4); + dirs[3] = tt->shouldRenderFace(level, x + 1, y, z, 5); + + if (!up && !down && !dirs[0] && !dirs[1] && !dirs[2] && !dirs[3]) return false; + + bool changed = false; + float c10 = 0.5f; + float c11 = 1; + float c2 = 0.8f; + float c3 = 0.6f; + + const float yo0 = 0; + const float yo1 = 1; + + const Material* m = tt->material; + int data = level->getData(x, y, z); + + float h0 = getWaterHeight(x, y, z, m); + float h1 = getWaterHeight(x, y, z + 1, m); + float h2 = getWaterHeight(x + 1, y, z + 1, m); + float h3 = getWaterHeight(x + 1, y, z, m); + + // renderFaceUp(tt, x, y, z, tt->getTexture(0)); + if (noCulling || up) { + changed = true; + int tex = tt->getTexture(1, data); + float angle = (float) LiquidTile::getSlopeAngle(level, x, y, z, m); + if (angle > -999) { + tex = tt->getTexture(2, data); + } + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + + float uc = (xt + 0.5f * 16) / 256.0f; + float vc = (yt + 0.5f * 16) / 256.0f; + if (angle < -999) { + angle = 0; + } else { + uc = (xt + 1 * 16) / 256.0f; + vc = (yt + 1 * 16) / 256.0f; + } + float s = (Mth::sin(angle) * 8) / 256.5f; // @attn: to get rid of "jitter" (caused + float c = (Mth::cos(angle) * 8) / 256.5f; // of fp rounding errors) in big oceans) + + float br = tt->getBrightness(level, x, y, z); + t.color(c11 * br, c11 * br, c11 * br); + t.vertexUV((float)x + 0, (float)y + h0, (float)z + 0, uc - c - s, vc - c + s); + t.vertexUV((float)x + 0, (float)y + h1, (float)z + 1, uc - c + s, vc + c + s); + t.vertexUV((float)x + 1, (float)y + h2, (float)z + 1, uc + c + s, vc + c - s); + t.vertexUV((float)x + 1, (float)y + h3, (float)z + 0, uc + c - s, vc - c - s); + } + + if (noCulling || down) { + float br = tt->getBrightness(level, x, y - 1, z); + t.color(c10 * br, c10 * br, c10 * br); + renderFaceDown(tt, (float)x, (float)y, (float)z, tt->getTexture(0)); + changed = true; + } + + for (int face = 0; face < 4; face++) { + int xt = x; + int yt = y; + int zt = z; + + if (face == 0) zt--; + if (face == 1) zt++; + if (face == 2) xt--; + if (face == 3) xt++; + + int tex = tt->getTexture(face + 2, data); + int xTex = (tex & 0xf) << 4; + int yTex = tex & 0xf0; + + if (noCulling || dirs[face]) { + float hh0; + float hh1; + float x0, z0, x1, z1; + if (face == 0) { + hh0 = h0; + hh1 = h3; + x0 = (float)(x ); + x1 = (float)(x + 1); + z0 = (float)(z ); + z1 = (float)(z ); + } else if (face == 1) { + hh0 = h2; + hh1 = h1; + x0 = (float)(x + 1); + x1 = (float)(x ); + z0 = (float)(z + 1); + z1 = (float)(z + 1); + } else if (face == 2) { + hh0 = h1; + hh1 = h0; + x0 = (float)(x ); + x1 = (float)(x ); + z0 = (float)(z + 1); + z1 = (float)(z ); + } else { + hh0 = h3; + hh1 = h2; + x0 = (float)(x + 1); + x1 = (float)(x + 1); + z0 = (float)(z ); + z1 = (float)(z + 1); + } + + changed = true; + float u0 = (xTex + 0 * 16) / 256.0f; + float u1 = (xTex + 1 * 16 - 0.01f) / 256.0f; + + float v01 = (yTex + (1 - hh0) * 16) / 256.0f; + float v02 = (yTex + (1 - hh1) * 16) / 256.0f; + float v1 = (yTex + 1 * 16 - 0.01f) / 256.0f; + + float br = tt->getBrightness(level, xt, yt, zt); + if (face < 2) br *= c2; + else br *= c3; + + float yf = (float)y; + t.color(c11 * br, c11 * br, c11 * br); + t.vertexUV(x0, yf + hh0, z0, u0, v01); + t.vertexUV(x1, yf + hh1, z1, u1, v02); + t.vertexUV(x1, yf + 0, z1, u1, v1); + t.vertexUV(x0, yf + 0, z0, u0, v1); + } + } + + //printf("w: %d ", (dirs[0] + dirs[1] + dirs[2] + dirs[3] + up + down)); + + tt->yy0 = yo0; + tt->yy1 = yo1; + + return changed; +} + +float TileRenderer::getWaterHeight( int x, int y, int z, const Material* m ) +{ + int count = 0; + float h = 0; + for (int i = 0; i < 4; i++) { + int xx = x - (i & 1); + int yy = y; + int zz = z - ((i >> 1) & 1); + if (level->getMaterial(xx, yy + 1, zz) == m) { + return 1; + } + const Material* tm = level->getMaterial(xx, yy, zz); + if (tm == m) { + int d = level->getData(xx, yy, zz); + if (d >= 8 || d == 0) { + h += (LiquidTile::getHeight(d)) * 10; + count += 10; + } + h += LiquidTile::getHeight(d); + count++; + } else if (!tm->isSolid()) { + h += 1; + count++; + } + } + return 1 - h / count; +} + +void TileRenderer::renderBlock(Tile* tt, LevelSource* level, int x, int y, int z) { + float c10 = 0.5f; + float c11 = 1; + float c2 = 0.8f; + float c3 = 0.6f; + + Tesselator& t = Tesselator::instance; + t.begin(); + + float center = tt->getBrightness(level, x, y, z); + float br = tt->getBrightness(level, x, y - 1, z); + if (br < center) br = center; + + t.color(c10 * br, c10 * br, c10 * br); + renderFaceDown(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(0)); + + br = tt->getBrightness(level, x, y + 1, z); + if (br < center) br = center; + t.color(c11 * br, c11 * br, c11 * br); + renderFaceUp(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(1)); + + br = tt->getBrightness(level, x, y, z - 1); + if (br < center) br = center; + t.color(c2 * br, c2 * br, c2 * br); + renderNorth(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(2)); + + br = tt->getBrightness(level, x, y, z + 1); + if (br < center) br = center; + t.color(c2 * br, c2 * br, c2 * br); + renderSouth(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(3)); + + br = tt->getBrightness(level, x - 1, y, z); + if (br < center) br = center; + t.color(c3 * br, c3 * br, c3 * br); + renderWest(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(4)); + + br = tt->getBrightness(level, x + 1, y, z); + if (br < center) br = center; + t.color(c3 * br, c3 * br, c3 * br); + renderEast(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(5)); + t.draw(); +} + +bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusion( Tile* tt, int pX, int pY, int pZ, float pBaseRed, float pBaseGreen, float pBaseBlue ) +{ + applyAmbienceOcclusion = true; + bool i = false; + float ll1 = ll000; + float ll2 = ll000; + float ll3 = ll000; + float ll4 = ll000; + bool tint0 = true; + bool tint1 = true; + bool tint2 = true; + bool tint3 = true; + bool tint4 = true; + bool tint5 = true; + + ll000 = tt->getBrightness(level, pX, pY, pZ); + llx00 = tt->getBrightness(level, pX - 1, pY, pZ); + ll0y0 = tt->getBrightness(level, pX, pY - 1, pZ); + ll00z = tt->getBrightness(level, pX, pY, pZ - 1); + llX00 = tt->getBrightness(level, pX + 1, pY, pZ); + ll0Y0 = tt->getBrightness(level, pX, pY + 1, pZ); + ll00Z = tt->getBrightness(level, pX, pY, pZ + 1); + + llTransXY0 = Tile::translucent[level->getTile(pX + 1, pY + 1, pZ)]; + llTransXy0 = Tile::translucent[level->getTile(pX + 1, pY - 1, pZ)]; + llTransX0Z = Tile::translucent[level->getTile(pX + 1, pY, pZ + 1)]; + llTransX0z = Tile::translucent[level->getTile(pX + 1, pY, pZ - 1)]; + llTransxY0 = Tile::translucent[level->getTile(pX - 1, pY + 1, pZ)]; + llTransxy0 = Tile::translucent[level->getTile(pX - 1, pY - 1, pZ)]; + llTransx0z = Tile::translucent[level->getTile(pX - 1, pY, pZ - 1)]; + llTransx0Z = Tile::translucent[level->getTile(pX - 1, pY, pZ + 1)]; + llTrans0YZ = Tile::translucent[level->getTile(pX, pY + 1, pZ + 1)]; + llTrans0Yz = Tile::translucent[level->getTile(pX, pY + 1, pZ - 1)]; + llTrans0yZ = Tile::translucent[level->getTile(pX, pY - 1, pZ + 1)]; + llTrans0yz = Tile::translucent[level->getTile(pX, pY - 1, pZ - 1)]; + + if (tt->tex == 3) tint0 = tint2 = tint3 = tint4 = tint5 = false; + + if ((noCulling) || (tt->shouldRenderFace(level, pX, pY - 1, pZ, 0))) { + if (blsmooth > 0) { + pY--; + + llxy0 = tt->getBrightness(level, pX - 1, pY, pZ); + ll0yz = tt->getBrightness(level, pX, pY, pZ - 1); + ll0yZ = tt->getBrightness(level, pX, pY, pZ + 1); + llXy0 = tt->getBrightness(level, pX + 1, pY, pZ); + + if (llTrans0yz || llTransxy0) { + llxyz = tt->getBrightness(level, pX - 1, pY, pZ - 1); + } else { + llxyz = llxy0; + } + if (llTrans0yZ || llTransxy0) { + llxyZ = tt->getBrightness(level, pX - 1, pY, pZ + 1); + } else { + llxyZ = llxy0; + } + if (llTrans0yz || llTransXy0) { + llXyz = tt->getBrightness(level, pX + 1, pY, pZ - 1); + } else { + llXyz = llXy0; + } + if (llTrans0yZ || llTransXy0) { + llXyZ = tt->getBrightness(level, pX + 1, pY, pZ + 1); + } else { + llXyZ = llXy0; + } + + pY++; + ll1 = (llxyZ + llxy0 + ll0yZ + ll0y0) / 4.0f; + ll4 = (ll0yZ + ll0y0 + llXyZ + llXy0) / 4.0f; + ll3 = (ll0y0 + ll0yz + llXy0 + llXyz) / 4.0f; + ll2 = (llxy0 + llxyz + ll0y0 + ll0yz) / 4.0f; + } else ll1 = ll2 = ll3 = ll4 = ll0y0; + c1r = c2r = c3r = c4r = (tint0 ? pBaseRed : 1.0f) * 0.5f; + c1g = c2g = c3g = c4g = (tint0 ? pBaseGreen : 1.0f) * 0.5f; + c1b = c2b = c3b = c4b = (tint0 ? pBaseBlue : 1.0f) * 0.5f; + c1r *= ll1; + c1g *= ll1; + c1b *= ll1; + c2r *= ll2; + c2g *= ll2; + c2b *= ll2; + c3r *= ll3; + c3g *= ll3; + c3b *= ll3; + c4r *= ll4; + c4g *= ll4; + c4b *= ll4; + + renderFaceDown(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 0)); + i = true; + } + if ((noCulling) || (tt->shouldRenderFace(level, pX, pY + 1, pZ, 1))) { + if (blsmooth > 0) { + pY++; + + llxY0 = tt->getBrightness(level, pX - 1, pY, pZ); + llXY0 = tt->getBrightness(level, pX + 1, pY, pZ); + ll0Yz = tt->getBrightness(level, pX, pY, pZ - 1); + ll0YZ = tt->getBrightness(level, pX, pY, pZ + 1); + + if (llTrans0Yz || llTransxY0) { + llxYz = tt->getBrightness(level, pX - 1, pY, pZ - 1); + } else { + llxYz = llxY0; + } + if (llTrans0Yz || llTransXY0) { + llXYz = tt->getBrightness(level, pX + 1, pY, pZ - 1); + } else { + llXYz = llXY0; + } + if (llTrans0YZ || llTransxY0) { + llxYZ = tt->getBrightness(level, pX - 1, pY, pZ + 1); + } else { + llxYZ = llxY0; + } + if (llTrans0YZ || llTransXY0) { + llXYZ = tt->getBrightness(level, pX + 1, pY, pZ + 1); + } else { + llXYZ = llXY0; + } + pY--; + + ll4 = (llxYZ + llxY0 + ll0YZ + ll0Y0) / 4.0f; + ll1 = (ll0YZ + ll0Y0 + llXYZ + llXY0) / 4.0f; + ll2 = (ll0Y0 + ll0Yz + llXY0 + llXYz) / 4.0f; + ll3 = (llxY0 + llxYz + ll0Y0 + ll0Yz) / 4.0f; + } else ll1 = ll2 = ll3 = ll4 = ll0Y0; + c1r = c2r = c3r = c4r = (tint1 ? pBaseRed : 1.0f); + c1g = c2g = c3g = c4g = (tint1 ? pBaseGreen : 1.0f); + c1b = c2b = c3b = c4b = (tint1 ? pBaseBlue : 1.0f); + c1r *= ll1; + c1g *= ll1; + c1b *= ll1; + c2r *= ll2; + c2g *= ll2; + c2b *= ll2; + c3r *= ll3; + c3g *= ll3; + c3b *= ll3; + c4r *= ll4; + c4g *= ll4; + c4b *= ll4; + renderFaceUp(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 1)); + i = true; + } + if ((noCulling) || (tt->shouldRenderFace(level, pX, pY, pZ - 1, 2))) { + if (blsmooth > 0) { + pZ--; + llx0z = tt->getBrightness(level, pX - 1, pY, pZ); + ll0yz = tt->getBrightness(level, pX, pY - 1, pZ); + ll0Yz = tt->getBrightness(level, pX, pY + 1, pZ); + llX0z = tt->getBrightness(level, pX + 1, pY, pZ); + + if (llTransx0z || llTrans0yz) { + llxyz = tt->getBrightness(level, pX - 1, pY - 1, pZ); + } else { + llxyz = llx0z; + } + if (llTransx0z || llTrans0Yz) { + llxYz = tt->getBrightness(level, pX - 1, pY + 1, pZ); + } else { + llxYz = llx0z; + } + if (llTransX0z || llTrans0yz) { + llXyz = tt->getBrightness(level, pX + 1, pY - 1, pZ); + } else { + llXyz = llX0z; + } + if (llTransX0z || llTrans0Yz) { + llXYz = tt->getBrightness(level, pX + 1, pY + 1, pZ); + } else { + llXYz = llX0z; + } + pZ++; + ll1 = (llx0z + llxYz + ll00z + ll0Yz) / 4.0f; + ll2 = (ll00z + ll0Yz + llX0z + llXYz) / 4.0f; + ll3 = (ll0yz + ll00z + llXyz + llX0z) / 4.0f; + ll4 = (llxyz + llx0z + ll0yz + ll00z) / 4.0f; + } else ll1 = ll2 = ll3 = ll4 = ll00z; + c1r = c2r = c3r = c4r = (tint2 ? pBaseRed : 1.0f) * 0.8f; + c1g = c2g = c3g = c4g = (tint2 ? pBaseGreen : 1.0f) * 0.8f; + c1b = c2b = c3b = c4b = (tint2 ? pBaseBlue : 1.0f) * 0.8f; + c1r *= ll1; + c1g *= ll1; + c1b *= ll1; + c2r *= ll2; + c2g *= ll2; + c2b *= ll2; + c3r *= ll3; + c3g *= ll3; + c3b *= ll3; + c4r *= ll4; + c4g *= ll4; + c4b *= ll4; + renderNorth(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 2)); + i = true; + } + if ((noCulling) || (tt->shouldRenderFace(level, pX, pY, pZ + 1, 3))) { + if (blsmooth > 0) { + pZ++; + + llx0Z = tt->getBrightness(level, pX - 1, pY, pZ); + llX0Z = tt->getBrightness(level, pX + 1, pY, pZ); + ll0yZ = tt->getBrightness(level, pX, pY - 1, pZ); + ll0YZ = tt->getBrightness(level, pX, pY + 1, pZ); + + if (llTransx0Z || llTrans0yZ) { + llxyZ = tt->getBrightness(level, pX - 1, pY - 1, pZ); + } else { + llxyZ = llx0Z; + } + if (llTransx0Z || llTrans0YZ) { + llxYZ = tt->getBrightness(level, pX - 1, pY + 1, pZ); + } else { + llxYZ = llx0Z; + } + if (llTransX0Z || llTrans0yZ) { + llXyZ = tt->getBrightness(level, pX + 1, pY - 1, pZ); + } else { + llXyZ = llX0Z; + } + if (llTransX0Z || llTrans0YZ) { + llXYZ = tt->getBrightness(level, pX + 1, pY + 1, pZ); + } else { + llXYZ = llX0Z; + } + pZ--; + ll1 = (llx0Z + llxYZ + ll00Z + ll0YZ) / 4.0f; + ll4 = (ll00Z + ll0YZ + llX0Z + llXYZ) / 4.0f; + ll3 = (ll0yZ + ll00Z + llXyZ + llX0Z) / 4.0f; + ll2 = (llxyZ + llx0Z + ll0yZ + ll00Z) / 4.0f; + } else ll1 = ll2 = ll3 = ll4 = ll00Z; + c1r = c2r = c3r = c4r = (tint3 ? pBaseRed : 1.0f) * 0.8f; + c1g = c2g = c3g = c4g = (tint3 ? pBaseGreen : 1.0f) * 0.8f; + c1b = c2b = c3b = c4b = (tint3 ? pBaseBlue : 1.0f) * 0.8f; + c1r *= ll1; + c1g *= ll1; + c1b *= ll1; + c2r *= ll2; + c2g *= ll2; + c2b *= ll2; + c3r *= ll3; + c3g *= ll3; + c3b *= ll3; + c4r *= ll4; + c4g *= ll4; + c4b *= ll4; + renderSouth(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 3)); + i = true; + } + if ((noCulling) || (tt->shouldRenderFace(level, pX - 1, pY, pZ, 4))) { + if (blsmooth > 0) { + pX--; + llxy0 = tt->getBrightness(level, pX, pY - 1, pZ); + llx0z = tt->getBrightness(level, pX, pY, pZ - 1); + llx0Z = tt->getBrightness(level, pX, pY, pZ + 1); + llxY0 = tt->getBrightness(level, pX, pY + 1, pZ); + + if (llTransx0z || llTransxy0) { + llxyz = tt->getBrightness(level, pX, pY - 1, pZ - 1); + } else { + llxyz = llx0z; + } + if (llTransx0Z || llTransxy0) { + llxyZ = tt->getBrightness(level, pX, pY - 1, pZ + 1); + } else { + llxyZ = llx0Z; + } + if (llTransx0z || llTransxY0) { + llxYz = tt->getBrightness(level, pX, pY + 1, pZ - 1); + } else { + llxYz = llx0z; + } + if (llTransx0Z || llTransxY0) { + llxYZ = tt->getBrightness(level, pX, pY + 1, pZ + 1); + } else { + llxYZ = llx0Z; + } + pX++; + ll4 = (llxy0 + llxyZ + llx00 + llx0Z) / 4.0f; + ll1 = (llx00 + llx0Z + llxY0 + llxYZ) / 4.0f; + ll2 = (llx0z + llx00 + llxYz + llxY0) / 4.0f; + ll3 = (llxyz + llxy0 + llx0z + llx00) / 4.0f; + } else ll1 = ll2 = ll3 = ll4 = llx00; + c1r = c2r = c3r = c4r = (tint4 ? pBaseRed : 1.0f) * 0.6f; + c1g = c2g = c3g = c4g = (tint4 ? pBaseGreen : 1.0f) * 0.6f; + c1b = c2b = c3b = c4b = (tint4 ? pBaseBlue : 1.0f) * 0.6f; + c1r *= ll1; + c1g *= ll1; + c1b *= ll1; + c2r *= ll2; + c2g *= ll2; + c2b *= ll2; + c3r *= ll3; + c3g *= ll3; + c3b *= ll3; + c4r *= ll4; + c4g *= ll4; + c4b *= ll4; + renderWest(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 4)); + i = true; + } + if ((noCulling) || (tt->shouldRenderFace(level, pX + 1, pY, pZ, 5))) { + if (blsmooth > 0) { + pX++; + llXy0 = tt->getBrightness(level, pX, pY - 1, pZ); + llX0z = tt->getBrightness(level, pX, pY, pZ - 1); + llX0Z = tt->getBrightness(level, pX, pY, pZ + 1); + llXY0 = tt->getBrightness(level, pX, pY + 1, pZ); + + if (llTransXy0 || llTransX0z) { + llXyz = tt->getBrightness(level, pX, pY - 1, pZ - 1); + } else { + llXyz = llX0z; + } + if (llTransXy0 || llTransX0Z) { + llXyZ = tt->getBrightness(level, pX, pY - 1, pZ + 1); + } else { + llXyZ = llX0Z; + } + if (llTransXY0 || llTransX0z) { + llXYz = tt->getBrightness(level, pX, pY + 1, pZ - 1); + } else { + llXYz = llX0z; + } + if (llTransXY0 || llTransX0Z) { + llXYZ = tt->getBrightness(level, pX, pY + 1, pZ + 1); + } else { + llXYZ = llX0Z; + } + pX--; + ll1 = (llXy0 + llXyZ + llX00 + llX0Z) / 4.0f; + ll4 = (llX00 + llX0Z + llXY0 + llXYZ) / 4.0f; + ll3 = (llX0z + llX00 + llXYz + llXY0) / 4.0f; + ll2 = (llXyz + llXy0 + llX0z + llX00) / 4.0f; + } else ll1 = ll2 = ll3 = ll4 = llX00; + c1r = c2r = c3r = c4r = (tint5 ? pBaseRed : 1.0f) * 0.6f; + c1g = c2g = c3g = c4g = (tint5 ? pBaseGreen : 1.0f) * 0.6f; + c1b = c2b = c3b = c4b = (tint5 ? pBaseBlue : 1.0f) * 0.6f; + c1r *= ll1; + c1g *= ll1; + c1b *= ll1; + c2r *= ll2; + c2g *= ll2; + c2b *= ll2; + c3r *= ll3; + c3g *= ll3; + c3b *= ll3; + c4r *= ll4; + c4g *= ll4; + c4b *= ll4; + + renderEast(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 5)); + i = true; + } + applyAmbienceOcclusion = false; + return i; +} + +bool TileRenderer::tesselateCactusInWorld(Tile* tt, int x, int y, int z) { + int col = tt->getColor(level, x, y, z); + float r = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + return tesselateCactusInWorld(tt, x, y, z, r, g, b); +} + +bool TileRenderer::tesselateCactusInWorld(Tile* tt, int x, int y, int z, float r, float g, float b) { + Tesselator& t = Tesselator::instance; + + bool changed = false; + float c10 = 0.5f; + float c11 = 1; + float c2 = 0.8f; + float c3 = 0.6f; + + float r10 = c10 * r; + float r11 = c11 * r; + float r2 = c2 * r; + float r3 = c3 * r; + + float g10 = c10 * g; + float g11 = c11 * g; + float g2 = c2 * g; + float g3 = c3 * g; + + float b10 = c10 * b; + float b11 = c11 * b; + float b2 = c2 * b; + float b3 = c3 * b; + + float s = 1 / 16.0f; + const float X = (float)x; + const float Y = (float)y; + const float Z = (float)z; + + float centerBrightness = tt->getBrightness(level, x, y, z); + + if (noCulling || tt->shouldRenderFace(level, x, y - 1, z, 0)) { + float br = tt->getBrightness(level, x, y - 1, z); + // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = +// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; + t.color(r10 * br, g10 * br, b10 * br); + renderFaceDown(tt, X, Y, Z, tt->getTexture(level, x, y, z, 0)); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x, y + 1, z, 1)) { + float br = tt->getBrightness(level, x, y + 1, z); + if (tt->yy1 != 1 && !tt->material->isLiquid()) br = centerBrightness; + // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = +// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; + t.color(r11 * br, g11 * br, b11 * br); + renderFaceUp(tt, X, Y, Z, tt->getTexture(level, x, y, z, 1)); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x, y, z - 1, 2)) { + float br = tt->getBrightness(level, x, y, z - 1); + if (tt->zz0 > 0) br = centerBrightness; + // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = +// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; + t.color(r2 * br, g2 * br, b2 * br); + t.addOffset(0, 0, s); + renderNorth(tt, X, Y, Z, tt->getTexture(level, x, y, z, 2)); + t.addOffset(0, 0, -s); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x, y, z + 1, 3)) { + float br = tt->getBrightness(level, x, y, z + 1); + if (tt->zz1 < 1) br = centerBrightness; + // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = +// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; + t.color(r2 * br, g2 * br, b2 * br); + t.addOffset(0, 0, -s); + renderSouth(tt, X, Y, Z, tt->getTexture(level, x, y, z, 3)); + t.addOffset(0, 0, s); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x - 1, y, z, 4)) { + float br = tt->getBrightness(level, x - 1, y, z); + if (tt->xx0 > 0) br = centerBrightness; + // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = +// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; + t.color(r3 * br, g3 * br, b3 * br); + t.addOffset(s, 0, 0); + renderWest(tt, X, Y, Z, tt->getTexture(level, x, y, z, 4)); + t.addOffset(-s, 0, 0); + changed = true; + } + + if (noCulling || tt->shouldRenderFace(level, x + 1, y, z, 5)) { + float br = tt->getBrightness(level, x + 1, y, z); + if (tt->xx1 < 1) br = centerBrightness; + // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br = +// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS; + t.color(r3 * br, g3 * br, b3 * br); + t.addOffset(-s, 0, 0); + renderEast(tt, X, Y, Z, tt->getTexture(level, x, y, z, 5)); + t.addOffset(s, 0, 0); + changed = true; + } + + return changed; +} + +bool TileRenderer::tesselateFenceInWorld(FenceTile* tt, int x, int y, int z) { + bool changed = true; + + float a = 6 / 16.0f; + float b = 10 / 16.0f; + tt->setShape(a, 0, a, b, 1, b); + tesselateBlockInWorld(tt, x, y, z); + + bool vertical = false; + bool horizontal = false; + + bool l = tt->connectsTo(level, x - 1, y, z); + bool r = tt->connectsTo(level, x + 1, y, z); + bool u = tt->connectsTo(level, x, y, z - 1); + bool d = tt->connectsTo(level, x, y, z + 1); + + if (l || r) vertical = true; + if (u || d) horizontal = true; + + if (!vertical && !horizontal) vertical = true; + + a = 7 / 16.0f; + b = 9 / 16.0f; + float h0 = 12 / 16.0f; + float h1 = 15 / 16.0f; + + float x0 = l ? 0 : a; + float x1 = r ? 1 : b; + float z0 = u ? 0 : a; + float z1 = d ? 1 : b; + + if (vertical) { + tt->setShape(x0, h0, a, x1, h1, b); + tesselateBlockInWorld(tt, x, y, z); + } + if (horizontal) { + tt->setShape(a, h0, z0, b, h1, z1); + tesselateBlockInWorld(tt, x, y, z); + } + + h0 = 6 / 16.0f; + h1 = 9 / 16.0f; + if (vertical) { + tt->setShape(x0, h0, a, x1, h1, b); + tesselateBlockInWorld(tt, x, y, z); + } + if (horizontal) { + tt->setShape(a, h0, z0, b, h1, z1); + tesselateBlockInWorld(tt, x, y, z); + } + + tt->setShape(0, 0, 0, 1, 1, 1); + return changed; +} + +bool TileRenderer::tesselateFenceGateInWorld(FenceGateTile* tt, int x, int y, int z) { + bool changed = true; + + int data = level->getData(x, y, z); + bool isOpen = FenceGateTile::isOpen(data); + int direction = FenceGateTile::getDirection(data); + + const float h00 = 6 / 16.0f; + const float h01 = 9 / 16.0f; + const float h10 = 12 / 16.0f; + const float h11 = 15 / 16.0f; + const float h20 = 5 / 16.0f; + const float h21 = 16 / 16.0f; + + // edge sticks + if (direction == Direction::EAST || direction == Direction::WEST) { + float x0 = 7 / 16.0f; + float x1 = 9 / 16.0f; + float z0 = 0 / 16.0f; + float z1 = 2 / 16.0f; + tt->setShape(x0, h20, z0, x1, h21, z1); + tesselateBlockInWorld(tt, x, y, z); + + z0 = 14 / 16.0f; + z1 = 16 / 16.0f; + tt->setShape(x0, h20, z0, x1, h21, z1); + tesselateBlockInWorld(tt, x, y, z); + } else { + float x0 = 0 / 16.0f; + float x1 = 2 / 16.0f; + float z0 = 7 / 16.0f; + float z1 = 9 / 16.0f; + tt->setShape(x0, h20, z0, x1, h21, z1); + tesselateBlockInWorld(tt, x, y, z); + + x0 = 14 / 16.0f; + x1 = 16 / 16.0f; + tt->setShape(x0, h20, z0, x1, h21, z1); + tesselateBlockInWorld(tt, x, y, z); + } + if (!isOpen) { + if (direction == Direction::EAST || direction == Direction::WEST) { + float x0 = 7 / 16.0f; + float x1 = 9 / 16.0f; + float z0 = 6 / 16.0f; + float z1 = 8 / 16.0f; + tt->setShape(x0, h00, z0, x1, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + z0 = 8 / 16.0f; + z1 = 10 / 16.0f; + tt->setShape(x0, h00, z0, x1, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + z0 = 10 / 16.0f; + z1 = 14 / 16.0f; + tt->setShape(x0, h00, z0, x1, h01, z1); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x0, h10, z0, x1, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + z0 = 2 / 16.0f; + z1 = 6 / 16.0f; + tt->setShape(x0, h00, z0, x1, h01, z1); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x0, h10, z0, x1, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + } else { + float x0 = 6 / 16.0f; + float x1 = 8 / 16.0f; + float z0 = 7 / 16.0f; + float z1 = 9 / 16.0f; + tt->setShape(x0, h00, z0, x1, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + x0 = 8 / 16.0f; + x1 = 10 / 16.0f; + tt->setShape(x0, h00, z0, x1, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + x0 = 10 / 16.0f; + x1 = 14 / 16.0f; + tt->setShape(x0, h00, z0, x1, h01, z1); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x0, h10, z0, x1, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + x0 = 2 / 16.0f; + x1 = 6 / 16.0f; + tt->setShape(x0, h00, z0, x1, h01, z1); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x0, h10, z0, x1, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + + } + } else { + if (direction == Direction::EAST) { + + const float z00 = 0 / 16.0f; + const float z01 = 2 / 16.0f; + const float z10 = 14 / 16.0f; + const float z11 = 16 / 16.0f; + + const float x0 = 9 / 16.0f; + const float x1 = 13 / 16.0f; + const float x2 = 15 / 16.0f; + + tt->setShape(x1, h00, z00, x2, h11, z01); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x1, h00, z10, x2, h11, z11); + tesselateBlockInWorld(tt, x, y, z); + + tt->setShape(x0, h00, z00, x1, h01, z01); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x0, h00, z10, x1, h01, z11); + tesselateBlockInWorld(tt, x, y, z); + + tt->setShape(x0, h10, z00, x1, h11, z01); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x0, h10, z10, x1, h11, z11); + tesselateBlockInWorld(tt, x, y, z); + } else if (direction == Direction::WEST) { + const float z00 = 0 / 16.0f; + const float z01 = 2 / 16.0f; + const float z10 = 14 / 16.0f; + const float z11 = 16 / 16.0f; + + const float x0 = 1 / 16.0f; + const float x1 = 3 / 16.0f; + const float x2 = 7 / 16.0f; + + tt->setShape(x0, h00, z00, x1, h11, z01); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x0, h00, z10, x1, h11, z11); + tesselateBlockInWorld(tt, x, y, z); + + tt->setShape(x1, h00, z00, x2, h01, z01); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x1, h00, z10, x2, h01, z11); + tesselateBlockInWorld(tt, x, y, z); + + tt->setShape(x1, h10, z00, x2, h11, z01); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x1, h10, z10, x2, h11, z11); + tesselateBlockInWorld(tt, x, y, z); + } else if (direction == Direction::SOUTH) { + + const float x00 = 0 / 16.0f; + const float x01 = 2 / 16.0f; + const float x10 = 14 / 16.0f; + const float x11 = 16 / 16.0f; + + const float z0 = 9 / 16.0f; + const float z1 = 13 / 16.0f; + const float z2 = 15 / 16.0f; + + tt->setShape(x00, h00, z1, x01, h11, z2); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x10, h00, z1, x11, h11, z2); + tesselateBlockInWorld(tt, x, y, z); + + tt->setShape(x00, h00, z0, x01, h01, z1); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x10, h00, z0, x11, h01, z1); + tesselateBlockInWorld(tt, x, y, z); + + tt->setShape(x00, h10, z0, x01, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x10, h10, z0, x11, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + } else if (direction == Direction::NORTH) { + const float x00 = 0 / 16.0f; + const float x01 = 2 / 16.0f; + const float x10 = 14 / 16.0f; + const float x11 = 16 / 16.0f; + + const float z0 = 1 / 16.0f; + const float z1 = 3 / 16.0f; + const float z2 = 7 / 16.0f; + + tt->setShape(x00, h00, z0, x01, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x10, h00, z0, x11, h11, z1); + tesselateBlockInWorld(tt, x, y, z); + + tt->setShape(x00, h00, z1, x01, h01, z2); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x10, h00, z1, x11, h01, z2); + tesselateBlockInWorld(tt, x, y, z); + + tt->setShape(x00, h10, z1, x01, h11, z2); + tesselateBlockInWorld(tt, x, y, z); + tt->setShape(x10, h10, z1, x11, h11, z2); + tesselateBlockInWorld(tt, x, y, z); + } + } + + tt->setShape(0, 0, 0, 1, 1, 1); + return changed; +} + +bool TileRenderer::tesselateBedInWorld(Tile *tt, int x, int y, int z) { + Tesselator& t = Tesselator::instance; + int data = level->getData(x, y, z); + int direction = BedTile::getDirection(data); + bool isHead = BedTile::isHeadPiece(data); + + float c10 = 0.5f; + float c11 = 1; + float c2 = 0.8f; + float c3 = 0.6f; + + float r11 = c11; + float g11 = c11; + float b11 = c11; + + float r10 = c10; + float r2 = c2; + float r3 = c3; + + float g10 = c10; + float g2 = c2; + float g3 = c3; + + float b10 = c10; + float b2 = c2; + float b3 = c3; + + float centerBrightness = tt->getBrightness(level, x, y, z); + // render wooden underside + { + t.color(r10 * centerBrightness, g10 * centerBrightness, b10 * centerBrightness); + int tex = tt->getTexture(level, x, y, z, Facing::DOWN); + + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + + float u0 = (xt) / 256.0f; + float u1 = (xt + 16 - 0.01f) / 256.0f; + float v0 = (yt) / 256.0f; + float v1 = (yt + 16 - 0.01f) / 256.0f; + + float x0 = x + tt->xx0; + float x1 = x + tt->xx1; + float y0 = y + tt->yy0 + 3.0f / 16.0f; + float z0 = z + tt->zz0; + float z1 = z + tt->zz1; + + t.vertexUV(x0, y0, z1, u0, v1); + t.vertexUV(x0, y0, z0, u0, v0); + t.vertexUV(x1, y0, z0, u1, v0); + t.vertexUV(x1, y0, z1, u1, v1); + } + + // render bed top + + float brightness = tt->getBrightness(level, x, y + 1, z); + t.color(r11 * brightness, g11 * brightness, b11 * brightness); + + int tex = tt->getTexture(level, x, y, z, Facing::UP); + + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + + float u0 = (xt) / 256.0f; + float u1 = (xt + 16 ) / 256.0f; + float v0 = (yt) / 256.0f; + float v1 = (yt + 16) / 256.0f; + + // Default is west + float topLeftU = u0; + float topRightU = u1; + float topLeftV = v0; + float topRightV = v0; + float bottomLeftU = u0; + float bottomRightU = u1; + float bottomLeftV = v1; + float bottomRightV = v1; + + if (direction == Direction::SOUTH) { + // rotate 90 degrees clockwise + topRightU = u0; + topLeftV = v1; + bottomLeftU = u1; + bottomRightV = v0; + } else if (direction == Direction::NORTH) { + // rotate 90 degrees counter-clockwise + topLeftU = u1; + topRightV = v1; + bottomRightU = u0; + bottomLeftV = v0; + } else if (direction == Direction::EAST) { + // rotate 180 degrees + topLeftU = u1; + topRightV = v1; + bottomRightU = u0; + bottomLeftV = v0; + topRightU = u0; + topLeftV = v1; + bottomLeftU = u1; + bottomRightV = v0; + } + + float x0 = x + tt->xx0; + float x1 = x + tt->xx1; + float y1 = y + tt->yy1; + float z0 = z + tt->zz0; + float z1 = z + tt->zz1; + + t.vertexUV(x1, y1, z1, bottomLeftU, bottomLeftV); + t.vertexUV(x1, y1, z0, topLeftU, topLeftV); + t.vertexUV(x0, y1, z0, topRightU, topRightV); + t.vertexUV(x0, y1, z1, bottomRightU, bottomRightV); + + // determine which edge to skip (the one between foot and head piece) + int skipEdge = Direction::DIRECTION_FACING[direction]; + if (isHead) { + skipEdge = Direction::DIRECTION_FACING[Direction::DIRECTION_OPPOSITE[direction]]; + } + // and which edge to x-flip + int flipEdge = Facing::WEST; + switch (direction) { + case Direction::NORTH: + break; + case Direction::SOUTH: + flipEdge = Facing::EAST; + break; + case Direction::EAST: + flipEdge = Facing::NORTH; + break; + case Direction::WEST: + flipEdge = Facing::SOUTH; + break; + } + + if ((skipEdge != Facing::NORTH) && (noCulling || tt->shouldRenderFace(level, x, y, z - 1, Facing::NORTH))) { + float br = tt->getBrightness(level, x, y, z - 1); + if (tt->zz0 > 0) br = centerBrightness; + + t.color(r2 * br, g2 * br, b2 * br); + xFlipTexture = flipEdge == Facing::NORTH; + renderNorth(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 2)); + } + + if ((skipEdge != Facing::SOUTH) && (noCulling || tt->shouldRenderFace(level, x, y, z + 1, Facing::SOUTH))) { + float br = tt->getBrightness(level, x, y, z + 1); + if (tt->zz1 < 1) br = centerBrightness; + + t.color(r2 * br, g2 * br, b2 * br); + + xFlipTexture = flipEdge == Facing::SOUTH; + renderSouth(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 3)); + } + + if ((skipEdge != Facing::WEST) && (noCulling || tt->shouldRenderFace(level, x - 1, y, z, Facing::WEST))) { + float br = tt->getBrightness(level, x - 1, y, z); + if (tt->xx0 > 0) br = centerBrightness; + + t.color(r3 * br, g3 * br, b3 * br); + xFlipTexture = flipEdge == Facing::WEST; + renderWest(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 4)); + } + + if ((skipEdge != Facing::EAST) && (noCulling || tt->shouldRenderFace(level, x + 1, y, z, Facing::EAST))) { + float br = tt->getBrightness(level, x + 1, y, z); + if (tt->xx1 < 1) br = centerBrightness; + + t.color(r3 * br, g3 * br, b3 * br); + xFlipTexture = flipEdge == Facing::EAST; + renderEast(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 5)); + } + xFlipTexture = false; + return true; +} + +bool TileRenderer::tesselateStairsInWorld( StairTile* tt, int x, int y, int z ) +{ + + tt->setBaseShape(level, x, y, z); + tesselateBlockInWorld(tt, x, y, z); + + bool checkInnerPiece = tt->setStepShape(level, x, y, z); + tesselateBlockInWorld(tt, x, y, z); + + if (checkInnerPiece) { + if (tt->setInnerPieceShape(level, x, y, z)) { + tesselateBlockInWorld(tt, x, y, z); + } + } + + // setShape(0, 0, 0, 1, 1, 1); + return true; +} + +bool TileRenderer::tesselateDoorInWorld( Tile* tt, int x, int y, int z ) +{ + Tesselator& t = Tesselator::instance; + + DoorTile* dt = (DoorTile*) tt; + + bool changed = false; + float c10 = 0.5f; + float c11 = 1; + float c2 = 0.8f; + float c3 = 0.6f; + + float centerBrightness = tt->getBrightness(level, x, y, z); + + { + float br = tt->getBrightness(level, x, y - 1, z); + if (dt->yy0 > 0) br = centerBrightness; + if (Tile::lightEmission[tt->id] > 0) br = 1.0f; + t.color(c10 * br, c10 * br, c10 * br); + renderFaceDown(tt, (float)x, (float)y, (float)z, tt->getTexture(level, x, y, z, 0)); + changed = true; + } + + { + float br = tt->getBrightness(level, x, y + 1, z); + if (dt->yy1 < 1) br = centerBrightness; + if (Tile::lightEmission[tt->id] > 0) br = 1.0f; + t.color(c11 * br, c11 * br, c11 * br); + renderFaceUp(tt, (float)x, (float)y, (float)z, tt->getTexture(level, x, y, z, 1)); + changed = true; + } + + { + float br = tt->getBrightness(level, x, y, z - 1); + if (dt->zz0 > 0) br = centerBrightness; + if (Tile::lightEmission[tt->id] > 0) br = 1.0f; + t.color(c2 * br, c2 * br, c2 * br); + int tex = tt->getTexture(level, x, y, z, 2); + if (tex < 0) { + xFlipTexture = true; + tex = -tex; + } + renderNorth(tt, (float)x, (float)y, (float)z, tex); + changed = true; + xFlipTexture = false; + } + + { + float br = tt->getBrightness(level, x, y, z + 1); + if (dt->zz1 < 1) br = centerBrightness; + if (Tile::lightEmission[tt->id] > 0) br = 1.0f; + t.color(c2 * br, c2 * br, c2 * br); + int tex = tt->getTexture(level, x, y, z, 3); + if (tex < 0) { + xFlipTexture = true; + tex = -tex; + } + renderSouth(tt, (float)x, (float)y, (float)z, tex); + changed = true; + xFlipTexture = false; + } + + { + float br = tt->getBrightness(level, x - 1, y, z); + if (dt->xx0 > 0) br = centerBrightness; + if (Tile::lightEmission[tt->id] > 0) br = 1.0f; + t.color(c3 * br, c3 * br, c3 * br); + int tex = tt->getTexture(level, x, y, z, 4); + if (tex < 0) { + xFlipTexture = true; + tex = -tex; + } + renderWest(tt, (float)x, (float)y, (float)z, tex); + changed = true; + xFlipTexture = false; + } + + { + float br = tt->getBrightness(level, x + 1, y, z); + if (dt->xx1 < 1) br = centerBrightness; + if (Tile::lightEmission[tt->id] > 0) br = 1.0f; + t.color(c3 * br, c3 * br, c3 * br); + int tex = tt->getTexture(level, x, y, z, 5); + if (tex < 0) { + xFlipTexture = true; + tex = -tex; + } + renderEast(tt, (float)x, (float)y, (float)z, tex); + changed = true; + xFlipTexture = false; + } + + return changed; +} + +bool TileRenderer::tesselateRowInWorld( Tile* tt, int x, int y, int z ) { + Tesselator& t = Tesselator::instance; + float br = tt->getBrightness(level, x, y, z); + t.color(br, br, br); + tesselateRowTexture(tt, level->getData(x, y, z), float(x), y - 1 / 16.0f, float(z)); + return true; +} + +void TileRenderer::renderFaceDown( Tile* tt, float x, float y, float z, int tex ) +{ + Tesselator& t = Tesselator::instance; + + if (fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + + float u0 = (xt + tt->xx0 * 16) / 256.0f; + float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f; + float v0 = (yt + tt->zz0 * 16) / 256.0f; + float v1 = (yt + tt->zz1 * 16 - 0.01f) / 256.0f; + + if (tt->xx0 < 0 || tt->xx1 > 1) { + u0 = (xt + 0 * 15.99f) / 256.0f; + u1 = (xt + 1 * 15.99f) / 256.0f; + } + if (tt->zz0 < 0 || tt->zz1 > 1) { + v0 = (yt + 0 * 15.99f) / 256.0f; + v1 = (yt + 1 * 15.99f) / 256.0f; + } + + float x0 = x + tt->xx0; + float x1 = x + tt->xx1; + float y0 = y + tt->yy0; + float z0 = z + tt->zz0; + float z1 = z + tt->zz1; + + if (applyAmbienceOcclusion) { + t.color(c1r, c1g, c1b); + t.vertexUV(x0, y0, z1, u0, v1); + t.color(c2r, c2g, c2b); + t.vertexUV(x0, y0, z0, u0, v0); + t.color(c3r, c3g, c3b); + t.vertexUV(x1, y0, z0, u1, v0); + t.color(c4r, c4g, c4b); + t.vertexUV(x1, y0, z1, u1, v1); + } else { + t.vertexUV(x0, y0, z1, u0, v1); + t.vertexUV(x0, y0, z0, u0, v0); + t.vertexUV(x1, y0, z0, u1, v0); + t.vertexUV(x1, y0, z1, u1, v1); + } +} + +void TileRenderer::renderFaceUp( Tile* tt, float x, float y, float z, int tex ) +{ + Tesselator& t = Tesselator::instance; + + if (fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + + float u0 = (xt + tt->xx0 * 16) / 256.0f; + float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f; + float v0 = (yt + tt->zz0 * 16) / 256.0f; + float v1 = (yt + tt->zz1 * 16 - 0.01f) / 256.0f; + + if (tt->xx0 < 0 || tt->xx1 > 1) { + u0 = (xt + 0 * 15.99f) / 256.0f; + u1 = (xt + 1 * 15.99f) / 256.0f; + } + if (tt->zz0 < 0 || tt->zz1 > 1) { + v0 = (yt + 0 * 15.99f) / 256.0f; + v1 = (yt + 1 * 15.99f) / 256.0f; + } + + float x0 = x + tt->xx0; + float x1 = x + tt->xx1; + float y1 = y + tt->yy1; + float z0 = z + tt->zz0; + float z1 = z + tt->zz1; + + if (applyAmbienceOcclusion) { + t.color(c1r, c1g, c1b); + t.vertexUV(x1, y1, z1, u1, v1); + t.color(c2r, c2g, c2b); + t.vertexUV(x1, y1, z0, u1, v0); + t.color(c3r, c3g, c3b); + t.vertexUV(x0, y1, z0, u0, v0); + t.color(c4r, c4g, c4b); + t.vertexUV(x0, y1, z1, u0, v1); + } else { + t.vertexUV(x1, y1, z1, u1, v1); + t.vertexUV(x1, y1, z0, u1, v0); + t.vertexUV(x0, y1, z0, u0, v0); + t.vertexUV(x0, y1, z1, u0, v1); + } +} + +void TileRenderer::renderNorth( Tile* tt, float x, float y, float z, int tex ) +{ + Tesselator& t = Tesselator::instance; + + if (fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + float u0 = (xt + tt->xx0 * 16) / 256.0f; + float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f; + float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f; + float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f; + if (xFlipTexture) { + float tmp = u0; + u0 = u1; + u1 = tmp; + } + + if (tt->xx0 < 0 || tt->xx1 > 1) { + u0 = (xt + 0 * 15.99f) / 256.0f; + u1 = (xt + 1 * 15.99f) / 256.0f; + } + if (tt->yy0 < 0 || tt->yy1 > 1) { + v0 = (yt + 0 * 15.99f) / 256.0f; + v1 = (yt + 1 * 15.99f) / 256.0f; + } + + float x0 = x + tt->xx0; + float x1 = x + tt->xx1; + float y0 = y + tt->yy0; + float y1 = y + tt->yy1; + float z0 = z + tt->zz0; + + if (applyAmbienceOcclusion) { + t.color(c1r, c1g, c1b); + t.vertexUV(x0, y1, z0, u1, v0); + t.color(c2r, c2g, c2b); + t.vertexUV(x1, y1, z0, u0, v0); + t.color(c3r, c3g, c3b); + t.vertexUV(x1, y0, z0, u0, v1); + t.color(c4r, c4g, c4b); + t.vertexUV(x0, y0, z0, u1, v1); + } else { + t.vertexUV(x0, y1, z0, u1, v0); + t.vertexUV(x1, y1, z0, u0, v0); + t.vertexUV(x1, y0, z0, u0, v1); + t.vertexUV(x0, y0, z0, u1, v1); + } +} + +void TileRenderer::renderSouth( Tile* tt, float x, float y, float z, int tex ) +{ + Tesselator& t = Tesselator::instance; + + if (fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + + float u0 = (xt + tt->xx0 * 16) / 256.0f; + float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f; + float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f; + float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f; + if (xFlipTexture) { + float tmp = u0; + u0 = u1; + u1 = tmp; + } + + if (tt->xx0 < 0 || tt->xx1 > 1) { + u0 = (xt + 0 * 15.99f) / 256.0f; + u1 = (xt + 1 * 15.99f) / 256.0f; + } + if (tt->yy0 < 0 || tt->yy1 > 1) { + v0 = (yt + 0 * 15.99f) / 256.0f; + v1 = (yt + 1 * 15.99f) / 256.0f; + } + + float x0 = x + tt->xx0; + float x1 = x + tt->xx1; + float y0 = y + tt->yy0; + float y1 = y + tt->yy1; + float z1 = z + tt->zz1; + + if (applyAmbienceOcclusion) { + t.color(c1r, c1g, c1b); + t.vertexUV(x0, y1, z1, u0, v0); + t.color(c2r, c2g, c2b); + t.vertexUV(x0, y0, z1, u0, v1); + t.color(c3r, c3g, c3b); + t.vertexUV(x1, y0, z1, u1, v1); + t.color(c4r, c4g, c4b); + t.vertexUV(x1, y1, z1, u1, v0); + } else { + t.vertexUV(x0, y1, z1, u0, v0); + t.vertexUV(x0, y0, z1, u0, v1); + t.vertexUV(x1, y0, z1, u1, v1); + t.vertexUV(x1, y1, z1, u1, v0); + } +} + +void TileRenderer::renderWest( Tile* tt, float x, float y, float z, int tex ) +{ + Tesselator& t = Tesselator::instance; + + if (fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + + float u0 = (xt + tt->zz0 * 16) / 256.0f; + float u1 = (xt + tt->zz1 * 16 - 0.01f) / 256.0f; + float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f; + float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f; + if (xFlipTexture) { + float tmp = u0; + u0 = u1; + u1 = tmp; + } + + if (tt->zz0 < 0 || tt->zz1 > 1) { + u0 = (xt + 0 * 15.99f) / 256.0f; + u1 = (xt + 1 * 15.99f) / 256.0f; + } + if (tt->yy0 < 0 || tt->yy1 > 1) { + v0 = (yt + 0 * 15.99f) / 256.0f; + v1 = (yt + 1 * 15.99f) / 256.0f; + } + + float x0 = x + tt->xx0; + float y0 = y + tt->yy0; + float y1 = y + tt->yy1; + float z0 = z + tt->zz0; + float z1 = z + tt->zz1; + + if (applyAmbienceOcclusion) { + t.color(c1r, c1g, c1b); + t.vertexUV(x0, y1, z1, u1, v0); + t.color(c2r, c2g, c2b); + t.vertexUV(x0, y1, z0, u0, v0); + t.color(c3r, c3g, c3b); + t.vertexUV(x0, y0, z0, u0, v1); + t.color(c4r, c4g, c4b); + t.vertexUV(x0, y0, z1, u1, v1); + } else { + t.vertexUV(x0, y1, z1, u1, v0); + t.vertexUV(x0, y1, z0, u0, v0); + t.vertexUV(x0, y0, z0, u0, v1); + t.vertexUV(x0, y0, z1, u1, v1); + } +} + +void TileRenderer::renderEast( Tile* tt, float x, float y, float z, int tex ) +{ + Tesselator& t = Tesselator::instance; + + if (fixedTexture >= 0) tex = fixedTexture; + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + + float u0 = (xt + tt->zz0 * 16) / 256.0f; + float u1 = (xt + tt->zz1 * 16 - 0.01f) / 256.0f; + float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f; + float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f; + if (xFlipTexture) { + float tmp = u0; + u0 = u1; + u1 = tmp; + } + + if (tt->zz0 < 0 || tt->zz1 > 1) { + u0 = (xt + 0 * 15.99f) / 256.0f; + u1 = (xt + 1 * 15.99f) / 256.0f; + } + if (tt->yy0 < 0 || tt->yy1 > 1) { + v0 = (yt + 0 * 15.99f) / 256.0f; + v1 = (yt + 1 * 15.99f) / 256.0f; + } + + float x1 = x + tt->xx1; + float y0 = y + tt->yy0; + float y1 = y + tt->yy1; + float z0 = z + tt->zz0; + float z1 = z + tt->zz1; + + if (applyAmbienceOcclusion) { + t.color(c1r, c1g, c1b); + t.vertexUV(x1, y0, z1, u0, v1); + t.color(c2r, c2g, c2b); + t.vertexUV(x1, y0, z0, u1, v1); + t.color(c3r, c3g, c3b); + t.vertexUV(x1, y1, z0, u1, v0); + t.color(c4r, c4g, c4b); + t.vertexUV(x1, y1, z1, u0, v0); + } else { + t.vertexUV(x1, y0, z1, u0, v1); + t.vertexUV(x1, y0, z0, u1, v1); + t.vertexUV(x1, y1, z0, u1, v0); + t.vertexUV(x1, y1, z1, u0, v0); + } +} + +void TileRenderer::renderTile( Tile* tile, int data ) +{ + Tesselator& t = Tesselator::instance; + + t.color(0xff, 0xff, 0xff); + int shape = tile->getRenderShape(); + + if (shape == Tile::SHAPE_BLOCK) { + tile->updateDefaultShape(); + t.addOffset(-0.5f, -0.5f, -0.5f); + t.begin(); + renderFaceDown(tile, 0, 0, 0, tile->getTexture(0, data)); + renderFaceUp(tile, 0, 0, 0, tile->getTexture(1, data)); + renderNorth(tile, 0, 0, 0, tile->getTexture(2, data)); + renderSouth(tile, 0, 0, 0, tile->getTexture(3, data)); + renderWest(tile, 0, 0, 0, tile->getTexture(4, data)); + renderEast(tile, 0, 0, 0, tile->getTexture(5, data)); + t.draw(); + + t.addOffset(0.5f, 0.5f, 0.5f); + + } else if (shape == Tile::SHAPE_CROSS_TEXTURE) { + t.begin(); + tesselateCrossTexture(tile, data, -0.5f, -0.5f, -0.5f); + t.draw(); + } else if(shape == Tile::SHAPE_STEM) { + t.begin(); + tile->updateDefaultShape(); + tesselateStemTexture(tile, data, tile->yy1, -0.5f, -0.5f, -0.5f); + t.draw(); + } else if (shape == Tile::SHAPE_CACTUS) { + tile->updateDefaultShape(); + t.offset(-0.5f, -0.5f, -0.5f); + float s = 1 / 16.0f; + t.begin(); + renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); + renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); + t.addOffset(0, 0, s); + renderNorth(tile, 0, 0, 0, tile->getTexture(2)); + t.addOffset(0, 0, -s); + t.addOffset(0, 0, -s); + renderSouth(tile, 0, 0, 0, tile->getTexture(3)); + t.addOffset(0, 0, s); + t.addOffset(s, 0, 0); + renderWest(tile, 0, 0, 0, tile->getTexture(4)); + t.addOffset(-s, 0, 0); + t.addOffset(-s, 0, 0); + renderEast(tile, 0, 0, 0, tile->getTexture(5)); + t.addOffset(s, 0, 0); + t.draw(); + t.offset(0, 0, 0);//0.5f, 0.5f, 0.5f); + } else if (shape == Tile::SHAPE_ROWS) { + t.begin(); + t.normal(0, -1, 0); + tesselateRowTexture(tile, data, -0.5f, -0.5f, -0.5f); + //} else if (shape == Tile::SHAPE_TORCH) { + //// t.begin(); + //// t.normal(0, -1, 0); + //// tesselateTorch(tile, -0.5f, -0.5f, -0.5f, 0, 0); + //// t.end(); + } else if (shape == Tile::SHAPE_ENTITYTILE_ANIMATED) { + EntityTileRenderer::instance->render(tile, data, 1.0f); + //glEnable(GL_RESCALE_NORMAL); + } else if (shape == Tile::SHAPE_STAIRS) { + t.addOffset(-0.5f, -0.5f, -0.5f); + t.begin(); + for (int i = 0; i < 2; i++) { + if (i == 0) tile->setShape(0, 0, 0, 1, 1, 0.5f); + if (i == 1) tile->setShape(0, 0, 0.5f, 1, 0.5f, 1); + + renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); + renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); + renderNorth(tile, 0, 0, 0, tile->getTexture(2)); + renderSouth(tile, 0, 0, 0, tile->getTexture(3)); + renderWest(tile, 0, 0, 0, tile->getTexture(4)); + renderEast(tile, 0, 0, 0, tile->getTexture(5)); + } + t.draw(); + t.addOffset(0.5f, 0.5f, 0.5f); + } + else if (shape == Tile::SHAPE_FENCE) { + t.addOffset(-0.5f, -0.5f, -0.5f); + t.begin(); + for (int i = 0; i < 4; i++) { + float w = 2 / 16.0f; + if (i == 0) tile->setShape(0.5f - w, 0, 0, 0.5f + w, 1, w * 2); + if (i == 1) tile->setShape(0.5f - w, 0, 1 - w * 2, 0.5f + w, 1, 1); + w = 1 / 16.0f; + if (i == 2) tile->setShape(0.5f - w, 1 - w * 3, -w * 2, 0.5f + w, 1 - w, 1 + w * 2); + if (i == 3) tile->setShape(0.5f - w, 0.5f - w * 3, -w * 2, 0.5f + w, 0.5f - w, 1 + w * 2); + + renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); + renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); + renderNorth(tile, 0, 0, 0, tile->getTexture(2)); + renderSouth(tile, 0, 0, 0, tile->getTexture(3)); + renderWest(tile, 0, 0, 0, tile->getTexture(4)); + renderEast(tile, 0, 0, 0, tile->getTexture(5)); + } + t.draw(); + t.addOffset(0.5f, 0.5f, 0.5f); + tile->setShape(0, 0, 0, 1, 1, 1); + } else if (shape == Tile::SHAPE_FENCE_GATE) { + t.addOffset(-0.5f, -0.5f, -0.5f); + t.begin(); + for (int i = 0; i < 3; i++) { + float w = 1 / 16.0f; + if (i == 0) tile->setShape(0.5f - w, .3f, 0, 0.5f + w, 1, w * 2); + if (i == 1) tile->setShape(0.5f - w, .3f, 1 - w * 2, 0.5f + w, 1, 1); + if (i == 2) tile->setShape(0.5f - w, .5f, w * 2, 0.5f + w, 1 - w, 1 - w * 2); + + renderFaceUp(tile, 0, 0, 0, tile->getTexture(0)); + renderFaceDown(tile, 0, 0, 0, tile->getTexture(1)); + renderNorth(tile, 0, 0, 0, tile->getTexture(2)); + renderSouth(tile, 0, 0, 0, tile->getTexture(3)); + renderWest(tile, 0, 0, 0, tile->getTexture(4)); + renderEast(tile, 0, 0, 0, tile->getTexture(5)); + } + t.draw(); + t.addOffset(0.5f, 0.5f, 0.5f); + tile->setShape(0, 0, 0, 1, 1, 1); + } + +} + +bool TileRenderer::canRender( int renderShape ) +{ + if (renderShape == Tile::SHAPE_BLOCK) return true; + if (renderShape == Tile::SHAPE_CACTUS) return true; + if (renderShape == Tile::SHAPE_STAIRS) return true; + if (renderShape == Tile::SHAPE_FENCE) return true; + if (renderShape == Tile::SHAPE_FENCE_GATE) return true; + //if (renderShape == Tile::SHAPE_CROSS_TEXTURE) return true; + //if (renderShape == Tile::SHAPE_ENTITYTILE_ANIMATED) return true; + + return false; +} + +void TileRenderer::renderGuiTile( Tile* tile, int data ) +{ + Tesselator& t = Tesselator::instance; + + int shape = tile->getRenderShape(); + + if (shape == Tile::SHAPE_BLOCK) { + tile->updateDefaultShape(); + + t.begin(); + t.addOffset(-0.5f, -0.5f, -0.5f); + t.color(0xff, 0xff, 0xff); + renderFaceUp(tile, 0, 0, 0, tile->getTexture(1, data)); + renderFaceDown(tile, 0, 0, 0, tile->getTexture(0, data)); + + t.color(0x80, 0x80, 0x80); + renderNorth(tile, 0, 0, 0, tile->getTexture(2, data)); + renderSouth(tile, 0, 0, 0, tile->getTexture(3, data)); + + t.color(0xbb, 0xbb, 0xbb); + renderEast(tile, 0, 0, 0, tile->getTexture(5, data)); + renderWest(tile, 0, 0, 0, tile->getTexture(4, data)); + t.draw(); + t.addOffset(0.5f, 0.5f, 0.5f); + + } else if (shape == Tile::SHAPE_CROSS_TEXTURE) { + t.begin(); + //t.normal(0, -1, 0); + tesselateCrossTexture(tile, data, -0.5f, -0.5f, -0.5f); + //t.end(); + t.draw(); + } else if (shape == Tile::SHAPE_CACTUS) { + tile->updateDefaultShape(); + t.begin(); + t.offset(-0.5f, -0.5f, -0.5f); + float s = 1 / 16.0f; + t.color(0xff, 0xff, 0xff); + renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); + renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); + + t.color(0x80, 0x80, 0x80); + t.addOffset(0, 0, s); + renderNorth(tile, 0, 0, 0, tile->getTexture(2)); + t.addOffset(0, 0, -s-s); + renderSouth(tile, 0, 0, 0, tile->getTexture(3)); + + t.color(0xbb, 0xbb, 0xbb); + t.addOffset(s, 0, s); + renderWest(tile, 0, 0, 0, tile->getTexture(4)); + t.addOffset(-s-s, 0, 0); + renderEast(tile, 0, 0, 0, tile->getTexture(5)); + + t.draw(); + t.addOffset(s+0.5f, 0.5f, 0.5f); + } else if (shape == Tile::SHAPE_STAIRS) { + t.offset(-0.5f, -0.5f, -0.5f); + t.begin(); + for (int i = 0; i < 2; i++) { + if (i == 0) tile->setShape(0, 0, 0, 1, 1, 0.5f); + if (i == 1) tile->setShape(0, 0, 0.5f, 1, 0.5f, 1); + + t.color(0xff, 0xff, 0xff); + renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); + renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); + + t.color(0x80, 0x80, 0x80); + renderNorth(tile, 0, 0, 0, tile->getTexture(2)); + renderSouth(tile, 0, 0, 0, tile->getTexture(3)); + + t.color(0xbb, 0xbb, 0xbb); + renderWest(tile, 0, 0, 0, tile->getTexture(4)); + renderEast(tile, 0, 0, 0, tile->getTexture(5)); + } + t.draw(); + t.offset(0, 0, 0); + } + else if (shape == Tile::SHAPE_FENCE) { + t.addOffset(-0.5f, -0.5f, -0.5f); + t.begin(); + for (int i = 0; i < 4; i++) { + float w = 2 / 16.0f; + if (i == 0) tile->setShape(0.5f - w, 0, 0, 0.5f + w, 1, w * 2); + if (i == 1) tile->setShape(0.5f - w, 0, 1 - w * 2, 0.5f + w, 1, 1); + w = 1 / 16.0f; + if (i == 2) tile->setShape(0.5f - w, 1 - w * 3, -w * 2, 0.5f + w, 1 - w, 1 + w * 2); + if (i == 3) tile->setShape(0.5f - w, 0.5f - w * 3, -w * 2, 0.5f + w, 0.5f - w, 1 + w * 2); + + t.color(0xff, 0xff, 0xff); + + renderFaceDown(tile, 0, 0, 0, tile->getTexture(0)); + renderFaceUp(tile, 0, 0, 0, tile->getTexture(1)); + + t.color(0x80, 0x80, 0x80); + renderNorth(tile, 0, 0, 0, tile->getTexture(2)); + renderSouth(tile, 0, 0, 0, tile->getTexture(3)); + + t.color(0xbb, 0xbb, 0xbb); + renderWest(tile, 0, 0, 0, tile->getTexture(4)); + renderEast(tile, 0, 0, 0, tile->getTexture(5)); + } + t.draw(); + t.addOffset(0.5f, 0.5f, 0.5f); + tile->setShape(0, 0, 0, 1, 1, 1); + } + else if (shape == Tile::SHAPE_FENCE_GATE) { + t.addOffset(-0.5f, -0.5f, -0.5f); + t.begin(); + for (int i = 0; i < 3; i++) { + float w = 1 / 16.0f; + if (i == 0) tile->setShape(0.5f - w, .3f, 0, 0.5f + w, 1, w * 2); + if (i == 1) tile->setShape(0.5f - w, .3f, 1 - w * 2, 0.5f + w, 1, 1); + w = 1 / 16.0f; + if (i == 2) tile->setShape(0.5f - w, .5f, 0, 0.5f + w, 1 - w, 1); + + t.color(0xff, 0xff, 0xff); + renderFaceUp(tile, 0, 0, 0, tile->getTexture(0)); + renderFaceDown(tile, 0, 0, 0, tile->getTexture(1)); + + t.color(0x80, 0x80, 0x80); + renderNorth(tile, 0, 0, 0, tile->getTexture(2)); + renderSouth(tile, 0, 0, 0, tile->getTexture(3)); + + t.color(0xbb, 0xbb, 0xbb); + renderWest(tile, 0, 0, 0, tile->getTexture(4)); + renderEast(tile, 0, 0, 0, tile->getTexture(5)); + } + t.draw(); + tile->setShape(0, 0, 0, 1, 1, 1); + t.addOffset(0.5f, 0.5f, 0.5f); + } +} + +bool TileRenderer::tesselateThinFenceInWorld(ThinFenceTile* tt, int x, int y, int z) { + const int depth = 128; + Tesselator& t = Tesselator::instance; + + float br = tt->getBrightness(level, x, y, z); + int col = tt->getColor(level, x, y, z); + float r = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + //if (GameRenderer::anaglyph3d) { + // float cr = (r * 30 + g * 59 + b * 11) / 100; + // float cg = (r * 30 + g * 70) / (100); + // float cb = (r * 30 + b * 70) / (100); + + // r = cr; + // g = cg; + // b = cb; + //} + t.color(br * r, br * g, br * b); + + int tex = 0; + int edgeTex = 0; + + if (fixedTexture >= 0) { + tex = fixedTexture; + edgeTex = fixedTexture; + } else { + int data = level->getData(x, y, z); + tex = tt->getTexture(0, data); + edgeTex = tt->getEdgeTexture(); + } + + const int xt = (tex & 0xf) << 4; + const int yt = tex & 0xf0; + float u0 = (xt) / 256.0f; + const float u1 = (xt + 7.99f) / 256.0f; + const float u2 = (xt + 15.99f) / 256.0f; + const float v0 = (yt) / 256.0f; + const float v2 = (yt + 15.99f) / 256.0f; + + const int xet = (edgeTex & 0xf) << 4; + const int yet = edgeTex & 0xf0; + + const float iu0 = (xet + 7) / 256.0f; + const float iu1 = (xet + 8.99f) / 256.0f; + const float iv0 = (yet) / 256.0f; + const float iv1 = (yet + 8) / 256.0f; + const float iv2 = (yet + 15.99f) / 256.0f; + + const float x0 = (float)x; + const float x1 = x0 + .5f; + const float x2 = x0 + 1; + const float y0 = (float)y + 0.001f; + const float y1 = y0 + 1 - 0.002f; + const float z0 = (float)z; + const float z1 = z0 + .5f; + const float z2 = z0 + 1; + const float ix0 = x0 + .5f - 1.0f / 16.0f; + const float ix1 = x0 + .5f + 1.0f / 16.0f; + const float iz0 = z0 + .5f - 1.0f / 16.0f; + const float iz1 = z0 + .5f + 1.0f / 16.0f; + + const bool n = tt->attachsTo(level->getTile(x, y, z - 1)); + const bool s = tt->attachsTo(level->getTile(x, y, z + 1)); + const bool w = tt->attachsTo(level->getTile(x - 1, y, z)); + const bool e = tt->attachsTo(level->getTile(x + 1, y, z)); + + const bool up = tt->shouldRenderFace(level, x, y + 1, z, Facing::UP); + const bool down = tt->shouldRenderFace(level, x, y - 1, z, Facing::DOWN); + + const float noZFightingOffset = 0.01f; + + if ((w && e) || (!w && !e && !n && !s)) { + t.vertexUV(x0, y1, z1, u0, v0); + t.vertexUV(x0, y0, z1, u0, v2); + t.vertexUV(x2, y0, z1, u2, v2); + t.vertexUV(x2, y1, z1, u2, v0); + + t.vertexUV(x2, y1, z1, u0, v0); + t.vertexUV(x2, y0, z1, u0, v2); + t.vertexUV(x0, y0, z1, u2, v2); + t.vertexUV(x0, y1, z1, u2, v0); + + if (up) { + // small edge texture + t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv0); + t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv2); + + t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv0); + t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv2); + } else { + if (y < (depth - 1) && level->isEmptyTile(x - 1, y + 1, z)) { + t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv2); + t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv1); + + t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv2); + t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1); + } + if (y < (depth - 1) && level->isEmptyTile(x + 1, y + 1, z)) { + t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv1); + t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv0); + + t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1); + t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv0); + } + } + if (down) { + // small edge texture + t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv0); + t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv2); + + t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv0); + t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv2); + } else { + if (y > 1 && level->isEmptyTile(x - 1, y - 1, z)) { + t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv2); + t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv1); + + t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv2); + t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1); + } + if (y > 1 && level->isEmptyTile(x + 1, y - 1, z)) { + t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv1); + t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv0); + + t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1); + t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv0); + } + } + + } else if (w && !e) { + // half-step towards west + t.vertexUV(x0, y1, z1, u0, v0); + t.vertexUV(x0, y0, z1, u0, v2); + t.vertexUV(x1, y0, z1, u1, v2); + t.vertexUV(x1, y1, z1, u1, v0); + + t.vertexUV(x1, y1, z1, u0, v0); + t.vertexUV(x1, y0, z1, u0, v2); + t.vertexUV(x0, y0, z1, u1, v2); + t.vertexUV(x0, y1, z1, u1, v0); + + // small edge texture + if (!s && !n) { + t.vertexUV(x1, y1, iz1, iu0, iv0); + t.vertexUV(x1, y0, iz1, iu0, iv2); + t.vertexUV(x1, y0, iz0, iu1, iv2); + t.vertexUV(x1, y1, iz0, iu1, iv0); + + t.vertexUV(x1, y1, iz0, iu0, iv0); + t.vertexUV(x1, y0, iz0, iu0, iv2); + t.vertexUV(x1, y0, iz1, iu1, iv2); + t.vertexUV(x1, y1, iz1, iu1, iv0); + } + + if (up || (y < (depth - 1) && level->isEmptyTile(x - 1, y + 1, z))) { + // small edge texture + t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv2); + t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv1); + + t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv2); + t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1); + } + if (down || (y > 1 && level->isEmptyTile(x - 1, y - 1, z))) { + // small edge texture + t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv2); + t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv1); + + t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv2); + t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv2); + t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1); + } + + } else if (!w && e) { + // half-step towards east + t.vertexUV(x1, y1, z1, u1, v0); + t.vertexUV(x1, y0, z1, u1, v2); + t.vertexUV(x2, y0, z1, u2, v2); + t.vertexUV(x2, y1, z1, u2, v0); + + t.vertexUV(x2, y1, z1, u1, v0); + t.vertexUV(x2, y0, z1, u1, v2); + t.vertexUV(x1, y0, z1, u2, v2); + t.vertexUV(x1, y1, z1, u2, v0); + + // small edge texture + if (!s && !n) { + t.vertexUV(x1, y1, iz0, iu0, iv0); + t.vertexUV(x1, y0, iz0, iu0, iv2); + t.vertexUV(x1, y0, iz1, iu1, iv2); + t.vertexUV(x1, y1, iz1, iu1, iv0); + + t.vertexUV(x1, y1, iz1, iu0, iv0); + t.vertexUV(x1, y0, iz1, iu0, iv2); + t.vertexUV(x1, y0, iz0, iu1, iv2); + t.vertexUV(x1, y1, iz0, iu1, iv0); + } + + if (up || (y < (depth - 1) && level->isEmptyTile(x + 1, y + 1, z))) { + // small edge texture + t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv1); + t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv0); + + t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1); + t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv0); + } + if (down || (y > 1 && level->isEmptyTile(x + 1, y - 1, z))) { + // small edge texture + t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv1); + t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv0); + + t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv0); + t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1); + t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1); + t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv0); + } + + } + + if ((n && s) || (!w && !e && !n && !s)) { + // straight north-south + t.vertexUV(x1, y1, z2, u0, v0); + t.vertexUV(x1, y0, z2, u0, v2); + t.vertexUV(x1, y0, z0, u2, v2); + t.vertexUV(x1, y1, z0, u2, v0); + + t.vertexUV(x1, y1, z0, u0, v0); + t.vertexUV(x1, y0, z0, u0, v2); + t.vertexUV(x1, y0, z2, u2, v2); + t.vertexUV(x1, y1, z2, u2, v0); + + if (up) { + // small edge texture + t.vertexUV(ix1, y1, z2, iu1, iv2); + t.vertexUV(ix1, y1, z0, iu1, iv0); + t.vertexUV(ix0, y1, z0, iu0, iv0); + t.vertexUV(ix0, y1, z2, iu0, iv2); + + t.vertexUV(ix1, y1, z0, iu1, iv2); + t.vertexUV(ix1, y1, z2, iu1, iv0); + t.vertexUV(ix0, y1, z2, iu0, iv0); + t.vertexUV(ix0, y1, z0, iu0, iv2); + } else { + if (y < (depth - 1) && level->isEmptyTile(x, y + 1, z - 1)) { + t.vertexUV(ix0, y1, z0, iu1, iv0); + t.vertexUV(ix0, y1, z1, iu1, iv1); + t.vertexUV(ix1, y1, z1, iu0, iv1); + t.vertexUV(ix1, y1, z0, iu0, iv0); + + t.vertexUV(ix0, y1, z1, iu1, iv0); + t.vertexUV(ix0, y1, z0, iu1, iv1); + t.vertexUV(ix1, y1, z0, iu0, iv1); + t.vertexUV(ix1, y1, z1, iu0, iv0); + } + if (y < (depth - 1) && level->isEmptyTile(x, y + 1, z + 1)) { + t.vertexUV(ix0, y1, z1, iu0, iv1); + t.vertexUV(ix0, y1, z2, iu0, iv2); + t.vertexUV(ix1, y1, z2, iu1, iv2); + t.vertexUV(ix1, y1, z1, iu1, iv1); + + t.vertexUV(ix0, y1, z2, iu0, iv1); + t.vertexUV(ix0, y1, z1, iu0, iv2); + t.vertexUV(ix1, y1, z1, iu1, iv2); + t.vertexUV(ix1, y1, z2, iu1, iv1); + } + } + if (down) { + // small edge texture + t.vertexUV(ix1, y0, z2, iu1, iv2); + t.vertexUV(ix1, y0, z0, iu1, iv0); + t.vertexUV(ix0, y0, z0, iu0, iv0); + t.vertexUV(ix0, y0, z2, iu0, iv2); + + t.vertexUV(ix1, y0, z0, iu1, iv2); + t.vertexUV(ix1, y0, z2, iu1, iv0); + t.vertexUV(ix0, y0, z2, iu0, iv0); + t.vertexUV(ix0, y0, z0, iu0, iv2); + } else { + if (y > 1 && level->isEmptyTile(x, y - 1, z - 1)) { + // north half-step + t.vertexUV(ix0, y0, z0, iu1, iv0); + t.vertexUV(ix0, y0, z1, iu1, iv1); + t.vertexUV(ix1, y0, z1, iu0, iv1); + t.vertexUV(ix1, y0, z0, iu0, iv0); + + t.vertexUV(ix0, y0, z1, iu1, iv0); + t.vertexUV(ix0, y0, z0, iu1, iv1); + t.vertexUV(ix1, y0, z0, iu0, iv1); + t.vertexUV(ix1, y0, z1, iu0, iv0); + } + if (y > 1 && level->isEmptyTile(x, y - 1, z + 1)) { + // south half-step + t.vertexUV(ix0, y0, z1, iu0, iv1); + t.vertexUV(ix0, y0, z2, iu0, iv2); + t.vertexUV(ix1, y0, z2, iu1, iv2); + t.vertexUV(ix1, y0, z1, iu1, iv1); + + t.vertexUV(ix0, y0, z2, iu0, iv1); + t.vertexUV(ix0, y0, z1, iu0, iv2); + t.vertexUV(ix1, y0, z1, iu1, iv2); + t.vertexUV(ix1, y0, z2, iu1, iv1); + } + } + + } else if (n && !s) { + // half-step towards north + t.vertexUV(x1, y1, z0, u0, v0); + t.vertexUV(x1, y0, z0, u0, v2); + t.vertexUV(x1, y0, z1, u1, v2); + t.vertexUV(x1, y1, z1, u1, v0); + + t.vertexUV(x1, y1, z1, u0, v0); + t.vertexUV(x1, y0, z1, u0, v2); + t.vertexUV(x1, y0, z0, u1, v2); + t.vertexUV(x1, y1, z0, u1, v0); + + // small edge texture + if (!e && !w) { + t.vertexUV(ix0, y1, z1, iu0, iv0); + t.vertexUV(ix0, y0, z1, iu0, iv2); + t.vertexUV(ix1, y0, z1, iu1, iv2); + t.vertexUV(ix1, y1, z1, iu1, iv0); + + t.vertexUV(ix1, y1, z1, iu0, iv0); + t.vertexUV(ix1, y0, z1, iu0, iv2); + t.vertexUV(ix0, y0, z1, iu1, iv2); + t.vertexUV(ix0, y1, z1, iu1, iv0); + } + + if (up || (y < (depth - 1) && level->isEmptyTile(x, y + 1, z - 1))) { + // small edge texture + t.vertexUV(ix0, y1, z0, iu1, iv0); + t.vertexUV(ix0, y1, z1, iu1, iv1); + t.vertexUV(ix1, y1, z1, iu0, iv1); + t.vertexUV(ix1, y1, z0, iu0, iv0); + + t.vertexUV(ix0, y1, z1, iu1, iv0); + t.vertexUV(ix0, y1, z0, iu1, iv1); + t.vertexUV(ix1, y1, z0, iu0, iv1); + t.vertexUV(ix1, y1, z1, iu0, iv0); + } + + if (down || (y > 1 && level->isEmptyTile(x, y - 1, z - 1))) { + // small edge texture + t.vertexUV(ix0, y0, z0, iu1, iv0); + t.vertexUV(ix0, y0, z1, iu1, iv1); + t.vertexUV(ix1, y0, z1, iu0, iv1); + t.vertexUV(ix1, y0, z0, iu0, iv0); + + t.vertexUV(ix0, y0, z1, iu1, iv0); + t.vertexUV(ix0, y0, z0, iu1, iv1); + t.vertexUV(ix1, y0, z0, iu0, iv1); + t.vertexUV(ix1, y0, z1, iu0, iv0); + } + + } else if (!n && s) { + // half-step towards south + t.vertexUV(x1, y1, z1, u1, v0); + t.vertexUV(x1, y0, z1, u1, v2); + t.vertexUV(x1, y0, z2, u2, v2); + t.vertexUV(x1, y1, z2, u2, v0); + + t.vertexUV(x1, y1, z2, u1, v0); + t.vertexUV(x1, y0, z2, u1, v2); + t.vertexUV(x1, y0, z1, u2, v2); + t.vertexUV(x1, y1, z1, u2, v0); + + // small edge texture + if (!e && !w) { + t.vertexUV(ix1, y1, z1, iu0, iv0); + t.vertexUV(ix1, y0, z1, iu0, iv2); + t.vertexUV(ix0, y0, z1, iu1, iv2); + t.vertexUV(ix0, y1, z1, iu1, iv0); + + t.vertexUV(ix0, y1, z1, iu0, iv0); + t.vertexUV(ix0, y0, z1, iu0, iv2); + t.vertexUV(ix1, y0, z1, iu1, iv2); + t.vertexUV(ix1, y1, z1, iu1, iv0); + } + + if (up || (y < (depth - 1) && level->isEmptyTile(x, y + 1, z + 1))) { + // small edge texture + t.vertexUV(ix0, y1, z1, iu0, iv1); + t.vertexUV(ix0, y1, z2, iu0, iv2); + t.vertexUV(ix1, y1, z2, iu1, iv2); + t.vertexUV(ix1, y1, z1, iu1, iv1); + + t.vertexUV(ix0, y1, z2, iu0, iv1); + t.vertexUV(ix0, y1, z1, iu0, iv2); + t.vertexUV(ix1, y1, z1, iu1, iv2); + t.vertexUV(ix1, y1, z2, iu1, iv1); + } + if (down || (y > 1 && level->isEmptyTile(x, y - 1, z + 1))) { + // small edge texture + t.vertexUV(ix0, y0, z1, iu0, iv1); + t.vertexUV(ix0, y0, z2, iu0, iv2); + t.vertexUV(ix1, y0, z2, iu1, iv2); + t.vertexUV(ix1, y0, z1, iu1, iv1); + + t.vertexUV(ix0, y0, z2, iu0, iv1); + t.vertexUV(ix0, y0, z1, iu0, iv2); + t.vertexUV(ix1, y0, z1, iu1, iv2); + t.vertexUV(ix1, y0, z2, iu1, iv1); + } + + } + + return true; +} + +void TileRenderer::tesselateRowTexture( Tile* tt, int data, float x, float y, float z ) { + Tesselator& t = Tesselator::instance; + + int tex = tt->getTexture(0, data); + if(fixedTexture >= 0) + tex = fixedTexture; + + int xt = (tex & 0xf) << 4; + int yt = tex & 0xf0; + float u0 = (xt) / 256.0f; + float u1 = (xt + 15.99f) / 256.f; + float v0 = (yt) / 256.0f; + float v1 = (yt + 15.99f) / 256.0f; + + float x0 = x + 0.5f - 0.25f; + float x1 = x + 0.5f + 0.25f; + float z0 = z + 0.5f - 0.5f; + float z1 = z + 0.5f + 0.5f; + t.vertexUV(x0, y + 1, z0, u0, v0); + t.vertexUV(x0, y + 0, z0, u0, v1); + t.vertexUV(x0, y + 0, z1, u1, v1); + t.vertexUV(x0, y + 1, z1, u1, v0); + + t.vertexUV(x0, y + 1, z1, u0, v0); + t.vertexUV(x0, y + 0, z1, u0, v1); + t.vertexUV(x0, y + 0, z0, u1, v1); + t.vertexUV(x0, y + 1, z0, u1, v0); + + t.vertexUV(x1, y + 1, z1, u0, v0); + t.vertexUV(x1, y + 0, z1, u0, v1); + t.vertexUV(x1, y + 0, z0, u1, v1); + t.vertexUV(x1, y + 1, z0, u1, v0); + + t.vertexUV(x1, y + 1, z0, u0, v0); + t.vertexUV(x1, y + 0, z0, u0, v1); + t.vertexUV(x1, y + 0, z1, u1, v1); + t.vertexUV(x1, y + 1, z1, u1, v0); + + x0 = x + 0.5f - 0.5f; + x1 = x + 0.5f + 0.5f; + z0 = z + 0.5f - 0.25f; + z1 = z + 0.5f + 0.25f; + + t.vertexUV(x0, y + 1, z0, u0, v0); + t.vertexUV(x0, y + 0, z0, u0, v1); + t.vertexUV(x1, y + 0, z0, u1, v1); + t.vertexUV(x1, y + 1, z0, u1, v0); + + t.vertexUV(x1, y + 1, z0, u0, v0); + t.vertexUV(x1, y + 0, z0, u0, v1); + t.vertexUV(x0, y + 0, z0, u1, v1); + t.vertexUV(x0, y + 1, z0, u1, v0); + + t.vertexUV(x1, y + 1, z1, u0, v0); + t.vertexUV(x1, y + 0, z1, u0, v1); + t.vertexUV(x0, y + 0, z1, u1, v1); + t.vertexUV(x0, y + 1, z1, u1, v0); + + t.vertexUV(x0, y + 1, z1, u0, v0); + t.vertexUV(x0, y + 0, z1, u0, v1); + t.vertexUV(x1, y + 0, z1, u1, v1); + t.vertexUV(x1, y + 1, z1, u1, v0); +} diff --git a/src/client/renderer/TileRenderer.h b/src/client/renderer/TileRenderer.h index 02420dd..f9ef69a 100755 --- a/src/client/renderer/TileRenderer.h +++ b/src/client/renderer/TileRenderer.h @@ -1,85 +1,85 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__TileRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__TileRenderer_H__ - -//package net.minecraft.client.renderer; - -class Tile; -class FenceTile; -class FenceGateTile; -class ThinFenceTile; -class StairTile; -class LevelSource; -class Material; - -class TileRenderer -{ -public: - TileRenderer(LevelSource* level = 0); - - void tesselateInWorld(Tile* tile, int x, int y, int z, int fixedTexture); - bool tesselateInWorld(Tile* tt, int x, int y, int z); - void tesselateInWorldNoCulling(Tile* tile, int x, int y, int z); - - bool tesselateTorchInWorld(Tile* tt, int x, int y, int z); - bool tesselateLadderInWorld(Tile* tt, int x, int y, int z); - bool tesselateCactusInWorld(Tile* tt, int x, int y, int z); - bool tesselateCactusInWorld(Tile* tt, int x, int y, int z, float r, float g, float b); - bool tesselateCrossInWorld(Tile* tt, int x, int y, int z); - bool tesselateStemInWorld(Tile* _tt, int x, int y, int z); - bool tesselateWaterInWorld(Tile* tt, int x, int y, int z); - bool tesselateStairsInWorld(StairTile* tt, int x, int y, int z); - bool tesselateDoorInWorld(Tile* tt, int x, int y, int z); - bool tesselateFenceInWorld(FenceTile* tt, int x, int y, int z); - bool tesselateThinFenceInWorld(ThinFenceTile* tt, int x, int y, int z); - bool tesselateFenceGateInWorld(FenceGateTile* tt, int x, int y, int z); - bool tesselateBedInWorld(Tile *tt, int x, int y, int z); - bool tesselateRowInWorld(Tile* tt, int x, int y, int z); - - void tesselateTorch(Tile* tt, float x, float y, float z, float xxa, float zza); - void tesselateCrossTexture(Tile* tt, int data, float x, float y, float z); - void tesselateStemTexture(Tile* tt, int data, float h, float x, float y, float z); - void tesselateStemDirTexture(Tile* tt, int data, int dir, float h, float x, float y, float z); - void tesselateRowTexture(Tile* tt, int data, float x, float y, float z); - - void renderBlock(Tile* tt, LevelSource* level, int x, int y, int z); - - /*public*/ - bool tesselateBlockInWorld(Tile* tt, int x, int y, int z); - bool tesselateBlockInWorld(Tile* tt, int x, int y, int z, float r, float g, float b); - bool tesselateBlockInWorldWithAmbienceOcclusion(Tile* tt, int pX, int pY, int pZ, float pBaseRed, float pBaseGreen, float pBaseBlue); - - void renderFaceDown(Tile* tt, float x, float y, float z, int tex); - void renderFaceUp(Tile* tt, float x, float y, float z, int tex); - void renderNorth(Tile* tt, float x, float y, float z, int tex); - void renderSouth(Tile* tt, float x, float y, float z, int tex); - void renderWest(Tile* tt, float x, float y, float z, int tex); - void renderEast(Tile* tt, float x, float y, float z, int tex); - - void renderTile(Tile* tile, int data); - void renderGuiTile(Tile* tile, int data); - - static bool canRender(int renderShape); -private: - float getWaterHeight(int x, int y, int z, const Material* m); - - LevelSource* level; - int fixedTexture; - bool xFlipTexture; - bool noCulling; - - bool applyAmbienceOcclusion; - float ll000, llx00, ll0y0, ll00z, llX00, ll0Y0, ll00Z; - float llxyz, llxy0, llxyZ, ll0yz, ll0yZ, llXyz, llXy0; - float llXyZ, llxYz, llxY0, llxYZ, ll0Yz, llXYz, llXY0; - float ll0YZ, llXYZ, llx0z, llX0z, llx0Z, llX0Z; - int blsmooth; - float c1r, c2r, c3r, c4r; - float c1g, c2g, c3g, c4g; - float c1b, c2b, c3b, c4b; - bool llTrans0Yz, llTransXY0, llTransxY0, llTrans0YZ; - bool llTransx0z, llTransX0Z, llTransx0Z, llTransX0z; - bool llTrans0yz, llTransXy0, llTransxy0, llTrans0yZ; -}; - - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__TileRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__TileRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__TileRenderer_H__ + +//package net.minecraft.client.renderer; + +class Tile; +class FenceTile; +class FenceGateTile; +class ThinFenceTile; +class StairTile; +class LevelSource; +class Material; + +class TileRenderer +{ +public: + TileRenderer(LevelSource* level = 0); + + void tesselateInWorld(Tile* tile, int x, int y, int z, int fixedTexture); + bool tesselateInWorld(Tile* tt, int x, int y, int z); + void tesselateInWorldNoCulling(Tile* tile, int x, int y, int z); + + bool tesselateTorchInWorld(Tile* tt, int x, int y, int z); + bool tesselateLadderInWorld(Tile* tt, int x, int y, int z); + bool tesselateCactusInWorld(Tile* tt, int x, int y, int z); + bool tesselateCactusInWorld(Tile* tt, int x, int y, int z, float r, float g, float b); + bool tesselateCrossInWorld(Tile* tt, int x, int y, int z); + bool tesselateStemInWorld(Tile* _tt, int x, int y, int z); + bool tesselateWaterInWorld(Tile* tt, int x, int y, int z); + bool tesselateStairsInWorld(StairTile* tt, int x, int y, int z); + bool tesselateDoorInWorld(Tile* tt, int x, int y, int z); + bool tesselateFenceInWorld(FenceTile* tt, int x, int y, int z); + bool tesselateThinFenceInWorld(ThinFenceTile* tt, int x, int y, int z); + bool tesselateFenceGateInWorld(FenceGateTile* tt, int x, int y, int z); + bool tesselateBedInWorld(Tile *tt, int x, int y, int z); + bool tesselateRowInWorld(Tile* tt, int x, int y, int z); + + void tesselateTorch(Tile* tt, float x, float y, float z, float xxa, float zza); + void tesselateCrossTexture(Tile* tt, int data, float x, float y, float z); + void tesselateStemTexture(Tile* tt, int data, float h, float x, float y, float z); + void tesselateStemDirTexture(Tile* tt, int data, int dir, float h, float x, float y, float z); + void tesselateRowTexture(Tile* tt, int data, float x, float y, float z); + + void renderBlock(Tile* tt, LevelSource* level, int x, int y, int z); + + /*public*/ + bool tesselateBlockInWorld(Tile* tt, int x, int y, int z); + bool tesselateBlockInWorld(Tile* tt, int x, int y, int z, float r, float g, float b); + bool tesselateBlockInWorldWithAmbienceOcclusion(Tile* tt, int pX, int pY, int pZ, float pBaseRed, float pBaseGreen, float pBaseBlue); + + void renderFaceDown(Tile* tt, float x, float y, float z, int tex); + void renderFaceUp(Tile* tt, float x, float y, float z, int tex); + void renderNorth(Tile* tt, float x, float y, float z, int tex); + void renderSouth(Tile* tt, float x, float y, float z, int tex); + void renderWest(Tile* tt, float x, float y, float z, int tex); + void renderEast(Tile* tt, float x, float y, float z, int tex); + + void renderTile(Tile* tile, int data); + void renderGuiTile(Tile* tile, int data); + + static bool canRender(int renderShape); +private: + float getWaterHeight(int x, int y, int z, const Material* m); + + LevelSource* level; + int fixedTexture; + bool xFlipTexture; + bool noCulling; + + bool applyAmbienceOcclusion; + float ll000, llx00, ll0y0, ll00z, llX00, ll0Y0, ll00Z; + float llxyz, llxy0, llxyZ, ll0yz, ll0yZ, llXyz, llXy0; + float llXyZ, llxYz, llxY0, llxYZ, ll0Yz, llXYz, llXY0; + float ll0YZ, llXYZ, llx0z, llX0z, llx0Z, llX0Z; + int blsmooth; + float c1r, c2r, c3r, c4r; + float c1g, c2g, c3g, c4g; + float c1b, c2b, c3b, c4b; + bool llTrans0Yz, llTransXY0, llTransxY0, llTrans0YZ; + bool llTransx0z, llTransX0Z, llTransx0Z, llTransX0z; + bool llTrans0yz, llTransXy0, llTransxy0, llTrans0yZ; +}; + + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__TileRenderer_H__*/ diff --git a/src/client/renderer/VertecDecl.h b/src/client/renderer/VertecDecl.h index 8abc1d3..683ae80 100755 --- a/src/client/renderer/VertecDecl.h +++ b/src/client/renderer/VertecDecl.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__VertexDecl_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__VertexDecl_H__ - -typedef struct VertexDeclPTC -{ - GLfloat x, y, z; - GLfloat u, v; - GLuint color; - -} VertexDeclPTC; - -typedef struct VertexDeclPTCN -{ - GLfloat x, y, z; - GLfloat u, v; - GLuint color; - GLuint normal; - -} VertexDeclPTCN; - -#endif /*#ifndef NET_MINECRAFT_CLIENT_RENDERER__VertexDecl_H__ */ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__VertexDecl_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__VertexDecl_H__ + +typedef struct VertexDeclPTC +{ + GLfloat x, y, z; + GLfloat u, v; + GLuint color; + +} VertexDeclPTC; + +typedef struct VertexDeclPTCN +{ + GLfloat x, y, z; + GLfloat u, v; + GLuint color; + GLuint normal; + +} VertexDeclPTCN; + +#endif /*#ifndef NET_MINECRAFT_CLIENT_RENDERER__VertexDecl_H__ */ diff --git a/src/client/renderer/culling/AllowAllCuller.h b/src/client/renderer/culling/AllowAllCuller.h index 9d408c9..451ff8e 100755 --- a/src/client/renderer/culling/AllowAllCuller.h +++ b/src/client/renderer/culling/AllowAllCuller.h @@ -1,29 +1,29 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__AllowAllCuller_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__AllowAllCuller_H__ - -//package net.minecraft.client.renderer.culling; - -#include "Culler.h" - -class AABB; - -class AllowAllCuller: public Culler -{ -public: - bool isVisible(const AABB& bb) { - return true; - } - - bool cubeFullyInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) { - return true; - } - - bool cubeInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) { - return true; - } - - void prepare(float xOff, float yOff, float zOff) { - } -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__AllowAllCuller_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__AllowAllCuller_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__AllowAllCuller_H__ + +//package net.minecraft.client.renderer.culling; + +#include "Culler.h" + +class AABB; + +class AllowAllCuller: public Culler +{ +public: + bool isVisible(const AABB& bb) { + return true; + } + + bool cubeFullyInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) { + return true; + } + + bool cubeInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) { + return true; + } + + void prepare(float xOff, float yOff, float zOff) { + } +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__AllowAllCuller_H__*/ diff --git a/src/client/renderer/culling/Culler.h b/src/client/renderer/culling/Culler.h index bfd8c03..fb09665 100755 --- a/src/client/renderer/culling/Culler.h +++ b/src/client/renderer/culling/Culler.h @@ -1,22 +1,22 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__Culler_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__Culler_H__ - -//package net.minecraft.client.renderer.culling; - -class AABB; - -class Culler -{ -public: - virtual ~Culler() {} - - virtual bool isVisible(const AABB& bb) = 0; - - virtual bool cubeInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) = 0; - - virtual bool cubeFullyInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) = 0; - - virtual void prepare(float xOff, float yOff, float zOff) {} -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__Culler_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__Culler_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__Culler_H__ + +//package net.minecraft.client.renderer.culling; + +class AABB; + +class Culler +{ +public: + virtual ~Culler() {} + + virtual bool isVisible(const AABB& bb) = 0; + + virtual bool cubeInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) = 0; + + virtual bool cubeFullyInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) = 0; + + virtual void prepare(float xOff, float yOff, float zOff) {} +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__Culler_H__*/ diff --git a/src/client/renderer/culling/Frustum.cpp b/src/client/renderer/culling/Frustum.cpp index e5a3832..1aae6c7 100755 --- a/src/client/renderer/culling/Frustum.cpp +++ b/src/client/renderer/culling/Frustum.cpp @@ -1,3 +1,3 @@ -#include "Frustum.h" - -Frustum Frustum::frustum; +#include "Frustum.h" + +Frustum Frustum::frustum; diff --git a/src/client/renderer/culling/Frustum.h b/src/client/renderer/culling/Frustum.h index 25396e3..6fc1355 100755 --- a/src/client/renderer/culling/Frustum.h +++ b/src/client/renderer/culling/Frustum.h @@ -1,163 +1,163 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__ - -//package net.minecraft.client.renderer.culling; - -/* import static org.lwjgl.opengl.GL11.* */ - - -// 1) Stolen and ported to java from the web somewhere. -// 2) ... and then ported back to C++! - -//***********************************************************************// -// // -// - "Talk to me like I'm a 3 year old!" Programming Lessons - // -// // -// $Author: DigiBen digiben@gametutorials.com // -// // -// $Program: Frustum Culling // -// // -// $Description: Demonstrates checking if shapes are in view // -// // -// $Date: 8/28/01 // -// // -//***********************************************************************// - -//#include "main.h" - -#include "FrustumData.h" -#include "../../../util/Mth.h" -#include "../gles.h" - -class Frustum: public FrustumData -{ -private: - static Frustum frustum; - -public: - static FrustumData& getFrustum() { - frustum.calculateFrustum(); - return frustum; - } - - ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* - ///// - ///// This normalizes a plane (A side) from a given frustum. - ///// - ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* -private: - void normalizePlane(float frustum[16][16], int side) - { - // Here we calculate the magnitude of the normal to the plane (point A B C) - // Remember that (A, B, C) is that same thing as the normal's (X, Y, Z). - // To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2) - float invMagnitude = Mth::invSqrt(frustum[side][A] * frustum[side][A] + frustum[side][B] * frustum[side][B] + frustum[side][C] * frustum[side][C]); - - // Then we divide the plane's values by it's magnitude. - // This makes it easier to work with. - frustum[side][A] *= invMagnitude; - frustum[side][B] *= invMagnitude; - frustum[side][C] *= invMagnitude; - frustum[side][D] *= invMagnitude; - } - - float _proj[16]; - float _modl[16]; - float _clip[16]; - - void calculateFrustum() - { - // glGetFloatv() is used to extract information about our OpenGL world. - // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix. - // It then stores the matrix into an array of [16]. - glGetFloatv(GL_PROJECTION_MATRIX, proj); - - // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix. - // This also stores it in an array of [16]. - glGetFloatv(GL_MODELVIEW_MATRIX, modl); - // Now that we have our modelview and projection matrix, if we combine these 2 matrices, - // it will give us our clipping planes. To combine 2 matrices, we multiply them. - - clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + modl[3] * proj[12]; - clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + modl[3] * proj[13]; - clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + modl[3] * proj[14]; - clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + modl[3] * proj[15]; - - clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + modl[7] * proj[12]; - clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + modl[7] * proj[13]; - clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + modl[7] * proj[14]; - clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + modl[7] * proj[15]; - - clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + modl[11] * proj[12]; - clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + modl[11] * proj[13]; - clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + modl[11] * proj[14]; - clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + modl[11] * proj[15]; - - clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + modl[15] * proj[12]; - clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + modl[15] * proj[13]; - clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + modl[15] * proj[14]; - clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + modl[15] * proj[15]; - - // Now we actually want to get the sides of the frustum. To do this we take - // the clipping planes we received above and extract the sides from them. - - // This will extract the RIGHT side of the frustum - m_Frustum[RIGHT][A] = clip[3] - clip[0]; - m_Frustum[RIGHT][B] = clip[7] - clip[4]; - m_Frustum[RIGHT][C] = clip[11] - clip[8]; - m_Frustum[RIGHT][D] = clip[15] - clip[12]; - - // Now that we have a normal (A,B,C) and a distance (D) to the plane, - // we want to normalize that normal and distance. - - // Normalize the RIGHT side - normalizePlane(m_Frustum, RIGHT); - - // This will extract the LEFT side of the frustum - m_Frustum[LEFT][A] = clip[3] + clip[0]; - m_Frustum[LEFT][B] = clip[7] + clip[4]; - m_Frustum[LEFT][C] = clip[11] + clip[8]; - m_Frustum[LEFT][D] = clip[15] + clip[12]; - - // Normalize the LEFT side - normalizePlane(m_Frustum, LEFT); - - // This will extract the BOTTOM side of the frustum - m_Frustum[BOTTOM][A] = clip[3] + clip[1]; - m_Frustum[BOTTOM][B] = clip[7] + clip[5]; - m_Frustum[BOTTOM][C] = clip[11] + clip[9]; - m_Frustum[BOTTOM][D] = clip[15] + clip[13]; - - // Normalize the BOTTOM side - normalizePlane(m_Frustum, BOTTOM); - - // This will extract the TOP side of the frustum - m_Frustum[TOP][A] = clip[3] - clip[1]; - m_Frustum[TOP][B] = clip[7] - clip[5]; - m_Frustum[TOP][C] = clip[11] - clip[9]; - m_Frustum[TOP][D] = clip[15] - clip[13]; - - // Normalize the TOP side - normalizePlane(m_Frustum, TOP); - - // This will extract the BACK side of the frustum - m_Frustum[BACK][A] = clip[3] - clip[2]; - m_Frustum[BACK][B] = clip[7] - clip[6]; - m_Frustum[BACK][C] = clip[11] - clip[10]; - m_Frustum[BACK][D] = clip[15] - clip[14]; - - // Normalize the BACK side - normalizePlane(m_Frustum, BACK); - - // This will extract the FRONT side of the frustum - m_Frustum[FRONT][A] = clip[3] + clip[2]; - m_Frustum[FRONT][B] = clip[7] + clip[6]; - m_Frustum[FRONT][C] = clip[11] + clip[10]; - m_Frustum[FRONT][D] = clip[15] + clip[14]; - - // Normalize the FRONT side - normalizePlane(m_Frustum, FRONT); - } -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__ + +//package net.minecraft.client.renderer.culling; + +/* import static org.lwjgl.opengl.GL11.* */ + + +// 1) Stolen and ported to java from the web somewhere. +// 2) ... and then ported back to C++! + +//***********************************************************************// +// // +// - "Talk to me like I'm a 3 year old!" Programming Lessons - // +// // +// $Author: DigiBen digiben@gametutorials.com // +// // +// $Program: Frustum Culling // +// // +// $Description: Demonstrates checking if shapes are in view // +// // +// $Date: 8/28/01 // +// // +//***********************************************************************// + +//#include "main.h" + +#include "FrustumData.h" +#include "../../../util/Mth.h" +#include "../gles.h" + +class Frustum: public FrustumData +{ +private: + static Frustum frustum; + +public: + static FrustumData& getFrustum() { + frustum.calculateFrustum(); + return frustum; + } + + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + ///// + ///// This normalizes a plane (A side) from a given frustum. + ///// + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* +private: + void normalizePlane(float frustum[16][16], int side) + { + // Here we calculate the magnitude of the normal to the plane (point A B C) + // Remember that (A, B, C) is that same thing as the normal's (X, Y, Z). + // To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2) + float invMagnitude = Mth::invSqrt(frustum[side][A] * frustum[side][A] + frustum[side][B] * frustum[side][B] + frustum[side][C] * frustum[side][C]); + + // Then we divide the plane's values by it's magnitude. + // This makes it easier to work with. + frustum[side][A] *= invMagnitude; + frustum[side][B] *= invMagnitude; + frustum[side][C] *= invMagnitude; + frustum[side][D] *= invMagnitude; + } + + float _proj[16]; + float _modl[16]; + float _clip[16]; + + void calculateFrustum() + { + // glGetFloatv() is used to extract information about our OpenGL world. + // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix. + // It then stores the matrix into an array of [16]. + glGetFloatv(GL_PROJECTION_MATRIX, proj); + + // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix. + // This also stores it in an array of [16]. + glGetFloatv(GL_MODELVIEW_MATRIX, modl); + // Now that we have our modelview and projection matrix, if we combine these 2 matrices, + // it will give us our clipping planes. To combine 2 matrices, we multiply them. + + clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + modl[3] * proj[12]; + clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + modl[3] * proj[13]; + clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + modl[3] * proj[14]; + clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + modl[3] * proj[15]; + + clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + modl[7] * proj[12]; + clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + modl[7] * proj[13]; + clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + modl[7] * proj[14]; + clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + modl[7] * proj[15]; + + clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + modl[11] * proj[12]; + clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + modl[11] * proj[13]; + clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + modl[11] * proj[14]; + clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + modl[11] * proj[15]; + + clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + modl[15] * proj[12]; + clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + modl[15] * proj[13]; + clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + modl[15] * proj[14]; + clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + modl[15] * proj[15]; + + // Now we actually want to get the sides of the frustum. To do this we take + // the clipping planes we received above and extract the sides from them. + + // This will extract the RIGHT side of the frustum + m_Frustum[RIGHT][A] = clip[3] - clip[0]; + m_Frustum[RIGHT][B] = clip[7] - clip[4]; + m_Frustum[RIGHT][C] = clip[11] - clip[8]; + m_Frustum[RIGHT][D] = clip[15] - clip[12]; + + // Now that we have a normal (A,B,C) and a distance (D) to the plane, + // we want to normalize that normal and distance. + + // Normalize the RIGHT side + normalizePlane(m_Frustum, RIGHT); + + // This will extract the LEFT side of the frustum + m_Frustum[LEFT][A] = clip[3] + clip[0]; + m_Frustum[LEFT][B] = clip[7] + clip[4]; + m_Frustum[LEFT][C] = clip[11] + clip[8]; + m_Frustum[LEFT][D] = clip[15] + clip[12]; + + // Normalize the LEFT side + normalizePlane(m_Frustum, LEFT); + + // This will extract the BOTTOM side of the frustum + m_Frustum[BOTTOM][A] = clip[3] + clip[1]; + m_Frustum[BOTTOM][B] = clip[7] + clip[5]; + m_Frustum[BOTTOM][C] = clip[11] + clip[9]; + m_Frustum[BOTTOM][D] = clip[15] + clip[13]; + + // Normalize the BOTTOM side + normalizePlane(m_Frustum, BOTTOM); + + // This will extract the TOP side of the frustum + m_Frustum[TOP][A] = clip[3] - clip[1]; + m_Frustum[TOP][B] = clip[7] - clip[5]; + m_Frustum[TOP][C] = clip[11] - clip[9]; + m_Frustum[TOP][D] = clip[15] - clip[13]; + + // Normalize the TOP side + normalizePlane(m_Frustum, TOP); + + // This will extract the BACK side of the frustum + m_Frustum[BACK][A] = clip[3] - clip[2]; + m_Frustum[BACK][B] = clip[7] - clip[6]; + m_Frustum[BACK][C] = clip[11] - clip[10]; + m_Frustum[BACK][D] = clip[15] - clip[14]; + + // Normalize the BACK side + normalizePlane(m_Frustum, BACK); + + // This will extract the FRONT side of the frustum + m_Frustum[FRONT][A] = clip[3] + clip[2]; + m_Frustum[FRONT][B] = clip[7] + clip[6]; + m_Frustum[FRONT][C] = clip[11] + clip[10]; + m_Frustum[FRONT][D] = clip[15] + clip[14]; + + // Normalize the FRONT side + normalizePlane(m_Frustum, FRONT); + } +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__*/ diff --git a/src/client/renderer/culling/FrustumCuller.h b/src/client/renderer/culling/FrustumCuller.h index 470c953..d4d947c 100755 --- a/src/client/renderer/culling/FrustumCuller.h +++ b/src/client/renderer/culling/FrustumCuller.h @@ -1,39 +1,39 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ - -//package net.minecraft.client.renderer.culling; - -#include "FrustumData.h" -#include "Frustum.h" - -class FrustumCuller: public Culler { - -private: - FrustumData frustum; - float xOff, yOff, zOff; - -public: - FrustumCuller() { - frustum = Frustum::getFrustum(); - } - - void prepare(float xOff, float yOff, float zOff) { - this->xOff = xOff; - this->yOff = yOff; - this->zOff = zOff; - } - - bool cubeFullyInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { - return frustum.cubeFullyInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); - } - - bool cubeInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { - return frustum.cubeInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); - } - - bool isVisible(const AABB& bb) { - return cubeInFrustum(bb.x0, bb.y0, bb.z0, bb.x1, bb.y1, bb.z1); - } -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ + +//package net.minecraft.client.renderer.culling; + +#include "FrustumData.h" +#include "Frustum.h" + +class FrustumCuller: public Culler { + +private: + FrustumData frustum; + float xOff, yOff, zOff; + +public: + FrustumCuller() { + frustum = Frustum::getFrustum(); + } + + void prepare(float xOff, float yOff, float zOff) { + this->xOff = xOff; + this->yOff = yOff; + this->zOff = zOff; + } + + bool cubeFullyInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { + return frustum.cubeFullyInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); + } + + bool cubeInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { + return frustum.cubeInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); + } + + bool isVisible(const AABB& bb) { + return cubeInFrustum(bb.x0, bb.y0, bb.z0, bb.x1, bb.y1, bb.z1); + } +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__*/ diff --git a/src/client/renderer/culling/FrustumData.h b/src/client/renderer/culling/FrustumData.h index f29a0de..4e0c25c 100755 --- a/src/client/renderer/culling/FrustumData.h +++ b/src/client/renderer/culling/FrustumData.h @@ -1,97 +1,97 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__ - -//package net.minecraft.client.renderer.culling; - -#include "../../../world/phys/AABB.h" - -// We create an enum of the sides so we don't have to call each side 0 or 1. -// This way it makes it more understandable and readable when dealing with frustum sides. -class FrustumData -{ -public: - //enum FrustumSide - static const int RIGHT = 0; // The RIGHT side of the frustum - static const int LEFT = 1; // The LEFT side of the frustum - static const int BOTTOM = 2; // The BOTTOM side of the frustum - static const int TOP = 3; // The TOP side of the frustum - static const int BACK = 4; // The BACK side of the frustum - static const int FRONT = 5; // The FRONT side of the frustum - - // Like above, instead of saying a number for the ABC and D of the plane, we - // want to be more descriptive. - static const int A = 0; // The X value of the plane's normal - static const int B = 1; // The Y value of the plane's normal - static const int C = 2; // The Z value of the plane's normal - static const int D = 3; // The distance the plane is from the origin - - float m_Frustum[16][16]; - float proj[16]; - float modl[16]; - float clip[16]; - - bool pointInFrustum(float x, float y, float z) - { - for (int i = 0; i < 6; i++) - { - if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0) - { - return false; - } - } - - return true; - } - bool sphereInFrustum(float x, float y, float z, float radius) - { - for (int i = 0; i < 6; i++) - { - if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius) - { - return false; - } - } - - return true; - } - bool cubeFullyInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) - { - for (int i = 0; i < 6; i++) - { - if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; - } - - return true; - } - - bool cubeInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) - { - for (int i = 0; i < 6; i++) - { - if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; - - return false; - } - - return true; - } - bool isVisible(const AABB& aabb) - { - return cubeInFrustum(aabb.x0, aabb.y0, aabb.z0, aabb.x1, aabb.y1, aabb.z1); - } -}; -#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__ + +//package net.minecraft.client.renderer.culling; + +#include "../../../world/phys/AABB.h" + +// We create an enum of the sides so we don't have to call each side 0 or 1. +// This way it makes it more understandable and readable when dealing with frustum sides. +class FrustumData +{ +public: + //enum FrustumSide + static const int RIGHT = 0; // The RIGHT side of the frustum + static const int LEFT = 1; // The LEFT side of the frustum + static const int BOTTOM = 2; // The BOTTOM side of the frustum + static const int TOP = 3; // The TOP side of the frustum + static const int BACK = 4; // The BACK side of the frustum + static const int FRONT = 5; // The FRONT side of the frustum + + // Like above, instead of saying a number for the ABC and D of the plane, we + // want to be more descriptive. + static const int A = 0; // The X value of the plane's normal + static const int B = 1; // The Y value of the plane's normal + static const int C = 2; // The Z value of the plane's normal + static const int D = 3; // The distance the plane is from the origin + + float m_Frustum[16][16]; + float proj[16]; + float modl[16]; + float clip[16]; + + bool pointInFrustum(float x, float y, float z) + { + for (int i = 0; i < 6; i++) + { + if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0) + { + return false; + } + } + + return true; + } + bool sphereInFrustum(float x, float y, float z, float radius) + { + for (int i = 0; i < 6; i++) + { + if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius) + { + return false; + } + } + + return true; + } + bool cubeFullyInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) + { + for (int i = 0; i < 6; i++) + { + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + } + + return true; + } + + bool cubeInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) + { + for (int i = 0; i < 6; i++) + { + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + + return false; + } + + return true; + } + bool isVisible(const AABB& aabb) + { + return cubeInFrustum(aabb.x0, aabb.y0, aabb.z0, aabb.x1, aabb.y1, aabb.z1); + } +}; +#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__*/ diff --git a/src/client/renderer/culling/tmp/Frustum.h b/src/client/renderer/culling/tmp/Frustum.h index 012a28e..aa0610e 100755 --- a/src/client/renderer/culling/tmp/Frustum.h +++ b/src/client/renderer/culling/tmp/Frustum.h @@ -1,172 +1,172 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__ - -//package net.minecraft.client.renderer.culling; - -/* import static org.lwjgl.opengl.GL11.* */ - -#include "java/nio/FloatBuffer.h" - -#include "client/MemoryTracker.h" - - -// Stolen and ported to java from the web somewhere. - -//***********************************************************************// -// // -// - "Talk to me like I'm a 3 year old!" Programming Lessons - // -// // -// $Author: DigiBen digiben@gametutorials.com // -// // -// $Program: Frustum Culling // -// // -// $Description: Demonstrates checking if shapes are in view // -// // -// $Date: 8/28/01 // -// // -//***********************************************************************// - -//#include "main.h" - -// We create an enum of the sides so we don't have to call each side 0 or 1. -// This way it makes it more understandable and readable when dealing with frustum sides. -/*public*/ class Frustum: public FrustumData -{ - /*private*/ static Frustum frustum = /*new*/ Frustum(); - - /*public*/ static FrustumData getFrustum() - { - frustum.calculateFrustum(); - return frustum; - } - - ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* - ///// - ///// This normalizes a plane (A side) from a given frustum. - ///// - ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* - - /*private*/ void normalizePlane(float[][] frustum, int side) - { - // Here we calculate the magnitude of the normal to the plane (point A B C) - // Remember that (A, B, C) is that same thing as the normal's (X, Y, Z). - // To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2) - float magnitude = (float) util.Mth.sqrt(frustum[side][A] * frustum[side][A] + frustum[side][B] * frustum[side][B] + frustum[side][C] * frustum[side][C]); - - // Then we divide the plane's values by it's magnitude. - // This makes it easier to work with. - frustum[side][A] /= magnitude; - frustum[side][B] /= magnitude; - frustum[side][C] /= magnitude; - frustum[side][D] /= magnitude; - } - - /*private*/ FloatBuffer _proj = MemoryTracker.createFloatBuffer(16); - /*private*/ FloatBuffer _modl = MemoryTracker.createFloatBuffer(16); - /*private*/ FloatBuffer _clip = MemoryTracker.createFloatBuffer(16); - - /*private*/ void calculateFrustum() - { - _proj.clear(); - _modl.clear(); - _clip.clear(); - - // glGetFloatv() is used to extract information about our OpenGL world. - // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix. - // It then stores the matrix into an array of [16]. - glGetFloat(GL_PROJECTION_MATRIX, _proj); - - // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix. - // This also stores it in an array of [16]. - glGetFloat(GL_MODELVIEW_MATRIX, _modl); - - _proj.flip().limit(16); - _proj.get(proj); - _modl.flip().limit(16); - _modl.get(modl); - - // Now that we have our modelview and projection matrix, if we combine these 2 matrices, - // it will give us our clipping planes. To combine 2 matrices, we multiply them. - - clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + modl[3] * proj[12]; - clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + modl[3] * proj[13]; - clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + modl[3] * proj[14]; - clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + modl[3] * proj[15]; - - clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + modl[7] * proj[12]; - clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + modl[7] * proj[13]; - clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + modl[7] * proj[14]; - clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + modl[7] * proj[15]; - - clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + modl[11] * proj[12]; - clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + modl[11] * proj[13]; - clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + modl[11] * proj[14]; - clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + modl[11] * proj[15]; - - clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + modl[15] * proj[12]; - clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + modl[15] * proj[13]; - clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + modl[15] * proj[14]; - clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + modl[15] * proj[15]; - - // Now we actually want to get the sides of the frustum. To do this we take - // the clipping planes we received above and extract the sides from them. - - // This will extract the RIGHT side of the frustum - m_Frustum[RIGHT][A] = clip[3] - clip[0]; - m_Frustum[RIGHT][B] = clip[7] - clip[4]; - m_Frustum[RIGHT][C] = clip[11] - clip[8]; - m_Frustum[RIGHT][D] = clip[15] - clip[12]; - - // Now that we have a normal (A,B,C) and a distance (D) to the plane, - // we want to normalize that normal and distance. - - // Normalize the RIGHT side - normalizePlane(m_Frustum, RIGHT); - - // This will extract the LEFT side of the frustum - m_Frustum[LEFT][A] = clip[3] + clip[0]; - m_Frustum[LEFT][B] = clip[7] + clip[4]; - m_Frustum[LEFT][C] = clip[11] + clip[8]; - m_Frustum[LEFT][D] = clip[15] + clip[12]; - - // Normalize the LEFT side - normalizePlane(m_Frustum, LEFT); - - // This will extract the BOTTOM side of the frustum - m_Frustum[BOTTOM][A] = clip[3] + clip[1]; - m_Frustum[BOTTOM][B] = clip[7] + clip[5]; - m_Frustum[BOTTOM][C] = clip[11] + clip[9]; - m_Frustum[BOTTOM][D] = clip[15] + clip[13]; - - // Normalize the BOTTOM side - normalizePlane(m_Frustum, BOTTOM); - - // This will extract the TOP side of the frustum - m_Frustum[TOP][A] = clip[3] - clip[1]; - m_Frustum[TOP][B] = clip[7] - clip[5]; - m_Frustum[TOP][C] = clip[11] - clip[9]; - m_Frustum[TOP][D] = clip[15] - clip[13]; - - // Normalize the TOP side - normalizePlane(m_Frustum, TOP); - - // This will extract the BACK side of the frustum - m_Frustum[BACK][A] = clip[3] - clip[2]; - m_Frustum[BACK][B] = clip[7] - clip[6]; - m_Frustum[BACK][C] = clip[11] - clip[10]; - m_Frustum[BACK][D] = clip[15] - clip[14]; - - // Normalize the BACK side - normalizePlane(m_Frustum, BACK); - - // This will extract the FRONT side of the frustum - m_Frustum[FRONT][A] = clip[3] + clip[2]; - m_Frustum[FRONT][B] = clip[7] + clip[6]; - m_Frustum[FRONT][C] = clip[11] + clip[10]; - m_Frustum[FRONT][D] = clip[15] + clip[14]; - - // Normalize the FRONT side - normalizePlane(m_Frustum, FRONT); - } -} -#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__ + +//package net.minecraft.client.renderer.culling; + +/* import static org.lwjgl.opengl.GL11.* */ + +#include "java/nio/FloatBuffer.h" + +#include "client/MemoryTracker.h" + + +// Stolen and ported to java from the web somewhere. + +//***********************************************************************// +// // +// - "Talk to me like I'm a 3 year old!" Programming Lessons - // +// // +// $Author: DigiBen digiben@gametutorials.com // +// // +// $Program: Frustum Culling // +// // +// $Description: Demonstrates checking if shapes are in view // +// // +// $Date: 8/28/01 // +// // +//***********************************************************************// + +//#include "main.h" + +// We create an enum of the sides so we don't have to call each side 0 or 1. +// This way it makes it more understandable and readable when dealing with frustum sides. +/*public*/ class Frustum: public FrustumData +{ + /*private*/ static Frustum frustum = /*new*/ Frustum(); + + /*public*/ static FrustumData getFrustum() + { + frustum.calculateFrustum(); + return frustum; + } + + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + ///// + ///// This normalizes a plane (A side) from a given frustum. + ///// + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + + /*private*/ void normalizePlane(float[][] frustum, int side) + { + // Here we calculate the magnitude of the normal to the plane (point A B C) + // Remember that (A, B, C) is that same thing as the normal's (X, Y, Z). + // To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2) + float magnitude = (float) util.Mth.sqrt(frustum[side][A] * frustum[side][A] + frustum[side][B] * frustum[side][B] + frustum[side][C] * frustum[side][C]); + + // Then we divide the plane's values by it's magnitude. + // This makes it easier to work with. + frustum[side][A] /= magnitude; + frustum[side][B] /= magnitude; + frustum[side][C] /= magnitude; + frustum[side][D] /= magnitude; + } + + /*private*/ FloatBuffer _proj = MemoryTracker.createFloatBuffer(16); + /*private*/ FloatBuffer _modl = MemoryTracker.createFloatBuffer(16); + /*private*/ FloatBuffer _clip = MemoryTracker.createFloatBuffer(16); + + /*private*/ void calculateFrustum() + { + _proj.clear(); + _modl.clear(); + _clip.clear(); + + // glGetFloatv() is used to extract information about our OpenGL world. + // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix. + // It then stores the matrix into an array of [16]. + glGetFloat(GL_PROJECTION_MATRIX, _proj); + + // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix. + // This also stores it in an array of [16]. + glGetFloat(GL_MODELVIEW_MATRIX, _modl); + + _proj.flip().limit(16); + _proj.get(proj); + _modl.flip().limit(16); + _modl.get(modl); + + // Now that we have our modelview and projection matrix, if we combine these 2 matrices, + // it will give us our clipping planes. To combine 2 matrices, we multiply them. + + clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + modl[3] * proj[12]; + clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + modl[3] * proj[13]; + clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + modl[3] * proj[14]; + clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + modl[3] * proj[15]; + + clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + modl[7] * proj[12]; + clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + modl[7] * proj[13]; + clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + modl[7] * proj[14]; + clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + modl[7] * proj[15]; + + clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + modl[11] * proj[12]; + clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + modl[11] * proj[13]; + clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + modl[11] * proj[14]; + clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + modl[11] * proj[15]; + + clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + modl[15] * proj[12]; + clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + modl[15] * proj[13]; + clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + modl[15] * proj[14]; + clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + modl[15] * proj[15]; + + // Now we actually want to get the sides of the frustum. To do this we take + // the clipping planes we received above and extract the sides from them. + + // This will extract the RIGHT side of the frustum + m_Frustum[RIGHT][A] = clip[3] - clip[0]; + m_Frustum[RIGHT][B] = clip[7] - clip[4]; + m_Frustum[RIGHT][C] = clip[11] - clip[8]; + m_Frustum[RIGHT][D] = clip[15] - clip[12]; + + // Now that we have a normal (A,B,C) and a distance (D) to the plane, + // we want to normalize that normal and distance. + + // Normalize the RIGHT side + normalizePlane(m_Frustum, RIGHT); + + // This will extract the LEFT side of the frustum + m_Frustum[LEFT][A] = clip[3] + clip[0]; + m_Frustum[LEFT][B] = clip[7] + clip[4]; + m_Frustum[LEFT][C] = clip[11] + clip[8]; + m_Frustum[LEFT][D] = clip[15] + clip[12]; + + // Normalize the LEFT side + normalizePlane(m_Frustum, LEFT); + + // This will extract the BOTTOM side of the frustum + m_Frustum[BOTTOM][A] = clip[3] + clip[1]; + m_Frustum[BOTTOM][B] = clip[7] + clip[5]; + m_Frustum[BOTTOM][C] = clip[11] + clip[9]; + m_Frustum[BOTTOM][D] = clip[15] + clip[13]; + + // Normalize the BOTTOM side + normalizePlane(m_Frustum, BOTTOM); + + // This will extract the TOP side of the frustum + m_Frustum[TOP][A] = clip[3] - clip[1]; + m_Frustum[TOP][B] = clip[7] - clip[5]; + m_Frustum[TOP][C] = clip[11] - clip[9]; + m_Frustum[TOP][D] = clip[15] - clip[13]; + + // Normalize the TOP side + normalizePlane(m_Frustum, TOP); + + // This will extract the BACK side of the frustum + m_Frustum[BACK][A] = clip[3] - clip[2]; + m_Frustum[BACK][B] = clip[7] - clip[6]; + m_Frustum[BACK][C] = clip[11] - clip[10]; + m_Frustum[BACK][D] = clip[15] - clip[14]; + + // Normalize the BACK side + normalizePlane(m_Frustum, BACK); + + // This will extract the FRONT side of the frustum + m_Frustum[FRONT][A] = clip[3] + clip[2]; + m_Frustum[FRONT][B] = clip[7] + clip[6]; + m_Frustum[FRONT][C] = clip[11] + clip[10]; + m_Frustum[FRONT][D] = clip[15] + clip[14]; + + // Normalize the FRONT side + normalizePlane(m_Frustum, FRONT); + } +} +#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__Frustum_H__*/ diff --git a/src/client/renderer/culling/tmp/FrustumCuller.h b/src/client/renderer/culling/tmp/FrustumCuller.h index 939093d..8f5400f 100755 --- a/src/client/renderer/culling/tmp/FrustumCuller.h +++ b/src/client/renderer/culling/tmp/FrustumCuller.h @@ -1,165 +1,165 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ - -//package net.minecraft.client.renderer.culling; - -#include "Culler.h" -#include "../../../world/phys/AABB.h" - -class FrustumCuller: public Culler -{ - float xOff, yOff, zOff; - float frustum[6][4]; - -public: - FrustumCuller() { - ExtractFrustum(); - } - - void prepare(float xOff, float yOff, float zOff) { - this->xOff = xOff; - this->yOff = yOff; - this->zOff = zOff; - } - - //bool cubeFullyInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { - // return frustum.cubeFullyInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); - //} - - bool cubeFullyInFrustum( float x0, float y0, float z0, float x1, float y1, float z1 ) { - int c2 = 0; - for( int p = 0; p < 6; p++ ) { - int c = 0; - if( frustum[p][0] * (x0) + frustum[p][1] * (y0) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) c++; - if( frustum[p][0] * (x1) + frustum[p][1] * (y0) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) c++; - if( frustum[p][0] * (x0) + frustum[p][1] * (y1) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) c++; - if( frustum[p][0] * (x1) + frustum[p][1] * (y1) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) c++; - if( frustum[p][0] * (x0) + frustum[p][1] * (y0) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) c++; - if( frustum[p][0] * (x1) + frustum[p][1] * (y0) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) c++; - if( frustum[p][0] * (x0) + frustum[p][1] * (y1) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) c++; - if( frustum[p][0] * (x1) + frustum[p][1] * (y1) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) c++; - if( c == 0 ) - return false; // 0 - if( c == 8 ) - c2++; - } - return c2 == 6; //(c2 == 6) ? 2 : 1; - } - - bool isVisible(const AABB& bb) { - printf("cube is : %s\n", bb.toString().c_str()); - return cubeInFrustum(bb.x0, bb.y0, bb.z0, bb.x1, bb.y1, bb.z1); - } - - bool cubeInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { - for(int p = 0; p < 6; p++ ) { - if( frustum[p][0] * (x0) + frustum[p][1] * (y0) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) continue; - if( frustum[p][0] * (x1) + frustum[p][1] * (y0) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) continue; - if( frustum[p][0] * (x0) + frustum[p][1] * (y1) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) continue; - if( frustum[p][0] * (x1) + frustum[p][1] * (y1) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) continue; - if( frustum[p][0] * (x0) + frustum[p][1] * (y0) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) continue; - if( frustum[p][0] * (x1) + frustum[p][1] * (y0) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) continue; - if( frustum[p][0] * (x0) + frustum[p][1] * (y1) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) continue; - if( frustum[p][0] * (x1) + frustum[p][1] * (y1) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) continue; - return false; - } - return true; - } - -private: - void ExtractFrustum() { - float proj[16]; - float modl[16]; - float clip[16]; - float t; - /* Get the current PROJECTION matrix from OpenGL */ - glGetFloatv( GL_PROJECTION_MATRIX, proj ); - /* Get the current MODELVIEW matrix from OpenGL */ - glGetFloatv( GL_MODELVIEW_MATRIX, modl ); - /* Combine the two matrices (multiply projection by modelview) */ - clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12]; - clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13]; - clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14]; - clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15]; - clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12]; - clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13]; - clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14]; - clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15]; - clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12]; - clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13]; - clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14]; - clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15]; - clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12]; - clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13]; - clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14]; - clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15]; - /* Extract the numbers for the RIGHT plane */ - frustum[0][0] = clip[ 3] - clip[ 0]; - frustum[0][1] = clip[ 7] - clip[ 4]; - frustum[0][2] = clip[11] - clip[ 8]; - frustum[0][3] = clip[15] - clip[12]; - /* Normalize the result */ - t = sqrt( frustum[0][0] * frustum[0][0] + frustum[0][1] * frustum[0][1] + frustum[0][2] * frustum[0][2] ); - frustum[0][0] /= t; - frustum[0][1] /= t; - frustum[0][2] /= t; - frustum[0][3] /= t; - /* Extract the numbers for the LEFT plane */ - frustum[1][0] = clip[ 3] + clip[ 0]; - frustum[1][1] = clip[ 7] + clip[ 4]; - frustum[1][2] = clip[11] + clip[ 8]; - frustum[1][3] = clip[15] + clip[12]; - /* Normalize the result */ - t = sqrt( frustum[1][0] * frustum[1][0] + frustum[1][1] * frustum[1][1] + frustum[1][2] * frustum[1][2] ); - frustum[1][0] /= t; - frustum[1][1] /= t; - frustum[1][2] /= t; - frustum[1][3] /= t; - /* Extract the BOTTOM plane */ - frustum[2][0] = clip[ 3] + clip[ 1]; - frustum[2][1] = clip[ 7] + clip[ 5]; - frustum[2][2] = clip[11] + clip[ 9]; - frustum[2][3] = clip[15] + clip[13]; - /* Normalize the result */ - t = sqrt( frustum[2][0] * frustum[2][0] + frustum[2][1] * frustum[2][1] + frustum[2][2] * frustum[2][2] ); - frustum[2][0] /= t; - frustum[2][1] /= t; - frustum[2][2] /= t; - frustum[2][3] /= t; - /* Extract the TOP plane */ - frustum[3][0] = clip[ 3] - clip[ 1]; - frustum[3][1] = clip[ 7] - clip[ 5]; - frustum[3][2] = clip[11] - clip[ 9]; - frustum[3][3] = clip[15] - clip[13]; - /* Normalize the result */ - t = sqrt( frustum[3][0] * frustum[3][0] + frustum[3][1] * frustum[3][1] + frustum[3][2] * frustum[3][2] ); - frustum[3][0] /= t; - frustum[3][1] /= t; - frustum[3][2] /= t; - frustum[3][3] /= t; - /* Extract the FAR plane */ - frustum[4][0] = clip[ 3] - clip[ 2]; - frustum[4][1] = clip[ 7] - clip[ 6]; - frustum[4][2] = clip[11] - clip[10]; - frustum[4][3] = clip[15] - clip[14]; - /* Normalize the result */ - t = sqrt( frustum[4][0] * frustum[4][0] + frustum[4][1] * frustum[4][1] + frustum[4][2] * frustum[4][2] ); - frustum[4][0] /= t; - frustum[4][1] /= t; - frustum[4][2] /= t; - frustum[4][3] /= t; - /* Extract the NEAR plane */ - frustum[5][0] = clip[ 3] + clip[ 2]; - frustum[5][1] = clip[ 7] + clip[ 6]; - frustum[5][2] = clip[11] + clip[10]; - frustum[5][3] = clip[15] + clip[14]; - /* Normalize the result */ - t = sqrt( frustum[5][0] * frustum[5][0] + frustum[5][1] * frustum[5][1] + frustum[5][2] * frustum[5][2] ); - frustum[5][0] /= t; - frustum[5][1] /= t; - frustum[5][2] /= t; - frustum[5][3] /= t; - } -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ + +//package net.minecraft.client.renderer.culling; + +#include "Culler.h" +#include "../../../world/phys/AABB.h" + +class FrustumCuller: public Culler +{ + float xOff, yOff, zOff; + float frustum[6][4]; + +public: + FrustumCuller() { + ExtractFrustum(); + } + + void prepare(float xOff, float yOff, float zOff) { + this->xOff = xOff; + this->yOff = yOff; + this->zOff = zOff; + } + + //bool cubeFullyInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { + // return frustum.cubeFullyInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); + //} + + bool cubeFullyInFrustum( float x0, float y0, float z0, float x1, float y1, float z1 ) { + int c2 = 0; + for( int p = 0; p < 6; p++ ) { + int c = 0; + if( frustum[p][0] * (x0) + frustum[p][1] * (y0) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) c++; + if( frustum[p][0] * (x1) + frustum[p][1] * (y0) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) c++; + if( frustum[p][0] * (x0) + frustum[p][1] * (y1) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) c++; + if( frustum[p][0] * (x1) + frustum[p][1] * (y1) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) c++; + if( frustum[p][0] * (x0) + frustum[p][1] * (y0) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) c++; + if( frustum[p][0] * (x1) + frustum[p][1] * (y0) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) c++; + if( frustum[p][0] * (x0) + frustum[p][1] * (y1) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) c++; + if( frustum[p][0] * (x1) + frustum[p][1] * (y1) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) c++; + if( c == 0 ) + return false; // 0 + if( c == 8 ) + c2++; + } + return c2 == 6; //(c2 == 6) ? 2 : 1; + } + + bool isVisible(const AABB& bb) { + printf("cube is : %s\n", bb.toString().c_str()); + return cubeInFrustum(bb.x0, bb.y0, bb.z0, bb.x1, bb.y1, bb.z1); + } + + bool cubeInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { + for(int p = 0; p < 6; p++ ) { + if( frustum[p][0] * (x0) + frustum[p][1] * (y0) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) continue; + if( frustum[p][0] * (x1) + frustum[p][1] * (y0) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) continue; + if( frustum[p][0] * (x0) + frustum[p][1] * (y1) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) continue; + if( frustum[p][0] * (x1) + frustum[p][1] * (y1) + frustum[p][2] * (z0) + frustum[p][3] > 0 ) continue; + if( frustum[p][0] * (x0) + frustum[p][1] * (y0) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) continue; + if( frustum[p][0] * (x1) + frustum[p][1] * (y0) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) continue; + if( frustum[p][0] * (x0) + frustum[p][1] * (y1) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) continue; + if( frustum[p][0] * (x1) + frustum[p][1] * (y1) + frustum[p][2] * (z1) + frustum[p][3] > 0 ) continue; + return false; + } + return true; + } + +private: + void ExtractFrustum() { + float proj[16]; + float modl[16]; + float clip[16]; + float t; + /* Get the current PROJECTION matrix from OpenGL */ + glGetFloatv( GL_PROJECTION_MATRIX, proj ); + /* Get the current MODELVIEW matrix from OpenGL */ + glGetFloatv( GL_MODELVIEW_MATRIX, modl ); + /* Combine the two matrices (multiply projection by modelview) */ + clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12]; + clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13]; + clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14]; + clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15]; + clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12]; + clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13]; + clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14]; + clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15]; + clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12]; + clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13]; + clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14]; + clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15]; + clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12]; + clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13]; + clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14]; + clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15]; + /* Extract the numbers for the RIGHT plane */ + frustum[0][0] = clip[ 3] - clip[ 0]; + frustum[0][1] = clip[ 7] - clip[ 4]; + frustum[0][2] = clip[11] - clip[ 8]; + frustum[0][3] = clip[15] - clip[12]; + /* Normalize the result */ + t = sqrt( frustum[0][0] * frustum[0][0] + frustum[0][1] * frustum[0][1] + frustum[0][2] * frustum[0][2] ); + frustum[0][0] /= t; + frustum[0][1] /= t; + frustum[0][2] /= t; + frustum[0][3] /= t; + /* Extract the numbers for the LEFT plane */ + frustum[1][0] = clip[ 3] + clip[ 0]; + frustum[1][1] = clip[ 7] + clip[ 4]; + frustum[1][2] = clip[11] + clip[ 8]; + frustum[1][3] = clip[15] + clip[12]; + /* Normalize the result */ + t = sqrt( frustum[1][0] * frustum[1][0] + frustum[1][1] * frustum[1][1] + frustum[1][2] * frustum[1][2] ); + frustum[1][0] /= t; + frustum[1][1] /= t; + frustum[1][2] /= t; + frustum[1][3] /= t; + /* Extract the BOTTOM plane */ + frustum[2][0] = clip[ 3] + clip[ 1]; + frustum[2][1] = clip[ 7] + clip[ 5]; + frustum[2][2] = clip[11] + clip[ 9]; + frustum[2][3] = clip[15] + clip[13]; + /* Normalize the result */ + t = sqrt( frustum[2][0] * frustum[2][0] + frustum[2][1] * frustum[2][1] + frustum[2][2] * frustum[2][2] ); + frustum[2][0] /= t; + frustum[2][1] /= t; + frustum[2][2] /= t; + frustum[2][3] /= t; + /* Extract the TOP plane */ + frustum[3][0] = clip[ 3] - clip[ 1]; + frustum[3][1] = clip[ 7] - clip[ 5]; + frustum[3][2] = clip[11] - clip[ 9]; + frustum[3][3] = clip[15] - clip[13]; + /* Normalize the result */ + t = sqrt( frustum[3][0] * frustum[3][0] + frustum[3][1] * frustum[3][1] + frustum[3][2] * frustum[3][2] ); + frustum[3][0] /= t; + frustum[3][1] /= t; + frustum[3][2] /= t; + frustum[3][3] /= t; + /* Extract the FAR plane */ + frustum[4][0] = clip[ 3] - clip[ 2]; + frustum[4][1] = clip[ 7] - clip[ 6]; + frustum[4][2] = clip[11] - clip[10]; + frustum[4][3] = clip[15] - clip[14]; + /* Normalize the result */ + t = sqrt( frustum[4][0] * frustum[4][0] + frustum[4][1] * frustum[4][1] + frustum[4][2] * frustum[4][2] ); + frustum[4][0] /= t; + frustum[4][1] /= t; + frustum[4][2] /= t; + frustum[4][3] /= t; + /* Extract the NEAR plane */ + frustum[5][0] = clip[ 3] + clip[ 2]; + frustum[5][1] = clip[ 7] + clip[ 6]; + frustum[5][2] = clip[11] + clip[10]; + frustum[5][3] = clip[15] + clip[14]; + /* Normalize the result */ + t = sqrt( frustum[5][0] * frustum[5][0] + frustum[5][1] * frustum[5][1] + frustum[5][2] * frustum[5][2] ); + frustum[5][0] /= t; + frustum[5][1] /= t; + frustum[5][2] /= t; + frustum[5][3] /= t; + } +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__*/ diff --git a/src/client/renderer/culling/tmp/FrustumData.h b/src/client/renderer/culling/tmp/FrustumData.h index 942b74c..509695a 100755 --- a/src/client/renderer/culling/tmp/FrustumData.h +++ b/src/client/renderer/culling/tmp/FrustumData.h @@ -1,102 +1,102 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__ - -//package net.minecraft.client.renderer.culling; - -#include "world/phys/AABB.h" - - -/*public*/ class FrustumData -{ - //enum FrustumSide - /*public*/ static const int RIGHT = 0; // The RIGHT side of the frustum - /*public*/ static const int LEFT = 1; // The LEFT side of the frustum - /*public*/ static const int BOTTOM = 2; // The BOTTOM side of the frustum - /*public*/ static const int TOP = 3; // The TOP side of the frustum - /*public*/ static const int BACK = 4; // The BACK side of the frustum - /*public*/ static const int FRONT = 5; // The FRONT side of the frustum - - // Like above, instead of saying a number for the ABC and D of the plane, we - // want to be more descriptive. - /*public*/ static const int A = 0; // The X value of the plane's normal - /*public*/ static const int B = 1; // The Y value of the plane's normal - /*public*/ static const int C = 2; // The Z value of the plane's normal - /*public*/ static const int D = 3; // The distance the plane is from the origin - - /*public*/ float[][] m_Frustum = /*new*/ float[16][16]; - /*public*/ float[] proj = /*new*/ float[16]; - /*public*/ float[] modl = /*new*/ float[16]; - /*public*/ float[] clip = /*new*/ float[16]; -/* - /*public*/ FrustumData(float[][] mFrustum, float[] proj, float[] modl, float[] clip) - { - m_Frustum = mFrustum; - this->proj = proj; - this->modl = modl; - this->clip = clip; - }*/ - /*public*/ bool pointInFrustum(float x, float y, float z) - { - for (int i = 0; i < 6; i++) - { - if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0) - { - return false; - } - } - - return true; - } - /*public*/ bool sphereInFrustum(float x, float y, float z, float radius) - { - for (int i = 0; i < 6; i++) - { - if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius) - { - return false; - } - } - - return true; - } - /*public*/ bool cubeFullyInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) - { - for (int i = 0; i < 6; i++) - { - if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; - if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; - } - - return true; - } - - /*public*/ bool cubeInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) - { - for (int i = 0; i < 6; i++) - { - if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; - if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; - - return false; - } - - return true; - } - /*public*/ bool isVisible(AABB aabb) - { - return cubeInFrustum(aabb.x0, aabb.y0, aabb.z0, aabb.x1, aabb.y1, aabb.z1); - } -} -#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__ + +//package net.minecraft.client.renderer.culling; + +#include "world/phys/AABB.h" + + +/*public*/ class FrustumData +{ + //enum FrustumSide + /*public*/ static const int RIGHT = 0; // The RIGHT side of the frustum + /*public*/ static const int LEFT = 1; // The LEFT side of the frustum + /*public*/ static const int BOTTOM = 2; // The BOTTOM side of the frustum + /*public*/ static const int TOP = 3; // The TOP side of the frustum + /*public*/ static const int BACK = 4; // The BACK side of the frustum + /*public*/ static const int FRONT = 5; // The FRONT side of the frustum + + // Like above, instead of saying a number for the ABC and D of the plane, we + // want to be more descriptive. + /*public*/ static const int A = 0; // The X value of the plane's normal + /*public*/ static const int B = 1; // The Y value of the plane's normal + /*public*/ static const int C = 2; // The Z value of the plane's normal + /*public*/ static const int D = 3; // The distance the plane is from the origin + + /*public*/ float[][] m_Frustum = /*new*/ float[16][16]; + /*public*/ float[] proj = /*new*/ float[16]; + /*public*/ float[] modl = /*new*/ float[16]; + /*public*/ float[] clip = /*new*/ float[16]; +/* + /*public*/ FrustumData(float[][] mFrustum, float[] proj, float[] modl, float[] clip) + { + m_Frustum = mFrustum; + this->proj = proj; + this->modl = modl; + this->clip = clip; + }*/ + /*public*/ bool pointInFrustum(float x, float y, float z) + { + for (int i = 0; i < 6; i++) + { + if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0) + { + return false; + } + } + + return true; + } + /*public*/ bool sphereInFrustum(float x, float y, float z, float radius) + { + for (int i = 0; i < 6; i++) + { + if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius) + { + return false; + } + } + + return true; + } + /*public*/ bool cubeFullyInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) + { + for (int i = 0; i < 6; i++) + { + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + } + + return true; + } + + /*public*/ bool cubeInFrustum(float x1, float y1, float z1, float x2, float y2, float z2) + { + for (int i = 0; i < 6; i++) + { + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + + return false; + } + + return true; + } + /*public*/ bool isVisible(AABB aabb) + { + return cubeInFrustum(aabb.x0, aabb.y0, aabb.z0, aabb.x1, aabb.y1, aabb.z1); + } +} +#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumData_H__*/ diff --git a/src/client/renderer/culling/tmp/_FrustumCuller.h b/src/client/renderer/culling/tmp/_FrustumCuller.h index f93edac..3f9ee53 100755 --- a/src/client/renderer/culling/tmp/_FrustumCuller.h +++ b/src/client/renderer/culling/tmp/_FrustumCuller.h @@ -1,37 +1,37 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ - -//package net.minecraft.client.renderer.culling; - -#include "world/phys/AABB.h" - -/*public*/ class FrustumCuller: /*implements-interface*/ public Culler { - - /*private*/ FrustumData frustum; - - /*public*/ FrustumCuller() { - frustum = Frustum.getFrustum(); - } - - /*private*/ float xOff, yOff, zOff; - - /*public*/ void prepare(float xOff, float yOff, float zOff) { - this->xOff = xOff; - this->yOff = yOff; - this->zOff = zOff; - } - - /*public*/ bool cubeFullyInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { - return frustum.cubeFullyInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); - } - - /*public*/ bool cubeInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { - return frustum.cubeInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); - } - - /*public*/ bool isVisible(AABB bb) { - return cubeInFrustum(bb.x0, bb.y0, bb.z0, bb.x1, bb.y1, bb.z1); - } -} - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__ + +//package net.minecraft.client.renderer.culling; + +#include "world/phys/AABB.h" + +/*public*/ class FrustumCuller: /*implements-interface*/ public Culler { + + /*private*/ FrustumData frustum; + + /*public*/ FrustumCuller() { + frustum = Frustum.getFrustum(); + } + + /*private*/ float xOff, yOff, zOff; + + /*public*/ void prepare(float xOff, float yOff, float zOff) { + this->xOff = xOff; + this->yOff = yOff; + this->zOff = zOff; + } + + /*public*/ bool cubeFullyInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { + return frustum.cubeFullyInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); + } + + /*public*/ bool cubeInFrustum(float x0, float y0, float z0, float x1, float y1, float z1) { + return frustum.cubeInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); + } + + /*public*/ bool isVisible(AABB bb) { + return cubeInFrustum(bb.x0, bb.y0, bb.z0, bb.x1, bb.y1, bb.z1); + } +} + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_CULLING__FrustumCuller_H__*/ diff --git a/src/client/renderer/entity/ArrowRenderer.cpp b/src/client/renderer/entity/ArrowRenderer.cpp index f24edfb..a5c4beb 100755 --- a/src/client/renderer/entity/ArrowRenderer.cpp +++ b/src/client/renderer/entity/ArrowRenderer.cpp @@ -1,63 +1,63 @@ -#include "ArrowRenderer.h" - -#include "../Tesselator.h" -#include "../Textures.h" -#include "../gles.h" -#include "../../../world/entity/Entity.h" -#include "../../../world/entity/projectile/Arrow.h" - -void ArrowRenderer::render(Entity* entity, float x, float y, float z, float rot, float a) { - bindTexture("item/arrows.png"); - Arrow* arrow = (Arrow*) entity; - glPushMatrix2(); - glTranslatef(x, y, z); - glRotatef(arrow->yRotO + (arrow->yRot - arrow->yRotO) * a - 90, 0, 1, 0); - glRotatef(arrow->xRotO + (arrow->xRot - arrow->xRotO) * a, 0, 0, 1); - Tesselator& t = Tesselator::instance; - - int type = 0; - - float u0 = 0 / 32.0f; - float u1 = 16 / 32.0f; - float v0 = (0 + type * 10) / 32.0f; - float v1 = (5 + type * 10) / 32.0f; - - float u02 = 0 / 32.0f; - float u12 = 5 / 32.0f; - float v02 = (5 + type * 10) / 32.0f; - float v12 = (10 + type * 10) / 32.0f; - float ss = 0.9f / 16.0f; - float shake = arrow->shakeTime - a; - if (shake > 0) { - float pow = -Mth::sin(shake * 3) * shake; - glRotatef(pow, 0, 0, 1); - } - glRotatef(45, 1, 0, 0); - glScalef(ss, ss, ss); - - glTranslatef(-4, 0, 0); - - t.begin(); - t.vertexUV(-7, -2, -2, u02, v02); - t.vertexUV(-7, -2, +2, u12, v02); - t.vertexUV(-7, +2, +2, u12, v12); - t.vertexUV(-7, +2, -2, u02, v12); - - t.vertexUV(-7, +2, -2, u02, v02); - t.vertexUV(-7, +2, +2, u12, v02); - t.vertexUV(-7, -2, +2, u12, v12); - t.vertexUV(-7, -2, -2, u02, v12); - t.draw(); - - for (int i = 0; i < 4; i++) { - glRotatef(90, 1, 0, 0); - t.begin(); - t.vertexUV(-8, -2, 0, u0, v0); - t.vertexUV(+8, -2, 0, u1, v0); - t.vertexUV(+8, +2, 0, u1, v1); - t.vertexUV(-8, +2, 0, u0, v1); - t.endOverrideAndDraw(); - } - - glPopMatrix2(); +#include "ArrowRenderer.h" + +#include "../Tesselator.h" +#include "../Textures.h" +#include "../gles.h" +#include "../../../world/entity/Entity.h" +#include "../../../world/entity/projectile/Arrow.h" + +void ArrowRenderer::render(Entity* entity, float x, float y, float z, float rot, float a) { + bindTexture("item/arrows.png"); + Arrow* arrow = (Arrow*) entity; + glPushMatrix2(); + glTranslatef(x, y, z); + glRotatef(arrow->yRotO + (arrow->yRot - arrow->yRotO) * a - 90, 0, 1, 0); + glRotatef(arrow->xRotO + (arrow->xRot - arrow->xRotO) * a, 0, 0, 1); + Tesselator& t = Tesselator::instance; + + int type = 0; + + float u0 = 0 / 32.0f; + float u1 = 16 / 32.0f; + float v0 = (0 + type * 10) / 32.0f; + float v1 = (5 + type * 10) / 32.0f; + + float u02 = 0 / 32.0f; + float u12 = 5 / 32.0f; + float v02 = (5 + type * 10) / 32.0f; + float v12 = (10 + type * 10) / 32.0f; + float ss = 0.9f / 16.0f; + float shake = arrow->shakeTime - a; + if (shake > 0) { + float pow = -Mth::sin(shake * 3) * shake; + glRotatef(pow, 0, 0, 1); + } + glRotatef(45, 1, 0, 0); + glScalef(ss, ss, ss); + + glTranslatef(-4, 0, 0); + + t.begin(); + t.vertexUV(-7, -2, -2, u02, v02); + t.vertexUV(-7, -2, +2, u12, v02); + t.vertexUV(-7, +2, +2, u12, v12); + t.vertexUV(-7, +2, -2, u02, v12); + + t.vertexUV(-7, +2, -2, u02, v02); + t.vertexUV(-7, +2, +2, u12, v02); + t.vertexUV(-7, -2, +2, u12, v12); + t.vertexUV(-7, -2, -2, u02, v12); + t.draw(); + + for (int i = 0; i < 4; i++) { + glRotatef(90, 1, 0, 0); + t.begin(); + t.vertexUV(-8, -2, 0, u0, v0); + t.vertexUV(+8, -2, 0, u1, v0); + t.vertexUV(+8, +2, 0, u1, v1); + t.vertexUV(-8, +2, 0, u0, v1); + t.endOverrideAndDraw(); + } + + glPopMatrix2(); } \ No newline at end of file diff --git a/src/client/renderer/entity/ArrowRenderer.h b/src/client/renderer/entity/ArrowRenderer.h index a69627f..b637b5a 100755 --- a/src/client/renderer/entity/ArrowRenderer.h +++ b/src/client/renderer/entity/ArrowRenderer.h @@ -1,9 +1,9 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ArrowRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ArrowRenderer_H__ -#include "EntityRenderer.h" - -class ArrowRenderer : public EntityRenderer { - void render(Entity* arrow, float x, float y, float z, float rot, float a); -}; - +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ArrowRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ArrowRenderer_H__ +#include "EntityRenderer.h" + +class ArrowRenderer : public EntityRenderer { + void render(Entity* arrow, float x, float y, float z, float rot, float a); +}; + #endif /* NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ArrowRenderer_H__ */ \ No newline at end of file diff --git a/src/client/renderer/entity/ChickenRenderer.cpp b/src/client/renderer/entity/ChickenRenderer.cpp index 3cc7eff..b8d88ab 100755 --- a/src/client/renderer/entity/ChickenRenderer.cpp +++ b/src/client/renderer/entity/ChickenRenderer.cpp @@ -1,23 +1,23 @@ -#include "ChickenRenderer.h" -#include "../../../util/Mth.h" -#include "../../../world/entity/animal/Chicken.h" - - -ChickenRenderer::ChickenRenderer( Model* model, float shadow ) -: super(model, shadow) -{ -} - -void ChickenRenderer::render( Entity* mob, float x, float y, float z, float rot, float a ) -{ - super::render(mob, x, y, z, rot, a); -} - -float ChickenRenderer::getBob( Mob* mob_, float a ) -{ - Chicken* mob = (Chicken*) mob_; - float flap = mob->oFlap+(mob->flap-mob->oFlap)*a; - float flapSpeed = mob->oFlapSpeed+(mob->flapSpeed-mob->oFlapSpeed)*a; - - return (Mth::sin(flap)+1)*flapSpeed; -} +#include "ChickenRenderer.h" +#include "../../../util/Mth.h" +#include "../../../world/entity/animal/Chicken.h" + + +ChickenRenderer::ChickenRenderer( Model* model, float shadow ) +: super(model, shadow) +{ +} + +void ChickenRenderer::render( Entity* mob, float x, float y, float z, float rot, float a ) +{ + super::render(mob, x, y, z, rot, a); +} + +float ChickenRenderer::getBob( Mob* mob_, float a ) +{ + Chicken* mob = (Chicken*) mob_; + float flap = mob->oFlap+(mob->flap-mob->oFlap)*a; + float flapSpeed = mob->oFlapSpeed+(mob->flapSpeed-mob->oFlapSpeed)*a; + + return (Mth::sin(flap)+1)*flapSpeed; +} diff --git a/src/client/renderer/entity/ChickenRenderer.h b/src/client/renderer/entity/ChickenRenderer.h index f3e1bcf..d334ae4 100755 --- a/src/client/renderer/entity/ChickenRenderer.h +++ b/src/client/renderer/entity/ChickenRenderer.h @@ -1,22 +1,22 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ChickenRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ChickenRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "MobRenderer.h" - -class Mob; - -class ChickenRenderer: public MobRenderer -{ - typedef MobRenderer super; -public: - ChickenRenderer(Model* model, float shadow); - - void render(Entity* mob, float x, float y, float z, float rot, float a); - -protected: - float getBob(Mob* mob_, float a); -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ChickenRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ChickenRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ChickenRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "MobRenderer.h" + +class Mob; + +class ChickenRenderer: public MobRenderer +{ + typedef MobRenderer super; +public: + ChickenRenderer(Model* model, float shadow); + + void render(Entity* mob, float x, float y, float z, float rot, float a); + +protected: + float getBob(Mob* mob_, float a); +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ChickenRenderer_H__*/ diff --git a/src/client/renderer/entity/CreeperRenderer.h b/src/client/renderer/entity/CreeperRenderer.h index 594bc48..60d793c 100755 --- a/src/client/renderer/entity/CreeperRenderer.h +++ b/src/client/renderer/entity/CreeperRenderer.h @@ -1,94 +1,94 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__CreeperRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__CreeperRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "MobRenderer.h" - -#include "../../model/CreeperModel.h" -#include "../../model/Model.h" -#include "../../../world/entity/monster/Creeper.h" -#include "../../../util/Mth.h" - -class CreeperRenderer: public MobRenderer -{ - typedef MobRenderer super; -public: - CreeperRenderer() - : super(new CreeperModel(), 0.5f) - { - } - -protected: - void scale(Mob* mob, float a) { - Creeper* creeper = (Creeper*) mob; - - float g = creeper->getSwelling(a); - - float wobble = 1.0f + Mth::sin(g * 100) * g * 0.01f; - if (g < 0) g = 0; - if (g > 1) g = 1; - g = g * g; - g = g * g; - float s = (1.0f + g * 0.4f) * wobble; - float hs = (1.0f + g * 0.1f) / wobble; - glScalef(s, hs, s); - } - - int getOverlayColor(Mob* mob, float br, float a) { - Creeper* creeper = (Creeper*) mob; - - float step = creeper->getSwelling(a); - - if ((int) (step * 10) % 2 == 0) return 0; - - int _a = (int) (step * 0.2f * 255); - if (_a < 0) _a = 0; - if (_a > 255) _a = 255; - - int r = 255; - int g = 255; - int b = 255; - - return (_a << 24) | (r << 16) | (g << 8) | b; - } - - // int prepareArmor(Mob* mob, int layer, float a) { - //Creeper* creeper = (Creeper*) mob; - // if (creeper->isPowered()) { - // if (layer == 1) { - // float time = mob->tickCount + a; - // bindTexture("/armor/power.png"); - // glMatrixMode(GL_TEXTURE); - // glLoadIdentity(); - // float uo = time * 0.01f; - // float vo = time * 0.01f; - // glTranslatef(uo, vo, 0); - // setArmor(armorModel); - // glMatrixMode(GL_MODELVIEW); - // glEnable(GL_BLEND); - // float br = 0.5f; - // glColor4f2(br, br, br, 1); - // glDisable(GL_LIGHTING); - // glBlendFunc2(GL_ONE, GL_ONE); - // return 1; - // } - // if (layer == 2) { - // glMatrixMode(GL_TEXTURE); - // glLoadIdentity(); - // glMatrixMode(GL_MODELVIEW); - // glEnable(GL_LIGHTING); - // glDisable(GL_BLEND); - // } - // } - // return -1; - // } - - // int prepareArmorOverlay(Mob* mob, int layer, float a) { - // return -1; - // } -//private: - //Model* armorModel = /*new*/ CreeperModel(2); -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__CreeperRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__CreeperRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__CreeperRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "MobRenderer.h" + +#include "../../model/CreeperModel.h" +#include "../../model/Model.h" +#include "../../../world/entity/monster/Creeper.h" +#include "../../../util/Mth.h" + +class CreeperRenderer: public MobRenderer +{ + typedef MobRenderer super; +public: + CreeperRenderer() + : super(new CreeperModel(), 0.5f) + { + } + +protected: + void scale(Mob* mob, float a) { + Creeper* creeper = (Creeper*) mob; + + float g = creeper->getSwelling(a); + + float wobble = 1.0f + Mth::sin(g * 100) * g * 0.01f; + if (g < 0) g = 0; + if (g > 1) g = 1; + g = g * g; + g = g * g; + float s = (1.0f + g * 0.4f) * wobble; + float hs = (1.0f + g * 0.1f) / wobble; + glScalef(s, hs, s); + } + + int getOverlayColor(Mob* mob, float br, float a) { + Creeper* creeper = (Creeper*) mob; + + float step = creeper->getSwelling(a); + + if ((int) (step * 10) % 2 == 0) return 0; + + int _a = (int) (step * 0.2f * 255); + if (_a < 0) _a = 0; + if (_a > 255) _a = 255; + + int r = 255; + int g = 255; + int b = 255; + + return (_a << 24) | (r << 16) | (g << 8) | b; + } + + // int prepareArmor(Mob* mob, int layer, float a) { + //Creeper* creeper = (Creeper*) mob; + // if (creeper->isPowered()) { + // if (layer == 1) { + // float time = mob->tickCount + a; + // bindTexture("/armor/power.png"); + // glMatrixMode(GL_TEXTURE); + // glLoadIdentity(); + // float uo = time * 0.01f; + // float vo = time * 0.01f; + // glTranslatef(uo, vo, 0); + // setArmor(armorModel); + // glMatrixMode(GL_MODELVIEW); + // glEnable(GL_BLEND); + // float br = 0.5f; + // glColor4f2(br, br, br, 1); + // glDisable(GL_LIGHTING); + // glBlendFunc2(GL_ONE, GL_ONE); + // return 1; + // } + // if (layer == 2) { + // glMatrixMode(GL_TEXTURE); + // glLoadIdentity(); + // glMatrixMode(GL_MODELVIEW); + // glEnable(GL_LIGHTING); + // glDisable(GL_BLEND); + // } + // } + // return -1; + // } + + // int prepareArmorOverlay(Mob* mob, int layer, float a) { + // return -1; + // } +//private: + //Model* armorModel = /*new*/ CreeperModel(2); +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__CreeperRenderer_H__*/ diff --git a/src/client/renderer/entity/EntityRenderDispatcher.cpp b/src/client/renderer/entity/EntityRenderDispatcher.cpp index a3148fc..1462680 100755 --- a/src/client/renderer/entity/EntityRenderDispatcher.cpp +++ b/src/client/renderer/entity/EntityRenderDispatcher.cpp @@ -1,194 +1,194 @@ -#include "EntityRenderDispatcher.h" - -#include "../../Options.h" -#include "../../../world/entity/player/Player.h" -#include "../../../world/item/Item.h" -#include "../../../world/level/Level.h" -#include "../../../world/level/tile/Tile.h" -#include "../../model/ModelInclude.h" - -#include "ItemSpriteRenderer.h" -#include "FallingTileRenderer.h" - -#include "HumanoidMobRenderer.h" -#include "ItemRenderer.h" -#include "TntRenderer.h" -#include "TripodCameraRenderer.h" -#include "PigRenderer.h" -#include "MobRenderer.h" -#include "PlayerRenderer.h" - -#include "CreeperRenderer.h" -#include "SpiderRenderer.h" - -#include "ChickenRenderer.h" -#include "SheepRenderer.h" -#include "ArrowRenderer.h" -#include "PaintingRenderer.h" - - -/*static*/ -EntityRenderDispatcher* EntityRenderDispatcher::instance = NULL; - -/*static*/ -float EntityRenderDispatcher::xOff = 0, - EntityRenderDispatcher::yOff = 0, - EntityRenderDispatcher::zOff = 0; - -EntityRenderDispatcher::EntityRenderDispatcher() -: itemInHandRenderer(NULL) -{ - //@note: The Models (model/armor) will be deleted by resp. MobRenderer - assign( ER_ITEM_RENDERER, new ItemRenderer()); - assign( ER_HUMANOID_RENDERER, new HumanoidMobRenderer(new HumanoidModel(), 0)); - assign( ER_PIG_RENDERER, new PigRenderer(new PigModel(), NULL/*new PigModel(0.5f)*/, 0)); - assign( ER_COW_RENDERER, new MobRenderer(new CowModel(), 0)); - assign( ER_CHICKEN_RENDERER, new ChickenRenderer( new ChickenModel(), 0)); - assign( ER_SHEEP_RENDERER, new SheepRenderer(new SheepModel(), new SheepFurModel(), 0)); - assign( ER_SKELETON_RENDERER, new HumanoidMobRenderer(new SkeletonModel(), 0.5f)); - assign( ER_ZOMBIE_RENDERER, new HumanoidMobRenderer(new ZombieModel(), 0.5f)); - assign( ER_CREEPER_RENDERER, new CreeperRenderer()); - assign( ER_SPIDER_RENDERER, new SpiderRenderer()); - assign( ER_TNT_RENDERER, new TntRenderer()); - assign( ER_ARROW_RENDERER, new ArrowRenderer()); - assign( ER_PLAYER_RENDERER, new PlayerRenderer(new HumanoidModel(0, 0, 64, 64), 0)); - assign( ER_THROWNEGG_RENDERER, new ItemSpriteRenderer(Item::egg->getIcon(0))); - assign( ER_SNOWBALL_RENDERER, new ItemSpriteRenderer(Item::snowBall->getIcon(0))); - assign( ER_PAINTING_RENDERER, new PaintingRenderer()); - assign( ER_FALLINGTILE_RENDERER,new FallingTileRenderer()); - - for (RendererIterator it = _renderers.begin(); it != _renderers.end(); ++it) { - it->second->init(this); - } -} - -void EntityRenderDispatcher::destroy() -{ - if (instance) { - delete instance; - instance = NULL; - } -} - -EntityRenderDispatcher* EntityRenderDispatcher::getInstance() -{ - if (!instance) - instance = new EntityRenderDispatcher(); - - return instance; -} - -EntityRenderDispatcher::~EntityRenderDispatcher() -{ - std::set destroyed; - for (RendererCIterator cit = _renderers.begin(); cit != _renderers.end(); ++cit) { - if (destroyed.find(cit->second) == destroyed.end()) { - destroyed.insert(cit->second); - delete cit->second; - } - } -} - -void EntityRenderDispatcher::prepare( Level* level, Font* font, Mob* player, Options* options, float a ) -{ - this->level = level; - this->options = options; - this->cameraEntity = player; - this->_font = font; - if(player->isSleeping()) { - int t = level->getTile(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); - if (t == Tile::bed->id) { - int data = level->getData(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); - - int direction = data & 3; - playerRotY = float(direction * 90 + 180); - playerRotX = 0; - } - } - else { - playerRotY = player->yRotO + (player->yRot - player->yRotO) * a; - playerRotX = player->xRotO + (player->xRot - player->xRotO) * a; - } - - xPlayer = player->xOld + (player->x - player->xOld) * a; - yPlayer = player->yOld + (player->y - player->yOld) * a; - zPlayer = player->zOld + (player->z - player->zOld) * a; -} - -void EntityRenderDispatcher::render( Entity* entity, float a ) -{ - float x = entity->xOld + (entity->x - entity->xOld) * a; - float y = entity->yOld + (entity->y - entity->yOld) * a; - float z = entity->zOld + (entity->z - entity->zOld) * a; - float r = entity->yRotO + (entity->yRot - entity->yRotO) * a; - - float br = entity->getBrightness(a); - glColor4f2(br, br, br, 1); - - render(entity, x - xOff, y - yOff, z - zOff, r, a); -} - -void EntityRenderDispatcher::render( Entity* entity, float x, float y, float z, float rot, float a ) -{ - EntityRenderer* renderer = getRenderer(entity); - if (renderer != NULL) { - renderer->render(entity, x, y, z, rot, a); - //renderer->postRender(entity, x, y, z, rot, a); - } -} - -EntityRenderer* EntityRenderDispatcher::getRenderer( Entity* entity ) -{ - EntityRendererId rendererId = entity->entityRendererId; - - if (rendererId == ER_QUERY_RENDERER) - rendererId = entity->queryEntityRenderer(); - - return getRenderer(rendererId); -} - -EntityRenderer* EntityRenderDispatcher::getRenderer( EntityRendererId rendererId ) -{ - - EntityRenderer* renderer = NULL; - RendererCIterator cit = _renderers.find(rendererId); - if (cit != _renderers.end()) { - renderer = cit->second; - } - return renderer; -} - -void EntityRenderDispatcher::setLevel( Level* level ) -{ - this->level = level; -} - -void EntityRenderDispatcher::setMinecraft( Minecraft* minecraft ) -{ - this->minecraft = minecraft; -} - -float EntityRenderDispatcher::distanceToSqr( float x, float y, float z ) -{ - float xd = x - xPlayer; - float yd = y - yPlayer; - float zd = z - zPlayer; - return xd * xd + yd * yd + zd * zd; -} - -Font* EntityRenderDispatcher::getFont() -{ - return _font; -} - -void EntityRenderDispatcher::onGraphicsReset() -{ - for (RendererIterator it = _renderers.begin(); it != _renderers.end(); ++it) { - it->second->onGraphicsReset(); - } -} - -void EntityRenderDispatcher::assign( EntityRendererId id, EntityRenderer* renderer ) -{ - _renderers.insert(std::make_pair(id, renderer)); -} +#include "EntityRenderDispatcher.h" + +#include "../../Options.h" +#include "../../../world/entity/player/Player.h" +#include "../../../world/item/Item.h" +#include "../../../world/level/Level.h" +#include "../../../world/level/tile/Tile.h" +#include "../../model/ModelInclude.h" + +#include "ItemSpriteRenderer.h" +#include "FallingTileRenderer.h" + +#include "HumanoidMobRenderer.h" +#include "ItemRenderer.h" +#include "TntRenderer.h" +#include "TripodCameraRenderer.h" +#include "PigRenderer.h" +#include "MobRenderer.h" +#include "PlayerRenderer.h" + +#include "CreeperRenderer.h" +#include "SpiderRenderer.h" + +#include "ChickenRenderer.h" +#include "SheepRenderer.h" +#include "ArrowRenderer.h" +#include "PaintingRenderer.h" + + +/*static*/ +EntityRenderDispatcher* EntityRenderDispatcher::instance = NULL; + +/*static*/ +float EntityRenderDispatcher::xOff = 0, + EntityRenderDispatcher::yOff = 0, + EntityRenderDispatcher::zOff = 0; + +EntityRenderDispatcher::EntityRenderDispatcher() +: itemInHandRenderer(NULL) +{ + //@note: The Models (model/armor) will be deleted by resp. MobRenderer + assign( ER_ITEM_RENDERER, new ItemRenderer()); + assign( ER_HUMANOID_RENDERER, new HumanoidMobRenderer(new HumanoidModel(), 0)); + assign( ER_PIG_RENDERER, new PigRenderer(new PigModel(), NULL/*new PigModel(0.5f)*/, 0)); + assign( ER_COW_RENDERER, new MobRenderer(new CowModel(), 0)); + assign( ER_CHICKEN_RENDERER, new ChickenRenderer( new ChickenModel(), 0)); + assign( ER_SHEEP_RENDERER, new SheepRenderer(new SheepModel(), new SheepFurModel(), 0)); + assign( ER_SKELETON_RENDERER, new HumanoidMobRenderer(new SkeletonModel(), 0.5f)); + assign( ER_ZOMBIE_RENDERER, new HumanoidMobRenderer(new ZombieModel(), 0.5f)); + assign( ER_CREEPER_RENDERER, new CreeperRenderer()); + assign( ER_SPIDER_RENDERER, new SpiderRenderer()); + assign( ER_TNT_RENDERER, new TntRenderer()); + assign( ER_ARROW_RENDERER, new ArrowRenderer()); + assign( ER_PLAYER_RENDERER, new PlayerRenderer(new HumanoidModel(0, 0, 64, 64), 0)); + assign( ER_THROWNEGG_RENDERER, new ItemSpriteRenderer(Item::egg->getIcon(0))); + assign( ER_SNOWBALL_RENDERER, new ItemSpriteRenderer(Item::snowBall->getIcon(0))); + assign( ER_PAINTING_RENDERER, new PaintingRenderer()); + assign( ER_FALLINGTILE_RENDERER,new FallingTileRenderer()); + + for (RendererIterator it = _renderers.begin(); it != _renderers.end(); ++it) { + it->second->init(this); + } +} + +void EntityRenderDispatcher::destroy() +{ + if (instance) { + delete instance; + instance = NULL; + } +} + +EntityRenderDispatcher* EntityRenderDispatcher::getInstance() +{ + if (!instance) + instance = new EntityRenderDispatcher(); + + return instance; +} + +EntityRenderDispatcher::~EntityRenderDispatcher() +{ + std::set destroyed; + for (RendererCIterator cit = _renderers.begin(); cit != _renderers.end(); ++cit) { + if (destroyed.find(cit->second) == destroyed.end()) { + destroyed.insert(cit->second); + delete cit->second; + } + } +} + +void EntityRenderDispatcher::prepare( Level* level, Font* font, Mob* player, Options* options, float a ) +{ + this->level = level; + this->options = options; + this->cameraEntity = player; + this->_font = font; + if(player->isSleeping()) { + int t = level->getTile(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + if (t == Tile::bed->id) { + int data = level->getData(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + + int direction = data & 3; + playerRotY = float(direction * 90 + 180); + playerRotX = 0; + } + } + else { + playerRotY = player->yRotO + (player->yRot - player->yRotO) * a; + playerRotX = player->xRotO + (player->xRot - player->xRotO) * a; + } + + xPlayer = player->xOld + (player->x - player->xOld) * a; + yPlayer = player->yOld + (player->y - player->yOld) * a; + zPlayer = player->zOld + (player->z - player->zOld) * a; +} + +void EntityRenderDispatcher::render( Entity* entity, float a ) +{ + float x = entity->xOld + (entity->x - entity->xOld) * a; + float y = entity->yOld + (entity->y - entity->yOld) * a; + float z = entity->zOld + (entity->z - entity->zOld) * a; + float r = entity->yRotO + (entity->yRot - entity->yRotO) * a; + + float br = entity->getBrightness(a); + glColor4f2(br, br, br, 1); + + render(entity, x - xOff, y - yOff, z - zOff, r, a); +} + +void EntityRenderDispatcher::render( Entity* entity, float x, float y, float z, float rot, float a ) +{ + EntityRenderer* renderer = getRenderer(entity); + if (renderer != NULL) { + renderer->render(entity, x, y, z, rot, a); + //renderer->postRender(entity, x, y, z, rot, a); + } +} + +EntityRenderer* EntityRenderDispatcher::getRenderer( Entity* entity ) +{ + EntityRendererId rendererId = entity->entityRendererId; + + if (rendererId == ER_QUERY_RENDERER) + rendererId = entity->queryEntityRenderer(); + + return getRenderer(rendererId); +} + +EntityRenderer* EntityRenderDispatcher::getRenderer( EntityRendererId rendererId ) +{ + + EntityRenderer* renderer = NULL; + RendererCIterator cit = _renderers.find(rendererId); + if (cit != _renderers.end()) { + renderer = cit->second; + } + return renderer; +} + +void EntityRenderDispatcher::setLevel( Level* level ) +{ + this->level = level; +} + +void EntityRenderDispatcher::setMinecraft( Minecraft* minecraft ) +{ + this->minecraft = minecraft; +} + +float EntityRenderDispatcher::distanceToSqr( float x, float y, float z ) +{ + float xd = x - xPlayer; + float yd = y - yPlayer; + float zd = z - zPlayer; + return xd * xd + yd * yd + zd * zd; +} + +Font* EntityRenderDispatcher::getFont() +{ + return _font; +} + +void EntityRenderDispatcher::onGraphicsReset() +{ + for (RendererIterator it = _renderers.begin(); it != _renderers.end(); ++it) { + it->second->onGraphicsReset(); + } +} + +void EntityRenderDispatcher::assign( EntityRendererId id, EntityRenderer* renderer ) +{ + _renderers.insert(std::make_pair(id, renderer)); +} diff --git a/src/client/renderer/entity/EntityRenderDispatcher.h b/src/client/renderer/entity/EntityRenderDispatcher.h index 06c4eac..7f2d277 100755 --- a/src/client/renderer/entity/EntityRenderDispatcher.h +++ b/src/client/renderer/entity/EntityRenderDispatcher.h @@ -1,76 +1,76 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderDispatcher_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderDispatcher_H__ - -//package net.minecraft.client.renderer.entity; - -#include -#include "../../../world/entity/EntityRendererId.h" - -class EntityRenderer; -class Level; -class Font; -class Textures; -class ItemInHandRenderer; -class Minecraft; -class Textures; -class Options; -class Entity; -class Mob; - -class EntityRenderDispatcher -{ - typedef std::map RendererMap; - typedef RendererMap::iterator RendererIterator; - typedef RendererMap::const_iterator RendererCIterator; - -public: - EntityRenderDispatcher(); - ~EntityRenderDispatcher(); - static EntityRenderDispatcher* getInstance(); - - static void destroy(); - - void prepare(Level* level, Font* font, Mob* player, Options* options, float a); - - void render(Entity* entity, float a); - void render(Entity* entity, float x, float y, float z, float rot, float a); - EntityRenderer* getRenderer( Entity* entity ); - EntityRenderer* getRenderer( EntityRendererId rendererId ); - - void setLevel(Level* level); - void setMinecraft(Minecraft* minecraft); - - float distanceToSqr(float x, float y, float z); - - Font* getFont(); - - void onGraphicsReset(); -private: - void assign(EntityRendererId id, EntityRenderer* renderer); -public: - ItemInHandRenderer* itemInHandRenderer; - - //TripodCameraRenderer tripodCameraRenderer; - - static float xOff, yOff, zOff; - - Textures* textures; - Level* level; - Minecraft* minecraft; - Mob* cameraEntity; - - float playerRotY; - float playerRotX; - Options* options; - - float xPlayer, yPlayer, zPlayer; - -private: - static EntityRenderDispatcher* instance; - - Font* _font; - // For onGraphicsReset etc - RendererMap _renderers; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderDispatcher_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderDispatcher_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderDispatcher_H__ + +//package net.minecraft.client.renderer.entity; + +#include +#include "../../../world/entity/EntityRendererId.h" + +class EntityRenderer; +class Level; +class Font; +class Textures; +class ItemInHandRenderer; +class Minecraft; +class Textures; +class Options; +class Entity; +class Mob; + +class EntityRenderDispatcher +{ + typedef std::map RendererMap; + typedef RendererMap::iterator RendererIterator; + typedef RendererMap::const_iterator RendererCIterator; + +public: + EntityRenderDispatcher(); + ~EntityRenderDispatcher(); + static EntityRenderDispatcher* getInstance(); + + static void destroy(); + + void prepare(Level* level, Font* font, Mob* player, Options* options, float a); + + void render(Entity* entity, float a); + void render(Entity* entity, float x, float y, float z, float rot, float a); + EntityRenderer* getRenderer( Entity* entity ); + EntityRenderer* getRenderer( EntityRendererId rendererId ); + + void setLevel(Level* level); + void setMinecraft(Minecraft* minecraft); + + float distanceToSqr(float x, float y, float z); + + Font* getFont(); + + void onGraphicsReset(); +private: + void assign(EntityRendererId id, EntityRenderer* renderer); +public: + ItemInHandRenderer* itemInHandRenderer; + + //TripodCameraRenderer tripodCameraRenderer; + + static float xOff, yOff, zOff; + + Textures* textures; + Level* level; + Minecraft* minecraft; + Mob* cameraEntity; + + float playerRotY; + float playerRotX; + Options* options; + + float xPlayer, yPlayer, zPlayer; + +private: + static EntityRenderDispatcher* instance; + + Font* _font; + // For onGraphicsReset etc + RendererMap _renderers; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderDispatcher_H__*/ diff --git a/src/client/renderer/entity/EntityRenderer.cpp b/src/client/renderer/entity/EntityRenderer.cpp index 641581b..7242f62 100755 --- a/src/client/renderer/entity/EntityRenderer.cpp +++ b/src/client/renderer/entity/EntityRenderer.cpp @@ -1,249 +1,249 @@ -#include "EntityRenderer.h" - -#include "../Tesselator.h" -#include "../Textures.h" -#include "../gles.h" -#include "../../../world/phys/AABB.h" -#include "EntityRenderDispatcher.h" - -EntityRenderDispatcher* EntityRenderer::entityRenderDispatcher = NULL; - -EntityRenderer::EntityRenderer() -: shadowRadius(0), - shadowStrength(1.0f) -{} - -void EntityRenderer::bindTexture(const std::string& resourceName) { - entityRenderDispatcher->textures->loadAndBindTexture(resourceName); -} - -//bool bindTexture(std::string urlTexture, std::string backupTexture) { -// Textures t = entityRenderDispatcher.textures; - -// int id = t.loadHttpTexture(urlTexture, backupTexture); -// if (id >= 0) { -// t.bind(id); -// return true; -// } else { -// return false; -// } -//} - -/*static*/ -void EntityRenderer::render(const AABB& bb, float xo, float yo, float zo) { - glDisable2(GL_TEXTURE_2D); - Tesselator& t = Tesselator::instance; - glColor4f2(1, 1, 1, 1); - t.begin(); - t.offset(xo, yo, zo); - //t.normal(0, 0, -1); - t.vertex(bb.x0, bb.y1, bb.z0); - t.vertex(bb.x1, bb.y1, bb.z0); - t.vertex(bb.x1, bb.y0, bb.z0); - t.vertex(bb.x0, bb.y0, bb.z0); - - //t.normal(0, 0, 1); - t.vertex(bb.x0, bb.y0, bb.z1); - t.vertex(bb.x1, bb.y0, bb.z1); - t.vertex(bb.x1, bb.y1, bb.z1); - t.vertex(bb.x0, bb.y1, bb.z1); - - //t.normal(0, -1, 0); - t.vertex(bb.x0, bb.y0, bb.z0); - t.vertex(bb.x1, bb.y0, bb.z0); - t.vertex(bb.x1, bb.y0, bb.z1); - t.vertex(bb.x0, bb.y0, bb.z1); - - //t.normal(0, 1, 0); - t.vertex(bb.x0, bb.y1, bb.z1); - t.vertex(bb.x1, bb.y1, bb.z1); - t.vertex(bb.x1, bb.y1, bb.z0); - t.vertex(bb.x0, bb.y1, bb.z0); - - //t.normal(-1, 0, 0); - t.vertex(bb.x0, bb.y0, bb.z1); - t.vertex(bb.x0, bb.y1, bb.z1); - t.vertex(bb.x0, bb.y1, bb.z0); - t.vertex(bb.x0, bb.y0, bb.z0); - - //t.normal(1, 0, 0); - t.vertex(bb.x1, bb.y0, bb.z0); - t.vertex(bb.x1, bb.y1, bb.z0); - t.vertex(bb.x1, bb.y1, bb.z1); - t.vertex(bb.x1, bb.y0, bb.z1); - t.offset(0, 0, 0); - t.draw(); - glEnable2(GL_TEXTURE_2D); - // model.render(0, 1) -} - -/*static*/ -void EntityRenderer::renderFlat(const AABB& bb) { - Tesselator& t = Tesselator::instance; - t.begin(); - t.vertex(bb.x0, bb.y1, bb.z0); - t.vertex(bb.x1, bb.y1, bb.z0); - t.vertex(bb.x1, bb.y0, bb.z0); - t.vertex(bb.x0, bb.y0, bb.z0); - t.vertex(bb.x0, bb.y0, bb.z1); - t.vertex(bb.x1, bb.y0, bb.z1); - t.vertex(bb.x1, bb.y1, bb.z1); - t.vertex(bb.x0, bb.y1, bb.z1); - t.vertex(bb.x0, bb.y0, bb.z0); - t.vertex(bb.x1, bb.y0, bb.z0); - t.vertex(bb.x1, bb.y0, bb.z1); - t.vertex(bb.x0, bb.y0, bb.z1); - t.vertex(bb.x0, bb.y1, bb.z1); - t.vertex(bb.x1, bb.y1, bb.z1); - t.vertex(bb.x1, bb.y1, bb.z0); - t.vertex(bb.x0, bb.y1, bb.z0); - t.vertex(bb.x0, bb.y0, bb.z1); - t.vertex(bb.x0, bb.y1, bb.z1); - t.vertex(bb.x0, bb.y1, bb.z0); - t.vertex(bb.x0, bb.y0, bb.z0); - t.vertex(bb.x1, bb.y0, bb.z0); - t.vertex(bb.x1, bb.y1, bb.z0); - t.vertex(bb.x1, bb.y1, bb.z1); - t.vertex(bb.x1, bb.y0, bb.z1); - t.draw(); -} - -void EntityRenderer::init(EntityRenderDispatcher* entityRenderDispatcher) { - this->entityRenderDispatcher = entityRenderDispatcher; -} - -Font* EntityRenderer::getFont() { - return entityRenderDispatcher->getFont(); -} - -//void postRender(Entity entity, float x, float y, float z, float rot, float a) { -// if (entityRenderDispatcher.options.fancyGraphics && shadowRadius > 0) { -// float dist = entityRenderDispatcher.distanceToSqr(entity.x, entity.y, entity.z); -// float pow = (float) ((1 - dist / (16.0f * 16.0f)) * shadowStrength); -// if (pow > 0) { -// renderShadow(entity, x, y, z, pow, a); -// } -// } -// if (entity.isOnFire()) renderFlame(entity, x, y, z, a); -//} - -//void renderFlame(Entity e, float x, float y, float z, float a) { -// glDisable2(GL_LIGHTING); -// int tex = Tile.fire.tex; - -// int xt = (tex & 0xf) << 4; -// int yt = tex & 0xf0; - -// float u0 = (xt) / 256.0f; -// float u1 = (xt + 15.99f) / 256.0f; -// float v0 = (yt) / 256.0f; -// float v1 = (yt + 15.99f) / 256.0f; - -// glPushMatrix2(); -// glTranslatef2((float) x, (float) y, (float) z); - -// float s = e.bbWidth * 1.4f; -// glScalef2(s, s, s); -// bindTexture("terrain.png"); -// Tesselator t = Tesselator.instance; - -// float r = 1.0f; -// float xo = 0.5f; -// float yo = 0.0f; - -// float h = e.bbHeight / e.bbWidth; - -// glRotatef2(-entityRenderDispatcher.playerRotY, 0, 1, 0); -// glTranslatef2(0, 0, -0.4f + ((int) h) * 0.02f); -// glColor4f2(1, 1, 1, 1); -// // glRotatef2(-playerRotX, 1, 0, 0); -// t.begin(); -// while (h > 0) { -// t.vertexUV(r - xo, 0 - yo, 0, u1, v1); -// t.vertexUV(0 - xo, 0 - yo, 0, u0, v1); -// t.vertexUV(0 - xo, 1.4f - yo, 0, u0, v0); -// t.vertexUV(r - xo, 1.4f - yo, 0, u1, v0); -// h -= 1; -// yo -= 1; -// r *= 0.9f; -// glTranslatef2(0, 0, -0.04f); -// } -// t.end(); -// glPopMatrix2(); -// glEnable2(GL_LIGHTING); -//} - -//void renderShadow(Entity e, float x, float y, float z, float pow, float a) { -// glEnable2(GL_BLEND); -// glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - -// Textures textures = entityRenderDispatcher.textures; -// textures.bind(textures.loadTexture("%clamp%/misc/shadow.png")); - -// Level level = getLevel(); - -// glDepthMask(false); -// float r = shadowRadius; - -// float ex = e.xOld + (e.x - e.xOld) * a; -// float ey = e.yOld + (e.y - e.yOld) * a + e.getShadowHeightOffs(); -// float ez = e.zOld + (e.z - e.zOld) * a; - -// int x0 = Mth.floor(ex - r); -// int x1 = Mth.floor(ex + r); -// int y0 = Mth.floor(ey - r); -// int y1 = Mth.floor(ey); -// int z0 = Mth.floor(ez - r); -// int z1 = Mth.floor(ez + r); - -// float xo = x - ex; -// float yo = y - ey; -// float zo = z - ez; - -// Tesselator tt = Tesselator.instance; -// tt.begin(); -// for (int xt = x0; xt <= x1; xt++) -// for (int yt = y0; yt <= y1; yt++) -// for (int zt = z0; zt <= z1; zt++) { -// int t = level.getTile(xt, yt - 1, zt); -// if (t > 0 && level.getRawBrightness(xt, yt, zt) > 3) { -// renderTileShadow(Tile.tiles[t], x, y + e.getShadowHeightOffs(), z, xt, yt, zt, pow, r, xo, yo + e.getShadowHeightOffs(), zo); -// } -// } -// tt.end(); - -// glColor4f2(1, 1, 1, 1); -// glDisable2(GL_BLEND); -// glDepthMask(true); -//} - -//Level* getLevel() { -// return entityRenderDispatcher.level; -//} - -//void renderTileShadow(Tile tt, float x, float y, float z, int xt, int yt, int zt, float pow, float r, float xo, float yo, float zo) { -// Tesselator t = Tesselator.instance; -// if (!tt.isCubeShaped()) return; - -// float a = ((pow - (y - (yt + yo)) / 2) * 0.5f) * getLevel().getBrightness(xt, yt, zt); -// if (a < 0) return; -// if (a > 1) a = 1; -// t.color(1, 1, 1, (float) a); -// // glColor4f2(1, 1, 1, (float) a); - -// float x0 = xt + tt.xx0 + xo; -// float x1 = xt + tt.xx1 + xo; -// float y0 = yt + tt.yy0 + yo + 1.0 / 64.0f; -// float z0 = zt + tt.zz0 + zo; -// float z1 = zt + tt.zz1 + zo; - -// float u0 = (float) ((x - (x0)) / 2 / r + 0.5f); -// float u1 = (float) ((x - (x1)) / 2 / r + 0.5f); -// float v0 = (float) ((z - (z0)) / 2 / r + 0.5f); -// float v1 = (float) ((z - (z1)) / 2 / r + 0.5f); - -// t.vertexUV(x0, y0, z0, u0, v0); -// t.vertexUV(x0, y0, z1, u0, v1); -// t.vertexUV(x1, y0, z1, u1, v1); -// t.vertexUV(x1, y0, z0, u1, v0); -//} +#include "EntityRenderer.h" + +#include "../Tesselator.h" +#include "../Textures.h" +#include "../gles.h" +#include "../../../world/phys/AABB.h" +#include "EntityRenderDispatcher.h" + +EntityRenderDispatcher* EntityRenderer::entityRenderDispatcher = NULL; + +EntityRenderer::EntityRenderer() +: shadowRadius(0), + shadowStrength(1.0f) +{} + +void EntityRenderer::bindTexture(const std::string& resourceName) { + entityRenderDispatcher->textures->loadAndBindTexture(resourceName); +} + +//bool bindTexture(std::string urlTexture, std::string backupTexture) { +// Textures t = entityRenderDispatcher.textures; + +// int id = t.loadHttpTexture(urlTexture, backupTexture); +// if (id >= 0) { +// t.bind(id); +// return true; +// } else { +// return false; +// } +//} + +/*static*/ +void EntityRenderer::render(const AABB& bb, float xo, float yo, float zo) { + glDisable2(GL_TEXTURE_2D); + Tesselator& t = Tesselator::instance; + glColor4f2(1, 1, 1, 1); + t.begin(); + t.offset(xo, yo, zo); + //t.normal(0, 0, -1); + t.vertex(bb.x0, bb.y1, bb.z0); + t.vertex(bb.x1, bb.y1, bb.z0); + t.vertex(bb.x1, bb.y0, bb.z0); + t.vertex(bb.x0, bb.y0, bb.z0); + + //t.normal(0, 0, 1); + t.vertex(bb.x0, bb.y0, bb.z1); + t.vertex(bb.x1, bb.y0, bb.z1); + t.vertex(bb.x1, bb.y1, bb.z1); + t.vertex(bb.x0, bb.y1, bb.z1); + + //t.normal(0, -1, 0); + t.vertex(bb.x0, bb.y0, bb.z0); + t.vertex(bb.x1, bb.y0, bb.z0); + t.vertex(bb.x1, bb.y0, bb.z1); + t.vertex(bb.x0, bb.y0, bb.z1); + + //t.normal(0, 1, 0); + t.vertex(bb.x0, bb.y1, bb.z1); + t.vertex(bb.x1, bb.y1, bb.z1); + t.vertex(bb.x1, bb.y1, bb.z0); + t.vertex(bb.x0, bb.y1, bb.z0); + + //t.normal(-1, 0, 0); + t.vertex(bb.x0, bb.y0, bb.z1); + t.vertex(bb.x0, bb.y1, bb.z1); + t.vertex(bb.x0, bb.y1, bb.z0); + t.vertex(bb.x0, bb.y0, bb.z0); + + //t.normal(1, 0, 0); + t.vertex(bb.x1, bb.y0, bb.z0); + t.vertex(bb.x1, bb.y1, bb.z0); + t.vertex(bb.x1, bb.y1, bb.z1); + t.vertex(bb.x1, bb.y0, bb.z1); + t.offset(0, 0, 0); + t.draw(); + glEnable2(GL_TEXTURE_2D); + // model.render(0, 1) +} + +/*static*/ +void EntityRenderer::renderFlat(const AABB& bb) { + Tesselator& t = Tesselator::instance; + t.begin(); + t.vertex(bb.x0, bb.y1, bb.z0); + t.vertex(bb.x1, bb.y1, bb.z0); + t.vertex(bb.x1, bb.y0, bb.z0); + t.vertex(bb.x0, bb.y0, bb.z0); + t.vertex(bb.x0, bb.y0, bb.z1); + t.vertex(bb.x1, bb.y0, bb.z1); + t.vertex(bb.x1, bb.y1, bb.z1); + t.vertex(bb.x0, bb.y1, bb.z1); + t.vertex(bb.x0, bb.y0, bb.z0); + t.vertex(bb.x1, bb.y0, bb.z0); + t.vertex(bb.x1, bb.y0, bb.z1); + t.vertex(bb.x0, bb.y0, bb.z1); + t.vertex(bb.x0, bb.y1, bb.z1); + t.vertex(bb.x1, bb.y1, bb.z1); + t.vertex(bb.x1, bb.y1, bb.z0); + t.vertex(bb.x0, bb.y1, bb.z0); + t.vertex(bb.x0, bb.y0, bb.z1); + t.vertex(bb.x0, bb.y1, bb.z1); + t.vertex(bb.x0, bb.y1, bb.z0); + t.vertex(bb.x0, bb.y0, bb.z0); + t.vertex(bb.x1, bb.y0, bb.z0); + t.vertex(bb.x1, bb.y1, bb.z0); + t.vertex(bb.x1, bb.y1, bb.z1); + t.vertex(bb.x1, bb.y0, bb.z1); + t.draw(); +} + +void EntityRenderer::init(EntityRenderDispatcher* entityRenderDispatcher) { + this->entityRenderDispatcher = entityRenderDispatcher; +} + +Font* EntityRenderer::getFont() { + return entityRenderDispatcher->getFont(); +} + +//void postRender(Entity entity, float x, float y, float z, float rot, float a) { +// if (entityRenderDispatcher.options.fancyGraphics && shadowRadius > 0) { +// float dist = entityRenderDispatcher.distanceToSqr(entity.x, entity.y, entity.z); +// float pow = (float) ((1 - dist / (16.0f * 16.0f)) * shadowStrength); +// if (pow > 0) { +// renderShadow(entity, x, y, z, pow, a); +// } +// } +// if (entity.isOnFire()) renderFlame(entity, x, y, z, a); +//} + +//void renderFlame(Entity e, float x, float y, float z, float a) { +// glDisable2(GL_LIGHTING); +// int tex = Tile.fire.tex; + +// int xt = (tex & 0xf) << 4; +// int yt = tex & 0xf0; + +// float u0 = (xt) / 256.0f; +// float u1 = (xt + 15.99f) / 256.0f; +// float v0 = (yt) / 256.0f; +// float v1 = (yt + 15.99f) / 256.0f; + +// glPushMatrix2(); +// glTranslatef2((float) x, (float) y, (float) z); + +// float s = e.bbWidth * 1.4f; +// glScalef2(s, s, s); +// bindTexture("terrain.png"); +// Tesselator t = Tesselator.instance; + +// float r = 1.0f; +// float xo = 0.5f; +// float yo = 0.0f; + +// float h = e.bbHeight / e.bbWidth; + +// glRotatef2(-entityRenderDispatcher.playerRotY, 0, 1, 0); +// glTranslatef2(0, 0, -0.4f + ((int) h) * 0.02f); +// glColor4f2(1, 1, 1, 1); +// // glRotatef2(-playerRotX, 1, 0, 0); +// t.begin(); +// while (h > 0) { +// t.vertexUV(r - xo, 0 - yo, 0, u1, v1); +// t.vertexUV(0 - xo, 0 - yo, 0, u0, v1); +// t.vertexUV(0 - xo, 1.4f - yo, 0, u0, v0); +// t.vertexUV(r - xo, 1.4f - yo, 0, u1, v0); +// h -= 1; +// yo -= 1; +// r *= 0.9f; +// glTranslatef2(0, 0, -0.04f); +// } +// t.end(); +// glPopMatrix2(); +// glEnable2(GL_LIGHTING); +//} + +//void renderShadow(Entity e, float x, float y, float z, float pow, float a) { +// glEnable2(GL_BLEND); +// glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// Textures textures = entityRenderDispatcher.textures; +// textures.bind(textures.loadTexture("%clamp%/misc/shadow.png")); + +// Level level = getLevel(); + +// glDepthMask(false); +// float r = shadowRadius; + +// float ex = e.xOld + (e.x - e.xOld) * a; +// float ey = e.yOld + (e.y - e.yOld) * a + e.getShadowHeightOffs(); +// float ez = e.zOld + (e.z - e.zOld) * a; + +// int x0 = Mth.floor(ex - r); +// int x1 = Mth.floor(ex + r); +// int y0 = Mth.floor(ey - r); +// int y1 = Mth.floor(ey); +// int z0 = Mth.floor(ez - r); +// int z1 = Mth.floor(ez + r); + +// float xo = x - ex; +// float yo = y - ey; +// float zo = z - ez; + +// Tesselator tt = Tesselator.instance; +// tt.begin(); +// for (int xt = x0; xt <= x1; xt++) +// for (int yt = y0; yt <= y1; yt++) +// for (int zt = z0; zt <= z1; zt++) { +// int t = level.getTile(xt, yt - 1, zt); +// if (t > 0 && level.getRawBrightness(xt, yt, zt) > 3) { +// renderTileShadow(Tile.tiles[t], x, y + e.getShadowHeightOffs(), z, xt, yt, zt, pow, r, xo, yo + e.getShadowHeightOffs(), zo); +// } +// } +// tt.end(); + +// glColor4f2(1, 1, 1, 1); +// glDisable2(GL_BLEND); +// glDepthMask(true); +//} + +//Level* getLevel() { +// return entityRenderDispatcher.level; +//} + +//void renderTileShadow(Tile tt, float x, float y, float z, int xt, int yt, int zt, float pow, float r, float xo, float yo, float zo) { +// Tesselator t = Tesselator.instance; +// if (!tt.isCubeShaped()) return; + +// float a = ((pow - (y - (yt + yo)) / 2) * 0.5f) * getLevel().getBrightness(xt, yt, zt); +// if (a < 0) return; +// if (a > 1) a = 1; +// t.color(1, 1, 1, (float) a); +// // glColor4f2(1, 1, 1, (float) a); + +// float x0 = xt + tt.xx0 + xo; +// float x1 = xt + tt.xx1 + xo; +// float y0 = yt + tt.yy0 + yo + 1.0 / 64.0f; +// float z0 = zt + tt.zz0 + zo; +// float z1 = zt + tt.zz1 + zo; + +// float u0 = (float) ((x - (x0)) / 2 / r + 0.5f); +// float u1 = (float) ((x - (x1)) / 2 / r + 0.5f); +// float v0 = (float) ((z - (z0)) / 2 / r + 0.5f); +// float v1 = (float) ((z - (z1)) / 2 / r + 0.5f); + +// t.vertexUV(x0, y0, z0, u0, v0); +// t.vertexUV(x0, y0, z1, u0, v1); +// t.vertexUV(x1, y0, z1, u1, v1); +// t.vertexUV(x1, y0, z0, u1, v0); +//} diff --git a/src/client/renderer/entity/EntityRenderer.h b/src/client/renderer/entity/EntityRenderer.h index 318f7f2..615e039 100755 --- a/src/client/renderer/entity/EntityRenderer.h +++ b/src/client/renderer/entity/EntityRenderer.h @@ -1,40 +1,40 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include -#include "../../model/HumanoidModel.h" - -class Textures; -class Tesselator; -class EntityRenderDispatcher; -class Entity; -class AABB; -class Font; - -class EntityRenderer -{ -protected: - EntityRenderer(); -public: - virtual ~EntityRenderer() {} - void init(EntityRenderDispatcher* entityRenderDispatcher); - - virtual void render(Entity* entity, float x, float y, float z, float rot, float a) = 0; - static void render(const AABB& bb, float xo, float yo, float zo); - static void renderFlat(const AABB& bb); - - Font* getFont(); - - virtual void onGraphicsReset() {} - -protected: - void bindTexture(const std::string& resourceName); - - float shadowRadius; - float shadowStrength; - static EntityRenderDispatcher* entityRenderDispatcher; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include +#include "../../model/HumanoidModel.h" + +class Textures; +class Tesselator; +class EntityRenderDispatcher; +class Entity; +class AABB; +class Font; + +class EntityRenderer +{ +protected: + EntityRenderer(); +public: + virtual ~EntityRenderer() {} + void init(EntityRenderDispatcher* entityRenderDispatcher); + + virtual void render(Entity* entity, float x, float y, float z, float rot, float a) = 0; + static void render(const AABB& bb, float xo, float yo, float zo); + static void renderFlat(const AABB& bb); + + Font* getFont(); + + virtual void onGraphicsReset() {} + +protected: + void bindTexture(const std::string& resourceName); + + float shadowRadius; + float shadowStrength; + static EntityRenderDispatcher* entityRenderDispatcher; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__EntityRenderer_H__*/ diff --git a/src/client/renderer/entity/FallingTileRenderer.cpp b/src/client/renderer/entity/FallingTileRenderer.cpp index 0cf9eb4..5cb78d3 100755 --- a/src/client/renderer/entity/FallingTileRenderer.cpp +++ b/src/client/renderer/entity/FallingTileRenderer.cpp @@ -1,32 +1,32 @@ -#include "FallingTileRenderer.h" -#include "../TileRenderer.h" -#include "../../../world/entity/item/FallingTile.h" -#include "../../../world/level/tile/Tile.h" -#include "../../../world/level/Level.h" - -FallingTileRenderer::FallingTileRenderer() { - this->shadowRadius = 0.5f; - tileRenderer = new TileRenderer(); -} - -FallingTileRenderer::~FallingTileRenderer() { - delete tileRenderer; -} - -void FallingTileRenderer::render( Entity* e, float x, float y, float z, float rot, float a ) { - FallingTile* tile = (FallingTile*) e; - - glPushMatrix(); - glTranslatef(x, y, z); - - bindTexture("terrain.png"); - Tile* tt = Tile::tiles[tile->tile]; - - Level* level = tile->getLevel(); - - if (tt != NULL) { - tileRenderer->renderBlock(tt, level, Mth::floor(tile->x), Mth::floor(tile->y), Mth::floor(tile->z)); - - } - glPopMatrix(); -} +#include "FallingTileRenderer.h" +#include "../TileRenderer.h" +#include "../../../world/entity/item/FallingTile.h" +#include "../../../world/level/tile/Tile.h" +#include "../../../world/level/Level.h" + +FallingTileRenderer::FallingTileRenderer() { + this->shadowRadius = 0.5f; + tileRenderer = new TileRenderer(); +} + +FallingTileRenderer::~FallingTileRenderer() { + delete tileRenderer; +} + +void FallingTileRenderer::render( Entity* e, float x, float y, float z, float rot, float a ) { + FallingTile* tile = (FallingTile*) e; + + glPushMatrix(); + glTranslatef(x, y, z); + + bindTexture("terrain.png"); + Tile* tt = Tile::tiles[tile->tile]; + + Level* level = tile->getLevel(); + + if (tt != NULL) { + tileRenderer->renderBlock(tt, level, Mth::floor(tile->x), Mth::floor(tile->y), Mth::floor(tile->z)); + + } + glPopMatrix(); +} diff --git a/src/client/renderer/entity/FallingTileRenderer.h b/src/client/renderer/entity/FallingTileRenderer.h index 0147c8a..19544a9 100755 --- a/src/client/renderer/entity/FallingTileRenderer.h +++ b/src/client/renderer/entity/FallingTileRenderer.h @@ -1,22 +1,22 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__FallingTileRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__FallingTileRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "EntityRenderer.h" - -class TileRenderer; - -class FallingTileRenderer: public EntityRenderer -{ - typedef EntityRenderer super; -public: - FallingTileRenderer(); - ~FallingTileRenderer(); - - void render(Entity* e, float x, float y, float z, float rot, float a); -private: - TileRenderer* tileRenderer; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__FallingTileRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__FallingTileRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__FallingTileRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "EntityRenderer.h" + +class TileRenderer; + +class FallingTileRenderer: public EntityRenderer +{ + typedef EntityRenderer super; +public: + FallingTileRenderer(); + ~FallingTileRenderer(); + + void render(Entity* e, float x, float y, float z, float rot, float a); +private: + TileRenderer* tileRenderer; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__FallingTileRenderer_H__*/ diff --git a/src/client/renderer/entity/HumanoidMobRenderer.cpp b/src/client/renderer/entity/HumanoidMobRenderer.cpp index 0922767..374056d 100755 --- a/src/client/renderer/entity/HumanoidMobRenderer.cpp +++ b/src/client/renderer/entity/HumanoidMobRenderer.cpp @@ -1,223 +1,223 @@ -#include "HumanoidMobRenderer.h" -#include "EntityRenderDispatcher.h" -#include "../ItemInHandRenderer.h" -#include "../TileRenderer.h" -#include "../Tesselator.h" -#include "../../model/HumanoidModel.h" -#include "../../../world/level/tile/Tile.h" -#include "../../../world/entity/player/Player.h" -#include "../../../world/entity/player/Inventory.h" -#include "../../../world/item/ItemInstance.h" -#include "../../../world/item/Item.h" -#include "../../../world/item/BowItem.h" - -HumanoidMobRenderer::HumanoidMobRenderer(HumanoidModel* humanoidModel, float shadow) -: super(humanoidModel, shadow), - humanoidModel(humanoidModel), - lastCapeXRot(0), - lastCapeZRot(0) -{ -} - -void HumanoidMobRenderer::renderHand() { - humanoidModel->attackTime = 0; - humanoidModel->setupAnim(0, 0, 0, 0, 0, 1 / 16.0f); - - //@attn @cuberender @enableClientState @vertexarray - glEnableClientState2(GL_VERTEX_ARRAY); - glEnableClientState2(GL_TEXTURE_COORD_ARRAY); - //glEnableClientState2(GL_COLOR_ARRAY); - humanoidModel->arm0.render(1 / 16.0f); - glDisableClientState2(GL_VERTEX_ARRAY); - glDisableClientState2(GL_TEXTURE_COORD_ARRAY); - //glDisableClientState2(GL_COLOR_ARRAY); -} - -void HumanoidMobRenderer::additionalRendering(Mob* mob, float a) { - ItemInstance* item = mob->getCarriedItem(); - if (item != NULL && item->count > 0) { - glPushMatrix2(); - humanoidModel->arm0.translateTo(1 / 16.0f); - glTranslatef2(-1.0f / 16.0f, 7.0f / 16.0f, 1.0f / 16.0f); - - if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { - float s = 8.0f / 16.0f; - glTranslatef2(0.0f, 3.0f / 16.0f, -5 / 16.0f); - s *= 0.75f; - glRotatef2(20.0f, 1.0f, 0.0f, 0.0f); - glRotatef2(45.0f, 0.0f, 1.0f, 0.0f); - glScalef2(s, -s, s); - } else if (item->id == Item::bow->id) { - const float s = 10.0f / 16.0f; - glTranslatef2(0 / 16.0f, 2 / 16.0f, 5 / 16.0f); - glRotatef2(-20, 0, 1, 0); - glScalef2(s, -s, s); - glRotatef2(-100, 1, 0, 0); - glRotatef2(45, 0, 1, 0); - } else if (Item::items[item->id]->isHandEquipped()) { - float s = 10.0f / 16.0f; - glTranslatef2(0.0f, 3.0f / 16.0f, 0.0f); - glScalef2(s, -s, s); - glRotatef2(-100.0f, 1.0f, 0.0f, 0.0f); - glRotatef2(45.0f, 0.0f, 1.0f, 0.0f); - } else { - float s = 6 / 16.0f; - glTranslatef2(+4 / 16.0f, +3 / 16.0f, -3 / 16.0f); - glScalef2(s, s, s); - glRotatef2(60.0f, 0.0f, 0.0f, 1.0f); - glRotatef2(-90.0f, 1.0f, 0.0f, 0.0f); - glRotatef2(20.0f, 0.0f, 0.0f, 1.0f); - } - entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item); - glPopMatrix2(); - } - - // Render player cape if available -{ - Player* player = Player::asPlayer(mob); - if (player) { - const std::string capeTex = player->getCapeTexture(); - if (!capeTex.empty()) { - - bindTexture(capeTex); - - glPushMatrix2(); - - // Attach to player body - humanoidModel->body.translateTo(1 / 16.0f); - - // Convert model units (pixels) to world units - glScalef2(1.0f / 16.0f, 1.0f / 16.0f, 1.0f / 16.0f); - - // Position cape slightly down and behind the shoulders - glTranslatef2(0.0f, 1.0f, 2.0f); - - // Java-like cape physics (interpolated inertia + body motion) - float pt = a; - - double capeX = player->getCapePrevX() + (player->getCapeX() - player->getCapePrevX()) * pt; - double capeY = player->getCapePrevY() + (player->getCapeY() - player->getCapePrevY()) * pt; - double capeZ = player->getCapePrevZ() + (player->getCapeZ() - player->getCapePrevZ()) * pt; - - double px = player->xo + (player->x - player->xo) * pt; - double py = player->yo + (player->y - player->yo) * pt; - double pz = player->zo + (player->z - player->zo) * pt; - - double dx = capeX - px; - double dy = capeY - py; - double dz = capeZ - pz; - - float bodyYaw = player->yBodyRotO + (player->yBodyRot - player->yBodyRotO) * pt; - - float rad = bodyYaw * Mth::PI / 180.0f; - double sinYaw = Mth::sin(rad); - double cosYaw = -Mth::cos(rad); - - float forward = (float)(dx * sinYaw + dz * cosYaw) * 100.0f; - float sideways = (float)(dx * cosYaw - dz * sinYaw) * 100.0f; - if (forward < 0.0f) forward = 0.0f; - - float lift = (float)dy * 10.0f; - if (lift < -6.0f) lift = -6.0f; - if (lift > 32.0f) lift = 32.0f; - - float walk = - Mth::sin((player->walkAnimPos + player->walkAnimSpeed) * 6.0f) * - 32.0f * - player->walkAnimSpeed; - - float capeXRot = 6.0f + forward / 2.0f + lift + walk; - float capeZRot = sideways / 2.0f; - - // Smooth out jitter by lerping from the previous frame - const float smooth = 0.3f; - capeXRot = lastCapeXRot + (capeXRot - lastCapeXRot) * smooth; - capeZRot = lastCapeZRot + (capeZRot - lastCapeZRot) * smooth; - - lastCapeXRot = capeXRot; - lastCapeZRot = capeZRot; - - glRotatef2(capeXRot, 1.0f, 0.0f, 0.0f); - glRotatef2(capeZRot, 0.0f, 0.0f, 1.0f); - - Tesselator& t = Tesselator::instance; - t.begin(); - - // UV coordinates (64x32 skin layout) - const float u0 = 1.0f / 64.0f; - const float u1 = 11.0f / 64.0f; - const float u2 = 12.0f / 64.0f; - const float u3 = 22.0f / 64.0f; - - const float uL0 = 0.0f / 64.0f; - const float uL1 = 1.0f / 64.0f; - - const float uR0 = 11.0f / 64.0f; - const float uR1 = 12.0f / 64.0f; - - const float v0 = 0.0f / 32.0f; - const float v1 = 1.0f / 32.0f; - - const float vTop = 1.0f / 32.0f; - const float vBottom = 17.0f / 32.0f; - - // Cape size (10x16x1 pixels) - const float halfW = 5.0f; - const float height = 16.0f; - const float depth = 1.0f; - - // Front - t.tex(u2, vTop); t.vertex(-halfW, 0.0f, 0.0f); - t.tex(u3, vTop); t.vertex(halfW, 0.0f, 0.0f); - t.tex(u3, vBottom); t.vertex(halfW, height, 0.0f); - t.tex(u2, vBottom); t.vertex(-halfW, height, 0.0f); - - // Back - t.tex(u0, vTop); t.vertex(halfW, 0.0f, depth); - t.tex(u1, vTop); t.vertex(-halfW, 0.0f, depth); - t.tex(u1, vBottom); t.vertex(-halfW, height, depth); - t.tex(u0, vBottom); t.vertex(halfW, height, depth); - - // Left - t.tex(uL0, vTop); t.vertex(-halfW, 0.0f, depth); - t.tex(uL1, vTop); t.vertex(-halfW, 0.0f, 0.0f); - t.tex(uL1, vBottom); t.vertex(-halfW, height, 0.0f); - t.tex(uL0, vBottom); t.vertex(-halfW, height, depth); - - // Right - t.tex(uR0, vTop); t.vertex(halfW, 0.0f, 0.0f); - t.tex(uR1, vTop); t.vertex(halfW, 0.0f, depth); - t.tex(uR1, vBottom); t.vertex(halfW, height, depth); - t.tex(uR0, vBottom); t.vertex(halfW, height, 0.0f); - - // Top - t.tex(u0, v0); t.vertex(-halfW, 0.0f, depth); - t.tex(u1, v0); t.vertex(halfW, 0.0f, depth); - t.tex(u1, v1); t.vertex(halfW, 0.0f, 0.0f); - t.tex(u0, v1); t.vertex(-halfW, 0.0f, 0.0f); - - // Bottom - t.tex(u2, v0); t.vertex(halfW, height, 0.0f); - t.tex(u3, v0); t.vertex(-halfW, height, 0.0f); - t.tex(u3, v1); t.vertex(-halfW, height, depth); - t.tex(u2, v1); t.vertex(halfW, height, depth); - - t.draw(); - - glPopMatrix2(); - } - } -} -} - -void HumanoidMobRenderer::render( Entity* mob_, float x, float y, float z, float rot, float a ) { - Mob* mob = (Mob*)mob_; - ItemInstance* carriedItem = mob->getCarriedItem(); - if(carriedItem != NULL) - humanoidModel->holdingRightHand = true; - - humanoidModel->sneaking = mob->isSneaking(); - - super::render(mob_, x, y, z, rot, a); - humanoidModel->holdingRightHand = false; -} +#include "HumanoidMobRenderer.h" +#include "EntityRenderDispatcher.h" +#include "../ItemInHandRenderer.h" +#include "../TileRenderer.h" +#include "../Tesselator.h" +#include "../../model/HumanoidModel.h" +#include "../../../world/level/tile/Tile.h" +#include "../../../world/entity/player/Player.h" +#include "../../../world/entity/player/Inventory.h" +#include "../../../world/item/ItemInstance.h" +#include "../../../world/item/Item.h" +#include "../../../world/item/BowItem.h" + +HumanoidMobRenderer::HumanoidMobRenderer(HumanoidModel* humanoidModel, float shadow) +: super(humanoidModel, shadow), + humanoidModel(humanoidModel), + lastCapeXRot(0), + lastCapeZRot(0) +{ +} + +void HumanoidMobRenderer::renderHand() { + humanoidModel->attackTime = 0; + humanoidModel->setupAnim(0, 0, 0, 0, 0, 1 / 16.0f); + + //@attn @cuberender @enableClientState @vertexarray + glEnableClientState2(GL_VERTEX_ARRAY); + glEnableClientState2(GL_TEXTURE_COORD_ARRAY); + //glEnableClientState2(GL_COLOR_ARRAY); + humanoidModel->arm0.render(1 / 16.0f); + glDisableClientState2(GL_VERTEX_ARRAY); + glDisableClientState2(GL_TEXTURE_COORD_ARRAY); + //glDisableClientState2(GL_COLOR_ARRAY); +} + +void HumanoidMobRenderer::additionalRendering(Mob* mob, float a) { + ItemInstance* item = mob->getCarriedItem(); + if (item != NULL && item->count > 0) { + glPushMatrix2(); + humanoidModel->arm0.translateTo(1 / 16.0f); + glTranslatef2(-1.0f / 16.0f, 7.0f / 16.0f, 1.0f / 16.0f); + + if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { + float s = 8.0f / 16.0f; + glTranslatef2(0.0f, 3.0f / 16.0f, -5 / 16.0f); + s *= 0.75f; + glRotatef2(20.0f, 1.0f, 0.0f, 0.0f); + glRotatef2(45.0f, 0.0f, 1.0f, 0.0f); + glScalef2(s, -s, s); + } else if (item->id == Item::bow->id) { + const float s = 10.0f / 16.0f; + glTranslatef2(0 / 16.0f, 2 / 16.0f, 5 / 16.0f); + glRotatef2(-20, 0, 1, 0); + glScalef2(s, -s, s); + glRotatef2(-100, 1, 0, 0); + glRotatef2(45, 0, 1, 0); + } else if (Item::items[item->id]->isHandEquipped()) { + float s = 10.0f / 16.0f; + glTranslatef2(0.0f, 3.0f / 16.0f, 0.0f); + glScalef2(s, -s, s); + glRotatef2(-100.0f, 1.0f, 0.0f, 0.0f); + glRotatef2(45.0f, 0.0f, 1.0f, 0.0f); + } else { + float s = 6 / 16.0f; + glTranslatef2(+4 / 16.0f, +3 / 16.0f, -3 / 16.0f); + glScalef2(s, s, s); + glRotatef2(60.0f, 0.0f, 0.0f, 1.0f); + glRotatef2(-90.0f, 1.0f, 0.0f, 0.0f); + glRotatef2(20.0f, 0.0f, 0.0f, 1.0f); + } + entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item); + glPopMatrix2(); + } + + // Render player cape if available +{ + Player* player = Player::asPlayer(mob); + if (player) { + const std::string capeTex = player->getCapeTexture(); + if (!capeTex.empty()) { + + bindTexture(capeTex); + + glPushMatrix2(); + + // Attach to player body + humanoidModel->body.translateTo(1 / 16.0f); + + // Convert model units (pixels) to world units + glScalef2(1.0f / 16.0f, 1.0f / 16.0f, 1.0f / 16.0f); + + // Position cape slightly down and behind the shoulders + glTranslatef2(0.0f, 1.0f, 2.0f); + + // Java-like cape physics (interpolated inertia + body motion) + float pt = a; + + double capeX = player->getCapePrevX() + (player->getCapeX() - player->getCapePrevX()) * pt; + double capeY = player->getCapePrevY() + (player->getCapeY() - player->getCapePrevY()) * pt; + double capeZ = player->getCapePrevZ() + (player->getCapeZ() - player->getCapePrevZ()) * pt; + + double px = player->xo + (player->x - player->xo) * pt; + double py = player->yo + (player->y - player->yo) * pt; + double pz = player->zo + (player->z - player->zo) * pt; + + double dx = capeX - px; + double dy = capeY - py; + double dz = capeZ - pz; + + float bodyYaw = player->yBodyRotO + (player->yBodyRot - player->yBodyRotO) * pt; + + float rad = bodyYaw * Mth::PI / 180.0f; + double sinYaw = Mth::sin(rad); + double cosYaw = -Mth::cos(rad); + + float forward = (float)(dx * sinYaw + dz * cosYaw) * 100.0f; + float sideways = (float)(dx * cosYaw - dz * sinYaw) * 100.0f; + if (forward < 0.0f) forward = 0.0f; + + float lift = (float)dy * 10.0f; + if (lift < -6.0f) lift = -6.0f; + if (lift > 32.0f) lift = 32.0f; + + float walk = + Mth::sin((player->walkAnimPos + player->walkAnimSpeed) * 6.0f) * + 32.0f * + player->walkAnimSpeed; + + float capeXRot = 6.0f + forward / 2.0f + lift + walk; + float capeZRot = sideways / 2.0f; + + // Smooth out jitter by lerping from the previous frame + const float smooth = 0.3f; + capeXRot = lastCapeXRot + (capeXRot - lastCapeXRot) * smooth; + capeZRot = lastCapeZRot + (capeZRot - lastCapeZRot) * smooth; + + lastCapeXRot = capeXRot; + lastCapeZRot = capeZRot; + + glRotatef2(capeXRot, 1.0f, 0.0f, 0.0f); + glRotatef2(capeZRot, 0.0f, 0.0f, 1.0f); + + Tesselator& t = Tesselator::instance; + t.begin(); + + // UV coordinates (64x32 skin layout) + const float u0 = 1.0f / 64.0f; + const float u1 = 11.0f / 64.0f; + const float u2 = 12.0f / 64.0f; + const float u3 = 22.0f / 64.0f; + + const float uL0 = 0.0f / 64.0f; + const float uL1 = 1.0f / 64.0f; + + const float uR0 = 11.0f / 64.0f; + const float uR1 = 12.0f / 64.0f; + + const float v0 = 0.0f / 32.0f; + const float v1 = 1.0f / 32.0f; + + const float vTop = 1.0f / 32.0f; + const float vBottom = 17.0f / 32.0f; + + // Cape size (10x16x1 pixels) + const float halfW = 5.0f; + const float height = 16.0f; + const float depth = 1.0f; + + // Front + t.tex(u2, vTop); t.vertex(-halfW, 0.0f, 0.0f); + t.tex(u3, vTop); t.vertex(halfW, 0.0f, 0.0f); + t.tex(u3, vBottom); t.vertex(halfW, height, 0.0f); + t.tex(u2, vBottom); t.vertex(-halfW, height, 0.0f); + + // Back + t.tex(u0, vTop); t.vertex(halfW, 0.0f, depth); + t.tex(u1, vTop); t.vertex(-halfW, 0.0f, depth); + t.tex(u1, vBottom); t.vertex(-halfW, height, depth); + t.tex(u0, vBottom); t.vertex(halfW, height, depth); + + // Left + t.tex(uL0, vTop); t.vertex(-halfW, 0.0f, depth); + t.tex(uL1, vTop); t.vertex(-halfW, 0.0f, 0.0f); + t.tex(uL1, vBottom); t.vertex(-halfW, height, 0.0f); + t.tex(uL0, vBottom); t.vertex(-halfW, height, depth); + + // Right + t.tex(uR0, vTop); t.vertex(halfW, 0.0f, 0.0f); + t.tex(uR1, vTop); t.vertex(halfW, 0.0f, depth); + t.tex(uR1, vBottom); t.vertex(halfW, height, depth); + t.tex(uR0, vBottom); t.vertex(halfW, height, 0.0f); + + // Top + t.tex(u0, v0); t.vertex(-halfW, 0.0f, depth); + t.tex(u1, v0); t.vertex(halfW, 0.0f, depth); + t.tex(u1, v1); t.vertex(halfW, 0.0f, 0.0f); + t.tex(u0, v1); t.vertex(-halfW, 0.0f, 0.0f); + + // Bottom + t.tex(u2, v0); t.vertex(halfW, height, 0.0f); + t.tex(u3, v0); t.vertex(-halfW, height, 0.0f); + t.tex(u3, v1); t.vertex(-halfW, height, depth); + t.tex(u2, v1); t.vertex(halfW, height, depth); + + t.draw(); + + glPopMatrix2(); + } + } +} +} + +void HumanoidMobRenderer::render( Entity* mob_, float x, float y, float z, float rot, float a ) { + Mob* mob = (Mob*)mob_; + ItemInstance* carriedItem = mob->getCarriedItem(); + if(carriedItem != NULL) + humanoidModel->holdingRightHand = true; + + humanoidModel->sneaking = mob->isSneaking(); + + super::render(mob_, x, y, z, rot, a); + humanoidModel->holdingRightHand = false; +} diff --git a/src/client/renderer/entity/HumanoidMobRenderer.h b/src/client/renderer/entity/HumanoidMobRenderer.h index a78ade8..b735d83 100755 --- a/src/client/renderer/entity/HumanoidMobRenderer.h +++ b/src/client/renderer/entity/HumanoidMobRenderer.h @@ -1,31 +1,31 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__HumanoidMobRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__HumanoidMobRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "MobRenderer.h" - -class HumanoidModel; -class Mob; - -class HumanoidMobRenderer: public MobRenderer -{ - typedef MobRenderer super; -public: - HumanoidMobRenderer(HumanoidModel* humanoidModel, float shadow); - - void renderHand(); - void render(Entity* mob_, float x, float y, float z, float rot, float a); -protected: - void additionalRendering(Mob* mob, float a); - - HumanoidModel* humanoidModel; - - // Last rotation values for cape smoothing (reduces jitter) - float lastCapeXRot; - float lastCapeZRot; -private: - // i guess ill keep this just in case seomthing breaks -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__HumanoidMobRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__HumanoidMobRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__HumanoidMobRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "MobRenderer.h" + +class HumanoidModel; +class Mob; + +class HumanoidMobRenderer: public MobRenderer +{ + typedef MobRenderer super; +public: + HumanoidMobRenderer(HumanoidModel* humanoidModel, float shadow); + + void renderHand(); + void render(Entity* mob_, float x, float y, float z, float rot, float a); +protected: + void additionalRendering(Mob* mob, float a); + + HumanoidModel* humanoidModel; + + // Last rotation values for cape smoothing (reduces jitter) + float lastCapeXRot; + float lastCapeZRot; +private: + // i guess ill keep this just in case seomthing breaks +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__HumanoidMobRenderer_H__*/ diff --git a/src/client/renderer/entity/ItemRenderer.cpp b/src/client/renderer/entity/ItemRenderer.cpp index 15e63d1..f3a6917 100755 --- a/src/client/renderer/entity/ItemRenderer.cpp +++ b/src/client/renderer/entity/ItemRenderer.cpp @@ -1,321 +1,321 @@ -#include "ItemRenderer.h" -#include "EntityRenderDispatcher.h" -#include "../Tesselator.h" -#include "../TileRenderer.h" -#include "../Textures.h" -#include "../../gui/Font.h" -#include "../../../world/entity/item/ItemEntity.h" -#include "../../../world/item/ItemInstance.h" -#include "../../../world/level/tile/Tile.h" -#include "../../../util/Mth.h" -#include "../../../util/Random.h" -#include "EntityRenderer.h" -#include "../ItemInHandRenderer.h" -#include "../../gui/Gui.h" -#include "../../../world/item/Item.h" - -/*static*/ -TileRenderer* ItemRenderer::tileRenderer = new TileRenderer(); - -ItemRenderer::ItemRenderer() -{ - shadowRadius = 0.15f; - shadowStrength = 0.75f; -} - -void ItemRenderer::teardown_static() { - if (tileRenderer) { - delete tileRenderer; - tileRenderer = NULL; - } -} - -void ItemRenderer::render(Entity* itemEntity_, float x, float y, float z, float rot, float a) { - ItemEntity* itemEntity = (ItemEntity*) itemEntity_; - random.setSeed(187); - ItemInstance* item = &itemEntity->item; - - glPushMatrix2(); - float bob = Mth::sin((itemEntity->age + a) / 10.0f + itemEntity->bobOffs) * 0.1f + 0.1f; - float spin = ((itemEntity->age + a) / 20.0f + itemEntity->bobOffs) * Mth::RADDEG; - - int count = 1; - if (item->count > 20) count = 4; - else if (item->count > 5) count = 3; - else if (item->count > 1) count = 2; - - glTranslatef2((float) x, (float) y + bob, (float) z); - //glEnable2(GL_RESCALE_NORMAL); - if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { - glRotatef2(spin, 0, 1, 0); - - float br = itemEntity->getBrightness(a); - if (item->id == Tile::sand->id || item->id == Tile::sandStone->id) br *= 0.8f; - glColor4f2(br, br, br, 1.0f); - - bindTexture("terrain.png"); - float s = 1 / 4.0f; - //if (!Tile::tiles[item->id]->isCubeShaped() && item->id != Tile::stoneSlabHalf->id) { - const int shape = Tile::tiles[item->id]->getRenderShape(); - if (shape == Tile::SHAPE_CROSS_TEXTURE || shape == Tile::SHAPE_TORCH) - s = 0.5f; - - glScalef2(s, s, s); - for (int i = 0; i < count; i++) { - if (i > 0) { - glPushMatrix2(); - float xo = (random.nextFloat() * 2 - 1) * 0.2f / s; - float yo = (random.nextFloat() * 2 - 1) * 0.2f / s; - float zo = (random.nextFloat() * 2 - 1) * 0.2f / s; - glTranslatef2(xo, yo, zo); - } - //static Stopwatch w; - //w.start(); - entityRenderDispatcher->itemInHandRenderer->renderItem(NULL, item); - //tileRenderer->renderTile(Tile::tiles[item->id], item->getAuxValue()); - //w.stop(); - //w.printEvery(100, "render-item"); - if (i > 0) glPopMatrix2(); - } - } else { - glScalef2(1 / 2.0f, 1 / 2.0f, 1 / 2.0f); - int icon = item->getIcon(); - if (item->id < 256) { - bindTexture("terrain.png"); - } else { - bindTexture("gui/items.png"); - } - Tesselator& t = Tesselator::instance; - - float u0 = ((icon % 16) * 16 + 0) / 256.0f; - float u1 = ((icon % 16) * 16 + 16) / 256.0f; - float v0 = ((icon / 16) * 16 + 0) / 256.0f; - float v1 = ((icon / 16) * 16 + 16) / 256.0f; - - float r = 1.0f; - float xo = 0.5f; - float yo = 0.25f; - - // glRotatef2(-playerRotX, 1, 0, 0); - for (int i = 0; i < count; i++) { - glPushMatrix2(); - if (i > 0) { - float _xo = (random.nextFloat() * 2 - 1) * 0.3f; - float _yo = (random.nextFloat() * 2 - 1) * 0.3f; - float _zo = (random.nextFloat() * 2 - 1) * 0.3f; - glTranslatef2(_xo, _yo, _zo); - } - glRotatef2(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); - t.begin(); - //t.normal(0, 1, 0); - t.vertexUV(0 - xo, 0 - yo, 0, u0, v1); - t.vertexUV(r - xo, 0 - yo, 0, u1, v1); - t.vertexUV(r - xo, 1 - yo, 0, u1, v0); - t.vertexUV(0 - xo, 1 - yo, 0, u0, v0); - //t.end(); - t.draw(); - - glPopMatrix2(); - } - } - //glDisable2(GL_RESCALE_NORMAL); - glPopMatrix2(); -} - - -// @note: _18 -> a,b,c,-1, a,b,c-1, ... -static const signed short _6[] = {139, 140, 141, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -static const signed short _17[] = {16, 17, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -static const signed short _18[] = {79, 80, 81, -1, 79, 80, 81, -1, 79, 80, 81, -1, 79, 80, 81, -1}; -static const signed short _24[] = {11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -static const signed short _35[] = {52, 59, 58, 57, 56, 55, 54, 53, 67, 66, 65, 64, 63, 62, 61, 60}; -static const signed short _44[] = {28, 32, 30, 29, 31, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -static const signed short _98[] = {1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -static const signed short _155[] = {34, 36, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -static const signed short _263[] = {230, 151, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -static const signed short _351[] = {-1, 152, 154, -1, 193, 215, 216, -1, -1, 217, 218, 219, 220, 221, 222, 144}; - -static const signed short _mapper[] = {-1, 7, 9, 8, 0, 5, -2, -1, -1, -1, -1, -1, 14, 15, 39, 38, 37, -2, -2, -1, 49, 41, 46, -1, -2, -1, -1, -1, -1, -1, 235, -1, -1, -1, -1, -2, -1, 134, 135, 136, 137, 43, 44, -1, -2, 6, 76, 71, 4, 47, 129, -1, -1, 22, 74, -1, 40, 45, 72, -1, -1, 75, -1, -1, -1, 128, -1, 21, -1, -1, -1, -1, -1, 42, -1, -1, -1, -1, -1, -1, 48, 77, 10, 236, -1, 69, -1, 20, -1, 50, -1, -1, -1, -1, -1, -1, 68, -1, -2, -1, -1, -1, 130, 78, -1, -1, -1, 70, 23, 25, -1, -1, 19, -1, 26, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, 27, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 73, -1, 51, -1, -1, -1, -1, -1, 82, -1, -1, 174, 173, 175, 231, 234, 147, 190, -2, 153, 150, 149, 146, 185, 166, 164, 167, 186, 170, 169, 171, 187, 177, 176, 178, 165, 195, 194, 188, 181, 180, 182, 189, 191, 228, 168, 172, 145, 179, 183, 142, 233, 232, 198, 200, 201, 202, -1, -1, -1, -1, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 192, 156, 157, 133, -1, 148, 131, -1, -1, -1, -1, -1, -1, -1, 226, -1, 199, -1, 159, 158, 138, 224, 225, -1, -1, -1, -1, -1, -1, -1, 227, -1, -1, -2, 223, 229, -1, 132, -1, -1, -1, 184, 196, -1, 143, 160, 161, 162, 163, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 155, 197}; - -#define IRMAPCASE(x) case x: return _##x [item->getAuxValue() & 15] - -int ItemRenderer::getAtlasPos(const ItemInstance* item) { - int id = item->id; - if (id < 0 || id >= sizeof(_mapper) / sizeof(const signed short)) - return -1; - - int texId = _mapper[id]; - if (texId != -2) - return texId; - - switch(id) { - IRMAPCASE(6); - IRMAPCASE(17); - IRMAPCASE(18); - IRMAPCASE(24); - IRMAPCASE(35); - IRMAPCASE(44); - IRMAPCASE(98); - IRMAPCASE(155); - IRMAPCASE(263); - IRMAPCASE(351); - default: - break; - } - return -1; -} - -/*static*/ -void ItemRenderer::renderGuiItem(Font* font, Textures* textures, const ItemInstance* item, float x, float y, bool fancy) { - renderGuiItem(font, textures, item, x, y, 16, 16, fancy); -} -void ItemRenderer::renderGuiItem(Font* font, Textures* textures, const ItemInstance* item, float x, float y, float w, float h, bool fancy) { - if (item == NULL) { - //LOGW("item is NULL @ ItemRenderer::renderGuiItem\n"); - return; - } - const int id = item->id; - if (!Item::items[id]) - return; - - int i = getAtlasPos(item); - - if (i < 0) { - Tesselator& t = Tesselator::instance; - if (!t.isOverridden()) - renderGuiItemCorrect(font, textures, item, int(x), int(y)); - else { - // @huge @attn @todo @fix: This is just guess-works.. - // it we're batching for saving the - // buffer, this will fail miserably - t.endOverrideAndDraw(); - glDisable2(GL_TEXTURE_2D); - fillRect(t, x, y, w, h, 0xff0000); - glEnable2(GL_TEXTURE_2D); - renderGuiItemCorrect(font, textures, item, int(x), int(y)); - t.beginOverride(); - } - return; - } - - textures->loadAndBindTexture("gui/gui_blocks.png"); - float u0, u1, v0, v1; - if (i < 128) { - const float P = 48.0f / 512.0f; - u0 = (float)(i%10) * P; - v0 = (float)(i/10) * P; - u1 = u0 + P; - v1 = v0 + P; - } else { - i -= 128; - const float P = 16.0f / 512.0f; - u0 = float(i & 31) * P; - v0 = 27 * P + float(i >> 5) * P; // 27 "icon" rows down - u1 = u0 + P; - v1 = v0 + P; - } - - const float blitOffset = 0; - Tesselator& t = Tesselator::instance; - t.begin(); - t.colorABGR( item->count>0? 0xffffffff : 0x60ffffff); - t.vertexUV(x, y + h, blitOffset, u0, v1); - t.vertexUV(x + w, y + h, blitOffset, u1, v1); - t.vertexUV(x + w, y, blitOffset, u1, v0); - t.vertexUV(x, y, blitOffset, u0, v0); - t.draw(); -} - -void ItemRenderer::renderGuiItemDecorations(const ItemInstance* item, float x, float y) { - if (!item) return; - if (item->count > 0 && item->isDamaged()) { - float p = std::floor(13.5f - (float) item->getDamageValue() * 13.0f / (float) item->getMaxDamage()); - int cc = (int) std::floor(255.5f - (float) item->getDamageValue() * 255.0f / (float) item->getMaxDamage()); - //glDisable(GL_LIGHTING); - //glDisable(GL_DEPTH_TEST); - //glDisable(GL_TEXTURE_2D); - - Tesselator& t = Tesselator::instance; - - int ca = (255 - cc) << 16 | (cc) << 8; - int cb = ((255 - cc) / 4) << 16 | (255 / 4) << 8; - fillRect(t, x + 2, y + 13, 13, 1, 0x000000); - fillRect(t, x + 2, y + 13, 12, 1, cb); - fillRect(t, x + 2, y + 13, p, 1, ca); - - //glEnable(GL_TEXTURE_2D); - //glEnable(GL_LIGHTING); - //glEnable(GL_DEPTH_TEST); - glColor4f2(1, 1, 1, 1); - } -} - -void ItemRenderer::fillRect(Tesselator& t, float x, float y, float w, float h, int c) { - t.begin(); - t.color(c); - t.vertex(x + 0, y + 0, 0); - t.vertex(x + 0, y + h, 0); - t.vertex(x + w, y + h, 0); - t.vertex(x + w, y + 0, 0); - t.draw(); -} - - -void ItemRenderer::renderGuiItemCorrect(Font* font, Textures* textures, const ItemInstance* item, int x, int y) { - if (item == NULL) - return; - - //glDisable(GL_CULL_FACE); - if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) - { - int paint = item->id; - textures->loadAndBindTexture("terrain.png"); - - static float ff = 0;// ff += 0.005f; - static float gg = 0;// gg += 0.01f; - - Tile* tile = Tile::tiles[paint]; - glPushMatrix2(); - glTranslatef2((GLfloat)(x - 2), (GLfloat)(y + 3), -8); - glScalef2(10.0f, 10.0f, 10.0f); - glTranslatef2(1.0f, 0.5f, 0.0f); - glRotatef2(ff + 180.0f + 30.0f, 1, 0, 0); - glRotatef2(gg + 45.0f, 0, 1, 0); - - //glColor4f2(1, 1, 1, 1); - glScalef2(1, 1, 1); - tileRenderer->renderGuiTile(tile, item->getAuxValue()); - glPopMatrix2(); - } - else if (item->getIcon() >= 0) - { - //if (item->id == Item::camera->id) { - // printf("item->id: %d, %d\n", item->id, item->getIcon()); - //} - if (item->id < 256) { - textures->loadAndBindTexture("terrain.png"); - } else { - textures->loadAndBindTexture("gui/items.png"); - } - //Tesselator& t = Tesselator::instance; - //t.scale2d(Gui::InvGuiScale, Gui::InvGuiScale); - blit((float)x, (float)y, (float)(item->getIcon() % 16 * 16), (float)(item->getIcon() / 16 * 16), 16, 16); - //t.resetScale(); - } - //glEnable(GL_CULL_FACE); -} - -/*static*/ -void ItemRenderer::blit(float x, float y, float sx, float sy, float w, float h) { - float blitOffset = 0; - const float us = 1 / 256.0f; - const float vs = 1 / 256.0f; - Tesselator& t = Tesselator::instance; - t.begin(); - t.vertexUV(x, y + h, blitOffset, sx * us, (sy + h) * vs); - t.vertexUV(x + w, y + h, blitOffset, (sx + w) * us, (sy + h) * vs); - t.vertexUV(x + w, y, blitOffset, (sx + w) * us, sy * vs); - t.vertexUV(x, y, blitOffset, sx * us, sy * vs); - //t.end(); - t.draw(); -} +#include "ItemRenderer.h" +#include "EntityRenderDispatcher.h" +#include "../Tesselator.h" +#include "../TileRenderer.h" +#include "../Textures.h" +#include "../../gui/Font.h" +#include "../../../world/entity/item/ItemEntity.h" +#include "../../../world/item/ItemInstance.h" +#include "../../../world/level/tile/Tile.h" +#include "../../../util/Mth.h" +#include "../../../util/Random.h" +#include "EntityRenderer.h" +#include "../ItemInHandRenderer.h" +#include "../../gui/Gui.h" +#include "../../../world/item/Item.h" + +/*static*/ +TileRenderer* ItemRenderer::tileRenderer = new TileRenderer(); + +ItemRenderer::ItemRenderer() +{ + shadowRadius = 0.15f; + shadowStrength = 0.75f; +} + +void ItemRenderer::teardown_static() { + if (tileRenderer) { + delete tileRenderer; + tileRenderer = NULL; + } +} + +void ItemRenderer::render(Entity* itemEntity_, float x, float y, float z, float rot, float a) { + ItemEntity* itemEntity = (ItemEntity*) itemEntity_; + random.setSeed(187); + ItemInstance* item = &itemEntity->item; + + glPushMatrix2(); + float bob = Mth::sin((itemEntity->age + a) / 10.0f + itemEntity->bobOffs) * 0.1f + 0.1f; + float spin = ((itemEntity->age + a) / 20.0f + itemEntity->bobOffs) * Mth::RADDEG; + + int count = 1; + if (item->count > 20) count = 4; + else if (item->count > 5) count = 3; + else if (item->count > 1) count = 2; + + glTranslatef2((float) x, (float) y + bob, (float) z); + //glEnable2(GL_RESCALE_NORMAL); + if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) { + glRotatef2(spin, 0, 1, 0); + + float br = itemEntity->getBrightness(a); + if (item->id == Tile::sand->id || item->id == Tile::sandStone->id) br *= 0.8f; + glColor4f2(br, br, br, 1.0f); + + bindTexture("terrain.png"); + float s = 1 / 4.0f; + //if (!Tile::tiles[item->id]->isCubeShaped() && item->id != Tile::stoneSlabHalf->id) { + const int shape = Tile::tiles[item->id]->getRenderShape(); + if (shape == Tile::SHAPE_CROSS_TEXTURE || shape == Tile::SHAPE_TORCH) + s = 0.5f; + + glScalef2(s, s, s); + for (int i = 0; i < count; i++) { + if (i > 0) { + glPushMatrix2(); + float xo = (random.nextFloat() * 2 - 1) * 0.2f / s; + float yo = (random.nextFloat() * 2 - 1) * 0.2f / s; + float zo = (random.nextFloat() * 2 - 1) * 0.2f / s; + glTranslatef2(xo, yo, zo); + } + //static Stopwatch w; + //w.start(); + entityRenderDispatcher->itemInHandRenderer->renderItem(NULL, item); + //tileRenderer->renderTile(Tile::tiles[item->id], item->getAuxValue()); + //w.stop(); + //w.printEvery(100, "render-item"); + if (i > 0) glPopMatrix2(); + } + } else { + glScalef2(1 / 2.0f, 1 / 2.0f, 1 / 2.0f); + int icon = item->getIcon(); + if (item->id < 256) { + bindTexture("terrain.png"); + } else { + bindTexture("gui/items.png"); + } + Tesselator& t = Tesselator::instance; + + float u0 = ((icon % 16) * 16 + 0) / 256.0f; + float u1 = ((icon % 16) * 16 + 16) / 256.0f; + float v0 = ((icon / 16) * 16 + 0) / 256.0f; + float v1 = ((icon / 16) * 16 + 16) / 256.0f; + + float r = 1.0f; + float xo = 0.5f; + float yo = 0.25f; + + // glRotatef2(-playerRotX, 1, 0, 0); + for (int i = 0; i < count; i++) { + glPushMatrix2(); + if (i > 0) { + float _xo = (random.nextFloat() * 2 - 1) * 0.3f; + float _yo = (random.nextFloat() * 2 - 1) * 0.3f; + float _zo = (random.nextFloat() * 2 - 1) * 0.3f; + glTranslatef2(_xo, _yo, _zo); + } + glRotatef2(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); + t.begin(); + //t.normal(0, 1, 0); + t.vertexUV(0 - xo, 0 - yo, 0, u0, v1); + t.vertexUV(r - xo, 0 - yo, 0, u1, v1); + t.vertexUV(r - xo, 1 - yo, 0, u1, v0); + t.vertexUV(0 - xo, 1 - yo, 0, u0, v0); + //t.end(); + t.draw(); + + glPopMatrix2(); + } + } + //glDisable2(GL_RESCALE_NORMAL); + glPopMatrix2(); +} + + +// @note: _18 -> a,b,c,-1, a,b,c-1, ... +static const signed short _6[] = {139, 140, 141, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +static const signed short _17[] = {16, 17, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +static const signed short _18[] = {79, 80, 81, -1, 79, 80, 81, -1, 79, 80, 81, -1, 79, 80, 81, -1}; +static const signed short _24[] = {11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +static const signed short _35[] = {52, 59, 58, 57, 56, 55, 54, 53, 67, 66, 65, 64, 63, 62, 61, 60}; +static const signed short _44[] = {28, 32, 30, 29, 31, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +static const signed short _98[] = {1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +static const signed short _155[] = {34, 36, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +static const signed short _263[] = {230, 151, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +static const signed short _351[] = {-1, 152, 154, -1, 193, 215, 216, -1, -1, 217, 218, 219, 220, 221, 222, 144}; + +static const signed short _mapper[] = {-1, 7, 9, 8, 0, 5, -2, -1, -1, -1, -1, -1, 14, 15, 39, 38, 37, -2, -2, -1, 49, 41, 46, -1, -2, -1, -1, -1, -1, -1, 235, -1, -1, -1, -1, -2, -1, 134, 135, 136, 137, 43, 44, -1, -2, 6, 76, 71, 4, 47, 129, -1, -1, 22, 74, -1, 40, 45, 72, -1, -1, 75, -1, -1, -1, 128, -1, 21, -1, -1, -1, -1, -1, 42, -1, -1, -1, -1, -1, -1, 48, 77, 10, 236, -1, 69, -1, 20, -1, 50, -1, -1, -1, -1, -1, -1, 68, -1, -2, -1, -1, -1, 130, 78, -1, -1, -1, 70, 23, 25, -1, -1, 19, -1, 26, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, 27, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 73, -1, 51, -1, -1, -1, -1, -1, 82, -1, -1, 174, 173, 175, 231, 234, 147, 190, -2, 153, 150, 149, 146, 185, 166, 164, 167, 186, 170, 169, 171, 187, 177, 176, 178, 165, 195, 194, 188, 181, 180, 182, 189, 191, 228, 168, 172, 145, 179, 183, 142, 233, 232, 198, 200, 201, 202, -1, -1, -1, -1, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 192, 156, 157, 133, -1, 148, 131, -1, -1, -1, -1, -1, -1, -1, 226, -1, 199, -1, 159, 158, 138, 224, 225, -1, -1, -1, -1, -1, -1, -1, 227, -1, -1, -2, 223, 229, -1, 132, -1, -1, -1, 184, 196, -1, 143, 160, 161, 162, 163, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 155, 197}; + +#define IRMAPCASE(x) case x: return _##x [item->getAuxValue() & 15] + +int ItemRenderer::getAtlasPos(const ItemInstance* item) { + int id = item->id; + if (id < 0 || id >= sizeof(_mapper) / sizeof(const signed short)) + return -1; + + int texId = _mapper[id]; + if (texId != -2) + return texId; + + switch(id) { + IRMAPCASE(6); + IRMAPCASE(17); + IRMAPCASE(18); + IRMAPCASE(24); + IRMAPCASE(35); + IRMAPCASE(44); + IRMAPCASE(98); + IRMAPCASE(155); + IRMAPCASE(263); + IRMAPCASE(351); + default: + break; + } + return -1; +} + +/*static*/ +void ItemRenderer::renderGuiItem(Font* font, Textures* textures, const ItemInstance* item, float x, float y, bool fancy) { + renderGuiItem(font, textures, item, x, y, 16, 16, fancy); +} +void ItemRenderer::renderGuiItem(Font* font, Textures* textures, const ItemInstance* item, float x, float y, float w, float h, bool fancy) { + if (item == NULL) { + //LOGW("item is NULL @ ItemRenderer::renderGuiItem\n"); + return; + } + const int id = item->id; + if (!Item::items[id]) + return; + + int i = getAtlasPos(item); + + if (i < 0) { + Tesselator& t = Tesselator::instance; + if (!t.isOverridden()) + renderGuiItemCorrect(font, textures, item, int(x), int(y)); + else { + // @huge @attn @todo @fix: This is just guess-works.. + // it we're batching for saving the + // buffer, this will fail miserably + t.endOverrideAndDraw(); + glDisable2(GL_TEXTURE_2D); + fillRect(t, x, y, w, h, 0xff0000); + glEnable2(GL_TEXTURE_2D); + renderGuiItemCorrect(font, textures, item, int(x), int(y)); + t.beginOverride(); + } + return; + } + + textures->loadAndBindTexture("gui/gui_blocks.png"); + float u0, u1, v0, v1; + if (i < 128) { + const float P = 48.0f / 512.0f; + u0 = (float)(i%10) * P; + v0 = (float)(i/10) * P; + u1 = u0 + P; + v1 = v0 + P; + } else { + i -= 128; + const float P = 16.0f / 512.0f; + u0 = float(i & 31) * P; + v0 = 27 * P + float(i >> 5) * P; // 27 "icon" rows down + u1 = u0 + P; + v1 = v0 + P; + } + + const float blitOffset = 0; + Tesselator& t = Tesselator::instance; + t.begin(); + t.colorABGR( item->count>0? 0xffffffff : 0x60ffffff); + t.vertexUV(x, y + h, blitOffset, u0, v1); + t.vertexUV(x + w, y + h, blitOffset, u1, v1); + t.vertexUV(x + w, y, blitOffset, u1, v0); + t.vertexUV(x, y, blitOffset, u0, v0); + t.draw(); +} + +void ItemRenderer::renderGuiItemDecorations(const ItemInstance* item, float x, float y) { + if (!item) return; + if (item->count > 0 && item->isDamaged()) { + float p = std::floor(13.5f - (float) item->getDamageValue() * 13.0f / (float) item->getMaxDamage()); + int cc = (int) std::floor(255.5f - (float) item->getDamageValue() * 255.0f / (float) item->getMaxDamage()); + //glDisable(GL_LIGHTING); + //glDisable(GL_DEPTH_TEST); + //glDisable(GL_TEXTURE_2D); + + Tesselator& t = Tesselator::instance; + + int ca = (255 - cc) << 16 | (cc) << 8; + int cb = ((255 - cc) / 4) << 16 | (255 / 4) << 8; + fillRect(t, x + 2, y + 13, 13, 1, 0x000000); + fillRect(t, x + 2, y + 13, 12, 1, cb); + fillRect(t, x + 2, y + 13, p, 1, ca); + + //glEnable(GL_TEXTURE_2D); + //glEnable(GL_LIGHTING); + //glEnable(GL_DEPTH_TEST); + glColor4f2(1, 1, 1, 1); + } +} + +void ItemRenderer::fillRect(Tesselator& t, float x, float y, float w, float h, int c) { + t.begin(); + t.color(c); + t.vertex(x + 0, y + 0, 0); + t.vertex(x + 0, y + h, 0); + t.vertex(x + w, y + h, 0); + t.vertex(x + w, y + 0, 0); + t.draw(); +} + + +void ItemRenderer::renderGuiItemCorrect(Font* font, Textures* textures, const ItemInstance* item, int x, int y) { + if (item == NULL) + return; + + //glDisable(GL_CULL_FACE); + if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) + { + int paint = item->id; + textures->loadAndBindTexture("terrain.png"); + + static float ff = 0;// ff += 0.005f; + static float gg = 0;// gg += 0.01f; + + Tile* tile = Tile::tiles[paint]; + glPushMatrix2(); + glTranslatef2((GLfloat)(x - 2), (GLfloat)(y + 3), -8); + glScalef2(10.0f, 10.0f, 10.0f); + glTranslatef2(1.0f, 0.5f, 0.0f); + glRotatef2(ff + 180.0f + 30.0f, 1, 0, 0); + glRotatef2(gg + 45.0f, 0, 1, 0); + + //glColor4f2(1, 1, 1, 1); + glScalef2(1, 1, 1); + tileRenderer->renderGuiTile(tile, item->getAuxValue()); + glPopMatrix2(); + } + else if (item->getIcon() >= 0) + { + //if (item->id == Item::camera->id) { + // printf("item->id: %d, %d\n", item->id, item->getIcon()); + //} + if (item->id < 256) { + textures->loadAndBindTexture("terrain.png"); + } else { + textures->loadAndBindTexture("gui/items.png"); + } + //Tesselator& t = Tesselator::instance; + //t.scale2d(Gui::InvGuiScale, Gui::InvGuiScale); + blit((float)x, (float)y, (float)(item->getIcon() % 16 * 16), (float)(item->getIcon() / 16 * 16), 16, 16); + //t.resetScale(); + } + //glEnable(GL_CULL_FACE); +} + +/*static*/ +void ItemRenderer::blit(float x, float y, float sx, float sy, float w, float h) { + float blitOffset = 0; + const float us = 1 / 256.0f; + const float vs = 1 / 256.0f; + Tesselator& t = Tesselator::instance; + t.begin(); + t.vertexUV(x, y + h, blitOffset, sx * us, (sy + h) * vs); + t.vertexUV(x + w, y + h, blitOffset, (sx + w) * us, (sy + h) * vs); + t.vertexUV(x + w, y, blitOffset, (sx + w) * us, sy * vs); + t.vertexUV(x, y, blitOffset, sx * us, sy * vs); + //t.end(); + t.draw(); +} diff --git a/src/client/renderer/entity/ItemRenderer.h b/src/client/renderer/entity/ItemRenderer.h index 9fb88f6..54dd51b 100755 --- a/src/client/renderer/entity/ItemRenderer.h +++ b/src/client/renderer/entity/ItemRenderer.h @@ -1,38 +1,38 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "EntityRenderer.h" -#include "../../../util/Random.h" - -class Font; -class Entity; -class ItemInstance; -class Textures; -class Tesselator; -class TileRenderer; - -class ItemRenderer: public EntityRenderer -{ -public: - ItemRenderer(); - - void render(Entity* itemEntity_, float x, float y, float z, float rot, float a); - static void renderGuiItem(Font* font, Textures* textures, const ItemInstance* item, float x, float y, bool fancy); - static void renderGuiItem(Font* font, Textures* textures, const ItemInstance* item, float x, float y, float w, float h, bool fancy); - static void renderGuiItemCorrect(Font* font, Textures* textures, const ItemInstance* item, int x, int y); - //void renderGuiItemDecorations(Font* font, Textures* textures, ItemInstance* item, int x, int y); - static void renderGuiItemDecorations(const ItemInstance* item, float x, float y); - - static void blit(float x, float y, float sx, float sy, float w, float h); - static int getAtlasPos(const ItemInstance* item); - - static void teardown_static(); -private: - static void fillRect(Tesselator& t, float x, float y, float w, float h, int c); - static TileRenderer* tileRenderer; - Random random; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "EntityRenderer.h" +#include "../../../util/Random.h" + +class Font; +class Entity; +class ItemInstance; +class Textures; +class Tesselator; +class TileRenderer; + +class ItemRenderer: public EntityRenderer +{ +public: + ItemRenderer(); + + void render(Entity* itemEntity_, float x, float y, float z, float rot, float a); + static void renderGuiItem(Font* font, Textures* textures, const ItemInstance* item, float x, float y, bool fancy); + static void renderGuiItem(Font* font, Textures* textures, const ItemInstance* item, float x, float y, float w, float h, bool fancy); + static void renderGuiItemCorrect(Font* font, Textures* textures, const ItemInstance* item, int x, int y); + //void renderGuiItemDecorations(Font* font, Textures* textures, ItemInstance* item, int x, int y); + static void renderGuiItemDecorations(const ItemInstance* item, float x, float y); + + static void blit(float x, float y, float sx, float sy, float w, float h); + static int getAtlasPos(const ItemInstance* item); + + static void teardown_static(); +private: + static void fillRect(Tesselator& t, float x, float y, float w, float h, int c); + static TileRenderer* tileRenderer; + Random random; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemRenderer_H__*/ diff --git a/src/client/renderer/entity/ItemSpriteRenderer.cpp b/src/client/renderer/entity/ItemSpriteRenderer.cpp index 7557c4d..037f316 100755 --- a/src/client/renderer/entity/ItemSpriteRenderer.cpp +++ b/src/client/renderer/entity/ItemSpriteRenderer.cpp @@ -1,41 +1,41 @@ -#include "ItemSpriteRenderer.h" -#include "EntityRenderDispatcher.h" -#include "../Tesselator.h" -#include "../../../world/entity/Entity.h" - - -ItemSpriteRenderer::ItemSpriteRenderer( int icon ) -: icon(icon) -{ -} - -void ItemSpriteRenderer::render( Entity* e, float x, float y, float z, float rot, float a ) -{ - glPushMatrix2(); - - glTranslatef2((float) x, (float) y, (float) z); - glScalef2(1 / 2.0f, 1 / 2.0f, 1 / 2.0f); - bindTexture("gui/items.png"); - Tesselator& t = Tesselator::instance; - - float u0 = ((icon % 16) * 16 + 0) / 256.0f; - float u1 = ((icon % 16) * 16 + 16) / 256.0f; - float v0 = ((icon / 16) * 16 + 0) / 256.0f; - float v1 = ((icon / 16) * 16 + 16) / 256.0f; - - - float r = 1.0f; - float xo = 0.5f; - float yo = 0.25f; - - glRotatef2(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); - glRotatef2(-entityRenderDispatcher->playerRotX, 1, 0, 0); - t.begin(); - t.vertexUV(0 - xo, 0 - yo, 0, u0, v1); - t.vertexUV(r - xo, 0 - yo, 0, u1, v1); - t.vertexUV(r - xo, 1 - yo, 0, u1, v0); - t.vertexUV(0 - xo, 1 - yo, 0, u0, v0); - t.draw(); - - glPopMatrix2(); -} +#include "ItemSpriteRenderer.h" +#include "EntityRenderDispatcher.h" +#include "../Tesselator.h" +#include "../../../world/entity/Entity.h" + + +ItemSpriteRenderer::ItemSpriteRenderer( int icon ) +: icon(icon) +{ +} + +void ItemSpriteRenderer::render( Entity* e, float x, float y, float z, float rot, float a ) +{ + glPushMatrix2(); + + glTranslatef2((float) x, (float) y, (float) z); + glScalef2(1 / 2.0f, 1 / 2.0f, 1 / 2.0f); + bindTexture("gui/items.png"); + Tesselator& t = Tesselator::instance; + + float u0 = ((icon % 16) * 16 + 0) / 256.0f; + float u1 = ((icon % 16) * 16 + 16) / 256.0f; + float v0 = ((icon / 16) * 16 + 0) / 256.0f; + float v1 = ((icon / 16) * 16 + 16) / 256.0f; + + + float r = 1.0f; + float xo = 0.5f; + float yo = 0.25f; + + glRotatef2(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); + glRotatef2(-entityRenderDispatcher->playerRotX, 1, 0, 0); + t.begin(); + t.vertexUV(0 - xo, 0 - yo, 0, u0, v1); + t.vertexUV(r - xo, 0 - yo, 0, u1, v1); + t.vertexUV(r - xo, 1 - yo, 0, u1, v0); + t.vertexUV(0 - xo, 1 - yo, 0, u0, v0); + t.draw(); + + glPopMatrix2(); +} diff --git a/src/client/renderer/entity/ItemSpriteRenderer.h b/src/client/renderer/entity/ItemSpriteRenderer.h index 5987bc9..e70455f 100755 --- a/src/client/renderer/entity/ItemSpriteRenderer.h +++ b/src/client/renderer/entity/ItemSpriteRenderer.h @@ -1,18 +1,18 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemSpriteRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemSpriteRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "EntityRenderer.h" - -class ItemSpriteRenderer: public EntityRenderer -{ -public: - ItemSpriteRenderer(int icon); - - void render(Entity* e, float x, float y, float z, float rot, float a); -private: - int icon; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemSpriteRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemSpriteRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemSpriteRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "EntityRenderer.h" + +class ItemSpriteRenderer: public EntityRenderer +{ +public: + ItemSpriteRenderer(int icon); + + void render(Entity* e, float x, float y, float z, float rot, float a); +private: + int icon; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__ItemSpriteRenderer_H__*/ diff --git a/src/client/renderer/entity/MobRenderer.cpp b/src/client/renderer/entity/MobRenderer.cpp index 51ccee9..4e0e080 100755 --- a/src/client/renderer/entity/MobRenderer.cpp +++ b/src/client/renderer/entity/MobRenderer.cpp @@ -1,241 +1,241 @@ -#include "MobRenderer.h" -#include "EntityRenderDispatcher.h" -#include "../../gui/Font.h" -#include "../Tesselator.h" -#include "../../Minecraft.h" -#include "../../model/Model.h" -#include "../../../world/entity/Mob.h" -#include "../../../util/Mth.h" - -MobRenderer::MobRenderer(Model* model, float shadow) -: model(model), - armor(NULL) -{ - shadowRadius = shadow; -} - -MobRenderer::~MobRenderer() { - delete model; -} - -void MobRenderer::setArmor(Model* armor) { - this->armor = armor; -} - -Model* MobRenderer::getArmor() { - return armor; -} - -void MobRenderer::render(Entity* e, float x, float y, float z, float rot, float a) -{ - Mob* mob = (Mob*) e; - - glPushMatrix2(); - glDisable2(GL_CULL_FACE); - - model->attackTime = getAttackAnim(mob, a); - model->riding = false;//mob.isRiding(); - model->young = mob->isBaby(); - - if (armor) { - armor->riding = model->riding; - armor->young = model->young; - armor->attackTime = model->attackTime; - } - - float bodyRot = (mob->yBodyRotO + (mob->yBodyRot - mob->yBodyRotO) * a); - float headRot = (mob->yRotO + (mob->yRot - mob->yRotO) * a); - float headRotx = (mob->xRotO + (mob->xRot - mob->xRotO) * a); - - float yoffset = mob->heightOffset; - setupPosition(mob, x, y - yoffset, z); - - float bob = getBob(mob, a); - setupRotations(mob, bob, bodyRot, a); - - float ascale = 1 / 16.0f; - glScalef2(-1, -1, 1); - - scale(mob, a); - glTranslatef2(0, -24 * ascale - 0.125f / 16.0f, 0); - - float ws = mob->walkAnimSpeedO + (mob->walkAnimSpeed - mob->walkAnimSpeedO) * a; - float wp = mob->walkAnimPos - mob->walkAnimSpeed * (1 - a); - if (mob->isBaby()) { - wp *= 3.0f; - } - - if (ws > 1) ws = 1; - - bindTexture(mob->getTexture()); - //glEnable2(GL_ALPHA_TEST); - - model->prepareMobModel(mob, wp, ws, a); - model->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); - - for (int i = 0; i < MAX_ARMOR_LAYERS; i++) { - if (prepareArmor(mob, i, a) < 0) continue; - - armor->prepareMobModel(mob, wp, ws, a); - armor->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); - glDisable2(GL_BLEND); - glEnable2(GL_ALPHA_TEST); - } - - additionalRendering(mob, a); - float br = mob->getBrightness(a); - int overlayColor = getOverlayColor(mob, br, a); - - bool renderOverlay = ((overlayColor >> 24) & 0xff) > 0; - bool renderHurt = (mob->hurtTime > 0) || (mob->deathTime > 0); - - if (renderOverlay || renderHurt) { - glDisable2(GL_TEXTURE_2D); - glDisable2(GL_ALPHA_TEST); - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthFunc(GL_EQUAL); - - if (renderHurt) { - glEnable(GL_COLOR_MATERIAL); - glColor4f2(br, 0, 0, 0.4f); - model->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); - for (int i = 0; i < MAX_ARMOR_LAYERS; i++) { - if (prepareArmor(mob, i, a) < 0) continue; - - glColor4f2(br, 0, 0, 0.4f); - armor->prepareMobModel(mob, wp, ws, a); - armor->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); - } - } - - if (renderOverlay) { - float r = ((overlayColor >> 16) & 0xff) / 255.0f; - float g = ((overlayColor >> 8) & 0xff) / 255.0f; - float b = ((overlayColor) & 0xff) / 255.0f; - float aa = ((overlayColor >> 24) & 0xff) / 255.0f; - glColor4f2(r, g, b, aa); - model->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); - for (int i = 0; i < MAX_ARMOR_LAYERS; i++) { - if (prepareArmor(mob, i, a) < 0) continue; - - glColor4f2(r, g, b, aa); - armor->prepareMobModel(mob, wp, ws, a); - armor->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); - } - } - - glDepthFunc(GL_LEQUAL); - glDisable2(GL_BLEND); - glEnable2(GL_ALPHA_TEST); - glEnable2(GL_TEXTURE_2D); - } - - glEnable2(GL_CULL_FACE); - //glEnable2(GL_DEPTH_TEST); - - glPopMatrix2(); - - renderName(mob, x, y, z); -} - -void MobRenderer::setupPosition(Entity* mob, float x, float y, float z) { - glTranslatef2((float) x, (float) y, (float) z); -} - -void MobRenderer::setupRotations(Entity* mob_, float bob, float bodyRot, float a) { - glRotatef2(180 - bodyRot, 0, 1, 0); - - Mob* mob = (Mob*)mob_; - if (mob->deathTime > 0) { - float fall = (mob->deathTime + a - 1) / 20.0f * 1.6f; - fall = Mth::sqrt(fall); - if (fall > 1) fall = 1; - glRotatef2(fall * getFlipDegrees(mob), 0, 0, 1); - } -} - -float MobRenderer::getAttackAnim(Mob* mob, float a) { - return mob->getAttackAnim(a); -} - -float MobRenderer::getBob(Mob* mob, float a) { - return (mob->tickCount + a); -} - -void MobRenderer::additionalRendering(Mob* mob, float a) { -} - -void MobRenderer::onGraphicsReset() { - if (model) model->onGraphicsReset(); - if (armor) armor->onGraphicsReset(); -} - -int MobRenderer::prepareArmor(Mob* mob, int layer, float a) { - return -1; -} - -float MobRenderer::getFlipDegrees(Mob* mob) { - return 90; -} - -int MobRenderer::getOverlayColor(Mob* mob, float br, float a) { - return 0; -} - -void MobRenderer::scale(Mob* mob, float a) { -} - -void MobRenderer::renderName(Mob* mob, float x, float y, float z) { - /* - std::stringstream ss; ss << mob->entityId; - renderNameTag(mob, ss.str(), x, y, z, 64); - */ -} - -void MobRenderer::renderNameTag(Mob* mob, const std::string& name, float x, float y, float z, int maxDist) { - float dist = mob->distanceToSqr(entityRenderDispatcher->cameraEntity); - - if (dist > maxDist*maxDist) - return; - - Font* font = getFont(); - - float size = 1.60f; - float s = 1 / 60.0f * size; - - glPushMatrix2(); - glTranslatef2((float) x + 0, (float) y + 1.0f /*2.3f*/, (float) z); - - glRotatef2(-entityRenderDispatcher->playerRotY, 0, 1, 0); - glRotatef2(entityRenderDispatcher->playerRotX, 1, 0, 0); - - glScalef2(-s, -s, s); - - glDepthMask(false); - glDisable2(GL_DEPTH_TEST); - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Tesselator& t = Tesselator::instance; - - glDisable2(GL_TEXTURE_2D); - t.begin(); - int w = font->width(name) / 2; - t.color(0.0f, 0.0f, 0.0f, 0.25f); - t.vertex(-(float)w - 1, -1, 0); - t.vertex(-(float)w - 1, +8, 0); - t.vertex(+(float)w + 1, +8, 0); - t.vertex(+(float)w + 1, -1, 0); - //t.end(); - t.draw(); - glEnable2(GL_TEXTURE_2D); - const float fnameWidth = (float)font->width(name) / -2; - font->draw(name, fnameWidth, 0, 0x20ffffff); - glEnable2(GL_DEPTH_TEST); - - glDepthMask(true); - font->draw(name, (float) fnameWidth, 0, 0xffffffff); - glDisable2(GL_BLEND); - glColor4f2(1, 1, 1, 1); - glPopMatrix2(); -} +#include "MobRenderer.h" +#include "EntityRenderDispatcher.h" +#include "../../gui/Font.h" +#include "../Tesselator.h" +#include "../../Minecraft.h" +#include "../../model/Model.h" +#include "../../../world/entity/Mob.h" +#include "../../../util/Mth.h" + +MobRenderer::MobRenderer(Model* model, float shadow) +: model(model), + armor(NULL) +{ + shadowRadius = shadow; +} + +MobRenderer::~MobRenderer() { + delete model; +} + +void MobRenderer::setArmor(Model* armor) { + this->armor = armor; +} + +Model* MobRenderer::getArmor() { + return armor; +} + +void MobRenderer::render(Entity* e, float x, float y, float z, float rot, float a) +{ + Mob* mob = (Mob*) e; + + glPushMatrix2(); + glDisable2(GL_CULL_FACE); + + model->attackTime = getAttackAnim(mob, a); + model->riding = false;//mob.isRiding(); + model->young = mob->isBaby(); + + if (armor) { + armor->riding = model->riding; + armor->young = model->young; + armor->attackTime = model->attackTime; + } + + float bodyRot = (mob->yBodyRotO + (mob->yBodyRot - mob->yBodyRotO) * a); + float headRot = (mob->yRotO + (mob->yRot - mob->yRotO) * a); + float headRotx = (mob->xRotO + (mob->xRot - mob->xRotO) * a); + + float yoffset = mob->heightOffset; + setupPosition(mob, x, y - yoffset, z); + + float bob = getBob(mob, a); + setupRotations(mob, bob, bodyRot, a); + + float ascale = 1 / 16.0f; + glScalef2(-1, -1, 1); + + scale(mob, a); + glTranslatef2(0, -24 * ascale - 0.125f / 16.0f, 0); + + float ws = mob->walkAnimSpeedO + (mob->walkAnimSpeed - mob->walkAnimSpeedO) * a; + float wp = mob->walkAnimPos - mob->walkAnimSpeed * (1 - a); + if (mob->isBaby()) { + wp *= 3.0f; + } + + if (ws > 1) ws = 1; + + bindTexture(mob->getTexture()); + //glEnable2(GL_ALPHA_TEST); + + model->prepareMobModel(mob, wp, ws, a); + model->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); + + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) { + if (prepareArmor(mob, i, a) < 0) continue; + + armor->prepareMobModel(mob, wp, ws, a); + armor->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); + glDisable2(GL_BLEND); + glEnable2(GL_ALPHA_TEST); + } + + additionalRendering(mob, a); + float br = mob->getBrightness(a); + int overlayColor = getOverlayColor(mob, br, a); + + bool renderOverlay = ((overlayColor >> 24) & 0xff) > 0; + bool renderHurt = (mob->hurtTime > 0) || (mob->deathTime > 0); + + if (renderOverlay || renderHurt) { + glDisable2(GL_TEXTURE_2D); + glDisable2(GL_ALPHA_TEST); + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_EQUAL); + + if (renderHurt) { + glEnable(GL_COLOR_MATERIAL); + glColor4f2(br, 0, 0, 0.4f); + model->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) { + if (prepareArmor(mob, i, a) < 0) continue; + + glColor4f2(br, 0, 0, 0.4f); + armor->prepareMobModel(mob, wp, ws, a); + armor->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); + } + } + + if (renderOverlay) { + float r = ((overlayColor >> 16) & 0xff) / 255.0f; + float g = ((overlayColor >> 8) & 0xff) / 255.0f; + float b = ((overlayColor) & 0xff) / 255.0f; + float aa = ((overlayColor >> 24) & 0xff) / 255.0f; + glColor4f2(r, g, b, aa); + model->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) { + if (prepareArmor(mob, i, a) < 0) continue; + + glColor4f2(r, g, b, aa); + armor->prepareMobModel(mob, wp, ws, a); + armor->render(e, wp, ws, bob, headRot - bodyRot, headRotx, ascale); + } + } + + glDepthFunc(GL_LEQUAL); + glDisable2(GL_BLEND); + glEnable2(GL_ALPHA_TEST); + glEnable2(GL_TEXTURE_2D); + } + + glEnable2(GL_CULL_FACE); + //glEnable2(GL_DEPTH_TEST); + + glPopMatrix2(); + + renderName(mob, x, y, z); +} + +void MobRenderer::setupPosition(Entity* mob, float x, float y, float z) { + glTranslatef2((float) x, (float) y, (float) z); +} + +void MobRenderer::setupRotations(Entity* mob_, float bob, float bodyRot, float a) { + glRotatef2(180 - bodyRot, 0, 1, 0); + + Mob* mob = (Mob*)mob_; + if (mob->deathTime > 0) { + float fall = (mob->deathTime + a - 1) / 20.0f * 1.6f; + fall = Mth::sqrt(fall); + if (fall > 1) fall = 1; + glRotatef2(fall * getFlipDegrees(mob), 0, 0, 1); + } +} + +float MobRenderer::getAttackAnim(Mob* mob, float a) { + return mob->getAttackAnim(a); +} + +float MobRenderer::getBob(Mob* mob, float a) { + return (mob->tickCount + a); +} + +void MobRenderer::additionalRendering(Mob* mob, float a) { +} + +void MobRenderer::onGraphicsReset() { + if (model) model->onGraphicsReset(); + if (armor) armor->onGraphicsReset(); +} + +int MobRenderer::prepareArmor(Mob* mob, int layer, float a) { + return -1; +} + +float MobRenderer::getFlipDegrees(Mob* mob) { + return 90; +} + +int MobRenderer::getOverlayColor(Mob* mob, float br, float a) { + return 0; +} + +void MobRenderer::scale(Mob* mob, float a) { +} + +void MobRenderer::renderName(Mob* mob, float x, float y, float z) { + /* + std::stringstream ss; ss << mob->entityId; + renderNameTag(mob, ss.str(), x, y, z, 64); + */ +} + +void MobRenderer::renderNameTag(Mob* mob, const std::string& name, float x, float y, float z, int maxDist) { + float dist = mob->distanceToSqr(entityRenderDispatcher->cameraEntity); + + if (dist > maxDist*maxDist) + return; + + Font* font = getFont(); + + float size = 1.60f; + float s = 1 / 60.0f * size; + + glPushMatrix2(); + glTranslatef2((float) x + 0, (float) y + 1.0f /*2.3f*/, (float) z); + + glRotatef2(-entityRenderDispatcher->playerRotY, 0, 1, 0); + glRotatef2(entityRenderDispatcher->playerRotX, 1, 0, 0); + + glScalef2(-s, -s, s); + + glDepthMask(false); + glDisable2(GL_DEPTH_TEST); + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Tesselator& t = Tesselator::instance; + + glDisable2(GL_TEXTURE_2D); + t.begin(); + int w = font->width(name) / 2; + t.color(0.0f, 0.0f, 0.0f, 0.25f); + t.vertex(-(float)w - 1, -1, 0); + t.vertex(-(float)w - 1, +8, 0); + t.vertex(+(float)w + 1, +8, 0); + t.vertex(+(float)w + 1, -1, 0); + //t.end(); + t.draw(); + glEnable2(GL_TEXTURE_2D); + const float fnameWidth = (float)font->width(name) / -2; + font->draw(name, fnameWidth, 0, 0x20ffffff); + glEnable2(GL_DEPTH_TEST); + + glDepthMask(true); + font->draw(name, (float) fnameWidth, 0, 0xffffffff); + glDisable2(GL_BLEND); + glColor4f2(1, 1, 1, 1); + glPopMatrix2(); +} diff --git a/src/client/renderer/entity/MobRenderer.h b/src/client/renderer/entity/MobRenderer.h index fee7786..2557e15 100755 --- a/src/client/renderer/entity/MobRenderer.h +++ b/src/client/renderer/entity/MobRenderer.h @@ -1,51 +1,51 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__MobRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__MobRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "EntityRenderer.h" - -#include - -class Model; -class Entity; -class Mob; - -class MobRenderer: public EntityRenderer -{ - static const int MAX_ARMOR_LAYERS = 4; -public: - // @note: MobRenderer::model will be deleted automagically - // Armor models (setArmor(Model*)) is to be deleted by derived classes. - MobRenderer(Model* model, float shadow); - ~MobRenderer(); - - virtual int prepareArmor(Mob* mob, int layer, float a); - - virtual void setupPosition(Entity* mob, float x, float y, float z); - virtual void setupRotations(Entity* mob_, float bob, float bodyRot, float a); - - virtual float getAttackAnim(Mob* mob, float a); - virtual float getBob(Mob* mob, float a); - - virtual float getFlipDegrees(Mob* mob); - - virtual int getOverlayColor(Mob* mob, float br, float a); - - virtual void scale(Mob* mob, float a); - - virtual void render(Entity* mob_, float x, float y, float z, float rot, float a); - virtual void renderName(Mob* mob, float x, float y, float z); - virtual void renderNameTag(Mob* mob, const std::string& name, float x, float y, float z, int maxDist); - virtual void additionalRendering(Mob* mob, float a); - - virtual void onGraphicsReset(); -protected: - void setArmor(Model* armor); - Model* getArmor(); - Model* model; // allows derived renderers to swap models dynamically for skin formats -private: - Model* armor; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__MobRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__MobRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__MobRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "EntityRenderer.h" + +#include + +class Model; +class Entity; +class Mob; + +class MobRenderer: public EntityRenderer +{ + static const int MAX_ARMOR_LAYERS = 4; +public: + // @note: MobRenderer::model will be deleted automagically + // Armor models (setArmor(Model*)) is to be deleted by derived classes. + MobRenderer(Model* model, float shadow); + ~MobRenderer(); + + virtual int prepareArmor(Mob* mob, int layer, float a); + + virtual void setupPosition(Entity* mob, float x, float y, float z); + virtual void setupRotations(Entity* mob_, float bob, float bodyRot, float a); + + virtual float getAttackAnim(Mob* mob, float a); + virtual float getBob(Mob* mob, float a); + + virtual float getFlipDegrees(Mob* mob); + + virtual int getOverlayColor(Mob* mob, float br, float a); + + virtual void scale(Mob* mob, float a); + + virtual void render(Entity* mob_, float x, float y, float z, float rot, float a); + virtual void renderName(Mob* mob, float x, float y, float z); + virtual void renderNameTag(Mob* mob, const std::string& name, float x, float y, float z, int maxDist); + virtual void additionalRendering(Mob* mob, float a); + + virtual void onGraphicsReset(); +protected: + void setArmor(Model* armor); + Model* getArmor(); + Model* model; // allows derived renderers to swap models dynamically for skin formats +private: + Model* armor; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__MobRenderer_H__*/ diff --git a/src/client/renderer/entity/PaintingRenderer.cpp b/src/client/renderer/entity/PaintingRenderer.cpp index 1513e95..0b0b923 100755 --- a/src/client/renderer/entity/PaintingRenderer.cpp +++ b/src/client/renderer/entity/PaintingRenderer.cpp @@ -1,99 +1,99 @@ -#include "PaintingRenderer.h" -#include "../../../world/entity/Painting.h" -#include "../../../world/Direction.h" -#include "../Tesselator.h" -#include "../Textures.h" -#include "../gles.h" - -void PaintingRenderer::render( Entity* entity, float x, float y, float z, float rot, float a ) { - glPushMatrix(); - glTranslatef(float(x), float(y), float(z)); - glRotatef(rot, 0, 1.0f, 0); - //glEnable(GL_RESCALE_NORMAL); - bindTexture("art/kz.png"); - Painting* painting = (Painting*)entity; - const Motive* motive = painting->motive; - - float s = 1.0f / 16.0f; - glScalef(s, s, s); - renderPainting(painting, motive->w, motive->h, motive->uo, motive->vo, a); - //glDisable(GL_RESCALE_NORMAL); - glPopMatrix(); -} - -void PaintingRenderer::renderPainting( Painting* painting, int w, int h, int uo, int vo, float a) { - float xx0 = -w / 2.0f; - float yy0 = -h / 2.0f; - - float edgeWidth = 0.5f; - - // Back - float bu0 = (12 * 16) / 256.0f; - float bu1 = (12 * 16 + 16) / 256.0f; - float bv0 = (0) / 256.0f; - float bv1 = (0 + 16) / 256.0f; - - // Border - float uu0 = (12 * 16) / 256.0f; - float uu1 = (12 * 16 + 16) / 256.0f; - float uv0 = (0.5f) / 256.0f; - float uv1 = (0.5f) / 256.0f; - - // Border - float su0 = (12 * 16 + 0.5f) / 256.0f; - float su1 = (12 * 16 + 0.5f) / 256.0f; - float sv0 = (0) / 256.0f; - float sv1 = (0 + 16) / 256.0f; - - for (int xs = 0; xs < w / 16; xs++) { - for (int ys = 0; ys < h / 16; ys++) { - float x0 = xx0 + (xs + 1) * 16; - float x1 = xx0 + (xs) * 16; - float y0 = yy0 + (ys + 1) * 16; - float y1 = yy0 + (ys) * 16; - //setBrightness(painting, (x0 + x1) / 2, (y0 + y1) / 2); - - // Painting - float fu0 = (uo + w - (xs) * 16) / 256.0f; - float fu1 = (uo + w - (xs + 1) * 16) / 256.0f; - float fv0 = (vo + h - (ys) * 16) / 256.0f; - float fv1 = (vo + h - (ys + 1) * 16) / 256.0f; - - Tesselator& t = Tesselator::instance; - float br = painting->getBrightness(a); - t.color(br, br, br); - - t.begin(); - t.vertexUV(x0, y1, -edgeWidth, fu1, fv0); - t.vertexUV(x1, y1, -edgeWidth, fu0, fv0); - t.vertexUV(x1, y0, -edgeWidth, fu0, fv1); - t.vertexUV(x0, y0, -edgeWidth, fu1, fv1); - - t.vertexUV(x0, y0, edgeWidth, bu0, bv0); - t.vertexUV(x1, y0, edgeWidth, bu1, bv0); - t.vertexUV(x1, y1, edgeWidth, bu1, bv1); - t.vertexUV(x0, y1, edgeWidth, bu0, bv1); - - t.vertexUV(x0, y0, -edgeWidth, uu0, uv0); - t.vertexUV(x1, y0, -edgeWidth, uu1, uv0); - t.vertexUV(x1, y0, edgeWidth, uu1, uv1); - t.vertexUV(x0, y0, edgeWidth, uu0, uv1); - - t.vertexUV(x0, y1, edgeWidth, uu0, uv0); - t.vertexUV(x1, y1, edgeWidth, uu1, uv0); - t.vertexUV(x1, y1, -edgeWidth, uu1, uv1); - t.vertexUV(x0, y1, -edgeWidth, uu0, uv1); - - t.vertexUV(x0, y0, edgeWidth, su1, sv0); - t.vertexUV(x0, y1, edgeWidth, su1, sv1); - t.vertexUV(x0, y1, -edgeWidth, su0, sv1); - t.vertexUV(x0, y0, -edgeWidth, su0, sv0); - - t.vertexUV(x1, y0, -edgeWidth, su1, sv0); - t.vertexUV(x1, y1, -edgeWidth, su1, sv1); - t.vertexUV(x1, y1, edgeWidth, su0, sv1); - t.vertexUV(x1, y0, edgeWidth, su0, sv0); - t.draw(); - } - } +#include "PaintingRenderer.h" +#include "../../../world/entity/Painting.h" +#include "../../../world/Direction.h" +#include "../Tesselator.h" +#include "../Textures.h" +#include "../gles.h" + +void PaintingRenderer::render( Entity* entity, float x, float y, float z, float rot, float a ) { + glPushMatrix(); + glTranslatef(float(x), float(y), float(z)); + glRotatef(rot, 0, 1.0f, 0); + //glEnable(GL_RESCALE_NORMAL); + bindTexture("art/kz.png"); + Painting* painting = (Painting*)entity; + const Motive* motive = painting->motive; + + float s = 1.0f / 16.0f; + glScalef(s, s, s); + renderPainting(painting, motive->w, motive->h, motive->uo, motive->vo, a); + //glDisable(GL_RESCALE_NORMAL); + glPopMatrix(); +} + +void PaintingRenderer::renderPainting( Painting* painting, int w, int h, int uo, int vo, float a) { + float xx0 = -w / 2.0f; + float yy0 = -h / 2.0f; + + float edgeWidth = 0.5f; + + // Back + float bu0 = (12 * 16) / 256.0f; + float bu1 = (12 * 16 + 16) / 256.0f; + float bv0 = (0) / 256.0f; + float bv1 = (0 + 16) / 256.0f; + + // Border + float uu0 = (12 * 16) / 256.0f; + float uu1 = (12 * 16 + 16) / 256.0f; + float uv0 = (0.5f) / 256.0f; + float uv1 = (0.5f) / 256.0f; + + // Border + float su0 = (12 * 16 + 0.5f) / 256.0f; + float su1 = (12 * 16 + 0.5f) / 256.0f; + float sv0 = (0) / 256.0f; + float sv1 = (0 + 16) / 256.0f; + + for (int xs = 0; xs < w / 16; xs++) { + for (int ys = 0; ys < h / 16; ys++) { + float x0 = xx0 + (xs + 1) * 16; + float x1 = xx0 + (xs) * 16; + float y0 = yy0 + (ys + 1) * 16; + float y1 = yy0 + (ys) * 16; + //setBrightness(painting, (x0 + x1) / 2, (y0 + y1) / 2); + + // Painting + float fu0 = (uo + w - (xs) * 16) / 256.0f; + float fu1 = (uo + w - (xs + 1) * 16) / 256.0f; + float fv0 = (vo + h - (ys) * 16) / 256.0f; + float fv1 = (vo + h - (ys + 1) * 16) / 256.0f; + + Tesselator& t = Tesselator::instance; + float br = painting->getBrightness(a); + t.color(br, br, br); + + t.begin(); + t.vertexUV(x0, y1, -edgeWidth, fu1, fv0); + t.vertexUV(x1, y1, -edgeWidth, fu0, fv0); + t.vertexUV(x1, y0, -edgeWidth, fu0, fv1); + t.vertexUV(x0, y0, -edgeWidth, fu1, fv1); + + t.vertexUV(x0, y0, edgeWidth, bu0, bv0); + t.vertexUV(x1, y0, edgeWidth, bu1, bv0); + t.vertexUV(x1, y1, edgeWidth, bu1, bv1); + t.vertexUV(x0, y1, edgeWidth, bu0, bv1); + + t.vertexUV(x0, y0, -edgeWidth, uu0, uv0); + t.vertexUV(x1, y0, -edgeWidth, uu1, uv0); + t.vertexUV(x1, y0, edgeWidth, uu1, uv1); + t.vertexUV(x0, y0, edgeWidth, uu0, uv1); + + t.vertexUV(x0, y1, edgeWidth, uu0, uv0); + t.vertexUV(x1, y1, edgeWidth, uu1, uv0); + t.vertexUV(x1, y1, -edgeWidth, uu1, uv1); + t.vertexUV(x0, y1, -edgeWidth, uu0, uv1); + + t.vertexUV(x0, y0, edgeWidth, su1, sv0); + t.vertexUV(x0, y1, edgeWidth, su1, sv1); + t.vertexUV(x0, y1, -edgeWidth, su0, sv1); + t.vertexUV(x0, y0, -edgeWidth, su0, sv0); + + t.vertexUV(x1, y0, -edgeWidth, su1, sv0); + t.vertexUV(x1, y1, -edgeWidth, su1, sv1); + t.vertexUV(x1, y1, edgeWidth, su0, sv1); + t.vertexUV(x1, y0, edgeWidth, su0, sv0); + t.draw(); + } + } } \ No newline at end of file diff --git a/src/client/renderer/entity/PaintingRenderer.h b/src/client/renderer/entity/PaintingRenderer.h index cc8d6ac..135c6a4 100755 --- a/src/client/renderer/entity/PaintingRenderer.h +++ b/src/client/renderer/entity/PaintingRenderer.h @@ -1,12 +1,12 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PaintingRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PaintingRenderer_H__ -#include "EntityRenderer.h" -class Painting; -class PaintingRenderer : public EntityRenderer { -public: - void render(Entity* entity, float x, float y, float z, float rot, float a); -private: - void renderPainting(Painting* painting, int w, int h, int uo, int vo, float a); -}; - +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PaintingRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PaintingRenderer_H__ +#include "EntityRenderer.h" +class Painting; +class PaintingRenderer : public EntityRenderer { +public: + void render(Entity* entity, float x, float y, float z, float rot, float a); +private: + void renderPainting(Painting* painting, int w, int h, int uo, int vo, float a); +}; + #endif /* NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PaintingRenderer_H__ */ \ No newline at end of file diff --git a/src/client/renderer/entity/PigRenderer.h b/src/client/renderer/entity/PigRenderer.h index 80edbac..1de3392 100755 --- a/src/client/renderer/entity/PigRenderer.h +++ b/src/client/renderer/entity/PigRenderer.h @@ -1,29 +1,29 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PigRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PigRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "../../model/Model.h" -#include "../../../world/entity/animal/Pig.h" - -class PigRenderer: public MobRenderer -{ - typedef MobRenderer super; -public: - PigRenderer(Model* model, Model* armor, float shadow) - : super(model, shadow) - { - // setArmor(armor); - } - - //void render(Entity* mob, float x, float y, float z, float rot, float a) { - // super::render(mob, x, y, z, rot, a); - //} -protected: - //int prepareArmor(Entity* pig, int layer, float a) { - // bindTexture("/mob/saddle.png"); - // return (layer == 0 && pig->hasSaddle()) ? 1 : -1; - //} -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PigRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PigRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PigRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "../../model/Model.h" +#include "../../../world/entity/animal/Pig.h" + +class PigRenderer: public MobRenderer +{ + typedef MobRenderer super; +public: + PigRenderer(Model* model, Model* armor, float shadow) + : super(model, shadow) + { + // setArmor(armor); + } + + //void render(Entity* mob, float x, float y, float z, float rot, float a) { + // super::render(mob, x, y, z, rot, a); + //} +protected: + //int prepareArmor(Entity* pig, int layer, float a) { + // bindTexture("/mob/saddle.png"); + // return (layer == 0 && pig->hasSaddle()) ? 1 : -1; + //} +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PigRenderer_H__*/ diff --git a/src/client/renderer/entity/PlayerRenderer.cpp b/src/client/renderer/entity/PlayerRenderer.cpp index 96ee2eb..a8bfe5b 100755 --- a/src/client/renderer/entity/PlayerRenderer.cpp +++ b/src/client/renderer/entity/PlayerRenderer.cpp @@ -1,124 +1,124 @@ -#include "PlayerRenderer.h" -#include "EntityRenderDispatcher.h" -#include "../Textures.h" -#include "../../../world/entity/player/Player.h" -#include "../../../world/level/Level.h" -#include "../../../world/item/ArmorItem.h" - -static const std::string armorFilenames[10] = { - "armor/cloth_1.png", "armor/cloth_2.png", - "armor/chain_1.png", "armor/chain_2.png", - "armor/iron_1.png", "armor/iron_2.png", - "armor/diamond_1.png", "armor/diamond_2.png", - "armor/gold_1.png", "armor/gold_2.png", -}; - -PlayerRenderer::PlayerRenderer( HumanoidModel* humanoidModel, float shadow ) -: super(humanoidModel, shadow), - playerModel64(humanoidModel), - playerModel32(new HumanoidModel(0, 0, 64, 32)), - armorParts1(new HumanoidModel(1.0f, 0, 64, 32)), - armorParts2(new HumanoidModel(0.5f, 0, 64, 32)) -{ - // default to legacy skin path until we know the exact texture size - model = playerModel32; - humanoidModel = playerModel32; -} - -PlayerRenderer::~PlayerRenderer() { - // prevent MobRenderer destructor from deleting model pointers we manage manually - model = nullptr; - - delete playerModel32; - delete playerModel64; - delete armorParts1; - delete armorParts2; -} - -void PlayerRenderer::setupPosition( Entity* mob, float x, float y, float z ) { - Player* player = (Player*) mob; - if(player->isAlive() && player->isSleeping()) { - return super::setupPosition(mob, x + player->bedOffsetX, y + player->bedOffsetY, z + player->bedOffsetZ); - } - return super::setupPosition(mob, x, y, z); -} - -void PlayerRenderer::setupRotations( Entity* mob, float bob, float bodyRot, float a ) { - Player* player = (Player*) mob; - if(player->isAlive() && player->isSleeping()) { - glRotatef(player->getSleepRotation(), 0, 1, 0); - glRotatef(getFlipDegrees(player), 0, 0, 1); - glRotatef(270, 0, 1, 0); - return; - } - super::setupRotations(mob, bob, bodyRot, a); -} - -bool PlayerRenderer::isModernPlayerSkin(Mob* mob) { - const std::string texName = mob->getTexture(); - TextureId texId = entityRenderDispatcher->textures->loadTexture(texName); - if (!Textures::isTextureIdValid(texId)) - return false; - const TextureData* texData = entityRenderDispatcher->textures->getTemporaryTextureData(texId); - return texData && texData->w == 64 && texData->h == 64; -} - -void PlayerRenderer::renderName( Mob* mob, float x, float y, float z ){ - //@todo: figure out how to handle HideGUI - if (mob != entityRenderDispatcher->cameraEntity && mob->level->adventureSettings.showNameTags) { - renderNameTag(mob, ((Player*)mob)->name, x, y, z, 32); - } -} - -void PlayerRenderer::render(Entity* mob_, float x, float y, float z, float rot, float a) { - Mob* mob = (Mob*) mob_; - HumanoidModel* desired = isModernPlayerSkin(mob) ? playerModel64 : playerModel32; - if (model != desired || humanoidModel != desired) { - model = desired; - humanoidModel = desired; - } - LOGI("[PlayerRenderer] %s: skin=%s, modelTex=%dx%d, desired=%s\n", - ((Player*)mob)->name.c_str(), mob->getTexture().c_str(), - humanoidModel->texWidth, humanoidModel->texHeight, - (desired == playerModel64 ? "64" : "32")); - HumanoidMobRenderer::render(mob_, x, y, z, rot, a); -} - -int PlayerRenderer::prepareArmor(Mob* mob, int layer, float a) { - Player* player = (Player*) mob; - - ItemInstance* itemInstance = player->getArmor(layer); - if (!ItemInstance::isArmorItem(itemInstance)) - return -1; - - ArmorItem* armorItem = (ArmorItem*) itemInstance->getItem(); - int fnIndex = (armorItem->modelIndex + armorItem->modelIndex) + (layer == 2 ? 1 : 0); - bindTexture(armorFilenames[fnIndex]); - - HumanoidModel* armor = layer == 2 ? armorParts2 : armorParts1; - - armor->head.visible = layer == 0; - //armor.hair.visible = layer == 0; - armor->body.visible = layer == 1 || layer == 2; - armor->arm0.visible = layer == 1; - armor->arm1.visible = layer == 1; - armor->leg0.visible = layer == 2 || layer == 3; - armor->leg1.visible = layer == 2 || layer == 3; - - setArmor(armor); - - /*if (itemInstance.isEnchanted()) - return 15; */ - - return 1; -} - -void PlayerRenderer::onGraphicsReset() { - if (playerModel32) playerModel32->onGraphicsReset(); - if (playerModel64) playerModel64->onGraphicsReset(); - - if (armorParts1) armorParts1->onGraphicsReset(); - if (armorParts2) armorParts2->onGraphicsReset(); - - super::onGraphicsReset(); -} +#include "PlayerRenderer.h" +#include "EntityRenderDispatcher.h" +#include "../Textures.h" +#include "../../../world/entity/player/Player.h" +#include "../../../world/level/Level.h" +#include "../../../world/item/ArmorItem.h" + +static const std::string armorFilenames[10] = { + "armor/cloth_1.png", "armor/cloth_2.png", + "armor/chain_1.png", "armor/chain_2.png", + "armor/iron_1.png", "armor/iron_2.png", + "armor/diamond_1.png", "armor/diamond_2.png", + "armor/gold_1.png", "armor/gold_2.png", +}; + +PlayerRenderer::PlayerRenderer( HumanoidModel* humanoidModel, float shadow ) +: super(humanoidModel, shadow), + playerModel64(humanoidModel), + playerModel32(new HumanoidModel(0, 0, 64, 32)), + armorParts1(new HumanoidModel(1.0f, 0, 64, 32)), + armorParts2(new HumanoidModel(0.5f, 0, 64, 32)) +{ + // default to legacy skin path until we know the exact texture size + model = playerModel32; + humanoidModel = playerModel32; +} + +PlayerRenderer::~PlayerRenderer() { + // prevent MobRenderer destructor from deleting model pointers we manage manually + model = nullptr; + + delete playerModel32; + delete playerModel64; + delete armorParts1; + delete armorParts2; +} + +void PlayerRenderer::setupPosition( Entity* mob, float x, float y, float z ) { + Player* player = (Player*) mob; + if(player->isAlive() && player->isSleeping()) { + return super::setupPosition(mob, x + player->bedOffsetX, y + player->bedOffsetY, z + player->bedOffsetZ); + } + return super::setupPosition(mob, x, y, z); +} + +void PlayerRenderer::setupRotations( Entity* mob, float bob, float bodyRot, float a ) { + Player* player = (Player*) mob; + if(player->isAlive() && player->isSleeping()) { + glRotatef(player->getSleepRotation(), 0, 1, 0); + glRotatef(getFlipDegrees(player), 0, 0, 1); + glRotatef(270, 0, 1, 0); + return; + } + super::setupRotations(mob, bob, bodyRot, a); +} + +bool PlayerRenderer::isModernPlayerSkin(Mob* mob) { + const std::string texName = mob->getTexture(); + TextureId texId = entityRenderDispatcher->textures->loadTexture(texName); + if (!Textures::isTextureIdValid(texId)) + return false; + const TextureData* texData = entityRenderDispatcher->textures->getTemporaryTextureData(texId); + return texData && texData->w == 64 && texData->h == 64; +} + +void PlayerRenderer::renderName( Mob* mob, float x, float y, float z ){ + //@todo: figure out how to handle HideGUI + if (mob != entityRenderDispatcher->cameraEntity && mob->level->adventureSettings.showNameTags) { + renderNameTag(mob, ((Player*)mob)->name, x, y, z, 32); + } +} + +void PlayerRenderer::render(Entity* mob_, float x, float y, float z, float rot, float a) { + Mob* mob = (Mob*) mob_; + HumanoidModel* desired = isModernPlayerSkin(mob) ? playerModel64 : playerModel32; + if (model != desired || humanoidModel != desired) { + model = desired; + humanoidModel = desired; + } + LOGI("[PlayerRenderer] %s: skin=%s, modelTex=%dx%d, desired=%s\n", + ((Player*)mob)->name.c_str(), mob->getTexture().c_str(), + humanoidModel->texWidth, humanoidModel->texHeight, + (desired == playerModel64 ? "64" : "32")); + HumanoidMobRenderer::render(mob_, x, y, z, rot, a); +} + +int PlayerRenderer::prepareArmor(Mob* mob, int layer, float a) { + Player* player = (Player*) mob; + + ItemInstance* itemInstance = player->getArmor(layer); + if (!ItemInstance::isArmorItem(itemInstance)) + return -1; + + ArmorItem* armorItem = (ArmorItem*) itemInstance->getItem(); + int fnIndex = (armorItem->modelIndex + armorItem->modelIndex) + (layer == 2 ? 1 : 0); + bindTexture(armorFilenames[fnIndex]); + + HumanoidModel* armor = layer == 2 ? armorParts2 : armorParts1; + + armor->head.visible = layer == 0; + //armor.hair.visible = layer == 0; + armor->body.visible = layer == 1 || layer == 2; + armor->arm0.visible = layer == 1; + armor->arm1.visible = layer == 1; + armor->leg0.visible = layer == 2 || layer == 3; + armor->leg1.visible = layer == 2 || layer == 3; + + setArmor(armor); + + /*if (itemInstance.isEnchanted()) + return 15; */ + + return 1; +} + +void PlayerRenderer::onGraphicsReset() { + if (playerModel32) playerModel32->onGraphicsReset(); + if (playerModel64) playerModel64->onGraphicsReset(); + + if (armorParts1) armorParts1->onGraphicsReset(); + if (armorParts2) armorParts2->onGraphicsReset(); + + super::onGraphicsReset(); +} diff --git a/src/client/renderer/entity/PlayerRenderer.h b/src/client/renderer/entity/PlayerRenderer.h index d4b25e2..9da8b3c 100755 --- a/src/client/renderer/entity/PlayerRenderer.h +++ b/src/client/renderer/entity/PlayerRenderer.h @@ -1,30 +1,30 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PlayerRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PlayerRenderer_H__ - -#include "HumanoidMobRenderer.h" - -class PlayerRenderer : public HumanoidMobRenderer -{ - typedef HumanoidMobRenderer super; -public: - PlayerRenderer(HumanoidModel* humanoidModel, float shadow); - ~PlayerRenderer(); - - virtual int prepareArmor(Mob* mob, int layer, float a); - bool isModernPlayerSkin(Mob* mob); - virtual void render(Entity* mob, float x, float y, float z, float rot, float a); - - virtual void setupPosition(Entity* mob, float x, float y, float z); - virtual void setupRotations(Entity* mob, float bob, float bodyRot, float a); - - virtual void renderName(Mob* mob, float x, float y, float z); - virtual void onGraphicsReset(); -private: - HumanoidModel* playerModel32; - HumanoidModel* playerModel64; - HumanoidModel* armorParts1; - HumanoidModel* armorParts2; -}; - - +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PlayerRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PlayerRenderer_H__ + +#include "HumanoidMobRenderer.h" + +class PlayerRenderer : public HumanoidMobRenderer +{ + typedef HumanoidMobRenderer super; +public: + PlayerRenderer(HumanoidModel* humanoidModel, float shadow); + ~PlayerRenderer(); + + virtual int prepareArmor(Mob* mob, int layer, float a); + bool isModernPlayerSkin(Mob* mob); + virtual void render(Entity* mob, float x, float y, float z, float rot, float a); + + virtual void setupPosition(Entity* mob, float x, float y, float z); + virtual void setupRotations(Entity* mob, float bob, float bodyRot, float a); + + virtual void renderName(Mob* mob, float x, float y, float z); + virtual void onGraphicsReset(); +private: + HumanoidModel* playerModel32; + HumanoidModel* playerModel64; + HumanoidModel* armorParts1; + HumanoidModel* armorParts2; +}; + + #endif /* NET_MINECRAFT_CLIENT_RENDERER_ENTITY__PlayerRenderer_H__ */ \ No newline at end of file diff --git a/src/client/renderer/entity/SheepRenderer.cpp b/src/client/renderer/entity/SheepRenderer.cpp index e8f6650..3b770e9 100755 --- a/src/client/renderer/entity/SheepRenderer.cpp +++ b/src/client/renderer/entity/SheepRenderer.cpp @@ -1,29 +1,29 @@ -#include "SheepRenderer.h" -#include "../../../world/entity/animal/Sheep.h" -#include "../gles.h" - -SheepRenderer::SheepRenderer( Model* model, Model* armor, float shadow ) -: super(model, shadow) -{ - setArmor(armor); -} - -SheepRenderer::~SheepRenderer() { - delete getArmor(); -} - -int SheepRenderer::prepareArmor(Mob* mob, int layer, float a) { - Sheep* sheep = (Sheep*) mob; - - if (layer == 0 && !sheep->isSheared()) { - bindTexture("mob/sheep_fur.png"); - - float brightness = sheep->getBrightness(a); - int color = sheep->getColor(); - glColor4f2( brightness * Sheep::COLOR[color][0], - brightness * Sheep::COLOR[color][1], - brightness * Sheep::COLOR[color][2], 1); - return 1; - } - return -1; -} +#include "SheepRenderer.h" +#include "../../../world/entity/animal/Sheep.h" +#include "../gles.h" + +SheepRenderer::SheepRenderer( Model* model, Model* armor, float shadow ) +: super(model, shadow) +{ + setArmor(armor); +} + +SheepRenderer::~SheepRenderer() { + delete getArmor(); +} + +int SheepRenderer::prepareArmor(Mob* mob, int layer, float a) { + Sheep* sheep = (Sheep*) mob; + + if (layer == 0 && !sheep->isSheared()) { + bindTexture("mob/sheep_fur.png"); + + float brightness = sheep->getBrightness(a); + int color = sheep->getColor(); + glColor4f2( brightness * Sheep::COLOR[color][0], + brightness * Sheep::COLOR[color][1], + brightness * Sheep::COLOR[color][2], 1); + return 1; + } + return -1; +} diff --git a/src/client/renderer/entity/SheepRenderer.h b/src/client/renderer/entity/SheepRenderer.h index 7998933..976fd21 100755 --- a/src/client/renderer/entity/SheepRenderer.h +++ b/src/client/renderer/entity/SheepRenderer.h @@ -1,20 +1,20 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SheepRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SheepRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "MobRenderer.h" -class Mob; - -class SheepRenderer: public MobRenderer -{ - typedef MobRenderer super; - -public: - SheepRenderer(Model* model, Model* armor, float shadow); - ~SheepRenderer(); -protected: - int prepareArmor(Mob* sheep, int layer, float a); -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SheepRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SheepRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SheepRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "MobRenderer.h" +class Mob; + +class SheepRenderer: public MobRenderer +{ + typedef MobRenderer super; + +public: + SheepRenderer(Model* model, Model* armor, float shadow); + ~SheepRenderer(); +protected: + int prepareArmor(Mob* sheep, int layer, float a); +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SheepRenderer_H__*/ diff --git a/src/client/renderer/entity/SpiderRenderer.h b/src/client/renderer/entity/SpiderRenderer.h index 9bdfdc8..319a911 100755 --- a/src/client/renderer/entity/SpiderRenderer.h +++ b/src/client/renderer/entity/SpiderRenderer.h @@ -1,46 +1,46 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SpiderRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SpiderRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "MobRenderer.h" - -#include "../gles.h" -#include "../../model/SpiderModel.h" -#include "../../../world/entity/monster/Spider.h" - -class SpiderRenderer: public MobRenderer -{ - typedef MobRenderer super; -public: - SpiderRenderer() - : super(new SpiderModel(), 1.0f) - { - //this->setArmor(/*new*/ SpiderModel()); - } - -protected: - float getFlipDegrees(Mob* spider) { - return 180; - } - -// int prepareArmor(Mob* spider, int layer, float a) { -// if (layer != 0) return -1; -// bindTexture("/mob/spider_eyes.png"); -// float br = 1.0f; -// glEnable(GL_BLEND); -// glDisable(GL_ALPHA_TEST); -//// glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -// glBlendFunc2(GL_ONE, GL_ONE); -// glColor4f2(1, 1, 1, br); -// return 1; -// } - - /*@Override*/ - void scale(Mob* mob, float a) { - float scale = ((Spider*)mob)->getModelScale(); - glScalef(scale, scale, scale); - } -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SpiderRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SpiderRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SpiderRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "MobRenderer.h" + +#include "../gles.h" +#include "../../model/SpiderModel.h" +#include "../../../world/entity/monster/Spider.h" + +class SpiderRenderer: public MobRenderer +{ + typedef MobRenderer super; +public: + SpiderRenderer() + : super(new SpiderModel(), 1.0f) + { + //this->setArmor(/*new*/ SpiderModel()); + } + +protected: + float getFlipDegrees(Mob* spider) { + return 180; + } + +// int prepareArmor(Mob* spider, int layer, float a) { +// if (layer != 0) return -1; +// bindTexture("/mob/spider_eyes.png"); +// float br = 1.0f; +// glEnable(GL_BLEND); +// glDisable(GL_ALPHA_TEST); +//// glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +// glBlendFunc2(GL_ONE, GL_ONE); +// glColor4f2(1, 1, 1, br); +// return 1; +// } + + /*@Override*/ + void scale(Mob* mob, float a) { + float scale = ((Spider*)mob)->getModelScale(); + glScalef(scale, scale, scale); + } +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__SpiderRenderer_H__*/ diff --git a/src/client/renderer/entity/TntRenderer.cpp b/src/client/renderer/entity/TntRenderer.cpp index 6371656..9bac6fc 100755 --- a/src/client/renderer/entity/TntRenderer.cpp +++ b/src/client/renderer/entity/TntRenderer.cpp @@ -1,47 +1,47 @@ -#include "TntRenderer.h" -#include "../Tesselator.h" -#include "../../../world/level/tile/Tile.h" -#include "../../../world/entity/item/PrimedTnt.h" - - -TntRenderer::TntRenderer() -{ - this->shadowRadius = 0.5f; -} - -void TntRenderer::render( Entity* tnt_, float x, float y, float z, float rot, float a ) -{ - PrimedTnt* tnt = (PrimedTnt*)tnt_; - glPushMatrix2(); - glTranslatef2((float) x, (float) y, (float) z); - if (tnt->life - a + 1 < 10) { - float g = 1 - ((tnt->life - a + 1) / 10.0f); - if (g < 0) g = 0; - if (g > 1) g = 1; - g = g * g; - g = g * g; - float s = 1.0f + g * 0.3f; - glScalef2(s, s, s); - } - - float br = (1 - ((tnt->life - a + 1) / 100.0f)) * 0.8f; - bindTexture("terrain.png"); - - Tesselator& t = Tesselator::instance; - t.color(1.0f, 1.0f, 1.0f); - tileRenderer.renderTile(Tile::tnt, 0); - - if (((tnt->life / 5) & 1) == 0) { - glDisable2(GL_TEXTURE_2D); - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_DST_ALPHA); - glColor4f2(1, 1, 1, br); - //t.color(1.0f, 1.0f, 1.0f, br); - tileRenderer.renderTile(Tile::tnt, 0); - glColor4f2(1, 1, 1, 1); - glDisable2(GL_BLEND); - //glEnable2(GL_LIGHTING); - glEnable2(GL_TEXTURE_2D); - } - glPopMatrix2(); -} +#include "TntRenderer.h" +#include "../Tesselator.h" +#include "../../../world/level/tile/Tile.h" +#include "../../../world/entity/item/PrimedTnt.h" + + +TntRenderer::TntRenderer() +{ + this->shadowRadius = 0.5f; +} + +void TntRenderer::render( Entity* tnt_, float x, float y, float z, float rot, float a ) +{ + PrimedTnt* tnt = (PrimedTnt*)tnt_; + glPushMatrix2(); + glTranslatef2((float) x, (float) y, (float) z); + if (tnt->life - a + 1 < 10) { + float g = 1 - ((tnt->life - a + 1) / 10.0f); + if (g < 0) g = 0; + if (g > 1) g = 1; + g = g * g; + g = g * g; + float s = 1.0f + g * 0.3f; + glScalef2(s, s, s); + } + + float br = (1 - ((tnt->life - a + 1) / 100.0f)) * 0.8f; + bindTexture("terrain.png"); + + Tesselator& t = Tesselator::instance; + t.color(1.0f, 1.0f, 1.0f); + tileRenderer.renderTile(Tile::tnt, 0); + + if (((tnt->life / 5) & 1) == 0) { + glDisable2(GL_TEXTURE_2D); + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_DST_ALPHA); + glColor4f2(1, 1, 1, br); + //t.color(1.0f, 1.0f, 1.0f, br); + tileRenderer.renderTile(Tile::tnt, 0); + glColor4f2(1, 1, 1, 1); + glDisable2(GL_BLEND); + //glEnable2(GL_LIGHTING); + glEnable2(GL_TEXTURE_2D); + } + glPopMatrix2(); +} diff --git a/src/client/renderer/entity/TntRenderer.h b/src/client/renderer/entity/TntRenderer.h index efa0e0d..05b0787 100755 --- a/src/client/renderer/entity/TntRenderer.h +++ b/src/client/renderer/entity/TntRenderer.h @@ -1,18 +1,18 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TntRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TntRenderer_H__ - -//package net.minecraft.client.renderer.entity; - -#include "EntityRenderer.h" -#include "../TileRenderer.h" - -class TntRenderer: public EntityRenderer -{ -public: - TntRenderer(); - void render(Entity* tnt_, float x, float y, float z, float rot, float a); - - TileRenderer tileRenderer; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TntRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TntRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TntRenderer_H__ + +//package net.minecraft.client.renderer.entity; + +#include "EntityRenderer.h" +#include "../TileRenderer.h" + +class TntRenderer: public EntityRenderer +{ +public: + TntRenderer(); + void render(Entity* tnt_, float x, float y, float z, float rot, float a); + + TileRenderer tileRenderer; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TntRenderer_H__*/ diff --git a/src/client/renderer/entity/TripodCameraRenderer.cpp b/src/client/renderer/entity/TripodCameraRenderer.cpp index 97838d5..ac57148 100755 --- a/src/client/renderer/entity/TripodCameraRenderer.cpp +++ b/src/client/renderer/entity/TripodCameraRenderer.cpp @@ -1,75 +1,75 @@ -#include "TripodCameraRenderer.h" -#include "EntityRenderDispatcher.h" -#include "../Tesselator.h" -#include "../../Minecraft.h" -#include "../../../world/entity/item/TripodCamera.h" -#include "../../../world/level/material/Material.h" - -TripodCameraRenderer::TripodCameraRenderer() -: tripod(0, 15 * 16 + 3, Material::plant), - cameraCube(0, 0) -{ - cameraCube.addBox(-4, -4, -6, 8, 8, 10); - cameraCube.y = 11; - this->shadowRadius = 0.5f; -} - -float TripodCameraRenderer::getFlashTime(const TripodCamera* c, float a) { - if (c->life >= 8) return -1; - if (c->life < 0) return -1; - return (c->life - a) * 0.125f; -} - -void TripodCameraRenderer::render(Entity* cam_, float x, float y, float z, float rot, float a) { - TripodCamera* cam = (TripodCamera*)cam_; - - glPushMatrix2(); - glTranslatef2((float) x, (float) y, (float) z); - - cameraCube.xRot = Mth::DEGRAD * (180.0f + cam->xRot * 0.5f); - cameraCube.yRot = -Mth::DEGRAD * cam->yRot; - - //float br = (1 - ((cam->life - a + 1) / 100.0f)) * 0.8f; - - Tesselator& t = Tesselator::instance; - t.color(1.0f, 1.0f, 1.0f); - - //printf("xyz: %f, %f, %f\n", x, y, z); - - // Render tripod legs - bindTexture("gui/items.png"); - t.begin(); - tileRenderer.tesselateCrossTexture(&tripod, 0, -0.5f, -0.5f, -0.5f);//, y, z); - t.draw(); - - // Render tripod - bindTexture("item/camera.png"); - cameraCube.render(1.0f / 16.0f); - - bool isCurrentlyPicked = entityRenderDispatcher->minecraft->hitResult.entity == cam; - - const float flashLife = getFlashTime(cam, a); - - if (flashLife >= 0) { - const float flashStrength = ::sin(flashLife * 6.2830f); - - // Flash - glColor4f2(1, 1, 1, flashStrength); - glColor4f2(1, 1, 1, 1); - } - - // "red light" flashing when photo is about to go off - if (isCurrentlyPicked) { - glDisable2(GL_TEXTURE_2D); - glEnable2(GL_BLEND); - glBlendFunc2(GL_SRC_ALPHA, GL_DST_ALPHA); - glColor4f2(0.5f,0.5f,0.5f,0.5f); - - cameraCube.renderHorrible(1.0f / 16.0f); - - glColor4f2(1,1,1,1); - glDisable2(GL_BLEND); - glEnable2(GL_TEXTURE_2D); - } - glPopMatrix2(); -} +#include "TripodCameraRenderer.h" +#include "EntityRenderDispatcher.h" +#include "../Tesselator.h" +#include "../../Minecraft.h" +#include "../../../world/entity/item/TripodCamera.h" +#include "../../../world/level/material/Material.h" + +TripodCameraRenderer::TripodCameraRenderer() +: tripod(0, 15 * 16 + 3, Material::plant), + cameraCube(0, 0) +{ + cameraCube.addBox(-4, -4, -6, 8, 8, 10); + cameraCube.y = 11; + this->shadowRadius = 0.5f; +} + +float TripodCameraRenderer::getFlashTime(const TripodCamera* c, float a) { + if (c->life >= 8) return -1; + if (c->life < 0) return -1; + return (c->life - a) * 0.125f; +} + +void TripodCameraRenderer::render(Entity* cam_, float x, float y, float z, float rot, float a) { + TripodCamera* cam = (TripodCamera*)cam_; + + glPushMatrix2(); + glTranslatef2((float) x, (float) y, (float) z); + + cameraCube.xRot = Mth::DEGRAD * (180.0f + cam->xRot * 0.5f); + cameraCube.yRot = -Mth::DEGRAD * cam->yRot; + + //float br = (1 - ((cam->life - a + 1) / 100.0f)) * 0.8f; + + Tesselator& t = Tesselator::instance; + t.color(1.0f, 1.0f, 1.0f); + + //printf("xyz: %f, %f, %f\n", x, y, z); + + // Render tripod legs + bindTexture("gui/items.png"); + t.begin(); + tileRenderer.tesselateCrossTexture(&tripod, 0, -0.5f, -0.5f, -0.5f);//, y, z); + t.draw(); + + // Render tripod + bindTexture("item/camera.png"); + cameraCube.render(1.0f / 16.0f); + + bool isCurrentlyPicked = entityRenderDispatcher->minecraft->hitResult.entity == cam; + + const float flashLife = getFlashTime(cam, a); + + if (flashLife >= 0) { + const float flashStrength = ::sin(flashLife * 6.2830f); + + // Flash + glColor4f2(1, 1, 1, flashStrength); + glColor4f2(1, 1, 1, 1); + } + + // "red light" flashing when photo is about to go off + if (isCurrentlyPicked) { + glDisable2(GL_TEXTURE_2D); + glEnable2(GL_BLEND); + glBlendFunc2(GL_SRC_ALPHA, GL_DST_ALPHA); + glColor4f2(0.5f,0.5f,0.5f,0.5f); + + cameraCube.renderHorrible(1.0f / 16.0f); + + glColor4f2(1,1,1,1); + glDisable2(GL_BLEND); + glEnable2(GL_TEXTURE_2D); + } + glPopMatrix2(); +} diff --git a/src/client/renderer/entity/TripodCameraRenderer.h b/src/client/renderer/entity/TripodCameraRenderer.h index 35d4058..6792717 100755 --- a/src/client/renderer/entity/TripodCameraRenderer.h +++ b/src/client/renderer/entity/TripodCameraRenderer.h @@ -1,29 +1,29 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TripodCameraRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TripodCameraRenderer_H__ - -#include "EntityRenderer.h" -#include "../TileRenderer.h" -#include "../../model/geom/ModelPart.h" -#include "../../../world/level/tile/Tile.h" - - -class TripodCamera; - -class TripodCameraRenderer: public EntityRenderer -{ - TileRenderer tileRenderer; - -public: - TripodCameraRenderer(); - - void render(Entity* cam_, float x, float y, float z, float rot, float a); - //void render( Tesselator &t ); - - static float getFlashTime(const TripodCamera* c, float a); - -private: - Tile tripod; - ModelPart cameraCube; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TripodCameraRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TripodCameraRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TripodCameraRenderer_H__ + +#include "EntityRenderer.h" +#include "../TileRenderer.h" +#include "../../model/geom/ModelPart.h" +#include "../../../world/level/tile/Tile.h" + + +class TripodCamera; + +class TripodCameraRenderer: public EntityRenderer +{ + TileRenderer tileRenderer; + +public: + TripodCameraRenderer(); + + void render(Entity* cam_, float x, float y, float z, float rot, float a); + //void render( Tesselator &t ); + + static float getFlashTime(const TripodCamera* c, float a); + +private: + Tile tripod; + ModelPart cameraCube; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__TripodCameraRenderer_H__*/ diff --git a/src/client/renderer/gles.cpp b/src/client/renderer/gles.cpp index 04ae3d3..fbec254 100755 --- a/src/client/renderer/gles.cpp +++ b/src/client/renderer/gles.cpp @@ -1,386 +1,386 @@ -#include "gles.h" -#include -#include - -static const float __glPi = 3.14159265358979323846f; - -static void __gluMakeIdentityf(GLfloat m[16]); - - -void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar) { - GLfloat m[4][4]; - GLfloat sine, cotangent, deltaZ; - GLfloat radians=(GLfloat)(fovy/2.0f*__glPi/180.0f); - - deltaZ=zFar-zNear; - sine=(GLfloat)sin(radians); - if ((deltaZ==0.0f) || (sine==0.0f) || (aspect==0.0f)) - { - return; - } - cotangent=(GLfloat)(cos(radians)/sine); - - __gluMakeIdentityf(&m[0][0]); - m[0][0] = cotangent / aspect; - m[1][1] = cotangent; - m[2][2] = -(zFar + zNear) / deltaZ; - m[2][3] = -1.0f; - m[3][2] = -2.0f * zNear * zFar / deltaZ; - m[3][3] = 0; - glMultMatrixf(&m[0][0]); -} - -void __gluMakeIdentityf(GLfloat m[16]) { - m[0] = 1; m[4] = 0; m[8] = 0; m[12] = 0; - m[1] = 0; m[5] = 1; m[9] = 0; m[13] = 0; - m[2] = 0; m[6] = 0; m[10] = 1; m[14] = 0; - m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1; -} - -void glInit() -{ -#ifndef OPENGL_ES - - GLenum err = glewInit(); - printf("Err: %d\n", err); -#endif -} - -void anGenBuffers(GLsizei n, GLuint* buffers) { -#ifdef __EMSCRIPTEN__ - glGenBuffers(n, buffers); -#else - static GLuint k = 1; - for (int i = 0; i < n; ++i) - buffers[i] = ++k; -#endif -} - -#ifdef USE_VBO -void drawArrayVT(int bufferId, int vertices, int vertexSize /* = 24 */, unsigned int mode /* = GL_TRIANGLES */) { - //if (Options::debugGl) LOGI("drawArray\n"); - glBindBuffer2(GL_ARRAY_BUFFER, bufferId); - glTexCoordPointer2(2, GL_FLOAT, vertexSize, (GLvoid*) (3 * 4)); - glEnableClientState2(GL_TEXTURE_COORD_ARRAY); - glVertexPointer2(3, GL_FLOAT, vertexSize, 0); - glEnableClientState2(GL_VERTEX_ARRAY); - glDrawArrays2(mode, 0, vertices); - glDisableClientState2(GL_VERTEX_ARRAY); - glDisableClientState2(GL_TEXTURE_COORD_ARRAY); -} - -#ifndef drawArrayVT_NoState -void drawArrayVT_NoState(int bufferId, int vertices, int vertexSize /* = 24 */) { - //if (Options::debugGl) LOGI("drawArray\n"); - glBindBuffer2(GL_ARRAY_BUFFER, bufferId); - glTexCoordPointer2(2, GL_FLOAT, vertexSize, (GLvoid*) (3 * 4)); - //glEnableClientState2(GL_TEXTURE_COORD_ARRAY); - glVertexPointer2(3, GL_FLOAT, vertexSize, 0); - //glEnableClientState2(GL_VERTEX_ARRAY); - glDrawArrays2(GL_TRIANGLES, 0, vertices); - //glDisableClientState2(GL_VERTEX_ARRAY); - //glDisableClientState2(GL_TEXTURE_COORD_ARRAY); -} -#endif - -void drawArrayVTC(int bufferId, int vertices, int vertexSize /* = 24 */) { - //if (Options::debugGl) LOGI("drawArray\n"); - //LOGI("draw-vtc: %d, %d, %d\n", bufferId, vertices, vertexSize); - glEnableClientState2(GL_VERTEX_ARRAY); - glEnableClientState2(GL_TEXTURE_COORD_ARRAY); - glEnableClientState2(GL_COLOR_ARRAY); - - glBindBuffer2(GL_ARRAY_BUFFER, bufferId); - - glVertexPointer2( 3, GL_FLOAT, vertexSize, 0); - glTexCoordPointer2(2, GL_FLOAT, vertexSize, (GLvoid*) (3 * 4)); - glColorPointer2(4, GL_UNSIGNED_BYTE, vertexSize, (GLvoid*) (5*4)); - - glDrawArrays2(GL_TRIANGLES, 0, vertices); - - glDisableClientState2(GL_VERTEX_ARRAY); - glDisableClientState2(GL_TEXTURE_COORD_ARRAY); - glDisableClientState2(GL_COLOR_ARRAY); -} - -#ifndef drawArrayVTC_NoState -void drawArrayVTC_NoState(int bufferId, int vertices, int vertexSize /* = 24 */) { - glBindBuffer2(GL_ARRAY_BUFFER, bufferId); - - glVertexPointer2( 3, GL_FLOAT, vertexSize, 0); - glTexCoordPointer2(2, GL_FLOAT, vertexSize, (GLvoid*) (3 * 4)); - glColorPointer2(4, GL_UNSIGNED_BYTE, vertexSize, (GLvoid*) (5*4)); - - glDrawArrays2(GL_TRIANGLES, 0, vertices); -} -#endif - -#endif - - -// -// Code borrowed from OpenGL.org -// http://www.opengl.org/wiki/GluProject_and_gluUnProject_code -// The gluUnProject code in Android seems to be broken -// - -void MultiplyMatrices4by4OpenGL_FLOAT(float *result, float *matrix1, float *matrix2) -{ - result[0]=matrix1[0]*matrix2[0]+ - matrix1[4]*matrix2[1]+ - matrix1[8]*matrix2[2]+ - matrix1[12]*matrix2[3]; - result[4]=matrix1[0]*matrix2[4]+ - matrix1[4]*matrix2[5]+ - matrix1[8]*matrix2[6]+ - matrix1[12]*matrix2[7]; - result[8]=matrix1[0]*matrix2[8]+ - matrix1[4]*matrix2[9]+ - matrix1[8]*matrix2[10]+ - matrix1[12]*matrix2[11]; - result[12]=matrix1[0]*matrix2[12]+ - matrix1[4]*matrix2[13]+ - matrix1[8]*matrix2[14]+ - matrix1[12]*matrix2[15]; - result[1]=matrix1[1]*matrix2[0]+ - matrix1[5]*matrix2[1]+ - matrix1[9]*matrix2[2]+ - matrix1[13]*matrix2[3]; - result[5]=matrix1[1]*matrix2[4]+ - matrix1[5]*matrix2[5]+ - matrix1[9]*matrix2[6]+ - matrix1[13]*matrix2[7]; - result[9]=matrix1[1]*matrix2[8]+ - matrix1[5]*matrix2[9]+ - matrix1[9]*matrix2[10]+ - matrix1[13]*matrix2[11]; - result[13]=matrix1[1]*matrix2[12]+ - matrix1[5]*matrix2[13]+ - matrix1[9]*matrix2[14]+ - matrix1[13]*matrix2[15]; - result[2]=matrix1[2]*matrix2[0]+ - matrix1[6]*matrix2[1]+ - matrix1[10]*matrix2[2]+ - matrix1[14]*matrix2[3]; - result[6]=matrix1[2]*matrix2[4]+ - matrix1[6]*matrix2[5]+ - matrix1[10]*matrix2[6]+ - matrix1[14]*matrix2[7]; - result[10]=matrix1[2]*matrix2[8]+ - matrix1[6]*matrix2[9]+ - matrix1[10]*matrix2[10]+ - matrix1[14]*matrix2[11]; - result[14]=matrix1[2]*matrix2[12]+ - matrix1[6]*matrix2[13]+ - matrix1[10]*matrix2[14]+ - matrix1[14]*matrix2[15]; - result[3]=matrix1[3]*matrix2[0]+ - matrix1[7]*matrix2[1]+ - matrix1[11]*matrix2[2]+ - matrix1[15]*matrix2[3]; - result[7]=matrix1[3]*matrix2[4]+ - matrix1[7]*matrix2[5]+ - matrix1[11]*matrix2[6]+ - matrix1[15]*matrix2[7]; - result[11]=matrix1[3]*matrix2[8]+ - matrix1[7]*matrix2[9]+ - matrix1[11]*matrix2[10]+ - matrix1[15]*matrix2[11]; - result[15]=matrix1[3]*matrix2[12]+ - matrix1[7]*matrix2[13]+ - matrix1[11]*matrix2[14]+ - matrix1[15]*matrix2[15]; -} - -void MultiplyMatrixByVector4by4OpenGL_FLOAT(float *resultvector, const float *matrix, const float *pvector) -{ - resultvector[0]=matrix[0]*pvector[0]+matrix[4]*pvector[1]+matrix[8]*pvector[2]+matrix[12]*pvector[3]; - resultvector[1]=matrix[1]*pvector[0]+matrix[5]*pvector[1]+matrix[9]*pvector[2]+matrix[13]*pvector[3]; - resultvector[2]=matrix[2]*pvector[0]+matrix[6]*pvector[1]+matrix[10]*pvector[2]+matrix[14]*pvector[3]; - resultvector[3]=matrix[3]*pvector[0]+matrix[7]*pvector[1]+matrix[11]*pvector[2]+matrix[15]*pvector[3]; -} - -#define SWAP_ROWS_DOUBLE(a, b) { double *_tmp = a; (a)=(b); (b)=_tmp; } -#define SWAP_ROWS_FLOAT(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; } -#define MAT(m,r,c) (m)[(c)*4+(r)] - -//This code comes directly from GLU except that it is for float -int glhInvertMatrixf2(float *m, float *out) -{ - float wtmp[4][8]; - float m0, m1, m2, m3, s; - float *r0, *r1, *r2, *r3; - r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; - r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1), - r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3), - r0[4] = 1.0f, r0[5] = r0[6] = r0[7] = 0.0f, - r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1), - r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3), - r1[5] = 1.0f, r1[4] = r1[6] = r1[7] = 0.0f, - r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1), - r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3), - r2[6] = 1.0f, r2[4] = r2[5] = r2[7] = 0.0f, - r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1), - r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3), - r3[7] = 1.0f, r3[4] = r3[5] = r3[6] = 0.0f; - /* choose pivot - or die */ - if (fabsf(r3[0]) > fabsf(r2[0])) - SWAP_ROWS_FLOAT(r3, r2); - if (fabsf(r2[0]) > fabsf(r1[0])) - SWAP_ROWS_FLOAT(r2, r1); - if (fabsf(r1[0]) > fabsf(r0[0])) - SWAP_ROWS_FLOAT(r1, r0); - if (0.0f == r0[0]) - return 0; - /* eliminate first variable */ - m1 = r1[0] / r0[0]; - m2 = r2[0] / r0[0]; - m3 = r3[0] / r0[0]; - s = r0[1]; - r1[1] -= m1 * s; - r2[1] -= m2 * s; - r3[1] -= m3 * s; - s = r0[2]; - r1[2] -= m1 * s; - r2[2] -= m2 * s; - r3[2] -= m3 * s; - s = r0[3]; - r1[3] -= m1 * s; - r2[3] -= m2 * s; - r3[3] -= m3 * s; - s = r0[4]; - if (s != 0.0f) { - r1[4] -= m1 * s; - r2[4] -= m2 * s; - r3[4] -= m3 * s; - } - s = r0[5]; - if (s != 0.0f) { - r1[5] -= m1 * s; - r2[5] -= m2 * s; - r3[5] -= m3 * s; - } - s = r0[6]; - if (s != 0.0f) { - r1[6] -= m1 * s; - r2[6] -= m2 * s; - r3[6] -= m3 * s; - } - s = r0[7]; - if (s != 0.0f) { - r1[7] -= m1 * s; - r2[7] -= m2 * s; - r3[7] -= m3 * s; - } - /* choose pivot - or die */ - if (fabsf(r3[1]) > fabsf(r2[1])) - SWAP_ROWS_FLOAT(r3, r2); - if (fabsf(r2[1]) > fabsf(r1[1])) - SWAP_ROWS_FLOAT(r2, r1); - if (0.0f == r1[1]) - return 0; - /* eliminate second variable */ - m2 = r2[1] / r1[1]; - m3 = r3[1] / r1[1]; - r2[2] -= m2 * r1[2]; - r3[2] -= m3 * r1[2]; - r2[3] -= m2 * r1[3]; - r3[3] -= m3 * r1[3]; - s = r1[4]; - if (0.0f != s) { - r2[4] -= m2 * s; - r3[4] -= m3 * s; - } - s = r1[5]; - if (0.0f != s) { - r2[5] -= m2 * s; - r3[5] -= m3 * s; - } - s = r1[6]; - if (0.0f != s) { - r2[6] -= m2 * s; - r3[6] -= m3 * s; - } - s = r1[7]; - if (0.0f != s) { - r2[7] -= m2 * s; - r3[7] -= m3 * s; - } - /* choose pivot - or die */ - if (fabsf(r3[2]) > fabsf(r2[2])) - SWAP_ROWS_FLOAT(r3, r2); - if (0.0f == r2[2]) - return 0; - /* eliminate third variable */ - m3 = r3[2] / r2[2]; - r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], - r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7]; - /* last check */ - if (0.0f == r3[3]) - return 0; - s = 1.0f / r3[3]; /* now back substitute row 3 */ - r3[4] *= s; - r3[5] *= s; - r3[6] *= s; - r3[7] *= s; - m2 = r2[3]; /* now back substitute row 2 */ - s = 1.0f / r2[2]; - r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), - r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); - m1 = r1[3]; - r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1, - r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1; - m0 = r0[3]; - r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, - r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; - m1 = r1[2]; /* now back substitute row 1 */ - s = 1.0f / r1[1]; - r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), - r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); - m0 = r0[2]; - r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, - r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; - m0 = r0[1]; /* now back substitute row 0 */ - s = 1.0f / r0[0]; - r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), - r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); - MAT(out, 0, 0) = r0[4]; - MAT(out, 0, 1) = r0[5], MAT(out, 0, 2) = r0[6]; - MAT(out, 0, 3) = r0[7], MAT(out, 1, 0) = r1[4]; - MAT(out, 1, 1) = r1[5], MAT(out, 1, 2) = r1[6]; - MAT(out, 1, 3) = r1[7], MAT(out, 2, 0) = r2[4]; - MAT(out, 2, 1) = r2[5], MAT(out, 2, 2) = r2[6]; - MAT(out, 2, 3) = r2[7], MAT(out, 3, 0) = r3[4]; - MAT(out, 3, 1) = r3[5], MAT(out, 3, 2) = r3[6]; - MAT(out, 3, 3) = r3[7]; - return 1; -} - -int glhUnProjectf( float winx, float winy, float winz, - float *modelview, float *projection, - int *viewport, float *objectCoordinate) -{ - //Transformation matrices - float m[16], A[16]; - float in[4], out[4]; - //Calculation for inverting a matrix, compute projection x modelview - //and store in A[16] - MultiplyMatrices4by4OpenGL_FLOAT(A, projection, modelview); - //Now compute the inverse of matrix A - if(glhInvertMatrixf2(A, m)==0) - return 0; - //Transformation of normalized coordinates between -1 and 1 - in[0]=(winx-(float)viewport[0])/(float)viewport[2]*2.0f-1.0f; - in[1]=(winy-(float)viewport[1])/(float)viewport[3]*2.0f-1.0f; - in[2]=2.0f*winz-1.0f; - in[3]=1.0f; - //Objects coordinates - MultiplyMatrixByVector4by4OpenGL_FLOAT(out, m, in); - if(out[3]==0.0f) - return 0; - out[3]=1.0f/out[3]; - objectCoordinate[0]=out[0]*out[3]; - objectCoordinate[1]=out[1]*out[3]; - objectCoordinate[2]=out[2]*out[3]; - return 1; -} +#include "gles.h" +#include +#include + +static const float __glPi = 3.14159265358979323846f; + +static void __gluMakeIdentityf(GLfloat m[16]); + + +void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar) { + GLfloat m[4][4]; + GLfloat sine, cotangent, deltaZ; + GLfloat radians=(GLfloat)(fovy/2.0f*__glPi/180.0f); + + deltaZ=zFar-zNear; + sine=(GLfloat)sin(radians); + if ((deltaZ==0.0f) || (sine==0.0f) || (aspect==0.0f)) + { + return; + } + cotangent=(GLfloat)(cos(radians)/sine); + + __gluMakeIdentityf(&m[0][0]); + m[0][0] = cotangent / aspect; + m[1][1] = cotangent; + m[2][2] = -(zFar + zNear) / deltaZ; + m[2][3] = -1.0f; + m[3][2] = -2.0f * zNear * zFar / deltaZ; + m[3][3] = 0; + glMultMatrixf(&m[0][0]); +} + +void __gluMakeIdentityf(GLfloat m[16]) { + m[0] = 1; m[4] = 0; m[8] = 0; m[12] = 0; + m[1] = 0; m[5] = 1; m[9] = 0; m[13] = 0; + m[2] = 0; m[6] = 0; m[10] = 1; m[14] = 0; + m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1; +} + +void glInit() +{ +#ifndef OPENGL_ES + + GLenum err = glewInit(); + printf("Err: %d\n", err); +#endif +} + +void anGenBuffers(GLsizei n, GLuint* buffers) { +#ifdef __EMSCRIPTEN__ + glGenBuffers(n, buffers); +#else + static GLuint k = 1; + for (int i = 0; i < n; ++i) + buffers[i] = ++k; +#endif +} + +#ifdef USE_VBO +void drawArrayVT(int bufferId, int vertices, int vertexSize /* = 24 */, unsigned int mode /* = GL_TRIANGLES */) { + //if (Options::debugGl) LOGI("drawArray\n"); + glBindBuffer2(GL_ARRAY_BUFFER, bufferId); + glTexCoordPointer2(2, GL_FLOAT, vertexSize, (GLvoid*) (3 * 4)); + glEnableClientState2(GL_TEXTURE_COORD_ARRAY); + glVertexPointer2(3, GL_FLOAT, vertexSize, 0); + glEnableClientState2(GL_VERTEX_ARRAY); + glDrawArrays2(mode, 0, vertices); + glDisableClientState2(GL_VERTEX_ARRAY); + glDisableClientState2(GL_TEXTURE_COORD_ARRAY); +} + +#ifndef drawArrayVT_NoState +void drawArrayVT_NoState(int bufferId, int vertices, int vertexSize /* = 24 */) { + //if (Options::debugGl) LOGI("drawArray\n"); + glBindBuffer2(GL_ARRAY_BUFFER, bufferId); + glTexCoordPointer2(2, GL_FLOAT, vertexSize, (GLvoid*) (3 * 4)); + //glEnableClientState2(GL_TEXTURE_COORD_ARRAY); + glVertexPointer2(3, GL_FLOAT, vertexSize, 0); + //glEnableClientState2(GL_VERTEX_ARRAY); + glDrawArrays2(GL_TRIANGLES, 0, vertices); + //glDisableClientState2(GL_VERTEX_ARRAY); + //glDisableClientState2(GL_TEXTURE_COORD_ARRAY); +} +#endif + +void drawArrayVTC(int bufferId, int vertices, int vertexSize /* = 24 */) { + //if (Options::debugGl) LOGI("drawArray\n"); + //LOGI("draw-vtc: %d, %d, %d\n", bufferId, vertices, vertexSize); + glEnableClientState2(GL_VERTEX_ARRAY); + glEnableClientState2(GL_TEXTURE_COORD_ARRAY); + glEnableClientState2(GL_COLOR_ARRAY); + + glBindBuffer2(GL_ARRAY_BUFFER, bufferId); + + glVertexPointer2( 3, GL_FLOAT, vertexSize, 0); + glTexCoordPointer2(2, GL_FLOAT, vertexSize, (GLvoid*) (3 * 4)); + glColorPointer2(4, GL_UNSIGNED_BYTE, vertexSize, (GLvoid*) (5*4)); + + glDrawArrays2(GL_TRIANGLES, 0, vertices); + + glDisableClientState2(GL_VERTEX_ARRAY); + glDisableClientState2(GL_TEXTURE_COORD_ARRAY); + glDisableClientState2(GL_COLOR_ARRAY); +} + +#ifndef drawArrayVTC_NoState +void drawArrayVTC_NoState(int bufferId, int vertices, int vertexSize /* = 24 */) { + glBindBuffer2(GL_ARRAY_BUFFER, bufferId); + + glVertexPointer2( 3, GL_FLOAT, vertexSize, 0); + glTexCoordPointer2(2, GL_FLOAT, vertexSize, (GLvoid*) (3 * 4)); + glColorPointer2(4, GL_UNSIGNED_BYTE, vertexSize, (GLvoid*) (5*4)); + + glDrawArrays2(GL_TRIANGLES, 0, vertices); +} +#endif + +#endif + + +// +// Code borrowed from OpenGL.org +// http://www.opengl.org/wiki/GluProject_and_gluUnProject_code +// The gluUnProject code in Android seems to be broken +// + +void MultiplyMatrices4by4OpenGL_FLOAT(float *result, float *matrix1, float *matrix2) +{ + result[0]=matrix1[0]*matrix2[0]+ + matrix1[4]*matrix2[1]+ + matrix1[8]*matrix2[2]+ + matrix1[12]*matrix2[3]; + result[4]=matrix1[0]*matrix2[4]+ + matrix1[4]*matrix2[5]+ + matrix1[8]*matrix2[6]+ + matrix1[12]*matrix2[7]; + result[8]=matrix1[0]*matrix2[8]+ + matrix1[4]*matrix2[9]+ + matrix1[8]*matrix2[10]+ + matrix1[12]*matrix2[11]; + result[12]=matrix1[0]*matrix2[12]+ + matrix1[4]*matrix2[13]+ + matrix1[8]*matrix2[14]+ + matrix1[12]*matrix2[15]; + result[1]=matrix1[1]*matrix2[0]+ + matrix1[5]*matrix2[1]+ + matrix1[9]*matrix2[2]+ + matrix1[13]*matrix2[3]; + result[5]=matrix1[1]*matrix2[4]+ + matrix1[5]*matrix2[5]+ + matrix1[9]*matrix2[6]+ + matrix1[13]*matrix2[7]; + result[9]=matrix1[1]*matrix2[8]+ + matrix1[5]*matrix2[9]+ + matrix1[9]*matrix2[10]+ + matrix1[13]*matrix2[11]; + result[13]=matrix1[1]*matrix2[12]+ + matrix1[5]*matrix2[13]+ + matrix1[9]*matrix2[14]+ + matrix1[13]*matrix2[15]; + result[2]=matrix1[2]*matrix2[0]+ + matrix1[6]*matrix2[1]+ + matrix1[10]*matrix2[2]+ + matrix1[14]*matrix2[3]; + result[6]=matrix1[2]*matrix2[4]+ + matrix1[6]*matrix2[5]+ + matrix1[10]*matrix2[6]+ + matrix1[14]*matrix2[7]; + result[10]=matrix1[2]*matrix2[8]+ + matrix1[6]*matrix2[9]+ + matrix1[10]*matrix2[10]+ + matrix1[14]*matrix2[11]; + result[14]=matrix1[2]*matrix2[12]+ + matrix1[6]*matrix2[13]+ + matrix1[10]*matrix2[14]+ + matrix1[14]*matrix2[15]; + result[3]=matrix1[3]*matrix2[0]+ + matrix1[7]*matrix2[1]+ + matrix1[11]*matrix2[2]+ + matrix1[15]*matrix2[3]; + result[7]=matrix1[3]*matrix2[4]+ + matrix1[7]*matrix2[5]+ + matrix1[11]*matrix2[6]+ + matrix1[15]*matrix2[7]; + result[11]=matrix1[3]*matrix2[8]+ + matrix1[7]*matrix2[9]+ + matrix1[11]*matrix2[10]+ + matrix1[15]*matrix2[11]; + result[15]=matrix1[3]*matrix2[12]+ + matrix1[7]*matrix2[13]+ + matrix1[11]*matrix2[14]+ + matrix1[15]*matrix2[15]; +} + +void MultiplyMatrixByVector4by4OpenGL_FLOAT(float *resultvector, const float *matrix, const float *pvector) +{ + resultvector[0]=matrix[0]*pvector[0]+matrix[4]*pvector[1]+matrix[8]*pvector[2]+matrix[12]*pvector[3]; + resultvector[1]=matrix[1]*pvector[0]+matrix[5]*pvector[1]+matrix[9]*pvector[2]+matrix[13]*pvector[3]; + resultvector[2]=matrix[2]*pvector[0]+matrix[6]*pvector[1]+matrix[10]*pvector[2]+matrix[14]*pvector[3]; + resultvector[3]=matrix[3]*pvector[0]+matrix[7]*pvector[1]+matrix[11]*pvector[2]+matrix[15]*pvector[3]; +} + +#define SWAP_ROWS_DOUBLE(a, b) { double *_tmp = a; (a)=(b); (b)=_tmp; } +#define SWAP_ROWS_FLOAT(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; } +#define MAT(m,r,c) (m)[(c)*4+(r)] + +//This code comes directly from GLU except that it is for float +int glhInvertMatrixf2(float *m, float *out) +{ + float wtmp[4][8]; + float m0, m1, m2, m3, s; + float *r0, *r1, *r2, *r3; + r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; + r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1), + r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3), + r0[4] = 1.0f, r0[5] = r0[6] = r0[7] = 0.0f, + r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1), + r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3), + r1[5] = 1.0f, r1[4] = r1[6] = r1[7] = 0.0f, + r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1), + r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3), + r2[6] = 1.0f, r2[4] = r2[5] = r2[7] = 0.0f, + r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1), + r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3), + r3[7] = 1.0f, r3[4] = r3[5] = r3[6] = 0.0f; + /* choose pivot - or die */ + if (fabsf(r3[0]) > fabsf(r2[0])) + SWAP_ROWS_FLOAT(r3, r2); + if (fabsf(r2[0]) > fabsf(r1[0])) + SWAP_ROWS_FLOAT(r2, r1); + if (fabsf(r1[0]) > fabsf(r0[0])) + SWAP_ROWS_FLOAT(r1, r0); + if (0.0f == r0[0]) + return 0; + /* eliminate first variable */ + m1 = r1[0] / r0[0]; + m2 = r2[0] / r0[0]; + m3 = r3[0] / r0[0]; + s = r0[1]; + r1[1] -= m1 * s; + r2[1] -= m2 * s; + r3[1] -= m3 * s; + s = r0[2]; + r1[2] -= m1 * s; + r2[2] -= m2 * s; + r3[2] -= m3 * s; + s = r0[3]; + r1[3] -= m1 * s; + r2[3] -= m2 * s; + r3[3] -= m3 * s; + s = r0[4]; + if (s != 0.0f) { + r1[4] -= m1 * s; + r2[4] -= m2 * s; + r3[4] -= m3 * s; + } + s = r0[5]; + if (s != 0.0f) { + r1[5] -= m1 * s; + r2[5] -= m2 * s; + r3[5] -= m3 * s; + } + s = r0[6]; + if (s != 0.0f) { + r1[6] -= m1 * s; + r2[6] -= m2 * s; + r3[6] -= m3 * s; + } + s = r0[7]; + if (s != 0.0f) { + r1[7] -= m1 * s; + r2[7] -= m2 * s; + r3[7] -= m3 * s; + } + /* choose pivot - or die */ + if (fabsf(r3[1]) > fabsf(r2[1])) + SWAP_ROWS_FLOAT(r3, r2); + if (fabsf(r2[1]) > fabsf(r1[1])) + SWAP_ROWS_FLOAT(r2, r1); + if (0.0f == r1[1]) + return 0; + /* eliminate second variable */ + m2 = r2[1] / r1[1]; + m3 = r3[1] / r1[1]; + r2[2] -= m2 * r1[2]; + r3[2] -= m3 * r1[2]; + r2[3] -= m2 * r1[3]; + r3[3] -= m3 * r1[3]; + s = r1[4]; + if (0.0f != s) { + r2[4] -= m2 * s; + r3[4] -= m3 * s; + } + s = r1[5]; + if (0.0f != s) { + r2[5] -= m2 * s; + r3[5] -= m3 * s; + } + s = r1[6]; + if (0.0f != s) { + r2[6] -= m2 * s; + r3[6] -= m3 * s; + } + s = r1[7]; + if (0.0f != s) { + r2[7] -= m2 * s; + r3[7] -= m3 * s; + } + /* choose pivot - or die */ + if (fabsf(r3[2]) > fabsf(r2[2])) + SWAP_ROWS_FLOAT(r3, r2); + if (0.0f == r2[2]) + return 0; + /* eliminate third variable */ + m3 = r3[2] / r2[2]; + r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], + r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7]; + /* last check */ + if (0.0f == r3[3]) + return 0; + s = 1.0f / r3[3]; /* now back substitute row 3 */ + r3[4] *= s; + r3[5] *= s; + r3[6] *= s; + r3[7] *= s; + m2 = r2[3]; /* now back substitute row 2 */ + s = 1.0f / r2[2]; + r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), + r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); + m1 = r1[3]; + r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1, + r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1; + m0 = r0[3]; + r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, + r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; + m1 = r1[2]; /* now back substitute row 1 */ + s = 1.0f / r1[1]; + r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), + r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); + m0 = r0[2]; + r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, + r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; + m0 = r0[1]; /* now back substitute row 0 */ + s = 1.0f / r0[0]; + r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), + r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); + MAT(out, 0, 0) = r0[4]; + MAT(out, 0, 1) = r0[5], MAT(out, 0, 2) = r0[6]; + MAT(out, 0, 3) = r0[7], MAT(out, 1, 0) = r1[4]; + MAT(out, 1, 1) = r1[5], MAT(out, 1, 2) = r1[6]; + MAT(out, 1, 3) = r1[7], MAT(out, 2, 0) = r2[4]; + MAT(out, 2, 1) = r2[5], MAT(out, 2, 2) = r2[6]; + MAT(out, 2, 3) = r2[7], MAT(out, 3, 0) = r3[4]; + MAT(out, 3, 1) = r3[5], MAT(out, 3, 2) = r3[6]; + MAT(out, 3, 3) = r3[7]; + return 1; +} + +int glhUnProjectf( float winx, float winy, float winz, + float *modelview, float *projection, + int *viewport, float *objectCoordinate) +{ + //Transformation matrices + float m[16], A[16]; + float in[4], out[4]; + //Calculation for inverting a matrix, compute projection x modelview + //and store in A[16] + MultiplyMatrices4by4OpenGL_FLOAT(A, projection, modelview); + //Now compute the inverse of matrix A + if(glhInvertMatrixf2(A, m)==0) + return 0; + //Transformation of normalized coordinates between -1 and 1 + in[0]=(winx-(float)viewport[0])/(float)viewport[2]*2.0f-1.0f; + in[1]=(winy-(float)viewport[1])/(float)viewport[3]*2.0f-1.0f; + in[2]=2.0f*winz-1.0f; + in[3]=1.0f; + //Objects coordinates + MultiplyMatrixByVector4by4OpenGL_FLOAT(out, m, in); + if(out[3]==0.0f) + return 0; + out[3]=1.0f/out[3]; + objectCoordinate[0]=out[0]*out[3]; + objectCoordinate[1]=out[1]*out[3]; + objectCoordinate[2]=out[2]*out[3]; + return 1; +} diff --git a/src/client/renderer/gles.h b/src/client/renderer/gles.h index 9a74c07..9ac3c10 100755 --- a/src/client/renderer/gles.h +++ b/src/client/renderer/gles.h @@ -1,148 +1,148 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER__gles_H__ -#define NET_MINECRAFT_CLIENT_RENDERER__gles_H__ - -#include "../../platform/log.h" -#include "../Options.h" - -// Android should always run OPENGL_ES -#if defined(ANDROID) || defined(__APPLE__) || defined(RPI) - #define OPENGL_ES -#endif - -// Other systems might run it, if they #define OPENGL_ES -// #if defined(OPENGL_ES) // || defined(ANDROID) - #define USE_VBO - #define GL_QUADS 0x0007 - #if defined(__APPLE__) - #import - #import - #elif defined(ANDROID) || defined(__EMSCRIPTEN__) - #import - #import - #else - #include - - // https://github.com/programmer1o1/MinecraftPE-v0.6.1/blob/main/handheld/src/client/renderer/gles.h#L135-L138 - #define glFogx(a,b) glFogi(a,b) - #define glOrthof(a,b,c,d,e,f) glOrtho(a,b,c,d,e,f) - #define glClearDepthf(x) glClearDepth(x) - #define glDepthRangef(a,b) glDepthRange(a,b) - #endif -// #else -// // Uglyness to fix redeclaration issues -// #ifdef WIN32 -// #include -// #include -// #endif -// #include -// #include - -// #define glFogx(a,b) glFogi(a,b) -// #define glOrthof(a,b,c,d,e,f) glOrtho(a,b,c,d,e,f) -// #endif - - -#define GLERRDEBUG 1 -#if GLERRDEBUG -//#define GLERR(x) if((x) != 0) { LOGI("GLError: " #x "(%d)\n", __LINE__) } -#define GLERR(x) do { const int errCode = glGetError(); if (errCode != 0) LOGE("OpenGL ERROR @%d: #%d @ (%s : %d)\n", x, errCode, __FILE__, __LINE__); } while (0) -#else -#define GLERR(x) x -#endif - -void anGenBuffers(GLsizei n, GLuint* buffer); - -#ifdef USE_VBO -#define drawArrayVT_NoState drawArrayVT -#define drawArrayVTC_NoState drawArrayVTC -void drawArrayVT(int bufferId, int vertices, int vertexSize = 24, unsigned int mode = GL_TRIANGLES); -#ifndef drawArrayVT_NoState -//void drawArrayVT_NoState(int bufferId, int vertices, int vertexSize = 24); -#endif -void drawArrayVTC(int bufferId, int vertices, int vertexSize = 24); -#ifndef drawArrayVTC_NoState -void drawArrayVTC_NoState(int bufferId, int vertices, int vertexSize = 24); -#endif -#endif - -void glInit(); -void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar); -int glhUnProjectf( float winx, float winy, float winz, - float *modelview, float *projection, - int *viewport, float *objectCoordinate); - -// Used for "debugging" (...). Obviously stupid dependency on Options (and ugly gl*2 calls). -#ifdef GLDEBUG - #define glTranslatef2(x, y, z) do{ if (Options::debugGl) LOGI("glTrans @ %s:%d: %f,%f,%f\n", __FILE__, __LINE__, x, y, z); glTranslatef(x, y, z); GLERR(0); } while(0) - #define glRotatef2(a, x, y, z) do{ if (Options::debugGl) LOGI("glRotat @ %s:%d: %f,%f,%f,%f\n", __FILE__, __LINE__, a, x, y, z); glRotatef(a, x, y, z); GLERR(1); } while(0) - #define glScalef2(x, y, z) do{ if (Options::debugGl) LOGI("glScale @ %s:%d: %f,%f,%f\n", __FILE__, __LINE__, x, y, z); glScalef(x, y, z); GLERR(2); } while(0) - #define glPushMatrix2() do{ if (Options::debugGl) LOGI("glPushM @ %s:%d\n", __FILE__, __LINE__); glPushMatrix(); GLERR(3); } while(0) - #define glPopMatrix2() do{ if (Options::debugGl) LOGI("glPopM @ %s:%d\n", __FILE__, __LINE__); glPopMatrix(); GLERR(4); } while(0) - #define glLoadIdentity2() do{ if (Options::debugGl) LOGI("glLoadI @ %s:%d\n", __FILE__, __LINE__); glLoadIdentity(); GLERR(5); } while(0) - - #define glVertexPointer2(a, b, c, d) do{ if (Options::debugGl) LOGI("glVertexPtr @ %s:%d : %d\n", __FILE__, __LINE__, 0); glVertexPointer(a, b, c, d); GLERR(6); } while(0) - #define glColorPointer2(a, b, c, d) do{ if (Options::debugGl) LOGI("glColorPtr @ %s:%d : %d\n", __FILE__, __LINE__, 0); glColorPointer(a, b, c, d); GLERR(7); } while(0) - #define glTexCoordPointer2(a, b, c, d) do{ if (Options::debugGl) LOGI("glTexPtr @ %s:%d : %d\n", __FILE__, __LINE__, 0); glTexCoordPointer(a, b, c, d); GLERR(8); } while(0) - #define glEnableClientState2(s) do{ if (Options::debugGl) LOGI("glEnableClient @ %s:%d : %d\n", __FILE__, __LINE__, 0); glEnableClientState(s); GLERR(9); } while(0) - #define glDisableClientState2(s) do{ if (Options::debugGl) LOGI("glDisableClient @ %s:%d : %d\n", __FILE__, __LINE__, 0); glDisableClientState(s); GLERR(10); } while(0) - #define glDrawArrays2(m, o, v) do{ if (Options::debugGl) LOGI("glDrawA @ %s:%d : %d\n", __FILE__, __LINE__, 0); glDrawArrays(m,o,v); GLERR(11); } while(0) - - #define glTexParameteri2(m, o, v) do{ if (Options::debugGl) LOGI("glTexParameteri @ %s:%d : %d\n", __FILE__, __LINE__, v); glTexParameteri(m,o,v); GLERR(12); } while(0) - #define glTexImage2D2(a,b,c,d,e,f,g,height,i) do{ if (Options::debugGl) LOGI("glTexImage2D @ %s:%d : %d\n", __FILE__, __LINE__, 0); glTexImage2D(a,b,c,d,e,f,g,height,i); GLERR(13); } while(0) - #define glTexSubImage2D2(a,b,c,d,e,f,g,height,i) do{ if (Options::debugGl) LOGI("glTexSubImage2D @ %s:%d : %d\n", __FILE__, __LINE__, 0); glTexSubImage2D(a,b,c,d,e,f,g,height,i); GLERR(14); } while(0) - #define glGenBuffers2(s, id) do{ if (Options::debugGl) LOGI("glGenBuffers @ %s:%d : %d\n", __FILE__, __LINE__, id); anGenBuffers(s, id); GLERR(15); } while(0) - #define glBindBuffer2(s, id) do{ if (Options::debugGl) LOGI("glBindBuffer @ %s:%d : %d\n", __FILE__, __LINE__, id); glBindBuffer(s, id); GLERR(16); } while(0) - #define glBufferData2(a, b, c, d) do{ if (Options::debugGl) LOGI("glBufferData @ %s:%d : %d\n", __FILE__, __LINE__, d); glBufferData(a, b, c, d); GLERR(17); } while(0) - #define glBindTexture2(m, z) do{ if (Options::debugGl) LOGI("glBindTexture @ %s:%d : %d\n", __FILE__, __LINE__, z); glBindTexture(m, z); GLERR(18); } while(0) - - #define glEnable2(s) do{ if (Options::debugGl) LOGI("glEnable @ %s:%d : %d\n", __FILE__, __LINE__, s); glEnable(s); GLERR(19); } while(0) - #define glDisable2(s) do{ if (Options::debugGl) LOGI("glDisable @ %s:%d : %d\n", __FILE__, __LINE__, s); glDisable(s); GLERR(20); } while(0) - - #define glColor4f2(r, g, b, a) do{ if (Options::debugGl) LOGI("glColor4f2 @ %s:%d : (%f,%f,%f,%f)\n", __FILE__, __LINE__, r,g,b,a); glColor4f(r,g,b,a); GLERR(21); } while(0) - - //#define glBlendMode2(s) do{ if (Options::debugGl) LOGI("glEnable @ %s:%d : %d\n", __FILE__, __LINE__, s); glEnable(s); GLERR(19); } while(0) - #define glBlendFunc2(src, dst) do{ if (Options::debugGl) LOGI("glBlendFunc @ %s:%d : %d - %d\n", __FILE__, __LINE__, src, dst); glBlendFunc(src, dst); GLERR(23); } while(0) - #define glShadeModel2(s) do{ if (Options::debugGl) LOGI("glShadeModel @ %s:%d : %d\n", __FILE__, __LINE__, s); glShadeModel(s); GLERR(25); } while(0) -#else - #define glTranslatef2 glTranslatef - #define glRotatef2 glRotatef - #define glScalef2 glScalef - #define glPushMatrix2 glPushMatrix - #define glPopMatrix2 glPopMatrix - #define glLoadIdentity2 glLoadIdentity - - #define glVertexPointer2 glVertexPointer - #define glColorPointer2 glColorPointer - #define glTexCoordPointer2 glTexCoordPointer - #define glEnableClientState2 glEnableClientState - #define glDisableClientState2 glDisableClientState - #define glDrawArrays2 glDrawArrays - - #define glTexParameteri2 glTexParameteri - #define glTexImage2D2 glTexImage2D - #define glTexSubImage2D2 glTexSubImage2D - #define glGenBuffers2 anGenBuffers - #define glBindBuffer2 glBindBuffer - #define glBufferData2 glBufferData - #define glBindTexture2 glBindTexture - - #define glEnable2 glEnable - #define glDisable2 glDisable - - #define glColor4f2 glColor4f - #define glBlendFunc2 glBlendFunc - #define glShadeModel2 glShadeModel -#endif - -// -// Extensions -// -#ifdef WIN32 - #define glGetProcAddress(a) wglGetProcAddress(a) -#else - #define glGetProcAddress(a) (void*(0)) -#endif - - - -#endif /*NET_MINECRAFT_CLIENT_RENDERER__gles_H__ */ +#ifndef NET_MINECRAFT_CLIENT_RENDERER__gles_H__ +#define NET_MINECRAFT_CLIENT_RENDERER__gles_H__ + +#include "../../platform/log.h" +#include "../Options.h" + +// Android should always run OPENGL_ES +#if defined(ANDROID) || defined(__APPLE__) || defined(RPI) + #define OPENGL_ES +#endif + +// Other systems might run it, if they #define OPENGL_ES +// #if defined(OPENGL_ES) // || defined(ANDROID) + #define USE_VBO + #define GL_QUADS 0x0007 + #if defined(__APPLE__) + #import + #import + #elif defined(ANDROID) || defined(__EMSCRIPTEN__) + #import + #import + #else + #include + + // https://github.com/programmer1o1/MinecraftPE-v0.6.1/blob/main/handheld/src/client/renderer/gles.h#L135-L138 + #define glFogx(a,b) glFogi(a,b) + #define glOrthof(a,b,c,d,e,f) glOrtho(a,b,c,d,e,f) + #define glClearDepthf(x) glClearDepth(x) + #define glDepthRangef(a,b) glDepthRange(a,b) + #endif +// #else +// // Uglyness to fix redeclaration issues +// #ifdef WIN32 +// #include +// #include +// #endif +// #include +// #include + +// #define glFogx(a,b) glFogi(a,b) +// #define glOrthof(a,b,c,d,e,f) glOrtho(a,b,c,d,e,f) +// #endif + + +#define GLERRDEBUG 1 +#if GLERRDEBUG +//#define GLERR(x) if((x) != 0) { LOGI("GLError: " #x "(%d)\n", __LINE__) } +#define GLERR(x) do { const int errCode = glGetError(); if (errCode != 0) LOGE("OpenGL ERROR @%d: #%d @ (%s : %d)\n", x, errCode, __FILE__, __LINE__); } while (0) +#else +#define GLERR(x) x +#endif + +void anGenBuffers(GLsizei n, GLuint* buffer); + +#ifdef USE_VBO +#define drawArrayVT_NoState drawArrayVT +#define drawArrayVTC_NoState drawArrayVTC +void drawArrayVT(int bufferId, int vertices, int vertexSize = 24, unsigned int mode = GL_TRIANGLES); +#ifndef drawArrayVT_NoState +//void drawArrayVT_NoState(int bufferId, int vertices, int vertexSize = 24); +#endif +void drawArrayVTC(int bufferId, int vertices, int vertexSize = 24); +#ifndef drawArrayVTC_NoState +void drawArrayVTC_NoState(int bufferId, int vertices, int vertexSize = 24); +#endif +#endif + +void glInit(); +void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar); +int glhUnProjectf( float winx, float winy, float winz, + float *modelview, float *projection, + int *viewport, float *objectCoordinate); + +// Used for "debugging" (...). Obviously stupid dependency on Options (and ugly gl*2 calls). +#ifdef GLDEBUG + #define glTranslatef2(x, y, z) do{ if (Options::debugGl) LOGI("glTrans @ %s:%d: %f,%f,%f\n", __FILE__, __LINE__, x, y, z); glTranslatef(x, y, z); GLERR(0); } while(0) + #define glRotatef2(a, x, y, z) do{ if (Options::debugGl) LOGI("glRotat @ %s:%d: %f,%f,%f,%f\n", __FILE__, __LINE__, a, x, y, z); glRotatef(a, x, y, z); GLERR(1); } while(0) + #define glScalef2(x, y, z) do{ if (Options::debugGl) LOGI("glScale @ %s:%d: %f,%f,%f\n", __FILE__, __LINE__, x, y, z); glScalef(x, y, z); GLERR(2); } while(0) + #define glPushMatrix2() do{ if (Options::debugGl) LOGI("glPushM @ %s:%d\n", __FILE__, __LINE__); glPushMatrix(); GLERR(3); } while(0) + #define glPopMatrix2() do{ if (Options::debugGl) LOGI("glPopM @ %s:%d\n", __FILE__, __LINE__); glPopMatrix(); GLERR(4); } while(0) + #define glLoadIdentity2() do{ if (Options::debugGl) LOGI("glLoadI @ %s:%d\n", __FILE__, __LINE__); glLoadIdentity(); GLERR(5); } while(0) + + #define glVertexPointer2(a, b, c, d) do{ if (Options::debugGl) LOGI("glVertexPtr @ %s:%d : %d\n", __FILE__, __LINE__, 0); glVertexPointer(a, b, c, d); GLERR(6); } while(0) + #define glColorPointer2(a, b, c, d) do{ if (Options::debugGl) LOGI("glColorPtr @ %s:%d : %d\n", __FILE__, __LINE__, 0); glColorPointer(a, b, c, d); GLERR(7); } while(0) + #define glTexCoordPointer2(a, b, c, d) do{ if (Options::debugGl) LOGI("glTexPtr @ %s:%d : %d\n", __FILE__, __LINE__, 0); glTexCoordPointer(a, b, c, d); GLERR(8); } while(0) + #define glEnableClientState2(s) do{ if (Options::debugGl) LOGI("glEnableClient @ %s:%d : %d\n", __FILE__, __LINE__, 0); glEnableClientState(s); GLERR(9); } while(0) + #define glDisableClientState2(s) do{ if (Options::debugGl) LOGI("glDisableClient @ %s:%d : %d\n", __FILE__, __LINE__, 0); glDisableClientState(s); GLERR(10); } while(0) + #define glDrawArrays2(m, o, v) do{ if (Options::debugGl) LOGI("glDrawA @ %s:%d : %d\n", __FILE__, __LINE__, 0); glDrawArrays(m,o,v); GLERR(11); } while(0) + + #define glTexParameteri2(m, o, v) do{ if (Options::debugGl) LOGI("glTexParameteri @ %s:%d : %d\n", __FILE__, __LINE__, v); glTexParameteri(m,o,v); GLERR(12); } while(0) + #define glTexImage2D2(a,b,c,d,e,f,g,height,i) do{ if (Options::debugGl) LOGI("glTexImage2D @ %s:%d : %d\n", __FILE__, __LINE__, 0); glTexImage2D(a,b,c,d,e,f,g,height,i); GLERR(13); } while(0) + #define glTexSubImage2D2(a,b,c,d,e,f,g,height,i) do{ if (Options::debugGl) LOGI("glTexSubImage2D @ %s:%d : %d\n", __FILE__, __LINE__, 0); glTexSubImage2D(a,b,c,d,e,f,g,height,i); GLERR(14); } while(0) + #define glGenBuffers2(s, id) do{ if (Options::debugGl) LOGI("glGenBuffers @ %s:%d : %d\n", __FILE__, __LINE__, id); anGenBuffers(s, id); GLERR(15); } while(0) + #define glBindBuffer2(s, id) do{ if (Options::debugGl) LOGI("glBindBuffer @ %s:%d : %d\n", __FILE__, __LINE__, id); glBindBuffer(s, id); GLERR(16); } while(0) + #define glBufferData2(a, b, c, d) do{ if (Options::debugGl) LOGI("glBufferData @ %s:%d : %d\n", __FILE__, __LINE__, d); glBufferData(a, b, c, d); GLERR(17); } while(0) + #define glBindTexture2(m, z) do{ if (Options::debugGl) LOGI("glBindTexture @ %s:%d : %d\n", __FILE__, __LINE__, z); glBindTexture(m, z); GLERR(18); } while(0) + + #define glEnable2(s) do{ if (Options::debugGl) LOGI("glEnable @ %s:%d : %d\n", __FILE__, __LINE__, s); glEnable(s); GLERR(19); } while(0) + #define glDisable2(s) do{ if (Options::debugGl) LOGI("glDisable @ %s:%d : %d\n", __FILE__, __LINE__, s); glDisable(s); GLERR(20); } while(0) + + #define glColor4f2(r, g, b, a) do{ if (Options::debugGl) LOGI("glColor4f2 @ %s:%d : (%f,%f,%f,%f)\n", __FILE__, __LINE__, r,g,b,a); glColor4f(r,g,b,a); GLERR(21); } while(0) + + //#define glBlendMode2(s) do{ if (Options::debugGl) LOGI("glEnable @ %s:%d : %d\n", __FILE__, __LINE__, s); glEnable(s); GLERR(19); } while(0) + #define glBlendFunc2(src, dst) do{ if (Options::debugGl) LOGI("glBlendFunc @ %s:%d : %d - %d\n", __FILE__, __LINE__, src, dst); glBlendFunc(src, dst); GLERR(23); } while(0) + #define glShadeModel2(s) do{ if (Options::debugGl) LOGI("glShadeModel @ %s:%d : %d\n", __FILE__, __LINE__, s); glShadeModel(s); GLERR(25); } while(0) +#else + #define glTranslatef2 glTranslatef + #define glRotatef2 glRotatef + #define glScalef2 glScalef + #define glPushMatrix2 glPushMatrix + #define glPopMatrix2 glPopMatrix + #define glLoadIdentity2 glLoadIdentity + + #define glVertexPointer2 glVertexPointer + #define glColorPointer2 glColorPointer + #define glTexCoordPointer2 glTexCoordPointer + #define glEnableClientState2 glEnableClientState + #define glDisableClientState2 glDisableClientState + #define glDrawArrays2 glDrawArrays + + #define glTexParameteri2 glTexParameteri + #define glTexImage2D2 glTexImage2D + #define glTexSubImage2D2 glTexSubImage2D + #define glGenBuffers2 anGenBuffers + #define glBindBuffer2 glBindBuffer + #define glBufferData2 glBufferData + #define glBindTexture2 glBindTexture + + #define glEnable2 glEnable + #define glDisable2 glDisable + + #define glColor4f2 glColor4f + #define glBlendFunc2 glBlendFunc + #define glShadeModel2 glShadeModel +#endif + +// +// Extensions +// +#ifdef WIN32 + #define glGetProcAddress(a) wglGetProcAddress(a) +#else + #define glGetProcAddress(a) (void*(0)) +#endif + + + +#endif /*NET_MINECRAFT_CLIENT_RENDERER__gles_H__ */ diff --git a/src/client/renderer/ptexture/DynamicTexture.cpp b/src/client/renderer/ptexture/DynamicTexture.cpp index e3b7b47..a9b7464 100755 --- a/src/client/renderer/ptexture/DynamicTexture.cpp +++ b/src/client/renderer/ptexture/DynamicTexture.cpp @@ -1,208 +1,208 @@ -#include "DynamicTexture.h" - -#include -#include "../Textures.h" -#include "../../../world/level/tile/Tile.h" -#include "../../../util/Mth.h" - -// -// DynamicTexture -// -DynamicTexture::DynamicTexture(int tex_) -: tex(tex_), - replicate(1) -{ - memset(pixels, 0, 16*16*4); -} - -void DynamicTexture::bindTexture(Textures* tex) { - tex->loadAndBindTexture("terrain.png"); -} - -// -// WaterTexture -// I was thinking of adding something simple (a simple frame copy from a -// "still water image sequence") every n:th tick for calm water, and shifting -// the rows of a texture for the running water. I might do that, but I got -// impressed over the java code, so I will try that first.. and I suspect they -// wont mix very good. -/* -WaterTexture::WaterTexture() -: super(Tile::water->tex), - _tick(0), - _frame(0) -{ -} - -void WaterTexture::tick() { -} -*/ - -WaterTexture::WaterTexture() -: super(Tile::water->tex), - _tick(0), - _frame(0) -{ - current = new float[16*16]; - next = new float[16*16]; - heat = new float[16*16]; - heata = new float[16*16]; - - for (int i = 0; i < 256; ++i) { - current[i] = 0; - next[i] = 0; - heat[i] = 0; - heata[i] = 0; - } -} - -WaterTexture::~WaterTexture() { - delete[] current; - delete[] next; - delete[] heat; - delete[] heata; -} - -void WaterTexture::tick() -{ - for (int x = 0; x < 16; x++) - for (int y = 0; y < 16; y++) { - float pow = 0; - for (int xx = x - 1; xx <= x + 1; xx++) { - int xi = (xx) & 15; - int yi = (y) & 15; - pow += current[xi + yi * 16]; - } - next[x + y * 16] = pow / 3.3f + heat[x + y * 16] * 0.8f; - } - - for (int x = 0; x < 16; x++) - for (int y = 0; y < 16; y++) { - heat[x + y * 16] += heata[x + y * 16] * 0.05f; - - if (heat[x + y * 16] < 0) heat[x + y * 16] = 0; - heata[x + y * 16] -= 0.1f; - if (Mth::random() < 0.05f) { - heata[x + y * 16] = 0.5f; - } - } - - float* tmp = next; - next = current; - current = tmp; - - for (int i = 0; i < 256; i++) { - float pow = current[i]; - if (pow > 1) pow = 1; - if (pow < 0) pow = 0; - - float pp = pow * pow; - - int r = (int) (32 + pp * 32); - int g = (int) (50 + pp * 64); - int b = (int) (255); - int a = (int) (146 + pp * 50); - - //if (anaglyph3d) { - // int rr = (r * 30 + g * 59 + b * 11) / 100; - // int gg = (r * 30 + g * 70) / (100); - // int bb = (r * 30 + b * 70) / (100); - - // r = rr; - // g = gg; - // b = bb; - //} - - pixels[i * 4 + 0] = r; - pixels[i * 4 + 1] = g; - pixels[i * 4 + 2] = b; - pixels[i * 4 + 3] = a; - } -} - -// -// WaterSideTexture -// -WaterSideTexture::WaterSideTexture() -: super(Tile::water->tex + 1), - _tick(0), - _frame(0), - _tickCount(0) -{ - replicate = 2; - - current = new float[16*16]; - next = new float[16*16]; - heat = new float[16*16]; - heata = new float[16*16]; - - for (int i = 0; i < 256; ++i) { - current[i] = 0; - next[i] = 0; - heat[i] = 0; - heata[i] = 0; - } -} - -WaterSideTexture::~WaterSideTexture() { - delete[] current; - delete[] next; - delete[] heat; - delete[] heata; -} - -void WaterSideTexture::tick() { - ++_tickCount; - for (int x = 0; x < 16; x++) - for (int y = 0; y < 16; y++) { - float pow = 0; - for (int xx = y - 2; xx <= y; xx++) { - int xi = (x) & 15; - int yi = (xx) & 15; - pow += current[xi + yi * 16]; - } - next[x + y * 16] = pow / 3.2f + heat[x + y * 16] * 0.8f; - } - - for (int x = 0; x < 16; x++) - for (int y = 0; y < 16; y++) { - heat[x + y * 16] += heata[x + y * 16] * 0.05f; - - if (heat[x + y * 16] < 0) heat[x + y * 16] = 0; - heata[x + y * 16] -= 0.3f; - if (Mth::random() < 0.2) { - heata[x + y * 16] = 0.5f; - } - } - float* tmp = next; - next = current; - current = tmp; - - for (int i = 0; i < 256; i++) { - float pow = current[(i - _tickCount * 16) & 255]; - if (pow > 1) pow = 1; - if (pow < 0) pow = 0; - - float pp = pow * pow; - - int r = (int) (32 + pp * 32); - int g = (int) (50 + pp * 64); - int b = (int) (255); - int a = (int) (146 + pp * 50); - - //if (anaglyph3d) { - // int rr = (r * 30 + g * 59 + b * 11) / 100; - // int gg = (r * 30 + g * 70) / (100); - // int bb = (r * 30 + b * 70) / (100); - - // r = rr; - // g = gg; - // b = bb; - //} - - pixels[i * 4 + 0] = r; - pixels[i * 4 + 1] = g; - pixels[i * 4 + 2] = b; - pixels[i * 4 + 3] = a; - } -} +#include "DynamicTexture.h" + +#include +#include "../Textures.h" +#include "../../../world/level/tile/Tile.h" +#include "../../../util/Mth.h" + +// +// DynamicTexture +// +DynamicTexture::DynamicTexture(int tex_) +: tex(tex_), + replicate(1) +{ + memset(pixels, 0, 16*16*4); +} + +void DynamicTexture::bindTexture(Textures* tex) { + tex->loadAndBindTexture("terrain.png"); +} + +// +// WaterTexture +// I was thinking of adding something simple (a simple frame copy from a +// "still water image sequence") every n:th tick for calm water, and shifting +// the rows of a texture for the running water. I might do that, but I got +// impressed over the java code, so I will try that first.. and I suspect they +// wont mix very good. +/* +WaterTexture::WaterTexture() +: super(Tile::water->tex), + _tick(0), + _frame(0) +{ +} + +void WaterTexture::tick() { +} +*/ + +WaterTexture::WaterTexture() +: super(Tile::water->tex), + _tick(0), + _frame(0) +{ + current = new float[16*16]; + next = new float[16*16]; + heat = new float[16*16]; + heata = new float[16*16]; + + for (int i = 0; i < 256; ++i) { + current[i] = 0; + next[i] = 0; + heat[i] = 0; + heata[i] = 0; + } +} + +WaterTexture::~WaterTexture() { + delete[] current; + delete[] next; + delete[] heat; + delete[] heata; +} + +void WaterTexture::tick() +{ + for (int x = 0; x < 16; x++) + for (int y = 0; y < 16; y++) { + float pow = 0; + for (int xx = x - 1; xx <= x + 1; xx++) { + int xi = (xx) & 15; + int yi = (y) & 15; + pow += current[xi + yi * 16]; + } + next[x + y * 16] = pow / 3.3f + heat[x + y * 16] * 0.8f; + } + + for (int x = 0; x < 16; x++) + for (int y = 0; y < 16; y++) { + heat[x + y * 16] += heata[x + y * 16] * 0.05f; + + if (heat[x + y * 16] < 0) heat[x + y * 16] = 0; + heata[x + y * 16] -= 0.1f; + if (Mth::random() < 0.05f) { + heata[x + y * 16] = 0.5f; + } + } + + float* tmp = next; + next = current; + current = tmp; + + for (int i = 0; i < 256; i++) { + float pow = current[i]; + if (pow > 1) pow = 1; + if (pow < 0) pow = 0; + + float pp = pow * pow; + + int r = (int) (32 + pp * 32); + int g = (int) (50 + pp * 64); + int b = (int) (255); + int a = (int) (146 + pp * 50); + + //if (anaglyph3d) { + // int rr = (r * 30 + g * 59 + b * 11) / 100; + // int gg = (r * 30 + g * 70) / (100); + // int bb = (r * 30 + b * 70) / (100); + + // r = rr; + // g = gg; + // b = bb; + //} + + pixels[i * 4 + 0] = r; + pixels[i * 4 + 1] = g; + pixels[i * 4 + 2] = b; + pixels[i * 4 + 3] = a; + } +} + +// +// WaterSideTexture +// +WaterSideTexture::WaterSideTexture() +: super(Tile::water->tex + 1), + _tick(0), + _frame(0), + _tickCount(0) +{ + replicate = 2; + + current = new float[16*16]; + next = new float[16*16]; + heat = new float[16*16]; + heata = new float[16*16]; + + for (int i = 0; i < 256; ++i) { + current[i] = 0; + next[i] = 0; + heat[i] = 0; + heata[i] = 0; + } +} + +WaterSideTexture::~WaterSideTexture() { + delete[] current; + delete[] next; + delete[] heat; + delete[] heata; +} + +void WaterSideTexture::tick() { + ++_tickCount; + for (int x = 0; x < 16; x++) + for (int y = 0; y < 16; y++) { + float pow = 0; + for (int xx = y - 2; xx <= y; xx++) { + int xi = (x) & 15; + int yi = (xx) & 15; + pow += current[xi + yi * 16]; + } + next[x + y * 16] = pow / 3.2f + heat[x + y * 16] * 0.8f; + } + + for (int x = 0; x < 16; x++) + for (int y = 0; y < 16; y++) { + heat[x + y * 16] += heata[x + y * 16] * 0.05f; + + if (heat[x + y * 16] < 0) heat[x + y * 16] = 0; + heata[x + y * 16] -= 0.3f; + if (Mth::random() < 0.2) { + heata[x + y * 16] = 0.5f; + } + } + float* tmp = next; + next = current; + current = tmp; + + for (int i = 0; i < 256; i++) { + float pow = current[(i - _tickCount * 16) & 255]; + if (pow > 1) pow = 1; + if (pow < 0) pow = 0; + + float pp = pow * pow; + + int r = (int) (32 + pp * 32); + int g = (int) (50 + pp * 64); + int b = (int) (255); + int a = (int) (146 + pp * 50); + + //if (anaglyph3d) { + // int rr = (r * 30 + g * 59 + b * 11) / 100; + // int gg = (r * 30 + g * 70) / (100); + // int bb = (r * 30 + b * 70) / (100); + + // r = rr; + // g = gg; + // b = bb; + //} + + pixels[i * 4 + 0] = r; + pixels[i * 4 + 1] = g; + pixels[i * 4 + 2] = b; + pixels[i * 4 + 3] = a; + } +} diff --git a/src/client/renderer/ptexture/DynamicTexture.h b/src/client/renderer/ptexture/DynamicTexture.h index c95b3d7..d36b4e8 100755 --- a/src/client/renderer/ptexture/DynamicTexture.h +++ b/src/client/renderer/ptexture/DynamicTexture.h @@ -1,59 +1,59 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_PTEXTURE__DynamicTexture_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_PTEXTURE__DynamicTexture_H__ - -#include - -class Textures; - -class DynamicTexture -{ -public: - int tex; - int replicate; - unsigned char pixels[16*16*4]; - - DynamicTexture(int tex_); - virtual ~DynamicTexture() {} - - virtual void tick() = 0; - virtual void bindTexture(Textures* tex); -}; - -class WaterTexture: public DynamicTexture -{ - typedef DynamicTexture super; - int _tick; - int _frame; - - float* current; - float* next; - float* heat; - float* heata; - -public: - WaterTexture(); - ~WaterTexture(); - - void tick(); -}; - -class WaterSideTexture: public DynamicTexture -{ - typedef DynamicTexture super; - int _tick; - int _frame; - int _tickCount; - - float* current; - float* next; - float* heat; - float* heata; - -public: - WaterSideTexture(); - ~WaterSideTexture(); - - void tick(); -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_PTEXTURE__DynamicTexture_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_PTEXTURE__DynamicTexture_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_PTEXTURE__DynamicTexture_H__ + +#include + +class Textures; + +class DynamicTexture +{ +public: + int tex; + int replicate; + unsigned char pixels[16*16*4]; + + DynamicTexture(int tex_); + virtual ~DynamicTexture() {} + + virtual void tick() = 0; + virtual void bindTexture(Textures* tex); +}; + +class WaterTexture: public DynamicTexture +{ + typedef DynamicTexture super; + int _tick; + int _frame; + + float* current; + float* next; + float* heat; + float* heata; + +public: + WaterTexture(); + ~WaterTexture(); + + void tick(); +}; + +class WaterSideTexture: public DynamicTexture +{ + typedef DynamicTexture super; + int _tick; + int _frame; + int _tickCount; + + float* current; + float* next; + float* heat; + float* heata; + +public: + WaterSideTexture(); + ~WaterSideTexture(); + + void tick(); +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_PTEXTURE__DynamicTexture_H__*/ diff --git a/src/client/renderer/tileentity/ChestRenderer.cpp b/src/client/renderer/tileentity/ChestRenderer.cpp index 5a5a58f..aa16bbd 100755 --- a/src/client/renderer/tileentity/ChestRenderer.cpp +++ b/src/client/renderer/tileentity/ChestRenderer.cpp @@ -1,76 +1,76 @@ -#include "ChestRenderer.h" -#include "TileEntityRenderer.h" -#include "../gles.h" -#include "../../model/ChestModel.h" -#include "../../../world/level/tile/entity/ChestTileEntity.h" -#include "../../../world/level/tile/ChestTile.h" -#include "../../../util/Mth.h" - - -void ChestRenderer::render( TileEntity* entity, float x, float y, float z, float a ) -{ - ChestTileEntity* chest = (ChestTileEntity*) entity; - int data = 0; - - if (chest->level) { - Tile* tile = chest->getTile(); - data = chest->getData(); - - if (tile != NULL && data == 0) { - ((ChestTile*)tile)->recalcLockDir(chest->level, chest->x, chest->y, chest->z); - data = chest->getData(); - } - - chest->checkNeighbors(); - } - if (chest->n != NULL || chest->w != NULL) return; - - ChestModel* model; - //if (chest->e != NULL || chest->s != NULL) { - // //model = &largeChestModel; - // bindTexture("item/largechest.png"); - //} else - { - model = &chestModel; - bindTexture("item/chest.png"); - } - - glPushMatrix2(); - glColor4f2(1, 1, 1, 1); - glTranslatef2(x, y + 1, z + 1); - glScalef2(1, -1, -1); - - glTranslatef2(0.5f, 0.5f, 0.5f); - GLfloat rot = 0; - if (data == 2) rot = 180; - if (data == 3) rot = 0; - if (data == 4) rot = 90; - if (data == 5) rot = -90; - - if (data == 2 && chest->e != NULL) { - glTranslatef2(1, 0, 0); - } - if (data == 5 && chest->s != NULL) { - glTranslatef2(0, 0, -1); - } - glRotatef2(rot, 0, 1, 0); - glTranslatef2(-0.5f, -0.5f, -0.5f); - - float open = chest->oOpenness + (chest->openness - chest->oOpenness) * a; - if (chest->n != NULL) { - float open2 = chest->n->oOpenness + (chest->n->openness - chest->n->oOpenness) * a; - if (open2 > open) open = open2; - } - if (chest->w != NULL) { - float open2 = chest->w->oOpenness + (chest->w->openness - chest->w->oOpenness) * a; - if (open2 > open) open = open2; - } - - open = 1 - open; - open = 1 - open * open * open; - - model->lid.xRot = -(open * Mth::PI / 2); - model->render(); - glPopMatrix2(); - glColor4f2(1, 1, 1, 1); -} +#include "ChestRenderer.h" +#include "TileEntityRenderer.h" +#include "../gles.h" +#include "../../model/ChestModel.h" +#include "../../../world/level/tile/entity/ChestTileEntity.h" +#include "../../../world/level/tile/ChestTile.h" +#include "../../../util/Mth.h" + + +void ChestRenderer::render( TileEntity* entity, float x, float y, float z, float a ) +{ + ChestTileEntity* chest = (ChestTileEntity*) entity; + int data = 0; + + if (chest->level) { + Tile* tile = chest->getTile(); + data = chest->getData(); + + if (tile != NULL && data == 0) { + ((ChestTile*)tile)->recalcLockDir(chest->level, chest->x, chest->y, chest->z); + data = chest->getData(); + } + + chest->checkNeighbors(); + } + if (chest->n != NULL || chest->w != NULL) return; + + ChestModel* model; + //if (chest->e != NULL || chest->s != NULL) { + // //model = &largeChestModel; + // bindTexture("item/largechest.png"); + //} else + { + model = &chestModel; + bindTexture("item/chest.png"); + } + + glPushMatrix2(); + glColor4f2(1, 1, 1, 1); + glTranslatef2(x, y + 1, z + 1); + glScalef2(1, -1, -1); + + glTranslatef2(0.5f, 0.5f, 0.5f); + GLfloat rot = 0; + if (data == 2) rot = 180; + if (data == 3) rot = 0; + if (data == 4) rot = 90; + if (data == 5) rot = -90; + + if (data == 2 && chest->e != NULL) { + glTranslatef2(1, 0, 0); + } + if (data == 5 && chest->s != NULL) { + glTranslatef2(0, 0, -1); + } + glRotatef2(rot, 0, 1, 0); + glTranslatef2(-0.5f, -0.5f, -0.5f); + + float open = chest->oOpenness + (chest->openness - chest->oOpenness) * a; + if (chest->n != NULL) { + float open2 = chest->n->oOpenness + (chest->n->openness - chest->n->oOpenness) * a; + if (open2 > open) open = open2; + } + if (chest->w != NULL) { + float open2 = chest->w->oOpenness + (chest->w->openness - chest->w->oOpenness) * a; + if (open2 > open) open = open2; + } + + open = 1 - open; + open = 1 - open * open * open; + + model->lid.xRot = -(open * Mth::PI / 2); + model->render(); + glPopMatrix2(); + glColor4f2(1, 1, 1, 1); +} diff --git a/src/client/renderer/tileentity/ChestRenderer.h b/src/client/renderer/tileentity/ChestRenderer.h index bf4e47e..82a77fa 100755 --- a/src/client/renderer/tileentity/ChestRenderer.h +++ b/src/client/renderer/tileentity/ChestRenderer.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__ChestRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__ChestRenderer_H__ - -//package net.minecraft.client.renderer.tileentity; - -#include "TileEntityRenderer.h" -#include "../../model/ChestModel.h" - -class ChestTileEntity; - -class ChestRenderer: public TileEntityRenderer -{ - /*@Override*/ - void render(TileEntity* chest, float x, float y, float z, float a); - -private: - ChestModel chestModel; - //LargeChestModel* largeChestModel; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__ChestRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__ChestRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__ChestRenderer_H__ + +//package net.minecraft.client.renderer.tileentity; + +#include "TileEntityRenderer.h" +#include "../../model/ChestModel.h" + +class ChestTileEntity; + +class ChestRenderer: public TileEntityRenderer +{ + /*@Override*/ + void render(TileEntity* chest, float x, float y, float z, float a); + +private: + ChestModel chestModel; + //LargeChestModel* largeChestModel; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__ChestRenderer_H__*/ diff --git a/src/client/renderer/tileentity/SignRenderer.cpp b/src/client/renderer/tileentity/SignRenderer.cpp index 0bd5d4c..f777a5c 100755 --- a/src/client/renderer/tileentity/SignRenderer.cpp +++ b/src/client/renderer/tileentity/SignRenderer.cpp @@ -1,65 +1,65 @@ -#include "SignRenderer.h" -#include "../../../world/level/tile/Tile.h" -#include "../../../world/level/tile/entity/SignTileEntity.h" - -void SignRenderer::render( TileEntity* te, float x, float y, float z, float a ) -{ - SignTileEntity* sign = (SignTileEntity*) te; - Tile* tile = sign->getTile(); - - glPushMatrix(); - float size = 16 / 24.0f; - if (tile == Tile::sign) { - glTranslatef(x + 0.5f, y + 0.75f * size, z + 0.5f); - float rot = sign->getData() * 360 / 16.0f; - glRotatef(-rot, 0, 1, 0); - signModel.cube2.visible = true; - } else { - int face = sign->getData(); - float rot = 0; - - if (face == 2) rot = 180; - if (face == 4) rot = 90; - if (face == 5) rot = -90; - - glTranslatef(x + 0.5f, y + 0.75f * size, z + 0.5f); - glRotatef(-rot, 0, 1, 0); - glTranslatef(0, -5 / 16.0f, -7 / 16.0f); - - signModel.cube2.visible = false; - } - - bindTexture("item/sign.png"); - - glPushMatrix(); - glScalef(size, -size, -size); - signModel.render(); - glPopMatrix(); - Font* font = getFont(); - - float s = 1 / 60.0f * size; - glTranslatef(0, 0.5f * size, 0.07f * size); - glScalef(s, -s, s); - glNormal3f(0, 0, -1 * s); - glDepthMask(false); - - int col = 0; - float yy = (float)(SignTileEntity::NUM_LINES * -5); - for (int i = 0; i < SignTileEntity::NUM_LINES; i++) { - std::string& msg = sign->messages[i]; - if (i == sign->selectedLine) { - std::string s = "> " + msg + " <"; - font->draw(s, (float)-font->width(s) / 2, yy, col); - } else { - font->draw(msg, (float)-font->width(msg) / 2, yy, col); - } - yy += 10; - } - glDepthMask(true); - glColor4f(1, 1, 1, 1); - glPopMatrix(); -} - -void SignRenderer::onGraphicsReset() { - signModel.onGraphicsReset(); -} +#include "SignRenderer.h" +#include "../../../world/level/tile/Tile.h" +#include "../../../world/level/tile/entity/SignTileEntity.h" + +void SignRenderer::render( TileEntity* te, float x, float y, float z, float a ) +{ + SignTileEntity* sign = (SignTileEntity*) te; + Tile* tile = sign->getTile(); + + glPushMatrix(); + float size = 16 / 24.0f; + if (tile == Tile::sign) { + glTranslatef(x + 0.5f, y + 0.75f * size, z + 0.5f); + float rot = sign->getData() * 360 / 16.0f; + glRotatef(-rot, 0, 1, 0); + signModel.cube2.visible = true; + } else { + int face = sign->getData(); + float rot = 0; + + if (face == 2) rot = 180; + if (face == 4) rot = 90; + if (face == 5) rot = -90; + + glTranslatef(x + 0.5f, y + 0.75f * size, z + 0.5f); + glRotatef(-rot, 0, 1, 0); + glTranslatef(0, -5 / 16.0f, -7 / 16.0f); + + signModel.cube2.visible = false; + } + + bindTexture("item/sign.png"); + + glPushMatrix(); + glScalef(size, -size, -size); + signModel.render(); + glPopMatrix(); + Font* font = getFont(); + + float s = 1 / 60.0f * size; + glTranslatef(0, 0.5f * size, 0.07f * size); + glScalef(s, -s, s); + glNormal3f(0, 0, -1 * s); + glDepthMask(false); + + int col = 0; + float yy = (float)(SignTileEntity::NUM_LINES * -5); + for (int i = 0; i < SignTileEntity::NUM_LINES; i++) { + std::string& msg = sign->messages[i]; + if (i == sign->selectedLine) { + std::string s = "> " + msg + " <"; + font->draw(s, (float)-font->width(s) / 2, yy, col); + } else { + font->draw(msg, (float)-font->width(msg) / 2, yy, col); + } + yy += 10; + } + glDepthMask(true); + glColor4f(1, 1, 1, 1); + glPopMatrix(); +} + +void SignRenderer::onGraphicsReset() { + signModel.onGraphicsReset(); +} diff --git a/src/client/renderer/tileentity/SignRenderer.h b/src/client/renderer/tileentity/SignRenderer.h index b9de6b3..3ce5bc3 100755 --- a/src/client/renderer/tileentity/SignRenderer.h +++ b/src/client/renderer/tileentity/SignRenderer.h @@ -1,21 +1,21 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__SignRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__SignRenderer_H__ - -//package net.minecraft.client.renderer.tileentity; - -#include "TileEntityRenderer.h" - -#include "../../gui/Font.h" -#include "../../model/SignModel.h" - -class SignRenderer: public TileEntityRenderer -{ -public: - /*@Override*/ - void render(TileEntity* te, float x, float y, float z, float a); - void onGraphicsReset(); -private: - SignModel signModel; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__SignRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__SignRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__SignRenderer_H__ + +//package net.minecraft.client.renderer.tileentity; + +#include "TileEntityRenderer.h" + +#include "../../gui/Font.h" +#include "../../model/SignModel.h" + +class SignRenderer: public TileEntityRenderer +{ +public: + /*@Override*/ + void render(TileEntity* te, float x, float y, float z, float a); + void onGraphicsReset(); +private: + SignModel signModel; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__SignRenderer_H__*/ diff --git a/src/client/renderer/tileentity/TileEntityRenderDispatcher.cpp b/src/client/renderer/tileentity/TileEntityRenderDispatcher.cpp index 9b1f82d..527049d 100755 --- a/src/client/renderer/tileentity/TileEntityRenderDispatcher.cpp +++ b/src/client/renderer/tileentity/TileEntityRenderDispatcher.cpp @@ -1,136 +1,136 @@ -#include "TileEntityRenderDispatcher.h" -#include "ChestRenderer.h" -#include "../Textures.h" -#include "../../gui/Font.h" -#include "../../../SharedConstants.h" -#include "../../../world/entity/Mob.h" -#include "../../../world/level/Level.h" -#include "../../../world/level/tile/entity/TileEntity.h" -#include "SignRenderer.h" - -/*static*/ -TileEntityRenderDispatcher* TileEntityRenderDispatcher::instance = 0; -/*static*/ -float TileEntityRenderDispatcher::xOff = 0, - TileEntityRenderDispatcher::yOff = 0, - TileEntityRenderDispatcher::zOff = 0; - -TileEntityRenderDispatcher* TileEntityRenderDispatcher::getInstance() -{ - if (!instance) instance = new TileEntityRenderDispatcher(); - return instance; -} - -TileEntityRenderDispatcher::~TileEntityRenderDispatcher() -{ - std::set destroyed; - for (RendererCIterator cit = _renderers.begin(); cit != _renderers.end(); ++cit) { - if (destroyed.find(cit->second) == destroyed.end()) { - destroyed.insert(cit->second); - delete cit->second; - } - } -} - -void TileEntityRenderDispatcher::destroy() -{ - if (instance) { - delete instance; - instance = NULL; - } -} - -void TileEntityRenderDispatcher::prepare( Level* level, Textures* textures, Font* font, Mob* player, float a ) -{ - if (this->level != level) - setLevel(level); - - this->textures = textures; - this->cameraEntity = player; - this->_font = font; - - playerRotY = player->yRotO + (player->yRot - player->yRotO) * a; - playerRotX = player->xRotO + (player->xRot - player->xRotO) * a; - - xPlayer = player->xOld + (player->x - player->xOld) * a; - yPlayer = player->yOld + (player->y - player->yOld) * a; - zPlayer = player->zOld + (player->z - player->zOld) * a; - - // xPlayer -= xPlayerOffs; - // yPlayer -= yPlayerOffs; - // zPlayer -= zPlayerOffs; -} - -void TileEntityRenderDispatcher::render( TileEntity* e, float a ) -{ - if (e->distanceToSqr(xPlayer, yPlayer, zPlayer) < 64 * 64) { - float br = level->getBrightness(e->x, e->y, e->z); - glColor4f(br, br, br, 1); - render(e, e->x - xOff, e->y - yOff, e->z - zOff, a); - } -} - -void TileEntityRenderDispatcher::render( TileEntity* entity, float x, float y, float z, float a ) -{ - TileEntityRenderer* renderer = getRenderer(entity); - if (renderer != NULL) { - renderer->render(entity, x, y, z, a); - } -} - -void TileEntityRenderDispatcher::setLevel( Level* level ) -{ - this->level = level; - - for (RendererCIterator cit = _renderers.begin(); cit != _renderers.end(); ++cit) - if (cit->second != NULL) - cit->second->onNewLevel(level); -} - -float TileEntityRenderDispatcher::distanceToSqr( float x, float y, float z ) -{ - float xd = x - xPlayer; - float yd = y - yPlayer; - float zd = z - zPlayer; - return xd * xd + yd * yd + zd * zd; -} - -Font* TileEntityRenderDispatcher::getFont() -{ - return _font; -} - -TileEntityRenderer* TileEntityRenderDispatcher::getRenderer( TileEntity* entity ) -{ - TileEntityRendererId rendererId = entity->rendererId; - - //if (rendererId == TR_QUERY_RENDERER) - // rendererId = entity->queryEntityRenderer(); - - return getRenderer(rendererId); -} - -TileEntityRenderer* TileEntityRenderDispatcher::getRenderer( TileEntityRendererId rendererId ) -{ - TileEntityRenderer* renderer = NULL; - RendererCIterator cit = _renderers.find(rendererId); - if (cit != _renderers.end()) { - renderer = cit->second; - } - return renderer; -} - -TileEntityRenderDispatcher::TileEntityRenderDispatcher() -{ - _renderers.insert(std::make_pair(TR_CHEST_RENDERER, new ChestRenderer())); - _renderers.insert(std::make_pair(TR_SIGN_RENDERER, new SignRenderer())); - - for (RendererCIterator cit = _renderers.begin(); cit != _renderers.end(); ++cit) - cit->second->init(this); -} - -void TileEntityRenderDispatcher::onGraphicsReset() { - for (RendererIterator it = _renderers.begin(); it != _renderers.end(); ++it) { - it->second->onGraphicsReset(); - } -} +#include "TileEntityRenderDispatcher.h" +#include "ChestRenderer.h" +#include "../Textures.h" +#include "../../gui/Font.h" +#include "../../../SharedConstants.h" +#include "../../../world/entity/Mob.h" +#include "../../../world/level/Level.h" +#include "../../../world/level/tile/entity/TileEntity.h" +#include "SignRenderer.h" + +/*static*/ +TileEntityRenderDispatcher* TileEntityRenderDispatcher::instance = 0; +/*static*/ +float TileEntityRenderDispatcher::xOff = 0, + TileEntityRenderDispatcher::yOff = 0, + TileEntityRenderDispatcher::zOff = 0; + +TileEntityRenderDispatcher* TileEntityRenderDispatcher::getInstance() +{ + if (!instance) instance = new TileEntityRenderDispatcher(); + return instance; +} + +TileEntityRenderDispatcher::~TileEntityRenderDispatcher() +{ + std::set destroyed; + for (RendererCIterator cit = _renderers.begin(); cit != _renderers.end(); ++cit) { + if (destroyed.find(cit->second) == destroyed.end()) { + destroyed.insert(cit->second); + delete cit->second; + } + } +} + +void TileEntityRenderDispatcher::destroy() +{ + if (instance) { + delete instance; + instance = NULL; + } +} + +void TileEntityRenderDispatcher::prepare( Level* level, Textures* textures, Font* font, Mob* player, float a ) +{ + if (this->level != level) + setLevel(level); + + this->textures = textures; + this->cameraEntity = player; + this->_font = font; + + playerRotY = player->yRotO + (player->yRot - player->yRotO) * a; + playerRotX = player->xRotO + (player->xRot - player->xRotO) * a; + + xPlayer = player->xOld + (player->x - player->xOld) * a; + yPlayer = player->yOld + (player->y - player->yOld) * a; + zPlayer = player->zOld + (player->z - player->zOld) * a; + + // xPlayer -= xPlayerOffs; + // yPlayer -= yPlayerOffs; + // zPlayer -= zPlayerOffs; +} + +void TileEntityRenderDispatcher::render( TileEntity* e, float a ) +{ + if (e->distanceToSqr(xPlayer, yPlayer, zPlayer) < 64 * 64) { + float br = level->getBrightness(e->x, e->y, e->z); + glColor4f(br, br, br, 1); + render(e, e->x - xOff, e->y - yOff, e->z - zOff, a); + } +} + +void TileEntityRenderDispatcher::render( TileEntity* entity, float x, float y, float z, float a ) +{ + TileEntityRenderer* renderer = getRenderer(entity); + if (renderer != NULL) { + renderer->render(entity, x, y, z, a); + } +} + +void TileEntityRenderDispatcher::setLevel( Level* level ) +{ + this->level = level; + + for (RendererCIterator cit = _renderers.begin(); cit != _renderers.end(); ++cit) + if (cit->second != NULL) + cit->second->onNewLevel(level); +} + +float TileEntityRenderDispatcher::distanceToSqr( float x, float y, float z ) +{ + float xd = x - xPlayer; + float yd = y - yPlayer; + float zd = z - zPlayer; + return xd * xd + yd * yd + zd * zd; +} + +Font* TileEntityRenderDispatcher::getFont() +{ + return _font; +} + +TileEntityRenderer* TileEntityRenderDispatcher::getRenderer( TileEntity* entity ) +{ + TileEntityRendererId rendererId = entity->rendererId; + + //if (rendererId == TR_QUERY_RENDERER) + // rendererId = entity->queryEntityRenderer(); + + return getRenderer(rendererId); +} + +TileEntityRenderer* TileEntityRenderDispatcher::getRenderer( TileEntityRendererId rendererId ) +{ + TileEntityRenderer* renderer = NULL; + RendererCIterator cit = _renderers.find(rendererId); + if (cit != _renderers.end()) { + renderer = cit->second; + } + return renderer; +} + +TileEntityRenderDispatcher::TileEntityRenderDispatcher() +{ + _renderers.insert(std::make_pair(TR_CHEST_RENDERER, new ChestRenderer())); + _renderers.insert(std::make_pair(TR_SIGN_RENDERER, new SignRenderer())); + + for (RendererCIterator cit = _renderers.begin(); cit != _renderers.end(); ++cit) + cit->second->init(this); +} + +void TileEntityRenderDispatcher::onGraphicsReset() { + for (RendererIterator it = _renderers.begin(); it != _renderers.end(); ++it) { + it->second->onGraphicsReset(); + } +} diff --git a/src/client/renderer/tileentity/TileEntityRenderDispatcher.h b/src/client/renderer/tileentity/TileEntityRenderDispatcher.h index 0f7539e..4533baa 100755 --- a/src/client/renderer/tileentity/TileEntityRenderDispatcher.h +++ b/src/client/renderer/tileentity/TileEntityRenderDispatcher.h @@ -1,61 +1,61 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderDispatcher_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderDispatcher_H__ - -//package net.minecraft.client.renderer.tileentity; - -#include -#include "../../../world/level/tile/entity/TileEntityRendererId.h" - -class TileEntityRenderer; -class Level; -class Textures; -class TileEntity; -class Font; -class Mob; - -class TileEntityRenderDispatcher -{ - typedef std::map RendererMap; - typedef RendererMap::iterator RendererIterator; - typedef RendererMap::const_iterator RendererCIterator; - -public: - ~TileEntityRenderDispatcher(); - - static TileEntityRenderDispatcher* getInstance(); - static void destroy(); - - void setLevel(Level* level); - void prepare(Level* level, Textures* textures, Font* font, Mob* player, float a); - - void render(TileEntity* e, float a); - void render(TileEntity* entity, float x, float y, float z, float a); - - float distanceToSqr(float x, float y, float z); - - Font* getFont(); - - TileEntityRenderer* getRenderer( TileEntity* entity ); - TileEntityRenderer* getRenderer( TileEntityRendererId rendererId ); - - void onGraphicsReset(); -private: - TileEntityRenderDispatcher(); -public: - static float xOff, yOff, zOff; - - Textures* textures; - Level* level; - Mob* cameraEntity; - float playerRotY; - float playerRotX; - - float xPlayer, yPlayer, zPlayer; -private: - static TileEntityRenderDispatcher* instance; - - Font* _font; - RendererMap _renderers; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderDispatcher_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderDispatcher_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderDispatcher_H__ + +//package net.minecraft.client.renderer.tileentity; + +#include +#include "../../../world/level/tile/entity/TileEntityRendererId.h" + +class TileEntityRenderer; +class Level; +class Textures; +class TileEntity; +class Font; +class Mob; + +class TileEntityRenderDispatcher +{ + typedef std::map RendererMap; + typedef RendererMap::iterator RendererIterator; + typedef RendererMap::const_iterator RendererCIterator; + +public: + ~TileEntityRenderDispatcher(); + + static TileEntityRenderDispatcher* getInstance(); + static void destroy(); + + void setLevel(Level* level); + void prepare(Level* level, Textures* textures, Font* font, Mob* player, float a); + + void render(TileEntity* e, float a); + void render(TileEntity* entity, float x, float y, float z, float a); + + float distanceToSqr(float x, float y, float z); + + Font* getFont(); + + TileEntityRenderer* getRenderer( TileEntity* entity ); + TileEntityRenderer* getRenderer( TileEntityRendererId rendererId ); + + void onGraphicsReset(); +private: + TileEntityRenderDispatcher(); +public: + static float xOff, yOff, zOff; + + Textures* textures; + Level* level; + Mob* cameraEntity; + float playerRotY; + float playerRotX; + + float xPlayer, yPlayer, zPlayer; +private: + static TileEntityRenderDispatcher* instance; + + Font* _font; + RendererMap _renderers; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderDispatcher_H__*/ diff --git a/src/client/renderer/tileentity/TileEntityRenderer.cpp b/src/client/renderer/tileentity/TileEntityRenderer.cpp index 042a3e3..89f06f3 100755 --- a/src/client/renderer/tileentity/TileEntityRenderer.cpp +++ b/src/client/renderer/tileentity/TileEntityRenderer.cpp @@ -1,32 +1,32 @@ -#include "TileEntityRenderer.h" -#include "TileEntityRenderDispatcher.h" -#include "../Textures.h" -#include "../../gui/Font.h" -#include "../../../world/level/Level.h" -#include "../../../world/level/tile/entity/TileEntity.h" - -TileEntityRenderer::TileEntityRenderer() -: tileEntityRenderDispatcher(NULL) -{ -} - -void TileEntityRenderer::bindTexture( const std::string& resourceName ) -{ - Textures* t = tileEntityRenderDispatcher->textures; - if (t != NULL) t->loadAndBindTexture(resourceName); -} - -Level* TileEntityRenderer::getLevel() -{ - return tileEntityRenderDispatcher->level; -} - -void TileEntityRenderer::init( TileEntityRenderDispatcher* tileEntityRenderDispatcher ) -{ - this->tileEntityRenderDispatcher = tileEntityRenderDispatcher; -} - -Font* TileEntityRenderer::getFont() -{ - return tileEntityRenderDispatcher->getFont(); -} +#include "TileEntityRenderer.h" +#include "TileEntityRenderDispatcher.h" +#include "../Textures.h" +#include "../../gui/Font.h" +#include "../../../world/level/Level.h" +#include "../../../world/level/tile/entity/TileEntity.h" + +TileEntityRenderer::TileEntityRenderer() +: tileEntityRenderDispatcher(NULL) +{ +} + +void TileEntityRenderer::bindTexture( const std::string& resourceName ) +{ + Textures* t = tileEntityRenderDispatcher->textures; + if (t != NULL) t->loadAndBindTexture(resourceName); +} + +Level* TileEntityRenderer::getLevel() +{ + return tileEntityRenderDispatcher->level; +} + +void TileEntityRenderer::init( TileEntityRenderDispatcher* tileEntityRenderDispatcher ) +{ + this->tileEntityRenderDispatcher = tileEntityRenderDispatcher; +} + +Font* TileEntityRenderer::getFont() +{ + return tileEntityRenderDispatcher->getFont(); +} diff --git a/src/client/renderer/tileentity/TileEntityRenderer.h b/src/client/renderer/tileentity/TileEntityRenderer.h index 0234682..7f8eaa1 100755 --- a/src/client/renderer/tileentity/TileEntityRenderer.h +++ b/src/client/renderer/tileentity/TileEntityRenderer.h @@ -1,38 +1,38 @@ -#ifndef NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderer_H__ -#define NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderer_H__ - -//package net.minecraft.client.renderer.tileentity; - -#include - -class Level; -class TileEntity; -class TileEntityRenderDispatcher; -class Font; - -class TileEntityRenderer -{ -public: - TileEntityRenderer(); - virtual ~TileEntityRenderer(){} - - void init(TileEntityRenderDispatcher* tileEntityRenderDispatcher); - virtual void render(TileEntity* entity, float x, float y, float z, float a) = 0; - - void bindTexture(const std::string& resourceName); -// /*protected*/ void bindTexture(const std::string& urlTexture, const std::string& backupTexture) { -// Textures t = tileEntityRenderDispatcher.textures; -// if (t != NULL) t.bind(t.loadHttpTexture(urlTexture, backupTexture)); -// } - - Level* getLevel(); - Font* getFont(); - - virtual void onGraphicsReset() {} - - virtual void onNewLevel(Level* level) {} -protected: - TileEntityRenderDispatcher* tileEntityRenderDispatcher; -}; - -#endif /*NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderer_H__*/ +#ifndef NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderer_H__ +#define NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderer_H__ + +//package net.minecraft.client.renderer.tileentity; + +#include + +class Level; +class TileEntity; +class TileEntityRenderDispatcher; +class Font; + +class TileEntityRenderer +{ +public: + TileEntityRenderer(); + virtual ~TileEntityRenderer(){} + + void init(TileEntityRenderDispatcher* tileEntityRenderDispatcher); + virtual void render(TileEntity* entity, float x, float y, float z, float a) = 0; + + void bindTexture(const std::string& resourceName); +// /*protected*/ void bindTexture(const std::string& urlTexture, const std::string& backupTexture) { +// Textures t = tileEntityRenderDispatcher.textures; +// if (t != NULL) t.bind(t.loadHttpTexture(urlTexture, backupTexture)); +// } + + Level* getLevel(); + Font* getFont(); + + virtual void onGraphicsReset() {} + + virtual void onNewLevel(Level* level) {} +protected: + TileEntityRenderDispatcher* tileEntityRenderDispatcher; +}; + +#endif /*NET_MINECRAFT_CLIENT_RENDERER_TILEENTITY__TileEntityRenderer_H__*/ diff --git a/src/client/sound/Sound.cpp b/src/client/sound/Sound.cpp index ff5442a..fc9945b 100755 --- a/src/client/sound/Sound.cpp +++ b/src/client/sound/Sound.cpp @@ -1,226 +1,226 @@ -#include "Sound.h" - -#if !defined(PRE_ANDROID23) && !defined(__APPLE__) && !defined(RPI) - -#include "data/cloth1.pcm" -#include "data/cloth2.pcm" -#include "data/cloth3.pcm" -#include "data/cloth4.pcm" -#include "data/grass1.pcm" -#include "data/grass2.pcm" -#include "data/grass3.pcm" -#include "data/grass4.pcm" -#include "data/gravel1.pcm" -#include "data/gravel2.pcm" -#include "data/gravel3.pcm" -#include "data/gravel4.pcm" -#include "data/sand1.pcm" -#include "data/sand2.pcm" -#include "data/sand3.pcm" -#include "data/sand4.pcm" -#include "data/stone1.pcm" -#include "data/stone2.pcm" -#include "data/stone3.pcm" -#include "data/stone4.pcm" -#include "data/wood1.pcm" -#include "data/wood2.pcm" -#include "data/wood3.pcm" -#include "data/wood4.pcm" - -#include "data/click.pcm" -#include "data/explode.pcm" -#include "data/splash.pcm" -#include "data/pop.pcm" -#include "data/pop2.pcm" -#include "data/hurt.pcm" -#include "data/door_open.pcm" -#include "data/door_close.pcm" -#include "data/glass1.pcm" -#include "data/glass2.pcm" -#include "data/glass3.pcm" - -#include "data/sheep1.pcm" -#include "data/sheep2.pcm" -#include "data/sheep3.pcm" - -#include "data/pig1.pcm" -#include "data/pig2.pcm" -#include "data/pig3.pcm" -#include "data/pigdeath.pcm" - -//#include "data/chicken1.pcm" -#include "data/chicken2.pcm" -#include "data/chicken3.pcm" -#include "data/chickenhurt1.pcm" -#include "data/chickenhurt2.pcm" - -#include "data/cow1.pcm" -#include "data/cow2.pcm" -#include "data/cow3.pcm" -#include "data/cow4.pcm" -#include "data/cowhurt1.pcm" -#include "data/cowhurt2.pcm" -#include "data/cowhurt3.pcm" - -#include "data/zombie1.pcm" -#include "data/zombie2.pcm" -#include "data/zombie3.pcm" -#include "data/zombiedeath.pcm" -#include "data/zombiehurt1.pcm" -#include "data/zombiehurt2.pcm" - -#include "data/zpig1.pcm" -#include "data/zpig2.pcm" -#include "data/zpig3.pcm" -#include "data/zpig4.pcm" -#include "data/zpigangry1.pcm" -#include "data/zpigangry2.pcm" -#include "data/zpigangry3.pcm" -#include "data/zpigangry4.pcm" -#include "data/zpigdeath.pcm" -#include "data/zpighurt1.pcm" -#include "data/zpighurt2.pcm" - -#include "data/bow.pcm" -#include "data/bowhit1.pcm" -#include "data/bowhit2.pcm" -#include "data/bowhit3.pcm" -#include "data/bowhit4.pcm" -#include "data/fallbig1.pcm" -#include "data/fallbig2.pcm" -#include "data/fallsmall.pcm" -#include "data/skeleton1.pcm" -#include "data/skeleton2.pcm" -#include "data/skeleton3.pcm" -#include "data/skeletondeath.pcm" -#include "data/skeletonhurt1.pcm" -#include "data/skeletonhurt2.pcm" -#include "data/skeletonhurt3.pcm" -#include "data/skeletonhurt4.pcm" -#include "data/spider1.pcm" -#include "data/spider2.pcm" -#include "data/spider3.pcm" -#include "data/spider4.pcm" -#include "data/spiderdeath.pcm" - -#include "data/creeper1.pcm" -#include "data/creeper2.pcm" -#include "data/creeper3.pcm" -#include "data/creeper4.pcm" -#include "data/creeperdeath.pcm" -#include "data/eat1.pcm" -#include "data/eat2.pcm" -#include "data/eat3.pcm" -#include "data/fuse.pcm" - -SoundDesc SA_cloth1((char*)PCM_cloth1); -SoundDesc SA_cloth2((char*)PCM_cloth2); -SoundDesc SA_cloth3((char*)PCM_cloth3); -SoundDesc SA_cloth4((char*)PCM_cloth4); -SoundDesc SA_grass1((char*)PCM_grass1); -SoundDesc SA_grass2((char*)PCM_grass2); -SoundDesc SA_grass3((char*)PCM_grass3); -SoundDesc SA_grass4((char*)PCM_grass4); -SoundDesc SA_gravel1((char*)PCM_gravel1); -SoundDesc SA_gravel2((char*)PCM_gravel2); -SoundDesc SA_gravel3((char*)PCM_gravel3); -SoundDesc SA_gravel4((char*)PCM_gravel4); -SoundDesc SA_sand1((char*)PCM_sand1); -SoundDesc SA_sand2((char*)PCM_sand2); -SoundDesc SA_sand3((char*)PCM_sand3); -SoundDesc SA_sand4((char*)PCM_sand4); -SoundDesc SA_stone1((char*)PCM_stone1); -SoundDesc SA_stone2((char*)PCM_stone2); -SoundDesc SA_stone3((char*)PCM_stone3); -SoundDesc SA_stone4((char*)PCM_stone4); -SoundDesc SA_wood1((char*)PCM_wood1); -SoundDesc SA_wood2((char*)PCM_wood2); -SoundDesc SA_wood3((char*)PCM_wood3); -SoundDesc SA_wood4((char*)PCM_wood4); - -SoundDesc SA_click((char*)PCM_click); -SoundDesc SA_explode((char*)PCM_explode); -SoundDesc SA_splash((char*)PCM_splash); - -SoundDesc SA_door_open((char*)PCM_door_open); -SoundDesc SA_door_close((char*)PCM_door_close); -SoundDesc SA_pop((char*)PCM_pop); -SoundDesc SA_hurt((char*)PCM_hurt); -SoundDesc SA_glass1((char*)PCM_glass1); -SoundDesc SA_glass2((char*)PCM_glass2); -SoundDesc SA_glass3((char*)PCM_glass3); - -SoundDesc SA_sheep1((char*)PCM_sheep1); -SoundDesc SA_sheep2((char*)PCM_sheep2); -SoundDesc SA_sheep3((char*)PCM_sheep3); -SoundDesc SA_pig1((char*)PCM_pig1); -SoundDesc SA_pig2((char*)PCM_pig2); -SoundDesc SA_pig3((char*)PCM_pig3); -SoundDesc SA_pigdeath((char*)PCM_pigdeath); - -//SoundDesc SA_chicken1((char*)PCM_chicken1); -SoundDesc SA_chicken2((char*)PCM_chicken2); -SoundDesc SA_chicken3((char*)PCM_chicken3); -SoundDesc SA_chickenhurt1((char*)PCM_chickenhurt1); -SoundDesc SA_chickenhurt2((char*)PCM_chickenhurt2); -SoundDesc SA_cow1((char*)PCM_cow1); -SoundDesc SA_cow2((char*)PCM_cow2); -SoundDesc SA_cow3((char*)PCM_cow3); -SoundDesc SA_cow4((char*)PCM_cow4); -SoundDesc SA_cowhurt1((char*)PCM_cowhurt1); -SoundDesc SA_cowhurt2((char*)PCM_cowhurt2); -SoundDesc SA_cowhurt3((char*)PCM_cowhurt3); - -SoundDesc SA_zombie1((char*)PCM_zombie1); -SoundDesc SA_zombie2((char*)PCM_zombie2); -SoundDesc SA_zombie3((char*)PCM_zombie3); - -SoundDesc SA_zpig1((char*)PCM_zpig1); -SoundDesc SA_zpig2((char*)PCM_zpig2); -SoundDesc SA_zpig3((char*)PCM_zpig3); -SoundDesc SA_zpig4((char*)PCM_zpig4); -SoundDesc SA_zpigangry1((char*)PCM_zpigangry1); -SoundDesc SA_zpigangry2((char*)PCM_zpigangry2); -SoundDesc SA_zpigangry3((char*)PCM_zpigangry3); -SoundDesc SA_zpigangry4((char*)PCM_zpigangry4); -SoundDesc SA_zpigdeath((char*)PCM_zpigdeath); -SoundDesc SA_zpighurt1((char*)PCM_zpighurt1); -SoundDesc SA_zpighurt2((char*)PCM_zpighurt2); - -SoundDesc SA_zombiedeath((char*)PCM_zombiedeath); -SoundDesc SA_zombiehurt1((char*)PCM_zombiehurt1); -SoundDesc SA_zombiehurt2((char*)PCM_zombiehurt2); - -SoundDesc SA_bow((char*)PCM_bow); -SoundDesc SA_bowhit1((char*)PCM_bowhit1); -SoundDesc SA_bowhit2((char*)PCM_bowhit2); -SoundDesc SA_bowhit3((char*)PCM_bowhit3); -SoundDesc SA_bowhit4((char*)PCM_bowhit4); -SoundDesc SA_fallbig1((char*)PCM_fallbig1); -SoundDesc SA_fallbig2((char*)PCM_fallbig2); -SoundDesc SA_fallsmall((char*)PCM_fallsmall); -SoundDesc SA_skeleton1((char*)PCM_skeleton1); -SoundDesc SA_skeleton2((char*)PCM_skeleton2); -SoundDesc SA_skeleton3((char*)PCM_skeleton3); -SoundDesc SA_skeletondeath((char*)PCM_skeletondeath); -SoundDesc SA_skeletonhurt1((char*)PCM_skeletonhurt1); -SoundDesc SA_skeletonhurt2((char*)PCM_skeletonhurt2); -SoundDesc SA_skeletonhurt3((char*)PCM_skeletonhurt3); -SoundDesc SA_skeletonhurt4((char*)PCM_skeletonhurt4); -SoundDesc SA_spider1((char*)PCM_spider1); -SoundDesc SA_spider2((char*)PCM_spider2); -SoundDesc SA_spider3((char*)PCM_spider3); -SoundDesc SA_spider4((char*)PCM_spider4); -SoundDesc SA_spiderdeath((char*)PCM_spiderdeath); - -SoundDesc SA_creeper1((char*)PCM_creeper1); -SoundDesc SA_creeper2((char*)PCM_creeper2); -SoundDesc SA_creeper3((char*)PCM_creeper3); -SoundDesc SA_creeper4((char*)PCM_creeper4); -SoundDesc SA_creeperdeath((char*)PCM_creeperdeath); -SoundDesc SA_eat1((char*)PCM_eat1); -SoundDesc SA_eat2((char*)PCM_eat2); -SoundDesc SA_eat3((char*)PCM_eat3); -SoundDesc SA_fuse((char*)PCM_fuse); - -#endif /*!PRE_ANDROID23 && !__APPLE__*/ +#include "Sound.h" + +#if !defined(PRE_ANDROID23) && !defined(__APPLE__) && !defined(RPI) + +#include "data/cloth1.pcm" +#include "data/cloth2.pcm" +#include "data/cloth3.pcm" +#include "data/cloth4.pcm" +#include "data/grass1.pcm" +#include "data/grass2.pcm" +#include "data/grass3.pcm" +#include "data/grass4.pcm" +#include "data/gravel1.pcm" +#include "data/gravel2.pcm" +#include "data/gravel3.pcm" +#include "data/gravel4.pcm" +#include "data/sand1.pcm" +#include "data/sand2.pcm" +#include "data/sand3.pcm" +#include "data/sand4.pcm" +#include "data/stone1.pcm" +#include "data/stone2.pcm" +#include "data/stone3.pcm" +#include "data/stone4.pcm" +#include "data/wood1.pcm" +#include "data/wood2.pcm" +#include "data/wood3.pcm" +#include "data/wood4.pcm" + +#include "data/click.pcm" +#include "data/explode.pcm" +#include "data/splash.pcm" +#include "data/pop.pcm" +#include "data/pop2.pcm" +#include "data/hurt.pcm" +#include "data/door_open.pcm" +#include "data/door_close.pcm" +#include "data/glass1.pcm" +#include "data/glass2.pcm" +#include "data/glass3.pcm" + +#include "data/sheep1.pcm" +#include "data/sheep2.pcm" +#include "data/sheep3.pcm" + +#include "data/pig1.pcm" +#include "data/pig2.pcm" +#include "data/pig3.pcm" +#include "data/pigdeath.pcm" + +//#include "data/chicken1.pcm" +#include "data/chicken2.pcm" +#include "data/chicken3.pcm" +#include "data/chickenhurt1.pcm" +#include "data/chickenhurt2.pcm" + +#include "data/cow1.pcm" +#include "data/cow2.pcm" +#include "data/cow3.pcm" +#include "data/cow4.pcm" +#include "data/cowhurt1.pcm" +#include "data/cowhurt2.pcm" +#include "data/cowhurt3.pcm" + +#include "data/zombie1.pcm" +#include "data/zombie2.pcm" +#include "data/zombie3.pcm" +#include "data/zombiedeath.pcm" +#include "data/zombiehurt1.pcm" +#include "data/zombiehurt2.pcm" + +#include "data/zpig1.pcm" +#include "data/zpig2.pcm" +#include "data/zpig3.pcm" +#include "data/zpig4.pcm" +#include "data/zpigangry1.pcm" +#include "data/zpigangry2.pcm" +#include "data/zpigangry3.pcm" +#include "data/zpigangry4.pcm" +#include "data/zpigdeath.pcm" +#include "data/zpighurt1.pcm" +#include "data/zpighurt2.pcm" + +#include "data/bow.pcm" +#include "data/bowhit1.pcm" +#include "data/bowhit2.pcm" +#include "data/bowhit3.pcm" +#include "data/bowhit4.pcm" +#include "data/fallbig1.pcm" +#include "data/fallbig2.pcm" +#include "data/fallsmall.pcm" +#include "data/skeleton1.pcm" +#include "data/skeleton2.pcm" +#include "data/skeleton3.pcm" +#include "data/skeletondeath.pcm" +#include "data/skeletonhurt1.pcm" +#include "data/skeletonhurt2.pcm" +#include "data/skeletonhurt3.pcm" +#include "data/skeletonhurt4.pcm" +#include "data/spider1.pcm" +#include "data/spider2.pcm" +#include "data/spider3.pcm" +#include "data/spider4.pcm" +#include "data/spiderdeath.pcm" + +#include "data/creeper1.pcm" +#include "data/creeper2.pcm" +#include "data/creeper3.pcm" +#include "data/creeper4.pcm" +#include "data/creeperdeath.pcm" +#include "data/eat1.pcm" +#include "data/eat2.pcm" +#include "data/eat3.pcm" +#include "data/fuse.pcm" + +SoundDesc SA_cloth1((char*)PCM_cloth1); +SoundDesc SA_cloth2((char*)PCM_cloth2); +SoundDesc SA_cloth3((char*)PCM_cloth3); +SoundDesc SA_cloth4((char*)PCM_cloth4); +SoundDesc SA_grass1((char*)PCM_grass1); +SoundDesc SA_grass2((char*)PCM_grass2); +SoundDesc SA_grass3((char*)PCM_grass3); +SoundDesc SA_grass4((char*)PCM_grass4); +SoundDesc SA_gravel1((char*)PCM_gravel1); +SoundDesc SA_gravel2((char*)PCM_gravel2); +SoundDesc SA_gravel3((char*)PCM_gravel3); +SoundDesc SA_gravel4((char*)PCM_gravel4); +SoundDesc SA_sand1((char*)PCM_sand1); +SoundDesc SA_sand2((char*)PCM_sand2); +SoundDesc SA_sand3((char*)PCM_sand3); +SoundDesc SA_sand4((char*)PCM_sand4); +SoundDesc SA_stone1((char*)PCM_stone1); +SoundDesc SA_stone2((char*)PCM_stone2); +SoundDesc SA_stone3((char*)PCM_stone3); +SoundDesc SA_stone4((char*)PCM_stone4); +SoundDesc SA_wood1((char*)PCM_wood1); +SoundDesc SA_wood2((char*)PCM_wood2); +SoundDesc SA_wood3((char*)PCM_wood3); +SoundDesc SA_wood4((char*)PCM_wood4); + +SoundDesc SA_click((char*)PCM_click); +SoundDesc SA_explode((char*)PCM_explode); +SoundDesc SA_splash((char*)PCM_splash); + +SoundDesc SA_door_open((char*)PCM_door_open); +SoundDesc SA_door_close((char*)PCM_door_close); +SoundDesc SA_pop((char*)PCM_pop); +SoundDesc SA_hurt((char*)PCM_hurt); +SoundDesc SA_glass1((char*)PCM_glass1); +SoundDesc SA_glass2((char*)PCM_glass2); +SoundDesc SA_glass3((char*)PCM_glass3); + +SoundDesc SA_sheep1((char*)PCM_sheep1); +SoundDesc SA_sheep2((char*)PCM_sheep2); +SoundDesc SA_sheep3((char*)PCM_sheep3); +SoundDesc SA_pig1((char*)PCM_pig1); +SoundDesc SA_pig2((char*)PCM_pig2); +SoundDesc SA_pig3((char*)PCM_pig3); +SoundDesc SA_pigdeath((char*)PCM_pigdeath); + +//SoundDesc SA_chicken1((char*)PCM_chicken1); +SoundDesc SA_chicken2((char*)PCM_chicken2); +SoundDesc SA_chicken3((char*)PCM_chicken3); +SoundDesc SA_chickenhurt1((char*)PCM_chickenhurt1); +SoundDesc SA_chickenhurt2((char*)PCM_chickenhurt2); +SoundDesc SA_cow1((char*)PCM_cow1); +SoundDesc SA_cow2((char*)PCM_cow2); +SoundDesc SA_cow3((char*)PCM_cow3); +SoundDesc SA_cow4((char*)PCM_cow4); +SoundDesc SA_cowhurt1((char*)PCM_cowhurt1); +SoundDesc SA_cowhurt2((char*)PCM_cowhurt2); +SoundDesc SA_cowhurt3((char*)PCM_cowhurt3); + +SoundDesc SA_zombie1((char*)PCM_zombie1); +SoundDesc SA_zombie2((char*)PCM_zombie2); +SoundDesc SA_zombie3((char*)PCM_zombie3); + +SoundDesc SA_zpig1((char*)PCM_zpig1); +SoundDesc SA_zpig2((char*)PCM_zpig2); +SoundDesc SA_zpig3((char*)PCM_zpig3); +SoundDesc SA_zpig4((char*)PCM_zpig4); +SoundDesc SA_zpigangry1((char*)PCM_zpigangry1); +SoundDesc SA_zpigangry2((char*)PCM_zpigangry2); +SoundDesc SA_zpigangry3((char*)PCM_zpigangry3); +SoundDesc SA_zpigangry4((char*)PCM_zpigangry4); +SoundDesc SA_zpigdeath((char*)PCM_zpigdeath); +SoundDesc SA_zpighurt1((char*)PCM_zpighurt1); +SoundDesc SA_zpighurt2((char*)PCM_zpighurt2); + +SoundDesc SA_zombiedeath((char*)PCM_zombiedeath); +SoundDesc SA_zombiehurt1((char*)PCM_zombiehurt1); +SoundDesc SA_zombiehurt2((char*)PCM_zombiehurt2); + +SoundDesc SA_bow((char*)PCM_bow); +SoundDesc SA_bowhit1((char*)PCM_bowhit1); +SoundDesc SA_bowhit2((char*)PCM_bowhit2); +SoundDesc SA_bowhit3((char*)PCM_bowhit3); +SoundDesc SA_bowhit4((char*)PCM_bowhit4); +SoundDesc SA_fallbig1((char*)PCM_fallbig1); +SoundDesc SA_fallbig2((char*)PCM_fallbig2); +SoundDesc SA_fallsmall((char*)PCM_fallsmall); +SoundDesc SA_skeleton1((char*)PCM_skeleton1); +SoundDesc SA_skeleton2((char*)PCM_skeleton2); +SoundDesc SA_skeleton3((char*)PCM_skeleton3); +SoundDesc SA_skeletondeath((char*)PCM_skeletondeath); +SoundDesc SA_skeletonhurt1((char*)PCM_skeletonhurt1); +SoundDesc SA_skeletonhurt2((char*)PCM_skeletonhurt2); +SoundDesc SA_skeletonhurt3((char*)PCM_skeletonhurt3); +SoundDesc SA_skeletonhurt4((char*)PCM_skeletonhurt4); +SoundDesc SA_spider1((char*)PCM_spider1); +SoundDesc SA_spider2((char*)PCM_spider2); +SoundDesc SA_spider3((char*)PCM_spider3); +SoundDesc SA_spider4((char*)PCM_spider4); +SoundDesc SA_spiderdeath((char*)PCM_spiderdeath); + +SoundDesc SA_creeper1((char*)PCM_creeper1); +SoundDesc SA_creeper2((char*)PCM_creeper2); +SoundDesc SA_creeper3((char*)PCM_creeper3); +SoundDesc SA_creeper4((char*)PCM_creeper4); +SoundDesc SA_creeperdeath((char*)PCM_creeperdeath); +SoundDesc SA_eat1((char*)PCM_eat1); +SoundDesc SA_eat2((char*)PCM_eat2); +SoundDesc SA_eat3((char*)PCM_eat3); +SoundDesc SA_fuse((char*)PCM_fuse); + +#endif /*!PRE_ANDROID23 && !__APPLE__*/ diff --git a/src/client/sound/Sound.h b/src/client/sound/Sound.h index 62f3db0..21ee9bf 100755 --- a/src/client/sound/Sound.h +++ b/src/client/sound/Sound.h @@ -1,180 +1,180 @@ -#ifndef NET_MINECRAFT_CLIENT_SOUND__Sound_H__ -#define NET_MINECRAFT_CLIENT_SOUND__Sound_H__ - -//package net.minecraft.client.sound; - -#include - -class SoundDesc -{ -public: - SoundDesc() - : buffer(0) - {} - - SoundDesc(char* data, int size, int channels, int width, int rate) - : buffer(data), - frames(data), - size(size), - channels(channels), - byteWidth(width), - frameRate(rate) - { - numFrames = size / (channels * byteWidth); - } - - SoundDesc(char* data) - : buffer(data) - { - // header [INT][Channels, BytePerSample, FrameRate, NumFrames] - channels = *((int*)&data[0]); - byteWidth = *((int*)&data[4]); - frameRate = *((int*)&data[8]); - numFrames = *((int*)&data[12]); - - size = channels * byteWidth * numFrames; - frames = buffer + 16; - } - - bool isValid() const { return buffer != 0; } - float length() const { - return ((float)numFrames) / frameRate; - } - - void destroy() const { - if (isValid()) { - delete buffer; - buffer = 0; - } - } - - char* frames; - int size; - - int channels; - int byteWidth; - int frameRate; - int numFrames; - - std::string name; -private: - mutable char* buffer; -}; - -#if !defined(PRE_ANDROID23) && !defined(__APPLE__) && !defined(RPI) - -extern SoundDesc SA_cloth1; -extern SoundDesc SA_cloth2; -extern SoundDesc SA_cloth3; -extern SoundDesc SA_cloth4; -extern SoundDesc SA_grass1; -extern SoundDesc SA_grass2; -extern SoundDesc SA_grass3; -extern SoundDesc SA_grass4; -extern SoundDesc SA_gravel1; -extern SoundDesc SA_gravel2; -extern SoundDesc SA_gravel3; -extern SoundDesc SA_gravel4; -extern SoundDesc SA_sand1; -extern SoundDesc SA_sand2; -extern SoundDesc SA_sand3; -extern SoundDesc SA_sand4; -extern SoundDesc SA_stone1; -extern SoundDesc SA_stone2; -extern SoundDesc SA_stone3; -extern SoundDesc SA_stone4; -extern SoundDesc SA_wood1; -extern SoundDesc SA_wood2; -extern SoundDesc SA_wood3; -extern SoundDesc SA_wood4; - -extern SoundDesc SA_click; -extern SoundDesc SA_explode; -extern SoundDesc SA_splash; - -extern SoundDesc SA_door_open; -extern SoundDesc SA_door_close; -extern SoundDesc SA_pop; -extern SoundDesc SA_pop2; -extern SoundDesc SA_hurt; -extern SoundDesc SA_glass1; -extern SoundDesc SA_glass2; -extern SoundDesc SA_glass3; - -extern SoundDesc SA_sheep1; -extern SoundDesc SA_sheep2; -extern SoundDesc SA_sheep3; -extern SoundDesc SA_pig1; -extern SoundDesc SA_pig2; -extern SoundDesc SA_pig3; -extern SoundDesc SA_pigdeath; - -//extern SoundDesc SA_chicken1; -extern SoundDesc SA_chicken2; -extern SoundDesc SA_chicken3; -extern SoundDesc SA_chickenhurt1; -extern SoundDesc SA_chickenhurt2; - -extern SoundDesc SA_cow1; -extern SoundDesc SA_cow2; -extern SoundDesc SA_cow3; -extern SoundDesc SA_cow4; -extern SoundDesc SA_cowhurt1; -extern SoundDesc SA_cowhurt2; -extern SoundDesc SA_cowhurt3; - -extern SoundDesc SA_zombie1; -extern SoundDesc SA_zombie2; -extern SoundDesc SA_zombie3; -extern SoundDesc SA_zombiedeath; -extern SoundDesc SA_zombiehurt1; -extern SoundDesc SA_zombiehurt2; - -extern SoundDesc SA_zpig1; -extern SoundDesc SA_zpig2; -extern SoundDesc SA_zpig3; -extern SoundDesc SA_zpig4; -extern SoundDesc SA_zpigangry1; -extern SoundDesc SA_zpigangry2; -extern SoundDesc SA_zpigangry3; -extern SoundDesc SA_zpigangry4; -extern SoundDesc SA_zpigdeath; -extern SoundDesc SA_zpighurt1; -extern SoundDesc SA_zpighurt2; - -extern SoundDesc SA_bow; -extern SoundDesc SA_bowhit1; -extern SoundDesc SA_bowhit2; -extern SoundDesc SA_bowhit3; -extern SoundDesc SA_bowhit4; -extern SoundDesc SA_fallbig1; -extern SoundDesc SA_fallbig2; -extern SoundDesc SA_fallsmall; -extern SoundDesc SA_skeleton1; -extern SoundDesc SA_skeleton2; -extern SoundDesc SA_skeleton3; -extern SoundDesc SA_skeletondeath; -extern SoundDesc SA_skeletonhurt1; -extern SoundDesc SA_skeletonhurt2; -extern SoundDesc SA_skeletonhurt3; -extern SoundDesc SA_skeletonhurt4; -extern SoundDesc SA_spider1; -extern SoundDesc SA_spider2; -extern SoundDesc SA_spider3; -extern SoundDesc SA_spider4; -extern SoundDesc SA_spiderdeath; - -extern SoundDesc SA_creeper1; -extern SoundDesc SA_creeper2; -extern SoundDesc SA_creeper3; -extern SoundDesc SA_creeper4; -extern SoundDesc SA_creeperdeath; -extern SoundDesc SA_eat1; -extern SoundDesc SA_eat2; -extern SoundDesc SA_eat3; -extern SoundDesc SA_fuse; - -#endif /*!PRE_ANDROID23 && !__APPLE__*/ - - -#endif /*NET_MINECRAFT_CLIENT_SOUND__Sound_H__*/ +#ifndef NET_MINECRAFT_CLIENT_SOUND__Sound_H__ +#define NET_MINECRAFT_CLIENT_SOUND__Sound_H__ + +//package net.minecraft.client.sound; + +#include + +class SoundDesc +{ +public: + SoundDesc() + : buffer(0) + {} + + SoundDesc(char* data, int size, int channels, int width, int rate) + : buffer(data), + frames(data), + size(size), + channels(channels), + byteWidth(width), + frameRate(rate) + { + numFrames = size / (channels * byteWidth); + } + + SoundDesc(char* data) + : buffer(data) + { + // header [INT][Channels, BytePerSample, FrameRate, NumFrames] + channels = *((int*)&data[0]); + byteWidth = *((int*)&data[4]); + frameRate = *((int*)&data[8]); + numFrames = *((int*)&data[12]); + + size = channels * byteWidth * numFrames; + frames = buffer + 16; + } + + bool isValid() const { return buffer != 0; } + float length() const { + return ((float)numFrames) / frameRate; + } + + void destroy() const { + if (isValid()) { + delete buffer; + buffer = 0; + } + } + + char* frames; + int size; + + int channels; + int byteWidth; + int frameRate; + int numFrames; + + std::string name; +private: + mutable char* buffer; +}; + +#if !defined(PRE_ANDROID23) && !defined(__APPLE__) && !defined(RPI) + +extern SoundDesc SA_cloth1; +extern SoundDesc SA_cloth2; +extern SoundDesc SA_cloth3; +extern SoundDesc SA_cloth4; +extern SoundDesc SA_grass1; +extern SoundDesc SA_grass2; +extern SoundDesc SA_grass3; +extern SoundDesc SA_grass4; +extern SoundDesc SA_gravel1; +extern SoundDesc SA_gravel2; +extern SoundDesc SA_gravel3; +extern SoundDesc SA_gravel4; +extern SoundDesc SA_sand1; +extern SoundDesc SA_sand2; +extern SoundDesc SA_sand3; +extern SoundDesc SA_sand4; +extern SoundDesc SA_stone1; +extern SoundDesc SA_stone2; +extern SoundDesc SA_stone3; +extern SoundDesc SA_stone4; +extern SoundDesc SA_wood1; +extern SoundDesc SA_wood2; +extern SoundDesc SA_wood3; +extern SoundDesc SA_wood4; + +extern SoundDesc SA_click; +extern SoundDesc SA_explode; +extern SoundDesc SA_splash; + +extern SoundDesc SA_door_open; +extern SoundDesc SA_door_close; +extern SoundDesc SA_pop; +extern SoundDesc SA_pop2; +extern SoundDesc SA_hurt; +extern SoundDesc SA_glass1; +extern SoundDesc SA_glass2; +extern SoundDesc SA_glass3; + +extern SoundDesc SA_sheep1; +extern SoundDesc SA_sheep2; +extern SoundDesc SA_sheep3; +extern SoundDesc SA_pig1; +extern SoundDesc SA_pig2; +extern SoundDesc SA_pig3; +extern SoundDesc SA_pigdeath; + +//extern SoundDesc SA_chicken1; +extern SoundDesc SA_chicken2; +extern SoundDesc SA_chicken3; +extern SoundDesc SA_chickenhurt1; +extern SoundDesc SA_chickenhurt2; + +extern SoundDesc SA_cow1; +extern SoundDesc SA_cow2; +extern SoundDesc SA_cow3; +extern SoundDesc SA_cow4; +extern SoundDesc SA_cowhurt1; +extern SoundDesc SA_cowhurt2; +extern SoundDesc SA_cowhurt3; + +extern SoundDesc SA_zombie1; +extern SoundDesc SA_zombie2; +extern SoundDesc SA_zombie3; +extern SoundDesc SA_zombiedeath; +extern SoundDesc SA_zombiehurt1; +extern SoundDesc SA_zombiehurt2; + +extern SoundDesc SA_zpig1; +extern SoundDesc SA_zpig2; +extern SoundDesc SA_zpig3; +extern SoundDesc SA_zpig4; +extern SoundDesc SA_zpigangry1; +extern SoundDesc SA_zpigangry2; +extern SoundDesc SA_zpigangry3; +extern SoundDesc SA_zpigangry4; +extern SoundDesc SA_zpigdeath; +extern SoundDesc SA_zpighurt1; +extern SoundDesc SA_zpighurt2; + +extern SoundDesc SA_bow; +extern SoundDesc SA_bowhit1; +extern SoundDesc SA_bowhit2; +extern SoundDesc SA_bowhit3; +extern SoundDesc SA_bowhit4; +extern SoundDesc SA_fallbig1; +extern SoundDesc SA_fallbig2; +extern SoundDesc SA_fallsmall; +extern SoundDesc SA_skeleton1; +extern SoundDesc SA_skeleton2; +extern SoundDesc SA_skeleton3; +extern SoundDesc SA_skeletondeath; +extern SoundDesc SA_skeletonhurt1; +extern SoundDesc SA_skeletonhurt2; +extern SoundDesc SA_skeletonhurt3; +extern SoundDesc SA_skeletonhurt4; +extern SoundDesc SA_spider1; +extern SoundDesc SA_spider2; +extern SoundDesc SA_spider3; +extern SoundDesc SA_spider4; +extern SoundDesc SA_spiderdeath; + +extern SoundDesc SA_creeper1; +extern SoundDesc SA_creeper2; +extern SoundDesc SA_creeper3; +extern SoundDesc SA_creeper4; +extern SoundDesc SA_creeperdeath; +extern SoundDesc SA_eat1; +extern SoundDesc SA_eat2; +extern SoundDesc SA_eat3; +extern SoundDesc SA_fuse; + +#endif /*!PRE_ANDROID23 && !__APPLE__*/ + + +#endif /*NET_MINECRAFT_CLIENT_SOUND__Sound_H__*/ diff --git a/src/client/sound/SoundEngine.cpp b/src/client/sound/SoundEngine.cpp index 0af6d43..f1bc4b4 100755 --- a/src/client/sound/SoundEngine.cpp +++ b/src/client/sound/SoundEngine.cpp @@ -1,257 +1,257 @@ -#include "SoundEngine.h" -#include "../Options.h" -#include "../Minecraft.h" -#include "../../world/entity/Mob.h" - - -SoundEngine::SoundEngine( float maxDistance ) -: idCounter(0), - mc(0), - _x(0), - _y(0), - _z(0), - _yRot(0), - _invMaxDistance(1.0f / maxDistance) -{ - -} - -SoundEngine::~SoundEngine() -{ - -} - -void SoundEngine::init( Minecraft* mc, Options* options ) -{ - this->mc = mc; - this->options = options; - - if (/*!loaded && */(options == NULL || - (options->getProgressValue(OPTIONS_SOUND_VOLUME) != 0.f || - options->getProgressValue(OPTIONS_MUSIC_VOLUME) != 0.f))) - { - loadLibrary(); - } - -#if !defined(PRE_ANDROID23) && !defined(__APPLE__) && !defined(RPI) - sounds.add("step.cloth", SA_cloth1); - sounds.add("step.cloth", SA_cloth2); - sounds.add("step.cloth", SA_cloth3); - sounds.add("step.cloth", SA_cloth4); - sounds.add("step.grass", SA_grass1); - sounds.add("step.grass", SA_grass2); - sounds.add("step.grass", SA_grass3); - sounds.add("step.grass", SA_grass4); - sounds.add("step.gravel", SA_gravel1); - sounds.add("step.gravel", SA_gravel2); - sounds.add("step.gravel", SA_gravel3); - sounds.add("step.gravel", SA_gravel4); - sounds.add("step.sand", SA_sand1); - sounds.add("step.sand", SA_sand2); - sounds.add("step.sand", SA_sand3); - sounds.add("step.sand", SA_sand4); - sounds.add("step.stone", SA_stone1); - sounds.add("step.stone", SA_stone2); - sounds.add("step.stone", SA_stone3); - sounds.add("step.stone", SA_stone4); - sounds.add("step.wood", SA_wood1); - sounds.add("step.wood", SA_wood2); - sounds.add("step.wood", SA_wood3); - sounds.add("step.wood", SA_wood4); - - sounds.add("random.splash", SA_splash); - sounds.add("random.explode", SA_explode); - sounds.add("random.click", SA_click); - - sounds.add("random.door_open", SA_door_open); - sounds.add("random.door_close", SA_door_close); - sounds.add("random.pop", SA_pop); - sounds.add("random.pop2", SA_pop2); - sounds.add("random.hurt", SA_hurt); - sounds.add("random.glass", SA_glass1); - sounds.add("random.glass", SA_glass2); - sounds.add("random.glass", SA_glass3); - - sounds.add("mob.sheep", SA_sheep1); - sounds.add("mob.sheep", SA_sheep2); - sounds.add("mob.sheep", SA_sheep3); - sounds.add("mob.pig", SA_pig1); - sounds.add("mob.pig", SA_pig2); - sounds.add("mob.pig", SA_pig3); - sounds.add("mob.pigdeath", SA_pigdeath); - - sounds.add("mob.cow", SA_cow1); - sounds.add("mob.cow", SA_cow2); - sounds.add("mob.cow", SA_cow3); - sounds.add("mob.cow", SA_cow4); - sounds.add("mob.cowhurt", SA_cowhurt1); - sounds.add("mob.cowhurt", SA_cowhurt2); - sounds.add("mob.cowhurt", SA_cowhurt3); - - sounds.add("mob.chicken", SA_chicken2); - sounds.add("mob.chicken", SA_chicken3); - sounds.add("mob.chickenhurt", SA_chickenhurt1); - sounds.add("mob.chickenhurt", SA_chickenhurt2); - - sounds.add("mob.zombie", SA_zombie1); - sounds.add("mob.zombie", SA_zombie2); - sounds.add("mob.zombie", SA_zombie3); - sounds.add("mob.zombiedeath", SA_zombiedeath); - sounds.add("mob.zombiehurt", SA_zombiehurt1); - sounds.add("mob.zombiehurt", SA_zombiehurt2); - - sounds.add("mob.skeleton", SA_skeleton1); - sounds.add("mob.skeleton", SA_skeleton2); - sounds.add("mob.skeleton", SA_skeleton3); - sounds.add("mob.skeletonhurt", SA_skeletonhurt1); - sounds.add("mob.skeletonhurt", SA_skeletonhurt2); - sounds.add("mob.skeletonhurt", SA_skeletonhurt3); - sounds.add("mob.skeletonhurt", SA_skeletonhurt4); - - sounds.add("mob.spider", SA_spider1); - sounds.add("mob.spider", SA_spider2); - sounds.add("mob.spider", SA_spider3); - sounds.add("mob.spider", SA_spider4); - sounds.add("mob.spiderdeath", SA_spiderdeath); - - sounds.add("mob.zombiepig.zpig", SA_zpig1); - sounds.add("mob.zombiepig.zpig", SA_zpig2); - sounds.add("mob.zombiepig.zpig", SA_zpig3); - sounds.add("mob.zombiepig.zpig", SA_zpig4); - sounds.add("mob.zombiepig.zpigangry", SA_zpigangry1); - sounds.add("mob.zombiepig.zpigangry", SA_zpigangry2); - sounds.add("mob.zombiepig.zpigangry", SA_zpigangry3); - sounds.add("mob.zombiepig.zpigangry", SA_zpigangry4); - sounds.add("mob.zombiepig.zpigdeath", SA_zpigdeath); - sounds.add("mob.zombiepig.zpighurt", SA_zpighurt1); - sounds.add("mob.zombiepig.zpighurt", SA_zpighurt2); - - sounds.add("damage.fallbig", SA_fallbig1); - sounds.add("damage.fallbig", SA_fallbig2); - sounds.add("damage.fallsmall", SA_fallsmall); - - sounds.add("random.bow", SA_bow); - sounds.add("random.bowhit", SA_bowhit1); - sounds.add("random.bowhit", SA_bowhit2); - sounds.add("random.bowhit", SA_bowhit3); - sounds.add("random.bowhit", SA_bowhit4); - - sounds.add("mob.creeper", SA_creeper1); - sounds.add("mob.creeper", SA_creeper2); - sounds.add("mob.creeper", SA_creeper3); - sounds.add("mob.creeper", SA_creeper4); - sounds.add("mob.creeperdeath", SA_creeperdeath); - sounds.add("random.eat", SA_eat1); - sounds.add("random.eat", SA_eat2); - sounds.add("random.eat", SA_eat3); - sounds.add("random.fuse", SA_fuse); - -#endif -} - -void SoundEngine::enable( bool status ) -{ -#if defined(__APPLE__) - soundSystem.enable(status); -#endif -} - -void SoundEngine::updateOptions() -{ - -} - -void SoundEngine::destroy() -{ - //if (loaded) soundSystem.cleanup(); -} - -void SoundEngine::update( Mob* player, float a ) -{ - if (/*!loaded || */options->getProgressValue(OPTIONS_SOUND_VOLUME) == 0) return; - if (player == NULL) return; - - _x = player->xo + (player->x - player->xo) * a; - _y = player->yo + (player->y - player->yo) * a; - _z = player->zo + (player->z - player->zo) * a; - _yRot = player->yRotO + (player->yRot - player->yRotO) * a; - - soundSystem.setListenerAngle(_yRot); - //soundSystem.setListenerPos(_x, _y, _z); //@note: not used, since we translate all sounds to Player space -} - -float SoundEngine::_getVolumeMult( float x, float y, float z ) -{ - const float dx = x - _x; - const float dy = y - _y; - const float dz = z - _z; - const float dist = Mth::sqrt(dx*dx + dy*dy + dz*dz); - const float out = Mth::clamp(1.1f - dist*_invMaxDistance, -1.0f, 1.0f); - return out; -} - -#if defined(PRE_ANDROID23) -void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) { - //volume *= (2.0f * _getVolumeMult(x, y, z)) - if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; - - volume *= _getVolumeMult(x, y, z); - mc->platform()->playSound(name, volume, pitch); -} -void SoundEngine::playUI(const std::string& name, float volume, float pitch) { - if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; - - //volume *= 2.0f; - mc->platform()->playSound(name, volume, pitch); -} -#elif defined(__APPLE__) -void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) { - if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; - - volume = Mth::clamp(volume, 0.0f, 1.0f); - - SoundDesc sound; - if (sounds.get(name, sound)) { - soundSystem.playAt(sound, x-_x, y-_y, z-_z, volume, pitch); - } -} -void SoundEngine::playUI(const std::string& name, float volume, float pitch) { - if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; - - volume = Mth::clamp(volume, 0.0f, 1.0f); - if (/*!loaded || */options->getProgressValue(OPTIONS_SOUND_VOLUME) == 0 || volume <= 0) return; - - SoundDesc sound; - if (sounds.get(name, sound)) { - soundSystem.playAt(sound, 0, 0, 0, volume, pitch); - } -} -#elif defined(RPI) -void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) {} -void SoundEngine::playUI(const std::string& name, float volume, float pitch) {} -#else -void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) { - if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; - - volume = Mth::clamp( volume * _getVolumeMult(x, y, z), 0.0f, 1.0f); - if (/*!loaded || */options->getProgressValue(OPTIONS_SOUND_VOLUME) == 0 || volume <= 0) return; - - SoundDesc sound; - if (sounds.get(name, sound)) { - float dist = SOUND_DISTANCE; - if (volume > 1) dist *= volume; - soundSystem.playAt(sound, x-_x, y-_y, z-_z, volume, pitch); - } -} -void SoundEngine::playUI(const std::string& name, float volume, float pitch) { - if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; - - volume = Mth::clamp(volume, 0.0f, 1.0f); - if (/*!loaded || */options->getProgressValue(OPTIONS_SOUND_VOLUME) == 0 || volume <= 0) return; - - SoundDesc sound; - if (sounds.get(name, sound)) { - soundSystem.playAt(sound, 0, 0, 0, volume, pitch); - } -} -#endif +#include "SoundEngine.h" +#include "../Options.h" +#include "../Minecraft.h" +#include "../../world/entity/Mob.h" + + +SoundEngine::SoundEngine( float maxDistance ) +: idCounter(0), + mc(0), + _x(0), + _y(0), + _z(0), + _yRot(0), + _invMaxDistance(1.0f / maxDistance) +{ + +} + +SoundEngine::~SoundEngine() +{ + +} + +void SoundEngine::init( Minecraft* mc, Options* options ) +{ + this->mc = mc; + this->options = options; + + if (/*!loaded && */(options == NULL || + (options->getProgressValue(OPTIONS_SOUND_VOLUME) != 0.f || + options->getProgressValue(OPTIONS_MUSIC_VOLUME) != 0.f))) + { + loadLibrary(); + } + +#if !defined(PRE_ANDROID23) && !defined(__APPLE__) && !defined(RPI) + sounds.add("step.cloth", SA_cloth1); + sounds.add("step.cloth", SA_cloth2); + sounds.add("step.cloth", SA_cloth3); + sounds.add("step.cloth", SA_cloth4); + sounds.add("step.grass", SA_grass1); + sounds.add("step.grass", SA_grass2); + sounds.add("step.grass", SA_grass3); + sounds.add("step.grass", SA_grass4); + sounds.add("step.gravel", SA_gravel1); + sounds.add("step.gravel", SA_gravel2); + sounds.add("step.gravel", SA_gravel3); + sounds.add("step.gravel", SA_gravel4); + sounds.add("step.sand", SA_sand1); + sounds.add("step.sand", SA_sand2); + sounds.add("step.sand", SA_sand3); + sounds.add("step.sand", SA_sand4); + sounds.add("step.stone", SA_stone1); + sounds.add("step.stone", SA_stone2); + sounds.add("step.stone", SA_stone3); + sounds.add("step.stone", SA_stone4); + sounds.add("step.wood", SA_wood1); + sounds.add("step.wood", SA_wood2); + sounds.add("step.wood", SA_wood3); + sounds.add("step.wood", SA_wood4); + + sounds.add("random.splash", SA_splash); + sounds.add("random.explode", SA_explode); + sounds.add("random.click", SA_click); + + sounds.add("random.door_open", SA_door_open); + sounds.add("random.door_close", SA_door_close); + sounds.add("random.pop", SA_pop); + sounds.add("random.pop2", SA_pop2); + sounds.add("random.hurt", SA_hurt); + sounds.add("random.glass", SA_glass1); + sounds.add("random.glass", SA_glass2); + sounds.add("random.glass", SA_glass3); + + sounds.add("mob.sheep", SA_sheep1); + sounds.add("mob.sheep", SA_sheep2); + sounds.add("mob.sheep", SA_sheep3); + sounds.add("mob.pig", SA_pig1); + sounds.add("mob.pig", SA_pig2); + sounds.add("mob.pig", SA_pig3); + sounds.add("mob.pigdeath", SA_pigdeath); + + sounds.add("mob.cow", SA_cow1); + sounds.add("mob.cow", SA_cow2); + sounds.add("mob.cow", SA_cow3); + sounds.add("mob.cow", SA_cow4); + sounds.add("mob.cowhurt", SA_cowhurt1); + sounds.add("mob.cowhurt", SA_cowhurt2); + sounds.add("mob.cowhurt", SA_cowhurt3); + + sounds.add("mob.chicken", SA_chicken2); + sounds.add("mob.chicken", SA_chicken3); + sounds.add("mob.chickenhurt", SA_chickenhurt1); + sounds.add("mob.chickenhurt", SA_chickenhurt2); + + sounds.add("mob.zombie", SA_zombie1); + sounds.add("mob.zombie", SA_zombie2); + sounds.add("mob.zombie", SA_zombie3); + sounds.add("mob.zombiedeath", SA_zombiedeath); + sounds.add("mob.zombiehurt", SA_zombiehurt1); + sounds.add("mob.zombiehurt", SA_zombiehurt2); + + sounds.add("mob.skeleton", SA_skeleton1); + sounds.add("mob.skeleton", SA_skeleton2); + sounds.add("mob.skeleton", SA_skeleton3); + sounds.add("mob.skeletonhurt", SA_skeletonhurt1); + sounds.add("mob.skeletonhurt", SA_skeletonhurt2); + sounds.add("mob.skeletonhurt", SA_skeletonhurt3); + sounds.add("mob.skeletonhurt", SA_skeletonhurt4); + + sounds.add("mob.spider", SA_spider1); + sounds.add("mob.spider", SA_spider2); + sounds.add("mob.spider", SA_spider3); + sounds.add("mob.spider", SA_spider4); + sounds.add("mob.spiderdeath", SA_spiderdeath); + + sounds.add("mob.zombiepig.zpig", SA_zpig1); + sounds.add("mob.zombiepig.zpig", SA_zpig2); + sounds.add("mob.zombiepig.zpig", SA_zpig3); + sounds.add("mob.zombiepig.zpig", SA_zpig4); + sounds.add("mob.zombiepig.zpigangry", SA_zpigangry1); + sounds.add("mob.zombiepig.zpigangry", SA_zpigangry2); + sounds.add("mob.zombiepig.zpigangry", SA_zpigangry3); + sounds.add("mob.zombiepig.zpigangry", SA_zpigangry4); + sounds.add("mob.zombiepig.zpigdeath", SA_zpigdeath); + sounds.add("mob.zombiepig.zpighurt", SA_zpighurt1); + sounds.add("mob.zombiepig.zpighurt", SA_zpighurt2); + + sounds.add("damage.fallbig", SA_fallbig1); + sounds.add("damage.fallbig", SA_fallbig2); + sounds.add("damage.fallsmall", SA_fallsmall); + + sounds.add("random.bow", SA_bow); + sounds.add("random.bowhit", SA_bowhit1); + sounds.add("random.bowhit", SA_bowhit2); + sounds.add("random.bowhit", SA_bowhit3); + sounds.add("random.bowhit", SA_bowhit4); + + sounds.add("mob.creeper", SA_creeper1); + sounds.add("mob.creeper", SA_creeper2); + sounds.add("mob.creeper", SA_creeper3); + sounds.add("mob.creeper", SA_creeper4); + sounds.add("mob.creeperdeath", SA_creeperdeath); + sounds.add("random.eat", SA_eat1); + sounds.add("random.eat", SA_eat2); + sounds.add("random.eat", SA_eat3); + sounds.add("random.fuse", SA_fuse); + +#endif +} + +void SoundEngine::enable( bool status ) +{ +#if defined(__APPLE__) + soundSystem.enable(status); +#endif +} + +void SoundEngine::updateOptions() +{ + +} + +void SoundEngine::destroy() +{ + //if (loaded) soundSystem.cleanup(); +} + +void SoundEngine::update( Mob* player, float a ) +{ + if (/*!loaded || */options->getProgressValue(OPTIONS_SOUND_VOLUME) == 0) return; + if (player == NULL) return; + + _x = player->xo + (player->x - player->xo) * a; + _y = player->yo + (player->y - player->yo) * a; + _z = player->zo + (player->z - player->zo) * a; + _yRot = player->yRotO + (player->yRot - player->yRotO) * a; + + soundSystem.setListenerAngle(_yRot); + //soundSystem.setListenerPos(_x, _y, _z); //@note: not used, since we translate all sounds to Player space +} + +float SoundEngine::_getVolumeMult( float x, float y, float z ) +{ + const float dx = x - _x; + const float dy = y - _y; + const float dz = z - _z; + const float dist = Mth::sqrt(dx*dx + dy*dy + dz*dz); + const float out = Mth::clamp(1.1f - dist*_invMaxDistance, -1.0f, 1.0f); + return out; +} + +#if defined(PRE_ANDROID23) +void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) { + //volume *= (2.0f * _getVolumeMult(x, y, z)) + if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; + + volume *= _getVolumeMult(x, y, z); + mc->platform()->playSound(name, volume, pitch); +} +void SoundEngine::playUI(const std::string& name, float volume, float pitch) { + if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; + + //volume *= 2.0f; + mc->platform()->playSound(name, volume, pitch); +} +#elif defined(__APPLE__) +void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) { + if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; + + volume = Mth::clamp(volume, 0.0f, 1.0f); + + SoundDesc sound; + if (sounds.get(name, sound)) { + soundSystem.playAt(sound, x-_x, y-_y, z-_z, volume, pitch); + } +} +void SoundEngine::playUI(const std::string& name, float volume, float pitch) { + if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; + + volume = Mth::clamp(volume, 0.0f, 1.0f); + if (/*!loaded || */options->getProgressValue(OPTIONS_SOUND_VOLUME) == 0 || volume <= 0) return; + + SoundDesc sound; + if (sounds.get(name, sound)) { + soundSystem.playAt(sound, 0, 0, 0, volume, pitch); + } +} +#elif defined(RPI) +void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) {} +void SoundEngine::playUI(const std::string& name, float volume, float pitch) {} +#else +void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) { + if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; + + volume = Mth::clamp( volume * _getVolumeMult(x, y, z), 0.0f, 1.0f); + if (/*!loaded || */options->getProgressValue(OPTIONS_SOUND_VOLUME) == 0 || volume <= 0) return; + + SoundDesc sound; + if (sounds.get(name, sound)) { + float dist = SOUND_DISTANCE; + if (volume > 1) dist *= volume; + soundSystem.playAt(sound, x-_x, y-_y, z-_z, volume, pitch); + } +} +void SoundEngine::playUI(const std::string& name, float volume, float pitch) { + if ((volume *= options->getProgressValue(OPTIONS_SOUND_VOLUME)) <= 0) return; + + volume = Mth::clamp(volume, 0.0f, 1.0f); + if (/*!loaded || */options->getProgressValue(OPTIONS_SOUND_VOLUME) == 0 || volume <= 0) return; + + SoundDesc sound; + if (sounds.get(name, sound)) { + soundSystem.playAt(sound, 0, 0, 0, volume, pitch); + } +} +#endif diff --git a/src/client/sound/SoundEngine.mm b/src/client/sound/SoundEngine.mm index c135d15..9b66881 100755 --- a/src/client/sound/SoundEngine.mm +++ b/src/client/sound/SoundEngine.mm @@ -1,385 +1,385 @@ -#include "SoundEngine.h" -#include "../Options.h" -#include "../Minecraft.h" -#include "../../world/entity/Mob.h" - -/* - - File: MyOpenALSupport.c - Abstract: OpenAL-related support functions - Version: 1.4 - - Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple - Inc. ("Apple") in consideration of your agreement to the following - terms, and your use, installation, modification or redistribution of - this Apple software constitutes acceptance of these terms. If you do - not agree with these terms, please do not use, install, modify or - redistribute this Apple software. - - In consideration of your agreement to abide by the following terms, and - subject to these terms, Apple grants you a personal, non-exclusive - license, under Apple's copyrights in this original Apple software (the - "Apple Software"), to use, reproduce, modify and redistribute the Apple - Software, with or without modifications, in source and/or binary forms; - provided that if you redistribute the Apple Software in its entirety and - without modifications, you must retain this notice and the following - text and disclaimers in all such redistributions of the Apple Software. - Neither the name, trademarks, service marks or logos of Apple Inc. may - be used to endorse or promote products derived from the Apple Software - without specific prior written permission from Apple. Except as - expressly stated in this notice, no other rights or licenses, express or - implied, are granted by Apple herein, including but not limited to any - patent rights that may be infringed by your derivative works or by other - works in which the Apple Software may be incorporated. - - The Apple Software is provided by Apple on an "AS IS" basis. APPLE - MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION - THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND - OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. - - IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL - OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, - MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED - AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), - STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - Copyright (C) 2009 Apple Inc. All Rights Reserved. - - - */ - -#import -#import -#import -#import - - -//ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq) -//{ -// static alBufferDataStaticProcPtr proc = NULL; -// -// if (proc == NULL) { -// proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic"); -// } -// -// if (proc) -// proc(bid, format, data, size, freq); -// -// return; -//} - -void* MyGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate) -{ - OSStatus err = noErr; - SInt64 theFileLengthInFrames = 0; - AudioStreamBasicDescription theFileFormat; - UInt32 thePropertySize = sizeof(theFileFormat); - ExtAudioFileRef extRef = NULL; - void* theData = NULL; - AudioStreamBasicDescription theOutputFormat; - - do { - // Open a file with ExtAudioFileOpen() - err = ExtAudioFileOpenURL(inFileURL, &extRef); - if(err) { printf("MyGetOpenALAudioData: ExtAudioFileOpenURL FAILED, Error = %ld\n", err); break; } - - // Get the audio data format - err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &theFileFormat); - if(err) { printf("MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileDataFormat) FAILED, Error = %ld\n", err); break; } - if (theFileFormat.mChannelsPerFrame > 2) { printf("MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); break;} - - // Set the client format to 16 bit signed integer (native-endian) data - // Maintain the channel count and sample rate of the original source format - theOutputFormat.mSampleRate = theFileFormat.mSampleRate; - theOutputFormat.mChannelsPerFrame = theFileFormat.mChannelsPerFrame; - - theOutputFormat.mFormatID = kAudioFormatLinearPCM; - theOutputFormat.mBytesPerPacket = 2 * theOutputFormat.mChannelsPerFrame; - theOutputFormat.mFramesPerPacket = 1; - theOutputFormat.mBytesPerFrame = 2 * theOutputFormat.mChannelsPerFrame; - theOutputFormat.mBitsPerChannel = 16; - theOutputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; - - // Set the desired client (output) data format - err = ExtAudioFileSetProperty(extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(theOutputFormat), &theOutputFormat); - if(err) { printf("MyGetOpenALAudioData: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) FAILED, Error = %ld\n", err); break; } - - // Get the total frame count - thePropertySize = sizeof(theFileLengthInFrames); - err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames); - if(err) { printf("MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileLengthFrames) FAILED, Error = %ld\n", err); break; } - - // Read all the data into memory - UInt32 dataSize = theFileLengthInFrames * theOutputFormat.mBytesPerFrame; - theData = (void*)new char[dataSize]; - if (theData) - { - AudioBufferList theDataBuffer; - theDataBuffer.mNumberBuffers = 1; - theDataBuffer.mBuffers[0].mDataByteSize = dataSize; - theDataBuffer.mBuffers[0].mNumberChannels = theOutputFormat.mChannelsPerFrame; - theDataBuffer.mBuffers[0].mData = theData; - - // Read the data into an AudioBufferList - err = ExtAudioFileRead(extRef, (UInt32*)&theFileLengthInFrames, &theDataBuffer); - if(err == noErr) - { - // success - *outDataSize = (ALsizei)dataSize; - *outDataFormat = (theOutputFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; - *outSampleRate = (ALsizei)theOutputFormat.mSampleRate; - } - else - { - // failure - free (theData); - theData = NULL; // make sure to return NULL - printf("MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", err); break; - } - } - } while(0); - - // Dispose the ExtAudioFileRef, it is no longer needed - if (extRef) ExtAudioFileDispose(extRef); - return theData; -} - - - -SoundEngine::SoundEngine( float maxDistance ) -: idCounter(0), - mc(0), - _x(0), - _y(0), - _z(0), - _yRot(0), - _invMaxDistance(1.0f / maxDistance) -{ -} - -SoundEngine::~SoundEngine() -{ -} - -SoundDesc SoundEngine::_pp(const std::string& fn) { - NSString* filename = [[NSString alloc] initWithUTF8String:fn.c_str()]; - NSString* path = [[NSBundle mainBundle] pathForResource:filename ofType:@"m4a"]; - [filename release]; - if (!path) - return SoundDesc(); - - CFURLRef fileURL = (CFURLRef)[[NSURL fileURLWithPath:path] retain]; - if (fileURL) { - static Stopwatch sw; - sw.start(); - - ALsizei size, freq; - ALenum format; - void* data = MyGetOpenALAudioData(fileURL, &size, &format, &freq); - - - CFRelease(fileURL); - sw.stop(); - sw.printEvery(1, "load sound " + fn + " :: "); - int channels = (format == AL_FORMAT_STEREO16 || format == AL_FORMAT_STEREO8)? 2 : 1; - int bytewidth= (format == AL_FORMAT_STEREO16 || format == AL_FORMAT_MONO16)? 2 : 1; - - return SoundDesc((char*)data, size, channels, bytewidth, freq); - } - return SoundDesc(); -} - -void SoundEngine::init( Minecraft* mc, Options* options ) -{ - this->mc = mc; - this->options = options; - - if (/*!loaded && */(options == NULL || (options->sound != 0 || options->music != 0))) { - loadLibrary(); - } - - sounds.add( "damage.fallbig", _pp("fallbig1")); - sounds.add( "damage.fallbig", _pp("fallbig2")); - sounds.add( "damage.fallsmall", _pp("fallsmall")); - sounds.add( "random.explode", _pp("explode")); - sounds.add( "random.splash", _pp("splash")); - sounds.add( "random.hurt", _pp("hurt")); - sounds.add( "random.pop", _pp("pop")); - sounds.add( "random.click", _pp("click")); - - sounds.add( "random.door_open", _pp("door_open")); - sounds.add( "random.door_close", _pp("door_close")); - - sounds.add( "random.bow", _pp("bow")); - sounds.add( "random.bowhit", _pp("bowhit1")); - sounds.add( "random.bowhit", _pp("bowhit2")); - sounds.add( "random.bowhit", _pp("bowhit3")); - sounds.add( "random.bowhit", _pp("bowhit4")); - - sounds.add( "random.glass", _pp("glass1")); - sounds.add( "random.glass", _pp("glass2")); - sounds.add( "random.glass", _pp("glass3")); - - sounds.add( "random.eat", _pp("eat1")); - sounds.add( "random.eat", _pp("eat2")); - sounds.add( "random.eat", _pp("eat3")); - sounds.add( "random.fuse", _pp("fuse")); - - sounds.add( "step.cloth", _pp("cloth1")); - sounds.add( "step.cloth", _pp("cloth2")); - sounds.add( "step.cloth", _pp("cloth3")); - sounds.add( "step.cloth", _pp("cloth4")); - - sounds.add( "step.grass", _pp("grass1")); - sounds.add( "step.grass", _pp("grass2")); - sounds.add( "step.grass", _pp("grass3")); - sounds.add( "step.grass", _pp("grass4")); - - //new SoundId(R.raw.gravel1, _pp("step.gravel"), - sounds.add( "step.gravel", _pp("gravel2")); - sounds.add( "step.gravel", _pp("gravel3")); - sounds.add( "step.gravel", _pp("gravel4")); - - sounds.add( "step.sand", _pp("sand1")); - sounds.add( "step.sand", _pp("sand2")); - sounds.add( "step.sand", _pp("sand3")); - sounds.add( "step.sand", _pp("sand4")); - - sounds.add( "step.stone", _pp("stone1")); - sounds.add( "step.stone", _pp("stone2")); - sounds.add( "step.stone", _pp("stone3")); - sounds.add( "step.stone", _pp("stone4")); - - sounds.add( "step.wood", _pp("wood1")); - sounds.add( "step.wood", _pp("wood2")); - sounds.add( "step.wood", _pp("wood3")); - sounds.add( "step.wood", _pp("wood4")); - - sounds.add( "mob.sheep", _pp("sheep1")); - sounds.add( "mob.sheep", _pp("sheep2")); - sounds.add( "mob.sheep", _pp("sheep3")); - - sounds.add( "mob.pig", _pp("pig1")); - sounds.add( "mob.pig", _pp("pig2")); - sounds.add( "mob.pig", _pp("pig3")); - sounds.add( "mob.pigdeath", _pp("pigdeath")); - - //sounds.add( "mob.chicken", _pp("chicken1")); - sounds.add( "mob.chicken", _pp("chicken2")); - sounds.add( "mob.chicken", _pp("chicken3")); - sounds.add( "mob.chickenhurt", _pp("chickenhurt1")); - sounds.add( "mob.chickenhurt", _pp("chickenhurt2")); - - sounds.add( "mob.cow", _pp("cow1")); - sounds.add( "mob.cow", _pp("cow2")); - sounds.add( "mob.cow", _pp("cow3")); - sounds.add( "mob.cow", _pp("cow4")); - sounds.add( "mob.cowhurt", _pp("cowhurt1")); - sounds.add( "mob.cowhurt", _pp("cowhurt2")); - sounds.add( "mob.cowhurt", _pp("cowhurt3")); - - sounds.add( "mob.zombie", _pp("zombie1")); - sounds.add( "mob.zombie", _pp("zombie2")); - sounds.add( "mob.zombie", _pp("zombie3")); - sounds.add( "mob.zombiedeath", _pp("zombiedeath")); - sounds.add( "mob.zombiehurt", _pp("zombiehurt1")); - sounds.add( "mob.zombiehurt", _pp("zombiehurt2")); - - sounds.add( "mob.skeleton", _pp("skeleton1")); - sounds.add( "mob.skeleton", _pp("skeleton2")); - sounds.add( "mob.skeleton", _pp("skeleton3")); - sounds.add( "mob.skeletonhurt", _pp("skeletonhurt1")); - sounds.add( "mob.skeletonhurt", _pp("skeletonhurt2")); - sounds.add( "mob.skeletonhurt", _pp("skeletonhurt3")); - sounds.add( "mob.skeletonhurt", _pp("skeletonhurt4")); - - sounds.add( "mob.spider", _pp("spider1")); - sounds.add( "mob.spider", _pp("spider2")); - sounds.add( "mob.spider", _pp("spider3")); - sounds.add( "mob.spiderdeath", _pp("spiderdeath")); - - sounds.add( "mob.creeper", _pp("creeper1")); - sounds.add( "mob.creeper", _pp("creeper2")); - sounds.add( "mob.creeper", _pp("creeper3")); - sounds.add( "mob.creeper", _pp("creeper4")); - sounds.add( "mob.creeperdeath", _pp("creeperdeath")); - - sounds.add( "mob.zombiepig.zpig", _pp("zpig1")); - sounds.add( "mob.zombiepig.zpig", _pp("zpig2")); - sounds.add( "mob.zombiepig.zpig", _pp("zpig3")); - sounds.add( "mob.zombiepig.zpig", _pp("zpig4")); - sounds.add( "mob.zombiepig.zpigangry", _pp("zpigangry1")); - sounds.add( "mob.zombiepig.zpigangry", _pp("zpigangry2")); - sounds.add( "mob.zombiepig.zpigangry", _pp("zpigangry3")); - sounds.add( "mob.zombiepig.zpigangry", _pp("zpigangry4")); - sounds.add( "mob.zombiepig.zpigdeath", _pp("zpigdeath")); - sounds.add( "mob.zombiepig.zpighurt", _pp("zpighurt1")); - sounds.add( "mob.zombiepig.zpighurt", _pp("zpighurt2")); -} - -void SoundEngine::enable( bool status ) -{ - soundSystem.enable(status); -} - -void SoundEngine::updateOptions() -{ - -} - -void SoundEngine::destroy() -{ - //if (loaded) soundSystem.cleanup(); -} - -void SoundEngine::update( Mob* player, float a ) -{ - if (/*!loaded || */options->sound == 0) return; - if (player == NULL) return; - - _x = player->xo + (player->x - player->xo) * a; - _y = player->yo + (player->y - player->yo) * a; - _z = player->zo + (player->z - player->zo) * a; - _yRot = player->yRotO + (player->yRot - player->yRotO) * a; - - soundSystem.setListenerAngle(_yRot); - //soundSystem.setListenerPos(_x, _y, _z); //@note: not used, since we translate all sounds to Player space -} - -float SoundEngine::_getVolumeMult( float x, float y, float z ) -{ - const float dx = x - _x; - const float dy = y - _y; - const float dz = z - _z; - const float dist = Mth::sqrt(dx*dx + dy*dy + dz*dz); - const float out = Mth::clamp(1.1f - dist*_invMaxDistance, -1.0f, 1.0f); - return out; -} - -void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) { - if ((volume *= options->sound) <= 0) return; - - volume = Mth::clamp(volume, 0.0f, 1.0f); - - SoundDesc sound; - if (sounds.get(name, sound)) { - soundSystem.playAt(sound, x-_x, y-_y, z-_z, volume, pitch); - } -} - -void SoundEngine::playUI(const std::string& name, float volume, float pitch) { - if ((volume *= options->sound) <= 0) return; - - volume = Mth::clamp(volume, 0.0f, 1.0f); - if (/*!loaded || */options->sound == 0 || volume <= 0) return; - - SoundDesc sound; - if (sounds.get(name, sound)) { - soundSystem.playAt(sound, 0, 0, 0, volume, pitch); - } -} - +#include "SoundEngine.h" +#include "../Options.h" +#include "../Minecraft.h" +#include "../../world/entity/Mob.h" + +/* + + File: MyOpenALSupport.c + Abstract: OpenAL-related support functions + Version: 1.4 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2009 Apple Inc. All Rights Reserved. + + + */ + +#import +#import +#import +#import + + +//ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq) +//{ +// static alBufferDataStaticProcPtr proc = NULL; +// +// if (proc == NULL) { +// proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic"); +// } +// +// if (proc) +// proc(bid, format, data, size, freq); +// +// return; +//} + +void* MyGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate) +{ + OSStatus err = noErr; + SInt64 theFileLengthInFrames = 0; + AudioStreamBasicDescription theFileFormat; + UInt32 thePropertySize = sizeof(theFileFormat); + ExtAudioFileRef extRef = NULL; + void* theData = NULL; + AudioStreamBasicDescription theOutputFormat; + + do { + // Open a file with ExtAudioFileOpen() + err = ExtAudioFileOpenURL(inFileURL, &extRef); + if(err) { printf("MyGetOpenALAudioData: ExtAudioFileOpenURL FAILED, Error = %ld\n", err); break; } + + // Get the audio data format + err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &theFileFormat); + if(err) { printf("MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileDataFormat) FAILED, Error = %ld\n", err); break; } + if (theFileFormat.mChannelsPerFrame > 2) { printf("MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); break;} + + // Set the client format to 16 bit signed integer (native-endian) data + // Maintain the channel count and sample rate of the original source format + theOutputFormat.mSampleRate = theFileFormat.mSampleRate; + theOutputFormat.mChannelsPerFrame = theFileFormat.mChannelsPerFrame; + + theOutputFormat.mFormatID = kAudioFormatLinearPCM; + theOutputFormat.mBytesPerPacket = 2 * theOutputFormat.mChannelsPerFrame; + theOutputFormat.mFramesPerPacket = 1; + theOutputFormat.mBytesPerFrame = 2 * theOutputFormat.mChannelsPerFrame; + theOutputFormat.mBitsPerChannel = 16; + theOutputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; + + // Set the desired client (output) data format + err = ExtAudioFileSetProperty(extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(theOutputFormat), &theOutputFormat); + if(err) { printf("MyGetOpenALAudioData: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) FAILED, Error = %ld\n", err); break; } + + // Get the total frame count + thePropertySize = sizeof(theFileLengthInFrames); + err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames); + if(err) { printf("MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileLengthFrames) FAILED, Error = %ld\n", err); break; } + + // Read all the data into memory + UInt32 dataSize = theFileLengthInFrames * theOutputFormat.mBytesPerFrame; + theData = (void*)new char[dataSize]; + if (theData) + { + AudioBufferList theDataBuffer; + theDataBuffer.mNumberBuffers = 1; + theDataBuffer.mBuffers[0].mDataByteSize = dataSize; + theDataBuffer.mBuffers[0].mNumberChannels = theOutputFormat.mChannelsPerFrame; + theDataBuffer.mBuffers[0].mData = theData; + + // Read the data into an AudioBufferList + err = ExtAudioFileRead(extRef, (UInt32*)&theFileLengthInFrames, &theDataBuffer); + if(err == noErr) + { + // success + *outDataSize = (ALsizei)dataSize; + *outDataFormat = (theOutputFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; + *outSampleRate = (ALsizei)theOutputFormat.mSampleRate; + } + else + { + // failure + free (theData); + theData = NULL; // make sure to return NULL + printf("MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", err); break; + } + } + } while(0); + + // Dispose the ExtAudioFileRef, it is no longer needed + if (extRef) ExtAudioFileDispose(extRef); + return theData; +} + + + +SoundEngine::SoundEngine( float maxDistance ) +: idCounter(0), + mc(0), + _x(0), + _y(0), + _z(0), + _yRot(0), + _invMaxDistance(1.0f / maxDistance) +{ +} + +SoundEngine::~SoundEngine() +{ +} + +SoundDesc SoundEngine::_pp(const std::string& fn) { + NSString* filename = [[NSString alloc] initWithUTF8String:fn.c_str()]; + NSString* path = [[NSBundle mainBundle] pathForResource:filename ofType:@"m4a"]; + [filename release]; + if (!path) + return SoundDesc(); + + CFURLRef fileURL = (CFURLRef)[[NSURL fileURLWithPath:path] retain]; + if (fileURL) { + static Stopwatch sw; + sw.start(); + + ALsizei size, freq; + ALenum format; + void* data = MyGetOpenALAudioData(fileURL, &size, &format, &freq); + + + CFRelease(fileURL); + sw.stop(); + sw.printEvery(1, "load sound " + fn + " :: "); + int channels = (format == AL_FORMAT_STEREO16 || format == AL_FORMAT_STEREO8)? 2 : 1; + int bytewidth= (format == AL_FORMAT_STEREO16 || format == AL_FORMAT_MONO16)? 2 : 1; + + return SoundDesc((char*)data, size, channels, bytewidth, freq); + } + return SoundDesc(); +} + +void SoundEngine::init( Minecraft* mc, Options* options ) +{ + this->mc = mc; + this->options = options; + + if (/*!loaded && */(options == NULL || (options->sound != 0 || options->music != 0))) { + loadLibrary(); + } + + sounds.add( "damage.fallbig", _pp("fallbig1")); + sounds.add( "damage.fallbig", _pp("fallbig2")); + sounds.add( "damage.fallsmall", _pp("fallsmall")); + sounds.add( "random.explode", _pp("explode")); + sounds.add( "random.splash", _pp("splash")); + sounds.add( "random.hurt", _pp("hurt")); + sounds.add( "random.pop", _pp("pop")); + sounds.add( "random.click", _pp("click")); + + sounds.add( "random.door_open", _pp("door_open")); + sounds.add( "random.door_close", _pp("door_close")); + + sounds.add( "random.bow", _pp("bow")); + sounds.add( "random.bowhit", _pp("bowhit1")); + sounds.add( "random.bowhit", _pp("bowhit2")); + sounds.add( "random.bowhit", _pp("bowhit3")); + sounds.add( "random.bowhit", _pp("bowhit4")); + + sounds.add( "random.glass", _pp("glass1")); + sounds.add( "random.glass", _pp("glass2")); + sounds.add( "random.glass", _pp("glass3")); + + sounds.add( "random.eat", _pp("eat1")); + sounds.add( "random.eat", _pp("eat2")); + sounds.add( "random.eat", _pp("eat3")); + sounds.add( "random.fuse", _pp("fuse")); + + sounds.add( "step.cloth", _pp("cloth1")); + sounds.add( "step.cloth", _pp("cloth2")); + sounds.add( "step.cloth", _pp("cloth3")); + sounds.add( "step.cloth", _pp("cloth4")); + + sounds.add( "step.grass", _pp("grass1")); + sounds.add( "step.grass", _pp("grass2")); + sounds.add( "step.grass", _pp("grass3")); + sounds.add( "step.grass", _pp("grass4")); + + //new SoundId(R.raw.gravel1, _pp("step.gravel"), + sounds.add( "step.gravel", _pp("gravel2")); + sounds.add( "step.gravel", _pp("gravel3")); + sounds.add( "step.gravel", _pp("gravel4")); + + sounds.add( "step.sand", _pp("sand1")); + sounds.add( "step.sand", _pp("sand2")); + sounds.add( "step.sand", _pp("sand3")); + sounds.add( "step.sand", _pp("sand4")); + + sounds.add( "step.stone", _pp("stone1")); + sounds.add( "step.stone", _pp("stone2")); + sounds.add( "step.stone", _pp("stone3")); + sounds.add( "step.stone", _pp("stone4")); + + sounds.add( "step.wood", _pp("wood1")); + sounds.add( "step.wood", _pp("wood2")); + sounds.add( "step.wood", _pp("wood3")); + sounds.add( "step.wood", _pp("wood4")); + + sounds.add( "mob.sheep", _pp("sheep1")); + sounds.add( "mob.sheep", _pp("sheep2")); + sounds.add( "mob.sheep", _pp("sheep3")); + + sounds.add( "mob.pig", _pp("pig1")); + sounds.add( "mob.pig", _pp("pig2")); + sounds.add( "mob.pig", _pp("pig3")); + sounds.add( "mob.pigdeath", _pp("pigdeath")); + + //sounds.add( "mob.chicken", _pp("chicken1")); + sounds.add( "mob.chicken", _pp("chicken2")); + sounds.add( "mob.chicken", _pp("chicken3")); + sounds.add( "mob.chickenhurt", _pp("chickenhurt1")); + sounds.add( "mob.chickenhurt", _pp("chickenhurt2")); + + sounds.add( "mob.cow", _pp("cow1")); + sounds.add( "mob.cow", _pp("cow2")); + sounds.add( "mob.cow", _pp("cow3")); + sounds.add( "mob.cow", _pp("cow4")); + sounds.add( "mob.cowhurt", _pp("cowhurt1")); + sounds.add( "mob.cowhurt", _pp("cowhurt2")); + sounds.add( "mob.cowhurt", _pp("cowhurt3")); + + sounds.add( "mob.zombie", _pp("zombie1")); + sounds.add( "mob.zombie", _pp("zombie2")); + sounds.add( "mob.zombie", _pp("zombie3")); + sounds.add( "mob.zombiedeath", _pp("zombiedeath")); + sounds.add( "mob.zombiehurt", _pp("zombiehurt1")); + sounds.add( "mob.zombiehurt", _pp("zombiehurt2")); + + sounds.add( "mob.skeleton", _pp("skeleton1")); + sounds.add( "mob.skeleton", _pp("skeleton2")); + sounds.add( "mob.skeleton", _pp("skeleton3")); + sounds.add( "mob.skeletonhurt", _pp("skeletonhurt1")); + sounds.add( "mob.skeletonhurt", _pp("skeletonhurt2")); + sounds.add( "mob.skeletonhurt", _pp("skeletonhurt3")); + sounds.add( "mob.skeletonhurt", _pp("skeletonhurt4")); + + sounds.add( "mob.spider", _pp("spider1")); + sounds.add( "mob.spider", _pp("spider2")); + sounds.add( "mob.spider", _pp("spider3")); + sounds.add( "mob.spiderdeath", _pp("spiderdeath")); + + sounds.add( "mob.creeper", _pp("creeper1")); + sounds.add( "mob.creeper", _pp("creeper2")); + sounds.add( "mob.creeper", _pp("creeper3")); + sounds.add( "mob.creeper", _pp("creeper4")); + sounds.add( "mob.creeperdeath", _pp("creeperdeath")); + + sounds.add( "mob.zombiepig.zpig", _pp("zpig1")); + sounds.add( "mob.zombiepig.zpig", _pp("zpig2")); + sounds.add( "mob.zombiepig.zpig", _pp("zpig3")); + sounds.add( "mob.zombiepig.zpig", _pp("zpig4")); + sounds.add( "mob.zombiepig.zpigangry", _pp("zpigangry1")); + sounds.add( "mob.zombiepig.zpigangry", _pp("zpigangry2")); + sounds.add( "mob.zombiepig.zpigangry", _pp("zpigangry3")); + sounds.add( "mob.zombiepig.zpigangry", _pp("zpigangry4")); + sounds.add( "mob.zombiepig.zpigdeath", _pp("zpigdeath")); + sounds.add( "mob.zombiepig.zpighurt", _pp("zpighurt1")); + sounds.add( "mob.zombiepig.zpighurt", _pp("zpighurt2")); +} + +void SoundEngine::enable( bool status ) +{ + soundSystem.enable(status); +} + +void SoundEngine::updateOptions() +{ + +} + +void SoundEngine::destroy() +{ + //if (loaded) soundSystem.cleanup(); +} + +void SoundEngine::update( Mob* player, float a ) +{ + if (/*!loaded || */options->sound == 0) return; + if (player == NULL) return; + + _x = player->xo + (player->x - player->xo) * a; + _y = player->yo + (player->y - player->yo) * a; + _z = player->zo + (player->z - player->zo) * a; + _yRot = player->yRotO + (player->yRot - player->yRotO) * a; + + soundSystem.setListenerAngle(_yRot); + //soundSystem.setListenerPos(_x, _y, _z); //@note: not used, since we translate all sounds to Player space +} + +float SoundEngine::_getVolumeMult( float x, float y, float z ) +{ + const float dx = x - _x; + const float dy = y - _y; + const float dz = z - _z; + const float dist = Mth::sqrt(dx*dx + dy*dy + dz*dz); + const float out = Mth::clamp(1.1f - dist*_invMaxDistance, -1.0f, 1.0f); + return out; +} + +void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) { + if ((volume *= options->sound) <= 0) return; + + volume = Mth::clamp(volume, 0.0f, 1.0f); + + SoundDesc sound; + if (sounds.get(name, sound)) { + soundSystem.playAt(sound, x-_x, y-_y, z-_z, volume, pitch); + } +} + +void SoundEngine::playUI(const std::string& name, float volume, float pitch) { + if ((volume *= options->sound) <= 0) return; + + volume = Mth::clamp(volume, 0.0f, 1.0f); + if (/*!loaded || */options->sound == 0 || volume <= 0) return; + + SoundDesc sound; + if (sounds.get(name, sound)) { + soundSystem.playAt(sound, 0, 0, 0, volume, pitch); + } +} + diff --git a/src/client/sound/SoundRepository.h b/src/client/sound/SoundRepository.h index 6c2b382..d5fcf27 100755 --- a/src/client/sound/SoundRepository.h +++ b/src/client/sound/SoundRepository.h @@ -1,59 +1,59 @@ -#ifndef NET_MINECRAFT_CLIENT_SOUND__SoundRepository_H__ -#define NET_MINECRAFT_CLIENT_SOUND__SoundRepository_H__ - -#include -#include -#include "Sound.h" -#include "../../util/Mth.h" -#include "../../platform/log.h" - -class SoundRepository -{ - typedef std::vector SoundList; - typedef std::map SoundMap; - -public: - ~SoundRepository() { -#ifdef __APPLE__ - SoundMap::iterator it = map.begin(); - for (; it != map.end(); ++it) { - SoundList& list = it->second; - for (unsigned int j = 0; j < list.size(); ++j) - list[j].destroy(); - } -#endif - } - - bool get(const std::string& name, SoundDesc& sound) { - SoundMap::iterator it = map.find(name); - if (it == map.end()) { - LOGI("Couldn't find a sound with id: %s\n", name.c_str()); - return false; - } - sound = it->second[Mth::random(it->second.size())]; - return true; - } - - void add(const std::string& name, const SoundDesc& sound) { - if (!sound.isValid()) return; - SoundMap::iterator it = map.find(name); - if (it == map.end()) { - SoundList list; - list.push_back( sound ); - map.insert( make_pair(name, list) ); - } else { - it->second.push_back(sound); - } - } - - void add(const std::string& name, SoundDesc& sound) { - if (!sound.isValid()) return; - add(name, const_cast(sound)); - sound.name = name; - } - -private: - SoundMap map; -}; - -#endif /*NET_MINECRAFT_CLIENT_SOUND__SoundRepository_H__*/ +#ifndef NET_MINECRAFT_CLIENT_SOUND__SoundRepository_H__ +#define NET_MINECRAFT_CLIENT_SOUND__SoundRepository_H__ + +#include +#include +#include "Sound.h" +#include "../../util/Mth.h" +#include "../../platform/log.h" + +class SoundRepository +{ + typedef std::vector SoundList; + typedef std::map SoundMap; + +public: + ~SoundRepository() { +#ifdef __APPLE__ + SoundMap::iterator it = map.begin(); + for (; it != map.end(); ++it) { + SoundList& list = it->second; + for (unsigned int j = 0; j < list.size(); ++j) + list[j].destroy(); + } +#endif + } + + bool get(const std::string& name, SoundDesc& sound) { + SoundMap::iterator it = map.find(name); + if (it == map.end()) { + LOGI("Couldn't find a sound with id: %s\n", name.c_str()); + return false; + } + sound = it->second[Mth::random(it->second.size())]; + return true; + } + + void add(const std::string& name, const SoundDesc& sound) { + if (!sound.isValid()) return; + SoundMap::iterator it = map.find(name); + if (it == map.end()) { + SoundList list; + list.push_back( sound ); + map.insert( make_pair(name, list) ); + } else { + it->second.push_back(sound); + } + } + + void add(const std::string& name, SoundDesc& sound) { + if (!sound.isValid()) return; + add(name, const_cast(sound)); + sound.name = name; + } + +private: + SoundMap map; +}; + +#endif /*NET_MINECRAFT_CLIENT_SOUND__SoundRepository_H__*/ diff --git a/src/locale/I18n.cpp b/src/locale/I18n.cpp index 380134c..e79537d 100755 --- a/src/locale/I18n.cpp +++ b/src/locale/I18n.cpp @@ -1,121 +1,121 @@ -#include "I18n.h" -#include -#include "../AppPlatform.h" -#include "../util/StringUtils.h" -#include "../world/level/tile/Tile.h" -#include "../world/item/ItemInstance.h" -#include - -I18n::Map I18n::_strings; - -void I18n::loadLanguage( AppPlatform* platform, const std::string& languageCode ) -{ - _strings.clear(); - fillTranslations(platform, "lang/en_US.lang", true); - - if (languageCode != "en_US") - fillTranslations(platform, "lang/" + languageCode + ".lang", true); -} - -bool I18n::get( const std::string& id, std::string& out ) { - Map::const_iterator cit = _strings.find(id); - if (cit != _strings.end()) { - out = cit->second; - return true; - } - return false; -} - -std::string I18n::get( const std::string& id ) -{ - Map::const_iterator cit = _strings.find(id); - if (cit != _strings.end()) - return cit->second; - - return id + '<';//lang.getElement(id); -} - -void I18n::fillTranslations( AppPlatform* platform, const std::string& filename, bool overwrite ) -{ - BinaryBlob blob = platform->readAssetFile(filename); - if (!blob.data || blob.size <= 0) - return; - - std::string data((const char*)blob.data, blob.size); - std::stringstream fin(data, std::ios_base::in); - - std::string line; - while( std::getline(fin, line) ) { - int spos = line.find('='); - if (spos == std::string::npos) - continue; - - std::string key = Util::stringTrim(line.substr(0, spos)); - Map::const_iterator cit = _strings.find(key); - if (!overwrite && cit != _strings.end()) - continue; - - std::string value = Util::stringTrim(line.substr(spos + 1)); - _strings.insert( std::make_pair(key, value ) ); - } - - delete[] blob.data; -} - -std::string I18n::getDescriptionString( const ItemInstance& item ) -{ - // Convert to lower. Normally std::transform would be used, but tolower might be - // implemented with a macro in certain C-implementations -> messing stuff up - const std::string desc = item.getDescriptionId(); - - std::string s = desc; - std::string trans; - - // Handle special cases - if (item.id == Tile::cloth->id) - return get(item.getAuxValue()? "desc.wool" : "desc.woolstring"); - else if (item.id == Tile::fenceGate->id) - return I18n::get("desc.fence"); - else if (item.id == Tile::stoneSlabHalf->id) - return I18n::get("desc.slab"); - - for (unsigned int i = 0; i < s.length(); ++i) - s[i] = ::tolower(s[i]); - - // Replace item./tile. with desc., hopefully it's enough - if (s[0] == 't') s = Util::stringReplace(s, "tile.", "desc."); - if (s[0] == 'i') s = Util::stringReplace(s, "item.", "desc."); - if (I18n::get(s, trans)) - return trans; - - // Remove all materials from the identifier, since swordWood should - // be read as just sword - const char* materials[] = { - "wood", - "iron", - "stone", - "diamond", - "gold", - "brick", - "emerald", - "lapis", - "cloth" - }; - - Util::removeAll(s, materials, sizeof(materials) / sizeof(const char*)); - if (I18n::get(s, trans)) - return trans; - - std::string mapping[] = { - "tile.workbench", "craftingtable", - }; - const char numMappings = sizeof(mapping) / sizeof(std::string); - for (int i = 0; i < numMappings; i += 2) { - if (desc == mapping[i]) { - if (I18n::get("desc." + mapping[i+1], trans)) - return trans; - } - } - - return desc + " : couldn't find desc"; -} +#include "I18n.h" +#include +#include "../AppPlatform.h" +#include "../util/StringUtils.h" +#include "../world/level/tile/Tile.h" +#include "../world/item/ItemInstance.h" +#include + +I18n::Map I18n::_strings; + +void I18n::loadLanguage( AppPlatform* platform, const std::string& languageCode ) +{ + _strings.clear(); + fillTranslations(platform, "lang/en_US.lang", true); + + if (languageCode != "en_US") + fillTranslations(platform, "lang/" + languageCode + ".lang", true); +} + +bool I18n::get( const std::string& id, std::string& out ) { + Map::const_iterator cit = _strings.find(id); + if (cit != _strings.end()) { + out = cit->second; + return true; + } + return false; +} + +std::string I18n::get( const std::string& id ) +{ + Map::const_iterator cit = _strings.find(id); + if (cit != _strings.end()) + return cit->second; + + return id + '<';//lang.getElement(id); +} + +void I18n::fillTranslations( AppPlatform* platform, const std::string& filename, bool overwrite ) +{ + BinaryBlob blob = platform->readAssetFile(filename); + if (!blob.data || blob.size <= 0) + return; + + std::string data((const char*)blob.data, blob.size); + std::stringstream fin(data, std::ios_base::in); + + std::string line; + while( std::getline(fin, line) ) { + int spos = line.find('='); + if (spos == std::string::npos) + continue; + + std::string key = Util::stringTrim(line.substr(0, spos)); + Map::const_iterator cit = _strings.find(key); + if (!overwrite && cit != _strings.end()) + continue; + + std::string value = Util::stringTrim(line.substr(spos + 1)); + _strings.insert( std::make_pair(key, value ) ); + } + + delete[] blob.data; +} + +std::string I18n::getDescriptionString( const ItemInstance& item ) +{ + // Convert to lower. Normally std::transform would be used, but tolower might be + // implemented with a macro in certain C-implementations -> messing stuff up + const std::string desc = item.getDescriptionId(); + + std::string s = desc; + std::string trans; + + // Handle special cases + if (item.id == Tile::cloth->id) + return get(item.getAuxValue()? "desc.wool" : "desc.woolstring"); + else if (item.id == Tile::fenceGate->id) + return I18n::get("desc.fence"); + else if (item.id == Tile::stoneSlabHalf->id) + return I18n::get("desc.slab"); + + for (unsigned int i = 0; i < s.length(); ++i) + s[i] = ::tolower(s[i]); + + // Replace item./tile. with desc., hopefully it's enough + if (s[0] == 't') s = Util::stringReplace(s, "tile.", "desc."); + if (s[0] == 'i') s = Util::stringReplace(s, "item.", "desc."); + if (I18n::get(s, trans)) + return trans; + + // Remove all materials from the identifier, since swordWood should + // be read as just sword + const char* materials[] = { + "wood", + "iron", + "stone", + "diamond", + "gold", + "brick", + "emerald", + "lapis", + "cloth" + }; + + Util::removeAll(s, materials, sizeof(materials) / sizeof(const char*)); + if (I18n::get(s, trans)) + return trans; + + std::string mapping[] = { + "tile.workbench", "craftingtable", + }; + const char numMappings = sizeof(mapping) / sizeof(std::string); + for (int i = 0; i < numMappings; i += 2) { + if (desc == mapping[i]) { + if (I18n::get("desc." + mapping[i+1], trans)) + return trans; + } + } + + return desc + " : couldn't find desc"; +} diff --git a/src/locale/I18n.h b/src/locale/I18n.h index cc4d432..cc0c27a 100755 --- a/src/locale/I18n.h +++ b/src/locale/I18n.h @@ -1,31 +1,31 @@ -#ifndef NET_MINECRAFT_LOCALE__I18n_H__ -#define NET_MINECRAFT_LOCALE__I18n_H__ - -//package net.minecraft.locale; - -#include -#include - -class AppPlatform; -class ItemInstance; - -class I18n -{ - typedef std::map Map; -public: - static void loadLanguage(AppPlatform* platform, const std::string& languageCode); - - static bool get(const std::string& id, std::string& out); - static std::string get(const std::string& id); - - //static std::string get(const std::string& id, Object... args) { - // return lang.getElement(id, args); - //} - static std::string getDescriptionString( const ItemInstance& item ); - -private: - static void fillTranslations(AppPlatform* platform, const std::string& filename, bool overwrite); - static Map _strings; -}; - -#endif /*NET_MINECRAFT_LOCALE__I18n_H__*/ +#ifndef NET_MINECRAFT_LOCALE__I18n_H__ +#define NET_MINECRAFT_LOCALE__I18n_H__ + +//package net.minecraft.locale; + +#include +#include + +class AppPlatform; +class ItemInstance; + +class I18n +{ + typedef std::map Map; +public: + static void loadLanguage(AppPlatform* platform, const std::string& languageCode); + + static bool get(const std::string& id, std::string& out); + static std::string get(const std::string& id); + + //static std::string get(const std::string& id, Object... args) { + // return lang.getElement(id, args); + //} + static std::string getDescriptionString( const ItemInstance& item ); + +private: + static void fillTranslations(AppPlatform* platform, const std::string& filename, bool overwrite); + static Map _strings; +}; + +#endif /*NET_MINECRAFT_LOCALE__I18n_H__*/ diff --git a/src/main.cpp b/src/main.cpp index 885f4bc..6a5eb3e 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,46 +1,46 @@ -/** - Entry point for cross compilation. - - This is ugly, yes. But it will change in the "half near" future - to a more correct system of solving the cross-platform entry - point "problem" */ - -#define _SECURE_SCL 0 - -// #ifdef WIN32 -// #include "vld.h" -// #endif - -#include "platform/log.h" - -#ifdef WIN32 -#endif -#ifdef ANDROID -#endif - - -#include "NinecraftApp.h" -#define MAIN_CLASS NinecraftApp - -// #ifdef PLATFORM_WINDOWS -// #include "main_win32.h" -// #endif - - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - #include "main_glfw.h" -#endif - -#ifdef PLATFORM_ANDROID - #ifdef PRE_ANDROID23 - #include "main_android_java.h" - #else - #include "main_android.h" - #endif -#endif - -#ifdef PLATFORM_RPI - #include "main_rpi.h" -#endif - - +/** + Entry point for cross compilation. + + This is ugly, yes. But it will change in the "half near" future + to a more correct system of solving the cross-platform entry + point "problem" */ + +#define _SECURE_SCL 0 + +// #ifdef WIN32 +// #include "vld.h" +// #endif + +#include "platform/log.h" + +#ifdef WIN32 +#endif +#ifdef ANDROID +#endif + + +#include "NinecraftApp.h" +#define MAIN_CLASS NinecraftApp + +// #ifdef PLATFORM_WINDOWS +// #include "main_win32.h" +// #endif + + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + #include "main_glfw.h" +#endif + +#ifdef PLATFORM_ANDROID + #ifdef PRE_ANDROID23 + #include "main_android_java.h" + #else + #include "main_android.h" + #endif +#endif + +#ifdef PLATFORM_RPI + #include "main_rpi.h" +#endif + + diff --git a/src/main_android.cpp b/src/main_android.cpp index dc47f35..074b3b3 100755 --- a/src/main_android.cpp +++ b/src/main_android.cpp @@ -1,224 +1,224 @@ -#include "App.h" -#include "AppPlatform_android.h" -#include - -// Horrible, I know. / A -#ifndef MAIN_CLASS -#include "main.cpp" -#endif - -#include - -// References for JNI -static pthread_mutex_t g_activityMutex = PTHREAD_MUTEX_INITIALIZER; -static jobject g_pActivity = 0; -static AppPlatform_android appPlatform; - -static void setupExternalPath(struct android_app* state, MAIN_CLASS* app) -{ - LOGI("setupExternalPath"); - - JNIEnv* env = state->activity->env; - state->activity->vm->AttachCurrentThread(&env, NULL); - - if (env) - { - LOGI("Environment exists"); - } - // try appspecific external directory first - jobject activity = state->activity->clazz; - jclass activityClass = env->GetObjectClass(activity); - jmethodID getExternalFilesDir = env->GetMethodID(activityClass, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); - - jobject file = NULL; - if (getExternalFilesDir != NULL) { - file = env->CallObjectMethod(activity, getExternalFilesDir, NULL); - } - - if (file == NULL) { - // Fallback to the legacy shared storage directory - jclass clazz = env->FindClass("android/os/Environment"); - jmethodID method = env->GetStaticMethodID(clazz, "getExternalStorageDirectory", "()Ljava/io/File;"); - if (env->ExceptionOccurred()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } - file = env->CallStaticObjectMethod(clazz, method); - } - - if (!file) { - LOGI("Failed to get external storage file object, using current working dir"); - app->externalStoragePath = "."; - app->externalCacheStoragePath = "."; - return; - } - - jclass fileClass = env->GetObjectClass(file); - jmethodID fileMethod = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"); - jobject pathString = env->CallObjectMethod(file, fileMethod); - - const char* str = env->GetStringUTFChars((jstring) pathString, NULL); - app->externalStoragePath = str; - app->externalCacheStoragePath = str; - LOGI("%s", str); - - // ensure the process working directory is set to a writable location - // on Android the default cwd may be '/' which isn't writable. By chdir'ing - // to the external storage path we make relative fopen calls (e.g. "options.txt") - // succeed and persist across launches, fixing the "options never save" bug. - if (chdir(str) != 0) { - LOGI("chdir to %s failed: %s", str, strerror(errno)); - } - - env->ReleaseStringUTFChars((jstring)pathString, str); - - // We're done, detach! - state->activity->vm->DetachCurrentThread(); -} - -extern "C" { -JNIEXPORT jint JNICALL - JNI_OnLoad( JavaVM * vm, void * reserved ) - { - pthread_mutex_init(&g_activityMutex, 0); - pthread_mutex_lock(&g_activityMutex); - - LOGI("Entering OnLoad %d\n", pthread_self()); - return appPlatform.init(vm); - } - - // Register/save a reference to the java main activity instance - JNIEXPORT void JNICALL - Java_com_mojang_minecraftpe_MainActivity_nativeRegisterThis(JNIEnv* env, jobject clazz) { - LOGI("@RegisterThis %d\n", pthread_self()); - g_pActivity = (jobject)env->NewGlobalRef( clazz ); - - pthread_mutex_unlock(&g_activityMutex); - } - - // Unregister/delete the reference to the java main activity instance - JNIEXPORT void JNICALL - Java_com_mojang_minecraftpe_MainActivity_nativeUnregisterThis(JNIEnv* env, jobject clazz) { - LOGI("@UnregisterThis %d\n", pthread_self()); - env->DeleteGlobalRef( g_pActivity ); - g_pActivity = 0; - - pthread_mutex_destroy(&g_activityMutex); - } - - JNIEXPORT void JNICALL - Java_com_mojang_minecraftpe_MainActivity_nativeStopThis(JNIEnv* env, jobject clazz) { - LOGI("Lost Focus!"); - } -} - -static void internal_process_input(struct android_app* app, struct android_poll_source* source) { - AInputEvent* event = NULL; - if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { - LOGV("New input event: type=%d\n", AInputEvent_getType(event)); - bool isBackButtonDown = AKeyEvent_getKeyCode(event) == AKEYCODE_BACK && AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN; - if(!(appPlatform.isKeyboardVisible() && isBackButtonDown)) { - if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { - return; - } - } - int32_t handled = 0; - if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); - AInputQueue_finishEvent(app->inputQueue, event, handled); - } else { - LOGE("Failure reading next input event: %s\n", strerror(errno)); - } -} - -void -android_main( struct android_app* state ) -{ - struct ENGINE engine; - - // Make sure glue isn't stripped. - app_dummy(); - - memset( (void*)&engine, 0, sizeof(engine) ); - state->userData = (void*)&engine; - state->onAppCmd = engine_handle_cmd; - state->onInputEvent = engine_handle_input; - state->destroyRequested = 0; - - pthread_mutex_lock(&g_activityMutex); - appPlatform.instance = g_pActivity; - pthread_mutex_unlock(&g_activityMutex); - - appPlatform.initConsts(); - - //LOGI("socket-stuff\n"); - //socketDesc = initSocket(1999); - - App* app = new MAIN_CLASS(); - - engine.userApp = app; - engine.app = state; - engine.is_inited = false; - engine.appContext.doRender = true; - engine.appContext.platform = &appPlatform; - - setupExternalPath(state, (MAIN_CLASS*)app); - - if( state->savedState != NULL ) - { - // We are starting with a previous saved state; restore from it. - app->loadState(state->savedState, state->savedStateSize); - } - - bool inited = false; - bool teardownPhase = false; - appPlatform._nativeActivity = state->activity; - // our 'main loop' - while( 1 ) - { - // Read all pending events. - int ident; - int events; - struct android_poll_source* source; - - while( (ident = ALooper_pollAll( 0, NULL, &events, (void**)&source) ) >= 0 ) - { - // Process this event. - // This will call the function pointer android_app::onInputEvent() which in our case is - // engine_handle_input() - if( source != NULL ) { - if(source->id == 2) { - // Back button is intercepted by the ime on android 4.1/4.2 resulting in the application stopping to respond. - internal_process_input( state, source ); - } else { - source->process( state, source ); - } - } - - } - // Check if we are exiting. - if( state->destroyRequested ) - { - //engine_term_display( &engine ); - delete app; - return; - } - - if (!inited && engine.is_inited) { - app->init(engine.appContext); - app->setSize(engine.width, engine.height); - inited = true; - } - - if (inited && engine.is_inited && engine.has_focus) { - app->update(); - } else { - sleepMs(50); - } - - if (!teardownPhase && app->wantToQuit()) { - teardownPhase = true; - LOGI("tearing down!"); - ANativeActivity_finish(state->activity); - } - } -} +#include "App.h" +#include "AppPlatform_android.h" +#include + +// Horrible, I know. / A +#ifndef MAIN_CLASS +#include "main.cpp" +#endif + +#include + +// References for JNI +static pthread_mutex_t g_activityMutex = PTHREAD_MUTEX_INITIALIZER; +static jobject g_pActivity = 0; +static AppPlatform_android appPlatform; + +static void setupExternalPath(struct android_app* state, MAIN_CLASS* app) +{ + LOGI("setupExternalPath"); + + JNIEnv* env = state->activity->env; + state->activity->vm->AttachCurrentThread(&env, NULL); + + if (env) + { + LOGI("Environment exists"); + } + // try appspecific external directory first + jobject activity = state->activity->clazz; + jclass activityClass = env->GetObjectClass(activity); + jmethodID getExternalFilesDir = env->GetMethodID(activityClass, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); + + jobject file = NULL; + if (getExternalFilesDir != NULL) { + file = env->CallObjectMethod(activity, getExternalFilesDir, NULL); + } + + if (file == NULL) { + // Fallback to the legacy shared storage directory + jclass clazz = env->FindClass("android/os/Environment"); + jmethodID method = env->GetStaticMethodID(clazz, "getExternalStorageDirectory", "()Ljava/io/File;"); + if (env->ExceptionOccurred()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + file = env->CallStaticObjectMethod(clazz, method); + } + + if (!file) { + LOGI("Failed to get external storage file object, using current working dir"); + app->externalStoragePath = "."; + app->externalCacheStoragePath = "."; + return; + } + + jclass fileClass = env->GetObjectClass(file); + jmethodID fileMethod = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"); + jobject pathString = env->CallObjectMethod(file, fileMethod); + + const char* str = env->GetStringUTFChars((jstring) pathString, NULL); + app->externalStoragePath = str; + app->externalCacheStoragePath = str; + LOGI("%s", str); + + // ensure the process working directory is set to a writable location + // on Android the default cwd may be '/' which isn't writable. By chdir'ing + // to the external storage path we make relative fopen calls (e.g. "options.txt") + // succeed and persist across launches, fixing the "options never save" bug. + if (chdir(str) != 0) { + LOGI("chdir to %s failed: %s", str, strerror(errno)); + } + + env->ReleaseStringUTFChars((jstring)pathString, str); + + // We're done, detach! + state->activity->vm->DetachCurrentThread(); +} + +extern "C" { +JNIEXPORT jint JNICALL + JNI_OnLoad( JavaVM * vm, void * reserved ) + { + pthread_mutex_init(&g_activityMutex, 0); + pthread_mutex_lock(&g_activityMutex); + + LOGI("Entering OnLoad %d\n", pthread_self()); + return appPlatform.init(vm); + } + + // Register/save a reference to the java main activity instance + JNIEXPORT void JNICALL + Java_com_mojang_minecraftpe_MainActivity_nativeRegisterThis(JNIEnv* env, jobject clazz) { + LOGI("@RegisterThis %d\n", pthread_self()); + g_pActivity = (jobject)env->NewGlobalRef( clazz ); + + pthread_mutex_unlock(&g_activityMutex); + } + + // Unregister/delete the reference to the java main activity instance + JNIEXPORT void JNICALL + Java_com_mojang_minecraftpe_MainActivity_nativeUnregisterThis(JNIEnv* env, jobject clazz) { + LOGI("@UnregisterThis %d\n", pthread_self()); + env->DeleteGlobalRef( g_pActivity ); + g_pActivity = 0; + + pthread_mutex_destroy(&g_activityMutex); + } + + JNIEXPORT void JNICALL + Java_com_mojang_minecraftpe_MainActivity_nativeStopThis(JNIEnv* env, jobject clazz) { + LOGI("Lost Focus!"); + } +} + +static void internal_process_input(struct android_app* app, struct android_poll_source* source) { + AInputEvent* event = NULL; + if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { + LOGV("New input event: type=%d\n", AInputEvent_getType(event)); + bool isBackButtonDown = AKeyEvent_getKeyCode(event) == AKEYCODE_BACK && AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN; + if(!(appPlatform.isKeyboardVisible() && isBackButtonDown)) { + if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { + return; + } + } + int32_t handled = 0; + if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); + AInputQueue_finishEvent(app->inputQueue, event, handled); + } else { + LOGE("Failure reading next input event: %s\n", strerror(errno)); + } +} + +void +android_main( struct android_app* state ) +{ + struct ENGINE engine; + + // Make sure glue isn't stripped. + app_dummy(); + + memset( (void*)&engine, 0, sizeof(engine) ); + state->userData = (void*)&engine; + state->onAppCmd = engine_handle_cmd; + state->onInputEvent = engine_handle_input; + state->destroyRequested = 0; + + pthread_mutex_lock(&g_activityMutex); + appPlatform.instance = g_pActivity; + pthread_mutex_unlock(&g_activityMutex); + + appPlatform.initConsts(); + + //LOGI("socket-stuff\n"); + //socketDesc = initSocket(1999); + + App* app = new MAIN_CLASS(); + + engine.userApp = app; + engine.app = state; + engine.is_inited = false; + engine.appContext.doRender = true; + engine.appContext.platform = &appPlatform; + + setupExternalPath(state, (MAIN_CLASS*)app); + + if( state->savedState != NULL ) + { + // We are starting with a previous saved state; restore from it. + app->loadState(state->savedState, state->savedStateSize); + } + + bool inited = false; + bool teardownPhase = false; + appPlatform._nativeActivity = state->activity; + // our 'main loop' + while( 1 ) + { + // Read all pending events. + int ident; + int events; + struct android_poll_source* source; + + while( (ident = ALooper_pollAll( 0, NULL, &events, (void**)&source) ) >= 0 ) + { + // Process this event. + // This will call the function pointer android_app::onInputEvent() which in our case is + // engine_handle_input() + if( source != NULL ) { + if(source->id == 2) { + // Back button is intercepted by the ime on android 4.1/4.2 resulting in the application stopping to respond. + internal_process_input( state, source ); + } else { + source->process( state, source ); + } + } + + } + // Check if we are exiting. + if( state->destroyRequested ) + { + //engine_term_display( &engine ); + delete app; + return; + } + + if (!inited && engine.is_inited) { + app->init(engine.appContext); + app->setSize(engine.width, engine.height); + inited = true; + } + + if (inited && engine.is_inited && engine.has_focus) { + app->update(); + } else { + sleepMs(50); + } + + if (!teardownPhase && app->wantToQuit()) { + teardownPhase = true; + LOGI("tearing down!"); + ANativeActivity_finish(state->activity); + } + } +} diff --git a/src/main_android.h b/src/main_android.h index e31102c..8d6e347 100755 --- a/src/main_android.h +++ b/src/main_android.h @@ -1,589 +1,589 @@ -#ifndef MAIN_CLASS -#error "Error: MAIN_CLASS must have been defined to your main class (e.g. #define MAIN_CLASS MyApp)" -#endif - -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#define TAG "MAIN_CLASS" - -#include "platform/log.h" -#include "platform/input/Controller.h" -#include "platform/input/Keyboard.h" -#include "platform/input/Mouse.h" -#include "platform/input/Multitouch.h" - -#include "EglConfigPrinter.h" - -const int BroadcastPort = 9991; - -/** - * Shared state for our app. - */ -struct ENGINE -{ - struct android_app* app; - - int render; - EGLDisplay display; - EGLSurface surface; - EGLContext context; - int width; - int height; - int has_focus; - App* userApp; - bool is_inited; - //bool init_gl; - struct AppContext appContext; -}; - -/** - * Initialize an EGL context for the current display. - */ -static -int -engine_init_display( struct ENGINE* engine ) -{ - LOGI("1) Initing display. Get display\n"); - // initialize OpenGL ES 2.0 and EGL - const EGLint attribs[] = \ - { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - //EGL_BUFFER_SIZE, 32, - //EGL_BLUE_SIZE, 5, - //EGL_GREEN_SIZE, 6, - //EGL_RED_SIZE, 5, - ///EGL_ALPHA_SIZE, 8, - //0x3098, 1, - //EGL_TRANSPARENT_TYPE, EGL_TRANSPARENT_RGB, - //EGL_ALPHA_SIZE, 8,//5, - EGL_DEPTH_SIZE, 16, //8 - //EGL_STENCIL_SIZE, 8, - //EGL_DEPTH_SIZE, 16, - //EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, - EGL_NONE - }; - - EGLint w, h, format; - EGLint numConfigs; - EGLConfig config; - EGLSurface surface; - EGLContext context; - - EGLDisplay display = eglGetDisplay( EGL_DEFAULT_DISPLAY ); - - LOGI("2) Initialize egl\n"); - - if( display == NULL ) - { - LOGW( "!!! NO DISPLAY !!! eglGetDisplay" ); - } - - eglInitialize( display, 0, 0 ); - //EGLConfigPrinter::printAllConfigs(display); - LOGI("3) Choose config\n"); - - /* Here, the application chooses the configuration it desires. In this - * sample, we have a very simplified selection process, where we pick - * the first EGLConfig that matches our criteria */ - eglChooseConfig( display, attribs, &config, 1, &numConfigs ); - LOGI("4) Get config attributes\n"); - - /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is - * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). - * As soon as we picked a EGLConfig, we can safely reconfigure the - * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ - eglGetConfigAttrib( display, config, EGL_NATIVE_VISUAL_ID, &format ); - LOGI("5) Choose config, set window geometry\n"); - - ANativeWindow_setBuffersGeometry( engine->app->window, 0, 0, format ); - - eglChooseConfig( display, attribs, NULL, 0, &numConfigs ); - - EGLConfig* configs = new EGLConfig[numConfigs]; - eglChooseConfig(display, attribs, configs, numConfigs, &numConfigs); - - for (int i = 0; i < numConfigs; ++i) { - EGLSurface surf = eglCreateWindowSurface( display, configs[i], engine->app->window, NULL ); - if (surf != EGL_NO_SURFACE) { - config = configs[i]; - surface = surf; - break; - } - } - - delete[] configs; - - - //surface = eglCreateWindowSurface( display, config, engine->app->window, NULL ); - LOGI("6) Creating context\n"); - - context = eglCreateContext( display, config, NULL, NULL ); - LOGI("7) Make current\n"); - - if( eglMakeCurrent( display, surface, surface, context ) == EGL_FALSE ) - { - LOGW("Unable to eglMakeCurrent"); - return -1; - } - LOGI("8) Query stats and set values\n"); - - eglQuerySurface( display, surface, EGL_WIDTH, &w ); - eglQuerySurface( display, surface, EGL_HEIGHT, &h ); - - if (w < h) { - int tmp = w; - w = h; - h = tmp; - } - - engine->display = engine->appContext.display = display; - engine->context = engine->appContext.context = context; - engine->surface = engine->appContext.surface = surface; - engine->width = w; - engine->height = h; - //Minecraft::width = w; - //Minecraft::height = h; - LOGI("9) Set-up viewport\n"); - - // Initialize GL state. - glViewport( 0, 0, w, h ); - LOGI("X) Graphics set-up finished!\n"); - - // Don't need to reload graphics first time - if (engine->is_inited) { - engine->userApp->onGraphicsReset(engine->appContext); - engine->userApp->setSize(w, h); - } - - eglSwapInterval(eglGetCurrentDisplay(), 2); - engine->is_inited = true; - - return 0; -} - -/** - * Tear down the EGL context currently associated with the display. - */ -static -void -engine_term_display( struct ENGINE* engine ) -{ - //LOGI( "engine_term_display" ); - - if( engine->display != EGL_NO_DISPLAY ) - { - eglMakeCurrent( engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); - - if( engine->context != EGL_NO_CONTEXT ) - { - eglDestroyContext( engine->display, engine->context ); - } - - if( engine->surface != EGL_NO_SURFACE ) - { - eglDestroySurface( engine->display, engine->surface ); - } - - eglTerminate( engine->display ); - } - - engine->display = EGL_NO_DISPLAY; - engine->context = EGL_NO_CONTEXT; - engine->surface = EGL_NO_SURFACE; -} - -static int socketDesc; - -static int broadcastData(int port, void* msg, int msgLen) -{ - struct sockaddr_in broadcastAddr; - - broadcastAddr.sin_family = AF_INET; - broadcastAddr.sin_port = htons(port); - broadcastAddr.sin_addr.s_addr = inet_addr("192.168.0.255"); - - return sendto(socketDesc, msg, msgLen, 0, (struct sockaddr*)&broadcastAddr, sizeof(broadcastAddr)); -} - -static void mouseDown(int buttonId, int x, int y) { - int msg[] = {buttonId, 0, x, y}; - //broadcastData(BroadcastPort, msg, sizeof(msg)); - - Mouse::feed(buttonId, 1, x, y); -} - -static void mouseUp(int buttonId, int x, int y) { - int msg[] = {buttonId, 0, x, y}; - //broadcastData(BroadcastPort, msg, sizeof(msg)); - - Mouse::feed(buttonId, 0, x, y); -} - -static void mouseMove(int x, int y) { - int msg[] = {0, 0, x, y}; - //broadcastData(BroadcastPort, msg, sizeof(msg)); - - Mouse::feed(0, 0, x, y); -} - -static void pointerDown(int pointerId, int x, int y) { - Multitouch::feed(1, 1, x, y, pointerId); -} -static void pointerUp(int pointerId, int x, int y) { - Multitouch::feed(1, 0, x, y, pointerId); -} -static void pointerMove(int pointerId, int x, int y) { - Multitouch::feed(0, 0, x, y, pointerId); -} - -__inline static const float padXtoSigned(int x) { - return (x - 483.0f) * 0.002070393374741201f; -} -__inline static const float padYtoSigned(int y) { - return (y - 180.0f) * 0.005555555555555556f; -} - -static void trackpadPress(int x, int y) { - Controller::feed(1, Controller::STATE_TOUCH, padXtoSigned(x), padYtoSigned(y)); -} -static void trackpadMove(int x, int y) { - Controller::feed(1, Controller::STATE_MOVE, padXtoSigned(x), padYtoSigned(y)); -} -static void trackpadRelease(int x, int y) { - Controller::feed(1, Controller::STATE_RELEASE, padXtoSigned(x), padYtoSigned(y)); -} - -static int isTouchpadTouched = 0; - -static -int32_t -handle_xperia_input( struct android_app* app, AInputEvent* event ) -{ - if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION ) - { - struct ENGINE* engine = (struct ENGINE*)app->userData; - int nPointerCount = AMotionEvent_getPointerCount( event ); - int nSourceId = AInputEvent_getSource( event ); - int nAction = AMOTION_EVENT_ACTION_MASK & AMotionEvent_getAction( event ); - int isTrackpad =(nSourceId == AINPUT_SOURCE_TOUCHPAD); - int n; - - for( n = 0 ; n < nPointerCount ; ++n ) - { - int nPointerId = AMotionEvent_getPointerId( event, n ); - - // We don't care about secondary pointers right now - if( !isTrackpad ) { - if (nAction == AMOTION_EVENT_ACTION_POINTER_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_UP ) - continue; - } - - int x = AMotionEvent_getX( event, n ); - int y = AMotionEvent_getY( event, n ); - - if( nAction == AMOTION_EVENT_ACTION_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_DOWN ) { - //LOGI("action down %d\n", isTrackpad); - if (isTrackpad) { - trackpadPress(x, y); - } else { - mouseDown(1, x, y); - //LOGI("mouse-pointer down"); - } - } - else if( nAction == AMOTION_EVENT_ACTION_UP || nAction == AMOTION_EVENT_ACTION_POINTER_UP /*|| nAction == AMOTION_EVENT_ACTION_CANCEL*/ ) { - //LOGI("action up %d\n", isTrackpad); - if (isTrackpad) { - //LOGI("trackpad-up\n"); - trackpadRelease(x, y); - } - else { - //LOGI("mouse-pointer up\n"); - mouseUp(1, x, y); - } - } else if (nAction == AMOTION_EVENT_ACTION_MOVE ) { - if (isTrackpad) - trackpadMove(x, y); - else - mouseMove(x, y); - } - } - return 1; - } -} -static int convertAndroidKeyCodeToWindowsKeyCode(int keyCode) { - switch(keyCode) { - /* - case 29: return Keyboard::KEY_A; - case 30: return Keyboard::KEY_B; - case 31: return Keyboard::KEY_C; - case 32: return Keyboard::KEY_D; - case 33: return Keyboard::KEY_E; - case 34: return Keyboard::KEY_F; - case 35: return Keyboard::KEY_G; - case 36: return Keyboard::KEY_H; - case 37: return Keyboard::KEY_I; - case 38: return Keyboard::KEY_J; - case 39: return Keyboard::KEY_K; - case 40: return Keyboard::KEY_L; - case 41: return Keyboard::KEY_M; - case 42: return Keyboard::KEY_N; - case 43: return Keyboard::KEY_O; - case 44: return Keyboard::KEY_P; - case 45: return Keyboard::KEY_Q; - case 46: return Keyboard::KEY_R; - case 47: return Keyboard::KEY_S; - case 48: return Keyboard::KEY_T; - case 49: return Keyboard::KEY_U; - case 50: return Keyboard::KEY_V; - case 51: return Keyboard::KEY_W; - case 52: return Keyboard::KEY_X; - case 53: return Keyboard::KEY_Y; - case 54: return Keyboard::KEY_Z; - */ - case 67: return Keyboard::KEY_BACKSPACE; - case 66: return Keyboard::KEY_RETURN; -// case 62: return Keyboard::KEY_SPACE; - default: return 0; - } -} -static -int32_t -handle_keyboard_input( struct android_app* app, AInputEvent* event ) -{ - if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY ) - { - struct ENGINE* engine = (struct ENGINE*)app->userData; - bool isDown = AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN; - int keyCode = AKeyEvent_getKeyCode(event); - int metaState = AKeyEvent_getMetaState(event); - int deviceId = AInputEvent_getDeviceId(event); - bool pressedBack = (keyCode == 4) && ((AKeyEvent_getMetaState(event) & AMETA_ALT_ON) == 0); - bool isRepeat = AKeyEvent_getRepeatCount(event) > 0; - if (!isRepeat){ - //int scanCode = AKeyEvent_getScanCode(event); - //LOGI("key-%s : %d", isDown?"key-down":"key-up", keyCode); - //LOGI("getId: %d\n", AInputEvent_getDeviceId(event)); - //LOGI("flags: %d\n", AKeyEvent_getFlags(event)); - //LOGI("scancode: %d\n", AKeyEvent_getScanCode(event)); - - //unsigned char msg[2] = {(unsigned char)(keyCode), (unsigned char)(isDown)}; - //broadcastData(BroadcastPort, msg, 2); - LOGW("New keycode: %d", keyCode); - if (!pressedBack) { - - int key = convertAndroidKeyCodeToWindowsKeyCode(keyCode); - if (key == 0) key = keyCode; - Keyboard::feed(key, isDown); - if(isDown) { - int uniChar = ((struct ENGINE*)app->userData)->appContext.platform->getKeyFromKeyCode(keyCode, metaState, deviceId); - char charI = (char)uniChar; - LOGW("Unichar: %d", uniChar); - if(charI > 31) { - LOGW("New Char %c[%d]", charI, uniChar); - Keyboard::feedText((char)charI); - } - } - } - } - if (pressedBack) - { - if (!isRepeat && !engine->userApp->handleBack(isDown)) - { - //LOGI("Returning 0 from engine_handle_input\n"); - return 0; - } - return 1; - } - - bool volumeButton = (keyCode == 24) || (keyCode == 25); - bool handled = !pressedBack && !volumeButton; - return handled; - } - - return 0; -} - -static -int32_t -handle_touch_input( struct android_app* app, AInputEvent* event ) -{ - if( AInputEvent_getType(event) != AINPUT_EVENT_TYPE_MOTION ) - return 0; - - int nSourceId = AInputEvent_getSource( event ); - if (nSourceId == AINPUT_SOURCE_TOUCHPAD) - return 0; - - struct ENGINE* engine = (struct ENGINE*)app->userData; - - int fullAction = AMotionEvent_getAction( event ); - int nAction = AMOTION_EVENT_ACTION_MASK & fullAction; - int pointerIndex = (fullAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - int pointerId = AMotionEvent_getPointerId(event, pointerIndex); - int x = (int)AMotionEvent_getX(event, pointerIndex); - int y = (int)AMotionEvent_getY(event, pointerIndex); - - //LOGI("xy:\t%d, %d\n", x, y); - - switch (nAction) { - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_DOWN: - mouseDown(1, x, y); - Multitouch::feed(1, 1, x, y, pointerId); - break; - case AMOTION_EVENT_ACTION_POINTER_UP: - case AMOTION_EVENT_ACTION_UP: - mouseUp(1, x, y); - Multitouch::feed(1, 0, x, y, pointerId); - break; - case AMOTION_EVENT_ACTION_MOVE: - int pcount = AMotionEvent_getPointerCount(event); - for (int i = 0; i < pcount; ++i) { - int pp = AMotionEvent_getPointerId(event, i); // @attn wtf? - float xx = AMotionEvent_getX(event, i); - float yy = AMotionEvent_getY(event, i); - // System.err.println(" " + p + " @ " + x + ", " + y); - //LOGI("> %.2f, %.2f\n", xx, yy); - mouseMove(xx, yy); - Multitouch::feed(0, 0, xx, yy, pp); - } - break; - } - return 0; -} - -/** - * Process the next input event. - */ -static -int32_t -engine_handle_input( struct android_app* app, AInputEvent* event ) -{ - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) - return handle_keyboard_input(app, event); - - const bool isTouchscreen = ((struct ENGINE*)app->userData)->appContext.platform->supportsTouchscreen();; - if (isTouchscreen) { - return handle_touch_input(app, event); - } else { - // We only have xperia by now, but in the best of worlds - // one function will feed all controllers - return handle_xperia_input(app, event); - } -} - -/** - * Process the next main command. - */ -static -void -engine_handle_cmd( struct android_app* app, int32_t cmd ) -{ - struct ENGINE* engine = (struct ENGINE*)app->userData; - switch( cmd ) - { - case APP_CMD_SAVE_STATE: - { - //LOGI("save-state\n"); - // The system has asked us to save our current state. Do so if needed - - // query first - void* savedState = 0; - int savedStateSize = 0; - engine->userApp->saveState(&savedState, &savedStateSize); - - // Seems OK - save - if (savedStateSize > 0) { - app->savedState = savedState; - app->savedStateSize = savedStateSize; - } - } - break; - - case APP_CMD_INIT_WINDOW: - //LOGI("init-window\n"); - // The window is being shown, get it ready. - if( engine->app->window != NULL ) - { - engine_init_display( engine ); - //engine->has_focus = 1; - // LOGI("has-inited-display\n"); - //ANativeWindow_setBuffersGeometry(engine->app->window, engine->width, engine->height, 1); - } - break; - - case APP_CMD_TERM_WINDOW: - //LOGI("term-window\n"); - // The window is being hidden or closed, clean it up. - engine_term_display( engine ); - break; - - case APP_CMD_GAINED_FOCUS: - //LOGI("gained-focus\n"); - //engine->has_focus = 1; - break; - - case APP_CMD_LOST_FOCUS: - //engine->has_focus = 0; - //engine_draw_frame( engine ); - break; - - case APP_CMD_PAUSE: - engine->has_focus = 0; - break; - case APP_CMD_RESUME: - engine->has_focus = 1; - break; - default: - //LOGI("unknown: %d\n", cmd); - break; - } -} - -static int initSocket(int port) -{ - int s = socket(PF_INET, SOCK_DGRAM, 0); - - struct addrinfo hints; - struct addrinfo *res; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags = AI_PASSIVE; - - getaddrinfo("192.168.0.110", "1999", &hints, &res); // ... - - int b = bind(s, res->ai_addr, res->ai_addrlen); - - int broadcast = 1; - if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &broadcast, - sizeof(broadcast)) == -1) { - perror("setsockopt (SO_BROADCAST)"); - return 0; - } - - return s; -} - -/** - * This is the main entry point of a native application that is using - * android_native_app_glue. It runs in its own thread, with its own - * event loop for receiving input events and doing other things (rendering). - */ -void -android_main( struct android_app* state ); +#ifndef MAIN_CLASS +#error "Error: MAIN_CLASS must have been defined to your main class (e.g. #define MAIN_CLASS MyApp)" +#endif + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define TAG "MAIN_CLASS" + +#include "platform/log.h" +#include "platform/input/Controller.h" +#include "platform/input/Keyboard.h" +#include "platform/input/Mouse.h" +#include "platform/input/Multitouch.h" + +#include "EglConfigPrinter.h" + +const int BroadcastPort = 9991; + +/** + * Shared state for our app. + */ +struct ENGINE +{ + struct android_app* app; + + int render; + EGLDisplay display; + EGLSurface surface; + EGLContext context; + int width; + int height; + int has_focus; + App* userApp; + bool is_inited; + //bool init_gl; + struct AppContext appContext; +}; + +/** + * Initialize an EGL context for the current display. + */ +static +int +engine_init_display( struct ENGINE* engine ) +{ + LOGI("1) Initing display. Get display\n"); + // initialize OpenGL ES 2.0 and EGL + const EGLint attribs[] = \ + { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + //EGL_BUFFER_SIZE, 32, + //EGL_BLUE_SIZE, 5, + //EGL_GREEN_SIZE, 6, + //EGL_RED_SIZE, 5, + ///EGL_ALPHA_SIZE, 8, + //0x3098, 1, + //EGL_TRANSPARENT_TYPE, EGL_TRANSPARENT_RGB, + //EGL_ALPHA_SIZE, 8,//5, + EGL_DEPTH_SIZE, 16, //8 + //EGL_STENCIL_SIZE, 8, + //EGL_DEPTH_SIZE, 16, + //EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_NONE + }; + + EGLint w, h, format; + EGLint numConfigs; + EGLConfig config; + EGLSurface surface; + EGLContext context; + + EGLDisplay display = eglGetDisplay( EGL_DEFAULT_DISPLAY ); + + LOGI("2) Initialize egl\n"); + + if( display == NULL ) + { + LOGW( "!!! NO DISPLAY !!! eglGetDisplay" ); + } + + eglInitialize( display, 0, 0 ); + //EGLConfigPrinter::printAllConfigs(display); + LOGI("3) Choose config\n"); + + /* Here, the application chooses the configuration it desires. In this + * sample, we have a very simplified selection process, where we pick + * the first EGLConfig that matches our criteria */ + eglChooseConfig( display, attribs, &config, 1, &numConfigs ); + LOGI("4) Get config attributes\n"); + + /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is + * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). + * As soon as we picked a EGLConfig, we can safely reconfigure the + * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ + eglGetConfigAttrib( display, config, EGL_NATIVE_VISUAL_ID, &format ); + LOGI("5) Choose config, set window geometry\n"); + + ANativeWindow_setBuffersGeometry( engine->app->window, 0, 0, format ); + + eglChooseConfig( display, attribs, NULL, 0, &numConfigs ); + + EGLConfig* configs = new EGLConfig[numConfigs]; + eglChooseConfig(display, attribs, configs, numConfigs, &numConfigs); + + for (int i = 0; i < numConfigs; ++i) { + EGLSurface surf = eglCreateWindowSurface( display, configs[i], engine->app->window, NULL ); + if (surf != EGL_NO_SURFACE) { + config = configs[i]; + surface = surf; + break; + } + } + + delete[] configs; + + + //surface = eglCreateWindowSurface( display, config, engine->app->window, NULL ); + LOGI("6) Creating context\n"); + + context = eglCreateContext( display, config, NULL, NULL ); + LOGI("7) Make current\n"); + + if( eglMakeCurrent( display, surface, surface, context ) == EGL_FALSE ) + { + LOGW("Unable to eglMakeCurrent"); + return -1; + } + LOGI("8) Query stats and set values\n"); + + eglQuerySurface( display, surface, EGL_WIDTH, &w ); + eglQuerySurface( display, surface, EGL_HEIGHT, &h ); + + if (w < h) { + int tmp = w; + w = h; + h = tmp; + } + + engine->display = engine->appContext.display = display; + engine->context = engine->appContext.context = context; + engine->surface = engine->appContext.surface = surface; + engine->width = w; + engine->height = h; + //Minecraft::width = w; + //Minecraft::height = h; + LOGI("9) Set-up viewport\n"); + + // Initialize GL state. + glViewport( 0, 0, w, h ); + LOGI("X) Graphics set-up finished!\n"); + + // Don't need to reload graphics first time + if (engine->is_inited) { + engine->userApp->onGraphicsReset(engine->appContext); + engine->userApp->setSize(w, h); + } + + eglSwapInterval(eglGetCurrentDisplay(), 2); + engine->is_inited = true; + + return 0; +} + +/** + * Tear down the EGL context currently associated with the display. + */ +static +void +engine_term_display( struct ENGINE* engine ) +{ + //LOGI( "engine_term_display" ); + + if( engine->display != EGL_NO_DISPLAY ) + { + eglMakeCurrent( engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + + if( engine->context != EGL_NO_CONTEXT ) + { + eglDestroyContext( engine->display, engine->context ); + } + + if( engine->surface != EGL_NO_SURFACE ) + { + eglDestroySurface( engine->display, engine->surface ); + } + + eglTerminate( engine->display ); + } + + engine->display = EGL_NO_DISPLAY; + engine->context = EGL_NO_CONTEXT; + engine->surface = EGL_NO_SURFACE; +} + +static int socketDesc; + +static int broadcastData(int port, void* msg, int msgLen) +{ + struct sockaddr_in broadcastAddr; + + broadcastAddr.sin_family = AF_INET; + broadcastAddr.sin_port = htons(port); + broadcastAddr.sin_addr.s_addr = inet_addr("192.168.0.255"); + + return sendto(socketDesc, msg, msgLen, 0, (struct sockaddr*)&broadcastAddr, sizeof(broadcastAddr)); +} + +static void mouseDown(int buttonId, int x, int y) { + int msg[] = {buttonId, 0, x, y}; + //broadcastData(BroadcastPort, msg, sizeof(msg)); + + Mouse::feed(buttonId, 1, x, y); +} + +static void mouseUp(int buttonId, int x, int y) { + int msg[] = {buttonId, 0, x, y}; + //broadcastData(BroadcastPort, msg, sizeof(msg)); + + Mouse::feed(buttonId, 0, x, y); +} + +static void mouseMove(int x, int y) { + int msg[] = {0, 0, x, y}; + //broadcastData(BroadcastPort, msg, sizeof(msg)); + + Mouse::feed(0, 0, x, y); +} + +static void pointerDown(int pointerId, int x, int y) { + Multitouch::feed(1, 1, x, y, pointerId); +} +static void pointerUp(int pointerId, int x, int y) { + Multitouch::feed(1, 0, x, y, pointerId); +} +static void pointerMove(int pointerId, int x, int y) { + Multitouch::feed(0, 0, x, y, pointerId); +} + +__inline static const float padXtoSigned(int x) { + return (x - 483.0f) * 0.002070393374741201f; +} +__inline static const float padYtoSigned(int y) { + return (y - 180.0f) * 0.005555555555555556f; +} + +static void trackpadPress(int x, int y) { + Controller::feed(1, Controller::STATE_TOUCH, padXtoSigned(x), padYtoSigned(y)); +} +static void trackpadMove(int x, int y) { + Controller::feed(1, Controller::STATE_MOVE, padXtoSigned(x), padYtoSigned(y)); +} +static void trackpadRelease(int x, int y) { + Controller::feed(1, Controller::STATE_RELEASE, padXtoSigned(x), padYtoSigned(y)); +} + +static int isTouchpadTouched = 0; + +static +int32_t +handle_xperia_input( struct android_app* app, AInputEvent* event ) +{ + if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION ) + { + struct ENGINE* engine = (struct ENGINE*)app->userData; + int nPointerCount = AMotionEvent_getPointerCount( event ); + int nSourceId = AInputEvent_getSource( event ); + int nAction = AMOTION_EVENT_ACTION_MASK & AMotionEvent_getAction( event ); + int isTrackpad =(nSourceId == AINPUT_SOURCE_TOUCHPAD); + int n; + + for( n = 0 ; n < nPointerCount ; ++n ) + { + int nPointerId = AMotionEvent_getPointerId( event, n ); + + // We don't care about secondary pointers right now + if( !isTrackpad ) { + if (nAction == AMOTION_EVENT_ACTION_POINTER_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_UP ) + continue; + } + + int x = AMotionEvent_getX( event, n ); + int y = AMotionEvent_getY( event, n ); + + if( nAction == AMOTION_EVENT_ACTION_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_DOWN ) { + //LOGI("action down %d\n", isTrackpad); + if (isTrackpad) { + trackpadPress(x, y); + } else { + mouseDown(1, x, y); + //LOGI("mouse-pointer down"); + } + } + else if( nAction == AMOTION_EVENT_ACTION_UP || nAction == AMOTION_EVENT_ACTION_POINTER_UP /*|| nAction == AMOTION_EVENT_ACTION_CANCEL*/ ) { + //LOGI("action up %d\n", isTrackpad); + if (isTrackpad) { + //LOGI("trackpad-up\n"); + trackpadRelease(x, y); + } + else { + //LOGI("mouse-pointer up\n"); + mouseUp(1, x, y); + } + } else if (nAction == AMOTION_EVENT_ACTION_MOVE ) { + if (isTrackpad) + trackpadMove(x, y); + else + mouseMove(x, y); + } + } + return 1; + } +} +static int convertAndroidKeyCodeToWindowsKeyCode(int keyCode) { + switch(keyCode) { + /* + case 29: return Keyboard::KEY_A; + case 30: return Keyboard::KEY_B; + case 31: return Keyboard::KEY_C; + case 32: return Keyboard::KEY_D; + case 33: return Keyboard::KEY_E; + case 34: return Keyboard::KEY_F; + case 35: return Keyboard::KEY_G; + case 36: return Keyboard::KEY_H; + case 37: return Keyboard::KEY_I; + case 38: return Keyboard::KEY_J; + case 39: return Keyboard::KEY_K; + case 40: return Keyboard::KEY_L; + case 41: return Keyboard::KEY_M; + case 42: return Keyboard::KEY_N; + case 43: return Keyboard::KEY_O; + case 44: return Keyboard::KEY_P; + case 45: return Keyboard::KEY_Q; + case 46: return Keyboard::KEY_R; + case 47: return Keyboard::KEY_S; + case 48: return Keyboard::KEY_T; + case 49: return Keyboard::KEY_U; + case 50: return Keyboard::KEY_V; + case 51: return Keyboard::KEY_W; + case 52: return Keyboard::KEY_X; + case 53: return Keyboard::KEY_Y; + case 54: return Keyboard::KEY_Z; + */ + case 67: return Keyboard::KEY_BACKSPACE; + case 66: return Keyboard::KEY_RETURN; +// case 62: return Keyboard::KEY_SPACE; + default: return 0; + } +} +static +int32_t +handle_keyboard_input( struct android_app* app, AInputEvent* event ) +{ + if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY ) + { + struct ENGINE* engine = (struct ENGINE*)app->userData; + bool isDown = AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN; + int keyCode = AKeyEvent_getKeyCode(event); + int metaState = AKeyEvent_getMetaState(event); + int deviceId = AInputEvent_getDeviceId(event); + bool pressedBack = (keyCode == 4) && ((AKeyEvent_getMetaState(event) & AMETA_ALT_ON) == 0); + bool isRepeat = AKeyEvent_getRepeatCount(event) > 0; + if (!isRepeat){ + //int scanCode = AKeyEvent_getScanCode(event); + //LOGI("key-%s : %d", isDown?"key-down":"key-up", keyCode); + //LOGI("getId: %d\n", AInputEvent_getDeviceId(event)); + //LOGI("flags: %d\n", AKeyEvent_getFlags(event)); + //LOGI("scancode: %d\n", AKeyEvent_getScanCode(event)); + + //unsigned char msg[2] = {(unsigned char)(keyCode), (unsigned char)(isDown)}; + //broadcastData(BroadcastPort, msg, 2); + LOGW("New keycode: %d", keyCode); + if (!pressedBack) { + + int key = convertAndroidKeyCodeToWindowsKeyCode(keyCode); + if (key == 0) key = keyCode; + Keyboard::feed(key, isDown); + if(isDown) { + int uniChar = ((struct ENGINE*)app->userData)->appContext.platform->getKeyFromKeyCode(keyCode, metaState, deviceId); + char charI = (char)uniChar; + LOGW("Unichar: %d", uniChar); + if(charI > 31) { + LOGW("New Char %c[%d]", charI, uniChar); + Keyboard::feedText((char)charI); + } + } + } + } + if (pressedBack) + { + if (!isRepeat && !engine->userApp->handleBack(isDown)) + { + //LOGI("Returning 0 from engine_handle_input\n"); + return 0; + } + return 1; + } + + bool volumeButton = (keyCode == 24) || (keyCode == 25); + bool handled = !pressedBack && !volumeButton; + return handled; + } + + return 0; +} + +static +int32_t +handle_touch_input( struct android_app* app, AInputEvent* event ) +{ + if( AInputEvent_getType(event) != AINPUT_EVENT_TYPE_MOTION ) + return 0; + + int nSourceId = AInputEvent_getSource( event ); + if (nSourceId == AINPUT_SOURCE_TOUCHPAD) + return 0; + + struct ENGINE* engine = (struct ENGINE*)app->userData; + + int fullAction = AMotionEvent_getAction( event ); + int nAction = AMOTION_EVENT_ACTION_MASK & fullAction; + int pointerIndex = (fullAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + int pointerId = AMotionEvent_getPointerId(event, pointerIndex); + int x = (int)AMotionEvent_getX(event, pointerIndex); + int y = (int)AMotionEvent_getY(event, pointerIndex); + + //LOGI("xy:\t%d, %d\n", x, y); + + switch (nAction) { + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_DOWN: + mouseDown(1, x, y); + Multitouch::feed(1, 1, x, y, pointerId); + break; + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_UP: + mouseUp(1, x, y); + Multitouch::feed(1, 0, x, y, pointerId); + break; + case AMOTION_EVENT_ACTION_MOVE: + int pcount = AMotionEvent_getPointerCount(event); + for (int i = 0; i < pcount; ++i) { + int pp = AMotionEvent_getPointerId(event, i); // @attn wtf? + float xx = AMotionEvent_getX(event, i); + float yy = AMotionEvent_getY(event, i); + // System.err.println(" " + p + " @ " + x + ", " + y); + //LOGI("> %.2f, %.2f\n", xx, yy); + mouseMove(xx, yy); + Multitouch::feed(0, 0, xx, yy, pp); + } + break; + } + return 0; +} + +/** + * Process the next input event. + */ +static +int32_t +engine_handle_input( struct android_app* app, AInputEvent* event ) +{ + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) + return handle_keyboard_input(app, event); + + const bool isTouchscreen = ((struct ENGINE*)app->userData)->appContext.platform->supportsTouchscreen();; + if (isTouchscreen) { + return handle_touch_input(app, event); + } else { + // We only have xperia by now, but in the best of worlds + // one function will feed all controllers + return handle_xperia_input(app, event); + } +} + +/** + * Process the next main command. + */ +static +void +engine_handle_cmd( struct android_app* app, int32_t cmd ) +{ + struct ENGINE* engine = (struct ENGINE*)app->userData; + switch( cmd ) + { + case APP_CMD_SAVE_STATE: + { + //LOGI("save-state\n"); + // The system has asked us to save our current state. Do so if needed + + // query first + void* savedState = 0; + int savedStateSize = 0; + engine->userApp->saveState(&savedState, &savedStateSize); + + // Seems OK - save + if (savedStateSize > 0) { + app->savedState = savedState; + app->savedStateSize = savedStateSize; + } + } + break; + + case APP_CMD_INIT_WINDOW: + //LOGI("init-window\n"); + // The window is being shown, get it ready. + if( engine->app->window != NULL ) + { + engine_init_display( engine ); + //engine->has_focus = 1; + // LOGI("has-inited-display\n"); + //ANativeWindow_setBuffersGeometry(engine->app->window, engine->width, engine->height, 1); + } + break; + + case APP_CMD_TERM_WINDOW: + //LOGI("term-window\n"); + // The window is being hidden or closed, clean it up. + engine_term_display( engine ); + break; + + case APP_CMD_GAINED_FOCUS: + //LOGI("gained-focus\n"); + //engine->has_focus = 1; + break; + + case APP_CMD_LOST_FOCUS: + //engine->has_focus = 0; + //engine_draw_frame( engine ); + break; + + case APP_CMD_PAUSE: + engine->has_focus = 0; + break; + case APP_CMD_RESUME: + engine->has_focus = 1; + break; + default: + //LOGI("unknown: %d\n", cmd); + break; + } +} + +static int initSocket(int port) +{ + int s = socket(PF_INET, SOCK_DGRAM, 0); + + struct addrinfo hints; + struct addrinfo *res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + + getaddrinfo("192.168.0.110", "1999", &hints, &res); // ... + + int b = bind(s, res->ai_addr, res->ai_addrlen); + + int broadcast = 1; + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &broadcast, + sizeof(broadcast)) == -1) { + perror("setsockopt (SO_BROADCAST)"); + return 0; + } + + return s; +} + +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things (rendering). + */ +void +android_main( struct android_app* state ); diff --git a/src/main_android_java.cpp b/src/main_android_java.cpp index c89ae03..889b66a 100755 --- a/src/main_android_java.cpp +++ b/src/main_android_java.cpp @@ -1,271 +1,271 @@ -#include "App.h" -#include "AppPlatform_android.h" - -// JNI keycode constants -#include - -//#include "main_android_java.h" -#include "platform/input/Multitouch.h" -#include -#include - -// Horrible, I know. / A -#ifndef MAIN_CLASS -#include "main.cpp" -#endif - - -// References for JNI -static jobject g_pActivity = 0; -static AppPlatform_android appPlatform; - -static void setupExternalPath(JNIEnv* env, MAIN_CLASS* app) -{ - //JVMAttacher ta(vm); - //JNIEnv* env = ta.getEnv(); - - LOGI("setupExternalPath"); - - if (env) - { - LOGI("Environment exists"); - } - // try appspecific external directory first - jobject activity = g_pActivity; - jclass activityClass = env->GetObjectClass(activity); - jmethodID getExternalFilesDir = env->GetMethodID(activityClass, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); - - jobject file = NULL; - if (getExternalFilesDir != NULL) { - file = env->CallObjectMethod(activity, getExternalFilesDir, NULL); - } - - if (file == NULL) { - // Fallback to the legacy shared storage directory - jclass clazz = env->FindClass("android/os/Environment"); - jmethodID method = env->GetStaticMethodID(clazz, "getExternalStorageDirectory", "()Ljava/io/File;"); - if (env->ExceptionOccurred()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } - file = env->CallStaticObjectMethod(clazz, method); - } - - if (!file) { - LOGI("Failed to get external storage file object, using current working dir"); - app->externalStoragePath = "."; - app->externalCacheStoragePath = "."; - return; - } - - jclass fileClass = env->GetObjectClass(file); - jmethodID fileMethod = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"); - jobject pathString = env->CallObjectMethod(file, fileMethod); - - const char* str = env->GetStringUTFChars((jstring) pathString, NULL); - app->externalStoragePath = str; - app->externalCacheStoragePath = str; - LOGI("%s", str); - - // same fix as the native entry point: make sure cwd is writable - if (chdir(str) != 0) { - LOGI("chdir to %s failed: %s", str, strerror(errno)); - } - - env->ReleaseStringUTFChars((jstring)pathString, str); -} - -static void pointerDown(int pointerId, int x, int y) { - Multitouch::feed(1, 1, x, y, pointerId); -} -static void pointerUp(int pointerId, int x, int y) { - Multitouch::feed(1, 0, x, y, pointerId); -} -static void pointerMove(int pointerId, int x, int y) { - Multitouch::feed(0, 0, x, y, pointerId); -} - - -static App* gApp = 0; -static AppContext gContext; -static bool g_inNativeOnCreate = false; - -extern "C" { -JNIEXPORT jint JNICALL -JNI_OnLoad( JavaVM * vm, void * reserved ) -{ - LOGI("Entering OnLoad\n"); - return appPlatform.init(vm); -} - -// Register/save a reference to the java main activity instance -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeRegisterThis(JNIEnv* env, jobject clazz) { - LOGI("@RegisterThis\n"); - g_pActivity = (jobject)env->NewGlobalRef( clazz ); -} - -// Unregister/delete the reference to the java main activity instance -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeUnregisterThis(JNIEnv* env, jobject clazz) { - LOGI("@UnregisterThis\n"); - env->DeleteGlobalRef( g_pActivity ); -} - -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeOnCreate(JNIEnv* env, jobject thiz, jint screenWidth, jint screenHeight) { - LOGI("@nativeOnCreate w=%d h=%d\n", (int)screenWidth, (int)screenHeight); - g_inNativeOnCreate = true; - - appPlatform.instance = g_pActivity; - appPlatform.setScreenDimensions((int)screenWidth, (int)screenHeight); - LOGI("nativeOnCreate: screen set, no initConsts needed\n"); - gContext.doRender = false; - gContext.platform = &appPlatform; - - LOGI("nativeOnCreate: creating gApp\n"); - gApp = new MAIN_CLASS(); - LOGI("nativeOnCreate: gApp=%p\n", gApp); - setupExternalPath(env, (MAIN_CLASS*)gApp); - if (env->ExceptionOccurred()) { - LOGI("nativeOnCreate: exception after setupExternalPath!\n"); - env->ExceptionDescribe(); - env->ExceptionClear(); - } - LOGI("nativeOnCreate: done\n"); - g_inNativeOnCreate = false; - //gApp->init(gContext); -} - -static int s_surfaceCreatedCount = 0; - -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_GLRenderer_nativeOnSurfaceCreated(JNIEnv* env) { - s_surfaceCreatedCount++; - if (g_inNativeOnCreate) { - // Skip re-entrant surface callbacks that fire during nativeOnCreate - return; - } - LOGI("@nativeOnSurfaceCreated #%d tid=%d\n", s_surfaceCreatedCount, (int)gettid()); - - if (gApp) { - // Don't call onGraphicsReset the first time - if (gApp->isInited()) { - LOGI("nativeOnSurfaceCreated: calling onGraphicsReset\n"); - gApp->onGraphicsReset(gContext); - } - - if (!gApp->isInited()) { - LOGI("nativeOnSurfaceCreated: calling init\n"); - gApp->init(gContext); - LOGI("nativeOnSurfaceCreated: init done, isInited=%d\n", (int)gApp->isInited()); - } - } -} - -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_GLRenderer_nativeOnSurfaceChanged(JNIEnv* env, jclass cls, jint w, jint h) { - LOGI("@nativeOnSurfaceChanged: %lu\n", (unsigned long)pthread_self()); - - if (gApp) { - gApp->setSize(w, h); - - if (!gApp->isInited()) - gApp->init(gContext); - - if (!gApp->isInited()) - LOGI("nativeOnSurfaceChanged: NOT INITED!\n"); - } -} - -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeOnDestroy(JNIEnv* env) { - LOGI("@nativeOnDestroy\n"); - - delete gApp; - gApp = 0; - //gApp->onGraphicsReset(gContext); -} - -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_GLRenderer_nativeUpdate(JNIEnv* env) { - //LOGI("@nativeUpdate: %p\n", pthread_self()); - if (gApp) { - if (!gApp->isInited()) - gApp->init(gContext); - - gApp->update(); - - if (gApp->wantToQuit()) - appPlatform.finish(); - } -} - -// -// Keyboard events -// -// helper to convert Android keycodes to our internal Keyboard constants -static int androidKeyToInternal(int androidKey) { - switch(androidKey) { - case AKEYCODE_DEL: return Keyboard::KEY_BACKSPACE; - case AKEYCODE_ENTER: - case AKEYCODE_NUMPAD_ENTER: - return Keyboard::KEY_RETURN; - // letters are delivered via nativeTextChar so no need to map here - default: - return androidKey; // fall back to raw code - } -} - -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeOnKeyDown(JNIEnv* env, jclass cls, jint keyCode) { - LOGI("@nativeOnKeyDown: %d\n", keyCode); - int mapped = androidKeyToInternal(keyCode); - Keyboard::feed(mapped, true); -} -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeTextChar(JNIEnv* env, jclass cls, jint unicodeChar) { - // soft-keyboards may send a backspace as a character code - if (unicodeChar == 8) { - Keyboard::feed(Keyboard::KEY_BACKSPACE, true); - Keyboard::feed(Keyboard::KEY_BACKSPACE, false); - } else if (unicodeChar > 0 && unicodeChar < 128) { - Keyboard::feedText((char)unicodeChar); - } -} -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeOnKeyUp(JNIEnv* env, jclass cls, jint keyCode) { - LOGI("@nativeOnKeyUp: %d\n", (int)keyCode); - int mapped = androidKeyToInternal(keyCode); - Keyboard::feed(mapped, false); -} - -JNIEXPORT jboolean JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeHandleBack(JNIEnv* env, jclass cls, jboolean isDown) { - LOGI("@nativeHandleBack: %d\n", isDown); - if (gApp) return gApp->handleBack(isDown)? JNI_TRUE : JNI_FALSE; - return JNI_FALSE; -} - -// -// Mouse events -// -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeMouseDown(JNIEnv* env, jclass cls, jint pointerId, jint buttonId, jfloat x, jfloat y) { - //LOGI("@nativeMouseDown: %f %f\n", x, y); - mouseDown(1, x, y); - pointerDown(pointerId, x, y); -} -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeMouseUp(JNIEnv* env, jclass cls, jint pointerId, jint buttonId, jfloat x, jfloat y) { - //LOGI("@nativeMouseUp: %f %f\n", x, y); - mouseUp(1, x, y); - pointerUp(pointerId, x, y); -} -JNIEXPORT void JNICALL -Java_com_mojang_minecraftpe_MainActivity_nativeMouseMove(JNIEnv* env, jclass cls, jint pointerId, jfloat x, jfloat y) { - //LOGI("@nativeMouseMove: %f %f\n", x, y); - mouseMove(x, y); - pointerMove(pointerId, x, y); -} -} +#include "App.h" +#include "AppPlatform_android.h" + +// JNI keycode constants +#include + +//#include "main_android_java.h" +#include "platform/input/Multitouch.h" +#include +#include + +// Horrible, I know. / A +#ifndef MAIN_CLASS +#include "main.cpp" +#endif + + +// References for JNI +static jobject g_pActivity = 0; +static AppPlatform_android appPlatform; + +static void setupExternalPath(JNIEnv* env, MAIN_CLASS* app) +{ + //JVMAttacher ta(vm); + //JNIEnv* env = ta.getEnv(); + + LOGI("setupExternalPath"); + + if (env) + { + LOGI("Environment exists"); + } + // try appspecific external directory first + jobject activity = g_pActivity; + jclass activityClass = env->GetObjectClass(activity); + jmethodID getExternalFilesDir = env->GetMethodID(activityClass, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); + + jobject file = NULL; + if (getExternalFilesDir != NULL) { + file = env->CallObjectMethod(activity, getExternalFilesDir, NULL); + } + + if (file == NULL) { + // Fallback to the legacy shared storage directory + jclass clazz = env->FindClass("android/os/Environment"); + jmethodID method = env->GetStaticMethodID(clazz, "getExternalStorageDirectory", "()Ljava/io/File;"); + if (env->ExceptionOccurred()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + file = env->CallStaticObjectMethod(clazz, method); + } + + if (!file) { + LOGI("Failed to get external storage file object, using current working dir"); + app->externalStoragePath = "."; + app->externalCacheStoragePath = "."; + return; + } + + jclass fileClass = env->GetObjectClass(file); + jmethodID fileMethod = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"); + jobject pathString = env->CallObjectMethod(file, fileMethod); + + const char* str = env->GetStringUTFChars((jstring) pathString, NULL); + app->externalStoragePath = str; + app->externalCacheStoragePath = str; + LOGI("%s", str); + + // same fix as the native entry point: make sure cwd is writable + if (chdir(str) != 0) { + LOGI("chdir to %s failed: %s", str, strerror(errno)); + } + + env->ReleaseStringUTFChars((jstring)pathString, str); +} + +static void pointerDown(int pointerId, int x, int y) { + Multitouch::feed(1, 1, x, y, pointerId); +} +static void pointerUp(int pointerId, int x, int y) { + Multitouch::feed(1, 0, x, y, pointerId); +} +static void pointerMove(int pointerId, int x, int y) { + Multitouch::feed(0, 0, x, y, pointerId); +} + + +static App* gApp = 0; +static AppContext gContext; +static bool g_inNativeOnCreate = false; + +extern "C" { +JNIEXPORT jint JNICALL +JNI_OnLoad( JavaVM * vm, void * reserved ) +{ + LOGI("Entering OnLoad\n"); + return appPlatform.init(vm); +} + +// Register/save a reference to the java main activity instance +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeRegisterThis(JNIEnv* env, jobject clazz) { + LOGI("@RegisterThis\n"); + g_pActivity = (jobject)env->NewGlobalRef( clazz ); +} + +// Unregister/delete the reference to the java main activity instance +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeUnregisterThis(JNIEnv* env, jobject clazz) { + LOGI("@UnregisterThis\n"); + env->DeleteGlobalRef( g_pActivity ); +} + +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeOnCreate(JNIEnv* env, jobject thiz, jint screenWidth, jint screenHeight) { + LOGI("@nativeOnCreate w=%d h=%d\n", (int)screenWidth, (int)screenHeight); + g_inNativeOnCreate = true; + + appPlatform.instance = g_pActivity; + appPlatform.setScreenDimensions((int)screenWidth, (int)screenHeight); + LOGI("nativeOnCreate: screen set, no initConsts needed\n"); + gContext.doRender = false; + gContext.platform = &appPlatform; + + LOGI("nativeOnCreate: creating gApp\n"); + gApp = new MAIN_CLASS(); + LOGI("nativeOnCreate: gApp=%p\n", gApp); + setupExternalPath(env, (MAIN_CLASS*)gApp); + if (env->ExceptionOccurred()) { + LOGI("nativeOnCreate: exception after setupExternalPath!\n"); + env->ExceptionDescribe(); + env->ExceptionClear(); + } + LOGI("nativeOnCreate: done\n"); + g_inNativeOnCreate = false; + //gApp->init(gContext); +} + +static int s_surfaceCreatedCount = 0; + +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_GLRenderer_nativeOnSurfaceCreated(JNIEnv* env) { + s_surfaceCreatedCount++; + if (g_inNativeOnCreate) { + // Skip re-entrant surface callbacks that fire during nativeOnCreate + return; + } + LOGI("@nativeOnSurfaceCreated #%d tid=%d\n", s_surfaceCreatedCount, (int)gettid()); + + if (gApp) { + // Don't call onGraphicsReset the first time + if (gApp->isInited()) { + LOGI("nativeOnSurfaceCreated: calling onGraphicsReset\n"); + gApp->onGraphicsReset(gContext); + } + + if (!gApp->isInited()) { + LOGI("nativeOnSurfaceCreated: calling init\n"); + gApp->init(gContext); + LOGI("nativeOnSurfaceCreated: init done, isInited=%d\n", (int)gApp->isInited()); + } + } +} + +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_GLRenderer_nativeOnSurfaceChanged(JNIEnv* env, jclass cls, jint w, jint h) { + LOGI("@nativeOnSurfaceChanged: %lu\n", (unsigned long)pthread_self()); + + if (gApp) { + gApp->setSize(w, h); + + if (!gApp->isInited()) + gApp->init(gContext); + + if (!gApp->isInited()) + LOGI("nativeOnSurfaceChanged: NOT INITED!\n"); + } +} + +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeOnDestroy(JNIEnv* env) { + LOGI("@nativeOnDestroy\n"); + + delete gApp; + gApp = 0; + //gApp->onGraphicsReset(gContext); +} + +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_GLRenderer_nativeUpdate(JNIEnv* env) { + //LOGI("@nativeUpdate: %p\n", pthread_self()); + if (gApp) { + if (!gApp->isInited()) + gApp->init(gContext); + + gApp->update(); + + if (gApp->wantToQuit()) + appPlatform.finish(); + } +} + +// +// Keyboard events +// +// helper to convert Android keycodes to our internal Keyboard constants +static int androidKeyToInternal(int androidKey) { + switch(androidKey) { + case AKEYCODE_DEL: return Keyboard::KEY_BACKSPACE; + case AKEYCODE_ENTER: + case AKEYCODE_NUMPAD_ENTER: + return Keyboard::KEY_RETURN; + // letters are delivered via nativeTextChar so no need to map here + default: + return androidKey; // fall back to raw code + } +} + +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeOnKeyDown(JNIEnv* env, jclass cls, jint keyCode) { + LOGI("@nativeOnKeyDown: %d\n", keyCode); + int mapped = androidKeyToInternal(keyCode); + Keyboard::feed(mapped, true); +} +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeTextChar(JNIEnv* env, jclass cls, jint unicodeChar) { + // soft-keyboards may send a backspace as a character code + if (unicodeChar == 8) { + Keyboard::feed(Keyboard::KEY_BACKSPACE, true); + Keyboard::feed(Keyboard::KEY_BACKSPACE, false); + } else if (unicodeChar > 0 && unicodeChar < 128) { + Keyboard::feedText((char)unicodeChar); + } +} +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeOnKeyUp(JNIEnv* env, jclass cls, jint keyCode) { + LOGI("@nativeOnKeyUp: %d\n", (int)keyCode); + int mapped = androidKeyToInternal(keyCode); + Keyboard::feed(mapped, false); +} + +JNIEXPORT jboolean JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeHandleBack(JNIEnv* env, jclass cls, jboolean isDown) { + LOGI("@nativeHandleBack: %d\n", isDown); + if (gApp) return gApp->handleBack(isDown)? JNI_TRUE : JNI_FALSE; + return JNI_FALSE; +} + +// +// Mouse events +// +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeMouseDown(JNIEnv* env, jclass cls, jint pointerId, jint buttonId, jfloat x, jfloat y) { + //LOGI("@nativeMouseDown: %f %f\n", x, y); + mouseDown(1, x, y); + pointerDown(pointerId, x, y); +} +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeMouseUp(JNIEnv* env, jclass cls, jint pointerId, jint buttonId, jfloat x, jfloat y) { + //LOGI("@nativeMouseUp: %f %f\n", x, y); + mouseUp(1, x, y); + pointerUp(pointerId, x, y); +} +JNIEXPORT void JNICALL +Java_com_mojang_minecraftpe_MainActivity_nativeMouseMove(JNIEnv* env, jclass cls, jint pointerId, jfloat x, jfloat y) { + //LOGI("@nativeMouseMove: %f %f\n", x, y); + mouseMove(x, y); + pointerMove(pointerId, x, y); +} +} diff --git a/src/main_android_java.h b/src/main_android_java.h index 89f8bb0..50ab3fd 100755 --- a/src/main_android_java.h +++ b/src/main_android_java.h @@ -1,174 +1,174 @@ -#ifndef MAIN_CLASS -#error "Error: MAIN_CLASS must have been defined to your main class (e.g. #define MAIN_CLASS MyApp)" -#endif - -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include - -#define TAG "MAIN_CLASS" - -#include "platform/log.h" -#include "platform/input/Keyboard.h" -#include "platform/input/Mouse.h" -#include "platform/input/Controller.h" - -#include "EglConfigPrinter.h" - -const int BroadcastPort = 9991; - -/** - * Shared state for our app. - */ -struct ENGINE -{ - struct android_app* app; - - int render; - EGLDisplay display; - EGLSurface surface; - EGLContext context; - int width; - int height; - int has_focus; - App* userApp; - bool is_inited; - //bool init_gl; - struct AppContext appContext; -}; - -static void mouseDown(int buttonId, int x, int y) { - int msg[] = {buttonId, 0, x, y}; - //broadcastData(BroadcastPort, msg, sizeof(msg)); - - Mouse::feed(buttonId, 1, x, y); -} - -static void mouseUp(int buttonId, int x, int y) { - int msg[] = {buttonId, 0, x, y}; - //broadcastData(BroadcastPort, msg, sizeof(msg)); - - Mouse::feed(buttonId, 0, x, y); -} - -static void mouseMove(int x, int y) { - int msg[] = {0, 0, x, y}; - //broadcastData(BroadcastPort, msg, sizeof(msg)); - - Mouse::feed(0, 0, x, y); -} - -#if 0 - -/** - * Process the next input event. - */ -static -int32_t -engine_handle_input( struct android_app* app, AInputEvent* event ) -{ - struct ENGINE* engine = (struct ENGINE*)app->userData; - if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION ) - { - //engine->render = 1; - int nPointerCount = AMotionEvent_getPointerCount( event ); - int nSourceId = AInputEvent_getSource( event ); - int n; - - for( n = 0 ; n < nPointerCount ; ++n ) - { - int nPointerId = AMotionEvent_getPointerId( event, n ); - int nAction = AMOTION_EVENT_ACTION_MASK & AMotionEvent_getAction( event ); - int isTrackpad = nSourceId == AINPUT_SOURCE_TOUCHPAD; - - // We don't care about secondary pointers right now - if( !isTrackpad && (nAction == AMOTION_EVENT_ACTION_POINTER_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_UP )) - continue; - - int x = AMotionEvent_getX( event, n ); - int y = AMotionEvent_getY( event, n ); - - if( nAction == AMOTION_EVENT_ACTION_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_DOWN ) { - //LOGI("action down %d\n", isTrackpad); - if (isTrackpad) { - trackpadPress(x, y); - } else { - mouseDown(1, x, y); - //LOGI("mouse-pointer down"); - } - } - else if( nAction == AMOTION_EVENT_ACTION_UP || nAction == AMOTION_EVENT_ACTION_POINTER_UP /*|| nAction == AMOTION_EVENT_ACTION_CANCEL*/ ) { - //LOGI("action up %d\n", isTrackpad); - if (isTrackpad) { - //LOGI("trackpad-up\n"); - trackpadRelease(x, y); - } - else { - //LOGI("mouse-pointer up\n"); - mouseUp(1, x, y); - } - } else if (nAction == AMOTION_EVENT_ACTION_MOVE ) { - if (isTrackpad) - trackpadMove(x, y); - else - mouseMove(x, y); - } - } - return 1; - } - else if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY ) - { - bool isDown = AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN; - int keyCode = AKeyEvent_getKeyCode(event); - bool pressedBack = keyCode == 4 && AInputEvent_getDeviceId(event) == 0; - bool isRepeat = AKeyEvent_getRepeatCount(event) > 0; - if (!isRepeat){ - //int scanCode = AKeyEvent_getScanCode(event); - //LOGI("key-%s : %d", isDown?"key-down":"key-up", keyCode); - //LOGI("getId: %d\n", AInputEvent_getDeviceId(event)); - //LOGI("flags: %d\n", AKeyEvent_getFlags(event)); - //LOGI("scancode: %d\n", AKeyEvent_getScanCode(event)); - - //unsigned char msg[2] = {(unsigned char)(keyCode), (unsigned char)(isDown)}; - //broadcastData(BroadcastPort, msg, 2); - - if (!pressedBack) - Keyboard::feed(keyCode, isDown); - } - if (keyCode == 4) - { - if (!isRepeat && !engine->userApp->handleBack(isDown)) - { - //LOGI("Returning 0 from engine_handle_input\n"); - return 0; - } - return 1; - } - - bool volumeButton = (keyCode == 24) || (keyCode == 25); - bool handled = !pressedBack && !volumeButton; - return handled; - } - - return 0; -} - -#endif - -/** - * This is the main entry point of a native application that is using - * android_native_app_glue. It runs in its own thread, with its own - * event loop for receiving input events and doing other things (rendering). - */ -void -android_main( struct android_app* state ); +#ifndef MAIN_CLASS +#error "Error: MAIN_CLASS must have been defined to your main class (e.g. #define MAIN_CLASS MyApp)" +#endif + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#define TAG "MAIN_CLASS" + +#include "platform/log.h" +#include "platform/input/Keyboard.h" +#include "platform/input/Mouse.h" +#include "platform/input/Controller.h" + +#include "EglConfigPrinter.h" + +const int BroadcastPort = 9991; + +/** + * Shared state for our app. + */ +struct ENGINE +{ + struct android_app* app; + + int render; + EGLDisplay display; + EGLSurface surface; + EGLContext context; + int width; + int height; + int has_focus; + App* userApp; + bool is_inited; + //bool init_gl; + struct AppContext appContext; +}; + +static void mouseDown(int buttonId, int x, int y) { + int msg[] = {buttonId, 0, x, y}; + //broadcastData(BroadcastPort, msg, sizeof(msg)); + + Mouse::feed(buttonId, 1, x, y); +} + +static void mouseUp(int buttonId, int x, int y) { + int msg[] = {buttonId, 0, x, y}; + //broadcastData(BroadcastPort, msg, sizeof(msg)); + + Mouse::feed(buttonId, 0, x, y); +} + +static void mouseMove(int x, int y) { + int msg[] = {0, 0, x, y}; + //broadcastData(BroadcastPort, msg, sizeof(msg)); + + Mouse::feed(0, 0, x, y); +} + +#if 0 + +/** + * Process the next input event. + */ +static +int32_t +engine_handle_input( struct android_app* app, AInputEvent* event ) +{ + struct ENGINE* engine = (struct ENGINE*)app->userData; + if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION ) + { + //engine->render = 1; + int nPointerCount = AMotionEvent_getPointerCount( event ); + int nSourceId = AInputEvent_getSource( event ); + int n; + + for( n = 0 ; n < nPointerCount ; ++n ) + { + int nPointerId = AMotionEvent_getPointerId( event, n ); + int nAction = AMOTION_EVENT_ACTION_MASK & AMotionEvent_getAction( event ); + int isTrackpad = nSourceId == AINPUT_SOURCE_TOUCHPAD; + + // We don't care about secondary pointers right now + if( !isTrackpad && (nAction == AMOTION_EVENT_ACTION_POINTER_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_UP )) + continue; + + int x = AMotionEvent_getX( event, n ); + int y = AMotionEvent_getY( event, n ); + + if( nAction == AMOTION_EVENT_ACTION_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_DOWN ) { + //LOGI("action down %d\n", isTrackpad); + if (isTrackpad) { + trackpadPress(x, y); + } else { + mouseDown(1, x, y); + //LOGI("mouse-pointer down"); + } + } + else if( nAction == AMOTION_EVENT_ACTION_UP || nAction == AMOTION_EVENT_ACTION_POINTER_UP /*|| nAction == AMOTION_EVENT_ACTION_CANCEL*/ ) { + //LOGI("action up %d\n", isTrackpad); + if (isTrackpad) { + //LOGI("trackpad-up\n"); + trackpadRelease(x, y); + } + else { + //LOGI("mouse-pointer up\n"); + mouseUp(1, x, y); + } + } else if (nAction == AMOTION_EVENT_ACTION_MOVE ) { + if (isTrackpad) + trackpadMove(x, y); + else + mouseMove(x, y); + } + } + return 1; + } + else if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY ) + { + bool isDown = AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN; + int keyCode = AKeyEvent_getKeyCode(event); + bool pressedBack = keyCode == 4 && AInputEvent_getDeviceId(event) == 0; + bool isRepeat = AKeyEvent_getRepeatCount(event) > 0; + if (!isRepeat){ + //int scanCode = AKeyEvent_getScanCode(event); + //LOGI("key-%s : %d", isDown?"key-down":"key-up", keyCode); + //LOGI("getId: %d\n", AInputEvent_getDeviceId(event)); + //LOGI("flags: %d\n", AKeyEvent_getFlags(event)); + //LOGI("scancode: %d\n", AKeyEvent_getScanCode(event)); + + //unsigned char msg[2] = {(unsigned char)(keyCode), (unsigned char)(isDown)}; + //broadcastData(BroadcastPort, msg, 2); + + if (!pressedBack) + Keyboard::feed(keyCode, isDown); + } + if (keyCode == 4) + { + if (!isRepeat && !engine->userApp->handleBack(isDown)) + { + //LOGI("Returning 0 from engine_handle_input\n"); + return 0; + } + return 1; + } + + bool volumeButton = (keyCode == 24) || (keyCode == 25); + bool handled = !pressedBack && !volumeButton; + return handled; + } + + return 0; +} + +#endif + +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things (rendering). + */ +void +android_main( struct android_app* state ); diff --git a/src/main_glfw.h b/src/main_glfw.h index 042c9fb..91cb11b 100755 --- a/src/main_glfw.h +++ b/src/main_glfw.h @@ -1,223 +1,223 @@ -#ifndef MAIN_GLFW_H__ -#define MAIN_GLFW_H__ - -#include "App.h" -#include "client/renderer/entity/PlayerRenderer.h" -#include "client/renderer/gles.h" -#include "GLFW/glfw3.h" - -#include -#include -#include -#include "platform/input/Keyboard.h" -#include "platform/input/Mouse.h" -#include "platform/input/Multitouch.h" -#include "AppPlatform_glfw.h" -#ifdef __EMSCRIPTEN__ -#include -#endif -static App* g_app = 0; - -int transformKey(int glfwkey) { - if (glfwkey >= GLFW_KEY_F1 && glfwkey <= GLFW_KEY_F12) { - return glfwkey - 178; - } - - switch (glfwkey) { - case GLFW_KEY_ESCAPE: return Keyboard::KEY_ESCAPE; - case GLFW_KEY_TAB: return Keyboard::KEY_TAB; - case GLFW_KEY_BACKSPACE: return Keyboard::KEY_BACKSPACE; - case GLFW_KEY_LEFT_SHIFT: return Keyboard::KEY_LSHIFT; - case GLFW_KEY_ENTER: return Keyboard::KEY_RETURN; - case GLFW_KEY_LEFT_CONTROL: return Keyboard::KEY_LEFT_CTRL; - default: return glfwkey; - } -} - -void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { - if(action == GLFW_REPEAT) return; - - if (key == GLFW_KEY_F11 && action == GLFW_PRESS) { - GLFWmonitor* monitor = glfwGetWindowMonitor(window); - if (monitor) { - // Currently fullscreen → go windowed - glfwSetWindowMonitor(window, NULL, 80, 80, 854, 480, 0); - } else { - // Currently windowed → go fullscreen on primary monitor - GLFWmonitor* primary = glfwGetPrimaryMonitor(); - const GLFWvidmode* mode = glfwGetVideoMode(primary); - glfwSetWindowMonitor(window, primary, 0, 0, mode->width, mode->height, mode->refreshRate); - } - return; - } - - Keyboard::feed(transformKey(key), action); -} - -void character_callback(GLFWwindow* window, unsigned int codepoint) { - Keyboard::feedText(codepoint); -} - -static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) { - static double lastX = 0.0, lastY = 0.0; - static bool firstMouse = true; - - if (firstMouse) { - lastX = xpos; - lastY = ypos; - firstMouse = false; - } - - double deltaX = xpos - lastX; - double deltaY = ypos - lastY; - - lastX = xpos; - lastY = ypos; - - if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) { - Mouse::feed(0, 0, xpos, ypos, deltaX, deltaY); - } else { - Mouse::feed( MouseAction::ACTION_MOVE, 0, xpos, ypos); - } - Multitouch::feed(0, 0, xpos, ypos, 0); -} - -void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { - if(action == GLFW_REPEAT) return; - - double xpos, ypos; - glfwGetCursorPos(window, &xpos, &ypos); - - if (button == GLFW_MOUSE_BUTTON_LEFT) { - Mouse::feed( MouseAction::ACTION_LEFT, action, xpos, ypos); - Multitouch::feed(1, action, xpos, ypos, 0); - } - - if (button == GLFW_MOUSE_BUTTON_RIGHT) { - Mouse::feed( MouseAction::ACTION_RIGHT, action, xpos, ypos); - } -} - -void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { - double xpos, ypos; - glfwGetCursorPos(window, &xpos, &ypos); - - Mouse::feed(3, 0, xpos, ypos, 0, yoffset); -} - -void window_size_callback(GLFWwindow* window, int width, int height) { - if (g_app) g_app->setSize(width, height); -} - -void error_callback(int error, const char* desc) { - printf("Error: %s\n", desc); -} - - -void loop() { - using clock = std::chrono::steady_clock; - auto frameStart = clock::now(); - - g_app->update(); - - glfwSwapBuffers(((AppPlatform_glfw*)g_app->platform())->window); - glfwPollEvents(); - - glfwSwapInterval(((MAIN_CLASS*)g_app)->options.getBooleanValue(OPTIONS_VSYNC) ? 1 : 0); - if(((MAIN_CLASS*)g_app)->options.getBooleanValue(OPTIONS_LIMIT_FRAMERATE)) { - auto frameEnd = clock::now(); - auto elapsed = std::chrono::duration_cast(frameEnd - frameStart); - auto target = std::chrono::microseconds(33333); // ~30 fps - if(elapsed < target) - std::this_thread::sleep_for(target - elapsed); - } -} - -int main(void) { - AppContext appContext; - -#ifndef STANDALONE_SERVER - // Platform init. - appContext.platform = new AppPlatform_glfw(); -#if defined(__EMSCRIPTEN__) - EM_ASM( - FS.mkdir('/games'); - FS.mkdir('/games/com.mojang'); - FS.mkdir('/games/com.mojang/minecraftWorlds'); - FS.mount(IDBFS, {}, '/games'); - FS.syncfs(true, function (err) {}); - ); -#endif - - glfwSetErrorCallback(error_callback); - - if (!glfwInit()) { - return 1; - } - - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); -#ifndef __EMSCRIPTEN__ - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); -#else - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); -#endif - - AppPlatform_glfw* platform = (AppPlatform_glfw*)appContext.platform; - - platform->window = glfwCreateWindow(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight(), "Minecraft PE 0.6.1", NULL, NULL); - - if (platform->window == NULL) { - return 1; - } - - glfwSetKeyCallback(platform->window, key_callback); - glfwSetCharCallback(platform->window, character_callback); - glfwSetCursorPosCallback(platform->window, cursor_position_callback); - glfwSetMouseButtonCallback(platform->window, mouse_button_callback); - glfwSetScrollCallback(platform->window, scroll_callback); - glfwSetWindowSizeCallback(platform->window, window_size_callback); - - glfwMakeContextCurrent(platform->window); - #ifndef __EMSCRIPTEN__ - gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); - glfwSwapInterval(0); - #endif -#endif - - App* app = new MAIN_CLASS(); - - g_app = app; - ((MAIN_CLASS*)g_app)->externalStoragePath = "."; - ((MAIN_CLASS*)g_app)->externalCacheStoragePath = "."; - g_app->init(appContext); - g_app->setSize(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight()); - -#ifdef __EMSCRIPTEN__ - emscripten_set_main_loop(loop, 0, 1); -#else - // Main event loop - while(!glfwWindowShouldClose(platform->window) && !app->wantToQuit()) { - loop(); - } -#endif - - delete app; - -#ifndef STANDALONE_SERVER - // Exit. - glfwDestroyWindow(platform->window); - glfwTerminate(); -#endif - - appContext.platform->finish(); - - delete appContext.platform; - - return 0; -} - -#endif /*MAIN_GLFW_H__*/ +#ifndef MAIN_GLFW_H__ +#define MAIN_GLFW_H__ + +#include "App.h" +#include "client/renderer/entity/PlayerRenderer.h" +#include "client/renderer/gles.h" +#include "GLFW/glfw3.h" + +#include +#include +#include +#include "platform/input/Keyboard.h" +#include "platform/input/Mouse.h" +#include "platform/input/Multitouch.h" +#include "AppPlatform_glfw.h" +#ifdef __EMSCRIPTEN__ +#include +#endif +static App* g_app = 0; + +int transformKey(int glfwkey) { + if (glfwkey >= GLFW_KEY_F1 && glfwkey <= GLFW_KEY_F12) { + return glfwkey - 178; + } + + switch (glfwkey) { + case GLFW_KEY_ESCAPE: return Keyboard::KEY_ESCAPE; + case GLFW_KEY_TAB: return Keyboard::KEY_TAB; + case GLFW_KEY_BACKSPACE: return Keyboard::KEY_BACKSPACE; + case GLFW_KEY_LEFT_SHIFT: return Keyboard::KEY_LSHIFT; + case GLFW_KEY_ENTER: return Keyboard::KEY_RETURN; + case GLFW_KEY_LEFT_CONTROL: return Keyboard::KEY_LEFT_CTRL; + default: return glfwkey; + } +} + +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { + if(action == GLFW_REPEAT) return; + + if (key == GLFW_KEY_F11 && action == GLFW_PRESS) { + GLFWmonitor* monitor = glfwGetWindowMonitor(window); + if (monitor) { + // Currently fullscreen → go windowed + glfwSetWindowMonitor(window, NULL, 80, 80, 854, 480, 0); + } else { + // Currently windowed → go fullscreen on primary monitor + GLFWmonitor* primary = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(primary); + glfwSetWindowMonitor(window, primary, 0, 0, mode->width, mode->height, mode->refreshRate); + } + return; + } + + Keyboard::feed(transformKey(key), action); +} + +void character_callback(GLFWwindow* window, unsigned int codepoint) { + Keyboard::feedText(codepoint); +} + +static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) { + static double lastX = 0.0, lastY = 0.0; + static bool firstMouse = true; + + if (firstMouse) { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + double deltaX = xpos - lastX; + double deltaY = ypos - lastY; + + lastX = xpos; + lastY = ypos; + + if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) { + Mouse::feed(0, 0, xpos, ypos, deltaX, deltaY); + } else { + Mouse::feed( MouseAction::ACTION_MOVE, 0, xpos, ypos); + } + Multitouch::feed(0, 0, xpos, ypos, 0); +} + +void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { + if(action == GLFW_REPEAT) return; + + double xpos, ypos; + glfwGetCursorPos(window, &xpos, &ypos); + + if (button == GLFW_MOUSE_BUTTON_LEFT) { + Mouse::feed( MouseAction::ACTION_LEFT, action, xpos, ypos); + Multitouch::feed(1, action, xpos, ypos, 0); + } + + if (button == GLFW_MOUSE_BUTTON_RIGHT) { + Mouse::feed( MouseAction::ACTION_RIGHT, action, xpos, ypos); + } +} + +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { + double xpos, ypos; + glfwGetCursorPos(window, &xpos, &ypos); + + Mouse::feed(3, 0, xpos, ypos, 0, yoffset); +} + +void window_size_callback(GLFWwindow* window, int width, int height) { + if (g_app) g_app->setSize(width, height); +} + +void error_callback(int error, const char* desc) { + printf("Error: %s\n", desc); +} + + +void loop() { + using clock = std::chrono::steady_clock; + auto frameStart = clock::now(); + + g_app->update(); + + glfwSwapBuffers(((AppPlatform_glfw*)g_app->platform())->window); + glfwPollEvents(); + + glfwSwapInterval(((MAIN_CLASS*)g_app)->options.getBooleanValue(OPTIONS_VSYNC) ? 1 : 0); + if(((MAIN_CLASS*)g_app)->options.getBooleanValue(OPTIONS_LIMIT_FRAMERATE)) { + auto frameEnd = clock::now(); + auto elapsed = std::chrono::duration_cast(frameEnd - frameStart); + auto target = std::chrono::microseconds(33333); // ~30 fps + if(elapsed < target) + std::this_thread::sleep_for(target - elapsed); + } +} + +int main(void) { + AppContext appContext; + +#ifndef STANDALONE_SERVER + // Platform init. + appContext.platform = new AppPlatform_glfw(); +#if defined(__EMSCRIPTEN__) + EM_ASM( + FS.mkdir('/games'); + FS.mkdir('/games/com.mojang'); + FS.mkdir('/games/com.mojang/minecraftWorlds'); + FS.mount(IDBFS, {}, '/games'); + FS.syncfs(true, function (err) {}); + ); +#endif + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) { + return 1; + } + + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); +#ifndef __EMSCRIPTEN__ + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); +#else + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); +#endif + + AppPlatform_glfw* platform = (AppPlatform_glfw*)appContext.platform; + + platform->window = glfwCreateWindow(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight(), "Minecraft PE 0.6.1", NULL, NULL); + + if (platform->window == NULL) { + return 1; + } + + glfwSetKeyCallback(platform->window, key_callback); + glfwSetCharCallback(platform->window, character_callback); + glfwSetCursorPosCallback(platform->window, cursor_position_callback); + glfwSetMouseButtonCallback(platform->window, mouse_button_callback); + glfwSetScrollCallback(platform->window, scroll_callback); + glfwSetWindowSizeCallback(platform->window, window_size_callback); + + glfwMakeContextCurrent(platform->window); + #ifndef __EMSCRIPTEN__ + gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); + glfwSwapInterval(0); + #endif +#endif + + App* app = new MAIN_CLASS(); + + g_app = app; + ((MAIN_CLASS*)g_app)->externalStoragePath = "."; + ((MAIN_CLASS*)g_app)->externalCacheStoragePath = "."; + g_app->init(appContext); + g_app->setSize(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight()); + +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(loop, 0, 1); +#else + // Main event loop + while(!glfwWindowShouldClose(platform->window) && !app->wantToQuit()) { + loop(); + } +#endif + + delete app; + +#ifndef STANDALONE_SERVER + // Exit. + glfwDestroyWindow(platform->window); + glfwTerminate(); +#endif + + appContext.platform->finish(); + + delete appContext.platform; + + return 0; +} + +#endif /*MAIN_GLFW_H__*/ diff --git a/src/main_win32.h b/src/main_win32.h index 69d0165..9191c6b 100755 --- a/src/main_win32.h +++ b/src/main_win32.h @@ -1,331 +1,331 @@ -#ifndef MAIN_WIN32_H__ -#define MAIN_WIN32_H__ - -/* -#define _CRTDBG_MAP_ALLOC -#include -#include -*/ - -#include "client/renderer/gles.h" -#include -// #include -#define WIN32_LEAN_AND_MEAN 1 -#include -#include - -#include -#include -#include "SharedConstants.h" - -#include -#include "platform/input/Mouse.h" -#include "platform/input/Multitouch.h" -#include "util/Mth.h" -#include "AppPlatform_win32.h" - -static App* g_app = 0; -static volatile bool g_running = true; - -static int getBits(int bits, int startBitInclusive, int endBitExclusive, int shiftTruncate) { - int sum = 0; - for (int i = startBitInclusive; i> startBitInclusive) : sum; -} - -void resizeWindow(HWND hWnd, int nWidth, int nHeight) { - RECT rcClient, rcWindow; - POINT ptDiff; - GetClientRect(hWnd, &rcClient); - GetWindowRect(hWnd, &rcWindow); - ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right; - ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom; - MoveWindow(hWnd,rcWindow.left, rcWindow.top, nWidth + ptDiff.x, nHeight + ptDiff.y, TRUE); -} - -void toggleResolutions(HWND hwnd, int direction) { - static int n = 0; - static int sizes[][3] = { - {854, 480, 1}, - {800, 480, 1}, - {480, 320, 1}, - {1024, 768, 1}, - {1280, 800, 1}, - {1024, 580, 1} - }; - static int count = sizeof(sizes) / sizeof(sizes[0]); - n = (count + n + direction) % count; - - int* size = sizes[n]; - int k = size[2]; - - resizeWindow(hwnd, k * size[0], k * size[1]); -} - -LRESULT WINAPI windowProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { - LRESULT retval = 1; - - switch (uMsg) - { - case WM_KEYDOWN: { - if (wParam == 33) toggleResolutions(hWnd, -1); - if (wParam == 34) toggleResolutions(hWnd, +1); - - //if (wParam == 'Q') ((Minecraft*)g_app)->leaveGame(); - Keyboard::feed((unsigned char) wParam, 1); //(unsigned char) getBits(lParam, 16, 23, 1) - - //char* lParamConv = (char*) &lParam; - //int convertResult = ToUnicode(wParam, lParamConv[1], ) - - return 0; - } - case WM_KEYUP: { - Keyboard::feed((unsigned char) wParam, 0); //(unsigned char) getBits(lParam, 16, 23, 1) - return 0; - } - case WM_CHAR: { - //LOGW("WM_CHAR: %d\n", wParam); - if(wParam >= 32) - Keyboard::feedText(wParam); - return 0; - } - case WM_LBUTTONDOWN: { - Mouse::feed( MouseAction::ACTION_LEFT, 1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - Multitouch::feed(1, 1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0); - break; - } - case WM_LBUTTONUP: { - Mouse::feed( MouseAction::ACTION_LEFT, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - Multitouch::feed(1, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0); - break; - } - case WM_RBUTTONDOWN: { - Mouse::feed( MouseAction::ACTION_RIGHT, 1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - break; - } - case WM_RBUTTONUP: { - Mouse::feed( MouseAction::ACTION_RIGHT, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - break; - } - case WM_MOUSEMOVE: { - Mouse::feed( MouseAction::ACTION_MOVE, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - Multitouch::feed(0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0); - break; - } - case WM_MOUSEWHEEL: { - // wheel delta is multiples of WHEEL_DELTA (120); convert to +/-1 - int delta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA; - short x = GET_X_LPARAM(lParam); - short y = GET_Y_LPARAM(lParam); - Mouse::feed(MouseAction::ACTION_WHEEL, 0, x, y, 0, delta); - break; - } - default: - if (uMsg == WM_NCDESTROY) g_running = false; - else { - if (uMsg == WM_SIZE) { - if (g_app) g_app->setSize( GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) ); - } - } - retval = DefWindowProc (hWnd, uMsg, wParam, lParam); - break; - } - return retval; -} - -void platform(HWND *result, int width, int height) { - WNDCLASS wc; - RECT wRect; - HWND hwnd; - HINSTANCE hInstance; - - wRect.left = 0L; - wRect.right = (long)width; - wRect.top = 0L; - wRect.bottom = (long)height; - - hInstance = GetModuleHandle(NULL); - - wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wc.lpfnWndProc = (WNDPROC)windowProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = NULL; - wc.lpszMenuName = NULL; - wc.lpszClassName = "OGLES"; - - RegisterClass(&wc); - - AdjustWindowRectEx(&wRect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE); - - std::string windowName("Minecraft PE" + Common::getGameVersionString()); - - hwnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, "OGLES", windowName.c_str(), WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, wRect.right-wRect.left, wRect.bottom-wRect.top, NULL, NULL, hInstance, NULL); - *result = hwnd; -} - -/** Thread that reads input data via UDP network datagrams - and fills Mouse and Keyboard structures accordingly. - @note: The bound local net address is unfortunately - hard coded right now (to prevent wrong Interface) */ -void inputNetworkThread(void* userdata) -{ - // set up an UDP socket for listening - WSADATA wsaData; - if (WSAStartup(0x101, &wsaData)) { - printf("Couldn't initialize winsock\n"); - return; - } - - SOCKET s = socket(AF_INET, SOCK_DGRAM, 0); - if (s == INVALID_SOCKET) { - printf("Couldn't create socket\n"); - return; - } - - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(9991); - addr.sin_addr.s_addr = inet_addr("192.168.0.119"); - - if (bind(s, (sockaddr*)&addr, sizeof(addr))) { - printf("Couldn't bind socket to port 9991\n"); - return; - } - - sockaddr fromAddr; - int fromAddrLen = sizeof(fromAddr); - - char buf[1500]; - int* iptrBuf = (int*)buf; - - printf("input-server listening...\n"); - - while (1) { - int read = recvfrom(s, buf, 1500, 0, &fromAddr, &fromAddrLen); - if (read < 0) - { - printf("recvfrom failed with code: %d\n", WSAGetLastError()); - return; - } - // Keyboard - if (read == 2) { - Keyboard::feed((unsigned char) buf[0], (int)buf[1]); - } - // Mouse - else if (read == 16) { - Mouse::feed(iptrBuf[0], iptrBuf[1], iptrBuf[2], iptrBuf[2]); - } - } -} - -int main(void) { - AppContext appContext; - MSG sMessage; - -#ifndef STANDALONE_SERVER - - EGLint aEGLAttributes[] = { - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 16, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, - EGL_NONE - }; - EGLint aEGLContextAttributes[] = { - EGL_CONTEXT_CLIENT_VERSION, 1, - EGL_NONE - }; - - EGLConfig m_eglConfig[1]; - EGLint nConfigs; - - HWND hwnd; - g_running = true; - - // Platform init. - appContext.platform = new AppPlatform_win32(); - platform(&hwnd, appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight()); - ShowWindow(hwnd, SW_SHOW); - SetForegroundWindow(hwnd); - SetFocus(hwnd); - - // EGL init. - appContext.display = eglGetDisplay(GetDC(hwnd)); - //m_eglDisplay = eglGetDisplay((EGLNativeDisplayType) EGL_DEFAULT_DISPLAY); - - eglInitialize(appContext.display, NULL, NULL); - - eglChooseConfig(appContext.display, aEGLAttributes, m_eglConfig, 1, &nConfigs); - printf("EGLConfig = %p\n", m_eglConfig[0]); - - appContext.surface = eglCreateWindowSurface(appContext.display, m_eglConfig[0], (NativeWindowType)hwnd, 0); - printf("EGLSurface = %p\n", appContext.surface); - - appContext.context = eglCreateContext(appContext.display, m_eglConfig[0], EGL_NO_CONTEXT, NULL);//aEGLContextAttributes); - printf("EGLContext = %p\n", appContext.context); - if (!appContext.context) { - printf("EGL error: %d\n", eglGetError()); - } - - eglMakeCurrent(appContext.display, appContext.surface, appContext.surface, appContext.context); - - glInit(); - -#endif - App* app = new MAIN_CLASS(); - - g_app = app; - ((MAIN_CLASS*)g_app)->externalStoragePath = "."; - ((MAIN_CLASS*)g_app)->externalCacheStoragePath = "."; - g_app->init(appContext); - g_app->setSize(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight()); - - //_beginthread(inputNetworkThread, 0, 0); - - // Main event loop - while(g_running && !app->wantToQuit()) - { - // Do Windows stuff: - while (PeekMessage (&sMessage, NULL, 0, 0, PM_REMOVE) > 0) { - if(sMessage.message == WM_QUIT) { - g_running = false; - break; - } - else { - TranslateMessage(&sMessage); - DispatchMessage(&sMessage); - } - } - app->update(); - - //Sleep(30); - } - - Sleep(50); - delete app; - Sleep(50); - appContext.platform->finish(); - Sleep(50); - delete appContext.platform; - Sleep(50); - //printf("_crtDumpMemoryLeaks: %d\n", _CrtDumpMemoryLeaks()); - -#ifndef STANDALONE_SERVER - // Exit. - eglMakeCurrent(appContext.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroyContext(appContext.display, appContext.context); - eglDestroySurface(appContext.display, appContext.surface); - eglTerminate(appContext.display); -#endif - - return 0; -} - -#endif /*MAIN_WIN32_H__*/ +#ifndef MAIN_WIN32_H__ +#define MAIN_WIN32_H__ + +/* +#define _CRTDBG_MAP_ALLOC +#include +#include +*/ + +#include "client/renderer/gles.h" +#include +// #include +#define WIN32_LEAN_AND_MEAN 1 +#include +#include + +#include +#include +#include "SharedConstants.h" + +#include +#include "platform/input/Mouse.h" +#include "platform/input/Multitouch.h" +#include "util/Mth.h" +#include "AppPlatform_win32.h" + +static App* g_app = 0; +static volatile bool g_running = true; + +static int getBits(int bits, int startBitInclusive, int endBitExclusive, int shiftTruncate) { + int sum = 0; + for (int i = startBitInclusive; i> startBitInclusive) : sum; +} + +void resizeWindow(HWND hWnd, int nWidth, int nHeight) { + RECT rcClient, rcWindow; + POINT ptDiff; + GetClientRect(hWnd, &rcClient); + GetWindowRect(hWnd, &rcWindow); + ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right; + ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom; + MoveWindow(hWnd,rcWindow.left, rcWindow.top, nWidth + ptDiff.x, nHeight + ptDiff.y, TRUE); +} + +void toggleResolutions(HWND hwnd, int direction) { + static int n = 0; + static int sizes[][3] = { + {854, 480, 1}, + {800, 480, 1}, + {480, 320, 1}, + {1024, 768, 1}, + {1280, 800, 1}, + {1024, 580, 1} + }; + static int count = sizeof(sizes) / sizeof(sizes[0]); + n = (count + n + direction) % count; + + int* size = sizes[n]; + int k = size[2]; + + resizeWindow(hwnd, k * size[0], k * size[1]); +} + +LRESULT WINAPI windowProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { + LRESULT retval = 1; + + switch (uMsg) + { + case WM_KEYDOWN: { + if (wParam == 33) toggleResolutions(hWnd, -1); + if (wParam == 34) toggleResolutions(hWnd, +1); + + //if (wParam == 'Q') ((Minecraft*)g_app)->leaveGame(); + Keyboard::feed((unsigned char) wParam, 1); //(unsigned char) getBits(lParam, 16, 23, 1) + + //char* lParamConv = (char*) &lParam; + //int convertResult = ToUnicode(wParam, lParamConv[1], ) + + return 0; + } + case WM_KEYUP: { + Keyboard::feed((unsigned char) wParam, 0); //(unsigned char) getBits(lParam, 16, 23, 1) + return 0; + } + case WM_CHAR: { + //LOGW("WM_CHAR: %d\n", wParam); + if(wParam >= 32) + Keyboard::feedText(wParam); + return 0; + } + case WM_LBUTTONDOWN: { + Mouse::feed( MouseAction::ACTION_LEFT, 1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + Multitouch::feed(1, 1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0); + break; + } + case WM_LBUTTONUP: { + Mouse::feed( MouseAction::ACTION_LEFT, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + Multitouch::feed(1, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0); + break; + } + case WM_RBUTTONDOWN: { + Mouse::feed( MouseAction::ACTION_RIGHT, 1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + break; + } + case WM_RBUTTONUP: { + Mouse::feed( MouseAction::ACTION_RIGHT, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + break; + } + case WM_MOUSEMOVE: { + Mouse::feed( MouseAction::ACTION_MOVE, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + Multitouch::feed(0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0); + break; + } + case WM_MOUSEWHEEL: { + // wheel delta is multiples of WHEEL_DELTA (120); convert to +/-1 + int delta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA; + short x = GET_X_LPARAM(lParam); + short y = GET_Y_LPARAM(lParam); + Mouse::feed(MouseAction::ACTION_WHEEL, 0, x, y, 0, delta); + break; + } + default: + if (uMsg == WM_NCDESTROY) g_running = false; + else { + if (uMsg == WM_SIZE) { + if (g_app) g_app->setSize( GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) ); + } + } + retval = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + return retval; +} + +void platform(HWND *result, int width, int height) { + WNDCLASS wc; + RECT wRect; + HWND hwnd; + HINSTANCE hInstance; + + wRect.left = 0L; + wRect.right = (long)width; + wRect.top = 0L; + wRect.bottom = (long)height; + + hInstance = GetModuleHandle(NULL); + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)windowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "OGLES"; + + RegisterClass(&wc); + + AdjustWindowRectEx(&wRect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE); + + std::string windowName("Minecraft PE" + Common::getGameVersionString()); + + hwnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, "OGLES", windowName.c_str(), WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, wRect.right-wRect.left, wRect.bottom-wRect.top, NULL, NULL, hInstance, NULL); + *result = hwnd; +} + +/** Thread that reads input data via UDP network datagrams + and fills Mouse and Keyboard structures accordingly. + @note: The bound local net address is unfortunately + hard coded right now (to prevent wrong Interface) */ +void inputNetworkThread(void* userdata) +{ + // set up an UDP socket for listening + WSADATA wsaData; + if (WSAStartup(0x101, &wsaData)) { + printf("Couldn't initialize winsock\n"); + return; + } + + SOCKET s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == INVALID_SOCKET) { + printf("Couldn't create socket\n"); + return; + } + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(9991); + addr.sin_addr.s_addr = inet_addr("192.168.0.119"); + + if (bind(s, (sockaddr*)&addr, sizeof(addr))) { + printf("Couldn't bind socket to port 9991\n"); + return; + } + + sockaddr fromAddr; + int fromAddrLen = sizeof(fromAddr); + + char buf[1500]; + int* iptrBuf = (int*)buf; + + printf("input-server listening...\n"); + + while (1) { + int read = recvfrom(s, buf, 1500, 0, &fromAddr, &fromAddrLen); + if (read < 0) + { + printf("recvfrom failed with code: %d\n", WSAGetLastError()); + return; + } + // Keyboard + if (read == 2) { + Keyboard::feed((unsigned char) buf[0], (int)buf[1]); + } + // Mouse + else if (read == 16) { + Mouse::feed(iptrBuf[0], iptrBuf[1], iptrBuf[2], iptrBuf[2]); + } + } +} + +int main(void) { + AppContext appContext; + MSG sMessage; + +#ifndef STANDALONE_SERVER + + EGLint aEGLAttributes[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_NONE + }; + EGLint aEGLContextAttributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 1, + EGL_NONE + }; + + EGLConfig m_eglConfig[1]; + EGLint nConfigs; + + HWND hwnd; + g_running = true; + + // Platform init. + appContext.platform = new AppPlatform_win32(); + platform(&hwnd, appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight()); + ShowWindow(hwnd, SW_SHOW); + SetForegroundWindow(hwnd); + SetFocus(hwnd); + + // EGL init. + appContext.display = eglGetDisplay(GetDC(hwnd)); + //m_eglDisplay = eglGetDisplay((EGLNativeDisplayType) EGL_DEFAULT_DISPLAY); + + eglInitialize(appContext.display, NULL, NULL); + + eglChooseConfig(appContext.display, aEGLAttributes, m_eglConfig, 1, &nConfigs); + printf("EGLConfig = %p\n", m_eglConfig[0]); + + appContext.surface = eglCreateWindowSurface(appContext.display, m_eglConfig[0], (NativeWindowType)hwnd, 0); + printf("EGLSurface = %p\n", appContext.surface); + + appContext.context = eglCreateContext(appContext.display, m_eglConfig[0], EGL_NO_CONTEXT, NULL);//aEGLContextAttributes); + printf("EGLContext = %p\n", appContext.context); + if (!appContext.context) { + printf("EGL error: %d\n", eglGetError()); + } + + eglMakeCurrent(appContext.display, appContext.surface, appContext.surface, appContext.context); + + glInit(); + +#endif + App* app = new MAIN_CLASS(); + + g_app = app; + ((MAIN_CLASS*)g_app)->externalStoragePath = "."; + ((MAIN_CLASS*)g_app)->externalCacheStoragePath = "."; + g_app->init(appContext); + g_app->setSize(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight()); + + //_beginthread(inputNetworkThread, 0, 0); + + // Main event loop + while(g_running && !app->wantToQuit()) + { + // Do Windows stuff: + while (PeekMessage (&sMessage, NULL, 0, 0, PM_REMOVE) > 0) { + if(sMessage.message == WM_QUIT) { + g_running = false; + break; + } + else { + TranslateMessage(&sMessage); + DispatchMessage(&sMessage); + } + } + app->update(); + + //Sleep(30); + } + + Sleep(50); + delete app; + Sleep(50); + appContext.platform->finish(); + Sleep(50); + delete appContext.platform; + Sleep(50); + //printf("_crtDumpMemoryLeaks: %d\n", _CrtDumpMemoryLeaks()); + +#ifndef STANDALONE_SERVER + // Exit. + eglMakeCurrent(appContext.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(appContext.display, appContext.context); + eglDestroySurface(appContext.display, appContext.surface); + eglTerminate(appContext.display); +#endif + + return 0; +} + +#endif /*MAIN_WIN32_H__*/ diff --git a/src/nbt/ByteArrayTag.h b/src/nbt/ByteArrayTag.h index 6f32f40..8618463 100755 --- a/src/nbt/ByteArrayTag.h +++ b/src/nbt/ByteArrayTag.h @@ -1,79 +1,79 @@ -#ifndef COM_MOJANG_NBT__ByteArrayTag_H__ -#define COM_MOJANG_NBT__ByteArrayTag_H__ - -//package com.mojang.nbt; - -/* import java.io.* */ - -#include "Tag.h" -#include - -typedef struct TagMemoryChunk { - TagMemoryChunk() - : data(NULL), - len(0) - {} - void* data; - int len; -} TagMemoryChunk; - -class ByteArrayTag: public Tag -{ - typedef Tag super; -public: - TagMemoryChunk data; - - ByteArrayTag(const std::string& name) - : super(name) - { - } - - ByteArrayTag(const std::string& name, TagMemoryChunk data) - : super(name), - data(data) - { - } - - char getId() const { - return TAG_Byte_Array; - } - - std::string toString() const { - std::stringstream ss; - ss << "[" << data.len << " bytes]"; - return ss.str(); - } - - //@Override - bool equals(const Tag& rhs) const { - if (super::equals(rhs)) { - ByteArrayTag& o = (ByteArrayTag&) rhs; - if (data.len != o.data.len) - return false; - - return memcmp(data.data, o.data.data, data.len) == 0; - } - return false; - } - - //@Override - Tag* copy() const { - TagMemoryChunk chunk; - chunk.data = new char[data.len]; - memcpy(chunk.data, data.data, data.len); - return new ByteArrayTag(getName(), chunk); - } - - void write(IDataOutput* dos) /*throws IOException*/ { - dos->writeInt(data.len); - dos->writeBytes(data.data, data.len); - } - - void load(IDataInput* dis) /*throws IOException*/ { - int length = dis->readInt(); - data.data = new char[length]; - dis->readBytes(data.data, length); - } -}; - -#endif /*COM_MOJANG_NBT__ByteArrayTag_H__*/ +#ifndef COM_MOJANG_NBT__ByteArrayTag_H__ +#define COM_MOJANG_NBT__ByteArrayTag_H__ + +//package com.mojang.nbt; + +/* import java.io.* */ + +#include "Tag.h" +#include + +typedef struct TagMemoryChunk { + TagMemoryChunk() + : data(NULL), + len(0) + {} + void* data; + int len; +} TagMemoryChunk; + +class ByteArrayTag: public Tag +{ + typedef Tag super; +public: + TagMemoryChunk data; + + ByteArrayTag(const std::string& name) + : super(name) + { + } + + ByteArrayTag(const std::string& name, TagMemoryChunk data) + : super(name), + data(data) + { + } + + char getId() const { + return TAG_Byte_Array; + } + + std::string toString() const { + std::stringstream ss; + ss << "[" << data.len << " bytes]"; + return ss.str(); + } + + //@Override + bool equals(const Tag& rhs) const { + if (super::equals(rhs)) { + ByteArrayTag& o = (ByteArrayTag&) rhs; + if (data.len != o.data.len) + return false; + + return memcmp(data.data, o.data.data, data.len) == 0; + } + return false; + } + + //@Override + Tag* copy() const { + TagMemoryChunk chunk; + chunk.data = new char[data.len]; + memcpy(chunk.data, data.data, data.len); + return new ByteArrayTag(getName(), chunk); + } + + void write(IDataOutput* dos) /*throws IOException*/ { + dos->writeInt(data.len); + dos->writeBytes(data.data, data.len); + } + + void load(IDataInput* dis) /*throws IOException*/ { + int length = dis->readInt(); + data.data = new char[length]; + dis->readBytes(data.data, length); + } +}; + +#endif /*COM_MOJANG_NBT__ByteArrayTag_H__*/ diff --git a/src/nbt/ByteTag.h b/src/nbt/ByteTag.h index de3f648..7b1c2c7 100755 --- a/src/nbt/ByteTag.h +++ b/src/nbt/ByteTag.h @@ -1,57 +1,57 @@ -#ifndef COM_MOJANG_NBT__ByteTag_H__ -#define COM_MOJANG_NBT__ByteTag_H__ - -//package com.mojang.nbt; - -/* import java.io.* */ - -#include "Tag.h" -#include - -class ByteTag: public Tag -{ - typedef Tag super; -public: - char data; - - ByteTag(const std::string& name) - : super(name) - {} - - ByteTag(const std::string& name, char data) - : super(name), - data(data) - { - } - - void write(IDataOutput* dos) /*throws IOException*/ { - dos->writeByte(data); - } - - void load(IDataInput* dis) /*throws IOException*/ { - data = dis->readByte(); - } - - char getId() const { - return TAG_Byte; - } - - std::string toString() const { - return std::string(data, 1); - } - - //@Override - bool equals(const Tag& rhs) const { - if (super::equals(rhs)) { - return data == ((ByteTag&)rhs).data; - } - return false; - } - - //@Override - Tag* copy() const { - return new ByteTag(getName(), data); - } -}; - -#endif /*COM_MOJANG_NBT__ByteTag_H__*/ +#ifndef COM_MOJANG_NBT__ByteTag_H__ +#define COM_MOJANG_NBT__ByteTag_H__ + +//package com.mojang.nbt; + +/* import java.io.* */ + +#include "Tag.h" +#include + +class ByteTag: public Tag +{ + typedef Tag super; +public: + char data; + + ByteTag(const std::string& name) + : super(name) + {} + + ByteTag(const std::string& name, char data) + : super(name), + data(data) + { + } + + void write(IDataOutput* dos) /*throws IOException*/ { + dos->writeByte(data); + } + + void load(IDataInput* dis) /*throws IOException*/ { + data = dis->readByte(); + } + + char getId() const { + return TAG_Byte; + } + + std::string toString() const { + return std::string(data, 1); + } + + //@Override + bool equals(const Tag& rhs) const { + if (super::equals(rhs)) { + return data == ((ByteTag&)rhs).data; + } + return false; + } + + //@Override + Tag* copy() const { + return new ByteTag(getName(), data); + } +}; + +#endif /*COM_MOJANG_NBT__ByteTag_H__*/ diff --git a/src/nbt/CompoundTag.h b/src/nbt/CompoundTag.h index 5873c74..1e3af9c 100755 --- a/src/nbt/CompoundTag.h +++ b/src/nbt/CompoundTag.h @@ -1,242 +1,242 @@ -#ifndef COM_MOJANG_NBT__CompoundTag_H__ -#define COM_MOJANG_NBT__CompoundTag_H__ - -//package com.mojang.nbt; - -#include "Tag.h" -#include -#include -#include -#include -#include "../util/CollectionUtils.h" - -#include "ByteTag.h" -#include "ShortTag.h" -#include "IntTag.h" -#include "LongTag.h" -#include "FloatTag.h" -#include "DoubleTag.h" -#include "ListTag.h" -#include "StringTag.h" -#include "ByteArrayTag.h" - -class CompoundTag: public Tag -{ - typedef Tag super; - typedef std::map TagMap; -public: - CompoundTag() - : super("") - { - } - - CompoundTag(const std::string& name) - : super(name) - { - } - - void write(IDataOutput* dos) /*throws IOException*/ { - for (TagMap::iterator it = tags.begin(); it != tags.end(); ++it) { - Tag::writeNamedTag(it->second, dos); - } - dos->writeByte(Tag::TAG_End); - } - - void load(IDataInput* dis) /*throws IOException*/ { - tags.clear(); - Tag* tag = NULL; - - while ((tag = Tag::readNamedTag(dis)) && tag->getId() != Tag::TAG_End) { - tags.insert(std::make_pair(tag->getName(), tag)); - } - // is now of type TAG_End (that's new'ed). Delete it. - delete tag; - } - - void getAllTags(std::vector& tags_) const { - //std::transform(tags.begin(), tags.end(), std::back_inserter(tags_), PairValueFunctor()); - for (TagMap::const_iterator it = tags.begin(); it != tags.end(); ++it) { - tags_.push_back(it->second); - } - } - - char getId() const { - return TAG_Compound; - } - - void put(const std::string& name, Tag* tag) { - tags.insert(std::make_pair(name, tag->setName(name))); - } - - void putByte(const std::string& name, char value) { - tags.insert(std::make_pair(name, new ByteTag(name, value))); - } - - void putShort(const std::string& name, short value) { - tags.insert(std::make_pair(name, new ShortTag(name, value))); - } - - void putInt(const std::string& name, int value) { - tags.insert(std::make_pair(name, new IntTag(name, value))); - } - - void putLong(const std::string& name, long value) { - tags.insert(std::make_pair(name, new LongTag(name, value))); - } - - void putFloat(const std::string& name, float value) { - tags.insert(std::make_pair(name, new FloatTag(name, value))); - } - - void putDouble(const std::string& name, float value) { - tags.insert(std::make_pair(name, new DoubleTag(name, value))); - } - - void putString(const std::string& name, const std::string& value) { - tags.insert(std::make_pair(name, new StringTag(name, value))); - } - - void putByteArray(const std::string& name, TagMemoryChunk mem) { - tags.insert(std::make_pair(name, new ByteArrayTag(name, mem))); - } - - void putCompound(const std::string& name, CompoundTag* value) { - tags.insert(std::make_pair(name, value->setName(name))); - } - - void putBoolean(const std::string& string, bool val) { - putByte(string, val ? (char) 1 : 0); - } - - Tag* get(const std::string& name) const { - TagMap::const_iterator it = tags.find(name); - if (it == tags.end()) - return NULL; - return it->second; - } - - __inline bool contains(const std::string& name) const { - return tags.find(name) != tags.end(); - } - bool contains(const std::string& name, int type) const { - Tag* tag = get(name); - return tag && tag->getId() == type; - } - - char getByte(const std::string& name) const { - if (!contains(name, TAG_Byte)) return (char) 0; - return ((ByteTag*) get(name))->data; - } - - short getShort(const std::string& name) const { - if (!contains(name, TAG_Short)) return (short) 0; - return ((ShortTag*) get(name))->data; - } - - int getInt(const std::string& name) const { - if (!contains(name, TAG_Int)) return (int) 0; - return ((IntTag*) get(name))->data; - } - - long long getLong(const std::string& name) const { - if (!contains(name, TAG_Long)) return (long long) 0; - return ((LongTag*) get(name))->data; - } - - float getFloat(const std::string& name) const { - if (!contains(name, TAG_Float)) return (float) 0; - return ((FloatTag*) get(name))->data; - } - - double getDouble(const std::string& name) const { - if (!contains(name, TAG_Double)) return (double) 0; - return ((DoubleTag*) get(name))->data; - } - - std::string getString(const std::string& name) const { - if (!contains(name, TAG_String)) return ""; - return ((StringTag*) get(name))->data; - } - - TagMemoryChunk getByteArray(const std::string& name) const { - if (!contains(name, TAG_Byte_Array)) return TagMemoryChunk(); - return ((ByteArrayTag*) get(name))->data; - } - - CompoundTag* getCompound(const std::string& name) const { - if (!contains(name, TAG_Compound)) return new CompoundTag(name); - return (CompoundTag*) get(name); - } - - ListTag* getList(const std::string& name) const { - if (!contains(name, TAG_List)) return new ListTag(name); - return (ListTag*) get(name); - } - - bool getBoolean(const std::string& string) const { - return getByte(string) != 0; - } - - std::string toString() const { - std::stringstream ss; - ss << tags.size() << " entries"; - return ss.str(); - } - - void print(const std::string& prefix_, PrintStream& out) const { - super::print(prefix_, out); - std::string prefix = prefix_; - out.print(prefix); out.println("{"); - prefix += " "; - for (TagMap::const_iterator it = tags.begin(); it != tags.end(); ++it) { - (it->second)->print(prefix, out); - } - out.print(prefix_); - out.println("}"); - } - - bool isEmpty() const { - return tags.empty(); - } - - Tag* copy() const { - CompoundTag* tag = new CompoundTag(getName()); - for (TagMap::const_iterator it = tags.begin(); it != tags.end(); ++it) { - //tag->put(it->first, get(it->first)->copy()); - tag->put(it->first, it->second->copy()); - } - return tag; - } - - //@Override - bool equals(const Tag& obj) const { - if (super::equals(obj)) { - CompoundTag& o = (CompoundTag&) obj; - - if (tags.size() != o.tags.size()) - return false; - - for (TagMap::const_iterator it = tags.begin(), jt = o.tags.begin(); it != tags.end(); ++it, ++jt) { - if (it->first != jt->first) return false; - if (!it->second->equals(*jt->second)) return false; - } - - return true; - } - return false; - } - - void deleteChildren() { - TagMap::iterator it = tags.begin(); - for (; it != tags.end(); ++it) { - if (!it->second) - continue; - it->second->deleteChildren(); - delete it->second; - } - } -private: - TagMap tags; -}; - -#endif /*COM_MOJANG_NBT__CompoundTag_H__*/ +#ifndef COM_MOJANG_NBT__CompoundTag_H__ +#define COM_MOJANG_NBT__CompoundTag_H__ + +//package com.mojang.nbt; + +#include "Tag.h" +#include +#include +#include +#include +#include "../util/CollectionUtils.h" + +#include "ByteTag.h" +#include "ShortTag.h" +#include "IntTag.h" +#include "LongTag.h" +#include "FloatTag.h" +#include "DoubleTag.h" +#include "ListTag.h" +#include "StringTag.h" +#include "ByteArrayTag.h" + +class CompoundTag: public Tag +{ + typedef Tag super; + typedef std::map TagMap; +public: + CompoundTag() + : super("") + { + } + + CompoundTag(const std::string& name) + : super(name) + { + } + + void write(IDataOutput* dos) /*throws IOException*/ { + for (TagMap::iterator it = tags.begin(); it != tags.end(); ++it) { + Tag::writeNamedTag(it->second, dos); + } + dos->writeByte(Tag::TAG_End); + } + + void load(IDataInput* dis) /*throws IOException*/ { + tags.clear(); + Tag* tag = NULL; + + while ((tag = Tag::readNamedTag(dis)) && tag->getId() != Tag::TAG_End) { + tags.insert(std::make_pair(tag->getName(), tag)); + } + // is now of type TAG_End (that's new'ed). Delete it. + delete tag; + } + + void getAllTags(std::vector& tags_) const { + //std::transform(tags.begin(), tags.end(), std::back_inserter(tags_), PairValueFunctor()); + for (TagMap::const_iterator it = tags.begin(); it != tags.end(); ++it) { + tags_.push_back(it->second); + } + } + + char getId() const { + return TAG_Compound; + } + + void put(const std::string& name, Tag* tag) { + tags.insert(std::make_pair(name, tag->setName(name))); + } + + void putByte(const std::string& name, char value) { + tags.insert(std::make_pair(name, new ByteTag(name, value))); + } + + void putShort(const std::string& name, short value) { + tags.insert(std::make_pair(name, new ShortTag(name, value))); + } + + void putInt(const std::string& name, int value) { + tags.insert(std::make_pair(name, new IntTag(name, value))); + } + + void putLong(const std::string& name, long value) { + tags.insert(std::make_pair(name, new LongTag(name, value))); + } + + void putFloat(const std::string& name, float value) { + tags.insert(std::make_pair(name, new FloatTag(name, value))); + } + + void putDouble(const std::string& name, float value) { + tags.insert(std::make_pair(name, new DoubleTag(name, value))); + } + + void putString(const std::string& name, const std::string& value) { + tags.insert(std::make_pair(name, new StringTag(name, value))); + } + + void putByteArray(const std::string& name, TagMemoryChunk mem) { + tags.insert(std::make_pair(name, new ByteArrayTag(name, mem))); + } + + void putCompound(const std::string& name, CompoundTag* value) { + tags.insert(std::make_pair(name, value->setName(name))); + } + + void putBoolean(const std::string& string, bool val) { + putByte(string, val ? (char) 1 : 0); + } + + Tag* get(const std::string& name) const { + TagMap::const_iterator it = tags.find(name); + if (it == tags.end()) + return NULL; + return it->second; + } + + __inline bool contains(const std::string& name) const { + return tags.find(name) != tags.end(); + } + bool contains(const std::string& name, int type) const { + Tag* tag = get(name); + return tag && tag->getId() == type; + } + + char getByte(const std::string& name) const { + if (!contains(name, TAG_Byte)) return (char) 0; + return ((ByteTag*) get(name))->data; + } + + short getShort(const std::string& name) const { + if (!contains(name, TAG_Short)) return (short) 0; + return ((ShortTag*) get(name))->data; + } + + int getInt(const std::string& name) const { + if (!contains(name, TAG_Int)) return (int) 0; + return ((IntTag*) get(name))->data; + } + + long long getLong(const std::string& name) const { + if (!contains(name, TAG_Long)) return (long long) 0; + return ((LongTag*) get(name))->data; + } + + float getFloat(const std::string& name) const { + if (!contains(name, TAG_Float)) return (float) 0; + return ((FloatTag*) get(name))->data; + } + + double getDouble(const std::string& name) const { + if (!contains(name, TAG_Double)) return (double) 0; + return ((DoubleTag*) get(name))->data; + } + + std::string getString(const std::string& name) const { + if (!contains(name, TAG_String)) return ""; + return ((StringTag*) get(name))->data; + } + + TagMemoryChunk getByteArray(const std::string& name) const { + if (!contains(name, TAG_Byte_Array)) return TagMemoryChunk(); + return ((ByteArrayTag*) get(name))->data; + } + + CompoundTag* getCompound(const std::string& name) const { + if (!contains(name, TAG_Compound)) return new CompoundTag(name); + return (CompoundTag*) get(name); + } + + ListTag* getList(const std::string& name) const { + if (!contains(name, TAG_List)) return new ListTag(name); + return (ListTag*) get(name); + } + + bool getBoolean(const std::string& string) const { + return getByte(string) != 0; + } + + std::string toString() const { + std::stringstream ss; + ss << tags.size() << " entries"; + return ss.str(); + } + + void print(const std::string& prefix_, PrintStream& out) const { + super::print(prefix_, out); + std::string prefix = prefix_; + out.print(prefix); out.println("{"); + prefix += " "; + for (TagMap::const_iterator it = tags.begin(); it != tags.end(); ++it) { + (it->second)->print(prefix, out); + } + out.print(prefix_); + out.println("}"); + } + + bool isEmpty() const { + return tags.empty(); + } + + Tag* copy() const { + CompoundTag* tag = new CompoundTag(getName()); + for (TagMap::const_iterator it = tags.begin(); it != tags.end(); ++it) { + //tag->put(it->first, get(it->first)->copy()); + tag->put(it->first, it->second->copy()); + } + return tag; + } + + //@Override + bool equals(const Tag& obj) const { + if (super::equals(obj)) { + CompoundTag& o = (CompoundTag&) obj; + + if (tags.size() != o.tags.size()) + return false; + + for (TagMap::const_iterator it = tags.begin(), jt = o.tags.begin(); it != tags.end(); ++it, ++jt) { + if (it->first != jt->first) return false; + if (!it->second->equals(*jt->second)) return false; + } + + return true; + } + return false; + } + + void deleteChildren() { + TagMap::iterator it = tags.begin(); + for (; it != tags.end(); ++it) { + if (!it->second) + continue; + it->second->deleteChildren(); + delete it->second; + } + } +private: + TagMap tags; +}; + +#endif /*COM_MOJANG_NBT__CompoundTag_H__*/ diff --git a/src/nbt/DoubleTag.h b/src/nbt/DoubleTag.h index 6d1a639..a3db624 100755 --- a/src/nbt/DoubleTag.h +++ b/src/nbt/DoubleTag.h @@ -1,57 +1,57 @@ -#ifndef COM_MOJANG_NBT__DoubleTag_H__ -#define COM_MOJANG_NBT__DoubleTag_H__ - -//package com.mojang.nbt; - -/* import java.io.* */ - -#include "Tag.h" -#include - -class DoubleTag: public Tag { - typedef Tag super; -public: - double data; - - DoubleTag(const std::string& name) - : super(name) - {} - - DoubleTag(const std::string& name, double data) - : super(name), - data(data) - {} - - void write(IDataOutput* dos) /*throws IOException*/ { - dos->writeDouble(data); - } - - void load(IDataInput* dis) /*throws IOException*/ { - data = dis->readDouble(); - } - - char getId() const { - return TAG_Double; - } - - std::string toString() const { - std::stringstream ss; - ss << data; - return ss.str(); - } - - //@Override - Tag* copy() const { - return new DoubleTag(getName(), data); - } - - //@Override - bool equals(const Tag& rhs) const { - if (super::equals(rhs)) { - return data == ((DoubleTag&)rhs).data; - } - return false; - } -}; - -#endif /*COM_MOJANG_NBT__DoubleTag_H__*/ +#ifndef COM_MOJANG_NBT__DoubleTag_H__ +#define COM_MOJANG_NBT__DoubleTag_H__ + +//package com.mojang.nbt; + +/* import java.io.* */ + +#include "Tag.h" +#include + +class DoubleTag: public Tag { + typedef Tag super; +public: + double data; + + DoubleTag(const std::string& name) + : super(name) + {} + + DoubleTag(const std::string& name, double data) + : super(name), + data(data) + {} + + void write(IDataOutput* dos) /*throws IOException*/ { + dos->writeDouble(data); + } + + void load(IDataInput* dis) /*throws IOException*/ { + data = dis->readDouble(); + } + + char getId() const { + return TAG_Double; + } + + std::string toString() const { + std::stringstream ss; + ss << data; + return ss.str(); + } + + //@Override + Tag* copy() const { + return new DoubleTag(getName(), data); + } + + //@Override + bool equals(const Tag& rhs) const { + if (super::equals(rhs)) { + return data == ((DoubleTag&)rhs).data; + } + return false; + } +}; + +#endif /*COM_MOJANG_NBT__DoubleTag_H__*/ diff --git a/src/nbt/EndTag.h b/src/nbt/EndTag.h index 6b85442..7ad25e1 100755 --- a/src/nbt/EndTag.h +++ b/src/nbt/EndTag.h @@ -1,41 +1,41 @@ -#ifndef COM_MOJANG_NBT__EndTag_H__ -#define COM_MOJANG_NBT__EndTag_H__ - -//package com.mojang.nbt; - -/* import java.io.* */ - -class EndTag: public Tag { - typedef Tag super; -public: - EndTag() - : super("") - { - } - - void load(IDataInput* dis) /*throws IOException*/ { - } - - void write(IDataOutput* dos) /*throws IOException*/ { - } - - char getId() const { - return TAG_End; - } - - std::string toString() const { - return "END"; - } - - //@Override - Tag* copy() const { - return new EndTag(); - } - - //@Override - bool equals(const Tag& rhs) const { - return super::equals(rhs); - } -}; - -#endif /*COM_MOJANG_NBT__EndTag_H__*/ +#ifndef COM_MOJANG_NBT__EndTag_H__ +#define COM_MOJANG_NBT__EndTag_H__ + +//package com.mojang.nbt; + +/* import java.io.* */ + +class EndTag: public Tag { + typedef Tag super; +public: + EndTag() + : super("") + { + } + + void load(IDataInput* dis) /*throws IOException*/ { + } + + void write(IDataOutput* dos) /*throws IOException*/ { + } + + char getId() const { + return TAG_End; + } + + std::string toString() const { + return "END"; + } + + //@Override + Tag* copy() const { + return new EndTag(); + } + + //@Override + bool equals(const Tag& rhs) const { + return super::equals(rhs); + } +}; + +#endif /*COM_MOJANG_NBT__EndTag_H__*/ diff --git a/src/nbt/FloatTag.h b/src/nbt/FloatTag.h index 884f65d..3aae664 100755 --- a/src/nbt/FloatTag.h +++ b/src/nbt/FloatTag.h @@ -1,59 +1,59 @@ -#ifndef COM_MOJANG_NBT__FloatTag_H__ -#define COM_MOJANG_NBT__FloatTag_H__ - -//package com.mojang.nbt; - -/* import java.io.* */ - -#include "Tag.h" -#include - -class FloatTag: public Tag { - typedef Tag super; -public: - float data; - - FloatTag(const std::string& name) - : super(name) - { - } - - FloatTag(const std::string& name, float data) - : super(name), - data(data) - { - } - - void write(IDataOutput* dos) /*throws IOException*/ { - dos->writeFloat(data); - } - - void load(IDataInput* dis) /*throws IOException*/ { - data = dis->readFloat(); - } - - char getId() const { - return TAG_Float; - } - - std::string toString() const { - std::stringstream ss; - ss << data; - return ss.str(); - } - - //@Override - Tag* copy() const { - return new FloatTag(getName(), data); - } - - //@Override - bool equals(const Tag& rhs) const { - if (super::equals(rhs)) { - return data == ((FloatTag&)rhs).data; - } - return false; - } -}; - -#endif /*COM_MOJANG_NBT__FloatTag_H__*/ +#ifndef COM_MOJANG_NBT__FloatTag_H__ +#define COM_MOJANG_NBT__FloatTag_H__ + +//package com.mojang.nbt; + +/* import java.io.* */ + +#include "Tag.h" +#include + +class FloatTag: public Tag { + typedef Tag super; +public: + float data; + + FloatTag(const std::string& name) + : super(name) + { + } + + FloatTag(const std::string& name, float data) + : super(name), + data(data) + { + } + + void write(IDataOutput* dos) /*throws IOException*/ { + dos->writeFloat(data); + } + + void load(IDataInput* dis) /*throws IOException*/ { + data = dis->readFloat(); + } + + char getId() const { + return TAG_Float; + } + + std::string toString() const { + std::stringstream ss; + ss << data; + return ss.str(); + } + + //@Override + Tag* copy() const { + return new FloatTag(getName(), data); + } + + //@Override + bool equals(const Tag& rhs) const { + if (super::equals(rhs)) { + return data == ((FloatTag&)rhs).data; + } + return false; + } +}; + +#endif /*COM_MOJANG_NBT__FloatTag_H__*/ diff --git a/src/nbt/IntTag.h b/src/nbt/IntTag.h index 5a803af..a76244f 100755 --- a/src/nbt/IntTag.h +++ b/src/nbt/IntTag.h @@ -1,58 +1,58 @@ -#ifndef COM_MOJANG_NBT__IntTag_H__ -#define COM_MOJANG_NBT__IntTag_H__ - -//package com.mojang.nbt; - -#include "Tag.h" -#include -/* import java.io.* */ - -class IntTag: public Tag { - typedef Tag super; -public: - int data; - - IntTag(const std::string& name) - : super(name) - { - } - - IntTag(const std::string& name, int data) - : super(name), - data(data) - { - } - - void write(IDataOutput* dos) /*throws IOException*/ { - dos->writeInt(data); - } - - void load(IDataInput* dis) /*throws IOException*/ { - data = dis->readInt(); - } - - char getId() const { - return TAG_Int; - } - - std::string toString() const { - std::stringstream ss; - ss << data; - return ss.str(); - } - - //@Override - Tag* copy() const { - return new IntTag(getName(), data); - } - - //@Override - bool equals(const Tag& rhs) const { - if (super::equals(rhs)) { - return data == ((IntTag&)rhs).data; - } - return false; - } -}; - -#endif /*COM_MOJANG_NBT__IntTag_H__*/ +#ifndef COM_MOJANG_NBT__IntTag_H__ +#define COM_MOJANG_NBT__IntTag_H__ + +//package com.mojang.nbt; + +#include "Tag.h" +#include +/* import java.io.* */ + +class IntTag: public Tag { + typedef Tag super; +public: + int data; + + IntTag(const std::string& name) + : super(name) + { + } + + IntTag(const std::string& name, int data) + : super(name), + data(data) + { + } + + void write(IDataOutput* dos) /*throws IOException*/ { + dos->writeInt(data); + } + + void load(IDataInput* dis) /*throws IOException*/ { + data = dis->readInt(); + } + + char getId() const { + return TAG_Int; + } + + std::string toString() const { + std::stringstream ss; + ss << data; + return ss.str(); + } + + //@Override + Tag* copy() const { + return new IntTag(getName(), data); + } + + //@Override + bool equals(const Tag& rhs) const { + if (super::equals(rhs)) { + return data == ((IntTag&)rhs).data; + } + return false; + } +}; + +#endif /*COM_MOJANG_NBT__IntTag_H__*/ diff --git a/src/nbt/ListTag.h b/src/nbt/ListTag.h index 5f1c913..87014be 100755 --- a/src/nbt/ListTag.h +++ b/src/nbt/ListTag.h @@ -1,171 +1,171 @@ -#ifndef COM_MOJANG_NBT__ListTag_H__ -#define COM_MOJANG_NBT__ListTag_H__ - -//package com.mojang.nbt; - -#include "Tag.h" -#include - -//template -class ListTag: public Tag -{ - typedef Tag super; - typedef Tag* T; - typedef std::vector List; - -public: - ListTag() - : super("") - { - } - - ListTag(const std::string& name) - : super(name) - { - } - - void write(IDataOutput* dos) /*throws IOException*/ { - if (list.size() > 0) type = list.front()->getId(); - else type = TAG_Byte; - - dos->writeByte(type); - dos->writeInt(list.size()); - - for (List::iterator it = list.begin(); it != list.end(); ++it) { - (*it)->write(dos); - } - } - - //@SuppressWarnings("unchecked") - void load(IDataInput* dis) /*throws IOException*/ { - type = dis->readByte(); - int size = dis->readInt(); - - list.clear();// = List(); - for (int i = 0; i < size; i++) { - Tag* tag = Tag::newTag(type, NullString); - tag->load(dis); - list.insert(list.end(), tag); - } - } - - char getId() const { - return TAG_List; - } - - std::string toString() const { - std::stringstream ss; - ss << list.size() << " entries of type " << Tag::getTagName(type); - return ss.str(); - } - - void print(const std::string& prefix_, PrintStream& out) const { - super::print(prefix_, out); - - std::string prefix = prefix_; - out.print(prefix); out.println("{"); - prefix += " "; - - for (List::const_iterator it = list.begin(); it != list.end(); ++it) { - (*it)->print(prefix, out); - } - out.print(prefix_); out.println("}"); - } - - void add(T tag) { - type = tag->getId(); - list.insert(list.end(), tag); - } - - T get(int index) const { - if (index >= size()) { - errorState |= TAGERR_OUT_OF_BOUNDS; - return NULL; - } - return *(list.begin() + index); - } - float getFloat(int index) { - T tag = get(index); - if (!tag) - return 0; - if (tag->getId() != TAG_Float) { - errorState |= TAGERR_BAD_TYPE; - return 0; - } - return ((FloatTag*)tag)->data; - } - - int size() const { - return list.size(); - } - - //@Override - Tag* copy() const { - ListTag* res = new ListTag(getName()); - res->type = type; - - for (List::const_iterator it = list.begin(); it != list.end(); ++it) { - T copy = (*it)->copy(); - res->list.insert(res->list.end(), copy); - } - return res; - } - - //@SuppressWarnings("rawtypes") - //@Override - bool equals(const Tag& rhs) const { - if (super::equals(rhs)) { - ListTag& o = (ListTag&) rhs; - if (type == o.type && list.size() == o.list.size()) { - for (List::const_iterator it = list.begin(), jt = o.list.begin(); it != list.end(); ++it, ++jt) { - if ((*it)->equals(**jt)) - return false; - } - return true; - } - } - return false; - } - - void deleteChildren() { - for (List::iterator it = list.begin(); it != list.end(); ++it) { - if (*it) { - (*it)->deleteChildren(); - delete (*it); - } - } - } - -private: - List list; - char type; -}; - - -class ListTagFloatAdder { -public: - ListTagFloatAdder() - : tag(NULL) - {} - ListTagFloatAdder(float f) - : tag(NULL) - { - operator() (f); - } - ListTagFloatAdder(ListTag* tag) - : tag(tag) - {} - - ListTagFloatAdder& operator() (const std::string& name, float f) { - if (!tag) tag = new ListTag(); - tag->add( new FloatTag(name, f) ); - return *this; - } - ListTagFloatAdder& operator() (float f) { - return operator() ("", f); - } - - ListTag* tag; -}; - -#endif /*COM_MOJANG_NBT__ListTag_H__*/ +#ifndef COM_MOJANG_NBT__ListTag_H__ +#define COM_MOJANG_NBT__ListTag_H__ + +//package com.mojang.nbt; + +#include "Tag.h" +#include + +//template +class ListTag: public Tag +{ + typedef Tag super; + typedef Tag* T; + typedef std::vector List; + +public: + ListTag() + : super("") + { + } + + ListTag(const std::string& name) + : super(name) + { + } + + void write(IDataOutput* dos) /*throws IOException*/ { + if (list.size() > 0) type = list.front()->getId(); + else type = TAG_Byte; + + dos->writeByte(type); + dos->writeInt(list.size()); + + for (List::iterator it = list.begin(); it != list.end(); ++it) { + (*it)->write(dos); + } + } + + //@SuppressWarnings("unchecked") + void load(IDataInput* dis) /*throws IOException*/ { + type = dis->readByte(); + int size = dis->readInt(); + + list.clear();// = List(); + for (int i = 0; i < size; i++) { + Tag* tag = Tag::newTag(type, NullString); + tag->load(dis); + list.insert(list.end(), tag); + } + } + + char getId() const { + return TAG_List; + } + + std::string toString() const { + std::stringstream ss; + ss << list.size() << " entries of type " << Tag::getTagName(type); + return ss.str(); + } + + void print(const std::string& prefix_, PrintStream& out) const { + super::print(prefix_, out); + + std::string prefix = prefix_; + out.print(prefix); out.println("{"); + prefix += " "; + + for (List::const_iterator it = list.begin(); it != list.end(); ++it) { + (*it)->print(prefix, out); + } + out.print(prefix_); out.println("}"); + } + + void add(T tag) { + type = tag->getId(); + list.insert(list.end(), tag); + } + + T get(int index) const { + if (index >= size()) { + errorState |= TAGERR_OUT_OF_BOUNDS; + return NULL; + } + return *(list.begin() + index); + } + float getFloat(int index) { + T tag = get(index); + if (!tag) + return 0; + if (tag->getId() != TAG_Float) { + errorState |= TAGERR_BAD_TYPE; + return 0; + } + return ((FloatTag*)tag)->data; + } + + int size() const { + return list.size(); + } + + //@Override + Tag* copy() const { + ListTag* res = new ListTag(getName()); + res->type = type; + + for (List::const_iterator it = list.begin(); it != list.end(); ++it) { + T copy = (*it)->copy(); + res->list.insert(res->list.end(), copy); + } + return res; + } + + //@SuppressWarnings("rawtypes") + //@Override + bool equals(const Tag& rhs) const { + if (super::equals(rhs)) { + ListTag& o = (ListTag&) rhs; + if (type == o.type && list.size() == o.list.size()) { + for (List::const_iterator it = list.begin(), jt = o.list.begin(); it != list.end(); ++it, ++jt) { + if ((*it)->equals(**jt)) + return false; + } + return true; + } + } + return false; + } + + void deleteChildren() { + for (List::iterator it = list.begin(); it != list.end(); ++it) { + if (*it) { + (*it)->deleteChildren(); + delete (*it); + } + } + } + +private: + List list; + char type; +}; + + +class ListTagFloatAdder { +public: + ListTagFloatAdder() + : tag(NULL) + {} + ListTagFloatAdder(float f) + : tag(NULL) + { + operator() (f); + } + ListTagFloatAdder(ListTag* tag) + : tag(tag) + {} + + ListTagFloatAdder& operator() (const std::string& name, float f) { + if (!tag) tag = new ListTag(); + tag->add( new FloatTag(name, f) ); + return *this; + } + ListTagFloatAdder& operator() (float f) { + return operator() ("", f); + } + + ListTag* tag; +}; + +#endif /*COM_MOJANG_NBT__ListTag_H__*/ diff --git a/src/nbt/LongTag.h b/src/nbt/LongTag.h index d620545..7b49638 100755 --- a/src/nbt/LongTag.h +++ b/src/nbt/LongTag.h @@ -1,56 +1,56 @@ -#ifndef COM_MOJANG_NBT__LongTag_H__ -#define COM_MOJANG_NBT__LongTag_H__ - -//package com.mojang.nbt; -#include "Tag.h" -#include - -class LongTag: public Tag { - typedef Tag super; -public: - long long data; - - LongTag(const std::string& name) - : super(name) - { - } - - LongTag(const std::string& name, long long data) - : super(name), - data(data) - { - } - - void write(IDataOutput* dos) /*throws IOException*/ { - dos->writeLongLong(data); - } - - void load(IDataInput* dis) /*throws IOException*/ { - data = dis->readLongLong(); - } - - char getId() const { - return TAG_Long; - } - - std::string toString() const { - std::stringstream ss; - ss << data; - return ss.str(); - } - - //@Override - Tag* copy() const { - return new LongTag(getName(), data); - } - - //@Override - bool equals(const Tag& rhs) const { - if (super::equals(rhs)) { - return data == ((LongTag&)rhs).data; - } - return false; - } -}; - -#endif /*COM_MOJANG_NBT__LongTag_H__*/ +#ifndef COM_MOJANG_NBT__LongTag_H__ +#define COM_MOJANG_NBT__LongTag_H__ + +//package com.mojang.nbt; +#include "Tag.h" +#include + +class LongTag: public Tag { + typedef Tag super; +public: + long long data; + + LongTag(const std::string& name) + : super(name) + { + } + + LongTag(const std::string& name, long long data) + : super(name), + data(data) + { + } + + void write(IDataOutput* dos) /*throws IOException*/ { + dos->writeLongLong(data); + } + + void load(IDataInput* dis) /*throws IOException*/ { + data = dis->readLongLong(); + } + + char getId() const { + return TAG_Long; + } + + std::string toString() const { + std::stringstream ss; + ss << data; + return ss.str(); + } + + //@Override + Tag* copy() const { + return new LongTag(getName(), data); + } + + //@Override + bool equals(const Tag& rhs) const { + if (super::equals(rhs)) { + return data == ((LongTag&)rhs).data; + } + return false; + } +}; + +#endif /*COM_MOJANG_NBT__LongTag_H__*/ diff --git a/src/nbt/NbtIo.h b/src/nbt/NbtIo.h index ee9bed3..6f68f81 100755 --- a/src/nbt/NbtIo.h +++ b/src/nbt/NbtIo.h @@ -1,26 +1,26 @@ -#ifndef COM_MOJANG_NBT__NbtIo_H__ -#define COM_MOJANG_NBT__NbtIo_H__ - -//package com.mojang.nbt; - -#include "CompoundTag.h" -#include "../util/DataIO.h" - -class NbtIo -{ -public: - static CompoundTag* read(IDataInput* dis) { - Tag *tag = Tag::readNamedTag(dis); - if (tag && tag->getId() == Tag::TAG_Compound) - return (CompoundTag*) tag; - return NULL; - } - - static bool write(CompoundTag* tag, IDataOutput* dos) { - if (!tag) return false; - Tag::writeNamedTag(tag, dos); - return false; - } -}; - -#endif /*COM_MOJANG_NBT__NbtIo_H__*/ +#ifndef COM_MOJANG_NBT__NbtIo_H__ +#define COM_MOJANG_NBT__NbtIo_H__ + +//package com.mojang.nbt; + +#include "CompoundTag.h" +#include "../util/DataIO.h" + +class NbtIo +{ +public: + static CompoundTag* read(IDataInput* dis) { + Tag *tag = Tag::readNamedTag(dis); + if (tag && tag->getId() == Tag::TAG_Compound) + return (CompoundTag*) tag; + return NULL; + } + + static bool write(CompoundTag* tag, IDataOutput* dos) { + if (!tag) return false; + Tag::writeNamedTag(tag, dos); + return false; + } +}; + +#endif /*COM_MOJANG_NBT__NbtIo_H__*/ diff --git a/src/nbt/ShortTag.h b/src/nbt/ShortTag.h index c2e0caf..a13a906 100755 --- a/src/nbt/ShortTag.h +++ b/src/nbt/ShortTag.h @@ -1,59 +1,59 @@ -#ifndef COM_MOJANG_NBT__ShortTag_H__ -#define COM_MOJANG_NBT__ShortTag_H__ - -//package com.mojang.nbt; - -/* import java.io.* */ - -#include "Tag.h" -#include - -class ShortTag: public Tag { - typedef Tag super; -public: - short data; - - ShortTag(const std::string& name) - : super(name) - { - } - - ShortTag(const std::string& name, short data) - : super(name), - data(data) - { - } - - void write(IDataOutput* dos) /*throws IOException*/ { - dos->writeShort(data); - } - - void load(IDataInput* dis) /*throws IOException*/ { - data = dis->readShort(); - } - - char getId() const { - return TAG_Short; - } - - std::string toString() const { - std::stringstream ss; - ss << data; - return ss.str(); - } - - //@Override - Tag* copy() const { - return new ShortTag(getName(), data); - } - - //@Override - bool equals(const Tag& rhs) const { - if (super::equals(rhs)) { - return data == ((ShortTag&)rhs).data; - } - return false; - } -}; - -#endif /*COM_MOJANG_NBT__ShortTag_H__*/ +#ifndef COM_MOJANG_NBT__ShortTag_H__ +#define COM_MOJANG_NBT__ShortTag_H__ + +//package com.mojang.nbt; + +/* import java.io.* */ + +#include "Tag.h" +#include + +class ShortTag: public Tag { + typedef Tag super; +public: + short data; + + ShortTag(const std::string& name) + : super(name) + { + } + + ShortTag(const std::string& name, short data) + : super(name), + data(data) + { + } + + void write(IDataOutput* dos) /*throws IOException*/ { + dos->writeShort(data); + } + + void load(IDataInput* dis) /*throws IOException*/ { + data = dis->readShort(); + } + + char getId() const { + return TAG_Short; + } + + std::string toString() const { + std::stringstream ss; + ss << data; + return ss.str(); + } + + //@Override + Tag* copy() const { + return new ShortTag(getName(), data); + } + + //@Override + bool equals(const Tag& rhs) const { + if (super::equals(rhs)) { + return data == ((ShortTag&)rhs).data; + } + return false; + } +}; + +#endif /*COM_MOJANG_NBT__ShortTag_H__*/ diff --git a/src/nbt/StringTag.h b/src/nbt/StringTag.h index 8967e58..16fd620 100755 --- a/src/nbt/StringTag.h +++ b/src/nbt/StringTag.h @@ -1,62 +1,62 @@ -#ifndef COM_MOJANG_NBT__StringTag_H__ -#define COM_MOJANG_NBT__StringTag_H__ - -//package com.mojang.nbt; - -#include "Tag.h" -#include - -class StringTag: public Tag -{ - typedef Tag super; -public: - std::string data; - int len; - - StringTag(const std::string& name) - : super(name), - len(name.length()) - { - } - - StringTag(const std::string& name, const std::string& data) - : super(name), - data(data), - len(name.length()) - { - } - - void write(IDataOutput* dos) /*throws IOException*/ { - dos->writeString(data); - } - - void load(IDataInput* dis) /*throws IOException*/ { - data = dis->readString(); // just a tiny bit slower, but safer - } - - char getId() const { - return TAG_String; - } - - std::string toString() const { - std::stringstream ss; - ss << data; - return ss.str(); - } - - //@Override - Tag* copy() const { - return new StringTag(getName(), data); - } - - //@Override - bool equals(const Tag& rhs) const { - if (super::equals(rhs)) { - StringTag& o = (StringTag&) rhs; - return data == o.data; - } - return false; - } -}; - -#endif /*COM_MOJANG_NBT__StringTag_H__*/ +#ifndef COM_MOJANG_NBT__StringTag_H__ +#define COM_MOJANG_NBT__StringTag_H__ + +//package com.mojang.nbt; + +#include "Tag.h" +#include + +class StringTag: public Tag +{ + typedef Tag super; +public: + std::string data; + int len; + + StringTag(const std::string& name) + : super(name), + len(name.length()) + { + } + + StringTag(const std::string& name, const std::string& data) + : super(name), + data(data), + len(name.length()) + { + } + + void write(IDataOutput* dos) /*throws IOException*/ { + dos->writeString(data); + } + + void load(IDataInput* dis) /*throws IOException*/ { + data = dis->readString(); // just a tiny bit slower, but safer + } + + char getId() const { + return TAG_String; + } + + std::string toString() const { + std::stringstream ss; + ss << data; + return ss.str(); + } + + //@Override + Tag* copy() const { + return new StringTag(getName(), data); + } + + //@Override + bool equals(const Tag& rhs) const { + if (super::equals(rhs)) { + StringTag& o = (StringTag&) rhs; + return data == o.data; + } + return false; + } +}; + +#endif /*COM_MOJANG_NBT__StringTag_H__*/ diff --git a/src/nbt/Tag.cpp b/src/nbt/Tag.cpp index b1ce3ae..231eee0 100755 --- a/src/nbt/Tag.cpp +++ b/src/nbt/Tag.cpp @@ -1,139 +1,139 @@ -#include "Tag.h" - -#include "EndTag.h" -#include "ByteTag.h" -#include "ShortTag.h" -#include "IntTag.h" -#include "LongTag.h" -#include "FloatTag.h" -#include "DoubleTag.h" -#include "ByteArrayTag.h" -#include "StringTag.h" -#include "ListTag.h" -#include "CompoundTag.h" - - -/*static*/ const std::string Tag::NullString = ""; - - -Tag::Tag(const std::string& name) -: name(name), - errorState(0) -{ -} - -/*virtual*/ -bool Tag::equals(const Tag& rhs) const { - return getId() == rhs.getId() - && name == rhs.name; -} - -/*virtual*/ -void Tag::print(PrintStream& out) const { - print("", out); -} - -/*virtual*/ -void Tag::print(const std::string& prefix, PrintStream& out) const { - std::string name = getName(); - - out.print(prefix); - out.print(getTagName(getId())); - if (name.length() > 0) { - out.print("(\"" + name + "\")"); - } - out.print(": "); - out.println(toString()); -} - -/*virtual*/ -Tag* Tag::setName(const std::string& name) { - this->name = name; - return this; -} - -/*virtual*/ -std::string Tag::getName() const { - return name; -} - -/*static*/ -Tag* Tag::readNamedTag(IDataInput* dis) /*throws IOException*/ { - char type = dis->readByte(); - if (type == Tag::TAG_End) return new EndTag(); - - Tag* tag = newTag(type, dis->readString()); - if (!tag) - return NULL; - - tag->load(dis); - return tag; -} - -/*static*/ -void Tag::writeNamedTag(Tag* tag, IDataOutput* dos) /*throws IOException*/ { - dos->writeByte(tag->getId()); - if (tag->getId() == Tag::TAG_End) return; - - dos->writeString(tag->getName()); - tag->write(dos); -} - -/*static*/ -Tag* Tag::newTag(char type, const std::string& name) -{ - switch (type) { - case TAG_End: - return new EndTag(); - case TAG_Byte: - return new ByteTag(name); - case TAG_Short: - return new ShortTag(name); - case TAG_Int: - return new IntTag(name); - case TAG_Long: - return new LongTag(name); - case TAG_Float: - return new FloatTag(name); - case TAG_Double: - return new DoubleTag(name); - case TAG_Byte_Array: - return new ByteArrayTag(name); - case TAG_String: - return new StringTag(name); - case TAG_List: - return new ListTag(name); - case TAG_Compound: - return new CompoundTag(name); - } - return NULL; -} - -/*static*/ -std::string Tag::getTagName(char type) { - switch (type) { - case TAG_End: - return "TAG_End"; - case TAG_Byte: - return "TAG_Byte"; - case TAG_Short: - return "TAG_Short"; - case TAG_Int: - return "TAG_Int"; - case TAG_Long: - return "TAG_Long"; - case TAG_Float: - return "TAG_Float"; - case TAG_Double: - return "TAG_Double"; - case TAG_Byte_Array: - return "TAG_Byte_Array"; - case TAG_String: - return "TAG_String"; - case TAG_List: - return "TAG_List"; - case TAG_Compound: - return "TAG_Compound"; - } - return "UNKNOWN"; -} +#include "Tag.h" + +#include "EndTag.h" +#include "ByteTag.h" +#include "ShortTag.h" +#include "IntTag.h" +#include "LongTag.h" +#include "FloatTag.h" +#include "DoubleTag.h" +#include "ByteArrayTag.h" +#include "StringTag.h" +#include "ListTag.h" +#include "CompoundTag.h" + + +/*static*/ const std::string Tag::NullString = ""; + + +Tag::Tag(const std::string& name) +: name(name), + errorState(0) +{ +} + +/*virtual*/ +bool Tag::equals(const Tag& rhs) const { + return getId() == rhs.getId() + && name == rhs.name; +} + +/*virtual*/ +void Tag::print(PrintStream& out) const { + print("", out); +} + +/*virtual*/ +void Tag::print(const std::string& prefix, PrintStream& out) const { + std::string name = getName(); + + out.print(prefix); + out.print(getTagName(getId())); + if (name.length() > 0) { + out.print("(\"" + name + "\")"); + } + out.print(": "); + out.println(toString()); +} + +/*virtual*/ +Tag* Tag::setName(const std::string& name) { + this->name = name; + return this; +} + +/*virtual*/ +std::string Tag::getName() const { + return name; +} + +/*static*/ +Tag* Tag::readNamedTag(IDataInput* dis) /*throws IOException*/ { + char type = dis->readByte(); + if (type == Tag::TAG_End) return new EndTag(); + + Tag* tag = newTag(type, dis->readString()); + if (!tag) + return NULL; + + tag->load(dis); + return tag; +} + +/*static*/ +void Tag::writeNamedTag(Tag* tag, IDataOutput* dos) /*throws IOException*/ { + dos->writeByte(tag->getId()); + if (tag->getId() == Tag::TAG_End) return; + + dos->writeString(tag->getName()); + tag->write(dos); +} + +/*static*/ +Tag* Tag::newTag(char type, const std::string& name) +{ + switch (type) { + case TAG_End: + return new EndTag(); + case TAG_Byte: + return new ByteTag(name); + case TAG_Short: + return new ShortTag(name); + case TAG_Int: + return new IntTag(name); + case TAG_Long: + return new LongTag(name); + case TAG_Float: + return new FloatTag(name); + case TAG_Double: + return new DoubleTag(name); + case TAG_Byte_Array: + return new ByteArrayTag(name); + case TAG_String: + return new StringTag(name); + case TAG_List: + return new ListTag(name); + case TAG_Compound: + return new CompoundTag(name); + } + return NULL; +} + +/*static*/ +std::string Tag::getTagName(char type) { + switch (type) { + case TAG_End: + return "TAG_End"; + case TAG_Byte: + return "TAG_Byte"; + case TAG_Short: + return "TAG_Short"; + case TAG_Int: + return "TAG_Int"; + case TAG_Long: + return "TAG_Long"; + case TAG_Float: + return "TAG_Float"; + case TAG_Double: + return "TAG_Double"; + case TAG_Byte_Array: + return "TAG_Byte_Array"; + case TAG_String: + return "TAG_String"; + case TAG_List: + return "TAG_List"; + case TAG_Compound: + return "TAG_Compound"; + } + return "UNKNOWN"; +} diff --git a/src/nbt/Tag.h b/src/nbt/Tag.h index 5cfc3ff..8449165 100755 --- a/src/nbt/Tag.h +++ b/src/nbt/Tag.h @@ -1,65 +1,65 @@ -#ifndef COM_MOJANG_NBT__Tag_H__ -#define COM_MOJANG_NBT__Tag_H__ - -//package com.mojang.nbt; - -/* import java.io.* */ - -#include "../util/DataIO.h" - -#include - - -class Tag -{ -public: - virtual ~Tag() {} - virtual void deleteChildren() {} - - static const char TAG_End = 0; - static const char TAG_Byte = 1; - static const char TAG_Short = 2; - static const char TAG_Int = 3; - static const char TAG_Long = 4; - static const char TAG_Float = 5; - static const char TAG_Double = 6; - static const char TAG_Byte_Array= 7; - static const char TAG_String = 8; - static const char TAG_List = 9; - static const char TAG_Compound = 10; - - static const std::string NullString; - - virtual void write(IDataOutput* dos) = 0; ///*throws IOException*/; - virtual void load(IDataInput* dis) = 0; ///*throws IOException*/; - - virtual std::string toString() const = 0; - virtual char getId() const = 0; - - virtual bool equals(const Tag& rhs) const; - - virtual void print(PrintStream& out) const; - virtual void print(const std::string& prefix, PrintStream& out) const; - - virtual Tag* setName(const std::string& name); - virtual std::string getName() const; - - static Tag* readNamedTag(IDataInput* dis); - static void writeNamedTag(Tag* tag, IDataOutput* dos); - - static Tag* newTag(char type, const std::string& name); - - static std::string getTagName(char type); - - virtual Tag* copy() const = 0; - - mutable int errorState; - static const int TAGERR_OUT_OF_BOUNDS = 1; - static const int TAGERR_BAD_TYPE = 2; -protected: - Tag(const std::string& name); -private: - std::string name; -}; - -#endif /*COM_MOJANG_NBT__Tag_H__*/ +#ifndef COM_MOJANG_NBT__Tag_H__ +#define COM_MOJANG_NBT__Tag_H__ + +//package com.mojang.nbt; + +/* import java.io.* */ + +#include "../util/DataIO.h" + +#include + + +class Tag +{ +public: + virtual ~Tag() {} + virtual void deleteChildren() {} + + static const char TAG_End = 0; + static const char TAG_Byte = 1; + static const char TAG_Short = 2; + static const char TAG_Int = 3; + static const char TAG_Long = 4; + static const char TAG_Float = 5; + static const char TAG_Double = 6; + static const char TAG_Byte_Array= 7; + static const char TAG_String = 8; + static const char TAG_List = 9; + static const char TAG_Compound = 10; + + static const std::string NullString; + + virtual void write(IDataOutput* dos) = 0; ///*throws IOException*/; + virtual void load(IDataInput* dis) = 0; ///*throws IOException*/; + + virtual std::string toString() const = 0; + virtual char getId() const = 0; + + virtual bool equals(const Tag& rhs) const; + + virtual void print(PrintStream& out) const; + virtual void print(const std::string& prefix, PrintStream& out) const; + + virtual Tag* setName(const std::string& name); + virtual std::string getName() const; + + static Tag* readNamedTag(IDataInput* dis); + static void writeNamedTag(Tag* tag, IDataOutput* dos); + + static Tag* newTag(char type, const std::string& name); + + static std::string getTagName(char type); + + virtual Tag* copy() const = 0; + + mutable int errorState; + static const int TAGERR_OUT_OF_BOUNDS = 1; + static const int TAGERR_BAD_TYPE = 2; +protected: + Tag(const std::string& name); +private: + std::string name; +}; + +#endif /*COM_MOJANG_NBT__Tag_H__*/ diff --git a/src/network/ClientSideNetworkHandler.cpp b/src/network/ClientSideNetworkHandler.cpp index d754b47..ca3f155 100755 --- a/src/network/ClientSideNetworkHandler.cpp +++ b/src/network/ClientSideNetworkHandler.cpp @@ -1,926 +1,926 @@ - -#include "ClientSideNetworkHandler.h" -#include "client/Options.h" -#include "packet/PacketInclude.h" -#include "RakNetInstance.h" -#include "../world/level/chunk/ChunkSource.h" -#include "../world/level/Level.h" -#include "../world/level/storage/LevelStorageSource.h" -#include "../world/entity/player/Player.h" -#include "../world/entity/player/Inventory.h" -#include "../client/Minecraft.h" -#include "../client/gamemode/GameMode.h" -#ifndef STANDALONE_SERVER -#include "../client/gui/screens/DisconnectionScreen.h" -#endif -#include "../client/player/LocalPlayer.h" -#include "../client/multiplayer/MultiPlayerLevel.h" -#include "../client/player/input/KeyboardInput.h" -#include "../client/sound/SoundEngine.h" -#include "../world/entity/MobFactory.h" -#include "../raknet/RakPeerInterface.h" -#include "../world/level/Explosion.h" -#include "../world/level/tile/entity/FurnaceTileEntity.h" -#include "../world/inventory/BaseContainerMenu.h" -#ifndef STANDALONE_SERVER -#include "../client/particle/TakeAnimationParticle.h" -#endif -#include "../world/entity/EntityFactory.h" -#include "../world/entity/item/PrimedTnt.h" -#include "../world/entity/projectile/Arrow.h" -#include "../world/level/tile/entity/ChestTileEntity.h" -#include "../client/player/RemotePlayer.h" -#include "../world/level/tile/LevelEvent.h" -#include "../world/entity/item/FallingTile.h" - -static MultiPlayerLevel* mpcast(Level* l) { return (MultiPlayerLevel*) l; } - -ClientSideNetworkHandler::ClientSideNetworkHandler(Minecraft* minecraft, IRakNetInstance* raknetInstance) -: minecraft(minecraft), - raknetInstance(raknetInstance), - level(NULL), - requestNextChunkPosition(0), - requestNextChunkIndex(0) -{ - rakPeer = raknetInstance->getPeer(); -} - -ClientSideNetworkHandler::~ClientSideNetworkHandler() -{ -} - -void ClientSideNetworkHandler::requestNextChunk() -{ - if (requestNextChunkIndex < NumRequestChunks) - { - IntPair& chunk = requestNextChunkIndexList[requestNextChunkIndex]; - RequestChunkPacket packet(chunk.x, chunk.y); - raknetInstance->send(packet); - - //LOGI("requesting chunks @ (%d, %d)\n", chunk.x, chunk.y); - - //raknetInstance->send(new RequestChunkPacket(requestNextChunkPosition % CHUNK_CACHE_WIDTH, requestNextChunkPosition / CHUNK_CACHE_WIDTH)); - requestNextChunkIndex++; - requestNextChunkPosition++; - } -} - -bool ClientSideNetworkHandler::areAllChunksLoaded() -{ - return (requestNextChunkPosition >= (CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH)); -} - -bool ClientSideNetworkHandler::isChunkLoaded(int x, int z) -{ - if (x < 0 || x >= CHUNK_CACHE_WIDTH || z < 0 || z >= CHUNK_CACHE_WIDTH) { - LOGE("Error: Tried to request chunk (%d, %d)\n", x, z); - return true; - } - return chunksLoaded[x * CHUNK_CACHE_WIDTH + z]; - //return areAllChunksLoaded(); -} - -void ClientSideNetworkHandler::onConnect(const RakNet::RakNetGUID& hostGuid) -{ - LOGI("onConnect, server guid: %s, local guid: %s\n", hostGuid.ToString(), rakPeer->GetMyGUID().ToString()); - serverGuid = hostGuid; - - clearChunksLoaded(); - LoginPacket packet(minecraft->options.getStringValue(OPTIONS_USERNAME).c_str(), SharedConstants::NetworkProtocolVersion); - raknetInstance->send(packet); -} - -void ClientSideNetworkHandler::onUnableToConnect() -{ - LOGI("onUnableToConnect\n"); -} - -void ClientSideNetworkHandler::onDisconnect(const RakNet::RakNetGUID& guid) -{ - LOGI("onDisconnect\n"); - if (level) - { - level->isClientSide = false; - for (int i = (int)level->players.size()-1; i >= 0; --i ) { - Player* p = level->players[i]; - if (p != minecraft->player) { - p->reallyRemoveIfPlayer = true; - level->removeEntity(p); - } - } - } -#ifndef STANDALONE_SERVER - minecraft->gui.addMessage("Disconnected from server"); -#endif -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, LoginStatusPacket* packet) { - if (packet->status == LoginStatus::Success) { - raknetInstance->setIsLoggedIn(true); - return; - } - - if (packet->status == LoginStatus::Failed_ClientOld) { - LOGI("Disconnect! Client is outdated!\n"); -#ifndef STANDALONE_SERVER - minecraft->setScreen(new DisconnectionScreen("Could not connect: Outdated client!")); -#endif - } - if (packet->status == LoginStatus::Failed_ServerOld) { - LOGI("Disconnect! Server is outdated!\n"); -#ifndef STANDALONE_SERVER - minecraft->setScreen(new DisconnectionScreen("Could not connect: Outdated server!")); -#endif - } -} - - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, StartGamePacket* packet) -{ - LOGI("StartGamePacket\n"); - -#ifdef RPI - if (packet->gameType != GameType::Creative) { - minecraft->setScreen(new DisconnectionScreen("Could not connect: Incompatible server!")); - return; - } -#endif - - const std::string& levelId = LevelStorageSource::TempLevelId; - LevelStorageSource* storageSource = minecraft->getLevelSource(); - storageSource->deleteLevel(levelId); - //level = new Level(storageSource->selectLevel(levelId, true), "temp", packet->levelSeed, SharedConstants::StorageVersion); - MultiPlayerLevel* level = new MultiPlayerLevel( - storageSource->selectLevel(levelId, true), - "temp", - LevelSettings(packet->levelSeed, LevelSettings::validateGameType(packet->gameType)), - SharedConstants::StorageVersion); - level->isClientSide = true; - - bool isCreative = (packet->gameType == GameType::Creative); - LocalPlayer* player = new LocalPlayer(minecraft, level, minecraft->options.getStringValue(OPTIONS_USERNAME), level->dimension->id, isCreative); - player->owner = rakPeer->GetMyGUID(); - player->entityId = packet->entityId; - player->moveTo(packet->x, packet->y, packet->z, player->yRot, player->xRot); - - LOGI("new pos: %f, %f [%f - %f]\n", player->x, player->z, player->bb.y0, player->bb.y1); - - minecraft->setLevel(level, "ClientSideNetworkHandler -> setLevel", player); - minecraft->setIsCreativeMode(isCreative); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MessagePacket* packet) -{ - LOGI("MessagePacket\n"); -#ifndef STANDALONE_SERVER - minecraft->gui.addMessage(packet->message.C_String()); -#endif -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetTimePacket* packet) -{ - if (!level) - return; - - LOGI("SetTimePacket\n"); - level->setTime(packet->time); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddEntityPacket* packet) -{ - if (!level) - return; - - Entity* e = EntityFactory::CreateEntity(packet->type, level); - if (!e) - return; - - e->entityId = packet->entityId; - e->setPos(packet->x, packet->y, packet->z); - - // Entity Specific stuff here - switch (packet->type) - { - case EntityTypes::IdFallingTile: { - int data = -packet->data(); - FallingTile* ft = (FallingTile*) e; - ft->tile = data & 0xff; - ft->data = data >> 16; - } - case EntityTypes::IdArrow: { - Entity* owner = level->getEntity(packet->data()); - if (owner && owner->isMob()) - ((Arrow*)e)->ownerId = owner->entityId; - break; - } - default: - break; - } - - if (packet->hasMovementData()) { - /* - e->xd = packet->xd; - e->yd = packet->yd; - e->zd = packet->zd; - */ - e->lerpMotion(packet->xd, packet->yd, packet->zd); - //LOGI("Client: reading entity with %f, %f, %f\n", e->xd, e->yd, e->zd); - } - - mpcast(level)->putEntity(packet->entityId, e); -} -void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, AddPaintingPacket* packet ) { - if (!level) - return; - - Painting* painting = new Painting(level, packet->xTile, packet->yTile, packet->zTile, packet->dir, packet->motive); - mpcast(level)->putEntity(packet->entityId, painting); -} -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddMobPacket* packet) -{ - LOGI("AddMobPacket (%p)\n", level); - - if (!level) { - LOGW("Trying to add a mob with no level!\n"); - //we skip this since we will get this player anyway when we request level - return; - } - if (!packet->type) { - LOGE("Trying to add a mob without a type id\n"); - return; - } - - Mob* mob = MobFactory::CreateMob(packet->type, level); - if (!mob) { - LOGE("Server tried to add an unknown mob type! :%d\n", packet->type); - return; - } - - mob->entityId = packet->entityId; - mob->moveTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot); - mob->getEntityData()->assignValues(&packet->unpack); - level->addEntity(mob); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddPlayerPacket* packet) -{ - if (!level) { - //we skip this since we will get this player anyway when we request level - return; - } - LOGI("AddPlayerPacket\n"); - - Player* player = new RemotePlayer(level, minecraft->isCreativeMode()); - minecraft->gameMode->initAbilities(player->abilities); - player->entityId = packet->entityId; - level->addEntity(player); - - player->moveTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot); - player->name = packet->name.C_String(); - player->owner = packet->owner; - player->getEntityData()->assignValues(&packet->unpack); - int slot = Inventory::MAX_SELECTION_SIZE; - if (packet->carriedItemId == 0) { - player->inventory->clearSlot(slot); - } else { - ItemInstance newItem(packet->carriedItemId, 1, packet->carriedItemAuxValue); - player->inventory->replaceSlot(slot, &newItem); - } - player->inventory->moveToSelectedSlot(slot, true); - //player->resetPos(); - - std::string message = packet->name.C_String(); - message += " joined the game"; -#ifndef STANDALONE_SERVER - minecraft->gui.addMessage(message); -#endif -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RemovePlayerPacket* packet) { - if (!level || source == minecraft->player->owner) return; - - if (Player* player = findPlayer(level, packet->entityId, &packet->owner)) { - player->reallyRemoveIfPlayer = true; - level->removeEntity(player); - } - - //for (int i = (int)level->players.size()-1; i >= 0; --i) { - // if (level->players[i]->owner == source) { - // level->players[i]->reallyRemoveIfPlayer = true; - // level->players.erase(level->players.begin() + i); - // break; - // } - //} - - //if (!player) return; - - //std::string message = packet->name.C_String(); - //message += " joined the game"; - //minecraft->gui.addMessage(message); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RemoveEntityPacket* packet) -{ - if (!level) return; - - Entity* entity = level->getEntity(packet->entityId); - LOGI("RemoveEntityPacket %p %p, %d\n", entity, minecraft->player, entity?(int)(entity->isPlayer()): -1); - if (!entity) return; - - level->removeEntity(entity); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddItemEntityPacket* packet) -{ - if (!level) return; - - ItemEntity* entity = new ItemEntity(level, packet->x, packet->y, packet->z, ItemInstance(packet->itemId, packet->itemCount, packet->auxValue)); - entity->xd = packet->xa(); - entity->yd = packet->ya(); - entity->zd = packet->za(); - //LOGI("item-entity @ client: %f, %f, %f\n", entity->xd, entity->yd, entity->zd); - - mpcast(level)->putEntity(packet->id, entity); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, TakeItemEntityPacket* packet) { - if (!level) return; - - Entity* e = level->getEntity(packet->itemId); - if (!e) - return; - - ItemInstance item; - if (e->isItemEntity()) { - item = ((ItemEntity*) e)->item; -#ifndef STANDALONE_SERVER - if (Entity* to = level->getEntity(packet->playerId)) - minecraft->particleEngine->add(new TakeAnimationParticle(level, (ItemEntity*)e, to, -0.5f)); -#endif - } - else if (e->getEntityTypeId() == EntityTypes::IdArrow) - item = ItemInstance(Item::arrow); - - if (item.isNull()) - return; - - // try take it and if we don't have space; re-throw it - if (minecraft->player->entityId == packet->playerId - && !minecraft->player->inventory->add(&item)) { - DropItemPacket dropPacket(packet->playerId, item); - minecraft->raknetInstance->send(dropPacket); - } - level->playSound(e, "random.pop", 0.2f, 1.0f * 2.f); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet) -{ - if (!level) - return; - - //printf("MovePlayerPacket\n"); - Entity* entity = level->getEntity(packet->entityId); - if (entity) - { - entity->lerpTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot, 3); - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MoveEntityPacket* packet) -{ - if (!level) - return; - - //printf("MovePlayerPacket\n"); - Entity* entity = level->getEntity(packet->entityId); - if (entity) - { - float xRot = packet->hasRot? packet->xRot : entity->xRot; - float yRot = packet->hasRot? packet->yRot : entity->yRot; - entity->lerpTo(packet->x, packet->y, packet->z, yRot, xRot, 3); - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, UpdateBlockPacket* packet) -{ - if (!level) return; - - //LOGI("UpdateBlockPacket @ %d, %d, %d\n", packet->x, packet->y, packet->z); - - int x = packet->x, z = packet->z; - - if (isChunkLoaded(x >> 4, z >> 4)) - { - //LOGI("chunk is loaded - UPDATE @ %d, %d, %d -- %d, %d\n", x, packet->y, z, packet->blockId, packet->blockData); - - int y = packet->y; - int tileId = Tile::transformToValidBlockId(packet->blockId, x, y, z); - level->setTileAndData(x, y, z, tileId, packet->blockData); - } else { - SBufferedBlockUpdate update; - update.blockId = packet->blockId; - update.blockData = packet->blockData; - update.setData = true; - update.x = packet->x; - update.y = packet->y; - update.z = packet->z; - bufferedBlockUpdates.push_back(update); - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ExplodePacket* packet) { - if (!level) return; - Explosion explosion(level, NULL, packet->x, packet->y, packet->z, packet->r); - explosion.toBlow.insert(packet->toBlow.begin(), packet->toBlow.end()); - explosion.finalizeExplosion(); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, LevelEventPacket* packet) { - if (!level) return; - if(packet->eventId == LevelEvent::ALL_PLAYERS_SLEEPING) { - minecraft->player->setAllPlayersSleeping(); - } - else { - minecraft->level->levelEvent(NULL, packet->eventId, packet->x, packet->y, packet->z, packet->data); - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, TileEventPacket* packet) { - if (!level) return; - minecraft->level->tileEvent(packet->x, packet->y, packet->z, packet->b0, packet->b1); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet) { - if (!level) return; - - Entity* e = level->getEntity(packet->entityId); - if (e) e->handleEntityEvent(packet->eventId); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ChunkDataPacket* packet) -{ - if (!level) { - LOGI("level @ handle ChunkDataPacket is 0\n"); - return; - } - //LOGI("ChunkDataPacket\n"); - - LevelChunk* chunk = level->getChunkSource()->create(packet->x, packet->z); - if (!chunk || chunk->isEmpty()) - { - LOGI("Failed to find write-able chunk\n"); - return; - } - - //unsigned char* blockIds = chunk->getBlockData(); - DataLayer& blockData = chunk->data; - - const int setSize = LEVEL_HEIGHT / 8; - const int setShift = 4; // power of LEVEL_HEIGHT / 8 - - bool recalcHeight = false; - - int x0 = 16, x1 = 0, z0 = 16, z1 = 0, y0 = LEVEL_HEIGHT, y1 = 0; - int rx = packet->x << 4; - int rz = packet->z << 4; - - unsigned char readBlockBuffer[setSize]; - unsigned char readDataBuffer[setSize / 2]; - - for (int i = 0; i < CHUNK_COLUMNS; i++) - { - unsigned char updateBits = 0; - packet->chunkData.Read(updateBits); - - if (updateBits > 0) - { - recalcHeight = true; - - int colX = (i % CHUNK_WIDTH); - int colZ = (i / CHUNK_WIDTH); - int colDataPosition = colX << 11 | colZ << 7; - - for (int set = 0; set < 8; set++) - { - if ((updateBits & (1 << set)) != 0) - { - packet->chunkData.Read((char*)readBlockBuffer, setSize); - packet->chunkData.Read((char*)readDataBuffer, setSize / 2); - - for (int part = 0; part < setSize; part++) - { - //if (readBlockBuffer[part] == ((Tile*)Tile::grass)->id) - // readBlockBuffer[part] = 255; - int x = rx + colX; - int y = (set << setShift) + part; - int z = rz + colZ; - - int tileId = Tile::transformToValidBlockId(readBlockBuffer[part], x, y, z); - level->setTileNoUpdate(x, y, z, tileId); - } - // ((part & 1) == 0) ? readDataBuffer[part >> 1] & 0xf : (readDataBuffer[part >> 1] & 0xf0) >> 4 - - //packet->chunkData.Read((char*)(&blockIds[colDataPosition + (set << setShift)]), setSize); - // block data is only 4 bits per block - //packet->chunkData.Read((char*)(&blockData.data[(colDataPosition + (set << setShift)) >> 1]), setSize >> 1); - - memcpy(&blockData.data[(colDataPosition + (set << setShift)) >> 1], readDataBuffer, setSize >> 1); - } - - if (((1 << set) << setShift) < y0) - { - y0 = ((1 << set) << setShift); - } - if (((1 << set) << setShift) + (LEVEL_HEIGHT / 8) - 1 > y1) - { - y1 = ((1 << set) << setShift) + (LEVEL_HEIGHT / 8) - 1; - } - } - if ((i % CHUNK_WIDTH) < x0) - { - x0 = (i % CHUNK_WIDTH); - } - if ((i % CHUNK_WIDTH) > x1) - { - x1 = (i % CHUNK_WIDTH); - } - if ((i / CHUNK_WIDTH) < z0) - { - z0 = (i / CHUNK_WIDTH); - } - if ((i / CHUNK_WIDTH) > z1) - { - z1 = (i / CHUNK_WIDTH); - } - } - } - - if (recalcHeight) - { -// chunk->recalcHeightmap(); -// //chunk->recalcBlockLights(); - level->setTilesDirty((packet->x << 4) + x0, y0, (packet->z << 4) + z0, (packet->x << 4) + x1, y1, (packet->z << 4) + z1); -// int rx = packet->x << 4; -// int rz = packet->z << 4; -// level->updateLight(LightLayer::Block, x0 + rx - 1, y0, z0 + rz - 1, x1 + rx + 1, y1, z1 + rz + 1); -// //for (int cx = x0; cx < x1; cx++) -// //{ -// // for (int cz = z0; cz < z1; cz++) -// // { -// // for (int cy = y0; cy < y1; cy++) -// // { -// //level->updateLight(LightLayer::Sky, cx + rx, cy, cz + rz, cx + rx, cy, cz + rz); -// // level->updateLight(LightLayer::Block, cx + rx - 1, cy, cz + rz - 1, cx + rx + 1, cy, cz + rz + 1); -// // } -// // } -// //} -// - } - //chunk->terrainPopulated = true; - chunk->unsaved = false; - - chunksLoaded[packet->x * CHUNK_CACHE_WIDTH + packet->z] = true; - - if (areAllChunksLoaded()) - { - ReadyPacket packet(ReadyPacket::READY_REQUESTEDCHUNKS); - raknetInstance->send(packet); - - for (unsigned int i = 0; i < bufferedBlockUpdates.size(); i++) - { - const SBufferedBlockUpdate& update = bufferedBlockUpdates[i]; - int tileId = Tile::transformToValidBlockId( update.blockId, update.x, update.y, update.z ); - if (update.setData) - level->setTileAndData(update.x, update.y, update.z, tileId, update.blockData); - else - level->setTile(update.x, update.y, update.z, tileId); - } - bufferedBlockUpdates.clear(); - } - else - { - requestNextChunk(); - } -} - -class _ChunkSorter -{ -public: - _ChunkSorter(int x, int z) - : x(x), - y(z) - { - } - - bool operator() (const IntPair& a, const IntPair& b) { - const int ax = a.x - x, ay = a.y - y; - const int bx = b.x - x, by = b.y - y; - return (ax*ax + ay*ay) < (bx*bx + by*by); - } -private: - int x; - int y; -}; - - -void ClientSideNetworkHandler::arrangeRequestChunkOrder() { - clearChunksLoaded(); - - // Default sort is around center of the world - int cx = CHUNK_CACHE_WIDTH / 2; - int cz = CHUNK_CACHE_WIDTH / 2; - - // If player exists, let's sort around him - Player* p = minecraft? minecraft->player : NULL; - if (p) { - cx = Mth::floor(p->x / (float)CHUNK_WIDTH); - cz = Mth::floor(p->z / (float)CHUNK_DEPTH); - } - - _ChunkSorter sorter(cx, cz); - std::sort(requestNextChunkIndexList, requestNextChunkIndexList + NumRequestChunks, sorter); -} - -void ClientSideNetworkHandler::levelGenerated(Level* level) -{ - this->level = level; - ReadyPacket packet(ReadyPacket::READY_CLIENTGENERATION); - raknetInstance->send(packet); - - arrangeRequestChunkOrder(); - requestNextChunk(); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet) -{ - if (!level) - return; - - Entity* entity = level->getEntity(packet->entityId); - if (!entity || !entity->isPlayer()) - return; - - Player* player = (Player*)entity; - // make sure it's not our local player - if (player->owner == rakPeer->GetMyGUID()) - { - printf("Attempted to modify local player's inventory\n"); - return; - } - // override the player's inventory - //int slot = player->inventory->getSlot(packet->itemId, packet->itemAuxValue); - //if (slot >= 0) { - // player->inventory->moveToSelectedSlot(slot, true); - //item->id = packet->itemId; - //item->setAuxValue(packet->itemAuxValue); - //item->count = 63; - int slot = Inventory::MAX_SELECTION_SIZE; - if (slot >= 0) { - if (packet->itemId == 0) { - player->inventory->clearSlot(slot); - } else { - ItemInstance newItem(packet->itemId, 63, packet->itemAuxValue); - player->inventory->replaceSlot(slot, &newItem); - } - player->inventory->moveToSelectedSlot(slot, true); - } else { - LOGW("Warning: Remote player doesn't have his thing, Odd!\n"); - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet) { - if (!level) - return; - - Entity* entity = level->getEntity(packet->entityId); - if (!entity || !entity->isPlayer()) - return; - - Player* player = (Player*)entity; - // make sure it's not our local player, since that should be UpdateArmorPacket - if (player->owner == rakPeer->GetMyGUID()) { - printf("Attempted to modify local player's armor visually\n"); - return; - } - - packet->fillIn(player); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, InteractPacket* packet) -{ - if (!level) - return; - - Entity* src = level->getEntity(packet->sourceId); - Entity* entity = level->getEntity(packet->targetId); - if (src && entity && src->isPlayer()) - { - Player* player = (Player*) src; - if (InteractPacket::Attack == packet->action) - minecraft->gameMode->attack(player, entity); - if (InteractPacket::Interact == packet->action) - minecraft->gameMode->interact(player, entity); - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetEntityDataPacket* packet) -{ - if (!level) - return; - - LOGI("SetEntityDataPacket\n"); - - Entity* e = level->getEntity(packet->id); - if (e) { - SynchedEntityData* data = e->getEntityData(); - if (data) - data->assignValues(&packet->getUnpackedData()); - } -} - -void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, SetEntityMotionPacket* packet ) -{ - if (!level) - return; - - if (Entity* e = level->getEntity(packet->id)) { - /* - e->xd = packet->xd; - e->yd = packet->yd; - e->zd = packet->zd; - */ - e->lerpMotion(packet->xd, packet->yd, packet->zd); - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AnimatePacket* packet) -{ - if (!level) - return; - - // Own player - Then don't play... : - if (minecraft->player->entityId == packet->entityId) { - if (packet->action == AnimatePacket::Swing) return; - } - - Entity* entity = level->getEntity(packet->entityId); - if (!entity || !entity->isPlayer()) - return; - - Player* player = (Player*) entity; - - switch (packet->action) { - case AnimatePacket::Swing: - player->swing(); - break; - case AnimatePacket::WAKE_UP: - player->stopSleepInBed(false, false, false); - break; - default: - LOGW("Unknown Animate action: %d\n", packet->action); - break; - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, UseItemPacket* packet) -{ -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetHealthPacket* packet) -{ - if (!level || !minecraft->player) - return; - - minecraft->player->hurtTo(packet->health); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetSpawnPositionPacket* packet) { - if (!level || !minecraft || !minecraft->player) return; - if (!level->inRange(packet->x, packet->y, packet->z)) return; - - minecraft->player->setRespawnPosition(Pos(packet->x, packet->y, packet->z)); - level->getLevelData()->setSpawn(packet->x, packet->y, packet->z); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, HurtArmorPacket* packet) { - if (!level || !minecraft->player) { - return; - } - - minecraft->player->hurtArmor(packet->dmg); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RespawnPacket* packet) -{ - if (level) { - //LOGI("RespawnPacket! %d\n", findPlayer(level, packet->entityId, NULL)); - NetEventCallback::handle(level, source, packet ); - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerOpenPacket* packet) -{ - if (!level || !minecraft || !minecraft->player) - return; - - if (packet->type == ContainerType::FURNACE) { - FurnaceTileEntity* te = new FurnaceTileEntity(); - te->clientSideOnly = true; - minecraft->player->openFurnace(te); - if (minecraft->player->containerMenu) - minecraft->player->containerMenu->containerId = packet->containerId; - } - if (packet->type == ContainerType::CONTAINER) { - ChestTileEntity* te = new ChestTileEntity(); - te->clientSideOnly = true; - minecraft->player->openContainer(te); - if (minecraft->player->containerMenu) - minecraft->player->containerMenu->containerId = packet->containerId; - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet) -{ - if (minecraft && minecraft->player && minecraft->player->containerMenu) - minecraft->player->closeContainer(); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetContentPacket* packet) -{ - if (!minecraft || !minecraft->player) - return; - - if (packet->containerId == 0) { - for (unsigned int i = 0; i < packet->items.size(); ++i) { - minecraft->player->inventory->setItem(Inventory::MAX_SELECTION_SIZE + i, &packet->items[i]); - } - } else if (minecraft->player->containerMenu && minecraft->player->containerMenu->containerId == packet->containerId) { - for (unsigned int i = 0; i < packet->items.size(); ++i) { - minecraft->player->containerMenu->setSlot(i, &packet->items[i]); - } - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet) -{ - //LOGI("ContainerSetSlot\n"); - - if (!minecraft->player - || !minecraft->player->containerMenu - || minecraft->player->containerMenu->containerId != packet->containerId) - return; - - //minecraft->player->containerMenu->setSlot(packet->slot, packet->item.isNull()? NULL : &packet->item); - minecraft->player->containerMenu->setSlot(packet->slot, &packet->item); -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetDataPacket* packet) -{ - //LOGI("ContainerSetData\n"); - if (minecraft->player && minecraft->player->containerMenu && minecraft->player->containerMenu->containerId == packet->containerId) { - //LOGI("client: SetData2 %d, %d\n", packet->id, packet->value); - minecraft->player->containerMenu->setData(packet->id, packet->value); - } -} - -void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, ChatPacket* packet ) -{ -#ifndef STANDALONE_SERVER - minecraft->gui.displayClientMessage(packet->message); -#endif -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet) -{ - if (!level) - return; - - TileEntity* te = level->getTileEntity(packet->x, packet->y, packet->z); - if (TileEntity::isType(te, TileEntityType::Sign)) { - SignTileEntity* ste = (SignTileEntity*) te; - if (ste->isEditable()) { - for (int i = 0; i < SignTileEntity::NUM_LINES; i++) { - ste->messages[i] = packet->lines[i]; - } - //ste->setChanged(); - } - } -} - -void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AdventureSettingsPacket* packet) { - if (!level) - return; - //assert(level != NULL && "level is NULL @ handle(AdventureSettingsPacket*)"); - - packet->fillIn(level->adventureSettings); -} - -void ClientSideNetworkHandler::clearChunksLoaded() -{ - // Init the chunk positions - for (int i = 0; i < NumRequestChunks; ++i) { - requestNextChunkIndexList[i].x = i/CHUNK_WIDTH; - requestNextChunkIndexList[i].y = i%CHUNK_WIDTH; - chunksLoaded[i] = false; - } -} + +#include "ClientSideNetworkHandler.h" +#include "client/Options.h" +#include "packet/PacketInclude.h" +#include "RakNetInstance.h" +#include "../world/level/chunk/ChunkSource.h" +#include "../world/level/Level.h" +#include "../world/level/storage/LevelStorageSource.h" +#include "../world/entity/player/Player.h" +#include "../world/entity/player/Inventory.h" +#include "../client/Minecraft.h" +#include "../client/gamemode/GameMode.h" +#ifndef STANDALONE_SERVER +#include "../client/gui/screens/DisconnectionScreen.h" +#endif +#include "../client/player/LocalPlayer.h" +#include "../client/multiplayer/MultiPlayerLevel.h" +#include "../client/player/input/KeyboardInput.h" +#include "../client/sound/SoundEngine.h" +#include "../world/entity/MobFactory.h" +#include "../raknet/RakPeerInterface.h" +#include "../world/level/Explosion.h" +#include "../world/level/tile/entity/FurnaceTileEntity.h" +#include "../world/inventory/BaseContainerMenu.h" +#ifndef STANDALONE_SERVER +#include "../client/particle/TakeAnimationParticle.h" +#endif +#include "../world/entity/EntityFactory.h" +#include "../world/entity/item/PrimedTnt.h" +#include "../world/entity/projectile/Arrow.h" +#include "../world/level/tile/entity/ChestTileEntity.h" +#include "../client/player/RemotePlayer.h" +#include "../world/level/tile/LevelEvent.h" +#include "../world/entity/item/FallingTile.h" + +static MultiPlayerLevel* mpcast(Level* l) { return (MultiPlayerLevel*) l; } + +ClientSideNetworkHandler::ClientSideNetworkHandler(Minecraft* minecraft, IRakNetInstance* raknetInstance) +: minecraft(minecraft), + raknetInstance(raknetInstance), + level(NULL), + requestNextChunkPosition(0), + requestNextChunkIndex(0) +{ + rakPeer = raknetInstance->getPeer(); +} + +ClientSideNetworkHandler::~ClientSideNetworkHandler() +{ +} + +void ClientSideNetworkHandler::requestNextChunk() +{ + if (requestNextChunkIndex < NumRequestChunks) + { + IntPair& chunk = requestNextChunkIndexList[requestNextChunkIndex]; + RequestChunkPacket packet(chunk.x, chunk.y); + raknetInstance->send(packet); + + //LOGI("requesting chunks @ (%d, %d)\n", chunk.x, chunk.y); + + //raknetInstance->send(new RequestChunkPacket(requestNextChunkPosition % CHUNK_CACHE_WIDTH, requestNextChunkPosition / CHUNK_CACHE_WIDTH)); + requestNextChunkIndex++; + requestNextChunkPosition++; + } +} + +bool ClientSideNetworkHandler::areAllChunksLoaded() +{ + return (requestNextChunkPosition >= (CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH)); +} + +bool ClientSideNetworkHandler::isChunkLoaded(int x, int z) +{ + if (x < 0 || x >= CHUNK_CACHE_WIDTH || z < 0 || z >= CHUNK_CACHE_WIDTH) { + LOGE("Error: Tried to request chunk (%d, %d)\n", x, z); + return true; + } + return chunksLoaded[x * CHUNK_CACHE_WIDTH + z]; + //return areAllChunksLoaded(); +} + +void ClientSideNetworkHandler::onConnect(const RakNet::RakNetGUID& hostGuid) +{ + LOGI("onConnect, server guid: %s, local guid: %s\n", hostGuid.ToString(), rakPeer->GetMyGUID().ToString()); + serverGuid = hostGuid; + + clearChunksLoaded(); + LoginPacket packet(minecraft->options.getStringValue(OPTIONS_USERNAME).c_str(), SharedConstants::NetworkProtocolVersion); + raknetInstance->send(packet); +} + +void ClientSideNetworkHandler::onUnableToConnect() +{ + LOGI("onUnableToConnect\n"); +} + +void ClientSideNetworkHandler::onDisconnect(const RakNet::RakNetGUID& guid) +{ + LOGI("onDisconnect\n"); + if (level) + { + level->isClientSide = false; + for (int i = (int)level->players.size()-1; i >= 0; --i ) { + Player* p = level->players[i]; + if (p != minecraft->player) { + p->reallyRemoveIfPlayer = true; + level->removeEntity(p); + } + } + } +#ifndef STANDALONE_SERVER + minecraft->gui.addMessage("Disconnected from server"); +#endif +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, LoginStatusPacket* packet) { + if (packet->status == LoginStatus::Success) { + raknetInstance->setIsLoggedIn(true); + return; + } + + if (packet->status == LoginStatus::Failed_ClientOld) { + LOGI("Disconnect! Client is outdated!\n"); +#ifndef STANDALONE_SERVER + minecraft->setScreen(new DisconnectionScreen("Could not connect: Outdated client!")); +#endif + } + if (packet->status == LoginStatus::Failed_ServerOld) { + LOGI("Disconnect! Server is outdated!\n"); +#ifndef STANDALONE_SERVER + minecraft->setScreen(new DisconnectionScreen("Could not connect: Outdated server!")); +#endif + } +} + + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, StartGamePacket* packet) +{ + LOGI("StartGamePacket\n"); + +#ifdef RPI + if (packet->gameType != GameType::Creative) { + minecraft->setScreen(new DisconnectionScreen("Could not connect: Incompatible server!")); + return; + } +#endif + + const std::string& levelId = LevelStorageSource::TempLevelId; + LevelStorageSource* storageSource = minecraft->getLevelSource(); + storageSource->deleteLevel(levelId); + //level = new Level(storageSource->selectLevel(levelId, true), "temp", packet->levelSeed, SharedConstants::StorageVersion); + MultiPlayerLevel* level = new MultiPlayerLevel( + storageSource->selectLevel(levelId, true), + "temp", + LevelSettings(packet->levelSeed, LevelSettings::validateGameType(packet->gameType)), + SharedConstants::StorageVersion); + level->isClientSide = true; + + bool isCreative = (packet->gameType == GameType::Creative); + LocalPlayer* player = new LocalPlayer(minecraft, level, minecraft->options.getStringValue(OPTIONS_USERNAME), level->dimension->id, isCreative); + player->owner = rakPeer->GetMyGUID(); + player->entityId = packet->entityId; + player->moveTo(packet->x, packet->y, packet->z, player->yRot, player->xRot); + + LOGI("new pos: %f, %f [%f - %f]\n", player->x, player->z, player->bb.y0, player->bb.y1); + + minecraft->setLevel(level, "ClientSideNetworkHandler -> setLevel", player); + minecraft->setIsCreativeMode(isCreative); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MessagePacket* packet) +{ + LOGI("MessagePacket\n"); +#ifndef STANDALONE_SERVER + minecraft->gui.addMessage(packet->message.C_String()); +#endif +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetTimePacket* packet) +{ + if (!level) + return; + + LOGI("SetTimePacket\n"); + level->setTime(packet->time); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddEntityPacket* packet) +{ + if (!level) + return; + + Entity* e = EntityFactory::CreateEntity(packet->type, level); + if (!e) + return; + + e->entityId = packet->entityId; + e->setPos(packet->x, packet->y, packet->z); + + // Entity Specific stuff here + switch (packet->type) + { + case EntityTypes::IdFallingTile: { + int data = -packet->data(); + FallingTile* ft = (FallingTile*) e; + ft->tile = data & 0xff; + ft->data = data >> 16; + } + case EntityTypes::IdArrow: { + Entity* owner = level->getEntity(packet->data()); + if (owner && owner->isMob()) + ((Arrow*)e)->ownerId = owner->entityId; + break; + } + default: + break; + } + + if (packet->hasMovementData()) { + /* + e->xd = packet->xd; + e->yd = packet->yd; + e->zd = packet->zd; + */ + e->lerpMotion(packet->xd, packet->yd, packet->zd); + //LOGI("Client: reading entity with %f, %f, %f\n", e->xd, e->yd, e->zd); + } + + mpcast(level)->putEntity(packet->entityId, e); +} +void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, AddPaintingPacket* packet ) { + if (!level) + return; + + Painting* painting = new Painting(level, packet->xTile, packet->yTile, packet->zTile, packet->dir, packet->motive); + mpcast(level)->putEntity(packet->entityId, painting); +} +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddMobPacket* packet) +{ + LOGI("AddMobPacket (%p)\n", level); + + if (!level) { + LOGW("Trying to add a mob with no level!\n"); + //we skip this since we will get this player anyway when we request level + return; + } + if (!packet->type) { + LOGE("Trying to add a mob without a type id\n"); + return; + } + + Mob* mob = MobFactory::CreateMob(packet->type, level); + if (!mob) { + LOGE("Server tried to add an unknown mob type! :%d\n", packet->type); + return; + } + + mob->entityId = packet->entityId; + mob->moveTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot); + mob->getEntityData()->assignValues(&packet->unpack); + level->addEntity(mob); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddPlayerPacket* packet) +{ + if (!level) { + //we skip this since we will get this player anyway when we request level + return; + } + LOGI("AddPlayerPacket\n"); + + Player* player = new RemotePlayer(level, minecraft->isCreativeMode()); + minecraft->gameMode->initAbilities(player->abilities); + player->entityId = packet->entityId; + level->addEntity(player); + + player->moveTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot); + player->name = packet->name.C_String(); + player->owner = packet->owner; + player->getEntityData()->assignValues(&packet->unpack); + int slot = Inventory::MAX_SELECTION_SIZE; + if (packet->carriedItemId == 0) { + player->inventory->clearSlot(slot); + } else { + ItemInstance newItem(packet->carriedItemId, 1, packet->carriedItemAuxValue); + player->inventory->replaceSlot(slot, &newItem); + } + player->inventory->moveToSelectedSlot(slot, true); + //player->resetPos(); + + std::string message = packet->name.C_String(); + message += " joined the game"; +#ifndef STANDALONE_SERVER + minecraft->gui.addMessage(message); +#endif +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RemovePlayerPacket* packet) { + if (!level || source == minecraft->player->owner) return; + + if (Player* player = findPlayer(level, packet->entityId, &packet->owner)) { + player->reallyRemoveIfPlayer = true; + level->removeEntity(player); + } + + //for (int i = (int)level->players.size()-1; i >= 0; --i) { + // if (level->players[i]->owner == source) { + // level->players[i]->reallyRemoveIfPlayer = true; + // level->players.erase(level->players.begin() + i); + // break; + // } + //} + + //if (!player) return; + + //std::string message = packet->name.C_String(); + //message += " joined the game"; + //minecraft->gui.addMessage(message); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RemoveEntityPacket* packet) +{ + if (!level) return; + + Entity* entity = level->getEntity(packet->entityId); + LOGI("RemoveEntityPacket %p %p, %d\n", entity, minecraft->player, entity?(int)(entity->isPlayer()): -1); + if (!entity) return; + + level->removeEntity(entity); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddItemEntityPacket* packet) +{ + if (!level) return; + + ItemEntity* entity = new ItemEntity(level, packet->x, packet->y, packet->z, ItemInstance(packet->itemId, packet->itemCount, packet->auxValue)); + entity->xd = packet->xa(); + entity->yd = packet->ya(); + entity->zd = packet->za(); + //LOGI("item-entity @ client: %f, %f, %f\n", entity->xd, entity->yd, entity->zd); + + mpcast(level)->putEntity(packet->id, entity); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, TakeItemEntityPacket* packet) { + if (!level) return; + + Entity* e = level->getEntity(packet->itemId); + if (!e) + return; + + ItemInstance item; + if (e->isItemEntity()) { + item = ((ItemEntity*) e)->item; +#ifndef STANDALONE_SERVER + if (Entity* to = level->getEntity(packet->playerId)) + minecraft->particleEngine->add(new TakeAnimationParticle(level, (ItemEntity*)e, to, -0.5f)); +#endif + } + else if (e->getEntityTypeId() == EntityTypes::IdArrow) + item = ItemInstance(Item::arrow); + + if (item.isNull()) + return; + + // try take it and if we don't have space; re-throw it + if (minecraft->player->entityId == packet->playerId + && !minecraft->player->inventory->add(&item)) { + DropItemPacket dropPacket(packet->playerId, item); + minecraft->raknetInstance->send(dropPacket); + } + level->playSound(e, "random.pop", 0.2f, 1.0f * 2.f); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet) +{ + if (!level) + return; + + //printf("MovePlayerPacket\n"); + Entity* entity = level->getEntity(packet->entityId); + if (entity) + { + entity->lerpTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot, 3); + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MoveEntityPacket* packet) +{ + if (!level) + return; + + //printf("MovePlayerPacket\n"); + Entity* entity = level->getEntity(packet->entityId); + if (entity) + { + float xRot = packet->hasRot? packet->xRot : entity->xRot; + float yRot = packet->hasRot? packet->yRot : entity->yRot; + entity->lerpTo(packet->x, packet->y, packet->z, yRot, xRot, 3); + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, UpdateBlockPacket* packet) +{ + if (!level) return; + + //LOGI("UpdateBlockPacket @ %d, %d, %d\n", packet->x, packet->y, packet->z); + + int x = packet->x, z = packet->z; + + if (isChunkLoaded(x >> 4, z >> 4)) + { + //LOGI("chunk is loaded - UPDATE @ %d, %d, %d -- %d, %d\n", x, packet->y, z, packet->blockId, packet->blockData); + + int y = packet->y; + int tileId = Tile::transformToValidBlockId(packet->blockId, x, y, z); + level->setTileAndData(x, y, z, tileId, packet->blockData); + } else { + SBufferedBlockUpdate update; + update.blockId = packet->blockId; + update.blockData = packet->blockData; + update.setData = true; + update.x = packet->x; + update.y = packet->y; + update.z = packet->z; + bufferedBlockUpdates.push_back(update); + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ExplodePacket* packet) { + if (!level) return; + Explosion explosion(level, NULL, packet->x, packet->y, packet->z, packet->r); + explosion.toBlow.insert(packet->toBlow.begin(), packet->toBlow.end()); + explosion.finalizeExplosion(); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, LevelEventPacket* packet) { + if (!level) return; + if(packet->eventId == LevelEvent::ALL_PLAYERS_SLEEPING) { + minecraft->player->setAllPlayersSleeping(); + } + else { + minecraft->level->levelEvent(NULL, packet->eventId, packet->x, packet->y, packet->z, packet->data); + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, TileEventPacket* packet) { + if (!level) return; + minecraft->level->tileEvent(packet->x, packet->y, packet->z, packet->b0, packet->b1); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet) { + if (!level) return; + + Entity* e = level->getEntity(packet->entityId); + if (e) e->handleEntityEvent(packet->eventId); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ChunkDataPacket* packet) +{ + if (!level) { + LOGI("level @ handle ChunkDataPacket is 0\n"); + return; + } + //LOGI("ChunkDataPacket\n"); + + LevelChunk* chunk = level->getChunkSource()->create(packet->x, packet->z); + if (!chunk || chunk->isEmpty()) + { + LOGI("Failed to find write-able chunk\n"); + return; + } + + //unsigned char* blockIds = chunk->getBlockData(); + DataLayer& blockData = chunk->data; + + const int setSize = LEVEL_HEIGHT / 8; + const int setShift = 4; // power of LEVEL_HEIGHT / 8 + + bool recalcHeight = false; + + int x0 = 16, x1 = 0, z0 = 16, z1 = 0, y0 = LEVEL_HEIGHT, y1 = 0; + int rx = packet->x << 4; + int rz = packet->z << 4; + + unsigned char readBlockBuffer[setSize]; + unsigned char readDataBuffer[setSize / 2]; + + for (int i = 0; i < CHUNK_COLUMNS; i++) + { + unsigned char updateBits = 0; + packet->chunkData.Read(updateBits); + + if (updateBits > 0) + { + recalcHeight = true; + + int colX = (i % CHUNK_WIDTH); + int colZ = (i / CHUNK_WIDTH); + int colDataPosition = colX << 11 | colZ << 7; + + for (int set = 0; set < 8; set++) + { + if ((updateBits & (1 << set)) != 0) + { + packet->chunkData.Read((char*)readBlockBuffer, setSize); + packet->chunkData.Read((char*)readDataBuffer, setSize / 2); + + for (int part = 0; part < setSize; part++) + { + //if (readBlockBuffer[part] == ((Tile*)Tile::grass)->id) + // readBlockBuffer[part] = 255; + int x = rx + colX; + int y = (set << setShift) + part; + int z = rz + colZ; + + int tileId = Tile::transformToValidBlockId(readBlockBuffer[part], x, y, z); + level->setTileNoUpdate(x, y, z, tileId); + } + // ((part & 1) == 0) ? readDataBuffer[part >> 1] & 0xf : (readDataBuffer[part >> 1] & 0xf0) >> 4 + + //packet->chunkData.Read((char*)(&blockIds[colDataPosition + (set << setShift)]), setSize); + // block data is only 4 bits per block + //packet->chunkData.Read((char*)(&blockData.data[(colDataPosition + (set << setShift)) >> 1]), setSize >> 1); + + memcpy(&blockData.data[(colDataPosition + (set << setShift)) >> 1], readDataBuffer, setSize >> 1); + } + + if (((1 << set) << setShift) < y0) + { + y0 = ((1 << set) << setShift); + } + if (((1 << set) << setShift) + (LEVEL_HEIGHT / 8) - 1 > y1) + { + y1 = ((1 << set) << setShift) + (LEVEL_HEIGHT / 8) - 1; + } + } + if ((i % CHUNK_WIDTH) < x0) + { + x0 = (i % CHUNK_WIDTH); + } + if ((i % CHUNK_WIDTH) > x1) + { + x1 = (i % CHUNK_WIDTH); + } + if ((i / CHUNK_WIDTH) < z0) + { + z0 = (i / CHUNK_WIDTH); + } + if ((i / CHUNK_WIDTH) > z1) + { + z1 = (i / CHUNK_WIDTH); + } + } + } + + if (recalcHeight) + { +// chunk->recalcHeightmap(); +// //chunk->recalcBlockLights(); + level->setTilesDirty((packet->x << 4) + x0, y0, (packet->z << 4) + z0, (packet->x << 4) + x1, y1, (packet->z << 4) + z1); +// int rx = packet->x << 4; +// int rz = packet->z << 4; +// level->updateLight(LightLayer::Block, x0 + rx - 1, y0, z0 + rz - 1, x1 + rx + 1, y1, z1 + rz + 1); +// //for (int cx = x0; cx < x1; cx++) +// //{ +// // for (int cz = z0; cz < z1; cz++) +// // { +// // for (int cy = y0; cy < y1; cy++) +// // { +// //level->updateLight(LightLayer::Sky, cx + rx, cy, cz + rz, cx + rx, cy, cz + rz); +// // level->updateLight(LightLayer::Block, cx + rx - 1, cy, cz + rz - 1, cx + rx + 1, cy, cz + rz + 1); +// // } +// // } +// //} +// + } + //chunk->terrainPopulated = true; + chunk->unsaved = false; + + chunksLoaded[packet->x * CHUNK_CACHE_WIDTH + packet->z] = true; + + if (areAllChunksLoaded()) + { + ReadyPacket packet(ReadyPacket::READY_REQUESTEDCHUNKS); + raknetInstance->send(packet); + + for (unsigned int i = 0; i < bufferedBlockUpdates.size(); i++) + { + const SBufferedBlockUpdate& update = bufferedBlockUpdates[i]; + int tileId = Tile::transformToValidBlockId( update.blockId, update.x, update.y, update.z ); + if (update.setData) + level->setTileAndData(update.x, update.y, update.z, tileId, update.blockData); + else + level->setTile(update.x, update.y, update.z, tileId); + } + bufferedBlockUpdates.clear(); + } + else + { + requestNextChunk(); + } +} + +class _ChunkSorter +{ +public: + _ChunkSorter(int x, int z) + : x(x), + y(z) + { + } + + bool operator() (const IntPair& a, const IntPair& b) { + const int ax = a.x - x, ay = a.y - y; + const int bx = b.x - x, by = b.y - y; + return (ax*ax + ay*ay) < (bx*bx + by*by); + } +private: + int x; + int y; +}; + + +void ClientSideNetworkHandler::arrangeRequestChunkOrder() { + clearChunksLoaded(); + + // Default sort is around center of the world + int cx = CHUNK_CACHE_WIDTH / 2; + int cz = CHUNK_CACHE_WIDTH / 2; + + // If player exists, let's sort around him + Player* p = minecraft? minecraft->player : NULL; + if (p) { + cx = Mth::floor(p->x / (float)CHUNK_WIDTH); + cz = Mth::floor(p->z / (float)CHUNK_DEPTH); + } + + _ChunkSorter sorter(cx, cz); + std::sort(requestNextChunkIndexList, requestNextChunkIndexList + NumRequestChunks, sorter); +} + +void ClientSideNetworkHandler::levelGenerated(Level* level) +{ + this->level = level; + ReadyPacket packet(ReadyPacket::READY_CLIENTGENERATION); + raknetInstance->send(packet); + + arrangeRequestChunkOrder(); + requestNextChunk(); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet) +{ + if (!level) + return; + + Entity* entity = level->getEntity(packet->entityId); + if (!entity || !entity->isPlayer()) + return; + + Player* player = (Player*)entity; + // make sure it's not our local player + if (player->owner == rakPeer->GetMyGUID()) + { + printf("Attempted to modify local player's inventory\n"); + return; + } + // override the player's inventory + //int slot = player->inventory->getSlot(packet->itemId, packet->itemAuxValue); + //if (slot >= 0) { + // player->inventory->moveToSelectedSlot(slot, true); + //item->id = packet->itemId; + //item->setAuxValue(packet->itemAuxValue); + //item->count = 63; + int slot = Inventory::MAX_SELECTION_SIZE; + if (slot >= 0) { + if (packet->itemId == 0) { + player->inventory->clearSlot(slot); + } else { + ItemInstance newItem(packet->itemId, 63, packet->itemAuxValue); + player->inventory->replaceSlot(slot, &newItem); + } + player->inventory->moveToSelectedSlot(slot, true); + } else { + LOGW("Warning: Remote player doesn't have his thing, Odd!\n"); + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet) { + if (!level) + return; + + Entity* entity = level->getEntity(packet->entityId); + if (!entity || !entity->isPlayer()) + return; + + Player* player = (Player*)entity; + // make sure it's not our local player, since that should be UpdateArmorPacket + if (player->owner == rakPeer->GetMyGUID()) { + printf("Attempted to modify local player's armor visually\n"); + return; + } + + packet->fillIn(player); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, InteractPacket* packet) +{ + if (!level) + return; + + Entity* src = level->getEntity(packet->sourceId); + Entity* entity = level->getEntity(packet->targetId); + if (src && entity && src->isPlayer()) + { + Player* player = (Player*) src; + if (InteractPacket::Attack == packet->action) + minecraft->gameMode->attack(player, entity); + if (InteractPacket::Interact == packet->action) + minecraft->gameMode->interact(player, entity); + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetEntityDataPacket* packet) +{ + if (!level) + return; + + LOGI("SetEntityDataPacket\n"); + + Entity* e = level->getEntity(packet->id); + if (e) { + SynchedEntityData* data = e->getEntityData(); + if (data) + data->assignValues(&packet->getUnpackedData()); + } +} + +void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, SetEntityMotionPacket* packet ) +{ + if (!level) + return; + + if (Entity* e = level->getEntity(packet->id)) { + /* + e->xd = packet->xd; + e->yd = packet->yd; + e->zd = packet->zd; + */ + e->lerpMotion(packet->xd, packet->yd, packet->zd); + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AnimatePacket* packet) +{ + if (!level) + return; + + // Own player - Then don't play... : + if (minecraft->player->entityId == packet->entityId) { + if (packet->action == AnimatePacket::Swing) return; + } + + Entity* entity = level->getEntity(packet->entityId); + if (!entity || !entity->isPlayer()) + return; + + Player* player = (Player*) entity; + + switch (packet->action) { + case AnimatePacket::Swing: + player->swing(); + break; + case AnimatePacket::WAKE_UP: + player->stopSleepInBed(false, false, false); + break; + default: + LOGW("Unknown Animate action: %d\n", packet->action); + break; + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, UseItemPacket* packet) +{ +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetHealthPacket* packet) +{ + if (!level || !minecraft->player) + return; + + minecraft->player->hurtTo(packet->health); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetSpawnPositionPacket* packet) { + if (!level || !minecraft || !minecraft->player) return; + if (!level->inRange(packet->x, packet->y, packet->z)) return; + + minecraft->player->setRespawnPosition(Pos(packet->x, packet->y, packet->z)); + level->getLevelData()->setSpawn(packet->x, packet->y, packet->z); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, HurtArmorPacket* packet) { + if (!level || !minecraft->player) { + return; + } + + minecraft->player->hurtArmor(packet->dmg); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RespawnPacket* packet) +{ + if (level) { + //LOGI("RespawnPacket! %d\n", findPlayer(level, packet->entityId, NULL)); + NetEventCallback::handle(level, source, packet ); + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerOpenPacket* packet) +{ + if (!level || !minecraft || !minecraft->player) + return; + + if (packet->type == ContainerType::FURNACE) { + FurnaceTileEntity* te = new FurnaceTileEntity(); + te->clientSideOnly = true; + minecraft->player->openFurnace(te); + if (minecraft->player->containerMenu) + minecraft->player->containerMenu->containerId = packet->containerId; + } + if (packet->type == ContainerType::CONTAINER) { + ChestTileEntity* te = new ChestTileEntity(); + te->clientSideOnly = true; + minecraft->player->openContainer(te); + if (minecraft->player->containerMenu) + minecraft->player->containerMenu->containerId = packet->containerId; + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet) +{ + if (minecraft && minecraft->player && minecraft->player->containerMenu) + minecraft->player->closeContainer(); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetContentPacket* packet) +{ + if (!minecraft || !minecraft->player) + return; + + if (packet->containerId == 0) { + for (unsigned int i = 0; i < packet->items.size(); ++i) { + minecraft->player->inventory->setItem(Inventory::MAX_SELECTION_SIZE + i, &packet->items[i]); + } + } else if (minecraft->player->containerMenu && minecraft->player->containerMenu->containerId == packet->containerId) { + for (unsigned int i = 0; i < packet->items.size(); ++i) { + minecraft->player->containerMenu->setSlot(i, &packet->items[i]); + } + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet) +{ + //LOGI("ContainerSetSlot\n"); + + if (!minecraft->player + || !minecraft->player->containerMenu + || minecraft->player->containerMenu->containerId != packet->containerId) + return; + + //minecraft->player->containerMenu->setSlot(packet->slot, packet->item.isNull()? NULL : &packet->item); + minecraft->player->containerMenu->setSlot(packet->slot, &packet->item); +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetDataPacket* packet) +{ + //LOGI("ContainerSetData\n"); + if (minecraft->player && minecraft->player->containerMenu && minecraft->player->containerMenu->containerId == packet->containerId) { + //LOGI("client: SetData2 %d, %d\n", packet->id, packet->value); + minecraft->player->containerMenu->setData(packet->id, packet->value); + } +} + +void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, ChatPacket* packet ) +{ +#ifndef STANDALONE_SERVER + minecraft->gui.displayClientMessage(packet->message); +#endif +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet) +{ + if (!level) + return; + + TileEntity* te = level->getTileEntity(packet->x, packet->y, packet->z); + if (TileEntity::isType(te, TileEntityType::Sign)) { + SignTileEntity* ste = (SignTileEntity*) te; + if (ste->isEditable()) { + for (int i = 0; i < SignTileEntity::NUM_LINES; i++) { + ste->messages[i] = packet->lines[i]; + } + //ste->setChanged(); + } + } +} + +void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AdventureSettingsPacket* packet) { + if (!level) + return; + //assert(level != NULL && "level is NULL @ handle(AdventureSettingsPacket*)"); + + packet->fillIn(level->adventureSettings); +} + +void ClientSideNetworkHandler::clearChunksLoaded() +{ + // Init the chunk positions + for (int i = 0; i < NumRequestChunks; ++i) { + requestNextChunkIndexList[i].x = i/CHUNK_WIDTH; + requestNextChunkIndexList[i].y = i%CHUNK_WIDTH; + chunksLoaded[i] = false; + } +} diff --git a/src/network/ClientSideNetworkHandler.h b/src/network/ClientSideNetworkHandler.h index 1505ec7..a7eb804 100755 --- a/src/network/ClientSideNetworkHandler.h +++ b/src/network/ClientSideNetworkHandler.h @@ -1,107 +1,107 @@ -#ifndef _MINECRAFT_NETWORK_CLIENTSIDENETWORKHANDLER_H_ -#define _MINECRAFT_NETWORK_CLIENTSIDENETWORKHANDLER_H_ - - -#include "NetEventCallback.h" -#include "../raknet/RakNetTypes.h" -#include "../world/level/LevelConstants.h" - -#include - -class Minecraft; -class Level; -class IRakNetInstance; - -struct SBufferedBlockUpdate -{ - int x, z; - unsigned char y; - unsigned char blockId; - unsigned char blockData; - bool setData; -}; -typedef std::vector BlockUpdateList; - -typedef struct IntPair { - int x, y; -} IntPair; - -class ClientSideNetworkHandler : public NetEventCallback -{ -public: - ClientSideNetworkHandler(Minecraft* minecraft, IRakNetInstance* raknetInstance); - virtual ~ClientSideNetworkHandler(); - - virtual void levelGenerated(Level* level); - - virtual void onConnect(const RakNet::RakNetGUID& hostGuid); - virtual void onUnableToConnect(); - virtual void onDisconnect(const RakNet::RakNetGUID& guid); - - virtual void handle(const RakNet::RakNetGUID& source, LoginStatusPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, StartGamePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, MessagePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, SetTimePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, AddItemEntityPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, AddPaintingPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, TakeItemEntityPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, AddEntityPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, AddMobPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, AddPlayerPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, RemoveEntityPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, RemovePlayerPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, MoveEntityPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, UpdateBlockPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ExplodePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, LevelEventPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, TileEventPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ChunkDataPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, InteractPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, SetEntityDataPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, SetEntityMotionPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, SetHealthPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, SetSpawnPositionPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, AnimatePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, UseItemPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, HurtArmorPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, RespawnPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ContainerOpenPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ContainerSetContentPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ContainerSetDataPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ChatPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, AdventureSettingsPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet); -private: - - void requestNextChunk(); - void arrangeRequestChunkOrder(); - - bool isChunkLoaded(int x, int z); - bool areAllChunksLoaded(); - void clearChunksLoaded(); -private: - - Minecraft* minecraft; - Level* level; - IRakNetInstance* raknetInstance; - RakNet::RakPeerInterface* rakPeer; - - RakNet::RakNetGUID serverGuid; - - BlockUpdateList bufferedBlockUpdates; - int requestNextChunkPosition; - - static const int NumRequestChunks = CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH; - - int requestNextChunkIndex; - IntPair requestNextChunkIndexList[NumRequestChunks]; - bool chunksLoaded[NumRequestChunks]; -}; - -#endif +#ifndef _MINECRAFT_NETWORK_CLIENTSIDENETWORKHANDLER_H_ +#define _MINECRAFT_NETWORK_CLIENTSIDENETWORKHANDLER_H_ + + +#include "NetEventCallback.h" +#include "../raknet/RakNetTypes.h" +#include "../world/level/LevelConstants.h" + +#include + +class Minecraft; +class Level; +class IRakNetInstance; + +struct SBufferedBlockUpdate +{ + int x, z; + unsigned char y; + unsigned char blockId; + unsigned char blockData; + bool setData; +}; +typedef std::vector BlockUpdateList; + +typedef struct IntPair { + int x, y; +} IntPair; + +class ClientSideNetworkHandler : public NetEventCallback +{ +public: + ClientSideNetworkHandler(Minecraft* minecraft, IRakNetInstance* raknetInstance); + virtual ~ClientSideNetworkHandler(); + + virtual void levelGenerated(Level* level); + + virtual void onConnect(const RakNet::RakNetGUID& hostGuid); + virtual void onUnableToConnect(); + virtual void onDisconnect(const RakNet::RakNetGUID& guid); + + virtual void handle(const RakNet::RakNetGUID& source, LoginStatusPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, StartGamePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, MessagePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, SetTimePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, AddItemEntityPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, AddPaintingPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, TakeItemEntityPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, AddEntityPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, AddMobPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, AddPlayerPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, RemoveEntityPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, RemovePlayerPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, MoveEntityPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, UpdateBlockPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ExplodePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, LevelEventPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, TileEventPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ChunkDataPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, InteractPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, SetEntityDataPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, SetEntityMotionPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, SetHealthPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, SetSpawnPositionPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, AnimatePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, UseItemPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, HurtArmorPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, RespawnPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ContainerOpenPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ContainerSetContentPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ContainerSetDataPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ChatPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, AdventureSettingsPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet); +private: + + void requestNextChunk(); + void arrangeRequestChunkOrder(); + + bool isChunkLoaded(int x, int z); + bool areAllChunksLoaded(); + void clearChunksLoaded(); +private: + + Minecraft* minecraft; + Level* level; + IRakNetInstance* raknetInstance; + RakNet::RakPeerInterface* rakPeer; + + RakNet::RakNetGUID serverGuid; + + BlockUpdateList bufferedBlockUpdates; + int requestNextChunkPosition; + + static const int NumRequestChunks = CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH; + + int requestNextChunkIndex; + IntPair requestNextChunkIndexList[NumRequestChunks]; + bool chunksLoaded[NumRequestChunks]; +}; + +#endif diff --git a/src/network/NATPunchHandler.cpp b/src/network/NATPunchHandler.cpp index 35108b0..81fd07d 100755 --- a/src/network/NATPunchHandler.cpp +++ b/src/network/NATPunchHandler.cpp @@ -1,33 +1,33 @@ -#include "NATPunchHandler.h" -#include "../raknet/TCPInterface.h" -#include "../raknet/HTTPConnection.h" -#include "PHPDirectoryServer2.h" - -using namespace RakNet; -NATPuchHandler::NATPuchHandler() { - tcpInterface = new TCPInterface; -} -NATPuchHandler::~NATPuchHandler() { - delete tcpInterface; -} - -void NATPuchHandler::initialize() { - tcpInterface->Start(0, 64); -} - -void NATPuchHandler::registerToGameList(const RakNet::RakString& serverName, int port) { - HTTPConnection httpConnection; - httpConnection.Init(tcpInterface, "johanbernhardsson.se"); - PHPDirectoryServer2 directoryServer; - directoryServer.Init(&httpConnection, "/DirectoryServer.php"); - directoryServer.UploadTable("", serverName, port, true); -} - -void NATPuchHandler::removeFromGameList() { - -} - -void NATPuchHandler::close() { - -} - +#include "NATPunchHandler.h" +#include "../raknet/TCPInterface.h" +#include "../raknet/HTTPConnection.h" +#include "PHPDirectoryServer2.h" + +using namespace RakNet; +NATPuchHandler::NATPuchHandler() { + tcpInterface = new TCPInterface; +} +NATPuchHandler::~NATPuchHandler() { + delete tcpInterface; +} + +void NATPuchHandler::initialize() { + tcpInterface->Start(0, 64); +} + +void NATPuchHandler::registerToGameList(const RakNet::RakString& serverName, int port) { + HTTPConnection httpConnection; + httpConnection.Init(tcpInterface, "johanbernhardsson.se"); + PHPDirectoryServer2 directoryServer; + directoryServer.Init(&httpConnection, "/DirectoryServer.php"); + directoryServer.UploadTable("", serverName, port, true); +} + +void NATPuchHandler::removeFromGameList() { + +} + +void NATPuchHandler::close() { + +} + diff --git a/src/network/NATPunchHandler.h b/src/network/NATPunchHandler.h index 0246c0d..45b4885 100755 --- a/src/network/NATPunchHandler.h +++ b/src/network/NATPunchHandler.h @@ -1,24 +1,24 @@ -#ifndef _MINECRAFT_NETWORK_NATPUNCHHANDLER_H_ -#define _MINECRAFT_NETWORK_NATPUNCHHANDLER_H_ -#include "../raknet/TCPInterface.h" -#include "../raknet/RakString.h" -class NATPuchHandler { -public: - enum NATPuchHandlerStatus { - NATPuchInitilized = 0, - NATPuchFetchingServerList = 1, - NATPuchConnecting = 2, - NATPuchConnected = 3, - NATPuchDissconnected = 4 - }; - NATPuchHandler(); - ~NATPuchHandler(); - void initialize(); - void registerToGameList(const RakNet::RakString& serverName, int port); - void removeFromGameList(); - void close(); -private: - RakNet::TCPInterface *tcpInterface; -}; - -#endif /* _MINECRAFT_NETWORK_NATPUNCHHANDLER_H_ */ +#ifndef _MINECRAFT_NETWORK_NATPUNCHHANDLER_H_ +#define _MINECRAFT_NETWORK_NATPUNCHHANDLER_H_ +#include "../raknet/TCPInterface.h" +#include "../raknet/RakString.h" +class NATPuchHandler { +public: + enum NATPuchHandlerStatus { + NATPuchInitilized = 0, + NATPuchFetchingServerList = 1, + NATPuchConnecting = 2, + NATPuchConnected = 3, + NATPuchDissconnected = 4 + }; + NATPuchHandler(); + ~NATPuchHandler(); + void initialize(); + void registerToGameList(const RakNet::RakString& serverName, int port); + void removeFromGameList(); + void close(); +private: + RakNet::TCPInterface *tcpInterface; +}; + +#endif /* _MINECRAFT_NETWORK_NATPUNCHHANDLER_H_ */ diff --git a/src/network/NetEventCallback.cpp b/src/network/NetEventCallback.cpp index aa10752..74fa590 100755 --- a/src/network/NetEventCallback.cpp +++ b/src/network/NetEventCallback.cpp @@ -1,59 +1,59 @@ -#include "NetEventCallback.h" -#include "../world/level/Level.h" -#include "packet/RespawnPacket.h" - -// -// Common packet handling implementation for Client and Server -// -/* -void NetEventCallback::handle( const RakNet::RakNetGUID& source, AnimatePacket* packet ) -{ - Entity* entity = level->getEntity(packet->entityId); - if (entity && entity->isPlayer()) { - Player* player = (Player*) entity; - - switch (packet->action) { - case AnimatePacket::Swing: - player->swing(); - break; - default: - LOGW("Unknown Animate action: %d\n", packet->action); - break; - } - } -} -*/ - - -Player* NetEventCallback::findPlayer(Level* level, int entityId) { - Entity* e = level->getEntity(entityId); - if (e) { - if (e->isPlayer()) return (Player*) e; - LOGE("Entity: %p is supposed to be a player but is not (type %d)!\n", e, e->getEntityTypeId()); - } - return NULL; -} - -Player* NetEventCallback::findPlayer(Level* level, const RakNet::RakNetGUID* source) { - for (unsigned int i = 0; i < level->players.size(); ++i) - if (level->players[i]->owner == *source) return level->players[i]; - return NULL; -} - -Player* NetEventCallback::findPlayer( Level* level, int entityId, const RakNet::RakNetGUID* source ) -{ - if (entityId != -1) - if (Player* p = findPlayer(level, entityId)) return p; - if (source != NULL) - if (Player* p = findPlayer(level, source)) return p; - return NULL; -} - -void NetEventCallback::handle( Level* level, const RakNet::RakNetGUID& source, RespawnPacket* packet ) -{ - if (Player* p = findPlayer(level, packet->entityId, NULL)) { - p->moveTo(packet->x, packet->y, packet->z, p->yRot, p->xRot); - p->reset(); - p->resetPos(true); - } -} +#include "NetEventCallback.h" +#include "../world/level/Level.h" +#include "packet/RespawnPacket.h" + +// +// Common packet handling implementation for Client and Server +// +/* +void NetEventCallback::handle( const RakNet::RakNetGUID& source, AnimatePacket* packet ) +{ + Entity* entity = level->getEntity(packet->entityId); + if (entity && entity->isPlayer()) { + Player* player = (Player*) entity; + + switch (packet->action) { + case AnimatePacket::Swing: + player->swing(); + break; + default: + LOGW("Unknown Animate action: %d\n", packet->action); + break; + } + } +} +*/ + + +Player* NetEventCallback::findPlayer(Level* level, int entityId) { + Entity* e = level->getEntity(entityId); + if (e) { + if (e->isPlayer()) return (Player*) e; + LOGE("Entity: %p is supposed to be a player but is not (type %d)!\n", e, e->getEntityTypeId()); + } + return NULL; +} + +Player* NetEventCallback::findPlayer(Level* level, const RakNet::RakNetGUID* source) { + for (unsigned int i = 0; i < level->players.size(); ++i) + if (level->players[i]->owner == *source) return level->players[i]; + return NULL; +} + +Player* NetEventCallback::findPlayer( Level* level, int entityId, const RakNet::RakNetGUID* source ) +{ + if (entityId != -1) + if (Player* p = findPlayer(level, entityId)) return p; + if (source != NULL) + if (Player* p = findPlayer(level, source)) return p; + return NULL; +} + +void NetEventCallback::handle( Level* level, const RakNet::RakNetGUID& source, RespawnPacket* packet ) +{ + if (Player* p = findPlayer(level, packet->entityId, NULL)) { + p->moveTo(packet->x, packet->y, packet->z, p->yRot, p->xRot); + p->reset(); + p->resetPos(true); + } +} diff --git a/src/network/NetEventCallback.h b/src/network/NetEventCallback.h index 53e5fa4..3ebc510 100755 --- a/src/network/NetEventCallback.h +++ b/src/network/NetEventCallback.h @@ -1,134 +1,134 @@ -#ifndef _MINECRAFT_NETWORK_NETEVENTCALLBACK_H_ -#define _MINECRAFT_NETWORK_NETEVENTCALLBACK_H_ - -class AddItemEntityPacket; -class AddPaintingPacket; -class AdventureSettingsPacket; -class TakeItemEntityPacket; -class LoginPacket; -class ReadyPacket; -class LoginStatusPacket; -class MessagePacket; -class SetTimePacket; -class StartGamePacket; -class AddEntityPacket; -class AddMobPacket; -class AddPlayerPacket; -class RemovePlayerPacket; -class RemoveEntityPacket; -class MoveEntityPacket; -//class TeleportEntityPacket; -class MovePlayerPacket; -class PlaceBlockPacket; -class RemoveBlockPacket; -class UpdateBlockPacket; -class ExplodePacket; -class LevelEventPacket; -class TileEventPacket; -class EntityEventPacket; -class RequestChunkPacket; -class ChunkDataPacket; -class PlayerEquipmentPacket; -class PlayerArmorEquipmentPacket; -class InteractPacket; -class SetEntityDataPacket; -class SetEntityMotionPacket; -class SetHealthPacket; -class SetSpawnPositionPacket; -class SendInventoryPacket; -class DropItemPacket; -class AnimatePacket; -class UseItemPacket; -class PlayerActionPacket; -class HurtArmorPacket; -class RespawnPacket; -class ContainerAckPacket; -class ContainerOpenPacket; -class ContainerClosePacket; -class ContainerSetSlotPacket; -class ContainerSetDataPacket; -class ContainerSetContentPacket; -class ChatPacket; -class SignUpdatePacket; -class Minecraft; -class Level; - -#include "../world/level/tile/Tile.h" - -namespace RakNet -{ - struct RakNetGUID; -} - -class NetEventCallback -{ -public: - virtual void levelGenerated(Level* level) {} - virtual ~NetEventCallback() {} - - virtual void onConnect(const RakNet::RakNetGUID& hostGuid) {}; - virtual void onUnableToConnect() {}; - virtual void onNewClient(const RakNet::RakNetGUID& clientGuid) {}; - virtual void onDisconnect(const RakNet::RakNetGUID& guid) {}; - - virtual void handle(const RakNet::RakNetGUID& source, LoginPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ReadyPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, LoginStatusPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, SetTimePacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, MessagePacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, StartGamePacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, AddItemEntityPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, AddPaintingPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, TakeItemEntityPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, AddEntityPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, AddMobPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, AddPlayerPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, RemovePlayerPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, RemoveEntityPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, MoveEntityPacket* packet) {} - //virtual void handle(const RakNet::RakNetGUID& source, TeleportEntityPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, PlaceBlockPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, RemoveBlockPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, UpdateBlockPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ExplodePacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, LevelEventPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, TileEventPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, RequestChunkPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ChunkDataPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, SetEntityDataPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, SetEntityMotionPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, SetHealthPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, SetSpawnPositionPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, InteractPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, UseItemPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, PlayerActionPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, HurtArmorPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, SendInventoryPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, DropItemPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ContainerOpenPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ContainerAckPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ContainerSetDataPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ContainerSetContentPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, ChatPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, AdventureSettingsPacket* packet) {} - virtual void handle(const RakNet::RakNetGUID& source, AnimatePacket* packet) {} - - // - // Common implementation for Client and Server - // - virtual void handle(const RakNet::RakNetGUID& source, RespawnPacket* packet) {} - virtual void handle(Level* level, const RakNet::RakNetGUID& source, RespawnPacket* packet); - - Player* findPlayer(Level* level, int entityId); - Player* findPlayer(Level* level, const RakNet::RakNetGUID* source); - Player* findPlayer(Level* level, int entityId, const RakNet::RakNetGUID* source); -}; - -#endif +#ifndef _MINECRAFT_NETWORK_NETEVENTCALLBACK_H_ +#define _MINECRAFT_NETWORK_NETEVENTCALLBACK_H_ + +class AddItemEntityPacket; +class AddPaintingPacket; +class AdventureSettingsPacket; +class TakeItemEntityPacket; +class LoginPacket; +class ReadyPacket; +class LoginStatusPacket; +class MessagePacket; +class SetTimePacket; +class StartGamePacket; +class AddEntityPacket; +class AddMobPacket; +class AddPlayerPacket; +class RemovePlayerPacket; +class RemoveEntityPacket; +class MoveEntityPacket; +//class TeleportEntityPacket; +class MovePlayerPacket; +class PlaceBlockPacket; +class RemoveBlockPacket; +class UpdateBlockPacket; +class ExplodePacket; +class LevelEventPacket; +class TileEventPacket; +class EntityEventPacket; +class RequestChunkPacket; +class ChunkDataPacket; +class PlayerEquipmentPacket; +class PlayerArmorEquipmentPacket; +class InteractPacket; +class SetEntityDataPacket; +class SetEntityMotionPacket; +class SetHealthPacket; +class SetSpawnPositionPacket; +class SendInventoryPacket; +class DropItemPacket; +class AnimatePacket; +class UseItemPacket; +class PlayerActionPacket; +class HurtArmorPacket; +class RespawnPacket; +class ContainerAckPacket; +class ContainerOpenPacket; +class ContainerClosePacket; +class ContainerSetSlotPacket; +class ContainerSetDataPacket; +class ContainerSetContentPacket; +class ChatPacket; +class SignUpdatePacket; +class Minecraft; +class Level; + +#include "../world/level/tile/Tile.h" + +namespace RakNet +{ + struct RakNetGUID; +} + +class NetEventCallback +{ +public: + virtual void levelGenerated(Level* level) {} + virtual ~NetEventCallback() {} + + virtual void onConnect(const RakNet::RakNetGUID& hostGuid) {}; + virtual void onUnableToConnect() {}; + virtual void onNewClient(const RakNet::RakNetGUID& clientGuid) {}; + virtual void onDisconnect(const RakNet::RakNetGUID& guid) {}; + + virtual void handle(const RakNet::RakNetGUID& source, LoginPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ReadyPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, LoginStatusPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, SetTimePacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, MessagePacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, StartGamePacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, AddItemEntityPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, AddPaintingPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, TakeItemEntityPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, AddEntityPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, AddMobPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, AddPlayerPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, RemovePlayerPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, RemoveEntityPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, MoveEntityPacket* packet) {} + //virtual void handle(const RakNet::RakNetGUID& source, TeleportEntityPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, PlaceBlockPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, RemoveBlockPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, UpdateBlockPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ExplodePacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, LevelEventPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, TileEventPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, RequestChunkPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ChunkDataPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, SetEntityDataPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, SetEntityMotionPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, SetHealthPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, SetSpawnPositionPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, InteractPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, UseItemPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, PlayerActionPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, HurtArmorPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, SendInventoryPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, DropItemPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ContainerOpenPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ContainerAckPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ContainerSetDataPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ContainerSetContentPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, ChatPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, AdventureSettingsPacket* packet) {} + virtual void handle(const RakNet::RakNetGUID& source, AnimatePacket* packet) {} + + // + // Common implementation for Client and Server + // + virtual void handle(const RakNet::RakNetGUID& source, RespawnPacket* packet) {} + virtual void handle(Level* level, const RakNet::RakNetGUID& source, RespawnPacket* packet); + + Player* findPlayer(Level* level, int entityId); + Player* findPlayer(Level* level, const RakNet::RakNetGUID* source); + Player* findPlayer(Level* level, int entityId, const RakNet::RakNetGUID* source); +}; + +#endif diff --git a/src/network/PHPDirectoryServer2.h b/src/network/PHPDirectoryServer2.h index 40bc73c..a5894e5 100755 --- a/src/network/PHPDirectoryServer2.h +++ b/src/network/PHPDirectoryServer2.h @@ -1,137 +1,137 @@ -/// \file -/// \brief Contains PHPDirectoryServer2, a client for communicating with a HTTP list of game servers -/// -/// This file is part of RakNet Copyright 2008 Kevin Jenkins. -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. -/// GPL license users are subject to the GNU General Public -/// License as published by the Free -/// Software Foundation; either version 2 of the License, or (at your -/// option) any later version. - -#ifndef __PHP_DIRECTORY_SERVER_2 -#define __PHP_DIRECTORY_SERVER_2 - -#include "../raknet/RakString.h" -#include "../raknet/HTTPConnection.h" -#include "../raknet/RakNetTypes.h" -#include "../raknet/DS_Queue.h" -#include "../raknet/DS_Table.h" -#include "../raknet/DS_Map.h" - -namespace RakNet { - -struct SystemAddress; - -enum HTTPReadResult -{ - HTTP_RESULT_GOT_TABLE, - HTTP_RESULT_EMPTY -}; - -/// \brief Use PHPDirectoryServer2 as a C++ client to DirectoryServer.php -/// -/// PHPDirectoryServer2 works with the HTTPConnection class (which works with the TCPInterface class) in order to communiate with DirectoryServer.php found under Samples/PHPDirectoryServer2 -class PHPDirectoryServer2 -{ -public: - PHPDirectoryServer2(); - virtual ~PHPDirectoryServer2(); - - /// Associate PHPDirectoryServer2 with the HTTPConnection class it will communicate through - /// \param[in] _http The instance of HTTP connection we will communicate through - /// \param[in] _path The path to the PHP file on the remote server. For example, if the path is mysite.com/raknet/DirectoryServer.php then you would enter raknet/DirectoryServer.php - void Init(HTTPConnection *_http, const char *_path); - - /// Set a parameter (these are passed to the server) - /// To delete a column, just pass an empty string for value - /// Store the game name and port with UploadTable, rather than SetField, as these columns are required and use reserved column names - /// \param[in] columnName The name of the column to store - /// \param[in] value What value to hold for the uploaded row (only one row can be uploaded at a time) - void SetField(RakNet::RakString columnName, RakNet::RakString value); - - /// Returns the number of fields set with SetField() - unsigned int GetFieldCount(void) const; - - /// Returns a field set with SetField() - /// \param[in] index The 0 based index into the field list - /// \param[out] columnName The \a columnName parameter passed to SetField() - /// \param[out] value The \a value parameter passed to SetField() - void GetField(unsigned int index, RakNet::RakString &columnName, RakNet::RakString &value); - - /// Set all parameters at once from a table - /// \param[in] table A table containing the values you want to send. Note that all values are stored as strings in PHP - void SetFields(DataStructures::Table *table); - - /// Clear all fields - void ClearFields(void); - - /// Upload the values set with SetFields() or SetField() - /// On success: - /// 1. HTTPConnection::HasRead() will return true. - /// 2. Pass the value returned by HTTPConnection::Read() to PHPDirectoryServer2::ProcessHTTPRead(). - /// 3. The return value of PHPDirectoryServer2::ProcessHTTPRead() will be HTTP_RESULT_EMPTY - /// \param[in] uploadPassword The upload password set in the PHP page itself when you first uploaded and viewed it in the webpage. - /// \param[in] gameName Every entry must have a game name. Pass it here. - /// \param[in] gamePort Every entry must have a game port. Pass it here. The IP address will be stored automatically, or you can manually set it by passing a field named _System_Address - /// \param[in] autoRepost Tables must be uploaded every 60 seconds or they get dropped. Set autoRepost to true to automatically reupload the most recent table. - void UploadTable(RakNet::RakString uploadPassword, RakNet::RakString gameName, unsigned short gamePort, bool autoRepost); - - /// Send a download request to the PHP server. - /// On success: - /// 1. HTTPConnection::HasRead() will return true. - /// 2. Pass the value returned by HTTPConnection::Read() to PHPDirectoryServer2::ProcessHTTPRead(). - /// 3. The return value of PHPDirectoryServer2::ProcessHTTPRead() will be HTTP_RESULT_GOT_TABLE or HTTP_RESULT_EMPTY - /// 4. On HTTP_RESULT_GOT_TABLE, use GetLastDownloadedTable() to read the results. - /// \param[in] downloadPassword The download password set in the PHP page itself when you first uploaded and viewed it in the webpage. - void DownloadTable(RakNet::RakString downloadPassword); - - /// Same as calling DownloadTable immediately followed by UploadTable, except only the download result is returned - /// \param[in] uploadPassword The upload password set in the PHP page itself when you first uploaded and viewed it in the webpage. - /// \param[in] downloadPassword The download password set in the PHP page itself when you first uploaded and viewed it in the webpage. - /// \param[in] gameName Every entry must have a game name. Pass it here. - /// \param[in] gamePort Every entry must have a game port. Pass it here. The IP address will be stored automatically, or you can manually set it by passing a field named _System_Address - /// \param[in] autoRepost Tables must be uploaded every 60 seconds or they get dropped. Set autoRepost to true to automatically reupload the most recent table. - void UploadAndDownloadTable(RakNet::RakString uploadPassword, RakNet::RakString downloadPassword, RakNet::RakString gameName, unsigned short gamePort, bool autoRepost); - - /// When HTTPConnection::ProcessDataPacket() returns true, and not an error, pass HTTPConnection::Read() to this function - /// The message will be parsed into DataStructures::Table, and a copy stored internally which can be retrieved by GetLastDownloadedTable(); - /// \param[in] packetData Returned from HTTPInterface::Read() - /// \return One of the values for HTTPReadResult - HTTPReadResult ProcessHTTPRead(RakNet::RakString httpRead); - - /// Returns the last value returned from ProcessHTTPString - /// Default columns are "__GAME_NAME", "__GAME_PORT", "_System_Address" - /// \return The table created by parsing httpString - const DataStructures::Table *GetLastDownloadedTable(void) const; - - /// Call this periodically - it will handle connection states and refreshing updates to the server - void Update(void); - -private: - HTTPConnection *http; - RakNet::RakString pathToPHP; - - RakNet::RakString gameNameParam; - unsigned short gamePortParam; - - void SendOperation(void); - void PushColumnsAndValues(DataStructures::List &columns, DataStructures::List &values); - - DataStructures::Table lastDownloadedTable; - DataStructures::Map fields; - RakNet::RakString currentOperation; - RakNet::TimeMS nextRepost; - -}; - -} // namespace RakNet - -#endif - +/// \file +/// \brief Contains PHPDirectoryServer2, a client for communicating with a HTTP list of game servers +/// +/// This file is part of RakNet Copyright 2008 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __PHP_DIRECTORY_SERVER_2 +#define __PHP_DIRECTORY_SERVER_2 + +#include "../raknet/RakString.h" +#include "../raknet/HTTPConnection.h" +#include "../raknet/RakNetTypes.h" +#include "../raknet/DS_Queue.h" +#include "../raknet/DS_Table.h" +#include "../raknet/DS_Map.h" + +namespace RakNet { + +struct SystemAddress; + +enum HTTPReadResult +{ + HTTP_RESULT_GOT_TABLE, + HTTP_RESULT_EMPTY +}; + +/// \brief Use PHPDirectoryServer2 as a C++ client to DirectoryServer.php +/// +/// PHPDirectoryServer2 works with the HTTPConnection class (which works with the TCPInterface class) in order to communiate with DirectoryServer.php found under Samples/PHPDirectoryServer2 +class PHPDirectoryServer2 +{ +public: + PHPDirectoryServer2(); + virtual ~PHPDirectoryServer2(); + + /// Associate PHPDirectoryServer2 with the HTTPConnection class it will communicate through + /// \param[in] _http The instance of HTTP connection we will communicate through + /// \param[in] _path The path to the PHP file on the remote server. For example, if the path is mysite.com/raknet/DirectoryServer.php then you would enter raknet/DirectoryServer.php + void Init(HTTPConnection *_http, const char *_path); + + /// Set a parameter (these are passed to the server) + /// To delete a column, just pass an empty string for value + /// Store the game name and port with UploadTable, rather than SetField, as these columns are required and use reserved column names + /// \param[in] columnName The name of the column to store + /// \param[in] value What value to hold for the uploaded row (only one row can be uploaded at a time) + void SetField(RakNet::RakString columnName, RakNet::RakString value); + + /// Returns the number of fields set with SetField() + unsigned int GetFieldCount(void) const; + + /// Returns a field set with SetField() + /// \param[in] index The 0 based index into the field list + /// \param[out] columnName The \a columnName parameter passed to SetField() + /// \param[out] value The \a value parameter passed to SetField() + void GetField(unsigned int index, RakNet::RakString &columnName, RakNet::RakString &value); + + /// Set all parameters at once from a table + /// \param[in] table A table containing the values you want to send. Note that all values are stored as strings in PHP + void SetFields(DataStructures::Table *table); + + /// Clear all fields + void ClearFields(void); + + /// Upload the values set with SetFields() or SetField() + /// On success: + /// 1. HTTPConnection::HasRead() will return true. + /// 2. Pass the value returned by HTTPConnection::Read() to PHPDirectoryServer2::ProcessHTTPRead(). + /// 3. The return value of PHPDirectoryServer2::ProcessHTTPRead() will be HTTP_RESULT_EMPTY + /// \param[in] uploadPassword The upload password set in the PHP page itself when you first uploaded and viewed it in the webpage. + /// \param[in] gameName Every entry must have a game name. Pass it here. + /// \param[in] gamePort Every entry must have a game port. Pass it here. The IP address will be stored automatically, or you can manually set it by passing a field named _System_Address + /// \param[in] autoRepost Tables must be uploaded every 60 seconds or they get dropped. Set autoRepost to true to automatically reupload the most recent table. + void UploadTable(RakNet::RakString uploadPassword, RakNet::RakString gameName, unsigned short gamePort, bool autoRepost); + + /// Send a download request to the PHP server. + /// On success: + /// 1. HTTPConnection::HasRead() will return true. + /// 2. Pass the value returned by HTTPConnection::Read() to PHPDirectoryServer2::ProcessHTTPRead(). + /// 3. The return value of PHPDirectoryServer2::ProcessHTTPRead() will be HTTP_RESULT_GOT_TABLE or HTTP_RESULT_EMPTY + /// 4. On HTTP_RESULT_GOT_TABLE, use GetLastDownloadedTable() to read the results. + /// \param[in] downloadPassword The download password set in the PHP page itself when you first uploaded and viewed it in the webpage. + void DownloadTable(RakNet::RakString downloadPassword); + + /// Same as calling DownloadTable immediately followed by UploadTable, except only the download result is returned + /// \param[in] uploadPassword The upload password set in the PHP page itself when you first uploaded and viewed it in the webpage. + /// \param[in] downloadPassword The download password set in the PHP page itself when you first uploaded and viewed it in the webpage. + /// \param[in] gameName Every entry must have a game name. Pass it here. + /// \param[in] gamePort Every entry must have a game port. Pass it here. The IP address will be stored automatically, or you can manually set it by passing a field named _System_Address + /// \param[in] autoRepost Tables must be uploaded every 60 seconds or they get dropped. Set autoRepost to true to automatically reupload the most recent table. + void UploadAndDownloadTable(RakNet::RakString uploadPassword, RakNet::RakString downloadPassword, RakNet::RakString gameName, unsigned short gamePort, bool autoRepost); + + /// When HTTPConnection::ProcessDataPacket() returns true, and not an error, pass HTTPConnection::Read() to this function + /// The message will be parsed into DataStructures::Table, and a copy stored internally which can be retrieved by GetLastDownloadedTable(); + /// \param[in] packetData Returned from HTTPInterface::Read() + /// \return One of the values for HTTPReadResult + HTTPReadResult ProcessHTTPRead(RakNet::RakString httpRead); + + /// Returns the last value returned from ProcessHTTPString + /// Default columns are "__GAME_NAME", "__GAME_PORT", "_System_Address" + /// \return The table created by parsing httpString + const DataStructures::Table *GetLastDownloadedTable(void) const; + + /// Call this periodically - it will handle connection states and refreshing updates to the server + void Update(void); + +private: + HTTPConnection *http; + RakNet::RakString pathToPHP; + + RakNet::RakString gameNameParam; + unsigned short gamePortParam; + + void SendOperation(void); + void PushColumnsAndValues(DataStructures::List &columns, DataStructures::List &values); + + DataStructures::Table lastDownloadedTable; + DataStructures::Map fields; + RakNet::RakString currentOperation; + RakNet::TimeMS nextRepost; + +}; + +} // namespace RakNet + +#endif + diff --git a/src/network/Packet.cpp b/src/network/Packet.cpp index ca1b328..d410dba 100755 --- a/src/network/Packet.cpp +++ b/src/network/Packet.cpp @@ -1,212 +1,212 @@ - -#include "Packet.h" -#include "../world/level/chunk/LevelChunk.h" - -#include "packet/PacketInclude.h" - -Packet::Packet() -: priority(HIGH_PRIORITY), - reliability(RELIABLE) -{} - -Packet* MinecraftPackets::createPacket(int id) -{ - Packet* packet = NULL; - - switch (id - ID_USER_PACKET_ENUM) { - default: - break; - - case PACKET_LOGIN: - packet = new LoginPacket(); - break; - case PACKET_LOGINSTATUS: - packet = new LoginStatusPacket(); - break; - case PACKET_READY: - packet = new ReadyPacket(); - break; - case PACKET_SETTIME: - packet = new SetTimePacket(); - break; - case PACKET_MESSAGE: - packet = new MessagePacket(); - break; - case PACKET_STARTGAME: - packet = new StartGamePacket(); - break; - case PACKET_ADDENTITY: - packet = new AddEntityPacket(); - break; - case PACKET_ADDITEMENTITY: - packet = new AddItemEntityPacket(); - break; - case PACKET_TAKEITEMENTITY: - packet = new TakeItemEntityPacket(); - break; - case PACKET_ADDMOB: - packet = new AddMobPacket(); - break; - case PACKET_ADDPLAYER: - packet = new AddPlayerPacket(); - break; - case PACKET_REMOVEPLAYER: - packet = new RemovePlayerPacket(); - break; - case PACKET_MOVEENTITY: - packet = new MoveEntityPacket(); - break; - case PACKET_MOVEENTITY_POSROT: - packet = new MoveEntityPacket_PosRot(); - break; - //case PACKET_TELEPORTENTITY: - // packet = new TeleportEntityPacket(); - // break; - case PACKET_MOVEPLAYER: - packet = new MovePlayerPacket(); - break; - case PACKET_RESPAWN: - packet = new RespawnPacket(); - break; - case PACKET_REMOVEENTITY: - packet = new RemoveEntityPacket(); - break; - case PACKET_PLACEBLOCK: - packet = new PlaceBlockPacket(); - break; - case PACKET_REMOVEBLOCK: - packet = new RemoveBlockPacket(); - break; - case PACKET_UPDATEBLOCK: - packet = new UpdateBlockPacket(); - break; - case PACKET_EXPLODE: - packet = new ExplodePacket(); - break; - case PACKET_LEVELEVENT: - packet = new LevelEventPacket(); - break; - case PACKET_TILEEVENT: - packet = new TileEventPacket(); - break; - case PACKET_ENTITYEVENT: - packet = new EntityEventPacket(); - break; - case PACKET_REQUESTCHUNK: - packet = new RequestChunkPacket(); - break; - case PACKET_CHUNKDATA: - packet = new ChunkDataPacket(); - break; - case PACKET_PLAYEREQUIPMENT: - packet = new PlayerEquipmentPacket(); - break; - case PACKET_PLAYERARMOREQUIPMENT: - packet = new PlayerArmorEquipmentPacket(); - break; - case PACKET_INTERACT: - packet = new InteractPacket(); - break; - case PACKET_USEITEM: - packet = new UseItemPacket(); - break; - case PACKET_PLAYERACTION: - packet = new PlayerActionPacket(); - break; - case PACKET_HURTARMOR: - packet = new HurtArmorPacket(); - break; - case PACKET_SETENTITYDATA: - packet = new SetEntityDataPacket(); - break; - case PACKET_SETENTITYMOTION: - packet = new SetEntityMotionPacket(); - break; - case PACKET_SETHEALTH: - packet = new SetHealthPacket(); - break; - case PACKET_SETSPAWNPOSITION: - packet = new SetSpawnPositionPacket(); - break; - case PACKET_ANIMATE: - packet = new AnimatePacket(); - break; - case PACKET_SENDINVENTORY: - packet = new SendInventoryPacket(); - break; - case PACKET_DROPITEM: - packet = new DropItemPacket(); - break; - case PACKET_CONTAINERACK: - packet = new ContainerAckPacket(); - break; - case PACKET_CONTAINEROPEN: - packet = new ContainerOpenPacket(); - break; - case PACKET_CONTAINERCLOSE: - packet = new ContainerClosePacket(); - break; - case PACKET_CONTAINERSETDATA: - packet = new ContainerSetDataPacket(); - break; - case PACKET_CONTAINERSETSLOT: - packet = new ContainerSetSlotPacket(); - break; - case PACKET_CONTAINERSETCONTENT: - packet = new ContainerSetContentPacket(); - break; - case PACKET_CHAT: - packet = new ChatPacket(); - break; - case PACKET_SIGNUPDATE: - packet = new SignUpdatePacket(); - break; - case PACKET_ADDPAINTING: - packet = new AddPaintingPacket(); - break; - case PACKET_ADVENTURESETTINGS: - packet = new AdventureSettingsPacket(); - break; - } - - return packet; -} - - -namespace PacketUtil -{ - signed char Rot_degreesToChar(float rot) { - return (signed char)(rot / 360.0f * 256.0f); - } - float Rot_charToDegrees(signed char rot) { - return ((float)rot) / 256.0f * 360.0f; - } - - void Rot_entityToChar(const Entity* e, signed char& yRot, signed char& xRot) { - xRot = Rot_degreesToChar(e->xRot); - yRot = Rot_degreesToChar(e->yRot); - } - - void Rot_charToEntity(Entity* e, signed char yRot, signed char xRot) { - e->xRot = e->xRotO = Rot_charToDegrees(xRot); - e->yRot = e->yRotO = Rot_charToDegrees(yRot); - } - - void writeItemInstance(const ItemInstance& item, RakNet::BitStream* stream) { - short id = item.id; - unsigned char count = item.count; - short aux = item.getAuxValue(); - stream->Write(id); - stream->Write(count); - stream->Write(aux); - } - - ItemInstance readItemInstance(RakNet::BitStream* stream) { - short id, aux; - unsigned char count; - stream->Read(id); - stream->Read(count); - stream->Read(aux); - return ItemInstance(id, count, aux); - } -} + +#include "Packet.h" +#include "../world/level/chunk/LevelChunk.h" + +#include "packet/PacketInclude.h" + +Packet::Packet() +: priority(HIGH_PRIORITY), + reliability(RELIABLE) +{} + +Packet* MinecraftPackets::createPacket(int id) +{ + Packet* packet = NULL; + + switch (id - ID_USER_PACKET_ENUM) { + default: + break; + + case PACKET_LOGIN: + packet = new LoginPacket(); + break; + case PACKET_LOGINSTATUS: + packet = new LoginStatusPacket(); + break; + case PACKET_READY: + packet = new ReadyPacket(); + break; + case PACKET_SETTIME: + packet = new SetTimePacket(); + break; + case PACKET_MESSAGE: + packet = new MessagePacket(); + break; + case PACKET_STARTGAME: + packet = new StartGamePacket(); + break; + case PACKET_ADDENTITY: + packet = new AddEntityPacket(); + break; + case PACKET_ADDITEMENTITY: + packet = new AddItemEntityPacket(); + break; + case PACKET_TAKEITEMENTITY: + packet = new TakeItemEntityPacket(); + break; + case PACKET_ADDMOB: + packet = new AddMobPacket(); + break; + case PACKET_ADDPLAYER: + packet = new AddPlayerPacket(); + break; + case PACKET_REMOVEPLAYER: + packet = new RemovePlayerPacket(); + break; + case PACKET_MOVEENTITY: + packet = new MoveEntityPacket(); + break; + case PACKET_MOVEENTITY_POSROT: + packet = new MoveEntityPacket_PosRot(); + break; + //case PACKET_TELEPORTENTITY: + // packet = new TeleportEntityPacket(); + // break; + case PACKET_MOVEPLAYER: + packet = new MovePlayerPacket(); + break; + case PACKET_RESPAWN: + packet = new RespawnPacket(); + break; + case PACKET_REMOVEENTITY: + packet = new RemoveEntityPacket(); + break; + case PACKET_PLACEBLOCK: + packet = new PlaceBlockPacket(); + break; + case PACKET_REMOVEBLOCK: + packet = new RemoveBlockPacket(); + break; + case PACKET_UPDATEBLOCK: + packet = new UpdateBlockPacket(); + break; + case PACKET_EXPLODE: + packet = new ExplodePacket(); + break; + case PACKET_LEVELEVENT: + packet = new LevelEventPacket(); + break; + case PACKET_TILEEVENT: + packet = new TileEventPacket(); + break; + case PACKET_ENTITYEVENT: + packet = new EntityEventPacket(); + break; + case PACKET_REQUESTCHUNK: + packet = new RequestChunkPacket(); + break; + case PACKET_CHUNKDATA: + packet = new ChunkDataPacket(); + break; + case PACKET_PLAYEREQUIPMENT: + packet = new PlayerEquipmentPacket(); + break; + case PACKET_PLAYERARMOREQUIPMENT: + packet = new PlayerArmorEquipmentPacket(); + break; + case PACKET_INTERACT: + packet = new InteractPacket(); + break; + case PACKET_USEITEM: + packet = new UseItemPacket(); + break; + case PACKET_PLAYERACTION: + packet = new PlayerActionPacket(); + break; + case PACKET_HURTARMOR: + packet = new HurtArmorPacket(); + break; + case PACKET_SETENTITYDATA: + packet = new SetEntityDataPacket(); + break; + case PACKET_SETENTITYMOTION: + packet = new SetEntityMotionPacket(); + break; + case PACKET_SETHEALTH: + packet = new SetHealthPacket(); + break; + case PACKET_SETSPAWNPOSITION: + packet = new SetSpawnPositionPacket(); + break; + case PACKET_ANIMATE: + packet = new AnimatePacket(); + break; + case PACKET_SENDINVENTORY: + packet = new SendInventoryPacket(); + break; + case PACKET_DROPITEM: + packet = new DropItemPacket(); + break; + case PACKET_CONTAINERACK: + packet = new ContainerAckPacket(); + break; + case PACKET_CONTAINEROPEN: + packet = new ContainerOpenPacket(); + break; + case PACKET_CONTAINERCLOSE: + packet = new ContainerClosePacket(); + break; + case PACKET_CONTAINERSETDATA: + packet = new ContainerSetDataPacket(); + break; + case PACKET_CONTAINERSETSLOT: + packet = new ContainerSetSlotPacket(); + break; + case PACKET_CONTAINERSETCONTENT: + packet = new ContainerSetContentPacket(); + break; + case PACKET_CHAT: + packet = new ChatPacket(); + break; + case PACKET_SIGNUPDATE: + packet = new SignUpdatePacket(); + break; + case PACKET_ADDPAINTING: + packet = new AddPaintingPacket(); + break; + case PACKET_ADVENTURESETTINGS: + packet = new AdventureSettingsPacket(); + break; + } + + return packet; +} + + +namespace PacketUtil +{ + signed char Rot_degreesToChar(float rot) { + return (signed char)(rot / 360.0f * 256.0f); + } + float Rot_charToDegrees(signed char rot) { + return ((float)rot) / 256.0f * 360.0f; + } + + void Rot_entityToChar(const Entity* e, signed char& yRot, signed char& xRot) { + xRot = Rot_degreesToChar(e->xRot); + yRot = Rot_degreesToChar(e->yRot); + } + + void Rot_charToEntity(Entity* e, signed char yRot, signed char xRot) { + e->xRot = e->xRotO = Rot_charToDegrees(xRot); + e->yRot = e->yRotO = Rot_charToDegrees(yRot); + } + + void writeItemInstance(const ItemInstance& item, RakNet::BitStream* stream) { + short id = item.id; + unsigned char count = item.count; + short aux = item.getAuxValue(); + stream->Write(id); + stream->Write(count); + stream->Write(aux); + } + + ItemInstance readItemInstance(RakNet::BitStream* stream) { + short id, aux; + unsigned char count; + stream->Read(id); + stream->Read(count); + stream->Read(aux); + return ItemInstance(id, count, aux); + } +} diff --git a/src/network/Packet.h b/src/network/Packet.h index e872219..1da1174 100755 --- a/src/network/Packet.h +++ b/src/network/Packet.h @@ -1,173 +1,173 @@ -#ifndef _MINECRAFT_NETWORK_PACKET_H_ -#define _MINECRAFT_NETWORK_PACKET_H_ - -#include "NetEventCallback.h" - -#include "../raknet/MessageIdentifiers.h" -#include "../raknet/RakNetTypes.h" -#include "../raknet/BitStream.h" -#include "../raknet/PacketPriority.h" - -class LevelChunk; - - -enum MinecraftPacketIds -{ - PACKET_KEEPALIVE = 0, - - PACKET_LOGIN, - PACKET_LOGINSTATUS, - PACKET_READY, - - PACKET_MESSAGE, - PACKET_SETTIME, - - PACKET_STARTGAME, - - PACKET_ADDMOB, - PACKET_ADDPLAYER, - PACKET_REMOVEPLAYER, - PACKET_TELEPORTENTITY, - - PACKET_ADDENTITY, - PACKET_REMOVEENTITY, - PACKET_ADDITEMENTITY, - PACKET_TAKEITEMENTITY, - - PACKET_MOVEENTITY, - PACKET_MOVEENTITY_POS, - PACKET_MOVEENTITY_ROT, - PACKET_MOVEENTITY_POSROT, - PACKET_MOVEPLAYER, - - PACKET_PLACEBLOCK, - PACKET_REMOVEBLOCK, - PACKET_UPDATEBLOCK, - - PACKET_ADDPAINTING, - - PACKET_EXPLODE, - - PACKET_LEVELEVENT, - PACKET_TILEEVENT, - PACKET_ENTITYEVENT, - - PACKET_REQUESTCHUNK, - PACKET_CHUNKDATA, - - PACKET_PLAYEREQUIPMENT, - PACKET_PLAYERARMOREQUIPMENT, - PACKET_INTERACT, - PACKET_USEITEM, - PACKET_PLAYERACTION, - PACKET_UPDATEARMOR, - PACKET_HURTARMOR, - - PACKET_SETENTITYDATA, - PACKET_SETENTITYMOTION, - PACKET_SETHEALTH, - PACKET_SETSPAWNPOSITION, - - PACKET_ANIMATE, - PACKET_RESPAWN, - - PACKET_SENDINVENTORY, - PACKET_DROPITEM, - - PACKET_CONTAINEROPEN, - PACKET_CONTAINERCLOSE, - PACKET_CONTAINERSETSLOT, - PACKET_CONTAINERSETDATA, - PACKET_CONTAINERSETCONTENT, - PACKET_CONTAINERACK, - - PACKET_CHAT, - PACKET_SIGNUPDATE, - - PACKET_ADVENTURESETTINGS, -/* - PACKET_PRELOGIN, - PACKET_CHAT, - PACKET_SETEQUIPPEDITEM, - PACKET_SETSPAWNPOSITION, - - PACKET_PLAYERACTION, - PACKET_SETCARRIEDITEM, - PACKET_ENTITYACTIONATPOSITION, - PACKET_PLAYERCOMMAND, - - PACKET_ADDENTITY, - PACKET_ADDMOB, - PACKET_ADDPAINTING, - PACKET_PLAYERINPUT, - PACKET_SETENTITYMOTION, - PACKET_REMOVEENTITY, - - PACKET_MOVEENTITY, - PACKET_SETRIDING, - PACKET_ENTITYDATA, - - PACKET_CHUNKVISIBILITY, - PACKET_BLOCKREGIONUPDATE, - PACKET_CHUNKBLOCKUPDATE, - PACKET_BLOCKUPDATE, - PACKET_BLOCKEVENT, - - PACKET_GAMEEVENT, - PACKET_ADDGLOBALENTITY, - - PACKET_CONTAINEROPEN, - PACKET_CONTAINERCLOSE, - PACKET_CONTAINERCLICK, - PACKET_CONTAINERSETSLOT, - PACKET_CONTAINERSETCONTENT, - PACKET_CONTAINERSETDATA, - PACKET_CONTAINERACK, - - PACKET_SIGNUPDATE, - PACKET_AWARDSTAT, - - PACKET_DISCONNECT, - */ - - PACKET_END_ID -}; - - -const int NUM_PACKETS = PACKET_END_ID; - -class Packet -{ -public: - Packet(); - virtual ~Packet() {} - - virtual void write(RakNet::BitStream* bitStream) = 0; - virtual void read(RakNet::BitStream* bitStream) = 0; - - virtual void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) = 0; - - PacketPriority priority; - PacketReliability reliability; -}; - - -class MinecraftPackets -{ -public: - - static Packet* createPacket(int id); - -}; - -namespace PacketUtil { - signed char Rot_degreesToChar(float rot); - float Rot_charToDegrees(signed char rot); - void Rot_entityToChar(const Entity* e, signed char& yRot, signed char& xRot); - void Rot_charToEntity(Entity* e, signed char yRot, signed char xRot); - - void writeItemInstance(const ItemInstance& item, RakNet::BitStream* stream); - ItemInstance readItemInstance(RakNet::BitStream* stream); -} - -#endif +#ifndef _MINECRAFT_NETWORK_PACKET_H_ +#define _MINECRAFT_NETWORK_PACKET_H_ + +#include "NetEventCallback.h" + +#include "../raknet/MessageIdentifiers.h" +#include "../raknet/RakNetTypes.h" +#include "../raknet/BitStream.h" +#include "../raknet/PacketPriority.h" + +class LevelChunk; + + +enum MinecraftPacketIds +{ + PACKET_KEEPALIVE = 0, + + PACKET_LOGIN, + PACKET_LOGINSTATUS, + PACKET_READY, + + PACKET_MESSAGE, + PACKET_SETTIME, + + PACKET_STARTGAME, + + PACKET_ADDMOB, + PACKET_ADDPLAYER, + PACKET_REMOVEPLAYER, + PACKET_TELEPORTENTITY, + + PACKET_ADDENTITY, + PACKET_REMOVEENTITY, + PACKET_ADDITEMENTITY, + PACKET_TAKEITEMENTITY, + + PACKET_MOVEENTITY, + PACKET_MOVEENTITY_POS, + PACKET_MOVEENTITY_ROT, + PACKET_MOVEENTITY_POSROT, + PACKET_MOVEPLAYER, + + PACKET_PLACEBLOCK, + PACKET_REMOVEBLOCK, + PACKET_UPDATEBLOCK, + + PACKET_ADDPAINTING, + + PACKET_EXPLODE, + + PACKET_LEVELEVENT, + PACKET_TILEEVENT, + PACKET_ENTITYEVENT, + + PACKET_REQUESTCHUNK, + PACKET_CHUNKDATA, + + PACKET_PLAYEREQUIPMENT, + PACKET_PLAYERARMOREQUIPMENT, + PACKET_INTERACT, + PACKET_USEITEM, + PACKET_PLAYERACTION, + PACKET_UPDATEARMOR, + PACKET_HURTARMOR, + + PACKET_SETENTITYDATA, + PACKET_SETENTITYMOTION, + PACKET_SETHEALTH, + PACKET_SETSPAWNPOSITION, + + PACKET_ANIMATE, + PACKET_RESPAWN, + + PACKET_SENDINVENTORY, + PACKET_DROPITEM, + + PACKET_CONTAINEROPEN, + PACKET_CONTAINERCLOSE, + PACKET_CONTAINERSETSLOT, + PACKET_CONTAINERSETDATA, + PACKET_CONTAINERSETCONTENT, + PACKET_CONTAINERACK, + + PACKET_CHAT, + PACKET_SIGNUPDATE, + + PACKET_ADVENTURESETTINGS, +/* + PACKET_PRELOGIN, + PACKET_CHAT, + PACKET_SETEQUIPPEDITEM, + PACKET_SETSPAWNPOSITION, + + PACKET_PLAYERACTION, + PACKET_SETCARRIEDITEM, + PACKET_ENTITYACTIONATPOSITION, + PACKET_PLAYERCOMMAND, + + PACKET_ADDENTITY, + PACKET_ADDMOB, + PACKET_ADDPAINTING, + PACKET_PLAYERINPUT, + PACKET_SETENTITYMOTION, + PACKET_REMOVEENTITY, + + PACKET_MOVEENTITY, + PACKET_SETRIDING, + PACKET_ENTITYDATA, + + PACKET_CHUNKVISIBILITY, + PACKET_BLOCKREGIONUPDATE, + PACKET_CHUNKBLOCKUPDATE, + PACKET_BLOCKUPDATE, + PACKET_BLOCKEVENT, + + PACKET_GAMEEVENT, + PACKET_ADDGLOBALENTITY, + + PACKET_CONTAINEROPEN, + PACKET_CONTAINERCLOSE, + PACKET_CONTAINERCLICK, + PACKET_CONTAINERSETSLOT, + PACKET_CONTAINERSETCONTENT, + PACKET_CONTAINERSETDATA, + PACKET_CONTAINERACK, + + PACKET_SIGNUPDATE, + PACKET_AWARDSTAT, + + PACKET_DISCONNECT, + */ + + PACKET_END_ID +}; + + +const int NUM_PACKETS = PACKET_END_ID; + +class Packet +{ +public: + Packet(); + virtual ~Packet() {} + + virtual void write(RakNet::BitStream* bitStream) = 0; + virtual void read(RakNet::BitStream* bitStream) = 0; + + virtual void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) = 0; + + PacketPriority priority; + PacketReliability reliability; +}; + + +class MinecraftPackets +{ +public: + + static Packet* createPacket(int id); + +}; + +namespace PacketUtil { + signed char Rot_degreesToChar(float rot); + float Rot_charToDegrees(signed char rot); + void Rot_entityToChar(const Entity* e, signed char& yRot, signed char& xRot); + void Rot_charToEntity(Entity* e, signed char yRot, signed char xRot); + + void writeItemInstance(const ItemInstance& item, RakNet::BitStream* stream); + ItemInstance readItemInstance(RakNet::BitStream* stream); +} + +#endif diff --git a/src/network/RakNetInstance.cpp b/src/network/RakNetInstance.cpp index 75a2814..4730672 100755 --- a/src/network/RakNetInstance.cpp +++ b/src/network/RakNetInstance.cpp @@ -1,740 +1,740 @@ -#include "RakNetInstance.h" -#include "Packet.h" -#include "NetEventCallback.h" -#include "../raknet/RakPeerInterface.h" -#include "../raknet/BitStream.h" -#include "../raknet/MessageIdentifiers.h" -#include "../raknet/GetTime.h" -#include "../AppConstants.h" - -#include "../platform/log.h" - -#define APP_IDENTIFIER "MCCPP;" APP_VERSION_STRING ";" -#define APP_IDENTIFIER_MINECON "MCCPP;MINECON;" - -RakNetInstance::RakNetInstance() -: rakPeer(NULL), - _isServer(false), - _isLoggedIn(false) -{ - rakPeer = RakNet::RakPeerInterface::GetInstance(); - rakPeer->SetTimeoutTime(20000, RakNet::UNASSIGNED_SYSTEM_ADDRESS); - rakPeer->SetOccasionalPing(true); -} - -RakNetInstance::~RakNetInstance() -{ - if (rakPeer) - { - rakPeer->Shutdown(100, 0); - RakNet::RakPeerInterface::DestroyInstance(rakPeer); - rakPeer = NULL; - } -} - -bool RakNetInstance::host(const std::string& localName, int port, int maxConnections /* = 4 */) -{ - if (rakPeer->IsActive()) - { - rakPeer->Shutdown(500); - } - - RakNet::SocketDescriptor socket(port, 0); - socket.socketFamily = AF_INET; - - rakPeer->SetMaximumIncomingConnections(maxConnections); - RakNet::StartupResult result = rakPeer->Startup(maxConnections, &socket, 1); - - _isServer = true; - isPingingForServers = false; - - return (result == RakNet::RAKNET_STARTED); -} - -void RakNetInstance::announceServer(const std::string& localName) -{ - if (_isServer && rakPeer->IsActive()) - { - RakNet::RakString connectionData; - -#if defined(MINECON) - connectionData += APP_IDENTIFIER_MINECON; -#else - connectionData += APP_IDENTIFIER; -#endif - connectionData += localName.c_str(); - - RakNet::BitStream bitStream; - bitStream.Write(connectionData); - rakPeer->SetOfflinePingResponse((const char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); - } -} - -bool RakNetInstance::connect(const char* host, int port) -{ - _isLoggedIn = false; - RakNet::StartupResult result; - - RakNet::SocketDescriptor socket(0, 0); - socket.socketFamily = AF_INET; - - if (rakPeer->IsActive()) - { - rakPeer->Shutdown(500); - } - - result = rakPeer->Startup(4, &socket, 1); - - _isServer = false; - isPingingForServers = false; - - if (result == RakNet::RAKNET_STARTED) - { - RakNet::ConnectionAttemptResult connectResult = rakPeer->Connect(host, port, NULL, 0, NULL, 0, 12, 500, 0); - - return (connectResult == RakNet::CONNECTION_ATTEMPT_STARTED); - } - - return false; -} - -void RakNetInstance::disconnect() -{ - if (rakPeer->IsActive()) - { - rakPeer->Shutdown(500); - } - _isLoggedIn = false; - _isServer = false; - isPingingForServers = false; -} - -void RakNetInstance::pingForHosts(int basePort) -{ - if (!rakPeer->IsActive()) - { - RakNet::SocketDescriptor socket(0, 0); - rakPeer->Startup(4, &socket, 1); - } - - isPingingForServers = true; - pingPort = basePort; - lastPingTime = RakNet::GetTimeMS(); - - for (int i = 0; i < 4; ++i) - rakPeer->Ping("255.255.255.255", basePort + i, true); -} - -void RakNetInstance::stopPingForHosts() -{ - if (isPingingForServers) - { - rakPeer->Shutdown(0); - isPingingForServers = false; - } -} - -const ServerList& RakNetInstance::getServerList() -{ - return availableServers; -} - -void RakNetInstance::clearServerList() -{ - availableServers.clear(); - /* - for (int i = 0; i < 20; ++i) { - PingedCompatibleServer ps; - ps.isSpecial = false; - ps.name = "Fake-Real"; - ps.address.FromString("192.168.1.236|19132"); - availableServers.push_back(ps); - } - */ -} - -RakNet::RakPeerInterface* RakNetInstance::getPeer() -{ - return rakPeer; -} - -bool RakNetInstance::isProbablyBroken() { - return rakPeer->errorState < -100; -} -void RakNetInstance::resetIsBroken() { - rakPeer->errorState = 0; -} - -bool RakNetInstance::isMyLocalGuid(const RakNet::RakNetGUID& guid) -{ - return rakPeer->IsActive() && rakPeer->GetMyGUID() == guid; -} - -void RakNetInstance::runEvents(NetEventCallback* callback) -{ - RakNet::Packet* currentEvent; - - while ((currentEvent = rakPeer->Receive()) != NULL) - { - int packetId = currentEvent->data[0]; - int length = currentEvent->length; - - RakNet::BitStream activeBitStream(currentEvent->data + 1, length - 1, false); - - if (callback) { - if (packetId < ID_USER_PACKET_ENUM) - { - //LOGI("Received event: %s\n", getPacketName(packetId)); - switch (packetId) - { - case ID_NEW_INCOMING_CONNECTION: - callback->onNewClient(currentEvent->guid); - break; - case ID_CONNECTION_REQUEST_ACCEPTED: - serverGuid = currentEvent->guid; - callback->onConnect(currentEvent->guid); - break; - case ID_CONNECTION_ATTEMPT_FAILED: - callback->onUnableToConnect(); - break; - case ID_DISCONNECTION_NOTIFICATION: - case ID_CONNECTION_LOST: - callback->onDisconnect(currentEvent->guid); - break; - case ID_UNCONNECTED_PONG: - { - RakNet::TimeMS time; - RakNet::RakString data; - activeBitStream.Read(time); - activeBitStream.Read(data); - - int index = handleUnconnectedPong(data, currentEvent, APP_IDENTIFIER, false); - if (index < 0) { - // Check if it's an official Mojang MineCon server - index = handleUnconnectedPong(data, currentEvent, APP_IDENTIFIER_MINECON, true); - if (index >= 0) availableServers[index].isSpecial = true; - } - } - break; - } - } - else - { - int userPacketId = packetId - ID_USER_PACKET_ENUM; - bool isStatusPacket = userPacketId <= PACKET_READY; - - if (isStatusPacket || _isServer || _isLoggedIn) { - - if (Packet* packet = MinecraftPackets::createPacket(packetId)) { - packet->read(&activeBitStream); - packet->handle(currentEvent->guid, callback); - delete packet; - } - } - } - } - - rakPeer->DeallocatePacket(currentEvent); - //delete activeBitStream; - } - - if (isPingingForServers) - { - if (RakNet::GetTimeMS() - lastPingTime > 1000) - { - // remove servers that hasn't responded for a while - ServerList::iterator it = availableServers.begin(); - for (; it != availableServers.end(); ) - { - if (RakNet::GetTimeMS() - it->pingTime > 3000) - { - it = availableServers.erase(it); - } - else - { - ++it; - } - } - - pingForHosts(pingPort); - } - } - -} - -void RakNetInstance::send(Packet& packet) { - RakNet::BitStream bitStream; - packet.write(&bitStream); - if (_isServer) - { - // broadcast to all connected clients - rakPeer->Send(&bitStream, packet.priority, packet.reliability, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true); - } - else - { - // send to server - rakPeer->Send(&bitStream, packet.priority, packet.reliability, 0, serverGuid, false); - } -} - -void RakNetInstance::send(const RakNet::RakNetGUID& guid, Packet& packet) { - RakNet::BitStream bitStream; - packet.write(&bitStream); - rakPeer->Send(&bitStream, packet.priority, packet.reliability, 0, guid, false); -} - - -void RakNetInstance::send(Packet* packet) -{ - send(*packet); - delete packet; -} - -void RakNetInstance::send(const RakNet::RakNetGUID& guid, Packet* packet) -{ - send(guid, *packet); - delete packet; -} - -#ifdef _DEBUG -const char* RakNetInstance::getPacketName(int packetId) -{ - - switch (packetId) - { - case ID_CONNECTED_PING: - return "ID_CONNECTED_PING"; - - case ID_UNCONNECTED_PING: - return "ID_UNCONNECTED_PING"; - - case ID_UNCONNECTED_PING_OPEN_CONNECTIONS: - return "ID_UNCONNECTED_PING_OPEN_CONNECTIONS"; - - case ID_CONNECTED_PONG: - return "ID_CONNECTED_PONG"; - - case ID_DETECT_LOST_CONNECTIONS: - return "ID_DETECT_LOST_CONNECTIONS"; - - case ID_OPEN_CONNECTION_REQUEST_1: - return "ID_OPEN_CONNECTION_REQUEST_1"; - - case ID_OPEN_CONNECTION_REPLY_1: - return "ID_OPEN_CONNECTION_REPLY_1"; - - case ID_OPEN_CONNECTION_REQUEST_2: - return "ID_OPEN_CONNECTION_REQUEST_2"; - - case ID_OPEN_CONNECTION_REPLY_2: - return "ID_OPEN_CONNECTION_REPLY_2"; - - case ID_CONNECTION_REQUEST: - return "ID_CONNECTION_REQUEST"; - - case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: - return "ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY"; - - case ID_OUR_SYSTEM_REQUIRES_SECURITY: - return "ID_OUR_SYSTEM_REQUIRES_SECURITY"; - - case ID_PUBLIC_KEY_MISMATCH: - return "ID_PUBLIC_KEY_MISMATCH"; - - case ID_OUT_OF_BAND_INTERNAL: - return "ID_OUT_OF_BAND_INTERNAL"; - - case ID_SND_RECEIPT_ACKED: - return "ID_SND_RECEIPT_ACKED"; - - case ID_SND_RECEIPT_LOSS: - return "ID_SND_RECEIPT_LOSS"; - - case ID_CONNECTION_REQUEST_ACCEPTED: - return "ID_CONNECTION_REQUEST_ACCEPTED"; - - case ID_CONNECTION_ATTEMPT_FAILED: - return "ID_CONNECTION_ATTEMPT_FAILED"; - - case ID_ALREADY_CONNECTED: - return "ID_ALREADY_CONNECTED"; - - case ID_NEW_INCOMING_CONNECTION: - return "ID_NEW_INCOMING_CONNECTION"; - - case ID_NO_FREE_INCOMING_CONNECTIONS: - return "ID_NO_FREE_INCOMING_CONNECTIONS"; - - case ID_DISCONNECTION_NOTIFICATION: - return "ID_DISCONNECTION_NOTIFICATION"; - - case ID_CONNECTION_LOST: - return "ID_CONNECTION_LOST"; - - case ID_CONNECTION_BANNED: - return "ID_CONNECTION_BANNED"; - - case ID_INVALID_PASSWORD: - return "ID_INVALID_PASSWORD"; - - case ID_INCOMPATIBLE_PROTOCOL_VERSION: - return "ID_INCOMPATIBLE_PROTOCOL_VERSION"; - - case ID_IP_RECENTLY_CONNECTED: - return "ID_IP_RECENTLY_CONNECTED"; - - case ID_TIMESTAMP: - return "ID_TIMESTAMP"; - - case ID_UNCONNECTED_PONG: - return "ID_UNCONNECTED_PONG"; - - case ID_ADVERTISE_SYSTEM: - return "ID_ADVERTISE_SYSTEM"; - - case ID_DOWNLOAD_PROGRESS: - return "ID_DOWNLOAD_PROGRESS"; - - case ID_REMOTE_DISCONNECTION_NOTIFICATION: - return "ID_REMOTE_DISCONNECTION_NOTIFICATION"; - - case ID_REMOTE_CONNECTION_LOST: - return "ID_REMOTE_CONNECTION_LOST"; - - case ID_REMOTE_NEW_INCOMING_CONNECTION: - return "ID_REMOTE_NEW_INCOMING_CONNECTION"; - - case ID_FILE_LIST_TRANSFER_HEADER: - return "ID_FILE_LIST_TRANSFER_HEADER"; - - case ID_FILE_LIST_TRANSFER_FILE: - return "ID_FILE_LIST_TRANSFER_FILE"; - - case ID_FILE_LIST_REFERENCE_PUSH_ACK: - return "ID_FILE_LIST_REFERENCE_PUSH_ACK"; - - case ID_DDT_DOWNLOAD_REQUEST: - return "ID_DDT_DOWNLOAD_REQUEST"; - - case ID_TRANSPORT_STRING: - return "ID_TRANSPORT_STRING"; - - case ID_REPLICA_MANAGER_CONSTRUCTION: - return "ID_REPLICA_MANAGER_CONSTRUCTION"; - - case ID_REPLICA_MANAGER_SCOPE_CHANGE: - return "ID_REPLICA_MANAGER_SCOPE_CHANGE"; - - case ID_REPLICA_MANAGER_SERIALIZE: - return "ID_REPLICA_MANAGER_SERIALIZE"; - - case ID_REPLICA_MANAGER_DOWNLOAD_STARTED: - return "ID_REPLICA_MANAGER_DOWNLOAD_STARTED"; - - case ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE: - return "ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE"; - - case ID_RAKVOICE_OPEN_CHANNEL_REQUEST: - return "ID_RAKVOICE_OPEN_CHANNEL_REQUEST"; - - case ID_RAKVOICE_OPEN_CHANNEL_REPLY: - return "ID_RAKVOICE_OPEN_CHANNEL_REPLY"; - - case ID_RAKVOICE_CLOSE_CHANNEL: - return "ID_RAKVOICE_CLOSE_CHANNEL"; - - case ID_RAKVOICE_DATA: - return "ID_RAKVOICE_DATA"; - - case ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE: - return "ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE"; - - case ID_AUTOPATCHER_CREATION_LIST: - return "ID_AUTOPATCHER_CREATION_LIST"; - - case ID_AUTOPATCHER_DELETION_LIST: - return "ID_AUTOPATCHER_DELETION_LIST"; - - case ID_AUTOPATCHER_GET_PATCH: - return "ID_AUTOPATCHER_GET_PATCH"; - - case ID_AUTOPATCHER_PATCH_LIST: - return "ID_AUTOPATCHER_PATCH_LIST"; - - case ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR: - return "ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR"; - - case ID_AUTOPATCHER_FINISHED_INTERNAL: - return "ID_AUTOPATCHER_FINISHED_INTERNAL"; - - case ID_AUTOPATCHER_FINISHED: - return "ID_AUTOPATCHER_FINISHED"; - - case ID_AUTOPATCHER_RESTART_APPLICATION: - return "ID_AUTOPATCHER_RESTART_APPLICATION"; - - case ID_NAT_PUNCHTHROUGH_REQUEST: - return "ID_NAT_PUNCHTHROUGH_REQUEST"; - - case ID_NAT_GROUP_PUNCHTHROUGH_REQUEST: - return "ID_NAT_GROUP_PUNCHTHROUGH_REQUEST"; - - case ID_NAT_GROUP_PUNCHTHROUGH_REPLY: - return "ID_NAT_GROUP_PUNCHTHROUGH_REPLY"; - - case ID_NAT_CONNECT_AT_TIME: - return "ID_NAT_CONNECT_AT_TIME"; - - case ID_NAT_GET_MOST_RECENT_PORT: - return "ID_NAT_GET_MOST_RECENT_PORT"; - - case ID_NAT_CLIENT_READY: - return "ID_NAT_CLIENT_READY"; - - case ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION: - return "ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION"; - - case ID_NAT_TARGET_NOT_CONNECTED: - return "ID_NAT_TARGET_NOT_CONNECTED"; - - case ID_NAT_TARGET_UNRESPONSIVE: - return "ID_NAT_TARGET_UNRESPONSIVE"; - - case ID_NAT_CONNECTION_TO_TARGET_LOST: - return "ID_NAT_CONNECTION_TO_TARGET_LOST"; - - case ID_NAT_ALREADY_IN_PROGRESS: - return "ID_NAT_ALREADY_IN_PROGRESS"; - - case ID_NAT_PUNCHTHROUGH_FAILED: - return "ID_NAT_PUNCHTHROUGH_FAILED"; - - case ID_NAT_PUNCHTHROUGH_SUCCEEDED: - return "ID_NAT_PUNCHTHROUGH_SUCCEEDED"; - - case ID_NAT_GROUP_PUNCH_FAILED: - return "ID_NAT_GROUP_PUNCH_FAILED"; - - case ID_NAT_GROUP_PUNCH_SUCCEEDED: - return "ID_NAT_GROUP_PUNCH_SUCCEEDED"; - - case ID_READY_EVENT_SET: - return "ID_READY_EVENT_SET"; - - case ID_READY_EVENT_UNSET: - return "ID_READY_EVENT_UNSET"; - - case ID_READY_EVENT_ALL_SET: - return "ID_READY_EVENT_ALL_SET"; - - case ID_READY_EVENT_QUERY: - return "ID_READY_EVENT_QUERY"; - - case ID_LOBBY_GENERAL: - return "ID_LOBBY_GENERAL"; - - case ID_RPC_REMOTE_ERROR: - return "ID_RPC_REMOTE_ERROR"; - - case ID_RPC_PLUGIN: - return "ID_RPC_PLUGIN"; - - case ID_FILE_LIST_REFERENCE_PUSH: - return "ID_FILE_LIST_REFERENCE_PUSH"; - - case ID_READY_EVENT_FORCE_ALL_SET: - return "ID_READY_EVENT_FORCE_ALL_SET"; - - case ID_ROOMS_EXECUTE_FUNC: - return "ID_ROOMS_EXECUTE_FUNC"; - - case ID_ROOMS_LOGON_STATUS: - return "ID_ROOMS_LOGON_STATUS"; - - case ID_ROOMS_HANDLE_CHANGE: - return "ID_ROOMS_HANDLE_CHANGE"; - - case ID_LOBBY2_SEND_MESSAGE: - return "ID_LOBBY2_SEND_MESSAGE"; - - case ID_LOBBY2_SERVER_ERROR: - return "ID_LOBBY2_SERVER_ERROR"; - - case ID_FCM2_NEW_HOST: - return "ID_FCM2_NEW_HOST"; - - case ID_FCM2_REQUEST_FCMGUID: - return "ID_FCM2_REQUEST_FCMGUID"; - - case ID_FCM2_RESPOND_CONNECTION_COUNT: - return "ID_FCM2_RESPOND_CONNECTION_COUNT"; - - case ID_FCM2_INFORM_FCMGUID: - return "ID_FCM2_INFORM_FCMGUID"; - - case ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT: - return "ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT"; - - case ID_UDP_PROXY_GENERAL: - return "ID_UDP_PROXY_GENERAL"; - - case ID_SQLite3_EXEC: - return "ID_SQLite3_EXEC"; - - case ID_SQLite3_UNKNOWN_DB: - return "ID_SQLite3_UNKNOWN_DB"; - - case ID_SQLLITE_LOGGER: - return "ID_SQLLITE_LOGGER"; - - case ID_NAT_TYPE_DETECTION_REQUEST: - return "ID_NAT_TYPE_DETECTION_REQUEST"; - - case ID_NAT_TYPE_DETECTION_RESULT: - return "ID_NAT_TYPE_DETECTION_RESULT"; - - case ID_ROUTER_2_INTERNAL: - return "ID_ROUTER_2_INTERNAL"; - - case ID_ROUTER_2_FORWARDING_NO_PATH: - return "ID_ROUTER_2_FORWARDING_NO_PATH"; - - case ID_ROUTER_2_FORWARDING_ESTABLISHED: - return "ID_ROUTER_2_FORWARDING_ESTABLISHED"; - - case ID_ROUTER_2_REROUTED: - return "ID_ROUTER_2_REROUTED"; - - case ID_TEAM_BALANCER_INTERNAL: - return "ID_TEAM_BALANCER_INTERNAL"; - - case ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING: - return "ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING"; - - case ID_TEAM_BALANCER_TEAMS_LOCKED: - return "ID_TEAM_BALANCER_TEAMS_LOCKED"; - - case ID_TEAM_BALANCER_TEAM_ASSIGNED: - return "ID_TEAM_BALANCER_TEAM_ASSIGNED"; - - case ID_LIGHTSPEED_INTEGRATION: - return "ID_LIGHTSPEED_INTEGRATION"; - - case ID_XBOX_LOBBY: - return "ID_XBOX_LOBBY"; - - case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS: - return "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS"; - - case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS: - return "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS"; - - case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE: - return "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE"; - - case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE: - return "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE"; - - case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT: - return "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT"; - - case ID_TWO_WAY_AUTHENTICATION_NEGOTIATION: - return "ID_TWO_WAY_AUTHENTICATION_NEGOTIATION"; - - case ID_CLOUD_POST_REQUEST: - return "ID_CLOUD_POST_REQUEST"; - - case ID_CLOUD_RELEASE_REQUEST: - return "ID_CLOUD_RELEASE_REQUEST"; - - case ID_CLOUD_GET_REQUEST: - return "ID_CLOUD_GET_REQUEST"; - - case ID_CLOUD_GET_RESPONSE: - return "ID_CLOUD_GET_RESPONSE"; - - case ID_CLOUD_UNSUBSCRIBE_REQUEST: - return "ID_CLOUD_UNSUBSCRIBE_REQUEST"; - - case ID_CLOUD_SERVER_TO_SERVER_COMMAND: - return "ID_CLOUD_SERVER_TO_SERVER_COMMAND"; - - case ID_CLOUD_SUBSCRIPTION_NOTIFICATION: - return "ID_CLOUD_SUBSCRIPTION_NOTIFICATION"; - - case ID_RESERVED_1: - return "ID_RESERVED_1"; - - case ID_RESERVED_2: - return "ID_RESERVED_2"; - - case ID_RESERVED_3: - return "ID_RESERVED_3"; - - case ID_RESERVED_4: - return "ID_RESERVED_4"; - - case ID_RESERVED_5: - return "ID_RESERVED_5"; - - case ID_RESERVED_6: - return "ID_RESERVED_6"; - - case ID_RESERVED_7: - return "ID_RESERVED_7"; - - case ID_RESERVED_8: - return "ID_RESERVED_8"; - - case ID_RESERVED_9: - return "ID_RESERVED_9"; - - default: - break; - } - return "Unknown or user-defined"; -} -#endif - -int RakNetInstance::handleUnconnectedPong(const RakNet::RakString& data, const RakNet::Packet* p, const char* appid, bool insertAtBeginning) -{ - RakNet::RakString appIdentifier(appid); - // This weird code is a result of RakString.Find being pretty useless - bool emptyNameOrLonger = data.GetLength() >= appIdentifier.GetLength(); - - if ( !emptyNameOrLonger || appIdentifier.StrCmp(data.SubStr(0, appIdentifier.GetLength())) != 0) - return -1; - - bool found = false; - for (unsigned int i = 0; i < availableServers.size(); i++) { - if (availableServers[i].address == p->systemAddress) { - availableServers[i].pingTime = RakNet::GetTimeMS(); - - bool emptyName = data.GetLength() == appIdentifier.GetLength(); - if (emptyName) - availableServers[i].name = ""; - else { - availableServers[i].name = data.SubStr(appIdentifier.GetLength(), data.GetLength() - appIdentifier.GetLength()); - } - //LOGI("Swapping name: %s\n", availableServers[i].name.C_String()); - return i; - } - } - PingedCompatibleServer server; - server.address = p->systemAddress; - server.pingTime = RakNet::GetTimeMS(); - server.isSpecial = false; - server.name = data.SubStr(appIdentifier.GetLength(), data.GetLength() - appIdentifier.GetLength()); - - if (insertAtBeginning) { - availableServers.insert(availableServers.begin(), server); - return 0; - } else { - availableServers.push_back(server); - return availableServers.size() - 1; - } -} - -void RakNetInstance::setIsLoggedIn( bool status ) { - _isLoggedIn = status; -} +#include "RakNetInstance.h" +#include "Packet.h" +#include "NetEventCallback.h" +#include "../raknet/RakPeerInterface.h" +#include "../raknet/BitStream.h" +#include "../raknet/MessageIdentifiers.h" +#include "../raknet/GetTime.h" +#include "../AppConstants.h" + +#include "../platform/log.h" + +#define APP_IDENTIFIER "MCCPP;" APP_VERSION_STRING ";" +#define APP_IDENTIFIER_MINECON "MCCPP;MINECON;" + +RakNetInstance::RakNetInstance() +: rakPeer(NULL), + _isServer(false), + _isLoggedIn(false) +{ + rakPeer = RakNet::RakPeerInterface::GetInstance(); + rakPeer->SetTimeoutTime(20000, RakNet::UNASSIGNED_SYSTEM_ADDRESS); + rakPeer->SetOccasionalPing(true); +} + +RakNetInstance::~RakNetInstance() +{ + if (rakPeer) + { + rakPeer->Shutdown(100, 0); + RakNet::RakPeerInterface::DestroyInstance(rakPeer); + rakPeer = NULL; + } +} + +bool RakNetInstance::host(const std::string& localName, int port, int maxConnections /* = 4 */) +{ + if (rakPeer->IsActive()) + { + rakPeer->Shutdown(500); + } + + RakNet::SocketDescriptor socket(port, 0); + socket.socketFamily = AF_INET; + + rakPeer->SetMaximumIncomingConnections(maxConnections); + RakNet::StartupResult result = rakPeer->Startup(maxConnections, &socket, 1); + + _isServer = true; + isPingingForServers = false; + + return (result == RakNet::RAKNET_STARTED); +} + +void RakNetInstance::announceServer(const std::string& localName) +{ + if (_isServer && rakPeer->IsActive()) + { + RakNet::RakString connectionData; + +#if defined(MINECON) + connectionData += APP_IDENTIFIER_MINECON; +#else + connectionData += APP_IDENTIFIER; +#endif + connectionData += localName.c_str(); + + RakNet::BitStream bitStream; + bitStream.Write(connectionData); + rakPeer->SetOfflinePingResponse((const char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); + } +} + +bool RakNetInstance::connect(const char* host, int port) +{ + _isLoggedIn = false; + RakNet::StartupResult result; + + RakNet::SocketDescriptor socket(0, 0); + socket.socketFamily = AF_INET; + + if (rakPeer->IsActive()) + { + rakPeer->Shutdown(500); + } + + result = rakPeer->Startup(4, &socket, 1); + + _isServer = false; + isPingingForServers = false; + + if (result == RakNet::RAKNET_STARTED) + { + RakNet::ConnectionAttemptResult connectResult = rakPeer->Connect(host, port, NULL, 0, NULL, 0, 12, 500, 0); + + return (connectResult == RakNet::CONNECTION_ATTEMPT_STARTED); + } + + return false; +} + +void RakNetInstance::disconnect() +{ + if (rakPeer->IsActive()) + { + rakPeer->Shutdown(500); + } + _isLoggedIn = false; + _isServer = false; + isPingingForServers = false; +} + +void RakNetInstance::pingForHosts(int basePort) +{ + if (!rakPeer->IsActive()) + { + RakNet::SocketDescriptor socket(0, 0); + rakPeer->Startup(4, &socket, 1); + } + + isPingingForServers = true; + pingPort = basePort; + lastPingTime = RakNet::GetTimeMS(); + + for (int i = 0; i < 4; ++i) + rakPeer->Ping("255.255.255.255", basePort + i, true); +} + +void RakNetInstance::stopPingForHosts() +{ + if (isPingingForServers) + { + rakPeer->Shutdown(0); + isPingingForServers = false; + } +} + +const ServerList& RakNetInstance::getServerList() +{ + return availableServers; +} + +void RakNetInstance::clearServerList() +{ + availableServers.clear(); + /* + for (int i = 0; i < 20; ++i) { + PingedCompatibleServer ps; + ps.isSpecial = false; + ps.name = "Fake-Real"; + ps.address.FromString("192.168.1.236|19132"); + availableServers.push_back(ps); + } + */ +} + +RakNet::RakPeerInterface* RakNetInstance::getPeer() +{ + return rakPeer; +} + +bool RakNetInstance::isProbablyBroken() { + return rakPeer->errorState < -100; +} +void RakNetInstance::resetIsBroken() { + rakPeer->errorState = 0; +} + +bool RakNetInstance::isMyLocalGuid(const RakNet::RakNetGUID& guid) +{ + return rakPeer->IsActive() && rakPeer->GetMyGUID() == guid; +} + +void RakNetInstance::runEvents(NetEventCallback* callback) +{ + RakNet::Packet* currentEvent; + + while ((currentEvent = rakPeer->Receive()) != NULL) + { + int packetId = currentEvent->data[0]; + int length = currentEvent->length; + + RakNet::BitStream activeBitStream(currentEvent->data + 1, length - 1, false); + + if (callback) { + if (packetId < ID_USER_PACKET_ENUM) + { + //LOGI("Received event: %s\n", getPacketName(packetId)); + switch (packetId) + { + case ID_NEW_INCOMING_CONNECTION: + callback->onNewClient(currentEvent->guid); + break; + case ID_CONNECTION_REQUEST_ACCEPTED: + serverGuid = currentEvent->guid; + callback->onConnect(currentEvent->guid); + break; + case ID_CONNECTION_ATTEMPT_FAILED: + callback->onUnableToConnect(); + break; + case ID_DISCONNECTION_NOTIFICATION: + case ID_CONNECTION_LOST: + callback->onDisconnect(currentEvent->guid); + break; + case ID_UNCONNECTED_PONG: + { + RakNet::TimeMS time; + RakNet::RakString data; + activeBitStream.Read(time); + activeBitStream.Read(data); + + int index = handleUnconnectedPong(data, currentEvent, APP_IDENTIFIER, false); + if (index < 0) { + // Check if it's an official Mojang MineCon server + index = handleUnconnectedPong(data, currentEvent, APP_IDENTIFIER_MINECON, true); + if (index >= 0) availableServers[index].isSpecial = true; + } + } + break; + } + } + else + { + int userPacketId = packetId - ID_USER_PACKET_ENUM; + bool isStatusPacket = userPacketId <= PACKET_READY; + + if (isStatusPacket || _isServer || _isLoggedIn) { + + if (Packet* packet = MinecraftPackets::createPacket(packetId)) { + packet->read(&activeBitStream); + packet->handle(currentEvent->guid, callback); + delete packet; + } + } + } + } + + rakPeer->DeallocatePacket(currentEvent); + //delete activeBitStream; + } + + if (isPingingForServers) + { + if (RakNet::GetTimeMS() - lastPingTime > 1000) + { + // remove servers that hasn't responded for a while + ServerList::iterator it = availableServers.begin(); + for (; it != availableServers.end(); ) + { + if (RakNet::GetTimeMS() - it->pingTime > 3000) + { + it = availableServers.erase(it); + } + else + { + ++it; + } + } + + pingForHosts(pingPort); + } + } + +} + +void RakNetInstance::send(Packet& packet) { + RakNet::BitStream bitStream; + packet.write(&bitStream); + if (_isServer) + { + // broadcast to all connected clients + rakPeer->Send(&bitStream, packet.priority, packet.reliability, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true); + } + else + { + // send to server + rakPeer->Send(&bitStream, packet.priority, packet.reliability, 0, serverGuid, false); + } +} + +void RakNetInstance::send(const RakNet::RakNetGUID& guid, Packet& packet) { + RakNet::BitStream bitStream; + packet.write(&bitStream); + rakPeer->Send(&bitStream, packet.priority, packet.reliability, 0, guid, false); +} + + +void RakNetInstance::send(Packet* packet) +{ + send(*packet); + delete packet; +} + +void RakNetInstance::send(const RakNet::RakNetGUID& guid, Packet* packet) +{ + send(guid, *packet); + delete packet; +} + +#ifdef _DEBUG +const char* RakNetInstance::getPacketName(int packetId) +{ + + switch (packetId) + { + case ID_CONNECTED_PING: + return "ID_CONNECTED_PING"; + + case ID_UNCONNECTED_PING: + return "ID_UNCONNECTED_PING"; + + case ID_UNCONNECTED_PING_OPEN_CONNECTIONS: + return "ID_UNCONNECTED_PING_OPEN_CONNECTIONS"; + + case ID_CONNECTED_PONG: + return "ID_CONNECTED_PONG"; + + case ID_DETECT_LOST_CONNECTIONS: + return "ID_DETECT_LOST_CONNECTIONS"; + + case ID_OPEN_CONNECTION_REQUEST_1: + return "ID_OPEN_CONNECTION_REQUEST_1"; + + case ID_OPEN_CONNECTION_REPLY_1: + return "ID_OPEN_CONNECTION_REPLY_1"; + + case ID_OPEN_CONNECTION_REQUEST_2: + return "ID_OPEN_CONNECTION_REQUEST_2"; + + case ID_OPEN_CONNECTION_REPLY_2: + return "ID_OPEN_CONNECTION_REPLY_2"; + + case ID_CONNECTION_REQUEST: + return "ID_CONNECTION_REQUEST"; + + case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: + return "ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY"; + + case ID_OUR_SYSTEM_REQUIRES_SECURITY: + return "ID_OUR_SYSTEM_REQUIRES_SECURITY"; + + case ID_PUBLIC_KEY_MISMATCH: + return "ID_PUBLIC_KEY_MISMATCH"; + + case ID_OUT_OF_BAND_INTERNAL: + return "ID_OUT_OF_BAND_INTERNAL"; + + case ID_SND_RECEIPT_ACKED: + return "ID_SND_RECEIPT_ACKED"; + + case ID_SND_RECEIPT_LOSS: + return "ID_SND_RECEIPT_LOSS"; + + case ID_CONNECTION_REQUEST_ACCEPTED: + return "ID_CONNECTION_REQUEST_ACCEPTED"; + + case ID_CONNECTION_ATTEMPT_FAILED: + return "ID_CONNECTION_ATTEMPT_FAILED"; + + case ID_ALREADY_CONNECTED: + return "ID_ALREADY_CONNECTED"; + + case ID_NEW_INCOMING_CONNECTION: + return "ID_NEW_INCOMING_CONNECTION"; + + case ID_NO_FREE_INCOMING_CONNECTIONS: + return "ID_NO_FREE_INCOMING_CONNECTIONS"; + + case ID_DISCONNECTION_NOTIFICATION: + return "ID_DISCONNECTION_NOTIFICATION"; + + case ID_CONNECTION_LOST: + return "ID_CONNECTION_LOST"; + + case ID_CONNECTION_BANNED: + return "ID_CONNECTION_BANNED"; + + case ID_INVALID_PASSWORD: + return "ID_INVALID_PASSWORD"; + + case ID_INCOMPATIBLE_PROTOCOL_VERSION: + return "ID_INCOMPATIBLE_PROTOCOL_VERSION"; + + case ID_IP_RECENTLY_CONNECTED: + return "ID_IP_RECENTLY_CONNECTED"; + + case ID_TIMESTAMP: + return "ID_TIMESTAMP"; + + case ID_UNCONNECTED_PONG: + return "ID_UNCONNECTED_PONG"; + + case ID_ADVERTISE_SYSTEM: + return "ID_ADVERTISE_SYSTEM"; + + case ID_DOWNLOAD_PROGRESS: + return "ID_DOWNLOAD_PROGRESS"; + + case ID_REMOTE_DISCONNECTION_NOTIFICATION: + return "ID_REMOTE_DISCONNECTION_NOTIFICATION"; + + case ID_REMOTE_CONNECTION_LOST: + return "ID_REMOTE_CONNECTION_LOST"; + + case ID_REMOTE_NEW_INCOMING_CONNECTION: + return "ID_REMOTE_NEW_INCOMING_CONNECTION"; + + case ID_FILE_LIST_TRANSFER_HEADER: + return "ID_FILE_LIST_TRANSFER_HEADER"; + + case ID_FILE_LIST_TRANSFER_FILE: + return "ID_FILE_LIST_TRANSFER_FILE"; + + case ID_FILE_LIST_REFERENCE_PUSH_ACK: + return "ID_FILE_LIST_REFERENCE_PUSH_ACK"; + + case ID_DDT_DOWNLOAD_REQUEST: + return "ID_DDT_DOWNLOAD_REQUEST"; + + case ID_TRANSPORT_STRING: + return "ID_TRANSPORT_STRING"; + + case ID_REPLICA_MANAGER_CONSTRUCTION: + return "ID_REPLICA_MANAGER_CONSTRUCTION"; + + case ID_REPLICA_MANAGER_SCOPE_CHANGE: + return "ID_REPLICA_MANAGER_SCOPE_CHANGE"; + + case ID_REPLICA_MANAGER_SERIALIZE: + return "ID_REPLICA_MANAGER_SERIALIZE"; + + case ID_REPLICA_MANAGER_DOWNLOAD_STARTED: + return "ID_REPLICA_MANAGER_DOWNLOAD_STARTED"; + + case ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE: + return "ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE"; + + case ID_RAKVOICE_OPEN_CHANNEL_REQUEST: + return "ID_RAKVOICE_OPEN_CHANNEL_REQUEST"; + + case ID_RAKVOICE_OPEN_CHANNEL_REPLY: + return "ID_RAKVOICE_OPEN_CHANNEL_REPLY"; + + case ID_RAKVOICE_CLOSE_CHANNEL: + return "ID_RAKVOICE_CLOSE_CHANNEL"; + + case ID_RAKVOICE_DATA: + return "ID_RAKVOICE_DATA"; + + case ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE: + return "ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE"; + + case ID_AUTOPATCHER_CREATION_LIST: + return "ID_AUTOPATCHER_CREATION_LIST"; + + case ID_AUTOPATCHER_DELETION_LIST: + return "ID_AUTOPATCHER_DELETION_LIST"; + + case ID_AUTOPATCHER_GET_PATCH: + return "ID_AUTOPATCHER_GET_PATCH"; + + case ID_AUTOPATCHER_PATCH_LIST: + return "ID_AUTOPATCHER_PATCH_LIST"; + + case ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR: + return "ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR"; + + case ID_AUTOPATCHER_FINISHED_INTERNAL: + return "ID_AUTOPATCHER_FINISHED_INTERNAL"; + + case ID_AUTOPATCHER_FINISHED: + return "ID_AUTOPATCHER_FINISHED"; + + case ID_AUTOPATCHER_RESTART_APPLICATION: + return "ID_AUTOPATCHER_RESTART_APPLICATION"; + + case ID_NAT_PUNCHTHROUGH_REQUEST: + return "ID_NAT_PUNCHTHROUGH_REQUEST"; + + case ID_NAT_GROUP_PUNCHTHROUGH_REQUEST: + return "ID_NAT_GROUP_PUNCHTHROUGH_REQUEST"; + + case ID_NAT_GROUP_PUNCHTHROUGH_REPLY: + return "ID_NAT_GROUP_PUNCHTHROUGH_REPLY"; + + case ID_NAT_CONNECT_AT_TIME: + return "ID_NAT_CONNECT_AT_TIME"; + + case ID_NAT_GET_MOST_RECENT_PORT: + return "ID_NAT_GET_MOST_RECENT_PORT"; + + case ID_NAT_CLIENT_READY: + return "ID_NAT_CLIENT_READY"; + + case ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION: + return "ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION"; + + case ID_NAT_TARGET_NOT_CONNECTED: + return "ID_NAT_TARGET_NOT_CONNECTED"; + + case ID_NAT_TARGET_UNRESPONSIVE: + return "ID_NAT_TARGET_UNRESPONSIVE"; + + case ID_NAT_CONNECTION_TO_TARGET_LOST: + return "ID_NAT_CONNECTION_TO_TARGET_LOST"; + + case ID_NAT_ALREADY_IN_PROGRESS: + return "ID_NAT_ALREADY_IN_PROGRESS"; + + case ID_NAT_PUNCHTHROUGH_FAILED: + return "ID_NAT_PUNCHTHROUGH_FAILED"; + + case ID_NAT_PUNCHTHROUGH_SUCCEEDED: + return "ID_NAT_PUNCHTHROUGH_SUCCEEDED"; + + case ID_NAT_GROUP_PUNCH_FAILED: + return "ID_NAT_GROUP_PUNCH_FAILED"; + + case ID_NAT_GROUP_PUNCH_SUCCEEDED: + return "ID_NAT_GROUP_PUNCH_SUCCEEDED"; + + case ID_READY_EVENT_SET: + return "ID_READY_EVENT_SET"; + + case ID_READY_EVENT_UNSET: + return "ID_READY_EVENT_UNSET"; + + case ID_READY_EVENT_ALL_SET: + return "ID_READY_EVENT_ALL_SET"; + + case ID_READY_EVENT_QUERY: + return "ID_READY_EVENT_QUERY"; + + case ID_LOBBY_GENERAL: + return "ID_LOBBY_GENERAL"; + + case ID_RPC_REMOTE_ERROR: + return "ID_RPC_REMOTE_ERROR"; + + case ID_RPC_PLUGIN: + return "ID_RPC_PLUGIN"; + + case ID_FILE_LIST_REFERENCE_PUSH: + return "ID_FILE_LIST_REFERENCE_PUSH"; + + case ID_READY_EVENT_FORCE_ALL_SET: + return "ID_READY_EVENT_FORCE_ALL_SET"; + + case ID_ROOMS_EXECUTE_FUNC: + return "ID_ROOMS_EXECUTE_FUNC"; + + case ID_ROOMS_LOGON_STATUS: + return "ID_ROOMS_LOGON_STATUS"; + + case ID_ROOMS_HANDLE_CHANGE: + return "ID_ROOMS_HANDLE_CHANGE"; + + case ID_LOBBY2_SEND_MESSAGE: + return "ID_LOBBY2_SEND_MESSAGE"; + + case ID_LOBBY2_SERVER_ERROR: + return "ID_LOBBY2_SERVER_ERROR"; + + case ID_FCM2_NEW_HOST: + return "ID_FCM2_NEW_HOST"; + + case ID_FCM2_REQUEST_FCMGUID: + return "ID_FCM2_REQUEST_FCMGUID"; + + case ID_FCM2_RESPOND_CONNECTION_COUNT: + return "ID_FCM2_RESPOND_CONNECTION_COUNT"; + + case ID_FCM2_INFORM_FCMGUID: + return "ID_FCM2_INFORM_FCMGUID"; + + case ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT: + return "ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT"; + + case ID_UDP_PROXY_GENERAL: + return "ID_UDP_PROXY_GENERAL"; + + case ID_SQLite3_EXEC: + return "ID_SQLite3_EXEC"; + + case ID_SQLite3_UNKNOWN_DB: + return "ID_SQLite3_UNKNOWN_DB"; + + case ID_SQLLITE_LOGGER: + return "ID_SQLLITE_LOGGER"; + + case ID_NAT_TYPE_DETECTION_REQUEST: + return "ID_NAT_TYPE_DETECTION_REQUEST"; + + case ID_NAT_TYPE_DETECTION_RESULT: + return "ID_NAT_TYPE_DETECTION_RESULT"; + + case ID_ROUTER_2_INTERNAL: + return "ID_ROUTER_2_INTERNAL"; + + case ID_ROUTER_2_FORWARDING_NO_PATH: + return "ID_ROUTER_2_FORWARDING_NO_PATH"; + + case ID_ROUTER_2_FORWARDING_ESTABLISHED: + return "ID_ROUTER_2_FORWARDING_ESTABLISHED"; + + case ID_ROUTER_2_REROUTED: + return "ID_ROUTER_2_REROUTED"; + + case ID_TEAM_BALANCER_INTERNAL: + return "ID_TEAM_BALANCER_INTERNAL"; + + case ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING: + return "ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING"; + + case ID_TEAM_BALANCER_TEAMS_LOCKED: + return "ID_TEAM_BALANCER_TEAMS_LOCKED"; + + case ID_TEAM_BALANCER_TEAM_ASSIGNED: + return "ID_TEAM_BALANCER_TEAM_ASSIGNED"; + + case ID_LIGHTSPEED_INTEGRATION: + return "ID_LIGHTSPEED_INTEGRATION"; + + case ID_XBOX_LOBBY: + return "ID_XBOX_LOBBY"; + + case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS: + return "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS"; + + case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS: + return "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS"; + + case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE: + return "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE"; + + case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE: + return "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE"; + + case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT: + return "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT"; + + case ID_TWO_WAY_AUTHENTICATION_NEGOTIATION: + return "ID_TWO_WAY_AUTHENTICATION_NEGOTIATION"; + + case ID_CLOUD_POST_REQUEST: + return "ID_CLOUD_POST_REQUEST"; + + case ID_CLOUD_RELEASE_REQUEST: + return "ID_CLOUD_RELEASE_REQUEST"; + + case ID_CLOUD_GET_REQUEST: + return "ID_CLOUD_GET_REQUEST"; + + case ID_CLOUD_GET_RESPONSE: + return "ID_CLOUD_GET_RESPONSE"; + + case ID_CLOUD_UNSUBSCRIBE_REQUEST: + return "ID_CLOUD_UNSUBSCRIBE_REQUEST"; + + case ID_CLOUD_SERVER_TO_SERVER_COMMAND: + return "ID_CLOUD_SERVER_TO_SERVER_COMMAND"; + + case ID_CLOUD_SUBSCRIPTION_NOTIFICATION: + return "ID_CLOUD_SUBSCRIPTION_NOTIFICATION"; + + case ID_RESERVED_1: + return "ID_RESERVED_1"; + + case ID_RESERVED_2: + return "ID_RESERVED_2"; + + case ID_RESERVED_3: + return "ID_RESERVED_3"; + + case ID_RESERVED_4: + return "ID_RESERVED_4"; + + case ID_RESERVED_5: + return "ID_RESERVED_5"; + + case ID_RESERVED_6: + return "ID_RESERVED_6"; + + case ID_RESERVED_7: + return "ID_RESERVED_7"; + + case ID_RESERVED_8: + return "ID_RESERVED_8"; + + case ID_RESERVED_9: + return "ID_RESERVED_9"; + + default: + break; + } + return "Unknown or user-defined"; +} +#endif + +int RakNetInstance::handleUnconnectedPong(const RakNet::RakString& data, const RakNet::Packet* p, const char* appid, bool insertAtBeginning) +{ + RakNet::RakString appIdentifier(appid); + // This weird code is a result of RakString.Find being pretty useless + bool emptyNameOrLonger = data.GetLength() >= appIdentifier.GetLength(); + + if ( !emptyNameOrLonger || appIdentifier.StrCmp(data.SubStr(0, appIdentifier.GetLength())) != 0) + return -1; + + bool found = false; + for (unsigned int i = 0; i < availableServers.size(); i++) { + if (availableServers[i].address == p->systemAddress) { + availableServers[i].pingTime = RakNet::GetTimeMS(); + + bool emptyName = data.GetLength() == appIdentifier.GetLength(); + if (emptyName) + availableServers[i].name = ""; + else { + availableServers[i].name = data.SubStr(appIdentifier.GetLength(), data.GetLength() - appIdentifier.GetLength()); + } + //LOGI("Swapping name: %s\n", availableServers[i].name.C_String()); + return i; + } + } + PingedCompatibleServer server; + server.address = p->systemAddress; + server.pingTime = RakNet::GetTimeMS(); + server.isSpecial = false; + server.name = data.SubStr(appIdentifier.GetLength(), data.GetLength() - appIdentifier.GetLength()); + + if (insertAtBeginning) { + availableServers.insert(availableServers.begin(), server); + return 0; + } else { + availableServers.push_back(server); + return availableServers.size() - 1; + } +} + +void RakNetInstance::setIsLoggedIn( bool status ) { + _isLoggedIn = status; +} diff --git a/src/network/RakNetInstance.h b/src/network/RakNetInstance.h index d3e8571..ddfbc1c 100755 --- a/src/network/RakNetInstance.h +++ b/src/network/RakNetInstance.h @@ -1,118 +1,118 @@ -#ifndef _MINECRAFT_NETWORK_RAKNETINSTANCE_H_ -#define _MINECRAFT_NETWORK_RAKNETINSTANCE_H_ - -#include -#include -#include "../raknet/RakNetTypes.h" -#include "../raknet/RakString.h" - -namespace RakNet -{ - class RakPeerInterface; -} - -class Packet; -class NetEventCallback; - -typedef struct PingedCompatibleServer -{ - RakNet::RakString name; - RakNet::SystemAddress address; - RakNet::TimeMS pingTime; - bool isSpecial; -} PingedCompatibleServer; -typedef std::vector ServerList; - -class IRakNetInstance -{ -public: - virtual ~IRakNetInstance() {} - - virtual bool host(const std::string& localName, int port, int maxConnections = 4) { return false; } - virtual bool connect(const char* host, int port) { return false; } - virtual void setIsLoggedIn(bool status) {} - - virtual void pingForHosts(int port) {} - virtual void stopPingForHosts() {} - virtual const ServerList& getServerList() { static ServerList l; return l; } - virtual void clearServerList() {} - - virtual void disconnect() {} - - virtual void announceServer(const std::string& localName) {} - - virtual RakNet::RakPeerInterface* getPeer() { return NULL; } - virtual bool isMyLocalGuid(const RakNet::RakNetGUID& guid) { return true; } - - virtual void runEvents(NetEventCallback* callback) {} - - virtual void send(Packet& packet) {} - virtual void send(const RakNet::RakNetGUID& guid, Packet& packet) {} - - // @attn: Those delete the packet - virtual void send(Packet* packet) {} - virtual void send(const RakNet::RakNetGUID& guid, Packet* packet) {} - - virtual bool isServer() { return true; } - virtual bool isProbablyBroken() { return false; } - virtual void resetIsBroken() {} -}; - -class RakNetInstance: public IRakNetInstance -{ -public: - - RakNetInstance(); - virtual ~RakNetInstance(); - - bool host(const std::string& localName, int port, int maxConnections = 4); - bool connect(const char* host, int port); - void setIsLoggedIn(bool status); - - void pingForHosts(int basePort); - void stopPingForHosts(); - const ServerList& getServerList(); - void clearServerList(); - - void disconnect(); - - void announceServer(const std::string& localName); - - RakNet::RakPeerInterface* getPeer(); - bool isMyLocalGuid(const RakNet::RakNetGUID& guid); - - void runEvents(NetEventCallback* callback); - - void send(Packet& packet); - void send(const RakNet::RakNetGUID& guid, Packet& packet); - - // @attn: Those delete the packet - void send(Packet* packet); - void send(const RakNet::RakNetGUID& guid, Packet* packet); - - bool isServer() { return _isServer; } - bool isProbablyBroken(); - void resetIsBroken(); - -#ifdef _DEBUG - const char* getPacketName(int packetId); -#else - const char* getPacketName(int packetId) { return ""; } -#endif - -private: - int handleUnconnectedPong(const RakNet::RakString& data, const RakNet::Packet*, const char* appid, bool insertAtBeginning); - - RakNet::RakPeerInterface* rakPeer; - RakNet::RakNetGUID serverGuid; - - ServerList availableServers; - bool isPingingForServers; - int pingPort; - RakNet::TimeMS lastPingTime; - - bool _isServer; - bool _isLoggedIn; -}; - -#endif /*_MINECRAFT_NETWORK_RAKNETINSTANCE_H_*/ +#ifndef _MINECRAFT_NETWORK_RAKNETINSTANCE_H_ +#define _MINECRAFT_NETWORK_RAKNETINSTANCE_H_ + +#include +#include +#include "../raknet/RakNetTypes.h" +#include "../raknet/RakString.h" + +namespace RakNet +{ + class RakPeerInterface; +} + +class Packet; +class NetEventCallback; + +typedef struct PingedCompatibleServer +{ + RakNet::RakString name; + RakNet::SystemAddress address; + RakNet::TimeMS pingTime; + bool isSpecial; +} PingedCompatibleServer; +typedef std::vector ServerList; + +class IRakNetInstance +{ +public: + virtual ~IRakNetInstance() {} + + virtual bool host(const std::string& localName, int port, int maxConnections = 4) { return false; } + virtual bool connect(const char* host, int port) { return false; } + virtual void setIsLoggedIn(bool status) {} + + virtual void pingForHosts(int port) {} + virtual void stopPingForHosts() {} + virtual const ServerList& getServerList() { static ServerList l; return l; } + virtual void clearServerList() {} + + virtual void disconnect() {} + + virtual void announceServer(const std::string& localName) {} + + virtual RakNet::RakPeerInterface* getPeer() { return NULL; } + virtual bool isMyLocalGuid(const RakNet::RakNetGUID& guid) { return true; } + + virtual void runEvents(NetEventCallback* callback) {} + + virtual void send(Packet& packet) {} + virtual void send(const RakNet::RakNetGUID& guid, Packet& packet) {} + + // @attn: Those delete the packet + virtual void send(Packet* packet) {} + virtual void send(const RakNet::RakNetGUID& guid, Packet* packet) {} + + virtual bool isServer() { return true; } + virtual bool isProbablyBroken() { return false; } + virtual void resetIsBroken() {} +}; + +class RakNetInstance: public IRakNetInstance +{ +public: + + RakNetInstance(); + virtual ~RakNetInstance(); + + bool host(const std::string& localName, int port, int maxConnections = 4); + bool connect(const char* host, int port); + void setIsLoggedIn(bool status); + + void pingForHosts(int basePort); + void stopPingForHosts(); + const ServerList& getServerList(); + void clearServerList(); + + void disconnect(); + + void announceServer(const std::string& localName); + + RakNet::RakPeerInterface* getPeer(); + bool isMyLocalGuid(const RakNet::RakNetGUID& guid); + + void runEvents(NetEventCallback* callback); + + void send(Packet& packet); + void send(const RakNet::RakNetGUID& guid, Packet& packet); + + // @attn: Those delete the packet + void send(Packet* packet); + void send(const RakNet::RakNetGUID& guid, Packet* packet); + + bool isServer() { return _isServer; } + bool isProbablyBroken(); + void resetIsBroken(); + +#ifdef _DEBUG + const char* getPacketName(int packetId); +#else + const char* getPacketName(int packetId) { return ""; } +#endif + +private: + int handleUnconnectedPong(const RakNet::RakString& data, const RakNet::Packet*, const char* appid, bool insertAtBeginning); + + RakNet::RakPeerInterface* rakPeer; + RakNet::RakNetGUID serverGuid; + + ServerList availableServers; + bool isPingingForServers; + int pingPort; + RakNet::TimeMS lastPingTime; + + bool _isServer; + bool _isLoggedIn; +}; + +#endif /*_MINECRAFT_NETWORK_RAKNETINSTANCE_H_*/ diff --git a/src/network/ServerSideNetworkHandler.cpp b/src/network/ServerSideNetworkHandler.cpp index 7736908..01fa65e 100755 --- a/src/network/ServerSideNetworkHandler.cpp +++ b/src/network/ServerSideNetworkHandler.cpp @@ -1,715 +1,715 @@ - -#include "ServerSideNetworkHandler.h" -#include "../world/level/Level.h" -#include "../world/entity/player/Player.h" -#include "../world/entity/player/Inventory.h" -#include "../world/Container.h" -#include "../world/inventory/BaseContainerMenu.h" -#include "packet/PacketInclude.h" - -#include "RakNetInstance.h" -#include "../client/Minecraft.h" -#include "../client/player/LocalPlayer.h" -#include "../client/gamemode/GameMode.h" -#include "../raknet/RakPeerInterface.h" -#include "../raknet/PacketPriority.h" -#ifndef STANDALONE_SERVER -#include "../client/sound/SoundEngine.h" -#endif -#include "../server/ServerPlayer.h" -#include "../world/entity/item/FallingTile.h" - -#define TIMES(x) for(int itc ## __LINE__ = 0; itc ## __LINE__ < x; ++ itc ## __LINE__) - -ServerSideNetworkHandler::ServerSideNetworkHandler(Minecraft* minecraft, IRakNetInstance* raknetInstance) -: minecraft(minecraft), - raknetInstance(raknetInstance), - level(NULL) -{ - allowIncomingConnections(false); - rakPeer = raknetInstance->getPeer(); -} - -ServerSideNetworkHandler::~ServerSideNetworkHandler() -{ - if (level) { - level->removeListener(this); - } - - for (unsigned int i = 0; i < _pendingPlayers.size(); ++i) - delete _pendingPlayers[i]; -} - -void ServerSideNetworkHandler::tileChanged(int x, int y, int z) -{ - //LOGI("tileChanged(%d, %d, %d)\n", x, y, z); - - // broadcast change event - UpdateBlockPacket packet(x, y, z, level->getTile(x, y, z), level->getData(x, y, z)); - RakNet::BitStream bitStream; - packet.write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true); -} - -Packet* ServerSideNetworkHandler::getAddPacketFromEntity( Entity* entity ) { - if (entity->isMob() && !entity->isPlayer()) { //@fix: This code is duplicated. See if it can be unified. - if (minecraft->player) { - // I guess this should always be true, but it crashed somewhere in this - // function once and I only see this one as a potential problem - return new AddMobPacket((Mob*)entity); - } - } - else if (entity->isPlayer()) { - - } else if (entity->isItemEntity()) { - AddItemEntityPacket* packet = new AddItemEntityPacket((ItemEntity*)entity); - entity->xd = packet->xa(); - entity->yd = packet->ya(); - entity->zd = packet->za(); - //LOGI("item-entity @ server: %f, %f, %f\n", e->xd, e->yd, e->zd); - return packet; - } else if(entity->isHangingEntity()) { - return new AddPaintingPacket((Painting*) entity); - } else { - int type = entity->getEntityTypeId(); - int data = entity->getAuxData(); - - if (EntityTypes::IdFallingTile == type) { - FallingTile* ft = (FallingTile*) entity; - data = -(ft->tile | (ft->data << 16)); - } - - //LOGI("Server: adding entity with %f, %f, %f\n", e->xd, e->yd, e->zd); - AddEntityPacket* packet = new AddEntityPacket(entity, data); - /* - entity->xd = packet->xd; - entity->yd = packet->yd; - entity->zd = packet->zd; - */ - return packet; - } - return NULL; -} -void ServerSideNetworkHandler::entityAdded(Entity* e) { - Packet* packet = getAddPacketFromEntity(e); - if(packet != NULL) { - if (e->isMob() && !e->isPlayer()) { - redistributePacket(packet, rakPeer->GetMyGUID()); - delete packet; - } else { - raknetInstance->send(packet); - // raknetInstance->send always deletes package - } - - } -} - -void ServerSideNetworkHandler::entityRemoved(Entity* e) -{ - if (!e->isPlayer()) { //@fix: This code MIGHT be duplicated. See if it can be unified. - RemoveEntityPacket packet(e->entityId); - redistributePacket(&packet, rakPeer->GetMyGUID()); - } else { // Is a player - RemovePlayerPacket packet((Player*) e); - redistributePacket(&packet, rakPeer->GetMyGUID()); - } -} - -void ServerSideNetworkHandler::redistributePacket(Packet* packet, const RakNet::RakNetGUID& fromPlayer) -{ - // broadcast the new player to all other players - RakNet::BitStream bitStream; - packet->write(&bitStream); - rakPeer->Send(&bitStream, packet->priority, packet->reliability, 0, fromPlayer, true); -} - -void ServerSideNetworkHandler::displayGameMessage(const std::string& message) -{ -#ifndef STANDALONE_SERVER - minecraft->gui.addMessage(message); -#else - LOGI("%s\n", message.c_str()); -#endif - MessagePacket packet(message.c_str()); - raknetInstance->send(packet); -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ChatPacket* packet) -{ - displayGameMessage(packet->message); -} - -void ServerSideNetworkHandler::onNewClient(const RakNet::RakNetGUID& clientGuid) -{ - LOGI("onNewClient, client guid: %s\n", clientGuid.ToString()); -} - -void ServerSideNetworkHandler::onDisconnect(const RakNet::RakNetGUID& guid) -{ - if (!level) return; - LOGI("onDisconnect\n"); - - const PlayerList& players = level->players; - for (unsigned int i = 0; i < players.size(); i++) - { - Player* player = players[i]; - - if (player->owner == guid) - { - std::string message = player->name; - message += " disconnected from the game"; - displayGameMessage(message); - - //RemoveEntityPacket packet(player->entityId); - //raknetInstance->send(packet); - player->reallyRemoveIfPlayer = true; - level->removeEntity(player); - //level->removePlayer(player); - - LOGI("&e@disc: %p", player); - - break; - } - } -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, LoginPacket* packet) -{ - if (!level) return; - if (!_allowIncoming) return; - - LOGI("LoginPacket\n"); - - int loginStatus = LoginStatus::Success; - // - // Bad/incompatible client version - // - bool oldClient = packet->clientNetworkVersion < SharedConstants::NetworkProtocolLowestSupportedVersion; - bool oldServer = packet->clientNetworkLowestSupportedVersion > SharedConstants::NetworkProtocolVersion; - if (oldClient || oldServer) - loginStatus = oldClient? LoginStatus::Failed_ClientOld : LoginStatus::Failed_ServerOld; - - RakNet::BitStream bitStream; - LoginStatusPacket(loginStatus).write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - - if (LoginStatus::Success != loginStatus) - return; - - // - // Valid client version - // - Player* newPlayer = new ServerPlayer(minecraft, level); - - minecraft->gameMode->initAbilities(newPlayer->abilities); - newPlayer->owner = source; - newPlayer->name = packet->clientName.C_String(); - _pendingPlayers.push_back(newPlayer); - - // Reset the player so he doesn't spawn inside blocks - while (newPlayer->y > 0) { - newPlayer->setPos(newPlayer->x, newPlayer->y, newPlayer->z); - if (level->getCubes(newPlayer, newPlayer->bb).size() == 0) break; - newPlayer->y += 1; - } - newPlayer->moveTo(newPlayer->x, newPlayer->y - newPlayer->heightOffset, newPlayer->z, newPlayer->yRot, newPlayer->xRot); - - // send world seed - { - RakNet::BitStream bitStream; - - // @todo: Read from LevelData? - int gameType = minecraft->isCreativeMode() - ? GameType::Creative - : GameType::Survival; - - StartGamePacket( - level->getSeed(), - level->getLevelData()->getGeneratorVersion(), - gameType, - newPlayer->entityId, - newPlayer->x, newPlayer->y - newPlayer->heightOffset, newPlayer->z - ).write(&bitStream); - - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - } -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ReadyPacket* packet) -{ - if (!level) return; - - if (packet->type == ReadyPacket::READY_CLIENTGENERATION) - onReady_ClientGeneration(source); - - if (packet->type == ReadyPacket::READY_REQUESTEDCHUNKS) - onReady_RequestedChunks(source); -} - -void ServerSideNetworkHandler::onReady_ClientGeneration(const RakNet::RakNetGUID& source) -{ - Player* newPlayer = popPendingPlayer(source); - if (!newPlayer) { - for (int i = 0; i < 3; ++i) - LOGE("We don't have a user associated with this player!\n"); - return; - } - // Create a bitstream that can be used by everyone (after ::reset() ) - RakNet::BitStream bitStream; - - // send level info - SetTimePacket(level->getTime()).write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - - // send adventure settings (includes showNameTags, etc.) - bitStream.Reset(); - AdventureSettingsPacket(level->adventureSettings).write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - - // send all pre-existing players to the new player - const PlayerList& players = level->players; - for (unsigned int i = 0; i < players.size(); i++) { - Player* player = players[i]; - - bitStream.Reset(); - AddPlayerPacket(player).write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - - if (player->getArmorTypeHash()) { - bitStream.Reset(); - PlayerArmorEquipmentPacket(player).write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - } - } - - level->addEntity(newPlayer); -#ifndef STANDALONE_SERVER - minecraft->gui.addMessage(newPlayer->name + " joined the game"); -#else - LOGW("%s joined the game\n", newPlayer->name.c_str()); -#endif - - // Send all Entities to the new player - for (unsigned int i = 0; i < level->entities.size(); ++i) { - Entity* e = level->entities[i]; - Packet* packet = getAddPacketFromEntity(e); - if(packet != NULL) { - bitStream.Reset(); - packet->write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - delete packet; - } - } - - // Additional packets - // * set spawn - /* - bitStream.Reset(); - SetSpawnPositionPacket spawnPacket(level->getSharedSpawnPos()); - raknetInstance->send(source, spawnPacket); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - */ - - // broadcast the new player to all other players - bitStream.Reset(); - AddPlayerPacket(newPlayer).write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, true); -} - -// -// Messages to be sent after client has finished applying changes -// -void ServerSideNetworkHandler::onReady_RequestedChunks(const RakNet::RakNetGUID& source) -{ - RakNet::BitStream bitStream; - // Send all TileEntities to the new player - for (unsigned int i = 0; i < level->tileEntities.size(); ++i) { - TileEntity* e = level->tileEntities[i]; - Packet* packet = e->getUpdatePacket(); - if (packet != NULL) { - bitStream.Reset(); - packet->write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - delete packet; - } - } -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet) -{ - if (!level) return; - - //LOGI("MovePlayerPacket\n"); - if (Entity* entity = level->getEntity(packet->entityId)) - { - entity->xd = entity->yd = entity->zd = 0; - entity->lerpTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot, 3); - - // broadcast this packet to other clients - redistributePacket(packet, source); - } -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RemoveBlockPacket* packet){ - if (!level) return; - - Player* player = getPlayer(source); - if (!player) return; - - player->swing(); - - int x = packet->x, y = packet->y, z = packet->z; - - // code copied from GameMode.cpp - int oldId = level->getTile(x, y, z); - int data = level->getData(x, y, z); - Tile* oldTile = Tile::tiles[oldId]; - bool changed = level->setTile(x, y, z, 0); - if (oldTile != NULL && changed) { - level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, oldTile->soundType->getBreakSound(), (oldTile->soundType->getVolume() + 1) / 2, oldTile->soundType->getPitch() * 0.8f); - - if (minecraft->gameMode->isSurvivalType() && player->canDestroy(oldTile)) - //oldTile->spawnResources(level, x, y, z, data, 1); //@todo - oldTile->playerDestroy(level, player, x, y, z, data); - - oldTile->destroy(level, x, y, z, data); - } -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RequestChunkPacket* packet) -{ - if (!level) - return; - - LevelChunk* chunk = level->getChunk(packet->x, packet->z); - - if (!chunk) - return; - - ChunkDataPacket cpacket(chunk->x, chunk->z, chunk); - - RakNet::BitStream bitStream; - cpacket.write(&bitStream); - rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); - //LOGI("RequestChunkPacket @ (%d, %d). %d bytes\n", packet->x, packet->z, cpacket.chunkData.GetNumberOfBytesUsed()); - //LOGI("size: %d\n", bitStream.GetNumberOfBytesUsed()); - - const LevelChunk::TEMap& teMap = chunk->getTileEntityMap(); - for (LevelChunk::TEMapCIterator cit = teMap.begin(); cit != teMap.end(); ++cit) { - TileEntity* te = cit->second; - if (Packet* p = te->getUpdatePacket()) { - bitStream.Reset(); - p->write(&bitStream); - raknetInstance->send(source, p); - } - } -} - -void ServerSideNetworkHandler::levelGenerated( Level* level ) -{ - this->level = level; - - if (minecraft->player) { - minecraft->player->owner = rakPeer->GetMyGUID(); - } - - level->addListener(this); -#ifndef STANDALONE_SERVER - allowIncomingConnections(minecraft->options.getBooleanValue(OPTIONS_SERVER_VISIBLE)); -#else - allowIncomingConnections(true); -#endif -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet) -{ - if (!level) return; - - Player* player = getPlayer(source); - if (!player) return; - if (rakPeer->GetMyGUID() == player->owner) return; - - // override the player's inventory - //int slot = player->inventory->getSlot(packet->itemId, packet->itemAuxValue); - int slot = Inventory::MAX_SELECTION_SIZE; - if (slot >= 0) { - if (packet->itemId == 0) { - player->inventory->clearSlot(slot); - } else { - // @note: 128 is an ugly hack for depletable items. - // @todo: fix - ItemInstance newItem(packet->itemId, 128, packet->itemAuxValue); - player->inventory->replaceSlot(slot, &newItem); - } - player->inventory->moveToSelectedSlot(slot, true); - redistributePacket(packet, source); - } else { - LOGW("Warning: Remote player doesn't have his thing, Odd!\n"); - } -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet) { - if (!level) return; - - Player* player = getPlayer(source); - if (!player) return; - if (rakPeer->GetMyGUID() == player->owner) return; - - packet->fillIn(player); - redistributePacket(packet, source); -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, InteractPacket* packet) { - if (!level) return; - - Entity* src = level->getEntity(packet->sourceId); - Entity* entity = level->getEntity(packet->targetId); - if (src && entity && src->isPlayer()) - { - Player* player = (Player*) src; - if (InteractPacket::Attack == packet->action) { - player->swing(); - minecraft->gameMode->attack(player, entity); - } - if (InteractPacket::Interact == packet->action) { - player->swing(); - minecraft->gameMode->interact(player, entity); - } - - redistributePacket(packet, source); - } -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AnimatePacket* packet) -{ - if (!level) - return; - - // Own player -> invalid - if (minecraft->player && minecraft->player->entityId == packet->entityId) { - return; - } - - Entity* entity = level->getEntity(packet->entityId); - if (!entity || !entity->isPlayer()) - return; - - Player* player = (Player*) entity; - - switch (packet->action) { - case AnimatePacket::Swing: - player->swing(); - break; - default: - LOGW("Unknown Animate action: %d\n", packet->action); - break; - } - redistributePacket(packet, source); -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, UseItemPacket* packet) -{ - if (!level) return; - - LOGI("UseItemPacket\n"); - Entity* entity = level->getEntity(packet->entityId); - if (entity && entity->isPlayer()) { - Player* player = (Player*) entity; - int x = packet->x, y = packet->y, z = packet->z; - Tile* t = Tile::tiles[level->getTile(x, y, z)]; - - if (t == Tile::invisible_bedrock) return; - if (t && t->use(level, x, y, z, player)) return; - if (packet->item.isNull()) return; - - ItemInstance* item = &packet->item; - - if(packet->face == 255) { - // Special case: x,y,z means direction-of-action - player->aimDirection.set(packet->x / 32768.0f, packet->y / 32768.0f, packet->z / 32768.0f); - minecraft->gameMode->useItem(player, level, item); - } - else { - minecraft->gameMode->useItemOn(player, level, item, packet->x, packet->y, packet->z, packet->face, - Vec3(packet->clickX + packet->x, packet->clickY + packet->y, packet->clickZ + packet->z)); - } - - //LOGW("Use Item not working! Out of synch?\n"); - - // Don't have to redistribute (ugg.. this will mess up), cause tileUpdated is sent - //redistributePacket(packet, source); - } -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet) { - if (!level) return; - - if (Entity* e = level->getEntity(packet->entityId)) - e->handleEntityEvent(packet->eventId); -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerActionPacket* packet) -{ - if (!level) return; - LOGI("PlayerActionPacket\n"); - Entity* entity = level->getEntity(packet->entityId); - if (entity && entity->isPlayer()) { - Player* player = (Player*) entity; - if(packet->action == PlayerActionPacket::RELEASE_USE_ITEM) { - minecraft->gameMode->releaseUsingItem(player); - return; - } - else if(packet->action == PlayerActionPacket::STOP_SLEEPING) { - player->stopSleepInBed(true, true, true); - } - } -} - -void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, RespawnPacket* packet ) -{ - if (!level) return; - - NetEventCallback::handle(level, source, packet ); - redistributePacket(packet, source); -} - -void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, SendInventoryPacket* packet ) -{ - if (!level) return; - - Entity* entity = level->getEntity(packet->entityId); - if (entity && entity->isPlayer()) { - Player* p = (Player*)entity; - p->inventory->replace(packet->items, packet->numItems); - if ((packet->extra & SendInventoryPacket::ExtraDrop) != 0) { - p->inventory->dropAll(false); - //@todo @armor : Drop armor - } - } -} - -void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, DropItemPacket* packet ) -{ - if (!level) return; - - Entity* entity = level->getEntity(packet->entityId); - if (entity && entity->isPlayer()) { - Player* p = (Player*)entity; - p->drop(new ItemInstance(packet->item), packet->dropType != 0); - } -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet) { - if (!level) return; - - Player* p = findPlayer(level, &source); - if (!p) return; - - if (p != minecraft->player) - static_cast(p)->doCloseContainer(); -} - -void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet) { - if (!level) return; - - Player* p = findPlayer(level, &source); - if (!p) return; - - if (p->containerMenu == NULL) { - LOGW("User has no container!\n"); - return; - } - if (p->containerMenu->containerId != packet->containerId) - { - LOGW("Wrong container id: %d vs %d\n", p->containerMenu->containerId, packet->containerId); - return; - } - - if (ContainerType::FURNACE == p->containerMenu->containerType) { - //LOGI("Server:Setting slot %d: %s\n", packet->slot, packet->item.toString().c_str()); - p->containerMenu->setSlot(packet->slot, &packet->item); - //p->containerMenu->setSlot(packet->slot, packet->item.isNull()? NULL : &packet->item); - } - if (ContainerType::CONTAINER == p->containerMenu->containerType) { - //LOGI("Server:Setting slot %d: %s\n", packet->slot, packet->item.toString().c_str()); - p->containerMenu->setSlot(packet->slot, &packet->item); - //p->containerMenu->setSlot(packet->slot, packet->item.isNull()? NULL : &packet->item); - } -} - -void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, SetHealthPacket* packet ) -{ - for (unsigned int i = 0; i < level->players.size(); ++i) { - Player* p = level->players[i]; - if (p->owner == source) { - if (packet->health <= -32) { - int diff = packet->health - SetHealthPacket::HEALTH_MODIFY_OFFSET; - if (diff > 0) p->hurt(NULL, diff); - else if (diff < 0) p->heal(-diff); - } - break; - } - } -} - -void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, SignUpdatePacket* packet ) { - redistributePacket(packet, source); - if (!level) - return; - - TileEntity* te = level->getTileEntity(packet->x, packet->y, packet->z); - if (TileEntity::isType(te, TileEntityType::Sign)) { - SignTileEntity* ste = (SignTileEntity*) te; - if (ste->isEditable()) { - for (int i = 0; i < SignTileEntity::NUM_LINES; i++) { - ste->messages[i] = packet->lines[i]; - } - //ste->setChanged(); - } - } -} - -void ServerSideNetworkHandler::allowIncomingConnections( bool doAllow ) -{ - if (doAllow) { - raknetInstance->announceServer(minecraft->options.getStringValue(OPTIONS_USERNAME)); - } else { - raknetInstance->announceServer(""); - } - _allowIncoming = doAllow; -} - -Player* ServerSideNetworkHandler::popPendingPlayer( const RakNet::RakNetGUID& source ) -{ - if (!level) { - LOGE("Could not add player since Level is NULL!\n"); - return NULL; - } - - for (unsigned int i = 0; i < _pendingPlayers.size(); ++i) { - Player* p = _pendingPlayers[i]; - if (p->owner == source) { - _pendingPlayers.erase(_pendingPlayers.begin() + i); - return p; - } - } - return NULL; -} - -void ServerSideNetworkHandler::levelEvent( Player* source, int type, int x, int y, int z, int data ) -{ - LevelEventPacket packet(type, x, y, z, data); - redistributePacket(&packet, source? source->owner : rakPeer->GetMyGUID()); -} - -void ServerSideNetworkHandler::tileEvent( int x, int y, int z, int b0, int b1 ) -{ - TileEventPacket packet(x, y, z, b0, b1); - raknetInstance->send(packet); -} - -Player* ServerSideNetworkHandler::getPlayer( const RakNet::RakNetGUID& source ) { - for (unsigned int i = 0; i < level->players.size(); ++i) - if (source == level->players[i]->owner) return level->players[i]; - return NULL; -} + +#include "ServerSideNetworkHandler.h" +#include "../world/level/Level.h" +#include "../world/entity/player/Player.h" +#include "../world/entity/player/Inventory.h" +#include "../world/Container.h" +#include "../world/inventory/BaseContainerMenu.h" +#include "packet/PacketInclude.h" + +#include "RakNetInstance.h" +#include "../client/Minecraft.h" +#include "../client/player/LocalPlayer.h" +#include "../client/gamemode/GameMode.h" +#include "../raknet/RakPeerInterface.h" +#include "../raknet/PacketPriority.h" +#ifndef STANDALONE_SERVER +#include "../client/sound/SoundEngine.h" +#endif +#include "../server/ServerPlayer.h" +#include "../world/entity/item/FallingTile.h" + +#define TIMES(x) for(int itc ## __LINE__ = 0; itc ## __LINE__ < x; ++ itc ## __LINE__) + +ServerSideNetworkHandler::ServerSideNetworkHandler(Minecraft* minecraft, IRakNetInstance* raknetInstance) +: minecraft(minecraft), + raknetInstance(raknetInstance), + level(NULL) +{ + allowIncomingConnections(false); + rakPeer = raknetInstance->getPeer(); +} + +ServerSideNetworkHandler::~ServerSideNetworkHandler() +{ + if (level) { + level->removeListener(this); + } + + for (unsigned int i = 0; i < _pendingPlayers.size(); ++i) + delete _pendingPlayers[i]; +} + +void ServerSideNetworkHandler::tileChanged(int x, int y, int z) +{ + //LOGI("tileChanged(%d, %d, %d)\n", x, y, z); + + // broadcast change event + UpdateBlockPacket packet(x, y, z, level->getTile(x, y, z), level->getData(x, y, z)); + RakNet::BitStream bitStream; + packet.write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true); +} + +Packet* ServerSideNetworkHandler::getAddPacketFromEntity( Entity* entity ) { + if (entity->isMob() && !entity->isPlayer()) { //@fix: This code is duplicated. See if it can be unified. + if (minecraft->player) { + // I guess this should always be true, but it crashed somewhere in this + // function once and I only see this one as a potential problem + return new AddMobPacket((Mob*)entity); + } + } + else if (entity->isPlayer()) { + + } else if (entity->isItemEntity()) { + AddItemEntityPacket* packet = new AddItemEntityPacket((ItemEntity*)entity); + entity->xd = packet->xa(); + entity->yd = packet->ya(); + entity->zd = packet->za(); + //LOGI("item-entity @ server: %f, %f, %f\n", e->xd, e->yd, e->zd); + return packet; + } else if(entity->isHangingEntity()) { + return new AddPaintingPacket((Painting*) entity); + } else { + int type = entity->getEntityTypeId(); + int data = entity->getAuxData(); + + if (EntityTypes::IdFallingTile == type) { + FallingTile* ft = (FallingTile*) entity; + data = -(ft->tile | (ft->data << 16)); + } + + //LOGI("Server: adding entity with %f, %f, %f\n", e->xd, e->yd, e->zd); + AddEntityPacket* packet = new AddEntityPacket(entity, data); + /* + entity->xd = packet->xd; + entity->yd = packet->yd; + entity->zd = packet->zd; + */ + return packet; + } + return NULL; +} +void ServerSideNetworkHandler::entityAdded(Entity* e) { + Packet* packet = getAddPacketFromEntity(e); + if(packet != NULL) { + if (e->isMob() && !e->isPlayer()) { + redistributePacket(packet, rakPeer->GetMyGUID()); + delete packet; + } else { + raknetInstance->send(packet); + // raknetInstance->send always deletes package + } + + } +} + +void ServerSideNetworkHandler::entityRemoved(Entity* e) +{ + if (!e->isPlayer()) { //@fix: This code MIGHT be duplicated. See if it can be unified. + RemoveEntityPacket packet(e->entityId); + redistributePacket(&packet, rakPeer->GetMyGUID()); + } else { // Is a player + RemovePlayerPacket packet((Player*) e); + redistributePacket(&packet, rakPeer->GetMyGUID()); + } +} + +void ServerSideNetworkHandler::redistributePacket(Packet* packet, const RakNet::RakNetGUID& fromPlayer) +{ + // broadcast the new player to all other players + RakNet::BitStream bitStream; + packet->write(&bitStream); + rakPeer->Send(&bitStream, packet->priority, packet->reliability, 0, fromPlayer, true); +} + +void ServerSideNetworkHandler::displayGameMessage(const std::string& message) +{ +#ifndef STANDALONE_SERVER + minecraft->gui.addMessage(message); +#else + LOGI("%s\n", message.c_str()); +#endif + MessagePacket packet(message.c_str()); + raknetInstance->send(packet); +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ChatPacket* packet) +{ + displayGameMessage(packet->message); +} + +void ServerSideNetworkHandler::onNewClient(const RakNet::RakNetGUID& clientGuid) +{ + LOGI("onNewClient, client guid: %s\n", clientGuid.ToString()); +} + +void ServerSideNetworkHandler::onDisconnect(const RakNet::RakNetGUID& guid) +{ + if (!level) return; + LOGI("onDisconnect\n"); + + const PlayerList& players = level->players; + for (unsigned int i = 0; i < players.size(); i++) + { + Player* player = players[i]; + + if (player->owner == guid) + { + std::string message = player->name; + message += " disconnected from the game"; + displayGameMessage(message); + + //RemoveEntityPacket packet(player->entityId); + //raknetInstance->send(packet); + player->reallyRemoveIfPlayer = true; + level->removeEntity(player); + //level->removePlayer(player); + + LOGI("&e@disc: %p", player); + + break; + } + } +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, LoginPacket* packet) +{ + if (!level) return; + if (!_allowIncoming) return; + + LOGI("LoginPacket\n"); + + int loginStatus = LoginStatus::Success; + // + // Bad/incompatible client version + // + bool oldClient = packet->clientNetworkVersion < SharedConstants::NetworkProtocolLowestSupportedVersion; + bool oldServer = packet->clientNetworkLowestSupportedVersion > SharedConstants::NetworkProtocolVersion; + if (oldClient || oldServer) + loginStatus = oldClient? LoginStatus::Failed_ClientOld : LoginStatus::Failed_ServerOld; + + RakNet::BitStream bitStream; + LoginStatusPacket(loginStatus).write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + + if (LoginStatus::Success != loginStatus) + return; + + // + // Valid client version + // + Player* newPlayer = new ServerPlayer(minecraft, level); + + minecraft->gameMode->initAbilities(newPlayer->abilities); + newPlayer->owner = source; + newPlayer->name = packet->clientName.C_String(); + _pendingPlayers.push_back(newPlayer); + + // Reset the player so he doesn't spawn inside blocks + while (newPlayer->y > 0) { + newPlayer->setPos(newPlayer->x, newPlayer->y, newPlayer->z); + if (level->getCubes(newPlayer, newPlayer->bb).size() == 0) break; + newPlayer->y += 1; + } + newPlayer->moveTo(newPlayer->x, newPlayer->y - newPlayer->heightOffset, newPlayer->z, newPlayer->yRot, newPlayer->xRot); + + // send world seed + { + RakNet::BitStream bitStream; + + // @todo: Read from LevelData? + int gameType = minecraft->isCreativeMode() + ? GameType::Creative + : GameType::Survival; + + StartGamePacket( + level->getSeed(), + level->getLevelData()->getGeneratorVersion(), + gameType, + newPlayer->entityId, + newPlayer->x, newPlayer->y - newPlayer->heightOffset, newPlayer->z + ).write(&bitStream); + + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + } +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ReadyPacket* packet) +{ + if (!level) return; + + if (packet->type == ReadyPacket::READY_CLIENTGENERATION) + onReady_ClientGeneration(source); + + if (packet->type == ReadyPacket::READY_REQUESTEDCHUNKS) + onReady_RequestedChunks(source); +} + +void ServerSideNetworkHandler::onReady_ClientGeneration(const RakNet::RakNetGUID& source) +{ + Player* newPlayer = popPendingPlayer(source); + if (!newPlayer) { + for (int i = 0; i < 3; ++i) + LOGE("We don't have a user associated with this player!\n"); + return; + } + // Create a bitstream that can be used by everyone (after ::reset() ) + RakNet::BitStream bitStream; + + // send level info + SetTimePacket(level->getTime()).write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + + // send adventure settings (includes showNameTags, etc.) + bitStream.Reset(); + AdventureSettingsPacket(level->adventureSettings).write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + + // send all pre-existing players to the new player + const PlayerList& players = level->players; + for (unsigned int i = 0; i < players.size(); i++) { + Player* player = players[i]; + + bitStream.Reset(); + AddPlayerPacket(player).write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + + if (player->getArmorTypeHash()) { + bitStream.Reset(); + PlayerArmorEquipmentPacket(player).write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + } + } + + level->addEntity(newPlayer); +#ifndef STANDALONE_SERVER + minecraft->gui.addMessage(newPlayer->name + " joined the game"); +#else + LOGW("%s joined the game\n", newPlayer->name.c_str()); +#endif + + // Send all Entities to the new player + for (unsigned int i = 0; i < level->entities.size(); ++i) { + Entity* e = level->entities[i]; + Packet* packet = getAddPacketFromEntity(e); + if(packet != NULL) { + bitStream.Reset(); + packet->write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + delete packet; + } + } + + // Additional packets + // * set spawn + /* + bitStream.Reset(); + SetSpawnPositionPacket spawnPacket(level->getSharedSpawnPos()); + raknetInstance->send(source, spawnPacket); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + */ + + // broadcast the new player to all other players + bitStream.Reset(); + AddPlayerPacket(newPlayer).write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, true); +} + +// +// Messages to be sent after client has finished applying changes +// +void ServerSideNetworkHandler::onReady_RequestedChunks(const RakNet::RakNetGUID& source) +{ + RakNet::BitStream bitStream; + // Send all TileEntities to the new player + for (unsigned int i = 0; i < level->tileEntities.size(); ++i) { + TileEntity* e = level->tileEntities[i]; + Packet* packet = e->getUpdatePacket(); + if (packet != NULL) { + bitStream.Reset(); + packet->write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + delete packet; + } + } +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet) +{ + if (!level) return; + + //LOGI("MovePlayerPacket\n"); + if (Entity* entity = level->getEntity(packet->entityId)) + { + entity->xd = entity->yd = entity->zd = 0; + entity->lerpTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot, 3); + + // broadcast this packet to other clients + redistributePacket(packet, source); + } +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RemoveBlockPacket* packet){ + if (!level) return; + + Player* player = getPlayer(source); + if (!player) return; + + player->swing(); + + int x = packet->x, y = packet->y, z = packet->z; + + // code copied from GameMode.cpp + int oldId = level->getTile(x, y, z); + int data = level->getData(x, y, z); + Tile* oldTile = Tile::tiles[oldId]; + bool changed = level->setTile(x, y, z, 0); + if (oldTile != NULL && changed) { + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, oldTile->soundType->getBreakSound(), (oldTile->soundType->getVolume() + 1) / 2, oldTile->soundType->getPitch() * 0.8f); + + if (minecraft->gameMode->isSurvivalType() && player->canDestroy(oldTile)) + //oldTile->spawnResources(level, x, y, z, data, 1); //@todo + oldTile->playerDestroy(level, player, x, y, z, data); + + oldTile->destroy(level, x, y, z, data); + } +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RequestChunkPacket* packet) +{ + if (!level) + return; + + LevelChunk* chunk = level->getChunk(packet->x, packet->z); + + if (!chunk) + return; + + ChunkDataPacket cpacket(chunk->x, chunk->z, chunk); + + RakNet::BitStream bitStream; + cpacket.write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); + //LOGI("RequestChunkPacket @ (%d, %d). %d bytes\n", packet->x, packet->z, cpacket.chunkData.GetNumberOfBytesUsed()); + //LOGI("size: %d\n", bitStream.GetNumberOfBytesUsed()); + + const LevelChunk::TEMap& teMap = chunk->getTileEntityMap(); + for (LevelChunk::TEMapCIterator cit = teMap.begin(); cit != teMap.end(); ++cit) { + TileEntity* te = cit->second; + if (Packet* p = te->getUpdatePacket()) { + bitStream.Reset(); + p->write(&bitStream); + raknetInstance->send(source, p); + } + } +} + +void ServerSideNetworkHandler::levelGenerated( Level* level ) +{ + this->level = level; + + if (minecraft->player) { + minecraft->player->owner = rakPeer->GetMyGUID(); + } + + level->addListener(this); +#ifndef STANDALONE_SERVER + allowIncomingConnections(minecraft->options.getBooleanValue(OPTIONS_SERVER_VISIBLE)); +#else + allowIncomingConnections(true); +#endif +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet) +{ + if (!level) return; + + Player* player = getPlayer(source); + if (!player) return; + if (rakPeer->GetMyGUID() == player->owner) return; + + // override the player's inventory + //int slot = player->inventory->getSlot(packet->itemId, packet->itemAuxValue); + int slot = Inventory::MAX_SELECTION_SIZE; + if (slot >= 0) { + if (packet->itemId == 0) { + player->inventory->clearSlot(slot); + } else { + // @note: 128 is an ugly hack for depletable items. + // @todo: fix + ItemInstance newItem(packet->itemId, 128, packet->itemAuxValue); + player->inventory->replaceSlot(slot, &newItem); + } + player->inventory->moveToSelectedSlot(slot, true); + redistributePacket(packet, source); + } else { + LOGW("Warning: Remote player doesn't have his thing, Odd!\n"); + } +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet) { + if (!level) return; + + Player* player = getPlayer(source); + if (!player) return; + if (rakPeer->GetMyGUID() == player->owner) return; + + packet->fillIn(player); + redistributePacket(packet, source); +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, InteractPacket* packet) { + if (!level) return; + + Entity* src = level->getEntity(packet->sourceId); + Entity* entity = level->getEntity(packet->targetId); + if (src && entity && src->isPlayer()) + { + Player* player = (Player*) src; + if (InteractPacket::Attack == packet->action) { + player->swing(); + minecraft->gameMode->attack(player, entity); + } + if (InteractPacket::Interact == packet->action) { + player->swing(); + minecraft->gameMode->interact(player, entity); + } + + redistributePacket(packet, source); + } +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AnimatePacket* packet) +{ + if (!level) + return; + + // Own player -> invalid + if (minecraft->player && minecraft->player->entityId == packet->entityId) { + return; + } + + Entity* entity = level->getEntity(packet->entityId); + if (!entity || !entity->isPlayer()) + return; + + Player* player = (Player*) entity; + + switch (packet->action) { + case AnimatePacket::Swing: + player->swing(); + break; + default: + LOGW("Unknown Animate action: %d\n", packet->action); + break; + } + redistributePacket(packet, source); +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, UseItemPacket* packet) +{ + if (!level) return; + + LOGI("UseItemPacket\n"); + Entity* entity = level->getEntity(packet->entityId); + if (entity && entity->isPlayer()) { + Player* player = (Player*) entity; + int x = packet->x, y = packet->y, z = packet->z; + Tile* t = Tile::tiles[level->getTile(x, y, z)]; + + if (t == Tile::invisible_bedrock) return; + if (t && t->use(level, x, y, z, player)) return; + if (packet->item.isNull()) return; + + ItemInstance* item = &packet->item; + + if(packet->face == 255) { + // Special case: x,y,z means direction-of-action + player->aimDirection.set(packet->x / 32768.0f, packet->y / 32768.0f, packet->z / 32768.0f); + minecraft->gameMode->useItem(player, level, item); + } + else { + minecraft->gameMode->useItemOn(player, level, item, packet->x, packet->y, packet->z, packet->face, + Vec3(packet->clickX + packet->x, packet->clickY + packet->y, packet->clickZ + packet->z)); + } + + //LOGW("Use Item not working! Out of synch?\n"); + + // Don't have to redistribute (ugg.. this will mess up), cause tileUpdated is sent + //redistributePacket(packet, source); + } +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet) { + if (!level) return; + + if (Entity* e = level->getEntity(packet->entityId)) + e->handleEntityEvent(packet->eventId); +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerActionPacket* packet) +{ + if (!level) return; + LOGI("PlayerActionPacket\n"); + Entity* entity = level->getEntity(packet->entityId); + if (entity && entity->isPlayer()) { + Player* player = (Player*) entity; + if(packet->action == PlayerActionPacket::RELEASE_USE_ITEM) { + minecraft->gameMode->releaseUsingItem(player); + return; + } + else if(packet->action == PlayerActionPacket::STOP_SLEEPING) { + player->stopSleepInBed(true, true, true); + } + } +} + +void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, RespawnPacket* packet ) +{ + if (!level) return; + + NetEventCallback::handle(level, source, packet ); + redistributePacket(packet, source); +} + +void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, SendInventoryPacket* packet ) +{ + if (!level) return; + + Entity* entity = level->getEntity(packet->entityId); + if (entity && entity->isPlayer()) { + Player* p = (Player*)entity; + p->inventory->replace(packet->items, packet->numItems); + if ((packet->extra & SendInventoryPacket::ExtraDrop) != 0) { + p->inventory->dropAll(false); + //@todo @armor : Drop armor + } + } +} + +void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, DropItemPacket* packet ) +{ + if (!level) return; + + Entity* entity = level->getEntity(packet->entityId); + if (entity && entity->isPlayer()) { + Player* p = (Player*)entity; + p->drop(new ItemInstance(packet->item), packet->dropType != 0); + } +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet) { + if (!level) return; + + Player* p = findPlayer(level, &source); + if (!p) return; + + if (p != minecraft->player) + static_cast(p)->doCloseContainer(); +} + +void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet) { + if (!level) return; + + Player* p = findPlayer(level, &source); + if (!p) return; + + if (p->containerMenu == NULL) { + LOGW("User has no container!\n"); + return; + } + if (p->containerMenu->containerId != packet->containerId) + { + LOGW("Wrong container id: %d vs %d\n", p->containerMenu->containerId, packet->containerId); + return; + } + + if (ContainerType::FURNACE == p->containerMenu->containerType) { + //LOGI("Server:Setting slot %d: %s\n", packet->slot, packet->item.toString().c_str()); + p->containerMenu->setSlot(packet->slot, &packet->item); + //p->containerMenu->setSlot(packet->slot, packet->item.isNull()? NULL : &packet->item); + } + if (ContainerType::CONTAINER == p->containerMenu->containerType) { + //LOGI("Server:Setting slot %d: %s\n", packet->slot, packet->item.toString().c_str()); + p->containerMenu->setSlot(packet->slot, &packet->item); + //p->containerMenu->setSlot(packet->slot, packet->item.isNull()? NULL : &packet->item); + } +} + +void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, SetHealthPacket* packet ) +{ + for (unsigned int i = 0; i < level->players.size(); ++i) { + Player* p = level->players[i]; + if (p->owner == source) { + if (packet->health <= -32) { + int diff = packet->health - SetHealthPacket::HEALTH_MODIFY_OFFSET; + if (diff > 0) p->hurt(NULL, diff); + else if (diff < 0) p->heal(-diff); + } + break; + } + } +} + +void ServerSideNetworkHandler::handle( const RakNet::RakNetGUID& source, SignUpdatePacket* packet ) { + redistributePacket(packet, source); + if (!level) + return; + + TileEntity* te = level->getTileEntity(packet->x, packet->y, packet->z); + if (TileEntity::isType(te, TileEntityType::Sign)) { + SignTileEntity* ste = (SignTileEntity*) te; + if (ste->isEditable()) { + for (int i = 0; i < SignTileEntity::NUM_LINES; i++) { + ste->messages[i] = packet->lines[i]; + } + //ste->setChanged(); + } + } +} + +void ServerSideNetworkHandler::allowIncomingConnections( bool doAllow ) +{ + if (doAllow) { + raknetInstance->announceServer(minecraft->options.getStringValue(OPTIONS_USERNAME)); + } else { + raknetInstance->announceServer(""); + } + _allowIncoming = doAllow; +} + +Player* ServerSideNetworkHandler::popPendingPlayer( const RakNet::RakNetGUID& source ) +{ + if (!level) { + LOGE("Could not add player since Level is NULL!\n"); + return NULL; + } + + for (unsigned int i = 0; i < _pendingPlayers.size(); ++i) { + Player* p = _pendingPlayers[i]; + if (p->owner == source) { + _pendingPlayers.erase(_pendingPlayers.begin() + i); + return p; + } + } + return NULL; +} + +void ServerSideNetworkHandler::levelEvent( Player* source, int type, int x, int y, int z, int data ) +{ + LevelEventPacket packet(type, x, y, z, data); + redistributePacket(&packet, source? source->owner : rakPeer->GetMyGUID()); +} + +void ServerSideNetworkHandler::tileEvent( int x, int y, int z, int b0, int b1 ) +{ + TileEventPacket packet(x, y, z, b0, b1); + raknetInstance->send(packet); +} + +Player* ServerSideNetworkHandler::getPlayer( const RakNet::RakNetGUID& source ) { + for (unsigned int i = 0; i < level->players.size(); ++i) + if (source == level->players[i]->owner) return level->players[i]; + return NULL; +} diff --git a/src/network/ServerSideNetworkHandler.h b/src/network/ServerSideNetworkHandler.h index 51531cf..f66d032 100755 --- a/src/network/ServerSideNetworkHandler.h +++ b/src/network/ServerSideNetworkHandler.h @@ -1,84 +1,84 @@ -#ifndef _MINECRAFT_NETWORK_SERVERSIDENETWORKHANDLER_H_ -#define _MINECRAFT_NETWORK_SERVERSIDENETWORKHANDLER_H_ - - -#include "NetEventCallback.h" -#include "../raknet/RakNetTypes.h" -#include "../world/level/LevelListener.h" -#include - -class Minecraft; -class Level; -class IRakNetInstance; -class Packet; -class Player; - -class ServerSideNetworkHandler : public NetEventCallback, public LevelListener -{ -public: - ServerSideNetworkHandler(Minecraft* minecraft, IRakNetInstance* raknetInstance); - virtual ~ServerSideNetworkHandler(); - - virtual void levelGenerated(Level* level); - - virtual void tileChanged(int x, int y, int z); - virtual void tileBrightnessChanged(int x, int y, int z) { /* do nothing */ } - virtual Packet* getAddPacketFromEntity(Entity* entity); - virtual void entityAdded(Entity* e); - virtual void entityRemoved(Entity* e); - virtual void levelEvent(Player* source, int type, int x, int y, int z, int data); - virtual void tileEvent(int x, int y, int z, int b0, int b1); - - virtual void onNewClient(const RakNet::RakNetGUID& clientGuid); - virtual void onDisconnect(const RakNet::RakNetGUID& guid); - - void onReady_ClientGeneration(const RakNet::RakNetGUID& source); - void onReady_RequestedChunks(const RakNet::RakNetGUID& source); - - virtual void handle(const RakNet::RakNetGUID& source, LoginPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ReadyPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet); - //virtual void handle(const RakNet::RakNetGUID& source, PlaceBlockPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, RemoveBlockPacket* packet); - //virtual void handle(const RakNet::RakNetGUID& source, ExplodePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, RequestChunkPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, SetHealthPacket* packet); - //virtual void handle(const RakNet::RakNetGUID& source, TeleportEntityPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, InteractPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, AnimatePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, UseItemPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, PlayerActionPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, RespawnPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, SendInventoryPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, DropItemPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet); - virtual void handle(const RakNet::RakNetGUID& source, ChatPacket* packet); - - bool allowsIncomingConnections() { return _allowIncoming; } - void allowIncomingConnections(bool doAllow); - - Player* popPendingPlayer(const RakNet::RakNetGUID& source); - - void displayGameMessage(const std::string& message); -private: - /** - * @brief Send packet to all players - */ - void redistributePacket(Packet* packet, const RakNet::RakNetGUID& fromPlayer); - Player* getPlayer(const RakNet::RakNetGUID& source); - - Minecraft* minecraft; - Level* level; - IRakNetInstance* raknetInstance; - RakNet::RakPeerInterface* rakPeer; - - std::vector _pendingPlayers; - bool _allowIncoming; -}; - -#endif +#ifndef _MINECRAFT_NETWORK_SERVERSIDENETWORKHANDLER_H_ +#define _MINECRAFT_NETWORK_SERVERSIDENETWORKHANDLER_H_ + + +#include "NetEventCallback.h" +#include "../raknet/RakNetTypes.h" +#include "../world/level/LevelListener.h" +#include + +class Minecraft; +class Level; +class IRakNetInstance; +class Packet; +class Player; + +class ServerSideNetworkHandler : public NetEventCallback, public LevelListener +{ +public: + ServerSideNetworkHandler(Minecraft* minecraft, IRakNetInstance* raknetInstance); + virtual ~ServerSideNetworkHandler(); + + virtual void levelGenerated(Level* level); + + virtual void tileChanged(int x, int y, int z); + virtual void tileBrightnessChanged(int x, int y, int z) { /* do nothing */ } + virtual Packet* getAddPacketFromEntity(Entity* entity); + virtual void entityAdded(Entity* e); + virtual void entityRemoved(Entity* e); + virtual void levelEvent(Player* source, int type, int x, int y, int z, int data); + virtual void tileEvent(int x, int y, int z, int b0, int b1); + + virtual void onNewClient(const RakNet::RakNetGUID& clientGuid); + virtual void onDisconnect(const RakNet::RakNetGUID& guid); + + void onReady_ClientGeneration(const RakNet::RakNetGUID& source); + void onReady_RequestedChunks(const RakNet::RakNetGUID& source); + + virtual void handle(const RakNet::RakNetGUID& source, LoginPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ReadyPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet); + //virtual void handle(const RakNet::RakNetGUID& source, PlaceBlockPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, RemoveBlockPacket* packet); + //virtual void handle(const RakNet::RakNetGUID& source, ExplodePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, RequestChunkPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, SetHealthPacket* packet); + //virtual void handle(const RakNet::RakNetGUID& source, TeleportEntityPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, InteractPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, AnimatePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, UseItemPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, PlayerActionPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, RespawnPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, SendInventoryPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, DropItemPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet); + virtual void handle(const RakNet::RakNetGUID& source, ChatPacket* packet); + + bool allowsIncomingConnections() { return _allowIncoming; } + void allowIncomingConnections(bool doAllow); + + Player* popPendingPlayer(const RakNet::RakNetGUID& source); + + void displayGameMessage(const std::string& message); +private: + /** + * @brief Send packet to all players + */ + void redistributePacket(Packet* packet, const RakNet::RakNetGUID& fromPlayer); + Player* getPlayer(const RakNet::RakNetGUID& source); + + Minecraft* minecraft; + Level* level; + IRakNetInstance* raknetInstance; + RakNet::RakPeerInterface* rakPeer; + + std::vector _pendingPlayers; + bool _allowIncoming; +}; + +#endif diff --git a/src/network/packet/AddEntityPacket.h b/src/network/packet/AddEntityPacket.h index 9c31089..9c01d54 100755 --- a/src/network/packet/AddEntityPacket.h +++ b/src/network/packet/AddEntityPacket.h @@ -1,93 +1,93 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__AddEntityPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__AddEntityPacket_H__ - -#include "../Packet.h" -#include "../../world/entity/Entity.h" - -class AddEntityPacket : public Packet -{ -public: - AddEntityPacket() {} - - AddEntityPacket(const Entity* e, int data = 0) - : entityId(e->entityId), - type(e->getEntityTypeId()), - x(e->x), - y(e->y), - z(e->z), - _data(data) - { - if (hasMovementData()) { - xd = e->xd; - yd = e->yd; - zd = e->zd; - } - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDENTITY)); - bitStream->Write(entityId); - unsigned char bType = (unsigned char)type; - bitStream->Write(bType); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(_data); - - if (hasMovementData()) { - const float M = 3.9f; - short xa = (short)(8000.0f * Mth::clamp(xd, -M, M)); - short ya = (short)(8000.0f * Mth::clamp(yd, -M, M)); - short za = (short)(8000.0f * Mth::clamp(zd, -M, M)); - - bitStream->Write(xa); - bitStream->Write(ya); - bitStream->Write(za); - } - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - unsigned char bType; - bitStream->Read(bType); - type = bType; - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - bitStream->Read(_data); - - if (hasMovementData()) { - short xa, ya, za; - bitStream->Read(xa); - bitStream->Read(ya); - bitStream->Read(za); - xd = xa / 8000.0f; - yd = ya / 8000.0f; - zd = za / 8000.0f; - } - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (AddEntityPacket*)this); - } - - bool hasMovementData() { - return _data > 0; - } - int data() { - return _data; - } - -public: - int entityId; - float x, y, z; - float xd, yd, zd; - int type; -private: - int _data; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__AddEntityPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__AddEntityPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__AddEntityPacket_H__ + +#include "../Packet.h" +#include "../../world/entity/Entity.h" + +class AddEntityPacket : public Packet +{ +public: + AddEntityPacket() {} + + AddEntityPacket(const Entity* e, int data = 0) + : entityId(e->entityId), + type(e->getEntityTypeId()), + x(e->x), + y(e->y), + z(e->z), + _data(data) + { + if (hasMovementData()) { + xd = e->xd; + yd = e->yd; + zd = e->zd; + } + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDENTITY)); + bitStream->Write(entityId); + unsigned char bType = (unsigned char)type; + bitStream->Write(bType); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(_data); + + if (hasMovementData()) { + const float M = 3.9f; + short xa = (short)(8000.0f * Mth::clamp(xd, -M, M)); + short ya = (short)(8000.0f * Mth::clamp(yd, -M, M)); + short za = (short)(8000.0f * Mth::clamp(zd, -M, M)); + + bitStream->Write(xa); + bitStream->Write(ya); + bitStream->Write(za); + } + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + unsigned char bType; + bitStream->Read(bType); + type = bType; + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + bitStream->Read(_data); + + if (hasMovementData()) { + short xa, ya, za; + bitStream->Read(xa); + bitStream->Read(ya); + bitStream->Read(za); + xd = xa / 8000.0f; + yd = ya / 8000.0f; + zd = za / 8000.0f; + } + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (AddEntityPacket*)this); + } + + bool hasMovementData() { + return _data > 0; + } + int data() { + return _data; + } + +public: + int entityId; + float x, y, z; + float xd, yd, zd; + int type; +private: + int _data; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__AddEntityPacket_H__*/ diff --git a/src/network/packet/AddItemEntityPacket.h b/src/network/packet/AddItemEntityPacket.h index 5e2564a..13173b0 100755 --- a/src/network/packet/AddItemEntityPacket.h +++ b/src/network/packet/AddItemEntityPacket.h @@ -1,78 +1,78 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__AddItemEntityPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__AddItemEntityPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" -#include "../../world/entity/item/ItemEntity.h" -#include "../../world/item/ItemInstance.h" -#include "../../util/Mth.h" - -class AddItemEntityPacket: public Packet { -public: - AddItemEntityPacket() { - } - - AddItemEntityPacket(const ItemEntity* itemEntity) - : id(itemEntity->entityId), - itemId(itemEntity->item.id), - itemCount(itemEntity->item.count), - auxValue(itemEntity->item.getAuxValue()), - x(itemEntity->x), - y(itemEntity->y), - z(itemEntity->z), - _xa((signed char) (itemEntity->xd * 128.0)), - _ya((signed char) (itemEntity->yd * 128.0)), - _za((signed char) (itemEntity->zd * 128.0)) - { - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(id); - bitStream->Read(itemId); - bitStream->Read(itemCount); - bitStream->Read(auxValue); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - bitStream->Read(_xa); - bitStream->Read(_ya); - bitStream->Read(_za); - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDITEMENTITY)); - bitStream->Write(id); - bitStream->Write(itemId); - bitStream->Write(itemCount); - bitStream->Write(auxValue); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(_xa); - bitStream->Write(_ya); - bitStream->Write(_za); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (AddItemEntityPacket*)this); - } - - float xa() { return (float)(_xa) / 128.0f; } - float ya() { return (float)(_ya) / 128.0f; } - float za() { return (float)(_za) / 128.0f; } - - int id; - float x, y, z; - - short itemId; - short auxValue; - unsigned char itemCount; -private: - signed char _xa, _ya, _za; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__AddItemEntityPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__AddItemEntityPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__AddItemEntityPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" +#include "../../world/entity/item/ItemEntity.h" +#include "../../world/item/ItemInstance.h" +#include "../../util/Mth.h" + +class AddItemEntityPacket: public Packet { +public: + AddItemEntityPacket() { + } + + AddItemEntityPacket(const ItemEntity* itemEntity) + : id(itemEntity->entityId), + itemId(itemEntity->item.id), + itemCount(itemEntity->item.count), + auxValue(itemEntity->item.getAuxValue()), + x(itemEntity->x), + y(itemEntity->y), + z(itemEntity->z), + _xa((signed char) (itemEntity->xd * 128.0)), + _ya((signed char) (itemEntity->yd * 128.0)), + _za((signed char) (itemEntity->zd * 128.0)) + { + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(id); + bitStream->Read(itemId); + bitStream->Read(itemCount); + bitStream->Read(auxValue); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + bitStream->Read(_xa); + bitStream->Read(_ya); + bitStream->Read(_za); + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDITEMENTITY)); + bitStream->Write(id); + bitStream->Write(itemId); + bitStream->Write(itemCount); + bitStream->Write(auxValue); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(_xa); + bitStream->Write(_ya); + bitStream->Write(_za); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (AddItemEntityPacket*)this); + } + + float xa() { return (float)(_xa) / 128.0f; } + float ya() { return (float)(_ya) / 128.0f; } + float za() { return (float)(_za) / 128.0f; } + + int id; + float x, y, z; + + short itemId; + short auxValue; + unsigned char itemCount; +private: + signed char _xa, _ya, _za; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__AddItemEntityPacket_H__*/ diff --git a/src/network/packet/AddMobPacket.h b/src/network/packet/AddMobPacket.h index 6ca63be..d27d7a4 100755 --- a/src/network/packet/AddMobPacket.h +++ b/src/network/packet/AddMobPacket.h @@ -1,78 +1,78 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__AddMobPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__AddMobPacket_H__ - -#include "../Packet.h" -#include "../../world/entity/Mob.h" -#include "../../util/RakDataIO.h" - -class AddMobPacket : public Packet -{ -public: - AddMobPacket() - : _entityData(NULL) - { - } - - ~AddMobPacket() { - for (unsigned int i = 0; i < unpack.size(); ++i) - delete unpack[i]; - } - - AddMobPacket(const Mob* mob) - : entityId(mob->entityId), - type(mob->getEntityTypeId()), - x(mob->x), - y(mob->y), - z(mob->z), - xRot(mob->xRot), - yRot(mob->yRot), - _entityData(mob->getEntityData()) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDMOB)); - bitStream->Write(entityId); - bitStream->Write(type); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(PacketUtil::Rot_degreesToChar(yRot)); - bitStream->Write(PacketUtil::Rot_degreesToChar(xRot)); - RakDataOutput dos(*bitStream); - _entityData->packAll(&dos); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(type); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - char rx, ry; - bitStream->Read(ry); - bitStream->Read(rx); - RakDataInput dis(*bitStream); - unpack = SynchedEntityData::unpack(&dis); - yRot = PacketUtil::Rot_degreesToChar(ry); - xRot = PacketUtil::Rot_charToDegrees(rx); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (AddMobPacket*)this); - } - -public: - int entityId; - int type; - float x, y, z; - float xRot, yRot; - SynchedEntityData::DataList unpack; -private: - const SynchedEntityData* _entityData; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__AddMobPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__AddMobPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__AddMobPacket_H__ + +#include "../Packet.h" +#include "../../world/entity/Mob.h" +#include "../../util/RakDataIO.h" + +class AddMobPacket : public Packet +{ +public: + AddMobPacket() + : _entityData(NULL) + { + } + + ~AddMobPacket() { + for (unsigned int i = 0; i < unpack.size(); ++i) + delete unpack[i]; + } + + AddMobPacket(const Mob* mob) + : entityId(mob->entityId), + type(mob->getEntityTypeId()), + x(mob->x), + y(mob->y), + z(mob->z), + xRot(mob->xRot), + yRot(mob->yRot), + _entityData(mob->getEntityData()) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDMOB)); + bitStream->Write(entityId); + bitStream->Write(type); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(PacketUtil::Rot_degreesToChar(yRot)); + bitStream->Write(PacketUtil::Rot_degreesToChar(xRot)); + RakDataOutput dos(*bitStream); + _entityData->packAll(&dos); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(type); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + char rx, ry; + bitStream->Read(ry); + bitStream->Read(rx); + RakDataInput dis(*bitStream); + unpack = SynchedEntityData::unpack(&dis); + yRot = PacketUtil::Rot_degreesToChar(ry); + xRot = PacketUtil::Rot_charToDegrees(rx); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (AddMobPacket*)this); + } + +public: + int entityId; + int type; + float x, y, z; + float xRot, yRot; + SynchedEntityData::DataList unpack; +private: + const SynchedEntityData* _entityData; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__AddMobPacket_H__*/ diff --git a/src/network/packet/AddPaintingPacket.h b/src/network/packet/AddPaintingPacket.h index c842c8d..6d4b8a0 100755 --- a/src/network/packet/AddPaintingPacket.h +++ b/src/network/packet/AddPaintingPacket.h @@ -1,48 +1,48 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__AddPaintingPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__AddPaintingPacket_H__ -#include "../Packet.h" -#include "../../world/entity/Painting.h" -class AddPaintingPacket : public Packet { -public: - AddPaintingPacket() : entityId(0), xTile(0), yTile(0), zTile(0), dir(-1) { - - } - AddPaintingPacket(Painting* painting) { - entityId = painting->entityId; - xTile = painting->xTile; - yTile = painting->yTile; - zTile = painting->zTile; - dir = painting->dir; - motive = painting->motive->name; - } - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDPAINTING)); - bitStream->Write(entityId); - bitStream->Write(xTile); - bitStream->Write(yTile); - bitStream->Write(zTile); - bitStream->Write(dir); - RakNet::RakString rakMotive(motive.c_str()); - bitStream->Write(rakMotive); - } - void read(RakNet::BitStream* bitStream) { - bitStream->Read(entityId); - bitStream->Read(xTile); - bitStream->Read(yTile); - bitStream->Read(zTile); - bitStream->Read(dir); - RakNet::RakString rakMotive; - bitStream->Read(rakMotive); - motive = std::string(rakMotive.C_String()); - } - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (AddPaintingPacket*)this); - } -public: - int entityId; - int xTile, yTile, zTile; - int dir; - std::string motive; -}; - +#ifndef NET_MINECRAFT_NETWORK_PACKET__AddPaintingPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__AddPaintingPacket_H__ +#include "../Packet.h" +#include "../../world/entity/Painting.h" +class AddPaintingPacket : public Packet { +public: + AddPaintingPacket() : entityId(0), xTile(0), yTile(0), zTile(0), dir(-1) { + + } + AddPaintingPacket(Painting* painting) { + entityId = painting->entityId; + xTile = painting->xTile; + yTile = painting->yTile; + zTile = painting->zTile; + dir = painting->dir; + motive = painting->motive->name; + } + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDPAINTING)); + bitStream->Write(entityId); + bitStream->Write(xTile); + bitStream->Write(yTile); + bitStream->Write(zTile); + bitStream->Write(dir); + RakNet::RakString rakMotive(motive.c_str()); + bitStream->Write(rakMotive); + } + void read(RakNet::BitStream* bitStream) { + bitStream->Read(entityId); + bitStream->Read(xTile); + bitStream->Read(yTile); + bitStream->Read(zTile); + bitStream->Read(dir); + RakNet::RakString rakMotive; + bitStream->Read(rakMotive); + motive = std::string(rakMotive.C_String()); + } + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (AddPaintingPacket*)this); + } +public: + int entityId; + int xTile, yTile, zTile; + int dir; + std::string motive; +}; + #endif /* NET_MINECRAFT_NETWORK_PACKET__AddPaintingPacket_H__ */ \ No newline at end of file diff --git a/src/network/packet/AddPlayerPacket.h b/src/network/packet/AddPlayerPacket.h index c7ec3aa..8f03e75 100755 --- a/src/network/packet/AddPlayerPacket.h +++ b/src/network/packet/AddPlayerPacket.h @@ -1,93 +1,93 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__AddPlayerPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__AddPlayerPacket_H__ - -#include "../Packet.h" -#include "../../world/entity/player/Player.h" -#include "../../world/entity/player/Inventory.h" - -class AddPlayerPacket : public Packet -{ -public: - AddPlayerPacket() - : _entityData(NULL) - {} - - AddPlayerPacket(const Player* p) - : owner(p->owner), - name(p->name.c_str()), - entityId(p->entityId), - x(p->x), - y(p->y - p->heightOffset), - z(p->z), - xRot(p->xRot), - yRot(p->yRot), - carriedItemId(0), - carriedItemAuxValue(0), - _entityData(p->getEntityData()) - { - if (ItemInstance* item = p->inventory->getSelected()) { - carriedItemId = item->id; - carriedItemAuxValue = item->getAuxValue(); - } - } - - ~AddPlayerPacket() { - for (unsigned int i = 0; i < unpack.size(); ++i) - delete unpack[i]; - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDPLAYER)); - - bitStream->Write(owner); - bitStream->Write(name); - bitStream->Write(entityId); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(PacketUtil::Rot_degreesToChar(yRot)); - bitStream->Write(PacketUtil::Rot_degreesToChar(xRot)); - bitStream->Write(carriedItemId); - bitStream->Write(carriedItemAuxValue); - RakDataOutput dos(*bitStream); - _entityData->packAll(&dos); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(owner); - bitStream->Read(name); - bitStream->Read(entityId); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - char rx, ry; - bitStream->Read(ry); - bitStream->Read(rx); - bitStream->Read(carriedItemId); - bitStream->Read(carriedItemAuxValue); - RakDataInput dis(*bitStream); - unpack = SynchedEntityData::unpack(&dis); - yRot = PacketUtil::Rot_degreesToChar(ry); - xRot = PacketUtil::Rot_charToDegrees(rx); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (AddPlayerPacket*)this); - } - - RakNet::RakNetGUID owner; - RakNet::RakString name; - int entityId; - float x, y, z; - float xRot, yRot; - short carriedItemId; - short carriedItemAuxValue; - SynchedEntityData::DataList unpack; -private: - const SynchedEntityData* _entityData; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__AddPlayerPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__AddPlayerPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__AddPlayerPacket_H__ + +#include "../Packet.h" +#include "../../world/entity/player/Player.h" +#include "../../world/entity/player/Inventory.h" + +class AddPlayerPacket : public Packet +{ +public: + AddPlayerPacket() + : _entityData(NULL) + {} + + AddPlayerPacket(const Player* p) + : owner(p->owner), + name(p->name.c_str()), + entityId(p->entityId), + x(p->x), + y(p->y - p->heightOffset), + z(p->z), + xRot(p->xRot), + yRot(p->yRot), + carriedItemId(0), + carriedItemAuxValue(0), + _entityData(p->getEntityData()) + { + if (ItemInstance* item = p->inventory->getSelected()) { + carriedItemId = item->id; + carriedItemAuxValue = item->getAuxValue(); + } + } + + ~AddPlayerPacket() { + for (unsigned int i = 0; i < unpack.size(); ++i) + delete unpack[i]; + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADDPLAYER)); + + bitStream->Write(owner); + bitStream->Write(name); + bitStream->Write(entityId); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(PacketUtil::Rot_degreesToChar(yRot)); + bitStream->Write(PacketUtil::Rot_degreesToChar(xRot)); + bitStream->Write(carriedItemId); + bitStream->Write(carriedItemAuxValue); + RakDataOutput dos(*bitStream); + _entityData->packAll(&dos); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(owner); + bitStream->Read(name); + bitStream->Read(entityId); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + char rx, ry; + bitStream->Read(ry); + bitStream->Read(rx); + bitStream->Read(carriedItemId); + bitStream->Read(carriedItemAuxValue); + RakDataInput dis(*bitStream); + unpack = SynchedEntityData::unpack(&dis); + yRot = PacketUtil::Rot_degreesToChar(ry); + xRot = PacketUtil::Rot_charToDegrees(rx); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (AddPlayerPacket*)this); + } + + RakNet::RakNetGUID owner; + RakNet::RakString name; + int entityId; + float x, y, z; + float xRot, yRot; + short carriedItemId; + short carriedItemAuxValue; + SynchedEntityData::DataList unpack; +private: + const SynchedEntityData* _entityData; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__AddPlayerPacket_H__*/ diff --git a/src/network/packet/AdventureSettingsPacket.h b/src/network/packet/AdventureSettingsPacket.h index 3ad0865..c6f8a10 100755 --- a/src/network/packet/AdventureSettingsPacket.h +++ b/src/network/packet/AdventureSettingsPacket.h @@ -1,67 +1,67 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__AdventureSettingsPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__AdventureSettingsPacket_H__ - -#include "../Packet.h" -#include "../../world/level/Level.h" - -class AdventureSettingsPacket: public Packet -{ -public: - enum Flags { - WorldImmutable = 1, - NoPvP = 2, - NoPvM = 4, - NoMvP = 8, - StaticTime = 16, - ShowNameTags = 32, - }; - - AdventureSettingsPacket() {} - - AdventureSettingsPacket(const AdventureSettings& settings) - : flags(0) - { - set(WorldImmutable, settings.immutableWorld); - set(NoPvP, settings.noPvP); - set(NoPvM, settings.noPvM); - set(NoMvP, settings.noMvP); - set(StaticTime, !settings.doTickTime); - set(ShowNameTags, settings.showNameTags); - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADVENTURESETTINGS)); - bitStream->Write(flags); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(flags); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (AdventureSettingsPacket*)this); - } - - void fillIn( AdventureSettings& adventureSettings ) const - { - adventureSettings.immutableWorld= isSet(WorldImmutable); - adventureSettings.noPvP = isSet(NoPvP); - adventureSettings.noPvM = isSet(NoPvM); - adventureSettings.noMvP = isSet(NoMvP); - adventureSettings.doTickTime = !isSet(StaticTime); - adventureSettings.showNameTags = isSet(ShowNameTags); - } - - unsigned int flags; - - void set(Flags flag, bool status) { status? set(flag) : clear(flag); } - void set(Flags flag) { flags |= flag; } - void toggle(Flags flag){ flags ^= flag; } - void clear(Flags flag) { flags &= ~flag; } - bool isSet(Flags flag) const { return (flags & flag) != 0; } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__AdventureSettingsPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__AdventureSettingsPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__AdventureSettingsPacket_H__ + +#include "../Packet.h" +#include "../../world/level/Level.h" + +class AdventureSettingsPacket: public Packet +{ +public: + enum Flags { + WorldImmutable = 1, + NoPvP = 2, + NoPvM = 4, + NoMvP = 8, + StaticTime = 16, + ShowNameTags = 32, + }; + + AdventureSettingsPacket() {} + + AdventureSettingsPacket(const AdventureSettings& settings) + : flags(0) + { + set(WorldImmutable, settings.immutableWorld); + set(NoPvP, settings.noPvP); + set(NoPvM, settings.noPvM); + set(NoMvP, settings.noMvP); + set(StaticTime, !settings.doTickTime); + set(ShowNameTags, settings.showNameTags); + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ADVENTURESETTINGS)); + bitStream->Write(flags); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(flags); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (AdventureSettingsPacket*)this); + } + + void fillIn( AdventureSettings& adventureSettings ) const + { + adventureSettings.immutableWorld= isSet(WorldImmutable); + adventureSettings.noPvP = isSet(NoPvP); + adventureSettings.noPvM = isSet(NoPvM); + adventureSettings.noMvP = isSet(NoMvP); + adventureSettings.doTickTime = !isSet(StaticTime); + adventureSettings.showNameTags = isSet(ShowNameTags); + } + + unsigned int flags; + + void set(Flags flag, bool status) { status? set(flag) : clear(flag); } + void set(Flags flag) { flags |= flag; } + void toggle(Flags flag){ flags ^= flag; } + void clear(Flags flag) { flags &= ~flag; } + bool isSet(Flags flag) const { return (flags & flag) != 0; } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__AdventureSettingsPacket_H__*/ diff --git a/src/network/packet/AnimatePacket.h b/src/network/packet/AnimatePacket.h index 81d3bfc..b9da10c 100755 --- a/src/network/packet/AnimatePacket.h +++ b/src/network/packet/AnimatePacket.h @@ -1,46 +1,46 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__AnimatePacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__AnimatePacket_H__ - -#include "../Packet.h" - -class AnimatePacket: public Packet -{ -public: - static const int Swing = 1; - static const int WAKE_UP = 3; - - int entityId; - char action; - - AnimatePacket() {} - - AnimatePacket(int action, int entityId) - : action(action), - entityId(entityId) - {} - AnimatePacket(int action, Entity* e) - : action(action), - entityId(e->entityId) - {} - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ANIMATE)); - - bitStream->Write(action); - bitStream->Write(entityId); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(action); - bitStream->Read(entityId); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (AnimatePacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__AnimatePacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__AnimatePacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__AnimatePacket_H__ + +#include "../Packet.h" + +class AnimatePacket: public Packet +{ +public: + static const int Swing = 1; + static const int WAKE_UP = 3; + + int entityId; + char action; + + AnimatePacket() {} + + AnimatePacket(int action, int entityId) + : action(action), + entityId(entityId) + {} + AnimatePacket(int action, Entity* e) + : action(action), + entityId(e->entityId) + {} + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ANIMATE)); + + bitStream->Write(action); + bitStream->Write(entityId); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(action); + bitStream->Read(entityId); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (AnimatePacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__AnimatePacket_H__*/ diff --git a/src/network/packet/ChatPacket.h b/src/network/packet/ChatPacket.h index 5d992a8..502fa70 100755 --- a/src/network/packet/ChatPacket.h +++ b/src/network/packet/ChatPacket.h @@ -1,41 +1,41 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ChatPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ChatPacket_H__ -#include "../Packet.h" -#include "../..//world/entity/player/Player.h" - -class ChatPacket: public Packet -{ -public: - static const int MAX_CHAT_LENGTH = 100; - static const int MAX_LENGTH = MAX_CHAT_LENGTH + Player::MAX_NAME_LENGTH; - ChatPacket() { - } - - ChatPacket(std::string message, bool isSystem = true) { - if(message.length() > MAX_LENGTH) { - message = message.substr(0, MAX_LENGTH); - } - this->message = message; - this->isSystem = isSystem; - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CHAT)); - bitStream->Write(message.c_str()); - } - - void read(RakNet::BitStream* bitStream) { - char buff[MAX_LENGTH + 30]; - bitStream->Read(buff); - message = buff; - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (ChatPacket*)this); - } - - std::string message; - bool isSystem; -}; - +#ifndef NET_MINECRAFT_NETWORK_PACKET__ChatPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ChatPacket_H__ +#include "../Packet.h" +#include "../..//world/entity/player/Player.h" + +class ChatPacket: public Packet +{ +public: + static const int MAX_CHAT_LENGTH = 100; + static const int MAX_LENGTH = MAX_CHAT_LENGTH + Player::MAX_NAME_LENGTH; + ChatPacket() { + } + + ChatPacket(std::string message, bool isSystem = true) { + if(message.length() > MAX_LENGTH) { + message = message.substr(0, MAX_LENGTH); + } + this->message = message; + this->isSystem = isSystem; + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CHAT)); + bitStream->Write(message.c_str()); + } + + void read(RakNet::BitStream* bitStream) { + char buff[MAX_LENGTH + 30]; + bitStream->Read(buff); + message = buff; + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (ChatPacket*)this); + } + + std::string message; + bool isSystem; +}; + #endif /* NET_MINECRAFT_NETWORK_PACKET__ChatPacket_H__ */ \ No newline at end of file diff --git a/src/network/packet/ChunkDataPacket.h b/src/network/packet/ChunkDataPacket.h index 51f1719..03e6654 100755 --- a/src/network/packet/ChunkDataPacket.h +++ b/src/network/packet/ChunkDataPacket.h @@ -1,77 +1,77 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ChunkDataPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ChunkDataPacket_H__ - -#include "../Packet.h" -#include "../../world/level/chunk/LevelChunk.h" - -class ChunkDataPacket : public Packet -{ -public: - - int x, z; - RakNet::BitStream chunkData; - LevelChunk* chunk; - - ChunkDataPacket() - { - } - - ChunkDataPacket(int x, int z, LevelChunk* chunk) - : x(x), - z(z), - chunk(chunk) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CHUNKDATA)); - - bitStream->Write(x); - bitStream->Write(z); - - unsigned char* blockIds = chunk->getBlockData(); - DataLayer& blockData = chunk->data; - - const int setSize = LEVEL_HEIGHT / 8; - const int setShift = 4; // power of LEVEL_HEIGHT / 8 - - chunkData.Reset(); - for (int i = 0; i < CHUNK_COLUMNS; i++) - { - unsigned char updateBits = chunk->updateMap[i]; - chunkData.Write(updateBits); - - if (updateBits > 0) - { - int colDataPosition = (i % CHUNK_WIDTH) << 11 | (i / CHUNK_WIDTH) << 7; - - for (int set = 0; set < 8; set++) - { - if ((updateBits & (1 << set)) != 0) - { - chunkData.Write((const char*)(&blockIds[colDataPosition + (set << setShift)]), setSize); - // block data is only 4 bits per block - chunkData.Write((const char*)(&blockData.data[(colDataPosition + (set << setShift)) >> 1]), setSize >> 1); - } - } - } - } - - bitStream->Write(chunkData); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(x); - bitStream->Read(z); - bitStream->Read(chunkData); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (ChunkDataPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__ChunkDataPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__ChunkDataPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ChunkDataPacket_H__ + +#include "../Packet.h" +#include "../../world/level/chunk/LevelChunk.h" + +class ChunkDataPacket : public Packet +{ +public: + + int x, z; + RakNet::BitStream chunkData; + LevelChunk* chunk; + + ChunkDataPacket() + { + } + + ChunkDataPacket(int x, int z, LevelChunk* chunk) + : x(x), + z(z), + chunk(chunk) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CHUNKDATA)); + + bitStream->Write(x); + bitStream->Write(z); + + unsigned char* blockIds = chunk->getBlockData(); + DataLayer& blockData = chunk->data; + + const int setSize = LEVEL_HEIGHT / 8; + const int setShift = 4; // power of LEVEL_HEIGHT / 8 + + chunkData.Reset(); + for (int i = 0; i < CHUNK_COLUMNS; i++) + { + unsigned char updateBits = chunk->updateMap[i]; + chunkData.Write(updateBits); + + if (updateBits > 0) + { + int colDataPosition = (i % CHUNK_WIDTH) << 11 | (i / CHUNK_WIDTH) << 7; + + for (int set = 0; set < 8; set++) + { + if ((updateBits & (1 << set)) != 0) + { + chunkData.Write((const char*)(&blockIds[colDataPosition + (set << setShift)]), setSize); + // block data is only 4 bits per block + chunkData.Write((const char*)(&blockData.data[(colDataPosition + (set << setShift)) >> 1]), setSize >> 1); + } + } + } + } + + bitStream->Write(chunkData); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(x); + bitStream->Read(z); + bitStream->Read(chunkData); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (ChunkDataPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__ChunkDataPacket_H__*/ diff --git a/src/network/packet/ContainerAckPacket.h b/src/network/packet/ContainerAckPacket.h index 7d60de6..aded442 100755 --- a/src/network/packet/ContainerAckPacket.h +++ b/src/network/packet/ContainerAckPacket.h @@ -1,43 +1,43 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerAckPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ContainerAckPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -class ContainerAckPacket: public Packet -{ -public: - ContainerAckPacket() { - } - - ContainerAckPacket(int containerId, short uid, bool accepted) - : containerId(containerId), - uid(uid), - accepted(accepted) - { - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERACK)); - bitStream->Write(containerId); - bitStream->Write(uid); - bitStream->Write(accepted); - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(containerId); - bitStream->Read(uid); - bitStream->Read(accepted); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (ContainerAckPacket*)this); - } - - short uid; - unsigned char containerId; - bool accepted; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerAckPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerAckPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ContainerAckPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +class ContainerAckPacket: public Packet +{ +public: + ContainerAckPacket() { + } + + ContainerAckPacket(int containerId, short uid, bool accepted) + : containerId(containerId), + uid(uid), + accepted(accepted) + { + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERACK)); + bitStream->Write(containerId); + bitStream->Write(uid); + bitStream->Write(accepted); + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(containerId); + bitStream->Read(uid); + bitStream->Read(accepted); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (ContainerAckPacket*)this); + } + + short uid; + unsigned char containerId; + bool accepted; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerAckPacket_H__*/ diff --git a/src/network/packet/ContainerClosePacket.h b/src/network/packet/ContainerClosePacket.h index cdfe865..0ee63ff 100755 --- a/src/network/packet/ContainerClosePacket.h +++ b/src/network/packet/ContainerClosePacket.h @@ -1,35 +1,35 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerClosePacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ContainerClosePacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -class ContainerClosePacket: public Packet -{ -public: - ContainerClosePacket() { - } - - ContainerClosePacket(int containerId) - : containerId(containerId) - { - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(containerId); - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERCLOSE)); - bitStream->Write(containerId); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (ContainerClosePacket*)this); - } - - unsigned char containerId; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerClosePacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerClosePacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ContainerClosePacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +class ContainerClosePacket: public Packet +{ +public: + ContainerClosePacket() { + } + + ContainerClosePacket(int containerId) + : containerId(containerId) + { + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(containerId); + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERCLOSE)); + bitStream->Write(containerId); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (ContainerClosePacket*)this); + } + + unsigned char containerId; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerClosePacket_H__*/ diff --git a/src/network/packet/ContainerOpenPacket.h b/src/network/packet/ContainerOpenPacket.h index 1009a37..838d974 100755 --- a/src/network/packet/ContainerOpenPacket.h +++ b/src/network/packet/ContainerOpenPacket.h @@ -1,47 +1,47 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerOpenPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ContainerOpenPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -class ContainerOpenPacket: public Packet -{ -public: - ContainerOpenPacket() { - } - - ContainerOpenPacket(int containerId, int type, const std::string& title, int size) - : containerId(containerId), - type(type), - title(title.c_str()), - size(size) - { - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINEROPEN)); - bitStream->Write(containerId); - bitStream->Write(type); - bitStream->Write(size); - bitStream->Write(title); - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(containerId); - bitStream->Read(type); - bitStream->Read(size); - bitStream->Read(title); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (ContainerOpenPacket*)this); - } - - RakNet::RakString title; - unsigned char containerId; - unsigned char type; - unsigned char size; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerOpenPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerOpenPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ContainerOpenPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +class ContainerOpenPacket: public Packet +{ +public: + ContainerOpenPacket() { + } + + ContainerOpenPacket(int containerId, int type, const std::string& title, int size) + : containerId(containerId), + type(type), + title(title.c_str()), + size(size) + { + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINEROPEN)); + bitStream->Write(containerId); + bitStream->Write(type); + bitStream->Write(size); + bitStream->Write(title); + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(containerId); + bitStream->Read(type); + bitStream->Read(size); + bitStream->Read(title); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (ContainerOpenPacket*)this); + } + + RakNet::RakString title; + unsigned char containerId; + unsigned char type; + unsigned char size; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerOpenPacket_H__*/ diff --git a/src/network/packet/ContainerSetContentPacket.h b/src/network/packet/ContainerSetContentPacket.h index 76dd26b..cbab653 100755 --- a/src/network/packet/ContainerSetContentPacket.h +++ b/src/network/packet/ContainerSetContentPacket.h @@ -1,44 +1,44 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerSetContentPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ContainerSetContentPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -class ContainerSetContentPacket: public Packet -{ -public: - ContainerSetContentPacket() { - } - - ContainerSetContentPacket(int containerId, const std::vector& newItems) - : containerId(containerId), - items(newItems.begin(), newItems.end()) - { - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERSETCONTENT)); - bitStream->Write(containerId); - bitStream->Write((short)items.size()); - for (unsigned int i = 0; i < items.size(); ++i) - PacketUtil::writeItemInstance(items[i], bitStream); - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(containerId); - short numItems; - bitStream->Read(numItems); - for (int i = 0; i < numItems; ++i) - items.push_back( PacketUtil::readItemInstance(bitStream) ); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (ContainerSetContentPacket*)this); - } - - unsigned char containerId; - std::vector items; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerSetContentPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerSetContentPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ContainerSetContentPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +class ContainerSetContentPacket: public Packet +{ +public: + ContainerSetContentPacket() { + } + + ContainerSetContentPacket(int containerId, const std::vector& newItems) + : containerId(containerId), + items(newItems.begin(), newItems.end()) + { + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERSETCONTENT)); + bitStream->Write(containerId); + bitStream->Write((short)items.size()); + for (unsigned int i = 0; i < items.size(); ++i) + PacketUtil::writeItemInstance(items[i], bitStream); + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(containerId); + short numItems; + bitStream->Read(numItems); + for (int i = 0; i < numItems; ++i) + items.push_back( PacketUtil::readItemInstance(bitStream) ); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (ContainerSetContentPacket*)this); + } + + unsigned char containerId; + std::vector items; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerSetContentPacket_H__*/ diff --git a/src/network/packet/ContainerSetDataPacket.h b/src/network/packet/ContainerSetDataPacket.h index d85b28d..2e54689 100755 --- a/src/network/packet/ContainerSetDataPacket.h +++ b/src/network/packet/ContainerSetDataPacket.h @@ -1,43 +1,43 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerSetDataPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ContainerSetDataPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -class ContainerSetDataPacket: public Packet -{ -public: - ContainerSetDataPacket() { - } - - ContainerSetDataPacket(int containerId, int id, int value) - : containerId(containerId), - id(id), - value(value) - { - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERSETDATA)); - bitStream->Write(containerId); - bitStream->Write(id); - bitStream->Write(value); - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(containerId); - bitStream->Read(id); - bitStream->Read(value); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (ContainerSetDataPacket*)this); - } - - short id; - short value; - unsigned char containerId; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerSetDataPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerSetDataPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ContainerSetDataPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +class ContainerSetDataPacket: public Packet +{ +public: + ContainerSetDataPacket() { + } + + ContainerSetDataPacket(int containerId, int id, int value) + : containerId(containerId), + id(id), + value(value) + { + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERSETDATA)); + bitStream->Write(containerId); + bitStream->Write(id); + bitStream->Write(value); + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(containerId); + bitStream->Read(id); + bitStream->Read(value); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (ContainerSetDataPacket*)this); + } + + short id; + short value; + unsigned char containerId; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerSetDataPacket_H__*/ diff --git a/src/network/packet/ContainerSetSlotPacket.h b/src/network/packet/ContainerSetSlotPacket.h index fd4c73e..61bef82 100755 --- a/src/network/packet/ContainerSetSlotPacket.h +++ b/src/network/packet/ContainerSetSlotPacket.h @@ -1,64 +1,64 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerSetSlotPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ContainerSetSlotPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" -#include "../../world/item/ItemInstance.h" - -// Note: This can be seen as "ContainerWantSetSlotPacket" when sent from -// client to server. Currently, the client handles side-effects relating -// to it's own inventory, regardless of the success of the operation. -class ContainerSetSlotPacket: public Packet -{ -public: - static const char SETTYPE_SET = 0; - static const char SETTYPE_ADD = 1; - //static const int SETTYPE_SUB = 2; - static const char SETTYPE_TAKE = 5; - - ContainerSetSlotPacket() { - } - - //@todo: pointer parameter? - ContainerSetSlotPacket(int containerId, int slot, const ItemInstance& item) - : containerId(containerId), - slot(slot), - item(item), - setType(SETTYPE_SET) - //item(item? *item : ItemInstance()) - { - } - ContainerSetSlotPacket(char setType, int containerId, int slot, const ItemInstance& item) - : setType(setType), - containerId(containerId), - slot(slot), - item(item) - //item(item? *item : ItemInstance()) - { - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERSETSLOT)); - bitStream->Write(containerId); - bitStream->Write(slot); - PacketUtil::writeItemInstance(item, bitStream); - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(containerId); - bitStream->Read(slot); - item = PacketUtil::readItemInstance(bitStream); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (ContainerSetSlotPacket*)this); - } - - unsigned char setType; - unsigned char containerId; - short slot; - ItemInstance item; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerSetSlotPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__ContainerSetSlotPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ContainerSetSlotPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" +#include "../../world/item/ItemInstance.h" + +// Note: This can be seen as "ContainerWantSetSlotPacket" when sent from +// client to server. Currently, the client handles side-effects relating +// to it's own inventory, regardless of the success of the operation. +class ContainerSetSlotPacket: public Packet +{ +public: + static const char SETTYPE_SET = 0; + static const char SETTYPE_ADD = 1; + //static const int SETTYPE_SUB = 2; + static const char SETTYPE_TAKE = 5; + + ContainerSetSlotPacket() { + } + + //@todo: pointer parameter? + ContainerSetSlotPacket(int containerId, int slot, const ItemInstance& item) + : containerId(containerId), + slot(slot), + item(item), + setType(SETTYPE_SET) + //item(item? *item : ItemInstance()) + { + } + ContainerSetSlotPacket(char setType, int containerId, int slot, const ItemInstance& item) + : setType(setType), + containerId(containerId), + slot(slot), + item(item) + //item(item? *item : ItemInstance()) + { + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_CONTAINERSETSLOT)); + bitStream->Write(containerId); + bitStream->Write(slot); + PacketUtil::writeItemInstance(item, bitStream); + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(containerId); + bitStream->Read(slot); + item = PacketUtil::readItemInstance(bitStream); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (ContainerSetSlotPacket*)this); + } + + unsigned char setType; + unsigned char containerId; + short slot; + ItemInstance item; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__ContainerSetSlotPacket_H__*/ diff --git a/src/network/packet/DropItemPacket.h b/src/network/packet/DropItemPacket.h index 9084efa..19775b9 100755 --- a/src/network/packet/DropItemPacket.h +++ b/src/network/packet/DropItemPacket.h @@ -1,45 +1,45 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__DropItemPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__DropItemPacket_H__ - -#include "../Packet.h" - -class DropItemPacket: public Packet -{ -public: - DropItemPacket() - { - } - - DropItemPacket(int entityId, const ItemInstance& item) - : entityId(entityId), - item(item), - dropType(0) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_DROPITEM)); - bitStream->Write(entityId); - bitStream->Write(dropType); - PacketUtil::writeItemInstance(item, bitStream); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(dropType); - item = PacketUtil::readItemInstance(bitStream); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (DropItemPacket*)this); - } - - int entityId; - unsigned char dropType; - ItemInstance item; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__DropItemPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__DropItemPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__DropItemPacket_H__ + +#include "../Packet.h" + +class DropItemPacket: public Packet +{ +public: + DropItemPacket() + { + } + + DropItemPacket(int entityId, const ItemInstance& item) + : entityId(entityId), + item(item), + dropType(0) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_DROPITEM)); + bitStream->Write(entityId); + bitStream->Write(dropType); + PacketUtil::writeItemInstance(item, bitStream); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(dropType); + item = PacketUtil::readItemInstance(bitStream); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (DropItemPacket*)this); + } + + int entityId; + unsigned char dropType; + ItemInstance item; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__DropItemPacket_H__*/ diff --git a/src/network/packet/EntityEventPacket.h b/src/network/packet/EntityEventPacket.h index bc2759c..209d3e7 100755 --- a/src/network/packet/EntityEventPacket.h +++ b/src/network/packet/EntityEventPacket.h @@ -1,40 +1,40 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__EntityEventPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__EntityEventPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -class EntityEventPacket: public Packet { -public: - EntityEventPacket() {} - - EntityEventPacket(int entityId, char eventId) - : entityId(entityId), - eventId(eventId) - {} - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ENTITYEVENT)); - bitStream->Write(entityId); - bitStream->Write(eventId); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(eventId); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (EntityEventPacket*)this); - } - - int entityId; - unsigned char eventId; - -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__EntityEventPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__EntityEventPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__EntityEventPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +class EntityEventPacket: public Packet { +public: + EntityEventPacket() {} + + EntityEventPacket(int entityId, char eventId) + : entityId(entityId), + eventId(eventId) + {} + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_ENTITYEVENT)); + bitStream->Write(entityId); + bitStream->Write(eventId); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(eventId); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (EntityEventPacket*)this); + } + + int entityId; + unsigned char eventId; + +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__EntityEventPacket_H__*/ diff --git a/src/network/packet/ExplodePacket.h b/src/network/packet/ExplodePacket.h index 9cd9adc..f379fdd 100755 --- a/src/network/packet/ExplodePacket.h +++ b/src/network/packet/ExplodePacket.h @@ -1,76 +1,76 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ExplodePacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ExplodePacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" -#include "../../world/level/Explosion.h" - -class ExplodePacket: public Packet -{ -public: - float x, y, z; - float r; - std::vector toBlow; - - ExplodePacket() {} - - ExplodePacket(float x, float y, float z, float r, const TilePosSet& tiles) - : x(x), - y(y), - z(z), - r(r), - toBlow(tiles.begin(), tiles.end()) - {} - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_EXPLODE)); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(r); - int xp = (int)x; - int yp = (int)y; - int zp = (int)z; - - int count = (int)toBlow.size(); - bitStream->Write(count); - for (int i = 0; i < count; ++i) { - const TilePos& tp = toBlow[i]; - bitStream->Write((signed char)(tp.x - xp)); - bitStream->Write((signed char)(tp.y - yp)); - bitStream->Write((signed char)(tp.z - zp)); - } - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - bitStream->Read(r); - int xp = (int)x; - int yp = (int)y; - int zp = (int)z; - - // Write the tileset for the exploded tiles - int count; - bitStream->Read(count); - toBlow.reserve(count); - for (int i = 0; i < count; ++i) { - signed char xx,yy,zz; - bitStream->Read(xx); - bitStream->Read(yy); - bitStream->Read(zz); - toBlow.push_back( TilePos(xp + xx, yp + yy, zp + zz) ); - } - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (ExplodePacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__ExplodePacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__ExplodePacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ExplodePacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" +#include "../../world/level/Explosion.h" + +class ExplodePacket: public Packet +{ +public: + float x, y, z; + float r; + std::vector toBlow; + + ExplodePacket() {} + + ExplodePacket(float x, float y, float z, float r, const TilePosSet& tiles) + : x(x), + y(y), + z(z), + r(r), + toBlow(tiles.begin(), tiles.end()) + {} + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_EXPLODE)); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(r); + int xp = (int)x; + int yp = (int)y; + int zp = (int)z; + + int count = (int)toBlow.size(); + bitStream->Write(count); + for (int i = 0; i < count; ++i) { + const TilePos& tp = toBlow[i]; + bitStream->Write((signed char)(tp.x - xp)); + bitStream->Write((signed char)(tp.y - yp)); + bitStream->Write((signed char)(tp.z - zp)); + } + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + bitStream->Read(r); + int xp = (int)x; + int yp = (int)y; + int zp = (int)z; + + // Write the tileset for the exploded tiles + int count; + bitStream->Read(count); + toBlow.reserve(count); + for (int i = 0; i < count; ++i) { + signed char xx,yy,zz; + bitStream->Read(xx); + bitStream->Read(yy); + bitStream->Read(zz); + toBlow.push_back( TilePos(xp + xx, yp + yy, zp + zz) ); + } + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (ExplodePacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__ExplodePacket_H__*/ diff --git a/src/network/packet/HurtArmorPacket.h b/src/network/packet/HurtArmorPacket.h index 5088905..c513c2c 100755 --- a/src/network/packet/HurtArmorPacket.h +++ b/src/network/packet/HurtArmorPacket.h @@ -1,32 +1,32 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__HurtArmorPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__HurtArmorPacket_H__ - -#include "../Packet.h" - -class HurtArmorPacket: public Packet -{ -public: - HurtArmorPacket() {} - - HurtArmorPacket(int dmg) - : dmg(dmg) - {} - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_HURTARMOR)); - - bitStream->Write(dmg); - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(dmg); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (HurtArmorPacket*)this); - } - - signed char dmg; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__HurtArmorPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__HurtArmorPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__HurtArmorPacket_H__ + +#include "../Packet.h" + +class HurtArmorPacket: public Packet +{ +public: + HurtArmorPacket() {} + + HurtArmorPacket(int dmg) + : dmg(dmg) + {} + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_HURTARMOR)); + + bitStream->Write(dmg); + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(dmg); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (HurtArmorPacket*)this); + } + + signed char dmg; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__HurtArmorPacket_H__*/ diff --git a/src/network/packet/InteractPacket.h b/src/network/packet/InteractPacket.h index 9daf160..85ba141 100755 --- a/src/network/packet/InteractPacket.h +++ b/src/network/packet/InteractPacket.h @@ -1,47 +1,47 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__InteractPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__InteractPacket_H__ - -#include "../Packet.h" - -class InteractPacket : public Packet -{ -public: - static const int Interact = 1; - static const int Attack = 2; - - char action; - int sourceId; - int targetId; - - InteractPacket() { - } - - InteractPacket(char action, int sourceId, int targetId) - : action(action), - sourceId(sourceId), - targetId(targetId) - {} - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_INTERACT)); - - bitStream->Write(action); - bitStream->Write(sourceId); - bitStream->Write(targetId); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(action); - bitStream->Read(sourceId); - bitStream->Read(targetId); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (InteractPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__InteractPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__InteractPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__InteractPacket_H__ + +#include "../Packet.h" + +class InteractPacket : public Packet +{ +public: + static const int Interact = 1; + static const int Attack = 2; + + char action; + int sourceId; + int targetId; + + InteractPacket() { + } + + InteractPacket(char action, int sourceId, int targetId) + : action(action), + sourceId(sourceId), + targetId(targetId) + {} + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_INTERACT)); + + bitStream->Write(action); + bitStream->Write(sourceId); + bitStream->Write(targetId); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(action); + bitStream->Read(sourceId); + bitStream->Read(targetId); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (InteractPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__InteractPacket_H__*/ diff --git a/src/network/packet/LevelEventPacket.h b/src/network/packet/LevelEventPacket.h index 2cb5741..071248a 100755 --- a/src/network/packet/LevelEventPacket.h +++ b/src/network/packet/LevelEventPacket.h @@ -1,49 +1,49 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__LevelEventPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__LevelEventPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -class LevelEventPacket: public Packet { -public: - LevelEventPacket() {} - - LevelEventPacket(int eventId, int x, int y, int z, int data) - : eventId(eventId), - x(x), - y(y), - z(z), - data(data) - {} - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_LEVELEVENT)); - bitStream->Write(eventId); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(data); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(eventId); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - bitStream->Read(data); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (LevelEventPacket*)this); - } - - short eventId; - short x, y, z; - int data; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__LevelEventPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__LevelEventPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__LevelEventPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +class LevelEventPacket: public Packet { +public: + LevelEventPacket() {} + + LevelEventPacket(int eventId, int x, int y, int z, int data) + : eventId(eventId), + x(x), + y(y), + z(z), + data(data) + {} + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_LEVELEVENT)); + bitStream->Write(eventId); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(data); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(eventId); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + bitStream->Read(data); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (LevelEventPacket*)this); + } + + short eventId; + short x, y, z; + int data; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__LevelEventPacket_H__*/ diff --git a/src/network/packet/LoginPacket.h b/src/network/packet/LoginPacket.h index b44a44a..da609a6 100755 --- a/src/network/packet/LoginPacket.h +++ b/src/network/packet/LoginPacket.h @@ -1,51 +1,51 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__LoginPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__LoginPacket_H__ - -#include "../Packet.h" - -class LoginPacket : public Packet -{ -public: - RakNet::RakString clientName; - int clientNetworkVersion; - int clientNetworkLowestSupportedVersion; - - LoginPacket() - : clientNetworkVersion(-1), - clientNetworkLowestSupportedVersion(-1) - { - } - - LoginPacket(const RakNet::RakString& clientName, int clientVersion) - : clientName(clientName), - clientNetworkVersion(clientVersion), - clientNetworkLowestSupportedVersion(clientVersion) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_LOGIN)); - bitStream->Write(clientName); - bitStream->Write(clientNetworkVersion); - bitStream->Write(clientNetworkLowestSupportedVersion); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(clientName); - // First versions didn't send the client version - //LOGI("unread: %d\n", bitStream->GetNumberOfUnreadBits()); - if (bitStream->GetNumberOfUnreadBits() > 0) { - bitStream->Read(clientNetworkVersion); - bitStream->Read(clientNetworkLowestSupportedVersion); - } - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (LoginPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__LoginPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__LoginPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__LoginPacket_H__ + +#include "../Packet.h" + +class LoginPacket : public Packet +{ +public: + RakNet::RakString clientName; + int clientNetworkVersion; + int clientNetworkLowestSupportedVersion; + + LoginPacket() + : clientNetworkVersion(-1), + clientNetworkLowestSupportedVersion(-1) + { + } + + LoginPacket(const RakNet::RakString& clientName, int clientVersion) + : clientName(clientName), + clientNetworkVersion(clientVersion), + clientNetworkLowestSupportedVersion(clientVersion) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_LOGIN)); + bitStream->Write(clientName); + bitStream->Write(clientNetworkVersion); + bitStream->Write(clientNetworkLowestSupportedVersion); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(clientName); + // First versions didn't send the client version + //LOGI("unread: %d\n", bitStream->GetNumberOfUnreadBits()); + if (bitStream->GetNumberOfUnreadBits() > 0) { + bitStream->Read(clientNetworkVersion); + bitStream->Read(clientNetworkLowestSupportedVersion); + } + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (LoginPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__LoginPacket_H__*/ diff --git a/src/network/packet/LoginStatusPacket.h b/src/network/packet/LoginStatusPacket.h index b194f8f..4d85ae6 100755 --- a/src/network/packet/LoginStatusPacket.h +++ b/src/network/packet/LoginStatusPacket.h @@ -1,40 +1,40 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__LoginStatusPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__LoginStatusPacket_H__ - -#include "../Packet.h" - -namespace LoginStatus { - const int Success = 0; - const int Failed_ClientOld = 1; - const int Failed_ServerOld = 2; -} - -class LoginStatusPacket : public Packet { -public: - LoginStatusPacket() - { - } - LoginStatusPacket(int status) - : status(status) - {} - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_LOGINSTATUS)); - bitStream->Write(status); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(status); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (LoginStatusPacket*)this); - } - - int status; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__LoginStatusPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__LoginStatusPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__LoginStatusPacket_H__ + +#include "../Packet.h" + +namespace LoginStatus { + const int Success = 0; + const int Failed_ClientOld = 1; + const int Failed_ServerOld = 2; +} + +class LoginStatusPacket : public Packet { +public: + LoginStatusPacket() + { + } + LoginStatusPacket(int status) + : status(status) + {} + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_LOGINSTATUS)); + bitStream->Write(status); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(status); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (LoginStatusPacket*)this); + } + + int status; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__LoginStatusPacket_H__*/ diff --git a/src/network/packet/MessagePacket.h b/src/network/packet/MessagePacket.h index 49a274e..3f95fbe 100755 --- a/src/network/packet/MessagePacket.h +++ b/src/network/packet/MessagePacket.h @@ -1,39 +1,39 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__MessagePacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__MessagePacket_H__ - -#include "../Packet.h" - -class MessagePacket : public Packet -{ -public: - - RakNet::RakString message; - - MessagePacket() - { - } - - MessagePacket(const RakNet::RakString& message) - : message(message) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_MESSAGE)); - - bitStream->Write(message); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(message); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (MessagePacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__MessagePacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__MessagePacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__MessagePacket_H__ + +#include "../Packet.h" + +class MessagePacket : public Packet +{ +public: + + RakNet::RakString message; + + MessagePacket() + { + } + + MessagePacket(const RakNet::RakString& message) + : message(message) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_MESSAGE)); + + bitStream->Write(message); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(message); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (MessagePacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__MessagePacket_H__*/ diff --git a/src/network/packet/MoveEntityPacket.h b/src/network/packet/MoveEntityPacket.h index 7a619f2..897d890 100755 --- a/src/network/packet/MoveEntityPacket.h +++ b/src/network/packet/MoveEntityPacket.h @@ -1,78 +1,78 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__MoveEntityPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__MoveEntityPacket_H__ - -#include "../Packet.h" - -class MoveEntityPacket: public Packet -{ -public: - MoveEntityPacket() - : hasRot(false) - {} - - // PACKET_MOVEENTITY is unknown and undefined (and not used) - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_MOVEENTITY)); - } - - void read(RakNet::BitStream* bitStream) {}; - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (MoveEntityPacket*)this); - } - - int entityId; - float x, y, z; - float xRot; - float yRot; - bool hasRot; -}; - -class MoveEntityPacket_PosRot: public MoveEntityPacket -{ - typedef MoveEntityPacket super; -public: - MoveEntityPacket_PosRot() { - hasRot = true; - } - - //MoveEntityPacket_PosRot(int entityId, float x, float y, float z, float yRot, float xRot) { - // set(entityId, x, y, z, yRot, xRot); - //} - - MoveEntityPacket_PosRot(const Entity* e) { - set(e->entityId, e->x, e->y - e->heightOffset, e->z, e->yRot, e->xRot); - } - - void set(int entityId, float x, float y, float z, float yRot, float xRot) { - this->entityId = entityId; - this->x = x; - this->y = y; - this->z = z; - this->xRot = xRot; - this->yRot = yRot; - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_MOVEENTITY_POSROT)); - - bitStream->Write(entityId); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(yRot); - bitStream->Write(xRot); - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(entityId); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - bitStream->Read(yRot); - bitStream->Read(xRot); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__MoveEntityPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__MoveEntityPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__MoveEntityPacket_H__ + +#include "../Packet.h" + +class MoveEntityPacket: public Packet +{ +public: + MoveEntityPacket() + : hasRot(false) + {} + + // PACKET_MOVEENTITY is unknown and undefined (and not used) + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_MOVEENTITY)); + } + + void read(RakNet::BitStream* bitStream) {}; + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (MoveEntityPacket*)this); + } + + int entityId; + float x, y, z; + float xRot; + float yRot; + bool hasRot; +}; + +class MoveEntityPacket_PosRot: public MoveEntityPacket +{ + typedef MoveEntityPacket super; +public: + MoveEntityPacket_PosRot() { + hasRot = true; + } + + //MoveEntityPacket_PosRot(int entityId, float x, float y, float z, float yRot, float xRot) { + // set(entityId, x, y, z, yRot, xRot); + //} + + MoveEntityPacket_PosRot(const Entity* e) { + set(e->entityId, e->x, e->y - e->heightOffset, e->z, e->yRot, e->xRot); + } + + void set(int entityId, float x, float y, float z, float yRot, float xRot) { + this->entityId = entityId; + this->x = x; + this->y = y; + this->z = z; + this->xRot = xRot; + this->yRot = yRot; + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_MOVEENTITY_POSROT)); + + bitStream->Write(entityId); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(yRot); + bitStream->Write(xRot); + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(entityId); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + bitStream->Read(yRot); + bitStream->Read(xRot); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__MoveEntityPacket_H__*/ diff --git a/src/network/packet/MovePlayerPacket.h b/src/network/packet/MovePlayerPacket.h index 8555432..3265bf9 100755 --- a/src/network/packet/MovePlayerPacket.h +++ b/src/network/packet/MovePlayerPacket.h @@ -1,54 +1,54 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__MovePlayerPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__MovePlayerPacket_H__ - -#include "../Packet.h" - -class MovePlayerPacket : public Packet -{ -public: - - int entityId; - float x, y, z, xRot, yRot; - - MovePlayerPacket() - { - } - - MovePlayerPacket(int entityId, float x, float y, float z, float xRot, float yRot) - : entityId(entityId), - x(x), - y(y), - z(z), - xRot(xRot), - yRot(yRot) - {} - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_MOVEPLAYER)); - - bitStream->Write(entityId); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(yRot); - bitStream->Write(xRot); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - bitStream->Read(yRot); - bitStream->Read(xRot); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (MovePlayerPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__MovePlayerPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__MovePlayerPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__MovePlayerPacket_H__ + +#include "../Packet.h" + +class MovePlayerPacket : public Packet +{ +public: + + int entityId; + float x, y, z, xRot, yRot; + + MovePlayerPacket() + { + } + + MovePlayerPacket(int entityId, float x, float y, float z, float xRot, float yRot) + : entityId(entityId), + x(x), + y(y), + z(z), + xRot(xRot), + yRot(yRot) + {} + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_MOVEPLAYER)); + + bitStream->Write(entityId); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(yRot); + bitStream->Write(xRot); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + bitStream->Read(yRot); + bitStream->Read(xRot); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (MovePlayerPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__MovePlayerPacket_H__*/ diff --git a/src/network/packet/PacketInclude.h b/src/network/packet/PacketInclude.h index 09aeae8..b7e7e06 100755 --- a/src/network/packet/PacketInclude.h +++ b/src/network/packet/PacketInclude.h @@ -1,54 +1,54 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__PacketInclude_H__ -#define NET_MINECRAFT_NETWORK_PACKET__PacketInclude_H__ - -#include "AddEntityPacket.h" -#include "AddItemEntityPacket.h" -#include "AddMobPacket.h" -#include "AddPaintingPacket.h" -#include "AddPlayerPacket.h" -#include "AnimatePacket.h" -#include "AdventureSettingsPacket.h" -#include "ChatPacket.h" -#include "ContainerAckPacket.h" -#include "ContainerOpenPacket.h" -#include "ContainerClosePacket.h" -#include "ContainerSetDataPacket.h" -#include "ContainerSetSlotPacket.h" -#include "ContainerSetContentPacket.h" -#include "ChunkDataPacket.h" -#include "DropItemPacket.h" -#include "EntityEventPacket.h" -#include "ExplodePacket.h" -#include "HurtArmorPacket.h" -#include "InteractPacket.h" -#include "LevelEventPacket.h" -#include "LoginPacket.h" -#include "LoginStatusPacket.h" -#include "MessagePacket.h" -#include "MoveEntityPacket.h" -#include "MovePlayerPacket.h" -#include "PlaceBlockPacket.h" -#include "PlayerActionPacket.h" -#include "PlayerEquipmentPacket.h" -#include "PlayerArmorEquipmentPacket.h" -#include "ReadyPacket.h" -#include "RemoveBlockPacket.h" -#include "RemoveEntityPacket.h" -#include "RemovePlayerPacket.h" -#include "RespawnPacket.h" -#include "RequestChunkPacket.h" -#include "SendInventoryPacket.h" -#include "SetEntityDataPacket.h" -#include "SetEntityMotionPacket.h" -#include "SetHealthPacket.h" -#include "SetSpawnPositionPacket.h" -#include "SetTimePacket.h" -#include "SignUpdatePacket.h" -#include "StartGamePacket.h" -#include "TakeItemEntityPacket.h" -//#include "TeleportEntityPacket.h" -#include "TileEventPacket.h" -#include "UpdateBlockPacket.h" -#include "UseItemPacket.h" - -#endif /*NET_MINECRAFT_NETWORK_PACKET__PacketInclude_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__PacketInclude_H__ +#define NET_MINECRAFT_NETWORK_PACKET__PacketInclude_H__ + +#include "AddEntityPacket.h" +#include "AddItemEntityPacket.h" +#include "AddMobPacket.h" +#include "AddPaintingPacket.h" +#include "AddPlayerPacket.h" +#include "AnimatePacket.h" +#include "AdventureSettingsPacket.h" +#include "ChatPacket.h" +#include "ContainerAckPacket.h" +#include "ContainerOpenPacket.h" +#include "ContainerClosePacket.h" +#include "ContainerSetDataPacket.h" +#include "ContainerSetSlotPacket.h" +#include "ContainerSetContentPacket.h" +#include "ChunkDataPacket.h" +#include "DropItemPacket.h" +#include "EntityEventPacket.h" +#include "ExplodePacket.h" +#include "HurtArmorPacket.h" +#include "InteractPacket.h" +#include "LevelEventPacket.h" +#include "LoginPacket.h" +#include "LoginStatusPacket.h" +#include "MessagePacket.h" +#include "MoveEntityPacket.h" +#include "MovePlayerPacket.h" +#include "PlaceBlockPacket.h" +#include "PlayerActionPacket.h" +#include "PlayerEquipmentPacket.h" +#include "PlayerArmorEquipmentPacket.h" +#include "ReadyPacket.h" +#include "RemoveBlockPacket.h" +#include "RemoveEntityPacket.h" +#include "RemovePlayerPacket.h" +#include "RespawnPacket.h" +#include "RequestChunkPacket.h" +#include "SendInventoryPacket.h" +#include "SetEntityDataPacket.h" +#include "SetEntityMotionPacket.h" +#include "SetHealthPacket.h" +#include "SetSpawnPositionPacket.h" +#include "SetTimePacket.h" +#include "SignUpdatePacket.h" +#include "StartGamePacket.h" +#include "TakeItemEntityPacket.h" +//#include "TeleportEntityPacket.h" +#include "TileEventPacket.h" +#include "UpdateBlockPacket.h" +#include "UseItemPacket.h" + +#endif /*NET_MINECRAFT_NETWORK_PACKET__PacketInclude_H__*/ diff --git a/src/network/packet/PlaceBlockPacket.h b/src/network/packet/PlaceBlockPacket.h index c5a8484..13ac2e8 100755 --- a/src/network/packet/PlaceBlockPacket.h +++ b/src/network/packet/PlaceBlockPacket.h @@ -1,60 +1,60 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__PlaceBlockPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__PlaceBlockPacket_H__ - -#include "../Packet.h" - -class PlaceBlockPacket : public Packet -{ -public: - - // the id of the player who is placing the block, used to animate the player - int entityId; - int x, z; - unsigned char y, facing, blockId, blockData; - - PlaceBlockPacket() - { - } - - PlaceBlockPacket(int entityId, int x, int y, int z, int facing, int blockId, int blockData) - : entityId(entityId), - x(x), - y((unsigned char)(y & 0xff)), - z(z), - facing ((unsigned char)(facing & 0xff)), - blockId((unsigned char)(blockId & 0xff)), - blockData((unsigned char)(blockData & 0xff)) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_PLACEBLOCK)); - - bitStream->Write(entityId); - bitStream->Write(x); - bitStream->Write(z); - bitStream->Write(y); - bitStream->Write(facing); - bitStream->Write(blockId); - bitStream->Write(blockData); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(x); - bitStream->Read(z); - bitStream->Read(y); - bitStream->Read(facing); - bitStream->Read(blockId); - bitStream->Read(blockData); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (PlaceBlockPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__PlaceBlockPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__PlaceBlockPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__PlaceBlockPacket_H__ + +#include "../Packet.h" + +class PlaceBlockPacket : public Packet +{ +public: + + // the id of the player who is placing the block, used to animate the player + int entityId; + int x, z; + unsigned char y, facing, blockId, blockData; + + PlaceBlockPacket() + { + } + + PlaceBlockPacket(int entityId, int x, int y, int z, int facing, int blockId, int blockData) + : entityId(entityId), + x(x), + y((unsigned char)(y & 0xff)), + z(z), + facing ((unsigned char)(facing & 0xff)), + blockId((unsigned char)(blockId & 0xff)), + blockData((unsigned char)(blockData & 0xff)) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_PLACEBLOCK)); + + bitStream->Write(entityId); + bitStream->Write(x); + bitStream->Write(z); + bitStream->Write(y); + bitStream->Write(facing); + bitStream->Write(blockId); + bitStream->Write(blockData); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(x); + bitStream->Read(z); + bitStream->Read(y); + bitStream->Read(facing); + bitStream->Read(blockId); + bitStream->Read(blockData); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (PlaceBlockPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__PlaceBlockPacket_H__*/ diff --git a/src/network/packet/PlayerActionPacket.h b/src/network/packet/PlayerActionPacket.h index 714dbd0..5f25f46 100755 --- a/src/network/packet/PlayerActionPacket.h +++ b/src/network/packet/PlayerActionPacket.h @@ -1,62 +1,62 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__PlayerActionPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__PlayerActionPacket_H__ - -#include "../Packet.h" - -class PlayerActionPacket : public Packet -{ -public: - static const int START_DESTROY_BLOCK = 0; - static const int ABORT_DESTROY_BLOCK = 1; - static const int STOP_DESTROY_BLOCK = 2; - static const int GET_UPDATED_BLOCK = 3; - static const int DROP_ITEM = 4; - static const int RELEASE_USE_ITEM = 5; - static const int STOP_SLEEPING = 6; - - PlayerActionPacket() - : x(0), - y(0), - z(0), - action(0), - face(0), - entityId(0) - {} - - PlayerActionPacket(int action, int x, int y, int z, int face, int entityId) - : x(x), - y(y), - z(z), - face(face), - action(action), - entityId(entityId) - {} - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(action); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - bitStream->Read(face); - bitStream->Read(entityId); - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_PLAYERACTION)); - - bitStream->Write(action); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(face); - bitStream->Write(entityId); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (PlayerActionPacket*)this); - } - - int x, y, z, face, action, entityId; -}; - -#endif /* NET_MINECRAFT_NETWORK_PACKET__PlayerActionPacket_H__ */ +#ifndef NET_MINECRAFT_NETWORK_PACKET__PlayerActionPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__PlayerActionPacket_H__ + +#include "../Packet.h" + +class PlayerActionPacket : public Packet +{ +public: + static const int START_DESTROY_BLOCK = 0; + static const int ABORT_DESTROY_BLOCK = 1; + static const int STOP_DESTROY_BLOCK = 2; + static const int GET_UPDATED_BLOCK = 3; + static const int DROP_ITEM = 4; + static const int RELEASE_USE_ITEM = 5; + static const int STOP_SLEEPING = 6; + + PlayerActionPacket() + : x(0), + y(0), + z(0), + action(0), + face(0), + entityId(0) + {} + + PlayerActionPacket(int action, int x, int y, int z, int face, int entityId) + : x(x), + y(y), + z(z), + face(face), + action(action), + entityId(entityId) + {} + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(action); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + bitStream->Read(face); + bitStream->Read(entityId); + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_PLAYERACTION)); + + bitStream->Write(action); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(face); + bitStream->Write(entityId); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (PlayerActionPacket*)this); + } + + int x, y, z, face, action, entityId; +}; + +#endif /* NET_MINECRAFT_NETWORK_PACKET__PlayerActionPacket_H__ */ diff --git a/src/network/packet/PlayerArmorEquipmentPacket.h b/src/network/packet/PlayerArmorEquipmentPacket.h index 69b28f1..198799c 100755 --- a/src/network/packet/PlayerArmorEquipmentPacket.h +++ b/src/network/packet/PlayerArmorEquipmentPacket.h @@ -1,78 +1,78 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__PlayerArmorEquipmentPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__PlayerArmorEquipmentPacket_H__ - -#include "../Packet.h" -#include "../../world/entity/player/Player.h" -#include "../../world/item/ArmorItem.h" -#include "../../world/item/ItemInstance.h" - -// @note: A visual update only -class PlayerArmorEquipmentPacket : public Packet -{ -public: - int entityId; - signed char head; - signed char torso; - signed char legs; - signed char feet; - - PlayerArmorEquipmentPacket() { - } - - PlayerArmorEquipmentPacket(Player* player) - : entityId(player->entityId) - { - get(head, player->getArmor(ArmorItem::SLOT_HEAD)); - get(torso, player->getArmor(ArmorItem::SLOT_TORSO)); - get(legs, player->getArmor(ArmorItem::SLOT_LEGS)); - get(feet, player->getArmor(ArmorItem::SLOT_FEET)); - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_PLAYERARMOREQUIPMENT)); - - bitStream->Write(entityId); - bitStream->Write(head); - bitStream->Write(torso); - bitStream->Write(legs); - bitStream->Write(feet); - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(entityId); - bitStream->Read(head); - bitStream->Read(torso); - bitStream->Read(legs); - bitStream->Read(feet); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (PlayerArmorEquipmentPacket*)this); - } - - void fillIn(Player* player) { - set(player, head, ArmorItem::SLOT_HEAD); - set(player, torso, ArmorItem::SLOT_TORSO); - set(player, legs, ArmorItem::SLOT_LEGS); - set(player, feet, ArmorItem::SLOT_FEET); - } - -private: - void get(signed char& s, const ItemInstance* item) { - if (item) { - s = item->id - 256; - } else { - s = -1; - } - } - - void set(Player* p, signed char s, int slot) { - if (s < 0) p->setArmor(slot, NULL); - else { - ItemInstance item((int)s + 256, 1, 0); - p->setArmor(slot, &item); - } - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__PlayerArmorEquipmentPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__PlayerArmorEquipmentPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__PlayerArmorEquipmentPacket_H__ + +#include "../Packet.h" +#include "../../world/entity/player/Player.h" +#include "../../world/item/ArmorItem.h" +#include "../../world/item/ItemInstance.h" + +// @note: A visual update only +class PlayerArmorEquipmentPacket : public Packet +{ +public: + int entityId; + signed char head; + signed char torso; + signed char legs; + signed char feet; + + PlayerArmorEquipmentPacket() { + } + + PlayerArmorEquipmentPacket(Player* player) + : entityId(player->entityId) + { + get(head, player->getArmor(ArmorItem::SLOT_HEAD)); + get(torso, player->getArmor(ArmorItem::SLOT_TORSO)); + get(legs, player->getArmor(ArmorItem::SLOT_LEGS)); + get(feet, player->getArmor(ArmorItem::SLOT_FEET)); + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_PLAYERARMOREQUIPMENT)); + + bitStream->Write(entityId); + bitStream->Write(head); + bitStream->Write(torso); + bitStream->Write(legs); + bitStream->Write(feet); + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(entityId); + bitStream->Read(head); + bitStream->Read(torso); + bitStream->Read(legs); + bitStream->Read(feet); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (PlayerArmorEquipmentPacket*)this); + } + + void fillIn(Player* player) { + set(player, head, ArmorItem::SLOT_HEAD); + set(player, torso, ArmorItem::SLOT_TORSO); + set(player, legs, ArmorItem::SLOT_LEGS); + set(player, feet, ArmorItem::SLOT_FEET); + } + +private: + void get(signed char& s, const ItemInstance* item) { + if (item) { + s = item->id - 256; + } else { + s = -1; + } + } + + void set(Player* p, signed char s, int slot) { + if (s < 0) p->setArmor(slot, NULL); + else { + ItemInstance item((int)s + 256, 1, 0); + p->setArmor(slot, &item); + } + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__PlayerArmorEquipmentPacket_H__*/ diff --git a/src/network/packet/PlayerEquipmentPacket.h b/src/network/packet/PlayerEquipmentPacket.h index 5dfb315..85da8e5 100755 --- a/src/network/packet/PlayerEquipmentPacket.h +++ b/src/network/packet/PlayerEquipmentPacket.h @@ -1,46 +1,46 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__PlayerEquipmentPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__PlayerEquipmentPacket_H__ - -#include "../Packet.h" - -class PlayerEquipmentPacket : public Packet -{ -public: - int entityId; - unsigned short itemId; - unsigned short itemAuxValue; - - PlayerEquipmentPacket() - { - } - - PlayerEquipmentPacket(int entityId, int itemId, int data) - : entityId(entityId), - itemId(itemId), - itemAuxValue(data) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_PLAYEREQUIPMENT)); - - bitStream->Write(entityId); - bitStream->Write(itemId); - bitStream->Write(itemAuxValue); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(itemId); - bitStream->Read(itemAuxValue); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (PlayerEquipmentPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__PlayerEquipmentPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__PlayerEquipmentPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__PlayerEquipmentPacket_H__ + +#include "../Packet.h" + +class PlayerEquipmentPacket : public Packet +{ +public: + int entityId; + unsigned short itemId; + unsigned short itemAuxValue; + + PlayerEquipmentPacket() + { + } + + PlayerEquipmentPacket(int entityId, int itemId, int data) + : entityId(entityId), + itemId(itemId), + itemAuxValue(data) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_PLAYEREQUIPMENT)); + + bitStream->Write(entityId); + bitStream->Write(itemId); + bitStream->Write(itemAuxValue); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(itemId); + bitStream->Read(itemAuxValue); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (PlayerEquipmentPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__PlayerEquipmentPacket_H__*/ diff --git a/src/network/packet/ReadyPacket.h b/src/network/packet/ReadyPacket.h index fa19ce3..f0a3608 100755 --- a/src/network/packet/ReadyPacket.h +++ b/src/network/packet/ReadyPacket.h @@ -1,42 +1,42 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__ReadyPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__ReadyPacket_H__ - -#include "../Packet.h" - -class ReadyPacket: public Packet -{ -public: - static const char READY_UNDEFINED = 0; - static const char READY_CLIENTGENERATION = 1; - static const char READY_REQUESTEDCHUNKS = 2; - - ReadyPacket() - : type(READY_UNDEFINED) - { - } - - ReadyPacket(char type) - : type(type) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_READY)); - bitStream->Write(type); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(type); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (ReadyPacket*)this); - } - - char type; -}; - -#endif /*#NET_MINECRAFT_NETWORK_PACKET__ReadyPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__ReadyPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__ReadyPacket_H__ + +#include "../Packet.h" + +class ReadyPacket: public Packet +{ +public: + static const char READY_UNDEFINED = 0; + static const char READY_CLIENTGENERATION = 1; + static const char READY_REQUESTEDCHUNKS = 2; + + ReadyPacket() + : type(READY_UNDEFINED) + { + } + + ReadyPacket(char type) + : type(type) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_READY)); + bitStream->Write(type); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(type); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (ReadyPacket*)this); + } + + char type; +}; + +#endif /*#NET_MINECRAFT_NETWORK_PACKET__ReadyPacket_H__*/ diff --git a/src/network/packet/RemoveBlockPacket.h b/src/network/packet/RemoveBlockPacket.h index 2f2ec5d..052b20f 100755 --- a/src/network/packet/RemoveBlockPacket.h +++ b/src/network/packet/RemoveBlockPacket.h @@ -1,52 +1,52 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__RemoveBlockPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__RemoveBlockPacket_H__ - -#include "../Packet.h" -#include "../../world/entity/player/Player.h" - -class RemoveBlockPacket : public Packet -{ -public: - - // the id of the player who is placing the block, used to animate the player - int entityId; - int x, z; - unsigned char y; - - RemoveBlockPacket() - { - } - - RemoveBlockPacket(Player* p, int x, int y, int z) - : entityId(p->entityId), - x(x), - y((unsigned char)(y & 0xff)), - z(z) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_REMOVEBLOCK)); - - bitStream->Write(entityId); - bitStream->Write(x); - bitStream->Write(z); - bitStream->Write(y); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(x); - bitStream->Read(z); - bitStream->Read(y); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (RemoveBlockPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__RemoveBlockPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__RemoveBlockPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__RemoveBlockPacket_H__ + +#include "../Packet.h" +#include "../../world/entity/player/Player.h" + +class RemoveBlockPacket : public Packet +{ +public: + + // the id of the player who is placing the block, used to animate the player + int entityId; + int x, z; + unsigned char y; + + RemoveBlockPacket() + { + } + + RemoveBlockPacket(Player* p, int x, int y, int z) + : entityId(p->entityId), + x(x), + y((unsigned char)(y & 0xff)), + z(z) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_REMOVEBLOCK)); + + bitStream->Write(entityId); + bitStream->Write(x); + bitStream->Write(z); + bitStream->Write(y); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(x); + bitStream->Read(z); + bitStream->Read(y); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (RemoveBlockPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__RemoveBlockPacket_H__*/ diff --git a/src/network/packet/RemoveEntityPacket.h b/src/network/packet/RemoveEntityPacket.h index 3579c29..4bbf5ab 100755 --- a/src/network/packet/RemoveEntityPacket.h +++ b/src/network/packet/RemoveEntityPacket.h @@ -1,39 +1,39 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__RemoveEntityPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__RemoveEntityPacket_H__ - -#include "../Packet.h" - -class RemoveEntityPacket : public Packet -{ -public: - - int entityId; - - RemoveEntityPacket() - { - } - - RemoveEntityPacket(int entityId) - : entityId(entityId) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_REMOVEENTITY)); - - bitStream->Write(entityId); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (RemoveEntityPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__RemoveEntityPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__RemoveEntityPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__RemoveEntityPacket_H__ + +#include "../Packet.h" + +class RemoveEntityPacket : public Packet +{ +public: + + int entityId; + + RemoveEntityPacket() + { + } + + RemoveEntityPacket(int entityId) + : entityId(entityId) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_REMOVEENTITY)); + + bitStream->Write(entityId); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (RemoveEntityPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__RemoveEntityPacket_H__*/ diff --git a/src/network/packet/RemovePlayerPacket.h b/src/network/packet/RemovePlayerPacket.h index 86840d4..b19bebf 100755 --- a/src/network/packet/RemovePlayerPacket.h +++ b/src/network/packet/RemovePlayerPacket.h @@ -1,41 +1,41 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__RemovePlayerPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__RemovePlayerPacket_H__ - -#include "../Packet.h" -#include "../../world/entity/player/Player.h" - -class RemovePlayerPacket : public Packet -{ -public: - RemovePlayerPacket() {} - - RemovePlayerPacket(const Player* p) - : entityId(p->entityId), - owner(p->owner) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_REMOVEPLAYER)); - - bitStream->Write(entityId); - bitStream->Write(owner); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(owner); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (RemovePlayerPacket*)this); - } - - int entityId; - RakNet::RakNetGUID owner; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__RemovePlayerPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__RemovePlayerPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__RemovePlayerPacket_H__ + +#include "../Packet.h" +#include "../../world/entity/player/Player.h" + +class RemovePlayerPacket : public Packet +{ +public: + RemovePlayerPacket() {} + + RemovePlayerPacket(const Player* p) + : entityId(p->entityId), + owner(p->owner) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_REMOVEPLAYER)); + + bitStream->Write(entityId); + bitStream->Write(owner); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(owner); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (RemovePlayerPacket*)this); + } + + int entityId; + RakNet::RakNetGUID owner; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__RemovePlayerPacket_H__*/ diff --git a/src/network/packet/RequestChunkPacket.h b/src/network/packet/RequestChunkPacket.h index e25add0..a8af120 100755 --- a/src/network/packet/RequestChunkPacket.h +++ b/src/network/packet/RequestChunkPacket.h @@ -1,42 +1,42 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__RequestChunkPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__RequestChunkPacket_H__ - -#include "../Packet.h" - -class RequestChunkPacket : public Packet -{ -public: - - int x, z; - - RequestChunkPacket() - { - } - - RequestChunkPacket(int _x, int _z) - { - x = _x; - z = _z; - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_REQUESTCHUNK)); - - bitStream->Write(x); - bitStream->Write(z); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(x); - bitStream->Read(z); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (RequestChunkPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__RequestChunkPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__RequestChunkPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__RequestChunkPacket_H__ + +#include "../Packet.h" + +class RequestChunkPacket : public Packet +{ +public: + + int x, z; + + RequestChunkPacket() + { + } + + RequestChunkPacket(int _x, int _z) + { + x = _x; + z = _z; + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_REQUESTCHUNK)); + + bitStream->Write(x); + bitStream->Write(z); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(x); + bitStream->Read(z); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (RequestChunkPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__RequestChunkPacket_H__*/ diff --git a/src/network/packet/RespawnPacket.h b/src/network/packet/RespawnPacket.h index 74e2371..d5c1c4f 100755 --- a/src/network/packet/RespawnPacket.h +++ b/src/network/packet/RespawnPacket.h @@ -1,47 +1,47 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__RespawnPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__RespawnPacket_H__ - -#include "../Packet.h" - -class RespawnPacket: public Packet -{ -public: - RespawnPacket() - { - } - - RespawnPacket(const Player* p) - : x(p->x),y(p->y),z(p->z), - entityId(p->entityId) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_RESPAWN)); - bitStream->Write(entityId); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (RespawnPacket*)this); - } - - float x; - float y; - float z; - int entityId; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__RespawnPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__RespawnPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__RespawnPacket_H__ + +#include "../Packet.h" + +class RespawnPacket: public Packet +{ +public: + RespawnPacket() + { + } + + RespawnPacket(const Player* p) + : x(p->x),y(p->y),z(p->z), + entityId(p->entityId) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_RESPAWN)); + bitStream->Write(entityId); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (RespawnPacket*)this); + } + + float x; + float y; + float z; + int entityId; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__RespawnPacket_H__*/ diff --git a/src/network/packet/SendInventoryPacket.h b/src/network/packet/SendInventoryPacket.h index 65d12f4..4165736 100755 --- a/src/network/packet/SendInventoryPacket.h +++ b/src/network/packet/SendInventoryPacket.h @@ -1,69 +1,69 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__SendInventoryPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__SendInventoryPacket_H__ - -#include "../Packet.h" - -class SendInventoryPacket: public Packet -{ -public: - SendInventoryPacket() - { - } - - SendInventoryPacket(Player* player, bool dropItems) - : entityId(player->entityId), - extra(dropItems? ExtraDrop : 0) - { - Inventory* inv = player->inventory; - numItems = 0; - for (int i = Inventory::MAX_SELECTION_SIZE; i < inv->getContainerSize(); ++i) { - ++numItems; - ItemInstance* item = inv->getItem(i); - items.push_back(item? *item : ItemInstance()); - } - for (int i = 0; i < NumArmorItems; ++i) { - ItemInstance* item = player->getArmor(i); - items.push_back(item? *item : ItemInstance()); - } - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SENDINVENTORY)); - bitStream->Write(entityId); - bitStream->Write(extra); - bitStream->Write(numItems); - // Inventory - for (int i = 0; i < numItems; ++i) - PacketUtil::writeItemInstance(items[i], bitStream); - // Armor - for (int i = 0; i < NumArmorItems; ++i) - PacketUtil::writeItemInstance(items[i + numItems], bitStream); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(entityId); - bitStream->Read(extra); - bitStream->Read(numItems); - items.clear(); - // Inventory, Armor - for (int i = 0; i < numItems + NumArmorItems; ++i) - items.push_back(PacketUtil::readItemInstance(bitStream)); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (SendInventoryPacket*)this); - } - - int entityId; - std::vector items; - short numItems; - unsigned char extra; - - static const int ExtraDrop = 1; - static const int NumArmorItems = 4; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__SendInventoryPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__SendInventoryPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__SendInventoryPacket_H__ + +#include "../Packet.h" + +class SendInventoryPacket: public Packet +{ +public: + SendInventoryPacket() + { + } + + SendInventoryPacket(Player* player, bool dropItems) + : entityId(player->entityId), + extra(dropItems? ExtraDrop : 0) + { + Inventory* inv = player->inventory; + numItems = 0; + for (int i = Inventory::MAX_SELECTION_SIZE; i < inv->getContainerSize(); ++i) { + ++numItems; + ItemInstance* item = inv->getItem(i); + items.push_back(item? *item : ItemInstance()); + } + for (int i = 0; i < NumArmorItems; ++i) { + ItemInstance* item = player->getArmor(i); + items.push_back(item? *item : ItemInstance()); + } + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SENDINVENTORY)); + bitStream->Write(entityId); + bitStream->Write(extra); + bitStream->Write(numItems); + // Inventory + for (int i = 0; i < numItems; ++i) + PacketUtil::writeItemInstance(items[i], bitStream); + // Armor + for (int i = 0; i < NumArmorItems; ++i) + PacketUtil::writeItemInstance(items[i + numItems], bitStream); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(entityId); + bitStream->Read(extra); + bitStream->Read(numItems); + items.clear(); + // Inventory, Armor + for (int i = 0; i < numItems + NumArmorItems; ++i) + items.push_back(PacketUtil::readItemInstance(bitStream)); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (SendInventoryPacket*)this); + } + + int entityId; + std::vector items; + short numItems; + unsigned char extra; + + static const int ExtraDrop = 1; + static const int NumArmorItems = 4; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__SendInventoryPacket_H__*/ diff --git a/src/network/packet/SetEntityDataPacket.h b/src/network/packet/SetEntityDataPacket.h index 4588716..1180c30 100755 --- a/src/network/packet/SetEntityDataPacket.h +++ b/src/network/packet/SetEntityDataPacket.h @@ -1,64 +1,64 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__SetEntityDataPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__SetEntityDataPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -#include "../../world/entity/SynchedEntityData.h" -#include "../../util/RakDataIO.h" - -class SetEntityDataPacket: public Packet -{ -public: - SetEntityDataPacket() - : deletePackedItems(false) - {} - - SetEntityDataPacket(int id, SynchedEntityData& entityData) - : id(id), - deletePackedItems(false), - packedItems(entityData.packDirty()) - { - } - - ~SetEntityDataPacket() { - if (deletePackedItems) - for (unsigned int i = 0; i < packedItems.size(); ++i) - delete packedItems[i]; - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETENTITYDATA)); - bitStream->Write(id); - - RakDataOutput dos(*bitStream); - SynchedEntityData::pack(&packedItems, &dos); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(id); - - RakDataInput dis(*bitStream); - packedItems = SynchedEntityData::unpack(&dis); - deletePackedItems = true; - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (SetEntityDataPacket*)this); - } - - SynchedEntityData::DataList& getUnpackedData() { - return packedItems; - } -public: - int id; -private: - bool deletePackedItems; - SynchedEntityData::DataList packedItems; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__SetEntityDataPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__SetEntityDataPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__SetEntityDataPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +#include "../../world/entity/SynchedEntityData.h" +#include "../../util/RakDataIO.h" + +class SetEntityDataPacket: public Packet +{ +public: + SetEntityDataPacket() + : deletePackedItems(false) + {} + + SetEntityDataPacket(int id, SynchedEntityData& entityData) + : id(id), + deletePackedItems(false), + packedItems(entityData.packDirty()) + { + } + + ~SetEntityDataPacket() { + if (deletePackedItems) + for (unsigned int i = 0; i < packedItems.size(); ++i) + delete packedItems[i]; + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETENTITYDATA)); + bitStream->Write(id); + + RakDataOutput dos(*bitStream); + SynchedEntityData::pack(&packedItems, &dos); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(id); + + RakDataInput dis(*bitStream); + packedItems = SynchedEntityData::unpack(&dis); + deletePackedItems = true; + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (SetEntityDataPacket*)this); + } + + SynchedEntityData::DataList& getUnpackedData() { + return packedItems; + } +public: + int id; +private: + bool deletePackedItems; + SynchedEntityData::DataList packedItems; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__SetEntityDataPacket_H__*/ diff --git a/src/network/packet/SetEntityMotionPacket.h b/src/network/packet/SetEntityMotionPacket.h index dc93b7e..4b04ede 100755 --- a/src/network/packet/SetEntityMotionPacket.h +++ b/src/network/packet/SetEntityMotionPacket.h @@ -1,68 +1,68 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__SetEntityMotionPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__SetEntityMotionPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" -#include "../../world/entity/Entity.h" - -class SetEntityMotionPacket: public Packet -{ -public: - SetEntityMotionPacket() {} - - SetEntityMotionPacket(Entity* e) { - init(e->entityId, e->xd, e->yd, e->zd); - } - - SetEntityMotionPacket(int id, float xd, float yd, float zd) { - init(id, xd, yd, zd); - } - - void write(RakNet::BitStream* bitStream) - { - const float M = 3.9f; - short xa = (short)(8000.0f * Mth::clamp(xd, -M, M)); - short ya = (short)(8000.0f * Mth::clamp(yd, -M, M)); - short za = (short)(8000.0f * Mth::clamp(zd, -M, M)); - - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETENTITYMOTION)); - bitStream->Write(id); - bitStream->Write(xa); - bitStream->Write(ya); - bitStream->Write(za); - } - - void read(RakNet::BitStream* bitStream) - { - short xa, ya, za; - bitStream->Read(id); - bitStream->Read(xa); - bitStream->Read(ya); - bitStream->Read(za); - - xd = xa / 8000.0f; - yd = ya / 8000.0f; - zd = za / 8000.0f; - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (SetEntityMotionPacket*)this); - } - -private: - void init(int entityId, float xd, float yd, float zd) { - this->id = entityId; - this->xd = xd; - this->yd = yd; - this->zd = zd; - } - -public: - int id; - float xd, yd, zd; - -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__SetEntityMotionPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__SetEntityMotionPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__SetEntityMotionPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" +#include "../../world/entity/Entity.h" + +class SetEntityMotionPacket: public Packet +{ +public: + SetEntityMotionPacket() {} + + SetEntityMotionPacket(Entity* e) { + init(e->entityId, e->xd, e->yd, e->zd); + } + + SetEntityMotionPacket(int id, float xd, float yd, float zd) { + init(id, xd, yd, zd); + } + + void write(RakNet::BitStream* bitStream) + { + const float M = 3.9f; + short xa = (short)(8000.0f * Mth::clamp(xd, -M, M)); + short ya = (short)(8000.0f * Mth::clamp(yd, -M, M)); + short za = (short)(8000.0f * Mth::clamp(zd, -M, M)); + + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETENTITYMOTION)); + bitStream->Write(id); + bitStream->Write(xa); + bitStream->Write(ya); + bitStream->Write(za); + } + + void read(RakNet::BitStream* bitStream) + { + short xa, ya, za; + bitStream->Read(id); + bitStream->Read(xa); + bitStream->Read(ya); + bitStream->Read(za); + + xd = xa / 8000.0f; + yd = ya / 8000.0f; + zd = za / 8000.0f; + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (SetEntityMotionPacket*)this); + } + +private: + void init(int entityId, float xd, float yd, float zd) { + this->id = entityId; + this->xd = xd; + this->yd = yd; + this->zd = zd; + } + +public: + int id; + float xd, yd, zd; + +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__SetEntityMotionPacket_H__*/ diff --git a/src/network/packet/SetHealthPacket.h b/src/network/packet/SetHealthPacket.h index bd6dbb2..bb77a0e 100755 --- a/src/network/packet/SetHealthPacket.h +++ b/src/network/packet/SetHealthPacket.h @@ -1,41 +1,41 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__SetHealthPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__SetHealthPacket_H__ - -#include "../Packet.h" - -//package net.minecraft.network.packet; - -class SetHealthPacket: public Packet { -public: - static const int HEALTH_MODIFY_OFFSET = -64; - - SetHealthPacket() { - } - - SetHealthPacket(int health) - : health(health) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETHEALTH)); - bitStream->Write((signed char)health); - } - - void read(RakNet::BitStream* bitStream) - { - signed char tmpHealth; - bitStream->Read(tmpHealth); - health = tmpHealth; - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (SetHealthPacket*)this); - } - - int health; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__SetHealthPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__SetHealthPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__SetHealthPacket_H__ + +#include "../Packet.h" + +//package net.minecraft.network.packet; + +class SetHealthPacket: public Packet { +public: + static const int HEALTH_MODIFY_OFFSET = -64; + + SetHealthPacket() { + } + + SetHealthPacket(int health) + : health(health) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETHEALTH)); + bitStream->Write((signed char)health); + } + + void read(RakNet::BitStream* bitStream) + { + signed char tmpHealth; + bitStream->Read(tmpHealth); + health = tmpHealth; + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (SetHealthPacket*)this); + } + + int health; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__SetHealthPacket_H__*/ diff --git a/src/network/packet/SetSpawnPositionPacket.h b/src/network/packet/SetSpawnPositionPacket.h index 6b409a6..0f6b0d6 100755 --- a/src/network/packet/SetSpawnPositionPacket.h +++ b/src/network/packet/SetSpawnPositionPacket.h @@ -1,52 +1,52 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__SetSpawnPositionPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__SetSpawnPositionPacket_H__ - -#include "../Packet.h" - -class SetSpawnPositionPacket : public Packet -{ -public: - int entityId; - int x, z; - unsigned char y; - - SetSpawnPositionPacket() - { - } - - SetSpawnPositionPacket(int x, int y, int z) - : x(x), - y((unsigned char)(y & 0xff)), - z(z) - { - } - SetSpawnPositionPacket(const Pos& pos) - : x(pos.x), - y((unsigned char)(pos.y & 0xff)), - z(pos.z) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETSPAWNPOSITION)); - - bitStream->Write(x); - bitStream->Write(z); - bitStream->Write(y); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(x); - bitStream->Read(z); - bitStream->Read(y); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (SetSpawnPositionPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__SetSpawnPositionPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__SetSpawnPositionPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__SetSpawnPositionPacket_H__ + +#include "../Packet.h" + +class SetSpawnPositionPacket : public Packet +{ +public: + int entityId; + int x, z; + unsigned char y; + + SetSpawnPositionPacket() + { + } + + SetSpawnPositionPacket(int x, int y, int z) + : x(x), + y((unsigned char)(y & 0xff)), + z(z) + { + } + SetSpawnPositionPacket(const Pos& pos) + : x(pos.x), + y((unsigned char)(pos.y & 0xff)), + z(pos.z) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETSPAWNPOSITION)); + + bitStream->Write(x); + bitStream->Write(z); + bitStream->Write(y); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(x); + bitStream->Read(z); + bitStream->Read(y); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (SetSpawnPositionPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__SetSpawnPositionPacket_H__*/ diff --git a/src/network/packet/SetTimePacket.h b/src/network/packet/SetTimePacket.h index e2fb484..41bc990 100755 --- a/src/network/packet/SetTimePacket.h +++ b/src/network/packet/SetTimePacket.h @@ -1,37 +1,37 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__SetTimePacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__SetTimePacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" -#include - -class SetTimePacket: public Packet { -public: - SetTimePacket() { - } - - SetTimePacket(long time) - : time((int32_t)time) - {} - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETTIME)); - bitStream->Write(time); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(time); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (SetTimePacket*)this); - } - - int32_t time; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__SetTimePacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__SetTimePacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__SetTimePacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" +#include + +class SetTimePacket: public Packet { +public: + SetTimePacket() { + } + + SetTimePacket(long time) + : time((int32_t)time) + {} + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SETTIME)); + bitStream->Write(time); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(time); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (SetTimePacket*)this); + } + + int32_t time; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__SetTimePacket_H__*/ diff --git a/src/network/packet/SignUpdatePacket.h b/src/network/packet/SignUpdatePacket.h index 1fadf5d..d985c58 100755 --- a/src/network/packet/SignUpdatePacket.h +++ b/src/network/packet/SignUpdatePacket.h @@ -1,68 +1,68 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__SignUpdatePacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__SignUpdatePacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" -#include "../../util/RakDataIO.h" -#include "../../world/level/tile/entity/SignTileEntity.h" - -class SignUpdatePacket: public Packet -{ -public: - SignUpdatePacket() {} - - SignUpdatePacket(int x, int y, int z, const std::string lines[]) - : x(x), - y(y), - z(z) - { - for (int i = 0; i < 4; ++i) - this->lines[i] = lines[i]; - } - - /*@Override*/ - void read(RakNet::BitStream* bitStream) { - short xx, zz; - unsigned char yy; - bitStream->Read(xx); - bitStream->Read(yy); - bitStream->Read(zz); - x = xx; - z = zz; - y = yy; - - RakDataInput dis(*bitStream); - for (int i = 0; i < 4; ++i) { - lines[i] = dis.readString(); - if (lines[i].length() > SignTileEntity::MAX_LINE_LENGTH) - lines[i].resize(SignTileEntity::MAX_LINE_LENGTH); - } - } - - /*@Override*/ - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SIGNUPDATE)); - short xx = x; - short zz = z; - unsigned char yy = y; - bitStream->Write(xx); - bitStream->Write(yy); - bitStream->Write(zz); - - RakDataOutput dos(*bitStream); - for (int i = 0; i < 4; ++i) - dos.writeString(lines[i]); - } - - /*@Override*/ - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (SignUpdatePacket*)this); - } - -public: - int x, y, z; - std::string lines[4]; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__SignUpdatePacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__SignUpdatePacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__SignUpdatePacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" +#include "../../util/RakDataIO.h" +#include "../../world/level/tile/entity/SignTileEntity.h" + +class SignUpdatePacket: public Packet +{ +public: + SignUpdatePacket() {} + + SignUpdatePacket(int x, int y, int z, const std::string lines[]) + : x(x), + y(y), + z(z) + { + for (int i = 0; i < 4; ++i) + this->lines[i] = lines[i]; + } + + /*@Override*/ + void read(RakNet::BitStream* bitStream) { + short xx, zz; + unsigned char yy; + bitStream->Read(xx); + bitStream->Read(yy); + bitStream->Read(zz); + x = xx; + z = zz; + y = yy; + + RakDataInput dis(*bitStream); + for (int i = 0; i < 4; ++i) { + lines[i] = dis.readString(); + if (lines[i].length() > SignTileEntity::MAX_LINE_LENGTH) + lines[i].resize(SignTileEntity::MAX_LINE_LENGTH); + } + } + + /*@Override*/ + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_SIGNUPDATE)); + short xx = x; + short zz = z; + unsigned char yy = y; + bitStream->Write(xx); + bitStream->Write(yy); + bitStream->Write(zz); + + RakDataOutput dos(*bitStream); + for (int i = 0; i < 4; ++i) + dos.writeString(lines[i]); + } + + /*@Override*/ + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (SignUpdatePacket*)this); + } + +public: + int x, y, z; + std::string lines[4]; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__SignUpdatePacket_H__*/ diff --git a/src/network/packet/StartGamePacket.h b/src/network/packet/StartGamePacket.h index 24adead..65e1abe 100755 --- a/src/network/packet/StartGamePacket.h +++ b/src/network/packet/StartGamePacket.h @@ -1,62 +1,62 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__StartGamePacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__StartGamePacket_H__ - -#include "../Packet.h" -#include - -class StartGamePacket : public Packet -{ -public: - int32_t levelSeed; - int levelGeneratorVersion; - int gameType; - - int entityId; - float x, y, z; - - StartGamePacket() - { - } - - StartGamePacket(long seed, int levelGeneratorVersion, int gameType, int entityId, float x, float y, float z) - : levelSeed((int32_t)seed), - levelGeneratorVersion(levelGeneratorVersion), - gameType(gameType), - entityId(entityId), - x(x), - y(y), - z(z) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_STARTGAME)); - - bitStream->Write(levelSeed); - bitStream->Write(levelGeneratorVersion); - bitStream->Write(gameType); - bitStream->Write(entityId); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(levelSeed); - bitStream->Read(levelGeneratorVersion); - bitStream->Read(gameType); - bitStream->Read(entityId); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (StartGamePacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__StartGamePacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__StartGamePacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__StartGamePacket_H__ + +#include "../Packet.h" +#include + +class StartGamePacket : public Packet +{ +public: + int32_t levelSeed; + int levelGeneratorVersion; + int gameType; + + int entityId; + float x, y, z; + + StartGamePacket() + { + } + + StartGamePacket(long seed, int levelGeneratorVersion, int gameType, int entityId, float x, float y, float z) + : levelSeed((int32_t)seed), + levelGeneratorVersion(levelGeneratorVersion), + gameType(gameType), + entityId(entityId), + x(x), + y(y), + z(z) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_STARTGAME)); + + bitStream->Write(levelSeed); + bitStream->Write(levelGeneratorVersion); + bitStream->Write(gameType); + bitStream->Write(entityId); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(levelSeed); + bitStream->Read(levelGeneratorVersion); + bitStream->Read(gameType); + bitStream->Read(entityId); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (StartGamePacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__StartGamePacket_H__*/ diff --git a/src/network/packet/TakeItemEntityPacket.h b/src/network/packet/TakeItemEntityPacket.h index ada5fed..7b19685 100755 --- a/src/network/packet/TakeItemEntityPacket.h +++ b/src/network/packet/TakeItemEntityPacket.h @@ -1,42 +1,42 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__TakeItemEntityPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__TakeItemEntityPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -class TakeItemEntityPacket: public Packet -{ -public: - TakeItemEntityPacket() { - } - - TakeItemEntityPacket(int itemId, int playerId) - : itemId(itemId), - playerId(playerId) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_TAKEITEMENTITY)); - bitStream->Write(itemId); - bitStream->Write(playerId); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(itemId); - bitStream->Read(playerId); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (TakeItemEntityPacket*)this); - } - - int itemId; - int playerId; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__TakeItemEntityPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__TakeItemEntityPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__TakeItemEntityPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +class TakeItemEntityPacket: public Packet +{ +public: + TakeItemEntityPacket() { + } + + TakeItemEntityPacket(int itemId, int playerId) + : itemId(itemId), + playerId(playerId) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_TAKEITEMENTITY)); + bitStream->Write(itemId); + bitStream->Write(playerId); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(itemId); + bitStream->Read(playerId); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (TakeItemEntityPacket*)this); + } + + int itemId; + int playerId; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__TakeItemEntityPacket_H__*/ diff --git a/src/network/packet/TeleportEntityPacket.h b/src/network/packet/TeleportEntityPacket.h index cb3a462..7801776 100755 --- a/src/network/packet/TeleportEntityPacket.h +++ b/src/network/packet/TeleportEntityPacket.h @@ -1,77 +1,77 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__TeleportEntityPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__TeleportEntityPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -#include "../../world/entity/Entity.h" -#include "../../util/Mth.h" - -class TeleportEntityPacket: public Packet -{ -public: - TeleportEntityPacket() { - } - - TeleportEntityPacket(Entity* e, char cause) - : id(e->entityId), - x(e->x), - y(e->y), - z(e->z), - yRot(e->yRot), - xRot(e->xRot), - cause(cause) - { - } - - TeleportEntityPacket(int id, float x, float y, float z, float yRot, float xRot, char cause) - : id(id), - x(x), - y(y), - z(z), - yRot(yRot), - xRot(xRot), - cause(cause) - { - } - - void write(RakNet::BitStream* bitStream) { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_TELEPORTENTITY)); - bitStream->Write(id); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(PacketUtil::Rot_degreesToChar(yRot)); - bitStream->Write(PacketUtil::Rot_degreesToChar(xRot)); - bitStream->Write(cause); - } - - void read(RakNet::BitStream* bitStream) { - bitStream->Read(id); - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - char rx, ry; - bitStream->Read(ry); - bitStream->Read(rx); - bitStream->Read(cause); - yRot = PacketUtil::Rot_degreesToChar(ry); - xRot = PacketUtil::Rot_charToDegrees(rx); - - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (TeleportEntityPacket*)this); - } - - int id; - float x, y, z; - float xRot, yRot; // sent as byte - char cause; - - static const int WANT_RESPAWN = 1; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__TeleportEntityPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__TeleportEntityPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__TeleportEntityPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +#include "../../world/entity/Entity.h" +#include "../../util/Mth.h" + +class TeleportEntityPacket: public Packet +{ +public: + TeleportEntityPacket() { + } + + TeleportEntityPacket(Entity* e, char cause) + : id(e->entityId), + x(e->x), + y(e->y), + z(e->z), + yRot(e->yRot), + xRot(e->xRot), + cause(cause) + { + } + + TeleportEntityPacket(int id, float x, float y, float z, float yRot, float xRot, char cause) + : id(id), + x(x), + y(y), + z(z), + yRot(yRot), + xRot(xRot), + cause(cause) + { + } + + void write(RakNet::BitStream* bitStream) { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_TELEPORTENTITY)); + bitStream->Write(id); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(PacketUtil::Rot_degreesToChar(yRot)); + bitStream->Write(PacketUtil::Rot_degreesToChar(xRot)); + bitStream->Write(cause); + } + + void read(RakNet::BitStream* bitStream) { + bitStream->Read(id); + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + char rx, ry; + bitStream->Read(ry); + bitStream->Read(rx); + bitStream->Read(cause); + yRot = PacketUtil::Rot_degreesToChar(ry); + xRot = PacketUtil::Rot_charToDegrees(rx); + + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (TeleportEntityPacket*)this); + } + + int id; + float x, y, z; + float xRot, yRot; // sent as byte + char cause; + + static const int WANT_RESPAWN = 1; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__TeleportEntityPacket_H__*/ diff --git a/src/network/packet/TileEventPacket.h b/src/network/packet/TileEventPacket.h index 1319fd0..43e800c 100755 --- a/src/network/packet/TileEventPacket.h +++ b/src/network/packet/TileEventPacket.h @@ -1,48 +1,48 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__TileEventPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__TileEventPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -class TileEventPacket: public Packet { -public: - TileEventPacket() {} - - TileEventPacket(int x, int y, int z, int b0, int b1) - : x(x), - y(y), - z(z), - b0(b0), - b1(b1) - {} - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_TILEEVENT)); - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(b0); - bitStream->Write(b1); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - bitStream->Read(b0); - bitStream->Read(b1); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (TileEventPacket*)this); - } - - int x, y, z; - int b0, b1; -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__TileEventPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__TileEventPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__TileEventPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +class TileEventPacket: public Packet { +public: + TileEventPacket() {} + + TileEventPacket(int x, int y, int z, int b0, int b1) + : x(x), + y(y), + z(z), + b0(b0), + b1(b1) + {} + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_TILEEVENT)); + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(b0); + bitStream->Write(b1); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + bitStream->Read(b0); + bitStream->Read(b1); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (TileEventPacket*)this); + } + + int x, y, z; + int b0, b1; +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__TileEventPacket_H__*/ diff --git a/src/network/packet/UpdateArmorPacket.h b/src/network/packet/UpdateArmorPacket.h index c1e812f..9e1a014 100755 --- a/src/network/packet/UpdateArmorPacket.h +++ b/src/network/packet/UpdateArmorPacket.h @@ -1,28 +1,28 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__UpdateArmorPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__UpdateArmorPacket_H__ - -#include "../Packet.h" - -class UpdateArmorPacket: public Packet -{ -public: - UpdateArmorPacket(Player* player) - { - } - - void write(RakNet::BitStream* bitStream){ - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_UPDATEARMOR)); - - //bitStream->Write(); - } - - void read(RakNet::BitStream* bitStream) { - //bitStream->Read(); - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { - callback->handle(source, (UpdateArmorPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__UpdateArmorPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__UpdateArmorPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__UpdateArmorPacket_H__ + +#include "../Packet.h" + +class UpdateArmorPacket: public Packet +{ +public: + UpdateArmorPacket(Player* player) + { + } + + void write(RakNet::BitStream* bitStream){ + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_UPDATEARMOR)); + + //bitStream->Write(); + } + + void read(RakNet::BitStream* bitStream) { + //bitStream->Read(); + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) { + callback->handle(source, (UpdateArmorPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__UpdateArmorPacket_H__*/ diff --git a/src/network/packet/UpdateBlockPacket.h b/src/network/packet/UpdateBlockPacket.h index 3559034..b794493 100755 --- a/src/network/packet/UpdateBlockPacket.h +++ b/src/network/packet/UpdateBlockPacket.h @@ -1,55 +1,55 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__UpdateBlockPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__UpdateBlockPacket_H__ - -#include "../Packet.h" - -class UpdateBlockPacket : public Packet -{ -public: - - int x, z; - unsigned char y; - unsigned char blockId; - unsigned char blockData; - - UpdateBlockPacket() - { - } - - UpdateBlockPacket(int _x, int _y, int _z, int _blockId, int _blockData) - { - x = _x; - y = (unsigned char)(_y & 0xff); - z = _z; - blockId = (unsigned char)(_blockId & 0xff); - blockData = (unsigned char)(_blockData & 0xff); - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_UPDATEBLOCK)); - - bitStream->Write(x); - bitStream->Write(z); - bitStream->Write(y); - bitStream->Write(blockId); - bitStream->Write(blockData); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(x); - bitStream->Read(z); - bitStream->Read(y); - bitStream->Read(blockId); - bitStream->Read(blockData); - - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (UpdateBlockPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__UpdateBlockPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__UpdateBlockPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__UpdateBlockPacket_H__ + +#include "../Packet.h" + +class UpdateBlockPacket : public Packet +{ +public: + + int x, z; + unsigned char y; + unsigned char blockId; + unsigned char blockData; + + UpdateBlockPacket() + { + } + + UpdateBlockPacket(int _x, int _y, int _z, int _blockId, int _blockData) + { + x = _x; + y = (unsigned char)(_y & 0xff); + z = _z; + blockId = (unsigned char)(_blockId & 0xff); + blockData = (unsigned char)(_blockData & 0xff); + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_UPDATEBLOCK)); + + bitStream->Write(x); + bitStream->Write(z); + bitStream->Write(y); + bitStream->Write(blockId); + bitStream->Write(blockData); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(x); + bitStream->Read(z); + bitStream->Read(y); + bitStream->Read(blockId); + bitStream->Read(blockData); + + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (UpdateBlockPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__UpdateBlockPacket_H__*/ diff --git a/src/network/packet/UseItemPacket.h b/src/network/packet/UseItemPacket.h index e1583de..6554b20 100755 --- a/src/network/packet/UseItemPacket.h +++ b/src/network/packet/UseItemPacket.h @@ -1,86 +1,86 @@ -#ifndef NET_MINECRAFT_NETWORK_PACKET__UseItemPacket_H__ -#define NET_MINECRAFT_NETWORK_PACKET__UseItemPacket_H__ - -//package net.minecraft.network.packet; - -#include "../Packet.h" - -#include "../../world/item/ItemInstance.h" - -class UseItemPacket: public Packet -{ -public: - int x, y, z, face; - float clickX, clickY, clickZ; - int entityId; - short itemId; - unsigned char itemData; - ItemInstance item; - - UseItemPacket() {} - - UseItemPacket(int x, int y, int z, int face, const ItemInstance* item, int entityId, float clickX, float clickY, float clickZ) - : x(x), - y(y), - z(z), - face(face), - itemId(item? item->id : 0), - itemData(item? item->getAuxValue() : 0), - entityId(entityId), - clickX(clickX), - clickY(clickY), - clickZ(clickZ) - {} - - UseItemPacket(const ItemInstance* item, int entityId, const Vec3& aim) - : face(255), - itemId(item? item->id : 0), - itemData(item? item->getAuxValue() : 0), - entityId(entityId), - x((int)(aim.x * 32768.0f)), - y((int)(aim.y * 32768.0f)), - z((int)(aim.z * 32768.0f)) - { - } - - void write(RakNet::BitStream* bitStream) - { - bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_USEITEM)); - - bitStream->Write(x); - bitStream->Write(y); - bitStream->Write(z); - bitStream->Write(face); - bitStream->Write(itemId); - bitStream->Write(itemData); - bitStream->Write(entityId); - bitStream->Write(clickX); - bitStream->Write(clickY); - bitStream->Write(clickZ); - } - - void read(RakNet::BitStream* bitStream) - { - bitStream->Read(x); - bitStream->Read(y); - bitStream->Read(z); - bitStream->Read(face); - bitStream->Read(itemId); - bitStream->Read(itemData); - bitStream->Read(entityId); - bitStream->Read(clickX); - bitStream->Read(clickY); - bitStream->Read(clickZ); - item.id = itemId; - item.setAuxValue(itemData); - item.count = (itemId == 0 && itemData == 0)? 0 : 1; - - } - - void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) - { - callback->handle(source, (UseItemPacket*)this); - } -}; - -#endif /*NET_MINECRAFT_NETWORK_PACKET__UseItemPacket_H__*/ +#ifndef NET_MINECRAFT_NETWORK_PACKET__UseItemPacket_H__ +#define NET_MINECRAFT_NETWORK_PACKET__UseItemPacket_H__ + +//package net.minecraft.network.packet; + +#include "../Packet.h" + +#include "../../world/item/ItemInstance.h" + +class UseItemPacket: public Packet +{ +public: + int x, y, z, face; + float clickX, clickY, clickZ; + int entityId; + short itemId; + unsigned char itemData; + ItemInstance item; + + UseItemPacket() {} + + UseItemPacket(int x, int y, int z, int face, const ItemInstance* item, int entityId, float clickX, float clickY, float clickZ) + : x(x), + y(y), + z(z), + face(face), + itemId(item? item->id : 0), + itemData(item? item->getAuxValue() : 0), + entityId(entityId), + clickX(clickX), + clickY(clickY), + clickZ(clickZ) + {} + + UseItemPacket(const ItemInstance* item, int entityId, const Vec3& aim) + : face(255), + itemId(item? item->id : 0), + itemData(item? item->getAuxValue() : 0), + entityId(entityId), + x((int)(aim.x * 32768.0f)), + y((int)(aim.y * 32768.0f)), + z((int)(aim.z * 32768.0f)) + { + } + + void write(RakNet::BitStream* bitStream) + { + bitStream->Write((RakNet::MessageID)(ID_USER_PACKET_ENUM + PACKET_USEITEM)); + + bitStream->Write(x); + bitStream->Write(y); + bitStream->Write(z); + bitStream->Write(face); + bitStream->Write(itemId); + bitStream->Write(itemData); + bitStream->Write(entityId); + bitStream->Write(clickX); + bitStream->Write(clickY); + bitStream->Write(clickZ); + } + + void read(RakNet::BitStream* bitStream) + { + bitStream->Read(x); + bitStream->Read(y); + bitStream->Read(z); + bitStream->Read(face); + bitStream->Read(itemId); + bitStream->Read(itemData); + bitStream->Read(entityId); + bitStream->Read(clickX); + bitStream->Read(clickY); + bitStream->Read(clickZ); + item.id = itemId; + item.setAuxValue(itemData); + item.count = (itemId == 0 && itemData == 0)? 0 : 1; + + } + + void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback) + { + callback->handle(source, (UseItemPacket*)this); + } +}; + +#endif /*NET_MINECRAFT_NETWORK_PACKET__UseItemPacket_H__*/ diff --git a/src/platform/CThread.cpp b/src/platform/CThread.cpp index e136198..6ec6a74 100755 --- a/src/platform/CThread.cpp +++ b/src/platform/CThread.cpp @@ -1,73 +1,73 @@ -/* - * CThread.cpp - * oxeye - * - * Created by aegzorz on 2007-02-09. - * Copyright 2007 Mojang AB. All rights reserved. - * - */ - -#include "CThread.h" - - - - CThread::CThread( pthread_fn threadFunc, void* threadParam ) - { - #ifdef WIN32 - mp_threadFunc = (LPTHREAD_START_ROUTINE) threadFunc; - - m_threadHandle = CreateThread( - NULL, // pointer to security attributes - NULL, // initial thread stack size - mp_threadFunc, // pointer to thread function - threadParam, // argument for new thread - NULL, // creation flags - &m_threadID // pointer to receive thread ID - ); - #endif - #if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) || defined(__EMSCRIPTEN__) - mp_threadFunc = (pthread_fn)threadFunc; - - pthread_attr_init(&m_attributes); - pthread_attr_setdetachstate( &m_attributes, PTHREAD_CREATE_DETACHED ); - /*int error =*/ pthread_create(&m_thread, &m_attributes, mp_threadFunc, threadParam); - #endif - #ifdef MACOSX - mp_threadFunc = (TaskProc) threadFunc; - - MPCreateTask( - mp_threadFunc, // pointer to thread function - threadParam, // argument for new thread - 0, // initial thread stack size - NULL, // queue id - NULL, // termination param 1 - NULL, // termination param 2 - 0, // task options - &m_threadID // pointer to receive task ID - ); - #endif - } - - void CThread::sleep( const unsigned int millis ) - { - #ifdef WIN32 - Sleep( millis ); - #endif - #if defined(LINUX) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) - usleep(millis * 1000); - #endif - } - - CThread::~CThread() - { - #ifdef WIN32 - TerminateThread(m_threadHandle, 0); - #endif - #if defined(LINUX) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) - // Thread was created detached; pthread_join on a detached thread is undefined - // and causes SIGABRT when the pthread_t is no longer valid. - pthread_attr_destroy(&m_attributes); - #endif - } - - +/* + * CThread.cpp + * oxeye + * + * Created by aegzorz on 2007-02-09. + * Copyright 2007 Mojang AB. All rights reserved. + * + */ + +#include "CThread.h" + + + + CThread::CThread( pthread_fn threadFunc, void* threadParam ) + { + #ifdef WIN32 + mp_threadFunc = (LPTHREAD_START_ROUTINE) threadFunc; + + m_threadHandle = CreateThread( + NULL, // pointer to security attributes + NULL, // initial thread stack size + mp_threadFunc, // pointer to thread function + threadParam, // argument for new thread + NULL, // creation flags + &m_threadID // pointer to receive thread ID + ); + #endif + #if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) || defined(__EMSCRIPTEN__) + mp_threadFunc = (pthread_fn)threadFunc; + + pthread_attr_init(&m_attributes); + pthread_attr_setdetachstate( &m_attributes, PTHREAD_CREATE_DETACHED ); + /*int error =*/ pthread_create(&m_thread, &m_attributes, mp_threadFunc, threadParam); + #endif + #ifdef MACOSX + mp_threadFunc = (TaskProc) threadFunc; + + MPCreateTask( + mp_threadFunc, // pointer to thread function + threadParam, // argument for new thread + 0, // initial thread stack size + NULL, // queue id + NULL, // termination param 1 + NULL, // termination param 2 + 0, // task options + &m_threadID // pointer to receive task ID + ); + #endif + } + + void CThread::sleep( const unsigned int millis ) + { + #ifdef WIN32 + Sleep( millis ); + #endif + #if defined(LINUX) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) + usleep(millis * 1000); + #endif + } + + CThread::~CThread() + { + #ifdef WIN32 + TerminateThread(m_threadHandle, 0); + #endif + #if defined(LINUX) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) + // Thread was created detached; pthread_join on a detached thread is undefined + // and causes SIGABRT when the pthread_t is no longer valid. + pthread_attr_destroy(&m_attributes); + #endif + } + + diff --git a/src/platform/CThread.h b/src/platform/CThread.h index c608e92..11d814e 100755 --- a/src/platform/CThread.h +++ b/src/platform/CThread.h @@ -1,55 +1,55 @@ -/* - * CThread.h - * Created by aegzorz on 2007-02-09. - * Copyright 2007 Oxeye. All rights reserved. - */ - -#ifndef _OX_CORE_CTHREAD_H_ -#define _OX_CORE_CTHREAD_H_ - -#ifdef WIN32 - #include -#endif - -typedef void *( * pthread_fn )( void * ); - -#if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) || defined(__EMSCRIPTEN__) - #include - #include - -#endif -#ifdef MACOSX - #include - #include -#endif - - class CThread - { - public: - CThread( pthread_fn threadFunc, void* threadParam ); - - virtual ~CThread(); - - static void sleep( const unsigned int millis ); - - private: - #ifdef WIN32 - LPTHREAD_START_ROUTINE mp_threadFunc; - DWORD m_threadID; - HANDLE m_threadHandle; - #endif - #if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) || defined(__EMSCRIPTEN__) - pthread_fn mp_threadFunc; - pthread_t m_thread; - pthread_attr_t m_attributes; - #endif - #ifdef MACOSX - TaskProc mp_threadFunc; - MPTaskID m_threadID; - #endif - - }; - - - -#endif // _OX_CORE_CTHREAD_H_ +/* + * CThread.h + * Created by aegzorz on 2007-02-09. + * Copyright 2007 Oxeye. All rights reserved. + */ + +#ifndef _OX_CORE_CTHREAD_H_ +#define _OX_CORE_CTHREAD_H_ + +#ifdef WIN32 + #include +#endif + +typedef void *( * pthread_fn )( void * ); + +#if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) || defined(__EMSCRIPTEN__) + #include + #include + +#endif +#ifdef MACOSX + #include + #include +#endif + + class CThread + { + public: + CThread( pthread_fn threadFunc, void* threadParam ); + + virtual ~CThread(); + + static void sleep( const unsigned int millis ); + + private: + #ifdef WIN32 + LPTHREAD_START_ROUTINE mp_threadFunc; + DWORD m_threadID; + HANDLE m_threadHandle; + #endif + #if defined(__linux__) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX) || defined(__EMSCRIPTEN__) + pthread_fn mp_threadFunc; + pthread_t m_thread; + pthread_attr_t m_attributes; + #endif + #ifdef MACOSX + TaskProc mp_threadFunc; + MPTaskID m_threadID; + #endif + + }; + + + +#endif // _OX_CORE_CTHREAD_H_ diff --git a/src/platform/audio/SoundSystem.h b/src/platform/audio/SoundSystem.h index 7576e9f..50ff75d 100755 --- a/src/platform/audio/SoundSystem.h +++ b/src/platform/audio/SoundSystem.h @@ -1,29 +1,29 @@ -#ifndef SoundSystem_H__ -#define SoundSystem_H__ - -#include "../../world/level/tile/Tile.h" -#include - -class SoundDesc; - -class SoundSystem -{ -public: - SoundSystem() {} - virtual ~SoundSystem() {} - - virtual bool isAvailable() { return false; } - virtual void enable(bool status) {} - - virtual void setListenerPos(float x, float y, float z){} - virtual void setListenerAngle(float deg){} - - virtual void load(const std::string& name){} - virtual void play(const std::string& name){} - virtual void pause(const std::string& name){} - virtual void stop(const std::string& name){} - - virtual void playAt(const SoundDesc& desc, float x, float y, float z, float volume, float pitch){} -}; - -#endif /*SoundSystem_H__ */ +#ifndef SoundSystem_H__ +#define SoundSystem_H__ + +#include "../../world/level/tile/Tile.h" +#include + +class SoundDesc; + +class SoundSystem +{ +public: + SoundSystem() {} + virtual ~SoundSystem() {} + + virtual bool isAvailable() { return false; } + virtual void enable(bool status) {} + + virtual void setListenerPos(float x, float y, float z){} + virtual void setListenerAngle(float deg){} + + virtual void load(const std::string& name){} + virtual void play(const std::string& name){} + virtual void pause(const std::string& name){} + virtual void stop(const std::string& name){} + + virtual void playAt(const SoundDesc& desc, float x, float y, float z, float volume, float pitch){} +}; + +#endif /*SoundSystem_H__ */ diff --git a/src/platform/audio/SoundSystemAL.cpp b/src/platform/audio/SoundSystemAL.cpp index 7ea7289..04646ac 100755 --- a/src/platform/audio/SoundSystemAL.cpp +++ b/src/platform/audio/SoundSystemAL.cpp @@ -1,257 +1,257 @@ -//#include "ios/OpenALSupport.h" -#include "SoundSystemAL.h" -#include "../../util/Mth.h" -#include "../../world/level/tile/Tile.h" -#include "../../world/phys/Vec3.h" -#include "../../client/sound/Sound.h" - -#include "../log.h" - -static const char* errIdString = 0; - -void checkError() { - - while (1) { - ALenum err = alGetError(); - if(err == AL_NO_ERROR) return; - - LOGI("### SoundSystemAL error: %d ####: %s\n", err, errIdString==0?"(none)":errIdString); - } -} - -//typedef ALvoid AL_APIENTRY (*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid *data, ALsizei size, ALsizei freq); -//ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq) -//{ -// static alBufferDataStaticProcPtr proc = NULL; -// -// if (proc == NULL) { -// proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic"); -// } -// -// if (proc) -// proc(bid, format, data, size, freq); -// -// return; -//} -// -SoundSystemAL::SoundSystemAL() -: available(true), - context(0), - device(0), - _rotation(-9999.9f) -{ - _buffers.reserve(64); - init(); -} - -SoundSystemAL::~SoundSystemAL() -{ - alDeleteSources(MaxNumSources, _sources); - - for (int i = 0; i < (int)_buffers.size(); ++i) - if (_buffers[i].inited) alDeleteBuffers(1, &_buffers[i].bufferID); - - alcMakeContextCurrent(NULL); - alcDestroyContext(context); - - // Close the device - alcCloseDevice(device); -} - -void SoundSystemAL::init() -{ - device = alcOpenDevice(NULL); - if(device) { - context = alcCreateContext(device, NULL); - alcMakeContextCurrent(context); - - alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); - - alGenSources(MaxNumSources, _sources); - for(int index = 0; index < MaxNumSources; index++) { - ALuint sourceID = _sources[index]; - - alSourcef(sourceID, AL_REFERENCE_DISTANCE, 5.0f); - alSourcef(sourceID, AL_MAX_DISTANCE, 16.0f); - alSourcef(sourceID, AL_ROLLOFF_FACTOR, 6.0f); - } - - float listenerPos[] = {0, 0, 0}; - float listenerOri[] = {0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; - float listenerVel[] = {0, 0, 0}; - alListenerfv(AL_POSITION, listenerPos); - alListenerfv(AL_ORIENTATION, listenerOri); - alListenerfv(AL_VELOCITY, listenerVel); - - errIdString = "Init audio"; - checkError(); - } -} - -void SoundSystemAL::enable(bool status) { - LOGI("Enabling? audio: %d (context %p)\n", status, context); - if (status) { - alcMakeContextCurrent(context); - errIdString = "Enable audio"; - } - else { - alcMakeContextCurrent(NULL); - errIdString = "Disable audio"; - } - - checkError(); -} - -void SoundSystemAL::destroy() {} - -void SoundSystemAL::setListenerPos( float x, float y, float z ) -{ - // Note: listener position is thought to be 0,0,0 now - - /* - if (_listenerPos.x != x || _listenerPos.y != y || _listenerPos.z != z) { - _listenerPos.set(x, y, z); - alListener3f(AL_POSITION, x, y, z); - - static int _n = 0; - if (++_n == 20) { - _n = 0; - LOGI("Setting position for listener: %f, %f, %f\n", _listenerPos.x, _listenerPos.y, _listenerPos.z); - } - } - */ -} - -void SoundSystemAL::setListenerAngle( float deg ) -{ - if (_rotation != deg) { - _rotation = deg; - - float rad = deg * Mth::DEGRAD; - - static ALfloat orientation[] = {0, 0, 0, 0, 1, 0}; - orientation[0] = -Mth::sin( rad ); - orientation[2] = Mth::cos( rad ); - alListenerfv(AL_ORIENTATION, orientation); - } -} - -void SoundSystemAL::playAt( const SoundDesc& sound, float x, float y, float z, float volume, float pitch ) -{ - if (pitch < 0.01f) pitch = 1; - - //LOGI("playing sound '%s' with volume/pitch: %f, %f @ %f, %f, %f\n", sound.name.c_str(), volume, pitch, x, y, z); - - ALuint bufferID; - if (!getBufferId(sound, &bufferID)) { - errIdString = "Get buffer (failed)"; - checkError(); - LOGE("getBufferId returned false!\n"); - return; - } - errIdString = "Get buffer"; - checkError(); - //LOGI("playing sound %d - '%s' with volume/pitch: %f, %f @ %f, %f, %f\n", bufferID, sound.name.c_str(), volume, pitch, x, y, z); - - int sourceIndex; - errIdString = "Get free index"; - if (!getFreeSourceIndex(&sourceIndex)) { - LOGI("No free sound sources left @ SoundSystemAL::playAt\n"); - return; - } - - ALuint sourceID = _sources[sourceIndex]; - checkError(); - - alSourcei(sourceID, AL_BUFFER, 0); - errIdString = "unbind"; - checkError(); - alSourcei(sourceID, AL_BUFFER, bufferID); - errIdString = "bind"; - checkError(); - - alSourcef(sourceID, AL_PITCH, pitch); - errIdString = "pitch"; - checkError(); - alSourcef(sourceID, AL_GAIN, volume); - errIdString = "gain"; - checkError(); - - alSourcei(sourceID, AL_LOOPING, AL_FALSE); - errIdString = "looping"; - checkError(); - alSource3f(sourceID, AL_POSITION, x, y, z); - errIdString = "position"; - checkError(); - - alSourcePlay(sourceID); - errIdString = "source play"; - - checkError(); -} - -/*static*/ -void SoundSystemAL::removeStoppedSounds() -{ -} - -bool SoundSystemAL::getFreeSourceIndex(int* sourceIndex) { - for (int i = 0; i < MaxNumSources; ++i) { - ALint state; - alGetSourcei(_sources[i], AL_SOURCE_STATE, &state); - if(state != AL_PLAYING) { - *sourceIndex = i; - return true; - } - } - return false; -} - -bool SoundSystemAL::getBufferId(const SoundDesc& sound, ALuint* buf) { - for (int i = 0; i < (int)_buffers.size(); ++i) { - // Points to the same data buffer -> sounds equal - if (_buffers[i].framePtr == sound.frames) { - //LOGI("Found %p for %s!\n", sound.frames, sound.name.c_str()); - *buf = _buffers[i].bufferID; - return true; - } - } - - if (!sound.isValid()) { - LOGE("Err: sound is invalid @ getBufferId! %s\n", sound.name.c_str()); - return false; - } - - ALuint bufferID; - alGenBuffers(1, &bufferID); - errIdString = "Gen buffer"; - checkError(); - - ALenum format = (sound.byteWidth==2) ? - (sound.channels==2? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16) - : (sound.channels==2? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8); - - alBufferData(bufferID, format, sound.frames, sound.size, sound.frameRate); - //LOGI("Creating %d (%p) from sound: '%s'\n", bufferID, sound.frames, sound.name.c_str()); - errIdString = "Buffer data"; - //LOGI("Creating buffer with data: %d (%d), %p, %d, %d\n", format, sound.byteWidth, sound.frames, sound.size, sound.frameRate); - checkError(); - - //LOGI("Sound ch: %d, fmt: %d, frames: %p, len: %f, fr: %d, sz: %d, numfr: %d\n", sound.channels, format, sound.frames, sound.length(), sound.frameRate, sound.size, sound.numFrames); - - - Buffer buffer; - buffer.inited = true; - buffer.framePtr = sound.frames; - buffer.bufferID = bufferID; - *buf = bufferID; - _buffers.push_back(buffer); - - // @huge @attn @note @fix: The original data is free'd - // On PLATFORM_DESKTOP the PCM data lives in static arrays (not heap), - // so calling delete[] on them causes a debug-heap __debugbreak crash. -#if !defined(PLATFORM_DESKTOP) - sound.destroy(); -#endif - return true; -} +//#include "ios/OpenALSupport.h" +#include "SoundSystemAL.h" +#include "../../util/Mth.h" +#include "../../world/level/tile/Tile.h" +#include "../../world/phys/Vec3.h" +#include "../../client/sound/Sound.h" + +#include "../log.h" + +static const char* errIdString = 0; + +void checkError() { + + while (1) { + ALenum err = alGetError(); + if(err == AL_NO_ERROR) return; + + LOGI("### SoundSystemAL error: %d ####: %s\n", err, errIdString==0?"(none)":errIdString); + } +} + +//typedef ALvoid AL_APIENTRY (*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid *data, ALsizei size, ALsizei freq); +//ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq) +//{ +// static alBufferDataStaticProcPtr proc = NULL; +// +// if (proc == NULL) { +// proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic"); +// } +// +// if (proc) +// proc(bid, format, data, size, freq); +// +// return; +//} +// +SoundSystemAL::SoundSystemAL() +: available(true), + context(0), + device(0), + _rotation(-9999.9f) +{ + _buffers.reserve(64); + init(); +} + +SoundSystemAL::~SoundSystemAL() +{ + alDeleteSources(MaxNumSources, _sources); + + for (int i = 0; i < (int)_buffers.size(); ++i) + if (_buffers[i].inited) alDeleteBuffers(1, &_buffers[i].bufferID); + + alcMakeContextCurrent(NULL); + alcDestroyContext(context); + + // Close the device + alcCloseDevice(device); +} + +void SoundSystemAL::init() +{ + device = alcOpenDevice(NULL); + if(device) { + context = alcCreateContext(device, NULL); + alcMakeContextCurrent(context); + + alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); + + alGenSources(MaxNumSources, _sources); + for(int index = 0; index < MaxNumSources; index++) { + ALuint sourceID = _sources[index]; + + alSourcef(sourceID, AL_REFERENCE_DISTANCE, 5.0f); + alSourcef(sourceID, AL_MAX_DISTANCE, 16.0f); + alSourcef(sourceID, AL_ROLLOFF_FACTOR, 6.0f); + } + + float listenerPos[] = {0, 0, 0}; + float listenerOri[] = {0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; + float listenerVel[] = {0, 0, 0}; + alListenerfv(AL_POSITION, listenerPos); + alListenerfv(AL_ORIENTATION, listenerOri); + alListenerfv(AL_VELOCITY, listenerVel); + + errIdString = "Init audio"; + checkError(); + } +} + +void SoundSystemAL::enable(bool status) { + LOGI("Enabling? audio: %d (context %p)\n", status, context); + if (status) { + alcMakeContextCurrent(context); + errIdString = "Enable audio"; + } + else { + alcMakeContextCurrent(NULL); + errIdString = "Disable audio"; + } + + checkError(); +} + +void SoundSystemAL::destroy() {} + +void SoundSystemAL::setListenerPos( float x, float y, float z ) +{ + // Note: listener position is thought to be 0,0,0 now + + /* + if (_listenerPos.x != x || _listenerPos.y != y || _listenerPos.z != z) { + _listenerPos.set(x, y, z); + alListener3f(AL_POSITION, x, y, z); + + static int _n = 0; + if (++_n == 20) { + _n = 0; + LOGI("Setting position for listener: %f, %f, %f\n", _listenerPos.x, _listenerPos.y, _listenerPos.z); + } + } + */ +} + +void SoundSystemAL::setListenerAngle( float deg ) +{ + if (_rotation != deg) { + _rotation = deg; + + float rad = deg * Mth::DEGRAD; + + static ALfloat orientation[] = {0, 0, 0, 0, 1, 0}; + orientation[0] = -Mth::sin( rad ); + orientation[2] = Mth::cos( rad ); + alListenerfv(AL_ORIENTATION, orientation); + } +} + +void SoundSystemAL::playAt( const SoundDesc& sound, float x, float y, float z, float volume, float pitch ) +{ + if (pitch < 0.01f) pitch = 1; + + //LOGI("playing sound '%s' with volume/pitch: %f, %f @ %f, %f, %f\n", sound.name.c_str(), volume, pitch, x, y, z); + + ALuint bufferID; + if (!getBufferId(sound, &bufferID)) { + errIdString = "Get buffer (failed)"; + checkError(); + LOGE("getBufferId returned false!\n"); + return; + } + errIdString = "Get buffer"; + checkError(); + //LOGI("playing sound %d - '%s' with volume/pitch: %f, %f @ %f, %f, %f\n", bufferID, sound.name.c_str(), volume, pitch, x, y, z); + + int sourceIndex; + errIdString = "Get free index"; + if (!getFreeSourceIndex(&sourceIndex)) { + LOGI("No free sound sources left @ SoundSystemAL::playAt\n"); + return; + } + + ALuint sourceID = _sources[sourceIndex]; + checkError(); + + alSourcei(sourceID, AL_BUFFER, 0); + errIdString = "unbind"; + checkError(); + alSourcei(sourceID, AL_BUFFER, bufferID); + errIdString = "bind"; + checkError(); + + alSourcef(sourceID, AL_PITCH, pitch); + errIdString = "pitch"; + checkError(); + alSourcef(sourceID, AL_GAIN, volume); + errIdString = "gain"; + checkError(); + + alSourcei(sourceID, AL_LOOPING, AL_FALSE); + errIdString = "looping"; + checkError(); + alSource3f(sourceID, AL_POSITION, x, y, z); + errIdString = "position"; + checkError(); + + alSourcePlay(sourceID); + errIdString = "source play"; + + checkError(); +} + +/*static*/ +void SoundSystemAL::removeStoppedSounds() +{ +} + +bool SoundSystemAL::getFreeSourceIndex(int* sourceIndex) { + for (int i = 0; i < MaxNumSources; ++i) { + ALint state; + alGetSourcei(_sources[i], AL_SOURCE_STATE, &state); + if(state != AL_PLAYING) { + *sourceIndex = i; + return true; + } + } + return false; +} + +bool SoundSystemAL::getBufferId(const SoundDesc& sound, ALuint* buf) { + for (int i = 0; i < (int)_buffers.size(); ++i) { + // Points to the same data buffer -> sounds equal + if (_buffers[i].framePtr == sound.frames) { + //LOGI("Found %p for %s!\n", sound.frames, sound.name.c_str()); + *buf = _buffers[i].bufferID; + return true; + } + } + + if (!sound.isValid()) { + LOGE("Err: sound is invalid @ getBufferId! %s\n", sound.name.c_str()); + return false; + } + + ALuint bufferID; + alGenBuffers(1, &bufferID); + errIdString = "Gen buffer"; + checkError(); + + ALenum format = (sound.byteWidth==2) ? + (sound.channels==2? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16) + : (sound.channels==2? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8); + + alBufferData(bufferID, format, sound.frames, sound.size, sound.frameRate); + //LOGI("Creating %d (%p) from sound: '%s'\n", bufferID, sound.frames, sound.name.c_str()); + errIdString = "Buffer data"; + //LOGI("Creating buffer with data: %d (%d), %p, %d, %d\n", format, sound.byteWidth, sound.frames, sound.size, sound.frameRate); + checkError(); + + //LOGI("Sound ch: %d, fmt: %d, frames: %p, len: %f, fr: %d, sz: %d, numfr: %d\n", sound.channels, format, sound.frames, sound.length(), sound.frameRate, sound.size, sound.numFrames); + + + Buffer buffer; + buffer.inited = true; + buffer.framePtr = sound.frames; + buffer.bufferID = bufferID; + *buf = bufferID; + _buffers.push_back(buffer); + + // @huge @attn @note @fix: The original data is free'd + // On PLATFORM_DESKTOP the PCM data lives in static arrays (not heap), + // so calling delete[] on them causes a debug-heap __debugbreak crash. +#if !defined(PLATFORM_DESKTOP) + sound.destroy(); +#endif + return true; +} diff --git a/src/platform/audio/SoundSystemAL.h b/src/platform/audio/SoundSystemAL.h index a7c225f..fe12ce6 100755 --- a/src/platform/audio/SoundSystemAL.h +++ b/src/platform/audio/SoundSystemAL.h @@ -1,79 +1,79 @@ -#ifndef SoundSystemAL_H__ -#define SoundSystemAL_H__ - -#include "SoundSystem.h" - -// If audio breaks for other platforms, this is probably why. Here, I'm just calling where Apple's OpenAL headers live (they arent in just "AL" -#if defined(__APPLE__) - #include - #include -#else - #include - #include -#endif - -#include -#include - -// -// NOTE: This class is only the core OpenAL part of the sound engine. -// Some audio setup code can still be managed from respective app -// setup code (e.g. the main app delegate for iOS). -// - -class SoundSystemAL: public SoundSystem -{ - //typedef std::list SoundList; -public: - SoundSystemAL(); - ~SoundSystemAL(); - - virtual void init(); - virtual void destroy(); - - virtual void enable(bool status); - - virtual void setListenerPos(float x, float y, float z); - virtual void setListenerAngle(float deg); - - virtual void load(const std::string& name){} - virtual void play(const std::string& name){} - virtual void pause(const std::string& name){} - virtual void stop(const std::string& name){} - virtual void playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch); - -private: - class Buffer { - public: - Buffer() - : inited(false) - {} - bool inited; - ALuint bufferID; - char* framePtr; - }; - - void removeStoppedSounds(); - - static const int MaxNumSources = 12; - - //SoundList playingBuffers; - - Vec3 _listenerPos; - float _rotation; - - bool available; - - ALCcontext* context; - ALCdevice* device; - - ALuint _sources[MaxNumSources]; - std::vector _buffers; - - bool getFreeSourceIndex(int* src); - bool getBufferId(const SoundDesc& sound, ALuint* buf); - -public: -}; - -#endif /*SoundSystemAL_H__ */ +#ifndef SoundSystemAL_H__ +#define SoundSystemAL_H__ + +#include "SoundSystem.h" + +// If audio breaks for other platforms, this is probably why. Here, I'm just calling where Apple's OpenAL headers live (they arent in just "AL" +#if defined(__APPLE__) + #include + #include +#else + #include + #include +#endif + +#include +#include + +// +// NOTE: This class is only the core OpenAL part of the sound engine. +// Some audio setup code can still be managed from respective app +// setup code (e.g. the main app delegate for iOS). +// + +class SoundSystemAL: public SoundSystem +{ + //typedef std::list SoundList; +public: + SoundSystemAL(); + ~SoundSystemAL(); + + virtual void init(); + virtual void destroy(); + + virtual void enable(bool status); + + virtual void setListenerPos(float x, float y, float z); + virtual void setListenerAngle(float deg); + + virtual void load(const std::string& name){} + virtual void play(const std::string& name){} + virtual void pause(const std::string& name){} + virtual void stop(const std::string& name){} + virtual void playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch); + +private: + class Buffer { + public: + Buffer() + : inited(false) + {} + bool inited; + ALuint bufferID; + char* framePtr; + }; + + void removeStoppedSounds(); + + static const int MaxNumSources = 12; + + //SoundList playingBuffers; + + Vec3 _listenerPos; + float _rotation; + + bool available; + + ALCcontext* context; + ALCdevice* device; + + ALuint _sources[MaxNumSources]; + std::vector _buffers; + + bool getFreeSourceIndex(int* src); + bool getBufferId(const SoundDesc& sound, ALuint* buf); + +public: +}; + +#endif /*SoundSystemAL_H__ */ diff --git a/src/platform/audio/SoundSystemSL.cpp b/src/platform/audio/SoundSystemSL.cpp index 15b4d7c..c079249 100755 --- a/src/platform/audio/SoundSystemSL.cpp +++ b/src/platform/audio/SoundSystemSL.cpp @@ -1,243 +1,243 @@ -#include "SoundSystemSL.h" -#include -#include -#include "../../util/Mth.h" -#include "../../world/level/tile/Tile.h" -#include "../../world/phys/Vec3.h" -#include "../../client/sound/Sound.h" - -#include "../log.h" - -// Only one engine can be created at once. You CAN (if you really want) -// start two games at once, then it will crash without objEngine being static. -/*static*/ SLObjectItf SoundSystemSL::objEngine = 0; - -typedef struct t_context { - SLObjectItf obj; - Mutex* mutex; -} t_context; - -Mutex SoundSystemSL::toRemoveMutex; -std::vector SoundSystemSL::toRemove; - -SoundSystemSL::SoundSystemSL() -: available(true), - listener(NULL), - numBuffersPlaying(0) -{ - init(); -} - -SoundSystemSL::~SoundSystemSL() -{ - toRemoveMutex.unlock(); - for (SoundList::iterator it = playingBuffers.begin(); it != playingBuffers.end(); ++it) - (**it)->Destroy(*it); - (*objOutput)->Destroy(objOutput); - - if (SoundSystemSL::objEngine != 0) { - (*SoundSystemSL::objEngine)->Destroy(SoundSystemSL::objEngine); - SoundSystemSL::objEngine = 0; - } -} - -void SoundSystemSL::init() -{ - SoundSystemSL::toRemove.clear(); - SoundSystemSL::toRemove.reserve(MAX_BUFFERS_PLAYING); - toRemoveCopy.resize(MAX_BUFFERS_PLAYING); - - SLresult res; - const int MAX_NUMBER_INTERFACES = 2; - SLboolean required[MAX_NUMBER_INTERFACES]; - SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; - - SLEngineOption EngineOption[] = {(SLuint32) - SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE}; - - /* Create OpenSL ES (destroy first if needed)*/ - if (SoundSystemSL::objEngine != 0) - (*SoundSystemSL::objEngine)->Destroy(SoundSystemSL::objEngine); - - res = slCreateEngine( &SoundSystemSL::objEngine, 1, EngineOption, 0, NULL, NULL); - checkErr(res); - - /* Realizing the SL Engine in synchronous mode. */ - res = (*SoundSystemSL::objEngine)->Realize(SoundSystemSL::objEngine, SL_BOOLEAN_FALSE); - if (checkErr(res)) { - available = false; - return; - } - - (*SoundSystemSL::objEngine)->GetInterface(SoundSystemSL::objEngine, SL_IID_ENGINE, (void*)&engEngine); - checkErr(res); - - /* Create Output Mix object to be used by player - no interfaces - required */ - res = (*engEngine)->CreateOutputMix(engEngine, &objOutput, 0, iidArray, required); - checkErr(res); - - /* Realizing the Output Mix object in synchronous mode. */ - res = (*objOutput)->Realize(objOutput, SL_BOOLEAN_FALSE); - checkErr(res); -} - -void SoundSystemSL::destroy() {} - -void SoundSystemSL::setListenerPos( float x, float y, float z ) -{ - if (!listener) { - listenerPos = Vec3(x, y, z); - return; - } - - SLVec3D pos = {(SLint32)(1000.0f * x), (SLint32)(1000.0f * y), (SLint32)(1000.0f * z)}; - SLresult res = (*listener)->SetLocationCartesian(listener, &pos); - checkErr(res); -} - -void SoundSystemSL::setListenerAngle( float deg ) -{ - if (!listener) return; - - SLresult res = (*listener)->SetOrientationAngles(listener, deg*1000.0f, 0, 0); - checkErr(res); -} - -void SoundSystemSL::playAt( const SoundDesc& sound, float x, float y, float z, float volume, float pitch ) -{ - removeStoppedSounds(); - - if (numBuffersPlaying >= MAX_BUFFERS_PLAYING) - return; - - /* Setup the data source structure for the player */ - SLDataLocator_AndroidSimpleBufferQueue uri = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; - SLDataFormat_PCM mime = { - SL_DATAFORMAT_PCM, - (SLuint32)sound.channels, - (SLuint32)(sound.frameRate * 1000), - (SLuint32)(sound.byteWidth << 3), - (SLuint32)(sound.byteWidth << 3), - sound.channels==1? SL_SPEAKER_FRONT_CENTER : - SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, - SL_BYTEORDER_LITTLEENDIAN - }; - SLDataSource audioSource = {&uri, &mime}; - SLDataLocator_OutputMix locator_outputmix; - SLDataSink audioSink; - SLObjectItf player; - SLPlayItf playItf; - //SL3DLocationItf locationItf; - SLVolumeItf volumeItf; - - /* Setup the data sink structure */ - locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; - locator_outputmix.outputMix = objOutput; - audioSink.pLocator = (void *)&locator_outputmix; - audioSink.pFormat = NULL; - - /* Buffer queue-able */ - static SLboolean required[2]; - static SLInterfaceID iidArray[2]; - required[0] = SL_BOOLEAN_TRUE; - iidArray[0] = SL_IID_BUFFERQUEUE; - required[1] = SL_BOOLEAN_TRUE; - iidArray[1] = SL_IID_VOLUME; - - /* Create the 3D player */ - SLresult res = (*engEngine)->CreateAudioPlayer(engEngine, &player, - &audioSource, &audioSink, 2, iidArray, required); - //printf("SL: Created audio player\n"); - checkErr(res); - - /* Realizing the player in synchronous mode. */ - res = (*player)->Realize(player, SL_BOOLEAN_FALSE); - //LOGI("SL: Realize audio player\n"); - checkErr(res); - - /* Get the play and volume interfaces */ - res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); - //LOGI("SL: Get Player interface\n"); - checkErr(res); - - res = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&volumeItf); - //LOGI("SL: Get Player interface\n"); - checkErr(res); - - SLmillibel maxVolume; - res = (*volumeItf)->GetMaxVolumeLevel(volumeItf, &maxVolume); - SLmillibel mbelVolume = maxVolume - 2000 * (1-volume);//Mth::lerp(SL_MILLIBEL_MIN, maxVolume, 0.95f + 0.05f*volume); - LOGI("min: %d, max: %d, current: %d (%f)\n", SL_MILLIBEL_MIN, maxVolume, mbelVolume, volume); - res = (*volumeItf)->SetVolumeLevel(volumeItf, mbelVolume); - checkErr(res); - - SLAndroidSimpleBufferQueueItf buffer1; - res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, &buffer1); - checkErr(res); - - //t_context* context = new t_context(); //{ player, &toRemoveMutex }; - //context->obj = player; - //context->mutex = &toRemoveMutex; - res = (*buffer1)->RegisterCallback(buffer1, SoundSystemSL::removePlayer, (void*)player); - checkErr(res); - - res = (*buffer1)->Enqueue(buffer1, sound.frames, sound.size); - checkErr(res); - - /* Start playing the 3D source */ - res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); - //LOGI("SL: Set play state\n"); - checkErr(res); - - playingBuffers.push_back(player); - ++numBuffersPlaying; -} - -bool SoundSystemSL::checkErr( SLresult res ) -{ - if ( res != SL_RESULT_SUCCESS ) { - LOGI("OpenSL error: %d\n", res); - return true; - } - return false; -} - -/*static*/ -void SoundSystemSL::removeStoppedSounds() -{ - toRemoveMutex.lock(); - const int numBuffersToRemove = toRemove.size(); - for (int i = 0; i < numBuffersToRemove; ++i) - toRemoveCopy[i] = toRemove[i]; - SoundSystemSL::toRemove.clear(); - toRemoveMutex.unlock(); - - for (int i = 0; i < numBuffersToRemove; ++i) { - SLObjectItf obj = toRemoveCopy[i]; - - SoundList::iterator it = playingBuffers.begin(); - while (it != playingBuffers.end()) { - if (*it == obj) { - playingBuffers.erase(it); - break; - } - ++it; - } - (*obj)->Destroy(obj); - --numBuffersPlaying; - } -} - -void SoundSystemSL::removePlayer( SLAndroidSimpleBufferQueueItf bq, void *context_ ) -{ - //t_context* context = (t_context*) context_; - //context->mutex->lock(); - //SoundSystemSL::toRemove.push_back( context->obj ); - //context->mutex->unlock(); - //delete context; - - SoundSystemSL::toRemoveMutex.lock(); - SoundSystemSL::toRemove.push_back( (SLObjectItf) context_ ); - SoundSystemSL::toRemoveMutex.unlock(); -} +#include "SoundSystemSL.h" +#include +#include +#include "../../util/Mth.h" +#include "../../world/level/tile/Tile.h" +#include "../../world/phys/Vec3.h" +#include "../../client/sound/Sound.h" + +#include "../log.h" + +// Only one engine can be created at once. You CAN (if you really want) +// start two games at once, then it will crash without objEngine being static. +/*static*/ SLObjectItf SoundSystemSL::objEngine = 0; + +typedef struct t_context { + SLObjectItf obj; + Mutex* mutex; +} t_context; + +Mutex SoundSystemSL::toRemoveMutex; +std::vector SoundSystemSL::toRemove; + +SoundSystemSL::SoundSystemSL() +: available(true), + listener(NULL), + numBuffersPlaying(0) +{ + init(); +} + +SoundSystemSL::~SoundSystemSL() +{ + toRemoveMutex.unlock(); + for (SoundList::iterator it = playingBuffers.begin(); it != playingBuffers.end(); ++it) + (**it)->Destroy(*it); + (*objOutput)->Destroy(objOutput); + + if (SoundSystemSL::objEngine != 0) { + (*SoundSystemSL::objEngine)->Destroy(SoundSystemSL::objEngine); + SoundSystemSL::objEngine = 0; + } +} + +void SoundSystemSL::init() +{ + SoundSystemSL::toRemove.clear(); + SoundSystemSL::toRemove.reserve(MAX_BUFFERS_PLAYING); + toRemoveCopy.resize(MAX_BUFFERS_PLAYING); + + SLresult res; + const int MAX_NUMBER_INTERFACES = 2; + SLboolean required[MAX_NUMBER_INTERFACES]; + SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; + + SLEngineOption EngineOption[] = {(SLuint32) + SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE}; + + /* Create OpenSL ES (destroy first if needed)*/ + if (SoundSystemSL::objEngine != 0) + (*SoundSystemSL::objEngine)->Destroy(SoundSystemSL::objEngine); + + res = slCreateEngine( &SoundSystemSL::objEngine, 1, EngineOption, 0, NULL, NULL); + checkErr(res); + + /* Realizing the SL Engine in synchronous mode. */ + res = (*SoundSystemSL::objEngine)->Realize(SoundSystemSL::objEngine, SL_BOOLEAN_FALSE); + if (checkErr(res)) { + available = false; + return; + } + + (*SoundSystemSL::objEngine)->GetInterface(SoundSystemSL::objEngine, SL_IID_ENGINE, (void*)&engEngine); + checkErr(res); + + /* Create Output Mix object to be used by player - no interfaces + required */ + res = (*engEngine)->CreateOutputMix(engEngine, &objOutput, 0, iidArray, required); + checkErr(res); + + /* Realizing the Output Mix object in synchronous mode. */ + res = (*objOutput)->Realize(objOutput, SL_BOOLEAN_FALSE); + checkErr(res); +} + +void SoundSystemSL::destroy() {} + +void SoundSystemSL::setListenerPos( float x, float y, float z ) +{ + if (!listener) { + listenerPos = Vec3(x, y, z); + return; + } + + SLVec3D pos = {(SLint32)(1000.0f * x), (SLint32)(1000.0f * y), (SLint32)(1000.0f * z)}; + SLresult res = (*listener)->SetLocationCartesian(listener, &pos); + checkErr(res); +} + +void SoundSystemSL::setListenerAngle( float deg ) +{ + if (!listener) return; + + SLresult res = (*listener)->SetOrientationAngles(listener, deg*1000.0f, 0, 0); + checkErr(res); +} + +void SoundSystemSL::playAt( const SoundDesc& sound, float x, float y, float z, float volume, float pitch ) +{ + removeStoppedSounds(); + + if (numBuffersPlaying >= MAX_BUFFERS_PLAYING) + return; + + /* Setup the data source structure for the player */ + SLDataLocator_AndroidSimpleBufferQueue uri = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; + SLDataFormat_PCM mime = { + SL_DATAFORMAT_PCM, + (SLuint32)sound.channels, + (SLuint32)(sound.frameRate * 1000), + (SLuint32)(sound.byteWidth << 3), + (SLuint32)(sound.byteWidth << 3), + sound.channels==1? SL_SPEAKER_FRONT_CENTER : + SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, + SL_BYTEORDER_LITTLEENDIAN + }; + SLDataSource audioSource = {&uri, &mime}; + SLDataLocator_OutputMix locator_outputmix; + SLDataSink audioSink; + SLObjectItf player; + SLPlayItf playItf; + //SL3DLocationItf locationItf; + SLVolumeItf volumeItf; + + /* Setup the data sink structure */ + locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; + locator_outputmix.outputMix = objOutput; + audioSink.pLocator = (void *)&locator_outputmix; + audioSink.pFormat = NULL; + + /* Buffer queue-able */ + static SLboolean required[2]; + static SLInterfaceID iidArray[2]; + required[0] = SL_BOOLEAN_TRUE; + iidArray[0] = SL_IID_BUFFERQUEUE; + required[1] = SL_BOOLEAN_TRUE; + iidArray[1] = SL_IID_VOLUME; + + /* Create the 3D player */ + SLresult res = (*engEngine)->CreateAudioPlayer(engEngine, &player, + &audioSource, &audioSink, 2, iidArray, required); + //printf("SL: Created audio player\n"); + checkErr(res); + + /* Realizing the player in synchronous mode. */ + res = (*player)->Realize(player, SL_BOOLEAN_FALSE); + //LOGI("SL: Realize audio player\n"); + checkErr(res); + + /* Get the play and volume interfaces */ + res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); + //LOGI("SL: Get Player interface\n"); + checkErr(res); + + res = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&volumeItf); + //LOGI("SL: Get Player interface\n"); + checkErr(res); + + SLmillibel maxVolume; + res = (*volumeItf)->GetMaxVolumeLevel(volumeItf, &maxVolume); + SLmillibel mbelVolume = maxVolume - 2000 * (1-volume);//Mth::lerp(SL_MILLIBEL_MIN, maxVolume, 0.95f + 0.05f*volume); + LOGI("min: %d, max: %d, current: %d (%f)\n", SL_MILLIBEL_MIN, maxVolume, mbelVolume, volume); + res = (*volumeItf)->SetVolumeLevel(volumeItf, mbelVolume); + checkErr(res); + + SLAndroidSimpleBufferQueueItf buffer1; + res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, &buffer1); + checkErr(res); + + //t_context* context = new t_context(); //{ player, &toRemoveMutex }; + //context->obj = player; + //context->mutex = &toRemoveMutex; + res = (*buffer1)->RegisterCallback(buffer1, SoundSystemSL::removePlayer, (void*)player); + checkErr(res); + + res = (*buffer1)->Enqueue(buffer1, sound.frames, sound.size); + checkErr(res); + + /* Start playing the 3D source */ + res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); + //LOGI("SL: Set play state\n"); + checkErr(res); + + playingBuffers.push_back(player); + ++numBuffersPlaying; +} + +bool SoundSystemSL::checkErr( SLresult res ) +{ + if ( res != SL_RESULT_SUCCESS ) { + LOGI("OpenSL error: %d\n", res); + return true; + } + return false; +} + +/*static*/ +void SoundSystemSL::removeStoppedSounds() +{ + toRemoveMutex.lock(); + const int numBuffersToRemove = toRemove.size(); + for (int i = 0; i < numBuffersToRemove; ++i) + toRemoveCopy[i] = toRemove[i]; + SoundSystemSL::toRemove.clear(); + toRemoveMutex.unlock(); + + for (int i = 0; i < numBuffersToRemove; ++i) { + SLObjectItf obj = toRemoveCopy[i]; + + SoundList::iterator it = playingBuffers.begin(); + while (it != playingBuffers.end()) { + if (*it == obj) { + playingBuffers.erase(it); + break; + } + ++it; + } + (*obj)->Destroy(obj); + --numBuffersPlaying; + } +} + +void SoundSystemSL::removePlayer( SLAndroidSimpleBufferQueueItf bq, void *context_ ) +{ + //t_context* context = (t_context*) context_; + //context->mutex->lock(); + //SoundSystemSL::toRemove.push_back( context->obj ); + //context->mutex->unlock(); + //delete context; + + SoundSystemSL::toRemoveMutex.lock(); + SoundSystemSL::toRemove.push_back( (SLObjectItf) context_ ); + SoundSystemSL::toRemoveMutex.unlock(); +} diff --git a/src/platform/audio/SoundSystemSL.h b/src/platform/audio/SoundSystemSL.h index 0606d91..69fbde5 100755 --- a/src/platform/audio/SoundSystemSL.h +++ b/src/platform/audio/SoundSystemSL.h @@ -1,79 +1,79 @@ -#ifndef SoundSystemSL_H__ -#define SoundSystemSL_H__ - -#include "SoundSystem.h" -#include -#include - -#include -#include - -#ifdef ANDROID -#include -class Mutex { - pthread_mutex_t _mutex; -public: - Mutex() { pthread_mutex_init(&_mutex, NULL); } - ~Mutex() { pthread_mutex_destroy(&_mutex); } - void lock() { pthread_mutex_lock(&_mutex); } - void unlock() { pthread_mutex_unlock(&_mutex); } -}; -#else -class Mutex { -public: - Mutex() {} - void lock() {} - void unlock() {}; -}; -#endif - - -class SoundSystemSL: public SoundSystem -{ - typedef std::list SoundList; -public: - SoundSystemSL(); - ~SoundSystemSL(); - - virtual void init(); - virtual void destroy(); - - virtual void setListenerPos(float x, float y, float z); - virtual void setListenerAngle(float deg); - - virtual void load(const std::string& name){} - virtual void play(const std::string& name){} - virtual void pause(const std::string& name){} - virtual void stop(const std::string& name){} - virtual void playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch); - -private: - bool checkErr( SLresult res ); - - void removeStoppedSounds(); - - SoundList playingBuffers; - - static SLObjectItf objEngine; - SLEngineItf engEngine; - - SLObjectItf objListener; - SL3DLocationItf listener; - - SLObjectItf objOutput; - - Vec3 listenerPos; - int numBuffersPlaying; - - bool available; - std::vector toRemoveCopy; - - static Mutex toRemoveMutex; - static std::vector toRemove; - static const int MAX_BUFFERS_PLAYING = 4; - -public: - static void removePlayer(SLAndroidSimpleBufferQueueItf bq, void *context); -}; - -#endif /*SoundSystemSL_H__ */ +#ifndef SoundSystemSL_H__ +#define SoundSystemSL_H__ + +#include "SoundSystem.h" +#include +#include + +#include +#include + +#ifdef ANDROID +#include +class Mutex { + pthread_mutex_t _mutex; +public: + Mutex() { pthread_mutex_init(&_mutex, NULL); } + ~Mutex() { pthread_mutex_destroy(&_mutex); } + void lock() { pthread_mutex_lock(&_mutex); } + void unlock() { pthread_mutex_unlock(&_mutex); } +}; +#else +class Mutex { +public: + Mutex() {} + void lock() {} + void unlock() {}; +}; +#endif + + +class SoundSystemSL: public SoundSystem +{ + typedef std::list SoundList; +public: + SoundSystemSL(); + ~SoundSystemSL(); + + virtual void init(); + virtual void destroy(); + + virtual void setListenerPos(float x, float y, float z); + virtual void setListenerAngle(float deg); + + virtual void load(const std::string& name){} + virtual void play(const std::string& name){} + virtual void pause(const std::string& name){} + virtual void stop(const std::string& name){} + virtual void playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch); + +private: + bool checkErr( SLresult res ); + + void removeStoppedSounds(); + + SoundList playingBuffers; + + static SLObjectItf objEngine; + SLEngineItf engEngine; + + SLObjectItf objListener; + SL3DLocationItf listener; + + SLObjectItf objOutput; + + Vec3 listenerPos; + int numBuffersPlaying; + + bool available; + std::vector toRemoveCopy; + + static Mutex toRemoveMutex; + static std::vector toRemove; + static const int MAX_BUFFERS_PLAYING = 4; + +public: + static void removePlayer(SLAndroidSimpleBufferQueueItf bq, void *context); +}; + +#endif /*SoundSystemSL_H__ */ diff --git a/src/platform/file.h b/src/platform/file.h index af57bee..677a86e 100755 --- a/src/platform/file.h +++ b/src/platform/file.h @@ -1,68 +1,68 @@ -#ifndef FILE_H__ -#define FILE_H__ - -bool DeleteDirectory(const std::string&, bool noRecycleBin = true); - -#ifdef WIN32 - #include - #include - #include - #include - -bool DeleteDirectory(const std::string& dir, bool noRecycleBin /*true*/) -{ - int len = strlen(dir.c_str()); - //TCHAR *pszFrom = new TCHAR[len+2]; - char* pszFrom = new char[len+2]; - strncpy(pszFrom, dir.c_str(), len); - pszFrom[len] = 0; - pszFrom[len+1] = 0; - - SHFILEOPSTRUCT fileop; - fileop.hwnd = NULL; // no status display - fileop.wFunc = FO_DELETE; // delete operation - fileop.pFrom = pszFrom; // source file name as double null terminated string - fileop.pTo = NULL; // no destination needed - fileop.fFlags = FOF_NOCONFIRMATION|FOF_SILENT; // do not prompt the user - - if(!noRecycleBin) - fileop.fFlags |= FOF_ALLOWUNDO; - - fileop.fAnyOperationsAborted = FALSE; - fileop.lpszProgressTitle = NULL; - fileop.hNameMappings = NULL; - - int ret = SHFileOperation(&fileop); - delete [] pszFrom; - return (ret == 0); -} -#else - #include - #include - -bool DeleteDirectory(const std::string& d, bool noRecycleBin /*true*/) -{ - const char* folder = d.c_str(); - - const size_t CMAX = 1024; - char fullPath[CMAX]; - DIR* dir = opendir(folder); - if (!dir) - return false; - - struct dirent *entry; - while ((entry = readdir(dir))) { - if (strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { - snprintf(fullPath, CMAX, "%s/%s", folder, entry->d_name); - remove(fullPath); - } - } - - closedir(dir); - - return remove(folder) == 0; -} - -#endif /*(ELSE) WIN32*/ - -#endif /*FILE_H__*/ +#ifndef FILE_H__ +#define FILE_H__ + +bool DeleteDirectory(const std::string&, bool noRecycleBin = true); + +#ifdef WIN32 + #include + #include + #include + #include + +bool DeleteDirectory(const std::string& dir, bool noRecycleBin /*true*/) +{ + int len = strlen(dir.c_str()); + //TCHAR *pszFrom = new TCHAR[len+2]; + char* pszFrom = new char[len+2]; + strncpy(pszFrom, dir.c_str(), len); + pszFrom[len] = 0; + pszFrom[len+1] = 0; + + SHFILEOPSTRUCT fileop; + fileop.hwnd = NULL; // no status display + fileop.wFunc = FO_DELETE; // delete operation + fileop.pFrom = pszFrom; // source file name as double null terminated string + fileop.pTo = NULL; // no destination needed + fileop.fFlags = FOF_NOCONFIRMATION|FOF_SILENT; // do not prompt the user + + if(!noRecycleBin) + fileop.fFlags |= FOF_ALLOWUNDO; + + fileop.fAnyOperationsAborted = FALSE; + fileop.lpszProgressTitle = NULL; + fileop.hNameMappings = NULL; + + int ret = SHFileOperation(&fileop); + delete [] pszFrom; + return (ret == 0); +} +#else + #include + #include + +bool DeleteDirectory(const std::string& d, bool noRecycleBin /*true*/) +{ + const char* folder = d.c_str(); + + const size_t CMAX = 1024; + char fullPath[CMAX]; + DIR* dir = opendir(folder); + if (!dir) + return false; + + struct dirent *entry; + while ((entry = readdir(dir))) { + if (strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { + snprintf(fullPath, CMAX, "%s/%s", folder, entry->d_name); + remove(fullPath); + } + } + + closedir(dir); + + return remove(folder) == 0; +} + +#endif /*(ELSE) WIN32*/ + +#endif /*FILE_H__*/ diff --git a/src/platform/input/Controller.cpp b/src/platform/input/Controller.cpp index 71ccabb..888067b 100755 --- a/src/platform/input/Controller.cpp +++ b/src/platform/input/Controller.cpp @@ -1,75 +1,75 @@ -#include "Controller.h" - -static int _abs(int x) { return x>=0? x:-x; } -static float _abs(float x) { return x>=0? x:-x; } - -/*static*/ float Controller::stickValuesX[NUM_STICKS] = {0}; -/*static*/ float Controller::stickValuesY[NUM_STICKS] = {0}; -/*static*/ bool Controller::isTouchedValues[NUM_STICKS] = {0}; - -bool Controller::isTouched( int stickIndex ) -{ - if (!isValidStick(stickIndex)) return false; - - return isTouchedValues[stickIndex-1]; -} - -void Controller::feed( int stickIndex, int state, float dx, float dy ) -{ - if (!isValidStick(stickIndex)) return; - - if (NUM_STICKS == 2) - stickIndex = dx<0? 1 : 2; - - isTouchedValues[stickIndex-1] = (state != STATE_RELEASE); - - // @note: Since I don't know where to put the Xperia Play specific - // calculations, I put them here! (normally I would probably have - // some kind of (XperiaPlay)ControllerReader but it doesn't make much - // more sense as long as we cant figure out (in code) whether or not - // we actually use an Xperia -> hardcode it here (Note#2, we CAN figure - // figure this out, at least by JNI/java-call but we arent doing it) - static float offsets[3] = {0, 0.64f, -0.64f}; - dx = linearTransform(dx + offsets[stickIndex], 0, 2.78f, true); - - stickValuesX[stickIndex-1] = dx; - stickValuesY[stickIndex-1] = dy; -} - -float Controller::getX( int stickIndex ) -{ - if (!isValidStick(stickIndex)) return 0; - return stickValuesX[stickIndex-1]; -} - -float Controller::getY( int stickIndex ) -{ - if (!isValidStick(stickIndex)) return 0; - return stickValuesY[stickIndex-1]; -} - -float Controller::getTransformedX( int stickIndex, float deadZone, float scale/*=1.0f*/, bool limit1/*=false*/ ) -{ - if (!isValidStick(stickIndex)) return 0; - return linearTransform(stickValuesX[stickIndex-1], deadZone, scale, limit1); -} - -float Controller::getTransformedY( int stickIndex, float deadZone, float scale/*=1.0f*/, bool limit1/*=false*/ ) -{ - if (!isValidStick(stickIndex)) return 0; - return linearTransform(stickValuesY[stickIndex-1], deadZone, scale, limit1); -} - -float Controller::linearTransform( float value, float deadZone, float scale/*=1.0f*/, bool limit1/*=false*/ ) -{ - float deadSigned = value >= 0? deadZone : -deadZone; - if (_abs(deadSigned) >= _abs(value)) return 0; - float ret = (value - deadSigned) * scale; - if (limit1 && _abs(ret) > 1) ret = ret>0.0f? 1.0f : -1.0f; - return ret; -} - -/*static*/ -bool Controller::isValidStick(int stick) { - return stick > 0 && stick <= NUM_STICKS; -} +#include "Controller.h" + +static int _abs(int x) { return x>=0? x:-x; } +static float _abs(float x) { return x>=0? x:-x; } + +/*static*/ float Controller::stickValuesX[NUM_STICKS] = {0}; +/*static*/ float Controller::stickValuesY[NUM_STICKS] = {0}; +/*static*/ bool Controller::isTouchedValues[NUM_STICKS] = {0}; + +bool Controller::isTouched( int stickIndex ) +{ + if (!isValidStick(stickIndex)) return false; + + return isTouchedValues[stickIndex-1]; +} + +void Controller::feed( int stickIndex, int state, float dx, float dy ) +{ + if (!isValidStick(stickIndex)) return; + + if (NUM_STICKS == 2) + stickIndex = dx<0? 1 : 2; + + isTouchedValues[stickIndex-1] = (state != STATE_RELEASE); + + // @note: Since I don't know where to put the Xperia Play specific + // calculations, I put them here! (normally I would probably have + // some kind of (XperiaPlay)ControllerReader but it doesn't make much + // more sense as long as we cant figure out (in code) whether or not + // we actually use an Xperia -> hardcode it here (Note#2, we CAN figure + // figure this out, at least by JNI/java-call but we arent doing it) + static float offsets[3] = {0, 0.64f, -0.64f}; + dx = linearTransform(dx + offsets[stickIndex], 0, 2.78f, true); + + stickValuesX[stickIndex-1] = dx; + stickValuesY[stickIndex-1] = dy; +} + +float Controller::getX( int stickIndex ) +{ + if (!isValidStick(stickIndex)) return 0; + return stickValuesX[stickIndex-1]; +} + +float Controller::getY( int stickIndex ) +{ + if (!isValidStick(stickIndex)) return 0; + return stickValuesY[stickIndex-1]; +} + +float Controller::getTransformedX( int stickIndex, float deadZone, float scale/*=1.0f*/, bool limit1/*=false*/ ) +{ + if (!isValidStick(stickIndex)) return 0; + return linearTransform(stickValuesX[stickIndex-1], deadZone, scale, limit1); +} + +float Controller::getTransformedY( int stickIndex, float deadZone, float scale/*=1.0f*/, bool limit1/*=false*/ ) +{ + if (!isValidStick(stickIndex)) return 0; + return linearTransform(stickValuesY[stickIndex-1], deadZone, scale, limit1); +} + +float Controller::linearTransform( float value, float deadZone, float scale/*=1.0f*/, bool limit1/*=false*/ ) +{ + float deadSigned = value >= 0? deadZone : -deadZone; + if (_abs(deadSigned) >= _abs(value)) return 0; + float ret = (value - deadSigned) * scale; + if (limit1 && _abs(ret) > 1) ret = ret>0.0f? 1.0f : -1.0f; + return ret; +} + +/*static*/ +bool Controller::isValidStick(int stick) { + return stick > 0 && stick <= NUM_STICKS; +} diff --git a/src/platform/input/Controller.h b/src/platform/input/Controller.h index 49ae289..faed858 100755 --- a/src/platform/input/Controller.h +++ b/src/platform/input/Controller.h @@ -1,33 +1,33 @@ -#ifndef CONTROLLER_H__ -#define CONTROLLER_H__ - -#include "../log.h" - -class Controller -{ -public: - static const int NUM_STICKS = 2; - static const int STATE_TOUCH = 1; - static const int STATE_RELEASE = 0; - static const int STATE_MOVE =-1; - - static bool isTouched(int stickIndex); - - static void feed(int stickIndex, int state, float dx, float dy); - - static float getX(int stickIndex); - static float getY(int stickIndex); - - static float getTransformedX(int stickIndex, float deadZone, float scale=1.0f, bool limit1=false); - static float getTransformedY(int stickIndex, float deadZone, float scale=1.0f, bool limit1=false); - -private: - static bool isValidStick(int stick); - static float linearTransform(float value, float deadZone, float scale=1.0f, bool limit1=false); - - static float stickValuesX[NUM_STICKS]; - static float stickValuesY[NUM_STICKS]; - static bool isTouchedValues[NUM_STICKS]; -}; - -#endif /*CONTROLLER_H__*/ +#ifndef CONTROLLER_H__ +#define CONTROLLER_H__ + +#include "../log.h" + +class Controller +{ +public: + static const int NUM_STICKS = 2; + static const int STATE_TOUCH = 1; + static const int STATE_RELEASE = 0; + static const int STATE_MOVE =-1; + + static bool isTouched(int stickIndex); + + static void feed(int stickIndex, int state, float dx, float dy); + + static float getX(int stickIndex); + static float getY(int stickIndex); + + static float getTransformedX(int stickIndex, float deadZone, float scale=1.0f, bool limit1=false); + static float getTransformedY(int stickIndex, float deadZone, float scale=1.0f, bool limit1=false); + +private: + static bool isValidStick(int stick); + static float linearTransform(float value, float deadZone, float scale=1.0f, bool limit1=false); + + static float stickValuesX[NUM_STICKS]; + static float stickValuesY[NUM_STICKS]; + static bool isTouchedValues[NUM_STICKS]; +}; + +#endif /*CONTROLLER_H__*/ diff --git a/src/platform/input/Keyboard.cpp b/src/platform/input/Keyboard.cpp index 3f9f0df..ede83a7 100755 --- a/src/platform/input/Keyboard.cpp +++ b/src/platform/input/Keyboard.cpp @@ -1,59 +1,59 @@ -#include "Keyboard.h" - -int Keyboard::_states[256] = {0}; - -std::vector Keyboard::_inputs; -std::vector Keyboard::_inputText; - -int Keyboard::_index = -1; - -int Keyboard::_textIndex = -1; - -const char* Keyboard::getKeyName(int key) { - switch (key) { - case KEY_A: return "A"; - case KEY_B: return "B"; - case KEY_C: return "C"; - case KEY_D: return "D"; - case KEY_E: return "E"; - case KEY_F: return "F"; - case KEY_G: return "G"; - case KEY_H: return "H"; - case KEY_I: return "I"; - case KEY_J: return "J"; - case KEY_K: return "K"; - case KEY_L: return "L"; - case KEY_M: return "M"; - case KEY_N: return "N"; - case KEY_O: return "O"; - case KEY_P: return "P"; - case KEY_Q: return "Q"; - case KEY_R: return "R"; - case KEY_S: return "S"; - case KEY_T: return "T"; - case KEY_U: return "U"; - case KEY_V: return "V"; - case KEY_W: return "W"; - case KEY_X: return "X"; - case KEY_Y: return "Y"; - case KEY_Z: return "Z"; - case KEY_BACKSPACE: return "Backspace"; - case KEY_RETURN: return "Return"; - case KEY_F1: return "F1"; - case KEY_F2: return "F2"; - case KEY_F3: return "F3"; - case KEY_F4: return "F4"; - case KEY_F5: return "F5"; - case KEY_F6: return "F6"; - case KEY_F7: return "F7"; - case KEY_F8: return "F8"; - case KEY_F9: return "F9"; - case KEY_F10: return "F10"; - case KEY_F11: return "F11"; - case KEY_F12: return "F12"; - case KEY_ESCAPE: return "Esc"; - case KEY_SPACE: return "Space"; - case KEY_LSHIFT: return "Left Shift"; - default: return "Unknown"; - } +#include "Keyboard.h" + +int Keyboard::_states[256] = {0}; + +std::vector Keyboard::_inputs; +std::vector Keyboard::_inputText; + +int Keyboard::_index = -1; + +int Keyboard::_textIndex = -1; + +const char* Keyboard::getKeyName(int key) { + switch (key) { + case KEY_A: return "A"; + case KEY_B: return "B"; + case KEY_C: return "C"; + case KEY_D: return "D"; + case KEY_E: return "E"; + case KEY_F: return "F"; + case KEY_G: return "G"; + case KEY_H: return "H"; + case KEY_I: return "I"; + case KEY_J: return "J"; + case KEY_K: return "K"; + case KEY_L: return "L"; + case KEY_M: return "M"; + case KEY_N: return "N"; + case KEY_O: return "O"; + case KEY_P: return "P"; + case KEY_Q: return "Q"; + case KEY_R: return "R"; + case KEY_S: return "S"; + case KEY_T: return "T"; + case KEY_U: return "U"; + case KEY_V: return "V"; + case KEY_W: return "W"; + case KEY_X: return "X"; + case KEY_Y: return "Y"; + case KEY_Z: return "Z"; + case KEY_BACKSPACE: return "Backspace"; + case KEY_RETURN: return "Return"; + case KEY_F1: return "F1"; + case KEY_F2: return "F2"; + case KEY_F3: return "F3"; + case KEY_F4: return "F4"; + case KEY_F5: return "F5"; + case KEY_F6: return "F6"; + case KEY_F7: return "F7"; + case KEY_F8: return "F8"; + case KEY_F9: return "F9"; + case KEY_F10: return "F10"; + case KEY_F11: return "F11"; + case KEY_F12: return "F12"; + case KEY_ESCAPE: return "Esc"; + case KEY_SPACE: return "Space"; + case KEY_LSHIFT: return "Left Shift"; + default: return "Unknown"; + } } \ No newline at end of file diff --git a/src/platform/input/Keyboard.h b/src/platform/input/Keyboard.h index 91868a0..01c052e 100755 --- a/src/platform/input/Keyboard.h +++ b/src/platform/input/Keyboard.h @@ -1,142 +1,142 @@ -#ifndef KEYBOARD_H__ -#define KEYBOARD_H__ - -#include - -/** A keyboard action; key-down or key-up */ -class KeyboardAction -{ -public: - static const int KEYUP = 0; - static const int KEYDOWN = 1; - - KeyboardAction(unsigned char keyCode, int state) { - this->key = keyCode; - this->state = state; - } - - int state; - unsigned char key; -}; - -/* Iterators */ -typedef std::vector KeyboardActionVec; -typedef KeyboardActionVec::iterator KeyboardActionIt; -typedef KeyboardActionVec::const_iterator KeyboardActionCIt; - -/** A static keyboard class, written to resemble the one in lwjgl somewhat */ -class Keyboard -{ -public: - static const int KEY_A = 65; - static const int KEY_B = 66; - static const int KEY_C = 67; - static const int KEY_D = 68; - static const int KEY_E = 69; - static const int KEY_F = 70; - static const int KEY_G = 71; - static const int KEY_H = 72; - static const int KEY_I = 73; - static const int KEY_J = 74; - static const int KEY_K = 75; - static const int KEY_L = 76; - static const int KEY_M = 77; - static const int KEY_N = 78; - static const int KEY_O = 79; - static const int KEY_P = 80; - static const int KEY_Q = 81; - static const int KEY_R = 82; - static const int KEY_S = 83; - static const int KEY_T = 84; - static const int KEY_U = 85; - static const int KEY_V = 86; - static const int KEY_W = 87; - static const int KEY_X = 88; - static const int KEY_Y = 89; - static const int KEY_Z = 90; - - static const int KEY_BACKSPACE = 8; - static const int KEY_TAB = 9; - static const int KEY_RETURN = 13; - - static const int KEY_F1 = 112; - static const int KEY_F2 = 113; - static const int KEY_F3 = 114; - static const int KEY_F4 = 115; - static const int KEY_F5 = 116; - static const int KEY_F6 = 117; - static const int KEY_F7 = 118; - static const int KEY_F8 = 119; - static const int KEY_F9 = 120; - static const int KEY_F10 = 121; - static const int KEY_F11 = 122; - static const int KEY_F12 = 123; - - - static const int KEY_ESCAPE = 27; - static const int KEY_SPACE = 32; - static const int KEY_LSHIFT = 10; - static const int KEY_LEFT_CTRL = 232; - - static bool isKeyDown(int keyCode) { - return _states[keyCode] == KeyboardAction::KEYDOWN; - } - - static void reset() { - _inputs.clear(); - _inputText.clear(); - _index = -1; - _textIndex = -1; - } - - static void feed(unsigned char keyCode, int state) { - _inputs.push_back(KeyboardAction(keyCode, state)); - - _states[keyCode] = state; - } - static void feedText(char character) { - _inputText.push_back(character); - } - - static bool next() { - if (_index + 1 >= (int)_inputs.size()) - return false; - - ++_index; - return true; - } - - static bool nextTextChar() { - if(_textIndex + 1 >= (int)_inputText.size()) - return false; - ++_textIndex; - return true; - } - - static void rewind() { - _index = -1; - _textIndex = -1; - } - - static int getEventKey() { - return _inputs[_index].key; - } - - static int getEventKeyState() { - return _inputs[_index].state; - } - static char getChar() { - return _inputText[_textIndex]; - } - - static const char* getKeyName(int key); -private: - static int _index; - static int _textIndex; - static int _states[256]; - static std::vector _inputs; - static std::vector _inputText; - static bool _inited; -}; - -#endif//KEYBOARD_H__ +#ifndef KEYBOARD_H__ +#define KEYBOARD_H__ + +#include + +/** A keyboard action; key-down or key-up */ +class KeyboardAction +{ +public: + static const int KEYUP = 0; + static const int KEYDOWN = 1; + + KeyboardAction(unsigned char keyCode, int state) { + this->key = keyCode; + this->state = state; + } + + int state; + unsigned char key; +}; + +/* Iterators */ +typedef std::vector KeyboardActionVec; +typedef KeyboardActionVec::iterator KeyboardActionIt; +typedef KeyboardActionVec::const_iterator KeyboardActionCIt; + +/** A static keyboard class, written to resemble the one in lwjgl somewhat */ +class Keyboard +{ +public: + static const int KEY_A = 65; + static const int KEY_B = 66; + static const int KEY_C = 67; + static const int KEY_D = 68; + static const int KEY_E = 69; + static const int KEY_F = 70; + static const int KEY_G = 71; + static const int KEY_H = 72; + static const int KEY_I = 73; + static const int KEY_J = 74; + static const int KEY_K = 75; + static const int KEY_L = 76; + static const int KEY_M = 77; + static const int KEY_N = 78; + static const int KEY_O = 79; + static const int KEY_P = 80; + static const int KEY_Q = 81; + static const int KEY_R = 82; + static const int KEY_S = 83; + static const int KEY_T = 84; + static const int KEY_U = 85; + static const int KEY_V = 86; + static const int KEY_W = 87; + static const int KEY_X = 88; + static const int KEY_Y = 89; + static const int KEY_Z = 90; + + static const int KEY_BACKSPACE = 8; + static const int KEY_TAB = 9; + static const int KEY_RETURN = 13; + + static const int KEY_F1 = 112; + static const int KEY_F2 = 113; + static const int KEY_F3 = 114; + static const int KEY_F4 = 115; + static const int KEY_F5 = 116; + static const int KEY_F6 = 117; + static const int KEY_F7 = 118; + static const int KEY_F8 = 119; + static const int KEY_F9 = 120; + static const int KEY_F10 = 121; + static const int KEY_F11 = 122; + static const int KEY_F12 = 123; + + + static const int KEY_ESCAPE = 27; + static const int KEY_SPACE = 32; + static const int KEY_LSHIFT = 10; + static const int KEY_LEFT_CTRL = 232; + + static bool isKeyDown(int keyCode) { + return _states[keyCode] == KeyboardAction::KEYDOWN; + } + + static void reset() { + _inputs.clear(); + _inputText.clear(); + _index = -1; + _textIndex = -1; + } + + static void feed(unsigned char keyCode, int state) { + _inputs.push_back(KeyboardAction(keyCode, state)); + + _states[keyCode] = state; + } + static void feedText(char character) { + _inputText.push_back(character); + } + + static bool next() { + if (_index + 1 >= (int)_inputs.size()) + return false; + + ++_index; + return true; + } + + static bool nextTextChar() { + if(_textIndex + 1 >= (int)_inputText.size()) + return false; + ++_textIndex; + return true; + } + + static void rewind() { + _index = -1; + _textIndex = -1; + } + + static int getEventKey() { + return _inputs[_index].key; + } + + static int getEventKeyState() { + return _inputs[_index].state; + } + static char getChar() { + return _inputText[_textIndex]; + } + + static const char* getKeyName(int key); +private: + static int _index; + static int _textIndex; + static int _states[256]; + static std::vector _inputs; + static std::vector _inputText; + static bool _inited; +}; + +#endif//KEYBOARD_H__ diff --git a/src/platform/input/Mouse.cpp b/src/platform/input/Mouse.cpp index 05d77ac..18b5ab4 100755 --- a/src/platform/input/Mouse.cpp +++ b/src/platform/input/Mouse.cpp @@ -1,169 +1,169 @@ -#include "Mouse.h" - -// -// MouseAction -// -MouseAction::MouseAction(char actionButtonId, char buttonData, short x, short y, char pointerId) -{ - this->action = actionButtonId; - this->data = buttonData; - this->x = x; - this->y = y; - this->dx = this->dy = 0; - this->pointerId = pointerId; -} - -MouseAction::MouseAction(char actionButtonId, char buttonData, short x, short y, short dx, short dy, char pointerId) -{ - this->action = actionButtonId; - this->data = buttonData; - this->x = x; - this->y = y; - this->dx = dx; - this->dy = dy; - this->pointerId = pointerId; -} - -bool MouseAction::isButton() const -{ - return action == ACTION_LEFT || action == ACTION_RIGHT; -} - -// -// MouseDevice -// -MouseDevice::MouseDevice() -: _index(-1), - _x(0), _xOld(0), - _y(0), _yOld(0), - _dx(DELTA_NOTSET), _dy(DELTA_NOTSET), - _firstMovementType(0) -{ - for (int i = 0; i < MAX_NUM_BUTTONS; ++i) - _buttonStates[i] = 0; -} - -void MouseDevice::reset() { - _index = -1; - _inputs.clear(); - _buttonStates[MouseAction::ACTION_WHEEL] = 0; -} - -char MouseDevice::getButtonState(int buttonId) { - if (buttonId < MouseAction::ACTION_LEFT || buttonId > MouseAction::ACTION_WHEEL) - return 0; - return _buttonStates[buttonId]; -} - -bool MouseDevice::isButtonDown(int buttonId) { - return getButtonState(buttonId) != 0; -} - -/// Was the current movement the first movement after mouse down? -bool MouseDevice::wasFirstMovement() { - return _firstMovementType == 1; -} - -short MouseDevice::getX() { return _x; } -short MouseDevice::getY() { return _y; } - -short MouseDevice::getDX() { return (DELTA_NOTSET != _dx)? _dx : _x - _xOld; } -short MouseDevice::getDY() { return (DELTA_NOTSET != _dy)? _dy : _y - _yOld; } - -void MouseDevice::reset2() { - _xOld = _x; - _yOld = _y; - _dx = _dy = DELTA_NOTSET; -} - -bool MouseDevice::next() { - if (_index + 1 >= (int)_inputs.size()) - return false; - - ++_index; - return true; -} - -void MouseDevice::rewind() { - _index = -1; -} - -bool MouseDevice::getEventButtonState() { - return _inputs[_index].data == MouseAction::DATA_DOWN; -} - -char MouseDevice::getEventButton() { - return _inputs[_index].action; -} - -const MouseAction& MouseDevice::getEvent() { return _inputs[_index]; } - -void MouseDevice::feed(char actionButtonId, char buttonData, short x, short y) { - feed(actionButtonId, buttonData, x, y, 0, 0); -} - -void MouseDevice::feed(char actionButtonId, char buttonData, short x, short y, short dx, short dy) { - - _inputs.push_back(MouseAction(actionButtonId, buttonData, x, y, dx, dy, 0)); - - if (actionButtonId != MouseAction::ACTION_MOVE) { - _buttonStates[actionButtonId] = buttonData; - - if (actionButtonId == MouseAction::ACTION_LEFT) - _firstMovementType = -1; - } else { - if (_dx == DELTA_NOTSET) { - _dx = _dy = 0; - } - _dx += dx; - _dy += dy; - - if (_firstMovementType == -1) - _firstMovementType = 1; - else - _firstMovementType = 0; - } - - _xOld = _x; - _yOld = _y; - _x = x; - _y = y; -} - -// -// Mouse - static class wrapping a MouseDevice -// -void Mouse::reset() { _instance.reset(); } - -char Mouse::getButtonState(int buttonId) { return _instance.getButtonState(buttonId); } - -bool Mouse::isButtonDown(int buttonId) { return _instance.isButtonDown(buttonId); } - -short Mouse::getX() { return _instance.getX(); } -short Mouse::getY() { return _instance.getY(); } - -short Mouse::getDX() { return _instance.getDX(); } -short Mouse::getDY() { return _instance.getDY(); } - -void Mouse::reset2() { _instance.reset2(); } - -bool Mouse::next() { return _instance.next(); } -void Mouse::rewind() { _instance.rewind(); } - -bool Mouse::getEventButtonState() { return _instance.getEventButtonState(); } - -char Mouse::getEventButton() { return _instance.getEventButton(); } - -const MouseAction& Mouse::getEvent() { return _instance.getEvent(); } - -void Mouse::feed(char actionButtonId, char buttonData, short x, short y) { - feed(actionButtonId, buttonData, x, y, 0, 0); -} -void Mouse::feed(char actionButtonId, char buttonData, short x, short y, short dx, short dy) { - //LOGI("Mouse::feed: %d, %d, xy: %d, %d\n", actionButtonId, buttonData, x, y); - return _instance.feed(actionButtonId, buttonData, x, y, dx, dy); -} - - -MouseDevice Mouse::_instance; - +#include "Mouse.h" + +// +// MouseAction +// +MouseAction::MouseAction(char actionButtonId, char buttonData, short x, short y, char pointerId) +{ + this->action = actionButtonId; + this->data = buttonData; + this->x = x; + this->y = y; + this->dx = this->dy = 0; + this->pointerId = pointerId; +} + +MouseAction::MouseAction(char actionButtonId, char buttonData, short x, short y, short dx, short dy, char pointerId) +{ + this->action = actionButtonId; + this->data = buttonData; + this->x = x; + this->y = y; + this->dx = dx; + this->dy = dy; + this->pointerId = pointerId; +} + +bool MouseAction::isButton() const +{ + return action == ACTION_LEFT || action == ACTION_RIGHT; +} + +// +// MouseDevice +// +MouseDevice::MouseDevice() +: _index(-1), + _x(0), _xOld(0), + _y(0), _yOld(0), + _dx(DELTA_NOTSET), _dy(DELTA_NOTSET), + _firstMovementType(0) +{ + for (int i = 0; i < MAX_NUM_BUTTONS; ++i) + _buttonStates[i] = 0; +} + +void MouseDevice::reset() { + _index = -1; + _inputs.clear(); + _buttonStates[MouseAction::ACTION_WHEEL] = 0; +} + +char MouseDevice::getButtonState(int buttonId) { + if (buttonId < MouseAction::ACTION_LEFT || buttonId > MouseAction::ACTION_WHEEL) + return 0; + return _buttonStates[buttonId]; +} + +bool MouseDevice::isButtonDown(int buttonId) { + return getButtonState(buttonId) != 0; +} + +/// Was the current movement the first movement after mouse down? +bool MouseDevice::wasFirstMovement() { + return _firstMovementType == 1; +} + +short MouseDevice::getX() { return _x; } +short MouseDevice::getY() { return _y; } + +short MouseDevice::getDX() { return (DELTA_NOTSET != _dx)? _dx : _x - _xOld; } +short MouseDevice::getDY() { return (DELTA_NOTSET != _dy)? _dy : _y - _yOld; } + +void MouseDevice::reset2() { + _xOld = _x; + _yOld = _y; + _dx = _dy = DELTA_NOTSET; +} + +bool MouseDevice::next() { + if (_index + 1 >= (int)_inputs.size()) + return false; + + ++_index; + return true; +} + +void MouseDevice::rewind() { + _index = -1; +} + +bool MouseDevice::getEventButtonState() { + return _inputs[_index].data == MouseAction::DATA_DOWN; +} + +char MouseDevice::getEventButton() { + return _inputs[_index].action; +} + +const MouseAction& MouseDevice::getEvent() { return _inputs[_index]; } + +void MouseDevice::feed(char actionButtonId, char buttonData, short x, short y) { + feed(actionButtonId, buttonData, x, y, 0, 0); +} + +void MouseDevice::feed(char actionButtonId, char buttonData, short x, short y, short dx, short dy) { + + _inputs.push_back(MouseAction(actionButtonId, buttonData, x, y, dx, dy, 0)); + + if (actionButtonId != MouseAction::ACTION_MOVE) { + _buttonStates[actionButtonId] = buttonData; + + if (actionButtonId == MouseAction::ACTION_LEFT) + _firstMovementType = -1; + } else { + if (_dx == DELTA_NOTSET) { + _dx = _dy = 0; + } + _dx += dx; + _dy += dy; + + if (_firstMovementType == -1) + _firstMovementType = 1; + else + _firstMovementType = 0; + } + + _xOld = _x; + _yOld = _y; + _x = x; + _y = y; +} + +// +// Mouse - static class wrapping a MouseDevice +// +void Mouse::reset() { _instance.reset(); } + +char Mouse::getButtonState(int buttonId) { return _instance.getButtonState(buttonId); } + +bool Mouse::isButtonDown(int buttonId) { return _instance.isButtonDown(buttonId); } + +short Mouse::getX() { return _instance.getX(); } +short Mouse::getY() { return _instance.getY(); } + +short Mouse::getDX() { return _instance.getDX(); } +short Mouse::getDY() { return _instance.getDY(); } + +void Mouse::reset2() { _instance.reset2(); } + +bool Mouse::next() { return _instance.next(); } +void Mouse::rewind() { _instance.rewind(); } + +bool Mouse::getEventButtonState() { return _instance.getEventButtonState(); } + +char Mouse::getEventButton() { return _instance.getEventButton(); } + +const MouseAction& Mouse::getEvent() { return _instance.getEvent(); } + +void Mouse::feed(char actionButtonId, char buttonData, short x, short y) { + feed(actionButtonId, buttonData, x, y, 0, 0); +} +void Mouse::feed(char actionButtonId, char buttonData, short x, short y, short dx, short dy) { + //LOGI("Mouse::feed: %d, %d, xy: %d, %d\n", actionButtonId, buttonData, x, y); + return _instance.feed(actionButtonId, buttonData, x, y, dx, dy); +} + + +MouseDevice Mouse::_instance; + diff --git a/src/platform/input/Mouse.h b/src/platform/input/Mouse.h index f7caccc..c56a4e3 100755 --- a/src/platform/input/Mouse.h +++ b/src/platform/input/Mouse.h @@ -1,112 +1,112 @@ -#ifndef MOUSE_H__ -#define MOUSE_H__ - -#include -#include "../log.h" - - -/** A mouse action such as a button press, release or mouse movement */ -class MouseAction -{ -public: - static const char ACTION_MOVE = 0; - static const char ACTION_LEFT = 1; - static const char ACTION_RIGHT = 2; - static const char ACTION_WHEEL = 3; - - static const char DATA_UP = 0; - static const char DATA_DOWN = 1; - - MouseAction(char actionButtonId, char buttonData, short x, short y, char pointerId); - MouseAction(char actionButtonId, char buttonData, short x, short y, short dx, short dy, char pointerId); - - bool isButton() const; - - short x, y; - short dx, dy; - char action; - char data; - char pointerId; -}; - -/* Iterators */ -typedef std::vector MouseActionVec; -typedef MouseActionVec::iterator MouseActionIt; -typedef MouseActionVec::const_iterator MouseActionCIt; - - -class MouseDevice -{ -public: - static const int MAX_NUM_BUTTONS = 4; - - MouseDevice(); - - char getButtonState(int buttonId); - bool isButtonDown(int buttonId); - - /// Was the current movement the first movement after mouse down? - bool wasFirstMovement(); - - short getX(); - short getY(); - - short getDX(); - short getDY(); - - void reset(); - void reset2(); - - bool next(); - void rewind(); - - bool getEventButtonState(); - char getEventButton(); - const MouseAction& getEvent(); - - void feed(char actionButtonId, char buttonData, short x, short y); - void feed(char actionButtonId, char buttonData, short x, short y, short dx, short dy); - -private: - int _index; - short _x, _y; - short _dx, _dy; - short _xOld, _yOld; - char _buttonStates[MAX_NUM_BUTTONS]; - std::vector _inputs; - int _firstMovementType; - static const int DELTA_NOTSET = -9999; -}; - -/** A static mouse class, written to resemble the one in lwjgl somewhat - UPDATE: which is very silly, since it doesn't support multi touch... */ -class Mouse -{ -public: - static char getButtonState(int buttonId); - static bool isButtonDown(int buttonId); - - static short getX(); - static short getY(); - - static short getDX(); - static short getDY(); - - static void reset(); - static void reset2(); - - static bool next(); - static void rewind(); - - static bool getEventButtonState(); - static char getEventButton(); - static const MouseAction& getEvent(); - - static void feed(char actionButtonId, char buttonData, short x, short y); - static void feed(char actionButtonId, char buttonData, short x, short y, short dx, short dy); - -private: - static MouseDevice _instance; -}; - -#endif//MOUSE_H__ +#ifndef MOUSE_H__ +#define MOUSE_H__ + +#include +#include "../log.h" + + +/** A mouse action such as a button press, release or mouse movement */ +class MouseAction +{ +public: + static const char ACTION_MOVE = 0; + static const char ACTION_LEFT = 1; + static const char ACTION_RIGHT = 2; + static const char ACTION_WHEEL = 3; + + static const char DATA_UP = 0; + static const char DATA_DOWN = 1; + + MouseAction(char actionButtonId, char buttonData, short x, short y, char pointerId); + MouseAction(char actionButtonId, char buttonData, short x, short y, short dx, short dy, char pointerId); + + bool isButton() const; + + short x, y; + short dx, dy; + char action; + char data; + char pointerId; +}; + +/* Iterators */ +typedef std::vector MouseActionVec; +typedef MouseActionVec::iterator MouseActionIt; +typedef MouseActionVec::const_iterator MouseActionCIt; + + +class MouseDevice +{ +public: + static const int MAX_NUM_BUTTONS = 4; + + MouseDevice(); + + char getButtonState(int buttonId); + bool isButtonDown(int buttonId); + + /// Was the current movement the first movement after mouse down? + bool wasFirstMovement(); + + short getX(); + short getY(); + + short getDX(); + short getDY(); + + void reset(); + void reset2(); + + bool next(); + void rewind(); + + bool getEventButtonState(); + char getEventButton(); + const MouseAction& getEvent(); + + void feed(char actionButtonId, char buttonData, short x, short y); + void feed(char actionButtonId, char buttonData, short x, short y, short dx, short dy); + +private: + int _index; + short _x, _y; + short _dx, _dy; + short _xOld, _yOld; + char _buttonStates[MAX_NUM_BUTTONS]; + std::vector _inputs; + int _firstMovementType; + static const int DELTA_NOTSET = -9999; +}; + +/** A static mouse class, written to resemble the one in lwjgl somewhat + UPDATE: which is very silly, since it doesn't support multi touch... */ +class Mouse +{ +public: + static char getButtonState(int buttonId); + static bool isButtonDown(int buttonId); + + static short getX(); + static short getY(); + + static short getDX(); + static short getDY(); + + static void reset(); + static void reset2(); + + static bool next(); + static void rewind(); + + static bool getEventButtonState(); + static char getEventButton(); + static const MouseAction& getEvent(); + + static void feed(char actionButtonId, char buttonData, short x, short y); + static void feed(char actionButtonId, char buttonData, short x, short y, short dx, short dy); + +private: + static MouseDevice _instance; +}; + +#endif//MOUSE_H__ diff --git a/src/platform/input/Multitouch.cpp b/src/platform/input/Multitouch.cpp index 8974536..ce2ca4b 100755 --- a/src/platform/input/Multitouch.cpp +++ b/src/platform/input/Multitouch.cpp @@ -1,20 +1,20 @@ -#include "Multitouch.h" - -int - Multitouch::_index = -1, - Multitouch::_activePointerCount = 0, - Multitouch::_activePointerList[Multitouch::MAX_POINTERS] = {-1}, - Multitouch::_activePointerThisUpdateCount = 0, - Multitouch::_activePointerThisUpdateList[Multitouch::MAX_POINTERS] = {-1}; - -bool - Multitouch::_wasPressed[Multitouch::MAX_POINTERS] = {false}, - Multitouch::_wasReleased[Multitouch::MAX_POINTERS] = {false}, - Multitouch::_wasPressedThisUpdate[Multitouch::MAX_POINTERS] = {false}, - Multitouch::_wasReleasedThisUpdate[Multitouch::MAX_POINTERS] = {false}; - -TouchPointer - Multitouch::_pointers[Multitouch::MAX_POINTERS]; - -std::vector - Multitouch::_inputs; +#include "Multitouch.h" + +int + Multitouch::_index = -1, + Multitouch::_activePointerCount = 0, + Multitouch::_activePointerList[Multitouch::MAX_POINTERS] = {-1}, + Multitouch::_activePointerThisUpdateCount = 0, + Multitouch::_activePointerThisUpdateList[Multitouch::MAX_POINTERS] = {-1}; + +bool + Multitouch::_wasPressed[Multitouch::MAX_POINTERS] = {false}, + Multitouch::_wasReleased[Multitouch::MAX_POINTERS] = {false}, + Multitouch::_wasPressedThisUpdate[Multitouch::MAX_POINTERS] = {false}, + Multitouch::_wasReleasedThisUpdate[Multitouch::MAX_POINTERS] = {false}; + +TouchPointer + Multitouch::_pointers[Multitouch::MAX_POINTERS]; + +std::vector + Multitouch::_inputs; diff --git a/src/platform/input/Multitouch.h b/src/platform/input/Multitouch.h index 2b4390d..24cab10 100755 --- a/src/platform/input/Multitouch.h +++ b/src/platform/input/Multitouch.h @@ -1,176 +1,176 @@ -#ifndef MULTITOUCH_H__ -#define MULTITOUCH_H__ - -#include "Mouse.h" -#include "../log.h" - -typedef MouseDevice TouchPointer; - -class Multitouch -{ -public: - static const int MAX_POINTERS = 12; - - static bool isPointerDown(int pointerId) { - return g(pointerId).isButtonDown(1); - } - - // Returns the first activeExtended (down OR released) pointer id - static int getFirstActivePointerIdEx() { - for (int i = 0; i < MAX_POINTERS; ++i) - if (_pointers[i].isButtonDown(1)) return i; - for (int i = 0; i < MAX_POINTERS; ++i) - if (_wasReleased[i]) return i; - return -1; - } - - // Returns the first activeExtended (down OR released) pointer id - // Compared to getFirstActivePointerIdEx, this method keeps the state the - // tick/render loop, while the former keeps it for a tick. - static int getFirstActivePointerIdExThisUpdate() { - for (int i = 0; i < MAX_POINTERS; ++i) - if (_pointers[i].isButtonDown(1)) return i; - for (int i = 0; i < MAX_POINTERS; ++i) - if (_wasReleasedThisUpdate[i]) return i; - return -1; - } - - // Get a list of all active (i.e. marked as touched/down) pointer ids - static int getActivePointerIds(const int** const ids) { - *ids = _activePointerList; - return _activePointerCount; - } - - // Get a list of all active (i.e. marked as touched/down) pointer ids - static int getActivePointerIdsThisUpdate(const int** const ids) { - *ids = _activePointerThisUpdateList; - return _activePointerThisUpdateCount; - } - - // Called when no more events will be pushed this update - static void commit() { - _activePointerCount = 0; - _activePointerThisUpdateCount = 0; - for (int i = 0; i < MAX_POINTERS; ++i) { - bool isDown = _pointers[i].isButtonDown(1); - if (isDown) { - _activePointerList[_activePointerCount++] = i; - _activePointerThisUpdateList[_activePointerThisUpdateCount++] = i; - } else if (_wasReleased[i]) { - _activePointerThisUpdateList[_activePointerThisUpdateCount++] = i; - } - } - } - - static short getX(int pointerId) { return g(pointerId).getX(); } - static short getY(int pointerId) { return g(pointerId).getY(); } - - static short getDX(int pointerId) { return g(pointerId).getDX(); } - static short getDY(int pointerId) { return g(pointerId).getDY(); } - - static bool wasFirstMovement(int pointerId) { return g(pointerId).wasFirstMovement(); } - - static bool isPressed(int pointerId) { - return _wasPressed[_clampPointerId(pointerId)]; - } - static bool isReleased(int pointerId) { - return _wasReleased[_clampPointerId(pointerId)]; - } - static bool isPressedThisUpdate(int pointerId) { - return _wasPressedThisUpdate[_clampPointerId(pointerId)]; - } - static bool isReleasedThisUpdate(int pointerId) { - return _wasReleasedThisUpdate[_clampPointerId(pointerId)]; - } - - static void reset() { - //LOGI("mtouch is reset\n"); - _inputs.clear(); - _index = -1; - - for (int i = 0; i < MAX_POINTERS; ++i) { - g(i).reset(); - - _wasPressed[i] = false; - _wasReleased[i] = false; - } - } - static void resetThisUpdate() { - for (int i = 0; i < MAX_POINTERS; ++i) { - _wasPressedThisUpdate[i] = false; - _wasReleasedThisUpdate[i] = false; - } - } - - static bool next() { - if (_index + 1 >= (int)_inputs.size()) - return false; - - ++_index; - return true; - } - - static void rewind() { - _index = -1; - } - - static MouseAction& getEvent() { - return _inputs[_index]; - } - - static void feed(char actionButtonId, char buttonData, short x, short y, char pointerId) { - pointerId = _clampPointerId(pointerId); - - _inputs.push_back(MouseAction(actionButtonId, buttonData, x, y, pointerId)); - g(pointerId).feed(actionButtonId, buttonData, x, y); - - if (actionButtonId > 0) { - if (buttonData == MouseAction::DATA_DOWN) { - _wasPressed[pointerId] = true; - _wasPressedThisUpdate[pointerId] = true; - //LOGI("isPressed %d %d\n", pointerId, isPressed(pointerId)); - } - else if (buttonData == MouseAction::DATA_UP) { - _wasReleased[pointerId] = true; - _wasReleasedThisUpdate[pointerId] = true; - //LOGI("isReleased %d %d\n", pointerId, isReleased(pointerId)); - } - } - } - - static int getMaxPointers() { - return MAX_POINTERS; - } - -private: - static TouchPointer& g(int pointerId) { - return _pointers[_clampPointerId(pointerId)]; - } - - static int _clampPointerId(int p) { - if (p < 0) return p; - if (p >= MAX_POINTERS) return MAX_POINTERS-1; - return p; - } - - // Between calls to reset() - static bool _wasPressed[MAX_POINTERS]; - static bool _wasReleased[MAX_POINTERS]; - - // Between calls to resetThisUpdate() - static bool _wasPressedThisUpdate[MAX_POINTERS]; - static bool _wasReleasedThisUpdate[MAX_POINTERS]; - - static TouchPointer _pointers[MAX_POINTERS]; - static std::vector _inputs; - - static int _activePointerList[MAX_POINTERS]; - static int _activePointerCount; - - static int _activePointerThisUpdateList[MAX_POINTERS]; - static int _activePointerThisUpdateCount; - - static int _index; -}; - -#endif /*MULTITOUCH_H__*/ +#ifndef MULTITOUCH_H__ +#define MULTITOUCH_H__ + +#include "Mouse.h" +#include "../log.h" + +typedef MouseDevice TouchPointer; + +class Multitouch +{ +public: + static const int MAX_POINTERS = 12; + + static bool isPointerDown(int pointerId) { + return g(pointerId).isButtonDown(1); + } + + // Returns the first activeExtended (down OR released) pointer id + static int getFirstActivePointerIdEx() { + for (int i = 0; i < MAX_POINTERS; ++i) + if (_pointers[i].isButtonDown(1)) return i; + for (int i = 0; i < MAX_POINTERS; ++i) + if (_wasReleased[i]) return i; + return -1; + } + + // Returns the first activeExtended (down OR released) pointer id + // Compared to getFirstActivePointerIdEx, this method keeps the state the + // tick/render loop, while the former keeps it for a tick. + static int getFirstActivePointerIdExThisUpdate() { + for (int i = 0; i < MAX_POINTERS; ++i) + if (_pointers[i].isButtonDown(1)) return i; + for (int i = 0; i < MAX_POINTERS; ++i) + if (_wasReleasedThisUpdate[i]) return i; + return -1; + } + + // Get a list of all active (i.e. marked as touched/down) pointer ids + static int getActivePointerIds(const int** const ids) { + *ids = _activePointerList; + return _activePointerCount; + } + + // Get a list of all active (i.e. marked as touched/down) pointer ids + static int getActivePointerIdsThisUpdate(const int** const ids) { + *ids = _activePointerThisUpdateList; + return _activePointerThisUpdateCount; + } + + // Called when no more events will be pushed this update + static void commit() { + _activePointerCount = 0; + _activePointerThisUpdateCount = 0; + for (int i = 0; i < MAX_POINTERS; ++i) { + bool isDown = _pointers[i].isButtonDown(1); + if (isDown) { + _activePointerList[_activePointerCount++] = i; + _activePointerThisUpdateList[_activePointerThisUpdateCount++] = i; + } else if (_wasReleased[i]) { + _activePointerThisUpdateList[_activePointerThisUpdateCount++] = i; + } + } + } + + static short getX(int pointerId) { return g(pointerId).getX(); } + static short getY(int pointerId) { return g(pointerId).getY(); } + + static short getDX(int pointerId) { return g(pointerId).getDX(); } + static short getDY(int pointerId) { return g(pointerId).getDY(); } + + static bool wasFirstMovement(int pointerId) { return g(pointerId).wasFirstMovement(); } + + static bool isPressed(int pointerId) { + return _wasPressed[_clampPointerId(pointerId)]; + } + static bool isReleased(int pointerId) { + return _wasReleased[_clampPointerId(pointerId)]; + } + static bool isPressedThisUpdate(int pointerId) { + return _wasPressedThisUpdate[_clampPointerId(pointerId)]; + } + static bool isReleasedThisUpdate(int pointerId) { + return _wasReleasedThisUpdate[_clampPointerId(pointerId)]; + } + + static void reset() { + //LOGI("mtouch is reset\n"); + _inputs.clear(); + _index = -1; + + for (int i = 0; i < MAX_POINTERS; ++i) { + g(i).reset(); + + _wasPressed[i] = false; + _wasReleased[i] = false; + } + } + static void resetThisUpdate() { + for (int i = 0; i < MAX_POINTERS; ++i) { + _wasPressedThisUpdate[i] = false; + _wasReleasedThisUpdate[i] = false; + } + } + + static bool next() { + if (_index + 1 >= (int)_inputs.size()) + return false; + + ++_index; + return true; + } + + static void rewind() { + _index = -1; + } + + static MouseAction& getEvent() { + return _inputs[_index]; + } + + static void feed(char actionButtonId, char buttonData, short x, short y, char pointerId) { + pointerId = _clampPointerId(pointerId); + + _inputs.push_back(MouseAction(actionButtonId, buttonData, x, y, pointerId)); + g(pointerId).feed(actionButtonId, buttonData, x, y); + + if (actionButtonId > 0) { + if (buttonData == MouseAction::DATA_DOWN) { + _wasPressed[pointerId] = true; + _wasPressedThisUpdate[pointerId] = true; + //LOGI("isPressed %d %d\n", pointerId, isPressed(pointerId)); + } + else if (buttonData == MouseAction::DATA_UP) { + _wasReleased[pointerId] = true; + _wasReleasedThisUpdate[pointerId] = true; + //LOGI("isReleased %d %d\n", pointerId, isReleased(pointerId)); + } + } + } + + static int getMaxPointers() { + return MAX_POINTERS; + } + +private: + static TouchPointer& g(int pointerId) { + return _pointers[_clampPointerId(pointerId)]; + } + + static int _clampPointerId(int p) { + if (p < 0) return p; + if (p >= MAX_POINTERS) return MAX_POINTERS-1; + return p; + } + + // Between calls to reset() + static bool _wasPressed[MAX_POINTERS]; + static bool _wasReleased[MAX_POINTERS]; + + // Between calls to resetThisUpdate() + static bool _wasPressedThisUpdate[MAX_POINTERS]; + static bool _wasReleasedThisUpdate[MAX_POINTERS]; + + static TouchPointer _pointers[MAX_POINTERS]; + static std::vector _inputs; + + static int _activePointerList[MAX_POINTERS]; + static int _activePointerCount; + + static int _activePointerThisUpdateList[MAX_POINTERS]; + static int _activePointerThisUpdateCount; + + static int _index; +}; + +#endif /*MULTITOUCH_H__*/ diff --git a/src/platform/log.h b/src/platform/log.h index d660a90..b7bf26d 100755 --- a/src/platform/log.h +++ b/src/platform/log.h @@ -1,47 +1,47 @@ -#ifndef LOG_H__ -#define LOG_H__ - -#ifdef __cplusplus - #include -#else - #include -#endif - -#define __LOG_PUBLISH(...) do { __VA_ARGS__; } while(0) - -#ifdef ANDROID - #include - #ifdef ANDROID_PUBLISH - #define LOGV(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) - #define LOGI(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) - #define LOGW(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) - #define LOGE(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) - #else - // @todo @fix; Obiously the tag shouldn't be hardcoded in here.. - #define LOGV(...) ((void)__android_log_print( ANDROID_LOG_VERBOSE, "MinecraftPE", __VA_ARGS__ )) - #define LOGI(...) ((void)__android_log_print( ANDROID_LOG_INFO, "MinecraftPE", __VA_ARGS__ )) - #define LOGW(...) ((void)__android_log_print( ANDROID_LOG_WARN, "MinecraftPE", __VA_ARGS__ )) - #define LOGE(...) ((void)__android_log_print( ANDROID_LOG_ERROR, "MinecraftPE", __VA_ARGS__ )) - #define printf LOGI - #endif -#else -#ifdef PUBLISH - #define LOGV(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) - #define LOGI(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) - #define LOGW(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) - #define LOGE(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) -#else - #define LOGV(...) (printf(__VA_ARGS__)) - #define LOGI(...) (printf(__VA_ARGS__)) - #define LOGW(...) (printf(__VA_ARGS__)) - #define LOGE(...) (printf(__VA_ARGS__)) -#endif -#endif - -#ifdef _DEBUG - #define LOGVV LOGV -#else - #define LOGVV(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) -#endif - -#endif /*LOG_H__*/ +#ifndef LOG_H__ +#define LOG_H__ + +#ifdef __cplusplus + #include +#else + #include +#endif + +#define __LOG_PUBLISH(...) do { __VA_ARGS__; } while(0) + +#ifdef ANDROID + #include + #ifdef ANDROID_PUBLISH + #define LOGV(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) + #define LOGI(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) + #define LOGW(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) + #define LOGE(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) + #else + // @todo @fix; Obiously the tag shouldn't be hardcoded in here.. + #define LOGV(...) ((void)__android_log_print( ANDROID_LOG_VERBOSE, "MinecraftPE", __VA_ARGS__ )) + #define LOGI(...) ((void)__android_log_print( ANDROID_LOG_INFO, "MinecraftPE", __VA_ARGS__ )) + #define LOGW(...) ((void)__android_log_print( ANDROID_LOG_WARN, "MinecraftPE", __VA_ARGS__ )) + #define LOGE(...) ((void)__android_log_print( ANDROID_LOG_ERROR, "MinecraftPE", __VA_ARGS__ )) + #define printf LOGI + #endif +#else +#ifdef PUBLISH + #define LOGV(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) + #define LOGI(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) + #define LOGW(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) + #define LOGE(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) +#else + #define LOGV(...) (printf(__VA_ARGS__)) + #define LOGI(...) (printf(__VA_ARGS__)) + #define LOGW(...) (printf(__VA_ARGS__)) + #define LOGE(...) (printf(__VA_ARGS__)) +#endif +#endif + +#ifdef _DEBUG + #define LOGVV LOGV +#else + #define LOGVV(fmt, ...) __LOG_PUBLISH(__VA_ARGS__) +#endif + +#endif /*LOG_H__*/ diff --git a/src/platform/time.cpp b/src/platform/time.cpp index 4bfb48d..f2d4411 100755 --- a/src/platform/time.cpp +++ b/src/platform/time.cpp @@ -1,224 +1,224 @@ -#include "time.h" -#include - -#ifdef WIN32 - #include -#else - #include - #include -#endif - -// Ugly ass temporary solution; -// since gettimeofday() returns seconds & microseconds since -// Epoch(1970) rather than since computer started, getTimeMs() -// would overflow if just using an int -> subtract the number -// of seconds that had passed when starting the app before -// doing any calculations. -#ifdef WIN32 - static long long getStartTime() { - LARGE_INTEGER ts; - QueryPerformanceCounter(&ts); - return ts.QuadPart; - } - static unsigned long long _t_start = getStartTime(); - -#else - static int getStartTime() { - timeval now; - gettimeofday(&now, 0); - return now.tv_sec; - } - static unsigned int _t_start = getStartTime(); -#endif - -#include - -/// Returns a platform dependent time in seconds -/// This is allowed to be here, since getTimeS and getTimeMs both have t(0) -/// as base (and obviously not good for an initial random seed) -int getRawTimeS() { -#ifdef WIN32 - return (int)(GetTickCount() / 1000); -#else - timeval now; - gettimeofday(&now, 0); - return (int)now.tv_sec; -#endif -} - -float getTimeS() { -#ifdef WIN32 - // If the game seems to go with different speeds, this might most - // possibly be the cause. QueryPerformanceCounter has indeed high - // resolution, but (imho) only guaranteed to be correct for (short) - // deltas between consecutive calls - LARGE_INTEGER freq, ts; - QueryPerformanceFrequency(&freq); - QueryPerformanceCounter(&ts); - return (ts.QuadPart - _t_start) / (float)freq.QuadPart; -#else - timeval now; - gettimeofday(&now, 0); - return (float)(now.tv_sec - _t_start) + now.tv_usec / 1000000.0f; -#endif -} - -int getTimeMs() { - return (int)(getTimeS() * 1000.0f); -} - -void sleepMs(int ms) { -#ifdef WIN32 - Sleep(ms); -#else - usleep(ms * 1000); -#endif -} - -#include -int getEpochTimeS() { - return (int)std::time (NULL); -} - - -StopwatchNLast::StopwatchNLast( int numMeasurements ) -: n(numMeasurements), - k(0), - sum(0) -{ - t = new float[n]; - memset(t, 0, n * sizeof(float)); -} - -StopwatchNLast::~StopwatchNLast() { - delete[] t; -} - -float StopwatchNLast::stop() { - float total = Stopwatch::stop(); - float curr = Stopwatch::getLast(); - - float toDelete = t[k]; - if (++k == n) k = 0; - t[k] = curr; - - sum += (curr - toDelete); - - return total; -} - -void StopwatchNLast::print( const std::string& prepend /*= ""*/ ) { - LOGI("%s\tTime (AVGms for the last %d runs): %f (Max: %f)\n", - prepend.c_str(), n, (1000.0f*sum/n), 1000.0f * getMax()); -} - - -// -// Stopwatch -// -Stopwatch::Stopwatch() -: _count(0), - _printcounter(0) -{ - reset(); -} - -void Stopwatch::start() { - _st = getTimeS(); -} - -float Stopwatch::stop() { - if (_st == -1) return 0; - _last = getTimeS() - _st; - if (_last > _max) _max = _last; - _tt += _last; - _st = -1; - ++_count; - return _tt; -} - -float Stopwatch::stopContinue() { - if (_st == -1) return 0; - const float now = getTimeS(); - _last = now - _st; - if (_last > _max) _max = _last; - _tt += _last; - _st = now; - ++_count; - return _tt; -} - -float Stopwatch::getLast() { - return _last; -} - -float Stopwatch::getTotal() { - return _tt; -} - -float Stopwatch::getMax() { - return _max; -} - -int Stopwatch::getCount() { - return _count; -} - -void Stopwatch::reset() { - _st = -1; - _tt = 0; - _max = 0; -} - -void Stopwatch::printEvery( int n, const std::string& prepend /*= ""*/ ) { - if (++_printcounter >= n) { - _printcounter = 0; - print(prepend); - } -} - -void Stopwatch::print( const std::string& prepend /*= ""*/ ) { - if (!_count) return; - LOGI("%s\tTime (AVGms/LTs(MAXs)/TTs, C) : %.3f/%.6f(%.6f)/%.4f, %d\n", - prepend.c_str(), (1000.0f*_tt/_count), _last, _max, _tt, _count); -} - -// -// StopwatchHandler - a collection of StopWatch:es -// -Stopwatch& StopwatchHandler::get( const std::string& s ) { - Map::iterator it = _map.find(s); - if (it != _map.end()) - return *it->second; - - Stopwatch* watch = new Stopwatch; - _map.insert(make_pair(s, watch)); - return *watch; -} - -void StopwatchHandler::clear( const std::string& s ) { - Map::iterator it = _map.find(s); - if (it != _map.end()) { - delete it->second; - } - _map.erase(s); -} - -void StopwatchHandler::clearAll() { - for (Map::iterator it = _map.begin(); it != _map.end(); ++it) - delete it->second; - _map.clear(); -} - -void StopwatchHandler::print() { - for (Map::iterator it = _map.begin(); it != _map.end(); ++it) { - it->second->print(it->first.c_str()); - } -} - -void StopwatchHandler::printEvery( int n ) { - if (++_printcounter >= n) { - _printcounter = 0; - print(); - } -} +#include "time.h" +#include + +#ifdef WIN32 + #include +#else + #include + #include +#endif + +// Ugly ass temporary solution; +// since gettimeofday() returns seconds & microseconds since +// Epoch(1970) rather than since computer started, getTimeMs() +// would overflow if just using an int -> subtract the number +// of seconds that had passed when starting the app before +// doing any calculations. +#ifdef WIN32 + static long long getStartTime() { + LARGE_INTEGER ts; + QueryPerformanceCounter(&ts); + return ts.QuadPart; + } + static unsigned long long _t_start = getStartTime(); + +#else + static int getStartTime() { + timeval now; + gettimeofday(&now, 0); + return now.tv_sec; + } + static unsigned int _t_start = getStartTime(); +#endif + +#include + +/// Returns a platform dependent time in seconds +/// This is allowed to be here, since getTimeS and getTimeMs both have t(0) +/// as base (and obviously not good for an initial random seed) +int getRawTimeS() { +#ifdef WIN32 + return (int)(GetTickCount() / 1000); +#else + timeval now; + gettimeofday(&now, 0); + return (int)now.tv_sec; +#endif +} + +float getTimeS() { +#ifdef WIN32 + // If the game seems to go with different speeds, this might most + // possibly be the cause. QueryPerformanceCounter has indeed high + // resolution, but (imho) only guaranteed to be correct for (short) + // deltas between consecutive calls + LARGE_INTEGER freq, ts; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&ts); + return (ts.QuadPart - _t_start) / (float)freq.QuadPart; +#else + timeval now; + gettimeofday(&now, 0); + return (float)(now.tv_sec - _t_start) + now.tv_usec / 1000000.0f; +#endif +} + +int getTimeMs() { + return (int)(getTimeS() * 1000.0f); +} + +void sleepMs(int ms) { +#ifdef WIN32 + Sleep(ms); +#else + usleep(ms * 1000); +#endif +} + +#include +int getEpochTimeS() { + return (int)std::time (NULL); +} + + +StopwatchNLast::StopwatchNLast( int numMeasurements ) +: n(numMeasurements), + k(0), + sum(0) +{ + t = new float[n]; + memset(t, 0, n * sizeof(float)); +} + +StopwatchNLast::~StopwatchNLast() { + delete[] t; +} + +float StopwatchNLast::stop() { + float total = Stopwatch::stop(); + float curr = Stopwatch::getLast(); + + float toDelete = t[k]; + if (++k == n) k = 0; + t[k] = curr; + + sum += (curr - toDelete); + + return total; +} + +void StopwatchNLast::print( const std::string& prepend /*= ""*/ ) { + LOGI("%s\tTime (AVGms for the last %d runs): %f (Max: %f)\n", + prepend.c_str(), n, (1000.0f*sum/n), 1000.0f * getMax()); +} + + +// +// Stopwatch +// +Stopwatch::Stopwatch() +: _count(0), + _printcounter(0) +{ + reset(); +} + +void Stopwatch::start() { + _st = getTimeS(); +} + +float Stopwatch::stop() { + if (_st == -1) return 0; + _last = getTimeS() - _st; + if (_last > _max) _max = _last; + _tt += _last; + _st = -1; + ++_count; + return _tt; +} + +float Stopwatch::stopContinue() { + if (_st == -1) return 0; + const float now = getTimeS(); + _last = now - _st; + if (_last > _max) _max = _last; + _tt += _last; + _st = now; + ++_count; + return _tt; +} + +float Stopwatch::getLast() { + return _last; +} + +float Stopwatch::getTotal() { + return _tt; +} + +float Stopwatch::getMax() { + return _max; +} + +int Stopwatch::getCount() { + return _count; +} + +void Stopwatch::reset() { + _st = -1; + _tt = 0; + _max = 0; +} + +void Stopwatch::printEvery( int n, const std::string& prepend /*= ""*/ ) { + if (++_printcounter >= n) { + _printcounter = 0; + print(prepend); + } +} + +void Stopwatch::print( const std::string& prepend /*= ""*/ ) { + if (!_count) return; + LOGI("%s\tTime (AVGms/LTs(MAXs)/TTs, C) : %.3f/%.6f(%.6f)/%.4f, %d\n", + prepend.c_str(), (1000.0f*_tt/_count), _last, _max, _tt, _count); +} + +// +// StopwatchHandler - a collection of StopWatch:es +// +Stopwatch& StopwatchHandler::get( const std::string& s ) { + Map::iterator it = _map.find(s); + if (it != _map.end()) + return *it->second; + + Stopwatch* watch = new Stopwatch; + _map.insert(make_pair(s, watch)); + return *watch; +} + +void StopwatchHandler::clear( const std::string& s ) { + Map::iterator it = _map.find(s); + if (it != _map.end()) { + delete it->second; + } + _map.erase(s); +} + +void StopwatchHandler::clearAll() { + for (Map::iterator it = _map.begin(); it != _map.end(); ++it) + delete it->second; + _map.clear(); +} + +void StopwatchHandler::print() { + for (Map::iterator it = _map.begin(); it != _map.end(); ++it) { + it->second->print(it->first.c_str()); + } +} + +void StopwatchHandler::printEvery( int n ) { + if (++_printcounter >= n) { + _printcounter = 0; + print(); + } +} diff --git a/src/platform/time.h b/src/platform/time.h index a739328..21ff63b 100755 --- a/src/platform/time.h +++ b/src/platform/time.h @@ -1,111 +1,111 @@ -#ifndef TIME_H__ -#define TIME_H__ - -#include -#include -#include "log.h" - -float getTimeS(); -int getTimeMs(); -int getRawTimeS(); -int getEpochTimeS(); - -void sleepMs(int ms); - -class Stopwatch { -public: - Stopwatch(); - virtual ~Stopwatch() {} - - void start(); - /** Returns the total number of seconds and stops the clock */ - virtual float stop(); - /* Returns the total number of seconds, but wont stop the clock */ - virtual float stopContinue(); - /** Returns the last measured time interval in seconds */ - float getLast(); - /** Returns the total measured time in seconds */ - float getTotal(); - /** Returns the maximum time between start() and stop() */ - float getMax(); - /** Returns the number of times the watch has been started and stopped */ - int getCount(); - - void reset(); - void printEvery(int n, const std::string& prepend = ""); - virtual void print(const std::string& prepend = ""); -private: - float _st; - float _tt; - float _last; - float _max; - int _count; - int _printcounter; -}; - -class SwStartStopper { -public: - SwStartStopper(Stopwatch& stopwatch) - : _stopwatch(stopwatch) - { - _stopwatch.start(); - } - - ~SwStartStopper() { - _stopwatch.stop(); - } -private: - Stopwatch& _stopwatch; -}; - -class SwStopper { -public: - SwStopper(Stopwatch& stopwatch) - : _stopwatch(stopwatch) - {} - - ~SwStopper() { - _stopwatch.stop(); - } -private: - Stopwatch& _stopwatch; -}; - -class StopwatchHandler { -typedef std::map Map; -public: - StopwatchHandler() - : _printcounter(0) - {} - ~StopwatchHandler() { - clearAll(); - } - Stopwatch& get(const std::string& s); - void clear(const std::string& s); - void clearAll(); - void print(); - void printEvery(int n); - -private: - Map _map; - int _printcounter; -}; - -class StopwatchNLast: public Stopwatch { -public: - StopwatchNLast(int numMeasurements); - ~StopwatchNLast(); - - float stop(); - - virtual void print(const std::string& prepend = ""); -private: - int n; - int k; - float* t; - float sum; - - Stopwatch sw; -}; - -#endif /*TIME_H__*/ +#ifndef TIME_H__ +#define TIME_H__ + +#include +#include +#include "log.h" + +float getTimeS(); +int getTimeMs(); +int getRawTimeS(); +int getEpochTimeS(); + +void sleepMs(int ms); + +class Stopwatch { +public: + Stopwatch(); + virtual ~Stopwatch() {} + + void start(); + /** Returns the total number of seconds and stops the clock */ + virtual float stop(); + /* Returns the total number of seconds, but wont stop the clock */ + virtual float stopContinue(); + /** Returns the last measured time interval in seconds */ + float getLast(); + /** Returns the total measured time in seconds */ + float getTotal(); + /** Returns the maximum time between start() and stop() */ + float getMax(); + /** Returns the number of times the watch has been started and stopped */ + int getCount(); + + void reset(); + void printEvery(int n, const std::string& prepend = ""); + virtual void print(const std::string& prepend = ""); +private: + float _st; + float _tt; + float _last; + float _max; + int _count; + int _printcounter; +}; + +class SwStartStopper { +public: + SwStartStopper(Stopwatch& stopwatch) + : _stopwatch(stopwatch) + { + _stopwatch.start(); + } + + ~SwStartStopper() { + _stopwatch.stop(); + } +private: + Stopwatch& _stopwatch; +}; + +class SwStopper { +public: + SwStopper(Stopwatch& stopwatch) + : _stopwatch(stopwatch) + {} + + ~SwStopper() { + _stopwatch.stop(); + } +private: + Stopwatch& _stopwatch; +}; + +class StopwatchHandler { +typedef std::map Map; +public: + StopwatchHandler() + : _printcounter(0) + {} + ~StopwatchHandler() { + clearAll(); + } + Stopwatch& get(const std::string& s); + void clear(const std::string& s); + void clearAll(); + void print(); + void printEvery(int n); + +private: + Map _map; + int _printcounter; +}; + +class StopwatchNLast: public Stopwatch { +public: + StopwatchNLast(int numMeasurements); + ~StopwatchNLast(); + + float stop(); + + virtual void print(const std::string& prepend = ""); +private: + int n; + int k; + float* t; + float sum; + + Stopwatch sw; +}; + +#endif /*TIME_H__*/ diff --git a/src/raknet/AutopatcherPatchContext.h b/src/raknet/AutopatcherPatchContext.h index 4fae91a..ab5d86b 100755 --- a/src/raknet/AutopatcherPatchContext.h +++ b/src/raknet/AutopatcherPatchContext.h @@ -1,23 +1,23 @@ -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// Usage of RakNet is subject to the appropriate license agreement. -/// - -#ifndef __AUTOPATCHER_PATCH_CONTEXT_H -#define __AUTOPATCHER_PATCH_CONTEXT_H - -enum PatchContext -{ - PC_HASH_1_WITH_PATCH, - PC_HASH_2_WITH_PATCH, - PC_WRITE_FILE, - PC_ERROR_FILE_WRITE_FAILURE, - PC_ERROR_PATCH_TARGET_MISSING, - PC_ERROR_PATCH_APPLICATION_FAILURE, - PC_ERROR_PATCH_RESULT_CHECKSUM_FAILURE, - PC_NOTICE_WILL_COPY_ON_RESTART, - PC_NOTICE_FILE_DOWNLOADED, - PC_NOTICE_FILE_DOWNLOADED_PATCH, -}; - -#endif +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// Usage of RakNet is subject to the appropriate license agreement. +/// + +#ifndef __AUTOPATCHER_PATCH_CONTEXT_H +#define __AUTOPATCHER_PATCH_CONTEXT_H + +enum PatchContext +{ + PC_HASH_1_WITH_PATCH, + PC_HASH_2_WITH_PATCH, + PC_WRITE_FILE, + PC_ERROR_FILE_WRITE_FAILURE, + PC_ERROR_PATCH_TARGET_MISSING, + PC_ERROR_PATCH_APPLICATION_FAILURE, + PC_ERROR_PATCH_RESULT_CHECKSUM_FAILURE, + PC_NOTICE_WILL_COPY_ON_RESTART, + PC_NOTICE_FILE_DOWNLOADED, + PC_NOTICE_FILE_DOWNLOADED_PATCH, +}; + +#endif diff --git a/src/raknet/AutopatcherRepositoryInterface.h b/src/raknet/AutopatcherRepositoryInterface.h index 303d321..d1ba5a3 100755 --- a/src/raknet/AutopatcherRepositoryInterface.h +++ b/src/raknet/AutopatcherRepositoryInterface.h @@ -1,72 +1,72 @@ -/// -/// \file AutopatcherRepositoryInterface.h -/// \brief An interface used by AutopatcherServer to get the data necessary to run an autopatcher. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// Usage of RakNet is subject to the appropriate license agreement. -/// - -#ifndef __AUTOPATCHER_REPOSITORY_INTERFACE_H -#define __AUTOPATCHER_REPOSITORY_INTERFACE_H - -#include "IncrementalReadInterface.h" -#include "SimpleMutex.h" - -namespace RakNet -{ -/// Forward declarations -class FileList; -class BitStream; - -/// An interface used by AutopatcherServer to get the data necessary to run an autopatcher. This is up to you to implement for custom repository solutions. -class AutopatcherRepositoryInterface : public IncrementalReadInterface -{ -public: - /// Get list of files added and deleted since a certain date. This is used by AutopatcherServer and not usually explicitly called. - /// \param[in] applicationName A null terminated string identifying the application - /// \param[out] addedFiles A list of the current versions of filenames with hashes as their data that were created after \a sinceData - /// \param[out] deletedFiles A list of the current versions of filenames that were deleted after \a sinceData - /// \param[in] An input date, in whatever format your repository uses - /// \param[out] currentDate The current server date, in whatever format your repository uses - /// \return True on success, false on failure. - virtual bool GetChangelistSinceDate(const char *applicationName, FileList *addedFiles, FileList *deletedFiles, double sinceDate)=0; - - /// Get patches (or files) for every file in input, assuming that input has a hash for each of those files. - /// \param[in] applicationName A null terminated string identifying the application - /// \param[in] input A list of files with SHA1_LENGTH byte hashes to get from the database. - /// \param[out] patchList You should return list of files with either the filedata or the patch. This is a subset of \a input. The context data for each file will be either PC_WRITE_FILE (to just write the file) or PC_HASH_WITH_PATCH (to patch). If PC_HASH_WITH_PATCH, then the file contains a SHA1_LENGTH byte patch followed by the hash. The datalength is patchlength + SHA1_LENGTH - /// \param[out] currentDate The current server date, in whatever format your repository uses - /// \return True on success, false on failure. - virtual bool GetPatches(const char *applicationName, FileList *input, FileList *patchList)=0; - - /// For the most recent update, return files that were patched, added, or deleted. For files that were patched, return both the patch in \a patchedFiles and the current version in \a updatedFiles - /// The cache will be used if the client last patched between \a priorRowPatchTime and \a mostRecentRowPatchTime - /// No files changed will be returned to the client if the client last patched after mostRecentRowPatchTime - /// \param[in,out] applicationName Name of the application to get patches for. If empty, uses the most recently updated application, and the string will be updated to reflect this name. - /// \param[out] patchedFiles Given the most recent update, if a file was patched, add it to this list. The context data for each file will be PC_HASH_WITH_PATCH. The first 4 bytes of data should be a hash of the file being patched. The second 4 bytes should be the hash of the file after the patch. The remaining bytes should be the patch itself. - /// \param[out] updatedFiles The current value of the file. List should have the same length and order as \a patchedFiles - /// \param[out] updatedFileHashes The hash of the current value of the file. List should have the same length and order as \a patchedFiles - /// \param[out] deletedFiles Files that were deleted in the last patch. - /// \param[out] priorRowPatchTime When the patch before the most recent patch took place. 0 means never. - /// \param[out] mostRecentRowPatchTime When the most recent patch took place. 0 means never. - /// \return true on success, false on failure - virtual bool GetMostRecentChangelistWithPatches( - RakNet::RakString &applicationName, - FileList *patchedFiles, - FileList *updatedFiles, - FileList *updatedFileHashes, - FileList *deletedFiles, - double *priorRowPatchTime, - double *mostRecentRowPatchTime)=0; - - /// \return Whatever this function returns is sent from the AutopatcherServer to the AutopatcherClient when one of the above functions returns false. - virtual const char *GetLastError(void) const=0; - - /// \return Passed to FileListTransfer::Send() as the _chunkSize parameter. - virtual const int GetIncrementalReadChunkSize(void) const=0; -}; - -} // namespace RakNet - -#endif - +/// +/// \file AutopatcherRepositoryInterface.h +/// \brief An interface used by AutopatcherServer to get the data necessary to run an autopatcher. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// Usage of RakNet is subject to the appropriate license agreement. +/// + +#ifndef __AUTOPATCHER_REPOSITORY_INTERFACE_H +#define __AUTOPATCHER_REPOSITORY_INTERFACE_H + +#include "IncrementalReadInterface.h" +#include "SimpleMutex.h" + +namespace RakNet +{ +/// Forward declarations +class FileList; +class BitStream; + +/// An interface used by AutopatcherServer to get the data necessary to run an autopatcher. This is up to you to implement for custom repository solutions. +class AutopatcherRepositoryInterface : public IncrementalReadInterface +{ +public: + /// Get list of files added and deleted since a certain date. This is used by AutopatcherServer and not usually explicitly called. + /// \param[in] applicationName A null terminated string identifying the application + /// \param[out] addedFiles A list of the current versions of filenames with hashes as their data that were created after \a sinceData + /// \param[out] deletedFiles A list of the current versions of filenames that were deleted after \a sinceData + /// \param[in] An input date, in whatever format your repository uses + /// \param[out] currentDate The current server date, in whatever format your repository uses + /// \return True on success, false on failure. + virtual bool GetChangelistSinceDate(const char *applicationName, FileList *addedFiles, FileList *deletedFiles, double sinceDate)=0; + + /// Get patches (or files) for every file in input, assuming that input has a hash for each of those files. + /// \param[in] applicationName A null terminated string identifying the application + /// \param[in] input A list of files with SHA1_LENGTH byte hashes to get from the database. + /// \param[out] patchList You should return list of files with either the filedata or the patch. This is a subset of \a input. The context data for each file will be either PC_WRITE_FILE (to just write the file) or PC_HASH_WITH_PATCH (to patch). If PC_HASH_WITH_PATCH, then the file contains a SHA1_LENGTH byte patch followed by the hash. The datalength is patchlength + SHA1_LENGTH + /// \param[out] currentDate The current server date, in whatever format your repository uses + /// \return True on success, false on failure. + virtual bool GetPatches(const char *applicationName, FileList *input, FileList *patchList)=0; + + /// For the most recent update, return files that were patched, added, or deleted. For files that were patched, return both the patch in \a patchedFiles and the current version in \a updatedFiles + /// The cache will be used if the client last patched between \a priorRowPatchTime and \a mostRecentRowPatchTime + /// No files changed will be returned to the client if the client last patched after mostRecentRowPatchTime + /// \param[in,out] applicationName Name of the application to get patches for. If empty, uses the most recently updated application, and the string will be updated to reflect this name. + /// \param[out] patchedFiles Given the most recent update, if a file was patched, add it to this list. The context data for each file will be PC_HASH_WITH_PATCH. The first 4 bytes of data should be a hash of the file being patched. The second 4 bytes should be the hash of the file after the patch. The remaining bytes should be the patch itself. + /// \param[out] updatedFiles The current value of the file. List should have the same length and order as \a patchedFiles + /// \param[out] updatedFileHashes The hash of the current value of the file. List should have the same length and order as \a patchedFiles + /// \param[out] deletedFiles Files that were deleted in the last patch. + /// \param[out] priorRowPatchTime When the patch before the most recent patch took place. 0 means never. + /// \param[out] mostRecentRowPatchTime When the most recent patch took place. 0 means never. + /// \return true on success, false on failure + virtual bool GetMostRecentChangelistWithPatches( + RakNet::RakString &applicationName, + FileList *patchedFiles, + FileList *updatedFiles, + FileList *updatedFileHashes, + FileList *deletedFiles, + double *priorRowPatchTime, + double *mostRecentRowPatchTime)=0; + + /// \return Whatever this function returns is sent from the AutopatcherServer to the AutopatcherClient when one of the above functions returns false. + virtual const char *GetLastError(void) const=0; + + /// \return Passed to FileListTransfer::Send() as the _chunkSize parameter. + virtual const int GetIncrementalReadChunkSize(void) const=0; +}; + +} // namespace RakNet + +#endif + diff --git a/src/raknet/BitStream.cpp b/src/raknet/BitStream.cpp index 7a0a14f..cdf88ac 100755 --- a/src/raknet/BitStream.cpp +++ b/src/raknet/BitStream.cpp @@ -1,1172 +1,1172 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization -#include "BitStream_NoTemplate.cpp" -#else - -#include "BitStream.h" -#include -#include -#include - -#include "SocketIncludes.h" -#include "RakNetDefines.h" - - - -#if defined(_WIN32) -#include // htonl -#include -#include -#include - - -#else -#include -#include -#if defined(ANDROID) -#include -#else -#include -#endif -#include -#endif - -// MSWin uses _copysign, others use copysign... -#ifndef _WIN32 -#define _copysign copysign -#endif - -using namespace RakNet; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -STATIC_FACTORY_DEFINITIONS(BitStream,BitStream) - -BitStream::BitStream() -{ - numberOfBitsUsed = 0; - //numberOfBitsAllocated = 32 * 8; - numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; - readOffset = 0; - //data = ( unsigned char* ) rakMalloc_Ex( 32, _FILE_AND_LINE_ ); - data = ( unsigned char* ) stackData; - -#ifdef _DEBUG - // RakAssert( data ); -#endif - //memset(data, 0, 32); - copyData = true; -} - -BitStream::BitStream( const unsigned int initialBytesToAllocate ) -{ - numberOfBitsUsed = 0; - readOffset = 0; - if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE) - { - data = ( unsigned char* ) stackData; - numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; - } - else - { - data = ( unsigned char* ) rakMalloc_Ex( (size_t) initialBytesToAllocate, _FILE_AND_LINE_ ); - numberOfBitsAllocated = initialBytesToAllocate << 3; - } -#ifdef _DEBUG - RakAssert( data ); -#endif - // memset(data, 0, initialBytesToAllocate); - copyData = true; -} - -BitStream::BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData ) -{ - numberOfBitsUsed = lengthInBytes << 3; - readOffset = 0; - copyData = _copyData; - numberOfBitsAllocated = lengthInBytes << 3; - - if ( copyData ) - { - if ( lengthInBytes > 0 ) - { - if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE) - { - data = ( unsigned char* ) stackData; - numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << 3; - } - else - { - data = ( unsigned char* ) rakMalloc_Ex( (size_t) lengthInBytes, _FILE_AND_LINE_ ); - } -#ifdef _DEBUG - RakAssert( data ); -#endif - memcpy( data, _data, (size_t) lengthInBytes ); - } - else - data = 0; - } - else - data = ( unsigned char* ) _data; -} - -// Use this if you pass a pointer copy to the constructor (_copyData==false) and want to overallocate to prevent reallocation -void BitStream::SetNumberOfBitsAllocated( const BitSize_t lengthInBits ) -{ -#ifdef _DEBUG - RakAssert( lengthInBits >= ( BitSize_t ) numberOfBitsAllocated ); -#endif - numberOfBitsAllocated = lengthInBits; -} - -BitStream::~BitStream() -{ - if ( copyData && numberOfBitsAllocated > (BITSTREAM_STACK_ALLOCATION_SIZE << 3)) - rakFree_Ex( data , _FILE_AND_LINE_ ); // Use realloc and free so we are more efficient than delete and new for resizing -} - -void BitStream::Reset( void ) -{ - // Note: Do NOT reallocate memory because BitStream is used - // in places to serialize/deserialize a buffer. Reallocation - // is a dangerous operation (may result in leaks). - - if ( numberOfBitsUsed > 0 ) - { - // memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed)); - } - - // Don't free memory here for speed efficiency - //free(data); // Use realloc and free so we are more efficient than delete and new for resizing - numberOfBitsUsed = 0; - - //numberOfBitsAllocated=8; - readOffset = 0; - - //data=(unsigned char*)rakMalloc_Ex(1, _FILE_AND_LINE_); - // if (numberOfBitsAllocated>0) - // memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated)); -} - -// Write an array or casted stream -void BitStream::Write( const char* inputByteArray, const unsigned int numberOfBytes ) -{ - if (numberOfBytes==0) - return; - - // Optimization: - if ((numberOfBitsUsed & 7) == 0) - { - AddBitsAndReallocate( BYTES_TO_BITS(numberOfBytes) ); - memcpy(data+BITS_TO_BYTES(numberOfBitsUsed), inputByteArray, (size_t) numberOfBytes); - numberOfBitsUsed+=BYTES_TO_BITS(numberOfBytes); - } - else - { - WriteBits( ( unsigned char* ) inputByteArray, numberOfBytes * 8, true ); - } - -} -void BitStream::Write( BitStream *bitStream) -{ - Write(bitStream, bitStream->GetNumberOfBitsUsed()-bitStream->GetReadOffset()); -} -void BitStream::Write( BitStream *bitStream, BitSize_t numberOfBits ) -{ - AddBitsAndReallocate( numberOfBits ); - BitSize_t numberOfBitsMod8; - - if ((bitStream->GetReadOffset()&7)==0 && (numberOfBitsUsed&7)==0) - { - int readOffsetBytes=bitStream->GetReadOffset()/8; - int numBytes=numberOfBits/8; - memcpy(data + (numberOfBitsUsed >> 3), bitStream->GetData()+readOffsetBytes, numBytes); - numberOfBits-=BYTES_TO_BITS(numBytes); - bitStream->SetReadOffset(BYTES_TO_BITS(numBytes+readOffsetBytes)); - numberOfBitsUsed+=BYTES_TO_BITS(numBytes); - } - - while (numberOfBits-->0 && bitStream->readOffset + 1 <= bitStream->numberOfBitsUsed) - { - numberOfBitsMod8 = numberOfBitsUsed & 7; - if ( numberOfBitsMod8 == 0 ) - { - // New byte - if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) ) - { - // Write 1 - data[ numberOfBitsUsed >> 3 ] = 0x80; - } - else - { - // Write 0 - data[ numberOfBitsUsed >> 3 ] = 0; - } - - } - else - { - // Existing byte - if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) ) - data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 - // else 0, do nothing - } - - bitStream->readOffset++; - numberOfBitsUsed++; - } -} -void BitStream::Write( BitStream &bitStream, BitSize_t numberOfBits ) -{ - Write(&bitStream, numberOfBits); -} -void BitStream::Write( BitStream &bitStream ) -{ - Write(&bitStream); -} -bool BitStream::Read( BitStream *bitStream, BitSize_t numberOfBits ) -{ - if (GetNumberOfUnreadBits() < numberOfBits) - return false; - bitStream->Write(this, numberOfBits); - return true; -} -bool BitStream::Read( BitStream *bitStream ) -{ - bitStream->Write(this); - return true; -} -bool BitStream::Read( BitStream &bitStream, BitSize_t numberOfBits ) -{ - if (GetNumberOfUnreadBits() < numberOfBits) - return false; - bitStream.Write(this, numberOfBits); - return true; -} -bool BitStream::Read( BitStream &bitStream ) -{ - bitStream.Write(this); - return true; -} - -// Read an array or casted stream -bool BitStream::Read( char* outByteArray, const unsigned int numberOfBytes ) -{ - // Optimization: - if ((readOffset & 7) == 0) - { - if ( readOffset + ( numberOfBytes << 3 ) > numberOfBitsUsed ) - return false; - - // Write the data - memcpy( outByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytes ); - - readOffset += numberOfBytes << 3; - return true; - } - else - { - return ReadBits( ( unsigned char* ) outByteArray, numberOfBytes * 8 ); - } -} - -// Sets the read pointer back to the beginning of your data. -void BitStream::ResetReadPointer( void ) -{ - readOffset = 0; -} - -// Sets the write pointer back to the beginning of your data. -void BitStream::ResetWritePointer( void ) -{ - numberOfBitsUsed = 0; -} - -// Write a 0 -void BitStream::Write0( void ) -{ - AddBitsAndReallocate( 1 ); - - // New bytes need to be zeroed - if ( ( numberOfBitsUsed & 7 ) == 0 ) - data[ numberOfBitsUsed >> 3 ] = 0; - - numberOfBitsUsed++; -} - -// Write a 1 -void BitStream::Write1( void ) -{ - AddBitsAndReallocate( 1 ); - - BitSize_t numberOfBitsMod8 = numberOfBitsUsed & 7; - - if ( numberOfBitsMod8 == 0 ) - data[ numberOfBitsUsed >> 3 ] = 0x80; - else - data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 - - numberOfBitsUsed++; -} - -// Returns true if the next data read is a 1, false if it is a 0 -bool BitStream::ReadBit( void ) -{ - bool result = ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0; - readOffset++; - return result; -} - -// Align the bitstream to the byte boundary and then write the specified number of bits. -// This is faster than WriteBits but wastes the bits to do the alignment and requires you to call -// SetReadToByteAlignment at the corresponding read position -void BitStream::WriteAlignedBytes( const unsigned char* inByteArray, const unsigned int numberOfBytesToWrite ) -{ - AlignWriteToByteBoundary(); - Write((const char*) inByteArray, numberOfBytesToWrite); -} -void BitStream::EndianSwapBytes( int byteOffset, int length ) -{ - if (DoEndianSwap()) - { - ReverseBytesInPlace(data+byteOffset, length); - } -} -/// Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite -void BitStream::WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite ) -{ - if (inByteArray==0 || inputLength==0) - { - WriteCompressed((unsigned int)0); - return; - } - WriteCompressed(inputLength); - WriteAlignedBytes((const unsigned char*) inByteArray, inputLength < maxBytesToWrite ? inputLength : maxBytesToWrite); -} - -// Read bits, starting at the next aligned bits. Note that the modulus 8 starting offset of the -// sequence must be the same as was used with WriteBits. This will be a problem with packet coalescence -// unless you byte align the coalesced packets. -bool BitStream::ReadAlignedBytes( unsigned char* inOutByteArray, const unsigned int numberOfBytesToRead ) -{ -#ifdef _DEBUG - RakAssert( numberOfBytesToRead > 0 ); -#endif - - if ( numberOfBytesToRead <= 0 ) - return false; - - // Byte align - AlignReadToByteBoundary(); - - if ( readOffset + ( numberOfBytesToRead << 3 ) > numberOfBitsUsed ) - return false; - - // Write the data - memcpy( inOutByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytesToRead ); - - readOffset += numberOfBytesToRead << 3; - - return true; -} -bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead ) -{ - return ReadAlignedBytesSafe(inOutByteArray,(unsigned int&) inputLength,(unsigned int)maxBytesToRead); -} -bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ) -{ - if (ReadCompressed(inputLength)==false) - return false; - if (inputLength > maxBytesToRead) - inputLength=maxBytesToRead; - if (inputLength==0) - return true; - return ReadAlignedBytes((unsigned char*) inOutByteArray, inputLength); -} -bool BitStream::ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead ) -{ - return ReadAlignedBytesSafeAlloc(outByteArray,(unsigned int&) inputLength, maxBytesToRead); -} -bool BitStream::ReadAlignedBytesSafeAlloc( char ** outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ) -{ - rakFree_Ex(*outByteArray, _FILE_AND_LINE_ ); - *outByteArray=0; - if (ReadCompressed(inputLength)==false) - return false; - if (inputLength > maxBytesToRead) - inputLength=maxBytesToRead; - if (inputLength==0) - return true; - *outByteArray = (char*) rakMalloc_Ex( (size_t) inputLength, _FILE_AND_LINE_ ); - return ReadAlignedBytes((unsigned char*) *outByteArray, inputLength); -} - -// Write numberToWrite bits from the input source -void BitStream::WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits ) -{ -// if (numberOfBitsToWrite<=0) -// return; - - AddBitsAndReallocate( numberOfBitsToWrite ); - - const BitSize_t numberOfBitsUsedMod8 = numberOfBitsUsed & 7; - - // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed - if (numberOfBitsUsedMod8==0 && (numberOfBitsToWrite&7)==0) - { - memcpy( data + ( numberOfBitsUsed >> 3 ), inByteArray, numberOfBitsToWrite>>3); - numberOfBitsUsed+=numberOfBitsToWrite; - return; - } - - unsigned char dataByte; - const unsigned char* inputPtr=inByteArray; - - // Faster to put the while at the top surprisingly enough - while ( numberOfBitsToWrite > 0 ) - //do - { - dataByte = *( inputPtr++ ); - - if ( numberOfBitsToWrite < 8 && rightAlignedBits ) // rightAlignedBits means in the case of a partial byte, the bits are aligned from the right (bit 0) rather than the left (as in the normal internal representation) - dataByte <<= 8 - numberOfBitsToWrite; // shift left to get the bits on the left, as in our internal representation - - // Writing to a new byte each time - if ( numberOfBitsUsedMod8 == 0 ) - * ( data + ( numberOfBitsUsed >> 3 ) ) = dataByte; - else - { - // Copy over the new data. - *( data + ( numberOfBitsUsed >> 3 ) ) |= dataByte >> ( numberOfBitsUsedMod8 ); // First half - - if ( 8 - ( numberOfBitsUsedMod8 ) < 8 && 8 - ( numberOfBitsUsedMod8 ) < numberOfBitsToWrite ) // If we didn't write it all out in the first half (8 - (numberOfBitsUsed%8) is the number we wrote in the first half) - { - *( data + ( numberOfBitsUsed >> 3 ) + 1 ) = (unsigned char) ( dataByte << ( 8 - ( numberOfBitsUsedMod8 ) ) ); // Second half (overlaps byte boundary) - } - } - - if ( numberOfBitsToWrite >= 8 ) - { - numberOfBitsUsed += 8; - numberOfBitsToWrite -= 8; - } - else - { - numberOfBitsUsed += numberOfBitsToWrite; - numberOfBitsToWrite=0; - } - } - // } while(numberOfBitsToWrite>0); -} - -// Set the stream to some initial data. For internal use -void BitStream::SetData( unsigned char *inByteArray ) -{ - data=inByteArray; - copyData=false; -} - -// Assume the input source points to a native type, compress and write it -void BitStream::WriteCompressed( const unsigned char* inByteArray, - const unsigned int size, const bool unsignedData ) -{ - BitSize_t currentByte = ( size >> 3 ) - 1; // PCs - - unsigned char byteMatch; - - if ( unsignedData ) - { - byteMatch = 0; - } - - else - { - byteMatch = 0xFF; - } - - // Write upper bytes with a single 1 - // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes - while ( currentByte > 0 ) - { - if ( inByteArray[ currentByte ] == byteMatch ) // If high byte is byteMatch (0 of 0xff) then it would have the same value shifted - { - bool b = true; - Write( b ); - } - else - { - // Write the remainder of the data after writing 0 - bool b = false; - Write( b ); - - WriteBits( inByteArray, ( currentByte + 1 ) << 3, true ); - // currentByte--; - - - return ; - } - - currentByte--; - } - - // If the upper half of the last byte is a 0 (positive) or 16 (negative) then write a 1 and the remaining 4 bits. Otherwise write a 0 and the 8 bites. - if ( ( unsignedData && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0x00 ) || - ( unsignedData == false && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0xF0 ) ) - { - bool b = true; - Write( b ); - WriteBits( inByteArray + currentByte, 4, true ); - } - - else - { - bool b = false; - Write( b ); - WriteBits( inByteArray + currentByte, 8, true ); - } -} - -// Read numberOfBitsToRead bits to the output source -// alignBitsToRight should be set to true to convert internal bitstream data to userdata -// It should be false if you used WriteBits with rightAlignedBits false -bool BitStream::ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight ) -{ -#ifdef _DEBUG - // RakAssert( numberOfBitsToRead > 0 ); -#endif - if (numberOfBitsToRead<=0) - return false; - - if ( readOffset + numberOfBitsToRead > numberOfBitsUsed ) - return false; - - - const BitSize_t readOffsetMod8 = readOffset & 7; - - // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed - if (readOffsetMod8==0 && (numberOfBitsToRead&7)==0) - { - memcpy( inOutByteArray, data + ( readOffset >> 3 ), numberOfBitsToRead>>3); - readOffset+=numberOfBitsToRead; - return true; - } - - - - BitSize_t offset = 0; - - memset( inOutByteArray, 0, (size_t) BITS_TO_BYTES( numberOfBitsToRead ) ); - - while ( numberOfBitsToRead > 0 ) - { - *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) ) << ( readOffsetMod8 ); // First half - - if ( readOffsetMod8 > 0 && numberOfBitsToRead > 8 - ( readOffsetMod8 ) ) // If we have a second half, we didn't read enough bytes in the first half - *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) + 1 ) >> ( 8 - ( readOffsetMod8 ) ); // Second half (overlaps byte boundary) - - if (numberOfBitsToRead>=8) - { - numberOfBitsToRead -= 8; - readOffset += 8; - offset++; - } - else - { - int neg = (int) numberOfBitsToRead - 8; - - if ( neg < 0 ) // Reading a partial byte for the last byte, shift right so the data is aligned on the right - { - - if ( alignBitsToRight ) - * ( inOutByteArray + offset ) >>= -neg; - - readOffset += 8 + neg; - } - else - readOffset += 8; - - offset++; - - numberOfBitsToRead=0; - } - } - - return true; -} - -// Assume the input source points to a compressed native type. Decompress and read it -bool BitStream::ReadCompressed( unsigned char* inOutByteArray, - const unsigned int size, const bool unsignedData ) -{ - unsigned int currentByte = ( size >> 3 ) - 1; - - - unsigned char byteMatch, halfByteMatch; - - if ( unsignedData ) - { - byteMatch = 0; - halfByteMatch = 0; - } - - else - { - byteMatch = 0xFF; - halfByteMatch = 0xF0; - } - - // Upper bytes are specified with a single 1 if they match byteMatch - // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes - while ( currentByte > 0 ) - { - // If we read a 1 then the data is byteMatch. - - bool b; - - if ( Read( b ) == false ) - return false; - - if ( b ) // Check that bit - { - inOutByteArray[ currentByte ] = byteMatch; - currentByte--; - } - else - { - // Read the rest of the bytes - - if ( ReadBits( inOutByteArray, ( currentByte + 1 ) << 3 ) == false ) - return false; - - return true; - } - } - - // All but the first bytes are byteMatch. If the upper half of the last byte is a 0 (positive) or 16 (negative) then what we read will be a 1 and the remaining 4 bits. - // Otherwise we read a 0 and the 8 bytes - //RakAssert(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from - if ( readOffset + 1 > numberOfBitsUsed ) - return false; - - bool b=false; - - if ( Read( b ) == false ) - return false; - - if ( b ) // Check that bit - { - - if ( ReadBits( inOutByteArray + currentByte, 4 ) == false ) - return false; - - inOutByteArray[ currentByte ] |= halfByteMatch; // We have to set the high 4 bits since these are set to 0 by ReadBits - } - else - { - if ( ReadBits( inOutByteArray + currentByte, 8 ) == false ) - return false; - } - - return true; -} - -// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite -void BitStream::AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite ) -{ - BitSize_t newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed; - - if ( numberOfBitsToWrite + numberOfBitsUsed > 0 && ( ( numberOfBitsAllocated - 1 ) >> 3 ) < ( ( newNumberOfBitsAllocated - 1 ) >> 3 ) ) // If we need to allocate 1 or more new bytes - { -#ifdef _DEBUG - // If this assert hits then we need to specify true for the third parameter in the constructor - // It needs to reallocate to hold all the data and can't do it unless we allocated to begin with - // Often hits if you call Write or Serialize on a read-only bitstream - RakAssert( copyData == true ); -#endif - - // Less memory efficient but saves on news and deletes - /// Cap to 1 meg buffer to save on huge allocations - newNumberOfBitsAllocated = ( numberOfBitsToWrite + numberOfBitsUsed ) * 2; - if (newNumberOfBitsAllocated - ( numberOfBitsToWrite + numberOfBitsUsed ) > 1048576 ) - newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed + 1048576; - - // BitSize_t newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated ); - // Use realloc and free so we are more efficient than delete and new for resizing - BitSize_t amountToAllocate = BITS_TO_BYTES( newNumberOfBitsAllocated ); - if (data==(unsigned char*)stackData) - { - if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE) - { - data = ( unsigned char* ) rakMalloc_Ex( (size_t) amountToAllocate, _FILE_AND_LINE_ ); - RakAssert(data); - - // need to copy the stack data over to our new memory area too - memcpy ((void *)data, (void *)stackData, (size_t) BITS_TO_BYTES( numberOfBitsAllocated )); - } - } - else - { - data = ( unsigned char* ) rakRealloc_Ex( data, (size_t) amountToAllocate, _FILE_AND_LINE_ ); - } - -#ifdef _DEBUG - RakAssert( data ); // Make sure realloc succeeded -#endif - // memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0 - } - - if ( newNumberOfBitsAllocated > numberOfBitsAllocated ) - numberOfBitsAllocated = newNumberOfBitsAllocated; -} -BitSize_t BitStream::GetNumberOfBitsAllocated(void) const -{ - return numberOfBitsAllocated; -} -void BitStream::PadWithZeroToByteLength( unsigned int bytes ) -{ - if (GetNumberOfBytesUsed() < bytes) - { - AlignWriteToByteBoundary(); - unsigned int numToWrite = bytes - GetNumberOfBytesUsed(); - AddBitsAndReallocate( BYTES_TO_BITS(numToWrite) ); - memset(data+BITS_TO_BYTES(numberOfBitsUsed), 0, (size_t) numToWrite); - numberOfBitsUsed+=BYTES_TO_BITS(numToWrite); - } -} - -/* -// Julius Goryavsky's version of Harley's algorithm. -// 17 elementary ops plus an indexed load, if the machine -// has "and not." - -int nlz10b(unsigned x) { - - static char table[64] = - {32,20,19, u, u,18, u, 7, 10,17, u, u,14, u, 6, u, - u, 9, u,16, u, u, 1,26, u,13, u, u,24, 5, u, u, - u,21, u, 8,11, u,15, u, u, u, u, 2,27, 0,25, u, - 22, u,12, u, u, 3,28, u, 23, u, 4,29, u, u,30,31}; - - x = x | (x >> 1); // Propagate leftmost - x = x | (x >> 2); // 1-bit to the right. - x = x | (x >> 4); - x = x | (x >> 8); - x = x & ~(x >> 16); - x = x*0xFD7049FF; // Activate this line or the following 3. -// x = (x << 9) - x; // Multiply by 511. -// x = (x << 11) - x; // Multiply by 2047. -// x = (x << 14) - x; // Multiply by 16383. - return table[x >> 26]; -} -*/ -int BitStream::NumberOfLeadingZeroes( int8_t x ) {return NumberOfLeadingZeroes((uint8_t)x);} -int BitStream::NumberOfLeadingZeroes( uint8_t x ) -{ - uint8_t y; - int n; - - n = 8; - y = x >> 4; if (y != 0) {n = n - 4; x = y;} - y = x >> 2; if (y != 0) {n = n - 2; x = y;} - y = x >> 1; if (y != 0) return n - 2; - return (int)(n - x); -} -int BitStream::NumberOfLeadingZeroes( int16_t x ) {return NumberOfLeadingZeroes((uint16_t)x);} -int BitStream::NumberOfLeadingZeroes( uint16_t x ) -{ - uint16_t y; - int n; - - n = 16; - y = x >> 8; if (y != 0) {n = n - 8; x = y;} - y = x >> 4; if (y != 0) {n = n - 4; x = y;} - y = x >> 2; if (y != 0) {n = n - 2; x = y;} - y = x >> 1; if (y != 0) return n - 2; - return (int)(n - x); -} -int BitStream::NumberOfLeadingZeroes( int32_t x ) {return NumberOfLeadingZeroes((uint32_t)x);} -int BitStream::NumberOfLeadingZeroes( uint32_t x ) -{ - uint32_t y; - int n; - - n = 32; - y = x >>16; if (y != 0) {n = n -16; x = y;} - y = x >> 8; if (y != 0) {n = n - 8; x = y;} - y = x >> 4; if (y != 0) {n = n - 4; x = y;} - y = x >> 2; if (y != 0) {n = n - 2; x = y;} - y = x >> 1; if (y != 0) return n - 2; - return (int)(n - x); -} -int BitStream::NumberOfLeadingZeroes( int64_t x ) {return NumberOfLeadingZeroes((uint64_t)x);} -int BitStream::NumberOfLeadingZeroes( uint64_t x ) -{ - uint64_t y; - int n; - - n = 64; - y = x >>32; if (y != 0) {n = n -32; x = y;} - y = x >>16; if (y != 0) {n = n -16; x = y;} - y = x >> 8; if (y != 0) {n = n - 8; x = y;} - y = x >> 4; if (y != 0) {n = n - 4; x = y;} - y = x >> 2; if (y != 0) {n = n - 2; x = y;} - y = x >> 1; if (y != 0) return n - 2; - return (int)(n - x); -} - -// Should hit if reads didn't match writes -void BitStream::AssertStreamEmpty( void ) -{ - RakAssert( readOffset == numberOfBitsUsed ); -} -void BitStream::PrintBits( char *out ) const -{ - if ( numberOfBitsUsed <= 0 ) - { - strcpy(out, "No bits\n" ); - return; - } - - unsigned int strIndex=0; - for ( BitSize_t counter = 0; counter < BITS_TO_BYTES( numberOfBitsUsed ) && strIndex < 2000 ; counter++ ) - { - BitSize_t stop; - - if ( counter == ( numberOfBitsUsed - 1 ) >> 3 ) - stop = 8 - ( ( ( numberOfBitsUsed - 1 ) & 7 ) + 1 ); - else - stop = 0; - - for ( BitSize_t counter2 = 7; counter2 >= stop; counter2-- ) - { - if ( ( data[ counter ] >> counter2 ) & 1 ) - out[strIndex++]='1'; - else - out[strIndex++]='0'; - - if (counter2==0) - break; - } - - out[strIndex++]=' '; - } - - out[strIndex++]='\n'; - - out[strIndex++]=0; -} -void BitStream::PrintBits( void ) const -{ - char out[2048]; - PrintBits(out); - RAKNET_DEBUG_PRINTF("%s", out); -} -void BitStream::PrintHex( char *out ) const -{ - BitSize_t i; - for ( i=0; i < GetNumberOfBytesUsed(); i++) - { - sprintf(out+i*3, "%02x ", data[i]); - } -} -void BitStream::PrintHex( void ) const -{ - char out[2048]; - PrintHex(out); - RAKNET_DEBUG_PRINTF("%s", out); -} - -// Exposes the data for you to look at, like PrintBits does. -// Data will point to the stream. Returns the length in bits of the stream. -BitSize_t BitStream::CopyData( unsigned char** _data ) const -{ -#ifdef _DEBUG - RakAssert( numberOfBitsUsed > 0 ); -#endif - - *_data = (unsigned char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES( numberOfBitsUsed ), _FILE_AND_LINE_ ); - memcpy( *_data, data, sizeof(unsigned char) * (size_t) ( BITS_TO_BYTES( numberOfBitsUsed ) ) ); - return numberOfBitsUsed; -} - -// Ignore data we don't intend to read -void BitStream::IgnoreBits( const BitSize_t numberOfBits ) -{ - readOffset += numberOfBits; -} - -void BitStream::IgnoreBytes( const unsigned int numberOfBytes ) -{ - IgnoreBits(BYTES_TO_BITS(numberOfBytes)); -} - -// Move the write pointer to a position on the array. Dangerous if you don't know what you are doing! -// Doesn't work with non-aligned data! -void BitStream::SetWriteOffset( const BitSize_t offset ) -{ - numberOfBitsUsed = offset; -} - -/* -BitSize_t BitStream::GetWriteOffset( void ) const -{ -return numberOfBitsUsed; -} - -// Returns the length in bits of the stream -BitSize_t BitStream::GetNumberOfBitsUsed( void ) const -{ -return GetWriteOffset(); -} - -// Returns the length in bytes of the stream -BitSize_t BitStream::GetNumberOfBytesUsed( void ) const -{ -return BITS_TO_BYTES( numberOfBitsUsed ); -} - -// Returns the number of bits into the stream that we have read -BitSize_t BitStream::GetReadOffset( void ) const -{ -return readOffset; -} - - -// Sets the read bit index -void BitStream::SetReadOffset( const BitSize_t newReadOffset ) -{ -readOffset=newReadOffset; -} - -// Returns the number of bits left in the stream that haven't been read -BitSize_t BitStream::GetNumberOfUnreadBits( void ) const -{ -return numberOfBitsUsed - readOffset; -} -// Exposes the internal data -unsigned char* BitStream::GetData( void ) const -{ -return data; -} - -*/ -// If we used the constructor version with copy data off, this makes sure it is set to on and the data pointed to is copied. -void BitStream::AssertCopyData( void ) -{ - if ( copyData == false ) - { - copyData = true; - - if ( numberOfBitsAllocated > 0 ) - { - unsigned char * newdata = ( unsigned char* ) rakMalloc_Ex( (size_t) BITS_TO_BYTES( numberOfBitsAllocated ), _FILE_AND_LINE_ ); -#ifdef _DEBUG - - RakAssert( data ); -#endif - - memcpy( newdata, data, (size_t) BITS_TO_BYTES( numberOfBitsAllocated ) ); - data = newdata; - } - - else - data = 0; - } -} -bool BitStream::IsNetworkOrderInternal(void) -{ - - - - - - static const bool isNetworkOrder=(htonl(12345) == 12345); - return isNetworkOrder; - -} -void BitStream::ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length) -{ - for (BitSize_t i=0; i < length; i++) - inOutByteArray[i]=inByteArray[length-i-1]; -} -void BitStream::ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length) -{ - unsigned char temp; - BitSize_t i; - for (i=0; i < (length>>1); i++) - { - temp = inOutData[i]; - inOutData[i]=inOutData[length-i-1]; - inOutData[length-i-1]=temp; - } -} - -bool BitStream::Read(char *varString) -{ - return RakString::Deserialize(varString,this); -} -bool BitStream::Read(unsigned char *varString) -{ - return RakString::Deserialize((char*) varString,this); -} -void BitStream::WriteAlignedVar8(const char *inByteArray) -{ - RakAssert((numberOfBitsUsed&7)==0); - AddBitsAndReallocate(1*8); - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; - numberOfBitsUsed+=1*8; -} -bool BitStream::ReadAlignedVar8(char *inOutByteArray) -{ - RakAssert((readOffset&7)==0); - if ( readOffset + 1*8 > numberOfBitsUsed ) - return false; - - inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; - readOffset+=1*8; - return true; -} -void BitStream::WriteAlignedVar16(const char *inByteArray) -{ - RakAssert((numberOfBitsUsed&7)==0); - AddBitsAndReallocate(2*8); -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[1]; - data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[0]; - } - else -#endif - { - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; - data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1]; - } - - numberOfBitsUsed+=2*8; -} -bool BitStream::ReadAlignedVar16(char *inOutByteArray) -{ - RakAssert((readOffset&7)==0); - if ( readOffset + 2*8 > numberOfBitsUsed ) - return false; -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - inOutByteArray[0] = data[( readOffset >> 3 ) + 1]; - inOutByteArray[1] = data[( readOffset >> 3 ) + 0]; - } - else -#endif - { - inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; - inOutByteArray[1] = data[( readOffset >> 3 ) + 1]; - } - - readOffset+=2*8; - return true; -} -void BitStream::WriteAlignedVar32(const char *inByteArray) -{ - RakAssert((numberOfBitsUsed&7)==0); - AddBitsAndReallocate(4*8); -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[3]; - data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[2]; - data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[1]; - data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[0]; - } - else -#endif - { - data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; - data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1]; - data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[2]; - data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[3]; - } - - numberOfBitsUsed+=4*8; -} -bool BitStream::ReadAlignedVar32(char *inOutByteArray) -{ - RakAssert((readOffset&7)==0); - if ( readOffset + 4*8 > numberOfBitsUsed ) - return false; -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - inOutByteArray[0] = data[( readOffset >> 3 ) + 3]; - inOutByteArray[1] = data[( readOffset >> 3 ) + 2]; - inOutByteArray[2] = data[( readOffset >> 3 ) + 1]; - inOutByteArray[3] = data[( readOffset >> 3 ) + 0]; - } - else -#endif - { - inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; - inOutByteArray[1] = data[( readOffset >> 3 ) + 1]; - inOutByteArray[2] = data[( readOffset >> 3 ) + 2]; - inOutByteArray[3] = data[( readOffset >> 3 ) + 3]; - } - - readOffset+=4*8; - return true; -} -bool BitStream::ReadFloat16( float &outFloat, float floatMin, float floatMax ) -{ - unsigned short percentile; - if (Read(percentile)) - { - RakAssert(floatMax>floatMin); - outFloat = floatMin + ((float) percentile / 65535.0f) * (floatMax-floatMin); - if (outFloatfloatMax) - outFloat=floatMax; - return true; - } - return false; -} -bool BitStream::SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax) -{ - if (writeToBitstream) - WriteFloat16(inOutFloat, floatMin, floatMax); - else - return ReadFloat16(inOutFloat, floatMin, floatMax); - return true; -} -void BitStream::WriteFloat16( float inOutFloat, float floatMin, float floatMax ) -{ - RakAssert(floatMax>floatMin); - if (inOutFloat>floatMax+.001) - { - RakAssert(inOutFloat<=floatMax+.001); - } - if (inOutFloat=floatMin-.001); - } - float percentile=65535.0f * (inOutFloat-floatMin)/(floatMax-floatMin); - if (percentile<0.0) - percentile=0.0; - if (percentile>65535.0f) - percentile=65535.0f; - Write((unsigned short)percentile); -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // #if _MSC_VER < 1299 +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization +#include "BitStream_NoTemplate.cpp" +#else + +#include "BitStream.h" +#include +#include +#include + +#include "SocketIncludes.h" +#include "RakNetDefines.h" + + + +#if defined(_WIN32) +#include // htonl +#include +#include +#include + + +#else +#include +#include +#if defined(ANDROID) +#include +#else +#include +#endif +#include +#endif + +// MSWin uses _copysign, others use copysign... +#ifndef _WIN32 +#define _copysign copysign +#endif + +using namespace RakNet; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +STATIC_FACTORY_DEFINITIONS(BitStream,BitStream) + +BitStream::BitStream() +{ + numberOfBitsUsed = 0; + //numberOfBitsAllocated = 32 * 8; + numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; + readOffset = 0; + //data = ( unsigned char* ) rakMalloc_Ex( 32, _FILE_AND_LINE_ ); + data = ( unsigned char* ) stackData; + +#ifdef _DEBUG + // RakAssert( data ); +#endif + //memset(data, 0, 32); + copyData = true; +} + +BitStream::BitStream( const unsigned int initialBytesToAllocate ) +{ + numberOfBitsUsed = 0; + readOffset = 0; + if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE) + { + data = ( unsigned char* ) stackData; + numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; + } + else + { + data = ( unsigned char* ) rakMalloc_Ex( (size_t) initialBytesToAllocate, _FILE_AND_LINE_ ); + numberOfBitsAllocated = initialBytesToAllocate << 3; + } +#ifdef _DEBUG + RakAssert( data ); +#endif + // memset(data, 0, initialBytesToAllocate); + copyData = true; +} + +BitStream::BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData ) +{ + numberOfBitsUsed = lengthInBytes << 3; + readOffset = 0; + copyData = _copyData; + numberOfBitsAllocated = lengthInBytes << 3; + + if ( copyData ) + { + if ( lengthInBytes > 0 ) + { + if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE) + { + data = ( unsigned char* ) stackData; + numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << 3; + } + else + { + data = ( unsigned char* ) rakMalloc_Ex( (size_t) lengthInBytes, _FILE_AND_LINE_ ); + } +#ifdef _DEBUG + RakAssert( data ); +#endif + memcpy( data, _data, (size_t) lengthInBytes ); + } + else + data = 0; + } + else + data = ( unsigned char* ) _data; +} + +// Use this if you pass a pointer copy to the constructor (_copyData==false) and want to overallocate to prevent reallocation +void BitStream::SetNumberOfBitsAllocated( const BitSize_t lengthInBits ) +{ +#ifdef _DEBUG + RakAssert( lengthInBits >= ( BitSize_t ) numberOfBitsAllocated ); +#endif + numberOfBitsAllocated = lengthInBits; +} + +BitStream::~BitStream() +{ + if ( copyData && numberOfBitsAllocated > (BITSTREAM_STACK_ALLOCATION_SIZE << 3)) + rakFree_Ex( data , _FILE_AND_LINE_ ); // Use realloc and free so we are more efficient than delete and new for resizing +} + +void BitStream::Reset( void ) +{ + // Note: Do NOT reallocate memory because BitStream is used + // in places to serialize/deserialize a buffer. Reallocation + // is a dangerous operation (may result in leaks). + + if ( numberOfBitsUsed > 0 ) + { + // memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed)); + } + + // Don't free memory here for speed efficiency + //free(data); // Use realloc and free so we are more efficient than delete and new for resizing + numberOfBitsUsed = 0; + + //numberOfBitsAllocated=8; + readOffset = 0; + + //data=(unsigned char*)rakMalloc_Ex(1, _FILE_AND_LINE_); + // if (numberOfBitsAllocated>0) + // memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated)); +} + +// Write an array or casted stream +void BitStream::Write( const char* inputByteArray, const unsigned int numberOfBytes ) +{ + if (numberOfBytes==0) + return; + + // Optimization: + if ((numberOfBitsUsed & 7) == 0) + { + AddBitsAndReallocate( BYTES_TO_BITS(numberOfBytes) ); + memcpy(data+BITS_TO_BYTES(numberOfBitsUsed), inputByteArray, (size_t) numberOfBytes); + numberOfBitsUsed+=BYTES_TO_BITS(numberOfBytes); + } + else + { + WriteBits( ( unsigned char* ) inputByteArray, numberOfBytes * 8, true ); + } + +} +void BitStream::Write( BitStream *bitStream) +{ + Write(bitStream, bitStream->GetNumberOfBitsUsed()-bitStream->GetReadOffset()); +} +void BitStream::Write( BitStream *bitStream, BitSize_t numberOfBits ) +{ + AddBitsAndReallocate( numberOfBits ); + BitSize_t numberOfBitsMod8; + + if ((bitStream->GetReadOffset()&7)==0 && (numberOfBitsUsed&7)==0) + { + int readOffsetBytes=bitStream->GetReadOffset()/8; + int numBytes=numberOfBits/8; + memcpy(data + (numberOfBitsUsed >> 3), bitStream->GetData()+readOffsetBytes, numBytes); + numberOfBits-=BYTES_TO_BITS(numBytes); + bitStream->SetReadOffset(BYTES_TO_BITS(numBytes+readOffsetBytes)); + numberOfBitsUsed+=BYTES_TO_BITS(numBytes); + } + + while (numberOfBits-->0 && bitStream->readOffset + 1 <= bitStream->numberOfBitsUsed) + { + numberOfBitsMod8 = numberOfBitsUsed & 7; + if ( numberOfBitsMod8 == 0 ) + { + // New byte + if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) ) + { + // Write 1 + data[ numberOfBitsUsed >> 3 ] = 0x80; + } + else + { + // Write 0 + data[ numberOfBitsUsed >> 3 ] = 0; + } + + } + else + { + // Existing byte + if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) ) + data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 + // else 0, do nothing + } + + bitStream->readOffset++; + numberOfBitsUsed++; + } +} +void BitStream::Write( BitStream &bitStream, BitSize_t numberOfBits ) +{ + Write(&bitStream, numberOfBits); +} +void BitStream::Write( BitStream &bitStream ) +{ + Write(&bitStream); +} +bool BitStream::Read( BitStream *bitStream, BitSize_t numberOfBits ) +{ + if (GetNumberOfUnreadBits() < numberOfBits) + return false; + bitStream->Write(this, numberOfBits); + return true; +} +bool BitStream::Read( BitStream *bitStream ) +{ + bitStream->Write(this); + return true; +} +bool BitStream::Read( BitStream &bitStream, BitSize_t numberOfBits ) +{ + if (GetNumberOfUnreadBits() < numberOfBits) + return false; + bitStream.Write(this, numberOfBits); + return true; +} +bool BitStream::Read( BitStream &bitStream ) +{ + bitStream.Write(this); + return true; +} + +// Read an array or casted stream +bool BitStream::Read( char* outByteArray, const unsigned int numberOfBytes ) +{ + // Optimization: + if ((readOffset & 7) == 0) + { + if ( readOffset + ( numberOfBytes << 3 ) > numberOfBitsUsed ) + return false; + + // Write the data + memcpy( outByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytes ); + + readOffset += numberOfBytes << 3; + return true; + } + else + { + return ReadBits( ( unsigned char* ) outByteArray, numberOfBytes * 8 ); + } +} + +// Sets the read pointer back to the beginning of your data. +void BitStream::ResetReadPointer( void ) +{ + readOffset = 0; +} + +// Sets the write pointer back to the beginning of your data. +void BitStream::ResetWritePointer( void ) +{ + numberOfBitsUsed = 0; +} + +// Write a 0 +void BitStream::Write0( void ) +{ + AddBitsAndReallocate( 1 ); + + // New bytes need to be zeroed + if ( ( numberOfBitsUsed & 7 ) == 0 ) + data[ numberOfBitsUsed >> 3 ] = 0; + + numberOfBitsUsed++; +} + +// Write a 1 +void BitStream::Write1( void ) +{ + AddBitsAndReallocate( 1 ); + + BitSize_t numberOfBitsMod8 = numberOfBitsUsed & 7; + + if ( numberOfBitsMod8 == 0 ) + data[ numberOfBitsUsed >> 3 ] = 0x80; + else + data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 + + numberOfBitsUsed++; +} + +// Returns true if the next data read is a 1, false if it is a 0 +bool BitStream::ReadBit( void ) +{ + bool result = ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0; + readOffset++; + return result; +} + +// Align the bitstream to the byte boundary and then write the specified number of bits. +// This is faster than WriteBits but wastes the bits to do the alignment and requires you to call +// SetReadToByteAlignment at the corresponding read position +void BitStream::WriteAlignedBytes( const unsigned char* inByteArray, const unsigned int numberOfBytesToWrite ) +{ + AlignWriteToByteBoundary(); + Write((const char*) inByteArray, numberOfBytesToWrite); +} +void BitStream::EndianSwapBytes( int byteOffset, int length ) +{ + if (DoEndianSwap()) + { + ReverseBytesInPlace(data+byteOffset, length); + } +} +/// Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite +void BitStream::WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite ) +{ + if (inByteArray==0 || inputLength==0) + { + WriteCompressed((unsigned int)0); + return; + } + WriteCompressed(inputLength); + WriteAlignedBytes((const unsigned char*) inByteArray, inputLength < maxBytesToWrite ? inputLength : maxBytesToWrite); +} + +// Read bits, starting at the next aligned bits. Note that the modulus 8 starting offset of the +// sequence must be the same as was used with WriteBits. This will be a problem with packet coalescence +// unless you byte align the coalesced packets. +bool BitStream::ReadAlignedBytes( unsigned char* inOutByteArray, const unsigned int numberOfBytesToRead ) +{ +#ifdef _DEBUG + RakAssert( numberOfBytesToRead > 0 ); +#endif + + if ( numberOfBytesToRead <= 0 ) + return false; + + // Byte align + AlignReadToByteBoundary(); + + if ( readOffset + ( numberOfBytesToRead << 3 ) > numberOfBitsUsed ) + return false; + + // Write the data + memcpy( inOutByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytesToRead ); + + readOffset += numberOfBytesToRead << 3; + + return true; +} +bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead ) +{ + return ReadAlignedBytesSafe(inOutByteArray,(unsigned int&) inputLength,(unsigned int)maxBytesToRead); +} +bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ) +{ + if (ReadCompressed(inputLength)==false) + return false; + if (inputLength > maxBytesToRead) + inputLength=maxBytesToRead; + if (inputLength==0) + return true; + return ReadAlignedBytes((unsigned char*) inOutByteArray, inputLength); +} +bool BitStream::ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead ) +{ + return ReadAlignedBytesSafeAlloc(outByteArray,(unsigned int&) inputLength, maxBytesToRead); +} +bool BitStream::ReadAlignedBytesSafeAlloc( char ** outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ) +{ + rakFree_Ex(*outByteArray, _FILE_AND_LINE_ ); + *outByteArray=0; + if (ReadCompressed(inputLength)==false) + return false; + if (inputLength > maxBytesToRead) + inputLength=maxBytesToRead; + if (inputLength==0) + return true; + *outByteArray = (char*) rakMalloc_Ex( (size_t) inputLength, _FILE_AND_LINE_ ); + return ReadAlignedBytes((unsigned char*) *outByteArray, inputLength); +} + +// Write numberToWrite bits from the input source +void BitStream::WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits ) +{ +// if (numberOfBitsToWrite<=0) +// return; + + AddBitsAndReallocate( numberOfBitsToWrite ); + + const BitSize_t numberOfBitsUsedMod8 = numberOfBitsUsed & 7; + + // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed + if (numberOfBitsUsedMod8==0 && (numberOfBitsToWrite&7)==0) + { + memcpy( data + ( numberOfBitsUsed >> 3 ), inByteArray, numberOfBitsToWrite>>3); + numberOfBitsUsed+=numberOfBitsToWrite; + return; + } + + unsigned char dataByte; + const unsigned char* inputPtr=inByteArray; + + // Faster to put the while at the top surprisingly enough + while ( numberOfBitsToWrite > 0 ) + //do + { + dataByte = *( inputPtr++ ); + + if ( numberOfBitsToWrite < 8 && rightAlignedBits ) // rightAlignedBits means in the case of a partial byte, the bits are aligned from the right (bit 0) rather than the left (as in the normal internal representation) + dataByte <<= 8 - numberOfBitsToWrite; // shift left to get the bits on the left, as in our internal representation + + // Writing to a new byte each time + if ( numberOfBitsUsedMod8 == 0 ) + * ( data + ( numberOfBitsUsed >> 3 ) ) = dataByte; + else + { + // Copy over the new data. + *( data + ( numberOfBitsUsed >> 3 ) ) |= dataByte >> ( numberOfBitsUsedMod8 ); // First half + + if ( 8 - ( numberOfBitsUsedMod8 ) < 8 && 8 - ( numberOfBitsUsedMod8 ) < numberOfBitsToWrite ) // If we didn't write it all out in the first half (8 - (numberOfBitsUsed%8) is the number we wrote in the first half) + { + *( data + ( numberOfBitsUsed >> 3 ) + 1 ) = (unsigned char) ( dataByte << ( 8 - ( numberOfBitsUsedMod8 ) ) ); // Second half (overlaps byte boundary) + } + } + + if ( numberOfBitsToWrite >= 8 ) + { + numberOfBitsUsed += 8; + numberOfBitsToWrite -= 8; + } + else + { + numberOfBitsUsed += numberOfBitsToWrite; + numberOfBitsToWrite=0; + } + } + // } while(numberOfBitsToWrite>0); +} + +// Set the stream to some initial data. For internal use +void BitStream::SetData( unsigned char *inByteArray ) +{ + data=inByteArray; + copyData=false; +} + +// Assume the input source points to a native type, compress and write it +void BitStream::WriteCompressed( const unsigned char* inByteArray, + const unsigned int size, const bool unsignedData ) +{ + BitSize_t currentByte = ( size >> 3 ) - 1; // PCs + + unsigned char byteMatch; + + if ( unsignedData ) + { + byteMatch = 0; + } + + else + { + byteMatch = 0xFF; + } + + // Write upper bytes with a single 1 + // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes + while ( currentByte > 0 ) + { + if ( inByteArray[ currentByte ] == byteMatch ) // If high byte is byteMatch (0 of 0xff) then it would have the same value shifted + { + bool b = true; + Write( b ); + } + else + { + // Write the remainder of the data after writing 0 + bool b = false; + Write( b ); + + WriteBits( inByteArray, ( currentByte + 1 ) << 3, true ); + // currentByte--; + + + return ; + } + + currentByte--; + } + + // If the upper half of the last byte is a 0 (positive) or 16 (negative) then write a 1 and the remaining 4 bits. Otherwise write a 0 and the 8 bites. + if ( ( unsignedData && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0x00 ) || + ( unsignedData == false && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0xF0 ) ) + { + bool b = true; + Write( b ); + WriteBits( inByteArray + currentByte, 4, true ); + } + + else + { + bool b = false; + Write( b ); + WriteBits( inByteArray + currentByte, 8, true ); + } +} + +// Read numberOfBitsToRead bits to the output source +// alignBitsToRight should be set to true to convert internal bitstream data to userdata +// It should be false if you used WriteBits with rightAlignedBits false +bool BitStream::ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight ) +{ +#ifdef _DEBUG + // RakAssert( numberOfBitsToRead > 0 ); +#endif + if (numberOfBitsToRead<=0) + return false; + + if ( readOffset + numberOfBitsToRead > numberOfBitsUsed ) + return false; + + + const BitSize_t readOffsetMod8 = readOffset & 7; + + // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed + if (readOffsetMod8==0 && (numberOfBitsToRead&7)==0) + { + memcpy( inOutByteArray, data + ( readOffset >> 3 ), numberOfBitsToRead>>3); + readOffset+=numberOfBitsToRead; + return true; + } + + + + BitSize_t offset = 0; + + memset( inOutByteArray, 0, (size_t) BITS_TO_BYTES( numberOfBitsToRead ) ); + + while ( numberOfBitsToRead > 0 ) + { + *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) ) << ( readOffsetMod8 ); // First half + + if ( readOffsetMod8 > 0 && numberOfBitsToRead > 8 - ( readOffsetMod8 ) ) // If we have a second half, we didn't read enough bytes in the first half + *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) + 1 ) >> ( 8 - ( readOffsetMod8 ) ); // Second half (overlaps byte boundary) + + if (numberOfBitsToRead>=8) + { + numberOfBitsToRead -= 8; + readOffset += 8; + offset++; + } + else + { + int neg = (int) numberOfBitsToRead - 8; + + if ( neg < 0 ) // Reading a partial byte for the last byte, shift right so the data is aligned on the right + { + + if ( alignBitsToRight ) + * ( inOutByteArray + offset ) >>= -neg; + + readOffset += 8 + neg; + } + else + readOffset += 8; + + offset++; + + numberOfBitsToRead=0; + } + } + + return true; +} + +// Assume the input source points to a compressed native type. Decompress and read it +bool BitStream::ReadCompressed( unsigned char* inOutByteArray, + const unsigned int size, const bool unsignedData ) +{ + unsigned int currentByte = ( size >> 3 ) - 1; + + + unsigned char byteMatch, halfByteMatch; + + if ( unsignedData ) + { + byteMatch = 0; + halfByteMatch = 0; + } + + else + { + byteMatch = 0xFF; + halfByteMatch = 0xF0; + } + + // Upper bytes are specified with a single 1 if they match byteMatch + // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes + while ( currentByte > 0 ) + { + // If we read a 1 then the data is byteMatch. + + bool b; + + if ( Read( b ) == false ) + return false; + + if ( b ) // Check that bit + { + inOutByteArray[ currentByte ] = byteMatch; + currentByte--; + } + else + { + // Read the rest of the bytes + + if ( ReadBits( inOutByteArray, ( currentByte + 1 ) << 3 ) == false ) + return false; + + return true; + } + } + + // All but the first bytes are byteMatch. If the upper half of the last byte is a 0 (positive) or 16 (negative) then what we read will be a 1 and the remaining 4 bits. + // Otherwise we read a 0 and the 8 bytes + //RakAssert(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from + if ( readOffset + 1 > numberOfBitsUsed ) + return false; + + bool b=false; + + if ( Read( b ) == false ) + return false; + + if ( b ) // Check that bit + { + + if ( ReadBits( inOutByteArray + currentByte, 4 ) == false ) + return false; + + inOutByteArray[ currentByte ] |= halfByteMatch; // We have to set the high 4 bits since these are set to 0 by ReadBits + } + else + { + if ( ReadBits( inOutByteArray + currentByte, 8 ) == false ) + return false; + } + + return true; +} + +// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite +void BitStream::AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite ) +{ + BitSize_t newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed; + + if ( numberOfBitsToWrite + numberOfBitsUsed > 0 && ( ( numberOfBitsAllocated - 1 ) >> 3 ) < ( ( newNumberOfBitsAllocated - 1 ) >> 3 ) ) // If we need to allocate 1 or more new bytes + { +#ifdef _DEBUG + // If this assert hits then we need to specify true for the third parameter in the constructor + // It needs to reallocate to hold all the data and can't do it unless we allocated to begin with + // Often hits if you call Write or Serialize on a read-only bitstream + RakAssert( copyData == true ); +#endif + + // Less memory efficient but saves on news and deletes + /// Cap to 1 meg buffer to save on huge allocations + newNumberOfBitsAllocated = ( numberOfBitsToWrite + numberOfBitsUsed ) * 2; + if (newNumberOfBitsAllocated - ( numberOfBitsToWrite + numberOfBitsUsed ) > 1048576 ) + newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed + 1048576; + + // BitSize_t newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated ); + // Use realloc and free so we are more efficient than delete and new for resizing + BitSize_t amountToAllocate = BITS_TO_BYTES( newNumberOfBitsAllocated ); + if (data==(unsigned char*)stackData) + { + if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE) + { + data = ( unsigned char* ) rakMalloc_Ex( (size_t) amountToAllocate, _FILE_AND_LINE_ ); + RakAssert(data); + + // need to copy the stack data over to our new memory area too + memcpy ((void *)data, (void *)stackData, (size_t) BITS_TO_BYTES( numberOfBitsAllocated )); + } + } + else + { + data = ( unsigned char* ) rakRealloc_Ex( data, (size_t) amountToAllocate, _FILE_AND_LINE_ ); + } + +#ifdef _DEBUG + RakAssert( data ); // Make sure realloc succeeded +#endif + // memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0 + } + + if ( newNumberOfBitsAllocated > numberOfBitsAllocated ) + numberOfBitsAllocated = newNumberOfBitsAllocated; +} +BitSize_t BitStream::GetNumberOfBitsAllocated(void) const +{ + return numberOfBitsAllocated; +} +void BitStream::PadWithZeroToByteLength( unsigned int bytes ) +{ + if (GetNumberOfBytesUsed() < bytes) + { + AlignWriteToByteBoundary(); + unsigned int numToWrite = bytes - GetNumberOfBytesUsed(); + AddBitsAndReallocate( BYTES_TO_BITS(numToWrite) ); + memset(data+BITS_TO_BYTES(numberOfBitsUsed), 0, (size_t) numToWrite); + numberOfBitsUsed+=BYTES_TO_BITS(numToWrite); + } +} + +/* +// Julius Goryavsky's version of Harley's algorithm. +// 17 elementary ops plus an indexed load, if the machine +// has "and not." + +int nlz10b(unsigned x) { + + static char table[64] = + {32,20,19, u, u,18, u, 7, 10,17, u, u,14, u, 6, u, + u, 9, u,16, u, u, 1,26, u,13, u, u,24, 5, u, u, + u,21, u, 8,11, u,15, u, u, u, u, 2,27, 0,25, u, + 22, u,12, u, u, 3,28, u, 23, u, 4,29, u, u,30,31}; + + x = x | (x >> 1); // Propagate leftmost + x = x | (x >> 2); // 1-bit to the right. + x = x | (x >> 4); + x = x | (x >> 8); + x = x & ~(x >> 16); + x = x*0xFD7049FF; // Activate this line or the following 3. +// x = (x << 9) - x; // Multiply by 511. +// x = (x << 11) - x; // Multiply by 2047. +// x = (x << 14) - x; // Multiply by 16383. + return table[x >> 26]; +} +*/ +int BitStream::NumberOfLeadingZeroes( int8_t x ) {return NumberOfLeadingZeroes((uint8_t)x);} +int BitStream::NumberOfLeadingZeroes( uint8_t x ) +{ + uint8_t y; + int n; + + n = 8; + y = x >> 4; if (y != 0) {n = n - 4; x = y;} + y = x >> 2; if (y != 0) {n = n - 2; x = y;} + y = x >> 1; if (y != 0) return n - 2; + return (int)(n - x); +} +int BitStream::NumberOfLeadingZeroes( int16_t x ) {return NumberOfLeadingZeroes((uint16_t)x);} +int BitStream::NumberOfLeadingZeroes( uint16_t x ) +{ + uint16_t y; + int n; + + n = 16; + y = x >> 8; if (y != 0) {n = n - 8; x = y;} + y = x >> 4; if (y != 0) {n = n - 4; x = y;} + y = x >> 2; if (y != 0) {n = n - 2; x = y;} + y = x >> 1; if (y != 0) return n - 2; + return (int)(n - x); +} +int BitStream::NumberOfLeadingZeroes( int32_t x ) {return NumberOfLeadingZeroes((uint32_t)x);} +int BitStream::NumberOfLeadingZeroes( uint32_t x ) +{ + uint32_t y; + int n; + + n = 32; + y = x >>16; if (y != 0) {n = n -16; x = y;} + y = x >> 8; if (y != 0) {n = n - 8; x = y;} + y = x >> 4; if (y != 0) {n = n - 4; x = y;} + y = x >> 2; if (y != 0) {n = n - 2; x = y;} + y = x >> 1; if (y != 0) return n - 2; + return (int)(n - x); +} +int BitStream::NumberOfLeadingZeroes( int64_t x ) {return NumberOfLeadingZeroes((uint64_t)x);} +int BitStream::NumberOfLeadingZeroes( uint64_t x ) +{ + uint64_t y; + int n; + + n = 64; + y = x >>32; if (y != 0) {n = n -32; x = y;} + y = x >>16; if (y != 0) {n = n -16; x = y;} + y = x >> 8; if (y != 0) {n = n - 8; x = y;} + y = x >> 4; if (y != 0) {n = n - 4; x = y;} + y = x >> 2; if (y != 0) {n = n - 2; x = y;} + y = x >> 1; if (y != 0) return n - 2; + return (int)(n - x); +} + +// Should hit if reads didn't match writes +void BitStream::AssertStreamEmpty( void ) +{ + RakAssert( readOffset == numberOfBitsUsed ); +} +void BitStream::PrintBits( char *out ) const +{ + if ( numberOfBitsUsed <= 0 ) + { + strcpy(out, "No bits\n" ); + return; + } + + unsigned int strIndex=0; + for ( BitSize_t counter = 0; counter < BITS_TO_BYTES( numberOfBitsUsed ) && strIndex < 2000 ; counter++ ) + { + BitSize_t stop; + + if ( counter == ( numberOfBitsUsed - 1 ) >> 3 ) + stop = 8 - ( ( ( numberOfBitsUsed - 1 ) & 7 ) + 1 ); + else + stop = 0; + + for ( BitSize_t counter2 = 7; counter2 >= stop; counter2-- ) + { + if ( ( data[ counter ] >> counter2 ) & 1 ) + out[strIndex++]='1'; + else + out[strIndex++]='0'; + + if (counter2==0) + break; + } + + out[strIndex++]=' '; + } + + out[strIndex++]='\n'; + + out[strIndex++]=0; +} +void BitStream::PrintBits( void ) const +{ + char out[2048]; + PrintBits(out); + RAKNET_DEBUG_PRINTF("%s", out); +} +void BitStream::PrintHex( char *out ) const +{ + BitSize_t i; + for ( i=0; i < GetNumberOfBytesUsed(); i++) + { + sprintf(out+i*3, "%02x ", data[i]); + } +} +void BitStream::PrintHex( void ) const +{ + char out[2048]; + PrintHex(out); + RAKNET_DEBUG_PRINTF("%s", out); +} + +// Exposes the data for you to look at, like PrintBits does. +// Data will point to the stream. Returns the length in bits of the stream. +BitSize_t BitStream::CopyData( unsigned char** _data ) const +{ +#ifdef _DEBUG + RakAssert( numberOfBitsUsed > 0 ); +#endif + + *_data = (unsigned char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES( numberOfBitsUsed ), _FILE_AND_LINE_ ); + memcpy( *_data, data, sizeof(unsigned char) * (size_t) ( BITS_TO_BYTES( numberOfBitsUsed ) ) ); + return numberOfBitsUsed; +} + +// Ignore data we don't intend to read +void BitStream::IgnoreBits( const BitSize_t numberOfBits ) +{ + readOffset += numberOfBits; +} + +void BitStream::IgnoreBytes( const unsigned int numberOfBytes ) +{ + IgnoreBits(BYTES_TO_BITS(numberOfBytes)); +} + +// Move the write pointer to a position on the array. Dangerous if you don't know what you are doing! +// Doesn't work with non-aligned data! +void BitStream::SetWriteOffset( const BitSize_t offset ) +{ + numberOfBitsUsed = offset; +} + +/* +BitSize_t BitStream::GetWriteOffset( void ) const +{ +return numberOfBitsUsed; +} + +// Returns the length in bits of the stream +BitSize_t BitStream::GetNumberOfBitsUsed( void ) const +{ +return GetWriteOffset(); +} + +// Returns the length in bytes of the stream +BitSize_t BitStream::GetNumberOfBytesUsed( void ) const +{ +return BITS_TO_BYTES( numberOfBitsUsed ); +} + +// Returns the number of bits into the stream that we have read +BitSize_t BitStream::GetReadOffset( void ) const +{ +return readOffset; +} + + +// Sets the read bit index +void BitStream::SetReadOffset( const BitSize_t newReadOffset ) +{ +readOffset=newReadOffset; +} + +// Returns the number of bits left in the stream that haven't been read +BitSize_t BitStream::GetNumberOfUnreadBits( void ) const +{ +return numberOfBitsUsed - readOffset; +} +// Exposes the internal data +unsigned char* BitStream::GetData( void ) const +{ +return data; +} + +*/ +// If we used the constructor version with copy data off, this makes sure it is set to on and the data pointed to is copied. +void BitStream::AssertCopyData( void ) +{ + if ( copyData == false ) + { + copyData = true; + + if ( numberOfBitsAllocated > 0 ) + { + unsigned char * newdata = ( unsigned char* ) rakMalloc_Ex( (size_t) BITS_TO_BYTES( numberOfBitsAllocated ), _FILE_AND_LINE_ ); +#ifdef _DEBUG + + RakAssert( data ); +#endif + + memcpy( newdata, data, (size_t) BITS_TO_BYTES( numberOfBitsAllocated ) ); + data = newdata; + } + + else + data = 0; + } +} +bool BitStream::IsNetworkOrderInternal(void) +{ + + + + + + static const bool isNetworkOrder=(htonl(12345) == 12345); + return isNetworkOrder; + +} +void BitStream::ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length) +{ + for (BitSize_t i=0; i < length; i++) + inOutByteArray[i]=inByteArray[length-i-1]; +} +void BitStream::ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length) +{ + unsigned char temp; + BitSize_t i; + for (i=0; i < (length>>1); i++) + { + temp = inOutData[i]; + inOutData[i]=inOutData[length-i-1]; + inOutData[length-i-1]=temp; + } +} + +bool BitStream::Read(char *varString) +{ + return RakString::Deserialize(varString,this); +} +bool BitStream::Read(unsigned char *varString) +{ + return RakString::Deserialize((char*) varString,this); +} +void BitStream::WriteAlignedVar8(const char *inByteArray) +{ + RakAssert((numberOfBitsUsed&7)==0); + AddBitsAndReallocate(1*8); + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; + numberOfBitsUsed+=1*8; +} +bool BitStream::ReadAlignedVar8(char *inOutByteArray) +{ + RakAssert((readOffset&7)==0); + if ( readOffset + 1*8 > numberOfBitsUsed ) + return false; + + inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; + readOffset+=1*8; + return true; +} +void BitStream::WriteAlignedVar16(const char *inByteArray) +{ + RakAssert((numberOfBitsUsed&7)==0); + AddBitsAndReallocate(2*8); +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[1]; + data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[0]; + } + else +#endif + { + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; + data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1]; + } + + numberOfBitsUsed+=2*8; +} +bool BitStream::ReadAlignedVar16(char *inOutByteArray) +{ + RakAssert((readOffset&7)==0); + if ( readOffset + 2*8 > numberOfBitsUsed ) + return false; +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + inOutByteArray[0] = data[( readOffset >> 3 ) + 1]; + inOutByteArray[1] = data[( readOffset >> 3 ) + 0]; + } + else +#endif + { + inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; + inOutByteArray[1] = data[( readOffset >> 3 ) + 1]; + } + + readOffset+=2*8; + return true; +} +void BitStream::WriteAlignedVar32(const char *inByteArray) +{ + RakAssert((numberOfBitsUsed&7)==0); + AddBitsAndReallocate(4*8); +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[3]; + data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[2]; + data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[1]; + data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[0]; + } + else +#endif + { + data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0]; + data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1]; + data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[2]; + data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[3]; + } + + numberOfBitsUsed+=4*8; +} +bool BitStream::ReadAlignedVar32(char *inOutByteArray) +{ + RakAssert((readOffset&7)==0); + if ( readOffset + 4*8 > numberOfBitsUsed ) + return false; +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + inOutByteArray[0] = data[( readOffset >> 3 ) + 3]; + inOutByteArray[1] = data[( readOffset >> 3 ) + 2]; + inOutByteArray[2] = data[( readOffset >> 3 ) + 1]; + inOutByteArray[3] = data[( readOffset >> 3 ) + 0]; + } + else +#endif + { + inOutByteArray[0] = data[( readOffset >> 3 ) + 0]; + inOutByteArray[1] = data[( readOffset >> 3 ) + 1]; + inOutByteArray[2] = data[( readOffset >> 3 ) + 2]; + inOutByteArray[3] = data[( readOffset >> 3 ) + 3]; + } + + readOffset+=4*8; + return true; +} +bool BitStream::ReadFloat16( float &outFloat, float floatMin, float floatMax ) +{ + unsigned short percentile; + if (Read(percentile)) + { + RakAssert(floatMax>floatMin); + outFloat = floatMin + ((float) percentile / 65535.0f) * (floatMax-floatMin); + if (outFloatfloatMax) + outFloat=floatMax; + return true; + } + return false; +} +bool BitStream::SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax) +{ + if (writeToBitstream) + WriteFloat16(inOutFloat, floatMin, floatMax); + else + return ReadFloat16(inOutFloat, floatMin, floatMax); + return true; +} +void BitStream::WriteFloat16( float inOutFloat, float floatMin, float floatMax ) +{ + RakAssert(floatMax>floatMin); + if (inOutFloat>floatMax+.001) + { + RakAssert(inOutFloat<=floatMax+.001); + } + if (inOutFloat=floatMin-.001); + } + float percentile=65535.0f * (inOutFloat-floatMin)/(floatMax-floatMin); + if (percentile<0.0) + percentile=0.0; + if (percentile>65535.0f) + percentile=65535.0f; + Write((unsigned short)percentile); +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // #if _MSC_VER < 1299 diff --git a/src/raknet/BitStream.h b/src/raknet/BitStream.h index 4a76f6a..0614a2c 100755 --- a/src/raknet/BitStream.h +++ b/src/raknet/BitStream.h @@ -1,2032 +1,2032 @@ -/// \file BitStream.h -/// \brief This class allows you to write and read native types as a string of bits. -/// \details BitStream is used extensively throughout RakNet and is designed to be used by users as well. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// - -#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization -#include "BitStream_NoTemplate.h" -#else - -#ifndef __BITSTREAM_H -#define __BITSTREAM_H - -#include "RakMemoryOverride.h" -#include "RakNetDefines.h" -#include "Export.h" -#include "RakNetTypes.h" -#include "RakString.h" -#include "RakWString.h" -#include "RakAssert.h" -#include -#include - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -// MSWin uses _copysign, others use copysign... -#ifndef _WIN32 -#define _copysign copysign -#endif - -namespace RakNet -{ - /// This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. - /// \sa BitStreamSample.txt - class RAK_DLL_EXPORT BitStream - { - - public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(BitStream) - - /// Default Constructor - BitStream(); - - /// \brief Create the bitstream, with some number of bytes to immediately allocate. - /// \details There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE. - /// In that case all it does is save you one or more realloc calls. - /// \param[in] initialBytesToAllocate the number of bytes to pre-allocate. - BitStream( const unsigned int initialBytesToAllocate ); - - /// \brief Initialize the BitStream, immediately setting the data it contains to a predefined pointer. - /// \details Set \a _copyData to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data. - /// You shouldn't call Write functions with \a _copyData as false, as this will write to unallocated memory - /// 99% of the time you will use this function to cast Packet::data to a bitstream for reading, in which case you should write something as follows: - /// \code - /// RakNet::BitStream bs(packet->data, packet->length, false); - /// \endcode - /// \param[in] _data An array of bytes. - /// \param[in] lengthInBytes Size of the \a _data. - /// \param[in] _copyData true or false to make a copy of \a _data or not. - BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData ); - - // Destructor - ~BitStream(); - - /// Resets the bitstream for reuse. - void Reset( void ); - - /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutTemplateVar The value to write - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool Serialize(bool writeToBitstream, templateType &inOutTemplateVar); - - /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutCurrentValue The current value to write - /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue); - - /// \brief Bidirectional version of SerializeDelta when you don't know what the last value is, or there is no last value. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutCurrentValue The current value to write - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue); - - /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutTemplateVar The value to write - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar); - - /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutCurrentValue The current value to write - /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template - bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue); - - /// \brief Save as SerializeCompressedDelta(templateType ¤tValue, const templateType &lastValue) when we have an unknown second parameter - /// \return true on data read. False on insufficient data in bitstream - template - bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutTemplateVar); - - /// \brief Bidirectional serialize/deserialize an array or casted stream or raw data. This does NOT do endian swapping. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutByteArray a byte buffer - /// \param[in] numberOfBytes the size of \a input in bytes - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - bool Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes ); - - /// \brief Serialize a float into 2 bytes, spanning the range between \a floatMin and \a floatMax - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutFloat The float to write - /// \param[in] floatMin Predetermined minimum value of f - /// \param[in] floatMax Predetermined maximum value of f - bool SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax); - - /// Serialize one type casted to another (smaller) type, to save bandwidth - /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t - /// Example: int num=53; SerializeCasted(true, num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes) - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] value The value to serialize - template - bool SerializeCasted( bool writeToBitstream, sourceType &value ); - - /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range - /// Then serialize only those bits - /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum - /// \param[in] minimum Minimum value of \a value - /// \param[in] maximum Maximum value of \a value - /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates - template - bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); - /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum - template - bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); - - /// \brief Bidirectional serialize/deserialize a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. - /// \details Will further compress y or z axis aligned vectors. - /// Accurate to 1/32767.5. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template // templateType for this function must be a float or double - bool SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ); - - /// \brief Bidirectional serialize/deserialize a vector, using 10 bytes instead of 12. - /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template // templateType for this function must be a float or double - bool SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ); - - /// \brief Bidirectional serialize/deserialize a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] w w - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - template // templateType for this function must be a float or double - bool SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z); - - /// \brief Bidirectional serialize/deserialize an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each. - /// \details Use 6 bytes instead of 36 - /// Lossy, although the result is renormalized - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool SerializeOrthMatrix( - bool writeToBitstream, - templateType &m00, templateType &m01, templateType &m02, - templateType &m10, templateType &m11, templateType &m12, - templateType &m20, templateType &m21, templateType &m22 ); - - /// \brief Bidirectional serialize/deserialize numberToSerialize bits to/from the input. - /// \details Right aligned data means in the case of a partial byte, the bits are aligned - /// from the right (bit 0) rather than the left (as in the normal - /// internal representation) You would set this to true when - /// writing user data, and false when copying bitstream data, such - /// as writing one bitstream to another - /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] inOutByteArray The data - /// \param[in] numberOfBitsToSerialize The number of bits to write - /// \param[in] rightAlignedBits if true data will be right aligned - /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - bool SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits = true ); - - /// \brief Write any integral type to a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] inTemplateVar The value to write - template - void Write(const templateType &inTemplateVar); - - /// \brief Write the dereferenced pointer to any integral type to a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] inTemplateVar The value to write - template - void WritePtr(templateType *inTemplateVar); - - /// \brief Write any integral type to a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template - void WriteDelta(const templateType ¤tValue, const templateType &lastValue); - - /// \brief WriteDelta when you don't know what the last value is, or there is no last value. - /// \param[in] currentValue The current value to write - template - void WriteDelta(const templateType ¤tValue); - - /// \brief Write any integral type to a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// \param[in] inTemplateVar The value to write - template - void WriteCompressed(const templateType &inTemplateVar); - - /// \brief Write any integral type to a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template - void WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue); - - /// \brief Save as WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) when we have an unknown second parameter - template - void WriteCompressedDelta(const templateType ¤tValue); - - /// \brief Read any integral type from a bitstream. - /// \details Define __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] outTemplateVar The value to read - /// \return true on success, false on failure. - template - bool Read(templateType &outTemplateVar); - - /// \brief Read any integral type from a bitstream. - /// \details If the written value differed from the value compared against in the write function, - /// var will be updated. Otherwise it will retain the current value. - /// ReadDelta is only valid from a previous call to WriteDelta - /// \param[in] outTemplateVar The value to read - /// \return true on success, false on failure. - template - bool ReadDelta(templateType &outTemplateVar); - - /// \brief Read any integral type from a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] outTemplateVar The value to read - /// \return true on success, false on failure. - template - bool ReadCompressed(templateType &outTemplateVar); - - /// \brief Read any integral type from a bitstream. - /// \details If the written value differed from the value compared against in the write function, - /// var will be updated. Otherwise it will retain the current value. - /// the current value will be updated. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// ReadCompressedDelta is only valid from a previous call to WriteDelta - /// \param[in] outTemplateVar The value to read - /// \return true on success, false on failure. - template - bool ReadCompressedDelta(templateType &outTemplateVar); - - /// \brief Read one bitstream to another. - /// \param[in] numberOfBits bits to read - /// \param bitStream the bitstream to read into from - /// \return true on success, false on failure. - bool Read( BitStream *bitStream, BitSize_t numberOfBits ); - bool Read( BitStream *bitStream ); - bool Read( BitStream &bitStream, BitSize_t numberOfBits ); - bool Read( BitStream &bitStream ); - - /// \brief Write an array or casted stream or raw data. This does NOT do endian swapping. - /// \param[in] inputByteArray a byte buffer - /// \param[in] numberOfBytes the size of \a input in bytes - void Write( const char* inputByteArray, const unsigned int numberOfBytes ); - - /// \brief Write one bitstream to another. - /// \param[in] numberOfBits bits to write - /// \param bitStream the bitstream to copy from - void Write( BitStream *bitStream, BitSize_t numberOfBits ); - void Write( BitStream *bitStream ); - void Write( BitStream &bitStream, BitSize_t numberOfBits ); - void Write( BitStream &bitStream );\ - - /// \brief Write a float into 2 bytes, spanning the range between \a floatMin and \a floatMax - /// \param[in] x The float to write - /// \param[in] floatMin Predetermined minimum value of f - /// \param[in] floatMax Predetermined maximum value of f - void WriteFloat16( float x, float floatMin, float floatMax ); - - /// Write one type serialized as another (smaller) type, to save bandwidth - /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t - /// Example: int num=53; WriteCasted(num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes) - /// \param[in] value The value to write - template - void WriteCasted( const sourceType &value ); - - /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range - /// Then write only those bits - /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program - /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum - /// \param[in] minimum Minimum value of \a value - /// \param[in] maximum Maximum value of \a value - /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Read(). - template - void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); - /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum - template - void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); - - /// \brief Write a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. - /// \details Will further compress y or z axis aligned vectors. - /// Accurate to 1/32767.5. - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - template // templateType for this function must be a float or double - void WriteNormVector( templateType x, templateType y, templateType z ); - - /// \brief Write a vector, using 10 bytes instead of 12. - /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, - /// so only use if accuracy is not important. - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - template // templateType for this function must be a float or double - void WriteVector( templateType x, templateType y, templateType z ); - - /// \brief Write a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. - /// \param[in] w w - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - template // templateType for this function must be a float or double - void WriteNormQuat( templateType w, templateType x, templateType y, templateType z); - - /// \brief Write an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each. - /// \details Use 6 bytes instead of 36 - /// Lossy, although the result is renormalized - template // templateType for this function must be a float or double - void WriteOrthMatrix( - templateType m00, templateType m01, templateType m02, - templateType m10, templateType m11, templateType m12, - templateType m20, templateType m21, templateType m22 ); - - /// \brief Read an array or casted stream of byte. - /// \details The array is raw data. There is no automatic endian conversion with this function - /// \param[in] output The result byte array. It should be larger than @em numberOfBytes. - /// \param[in] numberOfBytes The number of byte to read - /// \return true on success false if there is some missing bytes. - bool Read( char* output, const unsigned int numberOfBytes ); - - /// \brief Read a float into 2 bytes, spanning the range between \a floatMin and \a floatMax - /// \param[in] outFloat The float to read - /// \param[in] floatMin Predetermined minimum value of f - /// \param[in] floatMax Predetermined maximum value of f - bool ReadFloat16( float &outFloat, float floatMin, float floatMax ); - - /// Read one type serialized to another (smaller) type, to save bandwidth - /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t - /// Example: int num; ReadCasted(num); would read 1 bytefrom the stream, and put the value in an integer - /// \param[in] value The value to write - template - bool ReadCasted( sourceType &value ); - - /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range - /// Then read only those bits - /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program - /// \param[in] value Integer value to read, which should be between \a minimum and \a maximum - /// \param[in] minimum Minimum value of \a value - /// \param[in] maximum Maximum value of \a value - /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Write(). - template - bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); - /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum - template - bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); - - /// \brief Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. - /// \details Will further compress y or z axis aligned vectors. - /// Accurate to 1/32767.5. - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool ReadNormVector( templateType &x, templateType &y, templateType &z ); - - /// \brief Read 3 floats or doubles, using 10 bytes, where those float or doubles comprise a vector. - /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, - /// so only use if accuracy is not important. - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool ReadVector( templateType &x, templateType &y, templateType &z ); - - /// \brief Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. - /// \param[in] w w - /// \param[in] x x - /// \param[in] y y - /// \param[in] z z - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z); - - /// \brief Read an orthogonal matrix from a quaternion, reading 3 components of the quaternion in 2 bytes each and extrapolatig the 4th. - /// \details Use 6 bytes instead of 36 - /// Lossy, although the result is renormalized - /// \return true on success, false on failure. - template // templateType for this function must be a float or double - bool ReadOrthMatrix( - templateType &m00, templateType &m01, templateType &m02, - templateType &m10, templateType &m11, templateType &m12, - templateType &m20, templateType &m21, templateType &m22 ); - - /// \brief Sets the read pointer back to the beginning of your data. - void ResetReadPointer( void ); - - /// \brief Sets the write pointer back to the beginning of your data. - void ResetWritePointer( void ); - - /// \brief This is good to call when you are done with the stream to make - /// sure you didn't leave any data left over void - void AssertStreamEmpty( void ); - - /// \brief RAKNET_DEBUG_PRINTF the bits in the stream. Great for debugging. - void PrintBits( char *out ) const; - void PrintBits( void ) const; - void PrintHex( char *out ) const; - void PrintHex( void ) const; - - /// \brief Ignore data we don't intend to read - /// \param[in] numberOfBits The number of bits to ignore - void IgnoreBits( const BitSize_t numberOfBits ); - - /// \brief Ignore data we don't intend to read - /// \param[in] numberOfBits The number of bytes to ignore - void IgnoreBytes( const unsigned int numberOfBytes ); - - /// \brief Move the write pointer to a position on the array. - /// \param[in] offset the offset from the start of the array. - /// \attention - /// \details Dangerous if you don't know what you are doing! - /// For efficiency reasons you can only write mid-stream if your data is byte aligned. - void SetWriteOffset( const BitSize_t offset ); - - /// \brief Returns the length in bits of the stream - inline BitSize_t GetNumberOfBitsUsed( void ) const {return GetWriteOffset();} - inline BitSize_t GetWriteOffset( void ) const {return numberOfBitsUsed;} - - /// \brief Returns the length in bytes of the stream - inline BitSize_t GetNumberOfBytesUsed( void ) const {return BITS_TO_BYTES( numberOfBitsUsed );} - - /// \brief Returns the number of bits into the stream that we have read - inline BitSize_t GetReadOffset( void ) const {return readOffset;} - - /// \brief Sets the read bit index - void SetReadOffset( const BitSize_t newReadOffset ) {readOffset=newReadOffset;} - - /// \brief Returns the number of bits left in the stream that haven't been read - inline BitSize_t GetNumberOfUnreadBits( void ) const {return numberOfBitsUsed - readOffset;} - - /// \brief Makes a copy of the internal data for you \a _data will point to - /// the stream. Partial bytes are left aligned. - /// \param[out] _data The allocated copy of GetData() - /// \return The length in bits of the stream. - BitSize_t CopyData( unsigned char** _data ) const; - - /// \internal - /// Set the stream to some initial data. - void SetData( unsigned char *inByteArray ); - - /// Gets the data that BitStream is writing to / reading from. - /// Partial bytes are left aligned. - /// \return A pointer to the internal state - inline unsigned char* GetData( void ) const {return data;} - - /// \brief Write numberToWrite bits from the input source. - /// \details Right aligned data means in the case of a partial byte, the bits are aligned - /// from the right (bit 0) rather than the left (as in the normal - /// internal representation) You would set this to true when - /// writing user data, and false when copying bitstream data, such - /// as writing one bitstream to another. - /// \param[in] inByteArray The data - /// \param[in] numberOfBitsToWrite The number of bits to write - /// \param[in] rightAlignedBits if true data will be right aligned - void WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits = true ); - - /// \brief Align the bitstream to the byte boundary and then write the - /// specified number of bits. - /// \details This is faster than WriteBits but - /// wastes the bits to do the alignment and requires you to call - /// ReadAlignedBits at the corresponding read position. - /// \param[in] inByteArray The data - /// \param[in] numberOfBytesToWrite The size of input. - void WriteAlignedBytes( const unsigned char *inByteArray, const unsigned int numberOfBytesToWrite ); - - // Endian swap bytes already in the bitstream - void EndianSwapBytes( int byteOffset, int length ); - - /// \brief Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite - /// \param[in] inByteArray The data - /// \param[in] inputLength The size of input. - /// \param[in] maxBytesToWrite Max bytes to write - void WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite ); - - /// \brief Read bits, starting at the next aligned bits. - /// \details Note that the modulus 8 starting offset of the sequence must be the same as - /// was used with WriteBits. This will be a problem with packet - /// coalescence unless you byte align the coalesced packets. - /// \param[in] inOutByteArray The byte array larger than @em numberOfBytesToRead - /// \param[in] numberOfBytesToRead The number of byte to read from the internal state - /// \return true if there is enough byte. - bool ReadAlignedBytes( unsigned char *inOutByteArray, const unsigned int numberOfBytesToRead ); - - /// \brief Reads what was written by WriteAlignedBytesSafe. - /// \param[in] inOutByteArray The data - /// \param[in] maxBytesToRead Maximum number of bytes to read - /// \return true on success, false on failure. - bool ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead ); - bool ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ); - - /// \brief Same as ReadAlignedBytesSafe() but allocates the memory for you using new, rather than assuming it is safe to write to - /// \param[in] outByteArray outByteArray will be deleted if it is not a pointer to 0 - /// \return true on success, false on failure. - bool ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead ); - bool ReadAlignedBytesSafeAlloc( char **outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ); - - /// \brief Align the next write and/or read to a byte boundary. - /// \details This can be used to 'waste' bits to byte align for efficiency reasons It - /// can also be used to force coalesced bitstreams to start on byte - /// boundaries so so WriteAlignedBits and ReadAlignedBits both - /// calculate the same offset when aligning. - inline void AlignWriteToByteBoundary( void ) {numberOfBitsUsed += 8 - ( (( numberOfBitsUsed - 1 ) & 7) + 1 );} - - /// \brief Align the next write and/or read to a byte boundary. - /// \details This can be used to 'waste' bits to byte align for efficiency reasons It - /// can also be used to force coalesced bitstreams to start on byte - /// boundaries so so WriteAlignedBits and ReadAlignedBits both - /// calculate the same offset when aligning. - inline void AlignReadToByteBoundary( void ) {readOffset += 8 - ( (( readOffset - 1 ) & 7 ) + 1 );} - - /// \brief Read \a numberOfBitsToRead bits to the output source. - /// \details alignBitsToRight should be set to true to convert internal - /// bitstream data to userdata. It should be false if you used - /// WriteBits with rightAlignedBits false - /// \param[in] inOutByteArray The resulting bits array - /// \param[in] numberOfBitsToRead The number of bits to read - /// \param[in] alignBitsToRight if true bits will be right aligned. - /// \return true if there is enough bits to read - bool ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight = true ); - - /// \brief Write a 0 - void Write0( void ); - - /// \brief Write a 1 - void Write1( void ); - - /// \brief Reads 1 bit and returns true if that bit is 1 and false if it is 0. - bool ReadBit( void ); - - /// \brief If we used the constructor version with copy data off, this - /// *makes sure it is set to on and the data pointed to is copied. - void AssertCopyData( void ); - - /// \brief Use this if you pass a pointer copy to the constructor - /// *(_copyData==false) and want to overallocate to prevent - /// reallocation. - void SetNumberOfBitsAllocated( const BitSize_t lengthInBits ); - - /// \brief Reallocates (if necessary) in preparation of writing numberOfBitsToWrite - void AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite ); - - /// \internal - /// \return How many bits have been allocated internally - BitSize_t GetNumberOfBitsAllocated(void) const; - - /// \brief Read strings, non reference. - bool Read(char *varString); - bool Read(unsigned char *varString); - - /// Write zeros until the bitstream is filled up to \a bytes - void PadWithZeroToByteLength( unsigned int bytes ); - - /// Get the number of leading zeros for a number - /// \param[in] x Number to test - static int NumberOfLeadingZeroes( uint8_t x ); - static int NumberOfLeadingZeroes( uint16_t x ); - static int NumberOfLeadingZeroes( uint32_t x ); - static int NumberOfLeadingZeroes( uint64_t x ); - static int NumberOfLeadingZeroes( int8_t x ); - static int NumberOfLeadingZeroes( int16_t x ); - static int NumberOfLeadingZeroes( int32_t x ); - static int NumberOfLeadingZeroes( int64_t x ); - - /// \internal Unrolled inner loop, for when performance is critical - void WriteAlignedVar8(const char *inByteArray); - /// \internal Unrolled inner loop, for when performance is critical - bool ReadAlignedVar8(char *inOutByteArray); - /// \internal Unrolled inner loop, for when performance is critical - void WriteAlignedVar16(const char *inByteArray); - /// \internal Unrolled inner loop, for when performance is critical - bool ReadAlignedVar16(char *inOutByteArray); - /// \internal Unrolled inner loop, for when performance is critical - void WriteAlignedVar32(const char *inByteArray); - /// \internal Unrolled inner loop, for when performance is critical - bool ReadAlignedVar32(char *inOutByteArray); - - inline void Write(const char * const inStringVar) - { - RakString::Serialize(inStringVar, this); - } - inline void Write(const wchar_t * const inStringVar) - { - RakWString::Serialize(inStringVar, this); - } - inline void Write(const unsigned char * const inTemplateVar) - { - Write((const char*)inTemplateVar); - } - inline void Write(char * const inTemplateVar) - { - Write((const char*)inTemplateVar); - } - inline void Write(unsigned char * const inTemplateVar) - { - Write((const char*)inTemplateVar); - } - inline void WriteCompressed(const char * const inStringVar) - { - RakString::SerializeCompressed(inStringVar,this,0,false); - } - inline void WriteCompressed(const wchar_t * const inStringVar) - { - RakWString::Serialize(inStringVar,this); - } - inline void WriteCompressed(const unsigned char * const inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - inline void WriteCompressed(char * const inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - inline void WriteCompressed(unsigned char * const inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - - /// ---- Member function template specialization declarations ---- - // Used for VC7 -#if defined(_MSC_VER) && _MSC_VER == 1300 - /// Write a bool to a bitstream. - /// \param[in] var The value to write - template <> - void Write(const bool &var); - - /// Write a systemAddress to a bitstream - /// \param[in] var The value to write - template <> - void Write(const SystemAddress &var); - - /// Write a uint24_t to a bitstream - /// \param[in] var The value to write - template <> - void Write(const uint24_t &var); - - /// Write a RakNetGUID to a bitsteam - /// \param[in] var The value to write - template <> - void Write(const RakNetGuid &var); - - /// Write a string to a bitstream - /// \param[in] var The value to write - template <> - void Write(const char* const &var); - template <> - void Write(const unsigned char* const &var); - template <> - void Write(char* const &var); - template <> - void Write(unsigned char* const &var); - template <> - void Write(const RakString &var); - template <> - void Write(const RakWString &var); - - /// \brief Write a systemAddress. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - void WriteDelta(const SystemAddress ¤tValue, const SystemAddress &lastValue); - - template <> - void WriteDelta(const uint24_t ¤tValue, const uint24_t &lastValue); - - template <> - void WriteDelta(const RakNetGUID ¤tValue, const RakNetGUID &lastValue); - - /// \brief Write a bool delta. - /// \details Same thing as just calling Write - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - void WriteDelta(const bool ¤tValue, const bool &lastValue); - - template <> - void WriteCompressed(const SystemAddress &var); - - template <> - void WriteCompressed(const uint24_t &var); - - template <> - void WriteCompressed(const RakNetGUID &var); - - template <> - void WriteCompressed(const bool &var); - - /// For values between -1 and 1 - template <> - void WriteCompressed(const float &var); - - /// For values between -1 and 1 - template <> - void WriteCompressed(const double &var); - - /// Compressed string - template <> - void WriteCompressed(const char* var); - template <> - void WriteCompressed(const unsigned char* var); - template <> - void WriteCompressed(char* var); - template <> - void WriteCompressed(unsigned char* var); - template <> - void WriteCompressed(const RakString &var); - template <> - void WriteCompressed(const RakWString &var); - - /// \brief Write a bool delta. - /// \details Same thing as just calling Write - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - void WriteCompressedDelta(const bool ¤tValue, const bool &lastValue); - - /// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue) - /// when we have an unknown second bool - template <> - void WriteCompressedDelta(const bool ¤tValue); - - /// \brief Read a bool from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool Read(bool &var); - - /// \brief Read a systemAddress from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool Read(SystemAddress &var); - - template <> - bool Read(uint24_t &var); - - template <> - bool Read(RakNetGUID &var); - - /// \brief Read a String from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool Read(char *&var); - template <> - bool Read(wchar_t *&var); - template <> - bool Read(unsigned char *&var); - template <> - bool Read(RakString &var); - template <> - bool Read(RakWString &var); - - /// \brief Read a bool from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool ReadDelta(bool &var); - - template <> - bool ReadCompressed(SystemAddress &var); - - template <> - bool ReadCompressed(uint24_t &var); - - template <> - bool ReadCompressed(RakNetGUID &var); - - template <> - bool ReadCompressed(bool &var); - - template <> - bool ReadCompressed(float &var); - - /// For values between -1 and 1 - /// \return true on success, false on failure. - template <> - bool ReadCompressed(double &var); - - template <> - bool ReadCompressed(char* &var); - template <> - bool ReadCompressed(wchar_t* &var); - template <> - bool ReadCompressed(unsigned char *&var); - template <> - bool ReadCompressed(RakString &var); - template <> - bool ReadCompressed(RakWString &var); - - /// \brief Read a bool from a bitstream. - /// \param[in] var The value to read - /// \return true on success, false on failure. - template <> - bool ReadCompressedDelta(bool &var); -#endif - - inline static bool DoEndianSwap(void) { -#ifndef __BITSTREAM_NATIVE_END - return IsNetworkOrder()==false; -#else - return false; -#endif - } - inline static bool IsBigEndian(void) - { - return IsNetworkOrder(); - } - inline static bool IsNetworkOrder(void) {static const bool r = IsNetworkOrderInternal(); return r;} - // Not inline, won't compile on PC due to winsock include errors - static bool IsNetworkOrderInternal(void); - static void ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length); - static void ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length); - - private: - - BitStream( const BitStream &invalid) { - (void) invalid; - RakAssert(0); - } - - /// \brief Assume the input source points to a native type, compress and write it. - void WriteCompressed( const unsigned char* inByteArray, const unsigned int size, const bool unsignedData ); - - /// \brief Assume the input source points to a compressed native type. Decompress and read it. - bool ReadCompressed( unsigned char* inOutByteArray, const unsigned int size, const bool unsignedData ); - - - BitSize_t numberOfBitsUsed; - - BitSize_t numberOfBitsAllocated; - - BitSize_t readOffset; - - unsigned char *data; - - /// true if the internal buffer is copy of the data passed to the constructor - bool copyData; - - /// BitStreams that use less than BITSTREAM_STACK_ALLOCATION_SIZE use the stack, rather than the heap to store data. It switches over if BITSTREAM_STACK_ALLOCATION_SIZE is exceeded - unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE]; - }; - - template - inline bool BitStream::Serialize(bool writeToBitstream, templateType &inOutTemplateVar) - { - if (writeToBitstream) - Write(inOutTemplateVar); - else - return Read(inOutTemplateVar); - return true; - } - - template - inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue) - { - if (writeToBitstream) - WriteDelta(inOutCurrentValue, lastValue); - else - return ReadDelta(inOutCurrentValue); - return true; - } - - template - inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue) - { - if (writeToBitstream) - WriteDelta(inOutCurrentValue); - else - return ReadDelta(inOutCurrentValue); - return true; - } - - template - inline bool BitStream::SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar) - { - if (writeToBitstream) - WriteCompressed(inOutTemplateVar); - else - return ReadCompressed(inOutTemplateVar); - return true; - } - - template - inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue) - { - if (writeToBitstream) - WriteCompressedDelta(inOutCurrentValue,lastValue); - else - return ReadCompressedDelta(inOutCurrentValue); - return true; - } -//Stoppedhere - template - inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue) - { - if (writeToBitstream) - WriteCompressedDelta(inOutCurrentValue); - else - return ReadCompressedDelta(inOutCurrentValue); - return true; - } - - inline bool BitStream::Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes ) - { - if (writeToBitstream) - Write(inOutByteArray, numberOfBytes); - else - return Read(inOutByteArray, numberOfBytes); - return true; - } - - template - bool BitStream::SerializeCasted( bool writeToBitstream, sourceType &value ) - { - if (writeToBitstream) WriteCasted(value); - else return ReadCasted(value); - return true; - } - - template - bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange ) - { - int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); - return SerializeBitsFromIntegerRange(writeToBitstream,value,minimum,maximum,requiredBits,allowOutsideRange); - } - template - bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange ) - { - if (writeToBitstream) WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); - else return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); - return true; - } - - template - inline bool BitStream::SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ) - { - if (writeToBitstream) - WriteNormVector(x,y,z); - else - return ReadNormVector(x,y,z); - return true; - } - - template - inline bool BitStream::SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ) - { - if (writeToBitstream) - WriteVector(x,y,z); - else - return ReadVector(x,y,z); - return true; - } - - template - inline bool BitStream::SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z) - { - if (writeToBitstream) - WriteNormQuat(w,x,y,z); - else - return ReadNormQuat(w,x,y,z); - return true; - } - - template - inline bool BitStream::SerializeOrthMatrix( - bool writeToBitstream, - templateType &m00, templateType &m01, templateType &m02, - templateType &m10, templateType &m11, templateType &m12, - templateType &m20, templateType &m21, templateType &m22 ) - { - if (writeToBitstream) - WriteOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); - else - return ReadOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); - return true; - } - - inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits ) - { - if (writeToBitstream) - WriteBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits); - else - return ReadBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits); - return true; - } - - template - inline void BitStream::Write(const templateType &inTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(inTemplateVar)==1) - WriteBits( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType)); - WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ); - } - else -#endif - WriteBits( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true ); - } - } - - template - inline void BitStream::WritePtr(templateType *inTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(templateType)==1) - WriteBits( ( unsigned char* ) inTemplateVar, sizeof( templateType ) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - ReverseBytes((unsigned char*) inTemplateVar, output, sizeof(templateType)); - WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ); - } - else -#endif - WriteBits( ( unsigned char* ) inTemplateVar, sizeof(templateType) * 8, true ); - } - } - - /// \brief Write a bool to a bitstream. - /// \param[in] inTemplateVar The value to write - template <> - inline void BitStream::Write(const bool &inTemplateVar) - { - if ( inTemplateVar ) - Write1(); - else - Write0(); - } - - - /// \brief Write a systemAddress to a bitstream. - /// \param[in] inTemplateVar The value to write - template <> - inline void BitStream::Write(const SystemAddress &inTemplateVar) - { - Write(inTemplateVar.GetIPVersion()); - if (inTemplateVar.GetIPVersion()==4) - { - // Hide the address so routers don't modify it - SystemAddress var2=inTemplateVar; - uint32_t binaryAddress=~inTemplateVar.address.addr4.sin_addr.s_addr; - // Don't endian swap the address or port - WriteBits((unsigned char*)&binaryAddress, sizeof(binaryAddress)*8, true); - unsigned short p = var2.GetPortNetworkOrder(); - WriteBits((unsigned char*)&p, sizeof(unsigned short)*8, true); - } - else - { -#if RAKNET_SUPPORT_IPV6==1 - // Don't endian swap - WriteBits((const unsigned char*) &inTemplateVar.address.addr6, sizeof(inTemplateVar.address.addr6)*8, true); -#endif - } - } - - template <> - inline void BitStream::Write(const uint24_t &inTemplateVar) - { - AlignWriteToByteBoundary(); - AddBitsAndReallocate(3*8); - - if (IsBigEndian()==false) - { - data[( numberOfBitsUsed >> 3 ) + 0] = ((char *)&inTemplateVar.val)[0]; - data[( numberOfBitsUsed >> 3 ) + 1] = ((char *)&inTemplateVar.val)[1]; - data[( numberOfBitsUsed >> 3 ) + 2] = ((char *)&inTemplateVar.val)[2]; - } - else - { - data[( numberOfBitsUsed >> 3 ) + 0] = ((char *)&inTemplateVar.val)[3]; - data[( numberOfBitsUsed >> 3 ) + 1] = ((char *)&inTemplateVar.val)[2]; - data[( numberOfBitsUsed >> 3 ) + 2] = ((char *)&inTemplateVar.val)[1]; - } - - numberOfBitsUsed+=3*8; - } - - template <> - inline void BitStream::Write(const RakNetGUID &inTemplateVar) - { - Write(inTemplateVar.g); - } - - /// \brief Write a string to a bitstream. - /// \param[in] var The value to write - template <> - inline void BitStream::Write(const RakString &inTemplateVar) - { - inTemplateVar.Serialize(this); - } - template <> - inline void BitStream::Write(const RakWString &inTemplateVar) - { - inTemplateVar.Serialize(this); - } - template <> - inline void BitStream::Write(const char * const &inStringVar) - { - RakString::Serialize(inStringVar, this); - } - template <> - inline void BitStream::Write(const wchar_t * const &inStringVar) - { - RakWString::Serialize(inStringVar, this); - } - template <> - inline void BitStream::Write(const unsigned char * const &inTemplateVar) - { - Write((const char*)inTemplateVar); - } - template <> - inline void BitStream::Write(char * const &inTemplateVar) - { - Write((const char*)inTemplateVar); - } - template <> - inline void BitStream::Write(unsigned char * const &inTemplateVar) - { - Write((const char*)inTemplateVar); - } - - /// \brief Write any integral type to a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template - inline void BitStream::WriteDelta(const templateType ¤tValue, const templateType &lastValue) - { - if (currentValue==lastValue) - { - Write(false); - } - else - { - Write(true); - Write(currentValue); - } - } - - /// \brief Write a bool delta. Same thing as just calling Write - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - inline void BitStream::WriteDelta(const bool ¤tValue, const bool &lastValue) - { - (void) lastValue; - - Write(currentValue); - } - - /// \brief WriteDelta when you don't know what the last value is, or there is no last value. - /// \param[in] currentValue The current value to write - template - inline void BitStream::WriteDelta(const templateType ¤tValue) - { - Write(true); - Write(currentValue); - } - - /// \brief Write any integral type to a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] inTemplateVar The value to write - template - inline void BitStream::WriteCompressed(const templateType &inTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(inTemplateVar)==1) - WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END -#ifdef _MSC_VER -#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data -#endif - - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType)); - WriteCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true ); - } - else -#endif - WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true ); - } - } - - template <> - inline void BitStream::WriteCompressed(const SystemAddress &inTemplateVar) - { - Write(inTemplateVar); - } - - template <> - inline void BitStream::WriteCompressed(const RakNetGUID &inTemplateVar) - { - Write(inTemplateVar); - } - - template <> - inline void BitStream::WriteCompressed(const uint24_t &var) - { - Write(var); - } - - template <> - inline void BitStream::WriteCompressed(const bool &inTemplateVar) - { - Write(inTemplateVar); - } - - /// For values between -1 and 1 - template <> - inline void BitStream::WriteCompressed(const float &inTemplateVar) - { - RakAssert(inTemplateVar > -1.01f && inTemplateVar < 1.01f); - float varCopy=inTemplateVar; - if (varCopy < -1.0f) - varCopy=-1.0f; - if (varCopy > 1.0f) - varCopy=1.0f; - Write((unsigned short)((varCopy+1.0f)*32767.5f)); - } - - /// For values between -1 and 1 - template <> - inline void BitStream::WriteCompressed(const double &inTemplateVar) - { - RakAssert(inTemplateVar > -1.01 && inTemplateVar < 1.01); - double varCopy=inTemplateVar; - if (varCopy < -1.0f) - varCopy=-1.0f; - if (varCopy > 1.0f) - varCopy=1.0f; - Write((uint32_t)((varCopy+1.0)*2147483648.0)); - } - - /// Compress the string - template <> - inline void BitStream::WriteCompressed(const RakString &inTemplateVar) - { - inTemplateVar.SerializeCompressed(this,0,false); - } - template <> - inline void BitStream::WriteCompressed(const RakWString &inTemplateVar) - { - inTemplateVar.Serialize(this); - } - template <> - inline void BitStream::WriteCompressed(const char * const &inStringVar) - { - RakString::SerializeCompressed(inStringVar,this,0,false); - } - template <> - inline void BitStream::WriteCompressed(const wchar_t * const &inStringVar) - { - RakWString::Serialize(inStringVar,this); - } - template <> - inline void BitStream::WriteCompressed(const unsigned char * const &inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - template <> - inline void BitStream::WriteCompressed(char * const &inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - template <> - inline void BitStream::WriteCompressed(unsigned char * const &inTemplateVar) - { - WriteCompressed((const char*) inTemplateVar); - } - - - /// \brief Write any integral type to a bitstream. - /// \details If the current value is different from the last value - /// the current value will be written. Otherwise, a single bit will be written - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template - inline void BitStream::WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) - { - if (currentValue==lastValue) - { - Write(false); - } - else - { - Write(true); - WriteCompressed(currentValue); - } - } - - /// \brief Write a bool delta. Same thing as just calling Write - /// \param[in] currentValue The current value to write - /// \param[in] lastValue The last value to compare against - template <> - inline void BitStream::WriteCompressedDelta(const bool ¤tValue, const bool &lastValue) - { - (void) lastValue; - - Write(currentValue); - } - - /// \brief Save as WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) - /// when we have an unknown second parameter - template - inline void BitStream::WriteCompressedDelta(const templateType ¤tValue) - { - Write(true); - WriteCompressed(currentValue); - } - - /// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue) - /// when we have an unknown second bool - template <> - inline void BitStream::WriteCompressedDelta(const bool ¤tValue) - { - Write(currentValue); - } - - /// \brief Read any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping. - /// \param[in] outTemplateVar The value to read - template - inline bool BitStream::Read(templateType &outTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(outTemplateVar)==1) - return ReadBits( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END -#ifdef _MSC_VER -#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data -#endif - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - if (ReadBits( ( unsigned char* ) output, sizeof(templateType) * 8, true )) - { - ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType)); - return true; - } - return false; - } - else -#endif - return ReadBits( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true ); - } - } - - /// \brief Read a bool from a bitstream. - /// \param[in] outTemplateVar The value to read - template <> - inline bool BitStream::Read(bool &outTemplateVar) - { - if ( readOffset + 1 > numberOfBitsUsed ) - return false; - - if ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) // Is it faster to just write it out here? - outTemplateVar = true; - else - outTemplateVar = false; - - // Has to be on a different line for Mac - readOffset++; - - return true; - } - - /// \brief Read a systemAddress from a bitstream. - /// \param[in] outTemplateVar The value to read - template <> - inline bool BitStream::Read(SystemAddress &outTemplateVar) - { - unsigned char ipVersion; - Read(ipVersion); - if (ipVersion==4) - { - outTemplateVar.address.addr4.sin_family=AF_INET; - // Read(var.binaryAddress); - // Don't endian swap the address or port - uint32_t binaryAddress; - ReadBits( ( unsigned char* ) & binaryAddress, sizeof(binaryAddress) * 8, true ); - // Unhide the IP address, done to prevent routers from changing it - outTemplateVar.address.addr4.sin_addr.s_addr=~binaryAddress; - bool b = ReadBits(( unsigned char* ) & outTemplateVar.address.addr4.sin_port, sizeof(outTemplateVar.address.addr4.sin_port) * 8, true); - outTemplateVar.debugPort=ntohs(outTemplateVar.address.addr4.sin_port); - return b; - } - else - { -#if RAKNET_SUPPORT_IPV6==1 - bool b = ReadBits((unsigned char*) &outTemplateVar.address.addr6, sizeof(outTemplateVar.address.addr6)*8, true); - outTemplateVar.debugPort=ntohs(outTemplateVar.address.addr6.sin6_port); - return b; -#else - return false; -#endif - } - } - - template <> - inline bool BitStream::Read(uint24_t &outTemplateVar) - { - AlignReadToByteBoundary(); - if ( readOffset + 3*8 > numberOfBitsUsed ) - return false; - - if (IsBigEndian()==false) - { - ((char *)&outTemplateVar.val)[0]=data[ (readOffset >> 3) + 0]; - ((char *)&outTemplateVar.val)[1]=data[ (readOffset >> 3) + 1]; - ((char *)&outTemplateVar.val)[2]=data[ (readOffset >> 3) + 2]; - ((char *)&outTemplateVar.val)[3]=0; - } - else - { - - ((char *)&outTemplateVar.val)[3]=data[ (readOffset >> 3) + 0]; - ((char *)&outTemplateVar.val)[2]=data[ (readOffset >> 3) + 1]; - ((char *)&outTemplateVar.val)[1]=data[ (readOffset >> 3) + 2]; - ((char *)&outTemplateVar.val)[0]=0; - } - - readOffset+=3*8; - return true; - } - - template <> - inline bool BitStream::Read(RakNetGUID &outTemplateVar) - { - return Read(outTemplateVar.g); - } - - - template <> - inline bool BitStream::Read(RakString &outTemplateVar) - { - return outTemplateVar.Deserialize(this); - } - template <> - inline bool BitStream::Read(RakWString &outTemplateVar) - { - return outTemplateVar.Deserialize(this); - } - template <> - inline bool BitStream::Read(char *&varString) - { - return RakString::Deserialize(varString,this); - } - template <> - inline bool BitStream::Read(wchar_t *&varString) - { - return RakWString::Deserialize(varString,this); - } - template <> - inline bool BitStream::Read(unsigned char *&varString) - { - return RakString::Deserialize((char*) varString,this); - } - - /// \brief Read any integral type from a bitstream. - /// \details If the written value differed from the value compared against in the write function, - /// var will be updated. Otherwise it will retain the current value. - /// ReadDelta is only valid from a previous call to WriteDelta - /// \param[in] outTemplateVar The value to read - template - inline bool BitStream::ReadDelta(templateType &outTemplateVar) - { - bool dataWritten; - bool success; - success=Read(dataWritten); - if (dataWritten) - success=Read(outTemplateVar); - return success; - } - - /// \brief Read a bool from a bitstream. - /// \param[in] outTemplateVar The value to read - template <> - inline bool BitStream::ReadDelta(bool &outTemplateVar) - { - return Read(outTemplateVar); - } - - /// \brief Read any integral type from a bitstream. - /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// \param[in] outTemplateVar The value to read - template - inline bool BitStream::ReadCompressed(templateType &outTemplateVar) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (sizeof(outTemplateVar)==1) - return ReadCompressed( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true ); - else - { -#ifndef __BITSTREAM_NATIVE_END - if (DoEndianSwap()) - { - unsigned char output[sizeof(templateType)]; - if (ReadCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true )) - { - ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType)); - return true; - } - return false; - } - else -#endif - return ReadCompressed( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true ); - } - } - - template <> - inline bool BitStream::ReadCompressed(SystemAddress &outTemplateVar) - { - return Read(outTemplateVar); - } - - template <> - inline bool BitStream::ReadCompressed(uint24_t &outTemplateVar) - { - return Read(outTemplateVar); - } - - template <> - inline bool BitStream::ReadCompressed(RakNetGUID &outTemplateVar) - { - return Read(outTemplateVar); - } - - template <> - inline bool BitStream::ReadCompressed(bool &outTemplateVar) - { - return Read(outTemplateVar); - } - - /// For values between -1 and 1 - template <> - inline bool BitStream::ReadCompressed(float &outTemplateVar) - { - unsigned short compressedFloat; - if (Read(compressedFloat)) - { - outTemplateVar = ((float)compressedFloat / 32767.5f - 1.0f); - return true; - } - return false; - } - - /// For values between -1 and 1 - template <> - inline bool BitStream::ReadCompressed(double &outTemplateVar) - { - uint32_t compressedFloat; - if (Read(compressedFloat)) - { - outTemplateVar = ((double)compressedFloat / 2147483648.0 - 1.0); - return true; - } - return false; - } - - /// For strings - template <> - inline bool BitStream::ReadCompressed(RakString &outTemplateVar) - { - return outTemplateVar.DeserializeCompressed(this,false); - } - template <> - inline bool BitStream::ReadCompressed(RakWString &outTemplateVar) - { - return outTemplateVar.Deserialize(this); - } - template <> - inline bool BitStream::ReadCompressed(char *&outTemplateVar) - { - return RakString::DeserializeCompressed(outTemplateVar,this,false); - } - template <> - inline bool BitStream::ReadCompressed(wchar_t *&outTemplateVar) - { - return RakWString::Deserialize(outTemplateVar,this); - } - template <> - inline bool BitStream::ReadCompressed(unsigned char *&outTemplateVar) - { - return RakString::DeserializeCompressed((char*) outTemplateVar,this,false); - } - - /// \brief Read any integral type from a bitstream. - /// \details If the written value differed from the value compared against in the write function, - /// var will be updated. Otherwise it will retain the current value. - /// the current value will be updated. - /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. - /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type - /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte - /// ReadCompressedDelta is only valid from a previous call to WriteDelta - /// \param[in] outTemplateVar The value to read - template - inline bool BitStream::ReadCompressedDelta(templateType &outTemplateVar) - { - bool dataWritten; - bool success; - success=Read(dataWritten); - if (dataWritten) - success=ReadCompressed(outTemplateVar); - return success; - } - - /// \brief Read a bool from a bitstream. - /// \param[in] outTemplateVar The value to read - template <> - inline bool BitStream::ReadCompressedDelta(bool &outTemplateVar) - { - return Read(outTemplateVar); - } - - template - void BitStream::WriteCasted( const sourceType &value ) - { - destinationType val = (destinationType) value; - Write(val); - } - - template - void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, bool allowOutsideRange ) - { - int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); - WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); - } - template - void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, const int requiredBits, bool allowOutsideRange ) - { - RakAssert(maximum>=minimum); - RakAssert(allowOutsideRange==true || (value>=minimum && value<=maximum)); - if (allowOutsideRange) - { - if (valuemaximum) - { - Write(true); - Write(value); - return; - } - Write(false); - } - templateType valueOffMin=value-minimum; - if (IsBigEndian()==true) - { - unsigned char output[sizeof(templateType)]; - ReverseBytes((unsigned char*)&valueOffMin, output, sizeof(templateType)); - WriteBits(output,requiredBits); - } - else - { - WriteBits((unsigned char*) &valueOffMin,requiredBits); - } - } - - template // templateType for this function must be a float or double - void BitStream::WriteNormVector( templateType x, templateType y, templateType z ) - { -#ifdef _DEBUG - RakAssert(x <= 1.01 && y <= 1.01 && z <= 1.01 && x >= -1.01 && y >= -1.01 && z >= -1.01); -#endif - - WriteFloat16((float)x,-1.0f,1.0f); - WriteFloat16((float)y,-1.0f,1.0f); - WriteFloat16((float)z,-1.0f,1.0f); - } - - template // templateType for this function must be a float or double - void BitStream::WriteVector( templateType x, templateType y, templateType z ) - { - templateType magnitude = sqrt(x * x + y * y + z * z); - Write((float)magnitude); - if (magnitude > 0.00001f) - { - WriteCompressed((float)(x/magnitude)); - WriteCompressed((float)(y/magnitude)); - WriteCompressed((float)(z/magnitude)); - // Write((unsigned short)((x/magnitude+1.0f)*32767.5f)); - // Write((unsigned short)((y/magnitude+1.0f)*32767.5f)); - // Write((unsigned short)((z/magnitude+1.0f)*32767.5f)); - } - } - - template // templateType for this function must be a float or double - void BitStream::WriteNormQuat( templateType w, templateType x, templateType y, templateType z) - { - Write((bool)(w<0.0)); - Write((bool)(x<0.0)); - Write((bool)(y<0.0)); - Write((bool)(z<0.0)); - Write((unsigned short)(fabs(x)*65535.0)); - Write((unsigned short)(fabs(y)*65535.0)); - Write((unsigned short)(fabs(z)*65535.0)); - // Leave out w and calculate it on the target - } - - template // templateType for this function must be a float or double - void BitStream::WriteOrthMatrix( - templateType m00, templateType m01, templateType m02, - templateType m10, templateType m11, templateType m12, - templateType m20, templateType m21, templateType m22 ) - { - - double qw; - double qx; - double qy; - double qz; - - // Convert matrix to quat - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ - float sum; - sum = 1 + m00 + m11 + m22; - if (sum < 0.0f) sum=0.0f; - qw = sqrt( sum ) / 2; - sum = 1 + m00 - m11 - m22; - if (sum < 0.0f) sum=0.0f; - qx = sqrt( sum ) / 2; - sum = 1 - m00 + m11 - m22; - if (sum < 0.0f) sum=0.0f; - qy = sqrt( sum ) / 2; - sum = 1 - m00 - m11 + m22; - if (sum < 0.0f) sum=0.0f; - qz = sqrt( sum ) / 2; - if (qw < 0.0) qw=0.0; - if (qx < 0.0) qx=0.0; - if (qy < 0.0) qy=0.0; - if (qz < 0.0) qz=0.0; - qx = _copysign( (double) qx, (double) (m21 - m12) ); - qy = _copysign( (double) qy, (double) (m02 - m20) ); - qz = _copysign( (double) qz, (double) (m10 - m01) ); - - WriteNormQuat(qw,qx,qy,qz); - } - - template - bool BitStream::ReadCasted( sourceType &value ) - { - serializationType val; - bool success = Read(val); - value=(sourceType) val; - return success; - } - - template - bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange ) - { - int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); - return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); - } - template - bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange ) - { - RakAssert(maximum>=minimum); - if (allowOutsideRange) - { - bool isOutsideRange; - Read(isOutsideRange); - if (isOutsideRange) - return Read(value); - } - unsigned char output[sizeof(templateType)]; - memset(output,0,sizeof(output)); - bool success = ReadBits(output,requiredBits); - if (success) - { - if (IsBigEndian()==true) - ReverseBytesInPlace(output,sizeof(output)); - memcpy(&value,output,sizeof(output)); - - value+=minimum; - } - - return success; - } - - template // templateType for this function must be a float or double - bool BitStream::ReadNormVector( templateType &x, templateType &y, templateType &z ) - { - float xIn,yIn,zIn; - ReadFloat16(xIn,-1.0f,1.0f); - ReadFloat16(yIn,-1.0f,1.0f); - ReadFloat16(zIn,-1.0f,1.0f); - x=xIn; - y=yIn; - z=zIn; - return true; - } - - template // templateType for this function must be a float or double - bool BitStream::ReadVector( templateType &x, templateType &y, templateType &z ) - { - float magnitude; - //unsigned short sx,sy,sz; - if (!Read(magnitude)) - return false; - if (magnitude>0.00001f) - { - // Read(sx); - // Read(sy); - // if (!Read(sz)) - // return false; - // x=((float)sx / 32767.5f - 1.0f) * magnitude; - // y=((float)sy / 32767.5f - 1.0f) * magnitude; - // z=((float)sz / 32767.5f - 1.0f) * magnitude; - float cx,cy,cz; - ReadCompressed(cx); - ReadCompressed(cy); - if (!ReadCompressed(cz)) - return false; - x=cx; - y=cy; - z=cz; - x*=magnitude; - y*=magnitude; - z*=magnitude; - } - else - { - x=0.0; - y=0.0; - z=0.0; - } - return true; - } - - template // templateType for this function must be a float or double - bool BitStream::ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z) - { - bool cwNeg=false, cxNeg=false, cyNeg=false, czNeg=false; - unsigned short cx,cy,cz; - Read(cwNeg); - Read(cxNeg); - Read(cyNeg); - Read(czNeg); - Read(cx); - Read(cy); - if (!Read(cz)) - return false; - - // Calculate w from x,y,z - x=(templateType)(cx/65535.0); - y=(templateType)(cy/65535.0); - z=(templateType)(cz/65535.0); - if (cxNeg) x=-x; - if (cyNeg) y=-y; - if (czNeg) z=-z; - float difference = 1.0f - x*x - y*y - z*z; - if (difference < 0.0f) - difference=0.0f; - w = (templateType)(sqrt(difference)); - if (cwNeg) - w=-w; - - return true; - } - - template // templateType for this function must be a float or double - bool BitStream::ReadOrthMatrix( - templateType &m00, templateType &m01, templateType &m02, - templateType &m10, templateType &m11, templateType &m12, - templateType &m20, templateType &m21, templateType &m22 ) - { - float qw,qx,qy,qz; - if (!ReadNormQuat(qw,qx,qy,qz)) - return false; - - // Quat to orthogonal rotation matrix - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm - double sqw = (double)qw*(double)qw; - double sqx = (double)qx*(double)qx; - double sqy = (double)qy*(double)qy; - double sqz = (double)qz*(double)qz; - m00 = (templateType)(sqx - sqy - sqz + sqw); // since sqw + sqx + sqy + sqz =1 - m11 = (templateType)(-sqx + sqy - sqz + sqw); - m22 = (templateType)(-sqx - sqy + sqz + sqw); - - double tmp1 = (double)qx*(double)qy; - double tmp2 = (double)qz*(double)qw; - m10 = (templateType)(2.0 * (tmp1 + tmp2)); - m01 = (templateType)(2.0 * (tmp1 - tmp2)); - - tmp1 = (double)qx*(double)qz; - tmp2 = (double)qy*(double)qw; - m20 =(templateType)(2.0 * (tmp1 - tmp2)); - m02 = (templateType)(2.0 * (tmp1 + tmp2)); - tmp1 = (double)qy*(double)qz; - tmp2 = (double)qx*(double)qw; - m21 = (templateType)(2.0 * (tmp1 + tmp2)); - m12 = (templateType)(2.0 * (tmp1 - tmp2)); - - return true; - } - - template - BitStream& operator<<(BitStream& out, templateType& c) - { - out.Write(c); - return out; - } - template - BitStream& operator>>(BitStream& in, templateType& c) - { - bool success = in.Read(c); - RakAssert(success); - return in; - } - -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - -#endif // VC6 +/// \file BitStream.h +/// \brief This class allows you to write and read native types as a string of bits. +/// \details BitStream is used extensively throughout RakNet and is designed to be used by users as well. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// + +#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization +#include "BitStream_NoTemplate.h" +#else + +#ifndef __BITSTREAM_H +#define __BITSTREAM_H + +#include "RakMemoryOverride.h" +#include "RakNetDefines.h" +#include "Export.h" +#include "RakNetTypes.h" +#include "RakString.h" +#include "RakWString.h" +#include "RakAssert.h" +#include +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +// MSWin uses _copysign, others use copysign... +#ifndef _WIN32 +#define _copysign copysign +#endif + +namespace RakNet +{ + /// This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. + /// \sa BitStreamSample.txt + class RAK_DLL_EXPORT BitStream + { + + public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(BitStream) + + /// Default Constructor + BitStream(); + + /// \brief Create the bitstream, with some number of bytes to immediately allocate. + /// \details There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE. + /// In that case all it does is save you one or more realloc calls. + /// \param[in] initialBytesToAllocate the number of bytes to pre-allocate. + BitStream( const unsigned int initialBytesToAllocate ); + + /// \brief Initialize the BitStream, immediately setting the data it contains to a predefined pointer. + /// \details Set \a _copyData to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data. + /// You shouldn't call Write functions with \a _copyData as false, as this will write to unallocated memory + /// 99% of the time you will use this function to cast Packet::data to a bitstream for reading, in which case you should write something as follows: + /// \code + /// RakNet::BitStream bs(packet->data, packet->length, false); + /// \endcode + /// \param[in] _data An array of bytes. + /// \param[in] lengthInBytes Size of the \a _data. + /// \param[in] _copyData true or false to make a copy of \a _data or not. + BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData ); + + // Destructor + ~BitStream(); + + /// Resets the bitstream for reuse. + void Reset( void ); + + /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutTemplateVar The value to write + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool Serialize(bool writeToBitstream, templateType &inOutTemplateVar); + + /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutCurrentValue The current value to write + /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue); + + /// \brief Bidirectional version of SerializeDelta when you don't know what the last value is, or there is no last value. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutCurrentValue The current value to write + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue); + + /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutTemplateVar The value to write + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar); + + /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutCurrentValue The current value to write + /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template + bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue); + + /// \brief Save as SerializeCompressedDelta(templateType ¤tValue, const templateType &lastValue) when we have an unknown second parameter + /// \return true on data read. False on insufficient data in bitstream + template + bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutTemplateVar); + + /// \brief Bidirectional serialize/deserialize an array or casted stream or raw data. This does NOT do endian swapping. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutByteArray a byte buffer + /// \param[in] numberOfBytes the size of \a input in bytes + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes ); + + /// \brief Serialize a float into 2 bytes, spanning the range between \a floatMin and \a floatMax + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutFloat The float to write + /// \param[in] floatMin Predetermined minimum value of f + /// \param[in] floatMax Predetermined maximum value of f + bool SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax); + + /// Serialize one type casted to another (smaller) type, to save bandwidth + /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t + /// Example: int num=53; SerializeCasted(true, num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes) + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] value The value to serialize + template + bool SerializeCasted( bool writeToBitstream, sourceType &value ); + + /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range + /// Then serialize only those bits + /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum + /// \param[in] minimum Minimum value of \a value + /// \param[in] maximum Maximum value of \a value + /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates + template + bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); + /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum + template + bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); + + /// \brief Bidirectional serialize/deserialize a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. + /// \details Will further compress y or z axis aligned vectors. + /// Accurate to 1/32767.5. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template // templateType for this function must be a float or double + bool SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ); + + /// \brief Bidirectional serialize/deserialize a vector, using 10 bytes instead of 12. + /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template // templateType for this function must be a float or double + bool SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ); + + /// \brief Bidirectional serialize/deserialize a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] w w + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + template // templateType for this function must be a float or double + bool SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z); + + /// \brief Bidirectional serialize/deserialize an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each. + /// \details Use 6 bytes instead of 36 + /// Lossy, although the result is renormalized + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool SerializeOrthMatrix( + bool writeToBitstream, + templateType &m00, templateType &m01, templateType &m02, + templateType &m10, templateType &m11, templateType &m12, + templateType &m20, templateType &m21, templateType &m22 ); + + /// \brief Bidirectional serialize/deserialize numberToSerialize bits to/from the input. + /// \details Right aligned data means in the case of a partial byte, the bits are aligned + /// from the right (bit 0) rather than the left (as in the normal + /// internal representation) You would set this to true when + /// writing user data, and false when copying bitstream data, such + /// as writing one bitstream to another + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] inOutByteArray The data + /// \param[in] numberOfBitsToSerialize The number of bits to write + /// \param[in] rightAlignedBits if true data will be right aligned + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits = true ); + + /// \brief Write any integral type to a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] inTemplateVar The value to write + template + void Write(const templateType &inTemplateVar); + + /// \brief Write the dereferenced pointer to any integral type to a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] inTemplateVar The value to write + template + void WritePtr(templateType *inTemplateVar); + + /// \brief Write any integral type to a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template + void WriteDelta(const templateType ¤tValue, const templateType &lastValue); + + /// \brief WriteDelta when you don't know what the last value is, or there is no last value. + /// \param[in] currentValue The current value to write + template + void WriteDelta(const templateType ¤tValue); + + /// \brief Write any integral type to a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// \param[in] inTemplateVar The value to write + template + void WriteCompressed(const templateType &inTemplateVar); + + /// \brief Write any integral type to a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template + void WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue); + + /// \brief Save as WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) when we have an unknown second parameter + template + void WriteCompressedDelta(const templateType ¤tValue); + + /// \brief Read any integral type from a bitstream. + /// \details Define __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] outTemplateVar The value to read + /// \return true on success, false on failure. + template + bool Read(templateType &outTemplateVar); + + /// \brief Read any integral type from a bitstream. + /// \details If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// ReadDelta is only valid from a previous call to WriteDelta + /// \param[in] outTemplateVar The value to read + /// \return true on success, false on failure. + template + bool ReadDelta(templateType &outTemplateVar); + + /// \brief Read any integral type from a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] outTemplateVar The value to read + /// \return true on success, false on failure. + template + bool ReadCompressed(templateType &outTemplateVar); + + /// \brief Read any integral type from a bitstream. + /// \details If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// the current value will be updated. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// ReadCompressedDelta is only valid from a previous call to WriteDelta + /// \param[in] outTemplateVar The value to read + /// \return true on success, false on failure. + template + bool ReadCompressedDelta(templateType &outTemplateVar); + + /// \brief Read one bitstream to another. + /// \param[in] numberOfBits bits to read + /// \param bitStream the bitstream to read into from + /// \return true on success, false on failure. + bool Read( BitStream *bitStream, BitSize_t numberOfBits ); + bool Read( BitStream *bitStream ); + bool Read( BitStream &bitStream, BitSize_t numberOfBits ); + bool Read( BitStream &bitStream ); + + /// \brief Write an array or casted stream or raw data. This does NOT do endian swapping. + /// \param[in] inputByteArray a byte buffer + /// \param[in] numberOfBytes the size of \a input in bytes + void Write( const char* inputByteArray, const unsigned int numberOfBytes ); + + /// \brief Write one bitstream to another. + /// \param[in] numberOfBits bits to write + /// \param bitStream the bitstream to copy from + void Write( BitStream *bitStream, BitSize_t numberOfBits ); + void Write( BitStream *bitStream ); + void Write( BitStream &bitStream, BitSize_t numberOfBits ); + void Write( BitStream &bitStream );\ + + /// \brief Write a float into 2 bytes, spanning the range between \a floatMin and \a floatMax + /// \param[in] x The float to write + /// \param[in] floatMin Predetermined minimum value of f + /// \param[in] floatMax Predetermined maximum value of f + void WriteFloat16( float x, float floatMin, float floatMax ); + + /// Write one type serialized as another (smaller) type, to save bandwidth + /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t + /// Example: int num=53; WriteCasted(num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes) + /// \param[in] value The value to write + template + void WriteCasted( const sourceType &value ); + + /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range + /// Then write only those bits + /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program + /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum + /// \param[in] minimum Minimum value of \a value + /// \param[in] maximum Maximum value of \a value + /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Read(). + template + void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); + /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum + template + void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); + + /// \brief Write a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. + /// \details Will further compress y or z axis aligned vectors. + /// Accurate to 1/32767.5. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + template // templateType for this function must be a float or double + void WriteNormVector( templateType x, templateType y, templateType z ); + + /// \brief Write a vector, using 10 bytes instead of 12. + /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, + /// so only use if accuracy is not important. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + template // templateType for this function must be a float or double + void WriteVector( templateType x, templateType y, templateType z ); + + /// \brief Write a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. + /// \param[in] w w + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + template // templateType for this function must be a float or double + void WriteNormQuat( templateType w, templateType x, templateType y, templateType z); + + /// \brief Write an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each. + /// \details Use 6 bytes instead of 36 + /// Lossy, although the result is renormalized + template // templateType for this function must be a float or double + void WriteOrthMatrix( + templateType m00, templateType m01, templateType m02, + templateType m10, templateType m11, templateType m12, + templateType m20, templateType m21, templateType m22 ); + + /// \brief Read an array or casted stream of byte. + /// \details The array is raw data. There is no automatic endian conversion with this function + /// \param[in] output The result byte array. It should be larger than @em numberOfBytes. + /// \param[in] numberOfBytes The number of byte to read + /// \return true on success false if there is some missing bytes. + bool Read( char* output, const unsigned int numberOfBytes ); + + /// \brief Read a float into 2 bytes, spanning the range between \a floatMin and \a floatMax + /// \param[in] outFloat The float to read + /// \param[in] floatMin Predetermined minimum value of f + /// \param[in] floatMax Predetermined maximum value of f + bool ReadFloat16( float &outFloat, float floatMin, float floatMax ); + + /// Read one type serialized to another (smaller) type, to save bandwidth + /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t + /// Example: int num; ReadCasted(num); would read 1 bytefrom the stream, and put the value in an integer + /// \param[in] value The value to write + template + bool ReadCasted( sourceType &value ); + + /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range + /// Then read only those bits + /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program + /// \param[in] value Integer value to read, which should be between \a minimum and \a maximum + /// \param[in] minimum Minimum value of \a value + /// \param[in] maximum Maximum value of \a value + /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Write(). + template + bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false ); + /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum + template + bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false ); + + /// \brief Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. + /// \details Will further compress y or z axis aligned vectors. + /// Accurate to 1/32767.5. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool ReadNormVector( templateType &x, templateType &y, templateType &z ); + + /// \brief Read 3 floats or doubles, using 10 bytes, where those float or doubles comprise a vector. + /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, + /// so only use if accuracy is not important. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool ReadVector( templateType &x, templateType &y, templateType &z ); + + /// \brief Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. + /// \param[in] w w + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z); + + /// \brief Read an orthogonal matrix from a quaternion, reading 3 components of the quaternion in 2 bytes each and extrapolatig the 4th. + /// \details Use 6 bytes instead of 36 + /// Lossy, although the result is renormalized + /// \return true on success, false on failure. + template // templateType for this function must be a float or double + bool ReadOrthMatrix( + templateType &m00, templateType &m01, templateType &m02, + templateType &m10, templateType &m11, templateType &m12, + templateType &m20, templateType &m21, templateType &m22 ); + + /// \brief Sets the read pointer back to the beginning of your data. + void ResetReadPointer( void ); + + /// \brief Sets the write pointer back to the beginning of your data. + void ResetWritePointer( void ); + + /// \brief This is good to call when you are done with the stream to make + /// sure you didn't leave any data left over void + void AssertStreamEmpty( void ); + + /// \brief RAKNET_DEBUG_PRINTF the bits in the stream. Great for debugging. + void PrintBits( char *out ) const; + void PrintBits( void ) const; + void PrintHex( char *out ) const; + void PrintHex( void ) const; + + /// \brief Ignore data we don't intend to read + /// \param[in] numberOfBits The number of bits to ignore + void IgnoreBits( const BitSize_t numberOfBits ); + + /// \brief Ignore data we don't intend to read + /// \param[in] numberOfBits The number of bytes to ignore + void IgnoreBytes( const unsigned int numberOfBytes ); + + /// \brief Move the write pointer to a position on the array. + /// \param[in] offset the offset from the start of the array. + /// \attention + /// \details Dangerous if you don't know what you are doing! + /// For efficiency reasons you can only write mid-stream if your data is byte aligned. + void SetWriteOffset( const BitSize_t offset ); + + /// \brief Returns the length in bits of the stream + inline BitSize_t GetNumberOfBitsUsed( void ) const {return GetWriteOffset();} + inline BitSize_t GetWriteOffset( void ) const {return numberOfBitsUsed;} + + /// \brief Returns the length in bytes of the stream + inline BitSize_t GetNumberOfBytesUsed( void ) const {return BITS_TO_BYTES( numberOfBitsUsed );} + + /// \brief Returns the number of bits into the stream that we have read + inline BitSize_t GetReadOffset( void ) const {return readOffset;} + + /// \brief Sets the read bit index + void SetReadOffset( const BitSize_t newReadOffset ) {readOffset=newReadOffset;} + + /// \brief Returns the number of bits left in the stream that haven't been read + inline BitSize_t GetNumberOfUnreadBits( void ) const {return numberOfBitsUsed - readOffset;} + + /// \brief Makes a copy of the internal data for you \a _data will point to + /// the stream. Partial bytes are left aligned. + /// \param[out] _data The allocated copy of GetData() + /// \return The length in bits of the stream. + BitSize_t CopyData( unsigned char** _data ) const; + + /// \internal + /// Set the stream to some initial data. + void SetData( unsigned char *inByteArray ); + + /// Gets the data that BitStream is writing to / reading from. + /// Partial bytes are left aligned. + /// \return A pointer to the internal state + inline unsigned char* GetData( void ) const {return data;} + + /// \brief Write numberToWrite bits from the input source. + /// \details Right aligned data means in the case of a partial byte, the bits are aligned + /// from the right (bit 0) rather than the left (as in the normal + /// internal representation) You would set this to true when + /// writing user data, and false when copying bitstream data, such + /// as writing one bitstream to another. + /// \param[in] inByteArray The data + /// \param[in] numberOfBitsToWrite The number of bits to write + /// \param[in] rightAlignedBits if true data will be right aligned + void WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits = true ); + + /// \brief Align the bitstream to the byte boundary and then write the + /// specified number of bits. + /// \details This is faster than WriteBits but + /// wastes the bits to do the alignment and requires you to call + /// ReadAlignedBits at the corresponding read position. + /// \param[in] inByteArray The data + /// \param[in] numberOfBytesToWrite The size of input. + void WriteAlignedBytes( const unsigned char *inByteArray, const unsigned int numberOfBytesToWrite ); + + // Endian swap bytes already in the bitstream + void EndianSwapBytes( int byteOffset, int length ); + + /// \brief Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite + /// \param[in] inByteArray The data + /// \param[in] inputLength The size of input. + /// \param[in] maxBytesToWrite Max bytes to write + void WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite ); + + /// \brief Read bits, starting at the next aligned bits. + /// \details Note that the modulus 8 starting offset of the sequence must be the same as + /// was used with WriteBits. This will be a problem with packet + /// coalescence unless you byte align the coalesced packets. + /// \param[in] inOutByteArray The byte array larger than @em numberOfBytesToRead + /// \param[in] numberOfBytesToRead The number of byte to read from the internal state + /// \return true if there is enough byte. + bool ReadAlignedBytes( unsigned char *inOutByteArray, const unsigned int numberOfBytesToRead ); + + /// \brief Reads what was written by WriteAlignedBytesSafe. + /// \param[in] inOutByteArray The data + /// \param[in] maxBytesToRead Maximum number of bytes to read + /// \return true on success, false on failure. + bool ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead ); + bool ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ); + + /// \brief Same as ReadAlignedBytesSafe() but allocates the memory for you using new, rather than assuming it is safe to write to + /// \param[in] outByteArray outByteArray will be deleted if it is not a pointer to 0 + /// \return true on success, false on failure. + bool ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead ); + bool ReadAlignedBytesSafeAlloc( char **outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead ); + + /// \brief Align the next write and/or read to a byte boundary. + /// \details This can be used to 'waste' bits to byte align for efficiency reasons It + /// can also be used to force coalesced bitstreams to start on byte + /// boundaries so so WriteAlignedBits and ReadAlignedBits both + /// calculate the same offset when aligning. + inline void AlignWriteToByteBoundary( void ) {numberOfBitsUsed += 8 - ( (( numberOfBitsUsed - 1 ) & 7) + 1 );} + + /// \brief Align the next write and/or read to a byte boundary. + /// \details This can be used to 'waste' bits to byte align for efficiency reasons It + /// can also be used to force coalesced bitstreams to start on byte + /// boundaries so so WriteAlignedBits and ReadAlignedBits both + /// calculate the same offset when aligning. + inline void AlignReadToByteBoundary( void ) {readOffset += 8 - ( (( readOffset - 1 ) & 7 ) + 1 );} + + /// \brief Read \a numberOfBitsToRead bits to the output source. + /// \details alignBitsToRight should be set to true to convert internal + /// bitstream data to userdata. It should be false if you used + /// WriteBits with rightAlignedBits false + /// \param[in] inOutByteArray The resulting bits array + /// \param[in] numberOfBitsToRead The number of bits to read + /// \param[in] alignBitsToRight if true bits will be right aligned. + /// \return true if there is enough bits to read + bool ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight = true ); + + /// \brief Write a 0 + void Write0( void ); + + /// \brief Write a 1 + void Write1( void ); + + /// \brief Reads 1 bit and returns true if that bit is 1 and false if it is 0. + bool ReadBit( void ); + + /// \brief If we used the constructor version with copy data off, this + /// *makes sure it is set to on and the data pointed to is copied. + void AssertCopyData( void ); + + /// \brief Use this if you pass a pointer copy to the constructor + /// *(_copyData==false) and want to overallocate to prevent + /// reallocation. + void SetNumberOfBitsAllocated( const BitSize_t lengthInBits ); + + /// \brief Reallocates (if necessary) in preparation of writing numberOfBitsToWrite + void AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite ); + + /// \internal + /// \return How many bits have been allocated internally + BitSize_t GetNumberOfBitsAllocated(void) const; + + /// \brief Read strings, non reference. + bool Read(char *varString); + bool Read(unsigned char *varString); + + /// Write zeros until the bitstream is filled up to \a bytes + void PadWithZeroToByteLength( unsigned int bytes ); + + /// Get the number of leading zeros for a number + /// \param[in] x Number to test + static int NumberOfLeadingZeroes( uint8_t x ); + static int NumberOfLeadingZeroes( uint16_t x ); + static int NumberOfLeadingZeroes( uint32_t x ); + static int NumberOfLeadingZeroes( uint64_t x ); + static int NumberOfLeadingZeroes( int8_t x ); + static int NumberOfLeadingZeroes( int16_t x ); + static int NumberOfLeadingZeroes( int32_t x ); + static int NumberOfLeadingZeroes( int64_t x ); + + /// \internal Unrolled inner loop, for when performance is critical + void WriteAlignedVar8(const char *inByteArray); + /// \internal Unrolled inner loop, for when performance is critical + bool ReadAlignedVar8(char *inOutByteArray); + /// \internal Unrolled inner loop, for when performance is critical + void WriteAlignedVar16(const char *inByteArray); + /// \internal Unrolled inner loop, for when performance is critical + bool ReadAlignedVar16(char *inOutByteArray); + /// \internal Unrolled inner loop, for when performance is critical + void WriteAlignedVar32(const char *inByteArray); + /// \internal Unrolled inner loop, for when performance is critical + bool ReadAlignedVar32(char *inOutByteArray); + + inline void Write(const char * const inStringVar) + { + RakString::Serialize(inStringVar, this); + } + inline void Write(const wchar_t * const inStringVar) + { + RakWString::Serialize(inStringVar, this); + } + inline void Write(const unsigned char * const inTemplateVar) + { + Write((const char*)inTemplateVar); + } + inline void Write(char * const inTemplateVar) + { + Write((const char*)inTemplateVar); + } + inline void Write(unsigned char * const inTemplateVar) + { + Write((const char*)inTemplateVar); + } + inline void WriteCompressed(const char * const inStringVar) + { + RakString::SerializeCompressed(inStringVar,this,0,false); + } + inline void WriteCompressed(const wchar_t * const inStringVar) + { + RakWString::Serialize(inStringVar,this); + } + inline void WriteCompressed(const unsigned char * const inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + inline void WriteCompressed(char * const inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + inline void WriteCompressed(unsigned char * const inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + + /// ---- Member function template specialization declarations ---- + // Used for VC7 +#if defined(_MSC_VER) && _MSC_VER == 1300 + /// Write a bool to a bitstream. + /// \param[in] var The value to write + template <> + void Write(const bool &var); + + /// Write a systemAddress to a bitstream + /// \param[in] var The value to write + template <> + void Write(const SystemAddress &var); + + /// Write a uint24_t to a bitstream + /// \param[in] var The value to write + template <> + void Write(const uint24_t &var); + + /// Write a RakNetGUID to a bitsteam + /// \param[in] var The value to write + template <> + void Write(const RakNetGuid &var); + + /// Write a string to a bitstream + /// \param[in] var The value to write + template <> + void Write(const char* const &var); + template <> + void Write(const unsigned char* const &var); + template <> + void Write(char* const &var); + template <> + void Write(unsigned char* const &var); + template <> + void Write(const RakString &var); + template <> + void Write(const RakWString &var); + + /// \brief Write a systemAddress. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + void WriteDelta(const SystemAddress ¤tValue, const SystemAddress &lastValue); + + template <> + void WriteDelta(const uint24_t ¤tValue, const uint24_t &lastValue); + + template <> + void WriteDelta(const RakNetGUID ¤tValue, const RakNetGUID &lastValue); + + /// \brief Write a bool delta. + /// \details Same thing as just calling Write + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + void WriteDelta(const bool ¤tValue, const bool &lastValue); + + template <> + void WriteCompressed(const SystemAddress &var); + + template <> + void WriteCompressed(const uint24_t &var); + + template <> + void WriteCompressed(const RakNetGUID &var); + + template <> + void WriteCompressed(const bool &var); + + /// For values between -1 and 1 + template <> + void WriteCompressed(const float &var); + + /// For values between -1 and 1 + template <> + void WriteCompressed(const double &var); + + /// Compressed string + template <> + void WriteCompressed(const char* var); + template <> + void WriteCompressed(const unsigned char* var); + template <> + void WriteCompressed(char* var); + template <> + void WriteCompressed(unsigned char* var); + template <> + void WriteCompressed(const RakString &var); + template <> + void WriteCompressed(const RakWString &var); + + /// \brief Write a bool delta. + /// \details Same thing as just calling Write + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + void WriteCompressedDelta(const bool ¤tValue, const bool &lastValue); + + /// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue) + /// when we have an unknown second bool + template <> + void WriteCompressedDelta(const bool ¤tValue); + + /// \brief Read a bool from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool Read(bool &var); + + /// \brief Read a systemAddress from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool Read(SystemAddress &var); + + template <> + bool Read(uint24_t &var); + + template <> + bool Read(RakNetGUID &var); + + /// \brief Read a String from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool Read(char *&var); + template <> + bool Read(wchar_t *&var); + template <> + bool Read(unsigned char *&var); + template <> + bool Read(RakString &var); + template <> + bool Read(RakWString &var); + + /// \brief Read a bool from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool ReadDelta(bool &var); + + template <> + bool ReadCompressed(SystemAddress &var); + + template <> + bool ReadCompressed(uint24_t &var); + + template <> + bool ReadCompressed(RakNetGUID &var); + + template <> + bool ReadCompressed(bool &var); + + template <> + bool ReadCompressed(float &var); + + /// For values between -1 and 1 + /// \return true on success, false on failure. + template <> + bool ReadCompressed(double &var); + + template <> + bool ReadCompressed(char* &var); + template <> + bool ReadCompressed(wchar_t* &var); + template <> + bool ReadCompressed(unsigned char *&var); + template <> + bool ReadCompressed(RakString &var); + template <> + bool ReadCompressed(RakWString &var); + + /// \brief Read a bool from a bitstream. + /// \param[in] var The value to read + /// \return true on success, false on failure. + template <> + bool ReadCompressedDelta(bool &var); +#endif + + inline static bool DoEndianSwap(void) { +#ifndef __BITSTREAM_NATIVE_END + return IsNetworkOrder()==false; +#else + return false; +#endif + } + inline static bool IsBigEndian(void) + { + return IsNetworkOrder(); + } + inline static bool IsNetworkOrder(void) {static const bool r = IsNetworkOrderInternal(); return r;} + // Not inline, won't compile on PC due to winsock include errors + static bool IsNetworkOrderInternal(void); + static void ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length); + static void ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length); + + private: + + BitStream( const BitStream &invalid) { + (void) invalid; + RakAssert(0); + } + + /// \brief Assume the input source points to a native type, compress and write it. + void WriteCompressed( const unsigned char* inByteArray, const unsigned int size, const bool unsignedData ); + + /// \brief Assume the input source points to a compressed native type. Decompress and read it. + bool ReadCompressed( unsigned char* inOutByteArray, const unsigned int size, const bool unsignedData ); + + + BitSize_t numberOfBitsUsed; + + BitSize_t numberOfBitsAllocated; + + BitSize_t readOffset; + + unsigned char *data; + + /// true if the internal buffer is copy of the data passed to the constructor + bool copyData; + + /// BitStreams that use less than BITSTREAM_STACK_ALLOCATION_SIZE use the stack, rather than the heap to store data. It switches over if BITSTREAM_STACK_ALLOCATION_SIZE is exceeded + unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE]; + }; + + template + inline bool BitStream::Serialize(bool writeToBitstream, templateType &inOutTemplateVar) + { + if (writeToBitstream) + Write(inOutTemplateVar); + else + return Read(inOutTemplateVar); + return true; + } + + template + inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue) + { + if (writeToBitstream) + WriteDelta(inOutCurrentValue, lastValue); + else + return ReadDelta(inOutCurrentValue); + return true; + } + + template + inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue) + { + if (writeToBitstream) + WriteDelta(inOutCurrentValue); + else + return ReadDelta(inOutCurrentValue); + return true; + } + + template + inline bool BitStream::SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar) + { + if (writeToBitstream) + WriteCompressed(inOutTemplateVar); + else + return ReadCompressed(inOutTemplateVar); + return true; + } + + template + inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue) + { + if (writeToBitstream) + WriteCompressedDelta(inOutCurrentValue,lastValue); + else + return ReadCompressedDelta(inOutCurrentValue); + return true; + } +//Stoppedhere + template + inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue) + { + if (writeToBitstream) + WriteCompressedDelta(inOutCurrentValue); + else + return ReadCompressedDelta(inOutCurrentValue); + return true; + } + + inline bool BitStream::Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes ) + { + if (writeToBitstream) + Write(inOutByteArray, numberOfBytes); + else + return Read(inOutByteArray, numberOfBytes); + return true; + } + + template + bool BitStream::SerializeCasted( bool writeToBitstream, sourceType &value ) + { + if (writeToBitstream) WriteCasted(value); + else return ReadCasted(value); + return true; + } + + template + bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange ) + { + int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); + return SerializeBitsFromIntegerRange(writeToBitstream,value,minimum,maximum,requiredBits,allowOutsideRange); + } + template + bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange ) + { + if (writeToBitstream) WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); + else return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); + return true; + } + + template + inline bool BitStream::SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ) + { + if (writeToBitstream) + WriteNormVector(x,y,z); + else + return ReadNormVector(x,y,z); + return true; + } + + template + inline bool BitStream::SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z ) + { + if (writeToBitstream) + WriteVector(x,y,z); + else + return ReadVector(x,y,z); + return true; + } + + template + inline bool BitStream::SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z) + { + if (writeToBitstream) + WriteNormQuat(w,x,y,z); + else + return ReadNormQuat(w,x,y,z); + return true; + } + + template + inline bool BitStream::SerializeOrthMatrix( + bool writeToBitstream, + templateType &m00, templateType &m01, templateType &m02, + templateType &m10, templateType &m11, templateType &m12, + templateType &m20, templateType &m21, templateType &m22 ) + { + if (writeToBitstream) + WriteOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); + else + return ReadOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); + return true; + } + + inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits ) + { + if (writeToBitstream) + WriteBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits); + else + return ReadBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits); + return true; + } + + template + inline void BitStream::Write(const templateType &inTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(inTemplateVar)==1) + WriteBits( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType)); + WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ); + } + else +#endif + WriteBits( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true ); + } + } + + template + inline void BitStream::WritePtr(templateType *inTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(templateType)==1) + WriteBits( ( unsigned char* ) inTemplateVar, sizeof( templateType ) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + ReverseBytes((unsigned char*) inTemplateVar, output, sizeof(templateType)); + WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ); + } + else +#endif + WriteBits( ( unsigned char* ) inTemplateVar, sizeof(templateType) * 8, true ); + } + } + + /// \brief Write a bool to a bitstream. + /// \param[in] inTemplateVar The value to write + template <> + inline void BitStream::Write(const bool &inTemplateVar) + { + if ( inTemplateVar ) + Write1(); + else + Write0(); + } + + + /// \brief Write a systemAddress to a bitstream. + /// \param[in] inTemplateVar The value to write + template <> + inline void BitStream::Write(const SystemAddress &inTemplateVar) + { + Write(inTemplateVar.GetIPVersion()); + if (inTemplateVar.GetIPVersion()==4) + { + // Hide the address so routers don't modify it + SystemAddress var2=inTemplateVar; + uint32_t binaryAddress=~inTemplateVar.address.addr4.sin_addr.s_addr; + // Don't endian swap the address or port + WriteBits((unsigned char*)&binaryAddress, sizeof(binaryAddress)*8, true); + unsigned short p = var2.GetPortNetworkOrder(); + WriteBits((unsigned char*)&p, sizeof(unsigned short)*8, true); + } + else + { +#if RAKNET_SUPPORT_IPV6==1 + // Don't endian swap + WriteBits((const unsigned char*) &inTemplateVar.address.addr6, sizeof(inTemplateVar.address.addr6)*8, true); +#endif + } + } + + template <> + inline void BitStream::Write(const uint24_t &inTemplateVar) + { + AlignWriteToByteBoundary(); + AddBitsAndReallocate(3*8); + + if (IsBigEndian()==false) + { + data[( numberOfBitsUsed >> 3 ) + 0] = ((char *)&inTemplateVar.val)[0]; + data[( numberOfBitsUsed >> 3 ) + 1] = ((char *)&inTemplateVar.val)[1]; + data[( numberOfBitsUsed >> 3 ) + 2] = ((char *)&inTemplateVar.val)[2]; + } + else + { + data[( numberOfBitsUsed >> 3 ) + 0] = ((char *)&inTemplateVar.val)[3]; + data[( numberOfBitsUsed >> 3 ) + 1] = ((char *)&inTemplateVar.val)[2]; + data[( numberOfBitsUsed >> 3 ) + 2] = ((char *)&inTemplateVar.val)[1]; + } + + numberOfBitsUsed+=3*8; + } + + template <> + inline void BitStream::Write(const RakNetGUID &inTemplateVar) + { + Write(inTemplateVar.g); + } + + /// \brief Write a string to a bitstream. + /// \param[in] var The value to write + template <> + inline void BitStream::Write(const RakString &inTemplateVar) + { + inTemplateVar.Serialize(this); + } + template <> + inline void BitStream::Write(const RakWString &inTemplateVar) + { + inTemplateVar.Serialize(this); + } + template <> + inline void BitStream::Write(const char * const &inStringVar) + { + RakString::Serialize(inStringVar, this); + } + template <> + inline void BitStream::Write(const wchar_t * const &inStringVar) + { + RakWString::Serialize(inStringVar, this); + } + template <> + inline void BitStream::Write(const unsigned char * const &inTemplateVar) + { + Write((const char*)inTemplateVar); + } + template <> + inline void BitStream::Write(char * const &inTemplateVar) + { + Write((const char*)inTemplateVar); + } + template <> + inline void BitStream::Write(unsigned char * const &inTemplateVar) + { + Write((const char*)inTemplateVar); + } + + /// \brief Write any integral type to a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template + inline void BitStream::WriteDelta(const templateType ¤tValue, const templateType &lastValue) + { + if (currentValue==lastValue) + { + Write(false); + } + else + { + Write(true); + Write(currentValue); + } + } + + /// \brief Write a bool delta. Same thing as just calling Write + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + inline void BitStream::WriteDelta(const bool ¤tValue, const bool &lastValue) + { + (void) lastValue; + + Write(currentValue); + } + + /// \brief WriteDelta when you don't know what the last value is, or there is no last value. + /// \param[in] currentValue The current value to write + template + inline void BitStream::WriteDelta(const templateType ¤tValue) + { + Write(true); + Write(currentValue); + } + + /// \brief Write any integral type to a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] inTemplateVar The value to write + template + inline void BitStream::WriteCompressed(const templateType &inTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(inTemplateVar)==1) + WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END +#ifdef _MSC_VER +#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data +#endif + + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType)); + WriteCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true ); + } + else +#endif + WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true ); + } + } + + template <> + inline void BitStream::WriteCompressed(const SystemAddress &inTemplateVar) + { + Write(inTemplateVar); + } + + template <> + inline void BitStream::WriteCompressed(const RakNetGUID &inTemplateVar) + { + Write(inTemplateVar); + } + + template <> + inline void BitStream::WriteCompressed(const uint24_t &var) + { + Write(var); + } + + template <> + inline void BitStream::WriteCompressed(const bool &inTemplateVar) + { + Write(inTemplateVar); + } + + /// For values between -1 and 1 + template <> + inline void BitStream::WriteCompressed(const float &inTemplateVar) + { + RakAssert(inTemplateVar > -1.01f && inTemplateVar < 1.01f); + float varCopy=inTemplateVar; + if (varCopy < -1.0f) + varCopy=-1.0f; + if (varCopy > 1.0f) + varCopy=1.0f; + Write((unsigned short)((varCopy+1.0f)*32767.5f)); + } + + /// For values between -1 and 1 + template <> + inline void BitStream::WriteCompressed(const double &inTemplateVar) + { + RakAssert(inTemplateVar > -1.01 && inTemplateVar < 1.01); + double varCopy=inTemplateVar; + if (varCopy < -1.0f) + varCopy=-1.0f; + if (varCopy > 1.0f) + varCopy=1.0f; + Write((uint32_t)((varCopy+1.0)*2147483648.0)); + } + + /// Compress the string + template <> + inline void BitStream::WriteCompressed(const RakString &inTemplateVar) + { + inTemplateVar.SerializeCompressed(this,0,false); + } + template <> + inline void BitStream::WriteCompressed(const RakWString &inTemplateVar) + { + inTemplateVar.Serialize(this); + } + template <> + inline void BitStream::WriteCompressed(const char * const &inStringVar) + { + RakString::SerializeCompressed(inStringVar,this,0,false); + } + template <> + inline void BitStream::WriteCompressed(const wchar_t * const &inStringVar) + { + RakWString::Serialize(inStringVar,this); + } + template <> + inline void BitStream::WriteCompressed(const unsigned char * const &inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + template <> + inline void BitStream::WriteCompressed(char * const &inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + template <> + inline void BitStream::WriteCompressed(unsigned char * const &inTemplateVar) + { + WriteCompressed((const char*) inTemplateVar); + } + + + /// \brief Write any integral type to a bitstream. + /// \details If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template + inline void BitStream::WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) + { + if (currentValue==lastValue) + { + Write(false); + } + else + { + Write(true); + WriteCompressed(currentValue); + } + } + + /// \brief Write a bool delta. Same thing as just calling Write + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + template <> + inline void BitStream::WriteCompressedDelta(const bool ¤tValue, const bool &lastValue) + { + (void) lastValue; + + Write(currentValue); + } + + /// \brief Save as WriteCompressedDelta(const templateType ¤tValue, const templateType &lastValue) + /// when we have an unknown second parameter + template + inline void BitStream::WriteCompressedDelta(const templateType ¤tValue) + { + Write(true); + WriteCompressed(currentValue); + } + + /// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue) + /// when we have an unknown second bool + template <> + inline void BitStream::WriteCompressedDelta(const bool ¤tValue) + { + Write(currentValue); + } + + /// \brief Read any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] outTemplateVar The value to read + template + inline bool BitStream::Read(templateType &outTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(outTemplateVar)==1) + return ReadBits( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END +#ifdef _MSC_VER +#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data +#endif + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + if (ReadBits( ( unsigned char* ) output, sizeof(templateType) * 8, true )) + { + ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType)); + return true; + } + return false; + } + else +#endif + return ReadBits( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true ); + } + } + + /// \brief Read a bool from a bitstream. + /// \param[in] outTemplateVar The value to read + template <> + inline bool BitStream::Read(bool &outTemplateVar) + { + if ( readOffset + 1 > numberOfBitsUsed ) + return false; + + if ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) // Is it faster to just write it out here? + outTemplateVar = true; + else + outTemplateVar = false; + + // Has to be on a different line for Mac + readOffset++; + + return true; + } + + /// \brief Read a systemAddress from a bitstream. + /// \param[in] outTemplateVar The value to read + template <> + inline bool BitStream::Read(SystemAddress &outTemplateVar) + { + unsigned char ipVersion; + Read(ipVersion); + if (ipVersion==4) + { + outTemplateVar.address.addr4.sin_family=AF_INET; + // Read(var.binaryAddress); + // Don't endian swap the address or port + uint32_t binaryAddress; + ReadBits( ( unsigned char* ) & binaryAddress, sizeof(binaryAddress) * 8, true ); + // Unhide the IP address, done to prevent routers from changing it + outTemplateVar.address.addr4.sin_addr.s_addr=~binaryAddress; + bool b = ReadBits(( unsigned char* ) & outTemplateVar.address.addr4.sin_port, sizeof(outTemplateVar.address.addr4.sin_port) * 8, true); + outTemplateVar.debugPort=ntohs(outTemplateVar.address.addr4.sin_port); + return b; + } + else + { +#if RAKNET_SUPPORT_IPV6==1 + bool b = ReadBits((unsigned char*) &outTemplateVar.address.addr6, sizeof(outTemplateVar.address.addr6)*8, true); + outTemplateVar.debugPort=ntohs(outTemplateVar.address.addr6.sin6_port); + return b; +#else + return false; +#endif + } + } + + template <> + inline bool BitStream::Read(uint24_t &outTemplateVar) + { + AlignReadToByteBoundary(); + if ( readOffset + 3*8 > numberOfBitsUsed ) + return false; + + if (IsBigEndian()==false) + { + ((char *)&outTemplateVar.val)[0]=data[ (readOffset >> 3) + 0]; + ((char *)&outTemplateVar.val)[1]=data[ (readOffset >> 3) + 1]; + ((char *)&outTemplateVar.val)[2]=data[ (readOffset >> 3) + 2]; + ((char *)&outTemplateVar.val)[3]=0; + } + else + { + + ((char *)&outTemplateVar.val)[3]=data[ (readOffset >> 3) + 0]; + ((char *)&outTemplateVar.val)[2]=data[ (readOffset >> 3) + 1]; + ((char *)&outTemplateVar.val)[1]=data[ (readOffset >> 3) + 2]; + ((char *)&outTemplateVar.val)[0]=0; + } + + readOffset+=3*8; + return true; + } + + template <> + inline bool BitStream::Read(RakNetGUID &outTemplateVar) + { + return Read(outTemplateVar.g); + } + + + template <> + inline bool BitStream::Read(RakString &outTemplateVar) + { + return outTemplateVar.Deserialize(this); + } + template <> + inline bool BitStream::Read(RakWString &outTemplateVar) + { + return outTemplateVar.Deserialize(this); + } + template <> + inline bool BitStream::Read(char *&varString) + { + return RakString::Deserialize(varString,this); + } + template <> + inline bool BitStream::Read(wchar_t *&varString) + { + return RakWString::Deserialize(varString,this); + } + template <> + inline bool BitStream::Read(unsigned char *&varString) + { + return RakString::Deserialize((char*) varString,this); + } + + /// \brief Read any integral type from a bitstream. + /// \details If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// ReadDelta is only valid from a previous call to WriteDelta + /// \param[in] outTemplateVar The value to read + template + inline bool BitStream::ReadDelta(templateType &outTemplateVar) + { + bool dataWritten; + bool success; + success=Read(dataWritten); + if (dataWritten) + success=Read(outTemplateVar); + return success; + } + + /// \brief Read a bool from a bitstream. + /// \param[in] outTemplateVar The value to read + template <> + inline bool BitStream::ReadDelta(bool &outTemplateVar) + { + return Read(outTemplateVar); + } + + /// \brief Read any integral type from a bitstream. + /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] outTemplateVar The value to read + template + inline bool BitStream::ReadCompressed(templateType &outTemplateVar) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(outTemplateVar)==1) + return ReadCompressed( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + if (ReadCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true )) + { + ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType)); + return true; + } + return false; + } + else +#endif + return ReadCompressed( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true ); + } + } + + template <> + inline bool BitStream::ReadCompressed(SystemAddress &outTemplateVar) + { + return Read(outTemplateVar); + } + + template <> + inline bool BitStream::ReadCompressed(uint24_t &outTemplateVar) + { + return Read(outTemplateVar); + } + + template <> + inline bool BitStream::ReadCompressed(RakNetGUID &outTemplateVar) + { + return Read(outTemplateVar); + } + + template <> + inline bool BitStream::ReadCompressed(bool &outTemplateVar) + { + return Read(outTemplateVar); + } + + /// For values between -1 and 1 + template <> + inline bool BitStream::ReadCompressed(float &outTemplateVar) + { + unsigned short compressedFloat; + if (Read(compressedFloat)) + { + outTemplateVar = ((float)compressedFloat / 32767.5f - 1.0f); + return true; + } + return false; + } + + /// For values between -1 and 1 + template <> + inline bool BitStream::ReadCompressed(double &outTemplateVar) + { + uint32_t compressedFloat; + if (Read(compressedFloat)) + { + outTemplateVar = ((double)compressedFloat / 2147483648.0 - 1.0); + return true; + } + return false; + } + + /// For strings + template <> + inline bool BitStream::ReadCompressed(RakString &outTemplateVar) + { + return outTemplateVar.DeserializeCompressed(this,false); + } + template <> + inline bool BitStream::ReadCompressed(RakWString &outTemplateVar) + { + return outTemplateVar.Deserialize(this); + } + template <> + inline bool BitStream::ReadCompressed(char *&outTemplateVar) + { + return RakString::DeserializeCompressed(outTemplateVar,this,false); + } + template <> + inline bool BitStream::ReadCompressed(wchar_t *&outTemplateVar) + { + return RakWString::Deserialize(outTemplateVar,this); + } + template <> + inline bool BitStream::ReadCompressed(unsigned char *&outTemplateVar) + { + return RakString::DeserializeCompressed((char*) outTemplateVar,this,false); + } + + /// \brief Read any integral type from a bitstream. + /// \details If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// the current value will be updated. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// ReadCompressedDelta is only valid from a previous call to WriteDelta + /// \param[in] outTemplateVar The value to read + template + inline bool BitStream::ReadCompressedDelta(templateType &outTemplateVar) + { + bool dataWritten; + bool success; + success=Read(dataWritten); + if (dataWritten) + success=ReadCompressed(outTemplateVar); + return success; + } + + /// \brief Read a bool from a bitstream. + /// \param[in] outTemplateVar The value to read + template <> + inline bool BitStream::ReadCompressedDelta(bool &outTemplateVar) + { + return Read(outTemplateVar); + } + + template + void BitStream::WriteCasted( const sourceType &value ) + { + destinationType val = (destinationType) value; + Write(val); + } + + template + void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, bool allowOutsideRange ) + { + int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); + WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); + } + template + void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, const int requiredBits, bool allowOutsideRange ) + { + RakAssert(maximum>=minimum); + RakAssert(allowOutsideRange==true || (value>=minimum && value<=maximum)); + if (allowOutsideRange) + { + if (valuemaximum) + { + Write(true); + Write(value); + return; + } + Write(false); + } + templateType valueOffMin=value-minimum; + if (IsBigEndian()==true) + { + unsigned char output[sizeof(templateType)]; + ReverseBytes((unsigned char*)&valueOffMin, output, sizeof(templateType)); + WriteBits(output,requiredBits); + } + else + { + WriteBits((unsigned char*) &valueOffMin,requiredBits); + } + } + + template // templateType for this function must be a float or double + void BitStream::WriteNormVector( templateType x, templateType y, templateType z ) + { +#ifdef _DEBUG + RakAssert(x <= 1.01 && y <= 1.01 && z <= 1.01 && x >= -1.01 && y >= -1.01 && z >= -1.01); +#endif + + WriteFloat16((float)x,-1.0f,1.0f); + WriteFloat16((float)y,-1.0f,1.0f); + WriteFloat16((float)z,-1.0f,1.0f); + } + + template // templateType for this function must be a float or double + void BitStream::WriteVector( templateType x, templateType y, templateType z ) + { + templateType magnitude = sqrt(x * x + y * y + z * z); + Write((float)magnitude); + if (magnitude > 0.00001f) + { + WriteCompressed((float)(x/magnitude)); + WriteCompressed((float)(y/magnitude)); + WriteCompressed((float)(z/magnitude)); + // Write((unsigned short)((x/magnitude+1.0f)*32767.5f)); + // Write((unsigned short)((y/magnitude+1.0f)*32767.5f)); + // Write((unsigned short)((z/magnitude+1.0f)*32767.5f)); + } + } + + template // templateType for this function must be a float or double + void BitStream::WriteNormQuat( templateType w, templateType x, templateType y, templateType z) + { + Write((bool)(w<0.0)); + Write((bool)(x<0.0)); + Write((bool)(y<0.0)); + Write((bool)(z<0.0)); + Write((unsigned short)(fabs(x)*65535.0)); + Write((unsigned short)(fabs(y)*65535.0)); + Write((unsigned short)(fabs(z)*65535.0)); + // Leave out w and calculate it on the target + } + + template // templateType for this function must be a float or double + void BitStream::WriteOrthMatrix( + templateType m00, templateType m01, templateType m02, + templateType m10, templateType m11, templateType m12, + templateType m20, templateType m21, templateType m22 ) + { + + double qw; + double qx; + double qy; + double qz; + + // Convert matrix to quat + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ + float sum; + sum = 1 + m00 + m11 + m22; + if (sum < 0.0f) sum=0.0f; + qw = sqrt( sum ) / 2; + sum = 1 + m00 - m11 - m22; + if (sum < 0.0f) sum=0.0f; + qx = sqrt( sum ) / 2; + sum = 1 - m00 + m11 - m22; + if (sum < 0.0f) sum=0.0f; + qy = sqrt( sum ) / 2; + sum = 1 - m00 - m11 + m22; + if (sum < 0.0f) sum=0.0f; + qz = sqrt( sum ) / 2; + if (qw < 0.0) qw=0.0; + if (qx < 0.0) qx=0.0; + if (qy < 0.0) qy=0.0; + if (qz < 0.0) qz=0.0; + qx = _copysign( (double) qx, (double) (m21 - m12) ); + qy = _copysign( (double) qy, (double) (m02 - m20) ); + qz = _copysign( (double) qz, (double) (m10 - m01) ); + + WriteNormQuat(qw,qx,qy,qz); + } + + template + bool BitStream::ReadCasted( sourceType &value ) + { + serializationType val; + bool success = Read(val); + value=(sourceType) val; + return success; + } + + template + bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange ) + { + int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum)); + return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange); + } + template + bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange ) + { + RakAssert(maximum>=minimum); + if (allowOutsideRange) + { + bool isOutsideRange; + Read(isOutsideRange); + if (isOutsideRange) + return Read(value); + } + unsigned char output[sizeof(templateType)]; + memset(output,0,sizeof(output)); + bool success = ReadBits(output,requiredBits); + if (success) + { + if (IsBigEndian()==true) + ReverseBytesInPlace(output,sizeof(output)); + memcpy(&value,output,sizeof(output)); + + value+=minimum; + } + + return success; + } + + template // templateType for this function must be a float or double + bool BitStream::ReadNormVector( templateType &x, templateType &y, templateType &z ) + { + float xIn,yIn,zIn; + ReadFloat16(xIn,-1.0f,1.0f); + ReadFloat16(yIn,-1.0f,1.0f); + ReadFloat16(zIn,-1.0f,1.0f); + x=xIn; + y=yIn; + z=zIn; + return true; + } + + template // templateType for this function must be a float or double + bool BitStream::ReadVector( templateType &x, templateType &y, templateType &z ) + { + float magnitude; + //unsigned short sx,sy,sz; + if (!Read(magnitude)) + return false; + if (magnitude>0.00001f) + { + // Read(sx); + // Read(sy); + // if (!Read(sz)) + // return false; + // x=((float)sx / 32767.5f - 1.0f) * magnitude; + // y=((float)sy / 32767.5f - 1.0f) * magnitude; + // z=((float)sz / 32767.5f - 1.0f) * magnitude; + float cx,cy,cz; + ReadCompressed(cx); + ReadCompressed(cy); + if (!ReadCompressed(cz)) + return false; + x=cx; + y=cy; + z=cz; + x*=magnitude; + y*=magnitude; + z*=magnitude; + } + else + { + x=0.0; + y=0.0; + z=0.0; + } + return true; + } + + template // templateType for this function must be a float or double + bool BitStream::ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z) + { + bool cwNeg=false, cxNeg=false, cyNeg=false, czNeg=false; + unsigned short cx,cy,cz; + Read(cwNeg); + Read(cxNeg); + Read(cyNeg); + Read(czNeg); + Read(cx); + Read(cy); + if (!Read(cz)) + return false; + + // Calculate w from x,y,z + x=(templateType)(cx/65535.0); + y=(templateType)(cy/65535.0); + z=(templateType)(cz/65535.0); + if (cxNeg) x=-x; + if (cyNeg) y=-y; + if (czNeg) z=-z; + float difference = 1.0f - x*x - y*y - z*z; + if (difference < 0.0f) + difference=0.0f; + w = (templateType)(sqrt(difference)); + if (cwNeg) + w=-w; + + return true; + } + + template // templateType for this function must be a float or double + bool BitStream::ReadOrthMatrix( + templateType &m00, templateType &m01, templateType &m02, + templateType &m10, templateType &m11, templateType &m12, + templateType &m20, templateType &m21, templateType &m22 ) + { + float qw,qx,qy,qz; + if (!ReadNormQuat(qw,qx,qy,qz)) + return false; + + // Quat to orthogonal rotation matrix + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm + double sqw = (double)qw*(double)qw; + double sqx = (double)qx*(double)qx; + double sqy = (double)qy*(double)qy; + double sqz = (double)qz*(double)qz; + m00 = (templateType)(sqx - sqy - sqz + sqw); // since sqw + sqx + sqy + sqz =1 + m11 = (templateType)(-sqx + sqy - sqz + sqw); + m22 = (templateType)(-sqx - sqy + sqz + sqw); + + double tmp1 = (double)qx*(double)qy; + double tmp2 = (double)qz*(double)qw; + m10 = (templateType)(2.0 * (tmp1 + tmp2)); + m01 = (templateType)(2.0 * (tmp1 - tmp2)); + + tmp1 = (double)qx*(double)qz; + tmp2 = (double)qy*(double)qw; + m20 =(templateType)(2.0 * (tmp1 - tmp2)); + m02 = (templateType)(2.0 * (tmp1 + tmp2)); + tmp1 = (double)qy*(double)qz; + tmp2 = (double)qx*(double)qw; + m21 = (templateType)(2.0 * (tmp1 + tmp2)); + m12 = (templateType)(2.0 * (tmp1 - tmp2)); + + return true; + } + + template + BitStream& operator<<(BitStream& out, templateType& c) + { + out.Write(c); + return out; + } + template + BitStream& operator>>(BitStream& in, templateType& c) + { + bool success = in.Read(c); + RakAssert(success); + return in; + } + +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + +#endif // VC6 diff --git a/src/raknet/CCRakNetSlidingWindow.cpp b/src/raknet/CCRakNetSlidingWindow.cpp index ccc9757..cf32151 100755 --- a/src/raknet/CCRakNetSlidingWindow.cpp +++ b/src/raknet/CCRakNetSlidingWindow.cpp @@ -1,332 +1,332 @@ -#include "CCRakNetSlidingWindow.h" - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1 - -static const double UNSET_TIME_US=-1; - -#if CC_TIME_TYPE_BYTES==4 -static const CCTimeType SYN=10; -#else -static const CCTimeType SYN=10000; -#endif - -#include "MTUSize.h" -#include -#include -#include -#include "RakAssert.h" -#include "RakAlloca.h" - -using namespace RakNet; - -// ****************************************************** PUBLIC METHODS ****************************************************** - -CCRakNetSlidingWindow::CCRakNetSlidingWindow() -{ -} -// ---------------------------------------------------------------------------------------------------------------------------- -CCRakNetSlidingWindow::~CCRakNetSlidingWindow() -{ - -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::Init(CCTimeType curTime, uint32_t maxDatagramPayload) -{ - (void) curTime; - - RTT=UNSET_TIME_US; - MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload; - cwnd=maxDatagramPayload; - ssThresh=0.0; - oldestUnsentAck=0; - nextDatagramSequenceNumber=0; - nextCongestionControlBlock=0; - backoffThisBlock=speedUpThisBlock=false; - expectedNextSequenceNumber=0; - _isContinuousSend=false; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::Update(CCTimeType curTime, bool hasDataToSendOrResend) -{ - (void) curTime; - (void) hasDataToSendOrResend; -} -// ---------------------------------------------------------------------------------------------------------------------------- -int CCRakNetSlidingWindow::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) -{ - (void) curTime; - (void) isContinuousSend; - (void) timeSinceLastTick; - - return unacknowledgedBytes; -} -// ---------------------------------------------------------------------------------------------------------------------------- -int CCRakNetSlidingWindow::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) -{ - (void) curTime; - (void) timeSinceLastTick; - - _isContinuousSend=isContinuousSend; - - if (unacknowledgedBytes<=cwnd) - return (int) (cwnd-unacknowledgedBytes); - else - return 0; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetSlidingWindow::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick) -{ - CCTimeType rto = GetSenderRTOForACK(); - (void) estimatedTimeToNextTick; - - // iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 - if (rto==(CCTimeType) UNSET_TIME_US) - { - // Unknown how long until the remote system will retransmit, so better send right away - return true; - } - - return curTime >= oldestUnsentAck + SYN; -} -// ---------------------------------------------------------------------------------------------------------------------------- -DatagramSequenceNumberType CCRakNetSlidingWindow::GetNextDatagramSequenceNumber(void) -{ - return nextDatagramSequenceNumber; -} -// ---------------------------------------------------------------------------------------------------------------------------- -DatagramSequenceNumberType CCRakNetSlidingWindow::GetAndIncrementNextDatagramSequenceNumber(void) -{ - DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber; - nextDatagramSequenceNumber++; - return dsnt; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnSendBytes(CCTimeType curTime, uint32_t numBytes) -{ - (void) curTime; - (void) numBytes; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime) -{ - (void) curTime; - (void) sizeInBytes; - (void) datagramSequenceNumber; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetSlidingWindow::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount) -{ - (void) curTime; - (void) sizeInBytes; - (void) isContinuousSend; - - if (oldestUnsentAck==0) - oldestUnsentAck=curTime; - - if (datagramSequenceNumber==expectedNextSequenceNumber) - { - *skippedMessageCount=0; - expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; - } - else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber)) - { - *skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber; - // Sanity check, just use timeout resend if this was really valid - if (*skippedMessageCount>1000) - { - // During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice - if (*skippedMessageCount>(uint32_t)50000) - return false; - *skippedMessageCount=1000; - } - expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; - } - else - { - *skippedMessageCount=0; - } - - return true; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnResend(CCTimeType curTime) -{ - (void) curTime; - - if (_isContinuousSend && backoffThisBlock==false && cwnd>MAXIMUM_MTU_INCLUDING_UDP_HEADER*2) - { - ssThresh=cwnd/2; - if (ssThreshssThresh && ssThresh!=0) - { - cwnd=ssThresh; - cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd; - } - } - } - } - else - { - if (isNewCongestionControlPeriod) - cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd; - } -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ) -{ - (void) sequenceNumber; - - OnResend(curTime); -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS) -{ - (void) curTime; - (void) _B; - (void) _AS; - - *hasBAndAS=false; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnSendAck(CCTimeType curTime, uint32_t numBytes) -{ - (void) curTime; - (void) numBytes; - - oldestUnsentAck=0; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::OnSendNACK(CCTimeType curTime, uint32_t numBytes) -{ - (void) curTime; - (void) numBytes; - -} -// ---------------------------------------------------------------------------------------------------------------------------- -CCTimeType CCRakNetSlidingWindow::GetRTOForRetransmission(void) const -{ -#if CC_TIME_TYPE_BYTES==4 - const CCTimeType maxThreshold=2000; - const CCTimeType minThreshold=100; -#else - const CCTimeType maxThreshold=2000000; - const CCTimeType minThreshold=100000; -#endif - - if (RTT==UNSET_TIME_US) - { - return maxThreshold; - } - - if (RTT * 3 > maxThreshold) - return maxThreshold; - if (RTT * 3 < minThreshold) - return minThreshold; - return (CCTimeType) RTT * 3; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetSlidingWindow::SetMTU(uint32_t bytes) -{ - MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes; -} -// ---------------------------------------------------------------------------------------------------------------------------- -uint32_t CCRakNetSlidingWindow::GetMTU(void) const -{ - return MAXIMUM_MTU_INCLUDING_UDP_HEADER; -} -// ---------------------------------------------------------------------------------------------------------------------------- -BytesPerMicrosecond CCRakNetSlidingWindow::GetLocalReceiveRate(CCTimeType currentTime) const -{ - (void) currentTime; - - return 0; // TODO -} -// ---------------------------------------------------------------------------------------------------------------------------- -double CCRakNetSlidingWindow::GetRTT(void) const -{ - if (RTT==UNSET_TIME_US) - return 0.0; - return RTT; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetSlidingWindow::GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) -{ - // a > b? - const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2); - return b!=a && b-a>halfSpan; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetSlidingWindow::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) -{ - // a < b? - const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2; - return b!=a && b-a +#include +#include +#include "RakAssert.h" +#include "RakAlloca.h" + +using namespace RakNet; + +// ****************************************************** PUBLIC METHODS ****************************************************** + +CCRakNetSlidingWindow::CCRakNetSlidingWindow() +{ +} +// ---------------------------------------------------------------------------------------------------------------------------- +CCRakNetSlidingWindow::~CCRakNetSlidingWindow() +{ + +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::Init(CCTimeType curTime, uint32_t maxDatagramPayload) +{ + (void) curTime; + + RTT=UNSET_TIME_US; + MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload; + cwnd=maxDatagramPayload; + ssThresh=0.0; + oldestUnsentAck=0; + nextDatagramSequenceNumber=0; + nextCongestionControlBlock=0; + backoffThisBlock=speedUpThisBlock=false; + expectedNextSequenceNumber=0; + _isContinuousSend=false; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::Update(CCTimeType curTime, bool hasDataToSendOrResend) +{ + (void) curTime; + (void) hasDataToSendOrResend; +} +// ---------------------------------------------------------------------------------------------------------------------------- +int CCRakNetSlidingWindow::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) +{ + (void) curTime; + (void) isContinuousSend; + (void) timeSinceLastTick; + + return unacknowledgedBytes; +} +// ---------------------------------------------------------------------------------------------------------------------------- +int CCRakNetSlidingWindow::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) +{ + (void) curTime; + (void) timeSinceLastTick; + + _isContinuousSend=isContinuousSend; + + if (unacknowledgedBytes<=cwnd) + return (int) (cwnd-unacknowledgedBytes); + else + return 0; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetSlidingWindow::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick) +{ + CCTimeType rto = GetSenderRTOForACK(); + (void) estimatedTimeToNextTick; + + // iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 + if (rto==(CCTimeType) UNSET_TIME_US) + { + // Unknown how long until the remote system will retransmit, so better send right away + return true; + } + + return curTime >= oldestUnsentAck + SYN; +} +// ---------------------------------------------------------------------------------------------------------------------------- +DatagramSequenceNumberType CCRakNetSlidingWindow::GetNextDatagramSequenceNumber(void) +{ + return nextDatagramSequenceNumber; +} +// ---------------------------------------------------------------------------------------------------------------------------- +DatagramSequenceNumberType CCRakNetSlidingWindow::GetAndIncrementNextDatagramSequenceNumber(void) +{ + DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber; + nextDatagramSequenceNumber++; + return dsnt; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnSendBytes(CCTimeType curTime, uint32_t numBytes) +{ + (void) curTime; + (void) numBytes; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime) +{ + (void) curTime; + (void) sizeInBytes; + (void) datagramSequenceNumber; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetSlidingWindow::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount) +{ + (void) curTime; + (void) sizeInBytes; + (void) isContinuousSend; + + if (oldestUnsentAck==0) + oldestUnsentAck=curTime; + + if (datagramSequenceNumber==expectedNextSequenceNumber) + { + *skippedMessageCount=0; + expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; + } + else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber)) + { + *skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber; + // Sanity check, just use timeout resend if this was really valid + if (*skippedMessageCount>1000) + { + // During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice + if (*skippedMessageCount>(uint32_t)50000) + return false; + *skippedMessageCount=1000; + } + expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; + } + else + { + *skippedMessageCount=0; + } + + return true; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnResend(CCTimeType curTime) +{ + (void) curTime; + + if (_isContinuousSend && backoffThisBlock==false && cwnd>MAXIMUM_MTU_INCLUDING_UDP_HEADER*2) + { + ssThresh=cwnd/2; + if (ssThreshssThresh && ssThresh!=0) + { + cwnd=ssThresh; + cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd; + } + } + } + } + else + { + if (isNewCongestionControlPeriod) + cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd; + } +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ) +{ + (void) sequenceNumber; + + OnResend(curTime); +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS) +{ + (void) curTime; + (void) _B; + (void) _AS; + + *hasBAndAS=false; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnSendAck(CCTimeType curTime, uint32_t numBytes) +{ + (void) curTime; + (void) numBytes; + + oldestUnsentAck=0; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::OnSendNACK(CCTimeType curTime, uint32_t numBytes) +{ + (void) curTime; + (void) numBytes; + +} +// ---------------------------------------------------------------------------------------------------------------------------- +CCTimeType CCRakNetSlidingWindow::GetRTOForRetransmission(void) const +{ +#if CC_TIME_TYPE_BYTES==4 + const CCTimeType maxThreshold=2000; + const CCTimeType minThreshold=100; +#else + const CCTimeType maxThreshold=2000000; + const CCTimeType minThreshold=100000; +#endif + + if (RTT==UNSET_TIME_US) + { + return maxThreshold; + } + + if (RTT * 3 > maxThreshold) + return maxThreshold; + if (RTT * 3 < minThreshold) + return minThreshold; + return (CCTimeType) RTT * 3; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetSlidingWindow::SetMTU(uint32_t bytes) +{ + MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes; +} +// ---------------------------------------------------------------------------------------------------------------------------- +uint32_t CCRakNetSlidingWindow::GetMTU(void) const +{ + return MAXIMUM_MTU_INCLUDING_UDP_HEADER; +} +// ---------------------------------------------------------------------------------------------------------------------------- +BytesPerMicrosecond CCRakNetSlidingWindow::GetLocalReceiveRate(CCTimeType currentTime) const +{ + (void) currentTime; + + return 0; // TODO +} +// ---------------------------------------------------------------------------------------------------------------------------- +double CCRakNetSlidingWindow::GetRTT(void) const +{ + if (RTT==UNSET_TIME_US) + return 0.0; + return RTT; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetSlidingWindow::GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) +{ + // a > b? + const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2); + return b!=a && b-a>halfSpan; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetSlidingWindow::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) +{ + // a < b? + const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2; + return b!=a && b-a b, accounting for variable overflow? - static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); - /// Is a < b, accounting for variable overflow? - static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); -// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond); - uint64_t GetBytesPerSecondLimitByCongestionControl(void) const; - - protected: - - // Maximum amount of bytes that the user can send, e.g. the size of one full datagram - uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER; - - double RTT; - - double cwnd; // max bytes on wire - double ssThresh; // Threshhold between slow start and congestion avoidance - - /// When we get an ack, if oldestUnsentAck==0, set it to the current time - /// When we send out acks, set oldestUnsentAck to 0 - CCTimeType oldestUnsentAck; - - CCTimeType GetSenderRTOForACK(void) const; - - /// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment - DatagramSequenceNumberType nextDatagramSequenceNumber; - DatagramSequenceNumberType nextCongestionControlBlock; - bool backoffThisBlock, speedUpThisBlock; - /// Track which datagram sequence numbers have arrived. - /// If a sequence number is skipped, send a NAK for all skipped messages - DatagramSequenceNumberType expectedNextSequenceNumber; - - bool _isContinuousSend; - - bool IsInSlowStart(void) const; -}; - -} - -#endif - -#endif +/* +http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html + +cwnd=max bytes allowed on wire at once + +Start: +cwnd=mtu +ssthresh=unlimited + +Slow start: +On ack cwnd*=2 + +congestion avoidance: +On ack during new period +cwnd+=mtu*mtu/cwnd + +on loss or duplicate ack during period: +sshtresh=cwnd/2 +cwnd=MTU +This reenters slow start + +If cwnd < ssthresh, then use slow start +else use congestion avoidance + + +*/ + +#include "RakNetDefines.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1 + +#ifndef __CONGESTION_CONTROL_SLIDING_WINDOW_H +#define __CONGESTION_CONTROL_SLIDING_WINDOW_H + +#include "NativeTypes.h" +#include "RakNetTime.h" +#include "RakNetTypes.h" +#include "DS_Queue.h" + +/// Sizeof an UDP header in byte +#define UDP_HEADER_SIZE 28 + +#define CC_DEBUG_PRINTF_1(x) +#define CC_DEBUG_PRINTF_2(x,y) +#define CC_DEBUG_PRINTF_3(x,y,z) +#define CC_DEBUG_PRINTF_4(x,y,z,a) +#define CC_DEBUG_PRINTF_5(x,y,z,a,b) +//#define CC_DEBUG_PRINTF_1(x) printf(x) +//#define CC_DEBUG_PRINTF_2(x,y) printf(x,y) +//#define CC_DEBUG_PRINTF_3(x,y,z) printf(x,y,z) +//#define CC_DEBUG_PRINTF_4(x,y,z,a) printf(x,y,z,a) +//#define CC_DEBUG_PRINTF_5(x,y,z,a,b) printf(x,y,z,a,b) + +/// Set to 4 if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 +#define CC_TIME_TYPE_BYTES 8 + + +typedef RakNet::TimeUS CCTimeType; + + + + +typedef RakNet::uint24_t DatagramSequenceNumberType; +typedef double BytesPerMicrosecond; +typedef double BytesPerSecond; +typedef double MicrosecondsPerByte; + +namespace RakNet +{ + +class CCRakNetSlidingWindow +{ + public: + + CCRakNetSlidingWindow(); + ~CCRakNetSlidingWindow(); + + /// Reset all variables to their initial states, for a new connection + void Init(CCTimeType curTime, uint32_t maxDatagramPayload); + + /// Update over time + void Update(CCTimeType curTime, bool hasDataToSendOrResend); + + int GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); + int GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); + + /// Acks do not have to be sent immediately. Instead, they can be buffered up such that groups of acks are sent at a time + /// This reduces overall bandwidth usage + /// How long they can be buffered depends on the retransmit time of the sender + /// Should call once per update tick, and send if needed + bool ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick); + + /// Every data packet sent must contain a sequence number + /// Call this function to get it. The sequence number is passed into OnGotPacketPair() + DatagramSequenceNumberType GetAndIncrementNextDatagramSequenceNumber(void); + DatagramSequenceNumberType GetNextDatagramSequenceNumber(void); + + /// Call this when you send packets + /// Every 15th and 16th packets should be sent as a packet pair if possible + /// When packets marked as a packet pair arrive, pass to OnGotPacketPair() + /// When any packets arrive, (additionally) pass to OnGotPacket + /// Packets should contain our system time, so we can pass rtt to OnNonDuplicateAck() + void OnSendBytes(CCTimeType curTime, uint32_t numBytes); + + /// Call this when you get a packet pair + void OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime); + + /// Call this when you get a packet (including packet pairs) + /// If the DatagramSequenceNumberType is out of order, skippedMessageCount will be non-zero + /// In that case, send a NAK for every sequence number up to that count + bool OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount); + + /// Call when you get a NAK, with the sequence number of the lost message + /// Affects the congestion control + void OnResend(CCTimeType curTime); + void OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber); + + /// Call this when an ACK arrives. + /// hasBAndAS are possibly written with the ack, see OnSendAck() + /// B and AS are used in the calculations in UpdateWindowSizeAndAckOnAckPerSyn + /// B and AS are updated at most once per SYN + void OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ); + void OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ); + + /// Call when you send an ack, to see if the ack should have the B and AS parameters transmitted + /// Call before calling OnSendAck() + void OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS); + + /// Call when we send an ack, to write B and AS if needed + /// B and AS are only written once per SYN, to prevent slow calculations + /// Also updates SND, the period between sends, since data is written out + /// Be sure to call OnSendAckGetBAndAS() before calling OnSendAck(), since whether you write it or not affects \a numBytes + void OnSendAck(CCTimeType curTime, uint32_t numBytes); + + /// Call when we send a NACK + /// Also updates SND, the period between sends, since data is written out + void OnSendNACK(CCTimeType curTime, uint32_t numBytes); + + /// Retransmission time out for the sender + /// If the time difference between when a message was last transmitted, and the current time is greater than RTO then packet is eligible for retransmission, pending congestion control + /// RTO = (RTT + 4 * RTTVar) + SYN + /// If we have been continuously sending for the last RTO, and no ACK or NAK at all, SND*=2; + /// This is per message, which is different from UDT, but RakNet supports packetloss with continuing data where UDT is only RELIABLE_ORDERED + /// Minimum value is 100 milliseconds + CCTimeType GetRTOForRetransmission(void) const; + + /// Set the maximum amount of data that can be sent in one datagram + /// Default to MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE + void SetMTU(uint32_t bytes); + + /// Return what was set by SetMTU() + uint32_t GetMTU(void) const; + + /// Query for statistics + BytesPerMicrosecond GetLocalSendRate(void) const {return 0;} + BytesPerMicrosecond GetLocalReceiveRate(CCTimeType currentTime) const; + BytesPerMicrosecond GetRemoveReceiveRate(void) const {return 0;} + //BytesPerMicrosecond GetEstimatedBandwidth(void) const {return B;} + BytesPerMicrosecond GetEstimatedBandwidth(void) const {return GetLinkCapacityBytesPerSecond()*1000000.0;} + double GetLinkCapacityBytesPerSecond(void) const {return 0;} + + /// Query for statistics + double GetRTT(void) const; + + bool GetIsInSlowStart(void) const {return IsInSlowStart();} + uint32_t GetCWNDLimit(void) const {return (uint32_t) 0;} + + + /// Is a > b, accounting for variable overflow? + static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); + /// Is a < b, accounting for variable overflow? + static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); +// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond); + uint64_t GetBytesPerSecondLimitByCongestionControl(void) const; + + protected: + + // Maximum amount of bytes that the user can send, e.g. the size of one full datagram + uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER; + + double RTT; + + double cwnd; // max bytes on wire + double ssThresh; // Threshhold between slow start and congestion avoidance + + /// When we get an ack, if oldestUnsentAck==0, set it to the current time + /// When we send out acks, set oldestUnsentAck to 0 + CCTimeType oldestUnsentAck; + + CCTimeType GetSenderRTOForACK(void) const; + + /// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment + DatagramSequenceNumberType nextDatagramSequenceNumber; + DatagramSequenceNumberType nextCongestionControlBlock; + bool backoffThisBlock, speedUpThisBlock; + /// Track which datagram sequence numbers have arrived. + /// If a sequence number is skipped, send a NAK for all skipped messages + DatagramSequenceNumberType expectedNextSequenceNumber; + + bool _isContinuousSend; + + bool IsInSlowStart(void) const; +}; + +} + +#endif + +#endif diff --git a/src/raknet/CCRakNetUDT.cpp b/src/raknet/CCRakNetUDT.cpp index dcf942e..c6a8c79 100755 --- a/src/raknet/CCRakNetUDT.cpp +++ b/src/raknet/CCRakNetUDT.cpp @@ -1,798 +1,798 @@ -#include "CCRakNetUDT.h" - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 - -#include "Rand.h" -#include "MTUSize.h" -#include -#include -#include -//#include -#include "RakAssert.h" -#include "RakAlloca.h" - -using namespace RakNet; - -static const double UNSET_TIME_US=-1; -static const double CWND_MIN_THRESHOLD=2.0; -static const double UNDEFINED_TRANSFER_RATE=0.0; -/// Interval at which to update aspects of the system -/// 1. send acks -/// 2. update time interval between outgoing packets -/// 3, Yodate retransmit timeout -#if CC_TIME_TYPE_BYTES==4 -static const CCTimeType SYN=10; -#else -static const CCTimeType SYN=10000; -#endif - -#if CC_TIME_TYPE_BYTES==4 -#define MAX_RTT 1000 -#define RTT_TOLERANCE 30 -#else -#define MAX_RTT 1000000 -#define RTT_TOLERANCE 30000 -#endif - - -double RTTVarMultiple=4.0; - - -// ****************************************************** PUBLIC METHODS ****************************************************** - -CCRakNetUDT::CCRakNetUDT() -{ -} - -// ---------------------------------------------------------------------------------------------------------------------------- - -CCRakNetUDT::~CCRakNetUDT() -{ -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::Init(CCTimeType curTime, uint32_t maxDatagramPayload) -{ - (void) curTime; - - nextSYNUpdate=0; - packetPairRecieptHistoryWriteIndex=0; - packetArrivalHistoryWriteIndex=0; - packetArrivalHistoryWriteCount=0; - RTT=UNSET_TIME_US; - // RTTVar=UNSET_TIME_US; - isInSlowStart=true; - NAKCount=1000; - AvgNAKNum=1; - DecInterval=1; - DecCount=0; - nextDatagramSequenceNumber=0; - lastPacketPairPacketArrivalTime=0; - lastPacketPairSequenceNumber=(DatagramSequenceNumberType)(const uint32_t)-1; - lastPacketArrivalTime=0; - CWND=CWND_MIN_THRESHOLD; - lastUpdateWindowSizeAndAck=0; - lastTransmitOfBAndAS=0; - ExpCount=1.0; - totalUserDataBytesSent=0; - oldestUnsentAck=0; - MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload; - CWND_MAX_THRESHOLD=RESEND_BUFFER_ARRAY_LENGTH; -#if CC_TIME_TYPE_BYTES==4 - const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) 3.6; -#else - const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) .0036; -#endif - -#if CC_TIME_TYPE_BYTES==4 - lastRttOnIncreaseSendRate=1000; -#else - lastRttOnIncreaseSendRate=1000000; -#endif - nextCongestionControlBlock=0; - lastRtt=0; - - // B=DEFAULT_TRANSFER_RATE; - AS=UNDEFINED_TRANSFER_RATE; - const MicrosecondsPerByte DEFAULT_BYTE_INTERVAL=(MicrosecondsPerByte) (1.0/DEFAULT_TRANSFER_RATE); - SND=DEFAULT_BYTE_INTERVAL; - expectedNextSequenceNumber=0; - sendBAndASCount=0; - packetArrivalHistoryContinuousGapsIndex=0; - //packetPairRecipetHistoryGapsIndex=0; - hasWrittenToPacketPairReceiptHistory=false; - InitPacketArrivalHistory(); - - estimatedLinkCapacityBytesPerSecond=0; - bytesCanSendThisTick=0; - hadPacketlossThisBlock=false; - pingsLastInterval.Clear(__FILE__,__LINE__); -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::SetMTU(uint32_t bytes) -{ - MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes; -} -// ---------------------------------------------------------------------------------------------------------------------------- -uint32_t CCRakNetUDT::GetMTU(void) const -{ - return MAXIMUM_MTU_INCLUDING_UDP_HEADER; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::Update(CCTimeType curTime, bool hasDataToSendOrResend) -{ - (void) hasDataToSendOrResend; - (void) curTime; - - return; - - // I suspect this is causing major lag - - /* - if (hasDataToSendOrResend==false) - halveSNDOnNoDataTime=0; - else if (halveSNDOnNoDataTime==0) - { - UpdateHalveSNDOnNoDataTime(curTime); - ExpCount=1.0; - } - - // If you send, and get no data at all from that time to RTO, then halve send rate7 - if (HasHalveSNDOnNoDataTimeElapsed(curTime)) - { - /// 2000 bytes per second - /// 0.0005 seconds per byte - /// 0.5 milliseconds per byte - /// 500 microseconds per byte - // printf("No incoming data, halving send rate\n"); - SND*=2.0; - CapMinSnd(_FILE_AND_LINE_); - ExpCount+=1.0; - if (ExpCount>8.0) - ExpCount=8.0; - - UpdateHalveSNDOnNoDataTime(curTime); - } - */ -} -// ---------------------------------------------------------------------------------------------------------------------------- -int CCRakNetUDT::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) -{ - (void) curTime; - - if (isInSlowStart) - { - uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER); - return CWNDLimit; - } - return GetTransmissionBandwidth(curTime,timeSinceLastTick,unacknowledgedBytes,isContinuousSend); -} -// ---------------------------------------------------------------------------------------------------------------------------- -int CCRakNetUDT::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) -{ - (void) curTime; - - if (isInSlowStart) - { - uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER-unacknowledgedBytes); - return CWNDLimit; - } - if (bytesCanSendThisTick>0) - bytesCanSendThisTick=0; - -#if CC_TIME_TYPE_BYTES==4 - if (isContinuousSend==false && timeSinceLastTick>100) - timeSinceLastTick=100; -#else - if (isContinuousSend==false && timeSinceLastTick>100000) - timeSinceLastTick=100000; -#endif - - bytesCanSendThisTick=(int)((double)timeSinceLastTick*((double)1.0/SND)+(double)bytesCanSendThisTick); - if (bytesCanSendThisTick>0) - return bytesCanSendThisTick; - return 0; -} -uint64_t CCRakNetUDT::GetBytesPerSecondLimitByCongestionControl(void) const -{ - if (isInSlowStart) - return 0; -#if CC_TIME_TYPE_BYTES==4 - return (uint64_t) ((double)1.0/(SND*1000.0)); -#else - return (uint64_t) ((double)1.0/(SND*1000000.0)); -#endif -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetUDT::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick) -{ - CCTimeType rto = GetSenderRTOForACK(); - - // iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 - if (rto==(CCTimeType) UNSET_TIME_US) - { - // Unknown how long until the remote system will retransmit, so better send right away - return true; - } - - - // CCTimeType remoteRetransmitTime=oldestUnsentAck+rto-RTT*.5; - // CCTimeType ackArrivalTimeIfWeDelay=RTT*.5+estimatedTimeToNextTick+curTime; - // return ackArrivalTimeIfWeDelay= oldestUnsentAck + SYN || - estimatedTimeToNextTick+curTime < oldestUnsentAck+rto-RTT; -} -// ---------------------------------------------------------------------------------------------------------------------------- -DatagramSequenceNumberType CCRakNetUDT::GetNextDatagramSequenceNumber(void) -{ - return nextDatagramSequenceNumber; -} -// ---------------------------------------------------------------------------------------------------------------------------- -DatagramSequenceNumberType CCRakNetUDT::GetAndIncrementNextDatagramSequenceNumber(void) -{ - DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber; - nextDatagramSequenceNumber++; - return dsnt; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnSendBytes(CCTimeType curTime, uint32_t numBytes) -{ - (void) curTime; - - totalUserDataBytesSent+=numBytes; - if (isInSlowStart==false) - bytesCanSendThisTick-=numBytes; -} - -// ****************************************************** PROTECTED METHODS ****************************************************** - -void CCRakNetUDT::SetNextSYNUpdate(CCTimeType currentTime) -{ - nextSYNUpdate+=SYN; - if (nextSYNUpdate < currentTime) - nextSYNUpdate=currentTime+SYN; -} -// ---------------------------------------------------------------------------------------------------------------------------- -BytesPerMicrosecond CCRakNetUDT::ReceiverCalculateDataArrivalRate(CCTimeType curTime) const -{ - (void) curTime; - // Not an instantaneous measurement - /* - if (continuousBytesReceivedStartTime!=0 && curTime>continuousBytesReceivedStartTime) - { - #if CC_TIME_TYPE_BYTES==4 - const CCTimeType threshold=100; - #else - const CCTimeType threshold=100000; - #endif - if (curTime-continuousBytesReceivedStartTime>threshold) - return (BytesPerMicrosecond) continuousBytesReceived/(BytesPerMicrosecond) (curTime-continuousBytesReceivedStartTime); - } - - return UNDEFINED_TRANSFER_RATE; - */ - - - if (packetArrivalHistoryWriteCount=oneEighthMedian && - packetArrivalHistory[i] b? - const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2); - return b!=a && b-a>halfSpan; -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetUDT::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) -{ - // a < b? - const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2; - return b!=a && b-amaxThreshold) - return maxThreshold; - return ret; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnResend(CCTimeType curTime) -{ - (void) curTime; - - if (isInSlowStart) - { - if (AS!=UNDEFINED_TRANSFER_RATE) - EndSlowStart(); - return; - } - - if (hadPacketlossThisBlock==false) - { - // Logging - // printf("Sending SLOWER due to Resend, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt ); - - IncreaseTimeBetweenSends(); - hadPacketlossThisBlock=true; - } -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber) -{ - (void) nakSequenceNumber; - (void) curTime; - - if (isInSlowStart) - { - if (AS!=UNDEFINED_TRANSFER_RATE) - EndSlowStart(); - return; - } - - if (hadPacketlossThisBlock==false) - { - // Logging - //printf("Sending SLOWER due to NAK, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt ); - if (pingsLastInterval.Size()>10) - { - for (int i=0; i < 10; i++) - printf("%i, ", pingsLastInterval[pingsLastInterval.Size()-1-i]/1000); - } - printf("\n"); - IncreaseTimeBetweenSends(); - - hadPacketlossThisBlock=true; - } -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::EndSlowStart(void) -{ - RakAssert(isInSlowStart==true); - RakAssert(AS!=UNDEFINED_TRANSFER_RATE); - - // This overestimates - estimatedLinkCapacityBytesPerSecond=AS * 1000000.0; - - isInSlowStart=false; - SND=1.0/AS; - CapMinSnd(_FILE_AND_LINE_); - - // printf("ENDING SLOW START\n"); -#if CC_TIME_TYPE_BYTES==4 - // printf("Initial SND=%f Kilobytes per second\n", 1.0/SND); -#else - // printf("Initial SND=%f Megabytes per second\n", 1.0/SND); -#endif - if (SND > .1) - PrintLowBandwidthWarning(); -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime) -{ - (void) datagramSequenceNumber; - (void) sizeInBytes; - (void) curTime; - -} -// ---------------------------------------------------------------------------------------------------------------------------- -bool CCRakNetUDT::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount) -{ - CC_DEBUG_PRINTF_2("R%i ",datagramSequenceNumber.val); - - if (datagramSequenceNumber==expectedNextSequenceNumber) - { - *skippedMessageCount=0; - expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; - } - else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber)) - { - *skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber; - // Sanity check, just use timeout resend if this was really valid - if (*skippedMessageCount>1000) - { - // During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice - if (*skippedMessageCount>(uint32_t)50000) - return false; - *skippedMessageCount=1000; - } - expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; - } - else - { - *skippedMessageCount=0; - } - - if (curTime>lastPacketArrivalTime) - { - CCTimeType interval = curTime-lastPacketArrivalTime; - - // printf("Packet arrival gap is %I64u\n", (interval)); - - if (isContinuousSend) - { - continuousBytesReceived+=sizeInBytes; - if (continuousBytesReceivedStartTime==0) - continuousBytesReceivedStartTime=lastPacketArrivalTime; - - - mostRecentPacketArrivalHistory=(BytesPerMicrosecond)sizeInBytes/(BytesPerMicrosecond)interval; - - // if (mostRecentPacketArrivalHistory < (BytesPerMicrosecond)0.0035) - // { - // printf("%s:%i LIKELY BUG: Calculated packetArrivalHistory is below 28.8 Kbps modem\nReport to rakkar@jenkinssoftware.com with file and line number\n", _FILE_AND_LINE_); - // } - - packetArrivalHistoryContinuousGaps[packetArrivalHistoryContinuousGapsIndex++]=(int) interval; - packetArrivalHistoryContinuousGapsIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1); - - packetArrivalHistoryWriteCount++; - packetArrivalHistory[packetArrivalHistoryWriteIndex++]=mostRecentPacketArrivalHistory; - // Wrap to 0 at the end of the range - // Assumes power of 2 for CC_RAKNET_UDT_PACKET_HISTORY_LENGTH - packetArrivalHistoryWriteIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1); - } - else - { - continuousBytesReceivedStartTime=0; - continuousBytesReceived=0; - } - - lastPacketArrivalTime=curTime; - } - return true; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ) -{ -#if CC_TIME_TYPE_BYTES==4 - RakAssert(rtt < 10000); -#else - RakAssert(rtt < 10000000); -#endif - (void) _B; - - if (hasBAndAS) - { - /// RakAssert(_B!=UNDEFINED_TRANSFER_RATE && _AS!=UNDEFINED_TRANSFER_RATE); - // B=B * .875 + _B * .125; - // AS is packet arrival rate - RakAssert(_AS!=UNDEFINED_TRANSFER_RATE); - AS=_AS; - CC_DEBUG_PRINTF_4("ArrivalRate=%f linkCap=%f incomingLinkCap=%f\n", _AS,B,_B); - } - - if (oldestUnsentAck==0) - oldestUnsentAck=curTime; - - if (isInSlowStart==true) - { - nextCongestionControlBlock=nextDatagramSequenceNumber; - lastRttOnIncreaseSendRate=rtt; - UpdateWindowSizeAndAckOnAckPreSlowStart(totalUserDataBytesAcked); - } - else - { - UpdateWindowSizeAndAckOnAckPerSyn(curTime, rtt, isContinuousSend, sequenceNumber); - } - - lastUpdateWindowSizeAndAck=curTime; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS) -{ - if (curTime>lastTransmitOfBAndAS+SYN) - { - *_B=0; - *_AS=ReceiverCalculateDataArrivalRate(curTime); - - if (*_AS==UNDEFINED_TRANSFER_RATE) - { - *hasBAndAS=false; - } - else - { - *hasBAndAS=true; - } - } - else - { - *hasBAndAS=false; - } -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnSendAck(CCTimeType curTime, uint32_t numBytes) -{ - (void) numBytes; - (void) curTime; - - // This is not accounted for on the remote system, and thus causes bandwidth to be underutilized - //UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE); - - oldestUnsentAck=0; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::OnSendNACK(CCTimeType curTime, uint32_t numBytes) -{ - (void) numBytes; - (void) curTime; - - // This is not accounted for on the remote system, and thus causes bandwidth to be underutilized - // UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE); -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked) -{ - // During slow start, max window size is the number of full packets that have been sent out - // CWND=(double) ((double)totalUserDataBytesSent/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER); - CC_DEBUG_PRINTF_3("CWND increasing from %f to %f\n", CWND, (double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER)); - CWND=(double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER); - if (CWND>=CWND_MAX_THRESHOLD) - { - CWND=CWND_MAX_THRESHOLD; - - if (AS!=UNDEFINED_TRANSFER_RATE) - EndSlowStart(); - } - if (CWNDintervalSize) - pingsLastInterval.Pop(); - if (GreaterThan(sequenceNumber, nextCongestionControlBlock) && - sequenceNumber-nextCongestionControlBlock>=intervalSize && - pingsLastInterval.Size()==intervalSize) - { - double slopeSum=0.0; - double average=(double) pingsLastInterval[0]; - int sampleSize=pingsLastInterval.Size(); - for (int i=1; i < sampleSize; i++) - { - slopeSum+=(double)pingsLastInterval[i]-(double)pingsLastInterval[i-1]; - average+=pingsLastInterval[i]; - } - average/=sampleSize; - - if (hadPacketlossThisBlock==true) - { - } - else if (slopeSum < -.10*average) - { - // Logging - //printf("Ping dropping. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); - } - else if (slopeSum > .10*average) - { - // Logging - //printf("Ping rising. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); - IncreaseTimeBetweenSends(); - } - else - { - // Logging - //printf("Ping stable. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); - - // No packetloss over time threshhold, and rtt decreased, so send faster - lastRttOnIncreaseSendRate=rtt; - DecreaseTimeBetweenSends(); - } - - pingsLastInterval.Clear(__FILE__,__LINE__); - hadPacketlossThisBlock=false; - nextCongestionControlBlock=nextDatagramSequenceNumber; - } - - lastRtt=rtt; -} - -// ---------------------------------------------------------------------------------------------------------------------------- -double CCRakNetUDT::BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in) -{ -#if CC_TIME_TYPE_BYTES==4 - const BytesPerMicrosecond factor = 1.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER; -#else - const BytesPerMicrosecond factor = 1000.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER; -#endif - return in * factor; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::InitPacketArrivalHistory(void) -{ - unsigned int i; - for (i=0; i < CC_RAKNET_UDT_PACKET_HISTORY_LENGTH; i++) - { - packetArrivalHistory[i]=UNDEFINED_TRANSFER_RATE; - packetArrivalHistoryContinuousGaps[i]=0; - } - - packetArrivalHistoryWriteCount=0; - continuousBytesReceived=0; - continuousBytesReceivedStartTime=0; -} -// ---------------------------------------------------------------------------------------------------------------------------- -void CCRakNetUDT::PrintLowBandwidthWarning(void) -{ - - /* - printf("\n-------LOW BANDWIDTH -----\n"); - if (isInSlowStart==false) - printf("SND=%f Megabytes per second\n", 1.0/SND); - printf("Window size=%f\n", CWND); - printf("Pipe from packet pair = %f megabytes per second\n", B); - printf("RTT=%f milliseconds\n", RTT/1000.0); - printf("RTT Variance=%f milliseconds\n", RTTVar/1000.0); - printf("Retransmission=%i milliseconds\n", GetRTOForRetransmission()/1000); - printf("Packet arrival rate on the remote system=%f megabytes per second\n", AS); - printf("Packet arrival rate on our system=%f megabytes per second\n", ReceiverCalculateDataArrivalRate()); - printf("isInSlowStart=%i\n", isInSlowStart); - printf("---------------\n"); - */ -} -BytesPerMicrosecond CCRakNetUDT::GetLocalReceiveRate(CCTimeType currentTime) const -{ - return ReceiverCalculateDataArrivalRate(currentTime); -} -double CCRakNetUDT::GetRTT(void) const -{ - if (RTT==UNSET_TIME_US) - return 0.0; - return RTT; -} -void CCRakNetUDT::CapMinSnd(const char *file, int line) -{ - (void) file; - (void) line; - - if (SND > 500) - { - SND=500; - CC_DEBUG_PRINTF_3("%s:%i LIKELY BUG: SND has gotten above 500 microseconds between messages (28.8 modem)\nReport to rakkar@jenkinssoftware.com with file and line number\n", file, line); - } -} -void CCRakNetUDT::IncreaseTimeBetweenSends(void) -{ - // In order to converge, bigger numbers have to increase slower and decrease faster - // SND==500 then increment is .02 - // SND==0 then increment is near 0 - // (SND+1.0) brings it to the range of 1 to 501 - // Square the number, which is the range of 1 to 251001 - // Divide by 251001, which is the range of 1/251001 to 1 - - double increment; - increment = .02 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ; - // SND=500 then increment=.02 - // SND=0 then increment=near 0 - SND*=(1.02 - increment); - - // SND=0 then fast increase, slow decrease - // SND=500 then slow increase, fast decrease - CapMinSnd(__FILE__,__LINE__); -} -void CCRakNetUDT::DecreaseTimeBetweenSends(void) -{ - double increment; - increment = .01 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ; - // SND=500 then increment=.01 - // SND=0 then increment=near 0 - SND*=(.99 - increment); -} -/* -void CCRakNetUDT::SetTimeBetweenSendsLimit(unsigned int bitsPerSecond) -{ -// bitsPerSecond / 1000000 = bitsPerMicrosecond -// bitsPerMicrosecond / 8 = BytesPerMicrosecond -// 1 / BytesPerMicrosecond = MicrosecondsPerByte -// 1 / ( (bitsPerSecond / 1000000) / 8 ) = -// 1 / (bitsPerSecond / 8000000) = -// 8000000 / bitsPerSecond - -#if CC_TIME_TYPE_BYTES==4 - MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000 / (MicrosecondsPerByte)bitsPerSecond; -#else - MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000000 / (MicrosecondsPerByte)bitsPerSecond; -#endif - if (limit > SND) - SND=limit; -} -*/ - -#endif +#include "CCRakNetUDT.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 + +#include "Rand.h" +#include "MTUSize.h" +#include +#include +#include +//#include +#include "RakAssert.h" +#include "RakAlloca.h" + +using namespace RakNet; + +static const double UNSET_TIME_US=-1; +static const double CWND_MIN_THRESHOLD=2.0; +static const double UNDEFINED_TRANSFER_RATE=0.0; +/// Interval at which to update aspects of the system +/// 1. send acks +/// 2. update time interval between outgoing packets +/// 3, Yodate retransmit timeout +#if CC_TIME_TYPE_BYTES==4 +static const CCTimeType SYN=10; +#else +static const CCTimeType SYN=10000; +#endif + +#if CC_TIME_TYPE_BYTES==4 +#define MAX_RTT 1000 +#define RTT_TOLERANCE 30 +#else +#define MAX_RTT 1000000 +#define RTT_TOLERANCE 30000 +#endif + + +double RTTVarMultiple=4.0; + + +// ****************************************************** PUBLIC METHODS ****************************************************** + +CCRakNetUDT::CCRakNetUDT() +{ +} + +// ---------------------------------------------------------------------------------------------------------------------------- + +CCRakNetUDT::~CCRakNetUDT() +{ +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::Init(CCTimeType curTime, uint32_t maxDatagramPayload) +{ + (void) curTime; + + nextSYNUpdate=0; + packetPairRecieptHistoryWriteIndex=0; + packetArrivalHistoryWriteIndex=0; + packetArrivalHistoryWriteCount=0; + RTT=UNSET_TIME_US; + // RTTVar=UNSET_TIME_US; + isInSlowStart=true; + NAKCount=1000; + AvgNAKNum=1; + DecInterval=1; + DecCount=0; + nextDatagramSequenceNumber=0; + lastPacketPairPacketArrivalTime=0; + lastPacketPairSequenceNumber=(DatagramSequenceNumberType)(const uint32_t)-1; + lastPacketArrivalTime=0; + CWND=CWND_MIN_THRESHOLD; + lastUpdateWindowSizeAndAck=0; + lastTransmitOfBAndAS=0; + ExpCount=1.0; + totalUserDataBytesSent=0; + oldestUnsentAck=0; + MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload; + CWND_MAX_THRESHOLD=RESEND_BUFFER_ARRAY_LENGTH; +#if CC_TIME_TYPE_BYTES==4 + const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) 3.6; +#else + const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) .0036; +#endif + +#if CC_TIME_TYPE_BYTES==4 + lastRttOnIncreaseSendRate=1000; +#else + lastRttOnIncreaseSendRate=1000000; +#endif + nextCongestionControlBlock=0; + lastRtt=0; + + // B=DEFAULT_TRANSFER_RATE; + AS=UNDEFINED_TRANSFER_RATE; + const MicrosecondsPerByte DEFAULT_BYTE_INTERVAL=(MicrosecondsPerByte) (1.0/DEFAULT_TRANSFER_RATE); + SND=DEFAULT_BYTE_INTERVAL; + expectedNextSequenceNumber=0; + sendBAndASCount=0; + packetArrivalHistoryContinuousGapsIndex=0; + //packetPairRecipetHistoryGapsIndex=0; + hasWrittenToPacketPairReceiptHistory=false; + InitPacketArrivalHistory(); + + estimatedLinkCapacityBytesPerSecond=0; + bytesCanSendThisTick=0; + hadPacketlossThisBlock=false; + pingsLastInterval.Clear(__FILE__,__LINE__); +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::SetMTU(uint32_t bytes) +{ + MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes; +} +// ---------------------------------------------------------------------------------------------------------------------------- +uint32_t CCRakNetUDT::GetMTU(void) const +{ + return MAXIMUM_MTU_INCLUDING_UDP_HEADER; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::Update(CCTimeType curTime, bool hasDataToSendOrResend) +{ + (void) hasDataToSendOrResend; + (void) curTime; + + return; + + // I suspect this is causing major lag + + /* + if (hasDataToSendOrResend==false) + halveSNDOnNoDataTime=0; + else if (halveSNDOnNoDataTime==0) + { + UpdateHalveSNDOnNoDataTime(curTime); + ExpCount=1.0; + } + + // If you send, and get no data at all from that time to RTO, then halve send rate7 + if (HasHalveSNDOnNoDataTimeElapsed(curTime)) + { + /// 2000 bytes per second + /// 0.0005 seconds per byte + /// 0.5 milliseconds per byte + /// 500 microseconds per byte + // printf("No incoming data, halving send rate\n"); + SND*=2.0; + CapMinSnd(_FILE_AND_LINE_); + ExpCount+=1.0; + if (ExpCount>8.0) + ExpCount=8.0; + + UpdateHalveSNDOnNoDataTime(curTime); + } + */ +} +// ---------------------------------------------------------------------------------------------------------------------------- +int CCRakNetUDT::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) +{ + (void) curTime; + + if (isInSlowStart) + { + uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER); + return CWNDLimit; + } + return GetTransmissionBandwidth(curTime,timeSinceLastTick,unacknowledgedBytes,isContinuousSend); +} +// ---------------------------------------------------------------------------------------------------------------------------- +int CCRakNetUDT::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend) +{ + (void) curTime; + + if (isInSlowStart) + { + uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER-unacknowledgedBytes); + return CWNDLimit; + } + if (bytesCanSendThisTick>0) + bytesCanSendThisTick=0; + +#if CC_TIME_TYPE_BYTES==4 + if (isContinuousSend==false && timeSinceLastTick>100) + timeSinceLastTick=100; +#else + if (isContinuousSend==false && timeSinceLastTick>100000) + timeSinceLastTick=100000; +#endif + + bytesCanSendThisTick=(int)((double)timeSinceLastTick*((double)1.0/SND)+(double)bytesCanSendThisTick); + if (bytesCanSendThisTick>0) + return bytesCanSendThisTick; + return 0; +} +uint64_t CCRakNetUDT::GetBytesPerSecondLimitByCongestionControl(void) const +{ + if (isInSlowStart) + return 0; +#if CC_TIME_TYPE_BYTES==4 + return (uint64_t) ((double)1.0/(SND*1000.0)); +#else + return (uint64_t) ((double)1.0/(SND*1000000.0)); +#endif +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetUDT::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick) +{ + CCTimeType rto = GetSenderRTOForACK(); + + // iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 + if (rto==(CCTimeType) UNSET_TIME_US) + { + // Unknown how long until the remote system will retransmit, so better send right away + return true; + } + + + // CCTimeType remoteRetransmitTime=oldestUnsentAck+rto-RTT*.5; + // CCTimeType ackArrivalTimeIfWeDelay=RTT*.5+estimatedTimeToNextTick+curTime; + // return ackArrivalTimeIfWeDelay= oldestUnsentAck + SYN || + estimatedTimeToNextTick+curTime < oldestUnsentAck+rto-RTT; +} +// ---------------------------------------------------------------------------------------------------------------------------- +DatagramSequenceNumberType CCRakNetUDT::GetNextDatagramSequenceNumber(void) +{ + return nextDatagramSequenceNumber; +} +// ---------------------------------------------------------------------------------------------------------------------------- +DatagramSequenceNumberType CCRakNetUDT::GetAndIncrementNextDatagramSequenceNumber(void) +{ + DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber; + nextDatagramSequenceNumber++; + return dsnt; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnSendBytes(CCTimeType curTime, uint32_t numBytes) +{ + (void) curTime; + + totalUserDataBytesSent+=numBytes; + if (isInSlowStart==false) + bytesCanSendThisTick-=numBytes; +} + +// ****************************************************** PROTECTED METHODS ****************************************************** + +void CCRakNetUDT::SetNextSYNUpdate(CCTimeType currentTime) +{ + nextSYNUpdate+=SYN; + if (nextSYNUpdate < currentTime) + nextSYNUpdate=currentTime+SYN; +} +// ---------------------------------------------------------------------------------------------------------------------------- +BytesPerMicrosecond CCRakNetUDT::ReceiverCalculateDataArrivalRate(CCTimeType curTime) const +{ + (void) curTime; + // Not an instantaneous measurement + /* + if (continuousBytesReceivedStartTime!=0 && curTime>continuousBytesReceivedStartTime) + { + #if CC_TIME_TYPE_BYTES==4 + const CCTimeType threshold=100; + #else + const CCTimeType threshold=100000; + #endif + if (curTime-continuousBytesReceivedStartTime>threshold) + return (BytesPerMicrosecond) continuousBytesReceived/(BytesPerMicrosecond) (curTime-continuousBytesReceivedStartTime); + } + + return UNDEFINED_TRANSFER_RATE; + */ + + + if (packetArrivalHistoryWriteCount=oneEighthMedian && + packetArrivalHistory[i] b? + const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2); + return b!=a && b-a>halfSpan; +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetUDT::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b) +{ + // a < b? + const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2; + return b!=a && b-amaxThreshold) + return maxThreshold; + return ret; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnResend(CCTimeType curTime) +{ + (void) curTime; + + if (isInSlowStart) + { + if (AS!=UNDEFINED_TRANSFER_RATE) + EndSlowStart(); + return; + } + + if (hadPacketlossThisBlock==false) + { + // Logging + // printf("Sending SLOWER due to Resend, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt ); + + IncreaseTimeBetweenSends(); + hadPacketlossThisBlock=true; + } +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber) +{ + (void) nakSequenceNumber; + (void) curTime; + + if (isInSlowStart) + { + if (AS!=UNDEFINED_TRANSFER_RATE) + EndSlowStart(); + return; + } + + if (hadPacketlossThisBlock==false) + { + // Logging + //printf("Sending SLOWER due to NAK, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt ); + if (pingsLastInterval.Size()>10) + { + for (int i=0; i < 10; i++) + printf("%i, ", pingsLastInterval[pingsLastInterval.Size()-1-i]/1000); + } + printf("\n"); + IncreaseTimeBetweenSends(); + + hadPacketlossThisBlock=true; + } +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::EndSlowStart(void) +{ + RakAssert(isInSlowStart==true); + RakAssert(AS!=UNDEFINED_TRANSFER_RATE); + + // This overestimates + estimatedLinkCapacityBytesPerSecond=AS * 1000000.0; + + isInSlowStart=false; + SND=1.0/AS; + CapMinSnd(_FILE_AND_LINE_); + + // printf("ENDING SLOW START\n"); +#if CC_TIME_TYPE_BYTES==4 + // printf("Initial SND=%f Kilobytes per second\n", 1.0/SND); +#else + // printf("Initial SND=%f Megabytes per second\n", 1.0/SND); +#endif + if (SND > .1) + PrintLowBandwidthWarning(); +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime) +{ + (void) datagramSequenceNumber; + (void) sizeInBytes; + (void) curTime; + +} +// ---------------------------------------------------------------------------------------------------------------------------- +bool CCRakNetUDT::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount) +{ + CC_DEBUG_PRINTF_2("R%i ",datagramSequenceNumber.val); + + if (datagramSequenceNumber==expectedNextSequenceNumber) + { + *skippedMessageCount=0; + expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; + } + else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber)) + { + *skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber; + // Sanity check, just use timeout resend if this was really valid + if (*skippedMessageCount>1000) + { + // During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice + if (*skippedMessageCount>(uint32_t)50000) + return false; + *skippedMessageCount=1000; + } + expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1; + } + else + { + *skippedMessageCount=0; + } + + if (curTime>lastPacketArrivalTime) + { + CCTimeType interval = curTime-lastPacketArrivalTime; + + // printf("Packet arrival gap is %I64u\n", (interval)); + + if (isContinuousSend) + { + continuousBytesReceived+=sizeInBytes; + if (continuousBytesReceivedStartTime==0) + continuousBytesReceivedStartTime=lastPacketArrivalTime; + + + mostRecentPacketArrivalHistory=(BytesPerMicrosecond)sizeInBytes/(BytesPerMicrosecond)interval; + + // if (mostRecentPacketArrivalHistory < (BytesPerMicrosecond)0.0035) + // { + // printf("%s:%i LIKELY BUG: Calculated packetArrivalHistory is below 28.8 Kbps modem\nReport to rakkar@jenkinssoftware.com with file and line number\n", _FILE_AND_LINE_); + // } + + packetArrivalHistoryContinuousGaps[packetArrivalHistoryContinuousGapsIndex++]=(int) interval; + packetArrivalHistoryContinuousGapsIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1); + + packetArrivalHistoryWriteCount++; + packetArrivalHistory[packetArrivalHistoryWriteIndex++]=mostRecentPacketArrivalHistory; + // Wrap to 0 at the end of the range + // Assumes power of 2 for CC_RAKNET_UDT_PACKET_HISTORY_LENGTH + packetArrivalHistoryWriteIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1); + } + else + { + continuousBytesReceivedStartTime=0; + continuousBytesReceived=0; + } + + lastPacketArrivalTime=curTime; + } + return true; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ) +{ +#if CC_TIME_TYPE_BYTES==4 + RakAssert(rtt < 10000); +#else + RakAssert(rtt < 10000000); +#endif + (void) _B; + + if (hasBAndAS) + { + /// RakAssert(_B!=UNDEFINED_TRANSFER_RATE && _AS!=UNDEFINED_TRANSFER_RATE); + // B=B * .875 + _B * .125; + // AS is packet arrival rate + RakAssert(_AS!=UNDEFINED_TRANSFER_RATE); + AS=_AS; + CC_DEBUG_PRINTF_4("ArrivalRate=%f linkCap=%f incomingLinkCap=%f\n", _AS,B,_B); + } + + if (oldestUnsentAck==0) + oldestUnsentAck=curTime; + + if (isInSlowStart==true) + { + nextCongestionControlBlock=nextDatagramSequenceNumber; + lastRttOnIncreaseSendRate=rtt; + UpdateWindowSizeAndAckOnAckPreSlowStart(totalUserDataBytesAcked); + } + else + { + UpdateWindowSizeAndAckOnAckPerSyn(curTime, rtt, isContinuousSend, sequenceNumber); + } + + lastUpdateWindowSizeAndAck=curTime; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS) +{ + if (curTime>lastTransmitOfBAndAS+SYN) + { + *_B=0; + *_AS=ReceiverCalculateDataArrivalRate(curTime); + + if (*_AS==UNDEFINED_TRANSFER_RATE) + { + *hasBAndAS=false; + } + else + { + *hasBAndAS=true; + } + } + else + { + *hasBAndAS=false; + } +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnSendAck(CCTimeType curTime, uint32_t numBytes) +{ + (void) numBytes; + (void) curTime; + + // This is not accounted for on the remote system, and thus causes bandwidth to be underutilized + //UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE); + + oldestUnsentAck=0; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::OnSendNACK(CCTimeType curTime, uint32_t numBytes) +{ + (void) numBytes; + (void) curTime; + + // This is not accounted for on the remote system, and thus causes bandwidth to be underutilized + // UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE); +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked) +{ + // During slow start, max window size is the number of full packets that have been sent out + // CWND=(double) ((double)totalUserDataBytesSent/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER); + CC_DEBUG_PRINTF_3("CWND increasing from %f to %f\n", CWND, (double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER)); + CWND=(double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER); + if (CWND>=CWND_MAX_THRESHOLD) + { + CWND=CWND_MAX_THRESHOLD; + + if (AS!=UNDEFINED_TRANSFER_RATE) + EndSlowStart(); + } + if (CWNDintervalSize) + pingsLastInterval.Pop(); + if (GreaterThan(sequenceNumber, nextCongestionControlBlock) && + sequenceNumber-nextCongestionControlBlock>=intervalSize && + pingsLastInterval.Size()==intervalSize) + { + double slopeSum=0.0; + double average=(double) pingsLastInterval[0]; + int sampleSize=pingsLastInterval.Size(); + for (int i=1; i < sampleSize; i++) + { + slopeSum+=(double)pingsLastInterval[i]-(double)pingsLastInterval[i-1]; + average+=pingsLastInterval[i]; + } + average/=sampleSize; + + if (hadPacketlossThisBlock==true) + { + } + else if (slopeSum < -.10*average) + { + // Logging + //printf("Ping dropping. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); + } + else if (slopeSum > .10*average) + { + // Logging + //printf("Ping rising. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); + IncreaseTimeBetweenSends(); + } + else + { + // Logging + //printf("Ping stable. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt ); + + // No packetloss over time threshhold, and rtt decreased, so send faster + lastRttOnIncreaseSendRate=rtt; + DecreaseTimeBetweenSends(); + } + + pingsLastInterval.Clear(__FILE__,__LINE__); + hadPacketlossThisBlock=false; + nextCongestionControlBlock=nextDatagramSequenceNumber; + } + + lastRtt=rtt; +} + +// ---------------------------------------------------------------------------------------------------------------------------- +double CCRakNetUDT::BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in) +{ +#if CC_TIME_TYPE_BYTES==4 + const BytesPerMicrosecond factor = 1.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER; +#else + const BytesPerMicrosecond factor = 1000.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER; +#endif + return in * factor; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::InitPacketArrivalHistory(void) +{ + unsigned int i; + for (i=0; i < CC_RAKNET_UDT_PACKET_HISTORY_LENGTH; i++) + { + packetArrivalHistory[i]=UNDEFINED_TRANSFER_RATE; + packetArrivalHistoryContinuousGaps[i]=0; + } + + packetArrivalHistoryWriteCount=0; + continuousBytesReceived=0; + continuousBytesReceivedStartTime=0; +} +// ---------------------------------------------------------------------------------------------------------------------------- +void CCRakNetUDT::PrintLowBandwidthWarning(void) +{ + + /* + printf("\n-------LOW BANDWIDTH -----\n"); + if (isInSlowStart==false) + printf("SND=%f Megabytes per second\n", 1.0/SND); + printf("Window size=%f\n", CWND); + printf("Pipe from packet pair = %f megabytes per second\n", B); + printf("RTT=%f milliseconds\n", RTT/1000.0); + printf("RTT Variance=%f milliseconds\n", RTTVar/1000.0); + printf("Retransmission=%i milliseconds\n", GetRTOForRetransmission()/1000); + printf("Packet arrival rate on the remote system=%f megabytes per second\n", AS); + printf("Packet arrival rate on our system=%f megabytes per second\n", ReceiverCalculateDataArrivalRate()); + printf("isInSlowStart=%i\n", isInSlowStart); + printf("---------------\n"); + */ +} +BytesPerMicrosecond CCRakNetUDT::GetLocalReceiveRate(CCTimeType currentTime) const +{ + return ReceiverCalculateDataArrivalRate(currentTime); +} +double CCRakNetUDT::GetRTT(void) const +{ + if (RTT==UNSET_TIME_US) + return 0.0; + return RTT; +} +void CCRakNetUDT::CapMinSnd(const char *file, int line) +{ + (void) file; + (void) line; + + if (SND > 500) + { + SND=500; + CC_DEBUG_PRINTF_3("%s:%i LIKELY BUG: SND has gotten above 500 microseconds between messages (28.8 modem)\nReport to rakkar@jenkinssoftware.com with file and line number\n", file, line); + } +} +void CCRakNetUDT::IncreaseTimeBetweenSends(void) +{ + // In order to converge, bigger numbers have to increase slower and decrease faster + // SND==500 then increment is .02 + // SND==0 then increment is near 0 + // (SND+1.0) brings it to the range of 1 to 501 + // Square the number, which is the range of 1 to 251001 + // Divide by 251001, which is the range of 1/251001 to 1 + + double increment; + increment = .02 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ; + // SND=500 then increment=.02 + // SND=0 then increment=near 0 + SND*=(1.02 - increment); + + // SND=0 then fast increase, slow decrease + // SND=500 then slow increase, fast decrease + CapMinSnd(__FILE__,__LINE__); +} +void CCRakNetUDT::DecreaseTimeBetweenSends(void) +{ + double increment; + increment = .01 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ; + // SND=500 then increment=.01 + // SND=0 then increment=near 0 + SND*=(.99 - increment); +} +/* +void CCRakNetUDT::SetTimeBetweenSendsLimit(unsigned int bitsPerSecond) +{ +// bitsPerSecond / 1000000 = bitsPerMicrosecond +// bitsPerMicrosecond / 8 = BytesPerMicrosecond +// 1 / BytesPerMicrosecond = MicrosecondsPerByte +// 1 / ( (bitsPerSecond / 1000000) / 8 ) = +// 1 / (bitsPerSecond / 8000000) = +// 8000000 / bitsPerSecond + +#if CC_TIME_TYPE_BYTES==4 + MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000 / (MicrosecondsPerByte)bitsPerSecond; +#else + MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000000 / (MicrosecondsPerByte)bitsPerSecond; +#endif + if (limit > SND) + SND=limit; +} +*/ + +#endif diff --git a/src/raknet/CCRakNetUDT.h b/src/raknet/CCRakNetUDT.h index ca8a1c2..3400dde 100755 --- a/src/raknet/CCRakNetUDT.h +++ b/src/raknet/CCRakNetUDT.h @@ -1,394 +1,394 @@ -#include "RakNetDefines.h" - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 - -#ifndef __CONGESTION_CONTROL_UDT_H -#define __CONGESTION_CONTROL_UDT_H - -#include "NativeTypes.h" -#include "RakNetTime.h" -#include "RakNetTypes.h" -#include "DS_Queue.h" - -/// Set to 4 if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 -#define CC_TIME_TYPE_BYTES 8 - -namespace RakNet -{ - - -typedef uint64_t CCTimeType; - - - - -typedef uint24_t DatagramSequenceNumberType; -typedef double BytesPerMicrosecond; -typedef double BytesPerSecond; -typedef double MicrosecondsPerByte; - -/// CC_RAKNET_UDT_PACKET_HISTORY_LENGTH should be a power of 2 for the writeIndex variables to wrap properly -#define CC_RAKNET_UDT_PACKET_HISTORY_LENGTH 64 -#define RTT_HISTORY_LENGTH 64 - -/// Sizeof an UDP header in byte -#define UDP_HEADER_SIZE 28 - -#define CC_DEBUG_PRINTF_1(x) -#define CC_DEBUG_PRINTF_2(x,y) -#define CC_DEBUG_PRINTF_3(x,y,z) -#define CC_DEBUG_PRINTF_4(x,y,z,a) -#define CC_DEBUG_PRINTF_5(x,y,z,a,b) -//#define CC_DEBUG_PRINTF_1(x) printf(x) -//#define CC_DEBUG_PRINTF_2(x,y) printf(x,y) -//#define CC_DEBUG_PRINTF_3(x,y,z) printf(x,y,z) -//#define CC_DEBUG_PRINTF_4(x,y,z,a) printf(x,y,z,a) -//#define CC_DEBUG_PRINTF_5(x,y,z,a,b) printf(x,y,z,a,b) - -/// \brief Encapsulates UDT congestion control, as used by RakNet -/// Requirements: -///
        -///
      1. Each datagram is no more than MAXIMUM_MTU_SIZE, after accounting for the UDP header -///
      2. Each datagram containing a user message has a sequence number which is set after calling OnSendBytes(). Set it by calling GetAndIncrementNextDatagramSequenceNumber() -///
      3. System is designed to be used from a single thread. -///
      4. Each packet should have a timeout time based on GetSenderRTOForACK(). If this time elapses, add the packet to the head of the send list for retransmission. -///
      -/// -/// Recommended: -///
        -///
      1. Call sendto in its own thread. This takes a significant amount of time in high speed networks. -///
      -/// -/// Algorithm: -///
        -///
      1. On a new connection, call Init() -///
      2. On a periodic interval (SYN time is the best) call Update(). Also call ShouldSendACKs(), and send buffered ACKS if it returns true. -///
      3. Call OnSendAck() when sending acks. -///
      4. When you want to send or resend data, call GetNumberOfBytesToSend(). It will return you enough bytes to keep you busy for \a estimatedTimeToNextTick. You can send more than this to fill out a datagram, or to send packet pairs -///
      5. Call OnSendBytes() when sending datagrams. -///
      6. When data arrives, record the sequence number and buffer an ACK for it, to be sent from Update() if ShouldSendACKs() returns true -///
      7. Every 16 packets that you send, send two of them back to back (a packet pair) as long as both packets are the same size. If you don't have two packets the same size, it is fine to defer this until you do. -///
      8. When you get a packet, call OnGotPacket(). If the packet is also either of a packet pair, call OnGotPacketPair() -///
      9. If you get a packet, and the sequence number is not 1 + the last sequence number, send a NAK. On the remote system, call OnNAK() and resend that message. -///
      10. If you get an ACK, remove that message from retransmission. Call OnNonDuplicateAck(). -///
      11. If a message is not ACKed for GetRTOForRetransmission(), resend it. -///
      -class CCRakNetUDT -{ - public: - - CCRakNetUDT(); - ~CCRakNetUDT(); - - /// Reset all variables to their initial states, for a new connection - void Init(CCTimeType curTime, uint32_t maxDatagramPayload); - - /// Update over time - void Update(CCTimeType curTime, bool hasDataToSendOrResend); - - int GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); - int GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); - - /// Acks do not have to be sent immediately. Instead, they can be buffered up such that groups of acks are sent at a time - /// This reduces overall bandwidth usage - /// How long they can be buffered depends on the retransmit time of the sender - /// Should call once per update tick, and send if needed - bool ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick); - - /// Every data packet sent must contain a sequence number - /// Call this function to get it. The sequence number is passed into OnGotPacketPair() - DatagramSequenceNumberType GetAndIncrementNextDatagramSequenceNumber(void); - DatagramSequenceNumberType GetNextDatagramSequenceNumber(void); - - /// Call this when you send packets - /// Every 15th and 16th packets should be sent as a packet pair if possible - /// When packets marked as a packet pair arrive, pass to OnGotPacketPair() - /// When any packets arrive, (additionally) pass to OnGotPacket - /// Packets should contain our system time, so we can pass rtt to OnNonDuplicateAck() - void OnSendBytes(CCTimeType curTime, uint32_t numBytes); - - /// Call this when you get a packet pair - void OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime); - - /// Call this when you get a packet (including packet pairs) - /// If the DatagramSequenceNumberType is out of order, skippedMessageCount will be non-zero - /// In that case, send a NAK for every sequence number up to that count - bool OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount); - - /// Call when you get a NAK, with the sequence number of the lost message - /// Affects the congestion control - void OnResend(CCTimeType curTime); - void OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber); - - /// Call this when an ACK arrives. - /// hasBAndAS are possibly written with the ack, see OnSendAck() - /// B and AS are used in the calculations in UpdateWindowSizeAndAckOnAckPerSyn - /// B and AS are updated at most once per SYN - void OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ); - void OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ) {} - - /// Call when you send an ack, to see if the ack should have the B and AS parameters transmitted - /// Call before calling OnSendAck() - void OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS); - - /// Call when we send an ack, to write B and AS if needed - /// B and AS are only written once per SYN, to prevent slow calculations - /// Also updates SND, the period between sends, since data is written out - /// Be sure to call OnSendAckGetBAndAS() before calling OnSendAck(), since whether you write it or not affects \a numBytes - void OnSendAck(CCTimeType curTime, uint32_t numBytes); - - /// Call when we send a NACK - /// Also updates SND, the period between sends, since data is written out - void OnSendNACK(CCTimeType curTime, uint32_t numBytes); - - /// Retransmission time out for the sender - /// If the time difference between when a message was last transmitted, and the current time is greater than RTO then packet is eligible for retransmission, pending congestion control - /// RTO = (RTT + 4 * RTTVar) + SYN - /// If we have been continuously sending for the last RTO, and no ACK or NAK at all, SND*=2; - /// This is per message, which is different from UDT, but RakNet supports packetloss with continuing data where UDT is only RELIABLE_ORDERED - /// Minimum value is 100 milliseconds - CCTimeType GetRTOForRetransmission(void) const; - - /// Set the maximum amount of data that can be sent in one datagram - /// Default to MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE - void SetMTU(uint32_t bytes); - - /// Return what was set by SetMTU() - uint32_t GetMTU(void) const; - - /// Query for statistics - BytesPerMicrosecond GetLocalSendRate(void) const {return 1.0 / SND;} - BytesPerMicrosecond GetLocalReceiveRate(CCTimeType currentTime) const; - BytesPerMicrosecond GetRemoveReceiveRate(void) const {return AS;} - //BytesPerMicrosecond GetEstimatedBandwidth(void) const {return B;} - BytesPerMicrosecond GetEstimatedBandwidth(void) const {return GetLinkCapacityBytesPerSecond()*1000000.0;} - double GetLinkCapacityBytesPerSecond(void) const {return estimatedLinkCapacityBytesPerSecond;}; - - /// Query for statistics - double GetRTT(void) const; - - bool GetIsInSlowStart(void) const {return isInSlowStart;} - uint32_t GetCWNDLimit(void) const {return (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER);} - - - /// Is a > b, accounting for variable overflow? - static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); - /// Is a < b, accounting for variable overflow? - static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); -// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond); - uint64_t GetBytesPerSecondLimitByCongestionControl(void) const; - - protected: - // --------------------------- PROTECTED VARIABLES --------------------------- - /// time interval between bytes, in microseconds. - /// Only used when slowStart==false - /// Increased over time as we continually get messages - /// Decreased on NAK and timeout - /// Starts at 0 (invalid) - MicrosecondsPerByte SND; - - /// Supportive window mechanism, controlling the maximum number of in-flight packets - /// Used both during and after slow-start, but primarily during slow-start - /// Starts at 2, which is also the low threshhold - /// Max is the socket receive buffer / MTU - /// CWND = AS * (RTT + SYN) + 16 - double CWND; - - /// When we do an update process on the SYN interval, nextSYNUpdate is set to the next time we should update - /// Normally this is nextSYNUpdate+=SYN, in order to update on a consistent schedule - /// However, if this would result in an immediate update yet again, it is set to SYN microseconds past the current time (in case the thread did not update for a long time) - CCTimeType nextSYNUpdate; - - - /// Index into packetPairRecieptHistory where we will next write - /// The history is always full (starting with default values) so no read index is needed - int packetPairRecieptHistoryWriteIndex; - - /// Sent to the sender by the receiver from packetPairRecieptHistory whenever a back to back packet arrives on the receiver - /// Updated by B = B * .875 + incomingB * .125 - //BytesPerMicrosecond B; - - /// Running round trip time (ping*2) - /// Only sender needs to know this - /// Initialized to UNSET - /// Set to rtt on first calculation - /// Updated gradually by RTT = RTT * 0.875 + rtt * 0.125 - double RTT; - - /// Round trip time variance - /// Only sender needs to know this - /// Initialized to UNSET - /// Set to rtt on first calculation - // double RTTVar; - /// Update: Use min/max, RTTVar follows current variance too closely resulting in packetloss - double minRTT, maxRTT; - - /// Used to calculate packet arrival rate (in UDT) but data arrival rate (in RakNet, where not all datagrams are the same size) - /// Filter is used to cull lowest half of values for bytesPerMicrosecond, to discount spikes and inactivity - /// Referred to in the documentation as AS, data arrival rate - /// AS is sent to the sender and calculated every 10th ack - /// Each node represents (curTime-lastPacketArrivalTime)/bytes - /// Used with ReceiverCalculateDataArrivalRate(); - BytesPerMicrosecond packetArrivalHistory[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH]; - BytesPerMicrosecond packetArrivalHistoryContinuousGaps[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH]; - unsigned char packetArrivalHistoryContinuousGapsIndex; - uint64_t continuousBytesReceived; - CCTimeType continuousBytesReceivedStartTime; - unsigned int packetArrivalHistoryWriteCount; - - /// Index into packetArrivalHistory where we will next write - /// The history is always full (starting with default values) so no read index is needed - int packetArrivalHistoryWriteIndex; - - /// Tracks the time the last packet that arrived, so BytesPerMicrosecond can be calculated for packetArrivalHistory when a new packet arrives - CCTimeType lastPacketArrivalTime; - - /// Data arrival rate from the sender to the receiver, as told to us by the receiver - /// Used to calculate initial sending rate when slow start stops - BytesPerMicrosecond AS; - - /// When the receiver last calculated and send B and AS, from packetArrivalHistory and packetPairRecieptHistory - /// Used to prevent it from being calculated and send too frequently, as they are slow operations - CCTimeType lastTransmitOfBAndAS; - - /// New connections start in slow start - /// During slow start, SND is not used, only CWND - /// Slow start ends when we get a NAK, or the maximum size of CWND is reached - /// SND is initialized to the inverse of the receiver's packet arrival rate when slow start ends - bool isInSlowStart; - - /// How many NAKs arrived this congestion period - /// Initialized to 1 when the congestion period starts - uint32_t NAKCount; - - /// How many NAKs do you get on average during a congestion period? - /// Starts at 1 - /// Used to generate a random number, DecRandom, between 1 and AvgNAKNum - uint32_t AvgNAKNum; - - /// How many times we have decremented SND this congestion period. Used to limit the number of decrements to 5 - uint32_t DecCount; - - /// Every DecInterval NAKs per congestion period, we decrease the send rate - uint32_t DecInterval; - - /// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment - DatagramSequenceNumberType nextDatagramSequenceNumber; - - /// If a packet is marked as a packet pair, lastPacketPairPacketArrivalTime is set to the time it arrives - /// This is used so when the 2nd packet of the pair arrives, we can calculate the time interval between the two - CCTimeType lastPacketPairPacketArrivalTime; - - /// If a packet is marked as a packet pair, lastPacketPairSequenceNumber is checked to see if the last packet we got - /// was the packet immediately before the one that arrived - /// If so, we can use lastPacketPairPacketArrivalTime to get the time between the two packets, and thus estimate the link capacity - /// Initialized to -1, so the first packet of a packet pair won't be treated as the second - DatagramSequenceNumberType lastPacketPairSequenceNumber; - - /// Used to cap UpdateWindowSizeAndAckOnAckPerSyn() to once speed increase per SYN - /// This is to prevent speeding up faster than congestion control can compensate for - CCTimeType lastUpdateWindowSizeAndAck; - - /// Every time SND is halved due to timeout, the RTO is increased - /// This is to prevent massive retransmissions to an unresponsive system - /// Reset on any data arriving - double ExpCount; - - /// Total number of user data bytes sent - /// Used to adjust the window size, on ACK, during slow start - uint64_t totalUserDataBytesSent; - - /// When we get an ack, if oldestUnsentAck==0, set it to the current time - /// When we send out acks, set oldestUnsentAck to 0 - CCTimeType oldestUnsentAck; - - // Maximum amount of bytes that the user can send, e.g. the size of one full datagram - uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER; - - // Max window size - double CWND_MAX_THRESHOLD; - - /// Track which datagram sequence numbers have arrived. - /// If a sequence number is skipped, send a NAK for all skipped messages - DatagramSequenceNumberType expectedNextSequenceNumber; - - // How many times have we sent B and AS? Used to force it to send at least CC_RAKNET_UDT_PACKET_HISTORY_LENGTH times - // Otherwise, the default values in the array generate inaccuracy - uint32_t sendBAndASCount; - - /// Most recent values read into the corresponding lists - /// Used during the beginning of a connection, when the median filter is still inaccurate - BytesPerMicrosecond mostRecentPacketArrivalHistory; - - bool hasWrittenToPacketPairReceiptHistory; - -// uint32_t rttHistory[RTT_HISTORY_LENGTH]; -// uint32_t rttHistoryIndex; -// uint32_t rttHistoryWriteCount; -// uint32_t rttSum, rttLow; -// CCTimeType lastSndUpdateTime; - double estimatedLinkCapacityBytesPerSecond; - - // --------------------------- PROTECTED METHODS --------------------------- - /// Update nextSYNUpdate by SYN, or the same amount past the current time if no updates have occurred for a long time - void SetNextSYNUpdate(CCTimeType currentTime); - - /// Returns the rate of data arrival, based on packets arriving on the sender. - BytesPerMicrosecond ReceiverCalculateDataArrivalRate(CCTimeType curTime) const; - /// Returns the median of the data arrival rate - BytesPerMicrosecond ReceiverCalculateDataArrivalRateMedian(void) const; - - /// Calculates the median an array of BytesPerMicrosecond - static BytesPerMicrosecond CalculateListMedianRecursive(const BytesPerMicrosecond inputList[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum); -// static uint32_t CalculateListMedianRecursive(const uint32_t inputList[RTT_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum); - - /// Same as GetRTOForRetransmission, but does not factor in ExpCount - /// This is because the receiver does not know ExpCount for the sender, and even if it did, acks shouldn't be delayed for this reason - CCTimeType GetSenderRTOForACK(void) const; - - /// Stop slow start, and enter normal transfer rate - void EndSlowStart(void); - - /// Does the named conversion - inline double BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in); - - /// Update the round trip time, from ACK or ACK2 - //void UpdateRTT(CCTimeType rtt); - - /// Update the corresponding variables pre-slow start - void UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked); - - /// Update the corresponding variables post-slow start - void UpdateWindowSizeAndAckOnAckPerSyn(CCTimeType curTime, CCTimeType rtt, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber); - - - /// Sets halveSNDOnNoDataTime to the future, and also resets ExpCount, which is used to multiple the RTO on no data arriving at all - void ResetOnDataArrivalHalveSNDOnNoDataTime(CCTimeType curTime); - - // Init array - void InitPacketArrivalHistory(void); - - // Printf - void PrintLowBandwidthWarning(void); - - // Bug: SND can sometimes get super high - have seen 11693 - void CapMinSnd(const char *file, int line); - - void DecreaseTimeBetweenSends(void); - void IncreaseTimeBetweenSends(void); - - int bytesCanSendThisTick; - - CCTimeType lastRttOnIncreaseSendRate; - CCTimeType lastRtt; - - DatagramSequenceNumberType nextCongestionControlBlock; - bool hadPacketlossThisBlock; - DataStructures::Queue pingsLastInterval; -}; - -} - -#endif - -#endif +#include "RakNetDefines.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 + +#ifndef __CONGESTION_CONTROL_UDT_H +#define __CONGESTION_CONTROL_UDT_H + +#include "NativeTypes.h" +#include "RakNetTime.h" +#include "RakNetTypes.h" +#include "DS_Queue.h" + +/// Set to 4 if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 +#define CC_TIME_TYPE_BYTES 8 + +namespace RakNet +{ + + +typedef uint64_t CCTimeType; + + + + +typedef uint24_t DatagramSequenceNumberType; +typedef double BytesPerMicrosecond; +typedef double BytesPerSecond; +typedef double MicrosecondsPerByte; + +/// CC_RAKNET_UDT_PACKET_HISTORY_LENGTH should be a power of 2 for the writeIndex variables to wrap properly +#define CC_RAKNET_UDT_PACKET_HISTORY_LENGTH 64 +#define RTT_HISTORY_LENGTH 64 + +/// Sizeof an UDP header in byte +#define UDP_HEADER_SIZE 28 + +#define CC_DEBUG_PRINTF_1(x) +#define CC_DEBUG_PRINTF_2(x,y) +#define CC_DEBUG_PRINTF_3(x,y,z) +#define CC_DEBUG_PRINTF_4(x,y,z,a) +#define CC_DEBUG_PRINTF_5(x,y,z,a,b) +//#define CC_DEBUG_PRINTF_1(x) printf(x) +//#define CC_DEBUG_PRINTF_2(x,y) printf(x,y) +//#define CC_DEBUG_PRINTF_3(x,y,z) printf(x,y,z) +//#define CC_DEBUG_PRINTF_4(x,y,z,a) printf(x,y,z,a) +//#define CC_DEBUG_PRINTF_5(x,y,z,a,b) printf(x,y,z,a,b) + +/// \brief Encapsulates UDT congestion control, as used by RakNet +/// Requirements: +///
        +///
      1. Each datagram is no more than MAXIMUM_MTU_SIZE, after accounting for the UDP header +///
      2. Each datagram containing a user message has a sequence number which is set after calling OnSendBytes(). Set it by calling GetAndIncrementNextDatagramSequenceNumber() +///
      3. System is designed to be used from a single thread. +///
      4. Each packet should have a timeout time based on GetSenderRTOForACK(). If this time elapses, add the packet to the head of the send list for retransmission. +///
      +/// +/// Recommended: +///
        +///
      1. Call sendto in its own thread. This takes a significant amount of time in high speed networks. +///
      +/// +/// Algorithm: +///
        +///
      1. On a new connection, call Init() +///
      2. On a periodic interval (SYN time is the best) call Update(). Also call ShouldSendACKs(), and send buffered ACKS if it returns true. +///
      3. Call OnSendAck() when sending acks. +///
      4. When you want to send or resend data, call GetNumberOfBytesToSend(). It will return you enough bytes to keep you busy for \a estimatedTimeToNextTick. You can send more than this to fill out a datagram, or to send packet pairs +///
      5. Call OnSendBytes() when sending datagrams. +///
      6. When data arrives, record the sequence number and buffer an ACK for it, to be sent from Update() if ShouldSendACKs() returns true +///
      7. Every 16 packets that you send, send two of them back to back (a packet pair) as long as both packets are the same size. If you don't have two packets the same size, it is fine to defer this until you do. +///
      8. When you get a packet, call OnGotPacket(). If the packet is also either of a packet pair, call OnGotPacketPair() +///
      9. If you get a packet, and the sequence number is not 1 + the last sequence number, send a NAK. On the remote system, call OnNAK() and resend that message. +///
      10. If you get an ACK, remove that message from retransmission. Call OnNonDuplicateAck(). +///
      11. If a message is not ACKed for GetRTOForRetransmission(), resend it. +///
      +class CCRakNetUDT +{ + public: + + CCRakNetUDT(); + ~CCRakNetUDT(); + + /// Reset all variables to their initial states, for a new connection + void Init(CCTimeType curTime, uint32_t maxDatagramPayload); + + /// Update over time + void Update(CCTimeType curTime, bool hasDataToSendOrResend); + + int GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); + int GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend); + + /// Acks do not have to be sent immediately. Instead, they can be buffered up such that groups of acks are sent at a time + /// This reduces overall bandwidth usage + /// How long they can be buffered depends on the retransmit time of the sender + /// Should call once per update tick, and send if needed + bool ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick); + + /// Every data packet sent must contain a sequence number + /// Call this function to get it. The sequence number is passed into OnGotPacketPair() + DatagramSequenceNumberType GetAndIncrementNextDatagramSequenceNumber(void); + DatagramSequenceNumberType GetNextDatagramSequenceNumber(void); + + /// Call this when you send packets + /// Every 15th and 16th packets should be sent as a packet pair if possible + /// When packets marked as a packet pair arrive, pass to OnGotPacketPair() + /// When any packets arrive, (additionally) pass to OnGotPacket + /// Packets should contain our system time, so we can pass rtt to OnNonDuplicateAck() + void OnSendBytes(CCTimeType curTime, uint32_t numBytes); + + /// Call this when you get a packet pair + void OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime); + + /// Call this when you get a packet (including packet pairs) + /// If the DatagramSequenceNumberType is out of order, skippedMessageCount will be non-zero + /// In that case, send a NAK for every sequence number up to that count + bool OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount); + + /// Call when you get a NAK, with the sequence number of the lost message + /// Affects the congestion control + void OnResend(CCTimeType curTime); + void OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber); + + /// Call this when an ACK arrives. + /// hasBAndAS are possibly written with the ack, see OnSendAck() + /// B and AS are used in the calculations in UpdateWindowSizeAndAckOnAckPerSyn + /// B and AS are updated at most once per SYN + void OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber ); + void OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ) {} + + /// Call when you send an ack, to see if the ack should have the B and AS parameters transmitted + /// Call before calling OnSendAck() + void OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS); + + /// Call when we send an ack, to write B and AS if needed + /// B and AS are only written once per SYN, to prevent slow calculations + /// Also updates SND, the period between sends, since data is written out + /// Be sure to call OnSendAckGetBAndAS() before calling OnSendAck(), since whether you write it or not affects \a numBytes + void OnSendAck(CCTimeType curTime, uint32_t numBytes); + + /// Call when we send a NACK + /// Also updates SND, the period between sends, since data is written out + void OnSendNACK(CCTimeType curTime, uint32_t numBytes); + + /// Retransmission time out for the sender + /// If the time difference between when a message was last transmitted, and the current time is greater than RTO then packet is eligible for retransmission, pending congestion control + /// RTO = (RTT + 4 * RTTVar) + SYN + /// If we have been continuously sending for the last RTO, and no ACK or NAK at all, SND*=2; + /// This is per message, which is different from UDT, but RakNet supports packetloss with continuing data where UDT is only RELIABLE_ORDERED + /// Minimum value is 100 milliseconds + CCTimeType GetRTOForRetransmission(void) const; + + /// Set the maximum amount of data that can be sent in one datagram + /// Default to MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE + void SetMTU(uint32_t bytes); + + /// Return what was set by SetMTU() + uint32_t GetMTU(void) const; + + /// Query for statistics + BytesPerMicrosecond GetLocalSendRate(void) const {return 1.0 / SND;} + BytesPerMicrosecond GetLocalReceiveRate(CCTimeType currentTime) const; + BytesPerMicrosecond GetRemoveReceiveRate(void) const {return AS;} + //BytesPerMicrosecond GetEstimatedBandwidth(void) const {return B;} + BytesPerMicrosecond GetEstimatedBandwidth(void) const {return GetLinkCapacityBytesPerSecond()*1000000.0;} + double GetLinkCapacityBytesPerSecond(void) const {return estimatedLinkCapacityBytesPerSecond;}; + + /// Query for statistics + double GetRTT(void) const; + + bool GetIsInSlowStart(void) const {return isInSlowStart;} + uint32_t GetCWNDLimit(void) const {return (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER);} + + + /// Is a > b, accounting for variable overflow? + static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); + /// Is a < b, accounting for variable overflow? + static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b); +// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond); + uint64_t GetBytesPerSecondLimitByCongestionControl(void) const; + + protected: + // --------------------------- PROTECTED VARIABLES --------------------------- + /// time interval between bytes, in microseconds. + /// Only used when slowStart==false + /// Increased over time as we continually get messages + /// Decreased on NAK and timeout + /// Starts at 0 (invalid) + MicrosecondsPerByte SND; + + /// Supportive window mechanism, controlling the maximum number of in-flight packets + /// Used both during and after slow-start, but primarily during slow-start + /// Starts at 2, which is also the low threshhold + /// Max is the socket receive buffer / MTU + /// CWND = AS * (RTT + SYN) + 16 + double CWND; + + /// When we do an update process on the SYN interval, nextSYNUpdate is set to the next time we should update + /// Normally this is nextSYNUpdate+=SYN, in order to update on a consistent schedule + /// However, if this would result in an immediate update yet again, it is set to SYN microseconds past the current time (in case the thread did not update for a long time) + CCTimeType nextSYNUpdate; + + + /// Index into packetPairRecieptHistory where we will next write + /// The history is always full (starting with default values) so no read index is needed + int packetPairRecieptHistoryWriteIndex; + + /// Sent to the sender by the receiver from packetPairRecieptHistory whenever a back to back packet arrives on the receiver + /// Updated by B = B * .875 + incomingB * .125 + //BytesPerMicrosecond B; + + /// Running round trip time (ping*2) + /// Only sender needs to know this + /// Initialized to UNSET + /// Set to rtt on first calculation + /// Updated gradually by RTT = RTT * 0.875 + rtt * 0.125 + double RTT; + + /// Round trip time variance + /// Only sender needs to know this + /// Initialized to UNSET + /// Set to rtt on first calculation + // double RTTVar; + /// Update: Use min/max, RTTVar follows current variance too closely resulting in packetloss + double minRTT, maxRTT; + + /// Used to calculate packet arrival rate (in UDT) but data arrival rate (in RakNet, where not all datagrams are the same size) + /// Filter is used to cull lowest half of values for bytesPerMicrosecond, to discount spikes and inactivity + /// Referred to in the documentation as AS, data arrival rate + /// AS is sent to the sender and calculated every 10th ack + /// Each node represents (curTime-lastPacketArrivalTime)/bytes + /// Used with ReceiverCalculateDataArrivalRate(); + BytesPerMicrosecond packetArrivalHistory[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH]; + BytesPerMicrosecond packetArrivalHistoryContinuousGaps[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH]; + unsigned char packetArrivalHistoryContinuousGapsIndex; + uint64_t continuousBytesReceived; + CCTimeType continuousBytesReceivedStartTime; + unsigned int packetArrivalHistoryWriteCount; + + /// Index into packetArrivalHistory where we will next write + /// The history is always full (starting with default values) so no read index is needed + int packetArrivalHistoryWriteIndex; + + /// Tracks the time the last packet that arrived, so BytesPerMicrosecond can be calculated for packetArrivalHistory when a new packet arrives + CCTimeType lastPacketArrivalTime; + + /// Data arrival rate from the sender to the receiver, as told to us by the receiver + /// Used to calculate initial sending rate when slow start stops + BytesPerMicrosecond AS; + + /// When the receiver last calculated and send B and AS, from packetArrivalHistory and packetPairRecieptHistory + /// Used to prevent it from being calculated and send too frequently, as they are slow operations + CCTimeType lastTransmitOfBAndAS; + + /// New connections start in slow start + /// During slow start, SND is not used, only CWND + /// Slow start ends when we get a NAK, or the maximum size of CWND is reached + /// SND is initialized to the inverse of the receiver's packet arrival rate when slow start ends + bool isInSlowStart; + + /// How many NAKs arrived this congestion period + /// Initialized to 1 when the congestion period starts + uint32_t NAKCount; + + /// How many NAKs do you get on average during a congestion period? + /// Starts at 1 + /// Used to generate a random number, DecRandom, between 1 and AvgNAKNum + uint32_t AvgNAKNum; + + /// How many times we have decremented SND this congestion period. Used to limit the number of decrements to 5 + uint32_t DecCount; + + /// Every DecInterval NAKs per congestion period, we decrease the send rate + uint32_t DecInterval; + + /// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment + DatagramSequenceNumberType nextDatagramSequenceNumber; + + /// If a packet is marked as a packet pair, lastPacketPairPacketArrivalTime is set to the time it arrives + /// This is used so when the 2nd packet of the pair arrives, we can calculate the time interval between the two + CCTimeType lastPacketPairPacketArrivalTime; + + /// If a packet is marked as a packet pair, lastPacketPairSequenceNumber is checked to see if the last packet we got + /// was the packet immediately before the one that arrived + /// If so, we can use lastPacketPairPacketArrivalTime to get the time between the two packets, and thus estimate the link capacity + /// Initialized to -1, so the first packet of a packet pair won't be treated as the second + DatagramSequenceNumberType lastPacketPairSequenceNumber; + + /// Used to cap UpdateWindowSizeAndAckOnAckPerSyn() to once speed increase per SYN + /// This is to prevent speeding up faster than congestion control can compensate for + CCTimeType lastUpdateWindowSizeAndAck; + + /// Every time SND is halved due to timeout, the RTO is increased + /// This is to prevent massive retransmissions to an unresponsive system + /// Reset on any data arriving + double ExpCount; + + /// Total number of user data bytes sent + /// Used to adjust the window size, on ACK, during slow start + uint64_t totalUserDataBytesSent; + + /// When we get an ack, if oldestUnsentAck==0, set it to the current time + /// When we send out acks, set oldestUnsentAck to 0 + CCTimeType oldestUnsentAck; + + // Maximum amount of bytes that the user can send, e.g. the size of one full datagram + uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER; + + // Max window size + double CWND_MAX_THRESHOLD; + + /// Track which datagram sequence numbers have arrived. + /// If a sequence number is skipped, send a NAK for all skipped messages + DatagramSequenceNumberType expectedNextSequenceNumber; + + // How many times have we sent B and AS? Used to force it to send at least CC_RAKNET_UDT_PACKET_HISTORY_LENGTH times + // Otherwise, the default values in the array generate inaccuracy + uint32_t sendBAndASCount; + + /// Most recent values read into the corresponding lists + /// Used during the beginning of a connection, when the median filter is still inaccurate + BytesPerMicrosecond mostRecentPacketArrivalHistory; + + bool hasWrittenToPacketPairReceiptHistory; + +// uint32_t rttHistory[RTT_HISTORY_LENGTH]; +// uint32_t rttHistoryIndex; +// uint32_t rttHistoryWriteCount; +// uint32_t rttSum, rttLow; +// CCTimeType lastSndUpdateTime; + double estimatedLinkCapacityBytesPerSecond; + + // --------------------------- PROTECTED METHODS --------------------------- + /// Update nextSYNUpdate by SYN, or the same amount past the current time if no updates have occurred for a long time + void SetNextSYNUpdate(CCTimeType currentTime); + + /// Returns the rate of data arrival, based on packets arriving on the sender. + BytesPerMicrosecond ReceiverCalculateDataArrivalRate(CCTimeType curTime) const; + /// Returns the median of the data arrival rate + BytesPerMicrosecond ReceiverCalculateDataArrivalRateMedian(void) const; + + /// Calculates the median an array of BytesPerMicrosecond + static BytesPerMicrosecond CalculateListMedianRecursive(const BytesPerMicrosecond inputList[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum); +// static uint32_t CalculateListMedianRecursive(const uint32_t inputList[RTT_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum); + + /// Same as GetRTOForRetransmission, but does not factor in ExpCount + /// This is because the receiver does not know ExpCount for the sender, and even if it did, acks shouldn't be delayed for this reason + CCTimeType GetSenderRTOForACK(void) const; + + /// Stop slow start, and enter normal transfer rate + void EndSlowStart(void); + + /// Does the named conversion + inline double BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in); + + /// Update the round trip time, from ACK or ACK2 + //void UpdateRTT(CCTimeType rtt); + + /// Update the corresponding variables pre-slow start + void UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked); + + /// Update the corresponding variables post-slow start + void UpdateWindowSizeAndAckOnAckPerSyn(CCTimeType curTime, CCTimeType rtt, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber); + + + /// Sets halveSNDOnNoDataTime to the future, and also resets ExpCount, which is used to multiple the RTO on no data arriving at all + void ResetOnDataArrivalHalveSNDOnNoDataTime(CCTimeType curTime); + + // Init array + void InitPacketArrivalHistory(void); + + // Printf + void PrintLowBandwidthWarning(void); + + // Bug: SND can sometimes get super high - have seen 11693 + void CapMinSnd(const char *file, int line); + + void DecreaseTimeBetweenSends(void); + void IncreaseTimeBetweenSends(void); + + int bytesCanSendThisTick; + + CCTimeType lastRttOnIncreaseSendRate; + CCTimeType lastRtt; + + DatagramSequenceNumberType nextCongestionControlBlock; + bool hadPacketlossThisBlock; + DataStructures::Queue pingsLastInterval; +}; + +} + +#endif + +#endif diff --git a/src/raknet/CMakeLists.txt b/src/raknet/CMakeLists.txt index 65660a1..4f8c377 100755 --- a/src/raknet/CMakeLists.txt +++ b/src/raknet/CMakeLists.txt @@ -1 +1 @@ -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 2.6) diff --git a/src/raknet/CheckSum.cpp b/src/raknet/CheckSum.cpp index 4ba7fcd..91ecc3f 100755 --- a/src/raknet/CheckSum.cpp +++ b/src/raknet/CheckSum.cpp @@ -1,97 +1,97 @@ -/** -* @file -* @brief CheckSum implementation from http://www.flounder.com/checksum.htm -* -*/ -#include "CheckSum.h" - -/**************************************************************************** -* CheckSum::add -* Inputs: -* unsigned int d: word to add -* Result: void -* -* Effect: -* Adds the bytes of the unsigned int to the CheckSum -****************************************************************************/ - -void CheckSum::Add ( unsigned int value ) -{ - union - { - unsigned int value; - unsigned char bytes[ 4 ]; - } - - data; - data.value = value; - - for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) - Add ( data.bytes[ i ] ) - - ; -} // CheckSum::add(unsigned int) - -/**************************************************************************** -* CheckSum::add -* Inputs: -* unsigned short value: -* Result: void -* -* Effect: -* Adds the bytes of the unsigned short value to the CheckSum -****************************************************************************/ - -void CheckSum::Add ( unsigned short value ) -{ - union - { - unsigned short value; - unsigned char bytes[ 2 ]; - } - - data; - data.value = value; - - for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) - Add ( data.bytes[ i ] ) - - ; -} // CheckSum::add(unsigned short) - -/**************************************************************************** -* CheckSum::add -* Inputs: -* unsigned char value: -* Result: void -* -* Effect: -* Adds the byte to the CheckSum -****************************************************************************/ - -void CheckSum::Add ( unsigned char value ) -{ - unsigned char cipher = (unsigned char)( value ^ ( r >> 8 ) ); - r = ( cipher + r ) * c1 + c2; - sum += cipher; -} // CheckSum::add(unsigned char) - - -/**************************************************************************** -* CheckSum::add -* Inputs: -* LPunsigned char b: pointer to byte array -* unsigned int length: count -* Result: void -* -* Effect: -* Adds the bytes to the CheckSum -****************************************************************************/ - -void CheckSum::Add ( unsigned char *b, unsigned int length ) -{ - for ( unsigned int i = 0; i < length; i++ ) - Add ( b[ i ] ) - - ; -} // CheckSum::add(LPunsigned char, unsigned int) +/** +* @file +* @brief CheckSum implementation from http://www.flounder.com/checksum.htm +* +*/ +#include "CheckSum.h" + +/**************************************************************************** +* CheckSum::add +* Inputs: +* unsigned int d: word to add +* Result: void +* +* Effect: +* Adds the bytes of the unsigned int to the CheckSum +****************************************************************************/ + +void CheckSum::Add ( unsigned int value ) +{ + union + { + unsigned int value; + unsigned char bytes[ 4 ]; + } + + data; + data.value = value; + + for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) + Add ( data.bytes[ i ] ) + + ; +} // CheckSum::add(unsigned int) + +/**************************************************************************** +* CheckSum::add +* Inputs: +* unsigned short value: +* Result: void +* +* Effect: +* Adds the bytes of the unsigned short value to the CheckSum +****************************************************************************/ + +void CheckSum::Add ( unsigned short value ) +{ + union + { + unsigned short value; + unsigned char bytes[ 2 ]; + } + + data; + data.value = value; + + for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) + Add ( data.bytes[ i ] ) + + ; +} // CheckSum::add(unsigned short) + +/**************************************************************************** +* CheckSum::add +* Inputs: +* unsigned char value: +* Result: void +* +* Effect: +* Adds the byte to the CheckSum +****************************************************************************/ + +void CheckSum::Add ( unsigned char value ) +{ + unsigned char cipher = (unsigned char)( value ^ ( r >> 8 ) ); + r = ( cipher + r ) * c1 + c2; + sum += cipher; +} // CheckSum::add(unsigned char) + + +/**************************************************************************** +* CheckSum::add +* Inputs: +* LPunsigned char b: pointer to byte array +* unsigned int length: count +* Result: void +* +* Effect: +* Adds the bytes to the CheckSum +****************************************************************************/ + +void CheckSum::Add ( unsigned char *b, unsigned int length ) +{ + for ( unsigned int i = 0; i < length; i++ ) + Add ( b[ i ] ) + + ; +} // CheckSum::add(LPunsigned char, unsigned int) diff --git a/src/raknet/CheckSum.h b/src/raknet/CheckSum.h index 09e57f7..bd768a1 100755 --- a/src/raknet/CheckSum.h +++ b/src/raknet/CheckSum.h @@ -1,53 +1,53 @@ -/// -/// \file CheckSum.cpp -/// \brief [Internal] CheckSum implementation from http://www.flounder.com/checksum.htm -/// - -#ifndef __CHECKSUM_H -#define __CHECKSUM_H - -#include "RakMemoryOverride.h" - -/// Generates and validates checksums -class CheckSum -{ - -public: - - /// Default constructor - - CheckSum() - { - Clear(); - } - - void Clear() - { - sum = 0; - r = 55665; - c1 = 52845; - c2 = 22719; - } - - void Add ( unsigned int w ); - - - void Add ( unsigned short w ); - - void Add ( unsigned char* b, unsigned int length ); - - void Add ( unsigned char b ); - - unsigned int Get () - { - return sum; - } - -protected: - unsigned short r; - unsigned short c1; - unsigned short c2; - unsigned int sum; -}; - -#endif +/// +/// \file CheckSum.cpp +/// \brief [Internal] CheckSum implementation from http://www.flounder.com/checksum.htm +/// + +#ifndef __CHECKSUM_H +#define __CHECKSUM_H + +#include "RakMemoryOverride.h" + +/// Generates and validates checksums +class CheckSum +{ + +public: + + /// Default constructor + + CheckSum() + { + Clear(); + } + + void Clear() + { + sum = 0; + r = 55665; + c1 = 52845; + c2 = 22719; + } + + void Add ( unsigned int w ); + + + void Add ( unsigned short w ); + + void Add ( unsigned char* b, unsigned int length ); + + void Add ( unsigned char b ); + + unsigned int Get () + { + return sum; + } + +protected: + unsigned short r; + unsigned short c1; + unsigned short c2; + unsigned int sum; +}; + +#endif diff --git a/src/raknet/CloudClient.cpp b/src/raknet/CloudClient.cpp index cdfd2c6..248a42d 100755 --- a/src/raknet/CloudClient.cpp +++ b/src/raknet/CloudClient.cpp @@ -1,242 +1,242 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudClient==1 - -#include "CloudClient.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" -#include "RakPeerInterface.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(CloudClient,CloudClient); - -CloudClient::CloudClient() -{ - callback=0; - allocator=&unsetDefaultAllocator; -} -CloudClient::~CloudClient() -{ -} -void CloudClient::SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback) -{ - callback=_callback; - allocator=_allocator; -} -void CloudClient::Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier) -{ - RakAssert(cloudKey); - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_POST_REQUEST); - cloudKey->Serialize(true,&bsOut); - if (data==0) - dataLengthBytes=0; - bsOut.Write(dataLengthBytes); - if (dataLengthBytes>0) - bsOut.WriteAlignedBytes((const unsigned char*) data, dataLengthBytes); - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -void CloudClient::Release(DataStructures::List &keys, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_RELEASE_REQUEST); - RakAssert(keys.Size() < (uint16_t)-1 ); - bsOut.WriteCasted(keys.Size()); - for (uint16_t i=0; i < keys.Size(); i++) - { - keys[i].Serialize(true,&bsOut); - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -bool CloudClient::Get(CloudQuery *keyQuery, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); - keyQuery->Serialize(true, &bsOut); - bsOut.WriteCasted(0); // Specific systems - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); - return true; -} -bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); - keyQuery->Serialize(true, &bsOut); - bsOut.WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - bsOut.Write(specificSystems[i]); - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); - return true; -} -bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); - keyQuery->Serialize(true, &bsOut); - bsOut.WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID) - { - bsOut.Write(true); - bsOut.Write(specificSystems[i]->clientGUID); - } - else - { - bsOut.Write(false); - bsOut.Write(specificSystems[i]->clientSystemAddress); - } - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); - return true; -} -void CloudClient::Unsubscribe(DataStructures::List &keys, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); - RakAssert(keys.Size() < (uint16_t)-1 ); - bsOut.WriteCasted(keys.Size()); - for (uint16_t i=0; i < keys.Size(); i++) - { - keys[i].Serialize(true,&bsOut); - } - bsOut.WriteCasted(0); - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -void CloudClient::Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); - RakAssert(keys.Size() < (uint16_t)-1 ); - bsOut.WriteCasted(keys.Size()); - for (uint16_t i=0; i < keys.Size(); i++) - { - keys[i].Serialize(true,&bsOut); - } - bsOut.WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - bsOut.Write(specificSystems[i]); - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -void CloudClient::Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); - RakAssert(keys.Size() < (uint16_t)-1 ); - bsOut.WriteCasted(keys.Size()); - for (uint16_t i=0; i < keys.Size(); i++) - { - keys[i].Serialize(true,&bsOut); - } - bsOut.WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID) - { - bsOut.Write(true); - bsOut.Write(specificSystems[i]->clientGUID); - } - else - { - bsOut.Write(false); - bsOut.Write(specificSystems[i]->clientSystemAddress); - } - } - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); -} -PluginReceiveResult CloudClient::OnReceive(Packet *packet) -{ - (void) packet; - - return RR_CONTINUE_PROCESSING; -} -void CloudClient::OnGetReponse(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator) -{ - if (_callback==0) - _callback=callback; - if (_allocator==0) - _allocator=allocator; - - CloudQueryResult cloudQueryResult; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - cloudQueryResult.Serialize(false,&bsIn,_allocator); - bool deallocateRowsAfterReturn=true; - _callback->OnGet(&cloudQueryResult, &deallocateRowsAfterReturn); - if (deallocateRowsAfterReturn) - { - unsigned int i; - for (i=0; i < cloudQueryResult.rowsReturned.Size(); i++) - { - _allocator->DeallocateRowData(cloudQueryResult.rowsReturned[i]->data); - _allocator->DeallocateCloudQueryRow(cloudQueryResult.rowsReturned[i]); - } - } -} -void CloudClient::OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator) -{ - if (_allocator==0) - _allocator=allocator; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - cloudQueryResult->Serialize(false,&bsIn,_allocator); -} -void CloudClient::OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator) -{ - if (_callback==0) - _callback=callback; - if (_allocator==0) - _allocator=allocator; - - bool wasUpdated=false; - CloudQueryRow row; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bsIn.Read(wasUpdated); - row.Serialize(false,&bsIn,_allocator); - bool deallocateRowAfterReturn=true; - _callback->OnSubscriptionNotification(&row, wasUpdated, &deallocateRowAfterReturn); - if (deallocateRowAfterReturn) - { - _allocator->DeallocateRowData(row.data); - } -} -void CloudClient::OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator) -{ - if (_allocator==0) - _allocator=allocator; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bool b=false; - bsIn.Read(b); - *wasUpdated=b; - row->Serialize(false,&bsIn,_allocator); -} -void CloudClient::DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult) -{ - unsigned int i; - for (i=0; i < cloudQueryResult->rowsReturned.Size(); i++) - { - allocator->DeallocateRowData(cloudQueryResult->rowsReturned[i]->data); - allocator->DeallocateCloudQueryRow(cloudQueryResult->rowsReturned[i]); - } -} -void CloudClient::DeallocateWithDefaultAllocator(CloudQueryRow *row) -{ - allocator->DeallocateRowData(row->data); -} -#endif +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudClient==1 + +#include "CloudClient.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" +#include "RakPeerInterface.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(CloudClient,CloudClient); + +CloudClient::CloudClient() +{ + callback=0; + allocator=&unsetDefaultAllocator; +} +CloudClient::~CloudClient() +{ +} +void CloudClient::SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback) +{ + callback=_callback; + allocator=_allocator; +} +void CloudClient::Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier) +{ + RakAssert(cloudKey); + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_POST_REQUEST); + cloudKey->Serialize(true,&bsOut); + if (data==0) + dataLengthBytes=0; + bsOut.Write(dataLengthBytes); + if (dataLengthBytes>0) + bsOut.WriteAlignedBytes((const unsigned char*) data, dataLengthBytes); + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +void CloudClient::Release(DataStructures::List &keys, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_RELEASE_REQUEST); + RakAssert(keys.Size() < (uint16_t)-1 ); + bsOut.WriteCasted(keys.Size()); + for (uint16_t i=0; i < keys.Size(); i++) + { + keys[i].Serialize(true,&bsOut); + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +bool CloudClient::Get(CloudQuery *keyQuery, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); + keyQuery->Serialize(true, &bsOut); + bsOut.WriteCasted(0); // Specific systems + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); + return true; +} +bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); + keyQuery->Serialize(true, &bsOut); + bsOut.WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + bsOut.Write(specificSystems[i]); + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); + return true; +} +bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST); + keyQuery->Serialize(true, &bsOut); + bsOut.WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID) + { + bsOut.Write(true); + bsOut.Write(specificSystems[i]->clientGUID); + } + else + { + bsOut.Write(false); + bsOut.Write(specificSystems[i]->clientSystemAddress); + } + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); + return true; +} +void CloudClient::Unsubscribe(DataStructures::List &keys, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); + RakAssert(keys.Size() < (uint16_t)-1 ); + bsOut.WriteCasted(keys.Size()); + for (uint16_t i=0; i < keys.Size(); i++) + { + keys[i].Serialize(true,&bsOut); + } + bsOut.WriteCasted(0); + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +void CloudClient::Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); + RakAssert(keys.Size() < (uint16_t)-1 ); + bsOut.WriteCasted(keys.Size()); + for (uint16_t i=0; i < keys.Size(); i++) + { + keys[i].Serialize(true,&bsOut); + } + bsOut.WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + bsOut.Write(specificSystems[i]); + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +void CloudClient::Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST); + RakAssert(keys.Size() < (uint16_t)-1 ); + bsOut.WriteCasted(keys.Size()); + for (uint16_t i=0; i < keys.Size(); i++) + { + keys[i].Serialize(true,&bsOut); + } + bsOut.WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID) + { + bsOut.Write(true); + bsOut.Write(specificSystems[i]->clientGUID); + } + else + { + bsOut.Write(false); + bsOut.Write(specificSystems[i]->clientSystemAddress); + } + } + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false); +} +PluginReceiveResult CloudClient::OnReceive(Packet *packet) +{ + (void) packet; + + return RR_CONTINUE_PROCESSING; +} +void CloudClient::OnGetReponse(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator) +{ + if (_callback==0) + _callback=callback; + if (_allocator==0) + _allocator=allocator; + + CloudQueryResult cloudQueryResult; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + cloudQueryResult.Serialize(false,&bsIn,_allocator); + bool deallocateRowsAfterReturn=true; + _callback->OnGet(&cloudQueryResult, &deallocateRowsAfterReturn); + if (deallocateRowsAfterReturn) + { + unsigned int i; + for (i=0; i < cloudQueryResult.rowsReturned.Size(); i++) + { + _allocator->DeallocateRowData(cloudQueryResult.rowsReturned[i]->data); + _allocator->DeallocateCloudQueryRow(cloudQueryResult.rowsReturned[i]); + } + } +} +void CloudClient::OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator) +{ + if (_allocator==0) + _allocator=allocator; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + cloudQueryResult->Serialize(false,&bsIn,_allocator); +} +void CloudClient::OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator) +{ + if (_callback==0) + _callback=callback; + if (_allocator==0) + _allocator=allocator; + + bool wasUpdated=false; + CloudQueryRow row; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bsIn.Read(wasUpdated); + row.Serialize(false,&bsIn,_allocator); + bool deallocateRowAfterReturn=true; + _callback->OnSubscriptionNotification(&row, wasUpdated, &deallocateRowAfterReturn); + if (deallocateRowAfterReturn) + { + _allocator->DeallocateRowData(row.data); + } +} +void CloudClient::OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator) +{ + if (_allocator==0) + _allocator=allocator; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bool b=false; + bsIn.Read(b); + *wasUpdated=b; + row->Serialize(false,&bsIn,_allocator); +} +void CloudClient::DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult) +{ + unsigned int i; + for (i=0; i < cloudQueryResult->rowsReturned.Size(); i++) + { + allocator->DeallocateRowData(cloudQueryResult->rowsReturned[i]->data); + allocator->DeallocateCloudQueryRow(cloudQueryResult->rowsReturned[i]); + } +} +void CloudClient::DeallocateWithDefaultAllocator(CloudQueryRow *row) +{ + allocator->DeallocateRowData(row->data); +} +#endif diff --git a/src/raknet/CloudClient.h b/src/raknet/CloudClient.h index 293740d..dc75c1e 100755 --- a/src/raknet/CloudClient.h +++ b/src/raknet/CloudClient.h @@ -1,163 +1,163 @@ -/// \file CloudClient.h -/// \brief Queries CloudMemoryServer to download data that other clients have uploaded -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudClient==1 - -#ifndef __CLOUD_CLIENT_H -#define __CLOUD_CLIENT_H - -#include "PluginInterface2.h" -#include "CloudCommon.h" -#include "RakMemoryOverride.h" -#include "DS_Hash.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -class CloudClientCallback; - -/// \defgroup CLOUD_GROUP CloudComputing -/// \brief Contains the CloudClient and CloudServer plugins -/// \details The CloudServer plugins operates on requests from the CloudClient plugin. The servers are in a fully connected mesh topology, which the clients are connected to any server. Clients can interact with each other by posting and subscribing to memory updates, without being directly connected or even knowing about each other. -/// \ingroup PLUGINS_GROUP - -/// \brief Performs Post() and Get() operations on CloudMemoryServer -/// \details A CloudClient is a computer connected to one or more servers in a cloud configuration. Operations by one CloudClient can be received and subscribed to by other instances of CloudClient, without those clients being connected, even on different servers. -/// \ingroup CLOUD_GROUP -class RAK_DLL_EXPORT CloudClient : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(CloudClient) - - CloudClient(); - virtual ~CloudClient(); - - /// \brief Set the default callbacks for OnGetReponse(), OnSubscriptionNotification(), and OnSubscriptionDataDeleted() - /// \details Pointers to CloudAllocator and CloudClientCallback can be stored by the system if desired. If a callback is not provided to OnGetReponse(), OnSubscriptionNotification(), OnSubscriptionDataDeleted(), the callback passed here will be used instead. - /// \param[in] _allocator An instance of CloudAllocator - /// \param[in] _callback An instance of CloudClientCallback - virtual void SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback); - - /// \brief Uploads data to the cloud - /// \details Data uploaded to the cloud will be stored by the server sent to, identified by \a systemIdentifier. - /// As long as you are connected to this server, the data will persist. Queries for that data by the Get() operation will - /// return the RakNetGUID and SystemAddress of the uploader, as well as the data itself. - /// Furthermore, if any clients are subscribed to the particular CloudKey passed, those clients will get update notices that the data has changed - /// Passing data with the same \a cloudKey more than once will overwrite the prior value. - /// This call will silently fail if CloudServer::SetMaxUploadBytesPerClient() is exceeded - /// \param[in] cloudKey Identifies the data being uploaded - /// \param[in] data A pointer to data to upload. This pointer does not need to persist past the call - /// \param[in] dataLengthBytes The length in bytes of \a data - /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. - virtual void Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier); - - /// \brief Releases one or more data previously uploaded with Post() - /// \details If a remote system has subscribed to one or more of the \a keys uploaded, they will get ID_CLOUD_SUBSCRIPTION_NOTIFICATION notifications containing the last value uploaded before deletions - /// \param[in] cloudKey Identifies the data to release. It is possible to remove uploads from multiple Post() calls at once. - /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. - virtual void Release(DataStructures::List &keys, RakNetGUID systemIdentifier); - - /// \brief Gets data from the cloud - /// \details For a given query containing one or more keys, return data that matches those keys. - /// The values will be returned in the ID_CLOUD_GET_RESPONSE packet, which should be passed to OnGetReponse() and will invoke CloudClientCallback::OnGet() - /// CloudQuery::startingRowIndex is used to skip the first n values that would normally be returned.. - /// CloudQuery::maxRowsToReturn is used to limit the number of rows returned. The number of rows returned may also be limited by CloudServer::SetMaxBytesPerDownload(); - /// CloudQuery::subscribeToResults if set to true, will cause ID_CLOUD_SUBSCRIPTION_NOTIFICATION to be returned to us when any of the keys in the query are updated or are deleted. - /// ID_CLOUD_GET_RESPONSE will be returned even if subscribing to the result list. Only later updates will return ID_CLOUD_SUBSCRIPTION_NOTIFICATION. - /// Calling Get() with CloudQuery::subscribeToResults false, when you are already subscribed, does not remove the subscription. Use Unsubscribe() for this. - /// Resubscribing using the same CloudKey but a different or no \a specificSystems overwrites the subscribed systems for those keys. - /// \param[in] cloudQuery One or more keys, and optional parameters to perform with the Get - /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. - /// \param[in] specificSystems It is possible to get or subscribe to updates only for specific uploading CloudClient instances. Pass the desired instances here. The overload that does not have the specificSystems parameter is treated as subscribing to all updates from all clients. - virtual bool Get(CloudQuery *cloudQuery, RakNetGUID systemIdentifier); - virtual bool Get(CloudQuery *cloudQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); - virtual bool Get(CloudQuery *cloudQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); - - /// \brief Unsubscribe from updates previously subscribed to using Get() with the CloudQuery::subscribeToResults set to true - /// The \a keys and \a specificSystems parameters are logically treated as AND when checking subscriptions on the server - /// The overload that does not take specificSystems unsubscribes to all passed keys, regardless of system - /// You cannot unsubscribe specific systems when previously subscribed to updates from any system. To do this, first Unsubscribe() from all systems, and call Get() with the \a specificSystems parameter explicilty listing the systems you want to subscribe to. - virtual void Unsubscribe(DataStructures::List &keys, RakNetGUID systemIdentifier); - virtual void Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); - virtual void Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); - - /// \brief Call this when you get ID_CLOUD_GET_RESPONSE - /// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used - /// \param[in] packet Packet structure returned from RakPeerInterface - /// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used. - /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. - virtual void OnGetReponse(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0); - - /// \brief Call this when you get ID_CLOUD_GET_RESPONSE - /// Different form of OnGetReponse that returns to a structure that you pass, instead of using a callback - /// You are responsible for deallocation with this form - /// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used - /// \param[out] cloudQueryResult A pointer to a structure that will be filled out with data - /// \param[in] packet Packet structure returned from RakPeerInterface - /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. - virtual void OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator=0); - - /// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION - /// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used - /// \param[in] packet Packet structure returned from RakPeerInterface - /// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used. - /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. - virtual void OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0); - - /// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION - /// Different form of OnSubscriptionNotification that returns to a structure that you pass, instead of using a callback - /// You are responsible for deallocation with this form - /// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used - /// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion - /// \param[out] row A pointer to a structure that will be filled out with data - /// \param[in] packet Packet structure returned from RakPeerInterface - /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. - virtual void OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator=0); - - /// If you never specified an allocator, and used the non-callback form of OnGetReponse(), deallocate cloudQueryResult with this function - virtual void DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult); - - /// If you never specified an allocator, and used the non-callback form of OnSubscriptionNotification(), deallocate row with this function - virtual void DeallocateWithDefaultAllocator(CloudQueryRow *row); - -protected: - PluginReceiveResult OnReceive(Packet *packet); - - CloudClientCallback *callback; - CloudAllocator *allocator; - - CloudAllocator unsetDefaultAllocator; -}; - -/// \ingroup CLOUD_GROUP -/// Parses ID_CLOUD_GET_RESPONSE and ID_CLOUD_SUBSCRIPTION_NOTIFICATION in a convenient callback form -class RAK_DLL_EXPORT CloudClientCallback -{ -public: - CloudClientCallback() {} - virtual ~CloudClientCallback() {} - - /// \brief Called in response to ID_CLOUD_GET_RESPONSE - /// \param[out] result Contains the original query passed to Get(), and a list of rows returned. - /// \param[out] deallocateRowsAfterReturn CloudQueryResult::rowsReturned will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator. - virtual void OnGet(RakNet::CloudQueryResult *result, bool *deallocateRowsAfterReturn) {(void) result; (void) deallocateRowsAfterReturn;} - - /// \brief Called in response to ID_CLOUD_SUBSCRIPTION_NOTIFICATION - /// \param[out] result Contains the row updated - /// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion - /// \param[out] deallocateRowAfterReturn \a result will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator. - virtual void OnSubscriptionNotification(RakNet::CloudQueryRow *result, bool wasUpdated, bool *deallocateRowAfterReturn) {(void) result; (void) wasUpdated; (void) deallocateRowAfterReturn;} -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file CloudClient.h +/// \brief Queries CloudMemoryServer to download data that other clients have uploaded +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudClient==1 + +#ifndef __CLOUD_CLIENT_H +#define __CLOUD_CLIENT_H + +#include "PluginInterface2.h" +#include "CloudCommon.h" +#include "RakMemoryOverride.h" +#include "DS_Hash.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +class CloudClientCallback; + +/// \defgroup CLOUD_GROUP CloudComputing +/// \brief Contains the CloudClient and CloudServer plugins +/// \details The CloudServer plugins operates on requests from the CloudClient plugin. The servers are in a fully connected mesh topology, which the clients are connected to any server. Clients can interact with each other by posting and subscribing to memory updates, without being directly connected or even knowing about each other. +/// \ingroup PLUGINS_GROUP + +/// \brief Performs Post() and Get() operations on CloudMemoryServer +/// \details A CloudClient is a computer connected to one or more servers in a cloud configuration. Operations by one CloudClient can be received and subscribed to by other instances of CloudClient, without those clients being connected, even on different servers. +/// \ingroup CLOUD_GROUP +class RAK_DLL_EXPORT CloudClient : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(CloudClient) + + CloudClient(); + virtual ~CloudClient(); + + /// \brief Set the default callbacks for OnGetReponse(), OnSubscriptionNotification(), and OnSubscriptionDataDeleted() + /// \details Pointers to CloudAllocator and CloudClientCallback can be stored by the system if desired. If a callback is not provided to OnGetReponse(), OnSubscriptionNotification(), OnSubscriptionDataDeleted(), the callback passed here will be used instead. + /// \param[in] _allocator An instance of CloudAllocator + /// \param[in] _callback An instance of CloudClientCallback + virtual void SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback); + + /// \brief Uploads data to the cloud + /// \details Data uploaded to the cloud will be stored by the server sent to, identified by \a systemIdentifier. + /// As long as you are connected to this server, the data will persist. Queries for that data by the Get() operation will + /// return the RakNetGUID and SystemAddress of the uploader, as well as the data itself. + /// Furthermore, if any clients are subscribed to the particular CloudKey passed, those clients will get update notices that the data has changed + /// Passing data with the same \a cloudKey more than once will overwrite the prior value. + /// This call will silently fail if CloudServer::SetMaxUploadBytesPerClient() is exceeded + /// \param[in] cloudKey Identifies the data being uploaded + /// \param[in] data A pointer to data to upload. This pointer does not need to persist past the call + /// \param[in] dataLengthBytes The length in bytes of \a data + /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. + virtual void Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier); + + /// \brief Releases one or more data previously uploaded with Post() + /// \details If a remote system has subscribed to one or more of the \a keys uploaded, they will get ID_CLOUD_SUBSCRIPTION_NOTIFICATION notifications containing the last value uploaded before deletions + /// \param[in] cloudKey Identifies the data to release. It is possible to remove uploads from multiple Post() calls at once. + /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. + virtual void Release(DataStructures::List &keys, RakNetGUID systemIdentifier); + + /// \brief Gets data from the cloud + /// \details For a given query containing one or more keys, return data that matches those keys. + /// The values will be returned in the ID_CLOUD_GET_RESPONSE packet, which should be passed to OnGetReponse() and will invoke CloudClientCallback::OnGet() + /// CloudQuery::startingRowIndex is used to skip the first n values that would normally be returned.. + /// CloudQuery::maxRowsToReturn is used to limit the number of rows returned. The number of rows returned may also be limited by CloudServer::SetMaxBytesPerDownload(); + /// CloudQuery::subscribeToResults if set to true, will cause ID_CLOUD_SUBSCRIPTION_NOTIFICATION to be returned to us when any of the keys in the query are updated or are deleted. + /// ID_CLOUD_GET_RESPONSE will be returned even if subscribing to the result list. Only later updates will return ID_CLOUD_SUBSCRIPTION_NOTIFICATION. + /// Calling Get() with CloudQuery::subscribeToResults false, when you are already subscribed, does not remove the subscription. Use Unsubscribe() for this. + /// Resubscribing using the same CloudKey but a different or no \a specificSystems overwrites the subscribed systems for those keys. + /// \param[in] cloudQuery One or more keys, and optional parameters to perform with the Get + /// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to. + /// \param[in] specificSystems It is possible to get or subscribe to updates only for specific uploading CloudClient instances. Pass the desired instances here. The overload that does not have the specificSystems parameter is treated as subscribing to all updates from all clients. + virtual bool Get(CloudQuery *cloudQuery, RakNetGUID systemIdentifier); + virtual bool Get(CloudQuery *cloudQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); + virtual bool Get(CloudQuery *cloudQuery, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); + + /// \brief Unsubscribe from updates previously subscribed to using Get() with the CloudQuery::subscribeToResults set to true + /// The \a keys and \a specificSystems parameters are logically treated as AND when checking subscriptions on the server + /// The overload that does not take specificSystems unsubscribes to all passed keys, regardless of system + /// You cannot unsubscribe specific systems when previously subscribed to updates from any system. To do this, first Unsubscribe() from all systems, and call Get() with the \a specificSystems parameter explicilty listing the systems you want to subscribe to. + virtual void Unsubscribe(DataStructures::List &keys, RakNetGUID systemIdentifier); + virtual void Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); + virtual void Unsubscribe(DataStructures::List &keys, DataStructures::List &specificSystems, RakNetGUID systemIdentifier); + + /// \brief Call this when you get ID_CLOUD_GET_RESPONSE + /// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used + /// \param[in] packet Packet structure returned from RakPeerInterface + /// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used. + /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. + virtual void OnGetReponse(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0); + + /// \brief Call this when you get ID_CLOUD_GET_RESPONSE + /// Different form of OnGetReponse that returns to a structure that you pass, instead of using a callback + /// You are responsible for deallocation with this form + /// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used + /// \param[out] cloudQueryResult A pointer to a structure that will be filled out with data + /// \param[in] packet Packet structure returned from RakPeerInterface + /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. + virtual void OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator=0); + + /// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION + /// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used + /// \param[in] packet Packet structure returned from RakPeerInterface + /// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used. + /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. + virtual void OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0); + + /// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION + /// Different form of OnSubscriptionNotification that returns to a structure that you pass, instead of using a callback + /// You are responsible for deallocation with this form + /// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used + /// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion + /// \param[out] row A pointer to a structure that will be filled out with data + /// \param[in] packet Packet structure returned from RakPeerInterface + /// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used. + virtual void OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator=0); + + /// If you never specified an allocator, and used the non-callback form of OnGetReponse(), deallocate cloudQueryResult with this function + virtual void DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult); + + /// If you never specified an allocator, and used the non-callback form of OnSubscriptionNotification(), deallocate row with this function + virtual void DeallocateWithDefaultAllocator(CloudQueryRow *row); + +protected: + PluginReceiveResult OnReceive(Packet *packet); + + CloudClientCallback *callback; + CloudAllocator *allocator; + + CloudAllocator unsetDefaultAllocator; +}; + +/// \ingroup CLOUD_GROUP +/// Parses ID_CLOUD_GET_RESPONSE and ID_CLOUD_SUBSCRIPTION_NOTIFICATION in a convenient callback form +class RAK_DLL_EXPORT CloudClientCallback +{ +public: + CloudClientCallback() {} + virtual ~CloudClientCallback() {} + + /// \brief Called in response to ID_CLOUD_GET_RESPONSE + /// \param[out] result Contains the original query passed to Get(), and a list of rows returned. + /// \param[out] deallocateRowsAfterReturn CloudQueryResult::rowsReturned will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator. + virtual void OnGet(RakNet::CloudQueryResult *result, bool *deallocateRowsAfterReturn) {(void) result; (void) deallocateRowsAfterReturn;} + + /// \brief Called in response to ID_CLOUD_SUBSCRIPTION_NOTIFICATION + /// \param[out] result Contains the row updated + /// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion + /// \param[out] deallocateRowAfterReturn \a result will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator. + virtual void OnSubscriptionNotification(RakNet::CloudQueryRow *result, bool wasUpdated, bool *deallocateRowAfterReturn) {(void) result; (void) wasUpdated; (void) deallocateRowAfterReturn;} +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/CloudCommon.cpp b/src/raknet/CloudCommon.cpp index 7d07410..083857d 100755 --- a/src/raknet/CloudCommon.cpp +++ b/src/raknet/CloudCommon.cpp @@ -1,159 +1,159 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 - -#include "CloudCommon.h" -#include "BitStream.h" - -using namespace RakNet; - -int RakNet::CloudKeyComp(const CloudKey &key, const CloudKey &data) -{ - if (key.primaryKey < data.primaryKey) - return -1; - if (key.primaryKey > data.primaryKey) - return 1; - if (key.secondaryKey < data.secondaryKey) - return -1; - if (key.secondaryKey > data.secondaryKey) - return 1; - return 0; -} - -CloudQueryRow* CloudAllocator::AllocateCloudQueryRow(void) -{ - return RakNet::OP_NEW(_FILE_AND_LINE_); -} -void CloudAllocator::DeallocateCloudQueryRow(CloudQueryRow *row) -{ - RakNet::OP_DELETE(row,_FILE_AND_LINE_); -} -unsigned char *CloudAllocator::AllocateRowData(uint32_t bytesNeededForData) -{ - return (unsigned char*) rakMalloc_Ex(bytesNeededForData,_FILE_AND_LINE_); -} -void CloudAllocator::DeallocateRowData(void *data) -{ - rakFree_Ex(data, _FILE_AND_LINE_); -} -void CloudKey::Serialize(bool writeToBitstream, BitStream *bitStream) -{ - bitStream->Serialize(writeToBitstream, primaryKey); - bitStream->Serialize(writeToBitstream, secondaryKey); -} -void CloudQuery::Serialize(bool writeToBitstream, BitStream *bitStream) -{ - bool startingRowIndexIsZero=0; - bool maxRowsToReturnIsZero=0; - startingRowIndexIsZero=startingRowIndex==0; - maxRowsToReturnIsZero=maxRowsToReturn==0; - bitStream->Serialize(writeToBitstream,startingRowIndexIsZero); - bitStream->Serialize(writeToBitstream,maxRowsToReturnIsZero); - bitStream->Serialize(writeToBitstream,subscribeToResults); - if (startingRowIndexIsZero==false) - bitStream->Serialize(writeToBitstream,startingRowIndex); - if (maxRowsToReturnIsZero==false) - bitStream->Serialize(writeToBitstream,maxRowsToReturn); - RakAssert(keys.Size()<(uint16_t)-1); - uint16_t numKeys = (uint16_t) keys.Size(); - bitStream->Serialize(writeToBitstream,numKeys); - if (writeToBitstream) - { - for (uint16_t i=0; i < numKeys; i++) - { - keys[i].Serialize(true,bitStream); - } - } - else - { - CloudKey cmdk; - for (uint16_t i=0; i < numKeys; i++) - { - cmdk.Serialize(false,bitStream); - keys.Push(cmdk, _FILE_AND_LINE_); - } - } -} -void CloudQueryRow::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator) -{ - key.Serialize(writeToBitstream,bitStream); - bitStream->Serialize(writeToBitstream,serverSystemAddress); - bitStream->Serialize(writeToBitstream,clientSystemAddress); - bitStream->Serialize(writeToBitstream,serverGUID); - bitStream->Serialize(writeToBitstream,clientGUID); - bitStream->Serialize(writeToBitstream,length); - if (writeToBitstream) - { - bitStream->WriteAlignedBytes((const unsigned char*) data,length); - } - else - { - if (length>0) - { - data = allocator->AllocateRowData(length); - if (data) - { - bitStream->ReadAlignedBytes((unsigned char *) data,length); - } - else - { - notifyOutOfMemory(_FILE_AND_LINE_); - } - } - else - data=0; - } -} -void CloudQueryResult::SerializeHeader(bool writeToBitstream, BitStream *bitStream) -{ - cloudQuery.Serialize(writeToBitstream,bitStream); - bitStream->Serialize(writeToBitstream,subscribeToResults); -} -void CloudQueryResult::SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream) -{ - bitStream->Serialize(writeToBitstream,numRows); -} -void CloudQueryResult::SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator) -{ - if (writeToBitstream) - { - for (uint16_t i=0; i < numRows; i++) - { - rowsReturned[i]->Serialize(true,bitStream, allocator); - } - } - else - { - CloudQueryRow* cmdr; - for (uint16_t i=0; i < numRows; i++) - { - cmdr = allocator->AllocateCloudQueryRow(); - if (cmdr) - { - cmdr->Serialize(false,bitStream,allocator); - if (cmdr->data==0 && cmdr->length>0) - { - allocator->DeallocateCloudQueryRow(cmdr); - notifyOutOfMemory(_FILE_AND_LINE_); - numRows=i; - return; - } - rowsReturned.Push(cmdr, _FILE_AND_LINE_); - } - else - { - notifyOutOfMemory(_FILE_AND_LINE_); - numRows=i; - return; - } - } - } -} -void CloudQueryResult::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator) -{ - SerializeHeader(writeToBitstream, bitStream); - uint32_t numRows = (uint32_t) rowsReturned.Size(); - SerializeNumRows(writeToBitstream, numRows, bitStream); - SerializeCloudQueryRows(writeToBitstream, numRows, bitStream, allocator); -} - -#endif // #if _RAKNET_SUPPORT_CloudMemoryClient==1 || _RAKNET_SUPPORT_CloudMemoryServer==1 +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 + +#include "CloudCommon.h" +#include "BitStream.h" + +using namespace RakNet; + +int RakNet::CloudKeyComp(const CloudKey &key, const CloudKey &data) +{ + if (key.primaryKey < data.primaryKey) + return -1; + if (key.primaryKey > data.primaryKey) + return 1; + if (key.secondaryKey < data.secondaryKey) + return -1; + if (key.secondaryKey > data.secondaryKey) + return 1; + return 0; +} + +CloudQueryRow* CloudAllocator::AllocateCloudQueryRow(void) +{ + return RakNet::OP_NEW(_FILE_AND_LINE_); +} +void CloudAllocator::DeallocateCloudQueryRow(CloudQueryRow *row) +{ + RakNet::OP_DELETE(row,_FILE_AND_LINE_); +} +unsigned char *CloudAllocator::AllocateRowData(uint32_t bytesNeededForData) +{ + return (unsigned char*) rakMalloc_Ex(bytesNeededForData,_FILE_AND_LINE_); +} +void CloudAllocator::DeallocateRowData(void *data) +{ + rakFree_Ex(data, _FILE_AND_LINE_); +} +void CloudKey::Serialize(bool writeToBitstream, BitStream *bitStream) +{ + bitStream->Serialize(writeToBitstream, primaryKey); + bitStream->Serialize(writeToBitstream, secondaryKey); +} +void CloudQuery::Serialize(bool writeToBitstream, BitStream *bitStream) +{ + bool startingRowIndexIsZero=0; + bool maxRowsToReturnIsZero=0; + startingRowIndexIsZero=startingRowIndex==0; + maxRowsToReturnIsZero=maxRowsToReturn==0; + bitStream->Serialize(writeToBitstream,startingRowIndexIsZero); + bitStream->Serialize(writeToBitstream,maxRowsToReturnIsZero); + bitStream->Serialize(writeToBitstream,subscribeToResults); + if (startingRowIndexIsZero==false) + bitStream->Serialize(writeToBitstream,startingRowIndex); + if (maxRowsToReturnIsZero==false) + bitStream->Serialize(writeToBitstream,maxRowsToReturn); + RakAssert(keys.Size()<(uint16_t)-1); + uint16_t numKeys = (uint16_t) keys.Size(); + bitStream->Serialize(writeToBitstream,numKeys); + if (writeToBitstream) + { + for (uint16_t i=0; i < numKeys; i++) + { + keys[i].Serialize(true,bitStream); + } + } + else + { + CloudKey cmdk; + for (uint16_t i=0; i < numKeys; i++) + { + cmdk.Serialize(false,bitStream); + keys.Push(cmdk, _FILE_AND_LINE_); + } + } +} +void CloudQueryRow::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator) +{ + key.Serialize(writeToBitstream,bitStream); + bitStream->Serialize(writeToBitstream,serverSystemAddress); + bitStream->Serialize(writeToBitstream,clientSystemAddress); + bitStream->Serialize(writeToBitstream,serverGUID); + bitStream->Serialize(writeToBitstream,clientGUID); + bitStream->Serialize(writeToBitstream,length); + if (writeToBitstream) + { + bitStream->WriteAlignedBytes((const unsigned char*) data,length); + } + else + { + if (length>0) + { + data = allocator->AllocateRowData(length); + if (data) + { + bitStream->ReadAlignedBytes((unsigned char *) data,length); + } + else + { + notifyOutOfMemory(_FILE_AND_LINE_); + } + } + else + data=0; + } +} +void CloudQueryResult::SerializeHeader(bool writeToBitstream, BitStream *bitStream) +{ + cloudQuery.Serialize(writeToBitstream,bitStream); + bitStream->Serialize(writeToBitstream,subscribeToResults); +} +void CloudQueryResult::SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream) +{ + bitStream->Serialize(writeToBitstream,numRows); +} +void CloudQueryResult::SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator) +{ + if (writeToBitstream) + { + for (uint16_t i=0; i < numRows; i++) + { + rowsReturned[i]->Serialize(true,bitStream, allocator); + } + } + else + { + CloudQueryRow* cmdr; + for (uint16_t i=0; i < numRows; i++) + { + cmdr = allocator->AllocateCloudQueryRow(); + if (cmdr) + { + cmdr->Serialize(false,bitStream,allocator); + if (cmdr->data==0 && cmdr->length>0) + { + allocator->DeallocateCloudQueryRow(cmdr); + notifyOutOfMemory(_FILE_AND_LINE_); + numRows=i; + return; + } + rowsReturned.Push(cmdr, _FILE_AND_LINE_); + } + else + { + notifyOutOfMemory(_FILE_AND_LINE_); + numRows=i; + return; + } + } + } +} +void CloudQueryResult::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator) +{ + SerializeHeader(writeToBitstream, bitStream); + uint32_t numRows = (uint32_t) rowsReturned.Size(); + SerializeNumRows(writeToBitstream, numRows, bitStream); + SerializeCloudQueryRows(writeToBitstream, numRows, bitStream, allocator); +} + +#endif // #if _RAKNET_SUPPORT_CloudMemoryClient==1 || _RAKNET_SUPPORT_CloudMemoryServer==1 diff --git a/src/raknet/CloudCommon.h b/src/raknet/CloudCommon.h index dddb2cc..23fdf99 100755 --- a/src/raknet/CloudCommon.h +++ b/src/raknet/CloudCommon.h @@ -1,141 +1,141 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 - -#ifndef __CLOUD_COMMON_H -#define __CLOUD_COMMON_H - -#include "RakNetTypes.h" -#include "RakString.h" - -namespace RakNet -{ - -class BitStream; -struct CloudQueryRow; - -/// Allocates CloudQueryRow and the row data. Override to use derived classes or different allocators -/// \ingroup CLOUD_GROUP -class RAK_DLL_EXPORT CloudAllocator -{ -public: - CloudAllocator() {} - virtual ~CloudAllocator() {} - - /// \brief Allocate a row - virtual CloudQueryRow* AllocateCloudQueryRow(void); - /// \brief Free a row - virtual void DeallocateCloudQueryRow(CloudQueryRow *row); - /// \brief Allocate CloudQueryRow::data - virtual unsigned char *AllocateRowData(uint32_t bytesNeededForData); - /// \brief Free CloudQueryRow::data - virtual void DeallocateRowData(void *data); -}; - -/// Serves as a key to identify data uploaded to or queried from the server. -/// \ingroup CLOUD_GROUP -struct RAK_DLL_EXPORT CloudKey -{ - CloudKey() {} - CloudKey(RakNet::RakString _primaryKey, uint32_t _secondaryKey) : primaryKey(_primaryKey), secondaryKey(_secondaryKey) {} - ~CloudKey() {} - - /// Identifies the primary key. This is intended to be a major category, such as the name of the application - /// Must be non-empty - RakNet::RakString primaryKey; - - /// Identifies the secondary key. This is intended to be a subcategory enumeration, such as PLAYER_LIST or RUNNING_SCORES - uint32_t secondaryKey; - - /// \internal - void Serialize(bool writeToBitstream, BitStream *bitStream); -}; - -/// \internal -int CloudKeyComp(const CloudKey &key, const CloudKey &data); - -/// Data members used to query the cloud -/// \ingroup CLOUD_GROUP -struct RAK_DLL_EXPORT CloudQuery -{ - CloudQuery() {startingRowIndex=0; maxRowsToReturn=0; subscribeToResults=false;} - - /// List of keys to query. Must be at least of length 1. - /// This query is run on uploads from all clients, and those that match the combination of primaryKey and secondaryKey are potentially returned - /// If you pass more than one key at a time, the results are concatenated so if you need to differentiate between queries then send two different queries - DataStructures::List keys; - - /// If limiting the number of rows to return, this is the starting offset into the list. Has no effect unless maxRowsToReturn is > 0 - uint32_t startingRowIndex; - - /// Maximum number of rows to return. Actual number may still be less than this. Pass 0 to mean no-limit. - uint32_t maxRowsToReturn; - - /// If true, automatically get updates as the results returned to you change. Unsubscribe with CloudMemoryClient::Unsubscribe() - bool subscribeToResults; - - /// \internal - void Serialize(bool writeToBitstream, BitStream *bitStream); -}; - -/// \ingroup CLOUD_GROUP -struct RAK_DLL_EXPORT CloudQueryRow -{ - /// Key used to identify this data - CloudKey key; - - /// Data uploaded - unsigned char *data; - - /// Length of data uploaded - uint32_t length; - - /// System address of server that is holding this data, and the client is connected to - SystemAddress serverSystemAddress; - - /// System address of client that uploaded this data - SystemAddress clientSystemAddress; - - /// RakNetGUID of server that is holding this data, and the client is connected to - RakNetGUID serverGUID; - - /// RakNetGUID of client that uploaded this data - RakNetGUID clientGUID; - - /// \internal - void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator); -}; - -/// \ingroup CLOUD_GROUP -struct RAK_DLL_EXPORT CloudQueryResult -{ - /// Query originally passed to Download() - CloudQuery cloudQuery; - - /// Results returned from query. If there were multiple keys in CloudQuery::keys then see resultKeyIndices - DataStructures::List rowsReturned; - - /// If there were multiple keys in CloudQuery::keys, then each key is processed in order and the result concatenated to rowsReturned - /// The starting index of each query is written to resultKeyIndices - /// For example, if CloudQuery::keys had 4 keys, returning 3 rows, 0, rows, 5 rows, and 12 rows then - /// resultKeyIndices would be 0, 3, 3, 8 - DataStructures::List resultKeyIndices; - - - /// Whatever was passed to CloudClient::Get() as CloudQuery::subscribeToResults - bool subscribeToResults; - - /// \internal - void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator); - /// \internal - void SerializeHeader(bool writeToBitstream, BitStream *bitStream); - /// \internal - void SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream); - /// \internal - void SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator); -}; - -} // Namespace RakNet - -#endif // __CLOUD_COMMON_H - -#endif // #if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 + +#ifndef __CLOUD_COMMON_H +#define __CLOUD_COMMON_H + +#include "RakNetTypes.h" +#include "RakString.h" + +namespace RakNet +{ + +class BitStream; +struct CloudQueryRow; + +/// Allocates CloudQueryRow and the row data. Override to use derived classes or different allocators +/// \ingroup CLOUD_GROUP +class RAK_DLL_EXPORT CloudAllocator +{ +public: + CloudAllocator() {} + virtual ~CloudAllocator() {} + + /// \brief Allocate a row + virtual CloudQueryRow* AllocateCloudQueryRow(void); + /// \brief Free a row + virtual void DeallocateCloudQueryRow(CloudQueryRow *row); + /// \brief Allocate CloudQueryRow::data + virtual unsigned char *AllocateRowData(uint32_t bytesNeededForData); + /// \brief Free CloudQueryRow::data + virtual void DeallocateRowData(void *data); +}; + +/// Serves as a key to identify data uploaded to or queried from the server. +/// \ingroup CLOUD_GROUP +struct RAK_DLL_EXPORT CloudKey +{ + CloudKey() {} + CloudKey(RakNet::RakString _primaryKey, uint32_t _secondaryKey) : primaryKey(_primaryKey), secondaryKey(_secondaryKey) {} + ~CloudKey() {} + + /// Identifies the primary key. This is intended to be a major category, such as the name of the application + /// Must be non-empty + RakNet::RakString primaryKey; + + /// Identifies the secondary key. This is intended to be a subcategory enumeration, such as PLAYER_LIST or RUNNING_SCORES + uint32_t secondaryKey; + + /// \internal + void Serialize(bool writeToBitstream, BitStream *bitStream); +}; + +/// \internal +int CloudKeyComp(const CloudKey &key, const CloudKey &data); + +/// Data members used to query the cloud +/// \ingroup CLOUD_GROUP +struct RAK_DLL_EXPORT CloudQuery +{ + CloudQuery() {startingRowIndex=0; maxRowsToReturn=0; subscribeToResults=false;} + + /// List of keys to query. Must be at least of length 1. + /// This query is run on uploads from all clients, and those that match the combination of primaryKey and secondaryKey are potentially returned + /// If you pass more than one key at a time, the results are concatenated so if you need to differentiate between queries then send two different queries + DataStructures::List keys; + + /// If limiting the number of rows to return, this is the starting offset into the list. Has no effect unless maxRowsToReturn is > 0 + uint32_t startingRowIndex; + + /// Maximum number of rows to return. Actual number may still be less than this. Pass 0 to mean no-limit. + uint32_t maxRowsToReturn; + + /// If true, automatically get updates as the results returned to you change. Unsubscribe with CloudMemoryClient::Unsubscribe() + bool subscribeToResults; + + /// \internal + void Serialize(bool writeToBitstream, BitStream *bitStream); +}; + +/// \ingroup CLOUD_GROUP +struct RAK_DLL_EXPORT CloudQueryRow +{ + /// Key used to identify this data + CloudKey key; + + /// Data uploaded + unsigned char *data; + + /// Length of data uploaded + uint32_t length; + + /// System address of server that is holding this data, and the client is connected to + SystemAddress serverSystemAddress; + + /// System address of client that uploaded this data + SystemAddress clientSystemAddress; + + /// RakNetGUID of server that is holding this data, and the client is connected to + RakNetGUID serverGUID; + + /// RakNetGUID of client that uploaded this data + RakNetGUID clientGUID; + + /// \internal + void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator); +}; + +/// \ingroup CLOUD_GROUP +struct RAK_DLL_EXPORT CloudQueryResult +{ + /// Query originally passed to Download() + CloudQuery cloudQuery; + + /// Results returned from query. If there were multiple keys in CloudQuery::keys then see resultKeyIndices + DataStructures::List rowsReturned; + + /// If there were multiple keys in CloudQuery::keys, then each key is processed in order and the result concatenated to rowsReturned + /// The starting index of each query is written to resultKeyIndices + /// For example, if CloudQuery::keys had 4 keys, returning 3 rows, 0, rows, 5 rows, and 12 rows then + /// resultKeyIndices would be 0, 3, 3, 8 + DataStructures::List resultKeyIndices; + + + /// Whatever was passed to CloudClient::Get() as CloudQuery::subscribeToResults + bool subscribeToResults; + + /// \internal + void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator); + /// \internal + void SerializeHeader(bool writeToBitstream, BitStream *bitStream); + /// \internal + void SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream); + /// \internal + void SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator); +}; + +} // Namespace RakNet + +#endif // __CLOUD_COMMON_H + +#endif // #if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1 diff --git a/src/raknet/CloudServer.cpp b/src/raknet/CloudServer.cpp index d5fc8c1..f1c7529 100755 --- a/src/raknet/CloudServer.cpp +++ b/src/raknet/CloudServer.cpp @@ -1,1673 +1,1673 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudServer==1 - -#include "CloudServer.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" -#include "RakPeerInterface.h" - -enum ServerToServerCommands -{ - STSC_PROCESS_GET_REQUEST, - STSC_PROCESS_GET_RESPONSE, - STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS, - STSC_ADD_UPLOADED_KEY, - STSC_ADD_SUBSCRIBED_KEY, - STSC_REMOVE_UPLOADED_KEY, - STSC_REMOVE_SUBSCRIBED_KEY, - STSC_DATA_CHANGED, -}; - -using namespace RakNet; - -int CloudServer::RemoteServerComp(const RakNetGUID &key, RemoteServer* const &data ) -{ - if (key < data->serverAddress) - return -1; - if (key > data->serverAddress) - return 1; - return 0; -} -int CloudServer::KeySubscriberIDComp(const CloudKey &key, KeySubscriberID * const &data ) -{ - if (key.primaryKey < data->key.primaryKey) - return -1; - if (key.primaryKey > data->key.primaryKey) - return 1; - if (key.secondaryKey < data->key.secondaryKey) - return -1; - if (key.secondaryKey > data->key.secondaryKey) - return 1; - return 0; -} -int CloudServer::KeyDataPtrComp( const RakNetGUID &key, CloudData* const &data ) -{ - if (key < data->clientGUID) - return -1; - if (key > data->clientGUID) - return 1; - return 0; -} -int CloudServer::KeyDataListComp( const CloudKey &key, CloudDataList * const &data ) -{ - if (key.primaryKey < data->key.primaryKey) - return -1; - if (key.primaryKey > data->key.primaryKey) - return 1; - if (key.secondaryKey < data->key.secondaryKey) - return -1; - if (key.secondaryKey > data->key.secondaryKey) - return 1; - return 0; -} -int CloudServer::BufferedGetResponseFromServerComp(const RakNetGUID &key, CloudServer::BufferedGetResponseFromServer* const &data ) -{ - if (key < data->serverAddress) - return -1; - if (key > data->serverAddress) - return 1; - return 0; -} -int CloudServer::GetRequestComp(const uint32_t &key, CloudServer::GetRequest* const &data ) -{ - if (key < data->requestId) - return -1; - if (key > data->requestId) - return -1; - return 0; -} -void CloudServer::CloudQueryWithAddresses::Serialize(bool writeToBitstream, BitStream *bitStream) -{ - cloudQuery.Serialize(writeToBitstream, bitStream); - - if (writeToBitstream) - { - bitStream->WriteCasted(specificSystems.Size()); - RakAssert(specificSystems.Size() < (uint16_t)-1 ); - for (uint16_t i=0; i < specificSystems.Size(); i++) - { - bitStream->Write(specificSystems[i]); - } - } - else - { - uint16_t specificSystemsCount; - RakNetGUID addressOrGuid; - bitStream->Read(specificSystemsCount); - for (uint16_t i=0; i < specificSystemsCount; i++) - { - bitStream->Read(addressOrGuid); - specificSystems.Push(addressOrGuid, _FILE_AND_LINE_); - } - } -} -bool CloudServer::GetRequest::AllRemoteServersHaveResponded(void) const -{ - unsigned int i; - for (i=0; i < remoteServerResponses.Size(); i++) - if (remoteServerResponses[i]->gotResult==false) - return false; - return true; -} -void CloudServer::GetRequest::Clear(CloudAllocator *allocator) -{ - unsigned int i; - for (i=0; i < remoteServerResponses.Size(); i++) - { - remoteServerResponses[i]->Clear(allocator); - RakNet::OP_DELETE(remoteServerResponses[i], _FILE_AND_LINE_); - } - remoteServerResponses.Clear(false, _FILE_AND_LINE_); -} -void CloudServer::BufferedGetResponseFromServer::Clear(CloudAllocator *allocator) -{ - unsigned int i; - for (i=0; i < queryResult.rowsReturned.Size(); i++) - { - allocator->DeallocateRowData(queryResult.rowsReturned[i]->data); - allocator->DeallocateCloudQueryRow(queryResult.rowsReturned[i]); - } - queryResult.rowsReturned.Clear(false, _FILE_AND_LINE_); -} -CloudServer::CloudServer() -{ - maxUploadBytesPerClient=0; - maxBytesPerDowload=0; - nextGetRequestId=0; - nextGetRequestsCheck=0; -} -CloudServer::~CloudServer() -{ - Clear(); -} -void CloudServer::SetMaxUploadBytesPerClient(uint64_t bytes) -{ - maxUploadBytesPerClient=bytes; -} -void CloudServer::SetMaxBytesPerDownload(uint64_t bytes) -{ - maxBytesPerDowload=bytes; -} -void CloudServer::Update(void) -{ - // Timeout getRequests - RakNet::Time time = RakNet::Time(); - if (time > nextGetRequestsCheck) - { - nextGetRequestsCheck=time+1000; - - unsigned int i=0; - while (i < getRequests.Size()) - { - if (time - getRequests[i]->requestStartTime > 3000) - { - // Remote server is not responding, just send back data with whoever did respond - ProcessAndTransmitGetRequest(getRequests[i]); - getRequests[i]->Clear(this); - RakNet::OP_DELETE(getRequests[i],_FILE_AND_LINE_); - getRequests.RemoveAtIndex(i); - } - else - { - i++; - } - } - } -} -PluginReceiveResult CloudServer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_CLOUD_POST_REQUEST: - OnPostRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_CLOUD_RELEASE_REQUEST: - OnReleaseRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_CLOUD_GET_REQUEST: - OnGetRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_CLOUD_UNSUBSCRIBE_REQUEST: - OnUnsubscribeRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_CLOUD_SERVER_TO_SERVER_COMMAND: - if (packet->length>1) - { - switch (packet->data[1]) - { - case STSC_PROCESS_GET_REQUEST: - OnServerToServerGetRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_PROCESS_GET_RESPONSE: - OnServerToServerGetResponse(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS: - OnSendUploadedAndSubscribedKeysToServer(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_ADD_UPLOADED_KEY: - OnSendUploadedKeyToServers(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_ADD_SUBSCRIBED_KEY: - OnSendSubscribedKeyToServers(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_REMOVE_UPLOADED_KEY: - OnRemoveUploadedKeyFromServers(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_REMOVE_SUBSCRIBED_KEY: - OnRemoveSubscribedKeyFromServers(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case STSC_DATA_CHANGED: - OnServerDataChanged(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - return RR_CONTINUE_PROCESSING; -} -void CloudServer::OnPostRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - CloudKey key; - key.Serialize(false,&bsIn); - uint32_t dataLengthBytes; - bsIn.Read(dataLengthBytes); - if (maxUploadBytesPerClient>0 && dataLengthBytes>maxUploadBytesPerClient) - return; // Exceeded max upload bytes - - bsIn.AlignReadToByteBoundary(); - for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) - { - if (queryFilters[filterIndex]->OnPostRequest(packet->guid, packet->systemAddress, key, dataLengthBytes, (const char*) bsIn.GetData()+BITS_TO_BYTES(bsIn.GetReadOffset()))==false) - return; - } - - unsigned char *data; - if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) - { - data = (unsigned char *) rakMalloc_Ex(dataLengthBytes,_FILE_AND_LINE_); - if (data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return; - } - bsIn.ReadAlignedBytes(data,dataLengthBytes); - } - else - data=0; - - // Add this system to remoteSystems if they aren't there already - DataStructures::HashIndex remoteSystemsHashIndex = remoteSystems.GetIndexOf(packet->guid); - RemoteCloudClient *remoteCloudClient; - if (remoteSystemsHashIndex.IsInvalid()) - { - remoteCloudClient = RakNet::OP_NEW(_FILE_AND_LINE_); - remoteCloudClient->uploadedKeys.Insert(key,key,true,_FILE_AND_LINE_); - remoteCloudClient->uploadedBytes=0; - remoteSystems.Push(packet->guid, remoteCloudClient, _FILE_AND_LINE_); - } - else - { - remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemsHashIndex); - bool objectExists; - // Add to RemoteCloudClient::uploadedKeys if it isn't there already - unsigned int uploadedKeysIndex = remoteCloudClient->uploadedKeys.GetIndexFromKey(key,&objectExists); - if (objectExists==false) - { - remoteCloudClient->uploadedKeys.InsertAtIndex(key, uploadedKeysIndex, _FILE_AND_LINE_); - } - } - - bool cloudDataAlreadyUploaded; - unsigned int dataRepositoryIndex; - bool dataRepositoryExists; - CloudDataList* cloudDataList = GetOrAllocateCloudDataList(key, &dataRepositoryExists, dataRepositoryIndex); - if (dataRepositoryExists==false) - { - cloudDataList->uploaderCount=1; - cloudDataAlreadyUploaded=false; - } - else - { - cloudDataAlreadyUploaded=cloudDataList->uploaderCount>0; - cloudDataList->uploaderCount++; - } - - CloudData *cloudData; - bool keyDataListExists; - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(packet->guid, &keyDataListExists); - if (keyDataListExists==false) - { - if (maxUploadBytesPerClient>0 && remoteCloudClient->uploadedBytes+dataLengthBytes>maxUploadBytesPerClient) - { - // Undo prior insertion of cloudDataList into cloudData if needed - if (keyDataListExists==false) - { - RakNet::OP_DELETE(cloudDataList,_FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } - - if (remoteCloudClient->IsUnused()) - { - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - remoteSystems.Remove(packet->guid, _FILE_AND_LINE_); - } - - if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) - rakFree_Ex(data, _FILE_AND_LINE_); - - return; - } - - cloudData = RakNet::OP_NEW(_FILE_AND_LINE_); - cloudData->dataLengthBytes=dataLengthBytes; - cloudData->isUploaded=true; - if (forceAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - cloudData->serverSystemAddress=forceAddress; - cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetExternalID(packet->systemAddress).GetPort()); - } - else - { - cloudData->serverSystemAddress=rakPeerInterface->GetExternalID(packet->systemAddress); - } - if (cloudData->serverSystemAddress.GetPort()==0) - { - // Fix localhost port - cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetSocket(UNASSIGNED_SYSTEM_ADDRESS)->boundAddress.GetPort()); - } - cloudData->clientSystemAddress=packet->systemAddress; - cloudData->serverGUID=rakPeerInterface->GetMyGUID(); - cloudData->clientGUID=packet->guid; - cloudDataList->keyData.Insert(packet->guid,cloudData,true,_FILE_AND_LINE_); - } - else - { - cloudData = cloudDataList->keyData[keyDataListIndex]; - - if (cloudDataAlreadyUploaded==false) - { - if (forceAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - cloudData->serverSystemAddress=forceAddress; - cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetExternalID(packet->systemAddress).GetPort()); - } - else - { - cloudData->serverSystemAddress=rakPeerInterface->GetExternalID(packet->systemAddress); - } - if (cloudData->serverSystemAddress.GetPort()==0) - { - // Fix localhost port - cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetSocket(UNASSIGNED_SYSTEM_ADDRESS)->boundAddress.GetPort()); - } - - cloudData->clientSystemAddress=packet->systemAddress; - } - - if (maxUploadBytesPerClient>0 && remoteCloudClient->uploadedBytes-cloudData->dataLengthBytes+dataLengthBytes>maxUploadBytesPerClient) - { - // Undo prior insertion of cloudDataList into cloudData if needed - if (dataRepositoryExists==false) - { - RakNet::OP_DELETE(cloudDataList,_FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } - return; - } - else - { - // Subtract already used bytes we are overwriting - remoteCloudClient->uploadedBytes-=cloudData->dataLengthBytes; - } - - if (cloudData->allocatedData!=0) - rakFree_Ex(cloudData->allocatedData,_FILE_AND_LINE_); - } - - if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) - { - // Data already allocated - cloudData->allocatedData=data; - cloudData->dataPtr=data; - } - else - { - // Read to stack - if (dataLengthBytes>0) - bsIn.ReadAlignedBytes(cloudData->stackData,dataLengthBytes); - cloudData->allocatedData=0; - cloudData->dataPtr=cloudData->stackData; - } - // Update how many bytes were written for this data - cloudData->dataLengthBytes=dataLengthBytes; - remoteCloudClient->uploadedBytes+=dataLengthBytes; - - if (cloudDataAlreadyUploaded==false) - { - // New data field - SendUploadedKeyToServers(cloudDataList->key); - } - - // Existing data field changed - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, true ); - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, true ); - - // Send update to all remote servers that subscribed to this key - NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, true); - - // I could have also subscribed to a key not yet updated locally - // This means I have to go through every RemoteClient that wants this key - // Seems like cloudData->specificSubscribers is unnecessary in that case -} -void CloudServer::OnReleaseRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - - uint16_t keyCount; - bsIn.Read(keyCount); - - if (keyCount==0) - return; - - DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(packet->guid); - if (remoteSystemIndex.IsInvalid()==true) - return; - - RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); - - CloudKey key; - - // Read all in a list first so I can run filter on it - DataStructures::List cloudKeys; - for (uint16_t keyCountIndex=0; keyCountIndex < keyCount; keyCountIndex++) - { - key.Serialize(false, &bsIn); - cloudKeys.Push(key, _FILE_AND_LINE_); - } - - for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) - { - if (queryFilters[filterIndex]->OnReleaseRequest(packet->guid, packet->systemAddress, cloudKeys)) - return; - } - - for (uint16_t keyCountIndex=0; keyCountIndex < keyCount; keyCountIndex++) - { - // Serialize in list above so I can run the filter on it - // key.Serialize(false, &bsIn); - key=cloudKeys[keyCountIndex]; - - // Remove remote systems uploaded keys - bool objectExists; - unsigned int uploadedKeysIndex = remoteCloudClient->uploadedKeys.GetIndexFromKey(key,&objectExists); - if (objectExists) - { - bool dataRepositoryExists; - unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(key, &dataRepositoryExists); - CloudDataList* cloudDataList = dataRepository[dataRepositoryIndex]; - RakAssert(cloudDataList); - - CloudData *cloudData; - bool keyDataListExists; - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(packet->guid, &keyDataListExists); - cloudData = cloudDataList->keyData[keyDataListIndex]; - - remoteCloudClient->uploadedKeys.RemoveAtIndex(uploadedKeysIndex); - remoteCloudClient->uploadedBytes-=cloudData->dataLengthBytes; - cloudDataList->uploaderCount--; - - // Broadcast destruction of this key to subscribers - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, false ); - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, false ); - NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, false ); - - cloudData->Clear(); - - if (cloudData->IsUnused()) - { - RakNet::OP_DELETE(cloudData, _FILE_AND_LINE_); - cloudDataList->keyData.RemoveAtIndex(keyDataListIndex); - if (cloudDataList->IsNotUploaded()) - { - // Tell other servers that this key is no longer uploaded, so they do not request it from us - RemoveUploadedKeyFromServers(cloudDataList->key); - } - - if (cloudDataList->IsUnused()) - { - RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } - } - - if (remoteCloudClient->IsUnused()) - { - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); - break; - } - } - } -} -void CloudServer::OnGetRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - uint16_t specificSystemsCount; - CloudKey cloudKey; - - // Create a new GetRequest - GetRequest *getRequest; - getRequest = RakNet::OP_NEW(_FILE_AND_LINE_); - getRequest->cloudQueryWithAddresses.cloudQuery.Serialize(false, &bsIn); - getRequest->requestingClient=packet->guid; - - RakNetGUID addressOrGuid; - bsIn.Read(specificSystemsCount); - for (uint16_t i=0; i < specificSystemsCount; i++) - { - bsIn.Read(addressOrGuid); - getRequest->cloudQueryWithAddresses.specificSystems.Push(addressOrGuid, _FILE_AND_LINE_); - } - - if (getRequest->cloudQueryWithAddresses.cloudQuery.keys.Size()==0) - { - RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); - return; - } - - for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) - { - if (queryFilters[filterIndex]->OnGetRequest(packet->guid, packet->systemAddress, getRequest->cloudQueryWithAddresses.cloudQuery, getRequest->cloudQueryWithAddresses.specificSystems )==false) - return; - } - - getRequest->requestStartTime=RakNet::GetTime(); - getRequest->requestId=nextGetRequestId++; - - // Send request to servers that have this data - DataStructures::List remoteServersWithData; - GetServersWithUploadedKeys(getRequest->cloudQueryWithAddresses.cloudQuery.keys, remoteServersWithData); - - if (remoteServersWithData.Size()==0) - { - ProcessAndTransmitGetRequest(getRequest); - } - else - { - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_PROCESS_GET_REQUEST); - getRequest->cloudQueryWithAddresses.Serialize(true, &bsOut); - bsOut.Write(getRequest->requestId); - - for (unsigned int remoteServerIndex=0; remoteServerIndex < remoteServersWithData.Size(); remoteServerIndex++) - { - BufferedGetResponseFromServer* bufferedGetResponseFromServer = RakNet::OP_NEW(_FILE_AND_LINE_); - bufferedGetResponseFromServer->serverAddress=remoteServersWithData[remoteServerIndex]->serverAddress; - bufferedGetResponseFromServer->gotResult=false; - getRequest->remoteServerResponses.Insert(remoteServersWithData[remoteServerIndex]->serverAddress, bufferedGetResponseFromServer, true, _FILE_AND_LINE_); - - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServersWithData[remoteServerIndex]->serverAddress, false); - } - - // Record that this system made this request - getRequests.Insert(getRequest->requestId, getRequest, true, _FILE_AND_LINE_); - } - - if (getRequest->cloudQueryWithAddresses.cloudQuery.subscribeToResults) - { - // Add to key subscription list for the client, which contains a keyId / specificUploaderList pair - DataStructures::HashIndex remoteSystemsHashIndex = remoteSystems.GetIndexOf(packet->guid); - RemoteCloudClient *remoteCloudClient; - if (remoteSystemsHashIndex.IsInvalid()) - { - remoteCloudClient = RakNet::OP_NEW(_FILE_AND_LINE_); - remoteCloudClient->uploadedBytes=0; - remoteSystems.Push(packet->guid, remoteCloudClient, _FILE_AND_LINE_); - } - else - { - remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemsHashIndex); - } - - unsigned int keyIndex; - for (keyIndex=0; keyIndex < getRequest->cloudQueryWithAddresses.cloudQuery.keys.Size(); keyIndex++) - { - cloudKey = getRequest->cloudQueryWithAddresses.cloudQuery.keys[keyIndex]; - - unsigned int keySubscriberIndex; - bool hasKeySubscriber; - keySubscriberIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudKey, &hasKeySubscriber); - KeySubscriberID* keySubscriberId; - if (hasKeySubscriber) - { - DataStructures::List specificSystems; - UnsubscribeFromKey(remoteCloudClient, packet->guid, keySubscriberIndex, cloudKey, specificSystems); - } - - keySubscriberId = RakNet::OP_NEW(_FILE_AND_LINE_); - keySubscriberId->key=cloudKey; - - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < getRequest->cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) - { - keySubscriberId->specificSystemsSubscribedTo.Insert(getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex], getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex], true, _FILE_AND_LINE_); - } - - remoteCloudClient->subscribedKeys.InsertAtIndex(keySubscriberId, keySubscriberIndex, _FILE_AND_LINE_); - - // Add CloudData in a similar way - unsigned int dataRepositoryIndex; - bool dataRepositoryExists; - CloudDataList* cloudDataList = GetOrAllocateCloudDataList(cloudKey, &dataRepositoryExists, dataRepositoryIndex); - - // If this is the first local client to subscribe to this key, call SendSubscribedKeyToServers - if (cloudDataList->subscriberCount==0) - SendSubscribedKeyToServers(cloudKey); - - // If the subscription is specific, may have to also allocate CloudData - if (getRequest->cloudQueryWithAddresses.specificSystems.Size()) - { - CloudData *cloudData; - bool keyDataListExists; - - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < getRequest->cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) - { - RakNetGUID specificSystem = getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex]; - - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(specificSystem, &keyDataListExists); - if (keyDataListExists==false) - { - cloudData = RakNet::OP_NEW(_FILE_AND_LINE_); - cloudData->dataLengthBytes=0; - cloudData->allocatedData=0; - cloudData->isUploaded=false; - cloudData->dataPtr=0; - cloudData->serverSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; - cloudData->clientSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; - cloudData->serverGUID=rakPeerInterface->GetMyGUID(); - cloudData->clientGUID=specificSystem; - cloudDataList->keyData.Insert(specificSystem,cloudData,true,_FILE_AND_LINE_); - } - else - { - cloudData = cloudDataList->keyData[keyDataListIndex]; - } - - ++cloudDataList->subscriberCount; - cloudData->specificSubscribers.Insert(packet->guid, packet->guid, true, _FILE_AND_LINE_); - } - } - else - { - ++cloudDataList->subscriberCount; - cloudDataList->nonSpecificSubscribers.Insert(packet->guid, packet->guid, true, _FILE_AND_LINE_); - - // Remove packet->guid from CloudData::specificSubscribers among all instances of cloudDataList->keyData - unsigned int subscribedKeysIndex; - bool subscribedKeysIndexExists; - subscribedKeysIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudDataList->key, &subscribedKeysIndexExists); - if (subscribedKeysIndexExists) - { - KeySubscriberID* keySubscriberId; - keySubscriberId = remoteCloudClient->subscribedKeys[subscribedKeysIndex]; - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < keySubscriberId->specificSystemsSubscribedTo.Size(); specificSystemIndex++) - { - bool keyDataExists; - unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(keySubscriberId->specificSystemsSubscribedTo[specificSystemIndex], &keyDataExists); - if (keyDataExists) - { - CloudData *keyData = cloudDataList->keyData[keyDataIndex]; - keyData->specificSubscribers.Remove(packet->guid); - --cloudDataList->subscriberCount; - } - } - } - } - } - - if (remoteCloudClient->subscribedKeys.Size()==0) - { - // Didn't do anything - remoteSystems.Remove(packet->guid, _FILE_AND_LINE_); - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - } - } - - if (remoteServersWithData.Size()==0) - RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); -} -void CloudServer::OnUnsubscribeRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - - DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(packet->guid); - if (remoteSystemIndex.IsInvalid()==true) - return; - - RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); - - uint16_t keyCount, specificSystemCount; - DataStructures::List cloudKeys; - DataStructures::List specificSystems; - uint16_t index; - - CloudKey cloudKey; - bsIn.Read(keyCount); - for (index=0; index < keyCount; index++) - { - cloudKey.Serialize(false, &bsIn); - cloudKeys.Push(cloudKey, _FILE_AND_LINE_); - } - - RakNetGUID specificSystem; - bsIn.Read(specificSystemCount); - for (index=0; index < specificSystemCount; index++) - { - bsIn.Read(specificSystem); - specificSystems.Push(specificSystem, _FILE_AND_LINE_); - } - - for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) - { - if (queryFilters[filterIndex]->OnUnsubscribeRequest(packet->guid, packet->systemAddress, cloudKeys, specificSystems )==false) - return; - } - -// CloudDataList *cloudDataList; - bool dataRepositoryExists; -// unsigned int dataRepositoryIndex; - - for (index=0; index < keyCount; index++) - { - CloudKey cloudKey = cloudKeys[index]; - - // dataRepositoryIndex = - dataRepository.GetIndexFromKey(cloudKey, &dataRepositoryExists); - if (dataRepositoryExists==false) - continue; -// cloudDataList = dataRepository[dataRepositoryIndex]; - - unsigned int keySubscriberIndex; - bool hasKeySubscriber; - keySubscriberIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudKey, &hasKeySubscriber); - - if (hasKeySubscriber==false) - continue; - - UnsubscribeFromKey(remoteCloudClient, packet->guid, keySubscriberIndex, cloudKey, specificSystems); - } - - if (remoteCloudClient->IsUnused()) - { - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); - } -} -void CloudServer::OnServerToServerGetRequest(Packet *packet) -{ -// unsigned int remoteServerIndex; - bool objectExists; - //remoteServerIndex = - remoteServers.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists==false) - return; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - CloudQueryWithAddresses cloudQueryWithAddresses; - uint32_t requestId; - cloudQueryWithAddresses.Serialize(false, &bsIn); - bsIn.Read(requestId); - - DataStructures::List cloudDataResultList; - DataStructures::List cloudKeyResultList; - ProcessCloudQueryWithAddresses(cloudQueryWithAddresses, cloudDataResultList, cloudKeyResultList); - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_PROCESS_GET_RESPONSE); - bsOut.Write(requestId); - WriteCloudQueryRowFromResultList(cloudDataResultList, cloudKeyResultList, &bsOut); - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false); -} -void CloudServer::OnServerToServerGetResponse(Packet *packet) -{ - unsigned int remoteServerIndex; - bool objectExists; - remoteServerIndex = remoteServers.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists==false) - return; - - RemoteServer *remoteServer = remoteServers[remoteServerIndex]; - if (remoteServer==0) - return; - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - uint32_t requestId; - bsIn.Read(requestId); - - // Lookup request id - bool hasGetRequest; - unsigned int getRequestIndex; - getRequestIndex = getRequests.GetIndexFromKey(requestId, &hasGetRequest); - if (hasGetRequest==false) - return; - GetRequest *getRequest = getRequests[getRequestIndex]; - bool hasRemoteServer; - unsigned int remoteServerResponsesIndex; - remoteServerResponsesIndex = getRequest->remoteServerResponses.GetIndexFromKey(packet->guid, &hasRemoteServer); - if (hasRemoteServer==false) - return; - BufferedGetResponseFromServer *bufferedGetResponseFromServer; - bufferedGetResponseFromServer = getRequest->remoteServerResponses[remoteServerResponsesIndex]; - if (bufferedGetResponseFromServer->gotResult==true) - return; - bufferedGetResponseFromServer->gotResult=true; - uint32_t numRows; - bufferedGetResponseFromServer->queryResult.SerializeNumRows(false, numRows, &bsIn); - bufferedGetResponseFromServer->queryResult.SerializeCloudQueryRows(false, numRows, &bsIn, this); - - // If all results returned, then also process locally, and return to user - if (getRequest->AllRemoteServersHaveResponded()) - { - ProcessAndTransmitGetRequest(getRequest); - - getRequest->Clear(this); - RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); - - getRequests.RemoveAtIndex(getRequestIndex); - } -} -void CloudServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - - unsigned int remoteServerIndex; - bool objectExists; - remoteServerIndex = remoteServers.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists) - { - // Update remoteServerResponses by removing this server and sending the response if it is now complete - unsigned int getRequestIndex=0; - while (getRequestIndex < getRequests.Size()) - { - GetRequest *getRequest = getRequests[getRequestIndex]; - bool waitingForThisServer; - unsigned int remoteServerResponsesIndex = getRequest->remoteServerResponses.GetIndexFromKey(rakNetGUID, &waitingForThisServer); - if (waitingForThisServer) - { - getRequest->remoteServerResponses[remoteServerResponsesIndex]->Clear(this); - RakNet::OP_DELETE(getRequest->remoteServerResponses[remoteServerResponsesIndex], _FILE_AND_LINE_); - getRequest->remoteServerResponses.RemoveAtIndex(remoteServerResponsesIndex); - - if (getRequest->AllRemoteServersHaveResponded()) - { - ProcessAndTransmitGetRequest(getRequest); - getRequest->Clear(this); - RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); - - getRequests.RemoveAtIndex(getRequestIndex); - } - else - getRequestIndex++; - } - else - getRequestIndex++; - } - - RakNet::OP_DELETE(remoteServers[remoteServerIndex],_FILE_AND_LINE_); - remoteServers.RemoveAtIndex(remoteServerIndex); - } - - DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(rakNetGUID); - if (remoteSystemIndex.IsInvalid()==false) - { - RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); - unsigned int uploadedKeysIndex; - for (uploadedKeysIndex=0; uploadedKeysIndex < remoteCloudClient->uploadedKeys.Size(); uploadedKeysIndex++) - { - // Delete keys this system has uploaded - bool keyDataRepositoryExists; - unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(remoteCloudClient->uploadedKeys[uploadedKeysIndex], &keyDataRepositoryExists); - if (keyDataRepositoryExists) - { - CloudDataList* cloudDataList = dataRepository[dataRepositoryIndex]; - bool keyDataExists; - unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(rakNetGUID, &keyDataExists); - if (keyDataExists) - { - CloudData *cloudData = cloudDataList->keyData[keyDataIndex]; - cloudDataList->uploaderCount--; - - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, false ); - NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, false ); - NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, false ); - - cloudData->Clear(); - - if (cloudData->IsUnused()) - { - RakNet::OP_DELETE(cloudData,_FILE_AND_LINE_); - cloudDataList->keyData.RemoveAtIndex(keyDataIndex); - - if (cloudDataList->IsNotUploaded()) - { - // Tell other servers that this key is no longer uploaded, so they do not request it from us - RemoveUploadedKeyFromServers(cloudDataList->key); - } - - if (cloudDataList->IsUnused()) - { - // Tell other servers that this key is no longer uploaded, so they do not request it from us - RemoveUploadedKeyFromServers(cloudDataList->key); - - RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } - } - } - } - } - - unsigned int subscribedKeysIndex; - for (subscribedKeysIndex=0; subscribedKeysIndex < remoteCloudClient->subscribedKeys.Size(); subscribedKeysIndex++) - { - KeySubscriberID* keySubscriberId; - keySubscriberId = remoteCloudClient->subscribedKeys[subscribedKeysIndex]; - - bool keyDataRepositoryExists; - unsigned int keyDataRepositoryIndex = dataRepository.GetIndexFromKey(remoteCloudClient->subscribedKeys[subscribedKeysIndex]->key, &keyDataRepositoryExists); - if (keyDataRepositoryExists) - { - CloudDataList* cloudDataList = dataRepository[keyDataRepositoryIndex]; - if (keySubscriberId->specificSystemsSubscribedTo.Size()==0) - { - cloudDataList->nonSpecificSubscribers.Remove(rakNetGUID); - --cloudDataList->subscriberCount; - } - else - { - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < keySubscriberId->specificSystemsSubscribedTo.Size(); specificSystemIndex++) - { - bool keyDataExists; - unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(keySubscriberId->specificSystemsSubscribedTo[specificSystemIndex], &keyDataExists); - if (keyDataExists) - { - CloudData *keyData = cloudDataList->keyData[keyDataIndex]; - keyData->specificSubscribers.Remove(rakNetGUID); - --cloudDataList->subscriberCount; - } - } - } - } - - RakNet::OP_DELETE(keySubscriberId, _FILE_AND_LINE_); - } - - // Delete and remove from remoteSystems - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); - } -} -void CloudServer::OnRakPeerShutdown(void) -{ - Clear(); -} -void CloudServer::Clear(void) -{ - unsigned int i,j; - for (i=0; i < dataRepository.Size(); i++) - { - CloudDataList *cloudDataList = dataRepository[i]; - for (j=0; j < cloudDataList->keyData.Size(); j++) - { - cloudDataList->keyData[j]->Clear(); - RakNet::OP_DELETE(cloudDataList->keyData[j], _FILE_AND_LINE_); - } - RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); - } - dataRepository.Clear(false, _FILE_AND_LINE_); - - for (i=0; i < remoteServers.Size(); i++) - { - RakNet::OP_DELETE(remoteServers[i], _FILE_AND_LINE_); - } - remoteServers.Clear(false, _FILE_AND_LINE_); - - for (i=0; i < getRequests.Size(); i++) - { - GetRequest *getRequest = getRequests[i]; - getRequest->Clear(this); - RakNet::OP_DELETE(getRequests[i], _FILE_AND_LINE_); - } - getRequests.Clear(false, _FILE_AND_LINE_); - - DataStructures::List keyList; - DataStructures::List itemList; - remoteSystems.GetAsList(itemList, keyList, _FILE_AND_LINE_); - for (i=0; i < itemList.Size(); i++) - { - RemoteCloudClient* remoteCloudClient = itemList[i]; - for (j=0; j < remoteCloudClient->subscribedKeys.Size(); j++) - { - RakNet::OP_DELETE(remoteCloudClient->subscribedKeys[j], _FILE_AND_LINE_); - } - RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); - } - remoteSystems.Clear(_FILE_AND_LINE_); -} -void CloudServer::WriteCloudQueryRowFromResultList(DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut) -{ - bsOut->WriteCasted(cloudKeyResultList.Size()); - unsigned int i; - for (i=0; i < cloudKeyResultList.Size(); i++) - { - WriteCloudQueryRowFromResultList(i, cloudDataResultList, cloudKeyResultList, bsOut); - } -} -void CloudServer::WriteCloudQueryRowFromResultList(unsigned int i, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut) -{ - CloudQueryRow cloudQueryRow; - CloudData *cloudData = cloudDataResultList[i]; - cloudQueryRow.key=cloudKeyResultList[i]; - cloudQueryRow.data=cloudData->dataPtr; - cloudQueryRow.length=cloudData->dataLengthBytes; - cloudQueryRow.serverSystemAddress=cloudData->serverSystemAddress; - cloudQueryRow.clientSystemAddress=cloudData->clientSystemAddress; - cloudQueryRow.serverGUID=cloudData->serverGUID; - cloudQueryRow.clientGUID=cloudData->clientGUID; - cloudQueryRow.Serialize(true, bsOut, 0); -} -void CloudServer::NotifyClientSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, DataStructures::OrderedList &subscribers, bool wasUpdated ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID) ID_CLOUD_SUBSCRIPTION_NOTIFICATION); - bsOut.Write(wasUpdated); - CloudQueryRow row; - row.key=key; - row.data=cloudData->dataPtr; - row.length=cloudData->dataLengthBytes; - row.serverSystemAddress=cloudData->serverSystemAddress; - row.clientSystemAddress=cloudData->clientSystemAddress; - row.serverGUID=cloudData->serverGUID; - row.clientGUID=cloudData->clientGUID; - row.Serialize(true,&bsOut,0); - - unsigned int i; - for (i=0; i < subscribers.Size(); i++) - { - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, subscribers[i], false); - } -} -void CloudServer::NotifyClientSubscribersOfDataChange( CloudQueryRow *row, DataStructures::OrderedList &subscribers, bool wasUpdated ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID) ID_CLOUD_SUBSCRIPTION_NOTIFICATION); - bsOut.Write(wasUpdated); - row->Serialize(true,&bsOut,0); - - unsigned int i; - for (i=0; i < subscribers.Size(); i++) - { - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, subscribers[i], false); - } -} -void CloudServer::NotifyServerSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, bool wasUpdated ) -{ - // Find every server that has subscribed - // Send them change notifications - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_DATA_CHANGED); - bsOut.Write(wasUpdated); - CloudQueryRow row; - row.key=key; - row.data=cloudData->dataPtr; - row.length=cloudData->dataLengthBytes; - row.serverSystemAddress=cloudData->serverSystemAddress; - row.clientSystemAddress=cloudData->clientSystemAddress; - row.serverGUID=cloudData->serverGUID; - row.clientGUID=cloudData->clientGUID; - row.Serialize(true,&bsOut,0); - - unsigned int i; - for (i=0; i < remoteServers.Size(); i++) - { - if (remoteServers[i]->gotSubscribedAndUploadedKeys==false || remoteServers[i]->subscribedKeys.HasData(key)) - { - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); - } - } -} -void CloudServer::AddServer(RakNetGUID systemIdentifier) -{ - ConnectionState cs = rakPeerInterface->GetConnectionState(systemIdentifier); - if (cs==IS_DISCONNECTED || cs==IS_NOT_CONNECTED) - return; - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(systemIdentifier,&objectExists); - if (objectExists==false) - { - RemoteServer *remoteServer = RakNet::OP_NEW(_FILE_AND_LINE_); - remoteServer->gotSubscribedAndUploadedKeys=false; - remoteServer->serverAddress=systemIdentifier; - remoteServers.InsertAtIndex(remoteServer, index, _FILE_AND_LINE_); - - SendUploadedAndSubscribedKeysToServer(systemIdentifier); - } -} -void CloudServer::RemoveServer(RakNetGUID systemAddress) -{ - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(systemAddress,&objectExists); - if (objectExists==true) - { - RakNet::OP_DELETE(remoteServers[index],_FILE_AND_LINE_); - remoteServers.RemoveAtIndex(index); - } -} -void CloudServer::GetRemoteServers(DataStructures::List &remoteServersOut) -{ - remoteServersOut.Clear(true, _FILE_AND_LINE_); - - unsigned int i; - for (i=0; i < remoteServers.Size(); i++) - { - remoteServersOut.Push(remoteServers[i]->serverAddress, _FILE_AND_LINE_); - } -} -void CloudServer::ProcessAndTransmitGetRequest(GetRequest *getRequest) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID) ID_CLOUD_GET_RESPONSE); - - // BufferedGetResponseFromServer getResponse; - CloudQueryResult cloudQueryResult; - cloudQueryResult.cloudQuery=getRequest->cloudQueryWithAddresses.cloudQuery; - cloudQueryResult.subscribeToResults=getRequest->cloudQueryWithAddresses.cloudQuery.subscribeToResults; - cloudQueryResult.SerializeHeader(true, &bsOut); - - DataStructures::List cloudDataResultList; - DataStructures::List cloudKeyResultList; - ProcessCloudQueryWithAddresses(getRequest->cloudQueryWithAddresses, cloudDataResultList, cloudKeyResultList); - bool unlimitedRows=getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn==0; - - uint32_t localNumRows = (uint32_t) cloudDataResultList.Size(); - if (unlimitedRows==false && - localNumRows > getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex && - localNumRows - getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex > getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn ) - localNumRows=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex + getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn; - - BitSize_t bitStreamOffset = bsOut.GetWriteOffset(); - uint32_t localRowsToWrite; - unsigned int skipRows; - if (localNumRows>getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex) - { - localRowsToWrite=localNumRows-getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex; - skipRows=0; - } - else - { - localRowsToWrite=0; - skipRows=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex-localNumRows; - } - cloudQueryResult.SerializeNumRows(true, localRowsToWrite, &bsOut); - for (unsigned int i=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex; i < localNumRows; i++) - { - WriteCloudQueryRowFromResultList(i, cloudDataResultList, cloudKeyResultList, &bsOut); - } - - // Append remote systems for remaining rows - if (unlimitedRows==true || getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn>localRowsToWrite) - { - uint32_t remainingRows=0; - uint32_t additionalRowsWritten=0; - if (unlimitedRows==false) - remainingRows=getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn-localRowsToWrite; - - unsigned int remoteServerResponseIndex; - for (remoteServerResponseIndex=0; remoteServerResponseIndex < getRequest->remoteServerResponses.Size(); remoteServerResponseIndex++) - { - BufferedGetResponseFromServer *bufferedGetResponseFromServer = getRequest->remoteServerResponses[remoteServerResponseIndex]; - unsigned int cloudQueryRowIndex; - for (cloudQueryRowIndex=0; cloudQueryRowIndex < bufferedGetResponseFromServer->queryResult.rowsReturned.Size(); cloudQueryRowIndex++) - { - if (skipRows>0) - { - --skipRows; - continue; - } - bufferedGetResponseFromServer->queryResult.rowsReturned[cloudQueryRowIndex]->Serialize(true, &bsOut, this); - - ++additionalRowsWritten; - if (unlimitedRows==false && --remainingRows==0) - break; - } - - if (unlimitedRows==false && remainingRows==0) - break; - } - - if (additionalRowsWritten>0) - { - BitSize_t curOffset = bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(bitStreamOffset); - localRowsToWrite+=additionalRowsWritten; - cloudQueryResult.SerializeNumRows(true, localRowsToWrite, &bsOut); - bsOut.SetWriteOffset(curOffset); - } - } - - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, getRequest->requestingClient, false); -} -void CloudServer::ProcessCloudQueryWithAddresses( CloudServer::CloudQueryWithAddresses &cloudQueryWithAddresses, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList ) -{ - CloudQueryResult cloudQueryResult; - CloudQueryRow cloudQueryRow; - unsigned int queryIndex; - bool dataRepositoryExists; - CloudDataList* cloudDataList; - unsigned int keyDataIndex; - - // If specificSystems list empty, applies to all systems - // For each of keys in cloudQueryWithAddresses, return that data, limited by maxRowsToReturn - for (queryIndex=0; queryIndex < cloudQueryWithAddresses.cloudQuery.keys.Size(); queryIndex++) - { - const CloudKey &key = cloudQueryWithAddresses.cloudQuery.keys[queryIndex]; - - unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(key, &dataRepositoryExists); - if (dataRepositoryExists) - { - cloudDataList=dataRepository[dataRepositoryIndex]; - - if (cloudDataList->uploaderCount>0) - { - // Return all keyData that was uploaded by specificSystems, or all if not specified - if (cloudQueryWithAddresses.specificSystems.Size()>0) - { - // Return data for matching systems - unsigned int specificSystemIndex; - for (specificSystemIndex=0; specificSystemIndex < cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) - { - bool uploaderExists; - keyDataIndex = cloudDataList->keyData.GetIndexFromKey(cloudQueryWithAddresses.specificSystems[specificSystemIndex], &uploaderExists); - if (uploaderExists) - { - cloudDataResultList.Push(cloudDataList->keyData[keyDataIndex], _FILE_AND_LINE_); - cloudKeyResultList.Push(key, _FILE_AND_LINE_); - } - } - } - else - { - // Return data for all systems - for (keyDataIndex=0; keyDataIndex < cloudDataList->keyData.Size(); keyDataIndex++) - { - cloudDataResultList.Push(cloudDataList->keyData[keyDataIndex], _FILE_AND_LINE_); - cloudKeyResultList.Push(key, _FILE_AND_LINE_); - } - } - } - } - } -} -void CloudServer::SendUploadedAndSubscribedKeysToServer( RakNetGUID systemAddress ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS); - bsOut.WriteCasted(dataRepository.Size()); - for (unsigned int i=0; i < dataRepository.Size(); i++) - dataRepository[i]->key.Serialize(true, &bsOut); - - BitSize_t startOffset, endOffset; - uint16_t subscribedKeyCount=0; - startOffset=bsOut.GetWriteOffset(); - bsOut.WriteCasted(subscribedKeyCount); - for (unsigned int i=0; i < dataRepository.Size(); i++) - { - if (dataRepository[i]->subscriberCount>0) - { - dataRepository[i]->key.Serialize(true, &bsOut); - subscribedKeyCount++; - } - } - endOffset=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(startOffset); - bsOut.WriteCasted(subscribedKeyCount); - bsOut.SetWriteOffset(endOffset); - - if (dataRepository.Size()>0 || subscribedKeyCount>0) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false); -} -void CloudServer::SendUploadedKeyToServers( CloudKey &cloudKey ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_ADD_UPLOADED_KEY); - cloudKey.Serialize(true, &bsOut); - for (unsigned int i=0; i < remoteServers.Size(); i++) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); -} -void CloudServer::SendSubscribedKeyToServers( CloudKey &cloudKey ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_ADD_SUBSCRIBED_KEY); - cloudKey.Serialize(true, &bsOut); - for (unsigned int i=0; i < remoteServers.Size(); i++) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); -} -void CloudServer::RemoveUploadedKeyFromServers( CloudKey &cloudKey ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_REMOVE_UPLOADED_KEY); - cloudKey.Serialize(true, &bsOut); - for (unsigned int i=0; i < remoteServers.Size(); i++) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); -} -void CloudServer::RemoveSubscribedKeyFromServers( CloudKey &cloudKey ) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); - bsOut.Write((MessageID)STSC_REMOVE_SUBSCRIBED_KEY); - cloudKey.Serialize(true, &bsOut); - for (unsigned int i=0; i < remoteServers.Size(); i++) - SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); -} -void CloudServer::OnSendUploadedAndSubscribedKeysToServer( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - remoteServer->gotSubscribedAndUploadedKeys=true; - -// unsigned int insertionIndex; - bool alreadyHasKey; - uint16_t numUploadedKeys, numSubscribedKeys; - bsIn.Read(numUploadedKeys); - for (uint16_t i=0; i < numUploadedKeys; i++) - { - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); - - // insertionIndex = - remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==false) - remoteServer->uploadedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); - } - - bsIn.Read(numSubscribedKeys); - for (uint16_t i=0; i < numSubscribedKeys; i++) - { - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); - - //insertionIndex = - remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==false) - remoteServer->subscribedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); - } - - // Potential todo - join servers - // For each uploaded key that we subscribe to, query it - // For each subscribed key that we have, send it -} -void CloudServer::OnSendUploadedKeyToServers( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); -// unsigned int insertionIndex; - bool alreadyHasKey; -// insertionIndex = - remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==false) - remoteServer->uploadedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); -} -void CloudServer::OnSendSubscribedKeyToServers( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); -// unsigned int insertionIndex; - bool alreadyHasKey; -// insertionIndex = - remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - - // Do not need to send current values, the Get request will do that as the Get request is sent at the same time - if (alreadyHasKey==false) - remoteServer->subscribedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); -} -void CloudServer::OnRemoveUploadedKeyFromServers( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); - unsigned int insertionIndex; - bool alreadyHasKey; - insertionIndex = remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==true) - remoteServer->uploadedKeys.RemoveAtIndex(insertionIndex); -} -void CloudServer::OnRemoveSubscribedKeyFromServers( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - RemoteServer *remoteServer = remoteServers[index]; - CloudKey cloudKey; - cloudKey.Serialize(false, &bsIn); - unsigned int insertionIndex; - bool alreadyHasKey; - insertionIndex = remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); - if (alreadyHasKey==true) - remoteServer->subscribedKeys.RemoveAtIndex(insertionIndex); -} -void CloudServer::OnServerDataChanged( Packet *packet ) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - bool objectExists; - remoteServers.GetIndexFromKey(packet->guid,&objectExists); - if (objectExists==false) - return; - - // Find everyone that cares about this change and relay - bool wasUpdated=false; - bsIn.Read(wasUpdated); - CloudQueryRow row; - row.Serialize(false, &bsIn, this); - - CloudDataList *cloudDataList; - bool dataRepositoryExists; - unsigned int dataRepositoryIndex; - dataRepositoryIndex = dataRepository.GetIndexFromKey(row.key, &dataRepositoryExists); - if (dataRepositoryExists==false) - { - DeallocateRowData(row.data); - return; - } - cloudDataList = dataRepository[dataRepositoryIndex]; - CloudData *cloudData; - bool keyDataListExists; - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(row.clientGUID, &keyDataListExists); - if (keyDataListExists==true) - { - cloudData = cloudDataList->keyData[keyDataListIndex]; - NotifyClientSubscribersOfDataChange(&row, cloudData->specificSubscribers, wasUpdated ); - } - - NotifyClientSubscribersOfDataChange(&row, cloudDataList->nonSpecificSubscribers, wasUpdated ); - DeallocateRowData(row.data); -} -void CloudServer::GetServersWithUploadedKeys( - DataStructures::List &keys, - DataStructures::List &remoteServersWithData - ) -{ - remoteServersWithData.Clear(true, _FILE_AND_LINE_); - - unsigned int i,j; - for (i=0; i < remoteServers.Size(); i++) - { - remoteServers[i]->workingFlag=false; - } - - for (i=0; i < remoteServers.Size(); i++) - { - if (remoteServers[i]->workingFlag==false) - { - if (remoteServers[i]->gotSubscribedAndUploadedKeys==false) - { - remoteServers[i]->workingFlag=true; - remoteServersWithData.Push(remoteServers[i], _FILE_AND_LINE_); - } - else - { - remoteServers[i]->workingFlag=false; - for (j=0; j < keys.Size(); j++) - { - if (remoteServers[i]->workingFlag==false && remoteServers[i]->uploadedKeys.HasData(keys[j])) - { - remoteServers[i]->workingFlag=true; - remoteServersWithData.Push(remoteServers[i], _FILE_AND_LINE_); - break; - } - } - } - } - } -} - -CloudServer::CloudDataList *CloudServer::GetOrAllocateCloudDataList(CloudKey key, bool *dataRepositoryExists, unsigned int &dataRepositoryIndex) -{ - CloudDataList *cloudDataList; - - dataRepositoryIndex = dataRepository.GetIndexFromKey(key, dataRepositoryExists); - if (*dataRepositoryExists==false) - { - cloudDataList = RakNet::OP_NEW(_FILE_AND_LINE_); - cloudDataList->key=key; - cloudDataList->uploaderCount=0; - cloudDataList->subscriberCount=0; - dataRepository.InsertAtIndex(cloudDataList,dataRepositoryIndex,_FILE_AND_LINE_); - } - else - { - cloudDataList = dataRepository[dataRepositoryIndex]; - } - - return cloudDataList; -} - -void CloudServer::UnsubscribeFromKey(RemoteCloudClient *remoteCloudClient, RakNetGUID remoteCloudClientGuid, unsigned int keySubscriberIndex, CloudKey &cloudKey, DataStructures::List &specificSystems) -{ - KeySubscriberID* keySubscriberId = remoteCloudClient->subscribedKeys[keySubscriberIndex]; - - // If removing specific systems, but global subscription, fail - if (keySubscriberId->specificSystemsSubscribedTo.Size()==0 && specificSystems.Size()>0) - return; - - bool dataRepositoryExists; - CloudDataList *cloudDataList; - unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(cloudKey, &dataRepositoryExists); - if (dataRepositoryExists==false) - return; - - unsigned int i,j; - - cloudDataList = dataRepository[dataRepositoryIndex]; - if (specificSystems.Size()==0) - { - // Remove global subscriber. If returns false, have to remove specific subscribers - if (cloudDataList->RemoveSubscriber(remoteCloudClientGuid)==false) - { - for (i=0; i < keySubscriberId->specificSystemsSubscribedTo.Size(); i++) - { - RemoveSpecificSubscriber(keySubscriberId->specificSystemsSubscribedTo[i], cloudDataList, remoteCloudClientGuid); - } - } - keySubscriberId->specificSystemsSubscribedTo.Clear(true, _FILE_AND_LINE_); - } - else - { - for (j=0; j < specificSystems.Size(); j++) - { - unsigned int specificSystemsSubscribedToIndex; - bool hasSpecificSystemsSubscribedTo; - specificSystemsSubscribedToIndex=keySubscriberId->specificSystemsSubscribedTo.GetIndexFromKey(specificSystems[j], &hasSpecificSystemsSubscribedTo); - if (hasSpecificSystemsSubscribedTo) - { - RemoveSpecificSubscriber(specificSystems[j], cloudDataList, remoteCloudClientGuid); - keySubscriberId->specificSystemsSubscribedTo.RemoveAtIndex(specificSystemsSubscribedToIndex); - } - } - } - - if (keySubscriberId->specificSystemsSubscribedTo.Size()==0) - { - RakNet::OP_DELETE(keySubscriberId, _FILE_AND_LINE_); - remoteCloudClient->subscribedKeys.RemoveAtIndex(keySubscriberIndex); - } - - if (cloudDataList->subscriberCount==0) - RemoveSubscribedKeyFromServers(cloudKey); - - if (cloudDataList->IsUnused()) - { - RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); - dataRepository.RemoveAtIndex(dataRepositoryIndex); - } -} -void CloudServer::RemoveSpecificSubscriber(RakNetGUID specificSubscriber, CloudDataList *cloudDataList, RakNetGUID remoteCloudClientGuid) -{ - bool keyDataListExists; - unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(specificSubscriber, &keyDataListExists); - if (keyDataListExists==false) - return; - CloudData *cloudData = cloudDataList->keyData[keyDataListIndex]; - bool hasSpecificSubscriber; - unsigned int specificSubscriberIndex = cloudData->specificSubscribers.GetIndexFromKey(remoteCloudClientGuid, &hasSpecificSubscriber); - if (hasSpecificSubscriber) - { - cloudData->specificSubscribers.RemoveAtIndex(specificSubscriberIndex); - cloudDataList->subscriberCount--; - - if (cloudData->IsUnused()) - { - RakNet::OP_DELETE(cloudData, _FILE_AND_LINE_); - cloudDataList->keyData.RemoveAtIndex(keyDataListIndex); - } - } -} - -void CloudServer::ForceExternalSystemAddress(SystemAddress forcedAddress) -{ - forceAddress=forcedAddress; -} -void CloudServer::AddQueryFilter(CloudServerQueryFilter* filter) -{ - if (queryFilters.GetIndexOf(filter)!=(unsigned int) -1) - return; - queryFilters.Push(filter, _FILE_AND_LINE_); -} -void CloudServer::RemoveQueryFilter(CloudServerQueryFilter* filter) -{ - unsigned int index; - index = queryFilters.GetIndexOf(filter); - if (index != (unsigned int) -1) - queryFilters.RemoveAtIndex(index); -} -void CloudServer::RemoveAllQueryFilters(void) -{ - queryFilters.Clear(true, _FILE_AND_LINE_); -} - -#endif +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudServer==1 + +#include "CloudServer.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" +#include "RakPeerInterface.h" + +enum ServerToServerCommands +{ + STSC_PROCESS_GET_REQUEST, + STSC_PROCESS_GET_RESPONSE, + STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS, + STSC_ADD_UPLOADED_KEY, + STSC_ADD_SUBSCRIBED_KEY, + STSC_REMOVE_UPLOADED_KEY, + STSC_REMOVE_SUBSCRIBED_KEY, + STSC_DATA_CHANGED, +}; + +using namespace RakNet; + +int CloudServer::RemoteServerComp(const RakNetGUID &key, RemoteServer* const &data ) +{ + if (key < data->serverAddress) + return -1; + if (key > data->serverAddress) + return 1; + return 0; +} +int CloudServer::KeySubscriberIDComp(const CloudKey &key, KeySubscriberID * const &data ) +{ + if (key.primaryKey < data->key.primaryKey) + return -1; + if (key.primaryKey > data->key.primaryKey) + return 1; + if (key.secondaryKey < data->key.secondaryKey) + return -1; + if (key.secondaryKey > data->key.secondaryKey) + return 1; + return 0; +} +int CloudServer::KeyDataPtrComp( const RakNetGUID &key, CloudData* const &data ) +{ + if (key < data->clientGUID) + return -1; + if (key > data->clientGUID) + return 1; + return 0; +} +int CloudServer::KeyDataListComp( const CloudKey &key, CloudDataList * const &data ) +{ + if (key.primaryKey < data->key.primaryKey) + return -1; + if (key.primaryKey > data->key.primaryKey) + return 1; + if (key.secondaryKey < data->key.secondaryKey) + return -1; + if (key.secondaryKey > data->key.secondaryKey) + return 1; + return 0; +} +int CloudServer::BufferedGetResponseFromServerComp(const RakNetGUID &key, CloudServer::BufferedGetResponseFromServer* const &data ) +{ + if (key < data->serverAddress) + return -1; + if (key > data->serverAddress) + return 1; + return 0; +} +int CloudServer::GetRequestComp(const uint32_t &key, CloudServer::GetRequest* const &data ) +{ + if (key < data->requestId) + return -1; + if (key > data->requestId) + return -1; + return 0; +} +void CloudServer::CloudQueryWithAddresses::Serialize(bool writeToBitstream, BitStream *bitStream) +{ + cloudQuery.Serialize(writeToBitstream, bitStream); + + if (writeToBitstream) + { + bitStream->WriteCasted(specificSystems.Size()); + RakAssert(specificSystems.Size() < (uint16_t)-1 ); + for (uint16_t i=0; i < specificSystems.Size(); i++) + { + bitStream->Write(specificSystems[i]); + } + } + else + { + uint16_t specificSystemsCount; + RakNetGUID addressOrGuid; + bitStream->Read(specificSystemsCount); + for (uint16_t i=0; i < specificSystemsCount; i++) + { + bitStream->Read(addressOrGuid); + specificSystems.Push(addressOrGuid, _FILE_AND_LINE_); + } + } +} +bool CloudServer::GetRequest::AllRemoteServersHaveResponded(void) const +{ + unsigned int i; + for (i=0; i < remoteServerResponses.Size(); i++) + if (remoteServerResponses[i]->gotResult==false) + return false; + return true; +} +void CloudServer::GetRequest::Clear(CloudAllocator *allocator) +{ + unsigned int i; + for (i=0; i < remoteServerResponses.Size(); i++) + { + remoteServerResponses[i]->Clear(allocator); + RakNet::OP_DELETE(remoteServerResponses[i], _FILE_AND_LINE_); + } + remoteServerResponses.Clear(false, _FILE_AND_LINE_); +} +void CloudServer::BufferedGetResponseFromServer::Clear(CloudAllocator *allocator) +{ + unsigned int i; + for (i=0; i < queryResult.rowsReturned.Size(); i++) + { + allocator->DeallocateRowData(queryResult.rowsReturned[i]->data); + allocator->DeallocateCloudQueryRow(queryResult.rowsReturned[i]); + } + queryResult.rowsReturned.Clear(false, _FILE_AND_LINE_); +} +CloudServer::CloudServer() +{ + maxUploadBytesPerClient=0; + maxBytesPerDowload=0; + nextGetRequestId=0; + nextGetRequestsCheck=0; +} +CloudServer::~CloudServer() +{ + Clear(); +} +void CloudServer::SetMaxUploadBytesPerClient(uint64_t bytes) +{ + maxUploadBytesPerClient=bytes; +} +void CloudServer::SetMaxBytesPerDownload(uint64_t bytes) +{ + maxBytesPerDowload=bytes; +} +void CloudServer::Update(void) +{ + // Timeout getRequests + RakNet::Time time = RakNet::Time(); + if (time > nextGetRequestsCheck) + { + nextGetRequestsCheck=time+1000; + + unsigned int i=0; + while (i < getRequests.Size()) + { + if (time - getRequests[i]->requestStartTime > 3000) + { + // Remote server is not responding, just send back data with whoever did respond + ProcessAndTransmitGetRequest(getRequests[i]); + getRequests[i]->Clear(this); + RakNet::OP_DELETE(getRequests[i],_FILE_AND_LINE_); + getRequests.RemoveAtIndex(i); + } + else + { + i++; + } + } + } +} +PluginReceiveResult CloudServer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_CLOUD_POST_REQUEST: + OnPostRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_CLOUD_RELEASE_REQUEST: + OnReleaseRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_CLOUD_GET_REQUEST: + OnGetRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_CLOUD_UNSUBSCRIBE_REQUEST: + OnUnsubscribeRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_CLOUD_SERVER_TO_SERVER_COMMAND: + if (packet->length>1) + { + switch (packet->data[1]) + { + case STSC_PROCESS_GET_REQUEST: + OnServerToServerGetRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_PROCESS_GET_RESPONSE: + OnServerToServerGetResponse(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS: + OnSendUploadedAndSubscribedKeysToServer(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_ADD_UPLOADED_KEY: + OnSendUploadedKeyToServers(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_ADD_SUBSCRIBED_KEY: + OnSendSubscribedKeyToServers(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_REMOVE_UPLOADED_KEY: + OnRemoveUploadedKeyFromServers(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_REMOVE_SUBSCRIBED_KEY: + OnRemoveSubscribedKeyFromServers(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case STSC_DATA_CHANGED: + OnServerDataChanged(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + return RR_CONTINUE_PROCESSING; +} +void CloudServer::OnPostRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + CloudKey key; + key.Serialize(false,&bsIn); + uint32_t dataLengthBytes; + bsIn.Read(dataLengthBytes); + if (maxUploadBytesPerClient>0 && dataLengthBytes>maxUploadBytesPerClient) + return; // Exceeded max upload bytes + + bsIn.AlignReadToByteBoundary(); + for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) + { + if (queryFilters[filterIndex]->OnPostRequest(packet->guid, packet->systemAddress, key, dataLengthBytes, (const char*) bsIn.GetData()+BITS_TO_BYTES(bsIn.GetReadOffset()))==false) + return; + } + + unsigned char *data; + if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) + { + data = (unsigned char *) rakMalloc_Ex(dataLengthBytes,_FILE_AND_LINE_); + if (data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return; + } + bsIn.ReadAlignedBytes(data,dataLengthBytes); + } + else + data=0; + + // Add this system to remoteSystems if they aren't there already + DataStructures::HashIndex remoteSystemsHashIndex = remoteSystems.GetIndexOf(packet->guid); + RemoteCloudClient *remoteCloudClient; + if (remoteSystemsHashIndex.IsInvalid()) + { + remoteCloudClient = RakNet::OP_NEW(_FILE_AND_LINE_); + remoteCloudClient->uploadedKeys.Insert(key,key,true,_FILE_AND_LINE_); + remoteCloudClient->uploadedBytes=0; + remoteSystems.Push(packet->guid, remoteCloudClient, _FILE_AND_LINE_); + } + else + { + remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemsHashIndex); + bool objectExists; + // Add to RemoteCloudClient::uploadedKeys if it isn't there already + unsigned int uploadedKeysIndex = remoteCloudClient->uploadedKeys.GetIndexFromKey(key,&objectExists); + if (objectExists==false) + { + remoteCloudClient->uploadedKeys.InsertAtIndex(key, uploadedKeysIndex, _FILE_AND_LINE_); + } + } + + bool cloudDataAlreadyUploaded; + unsigned int dataRepositoryIndex; + bool dataRepositoryExists; + CloudDataList* cloudDataList = GetOrAllocateCloudDataList(key, &dataRepositoryExists, dataRepositoryIndex); + if (dataRepositoryExists==false) + { + cloudDataList->uploaderCount=1; + cloudDataAlreadyUploaded=false; + } + else + { + cloudDataAlreadyUploaded=cloudDataList->uploaderCount>0; + cloudDataList->uploaderCount++; + } + + CloudData *cloudData; + bool keyDataListExists; + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(packet->guid, &keyDataListExists); + if (keyDataListExists==false) + { + if (maxUploadBytesPerClient>0 && remoteCloudClient->uploadedBytes+dataLengthBytes>maxUploadBytesPerClient) + { + // Undo prior insertion of cloudDataList into cloudData if needed + if (keyDataListExists==false) + { + RakNet::OP_DELETE(cloudDataList,_FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } + + if (remoteCloudClient->IsUnused()) + { + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + remoteSystems.Remove(packet->guid, _FILE_AND_LINE_); + } + + if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) + rakFree_Ex(data, _FILE_AND_LINE_); + + return; + } + + cloudData = RakNet::OP_NEW(_FILE_AND_LINE_); + cloudData->dataLengthBytes=dataLengthBytes; + cloudData->isUploaded=true; + if (forceAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + cloudData->serverSystemAddress=forceAddress; + cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetExternalID(packet->systemAddress).GetPort()); + } + else + { + cloudData->serverSystemAddress=rakPeerInterface->GetExternalID(packet->systemAddress); + } + if (cloudData->serverSystemAddress.GetPort()==0) + { + // Fix localhost port + cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetSocket(UNASSIGNED_SYSTEM_ADDRESS)->boundAddress.GetPort()); + } + cloudData->clientSystemAddress=packet->systemAddress; + cloudData->serverGUID=rakPeerInterface->GetMyGUID(); + cloudData->clientGUID=packet->guid; + cloudDataList->keyData.Insert(packet->guid,cloudData,true,_FILE_AND_LINE_); + } + else + { + cloudData = cloudDataList->keyData[keyDataListIndex]; + + if (cloudDataAlreadyUploaded==false) + { + if (forceAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + cloudData->serverSystemAddress=forceAddress; + cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetExternalID(packet->systemAddress).GetPort()); + } + else + { + cloudData->serverSystemAddress=rakPeerInterface->GetExternalID(packet->systemAddress); + } + if (cloudData->serverSystemAddress.GetPort()==0) + { + // Fix localhost port + cloudData->serverSystemAddress.SetPort(rakPeerInterface->GetSocket(UNASSIGNED_SYSTEM_ADDRESS)->boundAddress.GetPort()); + } + + cloudData->clientSystemAddress=packet->systemAddress; + } + + if (maxUploadBytesPerClient>0 && remoteCloudClient->uploadedBytes-cloudData->dataLengthBytes+dataLengthBytes>maxUploadBytesPerClient) + { + // Undo prior insertion of cloudDataList into cloudData if needed + if (dataRepositoryExists==false) + { + RakNet::OP_DELETE(cloudDataList,_FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } + return; + } + else + { + // Subtract already used bytes we are overwriting + remoteCloudClient->uploadedBytes-=cloudData->dataLengthBytes; + } + + if (cloudData->allocatedData!=0) + rakFree_Ex(cloudData->allocatedData,_FILE_AND_LINE_); + } + + if (dataLengthBytes>CLOUD_SERVER_DATA_STACK_SIZE) + { + // Data already allocated + cloudData->allocatedData=data; + cloudData->dataPtr=data; + } + else + { + // Read to stack + if (dataLengthBytes>0) + bsIn.ReadAlignedBytes(cloudData->stackData,dataLengthBytes); + cloudData->allocatedData=0; + cloudData->dataPtr=cloudData->stackData; + } + // Update how many bytes were written for this data + cloudData->dataLengthBytes=dataLengthBytes; + remoteCloudClient->uploadedBytes+=dataLengthBytes; + + if (cloudDataAlreadyUploaded==false) + { + // New data field + SendUploadedKeyToServers(cloudDataList->key); + } + + // Existing data field changed + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, true ); + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, true ); + + // Send update to all remote servers that subscribed to this key + NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, true); + + // I could have also subscribed to a key not yet updated locally + // This means I have to go through every RemoteClient that wants this key + // Seems like cloudData->specificSubscribers is unnecessary in that case +} +void CloudServer::OnReleaseRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + + uint16_t keyCount; + bsIn.Read(keyCount); + + if (keyCount==0) + return; + + DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(packet->guid); + if (remoteSystemIndex.IsInvalid()==true) + return; + + RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); + + CloudKey key; + + // Read all in a list first so I can run filter on it + DataStructures::List cloudKeys; + for (uint16_t keyCountIndex=0; keyCountIndex < keyCount; keyCountIndex++) + { + key.Serialize(false, &bsIn); + cloudKeys.Push(key, _FILE_AND_LINE_); + } + + for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) + { + if (queryFilters[filterIndex]->OnReleaseRequest(packet->guid, packet->systemAddress, cloudKeys)) + return; + } + + for (uint16_t keyCountIndex=0; keyCountIndex < keyCount; keyCountIndex++) + { + // Serialize in list above so I can run the filter on it + // key.Serialize(false, &bsIn); + key=cloudKeys[keyCountIndex]; + + // Remove remote systems uploaded keys + bool objectExists; + unsigned int uploadedKeysIndex = remoteCloudClient->uploadedKeys.GetIndexFromKey(key,&objectExists); + if (objectExists) + { + bool dataRepositoryExists; + unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(key, &dataRepositoryExists); + CloudDataList* cloudDataList = dataRepository[dataRepositoryIndex]; + RakAssert(cloudDataList); + + CloudData *cloudData; + bool keyDataListExists; + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(packet->guid, &keyDataListExists); + cloudData = cloudDataList->keyData[keyDataListIndex]; + + remoteCloudClient->uploadedKeys.RemoveAtIndex(uploadedKeysIndex); + remoteCloudClient->uploadedBytes-=cloudData->dataLengthBytes; + cloudDataList->uploaderCount--; + + // Broadcast destruction of this key to subscribers + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, false ); + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, false ); + NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, false ); + + cloudData->Clear(); + + if (cloudData->IsUnused()) + { + RakNet::OP_DELETE(cloudData, _FILE_AND_LINE_); + cloudDataList->keyData.RemoveAtIndex(keyDataListIndex); + if (cloudDataList->IsNotUploaded()) + { + // Tell other servers that this key is no longer uploaded, so they do not request it from us + RemoveUploadedKeyFromServers(cloudDataList->key); + } + + if (cloudDataList->IsUnused()) + { + RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } + } + + if (remoteCloudClient->IsUnused()) + { + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); + break; + } + } + } +} +void CloudServer::OnGetRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + uint16_t specificSystemsCount; + CloudKey cloudKey; + + // Create a new GetRequest + GetRequest *getRequest; + getRequest = RakNet::OP_NEW(_FILE_AND_LINE_); + getRequest->cloudQueryWithAddresses.cloudQuery.Serialize(false, &bsIn); + getRequest->requestingClient=packet->guid; + + RakNetGUID addressOrGuid; + bsIn.Read(specificSystemsCount); + for (uint16_t i=0; i < specificSystemsCount; i++) + { + bsIn.Read(addressOrGuid); + getRequest->cloudQueryWithAddresses.specificSystems.Push(addressOrGuid, _FILE_AND_LINE_); + } + + if (getRequest->cloudQueryWithAddresses.cloudQuery.keys.Size()==0) + { + RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); + return; + } + + for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) + { + if (queryFilters[filterIndex]->OnGetRequest(packet->guid, packet->systemAddress, getRequest->cloudQueryWithAddresses.cloudQuery, getRequest->cloudQueryWithAddresses.specificSystems )==false) + return; + } + + getRequest->requestStartTime=RakNet::GetTime(); + getRequest->requestId=nextGetRequestId++; + + // Send request to servers that have this data + DataStructures::List remoteServersWithData; + GetServersWithUploadedKeys(getRequest->cloudQueryWithAddresses.cloudQuery.keys, remoteServersWithData); + + if (remoteServersWithData.Size()==0) + { + ProcessAndTransmitGetRequest(getRequest); + } + else + { + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_PROCESS_GET_REQUEST); + getRequest->cloudQueryWithAddresses.Serialize(true, &bsOut); + bsOut.Write(getRequest->requestId); + + for (unsigned int remoteServerIndex=0; remoteServerIndex < remoteServersWithData.Size(); remoteServerIndex++) + { + BufferedGetResponseFromServer* bufferedGetResponseFromServer = RakNet::OP_NEW(_FILE_AND_LINE_); + bufferedGetResponseFromServer->serverAddress=remoteServersWithData[remoteServerIndex]->serverAddress; + bufferedGetResponseFromServer->gotResult=false; + getRequest->remoteServerResponses.Insert(remoteServersWithData[remoteServerIndex]->serverAddress, bufferedGetResponseFromServer, true, _FILE_AND_LINE_); + + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServersWithData[remoteServerIndex]->serverAddress, false); + } + + // Record that this system made this request + getRequests.Insert(getRequest->requestId, getRequest, true, _FILE_AND_LINE_); + } + + if (getRequest->cloudQueryWithAddresses.cloudQuery.subscribeToResults) + { + // Add to key subscription list for the client, which contains a keyId / specificUploaderList pair + DataStructures::HashIndex remoteSystemsHashIndex = remoteSystems.GetIndexOf(packet->guid); + RemoteCloudClient *remoteCloudClient; + if (remoteSystemsHashIndex.IsInvalid()) + { + remoteCloudClient = RakNet::OP_NEW(_FILE_AND_LINE_); + remoteCloudClient->uploadedBytes=0; + remoteSystems.Push(packet->guid, remoteCloudClient, _FILE_AND_LINE_); + } + else + { + remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemsHashIndex); + } + + unsigned int keyIndex; + for (keyIndex=0; keyIndex < getRequest->cloudQueryWithAddresses.cloudQuery.keys.Size(); keyIndex++) + { + cloudKey = getRequest->cloudQueryWithAddresses.cloudQuery.keys[keyIndex]; + + unsigned int keySubscriberIndex; + bool hasKeySubscriber; + keySubscriberIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudKey, &hasKeySubscriber); + KeySubscriberID* keySubscriberId; + if (hasKeySubscriber) + { + DataStructures::List specificSystems; + UnsubscribeFromKey(remoteCloudClient, packet->guid, keySubscriberIndex, cloudKey, specificSystems); + } + + keySubscriberId = RakNet::OP_NEW(_FILE_AND_LINE_); + keySubscriberId->key=cloudKey; + + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < getRequest->cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) + { + keySubscriberId->specificSystemsSubscribedTo.Insert(getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex], getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex], true, _FILE_AND_LINE_); + } + + remoteCloudClient->subscribedKeys.InsertAtIndex(keySubscriberId, keySubscriberIndex, _FILE_AND_LINE_); + + // Add CloudData in a similar way + unsigned int dataRepositoryIndex; + bool dataRepositoryExists; + CloudDataList* cloudDataList = GetOrAllocateCloudDataList(cloudKey, &dataRepositoryExists, dataRepositoryIndex); + + // If this is the first local client to subscribe to this key, call SendSubscribedKeyToServers + if (cloudDataList->subscriberCount==0) + SendSubscribedKeyToServers(cloudKey); + + // If the subscription is specific, may have to also allocate CloudData + if (getRequest->cloudQueryWithAddresses.specificSystems.Size()) + { + CloudData *cloudData; + bool keyDataListExists; + + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < getRequest->cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) + { + RakNetGUID specificSystem = getRequest->cloudQueryWithAddresses.specificSystems[specificSystemIndex]; + + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(specificSystem, &keyDataListExists); + if (keyDataListExists==false) + { + cloudData = RakNet::OP_NEW(_FILE_AND_LINE_); + cloudData->dataLengthBytes=0; + cloudData->allocatedData=0; + cloudData->isUploaded=false; + cloudData->dataPtr=0; + cloudData->serverSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; + cloudData->clientSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; + cloudData->serverGUID=rakPeerInterface->GetMyGUID(); + cloudData->clientGUID=specificSystem; + cloudDataList->keyData.Insert(specificSystem,cloudData,true,_FILE_AND_LINE_); + } + else + { + cloudData = cloudDataList->keyData[keyDataListIndex]; + } + + ++cloudDataList->subscriberCount; + cloudData->specificSubscribers.Insert(packet->guid, packet->guid, true, _FILE_AND_LINE_); + } + } + else + { + ++cloudDataList->subscriberCount; + cloudDataList->nonSpecificSubscribers.Insert(packet->guid, packet->guid, true, _FILE_AND_LINE_); + + // Remove packet->guid from CloudData::specificSubscribers among all instances of cloudDataList->keyData + unsigned int subscribedKeysIndex; + bool subscribedKeysIndexExists; + subscribedKeysIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudDataList->key, &subscribedKeysIndexExists); + if (subscribedKeysIndexExists) + { + KeySubscriberID* keySubscriberId; + keySubscriberId = remoteCloudClient->subscribedKeys[subscribedKeysIndex]; + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < keySubscriberId->specificSystemsSubscribedTo.Size(); specificSystemIndex++) + { + bool keyDataExists; + unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(keySubscriberId->specificSystemsSubscribedTo[specificSystemIndex], &keyDataExists); + if (keyDataExists) + { + CloudData *keyData = cloudDataList->keyData[keyDataIndex]; + keyData->specificSubscribers.Remove(packet->guid); + --cloudDataList->subscriberCount; + } + } + } + } + } + + if (remoteCloudClient->subscribedKeys.Size()==0) + { + // Didn't do anything + remoteSystems.Remove(packet->guid, _FILE_AND_LINE_); + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + } + } + + if (remoteServersWithData.Size()==0) + RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); +} +void CloudServer::OnUnsubscribeRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + + DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(packet->guid); + if (remoteSystemIndex.IsInvalid()==true) + return; + + RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); + + uint16_t keyCount, specificSystemCount; + DataStructures::List cloudKeys; + DataStructures::List specificSystems; + uint16_t index; + + CloudKey cloudKey; + bsIn.Read(keyCount); + for (index=0; index < keyCount; index++) + { + cloudKey.Serialize(false, &bsIn); + cloudKeys.Push(cloudKey, _FILE_AND_LINE_); + } + + RakNetGUID specificSystem; + bsIn.Read(specificSystemCount); + for (index=0; index < specificSystemCount; index++) + { + bsIn.Read(specificSystem); + specificSystems.Push(specificSystem, _FILE_AND_LINE_); + } + + for (unsigned int filterIndex=0; filterIndex < queryFilters.Size(); filterIndex++) + { + if (queryFilters[filterIndex]->OnUnsubscribeRequest(packet->guid, packet->systemAddress, cloudKeys, specificSystems )==false) + return; + } + +// CloudDataList *cloudDataList; + bool dataRepositoryExists; +// unsigned int dataRepositoryIndex; + + for (index=0; index < keyCount; index++) + { + CloudKey cloudKey = cloudKeys[index]; + + // dataRepositoryIndex = + dataRepository.GetIndexFromKey(cloudKey, &dataRepositoryExists); + if (dataRepositoryExists==false) + continue; +// cloudDataList = dataRepository[dataRepositoryIndex]; + + unsigned int keySubscriberIndex; + bool hasKeySubscriber; + keySubscriberIndex = remoteCloudClient->subscribedKeys.GetIndexFromKey(cloudKey, &hasKeySubscriber); + + if (hasKeySubscriber==false) + continue; + + UnsubscribeFromKey(remoteCloudClient, packet->guid, keySubscriberIndex, cloudKey, specificSystems); + } + + if (remoteCloudClient->IsUnused()) + { + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); + } +} +void CloudServer::OnServerToServerGetRequest(Packet *packet) +{ +// unsigned int remoteServerIndex; + bool objectExists; + //remoteServerIndex = + remoteServers.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists==false) + return; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + CloudQueryWithAddresses cloudQueryWithAddresses; + uint32_t requestId; + cloudQueryWithAddresses.Serialize(false, &bsIn); + bsIn.Read(requestId); + + DataStructures::List cloudDataResultList; + DataStructures::List cloudKeyResultList; + ProcessCloudQueryWithAddresses(cloudQueryWithAddresses, cloudDataResultList, cloudKeyResultList); + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_PROCESS_GET_RESPONSE); + bsOut.Write(requestId); + WriteCloudQueryRowFromResultList(cloudDataResultList, cloudKeyResultList, &bsOut); + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false); +} +void CloudServer::OnServerToServerGetResponse(Packet *packet) +{ + unsigned int remoteServerIndex; + bool objectExists; + remoteServerIndex = remoteServers.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists==false) + return; + + RemoteServer *remoteServer = remoteServers[remoteServerIndex]; + if (remoteServer==0) + return; + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + uint32_t requestId; + bsIn.Read(requestId); + + // Lookup request id + bool hasGetRequest; + unsigned int getRequestIndex; + getRequestIndex = getRequests.GetIndexFromKey(requestId, &hasGetRequest); + if (hasGetRequest==false) + return; + GetRequest *getRequest = getRequests[getRequestIndex]; + bool hasRemoteServer; + unsigned int remoteServerResponsesIndex; + remoteServerResponsesIndex = getRequest->remoteServerResponses.GetIndexFromKey(packet->guid, &hasRemoteServer); + if (hasRemoteServer==false) + return; + BufferedGetResponseFromServer *bufferedGetResponseFromServer; + bufferedGetResponseFromServer = getRequest->remoteServerResponses[remoteServerResponsesIndex]; + if (bufferedGetResponseFromServer->gotResult==true) + return; + bufferedGetResponseFromServer->gotResult=true; + uint32_t numRows; + bufferedGetResponseFromServer->queryResult.SerializeNumRows(false, numRows, &bsIn); + bufferedGetResponseFromServer->queryResult.SerializeCloudQueryRows(false, numRows, &bsIn, this); + + // If all results returned, then also process locally, and return to user + if (getRequest->AllRemoteServersHaveResponded()) + { + ProcessAndTransmitGetRequest(getRequest); + + getRequest->Clear(this); + RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); + + getRequests.RemoveAtIndex(getRequestIndex); + } +} +void CloudServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + + unsigned int remoteServerIndex; + bool objectExists; + remoteServerIndex = remoteServers.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists) + { + // Update remoteServerResponses by removing this server and sending the response if it is now complete + unsigned int getRequestIndex=0; + while (getRequestIndex < getRequests.Size()) + { + GetRequest *getRequest = getRequests[getRequestIndex]; + bool waitingForThisServer; + unsigned int remoteServerResponsesIndex = getRequest->remoteServerResponses.GetIndexFromKey(rakNetGUID, &waitingForThisServer); + if (waitingForThisServer) + { + getRequest->remoteServerResponses[remoteServerResponsesIndex]->Clear(this); + RakNet::OP_DELETE(getRequest->remoteServerResponses[remoteServerResponsesIndex], _FILE_AND_LINE_); + getRequest->remoteServerResponses.RemoveAtIndex(remoteServerResponsesIndex); + + if (getRequest->AllRemoteServersHaveResponded()) + { + ProcessAndTransmitGetRequest(getRequest); + getRequest->Clear(this); + RakNet::OP_DELETE(getRequest, _FILE_AND_LINE_); + + getRequests.RemoveAtIndex(getRequestIndex); + } + else + getRequestIndex++; + } + else + getRequestIndex++; + } + + RakNet::OP_DELETE(remoteServers[remoteServerIndex],_FILE_AND_LINE_); + remoteServers.RemoveAtIndex(remoteServerIndex); + } + + DataStructures::HashIndex remoteSystemIndex = remoteSystems.GetIndexOf(rakNetGUID); + if (remoteSystemIndex.IsInvalid()==false) + { + RemoteCloudClient* remoteCloudClient = remoteSystems.ItemAtIndex(remoteSystemIndex); + unsigned int uploadedKeysIndex; + for (uploadedKeysIndex=0; uploadedKeysIndex < remoteCloudClient->uploadedKeys.Size(); uploadedKeysIndex++) + { + // Delete keys this system has uploaded + bool keyDataRepositoryExists; + unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(remoteCloudClient->uploadedKeys[uploadedKeysIndex], &keyDataRepositoryExists); + if (keyDataRepositoryExists) + { + CloudDataList* cloudDataList = dataRepository[dataRepositoryIndex]; + bool keyDataExists; + unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(rakNetGUID, &keyDataExists); + if (keyDataExists) + { + CloudData *cloudData = cloudDataList->keyData[keyDataIndex]; + cloudDataList->uploaderCount--; + + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudData->specificSubscribers, false ); + NotifyClientSubscribersOfDataChange(cloudData, cloudDataList->key, cloudDataList->nonSpecificSubscribers, false ); + NotifyServerSubscribersOfDataChange(cloudData, cloudDataList->key, false ); + + cloudData->Clear(); + + if (cloudData->IsUnused()) + { + RakNet::OP_DELETE(cloudData,_FILE_AND_LINE_); + cloudDataList->keyData.RemoveAtIndex(keyDataIndex); + + if (cloudDataList->IsNotUploaded()) + { + // Tell other servers that this key is no longer uploaded, so they do not request it from us + RemoveUploadedKeyFromServers(cloudDataList->key); + } + + if (cloudDataList->IsUnused()) + { + // Tell other servers that this key is no longer uploaded, so they do not request it from us + RemoveUploadedKeyFromServers(cloudDataList->key); + + RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } + } + } + } + } + + unsigned int subscribedKeysIndex; + for (subscribedKeysIndex=0; subscribedKeysIndex < remoteCloudClient->subscribedKeys.Size(); subscribedKeysIndex++) + { + KeySubscriberID* keySubscriberId; + keySubscriberId = remoteCloudClient->subscribedKeys[subscribedKeysIndex]; + + bool keyDataRepositoryExists; + unsigned int keyDataRepositoryIndex = dataRepository.GetIndexFromKey(remoteCloudClient->subscribedKeys[subscribedKeysIndex]->key, &keyDataRepositoryExists); + if (keyDataRepositoryExists) + { + CloudDataList* cloudDataList = dataRepository[keyDataRepositoryIndex]; + if (keySubscriberId->specificSystemsSubscribedTo.Size()==0) + { + cloudDataList->nonSpecificSubscribers.Remove(rakNetGUID); + --cloudDataList->subscriberCount; + } + else + { + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < keySubscriberId->specificSystemsSubscribedTo.Size(); specificSystemIndex++) + { + bool keyDataExists; + unsigned int keyDataIndex = cloudDataList->keyData.GetIndexFromKey(keySubscriberId->specificSystemsSubscribedTo[specificSystemIndex], &keyDataExists); + if (keyDataExists) + { + CloudData *keyData = cloudDataList->keyData[keyDataIndex]; + keyData->specificSubscribers.Remove(rakNetGUID); + --cloudDataList->subscriberCount; + } + } + } + } + + RakNet::OP_DELETE(keySubscriberId, _FILE_AND_LINE_); + } + + // Delete and remove from remoteSystems + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + remoteSystems.RemoveAtIndex(remoteSystemIndex, _FILE_AND_LINE_); + } +} +void CloudServer::OnRakPeerShutdown(void) +{ + Clear(); +} +void CloudServer::Clear(void) +{ + unsigned int i,j; + for (i=0; i < dataRepository.Size(); i++) + { + CloudDataList *cloudDataList = dataRepository[i]; + for (j=0; j < cloudDataList->keyData.Size(); j++) + { + cloudDataList->keyData[j]->Clear(); + RakNet::OP_DELETE(cloudDataList->keyData[j], _FILE_AND_LINE_); + } + RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); + } + dataRepository.Clear(false, _FILE_AND_LINE_); + + for (i=0; i < remoteServers.Size(); i++) + { + RakNet::OP_DELETE(remoteServers[i], _FILE_AND_LINE_); + } + remoteServers.Clear(false, _FILE_AND_LINE_); + + for (i=0; i < getRequests.Size(); i++) + { + GetRequest *getRequest = getRequests[i]; + getRequest->Clear(this); + RakNet::OP_DELETE(getRequests[i], _FILE_AND_LINE_); + } + getRequests.Clear(false, _FILE_AND_LINE_); + + DataStructures::List keyList; + DataStructures::List itemList; + remoteSystems.GetAsList(itemList, keyList, _FILE_AND_LINE_); + for (i=0; i < itemList.Size(); i++) + { + RemoteCloudClient* remoteCloudClient = itemList[i]; + for (j=0; j < remoteCloudClient->subscribedKeys.Size(); j++) + { + RakNet::OP_DELETE(remoteCloudClient->subscribedKeys[j], _FILE_AND_LINE_); + } + RakNet::OP_DELETE(remoteCloudClient, _FILE_AND_LINE_); + } + remoteSystems.Clear(_FILE_AND_LINE_); +} +void CloudServer::WriteCloudQueryRowFromResultList(DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut) +{ + bsOut->WriteCasted(cloudKeyResultList.Size()); + unsigned int i; + for (i=0; i < cloudKeyResultList.Size(); i++) + { + WriteCloudQueryRowFromResultList(i, cloudDataResultList, cloudKeyResultList, bsOut); + } +} +void CloudServer::WriteCloudQueryRowFromResultList(unsigned int i, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut) +{ + CloudQueryRow cloudQueryRow; + CloudData *cloudData = cloudDataResultList[i]; + cloudQueryRow.key=cloudKeyResultList[i]; + cloudQueryRow.data=cloudData->dataPtr; + cloudQueryRow.length=cloudData->dataLengthBytes; + cloudQueryRow.serverSystemAddress=cloudData->serverSystemAddress; + cloudQueryRow.clientSystemAddress=cloudData->clientSystemAddress; + cloudQueryRow.serverGUID=cloudData->serverGUID; + cloudQueryRow.clientGUID=cloudData->clientGUID; + cloudQueryRow.Serialize(true, bsOut, 0); +} +void CloudServer::NotifyClientSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, DataStructures::OrderedList &subscribers, bool wasUpdated ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID) ID_CLOUD_SUBSCRIPTION_NOTIFICATION); + bsOut.Write(wasUpdated); + CloudQueryRow row; + row.key=key; + row.data=cloudData->dataPtr; + row.length=cloudData->dataLengthBytes; + row.serverSystemAddress=cloudData->serverSystemAddress; + row.clientSystemAddress=cloudData->clientSystemAddress; + row.serverGUID=cloudData->serverGUID; + row.clientGUID=cloudData->clientGUID; + row.Serialize(true,&bsOut,0); + + unsigned int i; + for (i=0; i < subscribers.Size(); i++) + { + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, subscribers[i], false); + } +} +void CloudServer::NotifyClientSubscribersOfDataChange( CloudQueryRow *row, DataStructures::OrderedList &subscribers, bool wasUpdated ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID) ID_CLOUD_SUBSCRIPTION_NOTIFICATION); + bsOut.Write(wasUpdated); + row->Serialize(true,&bsOut,0); + + unsigned int i; + for (i=0; i < subscribers.Size(); i++) + { + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, subscribers[i], false); + } +} +void CloudServer::NotifyServerSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, bool wasUpdated ) +{ + // Find every server that has subscribed + // Send them change notifications + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_DATA_CHANGED); + bsOut.Write(wasUpdated); + CloudQueryRow row; + row.key=key; + row.data=cloudData->dataPtr; + row.length=cloudData->dataLengthBytes; + row.serverSystemAddress=cloudData->serverSystemAddress; + row.clientSystemAddress=cloudData->clientSystemAddress; + row.serverGUID=cloudData->serverGUID; + row.clientGUID=cloudData->clientGUID; + row.Serialize(true,&bsOut,0); + + unsigned int i; + for (i=0; i < remoteServers.Size(); i++) + { + if (remoteServers[i]->gotSubscribedAndUploadedKeys==false || remoteServers[i]->subscribedKeys.HasData(key)) + { + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); + } + } +} +void CloudServer::AddServer(RakNetGUID systemIdentifier) +{ + ConnectionState cs = rakPeerInterface->GetConnectionState(systemIdentifier); + if (cs==IS_DISCONNECTED || cs==IS_NOT_CONNECTED) + return; + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(systemIdentifier,&objectExists); + if (objectExists==false) + { + RemoteServer *remoteServer = RakNet::OP_NEW(_FILE_AND_LINE_); + remoteServer->gotSubscribedAndUploadedKeys=false; + remoteServer->serverAddress=systemIdentifier; + remoteServers.InsertAtIndex(remoteServer, index, _FILE_AND_LINE_); + + SendUploadedAndSubscribedKeysToServer(systemIdentifier); + } +} +void CloudServer::RemoveServer(RakNetGUID systemAddress) +{ + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(systemAddress,&objectExists); + if (objectExists==true) + { + RakNet::OP_DELETE(remoteServers[index],_FILE_AND_LINE_); + remoteServers.RemoveAtIndex(index); + } +} +void CloudServer::GetRemoteServers(DataStructures::List &remoteServersOut) +{ + remoteServersOut.Clear(true, _FILE_AND_LINE_); + + unsigned int i; + for (i=0; i < remoteServers.Size(); i++) + { + remoteServersOut.Push(remoteServers[i]->serverAddress, _FILE_AND_LINE_); + } +} +void CloudServer::ProcessAndTransmitGetRequest(GetRequest *getRequest) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID) ID_CLOUD_GET_RESPONSE); + + // BufferedGetResponseFromServer getResponse; + CloudQueryResult cloudQueryResult; + cloudQueryResult.cloudQuery=getRequest->cloudQueryWithAddresses.cloudQuery; + cloudQueryResult.subscribeToResults=getRequest->cloudQueryWithAddresses.cloudQuery.subscribeToResults; + cloudQueryResult.SerializeHeader(true, &bsOut); + + DataStructures::List cloudDataResultList; + DataStructures::List cloudKeyResultList; + ProcessCloudQueryWithAddresses(getRequest->cloudQueryWithAddresses, cloudDataResultList, cloudKeyResultList); + bool unlimitedRows=getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn==0; + + uint32_t localNumRows = (uint32_t) cloudDataResultList.Size(); + if (unlimitedRows==false && + localNumRows > getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex && + localNumRows - getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex > getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn ) + localNumRows=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex + getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn; + + BitSize_t bitStreamOffset = bsOut.GetWriteOffset(); + uint32_t localRowsToWrite; + unsigned int skipRows; + if (localNumRows>getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex) + { + localRowsToWrite=localNumRows-getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex; + skipRows=0; + } + else + { + localRowsToWrite=0; + skipRows=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex-localNumRows; + } + cloudQueryResult.SerializeNumRows(true, localRowsToWrite, &bsOut); + for (unsigned int i=getRequest->cloudQueryWithAddresses.cloudQuery.startingRowIndex; i < localNumRows; i++) + { + WriteCloudQueryRowFromResultList(i, cloudDataResultList, cloudKeyResultList, &bsOut); + } + + // Append remote systems for remaining rows + if (unlimitedRows==true || getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn>localRowsToWrite) + { + uint32_t remainingRows=0; + uint32_t additionalRowsWritten=0; + if (unlimitedRows==false) + remainingRows=getRequest->cloudQueryWithAddresses.cloudQuery.maxRowsToReturn-localRowsToWrite; + + unsigned int remoteServerResponseIndex; + for (remoteServerResponseIndex=0; remoteServerResponseIndex < getRequest->remoteServerResponses.Size(); remoteServerResponseIndex++) + { + BufferedGetResponseFromServer *bufferedGetResponseFromServer = getRequest->remoteServerResponses[remoteServerResponseIndex]; + unsigned int cloudQueryRowIndex; + for (cloudQueryRowIndex=0; cloudQueryRowIndex < bufferedGetResponseFromServer->queryResult.rowsReturned.Size(); cloudQueryRowIndex++) + { + if (skipRows>0) + { + --skipRows; + continue; + } + bufferedGetResponseFromServer->queryResult.rowsReturned[cloudQueryRowIndex]->Serialize(true, &bsOut, this); + + ++additionalRowsWritten; + if (unlimitedRows==false && --remainingRows==0) + break; + } + + if (unlimitedRows==false && remainingRows==0) + break; + } + + if (additionalRowsWritten>0) + { + BitSize_t curOffset = bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(bitStreamOffset); + localRowsToWrite+=additionalRowsWritten; + cloudQueryResult.SerializeNumRows(true, localRowsToWrite, &bsOut); + bsOut.SetWriteOffset(curOffset); + } + } + + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, getRequest->requestingClient, false); +} +void CloudServer::ProcessCloudQueryWithAddresses( CloudServer::CloudQueryWithAddresses &cloudQueryWithAddresses, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList ) +{ + CloudQueryResult cloudQueryResult; + CloudQueryRow cloudQueryRow; + unsigned int queryIndex; + bool dataRepositoryExists; + CloudDataList* cloudDataList; + unsigned int keyDataIndex; + + // If specificSystems list empty, applies to all systems + // For each of keys in cloudQueryWithAddresses, return that data, limited by maxRowsToReturn + for (queryIndex=0; queryIndex < cloudQueryWithAddresses.cloudQuery.keys.Size(); queryIndex++) + { + const CloudKey &key = cloudQueryWithAddresses.cloudQuery.keys[queryIndex]; + + unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(key, &dataRepositoryExists); + if (dataRepositoryExists) + { + cloudDataList=dataRepository[dataRepositoryIndex]; + + if (cloudDataList->uploaderCount>0) + { + // Return all keyData that was uploaded by specificSystems, or all if not specified + if (cloudQueryWithAddresses.specificSystems.Size()>0) + { + // Return data for matching systems + unsigned int specificSystemIndex; + for (specificSystemIndex=0; specificSystemIndex < cloudQueryWithAddresses.specificSystems.Size(); specificSystemIndex++) + { + bool uploaderExists; + keyDataIndex = cloudDataList->keyData.GetIndexFromKey(cloudQueryWithAddresses.specificSystems[specificSystemIndex], &uploaderExists); + if (uploaderExists) + { + cloudDataResultList.Push(cloudDataList->keyData[keyDataIndex], _FILE_AND_LINE_); + cloudKeyResultList.Push(key, _FILE_AND_LINE_); + } + } + } + else + { + // Return data for all systems + for (keyDataIndex=0; keyDataIndex < cloudDataList->keyData.Size(); keyDataIndex++) + { + cloudDataResultList.Push(cloudDataList->keyData[keyDataIndex], _FILE_AND_LINE_); + cloudKeyResultList.Push(key, _FILE_AND_LINE_); + } + } + } + } + } +} +void CloudServer::SendUploadedAndSubscribedKeysToServer( RakNetGUID systemAddress ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_ADD_UPLOADED_AND_SUBSCRIBED_KEYS); + bsOut.WriteCasted(dataRepository.Size()); + for (unsigned int i=0; i < dataRepository.Size(); i++) + dataRepository[i]->key.Serialize(true, &bsOut); + + BitSize_t startOffset, endOffset; + uint16_t subscribedKeyCount=0; + startOffset=bsOut.GetWriteOffset(); + bsOut.WriteCasted(subscribedKeyCount); + for (unsigned int i=0; i < dataRepository.Size(); i++) + { + if (dataRepository[i]->subscriberCount>0) + { + dataRepository[i]->key.Serialize(true, &bsOut); + subscribedKeyCount++; + } + } + endOffset=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(startOffset); + bsOut.WriteCasted(subscribedKeyCount); + bsOut.SetWriteOffset(endOffset); + + if (dataRepository.Size()>0 || subscribedKeyCount>0) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false); +} +void CloudServer::SendUploadedKeyToServers( CloudKey &cloudKey ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_ADD_UPLOADED_KEY); + cloudKey.Serialize(true, &bsOut); + for (unsigned int i=0; i < remoteServers.Size(); i++) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); +} +void CloudServer::SendSubscribedKeyToServers( CloudKey &cloudKey ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_ADD_SUBSCRIBED_KEY); + cloudKey.Serialize(true, &bsOut); + for (unsigned int i=0; i < remoteServers.Size(); i++) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); +} +void CloudServer::RemoveUploadedKeyFromServers( CloudKey &cloudKey ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_REMOVE_UPLOADED_KEY); + cloudKey.Serialize(true, &bsOut); + for (unsigned int i=0; i < remoteServers.Size(); i++) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); +} +void CloudServer::RemoveSubscribedKeyFromServers( CloudKey &cloudKey ) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_CLOUD_SERVER_TO_SERVER_COMMAND); + bsOut.Write((MessageID)STSC_REMOVE_SUBSCRIBED_KEY); + cloudKey.Serialize(true, &bsOut); + for (unsigned int i=0; i < remoteServers.Size(); i++) + SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, remoteServers[i]->serverAddress, false); +} +void CloudServer::OnSendUploadedAndSubscribedKeysToServer( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + remoteServer->gotSubscribedAndUploadedKeys=true; + +// unsigned int insertionIndex; + bool alreadyHasKey; + uint16_t numUploadedKeys, numSubscribedKeys; + bsIn.Read(numUploadedKeys); + for (uint16_t i=0; i < numUploadedKeys; i++) + { + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); + + // insertionIndex = + remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==false) + remoteServer->uploadedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); + } + + bsIn.Read(numSubscribedKeys); + for (uint16_t i=0; i < numSubscribedKeys; i++) + { + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); + + //insertionIndex = + remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==false) + remoteServer->subscribedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); + } + + // Potential todo - join servers + // For each uploaded key that we subscribe to, query it + // For each subscribed key that we have, send it +} +void CloudServer::OnSendUploadedKeyToServers( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); +// unsigned int insertionIndex; + bool alreadyHasKey; +// insertionIndex = + remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==false) + remoteServer->uploadedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); +} +void CloudServer::OnSendSubscribedKeyToServers( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); +// unsigned int insertionIndex; + bool alreadyHasKey; +// insertionIndex = + remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + + // Do not need to send current values, the Get request will do that as the Get request is sent at the same time + if (alreadyHasKey==false) + remoteServer->subscribedKeys.Insert(cloudKey,cloudKey,true,_FILE_AND_LINE_); +} +void CloudServer::OnRemoveUploadedKeyFromServers( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); + unsigned int insertionIndex; + bool alreadyHasKey; + insertionIndex = remoteServer->uploadedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==true) + remoteServer->uploadedKeys.RemoveAtIndex(insertionIndex); +} +void CloudServer::OnRemoveSubscribedKeyFromServers( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + unsigned int index = remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + RemoteServer *remoteServer = remoteServers[index]; + CloudKey cloudKey; + cloudKey.Serialize(false, &bsIn); + unsigned int insertionIndex; + bool alreadyHasKey; + insertionIndex = remoteServer->subscribedKeys.GetIndexFromKey(cloudKey, &alreadyHasKey); + if (alreadyHasKey==true) + remoteServer->subscribedKeys.RemoveAtIndex(insertionIndex); +} +void CloudServer::OnServerDataChanged( Packet *packet ) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + bool objectExists; + remoteServers.GetIndexFromKey(packet->guid,&objectExists); + if (objectExists==false) + return; + + // Find everyone that cares about this change and relay + bool wasUpdated=false; + bsIn.Read(wasUpdated); + CloudQueryRow row; + row.Serialize(false, &bsIn, this); + + CloudDataList *cloudDataList; + bool dataRepositoryExists; + unsigned int dataRepositoryIndex; + dataRepositoryIndex = dataRepository.GetIndexFromKey(row.key, &dataRepositoryExists); + if (dataRepositoryExists==false) + { + DeallocateRowData(row.data); + return; + } + cloudDataList = dataRepository[dataRepositoryIndex]; + CloudData *cloudData; + bool keyDataListExists; + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(row.clientGUID, &keyDataListExists); + if (keyDataListExists==true) + { + cloudData = cloudDataList->keyData[keyDataListIndex]; + NotifyClientSubscribersOfDataChange(&row, cloudData->specificSubscribers, wasUpdated ); + } + + NotifyClientSubscribersOfDataChange(&row, cloudDataList->nonSpecificSubscribers, wasUpdated ); + DeallocateRowData(row.data); +} +void CloudServer::GetServersWithUploadedKeys( + DataStructures::List &keys, + DataStructures::List &remoteServersWithData + ) +{ + remoteServersWithData.Clear(true, _FILE_AND_LINE_); + + unsigned int i,j; + for (i=0; i < remoteServers.Size(); i++) + { + remoteServers[i]->workingFlag=false; + } + + for (i=0; i < remoteServers.Size(); i++) + { + if (remoteServers[i]->workingFlag==false) + { + if (remoteServers[i]->gotSubscribedAndUploadedKeys==false) + { + remoteServers[i]->workingFlag=true; + remoteServersWithData.Push(remoteServers[i], _FILE_AND_LINE_); + } + else + { + remoteServers[i]->workingFlag=false; + for (j=0; j < keys.Size(); j++) + { + if (remoteServers[i]->workingFlag==false && remoteServers[i]->uploadedKeys.HasData(keys[j])) + { + remoteServers[i]->workingFlag=true; + remoteServersWithData.Push(remoteServers[i], _FILE_AND_LINE_); + break; + } + } + } + } + } +} + +CloudServer::CloudDataList *CloudServer::GetOrAllocateCloudDataList(CloudKey key, bool *dataRepositoryExists, unsigned int &dataRepositoryIndex) +{ + CloudDataList *cloudDataList; + + dataRepositoryIndex = dataRepository.GetIndexFromKey(key, dataRepositoryExists); + if (*dataRepositoryExists==false) + { + cloudDataList = RakNet::OP_NEW(_FILE_AND_LINE_); + cloudDataList->key=key; + cloudDataList->uploaderCount=0; + cloudDataList->subscriberCount=0; + dataRepository.InsertAtIndex(cloudDataList,dataRepositoryIndex,_FILE_AND_LINE_); + } + else + { + cloudDataList = dataRepository[dataRepositoryIndex]; + } + + return cloudDataList; +} + +void CloudServer::UnsubscribeFromKey(RemoteCloudClient *remoteCloudClient, RakNetGUID remoteCloudClientGuid, unsigned int keySubscriberIndex, CloudKey &cloudKey, DataStructures::List &specificSystems) +{ + KeySubscriberID* keySubscriberId = remoteCloudClient->subscribedKeys[keySubscriberIndex]; + + // If removing specific systems, but global subscription, fail + if (keySubscriberId->specificSystemsSubscribedTo.Size()==0 && specificSystems.Size()>0) + return; + + bool dataRepositoryExists; + CloudDataList *cloudDataList; + unsigned int dataRepositoryIndex = dataRepository.GetIndexFromKey(cloudKey, &dataRepositoryExists); + if (dataRepositoryExists==false) + return; + + unsigned int i,j; + + cloudDataList = dataRepository[dataRepositoryIndex]; + if (specificSystems.Size()==0) + { + // Remove global subscriber. If returns false, have to remove specific subscribers + if (cloudDataList->RemoveSubscriber(remoteCloudClientGuid)==false) + { + for (i=0; i < keySubscriberId->specificSystemsSubscribedTo.Size(); i++) + { + RemoveSpecificSubscriber(keySubscriberId->specificSystemsSubscribedTo[i], cloudDataList, remoteCloudClientGuid); + } + } + keySubscriberId->specificSystemsSubscribedTo.Clear(true, _FILE_AND_LINE_); + } + else + { + for (j=0; j < specificSystems.Size(); j++) + { + unsigned int specificSystemsSubscribedToIndex; + bool hasSpecificSystemsSubscribedTo; + specificSystemsSubscribedToIndex=keySubscriberId->specificSystemsSubscribedTo.GetIndexFromKey(specificSystems[j], &hasSpecificSystemsSubscribedTo); + if (hasSpecificSystemsSubscribedTo) + { + RemoveSpecificSubscriber(specificSystems[j], cloudDataList, remoteCloudClientGuid); + keySubscriberId->specificSystemsSubscribedTo.RemoveAtIndex(specificSystemsSubscribedToIndex); + } + } + } + + if (keySubscriberId->specificSystemsSubscribedTo.Size()==0) + { + RakNet::OP_DELETE(keySubscriberId, _FILE_AND_LINE_); + remoteCloudClient->subscribedKeys.RemoveAtIndex(keySubscriberIndex); + } + + if (cloudDataList->subscriberCount==0) + RemoveSubscribedKeyFromServers(cloudKey); + + if (cloudDataList->IsUnused()) + { + RakNet::OP_DELETE(cloudDataList, _FILE_AND_LINE_); + dataRepository.RemoveAtIndex(dataRepositoryIndex); + } +} +void CloudServer::RemoveSpecificSubscriber(RakNetGUID specificSubscriber, CloudDataList *cloudDataList, RakNetGUID remoteCloudClientGuid) +{ + bool keyDataListExists; + unsigned int keyDataListIndex = cloudDataList->keyData.GetIndexFromKey(specificSubscriber, &keyDataListExists); + if (keyDataListExists==false) + return; + CloudData *cloudData = cloudDataList->keyData[keyDataListIndex]; + bool hasSpecificSubscriber; + unsigned int specificSubscriberIndex = cloudData->specificSubscribers.GetIndexFromKey(remoteCloudClientGuid, &hasSpecificSubscriber); + if (hasSpecificSubscriber) + { + cloudData->specificSubscribers.RemoveAtIndex(specificSubscriberIndex); + cloudDataList->subscriberCount--; + + if (cloudData->IsUnused()) + { + RakNet::OP_DELETE(cloudData, _FILE_AND_LINE_); + cloudDataList->keyData.RemoveAtIndex(keyDataListIndex); + } + } +} + +void CloudServer::ForceExternalSystemAddress(SystemAddress forcedAddress) +{ + forceAddress=forcedAddress; +} +void CloudServer::AddQueryFilter(CloudServerQueryFilter* filter) +{ + if (queryFilters.GetIndexOf(filter)!=(unsigned int) -1) + return; + queryFilters.Push(filter, _FILE_AND_LINE_); +} +void CloudServer::RemoveQueryFilter(CloudServerQueryFilter* filter) +{ + unsigned int index; + index = queryFilters.GetIndexOf(filter); + if (index != (unsigned int) -1) + queryFilters.RemoveAtIndex(index); +} +void CloudServer::RemoveAllQueryFilters(void) +{ + queryFilters.Clear(true, _FILE_AND_LINE_); +} + +#endif diff --git a/src/raknet/CloudServer.h b/src/raknet/CloudServer.h index cf4f78f..391a46e 100755 --- a/src/raknet/CloudServer.h +++ b/src/raknet/CloudServer.h @@ -1,375 +1,375 @@ -/// \file CloudServer.h -/// \brief Stores client data, and allows cross-server communication to retrieve this data -/// \details TODO -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_CloudServer==1 - -#ifndef __CLOUD_SERVER_H -#define __CLOUD_SERVER_H - -#include "PluginInterface2.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" -#include "RakString.h" -#include "DS_Hash.h" -#include "CloudCommon.h" -#include "DS_OrderedList.h" - -/// If the data is smaller than this value, an allocation is avoid. However, this value exists for every row -#define CLOUD_SERVER_DATA_STACK_SIZE 32 - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \brief Zero or more instances of CloudServerQueryFilter can be attached to CloudServer to restrict client queries -/// All attached instances of CloudServerQueryFilter on each corresponding operation, from all directly connected clients -/// If any attached instance returns false for a given operation, that operation is silently rejected -/// \ingroup CLOUD_GROUP -class RAK_DLL_EXPORT CloudServerQueryFilter -{ -public: - CloudServerQueryFilter() {} - virtual ~CloudServerQueryFilter() {} - - /// Called when a local client wants to post data - /// \return true to allow, false to reject - virtual bool OnPostRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudKey key, uint32_t dataLength, const char *data)=0; - - /// Called when a local client wants to release data that it has previously uploaded - /// \return true to allow, false to reject - virtual bool OnReleaseRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List &cloudKeys)=0; - - /// Called when a local client wants to query data - /// If you return false, the client will get no response at all - /// \return true to allow, false to reject - virtual bool OnGetRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudQuery &query, DataStructures::List &specificSystems)=0; - - /// Called when a local client wants to stop getting updates for data - /// If you return false, the client will keep getting updates for that data - /// \return true to allow, false to reject - virtual bool OnUnsubscribeRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List &cloudKeys, DataStructures::List &specificSystems)=0; -}; - -/// \brief Stores client data, and allows cross-server communication to retrieve this data -/// \ingroup CLOUD_GROUP -class RAK_DLL_EXPORT CloudServer : public PluginInterface2, CloudAllocator -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(CloudServer) - - CloudServer(); - virtual ~CloudServer(); - - /// \brief Max bytes a client can upload - /// Data in excess of this value is silently ignored - /// defaults to 0 (unlimited) - /// \param[in] bytes Max bytes a client can upload. 0 means unlimited. - void SetMaxUploadBytesPerClient(uint64_t bytes); - - /// \brief Max bytes returned by a download. If the number of bytes would exceed this amount, the returned list is truncated - /// However, if this would result in no rows downloaded, then one row will be returned. - /// \param[in] bytes Max bytes a client can download from a single Get(). 0 means unlimited. - void SetMaxBytesPerDownload(uint64_t bytes); - - /// \brief Add a server, which is assumed to be connected in a fully connected mesh to all other servers and also running the CloudServer plugin - /// The other system must also call AddServer before getting the subscription data, or it will be rejected. - /// Sending a message telling the other system to call AddServer(), followed by calling AddServer() locally, would be sufficient for this to work. - /// \note This sends subscription data to the other system, using RELIABLE_ORDERED on channel 0 - /// \param[in] systemIdentifier Identifier of the remote system - void AddServer(RakNetGUID systemIdentifier); - - /// \brief Removes a server added through AddServer() - /// \param[in] systemIdentifier Identifier of the remote system - void RemoveServer(RakNetGUID systemIdentifier); - - /// Return list of servers added with AddServer() - /// \param[out] remoteServers List of servers added - void GetRemoteServers(DataStructures::List &remoteServersOut); - - /// \brief Frees all memory. Does not remove query filters - void Clear(void); - - /// \brief Report the specified SystemAddress to client queries, rather than what RakPeer reads. - /// This is useful if you already know your public IP - /// This only applies to future updates, so call it before updating to apply to all queries - /// \param[in] forcedAddress The systmeAddress to return in queries. Use UNASSIGNED_SYSTEM_ADDRESS (default) to use what RakPeer returns - void ForceExternalSystemAddress(SystemAddress forcedAddress); - - /// \brief Adds a callback called on each query. If all filters returns true for an operation, the operation is allowed. - /// If the filter was already added, the function silently fails - /// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters() - void AddQueryFilter(CloudServerQueryFilter* filter); - - /// \brief Removes a callback added with AddQueryFilter() - /// The instance is not deleted, only unreferenced. It is up to the user to delete the instance, if necessary - /// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters() - void RemoveQueryFilter(CloudServerQueryFilter* filter); - - /// \brief Removes all instances of CloudServerQueryFilter added with AddQueryFilter(). - /// The instances are not deleted, only unreferenced. It is up to the user to delete the instances, if necessary - void RemoveAllQueryFilters(void); - -protected: - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnRakPeerShutdown(void); - - - virtual void OnPostRequest(Packet *packet); - virtual void OnReleaseRequest(Packet *packet); - virtual void OnGetRequest(Packet *packet); - virtual void OnUnsubscribeRequest(Packet *packet); - virtual void OnServerToServerGetRequest(Packet *packet); - virtual void OnServerToServerGetResponse(Packet *packet); - - uint64_t maxUploadBytesPerClient, maxBytesPerDowload; - - // ---------------------------------------------------------------------------- - // For a given data key, quickly look up one or all systems that have uploaded - // ---------------------------------------------------------------------------- - struct CloudData - { - CloudData() {} - ~CloudData() {if (allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_);} - bool IsUnused(void) const {return isUploaded==false && specificSubscribers.Size()==0;} - void Clear(void) {if (dataPtr==allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_); allocatedData=0; dataPtr=0; dataLengthBytes=0; isUploaded=false;} - - unsigned char stackData[CLOUD_SERVER_DATA_STACK_SIZE]; - unsigned char *allocatedData; // Uses allocatedData instead of stackData if length of data exceeds CLOUD_SERVER_DATA_STACK_SIZE - unsigned char *dataPtr; // Points to either stackData or allocatedData - uint32_t dataLengthBytes; - bool isUploaded; - - /// System address of server that is holding this data, and the client is connected to - SystemAddress serverSystemAddress; - - /// System address of client that uploaded this data - SystemAddress clientSystemAddress; - - /// RakNetGUID of server that is holding this data, and the client is connected to - RakNetGUID serverGUID; - - /// RakNetGUID of client that uploaded this data - RakNetGUID clientGUID; - - /// When the key data changes from this particular system, notify these subscribers - /// This list mutually exclusive with CloudDataList::nonSpecificSubscribers - DataStructures::OrderedList specificSubscribers; - }; - void WriteCloudQueryRowFromResultList(unsigned int i, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut); - void WriteCloudQueryRowFromResultList(DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut); - - static int KeyDataPtrComp( const RakNetGUID &key, CloudData* const &data ); - struct CloudDataList - { - bool IsUnused(void) const {return keyData.Size()==0 && nonSpecificSubscribers.Size()==0;} - bool IsNotUploaded(void) const {return uploaderCount==0;} - bool RemoveSubscriber(RakNetGUID g) { - bool objectExists; - unsigned int index; - index = nonSpecificSubscribers.GetIndexFromKey(g, &objectExists); - if (objectExists) - { - subscriberCount--; - nonSpecificSubscribers.RemoveAtIndex(index); - return true; - } - return false; - } - - unsigned int uploaderCount, subscriberCount; - CloudKey key; - - // Data uploaded from or subscribed to for various systems - DataStructures::OrderedList keyData; - - /// When the key data changes from any system, notify these subscribers - /// This list mutually exclusive with CloudData::specificSubscribers - DataStructures::OrderedList nonSpecificSubscribers; - }; - - static int KeyDataListComp( const CloudKey &key, CloudDataList * const &data ); - DataStructures::OrderedList dataRepository; - - struct KeySubscriberID - { - CloudKey key; - DataStructures::OrderedList specificSystemsSubscribedTo; - }; - static int KeySubscriberIDComp(const CloudKey &key, KeySubscriberID * const &data ); - - // Remote systems - struct RemoteCloudClient - { - bool IsUnused(void) const {return uploadedKeys.Size()==0 && subscribedKeys.Size()==0;} - - DataStructures::OrderedList uploadedKeys; - DataStructures::OrderedList subscribedKeys; - uint64_t uploadedBytes; - }; - DataStructures::Hash remoteSystems; - - // For a given user, release all subscribed and uploaded keys - void ReleaseSystem(RakNetGUID clientAddress ); - - // For a given user, release a set of keys - void ReleaseKeys(RakNetGUID clientAddress, DataStructures::List &keys ); - - void NotifyClientSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, DataStructures::OrderedList &subscribers, bool wasUpdated ); - void NotifyClientSubscribersOfDataChange( CloudQueryRow *row, DataStructures::OrderedList &subscribers, bool wasUpdated ); - void NotifyServerSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, bool wasUpdated ); - - struct RemoteServer - { - RakNetGUID serverAddress; - // This server needs to know about these keys when they are updated or deleted - DataStructures::OrderedList subscribedKeys; - // This server has uploaded these keys, and needs to know about Get() requests - DataStructures::OrderedList uploadedKeys; - - // Just for processing - bool workingFlag; - - // If false, we don't know what keys they have yet, so send everything - bool gotSubscribedAndUploadedKeys; - }; - - static int RemoteServerComp(const RakNetGUID &key, RemoteServer* const &data ); - DataStructures::OrderedList remoteServers; - - struct BufferedGetResponseFromServer - { - void Clear(CloudAllocator *allocator); - - RakNetGUID serverAddress; - CloudQueryResult queryResult; - bool gotResult; - }; - - struct CloudQueryWithAddresses - { - // Inputs - CloudQuery cloudQuery; - DataStructures::List specificSystems; - - void Serialize(bool writeToBitstream, BitStream *bitStream); - }; - - static int BufferedGetResponseFromServerComp(const RakNetGUID &key, BufferedGetResponseFromServer* const &data ); - struct GetRequest - { - void Clear(CloudAllocator *allocator); - bool AllRemoteServersHaveResponded(void) const; - CloudQueryWithAddresses cloudQueryWithAddresses; - - // When request started. If takes too long for a response from another system, can abort remaining systems - RakNet::Time requestStartTime; - - // Assigned by server that gets the request to identify response. See nextGetRequestId - uint32_t requestId; - - RakNetGUID requestingClient; - - DataStructures::OrderedList remoteServerResponses; - }; - static int GetRequestComp(const uint32_t &key, GetRequest* const &data ); - DataStructures::OrderedList getRequests; - RakNet::Time nextGetRequestsCheck; - - uint32_t nextGetRequestId; - - void ProcessAndTransmitGetRequest(GetRequest *getRequest); - - void ProcessCloudQueryWithAddresses( - CloudServer::CloudQueryWithAddresses &cloudQueryWithAddresses, - DataStructures::List &cloudDataResultList, - DataStructures::List &cloudKeyResultList - ); - - void SendUploadedAndSubscribedKeysToServer( RakNetGUID systemAddress ); - void SendUploadedKeyToServers( CloudKey &cloudKey ); - void SendSubscribedKeyToServers( CloudKey &cloudKey ); - void RemoveUploadedKeyFromServers( CloudKey &cloudKey ); - void RemoveSubscribedKeyFromServers( CloudKey &cloudKey ); - - void OnSendUploadedAndSubscribedKeysToServer( Packet *packet ); - void OnSendUploadedKeyToServers( Packet *packet ); - void OnSendSubscribedKeyToServers( Packet *packet ); - void OnRemoveUploadedKeyFromServers( Packet *packet ); - void OnRemoveSubscribedKeyFromServers( Packet *packet ); - void OnServerDataChanged( Packet *packet ); - - void GetServersWithUploadedKeys( - DataStructures::List &keys, - DataStructures::List &remoteServersWithData - ); - - CloudServer::CloudDataList *GetOrAllocateCloudDataList(CloudKey key, bool *dataRepositoryExists, unsigned int &dataRepositoryIndex); - - void UnsubscribeFromKey(RemoteCloudClient *remoteCloudClient, RakNetGUID remoteCloudClientGuid, unsigned int keySubscriberIndex, CloudKey &cloudKey, DataStructures::List &specificSystems); - void RemoveSpecificSubscriber(RakNetGUID specificSubscriber, CloudDataList *cloudDataList, RakNetGUID remoteCloudClientGuid); - - DataStructures::List queryFilters; - - SystemAddress forceAddress; -}; - - -} // namespace RakNet - -#endif - - -// Key subscription -// -// A given system can subscribe to one or more keys. -// The subscription can be further be defined as only subscribing to keys uploaded by or changed by a given system. -// It is possible to subscribe to keys not yet uploaded, or uploaded to another system -// -// Operations: -// -// 1. SubscribeToKey() - Get() operation with subscription -// A. Add to key subscription list for the client, which contains a keyId / specificUploaderList pair -// B. Send to remote servers that for this key, they should send us updates -// C. (Done, get operation returns current values) -// -// 2. UpdateData() - Post() operation -// A. Find all subscribers to this data, for the uploading system. -// B. Send them the uploaded data -// C. Find all servers that subscribe to this data -// D. Send them the uploaded data -// -// 3. DeleteData() - Release() operation -// A. Find all subscribers to this data, for the deleting system. -// B. Inform them of the deletion -// C. Find all servers that subscribe to this data -// D. Inform them of the deletion -// -// 4. Unsubscribe() -// A. Find this subscriber, and remove their subscription -// B. If no one else is subscribing to this key for any system, notify remote servers we no longer need subscription updates -// -// Internal operations: -// -// 1. Find if any connected client has subscribed to a given key -// A. This is used add and remove our subscription for this key to remote servers -// -// 2. For a given key and updating address, find all connected clients that care -// A. First find connected clients that have subscribed to this key, regardless of address -// B. Then find connected clients that have subscribed to this key for this particular address -// -// 3. Find all remote servers that have subscribed to a given key -// A. This is so when the key is updated or deleted, we know who to send it to -// -// 4. For a given client (such as on disconnect), remove all records of their subscriptions - -#endif // _RAKNET_SUPPORT_* +/// \file CloudServer.h +/// \brief Stores client data, and allows cross-server communication to retrieve this data +/// \details TODO +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_CloudServer==1 + +#ifndef __CLOUD_SERVER_H +#define __CLOUD_SERVER_H + +#include "PluginInterface2.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" +#include "RakString.h" +#include "DS_Hash.h" +#include "CloudCommon.h" +#include "DS_OrderedList.h" + +/// If the data is smaller than this value, an allocation is avoid. However, this value exists for every row +#define CLOUD_SERVER_DATA_STACK_SIZE 32 + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief Zero or more instances of CloudServerQueryFilter can be attached to CloudServer to restrict client queries +/// All attached instances of CloudServerQueryFilter on each corresponding operation, from all directly connected clients +/// If any attached instance returns false for a given operation, that operation is silently rejected +/// \ingroup CLOUD_GROUP +class RAK_DLL_EXPORT CloudServerQueryFilter +{ +public: + CloudServerQueryFilter() {} + virtual ~CloudServerQueryFilter() {} + + /// Called when a local client wants to post data + /// \return true to allow, false to reject + virtual bool OnPostRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudKey key, uint32_t dataLength, const char *data)=0; + + /// Called when a local client wants to release data that it has previously uploaded + /// \return true to allow, false to reject + virtual bool OnReleaseRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List &cloudKeys)=0; + + /// Called when a local client wants to query data + /// If you return false, the client will get no response at all + /// \return true to allow, false to reject + virtual bool OnGetRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudQuery &query, DataStructures::List &specificSystems)=0; + + /// Called when a local client wants to stop getting updates for data + /// If you return false, the client will keep getting updates for that data + /// \return true to allow, false to reject + virtual bool OnUnsubscribeRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List &cloudKeys, DataStructures::List &specificSystems)=0; +}; + +/// \brief Stores client data, and allows cross-server communication to retrieve this data +/// \ingroup CLOUD_GROUP +class RAK_DLL_EXPORT CloudServer : public PluginInterface2, CloudAllocator +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(CloudServer) + + CloudServer(); + virtual ~CloudServer(); + + /// \brief Max bytes a client can upload + /// Data in excess of this value is silently ignored + /// defaults to 0 (unlimited) + /// \param[in] bytes Max bytes a client can upload. 0 means unlimited. + void SetMaxUploadBytesPerClient(uint64_t bytes); + + /// \brief Max bytes returned by a download. If the number of bytes would exceed this amount, the returned list is truncated + /// However, if this would result in no rows downloaded, then one row will be returned. + /// \param[in] bytes Max bytes a client can download from a single Get(). 0 means unlimited. + void SetMaxBytesPerDownload(uint64_t bytes); + + /// \brief Add a server, which is assumed to be connected in a fully connected mesh to all other servers and also running the CloudServer plugin + /// The other system must also call AddServer before getting the subscription data, or it will be rejected. + /// Sending a message telling the other system to call AddServer(), followed by calling AddServer() locally, would be sufficient for this to work. + /// \note This sends subscription data to the other system, using RELIABLE_ORDERED on channel 0 + /// \param[in] systemIdentifier Identifier of the remote system + void AddServer(RakNetGUID systemIdentifier); + + /// \brief Removes a server added through AddServer() + /// \param[in] systemIdentifier Identifier of the remote system + void RemoveServer(RakNetGUID systemIdentifier); + + /// Return list of servers added with AddServer() + /// \param[out] remoteServers List of servers added + void GetRemoteServers(DataStructures::List &remoteServersOut); + + /// \brief Frees all memory. Does not remove query filters + void Clear(void); + + /// \brief Report the specified SystemAddress to client queries, rather than what RakPeer reads. + /// This is useful if you already know your public IP + /// This only applies to future updates, so call it before updating to apply to all queries + /// \param[in] forcedAddress The systmeAddress to return in queries. Use UNASSIGNED_SYSTEM_ADDRESS (default) to use what RakPeer returns + void ForceExternalSystemAddress(SystemAddress forcedAddress); + + /// \brief Adds a callback called on each query. If all filters returns true for an operation, the operation is allowed. + /// If the filter was already added, the function silently fails + /// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters() + void AddQueryFilter(CloudServerQueryFilter* filter); + + /// \brief Removes a callback added with AddQueryFilter() + /// The instance is not deleted, only unreferenced. It is up to the user to delete the instance, if necessary + /// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters() + void RemoveQueryFilter(CloudServerQueryFilter* filter); + + /// \brief Removes all instances of CloudServerQueryFilter added with AddQueryFilter(). + /// The instances are not deleted, only unreferenced. It is up to the user to delete the instances, if necessary + void RemoveAllQueryFilters(void); + +protected: + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnRakPeerShutdown(void); + + + virtual void OnPostRequest(Packet *packet); + virtual void OnReleaseRequest(Packet *packet); + virtual void OnGetRequest(Packet *packet); + virtual void OnUnsubscribeRequest(Packet *packet); + virtual void OnServerToServerGetRequest(Packet *packet); + virtual void OnServerToServerGetResponse(Packet *packet); + + uint64_t maxUploadBytesPerClient, maxBytesPerDowload; + + // ---------------------------------------------------------------------------- + // For a given data key, quickly look up one or all systems that have uploaded + // ---------------------------------------------------------------------------- + struct CloudData + { + CloudData() {} + ~CloudData() {if (allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_);} + bool IsUnused(void) const {return isUploaded==false && specificSubscribers.Size()==0;} + void Clear(void) {if (dataPtr==allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_); allocatedData=0; dataPtr=0; dataLengthBytes=0; isUploaded=false;} + + unsigned char stackData[CLOUD_SERVER_DATA_STACK_SIZE]; + unsigned char *allocatedData; // Uses allocatedData instead of stackData if length of data exceeds CLOUD_SERVER_DATA_STACK_SIZE + unsigned char *dataPtr; // Points to either stackData or allocatedData + uint32_t dataLengthBytes; + bool isUploaded; + + /// System address of server that is holding this data, and the client is connected to + SystemAddress serverSystemAddress; + + /// System address of client that uploaded this data + SystemAddress clientSystemAddress; + + /// RakNetGUID of server that is holding this data, and the client is connected to + RakNetGUID serverGUID; + + /// RakNetGUID of client that uploaded this data + RakNetGUID clientGUID; + + /// When the key data changes from this particular system, notify these subscribers + /// This list mutually exclusive with CloudDataList::nonSpecificSubscribers + DataStructures::OrderedList specificSubscribers; + }; + void WriteCloudQueryRowFromResultList(unsigned int i, DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut); + void WriteCloudQueryRowFromResultList(DataStructures::List &cloudDataResultList, DataStructures::List &cloudKeyResultList, BitStream *bsOut); + + static int KeyDataPtrComp( const RakNetGUID &key, CloudData* const &data ); + struct CloudDataList + { + bool IsUnused(void) const {return keyData.Size()==0 && nonSpecificSubscribers.Size()==0;} + bool IsNotUploaded(void) const {return uploaderCount==0;} + bool RemoveSubscriber(RakNetGUID g) { + bool objectExists; + unsigned int index; + index = nonSpecificSubscribers.GetIndexFromKey(g, &objectExists); + if (objectExists) + { + subscriberCount--; + nonSpecificSubscribers.RemoveAtIndex(index); + return true; + } + return false; + } + + unsigned int uploaderCount, subscriberCount; + CloudKey key; + + // Data uploaded from or subscribed to for various systems + DataStructures::OrderedList keyData; + + /// When the key data changes from any system, notify these subscribers + /// This list mutually exclusive with CloudData::specificSubscribers + DataStructures::OrderedList nonSpecificSubscribers; + }; + + static int KeyDataListComp( const CloudKey &key, CloudDataList * const &data ); + DataStructures::OrderedList dataRepository; + + struct KeySubscriberID + { + CloudKey key; + DataStructures::OrderedList specificSystemsSubscribedTo; + }; + static int KeySubscriberIDComp(const CloudKey &key, KeySubscriberID * const &data ); + + // Remote systems + struct RemoteCloudClient + { + bool IsUnused(void) const {return uploadedKeys.Size()==0 && subscribedKeys.Size()==0;} + + DataStructures::OrderedList uploadedKeys; + DataStructures::OrderedList subscribedKeys; + uint64_t uploadedBytes; + }; + DataStructures::Hash remoteSystems; + + // For a given user, release all subscribed and uploaded keys + void ReleaseSystem(RakNetGUID clientAddress ); + + // For a given user, release a set of keys + void ReleaseKeys(RakNetGUID clientAddress, DataStructures::List &keys ); + + void NotifyClientSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, DataStructures::OrderedList &subscribers, bool wasUpdated ); + void NotifyClientSubscribersOfDataChange( CloudQueryRow *row, DataStructures::OrderedList &subscribers, bool wasUpdated ); + void NotifyServerSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, bool wasUpdated ); + + struct RemoteServer + { + RakNetGUID serverAddress; + // This server needs to know about these keys when they are updated or deleted + DataStructures::OrderedList subscribedKeys; + // This server has uploaded these keys, and needs to know about Get() requests + DataStructures::OrderedList uploadedKeys; + + // Just for processing + bool workingFlag; + + // If false, we don't know what keys they have yet, so send everything + bool gotSubscribedAndUploadedKeys; + }; + + static int RemoteServerComp(const RakNetGUID &key, RemoteServer* const &data ); + DataStructures::OrderedList remoteServers; + + struct BufferedGetResponseFromServer + { + void Clear(CloudAllocator *allocator); + + RakNetGUID serverAddress; + CloudQueryResult queryResult; + bool gotResult; + }; + + struct CloudQueryWithAddresses + { + // Inputs + CloudQuery cloudQuery; + DataStructures::List specificSystems; + + void Serialize(bool writeToBitstream, BitStream *bitStream); + }; + + static int BufferedGetResponseFromServerComp(const RakNetGUID &key, BufferedGetResponseFromServer* const &data ); + struct GetRequest + { + void Clear(CloudAllocator *allocator); + bool AllRemoteServersHaveResponded(void) const; + CloudQueryWithAddresses cloudQueryWithAddresses; + + // When request started. If takes too long for a response from another system, can abort remaining systems + RakNet::Time requestStartTime; + + // Assigned by server that gets the request to identify response. See nextGetRequestId + uint32_t requestId; + + RakNetGUID requestingClient; + + DataStructures::OrderedList remoteServerResponses; + }; + static int GetRequestComp(const uint32_t &key, GetRequest* const &data ); + DataStructures::OrderedList getRequests; + RakNet::Time nextGetRequestsCheck; + + uint32_t nextGetRequestId; + + void ProcessAndTransmitGetRequest(GetRequest *getRequest); + + void ProcessCloudQueryWithAddresses( + CloudServer::CloudQueryWithAddresses &cloudQueryWithAddresses, + DataStructures::List &cloudDataResultList, + DataStructures::List &cloudKeyResultList + ); + + void SendUploadedAndSubscribedKeysToServer( RakNetGUID systemAddress ); + void SendUploadedKeyToServers( CloudKey &cloudKey ); + void SendSubscribedKeyToServers( CloudKey &cloudKey ); + void RemoveUploadedKeyFromServers( CloudKey &cloudKey ); + void RemoveSubscribedKeyFromServers( CloudKey &cloudKey ); + + void OnSendUploadedAndSubscribedKeysToServer( Packet *packet ); + void OnSendUploadedKeyToServers( Packet *packet ); + void OnSendSubscribedKeyToServers( Packet *packet ); + void OnRemoveUploadedKeyFromServers( Packet *packet ); + void OnRemoveSubscribedKeyFromServers( Packet *packet ); + void OnServerDataChanged( Packet *packet ); + + void GetServersWithUploadedKeys( + DataStructures::List &keys, + DataStructures::List &remoteServersWithData + ); + + CloudServer::CloudDataList *GetOrAllocateCloudDataList(CloudKey key, bool *dataRepositoryExists, unsigned int &dataRepositoryIndex); + + void UnsubscribeFromKey(RemoteCloudClient *remoteCloudClient, RakNetGUID remoteCloudClientGuid, unsigned int keySubscriberIndex, CloudKey &cloudKey, DataStructures::List &specificSystems); + void RemoveSpecificSubscriber(RakNetGUID specificSubscriber, CloudDataList *cloudDataList, RakNetGUID remoteCloudClientGuid); + + DataStructures::List queryFilters; + + SystemAddress forceAddress; +}; + + +} // namespace RakNet + +#endif + + +// Key subscription +// +// A given system can subscribe to one or more keys. +// The subscription can be further be defined as only subscribing to keys uploaded by or changed by a given system. +// It is possible to subscribe to keys not yet uploaded, or uploaded to another system +// +// Operations: +// +// 1. SubscribeToKey() - Get() operation with subscription +// A. Add to key subscription list for the client, which contains a keyId / specificUploaderList pair +// B. Send to remote servers that for this key, they should send us updates +// C. (Done, get operation returns current values) +// +// 2. UpdateData() - Post() operation +// A. Find all subscribers to this data, for the uploading system. +// B. Send them the uploaded data +// C. Find all servers that subscribe to this data +// D. Send them the uploaded data +// +// 3. DeleteData() - Release() operation +// A. Find all subscribers to this data, for the deleting system. +// B. Inform them of the deletion +// C. Find all servers that subscribe to this data +// D. Inform them of the deletion +// +// 4. Unsubscribe() +// A. Find this subscriber, and remove their subscription +// B. If no one else is subscribing to this key for any system, notify remote servers we no longer need subscription updates +// +// Internal operations: +// +// 1. Find if any connected client has subscribed to a given key +// A. This is used add and remove our subscription for this key to remote servers +// +// 2. For a given key and updating address, find all connected clients that care +// A. First find connected clients that have subscribed to this key, regardless of address +// B. Then find connected clients that have subscribed to this key for this particular address +// +// 3. Find all remote servers that have subscribed to a given key +// A. This is so when the key is updated or deleted, we know who to send it to +// +// 4. For a given client (such as on disconnect), remove all records of their subscriptions + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/CommandParserInterface.cpp b/src/raknet/CommandParserInterface.cpp index 33d6695..d61b41c 100755 --- a/src/raknet/CommandParserInterface.cpp +++ b/src/raknet/CommandParserInterface.cpp @@ -1,161 +1,161 @@ -#include "CommandParserInterface.h" -#include "TransportInterface.h" -#include -#include "RakAssert.h" -#include - - -#if defined(_WIN32) -// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib -// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly -#include - -#else -#include -#include -#include -#endif - -#include "LinuxStrings.h" - -using namespace RakNet; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -const unsigned char CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS=255; - -int RakNet::RegisteredCommandComp( const char* const & key, const RegisteredCommand &data ) -{ - return _stricmp(key,data.command); -} - -CommandParserInterface::CommandParserInterface() {} -CommandParserInterface::~CommandParserInterface() {} - -void CommandParserInterface::ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength) -{ - unsigned strIndex, parameterListIndex; - unsigned strLen; - bool replaceDelineator=true; - - strLen = (unsigned) strlen(str); - - // Replace every instance of delineator, \n, \r with 0 - for (strIndex=0; strIndex < strLen; strIndex++) - { - if (str[strIndex]==delineator && replaceDelineator) - str[strIndex]=0; - - if (str[strIndex]=='\n' || str[strIndex]=='\r') - str[strIndex]=0; - - if (str[strIndex]==delineatorToggle) - { - str[strIndex]=0; - replaceDelineator=!replaceDelineator; - } - } - - // Fill up parameterList starting at each non-0 - for (strIndex=0, parameterListIndex=0; strIndex < strLen; ) - { - if (str[strIndex]!=0) - { - parameterList[parameterListIndex]=str+strIndex; - parameterListIndex++; - RakAssert(parameterListIndex < parameterListLength); - if (parameterListIndex >= parameterListLength) - break; - - strIndex++; - while (str[strIndex]!=0 && strIndex < strLen) - strIndex++; - } - else - strIndex++; - } - - parameterList[parameterListIndex]=0; - *numParameters=parameterListIndex; -} -void CommandParserInterface::SendCommandList(TransportInterface *transport, const SystemAddress &systemAddress) -{ - unsigned i; - if (commandList.Size()) - { - for (i=0; i < commandList.Size(); i++) - { - transport->Send(systemAddress, "%s", commandList[i].command); - if (i < commandList.Size()-1) - transport->Send(systemAddress, ", "); - } - transport->Send(systemAddress, "\r\n"); - } - else - transport->Send(systemAddress, "No registered commands\r\n"); -} -void CommandParserInterface::RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp) -{ - RegisteredCommand rc; - rc.command=command; - rc.commandHelp=commandHelp; - rc.parameterCount=parameterCount; - commandList.Insert( command, rc, true, _FILE_AND_LINE_); -} -bool CommandParserInterface::GetRegisteredCommand(const char *command, RegisteredCommand *rc) -{ - bool objectExists; - unsigned index; - index=commandList.GetIndexFromKey(command, &objectExists); - if (objectExists) - *rc=commandList[index]; - return objectExists; -} -void CommandParserInterface::OnTransportChange(TransportInterface *transport) -{ - (void) transport; -} -void CommandParserInterface::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport) -{ - (void) systemAddress; - (void) transport; -} -void CommandParserInterface::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport) -{ - (void) systemAddress; - (void) transport; -} -void CommandParserInterface::ReturnResult(bool res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress) -{ - if (res) - transport->Send(systemAddress, "%s returned true.\r\n", command); - else - transport->Send(systemAddress, "%s returned false.\r\n", command); -} -void CommandParserInterface::ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress) -{ - transport->Send(systemAddress, "%s returned %i.\r\n", command, res); -} -void CommandParserInterface::ReturnResult(const char *command, TransportInterface *transport, const SystemAddress &systemAddress) -{ - transport->Send(systemAddress, "Successfully called %s.\r\n", command); -} -void CommandParserInterface::ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress) -{ - transport->Send(systemAddress, "%s returned %s.\r\n", command, res); -} -void CommandParserInterface::ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress) -{ - char addr[128]; - systemAddress.ToString(false,addr); - char addr2[128]; - res.ToString(false,addr2); - transport->Send(systemAddress, "%s returned %s %s:%i\r\n", command,addr,addr2,res.GetPort()); -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - +#include "CommandParserInterface.h" +#include "TransportInterface.h" +#include +#include "RakAssert.h" +#include + + +#if defined(_WIN32) +// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib +// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly +#include + +#else +#include +#include +#include +#endif + +#include "LinuxStrings.h" + +using namespace RakNet; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +const unsigned char CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS=255; + +int RakNet::RegisteredCommandComp( const char* const & key, const RegisteredCommand &data ) +{ + return _stricmp(key,data.command); +} + +CommandParserInterface::CommandParserInterface() {} +CommandParserInterface::~CommandParserInterface() {} + +void CommandParserInterface::ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength) +{ + unsigned strIndex, parameterListIndex; + unsigned strLen; + bool replaceDelineator=true; + + strLen = (unsigned) strlen(str); + + // Replace every instance of delineator, \n, \r with 0 + for (strIndex=0; strIndex < strLen; strIndex++) + { + if (str[strIndex]==delineator && replaceDelineator) + str[strIndex]=0; + + if (str[strIndex]=='\n' || str[strIndex]=='\r') + str[strIndex]=0; + + if (str[strIndex]==delineatorToggle) + { + str[strIndex]=0; + replaceDelineator=!replaceDelineator; + } + } + + // Fill up parameterList starting at each non-0 + for (strIndex=0, parameterListIndex=0; strIndex < strLen; ) + { + if (str[strIndex]!=0) + { + parameterList[parameterListIndex]=str+strIndex; + parameterListIndex++; + RakAssert(parameterListIndex < parameterListLength); + if (parameterListIndex >= parameterListLength) + break; + + strIndex++; + while (str[strIndex]!=0 && strIndex < strLen) + strIndex++; + } + else + strIndex++; + } + + parameterList[parameterListIndex]=0; + *numParameters=parameterListIndex; +} +void CommandParserInterface::SendCommandList(TransportInterface *transport, const SystemAddress &systemAddress) +{ + unsigned i; + if (commandList.Size()) + { + for (i=0; i < commandList.Size(); i++) + { + transport->Send(systemAddress, "%s", commandList[i].command); + if (i < commandList.Size()-1) + transport->Send(systemAddress, ", "); + } + transport->Send(systemAddress, "\r\n"); + } + else + transport->Send(systemAddress, "No registered commands\r\n"); +} +void CommandParserInterface::RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp) +{ + RegisteredCommand rc; + rc.command=command; + rc.commandHelp=commandHelp; + rc.parameterCount=parameterCount; + commandList.Insert( command, rc, true, _FILE_AND_LINE_); +} +bool CommandParserInterface::GetRegisteredCommand(const char *command, RegisteredCommand *rc) +{ + bool objectExists; + unsigned index; + index=commandList.GetIndexFromKey(command, &objectExists); + if (objectExists) + *rc=commandList[index]; + return objectExists; +} +void CommandParserInterface::OnTransportChange(TransportInterface *transport) +{ + (void) transport; +} +void CommandParserInterface::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport) +{ + (void) systemAddress; + (void) transport; +} +void CommandParserInterface::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport) +{ + (void) systemAddress; + (void) transport; +} +void CommandParserInterface::ReturnResult(bool res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress) +{ + if (res) + transport->Send(systemAddress, "%s returned true.\r\n", command); + else + transport->Send(systemAddress, "%s returned false.\r\n", command); +} +void CommandParserInterface::ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress) +{ + transport->Send(systemAddress, "%s returned %i.\r\n", command, res); +} +void CommandParserInterface::ReturnResult(const char *command, TransportInterface *transport, const SystemAddress &systemAddress) +{ + transport->Send(systemAddress, "Successfully called %s.\r\n", command); +} +void CommandParserInterface::ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress) +{ + transport->Send(systemAddress, "%s returned %s.\r\n", command, res); +} +void CommandParserInterface::ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress) +{ + char addr[128]; + systemAddress.ToString(false,addr); + char addr2[128]; + res.ToString(false,addr2); + transport->Send(systemAddress, "%s returned %s %s:%i\r\n", command,addr,addr2,res.GetPort()); +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + diff --git a/src/raknet/CommandParserInterface.h b/src/raknet/CommandParserInterface.h index 7a9cc5b..f795d54 100755 --- a/src/raknet/CommandParserInterface.h +++ b/src/raknet/CommandParserInterface.h @@ -1,140 +1,140 @@ -/// \file CommandParserInterface.h -/// \brief Contains CommandParserInterface , from which you derive custom command parsers -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __COMMAND_PARSER_INTERFACE -#define __COMMAND_PARSER_INTERFACE - -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "DS_OrderedList.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class TransportInterface; - -/// \internal -/// Contains the information related to one command registered with RegisterCommand() -/// Implemented so I can have an automatic help system via SendCommandList() -struct RAK_DLL_EXPORT RegisteredCommand -{ - const char *command; - const char *commandHelp; - unsigned char parameterCount; -}; - -/// List of commands registered with RegisterCommand() -int RAK_DLL_EXPORT RegisteredCommandComp( const char* const & key, const RegisteredCommand &data ); - -/// \brief The interface used by command parsers. -/// \details CommandParserInterface provides a set of functions and interfaces that plug into the ConsoleServer class. -/// Each CommandParserInterface works at the same time as other interfaces in the system. -class RAK_DLL_EXPORT CommandParserInterface -{ -public: - CommandParserInterface(); - virtual ~CommandParserInterface(); - - /// You are responsible for overriding this function and returning a static string, which will identifier your parser. - /// This should return a static string - /// \return The name that you return. - virtual const char *GetName(void) const=0; - - /// \brief A callback for when \a systemAddress has connected to us. - /// \param[in] systemAddress The player that has connected. - /// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players. - virtual void OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport); - - /// \brief A callback for when \a systemAddress has disconnected, either gracefully or forcefully - /// \param[in] systemAddress The player that has disconnected. - /// \param[in] transport The transport interface that sent us this information. - virtual void OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport); - - /// \brief A callback for when you are expected to send a brief description of your parser to \a systemAddress - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player that requested help. - virtual void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress)=0; - - /// \brief Given \a command with parameters \a parameterList , do whatever processing you wish. - /// \param[in] command The command to process - /// \param[in] numParameters How many parameters were passed along with the command - /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player that sent this command. - /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing - virtual bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString)=0; - - /// \brief This is called every time transport interface is registered. - /// \details If you want to save a copy of the TransportInterface pointer - /// This is the place to do it - /// \param[in] transport The new TransportInterface - virtual void OnTransportChange(TransportInterface *transport); - - /// \internal - /// Scan commandList and return the associated array - /// \param[in] command The string to find - /// \param[out] rc Contains the result of this operation - /// \return True if we found the command, false otherwise - virtual bool GetRegisteredCommand(const char *command, RegisteredCommand *rc); - - /// \internal - /// Goes through str, replacing the delineating character with 0's. - /// \param[in] str The string sent by the transport interface - /// \param[in] delineator The character to scan for to use as a delineator - /// \param[in] delineatorToggle When encountered the delineator replacement is toggled on and off - /// \param[out] numParameters How many pointers were written to \a parameterList - /// \param[out] parameterList An array of pointers to characters. Will hold pointers to locations inside \a str - /// \param[in] parameterListLength How big the \a parameterList array is - static void ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength); - - /// \internal - /// Goes through the variable commandList and sends the command portion of each struct - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player to write to - virtual void SendCommandList(TransportInterface *transport, const SystemAddress &systemAddress); - - static const unsigned char VARIABLE_NUMBER_OF_PARAMETERS; - - // Currently only takes static strings - doesn't make a copy of what you pass. - // parameterCount is the number of parameters that the sender has to include with the command. - // Pass 255 to parameterCount to indicate variable number of parameters - - /// Registers a command. - /// \param[in] parameterCount How many parameters your command requires. If you want to accept a variable number of commands, pass CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS - /// \param[in] command A pointer to a STATIC string that has your command. I keep a copy of the pointer here so don't deallocate the string. - /// \param[in] commandHelp A pointer to a STATIC string that has the help information for your command. I keep a copy of the pointer here so don't deallocate the string. - virtual void RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp); - - /// \brief Just writes a string to the remote system based on the result ( \a res ) of your operation - /// \details This is not necessary to call, but makes it easier to return results of function calls. - /// \param[in] res The result to write - /// \param[in] command The command that this result came from - /// \param[in] transport The transport interface that will be written to - /// \param[in] systemAddress The player this result will be sent to - virtual void ReturnResult(bool res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); - virtual void ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); - virtual void ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); - virtual void ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress); - - /// \brief Just writes a string to the remote system when you are calling a function that has no return value. - /// \details This is not necessary to call, but makes it easier to return results of function calls. - /// \param[in] res The result to write - /// \param[in] command The command that this result came from - /// \param[in] transport The transport interface that will be written to - /// \param[in] systemAddress The player this result will be sent to - virtual void ReturnResult(const char *command,TransportInterface *transport, const SystemAddress &systemAddress); - -protected: - DataStructures::OrderedList commandList; -}; - -} // namespace RakNet - -#endif - +/// \file CommandParserInterface.h +/// \brief Contains CommandParserInterface , from which you derive custom command parsers +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __COMMAND_PARSER_INTERFACE +#define __COMMAND_PARSER_INTERFACE + +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "DS_OrderedList.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class TransportInterface; + +/// \internal +/// Contains the information related to one command registered with RegisterCommand() +/// Implemented so I can have an automatic help system via SendCommandList() +struct RAK_DLL_EXPORT RegisteredCommand +{ + const char *command; + const char *commandHelp; + unsigned char parameterCount; +}; + +/// List of commands registered with RegisterCommand() +int RAK_DLL_EXPORT RegisteredCommandComp( const char* const & key, const RegisteredCommand &data ); + +/// \brief The interface used by command parsers. +/// \details CommandParserInterface provides a set of functions and interfaces that plug into the ConsoleServer class. +/// Each CommandParserInterface works at the same time as other interfaces in the system. +class RAK_DLL_EXPORT CommandParserInterface +{ +public: + CommandParserInterface(); + virtual ~CommandParserInterface(); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + virtual const char *GetName(void) const=0; + + /// \brief A callback for when \a systemAddress has connected to us. + /// \param[in] systemAddress The player that has connected. + /// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players. + virtual void OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport); + + /// \brief A callback for when \a systemAddress has disconnected, either gracefully or forcefully + /// \param[in] systemAddress The player that has disconnected. + /// \param[in] transport The transport interface that sent us this information. + virtual void OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport); + + /// \brief A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + virtual void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress)=0; + + /// \brief Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + virtual bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString)=0; + + /// \brief This is called every time transport interface is registered. + /// \details If you want to save a copy of the TransportInterface pointer + /// This is the place to do it + /// \param[in] transport The new TransportInterface + virtual void OnTransportChange(TransportInterface *transport); + + /// \internal + /// Scan commandList and return the associated array + /// \param[in] command The string to find + /// \param[out] rc Contains the result of this operation + /// \return True if we found the command, false otherwise + virtual bool GetRegisteredCommand(const char *command, RegisteredCommand *rc); + + /// \internal + /// Goes through str, replacing the delineating character with 0's. + /// \param[in] str The string sent by the transport interface + /// \param[in] delineator The character to scan for to use as a delineator + /// \param[in] delineatorToggle When encountered the delineator replacement is toggled on and off + /// \param[out] numParameters How many pointers were written to \a parameterList + /// \param[out] parameterList An array of pointers to characters. Will hold pointers to locations inside \a str + /// \param[in] parameterListLength How big the \a parameterList array is + static void ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength); + + /// \internal + /// Goes through the variable commandList and sends the command portion of each struct + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player to write to + virtual void SendCommandList(TransportInterface *transport, const SystemAddress &systemAddress); + + static const unsigned char VARIABLE_NUMBER_OF_PARAMETERS; + + // Currently only takes static strings - doesn't make a copy of what you pass. + // parameterCount is the number of parameters that the sender has to include with the command. + // Pass 255 to parameterCount to indicate variable number of parameters + + /// Registers a command. + /// \param[in] parameterCount How many parameters your command requires. If you want to accept a variable number of commands, pass CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS + /// \param[in] command A pointer to a STATIC string that has your command. I keep a copy of the pointer here so don't deallocate the string. + /// \param[in] commandHelp A pointer to a STATIC string that has the help information for your command. I keep a copy of the pointer here so don't deallocate the string. + virtual void RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp); + + /// \brief Just writes a string to the remote system based on the result ( \a res ) of your operation + /// \details This is not necessary to call, but makes it easier to return results of function calls. + /// \param[in] res The result to write + /// \param[in] command The command that this result came from + /// \param[in] transport The transport interface that will be written to + /// \param[in] systemAddress The player this result will be sent to + virtual void ReturnResult(bool res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); + virtual void ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); + virtual void ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress); + virtual void ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress); + + /// \brief Just writes a string to the remote system when you are calling a function that has no return value. + /// \details This is not necessary to call, but makes it easier to return results of function calls. + /// \param[in] res The result to write + /// \param[in] command The command that this result came from + /// \param[in] transport The transport interface that will be written to + /// \param[in] systemAddress The player this result will be sent to + virtual void ReturnResult(const char *command,TransportInterface *transport, const SystemAddress &systemAddress); + +protected: + DataStructures::OrderedList commandList; +}; + +} // namespace RakNet + +#endif + diff --git a/src/raknet/ConnectionGraph2.cpp b/src/raknet/ConnectionGraph2.cpp index 683c28f..5950246 100755 --- a/src/raknet/ConnectionGraph2.cpp +++ b/src/raknet/ConnectionGraph2.cpp @@ -1,299 +1,299 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ConnectionGraph2==1 - -#include "ConnectionGraph2.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(ConnectionGraph2,ConnectionGraph2); - -int RakNet::ConnectionGraph2::RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data ) -{ - if (key < data->guid) - return -1; - if (key > data->guid) - return 1; - return 0; -} - -int RakNet::ConnectionGraph2::SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data ) -{ - if (key.guiddata.guid) - return 1; - return 0; -} -ConnectionGraph2::ConnectionGraph2() -{ - autoProcessNewConnections=true; -} -ConnectionGraph2::~ConnectionGraph2() -{ - -} -bool ConnectionGraph2::GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength) -{ - if ((saOut==0 && guidOut==0) || outLength==0 || *outLength==0 || remoteSystemGuid==UNASSIGNED_RAKNET_GUID) - { - *outLength=0; - return false; - } - - bool objectExists; - unsigned int idx = remoteSystems.GetIndexFromKey(remoteSystemGuid, &objectExists); - if (objectExists==false) - { - *outLength=0; - return false; - } - - unsigned int idx2; - if (remoteSystems[idx]->remoteConnections.Size() < *outLength) - *outLength=remoteSystems[idx]->remoteConnections.Size(); - for (idx2=0; idx2 < *outLength; idx2++) - { - if (guidOut) - guidOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].guid; - if (saOut) - saOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].systemAddress; - } - return true; -} -bool ConnectionGraph2::ConnectionExists(RakNetGUID g1, RakNetGUID g2) -{ - if (g1==g2) - return false; - - bool objectExists; - unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists); - if (objectExists==false) - { - return false; - } - SystemAddressAndGuid sag; - sag.guid=g2; - return remoteSystems[idx]->remoteConnections.HasData(sag); -} -uint16_t ConnectionGraph2::GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const -{ - if (g1==g2) - return 0; - - if (g1==rakPeerInterface->GetMyGUID()) - return (uint16_t) rakPeerInterface->GetAveragePing(g2); - if (g2==rakPeerInterface->GetMyGUID()) - return (uint16_t) rakPeerInterface->GetAveragePing(g1); - - bool objectExists; - unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists); - if (objectExists==false) - { - return (uint16_t) -1; - } - - SystemAddressAndGuid sag; - sag.guid=g2; - unsigned int idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(sag, &objectExists); - if (objectExists==false) - { - return (uint16_t) -1; - } - return remoteSystems[idx]->remoteConnections[idx2].sendersPingToThatSystem; -} - -/// Returns the system with the lowest total ping among all its connections. This can be used as the 'best host' for a peer to peer session -RakNetGUID ConnectionGraph2::GetLowestAveragePingSystem(void) const -{ - float lowestPing=-1.0; - unsigned int lowestPingIdx=(unsigned int) -1; - float thisAvePing=0.0f; - unsigned int idx, idx2; - int ap, count=0; - - for (idx=0; idxGetAveragePing(remoteSystems[idx]->guid); - if (ap!=-1) - { - thisAvePing+=(float) ap; - count++; - } - } - - if (count>0) - { - lowestPing=thisAvePing/count; - } - - for (idx=0; idxremoteConnections.Size(); idx2++) - { - ap=remoteSystem->remoteConnections[idx2].sendersPingToThatSystem; - if (ap!=-1) - { - thisAvePing+=(float) ap; - count++; - } - } - - if (count>0 && (lowestPing==-1.0f || thisAvePing/count < lowestPing)) - { - lowestPing=thisAvePing/count; - lowestPingIdx=idx; - } - } - - if (lowestPingIdx==(unsigned int) -1) - return rakPeerInterface->GetMyGUID(); - return remoteSystems[lowestPingIdx]->guid; -} - -void ConnectionGraph2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - // Send notice to all existing connections - RakNet::BitStream bs; - if (lostConnectionReason==LCR_CONNECTION_LOST) - bs.Write((MessageID)ID_REMOTE_CONNECTION_LOST); - else - bs.Write((MessageID)ID_REMOTE_DISCONNECTION_NOTIFICATION); - bs.Write(systemAddress); - bs.Write(rakNetGUID); - SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); - - bool objectExists; - unsigned int idx = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists) - { - RakNet::OP_DELETE(remoteSystems[idx],_FILE_AND_LINE_); - remoteSystems.RemoveAtIndex(idx); - } -} -void ConnectionGraph2::SetAutoProcessNewConnections(bool b) -{ - autoProcessNewConnections=b; -} -bool ConnectionGraph2::GetAutoProcessNewConnections(void) const -{ - return autoProcessNewConnections; -} -void ConnectionGraph2::AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID) -{ - // Relay the new connection to other systems. - RakNet::BitStream bs; - bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); - bs.Write((uint32_t)1); - bs.Write(systemAddress); - bs.Write(rakNetGUID); - bs.WriteCasted(rakPeerInterface->GetAveragePing(rakNetGUID)); - SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); - - // Send everyone to the new guy - DataStructures::List addresses; - DataStructures::List guids; - rakPeerInterface->GetSystemList(addresses, guids); - bs.Reset(); - bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); - BitSize_t writeOffset = bs.GetWriteOffset(); - bs.Write((uint32_t) addresses.Size()); - - unsigned int i; - uint32_t count=0; - for (i=0; i < addresses.Size(); i++) - { - if (addresses[i]==systemAddress) - continue; - - bs.Write(addresses[i]); - bs.Write(guids[i]); - bs.WriteCasted(rakPeerInterface->GetAveragePing(guids[i])); - count++; - } - - if (count>0) - { - BitSize_t writeOffset2 = bs.GetWriteOffset(); - bs.SetWriteOffset(writeOffset); - bs.Write(count); - bs.SetWriteOffset(writeOffset2); - SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false); - } - - bool objectExists; - unsigned int ii = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists==false) - { - RemoteSystem* remoteSystem = RakNet::OP_NEW(_FILE_AND_LINE_); - remoteSystem->guid=rakNetGUID; - remoteSystems.InsertAtIndex(remoteSystem,ii,_FILE_AND_LINE_); - } -} -void ConnectionGraph2::GetParticipantList(DataStructures::OrderedList &participantList) -{ - participantList.Clear(true, _FILE_AND_LINE_); - unsigned int i; - for (i=0; i < remoteSystems.Size(); i++) - participantList.InsertAtEnd(remoteSystems[i]->guid, _FILE_AND_LINE_); -} -void ConnectionGraph2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) isIncoming; - if (autoProcessNewConnections) - AddParticipant(systemAddress, rakNetGUID); -} -PluginReceiveResult ConnectionGraph2::OnReceive(Packet *packet) -{ - if (packet->data[0]==ID_REMOTE_CONNECTION_LOST || packet->data[0]==ID_REMOTE_DISCONNECTION_NOTIFICATION) - { - bool objectExists; - unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists) - { - RakNet::BitStream bs(packet->data,packet->length,false); - bs.IgnoreBytes(1); - SystemAddressAndGuid saag; - bs.Read(saag.systemAddress); - bs.Read(saag.guid); - unsigned long idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists); - if (objectExists) - remoteSystems[idx]->remoteConnections.RemoveAtIndex(idx2); - } - } - else if (packet->data[0]==ID_REMOTE_NEW_INCOMING_CONNECTION) - { - bool objectExists; - unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists) - { - uint32_t numAddresses; - RakNet::BitStream bs(packet->data,packet->length,false); - bs.IgnoreBytes(1); - bs.Read(numAddresses); - for (unsigned int idx2=0; idx2 < numAddresses; idx2++) - { - SystemAddressAndGuid saag; - bs.Read(saag.systemAddress); - bs.Read(saag.guid); - bs.Read(saag.sendersPingToThatSystem); - bool objectExists; - unsigned int ii = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists); - if (objectExists==false) - remoteSystems[idx]->remoteConnections.InsertAtIndex(saag,ii,_FILE_AND_LINE_); - } - } - } - - return RR_CONTINUE_PROCESSING; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ConnectionGraph2==1 + +#include "ConnectionGraph2.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(ConnectionGraph2,ConnectionGraph2); + +int RakNet::ConnectionGraph2::RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data ) +{ + if (key < data->guid) + return -1; + if (key > data->guid) + return 1; + return 0; +} + +int RakNet::ConnectionGraph2::SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data ) +{ + if (key.guiddata.guid) + return 1; + return 0; +} +ConnectionGraph2::ConnectionGraph2() +{ + autoProcessNewConnections=true; +} +ConnectionGraph2::~ConnectionGraph2() +{ + +} +bool ConnectionGraph2::GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength) +{ + if ((saOut==0 && guidOut==0) || outLength==0 || *outLength==0 || remoteSystemGuid==UNASSIGNED_RAKNET_GUID) + { + *outLength=0; + return false; + } + + bool objectExists; + unsigned int idx = remoteSystems.GetIndexFromKey(remoteSystemGuid, &objectExists); + if (objectExists==false) + { + *outLength=0; + return false; + } + + unsigned int idx2; + if (remoteSystems[idx]->remoteConnections.Size() < *outLength) + *outLength=remoteSystems[idx]->remoteConnections.Size(); + for (idx2=0; idx2 < *outLength; idx2++) + { + if (guidOut) + guidOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].guid; + if (saOut) + saOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].systemAddress; + } + return true; +} +bool ConnectionGraph2::ConnectionExists(RakNetGUID g1, RakNetGUID g2) +{ + if (g1==g2) + return false; + + bool objectExists; + unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists); + if (objectExists==false) + { + return false; + } + SystemAddressAndGuid sag; + sag.guid=g2; + return remoteSystems[idx]->remoteConnections.HasData(sag); +} +uint16_t ConnectionGraph2::GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const +{ + if (g1==g2) + return 0; + + if (g1==rakPeerInterface->GetMyGUID()) + return (uint16_t) rakPeerInterface->GetAveragePing(g2); + if (g2==rakPeerInterface->GetMyGUID()) + return (uint16_t) rakPeerInterface->GetAveragePing(g1); + + bool objectExists; + unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists); + if (objectExists==false) + { + return (uint16_t) -1; + } + + SystemAddressAndGuid sag; + sag.guid=g2; + unsigned int idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(sag, &objectExists); + if (objectExists==false) + { + return (uint16_t) -1; + } + return remoteSystems[idx]->remoteConnections[idx2].sendersPingToThatSystem; +} + +/// Returns the system with the lowest total ping among all its connections. This can be used as the 'best host' for a peer to peer session +RakNetGUID ConnectionGraph2::GetLowestAveragePingSystem(void) const +{ + float lowestPing=-1.0; + unsigned int lowestPingIdx=(unsigned int) -1; + float thisAvePing=0.0f; + unsigned int idx, idx2; + int ap, count=0; + + for (idx=0; idxGetAveragePing(remoteSystems[idx]->guid); + if (ap!=-1) + { + thisAvePing+=(float) ap; + count++; + } + } + + if (count>0) + { + lowestPing=thisAvePing/count; + } + + for (idx=0; idxremoteConnections.Size(); idx2++) + { + ap=remoteSystem->remoteConnections[idx2].sendersPingToThatSystem; + if (ap!=-1) + { + thisAvePing+=(float) ap; + count++; + } + } + + if (count>0 && (lowestPing==-1.0f || thisAvePing/count < lowestPing)) + { + lowestPing=thisAvePing/count; + lowestPingIdx=idx; + } + } + + if (lowestPingIdx==(unsigned int) -1) + return rakPeerInterface->GetMyGUID(); + return remoteSystems[lowestPingIdx]->guid; +} + +void ConnectionGraph2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + // Send notice to all existing connections + RakNet::BitStream bs; + if (lostConnectionReason==LCR_CONNECTION_LOST) + bs.Write((MessageID)ID_REMOTE_CONNECTION_LOST); + else + bs.Write((MessageID)ID_REMOTE_DISCONNECTION_NOTIFICATION); + bs.Write(systemAddress); + bs.Write(rakNetGUID); + SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); + + bool objectExists; + unsigned int idx = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists) + { + RakNet::OP_DELETE(remoteSystems[idx],_FILE_AND_LINE_); + remoteSystems.RemoveAtIndex(idx); + } +} +void ConnectionGraph2::SetAutoProcessNewConnections(bool b) +{ + autoProcessNewConnections=b; +} +bool ConnectionGraph2::GetAutoProcessNewConnections(void) const +{ + return autoProcessNewConnections; +} +void ConnectionGraph2::AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID) +{ + // Relay the new connection to other systems. + RakNet::BitStream bs; + bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); + bs.Write((uint32_t)1); + bs.Write(systemAddress); + bs.Write(rakNetGUID); + bs.WriteCasted(rakPeerInterface->GetAveragePing(rakNetGUID)); + SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); + + // Send everyone to the new guy + DataStructures::List addresses; + DataStructures::List guids; + rakPeerInterface->GetSystemList(addresses, guids); + bs.Reset(); + bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); + BitSize_t writeOffset = bs.GetWriteOffset(); + bs.Write((uint32_t) addresses.Size()); + + unsigned int i; + uint32_t count=0; + for (i=0; i < addresses.Size(); i++) + { + if (addresses[i]==systemAddress) + continue; + + bs.Write(addresses[i]); + bs.Write(guids[i]); + bs.WriteCasted(rakPeerInterface->GetAveragePing(guids[i])); + count++; + } + + if (count>0) + { + BitSize_t writeOffset2 = bs.GetWriteOffset(); + bs.SetWriteOffset(writeOffset); + bs.Write(count); + bs.SetWriteOffset(writeOffset2); + SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false); + } + + bool objectExists; + unsigned int ii = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists==false) + { + RemoteSystem* remoteSystem = RakNet::OP_NEW(_FILE_AND_LINE_); + remoteSystem->guid=rakNetGUID; + remoteSystems.InsertAtIndex(remoteSystem,ii,_FILE_AND_LINE_); + } +} +void ConnectionGraph2::GetParticipantList(DataStructures::OrderedList &participantList) +{ + participantList.Clear(true, _FILE_AND_LINE_); + unsigned int i; + for (i=0; i < remoteSystems.Size(); i++) + participantList.InsertAtEnd(remoteSystems[i]->guid, _FILE_AND_LINE_); +} +void ConnectionGraph2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) isIncoming; + if (autoProcessNewConnections) + AddParticipant(systemAddress, rakNetGUID); +} +PluginReceiveResult ConnectionGraph2::OnReceive(Packet *packet) +{ + if (packet->data[0]==ID_REMOTE_CONNECTION_LOST || packet->data[0]==ID_REMOTE_DISCONNECTION_NOTIFICATION) + { + bool objectExists; + unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists) + { + RakNet::BitStream bs(packet->data,packet->length,false); + bs.IgnoreBytes(1); + SystemAddressAndGuid saag; + bs.Read(saag.systemAddress); + bs.Read(saag.guid); + unsigned long idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists); + if (objectExists) + remoteSystems[idx]->remoteConnections.RemoveAtIndex(idx2); + } + } + else if (packet->data[0]==ID_REMOTE_NEW_INCOMING_CONNECTION) + { + bool objectExists; + unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists) + { + uint32_t numAddresses; + RakNet::BitStream bs(packet->data,packet->length,false); + bs.IgnoreBytes(1); + bs.Read(numAddresses); + for (unsigned int idx2=0; idx2 < numAddresses; idx2++) + { + SystemAddressAndGuid saag; + bs.Read(saag.systemAddress); + bs.Read(saag.guid); + bs.Read(saag.sendersPingToThatSystem); + bool objectExists; + unsigned int ii = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists); + if (objectExists==false) + remoteSystems[idx]->remoteConnections.InsertAtIndex(saag,ii,_FILE_AND_LINE_); + } + } + } + + return RR_CONTINUE_PROCESSING; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/ConnectionGraph2.h b/src/raknet/ConnectionGraph2.h index b639879..d5fa99a 100755 --- a/src/raknet/ConnectionGraph2.h +++ b/src/raknet/ConnectionGraph2.h @@ -1,118 +1,118 @@ -/// \file ConnectionGraph2.h -/// \brief Connection graph plugin, version 2. Tells new systems about existing and new connections -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ConnectionGraph2==1 - -#ifndef __CONNECTION_GRAPH_2_H -#define __CONNECTION_GRAPH_2_H - -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "DS_List.h" -#include "DS_WeightedGraph.h" -#include "GetTime.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \brief A one hop connection graph. -/// \details Sends ID_REMOTE_CONNECTION_LOST, ID_REMOTE_DISCONNECTION_NOTIFICATION, ID_REMOTE_NEW_INCOMING_CONNECTION
      -/// All identifiers are followed by SystemAddress, then RakNetGUID -/// Also stores the list for you, which you can access with GetConnectionListForRemoteSystem -/// \ingroup CONNECTION_GRAPH_GROUP -class RAK_DLL_EXPORT ConnectionGraph2 : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(ConnectionGraph2) - - ConnectionGraph2(); - ~ConnectionGraph2(); - - /// \brief Given a remote system identified by RakNetGUID, return the list of SystemAddresses and RakNetGUIDs they are connected to - /// \param[in] remoteSystemGuid Which system we are referring to. This only works for remote systems, not ourselves. - /// \param[out] saOut A preallocated array to hold the output list of SystemAddress. Can be 0 if you don't care. - /// \param[out] guidOut A preallocated array to hold the output list of RakNetGUID. Can be 0 if you don't care. - /// \param[in,out] outLength On input, the size of \a saOut and \a guidOut. On output, modified to reflect the number of elements actually written - /// \return True if \a remoteSystemGuid was found. Otherwise false, and \a saOut, \a guidOut remain unchanged. \a outLength will be set to 0. - bool GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength); - - /// Returns if g1 is connected to g2 - bool ConnectionExists(RakNetGUID g1, RakNetGUID g2); - - /// Returns the average ping between two systems in the connection graph. Returns -1 if no connection exists between those systems - uint16_t GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const; - - /// Returns the system with the lowest average ping among all its connections. - /// If you need one system in the peer to peer group to relay data, have the FullyConnectedMesh2 host call this function after host migration, and use that system - RakNetGUID GetLowestAveragePingSystem(void) const; - - /// \brief If called with false, then new connections are only added to the connection graph when you call ProcessNewConnection(); - /// \details This is useful if you want to perform validation before connecting a system to a mesh, or if you want a submesh (for example a server cloud) - /// \param[in] b True to automatically call ProcessNewConnection() on any new connection, false to not do so. Defaults to true. - void SetAutoProcessNewConnections(bool b); - - /// \brief Returns value passed to SetAutoProcessNewConnections() - /// \return Value passed to SetAutoProcessNewConnections(), or the default of true if it was never called - bool GetAutoProcessNewConnections(void) const; - - /// \brief If you call SetAutoProcessNewConnections(false);, then you will need to manually call ProcessNewConnection() on new connections - /// \details On ID_NEW_INCOMING_CONNECTION or ID_CONNECTION_REQUEST_ACCEPTED, adds that system to the graph - /// Do not call ProcessNewConnection() manually otherwise - /// \param[in] The packet->SystemAddress member - /// \param[in] The packet->guid member - void AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID); - - /// Get the participants added with AddParticipant() - /// \param[out] participantList Participants added with AddParticipant(); - void GetParticipantList(DataStructures::OrderedList &participantList); - - /// \internal - struct SystemAddressAndGuid - { - SystemAddress systemAddress; - RakNetGUID guid; - uint16_t sendersPingToThatSystem; - }; - /// \internal - static int SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data ); - - /// \internal - struct RemoteSystem - { - DataStructures::OrderedList remoteConnections; - RakNetGUID guid; - }; - /// \internal - static int RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data ); - -protected: - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - - // List of systems I am connected to, which in turn stores which systems they are connected to - DataStructures::OrderedList remoteSystems; - - bool autoProcessNewConnections; - -}; - -} // namespace RakNet - -#endif // #ifndef __CONNECTION_GRAPH_2_H - -#endif // _RAKNET_SUPPORT_* +/// \file ConnectionGraph2.h +/// \brief Connection graph plugin, version 2. Tells new systems about existing and new connections +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ConnectionGraph2==1 + +#ifndef __CONNECTION_GRAPH_2_H +#define __CONNECTION_GRAPH_2_H + +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "DS_List.h" +#include "DS_WeightedGraph.h" +#include "GetTime.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief A one hop connection graph. +/// \details Sends ID_REMOTE_CONNECTION_LOST, ID_REMOTE_DISCONNECTION_NOTIFICATION, ID_REMOTE_NEW_INCOMING_CONNECTION
      +/// All identifiers are followed by SystemAddress, then RakNetGUID +/// Also stores the list for you, which you can access with GetConnectionListForRemoteSystem +/// \ingroup CONNECTION_GRAPH_GROUP +class RAK_DLL_EXPORT ConnectionGraph2 : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(ConnectionGraph2) + + ConnectionGraph2(); + ~ConnectionGraph2(); + + /// \brief Given a remote system identified by RakNetGUID, return the list of SystemAddresses and RakNetGUIDs they are connected to + /// \param[in] remoteSystemGuid Which system we are referring to. This only works for remote systems, not ourselves. + /// \param[out] saOut A preallocated array to hold the output list of SystemAddress. Can be 0 if you don't care. + /// \param[out] guidOut A preallocated array to hold the output list of RakNetGUID. Can be 0 if you don't care. + /// \param[in,out] outLength On input, the size of \a saOut and \a guidOut. On output, modified to reflect the number of elements actually written + /// \return True if \a remoteSystemGuid was found. Otherwise false, and \a saOut, \a guidOut remain unchanged. \a outLength will be set to 0. + bool GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength); + + /// Returns if g1 is connected to g2 + bool ConnectionExists(RakNetGUID g1, RakNetGUID g2); + + /// Returns the average ping between two systems in the connection graph. Returns -1 if no connection exists between those systems + uint16_t GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const; + + /// Returns the system with the lowest average ping among all its connections. + /// If you need one system in the peer to peer group to relay data, have the FullyConnectedMesh2 host call this function after host migration, and use that system + RakNetGUID GetLowestAveragePingSystem(void) const; + + /// \brief If called with false, then new connections are only added to the connection graph when you call ProcessNewConnection(); + /// \details This is useful if you want to perform validation before connecting a system to a mesh, or if you want a submesh (for example a server cloud) + /// \param[in] b True to automatically call ProcessNewConnection() on any new connection, false to not do so. Defaults to true. + void SetAutoProcessNewConnections(bool b); + + /// \brief Returns value passed to SetAutoProcessNewConnections() + /// \return Value passed to SetAutoProcessNewConnections(), or the default of true if it was never called + bool GetAutoProcessNewConnections(void) const; + + /// \brief If you call SetAutoProcessNewConnections(false);, then you will need to manually call ProcessNewConnection() on new connections + /// \details On ID_NEW_INCOMING_CONNECTION or ID_CONNECTION_REQUEST_ACCEPTED, adds that system to the graph + /// Do not call ProcessNewConnection() manually otherwise + /// \param[in] The packet->SystemAddress member + /// \param[in] The packet->guid member + void AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID); + + /// Get the participants added with AddParticipant() + /// \param[out] participantList Participants added with AddParticipant(); + void GetParticipantList(DataStructures::OrderedList &participantList); + + /// \internal + struct SystemAddressAndGuid + { + SystemAddress systemAddress; + RakNetGUID guid; + uint16_t sendersPingToThatSystem; + }; + /// \internal + static int SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data ); + + /// \internal + struct RemoteSystem + { + DataStructures::OrderedList remoteConnections; + RakNetGUID guid; + }; + /// \internal + static int RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data ); + +protected: + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + + // List of systems I am connected to, which in turn stores which systems they are connected to + DataStructures::OrderedList remoteSystems; + + bool autoProcessNewConnections; + +}; + +} // namespace RakNet + +#endif // #ifndef __CONNECTION_GRAPH_2_H + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/ConsoleServer.cpp b/src/raknet/ConsoleServer.cpp index 1b20dfe..dea8703 100755 --- a/src/raknet/ConsoleServer.cpp +++ b/src/raknet/ConsoleServer.cpp @@ -1,311 +1,311 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ConsoleServer==1 - -#include "ConsoleServer.h" -#include "TransportInterface.h" -#include "CommandParserInterface.h" -#include -#include - -#define COMMAND_DELINATOR ' ' -#define COMMAND_DELINATOR_TOGGLE '"' - -#include "LinuxStrings.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(ConsoleServer,ConsoleServer); - -ConsoleServer::ConsoleServer() -{ - transport=0; - password[0]=0; - prompt=0; -} -ConsoleServer::~ConsoleServer() -{ - if (prompt) - rakFree_Ex(prompt, _FILE_AND_LINE_); -} -void ConsoleServer::SetTransportProvider(TransportInterface *transportInterface, unsigned short port) -{ - // Replace the current TransportInterface, stopping the old one, if present, and starting the new one. - if (transportInterface) - { - if (transport) - { - RemoveCommandParser(transport->GetCommandParser()); - transport->Stop(); - } - transport=transportInterface; - transport->Start(port, true); - - unsigned i; - for (i=0; i < commandParserList.Size(); i++) - commandParserList[i]->OnTransportChange(transport); - - // The transport itself might have a command parser - for example password for the RakNet transport - AddCommandParser(transport->GetCommandParser()); - } -} -void ConsoleServer::AddCommandParser(CommandParserInterface *commandParserInterface) -{ - if (commandParserInterface==0) - return; - - // Non-duplicate insertion - unsigned i; - for (i=0; i < commandParserList.Size(); i++) - { - if (commandParserList[i]==commandParserInterface) - return; - - if (_stricmp(commandParserList[i]->GetName(), commandParserInterface->GetName())==0) - { - // Naming conflict between two command parsers - RakAssert(0); - return; - } - } - - commandParserList.Insert(commandParserInterface, _FILE_AND_LINE_); - if (transport) - commandParserInterface->OnTransportChange(transport); -} -void ConsoleServer::RemoveCommandParser(CommandParserInterface *commandParserInterface) -{ - if (commandParserInterface==0) - return; - - // Overwrite the element we are removing from the back of the list and delete the back of the list - unsigned i; - for (i=0; i < commandParserList.Size(); i++) - { - if (commandParserList[i]==commandParserInterface) - { - commandParserList[i]=commandParserList[commandParserList.Size()-1]; - commandParserList.RemoveFromEnd(); - return; - } - } -} -void ConsoleServer::Update(void) -{ - unsigned i; - char *parameterList[20]; // Up to 20 parameters - unsigned numParameters; - RakNet::SystemAddress newOrLostConnectionId; - RakNet::Packet *p; - RakNet::RegisteredCommand rc; - - p = transport->Receive(); - newOrLostConnectionId=transport->HasNewIncomingConnection(); - - if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < commandParserList.Size(); i++) - { - commandParserList[i]->OnNewIncomingConnection(newOrLostConnectionId, transport); - } - - transport->Send(newOrLostConnectionId, "Connected to remote command console.\r\nType 'help' for help.\r\n"); - ListParsers(newOrLostConnectionId); - ShowPrompt(newOrLostConnectionId); - } - - newOrLostConnectionId=transport->HasLostConnection(); - if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < commandParserList.Size(); i++) - commandParserList[i]->OnConnectionLost(newOrLostConnectionId, transport); - } - - while (p) - { - bool commandParsed=false; - char copy[REMOTE_MAX_TEXT_INPUT]; - memcpy(copy, p->data, p->length); - copy[p->length]=0; - RakNet::CommandParserInterface::ParseConsoleString((char*)p->data, COMMAND_DELINATOR, COMMAND_DELINATOR_TOGGLE, &numParameters, parameterList, 20); // Up to 20 parameters - if (numParameters==0) - { - transport->DeallocatePacket(p); - p = transport->Receive(); - continue; - } - if (_stricmp(*parameterList, "help")==0 && numParameters<=2) - { - // Find the parser specified and display help for it - if (numParameters==1) - { - transport->Send(p->systemAddress, "\r\nINSTRUCTIONS:\r\n"); - transport->Send(p->systemAddress, "Enter commands on your keyboard, using spaces to delineate parameters.\r\n"); - transport->Send(p->systemAddress, "You can use quotation marks to toggle space delineation.\r\n"); - transport->Send(p->systemAddress, "You can connect multiple times from the same computer.\r\n"); - transport->Send(p->systemAddress, "You can direct commands to a parser by prefixing the parser name or number.\r\n"); - transport->Send(p->systemAddress, "COMMANDS:\r\n"); - transport->Send(p->systemAddress, "help Show this display.\r\n"); - transport->Send(p->systemAddress, "help Show help on a particular parser.\r\n"); - transport->Send(p->systemAddress, "help Show help on a particular command.\r\n"); - transport->Send(p->systemAddress, "quit Disconnects from the server.\r\n"); - transport->Send(p->systemAddress, "[] [] Execute a command\r\n"); - transport->Send(p->systemAddress, "[] [] Execute a command\r\n"); - ListParsers(p->systemAddress); - //ShowPrompt(p->systemAddress); - } - else // numParameters == 2, including the help tag - { - for (i=0; i < commandParserList.Size(); i++) - { - if (_stricmp(parameterList[1], commandParserList[i]->GetName())==0) - { - commandParsed=true; - commandParserList[i]->SendHelp(transport, p->systemAddress); - transport->Send(p->systemAddress, "COMMAND LIST:\r\n"); - commandParserList[i]->SendCommandList(transport, p->systemAddress); - transport->Send(p->systemAddress, "\r\n"); - break; - } - } - - if (commandParsed==false) - { - // Try again, for all commands for all parsers. - RakNet::RegisteredCommand rc; - for (i=0; i < commandParserList.Size(); i++) - { - if (commandParserList[i]->GetRegisteredCommand(parameterList[1], &rc)) - { - if (rc.parameterCount==RakNet::CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS) - transport->Send(p->systemAddress, "(Variable parms): %s %s\r\n", rc.command, rc.commandHelp); - else - transport->Send(p->systemAddress, "(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); - commandParsed=true; - break; - } - } - } - - if (commandParsed==false) - { - // Don't know what to do - transport->Send(p->systemAddress, "Unknown help topic: %s.\r\n", parameterList[1]); - } - //ShowPrompt(p->systemAddress); - } - } - else if (_stricmp(*parameterList, "quit")==0 && numParameters==1) - { - transport->Send(p->systemAddress, "Goodbye!\r\n"); - transport->CloseConnection(p->systemAddress); - } - else - { - bool tryAllParsers=true; - bool failed=false; - - if (numParameters >=2) // At minimum - { - unsigned commandParserIndex=(unsigned)-1; - // Prefixing with numbers directs to a particular parser - if (**parameterList>='0' && **parameterList<='9') - { - commandParserIndex=atoi(*parameterList); // Use specified parser unless it's an invalid number - commandParserIndex--; // Subtract 1 since we displayed numbers starting at index+1 - if (commandParserIndex >= commandParserList.Size()) - { - transport->Send(p->systemAddress, "Invalid index.\r\n"); - failed=true; - } - } - else - { - // // Prefixing with the name of a command parser directs to that parser. See if the first word matches a parser - for (i=0; i < commandParserList.Size(); i++) - { - if (_stricmp(parameterList[0], commandParserList[i]->GetName())==0) - { - commandParserIndex=i; // Matches parser at index i - break; - } - } - } - - if (failed==false) - { - // -1 means undirected, so otherwise this is directed to a target - if (commandParserIndex!=(unsigned)-1) - { - // Only this parser should use this command - tryAllParsers=false; - if (commandParserList[commandParserIndex]->GetRegisteredCommand(parameterList[1], &rc)) - { - commandParsed=true; - if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-2) - commandParserList[commandParserIndex]->OnCommand(rc.command, numParameters-2, parameterList+2, transport, p->systemAddress, copy); - else - transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); - } - } - } - } - - if (failed == false && tryAllParsers) - { - for (i=0; i < commandParserList.Size(); i++) - { - // Undirected command. Try all the parsers to see if they understand the command - // Pass the 1nd element as the command, and the remainder as the parameter list - if (commandParserList[i]->GetRegisteredCommand(parameterList[0], &rc)) - { - commandParsed=true; - - if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-1) - commandParserList[i]->OnCommand(rc.command, numParameters-1, parameterList+1, transport, p->systemAddress, copy); - else - transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); - } - } - } - if (commandParsed==false && commandParserList.Size() > 0) - { - transport->Send(p->systemAddress, "Unknown command: Type 'help' for help.\r\n"); - } - - } - - ShowPrompt(p->systemAddress); - - transport->DeallocatePacket(p); - p = transport->Receive(); - } -} - -void ConsoleServer::ListParsers(SystemAddress systemAddress) -{ - transport->Send(systemAddress,"INSTALLED PARSERS:\r\n"); - unsigned i; - for (i=0; i < commandParserList.Size(); i++) - { - transport->Send(systemAddress, "%i. %s\r\n", i+1, commandParserList[i]->GetName()); - } -} -void ConsoleServer::ShowPrompt(SystemAddress systemAddress) -{ - transport->Send(systemAddress, prompt); -} -void ConsoleServer::SetPrompt(const char *_prompt) -{ - if (prompt) - rakFree_Ex(prompt,_FILE_AND_LINE_); - if (_prompt && _prompt[0]) - { - size_t len = strlen(_prompt); - prompt = (char*) rakMalloc_Ex(len+1,_FILE_AND_LINE_); - strcpy(prompt,_prompt); - } - else - prompt=0; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ConsoleServer==1 + +#include "ConsoleServer.h" +#include "TransportInterface.h" +#include "CommandParserInterface.h" +#include +#include + +#define COMMAND_DELINATOR ' ' +#define COMMAND_DELINATOR_TOGGLE '"' + +#include "LinuxStrings.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(ConsoleServer,ConsoleServer); + +ConsoleServer::ConsoleServer() +{ + transport=0; + password[0]=0; + prompt=0; +} +ConsoleServer::~ConsoleServer() +{ + if (prompt) + rakFree_Ex(prompt, _FILE_AND_LINE_); +} +void ConsoleServer::SetTransportProvider(TransportInterface *transportInterface, unsigned short port) +{ + // Replace the current TransportInterface, stopping the old one, if present, and starting the new one. + if (transportInterface) + { + if (transport) + { + RemoveCommandParser(transport->GetCommandParser()); + transport->Stop(); + } + transport=transportInterface; + transport->Start(port, true); + + unsigned i; + for (i=0; i < commandParserList.Size(); i++) + commandParserList[i]->OnTransportChange(transport); + + // The transport itself might have a command parser - for example password for the RakNet transport + AddCommandParser(transport->GetCommandParser()); + } +} +void ConsoleServer::AddCommandParser(CommandParserInterface *commandParserInterface) +{ + if (commandParserInterface==0) + return; + + // Non-duplicate insertion + unsigned i; + for (i=0; i < commandParserList.Size(); i++) + { + if (commandParserList[i]==commandParserInterface) + return; + + if (_stricmp(commandParserList[i]->GetName(), commandParserInterface->GetName())==0) + { + // Naming conflict between two command parsers + RakAssert(0); + return; + } + } + + commandParserList.Insert(commandParserInterface, _FILE_AND_LINE_); + if (transport) + commandParserInterface->OnTransportChange(transport); +} +void ConsoleServer::RemoveCommandParser(CommandParserInterface *commandParserInterface) +{ + if (commandParserInterface==0) + return; + + // Overwrite the element we are removing from the back of the list and delete the back of the list + unsigned i; + for (i=0; i < commandParserList.Size(); i++) + { + if (commandParserList[i]==commandParserInterface) + { + commandParserList[i]=commandParserList[commandParserList.Size()-1]; + commandParserList.RemoveFromEnd(); + return; + } + } +} +void ConsoleServer::Update(void) +{ + unsigned i; + char *parameterList[20]; // Up to 20 parameters + unsigned numParameters; + RakNet::SystemAddress newOrLostConnectionId; + RakNet::Packet *p; + RakNet::RegisteredCommand rc; + + p = transport->Receive(); + newOrLostConnectionId=transport->HasNewIncomingConnection(); + + if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < commandParserList.Size(); i++) + { + commandParserList[i]->OnNewIncomingConnection(newOrLostConnectionId, transport); + } + + transport->Send(newOrLostConnectionId, "Connected to remote command console.\r\nType 'help' for help.\r\n"); + ListParsers(newOrLostConnectionId); + ShowPrompt(newOrLostConnectionId); + } + + newOrLostConnectionId=transport->HasLostConnection(); + if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < commandParserList.Size(); i++) + commandParserList[i]->OnConnectionLost(newOrLostConnectionId, transport); + } + + while (p) + { + bool commandParsed=false; + char copy[REMOTE_MAX_TEXT_INPUT]; + memcpy(copy, p->data, p->length); + copy[p->length]=0; + RakNet::CommandParserInterface::ParseConsoleString((char*)p->data, COMMAND_DELINATOR, COMMAND_DELINATOR_TOGGLE, &numParameters, parameterList, 20); // Up to 20 parameters + if (numParameters==0) + { + transport->DeallocatePacket(p); + p = transport->Receive(); + continue; + } + if (_stricmp(*parameterList, "help")==0 && numParameters<=2) + { + // Find the parser specified and display help for it + if (numParameters==1) + { + transport->Send(p->systemAddress, "\r\nINSTRUCTIONS:\r\n"); + transport->Send(p->systemAddress, "Enter commands on your keyboard, using spaces to delineate parameters.\r\n"); + transport->Send(p->systemAddress, "You can use quotation marks to toggle space delineation.\r\n"); + transport->Send(p->systemAddress, "You can connect multiple times from the same computer.\r\n"); + transport->Send(p->systemAddress, "You can direct commands to a parser by prefixing the parser name or number.\r\n"); + transport->Send(p->systemAddress, "COMMANDS:\r\n"); + transport->Send(p->systemAddress, "help Show this display.\r\n"); + transport->Send(p->systemAddress, "help Show help on a particular parser.\r\n"); + transport->Send(p->systemAddress, "help Show help on a particular command.\r\n"); + transport->Send(p->systemAddress, "quit Disconnects from the server.\r\n"); + transport->Send(p->systemAddress, "[] [] Execute a command\r\n"); + transport->Send(p->systemAddress, "[] [] Execute a command\r\n"); + ListParsers(p->systemAddress); + //ShowPrompt(p->systemAddress); + } + else // numParameters == 2, including the help tag + { + for (i=0; i < commandParserList.Size(); i++) + { + if (_stricmp(parameterList[1], commandParserList[i]->GetName())==0) + { + commandParsed=true; + commandParserList[i]->SendHelp(transport, p->systemAddress); + transport->Send(p->systemAddress, "COMMAND LIST:\r\n"); + commandParserList[i]->SendCommandList(transport, p->systemAddress); + transport->Send(p->systemAddress, "\r\n"); + break; + } + } + + if (commandParsed==false) + { + // Try again, for all commands for all parsers. + RakNet::RegisteredCommand rc; + for (i=0; i < commandParserList.Size(); i++) + { + if (commandParserList[i]->GetRegisteredCommand(parameterList[1], &rc)) + { + if (rc.parameterCount==RakNet::CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS) + transport->Send(p->systemAddress, "(Variable parms): %s %s\r\n", rc.command, rc.commandHelp); + else + transport->Send(p->systemAddress, "(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); + commandParsed=true; + break; + } + } + } + + if (commandParsed==false) + { + // Don't know what to do + transport->Send(p->systemAddress, "Unknown help topic: %s.\r\n", parameterList[1]); + } + //ShowPrompt(p->systemAddress); + } + } + else if (_stricmp(*parameterList, "quit")==0 && numParameters==1) + { + transport->Send(p->systemAddress, "Goodbye!\r\n"); + transport->CloseConnection(p->systemAddress); + } + else + { + bool tryAllParsers=true; + bool failed=false; + + if (numParameters >=2) // At minimum + { + unsigned commandParserIndex=(unsigned)-1; + // Prefixing with numbers directs to a particular parser + if (**parameterList>='0' && **parameterList<='9') + { + commandParserIndex=atoi(*parameterList); // Use specified parser unless it's an invalid number + commandParserIndex--; // Subtract 1 since we displayed numbers starting at index+1 + if (commandParserIndex >= commandParserList.Size()) + { + transport->Send(p->systemAddress, "Invalid index.\r\n"); + failed=true; + } + } + else + { + // // Prefixing with the name of a command parser directs to that parser. See if the first word matches a parser + for (i=0; i < commandParserList.Size(); i++) + { + if (_stricmp(parameterList[0], commandParserList[i]->GetName())==0) + { + commandParserIndex=i; // Matches parser at index i + break; + } + } + } + + if (failed==false) + { + // -1 means undirected, so otherwise this is directed to a target + if (commandParserIndex!=(unsigned)-1) + { + // Only this parser should use this command + tryAllParsers=false; + if (commandParserList[commandParserIndex]->GetRegisteredCommand(parameterList[1], &rc)) + { + commandParsed=true; + if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-2) + commandParserList[commandParserIndex]->OnCommand(rc.command, numParameters-2, parameterList+2, transport, p->systemAddress, copy); + else + transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); + } + } + } + } + + if (failed == false && tryAllParsers) + { + for (i=0; i < commandParserList.Size(); i++) + { + // Undirected command. Try all the parsers to see if they understand the command + // Pass the 1nd element as the command, and the remainder as the parameter list + if (commandParserList[i]->GetRegisteredCommand(parameterList[0], &rc)) + { + commandParsed=true; + + if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-1) + commandParserList[i]->OnCommand(rc.command, numParameters-1, parameterList+1, transport, p->systemAddress, copy); + else + transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp); + } + } + } + if (commandParsed==false && commandParserList.Size() > 0) + { + transport->Send(p->systemAddress, "Unknown command: Type 'help' for help.\r\n"); + } + + } + + ShowPrompt(p->systemAddress); + + transport->DeallocatePacket(p); + p = transport->Receive(); + } +} + +void ConsoleServer::ListParsers(SystemAddress systemAddress) +{ + transport->Send(systemAddress,"INSTALLED PARSERS:\r\n"); + unsigned i; + for (i=0; i < commandParserList.Size(); i++) + { + transport->Send(systemAddress, "%i. %s\r\n", i+1, commandParserList[i]->GetName()); + } +} +void ConsoleServer::ShowPrompt(SystemAddress systemAddress) +{ + transport->Send(systemAddress, prompt); +} +void ConsoleServer::SetPrompt(const char *_prompt) +{ + if (prompt) + rakFree_Ex(prompt,_FILE_AND_LINE_); + if (_prompt && _prompt[0]) + { + size_t len = strlen(_prompt); + prompt = (char*) rakMalloc_Ex(len+1,_FILE_AND_LINE_); + strcpy(prompt,_prompt); + } + else + prompt=0; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/ConsoleServer.h b/src/raknet/ConsoleServer.h index d65fc96..0776c54 100755 --- a/src/raknet/ConsoleServer.h +++ b/src/raknet/ConsoleServer.h @@ -1,77 +1,77 @@ -/// \file ConsoleServer.h -/// \brief Contains ConsoleServer , used to plugin to your game to accept remote console-based connections -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ConsoleServer==1 - -#ifndef __CONSOLE_SERVER_H -#define __CONSOLE_SERVER_H - -#include "RakMemoryOverride.h" -#include "DS_List.h" -#include "RakNetTypes.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class TransportInterface; -class CommandParserInterface; - - -/// \brief The main entry point for the server portion of your remote console application support. -/// \details ConsoleServer takes one TransportInterface and one or more CommandParserInterface (s) -/// The TransportInterface will be used to send data between the server and the client. The connecting client must support the -/// protocol used by your derivation of TransportInterface . TelnetTransport and RakNetTransport are two such derivations . -/// When a command is sent by a remote console, it will be processed by your implementations of CommandParserInterface -class RAK_DLL_EXPORT ConsoleServer -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(ConsoleServer) - - ConsoleServer(); - ~ConsoleServer(); - - /// \brief Call this with a derivation of TransportInterface so that the console server can send and receive commands - /// \param[in] transportInterface Your interface to use. - /// \param[in] port The port to host on. Telnet uses port 23 by default. RakNet can use whatever you want. - void SetTransportProvider(TransportInterface *transportInterface, unsigned short port); - - /// \brief Add an implementation of CommandParserInterface to the list of command parsers. - /// \param[in] commandParserInterface The command parser referred to - void AddCommandParser(CommandParserInterface *commandParserInterface); - - /// \brief Remove an implementation of CommandParserInterface previously added with AddCommandParser(). - /// \param[in] commandParserInterface The command parser referred to - void RemoveCommandParser(CommandParserInterface *commandParserInterface); - - /// \brief Call update to read packet sent from your TransportInterface. - /// You should do this fairly frequently. - void Update(void); - - /// \brief Sets a prompt to show when waiting for user input. - /// \details Pass an empty string to clear the prompt - /// Defaults to no prompt - /// \param[in] _prompt Null-terminated string of the prompt to use. If you want a newline, be sure to use /r/n - void SetPrompt(const char *_prompt); - -protected: - void ListParsers(SystemAddress systemAddress); - void ShowPrompt(SystemAddress systemAddress); - TransportInterface *transport; - DataStructures::List commandParserList; - char* password[256]; - char *prompt; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file ConsoleServer.h +/// \brief Contains ConsoleServer , used to plugin to your game to accept remote console-based connections +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ConsoleServer==1 + +#ifndef __CONSOLE_SERVER_H +#define __CONSOLE_SERVER_H + +#include "RakMemoryOverride.h" +#include "DS_List.h" +#include "RakNetTypes.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class TransportInterface; +class CommandParserInterface; + + +/// \brief The main entry point for the server portion of your remote console application support. +/// \details ConsoleServer takes one TransportInterface and one or more CommandParserInterface (s) +/// The TransportInterface will be used to send data between the server and the client. The connecting client must support the +/// protocol used by your derivation of TransportInterface . TelnetTransport and RakNetTransport are two such derivations . +/// When a command is sent by a remote console, it will be processed by your implementations of CommandParserInterface +class RAK_DLL_EXPORT ConsoleServer +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(ConsoleServer) + + ConsoleServer(); + ~ConsoleServer(); + + /// \brief Call this with a derivation of TransportInterface so that the console server can send and receive commands + /// \param[in] transportInterface Your interface to use. + /// \param[in] port The port to host on. Telnet uses port 23 by default. RakNet can use whatever you want. + void SetTransportProvider(TransportInterface *transportInterface, unsigned short port); + + /// \brief Add an implementation of CommandParserInterface to the list of command parsers. + /// \param[in] commandParserInterface The command parser referred to + void AddCommandParser(CommandParserInterface *commandParserInterface); + + /// \brief Remove an implementation of CommandParserInterface previously added with AddCommandParser(). + /// \param[in] commandParserInterface The command parser referred to + void RemoveCommandParser(CommandParserInterface *commandParserInterface); + + /// \brief Call update to read packet sent from your TransportInterface. + /// You should do this fairly frequently. + void Update(void); + + /// \brief Sets a prompt to show when waiting for user input. + /// \details Pass an empty string to clear the prompt + /// Defaults to no prompt + /// \param[in] _prompt Null-terminated string of the prompt to use. If you want a newline, be sure to use /r/n + void SetPrompt(const char *_prompt); + +protected: + void ListParsers(SystemAddress systemAddress); + void ShowPrompt(SystemAddress systemAddress); + TransportInterface *transport; + DataStructures::List commandParserList; + char* password[256]; + char *prompt; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/DS_BPlusTree.h b/src/raknet/DS_BPlusTree.h index 768bfd8..ad4d443 100755 --- a/src/raknet/DS_BPlusTree.h +++ b/src/raknet/DS_BPlusTree.h @@ -1,1146 +1,1146 @@ -/// \file DS_BPlusTree.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __B_PLUS_TREE_CPP -#define __B_PLUS_TREE_CPP - -#include "DS_MemoryPool.h" -#include "DS_Queue.h" -#include -#include "Export.h" - -// Java -// http://www.seanster.com/BplusTree/BplusTree.html - -// Overview -// http://babbage.clarku.edu/~achou/cs160/B+Trees/B+Trees.htm - -// Deletion -// http://dbpubs.stanford.edu:8090/pub/1995-19 - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -#include "RakMemoryOverride.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// Used in the BPlusTree. Used for both leaf and index nodes. - /// Don't use a constructor or destructor, due to the memory pool I am using - template - struct RAK_DLL_EXPORT Page - { - // We use the same data structure for both leaf and index nodes. - // It uses a little more memory for index nodes but reduces - // memory fragmentation, allocations, and deallocations. - bool isLeaf; - - // Used for both leaf and index nodes. - // For a leaf it means the number of elements in data - // For an index it means the number of keys and is one less than the number of children pointers. - int size; - - // Used for both leaf and index nodes. - KeyType keys[order]; - - // Used only for leaf nodes. Data is the actual data, while next is the pointer to the next leaf (for B+) - DataType data[order]; - Page *next; - Page *previous; - - // Used only for index nodes. Pointers to the children of this node. - Page *children[order+1]; - }; - - /// A BPlus tree - /// Written with efficiency and speed in mind. - template - class RAK_DLL_EXPORT BPlusTree - { - public: - struct ReturnAction - { - KeyType key1; - KeyType key2; - enum - { - NO_ACTION, - REPLACE_KEY1_WITH_KEY2, - PUSH_KEY_TO_PARENT, - SET_BRANCH_KEY, - } action; // 0=none, 1=replace key1 with key2 - }; - - BPlusTree(); - ~BPlusTree(); - void SetPoolPageSize(int size); // Set the page size for the memory pool. Optionsl - bool Get(const KeyType key, DataType &out) const; - bool Delete(const KeyType key); - bool Delete(const KeyType key, DataType &out); - bool Insert(const KeyType key, const DataType &data); - void Clear(void); - unsigned Size(void) const; - bool IsEmpty(void) const; - Page *GetListHead(void) const; - DataType GetDataHead(void) const; - void PrintLeaves(void); - void ForEachLeaf(void (*func)(Page * leaf, int index)); - void ForEachData(void (*func)(DataType input, int index)); - void PrintGraph(void); - protected: - void ValidateTreeRecursive(Page *cur); - void DeleteFromPageAtIndex(const int index, Page *cur); - static void PrintLeaf(Page * leaf, int index); - void FreePages(void); - bool GetIndexOf(const KeyType key, Page *page, int *out) const; - void ShiftKeysLeft(Page *cur); - bool CanRotateLeft(Page *cur, int childIndex); - bool CanRotateRight(Page *cur, int childIndex); - void RotateRight(Page *cur, int childIndex, ReturnAction *returnAction); - void RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction); - Page* InsertIntoNode(const KeyType key, const DataType &childData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction); - Page* InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction* returnAction, bool *success); - Page* GetLeafFromKey(const KeyType key) const; - bool FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out); - bool FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction); - void ShiftNodeLeft(Page *cur); - void ShiftNodeRight(Page *cur); - - MemoryPool > pagePool; - Page *root, *leftmostLeaf; - }; - - template - BPlusTree::BPlusTree () - { - RakAssert(order>1); - root=0; - leftmostLeaf=0; - } - template - BPlusTree::~BPlusTree () - { - Clear(); - } - template - void BPlusTree::SetPoolPageSize(int size) - { - pagePool.SetPageSize(size); - } - template - bool BPlusTree::Get(const KeyType key, DataType &out) const - { - if (root==0) - return false; - - Page* leaf = GetLeafFromKey(key); - int childIndex; - - if (GetIndexOf(key, leaf, &childIndex)) - { - out=leaf->data[childIndex]; - return true; - } - return false; - } - template - void BPlusTree::DeleteFromPageAtIndex(const int index, Page *cur) - { - int i; - for (i=index; i < cur->size-1; i++) - cur->keys[i]=cur->keys[i+1]; - if (cur->isLeaf) - { - for (i=index; i < cur->size-1; i++) - cur->data[i]=cur->data[i+1]; - } - else - { - for (i=index; i < cur->size-1; i++) - cur->children[i+1]=cur->children[i+2]; - } - cur->size--; - } - template - bool BPlusTree::Delete(const KeyType key) - { - DataType temp; - return Delete(key, temp); - } - template - bool BPlusTree::Delete(const KeyType key, DataType &out) - { - if (root==0) - return false; - - ReturnAction returnAction; - returnAction.action=ReturnAction::NO_ACTION; - int childIndex; - bool underflow=false; - if (root==leftmostLeaf) - { - if (GetIndexOf(key, root, &childIndex)==false) - return false; - out=root->data[childIndex]; - DeleteFromPageAtIndex(childIndex,root); - if (root->size==0) - { - pagePool.Release(root, _FILE_AND_LINE_); - root=0; - leftmostLeaf=0; - } - return true; - } - else if (FindDeleteRebalance(key, root, &underflow,root->keys[0], &returnAction, out)==false) - return false; - -// RakAssert(returnAction.action==ReturnAction::NO_ACTION); - - if (underflow && root->size==0) - { - // Move the root down. - Page *oldRoot=root; - root=root->children[0]; - pagePool.Release(oldRoot, _FILE_AND_LINE_); - // memset(oldRoot,0,sizeof(root)); - } - - return true; - } - template - bool BPlusTree::FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out) - { - // Get index of child to follow. - int branchIndex, childIndex; - if (GetIndexOf(key, cur, &childIndex)) - branchIndex=childIndex+1; - else - branchIndex=childIndex; - - // If child is not a leaf, call recursively - if (cur->children[branchIndex]->isLeaf==false) - { - if (branchIndexsize) - rightRootKey=cur->keys[branchIndex]; // Shift right to left - else - rightRootKey=cur->keys[branchIndex-1]; // Shift center to left - - if (FindDeleteRebalance(key, cur->children[branchIndex], underflow, rightRootKey, returnAction, out)==false) - return false; - - // Call again in case the root key changed - if (branchIndexsize) - rightRootKey=cur->keys[branchIndex]; // Shift right to left - else - rightRootKey=cur->keys[branchIndex-1]; // Shift center to left - - if (returnAction->action==ReturnAction::SET_BRANCH_KEY && branchIndex!=childIndex) - { - returnAction->action=ReturnAction::NO_ACTION; - cur->keys[childIndex]=returnAction->key1; - - if (branchIndexsize) - rightRootKey=cur->keys[branchIndex]; // Shift right to left - else - rightRootKey=cur->keys[branchIndex-1]; // Shift center to left - } - } - else - { - // If child is a leaf, get the index of the key. If the item is not found, cancel delete. - if (GetIndexOf(key, cur->children[branchIndex], &childIndex)==false) - return false; - - // Delete: - // Remove childIndex from the child at branchIndex - out=cur->children[branchIndex]->data[childIndex]; - DeleteFromPageAtIndex(childIndex, cur->children[branchIndex]); - - if (childIndex==0) - { - if (branchIndex>0) - cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; - - if (branchIndex==0) - { - returnAction->action=ReturnAction::SET_BRANCH_KEY; - returnAction->key1=cur->children[0]->keys[0]; - } - } - - if (cur->children[branchIndex]->size < order/2) - *underflow=true; - else - *underflow=false; - } - - // Fix underflow: - if (*underflow) - { - *underflow=FixUnderflow(branchIndex, cur, rightRootKey, returnAction); - } - - return true; - } - template - bool BPlusTree::FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction) - { - // Borrow from a neighbor that has excess. - Page *source; - Page *dest; - - if (branchIndex>0 && cur->children[branchIndex-1]->size > order/2) - { - dest=cur->children[branchIndex]; - source=cur->children[branchIndex-1]; - - // Left has excess - ShiftNodeRight(dest); - if (dest->isLeaf) - { - dest->keys[0]=source->keys[source->size-1]; - dest->data[0]=source->data[source->size-1]; - } - else - { - dest->children[0]=source->children[source->size]; - dest->keys[0]=cur->keys[branchIndex-1]; - } - // Update the parent key for the child (middle) - cur->keys[branchIndex-1]=source->keys[source->size-1]; - source->size--; - - // if (branchIndex==0) - // { - // returnAction->action=ReturnAction::SET_BRANCH_KEY; - // returnAction->key1=dest->keys[0]; - // } - - // No underflow - return false; - } - else if (branchIndexsize && cur->children[branchIndex+1]->size > order/2) - { - dest=cur->children[branchIndex]; - source=cur->children[branchIndex+1]; - - // Right has excess - if (dest->isLeaf) - { - dest->keys[dest->size]=source->keys[0]; - dest->data[dest->size]=source->data[0]; - - // The first key in the leaf after shifting is the parent key for the right branch - cur->keys[branchIndex]=source->keys[1]; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (order<=3 && dest->size==0) - { - if (branchIndex==0) - { - returnAction->action=ReturnAction::SET_BRANCH_KEY; - returnAction->key1=dest->keys[0]; - } - else - cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; - } - } - else - { - if (returnAction->action==ReturnAction::NO_ACTION) - { - returnAction->action=ReturnAction::SET_BRANCH_KEY; - returnAction->key1=dest->keys[0]; - } - - dest->keys[dest->size]=rightRootKey; - dest->children[dest->size+1]=source->children[0]; - - // The shifted off key is the leftmost key for a node - cur->keys[branchIndex]=source->keys[0]; - } - - - dest->size++; - ShiftNodeLeft(source); - - //cur->keys[branchIndex]=source->keys[0]; - -// returnAction->action=ReturnAction::SET_BRANCH_KEY; -// returnAction->key1=dest->keys[dest->size-1]; - - // No underflow - return false; - } - else - { - int sourceIndex; - - // If no neighbors have excess, merge two branches. - // - // To merge two leaves, just copy the data and keys over. - // - // To merge two branches, copy the pointers and keys over, using rightRootKey as the key for the extra pointer - if (branchIndexsize) - { - // Merge right child to current child and delete right child. - dest=cur->children[branchIndex]; - source=cur->children[branchIndex+1]; - } - else - { - // Move current child to left and delete current child - dest=cur->children[branchIndex-1]; - source=cur->children[branchIndex]; - } - - // Merge - if (dest->isLeaf) - { - for (sourceIndex=0; sourceIndexsize; sourceIndex++) - { - dest->keys[dest->size]=source->keys[sourceIndex]; - dest->data[dest->size++]=source->data[sourceIndex]; - } - } - else - { - // We want the tree root key of the source, not the current. - dest->keys[dest->size]=rightRootKey; - dest->children[dest->size++ + 1]=source->children[0]; - for (sourceIndex=0; sourceIndexsize; sourceIndex++) - { - dest->keys[dest->size]=source->keys[sourceIndex]; - dest->children[dest->size++ + 1]=source->children[sourceIndex + 1]; - } - } - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (order<=3 && branchIndex>0 && cur->children[branchIndex]->isLeaf) // With order==2 it is possible to delete data[0], which is not possible with higher orders. - cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; - - if (branchIndexsize) - { - // Update the parent key, removing the source (right) - DeleteFromPageAtIndex(branchIndex, cur); - } - else - { - if (branchIndex>0) - { - // Update parent key, removing the source (current) - DeleteFromPageAtIndex(branchIndex-1, cur); - } - } - - if (branchIndex==0 && dest->isLeaf) - { - returnAction->action=ReturnAction::SET_BRANCH_KEY; - returnAction->key1=dest->keys[0]; - } - - if (source==leftmostLeaf) - leftmostLeaf=source->next; - - if (source->isLeaf) - { - if (source->previous) - source->previous->next=source->next; - if (source->next) - source->next->previous=source->previous; - } - - // Free the source node - pagePool.Release(source, _FILE_AND_LINE_); - // memset(source,0,sizeof(root)); - - // Return underflow or not of parent. - return cur->size < order/2; - } - } - template - void BPlusTree::ShiftNodeRight(Page *cur) - { - int i; - for (i=cur->size; i>0; i--) - cur->keys[i]=cur->keys[i-1]; - if (cur->isLeaf) - { - for (i=cur->size; i>0; i--) - cur->data[i]=cur->data[i-1]; - } - else - { - for (i=cur->size+1; i>0; i--) - cur->children[i]=cur->children[i-1]; - } - - cur->size++; - } - template - void BPlusTree::ShiftNodeLeft(Page *cur) - { - int i; - for (i=0; i < cur->size-1; i++) - cur->keys[i]=cur->keys[i+1]; - if (cur->isLeaf) - { - for (i=0; i < cur->size; i++) - cur->data[i]=cur->data[i+1]; - } - else - { - for (i=0; i < cur->size; i++) - cur->children[i]=cur->children[i+1]; - } - cur->size--; - } - template - Page* BPlusTree::InsertIntoNode(const KeyType key, const DataType &leafData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction) - { - int i; - if (cur->size < order) - { - for (i=cur->size; i > insertionIndex; i--) - cur->keys[i]=cur->keys[i-1]; - if (cur->isLeaf) - { - for (i=cur->size; i > insertionIndex; i--) - cur->data[i]=cur->data[i-1]; - } - else - { - for (i=cur->size+1; i > insertionIndex+1; i--) - cur->children[i]=cur->children[i-1]; - } - cur->keys[insertionIndex]=key; - if (cur->isLeaf) - cur->data[insertionIndex]=leafData; - else - cur->children[insertionIndex+1]=nodeData; - - cur->size++; - } - else - { - Page* newPage = pagePool.Allocate( _FILE_AND_LINE_ ); - newPage->isLeaf=cur->isLeaf; - if (cur->isLeaf) - { - newPage->next=cur->next; - if (cur->next) - cur->next->previous=newPage; - newPage->previous=cur; - cur->next=newPage; - } - - int destIndex, sourceIndex; - - if (insertionIndex>=(order+1)/2) - { - destIndex=0; - sourceIndex=order/2; - - for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) - { - newPage->keys[destIndex]=cur->keys[sourceIndex]; - } - newPage->keys[destIndex++]=key; - for (; sourceIndex < order; sourceIndex++, destIndex++) - { - newPage->keys[destIndex]=cur->keys[sourceIndex]; - } - - destIndex=0; - sourceIndex=order/2; - if (cur->isLeaf) - { - for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) - { - newPage->data[destIndex]=cur->data[sourceIndex]; - } - newPage->data[destIndex++]=leafData; - for (; sourceIndex < order; sourceIndex++, destIndex++) - { - newPage->data[destIndex]=cur->data[sourceIndex]; - } - } - else - { - - for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) - { - newPage->children[destIndex]=cur->children[sourceIndex+1]; - } - newPage->children[destIndex++]=nodeData; - - // sourceIndex+1 is sort of a hack but it works - because there is one extra child than keys - // skip past the last child for cur - for (; sourceIndex+1 < cur->size+1; sourceIndex++, destIndex++) - { - newPage->children[destIndex]=cur->children[sourceIndex+1]; - } - - // the first key is the middle key. Remove it from the page and push it to the parent - returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; - returnAction->key1=newPage->keys[0]; - for (int i=0; i < destIndex-1; i++) - newPage->keys[i]=newPage->keys[i+1]; - - } - cur->size=order/2; - } - else - { - destIndex=0; - sourceIndex=(order+1)/2-1; - for (; sourceIndex < order; sourceIndex++, destIndex++) - newPage->keys[destIndex]=cur->keys[sourceIndex]; - destIndex=0; - if (cur->isLeaf) - { - sourceIndex=(order+1)/2-1; - for (; sourceIndex < order; sourceIndex++, destIndex++) - newPage->data[destIndex]=cur->data[sourceIndex]; - } - else - { - sourceIndex=(order+1)/2; - for (; sourceIndex < order+1; sourceIndex++, destIndex++) - newPage->children[destIndex]=cur->children[sourceIndex]; - - // the first key is the middle key. Remove it from the page and push it to the parent - returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; - returnAction->key1=newPage->keys[0]; - for (int i=0; i < destIndex-1; i++) - newPage->keys[i]=newPage->keys[i+1]; - } - cur->size=(order+1)/2-1; - if (cur->size) - { - bool b = GetIndexOf(key, cur, &insertionIndex); - (void) b; - RakAssert(b==false); - } - else - insertionIndex=0; - InsertIntoNode(key, leafData, insertionIndex, nodeData, cur, returnAction); - } - - newPage->size=destIndex; - - return newPage; - } - - return 0; - } - - template - bool BPlusTree::CanRotateLeft(Page *cur, int childIndex) - { - return childIndex>0 && cur->children[childIndex-1]->size - void BPlusTree::RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction) - { - Page *dest = cur->children[childIndex-1]; - Page *source = cur->children[childIndex]; - returnAction->key1=source->keys[0]; - dest->keys[dest->size]=source->keys[0]; - dest->data[dest->size]=source->data[0]; - dest->size++; - for (int i=0; i < source->size-1; i++) - { - source->keys[i]=source->keys[i+1]; - source->data[i]=source->data[i+1]; - } - source->size--; - cur->keys[childIndex-1]=source->keys[0]; - returnAction->key2=source->keys[0]; - } - - template - bool BPlusTree::CanRotateRight(Page *cur, int childIndex) - { - return childIndex < cur->size && cur->children[childIndex+1]->size - void BPlusTree::RotateRight(Page *cur, int childIndex, ReturnAction *returnAction) - { - Page *dest = cur->children[childIndex+1]; - Page *source = cur->children[childIndex]; - returnAction->key1=dest->keys[0]; - for (int i= dest->size; i > 0; i--) - { - dest->keys[i]=dest->keys[i-1]; - dest->data[i]=dest->data[i-1]; - } - dest->keys[0]=source->keys[source->size-1]; - dest->data[0]=source->data[source->size-1]; - dest->size++; - source->size--; - - cur->keys[childIndex]=dest->keys[0]; - returnAction->key2=dest->keys[0]; - } - template - Page* BPlusTree::GetLeafFromKey(const KeyType key) const - { - Page* cur = root; - int childIndex; - while (cur->isLeaf==false) - { - // When searching, if we match the exact key we go down the pointer after that index - if (GetIndexOf(key, cur, &childIndex)) - childIndex++; - cur = cur->children[childIndex]; - } - return cur; - } - - template - Page* BPlusTree::InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction *returnAction, bool *success) - { - int childIndex; - int branchIndex; - if (GetIndexOf(key, cur, &childIndex)) - branchIndex=childIndex+1; - else - branchIndex=childIndex; - Page* newPage; - if (cur->isLeaf==false) - { - if (cur->children[branchIndex]->isLeaf==true && cur->children[branchIndex]->size==order) - { - if (branchIndex==childIndex+1) - { - *success=false; - return 0; // Already exists - } - - if (CanRotateLeft(cur, branchIndex)) - { - returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; - if (key > cur->children[branchIndex]->keys[0]) - { - RotateLeft(cur, branchIndex, returnAction); - - int insertionIndex; - GetIndexOf(key, cur->children[branchIndex], &insertionIndex); - InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); - } - else - { - // Move head element to left and replace it with key,data - Page* dest=cur->children[branchIndex-1]; - Page* source=cur->children[branchIndex]; - returnAction->key1=source->keys[0]; - returnAction->key2=key; - dest->keys[dest->size]=source->keys[0]; - dest->data[dest->size]=source->data[0]; - dest->size++; - source->keys[0]=key; - source->data[0]=data; - } - cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; - - return 0; - } - else if (CanRotateRight(cur, branchIndex)) - { - returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; - - if (key < cur->children[branchIndex]->keys[cur->children[branchIndex]->size-1]) - { - RotateRight(cur, branchIndex, returnAction); - - int insertionIndex; - GetIndexOf(key, cur->children[branchIndex], &insertionIndex); - InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); - - } - else - { - // Insert to the head of the right leaf instead and change our key - returnAction->key1=cur->children[branchIndex+1]->keys[0]; - InsertIntoNode(key, data, 0, 0, cur->children[branchIndex+1], 0); - returnAction->key2=key; - } - cur->keys[branchIndex]=cur->children[branchIndex+1]->keys[0]; - return 0; - } - } - - newPage=InsertBranchDown(key,data,cur->children[branchIndex], returnAction, success); - if (returnAction->action==ReturnAction::REPLACE_KEY1_WITH_KEY2) - { - if (branchIndex>0 && cur->keys[branchIndex-1]==returnAction->key1) - cur->keys[branchIndex-1]=returnAction->key2; - } - if (newPage) - { - if (newPage->isLeaf==false) - { - RakAssert(returnAction->action==ReturnAction::PUSH_KEY_TO_PARENT); - newPage->size--; - return InsertIntoNode(returnAction->key1, data, branchIndex, newPage, cur, returnAction); - } - else - { - return InsertIntoNode(newPage->keys[0], data, branchIndex, newPage, cur, returnAction); - } - } - } - else - { - if (branchIndex==childIndex+1) - { - *success=false; - return 0; // Already exists - } - else - { - return InsertIntoNode(key, data, branchIndex, 0, cur, returnAction); - } - } - - return 0; - } - template - bool BPlusTree::Insert(const KeyType key, const DataType &data) - { - if (root==0) - { - // Allocate root and make root a leaf - root = pagePool.Allocate( _FILE_AND_LINE_ ); - root->isLeaf=true; - leftmostLeaf=root; - root->size=1; - root->keys[0]=key; - root->data[0]=data; - root->next=0; - root->previous=0; - } - else - { - bool success=true; - ReturnAction returnAction; - returnAction.action=ReturnAction::NO_ACTION; - Page* newPage = InsertBranchDown(key, data, root, &returnAction, &success); - if (success==false) - return false; - if (newPage) - { - KeyType newKey; - if (newPage->isLeaf==false) - { - // One key is pushed up through the stack. I store that at keys[0] but it has to be removed for the page to be correct - RakAssert(returnAction.action==ReturnAction::PUSH_KEY_TO_PARENT); - newKey=returnAction.key1; - newPage->size--; - } - else - newKey = newPage->keys[0]; - // propagate the root - Page* newRoot = pagePool.Allocate( _FILE_AND_LINE_ ); - newRoot->isLeaf=false; - newRoot->size=1; - newRoot->keys[0]=newKey; - newRoot->children[0]=root; - newRoot->children[1]=newPage; - root=newRoot; - } - } - - return true; - } - template - void BPlusTree::ShiftKeysLeft(Page *cur) - { - int i; - for (i=0; i < cur->size; i++) - cur->keys[i]=cur->keys[i+1]; - } - template - void BPlusTree::Clear(void) - { - if (root) - { - FreePages(); - leftmostLeaf=0; - root=0; - } - pagePool.Clear(_FILE_AND_LINE_); - } - template - unsigned BPlusTree::Size(void) const - { - int count=0; - DataStructures::Page *cur = GetListHead(); - while (cur) - { - count+=cur->size; - cur=cur->next; - } - return count; - } - template - bool BPlusTree::IsEmpty(void) const - { - return root==0; - } - template - bool BPlusTree::GetIndexOf(const KeyType key, Page *page, int *out) const - { - RakAssert(page->size>0); - int index, upperBound, lowerBound; - upperBound=page->size-1; - lowerBound=0; - index = page->size/2; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - if (key==page->keys[index]) - { - *out=index; - return true; - } - else if (keykeys[index]) - upperBound=index-1; - else - lowerBound=index+1; - - index=lowerBound+(upperBound-lowerBound)/2; - - if (lowerBound>upperBound) - { - *out=lowerBound; - return false; // No match - } - } - } - template - void BPlusTree::FreePages(void) - { - DataStructures::Queue *> queue; - DataStructures::Page *ptr; - int i; - queue.Push(root, _FILE_AND_LINE_ ); - while (queue.Size()) - { - ptr=queue.Pop(); - if (ptr->isLeaf==false) - { - for (i=0; i < ptr->size+1; i++) - queue.Push(ptr->children[i], _FILE_AND_LINE_ ); - } - pagePool.Release(ptr, _FILE_AND_LINE_); - // memset(ptr,0,sizeof(root)); - }; - } - template - Page *BPlusTree::GetListHead(void) const - { - return leftmostLeaf; - } - template - DataType BPlusTree::GetDataHead(void) const - { - return leftmostLeaf->data[0]; - } - template - void BPlusTree::ForEachLeaf(void (*func)(Page * leaf, int index)) - { - int count=0; - DataStructures::Page *cur = GetListHead(); - while (cur) - { - func(cur, count++); - cur=cur->next; - } - } - template - void BPlusTree::ForEachData(void (*func)(DataType input, int index)) - { - int count=0,i; - DataStructures::Page *cur = GetListHead(); - while (cur) - { - for (i=0; i < cur->size; i++) - func(cur->data[i], count++); - cur=cur->next; - } - } - template - void BPlusTree::PrintLeaf(Page * leaf, int index) - { - int i; - RAKNET_DEBUG_PRINTF("%i] SELF=%p\n", index+1, leaf); - for (i=0; i < leaf->size; i++) - RAKNET_DEBUG_PRINTF(" %i. %i\n", i+1, leaf->data[i]); - } - template - void BPlusTree::PrintLeaves(void) - { - ForEachLeaf(PrintLeaf); - } - - template - void BPlusTree::ValidateTreeRecursive(Page *cur) - { - RakAssert(cur==root || cur->size>=order/2); - - if (cur->children[0]->isLeaf) - { - RakAssert(cur->children[0]->keys[0] < cur->keys[0]); - for (int i=0; i < cur->size; i++) - { - RakAssert(cur->children[i+1]->keys[0]==cur->keys[i]); - } - } - else - { - for (int i=0; i < cur->size+1; i++) - ValidateTreeRecursive(cur->children[i]); - } - } - - template - void BPlusTree::PrintGraph(void) - { - DataStructures::Queue *> queue; - queue.Push(root,_FILE_AND_LINE_); - queue.Push(0,_FILE_AND_LINE_); - DataStructures::Page *ptr; - int i,j; - if (root) - { - RAKNET_DEBUG_PRINTF("%p(", root); - for (i=0; i < root->size; i++) - { - RAKNET_DEBUG_PRINTF("%i ", root->keys[i]); - } - RAKNET_DEBUG_PRINTF(") "); - RAKNET_DEBUG_PRINTF("\n"); - } - while (queue.Size()) - { - ptr=queue.Pop(); - if (ptr==0) - RAKNET_DEBUG_PRINTF("\n"); - else if (ptr->isLeaf==false) - { - for (i=0; i < ptr->size+1; i++) - { - RAKNET_DEBUG_PRINTF("%p(", ptr->children[i]); - //RAKNET_DEBUG_PRINTF("(", ptr->children[i]); - for (j=0; j < ptr->children[i]->size; j++) - RAKNET_DEBUG_PRINTF("%i ", ptr->children[i]->keys[j]); - RAKNET_DEBUG_PRINTF(") "); - queue.Push(ptr->children[i],_FILE_AND_LINE_); - } - queue.Push(0,_FILE_AND_LINE_); - RAKNET_DEBUG_PRINTF(" -- "); - } - } - RAKNET_DEBUG_PRINTF("\n"); - } -} -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - -// Code to test this hellish data structure. -/* -#include "DS_BPlusTree.h" -#include - -// Handle underflow on root. If there is only one item left then I can go downwards. -// Make sure I keep the leftmost pointer valid by traversing it -// When I free a leaf, be sure to adjust the pointers around it. - -#include "Rand.h" - -void main(void) -{ - DataStructures::BPlusTree btree; - DataStructures::List haveList, removedList; - int temp; - int i, j, index; - int testSize; - bool b; - - for (testSize=0; testSize < 514; testSize++) - { - RAKNET_DEBUG_PRINTF("TestSize=%i\n", testSize); - - for (i=0; i < testSize; i++) - haveList.Insert(i); - - for (i=0; i < testSize; i++) - { - index=i+randomMT()%(testSize-i); - temp=haveList[index]; - haveList[index]=haveList[i]; - haveList[i]=temp; - } - - for (i=0; i +#include "Export.h" + +// Java +// http://www.seanster.com/BplusTree/BplusTree.html + +// Overview +// http://babbage.clarku.edu/~achou/cs160/B+Trees/B+Trees.htm + +// Deletion +// http://dbpubs.stanford.edu:8090/pub/1995-19 + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// Used in the BPlusTree. Used for both leaf and index nodes. + /// Don't use a constructor or destructor, due to the memory pool I am using + template + struct RAK_DLL_EXPORT Page + { + // We use the same data structure for both leaf and index nodes. + // It uses a little more memory for index nodes but reduces + // memory fragmentation, allocations, and deallocations. + bool isLeaf; + + // Used for both leaf and index nodes. + // For a leaf it means the number of elements in data + // For an index it means the number of keys and is one less than the number of children pointers. + int size; + + // Used for both leaf and index nodes. + KeyType keys[order]; + + // Used only for leaf nodes. Data is the actual data, while next is the pointer to the next leaf (for B+) + DataType data[order]; + Page *next; + Page *previous; + + // Used only for index nodes. Pointers to the children of this node. + Page *children[order+1]; + }; + + /// A BPlus tree + /// Written with efficiency and speed in mind. + template + class RAK_DLL_EXPORT BPlusTree + { + public: + struct ReturnAction + { + KeyType key1; + KeyType key2; + enum + { + NO_ACTION, + REPLACE_KEY1_WITH_KEY2, + PUSH_KEY_TO_PARENT, + SET_BRANCH_KEY, + } action; // 0=none, 1=replace key1 with key2 + }; + + BPlusTree(); + ~BPlusTree(); + void SetPoolPageSize(int size); // Set the page size for the memory pool. Optionsl + bool Get(const KeyType key, DataType &out) const; + bool Delete(const KeyType key); + bool Delete(const KeyType key, DataType &out); + bool Insert(const KeyType key, const DataType &data); + void Clear(void); + unsigned Size(void) const; + bool IsEmpty(void) const; + Page *GetListHead(void) const; + DataType GetDataHead(void) const; + void PrintLeaves(void); + void ForEachLeaf(void (*func)(Page * leaf, int index)); + void ForEachData(void (*func)(DataType input, int index)); + void PrintGraph(void); + protected: + void ValidateTreeRecursive(Page *cur); + void DeleteFromPageAtIndex(const int index, Page *cur); + static void PrintLeaf(Page * leaf, int index); + void FreePages(void); + bool GetIndexOf(const KeyType key, Page *page, int *out) const; + void ShiftKeysLeft(Page *cur); + bool CanRotateLeft(Page *cur, int childIndex); + bool CanRotateRight(Page *cur, int childIndex); + void RotateRight(Page *cur, int childIndex, ReturnAction *returnAction); + void RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction); + Page* InsertIntoNode(const KeyType key, const DataType &childData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction); + Page* InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction* returnAction, bool *success); + Page* GetLeafFromKey(const KeyType key) const; + bool FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out); + bool FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction); + void ShiftNodeLeft(Page *cur); + void ShiftNodeRight(Page *cur); + + MemoryPool > pagePool; + Page *root, *leftmostLeaf; + }; + + template + BPlusTree::BPlusTree () + { + RakAssert(order>1); + root=0; + leftmostLeaf=0; + } + template + BPlusTree::~BPlusTree () + { + Clear(); + } + template + void BPlusTree::SetPoolPageSize(int size) + { + pagePool.SetPageSize(size); + } + template + bool BPlusTree::Get(const KeyType key, DataType &out) const + { + if (root==0) + return false; + + Page* leaf = GetLeafFromKey(key); + int childIndex; + + if (GetIndexOf(key, leaf, &childIndex)) + { + out=leaf->data[childIndex]; + return true; + } + return false; + } + template + void BPlusTree::DeleteFromPageAtIndex(const int index, Page *cur) + { + int i; + for (i=index; i < cur->size-1; i++) + cur->keys[i]=cur->keys[i+1]; + if (cur->isLeaf) + { + for (i=index; i < cur->size-1; i++) + cur->data[i]=cur->data[i+1]; + } + else + { + for (i=index; i < cur->size-1; i++) + cur->children[i+1]=cur->children[i+2]; + } + cur->size--; + } + template + bool BPlusTree::Delete(const KeyType key) + { + DataType temp; + return Delete(key, temp); + } + template + bool BPlusTree::Delete(const KeyType key, DataType &out) + { + if (root==0) + return false; + + ReturnAction returnAction; + returnAction.action=ReturnAction::NO_ACTION; + int childIndex; + bool underflow=false; + if (root==leftmostLeaf) + { + if (GetIndexOf(key, root, &childIndex)==false) + return false; + out=root->data[childIndex]; + DeleteFromPageAtIndex(childIndex,root); + if (root->size==0) + { + pagePool.Release(root, _FILE_AND_LINE_); + root=0; + leftmostLeaf=0; + } + return true; + } + else if (FindDeleteRebalance(key, root, &underflow,root->keys[0], &returnAction, out)==false) + return false; + +// RakAssert(returnAction.action==ReturnAction::NO_ACTION); + + if (underflow && root->size==0) + { + // Move the root down. + Page *oldRoot=root; + root=root->children[0]; + pagePool.Release(oldRoot, _FILE_AND_LINE_); + // memset(oldRoot,0,sizeof(root)); + } + + return true; + } + template + bool BPlusTree::FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out) + { + // Get index of child to follow. + int branchIndex, childIndex; + if (GetIndexOf(key, cur, &childIndex)) + branchIndex=childIndex+1; + else + branchIndex=childIndex; + + // If child is not a leaf, call recursively + if (cur->children[branchIndex]->isLeaf==false) + { + if (branchIndexsize) + rightRootKey=cur->keys[branchIndex]; // Shift right to left + else + rightRootKey=cur->keys[branchIndex-1]; // Shift center to left + + if (FindDeleteRebalance(key, cur->children[branchIndex], underflow, rightRootKey, returnAction, out)==false) + return false; + + // Call again in case the root key changed + if (branchIndexsize) + rightRootKey=cur->keys[branchIndex]; // Shift right to left + else + rightRootKey=cur->keys[branchIndex-1]; // Shift center to left + + if (returnAction->action==ReturnAction::SET_BRANCH_KEY && branchIndex!=childIndex) + { + returnAction->action=ReturnAction::NO_ACTION; + cur->keys[childIndex]=returnAction->key1; + + if (branchIndexsize) + rightRootKey=cur->keys[branchIndex]; // Shift right to left + else + rightRootKey=cur->keys[branchIndex-1]; // Shift center to left + } + } + else + { + // If child is a leaf, get the index of the key. If the item is not found, cancel delete. + if (GetIndexOf(key, cur->children[branchIndex], &childIndex)==false) + return false; + + // Delete: + // Remove childIndex from the child at branchIndex + out=cur->children[branchIndex]->data[childIndex]; + DeleteFromPageAtIndex(childIndex, cur->children[branchIndex]); + + if (childIndex==0) + { + if (branchIndex>0) + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + + if (branchIndex==0) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=cur->children[0]->keys[0]; + } + } + + if (cur->children[branchIndex]->size < order/2) + *underflow=true; + else + *underflow=false; + } + + // Fix underflow: + if (*underflow) + { + *underflow=FixUnderflow(branchIndex, cur, rightRootKey, returnAction); + } + + return true; + } + template + bool BPlusTree::FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction) + { + // Borrow from a neighbor that has excess. + Page *source; + Page *dest; + + if (branchIndex>0 && cur->children[branchIndex-1]->size > order/2) + { + dest=cur->children[branchIndex]; + source=cur->children[branchIndex-1]; + + // Left has excess + ShiftNodeRight(dest); + if (dest->isLeaf) + { + dest->keys[0]=source->keys[source->size-1]; + dest->data[0]=source->data[source->size-1]; + } + else + { + dest->children[0]=source->children[source->size]; + dest->keys[0]=cur->keys[branchIndex-1]; + } + // Update the parent key for the child (middle) + cur->keys[branchIndex-1]=source->keys[source->size-1]; + source->size--; + + // if (branchIndex==0) + // { + // returnAction->action=ReturnAction::SET_BRANCH_KEY; + // returnAction->key1=dest->keys[0]; + // } + + // No underflow + return false; + } + else if (branchIndexsize && cur->children[branchIndex+1]->size > order/2) + { + dest=cur->children[branchIndex]; + source=cur->children[branchIndex+1]; + + // Right has excess + if (dest->isLeaf) + { + dest->keys[dest->size]=source->keys[0]; + dest->data[dest->size]=source->data[0]; + + // The first key in the leaf after shifting is the parent key for the right branch + cur->keys[branchIndex]=source->keys[1]; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (order<=3 && dest->size==0) + { + if (branchIndex==0) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=dest->keys[0]; + } + else + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + } + } + else + { + if (returnAction->action==ReturnAction::NO_ACTION) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=dest->keys[0]; + } + + dest->keys[dest->size]=rightRootKey; + dest->children[dest->size+1]=source->children[0]; + + // The shifted off key is the leftmost key for a node + cur->keys[branchIndex]=source->keys[0]; + } + + + dest->size++; + ShiftNodeLeft(source); + + //cur->keys[branchIndex]=source->keys[0]; + +// returnAction->action=ReturnAction::SET_BRANCH_KEY; +// returnAction->key1=dest->keys[dest->size-1]; + + // No underflow + return false; + } + else + { + int sourceIndex; + + // If no neighbors have excess, merge two branches. + // + // To merge two leaves, just copy the data and keys over. + // + // To merge two branches, copy the pointers and keys over, using rightRootKey as the key for the extra pointer + if (branchIndexsize) + { + // Merge right child to current child and delete right child. + dest=cur->children[branchIndex]; + source=cur->children[branchIndex+1]; + } + else + { + // Move current child to left and delete current child + dest=cur->children[branchIndex-1]; + source=cur->children[branchIndex]; + } + + // Merge + if (dest->isLeaf) + { + for (sourceIndex=0; sourceIndexsize; sourceIndex++) + { + dest->keys[dest->size]=source->keys[sourceIndex]; + dest->data[dest->size++]=source->data[sourceIndex]; + } + } + else + { + // We want the tree root key of the source, not the current. + dest->keys[dest->size]=rightRootKey; + dest->children[dest->size++ + 1]=source->children[0]; + for (sourceIndex=0; sourceIndexsize; sourceIndex++) + { + dest->keys[dest->size]=source->keys[sourceIndex]; + dest->children[dest->size++ + 1]=source->children[sourceIndex + 1]; + } + } + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (order<=3 && branchIndex>0 && cur->children[branchIndex]->isLeaf) // With order==2 it is possible to delete data[0], which is not possible with higher orders. + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + + if (branchIndexsize) + { + // Update the parent key, removing the source (right) + DeleteFromPageAtIndex(branchIndex, cur); + } + else + { + if (branchIndex>0) + { + // Update parent key, removing the source (current) + DeleteFromPageAtIndex(branchIndex-1, cur); + } + } + + if (branchIndex==0 && dest->isLeaf) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=dest->keys[0]; + } + + if (source==leftmostLeaf) + leftmostLeaf=source->next; + + if (source->isLeaf) + { + if (source->previous) + source->previous->next=source->next; + if (source->next) + source->next->previous=source->previous; + } + + // Free the source node + pagePool.Release(source, _FILE_AND_LINE_); + // memset(source,0,sizeof(root)); + + // Return underflow or not of parent. + return cur->size < order/2; + } + } + template + void BPlusTree::ShiftNodeRight(Page *cur) + { + int i; + for (i=cur->size; i>0; i--) + cur->keys[i]=cur->keys[i-1]; + if (cur->isLeaf) + { + for (i=cur->size; i>0; i--) + cur->data[i]=cur->data[i-1]; + } + else + { + for (i=cur->size+1; i>0; i--) + cur->children[i]=cur->children[i-1]; + } + + cur->size++; + } + template + void BPlusTree::ShiftNodeLeft(Page *cur) + { + int i; + for (i=0; i < cur->size-1; i++) + cur->keys[i]=cur->keys[i+1]; + if (cur->isLeaf) + { + for (i=0; i < cur->size; i++) + cur->data[i]=cur->data[i+1]; + } + else + { + for (i=0; i < cur->size; i++) + cur->children[i]=cur->children[i+1]; + } + cur->size--; + } + template + Page* BPlusTree::InsertIntoNode(const KeyType key, const DataType &leafData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction) + { + int i; + if (cur->size < order) + { + for (i=cur->size; i > insertionIndex; i--) + cur->keys[i]=cur->keys[i-1]; + if (cur->isLeaf) + { + for (i=cur->size; i > insertionIndex; i--) + cur->data[i]=cur->data[i-1]; + } + else + { + for (i=cur->size+1; i > insertionIndex+1; i--) + cur->children[i]=cur->children[i-1]; + } + cur->keys[insertionIndex]=key; + if (cur->isLeaf) + cur->data[insertionIndex]=leafData; + else + cur->children[insertionIndex+1]=nodeData; + + cur->size++; + } + else + { + Page* newPage = pagePool.Allocate( _FILE_AND_LINE_ ); + newPage->isLeaf=cur->isLeaf; + if (cur->isLeaf) + { + newPage->next=cur->next; + if (cur->next) + cur->next->previous=newPage; + newPage->previous=cur; + cur->next=newPage; + } + + int destIndex, sourceIndex; + + if (insertionIndex>=(order+1)/2) + { + destIndex=0; + sourceIndex=order/2; + + for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) + { + newPage->keys[destIndex]=cur->keys[sourceIndex]; + } + newPage->keys[destIndex++]=key; + for (; sourceIndex < order; sourceIndex++, destIndex++) + { + newPage->keys[destIndex]=cur->keys[sourceIndex]; + } + + destIndex=0; + sourceIndex=order/2; + if (cur->isLeaf) + { + for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) + { + newPage->data[destIndex]=cur->data[sourceIndex]; + } + newPage->data[destIndex++]=leafData; + for (; sourceIndex < order; sourceIndex++, destIndex++) + { + newPage->data[destIndex]=cur->data[sourceIndex]; + } + } + else + { + + for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) + { + newPage->children[destIndex]=cur->children[sourceIndex+1]; + } + newPage->children[destIndex++]=nodeData; + + // sourceIndex+1 is sort of a hack but it works - because there is one extra child than keys + // skip past the last child for cur + for (; sourceIndex+1 < cur->size+1; sourceIndex++, destIndex++) + { + newPage->children[destIndex]=cur->children[sourceIndex+1]; + } + + // the first key is the middle key. Remove it from the page and push it to the parent + returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; + returnAction->key1=newPage->keys[0]; + for (int i=0; i < destIndex-1; i++) + newPage->keys[i]=newPage->keys[i+1]; + + } + cur->size=order/2; + } + else + { + destIndex=0; + sourceIndex=(order+1)/2-1; + for (; sourceIndex < order; sourceIndex++, destIndex++) + newPage->keys[destIndex]=cur->keys[sourceIndex]; + destIndex=0; + if (cur->isLeaf) + { + sourceIndex=(order+1)/2-1; + for (; sourceIndex < order; sourceIndex++, destIndex++) + newPage->data[destIndex]=cur->data[sourceIndex]; + } + else + { + sourceIndex=(order+1)/2; + for (; sourceIndex < order+1; sourceIndex++, destIndex++) + newPage->children[destIndex]=cur->children[sourceIndex]; + + // the first key is the middle key. Remove it from the page and push it to the parent + returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; + returnAction->key1=newPage->keys[0]; + for (int i=0; i < destIndex-1; i++) + newPage->keys[i]=newPage->keys[i+1]; + } + cur->size=(order+1)/2-1; + if (cur->size) + { + bool b = GetIndexOf(key, cur, &insertionIndex); + (void) b; + RakAssert(b==false); + } + else + insertionIndex=0; + InsertIntoNode(key, leafData, insertionIndex, nodeData, cur, returnAction); + } + + newPage->size=destIndex; + + return newPage; + } + + return 0; + } + + template + bool BPlusTree::CanRotateLeft(Page *cur, int childIndex) + { + return childIndex>0 && cur->children[childIndex-1]->size + void BPlusTree::RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction) + { + Page *dest = cur->children[childIndex-1]; + Page *source = cur->children[childIndex]; + returnAction->key1=source->keys[0]; + dest->keys[dest->size]=source->keys[0]; + dest->data[dest->size]=source->data[0]; + dest->size++; + for (int i=0; i < source->size-1; i++) + { + source->keys[i]=source->keys[i+1]; + source->data[i]=source->data[i+1]; + } + source->size--; + cur->keys[childIndex-1]=source->keys[0]; + returnAction->key2=source->keys[0]; + } + + template + bool BPlusTree::CanRotateRight(Page *cur, int childIndex) + { + return childIndex < cur->size && cur->children[childIndex+1]->size + void BPlusTree::RotateRight(Page *cur, int childIndex, ReturnAction *returnAction) + { + Page *dest = cur->children[childIndex+1]; + Page *source = cur->children[childIndex]; + returnAction->key1=dest->keys[0]; + for (int i= dest->size; i > 0; i--) + { + dest->keys[i]=dest->keys[i-1]; + dest->data[i]=dest->data[i-1]; + } + dest->keys[0]=source->keys[source->size-1]; + dest->data[0]=source->data[source->size-1]; + dest->size++; + source->size--; + + cur->keys[childIndex]=dest->keys[0]; + returnAction->key2=dest->keys[0]; + } + template + Page* BPlusTree::GetLeafFromKey(const KeyType key) const + { + Page* cur = root; + int childIndex; + while (cur->isLeaf==false) + { + // When searching, if we match the exact key we go down the pointer after that index + if (GetIndexOf(key, cur, &childIndex)) + childIndex++; + cur = cur->children[childIndex]; + } + return cur; + } + + template + Page* BPlusTree::InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction *returnAction, bool *success) + { + int childIndex; + int branchIndex; + if (GetIndexOf(key, cur, &childIndex)) + branchIndex=childIndex+1; + else + branchIndex=childIndex; + Page* newPage; + if (cur->isLeaf==false) + { + if (cur->children[branchIndex]->isLeaf==true && cur->children[branchIndex]->size==order) + { + if (branchIndex==childIndex+1) + { + *success=false; + return 0; // Already exists + } + + if (CanRotateLeft(cur, branchIndex)) + { + returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; + if (key > cur->children[branchIndex]->keys[0]) + { + RotateLeft(cur, branchIndex, returnAction); + + int insertionIndex; + GetIndexOf(key, cur->children[branchIndex], &insertionIndex); + InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); + } + else + { + // Move head element to left and replace it with key,data + Page* dest=cur->children[branchIndex-1]; + Page* source=cur->children[branchIndex]; + returnAction->key1=source->keys[0]; + returnAction->key2=key; + dest->keys[dest->size]=source->keys[0]; + dest->data[dest->size]=source->data[0]; + dest->size++; + source->keys[0]=key; + source->data[0]=data; + } + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + + return 0; + } + else if (CanRotateRight(cur, branchIndex)) + { + returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; + + if (key < cur->children[branchIndex]->keys[cur->children[branchIndex]->size-1]) + { + RotateRight(cur, branchIndex, returnAction); + + int insertionIndex; + GetIndexOf(key, cur->children[branchIndex], &insertionIndex); + InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); + + } + else + { + // Insert to the head of the right leaf instead and change our key + returnAction->key1=cur->children[branchIndex+1]->keys[0]; + InsertIntoNode(key, data, 0, 0, cur->children[branchIndex+1], 0); + returnAction->key2=key; + } + cur->keys[branchIndex]=cur->children[branchIndex+1]->keys[0]; + return 0; + } + } + + newPage=InsertBranchDown(key,data,cur->children[branchIndex], returnAction, success); + if (returnAction->action==ReturnAction::REPLACE_KEY1_WITH_KEY2) + { + if (branchIndex>0 && cur->keys[branchIndex-1]==returnAction->key1) + cur->keys[branchIndex-1]=returnAction->key2; + } + if (newPage) + { + if (newPage->isLeaf==false) + { + RakAssert(returnAction->action==ReturnAction::PUSH_KEY_TO_PARENT); + newPage->size--; + return InsertIntoNode(returnAction->key1, data, branchIndex, newPage, cur, returnAction); + } + else + { + return InsertIntoNode(newPage->keys[0], data, branchIndex, newPage, cur, returnAction); + } + } + } + else + { + if (branchIndex==childIndex+1) + { + *success=false; + return 0; // Already exists + } + else + { + return InsertIntoNode(key, data, branchIndex, 0, cur, returnAction); + } + } + + return 0; + } + template + bool BPlusTree::Insert(const KeyType key, const DataType &data) + { + if (root==0) + { + // Allocate root and make root a leaf + root = pagePool.Allocate( _FILE_AND_LINE_ ); + root->isLeaf=true; + leftmostLeaf=root; + root->size=1; + root->keys[0]=key; + root->data[0]=data; + root->next=0; + root->previous=0; + } + else + { + bool success=true; + ReturnAction returnAction; + returnAction.action=ReturnAction::NO_ACTION; + Page* newPage = InsertBranchDown(key, data, root, &returnAction, &success); + if (success==false) + return false; + if (newPage) + { + KeyType newKey; + if (newPage->isLeaf==false) + { + // One key is pushed up through the stack. I store that at keys[0] but it has to be removed for the page to be correct + RakAssert(returnAction.action==ReturnAction::PUSH_KEY_TO_PARENT); + newKey=returnAction.key1; + newPage->size--; + } + else + newKey = newPage->keys[0]; + // propagate the root + Page* newRoot = pagePool.Allocate( _FILE_AND_LINE_ ); + newRoot->isLeaf=false; + newRoot->size=1; + newRoot->keys[0]=newKey; + newRoot->children[0]=root; + newRoot->children[1]=newPage; + root=newRoot; + } + } + + return true; + } + template + void BPlusTree::ShiftKeysLeft(Page *cur) + { + int i; + for (i=0; i < cur->size; i++) + cur->keys[i]=cur->keys[i+1]; + } + template + void BPlusTree::Clear(void) + { + if (root) + { + FreePages(); + leftmostLeaf=0; + root=0; + } + pagePool.Clear(_FILE_AND_LINE_); + } + template + unsigned BPlusTree::Size(void) const + { + int count=0; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + count+=cur->size; + cur=cur->next; + } + return count; + } + template + bool BPlusTree::IsEmpty(void) const + { + return root==0; + } + template + bool BPlusTree::GetIndexOf(const KeyType key, Page *page, int *out) const + { + RakAssert(page->size>0); + int index, upperBound, lowerBound; + upperBound=page->size-1; + lowerBound=0; + index = page->size/2; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + if (key==page->keys[index]) + { + *out=index; + return true; + } + else if (keykeys[index]) + upperBound=index-1; + else + lowerBound=index+1; + + index=lowerBound+(upperBound-lowerBound)/2; + + if (lowerBound>upperBound) + { + *out=lowerBound; + return false; // No match + } + } + } + template + void BPlusTree::FreePages(void) + { + DataStructures::Queue *> queue; + DataStructures::Page *ptr; + int i; + queue.Push(root, _FILE_AND_LINE_ ); + while (queue.Size()) + { + ptr=queue.Pop(); + if (ptr->isLeaf==false) + { + for (i=0; i < ptr->size+1; i++) + queue.Push(ptr->children[i], _FILE_AND_LINE_ ); + } + pagePool.Release(ptr, _FILE_AND_LINE_); + // memset(ptr,0,sizeof(root)); + }; + } + template + Page *BPlusTree::GetListHead(void) const + { + return leftmostLeaf; + } + template + DataType BPlusTree::GetDataHead(void) const + { + return leftmostLeaf->data[0]; + } + template + void BPlusTree::ForEachLeaf(void (*func)(Page * leaf, int index)) + { + int count=0; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + func(cur, count++); + cur=cur->next; + } + } + template + void BPlusTree::ForEachData(void (*func)(DataType input, int index)) + { + int count=0,i; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + for (i=0; i < cur->size; i++) + func(cur->data[i], count++); + cur=cur->next; + } + } + template + void BPlusTree::PrintLeaf(Page * leaf, int index) + { + int i; + RAKNET_DEBUG_PRINTF("%i] SELF=%p\n", index+1, leaf); + for (i=0; i < leaf->size; i++) + RAKNET_DEBUG_PRINTF(" %i. %i\n", i+1, leaf->data[i]); + } + template + void BPlusTree::PrintLeaves(void) + { + ForEachLeaf(PrintLeaf); + } + + template + void BPlusTree::ValidateTreeRecursive(Page *cur) + { + RakAssert(cur==root || cur->size>=order/2); + + if (cur->children[0]->isLeaf) + { + RakAssert(cur->children[0]->keys[0] < cur->keys[0]); + for (int i=0; i < cur->size; i++) + { + RakAssert(cur->children[i+1]->keys[0]==cur->keys[i]); + } + } + else + { + for (int i=0; i < cur->size+1; i++) + ValidateTreeRecursive(cur->children[i]); + } + } + + template + void BPlusTree::PrintGraph(void) + { + DataStructures::Queue *> queue; + queue.Push(root,_FILE_AND_LINE_); + queue.Push(0,_FILE_AND_LINE_); + DataStructures::Page *ptr; + int i,j; + if (root) + { + RAKNET_DEBUG_PRINTF("%p(", root); + for (i=0; i < root->size; i++) + { + RAKNET_DEBUG_PRINTF("%i ", root->keys[i]); + } + RAKNET_DEBUG_PRINTF(") "); + RAKNET_DEBUG_PRINTF("\n"); + } + while (queue.Size()) + { + ptr=queue.Pop(); + if (ptr==0) + RAKNET_DEBUG_PRINTF("\n"); + else if (ptr->isLeaf==false) + { + for (i=0; i < ptr->size+1; i++) + { + RAKNET_DEBUG_PRINTF("%p(", ptr->children[i]); + //RAKNET_DEBUG_PRINTF("(", ptr->children[i]); + for (j=0; j < ptr->children[i]->size; j++) + RAKNET_DEBUG_PRINTF("%i ", ptr->children[i]->keys[j]); + RAKNET_DEBUG_PRINTF(") "); + queue.Push(ptr->children[i],_FILE_AND_LINE_); + } + queue.Push(0,_FILE_AND_LINE_); + RAKNET_DEBUG_PRINTF(" -- "); + } + } + RAKNET_DEBUG_PRINTF("\n"); + } +} +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + +// Code to test this hellish data structure. +/* +#include "DS_BPlusTree.h" +#include + +// Handle underflow on root. If there is only one item left then I can go downwards. +// Make sure I keep the leftmost pointer valid by traversing it +// When I free a leaf, be sure to adjust the pointers around it. + +#include "Rand.h" + +void main(void) +{ + DataStructures::BPlusTree btree; + DataStructures::List haveList, removedList; + int temp; + int i, j, index; + int testSize; + bool b; + + for (testSize=0; testSize < 514; testSize++) + { + RAKNET_DEBUG_PRINTF("TestSize=%i\n", testSize); + + for (i=0; i < testSize; i++) + haveList.Insert(i); + + for (i=0; i < testSize; i++) + { + index=i+randomMT()%(testSize-i); + temp=haveList[index]; + haveList[index]=haveList[i]; + haveList[i]=temp; + } + + for (i=0; i - * - * OR - * - * AVLBalancedBinarySearchTree - * - * Use the AVL balanced tree if you want the tree to be balanced after every deletion and addition. This avoids the potential - * worst case scenario where ordered input to a binary search tree gives linear search time results. It's not needed - * if input will be evenly distributed, in which case the search time is O (log n). The search time for the AVL - * balanced binary tree is O (log n) irregardless of input. - * - * Has the following member functions - * unsigned int Height() - Returns the height of the tree at the optional specified starting index. Default is the root - * add(element) - adds an element to the BinarySearchTree - * bool del(element) - deletes the node containing element if the element is in the tree as defined by a comparison with the == operator. Returns true on success, false if the element is not found - * bool IsInelement) - returns true if element is in the tree as defined by a comparison with the == operator. Otherwise returns false - * DisplayInorder(array) - Fills an array with an inorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. - * DisplayPreorder(array) - Fills an array with an preorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. - * DisplayPostorder(array) - Fills an array with an postorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. - * DisplayBreadthFirstSearch(array) - Fills an array with a breadth first search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. - * clear - Destroys the tree. Same as calling the destructor - * unsigned int Height() - Returns the height of the tree - * unsigned int size() - returns the size of the BinarySearchTree - * GetPointerToNode(element) - returns a pointer to the comparision element in the tree, allowing for direct modification when necessary with complex data types. - * Be warned, it is possible to corrupt the tree if the element used for comparisons is modified. Returns NULL if the item is not found - * - * - * EXAMPLE - * @code - * BinarySearchTree A; - * A.Add(10); - * A.Add(15); - * A.Add(5); - * int* array = RakNet::OP_NEW(A.Size(), _FILE_AND_LINE_ ); - * A.DisplayInorder(array); - * array[0]; // returns 5 - * array[1]; // returns 10 - * array[2]; // returns 15 - * @endcode - * compress - reallocates memory to fit the number of elements. Best used when the number of elements decreases - * - * clear - empties the BinarySearchTree and returns storage - * The assignment and copy constructors are defined - * - * \note The template type must have the copy constructor and - * assignment operator defined and must work with >, <, and == All - * elements in the tree MUST be distinct The assignment operator is - * defined between BinarySearchTree and AVLBalancedBinarySearchTree - * as long as they are of the same template type. However, passing a - * BinarySearchTree to an AVLBalancedBinarySearchTree will lose its - * structure unless it happened to be AVL balanced to begin with - * Requires queue_linked_list.cpp for the breadth first search used - * in the copy constructor, overloaded assignment operator, and - * display_breadth_first_search. - * - * - */ - template - class RAK_DLL_EXPORT BinarySearchTree - { - - public: - - struct node - { - BinarySearchTreeType* item; - node* left; - node* right; - }; - - BinarySearchTree(); - virtual ~BinarySearchTree(); - BinarySearchTree( const BinarySearchTree& original_type ); - BinarySearchTree& operator= ( const BinarySearchTree& original_copy ); - unsigned int Size( void ); - void Clear( const char *file, unsigned int line ); - unsigned int Height( node* starting_node = 0 ); - node* Add ( const BinarySearchTreeType& input, const char *file, unsigned int line ); - node* Del( const BinarySearchTreeType& input, const char *file, unsigned int line ); - bool IsIn( const BinarySearchTreeType& input ); - void DisplayInorder( BinarySearchTreeType* return_array ); - void DisplayPreorder( BinarySearchTreeType* return_array ); - void DisplayPostorder( BinarySearchTreeType* return_array ); - void DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ); - BinarySearchTreeType*& GetPointerToNode( const BinarySearchTreeType& element ); - - protected: - - node* root; - - enum Direction_Types - { - NOT_FOUND, LEFT, RIGHT, ROOT - } direction; - unsigned int HeightRecursive( node* current ); - unsigned int BinarySearchTree_size; - node*& Find( const BinarySearchTreeType& element, node** parent ); - node*& FindParent( const BinarySearchTreeType& element ); - void DisplayPostorderRecursive( node* current, BinarySearchTreeType* return_array, unsigned int& index ); - void FixTree( node* current ); - - }; - - /// An AVLBalancedBinarySearchTree is a binary tree that is always balanced - template - class RAK_DLL_EXPORT AVLBalancedBinarySearchTree : public BinarySearchTree - { - - public: - AVLBalancedBinarySearchTree() {} - virtual ~AVLBalancedBinarySearchTree(); - void Add ( const BinarySearchTreeType& input ); - void Del( const BinarySearchTreeType& input ); - BinarySearchTree& operator= ( BinarySearchTree& original_copy ) - { - return BinarySearchTree::operator= ( original_copy ); - } - - private: - void BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ); - void RotateRight( typename BinarySearchTree::node *C ); - void RotateLeft( typename BinarySearchTree::node* C ); - void DoubleRotateRight( typename BinarySearchTree::node *A ); - void DoubleRotateLeft( typename BinarySearchTree::node* A ); - bool RightHigher( typename BinarySearchTree::node* A ); - bool LeftHigher( typename BinarySearchTree::node* A ); - }; - - template - void AVLBalancedBinarySearchTree::BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ) - { - int left_height, right_height; - - while ( current ) - { - if ( current->left == 0 ) - left_height = 0; - else - left_height = Height( current->left ); - - if ( current->right == 0 ) - right_height = 0; - else - right_height = Height( current->right ); - - if ( right_height - left_height == 2 ) - { - if ( RightHigher( current->right ) ) - RotateLeft( current->right ); - else - DoubleRotateLeft( current ); - - if ( rotateOnce ) - break; - } - - else - if ( right_height - left_height == -2 ) - { - if ( LeftHigher( current->left ) ) - RotateRight( current->left ); - else - DoubleRotateRight( current ); - - if ( rotateOnce ) - break; - } - - if ( current == this->root ) - break; - - current = FindParent( *( current->item ) ); - - } - } - - template - void AVLBalancedBinarySearchTree::Add ( const BinarySearchTreeType& input ) - { - - typename BinarySearchTree::node * current = BinarySearchTree::Add ( input, _FILE_AND_LINE_ ); - BalanceTree( current, true ); - } - - template - void AVLBalancedBinarySearchTree::Del( const BinarySearchTreeType& input ) - { - typename BinarySearchTree::node * current = BinarySearchTree::Del( input, _FILE_AND_LINE_ ); - BalanceTree( current, false ); - - } - - template - bool AVLBalancedBinarySearchTree::RightHigher( typename BinarySearchTree::node *A ) - { - if ( A == 0 ) - return false; - - return Height( A->right ) > Height( A->left ); - } - - template - bool AVLBalancedBinarySearchTree::LeftHigher( typename BinarySearchTree::node *A ) - { - if ( A == 0 ) - return false; - - return Height( A->left ) > Height( A->right ); - } - - template - void AVLBalancedBinarySearchTree::RotateRight( typename BinarySearchTree::node *C ) - { - typename BinarySearchTree::node * A, *B, *D; - /* - RIGHT ROTATION - - A = parent(b) - b= parent(c) - c = node to rotate around - - A - | // Either direction - B - / \ - C - / \ - D - - TO - - A - | // Either Direction - C - / \ - B - / \ - D - - - - - */ - - B = FindParent( *( C->item ) ); - A = FindParent( *( B->item ) ); - D = C->right; - - if ( A ) - { - // Direction was set by the last find_parent call - - if ( this->direction == this->LEFT ) - A->left = C; - else - A->right = C; - } - - else - this->root = C; // If B has no parent parent then B must have been the root node - - B->left = D; - - C->right = B; - } - - template - void AVLBalancedBinarySearchTree::DoubleRotateRight( typename BinarySearchTree::node *A ) - { - // The left side of the left child must be higher for the tree to balance with a right rotation. If it isn't, rotate it left before the normal rotation so it is. - RotateLeft( A->left->right ); - RotateRight( A->left ); - } - - template - void AVLBalancedBinarySearchTree::RotateLeft( typename BinarySearchTree::node *C ) - { - typename BinarySearchTree::node * A, *B, *D; - /* - RIGHT ROTATION - - A = parent(b) - b= parent(c) - c = node to rotate around - - A - | // Either direction - B - / \ - C - / \ - D - - TO - - A - | // Either Direction - C - / \ - B - / \ - D - - - - - */ - - B = FindParent( *( C->item ) ); - A = FindParent( *( B->item ) ); - D = C->left; - - if ( A ) - { - // Direction was set by the last find_parent call - - if ( this->direction == this->LEFT ) - A->left = C; - else - A->right = C; - } - - else - this->root = C; // If B has no parent parent then B must have been the root node - - B->right = D; - - C->left = B; - } - - template - void AVLBalancedBinarySearchTree::DoubleRotateLeft( typename BinarySearchTree::node *A ) - { - // The left side of the right child must be higher for the tree to balance with a left rotation. If it isn't, rotate it right before the normal rotation so it is. - RotateRight( A->right->left ); - RotateLeft( A->right ); - } - - template - AVLBalancedBinarySearchTree::~AVLBalancedBinarySearchTree() - { - this->Clear(_FILE_AND_LINE_); - } - - template - unsigned int BinarySearchTree::Size( void ) - { - return BinarySearchTree_size; - } - - template - unsigned int BinarySearchTree::Height( typename BinarySearchTree::node* starting_node ) - { - if ( BinarySearchTree_size == 0 || starting_node == 0 ) - return 0; - else - return HeightRecursive( starting_node ); - } - - // Recursively return the height of a binary tree - template - unsigned int BinarySearchTree::HeightRecursive( typename BinarySearchTree::node* current ) - { - unsigned int left_height = 0, right_height = 0; - - if ( ( current->left == 0 ) && ( current->right == 0 ) ) - return 1; // Leaf - - if ( current->left != 0 ) - left_height = 1 + HeightRecursive( current->left ); - - if ( current->right != 0 ) - right_height = 1 + HeightRecursive( current->right ); - - if ( left_height > right_height ) - return left_height; - else - return right_height; - } - - template - BinarySearchTree::BinarySearchTree() - { - BinarySearchTree_size = 0; - root = 0; - } - - template - BinarySearchTree::~BinarySearchTree() - { - this->Clear(_FILE_AND_LINE_); - } - - template - BinarySearchTreeType*& BinarySearchTree::GetPointerToNode( const BinarySearchTreeType& element ) - { - static typename BinarySearchTree::node * tempnode; - static BinarySearchTreeType* dummyptr = 0; - tempnode = Find ( element, &tempnode ); - - if ( this->direction == this->NOT_FOUND ) - return dummyptr; - - return tempnode->item; - } - - template - typename BinarySearchTree::node*& BinarySearchTree::Find( const BinarySearchTreeType& element, typename BinarySearchTree::node** parent ) - { - static typename BinarySearchTree::node * current; - - current = this->root; - *parent = 0; - this->direction = this->ROOT; - - if ( BinarySearchTree_size == 0 ) - { - this->direction = this->NOT_FOUND; - return current = 0; - } - - // Check if the item is at the root - if ( element == *( current->item ) ) - { - this->direction = this->ROOT; - return current; - } - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( true ) - { - // Move pointer - - if ( element < *( current->item ) ) - { - *parent = current; - this->direction = this->LEFT; - current = current->left; - } - - else - if ( element > *( current->item ) ) - { - *parent = current; - this->direction = this->RIGHT; - current = current->right; - } - - if ( current == 0 ) - break; - - // Check if new position holds the item - if ( element == *( current->item ) ) - { - return current; - } - } - - - this->direction = this->NOT_FOUND; - return current = 0; - } - - template - typename BinarySearchTree::node*& BinarySearchTree::FindParent( const BinarySearchTreeType& element ) - { - static typename BinarySearchTree::node * parent; - Find ( element, &parent ); - return parent; - } - - // Performs a series of value swaps starting with current to fix the tree if needed - template - void BinarySearchTree::FixTree( typename BinarySearchTree::node* current ) - { - BinarySearchTreeType temp; - - while ( 1 ) - { - if ( ( ( current->left ) != 0 ) && ( *( current->item ) < *( current->left->item ) ) ) - { - // Swap the current value with the one to the left - temp = *( current->left->item ); - *( current->left->item ) = *( current->item ); - *( current->item ) = temp; - current = current->left; - } - - else - if ( ( ( current->right ) != 0 ) && ( *( current->item ) > *( current->right->item ) ) ) - { - // Swap the current value with the one to the right - temp = *( current->right->item ); - *( current->right->item ) = *( current->item ); - *( current->item ) = temp; - current = current->right; - } - - else - break; // current points to the right place so quit - } - } - - template - typename BinarySearchTree::node* BinarySearchTree::Del( const BinarySearchTreeType& input, const char *file, unsigned int line ) - { - typename BinarySearchTree::node * node_to_delete, *current, *parent; - - if ( BinarySearchTree_size == 0 ) - return 0; - - if ( BinarySearchTree_size == 1 ) - { - Clear(file, line); - return 0; - } - - node_to_delete = Find( input, &parent ); - - if ( direction == NOT_FOUND ) - return 0; // Couldn't find the element - - current = node_to_delete; - - // Replace the deleted node with the appropriate value - if ( ( current->right ) == 0 && ( current->left ) == 0 ) // Leaf node, just remove it - { - - if ( parent ) - { - if ( direction == LEFT ) - parent->left = 0; - else - parent->right = 0; - } - - RakNet::OP_DELETE(node_to_delete->item, file, line); - RakNet::OP_DELETE(node_to_delete, file, line); - BinarySearchTree_size--; - return parent; - } - else - if ( ( current->right ) != 0 && ( current->left ) == 0 ) // Node has only one child, delete it and cause the parent to point to that child - { - - if ( parent ) - { - if ( direction == RIGHT ) - parent->right = current->right; - else - parent->left = current->right; - } - - else - root = current->right; // Without a parent this must be the root node - - RakNet::OP_DELETE(node_to_delete->item, file, line); - - RakNet::OP_DELETE(node_to_delete, file, line); - - BinarySearchTree_size--; - - return parent; - } - else - if ( ( current->right ) == 0 && ( current->left ) != 0 ) // Node has only one child, delete it and cause the parent to point to that child - { - - if ( parent ) - { - if ( direction == RIGHT ) - parent->right = current->left; - else - parent->left = current->left; - } - - else - root = current->left; // Without a parent this must be the root node - - RakNet::OP_DELETE(node_to_delete->item, file, line); - - RakNet::OP_DELETE(node_to_delete, file, line); - - BinarySearchTree_size--; - - return parent; - } - else // Go right, then as left as far as you can - { - parent = current; - direction = RIGHT; - current = current->right; // Must have a right branch because the if statements above indicated that it has 2 branches - - while ( current->left ) - { - direction = LEFT; - parent = current; - current = current->left; - } - - // Replace the value held by the node to RakNet::OP_DELETE(with the value pointed to by current, _FILE_AND_LINE_); - *( node_to_delete->item ) = *( current->item ); - - // Delete current. - // If it is a leaf node just delete it - if ( current->right == 0 ) - { - if ( direction == RIGHT ) - parent->right = 0; - else - parent->left = 0; - - RakNet::OP_DELETE(current->item, file, line); - - RakNet::OP_DELETE(current, file, line); - - BinarySearchTree_size--; - - return parent; - } - - else - { - // Skip this node and make its parent point to its right branch - - if ( direction == RIGHT ) - parent->right = current->right; - else - parent->left = current->right; - - RakNet::OP_DELETE(current->item, file, line); - - RakNet::OP_DELETE(current, file, line); - - BinarySearchTree_size--; - - return parent; - } - } - } - - template - typename BinarySearchTree::node* BinarySearchTree::Add ( const BinarySearchTreeType& input, const char *file, unsigned int line ) - { - typename BinarySearchTree::node * current; - - // Add the new element to the tree according to the following alogrithm: - // 1. If the current node is empty add the new leaf - // 2. If the element is less than the current node then go down the left branch - // 3. If the element is greater than the current node then go down the right branch - - if ( BinarySearchTree_size == 0 ) - { - BinarySearchTree_size = 1; - root = RakNet::OP_NEW( file, line ); - root->item = RakNet::OP_NEW( file, line ); - *( root->item ) = input; - root->left = 0; - root->right = 0; - - return root; - } - - else - { - // start at the root - current = root; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( true ) // This loop traverses the tree to find a spot for insertion - { - - if ( input < *( current->item ) ) - { - if ( current->left == 0 ) - { - current->left = RakNet::OP_NEW( file, line ); - current->left->item = RakNet::OP_NEW( file, line ); - current = current->left; - current->left = 0; - current->right = 0; - *( current->item ) = input; - - BinarySearchTree_size++; - return current; - } - - else - { - current = current->left; - } - } - - else - if ( input > *( current->item ) ) - { - if ( current->right == 0 ) - { - current->right = RakNet::OP_NEW( file, line ); - current->right->item = RakNet::OP_NEW( file, line ); - current = current->right; - current->left = 0; - current->right = 0; - *( current->item ) = input; - - BinarySearchTree_size++; - return current; - } - - else - { - current = current->right; - } - } - - else - return 0; // ((input == current->item) == true) which is not allowed since the tree only takes discrete values. Do nothing - } - } - } - - template - bool BinarySearchTree::IsIn( const BinarySearchTreeType& input ) - { - typename BinarySearchTree::node * parent; - find( input, &parent ); - - if ( direction != NOT_FOUND ) - return true; - else - return false; - } - - - template - void BinarySearchTree::DisplayInorder( BinarySearchTreeType* return_array ) - { - typename BinarySearchTree::node * current, *parent; - bool just_printed = false; - - unsigned int index = 0; - - current = root; - - if ( BinarySearchTree_size == 0 ) - return ; // Do nothing for an empty tree - - else - if ( BinarySearchTree_size == 1 ) - { - return_array[ 0 ] = *( root->item ); - return ; - } - - - direction = ROOT; // Reset the direction - - while ( index != BinarySearchTree_size ) - { - // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes - - if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) - { - // Go left if the following 2 conditions are true - // I can go left - // I did not just move up from a right child - // I did not just move up from a left child - - current = current->left; - direction = ROOT; // Reset the direction - } - - else - if ( ( direction != RIGHT ) && ( just_printed == false ) ) - { - // Otherwise, print the current node if the following 3 conditions are true: - // I did not just move up from a right child - // I did not print this ndoe last cycle - - return_array[ index++ ] = *( current->item ); - just_printed = true; - } - - else - if ( ( current->right != 0 ) && ( direction != RIGHT ) ) - { - // Otherwise, go right if the following 2 conditions are true - // I did not just move up from a right child - // I can go right - - current = current->right; - direction = ROOT; // Reset the direction - just_printed = false; - } - - else - { - // Otherwise I've done everything I can. Move up the tree one node - parent = FindParent( *( current->item ) ); - current = parent; - just_printed = false; - } - } - } - - template - void BinarySearchTree::DisplayPreorder( BinarySearchTreeType* return_array ) - { - typename BinarySearchTree::node * current, *parent; - - unsigned int index = 0; - - current = root; - - if ( BinarySearchTree_size == 0 ) - return ; // Do nothing for an empty tree - - else - if ( BinarySearchTree_size == 1 ) - { - return_array[ 0 ] = *( root->item ); - return ; - } - - - direction = ROOT; // Reset the direction - return_array[ index++ ] = *( current->item ); - - while ( index != BinarySearchTree_size ) - { - // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes - - if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) - { - - current = current->left; - direction = ROOT; - - // Everytime you move a node print it - return_array[ index++ ] = *( current->item ); - } - - else - if ( ( current->right != 0 ) && ( direction != RIGHT ) ) - { - current = current->right; - direction = ROOT; - - // Everytime you move a node print it - return_array[ index++ ] = *( current->item ); - } - - else - { - // Otherwise I've done everything I can. Move up the tree one node - parent = FindParent( *( current->item ) ); - current = parent; - } - } - } - - template - inline void BinarySearchTree::DisplayPostorder( BinarySearchTreeType* return_array ) - { - unsigned int index = 0; - - if ( BinarySearchTree_size == 0 ) - return ; // Do nothing for an empty tree - - else - if ( BinarySearchTree_size == 1 ) - { - return_array[ 0 ] = *( root->item ); - return ; - } - - DisplayPostorderRecursive( root, return_array, index ); - } - - - // Recursively do a postorder traversal - template - void BinarySearchTree::DisplayPostorderRecursive( typename BinarySearchTree::node* current, BinarySearchTreeType* return_array, unsigned int& index ) - { - if ( current->left != 0 ) - DisplayPostorderRecursive( current->left, return_array, index ); - - if ( current->right != 0 ) - DisplayPostorderRecursive( current->right, return_array, index ); - - return_array[ index++ ] = *( current->item ); - - } - - - template - void BinarySearchTree::DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ) - { - typename BinarySearchTree::node * current; - unsigned int index = 0; - - // Display the tree using a breadth first search - // Put the children of the current node into the queue - // Pop the queue, put its children into the queue, repeat until queue is empty - - if ( BinarySearchTree_size == 0 ) - return ; // Do nothing for an empty tree - - else - if ( BinarySearchTree_size == 1 ) - { - return_array[ 0 ] = *( root->item ); - return ; - } - - else - { - DataStructures::QueueLinkedList tree_queue; - - // Add the root of the tree I am copying from - tree_queue.Push( root ); - - do - { - current = tree_queue.Pop(); - return_array[ index++ ] = *( current->item ); - - // Add the child or children of the tree I am copying from to the queue - - if ( current->left != 0 ) - tree_queue.Push( current->left ); - - if ( current->right != 0 ) - tree_queue.Push( current->right ); - - } - - while ( tree_queue.Size() > 0 ); - } - } - - - template - BinarySearchTree::BinarySearchTree( const BinarySearchTree& original_copy ) - { - typename BinarySearchTree::node * current; - // Copy the tree using a breadth first search - // Put the children of the current node into the queue - // Pop the queue, put its children into the queue, repeat until queue is empty - - // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. - BinarySearchTree_size = 0; - root = 0; - - if ( original_copy.BinarySearchTree_size == 0 ) - { - BinarySearchTree_size = 0; - } - - else - { - DataStructures::QueueLinkedList tree_queue; - - // Add the root of the tree I am copying from - tree_queue.Push( original_copy.root ); - - do - { - current = tree_queue.Pop(); - - Add ( *( current->item ), _FILE_AND_LINE_ ) - - ; - - // Add the child or children of the tree I am copying from to the queue - if ( current->left != 0 ) - tree_queue.Push( current->left ); - - if ( current->right != 0 ) - tree_queue.Push( current->right ); - - } - - while ( tree_queue.Size() > 0 ); - } - } - - template - BinarySearchTree& BinarySearchTree::operator= ( const BinarySearchTree& original_copy ) - { - typename BinarySearchTree::node * current; - - if ( ( &original_copy ) == this ) - return *this; - - Clear( _FILE_AND_LINE_ ); // Remove the current tree - - // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. - BinarySearchTree_size = 0; - - root = 0; - - - // Copy the tree using a breadth first search - // Put the children of the current node into the queue - // Pop the queue, put its children into the queue, repeat until queue is empty - if ( original_copy.BinarySearchTree_size == 0 ) - { - BinarySearchTree_size = 0; - } - - else - { - DataStructures::QueueLinkedList tree_queue; - - // Add the root of the tree I am copying from - tree_queue.Push( original_copy.root ); - - do - { - current = tree_queue.Pop(); - - Add ( *( current->item ), _FILE_AND_LINE_ ) - - ; - - // Add the child or children of the tree I am copying from to the queue - if ( current->left != 0 ) - tree_queue.Push( current->left ); - - if ( current->right != 0 ) - tree_queue.Push( current->right ); - - } - - while ( tree_queue.Size() > 0 ); - } - - return *this; - } - - template - inline void BinarySearchTree::Clear ( const char *file, unsigned int line ) - { - typename BinarySearchTree::node * current, *parent; - - current = root; - - while ( BinarySearchTree_size > 0 ) - { - if ( BinarySearchTree_size == 1 ) - { - RakNet::OP_DELETE(root->item, file, line); - RakNet::OP_DELETE(root, file, line); - root = 0; - BinarySearchTree_size = 0; - } - - else - { - if ( current->left != 0 ) - { - current = current->left; - } - - else - if ( current->right != 0 ) - { - current = current->right; - } - - else // leaf - { - // Not root node so must have a parent - parent = FindParent( *( current->item ) ); - - if ( ( parent->left ) == current ) - parent->left = 0; - else - parent->right = 0; - - RakNet::OP_DELETE(current->item, file, line); - - RakNet::OP_DELETE(current, file, line); - - current = parent; - - BinarySearchTree_size--; - } - } - } - } - -} // End namespace - -#endif - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +/// \file DS_BinarySearchTree.h +/// \internal +/// \brief A binary search tree, and an AVL balanced BST derivation. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __BINARY_SEARCH_TREE_H +#define __BINARY_SEARCH_TREE_H + +#include "DS_QueueLinkedList.h" +#include "RakMemoryOverride.h" +#include "Export.h" + + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /** + * \brief A binary search tree and an AVL balanced binary search tree. + * \details + * Initilize with the following structure + * + * BinarySearchTree + * + * OR + * + * AVLBalancedBinarySearchTree + * + * Use the AVL balanced tree if you want the tree to be balanced after every deletion and addition. This avoids the potential + * worst case scenario where ordered input to a binary search tree gives linear search time results. It's not needed + * if input will be evenly distributed, in which case the search time is O (log n). The search time for the AVL + * balanced binary tree is O (log n) irregardless of input. + * + * Has the following member functions + * unsigned int Height() - Returns the height of the tree at the optional specified starting index. Default is the root + * add(element) - adds an element to the BinarySearchTree + * bool del(element) - deletes the node containing element if the element is in the tree as defined by a comparison with the == operator. Returns true on success, false if the element is not found + * bool IsInelement) - returns true if element is in the tree as defined by a comparison with the == operator. Otherwise returns false + * DisplayInorder(array) - Fills an array with an inorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * DisplayPreorder(array) - Fills an array with an preorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * DisplayPostorder(array) - Fills an array with an postorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * DisplayBreadthFirstSearch(array) - Fills an array with a breadth first search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * clear - Destroys the tree. Same as calling the destructor + * unsigned int Height() - Returns the height of the tree + * unsigned int size() - returns the size of the BinarySearchTree + * GetPointerToNode(element) - returns a pointer to the comparision element in the tree, allowing for direct modification when necessary with complex data types. + * Be warned, it is possible to corrupt the tree if the element used for comparisons is modified. Returns NULL if the item is not found + * + * + * EXAMPLE + * @code + * BinarySearchTree A; + * A.Add(10); + * A.Add(15); + * A.Add(5); + * int* array = RakNet::OP_NEW(A.Size(), _FILE_AND_LINE_ ); + * A.DisplayInorder(array); + * array[0]; // returns 5 + * array[1]; // returns 10 + * array[2]; // returns 15 + * @endcode + * compress - reallocates memory to fit the number of elements. Best used when the number of elements decreases + * + * clear - empties the BinarySearchTree and returns storage + * The assignment and copy constructors are defined + * + * \note The template type must have the copy constructor and + * assignment operator defined and must work with >, <, and == All + * elements in the tree MUST be distinct The assignment operator is + * defined between BinarySearchTree and AVLBalancedBinarySearchTree + * as long as they are of the same template type. However, passing a + * BinarySearchTree to an AVLBalancedBinarySearchTree will lose its + * structure unless it happened to be AVL balanced to begin with + * Requires queue_linked_list.cpp for the breadth first search used + * in the copy constructor, overloaded assignment operator, and + * display_breadth_first_search. + * + * + */ + template + class RAK_DLL_EXPORT BinarySearchTree + { + + public: + + struct node + { + BinarySearchTreeType* item; + node* left; + node* right; + }; + + BinarySearchTree(); + virtual ~BinarySearchTree(); + BinarySearchTree( const BinarySearchTree& original_type ); + BinarySearchTree& operator= ( const BinarySearchTree& original_copy ); + unsigned int Size( void ); + void Clear( const char *file, unsigned int line ); + unsigned int Height( node* starting_node = 0 ); + node* Add ( const BinarySearchTreeType& input, const char *file, unsigned int line ); + node* Del( const BinarySearchTreeType& input, const char *file, unsigned int line ); + bool IsIn( const BinarySearchTreeType& input ); + void DisplayInorder( BinarySearchTreeType* return_array ); + void DisplayPreorder( BinarySearchTreeType* return_array ); + void DisplayPostorder( BinarySearchTreeType* return_array ); + void DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ); + BinarySearchTreeType*& GetPointerToNode( const BinarySearchTreeType& element ); + + protected: + + node* root; + + enum Direction_Types + { + NOT_FOUND, LEFT, RIGHT, ROOT + } direction; + unsigned int HeightRecursive( node* current ); + unsigned int BinarySearchTree_size; + node*& Find( const BinarySearchTreeType& element, node** parent ); + node*& FindParent( const BinarySearchTreeType& element ); + void DisplayPostorderRecursive( node* current, BinarySearchTreeType* return_array, unsigned int& index ); + void FixTree( node* current ); + + }; + + /// An AVLBalancedBinarySearchTree is a binary tree that is always balanced + template + class RAK_DLL_EXPORT AVLBalancedBinarySearchTree : public BinarySearchTree + { + + public: + AVLBalancedBinarySearchTree() {} + virtual ~AVLBalancedBinarySearchTree(); + void Add ( const BinarySearchTreeType& input ); + void Del( const BinarySearchTreeType& input ); + BinarySearchTree& operator= ( BinarySearchTree& original_copy ) + { + return BinarySearchTree::operator= ( original_copy ); + } + + private: + void BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ); + void RotateRight( typename BinarySearchTree::node *C ); + void RotateLeft( typename BinarySearchTree::node* C ); + void DoubleRotateRight( typename BinarySearchTree::node *A ); + void DoubleRotateLeft( typename BinarySearchTree::node* A ); + bool RightHigher( typename BinarySearchTree::node* A ); + bool LeftHigher( typename BinarySearchTree::node* A ); + }; + + template + void AVLBalancedBinarySearchTree::BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ) + { + int left_height, right_height; + + while ( current ) + { + if ( current->left == 0 ) + left_height = 0; + else + left_height = Height( current->left ); + + if ( current->right == 0 ) + right_height = 0; + else + right_height = Height( current->right ); + + if ( right_height - left_height == 2 ) + { + if ( RightHigher( current->right ) ) + RotateLeft( current->right ); + else + DoubleRotateLeft( current ); + + if ( rotateOnce ) + break; + } + + else + if ( right_height - left_height == -2 ) + { + if ( LeftHigher( current->left ) ) + RotateRight( current->left ); + else + DoubleRotateRight( current ); + + if ( rotateOnce ) + break; + } + + if ( current == this->root ) + break; + + current = FindParent( *( current->item ) ); + + } + } + + template + void AVLBalancedBinarySearchTree::Add ( const BinarySearchTreeType& input ) + { + + typename BinarySearchTree::node * current = BinarySearchTree::Add ( input, _FILE_AND_LINE_ ); + BalanceTree( current, true ); + } + + template + void AVLBalancedBinarySearchTree::Del( const BinarySearchTreeType& input ) + { + typename BinarySearchTree::node * current = BinarySearchTree::Del( input, _FILE_AND_LINE_ ); + BalanceTree( current, false ); + + } + + template + bool AVLBalancedBinarySearchTree::RightHigher( typename BinarySearchTree::node *A ) + { + if ( A == 0 ) + return false; + + return Height( A->right ) > Height( A->left ); + } + + template + bool AVLBalancedBinarySearchTree::LeftHigher( typename BinarySearchTree::node *A ) + { + if ( A == 0 ) + return false; + + return Height( A->left ) > Height( A->right ); + } + + template + void AVLBalancedBinarySearchTree::RotateRight( typename BinarySearchTree::node *C ) + { + typename BinarySearchTree::node * A, *B, *D; + /* + RIGHT ROTATION + + A = parent(b) + b= parent(c) + c = node to rotate around + + A + | // Either direction + B + / \ + C + / \ + D + + TO + + A + | // Either Direction + C + / \ + B + / \ + D + + + + + */ + + B = FindParent( *( C->item ) ); + A = FindParent( *( B->item ) ); + D = C->right; + + if ( A ) + { + // Direction was set by the last find_parent call + + if ( this->direction == this->LEFT ) + A->left = C; + else + A->right = C; + } + + else + this->root = C; // If B has no parent parent then B must have been the root node + + B->left = D; + + C->right = B; + } + + template + void AVLBalancedBinarySearchTree::DoubleRotateRight( typename BinarySearchTree::node *A ) + { + // The left side of the left child must be higher for the tree to balance with a right rotation. If it isn't, rotate it left before the normal rotation so it is. + RotateLeft( A->left->right ); + RotateRight( A->left ); + } + + template + void AVLBalancedBinarySearchTree::RotateLeft( typename BinarySearchTree::node *C ) + { + typename BinarySearchTree::node * A, *B, *D; + /* + RIGHT ROTATION + + A = parent(b) + b= parent(c) + c = node to rotate around + + A + | // Either direction + B + / \ + C + / \ + D + + TO + + A + | // Either Direction + C + / \ + B + / \ + D + + + + + */ + + B = FindParent( *( C->item ) ); + A = FindParent( *( B->item ) ); + D = C->left; + + if ( A ) + { + // Direction was set by the last find_parent call + + if ( this->direction == this->LEFT ) + A->left = C; + else + A->right = C; + } + + else + this->root = C; // If B has no parent parent then B must have been the root node + + B->right = D; + + C->left = B; + } + + template + void AVLBalancedBinarySearchTree::DoubleRotateLeft( typename BinarySearchTree::node *A ) + { + // The left side of the right child must be higher for the tree to balance with a left rotation. If it isn't, rotate it right before the normal rotation so it is. + RotateRight( A->right->left ); + RotateLeft( A->right ); + } + + template + AVLBalancedBinarySearchTree::~AVLBalancedBinarySearchTree() + { + this->Clear(_FILE_AND_LINE_); + } + + template + unsigned int BinarySearchTree::Size( void ) + { + return BinarySearchTree_size; + } + + template + unsigned int BinarySearchTree::Height( typename BinarySearchTree::node* starting_node ) + { + if ( BinarySearchTree_size == 0 || starting_node == 0 ) + return 0; + else + return HeightRecursive( starting_node ); + } + + // Recursively return the height of a binary tree + template + unsigned int BinarySearchTree::HeightRecursive( typename BinarySearchTree::node* current ) + { + unsigned int left_height = 0, right_height = 0; + + if ( ( current->left == 0 ) && ( current->right == 0 ) ) + return 1; // Leaf + + if ( current->left != 0 ) + left_height = 1 + HeightRecursive( current->left ); + + if ( current->right != 0 ) + right_height = 1 + HeightRecursive( current->right ); + + if ( left_height > right_height ) + return left_height; + else + return right_height; + } + + template + BinarySearchTree::BinarySearchTree() + { + BinarySearchTree_size = 0; + root = 0; + } + + template + BinarySearchTree::~BinarySearchTree() + { + this->Clear(_FILE_AND_LINE_); + } + + template + BinarySearchTreeType*& BinarySearchTree::GetPointerToNode( const BinarySearchTreeType& element ) + { + static typename BinarySearchTree::node * tempnode; + static BinarySearchTreeType* dummyptr = 0; + tempnode = Find ( element, &tempnode ); + + if ( this->direction == this->NOT_FOUND ) + return dummyptr; + + return tempnode->item; + } + + template + typename BinarySearchTree::node*& BinarySearchTree::Find( const BinarySearchTreeType& element, typename BinarySearchTree::node** parent ) + { + static typename BinarySearchTree::node * current; + + current = this->root; + *parent = 0; + this->direction = this->ROOT; + + if ( BinarySearchTree_size == 0 ) + { + this->direction = this->NOT_FOUND; + return current = 0; + } + + // Check if the item is at the root + if ( element == *( current->item ) ) + { + this->direction = this->ROOT; + return current; + } + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) + { + // Move pointer + + if ( element < *( current->item ) ) + { + *parent = current; + this->direction = this->LEFT; + current = current->left; + } + + else + if ( element > *( current->item ) ) + { + *parent = current; + this->direction = this->RIGHT; + current = current->right; + } + + if ( current == 0 ) + break; + + // Check if new position holds the item + if ( element == *( current->item ) ) + { + return current; + } + } + + + this->direction = this->NOT_FOUND; + return current = 0; + } + + template + typename BinarySearchTree::node*& BinarySearchTree::FindParent( const BinarySearchTreeType& element ) + { + static typename BinarySearchTree::node * parent; + Find ( element, &parent ); + return parent; + } + + // Performs a series of value swaps starting with current to fix the tree if needed + template + void BinarySearchTree::FixTree( typename BinarySearchTree::node* current ) + { + BinarySearchTreeType temp; + + while ( 1 ) + { + if ( ( ( current->left ) != 0 ) && ( *( current->item ) < *( current->left->item ) ) ) + { + // Swap the current value with the one to the left + temp = *( current->left->item ); + *( current->left->item ) = *( current->item ); + *( current->item ) = temp; + current = current->left; + } + + else + if ( ( ( current->right ) != 0 ) && ( *( current->item ) > *( current->right->item ) ) ) + { + // Swap the current value with the one to the right + temp = *( current->right->item ); + *( current->right->item ) = *( current->item ); + *( current->item ) = temp; + current = current->right; + } + + else + break; // current points to the right place so quit + } + } + + template + typename BinarySearchTree::node* BinarySearchTree::Del( const BinarySearchTreeType& input, const char *file, unsigned int line ) + { + typename BinarySearchTree::node * node_to_delete, *current, *parent; + + if ( BinarySearchTree_size == 0 ) + return 0; + + if ( BinarySearchTree_size == 1 ) + { + Clear(file, line); + return 0; + } + + node_to_delete = Find( input, &parent ); + + if ( direction == NOT_FOUND ) + return 0; // Couldn't find the element + + current = node_to_delete; + + // Replace the deleted node with the appropriate value + if ( ( current->right ) == 0 && ( current->left ) == 0 ) // Leaf node, just remove it + { + + if ( parent ) + { + if ( direction == LEFT ) + parent->left = 0; + else + parent->right = 0; + } + + RakNet::OP_DELETE(node_to_delete->item, file, line); + RakNet::OP_DELETE(node_to_delete, file, line); + BinarySearchTree_size--; + return parent; + } + else + if ( ( current->right ) != 0 && ( current->left ) == 0 ) // Node has only one child, delete it and cause the parent to point to that child + { + + if ( parent ) + { + if ( direction == RIGHT ) + parent->right = current->right; + else + parent->left = current->right; + } + + else + root = current->right; // Without a parent this must be the root node + + RakNet::OP_DELETE(node_to_delete->item, file, line); + + RakNet::OP_DELETE(node_to_delete, file, line); + + BinarySearchTree_size--; + + return parent; + } + else + if ( ( current->right ) == 0 && ( current->left ) != 0 ) // Node has only one child, delete it and cause the parent to point to that child + { + + if ( parent ) + { + if ( direction == RIGHT ) + parent->right = current->left; + else + parent->left = current->left; + } + + else + root = current->left; // Without a parent this must be the root node + + RakNet::OP_DELETE(node_to_delete->item, file, line); + + RakNet::OP_DELETE(node_to_delete, file, line); + + BinarySearchTree_size--; + + return parent; + } + else // Go right, then as left as far as you can + { + parent = current; + direction = RIGHT; + current = current->right; // Must have a right branch because the if statements above indicated that it has 2 branches + + while ( current->left ) + { + direction = LEFT; + parent = current; + current = current->left; + } + + // Replace the value held by the node to RakNet::OP_DELETE(with the value pointed to by current, _FILE_AND_LINE_); + *( node_to_delete->item ) = *( current->item ); + + // Delete current. + // If it is a leaf node just delete it + if ( current->right == 0 ) + { + if ( direction == RIGHT ) + parent->right = 0; + else + parent->left = 0; + + RakNet::OP_DELETE(current->item, file, line); + + RakNet::OP_DELETE(current, file, line); + + BinarySearchTree_size--; + + return parent; + } + + else + { + // Skip this node and make its parent point to its right branch + + if ( direction == RIGHT ) + parent->right = current->right; + else + parent->left = current->right; + + RakNet::OP_DELETE(current->item, file, line); + + RakNet::OP_DELETE(current, file, line); + + BinarySearchTree_size--; + + return parent; + } + } + } + + template + typename BinarySearchTree::node* BinarySearchTree::Add ( const BinarySearchTreeType& input, const char *file, unsigned int line ) + { + typename BinarySearchTree::node * current; + + // Add the new element to the tree according to the following alogrithm: + // 1. If the current node is empty add the new leaf + // 2. If the element is less than the current node then go down the left branch + // 3. If the element is greater than the current node then go down the right branch + + if ( BinarySearchTree_size == 0 ) + { + BinarySearchTree_size = 1; + root = RakNet::OP_NEW( file, line ); + root->item = RakNet::OP_NEW( file, line ); + *( root->item ) = input; + root->left = 0; + root->right = 0; + + return root; + } + + else + { + // start at the root + current = root; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) // This loop traverses the tree to find a spot for insertion + { + + if ( input < *( current->item ) ) + { + if ( current->left == 0 ) + { + current->left = RakNet::OP_NEW( file, line ); + current->left->item = RakNet::OP_NEW( file, line ); + current = current->left; + current->left = 0; + current->right = 0; + *( current->item ) = input; + + BinarySearchTree_size++; + return current; + } + + else + { + current = current->left; + } + } + + else + if ( input > *( current->item ) ) + { + if ( current->right == 0 ) + { + current->right = RakNet::OP_NEW( file, line ); + current->right->item = RakNet::OP_NEW( file, line ); + current = current->right; + current->left = 0; + current->right = 0; + *( current->item ) = input; + + BinarySearchTree_size++; + return current; + } + + else + { + current = current->right; + } + } + + else + return 0; // ((input == current->item) == true) which is not allowed since the tree only takes discrete values. Do nothing + } + } + } + + template + bool BinarySearchTree::IsIn( const BinarySearchTreeType& input ) + { + typename BinarySearchTree::node * parent; + find( input, &parent ); + + if ( direction != NOT_FOUND ) + return true; + else + return false; + } + + + template + void BinarySearchTree::DisplayInorder( BinarySearchTreeType* return_array ) + { + typename BinarySearchTree::node * current, *parent; + bool just_printed = false; + + unsigned int index = 0; + + current = root; + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + + direction = ROOT; // Reset the direction + + while ( index != BinarySearchTree_size ) + { + // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes + + if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) + { + // Go left if the following 2 conditions are true + // I can go left + // I did not just move up from a right child + // I did not just move up from a left child + + current = current->left; + direction = ROOT; // Reset the direction + } + + else + if ( ( direction != RIGHT ) && ( just_printed == false ) ) + { + // Otherwise, print the current node if the following 3 conditions are true: + // I did not just move up from a right child + // I did not print this ndoe last cycle + + return_array[ index++ ] = *( current->item ); + just_printed = true; + } + + else + if ( ( current->right != 0 ) && ( direction != RIGHT ) ) + { + // Otherwise, go right if the following 2 conditions are true + // I did not just move up from a right child + // I can go right + + current = current->right; + direction = ROOT; // Reset the direction + just_printed = false; + } + + else + { + // Otherwise I've done everything I can. Move up the tree one node + parent = FindParent( *( current->item ) ); + current = parent; + just_printed = false; + } + } + } + + template + void BinarySearchTree::DisplayPreorder( BinarySearchTreeType* return_array ) + { + typename BinarySearchTree::node * current, *parent; + + unsigned int index = 0; + + current = root; + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + + direction = ROOT; // Reset the direction + return_array[ index++ ] = *( current->item ); + + while ( index != BinarySearchTree_size ) + { + // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes + + if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) + { + + current = current->left; + direction = ROOT; + + // Everytime you move a node print it + return_array[ index++ ] = *( current->item ); + } + + else + if ( ( current->right != 0 ) && ( direction != RIGHT ) ) + { + current = current->right; + direction = ROOT; + + // Everytime you move a node print it + return_array[ index++ ] = *( current->item ); + } + + else + { + // Otherwise I've done everything I can. Move up the tree one node + parent = FindParent( *( current->item ) ); + current = parent; + } + } + } + + template + inline void BinarySearchTree::DisplayPostorder( BinarySearchTreeType* return_array ) + { + unsigned int index = 0; + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + DisplayPostorderRecursive( root, return_array, index ); + } + + + // Recursively do a postorder traversal + template + void BinarySearchTree::DisplayPostorderRecursive( typename BinarySearchTree::node* current, BinarySearchTreeType* return_array, unsigned int& index ) + { + if ( current->left != 0 ) + DisplayPostorderRecursive( current->left, return_array, index ); + + if ( current->right != 0 ) + DisplayPostorderRecursive( current->right, return_array, index ); + + return_array[ index++ ] = *( current->item ); + + } + + + template + void BinarySearchTree::DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ) + { + typename BinarySearchTree::node * current; + unsigned int index = 0; + + // Display the tree using a breadth first search + // Put the children of the current node into the queue + // Pop the queue, put its children into the queue, repeat until queue is empty + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + else + { + DataStructures::QueueLinkedList tree_queue; + + // Add the root of the tree I am copying from + tree_queue.Push( root ); + + do + { + current = tree_queue.Pop(); + return_array[ index++ ] = *( current->item ); + + // Add the child or children of the tree I am copying from to the queue + + if ( current->left != 0 ) + tree_queue.Push( current->left ); + + if ( current->right != 0 ) + tree_queue.Push( current->right ); + + } + + while ( tree_queue.Size() > 0 ); + } + } + + + template + BinarySearchTree::BinarySearchTree( const BinarySearchTree& original_copy ) + { + typename BinarySearchTree::node * current; + // Copy the tree using a breadth first search + // Put the children of the current node into the queue + // Pop the queue, put its children into the queue, repeat until queue is empty + + // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. + BinarySearchTree_size = 0; + root = 0; + + if ( original_copy.BinarySearchTree_size == 0 ) + { + BinarySearchTree_size = 0; + } + + else + { + DataStructures::QueueLinkedList tree_queue; + + // Add the root of the tree I am copying from + tree_queue.Push( original_copy.root ); + + do + { + current = tree_queue.Pop(); + + Add ( *( current->item ), _FILE_AND_LINE_ ) + + ; + + // Add the child or children of the tree I am copying from to the queue + if ( current->left != 0 ) + tree_queue.Push( current->left ); + + if ( current->right != 0 ) + tree_queue.Push( current->right ); + + } + + while ( tree_queue.Size() > 0 ); + } + } + + template + BinarySearchTree& BinarySearchTree::operator= ( const BinarySearchTree& original_copy ) + { + typename BinarySearchTree::node * current; + + if ( ( &original_copy ) == this ) + return *this; + + Clear( _FILE_AND_LINE_ ); // Remove the current tree + + // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. + BinarySearchTree_size = 0; + + root = 0; + + + // Copy the tree using a breadth first search + // Put the children of the current node into the queue + // Pop the queue, put its children into the queue, repeat until queue is empty + if ( original_copy.BinarySearchTree_size == 0 ) + { + BinarySearchTree_size = 0; + } + + else + { + DataStructures::QueueLinkedList tree_queue; + + // Add the root of the tree I am copying from + tree_queue.Push( original_copy.root ); + + do + { + current = tree_queue.Pop(); + + Add ( *( current->item ), _FILE_AND_LINE_ ) + + ; + + // Add the child or children of the tree I am copying from to the queue + if ( current->left != 0 ) + tree_queue.Push( current->left ); + + if ( current->right != 0 ) + tree_queue.Push( current->right ); + + } + + while ( tree_queue.Size() > 0 ); + } + + return *this; + } + + template + inline void BinarySearchTree::Clear ( const char *file, unsigned int line ) + { + typename BinarySearchTree::node * current, *parent; + + current = root; + + while ( BinarySearchTree_size > 0 ) + { + if ( BinarySearchTree_size == 1 ) + { + RakNet::OP_DELETE(root->item, file, line); + RakNet::OP_DELETE(root, file, line); + root = 0; + BinarySearchTree_size = 0; + } + + else + { + if ( current->left != 0 ) + { + current = current->left; + } + + else + if ( current->right != 0 ) + { + current = current->right; + } + + else // leaf + { + // Not root node so must have a parent + parent = FindParent( *( current->item ) ); + + if ( ( parent->left ) == current ) + parent->left = 0; + else + parent->right = 0; + + RakNet::OP_DELETE(current->item, file, line); + + RakNet::OP_DELETE(current, file, line); + + current = parent; + + BinarySearchTree_size--; + } + } + } + } + +} // End namespace + +#endif + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/src/raknet/DS_BytePool.cpp b/src/raknet/DS_BytePool.cpp index 823461d..9d40acf 100755 --- a/src/raknet/DS_BytePool.cpp +++ b/src/raknet/DS_BytePool.cpp @@ -1,149 +1,149 @@ -#include "DS_BytePool.h" -#include "RakAssert.h" -#ifndef __APPLE__ -// Use stdlib and not malloc for compatibility -#include -#endif - -using namespace DataStructures; - -BytePool::BytePool() -{ - pool128.SetPageSize(8192*4); - pool512.SetPageSize(8192*4); - pool2048.SetPageSize(8192*4); - pool8192.SetPageSize(8192*4); -} -BytePool::~BytePool() -{ -} -void BytePool::SetPageSize(int size) -{ - pool128.SetPageSize(size); - pool512.SetPageSize(size); - pool2048.SetPageSize(size); - pool8192.SetPageSize(size); -} -unsigned char *BytePool::Allocate(int bytesWanted, const char *file, unsigned int line) -{ -#ifdef _DISABLE_BYTE_POOL - return rakMalloc_Ex(bytesWanted, _FILE_AND_LINE_); -#endif - unsigned char *out; - if (bytesWanted <= 127) - { - #ifdef _THREADSAFE_BYTE_POOL - mutex128.Lock(); - #endif - out = (unsigned char*) pool128.Allocate(file, line); - #ifdef _THREADSAFE_BYTE_POOL - mutex128.Unlock(); - #endif - out[0]=0; - return ((unsigned char*) out)+1; - } - if (bytesWanted <= 511) - { - #ifdef _THREADSAFE_BYTE_POOL - mutex512.Lock(); - #endif - out = (unsigned char*) pool512.Allocate(file, line); - #ifdef _THREADSAFE_BYTE_POOL - mutex512.Unlock(); - #endif - out[0]=1; - return ((unsigned char*) out)+1; - } - if (bytesWanted <= 2047) - { - #ifdef _THREADSAFE_BYTE_POOL - mutex2048.Lock(); - #endif - out = (unsigned char*) pool2048.Allocate(file, line); - #ifdef _THREADSAFE_BYTE_POOL - mutex2048.Unlock(); - #endif - out[0]=2; - return ((unsigned char*) out)+1; - } - if (bytesWanted <= 8191) - { - #ifdef _THREADSAFE_BYTE_POOL - mutex8192.Lock(); - #endif - out = (unsigned char*) pool8192.Allocate(file, line); - #ifdef _THREADSAFE_BYTE_POOL - mutex8192.Unlock(); - #endif - out[0]=3; - return ((unsigned char*) out)+1; - } - - out = (unsigned char*) rakMalloc_Ex(bytesWanted+1, _FILE_AND_LINE_); - out[0]=(unsigned char)255; - return out+1; -} -void BytePool::Release(unsigned char *data, const char *file, unsigned int line) -{ -#ifdef _DISABLE_BYTE_POOL - _rakFree_Ex(data, _FILE_AND_LINE_ ); -#endif - unsigned char *realData = data-1; - switch (realData[0]) - { - case 0: - #ifdef _THREADSAFE_BYTE_POOL - mutex128.Lock(); - #endif - pool128.Release((unsigned char(*)[128]) realData, file, line ); - #ifdef _THREADSAFE_BYTE_POOL - mutex128.Unlock(); - #endif - break; - case 1: - #ifdef _THREADSAFE_BYTE_POOL - mutex512.Lock(); - #endif - pool512.Release((unsigned char(*)[512]) realData, file, line ); - #ifdef _THREADSAFE_BYTE_POOL - mutex512.Unlock(); - #endif - break; - case 2: - #ifdef _THREADSAFE_BYTE_POOL - mutex2048.Lock(); - #endif - pool2048.Release((unsigned char(*)[2048]) realData, file, line ); - #ifdef _THREADSAFE_BYTE_POOL - mutex2048.Unlock(); - #endif - break; - case 3: - #ifdef _THREADSAFE_BYTE_POOL - mutex8192.Lock(); - #endif - pool8192.Release((unsigned char(*)[8192]) realData, file, line ); - #ifdef _THREADSAFE_BYTE_POOL - mutex8192.Unlock(); - #endif - break; - case 255: - rakFree_Ex(realData, file, line ); - break; - default: - RakAssert(0); - break; - } -} -void BytePool::Clear(const char *file, unsigned int line) -{ - (void) file; - (void) line; - -#ifdef _THREADSAFE_BYTE_POOL - pool128.Clear(file, line); - pool512.Clear(file, line); - pool2048.Clear(file, line); - pool8192.Clear(file, line); -#endif -} +#include "DS_BytePool.h" +#include "RakAssert.h" +#ifndef __APPLE__ +// Use stdlib and not malloc for compatibility +#include +#endif + +using namespace DataStructures; + +BytePool::BytePool() +{ + pool128.SetPageSize(8192*4); + pool512.SetPageSize(8192*4); + pool2048.SetPageSize(8192*4); + pool8192.SetPageSize(8192*4); +} +BytePool::~BytePool() +{ +} +void BytePool::SetPageSize(int size) +{ + pool128.SetPageSize(size); + pool512.SetPageSize(size); + pool2048.SetPageSize(size); + pool8192.SetPageSize(size); +} +unsigned char *BytePool::Allocate(int bytesWanted, const char *file, unsigned int line) +{ +#ifdef _DISABLE_BYTE_POOL + return rakMalloc_Ex(bytesWanted, _FILE_AND_LINE_); +#endif + unsigned char *out; + if (bytesWanted <= 127) + { + #ifdef _THREADSAFE_BYTE_POOL + mutex128.Lock(); + #endif + out = (unsigned char*) pool128.Allocate(file, line); + #ifdef _THREADSAFE_BYTE_POOL + mutex128.Unlock(); + #endif + out[0]=0; + return ((unsigned char*) out)+1; + } + if (bytesWanted <= 511) + { + #ifdef _THREADSAFE_BYTE_POOL + mutex512.Lock(); + #endif + out = (unsigned char*) pool512.Allocate(file, line); + #ifdef _THREADSAFE_BYTE_POOL + mutex512.Unlock(); + #endif + out[0]=1; + return ((unsigned char*) out)+1; + } + if (bytesWanted <= 2047) + { + #ifdef _THREADSAFE_BYTE_POOL + mutex2048.Lock(); + #endif + out = (unsigned char*) pool2048.Allocate(file, line); + #ifdef _THREADSAFE_BYTE_POOL + mutex2048.Unlock(); + #endif + out[0]=2; + return ((unsigned char*) out)+1; + } + if (bytesWanted <= 8191) + { + #ifdef _THREADSAFE_BYTE_POOL + mutex8192.Lock(); + #endif + out = (unsigned char*) pool8192.Allocate(file, line); + #ifdef _THREADSAFE_BYTE_POOL + mutex8192.Unlock(); + #endif + out[0]=3; + return ((unsigned char*) out)+1; + } + + out = (unsigned char*) rakMalloc_Ex(bytesWanted+1, _FILE_AND_LINE_); + out[0]=(unsigned char)255; + return out+1; +} +void BytePool::Release(unsigned char *data, const char *file, unsigned int line) +{ +#ifdef _DISABLE_BYTE_POOL + _rakFree_Ex(data, _FILE_AND_LINE_ ); +#endif + unsigned char *realData = data-1; + switch (realData[0]) + { + case 0: + #ifdef _THREADSAFE_BYTE_POOL + mutex128.Lock(); + #endif + pool128.Release((unsigned char(*)[128]) realData, file, line ); + #ifdef _THREADSAFE_BYTE_POOL + mutex128.Unlock(); + #endif + break; + case 1: + #ifdef _THREADSAFE_BYTE_POOL + mutex512.Lock(); + #endif + pool512.Release((unsigned char(*)[512]) realData, file, line ); + #ifdef _THREADSAFE_BYTE_POOL + mutex512.Unlock(); + #endif + break; + case 2: + #ifdef _THREADSAFE_BYTE_POOL + mutex2048.Lock(); + #endif + pool2048.Release((unsigned char(*)[2048]) realData, file, line ); + #ifdef _THREADSAFE_BYTE_POOL + mutex2048.Unlock(); + #endif + break; + case 3: + #ifdef _THREADSAFE_BYTE_POOL + mutex8192.Lock(); + #endif + pool8192.Release((unsigned char(*)[8192]) realData, file, line ); + #ifdef _THREADSAFE_BYTE_POOL + mutex8192.Unlock(); + #endif + break; + case 255: + rakFree_Ex(realData, file, line ); + break; + default: + RakAssert(0); + break; + } +} +void BytePool::Clear(const char *file, unsigned int line) +{ + (void) file; + (void) line; + +#ifdef _THREADSAFE_BYTE_POOL + pool128.Clear(file, line); + pool512.Clear(file, line); + pool2048.Clear(file, line); + pool8192.Clear(file, line); +#endif +} diff --git a/src/raknet/DS_BytePool.h b/src/raknet/DS_BytePool.h index bdb8301..53eb834 100755 --- a/src/raknet/DS_BytePool.h +++ b/src/raknet/DS_BytePool.h @@ -1,46 +1,46 @@ -/// \file DS_BytePool.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __BYTE_POOL_H -#define __BYTE_POOL_H - -#include "RakMemoryOverride.h" -#include "DS_MemoryPool.h" -#include "Export.h" -#include "SimpleMutex.h" -#include "RakAssert.h" - -// #define _DISABLE_BYTE_POOL -// #define _THREADSAFE_BYTE_POOL - -namespace DataStructures -{ - // Allocate some number of bytes from pools. Uses the heap if necessary. - class RAK_DLL_EXPORT BytePool - { - public: - BytePool(); - ~BytePool(); - // Should be at least 8 times bigger than 8192 - void SetPageSize(int size); - unsigned char* Allocate(int bytesWanted, const char *file, unsigned int line); - void Release(unsigned char *data, const char *file, unsigned int line); - void Clear(const char *file, unsigned int line); - protected: - MemoryPool pool128; - MemoryPool pool512; - MemoryPool pool2048; - MemoryPool pool8192; -#ifdef _THREADSAFE_BYTE_POOL - SimpleMutex mutex128; - SimpleMutex mutex512; - SimpleMutex mutex2048; - SimpleMutex mutex8192; -#endif - }; -} - -#endif +/// \file DS_BytePool.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __BYTE_POOL_H +#define __BYTE_POOL_H + +#include "RakMemoryOverride.h" +#include "DS_MemoryPool.h" +#include "Export.h" +#include "SimpleMutex.h" +#include "RakAssert.h" + +// #define _DISABLE_BYTE_POOL +// #define _THREADSAFE_BYTE_POOL + +namespace DataStructures +{ + // Allocate some number of bytes from pools. Uses the heap if necessary. + class RAK_DLL_EXPORT BytePool + { + public: + BytePool(); + ~BytePool(); + // Should be at least 8 times bigger than 8192 + void SetPageSize(int size); + unsigned char* Allocate(int bytesWanted, const char *file, unsigned int line); + void Release(unsigned char *data, const char *file, unsigned int line); + void Clear(const char *file, unsigned int line); + protected: + MemoryPool pool128; + MemoryPool pool512; + MemoryPool pool2048; + MemoryPool pool8192; +#ifdef _THREADSAFE_BYTE_POOL + SimpleMutex mutex128; + SimpleMutex mutex512; + SimpleMutex mutex2048; + SimpleMutex mutex8192; +#endif + }; +} + +#endif diff --git a/src/raknet/DS_ByteQueue.cpp b/src/raknet/DS_ByteQueue.cpp index 5e0ffd8..a87b7af 100755 --- a/src/raknet/DS_ByteQueue.cpp +++ b/src/raknet/DS_ByteQueue.cpp @@ -1,127 +1,127 @@ -#include "DS_ByteQueue.h" -#include // Memmove -#include // realloc -#include - - -using namespace DataStructures; - -ByteQueue::ByteQueue() -{ - readOffset=writeOffset=lengthAllocated=0; - data=0; -} -ByteQueue::~ByteQueue() -{ - Clear(_FILE_AND_LINE_); - - -} -void ByteQueue::WriteBytes(const char *in, unsigned length, const char *file, unsigned int line) -{ - unsigned bytesWritten; - bytesWritten=GetBytesWritten(); - if (lengthAllocated==0 || length > lengthAllocated-bytesWritten-1) - { - unsigned oldLengthAllocated=lengthAllocated; - // Always need to waste 1 byte for the math to work, else writeoffset==readoffset - unsigned newAmountToAllocate=length+oldLengthAllocated+1; - if (newAmountToAllocate<256) - newAmountToAllocate=256; - lengthAllocated=lengthAllocated + newAmountToAllocate; - data=(char*)rakRealloc_Ex(data, lengthAllocated, file, line); - if (writeOffset < readOffset) - { - if (writeOffset <= newAmountToAllocate) - { - memcpy(data + oldLengthAllocated, data, writeOffset); - writeOffset=readOffset+bytesWritten; - } - else - { - memcpy(data + oldLengthAllocated, data, newAmountToAllocate); - memmove(data, data+newAmountToAllocate, writeOffset-newAmountToAllocate); - writeOffset-=newAmountToAllocate; - } - } - } - - if (length <= lengthAllocated-writeOffset) - memcpy(data+writeOffset, in, length); - else - { - // Wrap - memcpy(data+writeOffset, in, lengthAllocated-writeOffset); - memcpy(data, in+(lengthAllocated-writeOffset), length-(lengthAllocated-writeOffset)); - } - writeOffset=(writeOffset+length) % lengthAllocated; -} -bool ByteQueue::ReadBytes(char *out, unsigned maxLengthToRead, bool peek) -{ - unsigned bytesWritten = GetBytesWritten(); - unsigned bytesToRead = bytesWritten < maxLengthToRead ? bytesWritten : maxLengthToRead; - if (bytesToRead==0) - return false; - if (writeOffset>=readOffset) - { - memcpy(out, data+readOffset, bytesToRead); - } - else - { - unsigned availableUntilWrap = lengthAllocated-readOffset; - if (bytesToRead <= availableUntilWrap) - { - memcpy(out, data+readOffset, bytesToRead); - } - else - { - memcpy(out, data+readOffset, availableUntilWrap); - memcpy(out+availableUntilWrap, data, bytesToRead-availableUntilWrap); - } - } - - if (peek==false) - IncrementReadOffset(bytesToRead); - - return true; -} -char* ByteQueue::PeekContiguousBytes(unsigned int *outLength) const -{ - if (writeOffset>=readOffset) - *outLength=writeOffset-readOffset; - else - *outLength=lengthAllocated-readOffset; - return data+readOffset; -} -void ByteQueue::Clear(const char *file, unsigned int line) -{ - if (lengthAllocated) - rakFree_Ex(data, file, line ); - readOffset=writeOffset=lengthAllocated=0; - data=0; -} -unsigned ByteQueue::GetBytesWritten(void) const -{ - if (writeOffset>=readOffset) - return writeOffset-readOffset; - else - return writeOffset+(lengthAllocated-readOffset); -} -void ByteQueue::IncrementReadOffset(unsigned length) -{ - readOffset=(readOffset+length) % lengthAllocated; -} -void ByteQueue::DecrementReadOffset(unsigned length) -{ - if (length>readOffset) - readOffset=lengthAllocated-(length-readOffset); - else - readOffset-=length; -} -void ByteQueue::Print(void) -{ - unsigned i; - for (i=readOffset; i!=writeOffset; i++) - RAKNET_DEBUG_PRINTF("%i ", data[i]); - RAKNET_DEBUG_PRINTF("\n"); -} +#include "DS_ByteQueue.h" +#include // Memmove +#include // realloc +#include + + +using namespace DataStructures; + +ByteQueue::ByteQueue() +{ + readOffset=writeOffset=lengthAllocated=0; + data=0; +} +ByteQueue::~ByteQueue() +{ + Clear(_FILE_AND_LINE_); + + +} +void ByteQueue::WriteBytes(const char *in, unsigned length, const char *file, unsigned int line) +{ + unsigned bytesWritten; + bytesWritten=GetBytesWritten(); + if (lengthAllocated==0 || length > lengthAllocated-bytesWritten-1) + { + unsigned oldLengthAllocated=lengthAllocated; + // Always need to waste 1 byte for the math to work, else writeoffset==readoffset + unsigned newAmountToAllocate=length+oldLengthAllocated+1; + if (newAmountToAllocate<256) + newAmountToAllocate=256; + lengthAllocated=lengthAllocated + newAmountToAllocate; + data=(char*)rakRealloc_Ex(data, lengthAllocated, file, line); + if (writeOffset < readOffset) + { + if (writeOffset <= newAmountToAllocate) + { + memcpy(data + oldLengthAllocated, data, writeOffset); + writeOffset=readOffset+bytesWritten; + } + else + { + memcpy(data + oldLengthAllocated, data, newAmountToAllocate); + memmove(data, data+newAmountToAllocate, writeOffset-newAmountToAllocate); + writeOffset-=newAmountToAllocate; + } + } + } + + if (length <= lengthAllocated-writeOffset) + memcpy(data+writeOffset, in, length); + else + { + // Wrap + memcpy(data+writeOffset, in, lengthAllocated-writeOffset); + memcpy(data, in+(lengthAllocated-writeOffset), length-(lengthAllocated-writeOffset)); + } + writeOffset=(writeOffset+length) % lengthAllocated; +} +bool ByteQueue::ReadBytes(char *out, unsigned maxLengthToRead, bool peek) +{ + unsigned bytesWritten = GetBytesWritten(); + unsigned bytesToRead = bytesWritten < maxLengthToRead ? bytesWritten : maxLengthToRead; + if (bytesToRead==0) + return false; + if (writeOffset>=readOffset) + { + memcpy(out, data+readOffset, bytesToRead); + } + else + { + unsigned availableUntilWrap = lengthAllocated-readOffset; + if (bytesToRead <= availableUntilWrap) + { + memcpy(out, data+readOffset, bytesToRead); + } + else + { + memcpy(out, data+readOffset, availableUntilWrap); + memcpy(out+availableUntilWrap, data, bytesToRead-availableUntilWrap); + } + } + + if (peek==false) + IncrementReadOffset(bytesToRead); + + return true; +} +char* ByteQueue::PeekContiguousBytes(unsigned int *outLength) const +{ + if (writeOffset>=readOffset) + *outLength=writeOffset-readOffset; + else + *outLength=lengthAllocated-readOffset; + return data+readOffset; +} +void ByteQueue::Clear(const char *file, unsigned int line) +{ + if (lengthAllocated) + rakFree_Ex(data, file, line ); + readOffset=writeOffset=lengthAllocated=0; + data=0; +} +unsigned ByteQueue::GetBytesWritten(void) const +{ + if (writeOffset>=readOffset) + return writeOffset-readOffset; + else + return writeOffset+(lengthAllocated-readOffset); +} +void ByteQueue::IncrementReadOffset(unsigned length) +{ + readOffset=(readOffset+length) % lengthAllocated; +} +void ByteQueue::DecrementReadOffset(unsigned length) +{ + if (length>readOffset) + readOffset=lengthAllocated-(length-readOffset); + else + readOffset-=length; +} +void ByteQueue::Print(void) +{ + unsigned i; + for (i=readOffset; i!=writeOffset; i++) + RAKNET_DEBUG_PRINTF("%i ", data[i]); + RAKNET_DEBUG_PRINTF("\n"); +} diff --git a/src/raknet/DS_ByteQueue.h b/src/raknet/DS_ByteQueue.h index e6a0e31..a288813 100755 --- a/src/raknet/DS_ByteQueue.h +++ b/src/raknet/DS_ByteQueue.h @@ -1,40 +1,40 @@ -/// \file DS_ByteQueue.h -/// \internal -/// \brief Byte queue -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __BYTE_QUEUE_H -#define __BYTE_QUEUE_H - -#include "RakMemoryOverride.h" -#include "Export.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - class ByteQueue - { - public: - ByteQueue(); - ~ByteQueue(); - void WriteBytes(const char *in, unsigned length, const char *file, unsigned int line); - bool ReadBytes(char *out, unsigned maxLengthToRead, bool peek); - unsigned GetBytesWritten(void) const; - char* PeekContiguousBytes(unsigned int *outLength) const; - void IncrementReadOffset(unsigned length); - void DecrementReadOffset(unsigned length); - void Clear(const char *file, unsigned int line); - void Print(void); - - protected: - char *data; - unsigned readOffset, writeOffset, lengthAllocated; - }; -} - -#endif +/// \file DS_ByteQueue.h +/// \internal +/// \brief Byte queue +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __BYTE_QUEUE_H +#define __BYTE_QUEUE_H + +#include "RakMemoryOverride.h" +#include "Export.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + class ByteQueue + { + public: + ByteQueue(); + ~ByteQueue(); + void WriteBytes(const char *in, unsigned length, const char *file, unsigned int line); + bool ReadBytes(char *out, unsigned maxLengthToRead, bool peek); + unsigned GetBytesWritten(void) const; + char* PeekContiguousBytes(unsigned int *outLength) const; + void IncrementReadOffset(unsigned length); + void DecrementReadOffset(unsigned length); + void Clear(const char *file, unsigned int line); + void Print(void); + + protected: + char *data; + unsigned readOffset, writeOffset, lengthAllocated; + }; +} + +#endif diff --git a/src/raknet/DS_Hash.h b/src/raknet/DS_Hash.h index 39848d3..b61d556 100755 --- a/src/raknet/DS_Hash.h +++ b/src/raknet/DS_Hash.h @@ -1,344 +1,344 @@ -/// \internal -/// \brief Hashing container -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __HASH_H -#define __HASH_H - -#include "RakAssert.h" -#include // memmove -#include "Export.h" -#include "RakMemoryOverride.h" -#include "RakString.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - struct HashIndex - { - unsigned int primaryIndex; - unsigned int secondaryIndex; - bool IsInvalid(void) const {return primaryIndex==(unsigned int) -1;} - void SetInvalid(void) {primaryIndex=(unsigned int) -1; secondaryIndex=(unsigned int) -1;} - }; - - /// \brief Using a string as a identifier for a node, store an allocated pointer to that node - template - class RAK_DLL_EXPORT Hash - { - public: - /// Default constructor - Hash(); - - // Destructor - ~Hash(); - - void Push(key_type key, const data_type &input, const char *file, unsigned int line ); - data_type* Peek(key_type key ); - bool Pop(data_type& out, key_type key, const char *file, unsigned int line ); - bool RemoveAtIndex(HashIndex index, const char *file, unsigned int line ); - bool Remove(key_type key, const char *file, unsigned int line ); - HashIndex GetIndexOf(key_type key); - bool HasData(key_type key); - data_type& ItemAtIndex(const HashIndex &index); - key_type KeyAtIndex(const HashIndex &index); - void GetAsList(DataStructures::List &itemList,DataStructures::List &keyList,const char *file, unsigned int line) const; - unsigned int Size(void) const; - - /// \brief Clear the list - void Clear( const char *file, unsigned int line ); - - struct Node - { - Node(key_type strIn, const data_type &_data) {string=strIn; data=_data;} - key_type string; - data_type data; - // Next in the list for this key - Node *next; - }; - - protected: - void ClearIndex(unsigned int index,const char *file, unsigned int line); - Node **nodeList; - unsigned int size; - }; - - template - Hash::Hash() - { - nodeList=0; - size=0; - } - - template - Hash::~Hash() - { - Clear(_FILE_AND_LINE_); - } - - template - void Hash::Push(key_type key, const data_type &input, const char *file, unsigned int line ) - { - unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; - if (nodeList==0) - { - nodeList=RakNet::OP_NEW_ARRAY(HASH_SIZE,file,line); - memset(nodeList,0,sizeof(Node *)*HASH_SIZE); - } - - Node *newNode=RakNet::OP_NEW_2(file,line,key,input); - newNode->next=nodeList[hashIndex]; - nodeList[hashIndex]=newNode; - - size++; - } - - template - data_type* Hash::Peek(key_type key ) - { - unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; - Node *node = nodeList[hashIndex]; - while (node!=0) - { - if (node->string==key) - return &node->data; - node=node->next; - } - return 0; - } - - template - bool Hash::Pop(data_type& out, key_type key, const char *file, unsigned int line ) - { - unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; - Node *node = nodeList[hashIndex]; - if (node==0) - return false; - if (node->next==0) - { - // Only one item. - if (node->string==key) - { - // Delete last item - out=node->data; - ClearIndex(hashIndex,_FILE_AND_LINE_); - return true; - } - else - { - // Single item doesn't match - return false; - } - } - else if (node->string==key) - { - // First item does match, but more than one item - out=node->data; - nodeList[hashIndex]=node->next; - RakNet::OP_DELETE(node,file,line); - size--; - return true; - } - - Node *last=node; - node=node->next; - - while (node!=0) - { - // First item does not match, but subsequent item might - if (node->string==key) - { - out=node->data; - // Skip over subsequent item - last->next=node->next; - // Delete existing item - RakNet::OP_DELETE(node,file,line); - size--; - return true; - } - last=node; - node=node->next; - } - return false; - } - - template - bool Hash::RemoveAtIndex(HashIndex index, const char *file, unsigned int line ) - { - if (index.IsInvalid()) - return false; - - Node *node = nodeList[index.primaryIndex]; - if (node==0) - return false; - if (node->next==0) - { - // Delete last item - ClearIndex(index.primaryIndex,file,line); - return true; - } - else if (index.secondaryIndex==0) - { - // First item does match, but more than one item - nodeList[index.primaryIndex]=node->next; - RakNet::OP_DELETE(node,file,line); - size--; - return true; - } - - Node *last=node; - node=node->next; - --index.secondaryIndex; - - while (index.secondaryIndex!=0) - { - last=node; - node=node->next; - --index.secondaryIndex; - } - - // Skip over subsequent item - last->next=node->next; - // Delete existing item - RakNet::OP_DELETE(node,file,line); - size--; - return true; - } - - template - bool Hash::Remove(key_type key, const char *file, unsigned int line ) - { - return RemoveAtIndex(GetIndexOf(key),file,line); - } - - template - HashIndex Hash::GetIndexOf(key_type key) - { - if (nodeList==0) - { - HashIndex temp; - temp.SetInvalid(); - return temp; - } - HashIndex idx; - idx.primaryIndex=(*hashFunction)(key) % HASH_SIZE; - Node *node = nodeList[idx.primaryIndex]; - if (node==0) - { - idx.SetInvalid(); - return idx; - } - idx.secondaryIndex=0; - while (node!=0) - { - if (node->string==key) - { - return idx; - } - node=node->next; - idx.secondaryIndex++; - } - - idx.SetInvalid(); - return idx; - } - - template - bool Hash::HasData(key_type key) - { - return GetIndexOf(key).IsInvalid()==false; - } - - template - data_type& Hash::ItemAtIndex(const HashIndex &index) - { - Node *node = nodeList[index.primaryIndex]; - RakAssert(node); - unsigned int i; - for (i=0; i < index.secondaryIndex; i++) - { - node=node->next; - RakAssert(node); - } - return node->data; - } - - template - key_type Hash::KeyAtIndex(const HashIndex &index) - { - Node *node = nodeList[index.primaryIndex]; - RakAssert(node); - unsigned int i; - for (i=0; i < index.secondaryIndex; i++) - { - node=node->next; - RakAssert(node); - } - return node->string; - } - - template - void Hash::Clear(const char *file, unsigned int line) - { - if (nodeList) - { - unsigned int i; - for (i=0; i < HASH_SIZE; i++) - ClearIndex(i,file,line); - RakNet::OP_DELETE_ARRAY(nodeList,file,line); - nodeList=0; - size=0; - } - } - - template - void Hash::ClearIndex(unsigned int index,const char *file, unsigned int line) - { - Node *node = nodeList[index]; - Node *next; - while (node) - { - next=node->next; - RakNet::OP_DELETE(node,file,line); - node=next; - size--; - } - nodeList[index]=0; - } - - template - void Hash::GetAsList(DataStructures::List &itemList,DataStructures::List &keyList,const char *file, unsigned int line) const - { - if (nodeList==0) - return; - itemList.Clear(false,_FILE_AND_LINE_); - keyList.Clear(false,_FILE_AND_LINE_); - - Node *node; - unsigned int i; - for (i=0; i < HASH_SIZE; i++) - { - if (nodeList[i]) - { - node=nodeList[i]; - while (node) - { - itemList.Push(node->data,file,line); - keyList.Push(node->string,file,line); - node=node->next; - } - } - } - } - template - unsigned int Hash::Size(void) const - { - return size; - } -} -#endif +/// \internal +/// \brief Hashing container +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __HASH_H +#define __HASH_H + +#include "RakAssert.h" +#include // memmove +#include "Export.h" +#include "RakMemoryOverride.h" +#include "RakString.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + struct HashIndex + { + unsigned int primaryIndex; + unsigned int secondaryIndex; + bool IsInvalid(void) const {return primaryIndex==(unsigned int) -1;} + void SetInvalid(void) {primaryIndex=(unsigned int) -1; secondaryIndex=(unsigned int) -1;} + }; + + /// \brief Using a string as a identifier for a node, store an allocated pointer to that node + template + class RAK_DLL_EXPORT Hash + { + public: + /// Default constructor + Hash(); + + // Destructor + ~Hash(); + + void Push(key_type key, const data_type &input, const char *file, unsigned int line ); + data_type* Peek(key_type key ); + bool Pop(data_type& out, key_type key, const char *file, unsigned int line ); + bool RemoveAtIndex(HashIndex index, const char *file, unsigned int line ); + bool Remove(key_type key, const char *file, unsigned int line ); + HashIndex GetIndexOf(key_type key); + bool HasData(key_type key); + data_type& ItemAtIndex(const HashIndex &index); + key_type KeyAtIndex(const HashIndex &index); + void GetAsList(DataStructures::List &itemList,DataStructures::List &keyList,const char *file, unsigned int line) const; + unsigned int Size(void) const; + + /// \brief Clear the list + void Clear( const char *file, unsigned int line ); + + struct Node + { + Node(key_type strIn, const data_type &_data) {string=strIn; data=_data;} + key_type string; + data_type data; + // Next in the list for this key + Node *next; + }; + + protected: + void ClearIndex(unsigned int index,const char *file, unsigned int line); + Node **nodeList; + unsigned int size; + }; + + template + Hash::Hash() + { + nodeList=0; + size=0; + } + + template + Hash::~Hash() + { + Clear(_FILE_AND_LINE_); + } + + template + void Hash::Push(key_type key, const data_type &input, const char *file, unsigned int line ) + { + unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; + if (nodeList==0) + { + nodeList=RakNet::OP_NEW_ARRAY(HASH_SIZE,file,line); + memset(nodeList,0,sizeof(Node *)*HASH_SIZE); + } + + Node *newNode=RakNet::OP_NEW_2(file,line,key,input); + newNode->next=nodeList[hashIndex]; + nodeList[hashIndex]=newNode; + + size++; + } + + template + data_type* Hash::Peek(key_type key ) + { + unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; + Node *node = nodeList[hashIndex]; + while (node!=0) + { + if (node->string==key) + return &node->data; + node=node->next; + } + return 0; + } + + template + bool Hash::Pop(data_type& out, key_type key, const char *file, unsigned int line ) + { + unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE; + Node *node = nodeList[hashIndex]; + if (node==0) + return false; + if (node->next==0) + { + // Only one item. + if (node->string==key) + { + // Delete last item + out=node->data; + ClearIndex(hashIndex,_FILE_AND_LINE_); + return true; + } + else + { + // Single item doesn't match + return false; + } + } + else if (node->string==key) + { + // First item does match, but more than one item + out=node->data; + nodeList[hashIndex]=node->next; + RakNet::OP_DELETE(node,file,line); + size--; + return true; + } + + Node *last=node; + node=node->next; + + while (node!=0) + { + // First item does not match, but subsequent item might + if (node->string==key) + { + out=node->data; + // Skip over subsequent item + last->next=node->next; + // Delete existing item + RakNet::OP_DELETE(node,file,line); + size--; + return true; + } + last=node; + node=node->next; + } + return false; + } + + template + bool Hash::RemoveAtIndex(HashIndex index, const char *file, unsigned int line ) + { + if (index.IsInvalid()) + return false; + + Node *node = nodeList[index.primaryIndex]; + if (node==0) + return false; + if (node->next==0) + { + // Delete last item + ClearIndex(index.primaryIndex,file,line); + return true; + } + else if (index.secondaryIndex==0) + { + // First item does match, but more than one item + nodeList[index.primaryIndex]=node->next; + RakNet::OP_DELETE(node,file,line); + size--; + return true; + } + + Node *last=node; + node=node->next; + --index.secondaryIndex; + + while (index.secondaryIndex!=0) + { + last=node; + node=node->next; + --index.secondaryIndex; + } + + // Skip over subsequent item + last->next=node->next; + // Delete existing item + RakNet::OP_DELETE(node,file,line); + size--; + return true; + } + + template + bool Hash::Remove(key_type key, const char *file, unsigned int line ) + { + return RemoveAtIndex(GetIndexOf(key),file,line); + } + + template + HashIndex Hash::GetIndexOf(key_type key) + { + if (nodeList==0) + { + HashIndex temp; + temp.SetInvalid(); + return temp; + } + HashIndex idx; + idx.primaryIndex=(*hashFunction)(key) % HASH_SIZE; + Node *node = nodeList[idx.primaryIndex]; + if (node==0) + { + idx.SetInvalid(); + return idx; + } + idx.secondaryIndex=0; + while (node!=0) + { + if (node->string==key) + { + return idx; + } + node=node->next; + idx.secondaryIndex++; + } + + idx.SetInvalid(); + return idx; + } + + template + bool Hash::HasData(key_type key) + { + return GetIndexOf(key).IsInvalid()==false; + } + + template + data_type& Hash::ItemAtIndex(const HashIndex &index) + { + Node *node = nodeList[index.primaryIndex]; + RakAssert(node); + unsigned int i; + for (i=0; i < index.secondaryIndex; i++) + { + node=node->next; + RakAssert(node); + } + return node->data; + } + + template + key_type Hash::KeyAtIndex(const HashIndex &index) + { + Node *node = nodeList[index.primaryIndex]; + RakAssert(node); + unsigned int i; + for (i=0; i < index.secondaryIndex; i++) + { + node=node->next; + RakAssert(node); + } + return node->string; + } + + template + void Hash::Clear(const char *file, unsigned int line) + { + if (nodeList) + { + unsigned int i; + for (i=0; i < HASH_SIZE; i++) + ClearIndex(i,file,line); + RakNet::OP_DELETE_ARRAY(nodeList,file,line); + nodeList=0; + size=0; + } + } + + template + void Hash::ClearIndex(unsigned int index,const char *file, unsigned int line) + { + Node *node = nodeList[index]; + Node *next; + while (node) + { + next=node->next; + RakNet::OP_DELETE(node,file,line); + node=next; + size--; + } + nodeList[index]=0; + } + + template + void Hash::GetAsList(DataStructures::List &itemList,DataStructures::List &keyList,const char *file, unsigned int line) const + { + if (nodeList==0) + return; + itemList.Clear(false,_FILE_AND_LINE_); + keyList.Clear(false,_FILE_AND_LINE_); + + Node *node; + unsigned int i; + for (i=0; i < HASH_SIZE; i++) + { + if (nodeList[i]) + { + node=nodeList[i]; + while (node) + { + itemList.Push(node->data,file,line); + keyList.Push(node->string,file,line); + node=node->next; + } + } + } + } + template + unsigned int Hash::Size(void) const + { + return size; + } +} +#endif diff --git a/src/raknet/DS_Heap.h b/src/raknet/DS_Heap.h index 92bca66..6551b84 100755 --- a/src/raknet/DS_Heap.h +++ b/src/raknet/DS_Heap.h @@ -1,297 +1,297 @@ -/// \file DS_Heap.h -/// \internal -/// \brief Heap (Also serves as a priority queue) -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAKNET_HEAP_H -#define __RAKNET_HEAP_H - -#include "RakMemoryOverride.h" -#include "DS_List.h" -#include "Export.h" -#include "RakAssert.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template - class RAK_DLL_EXPORT Heap - { - public: - struct HeapNode - { - HeapNode() {} - HeapNode(const weight_type &w, const data_type &d) : weight(w), data(d) {} - weight_type weight; // I'm assuming key is a native numerical type - float or int - data_type data; - }; - - Heap(); - ~Heap(); - void Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line); - /// Call before calling PushSeries, for a new series of items - void StartSeries(void) {optimizeNextSeriesPush=false;} - /// If you are going to push a list of items, where the weights of the items on the list are in order and follow the heap order, PushSeries is faster than Push() - void PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line); - data_type Pop(const unsigned startingIndex); - data_type Peek(const unsigned startingIndex=0) const; - weight_type PeekWeight(const unsigned startingIndex=0) const; - void Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line); - data_type& operator[] ( const unsigned int position ) const; - unsigned Size(void) const; - - protected: - unsigned LeftChild(const unsigned i) const; - unsigned RightChild(const unsigned i) const; - unsigned Parent(const unsigned i) const; - void Swap(const unsigned i, const unsigned j); - DataStructures::List heap; - bool optimizeNextSeriesPush; - }; - - template - Heap::Heap() - { - optimizeNextSeriesPush=false; - } - - template - Heap::~Heap() - { - //Clear(true, _FILE_AND_LINE_); - } - - template - void Heap::PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line) - { - if (optimizeNextSeriesPush==false) - { - // If the weight of what we are inserting is greater than / less than in order of the heap of every sibling and sibling of parent, then can optimize next push - unsigned currentIndex = heap.Size(); - unsigned parentIndex; - if (currentIndex>0) - { - for (parentIndex = Parent(currentIndex); parentIndex < currentIndex; parentIndex++) - { -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - if (isMaxHeap) - { - // Every child is less than its parent - if (weight>heap[parentIndex].weight) - { - // Can't optimize - Push(weight,data,file,line); - return; - } - } - else - { - // Every child is greater than than its parent - if (weight - void Heap::Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line) - { - unsigned currentIndex = heap.Size(); - unsigned parentIndex; - heap.Insert(HeapNode(weight, data), file, line); - while (currentIndex!=0) - { - parentIndex = Parent(currentIndex); -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (isMaxHeap) - { - if (heap[parentIndex].weight < weight) - { - Swap(currentIndex, parentIndex); - currentIndex=parentIndex; - } - else - break; - } - else - { - if (heap[parentIndex].weight > weight) - { - Swap(currentIndex, parentIndex); - currentIndex=parentIndex; - } - else - break; - } - } - } - - template - data_type Heap::Pop(const unsigned startingIndex) - { - // While we have children, swap out with the larger of the two children. - - // This line will assert on an empty heap - data_type returnValue=heap[startingIndex].data; - - // Move the last element to the head, and re-heapify - heap[startingIndex]=heap[heap.Size()-1]; - - unsigned currentIndex,leftChild,rightChild; - weight_type currentWeight; - currentIndex=startingIndex; - currentWeight=heap[startingIndex].weight; - heap.RemoveFromEnd(); - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - leftChild=LeftChild(currentIndex); - rightChild=RightChild(currentIndex); - if (leftChild >= heap.Size()) - { - // Done - return returnValue; - } - if (rightChild >= heap.Size()) - { - // Only left node. - if ((isMaxHeap==true && currentWeight < heap[leftChild].weight) || - (isMaxHeap==false && currentWeight > heap[leftChild].weight)) - Swap(leftChild, currentIndex); - - return returnValue; - } - else - { - // Swap with the bigger/smaller of the two children and continue - if (isMaxHeap) - { - if (heap[leftChild].weight <= currentWeight && heap[rightChild].weight <= currentWeight) - return returnValue; - - if (heap[leftChild].weight > heap[rightChild].weight) - { - Swap(leftChild, currentIndex); - currentIndex=leftChild; - } - else - { - Swap(rightChild, currentIndex); - currentIndex=rightChild; - } - } - else - { - if (heap[leftChild].weight >= currentWeight && heap[rightChild].weight >= currentWeight) - return returnValue; - - if (heap[leftChild].weight < heap[rightChild].weight) - { - Swap(leftChild, currentIndex); - currentIndex=leftChild; - } - else - { - Swap(rightChild, currentIndex); - currentIndex=rightChild; - } - } - } - } - } - - template - inline data_type Heap::Peek(const unsigned startingIndex) const - { - return heap[startingIndex].data; - } - - template - inline weight_type Heap::PeekWeight(const unsigned startingIndex) const - { - return heap[startingIndex].weight; - } - - template - void Heap::Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line) - { - heap.Clear(doNotDeallocateSmallBlocks, file, line); - } - - template - inline data_type& Heap::operator[] ( const unsigned int position ) const - { - return heap[position].data; - } - template - unsigned Heap::Size(void) const - { - return heap.Size(); - } - - template - inline unsigned Heap::LeftChild(const unsigned i) const - { - return i*2+1; - } - - template - inline unsigned Heap::RightChild(const unsigned i) const - { - return i*2+2; - } - - template - inline unsigned Heap::Parent(const unsigned i) const - { -#ifdef _DEBUG - RakAssert(i!=0); -#endif - return (i-1)/2; - } - - template - void Heap::Swap(const unsigned i, const unsigned j) - { - HeapNode temp; - temp=heap[i]; - heap[i]=heap[j]; - heap[j]=temp; - } -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif +/// \file DS_Heap.h +/// \internal +/// \brief Heap (Also serves as a priority queue) +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAKNET_HEAP_H +#define __RAKNET_HEAP_H + +#include "RakMemoryOverride.h" +#include "DS_List.h" +#include "Export.h" +#include "RakAssert.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + class RAK_DLL_EXPORT Heap + { + public: + struct HeapNode + { + HeapNode() {} + HeapNode(const weight_type &w, const data_type &d) : weight(w), data(d) {} + weight_type weight; // I'm assuming key is a native numerical type - float or int + data_type data; + }; + + Heap(); + ~Heap(); + void Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line); + /// Call before calling PushSeries, for a new series of items + void StartSeries(void) {optimizeNextSeriesPush=false;} + /// If you are going to push a list of items, where the weights of the items on the list are in order and follow the heap order, PushSeries is faster than Push() + void PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line); + data_type Pop(const unsigned startingIndex); + data_type Peek(const unsigned startingIndex=0) const; + weight_type PeekWeight(const unsigned startingIndex=0) const; + void Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line); + data_type& operator[] ( const unsigned int position ) const; + unsigned Size(void) const; + + protected: + unsigned LeftChild(const unsigned i) const; + unsigned RightChild(const unsigned i) const; + unsigned Parent(const unsigned i) const; + void Swap(const unsigned i, const unsigned j); + DataStructures::List heap; + bool optimizeNextSeriesPush; + }; + + template + Heap::Heap() + { + optimizeNextSeriesPush=false; + } + + template + Heap::~Heap() + { + //Clear(true, _FILE_AND_LINE_); + } + + template + void Heap::PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line) + { + if (optimizeNextSeriesPush==false) + { + // If the weight of what we are inserting is greater than / less than in order of the heap of every sibling and sibling of parent, then can optimize next push + unsigned currentIndex = heap.Size(); + unsigned parentIndex; + if (currentIndex>0) + { + for (parentIndex = Parent(currentIndex); parentIndex < currentIndex; parentIndex++) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (isMaxHeap) + { + // Every child is less than its parent + if (weight>heap[parentIndex].weight) + { + // Can't optimize + Push(weight,data,file,line); + return; + } + } + else + { + // Every child is greater than than its parent + if (weight + void Heap::Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line) + { + unsigned currentIndex = heap.Size(); + unsigned parentIndex; + heap.Insert(HeapNode(weight, data), file, line); + while (currentIndex!=0) + { + parentIndex = Parent(currentIndex); +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (isMaxHeap) + { + if (heap[parentIndex].weight < weight) + { + Swap(currentIndex, parentIndex); + currentIndex=parentIndex; + } + else + break; + } + else + { + if (heap[parentIndex].weight > weight) + { + Swap(currentIndex, parentIndex); + currentIndex=parentIndex; + } + else + break; + } + } + } + + template + data_type Heap::Pop(const unsigned startingIndex) + { + // While we have children, swap out with the larger of the two children. + + // This line will assert on an empty heap + data_type returnValue=heap[startingIndex].data; + + // Move the last element to the head, and re-heapify + heap[startingIndex]=heap[heap.Size()-1]; + + unsigned currentIndex,leftChild,rightChild; + weight_type currentWeight; + currentIndex=startingIndex; + currentWeight=heap[startingIndex].weight; + heap.RemoveFromEnd(); + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + leftChild=LeftChild(currentIndex); + rightChild=RightChild(currentIndex); + if (leftChild >= heap.Size()) + { + // Done + return returnValue; + } + if (rightChild >= heap.Size()) + { + // Only left node. + if ((isMaxHeap==true && currentWeight < heap[leftChild].weight) || + (isMaxHeap==false && currentWeight > heap[leftChild].weight)) + Swap(leftChild, currentIndex); + + return returnValue; + } + else + { + // Swap with the bigger/smaller of the two children and continue + if (isMaxHeap) + { + if (heap[leftChild].weight <= currentWeight && heap[rightChild].weight <= currentWeight) + return returnValue; + + if (heap[leftChild].weight > heap[rightChild].weight) + { + Swap(leftChild, currentIndex); + currentIndex=leftChild; + } + else + { + Swap(rightChild, currentIndex); + currentIndex=rightChild; + } + } + else + { + if (heap[leftChild].weight >= currentWeight && heap[rightChild].weight >= currentWeight) + return returnValue; + + if (heap[leftChild].weight < heap[rightChild].weight) + { + Swap(leftChild, currentIndex); + currentIndex=leftChild; + } + else + { + Swap(rightChild, currentIndex); + currentIndex=rightChild; + } + } + } + } + } + + template + inline data_type Heap::Peek(const unsigned startingIndex) const + { + return heap[startingIndex].data; + } + + template + inline weight_type Heap::PeekWeight(const unsigned startingIndex) const + { + return heap[startingIndex].weight; + } + + template + void Heap::Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line) + { + heap.Clear(doNotDeallocateSmallBlocks, file, line); + } + + template + inline data_type& Heap::operator[] ( const unsigned int position ) const + { + return heap[position].data; + } + template + unsigned Heap::Size(void) const + { + return heap.Size(); + } + + template + inline unsigned Heap::LeftChild(const unsigned i) const + { + return i*2+1; + } + + template + inline unsigned Heap::RightChild(const unsigned i) const + { + return i*2+2; + } + + template + inline unsigned Heap::Parent(const unsigned i) const + { +#ifdef _DEBUG + RakAssert(i!=0); +#endif + return (i-1)/2; + } + + template + void Heap::Swap(const unsigned i, const unsigned j) + { + HeapNode temp; + temp=heap[i]; + heap[i]=heap[j]; + heap[j]=temp; + } +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/src/raknet/DS_HuffmanEncodingTree.cpp b/src/raknet/DS_HuffmanEncodingTree.cpp index 3b03f40..abca294 100755 --- a/src/raknet/DS_HuffmanEncodingTree.cpp +++ b/src/raknet/DS_HuffmanEncodingTree.cpp @@ -1,299 +1,299 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "DS_HuffmanEncodingTree.h" -#include "DS_Queue.h" -#include "BitStream.h" -#include "RakAssert.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -HuffmanEncodingTree::HuffmanEncodingTree() -{ - root = 0; -} - -HuffmanEncodingTree::~HuffmanEncodingTree() -{ - FreeMemory(); -} - -void HuffmanEncodingTree::FreeMemory( void ) -{ - if ( root == 0 ) - return ; - - // Use an in-order traversal to delete the tree - DataStructures::Queue nodeQueue; - - HuffmanEncodingTreeNode *node; - - nodeQueue.Push( root, _FILE_AND_LINE_ ); - - while ( nodeQueue.Size() > 0 ) - { - node = nodeQueue.Pop(); - - if ( node->left ) - nodeQueue.Push( node->left, _FILE_AND_LINE_ ); - - if ( node->right ) - nodeQueue.Push( node->right, _FILE_AND_LINE_ ); - - RakNet::OP_DELETE(node, _FILE_AND_LINE_); - } - - // Delete the encoding table - for ( int i = 0; i < 256; i++ ) - rakFree_Ex(encodingTable[ i ].encoding, _FILE_AND_LINE_ ); - - root = 0; -} - - -////#include - -// Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree -void HuffmanEncodingTree::GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ) -{ - int counter; - HuffmanEncodingTreeNode * node; - HuffmanEncodingTreeNode *leafList[ 256 ]; // Keep a copy of the pointers to all the leaves so we can generate the encryption table bottom-up, which is easier - // 1. Make 256 trees each with a weight equal to the frequency of the corresponding character - DataStructures::LinkedList huffmanEncodingTreeNodeList; - - FreeMemory(); - - for ( counter = 0; counter < 256; counter++ ) - { - node = RakNet::OP_NEW( _FILE_AND_LINE_ ); - node->left = 0; - node->right = 0; - node->value = (unsigned char) counter; - node->weight = frequencyTable[ counter ]; - - if ( node->weight == 0 ) - node->weight = 1; // 0 weights are illegal - - leafList[ counter ] = node; // Used later to generate the encryption table - - InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); // Insert and maintain sort order. - } - - - // 2. While there is more than one tree, take the two smallest trees and merge them so that the two trees are the left and right - // children of a new node, where the new node has the weight the sum of the weight of the left and right child nodes. -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( 1 ) - { - huffmanEncodingTreeNodeList.Beginning(); - HuffmanEncodingTreeNode *lesser, *greater; - lesser = huffmanEncodingTreeNodeList.Pop(); - greater = huffmanEncodingTreeNodeList.Pop(); - node = RakNet::OP_NEW( _FILE_AND_LINE_ ); - node->left = lesser; - node->right = greater; - node->weight = lesser->weight + greater->weight; - lesser->parent = node; // This is done to make generating the encryption table easier - greater->parent = node; // This is done to make generating the encryption table easier - - if ( huffmanEncodingTreeNodeList.Size() == 0 ) - { - // 3. Assign the one remaining node in the list to the root node. - root = node; - root->parent = 0; - break; - } - - // Put the new node back into the list at the correct spot to maintain the sort. Linear search time - InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); - } - - bool tempPath[ 256 ]; // Maximum path length is 256 - unsigned short tempPathLength; - HuffmanEncodingTreeNode *currentNode; - RakNet::BitStream bitStream; - - // Generate the encryption table. From before, we have an array of pointers to all the leaves which contain pointers to their parents. - // This can be done more efficiently but this isn't bad and it's way easier to program and debug - - for ( counter = 0; counter < 256; counter++ ) - { - // Already done at the end of the loop and before it! - tempPathLength = 0; - - // Set the current node at the leaf - currentNode = leafList[ counter ]; - - do - { - if ( currentNode->parent->left == currentNode ) // We're storing the paths in reverse order.since we are going from the leaf to the root - tempPath[ tempPathLength++ ] = false; - else - tempPath[ tempPathLength++ ] = true; - - currentNode = currentNode->parent; - } - - while ( currentNode != root ); - - // Write to the bitstream in the reverse order that we stored the path, which gives us the correct order from the root to the leaf - while ( tempPathLength-- > 0 ) - { - if ( tempPath[ tempPathLength ] ) // Write 1's and 0's because writing a bool will write the BitStream TYPE_CHECKING validation bits if that is defined along with the actual data bit, which is not what we want - bitStream.Write1(); - else - bitStream.Write0(); - } - - // Read data from the bitstream, which is written to the encoding table in bits and bitlength. Note this function allocates the encodingTable[counter].encoding pointer - encodingTable[ counter ].bitLength = ( unsigned char ) bitStream.CopyData( &encodingTable[ counter ].encoding ); - - // Reset the bitstream for the next iteration - bitStream.Reset(); - } -} - -// Pass an array of bytes to array and a preallocated BitStream to receive the output -void HuffmanEncodingTree::EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output ) -{ - unsigned counter; - - // For each input byte, Write out the corresponding series of 1's and 0's that give the encoded representation - for ( counter = 0; counter < sizeInBytes; counter++ ) - { - output->WriteBits( encodingTable[ input[ counter ] ].encoding, encodingTable[ input[ counter ] ].bitLength, false ); // Data is left aligned - } - - // Byte align the output so the unassigned remaining bits don't equate to some actual value - if ( output->GetNumberOfBitsUsed() % 8 != 0 ) - { - // Find an input that is longer than the remaining bits. Write out part of it to pad the output to be byte aligned. - unsigned char remainingBits = (unsigned char) ( 8 - ( output->GetNumberOfBitsUsed() % 8 ) ); - - for ( counter = 0; counter < 256; counter++ ) - if ( encodingTable[ counter ].bitLength > remainingBits ) - { - output->WriteBits( encodingTable[ counter ].encoding, remainingBits, false ); // Data is left aligned - break; - } - -#ifdef _DEBUG - RakAssert( counter != 256 ); // Given 256 elements, we should always be able to find an input that would be >= 7 bits - -#endif - - } -} - -unsigned HuffmanEncodingTree::DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output ) -{ - HuffmanEncodingTreeNode * currentNode; - - unsigned outputWriteIndex; - outputWriteIndex = 0; - currentNode = root; - - // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root - - for ( unsigned counter = 0; counter < sizeInBits; counter++ ) - { - if ( input->ReadBit() == false ) // left! - currentNode = currentNode->left; - else - currentNode = currentNode->right; - - if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf - { - - if ( outputWriteIndex < maxCharsToWrite ) - output[ outputWriteIndex ] = currentNode->value; - - outputWriteIndex++; - - currentNode = root; - } - } - - return outputWriteIndex; -} - -// Pass an array of encoded bytes to array and a preallocated BitStream to receive the output -void HuffmanEncodingTree::DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output ) -{ - HuffmanEncodingTreeNode * currentNode; - - if ( sizeInBits <= 0 ) - return ; - - RakNet::BitStream bitStream( input, BITS_TO_BYTES(sizeInBits), false ); - - currentNode = root; - - // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root - for ( unsigned counter = 0; counter < sizeInBits; counter++ ) - { - if ( bitStream.ReadBit() == false ) // left! - currentNode = currentNode->left; - else - currentNode = currentNode->right; - - if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf - { - output->WriteBits( &( currentNode->value ), sizeof( char ) * 8, true ); // Use WriteBits instead of Write(char) because we want to avoid TYPE_CHECKING - currentNode = root; - } - } -} - -// Insertion sort. Slow but easy to write in this case -void HuffmanEncodingTree::InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const -{ - if ( huffmanEncodingTreeNodeList->Size() == 0 ) - { - huffmanEncodingTreeNodeList->Insert( node ); - return ; - } - - huffmanEncodingTreeNodeList->Beginning(); - - unsigned counter = 0; -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( 1 ) - { - if ( huffmanEncodingTreeNodeList->Peek()->weight < node->weight ) - ++( *huffmanEncodingTreeNodeList ); - else - { - huffmanEncodingTreeNodeList->Insert( node ); - break; - } - - // Didn't find a spot in the middle - add to the end - if ( ++counter == huffmanEncodingTreeNodeList->Size() ) - { - huffmanEncodingTreeNodeList->End(); - - huffmanEncodingTreeNodeList->Add( node ) - - ; // Add to the end - break; - } - } -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "DS_HuffmanEncodingTree.h" +#include "DS_Queue.h" +#include "BitStream.h" +#include "RakAssert.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +HuffmanEncodingTree::HuffmanEncodingTree() +{ + root = 0; +} + +HuffmanEncodingTree::~HuffmanEncodingTree() +{ + FreeMemory(); +} + +void HuffmanEncodingTree::FreeMemory( void ) +{ + if ( root == 0 ) + return ; + + // Use an in-order traversal to delete the tree + DataStructures::Queue nodeQueue; + + HuffmanEncodingTreeNode *node; + + nodeQueue.Push( root, _FILE_AND_LINE_ ); + + while ( nodeQueue.Size() > 0 ) + { + node = nodeQueue.Pop(); + + if ( node->left ) + nodeQueue.Push( node->left, _FILE_AND_LINE_ ); + + if ( node->right ) + nodeQueue.Push( node->right, _FILE_AND_LINE_ ); + + RakNet::OP_DELETE(node, _FILE_AND_LINE_); + } + + // Delete the encoding table + for ( int i = 0; i < 256; i++ ) + rakFree_Ex(encodingTable[ i ].encoding, _FILE_AND_LINE_ ); + + root = 0; +} + + +////#include + +// Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree +void HuffmanEncodingTree::GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ) +{ + int counter; + HuffmanEncodingTreeNode * node; + HuffmanEncodingTreeNode *leafList[ 256 ]; // Keep a copy of the pointers to all the leaves so we can generate the encryption table bottom-up, which is easier + // 1. Make 256 trees each with a weight equal to the frequency of the corresponding character + DataStructures::LinkedList huffmanEncodingTreeNodeList; + + FreeMemory(); + + for ( counter = 0; counter < 256; counter++ ) + { + node = RakNet::OP_NEW( _FILE_AND_LINE_ ); + node->left = 0; + node->right = 0; + node->value = (unsigned char) counter; + node->weight = frequencyTable[ counter ]; + + if ( node->weight == 0 ) + node->weight = 1; // 0 weights are illegal + + leafList[ counter ] = node; // Used later to generate the encryption table + + InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); // Insert and maintain sort order. + } + + + // 2. While there is more than one tree, take the two smallest trees and merge them so that the two trees are the left and right + // children of a new node, where the new node has the weight the sum of the weight of the left and right child nodes. +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( 1 ) + { + huffmanEncodingTreeNodeList.Beginning(); + HuffmanEncodingTreeNode *lesser, *greater; + lesser = huffmanEncodingTreeNodeList.Pop(); + greater = huffmanEncodingTreeNodeList.Pop(); + node = RakNet::OP_NEW( _FILE_AND_LINE_ ); + node->left = lesser; + node->right = greater; + node->weight = lesser->weight + greater->weight; + lesser->parent = node; // This is done to make generating the encryption table easier + greater->parent = node; // This is done to make generating the encryption table easier + + if ( huffmanEncodingTreeNodeList.Size() == 0 ) + { + // 3. Assign the one remaining node in the list to the root node. + root = node; + root->parent = 0; + break; + } + + // Put the new node back into the list at the correct spot to maintain the sort. Linear search time + InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); + } + + bool tempPath[ 256 ]; // Maximum path length is 256 + unsigned short tempPathLength; + HuffmanEncodingTreeNode *currentNode; + RakNet::BitStream bitStream; + + // Generate the encryption table. From before, we have an array of pointers to all the leaves which contain pointers to their parents. + // This can be done more efficiently but this isn't bad and it's way easier to program and debug + + for ( counter = 0; counter < 256; counter++ ) + { + // Already done at the end of the loop and before it! + tempPathLength = 0; + + // Set the current node at the leaf + currentNode = leafList[ counter ]; + + do + { + if ( currentNode->parent->left == currentNode ) // We're storing the paths in reverse order.since we are going from the leaf to the root + tempPath[ tempPathLength++ ] = false; + else + tempPath[ tempPathLength++ ] = true; + + currentNode = currentNode->parent; + } + + while ( currentNode != root ); + + // Write to the bitstream in the reverse order that we stored the path, which gives us the correct order from the root to the leaf + while ( tempPathLength-- > 0 ) + { + if ( tempPath[ tempPathLength ] ) // Write 1's and 0's because writing a bool will write the BitStream TYPE_CHECKING validation bits if that is defined along with the actual data bit, which is not what we want + bitStream.Write1(); + else + bitStream.Write0(); + } + + // Read data from the bitstream, which is written to the encoding table in bits and bitlength. Note this function allocates the encodingTable[counter].encoding pointer + encodingTable[ counter ].bitLength = ( unsigned char ) bitStream.CopyData( &encodingTable[ counter ].encoding ); + + // Reset the bitstream for the next iteration + bitStream.Reset(); + } +} + +// Pass an array of bytes to array and a preallocated BitStream to receive the output +void HuffmanEncodingTree::EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output ) +{ + unsigned counter; + + // For each input byte, Write out the corresponding series of 1's and 0's that give the encoded representation + for ( counter = 0; counter < sizeInBytes; counter++ ) + { + output->WriteBits( encodingTable[ input[ counter ] ].encoding, encodingTable[ input[ counter ] ].bitLength, false ); // Data is left aligned + } + + // Byte align the output so the unassigned remaining bits don't equate to some actual value + if ( output->GetNumberOfBitsUsed() % 8 != 0 ) + { + // Find an input that is longer than the remaining bits. Write out part of it to pad the output to be byte aligned. + unsigned char remainingBits = (unsigned char) ( 8 - ( output->GetNumberOfBitsUsed() % 8 ) ); + + for ( counter = 0; counter < 256; counter++ ) + if ( encodingTable[ counter ].bitLength > remainingBits ) + { + output->WriteBits( encodingTable[ counter ].encoding, remainingBits, false ); // Data is left aligned + break; + } + +#ifdef _DEBUG + RakAssert( counter != 256 ); // Given 256 elements, we should always be able to find an input that would be >= 7 bits + +#endif + + } +} + +unsigned HuffmanEncodingTree::DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output ) +{ + HuffmanEncodingTreeNode * currentNode; + + unsigned outputWriteIndex; + outputWriteIndex = 0; + currentNode = root; + + // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root + + for ( unsigned counter = 0; counter < sizeInBits; counter++ ) + { + if ( input->ReadBit() == false ) // left! + currentNode = currentNode->left; + else + currentNode = currentNode->right; + + if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf + { + + if ( outputWriteIndex < maxCharsToWrite ) + output[ outputWriteIndex ] = currentNode->value; + + outputWriteIndex++; + + currentNode = root; + } + } + + return outputWriteIndex; +} + +// Pass an array of encoded bytes to array and a preallocated BitStream to receive the output +void HuffmanEncodingTree::DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output ) +{ + HuffmanEncodingTreeNode * currentNode; + + if ( sizeInBits <= 0 ) + return ; + + RakNet::BitStream bitStream( input, BITS_TO_BYTES(sizeInBits), false ); + + currentNode = root; + + // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root + for ( unsigned counter = 0; counter < sizeInBits; counter++ ) + { + if ( bitStream.ReadBit() == false ) // left! + currentNode = currentNode->left; + else + currentNode = currentNode->right; + + if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf + { + output->WriteBits( &( currentNode->value ), sizeof( char ) * 8, true ); // Use WriteBits instead of Write(char) because we want to avoid TYPE_CHECKING + currentNode = root; + } + } +} + +// Insertion sort. Slow but easy to write in this case +void HuffmanEncodingTree::InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const +{ + if ( huffmanEncodingTreeNodeList->Size() == 0 ) + { + huffmanEncodingTreeNodeList->Insert( node ); + return ; + } + + huffmanEncodingTreeNodeList->Beginning(); + + unsigned counter = 0; +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( 1 ) + { + if ( huffmanEncodingTreeNodeList->Peek()->weight < node->weight ) + ++( *huffmanEncodingTreeNodeList ); + else + { + huffmanEncodingTreeNodeList->Insert( node ); + break; + } + + // Didn't find a spot in the middle - add to the end + if ( ++counter == huffmanEncodingTreeNodeList->Size() ) + { + huffmanEncodingTreeNodeList->End(); + + huffmanEncodingTreeNodeList->Add( node ) + + ; // Add to the end + break; + } + } +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/src/raknet/DS_HuffmanEncodingTree.h b/src/raknet/DS_HuffmanEncodingTree.h index 41cae1f..645f285 100755 --- a/src/raknet/DS_HuffmanEncodingTree.h +++ b/src/raknet/DS_HuffmanEncodingTree.h @@ -1,67 +1,67 @@ -/// \file DS_HuffmanEncodingTree.h -/// \brief \b [Internal] Generates a huffman encoding tree, used for string and global compression. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __HUFFMAN_ENCODING_TREE -#define __HUFFMAN_ENCODING_TREE - -#include "RakMemoryOverride.h" -#include "DS_HuffmanEncodingTreeNode.h" -#include "BitStream.h" -#include "Export.h" -#include "DS_LinkedList.h" - -namespace RakNet -{ - -/// This generates special cases of the huffman encoding tree using 8 bit keys with the additional condition that unused combinations of 8 bits are treated as a frequency of 1 -class RAK_DLL_EXPORT HuffmanEncodingTree -{ - -public: - HuffmanEncodingTree(); - ~HuffmanEncodingTree(); - - /// \brief Pass an array of bytes to array and a preallocated BitStream to receive the output. - /// \param [in] input Array of bytes to encode - /// \param [in] sizeInBytes size of \a input - /// \param [out] output The bitstream to write to - void EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output ); - - // \brief Decodes an array encoded by EncodeArray(). - unsigned DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output ); - void DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output ); - - /// \brief Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree. - void GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ); - - /// \brief Free the memory used by the tree. - void FreeMemory( void ); - -private: - - /// The root node of the tree - - HuffmanEncodingTreeNode *root; - - /// Used to hold bit encoding for one character - - - struct CharacterEncoding - { - unsigned char* encoding; - unsigned short bitLength; - }; - - CharacterEncoding encodingTable[ 256 ]; - - void InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const; -}; - -} // namespace RakNet - -#endif +/// \file DS_HuffmanEncodingTree.h +/// \brief \b [Internal] Generates a huffman encoding tree, used for string and global compression. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __HUFFMAN_ENCODING_TREE +#define __HUFFMAN_ENCODING_TREE + +#include "RakMemoryOverride.h" +#include "DS_HuffmanEncodingTreeNode.h" +#include "BitStream.h" +#include "Export.h" +#include "DS_LinkedList.h" + +namespace RakNet +{ + +/// This generates special cases of the huffman encoding tree using 8 bit keys with the additional condition that unused combinations of 8 bits are treated as a frequency of 1 +class RAK_DLL_EXPORT HuffmanEncodingTree +{ + +public: + HuffmanEncodingTree(); + ~HuffmanEncodingTree(); + + /// \brief Pass an array of bytes to array and a preallocated BitStream to receive the output. + /// \param [in] input Array of bytes to encode + /// \param [in] sizeInBytes size of \a input + /// \param [out] output The bitstream to write to + void EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output ); + + // \brief Decodes an array encoded by EncodeArray(). + unsigned DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output ); + void DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output ); + + /// \brief Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree. + void GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ); + + /// \brief Free the memory used by the tree. + void FreeMemory( void ); + +private: + + /// The root node of the tree + + HuffmanEncodingTreeNode *root; + + /// Used to hold bit encoding for one character + + + struct CharacterEncoding + { + unsigned char* encoding; + unsigned short bitLength; + }; + + CharacterEncoding encodingTable[ 256 ]; + + void InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const; +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/DS_HuffmanEncodingTreeFactory.h b/src/raknet/DS_HuffmanEncodingTreeFactory.h index c6a6acc..eab9c7b 100755 --- a/src/raknet/DS_HuffmanEncodingTreeFactory.h +++ b/src/raknet/DS_HuffmanEncodingTreeFactory.h @@ -1,57 +1,57 @@ -/// \file DS_HuffmanEncodingTreeFactory.h -/// \internal -/// \brief Creates instances of the class HuffmanEncodingTree -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __HUFFMAN_ENCODING_TREE_FACTORY -#define __HUFFMAN_ENCODING_TREE_FACTORY - -#include "RakMemoryOverride.h" - -namespace RakNet { -/// Forward declarations -class HuffmanEncodingTree; - -/// \brief Creates instances of the class HuffmanEncodingTree -/// \details This class takes a frequency table and given that frequence table, will generate an instance of HuffmanEncodingTree -class HuffmanEncodingTreeFactory -{ -public: - /// Default constructor - HuffmanEncodingTreeFactory(); - - /// \brief Reset the frequency table. - /// \details You don't need to call this unless you want to reuse the class for a new tree - void Reset( void ); - - /// \brief Pass an array of bytes to this to add those elements to the frequency table. - /// \param[in] array the data to insert into the frequency table - /// \param[in] size the size of the data to insert - void AddToFrequencyTable( unsigned char *array, int size ); - - /// \brief Copies the frequency table to the array passed. Retrieve the frequency table. - /// \param[in] _frequency The frequency table used currently - void GetFrequencyTable( unsigned int _frequency[ 256 ] ); - - /// \brief Returns the frequency table as a pointer. - /// \return the address of the frenquency table - unsigned int * GetFrequencyTable( void ); - - /// \brief Generate a HuffmanEncodingTree. - /// \details You can also use GetFrequencyTable and GenerateFromFrequencyTable in the tree itself - /// \return The generated instance of HuffmanEncodingTree - HuffmanEncodingTree * GenerateTree( void ); - -private: - - /// Frequency table - unsigned int frequency[ 256 ]; -}; - -} // namespace RakNet - -#endif +/// \file DS_HuffmanEncodingTreeFactory.h +/// \internal +/// \brief Creates instances of the class HuffmanEncodingTree +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __HUFFMAN_ENCODING_TREE_FACTORY +#define __HUFFMAN_ENCODING_TREE_FACTORY + +#include "RakMemoryOverride.h" + +namespace RakNet { +/// Forward declarations +class HuffmanEncodingTree; + +/// \brief Creates instances of the class HuffmanEncodingTree +/// \details This class takes a frequency table and given that frequence table, will generate an instance of HuffmanEncodingTree +class HuffmanEncodingTreeFactory +{ +public: + /// Default constructor + HuffmanEncodingTreeFactory(); + + /// \brief Reset the frequency table. + /// \details You don't need to call this unless you want to reuse the class for a new tree + void Reset( void ); + + /// \brief Pass an array of bytes to this to add those elements to the frequency table. + /// \param[in] array the data to insert into the frequency table + /// \param[in] size the size of the data to insert + void AddToFrequencyTable( unsigned char *array, int size ); + + /// \brief Copies the frequency table to the array passed. Retrieve the frequency table. + /// \param[in] _frequency The frequency table used currently + void GetFrequencyTable( unsigned int _frequency[ 256 ] ); + + /// \brief Returns the frequency table as a pointer. + /// \return the address of the frenquency table + unsigned int * GetFrequencyTable( void ); + + /// \brief Generate a HuffmanEncodingTree. + /// \details You can also use GetFrequencyTable and GenerateFromFrequencyTable in the tree itself + /// \return The generated instance of HuffmanEncodingTree + HuffmanEncodingTree * GenerateTree( void ); + +private: + + /// Frequency table + unsigned int frequency[ 256 ]; +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/DS_HuffmanEncodingTreeNode.h b/src/raknet/DS_HuffmanEncodingTreeNode.h index 40523e1..f726e4f 100755 --- a/src/raknet/DS_HuffmanEncodingTreeNode.h +++ b/src/raknet/DS_HuffmanEncodingTreeNode.h @@ -1,21 +1,21 @@ -/// \file -/// \brief \b [Internal] A single node in the Huffman Encoding Tree. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __HUFFMAN_ENCODING_TREE_NODE -#define __HUFFMAN_ENCODING_TREE_NODE - -struct HuffmanEncodingTreeNode -{ - unsigned char value; - unsigned weight; - HuffmanEncodingTreeNode *left; - HuffmanEncodingTreeNode *right; - HuffmanEncodingTreeNode *parent; -}; - -#endif +/// \file +/// \brief \b [Internal] A single node in the Huffman Encoding Tree. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __HUFFMAN_ENCODING_TREE_NODE +#define __HUFFMAN_ENCODING_TREE_NODE + +struct HuffmanEncodingTreeNode +{ + unsigned char value; + unsigned weight; + HuffmanEncodingTreeNode *left; + HuffmanEncodingTreeNode *right; + HuffmanEncodingTreeNode *parent; +}; + +#endif diff --git a/src/raknet/DS_LinkedList.h b/src/raknet/DS_LinkedList.h index 4002855..be9548e 100755 --- a/src/raknet/DS_LinkedList.h +++ b/src/raknet/DS_LinkedList.h @@ -1,1252 +1,1252 @@ -/// \file DS_LinkedList.h -/// \internal -/// \brief Straightforward linked list data structure. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __LINKED_LIST_H -#define __LINKED_LIST_H - -#include "Export.h" -#include "RakMemoryOverride.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - // Prototype to prevent error in CircularLinkedList class when a reference is made to a LinkedList class - template - class RAK_DLL_EXPORT LinkedList; - - /** - * \brief (Circular) Linked List ADT (Doubly Linked Pointer to Node Style) - - * - * \details - * By Kevin Jenkins (http://www.rakkar.org) - * Initilize with the following command - * LinkedList - * OR - * CircularLinkedList - * - * Has the following member functions - * - size: returns number of elements in the linked list - * - insert(item): inserts @em item at the current position in - * the LinkedList. - * - add(item): inserts @em item after the current position in - * the LinkedList. Does not increment the position - * - replace(item): replaces the element at the current position @em item. - * - peek: returns the element at the current position - * - pop: returns the element at the current position and deletes it - * - del: deletes the current element. Does nothing for an empty list. - * - clear: empties the LinkedList and returns storage - * - bool IsInitem): Does a linear search for @em item. Does not set - * the position to it, only returns true on item found, false otherwise - * - bool find(item): Does a linear search for @em item and sets the current - * position to point to it if and only if the item is found. Returns true - * on item found, false otherwise - * - sort: Sorts the elements of the list with a mergesort and sets the - * current pointer to the first element - * - concatenate(list L): This appends L to the current list - * - ++(prefix): moves the pointer one element up in the list and returns the - * appropriate copy of the element in the list - * - --(prefix): moves the pointer one element back in the list and returns - * the appropriate copy of the element in the list - * - beginning - moves the pointer to the start of the list. For circular - * linked lists this is first 'position' created. You should call this - * after the sort function to read the first value. - * - end - moves the pointer to the end of the list. For circular linked - * lists this is one less than the first 'position' created - * The assignment and copy constructor operators are defined - * - * \note - * 1. LinkedList and CircularLinkedList are exactly the same except LinkedList - * won't let you wrap around the root and lets you jump to two positions - * relative to the root/ - * 2. Postfix ++ and -- can be used but simply call the prefix versions. - * - * - * EXAMPLE: - * @code - * LinkedList A; // Creates a Linked List of integers called A - * CircularLinkedList B; // Creates a Circular Linked List of - * // integers called B - * - * A.Insert(20); // Adds 20 to A. A: 20 - current is 20 - * A.Insert(5); // Adds 5 to A. A: 5 20 - current is 5 - * A.Insert(1); // Adds 1 to A. A: 1 5 20 - current is 1 - * - * A.IsIn1); // returns true - * A.IsIn200); // returns false - * A.Find(5); // returns true and sets current to 5 - * A.Peek(); // returns 5 - * A.Find(1); // returns true and sets current to 1 - * - * (++A).Peek(); // Returns 5 - * A.Peek(); // Returns 5 - * - * A.Replace(10); // Replaces 5 with 10. - * A.Peek(); // Returns 10 - * - * A.Beginning(); // Current points to the beginning of the list at 1 - * - * (++A).Peek(); // Returns 5 - * A.Peek(); // Returns 10 - * - * A.Del(); // Deletes 10. Current points to the next element, which is 20 - * A.Peek(); // Returns 20 - * - * A.Beginning(); // Current points to the beginning of the list at 1 - * - * (++A).Peek(); // Returns 5 - * A.Peek(); // Returns 20 - * - * A.Clear(_FILE_AND_LINE_); // Deletes all nodes in A - * - * A.Insert(5); // A: 5 - current is 5 - * A.Insert(6); // A: 6 5 - current is 6 - * A.Insert(7); // A: 7 6 5 - current is 7 - * - * A.Clear(_FILE_AND_LINE_); - * B.Clear(_FILE_AND_LINE_); - * - * B.Add(10); - * B.Add(20); - * B.Add(30); - * B.Add(5); - * B.Add(2); - * B.Add(25); - * // Sorts the numbers in the list and sets the current pointer to the - * // first element - * B.sort(); - * - * // Postfix ++ just calls the prefix version and has no functional - * // difference. - * B.Peek(); // Returns 2 - * B++; - * B.Peek(); // Returns 5 - * B++; - * B.Peek(); // Returns 10 - * B++; - * B.Peek(); // Returns 20 - * B++; - * B.Peek(); // Returns 25 - * B++; - * B.Peek(); // Returns 30 - * @endcode - */ - template - - class CircularLinkedList - { - - public: - - struct node - { - CircularLinkedListType item; - - node* previous; - node* next; - }; - - CircularLinkedList(); - ~CircularLinkedList(); - CircularLinkedList( const CircularLinkedList& original_copy ); - // CircularLinkedList(LinkedList original_copy) {CircularLinkedList(original_copy);} // Converts linked list to circular type - bool operator= ( const CircularLinkedList& original_copy ); - CircularLinkedList& operator++(); // CircularLinkedList A; ++A; - CircularLinkedList& operator++( int ); // Circular_Linked List A; A++; - CircularLinkedList& operator--(); // CircularLinkedList A; --A; - CircularLinkedList& operator--( int ); // Circular_Linked List A; A--; - bool IsIn( const CircularLinkedListType& input ); - bool Find( const CircularLinkedListType& input ); - void Insert( const CircularLinkedListType& input ); - - CircularLinkedListType& Add ( const CircularLinkedListType& input ) - - ; // Adds after the current position - void Replace( const CircularLinkedListType& input ); - - void Del( void ); - - unsigned int Size( void ); - - CircularLinkedListType& Peek( void ); - - CircularLinkedListType Pop( void ); - - void Clear( void ); - - void Sort( void ); - - void Beginning( void ); - - void End( void ); - - void Concatenate( const CircularLinkedList& L ); - - protected: - unsigned int list_size; - - node *root; - - node *position; - - node* FindPointer( const CircularLinkedListType& input ); - - private: - CircularLinkedList Merge( CircularLinkedList L1, CircularLinkedList L2 ); - - CircularLinkedList Mergesort( const CircularLinkedList& L ); - }; - - template - - class LinkedList : public CircularLinkedList - { - - public: - LinkedList() - {} - - LinkedList( const LinkedList& original_copy ); - ~LinkedList(); - bool operator= ( const LinkedList& original_copy ); - LinkedList& operator++(); // LinkedList A; ++A; - LinkedList& operator++( int ); // Linked List A; A++; - LinkedList& operator--(); // LinkedList A; --A; - LinkedList& operator--( int ); // Linked List A; A--; - - private: - LinkedList Merge( LinkedList L1, LinkedList L2 ); - LinkedList Mergesort( const LinkedList& L ); - - }; - - - template - inline void CircularLinkedList::Beginning( void ) - { - if ( this->root ) - this->position = this->root; - } - - template - inline void CircularLinkedList::End( void ) - { - if ( this->root ) - this->position = this->root->previous; - } - - template - bool LinkedList::operator= ( const LinkedList& original_copy ) - { - typename LinkedList::node * original_copy_pointer, *last, *save_position; - - if ( ( &original_copy ) != this ) - { - - this->Clear(); - - - if ( original_copy.list_size == 0 ) - { - this->root = 0; - this->position = 0; - this->list_size = 0; - } - - else - if ( original_copy.list_size == 1 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - } - - else - { - // Setup the first part of the root node - original_copy_pointer = original_copy.root; - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - do - { - - - // Save the current element - last = this->position; - - // Point to the next node in the source list - original_copy_pointer = original_copy_pointer->next; - - // Create a new node and point position to it - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // Copy the item to the new node - // *(position->item)=*(original_copy_pointer->item); - this->position->item = original_copy_pointer->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - - // Set the previous pointer for the new node - ( this->position->previous ) = last; - - // Set the next pointer for the old node to the new node - ( last->next ) = this->position; - - } - - while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); - - // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node - this->position->next = this->root; - - this->root->previous = this->position; - - this->list_size = original_copy.list_size; - - this->position = save_position; - } - } - - return true; - } - - - template - CircularLinkedList::CircularLinkedList() - { - this->root = 0; - this->position = 0; - this->list_size = 0; - } - - template - CircularLinkedList::~CircularLinkedList() - { - this->Clear(); - } - - template - LinkedList::~LinkedList() - { - this->Clear(); - } - - template - LinkedList::LinkedList( const LinkedList& original_copy ) - { - typename LinkedList::node * original_copy_pointer, *last, *save_position; - - if ( original_copy.list_size == 0 ) - { - this->root = 0; - this->position = 0; - this->list_size = 0; - return ; - } - - else - if ( original_copy.list_size == 1 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // *(root->item) = *((original_copy.root)->item); - this->root->item = original_copy.root->item; - } - - else - { - // Setup the first part of the root node - original_copy_pointer = original_copy.root; - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - do - { - // Save the current element - last = this->position; - - // Point to the next node in the source list - original_copy_pointer = original_copy_pointer->next; - - // Create a new node and point position to it - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // Copy the item to the new node - // *(position->item)=*(original_copy_pointer->item); - this->position->item = original_copy_pointer->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - // Set the previous pointer for the new node - ( this->position->previous ) = last; - - // Set the next pointer for the old node to the new node - ( last->next ) = this->position; - - } - - while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); - - // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node - this->position->next = this->root; - - this->root->previous = this->position; - - this->list_size = original_copy.list_size; - - this->position = save_position; - } - } - -#ifdef _MSC_VER -#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized -#endif - template - CircularLinkedList::CircularLinkedList( const CircularLinkedList& original_copy ) - { - node * original_copy_pointer; - node *last; - node *save_position; - - if ( original_copy.list_size == 0 ) - { - this->root = 0; - this->position = 0; - this->list_size = 0; - return ; - } - - else - if ( original_copy.list_size == 1 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // *(root->item) = *((original_copy.root)->item); - this->root->item = original_copy.root->item; - } - - else - { - // Setup the first part of the root node - original_copy_pointer = original_copy.root; - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - do - { - - - // Save the current element - last = this->position; - - // Point to the next node in the source list - original_copy_pointer = original_copy_pointer->next; - - // Create a new node and point position to it - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // Copy the item to the new node - // *(position->item)=*(original_copy_pointer->item); - this->position->item = original_copy_pointer->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = position; - - // Set the previous pointer for the new node - ( this->position->previous ) = last; - - // Set the next pointer for the old node to the new node - ( last->next ) = this->position; - - } - - while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); - - // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node - this->position->next = this->root; - - this->root->previous = position; - - this->list_size = original_copy.list_size; - - this->position = save_position; - } - } - -#ifdef _MSC_VER -#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized -#endif - template - bool CircularLinkedList::operator= ( const CircularLinkedList& original_copy ) - { - node * original_copy_pointer; - node *last; - node *save_position; - - if ( ( &original_copy ) != this ) - { - - this->Clear(); - - - if ( original_copy.list_size == 0 ) - { - this->root = 0; - this->position = 0; - this->list_size = 0; - } - - else - if ( original_copy.list_size == 1 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - } - - else - { - // Setup the first part of the root node - original_copy_pointer = original_copy.root; - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->position = this->root; - // *(root->item)=*((original_copy.root)->item); - this->root->item = original_copy.root->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - do - { - // Save the current element - last = this->position; - - // Point to the next node in the source list - original_copy_pointer = original_copy_pointer->next; - - // Create a new node and point position to it - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // Copy the item to the new node - // *(position->item)=*(original_copy_pointer->item); - this->position->item = original_copy_pointer->item; - - if ( original_copy_pointer == original_copy.position ) - save_position = this->position; - - // Set the previous pointer for the new node - ( this->position->previous ) = last; - - // Set the next pointer for the old node to the new node - ( last->next ) = this->position; - - } - - while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); - - // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node - this->position->next = this->root; - - this->root->previous = this->position; - - this->list_size = original_copy.list_size; - - this->position = save_position; - } - } - - return true; - } - - template - void CircularLinkedList::Insert( const CircularLinkedListType& input ) - { - node * new_node; - - if ( list_size == 0 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - //*(root->item)=input; - this->root->item = input; - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - } - - else - if ( list_size == 1 ) - { - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->position; - this->root->previous = this->position; - this->position->previous = this->root; - this->position->next = this->root; - // *(position->item)=input; - this->position->item = input; - this->root = this->position; // Since we're inserting into a 1 element list the old root is now the second item - this->list_size = 2; - } - - else - { - /* - - B - | - A --- C - - position->previous=A - new_node=B - position=C - - Note that the order of the following statements is important */ - - new_node = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // new_node->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // *(new_node->item)=input; - new_node->item = input; - - // Point next of A to B - ( this->position->previous ) ->next = new_node; - - // Point last of B to A - new_node->previous = this->position->previous; - - // Point last of C to B - this->position->previous = new_node; - - // Point next of B to C - new_node->next = this->position; - - // Since the root pointer is bound to a node rather than an index this moves it back if you insert an element at the root - - if ( this->position == this->root ) - { - this->root = new_node; - this->position = this->root; - } - - // Increase the recorded size of the list by one - this->list_size++; - } - } - - template - CircularLinkedListType& CircularLinkedList::Add ( const CircularLinkedListType& input ) - { - node * new_node; - - if ( this->list_size == 0 ) - { - this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // *(root->item)=input; - this->root->item = input; - this->root->next = this->root; - this->root->previous = this->root; - this->list_size = 1; - this->position = this->root; - // return *(position->item); - return this->position->item; - } - - else - if ( list_size == 1 ) - { - this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - this->root->next = this->position; - this->root->previous = this->position; - this->position->previous = this->root; - this->position->next = this->root; - // *(position->item)=input; - this->position->item = input; - this->list_size = 2; - this->position = this->root; // Don't move the position from the root - // return *(position->item); - return this->position->item; - } - - else - { - /* - - B - | - A --- C - - new_node=B - position=A - position->next=C - - Note that the order of the following statements is important */ - - new_node = RakNet::OP_NEW( _FILE_AND_LINE_ ); - // new_node->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); - - // *(new_node->item)=input; - new_node->item = input; - - // Point last of B to A - new_node->previous = this->position; - - // Point next of B to C - new_node->next = ( this->position->next ); - - // Point last of C to B - ( this->position->next ) ->previous = new_node; - - // Point next of A to B - ( this->position->next ) = new_node; - - // Increase the recorded size of the list by one - this->list_size++; - - // return *(new_node->item); - return new_node->item; - } - } - - template - inline void CircularLinkedList::Replace( const CircularLinkedListType& input ) - { - if ( this->list_size > 0 ) - // *(position->item)=input; - this->position->item = input; - } - - template - void CircularLinkedList::Del() - { - node * new_position; - - if ( this->list_size == 0 ) - return ; - - else - if ( this->list_size == 1 ) - { - // RakNet::OP_DELETE(root->item, _FILE_AND_LINE_); - RakNet::OP_DELETE(this->root, _FILE_AND_LINE_); - this->root = this->position = 0; - this->list_size = 0; - } - - else - { - ( this->position->previous ) ->next = this->position->next; - ( this->position->next ) ->previous = this->position->previous; - new_position = this->position->next; - - if ( this->position == this->root ) - this->root = new_position; - - // RakNet::OP_DELETE(position->item, _FILE_AND_LINE_); - RakNet::OP_DELETE(this->position, _FILE_AND_LINE_); - - this->position = new_position; - - this->list_size--; - } - } - - template - bool CircularLinkedList::IsIn(const CircularLinkedListType& input ) - { - node * return_value, *old_position; - - old_position = this->position; - - return_value = FindPointer( input ); - this->position = old_position; - - if ( return_value != 0 ) - return true; - else - return false; // Can't find the item don't do anything - } - - template - bool CircularLinkedList::Find( const CircularLinkedListType& input ) - { - node * return_value; - - return_value = FindPointer( input ); - - if ( return_value != 0 ) - { - this->position = return_value; - return true; - } - - else - return false; // Can't find the item don't do anything - } - - template - typename CircularLinkedList::node* CircularLinkedList::FindPointer( const CircularLinkedListType& input ) - { - node * current; - - if ( this->list_size == 0 ) - return 0; - - current = this->root; - - // Search for the item starting from the root node and incrementing the pointer after every check - // If you wind up pointing at the root again you looped around the list so didn't find the item, in which case return 0 - do - { - // if (*(current->item) == input) return current; - - if ( current->item == input ) - return current; - - current = current->next; - } - - while ( current != this->root ); - - return 0; - - } - - template - inline unsigned int CircularLinkedList::Size( void ) - { - return this->list_size; - } - - template - inline CircularLinkedListType& CircularLinkedList::Peek( void ) - { - // return *(position->item); - return this->position->item; - } - - template - CircularLinkedListType CircularLinkedList::Pop( void ) - { - CircularLinkedListType element; - element = Peek(); - Del(); - return CircularLinkedListType( element ); // return temporary - } - - // Prefix - template - CircularLinkedList& CircularLinkedList::operator++() - { - if ( this->list_size != 0 ) - position = position->next; - - return *this; - } - - /* - // Postfix - template - CircularLinkedList& CircularLinkedList::operator++(int) - { - CircularLinkedList before; - before=*this; - operator++(); - return before; - } - */ - - template - CircularLinkedList& CircularLinkedList::operator++( int ) - { - return this->operator++(); - } - - // Prefix - template - CircularLinkedList& CircularLinkedList::operator--() - { - if ( this->list_size != 0 ) - this->position = this->position->previous; - - return *this; - } - - /* - // Postfix - template - CircularLinkedList& CircularLinkedList::operator--(int) - { - CircularLinkedList before; - before=*this; - operator--(); - return before; - } - */ - - template - CircularLinkedList& CircularLinkedList::operator--( int ) - { - return this->operator--(); - } - - template - void CircularLinkedList::Clear( void ) - { - if ( this->list_size == 0 ) - return ; - else - if ( this->list_size == 1 ) // {RakNet::OP_DELETE(root->item); RakNet::OP_DELETE(root, _FILE_AND_LINE_);} - { - RakNet::OP_DELETE(this->root, _FILE_AND_LINE_); - } - - else - { - node* current; - node* temp; - - current = this->root; - - do - { - temp = current; - current = current->next; - // RakNet::OP_DELETE(temp->item, _FILE_AND_LINE_); - RakNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - - while ( current != this->root ); - } - - this->list_size = 0; - this->root = 0; - this->position = 0; - } - - template - inline void CircularLinkedList::Concatenate( const CircularLinkedList& L ) - { - unsigned int counter; - node* ptr; - - if ( L.list_size == 0 ) - return ; - - if ( this->list_size == 0 ) - * this = L; - - ptr = L.root; - - this->position = this->root->previous; - - // Cycle through each element in L and add it to the current list - for ( counter = 0; counter < L.list_size; counter++ ) - { - // Add item after the current item pointed to - // add(*(ptr->item)); - - Add ( ptr->item ); - - // Update pointers. Moving ptr keeps the current pointer at the end of the list since the add function does not move the pointer - ptr = ptr->next; - - this->position = this->position->next; - } - } - - template - inline void CircularLinkedList::Sort( void ) - { - if ( this->list_size <= 1 ) - return ; - - // Call equal operator to assign result of mergesort to current object - *this = Mergesort( *this ); - - this->position = this->root; - } - - template - CircularLinkedList CircularLinkedList::Mergesort( const CircularLinkedList& L ) - { - unsigned int counter; - node* location; - CircularLinkedList L1; - CircularLinkedList L2; - - location = L.root; - - // Split the list into two equal size sublists, L1 and L2 - - for ( counter = 0; counter < L.list_size / 2; counter++ ) - { - // L1.add (*(location->item)); - L1.Add ( location->item ); - location = location->next; - } - - for ( ;counter < L.list_size; counter++ ) - { - // L2.Add(*(location->item)); - L2.Add ( location->item ); - location = location->next; - } - - // Recursively sort the sublists - if ( L1.list_size > 1 ) - L1 = Mergesort( L1 ); - - if ( L2.list_size > 1 ) - L2 = Mergesort( L2 ); - - // Merge the two sublists - return Merge( L1, L2 ); - } - - template - CircularLinkedList CircularLinkedList::Merge( CircularLinkedList L1, CircularLinkedList L2 ) - { - CircularLinkedList X; - CircularLinkedListType element; - L1.position = L1.root; - L2.position = L2.root; - - // While neither list is empty - - while ( ( L1.list_size != 0 ) && ( L2.list_size != 0 ) ) - { - // Compare the first items of L1 and L2 - // Remove the smaller of the two items from the list - - if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) - // if ((*((L1.root)->item)) < (*((L2.root)->item))) - { - // element = *((L1.root)->item); - element = ( L1.root ) ->item; - L1.Del(); - } - else - { - // element = *((L2.root)->item); - element = ( L2.root ) ->item; - L2.Del(); - } - - // Add this item to the end of X - X.Add( element ); - - X++; - } - - // Add the remaining list to X - if ( L1.list_size != 0 ) - X.Concatenate( L1 ); - else - X.Concatenate( L2 ); - - return X; - } - - template - LinkedList LinkedList::Mergesort( const LinkedList& L ) - { - unsigned int counter; - typename LinkedList::node* location; - LinkedList L1; - LinkedList L2; - - location = L.root; - - // Split the list into two equal size sublists, L1 and L2 - - for ( counter = 0; counter < L.LinkedList_size / 2; counter++ ) - { - // L1.add (*(location->item)); - L1.Add ( location->item ); - location = location->next; - } - - for ( ;counter < L.LinkedList_size; counter++ ) - { - // L2.Add(*(location->item)); - L2.Add ( location->item ); - location = location->next; - } - - // Recursively sort the sublists - if ( L1.list_size > 1 ) - L1 = Mergesort( L1 ); - - if ( L2.list_size > 1 ) - L2 = Mergesort( L2 ); - - // Merge the two sublists - return Merge( L1, L2 ); - } - - template - LinkedList LinkedList::Merge( LinkedList L1, LinkedList L2 ) - { - LinkedList X; - LinkedListType element; - L1.position = L1.root; - L2.position = L2.root; - - // While neither list is empty - - while ( ( L1.LinkedList_size != 0 ) && ( L2.LinkedList_size != 0 ) ) - { - // Compare the first items of L1 and L2 - // Remove the smaller of the two items from the list - - if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) - // if ((*((L1.root)->item)) < (*((L2.root)->item))) - { - element = ( L1.root ) ->item; - // element = *((L1.root)->item); - L1.Del(); - } - else - { - element = ( L2.root ) ->item; - // element = *((L2.root)->item); - L2.Del(); - } - - // Add this item to the end of X - X.Add( element ); - } - - // Add the remaining list to X - if ( L1.LinkedList_size != 0 ) - X.concatenate( L1 ); - else - X.concatenate( L2 ); - - return X; - } - - - // Prefix - template - LinkedList& LinkedList::operator++() - { - if ( ( this->list_size != 0 ) && ( this->position->next != this->root ) ) - this->position = this->position->next; - - return *this; - } - - /* - // Postfix - template - LinkedList& LinkedList::operator++(int) - { - LinkedList before; - before=*this; - operator++(); - return before; - } - */ - // Postfix - template - LinkedList& LinkedList::operator++( int ) - { - return this->operator++(); - } - - // Prefix - template - LinkedList& LinkedList::operator--() - { - if ( ( this->list_size != 0 ) && ( this->position != this->root ) ) - this->position = this->position->previous; - - return *this; - } - - /* - // Postfix - template - LinkedList& LinkedList::operator--(int) - { - LinkedList before; - before=*this; - operator--(); - return before; - } - */ - - // Postfix - template - LinkedList& LinkedList::operator--( int ) - { - return this->operator--(); - } - -} // End namespace - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif +/// \file DS_LinkedList.h +/// \internal +/// \brief Straightforward linked list data structure. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __LINKED_LIST_H +#define __LINKED_LIST_H + +#include "Export.h" +#include "RakMemoryOverride.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + // Prototype to prevent error in CircularLinkedList class when a reference is made to a LinkedList class + template + class RAK_DLL_EXPORT LinkedList; + + /** + * \brief (Circular) Linked List ADT (Doubly Linked Pointer to Node Style) - + * + * \details + * By Kevin Jenkins (http://www.rakkar.org) + * Initilize with the following command + * LinkedList + * OR + * CircularLinkedList + * + * Has the following member functions + * - size: returns number of elements in the linked list + * - insert(item): inserts @em item at the current position in + * the LinkedList. + * - add(item): inserts @em item after the current position in + * the LinkedList. Does not increment the position + * - replace(item): replaces the element at the current position @em item. + * - peek: returns the element at the current position + * - pop: returns the element at the current position and deletes it + * - del: deletes the current element. Does nothing for an empty list. + * - clear: empties the LinkedList and returns storage + * - bool IsInitem): Does a linear search for @em item. Does not set + * the position to it, only returns true on item found, false otherwise + * - bool find(item): Does a linear search for @em item and sets the current + * position to point to it if and only if the item is found. Returns true + * on item found, false otherwise + * - sort: Sorts the elements of the list with a mergesort and sets the + * current pointer to the first element + * - concatenate(list L): This appends L to the current list + * - ++(prefix): moves the pointer one element up in the list and returns the + * appropriate copy of the element in the list + * - --(prefix): moves the pointer one element back in the list and returns + * the appropriate copy of the element in the list + * - beginning - moves the pointer to the start of the list. For circular + * linked lists this is first 'position' created. You should call this + * after the sort function to read the first value. + * - end - moves the pointer to the end of the list. For circular linked + * lists this is one less than the first 'position' created + * The assignment and copy constructor operators are defined + * + * \note + * 1. LinkedList and CircularLinkedList are exactly the same except LinkedList + * won't let you wrap around the root and lets you jump to two positions + * relative to the root/ + * 2. Postfix ++ and -- can be used but simply call the prefix versions. + * + * + * EXAMPLE: + * @code + * LinkedList A; // Creates a Linked List of integers called A + * CircularLinkedList B; // Creates a Circular Linked List of + * // integers called B + * + * A.Insert(20); // Adds 20 to A. A: 20 - current is 20 + * A.Insert(5); // Adds 5 to A. A: 5 20 - current is 5 + * A.Insert(1); // Adds 1 to A. A: 1 5 20 - current is 1 + * + * A.IsIn1); // returns true + * A.IsIn200); // returns false + * A.Find(5); // returns true and sets current to 5 + * A.Peek(); // returns 5 + * A.Find(1); // returns true and sets current to 1 + * + * (++A).Peek(); // Returns 5 + * A.Peek(); // Returns 5 + * + * A.Replace(10); // Replaces 5 with 10. + * A.Peek(); // Returns 10 + * + * A.Beginning(); // Current points to the beginning of the list at 1 + * + * (++A).Peek(); // Returns 5 + * A.Peek(); // Returns 10 + * + * A.Del(); // Deletes 10. Current points to the next element, which is 20 + * A.Peek(); // Returns 20 + * + * A.Beginning(); // Current points to the beginning of the list at 1 + * + * (++A).Peek(); // Returns 5 + * A.Peek(); // Returns 20 + * + * A.Clear(_FILE_AND_LINE_); // Deletes all nodes in A + * + * A.Insert(5); // A: 5 - current is 5 + * A.Insert(6); // A: 6 5 - current is 6 + * A.Insert(7); // A: 7 6 5 - current is 7 + * + * A.Clear(_FILE_AND_LINE_); + * B.Clear(_FILE_AND_LINE_); + * + * B.Add(10); + * B.Add(20); + * B.Add(30); + * B.Add(5); + * B.Add(2); + * B.Add(25); + * // Sorts the numbers in the list and sets the current pointer to the + * // first element + * B.sort(); + * + * // Postfix ++ just calls the prefix version and has no functional + * // difference. + * B.Peek(); // Returns 2 + * B++; + * B.Peek(); // Returns 5 + * B++; + * B.Peek(); // Returns 10 + * B++; + * B.Peek(); // Returns 20 + * B++; + * B.Peek(); // Returns 25 + * B++; + * B.Peek(); // Returns 30 + * @endcode + */ + template + + class CircularLinkedList + { + + public: + + struct node + { + CircularLinkedListType item; + + node* previous; + node* next; + }; + + CircularLinkedList(); + ~CircularLinkedList(); + CircularLinkedList( const CircularLinkedList& original_copy ); + // CircularLinkedList(LinkedList original_copy) {CircularLinkedList(original_copy);} // Converts linked list to circular type + bool operator= ( const CircularLinkedList& original_copy ); + CircularLinkedList& operator++(); // CircularLinkedList A; ++A; + CircularLinkedList& operator++( int ); // Circular_Linked List A; A++; + CircularLinkedList& operator--(); // CircularLinkedList A; --A; + CircularLinkedList& operator--( int ); // Circular_Linked List A; A--; + bool IsIn( const CircularLinkedListType& input ); + bool Find( const CircularLinkedListType& input ); + void Insert( const CircularLinkedListType& input ); + + CircularLinkedListType& Add ( const CircularLinkedListType& input ) + + ; // Adds after the current position + void Replace( const CircularLinkedListType& input ); + + void Del( void ); + + unsigned int Size( void ); + + CircularLinkedListType& Peek( void ); + + CircularLinkedListType Pop( void ); + + void Clear( void ); + + void Sort( void ); + + void Beginning( void ); + + void End( void ); + + void Concatenate( const CircularLinkedList& L ); + + protected: + unsigned int list_size; + + node *root; + + node *position; + + node* FindPointer( const CircularLinkedListType& input ); + + private: + CircularLinkedList Merge( CircularLinkedList L1, CircularLinkedList L2 ); + + CircularLinkedList Mergesort( const CircularLinkedList& L ); + }; + + template + + class LinkedList : public CircularLinkedList + { + + public: + LinkedList() + {} + + LinkedList( const LinkedList& original_copy ); + ~LinkedList(); + bool operator= ( const LinkedList& original_copy ); + LinkedList& operator++(); // LinkedList A; ++A; + LinkedList& operator++( int ); // Linked List A; A++; + LinkedList& operator--(); // LinkedList A; --A; + LinkedList& operator--( int ); // Linked List A; A--; + + private: + LinkedList Merge( LinkedList L1, LinkedList L2 ); + LinkedList Mergesort( const LinkedList& L ); + + }; + + + template + inline void CircularLinkedList::Beginning( void ) + { + if ( this->root ) + this->position = this->root; + } + + template + inline void CircularLinkedList::End( void ) + { + if ( this->root ) + this->position = this->root->previous; + } + + template + bool LinkedList::operator= ( const LinkedList& original_copy ) + { + typename LinkedList::node * original_copy_pointer, *last, *save_position; + + if ( ( &original_copy ) != this ) + { + + this->Clear(); + + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + + + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = this->position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + + return true; + } + + + template + CircularLinkedList::CircularLinkedList() + { + this->root = 0; + this->position = 0; + this->list_size = 0; + } + + template + CircularLinkedList::~CircularLinkedList() + { + this->Clear(); + } + + template + LinkedList::~LinkedList() + { + this->Clear(); + } + + template + LinkedList::LinkedList( const LinkedList& original_copy ) + { + typename LinkedList::node * original_copy_pointer, *last, *save_position; + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + return ; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item) = *((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = this->position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + +#ifdef _MSC_VER +#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized +#endif + template + CircularLinkedList::CircularLinkedList( const CircularLinkedList& original_copy ) + { + node * original_copy_pointer; + node *last; + node *save_position; + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + return ; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item) = *((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + + + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = position; + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + +#ifdef _MSC_VER +#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized +#endif + template + bool CircularLinkedList::operator= ( const CircularLinkedList& original_copy ) + { + node * original_copy_pointer; + node *last; + node *save_position; + + if ( ( &original_copy ) != this ) + { + + this->Clear(); + + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = this->position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + + return true; + } + + template + void CircularLinkedList::Insert( const CircularLinkedListType& input ) + { + node * new_node; + + if ( list_size == 0 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + //*(root->item)=input; + this->root->item = input; + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + } + + else + if ( list_size == 1 ) + { + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->position; + this->root->previous = this->position; + this->position->previous = this->root; + this->position->next = this->root; + // *(position->item)=input; + this->position->item = input; + this->root = this->position; // Since we're inserting into a 1 element list the old root is now the second item + this->list_size = 2; + } + + else + { + /* + + B + | + A --- C + + position->previous=A + new_node=B + position=C + + Note that the order of the following statements is important */ + + new_node = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // new_node->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // *(new_node->item)=input; + new_node->item = input; + + // Point next of A to B + ( this->position->previous ) ->next = new_node; + + // Point last of B to A + new_node->previous = this->position->previous; + + // Point last of C to B + this->position->previous = new_node; + + // Point next of B to C + new_node->next = this->position; + + // Since the root pointer is bound to a node rather than an index this moves it back if you insert an element at the root + + if ( this->position == this->root ) + { + this->root = new_node; + this->position = this->root; + } + + // Increase the recorded size of the list by one + this->list_size++; + } + } + + template + CircularLinkedListType& CircularLinkedList::Add ( const CircularLinkedListType& input ) + { + node * new_node; + + if ( this->list_size == 0 ) + { + this->root = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // root->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // *(root->item)=input; + this->root->item = input; + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // return *(position->item); + return this->position->item; + } + + else + if ( list_size == 1 ) + { + this->position = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // position->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + this->root->next = this->position; + this->root->previous = this->position; + this->position->previous = this->root; + this->position->next = this->root; + // *(position->item)=input; + this->position->item = input; + this->list_size = 2; + this->position = this->root; // Don't move the position from the root + // return *(position->item); + return this->position->item; + } + + else + { + /* + + B + | + A --- C + + new_node=B + position=A + position->next=C + + Note that the order of the following statements is important */ + + new_node = RakNet::OP_NEW( _FILE_AND_LINE_ ); + // new_node->item = RakNet::OP_NEW( _FILE_AND_LINE_ ); + + // *(new_node->item)=input; + new_node->item = input; + + // Point last of B to A + new_node->previous = this->position; + + // Point next of B to C + new_node->next = ( this->position->next ); + + // Point last of C to B + ( this->position->next ) ->previous = new_node; + + // Point next of A to B + ( this->position->next ) = new_node; + + // Increase the recorded size of the list by one + this->list_size++; + + // return *(new_node->item); + return new_node->item; + } + } + + template + inline void CircularLinkedList::Replace( const CircularLinkedListType& input ) + { + if ( this->list_size > 0 ) + // *(position->item)=input; + this->position->item = input; + } + + template + void CircularLinkedList::Del() + { + node * new_position; + + if ( this->list_size == 0 ) + return ; + + else + if ( this->list_size == 1 ) + { + // RakNet::OP_DELETE(root->item, _FILE_AND_LINE_); + RakNet::OP_DELETE(this->root, _FILE_AND_LINE_); + this->root = this->position = 0; + this->list_size = 0; + } + + else + { + ( this->position->previous ) ->next = this->position->next; + ( this->position->next ) ->previous = this->position->previous; + new_position = this->position->next; + + if ( this->position == this->root ) + this->root = new_position; + + // RakNet::OP_DELETE(position->item, _FILE_AND_LINE_); + RakNet::OP_DELETE(this->position, _FILE_AND_LINE_); + + this->position = new_position; + + this->list_size--; + } + } + + template + bool CircularLinkedList::IsIn(const CircularLinkedListType& input ) + { + node * return_value, *old_position; + + old_position = this->position; + + return_value = FindPointer( input ); + this->position = old_position; + + if ( return_value != 0 ) + return true; + else + return false; // Can't find the item don't do anything + } + + template + bool CircularLinkedList::Find( const CircularLinkedListType& input ) + { + node * return_value; + + return_value = FindPointer( input ); + + if ( return_value != 0 ) + { + this->position = return_value; + return true; + } + + else + return false; // Can't find the item don't do anything + } + + template + typename CircularLinkedList::node* CircularLinkedList::FindPointer( const CircularLinkedListType& input ) + { + node * current; + + if ( this->list_size == 0 ) + return 0; + + current = this->root; + + // Search for the item starting from the root node and incrementing the pointer after every check + // If you wind up pointing at the root again you looped around the list so didn't find the item, in which case return 0 + do + { + // if (*(current->item) == input) return current; + + if ( current->item == input ) + return current; + + current = current->next; + } + + while ( current != this->root ); + + return 0; + + } + + template + inline unsigned int CircularLinkedList::Size( void ) + { + return this->list_size; + } + + template + inline CircularLinkedListType& CircularLinkedList::Peek( void ) + { + // return *(position->item); + return this->position->item; + } + + template + CircularLinkedListType CircularLinkedList::Pop( void ) + { + CircularLinkedListType element; + element = Peek(); + Del(); + return CircularLinkedListType( element ); // return temporary + } + + // Prefix + template + CircularLinkedList& CircularLinkedList::operator++() + { + if ( this->list_size != 0 ) + position = position->next; + + return *this; + } + + /* + // Postfix + template + CircularLinkedList& CircularLinkedList::operator++(int) + { + CircularLinkedList before; + before=*this; + operator++(); + return before; + } + */ + + template + CircularLinkedList& CircularLinkedList::operator++( int ) + { + return this->operator++(); + } + + // Prefix + template + CircularLinkedList& CircularLinkedList::operator--() + { + if ( this->list_size != 0 ) + this->position = this->position->previous; + + return *this; + } + + /* + // Postfix + template + CircularLinkedList& CircularLinkedList::operator--(int) + { + CircularLinkedList before; + before=*this; + operator--(); + return before; + } + */ + + template + CircularLinkedList& CircularLinkedList::operator--( int ) + { + return this->operator--(); + } + + template + void CircularLinkedList::Clear( void ) + { + if ( this->list_size == 0 ) + return ; + else + if ( this->list_size == 1 ) // {RakNet::OP_DELETE(root->item); RakNet::OP_DELETE(root, _FILE_AND_LINE_);} + { + RakNet::OP_DELETE(this->root, _FILE_AND_LINE_); + } + + else + { + node* current; + node* temp; + + current = this->root; + + do + { + temp = current; + current = current->next; + // RakNet::OP_DELETE(temp->item, _FILE_AND_LINE_); + RakNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + + while ( current != this->root ); + } + + this->list_size = 0; + this->root = 0; + this->position = 0; + } + + template + inline void CircularLinkedList::Concatenate( const CircularLinkedList& L ) + { + unsigned int counter; + node* ptr; + + if ( L.list_size == 0 ) + return ; + + if ( this->list_size == 0 ) + * this = L; + + ptr = L.root; + + this->position = this->root->previous; + + // Cycle through each element in L and add it to the current list + for ( counter = 0; counter < L.list_size; counter++ ) + { + // Add item after the current item pointed to + // add(*(ptr->item)); + + Add ( ptr->item ); + + // Update pointers. Moving ptr keeps the current pointer at the end of the list since the add function does not move the pointer + ptr = ptr->next; + + this->position = this->position->next; + } + } + + template + inline void CircularLinkedList::Sort( void ) + { + if ( this->list_size <= 1 ) + return ; + + // Call equal operator to assign result of mergesort to current object + *this = Mergesort( *this ); + + this->position = this->root; + } + + template + CircularLinkedList CircularLinkedList::Mergesort( const CircularLinkedList& L ) + { + unsigned int counter; + node* location; + CircularLinkedList L1; + CircularLinkedList L2; + + location = L.root; + + // Split the list into two equal size sublists, L1 and L2 + + for ( counter = 0; counter < L.list_size / 2; counter++ ) + { + // L1.add (*(location->item)); + L1.Add ( location->item ); + location = location->next; + } + + for ( ;counter < L.list_size; counter++ ) + { + // L2.Add(*(location->item)); + L2.Add ( location->item ); + location = location->next; + } + + // Recursively sort the sublists + if ( L1.list_size > 1 ) + L1 = Mergesort( L1 ); + + if ( L2.list_size > 1 ) + L2 = Mergesort( L2 ); + + // Merge the two sublists + return Merge( L1, L2 ); + } + + template + CircularLinkedList CircularLinkedList::Merge( CircularLinkedList L1, CircularLinkedList L2 ) + { + CircularLinkedList X; + CircularLinkedListType element; + L1.position = L1.root; + L2.position = L2.root; + + // While neither list is empty + + while ( ( L1.list_size != 0 ) && ( L2.list_size != 0 ) ) + { + // Compare the first items of L1 and L2 + // Remove the smaller of the two items from the list + + if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) + // if ((*((L1.root)->item)) < (*((L2.root)->item))) + { + // element = *((L1.root)->item); + element = ( L1.root ) ->item; + L1.Del(); + } + else + { + // element = *((L2.root)->item); + element = ( L2.root ) ->item; + L2.Del(); + } + + // Add this item to the end of X + X.Add( element ); + + X++; + } + + // Add the remaining list to X + if ( L1.list_size != 0 ) + X.Concatenate( L1 ); + else + X.Concatenate( L2 ); + + return X; + } + + template + LinkedList LinkedList::Mergesort( const LinkedList& L ) + { + unsigned int counter; + typename LinkedList::node* location; + LinkedList L1; + LinkedList L2; + + location = L.root; + + // Split the list into two equal size sublists, L1 and L2 + + for ( counter = 0; counter < L.LinkedList_size / 2; counter++ ) + { + // L1.add (*(location->item)); + L1.Add ( location->item ); + location = location->next; + } + + for ( ;counter < L.LinkedList_size; counter++ ) + { + // L2.Add(*(location->item)); + L2.Add ( location->item ); + location = location->next; + } + + // Recursively sort the sublists + if ( L1.list_size > 1 ) + L1 = Mergesort( L1 ); + + if ( L2.list_size > 1 ) + L2 = Mergesort( L2 ); + + // Merge the two sublists + return Merge( L1, L2 ); + } + + template + LinkedList LinkedList::Merge( LinkedList L1, LinkedList L2 ) + { + LinkedList X; + LinkedListType element; + L1.position = L1.root; + L2.position = L2.root; + + // While neither list is empty + + while ( ( L1.LinkedList_size != 0 ) && ( L2.LinkedList_size != 0 ) ) + { + // Compare the first items of L1 and L2 + // Remove the smaller of the two items from the list + + if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) + // if ((*((L1.root)->item)) < (*((L2.root)->item))) + { + element = ( L1.root ) ->item; + // element = *((L1.root)->item); + L1.Del(); + } + else + { + element = ( L2.root ) ->item; + // element = *((L2.root)->item); + L2.Del(); + } + + // Add this item to the end of X + X.Add( element ); + } + + // Add the remaining list to X + if ( L1.LinkedList_size != 0 ) + X.concatenate( L1 ); + else + X.concatenate( L2 ); + + return X; + } + + + // Prefix + template + LinkedList& LinkedList::operator++() + { + if ( ( this->list_size != 0 ) && ( this->position->next != this->root ) ) + this->position = this->position->next; + + return *this; + } + + /* + // Postfix + template + LinkedList& LinkedList::operator++(int) + { + LinkedList before; + before=*this; + operator++(); + return before; + } + */ + // Postfix + template + LinkedList& LinkedList::operator++( int ) + { + return this->operator++(); + } + + // Prefix + template + LinkedList& LinkedList::operator--() + { + if ( ( this->list_size != 0 ) && ( this->position != this->root ) ) + this->position = this->position->previous; + + return *this; + } + + /* + // Postfix + template + LinkedList& LinkedList::operator--(int) + { + LinkedList before; + before=*this; + operator--(); + return before; + } + */ + + // Postfix + template + LinkedList& LinkedList::operator--( int ) + { + return this->operator--(); + } + +} // End namespace + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/src/raknet/DS_List.h b/src/raknet/DS_List.h index a360053..f99d411 100755 --- a/src/raknet/DS_List.h +++ b/src/raknet/DS_List.h @@ -1,518 +1,518 @@ -/// \file DS_List.h -/// \internal -/// \brief Array based list. -/// \details Usually the Queue class is used instead, since it has all the same functionality and is only worse at random access. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __LIST_H -#define __LIST_H - -#include "RakAssert.h" -#include // memmove -#include "Export.h" -#include "RakMemoryOverride.h" - -/// Maximum unsigned long -static const unsigned int MAX_UNSIGNED_LONG = 4294967295U; - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// \brief Array based implementation of a list. - /// \note ONLY USE THIS FOR SHALLOW COPIES. I don't bother with operator= to improve performance. - template - class RAK_DLL_EXPORT List - { - public: - /// Default constructor - List(); - - // Destructor - ~List(); - - /// \brief Copy constructor. - /// \param[in] original_copy The list to duplicate - List( const List& original_copy ); - - /// \brief Assign one list to another. - List& operator= ( const List& original_copy ); - - /// \brief Access an element by its index in the array. - /// \param[in] position The index into the array. - /// \return The element at position \a position. - list_type& operator[] ( const unsigned int position ) const; - - /// \brief Access an element by its index in the array. - /// \param[in] position The index into the array. - /// \return The element at position \a position. - list_type& Get ( const unsigned int position ) const; - - /// \brief Push an element at the end of the stack. - /// \param[in] input The new element. - void Push(const list_type &input, const char *file, unsigned int line ); - - /// \brief Pop an element from the end of the stack. - /// \pre Size()>0 - /// \return The element at the end. - list_type& Pop(void); - - /// \brief Insert an element at position \a position in the list. - /// \param[in] input The new element. - /// \param[in] position The position of the new element. - void Insert( const list_type &input, const unsigned int position, const char *file, unsigned int line ); - - /// \brief Insert at the end of the list. - /// \param[in] input The new element. - void Insert( const list_type &input, const char *file, unsigned int line ); - - /// \brief Replace the value at \a position by \a input. - /// \details If the size of the list is less than @em position, it increase the capacity of - /// the list and fill slot with @em filler. - /// \param[in] input The element to replace at position @em position. - /// \param[in] filler The element use to fill new allocated capacity. - /// \param[in] position The position of input in the list. - void Replace( const list_type &input, const list_type filler, const unsigned int position, const char *file, unsigned int line ); - - /// \brief Replace the last element of the list by \a input. - /// \param[in] input The element used to replace the last element. - void Replace( const list_type &input ); - - /// \brief Delete the element at position \a position. - /// \param[in] position The index of the element to delete - void RemoveAtIndex( const unsigned int position ); - - /// \brief Delete the element at position \a position. - /// \note - swaps middle with end of list, only use if list order does not matter - /// \param[in] position The index of the element to delete - void RemoveAtIndexFast( const unsigned int position ); - - /// \brief Delete the element at the end of the list. - void RemoveFromEnd(const unsigned num=1); - - /// \brief Returns the index of the specified item or MAX_UNSIGNED_LONG if not found. - /// \param[in] input The element to check for - /// \return The index or position of @em input in the list. - /// \retval MAX_UNSIGNED_LONG The object is not in the list - /// \retval [Integer] The index of the element in the list - unsigned int GetIndexOf( const list_type &input ) const; - - /// \return The number of elements in the list - unsigned int Size( void ) const; - - /// \brief Clear the list - void Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line ); - - /// \brief Preallocate the list, so it needs fewer reallocations at runtime. - void Preallocate( unsigned countNeeded, const char *file, unsigned int line ); - - /// \brief Frees overallocated members, to use the minimum memory necessary. - /// \attention - /// This is a slow operation - void Compress( const char *file, unsigned int line ); - - private: - /// An array of user values - list_type* listArray; - - /// Number of elements in the list - unsigned int list_size; - - /// Size of \a array - unsigned int allocation_size; - }; - template - List::List() - { - allocation_size = 0; - listArray = 0; - list_size = 0; - } - - template - List::~List() - { - if (allocation_size>0) - RakNet::OP_DELETE_ARRAY(listArray, _FILE_AND_LINE_); - } - - - template - List::List( const List& original_copy ) - { - // Allocate memory for copy - - if ( original_copy.list_size == 0 ) - { - list_size = 0; - allocation_size = 0; - } - else - { - listArray = RakNet::OP_NEW_ARRAY( original_copy.list_size , _FILE_AND_LINE_ ); - - for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) - listArray[ counter ] = original_copy.listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); - - list_size = allocation_size = original_copy.list_size; - } - } - - template - List& List::operator= ( const List& original_copy ) - { - if ( ( &original_copy ) != this ) - { - Clear( false, _FILE_AND_LINE_ ); - - // Allocate memory for copy - - if ( original_copy.list_size == 0 ) - { - list_size = 0; - allocation_size = 0; - } - - else - { - listArray = RakNet::OP_NEW_ARRAY( original_copy.list_size , _FILE_AND_LINE_ ); - - for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) - listArray[ counter ] = original_copy.listArray[ counter ]; - // Don't call constructors, assignment operators, etc. - //memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); - - list_size = allocation_size = original_copy.list_size; - } - } - - return *this; - } - - - template - inline list_type& List::operator[] ( const unsigned int position ) const - { - #ifdef _DEBUG - if (position>=list_size) - { - RakAssert ( position < list_size ); - } - #endif - return listArray[ position ]; - } - - // Just here for debugging - template - inline list_type& List::Get ( const unsigned int position ) const - { - return listArray[ position ]; - } - - template - void List::Push(const list_type &input, const char *file, unsigned int line) - { - Insert(input, file, line); - } - - template - inline list_type& List::Pop(void) - { -#ifdef _DEBUG - RakAssert(list_size>0); -#endif - --list_size; - return listArray[list_size]; - } - - template - void List::Insert( const list_type &input, const unsigned int position, const char *file, unsigned int line ) - { -#ifdef _DEBUG - if (position>list_size) - { - RakAssert( position <= list_size ); - } -#endif - - // Reallocate list if necessary - if ( list_size == allocation_size ) - { - // allocate twice the currently allocated memory - list_type * new_array; - - if ( allocation_size == 0 ) - allocation_size = 16; - else - allocation_size *= 2; - - new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); - - // copy old array over - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated and twice as large array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - - listArray = new_array; - } - - // Move the elements in the list to make room - for ( unsigned int counter = list_size; counter != position; counter-- ) - listArray[ counter ] = listArray[ counter - 1 ]; - - // Don't call constructors, assignment operators, etc. - //memmove(listArray+position+1, listArray+position, (list_size-position)*sizeof(list_type)); - - // Insert the new item at the correct spot - listArray[ position ] = input; - - ++list_size; - - } - - - template - void List::Insert( const list_type &input, const char *file, unsigned int line ) - { - // Reallocate list if necessary - - if ( list_size == allocation_size ) - { - // allocate twice the currently allocated memory - list_type * new_array; - - if ( allocation_size == 0 ) - allocation_size = 16; - else - allocation_size *= 2; - - new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); - - if (listArray) - { - // copy old array over - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated and twice as large array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - } - - listArray = new_array; - } - - // Insert the new item at the correct spot - listArray[ list_size ] = input; - - ++list_size; - } - - template - inline void List::Replace( const list_type &input, const list_type filler, const unsigned int position, const char *file, unsigned int line ) - { - if ( ( list_size > 0 ) && ( position < list_size ) ) - { - // Direct replacement - listArray[ position ] = input; - } - else - { - if ( position >= allocation_size ) - { - // Reallocate the list to size position and fill in blanks with filler - list_type * new_array; - allocation_size = position + 1; - - new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); - - // copy old array over - - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - - listArray = new_array; - } - - // Fill in holes with filler - while ( list_size < position ) - listArray[ list_size++ ] = filler; - - // Fill in the last element with the new item - listArray[ list_size++ ] = input; - -#ifdef _DEBUG - - RakAssert( list_size == position + 1 ); - -#endif - - } - } - - template - inline void List::Replace( const list_type &input ) - { - if ( list_size > 0 ) - listArray[ list_size - 1 ] = input; - } - - template - void List::RemoveAtIndex( const unsigned int position ) - { -#ifdef _DEBUG - if (position >= list_size) - { - RakAssert( position < list_size ); - return; - } -#endif - - if ( position < list_size ) - { - // Compress the array - for ( unsigned int counter = position; counter < list_size - 1 ; ++counter ) - listArray[ counter ] = listArray[ counter + 1 ]; - // Don't call constructors, assignment operators, etc. - // memmove(listArray+position, listArray+position+1, (list_size-1-position) * sizeof(list_type)); - - RemoveFromEnd(); - } - } - - template - void List::RemoveAtIndexFast( const unsigned int position ) - { -#ifdef _DEBUG - if (position >= list_size) - { - RakAssert( position < list_size ); - return; - } -#endif - --list_size; - listArray[position]=listArray[list_size]; - } - - template - inline void List::RemoveFromEnd( const unsigned num ) - { - // Delete the last elements on the list. No compression needed -#ifdef _DEBUG - RakAssert(list_size>=num); -#endif - list_size-=num; - } - - template - unsigned int List::GetIndexOf( const list_type &input ) const - { - for ( unsigned int i = 0; i < list_size; ++i ) - if ( listArray[ i ] == input ) - return i; - - return MAX_UNSIGNED_LONG; - } - - template - inline unsigned int List::Size( void ) const - { - return list_size; - } - - template - void List::Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line ) - { - if ( allocation_size == 0 ) - return; - - if (allocation_size>512 || doNotDeallocateSmallBlocks==false) - { - RakNet::OP_DELETE_ARRAY(listArray, file, line); - allocation_size = 0; - listArray = 0; - } - list_size = 0; - } - - template - void List::Compress( const char *file, unsigned int line ) - { - list_type * new_array; - - if ( allocation_size == 0 ) - return ; - - new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); - - // copy old array over - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - - listArray = new_array; - } - - template - void List::Preallocate( unsigned countNeeded, const char *file, unsigned int line ) - { - unsigned amountToAllocate = allocation_size; - if (allocation_size==0) - amountToAllocate=16; - while (amountToAllocate < countNeeded) - amountToAllocate<<=1; - - if ( allocation_size < amountToAllocate) - { - // allocate twice the currently allocated memory - list_type * new_array; - - allocation_size=amountToAllocate; - - new_array = RakNet::OP_NEW_ARRAY< list_type >( allocation_size , file, line ); - - if (listArray) - { - // copy old array over - for ( unsigned int counter = 0; counter < list_size; ++counter ) - new_array[ counter ] = listArray[ counter ]; - - // Don't call constructors, assignment operators, etc. - //memcpy(new_array, listArray, list_size*sizeof(list_type)); - - // set old array to point to the newly allocated and twice as large array - RakNet::OP_DELETE_ARRAY(listArray, file, line); - } - - listArray = new_array; - } - } - -} // End namespace - -#endif +/// \file DS_List.h +/// \internal +/// \brief Array based list. +/// \details Usually the Queue class is used instead, since it has all the same functionality and is only worse at random access. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __LIST_H +#define __LIST_H + +#include "RakAssert.h" +#include // memmove +#include "Export.h" +#include "RakMemoryOverride.h" + +/// Maximum unsigned long +static const unsigned int MAX_UNSIGNED_LONG = 4294967295U; + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief Array based implementation of a list. + /// \note ONLY USE THIS FOR SHALLOW COPIES. I don't bother with operator= to improve performance. + template + class RAK_DLL_EXPORT List + { + public: + /// Default constructor + List(); + + // Destructor + ~List(); + + /// \brief Copy constructor. + /// \param[in] original_copy The list to duplicate + List( const List& original_copy ); + + /// \brief Assign one list to another. + List& operator= ( const List& original_copy ); + + /// \brief Access an element by its index in the array. + /// \param[in] position The index into the array. + /// \return The element at position \a position. + list_type& operator[] ( const unsigned int position ) const; + + /// \brief Access an element by its index in the array. + /// \param[in] position The index into the array. + /// \return The element at position \a position. + list_type& Get ( const unsigned int position ) const; + + /// \brief Push an element at the end of the stack. + /// \param[in] input The new element. + void Push(const list_type &input, const char *file, unsigned int line ); + + /// \brief Pop an element from the end of the stack. + /// \pre Size()>0 + /// \return The element at the end. + list_type& Pop(void); + + /// \brief Insert an element at position \a position in the list. + /// \param[in] input The new element. + /// \param[in] position The position of the new element. + void Insert( const list_type &input, const unsigned int position, const char *file, unsigned int line ); + + /// \brief Insert at the end of the list. + /// \param[in] input The new element. + void Insert( const list_type &input, const char *file, unsigned int line ); + + /// \brief Replace the value at \a position by \a input. + /// \details If the size of the list is less than @em position, it increase the capacity of + /// the list and fill slot with @em filler. + /// \param[in] input The element to replace at position @em position. + /// \param[in] filler The element use to fill new allocated capacity. + /// \param[in] position The position of input in the list. + void Replace( const list_type &input, const list_type filler, const unsigned int position, const char *file, unsigned int line ); + + /// \brief Replace the last element of the list by \a input. + /// \param[in] input The element used to replace the last element. + void Replace( const list_type &input ); + + /// \brief Delete the element at position \a position. + /// \param[in] position The index of the element to delete + void RemoveAtIndex( const unsigned int position ); + + /// \brief Delete the element at position \a position. + /// \note - swaps middle with end of list, only use if list order does not matter + /// \param[in] position The index of the element to delete + void RemoveAtIndexFast( const unsigned int position ); + + /// \brief Delete the element at the end of the list. + void RemoveFromEnd(const unsigned num=1); + + /// \brief Returns the index of the specified item or MAX_UNSIGNED_LONG if not found. + /// \param[in] input The element to check for + /// \return The index or position of @em input in the list. + /// \retval MAX_UNSIGNED_LONG The object is not in the list + /// \retval [Integer] The index of the element in the list + unsigned int GetIndexOf( const list_type &input ) const; + + /// \return The number of elements in the list + unsigned int Size( void ) const; + + /// \brief Clear the list + void Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line ); + + /// \brief Preallocate the list, so it needs fewer reallocations at runtime. + void Preallocate( unsigned countNeeded, const char *file, unsigned int line ); + + /// \brief Frees overallocated members, to use the minimum memory necessary. + /// \attention + /// This is a slow operation + void Compress( const char *file, unsigned int line ); + + private: + /// An array of user values + list_type* listArray; + + /// Number of elements in the list + unsigned int list_size; + + /// Size of \a array + unsigned int allocation_size; + }; + template + List::List() + { + allocation_size = 0; + listArray = 0; + list_size = 0; + } + + template + List::~List() + { + if (allocation_size>0) + RakNet::OP_DELETE_ARRAY(listArray, _FILE_AND_LINE_); + } + + + template + List::List( const List& original_copy ) + { + // Allocate memory for copy + + if ( original_copy.list_size == 0 ) + { + list_size = 0; + allocation_size = 0; + } + else + { + listArray = RakNet::OP_NEW_ARRAY( original_copy.list_size , _FILE_AND_LINE_ ); + + for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) + listArray[ counter ] = original_copy.listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); + + list_size = allocation_size = original_copy.list_size; + } + } + + template + List& List::operator= ( const List& original_copy ) + { + if ( ( &original_copy ) != this ) + { + Clear( false, _FILE_AND_LINE_ ); + + // Allocate memory for copy + + if ( original_copy.list_size == 0 ) + { + list_size = 0; + allocation_size = 0; + } + + else + { + listArray = RakNet::OP_NEW_ARRAY( original_copy.list_size , _FILE_AND_LINE_ ); + + for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) + listArray[ counter ] = original_copy.listArray[ counter ]; + // Don't call constructors, assignment operators, etc. + //memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); + + list_size = allocation_size = original_copy.list_size; + } + } + + return *this; + } + + + template + inline list_type& List::operator[] ( const unsigned int position ) const + { + #ifdef _DEBUG + if (position>=list_size) + { + RakAssert ( position < list_size ); + } + #endif + return listArray[ position ]; + } + + // Just here for debugging + template + inline list_type& List::Get ( const unsigned int position ) const + { + return listArray[ position ]; + } + + template + void List::Push(const list_type &input, const char *file, unsigned int line) + { + Insert(input, file, line); + } + + template + inline list_type& List::Pop(void) + { +#ifdef _DEBUG + RakAssert(list_size>0); +#endif + --list_size; + return listArray[list_size]; + } + + template + void List::Insert( const list_type &input, const unsigned int position, const char *file, unsigned int line ) + { +#ifdef _DEBUG + if (position>list_size) + { + RakAssert( position <= list_size ); + } +#endif + + // Reallocate list if necessary + if ( list_size == allocation_size ) + { + // allocate twice the currently allocated memory + list_type * new_array; + + if ( allocation_size == 0 ) + allocation_size = 16; + else + allocation_size *= 2; + + new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); + + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated and twice as large array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + + listArray = new_array; + } + + // Move the elements in the list to make room + for ( unsigned int counter = list_size; counter != position; counter-- ) + listArray[ counter ] = listArray[ counter - 1 ]; + + // Don't call constructors, assignment operators, etc. + //memmove(listArray+position+1, listArray+position, (list_size-position)*sizeof(list_type)); + + // Insert the new item at the correct spot + listArray[ position ] = input; + + ++list_size; + + } + + + template + void List::Insert( const list_type &input, const char *file, unsigned int line ) + { + // Reallocate list if necessary + + if ( list_size == allocation_size ) + { + // allocate twice the currently allocated memory + list_type * new_array; + + if ( allocation_size == 0 ) + allocation_size = 16; + else + allocation_size *= 2; + + new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); + + if (listArray) + { + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated and twice as large array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + } + + listArray = new_array; + } + + // Insert the new item at the correct spot + listArray[ list_size ] = input; + + ++list_size; + } + + template + inline void List::Replace( const list_type &input, const list_type filler, const unsigned int position, const char *file, unsigned int line ) + { + if ( ( list_size > 0 ) && ( position < list_size ) ) + { + // Direct replacement + listArray[ position ] = input; + } + else + { + if ( position >= allocation_size ) + { + // Reallocate the list to size position and fill in blanks with filler + list_type * new_array; + allocation_size = position + 1; + + new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); + + // copy old array over + + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + + listArray = new_array; + } + + // Fill in holes with filler + while ( list_size < position ) + listArray[ list_size++ ] = filler; + + // Fill in the last element with the new item + listArray[ list_size++ ] = input; + +#ifdef _DEBUG + + RakAssert( list_size == position + 1 ); + +#endif + + } + } + + template + inline void List::Replace( const list_type &input ) + { + if ( list_size > 0 ) + listArray[ list_size - 1 ] = input; + } + + template + void List::RemoveAtIndex( const unsigned int position ) + { +#ifdef _DEBUG + if (position >= list_size) + { + RakAssert( position < list_size ); + return; + } +#endif + + if ( position < list_size ) + { + // Compress the array + for ( unsigned int counter = position; counter < list_size - 1 ; ++counter ) + listArray[ counter ] = listArray[ counter + 1 ]; + // Don't call constructors, assignment operators, etc. + // memmove(listArray+position, listArray+position+1, (list_size-1-position) * sizeof(list_type)); + + RemoveFromEnd(); + } + } + + template + void List::RemoveAtIndexFast( const unsigned int position ) + { +#ifdef _DEBUG + if (position >= list_size) + { + RakAssert( position < list_size ); + return; + } +#endif + --list_size; + listArray[position]=listArray[list_size]; + } + + template + inline void List::RemoveFromEnd( const unsigned num ) + { + // Delete the last elements on the list. No compression needed +#ifdef _DEBUG + RakAssert(list_size>=num); +#endif + list_size-=num; + } + + template + unsigned int List::GetIndexOf( const list_type &input ) const + { + for ( unsigned int i = 0; i < list_size; ++i ) + if ( listArray[ i ] == input ) + return i; + + return MAX_UNSIGNED_LONG; + } + + template + inline unsigned int List::Size( void ) const + { + return list_size; + } + + template + void List::Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line ) + { + if ( allocation_size == 0 ) + return; + + if (allocation_size>512 || doNotDeallocateSmallBlocks==false) + { + RakNet::OP_DELETE_ARRAY(listArray, file, line); + allocation_size = 0; + listArray = 0; + } + list_size = 0; + } + + template + void List::Compress( const char *file, unsigned int line ) + { + list_type * new_array; + + if ( allocation_size == 0 ) + return ; + + new_array = RakNet::OP_NEW_ARRAY( allocation_size , file, line ); + + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + + listArray = new_array; + } + + template + void List::Preallocate( unsigned countNeeded, const char *file, unsigned int line ) + { + unsigned amountToAllocate = allocation_size; + if (allocation_size==0) + amountToAllocate=16; + while (amountToAllocate < countNeeded) + amountToAllocate<<=1; + + if ( allocation_size < amountToAllocate) + { + // allocate twice the currently allocated memory + list_type * new_array; + + allocation_size=amountToAllocate; + + new_array = RakNet::OP_NEW_ARRAY< list_type >( allocation_size , file, line ); + + if (listArray) + { + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated and twice as large array + RakNet::OP_DELETE_ARRAY(listArray, file, line); + } + + listArray = new_array; + } + } + +} // End namespace + +#endif diff --git a/src/raknet/DS_Map.h b/src/raknet/DS_Map.h index 2e789da..7905de1 100755 --- a/src/raknet/DS_Map.h +++ b/src/raknet/DS_Map.h @@ -1,321 +1,321 @@ -/// \file DS_Map.h -/// \internal -/// \brief Map -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAKNET_MAP_H -#define __RAKNET_MAP_H - -#include "DS_OrderedList.h" -#include "Export.h" -#include "RakMemoryOverride.h" -#include "RakAssert.h" - -// If I want to change this to a red-black tree, this is a good site: http://www.cs.auckland.ac.nz/software/AlgAnim/red_black.html -// This makes insertions and deletions faster. But then traversals are slow, while they are currently fast. - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// The default comparison has to be first so it can be called as a default parameter. - /// It then is followed by MapNode, followed by NodeComparisonFunc - template - int defaultMapKeyComparison(const key_type &a, const key_type &b) - { - if (a > - class RAK_DLL_EXPORT Map - { - public: - static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(key_type(),key_type());} - - struct MapNode - { - MapNode() {} - MapNode(key_type _key, data_type _data) : mapNodeKey(_key), mapNodeData(_data) {} - MapNode& operator = ( const MapNode& input ) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData; return *this;} - MapNode( const MapNode & input) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData;} - key_type mapNodeKey; - data_type mapNodeData; - }; - - // Has to be a static because the comparison callback for DataStructures::OrderedList is a C function - static int NodeComparisonFunc(const key_type &a, const MapNode &b) - { -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - return key_comparison_func(a, b.mapNodeKey); - } - - Map(); - ~Map(); - Map( const Map& original_copy ); - Map& operator= ( const Map& original_copy ); - - data_type& Get(const key_type &key) const; - data_type Pop(const key_type &key); - // Add if needed - void Set(const key_type &key, const data_type &data); - // Must already exist - void SetExisting(const key_type &key, const data_type &data); - // Must add - void SetNew(const key_type &key, const data_type &data); - bool Has(const key_type &key) const; - bool Delete(const key_type &key); - data_type& operator[] ( const unsigned int position ) const; - key_type GetKeyAtIndex( const unsigned int position ) const; - unsigned GetIndexAtKey( const key_type &key ); - void RemoveAtIndex(const unsigned index); - void Clear(void); - unsigned Size(void) const; - - protected: - DataStructures::OrderedList< key_type,MapNode,&Map::NodeComparisonFunc > mapNodeList; - - void SaveLastSearch(const key_type &key, unsigned index) const; - bool HasSavedSearchResult(const key_type &key) const; - - unsigned lastSearchIndex; - key_type lastSearchKey; - bool lastSearchIndexValid; - }; - - template - Map::Map() - { - lastSearchIndexValid=false; - } - - template - Map::~Map() - { - Clear(); - } - - template - Map::Map( const Map& original_copy ) - { - mapNodeList=original_copy.mapNodeList; - lastSearchIndex=original_copy.lastSearchIndex; - lastSearchKey=original_copy.lastSearchKey; - lastSearchIndexValid=original_copy.lastSearchIndexValid; - } - - template - Map& Map::operator= ( const Map& original_copy ) - { - mapNodeList=original_copy.mapNodeList; - lastSearchIndex=original_copy.lastSearchIndex; - lastSearchKey=original_copy.lastSearchKey; - lastSearchIndexValid=original_copy.lastSearchIndexValid; - return *this; - } - - template - data_type& Map::Get(const key_type &key) const - { - if (HasSavedSearchResult(key)) - return mapNodeList[lastSearchIndex].mapNodeData; - - bool objectExists; - unsigned index; - index=mapNodeList.GetIndexFromKey(key, &objectExists); - RakAssert(objectExists); - SaveLastSearch(key,index); - return mapNodeList[index].mapNodeData; - } - - template - unsigned Map::GetIndexAtKey( const key_type &key ) - { - if (HasSavedSearchResult(key)) - return lastSearchIndex; - - bool objectExists; - unsigned index; - index=mapNodeList.GetIndexFromKey(key, &objectExists); - if (objectExists==false) - { - RakAssert(objectExists); - } - SaveLastSearch(key,index); - return index; - } - - template - void Map::RemoveAtIndex(const unsigned index) - { - mapNodeList.RemoveAtIndex(index); - lastSearchIndexValid=false; - } - - template - data_type Map::Pop(const key_type &key) - { - bool objectExists; - unsigned index; - if (HasSavedSearchResult(key)) - index=lastSearchIndex; - else - { - index=mapNodeList.GetIndexFromKey(key, &objectExists); - RakAssert(objectExists); - } - data_type tmp = mapNodeList[index].mapNodeData; - mapNodeList.RemoveAtIndex(index); - lastSearchIndexValid=false; - return tmp; - } - - template - void Map::Set(const key_type &key, const data_type &data) - { - bool objectExists; - unsigned index; - - if (HasSavedSearchResult(key)) - { - mapNodeList[lastSearchIndex].mapNodeData=data; - return; - } - - index=mapNodeList.GetIndexFromKey(key, &objectExists); - - if (objectExists) - { - SaveLastSearch(key,index); - mapNodeList[index].mapNodeData=data; - } - else - { - SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_)); - } - } - - template - void Map::SetExisting(const key_type &key, const data_type &data) - { - bool objectExists; - unsigned index; - - if (HasSavedSearchResult(key)) - { - index=lastSearchIndex; - } - else - { - index=mapNodeList.GetIndexFromKey(key, &objectExists); - RakAssert(objectExists); - SaveLastSearch(key,index); - } - - mapNodeList[index].mapNodeData=data; - } - - template - void Map::SetNew(const key_type &key, const data_type &data) - { -#ifdef _DEBUG - bool objectExists; - mapNodeList.GetIndexFromKey(key, &objectExists); - RakAssert(objectExists==false); -#endif - SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_)); - } - - template - bool Map::Has(const key_type &key) const - { - if (HasSavedSearchResult(key)) - return true; - - bool objectExists; - unsigned index; - index=mapNodeList.GetIndexFromKey(key, &objectExists); - if (objectExists) - SaveLastSearch(key,index); - return objectExists; - } - - template - bool Map::Delete(const key_type &key) - { - if (HasSavedSearchResult(key)) - { - lastSearchIndexValid=false; - mapNodeList.RemoveAtIndex(lastSearchIndex); - return true; - } - - bool objectExists; - unsigned index; - index=mapNodeList.GetIndexFromKey(key, &objectExists); - if (objectExists) - { - lastSearchIndexValid=false; - mapNodeList.RemoveAtIndex(index); - return true; - } - else - return false; - } - - template - void Map::Clear(void) - { - lastSearchIndexValid=false; - mapNodeList.Clear(false, _FILE_AND_LINE_); - } - - template - data_type& Map::operator[]( const unsigned int position ) const - { - return mapNodeList[position].mapNodeData; - } - - template - key_type Map::GetKeyAtIndex( const unsigned int position ) const - { - return mapNodeList[position].mapNodeKey; - } - - template - unsigned Map::Size(void) const - { - return mapNodeList.Size(); - } - - template - void Map::SaveLastSearch(const key_type &key, const unsigned index) const - { - (void) key; - (void) index; - - /* - lastSearchIndex=index; - lastSearchKey=key; - lastSearchIndexValid=true; - */ - } - - template - bool Map::HasSavedSearchResult(const key_type &key) const - { - (void) key; - - // Not threadsafe! - return false; - // return lastSearchIndexValid && key_comparison_func(key,lastSearchKey)==0; - } -} - -#endif +/// \file DS_Map.h +/// \internal +/// \brief Map +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAKNET_MAP_H +#define __RAKNET_MAP_H + +#include "DS_OrderedList.h" +#include "Export.h" +#include "RakMemoryOverride.h" +#include "RakAssert.h" + +// If I want to change this to a red-black tree, this is a good site: http://www.cs.auckland.ac.nz/software/AlgAnim/red_black.html +// This makes insertions and deletions faster. But then traversals are slow, while they are currently fast. + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// The default comparison has to be first so it can be called as a default parameter. + /// It then is followed by MapNode, followed by NodeComparisonFunc + template + int defaultMapKeyComparison(const key_type &a, const key_type &b) + { + if (a > + class RAK_DLL_EXPORT Map + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(key_type(),key_type());} + + struct MapNode + { + MapNode() {} + MapNode(key_type _key, data_type _data) : mapNodeKey(_key), mapNodeData(_data) {} + MapNode& operator = ( const MapNode& input ) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData; return *this;} + MapNode( const MapNode & input) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData;} + key_type mapNodeKey; + data_type mapNodeData; + }; + + // Has to be a static because the comparison callback for DataStructures::OrderedList is a C function + static int NodeComparisonFunc(const key_type &a, const MapNode &b) + { +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + return key_comparison_func(a, b.mapNodeKey); + } + + Map(); + ~Map(); + Map( const Map& original_copy ); + Map& operator= ( const Map& original_copy ); + + data_type& Get(const key_type &key) const; + data_type Pop(const key_type &key); + // Add if needed + void Set(const key_type &key, const data_type &data); + // Must already exist + void SetExisting(const key_type &key, const data_type &data); + // Must add + void SetNew(const key_type &key, const data_type &data); + bool Has(const key_type &key) const; + bool Delete(const key_type &key); + data_type& operator[] ( const unsigned int position ) const; + key_type GetKeyAtIndex( const unsigned int position ) const; + unsigned GetIndexAtKey( const key_type &key ); + void RemoveAtIndex(const unsigned index); + void Clear(void); + unsigned Size(void) const; + + protected: + DataStructures::OrderedList< key_type,MapNode,&Map::NodeComparisonFunc > mapNodeList; + + void SaveLastSearch(const key_type &key, unsigned index) const; + bool HasSavedSearchResult(const key_type &key) const; + + unsigned lastSearchIndex; + key_type lastSearchKey; + bool lastSearchIndexValid; + }; + + template + Map::Map() + { + lastSearchIndexValid=false; + } + + template + Map::~Map() + { + Clear(); + } + + template + Map::Map( const Map& original_copy ) + { + mapNodeList=original_copy.mapNodeList; + lastSearchIndex=original_copy.lastSearchIndex; + lastSearchKey=original_copy.lastSearchKey; + lastSearchIndexValid=original_copy.lastSearchIndexValid; + } + + template + Map& Map::operator= ( const Map& original_copy ) + { + mapNodeList=original_copy.mapNodeList; + lastSearchIndex=original_copy.lastSearchIndex; + lastSearchKey=original_copy.lastSearchKey; + lastSearchIndexValid=original_copy.lastSearchIndexValid; + return *this; + } + + template + data_type& Map::Get(const key_type &key) const + { + if (HasSavedSearchResult(key)) + return mapNodeList[lastSearchIndex].mapNodeData; + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists); + SaveLastSearch(key,index); + return mapNodeList[index].mapNodeData; + } + + template + unsigned Map::GetIndexAtKey( const key_type &key ) + { + if (HasSavedSearchResult(key)) + return lastSearchIndex; + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + if (objectExists==false) + { + RakAssert(objectExists); + } + SaveLastSearch(key,index); + return index; + } + + template + void Map::RemoveAtIndex(const unsigned index) + { + mapNodeList.RemoveAtIndex(index); + lastSearchIndexValid=false; + } + + template + data_type Map::Pop(const key_type &key) + { + bool objectExists; + unsigned index; + if (HasSavedSearchResult(key)) + index=lastSearchIndex; + else + { + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists); + } + data_type tmp = mapNodeList[index].mapNodeData; + mapNodeList.RemoveAtIndex(index); + lastSearchIndexValid=false; + return tmp; + } + + template + void Map::Set(const key_type &key, const data_type &data) + { + bool objectExists; + unsigned index; + + if (HasSavedSearchResult(key)) + { + mapNodeList[lastSearchIndex].mapNodeData=data; + return; + } + + index=mapNodeList.GetIndexFromKey(key, &objectExists); + + if (objectExists) + { + SaveLastSearch(key,index); + mapNodeList[index].mapNodeData=data; + } + else + { + SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_)); + } + } + + template + void Map::SetExisting(const key_type &key, const data_type &data) + { + bool objectExists; + unsigned index; + + if (HasSavedSearchResult(key)) + { + index=lastSearchIndex; + } + else + { + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists); + SaveLastSearch(key,index); + } + + mapNodeList[index].mapNodeData=data; + } + + template + void Map::SetNew(const key_type &key, const data_type &data) + { +#ifdef _DEBUG + bool objectExists; + mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists==false); +#endif + SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_)); + } + + template + bool Map::Has(const key_type &key) const + { + if (HasSavedSearchResult(key)) + return true; + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + if (objectExists) + SaveLastSearch(key,index); + return objectExists; + } + + template + bool Map::Delete(const key_type &key) + { + if (HasSavedSearchResult(key)) + { + lastSearchIndexValid=false; + mapNodeList.RemoveAtIndex(lastSearchIndex); + return true; + } + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + if (objectExists) + { + lastSearchIndexValid=false; + mapNodeList.RemoveAtIndex(index); + return true; + } + else + return false; + } + + template + void Map::Clear(void) + { + lastSearchIndexValid=false; + mapNodeList.Clear(false, _FILE_AND_LINE_); + } + + template + data_type& Map::operator[]( const unsigned int position ) const + { + return mapNodeList[position].mapNodeData; + } + + template + key_type Map::GetKeyAtIndex( const unsigned int position ) const + { + return mapNodeList[position].mapNodeKey; + } + + template + unsigned Map::Size(void) const + { + return mapNodeList.Size(); + } + + template + void Map::SaveLastSearch(const key_type &key, const unsigned index) const + { + (void) key; + (void) index; + + /* + lastSearchIndex=index; + lastSearchKey=key; + lastSearchIndexValid=true; + */ + } + + template + bool Map::HasSavedSearchResult(const key_type &key) const + { + (void) key; + + // Not threadsafe! + return false; + // return lastSearchIndexValid && key_comparison_func(key,lastSearchKey)==0; + } +} + +#endif diff --git a/src/raknet/DS_MemoryPool.h b/src/raknet/DS_MemoryPool.h index 62ebe8b..903fd8e 100755 --- a/src/raknet/DS_MemoryPool.h +++ b/src/raknet/DS_MemoryPool.h @@ -1,294 +1,294 @@ -/// \file DS_MemoryPool.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __MEMORY_POOL_H -#define __MEMORY_POOL_H - -#ifndef __APPLE__ -// Use stdlib and not malloc for compatibility -#include -#endif -#include "RakAssert.h" -#include "Export.h" - -#include "RakMemoryOverride.h" - -// DS_MEMORY_POOL_MAX_FREE_PAGES must be > 1 -#define DS_MEMORY_POOL_MAX_FREE_PAGES 4 - -//#define _DISABLE_MEMORY_POOL - -namespace DataStructures -{ - /// Very fast memory pool for allocating and deallocating structures that don't have constructors or destructors. - /// Contains a list of pages, each of which has an array of the user structures - template - class RAK_DLL_EXPORT MemoryPool - { - public: - struct Page; - struct MemoryWithPage - { - MemoryBlockType userMemory; - Page *parentPage; - }; - struct Page - { - MemoryWithPage** availableStack; - int availableStackSize; - MemoryWithPage* block; - Page *next, *prev; - }; - - MemoryPool(); - ~MemoryPool(); - void SetPageSize(int size); // Defaults to 16384 bytes - MemoryBlockType *Allocate(const char *file, unsigned int line); - void Release(MemoryBlockType *m, const char *file, unsigned int line); - void Clear(const char *file, unsigned int line); - - int GetAvailablePagesSize(void) const {return availablePagesSize;} - int GetUnavailablePagesSize(void) const {return unavailablePagesSize;} - int GetMemoryPoolPageSize(void) const {return memoryPoolPageSize;} - protected: - int BlocksPerPage(void) const; - void AllocateFirst(void); - bool InitPage(Page *page, Page *prev, const char *file, unsigned int line); - - // availablePages contains pages which have room to give the user new blocks. We return these blocks from the head of the list - // unavailablePages are pages which are totally full, and from which we do not return new blocks. - // Pages move from the head of unavailablePages to the tail of availablePages, and from the head of availablePages to the tail of unavailablePages - Page *availablePages, *unavailablePages; - int availablePagesSize, unavailablePagesSize; - int memoryPoolPageSize; - }; - - template - MemoryPool::MemoryPool() - { -#ifndef _DISABLE_MEMORY_POOL - //AllocateFirst(); - availablePagesSize=0; - unavailablePagesSize=0; - memoryPoolPageSize=16384; -#endif - } - template - MemoryPool::~MemoryPool() - { -#ifndef _DISABLE_MEMORY_POOL - Clear(_FILE_AND_LINE_); -#endif - } - - template - void MemoryPool::SetPageSize(int size) - { - memoryPoolPageSize=size; - } - - template - MemoryBlockType* MemoryPool::Allocate(const char *file, unsigned int line) - { -#ifdef _DISABLE_MEMORY_POOL - return (MemoryBlockType*) rakMalloc_Ex(sizeof(MemoryBlockType), file, line); -#else - - if (availablePagesSize>0) - { - MemoryBlockType *retVal; - Page *curPage; - curPage=availablePages; - retVal = (MemoryBlockType*) curPage->availableStack[--(curPage->availableStackSize)]; - if (curPage->availableStackSize==0) - { - --availablePagesSize; - availablePages=curPage->next; - RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); - curPage->next->prev=curPage->prev; - curPage->prev->next=curPage->next; - - if (unavailablePagesSize++==0) - { - unavailablePages=curPage; - curPage->next=curPage; - curPage->prev=curPage; - } - else - { - curPage->next=unavailablePages; - curPage->prev=unavailablePages->prev; - unavailablePages->prev->next=curPage; - unavailablePages->prev=curPage; - } - } - - RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); - return retVal; - } - - availablePages = (Page *) rakMalloc_Ex(sizeof(Page), file, line); - if (availablePages==0) - return 0; - availablePagesSize=1; - if (InitPage(availablePages, availablePages, file, line)==false) - return 0; - // If this assert hits, we couldn't allocate even 1 block per page. Increase the page size - RakAssert(availablePages->availableStackSize>1); - - return (MemoryBlockType *) availablePages->availableStack[--availablePages->availableStackSize]; -#endif - } - template - void MemoryPool::Release(MemoryBlockType *m, const char *file, unsigned int line) - { -#ifdef _DISABLE_MEMORY_POOL - rakFree_Ex(m, file, line); - return; -#else - // Find the page this block is in and return it. - Page *curPage; - MemoryWithPage *memoryWithPage = (MemoryWithPage*)m; - curPage=memoryWithPage->parentPage; - - if (curPage->availableStackSize==0) - { - // The page is in the unavailable list so move it to the available list - curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; - unavailablePagesSize--; - - // As this page is no longer totally empty, move it to the end of available pages - curPage->next->prev=curPage->prev; - curPage->prev->next=curPage->next; - - if (unavailablePagesSize>0 && curPage==unavailablePages) - unavailablePages=unavailablePages->next; - - if (availablePagesSize++==0) - { - availablePages=curPage; - curPage->next=curPage; - curPage->prev=curPage; - } - else - { - curPage->next=availablePages; - curPage->prev=availablePages->prev; - availablePages->prev->next=curPage; - availablePages->prev=curPage; - } - } - else - { - curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; - - if (curPage->availableStackSize==BlocksPerPage() && - availablePagesSize>=DS_MEMORY_POOL_MAX_FREE_PAGES) - { - // After a certain point, just deallocate empty pages rather than keep them around - if (curPage==availablePages) - { - availablePages=curPage->next; - RakAssert(availablePages->availableStackSize>0); - } - curPage->prev->next=curPage->next; - curPage->next->prev=curPage->prev; - availablePagesSize--; - rakFree_Ex(curPage->availableStack, file, line ); - rakFree_Ex(curPage->block, file, line ); - rakFree_Ex(curPage, file, line ); - } - } -#endif - } - template - void MemoryPool::Clear(const char *file, unsigned int line) - { -#ifdef _DISABLE_MEMORY_POOL - return; -#else - Page *cur, *freed; - - if (availablePagesSize>0) - { - cur = availablePages; -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - while (true) - // do - { - rakFree_Ex(cur->availableStack, file, line ); - rakFree_Ex(cur->block, file, line ); - freed=cur; - cur=cur->next; - if (cur==availablePages) - { - rakFree_Ex(freed, file, line ); - break; - } - rakFree_Ex(freed, file, line ); - }// while(cur!=availablePages); - } - - if (unavailablePagesSize>0) - { - cur = unavailablePages; - while (1) - //do - { - rakFree_Ex(cur->availableStack, file, line ); - rakFree_Ex(cur->block, file, line ); - freed=cur; - cur=cur->next; - if (cur==unavailablePages) - { - rakFree_Ex(freed, file, line ); - break; - } - rakFree_Ex(freed, file, line ); - } // while(cur!=unavailablePages); - } - - availablePagesSize=0; - unavailablePagesSize=0; -#endif - } - template - int MemoryPool::BlocksPerPage(void) const - { - return memoryPoolPageSize / sizeof(MemoryWithPage); - } - template - bool MemoryPool::InitPage(Page *page, Page *prev, const char *file, unsigned int line) - { - int i=0; - const int bpp = BlocksPerPage(); - page->block=(MemoryWithPage*) rakMalloc_Ex(memoryPoolPageSize, file, line); - if (page->block==0) - return false; - page->availableStack=(MemoryWithPage**)rakMalloc_Ex(sizeof(MemoryWithPage*)*bpp, file, line); - if (page->availableStack==0) - { - rakFree_Ex(page->block, file, line ); - return false; - } - MemoryWithPage *curBlock = page->block; - MemoryWithPage **curStack = page->availableStack; - while (i < bpp) - { - curBlock->parentPage=page; - curStack[i]=curBlock++; - i++; - } - page->availableStackSize=bpp; - page->next=availablePages; - page->prev=prev; - return true; - } -} - -#endif +/// \file DS_MemoryPool.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __MEMORY_POOL_H +#define __MEMORY_POOL_H + +#ifndef __APPLE__ +// Use stdlib and not malloc for compatibility +#include +#endif +#include "RakAssert.h" +#include "Export.h" + +#include "RakMemoryOverride.h" + +// DS_MEMORY_POOL_MAX_FREE_PAGES must be > 1 +#define DS_MEMORY_POOL_MAX_FREE_PAGES 4 + +//#define _DISABLE_MEMORY_POOL + +namespace DataStructures +{ + /// Very fast memory pool for allocating and deallocating structures that don't have constructors or destructors. + /// Contains a list of pages, each of which has an array of the user structures + template + class RAK_DLL_EXPORT MemoryPool + { + public: + struct Page; + struct MemoryWithPage + { + MemoryBlockType userMemory; + Page *parentPage; + }; + struct Page + { + MemoryWithPage** availableStack; + int availableStackSize; + MemoryWithPage* block; + Page *next, *prev; + }; + + MemoryPool(); + ~MemoryPool(); + void SetPageSize(int size); // Defaults to 16384 bytes + MemoryBlockType *Allocate(const char *file, unsigned int line); + void Release(MemoryBlockType *m, const char *file, unsigned int line); + void Clear(const char *file, unsigned int line); + + int GetAvailablePagesSize(void) const {return availablePagesSize;} + int GetUnavailablePagesSize(void) const {return unavailablePagesSize;} + int GetMemoryPoolPageSize(void) const {return memoryPoolPageSize;} + protected: + int BlocksPerPage(void) const; + void AllocateFirst(void); + bool InitPage(Page *page, Page *prev, const char *file, unsigned int line); + + // availablePages contains pages which have room to give the user new blocks. We return these blocks from the head of the list + // unavailablePages are pages which are totally full, and from which we do not return new blocks. + // Pages move from the head of unavailablePages to the tail of availablePages, and from the head of availablePages to the tail of unavailablePages + Page *availablePages, *unavailablePages; + int availablePagesSize, unavailablePagesSize; + int memoryPoolPageSize; + }; + + template + MemoryPool::MemoryPool() + { +#ifndef _DISABLE_MEMORY_POOL + //AllocateFirst(); + availablePagesSize=0; + unavailablePagesSize=0; + memoryPoolPageSize=16384; +#endif + } + template + MemoryPool::~MemoryPool() + { +#ifndef _DISABLE_MEMORY_POOL + Clear(_FILE_AND_LINE_); +#endif + } + + template + void MemoryPool::SetPageSize(int size) + { + memoryPoolPageSize=size; + } + + template + MemoryBlockType* MemoryPool::Allocate(const char *file, unsigned int line) + { +#ifdef _DISABLE_MEMORY_POOL + return (MemoryBlockType*) rakMalloc_Ex(sizeof(MemoryBlockType), file, line); +#else + + if (availablePagesSize>0) + { + MemoryBlockType *retVal; + Page *curPage; + curPage=availablePages; + retVal = (MemoryBlockType*) curPage->availableStack[--(curPage->availableStackSize)]; + if (curPage->availableStackSize==0) + { + --availablePagesSize; + availablePages=curPage->next; + RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); + curPage->next->prev=curPage->prev; + curPage->prev->next=curPage->next; + + if (unavailablePagesSize++==0) + { + unavailablePages=curPage; + curPage->next=curPage; + curPage->prev=curPage; + } + else + { + curPage->next=unavailablePages; + curPage->prev=unavailablePages->prev; + unavailablePages->prev->next=curPage; + unavailablePages->prev=curPage; + } + } + + RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); + return retVal; + } + + availablePages = (Page *) rakMalloc_Ex(sizeof(Page), file, line); + if (availablePages==0) + return 0; + availablePagesSize=1; + if (InitPage(availablePages, availablePages, file, line)==false) + return 0; + // If this assert hits, we couldn't allocate even 1 block per page. Increase the page size + RakAssert(availablePages->availableStackSize>1); + + return (MemoryBlockType *) availablePages->availableStack[--availablePages->availableStackSize]; +#endif + } + template + void MemoryPool::Release(MemoryBlockType *m, const char *file, unsigned int line) + { +#ifdef _DISABLE_MEMORY_POOL + rakFree_Ex(m, file, line); + return; +#else + // Find the page this block is in and return it. + Page *curPage; + MemoryWithPage *memoryWithPage = (MemoryWithPage*)m; + curPage=memoryWithPage->parentPage; + + if (curPage->availableStackSize==0) + { + // The page is in the unavailable list so move it to the available list + curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; + unavailablePagesSize--; + + // As this page is no longer totally empty, move it to the end of available pages + curPage->next->prev=curPage->prev; + curPage->prev->next=curPage->next; + + if (unavailablePagesSize>0 && curPage==unavailablePages) + unavailablePages=unavailablePages->next; + + if (availablePagesSize++==0) + { + availablePages=curPage; + curPage->next=curPage; + curPage->prev=curPage; + } + else + { + curPage->next=availablePages; + curPage->prev=availablePages->prev; + availablePages->prev->next=curPage; + availablePages->prev=curPage; + } + } + else + { + curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; + + if (curPage->availableStackSize==BlocksPerPage() && + availablePagesSize>=DS_MEMORY_POOL_MAX_FREE_PAGES) + { + // After a certain point, just deallocate empty pages rather than keep them around + if (curPage==availablePages) + { + availablePages=curPage->next; + RakAssert(availablePages->availableStackSize>0); + } + curPage->prev->next=curPage->next; + curPage->next->prev=curPage->prev; + availablePagesSize--; + rakFree_Ex(curPage->availableStack, file, line ); + rakFree_Ex(curPage->block, file, line ); + rakFree_Ex(curPage, file, line ); + } + } +#endif + } + template + void MemoryPool::Clear(const char *file, unsigned int line) + { +#ifdef _DISABLE_MEMORY_POOL + return; +#else + Page *cur, *freed; + + if (availablePagesSize>0) + { + cur = availablePages; +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + while (true) + // do + { + rakFree_Ex(cur->availableStack, file, line ); + rakFree_Ex(cur->block, file, line ); + freed=cur; + cur=cur->next; + if (cur==availablePages) + { + rakFree_Ex(freed, file, line ); + break; + } + rakFree_Ex(freed, file, line ); + }// while(cur!=availablePages); + } + + if (unavailablePagesSize>0) + { + cur = unavailablePages; + while (1) + //do + { + rakFree_Ex(cur->availableStack, file, line ); + rakFree_Ex(cur->block, file, line ); + freed=cur; + cur=cur->next; + if (cur==unavailablePages) + { + rakFree_Ex(freed, file, line ); + break; + } + rakFree_Ex(freed, file, line ); + } // while(cur!=unavailablePages); + } + + availablePagesSize=0; + unavailablePagesSize=0; +#endif + } + template + int MemoryPool::BlocksPerPage(void) const + { + return memoryPoolPageSize / sizeof(MemoryWithPage); + } + template + bool MemoryPool::InitPage(Page *page, Page *prev, const char *file, unsigned int line) + { + int i=0; + const int bpp = BlocksPerPage(); + page->block=(MemoryWithPage*) rakMalloc_Ex(memoryPoolPageSize, file, line); + if (page->block==0) + return false; + page->availableStack=(MemoryWithPage**)rakMalloc_Ex(sizeof(MemoryWithPage*)*bpp, file, line); + if (page->availableStack==0) + { + rakFree_Ex(page->block, file, line ); + return false; + } + MemoryWithPage *curBlock = page->block; + MemoryWithPage **curStack = page->availableStack; + while (i < bpp) + { + curBlock->parentPage=page; + curStack[i]=curBlock++; + i++; + } + page->availableStackSize=bpp; + page->next=availablePages; + page->prev=prev; + return true; + } +} + +#endif diff --git a/src/raknet/DS_Multilist.h b/src/raknet/DS_Multilist.h index e6acdb8..370ff8d 100755 --- a/src/raknet/DS_Multilist.h +++ b/src/raknet/DS_Multilist.h @@ -1,1644 +1,1644 @@ -/// \file DS_Multilist.h -/// \internal -/// \brief ADT that can represent an unordered list, ordered list, stack, or queue with a common interface -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __MULTILIST_H -#define __MULTILIST_H - -#include "RakAssert.h" -#include // memmove -#include "Export.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" - - -#ifdef _MSC_VER -#pragma warning( push ) -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#pragma warning( disable : 4512 ) // warning C4512: assignment operator could not be generated -#endif - -/// What algorithm to use to store the data for the Multilist -enum MultilistType -{ - /// Removing from the middle of the list will swap the end of the list rather than shift the elements. Push and Pop operate on the tail. - ML_UNORDERED_LIST, - /// A normal list, with the list order preserved. Push and Pop operate on the tail. - ML_STACK, - /// A queue. Push and Pop operate on the head - ML_QUEUE, - /// A list that is always kept in order. Elements must be unique, and compare against each other consistently using <, ==, and > - ML_ORDERED_LIST, - /// A list whose type can change at runtime - ML_VARIABLE_DURING_RUNTIME -}; - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// Can be used with Multilist::ForEach - /// Assuming the Multilist holds pointers, will delete those pointers - template - void DeletePtr_RakNet(templateType &ptr, const char *file, unsigned int line ) {RakNet::OP_DELETE(ptr, file, line);} - - /// Can be used with Multilist::ForEach - /// Assuming the Multilist holds pointers, will delete those pointers - template - void DeletePtr(templateType &ptr) {delete ptr;} - - /// The following is invalid. - /// bool operator<( const MyClass *myClass, const int &inputKey ) {return myClass->value < inputKey;} - /// At least one type has to be a reference to a class - /// MLKeyRef is a helper class to turn a native type into a class, so you can compare that native type against a pointer to a different class - /// Used for he Multilist, when _DataType != _KeyType - template < class templateType > - class MLKeyRef - { - public: - MLKeyRef(const templateType& input) : val(input) {} - const templateType &Get(void) const {return val;} - bool operator<( const templateType &right ) {return val < right;} - bool operator>( const templateType &right ) {return val > right;} - bool operator==( const templateType &right ) {return val == right;} - protected: - const templateType &val; - }; - - /// For the Multilist, when _DataType != _KeyType, you must define the comparison operators between the key and the data - /// This is non-trivial due to the need to use MLKeyRef in case the type held is a pointer to a structure or class and the key type is not a class - /// For convenience, this macro will implement the comparison operators under the following conditions - /// 1. _DataType is a pointer to a class or structure - /// 2. The key is a member variable of _DataType - #define DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS( _CLASS_NAME_, _KEY_TYPE_, _MEMBER_VARIABLE_NAME_ ) \ - bool operator<( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() < cls->_MEMBER_VARIABLE_NAME_;} \ - bool operator>( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() > cls->_MEMBER_VARIABLE_NAME_;} \ - bool operator==( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() == cls->_MEMBER_VARIABLE_NAME_;} - - typedef uint32_t DefaultIndexType; - - /// \brief The multilist, representing an abstract data type that generally holds lists. - /// \param[in] _MultilistType What type of list this is, \sa MultilistType - /// \param[in] _DataType What type of data this list holds. - /// \param[in] _KeyType If a function takes a key to sort on, what type of key this is. The comparison operator between _DataType and _KeyType must be defined - /// \param[in] _IndexType What variable type to use for indices - template - class RAK_DLL_EXPORT Multilist - { - public: - Multilist(); - ~Multilist(); - Multilist( const Multilist& source ); - Multilist& operator= ( const Multilist& source ); - _DataType& operator[] ( const _IndexType position ) const; - /// Unordered list, stack is LIFO - /// QUEUE is FIFO - /// Ordered list is inserted in order - void Push(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ ); - void Push(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Gets or removes and gets an element from the list, according to the same rules as Push(). - /// Ordered list is LIFO for the purposes of Pop and Peek. - _DataType &Pop(const char *file=__FILE__, unsigned int line=__LINE__); - _DataType &Peek(void) const; - - /// \brief Same as Push(), except FIFO and LIFO are reversed. - /// Ordered list still inserts in order. - void PushOpposite(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ ); - void PushOpposite(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Same as Pop() and Peek(), except FIFO and LIFO are reversed. - _DataType &PopOpposite(const char *file=__FILE__, unsigned int line=__LINE__); - _DataType &PeekOpposite(void) const; - - /// \brief Stack,Queue: Inserts at index indicated, elements are shifted. - /// Ordered list: Inserts, position is ignored - void InsertAtIndex(const _DataType &d, _IndexType index, const char *file=__FILE__, unsigned int line=__LINE__); - - /// \brief Unordered list, removes at index indicated, swaps last element with that element. - /// Otherwise, array is shifted left to overwrite removed element - /// \details Index[0] returns the same as Pop() for a queue. - /// Same as PopOpposite() for the list and ordered list - void RemoveAtIndex(_IndexType position, const char *file=__FILE__, unsigned int line=__LINE__); - - /// \brief Find the index of \a key, and remove at that index. - bool RemoveAtKey(_KeyType key, bool assertIfDoesNotExist, const char *file=__FILE__, unsigned int line=__LINE__); - - /// \brief Finds the index of \a key. Return -1 if the key is not found. - _IndexType GetIndexOf(_KeyType key) const; - - /// \brief Returns where in the list we should insert the item, to preserve list order. - /// Returns -1 if the item is already in the list - _IndexType GetInsertionIndex(_KeyType key) const; - - /// \brief Finds the index of \a key. Return 0 if the key is not found. Useful if _DataType is always non-zero pointers. - _DataType GetPtr(_KeyType key) const; - - /// \brief Iterate over the list, calling the function pointer on each element. - void ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line); - void ForEach(void (*func)(_DataType &item)); - - /// \brief Returns if the list is empty. - bool IsEmpty(void) const; - - /// \brief Returns the number of elements used in the list. - _IndexType GetSize(void) const; - - /// \brief Empties the list. The list is not deallocated if it is small, - /// unless \a deallocateSmallBlocks is true - void Clear( bool deallocateSmallBlocks=true, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Empties the list, first calling RakNet::OP_Delete on all items. - /// \details The list is not deallocated if it is small, unless \a deallocateSmallBlocks is true - void ClearPointers( bool deallocateSmallBlocks=true, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Empty one item from the list, first calling RakNet::OP_Delete on that item. - void ClearPointer( _KeyType key, const char *file=__FILE__, unsigned int line=__LINE__ ); - - /// \brief Reverses the elements in the list, and flips the sort order - /// returned by GetSortOrder() if IsSorted() returns true at the time the function is called - void ReverseList(void); - - /// \brief Reallocates the list to a larger size. - /// If \a size is smaller than the value returned by GetSize(), the call does nothing. - void Reallocate(_IndexType size, const char *file=__FILE__, unsigned int line=__LINE__); - - /// \brief Sorts the list unless it is an ordered list, in which it does nothing as the list is assumed to already be sorted. - /// \details However, if \a force is true, it will also resort the ordered list, useful if the comparison operator between _KeyType and _DataType would now return different results - /// Once the list is sorted, further operations to lookup by key will be log2(n) until the list is modified - void Sort(bool force); - - /// \brief Sets the list to be remembered as sorted. - /// \details Optimization if the source is sorted already - void TagSorted(void); - - /// \brief Defaults to ascending. - /// \details Used by Sort(), and by ML_ORDERED_LIST - void SetSortOrder(bool ascending); - - /// \brief Returns true if ascending. - bool GetSortOrder(void) const; - - /// \brief Returns true if the list is currently believed to be in a sorted state. - /// \details Doesn't actually check for sortedness, just if Sort() - /// was recently called, or MultilistType is ML_ORDERED_LIST - bool IsSorted(void) const; - - /// Returns what type of list this is - MultilistType GetMultilistType(void) const; - - /// \brief Changes what type of list this is. - /// \pre Template must be defined with ML_VARIABLE_DURING_RUNTIME for this to do anything - /// \param[in] mlType Any value of the enum MultilistType, except ML_VARIABLE_DURING_RUNTIME - void SetMultilistType(MultilistType newType); - - /// \brief Returns the intersection of two lists. - /// Intersection is items common to both lists. - static void FindIntersection( - Multilist& source1, - Multilist& source2, - Multilist& intersection, - Multilist& uniqueToSource1, - Multilist& uniqueToSource2); - - protected: - void ReallocateIfNeeded(const char *file, unsigned int line); - void DeallocateIfNeeded(const char *file, unsigned int line); - void ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line); - void ReverseListInternal(void); - void InsertInOrderedList(const _DataType &d, const _KeyType &key); - _IndexType GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const; - void InsertShiftArrayRight(const _DataType &d, _IndexType index); - void DeleteShiftArrayLeft(_IndexType index); - void QSortAscending(_IndexType left, _IndexType right); - void QSortDescending(_IndexType left, _IndexType right); - void CopySource( const Multilist& source ); - - /// An array of user values - _DataType* data; - - /// Number of elements in the list - _IndexType dataSize; - - /// Size of \a array - _IndexType allocationSize; - - /// Array index for the head of the queue - _IndexType queueHead; - - /// Array index for the tail of the queue - _IndexType queueTail; - - /// How many bytes the user chose to preallocate - /// Won't automatically deallocate below this - _IndexType preallocationSize; - - enum - { - ML_UNSORTED, - ML_SORTED_ASCENDING, - ML_SORTED_DESCENDING - } sortState; - - bool ascendingSort; - - // In case we are using the variable type multilist - MultilistType variableMultilistType; - }; - - template - Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist() - { - data=0; - dataSize=0; - allocationSize=0; - ascendingSort=true; - sortState=ML_UNSORTED; - queueHead=0; - queueTail=0; - preallocationSize=0; - - if (_MultilistType==ML_ORDERED_LIST) - sortState=ML_SORTED_ASCENDING; - else - sortState=ML_UNSORTED; - - if (_MultilistType==ML_VARIABLE_DURING_RUNTIME) - variableMultilistType=ML_UNORDERED_LIST; - } - - template - Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::~Multilist() - { - if (data!=0) - RakNet::OP_DELETE_ARRAY(data, _FILE_AND_LINE_); - } - - template - Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist( const Multilist& source ) - { - CopySource(source); - } - - template - Multilist<_MultilistType, _DataType, _KeyType, _IndexType>& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator= ( const Multilist& source ) - { - Clear(true); - CopySource(source); - return *this; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::CopySource( const Multilist& source ) - { - dataSize=source.GetSize(); - ascendingSort=source.ascendingSort; - sortState=source.sortState; - queueHead=0; - queueTail=dataSize; - preallocationSize=source.preallocationSize; - variableMultilistType=source.variableMultilistType; - if (source.data==0) - { - data=0; - allocationSize=0; - } - else - { - allocationSize=dataSize; - data = RakNet::OP_NEW_ARRAY<_DataType>(dataSize,_FILE_AND_LINE_); - _IndexType i; - for (i=0; i < dataSize; i++) - data[i]=source[i]; - } - } - - template - _DataType& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator[] ( const _IndexType position ) const - { - RakAssert(position= allocationSize ) - return data[ queueHead + position - allocationSize ]; - else - return data[ queueHead + position ]; - } - - return data[position]; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const char *file, unsigned int line ) - { - Push(d,d,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const _KeyType &key, const char *file, unsigned int line ) - { - ReallocateIfNeeded(file,line); - - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) - { - data[dataSize]=d; - dataSize++; - } - else if (GetMultilistType()==ML_QUEUE) - { - data[queueTail++] = d; - - if ( queueTail == allocationSize ) - queueTail = 0; - dataSize++; - } - else - { - RakAssert(GetMultilistType()==ML_ORDERED_LIST); - InsertInOrderedList(d,key); - } - - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE) - { - // Break sort if no longer sorted - if (sortState!=ML_UNSORTED && dataSize>1) - { - if (ascendingSort) - { - if ( MLKeyRef<_KeyType>(key) < operator[](dataSize-2) ) - sortState=ML_UNSORTED; - } - else - { - if ( MLKeyRef<_KeyType>(key) > operator[](dataSize-2) ) - sortState=ML_UNSORTED; - } - - sortState=ML_UNSORTED; - } - } - } - - template - _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Pop(const char *file, unsigned int line) - { - RakAssert(IsEmpty()==false); - DeallocateIfNeeded(file,line); - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - dataSize--; - return data[dataSize]; - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - - if ( ++queueHead == allocationSize ) - queueHead = 0; - - if ( queueHead == 0 ) - return data[ allocationSize -1 ]; - - dataSize--; - return data[ queueHead -1 ]; - } - } - - template - _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Peek(void) const - { - RakAssert(IsEmpty()==false); - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - return data[dataSize-1]; - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - return data[ queueHead ]; - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const char *file, unsigned int line ) - { - PushOpposite(d,d,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const _KeyType &key, const char *file, unsigned int line ) - { - ReallocateIfNeeded(file,line); - - // Unordered list Push at back - if (GetMultilistType()==ML_UNORDERED_LIST) - { - data[dataSize]=d; - dataSize++; - } - else if (GetMultilistType()==ML_STACK) - { - // Stack push at front of the list, instead of back as normal - InsertAtIndex(d,0,file,line); - } - else if (GetMultilistType()==ML_QUEUE) - { - // Queue push at front of the list, instead of back as normal - InsertAtIndex(d,0,file,line); - } - else - { - RakAssert(GetMultilistType()==ML_ORDERED_LIST); - InsertInOrderedList(d,key); - } - - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE) - { - // Break sort if no longer sorted - if (sortState!=ML_UNSORTED && dataSize>1) - { - if (ascendingSort) - { - if ( MLKeyRef<_KeyType>(key) > operator[](1) ) - sortState=ML_UNSORTED; - } - else - { - if ( MLKeyRef<_KeyType>(key) < operator[](1) ) - sortState=ML_UNSORTED; - } - } - } - } - - template - _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PopOpposite(const char *file, unsigned int line) - { - RakAssert(IsEmpty()==false); - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - // Copy leftmost to end - ReallocateIfNeeded(file,line); - data[dataSize]=data[0]; - DeleteShiftArrayLeft(0); - --dataSize; - // Assuming still leaves at least one element past the end of the list allocated - DeallocateIfNeeded(file,line); - // Return end - return data[dataSize+1]; - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - // Deallocate first, since we are returning off the existing list - DeallocateIfNeeded(file,line); - dataSize--; - - if (queueTail==0) - queueTail=allocationSize-1; - else - --queueTail; - - return data[queueTail]; - } - } - - template - _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PeekOpposite(void) const - { - RakAssert(IsEmpty()==false); - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - return data[0]; - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - _IndexType priorIndex; - if (queueTail==0) - priorIndex=allocationSize-1; - else - priorIndex=queueTail-1; - - return data[priorIndex]; - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertAtIndex(const _DataType &d, _IndexType index, const char *file, unsigned int line) - { - ReallocateIfNeeded(file,line); - - if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - if (index>=dataSize) - { - // insert at end - data[dataSize]=d; - - dataSize++; - } - else - { - // insert at index - InsertShiftArrayRight(d,index); - } - } - else - { - data[queueTail++] = d; - - if ( queueTail == allocationSize ) - queueTail = 0; - - ++dataSize; - - if (dataSize==1) - return; - - _IndexType writeIndex, readIndex, trueWriteIndex, trueReadIndex; - writeIndex=dataSize-1; - readIndex=writeIndex-1; - while (readIndex >= index) - { - if ( queueHead + writeIndex >= allocationSize ) - trueWriteIndex = queueHead + writeIndex - allocationSize; - else - trueWriteIndex = queueHead + writeIndex; - - if ( queueHead + readIndex >= allocationSize ) - trueReadIndex = queueHead + readIndex - allocationSize; - else - trueReadIndex = queueHead + readIndex; - - data[trueWriteIndex]=data[trueReadIndex]; - - if (readIndex==0) - break; - writeIndex--; - readIndex--; - } - - if ( queueHead + index >= allocationSize ) - trueWriteIndex = queueHead + index - allocationSize; - else - trueWriteIndex = queueHead + index; - - data[trueWriteIndex]=d; - } - - if (_MultilistType!=ML_ORDERED_LIST) - sortState=ML_UNSORTED; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtIndex(_IndexType position, const char *file, unsigned int line) - { - RakAssert(position < dataSize); - RakAssert(IsEmpty()==false); - - if (GetMultilistType()==ML_UNORDERED_LIST) - { - // Copy tail to current - data[position]=data[dataSize-1]; - } - else if (GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) - { - DeleteShiftArrayLeft(position); - } - else - { - RakAssert(GetMultilistType()==ML_QUEUE); - - _IndexType index, next; - - if ( queueHead + position >= allocationSize ) - index = queueHead + position - allocationSize; - else - index = queueHead + position; - - next = index + 1; - - if ( next == allocationSize ) - next = 0; - - while ( next != queueTail ) - { - // Overwrite the previous element - data[ index ] = data[ next ]; - index = next; - //next = (next + 1) % allocationSize; - - if ( ++next == allocationSize ) - next = 0; - } - - // Move the queueTail back - if ( queueTail == 0 ) - queueTail = allocationSize - 1; - else - --queueTail; - } - - - dataSize--; - DeallocateIfNeeded(file,line); - } - - template - bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtKey(_KeyType key, bool assertIfDoesNotExist, const char *file, unsigned int line) - { - _IndexType index = GetIndexOf(key); - if (index==(_IndexType)-1) - { - RakAssert(assertIfDoesNotExist==false && "RemoveAtKey element not found"); - return false; - } - RemoveAtIndex(index,file,line); - return true; - } - - template - _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexOf(_KeyType key) const - { - _IndexType i; - if (IsSorted()) - { - bool objectExists; - i=GetIndexFromKeyInSortedList(key, &objectExists); - if (objectExists) - return i; - return (_IndexType)-1; - } - else if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) - { - for (i=0; i < dataSize; i++) - { - if (MLKeyRef<_KeyType>(key)==data[i]) - return i; - } - return (_IndexType)-1; - } - else - { - RakAssert( GetMultilistType()==ML_QUEUE ); - - for (i=0; i < dataSize; i++) - { - if (MLKeyRef<_KeyType>(key)==operator[](i)) - return i; - } - return (_IndexType)-1; - } - } - - template - _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetInsertionIndex(_KeyType key) const - { - _IndexType i; - if (IsSorted()) - { - bool objectExists; - i=GetIndexFromKeyInSortedList(key, &objectExists); - if (objectExists) - return (_IndexType)-1; - return i; - } - else if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) - { - for (i=0; i < dataSize; i++) - { - if (MLKeyRef<_KeyType>(key)==data[i]) - return (_IndexType)-1; - } - return dataSize; - } - else - { - RakAssert( GetMultilistType()==ML_QUEUE ); - - for (i=0; i < dataSize; i++) - { - if (MLKeyRef<_KeyType>(key)==operator[](i)) - return (_IndexType)-1; - } - return dataSize; - } - } - - template - _DataType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetPtr(_KeyType key) const - { - _IndexType i = GetIndexOf(key); - if (i==(_IndexType)-1) - return 0; - return data[i]; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line) - { - _IndexType i; - for (i=0; i < dataSize; i++) - func(operator[](i), file, line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item)) - { - _IndexType i; - for (i=0; i < dataSize; i++) - func(operator[](i)); - } - - template - bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsEmpty(void) const - { - return dataSize==0; - } - - template - _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSize(void) const - { - return dataSize; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Clear( bool deallocateSmallBlocks, const char *file, unsigned int line ) - { - dataSize=0; - if (GetMultilistType()==ML_ORDERED_LIST) - if (ascendingSort) - sortState=ML_SORTED_ASCENDING; - else - sortState=ML_SORTED_DESCENDING; - else - sortState=ML_UNSORTED; - queueHead=0; - queueTail=0; - - if (deallocateSmallBlocks && allocationSize < 128 && data) - { - RakNet::OP_DELETE_ARRAY(data,file,line); - data=0; - allocationSize=0; - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ClearPointers( bool deallocateSmallBlocks, const char *file, unsigned int line ) - { - _IndexType i; - for (i=0; i < dataSize; i++) - RakNet::OP_DELETE(operator[](i), file, line); - Clear(deallocateSmallBlocks, file, line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ClearPointer( _KeyType key, const char *file, unsigned int line ) - { - _IndexType i; - i = GetIndexOf(key); - if (i!=-1) - { - RakNet::OP_DELETE(operator[](i), file, line); - RemoveAtIndex(i); - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseList(void) - { - if (IsSorted()) - ascendingSort=!ascendingSort; - - ReverseListInternal(); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Reallocate(_IndexType size, const char *file, unsigned int line) - { - _IndexType newAllocationSize; - if (size < dataSize) - newAllocationSize=dataSize; - else - newAllocationSize=size; - preallocationSize=size; - ReallocToSize(newAllocationSize,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Sort(bool force) - { - if (IsSorted() && force==false) - return; - - if (dataSize>1) - { - if (ascendingSort) - QSortAscending(0,dataSize-1); - else - QSortDescending(0,dataSize-1); - } - - TagSorted(); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::TagSorted(void) - { - if (ascendingSort) - sortState=ML_SORTED_ASCENDING; - else - sortState=ML_SORTED_DESCENDING; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortAscending(_IndexType leftEdge, _IndexType rightEdge) - { - _DataType temp; - _IndexType left=leftEdge; - _IndexType right=rightEdge; - _IndexType pivotIndex=left++; - - while (left data[pivotIndex]) - { - --left; - - data[pivotIndex]=data[left]; - data[left]=temp; - } - else - { - data[pivotIndex]=data[left]; - data[left]=temp; - - --left; - } - - if (left!=leftEdge) - QSortAscending(leftEdge, left); - - if (right!=rightEdge) - QSortAscending(right, rightEdge); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortDescending(_IndexType leftEdge, _IndexType rightEdge) - { - _DataType temp; - _IndexType left=leftEdge; - _IndexType right=rightEdge; - _IndexType pivotIndex=left++; - - while (left= data[pivotIndex]) - { - ++left; - } - else - { - temp=data[left]; - data[left]=data[right]; - data[right]=temp; - --right; - } - } - - temp=data[pivotIndex]; - - // Move pivot to center - if (data[left] < data[pivotIndex]) - { - --left; - - data[pivotIndex]=data[left]; - data[left]=temp; - } - else - { - data[pivotIndex]=data[left]; - data[left]=temp; - - --left; - } - - if (left!=leftEdge) - QSortDescending(leftEdge, left); - - if (right!=rightEdge) - QSortDescending(right, rightEdge); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetSortOrder(bool ascending) - { - if (ascendingSort!=ascending && IsSorted()) - { - ascendingSort=ascending; - // List is sorted, and the sort order has changed. So reverse the list - ReverseListInternal(); - } - else - ascendingSort=ascending; - } - - template - bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSortOrder(void) const - { - return ascendingSort; - } - - template - bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsSorted(void) const - { - return GetMultilistType()==ML_ORDERED_LIST || sortState!=ML_UNSORTED; - } - - template - MultilistType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetMultilistType(void) const - { - if (_MultilistType==ML_VARIABLE_DURING_RUNTIME) - return variableMultilistType; - return _MultilistType; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetMultilistType(MultilistType newType) - { - RakAssert(_MultilistType==ML_VARIABLE_DURING_RUNTIME); - switch (variableMultilistType) - { - case ML_UNORDERED_LIST: - switch (newType) - { - case ML_UNORDERED_LIST: - // No change - break; - case ML_STACK: - // Same data format - break; - case ML_QUEUE: - queueHead=0; - queueTail=dataSize; - break; - case ML_ORDERED_LIST: - Sort(false); - break; - } - break; - case ML_STACK: - switch (newType) - { - case ML_UNORDERED_LIST: - // Same data format - break; - case ML_STACK: - // No change - break; - case ML_QUEUE: - queueHead=0; - queueTail=dataSize; - break; - case ML_ORDERED_LIST: - Sort(false); - break; - } - break; - case ML_QUEUE: - switch (newType) - { - case ML_UNORDERED_LIST: - case ML_STACK: - case ML_ORDERED_LIST: - if (queueTail < queueHead) - { - // Realign data if wrapped - ReallocToSize(dataSize, _FILE_AND_LINE_); - } - else - { - // Else can just copy starting at head - _IndexType i; - for (i=0; i < dataSize; i++) - data[i]=operator[](i); - } - if (newType==ML_ORDERED_LIST) - Sort(false); - break; - case ML_QUEUE: - // No change - break; - } - break; - case ML_ORDERED_LIST: - switch (newType) - { - case ML_UNORDERED_LIST: - case ML_STACK: - case ML_QUEUE: - // Same data format - // Tag as sorted - if (ascendingSort) - sortState=ML_SORTED_ASCENDING; - else - sortState=ML_SORTED_DESCENDING; - if (newType==ML_QUEUE) - { - queueHead=0; - queueTail=dataSize; - } - break; - case ML_ORDERED_LIST: - // No change - break; - } - break; - } - - variableMultilistType=newType; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::FindIntersection( - Multilist& source1, - Multilist& source2, - Multilist& intersection, - Multilist& uniqueToSource1, - Multilist& uniqueToSource2) - { - _IndexType index1=0, index2=0; - source1.SetSortOrder(true); - source2.SetSortOrder(true); - source1.Sort(false); - source2.Sort(false); - intersection.Clear(true,_FILE_AND_LINE_); - uniqueToSource1.Clear(true,_FILE_AND_LINE_); - uniqueToSource2.Clear(true,_FILE_AND_LINE_); - - while (index1 < source1.GetSize() && index2 < source2.GetSize()) - { - if (source1[index1] - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocateIfNeeded(const char *file, unsigned int line) - { - if (dataSize65536) - newAllocationSize=allocationSize+65536; - else - { - newAllocationSize=allocationSize<<1; // * 2 - // Protect against underflow - if (newAllocationSize < allocationSize) - newAllocationSize=allocationSize+65536; - } - - ReallocToSize(newAllocationSize,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeallocateIfNeeded(const char *file, unsigned int line) - { - if (allocationSize<512) - return; - if (dataSize >= allocationSize/3 ) - return; - if (dataSize <= preallocationSize ) - return; - - _IndexType newAllocationSize = dataSize<<1; // * 2 - - ReallocToSize(newAllocationSize,file,line); - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line) - { - _DataType* newData = RakNet::OP_NEW_ARRAY<_DataType>(newAllocationSize,file,line); - _IndexType i; - for (i=0; i < dataSize; i++) - newData[i]=operator[](i); - if (dataSize>0) - { - RakNet::OP_DELETE_ARRAY(data,file,line); - if (GetMultilistType()==ML_QUEUE) - { - queueHead=0; - queueTail=dataSize; - } - } - data=newData; - allocationSize=newAllocationSize; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseListInternal(void) - { - _DataType temp; - _IndexType i; - for (i=0; i < dataSize/2; i++) - { - temp=operator[](i); - operator[](i)=operator[](dataSize-1-i); - operator[](dataSize-1-i)=temp; - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertInOrderedList(const _DataType &d, const _KeyType &key) - { - RakAssert(GetMultilistType()==ML_ORDERED_LIST); - - bool objectExists; - _IndexType index; - index = GetIndexFromKeyInSortedList(key, &objectExists); - - // if (objectExists) - // { - // Ordered list only allows unique insertions - // RakAssert("Duplicate insertion into ordered list" && false); - // return; - // } - - if (index>=dataSize) - { - // insert at end - data[dataSize]=d; - dataSize++; - } - else - { - // insert at index - InsertShiftArrayRight(d,index); - } - } - - template - _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const - { - RakAssert(IsSorted()); - _IndexType index, upperBound, lowerBound; - - if (dataSize==0) - { - *objectExists=false; - return 0; - } - - upperBound=dataSize-1; - lowerBound=0; - index = dataSize/2; - -#ifdef _MSC_VER - #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - if (MLKeyRef<_KeyType>(key) > operator[](index) ) - { - if (ascendingSort) - lowerBound=index+1; - else - upperBound=index-1; - } - else if (MLKeyRef<_KeyType>(key) < operator[](index) ) - { - if (ascendingSort) - upperBound=index-1; - else - lowerBound=index+1; - } - else - { - // == - *objectExists=true; - return index; - } - - index=lowerBound+(upperBound-lowerBound)/2; - - if (lowerBound>upperBound || upperBound==(_IndexType)-1) - { - *objectExists=false; - return lowerBound; // No match - } - } - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertShiftArrayRight(const _DataType &d, _IndexType index) - { - RakAssert(_MultilistType!=ML_QUEUE); - - // Move the elements in the list to make room - _IndexType i; - for ( i = dataSize; i != index; i-- ) - data[ i ] = data[ i - 1 ]; - - // Insert the new item at the correct spot - data[ index ] = d; - - ++dataSize; - } - - template - void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeleteShiftArrayLeft( _IndexType index ) - { - RakAssert(index < dataSize); - RakAssert(_MultilistType!=ML_QUEUE); - - _IndexType i; - for ( i = index; i < dataSize-1; i++ ) - data[i]=data[i+1]; - } -}; - -/* -struct KeyAndValue -{ - int key; - short value; -}; - -DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS(KeyAndValue,int,key) - -void MultilistUnitTest(void) -{ - DataStructures::DefaultIndexType oldSize; - DataStructures::Multilist ml1; - ml1.Reallocate(64); - RakAssert(ml1.IsEmpty()); - ml1.Push(53); - RakAssert(ml1.Peek()==53); - RakAssert(ml1.IsEmpty()==false); - RakAssert(ml1.Pop()==53); - RakAssert(ml1.IsEmpty()==true); - for (int i=0; i < 512; i++) - ml1.Push(i); - RakAssert(ml1.GetIndexOf(200)==200); - RakAssert(ml1.PeekOpposite()==0); - RakAssert(ml1.PopOpposite()==0); - RakAssert(ml1.PeekOpposite()==1); - RakAssert(ml1.Peek()==511); - ml1.ReverseList(); - for (int i=0; i < 511; i++) - RakAssert(ml1[i]==511-i); - RakAssert(ml1.PeekOpposite()==511); - RakAssert(ml1.Peek()==1); - oldSize = ml1.GetSize(); - ml1.RemoveAtIndex(0); - RakAssert(ml1.GetSize()==oldSize-1); - RakAssert(ml1.PeekOpposite()==1); - ml1.Clear(_FILE_AND_LINE_); - RakAssert(ml1.IsEmpty()==true); - - ml1.Sort(true); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(100); - ml1.Sort(true); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(50); - ml1.Push(100); - ml1.Sort(true); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(100); - ml1.Push(50); - ml1.Sort(true); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(100); - ml1.Push(50); - ml1.Push(150); - ml1.Push(25); - ml1.Push(175); - ml1.Sort(true); - RakAssert(ml1[0]==25); - RakAssert(ml1[1]==50); - RakAssert(ml1[2]==100); - RakAssert(ml1[3]==150); - RakAssert(ml1[4]==175); - RakAssert(ml1.GetIndexOf(25)==0); - RakAssert(ml1.GetIndexOf(50)==1); - RakAssert(ml1.GetIndexOf(100)==2); - RakAssert(ml1.GetIndexOf(150)==3); - RakAssert(ml1.GetIndexOf(175)==4); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(1); - ml1.Push(2); - ml1.Push(3); - ml1.Push(4); - ml1.Push(5); - ml1.Sort(true); - RakAssert(ml1[0]==1); - RakAssert(ml1[1]==2); - RakAssert(ml1[2]==3); - RakAssert(ml1[3]==4); - RakAssert(ml1[4]==5); - RakAssert(ml1.GetIndexOf(1)==0); - RakAssert(ml1.GetIndexOf(2)==1); - RakAssert(ml1.GetIndexOf(3)==2); - RakAssert(ml1.GetIndexOf(4)==3); - RakAssert(ml1.GetIndexOf(5)==4); - ml1.Clear(_FILE_AND_LINE_); - - ml1.Push(5); - ml1.Push(4); - ml1.Push(3); - ml1.Push(2); - ml1.Push(1); - ml1.Sort(true); - RakAssert(ml1[0]==1); - RakAssert(ml1[1]==2); - RakAssert(ml1[2]==3); - RakAssert(ml1[3]==4); - RakAssert(ml1[4]==5); - RakAssert(ml1.GetIndexOf(1)==0); - RakAssert(ml1.GetIndexOf(2)==1); - RakAssert(ml1.GetIndexOf(3)==2); - RakAssert(ml1.GetIndexOf(4)==3); - RakAssert(ml1.GetIndexOf(5)==4); - ml1.Sort(true); - RakAssert(ml1[0]==1); - RakAssert(ml1[1]==2); - RakAssert(ml1[2]==3); - RakAssert(ml1[3]==4); - RakAssert(ml1[4]==5); - RakAssert(ml1.GetIndexOf(1)==0); - RakAssert(ml1.GetIndexOf(2)==1); - RakAssert(ml1.GetIndexOf(3)==2); - RakAssert(ml1.GetIndexOf(4)==3); - RakAssert(ml1.GetIndexOf(5)==4); - ml1.Clear(_FILE_AND_LINE_); - - DataStructures::Multilist ml2; - ml2.Reallocate(64); - RakAssert(ml2.IsEmpty()); - ml2.Push(53); - RakAssert(ml2.Peek()==53); - RakAssert(ml2.IsEmpty()==false); - RakAssert(ml2.Pop()==53); - RakAssert(ml2.IsEmpty()==true); - for (int i=0; i < 512; i++) - ml2.Push(i); - RakAssert(ml2.GetIndexOf(200)==200); - RakAssert(ml2.PeekOpposite()==0); - RakAssert(ml2.PopOpposite()==0); - RakAssert(ml2.PeekOpposite()==1); - RakAssert(ml2.Peek()==511); - ml2.ReverseList(); - for (int i=0; i < 511; i++) - RakAssert(ml2[i]==511-i); - RakAssert(ml2.PeekOpposite()==511); - RakAssert(ml2.Peek()==1); - oldSize = ml2.GetSize(); - ml2.RemoveAtIndex(0); - RakAssert(ml2.GetSize()==oldSize-1); - RakAssert(ml2.Peek()==1); - RakAssert(ml2.PeekOpposite()==510); - ml2.Clear(_FILE_AND_LINE_); - RakAssert(ml2.IsEmpty()==true); - - DataStructures::Multilist ml3; - RakAssert(ml3.IsEmpty()); - ml3.Push(53); - RakAssert(ml3.Peek()==53); - RakAssert(ml3.IsEmpty()==false); - RakAssert(ml3.Pop()==53); - RakAssert(ml3.IsEmpty()==true); - for (int i=0; i < 512; i++) - ml3.Push(i); - RakAssert(ml3.GetIndexOf(200)==200); - RakAssert(ml3.PeekOpposite()==511); - RakAssert(ml3.PopOpposite()==511); - RakAssert(ml3.PeekOpposite()==510); - RakAssert(ml3.Peek()==0); - ml3.ReverseList(); - for (int i=0; i < 511; i++) - RakAssert(ml3[i]==511-1-i); - RakAssert(ml3.PeekOpposite()==0); - RakAssert(ml3.Peek()==510); - oldSize = ml3.GetSize(); - ml3.RemoveAtIndex(0); - RakAssert(ml3.GetSize()==oldSize-1); - RakAssert(ml3.Peek()==509); - RakAssert(ml3.PeekOpposite()==0); - ml3.Clear(_FILE_AND_LINE_); - RakAssert(ml3.IsEmpty()==true); - - ml3.PushOpposite(100); - ml3.PushOpposite(50); - ml3.PushOpposite(150); - ml3.PushOpposite(25); - ml3.PushOpposite(175); - ml3.Sort(true); - RakAssert(ml3[0]==25); - RakAssert(ml3[1]==50); - RakAssert(ml3[2]==100); - RakAssert(ml3[3]==150); - RakAssert(ml3[4]==175); - RakAssert(ml3.GetIndexOf(25)==0); - RakAssert(ml3.GetIndexOf(50)==1); - RakAssert(ml3.GetIndexOf(100)==2); - RakAssert(ml3.GetIndexOf(150)==3); - RakAssert(ml3.GetIndexOf(175)==4); - ml3.Clear(_FILE_AND_LINE_); - - ml3.PushOpposite(1); - ml3.PushOpposite(2); - ml3.PushOpposite(3); - ml3.PushOpposite(4); - ml3.PushOpposite(5); - ml3.Sort(true); - RakAssert(ml3[0]==1); - RakAssert(ml3[1]==2); - RakAssert(ml3[2]==3); - RakAssert(ml3[3]==4); - RakAssert(ml3[4]==5); - RakAssert(ml3.GetIndexOf(1)==0); - RakAssert(ml3.GetIndexOf(2)==1); - RakAssert(ml3.GetIndexOf(3)==2); - RakAssert(ml3.GetIndexOf(4)==3); - RakAssert(ml3.GetIndexOf(5)==4); - ml3.Clear(_FILE_AND_LINE_); - - ml3.PushOpposite(5); - ml3.PushOpposite(4); - ml3.PushOpposite(3); - ml3.PushOpposite(2); - ml3.PushOpposite(1); - ml3.Sort(true); - RakAssert(ml3[0]==1); - RakAssert(ml3[1]==2); - RakAssert(ml3[2]==3); - RakAssert(ml3[3]==4); - RakAssert(ml3[4]==5); - RakAssert(ml3.GetIndexOf(1)==0); - RakAssert(ml3.GetIndexOf(2)==1); - RakAssert(ml3.GetIndexOf(3)==2); - RakAssert(ml3.GetIndexOf(4)==3); - RakAssert(ml3.GetIndexOf(5)==4); - ml3.Sort(true); - RakAssert(ml3[0]==1); - RakAssert(ml3[1]==2); - RakAssert(ml3[2]==3); - RakAssert(ml3[3]==4); - RakAssert(ml3[4]==5); - RakAssert(ml3.GetIndexOf(1)==0); - RakAssert(ml3.GetIndexOf(2)==1); - RakAssert(ml3.GetIndexOf(3)==2); - RakAssert(ml3.GetIndexOf(4)==3); - RakAssert(ml3.GetIndexOf(5)==4); - - ml3.SetSortOrder(false); - ml3.Sort(false); - RakAssert(ml3[0]==5); - RakAssert(ml3[1]==4); - RakAssert(ml3[2]==3); - RakAssert(ml3[3]==2); - RakAssert(ml3[4]==1); - RakAssert(ml3.GetIndexOf(1)==4); - RakAssert(ml3.GetIndexOf(2)==3); - RakAssert(ml3.GetIndexOf(3)==2); - RakAssert(ml3.GetIndexOf(4)==1); - RakAssert(ml3.GetIndexOf(5)==0); - - ml3.Clear(_FILE_AND_LINE_); - - DataStructures::Multilist ml4; - ml4.Reallocate(64); - RakAssert(ml4.IsEmpty()); - ml4.Push(53); - RakAssert(ml4.Peek()==53); - RakAssert(ml4.IsEmpty()==false); - RakAssert(ml4.Pop()==53); - RakAssert(ml4.IsEmpty()==true); - for (int i=0; i < 512; i++) - ml4.Push(i); - RakAssert(ml4.GetIndexOf(200)==200); - RakAssert(ml4.PeekOpposite()==0); - RakAssert(ml4.PopOpposite()==0); - RakAssert(ml4.PeekOpposite()==1); - RakAssert(ml4.Peek()==511); - ml4.ReverseList(); - for (int i=0; i < 511; i++) - RakAssert(ml4[i]==511-i); - RakAssert(ml4.PeekOpposite()==511); - RakAssert(ml4.Peek()==1); - oldSize = ml4.GetSize(); - ml4.RemoveAtIndex(0); - RakAssert(ml4.GetSize()==oldSize-1); - RakAssert(ml4.Peek()==1); - RakAssert(ml4.PeekOpposite()==510); - ml4.Clear(_FILE_AND_LINE_); - RakAssert(ml4.IsEmpty()==true); - - DataStructures::Multilist ml5; - - for (int i=0; i < 16; i++) - { - KeyAndValue *kav = new KeyAndValue; - kav->key=i; - kav->value=i+100; - ml5.Push(kav,kav->key); - } - - RakAssert(ml5.GetIndexOf(0)==0); - RakAssert(ml5.GetIndexOf(5)==5); - RakAssert(ml5.GetIndexOf(15)==15); - RakAssert(ml5.GetIndexOf(16)==-1); - ml5.RemoveAtKey(0,true); - RakAssert(ml5.GetIndexOf(1)==0); - KeyAndValue *iPtr = ml5.GetPtr(5); - RakAssert(iPtr); - RakAssert(iPtr->value=105); - iPtr = ml5.GetPtr(1234); - RakAssert(iPtr==0); - ml5.ForEach(DataStructures::DeletePtr); - - - DataStructures::Multilist ml6; - ml6.Push(2); - ml6.Push(1); - ml6.Push(6); - ml6.Push(3); - RakAssert(ml6.Peek()==3); - ml6.SetMultilistType(ML_STACK); - RakAssert(ml6.Peek()==3); - ml6.SetMultilistType(ML_QUEUE); - RakAssert(ml6.Peek()==2); - ml6.SetMultilistType(ML_ORDERED_LIST); - RakAssert(ml6.Peek()=6); - ml6.SetMultilistType(ML_STACK); - RakAssert(ml6.Peek()==6); - ml6.SetMultilistType(ML_QUEUE); - RakAssert(ml6.Peek()==1); -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif -*/ - -#endif +/// \file DS_Multilist.h +/// \internal +/// \brief ADT that can represent an unordered list, ordered list, stack, or queue with a common interface +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __MULTILIST_H +#define __MULTILIST_H + +#include "RakAssert.h" +#include // memmove +#include "Export.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" + + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#pragma warning( disable : 4512 ) // warning C4512: assignment operator could not be generated +#endif + +/// What algorithm to use to store the data for the Multilist +enum MultilistType +{ + /// Removing from the middle of the list will swap the end of the list rather than shift the elements. Push and Pop operate on the tail. + ML_UNORDERED_LIST, + /// A normal list, with the list order preserved. Push and Pop operate on the tail. + ML_STACK, + /// A queue. Push and Pop operate on the head + ML_QUEUE, + /// A list that is always kept in order. Elements must be unique, and compare against each other consistently using <, ==, and > + ML_ORDERED_LIST, + /// A list whose type can change at runtime + ML_VARIABLE_DURING_RUNTIME +}; + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// Can be used with Multilist::ForEach + /// Assuming the Multilist holds pointers, will delete those pointers + template + void DeletePtr_RakNet(templateType &ptr, const char *file, unsigned int line ) {RakNet::OP_DELETE(ptr, file, line);} + + /// Can be used with Multilist::ForEach + /// Assuming the Multilist holds pointers, will delete those pointers + template + void DeletePtr(templateType &ptr) {delete ptr;} + + /// The following is invalid. + /// bool operator<( const MyClass *myClass, const int &inputKey ) {return myClass->value < inputKey;} + /// At least one type has to be a reference to a class + /// MLKeyRef is a helper class to turn a native type into a class, so you can compare that native type against a pointer to a different class + /// Used for he Multilist, when _DataType != _KeyType + template < class templateType > + class MLKeyRef + { + public: + MLKeyRef(const templateType& input) : val(input) {} + const templateType &Get(void) const {return val;} + bool operator<( const templateType &right ) {return val < right;} + bool operator>( const templateType &right ) {return val > right;} + bool operator==( const templateType &right ) {return val == right;} + protected: + const templateType &val; + }; + + /// For the Multilist, when _DataType != _KeyType, you must define the comparison operators between the key and the data + /// This is non-trivial due to the need to use MLKeyRef in case the type held is a pointer to a structure or class and the key type is not a class + /// For convenience, this macro will implement the comparison operators under the following conditions + /// 1. _DataType is a pointer to a class or structure + /// 2. The key is a member variable of _DataType + #define DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS( _CLASS_NAME_, _KEY_TYPE_, _MEMBER_VARIABLE_NAME_ ) \ + bool operator<( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() < cls->_MEMBER_VARIABLE_NAME_;} \ + bool operator>( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() > cls->_MEMBER_VARIABLE_NAME_;} \ + bool operator==( const DataStructures::MLKeyRef<_KEY_TYPE_> &inputKey, const _CLASS_NAME_ *cls ) {return inputKey.Get() == cls->_MEMBER_VARIABLE_NAME_;} + + typedef uint32_t DefaultIndexType; + + /// \brief The multilist, representing an abstract data type that generally holds lists. + /// \param[in] _MultilistType What type of list this is, \sa MultilistType + /// \param[in] _DataType What type of data this list holds. + /// \param[in] _KeyType If a function takes a key to sort on, what type of key this is. The comparison operator between _DataType and _KeyType must be defined + /// \param[in] _IndexType What variable type to use for indices + template + class RAK_DLL_EXPORT Multilist + { + public: + Multilist(); + ~Multilist(); + Multilist( const Multilist& source ); + Multilist& operator= ( const Multilist& source ); + _DataType& operator[] ( const _IndexType position ) const; + /// Unordered list, stack is LIFO + /// QUEUE is FIFO + /// Ordered list is inserted in order + void Push(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ ); + void Push(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Gets or removes and gets an element from the list, according to the same rules as Push(). + /// Ordered list is LIFO for the purposes of Pop and Peek. + _DataType &Pop(const char *file=__FILE__, unsigned int line=__LINE__); + _DataType &Peek(void) const; + + /// \brief Same as Push(), except FIFO and LIFO are reversed. + /// Ordered list still inserts in order. + void PushOpposite(const _DataType &d, const char *file=__FILE__, unsigned int line=__LINE__ ); + void PushOpposite(const _DataType &d, const _KeyType &key, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Same as Pop() and Peek(), except FIFO and LIFO are reversed. + _DataType &PopOpposite(const char *file=__FILE__, unsigned int line=__LINE__); + _DataType &PeekOpposite(void) const; + + /// \brief Stack,Queue: Inserts at index indicated, elements are shifted. + /// Ordered list: Inserts, position is ignored + void InsertAtIndex(const _DataType &d, _IndexType index, const char *file=__FILE__, unsigned int line=__LINE__); + + /// \brief Unordered list, removes at index indicated, swaps last element with that element. + /// Otherwise, array is shifted left to overwrite removed element + /// \details Index[0] returns the same as Pop() for a queue. + /// Same as PopOpposite() for the list and ordered list + void RemoveAtIndex(_IndexType position, const char *file=__FILE__, unsigned int line=__LINE__); + + /// \brief Find the index of \a key, and remove at that index. + bool RemoveAtKey(_KeyType key, bool assertIfDoesNotExist, const char *file=__FILE__, unsigned int line=__LINE__); + + /// \brief Finds the index of \a key. Return -1 if the key is not found. + _IndexType GetIndexOf(_KeyType key) const; + + /// \brief Returns where in the list we should insert the item, to preserve list order. + /// Returns -1 if the item is already in the list + _IndexType GetInsertionIndex(_KeyType key) const; + + /// \brief Finds the index of \a key. Return 0 if the key is not found. Useful if _DataType is always non-zero pointers. + _DataType GetPtr(_KeyType key) const; + + /// \brief Iterate over the list, calling the function pointer on each element. + void ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line); + void ForEach(void (*func)(_DataType &item)); + + /// \brief Returns if the list is empty. + bool IsEmpty(void) const; + + /// \brief Returns the number of elements used in the list. + _IndexType GetSize(void) const; + + /// \brief Empties the list. The list is not deallocated if it is small, + /// unless \a deallocateSmallBlocks is true + void Clear( bool deallocateSmallBlocks=true, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Empties the list, first calling RakNet::OP_Delete on all items. + /// \details The list is not deallocated if it is small, unless \a deallocateSmallBlocks is true + void ClearPointers( bool deallocateSmallBlocks=true, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Empty one item from the list, first calling RakNet::OP_Delete on that item. + void ClearPointer( _KeyType key, const char *file=__FILE__, unsigned int line=__LINE__ ); + + /// \brief Reverses the elements in the list, and flips the sort order + /// returned by GetSortOrder() if IsSorted() returns true at the time the function is called + void ReverseList(void); + + /// \brief Reallocates the list to a larger size. + /// If \a size is smaller than the value returned by GetSize(), the call does nothing. + void Reallocate(_IndexType size, const char *file=__FILE__, unsigned int line=__LINE__); + + /// \brief Sorts the list unless it is an ordered list, in which it does nothing as the list is assumed to already be sorted. + /// \details However, if \a force is true, it will also resort the ordered list, useful if the comparison operator between _KeyType and _DataType would now return different results + /// Once the list is sorted, further operations to lookup by key will be log2(n) until the list is modified + void Sort(bool force); + + /// \brief Sets the list to be remembered as sorted. + /// \details Optimization if the source is sorted already + void TagSorted(void); + + /// \brief Defaults to ascending. + /// \details Used by Sort(), and by ML_ORDERED_LIST + void SetSortOrder(bool ascending); + + /// \brief Returns true if ascending. + bool GetSortOrder(void) const; + + /// \brief Returns true if the list is currently believed to be in a sorted state. + /// \details Doesn't actually check for sortedness, just if Sort() + /// was recently called, or MultilistType is ML_ORDERED_LIST + bool IsSorted(void) const; + + /// Returns what type of list this is + MultilistType GetMultilistType(void) const; + + /// \brief Changes what type of list this is. + /// \pre Template must be defined with ML_VARIABLE_DURING_RUNTIME for this to do anything + /// \param[in] mlType Any value of the enum MultilistType, except ML_VARIABLE_DURING_RUNTIME + void SetMultilistType(MultilistType newType); + + /// \brief Returns the intersection of two lists. + /// Intersection is items common to both lists. + static void FindIntersection( + Multilist& source1, + Multilist& source2, + Multilist& intersection, + Multilist& uniqueToSource1, + Multilist& uniqueToSource2); + + protected: + void ReallocateIfNeeded(const char *file, unsigned int line); + void DeallocateIfNeeded(const char *file, unsigned int line); + void ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line); + void ReverseListInternal(void); + void InsertInOrderedList(const _DataType &d, const _KeyType &key); + _IndexType GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const; + void InsertShiftArrayRight(const _DataType &d, _IndexType index); + void DeleteShiftArrayLeft(_IndexType index); + void QSortAscending(_IndexType left, _IndexType right); + void QSortDescending(_IndexType left, _IndexType right); + void CopySource( const Multilist& source ); + + /// An array of user values + _DataType* data; + + /// Number of elements in the list + _IndexType dataSize; + + /// Size of \a array + _IndexType allocationSize; + + /// Array index for the head of the queue + _IndexType queueHead; + + /// Array index for the tail of the queue + _IndexType queueTail; + + /// How many bytes the user chose to preallocate + /// Won't automatically deallocate below this + _IndexType preallocationSize; + + enum + { + ML_UNSORTED, + ML_SORTED_ASCENDING, + ML_SORTED_DESCENDING + } sortState; + + bool ascendingSort; + + // In case we are using the variable type multilist + MultilistType variableMultilistType; + }; + + template + Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist() + { + data=0; + dataSize=0; + allocationSize=0; + ascendingSort=true; + sortState=ML_UNSORTED; + queueHead=0; + queueTail=0; + preallocationSize=0; + + if (_MultilistType==ML_ORDERED_LIST) + sortState=ML_SORTED_ASCENDING; + else + sortState=ML_UNSORTED; + + if (_MultilistType==ML_VARIABLE_DURING_RUNTIME) + variableMultilistType=ML_UNORDERED_LIST; + } + + template + Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::~Multilist() + { + if (data!=0) + RakNet::OP_DELETE_ARRAY(data, _FILE_AND_LINE_); + } + + template + Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Multilist( const Multilist& source ) + { + CopySource(source); + } + + template + Multilist<_MultilistType, _DataType, _KeyType, _IndexType>& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator= ( const Multilist& source ) + { + Clear(true); + CopySource(source); + return *this; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::CopySource( const Multilist& source ) + { + dataSize=source.GetSize(); + ascendingSort=source.ascendingSort; + sortState=source.sortState; + queueHead=0; + queueTail=dataSize; + preallocationSize=source.preallocationSize; + variableMultilistType=source.variableMultilistType; + if (source.data==0) + { + data=0; + allocationSize=0; + } + else + { + allocationSize=dataSize; + data = RakNet::OP_NEW_ARRAY<_DataType>(dataSize,_FILE_AND_LINE_); + _IndexType i; + for (i=0; i < dataSize; i++) + data[i]=source[i]; + } + } + + template + _DataType& Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::operator[] ( const _IndexType position ) const + { + RakAssert(position= allocationSize ) + return data[ queueHead + position - allocationSize ]; + else + return data[ queueHead + position ]; + } + + return data[position]; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const char *file, unsigned int line ) + { + Push(d,d,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Push(const _DataType &d, const _KeyType &key, const char *file, unsigned int line ) + { + ReallocateIfNeeded(file,line); + + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) + { + data[dataSize]=d; + dataSize++; + } + else if (GetMultilistType()==ML_QUEUE) + { + data[queueTail++] = d; + + if ( queueTail == allocationSize ) + queueTail = 0; + dataSize++; + } + else + { + RakAssert(GetMultilistType()==ML_ORDERED_LIST); + InsertInOrderedList(d,key); + } + + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE) + { + // Break sort if no longer sorted + if (sortState!=ML_UNSORTED && dataSize>1) + { + if (ascendingSort) + { + if ( MLKeyRef<_KeyType>(key) < operator[](dataSize-2) ) + sortState=ML_UNSORTED; + } + else + { + if ( MLKeyRef<_KeyType>(key) > operator[](dataSize-2) ) + sortState=ML_UNSORTED; + } + + sortState=ML_UNSORTED; + } + } + } + + template + _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Pop(const char *file, unsigned int line) + { + RakAssert(IsEmpty()==false); + DeallocateIfNeeded(file,line); + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + dataSize--; + return data[dataSize]; + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + + if ( ++queueHead == allocationSize ) + queueHead = 0; + + if ( queueHead == 0 ) + return data[ allocationSize -1 ]; + + dataSize--; + return data[ queueHead -1 ]; + } + } + + template + _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Peek(void) const + { + RakAssert(IsEmpty()==false); + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + return data[dataSize-1]; + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + return data[ queueHead ]; + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const char *file, unsigned int line ) + { + PushOpposite(d,d,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PushOpposite(const _DataType &d, const _KeyType &key, const char *file, unsigned int line ) + { + ReallocateIfNeeded(file,line); + + // Unordered list Push at back + if (GetMultilistType()==ML_UNORDERED_LIST) + { + data[dataSize]=d; + dataSize++; + } + else if (GetMultilistType()==ML_STACK) + { + // Stack push at front of the list, instead of back as normal + InsertAtIndex(d,0,file,line); + } + else if (GetMultilistType()==ML_QUEUE) + { + // Queue push at front of the list, instead of back as normal + InsertAtIndex(d,0,file,line); + } + else + { + RakAssert(GetMultilistType()==ML_ORDERED_LIST); + InsertInOrderedList(d,key); + } + + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_QUEUE) + { + // Break sort if no longer sorted + if (sortState!=ML_UNSORTED && dataSize>1) + { + if (ascendingSort) + { + if ( MLKeyRef<_KeyType>(key) > operator[](1) ) + sortState=ML_UNSORTED; + } + else + { + if ( MLKeyRef<_KeyType>(key) < operator[](1) ) + sortState=ML_UNSORTED; + } + } + } + } + + template + _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PopOpposite(const char *file, unsigned int line) + { + RakAssert(IsEmpty()==false); + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + // Copy leftmost to end + ReallocateIfNeeded(file,line); + data[dataSize]=data[0]; + DeleteShiftArrayLeft(0); + --dataSize; + // Assuming still leaves at least one element past the end of the list allocated + DeallocateIfNeeded(file,line); + // Return end + return data[dataSize+1]; + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + // Deallocate first, since we are returning off the existing list + DeallocateIfNeeded(file,line); + dataSize--; + + if (queueTail==0) + queueTail=allocationSize-1; + else + --queueTail; + + return data[queueTail]; + } + } + + template + _DataType &Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::PeekOpposite(void) const + { + RakAssert(IsEmpty()==false); + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + return data[0]; + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + _IndexType priorIndex; + if (queueTail==0) + priorIndex=allocationSize-1; + else + priorIndex=queueTail-1; + + return data[priorIndex]; + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertAtIndex(const _DataType &d, _IndexType index, const char *file, unsigned int line) + { + ReallocateIfNeeded(file,line); + + if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + if (index>=dataSize) + { + // insert at end + data[dataSize]=d; + + dataSize++; + } + else + { + // insert at index + InsertShiftArrayRight(d,index); + } + } + else + { + data[queueTail++] = d; + + if ( queueTail == allocationSize ) + queueTail = 0; + + ++dataSize; + + if (dataSize==1) + return; + + _IndexType writeIndex, readIndex, trueWriteIndex, trueReadIndex; + writeIndex=dataSize-1; + readIndex=writeIndex-1; + while (readIndex >= index) + { + if ( queueHead + writeIndex >= allocationSize ) + trueWriteIndex = queueHead + writeIndex - allocationSize; + else + trueWriteIndex = queueHead + writeIndex; + + if ( queueHead + readIndex >= allocationSize ) + trueReadIndex = queueHead + readIndex - allocationSize; + else + trueReadIndex = queueHead + readIndex; + + data[trueWriteIndex]=data[trueReadIndex]; + + if (readIndex==0) + break; + writeIndex--; + readIndex--; + } + + if ( queueHead + index >= allocationSize ) + trueWriteIndex = queueHead + index - allocationSize; + else + trueWriteIndex = queueHead + index; + + data[trueWriteIndex]=d; + } + + if (_MultilistType!=ML_ORDERED_LIST) + sortState=ML_UNSORTED; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtIndex(_IndexType position, const char *file, unsigned int line) + { + RakAssert(position < dataSize); + RakAssert(IsEmpty()==false); + + if (GetMultilistType()==ML_UNORDERED_LIST) + { + // Copy tail to current + data[position]=data[dataSize-1]; + } + else if (GetMultilistType()==ML_STACK || GetMultilistType()==ML_ORDERED_LIST) + { + DeleteShiftArrayLeft(position); + } + else + { + RakAssert(GetMultilistType()==ML_QUEUE); + + _IndexType index, next; + + if ( queueHead + position >= allocationSize ) + index = queueHead + position - allocationSize; + else + index = queueHead + position; + + next = index + 1; + + if ( next == allocationSize ) + next = 0; + + while ( next != queueTail ) + { + // Overwrite the previous element + data[ index ] = data[ next ]; + index = next; + //next = (next + 1) % allocationSize; + + if ( ++next == allocationSize ) + next = 0; + } + + // Move the queueTail back + if ( queueTail == 0 ) + queueTail = allocationSize - 1; + else + --queueTail; + } + + + dataSize--; + DeallocateIfNeeded(file,line); + } + + template + bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::RemoveAtKey(_KeyType key, bool assertIfDoesNotExist, const char *file, unsigned int line) + { + _IndexType index = GetIndexOf(key); + if (index==(_IndexType)-1) + { + RakAssert(assertIfDoesNotExist==false && "RemoveAtKey element not found"); + return false; + } + RemoveAtIndex(index,file,line); + return true; + } + + template + _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexOf(_KeyType key) const + { + _IndexType i; + if (IsSorted()) + { + bool objectExists; + i=GetIndexFromKeyInSortedList(key, &objectExists); + if (objectExists) + return i; + return (_IndexType)-1; + } + else if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) + { + for (i=0; i < dataSize; i++) + { + if (MLKeyRef<_KeyType>(key)==data[i]) + return i; + } + return (_IndexType)-1; + } + else + { + RakAssert( GetMultilistType()==ML_QUEUE ); + + for (i=0; i < dataSize; i++) + { + if (MLKeyRef<_KeyType>(key)==operator[](i)) + return i; + } + return (_IndexType)-1; + } + } + + template + _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetInsertionIndex(_KeyType key) const + { + _IndexType i; + if (IsSorted()) + { + bool objectExists; + i=GetIndexFromKeyInSortedList(key, &objectExists); + if (objectExists) + return (_IndexType)-1; + return i; + } + else if (GetMultilistType()==ML_UNORDERED_LIST || GetMultilistType()==ML_STACK) + { + for (i=0; i < dataSize; i++) + { + if (MLKeyRef<_KeyType>(key)==data[i]) + return (_IndexType)-1; + } + return dataSize; + } + else + { + RakAssert( GetMultilistType()==ML_QUEUE ); + + for (i=0; i < dataSize; i++) + { + if (MLKeyRef<_KeyType>(key)==operator[](i)) + return (_IndexType)-1; + } + return dataSize; + } + } + + template + _DataType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetPtr(_KeyType key) const + { + _IndexType i = GetIndexOf(key); + if (i==(_IndexType)-1) + return 0; + return data[i]; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item, const char *file, unsigned int line), const char *file, unsigned int line) + { + _IndexType i; + for (i=0; i < dataSize; i++) + func(operator[](i), file, line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ForEach(void (*func)(_DataType &item)) + { + _IndexType i; + for (i=0; i < dataSize; i++) + func(operator[](i)); + } + + template + bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsEmpty(void) const + { + return dataSize==0; + } + + template + _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSize(void) const + { + return dataSize; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Clear( bool deallocateSmallBlocks, const char *file, unsigned int line ) + { + dataSize=0; + if (GetMultilistType()==ML_ORDERED_LIST) + if (ascendingSort) + sortState=ML_SORTED_ASCENDING; + else + sortState=ML_SORTED_DESCENDING; + else + sortState=ML_UNSORTED; + queueHead=0; + queueTail=0; + + if (deallocateSmallBlocks && allocationSize < 128 && data) + { + RakNet::OP_DELETE_ARRAY(data,file,line); + data=0; + allocationSize=0; + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ClearPointers( bool deallocateSmallBlocks, const char *file, unsigned int line ) + { + _IndexType i; + for (i=0; i < dataSize; i++) + RakNet::OP_DELETE(operator[](i), file, line); + Clear(deallocateSmallBlocks, file, line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ClearPointer( _KeyType key, const char *file, unsigned int line ) + { + _IndexType i; + i = GetIndexOf(key); + if (i!=-1) + { + RakNet::OP_DELETE(operator[](i), file, line); + RemoveAtIndex(i); + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseList(void) + { + if (IsSorted()) + ascendingSort=!ascendingSort; + + ReverseListInternal(); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Reallocate(_IndexType size, const char *file, unsigned int line) + { + _IndexType newAllocationSize; + if (size < dataSize) + newAllocationSize=dataSize; + else + newAllocationSize=size; + preallocationSize=size; + ReallocToSize(newAllocationSize,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::Sort(bool force) + { + if (IsSorted() && force==false) + return; + + if (dataSize>1) + { + if (ascendingSort) + QSortAscending(0,dataSize-1); + else + QSortDescending(0,dataSize-1); + } + + TagSorted(); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::TagSorted(void) + { + if (ascendingSort) + sortState=ML_SORTED_ASCENDING; + else + sortState=ML_SORTED_DESCENDING; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortAscending(_IndexType leftEdge, _IndexType rightEdge) + { + _DataType temp; + _IndexType left=leftEdge; + _IndexType right=rightEdge; + _IndexType pivotIndex=left++; + + while (left data[pivotIndex]) + { + --left; + + data[pivotIndex]=data[left]; + data[left]=temp; + } + else + { + data[pivotIndex]=data[left]; + data[left]=temp; + + --left; + } + + if (left!=leftEdge) + QSortAscending(leftEdge, left); + + if (right!=rightEdge) + QSortAscending(right, rightEdge); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::QSortDescending(_IndexType leftEdge, _IndexType rightEdge) + { + _DataType temp; + _IndexType left=leftEdge; + _IndexType right=rightEdge; + _IndexType pivotIndex=left++; + + while (left= data[pivotIndex]) + { + ++left; + } + else + { + temp=data[left]; + data[left]=data[right]; + data[right]=temp; + --right; + } + } + + temp=data[pivotIndex]; + + // Move pivot to center + if (data[left] < data[pivotIndex]) + { + --left; + + data[pivotIndex]=data[left]; + data[left]=temp; + } + else + { + data[pivotIndex]=data[left]; + data[left]=temp; + + --left; + } + + if (left!=leftEdge) + QSortDescending(leftEdge, left); + + if (right!=rightEdge) + QSortDescending(right, rightEdge); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetSortOrder(bool ascending) + { + if (ascendingSort!=ascending && IsSorted()) + { + ascendingSort=ascending; + // List is sorted, and the sort order has changed. So reverse the list + ReverseListInternal(); + } + else + ascendingSort=ascending; + } + + template + bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetSortOrder(void) const + { + return ascendingSort; + } + + template + bool Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::IsSorted(void) const + { + return GetMultilistType()==ML_ORDERED_LIST || sortState!=ML_UNSORTED; + } + + template + MultilistType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetMultilistType(void) const + { + if (_MultilistType==ML_VARIABLE_DURING_RUNTIME) + return variableMultilistType; + return _MultilistType; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::SetMultilistType(MultilistType newType) + { + RakAssert(_MultilistType==ML_VARIABLE_DURING_RUNTIME); + switch (variableMultilistType) + { + case ML_UNORDERED_LIST: + switch (newType) + { + case ML_UNORDERED_LIST: + // No change + break; + case ML_STACK: + // Same data format + break; + case ML_QUEUE: + queueHead=0; + queueTail=dataSize; + break; + case ML_ORDERED_LIST: + Sort(false); + break; + } + break; + case ML_STACK: + switch (newType) + { + case ML_UNORDERED_LIST: + // Same data format + break; + case ML_STACK: + // No change + break; + case ML_QUEUE: + queueHead=0; + queueTail=dataSize; + break; + case ML_ORDERED_LIST: + Sort(false); + break; + } + break; + case ML_QUEUE: + switch (newType) + { + case ML_UNORDERED_LIST: + case ML_STACK: + case ML_ORDERED_LIST: + if (queueTail < queueHead) + { + // Realign data if wrapped + ReallocToSize(dataSize, _FILE_AND_LINE_); + } + else + { + // Else can just copy starting at head + _IndexType i; + for (i=0; i < dataSize; i++) + data[i]=operator[](i); + } + if (newType==ML_ORDERED_LIST) + Sort(false); + break; + case ML_QUEUE: + // No change + break; + } + break; + case ML_ORDERED_LIST: + switch (newType) + { + case ML_UNORDERED_LIST: + case ML_STACK: + case ML_QUEUE: + // Same data format + // Tag as sorted + if (ascendingSort) + sortState=ML_SORTED_ASCENDING; + else + sortState=ML_SORTED_DESCENDING; + if (newType==ML_QUEUE) + { + queueHead=0; + queueTail=dataSize; + } + break; + case ML_ORDERED_LIST: + // No change + break; + } + break; + } + + variableMultilistType=newType; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::FindIntersection( + Multilist& source1, + Multilist& source2, + Multilist& intersection, + Multilist& uniqueToSource1, + Multilist& uniqueToSource2) + { + _IndexType index1=0, index2=0; + source1.SetSortOrder(true); + source2.SetSortOrder(true); + source1.Sort(false); + source2.Sort(false); + intersection.Clear(true,_FILE_AND_LINE_); + uniqueToSource1.Clear(true,_FILE_AND_LINE_); + uniqueToSource2.Clear(true,_FILE_AND_LINE_); + + while (index1 < source1.GetSize() && index2 < source2.GetSize()) + { + if (source1[index1] + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocateIfNeeded(const char *file, unsigned int line) + { + if (dataSize65536) + newAllocationSize=allocationSize+65536; + else + { + newAllocationSize=allocationSize<<1; // * 2 + // Protect against underflow + if (newAllocationSize < allocationSize) + newAllocationSize=allocationSize+65536; + } + + ReallocToSize(newAllocationSize,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeallocateIfNeeded(const char *file, unsigned int line) + { + if (allocationSize<512) + return; + if (dataSize >= allocationSize/3 ) + return; + if (dataSize <= preallocationSize ) + return; + + _IndexType newAllocationSize = dataSize<<1; // * 2 + + ReallocToSize(newAllocationSize,file,line); + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReallocToSize(_IndexType newAllocationSize, const char *file, unsigned int line) + { + _DataType* newData = RakNet::OP_NEW_ARRAY<_DataType>(newAllocationSize,file,line); + _IndexType i; + for (i=0; i < dataSize; i++) + newData[i]=operator[](i); + if (dataSize>0) + { + RakNet::OP_DELETE_ARRAY(data,file,line); + if (GetMultilistType()==ML_QUEUE) + { + queueHead=0; + queueTail=dataSize; + } + } + data=newData; + allocationSize=newAllocationSize; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::ReverseListInternal(void) + { + _DataType temp; + _IndexType i; + for (i=0; i < dataSize/2; i++) + { + temp=operator[](i); + operator[](i)=operator[](dataSize-1-i); + operator[](dataSize-1-i)=temp; + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertInOrderedList(const _DataType &d, const _KeyType &key) + { + RakAssert(GetMultilistType()==ML_ORDERED_LIST); + + bool objectExists; + _IndexType index; + index = GetIndexFromKeyInSortedList(key, &objectExists); + + // if (objectExists) + // { + // Ordered list only allows unique insertions + // RakAssert("Duplicate insertion into ordered list" && false); + // return; + // } + + if (index>=dataSize) + { + // insert at end + data[dataSize]=d; + dataSize++; + } + else + { + // insert at index + InsertShiftArrayRight(d,index); + } + } + + template + _IndexType Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::GetIndexFromKeyInSortedList(const _KeyType &key, bool *objectExists) const + { + RakAssert(IsSorted()); + _IndexType index, upperBound, lowerBound; + + if (dataSize==0) + { + *objectExists=false; + return 0; + } + + upperBound=dataSize-1; + lowerBound=0; + index = dataSize/2; + +#ifdef _MSC_VER + #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + if (MLKeyRef<_KeyType>(key) > operator[](index) ) + { + if (ascendingSort) + lowerBound=index+1; + else + upperBound=index-1; + } + else if (MLKeyRef<_KeyType>(key) < operator[](index) ) + { + if (ascendingSort) + upperBound=index-1; + else + lowerBound=index+1; + } + else + { + // == + *objectExists=true; + return index; + } + + index=lowerBound+(upperBound-lowerBound)/2; + + if (lowerBound>upperBound || upperBound==(_IndexType)-1) + { + *objectExists=false; + return lowerBound; // No match + } + } + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::InsertShiftArrayRight(const _DataType &d, _IndexType index) + { + RakAssert(_MultilistType!=ML_QUEUE); + + // Move the elements in the list to make room + _IndexType i; + for ( i = dataSize; i != index; i-- ) + data[ i ] = data[ i - 1 ]; + + // Insert the new item at the correct spot + data[ index ] = d; + + ++dataSize; + } + + template + void Multilist<_MultilistType, _DataType, _KeyType, _IndexType>::DeleteShiftArrayLeft( _IndexType index ) + { + RakAssert(index < dataSize); + RakAssert(_MultilistType!=ML_QUEUE); + + _IndexType i; + for ( i = index; i < dataSize-1; i++ ) + data[i]=data[i+1]; + } +}; + +/* +struct KeyAndValue +{ + int key; + short value; +}; + +DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS(KeyAndValue,int,key) + +void MultilistUnitTest(void) +{ + DataStructures::DefaultIndexType oldSize; + DataStructures::Multilist ml1; + ml1.Reallocate(64); + RakAssert(ml1.IsEmpty()); + ml1.Push(53); + RakAssert(ml1.Peek()==53); + RakAssert(ml1.IsEmpty()==false); + RakAssert(ml1.Pop()==53); + RakAssert(ml1.IsEmpty()==true); + for (int i=0; i < 512; i++) + ml1.Push(i); + RakAssert(ml1.GetIndexOf(200)==200); + RakAssert(ml1.PeekOpposite()==0); + RakAssert(ml1.PopOpposite()==0); + RakAssert(ml1.PeekOpposite()==1); + RakAssert(ml1.Peek()==511); + ml1.ReverseList(); + for (int i=0; i < 511; i++) + RakAssert(ml1[i]==511-i); + RakAssert(ml1.PeekOpposite()==511); + RakAssert(ml1.Peek()==1); + oldSize = ml1.GetSize(); + ml1.RemoveAtIndex(0); + RakAssert(ml1.GetSize()==oldSize-1); + RakAssert(ml1.PeekOpposite()==1); + ml1.Clear(_FILE_AND_LINE_); + RakAssert(ml1.IsEmpty()==true); + + ml1.Sort(true); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(100); + ml1.Sort(true); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(50); + ml1.Push(100); + ml1.Sort(true); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(100); + ml1.Push(50); + ml1.Sort(true); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(100); + ml1.Push(50); + ml1.Push(150); + ml1.Push(25); + ml1.Push(175); + ml1.Sort(true); + RakAssert(ml1[0]==25); + RakAssert(ml1[1]==50); + RakAssert(ml1[2]==100); + RakAssert(ml1[3]==150); + RakAssert(ml1[4]==175); + RakAssert(ml1.GetIndexOf(25)==0); + RakAssert(ml1.GetIndexOf(50)==1); + RakAssert(ml1.GetIndexOf(100)==2); + RakAssert(ml1.GetIndexOf(150)==3); + RakAssert(ml1.GetIndexOf(175)==4); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(1); + ml1.Push(2); + ml1.Push(3); + ml1.Push(4); + ml1.Push(5); + ml1.Sort(true); + RakAssert(ml1[0]==1); + RakAssert(ml1[1]==2); + RakAssert(ml1[2]==3); + RakAssert(ml1[3]==4); + RakAssert(ml1[4]==5); + RakAssert(ml1.GetIndexOf(1)==0); + RakAssert(ml1.GetIndexOf(2)==1); + RakAssert(ml1.GetIndexOf(3)==2); + RakAssert(ml1.GetIndexOf(4)==3); + RakAssert(ml1.GetIndexOf(5)==4); + ml1.Clear(_FILE_AND_LINE_); + + ml1.Push(5); + ml1.Push(4); + ml1.Push(3); + ml1.Push(2); + ml1.Push(1); + ml1.Sort(true); + RakAssert(ml1[0]==1); + RakAssert(ml1[1]==2); + RakAssert(ml1[2]==3); + RakAssert(ml1[3]==4); + RakAssert(ml1[4]==5); + RakAssert(ml1.GetIndexOf(1)==0); + RakAssert(ml1.GetIndexOf(2)==1); + RakAssert(ml1.GetIndexOf(3)==2); + RakAssert(ml1.GetIndexOf(4)==3); + RakAssert(ml1.GetIndexOf(5)==4); + ml1.Sort(true); + RakAssert(ml1[0]==1); + RakAssert(ml1[1]==2); + RakAssert(ml1[2]==3); + RakAssert(ml1[3]==4); + RakAssert(ml1[4]==5); + RakAssert(ml1.GetIndexOf(1)==0); + RakAssert(ml1.GetIndexOf(2)==1); + RakAssert(ml1.GetIndexOf(3)==2); + RakAssert(ml1.GetIndexOf(4)==3); + RakAssert(ml1.GetIndexOf(5)==4); + ml1.Clear(_FILE_AND_LINE_); + + DataStructures::Multilist ml2; + ml2.Reallocate(64); + RakAssert(ml2.IsEmpty()); + ml2.Push(53); + RakAssert(ml2.Peek()==53); + RakAssert(ml2.IsEmpty()==false); + RakAssert(ml2.Pop()==53); + RakAssert(ml2.IsEmpty()==true); + for (int i=0; i < 512; i++) + ml2.Push(i); + RakAssert(ml2.GetIndexOf(200)==200); + RakAssert(ml2.PeekOpposite()==0); + RakAssert(ml2.PopOpposite()==0); + RakAssert(ml2.PeekOpposite()==1); + RakAssert(ml2.Peek()==511); + ml2.ReverseList(); + for (int i=0; i < 511; i++) + RakAssert(ml2[i]==511-i); + RakAssert(ml2.PeekOpposite()==511); + RakAssert(ml2.Peek()==1); + oldSize = ml2.GetSize(); + ml2.RemoveAtIndex(0); + RakAssert(ml2.GetSize()==oldSize-1); + RakAssert(ml2.Peek()==1); + RakAssert(ml2.PeekOpposite()==510); + ml2.Clear(_FILE_AND_LINE_); + RakAssert(ml2.IsEmpty()==true); + + DataStructures::Multilist ml3; + RakAssert(ml3.IsEmpty()); + ml3.Push(53); + RakAssert(ml3.Peek()==53); + RakAssert(ml3.IsEmpty()==false); + RakAssert(ml3.Pop()==53); + RakAssert(ml3.IsEmpty()==true); + for (int i=0; i < 512; i++) + ml3.Push(i); + RakAssert(ml3.GetIndexOf(200)==200); + RakAssert(ml3.PeekOpposite()==511); + RakAssert(ml3.PopOpposite()==511); + RakAssert(ml3.PeekOpposite()==510); + RakAssert(ml3.Peek()==0); + ml3.ReverseList(); + for (int i=0; i < 511; i++) + RakAssert(ml3[i]==511-1-i); + RakAssert(ml3.PeekOpposite()==0); + RakAssert(ml3.Peek()==510); + oldSize = ml3.GetSize(); + ml3.RemoveAtIndex(0); + RakAssert(ml3.GetSize()==oldSize-1); + RakAssert(ml3.Peek()==509); + RakAssert(ml3.PeekOpposite()==0); + ml3.Clear(_FILE_AND_LINE_); + RakAssert(ml3.IsEmpty()==true); + + ml3.PushOpposite(100); + ml3.PushOpposite(50); + ml3.PushOpposite(150); + ml3.PushOpposite(25); + ml3.PushOpposite(175); + ml3.Sort(true); + RakAssert(ml3[0]==25); + RakAssert(ml3[1]==50); + RakAssert(ml3[2]==100); + RakAssert(ml3[3]==150); + RakAssert(ml3[4]==175); + RakAssert(ml3.GetIndexOf(25)==0); + RakAssert(ml3.GetIndexOf(50)==1); + RakAssert(ml3.GetIndexOf(100)==2); + RakAssert(ml3.GetIndexOf(150)==3); + RakAssert(ml3.GetIndexOf(175)==4); + ml3.Clear(_FILE_AND_LINE_); + + ml3.PushOpposite(1); + ml3.PushOpposite(2); + ml3.PushOpposite(3); + ml3.PushOpposite(4); + ml3.PushOpposite(5); + ml3.Sort(true); + RakAssert(ml3[0]==1); + RakAssert(ml3[1]==2); + RakAssert(ml3[2]==3); + RakAssert(ml3[3]==4); + RakAssert(ml3[4]==5); + RakAssert(ml3.GetIndexOf(1)==0); + RakAssert(ml3.GetIndexOf(2)==1); + RakAssert(ml3.GetIndexOf(3)==2); + RakAssert(ml3.GetIndexOf(4)==3); + RakAssert(ml3.GetIndexOf(5)==4); + ml3.Clear(_FILE_AND_LINE_); + + ml3.PushOpposite(5); + ml3.PushOpposite(4); + ml3.PushOpposite(3); + ml3.PushOpposite(2); + ml3.PushOpposite(1); + ml3.Sort(true); + RakAssert(ml3[0]==1); + RakAssert(ml3[1]==2); + RakAssert(ml3[2]==3); + RakAssert(ml3[3]==4); + RakAssert(ml3[4]==5); + RakAssert(ml3.GetIndexOf(1)==0); + RakAssert(ml3.GetIndexOf(2)==1); + RakAssert(ml3.GetIndexOf(3)==2); + RakAssert(ml3.GetIndexOf(4)==3); + RakAssert(ml3.GetIndexOf(5)==4); + ml3.Sort(true); + RakAssert(ml3[0]==1); + RakAssert(ml3[1]==2); + RakAssert(ml3[2]==3); + RakAssert(ml3[3]==4); + RakAssert(ml3[4]==5); + RakAssert(ml3.GetIndexOf(1)==0); + RakAssert(ml3.GetIndexOf(2)==1); + RakAssert(ml3.GetIndexOf(3)==2); + RakAssert(ml3.GetIndexOf(4)==3); + RakAssert(ml3.GetIndexOf(5)==4); + + ml3.SetSortOrder(false); + ml3.Sort(false); + RakAssert(ml3[0]==5); + RakAssert(ml3[1]==4); + RakAssert(ml3[2]==3); + RakAssert(ml3[3]==2); + RakAssert(ml3[4]==1); + RakAssert(ml3.GetIndexOf(1)==4); + RakAssert(ml3.GetIndexOf(2)==3); + RakAssert(ml3.GetIndexOf(3)==2); + RakAssert(ml3.GetIndexOf(4)==1); + RakAssert(ml3.GetIndexOf(5)==0); + + ml3.Clear(_FILE_AND_LINE_); + + DataStructures::Multilist ml4; + ml4.Reallocate(64); + RakAssert(ml4.IsEmpty()); + ml4.Push(53); + RakAssert(ml4.Peek()==53); + RakAssert(ml4.IsEmpty()==false); + RakAssert(ml4.Pop()==53); + RakAssert(ml4.IsEmpty()==true); + for (int i=0; i < 512; i++) + ml4.Push(i); + RakAssert(ml4.GetIndexOf(200)==200); + RakAssert(ml4.PeekOpposite()==0); + RakAssert(ml4.PopOpposite()==0); + RakAssert(ml4.PeekOpposite()==1); + RakAssert(ml4.Peek()==511); + ml4.ReverseList(); + for (int i=0; i < 511; i++) + RakAssert(ml4[i]==511-i); + RakAssert(ml4.PeekOpposite()==511); + RakAssert(ml4.Peek()==1); + oldSize = ml4.GetSize(); + ml4.RemoveAtIndex(0); + RakAssert(ml4.GetSize()==oldSize-1); + RakAssert(ml4.Peek()==1); + RakAssert(ml4.PeekOpposite()==510); + ml4.Clear(_FILE_AND_LINE_); + RakAssert(ml4.IsEmpty()==true); + + DataStructures::Multilist ml5; + + for (int i=0; i < 16; i++) + { + KeyAndValue *kav = new KeyAndValue; + kav->key=i; + kav->value=i+100; + ml5.Push(kav,kav->key); + } + + RakAssert(ml5.GetIndexOf(0)==0); + RakAssert(ml5.GetIndexOf(5)==5); + RakAssert(ml5.GetIndexOf(15)==15); + RakAssert(ml5.GetIndexOf(16)==-1); + ml5.RemoveAtKey(0,true); + RakAssert(ml5.GetIndexOf(1)==0); + KeyAndValue *iPtr = ml5.GetPtr(5); + RakAssert(iPtr); + RakAssert(iPtr->value=105); + iPtr = ml5.GetPtr(1234); + RakAssert(iPtr==0); + ml5.ForEach(DataStructures::DeletePtr); + + + DataStructures::Multilist ml6; + ml6.Push(2); + ml6.Push(1); + ml6.Push(6); + ml6.Push(3); + RakAssert(ml6.Peek()==3); + ml6.SetMultilistType(ML_STACK); + RakAssert(ml6.Peek()==3); + ml6.SetMultilistType(ML_QUEUE); + RakAssert(ml6.Peek()==2); + ml6.SetMultilistType(ML_ORDERED_LIST); + RakAssert(ml6.Peek()=6); + ml6.SetMultilistType(ML_STACK); + RakAssert(ml6.Peek()==6); + ml6.SetMultilistType(ML_QUEUE); + RakAssert(ml6.Peek()==1); +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif +*/ + +#endif diff --git a/src/raknet/DS_OrderedChannelHeap.h b/src/raknet/DS_OrderedChannelHeap.h index 104391e..71f71c5 100755 --- a/src/raknet/DS_OrderedChannelHeap.h +++ b/src/raknet/DS_OrderedChannelHeap.h @@ -1,244 +1,244 @@ -/// \file DS_OrderedChannelHeap.h -/// \internal -/// \brief Ordered Channel Heap . This is a heap where you add to it on multiple ordered channels, with each channel having a different weight. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAKNET_ORDERED_CHANNEL_HEAP_H -#define __RAKNET_ORDERED_CHANNEL_HEAP_H - -#include "DS_Heap.h" -#include "DS_Map.h" -#include "DS_Queue.h" -#include "Export.h" -#include "RakAssert.h" -#include "Rand.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template > - class RAK_DLL_EXPORT OrderedChannelHeap - { - public: - static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(channel_key_type(),channel_key_type());} - - OrderedChannelHeap(); - ~OrderedChannelHeap(); - void Push(const channel_key_type &channelID, const heap_data_type &data); - void PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data); - heap_data_type Pop(const unsigned startingIndex=0); - heap_data_type Peek(const unsigned startingIndex) const; - void AddChannel(const channel_key_type &channelID, const double weight); - void RemoveChannel(channel_key_type channelID); - void Clear(void); - heap_data_type& operator[] ( const unsigned int position ) const; - unsigned ChannelSize(const channel_key_type &channelID); - unsigned Size(void) const; - - struct QueueAndWeight - { - DataStructures::Queue randResultQueue; - double weight; - bool signalDeletion; - }; - - struct HeapChannelAndData - { - HeapChannelAndData() {} - HeapChannelAndData(const channel_key_type &_channel, const heap_data_type &_data) : data(_data), channel(_channel) {} - heap_data_type data; - channel_key_type channel; - }; - - protected: - DataStructures::Map map; - DataStructures::Heap heap; - void GreatestRandResult(void); - }; - - template - OrderedChannelHeap::OrderedChannelHeap() - { - } - - template - OrderedChannelHeap::~OrderedChannelHeap() - { - Clear(); - } - - template - void OrderedChannelHeap::Push(const channel_key_type &channelID, const heap_data_type &data) - { - PushAtHead(MAX_UNSIGNED_LONG, channelID, data); - } - - template - void OrderedChannelHeap::GreatestRandResult(void) - { - double greatest; - unsigned i; - greatest=0.0; - for (i=0; i < map.Size(); i++) - { - if (map[i]->randResultQueue.Size() && map[i]->randResultQueue[0]>greatest) - greatest=map[i]->randResultQueue[0]; - } - return greatest; - } - - template - void OrderedChannelHeap::PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data) - { - // If an assert hits here then this is an unknown channel. Call AddChannel first. - QueueAndWeight *queueAndWeight=map.Get(channelID); - double maxRange, minRange, rnd; - if (queueAndWeight->randResultQueue.Size()==0) - { - // Set maxRange to the greatest random number waiting to be returned, rather than 1.0 necessarily - // This is so weights are scaled similarly among channels. For example, if the head weight for a used channel was .25 - // and then we added another channel, the new channel would need to choose between .25 and 0 - // If we chose between 1.0 and 0, it would be 1/.25 (4x) more likely to be at the head of the heap than it should be - maxRange=GreatestRandResult(); - if (maxRange==0.0) - maxRange=1.0; - minRange=0.0; - } - else if (index >= queueAndWeight->randResultQueue.Size()) - { - maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; - minRange=0.0; - } - else - { - if (index==0) - { - maxRange=GreatestRandResult(); - if (maxRange==queueAndWeight->randResultQueue[0]) - maxRange=1.0; - } - else if (index >= queueAndWeight->randResultQueue.Size()) - maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; - else - maxRange=queueAndWeight->randResultQueue[index-1]*.99999999; - - minRange=maxRange=queueAndWeight->randResultQueue[index]*1.00000001; - } - -#ifdef _DEBUG - RakAssert(maxRange!=0.0); -#endif - rnd=frandomMT() * (maxRange - minRange); - if (rnd==0.0) - rnd=maxRange/2.0; - - if (index >= queueAndWeight->randResultQueue.Size()) - queueAndWeight->randResultQueue.Push(rnd); - else - queueAndWeight->randResultQueue.PushAtHead(rnd, index); - - heap.Push(rnd*queueAndWeight->weight, HeapChannelAndData(channelID, data)); - } - - template - heap_data_type OrderedChannelHeap::Pop(const unsigned startingIndex) - { - RakAssert(startingIndex < heap.Size()); - - QueueAndWeight *queueAndWeight=map.Get(heap[startingIndex].channel); - if (startingIndex!=0) - { - // Ugly - have to count in the heap how many nodes have the same channel, so we know where to delete from in the queue - unsigned indiceCount=0; - unsigned i; - for (i=0; i < startingIndex; i++) - if (channel_key_comparison_func(heap[i].channel,heap[startingIndex].channel)==0) - indiceCount++; - queueAndWeight->randResultQueue.RemoveAtIndex(indiceCount); - } - else - { - // TODO - ordered channel heap uses progressively lower values as items are inserted. But this won't give relative ordering among channels. I have to renormalize after every pop. - queueAndWeight->randResultQueue.Pop(); - } - - // Try to remove the channel after every pop, because doing so is not valid while there are elements in the list. - if (queueAndWeight->signalDeletion) - RemoveChannel(heap[startingIndex].channel); - - return heap.Pop(startingIndex).data; - } - - template - heap_data_type OrderedChannelHeap::Peek(const unsigned startingIndex) const - { - HeapChannelAndData heapChannelAndData = heap.Peek(startingIndex); - return heapChannelAndData.data; - } - - template - void OrderedChannelHeap::AddChannel(const channel_key_type &channelID, const double weight) - { - QueueAndWeight *qaw = RakNet::OP_NEW( _FILE_AND_LINE_ ); - qaw->weight=weight; - qaw->signalDeletion=false; - map.SetNew(channelID, qaw); - } - - template - void OrderedChannelHeap::RemoveChannel(channel_key_type channelID) - { - if (map.Has(channelID)) - { - unsigned i; - i=map.GetIndexAtKey(channelID); - if (map[i]->randResultQueue.Size()==0) - { - RakNet::OP_DELETE(map[i], _FILE_AND_LINE_); - map.RemoveAtIndex(i); - } - else - { - // Signal this channel for deletion later, because the heap has nodes with this channel right now - map[i]->signalDeletion=true; - } - } - } - - template - unsigned OrderedChannelHeap::Size(void) const - { - return heap.Size(); - } - - template - heap_data_type& OrderedChannelHeap::operator[]( const unsigned int position ) const - { - return heap[position].data; - } - - - template - unsigned OrderedChannelHeap::ChannelSize(const channel_key_type &channelID) - { - QueueAndWeight *queueAndWeight=map.Get(channelID); - return queueAndWeight->randResultQueue.Size(); - } - - template - void OrderedChannelHeap::Clear(void) - { - unsigned i; - for (i=0; i < map.Size(); i++) - RakNet::OP_DELETE(map[i], _FILE_AND_LINE_); - map.Clear(_FILE_AND_LINE_); - heap.Clear(_FILE_AND_LINE_); - } -} - -#endif +/// \file DS_OrderedChannelHeap.h +/// \internal +/// \brief Ordered Channel Heap . This is a heap where you add to it on multiple ordered channels, with each channel having a different weight. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAKNET_ORDERED_CHANNEL_HEAP_H +#define __RAKNET_ORDERED_CHANNEL_HEAP_H + +#include "DS_Heap.h" +#include "DS_Map.h" +#include "DS_Queue.h" +#include "Export.h" +#include "RakAssert.h" +#include "Rand.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template > + class RAK_DLL_EXPORT OrderedChannelHeap + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(channel_key_type(),channel_key_type());} + + OrderedChannelHeap(); + ~OrderedChannelHeap(); + void Push(const channel_key_type &channelID, const heap_data_type &data); + void PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data); + heap_data_type Pop(const unsigned startingIndex=0); + heap_data_type Peek(const unsigned startingIndex) const; + void AddChannel(const channel_key_type &channelID, const double weight); + void RemoveChannel(channel_key_type channelID); + void Clear(void); + heap_data_type& operator[] ( const unsigned int position ) const; + unsigned ChannelSize(const channel_key_type &channelID); + unsigned Size(void) const; + + struct QueueAndWeight + { + DataStructures::Queue randResultQueue; + double weight; + bool signalDeletion; + }; + + struct HeapChannelAndData + { + HeapChannelAndData() {} + HeapChannelAndData(const channel_key_type &_channel, const heap_data_type &_data) : data(_data), channel(_channel) {} + heap_data_type data; + channel_key_type channel; + }; + + protected: + DataStructures::Map map; + DataStructures::Heap heap; + void GreatestRandResult(void); + }; + + template + OrderedChannelHeap::OrderedChannelHeap() + { + } + + template + OrderedChannelHeap::~OrderedChannelHeap() + { + Clear(); + } + + template + void OrderedChannelHeap::Push(const channel_key_type &channelID, const heap_data_type &data) + { + PushAtHead(MAX_UNSIGNED_LONG, channelID, data); + } + + template + void OrderedChannelHeap::GreatestRandResult(void) + { + double greatest; + unsigned i; + greatest=0.0; + for (i=0; i < map.Size(); i++) + { + if (map[i]->randResultQueue.Size() && map[i]->randResultQueue[0]>greatest) + greatest=map[i]->randResultQueue[0]; + } + return greatest; + } + + template + void OrderedChannelHeap::PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data) + { + // If an assert hits here then this is an unknown channel. Call AddChannel first. + QueueAndWeight *queueAndWeight=map.Get(channelID); + double maxRange, minRange, rnd; + if (queueAndWeight->randResultQueue.Size()==0) + { + // Set maxRange to the greatest random number waiting to be returned, rather than 1.0 necessarily + // This is so weights are scaled similarly among channels. For example, if the head weight for a used channel was .25 + // and then we added another channel, the new channel would need to choose between .25 and 0 + // If we chose between 1.0 and 0, it would be 1/.25 (4x) more likely to be at the head of the heap than it should be + maxRange=GreatestRandResult(); + if (maxRange==0.0) + maxRange=1.0; + minRange=0.0; + } + else if (index >= queueAndWeight->randResultQueue.Size()) + { + maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; + minRange=0.0; + } + else + { + if (index==0) + { + maxRange=GreatestRandResult(); + if (maxRange==queueAndWeight->randResultQueue[0]) + maxRange=1.0; + } + else if (index >= queueAndWeight->randResultQueue.Size()) + maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999; + else + maxRange=queueAndWeight->randResultQueue[index-1]*.99999999; + + minRange=maxRange=queueAndWeight->randResultQueue[index]*1.00000001; + } + +#ifdef _DEBUG + RakAssert(maxRange!=0.0); +#endif + rnd=frandomMT() * (maxRange - minRange); + if (rnd==0.0) + rnd=maxRange/2.0; + + if (index >= queueAndWeight->randResultQueue.Size()) + queueAndWeight->randResultQueue.Push(rnd); + else + queueAndWeight->randResultQueue.PushAtHead(rnd, index); + + heap.Push(rnd*queueAndWeight->weight, HeapChannelAndData(channelID, data)); + } + + template + heap_data_type OrderedChannelHeap::Pop(const unsigned startingIndex) + { + RakAssert(startingIndex < heap.Size()); + + QueueAndWeight *queueAndWeight=map.Get(heap[startingIndex].channel); + if (startingIndex!=0) + { + // Ugly - have to count in the heap how many nodes have the same channel, so we know where to delete from in the queue + unsigned indiceCount=0; + unsigned i; + for (i=0; i < startingIndex; i++) + if (channel_key_comparison_func(heap[i].channel,heap[startingIndex].channel)==0) + indiceCount++; + queueAndWeight->randResultQueue.RemoveAtIndex(indiceCount); + } + else + { + // TODO - ordered channel heap uses progressively lower values as items are inserted. But this won't give relative ordering among channels. I have to renormalize after every pop. + queueAndWeight->randResultQueue.Pop(); + } + + // Try to remove the channel after every pop, because doing so is not valid while there are elements in the list. + if (queueAndWeight->signalDeletion) + RemoveChannel(heap[startingIndex].channel); + + return heap.Pop(startingIndex).data; + } + + template + heap_data_type OrderedChannelHeap::Peek(const unsigned startingIndex) const + { + HeapChannelAndData heapChannelAndData = heap.Peek(startingIndex); + return heapChannelAndData.data; + } + + template + void OrderedChannelHeap::AddChannel(const channel_key_type &channelID, const double weight) + { + QueueAndWeight *qaw = RakNet::OP_NEW( _FILE_AND_LINE_ ); + qaw->weight=weight; + qaw->signalDeletion=false; + map.SetNew(channelID, qaw); + } + + template + void OrderedChannelHeap::RemoveChannel(channel_key_type channelID) + { + if (map.Has(channelID)) + { + unsigned i; + i=map.GetIndexAtKey(channelID); + if (map[i]->randResultQueue.Size()==0) + { + RakNet::OP_DELETE(map[i], _FILE_AND_LINE_); + map.RemoveAtIndex(i); + } + else + { + // Signal this channel for deletion later, because the heap has nodes with this channel right now + map[i]->signalDeletion=true; + } + } + } + + template + unsigned OrderedChannelHeap::Size(void) const + { + return heap.Size(); + } + + template + heap_data_type& OrderedChannelHeap::operator[]( const unsigned int position ) const + { + return heap[position].data; + } + + + template + unsigned OrderedChannelHeap::ChannelSize(const channel_key_type &channelID) + { + QueueAndWeight *queueAndWeight=map.Get(channelID); + return queueAndWeight->randResultQueue.Size(); + } + + template + void OrderedChannelHeap::Clear(void) + { + unsigned i; + for (i=0; i < map.Size(); i++) + RakNet::OP_DELETE(map[i], _FILE_AND_LINE_); + map.Clear(_FILE_AND_LINE_); + heap.Clear(_FILE_AND_LINE_); + } +} + +#endif diff --git a/src/raknet/DS_OrderedList.h b/src/raknet/DS_OrderedList.h index efbff60..b979def 100755 --- a/src/raknet/DS_OrderedList.h +++ b/src/raknet/DS_OrderedList.h @@ -1,263 +1,263 @@ -/// \file DS_OrderedList.h -/// \internal -/// \brief Quicksort ordered list. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "DS_List.h" -#include "RakMemoryOverride.h" -#include "Export.h" - -#ifndef __ORDERED_LIST_H -#define __ORDERED_LIST_H - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template - int defaultOrderedListComparison(const key_type &a, const data_type &b) - { - if (a > - class RAK_DLL_EXPORT OrderedList - { - public: - static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultOrderedListComparison(key_type(),data_type());} - - OrderedList(); - ~OrderedList(); - OrderedList( const OrderedList& original_copy ); - OrderedList& operator= ( const OrderedList& original_copy ); - - /// comparisonFunction must take a key_type and a data_type and return <0, ==0, or >0 - /// If the data type has comparison operators already defined then you can just use defaultComparison - bool HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; - // GetIndexFromKey returns where the insert should go at the same time checks if it is there - unsigned GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; - data_type GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; - bool GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; - unsigned Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, int (*cf)(const key_type&, const data_type&)=default_comparison_function); - unsigned Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); - unsigned RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); - data_type& operator[] ( const unsigned int position ) const; - void RemoveAtIndex(const unsigned index); - void InsertAtIndex(const data_type &data, const unsigned index, const char *file, unsigned int line); - void InsertAtEnd(const data_type &data, const char *file, unsigned int line); - void RemoveFromEnd(const unsigned num=1); - void Clear(bool doNotDeallocate, const char *file, unsigned int line); - unsigned Size(void) const; - - protected: - DataStructures::List orderedList; - }; - - template - OrderedList::OrderedList() - { - } - - template - OrderedList::~OrderedList() - { - Clear(false, _FILE_AND_LINE_); - } - - template - OrderedList::OrderedList( const OrderedList& original_copy ) - { - orderedList=original_copy.orderedList; - } - - template - OrderedList& OrderedList::operator= ( const OrderedList& original_copy ) - { - orderedList=original_copy.orderedList; - return *this; - } - - template - bool OrderedList::HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)) const - { - bool objectExists; - GetIndexFromKey(key, &objectExists, cf); - return objectExists; - } - - template - data_type OrderedList::GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)) const - { - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - RakAssert(objectExists); - return orderedList[index]; - } - template - bool OrderedList::GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)) const - { - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - if (objectExists) - element = orderedList[index]; - return objectExists; - } - template - unsigned OrderedList::GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)) const - { - int index, upperBound, lowerBound; - int res; - - if (orderedList.Size()==0) - { - *objectExists=false; - return 0; - } - - upperBound=(int)orderedList.Size()-1; - lowerBound=0; - index = (int)orderedList.Size()/2; - -#ifdef _MSC_VER - #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - res = cf(key,orderedList[index]); - if (res==0) - { - *objectExists=true; - return index; - } - else if (res<0) - { - upperBound=index-1; - } - else// if (res>0) - { - lowerBound=index+1; - } - - index=lowerBound+(upperBound-lowerBound)/2; - - if (lowerBound>upperBound) - { - *objectExists=false; - return lowerBound; // No match - } - } - } - - template - unsigned OrderedList::Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, int (*cf)(const key_type&, const data_type&)) - { - (void) assertOnDuplicate; - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - - // Don't allow duplicate insertion. - if (objectExists) - { - // This is usually a bug! - RakAssert(assertOnDuplicate==false); - return (unsigned)-1; - } - - if (index>=orderedList.Size()) - { - orderedList.Insert(data, file, line); - return orderedList.Size()-1; - } - else - { - orderedList.Insert(data,index, file, line); - return index; - } - } - - template - unsigned OrderedList::Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)) - { - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - - // Can't find the element to remove if this assert hits - // RakAssert(objectExists==true); - if (objectExists==false) - { - RakAssert(objectExists==true); - return 0; - } - - orderedList.RemoveAtIndex(index); - return index; - } - - template - unsigned OrderedList::RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)) - { - bool objectExists; - unsigned index; - index = GetIndexFromKey(key, &objectExists, cf); - - // Can't find the element to remove if this assert hits - if (objectExists==false) - return 0; - - orderedList.RemoveAtIndex(index); - return index; - } - - template - void OrderedList::RemoveAtIndex(const unsigned index) - { - orderedList.RemoveAtIndex(index); - } - - template - void OrderedList::InsertAtIndex(const data_type &data, const unsigned index, const char *file, unsigned int line) - { - orderedList.Insert(data, index, file, line); - } - - template - void OrderedList::InsertAtEnd(const data_type &data, const char *file, unsigned int line) - { - orderedList.Insert(data, file, line); - } - - template - void OrderedList::RemoveFromEnd(const unsigned num) - { - orderedList.RemoveFromEnd(num); - } - - template - void OrderedList::Clear(bool doNotDeallocate, const char *file, unsigned int line) - { - orderedList.Clear(doNotDeallocate, file, line); - } - - template - data_type& OrderedList::operator[]( const unsigned int position ) const - { - return orderedList[position]; - } - - template - unsigned OrderedList::Size(void) const - { - return orderedList.Size(); - } -} - -#endif +/// \file DS_OrderedList.h +/// \internal +/// \brief Quicksort ordered list. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "DS_List.h" +#include "RakMemoryOverride.h" +#include "Export.h" + +#ifndef __ORDERED_LIST_H +#define __ORDERED_LIST_H + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + int defaultOrderedListComparison(const key_type &a, const data_type &b) + { + if (a > + class RAK_DLL_EXPORT OrderedList + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultOrderedListComparison(key_type(),data_type());} + + OrderedList(); + ~OrderedList(); + OrderedList( const OrderedList& original_copy ); + OrderedList& operator= ( const OrderedList& original_copy ); + + /// comparisonFunction must take a key_type and a data_type and return <0, ==0, or >0 + /// If the data type has comparison operators already defined then you can just use defaultComparison + bool HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + // GetIndexFromKey returns where the insert should go at the same time checks if it is there + unsigned GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + data_type GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + bool GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + unsigned Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, int (*cf)(const key_type&, const data_type&)=default_comparison_function); + unsigned Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); + unsigned RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); + data_type& operator[] ( const unsigned int position ) const; + void RemoveAtIndex(const unsigned index); + void InsertAtIndex(const data_type &data, const unsigned index, const char *file, unsigned int line); + void InsertAtEnd(const data_type &data, const char *file, unsigned int line); + void RemoveFromEnd(const unsigned num=1); + void Clear(bool doNotDeallocate, const char *file, unsigned int line); + unsigned Size(void) const; + + protected: + DataStructures::List orderedList; + }; + + template + OrderedList::OrderedList() + { + } + + template + OrderedList::~OrderedList() + { + Clear(false, _FILE_AND_LINE_); + } + + template + OrderedList::OrderedList( const OrderedList& original_copy ) + { + orderedList=original_copy.orderedList; + } + + template + OrderedList& OrderedList::operator= ( const OrderedList& original_copy ) + { + orderedList=original_copy.orderedList; + return *this; + } + + template + bool OrderedList::HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)) const + { + bool objectExists; + GetIndexFromKey(key, &objectExists, cf); + return objectExists; + } + + template + data_type OrderedList::GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)) const + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + RakAssert(objectExists); + return orderedList[index]; + } + template + bool OrderedList::GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)) const + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + if (objectExists) + element = orderedList[index]; + return objectExists; + } + template + unsigned OrderedList::GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)) const + { + int index, upperBound, lowerBound; + int res; + + if (orderedList.Size()==0) + { + *objectExists=false; + return 0; + } + + upperBound=(int)orderedList.Size()-1; + lowerBound=0; + index = (int)orderedList.Size()/2; + +#ifdef _MSC_VER + #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + res = cf(key,orderedList[index]); + if (res==0) + { + *objectExists=true; + return index; + } + else if (res<0) + { + upperBound=index-1; + } + else// if (res>0) + { + lowerBound=index+1; + } + + index=lowerBound+(upperBound-lowerBound)/2; + + if (lowerBound>upperBound) + { + *objectExists=false; + return lowerBound; // No match + } + } + } + + template + unsigned OrderedList::Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, int (*cf)(const key_type&, const data_type&)) + { + (void) assertOnDuplicate; + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + + // Don't allow duplicate insertion. + if (objectExists) + { + // This is usually a bug! + RakAssert(assertOnDuplicate==false); + return (unsigned)-1; + } + + if (index>=orderedList.Size()) + { + orderedList.Insert(data, file, line); + return orderedList.Size()-1; + } + else + { + orderedList.Insert(data,index, file, line); + return index; + } + } + + template + unsigned OrderedList::Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)) + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + + // Can't find the element to remove if this assert hits + // RakAssert(objectExists==true); + if (objectExists==false) + { + RakAssert(objectExists==true); + return 0; + } + + orderedList.RemoveAtIndex(index); + return index; + } + + template + unsigned OrderedList::RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)) + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + + // Can't find the element to remove if this assert hits + if (objectExists==false) + return 0; + + orderedList.RemoveAtIndex(index); + return index; + } + + template + void OrderedList::RemoveAtIndex(const unsigned index) + { + orderedList.RemoveAtIndex(index); + } + + template + void OrderedList::InsertAtIndex(const data_type &data, const unsigned index, const char *file, unsigned int line) + { + orderedList.Insert(data, index, file, line); + } + + template + void OrderedList::InsertAtEnd(const data_type &data, const char *file, unsigned int line) + { + orderedList.Insert(data, file, line); + } + + template + void OrderedList::RemoveFromEnd(const unsigned num) + { + orderedList.RemoveFromEnd(num); + } + + template + void OrderedList::Clear(bool doNotDeallocate, const char *file, unsigned int line) + { + orderedList.Clear(doNotDeallocate, file, line); + } + + template + data_type& OrderedList::operator[]( const unsigned int position ) const + { + return orderedList[position]; + } + + template + unsigned OrderedList::Size(void) const + { + return orderedList.Size(); + } +} + +#endif diff --git a/src/raknet/DS_Queue.h b/src/raknet/DS_Queue.h index 6fd0004..ffa22af 100755 --- a/src/raknet/DS_Queue.h +++ b/src/raknet/DS_Queue.h @@ -1,435 +1,435 @@ -/// \file DS_Queue.h -/// \internal -/// \brief A queue used by RakNet. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __QUEUE_H -#define __QUEUE_H - -// Template classes have to have all the code in the header file -#include "RakAssert.h" -#include "Export.h" -#include "RakMemoryOverride.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// \brief A queue implemented as an array with a read and write index. - template - class RAK_DLL_EXPORT Queue - { - public: - Queue(); - ~Queue(); - Queue( Queue& original_copy ); - bool operator= ( const Queue& original_copy ); - void Push( const queue_type& input, const char *file, unsigned int line ); - void PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line ); - queue_type& operator[] ( unsigned int position ) const; // Not a normal thing you do with a queue but can be used for efficiency - void RemoveAtIndex( unsigned int position ); // Not a normal thing you do with a queue but can be used for efficiency - inline queue_type Peek( void ) const; - inline queue_type PeekTail( void ) const; - inline queue_type Pop( void ); - // Debug: Set pointer to 0, for memory leak detection - inline queue_type PopDeref( void ); - inline unsigned int Size( void ) const; - inline bool IsEmpty(void) const; - inline unsigned int AllocationSize( void ) const; - inline void Clear( const char *file, unsigned int line ); - void Compress( const char *file, unsigned int line ); - bool Find ( queue_type q ); - void ClearAndForceAllocation( int size, const char *file, unsigned int line ); // Force a memory allocation to a certain larger size - - private: - queue_type* array; - unsigned int head; // Array index for the head of the queue - unsigned int tail; // Array index for the tail of the queue - unsigned int allocation_size; - }; - - - template - inline unsigned int Queue::Size( void ) const - { - if ( head <= tail ) - return tail -head; - else - return allocation_size -head + tail; - } - - template - inline bool Queue::IsEmpty(void) const - { - return head==tail; - } - - template - inline unsigned int Queue::AllocationSize( void ) const - { - return allocation_size; - } - - template - Queue::Queue() - { - //allocation_size = 16; - //array = RakNet::OP_NEW_ARRAY(allocation_size, _FILE_AND_LINE_ ); - allocation_size = 0; - array=0; - head = 0; - tail = 0; - } - - template - Queue::~Queue() - { - if (allocation_size>0) - RakNet::OP_DELETE_ARRAY(array, _FILE_AND_LINE_); - } - - template - inline queue_type Queue::Pop( void ) - { -#ifdef _DEBUG - RakAssert( head != tail); -#endif - //head=(head+1) % allocation_size; - - if ( ++head == allocation_size ) - head = 0; - - if ( head == 0 ) - return ( queue_type ) array[ allocation_size -1 ]; - - return ( queue_type ) array[ head -1 ]; - } - - template - inline queue_type Queue::PopDeref( void ) - { - if ( ++head == allocation_size ) - head = 0; - - queue_type q; - if ( head == 0 ) - { - q=array[ allocation_size -1 ]; - array[ allocation_size -1 ]=0; - return q; - } - - q=array[ head -1 ]; - array[ head -1 ]=0; - return q; - } - - template - void Queue::PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line ) - { - RakAssert(index <= Size()); - - // Just force a reallocation, will be overwritten - Push(input, file, line ); - - if (Size()==1) - return; - - unsigned writeIndex, readIndex, trueWriteIndex, trueReadIndex; - writeIndex=Size()-1; - readIndex=writeIndex-1; - while (readIndex >= index) - { - if ( head + writeIndex >= allocation_size ) - trueWriteIndex = head + writeIndex - allocation_size; - else - trueWriteIndex = head + writeIndex; - - if ( head + readIndex >= allocation_size ) - trueReadIndex = head + readIndex - allocation_size; - else - trueReadIndex = head + readIndex; - - array[trueWriteIndex]=array[trueReadIndex]; - - if (readIndex==0) - break; - writeIndex--; - readIndex--; - } - - if ( head + index >= allocation_size ) - trueWriteIndex = head + index - allocation_size; - else - trueWriteIndex = head + index; - - array[trueWriteIndex]=input; - } - - - template - inline queue_type Queue::Peek( void ) const - { -#ifdef _DEBUG - RakAssert( head != tail ); -#endif - - return ( queue_type ) array[ head ]; - } - - template - inline queue_type Queue::PeekTail( void ) const - { -#ifdef _DEBUG - RakAssert( head != tail ); -#endif - if (tail!=0) - return ( queue_type ) array[ tail-1 ]; - else - return ( queue_type ) array[ allocation_size-1 ]; - } - - template - void Queue::Push( const queue_type& input, const char *file, unsigned int line ) - { - if ( allocation_size == 0 ) - { - array = RakNet::OP_NEW_ARRAY(16, file, line ); - head = 0; - tail = 1; - array[ 0 ] = input; - allocation_size = 16; - return ; - } - - array[ tail++ ] = input; - - if ( tail == allocation_size ) - tail = 0; - - if ( tail == head ) - { - // unsigned int index=tail; - - // Need to allocate more memory. - queue_type * new_array; - new_array = RakNet::OP_NEW_ARRAY(allocation_size * 2, file, line ); -#ifdef _DEBUG - RakAssert( new_array ); -#endif - if (new_array==0) - return; - - for ( unsigned int counter = 0; counter < allocation_size; ++counter ) - new_array[ counter ] = array[ ( head + counter ) % ( allocation_size ) ]; - - head = 0; - - tail = allocation_size; - - allocation_size *= 2; - - // Delete the old array and move the pointer to the new array - RakNet::OP_DELETE_ARRAY(array, file, line); - - array = new_array; - } - - } - - template - Queue::Queue( Queue& original_copy ) - { - // Allocate memory for copy - - if ( original_copy.Size() == 0 ) - { - allocation_size = 0; - } - - else - { - array = RakNet::OP_NEW_ARRAY( original_copy.Size() + 1 , _FILE_AND_LINE_ ); - - for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) - array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; - - head = 0; - - tail = original_copy.Size(); - - allocation_size = original_copy.Size() + 1; - } - } - - template - bool Queue::operator= ( const Queue& original_copy ) - { - if ( ( &original_copy ) == this ) - return false; - - Clear(_FILE_AND_LINE_); - - // Allocate memory for copy - if ( original_copy.Size() == 0 ) - { - allocation_size = 0; - } - - else - { - array = RakNet::OP_NEW_ARRAY( original_copy.Size() + 1 , _FILE_AND_LINE_ ); - - for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) - array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; - - head = 0; - - tail = original_copy.Size(); - - allocation_size = original_copy.Size() + 1; - } - - return true; - } - - template - inline void Queue::Clear ( const char *file, unsigned int line ) - { - if ( allocation_size == 0 ) - return ; - - if (allocation_size > 32) - { - RakNet::OP_DELETE_ARRAY(array, file, line); - allocation_size = 0; - } - - head = 0; - tail = 0; - } - - template - void Queue::Compress ( const char *file, unsigned int line ) - { - queue_type* new_array; - unsigned int newAllocationSize; - if (allocation_size==0) - return; - - newAllocationSize=1; - while (newAllocationSize <= Size()) - newAllocationSize<<=1; // Must be a better way to do this but I'm too dumb to figure it out quickly :) - - new_array = RakNet::OP_NEW_ARRAY(newAllocationSize, file, line ); - - for (unsigned int counter=0; counter < Size(); ++counter) - new_array[counter] = array[(head + counter)%(allocation_size)]; - - tail=Size(); - allocation_size=newAllocationSize; - head=0; - - // Delete the old array and move the pointer to the new array - RakNet::OP_DELETE_ARRAY(array, file, line); - array=new_array; - } - - template - bool Queue::Find ( queue_type q ) - { - if ( allocation_size == 0 ) - return false; - - unsigned int counter = head; - - while ( counter != tail ) - { - if ( array[ counter ] == q ) - return true; - - counter = ( counter + 1 ) % allocation_size; - } - - return false; - } - - template - void Queue::ClearAndForceAllocation( int size, const char *file, unsigned int line ) - { - RakNet::OP_DELETE_ARRAY(array, file, line); - if (size>0) - array = RakNet::OP_NEW_ARRAY(size, file, line ); - else - array=0; - allocation_size = size; - head = 0; - tail = 0; - } - - template - inline queue_type& Queue::operator[] ( unsigned int position ) const - { -#ifdef _DEBUG - RakAssert( position < Size() ); -#endif - //return array[(head + position) % allocation_size]; - - if ( head + position >= allocation_size ) - return array[ head + position - allocation_size ]; - else - return array[ head + position ]; - } - - template - void Queue::RemoveAtIndex( unsigned int position ) - { -#ifdef _DEBUG - RakAssert( position < Size() ); - RakAssert( head != tail ); -#endif - - if ( head == tail || position >= Size() ) - return ; - - unsigned int index; - - unsigned int next; - - //index = (head + position) % allocation_size; - if ( head + position >= allocation_size ) - index = head + position - allocation_size; - else - index = head + position; - - //next = (index + 1) % allocation_size; - next = index + 1; - - if ( next == allocation_size ) - next = 0; - - while ( next != tail ) - { - // Overwrite the previous element - array[ index ] = array[ next ]; - index = next; - //next = (next + 1) % allocation_size; - - if ( ++next == allocation_size ) - next = 0; - } - - // Move the tail back - if ( tail == 0 ) - tail = allocation_size - 1; - else - --tail; - } -} // End namespace - -#endif - +/// \file DS_Queue.h +/// \internal +/// \brief A queue used by RakNet. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __QUEUE_H +#define __QUEUE_H + +// Template classes have to have all the code in the header file +#include "RakAssert.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief A queue implemented as an array with a read and write index. + template + class RAK_DLL_EXPORT Queue + { + public: + Queue(); + ~Queue(); + Queue( Queue& original_copy ); + bool operator= ( const Queue& original_copy ); + void Push( const queue_type& input, const char *file, unsigned int line ); + void PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line ); + queue_type& operator[] ( unsigned int position ) const; // Not a normal thing you do with a queue but can be used for efficiency + void RemoveAtIndex( unsigned int position ); // Not a normal thing you do with a queue but can be used for efficiency + inline queue_type Peek( void ) const; + inline queue_type PeekTail( void ) const; + inline queue_type Pop( void ); + // Debug: Set pointer to 0, for memory leak detection + inline queue_type PopDeref( void ); + inline unsigned int Size( void ) const; + inline bool IsEmpty(void) const; + inline unsigned int AllocationSize( void ) const; + inline void Clear( const char *file, unsigned int line ); + void Compress( const char *file, unsigned int line ); + bool Find ( queue_type q ); + void ClearAndForceAllocation( int size, const char *file, unsigned int line ); // Force a memory allocation to a certain larger size + + private: + queue_type* array; + unsigned int head; // Array index for the head of the queue + unsigned int tail; // Array index for the tail of the queue + unsigned int allocation_size; + }; + + + template + inline unsigned int Queue::Size( void ) const + { + if ( head <= tail ) + return tail -head; + else + return allocation_size -head + tail; + } + + template + inline bool Queue::IsEmpty(void) const + { + return head==tail; + } + + template + inline unsigned int Queue::AllocationSize( void ) const + { + return allocation_size; + } + + template + Queue::Queue() + { + //allocation_size = 16; + //array = RakNet::OP_NEW_ARRAY(allocation_size, _FILE_AND_LINE_ ); + allocation_size = 0; + array=0; + head = 0; + tail = 0; + } + + template + Queue::~Queue() + { + if (allocation_size>0) + RakNet::OP_DELETE_ARRAY(array, _FILE_AND_LINE_); + } + + template + inline queue_type Queue::Pop( void ) + { +#ifdef _DEBUG + RakAssert( head != tail); +#endif + //head=(head+1) % allocation_size; + + if ( ++head == allocation_size ) + head = 0; + + if ( head == 0 ) + return ( queue_type ) array[ allocation_size -1 ]; + + return ( queue_type ) array[ head -1 ]; + } + + template + inline queue_type Queue::PopDeref( void ) + { + if ( ++head == allocation_size ) + head = 0; + + queue_type q; + if ( head == 0 ) + { + q=array[ allocation_size -1 ]; + array[ allocation_size -1 ]=0; + return q; + } + + q=array[ head -1 ]; + array[ head -1 ]=0; + return q; + } + + template + void Queue::PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line ) + { + RakAssert(index <= Size()); + + // Just force a reallocation, will be overwritten + Push(input, file, line ); + + if (Size()==1) + return; + + unsigned writeIndex, readIndex, trueWriteIndex, trueReadIndex; + writeIndex=Size()-1; + readIndex=writeIndex-1; + while (readIndex >= index) + { + if ( head + writeIndex >= allocation_size ) + trueWriteIndex = head + writeIndex - allocation_size; + else + trueWriteIndex = head + writeIndex; + + if ( head + readIndex >= allocation_size ) + trueReadIndex = head + readIndex - allocation_size; + else + trueReadIndex = head + readIndex; + + array[trueWriteIndex]=array[trueReadIndex]; + + if (readIndex==0) + break; + writeIndex--; + readIndex--; + } + + if ( head + index >= allocation_size ) + trueWriteIndex = head + index - allocation_size; + else + trueWriteIndex = head + index; + + array[trueWriteIndex]=input; + } + + + template + inline queue_type Queue::Peek( void ) const + { +#ifdef _DEBUG + RakAssert( head != tail ); +#endif + + return ( queue_type ) array[ head ]; + } + + template + inline queue_type Queue::PeekTail( void ) const + { +#ifdef _DEBUG + RakAssert( head != tail ); +#endif + if (tail!=0) + return ( queue_type ) array[ tail-1 ]; + else + return ( queue_type ) array[ allocation_size-1 ]; + } + + template + void Queue::Push( const queue_type& input, const char *file, unsigned int line ) + { + if ( allocation_size == 0 ) + { + array = RakNet::OP_NEW_ARRAY(16, file, line ); + head = 0; + tail = 1; + array[ 0 ] = input; + allocation_size = 16; + return ; + } + + array[ tail++ ] = input; + + if ( tail == allocation_size ) + tail = 0; + + if ( tail == head ) + { + // unsigned int index=tail; + + // Need to allocate more memory. + queue_type * new_array; + new_array = RakNet::OP_NEW_ARRAY(allocation_size * 2, file, line ); +#ifdef _DEBUG + RakAssert( new_array ); +#endif + if (new_array==0) + return; + + for ( unsigned int counter = 0; counter < allocation_size; ++counter ) + new_array[ counter ] = array[ ( head + counter ) % ( allocation_size ) ]; + + head = 0; + + tail = allocation_size; + + allocation_size *= 2; + + // Delete the old array and move the pointer to the new array + RakNet::OP_DELETE_ARRAY(array, file, line); + + array = new_array; + } + + } + + template + Queue::Queue( Queue& original_copy ) + { + // Allocate memory for copy + + if ( original_copy.Size() == 0 ) + { + allocation_size = 0; + } + + else + { + array = RakNet::OP_NEW_ARRAY( original_copy.Size() + 1 , _FILE_AND_LINE_ ); + + for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) + array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; + + head = 0; + + tail = original_copy.Size(); + + allocation_size = original_copy.Size() + 1; + } + } + + template + bool Queue::operator= ( const Queue& original_copy ) + { + if ( ( &original_copy ) == this ) + return false; + + Clear(_FILE_AND_LINE_); + + // Allocate memory for copy + if ( original_copy.Size() == 0 ) + { + allocation_size = 0; + } + + else + { + array = RakNet::OP_NEW_ARRAY( original_copy.Size() + 1 , _FILE_AND_LINE_ ); + + for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) + array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; + + head = 0; + + tail = original_copy.Size(); + + allocation_size = original_copy.Size() + 1; + } + + return true; + } + + template + inline void Queue::Clear ( const char *file, unsigned int line ) + { + if ( allocation_size == 0 ) + return ; + + if (allocation_size > 32) + { + RakNet::OP_DELETE_ARRAY(array, file, line); + allocation_size = 0; + } + + head = 0; + tail = 0; + } + + template + void Queue::Compress ( const char *file, unsigned int line ) + { + queue_type* new_array; + unsigned int newAllocationSize; + if (allocation_size==0) + return; + + newAllocationSize=1; + while (newAllocationSize <= Size()) + newAllocationSize<<=1; // Must be a better way to do this but I'm too dumb to figure it out quickly :) + + new_array = RakNet::OP_NEW_ARRAY(newAllocationSize, file, line ); + + for (unsigned int counter=0; counter < Size(); ++counter) + new_array[counter] = array[(head + counter)%(allocation_size)]; + + tail=Size(); + allocation_size=newAllocationSize; + head=0; + + // Delete the old array and move the pointer to the new array + RakNet::OP_DELETE_ARRAY(array, file, line); + array=new_array; + } + + template + bool Queue::Find ( queue_type q ) + { + if ( allocation_size == 0 ) + return false; + + unsigned int counter = head; + + while ( counter != tail ) + { + if ( array[ counter ] == q ) + return true; + + counter = ( counter + 1 ) % allocation_size; + } + + return false; + } + + template + void Queue::ClearAndForceAllocation( int size, const char *file, unsigned int line ) + { + RakNet::OP_DELETE_ARRAY(array, file, line); + if (size>0) + array = RakNet::OP_NEW_ARRAY(size, file, line ); + else + array=0; + allocation_size = size; + head = 0; + tail = 0; + } + + template + inline queue_type& Queue::operator[] ( unsigned int position ) const + { +#ifdef _DEBUG + RakAssert( position < Size() ); +#endif + //return array[(head + position) % allocation_size]; + + if ( head + position >= allocation_size ) + return array[ head + position - allocation_size ]; + else + return array[ head + position ]; + } + + template + void Queue::RemoveAtIndex( unsigned int position ) + { +#ifdef _DEBUG + RakAssert( position < Size() ); + RakAssert( head != tail ); +#endif + + if ( head == tail || position >= Size() ) + return ; + + unsigned int index; + + unsigned int next; + + //index = (head + position) % allocation_size; + if ( head + position >= allocation_size ) + index = head + position - allocation_size; + else + index = head + position; + + //next = (index + 1) % allocation_size; + next = index + 1; + + if ( next == allocation_size ) + next = 0; + + while ( next != tail ) + { + // Overwrite the previous element + array[ index ] = array[ next ]; + index = next; + //next = (next + 1) % allocation_size; + + if ( ++next == allocation_size ) + next = 0; + } + + // Move the tail back + if ( tail == 0 ) + tail = allocation_size - 1; + else + --tail; + } +} // End namespace + +#endif + diff --git a/src/raknet/DS_QueueLinkedList.h b/src/raknet/DS_QueueLinkedList.h index 0e1d234..4b3499c 100755 --- a/src/raknet/DS_QueueLinkedList.h +++ b/src/raknet/DS_QueueLinkedList.h @@ -1,103 +1,103 @@ -/// \file DS_QueueLinkedList.h -/// \internal -/// \brief A queue implemented as a linked list. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __QUEUE_LINKED_LIST_H -#define __QUEUE_LINKED_LIST_H - -#include "DS_LinkedList.h" -#include "Export.h" -#include "RakMemoryOverride.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// \brief A queue implemented using a linked list. Rarely used. - template - class RAK_DLL_EXPORT QueueLinkedList - { - - public: - QueueLinkedList(); - QueueLinkedList( const QueueLinkedList& original_copy ); - bool operator= ( const QueueLinkedList& original_copy ); - QueueType Pop( void ); - QueueType& Peek( void ); - QueueType& EndPeek( void ); - void Push( const QueueType& input ); - unsigned int Size( void ); - void Clear( void ); - void Compress( void ); - - private: - LinkedList data; - }; - - template - QueueLinkedList::QueueLinkedList() - { - } - - template - inline unsigned int QueueLinkedList::Size() - { - return data.Size(); - } - - template - inline QueueType QueueLinkedList::Pop( void ) - { - data.Beginning(); - return ( QueueType ) data.Pop(); - } - - template - inline QueueType& QueueLinkedList::Peek( void ) - { - data.Beginning(); - return ( QueueType ) data.Peek(); - } - - template - inline QueueType& QueueLinkedList::EndPeek( void ) - { - data.End(); - return ( QueueType ) data.Peek(); - } - - template - void QueueLinkedList::Push( const QueueType& input ) - { - data.End(); - data.Add( input ); - } - - template - QueueLinkedList::QueueLinkedList( const QueueLinkedList& original_copy ) - { - data = original_copy.data; - } - - template - bool QueueLinkedList::operator= ( const QueueLinkedList& original_copy ) - { - if ( ( &original_copy ) == this ) - return false; - - data = original_copy.data; - } - - template - void QueueLinkedList::Clear ( void ) - { - data.Clear(); - } -} // End namespace - -#endif +/// \file DS_QueueLinkedList.h +/// \internal +/// \brief A queue implemented as a linked list. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __QUEUE_LINKED_LIST_H +#define __QUEUE_LINKED_LIST_H + +#include "DS_LinkedList.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief A queue implemented using a linked list. Rarely used. + template + class RAK_DLL_EXPORT QueueLinkedList + { + + public: + QueueLinkedList(); + QueueLinkedList( const QueueLinkedList& original_copy ); + bool operator= ( const QueueLinkedList& original_copy ); + QueueType Pop( void ); + QueueType& Peek( void ); + QueueType& EndPeek( void ); + void Push( const QueueType& input ); + unsigned int Size( void ); + void Clear( void ); + void Compress( void ); + + private: + LinkedList data; + }; + + template + QueueLinkedList::QueueLinkedList() + { + } + + template + inline unsigned int QueueLinkedList::Size() + { + return data.Size(); + } + + template + inline QueueType QueueLinkedList::Pop( void ) + { + data.Beginning(); + return ( QueueType ) data.Pop(); + } + + template + inline QueueType& QueueLinkedList::Peek( void ) + { + data.Beginning(); + return ( QueueType ) data.Peek(); + } + + template + inline QueueType& QueueLinkedList::EndPeek( void ) + { + data.End(); + return ( QueueType ) data.Peek(); + } + + template + void QueueLinkedList::Push( const QueueType& input ) + { + data.End(); + data.Add( input ); + } + + template + QueueLinkedList::QueueLinkedList( const QueueLinkedList& original_copy ) + { + data = original_copy.data; + } + + template + bool QueueLinkedList::operator= ( const QueueLinkedList& original_copy ) + { + if ( ( &original_copy ) == this ) + return false; + + data = original_copy.data; + } + + template + void QueueLinkedList::Clear ( void ) + { + data.Clear(); + } +} // End namespace + +#endif diff --git a/src/raknet/DS_RangeList.h b/src/raknet/DS_RangeList.h index b40b859..4136737 100755 --- a/src/raknet/DS_RangeList.h +++ b/src/raknet/DS_RangeList.h @@ -1,236 +1,236 @@ -/// \file DS_RangeList.h -/// \internal -/// \brief A queue implemented as a linked list. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RANGE_LIST_H -#define __RANGE_LIST_H - -#include "DS_OrderedList.h" -#include "BitStream.h" -#include "RakMemoryOverride.h" -#include "RakAssert.h" - -namespace DataStructures -{ - template - struct RangeNode - { - RangeNode() {} - ~RangeNode() {} - RangeNode(range_type min, range_type max) {minIndex=min; maxIndex=max;} - range_type minIndex; - range_type maxIndex; - }; - - - template - int RangeNodeComp(const range_type &a, const RangeNode &b) - { - if (a - class RAK_DLL_EXPORT RangeList - { - public: - RangeList(); - ~RangeList(); - void Insert(range_type index); - void Clear(void); - unsigned Size(void) const; - unsigned RangeSum(void) const; - RakNet::BitSize_t Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized); - bool Deserialize(RakNet::BitStream *out); - - DataStructures::OrderedList , RangeNodeComp > ranges; - }; - - template - RakNet::BitSize_t RangeList::Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized) - { - RakAssert(ranges.Size() < (unsigned short)-1); - RakNet::BitStream tempBS; - RakNet::BitSize_t bitsWritten; - unsigned short countWritten; - unsigned i; - countWritten=0; - bitsWritten=0; - for (i=0; i < ranges.Size(); i++) - { - if ((int)sizeof(unsigned short)*8+bitsWritten+(int)sizeof(range_type)*8*2+1>maxBits) - break; - unsigned char minEqualsMax; - if (ranges[i].minIndex==ranges[i].maxIndex) - minEqualsMax=1; - else - minEqualsMax=0; - tempBS.Write(minEqualsMax); // Use one byte, intead of one bit, for speed, as this is done a lot - tempBS.Write(ranges[i].minIndex); - bitsWritten+=sizeof(range_type)*8+8; - if (ranges[i].minIndex!=ranges[i].maxIndex) - { - tempBS.Write(ranges[i].maxIndex); - bitsWritten+=sizeof(range_type)*8; - } - countWritten++; - } - - in->AlignWriteToByteBoundary(); - RakNet::BitSize_t before=in->GetWriteOffset(); - in->Write(countWritten); - bitsWritten+=in->GetWriteOffset()-before; - // RAKNET_DEBUG_PRINTF("%i ", in->GetNumberOfBitsUsed()); - in->Write(&tempBS, tempBS.GetNumberOfBitsUsed()); - // RAKNET_DEBUG_PRINTF("%i %i \n", tempBS.GetNumberOfBitsUsed(),in->GetNumberOfBitsUsed()); - - if (clearSerialized && countWritten) - { - unsigned rangeSize=ranges.Size(); - for (i=0; i < rangeSize-countWritten; i++) - { - ranges[i]=ranges[i+countWritten]; - } - ranges.RemoveFromEnd(countWritten); - } - - return bitsWritten; - } - template - bool RangeList::Deserialize(RakNet::BitStream *out) - { - ranges.Clear(true, _FILE_AND_LINE_); - unsigned short count; - out->AlignReadToByteBoundary(); - out->Read(count); - unsigned short i; - range_type min,max; - unsigned char maxEqualToMin=0; - - for (i=0; i < count; i++) - { - out->Read(maxEqualToMin); - if (out->Read(min)==false) - return false; - if (maxEqualToMin==false) - { - if (out->Read(max)==false) - return false; - if (max(min,max), _FILE_AND_LINE_); - } - return true; - } - - template - RangeList::RangeList() - { - RangeNodeComp(0, RangeNode()); - } - - template - RangeList::~RangeList() - { - Clear(); - } - - template - void RangeList::Insert(range_type index) - { - if (ranges.Size()==0) - { - ranges.Insert(index, RangeNode(index, index), true, _FILE_AND_LINE_); - return; - } - - bool objectExists; - unsigned insertionIndex=ranges.GetIndexFromKey(index, &objectExists); - if (insertionIndex==ranges.Size()) - { - if (index == ranges[insertionIndex-1].maxIndex+(range_type)1) - ranges[insertionIndex-1].maxIndex++; - else if (index > ranges[insertionIndex-1].maxIndex+(range_type)1) - { - // Insert at end - ranges.Insert(index, RangeNode(index, index), true, _FILE_AND_LINE_); - } - - return; - } - - if (index < ranges[insertionIndex].minIndex-(range_type)1) - { - // Insert here - ranges.InsertAtIndex(RangeNode(index, index), insertionIndex, _FILE_AND_LINE_); - - return; - } - else if (index == ranges[insertionIndex].minIndex-(range_type)1) - { - // Decrease minIndex and join left - ranges[insertionIndex].minIndex--; - if (insertionIndex>0 && ranges[insertionIndex-1].maxIndex+(range_type)1==ranges[insertionIndex].minIndex) - { - ranges[insertionIndex-1].maxIndex=ranges[insertionIndex].maxIndex; - ranges.RemoveAtIndex(insertionIndex); - } - - return; - } - else if (index >= ranges[insertionIndex].minIndex && index <= ranges[insertionIndex].maxIndex) - { - // Already exists - return; - } - else if (index == ranges[insertionIndex].maxIndex+(range_type)1) - { - // Increase maxIndex and join right - ranges[insertionIndex].maxIndex++; - if (insertionIndex - void RangeList::Clear(void) - { - ranges.Clear(true, _FILE_AND_LINE_); - } - - template - unsigned RangeList::Size(void) const - { - return ranges.Size(); - } - - template - unsigned RangeList::RangeSum(void) const - { - unsigned sum=0,i; - for (i=0; i < ranges.Size(); i++) - sum+=ranges[i].maxIndex-ranges[i].minIndex+1; - return sum; - } - -} - -#endif +/// \file DS_RangeList.h +/// \internal +/// \brief A queue implemented as a linked list. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RANGE_LIST_H +#define __RANGE_LIST_H + +#include "DS_OrderedList.h" +#include "BitStream.h" +#include "RakMemoryOverride.h" +#include "RakAssert.h" + +namespace DataStructures +{ + template + struct RangeNode + { + RangeNode() {} + ~RangeNode() {} + RangeNode(range_type min, range_type max) {minIndex=min; maxIndex=max;} + range_type minIndex; + range_type maxIndex; + }; + + + template + int RangeNodeComp(const range_type &a, const RangeNode &b) + { + if (a + class RAK_DLL_EXPORT RangeList + { + public: + RangeList(); + ~RangeList(); + void Insert(range_type index); + void Clear(void); + unsigned Size(void) const; + unsigned RangeSum(void) const; + RakNet::BitSize_t Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized); + bool Deserialize(RakNet::BitStream *out); + + DataStructures::OrderedList , RangeNodeComp > ranges; + }; + + template + RakNet::BitSize_t RangeList::Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized) + { + RakAssert(ranges.Size() < (unsigned short)-1); + RakNet::BitStream tempBS; + RakNet::BitSize_t bitsWritten; + unsigned short countWritten; + unsigned i; + countWritten=0; + bitsWritten=0; + for (i=0; i < ranges.Size(); i++) + { + if ((int)sizeof(unsigned short)*8+bitsWritten+(int)sizeof(range_type)*8*2+1>maxBits) + break; + unsigned char minEqualsMax; + if (ranges[i].minIndex==ranges[i].maxIndex) + minEqualsMax=1; + else + minEqualsMax=0; + tempBS.Write(minEqualsMax); // Use one byte, intead of one bit, for speed, as this is done a lot + tempBS.Write(ranges[i].minIndex); + bitsWritten+=sizeof(range_type)*8+8; + if (ranges[i].minIndex!=ranges[i].maxIndex) + { + tempBS.Write(ranges[i].maxIndex); + bitsWritten+=sizeof(range_type)*8; + } + countWritten++; + } + + in->AlignWriteToByteBoundary(); + RakNet::BitSize_t before=in->GetWriteOffset(); + in->Write(countWritten); + bitsWritten+=in->GetWriteOffset()-before; + // RAKNET_DEBUG_PRINTF("%i ", in->GetNumberOfBitsUsed()); + in->Write(&tempBS, tempBS.GetNumberOfBitsUsed()); + // RAKNET_DEBUG_PRINTF("%i %i \n", tempBS.GetNumberOfBitsUsed(),in->GetNumberOfBitsUsed()); + + if (clearSerialized && countWritten) + { + unsigned rangeSize=ranges.Size(); + for (i=0; i < rangeSize-countWritten; i++) + { + ranges[i]=ranges[i+countWritten]; + } + ranges.RemoveFromEnd(countWritten); + } + + return bitsWritten; + } + template + bool RangeList::Deserialize(RakNet::BitStream *out) + { + ranges.Clear(true, _FILE_AND_LINE_); + unsigned short count; + out->AlignReadToByteBoundary(); + out->Read(count); + unsigned short i; + range_type min,max; + unsigned char maxEqualToMin=0; + + for (i=0; i < count; i++) + { + out->Read(maxEqualToMin); + if (out->Read(min)==false) + return false; + if (maxEqualToMin==false) + { + if (out->Read(max)==false) + return false; + if (max(min,max), _FILE_AND_LINE_); + } + return true; + } + + template + RangeList::RangeList() + { + RangeNodeComp(0, RangeNode()); + } + + template + RangeList::~RangeList() + { + Clear(); + } + + template + void RangeList::Insert(range_type index) + { + if (ranges.Size()==0) + { + ranges.Insert(index, RangeNode(index, index), true, _FILE_AND_LINE_); + return; + } + + bool objectExists; + unsigned insertionIndex=ranges.GetIndexFromKey(index, &objectExists); + if (insertionIndex==ranges.Size()) + { + if (index == ranges[insertionIndex-1].maxIndex+(range_type)1) + ranges[insertionIndex-1].maxIndex++; + else if (index > ranges[insertionIndex-1].maxIndex+(range_type)1) + { + // Insert at end + ranges.Insert(index, RangeNode(index, index), true, _FILE_AND_LINE_); + } + + return; + } + + if (index < ranges[insertionIndex].minIndex-(range_type)1) + { + // Insert here + ranges.InsertAtIndex(RangeNode(index, index), insertionIndex, _FILE_AND_LINE_); + + return; + } + else if (index == ranges[insertionIndex].minIndex-(range_type)1) + { + // Decrease minIndex and join left + ranges[insertionIndex].minIndex--; + if (insertionIndex>0 && ranges[insertionIndex-1].maxIndex+(range_type)1==ranges[insertionIndex].minIndex) + { + ranges[insertionIndex-1].maxIndex=ranges[insertionIndex].maxIndex; + ranges.RemoveAtIndex(insertionIndex); + } + + return; + } + else if (index >= ranges[insertionIndex].minIndex && index <= ranges[insertionIndex].maxIndex) + { + // Already exists + return; + } + else if (index == ranges[insertionIndex].maxIndex+(range_type)1) + { + // Increase maxIndex and join right + ranges[insertionIndex].maxIndex++; + if (insertionIndex + void RangeList::Clear(void) + { + ranges.Clear(true, _FILE_AND_LINE_); + } + + template + unsigned RangeList::Size(void) const + { + return ranges.Size(); + } + + template + unsigned RangeList::RangeSum(void) const + { + unsigned sum=0,i; + for (i=0; i < ranges.Size(); i++) + sum+=ranges[i].maxIndex-ranges[i].minIndex+1; + return sum; + } + +} + +#endif diff --git a/src/raknet/DS_Table.cpp b/src/raknet/DS_Table.cpp index 281a60f..0e35893 100755 --- a/src/raknet/DS_Table.cpp +++ b/src/raknet/DS_Table.cpp @@ -1,1118 +1,1118 @@ -#include "DS_Table.h" -#include "DS_OrderedList.h" -#include -#include "RakAssert.h" -#include "RakAssert.h" -#include "Itoa.h" - -using namespace DataStructures; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -void ExtendRows(Table::Row* input, int index) -{ - (void) index; - input->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); -} -void FreeRow(Table::Row* input, int index) -{ - (void) index; - - unsigned i; - for (i=0; i < input->cells.Size(); i++) - { - RakNet::OP_DELETE(input->cells[i], _FILE_AND_LINE_); - } - RakNet::OP_DELETE(input, _FILE_AND_LINE_); -} -Table::Cell::Cell() -{ - isEmpty=true; - c=0; - ptr=0; - i=0.0; -} -Table::Cell::~Cell() -{ - Clear(); -} -Table::Cell& Table::Cell::operator = ( const Table::Cell& input ) -{ - isEmpty=input.isEmpty; - i=input.i; - ptr=input.ptr; - if (c) - rakFree_Ex(c, _FILE_AND_LINE_); - if (input.c) - { - c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); - memcpy(c, input.c, (int) i); - } - else - c=0; - return *this; -} -Table::Cell::Cell( const Table::Cell & input) -{ - isEmpty=input.isEmpty; - i=input.i; - ptr=input.ptr; - if (input.c) - { - if (c) - rakFree_Ex(c, _FILE_AND_LINE_); - c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); - memcpy(c, input.c, (int) i); - } -} -void Table::Cell::Set(double input) -{ - Clear(); - i=input; - c=0; - ptr=0; - isEmpty=false; -} -void Table::Cell::Set(unsigned int input) -{ - Set((int) input); -} -void Table::Cell::Set(int input) -{ - Clear(); - i=(double) input; - c=0; - ptr=0; - isEmpty=false; -} - -void Table::Cell::Set(const char *input) -{ - Clear(); - - if (input) - { - i=(int)strlen(input)+1; - c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); - strcpy(c, input); - } - else - { - c=0; - i=0; - } - ptr=0; - isEmpty=false; -} -void Table::Cell::Set(const char *input, int inputLength) -{ - Clear(); - if (input) - { - c = (char*) rakMalloc_Ex( inputLength, _FILE_AND_LINE_ ); - i=inputLength; - memcpy(c, input, inputLength); - } - else - { - c=0; - i=0; - } - ptr=0; - isEmpty=false; -} -void Table::Cell::SetPtr(void* p) -{ - Clear(); - c=0; - ptr=p; - isEmpty=false; -} -void Table::Cell::Get(int *output) -{ - RakAssert(isEmpty==false); - int o = (int) i; - *output=o; -} -void Table::Cell::Get(double *output) -{ - RakAssert(isEmpty==false); - *output=i; -} -void Table::Cell::Get(char *output) -{ - RakAssert(isEmpty==false); - strcpy(output, c); -} -void Table::Cell::Get(char *output, int *outputLength) -{ - RakAssert(isEmpty==false); - memcpy(output, c, (int) i); - if (outputLength) - *outputLength=(int) i; -} -RakNet::RakString Table::Cell::ToString(ColumnType columnType) -{ - if (isEmpty) - return RakNet::RakString(); - - if (columnType==NUMERIC) - { - return RakNet::RakString("%f", i); - } - else if (columnType==STRING) - { - return RakNet::RakString(c); - } - else if (columnType==BINARY) - { - return RakNet::RakString(""); - } - else if (columnType==POINTER) - { - return RakNet::RakString("%p", ptr); - } - - return RakNet::RakString(); -} -Table::Cell::Cell(double numericValue, char *charValue, void *ptr, ColumnType type) -{ - SetByType(numericValue,charValue,ptr,type); -} -void Table::Cell::SetByType(double numericValue, char *charValue, void *ptr, ColumnType type) -{ - isEmpty=true; - if (type==NUMERIC) - { - Set(numericValue); - } - else if (type==STRING) - { - Set(charValue); - } - else if (type==BINARY) - { - Set(charValue, (int) numericValue); - } - else if (type==POINTER) - { - SetPtr(ptr); - } - else - { - ptr=(void*) charValue; - } -} -Table::ColumnType Table::Cell::EstimateColumnType(void) const -{ - if (c) - if (i!=0.0f) - return BINARY; - else - return STRING; - if (ptr) - return POINTER; - return NUMERIC; -} -void Table::Cell::Clear(void) -{ - if (isEmpty==false && c) - { - rakFree_Ex(c, _FILE_AND_LINE_); - c=0; - } - isEmpty=true; -} -Table::ColumnDescriptor::ColumnDescriptor() -{ - -} -Table::ColumnDescriptor::~ColumnDescriptor() -{ - -} -Table::ColumnDescriptor::ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType ct) -{ - columnType=ct; - strcpy(columnName, cn); -} -void Table::Row::UpdateCell(unsigned columnIndex, double value) -{ - cells[columnIndex]->Clear(); - cells[columnIndex]->Set(value); - -// cells[columnIndex]->i=value; -// cells[columnIndex]->c=0; -// cells[columnIndex]->isEmpty=false; -} -void Table::Row::UpdateCell(unsigned columnIndex, const char *str) -{ - cells[columnIndex]->Clear(); - cells[columnIndex]->Set(str); -} -void Table::Row::UpdateCell(unsigned columnIndex, int byteLength, const char *data) -{ - cells[columnIndex]->Clear(); - cells[columnIndex]->Set(data,byteLength); -} -Table::Table() -{ -} -Table::~Table() -{ - Clear(); -} -unsigned Table::AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType) -{ - if (columnName[0]==0) - return (unsigned) -1; - - // Add this column. - columns.Insert(Table::ColumnDescriptor(columnName, columnType), _FILE_AND_LINE_); - - // Extend the rows by one - rows.ForEachData(ExtendRows); - - return columns.Size()-1; -} -void Table::RemoveColumn(unsigned columnIndex) -{ - if (columnIndex >= columns.Size()) - return; - - columns.RemoveAtIndex(columnIndex); - - // Remove this index from each row. - int i; - DataStructures::Page *cur = rows.GetListHead(); - while (cur) - { - for (i=0; i < cur->size; i++) - { - RakNet::OP_DELETE(cur->data[i]->cells[columnIndex], _FILE_AND_LINE_); - cur->data[i]->cells.RemoveAtIndex(columnIndex); - } - - cur=cur->next; - } -} -unsigned Table::ColumnIndex(const char *columnName) const -{ - unsigned columnIndex; - for (columnIndex=0; columnIndex= columns.Size()) - return 0; - else - return (char*)columns[index].columnName; -} -Table::ColumnType Table::GetColumnType(unsigned index) const -{ - if (index >= columns.Size()) - return (Table::ColumnType) 0; - else - return columns[index].columnType; -} -unsigned Table::GetColumnCount(void) const -{ - return columns.Size(); -} -unsigned Table::GetRowCount(void) const -{ - return rows.Size(); -} -Table::Row* Table::AddRow(unsigned rowId) -{ - Row *newRow; - newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); - if (rows.Insert(rowId, newRow)==false) - { - RakNet::OP_DELETE(newRow, _FILE_AND_LINE_); - return 0; // Already exists - } - unsigned rowIndex; - for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) - newRow->cells.Insert( RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); - return newRow; -} -Table::Row* Table::AddRow(unsigned rowId, DataStructures::List &initialCellValues) -{ - Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); - unsigned rowIndex; - for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) - { - if (rowIndex < initialCellValues.Size() && initialCellValues[rowIndex].isEmpty==false) - { - Table::Cell *c; - c = RakNet::OP_NEW(_FILE_AND_LINE_); - c->SetByType(initialCellValues[rowIndex].i,initialCellValues[rowIndex].c,initialCellValues[rowIndex].ptr,columns[rowIndex].columnType); - newRow->cells.Insert(c, _FILE_AND_LINE_ ); - } - else - newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); - } - rows.Insert(rowId, newRow); - return newRow; -} -Table::Row* Table::AddRow(unsigned rowId, DataStructures::List &initialCellValues, bool copyCells) -{ - Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); - unsigned rowIndex; - for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) - { - if (rowIndex < initialCellValues.Size() && initialCellValues[rowIndex] && initialCellValues[rowIndex]->isEmpty==false) - { - if (copyCells==false) - newRow->cells.Insert(RakNet::OP_NEW_4( _FILE_AND_LINE_, initialCellValues[rowIndex]->i, initialCellValues[rowIndex]->c, initialCellValues[rowIndex]->ptr, columns[rowIndex].columnType), _FILE_AND_LINE_); - else - { - Table::Cell *c = RakNet::OP_NEW( _FILE_AND_LINE_ ); - newRow->cells.Insert(c, _FILE_AND_LINE_); - *c=*(initialCellValues[rowIndex]); - } - } - else - newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_); - } - rows.Insert(rowId, newRow); - return newRow; -} -Table::Row* Table::AddRowColumns(unsigned rowId, Row *row, DataStructures::List columnIndices) -{ - Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); - unsigned columnIndex; - for (columnIndex=0; columnIndex < columnIndices.Size(); columnIndex++) - { - if (row->cells[columnIndices[columnIndex]]->isEmpty==false) - { - newRow->cells.Insert(RakNet::OP_NEW_4( _FILE_AND_LINE_, - row->cells[columnIndices[columnIndex]]->i, - row->cells[columnIndices[columnIndex]]->c, - row->cells[columnIndices[columnIndex]]->ptr, - columns[columnIndex].columnType - ), _FILE_AND_LINE_); - } - else - { - newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_); - } - } - rows.Insert(rowId, newRow); - return newRow; -} -bool Table::RemoveRow(unsigned rowId) -{ - Row *out; - if (rows.Delete(rowId, out)) - { - DeleteRow(out); - return true; - } - return false; -} -void Table::RemoveRows(Table *tableContainingRowIDs) -{ - unsigned i; - DataStructures::Page *cur = tableContainingRowIDs->GetRows().GetListHead(); - while (cur) - { - for (i=0; i < (unsigned)cur->size; i++) - { - rows.Delete(cur->keys[i]); - } - cur=cur->next; - } - return; -} -bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int value) -{ - RakAssert(columns[columnIndex].columnType==NUMERIC); - - Row *row = GetRowByID(rowId); - if (row) - { - row->UpdateCell(columnIndex, value); - return true; - } - return false; -} -bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, char *str) -{ - RakAssert(columns[columnIndex].columnType==STRING); - - Row *row = GetRowByID(rowId); - if (row) - { - row->UpdateCell(columnIndex, str); - return true; - } - return false; -} -bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data) -{ - RakAssert(columns[columnIndex].columnType==BINARY); - - Row *row = GetRowByID(rowId); - if (row) - { - row->UpdateCell(columnIndex, byteLength, data); - return true; - } - return false; -} -bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value) -{ - RakAssert(columns[columnIndex].columnType==NUMERIC); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->UpdateCell(columnIndex, value); - return true; - } - return false; -} -bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str) -{ - RakAssert(columns[columnIndex].columnType==STRING); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->UpdateCell(columnIndex, str); - return true; - } - return false; -} -bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data) -{ - RakAssert(columns[columnIndex].columnType==BINARY); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->UpdateCell(columnIndex, byteLength, data); - return true; - } - return false; -} -void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output) -{ - RakAssert(columns[columnIndex].columnType==NUMERIC); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->cells[columnIndex]->Get(output); - } -} -void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output) -{ - RakAssert(columns[columnIndex].columnType==STRING); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->cells[columnIndex]->Get(output); - } -} -void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength) -{ - RakAssert(columns[columnIndex].columnType==BINARY); - - Row *row = GetRowByIndex(rowIndex,0); - if (row) - { - row->cells[columnIndex]->Get(output, outputLength); - } -} -Table::FilterQuery::FilterQuery() -{ - columnName[0]=0; -} -Table::FilterQuery::~FilterQuery() -{ - -} -Table::FilterQuery::FilterQuery(unsigned column, Cell *cell, FilterQueryType op) -{ - columnIndex=column; - cellValue=cell; - operation=op; -} -Table::Row* Table::GetRowByID(unsigned rowId) const -{ - Row *row; - if (rows.Get(rowId, row)) - return row; - return 0; -} - -Table::Row* Table::GetRowByIndex(unsigned rowIndex, unsigned *key) const -{ - DataStructures::Page *cur = rows.GetListHead(); - while (cur) - { - if (rowIndex < (unsigned)cur->size) - { - if (key) - *key=cur->keys[rowIndex]; - return cur->data[rowIndex]; - } - if (rowIndex <= (unsigned)cur->size) - rowIndex-=cur->size; - else - return 0; - cur=cur->next; - } - return 0; -} - -void Table::QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result) -{ - unsigned i; - DataStructures::List columnIndicesToReturn; - - // Clear the result table. - result->Clear(); - - if (columnIndicesSubset && numColumnSubset>0) - { - for (i=0; i < numColumnSubset; i++) - { - if (columnIndicesSubset[i]AddColumn(columns[columnIndicesToReturn[i]].columnName,columns[columnIndicesToReturn[i]].columnType); - } - - // Get the column indices of the filter queries. - DataStructures::List inclusionFilterColumnIndices; - if (inclusionFilters && numInclusionFilters>0) - { - for (i=0; i < numInclusionFilters; i++) - { - if (inclusionFilters[i].columnName[0]) - inclusionFilters[i].columnIndex=ColumnIndex(inclusionFilters[i].columnName); - if (inclusionFilters[i].columnIndex *cur = rows.GetListHead(); - while (cur) - { - for (i=0; i < (unsigned)cur->size; i++) - { - QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, cur->keys[i], cur->data[i], inclusionFilters, result); - } - cur=cur->next; - } - } - else - { - // Specific rows - Row *row; - for (i=0; i < numRowIDs; i++) - { - if (rows.Get(rowIds[i], row)) - { - QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, rowIds[i], row, inclusionFilters, result); - } - } - } -} - -void Table::QueryRow(DataStructures::List &inclusionFilterColumnIndices, DataStructures::List &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result) -{ - bool pass=false; - unsigned columnIndex; - unsigned j; - - // If no inclusion filters, just add the row - if (inclusionFilterColumnIndices.Size()==0) - { - result->AddRowColumns(key, row, columnIndicesToReturn); - } - else - { - // Go through all inclusion filters. Only add this row if all filters pass. - for (j=0; jcells[columnIndex]->isEmpty==false ) - { - if (columns[inclusionFilterColumnIndices[j]].columnType==STRING && - (row->cells[columnIndex]->c==0 || - inclusionFilters[j].cellValue->c==0) ) - continue; - - switch (inclusionFilters[j].operation) - { - case QF_EQUAL: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)==0; - break; - case BINARY: - pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i && - memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, (int) row->cells[columnIndex]->i)==0; - break; - case POINTER: - pass=row->cells[columnIndex]->ptr==inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_NOT_EQUAL: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i!=inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)!=0; - break; - case BINARY: - pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i && - memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, (int) row->cells[columnIndex]->i)==0; - break; - case POINTER: - pass=row->cells[columnIndex]->ptr!=inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_GREATER_THAN: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i>inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)>0; - break; - case BINARY: - break; - case POINTER: - pass=row->cells[columnIndex]->ptr>inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_GREATER_THAN_EQ: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i>=inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)>=0; - break; - case BINARY: - break; - case POINTER: - pass=row->cells[columnIndex]->ptr>=inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_LESS_THAN: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->ii; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)<0; - break; - case BINARY: - break; - case POINTER: - pass=row->cells[columnIndex]->ptrptr; - break; - } - break; - case QF_LESS_THAN_EQ: - switch(columns[inclusionFilterColumnIndices[j]].columnType) - { - case NUMERIC: - pass=row->cells[columnIndex]->i<=inclusionFilters[j].cellValue->i; - break; - case STRING: - pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)<=0; - break; - case BINARY: - break; - case POINTER: - pass=row->cells[columnIndex]->ptr<=inclusionFilters[j].cellValue->ptr; - break; - } - break; - case QF_IS_EMPTY: - pass=false; - break; - case QF_NOT_EMPTY: - pass=true; - break; - default: - pass=false; - RakAssert(0); - break; - } - } - else - { - if (inclusionFilters[j].operation==QF_IS_EMPTY) - pass=true; - else - pass=false; // No value for this cell - } - - if (pass==false) - break; - } - - if (pass) - { - result->AddRowColumns(key, row, columnIndicesToReturn); - } - } -} - -static Table::SortQuery *_sortQueries; -static unsigned _numSortQueries; -static DataStructures::List *_columnIndices; -static DataStructures::List *_columns; -int RowSort(Table::Row* const &first, Table::Row* const &second) // first is the one inserting, second is the one already there. -{ - unsigned i, columnIndex; - for (i=0; i<_numSortQueries; i++) - { - columnIndex=(*_columnIndices)[i]; - if (columnIndex==(unsigned)-1) - continue; - - if (first->cells[columnIndex]->isEmpty==true && second->cells[columnIndex]->isEmpty==false) - return 1; // Empty cells always go at the end - - if (first->cells[columnIndex]->isEmpty==false && second->cells[columnIndex]->isEmpty==true) - return -1; // Empty cells always go at the end - - if (_sortQueries[i].operation==Table::QS_INCREASING_ORDER) - { - if ((*_columns)[columnIndex].columnType==Table::NUMERIC) - { - if (first->cells[columnIndex]->i>second->cells[columnIndex]->i) - return 1; - if (first->cells[columnIndex]->icells[columnIndex]->i) - return -1; - } - else - { - // String - if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0) - return 1; - if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0) - return -1; - } - } - else - { - if ((*_columns)[columnIndex].columnType==Table::NUMERIC) - { - if (first->cells[columnIndex]->icells[columnIndex]->i) - return 1; - if (first->cells[columnIndex]->i>second->cells[columnIndex]->i) - return -1; - } - else - { - // String - if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0) - return 1; - if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0) - return -1; - } - } - } - - return 0; -} -void Table::SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out) -{ - unsigned i; - unsigned outLength; - DataStructures::List columnIndices; - _sortQueries=sortQueries; - _numSortQueries=numSortQueries; - _columnIndices=&columnIndices; - _columns=&columns; - bool anyValid=false; - - for (i=0; i < numSortQueries; i++) - { - if (sortQueries[i].columnIndex *cur; - cur = rows.GetListHead(); - if (anyValid==false) - { - outLength=0; - while (cur) - { - for (i=0; i < (unsigned)cur->size; i++) - { - out[(outLength)++]=cur->data[i]; - } - cur=cur->next; - } - return; - } - - // Start adding to ordered list. - DataStructures::OrderedList orderedList; - while (cur) - { - for (i=0; i < (unsigned)cur->size; i++) - { - RakAssert(cur->data[i]); - orderedList.Insert(cur->data[i],cur->data[i], true, _FILE_AND_LINE_); - } - cur=cur->next; - } - - outLength=0; - for (i=0; i < orderedList.Size(); i++) - out[(outLength)++]=orderedList[i]; -} -void Table::PrintColumnHeaders(char *out, int outLength, char columnDelineator) const -{ - if (outLength<=0) - return; - if (outLength==1) - { - *out=0; - return; - } - - unsigned i; - out[0]=0; - int len; - for (i=0; i < columns.Size(); i++) - { - if (i!=0) - { - len = (int) strlen(out); - if (len < outLength-1) - sprintf(out+len, "%c", columnDelineator); - else - return; - } - - len = (int) strlen(out); - if (len < outLength-(int) strlen(columns[i].columnName)) - sprintf(out+len, "%s", columns[i].columnName); - else - return; - } -} -void Table::PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const -{ - if (outLength<=0) - return; - if (outLength==1) - { - *out=0; - return; - } - - if (inputRow->cells.Size()!=columns.Size()) - { - strncpy(out, "Cell width does not match column width.\n", outLength); - out[outLength-1]=0; - return; - } - - char buff[512]; - unsigned i; - int len; - out[0]=0; - for (i=0; i < columns.Size(); i++) - { - if (columns[i].columnType==NUMERIC) - { - if (inputRow->cells[i]->isEmpty==false) - { - sprintf(buff, "%f", inputRow->cells[i]->i); - len=(int)strlen(buff); - } - else - len=0; - if (i+1!=columns.Size()) - buff[len++]=columnDelineator; - buff[len]=0; - } - else if (columns[i].columnType==STRING) - { - if (inputRow->cells[i]->isEmpty==false && inputRow->cells[i]->c) - { - strncpy(buff, inputRow->cells[i]->c, 512-2); - buff[512-2]=0; - len=(int)strlen(buff); - } - else - len=0; - if (i+1!=columns.Size()) - buff[len++]=columnDelineator; - buff[len]=0; - } - else if (columns[i].columnType==POINTER) - { - if (inputRow->cells[i]->isEmpty==false && inputRow->cells[i]->ptr) - { - sprintf(buff, "%p", inputRow->cells[i]->ptr); - len=(int)strlen(buff); - } - else - len=0; - if (i+1!=columns.Size()) - buff[len++]=columnDelineator; - buff[len]=0; - } - else - { - if (printDelineatorForBinary) - { - if (i+1!=columns.Size()) - buff[0]=columnDelineator; - buff[1]=0; - } - else - buff[0]=0; - - } - - len=(int)strlen(out); - if (outLength==len+1) - break; - strncpy(out+len, buff, outLength-len); - out[outLength-1]=0; - } -} - -void Table::Clear(void) -{ - rows.ForEachData(FreeRow); - rows.Clear(); - columns.Clear(true, _FILE_AND_LINE_); -} -const List& Table::GetColumns(void) const -{ - return columns; -} -const DataStructures::BPlusTree& Table::GetRows(void) const -{ - return rows; -} -DataStructures::Page * Table::GetListHead(void) -{ - return rows.GetListHead(); -} -unsigned Table::GetAvailableRowId(void) const -{ - bool setKey=false; - unsigned key=0; - int i; - DataStructures::Page *cur = rows.GetListHead(); - - while (cur) - { - for (i=0; i < cur->size; i++) - { - if (setKey==false) - { - key=cur->keys[i]+1; - setKey=true; - } - else - { - if (key!=cur->keys[i]) - return key; - key++; - } - } - - cur=cur->next; - } - return key; -} -void Table::DeleteRow(Table::Row *row) -{ - unsigned rowIndex; - for (rowIndex=0; rowIndex < row->cells.Size(); rowIndex++) - { - RakNet::OP_DELETE(row->cells[rowIndex], _FILE_AND_LINE_); - } - RakNet::OP_DELETE(row, _FILE_AND_LINE_); -} -Table& Table::operator = ( const Table& input ) -{ - Clear(); - - unsigned int i; - for (i=0; i < input.GetColumnCount(); i++) - AddColumn(input.ColumnName(i), input.GetColumnType(i)); - - DataStructures::Page *cur = input.GetRows().GetListHead(); - while (cur) - { - for (i=0; i < (unsigned int) cur->size; i++) - { - AddRow(cur->keys[i], cur->data[i]->cells, false); - } - - cur=cur->next; - } - - return *this; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +#include "DS_Table.h" +#include "DS_OrderedList.h" +#include +#include "RakAssert.h" +#include "RakAssert.h" +#include "Itoa.h" + +using namespace DataStructures; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +void ExtendRows(Table::Row* input, int index) +{ + (void) index; + input->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); +} +void FreeRow(Table::Row* input, int index) +{ + (void) index; + + unsigned i; + for (i=0; i < input->cells.Size(); i++) + { + RakNet::OP_DELETE(input->cells[i], _FILE_AND_LINE_); + } + RakNet::OP_DELETE(input, _FILE_AND_LINE_); +} +Table::Cell::Cell() +{ + isEmpty=true; + c=0; + ptr=0; + i=0.0; +} +Table::Cell::~Cell() +{ + Clear(); +} +Table::Cell& Table::Cell::operator = ( const Table::Cell& input ) +{ + isEmpty=input.isEmpty; + i=input.i; + ptr=input.ptr; + if (c) + rakFree_Ex(c, _FILE_AND_LINE_); + if (input.c) + { + c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); + memcpy(c, input.c, (int) i); + } + else + c=0; + return *this; +} +Table::Cell::Cell( const Table::Cell & input) +{ + isEmpty=input.isEmpty; + i=input.i; + ptr=input.ptr; + if (input.c) + { + if (c) + rakFree_Ex(c, _FILE_AND_LINE_); + c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); + memcpy(c, input.c, (int) i); + } +} +void Table::Cell::Set(double input) +{ + Clear(); + i=input; + c=0; + ptr=0; + isEmpty=false; +} +void Table::Cell::Set(unsigned int input) +{ + Set((int) input); +} +void Table::Cell::Set(int input) +{ + Clear(); + i=(double) input; + c=0; + ptr=0; + isEmpty=false; +} + +void Table::Cell::Set(const char *input) +{ + Clear(); + + if (input) + { + i=(int)strlen(input)+1; + c = (char*) rakMalloc_Ex( (int) i, _FILE_AND_LINE_ ); + strcpy(c, input); + } + else + { + c=0; + i=0; + } + ptr=0; + isEmpty=false; +} +void Table::Cell::Set(const char *input, int inputLength) +{ + Clear(); + if (input) + { + c = (char*) rakMalloc_Ex( inputLength, _FILE_AND_LINE_ ); + i=inputLength; + memcpy(c, input, inputLength); + } + else + { + c=0; + i=0; + } + ptr=0; + isEmpty=false; +} +void Table::Cell::SetPtr(void* p) +{ + Clear(); + c=0; + ptr=p; + isEmpty=false; +} +void Table::Cell::Get(int *output) +{ + RakAssert(isEmpty==false); + int o = (int) i; + *output=o; +} +void Table::Cell::Get(double *output) +{ + RakAssert(isEmpty==false); + *output=i; +} +void Table::Cell::Get(char *output) +{ + RakAssert(isEmpty==false); + strcpy(output, c); +} +void Table::Cell::Get(char *output, int *outputLength) +{ + RakAssert(isEmpty==false); + memcpy(output, c, (int) i); + if (outputLength) + *outputLength=(int) i; +} +RakNet::RakString Table::Cell::ToString(ColumnType columnType) +{ + if (isEmpty) + return RakNet::RakString(); + + if (columnType==NUMERIC) + { + return RakNet::RakString("%f", i); + } + else if (columnType==STRING) + { + return RakNet::RakString(c); + } + else if (columnType==BINARY) + { + return RakNet::RakString(""); + } + else if (columnType==POINTER) + { + return RakNet::RakString("%p", ptr); + } + + return RakNet::RakString(); +} +Table::Cell::Cell(double numericValue, char *charValue, void *ptr, ColumnType type) +{ + SetByType(numericValue,charValue,ptr,type); +} +void Table::Cell::SetByType(double numericValue, char *charValue, void *ptr, ColumnType type) +{ + isEmpty=true; + if (type==NUMERIC) + { + Set(numericValue); + } + else if (type==STRING) + { + Set(charValue); + } + else if (type==BINARY) + { + Set(charValue, (int) numericValue); + } + else if (type==POINTER) + { + SetPtr(ptr); + } + else + { + ptr=(void*) charValue; + } +} +Table::ColumnType Table::Cell::EstimateColumnType(void) const +{ + if (c) + if (i!=0.0f) + return BINARY; + else + return STRING; + if (ptr) + return POINTER; + return NUMERIC; +} +void Table::Cell::Clear(void) +{ + if (isEmpty==false && c) + { + rakFree_Ex(c, _FILE_AND_LINE_); + c=0; + } + isEmpty=true; +} +Table::ColumnDescriptor::ColumnDescriptor() +{ + +} +Table::ColumnDescriptor::~ColumnDescriptor() +{ + +} +Table::ColumnDescriptor::ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType ct) +{ + columnType=ct; + strcpy(columnName, cn); +} +void Table::Row::UpdateCell(unsigned columnIndex, double value) +{ + cells[columnIndex]->Clear(); + cells[columnIndex]->Set(value); + +// cells[columnIndex]->i=value; +// cells[columnIndex]->c=0; +// cells[columnIndex]->isEmpty=false; +} +void Table::Row::UpdateCell(unsigned columnIndex, const char *str) +{ + cells[columnIndex]->Clear(); + cells[columnIndex]->Set(str); +} +void Table::Row::UpdateCell(unsigned columnIndex, int byteLength, const char *data) +{ + cells[columnIndex]->Clear(); + cells[columnIndex]->Set(data,byteLength); +} +Table::Table() +{ +} +Table::~Table() +{ + Clear(); +} +unsigned Table::AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType) +{ + if (columnName[0]==0) + return (unsigned) -1; + + // Add this column. + columns.Insert(Table::ColumnDescriptor(columnName, columnType), _FILE_AND_LINE_); + + // Extend the rows by one + rows.ForEachData(ExtendRows); + + return columns.Size()-1; +} +void Table::RemoveColumn(unsigned columnIndex) +{ + if (columnIndex >= columns.Size()) + return; + + columns.RemoveAtIndex(columnIndex); + + // Remove this index from each row. + int i; + DataStructures::Page *cur = rows.GetListHead(); + while (cur) + { + for (i=0; i < cur->size; i++) + { + RakNet::OP_DELETE(cur->data[i]->cells[columnIndex], _FILE_AND_LINE_); + cur->data[i]->cells.RemoveAtIndex(columnIndex); + } + + cur=cur->next; + } +} +unsigned Table::ColumnIndex(const char *columnName) const +{ + unsigned columnIndex; + for (columnIndex=0; columnIndex= columns.Size()) + return 0; + else + return (char*)columns[index].columnName; +} +Table::ColumnType Table::GetColumnType(unsigned index) const +{ + if (index >= columns.Size()) + return (Table::ColumnType) 0; + else + return columns[index].columnType; +} +unsigned Table::GetColumnCount(void) const +{ + return columns.Size(); +} +unsigned Table::GetRowCount(void) const +{ + return rows.Size(); +} +Table::Row* Table::AddRow(unsigned rowId) +{ + Row *newRow; + newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); + if (rows.Insert(rowId, newRow)==false) + { + RakNet::OP_DELETE(newRow, _FILE_AND_LINE_); + return 0; // Already exists + } + unsigned rowIndex; + for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) + newRow->cells.Insert( RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); + return newRow; +} +Table::Row* Table::AddRow(unsigned rowId, DataStructures::List &initialCellValues) +{ + Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); + unsigned rowIndex; + for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) + { + if (rowIndex < initialCellValues.Size() && initialCellValues[rowIndex].isEmpty==false) + { + Table::Cell *c; + c = RakNet::OP_NEW(_FILE_AND_LINE_); + c->SetByType(initialCellValues[rowIndex].i,initialCellValues[rowIndex].c,initialCellValues[rowIndex].ptr,columns[rowIndex].columnType); + newRow->cells.Insert(c, _FILE_AND_LINE_ ); + } + else + newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_ ); + } + rows.Insert(rowId, newRow); + return newRow; +} +Table::Row* Table::AddRow(unsigned rowId, DataStructures::List &initialCellValues, bool copyCells) +{ + Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); + unsigned rowIndex; + for (rowIndex=0; rowIndex < columns.Size(); rowIndex++) + { + if (rowIndex < initialCellValues.Size() && initialCellValues[rowIndex] && initialCellValues[rowIndex]->isEmpty==false) + { + if (copyCells==false) + newRow->cells.Insert(RakNet::OP_NEW_4( _FILE_AND_LINE_, initialCellValues[rowIndex]->i, initialCellValues[rowIndex]->c, initialCellValues[rowIndex]->ptr, columns[rowIndex].columnType), _FILE_AND_LINE_); + else + { + Table::Cell *c = RakNet::OP_NEW( _FILE_AND_LINE_ ); + newRow->cells.Insert(c, _FILE_AND_LINE_); + *c=*(initialCellValues[rowIndex]); + } + } + else + newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_); + } + rows.Insert(rowId, newRow); + return newRow; +} +Table::Row* Table::AddRowColumns(unsigned rowId, Row *row, DataStructures::List columnIndices) +{ + Row *newRow = RakNet::OP_NEW( _FILE_AND_LINE_ ); + unsigned columnIndex; + for (columnIndex=0; columnIndex < columnIndices.Size(); columnIndex++) + { + if (row->cells[columnIndices[columnIndex]]->isEmpty==false) + { + newRow->cells.Insert(RakNet::OP_NEW_4( _FILE_AND_LINE_, + row->cells[columnIndices[columnIndex]]->i, + row->cells[columnIndices[columnIndex]]->c, + row->cells[columnIndices[columnIndex]]->ptr, + columns[columnIndex].columnType + ), _FILE_AND_LINE_); + } + else + { + newRow->cells.Insert(RakNet::OP_NEW(_FILE_AND_LINE_), _FILE_AND_LINE_); + } + } + rows.Insert(rowId, newRow); + return newRow; +} +bool Table::RemoveRow(unsigned rowId) +{ + Row *out; + if (rows.Delete(rowId, out)) + { + DeleteRow(out); + return true; + } + return false; +} +void Table::RemoveRows(Table *tableContainingRowIDs) +{ + unsigned i; + DataStructures::Page *cur = tableContainingRowIDs->GetRows().GetListHead(); + while (cur) + { + for (i=0; i < (unsigned)cur->size; i++) + { + rows.Delete(cur->keys[i]); + } + cur=cur->next; + } + return; +} +bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int value) +{ + RakAssert(columns[columnIndex].columnType==NUMERIC); + + Row *row = GetRowByID(rowId); + if (row) + { + row->UpdateCell(columnIndex, value); + return true; + } + return false; +} +bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, char *str) +{ + RakAssert(columns[columnIndex].columnType==STRING); + + Row *row = GetRowByID(rowId); + if (row) + { + row->UpdateCell(columnIndex, str); + return true; + } + return false; +} +bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data) +{ + RakAssert(columns[columnIndex].columnType==BINARY); + + Row *row = GetRowByID(rowId); + if (row) + { + row->UpdateCell(columnIndex, byteLength, data); + return true; + } + return false; +} +bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value) +{ + RakAssert(columns[columnIndex].columnType==NUMERIC); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->UpdateCell(columnIndex, value); + return true; + } + return false; +} +bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str) +{ + RakAssert(columns[columnIndex].columnType==STRING); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->UpdateCell(columnIndex, str); + return true; + } + return false; +} +bool Table::UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data) +{ + RakAssert(columns[columnIndex].columnType==BINARY); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->UpdateCell(columnIndex, byteLength, data); + return true; + } + return false; +} +void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output) +{ + RakAssert(columns[columnIndex].columnType==NUMERIC); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->cells[columnIndex]->Get(output); + } +} +void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output) +{ + RakAssert(columns[columnIndex].columnType==STRING); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->cells[columnIndex]->Get(output); + } +} +void Table::GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength) +{ + RakAssert(columns[columnIndex].columnType==BINARY); + + Row *row = GetRowByIndex(rowIndex,0); + if (row) + { + row->cells[columnIndex]->Get(output, outputLength); + } +} +Table::FilterQuery::FilterQuery() +{ + columnName[0]=0; +} +Table::FilterQuery::~FilterQuery() +{ + +} +Table::FilterQuery::FilterQuery(unsigned column, Cell *cell, FilterQueryType op) +{ + columnIndex=column; + cellValue=cell; + operation=op; +} +Table::Row* Table::GetRowByID(unsigned rowId) const +{ + Row *row; + if (rows.Get(rowId, row)) + return row; + return 0; +} + +Table::Row* Table::GetRowByIndex(unsigned rowIndex, unsigned *key) const +{ + DataStructures::Page *cur = rows.GetListHead(); + while (cur) + { + if (rowIndex < (unsigned)cur->size) + { + if (key) + *key=cur->keys[rowIndex]; + return cur->data[rowIndex]; + } + if (rowIndex <= (unsigned)cur->size) + rowIndex-=cur->size; + else + return 0; + cur=cur->next; + } + return 0; +} + +void Table::QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result) +{ + unsigned i; + DataStructures::List columnIndicesToReturn; + + // Clear the result table. + result->Clear(); + + if (columnIndicesSubset && numColumnSubset>0) + { + for (i=0; i < numColumnSubset; i++) + { + if (columnIndicesSubset[i]AddColumn(columns[columnIndicesToReturn[i]].columnName,columns[columnIndicesToReturn[i]].columnType); + } + + // Get the column indices of the filter queries. + DataStructures::List inclusionFilterColumnIndices; + if (inclusionFilters && numInclusionFilters>0) + { + for (i=0; i < numInclusionFilters; i++) + { + if (inclusionFilters[i].columnName[0]) + inclusionFilters[i].columnIndex=ColumnIndex(inclusionFilters[i].columnName); + if (inclusionFilters[i].columnIndex *cur = rows.GetListHead(); + while (cur) + { + for (i=0; i < (unsigned)cur->size; i++) + { + QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, cur->keys[i], cur->data[i], inclusionFilters, result); + } + cur=cur->next; + } + } + else + { + // Specific rows + Row *row; + for (i=0; i < numRowIDs; i++) + { + if (rows.Get(rowIds[i], row)) + { + QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, rowIds[i], row, inclusionFilters, result); + } + } + } +} + +void Table::QueryRow(DataStructures::List &inclusionFilterColumnIndices, DataStructures::List &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result) +{ + bool pass=false; + unsigned columnIndex; + unsigned j; + + // If no inclusion filters, just add the row + if (inclusionFilterColumnIndices.Size()==0) + { + result->AddRowColumns(key, row, columnIndicesToReturn); + } + else + { + // Go through all inclusion filters. Only add this row if all filters pass. + for (j=0; jcells[columnIndex]->isEmpty==false ) + { + if (columns[inclusionFilterColumnIndices[j]].columnType==STRING && + (row->cells[columnIndex]->c==0 || + inclusionFilters[j].cellValue->c==0) ) + continue; + + switch (inclusionFilters[j].operation) + { + case QF_EQUAL: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)==0; + break; + case BINARY: + pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i && + memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, (int) row->cells[columnIndex]->i)==0; + break; + case POINTER: + pass=row->cells[columnIndex]->ptr==inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_NOT_EQUAL: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i!=inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)!=0; + break; + case BINARY: + pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i && + memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, (int) row->cells[columnIndex]->i)==0; + break; + case POINTER: + pass=row->cells[columnIndex]->ptr!=inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_GREATER_THAN: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i>inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)>0; + break; + case BINARY: + break; + case POINTER: + pass=row->cells[columnIndex]->ptr>inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_GREATER_THAN_EQ: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i>=inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)>=0; + break; + case BINARY: + break; + case POINTER: + pass=row->cells[columnIndex]->ptr>=inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_LESS_THAN: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->ii; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)<0; + break; + case BINARY: + break; + case POINTER: + pass=row->cells[columnIndex]->ptrptr; + break; + } + break; + case QF_LESS_THAN_EQ: + switch(columns[inclusionFilterColumnIndices[j]].columnType) + { + case NUMERIC: + pass=row->cells[columnIndex]->i<=inclusionFilters[j].cellValue->i; + break; + case STRING: + pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)<=0; + break; + case BINARY: + break; + case POINTER: + pass=row->cells[columnIndex]->ptr<=inclusionFilters[j].cellValue->ptr; + break; + } + break; + case QF_IS_EMPTY: + pass=false; + break; + case QF_NOT_EMPTY: + pass=true; + break; + default: + pass=false; + RakAssert(0); + break; + } + } + else + { + if (inclusionFilters[j].operation==QF_IS_EMPTY) + pass=true; + else + pass=false; // No value for this cell + } + + if (pass==false) + break; + } + + if (pass) + { + result->AddRowColumns(key, row, columnIndicesToReturn); + } + } +} + +static Table::SortQuery *_sortQueries; +static unsigned _numSortQueries; +static DataStructures::List *_columnIndices; +static DataStructures::List *_columns; +int RowSort(Table::Row* const &first, Table::Row* const &second) // first is the one inserting, second is the one already there. +{ + unsigned i, columnIndex; + for (i=0; i<_numSortQueries; i++) + { + columnIndex=(*_columnIndices)[i]; + if (columnIndex==(unsigned)-1) + continue; + + if (first->cells[columnIndex]->isEmpty==true && second->cells[columnIndex]->isEmpty==false) + return 1; // Empty cells always go at the end + + if (first->cells[columnIndex]->isEmpty==false && second->cells[columnIndex]->isEmpty==true) + return -1; // Empty cells always go at the end + + if (_sortQueries[i].operation==Table::QS_INCREASING_ORDER) + { + if ((*_columns)[columnIndex].columnType==Table::NUMERIC) + { + if (first->cells[columnIndex]->i>second->cells[columnIndex]->i) + return 1; + if (first->cells[columnIndex]->icells[columnIndex]->i) + return -1; + } + else + { + // String + if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0) + return 1; + if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0) + return -1; + } + } + else + { + if ((*_columns)[columnIndex].columnType==Table::NUMERIC) + { + if (first->cells[columnIndex]->icells[columnIndex]->i) + return 1; + if (first->cells[columnIndex]->i>second->cells[columnIndex]->i) + return -1; + } + else + { + // String + if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0) + return 1; + if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0) + return -1; + } + } + } + + return 0; +} +void Table::SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out) +{ + unsigned i; + unsigned outLength; + DataStructures::List columnIndices; + _sortQueries=sortQueries; + _numSortQueries=numSortQueries; + _columnIndices=&columnIndices; + _columns=&columns; + bool anyValid=false; + + for (i=0; i < numSortQueries; i++) + { + if (sortQueries[i].columnIndex *cur; + cur = rows.GetListHead(); + if (anyValid==false) + { + outLength=0; + while (cur) + { + for (i=0; i < (unsigned)cur->size; i++) + { + out[(outLength)++]=cur->data[i]; + } + cur=cur->next; + } + return; + } + + // Start adding to ordered list. + DataStructures::OrderedList orderedList; + while (cur) + { + for (i=0; i < (unsigned)cur->size; i++) + { + RakAssert(cur->data[i]); + orderedList.Insert(cur->data[i],cur->data[i], true, _FILE_AND_LINE_); + } + cur=cur->next; + } + + outLength=0; + for (i=0; i < orderedList.Size(); i++) + out[(outLength)++]=orderedList[i]; +} +void Table::PrintColumnHeaders(char *out, int outLength, char columnDelineator) const +{ + if (outLength<=0) + return; + if (outLength==1) + { + *out=0; + return; + } + + unsigned i; + out[0]=0; + int len; + for (i=0; i < columns.Size(); i++) + { + if (i!=0) + { + len = (int) strlen(out); + if (len < outLength-1) + sprintf(out+len, "%c", columnDelineator); + else + return; + } + + len = (int) strlen(out); + if (len < outLength-(int) strlen(columns[i].columnName)) + sprintf(out+len, "%s", columns[i].columnName); + else + return; + } +} +void Table::PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const +{ + if (outLength<=0) + return; + if (outLength==1) + { + *out=0; + return; + } + + if (inputRow->cells.Size()!=columns.Size()) + { + strncpy(out, "Cell width does not match column width.\n", outLength); + out[outLength-1]=0; + return; + } + + char buff[512]; + unsigned i; + int len; + out[0]=0; + for (i=0; i < columns.Size(); i++) + { + if (columns[i].columnType==NUMERIC) + { + if (inputRow->cells[i]->isEmpty==false) + { + sprintf(buff, "%f", inputRow->cells[i]->i); + len=(int)strlen(buff); + } + else + len=0; + if (i+1!=columns.Size()) + buff[len++]=columnDelineator; + buff[len]=0; + } + else if (columns[i].columnType==STRING) + { + if (inputRow->cells[i]->isEmpty==false && inputRow->cells[i]->c) + { + strncpy(buff, inputRow->cells[i]->c, 512-2); + buff[512-2]=0; + len=(int)strlen(buff); + } + else + len=0; + if (i+1!=columns.Size()) + buff[len++]=columnDelineator; + buff[len]=0; + } + else if (columns[i].columnType==POINTER) + { + if (inputRow->cells[i]->isEmpty==false && inputRow->cells[i]->ptr) + { + sprintf(buff, "%p", inputRow->cells[i]->ptr); + len=(int)strlen(buff); + } + else + len=0; + if (i+1!=columns.Size()) + buff[len++]=columnDelineator; + buff[len]=0; + } + else + { + if (printDelineatorForBinary) + { + if (i+1!=columns.Size()) + buff[0]=columnDelineator; + buff[1]=0; + } + else + buff[0]=0; + + } + + len=(int)strlen(out); + if (outLength==len+1) + break; + strncpy(out+len, buff, outLength-len); + out[outLength-1]=0; + } +} + +void Table::Clear(void) +{ + rows.ForEachData(FreeRow); + rows.Clear(); + columns.Clear(true, _FILE_AND_LINE_); +} +const List& Table::GetColumns(void) const +{ + return columns; +} +const DataStructures::BPlusTree& Table::GetRows(void) const +{ + return rows; +} +DataStructures::Page * Table::GetListHead(void) +{ + return rows.GetListHead(); +} +unsigned Table::GetAvailableRowId(void) const +{ + bool setKey=false; + unsigned key=0; + int i; + DataStructures::Page *cur = rows.GetListHead(); + + while (cur) + { + for (i=0; i < cur->size; i++) + { + if (setKey==false) + { + key=cur->keys[i]+1; + setKey=true; + } + else + { + if (key!=cur->keys[i]) + return key; + key++; + } + } + + cur=cur->next; + } + return key; +} +void Table::DeleteRow(Table::Row *row) +{ + unsigned rowIndex; + for (rowIndex=0; rowIndex < row->cells.Size(); rowIndex++) + { + RakNet::OP_DELETE(row->cells[rowIndex], _FILE_AND_LINE_); + } + RakNet::OP_DELETE(row, _FILE_AND_LINE_); +} +Table& Table::operator = ( const Table& input ) +{ + Clear(); + + unsigned int i; + for (i=0; i < input.GetColumnCount(); i++) + AddColumn(input.ColumnName(i), input.GetColumnType(i)); + + DataStructures::Page *cur = input.GetRows().GetListHead(); + while (cur) + { + for (i=0; i < (unsigned int) cur->size; i++) + { + AddRow(cur->keys[i], cur->data[i]->cells, false); + } + + cur=cur->next; + } + + return *this; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/src/raknet/DS_Table.h b/src/raknet/DS_Table.h index 56d08fb..9699b3e 100755 --- a/src/raknet/DS_Table.h +++ b/src/raknet/DS_Table.h @@ -1,343 +1,343 @@ -/// \file DS_Table.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __TABLE_H -#define __TABLE_H - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -#include "DS_List.h" -#include "DS_BPlusTree.h" -#include "RakMemoryOverride.h" -#include "Export.h" -#include "RakString.h" - -#define _TABLE_BPLUS_TREE_ORDER 16 -#define _TABLE_MAX_COLUMN_NAME_LENGTH 64 - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - - /// \brief Holds a set of columns, a set of rows, and rows times columns cells. - /// \details The table data structure is useful if you want to store a set of structures and perform queries on those structures.
      - /// This is a relatively simple and fast implementation of the types of tables commonly used in databases.
      - /// See TableSerializer to serialize data members of the table.
      - /// See LightweightDatabaseClient and LightweightDatabaseServer to transmit the table over the network. - class RAK_DLL_EXPORT Table - { - public: - - enum ColumnType - { - // Cell::i used - NUMERIC, - - // Cell::c used to hold a null terminated string. - STRING, - - // Cell::c holds data. Cell::i holds data length of c in bytes. - BINARY, - - // Cell::c holds data. Not deallocated. Set manually by assigning ptr. - POINTER, - }; - - - /// Holds the actual data in the table - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT Cell - { - Cell(); - ~Cell(); - Cell(double numericValue, char *charValue, void *ptr, ColumnType type); - void SetByType(double numericValue, char *charValue, void *ptr, ColumnType type); - void Clear(void); - - /// Numeric - void Set(int input); - void Set(unsigned int input); - void Set(double input); - - /// String - void Set(const char *input); - - /// Binary - void Set(const char *input, int inputLength); - - /// Pointer - void SetPtr(void* p); - - /// Numeric - void Get(int *output); - void Get(double *output); - - /// String - void Get(char *output); - - /// Binary - void Get(char *output, int *outputLength); - - RakNet::RakString ToString(ColumnType columnType); - - // assignment operator and copy constructor - Cell& operator = ( const Cell& input ); - Cell( const Cell & input); - - ColumnType EstimateColumnType(void) const; - - bool isEmpty; - double i; - char *c; - void *ptr; - }; - - /// Stores the name and type of the column - /// \internal - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT ColumnDescriptor - { - ColumnDescriptor(); - ~ColumnDescriptor(); - ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH],ColumnType ct); - - char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; - ColumnType columnType; - }; - - /// Stores the list of cells for this row, and a special flag used for internal sorting - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT Row - { - // list of cells - DataStructures::List cells; - - /// Numeric - void UpdateCell(unsigned columnIndex, double value); - - /// String - void UpdateCell(unsigned columnIndex, const char *str); - - /// Binary - void UpdateCell(unsigned columnIndex, int byteLength, const char *data); - }; - - // Operations to perform for cell comparison - enum FilterQueryType - { - QF_EQUAL, - QF_NOT_EQUAL, - QF_GREATER_THAN, - QF_GREATER_THAN_EQ, - QF_LESS_THAN, - QF_LESS_THAN_EQ, - QF_IS_EMPTY, - QF_NOT_EMPTY, - }; - - // Compare the cell value for a row at columnName to the cellValue using operation. - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT FilterQuery - { - FilterQuery(); - ~FilterQuery(); - FilterQuery(unsigned column, Cell *cell, FilterQueryType op); - - // If columnName is specified, columnIndex will be looked up using it. - char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; - unsigned columnIndex; - Cell *cellValue; - FilterQueryType operation; - }; - - /// Increasing or decreasing sort order - enum SortQueryType - { - QS_INCREASING_ORDER, - QS_DECREASING_ORDER, - }; - - // Sort on increasing or decreasing order for a particular column - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct RAK_DLL_EXPORT SortQuery - { - /// The index of the table column we are sorting on - unsigned columnIndex; - - /// See SortQueryType - SortQueryType operation; - }; - - // Constructor - Table(); - - // Destructor - ~Table(); - - /// \brief Adds a column to the table - /// \param[in] columnName The name of the column - /// \param[in] columnType What type of data this column will hold - /// \return The index of the new column - unsigned AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType); - - /// \brief Removes a column by index - /// \param[in] columnIndex The index of the column to remove - void RemoveColumn(unsigned columnIndex); - - /// \brief Gets the index of a column by name - /// \details Column indices are stored in the order they are added. - /// \param[in] columnName The name of the column - /// \return The index of the column, or (unsigned)-1 if no such column - unsigned ColumnIndex(char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]) const; - unsigned ColumnIndex(const char *columnName) const; - - /// \brief Gives the string name of the column at a certain index - /// \param[in] index The index of the column - /// \return The name of the column, or 0 if an invalid index - char* ColumnName(unsigned index) const; - - /// \brief Returns the type of a column, referenced by index - /// \param[in] index The index of the column - /// \return The type of the column - ColumnType GetColumnType(unsigned index) const; - - /// Returns the number of columns - /// \return The number of columns in the table - unsigned GetColumnCount(void) const; - - /// Returns the number of rows - /// \return The number of rows in the table - unsigned GetRowCount(void) const; - - /// \brief Adds a row to the table - /// \details New rows are added with empty values for all cells. However, if you specify initialCelLValues you can specify initial values - /// It's up to you to ensure that the values in the specific cells match the type of data used by that row - /// rowId can be considered the primary key for the row. It is much faster to lookup a row by its rowId than by searching keys. - /// rowId must be unique - /// Rows are stored in sorted order in the table, using rowId as the sort key - /// \param[in] rowId The UNIQUE primary key for the row. This can never be changed. - /// \param[in] initialCellValues Initial values to give the row (optional) - /// \return The newly added row - Table::Row* AddRow(unsigned rowId); - Table::Row* AddRow(unsigned rowId, DataStructures::List &initialCellValues); - Table::Row* AddRow(unsigned rowId, DataStructures::List &initialCellValues, bool copyCells=false); - - /// \brief Removes a row specified by rowId. - /// \param[in] rowId The ID of the row - /// \return true if the row was deleted. False if not. - bool RemoveRow(unsigned rowId); - - /// \brief Removes all the rows with IDs that the specified table also has. - /// \param[in] tableContainingRowIDs The IDs of the rows - void RemoveRows(Table *tableContainingRowIDs); - - /// \brief Updates a particular cell in the table. - /// \note If you are going to update many cells of a particular row, it is more efficient to call GetRow and perform the operations on the row directly. - /// \note Row pointers do not change, so you can also write directly to the rows for more efficiency. - /// \param[in] rowId The ID of the row - /// \param[in] columnIndex The column of the cell - /// \param[in] value The data to set - bool UpdateCell(unsigned rowId, unsigned columnIndex, int value); - bool UpdateCell(unsigned rowId, unsigned columnIndex, char *str); - bool UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data); - bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value); - bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str); - bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data); - - /// \brief Note this is much less efficient to call than GetRow, then working with the cells directly. - /// Numeric, string, binary - void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output); - void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output); - void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength); - - /// \brief Gets a row. More efficient to do this and access Row::cells than to repeatedly call GetCell. - /// You can also update cells in rows from this function. - /// \param[in] rowId The ID of the row - /// \return The desired row, or 0 if no such row. - Row* GetRowByID(unsigned rowId) const; - - /// \brief Gets a row at a specific index. - /// rowIndex should be less than GetRowCount() - /// \param[in] rowIndex The index of the row - /// \param[out] key The ID of the row returned - /// \return The desired row, or 0 if no such row. - Row* GetRowByIndex(unsigned rowIndex, unsigned *key) const; - - /// \brief Queries the table, optionally returning only a subset of columns and rows. - /// \param[in] columnSubset An array of column indices. Only columns in this array are returned. Pass 0 for all columns - /// \param[in] numColumnSubset The number of elements in \a columnSubset - /// \param[in] inclusionFilters An array of FilterQuery. All filters must pass for the row to be returned. - /// \param[in] numInclusionFilters The number of elements in \a inclusionFilters - /// \param[in] rowIds An arrow of row IDs. Only these rows with these IDs are returned. Pass 0 for all rows. - /// \param[in] numRowIDs The number of elements in \a rowIds - /// \param[out] result The result of the query. If no rows are returned, the table will only have columns. - void QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result); - - /// \brief Sorts the table by rows - /// \details You can sort the table in ascending or descending order on one or more columns - /// Columns have precedence in the order they appear in the \a sortQueries array - /// If a row cell on column n has the same value as a a different row on column n, then the row will be compared on column n+1 - /// \param[in] sortQueries A list of SortQuery structures, defining the sorts to perform on the table - /// \param[in] numColumnSubset The number of elements in \a numSortQueries - /// \param[out] out The address of an array of Rows, which will receive the sorted output. The array must be long enough to contain all returned rows, up to GetRowCount() - void SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out); - - /// \brief Frees all memory in the table. - void Clear(void); - - /// \brief Prints out the names of all the columns. - /// \param[out] out A pointer to an array of bytes which will hold the output. - /// \param[in] outLength The size of the \a out array - /// \param[in] columnDelineator What character to print to delineate columns - void PrintColumnHeaders(char *out, int outLength, char columnDelineator) const; - - /// \brief Writes a text representation of the row to \a out. - /// \param[out] out A pointer to an array of bytes which will hold the output. - /// \param[in] outLength The size of the \a out array - /// \param[in] columnDelineator What character to print to delineate columns - /// \param[in] printDelineatorForBinary Binary output is not printed. True to still print the delineator. - /// \param[in] inputRow The row to print - void PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const; - - /// \brief Direct access to make things easier. - const DataStructures::List& GetColumns(void) const; - - /// \brief Direct access to make things easier. - const DataStructures::BPlusTree& GetRows(void) const; - - /// \brief Get the head of a linked list containing all the row data. - DataStructures::Page * GetListHead(void); - - /// \brief Get the first free row id. - /// This could be made more efficient. - unsigned GetAvailableRowId(void) const; - - Table& operator = ( const Table& input ); - - protected: - Table::Row* AddRowColumns(unsigned rowId, Row *row, DataStructures::List columnIndices); - - void DeleteRow(Row *row); - - void QueryRow(DataStructures::List &inclusionFilterColumnIndices, DataStructures::List &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result); - - // 16 is arbitrary and is the order of the BPlus tree. Higher orders are better for searching while lower orders are better for - // Insertions and deletions. - DataStructures::BPlusTree rows; - - // Columns in the table. - DataStructures::List columns; - }; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif +/// \file DS_Table.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __TABLE_H +#define __TABLE_H + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +#include "DS_List.h" +#include "DS_BPlusTree.h" +#include "RakMemoryOverride.h" +#include "Export.h" +#include "RakString.h" + +#define _TABLE_BPLUS_TREE_ORDER 16 +#define _TABLE_MAX_COLUMN_NAME_LENGTH 64 + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + + /// \brief Holds a set of columns, a set of rows, and rows times columns cells. + /// \details The table data structure is useful if you want to store a set of structures and perform queries on those structures.
      + /// This is a relatively simple and fast implementation of the types of tables commonly used in databases.
      + /// See TableSerializer to serialize data members of the table.
      + /// See LightweightDatabaseClient and LightweightDatabaseServer to transmit the table over the network. + class RAK_DLL_EXPORT Table + { + public: + + enum ColumnType + { + // Cell::i used + NUMERIC, + + // Cell::c used to hold a null terminated string. + STRING, + + // Cell::c holds data. Cell::i holds data length of c in bytes. + BINARY, + + // Cell::c holds data. Not deallocated. Set manually by assigning ptr. + POINTER, + }; + + + /// Holds the actual data in the table + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT Cell + { + Cell(); + ~Cell(); + Cell(double numericValue, char *charValue, void *ptr, ColumnType type); + void SetByType(double numericValue, char *charValue, void *ptr, ColumnType type); + void Clear(void); + + /// Numeric + void Set(int input); + void Set(unsigned int input); + void Set(double input); + + /// String + void Set(const char *input); + + /// Binary + void Set(const char *input, int inputLength); + + /// Pointer + void SetPtr(void* p); + + /// Numeric + void Get(int *output); + void Get(double *output); + + /// String + void Get(char *output); + + /// Binary + void Get(char *output, int *outputLength); + + RakNet::RakString ToString(ColumnType columnType); + + // assignment operator and copy constructor + Cell& operator = ( const Cell& input ); + Cell( const Cell & input); + + ColumnType EstimateColumnType(void) const; + + bool isEmpty; + double i; + char *c; + void *ptr; + }; + + /// Stores the name and type of the column + /// \internal + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT ColumnDescriptor + { + ColumnDescriptor(); + ~ColumnDescriptor(); + ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH],ColumnType ct); + + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; + ColumnType columnType; + }; + + /// Stores the list of cells for this row, and a special flag used for internal sorting + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT Row + { + // list of cells + DataStructures::List cells; + + /// Numeric + void UpdateCell(unsigned columnIndex, double value); + + /// String + void UpdateCell(unsigned columnIndex, const char *str); + + /// Binary + void UpdateCell(unsigned columnIndex, int byteLength, const char *data); + }; + + // Operations to perform for cell comparison + enum FilterQueryType + { + QF_EQUAL, + QF_NOT_EQUAL, + QF_GREATER_THAN, + QF_GREATER_THAN_EQ, + QF_LESS_THAN, + QF_LESS_THAN_EQ, + QF_IS_EMPTY, + QF_NOT_EMPTY, + }; + + // Compare the cell value for a row at columnName to the cellValue using operation. + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT FilterQuery + { + FilterQuery(); + ~FilterQuery(); + FilterQuery(unsigned column, Cell *cell, FilterQueryType op); + + // If columnName is specified, columnIndex will be looked up using it. + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; + unsigned columnIndex; + Cell *cellValue; + FilterQueryType operation; + }; + + /// Increasing or decreasing sort order + enum SortQueryType + { + QS_INCREASING_ORDER, + QS_DECREASING_ORDER, + }; + + // Sort on increasing or decreasing order for a particular column + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct RAK_DLL_EXPORT SortQuery + { + /// The index of the table column we are sorting on + unsigned columnIndex; + + /// See SortQueryType + SortQueryType operation; + }; + + // Constructor + Table(); + + // Destructor + ~Table(); + + /// \brief Adds a column to the table + /// \param[in] columnName The name of the column + /// \param[in] columnType What type of data this column will hold + /// \return The index of the new column + unsigned AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType); + + /// \brief Removes a column by index + /// \param[in] columnIndex The index of the column to remove + void RemoveColumn(unsigned columnIndex); + + /// \brief Gets the index of a column by name + /// \details Column indices are stored in the order they are added. + /// \param[in] columnName The name of the column + /// \return The index of the column, or (unsigned)-1 if no such column + unsigned ColumnIndex(char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]) const; + unsigned ColumnIndex(const char *columnName) const; + + /// \brief Gives the string name of the column at a certain index + /// \param[in] index The index of the column + /// \return The name of the column, or 0 if an invalid index + char* ColumnName(unsigned index) const; + + /// \brief Returns the type of a column, referenced by index + /// \param[in] index The index of the column + /// \return The type of the column + ColumnType GetColumnType(unsigned index) const; + + /// Returns the number of columns + /// \return The number of columns in the table + unsigned GetColumnCount(void) const; + + /// Returns the number of rows + /// \return The number of rows in the table + unsigned GetRowCount(void) const; + + /// \brief Adds a row to the table + /// \details New rows are added with empty values for all cells. However, if you specify initialCelLValues you can specify initial values + /// It's up to you to ensure that the values in the specific cells match the type of data used by that row + /// rowId can be considered the primary key for the row. It is much faster to lookup a row by its rowId than by searching keys. + /// rowId must be unique + /// Rows are stored in sorted order in the table, using rowId as the sort key + /// \param[in] rowId The UNIQUE primary key for the row. This can never be changed. + /// \param[in] initialCellValues Initial values to give the row (optional) + /// \return The newly added row + Table::Row* AddRow(unsigned rowId); + Table::Row* AddRow(unsigned rowId, DataStructures::List &initialCellValues); + Table::Row* AddRow(unsigned rowId, DataStructures::List &initialCellValues, bool copyCells=false); + + /// \brief Removes a row specified by rowId. + /// \param[in] rowId The ID of the row + /// \return true if the row was deleted. False if not. + bool RemoveRow(unsigned rowId); + + /// \brief Removes all the rows with IDs that the specified table also has. + /// \param[in] tableContainingRowIDs The IDs of the rows + void RemoveRows(Table *tableContainingRowIDs); + + /// \brief Updates a particular cell in the table. + /// \note If you are going to update many cells of a particular row, it is more efficient to call GetRow and perform the operations on the row directly. + /// \note Row pointers do not change, so you can also write directly to the rows for more efficiency. + /// \param[in] rowId The ID of the row + /// \param[in] columnIndex The column of the cell + /// \param[in] value The data to set + bool UpdateCell(unsigned rowId, unsigned columnIndex, int value); + bool UpdateCell(unsigned rowId, unsigned columnIndex, char *str); + bool UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data); + bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value); + bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str); + bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data); + + /// \brief Note this is much less efficient to call than GetRow, then working with the cells directly. + /// Numeric, string, binary + void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output); + void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output); + void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength); + + /// \brief Gets a row. More efficient to do this and access Row::cells than to repeatedly call GetCell. + /// You can also update cells in rows from this function. + /// \param[in] rowId The ID of the row + /// \return The desired row, or 0 if no such row. + Row* GetRowByID(unsigned rowId) const; + + /// \brief Gets a row at a specific index. + /// rowIndex should be less than GetRowCount() + /// \param[in] rowIndex The index of the row + /// \param[out] key The ID of the row returned + /// \return The desired row, or 0 if no such row. + Row* GetRowByIndex(unsigned rowIndex, unsigned *key) const; + + /// \brief Queries the table, optionally returning only a subset of columns and rows. + /// \param[in] columnSubset An array of column indices. Only columns in this array are returned. Pass 0 for all columns + /// \param[in] numColumnSubset The number of elements in \a columnSubset + /// \param[in] inclusionFilters An array of FilterQuery. All filters must pass for the row to be returned. + /// \param[in] numInclusionFilters The number of elements in \a inclusionFilters + /// \param[in] rowIds An arrow of row IDs. Only these rows with these IDs are returned. Pass 0 for all rows. + /// \param[in] numRowIDs The number of elements in \a rowIds + /// \param[out] result The result of the query. If no rows are returned, the table will only have columns. + void QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result); + + /// \brief Sorts the table by rows + /// \details You can sort the table in ascending or descending order on one or more columns + /// Columns have precedence in the order they appear in the \a sortQueries array + /// If a row cell on column n has the same value as a a different row on column n, then the row will be compared on column n+1 + /// \param[in] sortQueries A list of SortQuery structures, defining the sorts to perform on the table + /// \param[in] numColumnSubset The number of elements in \a numSortQueries + /// \param[out] out The address of an array of Rows, which will receive the sorted output. The array must be long enough to contain all returned rows, up to GetRowCount() + void SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out); + + /// \brief Frees all memory in the table. + void Clear(void); + + /// \brief Prints out the names of all the columns. + /// \param[out] out A pointer to an array of bytes which will hold the output. + /// \param[in] outLength The size of the \a out array + /// \param[in] columnDelineator What character to print to delineate columns + void PrintColumnHeaders(char *out, int outLength, char columnDelineator) const; + + /// \brief Writes a text representation of the row to \a out. + /// \param[out] out A pointer to an array of bytes which will hold the output. + /// \param[in] outLength The size of the \a out array + /// \param[in] columnDelineator What character to print to delineate columns + /// \param[in] printDelineatorForBinary Binary output is not printed. True to still print the delineator. + /// \param[in] inputRow The row to print + void PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const; + + /// \brief Direct access to make things easier. + const DataStructures::List& GetColumns(void) const; + + /// \brief Direct access to make things easier. + const DataStructures::BPlusTree& GetRows(void) const; + + /// \brief Get the head of a linked list containing all the row data. + DataStructures::Page * GetListHead(void); + + /// \brief Get the first free row id. + /// This could be made more efficient. + unsigned GetAvailableRowId(void) const; + + Table& operator = ( const Table& input ); + + protected: + Table::Row* AddRowColumns(unsigned rowId, Row *row, DataStructures::List columnIndices); + + void DeleteRow(Row *row); + + void QueryRow(DataStructures::List &inclusionFilterColumnIndices, DataStructures::List &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result); + + // 16 is arbitrary and is the order of the BPlus tree. Higher orders are better for searching while lower orders are better for + // Insertions and deletions. + DataStructures::BPlusTree rows; + + // Columns in the table. + DataStructures::List columns; + }; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/src/raknet/DS_ThreadsafeAllocatingQueue.h b/src/raknet/DS_ThreadsafeAllocatingQueue.h index a9a1248..178d77e 100755 --- a/src/raknet/DS_ThreadsafeAllocatingQueue.h +++ b/src/raknet/DS_ThreadsafeAllocatingQueue.h @@ -1,143 +1,143 @@ -/// \file DS_ThreadsafeAllocatingQueue.h -/// \internal -/// A threadsafe queue, that also uses a memory pool for allocation - -#ifndef __THREADSAFE_ALLOCATING_QUEUE -#define __THREADSAFE_ALLOCATING_QUEUE - -#include "DS_Queue.h" -#include "SimpleMutex.h" -#include "DS_MemoryPool.h" - -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - -namespace DataStructures -{ - -template -class RAK_DLL_EXPORT ThreadsafeAllocatingQueue -{ -public: - // Queue operations - void Push(structureType *s); - structureType *PopInaccurate(void); - structureType *Pop(void); - void SetPageSize(int size); - bool IsEmpty(void); - - // Memory pool operations - structureType *Allocate(const char *file, unsigned int line); - void Deallocate(structureType *s, const char *file, unsigned int line); - void Clear(const char *file, unsigned int line); -protected: - - MemoryPool memoryPool; - RakNet::SimpleMutex memoryPoolMutex; - Queue queue; - RakNet::SimpleMutex queueMutex; -}; - -template -void ThreadsafeAllocatingQueue::Push(structureType *s) -{ - queueMutex.Lock(); - queue.Push(s, _FILE_AND_LINE_ ); - queueMutex.Unlock(); -} - -template -structureType *ThreadsafeAllocatingQueue::PopInaccurate(void) -{ - structureType *s; - if (queue.IsEmpty()) - return 0; - queueMutex.Lock(); - if (queue.IsEmpty()==false) - s=queue.Pop(); - else - s=0; - queueMutex.Unlock(); - return s; -} - -template -structureType *ThreadsafeAllocatingQueue::Pop(void) -{ - structureType *s; - queueMutex.Lock(); - if (queue.IsEmpty()) - { - queueMutex.Unlock(); - return 0; - } - s=queue.Pop(); - queueMutex.Unlock(); - return s; -} - -template -structureType *ThreadsafeAllocatingQueue::Allocate(const char *file, unsigned int line) -{ - structureType *s; - memoryPoolMutex.Lock(); - s=memoryPool.Allocate(file, line); - memoryPoolMutex.Unlock(); - // Call new operator, memoryPool doesn't do this - s = new ((void*)s) structureType; - return s; -} -template -void ThreadsafeAllocatingQueue::Deallocate(structureType *s, const char *file, unsigned int line) -{ - // Call delete operator, memory pool doesn't do this - s->~structureType(); - memoryPoolMutex.Lock(); - memoryPool.Release(s, file, line); - memoryPoolMutex.Unlock(); -} - -template -void ThreadsafeAllocatingQueue::Clear(const char *file, unsigned int line) -{ - memoryPoolMutex.Lock(); - for (unsigned int i=0; i < queue.Size(); i++) - { - queue[i]->~structureType(); - memoryPool.Release(queue[i], file, line); - } - queue.Clear(file, line); - memoryPoolMutex.Unlock(); - memoryPoolMutex.Lock(); - memoryPool.Clear(file, line); - memoryPoolMutex.Unlock(); -} - -template -void ThreadsafeAllocatingQueue::SetPageSize(int size) -{ - memoryPool.SetPageSize(size); -} - -template -bool ThreadsafeAllocatingQueue::IsEmpty(void) -{ - bool isEmpty; - queueMutex.Lock(); - isEmpty=queue.IsEmpty(); - queueMutex.Unlock(); - return isEmpty; -} - -}; - - -// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) -// #pragma pop_macro("new") -// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - -#endif +/// \file DS_ThreadsafeAllocatingQueue.h +/// \internal +/// A threadsafe queue, that also uses a memory pool for allocation + +#ifndef __THREADSAFE_ALLOCATING_QUEUE +#define __THREADSAFE_ALLOCATING_QUEUE + +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "DS_MemoryPool.h" + +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + +namespace DataStructures +{ + +template +class RAK_DLL_EXPORT ThreadsafeAllocatingQueue +{ +public: + // Queue operations + void Push(structureType *s); + structureType *PopInaccurate(void); + structureType *Pop(void); + void SetPageSize(int size); + bool IsEmpty(void); + + // Memory pool operations + structureType *Allocate(const char *file, unsigned int line); + void Deallocate(structureType *s, const char *file, unsigned int line); + void Clear(const char *file, unsigned int line); +protected: + + MemoryPool memoryPool; + RakNet::SimpleMutex memoryPoolMutex; + Queue queue; + RakNet::SimpleMutex queueMutex; +}; + +template +void ThreadsafeAllocatingQueue::Push(structureType *s) +{ + queueMutex.Lock(); + queue.Push(s, _FILE_AND_LINE_ ); + queueMutex.Unlock(); +} + +template +structureType *ThreadsafeAllocatingQueue::PopInaccurate(void) +{ + structureType *s; + if (queue.IsEmpty()) + return 0; + queueMutex.Lock(); + if (queue.IsEmpty()==false) + s=queue.Pop(); + else + s=0; + queueMutex.Unlock(); + return s; +} + +template +structureType *ThreadsafeAllocatingQueue::Pop(void) +{ + structureType *s; + queueMutex.Lock(); + if (queue.IsEmpty()) + { + queueMutex.Unlock(); + return 0; + } + s=queue.Pop(); + queueMutex.Unlock(); + return s; +} + +template +structureType *ThreadsafeAllocatingQueue::Allocate(const char *file, unsigned int line) +{ + structureType *s; + memoryPoolMutex.Lock(); + s=memoryPool.Allocate(file, line); + memoryPoolMutex.Unlock(); + // Call new operator, memoryPool doesn't do this + s = new ((void*)s) structureType; + return s; +} +template +void ThreadsafeAllocatingQueue::Deallocate(structureType *s, const char *file, unsigned int line) +{ + // Call delete operator, memory pool doesn't do this + s->~structureType(); + memoryPoolMutex.Lock(); + memoryPool.Release(s, file, line); + memoryPoolMutex.Unlock(); +} + +template +void ThreadsafeAllocatingQueue::Clear(const char *file, unsigned int line) +{ + memoryPoolMutex.Lock(); + for (unsigned int i=0; i < queue.Size(); i++) + { + queue[i]->~structureType(); + memoryPool.Release(queue[i], file, line); + } + queue.Clear(file, line); + memoryPoolMutex.Unlock(); + memoryPoolMutex.Lock(); + memoryPool.Clear(file, line); + memoryPoolMutex.Unlock(); +} + +template +void ThreadsafeAllocatingQueue::SetPageSize(int size) +{ + memoryPool.SetPageSize(size); +} + +template +bool ThreadsafeAllocatingQueue::IsEmpty(void) +{ + bool isEmpty; + queueMutex.Lock(); + isEmpty=queue.IsEmpty(); + queueMutex.Unlock(); + return isEmpty; +} + +}; + + +// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) +// #pragma pop_macro("new") +// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + +#endif diff --git a/src/raknet/DS_Tree.h b/src/raknet/DS_Tree.h index 50b723c..98949f1 100755 --- a/src/raknet/DS_Tree.h +++ b/src/raknet/DS_Tree.h @@ -1,98 +1,98 @@ -/// \file DS_Tree.h -/// \internal -/// \brief Just a regular tree -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __DS_TREE_H -#define __DS_TREE_H - -#include "Export.h" -#include "DS_List.h" -#include "DS_Queue.h" -#include "RakMemoryOverride.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template - class RAK_DLL_EXPORT Tree - { - public: - Tree(); - Tree(TreeType &inputData); - ~Tree(); - void LevelOrderTraversal(DataStructures::List &output); - void AddChild(TreeType &newData); - void DeleteDecendants(void); - - TreeType data; - DataStructures::List children; - }; - - template - Tree::Tree() - { - - } - - template - Tree::Tree(TreeType &inputData) - { - data=inputData; - } - - template - Tree::~Tree() - { - DeleteDecendants(); - } - - template - void Tree::LevelOrderTraversal(DataStructures::List &output) - { - unsigned i; - Tree *node; - DataStructures::Queue*> queue; - - for (i=0; i < children.Size(); i++) - queue.Push(children[i]); - - while (queue.Size()) - { - node=queue.Pop(); - output.Insert(node, _FILE_AND_LINE_); - for (i=0; i < node->children.Size(); i++) - queue.Push(node->children[i]); - } - } - - template - void Tree::AddChild(TreeType &newData) - { - children.Insert(RakNet::OP_NEW(newData, _FILE_AND_LINE_)); - } - - template - void Tree::DeleteDecendants(void) - { - /* - DataStructures::List output; - LevelOrderTraversal(output); - unsigned i; - for (i=0; i < output.Size(); i++) - RakNet::OP_DELETE(output[i], _FILE_AND_LINE_); -*/ - - // Already recursive to do this - unsigned int i; - for (i=0; i < children.Size(); i++) - RakNet::OP_DELETE(children[i], _FILE_AND_LINE_); - } -} - -#endif +/// \file DS_Tree.h +/// \internal +/// \brief Just a regular tree +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __DS_TREE_H +#define __DS_TREE_H + +#include "Export.h" +#include "DS_List.h" +#include "DS_Queue.h" +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + class RAK_DLL_EXPORT Tree + { + public: + Tree(); + Tree(TreeType &inputData); + ~Tree(); + void LevelOrderTraversal(DataStructures::List &output); + void AddChild(TreeType &newData); + void DeleteDecendants(void); + + TreeType data; + DataStructures::List children; + }; + + template + Tree::Tree() + { + + } + + template + Tree::Tree(TreeType &inputData) + { + data=inputData; + } + + template + Tree::~Tree() + { + DeleteDecendants(); + } + + template + void Tree::LevelOrderTraversal(DataStructures::List &output) + { + unsigned i; + Tree *node; + DataStructures::Queue*> queue; + + for (i=0; i < children.Size(); i++) + queue.Push(children[i]); + + while (queue.Size()) + { + node=queue.Pop(); + output.Insert(node, _FILE_AND_LINE_); + for (i=0; i < node->children.Size(); i++) + queue.Push(node->children[i]); + } + } + + template + void Tree::AddChild(TreeType &newData) + { + children.Insert(RakNet::OP_NEW(newData, _FILE_AND_LINE_)); + } + + template + void Tree::DeleteDecendants(void) + { + /* + DataStructures::List output; + LevelOrderTraversal(output); + unsigned i; + for (i=0; i < output.Size(); i++) + RakNet::OP_DELETE(output[i], _FILE_AND_LINE_); +*/ + + // Already recursive to do this + unsigned int i; + for (i=0; i < children.Size(); i++) + RakNet::OP_DELETE(children[i], _FILE_AND_LINE_); + } +} + +#endif diff --git a/src/raknet/DS_WeightedGraph.h b/src/raknet/DS_WeightedGraph.h index 28f4424..d2ad665 100755 --- a/src/raknet/DS_WeightedGraph.h +++ b/src/raknet/DS_WeightedGraph.h @@ -1,537 +1,537 @@ -/// \file DS_WeightedGraph.h -/// \internal -/// \brief Weighted graph. -/// \details I'm assuming the indices are complex map types, rather than sequential numbers (which could be implemented much more efficiently). -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __WEIGHTED_GRAPH_H -#define __WEIGHTED_GRAPH_H - -#include "DS_OrderedList.h" -#include "DS_Map.h" -#include "DS_Heap.h" -#include "DS_Queue.h" -#include "DS_Tree.h" -#include "RakAssert.h" -#include "RakMemoryOverride.h" -#ifdef _DEBUG -#include -#endif - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - template - class RAK_DLL_EXPORT WeightedGraph - { - public: - static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(node_type(),node_type());} - - WeightedGraph(); - ~WeightedGraph(); - WeightedGraph( const WeightedGraph& original_copy ); - WeightedGraph& operator= ( const WeightedGraph& original_copy ); - void AddNode(const node_type &node); - void RemoveNode(const node_type &node); - void AddConnection(const node_type &node1, const node_type &node2, weight_type weight); - void RemoveConnection(const node_type &node1, const node_type &node2); - bool HasConnection(const node_type &node1, const node_type &node2); - void Print(void); - void Clear(void); - bool GetShortestPath(DataStructures::List &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT); - bool GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT ); - unsigned GetNodeCount(void) const; - unsigned GetConnectionCount(unsigned nodeIndex) const; - void GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const; - node_type GetNodeAtIndex(unsigned nodeIndex) const; - - protected: - void ClearDijkstra(void); - void GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT); - - DataStructures::Map *> adjacencyLists; - - // All these variables are for path finding with Dijkstra - // 08/23/06 Won't compile as a DLL inside this struct - // struct - // { - bool isValidPath; - node_type rootNode; - DataStructures::OrderedList costMatrixIndices; - weight_type *costMatrix; - node_type *leastNodeArray; - // } dijkstra; - - struct NodeAndParent - { - DataStructures::Tree*node; - DataStructures::Tree*parent; - }; - }; - - template - WeightedGraph::WeightedGraph() - { - isValidPath=false; - costMatrix=0; - } - - template - WeightedGraph::~WeightedGraph() - { - Clear(); - } - - template - WeightedGraph::WeightedGraph( const WeightedGraph& original_copy ) - { - adjacencyLists=original_copy.adjacencyLists; - - isValidPath=original_copy.isValidPath; - if (isValidPath) - { - rootNode=original_copy.rootNode; - costMatrixIndices=original_copy.costMatrixIndices; - costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ ); - leastNodeArray = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size(), _FILE_AND_LINE_ ); - memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type)); - memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type)); - } - } - - template - WeightedGraph& WeightedGraph::operator=( const WeightedGraph& original_copy ) - { - adjacencyLists=original_copy.adjacencyLists; - - isValidPath=original_copy.isValidPath; - if (isValidPath) - { - rootNode=original_copy.rootNode; - costMatrixIndices=original_copy.costMatrixIndices; - costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ ); - leastNodeArray = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size(), _FILE_AND_LINE_ ); - memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type)); - memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type)); - } - - return *this; - } - - template - void WeightedGraph::AddNode(const node_type &node) - { - adjacencyLists.SetNew(node, RakNet::OP_NEW >( _FILE_AND_LINE_) ); - } - - template - void WeightedGraph::RemoveNode(const node_type &node) - { - unsigned i; - DataStructures::Queue removeNodeQueue; - - removeNodeQueue.Push(node, _FILE_AND_LINE_ ); - while (removeNodeQueue.Size()) - { - RakNet::OP_DELETE(adjacencyLists.Pop(removeNodeQueue.Pop()), _FILE_AND_LINE_); - - // Remove this node from all of the other lists as well - for (i=0; i < adjacencyLists.Size(); i++) - { - adjacencyLists[i]->Delete(node); - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (allow_unlinkedNodes==false && adjacencyLists[i]->Size()==0) - removeNodeQueue.Push(adjacencyLists.GetKeyAtIndex(i), _FILE_AND_LINE_ ); - } - } - - ClearDijkstra(); - } - - template - bool WeightedGraph::HasConnection(const node_type &node1, const node_type &node2) - { - if (node1==node2) - return false; - if (adjacencyLists.Has(node1)==false) - return false; - return adjacencyLists.Get(node1)->Has(node2); - } - - template - void WeightedGraph::AddConnection(const node_type &node1, const node_type &node2, weight_type weight) - { - if (node1==node2) - return; - - if (adjacencyLists.Has(node1)==false) - AddNode(node1); - adjacencyLists.Get(node1)->Set(node2, weight); - if (adjacencyLists.Has(node2)==false) - AddNode(node2); - adjacencyLists.Get(node2)->Set(node1, weight); - } - - template - void WeightedGraph::RemoveConnection(const node_type &node1, const node_type &node2) - { - adjacencyLists.Get(node2)->Delete(node1); - adjacencyLists.Get(node1)->Delete(node2); - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - if (allow_unlinkedNodes==false) // If we do not allow _unlinked nodes, then if there are no connections, remove the node - { - if (adjacencyLists.Get(node1)->Size()==0) - RemoveNode(node1); // Will also remove node1 from the adjacency list of node2 - if (adjacencyLists.Has(node2) && adjacencyLists.Get(node2)->Size()==0) - RemoveNode(node2); - } - - ClearDijkstra(); - } - - template - void WeightedGraph::Clear(void) - { - unsigned i; - for (i=0; i < adjacencyLists.Size(); i++) - RakNet::OP_DELETE(adjacencyLists[i], _FILE_AND_LINE_); - adjacencyLists.Clear(); - - ClearDijkstra(); - } - - template - bool WeightedGraph::GetShortestPath(DataStructures::List &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT) - { - path.Clear(false, _FILE_AND_LINE_); - if (startNode==endNode) - { - path.Insert(startNode, _FILE_AND_LINE_); - path.Insert(endNode, _FILE_AND_LINE_); - return true; - } - - if (isValidPath==false || rootNode!=startNode) - { - ClearDijkstra(); - GenerateDisjktraMatrix(startNode, INFINITE_WEIGHT); - } - - // return the results - bool objectExists; - unsigned col,row; - weight_type currentWeight; - DataStructures::Queue outputQueue; - col=costMatrixIndices.GetIndexFromKey(endNode, &objectExists); - if (costMatrixIndices.Size()<2) - { - return false; - } - if (objectExists==false) - { - return false; - } - node_type vertex; - row=costMatrixIndices.Size()-2; - if (row==0) - { - path.Insert(startNode, _FILE_AND_LINE_); - path.Insert(endNode, _FILE_AND_LINE_); - return true; - } - currentWeight=costMatrix[row*adjacencyLists.Size() + col]; - if (currentWeight==INFINITE_WEIGHT) - { - // No path - return true; - } - vertex=endNode; - outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_); - row--; -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - while (costMatrix[row*adjacencyLists.Size() + col] == currentWeight) - { - if (row==0) - { - path.Insert(startNode, _FILE_AND_LINE_); - for (col=0; outputQueue.Size(); col++) - path.Insert(outputQueue.Pop(), _FILE_AND_LINE_); - return true; - } - --row; - } - - vertex=leastNodeArray[row]; - outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_); - if (row==0) - break; - col=costMatrixIndices.GetIndexFromKey(vertex, &objectExists); - currentWeight=costMatrix[row*adjacencyLists.Size() + col]; - } - - path.Insert(startNode, _FILE_AND_LINE_); - for (col=0; outputQueue.Size(); col++) - path.Insert(outputQueue.Pop(), _FILE_AND_LINE_); - return true; - } - - template - node_type WeightedGraph::GetNodeAtIndex(unsigned nodeIndex) const - { - return adjacencyLists.GetKeyAtIndex(nodeIndex); - } - - template - unsigned WeightedGraph::GetNodeCount(void) const - { - return adjacencyLists.Size(); - } - - template - unsigned WeightedGraph::GetConnectionCount(unsigned nodeIndex) const - { - return adjacencyLists[nodeIndex]->Size(); - } - - template - void WeightedGraph::GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const - { - outWeight=adjacencyLists[nodeIndex]->operator[](connectionIndex); - outNode=adjacencyLists[nodeIndex]->GetKeyAtIndex(connectionIndex); - } - - template - bool WeightedGraph::GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT ) - { - // Find the shortest path from the start node to each of the input nodes. Add this path to a new WeightedGraph if the result is reachable - DataStructures::List path; - DataStructures::WeightedGraph outGraph; - bool res; - unsigned i,j; - for (i=0; i < inputNodes->Size(); i++) - { - res=GetShortestPath(path, startNode, (*inputNodes)[i], INFINITE_WEIGHT); - if (res && path.Size()>0) - { - for (j=0; j < path.Size()-1; j++) - { - // Don't bother looking up the weight - outGraph.AddConnection(path[j], path[j+1], INFINITE_WEIGHT); - } - } - } - - // Copy the graph to a tree. - DataStructures::Queue nodesToProcess; - DataStructures::Tree *current; - DataStructures::Map *adjacencyList; - node_type key; - NodeAndParent nap, nap2; - outTree.DeleteDecendants(); - outTree.data=startNode; - current=&outTree; - if (outGraph.adjacencyLists.Has(startNode)==false) - return false; - adjacencyList = outGraph.adjacencyLists.Get(startNode); - - for (i=0; i < adjacencyList->Size(); i++) - { - nap2.node=RakNet::OP_NEW >( _FILE_AND_LINE_ ); - nap2.node->data=adjacencyList->GetKeyAtIndex(i); - nap2.parent=current; - nodesToProcess.Push(nap2, _FILE_AND_LINE_ ); - current->children.Insert(nap2.node, _FILE_AND_LINE_); - } - - while (nodesToProcess.Size()) - { - nap=nodesToProcess.Pop(); - current=nap.node; - adjacencyList = outGraph.adjacencyLists.Get(nap.node->data); - - for (i=0; i < adjacencyList->Size(); i++) - { - key=adjacencyList->GetKeyAtIndex(i); - if (key!=nap.parent->data) - { - nap2.node=RakNet::OP_NEW >( _FILE_AND_LINE_ ); - nap2.node->data=key; - nap2.parent=current; - nodesToProcess.Push(nap2, _FILE_AND_LINE_ ); - current->children.Insert(nap2.node, _FILE_AND_LINE_); - } - } - } - - return true; - } - - template - void WeightedGraph::GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT) - { - if (adjacencyLists.Size()==0) - return; - - costMatrix = RakNet::OP_NEW_ARRAY(adjacencyLists.Size() * adjacencyLists.Size(), _FILE_AND_LINE_ ); - leastNodeArray = RakNet::OP_NEW_ARRAY(adjacencyLists.Size(), _FILE_AND_LINE_ ); - - node_type currentNode; - unsigned col, row, row2, openSetIndex; - node_type adjacentKey; - unsigned adjacentIndex; - weight_type edgeWeight, currentNodeWeight, adjacentNodeWeight; - DataStructures::Map *adjacencyList; - DataStructures::Heap minHeap; - DataStructures::Map openSet; - - for (col=0; col < adjacencyLists.Size(); col++) - { - // This should be already sorted, so it's a bit inefficient to do an insertion sort, but what the heck - costMatrixIndices.Insert(adjacencyLists.GetKeyAtIndex(col),adjacencyLists.GetKeyAtIndex(col), true, _FILE_AND_LINE_); - } - for (col=0; col < adjacencyLists.Size() * adjacencyLists.Size(); col++) - costMatrix[col]=INFINITE_WEIGHT; - currentNode=startNode; - row=0; - currentNodeWeight=0; - rootNode=startNode; - - // Clear the starting node column - if (adjacencyLists.Size()) - { - adjacentIndex=adjacencyLists.GetIndexAtKey(startNode); - for (row2=0; row2 < adjacencyLists.Size(); row2++) - costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=0; - } - - while (row < adjacencyLists.Size()-1) - { - adjacencyList = adjacencyLists.Get(currentNode); - // Go through all connections from the current node. If the new weight is less than the current weight, then update that weight. - for (col=0; col < adjacencyList->Size(); col++) - { - edgeWeight=(*adjacencyList)[col]; - adjacentKey=adjacencyList->GetKeyAtIndex(col); - adjacentIndex=adjacencyLists.GetIndexAtKey(adjacentKey); - adjacentNodeWeight=costMatrix[row*adjacencyLists.Size() + adjacentIndex]; - - if (currentNodeWeight + edgeWeight < adjacentNodeWeight) - { - // Update the weight for the adjacent node - for (row2=row; row2 < adjacencyLists.Size(); row2++) - costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=currentNodeWeight + edgeWeight; - openSet.Set(adjacentKey, currentNodeWeight + edgeWeight); - } - } - - // Find the lowest in the open set - minHeap.Clear(true,_FILE_AND_LINE_); - for (openSetIndex=0; openSetIndex < openSet.Size(); openSetIndex++) - minHeap.Push(openSet[openSetIndex], openSet.GetKeyAtIndex(openSetIndex),_FILE_AND_LINE_); - - /* - unsigned i,j; - for (i=0; i < adjacencyLists.Size()-1; i++) - { - for (j=0; j < adjacencyLists.Size(); j++) - { - RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); - } - RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); - RAKNET_DEBUG_PRINTF("\n"); - } - */ - - if (minHeap.Size()==0) - { - // Unreachable nodes - isValidPath=true; - return; - } - - currentNodeWeight=minHeap.PeekWeight(0); - leastNodeArray[row]=minHeap.Pop(0); - currentNode=leastNodeArray[row]; - openSet.Delete(currentNode); - row++; - } - - /* -#ifdef _DEBUG - unsigned i,j; - for (i=0; i < adjacencyLists.Size()-1; i++) - { - for (j=0; j < adjacencyLists.Size(); j++) - { - RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); - } - RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); - RAKNET_DEBUG_PRINTF("\n"); - } -#endif - */ - - isValidPath=true; - } - - template - void WeightedGraph::ClearDijkstra(void) - { - if (isValidPath) - { - isValidPath=false; - RakNet::OP_DELETE_ARRAY(costMatrix, _FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(leastNodeArray, _FILE_AND_LINE_); - costMatrixIndices.Clear(false, _FILE_AND_LINE_); - } - } - - template - void WeightedGraph::Print(void) - { -#ifdef _DEBUG - unsigned i,j; - for (i=0; i < adjacencyLists.Size(); i++) - { - //RAKNET_DEBUG_PRINTF("%i connected to ", i); - RAKNET_DEBUG_PRINTF("%s connected to ", adjacencyLists.GetKeyAtIndex(i).systemAddress.ToString()); - - if (adjacencyLists[i]->Size()==0) - RAKNET_DEBUG_PRINTF(""); - else - { - for (j=0; j < adjacencyLists[i]->Size(); j++) - // RAKNET_DEBUG_PRINTF("%i (%.2f) ", adjacencyLists.GetIndexAtKey(adjacencyLists[i]->GetKeyAtIndex(j)), (float) adjacencyLists[i]->operator[](j) ); - RAKNET_DEBUG_PRINTF("%s (%.2f) ", adjacencyLists[i]->GetKeyAtIndex(j).systemAddress.ToString(), (float) adjacencyLists[i]->operator[](j) ); - } - - RAKNET_DEBUG_PRINTF("\n"); - } -#endif - } -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif +/// \file DS_WeightedGraph.h +/// \internal +/// \brief Weighted graph. +/// \details I'm assuming the indices are complex map types, rather than sequential numbers (which could be implemented much more efficiently). +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __WEIGHTED_GRAPH_H +#define __WEIGHTED_GRAPH_H + +#include "DS_OrderedList.h" +#include "DS_Map.h" +#include "DS_Heap.h" +#include "DS_Queue.h" +#include "DS_Tree.h" +#include "RakAssert.h" +#include "RakMemoryOverride.h" +#ifdef _DEBUG +#include +#endif + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + class RAK_DLL_EXPORT WeightedGraph + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(node_type(),node_type());} + + WeightedGraph(); + ~WeightedGraph(); + WeightedGraph( const WeightedGraph& original_copy ); + WeightedGraph& operator= ( const WeightedGraph& original_copy ); + void AddNode(const node_type &node); + void RemoveNode(const node_type &node); + void AddConnection(const node_type &node1, const node_type &node2, weight_type weight); + void RemoveConnection(const node_type &node1, const node_type &node2); + bool HasConnection(const node_type &node1, const node_type &node2); + void Print(void); + void Clear(void); + bool GetShortestPath(DataStructures::List &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT); + bool GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT ); + unsigned GetNodeCount(void) const; + unsigned GetConnectionCount(unsigned nodeIndex) const; + void GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const; + node_type GetNodeAtIndex(unsigned nodeIndex) const; + + protected: + void ClearDijkstra(void); + void GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT); + + DataStructures::Map *> adjacencyLists; + + // All these variables are for path finding with Dijkstra + // 08/23/06 Won't compile as a DLL inside this struct + // struct + // { + bool isValidPath; + node_type rootNode; + DataStructures::OrderedList costMatrixIndices; + weight_type *costMatrix; + node_type *leastNodeArray; + // } dijkstra; + + struct NodeAndParent + { + DataStructures::Tree*node; + DataStructures::Tree*parent; + }; + }; + + template + WeightedGraph::WeightedGraph() + { + isValidPath=false; + costMatrix=0; + } + + template + WeightedGraph::~WeightedGraph() + { + Clear(); + } + + template + WeightedGraph::WeightedGraph( const WeightedGraph& original_copy ) + { + adjacencyLists=original_copy.adjacencyLists; + + isValidPath=original_copy.isValidPath; + if (isValidPath) + { + rootNode=original_copy.rootNode; + costMatrixIndices=original_copy.costMatrixIndices; + costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ ); + leastNodeArray = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size(), _FILE_AND_LINE_ ); + memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type)); + memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type)); + } + } + + template + WeightedGraph& WeightedGraph::operator=( const WeightedGraph& original_copy ) + { + adjacencyLists=original_copy.adjacencyLists; + + isValidPath=original_copy.isValidPath; + if (isValidPath) + { + rootNode=original_copy.rootNode; + costMatrixIndices=original_copy.costMatrixIndices; + costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ ); + leastNodeArray = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size(), _FILE_AND_LINE_ ); + memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type)); + memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type)); + } + + return *this; + } + + template + void WeightedGraph::AddNode(const node_type &node) + { + adjacencyLists.SetNew(node, RakNet::OP_NEW >( _FILE_AND_LINE_) ); + } + + template + void WeightedGraph::RemoveNode(const node_type &node) + { + unsigned i; + DataStructures::Queue removeNodeQueue; + + removeNodeQueue.Push(node, _FILE_AND_LINE_ ); + while (removeNodeQueue.Size()) + { + RakNet::OP_DELETE(adjacencyLists.Pop(removeNodeQueue.Pop()), _FILE_AND_LINE_); + + // Remove this node from all of the other lists as well + for (i=0; i < adjacencyLists.Size(); i++) + { + adjacencyLists[i]->Delete(node); + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (allow_unlinkedNodes==false && adjacencyLists[i]->Size()==0) + removeNodeQueue.Push(adjacencyLists.GetKeyAtIndex(i), _FILE_AND_LINE_ ); + } + } + + ClearDijkstra(); + } + + template + bool WeightedGraph::HasConnection(const node_type &node1, const node_type &node2) + { + if (node1==node2) + return false; + if (adjacencyLists.Has(node1)==false) + return false; + return adjacencyLists.Get(node1)->Has(node2); + } + + template + void WeightedGraph::AddConnection(const node_type &node1, const node_type &node2, weight_type weight) + { + if (node1==node2) + return; + + if (adjacencyLists.Has(node1)==false) + AddNode(node1); + adjacencyLists.Get(node1)->Set(node2, weight); + if (adjacencyLists.Has(node2)==false) + AddNode(node2); + adjacencyLists.Get(node2)->Set(node1, weight); + } + + template + void WeightedGraph::RemoveConnection(const node_type &node1, const node_type &node2) + { + adjacencyLists.Get(node2)->Delete(node1); + adjacencyLists.Get(node1)->Delete(node2); + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (allow_unlinkedNodes==false) // If we do not allow _unlinked nodes, then if there are no connections, remove the node + { + if (adjacencyLists.Get(node1)->Size()==0) + RemoveNode(node1); // Will also remove node1 from the adjacency list of node2 + if (adjacencyLists.Has(node2) && adjacencyLists.Get(node2)->Size()==0) + RemoveNode(node2); + } + + ClearDijkstra(); + } + + template + void WeightedGraph::Clear(void) + { + unsigned i; + for (i=0; i < adjacencyLists.Size(); i++) + RakNet::OP_DELETE(adjacencyLists[i], _FILE_AND_LINE_); + adjacencyLists.Clear(); + + ClearDijkstra(); + } + + template + bool WeightedGraph::GetShortestPath(DataStructures::List &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT) + { + path.Clear(false, _FILE_AND_LINE_); + if (startNode==endNode) + { + path.Insert(startNode, _FILE_AND_LINE_); + path.Insert(endNode, _FILE_AND_LINE_); + return true; + } + + if (isValidPath==false || rootNode!=startNode) + { + ClearDijkstra(); + GenerateDisjktraMatrix(startNode, INFINITE_WEIGHT); + } + + // return the results + bool objectExists; + unsigned col,row; + weight_type currentWeight; + DataStructures::Queue outputQueue; + col=costMatrixIndices.GetIndexFromKey(endNode, &objectExists); + if (costMatrixIndices.Size()<2) + { + return false; + } + if (objectExists==false) + { + return false; + } + node_type vertex; + row=costMatrixIndices.Size()-2; + if (row==0) + { + path.Insert(startNode, _FILE_AND_LINE_); + path.Insert(endNode, _FILE_AND_LINE_); + return true; + } + currentWeight=costMatrix[row*adjacencyLists.Size() + col]; + if (currentWeight==INFINITE_WEIGHT) + { + // No path + return true; + } + vertex=endNode; + outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_); + row--; +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + while (costMatrix[row*adjacencyLists.Size() + col] == currentWeight) + { + if (row==0) + { + path.Insert(startNode, _FILE_AND_LINE_); + for (col=0; outputQueue.Size(); col++) + path.Insert(outputQueue.Pop(), _FILE_AND_LINE_); + return true; + } + --row; + } + + vertex=leastNodeArray[row]; + outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_); + if (row==0) + break; + col=costMatrixIndices.GetIndexFromKey(vertex, &objectExists); + currentWeight=costMatrix[row*adjacencyLists.Size() + col]; + } + + path.Insert(startNode, _FILE_AND_LINE_); + for (col=0; outputQueue.Size(); col++) + path.Insert(outputQueue.Pop(), _FILE_AND_LINE_); + return true; + } + + template + node_type WeightedGraph::GetNodeAtIndex(unsigned nodeIndex) const + { + return adjacencyLists.GetKeyAtIndex(nodeIndex); + } + + template + unsigned WeightedGraph::GetNodeCount(void) const + { + return adjacencyLists.Size(); + } + + template + unsigned WeightedGraph::GetConnectionCount(unsigned nodeIndex) const + { + return adjacencyLists[nodeIndex]->Size(); + } + + template + void WeightedGraph::GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const + { + outWeight=adjacencyLists[nodeIndex]->operator[](connectionIndex); + outNode=adjacencyLists[nodeIndex]->GetKeyAtIndex(connectionIndex); + } + + template + bool WeightedGraph::GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT ) + { + // Find the shortest path from the start node to each of the input nodes. Add this path to a new WeightedGraph if the result is reachable + DataStructures::List path; + DataStructures::WeightedGraph outGraph; + bool res; + unsigned i,j; + for (i=0; i < inputNodes->Size(); i++) + { + res=GetShortestPath(path, startNode, (*inputNodes)[i], INFINITE_WEIGHT); + if (res && path.Size()>0) + { + for (j=0; j < path.Size()-1; j++) + { + // Don't bother looking up the weight + outGraph.AddConnection(path[j], path[j+1], INFINITE_WEIGHT); + } + } + } + + // Copy the graph to a tree. + DataStructures::Queue nodesToProcess; + DataStructures::Tree *current; + DataStructures::Map *adjacencyList; + node_type key; + NodeAndParent nap, nap2; + outTree.DeleteDecendants(); + outTree.data=startNode; + current=&outTree; + if (outGraph.adjacencyLists.Has(startNode)==false) + return false; + adjacencyList = outGraph.adjacencyLists.Get(startNode); + + for (i=0; i < adjacencyList->Size(); i++) + { + nap2.node=RakNet::OP_NEW >( _FILE_AND_LINE_ ); + nap2.node->data=adjacencyList->GetKeyAtIndex(i); + nap2.parent=current; + nodesToProcess.Push(nap2, _FILE_AND_LINE_ ); + current->children.Insert(nap2.node, _FILE_AND_LINE_); + } + + while (nodesToProcess.Size()) + { + nap=nodesToProcess.Pop(); + current=nap.node; + adjacencyList = outGraph.adjacencyLists.Get(nap.node->data); + + for (i=0; i < adjacencyList->Size(); i++) + { + key=adjacencyList->GetKeyAtIndex(i); + if (key!=nap.parent->data) + { + nap2.node=RakNet::OP_NEW >( _FILE_AND_LINE_ ); + nap2.node->data=key; + nap2.parent=current; + nodesToProcess.Push(nap2, _FILE_AND_LINE_ ); + current->children.Insert(nap2.node, _FILE_AND_LINE_); + } + } + } + + return true; + } + + template + void WeightedGraph::GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT) + { + if (adjacencyLists.Size()==0) + return; + + costMatrix = RakNet::OP_NEW_ARRAY(adjacencyLists.Size() * adjacencyLists.Size(), _FILE_AND_LINE_ ); + leastNodeArray = RakNet::OP_NEW_ARRAY(adjacencyLists.Size(), _FILE_AND_LINE_ ); + + node_type currentNode; + unsigned col, row, row2, openSetIndex; + node_type adjacentKey; + unsigned adjacentIndex; + weight_type edgeWeight, currentNodeWeight, adjacentNodeWeight; + DataStructures::Map *adjacencyList; + DataStructures::Heap minHeap; + DataStructures::Map openSet; + + for (col=0; col < adjacencyLists.Size(); col++) + { + // This should be already sorted, so it's a bit inefficient to do an insertion sort, but what the heck + costMatrixIndices.Insert(adjacencyLists.GetKeyAtIndex(col),adjacencyLists.GetKeyAtIndex(col), true, _FILE_AND_LINE_); + } + for (col=0; col < adjacencyLists.Size() * adjacencyLists.Size(); col++) + costMatrix[col]=INFINITE_WEIGHT; + currentNode=startNode; + row=0; + currentNodeWeight=0; + rootNode=startNode; + + // Clear the starting node column + if (adjacencyLists.Size()) + { + adjacentIndex=adjacencyLists.GetIndexAtKey(startNode); + for (row2=0; row2 < adjacencyLists.Size(); row2++) + costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=0; + } + + while (row < adjacencyLists.Size()-1) + { + adjacencyList = adjacencyLists.Get(currentNode); + // Go through all connections from the current node. If the new weight is less than the current weight, then update that weight. + for (col=0; col < adjacencyList->Size(); col++) + { + edgeWeight=(*adjacencyList)[col]; + adjacentKey=adjacencyList->GetKeyAtIndex(col); + adjacentIndex=adjacencyLists.GetIndexAtKey(adjacentKey); + adjacentNodeWeight=costMatrix[row*adjacencyLists.Size() + adjacentIndex]; + + if (currentNodeWeight + edgeWeight < adjacentNodeWeight) + { + // Update the weight for the adjacent node + for (row2=row; row2 < adjacencyLists.Size(); row2++) + costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=currentNodeWeight + edgeWeight; + openSet.Set(adjacentKey, currentNodeWeight + edgeWeight); + } + } + + // Find the lowest in the open set + minHeap.Clear(true,_FILE_AND_LINE_); + for (openSetIndex=0; openSetIndex < openSet.Size(); openSetIndex++) + minHeap.Push(openSet[openSetIndex], openSet.GetKeyAtIndex(openSetIndex),_FILE_AND_LINE_); + + /* + unsigned i,j; + for (i=0; i < adjacencyLists.Size()-1; i++) + { + for (j=0; j < adjacencyLists.Size(); j++) + { + RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); + } + RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); + RAKNET_DEBUG_PRINTF("\n"); + } + */ + + if (minHeap.Size()==0) + { + // Unreachable nodes + isValidPath=true; + return; + } + + currentNodeWeight=minHeap.PeekWeight(0); + leastNodeArray[row]=minHeap.Pop(0); + currentNode=leastNodeArray[row]; + openSet.Delete(currentNode); + row++; + } + + /* +#ifdef _DEBUG + unsigned i,j; + for (i=0; i < adjacencyLists.Size()-1; i++) + { + for (j=0; j < adjacencyLists.Size(); j++) + { + RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); + } + RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); + RAKNET_DEBUG_PRINTF("\n"); + } +#endif + */ + + isValidPath=true; + } + + template + void WeightedGraph::ClearDijkstra(void) + { + if (isValidPath) + { + isValidPath=false; + RakNet::OP_DELETE_ARRAY(costMatrix, _FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(leastNodeArray, _FILE_AND_LINE_); + costMatrixIndices.Clear(false, _FILE_AND_LINE_); + } + } + + template + void WeightedGraph::Print(void) + { +#ifdef _DEBUG + unsigned i,j; + for (i=0; i < adjacencyLists.Size(); i++) + { + //RAKNET_DEBUG_PRINTF("%i connected to ", i); + RAKNET_DEBUG_PRINTF("%s connected to ", adjacencyLists.GetKeyAtIndex(i).systemAddress.ToString()); + + if (adjacencyLists[i]->Size()==0) + RAKNET_DEBUG_PRINTF(""); + else + { + for (j=0; j < adjacencyLists[i]->Size(); j++) + // RAKNET_DEBUG_PRINTF("%i (%.2f) ", adjacencyLists.GetIndexAtKey(adjacencyLists[i]->GetKeyAtIndex(j)), (float) adjacencyLists[i]->operator[](j) ); + RAKNET_DEBUG_PRINTF("%s (%.2f) ", adjacencyLists[i]->GetKeyAtIndex(j).systemAddress.ToString(), (float) adjacencyLists[i]->operator[](j) ); + } + + RAKNET_DEBUG_PRINTF("\n"); + } +#endif + } +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/src/raknet/DataCompressor.cpp b/src/raknet/DataCompressor.cpp index f281ff2..5cf1d34 100755 --- a/src/raknet/DataCompressor.cpp +++ b/src/raknet/DataCompressor.cpp @@ -1,63 +1,63 @@ -#include "DataCompressor.h" -#include "DS_HuffmanEncodingTree.h" -#include "RakAssert.h" -#include // Use string.h rather than memory.h for a console - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(DataCompressor,DataCompressor) - -void DataCompressor::Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ) -{ - // Don't use this for small files as you will just make them bigger! - RakAssert(sizeInBytes > 2048); - - unsigned int frequencyTable[ 256 ]; - unsigned int i; - memset(frequencyTable,0,256*sizeof(unsigned int)); - for (i=0; i < sizeInBytes; i++) - ++frequencyTable[userData[i]]; - HuffmanEncodingTree tree; - BitSize_t writeOffset1, writeOffset2, bitsUsed1, bitsUsed2; - tree.GenerateFromFrequencyTable(frequencyTable); - output->WriteCompressed(sizeInBytes); - for (i=0; i < 256; i++) - output->WriteCompressed(frequencyTable[i]); - output->AlignWriteToByteBoundary(); - writeOffset1=output->GetWriteOffset(); - output->Write((unsigned int)0); // Dummy value - bitsUsed1=output->GetNumberOfBitsUsed(); - tree.EncodeArray(userData, sizeInBytes, output); - bitsUsed2=output->GetNumberOfBitsUsed(); - writeOffset2=output->GetWriteOffset(); - output->SetWriteOffset(writeOffset1); - output->Write(bitsUsed2-bitsUsed1); // Go back and write how many bits were used for the encoding - output->SetWriteOffset(writeOffset2); -} - -unsigned DataCompressor::DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ) -{ - HuffmanEncodingTree tree; - unsigned int bitsUsed, destinationSizeInBytes; - unsigned int decompressedBytes; - unsigned int frequencyTable[ 256 ]; - unsigned i; - - input->ReadCompressed(destinationSizeInBytes); - for (i=0; i < 256; i++) - input->ReadCompressed(frequencyTable[i]); - input->AlignReadToByteBoundary(); - if (input->Read(bitsUsed)==false) - { - // Read error -#ifdef _DEBUG - RakAssert(0); -#endif - return 0; - } - *output = (unsigned char*) rakMalloc_Ex(destinationSizeInBytes, _FILE_AND_LINE_); - tree.GenerateFromFrequencyTable(frequencyTable); - decompressedBytes=tree.DecodeArray(input, bitsUsed, destinationSizeInBytes, *output ); - RakAssert(decompressedBytes==destinationSizeInBytes); - return destinationSizeInBytes; -} +#include "DataCompressor.h" +#include "DS_HuffmanEncodingTree.h" +#include "RakAssert.h" +#include // Use string.h rather than memory.h for a console + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(DataCompressor,DataCompressor) + +void DataCompressor::Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ) +{ + // Don't use this for small files as you will just make them bigger! + RakAssert(sizeInBytes > 2048); + + unsigned int frequencyTable[ 256 ]; + unsigned int i; + memset(frequencyTable,0,256*sizeof(unsigned int)); + for (i=0; i < sizeInBytes; i++) + ++frequencyTable[userData[i]]; + HuffmanEncodingTree tree; + BitSize_t writeOffset1, writeOffset2, bitsUsed1, bitsUsed2; + tree.GenerateFromFrequencyTable(frequencyTable); + output->WriteCompressed(sizeInBytes); + for (i=0; i < 256; i++) + output->WriteCompressed(frequencyTable[i]); + output->AlignWriteToByteBoundary(); + writeOffset1=output->GetWriteOffset(); + output->Write((unsigned int)0); // Dummy value + bitsUsed1=output->GetNumberOfBitsUsed(); + tree.EncodeArray(userData, sizeInBytes, output); + bitsUsed2=output->GetNumberOfBitsUsed(); + writeOffset2=output->GetWriteOffset(); + output->SetWriteOffset(writeOffset1); + output->Write(bitsUsed2-bitsUsed1); // Go back and write how many bits were used for the encoding + output->SetWriteOffset(writeOffset2); +} + +unsigned DataCompressor::DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ) +{ + HuffmanEncodingTree tree; + unsigned int bitsUsed, destinationSizeInBytes; + unsigned int decompressedBytes; + unsigned int frequencyTable[ 256 ]; + unsigned i; + + input->ReadCompressed(destinationSizeInBytes); + for (i=0; i < 256; i++) + input->ReadCompressed(frequencyTable[i]); + input->AlignReadToByteBoundary(); + if (input->Read(bitsUsed)==false) + { + // Read error +#ifdef _DEBUG + RakAssert(0); +#endif + return 0; + } + *output = (unsigned char*) rakMalloc_Ex(destinationSizeInBytes, _FILE_AND_LINE_); + tree.GenerateFromFrequencyTable(frequencyTable); + decompressedBytes=tree.DecodeArray(input, bitsUsed, destinationSizeInBytes, *output ); + RakAssert(decompressedBytes==destinationSizeInBytes); + return destinationSizeInBytes; +} diff --git a/src/raknet/DataCompressor.h b/src/raknet/DataCompressor.h index 9152396..57f88ce 100755 --- a/src/raknet/DataCompressor.h +++ b/src/raknet/DataCompressor.h @@ -1,33 +1,33 @@ -/// \file DataCompressor.h -/// \brief DataCompressor does compression on a block of data. -/// \details Not very good compression, but it's small and fast so is something you can use per-message at runtime. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __DATA_COMPRESSOR_H -#define __DATA_COMPRESSOR_H - -#include "RakMemoryOverride.h" -#include "DS_HuffmanEncodingTree.h" -#include "Export.h" - -namespace RakNet -{ - -/// \brief Does compression on a block of data. Not very good compression, but it's small and fast so is something you can compute at runtime. -class RAK_DLL_EXPORT DataCompressor -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(DataCompressor) - - static void Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ); - static unsigned DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ); -}; - -} // namespace RakNet - -#endif +/// \file DataCompressor.h +/// \brief DataCompressor does compression on a block of data. +/// \details Not very good compression, but it's small and fast so is something you can use per-message at runtime. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __DATA_COMPRESSOR_H +#define __DATA_COMPRESSOR_H + +#include "RakMemoryOverride.h" +#include "DS_HuffmanEncodingTree.h" +#include "Export.h" + +namespace RakNet +{ + +/// \brief Does compression on a block of data. Not very good compression, but it's small and fast so is something you can compute at runtime. +class RAK_DLL_EXPORT DataCompressor +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(DataCompressor) + + static void Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ); + static unsigned DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ); +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/DirectoryDeltaTransfer.cpp b/src/raknet/DirectoryDeltaTransfer.cpp index e612c92..2386ff9 100755 --- a/src/raknet/DirectoryDeltaTransfer.cpp +++ b/src/raknet/DirectoryDeltaTransfer.cpp @@ -1,242 +1,242 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 - -#include "DirectoryDeltaTransfer.h" -#include "FileList.h" -#include "StringCompressor.h" -#include "RakPeerInterface.h" -#include "FileListTransfer.h" -#include "FileListTransferCBInterface.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "FileOperations.h" -#include "IncrementalReadInterface.h" - -using namespace RakNet; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -class DDTCallback : public FileListTransferCBInterface -{ -public: - unsigned subdirLen; - char outputSubdir[512]; - FileListTransferCBInterface *onFileCallback; - - DDTCallback() {} - virtual ~DDTCallback() {} - - virtual bool OnFile(OnFileStruct *onFileStruct) - { - char fullPathToDir[1024]; - - if (onFileStruct->fileName && onFileStruct->fileData && subdirLen < strlen(onFileStruct->fileName)) - { - strcpy(fullPathToDir, outputSubdir); - strcat(fullPathToDir, onFileStruct->fileName+subdirLen); - WriteFileWithDirectories(fullPathToDir, (char*)onFileStruct->fileData, (unsigned int ) onFileStruct->byteLengthOfThisFile); - } - else - fullPathToDir[0]=0; - - return onFileCallback->OnFile(onFileStruct); - } - - virtual void OnFileProgress(FileProgressStruct *fps) - { - char fullPathToDir[1024]; - - if (fps->onFileStruct->fileName && subdirLen < strlen(fps->onFileStruct->fileName)) - { - strcpy(fullPathToDir, outputSubdir); - strcat(fullPathToDir, fps->onFileStruct->fileName+subdirLen); - } - else - fullPathToDir[0]=0; - - onFileCallback->OnFileProgress(fps); - } - virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs) - { - return onFileCallback->OnDownloadComplete(dcs); - } -}; - -STATIC_FACTORY_DEFINITIONS(DirectoryDeltaTransfer,DirectoryDeltaTransfer); - -DirectoryDeltaTransfer::DirectoryDeltaTransfer() -{ - applicationDirectory[0]=0; - fileListTransfer=0; - availableUploads = RakNet::OP_NEW( _FILE_AND_LINE_ ); - priority=HIGH_PRIORITY; - orderingChannel=0; - incrementalReadInterface=0; -} -DirectoryDeltaTransfer::~DirectoryDeltaTransfer() -{ - RakNet::OP_DELETE(availableUploads, _FILE_AND_LINE_); -} -void DirectoryDeltaTransfer::SetFileListTransferPlugin(FileListTransfer *flt) -{ - if (fileListTransfer) - { - DataStructures::List fileListProgressList; - fileListTransfer->GetCallbacks(fileListProgressList); - unsigned int i; - for (i=0; i < fileListProgressList.Size(); i++) - availableUploads->RemoveCallback(fileListProgressList[i]); - } - - fileListTransfer=flt; - - if (flt) - { - DataStructures::List fileListProgressList; - flt->GetCallbacks(fileListProgressList); - unsigned int i; - for (i=0; i < fileListProgressList.Size(); i++) - availableUploads->AddCallback(fileListProgressList[i]); - } - else - { - availableUploads->ClearCallbacks(); - } -} -void DirectoryDeltaTransfer::SetApplicationDirectory(const char *pathToApplication) -{ - if (pathToApplication==0 || pathToApplication[0]==0) - applicationDirectory[0]=0; - else - { - strncpy(applicationDirectory, pathToApplication, 510); - if (applicationDirectory[strlen(applicationDirectory)-1]!='/' && applicationDirectory[strlen(applicationDirectory)-1]!='\\') - strcat(applicationDirectory, "/"); - applicationDirectory[511]=0; - } -} -void DirectoryDeltaTransfer::SetUploadSendParameters(PacketPriority _priority, char _orderingChannel) -{ - priority=_priority; - orderingChannel=_orderingChannel; -} -void DirectoryDeltaTransfer::AddUploadsFromSubdirectory(const char *subdir) -{ - availableUploads->AddFilesFromDirectory(applicationDirectory, subdir, true, false, true, FileListNodeContext(0,0)); -} -unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb) -{ - RakAssert(host!=UNASSIGNED_SYSTEM_ADDRESS); - - DDTCallback *transferCallback; - - localFiles.AddCallback(cb); - - // Prepare the callback data - transferCallback = RakNet::OP_NEW( _FILE_AND_LINE_ ); - if (subdir && subdir[0]) - { - transferCallback->subdirLen=(unsigned int)strlen(subdir); - if (subdir[transferCallback->subdirLen-1]!='/' && subdir[transferCallback->subdirLen-1]!='\\') - transferCallback->subdirLen++; - } - else - transferCallback->subdirLen=0; - if (prependAppDirToOutputSubdir) - strcpy(transferCallback->outputSubdir, applicationDirectory); - else - transferCallback->outputSubdir[0]=0; - if (outputSubdir) - strcat(transferCallback->outputSubdir, outputSubdir); - if (transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='/' && transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='\\') - strcat(transferCallback->outputSubdir, "/"); - transferCallback->onFileCallback=onFileCallback; - - // Setup the transfer plugin to get the response to this download request - unsigned short setId = fileListTransfer->SetupReceive(transferCallback, true, host); - - // Send to the host, telling it to process this request - RakNet::BitStream outBitstream; - outBitstream.Write((MessageID)ID_DDT_DOWNLOAD_REQUEST); - outBitstream.Write(setId); - StringCompressor::Instance()->EncodeString(subdir, 256, &outBitstream); - StringCompressor::Instance()->EncodeString(outputSubdir, 256, &outBitstream); - localFiles.Serialize(&outBitstream); - SendUnified(&outBitstream, _priority, RELIABLE_ORDERED, _orderingChannel, host, false); - - return setId; -} -unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb) -{ - FileList localFiles; - // Get a hash of all the files that we already have (if any) - localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0)); - return DownloadFromSubdirectory(localFiles, subdir, outputSubdir, prependAppDirToOutputSubdir, host, onFileCallback, _priority, _orderingChannel, cb); -} -void DirectoryDeltaTransfer::GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir) -{ - localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0)); -} -void DirectoryDeltaTransfer::ClearUploads(void) -{ - availableUploads->Clear(); -} -void DirectoryDeltaTransfer::OnDownloadRequest(Packet *packet) -{ - char subdir[256]; - char remoteSubdir[256]; - RakNet::BitStream inBitstream(packet->data, packet->length, false); - FileList remoteFileHash; - FileList delta; - unsigned short setId; - inBitstream.IgnoreBits(8); - inBitstream.Read(setId); - StringCompressor::Instance()->DecodeString(subdir, 256, &inBitstream); - StringCompressor::Instance()->DecodeString(remoteSubdir, 256, &inBitstream); - if (remoteFileHash.Deserialize(&inBitstream)==false) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return; - } - - availableUploads->GetDeltaToCurrent(&remoteFileHash, &delta, subdir, remoteSubdir); - if (incrementalReadInterface==0) - delta.PopulateDataFromDisk(applicationDirectory, true, false, true); - else - delta.FlagFilesAsReferences(); - - // This will call the ddtCallback interface that was passed to FileListTransfer::SetupReceive on the remote system - fileListTransfer->Send(&delta, rakPeerInterface, packet->systemAddress, setId, priority, orderingChannel, incrementalReadInterface, chunkSize); -} -PluginReceiveResult DirectoryDeltaTransfer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_DDT_DOWNLOAD_REQUEST: - OnDownloadRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - return RR_CONTINUE_PROCESSING; -} - -unsigned DirectoryDeltaTransfer::GetNumberOfFilesForUpload(void) const -{ - return availableUploads->fileList.Size(); -} - -void DirectoryDeltaTransfer::SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) -{ - incrementalReadInterface=_incrementalReadInterface; - chunkSize=_chunkSize; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 + +#include "DirectoryDeltaTransfer.h" +#include "FileList.h" +#include "StringCompressor.h" +#include "RakPeerInterface.h" +#include "FileListTransfer.h" +#include "FileListTransferCBInterface.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "FileOperations.h" +#include "IncrementalReadInterface.h" + +using namespace RakNet; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +class DDTCallback : public FileListTransferCBInterface +{ +public: + unsigned subdirLen; + char outputSubdir[512]; + FileListTransferCBInterface *onFileCallback; + + DDTCallback() {} + virtual ~DDTCallback() {} + + virtual bool OnFile(OnFileStruct *onFileStruct) + { + char fullPathToDir[1024]; + + if (onFileStruct->fileName && onFileStruct->fileData && subdirLen < strlen(onFileStruct->fileName)) + { + strcpy(fullPathToDir, outputSubdir); + strcat(fullPathToDir, onFileStruct->fileName+subdirLen); + WriteFileWithDirectories(fullPathToDir, (char*)onFileStruct->fileData, (unsigned int ) onFileStruct->byteLengthOfThisFile); + } + else + fullPathToDir[0]=0; + + return onFileCallback->OnFile(onFileStruct); + } + + virtual void OnFileProgress(FileProgressStruct *fps) + { + char fullPathToDir[1024]; + + if (fps->onFileStruct->fileName && subdirLen < strlen(fps->onFileStruct->fileName)) + { + strcpy(fullPathToDir, outputSubdir); + strcat(fullPathToDir, fps->onFileStruct->fileName+subdirLen); + } + else + fullPathToDir[0]=0; + + onFileCallback->OnFileProgress(fps); + } + virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs) + { + return onFileCallback->OnDownloadComplete(dcs); + } +}; + +STATIC_FACTORY_DEFINITIONS(DirectoryDeltaTransfer,DirectoryDeltaTransfer); + +DirectoryDeltaTransfer::DirectoryDeltaTransfer() +{ + applicationDirectory[0]=0; + fileListTransfer=0; + availableUploads = RakNet::OP_NEW( _FILE_AND_LINE_ ); + priority=HIGH_PRIORITY; + orderingChannel=0; + incrementalReadInterface=0; +} +DirectoryDeltaTransfer::~DirectoryDeltaTransfer() +{ + RakNet::OP_DELETE(availableUploads, _FILE_AND_LINE_); +} +void DirectoryDeltaTransfer::SetFileListTransferPlugin(FileListTransfer *flt) +{ + if (fileListTransfer) + { + DataStructures::List fileListProgressList; + fileListTransfer->GetCallbacks(fileListProgressList); + unsigned int i; + for (i=0; i < fileListProgressList.Size(); i++) + availableUploads->RemoveCallback(fileListProgressList[i]); + } + + fileListTransfer=flt; + + if (flt) + { + DataStructures::List fileListProgressList; + flt->GetCallbacks(fileListProgressList); + unsigned int i; + for (i=0; i < fileListProgressList.Size(); i++) + availableUploads->AddCallback(fileListProgressList[i]); + } + else + { + availableUploads->ClearCallbacks(); + } +} +void DirectoryDeltaTransfer::SetApplicationDirectory(const char *pathToApplication) +{ + if (pathToApplication==0 || pathToApplication[0]==0) + applicationDirectory[0]=0; + else + { + strncpy(applicationDirectory, pathToApplication, 510); + if (applicationDirectory[strlen(applicationDirectory)-1]!='/' && applicationDirectory[strlen(applicationDirectory)-1]!='\\') + strcat(applicationDirectory, "/"); + applicationDirectory[511]=0; + } +} +void DirectoryDeltaTransfer::SetUploadSendParameters(PacketPriority _priority, char _orderingChannel) +{ + priority=_priority; + orderingChannel=_orderingChannel; +} +void DirectoryDeltaTransfer::AddUploadsFromSubdirectory(const char *subdir) +{ + availableUploads->AddFilesFromDirectory(applicationDirectory, subdir, true, false, true, FileListNodeContext(0,0)); +} +unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb) +{ + RakAssert(host!=UNASSIGNED_SYSTEM_ADDRESS); + + DDTCallback *transferCallback; + + localFiles.AddCallback(cb); + + // Prepare the callback data + transferCallback = RakNet::OP_NEW( _FILE_AND_LINE_ ); + if (subdir && subdir[0]) + { + transferCallback->subdirLen=(unsigned int)strlen(subdir); + if (subdir[transferCallback->subdirLen-1]!='/' && subdir[transferCallback->subdirLen-1]!='\\') + transferCallback->subdirLen++; + } + else + transferCallback->subdirLen=0; + if (prependAppDirToOutputSubdir) + strcpy(transferCallback->outputSubdir, applicationDirectory); + else + transferCallback->outputSubdir[0]=0; + if (outputSubdir) + strcat(transferCallback->outputSubdir, outputSubdir); + if (transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='/' && transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='\\') + strcat(transferCallback->outputSubdir, "/"); + transferCallback->onFileCallback=onFileCallback; + + // Setup the transfer plugin to get the response to this download request + unsigned short setId = fileListTransfer->SetupReceive(transferCallback, true, host); + + // Send to the host, telling it to process this request + RakNet::BitStream outBitstream; + outBitstream.Write((MessageID)ID_DDT_DOWNLOAD_REQUEST); + outBitstream.Write(setId); + StringCompressor::Instance()->EncodeString(subdir, 256, &outBitstream); + StringCompressor::Instance()->EncodeString(outputSubdir, 256, &outBitstream); + localFiles.Serialize(&outBitstream); + SendUnified(&outBitstream, _priority, RELIABLE_ORDERED, _orderingChannel, host, false); + + return setId; +} +unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb) +{ + FileList localFiles; + // Get a hash of all the files that we already have (if any) + localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0)); + return DownloadFromSubdirectory(localFiles, subdir, outputSubdir, prependAppDirToOutputSubdir, host, onFileCallback, _priority, _orderingChannel, cb); +} +void DirectoryDeltaTransfer::GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir) +{ + localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0)); +} +void DirectoryDeltaTransfer::ClearUploads(void) +{ + availableUploads->Clear(); +} +void DirectoryDeltaTransfer::OnDownloadRequest(Packet *packet) +{ + char subdir[256]; + char remoteSubdir[256]; + RakNet::BitStream inBitstream(packet->data, packet->length, false); + FileList remoteFileHash; + FileList delta; + unsigned short setId; + inBitstream.IgnoreBits(8); + inBitstream.Read(setId); + StringCompressor::Instance()->DecodeString(subdir, 256, &inBitstream); + StringCompressor::Instance()->DecodeString(remoteSubdir, 256, &inBitstream); + if (remoteFileHash.Deserialize(&inBitstream)==false) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return; + } + + availableUploads->GetDeltaToCurrent(&remoteFileHash, &delta, subdir, remoteSubdir); + if (incrementalReadInterface==0) + delta.PopulateDataFromDisk(applicationDirectory, true, false, true); + else + delta.FlagFilesAsReferences(); + + // This will call the ddtCallback interface that was passed to FileListTransfer::SetupReceive on the remote system + fileListTransfer->Send(&delta, rakPeerInterface, packet->systemAddress, setId, priority, orderingChannel, incrementalReadInterface, chunkSize); +} +PluginReceiveResult DirectoryDeltaTransfer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_DDT_DOWNLOAD_REQUEST: + OnDownloadRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + return RR_CONTINUE_PROCESSING; +} + +unsigned DirectoryDeltaTransfer::GetNumberOfFilesForUpload(void) const +{ + return availableUploads->fileList.Size(); +} + +void DirectoryDeltaTransfer::SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) +{ + incrementalReadInterface=_incrementalReadInterface; + chunkSize=_chunkSize; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/DirectoryDeltaTransfer.h b/src/raknet/DirectoryDeltaTransfer.h index 5c78622..1162e28 100755 --- a/src/raknet/DirectoryDeltaTransfer.h +++ b/src/raknet/DirectoryDeltaTransfer.h @@ -1,164 +1,164 @@ -/// \file DirectoryDeltaTransfer.h -/// \brief Simple class to send changes between directories. -/// \details In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __DIRECTORY_DELTA_TRANSFER_H -#define __DIRECTORY_DELTA_TRANSFER_H - -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "DS_Map.h" -#include "PacketPriority.h" - -/// \defgroup DIRECTORY_DELTA_TRANSFER_GROUP DirectoryDeltaTransfer -/// \brief Simple class to send changes between directories -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief Simple class to send changes between directories. In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. -/// \details -/// \sa AutopatcherClient class for database driven patching, including binary deltas and search by date. -/// -/// To use, first set the path to your application. For example "C:/Games/MyRPG/"
      -/// To allow other systems to download files, call AddUploadsFromSubdirectory, where the parameter is a path relative
      -/// to the path to your application. This includes subdirectories.
      -/// For example:
      -/// SetApplicationDirectory("C:/Games/MyRPG/");
      -/// AddUploadsFromSubdirectory("Mods/Skins/");
      -/// would allow downloads from
      -/// "C:/Games/MyRPG/Mods/Skins/*.*" as well as "C:/Games/MyRPG/Mods/Skins/Level1/*.*"
      -/// It would NOT allow downloads from C:/Games/MyRPG/Levels, nor would it allow downloads from C:/Windows
      -/// While pathToApplication can be anything you want, applicationSubdirectory must match either partially or fully between systems. -/// \ingroup DIRECTORY_DELTA_TRANSFER_GROUP - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -class FileList; -struct Packet; -struct InternalPacket; -struct DownloadRequest; -class FileListTransfer; -class FileListTransferCBInterface; -class FileListProgress; -class IncrementalReadInterface; - -class RAK_DLL_EXPORT DirectoryDeltaTransfer : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(DirectoryDeltaTransfer) - - // Constructor - DirectoryDeltaTransfer(); - - // Destructor - virtual ~DirectoryDeltaTransfer(); - - /// \brief This plugin has a dependency on the FileListTransfer plugin, which it uses to actually send the files. - /// \details So you need an instance of that plugin registered with RakPeerInterface, and a pointer to that interface should be passed here. - /// \param[in] flt A pointer to a registered instance of FileListTransfer - void SetFileListTransferPlugin(FileListTransfer *flt); - - /// \brief Set the local root directory to base all file uploads and downloads off of. - /// \param[in] pathToApplication This path will be prepended to \a applicationSubdirectory in AddUploadsFromSubdirectory to find the actual path on disk. - void SetApplicationDirectory(const char *pathToApplication); - - /// \brief What parameters to use for the RakPeerInterface::Send() call when uploading files. - /// \param[in] _priority See RakPeerInterface::Send() - /// \param[in] _orderingChannel See RakPeerInterface::Send() - void SetUploadSendParameters(PacketPriority _priority, char _orderingChannel); - - /// \brief Add all files in the specified subdirectory recursively. - /// \details \a subdir is appended to \a pathToApplication in SetApplicationDirectory(). - /// All files in the resultant directory and subdirectories are then hashed so that users can download them. - /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin - /// \param[in] subdir Concatenated with pathToApplication to form the final path from which to allow uploads. - void AddUploadsFromSubdirectory(const char *subdir); - - /// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory. - /// \details \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory - /// Therefore, - /// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"... - /// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"... - /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin - /// \note Blocking. Will block while hashes of the local files are generated - /// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir. - /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. - /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. - /// \param[in] host The address of the remote system to send the message to. - /// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done - /// \param[in] _priority See RakPeerInterface::Send() - /// \param[in] _orderingChannel See RakPeerInterface::Send() - /// \param[in] cb Callback to get progress updates. Pass 0 to not use. - /// \return A set ID, identifying this download set. Returns 65535 on host unreachable. - unsigned short DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb); - - /// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory. - /// \details \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory - /// Therefore, - /// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"... - /// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"... - /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin - /// \note Nonblocking, but requires call to GenerateHashes() - /// \param[in] localFiles Hashes of local files already on the harddrive. Populate with GenerateHashes(), which you may wish to call from a thread - /// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir. - /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. - /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. - /// \param[in] host The address of the remote system to send the message to. - /// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done - /// \param[in] _priority See RakPeerInterface::Send() - /// \param[in] _orderingChannel See RakPeerInterface::Send() - /// \param[in] cb Callback to get progress updates. Pass 0 to not use. - /// \return A set ID, identifying this download set. Returns 65535 on host unreachable. - unsigned short DownloadFromSubdirectory(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb); - - /// Hash files already on the harddrive, in preparation for a call to DownloadFromSubdirectory(). Passed to second version of DownloadFromSubdirectory() - /// This is slow, and it is exposed so you can call it from a thread before calling DownloadFromSubdirectory() - /// \param[out] localFiles List of hashed files populated from \a outputSubdir and \a prependAppDirToOutputSubdir - /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. - /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. - void GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir); - - /// \brief Clear all allowed uploads previously set with AddUploadsFromSubdirectory - void ClearUploads(void); - - /// \brief Returns how many files are available for upload - /// \return How many files are available for upload - unsigned GetNumberOfFilesForUpload(void) const; - - /// \brief Normally, if a remote system requests files, those files are all loaded into memory and sent immediately. - /// \details This function allows the files to be read in incremental chunks, saving memory - /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, filePullInterface will be used to read the file in chunks of size \a chunkSize - /// \param[in] _chunkSize How large of a block of a file to send at once - void SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); -protected: - void OnDownloadRequest(Packet *packet); - - char applicationDirectory[512]; - FileListTransfer *fileListTransfer; - FileList *availableUploads; - PacketPriority priority; - char orderingChannel; - IncrementalReadInterface *incrementalReadInterface; - unsigned int chunkSize; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file DirectoryDeltaTransfer.h +/// \brief Simple class to send changes between directories. +/// \details In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __DIRECTORY_DELTA_TRANSFER_H +#define __DIRECTORY_DELTA_TRANSFER_H + +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "DS_Map.h" +#include "PacketPriority.h" + +/// \defgroup DIRECTORY_DELTA_TRANSFER_GROUP DirectoryDeltaTransfer +/// \brief Simple class to send changes between directories +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief Simple class to send changes between directories. In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. +/// \details +/// \sa AutopatcherClient class for database driven patching, including binary deltas and search by date. +/// +/// To use, first set the path to your application. For example "C:/Games/MyRPG/"
      +/// To allow other systems to download files, call AddUploadsFromSubdirectory, where the parameter is a path relative
      +/// to the path to your application. This includes subdirectories.
      +/// For example:
      +/// SetApplicationDirectory("C:/Games/MyRPG/");
      +/// AddUploadsFromSubdirectory("Mods/Skins/");
      +/// would allow downloads from
      +/// "C:/Games/MyRPG/Mods/Skins/*.*" as well as "C:/Games/MyRPG/Mods/Skins/Level1/*.*"
      +/// It would NOT allow downloads from C:/Games/MyRPG/Levels, nor would it allow downloads from C:/Windows
      +/// While pathToApplication can be anything you want, applicationSubdirectory must match either partially or fully between systems. +/// \ingroup DIRECTORY_DELTA_TRANSFER_GROUP + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +class FileList; +struct Packet; +struct InternalPacket; +struct DownloadRequest; +class FileListTransfer; +class FileListTransferCBInterface; +class FileListProgress; +class IncrementalReadInterface; + +class RAK_DLL_EXPORT DirectoryDeltaTransfer : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(DirectoryDeltaTransfer) + + // Constructor + DirectoryDeltaTransfer(); + + // Destructor + virtual ~DirectoryDeltaTransfer(); + + /// \brief This plugin has a dependency on the FileListTransfer plugin, which it uses to actually send the files. + /// \details So you need an instance of that plugin registered with RakPeerInterface, and a pointer to that interface should be passed here. + /// \param[in] flt A pointer to a registered instance of FileListTransfer + void SetFileListTransferPlugin(FileListTransfer *flt); + + /// \brief Set the local root directory to base all file uploads and downloads off of. + /// \param[in] pathToApplication This path will be prepended to \a applicationSubdirectory in AddUploadsFromSubdirectory to find the actual path on disk. + void SetApplicationDirectory(const char *pathToApplication); + + /// \brief What parameters to use for the RakPeerInterface::Send() call when uploading files. + /// \param[in] _priority See RakPeerInterface::Send() + /// \param[in] _orderingChannel See RakPeerInterface::Send() + void SetUploadSendParameters(PacketPriority _priority, char _orderingChannel); + + /// \brief Add all files in the specified subdirectory recursively. + /// \details \a subdir is appended to \a pathToApplication in SetApplicationDirectory(). + /// All files in the resultant directory and subdirectories are then hashed so that users can download them. + /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin + /// \param[in] subdir Concatenated with pathToApplication to form the final path from which to allow uploads. + void AddUploadsFromSubdirectory(const char *subdir); + + /// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory. + /// \details \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory + /// Therefore, + /// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"... + /// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"... + /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin + /// \note Blocking. Will block while hashes of the local files are generated + /// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir. + /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. + /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. + /// \param[in] host The address of the remote system to send the message to. + /// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done + /// \param[in] _priority See RakPeerInterface::Send() + /// \param[in] _orderingChannel See RakPeerInterface::Send() + /// \param[in] cb Callback to get progress updates. Pass 0 to not use. + /// \return A set ID, identifying this download set. Returns 65535 on host unreachable. + unsigned short DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb); + + /// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory. + /// \details \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory + /// Therefore, + /// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"... + /// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"... + /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin + /// \note Nonblocking, but requires call to GenerateHashes() + /// \param[in] localFiles Hashes of local files already on the harddrive. Populate with GenerateHashes(), which you may wish to call from a thread + /// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir. + /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. + /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. + /// \param[in] host The address of the remote system to send the message to. + /// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done + /// \param[in] _priority See RakPeerInterface::Send() + /// \param[in] _orderingChannel See RakPeerInterface::Send() + /// \param[in] cb Callback to get progress updates. Pass 0 to not use. + /// \return A set ID, identifying this download set. Returns 65535 on host unreachable. + unsigned short DownloadFromSubdirectory(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb); + + /// Hash files already on the harddrive, in preparation for a call to DownloadFromSubdirectory(). Passed to second version of DownloadFromSubdirectory() + /// This is slow, and it is exposed so you can call it from a thread before calling DownloadFromSubdirectory() + /// \param[out] localFiles List of hashed files populated from \a outputSubdir and \a prependAppDirToOutputSubdir + /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. + /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. + void GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir); + + /// \brief Clear all allowed uploads previously set with AddUploadsFromSubdirectory + void ClearUploads(void); + + /// \brief Returns how many files are available for upload + /// \return How many files are available for upload + unsigned GetNumberOfFilesForUpload(void) const; + + /// \brief Normally, if a remote system requests files, those files are all loaded into memory and sent immediately. + /// \details This function allows the files to be read in incremental chunks, saving memory + /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, filePullInterface will be used to read the file in chunks of size \a chunkSize + /// \param[in] _chunkSize How large of a block of a file to send at once + void SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); +protected: + void OnDownloadRequest(Packet *packet); + + char applicationDirectory[512]; + FileListTransfer *fileListTransfer; + FileList *availableUploads; + PacketPriority priority; + char orderingChannel; + IncrementalReadInterface *incrementalReadInterface; + unsigned int chunkSize; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/DynDNS.cpp b/src/raknet/DynDNS.cpp index 3e189ee..bd51714 100755 --- a/src/raknet/DynDNS.cpp +++ b/src/raknet/DynDNS.cpp @@ -1,236 +1,236 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "TCPInterface.h" -#include "SocketLayer.h" -#include "DynDNS.h" -#include "GetTime.h" - -using namespace RakNet; - -struct DynDnsResult -{ - const char *description; - const char *code; - DynDnsResultCode resultCode; -}; - -DynDnsResult resultTable[13] = -{ - // See http://www.dyndns.com/developers/specs/flow.pdf - {"DNS update success.\nPlease wait up to 60 seconds for the change to take effect.\n", "good", RC_SUCCESS}, // Even with success, it takes time for the cache to update! - {"No change", "nochg", RC_NO_CHANGE}, - {"Host has been blocked. You will need to contact DynDNS to reenable.", "abuse", RC_ABUSE}, - {"Useragent is blocked", "badagent", RC_BAD_AGENT}, - {"Username/password pair bad", "badauth", RC_BAD_AUTH}, - {"Bad system parameter", "badsys", RC_BAD_SYS}, - {"DNS inconsistency", "dnserr", RC_DNS_ERROR}, - {"Paid account feature", "!donator", RC_NOT_DONATOR}, - {"No such host in system", "nohost", RC_NO_HOST}, - {"Invalid hostname format", "notfqdn", RC_NOT_FQDN}, - {"Serious error", "numhost", RC_NUM_HOST}, - {"This host exists, but does not belong to you", "!yours", RC_NOT_YOURS}, - {"911", "911", RC_911}, -}; -DynDNS::DynDNS() -{ - connectPhase=CP_IDLE; - tcp=0; -} -DynDNS::~DynDNS() -{ - if (tcp) - RakNet::OP_DELETE(tcp, _FILE_AND_LINE_); -} -void DynDNS::Stop(void) -{ - tcp->Stop(); - connectPhase = CP_IDLE; - RakNet::OP_DELETE(tcp, _FILE_AND_LINE_); - tcp=0; -} - - -// newIPAddress is optional - if left out, DynDNS will use whatever it receives -void DynDNS::UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword ) -{ - myIPStr[0]=0; - - if (tcp==0) - tcp = RakNet::OP_NEW(_FILE_AND_LINE_); - connectPhase = CP_IDLE; - host = dnsHost; - - if (tcp->Start(0, 1)==false) - { - SetCompleted(RC_TCP_FAILED_TO_START, "TCP failed to start"); - return; - } - - connectPhase = CP_CONNECTING_TO_CHECKIP; - tcp->Connect("checkip.dyndns.org", 80, false); - - // See https://www.dyndns.com/developers/specs/syntax.html - getString="GET /nic/update?hostname="; - getString+=dnsHost; - if (newIPAddress) - { - getString+="&myip="; - getString+=newIPAddress; - } - getString+="&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG HTTP/1.0\n"; - getString+="Host: members.dyndns.org\n"; - getString+="Authorization: Basic "; - char outputData[512]; - TCPInterface::Base64Encoding(usernameAndPassword, (int) strlen(usernameAndPassword), outputData); - getString+=outputData; - getString+="User-Agent: Jenkins Software LLC - PC - 1.0\n\n"; -} -void DynDNS::Update(void) -{ - if (connectPhase==CP_IDLE) - return; - - serverAddress=tcp->HasFailedConnectionAttempt(); - if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - SetCompleted(RC_TCP_DID_NOT_CONNECT, "Could not connect to DynDNS"); - return; - } - - serverAddress=tcp->HasCompletedConnectionAttempt(); - if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - if (connectPhase == CP_CONNECTING_TO_CHECKIP) - { - checkIpAddress=serverAddress; - connectPhase = CP_WAITING_FOR_CHECKIP_RESPONSE; - tcp->Send("GET\n\n", (unsigned int) strlen("GET\n\n"), serverAddress, false); // Needs 2 newlines! This is not documented and wasted a lot of my time - } - else - { - connectPhase = CP_WAITING_FOR_DYNDNS_RESPONSE; - tcp->Send(getString.C_String(), (unsigned int) getString.GetLength(), serverAddress, false); - } - phaseTimeout=RakNet::GetTime()+1000; - } - - if (connectPhase==CP_WAITING_FOR_CHECKIP_RESPONSE && RakNet::GetTime()>phaseTimeout) - { - connectPhase = CP_CONNECTING_TO_DYNDNS; - tcp->CloseConnection(checkIpAddress); - tcp->Connect("members.dyndns.org", 80, false); - } - else if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE && RakNet::GetTime()>phaseTimeout) - { - SetCompleted(RC_DYNDNS_TIMEOUT, "DynDNS did not respond"); - return; - } - - Packet *packet = tcp->Receive(); - if (packet) - { - if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE) - { - unsigned int i; - - char *result; - result=strstr((char*) packet->data, "Connection: close"); - if (result!=0) - { - result+=strlen("Connection: close"); - while (*result && (*result=='\r') || (*result=='\n') || (*result==' ') ) - result++; - for (i=0; i < 13; i++) - { - if (strncmp(resultTable[i].code, result, strlen(resultTable[i].code))==0) - { - if (resultTable[i].resultCode==RC_SUCCESS) - { - // Read my external IP into myIPStr - // Advance until we hit a number - while (*result && ((*result<'0') || (*result>'9')) ) - result++; - if (*result) - { - SystemAddress parser; - parser.FromString(result); - parser.ToString(false, myIPStr); - } - } - tcp->DeallocatePacket(packet); - SetCompleted(resultTable[i].resultCode, resultTable[i].description); - break; - } - } - if (i==13) - { - tcp->DeallocatePacket(packet); - SetCompleted(RC_UNKNOWN_RESULT, "DynDNS returned unknown result"); - } - } - else - { - tcp->DeallocatePacket(packet); - SetCompleted(RC_PARSING_FAILURE, "Parsing failure on returned string from DynDNS"); - } - - return; - } - else - { - /* - HTTP/1.1 200 OK - Content-Type: text/html - Server: DynDNS-CheckIP/1.0 - Connection: close - Cache-Control: no-cache - Pragma: no-cache - Content-Length: 105 - - Current IP CheckCurrent IP Address: 98.1 - 89.219.22 - - - Connection to host lost. - */ - - char *result; - result=strstr((char*) packet->data, "Current IP Address: "); - if (result!=0) - { - result+=strlen("Current IP Address: "); - SystemAddress myIp; - myIp.FromString(result); - myIp.ToString(false, myIPStr); - - // Resolve DNS we are setting. If equal to current then abort - const char *existingHost = ( char* ) SocketLayer::DomainNameToIP( host.C_String() ); - if (existingHost && strcmp(existingHost, myIPStr)==0) - { - // DynDNS considers setting the IP to what it is already set abuse - tcp->DeallocatePacket(packet); - SetCompleted(RC_DNS_ALREADY_SET, "No action needed"); - return; - } - } - - tcp->DeallocatePacket(packet); - tcp->CloseConnection(packet->systemAddress); - - connectPhase = CP_CONNECTING_TO_DYNDNS; - tcp->Connect("members.dyndns.org", 80, false); - } - } - - if (tcp->HasLostConnection()!=UNASSIGNED_SYSTEM_ADDRESS) - { - if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE) - { - SetCompleted(RC_CONNECTION_LOST_WITHOUT_RESPONSE, "Connection lost to DynDNS during GET operation"); - } - } -} - - -#endif // _RAKNET_SUPPORT_DynDNS +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "TCPInterface.h" +#include "SocketLayer.h" +#include "DynDNS.h" +#include "GetTime.h" + +using namespace RakNet; + +struct DynDnsResult +{ + const char *description; + const char *code; + DynDnsResultCode resultCode; +}; + +DynDnsResult resultTable[13] = +{ + // See http://www.dyndns.com/developers/specs/flow.pdf + {"DNS update success.\nPlease wait up to 60 seconds for the change to take effect.\n", "good", RC_SUCCESS}, // Even with success, it takes time for the cache to update! + {"No change", "nochg", RC_NO_CHANGE}, + {"Host has been blocked. You will need to contact DynDNS to reenable.", "abuse", RC_ABUSE}, + {"Useragent is blocked", "badagent", RC_BAD_AGENT}, + {"Username/password pair bad", "badauth", RC_BAD_AUTH}, + {"Bad system parameter", "badsys", RC_BAD_SYS}, + {"DNS inconsistency", "dnserr", RC_DNS_ERROR}, + {"Paid account feature", "!donator", RC_NOT_DONATOR}, + {"No such host in system", "nohost", RC_NO_HOST}, + {"Invalid hostname format", "notfqdn", RC_NOT_FQDN}, + {"Serious error", "numhost", RC_NUM_HOST}, + {"This host exists, but does not belong to you", "!yours", RC_NOT_YOURS}, + {"911", "911", RC_911}, +}; +DynDNS::DynDNS() +{ + connectPhase=CP_IDLE; + tcp=0; +} +DynDNS::~DynDNS() +{ + if (tcp) + RakNet::OP_DELETE(tcp, _FILE_AND_LINE_); +} +void DynDNS::Stop(void) +{ + tcp->Stop(); + connectPhase = CP_IDLE; + RakNet::OP_DELETE(tcp, _FILE_AND_LINE_); + tcp=0; +} + + +// newIPAddress is optional - if left out, DynDNS will use whatever it receives +void DynDNS::UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword ) +{ + myIPStr[0]=0; + + if (tcp==0) + tcp = RakNet::OP_NEW(_FILE_AND_LINE_); + connectPhase = CP_IDLE; + host = dnsHost; + + if (tcp->Start(0, 1)==false) + { + SetCompleted(RC_TCP_FAILED_TO_START, "TCP failed to start"); + return; + } + + connectPhase = CP_CONNECTING_TO_CHECKIP; + tcp->Connect("checkip.dyndns.org", 80, false); + + // See https://www.dyndns.com/developers/specs/syntax.html + getString="GET /nic/update?hostname="; + getString+=dnsHost; + if (newIPAddress) + { + getString+="&myip="; + getString+=newIPAddress; + } + getString+="&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG HTTP/1.0\n"; + getString+="Host: members.dyndns.org\n"; + getString+="Authorization: Basic "; + char outputData[512]; + TCPInterface::Base64Encoding(usernameAndPassword, (int) strlen(usernameAndPassword), outputData); + getString+=outputData; + getString+="User-Agent: Jenkins Software LLC - PC - 1.0\n\n"; +} +void DynDNS::Update(void) +{ + if (connectPhase==CP_IDLE) + return; + + serverAddress=tcp->HasFailedConnectionAttempt(); + if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + SetCompleted(RC_TCP_DID_NOT_CONNECT, "Could not connect to DynDNS"); + return; + } + + serverAddress=tcp->HasCompletedConnectionAttempt(); + if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + if (connectPhase == CP_CONNECTING_TO_CHECKIP) + { + checkIpAddress=serverAddress; + connectPhase = CP_WAITING_FOR_CHECKIP_RESPONSE; + tcp->Send("GET\n\n", (unsigned int) strlen("GET\n\n"), serverAddress, false); // Needs 2 newlines! This is not documented and wasted a lot of my time + } + else + { + connectPhase = CP_WAITING_FOR_DYNDNS_RESPONSE; + tcp->Send(getString.C_String(), (unsigned int) getString.GetLength(), serverAddress, false); + } + phaseTimeout=RakNet::GetTime()+1000; + } + + if (connectPhase==CP_WAITING_FOR_CHECKIP_RESPONSE && RakNet::GetTime()>phaseTimeout) + { + connectPhase = CP_CONNECTING_TO_DYNDNS; + tcp->CloseConnection(checkIpAddress); + tcp->Connect("members.dyndns.org", 80, false); + } + else if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE && RakNet::GetTime()>phaseTimeout) + { + SetCompleted(RC_DYNDNS_TIMEOUT, "DynDNS did not respond"); + return; + } + + Packet *packet = tcp->Receive(); + if (packet) + { + if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE) + { + unsigned int i; + + char *result; + result=strstr((char*) packet->data, "Connection: close"); + if (result!=0) + { + result+=strlen("Connection: close"); + while (*result && (*result=='\r') || (*result=='\n') || (*result==' ') ) + result++; + for (i=0; i < 13; i++) + { + if (strncmp(resultTable[i].code, result, strlen(resultTable[i].code))==0) + { + if (resultTable[i].resultCode==RC_SUCCESS) + { + // Read my external IP into myIPStr + // Advance until we hit a number + while (*result && ((*result<'0') || (*result>'9')) ) + result++; + if (*result) + { + SystemAddress parser; + parser.FromString(result); + parser.ToString(false, myIPStr); + } + } + tcp->DeallocatePacket(packet); + SetCompleted(resultTable[i].resultCode, resultTable[i].description); + break; + } + } + if (i==13) + { + tcp->DeallocatePacket(packet); + SetCompleted(RC_UNKNOWN_RESULT, "DynDNS returned unknown result"); + } + } + else + { + tcp->DeallocatePacket(packet); + SetCompleted(RC_PARSING_FAILURE, "Parsing failure on returned string from DynDNS"); + } + + return; + } + else + { + /* + HTTP/1.1 200 OK + Content-Type: text/html + Server: DynDNS-CheckIP/1.0 + Connection: close + Cache-Control: no-cache + Pragma: no-cache + Content-Length: 105 + + Current IP CheckCurrent IP Address: 98.1 + 89.219.22 + + + Connection to host lost. + */ + + char *result; + result=strstr((char*) packet->data, "Current IP Address: "); + if (result!=0) + { + result+=strlen("Current IP Address: "); + SystemAddress myIp; + myIp.FromString(result); + myIp.ToString(false, myIPStr); + + // Resolve DNS we are setting. If equal to current then abort + const char *existingHost = ( char* ) SocketLayer::DomainNameToIP( host.C_String() ); + if (existingHost && strcmp(existingHost, myIPStr)==0) + { + // DynDNS considers setting the IP to what it is already set abuse + tcp->DeallocatePacket(packet); + SetCompleted(RC_DNS_ALREADY_SET, "No action needed"); + return; + } + } + + tcp->DeallocatePacket(packet); + tcp->CloseConnection(packet->systemAddress); + + connectPhase = CP_CONNECTING_TO_DYNDNS; + tcp->Connect("members.dyndns.org", 80, false); + } + } + + if (tcp->HasLostConnection()!=UNASSIGNED_SYSTEM_ADDRESS) + { + if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE) + { + SetCompleted(RC_CONNECTION_LOST_WITHOUT_RESPONSE, "Connection lost to DynDNS during GET operation"); + } + } +} + + +#endif // _RAKNET_SUPPORT_DynDNS diff --git a/src/raknet/DynDNS.h b/src/raknet/DynDNS.h index ed30bdd..e163377 100755 --- a/src/raknet/DynDNS.h +++ b/src/raknet/DynDNS.h @@ -1,100 +1,100 @@ -/// \file DynDNS.h -/// \brief Helper to class to update DynDNS -/// This can be used to determine what permissions are should be allowed to the other system -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1 - -class TCPInterface; - -#ifndef __DYN_DNS_H -#define __DYN_DNS_H - -namespace RakNet -{ - -enum DynDnsResultCode -{ - // ----- Success ----- - RC_SUCCESS, - RC_DNS_ALREADY_SET, // RakNet detects no action is needed - - // ----- Ignorable failure (treat same as success) ----- - RC_NO_CHANGE, // DynDNS detects no action is needed (treated as abuse though) - - // ----- User error ----- - RC_NOT_DONATOR, // You have to pay to do this - RC_NO_HOST, // This host does not exist at all - RC_BAD_AUTH, // You set the wrong password - RC_NOT_YOURS, // This is not your host - - // ----- Permanent failure ----- - RC_ABUSE, // Your host has been blocked, too many failures disable your account - RC_TCP_FAILED_TO_START, // TCP port already in use - RC_TCP_DID_NOT_CONNECT, // DynDNS down? - RC_UNKNOWN_RESULT, // DynDNS returned a result code that was not documented as of 12/4/2010 on http://www.dyndns.com/developers/specs/flow.pdf - RC_PARSING_FAILURE, // Can't read the result returned, format change? - RC_CONNECTION_LOST_WITHOUT_RESPONSE, // Lost the connection to DynDNS while communicating - RC_BAD_AGENT, // ??? - RC_BAD_SYS, // ??? - RC_DNS_ERROR, // ??? - RC_NOT_FQDN, // ??? - RC_NUM_HOST, // ??? - RC_911, // ??? - RC_DYNDNS_TIMEOUT, // DynDNS did not respond -}; - -// Can only process one at a time with the current implementation -class RAK_DLL_EXPORT DynDNS -{ -public: - DynDNS(); - ~DynDNS(); - - // Pass 0 for newIPAddress to autodetect whatever you are uploading from - // usernameAndPassword should be in the format username:password - void UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword ); - void Update(void); - - // Output - bool IsRunning(void) const {return connectPhase!=CP_IDLE;} - bool IsCompleted(void) const {return connectPhase==CP_IDLE;} - RakNet::DynDnsResultCode GetCompletedResultCode(void) {return result;} - const char *GetCompletedDescription(void) const {return resultDescription;} - bool WasResultSuccessful(void) const {return result==RC_SUCCESS || result==RC_DNS_ALREADY_SET || result==RC_NO_CHANGE;} - char *GetMyPublicIP(void) const {return (char*) myIPStr;} // We get our public IP as part of the process. This is valid once completed - -protected: - void Stop(void); - void SetCompleted(RakNet::DynDnsResultCode _result, const char *_resultDescription) {Stop(); result=_result; resultDescription=_resultDescription;} - - enum ConnectPhase - { - CP_CONNECTING_TO_CHECKIP, - CP_WAITING_FOR_CHECKIP_RESPONSE, - CP_CONNECTING_TO_DYNDNS, - CP_WAITING_FOR_DYNDNS_RESPONSE, - CP_IDLE, - }; - - TCPInterface *tcp; - RakNet::RakString getString; - SystemAddress serverAddress; - ConnectPhase connectPhase; - RakNet::RakString host; - RakNet::Time phaseTimeout; - SystemAddress checkIpAddress; - const char *resultDescription; - RakNet::DynDnsResultCode result; - char myIPStr[32]; -}; - -} // namespace RakNet - -#endif // __DYN_DNS_H - -#endif // _RAKNET_SUPPORT_DynDNS +/// \file DynDNS.h +/// \brief Helper to class to update DynDNS +/// This can be used to determine what permissions are should be allowed to the other system +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1 + +class TCPInterface; + +#ifndef __DYN_DNS_H +#define __DYN_DNS_H + +namespace RakNet +{ + +enum DynDnsResultCode +{ + // ----- Success ----- + RC_SUCCESS, + RC_DNS_ALREADY_SET, // RakNet detects no action is needed + + // ----- Ignorable failure (treat same as success) ----- + RC_NO_CHANGE, // DynDNS detects no action is needed (treated as abuse though) + + // ----- User error ----- + RC_NOT_DONATOR, // You have to pay to do this + RC_NO_HOST, // This host does not exist at all + RC_BAD_AUTH, // You set the wrong password + RC_NOT_YOURS, // This is not your host + + // ----- Permanent failure ----- + RC_ABUSE, // Your host has been blocked, too many failures disable your account + RC_TCP_FAILED_TO_START, // TCP port already in use + RC_TCP_DID_NOT_CONNECT, // DynDNS down? + RC_UNKNOWN_RESULT, // DynDNS returned a result code that was not documented as of 12/4/2010 on http://www.dyndns.com/developers/specs/flow.pdf + RC_PARSING_FAILURE, // Can't read the result returned, format change? + RC_CONNECTION_LOST_WITHOUT_RESPONSE, // Lost the connection to DynDNS while communicating + RC_BAD_AGENT, // ??? + RC_BAD_SYS, // ??? + RC_DNS_ERROR, // ??? + RC_NOT_FQDN, // ??? + RC_NUM_HOST, // ??? + RC_911, // ??? + RC_DYNDNS_TIMEOUT, // DynDNS did not respond +}; + +// Can only process one at a time with the current implementation +class RAK_DLL_EXPORT DynDNS +{ +public: + DynDNS(); + ~DynDNS(); + + // Pass 0 for newIPAddress to autodetect whatever you are uploading from + // usernameAndPassword should be in the format username:password + void UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword ); + void Update(void); + + // Output + bool IsRunning(void) const {return connectPhase!=CP_IDLE;} + bool IsCompleted(void) const {return connectPhase==CP_IDLE;} + RakNet::DynDnsResultCode GetCompletedResultCode(void) {return result;} + const char *GetCompletedDescription(void) const {return resultDescription;} + bool WasResultSuccessful(void) const {return result==RC_SUCCESS || result==RC_DNS_ALREADY_SET || result==RC_NO_CHANGE;} + char *GetMyPublicIP(void) const {return (char*) myIPStr;} // We get our public IP as part of the process. This is valid once completed + +protected: + void Stop(void); + void SetCompleted(RakNet::DynDnsResultCode _result, const char *_resultDescription) {Stop(); result=_result; resultDescription=_resultDescription;} + + enum ConnectPhase + { + CP_CONNECTING_TO_CHECKIP, + CP_WAITING_FOR_CHECKIP_RESPONSE, + CP_CONNECTING_TO_DYNDNS, + CP_WAITING_FOR_DYNDNS_RESPONSE, + CP_IDLE, + }; + + TCPInterface *tcp; + RakNet::RakString getString; + SystemAddress serverAddress; + ConnectPhase connectPhase; + RakNet::RakString host; + RakNet::Time phaseTimeout; + SystemAddress checkIpAddress; + const char *resultDescription; + RakNet::DynDnsResultCode result; + char myIPStr[32]; +}; + +} // namespace RakNet + +#endif // __DYN_DNS_H + +#endif // _RAKNET_SUPPORT_DynDNS diff --git a/src/raknet/EmailSender.cpp b/src/raknet/EmailSender.cpp index 4b38481..2a0e4a4 100755 --- a/src/raknet/EmailSender.cpp +++ b/src/raknet/EmailSender.cpp @@ -1,362 +1,362 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1 - -// Useful sites -// http://www.faqs.org\rfcs\rfc2821.html -// http://www2.rad.com\networks/1995/mime/examples.htm - -#include "EmailSender.h" -#include "TCPInterface.h" -#include "GetTime.h" -#include "Rand.h" -#include "FileList.h" -#include "BitStream.h" -#include - - - - - -#include "RakSleep.h" - -using namespace RakNet; - - -STATIC_FACTORY_DEFINITIONS(EmailSender,EmailSender); - -const char *EmailSender::Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password) -{ - RakNet::Packet *packet; - char query[1024]; - TCPInterface tcpInterface; - SystemAddress emailServer; - if (tcpInterface.Start(0, 0)==false) - return "Unknown error starting TCP"; - emailServer=tcpInterface.Connect(hostAddress, hostPort,true); - if (emailServer==UNASSIGNED_SYSTEM_ADDRESS) - return "Failed to connect to host"; -#if OPEN_SSL_CLIENT_SUPPORT==1 - tcpInterface.StartSSLClient(emailServer); -#endif - RakNet::TimeMS timeoutTime = RakNet::GetTimeMS()+3000; - packet=0; - while (RakNet::GetTimeMS() < timeoutTime) - { - packet = tcpInterface.Receive(); - if (packet) - { - if (doPrintf) - RAKNET_DEBUG_PRINTF("%s", packet->data); - break; - } - RakSleep(250); - } - - if (packet==0) - return "Timeout while waiting for initial data from server."; - - tcpInterface.Send("EHLO\r\n", 6, emailServer,false); - const char *response; - bool authenticate=false; -#ifdef _MSC_VER -#pragma warning(disable:4127) // conditional expression is constant -#endif - while (1) - { - response=GetResponse(&tcpInterface, emailServer, doPrintf); - - if (response!=0 && strcmp(response, "AUTHENTICATE")==0) - { - authenticate=true; - break; - } - - // Something other than continue? - if (response!=0 && strcmp(response, "CONTINUE")!=0) - return response; - - // Success? - if (response==0) - break; - } - - if (authenticate) - { - sprintf(query, "EHLO %s\r\n", sender); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - if (password==0) - return "Password needed"; - char *outputData = RakNet::OP_NEW_ARRAY((const int) (strlen(sender)+strlen(password)+2)*3, _FILE_AND_LINE_ ); - RakNet::BitStream bs; - char zero=0; - bs.Write(&zero,1); - bs.Write(sender,(const unsigned int)strlen(sender)); - //bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net")); - bs.Write(&zero,1); - bs.Write(password,(const unsigned int)strlen(password)); - bs.Write(&zero,1); - //bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password")); - TCPInterface::Base64Encoding((const char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData); - sprintf(query, "AUTH PLAIN %s", outputData); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - } - - - if (sender) - sprintf(query, "MAIL From: <%s>\r\n", sender); - else - sprintf(query, "MAIL From: <>\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - - if (recipient) - sprintf(query, "RCPT TO: <%s>\r\n", recipient); - else - sprintf(query, "RCPT TO: <>\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - - tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer,false); - - // Wait for 354... - - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - - if (subject) - { - sprintf(query, "Subject: %s\r\n", subject); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - if (senderName) - { - sprintf(query, "From: %s\r\n", senderName); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - if (recipientName) - { - sprintf(query, "To: %s\r\n", recipientName); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - - const int boundarySize=60; - char boundary[boundarySize+1]; - int i,j; - if (attachedFiles && attachedFiles->fileList.Size()) - { - rakNetRandom.SeedMT((unsigned int) RakNet::GetTimeMS()); - // Random multipart message boundary - for (i=0; i < boundarySize; i++) - boundary[i]=TCPInterface::Base64Map()[rakNetRandom.RandomMT()%64]; - boundary[boundarySize]=0; - } - - sprintf(query, "MIME-version: 1.0\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - if (attachedFiles && attachedFiles->fileList.Size()) - { - sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - - sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - // Write the body of the email, doing some lame shitty shit where I have to make periods at the start of a newline have a second period. - char *newBody; - int bodyLength; - bodyLength=(int)strlen(body); - newBody = (char*) rakMalloc_Ex( bodyLength*3, _FILE_AND_LINE_ ); - if (bodyLength>0) - newBody[0]=body[0]; - for (i=1, j=1; i < bodyLength; i++) - { - // Transform \n . \r \n into \n . . \r \n - if (i < bodyLength-2 && - body[i-1]=='\n' && - body[i+0]=='.' && - body[i+1]=='\r' && - body[i+2]=='\n') - { - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='\r'; - newBody[j++]='\n'; - i+=2; - } - // Transform \n . . \r \n into \n . . . \r \n - // Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed - else if (i <= bodyLength-3 && - body[i-1]=='\n' && - body[i+0]=='.' && - body[i+1]=='.' && - body[i+2]=='\r' && - body[i+3]=='\n') - { - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='\r'; - newBody[j++]='\n'; - i+=3; - } - // Transform \n . \n into \n . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does) - else if (i < bodyLength-1 && - body[i-1]=='\n' && - body[i+0]=='.' && - body[i+1]=='\n') - { - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='\r'; - newBody[j++]='\n'; - i+=1; - } - // Transform \n . . \n into \n . . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does) - // In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed - else if (i <= bodyLength-2 && - body[i-1]=='\n' && - body[i+0]=='.' && - body[i+1]=='.' && - body[i+2]=='\n') - { - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='.'; - newBody[j++]='\r'; - newBody[j++]='\n'; - i+=2; - } - else - newBody[j++]=body[i]; - } - - newBody[j++]='\r'; - newBody[j++]='\n'; - tcpInterface.Send(newBody, j, emailServer,false); - - rakFree_Ex(newBody, _FILE_AND_LINE_ ); - int outputOffset; - - // What a pain in the rear. I have to map the binary to printable characters using 6 bits per character. - if (attachedFiles && attachedFiles->fileList.Size()) - { - for (i=0; i < (int) attachedFiles->fileList.Size(); i++) - { - // Write boundary - sprintf(query, "\r\n--%s\r\n", boundary); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - sprintf(query, "Content-Type: APPLICATION/Octet-Stream; SizeOnDisk=%i; name=\"%s\"\r\nContent-Transfer-Encoding: BASE64\r\nContent-Description: %s\r\n\r\n", attachedFiles->fileList[i].dataLengthBytes, attachedFiles->fileList[i].filename.C_String(), attachedFiles->fileList[i].filename.C_String()); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - - newBody = (char*) rakMalloc_Ex( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2, _FILE_AND_LINE_ ); - - outputOffset=TCPInterface::Base64Encoding(attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody); - - // Send the base64 mapped file. - tcpInterface.Send(newBody, outputOffset, emailServer,false); - rakFree_Ex(newBody, _FILE_AND_LINE_ ); - - } - - // Write last boundary - sprintf(query, "\r\n--%s--\r\n", boundary); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - } - - - sprintf(query, "\r\n.\r\n"); - tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); - response=GetResponse(&tcpInterface, emailServer, doPrintf); - if (response!=0) - return response; - - tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer,false); - - RakSleep(30); - if (doPrintf) - { - packet = tcpInterface.Receive(); - while (packet) - { - RAKNET_DEBUG_PRINTF("%s", packet->data); - packet = tcpInterface.Receive(); - } - } - tcpInterface.Stop(); - return 0; // Success -} - -const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf) -{ - RakNet::Packet *packet; - RakNet::TimeMS timeout; - timeout=RakNet::GetTimeMS()+5000; -#ifdef _MSC_VER - #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while (1) - { - if (tcpInterface->HasLostConnection()==emailServer) - return "Connection to server lost."; - packet = tcpInterface->Receive(); - if (packet) - { - if (doPrintf) - { - RAKNET_DEBUG_PRINTF("%s", packet->data); - } -#if OPEN_SSL_CLIENT_SUPPORT==1 - if (strstr((const char*)packet->data, "220")) - { - tcpInterface->StartSSLClient(packet->systemAddress); - return "AUTHENTICATE"; // OK - } -// if (strstr((const char*)packet->data, "250-AUTH LOGIN PLAIN")) -// { -// tcpInterface->StartSSLClient(packet->systemAddress); -// return "AUTHENTICATE"; // OK -// } -#endif - if (strstr((const char*)packet->data, "235")) - return 0; // Authentication accepted - if (strstr((const char*)packet->data, "354")) - return 0; // Go ahead -#if OPEN_SSL_CLIENT_SUPPORT==1 - if (strstr((const char*)packet->data, "250-STARTTLS")) - { - tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress, false); - return "CONTINUE"; - } -#endif - if (strstr((const char*)packet->data, "250")) - return 0; // OK - if (strstr((const char*)packet->data, "550")) - return "Failed on error code 550"; - if (strstr((const char*)packet->data, "553")) - return "Failed on error code 553"; - } - if (RakNet::GetTimeMS() > timeout) - return "Timed out"; - RakSleep(100); - } -} - - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1 + +// Useful sites +// http://www.faqs.org\rfcs\rfc2821.html +// http://www2.rad.com\networks/1995/mime/examples.htm + +#include "EmailSender.h" +#include "TCPInterface.h" +#include "GetTime.h" +#include "Rand.h" +#include "FileList.h" +#include "BitStream.h" +#include + + + + + +#include "RakSleep.h" + +using namespace RakNet; + + +STATIC_FACTORY_DEFINITIONS(EmailSender,EmailSender); + +const char *EmailSender::Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password) +{ + RakNet::Packet *packet; + char query[1024]; + TCPInterface tcpInterface; + SystemAddress emailServer; + if (tcpInterface.Start(0, 0)==false) + return "Unknown error starting TCP"; + emailServer=tcpInterface.Connect(hostAddress, hostPort,true); + if (emailServer==UNASSIGNED_SYSTEM_ADDRESS) + return "Failed to connect to host"; +#if OPEN_SSL_CLIENT_SUPPORT==1 + tcpInterface.StartSSLClient(emailServer); +#endif + RakNet::TimeMS timeoutTime = RakNet::GetTimeMS()+3000; + packet=0; + while (RakNet::GetTimeMS() < timeoutTime) + { + packet = tcpInterface.Receive(); + if (packet) + { + if (doPrintf) + RAKNET_DEBUG_PRINTF("%s", packet->data); + break; + } + RakSleep(250); + } + + if (packet==0) + return "Timeout while waiting for initial data from server."; + + tcpInterface.Send("EHLO\r\n", 6, emailServer,false); + const char *response; + bool authenticate=false; +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + while (1) + { + response=GetResponse(&tcpInterface, emailServer, doPrintf); + + if (response!=0 && strcmp(response, "AUTHENTICATE")==0) + { + authenticate=true; + break; + } + + // Something other than continue? + if (response!=0 && strcmp(response, "CONTINUE")!=0) + return response; + + // Success? + if (response==0) + break; + } + + if (authenticate) + { + sprintf(query, "EHLO %s\r\n", sender); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + if (password==0) + return "Password needed"; + char *outputData = RakNet::OP_NEW_ARRAY((const int) (strlen(sender)+strlen(password)+2)*3, _FILE_AND_LINE_ ); + RakNet::BitStream bs; + char zero=0; + bs.Write(&zero,1); + bs.Write(sender,(const unsigned int)strlen(sender)); + //bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net")); + bs.Write(&zero,1); + bs.Write(password,(const unsigned int)strlen(password)); + bs.Write(&zero,1); + //bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password")); + TCPInterface::Base64Encoding((const char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData); + sprintf(query, "AUTH PLAIN %s", outputData); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + } + + + if (sender) + sprintf(query, "MAIL From: <%s>\r\n", sender); + else + sprintf(query, "MAIL From: <>\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + + if (recipient) + sprintf(query, "RCPT TO: <%s>\r\n", recipient); + else + sprintf(query, "RCPT TO: <>\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + + tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer,false); + + // Wait for 354... + + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + + if (subject) + { + sprintf(query, "Subject: %s\r\n", subject); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + if (senderName) + { + sprintf(query, "From: %s\r\n", senderName); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + if (recipientName) + { + sprintf(query, "To: %s\r\n", recipientName); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + + const int boundarySize=60; + char boundary[boundarySize+1]; + int i,j; + if (attachedFiles && attachedFiles->fileList.Size()) + { + rakNetRandom.SeedMT((unsigned int) RakNet::GetTimeMS()); + // Random multipart message boundary + for (i=0; i < boundarySize; i++) + boundary[i]=TCPInterface::Base64Map()[rakNetRandom.RandomMT()%64]; + boundary[boundarySize]=0; + } + + sprintf(query, "MIME-version: 1.0\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + if (attachedFiles && attachedFiles->fileList.Size()) + { + sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + + sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + // Write the body of the email, doing some lame shitty shit where I have to make periods at the start of a newline have a second period. + char *newBody; + int bodyLength; + bodyLength=(int)strlen(body); + newBody = (char*) rakMalloc_Ex( bodyLength*3, _FILE_AND_LINE_ ); + if (bodyLength>0) + newBody[0]=body[0]; + for (i=1, j=1; i < bodyLength; i++) + { + // Transform \n . \r \n into \n . . \r \n + if (i < bodyLength-2 && + body[i-1]=='\n' && + body[i+0]=='.' && + body[i+1]=='\r' && + body[i+2]=='\n') + { + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='\r'; + newBody[j++]='\n'; + i+=2; + } + // Transform \n . . \r \n into \n . . . \r \n + // Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed + else if (i <= bodyLength-3 && + body[i-1]=='\n' && + body[i+0]=='.' && + body[i+1]=='.' && + body[i+2]=='\r' && + body[i+3]=='\n') + { + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='\r'; + newBody[j++]='\n'; + i+=3; + } + // Transform \n . \n into \n . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does) + else if (i < bodyLength-1 && + body[i-1]=='\n' && + body[i+0]=='.' && + body[i+1]=='\n') + { + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='\r'; + newBody[j++]='\n'; + i+=1; + } + // Transform \n . . \n into \n . . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does) + // In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed + else if (i <= bodyLength-2 && + body[i-1]=='\n' && + body[i+0]=='.' && + body[i+1]=='.' && + body[i+2]=='\n') + { + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='.'; + newBody[j++]='\r'; + newBody[j++]='\n'; + i+=2; + } + else + newBody[j++]=body[i]; + } + + newBody[j++]='\r'; + newBody[j++]='\n'; + tcpInterface.Send(newBody, j, emailServer,false); + + rakFree_Ex(newBody, _FILE_AND_LINE_ ); + int outputOffset; + + // What a pain in the rear. I have to map the binary to printable characters using 6 bits per character. + if (attachedFiles && attachedFiles->fileList.Size()) + { + for (i=0; i < (int) attachedFiles->fileList.Size(); i++) + { + // Write boundary + sprintf(query, "\r\n--%s\r\n", boundary); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + sprintf(query, "Content-Type: APPLICATION/Octet-Stream; SizeOnDisk=%i; name=\"%s\"\r\nContent-Transfer-Encoding: BASE64\r\nContent-Description: %s\r\n\r\n", attachedFiles->fileList[i].dataLengthBytes, attachedFiles->fileList[i].filename.C_String(), attachedFiles->fileList[i].filename.C_String()); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + + newBody = (char*) rakMalloc_Ex( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2, _FILE_AND_LINE_ ); + + outputOffset=TCPInterface::Base64Encoding(attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody); + + // Send the base64 mapped file. + tcpInterface.Send(newBody, outputOffset, emailServer,false); + rakFree_Ex(newBody, _FILE_AND_LINE_ ); + + } + + // Write last boundary + sprintf(query, "\r\n--%s--\r\n", boundary); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + } + + + sprintf(query, "\r\n.\r\n"); + tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false); + response=GetResponse(&tcpInterface, emailServer, doPrintf); + if (response!=0) + return response; + + tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer,false); + + RakSleep(30); + if (doPrintf) + { + packet = tcpInterface.Receive(); + while (packet) + { + RAKNET_DEBUG_PRINTF("%s", packet->data); + packet = tcpInterface.Receive(); + } + } + tcpInterface.Stop(); + return 0; // Success +} + +const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf) +{ + RakNet::Packet *packet; + RakNet::TimeMS timeout; + timeout=RakNet::GetTimeMS()+5000; +#ifdef _MSC_VER + #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + if (tcpInterface->HasLostConnection()==emailServer) + return "Connection to server lost."; + packet = tcpInterface->Receive(); + if (packet) + { + if (doPrintf) + { + RAKNET_DEBUG_PRINTF("%s", packet->data); + } +#if OPEN_SSL_CLIENT_SUPPORT==1 + if (strstr((const char*)packet->data, "220")) + { + tcpInterface->StartSSLClient(packet->systemAddress); + return "AUTHENTICATE"; // OK + } +// if (strstr((const char*)packet->data, "250-AUTH LOGIN PLAIN")) +// { +// tcpInterface->StartSSLClient(packet->systemAddress); +// return "AUTHENTICATE"; // OK +// } +#endif + if (strstr((const char*)packet->data, "235")) + return 0; // Authentication accepted + if (strstr((const char*)packet->data, "354")) + return 0; // Go ahead +#if OPEN_SSL_CLIENT_SUPPORT==1 + if (strstr((const char*)packet->data, "250-STARTTLS")) + { + tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress, false); + return "CONTINUE"; + } +#endif + if (strstr((const char*)packet->data, "250")) + return 0; // OK + if (strstr((const char*)packet->data, "550")) + return "Failed on error code 550"; + if (strstr((const char*)packet->data, "553")) + return "Failed on error code 553"; + } + if (RakNet::GetTimeMS() > timeout) + return "Timed out"; + RakSleep(100); + } +} + + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/EmailSender.h b/src/raknet/EmailSender.h index b2a75b4..04c0880 100755 --- a/src/raknet/EmailSender.h +++ b/src/raknet/EmailSender.h @@ -1,58 +1,58 @@ -/// \file EmailSender.h -/// \brief Rudimentary class to send email from code. Don't expect anything fancy. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __EMAIL_SENDER_H -#define __EMAIL_SENDER_H - -#include "RakNetTypes.h" -#include "RakMemoryOverride.h" -#include "Export.h" -#include "Rand.h" -#include "TCPInterface.h" - -namespace RakNet -{ -/// Forward declarations -class FileList; -class TCPInterface; - -/// \brief Rudimentary class to send email from code. -class RAK_DLL_EXPORT EmailSender -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(EmailSender) - - /// \brief Sends an email. - /// \param[in] hostAddress The address of the email server. - /// \param[in] hostPort The port of the email server (usually 25) - /// \param[in] sender The email address you are sending from. - /// \param[in] recipient The email address you are sending to. - /// \param[in] senderName The email address you claim to be sending from - /// \param[in] recipientName The email address you claim to be sending to - /// \param[in] subject Email subject - /// \param[in] body Email body - /// \param[in] attachedFiles List of files to attach to the email. (Can be 0 to send none). - /// \param[in] doPrintf true to output SMTP info to console(for debugging?) - /// \param[in] password Used if the server uses AUTHENTICATE PLAIN over TLS (such as gmail) - /// \return 0 on success, otherwise a string indicating the error message - const char *Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password); - -protected: - const char *GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf); - RakNetRandom rakNetRandom; -}; - -} // namespace RakNet - -#endif - - -#endif // _RAKNET_SUPPORT_* +/// \file EmailSender.h +/// \brief Rudimentary class to send email from code. Don't expect anything fancy. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __EMAIL_SENDER_H +#define __EMAIL_SENDER_H + +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" +#include "Export.h" +#include "Rand.h" +#include "TCPInterface.h" + +namespace RakNet +{ +/// Forward declarations +class FileList; +class TCPInterface; + +/// \brief Rudimentary class to send email from code. +class RAK_DLL_EXPORT EmailSender +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(EmailSender) + + /// \brief Sends an email. + /// \param[in] hostAddress The address of the email server. + /// \param[in] hostPort The port of the email server (usually 25) + /// \param[in] sender The email address you are sending from. + /// \param[in] recipient The email address you are sending to. + /// \param[in] senderName The email address you claim to be sending from + /// \param[in] recipientName The email address you claim to be sending to + /// \param[in] subject Email subject + /// \param[in] body Email body + /// \param[in] attachedFiles List of files to attach to the email. (Can be 0 to send none). + /// \param[in] doPrintf true to output SMTP info to console(for debugging?) + /// \param[in] password Used if the server uses AUTHENTICATE PLAIN over TLS (such as gmail) + /// \return 0 on success, otherwise a string indicating the error message + const char *Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password); + +protected: + const char *GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf); + RakNetRandom rakNetRandom; +}; + +} // namespace RakNet + +#endif + + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/EncodeClassName.cpp b/src/raknet/EncodeClassName.cpp index d3f5a12..8b13789 100755 --- a/src/raknet/EncodeClassName.cpp +++ b/src/raknet/EncodeClassName.cpp @@ -1 +1 @@ - + diff --git a/src/raknet/EpochTimeToString.cpp b/src/raknet/EpochTimeToString.cpp index b3410ca..c0174aa 100755 --- a/src/raknet/EpochTimeToString.cpp +++ b/src/raknet/EpochTimeToString.cpp @@ -1,35 +1,35 @@ -#include "FormatString.h" -#include "EpochTimeToString.h" -#include -#include -#include -// localtime -#include -#include "LinuxStrings.h" - -char * EpochTimeToString(long long time) -{ - static int textIndex=0; - static char text[4][64]; - - if (++textIndex==4) - textIndex=0; - - struct tm * timeinfo; - time_t t = time; - timeinfo = localtime ( &t ); - strftime (text[textIndex],64,"%c.",timeinfo); - - /* - time_t - // Copied from the docs - struct tm *newtime; - newtime = _localtime64(& time); - asctime_s( text[textIndex], sizeof(text[textIndex]), newtime ); - - while (text[textIndex][0] && (text[textIndex][strlen(text[textIndex])-1]=='\n' || text[textIndex][strlen(text[textIndex])-1]=='\r')) - text[textIndex][strlen(text[textIndex])-1]=0; - */ - - return text[textIndex]; -} +#include "FormatString.h" +#include "EpochTimeToString.h" +#include +#include +#include +// localtime +#include +#include "LinuxStrings.h" + +char * EpochTimeToString(long long time) +{ + static int textIndex=0; + static char text[4][64]; + + if (++textIndex==4) + textIndex=0; + + struct tm * timeinfo; + time_t t = time; + timeinfo = localtime ( &t ); + strftime (text[textIndex],64,"%c.",timeinfo); + + /* + time_t + // Copied from the docs + struct tm *newtime; + newtime = _localtime64(& time); + asctime_s( text[textIndex], sizeof(text[textIndex]), newtime ); + + while (text[textIndex][0] && (text[textIndex][strlen(text[textIndex])-1]=='\n' || text[textIndex][strlen(text[textIndex])-1]=='\r')) + text[textIndex][strlen(text[textIndex])-1]=0; + */ + + return text[textIndex]; +} diff --git a/src/raknet/EpochTimeToString.h b/src/raknet/EpochTimeToString.h index 8644dc4..b6b299a 100755 --- a/src/raknet/EpochTimeToString.h +++ b/src/raknet/EpochTimeToString.h @@ -1,15 +1,15 @@ -/// \file EpochTimeToString.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __EPOCH_TIME_TO_STRING_H -#define __EPOCH_TIME_TO_STRING_H - -#include "Export.h" - -RAK_DLL_EXPORT char * EpochTimeToString(long long time); - -#endif - +/// \file EpochTimeToString.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __EPOCH_TIME_TO_STRING_H +#define __EPOCH_TIME_TO_STRING_H + +#include "Export.h" + +RAK_DLL_EXPORT char * EpochTimeToString(long long time); + +#endif + diff --git a/src/raknet/Export.h b/src/raknet/Export.h index 5c90a19..50ca3eb 100755 --- a/src/raknet/Export.h +++ b/src/raknet/Export.h @@ -1,13 +1,13 @@ -#include "RakNetDefines.h" - -#if defined(_WIN32) && !(defined(__GNUC__) || defined(__GCCXML__)) && !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) -#define RAK_DLL_EXPORT __declspec(dllexport) -#else -#define RAK_DLL_EXPORT -#endif - -#define STATIC_FACTORY_DECLARATIONS(x) static x* GetInstance(void); \ -static void DestroyInstance( x *i); - -#define STATIC_FACTORY_DEFINITIONS(x,y) x* x::GetInstance(void) {return RakNet::OP_NEW( _FILE_AND_LINE_ );} \ -void x::DestroyInstance( x *i) {RakNet::OP_DELETE(( y* ) i, _FILE_AND_LINE_);} +#include "RakNetDefines.h" + +#if defined(_WIN32) && !(defined(__GNUC__) || defined(__GCCXML__)) && !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) +#define RAK_DLL_EXPORT __declspec(dllexport) +#else +#define RAK_DLL_EXPORT +#endif + +#define STATIC_FACTORY_DECLARATIONS(x) static x* GetInstance(void); \ +static void DestroyInstance( x *i); + +#define STATIC_FACTORY_DEFINITIONS(x,y) x* x::GetInstance(void) {return RakNet::OP_NEW( _FILE_AND_LINE_ );} \ +void x::DestroyInstance( x *i) {RakNet::OP_DELETE(( y* ) i, _FILE_AND_LINE_);} diff --git a/src/raknet/FileList.cpp b/src/raknet/FileList.cpp index db6890a..7fe2ff2 100755 --- a/src/raknet/FileList.cpp +++ b/src/raknet/FileList.cpp @@ -1,794 +1,794 @@ -#include "FileList.h" - -#if _RAKNET_SUPPORT_FileOperations==1 - -#include // RAKNET_DEBUG_PRINTF -#include "RakAssert.h" -#if defined(ANDROID) -#include -#elif defined(_WIN32) || defined(__CYGWIN__) -#include - - -// #elif !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) && !defined ( __PPC__ ) && !defined ( __FreeBSD__ ) && !defined ( __S3E__ ) -// #include -#endif - - -#ifdef _WIN32 -// For mkdir -#include - - -#else -#include -#endif - -//#include "SHA1.h" -#include "DS_Queue.h" -#include "StringCompressor.h" -#include "BitStream.h" -#include "FileOperations.h" -#include "SuperFastHash.h" -#include "RakAssert.h" -#include "LinuxStrings.h" - -#define MAX_FILENAME_LENGTH 512 -static const unsigned HASH_LENGTH=4; - -using namespace RakNet; - -// alloca - -#if defined(_WIN32) -#include - - -#else -#if !defined ( __FreeBSD__ ) -#include -#endif -#include -#include -#include -#include "_FindFirst.h" -#include //defines intptr_t -#endif - -#include "RakAlloca.h" - -//int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data ) -//{ -// return strcmp(key, data.filename); -//} - - -STATIC_FACTORY_DEFINITIONS(FileListProgress,FileListProgress) -STATIC_FACTORY_DEFINITIONS(FLP_Printf,FLP_Printf) -STATIC_FACTORY_DEFINITIONS(FileList,FileList) - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// First callback called when FileList::AddFilesFromDirectory() starts -void FLP_Printf::OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) { - (void) fileList; - RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dir);} - -/// Called for each directory, when that directory begins processing -void FLP_Printf::OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) { - (void) fileList; - RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", dir, directoriesRemaining);} -void FLP_Printf::OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID ) -{ - (void) setID; - - char str[32]; - systemAddress.ToString(true, (char*) str); - RAKNET_DEBUG_PRINTF("File pushes complete to %s\n", str); -} -void FLP_Printf::OnSendAborted( SystemAddress systemAddress ) -{ - char str[32]; - systemAddress.ToString(true, (char*) str); - RAKNET_DEBUG_PRINTF("Send aborted to %s\n", str); -} -FileList::FileList() -{ -} -FileList::~FileList() -{ - Clear(); -} -void FileList::AddFile(const char *filepath, const char *filename, FileListNodeContext context) -{ - if (filepath==0 || filename==0) - return; - - char *data; - //std::fstream file; - //file.open(filename, std::ios::in | std::ios::binary); - - FILE *fp = fopen(filepath, "rb"); - if (fp==0) - return; - fseek(fp, 0, SEEK_END); - int length = ftell(fp); - fseek(fp, 0, SEEK_SET); - - if (length > (int) ((unsigned int)-1 / 8)) - { - // If this assert hits, split up your file. You could also change BitSize_t in RakNetTypes.h to unsigned long long but this is not recommended for performance reasons - RakAssert("Cannot add files over 536 MB" && 0); - fclose(fp); - return; - } - - - - bool usedAlloca=false; - if (length < MAX_ALLOCA_STACK_ALLOCATION) - { - data = ( char* ) alloca( length ); - usedAlloca=true; - } - else - - { - data = (char*) rakMalloc_Ex( length, _FILE_AND_LINE_ ); - } - - fread(data, 1, length, fp); - AddFile(filename, filepath, data, length, length, context); - fclose(fp); - - - if (usedAlloca==false) - - rakFree_Ex(data, _FILE_AND_LINE_ ); - -} -void FileList::AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference, bool takeDataPointer) -{ - if (filename==0) - return; - if (strlen(filename)>MAX_FILENAME_LENGTH) - { - // Should be enough for anyone - RakAssert(0); - return; - } - // If adding a reference, do not send data - RakAssert(isAReference==false || data==0); - // Avoid duplicate insertions unless the data is different, in which case overwrite the old data - unsigned i; - for (i=0; i dirList; - char root[260]; - char fullPath[520]; - _finddata_t fileInfo; - intptr_t dir; - FILE *fp; - char *dirSoFar, *fileData; - dirSoFar=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ ); - - if (applicationDirectory) - strcpy(root, applicationDirectory); - else - root[0]=0; - - int rootLen=(int)strlen(root); - if (rootLen) - { - strcpy(dirSoFar, root); - if (FixEndingSlash(dirSoFar)) - rootLen++; - } - else - dirSoFar[0]=0; - - if (subDirectory) - { - strcat(dirSoFar, subDirectory); - FixEndingSlash(dirSoFar); - } - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnAddFilesFromDirectoryStarted(this, dirSoFar); - // RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dirSoFar); - dirList.Push(dirSoFar, _FILE_AND_LINE_ ); - while (dirList.Size()) - { - dirSoFar=dirList.Pop(); - strcpy(fullPath, dirSoFar); - // Changed from *.* to * for Linux compatibility - strcat(fullPath, "*"); - - - dir=_findfirst(fullPath, &fileInfo ); - if (dir==-1) - { - _findclose(dir); - rakFree_Ex(dirSoFar, _FILE_AND_LINE_ ); - unsigned i; - for (i=0; i < dirList.Size(); i++) - rakFree_Ex(dirList[i], _FILE_AND_LINE_ ); - return; - } - -// RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", fullPath, dirList.Size()); - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnDirectory(this, fullPath, dirList.Size()); - - do - { - // no guarantee these entries are first... - if (strcmp("." , fileInfo.name) == 0 || - strcmp("..", fileInfo.name) == 0) - { - continue; - } - - if ((fileInfo.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM))==0) - { - strcpy(fullPath, dirSoFar); - strcat(fullPath, fileInfo.name); - fileData=0; - - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnFile(this, dirSoFar, fileInfo.name, fileInfo.size); - - if (writeData && writeHash) - { - fp = fopen(fullPath, "rb"); - if (fp) - { - fileData= (char*) rakMalloc_Ex( fileInfo.size+HASH_LENGTH, _FILE_AND_LINE_ ); - fread(fileData+HASH_LENGTH, fileInfo.size, 1, fp); - fclose(fp); - - unsigned int hash = SuperFastHash(fileData+HASH_LENGTH, fileInfo.size); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); - memcpy(fileData, &hash, HASH_LENGTH); - - // sha1.Reset(); - // sha1.Update( ( unsigned char* ) fileData+HASH_LENGTH, fileInfo.size ); - // sha1.Final(); - // memcpy(fileData, sha1.GetHash(), HASH_LENGTH); - // File data and hash - AddFile((const char*)fullPath+rootLen, fullPath, fileData, fileInfo.size+HASH_LENGTH, fileInfo.size, context); - } - } - else if (writeHash) - { -// sha1.Reset(); -// sha1.HashFile((char*)fullPath); -// sha1.Final(); - - unsigned int hash = SuperFastHashFile(fullPath); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); - - // Hash only - // AddFile((const char*)fullPath+rootLen, (const char*)sha1.GetHash(), HASH_LENGTH, fileInfo.size, context); - AddFile((const char*)fullPath+rootLen, fullPath, (const char*)&hash, HASH_LENGTH, fileInfo.size, context); - } - else if (writeData) - { - fileData= (char*) rakMalloc_Ex( fileInfo.size, _FILE_AND_LINE_ ); - fp = fopen(fullPath, "rb"); - fread(fileData, fileInfo.size, 1, fp); - fclose(fp); - - // File data only - AddFile(fullPath+rootLen, fullPath, fileData, fileInfo.size, fileInfo.size, context); - } - else - { - // Just the filename - AddFile(fullPath+rootLen, fullPath, 0, 0, fileInfo.size, context); - } - - if (fileData) - rakFree_Ex(fileData, _FILE_AND_LINE_ ); - } - else if ((fileInfo.attrib & _A_SUBDIR) && (fileInfo.attrib & (_A_HIDDEN | _A_SYSTEM))==0 && recursive) - { - char *newDir=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ ); - strcpy(newDir, dirSoFar); - strcat(newDir, fileInfo.name); - strcat(newDir, "/"); - dirList.Push(newDir, _FILE_AND_LINE_ ); - } - - } while (_findnext(dir, &fileInfo ) != -1); - - _findclose(dir); - rakFree_Ex(dirSoFar, _FILE_AND_LINE_ ); - } - -} -void FileList::Clear(void) -{ - unsigned i; - for (i=0; iWriteCompressed(fileList.Size()); - unsigned i; - for (i=0; i < fileList.Size(); i++) - { - outBitStream->WriteCompressed(fileList[i].context.op); - outBitStream->WriteCompressed(fileList[i].context.fileId); - StringCompressor::Instance()->EncodeString(fileList[i].filename.C_String(), MAX_FILENAME_LENGTH, outBitStream); - - bool writeFileData = fileList[i].dataLengthBytes>0==true; - outBitStream->Write(writeFileData); - if (writeFileData) - { - outBitStream->WriteCompressed(fileList[i].dataLengthBytes); - outBitStream->Write(fileList[i].data, fileList[i].dataLengthBytes); - } - - outBitStream->Write((bool)(fileList[i].fileLengthBytes==fileList[i].dataLengthBytes)); - if (fileList[i].fileLengthBytes!=fileList[i].dataLengthBytes) - outBitStream->WriteCompressed(fileList[i].fileLengthBytes); - } -} -bool FileList::Deserialize(RakNet::BitStream *inBitStream) -{ - bool b, dataLenNonZero=false, fileLenMatchesDataLen=false; - char filename[512]; - unsigned int fileListSize; - FileListNode n; - b=inBitStream->ReadCompressed(fileListSize); -#ifdef _DEBUG - RakAssert(b); - RakAssert(fileListSize < 10000); -#endif - if (b==false || fileListSize > 10000) - return false; // Sanity check - Clear(); - unsigned i; - for (i=0; i < fileListSize; i++) - { - inBitStream->ReadCompressed(n.context.op); - inBitStream->ReadCompressed(n.context.fileId); - StringCompressor::Instance()->DecodeString((char*)filename, MAX_FILENAME_LENGTH, inBitStream); - inBitStream->Read(dataLenNonZero); - if (dataLenNonZero) - { - inBitStream->ReadCompressed(n.dataLengthBytes); - // sanity check - if (n.dataLengthBytes>2000000000) - { -#ifdef _DEBUG - RakAssert(n.dataLengthBytes<=2000000000); -#endif - return false; - } - n.data=(char*) rakMalloc_Ex( (size_t) n.dataLengthBytes, _FILE_AND_LINE_ ); - inBitStream->Read(n.data, n.dataLengthBytes); - } - else - { - n.dataLengthBytes=0; - n.data=0; - } - - b=inBitStream->Read(fileLenMatchesDataLen); - if (fileLenMatchesDataLen) - n.fileLengthBytes=(unsigned) n.dataLengthBytes; - else - b=inBitStream->ReadCompressed(n.fileLengthBytes); -#ifdef _DEBUG - RakAssert(b); -#endif - if (b==0) - { - Clear(); - return false; - } - n.filename=filename; - n.fullPathToFile=filename; - fileList.Insert(n, _FILE_AND_LINE_); - } - - return true; -} -void FileList::GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir) -{ - // For all files in this list that do not match the input list, write them to the output list. - // dirSubset allows checking only a portion of the files in this list. - unsigned thisIndex, inputIndex; - unsigned dirSubsetLen, localPathLen, remoteSubdirLen; - bool match; - if (dirSubset) - dirSubsetLen = (unsigned int) strlen(dirSubset); - else - dirSubsetLen = 0; - if (remoteSubdir && remoteSubdir[0]) - { - remoteSubdirLen=(unsigned int) strlen(remoteSubdir); - if (IsSlash(remoteSubdir[remoteSubdirLen-1])) - remoteSubdirLen--; - } - else - remoteSubdirLen=0; - - for (thisIndex=0; thisIndex < fileList.Size(); thisIndex++) - { - localPathLen = (unsigned int) fileList[thisIndex].filename.GetLength(); - while (localPathLen>0) - { - if (IsSlash(fileList[thisIndex].filename[localPathLen-1])) - { - localPathLen--; - break; - } - localPathLen--; - } - - // fileList[thisIndex].filename has to match dirSubset and be shorter or equal to it in length. - if (dirSubsetLen>0 && - (localPathLendirSubsetLen && IsSlash(fileList[thisIndex].filename[dirSubsetLen])==false))) - continue; - - match=false; - for (inputIndex=0; inputIndex < input->fileList.Size(); inputIndex++) - { - // If the filenames, hashes, and lengths match then skip this element in fileList. Otherwise write it to output - if (_stricmp(input->fileList[inputIndex].filename.C_String()+remoteSubdirLen,fileList[thisIndex].filename.C_String()+dirSubsetLen)==0) - { - match=true; - if (input->fileList[inputIndex].fileLengthBytes==fileList[thisIndex].fileLengthBytes && - input->fileList[inputIndex].dataLengthBytes==fileList[thisIndex].dataLengthBytes && - memcmp(input->fileList[inputIndex].data,fileList[thisIndex].data,(size_t) fileList[thisIndex].dataLengthBytes)==0) - { - // File exists on both machines and is the same. - break; - } - else - { - // File exists on both machines and is not the same. - output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false); - break; - } - } - } - if (match==false) - { - // Other system does not have the file at all - output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false); - } - } -} -void FileList::ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash) -{ - unsigned fileLength; -// CSHA1 sha1; - FILE *fp; - char fullPath[512]; - unsigned i; -// char *fileData; - - for (i=0; i < fileList.Size(); i++) - { - strcpy(fullPath, applicationDirectory); - FixEndingSlash(fullPath); - strcat(fullPath,fileList[i].filename); - fp=fopen(fullPath, "rb"); - if (fp==0) - { - missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, 0, FileListNodeContext(0,0), false); - } - else - { - fseek(fp, 0, SEEK_END); - fileLength = ftell(fp); - fseek(fp, 0, SEEK_SET); - - if (fileLength != fileList[i].fileLengthBytes && alwaysWriteHash==false) - { - missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false); - } - else - { - -// fileData= (char*) rakMalloc_Ex( fileLength, _FILE_AND_LINE_ ); -// fread(fileData, fileLength, 1, fp); - -// sha1.Reset(); -// sha1.Update( ( unsigned char* ) fileData, fileLength ); -// sha1.Final(); - -// rakFree_Ex(fileData, _FILE_AND_LINE_ ); - - unsigned int hash = SuperFastHashFilePtr(fp); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); - - //if (fileLength != fileList[i].fileLength || memcmp( sha1.GetHash(), fileList[i].data, HASH_LENGTH)!=0) - if (fileLength != fileList[i].fileLengthBytes || memcmp( &hash, fileList[i].data, HASH_LENGTH)!=0) - { - if (neverWriteHash==false) - // missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)sha1.GetHash(), HASH_LENGTH, fileLength, 0); - missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, (const char *) &hash, HASH_LENGTH, fileLength, FileListNodeContext(0,0), false); - else - missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false); - } - } - fclose(fp); - } - } -} -void FileList::PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles) -{ - FILE *fp; - char fullPath[512]; - unsigned i; -// CSHA1 sha1; - - i=0; - while (i < fileList.Size()) - { - rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ ); - strcpy(fullPath, applicationDirectory); - FixEndingSlash(fullPath); - strcat(fullPath,fileList[i].filename.C_String()); - fp=fopen(fullPath, "rb"); - if (fp) - { - if (writeFileHash || writeFileData) - { - fseek(fp, 0, SEEK_END); - fileList[i].fileLengthBytes = ftell(fp); - fseek(fp, 0, SEEK_SET); - if (writeFileHash) - { - if (writeFileData) - { - // Hash + data so offset the data by HASH_LENGTH - fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes+HASH_LENGTH, _FILE_AND_LINE_ ); - fread(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes, 1, fp); -// sha1.Reset(); -// sha1.Update((unsigned char*)fileList[i].data+HASH_LENGTH, fileList[i].fileLength); -// sha1.Final(); - unsigned int hash = SuperFastHash(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); -// memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH); - memcpy(fileList[i].data, &hash, HASH_LENGTH); - } - else - { - // Hash only - fileList[i].dataLengthBytes=HASH_LENGTH; - if (fileList[i].fileLengthBytes < HASH_LENGTH) - fileList[i].data=(char*) rakMalloc_Ex( HASH_LENGTH, _FILE_AND_LINE_ ); - else - fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ ); - fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp); - // sha1.Reset(); - // sha1.Update((unsigned char*)fileList[i].data, fileList[i].fileLength); - // sha1.Final(); - unsigned int hash = SuperFastHash(fileList[i].data, fileList[i].fileLengthBytes); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); - // memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH); - memcpy(fileList[i].data, &hash, HASH_LENGTH); - } - } - else - { - // Data only - fileList[i].dataLengthBytes=fileList[i].fileLengthBytes; - fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ ); - fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp); - } - - fclose(fp); - i++; - } - else - { - fileList[i].data=0; - fileList[i].dataLengthBytes=0; - } - } - else - { - if (removeUnknownFiles) - { - fileList.RemoveAtIndex(i); - } - else - i++; - } - } -} -void FileList::FlagFilesAsReferences(void) -{ - for (unsigned int i=0; i < fileList.Size(); i++) - { - fileList[i].isAReference=true; - fileList[i].dataLengthBytes=fileList[i].fileLengthBytes; - } -} -void FileList::WriteDataToDisk(const char *applicationDirectory) -{ - char fullPath[512]; - unsigned i,j; - - for (i=0; i < fileList.Size(); i++) - { - strcpy(fullPath, applicationDirectory); - FixEndingSlash(fullPath); - strcat(fullPath,fileList[i].filename.C_String()); - - // Security - Don't allow .. in the filename anywhere so you can't write outside of the root directory - for (j=1; j < fileList[i].filename.GetLength(); j++) - { - if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.') - { -#ifdef _DEBUG - RakAssert(0); -#endif - // Just cancel the write entirely - return; - } - } - - WriteFileWithDirectories(fullPath, fileList[i].data, (unsigned int) fileList[i].dataLengthBytes); - } -} - -#ifdef _MSC_VER -#pragma warning( disable : 4966 ) // unlink declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. -#endif -void FileList::DeleteFiles(const char *applicationDirectory) -{ - - - - char fullPath[512]; - unsigned i,j; - - for (i=0; i < fileList.Size(); i++) - { - // The filename should not have .. in the path - if it does ignore it - for (j=1; j < fileList[i].filename.GetLength(); j++) - { - if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.') - { -#ifdef _DEBUG - RakAssert(0); -#endif - // Just cancel the deletion entirely - return; - } - } - - strcpy(fullPath, applicationDirectory); - FixEndingSlash(fullPath); - strcat(fullPath, fileList[i].filename.C_String()); - -#ifdef _MSC_VER -#pragma warning( disable : 4966 ) // unlink declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. -#endif - int result = unlink(fullPath); - if (result!=0) - { - RAKNET_DEBUG_PRINTF("FileList::DeleteFiles: unlink (%s) failed.\n", fullPath); - } - } - -} - -void FileList::AddCallback(FileListProgress *cb) -{ - if (cb==0) - return; - - if ((unsigned int) fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int)-1) - fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_); -} -void FileList::RemoveCallback(FileListProgress *cb) -{ - unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb); - if (idx!=(unsigned int) -1) - fileListProgressCallbacks.RemoveAtIndex(idx); -} -void FileList::ClearCallbacks(void) -{ - fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_); -} -void FileList::GetCallbacks(DataStructures::List &callbacks) -{ - callbacks = fileListProgressCallbacks; -} - - -bool FileList::FixEndingSlash(char *str) -{ -#ifdef _WIN32 - if (str[strlen(str)-1]!='/' && str[strlen(str)-1]!='\\') - { - strcat(str, "\\"); // Only \ works with system commands, used by AutopatcherClient - return true; - } -#else - if (str[strlen(str)-1]!='\\' && str[strlen(str)-1]!='/') - { - strcat(str, "/"); // Only / works with Linux - return true; - } -#endif - - return false; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_FileOperations +#include "FileList.h" + +#if _RAKNET_SUPPORT_FileOperations==1 + +#include // RAKNET_DEBUG_PRINTF +#include "RakAssert.h" +#if defined(ANDROID) +#include +#elif defined(_WIN32) || defined(__CYGWIN__) +#include + + +// #elif !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) && !defined ( __PPC__ ) && !defined ( __FreeBSD__ ) && !defined ( __S3E__ ) +// #include +#endif + + +#ifdef _WIN32 +// For mkdir +#include + + +#else +#include +#endif + +//#include "SHA1.h" +#include "DS_Queue.h" +#include "StringCompressor.h" +#include "BitStream.h" +#include "FileOperations.h" +#include "SuperFastHash.h" +#include "RakAssert.h" +#include "LinuxStrings.h" + +#define MAX_FILENAME_LENGTH 512 +static const unsigned HASH_LENGTH=4; + +using namespace RakNet; + +// alloca + +#if defined(_WIN32) +#include + + +#else +#if !defined ( __FreeBSD__ ) +#include +#endif +#include +#include +#include +#include "_FindFirst.h" +#include //defines intptr_t +#endif + +#include "RakAlloca.h" + +//int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data ) +//{ +// return strcmp(key, data.filename); +//} + + +STATIC_FACTORY_DEFINITIONS(FileListProgress,FileListProgress) +STATIC_FACTORY_DEFINITIONS(FLP_Printf,FLP_Printf) +STATIC_FACTORY_DEFINITIONS(FileList,FileList) + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// First callback called when FileList::AddFilesFromDirectory() starts +void FLP_Printf::OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) { + (void) fileList; + RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dir);} + +/// Called for each directory, when that directory begins processing +void FLP_Printf::OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) { + (void) fileList; + RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", dir, directoriesRemaining);} +void FLP_Printf::OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID ) +{ + (void) setID; + + char str[32]; + systemAddress.ToString(true, (char*) str); + RAKNET_DEBUG_PRINTF("File pushes complete to %s\n", str); +} +void FLP_Printf::OnSendAborted( SystemAddress systemAddress ) +{ + char str[32]; + systemAddress.ToString(true, (char*) str); + RAKNET_DEBUG_PRINTF("Send aborted to %s\n", str); +} +FileList::FileList() +{ +} +FileList::~FileList() +{ + Clear(); +} +void FileList::AddFile(const char *filepath, const char *filename, FileListNodeContext context) +{ + if (filepath==0 || filename==0) + return; + + char *data; + //std::fstream file; + //file.open(filename, std::ios::in | std::ios::binary); + + FILE *fp = fopen(filepath, "rb"); + if (fp==0) + return; + fseek(fp, 0, SEEK_END); + int length = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (length > (int) ((unsigned int)-1 / 8)) + { + // If this assert hits, split up your file. You could also change BitSize_t in RakNetTypes.h to unsigned long long but this is not recommended for performance reasons + RakAssert("Cannot add files over 536 MB" && 0); + fclose(fp); + return; + } + + + + bool usedAlloca=false; + if (length < MAX_ALLOCA_STACK_ALLOCATION) + { + data = ( char* ) alloca( length ); + usedAlloca=true; + } + else + + { + data = (char*) rakMalloc_Ex( length, _FILE_AND_LINE_ ); + } + + fread(data, 1, length, fp); + AddFile(filename, filepath, data, length, length, context); + fclose(fp); + + + if (usedAlloca==false) + + rakFree_Ex(data, _FILE_AND_LINE_ ); + +} +void FileList::AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference, bool takeDataPointer) +{ + if (filename==0) + return; + if (strlen(filename)>MAX_FILENAME_LENGTH) + { + // Should be enough for anyone + RakAssert(0); + return; + } + // If adding a reference, do not send data + RakAssert(isAReference==false || data==0); + // Avoid duplicate insertions unless the data is different, in which case overwrite the old data + unsigned i; + for (i=0; i dirList; + char root[260]; + char fullPath[520]; + _finddata_t fileInfo; + intptr_t dir; + FILE *fp; + char *dirSoFar, *fileData; + dirSoFar=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ ); + + if (applicationDirectory) + strcpy(root, applicationDirectory); + else + root[0]=0; + + int rootLen=(int)strlen(root); + if (rootLen) + { + strcpy(dirSoFar, root); + if (FixEndingSlash(dirSoFar)) + rootLen++; + } + else + dirSoFar[0]=0; + + if (subDirectory) + { + strcat(dirSoFar, subDirectory); + FixEndingSlash(dirSoFar); + } + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnAddFilesFromDirectoryStarted(this, dirSoFar); + // RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dirSoFar); + dirList.Push(dirSoFar, _FILE_AND_LINE_ ); + while (dirList.Size()) + { + dirSoFar=dirList.Pop(); + strcpy(fullPath, dirSoFar); + // Changed from *.* to * for Linux compatibility + strcat(fullPath, "*"); + + + dir=_findfirst(fullPath, &fileInfo ); + if (dir==-1) + { + _findclose(dir); + rakFree_Ex(dirSoFar, _FILE_AND_LINE_ ); + unsigned i; + for (i=0; i < dirList.Size(); i++) + rakFree_Ex(dirList[i], _FILE_AND_LINE_ ); + return; + } + +// RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", fullPath, dirList.Size()); + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnDirectory(this, fullPath, dirList.Size()); + + do + { + // no guarantee these entries are first... + if (strcmp("." , fileInfo.name) == 0 || + strcmp("..", fileInfo.name) == 0) + { + continue; + } + + if ((fileInfo.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM))==0) + { + strcpy(fullPath, dirSoFar); + strcat(fullPath, fileInfo.name); + fileData=0; + + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnFile(this, dirSoFar, fileInfo.name, fileInfo.size); + + if (writeData && writeHash) + { + fp = fopen(fullPath, "rb"); + if (fp) + { + fileData= (char*) rakMalloc_Ex( fileInfo.size+HASH_LENGTH, _FILE_AND_LINE_ ); + fread(fileData+HASH_LENGTH, fileInfo.size, 1, fp); + fclose(fp); + + unsigned int hash = SuperFastHash(fileData+HASH_LENGTH, fileInfo.size); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); + memcpy(fileData, &hash, HASH_LENGTH); + + // sha1.Reset(); + // sha1.Update( ( unsigned char* ) fileData+HASH_LENGTH, fileInfo.size ); + // sha1.Final(); + // memcpy(fileData, sha1.GetHash(), HASH_LENGTH); + // File data and hash + AddFile((const char*)fullPath+rootLen, fullPath, fileData, fileInfo.size+HASH_LENGTH, fileInfo.size, context); + } + } + else if (writeHash) + { +// sha1.Reset(); +// sha1.HashFile((char*)fullPath); +// sha1.Final(); + + unsigned int hash = SuperFastHashFile(fullPath); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); + + // Hash only + // AddFile((const char*)fullPath+rootLen, (const char*)sha1.GetHash(), HASH_LENGTH, fileInfo.size, context); + AddFile((const char*)fullPath+rootLen, fullPath, (const char*)&hash, HASH_LENGTH, fileInfo.size, context); + } + else if (writeData) + { + fileData= (char*) rakMalloc_Ex( fileInfo.size, _FILE_AND_LINE_ ); + fp = fopen(fullPath, "rb"); + fread(fileData, fileInfo.size, 1, fp); + fclose(fp); + + // File data only + AddFile(fullPath+rootLen, fullPath, fileData, fileInfo.size, fileInfo.size, context); + } + else + { + // Just the filename + AddFile(fullPath+rootLen, fullPath, 0, 0, fileInfo.size, context); + } + + if (fileData) + rakFree_Ex(fileData, _FILE_AND_LINE_ ); + } + else if ((fileInfo.attrib & _A_SUBDIR) && (fileInfo.attrib & (_A_HIDDEN | _A_SYSTEM))==0 && recursive) + { + char *newDir=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ ); + strcpy(newDir, dirSoFar); + strcat(newDir, fileInfo.name); + strcat(newDir, "/"); + dirList.Push(newDir, _FILE_AND_LINE_ ); + } + + } while (_findnext(dir, &fileInfo ) != -1); + + _findclose(dir); + rakFree_Ex(dirSoFar, _FILE_AND_LINE_ ); + } + +} +void FileList::Clear(void) +{ + unsigned i; + for (i=0; iWriteCompressed(fileList.Size()); + unsigned i; + for (i=0; i < fileList.Size(); i++) + { + outBitStream->WriteCompressed(fileList[i].context.op); + outBitStream->WriteCompressed(fileList[i].context.fileId); + StringCompressor::Instance()->EncodeString(fileList[i].filename.C_String(), MAX_FILENAME_LENGTH, outBitStream); + + bool writeFileData = fileList[i].dataLengthBytes>0==true; + outBitStream->Write(writeFileData); + if (writeFileData) + { + outBitStream->WriteCompressed(fileList[i].dataLengthBytes); + outBitStream->Write(fileList[i].data, fileList[i].dataLengthBytes); + } + + outBitStream->Write((bool)(fileList[i].fileLengthBytes==fileList[i].dataLengthBytes)); + if (fileList[i].fileLengthBytes!=fileList[i].dataLengthBytes) + outBitStream->WriteCompressed(fileList[i].fileLengthBytes); + } +} +bool FileList::Deserialize(RakNet::BitStream *inBitStream) +{ + bool b, dataLenNonZero=false, fileLenMatchesDataLen=false; + char filename[512]; + unsigned int fileListSize; + FileListNode n; + b=inBitStream->ReadCompressed(fileListSize); +#ifdef _DEBUG + RakAssert(b); + RakAssert(fileListSize < 10000); +#endif + if (b==false || fileListSize > 10000) + return false; // Sanity check + Clear(); + unsigned i; + for (i=0; i < fileListSize; i++) + { + inBitStream->ReadCompressed(n.context.op); + inBitStream->ReadCompressed(n.context.fileId); + StringCompressor::Instance()->DecodeString((char*)filename, MAX_FILENAME_LENGTH, inBitStream); + inBitStream->Read(dataLenNonZero); + if (dataLenNonZero) + { + inBitStream->ReadCompressed(n.dataLengthBytes); + // sanity check + if (n.dataLengthBytes>2000000000) + { +#ifdef _DEBUG + RakAssert(n.dataLengthBytes<=2000000000); +#endif + return false; + } + n.data=(char*) rakMalloc_Ex( (size_t) n.dataLengthBytes, _FILE_AND_LINE_ ); + inBitStream->Read(n.data, n.dataLengthBytes); + } + else + { + n.dataLengthBytes=0; + n.data=0; + } + + b=inBitStream->Read(fileLenMatchesDataLen); + if (fileLenMatchesDataLen) + n.fileLengthBytes=(unsigned) n.dataLengthBytes; + else + b=inBitStream->ReadCompressed(n.fileLengthBytes); +#ifdef _DEBUG + RakAssert(b); +#endif + if (b==0) + { + Clear(); + return false; + } + n.filename=filename; + n.fullPathToFile=filename; + fileList.Insert(n, _FILE_AND_LINE_); + } + + return true; +} +void FileList::GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir) +{ + // For all files in this list that do not match the input list, write them to the output list. + // dirSubset allows checking only a portion of the files in this list. + unsigned thisIndex, inputIndex; + unsigned dirSubsetLen, localPathLen, remoteSubdirLen; + bool match; + if (dirSubset) + dirSubsetLen = (unsigned int) strlen(dirSubset); + else + dirSubsetLen = 0; + if (remoteSubdir && remoteSubdir[0]) + { + remoteSubdirLen=(unsigned int) strlen(remoteSubdir); + if (IsSlash(remoteSubdir[remoteSubdirLen-1])) + remoteSubdirLen--; + } + else + remoteSubdirLen=0; + + for (thisIndex=0; thisIndex < fileList.Size(); thisIndex++) + { + localPathLen = (unsigned int) fileList[thisIndex].filename.GetLength(); + while (localPathLen>0) + { + if (IsSlash(fileList[thisIndex].filename[localPathLen-1])) + { + localPathLen--; + break; + } + localPathLen--; + } + + // fileList[thisIndex].filename has to match dirSubset and be shorter or equal to it in length. + if (dirSubsetLen>0 && + (localPathLendirSubsetLen && IsSlash(fileList[thisIndex].filename[dirSubsetLen])==false))) + continue; + + match=false; + for (inputIndex=0; inputIndex < input->fileList.Size(); inputIndex++) + { + // If the filenames, hashes, and lengths match then skip this element in fileList. Otherwise write it to output + if (_stricmp(input->fileList[inputIndex].filename.C_String()+remoteSubdirLen,fileList[thisIndex].filename.C_String()+dirSubsetLen)==0) + { + match=true; + if (input->fileList[inputIndex].fileLengthBytes==fileList[thisIndex].fileLengthBytes && + input->fileList[inputIndex].dataLengthBytes==fileList[thisIndex].dataLengthBytes && + memcmp(input->fileList[inputIndex].data,fileList[thisIndex].data,(size_t) fileList[thisIndex].dataLengthBytes)==0) + { + // File exists on both machines and is the same. + break; + } + else + { + // File exists on both machines and is not the same. + output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false); + break; + } + } + } + if (match==false) + { + // Other system does not have the file at all + output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false); + } + } +} +void FileList::ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash) +{ + unsigned fileLength; +// CSHA1 sha1; + FILE *fp; + char fullPath[512]; + unsigned i; +// char *fileData; + + for (i=0; i < fileList.Size(); i++) + { + strcpy(fullPath, applicationDirectory); + FixEndingSlash(fullPath); + strcat(fullPath,fileList[i].filename); + fp=fopen(fullPath, "rb"); + if (fp==0) + { + missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, 0, FileListNodeContext(0,0), false); + } + else + { + fseek(fp, 0, SEEK_END); + fileLength = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (fileLength != fileList[i].fileLengthBytes && alwaysWriteHash==false) + { + missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false); + } + else + { + +// fileData= (char*) rakMalloc_Ex( fileLength, _FILE_AND_LINE_ ); +// fread(fileData, fileLength, 1, fp); + +// sha1.Reset(); +// sha1.Update( ( unsigned char* ) fileData, fileLength ); +// sha1.Final(); + +// rakFree_Ex(fileData, _FILE_AND_LINE_ ); + + unsigned int hash = SuperFastHashFilePtr(fp); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); + + //if (fileLength != fileList[i].fileLength || memcmp( sha1.GetHash(), fileList[i].data, HASH_LENGTH)!=0) + if (fileLength != fileList[i].fileLengthBytes || memcmp( &hash, fileList[i].data, HASH_LENGTH)!=0) + { + if (neverWriteHash==false) + // missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)sha1.GetHash(), HASH_LENGTH, fileLength, 0); + missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, (const char *) &hash, HASH_LENGTH, fileLength, FileListNodeContext(0,0), false); + else + missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false); + } + } + fclose(fp); + } + } +} +void FileList::PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles) +{ + FILE *fp; + char fullPath[512]; + unsigned i; +// CSHA1 sha1; + + i=0; + while (i < fileList.Size()) + { + rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ ); + strcpy(fullPath, applicationDirectory); + FixEndingSlash(fullPath); + strcat(fullPath,fileList[i].filename.C_String()); + fp=fopen(fullPath, "rb"); + if (fp) + { + if (writeFileHash || writeFileData) + { + fseek(fp, 0, SEEK_END); + fileList[i].fileLengthBytes = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (writeFileHash) + { + if (writeFileData) + { + // Hash + data so offset the data by HASH_LENGTH + fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes+HASH_LENGTH, _FILE_AND_LINE_ ); + fread(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes, 1, fp); +// sha1.Reset(); +// sha1.Update((unsigned char*)fileList[i].data+HASH_LENGTH, fileList[i].fileLength); +// sha1.Final(); + unsigned int hash = SuperFastHash(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); +// memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH); + memcpy(fileList[i].data, &hash, HASH_LENGTH); + } + else + { + // Hash only + fileList[i].dataLengthBytes=HASH_LENGTH; + if (fileList[i].fileLengthBytes < HASH_LENGTH) + fileList[i].data=(char*) rakMalloc_Ex( HASH_LENGTH, _FILE_AND_LINE_ ); + else + fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ ); + fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp); + // sha1.Reset(); + // sha1.Update((unsigned char*)fileList[i].data, fileList[i].fileLength); + // sha1.Final(); + unsigned int hash = SuperFastHash(fileList[i].data, fileList[i].fileLengthBytes); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash)); + // memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH); + memcpy(fileList[i].data, &hash, HASH_LENGTH); + } + } + else + { + // Data only + fileList[i].dataLengthBytes=fileList[i].fileLengthBytes; + fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ ); + fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp); + } + + fclose(fp); + i++; + } + else + { + fileList[i].data=0; + fileList[i].dataLengthBytes=0; + } + } + else + { + if (removeUnknownFiles) + { + fileList.RemoveAtIndex(i); + } + else + i++; + } + } +} +void FileList::FlagFilesAsReferences(void) +{ + for (unsigned int i=0; i < fileList.Size(); i++) + { + fileList[i].isAReference=true; + fileList[i].dataLengthBytes=fileList[i].fileLengthBytes; + } +} +void FileList::WriteDataToDisk(const char *applicationDirectory) +{ + char fullPath[512]; + unsigned i,j; + + for (i=0; i < fileList.Size(); i++) + { + strcpy(fullPath, applicationDirectory); + FixEndingSlash(fullPath); + strcat(fullPath,fileList[i].filename.C_String()); + + // Security - Don't allow .. in the filename anywhere so you can't write outside of the root directory + for (j=1; j < fileList[i].filename.GetLength(); j++) + { + if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.') + { +#ifdef _DEBUG + RakAssert(0); +#endif + // Just cancel the write entirely + return; + } + } + + WriteFileWithDirectories(fullPath, fileList[i].data, (unsigned int) fileList[i].dataLengthBytes); + } +} + +#ifdef _MSC_VER +#pragma warning( disable : 4966 ) // unlink declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. +#endif +void FileList::DeleteFiles(const char *applicationDirectory) +{ + + + + char fullPath[512]; + unsigned i,j; + + for (i=0; i < fileList.Size(); i++) + { + // The filename should not have .. in the path - if it does ignore it + for (j=1; j < fileList[i].filename.GetLength(); j++) + { + if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.') + { +#ifdef _DEBUG + RakAssert(0); +#endif + // Just cancel the deletion entirely + return; + } + } + + strcpy(fullPath, applicationDirectory); + FixEndingSlash(fullPath); + strcat(fullPath, fileList[i].filename.C_String()); + +#ifdef _MSC_VER +#pragma warning( disable : 4966 ) // unlink declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. +#endif + int result = unlink(fullPath); + if (result!=0) + { + RAKNET_DEBUG_PRINTF("FileList::DeleteFiles: unlink (%s) failed.\n", fullPath); + } + } + +} + +void FileList::AddCallback(FileListProgress *cb) +{ + if (cb==0) + return; + + if ((unsigned int) fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int)-1) + fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_); +} +void FileList::RemoveCallback(FileListProgress *cb) +{ + unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb); + if (idx!=(unsigned int) -1) + fileListProgressCallbacks.RemoveAtIndex(idx); +} +void FileList::ClearCallbacks(void) +{ + fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_); +} +void FileList::GetCallbacks(DataStructures::List &callbacks) +{ + callbacks = fileListProgressCallbacks; +} + + +bool FileList::FixEndingSlash(char *str) +{ +#ifdef _WIN32 + if (str[strlen(str)-1]!='/' && str[strlen(str)-1]!='\\') + { + strcat(str, "\\"); // Only \ works with system commands, used by AutopatcherClient + return true; + } +#else + if (str[strlen(str)-1]!='\\' && str[strlen(str)-1]!='/') + { + strcat(str, "/"); // Only / works with Linux + return true; + } +#endif + + return false; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_FileOperations diff --git a/src/raknet/FileList.h b/src/raknet/FileList.h index 6469a1f..4642658 100755 --- a/src/raknet/FileList.h +++ b/src/raknet/FileList.h @@ -1,258 +1,258 @@ -/// \file FileList.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __FILE_LIST -#define __FILE_LIST - -#include "Export.h" -#include "DS_List.h" -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "FileListNodeContext.h" -#include "RakString.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -namespace RakNet -{ - class BitStream; -} - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -class FileList; - - -/// Represents once instance of a file -struct FileListNode -{ - /// Name of the file - RakNet::RakString filename; - - /// Full path to the file, which may be different than filename - RakNet::RakString fullPathToFile; - - /// File data (may be null if not ready) - char *data; - - /// Length of \a data. May be greater than fileLength if prepended with a file hash - BitSize_t dataLengthBytes; - - /// Length of the file - unsigned fileLengthBytes; - - /// User specific data for whatever, describing this file. - FileListNodeContext context; - - /// If true, data and dataLengthBytes should be empty. This is just storing the filename - bool isAReference; -}; - -/// Callback interface set with FileList::SetCallback() in case you want progress notifications when FileList::AddFilesFromDirectory() is called -class RAK_DLL_EXPORT FileListProgress -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FileListProgress) - - FileListProgress() {} - virtual ~FileListProgress() {} - - /// First callback called when FileList::AddFilesFromDirectory() starts - virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) { - (void) fileList; - (void) dir; - } - - /// Called for each directory, when that directory begins processing - virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) { - (void) fileList; - (void) dir; - (void) directoriesRemaining; - } - - /// Called for each file, when that file begins processing - virtual void OnFile(FileList *fileList, char *dir, char *fileName, unsigned int fileSize) { - (void) fileList; - (void) dir; - (void) fileName; - (void) fileSize; - } - - /// \brief This function is called when we are sending a file to a remote system. - /// \param[in] fileName The name of the file being sent - /// \param[in] fileLengthBytes How long the file is - /// \param[in] offset The offset in bytes into the file that we are sending - /// \param[in] bytesBeingSent How many bytes we are sending this push - /// \param[in] done If this file is now done with this push - /// \param[in] targetSystem Who we are sending to - virtual void OnFilePush(const char *fileName, unsigned int fileLengthBytes, unsigned int offset, unsigned int bytesBeingSent, bool done, SystemAddress targetSystem, unsigned short setId) - { - (void) fileName; - (void) fileLengthBytes; - (void) offset; - (void) bytesBeingSent; - (void) done; - (void) targetSystem; - (void) setId; - } - - /// \brief This function is called when all files have been read and are being transferred to a remote system - virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setId ) - { - (void) systemAddress; - (void) setId; - } - - /// \brief This function is called when a send to a system was aborted (probably due to disconnection) - virtual void OnSendAborted( SystemAddress systemAddress ) - { - (void) systemAddress; - } -}; - -/// Implementation of FileListProgress to use RAKNET_DEBUG_PRINTF -class RAK_DLL_EXPORT FLP_Printf : public FileListProgress -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FLP_Printf) - - FLP_Printf() {} - virtual ~FLP_Printf() {} - - /// First callback called when FileList::AddFilesFromDirectory() starts - virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir); - - /// Called for each directory, when that directory begins processing - virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining); - - /// \brief This function is called when all files have been transferred to a particular remote system - virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID ); - - /// \brief This function is called when a send to a system was aborted (probably due to disconnection) - virtual void OnSendAborted( SystemAddress systemAddress ); -}; - -class RAK_DLL_EXPORT FileList -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FileList) - - FileList(); - ~FileList(); - /// \brief Add all the files at a given directory. - /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. - /// \param[in] subDirectory The rest of the path to the file. This is stored as a prefix to the filename - /// \param[in] writeHash The first 4 bytes is a hash of the file, with the remainder the actual file data (should \a writeData be true) - /// \param[in] writeData Write the contents of each file - /// \param[in] recursive Whether or not to visit subdirectories - /// \param[in] context User defined byte to store with each file. Use for whatever you want. - void AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, FileListNodeContext context); - - /// Deallocate all memory - void Clear(void); - - /// Write all encoded data into a bitstream - void Serialize(RakNet::BitStream *outBitStream); - - /// Read all encoded data from a bitstream. Clear() is called before deserializing. - bool Deserialize(RakNet::BitStream *inBitStream); - - /// \brief Given the existing set of files, search applicationDirectory for the same files. - /// \details For each file that is missing or different, add that file to \a missingOrChangedFiles. Note: the file contents are not written, and only the hash if written if \a alwaysWriteHash is true - /// alwaysWriteHash and neverWriteHash are optimizations to avoid reading the file contents to generate the hash if not necessary because the file is missing or has different lengths anyway. - /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. - /// \param[out] missingOrChangedFiles Output list written to - /// \param[in] alwaysWriteHash If true, and neverWriteHash is false, will hash the file content of the file on disk, and write that as the file data with a length of SHA1_LENGTH bytes. If false, if the file length is different, will only write the filename. - /// \param[in] neverWriteHash If true, will never write the hash, even if available. If false, will write the hash if the file lengths are the same and it was forced to do a comparison. - void ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash); - - /// \brief Return the files that need to be written to make \a input match this current FileList. - /// \details Specify dirSubset to only consider files that start with this path - /// specify remoteSubdir to assume that all filenames in input start with this path, so strip it off when comparing filenames. - /// \param[in] input Full list of files - /// \param[out] output Files that we need to match input - /// \param[in] dirSubset If the filename does not start with this path, just skip this file. - /// \param[in] remoteSubdir Remove this from the filenames of \a input when comparing to existing filenames. - void GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir); - - /// \brief Assuming FileList contains a list of filenames presumably without data, read the data for these filenames - /// \param[in] applicationDirectory Prepend this path to each filename. Trailing slash will be added if necessary. Use \ as the path delineator. - /// \param[in] writeFileData True to read and store the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true - /// \param[in] writeFileHash True to read and store the hash of the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true - /// \param[in] removeUnknownFiles If a file does not exist on disk but is in the file list, remove it from the file list? - void PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles); - - /// By default, GetDeltaToCurrent tags files as non-references, meaning they are assumed to be populated later - /// This tags all files as references, required for IncrementalReadInterface to process them incrementally - void FlagFilesAsReferences(void); - - /// \brief Write all files to disk, prefixing the paths with applicationDirectory - /// \param[in] applicationDirectory path prefix - void WriteDataToDisk(const char *applicationDirectory); - - /// \brief Add a file, given data already in memory. - /// \param[in] filename Name of a file, optionally prefixed with a partial or complete path. Use \ as the path delineator. - /// \param[in] fullPathToFile Full path to the file on disk - /// \param[in] data Contents to write - /// \param[in] dataLength length of the data, which may be greater than fileLength should you prefix extra data, such as the hash - /// \param[in] fileLength Length of the file - /// \param[in] context User defined byte to store with each file. Use for whatever you want. - /// \param[in] isAReference Means that this is just a reference to a file elsewhere - does not actually have any data - /// \param[in] takeDataPointer If true, do not allocate dataLength. Just take the pointer passed to the \a data parameter - void AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference=false, bool takeDataPointer=false); - - /// \brief Add a file, reading it from disk. - /// \param[in] filepath Complete path to the file, including the filename itself - /// \param[in] filename filename to store internally, anything you want, but usually either the complete path or a subset of the complete path. - /// \param[in] context User defined byte to store with each file. Use for whatever you want. - void AddFile(const char *filepath, const char *filename, FileListNodeContext context); - - /// \brief Delete all files stored in the file list. - /// \param[in] applicationDirectory Prefixed to the path to each filename. Use \ as the path delineator. - void DeleteFiles(const char *applicationDirectory); - - /// \brief Adds a callback to get progress reports about what the file list instances do. - /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. - void AddCallback(FileListProgress *cb); - - /// \brief Removes a callback - /// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback() - void RemoveCallback(FileListProgress *cb); - - /// \brief Removes all callbacks - void ClearCallbacks(void); - - /// Returns all callbacks added with AddCallback() - /// \param[out] callbacks The list is set to the list of callbacks - void GetCallbacks(DataStructures::List &callbacks); - - // Here so you can read it, but don't modify it - DataStructures::List fileList; - - static bool FixEndingSlash(char *str); -protected: - DataStructures::List fileListProgressCallbacks; -}; - -} // namespace RakNet - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - -#endif // _RAKNET_SUPPORT_FileOperations +/// \file FileList.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __FILE_LIST +#define __FILE_LIST + +#include "Export.h" +#include "DS_List.h" +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "FileListNodeContext.h" +#include "RakString.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +namespace RakNet +{ + class BitStream; +} + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +class FileList; + + +/// Represents once instance of a file +struct FileListNode +{ + /// Name of the file + RakNet::RakString filename; + + /// Full path to the file, which may be different than filename + RakNet::RakString fullPathToFile; + + /// File data (may be null if not ready) + char *data; + + /// Length of \a data. May be greater than fileLength if prepended with a file hash + BitSize_t dataLengthBytes; + + /// Length of the file + unsigned fileLengthBytes; + + /// User specific data for whatever, describing this file. + FileListNodeContext context; + + /// If true, data and dataLengthBytes should be empty. This is just storing the filename + bool isAReference; +}; + +/// Callback interface set with FileList::SetCallback() in case you want progress notifications when FileList::AddFilesFromDirectory() is called +class RAK_DLL_EXPORT FileListProgress +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FileListProgress) + + FileListProgress() {} + virtual ~FileListProgress() {} + + /// First callback called when FileList::AddFilesFromDirectory() starts + virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) { + (void) fileList; + (void) dir; + } + + /// Called for each directory, when that directory begins processing + virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) { + (void) fileList; + (void) dir; + (void) directoriesRemaining; + } + + /// Called for each file, when that file begins processing + virtual void OnFile(FileList *fileList, char *dir, char *fileName, unsigned int fileSize) { + (void) fileList; + (void) dir; + (void) fileName; + (void) fileSize; + } + + /// \brief This function is called when we are sending a file to a remote system. + /// \param[in] fileName The name of the file being sent + /// \param[in] fileLengthBytes How long the file is + /// \param[in] offset The offset in bytes into the file that we are sending + /// \param[in] bytesBeingSent How many bytes we are sending this push + /// \param[in] done If this file is now done with this push + /// \param[in] targetSystem Who we are sending to + virtual void OnFilePush(const char *fileName, unsigned int fileLengthBytes, unsigned int offset, unsigned int bytesBeingSent, bool done, SystemAddress targetSystem, unsigned short setId) + { + (void) fileName; + (void) fileLengthBytes; + (void) offset; + (void) bytesBeingSent; + (void) done; + (void) targetSystem; + (void) setId; + } + + /// \brief This function is called when all files have been read and are being transferred to a remote system + virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setId ) + { + (void) systemAddress; + (void) setId; + } + + /// \brief This function is called when a send to a system was aborted (probably due to disconnection) + virtual void OnSendAborted( SystemAddress systemAddress ) + { + (void) systemAddress; + } +}; + +/// Implementation of FileListProgress to use RAKNET_DEBUG_PRINTF +class RAK_DLL_EXPORT FLP_Printf : public FileListProgress +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FLP_Printf) + + FLP_Printf() {} + virtual ~FLP_Printf() {} + + /// First callback called when FileList::AddFilesFromDirectory() starts + virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir); + + /// Called for each directory, when that directory begins processing + virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining); + + /// \brief This function is called when all files have been transferred to a particular remote system + virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID ); + + /// \brief This function is called when a send to a system was aborted (probably due to disconnection) + virtual void OnSendAborted( SystemAddress systemAddress ); +}; + +class RAK_DLL_EXPORT FileList +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FileList) + + FileList(); + ~FileList(); + /// \brief Add all the files at a given directory. + /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. + /// \param[in] subDirectory The rest of the path to the file. This is stored as a prefix to the filename + /// \param[in] writeHash The first 4 bytes is a hash of the file, with the remainder the actual file data (should \a writeData be true) + /// \param[in] writeData Write the contents of each file + /// \param[in] recursive Whether or not to visit subdirectories + /// \param[in] context User defined byte to store with each file. Use for whatever you want. + void AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, FileListNodeContext context); + + /// Deallocate all memory + void Clear(void); + + /// Write all encoded data into a bitstream + void Serialize(RakNet::BitStream *outBitStream); + + /// Read all encoded data from a bitstream. Clear() is called before deserializing. + bool Deserialize(RakNet::BitStream *inBitStream); + + /// \brief Given the existing set of files, search applicationDirectory for the same files. + /// \details For each file that is missing or different, add that file to \a missingOrChangedFiles. Note: the file contents are not written, and only the hash if written if \a alwaysWriteHash is true + /// alwaysWriteHash and neverWriteHash are optimizations to avoid reading the file contents to generate the hash if not necessary because the file is missing or has different lengths anyway. + /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. + /// \param[out] missingOrChangedFiles Output list written to + /// \param[in] alwaysWriteHash If true, and neverWriteHash is false, will hash the file content of the file on disk, and write that as the file data with a length of SHA1_LENGTH bytes. If false, if the file length is different, will only write the filename. + /// \param[in] neverWriteHash If true, will never write the hash, even if available. If false, will write the hash if the file lengths are the same and it was forced to do a comparison. + void ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash); + + /// \brief Return the files that need to be written to make \a input match this current FileList. + /// \details Specify dirSubset to only consider files that start with this path + /// specify remoteSubdir to assume that all filenames in input start with this path, so strip it off when comparing filenames. + /// \param[in] input Full list of files + /// \param[out] output Files that we need to match input + /// \param[in] dirSubset If the filename does not start with this path, just skip this file. + /// \param[in] remoteSubdir Remove this from the filenames of \a input when comparing to existing filenames. + void GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir); + + /// \brief Assuming FileList contains a list of filenames presumably without data, read the data for these filenames + /// \param[in] applicationDirectory Prepend this path to each filename. Trailing slash will be added if necessary. Use \ as the path delineator. + /// \param[in] writeFileData True to read and store the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true + /// \param[in] writeFileHash True to read and store the hash of the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true + /// \param[in] removeUnknownFiles If a file does not exist on disk but is in the file list, remove it from the file list? + void PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles); + + /// By default, GetDeltaToCurrent tags files as non-references, meaning they are assumed to be populated later + /// This tags all files as references, required for IncrementalReadInterface to process them incrementally + void FlagFilesAsReferences(void); + + /// \brief Write all files to disk, prefixing the paths with applicationDirectory + /// \param[in] applicationDirectory path prefix + void WriteDataToDisk(const char *applicationDirectory); + + /// \brief Add a file, given data already in memory. + /// \param[in] filename Name of a file, optionally prefixed with a partial or complete path. Use \ as the path delineator. + /// \param[in] fullPathToFile Full path to the file on disk + /// \param[in] data Contents to write + /// \param[in] dataLength length of the data, which may be greater than fileLength should you prefix extra data, such as the hash + /// \param[in] fileLength Length of the file + /// \param[in] context User defined byte to store with each file. Use for whatever you want. + /// \param[in] isAReference Means that this is just a reference to a file elsewhere - does not actually have any data + /// \param[in] takeDataPointer If true, do not allocate dataLength. Just take the pointer passed to the \a data parameter + void AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference=false, bool takeDataPointer=false); + + /// \brief Add a file, reading it from disk. + /// \param[in] filepath Complete path to the file, including the filename itself + /// \param[in] filename filename to store internally, anything you want, but usually either the complete path or a subset of the complete path. + /// \param[in] context User defined byte to store with each file. Use for whatever you want. + void AddFile(const char *filepath, const char *filename, FileListNodeContext context); + + /// \brief Delete all files stored in the file list. + /// \param[in] applicationDirectory Prefixed to the path to each filename. Use \ as the path delineator. + void DeleteFiles(const char *applicationDirectory); + + /// \brief Adds a callback to get progress reports about what the file list instances do. + /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. + void AddCallback(FileListProgress *cb); + + /// \brief Removes a callback + /// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback() + void RemoveCallback(FileListProgress *cb); + + /// \brief Removes all callbacks + void ClearCallbacks(void); + + /// Returns all callbacks added with AddCallback() + /// \param[out] callbacks The list is set to the list of callbacks + void GetCallbacks(DataStructures::List &callbacks); + + // Here so you can read it, but don't modify it + DataStructures::List fileList; + + static bool FixEndingSlash(char *str); +protected: + DataStructures::List fileListProgressCallbacks; +}; + +} // namespace RakNet + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + +#endif // _RAKNET_SUPPORT_FileOperations diff --git a/src/raknet/FileListNodeContext.h b/src/raknet/FileListNodeContext.h index c0fd2e7..6ec227a 100755 --- a/src/raknet/FileListNodeContext.h +++ b/src/raknet/FileListNodeContext.h @@ -1,39 +1,39 @@ -/// \file FileListNodeContext.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __FILE_LIST_NODE_CONTEXT_H -#define __FILE_LIST_NODE_CONTEXT_H - -#include "BitStream.h" - -struct FileListNodeContext -{ - FileListNodeContext() {dataPtr=0; dataLength=0;} - FileListNodeContext(unsigned char o, unsigned int f) : op(o), fileId(f) {dataPtr=0; dataLength=0;} - ~FileListNodeContext() {} - - unsigned char op; - unsigned int fileId; - void *dataPtr; - unsigned int dataLength; -}; - -inline RakNet::BitStream& operator<<(RakNet::BitStream& out, FileListNodeContext& in) -{ - out.Write(in.op); - out.Write(in.fileId); - return out; -} -inline RakNet::BitStream& operator>>(RakNet::BitStream& in, FileListNodeContext& out) -{ - in.Read(out.op); - bool success = in.Read(out.fileId); - (void) success; - assert(success); - return in; -} - -#endif +/// \file FileListNodeContext.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __FILE_LIST_NODE_CONTEXT_H +#define __FILE_LIST_NODE_CONTEXT_H + +#include "BitStream.h" + +struct FileListNodeContext +{ + FileListNodeContext() {dataPtr=0; dataLength=0;} + FileListNodeContext(unsigned char o, unsigned int f) : op(o), fileId(f) {dataPtr=0; dataLength=0;} + ~FileListNodeContext() {} + + unsigned char op; + unsigned int fileId; + void *dataPtr; + unsigned int dataLength; +}; + +inline RakNet::BitStream& operator<<(RakNet::BitStream& out, FileListNodeContext& in) +{ + out.Write(in.op); + out.Write(in.fileId); + return out; +} +inline RakNet::BitStream& operator>>(RakNet::BitStream& in, FileListNodeContext& out) +{ + in.Read(out.op); + bool success = in.Read(out.fileId); + (void) success; + assert(success); + return in; +} + +#endif diff --git a/src/raknet/FileListTransfer.cpp b/src/raknet/FileListTransfer.cpp index 887fb7e..11fbe5f 100755 --- a/src/raknet/FileListTransfer.cpp +++ b/src/raknet/FileListTransfer.cpp @@ -1,1086 +1,1086 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FileListTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 - -#include "FileListTransfer.h" -#include "DS_HuffmanEncodingTree.h" -#include "FileListTransferCBInterface.h" -#include "StringCompressor.h" -#include "FileList.h" -#include "DS_Queue.h" -#include "MessageIdentifiers.h" -#include "RakNetTypes.h" -#include "RakPeerInterface.h" -#include "RakNetStatistics.h" -#include "IncrementalReadInterface.h" -#include "RakAssert.h" -#include "RakAlloca.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -namespace RakNet -{ - -struct FLR_MemoryBlock -{ - char *flrMemoryBlock; -}; - -struct FileListReceiver -{ - FileListReceiver(); - ~FileListReceiver(); - FileListTransferCBInterface *downloadHandler; - SystemAddress allowedSender; - unsigned short setID; - unsigned setCount; - unsigned setTotalCompressedTransmissionLength; - unsigned setTotalFinalLength; - unsigned setTotalDownloadedLength; - bool gotSetHeader; - bool deleteDownloadHandler; - bool isCompressed; - int filesReceived; - DataStructures::Map pushedFiles; - - // Notifications - unsigned int partLength; - -}; - -} // namespace RakNet - -using namespace RakNet; - -FileListReceiver::FileListReceiver() {filesReceived=0; setTotalDownloadedLength=0; partLength=1; DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON();} -FileListReceiver::~FileListReceiver() { - unsigned int i=0; - for (i=0; i < pushedFiles.Size(); i++) - rakFree_Ex(pushedFiles[i].flrMemoryBlock, _FILE_AND_LINE_ ); -} - -STATIC_FACTORY_DEFINITIONS(FileListTransfer,FileListTransfer) - -void FileListTransfer::FileToPushRecipient::DeleteThis(void) -{ - for (unsigned int j=0; j < filesToPush.Size(); j++) - RakNet::OP_DELETE(filesToPush[j],_FILE_AND_LINE_); - RakNet::OP_DELETE(this,_FILE_AND_LINE_); -} -void FileListTransfer::FileToPushRecipient::AddRef(void) -{ - refCountMutex.Lock(); - ++refCount; - refCountMutex.Unlock(); -} -void FileListTransfer::FileToPushRecipient::Deref(void) -{ - refCountMutex.Lock(); - --refCount; - if (refCount==0) - { - refCountMutex.Unlock(); - DeleteThis(); - return; - } - refCountMutex.Unlock(); -} -FileListTransfer::FileListTransfer() -{ - setId=0; - DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON(); -} -FileListTransfer::~FileListTransfer() -{ - threadPool.StopThreads(); - Clear(); -} -void FileListTransfer::StartIncrementalReadThreads(int numThreads, int threadPriority) -{ - (void) threadPriority; - - threadPool.StartThreads(numThreads, 0); -} -unsigned short FileListTransfer::SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender) -{ - if (rakPeerInterface && rakPeerInterface->GetConnectionState(allowedSender)!=IS_CONNECTED) - return (unsigned short)-1; - FileListReceiver *receiver; - - if (fileListReceivers.Has(setId)) - { - receiver=fileListReceivers.Get(setId); - receiver->downloadHandler->OnDereference(); - if (receiver->deleteDownloadHandler) - RakNet::OP_DELETE(receiver->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(receiver, _FILE_AND_LINE_); - fileListReceivers.Delete(setId); - } - - unsigned short oldId; - receiver = RakNet::OP_NEW( _FILE_AND_LINE_ ); - RakAssert(handler); - receiver->downloadHandler=handler; - receiver->allowedSender=allowedSender; - receiver->gotSetHeader=false; - receiver->deleteDownloadHandler=deleteHandler; - receiver->setID=setId; - fileListReceivers.Set(setId, receiver); - oldId=setId; - if (++setId==(unsigned short)-1) - setId=0; - return oldId; -} - -void FileListTransfer::Send(FileList *fileList, RakNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) -{ - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileList->AddCallback(fileListProgressCallbacks[flpcIndex]); - - unsigned int i, totalLength; - RakNet::BitStream outBitstream; - bool sendReference; - const char *dataBlocks[2]; - int lengths[2]; - totalLength=0; - for (i=0; i < fileList->fileList.Size(); i++) - { - const FileListNode &fileListNode = fileList->fileList[i]; - totalLength+=fileListNode.fileLengthBytes; - } - - // Write the chunk header, which contains the frequency table, the total number of files, and the total number of bytes - bool anythingToWrite; - outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_HEADER); - outBitstream.Write(setID); - anythingToWrite=fileList->fileList.Size()>0; - outBitstream.Write(anythingToWrite); - if (anythingToWrite) - { - outBitstream.WriteCompressed(fileList->fileList.Size()); - outBitstream.WriteCompressed(totalLength); - - if (rakPeer) - rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - else - SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - - DataStructures::Queue filesToPush; - - for (i=0; i < fileList->fileList.Size(); i++) - { - sendReference = fileList->fileList[i].isAReference && _incrementalReadInterface!=0; - if (sendReference) - { - FileToPush *fileToPush = RakNet::OP_NEW(_FILE_AND_LINE_); - fileToPush->fileListNode.context=fileList->fileList[i].context; - fileToPush->setIndex=i; - fileToPush->fileListNode.filename=fileList->fileList[i].filename; - fileToPush->fileListNode.fullPathToFile=fileList->fileList[i].fullPathToFile; - fileToPush->fileListNode.fileLengthBytes=fileList->fileList[i].fileLengthBytes; - fileToPush->fileListNode.dataLengthBytes=fileList->fileList[i].dataLengthBytes; - // fileToPush->systemAddress=recipient; - fileToPush->setID=setID; - fileToPush->packetPriority=priority; - fileToPush->orderingChannel=orderingChannel; - fileToPush->currentOffset=0; - fileToPush->incrementalReadInterface=_incrementalReadInterface; - fileToPush->chunkSize=_chunkSize; - filesToPush.Push(fileToPush,_FILE_AND_LINE_); - } - else - { - outBitstream.Reset(); - outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); - outBitstream << fileList->fileList[i].context; - // outBitstream.Write(fileList->fileList[i].context); - outBitstream.Write(setID); - StringCompressor::Instance()->EncodeString(fileList->fileList[i].filename, 512, &outBitstream); - - outBitstream.WriteCompressed(i); - outBitstream.WriteCompressed(fileList->fileList[i].dataLengthBytes); // Original length in bytes - - outBitstream.AlignWriteToByteBoundary(); - - dataBlocks[0]=(char*) outBitstream.GetData(); - lengths[0]=outBitstream.GetNumberOfBytesUsed(); - dataBlocks[1]=fileList->fileList[i].data; - lengths[1]=fileList->fileList[i].dataLengthBytes; - SendListUnified(dataBlocks,lengths,2,priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - } - } - - if (filesToPush.IsEmpty()==false) - { - FileToPushRecipient *ftpr=0; - - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - if (fileToPushRecipientList[i]->systemAddress==recipient) - { - ftpr=fileToPushRecipientList[i]; - ftpr->AddRef(); - break; - } - } - fileToPushRecipientListMutex.Unlock(); - - if (ftpr==0) - { - ftpr = RakNet::OP_NEW(_FILE_AND_LINE_); - ftpr->systemAddress=recipient; - ftpr->refCount=2; // Allocated and in the list - fileToPushRecipientList.Push(ftpr, _FILE_AND_LINE_); - } - while (filesToPush.IsEmpty()==false) - { - ftpr->filesToPush.Push(filesToPush.Pop(), _FILE_AND_LINE_); - } - // ftpr out of scope - ftpr->Deref(); - SendIRIToAddress(recipient); - return; - } - else - { - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID); - } - } - else - { - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID); - - if (rakPeer) - rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - else - SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); - } -} - -bool FileListTransfer::DecodeSetHeader(Packet *packet) -{ - bool anythingToWrite=false; - unsigned short setID; - RakNet::BitStream inBitStream(packet->data, packet->length, false); - inBitStream.IgnoreBits(8); - inBitStream.Read(setID); - FileListReceiver *fileListReceiver; - if (fileListReceivers.Has(setID)==false) - { - // If this assert hits you didn't call SetupReceive -#ifdef _DEBUG - RakAssert(0); -#endif - return false; - } - fileListReceiver=fileListReceivers.Get(setID); - if (fileListReceiver->allowedSender!=packet->systemAddress) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return false; - } - -#ifdef _DEBUG - RakAssert(fileListReceiver->gotSetHeader==false); -#endif - - inBitStream.Read(anythingToWrite); - - if (anythingToWrite) - { - inBitStream.ReadCompressed(fileListReceiver->setCount); - if (inBitStream.ReadCompressed(fileListReceiver->setTotalFinalLength)) - { - fileListReceiver->setTotalCompressedTransmissionLength=fileListReceiver->setTotalFinalLength; - fileListReceiver->gotSetHeader=true; - return true; - } - - } - else - { - FileListTransferCBInterface::DownloadCompleteStruct dcs; - dcs.setID=fileListReceiver->setID; - dcs.numberOfFilesInThisSet=fileListReceiver->setCount; - dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - dcs.senderSystemAddress=packet->systemAddress; - dcs.senderGuid=packet->guid; - - if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) - { - fileListReceiver->downloadHandler->OnDereference(); - fileListReceivers.Delete(setID); - if (fileListReceiver->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); - } - - return true; - } - - return false; -} - -bool FileListTransfer::DecodeFile(Packet *packet, bool isTheFileAndIsNotDownloadProgress) -{ - FileListTransferCBInterface::OnFileStruct onFileStruct; - RakNet::BitStream inBitStream(packet->data, packet->length, false); - inBitStream.IgnoreBits(8); - - onFileStruct.senderSystemAddress=packet->systemAddress; - onFileStruct.senderGuid=packet->guid; - - unsigned int partCount=0; - unsigned int partTotal=0; - unsigned int partLength=0; - onFileStruct.fileData=0; - if (isTheFileAndIsNotDownloadProgress==false) - { - // Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp - inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true ); - inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true ); - inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true ); - inBitStream.IgnoreBits(8); - // The header is appended to every chunk, which we continue to read after this statement flrMemoryBlock - } - inBitStream >> onFileStruct.context; - // inBitStream.Read(onFileStruct.context); - inBitStream.Read(onFileStruct.setID); - FileListReceiver *fileListReceiver; - if (fileListReceivers.Has(onFileStruct.setID)==false) - { - return false; - } - fileListReceiver=fileListReceivers.Get(onFileStruct.setID); - if (fileListReceiver->allowedSender!=packet->systemAddress) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return false; - } - -#ifdef _DEBUG - RakAssert(fileListReceiver->gotSetHeader==true); -#endif - - if (StringCompressor::Instance()->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return false; - } - - inBitStream.ReadCompressed(onFileStruct.fileIndex); - inBitStream.ReadCompressed(onFileStruct.byteLengthOfThisFile); - onFileStruct.bytesDownloadedForThisFile=onFileStruct.byteLengthOfThisFile; - - if (isTheFileAndIsNotDownloadProgress) - { - // Support SendLists - inBitStream.AlignReadToByteBoundary(); - - onFileStruct.fileData = (char*) rakMalloc_Ex( (size_t) onFileStruct.byteLengthOfThisFile, _FILE_AND_LINE_ ); - - inBitStream.Read((char*)onFileStruct.fileData, onFileStruct.byteLengthOfThisFile); - - fileListReceiver->setTotalDownloadedLength+=onFileStruct.byteLengthOfThisFile; - } - - - onFileStruct.numberOfFilesInThisSet=fileListReceiver->setCount; -// onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength; - onFileStruct.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - - // User callback for this file. - if (isTheFileAndIsNotDownloadProgress) - { - onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength; - - FileListTransferCBInterface::FileProgressStruct fps; - fps.onFileStruct=&onFileStruct; - fps.partCount=1; - fps.partTotal=1; - fps.dataChunkLength=onFileStruct.byteLengthOfThisFile; - fps.firstDataChunk=onFileStruct.fileData; - fps.iriDataChunk=onFileStruct.fileData; - fps.allocateIrIDataChunkAutomatically=true; - fps.iriWriteOffset=0; - fps.senderSystemAddress=packet->systemAddress; - fps.senderGuid=packet->guid; - fileListReceiver->downloadHandler->OnFileProgress(&fps); - - // Got a complete file - // Either we are using IncrementalReadInterface and it was a small file or - // We are not using IncrementalReadInterface - if (fileListReceiver->downloadHandler->OnFile(&onFileStruct)) - rakFree_Ex(onFileStruct.fileData, _FILE_AND_LINE_ ); - - fileListReceiver->filesReceived++; - - // If this set is done, free the memory for it. - if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived) - { - FileListTransferCBInterface::DownloadCompleteStruct dcs; - dcs.setID=fileListReceiver->setID; - dcs.numberOfFilesInThisSet=fileListReceiver->setCount; - dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - dcs.senderSystemAddress=packet->systemAddress; - dcs.senderGuid=packet->guid; - - if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) - { - fileListReceiver->downloadHandler->OnDereference(); - if (fileListReceiver->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); - fileListReceivers.Delete(onFileStruct.setID); - RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); - } - } - - } - else - { - inBitStream.AlignReadToByteBoundary(); - - char *firstDataChunk; - unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits(); - unsigned int unreadBytes = BITS_TO_BYTES(unreadBits); - firstDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); - - onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength+unreadBytes; - onFileStruct.bytesDownloadedForThisFile=onFileStruct.byteLengthOfThisFile; - - FileListTransferCBInterface::FileProgressStruct fps; - fps.onFileStruct=&onFileStruct; - fps.partCount=partCount; - fps.partTotal=partTotal; - fps.dataChunkLength=unreadBytes; - fps.firstDataChunk=firstDataChunk; - fps.iriDataChunk=0; - fps.allocateIrIDataChunkAutomatically=true; - fps.iriWriteOffset=0; - fps.senderSystemAddress=packet->systemAddress; - fps.senderGuid=packet->guid; - - // Remote system is sending a complete file, but the file is large enough that we get ID_PROGRESS_NOTIFICATION from the transport layer - fileListReceiver->downloadHandler->OnFileProgress(&fps); - - } - - return true; -} -PluginReceiveResult FileListTransfer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_FILE_LIST_TRANSFER_HEADER: - DecodeSetHeader(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FILE_LIST_TRANSFER_FILE: - DecodeFile(packet, true); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FILE_LIST_REFERENCE_PUSH: - OnReferencePush(packet, true); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FILE_LIST_REFERENCE_PUSH_ACK: - OnReferencePushAck(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_DOWNLOAD_PROGRESS: - if (packet->length>sizeof(MessageID)+sizeof(unsigned int)*3) - { - if (packet->data[sizeof(MessageID)+sizeof(unsigned int)*3]==ID_FILE_LIST_TRANSFER_FILE) - { - DecodeFile(packet, false); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - if (packet->data[sizeof(MessageID)+sizeof(unsigned int)*3]==ID_FILE_LIST_REFERENCE_PUSH) - { - OnReferencePush(packet, false); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - break; - } - - return RR_CONTINUE_PROCESSING; -} -void FileListTransfer::OnRakPeerShutdown(void) -{ - threadPool.StopThreads(); - threadPool.ClearInput(); - Clear(); -} -void FileListTransfer::Clear(void) -{ - unsigned i; - for (i=0; i < fileListReceivers.Size(); i++) - { - fileListReceivers[i]->downloadHandler->OnDereference(); - if (fileListReceivers[i]->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); - } - fileListReceivers.Clear(); - - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - FileToPushRecipient *ftpr = fileToPushRecipientList[i]; - // Taken out of the list - ftpr->Deref(); - } - fileToPushRecipientList.Clear(false,_FILE_AND_LINE_); - fileToPushRecipientListMutex.Unlock(); - - //filesToPush.Clear(false, _FILE_AND_LINE_); -} -void FileListTransfer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - RemoveReceiver(systemAddress); -} -void FileListTransfer::CancelReceive(unsigned short setId) -{ - if (fileListReceivers.Has(setId)==false) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return; - } - FileListReceiver *fileListReceiver=fileListReceivers.Get(setId); - fileListReceiver->downloadHandler->OnDereference(); - if (fileListReceiver->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); - fileListReceivers.Delete(setId); -} -void FileListTransfer::RemoveReceiver(SystemAddress systemAddress) -{ - unsigned i; - i=0; - threadPool.LockInput(); - while (i < threadPool.InputSize()) - { - if (threadPool.GetInputAtIndex(i).systemAddress==systemAddress) - { - threadPool.RemoveInputAtIndex(i); - } - else - i++; - } - threadPool.UnlockInput(); - - i=0; - while (i < fileListReceivers.Size()) - { - if (fileListReceivers[i]->allowedSender==systemAddress) - { - fileListReceivers[i]->downloadHandler->OnDereference(); - if (fileListReceivers[i]->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); - fileListReceivers.RemoveAtIndex(i); - } - else - i++; - } - - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - if (fileToPushRecipientList[i]->systemAddress==systemAddress) - { - FileToPushRecipient *ftpr = fileToPushRecipientList[i]; - - // Tell the user that this recipient was lost - for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) - fileListProgressCallbacks[flpcIndex]->OnSendAborted(ftpr->systemAddress); - - fileToPushRecipientList.RemoveAtIndex(i); - // Taken out of the list - ftpr->Deref(); - break; - } - } - fileToPushRecipientListMutex.Unlock(); -} -bool FileListTransfer::IsHandlerActive(unsigned short setId) -{ - return fileListReceivers.Has(setId); -} -void FileListTransfer::AddCallback(FileListProgress *cb) -{ - if (cb==0) - return; - - if (fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int) -1) - fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_); -} -void FileListTransfer::RemoveCallback(FileListProgress *cb) -{ - unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb); - if (idx!=(unsigned int) -1) - fileListProgressCallbacks.RemoveAtIndex(idx); -} -void FileListTransfer::ClearCallbacks(void) -{ - fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_); -} -void FileListTransfer::GetCallbacks(DataStructures::List &callbacks) -{ - callbacks = fileListProgressCallbacks; -} - -void FileListTransfer::Update(void) -{ - unsigned i; - i=0; - while (i < fileListReceivers.Size()) - { - if (fileListReceivers[i]->downloadHandler->Update()==false) - { - fileListReceivers[i]->downloadHandler->OnDereference(); - if (fileListReceivers[i]->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); - fileListReceivers.RemoveAtIndex(i); - } - else - i++; - } -} -void FileListTransfer::OnReferencePush(Packet *packet, bool isTheFileAndIsNotDownloadProgress) -{ - RakNet::BitStream refPushAck; - if (isTheFileAndIsNotDownloadProgress) - { - // This is not a progress notification, it is actually the entire packet - refPushAck.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH_ACK); - SendUnified(&refPushAck,HIGH_PRIORITY, RELIABLE, 0, packet->systemAddress, false); - } - else - { - // 12/23/09 Why do I care about ID_DOWNLOAD_PROGRESS for reference pushes? - return; - } - - FileListTransferCBInterface::OnFileStruct onFileStruct; - RakNet::BitStream inBitStream(packet->data, packet->length, false); - inBitStream.IgnoreBits(8); - - unsigned int partCount=0; - unsigned int partTotal=1; - unsigned int partLength=0; - onFileStruct.fileData=0; - if (isTheFileAndIsNotDownloadProgress==false) - { - // UNREACHABLE CODE - // Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp - inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true ); - inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true ); - inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true ); - inBitStream.IgnoreBits(8); - // The header is appended to every chunk, which we continue to read after this statement flrMemoryBlock - } - - inBitStream >> onFileStruct.context; - // inBitStream.Read(onFileStruct.context); - inBitStream.Read(onFileStruct.setID); - FileListReceiver *fileListReceiver; - if (fileListReceivers.Has(onFileStruct.setID)==false) - { - return; - } - fileListReceiver=fileListReceivers.Get(onFileStruct.setID); - if (fileListReceiver->allowedSender!=packet->systemAddress) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return; - } - -#ifdef _DEBUG - RakAssert(fileListReceiver->gotSetHeader==true); -#endif - - if (StringCompressor::Instance()->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false) - { -#ifdef _DEBUG - RakAssert(0); -#endif - return; - } - - inBitStream.ReadCompressed(onFileStruct.fileIndex); - inBitStream.ReadCompressed(onFileStruct.byteLengthOfThisFile); - unsigned int offset; - unsigned int chunkLength; - inBitStream.ReadCompressed(offset); - inBitStream.ReadCompressed(chunkLength); - - bool lastChunk=false; - inBitStream.Read(lastChunk); - bool finished = lastChunk && isTheFileAndIsNotDownloadProgress; - - if (isTheFileAndIsNotDownloadProgress==false) - fileListReceiver->partLength=partLength; - - FLR_MemoryBlock mb; - if (fileListReceiver->pushedFiles.Has(onFileStruct.fileIndex)==false) - { - mb.flrMemoryBlock=(char*) rakMalloc_Ex(onFileStruct.byteLengthOfThisFile, _FILE_AND_LINE_); - fileListReceiver->pushedFiles.SetNew(onFileStruct.fileIndex, mb); - } - else - { - mb=fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex); - } - - unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits(); - unsigned int unreadBytes = BITS_TO_BYTES(unreadBits); - unsigned int amountToRead; - if (isTheFileAndIsNotDownloadProgress) - amountToRead=chunkLength; - else - amountToRead=unreadBytes; - - inBitStream.AlignReadToByteBoundary(); - - FileListTransferCBInterface::FileProgressStruct fps; - - if (isTheFileAndIsNotDownloadProgress) - { - if (mb.flrMemoryBlock) - { - // Either the very first block, or a subsequent block and allocateIrIDataChunkAutomatically was true for the first block - memcpy(mb.flrMemoryBlock+offset, inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()), amountToRead); - fps.iriDataChunk=mb.flrMemoryBlock+offset; - } - else - { - // In here mb.flrMemoryBlock is null - // This means the first block explicitly deallocated the memory, and no blocks will be permanently held by RakNet - fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); - } - - onFileStruct.bytesDownloadedForThisFile=offset+amountToRead; - } - else - { - fileListReceiver->setTotalDownloadedLength+=partLength; - onFileStruct.bytesDownloadedForThisFile=partCount*partLength; - fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); - } - onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength; - - onFileStruct.numberOfFilesInThisSet=fileListReceiver->setCount; -// onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength; - onFileStruct.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - // Note: mb.flrMemoryBlock may be null here - onFileStruct.fileData=mb.flrMemoryBlock; - onFileStruct.senderSystemAddress=packet->systemAddress; - onFileStruct.senderGuid=packet->guid; - - unsigned int totalNotifications; - unsigned int currentNotificationIndex; - if (chunkLength==0 || chunkLength==onFileStruct.byteLengthOfThisFile) - totalNotifications=1; - else - totalNotifications = onFileStruct.byteLengthOfThisFile / chunkLength + 1; - - if (chunkLength==0) - currentNotificationIndex = 0; - else - currentNotificationIndex = offset / chunkLength; - - fps.onFileStruct=&onFileStruct; - fps.partCount=currentNotificationIndex; - fps.partTotal=totalNotifications; - fps.dataChunkLength=amountToRead; - fps.firstDataChunk=mb.flrMemoryBlock; - fps.allocateIrIDataChunkAutomatically=true; - fps.onFileStruct->fileData=mb.flrMemoryBlock; - fps.iriWriteOffset=offset; - fps.senderSystemAddress=packet->systemAddress; - fps.senderGuid=packet->guid; - - if (finished) - { - char *oldFileData=fps.onFileStruct->fileData; - if (fps.partCount==0) - fps.firstDataChunk=fps.iriDataChunk; - if (fps.partTotal==1) - fps.onFileStruct->fileData=fps.iriDataChunk; - fileListReceiver->downloadHandler->OnFileProgress(&fps); - - // Incremental read interface sent us a file chunk - // This is the last file chunk we were waiting for to consider the file done - if (fileListReceiver->downloadHandler->OnFile(&onFileStruct)) - rakFree_Ex(oldFileData, _FILE_AND_LINE_ ); - fileListReceiver->pushedFiles.Delete(onFileStruct.fileIndex); - - fileListReceiver->filesReceived++; - - // If this set is done, free the memory for it. - if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived) - { - FileListTransferCBInterface::DownloadCompleteStruct dcs; - dcs.setID=fileListReceiver->setID; - dcs.numberOfFilesInThisSet=fileListReceiver->setCount; - dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; - dcs.senderSystemAddress=packet->systemAddress; - dcs.senderGuid=packet->guid; - - if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) - { - fileListReceiver->downloadHandler->OnDereference(); - fileListReceivers.Delete(onFileStruct.setID); - if (fileListReceiver->deleteDownloadHandler) - RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); - RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); - } - } - } - else - { - if (isTheFileAndIsNotDownloadProgress) - { - // 12/23/09 Don't use OnReferencePush anymore, just use OnFileProgress - fileListReceiver->downloadHandler->OnFileProgress(&fps); - - if (fps.allocateIrIDataChunkAutomatically==false) - { - rakFree_Ex(fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock, _FILE_AND_LINE_ ); - fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock=0; - } - } - else - { - // This is a download progress notification for a file chunk using incremental read interface - // We don't have all the data for this chunk yet - - // UNREACHABLE CODE - totalNotifications = onFileStruct.byteLengthOfThisFile / fileListReceiver->partLength + 1; - if (isTheFileAndIsNotDownloadProgress==false) - currentNotificationIndex = (offset+partCount*fileListReceiver->partLength) / fileListReceiver->partLength ; - else - currentNotificationIndex = (offset+chunkLength) / fileListReceiver->partLength ; - unreadBytes = onFileStruct.byteLengthOfThisFile - ((currentNotificationIndex+1) * fileListReceiver->partLength); - - if (rakPeerInterface) - { - // Thus chunk is incomplete - fps.iriDataChunk=0; - - fileListReceiver->downloadHandler->OnFileProgress(&fps); - } - } - } - - return; -} -namespace RakNet -{ -int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData) -{ - (void) perThreadData; - - FileListTransfer *fileListTransfer = threadData.fileListTransfer; - SystemAddress systemAddress = threadData.systemAddress; - *returnOutput=false; - - // Was previously using GetStatistics to get outgoing buffer size, but TCP with UnifiedSend doesn't have this - unsigned int bytesRead; - const char *dataBlocks[2]; - int lengths[2]; - unsigned int smallFileTotalSize=0; - RakNet::BitStream outBitstream; - unsigned int ftpIndex; - - fileListTransfer->fileToPushRecipientListMutex.Lock(); - for (ftpIndex=0; ftpIndex < fileListTransfer->fileToPushRecipientList.Size(); ftpIndex++) - { - FileListTransfer::FileToPushRecipient *ftpr = fileListTransfer->fileToPushRecipientList[ftpIndex]; - // Referenced by both ftpr and list - ftpr->AddRef(); - - fileListTransfer->fileToPushRecipientListMutex.Unlock(); - - if (ftpr->systemAddress==systemAddress) - { - FileListTransfer::FileToPush *ftp = ftpr->filesToPush.Peek(); - - // Read and send chunk. If done, delete at this index - void *buff = rakMalloc_Ex(ftp->chunkSize, _FILE_AND_LINE_); - if (buff==0) - { - ftpr->Deref(); - notifyOutOfMemory(_FILE_AND_LINE_); - return 0; - } - - // Read the next file chunk - bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context); - - bool done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead; - while (done && ftp->currentOffset==0 && ftpr->filesToPush.Size()>=2 && smallFileTotalSizechunkSize) - { - // Send all small files at once, rather than wait for ID_FILE_LIST_REFERENCE_PUSH. But at least one ID_FILE_LIST_REFERENCE_PUSH must be sent - outBitstream.Reset(); - outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); - // outBitstream.Write(ftp->fileListNode.context); - outBitstream << ftp->fileListNode.context; - outBitstream.Write(ftp->setID); - StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream); - outBitstream.WriteCompressed(ftp->setIndex); - outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes - outBitstream.AlignWriteToByteBoundary(); - dataBlocks[0]=(char*) outBitstream.GetData(); - lengths[0]=outBitstream.GetNumberOfBytesUsed(); - dataBlocks[1]=(const char*) buff; - lengths[1]=bytesRead; - - fileListTransfer->SendListUnified(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, systemAddress, false); - - // LWS : fixed freed pointer reference -// unsigned int chunkSize = ftp->chunkSize; - RakNet::OP_DELETE(ftp,_FILE_AND_LINE_); - ftpr->filesToPush.Pop(); - smallFileTotalSize+=bytesRead; - //done = bytesRead!=ftp->chunkSize; - ftp = ftpr->filesToPush.Peek(); - - bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context); - done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead; - } - - - outBitstream.Reset(); - outBitstream.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH); - // outBitstream.Write(ftp->fileListNode.context); - outBitstream << ftp->fileListNode.context; - outBitstream.Write(ftp->setID); - StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream); - outBitstream.WriteCompressed(ftp->setIndex); - outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes - outBitstream.WriteCompressed(ftp->currentOffset); - ftp->currentOffset+=bytesRead; - outBitstream.WriteCompressed(bytesRead); - outBitstream.Write(done); - - for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++) - fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePush(ftp->fileListNode.filename, ftp->fileListNode.fileLengthBytes, ftp->currentOffset-bytesRead, bytesRead, done, systemAddress, ftp->setID); - - dataBlocks[0]=(char*) outBitstream.GetData(); - lengths[0]=outBitstream.GetNumberOfBytesUsed(); - dataBlocks[1]=(char*) buff; - lengths[1]=bytesRead; - //rakPeerInterface->SendList(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, ftp->systemAddress, false); - fileListTransfer->SendListUnified(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, systemAddress, false); - - // Mutex state: FileToPushRecipient (ftpr) has AddRef. fileToPushRecipientListMutex not locked. - if (done) - { - // Done - unsigned short setId = ftp->setID; - RakNet::OP_DELETE(ftp,_FILE_AND_LINE_); - ftpr->filesToPush.Pop(); - - if (ftpr->filesToPush.Size()==0) - { - for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++) - fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(systemAddress, setId); - - // Remove ftpr from fileToPushRecipientList - fileListTransfer->RemoveFromList(ftpr); - } - } - - // ftpr out of scope - ftpr->Deref(); - - rakFree_Ex(buff, _FILE_AND_LINE_ ); - return 0; - } - else - { - ftpr->Deref(); - fileListTransfer->fileToPushRecipientListMutex.Lock(); - } - } - - fileListTransfer->fileToPushRecipientListMutex.Unlock(); - - return 0; -} -} -void FileListTransfer::SendIRIToAddress(SystemAddress systemAddress) -{ - ThreadData threadData; - threadData.fileListTransfer=this; - threadData.systemAddress=systemAddress; - - if (threadPool.WasStarted()) - { - threadPool.AddInput(SendIRIToAddressCB, threadData); - } - else - { - bool doesNothing; - SendIRIToAddressCB(threadData, &doesNothing, 0); - } -} -void FileListTransfer::OnReferencePushAck(Packet *packet) -{ - SendIRIToAddress(packet->systemAddress); -} -void FileListTransfer::RemoveFromList(FileToPushRecipient *ftpr) -{ - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - if (fileToPushRecipientList[i]==ftpr) - { - fileToPushRecipientList.RemoveAtIndex(i); - // List no longer references - ftpr->Deref(); - fileToPushRecipientListMutex.Unlock(); - return; - } - } - fileToPushRecipientListMutex.Unlock(); -} -unsigned int FileListTransfer::GetPendingFilesToAddress(SystemAddress recipient) -{ - fileToPushRecipientListMutex.Lock(); - for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) - { - if (fileToPushRecipientList[i]->systemAddress==recipient) - { - unsigned int size = fileToPushRecipientList[i]->filesToPush.Size(); - fileToPushRecipientListMutex.Unlock(); - return size; - } - } - fileToPushRecipientListMutex.Unlock(); - - return 0; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FileListTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 + +#include "FileListTransfer.h" +#include "DS_HuffmanEncodingTree.h" +#include "FileListTransferCBInterface.h" +#include "StringCompressor.h" +#include "FileList.h" +#include "DS_Queue.h" +#include "MessageIdentifiers.h" +#include "RakNetTypes.h" +#include "RakPeerInterface.h" +#include "RakNetStatistics.h" +#include "IncrementalReadInterface.h" +#include "RakAssert.h" +#include "RakAlloca.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +namespace RakNet +{ + +struct FLR_MemoryBlock +{ + char *flrMemoryBlock; +}; + +struct FileListReceiver +{ + FileListReceiver(); + ~FileListReceiver(); + FileListTransferCBInterface *downloadHandler; + SystemAddress allowedSender; + unsigned short setID; + unsigned setCount; + unsigned setTotalCompressedTransmissionLength; + unsigned setTotalFinalLength; + unsigned setTotalDownloadedLength; + bool gotSetHeader; + bool deleteDownloadHandler; + bool isCompressed; + int filesReceived; + DataStructures::Map pushedFiles; + + // Notifications + unsigned int partLength; + +}; + +} // namespace RakNet + +using namespace RakNet; + +FileListReceiver::FileListReceiver() {filesReceived=0; setTotalDownloadedLength=0; partLength=1; DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON();} +FileListReceiver::~FileListReceiver() { + unsigned int i=0; + for (i=0; i < pushedFiles.Size(); i++) + rakFree_Ex(pushedFiles[i].flrMemoryBlock, _FILE_AND_LINE_ ); +} + +STATIC_FACTORY_DEFINITIONS(FileListTransfer,FileListTransfer) + +void FileListTransfer::FileToPushRecipient::DeleteThis(void) +{ + for (unsigned int j=0; j < filesToPush.Size(); j++) + RakNet::OP_DELETE(filesToPush[j],_FILE_AND_LINE_); + RakNet::OP_DELETE(this,_FILE_AND_LINE_); +} +void FileListTransfer::FileToPushRecipient::AddRef(void) +{ + refCountMutex.Lock(); + ++refCount; + refCountMutex.Unlock(); +} +void FileListTransfer::FileToPushRecipient::Deref(void) +{ + refCountMutex.Lock(); + --refCount; + if (refCount==0) + { + refCountMutex.Unlock(); + DeleteThis(); + return; + } + refCountMutex.Unlock(); +} +FileListTransfer::FileListTransfer() +{ + setId=0; + DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON(); +} +FileListTransfer::~FileListTransfer() +{ + threadPool.StopThreads(); + Clear(); +} +void FileListTransfer::StartIncrementalReadThreads(int numThreads, int threadPriority) +{ + (void) threadPriority; + + threadPool.StartThreads(numThreads, 0); +} +unsigned short FileListTransfer::SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender) +{ + if (rakPeerInterface && rakPeerInterface->GetConnectionState(allowedSender)!=IS_CONNECTED) + return (unsigned short)-1; + FileListReceiver *receiver; + + if (fileListReceivers.Has(setId)) + { + receiver=fileListReceivers.Get(setId); + receiver->downloadHandler->OnDereference(); + if (receiver->deleteDownloadHandler) + RakNet::OP_DELETE(receiver->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(receiver, _FILE_AND_LINE_); + fileListReceivers.Delete(setId); + } + + unsigned short oldId; + receiver = RakNet::OP_NEW( _FILE_AND_LINE_ ); + RakAssert(handler); + receiver->downloadHandler=handler; + receiver->allowedSender=allowedSender; + receiver->gotSetHeader=false; + receiver->deleteDownloadHandler=deleteHandler; + receiver->setID=setId; + fileListReceivers.Set(setId, receiver); + oldId=setId; + if (++setId==(unsigned short)-1) + setId=0; + return oldId; +} + +void FileListTransfer::Send(FileList *fileList, RakNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) +{ + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileList->AddCallback(fileListProgressCallbacks[flpcIndex]); + + unsigned int i, totalLength; + RakNet::BitStream outBitstream; + bool sendReference; + const char *dataBlocks[2]; + int lengths[2]; + totalLength=0; + for (i=0; i < fileList->fileList.Size(); i++) + { + const FileListNode &fileListNode = fileList->fileList[i]; + totalLength+=fileListNode.fileLengthBytes; + } + + // Write the chunk header, which contains the frequency table, the total number of files, and the total number of bytes + bool anythingToWrite; + outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_HEADER); + outBitstream.Write(setID); + anythingToWrite=fileList->fileList.Size()>0; + outBitstream.Write(anythingToWrite); + if (anythingToWrite) + { + outBitstream.WriteCompressed(fileList->fileList.Size()); + outBitstream.WriteCompressed(totalLength); + + if (rakPeer) + rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + else + SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + + DataStructures::Queue filesToPush; + + for (i=0; i < fileList->fileList.Size(); i++) + { + sendReference = fileList->fileList[i].isAReference && _incrementalReadInterface!=0; + if (sendReference) + { + FileToPush *fileToPush = RakNet::OP_NEW(_FILE_AND_LINE_); + fileToPush->fileListNode.context=fileList->fileList[i].context; + fileToPush->setIndex=i; + fileToPush->fileListNode.filename=fileList->fileList[i].filename; + fileToPush->fileListNode.fullPathToFile=fileList->fileList[i].fullPathToFile; + fileToPush->fileListNode.fileLengthBytes=fileList->fileList[i].fileLengthBytes; + fileToPush->fileListNode.dataLengthBytes=fileList->fileList[i].dataLengthBytes; + // fileToPush->systemAddress=recipient; + fileToPush->setID=setID; + fileToPush->packetPriority=priority; + fileToPush->orderingChannel=orderingChannel; + fileToPush->currentOffset=0; + fileToPush->incrementalReadInterface=_incrementalReadInterface; + fileToPush->chunkSize=_chunkSize; + filesToPush.Push(fileToPush,_FILE_AND_LINE_); + } + else + { + outBitstream.Reset(); + outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); + outBitstream << fileList->fileList[i].context; + // outBitstream.Write(fileList->fileList[i].context); + outBitstream.Write(setID); + StringCompressor::Instance()->EncodeString(fileList->fileList[i].filename, 512, &outBitstream); + + outBitstream.WriteCompressed(i); + outBitstream.WriteCompressed(fileList->fileList[i].dataLengthBytes); // Original length in bytes + + outBitstream.AlignWriteToByteBoundary(); + + dataBlocks[0]=(char*) outBitstream.GetData(); + lengths[0]=outBitstream.GetNumberOfBytesUsed(); + dataBlocks[1]=fileList->fileList[i].data; + lengths[1]=fileList->fileList[i].dataLengthBytes; + SendListUnified(dataBlocks,lengths,2,priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + } + } + + if (filesToPush.IsEmpty()==false) + { + FileToPushRecipient *ftpr=0; + + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + if (fileToPushRecipientList[i]->systemAddress==recipient) + { + ftpr=fileToPushRecipientList[i]; + ftpr->AddRef(); + break; + } + } + fileToPushRecipientListMutex.Unlock(); + + if (ftpr==0) + { + ftpr = RakNet::OP_NEW(_FILE_AND_LINE_); + ftpr->systemAddress=recipient; + ftpr->refCount=2; // Allocated and in the list + fileToPushRecipientList.Push(ftpr, _FILE_AND_LINE_); + } + while (filesToPush.IsEmpty()==false) + { + ftpr->filesToPush.Push(filesToPush.Pop(), _FILE_AND_LINE_); + } + // ftpr out of scope + ftpr->Deref(); + SendIRIToAddress(recipient); + return; + } + else + { + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID); + } + } + else + { + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID); + + if (rakPeer) + rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + else + SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); + } +} + +bool FileListTransfer::DecodeSetHeader(Packet *packet) +{ + bool anythingToWrite=false; + unsigned short setID; + RakNet::BitStream inBitStream(packet->data, packet->length, false); + inBitStream.IgnoreBits(8); + inBitStream.Read(setID); + FileListReceiver *fileListReceiver; + if (fileListReceivers.Has(setID)==false) + { + // If this assert hits you didn't call SetupReceive +#ifdef _DEBUG + RakAssert(0); +#endif + return false; + } + fileListReceiver=fileListReceivers.Get(setID); + if (fileListReceiver->allowedSender!=packet->systemAddress) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return false; + } + +#ifdef _DEBUG + RakAssert(fileListReceiver->gotSetHeader==false); +#endif + + inBitStream.Read(anythingToWrite); + + if (anythingToWrite) + { + inBitStream.ReadCompressed(fileListReceiver->setCount); + if (inBitStream.ReadCompressed(fileListReceiver->setTotalFinalLength)) + { + fileListReceiver->setTotalCompressedTransmissionLength=fileListReceiver->setTotalFinalLength; + fileListReceiver->gotSetHeader=true; + return true; + } + + } + else + { + FileListTransferCBInterface::DownloadCompleteStruct dcs; + dcs.setID=fileListReceiver->setID; + dcs.numberOfFilesInThisSet=fileListReceiver->setCount; + dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + dcs.senderSystemAddress=packet->systemAddress; + dcs.senderGuid=packet->guid; + + if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) + { + fileListReceiver->downloadHandler->OnDereference(); + fileListReceivers.Delete(setID); + if (fileListReceiver->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); + } + + return true; + } + + return false; +} + +bool FileListTransfer::DecodeFile(Packet *packet, bool isTheFileAndIsNotDownloadProgress) +{ + FileListTransferCBInterface::OnFileStruct onFileStruct; + RakNet::BitStream inBitStream(packet->data, packet->length, false); + inBitStream.IgnoreBits(8); + + onFileStruct.senderSystemAddress=packet->systemAddress; + onFileStruct.senderGuid=packet->guid; + + unsigned int partCount=0; + unsigned int partTotal=0; + unsigned int partLength=0; + onFileStruct.fileData=0; + if (isTheFileAndIsNotDownloadProgress==false) + { + // Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp + inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true ); + inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true ); + inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true ); + inBitStream.IgnoreBits(8); + // The header is appended to every chunk, which we continue to read after this statement flrMemoryBlock + } + inBitStream >> onFileStruct.context; + // inBitStream.Read(onFileStruct.context); + inBitStream.Read(onFileStruct.setID); + FileListReceiver *fileListReceiver; + if (fileListReceivers.Has(onFileStruct.setID)==false) + { + return false; + } + fileListReceiver=fileListReceivers.Get(onFileStruct.setID); + if (fileListReceiver->allowedSender!=packet->systemAddress) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return false; + } + +#ifdef _DEBUG + RakAssert(fileListReceiver->gotSetHeader==true); +#endif + + if (StringCompressor::Instance()->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return false; + } + + inBitStream.ReadCompressed(onFileStruct.fileIndex); + inBitStream.ReadCompressed(onFileStruct.byteLengthOfThisFile); + onFileStruct.bytesDownloadedForThisFile=onFileStruct.byteLengthOfThisFile; + + if (isTheFileAndIsNotDownloadProgress) + { + // Support SendLists + inBitStream.AlignReadToByteBoundary(); + + onFileStruct.fileData = (char*) rakMalloc_Ex( (size_t) onFileStruct.byteLengthOfThisFile, _FILE_AND_LINE_ ); + + inBitStream.Read((char*)onFileStruct.fileData, onFileStruct.byteLengthOfThisFile); + + fileListReceiver->setTotalDownloadedLength+=onFileStruct.byteLengthOfThisFile; + } + + + onFileStruct.numberOfFilesInThisSet=fileListReceiver->setCount; +// onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength; + onFileStruct.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + + // User callback for this file. + if (isTheFileAndIsNotDownloadProgress) + { + onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength; + + FileListTransferCBInterface::FileProgressStruct fps; + fps.onFileStruct=&onFileStruct; + fps.partCount=1; + fps.partTotal=1; + fps.dataChunkLength=onFileStruct.byteLengthOfThisFile; + fps.firstDataChunk=onFileStruct.fileData; + fps.iriDataChunk=onFileStruct.fileData; + fps.allocateIrIDataChunkAutomatically=true; + fps.iriWriteOffset=0; + fps.senderSystemAddress=packet->systemAddress; + fps.senderGuid=packet->guid; + fileListReceiver->downloadHandler->OnFileProgress(&fps); + + // Got a complete file + // Either we are using IncrementalReadInterface and it was a small file or + // We are not using IncrementalReadInterface + if (fileListReceiver->downloadHandler->OnFile(&onFileStruct)) + rakFree_Ex(onFileStruct.fileData, _FILE_AND_LINE_ ); + + fileListReceiver->filesReceived++; + + // If this set is done, free the memory for it. + if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived) + { + FileListTransferCBInterface::DownloadCompleteStruct dcs; + dcs.setID=fileListReceiver->setID; + dcs.numberOfFilesInThisSet=fileListReceiver->setCount; + dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + dcs.senderSystemAddress=packet->systemAddress; + dcs.senderGuid=packet->guid; + + if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) + { + fileListReceiver->downloadHandler->OnDereference(); + if (fileListReceiver->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); + fileListReceivers.Delete(onFileStruct.setID); + RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); + } + } + + } + else + { + inBitStream.AlignReadToByteBoundary(); + + char *firstDataChunk; + unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits(); + unsigned int unreadBytes = BITS_TO_BYTES(unreadBits); + firstDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); + + onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength+unreadBytes; + onFileStruct.bytesDownloadedForThisFile=onFileStruct.byteLengthOfThisFile; + + FileListTransferCBInterface::FileProgressStruct fps; + fps.onFileStruct=&onFileStruct; + fps.partCount=partCount; + fps.partTotal=partTotal; + fps.dataChunkLength=unreadBytes; + fps.firstDataChunk=firstDataChunk; + fps.iriDataChunk=0; + fps.allocateIrIDataChunkAutomatically=true; + fps.iriWriteOffset=0; + fps.senderSystemAddress=packet->systemAddress; + fps.senderGuid=packet->guid; + + // Remote system is sending a complete file, but the file is large enough that we get ID_PROGRESS_NOTIFICATION from the transport layer + fileListReceiver->downloadHandler->OnFileProgress(&fps); + + } + + return true; +} +PluginReceiveResult FileListTransfer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_FILE_LIST_TRANSFER_HEADER: + DecodeSetHeader(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FILE_LIST_TRANSFER_FILE: + DecodeFile(packet, true); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FILE_LIST_REFERENCE_PUSH: + OnReferencePush(packet, true); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FILE_LIST_REFERENCE_PUSH_ACK: + OnReferencePushAck(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_DOWNLOAD_PROGRESS: + if (packet->length>sizeof(MessageID)+sizeof(unsigned int)*3) + { + if (packet->data[sizeof(MessageID)+sizeof(unsigned int)*3]==ID_FILE_LIST_TRANSFER_FILE) + { + DecodeFile(packet, false); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + if (packet->data[sizeof(MessageID)+sizeof(unsigned int)*3]==ID_FILE_LIST_REFERENCE_PUSH) + { + OnReferencePush(packet, false); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + break; + } + + return RR_CONTINUE_PROCESSING; +} +void FileListTransfer::OnRakPeerShutdown(void) +{ + threadPool.StopThreads(); + threadPool.ClearInput(); + Clear(); +} +void FileListTransfer::Clear(void) +{ + unsigned i; + for (i=0; i < fileListReceivers.Size(); i++) + { + fileListReceivers[i]->downloadHandler->OnDereference(); + if (fileListReceivers[i]->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); + } + fileListReceivers.Clear(); + + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + FileToPushRecipient *ftpr = fileToPushRecipientList[i]; + // Taken out of the list + ftpr->Deref(); + } + fileToPushRecipientList.Clear(false,_FILE_AND_LINE_); + fileToPushRecipientListMutex.Unlock(); + + //filesToPush.Clear(false, _FILE_AND_LINE_); +} +void FileListTransfer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + RemoveReceiver(systemAddress); +} +void FileListTransfer::CancelReceive(unsigned short setId) +{ + if (fileListReceivers.Has(setId)==false) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return; + } + FileListReceiver *fileListReceiver=fileListReceivers.Get(setId); + fileListReceiver->downloadHandler->OnDereference(); + if (fileListReceiver->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); + fileListReceivers.Delete(setId); +} +void FileListTransfer::RemoveReceiver(SystemAddress systemAddress) +{ + unsigned i; + i=0; + threadPool.LockInput(); + while (i < threadPool.InputSize()) + { + if (threadPool.GetInputAtIndex(i).systemAddress==systemAddress) + { + threadPool.RemoveInputAtIndex(i); + } + else + i++; + } + threadPool.UnlockInput(); + + i=0; + while (i < fileListReceivers.Size()) + { + if (fileListReceivers[i]->allowedSender==systemAddress) + { + fileListReceivers[i]->downloadHandler->OnDereference(); + if (fileListReceivers[i]->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); + fileListReceivers.RemoveAtIndex(i); + } + else + i++; + } + + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + if (fileToPushRecipientList[i]->systemAddress==systemAddress) + { + FileToPushRecipient *ftpr = fileToPushRecipientList[i]; + + // Tell the user that this recipient was lost + for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++) + fileListProgressCallbacks[flpcIndex]->OnSendAborted(ftpr->systemAddress); + + fileToPushRecipientList.RemoveAtIndex(i); + // Taken out of the list + ftpr->Deref(); + break; + } + } + fileToPushRecipientListMutex.Unlock(); +} +bool FileListTransfer::IsHandlerActive(unsigned short setId) +{ + return fileListReceivers.Has(setId); +} +void FileListTransfer::AddCallback(FileListProgress *cb) +{ + if (cb==0) + return; + + if (fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int) -1) + fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_); +} +void FileListTransfer::RemoveCallback(FileListProgress *cb) +{ + unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb); + if (idx!=(unsigned int) -1) + fileListProgressCallbacks.RemoveAtIndex(idx); +} +void FileListTransfer::ClearCallbacks(void) +{ + fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_); +} +void FileListTransfer::GetCallbacks(DataStructures::List &callbacks) +{ + callbacks = fileListProgressCallbacks; +} + +void FileListTransfer::Update(void) +{ + unsigned i; + i=0; + while (i < fileListReceivers.Size()) + { + if (fileListReceivers[i]->downloadHandler->Update()==false) + { + fileListReceivers[i]->downloadHandler->OnDereference(); + if (fileListReceivers[i]->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceivers[i]->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceivers[i], _FILE_AND_LINE_); + fileListReceivers.RemoveAtIndex(i); + } + else + i++; + } +} +void FileListTransfer::OnReferencePush(Packet *packet, bool isTheFileAndIsNotDownloadProgress) +{ + RakNet::BitStream refPushAck; + if (isTheFileAndIsNotDownloadProgress) + { + // This is not a progress notification, it is actually the entire packet + refPushAck.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH_ACK); + SendUnified(&refPushAck,HIGH_PRIORITY, RELIABLE, 0, packet->systemAddress, false); + } + else + { + // 12/23/09 Why do I care about ID_DOWNLOAD_PROGRESS for reference pushes? + return; + } + + FileListTransferCBInterface::OnFileStruct onFileStruct; + RakNet::BitStream inBitStream(packet->data, packet->length, false); + inBitStream.IgnoreBits(8); + + unsigned int partCount=0; + unsigned int partTotal=1; + unsigned int partLength=0; + onFileStruct.fileData=0; + if (isTheFileAndIsNotDownloadProgress==false) + { + // UNREACHABLE CODE + // Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp + inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true ); + inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true ); + inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true ); + inBitStream.IgnoreBits(8); + // The header is appended to every chunk, which we continue to read after this statement flrMemoryBlock + } + + inBitStream >> onFileStruct.context; + // inBitStream.Read(onFileStruct.context); + inBitStream.Read(onFileStruct.setID); + FileListReceiver *fileListReceiver; + if (fileListReceivers.Has(onFileStruct.setID)==false) + { + return; + } + fileListReceiver=fileListReceivers.Get(onFileStruct.setID); + if (fileListReceiver->allowedSender!=packet->systemAddress) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return; + } + +#ifdef _DEBUG + RakAssert(fileListReceiver->gotSetHeader==true); +#endif + + if (StringCompressor::Instance()->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false) + { +#ifdef _DEBUG + RakAssert(0); +#endif + return; + } + + inBitStream.ReadCompressed(onFileStruct.fileIndex); + inBitStream.ReadCompressed(onFileStruct.byteLengthOfThisFile); + unsigned int offset; + unsigned int chunkLength; + inBitStream.ReadCompressed(offset); + inBitStream.ReadCompressed(chunkLength); + + bool lastChunk=false; + inBitStream.Read(lastChunk); + bool finished = lastChunk && isTheFileAndIsNotDownloadProgress; + + if (isTheFileAndIsNotDownloadProgress==false) + fileListReceiver->partLength=partLength; + + FLR_MemoryBlock mb; + if (fileListReceiver->pushedFiles.Has(onFileStruct.fileIndex)==false) + { + mb.flrMemoryBlock=(char*) rakMalloc_Ex(onFileStruct.byteLengthOfThisFile, _FILE_AND_LINE_); + fileListReceiver->pushedFiles.SetNew(onFileStruct.fileIndex, mb); + } + else + { + mb=fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex); + } + + unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits(); + unsigned int unreadBytes = BITS_TO_BYTES(unreadBits); + unsigned int amountToRead; + if (isTheFileAndIsNotDownloadProgress) + amountToRead=chunkLength; + else + amountToRead=unreadBytes; + + inBitStream.AlignReadToByteBoundary(); + + FileListTransferCBInterface::FileProgressStruct fps; + + if (isTheFileAndIsNotDownloadProgress) + { + if (mb.flrMemoryBlock) + { + // Either the very first block, or a subsequent block and allocateIrIDataChunkAutomatically was true for the first block + memcpy(mb.flrMemoryBlock+offset, inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()), amountToRead); + fps.iriDataChunk=mb.flrMemoryBlock+offset; + } + else + { + // In here mb.flrMemoryBlock is null + // This means the first block explicitly deallocated the memory, and no blocks will be permanently held by RakNet + fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); + } + + onFileStruct.bytesDownloadedForThisFile=offset+amountToRead; + } + else + { + fileListReceiver->setTotalDownloadedLength+=partLength; + onFileStruct.bytesDownloadedForThisFile=partCount*partLength; + fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()); + } + onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength; + + onFileStruct.numberOfFilesInThisSet=fileListReceiver->setCount; +// onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength; + onFileStruct.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + // Note: mb.flrMemoryBlock may be null here + onFileStruct.fileData=mb.flrMemoryBlock; + onFileStruct.senderSystemAddress=packet->systemAddress; + onFileStruct.senderGuid=packet->guid; + + unsigned int totalNotifications; + unsigned int currentNotificationIndex; + if (chunkLength==0 || chunkLength==onFileStruct.byteLengthOfThisFile) + totalNotifications=1; + else + totalNotifications = onFileStruct.byteLengthOfThisFile / chunkLength + 1; + + if (chunkLength==0) + currentNotificationIndex = 0; + else + currentNotificationIndex = offset / chunkLength; + + fps.onFileStruct=&onFileStruct; + fps.partCount=currentNotificationIndex; + fps.partTotal=totalNotifications; + fps.dataChunkLength=amountToRead; + fps.firstDataChunk=mb.flrMemoryBlock; + fps.allocateIrIDataChunkAutomatically=true; + fps.onFileStruct->fileData=mb.flrMemoryBlock; + fps.iriWriteOffset=offset; + fps.senderSystemAddress=packet->systemAddress; + fps.senderGuid=packet->guid; + + if (finished) + { + char *oldFileData=fps.onFileStruct->fileData; + if (fps.partCount==0) + fps.firstDataChunk=fps.iriDataChunk; + if (fps.partTotal==1) + fps.onFileStruct->fileData=fps.iriDataChunk; + fileListReceiver->downloadHandler->OnFileProgress(&fps); + + // Incremental read interface sent us a file chunk + // This is the last file chunk we were waiting for to consider the file done + if (fileListReceiver->downloadHandler->OnFile(&onFileStruct)) + rakFree_Ex(oldFileData, _FILE_AND_LINE_ ); + fileListReceiver->pushedFiles.Delete(onFileStruct.fileIndex); + + fileListReceiver->filesReceived++; + + // If this set is done, free the memory for it. + if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived) + { + FileListTransferCBInterface::DownloadCompleteStruct dcs; + dcs.setID=fileListReceiver->setID; + dcs.numberOfFilesInThisSet=fileListReceiver->setCount; + dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength; + dcs.senderSystemAddress=packet->systemAddress; + dcs.senderGuid=packet->guid; + + if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false) + { + fileListReceiver->downloadHandler->OnDereference(); + fileListReceivers.Delete(onFileStruct.setID); + if (fileListReceiver->deleteDownloadHandler) + RakNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_); + RakNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_); + } + } + } + else + { + if (isTheFileAndIsNotDownloadProgress) + { + // 12/23/09 Don't use OnReferencePush anymore, just use OnFileProgress + fileListReceiver->downloadHandler->OnFileProgress(&fps); + + if (fps.allocateIrIDataChunkAutomatically==false) + { + rakFree_Ex(fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock, _FILE_AND_LINE_ ); + fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock=0; + } + } + else + { + // This is a download progress notification for a file chunk using incremental read interface + // We don't have all the data for this chunk yet + + // UNREACHABLE CODE + totalNotifications = onFileStruct.byteLengthOfThisFile / fileListReceiver->partLength + 1; + if (isTheFileAndIsNotDownloadProgress==false) + currentNotificationIndex = (offset+partCount*fileListReceiver->partLength) / fileListReceiver->partLength ; + else + currentNotificationIndex = (offset+chunkLength) / fileListReceiver->partLength ; + unreadBytes = onFileStruct.byteLengthOfThisFile - ((currentNotificationIndex+1) * fileListReceiver->partLength); + + if (rakPeerInterface) + { + // Thus chunk is incomplete + fps.iriDataChunk=0; + + fileListReceiver->downloadHandler->OnFileProgress(&fps); + } + } + } + + return; +} +namespace RakNet +{ +int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData) +{ + (void) perThreadData; + + FileListTransfer *fileListTransfer = threadData.fileListTransfer; + SystemAddress systemAddress = threadData.systemAddress; + *returnOutput=false; + + // Was previously using GetStatistics to get outgoing buffer size, but TCP with UnifiedSend doesn't have this + unsigned int bytesRead; + const char *dataBlocks[2]; + int lengths[2]; + unsigned int smallFileTotalSize=0; + RakNet::BitStream outBitstream; + unsigned int ftpIndex; + + fileListTransfer->fileToPushRecipientListMutex.Lock(); + for (ftpIndex=0; ftpIndex < fileListTransfer->fileToPushRecipientList.Size(); ftpIndex++) + { + FileListTransfer::FileToPushRecipient *ftpr = fileListTransfer->fileToPushRecipientList[ftpIndex]; + // Referenced by both ftpr and list + ftpr->AddRef(); + + fileListTransfer->fileToPushRecipientListMutex.Unlock(); + + if (ftpr->systemAddress==systemAddress) + { + FileListTransfer::FileToPush *ftp = ftpr->filesToPush.Peek(); + + // Read and send chunk. If done, delete at this index + void *buff = rakMalloc_Ex(ftp->chunkSize, _FILE_AND_LINE_); + if (buff==0) + { + ftpr->Deref(); + notifyOutOfMemory(_FILE_AND_LINE_); + return 0; + } + + // Read the next file chunk + bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context); + + bool done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead; + while (done && ftp->currentOffset==0 && ftpr->filesToPush.Size()>=2 && smallFileTotalSizechunkSize) + { + // Send all small files at once, rather than wait for ID_FILE_LIST_REFERENCE_PUSH. But at least one ID_FILE_LIST_REFERENCE_PUSH must be sent + outBitstream.Reset(); + outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); + // outBitstream.Write(ftp->fileListNode.context); + outBitstream << ftp->fileListNode.context; + outBitstream.Write(ftp->setID); + StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream); + outBitstream.WriteCompressed(ftp->setIndex); + outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes + outBitstream.AlignWriteToByteBoundary(); + dataBlocks[0]=(char*) outBitstream.GetData(); + lengths[0]=outBitstream.GetNumberOfBytesUsed(); + dataBlocks[1]=(const char*) buff; + lengths[1]=bytesRead; + + fileListTransfer->SendListUnified(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, systemAddress, false); + + // LWS : fixed freed pointer reference +// unsigned int chunkSize = ftp->chunkSize; + RakNet::OP_DELETE(ftp,_FILE_AND_LINE_); + ftpr->filesToPush.Pop(); + smallFileTotalSize+=bytesRead; + //done = bytesRead!=ftp->chunkSize; + ftp = ftpr->filesToPush.Peek(); + + bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context); + done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead; + } + + + outBitstream.Reset(); + outBitstream.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH); + // outBitstream.Write(ftp->fileListNode.context); + outBitstream << ftp->fileListNode.context; + outBitstream.Write(ftp->setID); + StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream); + outBitstream.WriteCompressed(ftp->setIndex); + outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes + outBitstream.WriteCompressed(ftp->currentOffset); + ftp->currentOffset+=bytesRead; + outBitstream.WriteCompressed(bytesRead); + outBitstream.Write(done); + + for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++) + fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePush(ftp->fileListNode.filename, ftp->fileListNode.fileLengthBytes, ftp->currentOffset-bytesRead, bytesRead, done, systemAddress, ftp->setID); + + dataBlocks[0]=(char*) outBitstream.GetData(); + lengths[0]=outBitstream.GetNumberOfBytesUsed(); + dataBlocks[1]=(char*) buff; + lengths[1]=bytesRead; + //rakPeerInterface->SendList(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, ftp->systemAddress, false); + fileListTransfer->SendListUnified(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, systemAddress, false); + + // Mutex state: FileToPushRecipient (ftpr) has AddRef. fileToPushRecipientListMutex not locked. + if (done) + { + // Done + unsigned short setId = ftp->setID; + RakNet::OP_DELETE(ftp,_FILE_AND_LINE_); + ftpr->filesToPush.Pop(); + + if (ftpr->filesToPush.Size()==0) + { + for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++) + fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(systemAddress, setId); + + // Remove ftpr from fileToPushRecipientList + fileListTransfer->RemoveFromList(ftpr); + } + } + + // ftpr out of scope + ftpr->Deref(); + + rakFree_Ex(buff, _FILE_AND_LINE_ ); + return 0; + } + else + { + ftpr->Deref(); + fileListTransfer->fileToPushRecipientListMutex.Lock(); + } + } + + fileListTransfer->fileToPushRecipientListMutex.Unlock(); + + return 0; +} +} +void FileListTransfer::SendIRIToAddress(SystemAddress systemAddress) +{ + ThreadData threadData; + threadData.fileListTransfer=this; + threadData.systemAddress=systemAddress; + + if (threadPool.WasStarted()) + { + threadPool.AddInput(SendIRIToAddressCB, threadData); + } + else + { + bool doesNothing; + SendIRIToAddressCB(threadData, &doesNothing, 0); + } +} +void FileListTransfer::OnReferencePushAck(Packet *packet) +{ + SendIRIToAddress(packet->systemAddress); +} +void FileListTransfer::RemoveFromList(FileToPushRecipient *ftpr) +{ + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + if (fileToPushRecipientList[i]==ftpr) + { + fileToPushRecipientList.RemoveAtIndex(i); + // List no longer references + ftpr->Deref(); + fileToPushRecipientListMutex.Unlock(); + return; + } + } + fileToPushRecipientListMutex.Unlock(); +} +unsigned int FileListTransfer::GetPendingFilesToAddress(SystemAddress recipient) +{ + fileToPushRecipientListMutex.Lock(); + for (unsigned int i=0; i < fileToPushRecipientList.Size(); i++) + { + if (fileToPushRecipientList[i]->systemAddress==recipient) + { + unsigned int size = fileToPushRecipientList[i]->filesToPush.Size(); + fileToPushRecipientListMutex.Unlock(); + return size; + } + } + fileToPushRecipientListMutex.Unlock(); + + return 0; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/FileListTransfer.h b/src/raknet/FileListTransfer.h index 11bc4ad..c3d794c 100755 --- a/src/raknet/FileListTransfer.h +++ b/src/raknet/FileListTransfer.h @@ -1,173 +1,173 @@ -/// \file FileListTransfer.h -/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FileListTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __FILE_LIST_TRANFER_H -#define __FILE_LIST_TRANFER_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "DS_Map.h" -#include "RakNetTypes.h" -#include "PacketPriority.h" -#include "RakMemoryOverride.h" -#include "FileList.h" -#include "DS_Queue.h" -#include "SimpleMutex.h" -#include "ThreadPool.h" - -namespace RakNet -{ -/// Forward declarations -class IncrementalReadInterface; -class FileListTransferCBInterface; -class FileListProgress; -struct FileListReceiver; - -/// \defgroup FILE_LIST_TRANSFER_GROUP FileListTransfer -/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. -/// \details Similar to the DirectoryDeltaTransfer plugin, except that it doesn't send deltas based on pre-existing files or actually write the files to disk. -/// -/// Usage: -/// Call SetupReceive to allow one file set to arrive. The value returned by FileListTransfer::SetupReceive() -/// is the setID that is allowed. -/// It's up to you to transmit this value to the other system, along with information indicating what kind of files you want to get. -/// The other system should then prepare a FileList and call FileListTransfer::Send(), passing the return value of FileListTransfer::SetupReceive() -/// as the \a setID parameter to FileListTransfer::Send() -/// \ingroup FILE_LIST_TRANSFER_GROUP -class RAK_DLL_EXPORT FileListTransfer : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FileListTransfer) - - FileListTransfer(); - virtual ~FileListTransfer(); - - /// \brief Optionally start worker threads when using _incrementalReadInterface for the Send() operation - /// \param[in] numThreads how many worker threads to start - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - void StartIncrementalReadThreads(int numThreads, int threadPriority=-99999); - - /// \brief Allows one corresponding Send() call from another system to arrive. - /// \param[in] handler The class to call on each file - /// \param[in] deleteHandler True to delete the handler when it is no longer needed. False to not do so. - /// \param[in] allowedSender Which system to allow files from. - /// \return A set ID value, which should be passed as the \a setID value to the Send() call on the other system. This value will be returned in the callback and is unique per file set. Returns 65535 on failure (not connected to sender) - unsigned short SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender); - - /// \brief Send the FileList structure to another system, which must have previously called SetupReceive(). - /// \param[in] fileList A list of files. The data contained in FileList::data will be sent incrementally and compressed among all files in the set - /// \param[in] rakPeer The instance of RakNet to use to send the message. Pass 0 to use the instance the plugin is attached to - /// \param[in] recipient The address of the system to send to - /// \param[in] setID The return value of SetupReceive() which was previously called on \a recipient - /// \param[in] priority Passed to RakPeerInterface::Send() - /// \param[in] orderingChannel Passed to RakPeerInterface::Send() - /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, _incrementalReadInterface will be used to read the file in chunks of size \a chunkSize - /// \param[in] _chunkSize How large of a block of a file to send at once - void Send(FileList *fileList, RakNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface=0, unsigned int _chunkSize=262144*4*16); - - /// Return number of files waiting to go out to a particular address - unsigned int GetPendingFilesToAddress(SystemAddress recipient); - - /// \brief Stop a download. - void CancelReceive(unsigned short setId); - - /// \brief Remove all handlers associated with a particular system address. - void RemoveReceiver(SystemAddress systemAddress); - - /// \brief Is a handler passed to SetupReceive still running? - bool IsHandlerActive(unsigned short setId); - - /// \brief Adds a callback to get progress reports about what the file list instances do. - /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. - void AddCallback(FileListProgress *cb); - - /// \brief Removes a callback - /// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback() - void RemoveCallback(FileListProgress *cb); - - /// \brief Removes all callbacks - void ClearCallbacks(void); - - /// Returns all callbacks added with AddCallback() - /// \param[out] callbacks The list is set to the list of callbacks - void GetCallbacks(DataStructures::List &callbacks); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal For plugin handling - virtual void OnRakPeerShutdown(void); - /// \internal For plugin handling - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal For plugin handling - virtual void Update(void); - -protected: - bool DecodeSetHeader(Packet *packet); - bool DecodeFile(Packet *packet, bool fullFile); - - void Clear(void); - - void OnReferencePush(Packet *packet, bool fullFile); - void OnReferencePushAck(Packet *packet); - void SendIRIToAddress(SystemAddress systemAddress); - - DataStructures::Map fileListReceivers; - unsigned short setId; - DataStructures::List fileListProgressCallbacks; - - struct FileToPush - { - FileListNode fileListNode; - PacketPriority packetPriority; - char orderingChannel; - unsigned int currentOffset; - unsigned short setID; - unsigned int setIndex; - IncrementalReadInterface *incrementalReadInterface; - unsigned int chunkSize; - }; - struct FileToPushRecipient - { - unsigned int refCount; - SimpleMutex refCountMutex; - void DeleteThis(void); - void AddRef(void); - void Deref(void); - - SystemAddress systemAddress; - DataStructures::Queue filesToPush; - }; - DataStructures::List< FileToPushRecipient* > fileToPushRecipientList; - SimpleMutex fileToPushRecipientListMutex; - void RemoveFromList(FileToPushRecipient *ftpr); - - struct ThreadData - { - FileListTransfer *fileListTransfer; - SystemAddress systemAddress; - }; - - ThreadPool threadPool; - - friend int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData); -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file FileListTransfer.h +/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FileListTransfer==1 && _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __FILE_LIST_TRANFER_H +#define __FILE_LIST_TRANFER_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "DS_Map.h" +#include "RakNetTypes.h" +#include "PacketPriority.h" +#include "RakMemoryOverride.h" +#include "FileList.h" +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "ThreadPool.h" + +namespace RakNet +{ +/// Forward declarations +class IncrementalReadInterface; +class FileListTransferCBInterface; +class FileListProgress; +struct FileListReceiver; + +/// \defgroup FILE_LIST_TRANSFER_GROUP FileListTransfer +/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. +/// \details Similar to the DirectoryDeltaTransfer plugin, except that it doesn't send deltas based on pre-existing files or actually write the files to disk. +/// +/// Usage: +/// Call SetupReceive to allow one file set to arrive. The value returned by FileListTransfer::SetupReceive() +/// is the setID that is allowed. +/// It's up to you to transmit this value to the other system, along with information indicating what kind of files you want to get. +/// The other system should then prepare a FileList and call FileListTransfer::Send(), passing the return value of FileListTransfer::SetupReceive() +/// as the \a setID parameter to FileListTransfer::Send() +/// \ingroup FILE_LIST_TRANSFER_GROUP +class RAK_DLL_EXPORT FileListTransfer : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FileListTransfer) + + FileListTransfer(); + virtual ~FileListTransfer(); + + /// \brief Optionally start worker threads when using _incrementalReadInterface for the Send() operation + /// \param[in] numThreads how many worker threads to start + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + void StartIncrementalReadThreads(int numThreads, int threadPriority=-99999); + + /// \brief Allows one corresponding Send() call from another system to arrive. + /// \param[in] handler The class to call on each file + /// \param[in] deleteHandler True to delete the handler when it is no longer needed. False to not do so. + /// \param[in] allowedSender Which system to allow files from. + /// \return A set ID value, which should be passed as the \a setID value to the Send() call on the other system. This value will be returned in the callback and is unique per file set. Returns 65535 on failure (not connected to sender) + unsigned short SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender); + + /// \brief Send the FileList structure to another system, which must have previously called SetupReceive(). + /// \param[in] fileList A list of files. The data contained in FileList::data will be sent incrementally and compressed among all files in the set + /// \param[in] rakPeer The instance of RakNet to use to send the message. Pass 0 to use the instance the plugin is attached to + /// \param[in] recipient The address of the system to send to + /// \param[in] setID The return value of SetupReceive() which was previously called on \a recipient + /// \param[in] priority Passed to RakPeerInterface::Send() + /// \param[in] orderingChannel Passed to RakPeerInterface::Send() + /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, _incrementalReadInterface will be used to read the file in chunks of size \a chunkSize + /// \param[in] _chunkSize How large of a block of a file to send at once + void Send(FileList *fileList, RakNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface=0, unsigned int _chunkSize=262144*4*16); + + /// Return number of files waiting to go out to a particular address + unsigned int GetPendingFilesToAddress(SystemAddress recipient); + + /// \brief Stop a download. + void CancelReceive(unsigned short setId); + + /// \brief Remove all handlers associated with a particular system address. + void RemoveReceiver(SystemAddress systemAddress); + + /// \brief Is a handler passed to SetupReceive still running? + bool IsHandlerActive(unsigned short setId); + + /// \brief Adds a callback to get progress reports about what the file list instances do. + /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. + void AddCallback(FileListProgress *cb); + + /// \brief Removes a callback + /// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback() + void RemoveCallback(FileListProgress *cb); + + /// \brief Removes all callbacks + void ClearCallbacks(void); + + /// Returns all callbacks added with AddCallback() + /// \param[out] callbacks The list is set to the list of callbacks + void GetCallbacks(DataStructures::List &callbacks); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal For plugin handling + virtual void OnRakPeerShutdown(void); + /// \internal For plugin handling + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal For plugin handling + virtual void Update(void); + +protected: + bool DecodeSetHeader(Packet *packet); + bool DecodeFile(Packet *packet, bool fullFile); + + void Clear(void); + + void OnReferencePush(Packet *packet, bool fullFile); + void OnReferencePushAck(Packet *packet); + void SendIRIToAddress(SystemAddress systemAddress); + + DataStructures::Map fileListReceivers; + unsigned short setId; + DataStructures::List fileListProgressCallbacks; + + struct FileToPush + { + FileListNode fileListNode; + PacketPriority packetPriority; + char orderingChannel; + unsigned int currentOffset; + unsigned short setID; + unsigned int setIndex; + IncrementalReadInterface *incrementalReadInterface; + unsigned int chunkSize; + }; + struct FileToPushRecipient + { + unsigned int refCount; + SimpleMutex refCountMutex; + void DeleteThis(void); + void AddRef(void); + void Deref(void); + + SystemAddress systemAddress; + DataStructures::Queue filesToPush; + }; + DataStructures::List< FileToPushRecipient* > fileToPushRecipientList; + SimpleMutex fileToPushRecipientListMutex; + void RemoveFromList(FileToPushRecipient *ftpr); + + struct ThreadData + { + FileListTransfer *fileListTransfer; + SystemAddress systemAddress; + }; + + ThreadPool threadPool; + + friend int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData); +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/FileListTransferCBInterface.h b/src/raknet/FileListTransferCBInterface.h index 7337317..ed1fb91 100755 --- a/src/raknet/FileListTransferCBInterface.h +++ b/src/raknet/FileListTransferCBInterface.h @@ -1,154 +1,154 @@ -/// \file FileListTransferCBInterface.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H -#define __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H - -#include "RakMemoryOverride.h" -#include "FileListNodeContext.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -namespace RakNet -{ - -/// \brief Used by FileListTransfer plugin as a callback for when we get a file. -/// \details You get the last file when fileIndex==numberOfFilesInThisSet -/// \sa FileListTransfer -class FileListTransferCBInterface -{ -public: - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct OnFileStruct - { - /// \brief The index into the set of files, from 0 to numberOfFilesInThisSet - unsigned fileIndex; - - /// \brief The name of the file - char fileName[512]; - - /// \brief The data pointed to by the file - char *fileData; - - /// \brief The actual length of this file. - BitSize_t byteLengthOfThisFile; - - /// \brief How many bytes of this file has been downloaded - BitSize_t bytesDownloadedForThisFile; - - /// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time. - /// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive - unsigned short setID; - - /// \brief The number of files that are in this set. - unsigned numberOfFilesInThisSet; - - /// \brief The total length of the transmitted files for this set, after being uncompressed - unsigned byteLengthOfThisSet; - - /// \brief The total length, in bytes, downloaded for this set. - unsigned bytesDownloadedForThisSet; - - /// \brief User data passed to one of the functions in the FileList class. - /// \details However, on error, this is instead changed to one of the enumerations in the PatchContext structure. - FileListNodeContext context; - - /// \brief Who sent this file - SystemAddress senderSystemAddress; - - /// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP) - RakNetGUID senderGuid; - }; - - // Note: If this structure is changed the struct in the swig files need to be changed as well - struct FileProgressStruct - { - /// \param[out] onFileStruct General information about this file, such as the filename and the first \a partLength bytes. You do NOT need to save this data yourself. The complete file will arrive normally. - OnFileStruct *onFileStruct; - /// \param[out] partCount The zero based index into partTotal. The percentage complete done of this file is 100 * (partCount+1)/partTotal - unsigned int partCount; - /// \param[out] partTotal The total number of parts this file was split into. Each part will be roughly the MTU size, minus the UDP header and RakNet headers - unsigned int partTotal; - /// \param[out] dataChunkLength How many bytes long firstDataChunk and iriDataChunk are - unsigned int dataChunkLength; - /// \param[out] firstDataChunk The first \a partLength of the final file. If you store identifying information about the file in the first \a partLength bytes, you can read them while the download is taking place. If this hasn't arrived yet, firstDataChunk will be 0 - char *firstDataChunk; - /// \param[out] iriDataChunk If the remote system is sending this file using IncrementalReadInterface, then this is the chunk we just downloaded. It will not exist in memory after this callback. You should either store this to disk, or in memory. If it is 0, then the file is smaller than one chunk, and will be held in memory automatically - char *iriDataChunk; - /// \param[out] iriWriteOffset Offset in bytes from the start of the file for the data pointed to by iriDataChunk - unsigned int iriWriteOffset; - /// \param[out] Who sent this file - SystemAddress senderSystemAddress; - /// \param[out] Who sent this file. Not valid when using TCP, only RakPeer (UDP) - RakNetGUID senderGuid; - /// \param[in] allocateIrIDataChunkAutomatically If true, then RakNet will hold iriDataChunk for you and return it in OnFile. Defaults to true - bool allocateIrIDataChunkAutomatically; - }; - - struct DownloadCompleteStruct - { - /// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time. - /// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive - unsigned short setID; - - /// \brief The number of files that are in this set. - unsigned numberOfFilesInThisSet; - - /// \brief The total length of the transmitted files for this set, after being uncompressed - unsigned byteLengthOfThisSet; - - /// \brief Who sent this file - SystemAddress senderSystemAddress; - - /// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP) - RakNetGUID senderGuid; - }; - - FileListTransferCBInterface() {} - virtual ~FileListTransferCBInterface() {} - - /// \brief Got a file. - /// \details This structure is only valid for the duration of this function call. - /// \return Return true to have RakNet delete the memory allocated to hold this file for this function call. - virtual bool OnFile(OnFileStruct *onFileStruct)=0; - - /// \brief Got part of a big file internally in RakNet - /// \details This is called in one of two circumstances: Either the transport layer is returning ID_PROGRESS_NOTIFICATION, or you got a block via IncrementalReadInterface - /// If the transport layer is returning ID_PROGRESS_NOTIFICATION (see RakPeer::SetSplitMessageProgressInterval()) then FileProgressStruct::iriDataChunk will be 0. - /// If this is a block via IncrementalReadInterface, then iriDataChunk will point to the block just downloaded. - /// If not using IncrementalReadInterface, then you only care about partCount and partTotal to tell how far the download has progressed. YOu can use firstDataChunk to read the first part of the file if desired. The file is usable when you get the OnFile callback. - /// If using IncrementalReadInterface and you let RakNet buffer the files in memory (default), then it is the same as above. The file is usable when you get the OnFile callback. - /// If using IncrementalReadInterface and you do not let RakNet buffer the files in memory, then set allocateIrIDataChunkAutomatically to false. Write the file to disk whenever you get OnFileProgress and iriDataChunk is not 0, and ignore OnFile. - virtual void OnFileProgress(FileProgressStruct *fps)=0; - - /// \brief Called while the handler is active by FileListTransfer - /// \details Return false when you are done with the class. - /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. - virtual bool Update(void) {return true;} - - /// \brief Called when the download is completed. - /// \details If you are finished with this class, return false. - /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. - /// Otherwise return true, and Update will continue to be called. - virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs) {(void) dcs; return false;} - - /// \brief This function is called when this instance is about to be dereferenced by the FileListTransfer plugin. - /// \details Update will no longer be called. - /// It will will be deleted automatically if true was passed to FileListTransfer::SetupReceive::deleteHandler - /// Otherwise it is up to you to delete it yourself. - virtual void OnDereference(void) {} -}; - -} // namespace RakNet - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - +/// \file FileListTransferCBInterface.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H +#define __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H + +#include "RakMemoryOverride.h" +#include "FileListNodeContext.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +namespace RakNet +{ + +/// \brief Used by FileListTransfer plugin as a callback for when we get a file. +/// \details You get the last file when fileIndex==numberOfFilesInThisSet +/// \sa FileListTransfer +class FileListTransferCBInterface +{ +public: + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct OnFileStruct + { + /// \brief The index into the set of files, from 0 to numberOfFilesInThisSet + unsigned fileIndex; + + /// \brief The name of the file + char fileName[512]; + + /// \brief The data pointed to by the file + char *fileData; + + /// \brief The actual length of this file. + BitSize_t byteLengthOfThisFile; + + /// \brief How many bytes of this file has been downloaded + BitSize_t bytesDownloadedForThisFile; + + /// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time. + /// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive + unsigned short setID; + + /// \brief The number of files that are in this set. + unsigned numberOfFilesInThisSet; + + /// \brief The total length of the transmitted files for this set, after being uncompressed + unsigned byteLengthOfThisSet; + + /// \brief The total length, in bytes, downloaded for this set. + unsigned bytesDownloadedForThisSet; + + /// \brief User data passed to one of the functions in the FileList class. + /// \details However, on error, this is instead changed to one of the enumerations in the PatchContext structure. + FileListNodeContext context; + + /// \brief Who sent this file + SystemAddress senderSystemAddress; + + /// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP) + RakNetGUID senderGuid; + }; + + // Note: If this structure is changed the struct in the swig files need to be changed as well + struct FileProgressStruct + { + /// \param[out] onFileStruct General information about this file, such as the filename and the first \a partLength bytes. You do NOT need to save this data yourself. The complete file will arrive normally. + OnFileStruct *onFileStruct; + /// \param[out] partCount The zero based index into partTotal. The percentage complete done of this file is 100 * (partCount+1)/partTotal + unsigned int partCount; + /// \param[out] partTotal The total number of parts this file was split into. Each part will be roughly the MTU size, minus the UDP header and RakNet headers + unsigned int partTotal; + /// \param[out] dataChunkLength How many bytes long firstDataChunk and iriDataChunk are + unsigned int dataChunkLength; + /// \param[out] firstDataChunk The first \a partLength of the final file. If you store identifying information about the file in the first \a partLength bytes, you can read them while the download is taking place. If this hasn't arrived yet, firstDataChunk will be 0 + char *firstDataChunk; + /// \param[out] iriDataChunk If the remote system is sending this file using IncrementalReadInterface, then this is the chunk we just downloaded. It will not exist in memory after this callback. You should either store this to disk, or in memory. If it is 0, then the file is smaller than one chunk, and will be held in memory automatically + char *iriDataChunk; + /// \param[out] iriWriteOffset Offset in bytes from the start of the file for the data pointed to by iriDataChunk + unsigned int iriWriteOffset; + /// \param[out] Who sent this file + SystemAddress senderSystemAddress; + /// \param[out] Who sent this file. Not valid when using TCP, only RakPeer (UDP) + RakNetGUID senderGuid; + /// \param[in] allocateIrIDataChunkAutomatically If true, then RakNet will hold iriDataChunk for you and return it in OnFile. Defaults to true + bool allocateIrIDataChunkAutomatically; + }; + + struct DownloadCompleteStruct + { + /// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time. + /// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive + unsigned short setID; + + /// \brief The number of files that are in this set. + unsigned numberOfFilesInThisSet; + + /// \brief The total length of the transmitted files for this set, after being uncompressed + unsigned byteLengthOfThisSet; + + /// \brief Who sent this file + SystemAddress senderSystemAddress; + + /// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP) + RakNetGUID senderGuid; + }; + + FileListTransferCBInterface() {} + virtual ~FileListTransferCBInterface() {} + + /// \brief Got a file. + /// \details This structure is only valid for the duration of this function call. + /// \return Return true to have RakNet delete the memory allocated to hold this file for this function call. + virtual bool OnFile(OnFileStruct *onFileStruct)=0; + + /// \brief Got part of a big file internally in RakNet + /// \details This is called in one of two circumstances: Either the transport layer is returning ID_PROGRESS_NOTIFICATION, or you got a block via IncrementalReadInterface + /// If the transport layer is returning ID_PROGRESS_NOTIFICATION (see RakPeer::SetSplitMessageProgressInterval()) then FileProgressStruct::iriDataChunk will be 0. + /// If this is a block via IncrementalReadInterface, then iriDataChunk will point to the block just downloaded. + /// If not using IncrementalReadInterface, then you only care about partCount and partTotal to tell how far the download has progressed. YOu can use firstDataChunk to read the first part of the file if desired. The file is usable when you get the OnFile callback. + /// If using IncrementalReadInterface and you let RakNet buffer the files in memory (default), then it is the same as above. The file is usable when you get the OnFile callback. + /// If using IncrementalReadInterface and you do not let RakNet buffer the files in memory, then set allocateIrIDataChunkAutomatically to false. Write the file to disk whenever you get OnFileProgress and iriDataChunk is not 0, and ignore OnFile. + virtual void OnFileProgress(FileProgressStruct *fps)=0; + + /// \brief Called while the handler is active by FileListTransfer + /// \details Return false when you are done with the class. + /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. + virtual bool Update(void) {return true;} + + /// \brief Called when the download is completed. + /// \details If you are finished with this class, return false. + /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. + /// Otherwise return true, and Update will continue to be called. + virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs) {(void) dcs; return false;} + + /// \brief This function is called when this instance is about to be dereferenced by the FileListTransfer plugin. + /// \details Update will no longer be called. + /// It will will be deleted automatically if true was passed to FileListTransfer::SetupReceive::deleteHandler + /// Otherwise it is up to you to delete it yourself. + virtual void OnDereference(void) {} +}; + +} // namespace RakNet + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + diff --git a/src/raknet/FileOperations.cpp b/src/raknet/FileOperations.cpp index 93f6f31..92f3af3 100755 --- a/src/raknet/FileOperations.cpp +++ b/src/raknet/FileOperations.cpp @@ -1,173 +1,173 @@ -#include "FileOperations.h" -#if _RAKNET_SUPPORT_FileOperations==1 -#include "RakMemoryOverride.h" -#include "_FindFirst.h" // For linux -#include -#include -#ifdef _WIN32 -// For mkdir -#include -#include -#else -#include -#include -#include "_FindFirst.h" -#endif -#include "errno.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -#ifdef _MSC_VER -#pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. -#endif -bool WriteFileWithDirectories( const char *path, char *data, unsigned dataLength ) -{ - int index; - FILE *fp; - char *pathCopy; - int res; - - if ( path == 0 || path[ 0 ] == 0 ) - return false; - - pathCopy = (char*) rakMalloc_Ex( strlen( path ) + 1, _FILE_AND_LINE_ ); - - strcpy( pathCopy, path ); - - // Ignore first / if there is one - if (pathCopy[0]) - { - index = 1; - while ( pathCopy[ index ] ) - { - if ( pathCopy[ index ] == '/' || pathCopy[ index ] == '\\') - { - pathCopy[ index ] = 0; - - #ifdef _WIN32 - #pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. - res = mkdir( pathCopy ); - #else - - res = mkdir( pathCopy, 0744 ); - #endif - if (res<0 && errno!=EEXIST && errno!=EACCES) - { - rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); - return false; - } - - pathCopy[ index ] = '/'; - } - - index++; - } - } - - if (data) - { - fp = fopen( path, "wb" ); - - if ( fp == 0 ) - { - rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); - return false; - } - - fwrite( data, 1, dataLength, fp ); - - fclose( fp ); - } - else - { -#ifdef _WIN32 -#pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. - res = mkdir( pathCopy ); -#else - res = mkdir( pathCopy, 0744 ); -#endif - - if (res<0 && errno!=EEXIST) - { - rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); - return false; - } - } - - rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); - - return true; -} -bool IsSlash(unsigned char c) -{ - return c=='/' || c=='\\'; -} - -void AddSlash( char *input ) -{ - if (input==0 || input[0]==0) - return; - - int lastCharIndex=(int) strlen(input)-1; - if (input[lastCharIndex]=='\\') - input[lastCharIndex]='/'; - else if (input[lastCharIndex]!='/') - { - input[lastCharIndex+1]='/'; - input[lastCharIndex+2]=0; - } -} -bool DirectoryExists(const char *directory) -{ - _finddata_t fileInfo; - intptr_t dir; - char baseDirWithStars[560]; - strcpy(baseDirWithStars, directory); - AddSlash(baseDirWithStars); - strcat(baseDirWithStars, "*.*"); - dir=_findfirst(baseDirWithStars, &fileInfo ); - if (dir==-1) - return false; - _findclose(dir); - return true; -} -void QuoteIfSpaces(char *str) -{ - unsigned i; - bool hasSpace=false; - for (i=0; str[i]; i++) - { - if (str[i]==' ') - { - hasSpace=true; - break; - } - } - if (hasSpace) - { - int len=(int)strlen(str); - memmove(str+1, str, len); - str[0]='\"'; - str[len]='\"'; - str[len+1]=0; - } -} -unsigned int GetFileLength(const char *path) -{ - FILE *fp = fopen(path, "rb"); - if (fp==0) return 0; - fseek(fp, 0, SEEK_END); - unsigned int fileLength = ftell(fp); - fclose(fp); - return fileLength; - -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_FileOperations - +#include "FileOperations.h" +#if _RAKNET_SUPPORT_FileOperations==1 +#include "RakMemoryOverride.h" +#include "_FindFirst.h" // For linux +#include +#include +#ifdef _WIN32 +// For mkdir +#include +#include +#else +#include +#include +#include "_FindFirst.h" +#endif +#include "errno.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +#ifdef _MSC_VER +#pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. +#endif +bool WriteFileWithDirectories( const char *path, char *data, unsigned dataLength ) +{ + int index; + FILE *fp; + char *pathCopy; + int res; + + if ( path == 0 || path[ 0 ] == 0 ) + return false; + + pathCopy = (char*) rakMalloc_Ex( strlen( path ) + 1, _FILE_AND_LINE_ ); + + strcpy( pathCopy, path ); + + // Ignore first / if there is one + if (pathCopy[0]) + { + index = 1; + while ( pathCopy[ index ] ) + { + if ( pathCopy[ index ] == '/' || pathCopy[ index ] == '\\') + { + pathCopy[ index ] = 0; + + #ifdef _WIN32 + #pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. + res = mkdir( pathCopy ); + #else + + res = mkdir( pathCopy, 0744 ); + #endif + if (res<0 && errno!=EEXIST && errno!=EACCES) + { + rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); + return false; + } + + pathCopy[ index ] = '/'; + } + + index++; + } + } + + if (data) + { + fp = fopen( path, "wb" ); + + if ( fp == 0 ) + { + rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); + return false; + } + + fwrite( data, 1, dataLength, fp ); + + fclose( fp ); + } + else + { +#ifdef _WIN32 +#pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated. + res = mkdir( pathCopy ); +#else + res = mkdir( pathCopy, 0744 ); +#endif + + if (res<0 && errno!=EEXIST) + { + rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); + return false; + } + } + + rakFree_Ex(pathCopy, _FILE_AND_LINE_ ); + + return true; +} +bool IsSlash(unsigned char c) +{ + return c=='/' || c=='\\'; +} + +void AddSlash( char *input ) +{ + if (input==0 || input[0]==0) + return; + + int lastCharIndex=(int) strlen(input)-1; + if (input[lastCharIndex]=='\\') + input[lastCharIndex]='/'; + else if (input[lastCharIndex]!='/') + { + input[lastCharIndex+1]='/'; + input[lastCharIndex+2]=0; + } +} +bool DirectoryExists(const char *directory) +{ + _finddata_t fileInfo; + intptr_t dir; + char baseDirWithStars[560]; + strcpy(baseDirWithStars, directory); + AddSlash(baseDirWithStars); + strcat(baseDirWithStars, "*.*"); + dir=_findfirst(baseDirWithStars, &fileInfo ); + if (dir==-1) + return false; + _findclose(dir); + return true; +} +void QuoteIfSpaces(char *str) +{ + unsigned i; + bool hasSpace=false; + for (i=0; str[i]; i++) + { + if (str[i]==' ') + { + hasSpace=true; + break; + } + } + if (hasSpace) + { + int len=(int)strlen(str); + memmove(str+1, str, len); + str[0]='\"'; + str[len]='\"'; + str[len+1]=0; + } +} +unsigned int GetFileLength(const char *path) +{ + FILE *fp = fopen(path, "rb"); + if (fp==0) return 0; + fseek(fp, 0, SEEK_END); + unsigned int fileLength = ftell(fp); + fclose(fp); + return fileLength; + +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_FileOperations + diff --git a/src/raknet/FileOperations.h b/src/raknet/FileOperations.h index fc7be06..1e44bd8 100755 --- a/src/raknet/FileOperations.h +++ b/src/raknet/FileOperations.h @@ -1,24 +1,24 @@ -/// \file FileOperations.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FileOperations==1 - -#ifndef __FILE_OPERATIONS_H -#define __FILE_OPERATIONS_H - -#include "Export.h" - -bool RAK_DLL_EXPORT WriteFileWithDirectories( const char *path, char *data, unsigned dataLength ); -bool RAK_DLL_EXPORT IsSlash(unsigned char c); -void RAK_DLL_EXPORT AddSlash( char *input ); -void RAK_DLL_EXPORT QuoteIfSpaces(char *str); -bool RAK_DLL_EXPORT DirectoryExists(const char *directory); -unsigned int RAK_DLL_EXPORT GetFileLength(const char *path); - -#endif - -#endif // _RAKNET_SUPPORT_FileOperations +/// \file FileOperations.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FileOperations==1 + +#ifndef __FILE_OPERATIONS_H +#define __FILE_OPERATIONS_H + +#include "Export.h" + +bool RAK_DLL_EXPORT WriteFileWithDirectories( const char *path, char *data, unsigned dataLength ); +bool RAK_DLL_EXPORT IsSlash(unsigned char c); +void RAK_DLL_EXPORT AddSlash( char *input ); +void RAK_DLL_EXPORT QuoteIfSpaces(char *str); +bool RAK_DLL_EXPORT DirectoryExists(const char *directory); +unsigned int RAK_DLL_EXPORT GetFileLength(const char *path); + +#endif + +#endif // _RAKNET_SUPPORT_FileOperations diff --git a/src/raknet/FormatString.cpp b/src/raknet/FormatString.cpp index f91ae31..e4df09e 100755 --- a/src/raknet/FormatString.cpp +++ b/src/raknet/FormatString.cpp @@ -1,30 +1,30 @@ -#include "FormatString.h" -#include -#include -#include -#include "LinuxStrings.h" - -char * FormatString(const char *format, ...) -{ - static int textIndex=0; - static char text[4][8096]; - va_list ap; - va_start(ap, format); - - if (++textIndex==4) - textIndex=0; - _vsnprintf(text[textIndex], 8096, format, ap); - va_end(ap); - text[textIndex][8096-1]=0; - - return text[textIndex]; -} - -char * FormatStringTS(char *output, const char *format, ...) -{ - va_list ap; - va_start(ap, format); - _vsnprintf(output, 512, format, ap); - va_end(ap); - return output; -} +#include "FormatString.h" +#include +#include +#include +#include "LinuxStrings.h" + +char * FormatString(const char *format, ...) +{ + static int textIndex=0; + static char text[4][8096]; + va_list ap; + va_start(ap, format); + + if (++textIndex==4) + textIndex=0; + _vsnprintf(text[textIndex], 8096, format, ap); + va_end(ap); + text[textIndex][8096-1]=0; + + return text[textIndex]; +} + +char * FormatStringTS(char *output, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + _vsnprintf(output, 512, format, ap); + va_end(ap); + return output; +} diff --git a/src/raknet/FormatString.h b/src/raknet/FormatString.h index 9badba8..1ebdab9 100755 --- a/src/raknet/FormatString.h +++ b/src/raknet/FormatString.h @@ -1,22 +1,22 @@ -/// \file FormatString.h -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef __FORMAT_STRING_H -#define __FORMAT_STRING_H - -#include "Export.h" - -extern "C" { -char * FormatString(const char *format, ...); -} -// Threadsafe -extern "C" { -char * FormatStringTS(char *output, const char *format, ...); -} - - -#endif - +/// \file FormatString.h +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef __FORMAT_STRING_H +#define __FORMAT_STRING_H + +#include "Export.h" + +extern "C" { +char * FormatString(const char *format, ...); +} +// Threadsafe +extern "C" { +char * FormatStringTS(char *output, const char *format, ...); +} + + +#endif + diff --git a/src/raknet/FullyConnectedMesh2.cpp b/src/raknet/FullyConnectedMesh2.cpp index fb9877e..3837a3e 100755 --- a/src/raknet/FullyConnectedMesh2.cpp +++ b/src/raknet/FullyConnectedMesh2.cpp @@ -1,552 +1,552 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FullyConnectedMesh2==1 - -#include "FullyConnectedMesh2.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" -#include "RakAssert.h" -#include "GetTime.h" -#include "Rand.h" -#include "DS_OrderedList.h" - -using namespace RakNet; - -int FCM2ParticipantComp( const FullyConnectedMesh2::FCM2Participant &key, const FullyConnectedMesh2::FCM2Participant &data ) -{ - if (key.fcm2Guid < data.fcm2Guid) - return -1; - if (key.fcm2Guid > data.fcm2Guid) - return 1; - return 0; -} - -STATIC_FACTORY_DEFINITIONS(FullyConnectedMesh2,FullyConnectedMesh2); - -FullyConnectedMesh2::FullyConnectedMesh2() -{ - startupTime=0; - totalConnectionCount=0; - ourFCMGuid=0; - autoParticipateConnections=true; - - - - - connectOnNewRemoteConnections=true; - - hostRakNetGuid=UNASSIGNED_RAKNET_GUID; -} -FullyConnectedMesh2::~FullyConnectedMesh2() -{ - Clear(); -} -RakNetGUID FullyConnectedMesh2::GetConnectedHost(void) const -{ - if (ourFCMGuid==0) - return UNASSIGNED_RAKNET_GUID; - return hostRakNetGuid; -} -SystemAddress FullyConnectedMesh2::GetConnectedHostAddr(void) const -{ - if (ourFCMGuid==0) - return UNASSIGNED_SYSTEM_ADDRESS; - return rakPeerInterface->GetSystemAddressFromGuid(hostRakNetGuid); -} -RakNetGUID FullyConnectedMesh2::GetHostSystem(void) const -{ - if (ourFCMGuid==0) - return rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - - return hostRakNetGuid; -} -bool FullyConnectedMesh2::IsHostSystem(void) const -{ - return GetHostSystem()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -} -void FullyConnectedMesh2::GetHostOrder(DataStructures::List &hostList) -{ - hostList.Clear(true, _FILE_AND_LINE_); - - if (ourFCMGuid==0 || fcm2ParticipantList.Size()==0) - { - hostList.Push(rakPeerInterface->GetMyGUID(), _FILE_AND_LINE_); - return; - } - - FCM2Participant fcm2; - fcm2.fcm2Guid=ourFCMGuid; - fcm2.rakNetGuid=rakPeerInterface->GetMyGUID(); - - DataStructures::OrderedList olist; - olist.Insert(fcm2, fcm2, true, _FILE_AND_LINE_); - for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) - olist.Insert(fcm2ParticipantList[i], fcm2ParticipantList[i], true, _FILE_AND_LINE_); - - for (unsigned int i=0; i < olist.Size(); i++) - { - hostList.Push(olist[i].rakNetGuid, _FILE_AND_LINE_); - } -} -bool FullyConnectedMesh2::IsConnectedHost(void) const -{ - return GetConnectedHost()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -} -void FullyConnectedMesh2::SetAutoparticipateConnections(bool b) -{ - autoParticipateConnections=b; -} -void FullyConnectedMesh2::ResetHostCalculation(void) -{ - hostRakNetGuid=UNASSIGNED_RAKNET_GUID; - startupTime=RakNet::GetTimeUS(); - totalConnectionCount=0; - ourFCMGuid=0; - for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) - SendFCMGuidRequest(fcm2ParticipantList[i].rakNetGuid); -} -bool FullyConnectedMesh2::AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid ) -{ - for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) - { - if (fcm2ParticipantList[i].rakNetGuid==rakNetGuid) - { - if (theirFCMGuid!=0) - fcm2ParticipantList[i].fcm2Guid=theirFCMGuid; - return false; - } - } - - FCM2Participant participant; - participant.rakNetGuid=rakNetGuid; - participant.fcm2Guid=theirFCMGuid; - fcm2ParticipantList.Push(participant,_FILE_AND_LINE_); - - SendFCMGuidRequest(rakNetGuid); - - return true; -} -void FullyConnectedMesh2::AddParticipant( RakNetGUID rakNetGuid ) -{ - if (rakPeerInterface->GetConnectionState(rakPeerInterface->GetSystemAddressFromGuid(rakNetGuid))!=IS_CONNECTED) - { -#ifdef DEBUG_FCM2 - printf("AddParticipant to %s failed (not connected)\n", rakNetGuid.ToString()); -#endif - return; - } - - AddParticipantInternal(rakNetGuid,0); -} -void FullyConnectedMesh2::GetParticipantList(DataStructures::List &participantList) -{ - participantList.Clear(true, _FILE_AND_LINE_); - unsigned int i; - for (i=0; i < fcm2ParticipantList.Size(); i++) - participantList.Push(fcm2ParticipantList[i].rakNetGuid, _FILE_AND_LINE_); -} -PluginReceiveResult FullyConnectedMesh2::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_REMOTE_NEW_INCOMING_CONNECTION: - { - if (connectOnNewRemoteConnections) - ConnectToRemoteNewIncomingConnections(packet); - } - break; - case ID_FCM2_REQUEST_FCMGUID: - OnRequestFCMGuid(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FCM2_RESPOND_CONNECTION_COUNT: - OnRespondConnectionCount(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FCM2_INFORM_FCMGUID: - OnInformFCMGuid(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT: - OnUpdateMinTotalConnectionCount(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_FCM2_NEW_HOST: - if (packet->wasGeneratedLocally==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - break; - } - return RR_CONTINUE_PROCESSING; -} -void FullyConnectedMesh2::OnRakPeerStartup(void) -{ - Clear(); - startupTime=RakNet::GetTimeUS(); -} -void FullyConnectedMesh2::OnAttach(void) -{ - Clear(); - // In case Startup() was called first - if (rakPeerInterface->IsActive()) - startupTime=RakNet::GetTimeUS(); -} -void FullyConnectedMesh2::OnRakPeerShutdown(void) -{ - Clear(); - startupTime=0; -} -void FullyConnectedMesh2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - (void) rakNetGUID; - - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - { - if (fcm2ParticipantList[idx].rakNetGuid==rakNetGUID) - { - fcm2ParticipantList[idx]=fcm2ParticipantList[fcm2ParticipantList.Size()-1]; -#ifdef DEBUG_FCM2 - printf("Popping participant %s\n", fcm2ParticipantList[fcm2ParticipantList.Size()-1].rakNetGuid.ToString()); -#endif - - fcm2ParticipantList.Pop(); - if (rakNetGUID==hostRakNetGuid && ourFCMGuid!=0) - { - if (fcm2ParticipantList.Size()==0) - { - hostRakNetGuid=rakPeerInterface->GetMyGUID(); - hostFCM2Guid=ourFCMGuid; - } - else - { - CalculateHost(&hostRakNetGuid, &hostFCM2Guid); - } - PushNewHost(hostRakNetGuid, rakNetGUID); - } - return; - } - } - -} -RakNet::TimeUS FullyConnectedMesh2::GetElapsedRuntime(void) -{ - RakNet::TimeUS curTime=RakNet::GetTimeUS(); - if (curTime>startupTime) - return curTime-startupTime; - else - return 0; -} -void FullyConnectedMesh2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) isIncoming; - (void) rakNetGUID; - (void) systemAddress; - - if (autoParticipateConnections) - AddParticipant(rakNetGUID); -} -void FullyConnectedMesh2::Clear(void) -{ - fcm2ParticipantList.Clear(false, _FILE_AND_LINE_); - - totalConnectionCount=0; - ourFCMGuid=0; - lastPushedHost=UNASSIGNED_RAKNET_GUID; -} -void FullyConnectedMesh2::PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(oldHost)); - RakNet::BitStream bs(p->data,p->length,false); - bs.SetWriteOffset(0); - bs.Write((MessageID)ID_FCM2_NEW_HOST); - bs.Write(oldHost); - p->systemAddress=rakPeerInterface->GetSystemAddressFromGuid(guid); - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=guid; - p->wasGeneratedLocally=true; - rakPeerInterface->PushBackPacket(p, true); - - lastPushedHost=guid; -} -void FullyConnectedMesh2::SendFCMGuidRequest(RakNetGUID rakNetGuid) -{ - if (rakNetGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return; - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_FCM2_REQUEST_FCMGUID); - if (ourFCMGuid==0) - { - bsOut.Write(false); - bsOut.Write(GetElapsedRuntime()); - } - else - { - bsOut.Write(true); - bsOut.Write(totalConnectionCount); - bsOut.Write(ourFCMGuid); - } - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,rakNetGuid,false); -} -void FullyConnectedMesh2::SendOurFCMGuid(SystemAddress addr) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_FCM2_INFORM_FCMGUID); - RakAssert(ourFCMGuid!=0); // Can't inform others of our FCM2Guid if it's unset! - bsOut.Write(ourFCMGuid); - bsOut.Write(totalConnectionCount); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false); -} -void FullyConnectedMesh2::SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_FCM2_RESPOND_CONNECTION_COUNT); - bsOut.Write(responseTotalConnectionCount); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false); -} -void FullyConnectedMesh2::AssignOurFCMGuid(void) -{ - // Only assigned once ever - RakAssert(ourFCMGuid==0); - unsigned int randomNumber = randomMT(); - randomNumber ^= (unsigned int) (RakNet::GetTimeUS() & 0xFFFFFFFF); - randomNumber ^= (unsigned int) (rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).g & 0xFFFFFFFF); - ourFCMGuid |= randomNumber; - uint64_t reponse64 = totalConnectionCount; - ourFCMGuid |= reponse64<<32; -} -void FullyConnectedMesh2::CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid) -{ - // Can't calculate host without knowing our own - RakAssert(ourFCMGuid!=0); - - // Can't calculate host without being connected to anyone else - RakAssert(fcm2ParticipantList.Size()>0); - - // Return the lowest value of all FCM2Guid - FCM2Guid lowestFCMGuid=ourFCMGuid; - // SystemAddress associatedSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; - RakNetGUID associatedRakNetGuid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - { - if (fcm2ParticipantList[idx].fcm2Guid!=0 && fcm2ParticipantList[idx].fcm2Guiddata,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bool hasRemoteFCMGuid=false; - bsIn.Read(hasRemoteFCMGuid); - RakNet::TimeUS senderElapsedRuntime=0; - unsigned int remoteTotalConnectionCount=0; - FCM2Guid theirFCMGuid=0; - if (hasRemoteFCMGuid) - { - bsIn.Read(remoteTotalConnectionCount); - bsIn.Read(theirFCMGuid); - } - else - { - bsIn.Read(senderElapsedRuntime); - } - AddParticipantInternal(packet->guid,theirFCMGuid); - if (ourFCMGuid==0) - { - if (hasRemoteFCMGuid==false) - { - // Nobody has a fcmGuid - - RakNet::TimeUS ourElapsedRuntime = GetElapsedRuntime(); - if (ourElapsedRuntime>senderElapsedRuntime) - { - // We are probably host - SendConnectionCountResponse(packet->systemAddress, 2); - } - else - { - // They are probably host - SendConnectionCountResponse(packet->systemAddress, 1); - } - } - else - { - // They have a fcmGuid, we do not - IncrementTotalConnectionCount(remoteTotalConnectionCount+1); - - AssignOurFCMGuid(); - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); - } - } - else - { - if (hasRemoteFCMGuid==false) - { - // We have a fcmGuid they do not - SendConnectionCountResponse(packet->systemAddress, totalConnectionCount+1); - } - else - { - // We both have fcmGuids - IncrementTotalConnectionCount(remoteTotalConnectionCount); - - SendOurFCMGuid(packet->systemAddress); - } - } - CalculateAndPushHost(); -} -void FullyConnectedMesh2::OnRespondConnectionCount(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - unsigned int responseTotalConnectionCount; - bsIn.Read(responseTotalConnectionCount); - IncrementTotalConnectionCount(responseTotalConnectionCount); - bool wasAssigned; - if (ourFCMGuid==0) - { - wasAssigned=true; - AssignOurFCMGuid(); - } - else - wasAssigned=false; - - // 1 is returned to give us lower priority, but the actual minimum is 2 - IncrementTotalConnectionCount(2); - - if (wasAssigned==true) - { - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); - CalculateAndPushHost(); - } -} -void FullyConnectedMesh2::OnInformFCMGuid(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - - FCM2Guid theirFCMGuid; - unsigned int theirTotalConnectionCount; - bsIn.Read(theirFCMGuid); - bsIn.Read(theirTotalConnectionCount); - IncrementTotalConnectionCount(theirTotalConnectionCount); - - if (AddParticipantInternal(packet->guid,theirFCMGuid)) - { - // 1/19/2010 - Relay increased total connection count in case new participant only connects to part of the mesh - unsigned int idx; - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT); - bsOut.Write(totalConnectionCount); - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - { - if (packet->guid!=fcm2ParticipantList[idx].rakNetGuid) - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,fcm2ParticipantList[idx].rakNetGuid,false); - } - } - - if (ourFCMGuid==0) - { - AssignOurFCMGuid(); - unsigned int idx; - for (idx=0; idx < fcm2ParticipantList.Size(); idx++) - SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); - } - - CalculateAndPushHost(); -} -void FullyConnectedMesh2::OnUpdateMinTotalConnectionCount(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - unsigned int newMin; - bsIn.Read(newMin); - IncrementTotalConnectionCount(newMin); -} -void FullyConnectedMesh2::GetParticipantCount(unsigned int *participantListSize) const -{ - *participantListSize=fcm2ParticipantList.Size(); -} - -unsigned int FullyConnectedMesh2::GetParticipantCount(void) const -{ - return fcm2ParticipantList.Size(); -} -void FullyConnectedMesh2::CalculateAndPushHost(void) -{ - RakNetGUID newHostGuid; - FCM2Guid newFcmGuid; - if (ParticipantListComplete()) - { - CalculateHost(&newHostGuid, &newFcmGuid); - if (newHostGuid!=lastPushedHost) - { - hostRakNetGuid=newHostGuid; - hostFCM2Guid=newFcmGuid; - PushNewHost(hostRakNetGuid, hostRakNetGuid); - } - } -} -bool FullyConnectedMesh2::ParticipantListComplete(void) -{ - for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) - { - if (fcm2ParticipantList[i].fcm2Guid==0) - return false; - } - return true; -} -void FullyConnectedMesh2::IncrementTotalConnectionCount(unsigned int i) -{ - if (i>totalConnectionCount) - { - totalConnectionCount=i; - // printf("totalConnectionCount=%i\n",i); - } -} -void FullyConnectedMesh2::SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw) -{ - connectOnNewRemoteConnections=attemptConnection; - connectionPassword=pw; -} - -void FullyConnectedMesh2::ConnectToRemoteNewIncomingConnections(Packet *packet) -{ - unsigned int count; - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bsIn.Read(count); - SystemAddress remoteAddress; - RakNetGUID remoteGuid; - char str[64]; - for (unsigned int i=0; i < count; i++) - { - bsIn.Read(remoteAddress); - bsIn.Read(remoteGuid); - remoteAddress.ToString(false,str); - rakPeerInterface->Connect(str,remoteAddress.GetPort(),connectionPassword.C_String(),(int) connectionPassword.GetLength()); - } -} -unsigned int FullyConnectedMesh2::GetTotalConnectionCount(void) const -{ - return totalConnectionCount; -} - -#endif // _RAKNET_SUPPORT_* +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FullyConnectedMesh2==1 + +#include "FullyConnectedMesh2.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" +#include "RakAssert.h" +#include "GetTime.h" +#include "Rand.h" +#include "DS_OrderedList.h" + +using namespace RakNet; + +int FCM2ParticipantComp( const FullyConnectedMesh2::FCM2Participant &key, const FullyConnectedMesh2::FCM2Participant &data ) +{ + if (key.fcm2Guid < data.fcm2Guid) + return -1; + if (key.fcm2Guid > data.fcm2Guid) + return 1; + return 0; +} + +STATIC_FACTORY_DEFINITIONS(FullyConnectedMesh2,FullyConnectedMesh2); + +FullyConnectedMesh2::FullyConnectedMesh2() +{ + startupTime=0; + totalConnectionCount=0; + ourFCMGuid=0; + autoParticipateConnections=true; + + + + + connectOnNewRemoteConnections=true; + + hostRakNetGuid=UNASSIGNED_RAKNET_GUID; +} +FullyConnectedMesh2::~FullyConnectedMesh2() +{ + Clear(); +} +RakNetGUID FullyConnectedMesh2::GetConnectedHost(void) const +{ + if (ourFCMGuid==0) + return UNASSIGNED_RAKNET_GUID; + return hostRakNetGuid; +} +SystemAddress FullyConnectedMesh2::GetConnectedHostAddr(void) const +{ + if (ourFCMGuid==0) + return UNASSIGNED_SYSTEM_ADDRESS; + return rakPeerInterface->GetSystemAddressFromGuid(hostRakNetGuid); +} +RakNetGUID FullyConnectedMesh2::GetHostSystem(void) const +{ + if (ourFCMGuid==0) + return rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + + return hostRakNetGuid; +} +bool FullyConnectedMesh2::IsHostSystem(void) const +{ + return GetHostSystem()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +} +void FullyConnectedMesh2::GetHostOrder(DataStructures::List &hostList) +{ + hostList.Clear(true, _FILE_AND_LINE_); + + if (ourFCMGuid==0 || fcm2ParticipantList.Size()==0) + { + hostList.Push(rakPeerInterface->GetMyGUID(), _FILE_AND_LINE_); + return; + } + + FCM2Participant fcm2; + fcm2.fcm2Guid=ourFCMGuid; + fcm2.rakNetGuid=rakPeerInterface->GetMyGUID(); + + DataStructures::OrderedList olist; + olist.Insert(fcm2, fcm2, true, _FILE_AND_LINE_); + for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) + olist.Insert(fcm2ParticipantList[i], fcm2ParticipantList[i], true, _FILE_AND_LINE_); + + for (unsigned int i=0; i < olist.Size(); i++) + { + hostList.Push(olist[i].rakNetGuid, _FILE_AND_LINE_); + } +} +bool FullyConnectedMesh2::IsConnectedHost(void) const +{ + return GetConnectedHost()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +} +void FullyConnectedMesh2::SetAutoparticipateConnections(bool b) +{ + autoParticipateConnections=b; +} +void FullyConnectedMesh2::ResetHostCalculation(void) +{ + hostRakNetGuid=UNASSIGNED_RAKNET_GUID; + startupTime=RakNet::GetTimeUS(); + totalConnectionCount=0; + ourFCMGuid=0; + for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) + SendFCMGuidRequest(fcm2ParticipantList[i].rakNetGuid); +} +bool FullyConnectedMesh2::AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid ) +{ + for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) + { + if (fcm2ParticipantList[i].rakNetGuid==rakNetGuid) + { + if (theirFCMGuid!=0) + fcm2ParticipantList[i].fcm2Guid=theirFCMGuid; + return false; + } + } + + FCM2Participant participant; + participant.rakNetGuid=rakNetGuid; + participant.fcm2Guid=theirFCMGuid; + fcm2ParticipantList.Push(participant,_FILE_AND_LINE_); + + SendFCMGuidRequest(rakNetGuid); + + return true; +} +void FullyConnectedMesh2::AddParticipant( RakNetGUID rakNetGuid ) +{ + if (rakPeerInterface->GetConnectionState(rakPeerInterface->GetSystemAddressFromGuid(rakNetGuid))!=IS_CONNECTED) + { +#ifdef DEBUG_FCM2 + printf("AddParticipant to %s failed (not connected)\n", rakNetGuid.ToString()); +#endif + return; + } + + AddParticipantInternal(rakNetGuid,0); +} +void FullyConnectedMesh2::GetParticipantList(DataStructures::List &participantList) +{ + participantList.Clear(true, _FILE_AND_LINE_); + unsigned int i; + for (i=0; i < fcm2ParticipantList.Size(); i++) + participantList.Push(fcm2ParticipantList[i].rakNetGuid, _FILE_AND_LINE_); +} +PluginReceiveResult FullyConnectedMesh2::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_REMOTE_NEW_INCOMING_CONNECTION: + { + if (connectOnNewRemoteConnections) + ConnectToRemoteNewIncomingConnections(packet); + } + break; + case ID_FCM2_REQUEST_FCMGUID: + OnRequestFCMGuid(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FCM2_RESPOND_CONNECTION_COUNT: + OnRespondConnectionCount(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FCM2_INFORM_FCMGUID: + OnInformFCMGuid(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT: + OnUpdateMinTotalConnectionCount(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_FCM2_NEW_HOST: + if (packet->wasGeneratedLocally==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + break; + } + return RR_CONTINUE_PROCESSING; +} +void FullyConnectedMesh2::OnRakPeerStartup(void) +{ + Clear(); + startupTime=RakNet::GetTimeUS(); +} +void FullyConnectedMesh2::OnAttach(void) +{ + Clear(); + // In case Startup() was called first + if (rakPeerInterface->IsActive()) + startupTime=RakNet::GetTimeUS(); +} +void FullyConnectedMesh2::OnRakPeerShutdown(void) +{ + Clear(); + startupTime=0; +} +void FullyConnectedMesh2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + (void) rakNetGUID; + + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + { + if (fcm2ParticipantList[idx].rakNetGuid==rakNetGUID) + { + fcm2ParticipantList[idx]=fcm2ParticipantList[fcm2ParticipantList.Size()-1]; +#ifdef DEBUG_FCM2 + printf("Popping participant %s\n", fcm2ParticipantList[fcm2ParticipantList.Size()-1].rakNetGuid.ToString()); +#endif + + fcm2ParticipantList.Pop(); + if (rakNetGUID==hostRakNetGuid && ourFCMGuid!=0) + { + if (fcm2ParticipantList.Size()==0) + { + hostRakNetGuid=rakPeerInterface->GetMyGUID(); + hostFCM2Guid=ourFCMGuid; + } + else + { + CalculateHost(&hostRakNetGuid, &hostFCM2Guid); + } + PushNewHost(hostRakNetGuid, rakNetGUID); + } + return; + } + } + +} +RakNet::TimeUS FullyConnectedMesh2::GetElapsedRuntime(void) +{ + RakNet::TimeUS curTime=RakNet::GetTimeUS(); + if (curTime>startupTime) + return curTime-startupTime; + else + return 0; +} +void FullyConnectedMesh2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) isIncoming; + (void) rakNetGUID; + (void) systemAddress; + + if (autoParticipateConnections) + AddParticipant(rakNetGUID); +} +void FullyConnectedMesh2::Clear(void) +{ + fcm2ParticipantList.Clear(false, _FILE_AND_LINE_); + + totalConnectionCount=0; + ourFCMGuid=0; + lastPushedHost=UNASSIGNED_RAKNET_GUID; +} +void FullyConnectedMesh2::PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(oldHost)); + RakNet::BitStream bs(p->data,p->length,false); + bs.SetWriteOffset(0); + bs.Write((MessageID)ID_FCM2_NEW_HOST); + bs.Write(oldHost); + p->systemAddress=rakPeerInterface->GetSystemAddressFromGuid(guid); + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=guid; + p->wasGeneratedLocally=true; + rakPeerInterface->PushBackPacket(p, true); + + lastPushedHost=guid; +} +void FullyConnectedMesh2::SendFCMGuidRequest(RakNetGUID rakNetGuid) +{ + if (rakNetGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return; + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_FCM2_REQUEST_FCMGUID); + if (ourFCMGuid==0) + { + bsOut.Write(false); + bsOut.Write(GetElapsedRuntime()); + } + else + { + bsOut.Write(true); + bsOut.Write(totalConnectionCount); + bsOut.Write(ourFCMGuid); + } + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,rakNetGuid,false); +} +void FullyConnectedMesh2::SendOurFCMGuid(SystemAddress addr) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_FCM2_INFORM_FCMGUID); + RakAssert(ourFCMGuid!=0); // Can't inform others of our FCM2Guid if it's unset! + bsOut.Write(ourFCMGuid); + bsOut.Write(totalConnectionCount); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false); +} +void FullyConnectedMesh2::SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_FCM2_RESPOND_CONNECTION_COUNT); + bsOut.Write(responseTotalConnectionCount); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false); +} +void FullyConnectedMesh2::AssignOurFCMGuid(void) +{ + // Only assigned once ever + RakAssert(ourFCMGuid==0); + unsigned int randomNumber = randomMT(); + randomNumber ^= (unsigned int) (RakNet::GetTimeUS() & 0xFFFFFFFF); + randomNumber ^= (unsigned int) (rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).g & 0xFFFFFFFF); + ourFCMGuid |= randomNumber; + uint64_t reponse64 = totalConnectionCount; + ourFCMGuid |= reponse64<<32; +} +void FullyConnectedMesh2::CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid) +{ + // Can't calculate host without knowing our own + RakAssert(ourFCMGuid!=0); + + // Can't calculate host without being connected to anyone else + RakAssert(fcm2ParticipantList.Size()>0); + + // Return the lowest value of all FCM2Guid + FCM2Guid lowestFCMGuid=ourFCMGuid; + // SystemAddress associatedSystemAddress=UNASSIGNED_SYSTEM_ADDRESS; + RakNetGUID associatedRakNetGuid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + { + if (fcm2ParticipantList[idx].fcm2Guid!=0 && fcm2ParticipantList[idx].fcm2Guiddata,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bool hasRemoteFCMGuid=false; + bsIn.Read(hasRemoteFCMGuid); + RakNet::TimeUS senderElapsedRuntime=0; + unsigned int remoteTotalConnectionCount=0; + FCM2Guid theirFCMGuid=0; + if (hasRemoteFCMGuid) + { + bsIn.Read(remoteTotalConnectionCount); + bsIn.Read(theirFCMGuid); + } + else + { + bsIn.Read(senderElapsedRuntime); + } + AddParticipantInternal(packet->guid,theirFCMGuid); + if (ourFCMGuid==0) + { + if (hasRemoteFCMGuid==false) + { + // Nobody has a fcmGuid + + RakNet::TimeUS ourElapsedRuntime = GetElapsedRuntime(); + if (ourElapsedRuntime>senderElapsedRuntime) + { + // We are probably host + SendConnectionCountResponse(packet->systemAddress, 2); + } + else + { + // They are probably host + SendConnectionCountResponse(packet->systemAddress, 1); + } + } + else + { + // They have a fcmGuid, we do not + IncrementTotalConnectionCount(remoteTotalConnectionCount+1); + + AssignOurFCMGuid(); + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); + } + } + else + { + if (hasRemoteFCMGuid==false) + { + // We have a fcmGuid they do not + SendConnectionCountResponse(packet->systemAddress, totalConnectionCount+1); + } + else + { + // We both have fcmGuids + IncrementTotalConnectionCount(remoteTotalConnectionCount); + + SendOurFCMGuid(packet->systemAddress); + } + } + CalculateAndPushHost(); +} +void FullyConnectedMesh2::OnRespondConnectionCount(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + unsigned int responseTotalConnectionCount; + bsIn.Read(responseTotalConnectionCount); + IncrementTotalConnectionCount(responseTotalConnectionCount); + bool wasAssigned; + if (ourFCMGuid==0) + { + wasAssigned=true; + AssignOurFCMGuid(); + } + else + wasAssigned=false; + + // 1 is returned to give us lower priority, but the actual minimum is 2 + IncrementTotalConnectionCount(2); + + if (wasAssigned==true) + { + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); + CalculateAndPushHost(); + } +} +void FullyConnectedMesh2::OnInformFCMGuid(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + + FCM2Guid theirFCMGuid; + unsigned int theirTotalConnectionCount; + bsIn.Read(theirFCMGuid); + bsIn.Read(theirTotalConnectionCount); + IncrementTotalConnectionCount(theirTotalConnectionCount); + + if (AddParticipantInternal(packet->guid,theirFCMGuid)) + { + // 1/19/2010 - Relay increased total connection count in case new participant only connects to part of the mesh + unsigned int idx; + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT); + bsOut.Write(totalConnectionCount); + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + { + if (packet->guid!=fcm2ParticipantList[idx].rakNetGuid) + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,fcm2ParticipantList[idx].rakNetGuid,false); + } + } + + if (ourFCMGuid==0) + { + AssignOurFCMGuid(); + unsigned int idx; + for (idx=0; idx < fcm2ParticipantList.Size(); idx++) + SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid)); + } + + CalculateAndPushHost(); +} +void FullyConnectedMesh2::OnUpdateMinTotalConnectionCount(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + unsigned int newMin; + bsIn.Read(newMin); + IncrementTotalConnectionCount(newMin); +} +void FullyConnectedMesh2::GetParticipantCount(unsigned int *participantListSize) const +{ + *participantListSize=fcm2ParticipantList.Size(); +} + +unsigned int FullyConnectedMesh2::GetParticipantCount(void) const +{ + return fcm2ParticipantList.Size(); +} +void FullyConnectedMesh2::CalculateAndPushHost(void) +{ + RakNetGUID newHostGuid; + FCM2Guid newFcmGuid; + if (ParticipantListComplete()) + { + CalculateHost(&newHostGuid, &newFcmGuid); + if (newHostGuid!=lastPushedHost) + { + hostRakNetGuid=newHostGuid; + hostFCM2Guid=newFcmGuid; + PushNewHost(hostRakNetGuid, hostRakNetGuid); + } + } +} +bool FullyConnectedMesh2::ParticipantListComplete(void) +{ + for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++) + { + if (fcm2ParticipantList[i].fcm2Guid==0) + return false; + } + return true; +} +void FullyConnectedMesh2::IncrementTotalConnectionCount(unsigned int i) +{ + if (i>totalConnectionCount) + { + totalConnectionCount=i; + // printf("totalConnectionCount=%i\n",i); + } +} +void FullyConnectedMesh2::SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw) +{ + connectOnNewRemoteConnections=attemptConnection; + connectionPassword=pw; +} + +void FullyConnectedMesh2::ConnectToRemoteNewIncomingConnections(Packet *packet) +{ + unsigned int count; + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bsIn.Read(count); + SystemAddress remoteAddress; + RakNetGUID remoteGuid; + char str[64]; + for (unsigned int i=0; i < count; i++) + { + bsIn.Read(remoteAddress); + bsIn.Read(remoteGuid); + remoteAddress.ToString(false,str); + rakPeerInterface->Connect(str,remoteAddress.GetPort(),connectionPassword.C_String(),(int) connectionPassword.GetLength()); + } +} +unsigned int FullyConnectedMesh2::GetTotalConnectionCount(void) const +{ + return totalConnectionCount; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/FullyConnectedMesh2.h b/src/raknet/FullyConnectedMesh2.h index f3859ea..a8f94b1 100755 --- a/src/raknet/FullyConnectedMesh2.h +++ b/src/raknet/FullyConnectedMesh2.h @@ -1,253 +1,253 @@ -/// \file FullyConnectedMesh2.h -/// \brief Fully connected mesh plugin, revision 2. -/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_FullyConnectedMesh2==1 - -#ifndef __FULLY_CONNECTED_MESH_2_H -#define __FULLY_CONNECTED_MESH_2_H - -#include "PluginInterface2.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" -#include "DS_List.h" -#include "RakString.h" - -typedef int64_t FCM2Guid; - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \brief Fully connected mesh plugin, revision 2 -/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.
      -/// It will also calculate which system has been running longest, to find out who should be host, if you need one system to act as a host -/// \pre You must also install the ConnectionGraph2 plugin in order to use SetConnectOnNewRemoteConnection() -/// \ingroup FULLY_CONNECTED_MESH_GROUP -class RAK_DLL_EXPORT FullyConnectedMesh2 : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(FullyConnectedMesh2) - - FullyConnectedMesh2(); - virtual ~FullyConnectedMesh2(); - - /// When the message ID_REMOTE_NEW_INCOMING_CONNECTION arrives, we try to connect to that system - /// If \a attemptConnection is false, you can manually connect to all systems listed in ID_REMOTE_NEW_INCOMING_CONNECTION with ConnectToRemoteNewIncomingConnections() - /// \note This will not work on any console. It will also not work if NAT punchthrough is needed. Generally, this should be false and you should connect manually. It is here for legacy reasons. - /// \param[in] attemptConnection If true, we try to connect to any systems we are notified about with ID_REMOTE_NEW_INCOMING_CONNECTION, which comes from the ConnectionGraph2 plugin. Defaults to true. - /// \param[in] pw The password to use to connect with. Only used if \a attemptConnection is true - void SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw); - - /// \brief The connected host is whichever system we are connected to that has been running the longest. - /// \details Will return UNASSIGNED_RAKNET_GUID if we are not connected to anyone, or if we are connected and are calculating the host - /// If includeCalculating is true, will return the estimated calculated host as long as the calculation is nearly complete - /// includeCalculating should be true if you are taking action based on another system becoming host, because not all host calculations may complete at the exact same time - /// \sa ConnectionGraph2::GetLowestAveragePingSystem() . If you need one system in the peer to peer group to relay data, have the host call this function after host migration, and use that system - /// \return System address of whichever system is host. - RakNetGUID GetConnectedHost(void) const; - SystemAddress GetConnectedHostAddr(void) const; - - /// \return System address of whichever system is host. Always returns something, even though it may be our own system. - RakNetGUID GetHostSystem(void) const; - - /// \return If our system is host - bool IsHostSystem(void) const; - - /// Get the list of connected systems, from oldest connected to newest - /// This is also the order that the hosts will be chosen in - void GetHostOrder(DataStructures::List &hostList); - - /// \param[in] includeCalculating If true, and we are currently calculating a new host, return the new host if the calculation is nearly complete - /// \return If our system is host - bool IsConnectedHost(void) const; - - /// \brief Automatically add new connections to the fully connected mesh. - /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function - /// \details Defaults to true. - /// \param[in] b As stated - void SetAutoparticipateConnections(bool b); - - /// Clear our own host order, and recalculate as if we had just reconnected - /// Call this to reset the running time of the host just before joining/creating a game room for networking - void ResetHostCalculation(void); - - /// \brief if SetAutoparticipateConnections() is called with false, then you need to use AddParticipant before these systems will be added to the mesh - /// FullyConnectedMesh2 will track who is the who host among a fully connected mesh of participants - /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function - /// \param[in] participant The new participant - void AddParticipant(RakNetGUID rakNetGuid); - - /// Get the participants added with AddParticipant() - /// \param[out] participantList Participants added with AddParticipant(); - void GetParticipantList(DataStructures::List &participantList); - - /// Connect to all systems from ID_REMOTE_NEW_INCOMING_CONNECTION - /// You can call this if SetConnectOnNewRemoteConnection is false - /// \param[in] packet The packet containing ID_REMOTE_NEW_INCOMING_CONNECTION - /// \param[in] connectionPassword Password passed to RakPeerInterface::Connect() - /// \param[in] connectionPasswordLength Password length passed to RakPeerInterface::Connect() - void ConnectToRemoteNewIncomingConnections(Packet *packet); - - /// \brief Clear all memory and reset everything - void Clear(void); - - unsigned int GetParticipantCount(void) const; - void GetParticipantCount(unsigned int *participantListSize) const; - /// \internal - RakNet::TimeUS GetElapsedRuntime(void); - - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal - virtual void OnRakPeerStartup(void); - /// \internal - virtual void OnAttach(void); - /// \internal - virtual void OnRakPeerShutdown(void); - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - - /// \internal - struct FCM2Participant - { - FCM2Participant() {} - FCM2Participant(const FCM2Guid &_fcm2Guid, const RakNetGUID &_rakNetGuid) : fcm2Guid(_fcm2Guid), rakNetGuid(_rakNetGuid) {} - - // Low half is a random number. - // High half is the order we connected in (totalConnectionCount) - FCM2Guid fcm2Guid; - RakNetGUID rakNetGuid; - }; - - /// \internal for debugging - unsigned int GetTotalConnectionCount(void) const; - -protected: - void PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost); - void SendOurFCMGuid(SystemAddress addr); - void SendFCMGuidRequest(RakNetGUID rakNetGuid); - void SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount); - void OnRequestFCMGuid(Packet *packet); - void OnRespondConnectionCount(Packet *packet); - void OnInformFCMGuid(Packet *packet); - void OnUpdateMinTotalConnectionCount(Packet *packet); - void AssignOurFCMGuid(void); - void CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid); - bool AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid ); - void CalculateAndPushHost(void); - bool ParticipantListComplete(void); - void IncrementTotalConnectionCount(unsigned int i); - - // Used to track how long RakNet has been running. This is so we know who has been running longest - RakNet::TimeUS startupTime; - - // Option for SetAutoparticipateConnections - bool autoParticipateConnections; - - // totalConnectionCount is roughly maintained across all systems, and increments by 1 each time a new system connects to the mesh - // It is always kept at the highest known value - // It is used as the high 4 bytes for new FCMGuids. This causes newer values of FCM2Guid to be higher than lower values. The lowest value is the host. - unsigned int totalConnectionCount; - - // Our own ourFCMGuid. Starts at unassigned (0). Assigned once we send ID_FCM2_REQUEST_FCMGUID and get back ID_FCM2_RESPOND_CONNECTION_COUNT - FCM2Guid ourFCMGuid; - - /// List of systems we know the FCM2Guid for - DataStructures::List fcm2ParticipantList; - - RakNetGUID lastPushedHost; - - // Optimization: Store last calculated host in these variables. - RakNetGUID hostRakNetGuid; - FCM2Guid hostFCM2Guid; - - RakNet::RakString connectionPassword; - bool connectOnNewRemoteConnections; -}; - -} // namespace RakNet - -/* -Startup() -ourFCMGuid=unknown -totalConnectionCount=0 -Set startupTime - -AddParticipant() -if (sender by guid is a participant) -return; -AddParticipantInternal(guid); -if (ourFCMGuid==unknown) -Send to that system a request for their fcmGuid, totalConnectionCount. Inform startupTime. -else -Send to that system a request for their fcmGuid. Inform total connection count, our fcmGuid - -OnRequestGuid() -if (sender by guid is not a participant) -{ - // They added us as a participant, but we didn't add them. This can be caused by lag where both participants are not added at the same time. - // It doesn't affect the outcome as long as we still process the data - AddParticipantInternal(guid); -} -if (ourFCMGuid==unknown) -{ - if (includedStartupTime) - { - // Nobody has a fcmGuid - - if (their startup time is greater than our startup time) - ReplyConnectionCount(1); - else - ReplyConnectionCount(2); - } - else - { - // They have a fcmGuid, we do not - - SetMaxTotalConnectionCount(remoteCount); - AssignTheirGuid() - GenerateOurGuid(); - SendOurGuid(all); - } -} -else -{ - if (includedStartupTime) - { - // We have a fcmGuid they do not - - ReplyConnectionCount(totalConnectionCount+1); - SendOurGuid(sender); - } - else - { - // We both have fcmGuids - - SetMaxTotalConnectionCount(remoteCount); - AssignTheirGuid(); - SendOurGuid(sender); - } -} - -OnReplyConnectionCount() -SetMaxTotalConnectionCount(remoteCount); -GenerateOurGuid(); -SendOurGuid(allParticipants); - -OnReceiveTheirGuid() -AssignTheirGuid() -*/ - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file FullyConnectedMesh2.h +/// \brief Fully connected mesh plugin, revision 2. +/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_FullyConnectedMesh2==1 + +#ifndef __FULLY_CONNECTED_MESH_2_H +#define __FULLY_CONNECTED_MESH_2_H + +#include "PluginInterface2.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" +#include "DS_List.h" +#include "RakString.h" + +typedef int64_t FCM2Guid; + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief Fully connected mesh plugin, revision 2 +/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.
      +/// It will also calculate which system has been running longest, to find out who should be host, if you need one system to act as a host +/// \pre You must also install the ConnectionGraph2 plugin in order to use SetConnectOnNewRemoteConnection() +/// \ingroup FULLY_CONNECTED_MESH_GROUP +class RAK_DLL_EXPORT FullyConnectedMesh2 : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(FullyConnectedMesh2) + + FullyConnectedMesh2(); + virtual ~FullyConnectedMesh2(); + + /// When the message ID_REMOTE_NEW_INCOMING_CONNECTION arrives, we try to connect to that system + /// If \a attemptConnection is false, you can manually connect to all systems listed in ID_REMOTE_NEW_INCOMING_CONNECTION with ConnectToRemoteNewIncomingConnections() + /// \note This will not work on any console. It will also not work if NAT punchthrough is needed. Generally, this should be false and you should connect manually. It is here for legacy reasons. + /// \param[in] attemptConnection If true, we try to connect to any systems we are notified about with ID_REMOTE_NEW_INCOMING_CONNECTION, which comes from the ConnectionGraph2 plugin. Defaults to true. + /// \param[in] pw The password to use to connect with. Only used if \a attemptConnection is true + void SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw); + + /// \brief The connected host is whichever system we are connected to that has been running the longest. + /// \details Will return UNASSIGNED_RAKNET_GUID if we are not connected to anyone, or if we are connected and are calculating the host + /// If includeCalculating is true, will return the estimated calculated host as long as the calculation is nearly complete + /// includeCalculating should be true if you are taking action based on another system becoming host, because not all host calculations may complete at the exact same time + /// \sa ConnectionGraph2::GetLowestAveragePingSystem() . If you need one system in the peer to peer group to relay data, have the host call this function after host migration, and use that system + /// \return System address of whichever system is host. + RakNetGUID GetConnectedHost(void) const; + SystemAddress GetConnectedHostAddr(void) const; + + /// \return System address of whichever system is host. Always returns something, even though it may be our own system. + RakNetGUID GetHostSystem(void) const; + + /// \return If our system is host + bool IsHostSystem(void) const; + + /// Get the list of connected systems, from oldest connected to newest + /// This is also the order that the hosts will be chosen in + void GetHostOrder(DataStructures::List &hostList); + + /// \param[in] includeCalculating If true, and we are currently calculating a new host, return the new host if the calculation is nearly complete + /// \return If our system is host + bool IsConnectedHost(void) const; + + /// \brief Automatically add new connections to the fully connected mesh. + /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function + /// \details Defaults to true. + /// \param[in] b As stated + void SetAutoparticipateConnections(bool b); + + /// Clear our own host order, and recalculate as if we had just reconnected + /// Call this to reset the running time of the host just before joining/creating a game room for networking + void ResetHostCalculation(void); + + /// \brief if SetAutoparticipateConnections() is called with false, then you need to use AddParticipant before these systems will be added to the mesh + /// FullyConnectedMesh2 will track who is the who host among a fully connected mesh of participants + /// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function + /// \param[in] participant The new participant + void AddParticipant(RakNetGUID rakNetGuid); + + /// Get the participants added with AddParticipant() + /// \param[out] participantList Participants added with AddParticipant(); + void GetParticipantList(DataStructures::List &participantList); + + /// Connect to all systems from ID_REMOTE_NEW_INCOMING_CONNECTION + /// You can call this if SetConnectOnNewRemoteConnection is false + /// \param[in] packet The packet containing ID_REMOTE_NEW_INCOMING_CONNECTION + /// \param[in] connectionPassword Password passed to RakPeerInterface::Connect() + /// \param[in] connectionPasswordLength Password length passed to RakPeerInterface::Connect() + void ConnectToRemoteNewIncomingConnections(Packet *packet); + + /// \brief Clear all memory and reset everything + void Clear(void); + + unsigned int GetParticipantCount(void) const; + void GetParticipantCount(unsigned int *participantListSize) const; + /// \internal + RakNet::TimeUS GetElapsedRuntime(void); + + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal + virtual void OnRakPeerStartup(void); + /// \internal + virtual void OnAttach(void); + /// \internal + virtual void OnRakPeerShutdown(void); + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + + /// \internal + struct FCM2Participant + { + FCM2Participant() {} + FCM2Participant(const FCM2Guid &_fcm2Guid, const RakNetGUID &_rakNetGuid) : fcm2Guid(_fcm2Guid), rakNetGuid(_rakNetGuid) {} + + // Low half is a random number. + // High half is the order we connected in (totalConnectionCount) + FCM2Guid fcm2Guid; + RakNetGUID rakNetGuid; + }; + + /// \internal for debugging + unsigned int GetTotalConnectionCount(void) const; + +protected: + void PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost); + void SendOurFCMGuid(SystemAddress addr); + void SendFCMGuidRequest(RakNetGUID rakNetGuid); + void SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount); + void OnRequestFCMGuid(Packet *packet); + void OnRespondConnectionCount(Packet *packet); + void OnInformFCMGuid(Packet *packet); + void OnUpdateMinTotalConnectionCount(Packet *packet); + void AssignOurFCMGuid(void); + void CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid); + bool AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid ); + void CalculateAndPushHost(void); + bool ParticipantListComplete(void); + void IncrementTotalConnectionCount(unsigned int i); + + // Used to track how long RakNet has been running. This is so we know who has been running longest + RakNet::TimeUS startupTime; + + // Option for SetAutoparticipateConnections + bool autoParticipateConnections; + + // totalConnectionCount is roughly maintained across all systems, and increments by 1 each time a new system connects to the mesh + // It is always kept at the highest known value + // It is used as the high 4 bytes for new FCMGuids. This causes newer values of FCM2Guid to be higher than lower values. The lowest value is the host. + unsigned int totalConnectionCount; + + // Our own ourFCMGuid. Starts at unassigned (0). Assigned once we send ID_FCM2_REQUEST_FCMGUID and get back ID_FCM2_RESPOND_CONNECTION_COUNT + FCM2Guid ourFCMGuid; + + /// List of systems we know the FCM2Guid for + DataStructures::List fcm2ParticipantList; + + RakNetGUID lastPushedHost; + + // Optimization: Store last calculated host in these variables. + RakNetGUID hostRakNetGuid; + FCM2Guid hostFCM2Guid; + + RakNet::RakString connectionPassword; + bool connectOnNewRemoteConnections; +}; + +} // namespace RakNet + +/* +Startup() +ourFCMGuid=unknown +totalConnectionCount=0 +Set startupTime + +AddParticipant() +if (sender by guid is a participant) +return; +AddParticipantInternal(guid); +if (ourFCMGuid==unknown) +Send to that system a request for their fcmGuid, totalConnectionCount. Inform startupTime. +else +Send to that system a request for their fcmGuid. Inform total connection count, our fcmGuid + +OnRequestGuid() +if (sender by guid is not a participant) +{ + // They added us as a participant, but we didn't add them. This can be caused by lag where both participants are not added at the same time. + // It doesn't affect the outcome as long as we still process the data + AddParticipantInternal(guid); +} +if (ourFCMGuid==unknown) +{ + if (includedStartupTime) + { + // Nobody has a fcmGuid + + if (their startup time is greater than our startup time) + ReplyConnectionCount(1); + else + ReplyConnectionCount(2); + } + else + { + // They have a fcmGuid, we do not + + SetMaxTotalConnectionCount(remoteCount); + AssignTheirGuid() + GenerateOurGuid(); + SendOurGuid(all); + } +} +else +{ + if (includedStartupTime) + { + // We have a fcmGuid they do not + + ReplyConnectionCount(totalConnectionCount+1); + SendOurGuid(sender); + } + else + { + // We both have fcmGuids + + SetMaxTotalConnectionCount(remoteCount); + AssignTheirGuid(); + SendOurGuid(sender); + } +} + +OnReplyConnectionCount() +SetMaxTotalConnectionCount(remoteCount); +GenerateOurGuid(); +SendOurGuid(allParticipants); + +OnReceiveTheirGuid() +AssignTheirGuid() +*/ + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/GetTime.cpp b/src/raknet/GetTime.cpp index ce7451c..8dbf906 100755 --- a/src/raknet/GetTime.cpp +++ b/src/raknet/GetTime.cpp @@ -1,222 +1,222 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#if defined(_WIN32) -#include "WindowsIncludes.h" -// To call timeGetTime -// on Code::Blocks, this needs to be libwinmm.a instead -#pragma comment(lib, "Winmm.lib") -#endif - -#include "GetTime.h" - - - - -#if defined(_WIN32) -DWORD mProcMask; -DWORD mSysMask; -HANDLE mThread; - - - - - - - - - -#else -#include -#include -RakNet::TimeUS initialTime; -#endif - -static bool initialized=false; - -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 -#include "SimpleMutex.h" -RakNet::TimeUS lastNormalizedReturnedValue=0; -RakNet::TimeUS lastNormalizedInputValue=0; -/// This constraints timer forward jumps to 1 second, and does not let it jump backwards -/// See http://support.microsoft.com/kb/274323 where the timer can sometimes jump forward by hours or days -/// This also has the effect where debugging a sending system won't treat the time spent halted past 1 second as elapsed network time -RakNet::TimeUS NormalizeTime(RakNet::TimeUS timeIn) -{ - RakNet::TimeUS diff, lastNormalizedReturnedValueCopy; - static RakNet::SimpleMutex mutex; - - mutex.Lock(); - if (timeIn>=lastNormalizedInputValue) - { - diff = timeIn-lastNormalizedInputValue; - if (diff > GET_TIME_SPIKE_LIMIT) - lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT; - else - lastNormalizedReturnedValue+=diff; - } - else - lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT; - - lastNormalizedInputValue=timeIn; - lastNormalizedReturnedValueCopy=lastNormalizedReturnedValue; - mutex.Unlock(); - - return lastNormalizedReturnedValueCopy; -} -#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 -RakNet::Time RakNet::GetTime( void ) -{ - return (RakNet::Time)(GetTimeUS()/1000); -} -RakNet::TimeMS RakNet::GetTimeMS( void ) -{ - return (RakNet::TimeMS)(GetTimeUS()/1000); -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#if defined(_WIN32) -RakNet::TimeUS GetTimeUS_Windows( void ) -{ - if ( initialized == false) - { - initialized = true; - - // Save the current process -#if !defined(_WIN32_WCE) - HANDLE mProc = GetCurrentProcess(); - - // Get the current Affinity -#if _MSC_VER >= 1400 && defined (_M_X64) - GetProcessAffinityMask(mProc, (PDWORD_PTR)&mProcMask, (PDWORD_PTR)&mSysMask); -#else -#ifndef __MINGW32__ - GetProcessAffinityMask(mProc, &mProcMask, &mSysMask); -#endif -#endif - mThread = GetCurrentThread(); - -#endif // _WIN32_WCE - } - - // 9/26/2010 In China running LuDaShi, QueryPerformanceFrequency has to be called every time because CPU clock speeds can be different - RakNet::TimeUS curTime; - LARGE_INTEGER PerfVal; - LARGE_INTEGER yo1; - - QueryPerformanceFrequency( &yo1 ); - QueryPerformanceCounter( &PerfVal ); - - __int64 quotient, remainder; - quotient=((PerfVal.QuadPart) / yo1.QuadPart); - remainder=((PerfVal.QuadPart) % yo1.QuadPart); - curTime = (RakNet::TimeUS) quotient*(RakNet::TimeUS)1000000 + (remainder*(RakNet::TimeUS)1000000 / yo1.QuadPart); - -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 - return NormalizeTime(curTime); -#else - return curTime; -#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 -} -#elif defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__) -RakNet::TimeUS GetTimeUS_Linux( void ) -{ - timeval tp; - if ( initialized == false) - { - gettimeofday( &tp, 0 ); - initialized=true; - // I do this because otherwise RakNet::Time in milliseconds won't work as it will underflow when dividing by 1000 to do the conversion - initialTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec ); - } - - // GCC - RakNet::TimeUS curTime; - gettimeofday( &tp, 0 ); - - curTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec ); - -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 - return NormalizeTime(curTime - initialTime); -#else - return curTime - initialTime; -#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 -} -#endif - -RakNet::TimeUS RakNet::GetTimeUS( void ) -{ - - - - - - -#if defined(_WIN32) - return GetTimeUS_Windows(); -#else - return GetTimeUS_Linux(); -#endif -} -bool RakNet::GreaterThan(RakNet::Time a, RakNet::Time b) -{ - // a > b? - const RakNet::Time halfSpan =(RakNet::Time) (((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2); - return b!=a && b-a>halfSpan; -} -bool RakNet::LessThan(RakNet::Time a, RakNet::Time b) -{ - // a < b? - const RakNet::Time halfSpan = ((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2; - return b!=a && b-a +#include +RakNet::TimeUS initialTime; +#endif + +static bool initialized=false; + +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 +#include "SimpleMutex.h" +RakNet::TimeUS lastNormalizedReturnedValue=0; +RakNet::TimeUS lastNormalizedInputValue=0; +/// This constraints timer forward jumps to 1 second, and does not let it jump backwards +/// See http://support.microsoft.com/kb/274323 where the timer can sometimes jump forward by hours or days +/// This also has the effect where debugging a sending system won't treat the time spent halted past 1 second as elapsed network time +RakNet::TimeUS NormalizeTime(RakNet::TimeUS timeIn) +{ + RakNet::TimeUS diff, lastNormalizedReturnedValueCopy; + static RakNet::SimpleMutex mutex; + + mutex.Lock(); + if (timeIn>=lastNormalizedInputValue) + { + diff = timeIn-lastNormalizedInputValue; + if (diff > GET_TIME_SPIKE_LIMIT) + lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT; + else + lastNormalizedReturnedValue+=diff; + } + else + lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT; + + lastNormalizedInputValue=timeIn; + lastNormalizedReturnedValueCopy=lastNormalizedReturnedValue; + mutex.Unlock(); + + return lastNormalizedReturnedValueCopy; +} +#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 +RakNet::Time RakNet::GetTime( void ) +{ + return (RakNet::Time)(GetTimeUS()/1000); +} +RakNet::TimeMS RakNet::GetTimeMS( void ) +{ + return (RakNet::TimeMS)(GetTimeUS()/1000); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if defined(_WIN32) +RakNet::TimeUS GetTimeUS_Windows( void ) +{ + if ( initialized == false) + { + initialized = true; + + // Save the current process +#if !defined(_WIN32_WCE) + HANDLE mProc = GetCurrentProcess(); + + // Get the current Affinity +#if _MSC_VER >= 1400 && defined (_M_X64) + GetProcessAffinityMask(mProc, (PDWORD_PTR)&mProcMask, (PDWORD_PTR)&mSysMask); +#else +#ifndef __MINGW32__ + GetProcessAffinityMask(mProc, &mProcMask, &mSysMask); +#endif +#endif + mThread = GetCurrentThread(); + +#endif // _WIN32_WCE + } + + // 9/26/2010 In China running LuDaShi, QueryPerformanceFrequency has to be called every time because CPU clock speeds can be different + RakNet::TimeUS curTime; + LARGE_INTEGER PerfVal; + LARGE_INTEGER yo1; + + QueryPerformanceFrequency( &yo1 ); + QueryPerformanceCounter( &PerfVal ); + + __int64 quotient, remainder; + quotient=((PerfVal.QuadPart) / yo1.QuadPart); + remainder=((PerfVal.QuadPart) % yo1.QuadPart); + curTime = (RakNet::TimeUS) quotient*(RakNet::TimeUS)1000000 + (remainder*(RakNet::TimeUS)1000000 / yo1.QuadPart); + +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 + return NormalizeTime(curTime); +#else + return curTime; +#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 +} +#elif defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__) +RakNet::TimeUS GetTimeUS_Linux( void ) +{ + timeval tp; + if ( initialized == false) + { + gettimeofday( &tp, 0 ); + initialized=true; + // I do this because otherwise RakNet::Time in milliseconds won't work as it will underflow when dividing by 1000 to do the conversion + initialTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec ); + } + + // GCC + RakNet::TimeUS curTime; + gettimeofday( &tp, 0 ); + + curTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec ); + +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 + return NormalizeTime(curTime - initialTime); +#else + return curTime - initialTime; +#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 +} +#endif + +RakNet::TimeUS RakNet::GetTimeUS( void ) +{ + + + + + + +#if defined(_WIN32) + return GetTimeUS_Windows(); +#else + return GetTimeUS_Linux(); +#endif +} +bool RakNet::GreaterThan(RakNet::Time a, RakNet::Time b) +{ + // a > b? + const RakNet::Time halfSpan =(RakNet::Time) (((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2); + return b!=a && b-a>halfSpan; +} +bool RakNet::LessThan(RakNet::Time a, RakNet::Time b) +{ + // a < b? + const RakNet::Time halfSpan = ((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2; + return b!=a && b-a b? - extern RAK_DLL_EXPORT bool GreaterThan(RakNet::Time a, RakNet::Time b); - /// a < b? - extern RAK_DLL_EXPORT bool LessThan(RakNet::Time a, RakNet::Time b); -} - -#endif +/// \file GetTime.h +/// \brief Returns the value from QueryPerformanceCounter. This is the function RakNet uses to represent time. This time won't match the time returned by GetTimeCount(). See http://www.jenkinssoftware.com/forum/index.php?topic=2798.0 +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __GET_TIME_H +#define __GET_TIME_H + +#include "Export.h" +#include "RakNetTime.h" // For RakNet::TimeMS + +namespace RakNet +{ + /// Same as GetTimeMS + /// Holds the time in either a 32 or 64 bit variable, depending on __GET_TIME_64BIT + RakNet::Time RAK_DLL_EXPORT GetTime( void ); + + /// Return the time as 32 bit + /// \note The maximum delta between returned calls is 1 second - however, RakNet calls this constantly anyway. See NormalizeTime() in the cpp. + RakNet::TimeMS RAK_DLL_EXPORT GetTimeMS( void ); + + /// Return the time as 64 bit + /// \note The maximum delta between returned calls is 1 second - however, RakNet calls this constantly anyway. See NormalizeTime() in the cpp. + RakNet::TimeUS RAK_DLL_EXPORT GetTimeUS( void ); + + /// a > b? + extern RAK_DLL_EXPORT bool GreaterThan(RakNet::Time a, RakNet::Time b); + /// a < b? + extern RAK_DLL_EXPORT bool LessThan(RakNet::Time a, RakNet::Time b); +} + +#endif diff --git a/src/raknet/Getche.cpp b/src/raknet/Getche.cpp index c6b6e21..6f8e420 100755 --- a/src/raknet/Getche.cpp +++ b/src/raknet/Getche.cpp @@ -1,27 +1,27 @@ - - -#if defined(_WIN32) -#include /* getche() */ -#elif defined(__S3E__) - -#else - -#include "Getche.h" - -char getche() -{ - - - struct termios oldt, - newt; - char ch; - tcgetattr( STDIN_FILENO, &oldt ); - newt = oldt; - newt.c_lflag &= ~( ICANON | ECHO ); - tcsetattr( STDIN_FILENO, TCSANOW, &newt ); - ch = getchar(); - tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); - return ch; - -} -#endif + + +#if defined(_WIN32) +#include /* getche() */ +#elif defined(__S3E__) + +#else + +#include "Getche.h" + +char getche() +{ + + + struct termios oldt, + newt; + char ch; + tcgetattr( STDIN_FILENO, &oldt ); + newt = oldt; + newt.c_lflag &= ~( ICANON | ECHO ); + tcsetattr( STDIN_FILENO, TCSANOW, &newt ); + ch = getchar(); + tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); + return ch; + +} +#endif diff --git a/src/raknet/Getche.h b/src/raknet/Getche.h index 70326d9..dff2ac3 100755 --- a/src/raknet/Getche.h +++ b/src/raknet/Getche.h @@ -1,11 +1,11 @@ - - -#if defined(_WIN32) -#include /* getche() */ - -#else -#include -#include -#include -char getche(); -#endif + + +#if defined(_WIN32) +#include /* getche() */ + +#else +#include +#include +#include +char getche(); +#endif diff --git a/src/raknet/Gets.cpp b/src/raknet/Gets.cpp index 2ff468a..be17d65 100755 --- a/src/raknet/Gets.cpp +++ b/src/raknet/Gets.cpp @@ -1,25 +1,25 @@ -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -char * Gets ( char * str, int num ) -{ - fgets(str, num, stdin); - if (str[0]=='\n' || str[0]=='\r') - str[0]=0; - - size_t len=strlen(str); - if (len>0 && (str[len-1]=='\n' || str[len-1]=='\r')) - str[len-1]=0; - if (len>1 && (str[len-2]=='\n' || str[len-2]=='\r')) - str[len-2]=0; - - return str; -} - -#ifdef __cplusplus -} -#endif +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +char * Gets ( char * str, int num ) +{ + fgets(str, num, stdin); + if (str[0]=='\n' || str[0]=='\r') + str[0]=0; + + size_t len=strlen(str); + if (len>0 && (str[len-1]=='\n' || str[len-1]=='\r')) + str[len-1]=0; + if (len>1 && (str[len-2]=='\n' || str[len-2]=='\r')) + str[len-2]=0; + + return str; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/raknet/Gets.h b/src/raknet/Gets.h index 78bcb6d..8925288 100755 --- a/src/raknet/Gets.h +++ b/src/raknet/Gets.h @@ -1,14 +1,14 @@ -#ifndef __GETS__H_ -#define __GETS__H_ - -#ifdef __cplusplus -extern "C" { -#endif - -char * Gets ( char * str, int num ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef __GETS__H_ +#define __GETS__H_ + +#ifdef __cplusplus +extern "C" { +#endif + +char * Gets ( char * str, int num ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/raknet/GridSectorizer.cpp b/src/raknet/GridSectorizer.cpp index 1fafc30..3fb9870 100755 --- a/src/raknet/GridSectorizer.cpp +++ b/src/raknet/GridSectorizer.cpp @@ -1,191 +1,191 @@ -#include "RakAssert.h" -#include "GridSectorizer.h" -//#include -#include - -GridSectorizer::GridSectorizer() -{ - grid=0; -} -GridSectorizer::~GridSectorizer() -{ - if (grid) - RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_); -} -void GridSectorizer::Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY) -{ - RakAssert(_maxCellWidth > 0.0f && _maxCellHeight > 0.0f); - if (grid) - RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_); - - cellOriginX=minX; - cellOriginY=minY; - gridWidth=maxX-minX; - gridHeight=maxY-minY; - gridCellWidthCount=(int) ceil(gridWidth/_maxCellWidth); - gridCellHeightCount=(int) ceil(gridHeight/_maxCellHeight); - // Make the cells slightly smaller, so we allocate an extra unneeded cell if on the edge. This way we don't go outside the array on rounding errors. - cellWidth=gridWidth/gridCellWidthCount; - cellHeight=gridHeight/gridCellHeightCount; - invCellWidth = 1.0f / cellWidth; - invCellHeight = 1.0f / cellHeight; - -#ifdef _USE_ORDERED_LIST - grid = RakNet::OP_NEW>(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ ); - DataStructures::OrderedList::IMPLEMENT_DEFAULT_COMPARISON(); -#else - grid = RakNet::OP_NEW_ARRAY >(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ ); -#endif -} -void GridSectorizer::AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY) -{ - RakAssert(cellWidth>0.0f); - RakAssert(minX < maxX && minY < maxY); - - int xStart, yStart, xEnd, yEnd, xCur, yCur; - xStart=WorldToCellXOffsetAndClamped(minX); - yStart=WorldToCellYOffsetAndClamped(minY); - xEnd=WorldToCellXOffsetAndClamped(maxX); - yEnd=WorldToCellYOffsetAndClamped(maxY); - - for (xCur=xStart; xCur <= xEnd; ++xCur) - { - for (yCur=yStart; yCur <= yEnd; ++yCur) - { -#ifdef _USE_ORDERED_LIST - grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true); -#else - grid[yCur*gridCellWidthCount+xCur].Insert(entry, _FILE_AND_LINE_); -#endif - } - } -} -#ifdef _USE_ORDERED_LIST -void GridSectorizer::RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY) -{ - RakAssert(cellWidth>0.0f); - RakAssert(minX <= maxX && minY <= maxY); - - int xStart, yStart, xEnd, yEnd, xCur, yCur; - xStart=WorldToCellXOffsetAndClamped(minX); - yStart=WorldToCellYOffsetAndClamped(minY); - xEnd=WorldToCellXOffsetAndClamped(maxX); - yEnd=WorldToCellYOffsetAndClamped(maxY); - - for (xCur=xStart; xCur <= xEnd; ++xCur) - { - for (yCur=yStart; yCur <= yEnd; ++yCur) - { - grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry); - } - } -} -void GridSectorizer::MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY, - const float destMinX, const float destMinY, const float destMaxX, const float destMaxY) -{ - RakAssert(cellWidth>0.0f); - RakAssert(sourceMinX < sourceMaxX && sourceMinY < sourceMaxY); - RakAssert(destMinX < destMaxX && destMinY < destMaxY); - - if (PositionCrossesCells(sourceMinX, sourceMinY, destMinX, destMinY)==false && - PositionCrossesCells(destMinX, destMinY, destMinX, destMinY)==false) - return; - - int xStartSource, yStartSource, xEndSource, yEndSource; - int xStartDest, yStartDest, xEndDest, yEndDest; - int xCur, yCur; - xStartSource=WorldToCellXOffsetAndClamped(sourceMinX); - yStartSource=WorldToCellYOffsetAndClamped(sourceMinY); - xEndSource=WorldToCellXOffsetAndClamped(sourceMaxX); - yEndSource=WorldToCellYOffsetAndClamped(sourceMaxY); - - xStartDest=WorldToCellXOffsetAndClamped(destMinX); - yStartDest=WorldToCellYOffsetAndClamped(destMinY); - xEndDest=WorldToCellXOffsetAndClamped(destMaxX); - yEndDest=WorldToCellYOffsetAndClamped(destMaxY); - - // Remove source that is not in dest - for (xCur=xStartSource; xCur <= xEndSource; ++xCur) - { - for (yCur=yStartSource; yCur <= yEndSource; ++yCur) - { - if (xCur < xStartDest || xCur > xEndDest || - yCur < yStartDest || yCur > yEndDest) - { - grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry); - } - } - } - - // Add dest that is not in source - for (xCur=xStartDest; xCur <= xEndDest; ++xCur) - { - for (yCur=yStartDest; yCur <= yEndDest; ++yCur) - { - if (xCur < xStartSource || xCur > xEndSource || - yCur < yStartSource || yCur > yEndSource) - { - grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true); - } - } - } -} -#endif -void GridSectorizer::GetEntries(DataStructures::List& intersectionList, const float minX, const float minY, const float maxX, const float maxY) -{ -#ifdef _USE_ORDERED_LIST - DataStructures::OrderedList* cell; -#else - DataStructures::List* cell; -#endif - int xStart, yStart, xEnd, yEnd, xCur, yCur; - unsigned index; - xStart=WorldToCellXOffsetAndClamped(minX); - yStart=WorldToCellYOffsetAndClamped(minY); - xEnd=WorldToCellXOffsetAndClamped(maxX); - yEnd=WorldToCellYOffsetAndClamped(maxY); - - intersectionList.Clear(true, _FILE_AND_LINE_); - for (xCur=xStart; xCur <= xEnd; ++xCur) - { - for (yCur=yStart; yCur <= yEnd; ++yCur) - { - cell = grid+yCur*gridCellWidthCount+xCur; - for (index=0; index < cell->Size(); ++index) - intersectionList.Insert(cell->operator [](index), _FILE_AND_LINE_); - } - } -} -bool GridSectorizer::PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const -{ - return originX/cellWidth!=destinationX/cellWidth || originY/cellHeight!=destinationY/cellHeight; -} -int GridSectorizer::WorldToCellX(const float input) const -{ - return (int)((input-cellOriginX)*invCellWidth); -} -int GridSectorizer::WorldToCellY(const float input) const -{ - return (int)((input-cellOriginY)*invCellHeight); -} -int GridSectorizer::WorldToCellXOffsetAndClamped(const float input) const -{ - int cell=WorldToCellX(input); - cell = cell > 0 ? cell : 0; // __max(cell,0); - cell = gridCellWidthCount-1 < cell ? gridCellWidthCount-1 : cell; // __min(gridCellWidthCount-1, cell); - return cell; -} -int GridSectorizer::WorldToCellYOffsetAndClamped(const float input) const -{ - int cell=WorldToCellY(input); - cell = cell > 0 ? cell : 0; // __max(cell,0); - cell = gridCellHeightCount-1 < cell ? gridCellHeightCount-1 : cell; // __min(gridCellHeightCount-1, cell); - return cell; -} -void GridSectorizer::Clear(void) -{ - int cur; - int count = gridCellWidthCount*gridCellHeightCount; - for (cur=0; cur +#include + +GridSectorizer::GridSectorizer() +{ + grid=0; +} +GridSectorizer::~GridSectorizer() +{ + if (grid) + RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_); +} +void GridSectorizer::Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY) +{ + RakAssert(_maxCellWidth > 0.0f && _maxCellHeight > 0.0f); + if (grid) + RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_); + + cellOriginX=minX; + cellOriginY=minY; + gridWidth=maxX-minX; + gridHeight=maxY-minY; + gridCellWidthCount=(int) ceil(gridWidth/_maxCellWidth); + gridCellHeightCount=(int) ceil(gridHeight/_maxCellHeight); + // Make the cells slightly smaller, so we allocate an extra unneeded cell if on the edge. This way we don't go outside the array on rounding errors. + cellWidth=gridWidth/gridCellWidthCount; + cellHeight=gridHeight/gridCellHeightCount; + invCellWidth = 1.0f / cellWidth; + invCellHeight = 1.0f / cellHeight; + +#ifdef _USE_ORDERED_LIST + grid = RakNet::OP_NEW>(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ ); + DataStructures::OrderedList::IMPLEMENT_DEFAULT_COMPARISON(); +#else + grid = RakNet::OP_NEW_ARRAY >(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ ); +#endif +} +void GridSectorizer::AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY) +{ + RakAssert(cellWidth>0.0f); + RakAssert(minX < maxX && minY < maxY); + + int xStart, yStart, xEnd, yEnd, xCur, yCur; + xStart=WorldToCellXOffsetAndClamped(minX); + yStart=WorldToCellYOffsetAndClamped(minY); + xEnd=WorldToCellXOffsetAndClamped(maxX); + yEnd=WorldToCellYOffsetAndClamped(maxY); + + for (xCur=xStart; xCur <= xEnd; ++xCur) + { + for (yCur=yStart; yCur <= yEnd; ++yCur) + { +#ifdef _USE_ORDERED_LIST + grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true); +#else + grid[yCur*gridCellWidthCount+xCur].Insert(entry, _FILE_AND_LINE_); +#endif + } + } +} +#ifdef _USE_ORDERED_LIST +void GridSectorizer::RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY) +{ + RakAssert(cellWidth>0.0f); + RakAssert(minX <= maxX && minY <= maxY); + + int xStart, yStart, xEnd, yEnd, xCur, yCur; + xStart=WorldToCellXOffsetAndClamped(minX); + yStart=WorldToCellYOffsetAndClamped(minY); + xEnd=WorldToCellXOffsetAndClamped(maxX); + yEnd=WorldToCellYOffsetAndClamped(maxY); + + for (xCur=xStart; xCur <= xEnd; ++xCur) + { + for (yCur=yStart; yCur <= yEnd; ++yCur) + { + grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry); + } + } +} +void GridSectorizer::MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY, + const float destMinX, const float destMinY, const float destMaxX, const float destMaxY) +{ + RakAssert(cellWidth>0.0f); + RakAssert(sourceMinX < sourceMaxX && sourceMinY < sourceMaxY); + RakAssert(destMinX < destMaxX && destMinY < destMaxY); + + if (PositionCrossesCells(sourceMinX, sourceMinY, destMinX, destMinY)==false && + PositionCrossesCells(destMinX, destMinY, destMinX, destMinY)==false) + return; + + int xStartSource, yStartSource, xEndSource, yEndSource; + int xStartDest, yStartDest, xEndDest, yEndDest; + int xCur, yCur; + xStartSource=WorldToCellXOffsetAndClamped(sourceMinX); + yStartSource=WorldToCellYOffsetAndClamped(sourceMinY); + xEndSource=WorldToCellXOffsetAndClamped(sourceMaxX); + yEndSource=WorldToCellYOffsetAndClamped(sourceMaxY); + + xStartDest=WorldToCellXOffsetAndClamped(destMinX); + yStartDest=WorldToCellYOffsetAndClamped(destMinY); + xEndDest=WorldToCellXOffsetAndClamped(destMaxX); + yEndDest=WorldToCellYOffsetAndClamped(destMaxY); + + // Remove source that is not in dest + for (xCur=xStartSource; xCur <= xEndSource; ++xCur) + { + for (yCur=yStartSource; yCur <= yEndSource; ++yCur) + { + if (xCur < xStartDest || xCur > xEndDest || + yCur < yStartDest || yCur > yEndDest) + { + grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry); + } + } + } + + // Add dest that is not in source + for (xCur=xStartDest; xCur <= xEndDest; ++xCur) + { + for (yCur=yStartDest; yCur <= yEndDest; ++yCur) + { + if (xCur < xStartSource || xCur > xEndSource || + yCur < yStartSource || yCur > yEndSource) + { + grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true); + } + } + } +} +#endif +void GridSectorizer::GetEntries(DataStructures::List& intersectionList, const float minX, const float minY, const float maxX, const float maxY) +{ +#ifdef _USE_ORDERED_LIST + DataStructures::OrderedList* cell; +#else + DataStructures::List* cell; +#endif + int xStart, yStart, xEnd, yEnd, xCur, yCur; + unsigned index; + xStart=WorldToCellXOffsetAndClamped(minX); + yStart=WorldToCellYOffsetAndClamped(minY); + xEnd=WorldToCellXOffsetAndClamped(maxX); + yEnd=WorldToCellYOffsetAndClamped(maxY); + + intersectionList.Clear(true, _FILE_AND_LINE_); + for (xCur=xStart; xCur <= xEnd; ++xCur) + { + for (yCur=yStart; yCur <= yEnd; ++yCur) + { + cell = grid+yCur*gridCellWidthCount+xCur; + for (index=0; index < cell->Size(); ++index) + intersectionList.Insert(cell->operator [](index), _FILE_AND_LINE_); + } + } +} +bool GridSectorizer::PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const +{ + return originX/cellWidth!=destinationX/cellWidth || originY/cellHeight!=destinationY/cellHeight; +} +int GridSectorizer::WorldToCellX(const float input) const +{ + return (int)((input-cellOriginX)*invCellWidth); +} +int GridSectorizer::WorldToCellY(const float input) const +{ + return (int)((input-cellOriginY)*invCellHeight); +} +int GridSectorizer::WorldToCellXOffsetAndClamped(const float input) const +{ + int cell=WorldToCellX(input); + cell = cell > 0 ? cell : 0; // __max(cell,0); + cell = gridCellWidthCount-1 < cell ? gridCellWidthCount-1 : cell; // __min(gridCellWidthCount-1, cell); + return cell; +} +int GridSectorizer::WorldToCellYOffsetAndClamped(const float input) const +{ + int cell=WorldToCellY(input); + cell = cell > 0 ? cell : 0; // __max(cell,0); + cell = gridCellHeightCount-1 < cell ? gridCellHeightCount-1 : cell; // __min(gridCellHeightCount-1, cell); + return cell; +} +void GridSectorizer::Clear(void) +{ + int cur; + int count = gridCellWidthCount*gridCellHeightCount; + for (cur=0; cur& intersectionList, const float minX, const float minY, const float maxX, const float maxY); - - void Clear(void); - -protected: - int WorldToCellX(const float input) const; - int WorldToCellY(const float input) const; - int WorldToCellXOffsetAndClamped(const float input) const; - int WorldToCellYOffsetAndClamped(const float input) const; - - // Returns true or false if a position crosses cells in the grid. If false, you don't need to move entries - bool PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const; - - float cellOriginX, cellOriginY; - float cellWidth, cellHeight; - float invCellWidth, invCellHeight; - float gridWidth, gridHeight; - int gridCellWidthCount, gridCellHeightCount; - - - // int gridWidth, gridHeight; - -#ifdef _USE_ORDERED_LIST - DataStructures::OrderedList* grid; -#else - DataStructures::List* grid; -#endif -}; - -#endif +#ifndef _GRID_SECTORIZER_H +#define _GRID_SECTORIZER_H + +//#define _USE_ORDERED_LIST + +#include "RakMemoryOverride.h" + +#ifdef _USE_ORDERED_LIST +#include "DS_OrderedList.h" +#else +#include "DS_List.h" +#endif + +class GridSectorizer +{ +public: + GridSectorizer(); + ~GridSectorizer(); + + // _cellWidth, _cellHeight is the width and height of each cell in world units + // minX, minY, maxX, maxY are the world dimensions (can be changed to dynamically allocate later if needed) + void Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY); + + // Adds a pointer to the grid with bounding rectangle dimensions + void AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY); + +#ifdef _USE_ORDERED_LIST + + // Removes a pointer, as above + void RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY); + + // Adds and removes in one pass, more efficient than calling both functions consecutively + void MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY, + const float destMinX, const float destMinY, const float destMaxX, const float destMaxY); + +#endif + + // Adds to intersectionList all entries in a certain radius + void GetEntries(DataStructures::List& intersectionList, const float minX, const float minY, const float maxX, const float maxY); + + void Clear(void); + +protected: + int WorldToCellX(const float input) const; + int WorldToCellY(const float input) const; + int WorldToCellXOffsetAndClamped(const float input) const; + int WorldToCellYOffsetAndClamped(const float input) const; + + // Returns true or false if a position crosses cells in the grid. If false, you don't need to move entries + bool PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const; + + float cellOriginX, cellOriginY; + float cellWidth, cellHeight; + float invCellWidth, invCellHeight; + float gridWidth, gridHeight; + int gridCellWidthCount, gridCellHeightCount; + + + // int gridWidth, gridHeight; + +#ifdef _USE_ORDERED_LIST + DataStructures::OrderedList* grid; +#else + DataStructures::List* grid; +#endif +}; + +#endif diff --git a/src/raknet/HTTPConnection.cpp b/src/raknet/HTTPConnection.cpp index dd541fb..78b6e1c 100755 --- a/src/raknet/HTTPConnection.cpp +++ b/src/raknet/HTTPConnection.cpp @@ -1,313 +1,313 @@ -/// \file -/// \brief Contains HTTPConnection, used to communicate with web servers -/// -/// This file is part of RakNet Copyright 2008 Kevin Jenkins. -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. -/// GPL license users are subject to the GNU General Public -/// License as published by the Free -/// Software Foundation; either version 2 of the License, or (at your -/// option) any later version. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "TCPInterface.h" -#include "HTTPConnection.h" -#include "RakSleep.h" -#include "RakString.h" -#include "RakAssert.h" -#include -#include -#include -#include - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(HTTPConnection,HTTPConnection); - -HTTPConnection::HTTPConnection() : connectionState(CS_NONE) -{ - tcp=0; -} - -void HTTPConnection::Init(TCPInterface* _tcp, const char *_host, unsigned short _port) -{ - tcp=_tcp; - host=_host; - port=_port; -} - -void HTTPConnection::Post(const char *remote_path, const char *data, const char *_contentType) -{ - OutgoingCommand op; - op.contentType=_contentType; - op.data=data; - op.remotePath=remote_path; - op.isPost=true; - outgoingCommand.Push(op, _FILE_AND_LINE_ ); - //printf("Adding outgoing post\n"); -} - -void HTTPConnection::Get(const char *path) -{ - OutgoingCommand op; - op.remotePath=path; - op.isPost=false; - outgoingCommand.Push(op, _FILE_AND_LINE_ ); -} - -bool HTTPConnection::HasBadResponse(int *code, RakNet::RakString *data) -{ - if(badResponses.IsEmpty()) - return false; - - if (code) - *code = badResponses.Peek().code; - if (data) - *data = badResponses.Pop().data; - return true; -} -void HTTPConnection::CloseConnection() -{ - connectionState=CS_DISCONNECTING; -} -void HTTPConnection::Update(void) -{ - SystemAddress sa; - sa = tcp->HasCompletedConnectionAttempt(); - while (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { -// printf("Connected\n"); - connectionState=CS_CONNECTED; - server=sa; - sa = tcp->HasCompletedConnectionAttempt(); - } - - sa = tcp->HasFailedConnectionAttempt(); - while (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - //printf("Failed connected\n"); - CloseConnection(); - sa = tcp->HasFailedConnectionAttempt(); - } - - sa = tcp->HasLostConnection(); - while (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - //printf("Lost connection\n"); - CloseConnection(); - sa = tcp->HasLostConnection(); - } - - - switch (connectionState) - { - case CS_NONE: - { - if (outgoingCommand.IsEmpty()) - return; - - //printf("Connecting\n"); - server = tcp->Connect(host, port, false); - connectionState = CS_CONNECTING; - } - break; - case CS_DISCONNECTING: - { - if (tcp->ReceiveHasPackets()==false) - { - if (incomingData.IsEmpty()==false) - { - results.Push(incomingData, _FILE_AND_LINE_ ); - } - incomingData.Clear(); - tcp->CloseConnection(server); - connectionState=CS_NONE; - } - } - break; - case CS_CONNECTING: - { - } - break; - case CS_CONNECTED: - { - //printf("Connected\n"); - if (outgoingCommand.IsEmpty()) - { - //printf("Closed connection (nothing to do)\n"); - CloseConnection(); - return; - } - -#if OPEN_SSL_CLIENT_SUPPORT==1 - tcp->StartSSLClient(server); -#endif - - //printf("Sending request\n"); - currentProcessingCommand = outgoingCommand.Pop(); - RakString request; - if (currentProcessingCommand.isPost) - { - request.Set("POST %s HTTP/1.0\r\n" - "Host: %s:%i\r\n" - "Content-Type: %s\r\n" - "Content-Length: %u\r\n" - "\r\n" - "%s", - currentProcessingCommand.remotePath.C_String(), - host.C_String(), - port, - currentProcessingCommand.contentType.C_String(), - (unsigned) currentProcessingCommand.data.GetLength(), - currentProcessingCommand.data.C_String()); - } - else - { - // request.Set("GET %s\r\n", host.C_String()); - // http://www.jenkinssoftware.com/forum/index.php?topic=4601.0;topicseen - request.Set("GET %s HTTP/1.0\r\n" - "Host: %s:%i\r\n" - "\r\n", - currentProcessingCommand.remotePath.C_String(), - host.C_String(), - port); - } - - // printf(request.C_String()); - // request.URLEncode(); - tcp->Send(request.C_String(), (unsigned int) request.GetLength(), server,false); - connectionState=CS_PROCESSING; - } - break; - case CS_PROCESSING: - { - } - } - -// if (connectionState==CS_PROCESSING && currentProcessingCommand.data.IsEmpty()==false) -// outgoingCommand.PushAtHead(currentProcessingCommand); -} -bool HTTPConnection::HasRead(void) const -{ - return results.IsEmpty()==false; -} -RakString HTTPConnection::Read(void) -{ - if (results.IsEmpty()) - return RakString(); - - RakNet::RakString resultStr = results.Pop(); - // const char *start_of_body = strstr(resultStr.C_String(), "\r\n\r\n"); - const char *start_of_body = strpbrk(resultStr.C_String(), "\001\002\003%"); - - if(start_of_body) - return RakNet::RakString::NonVariadic(start_of_body); - else - return resultStr; -} -SystemAddress HTTPConnection::GetServerAddress(void) const -{ - return server; -} -void HTTPConnection::ProcessTCPPacket(Packet *packet) -{ - RakAssert(packet); - - // read all the packets possible - if(packet->systemAddress == server) - { - if(incomingData.GetLength() == 0) - { - int response_code = atoi((char *)packet->data + strlen("HTTP/1.0 ")); - - if(response_code > 299) - { - badResponses.Push(BadResponse(packet->data, response_code), _FILE_AND_LINE_ ); - //printf("Closed connection (Bad response 2)\n"); - CloseConnection(); - return; - } - } - - RakNet::RakString incomingTemp = RakNet::RakString::NonVariadic((const char*) packet->data); - incomingTemp.URLDecode(); - incomingData += incomingTemp; - - // printf((const char*) packet->data); - // printf("\n"); - - RakAssert(strlen((char *)packet->data) == packet->length); // otherwise it contains Null bytes - - const char *start_of_body = strstr(incomingData, "\r\n\r\n"); - - // besides having the server close the connection, they may - // provide a length header and supply that many bytes - if( - // Why was start_of_body here? Makes the GET command fail - // start_of_body && - connectionState == CS_PROCESSING) - { - /* - // The stupid programmer that wrote this originally didn't think that just because the header contains this value doesn't mean you got the whole message - if (strstr((const char*) packet->data, "\r\nConnection: close\r\n")) - { - CloseConnection(); - } - else - { - */ - long length_of_headers; - if (start_of_body) - { - length_of_headers = (long)(start_of_body + 4 - incomingData.C_String()); - const char *length_header = strstr(incomingData, "\r\nLength: "); - - if(length_header) - { - long length = atol(length_header + 10) + length_of_headers; - - if((long) incomingData.GetLength() >= length) - { - //printf("Closed connection (Got all data due to length header)\n"); - CloseConnection(); - } - } - } - else - { - // No processing needed - } - - - //} - } - } -} - -bool HTTPConnection::IsBusy(void) const -{ - return connectionState != CS_NONE; -} - -int HTTPConnection::GetState(void) const -{ - return connectionState; -} - - -HTTPConnection::~HTTPConnection(void) -{ - if (tcp) - tcp->CloseConnection(server); -} - - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains HTTPConnection, used to communicate with web servers +/// +/// This file is part of RakNet Copyright 2008 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "TCPInterface.h" +#include "HTTPConnection.h" +#include "RakSleep.h" +#include "RakString.h" +#include "RakAssert.h" +#include +#include +#include +#include + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(HTTPConnection,HTTPConnection); + +HTTPConnection::HTTPConnection() : connectionState(CS_NONE) +{ + tcp=0; +} + +void HTTPConnection::Init(TCPInterface* _tcp, const char *_host, unsigned short _port) +{ + tcp=_tcp; + host=_host; + port=_port; +} + +void HTTPConnection::Post(const char *remote_path, const char *data, const char *_contentType) +{ + OutgoingCommand op; + op.contentType=_contentType; + op.data=data; + op.remotePath=remote_path; + op.isPost=true; + outgoingCommand.Push(op, _FILE_AND_LINE_ ); + //printf("Adding outgoing post\n"); +} + +void HTTPConnection::Get(const char *path) +{ + OutgoingCommand op; + op.remotePath=path; + op.isPost=false; + outgoingCommand.Push(op, _FILE_AND_LINE_ ); +} + +bool HTTPConnection::HasBadResponse(int *code, RakNet::RakString *data) +{ + if(badResponses.IsEmpty()) + return false; + + if (code) + *code = badResponses.Peek().code; + if (data) + *data = badResponses.Pop().data; + return true; +} +void HTTPConnection::CloseConnection() +{ + connectionState=CS_DISCONNECTING; +} +void HTTPConnection::Update(void) +{ + SystemAddress sa; + sa = tcp->HasCompletedConnectionAttempt(); + while (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { +// printf("Connected\n"); + connectionState=CS_CONNECTED; + server=sa; + sa = tcp->HasCompletedConnectionAttempt(); + } + + sa = tcp->HasFailedConnectionAttempt(); + while (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + //printf("Failed connected\n"); + CloseConnection(); + sa = tcp->HasFailedConnectionAttempt(); + } + + sa = tcp->HasLostConnection(); + while (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + //printf("Lost connection\n"); + CloseConnection(); + sa = tcp->HasLostConnection(); + } + + + switch (connectionState) + { + case CS_NONE: + { + if (outgoingCommand.IsEmpty()) + return; + + //printf("Connecting\n"); + server = tcp->Connect(host, port, false); + connectionState = CS_CONNECTING; + } + break; + case CS_DISCONNECTING: + { + if (tcp->ReceiveHasPackets()==false) + { + if (incomingData.IsEmpty()==false) + { + results.Push(incomingData, _FILE_AND_LINE_ ); + } + incomingData.Clear(); + tcp->CloseConnection(server); + connectionState=CS_NONE; + } + } + break; + case CS_CONNECTING: + { + } + break; + case CS_CONNECTED: + { + //printf("Connected\n"); + if (outgoingCommand.IsEmpty()) + { + //printf("Closed connection (nothing to do)\n"); + CloseConnection(); + return; + } + +#if OPEN_SSL_CLIENT_SUPPORT==1 + tcp->StartSSLClient(server); +#endif + + //printf("Sending request\n"); + currentProcessingCommand = outgoingCommand.Pop(); + RakString request; + if (currentProcessingCommand.isPost) + { + request.Set("POST %s HTTP/1.0\r\n" + "Host: %s:%i\r\n" + "Content-Type: %s\r\n" + "Content-Length: %u\r\n" + "\r\n" + "%s", + currentProcessingCommand.remotePath.C_String(), + host.C_String(), + port, + currentProcessingCommand.contentType.C_String(), + (unsigned) currentProcessingCommand.data.GetLength(), + currentProcessingCommand.data.C_String()); + } + else + { + // request.Set("GET %s\r\n", host.C_String()); + // http://www.jenkinssoftware.com/forum/index.php?topic=4601.0;topicseen + request.Set("GET %s HTTP/1.0\r\n" + "Host: %s:%i\r\n" + "\r\n", + currentProcessingCommand.remotePath.C_String(), + host.C_String(), + port); + } + + // printf(request.C_String()); + // request.URLEncode(); + tcp->Send(request.C_String(), (unsigned int) request.GetLength(), server,false); + connectionState=CS_PROCESSING; + } + break; + case CS_PROCESSING: + { + } + } + +// if (connectionState==CS_PROCESSING && currentProcessingCommand.data.IsEmpty()==false) +// outgoingCommand.PushAtHead(currentProcessingCommand); +} +bool HTTPConnection::HasRead(void) const +{ + return results.IsEmpty()==false; +} +RakString HTTPConnection::Read(void) +{ + if (results.IsEmpty()) + return RakString(); + + RakNet::RakString resultStr = results.Pop(); + // const char *start_of_body = strstr(resultStr.C_String(), "\r\n\r\n"); + const char *start_of_body = strpbrk(resultStr.C_String(), "\001\002\003%"); + + if(start_of_body) + return RakNet::RakString::NonVariadic(start_of_body); + else + return resultStr; +} +SystemAddress HTTPConnection::GetServerAddress(void) const +{ + return server; +} +void HTTPConnection::ProcessTCPPacket(Packet *packet) +{ + RakAssert(packet); + + // read all the packets possible + if(packet->systemAddress == server) + { + if(incomingData.GetLength() == 0) + { + int response_code = atoi((char *)packet->data + strlen("HTTP/1.0 ")); + + if(response_code > 299) + { + badResponses.Push(BadResponse(packet->data, response_code), _FILE_AND_LINE_ ); + //printf("Closed connection (Bad response 2)\n"); + CloseConnection(); + return; + } + } + + RakNet::RakString incomingTemp = RakNet::RakString::NonVariadic((const char*) packet->data); + incomingTemp.URLDecode(); + incomingData += incomingTemp; + + // printf((const char*) packet->data); + // printf("\n"); + + RakAssert(strlen((char *)packet->data) == packet->length); // otherwise it contains Null bytes + + const char *start_of_body = strstr(incomingData, "\r\n\r\n"); + + // besides having the server close the connection, they may + // provide a length header and supply that many bytes + if( + // Why was start_of_body here? Makes the GET command fail + // start_of_body && + connectionState == CS_PROCESSING) + { + /* + // The stupid programmer that wrote this originally didn't think that just because the header contains this value doesn't mean you got the whole message + if (strstr((const char*) packet->data, "\r\nConnection: close\r\n")) + { + CloseConnection(); + } + else + { + */ + long length_of_headers; + if (start_of_body) + { + length_of_headers = (long)(start_of_body + 4 - incomingData.C_String()); + const char *length_header = strstr(incomingData, "\r\nLength: "); + + if(length_header) + { + long length = atol(length_header + 10) + length_of_headers; + + if((long) incomingData.GetLength() >= length) + { + //printf("Closed connection (Got all data due to length header)\n"); + CloseConnection(); + } + } + } + else + { + // No processing needed + } + + + //} + } + } +} + +bool HTTPConnection::IsBusy(void) const +{ + return connectionState != CS_NONE; +} + +int HTTPConnection::GetState(void) const +{ + return connectionState; +} + + +HTTPConnection::~HTTPConnection(void) +{ + if (tcp) + tcp->CloseConnection(server); +} + + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/HTTPConnection.h b/src/raknet/HTTPConnection.h index cd1b0c6..b2e3921 100755 --- a/src/raknet/HTTPConnection.h +++ b/src/raknet/HTTPConnection.h @@ -1,176 +1,176 @@ -/// \file HTTPConnection.h -/// \brief Contains HTTPConnection, used to communicate with web servers -/// -/// This file is part of RakNet Copyright 2008 Kevin Jenkins. -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. -/// GPL license users are subject to the GNU General Public -/// License as published by the Free -/// Software Foundation; either version 2 of the License, or (at your -/// option) any later version. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#ifndef __HTTP_CONNECTION -#define __HTTP_CONNECTION - -#include "Export.h" -#include "RakString.h" -#include "RakMemoryOverride.h" -#include "RakNetTypes.h" -#include "DS_Queue.h" - -namespace RakNet -{ -/// Forward declarations -class TCPInterface; -struct SystemAddress; - -/// \brief Use HTTPConnection to communicate with a web server. -/// \details Start an instance of TCPInterface via the Start() command. -/// Instantiate a new instance of HTTPConnection, and associate TCPInterface with the class in the constructor. -/// Use Post() to send commands to the web server, and ProcessDataPacket() to update the connection with packets returned from TCPInterface that have the system address of the web server -/// This class will handle connecting and reconnecting as necessary. -/// -/// Note that only one Post() can be handled at a time. -class RAK_DLL_EXPORT HTTPConnection -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(HTTPConnection) - - /// Returns a HTTP object associated with this tcp connection - HTTPConnection(); - virtual ~HTTPConnection(); - - /// \pre tcp should already be started - void Init(TCPInterface *_tcp, const char *host, unsigned short port=80); - - /// Submit data to the HTTP server - /// HTTP only allows one request at a time per connection - /// - /// \pre IsBusy()==false - /// \param path the path on the remote server you want to POST to. For example "index.html" - /// \param data A NULL terminated string to submit to the server - /// \param contentType "Content-Type:" passed to post. - void Post(const char *path, const char *data, const char *_contentType="application/x-www-form-urlencoded"); - - /// Get a file from a webserver - /// \param path the path on the remote server you want to GET from. For example "index.html" - void Get(const char *path); - - /// Is there a Read result ready? - bool HasRead(void) const; - - /// Get one result from the server - /// \pre HasResult must return true - RakNet::RakString Read(void); - - /// Call periodically to do time-based updates - void Update(void); - - /// Returns the address of the server we are connected to - SystemAddress GetServerAddress(void) const; - - /// Process an HTTP data packet returned from TCPInterface - /// Returns true when we have gotten all the data from the HTTP server. - /// If this returns true then it's safe to Post() another request - /// Deallocate the packet as usual via TCPInterface - /// \param packet NULL or a packet associated with our host and port - void ProcessTCPPacket(Packet *packet); - - /// Results of HTTP requests. Standard response codes are < 999 - /// ( define HTTP codes and our internal codes as needed ) - enum ResponseCodes { NoBody=1001, OK=200, Deleted=1002 }; - - HTTPConnection& operator=(const HTTPConnection& rhs){(void) rhs; return *this;} - - /// Encapsulates a raw HTTP response and response code - struct BadResponse - { - public: - BadResponse() {code=0;} - - BadResponse(const unsigned char *_data, int _code) - : data((const char *)_data), code(_code) {} - - BadResponse(const char *_data, int _code) - : data(_data), code(_code) {} - - operator int () const { return code; } - - RakNet::RakString data; - int code; // ResponseCodes - }; - - /// Queued events of failed exchanges with the HTTP server - bool HasBadResponse(int *code, RakNet::RakString *data); - - /// Returns false if the connection is not doing anything else - bool IsBusy(void) const; - - /// \internal - int GetState(void) const; - - struct OutgoingCommand - { - RakNet::RakString remotePath; - RakNet::RakString data; - RakNet::RakString contentType; - bool isPost; - }; - - DataStructures::Queue outgoingCommand; - OutgoingCommand currentProcessingCommand; - -private: - SystemAddress server; - TCPInterface *tcp; - RakNet::RakString host; - unsigned short port; - DataStructures::Queue badResponses; - - enum ConnectionState - { - CS_NONE, - CS_DISCONNECTING, - CS_CONNECTING, - CS_CONNECTED, - CS_PROCESSING, - } connectionState; - - RakNet::RakString incomingData; - DataStructures::Queue results; - - void CloseConnection(); - - /* - enum { RAK_HTTP_INITIAL, - RAK_HTTP_STARTING, - RAK_HTTP_CONNECTING, - RAK_HTTP_ESTABLISHED, - RAK_HTTP_REQUEST_SENT, - RAK_HTTP_IDLE } state; - - RakNet::RakString outgoing, incoming, path, contentType; - void Process(Packet *packet); // the workhorse - - // this helps check the various status lists in TCPInterface - typedef SystemAddress (TCPInterface::*StatusCheckFunction)(void); - bool InList(StatusCheckFunction func); - */ - -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file HTTPConnection.h +/// \brief Contains HTTPConnection, used to communicate with web servers +/// +/// This file is part of RakNet Copyright 2008 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#ifndef __HTTP_CONNECTION +#define __HTTP_CONNECTION + +#include "Export.h" +#include "RakString.h" +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "DS_Queue.h" + +namespace RakNet +{ +/// Forward declarations +class TCPInterface; +struct SystemAddress; + +/// \brief Use HTTPConnection to communicate with a web server. +/// \details Start an instance of TCPInterface via the Start() command. +/// Instantiate a new instance of HTTPConnection, and associate TCPInterface with the class in the constructor. +/// Use Post() to send commands to the web server, and ProcessDataPacket() to update the connection with packets returned from TCPInterface that have the system address of the web server +/// This class will handle connecting and reconnecting as necessary. +/// +/// Note that only one Post() can be handled at a time. +class RAK_DLL_EXPORT HTTPConnection +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(HTTPConnection) + + /// Returns a HTTP object associated with this tcp connection + HTTPConnection(); + virtual ~HTTPConnection(); + + /// \pre tcp should already be started + void Init(TCPInterface *_tcp, const char *host, unsigned short port=80); + + /// Submit data to the HTTP server + /// HTTP only allows one request at a time per connection + /// + /// \pre IsBusy()==false + /// \param path the path on the remote server you want to POST to. For example "index.html" + /// \param data A NULL terminated string to submit to the server + /// \param contentType "Content-Type:" passed to post. + void Post(const char *path, const char *data, const char *_contentType="application/x-www-form-urlencoded"); + + /// Get a file from a webserver + /// \param path the path on the remote server you want to GET from. For example "index.html" + void Get(const char *path); + + /// Is there a Read result ready? + bool HasRead(void) const; + + /// Get one result from the server + /// \pre HasResult must return true + RakNet::RakString Read(void); + + /// Call periodically to do time-based updates + void Update(void); + + /// Returns the address of the server we are connected to + SystemAddress GetServerAddress(void) const; + + /// Process an HTTP data packet returned from TCPInterface + /// Returns true when we have gotten all the data from the HTTP server. + /// If this returns true then it's safe to Post() another request + /// Deallocate the packet as usual via TCPInterface + /// \param packet NULL or a packet associated with our host and port + void ProcessTCPPacket(Packet *packet); + + /// Results of HTTP requests. Standard response codes are < 999 + /// ( define HTTP codes and our internal codes as needed ) + enum ResponseCodes { NoBody=1001, OK=200, Deleted=1002 }; + + HTTPConnection& operator=(const HTTPConnection& rhs){(void) rhs; return *this;} + + /// Encapsulates a raw HTTP response and response code + struct BadResponse + { + public: + BadResponse() {code=0;} + + BadResponse(const unsigned char *_data, int _code) + : data((const char *)_data), code(_code) {} + + BadResponse(const char *_data, int _code) + : data(_data), code(_code) {} + + operator int () const { return code; } + + RakNet::RakString data; + int code; // ResponseCodes + }; + + /// Queued events of failed exchanges with the HTTP server + bool HasBadResponse(int *code, RakNet::RakString *data); + + /// Returns false if the connection is not doing anything else + bool IsBusy(void) const; + + /// \internal + int GetState(void) const; + + struct OutgoingCommand + { + RakNet::RakString remotePath; + RakNet::RakString data; + RakNet::RakString contentType; + bool isPost; + }; + + DataStructures::Queue outgoingCommand; + OutgoingCommand currentProcessingCommand; + +private: + SystemAddress server; + TCPInterface *tcp; + RakNet::RakString host; + unsigned short port; + DataStructures::Queue badResponses; + + enum ConnectionState + { + CS_NONE, + CS_DISCONNECTING, + CS_CONNECTING, + CS_CONNECTED, + CS_PROCESSING, + } connectionState; + + RakNet::RakString incomingData; + DataStructures::Queue results; + + void CloseConnection(); + + /* + enum { RAK_HTTP_INITIAL, + RAK_HTTP_STARTING, + RAK_HTTP_CONNECTING, + RAK_HTTP_ESTABLISHED, + RAK_HTTP_REQUEST_SENT, + RAK_HTTP_IDLE } state; + + RakNet::RakString outgoing, incoming, path, contentType; + void Process(Packet *packet); // the workhorse + + // this helps check the various status lists in TCPInterface + typedef SystemAddress (TCPInterface::*StatusCheckFunction)(void); + bool InList(StatusCheckFunction func); + */ + +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/IncrementalReadInterface.cpp b/src/raknet/IncrementalReadInterface.cpp index 2809380..238f8a9 100755 --- a/src/raknet/IncrementalReadInterface.cpp +++ b/src/raknet/IncrementalReadInterface.cpp @@ -1,15 +1,15 @@ -#include "IncrementalReadInterface.h" -#include - -using namespace RakNet; - -unsigned int IncrementalReadInterface::GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context) -{ - FILE *fp = fopen(filename, "rb"); - if (fp==0) - return 0; - fseek(fp,startReadBytes,SEEK_SET); - unsigned int numRead = (unsigned int) fread(preallocatedDestination,1,numBytesToRead, fp); - fclose(fp); - return numRead; -} +#include "IncrementalReadInterface.h" +#include + +using namespace RakNet; + +unsigned int IncrementalReadInterface::GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context) +{ + FILE *fp = fopen(filename, "rb"); + if (fp==0) + return 0; + fseek(fp,startReadBytes,SEEK_SET); + unsigned int numRead = (unsigned int) fread(preallocatedDestination,1,numBytesToRead, fp); + fclose(fp); + return numRead; +} diff --git a/src/raknet/IncrementalReadInterface.h b/src/raknet/IncrementalReadInterface.h index 0bf56a8..3f396e4 100755 --- a/src/raknet/IncrementalReadInterface.h +++ b/src/raknet/IncrementalReadInterface.h @@ -1,28 +1,28 @@ -#ifndef __INCREMENTAL_READ_INTERFACE_H -#define __INCREMENTAL_READ_INTERFACE_H - -#include "FileListNodeContext.h" -#include "Export.h" - -namespace RakNet -{ - -class RAK_DLL_EXPORT IncrementalReadInterface -{ -public: - IncrementalReadInterface() {} - virtual ~IncrementalReadInterface() {} - - /// Read part of a file into \a destination - /// Return the number of bytes written. Return 0 when file is done. - /// \param[in] filename Filename to read - /// \param[in] startReadBytes What offset from the start of the file to read from - /// \param[in] numBytesToRead How many bytes to read. This is also how many bytes have been allocated to preallocatedDestination - /// \param[out] preallocatedDestination Write your data here - /// \return The number of bytes read, or 0 if none - virtual unsigned int GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context); -}; - -} // namespace RakNet - -#endif +#ifndef __INCREMENTAL_READ_INTERFACE_H +#define __INCREMENTAL_READ_INTERFACE_H + +#include "FileListNodeContext.h" +#include "Export.h" + +namespace RakNet +{ + +class RAK_DLL_EXPORT IncrementalReadInterface +{ +public: + IncrementalReadInterface() {} + virtual ~IncrementalReadInterface() {} + + /// Read part of a file into \a destination + /// Return the number of bytes written. Return 0 when file is done. + /// \param[in] filename Filename to read + /// \param[in] startReadBytes What offset from the start of the file to read from + /// \param[in] numBytesToRead How many bytes to read. This is also how many bytes have been allocated to preallocatedDestination + /// \param[out] preallocatedDestination Write your data here + /// \return The number of bytes read, or 0 if none + virtual unsigned int GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context); +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/InternalPacket.h b/src/raknet/InternalPacket.h index 4461cce..790fbbe 100755 --- a/src/raknet/InternalPacket.h +++ b/src/raknet/InternalPacket.h @@ -1,131 +1,131 @@ -/// \file -/// \brief \b [Internal] A class which stores a user message, and all information associated with sending and receiving that message. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. -/// GPL license users are subject to the GNU General Public -/// License as published by the Free -/// Software Foundation; either version 2 of the License, or (at your -/// option) any later version. - -#ifndef __INTERNAL_PACKET_H -#define __INTERNAL_PACKET_H - -#include "PacketPriority.h" -#include "RakNetTypes.h" -#include "RakMemoryOverride.h" -#include "RakNetDefines.h" -#include "NativeTypes.h" -#include "RakNetDefines.h" -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 -#include "CCRakNetUDT.h" -#else -#include "CCRakNetSlidingWindow.h" -#endif - -namespace RakNet { - -typedef uint16_t SplitPacketIdType; -typedef uint32_t SplitPacketIndexType; - -/// This is the counter used for holding packet numbers, so we can detect duplicate packets. It should be large enough that if the variables -/// Internally assumed to be 4 bytes, but written as 3 bytes in ReliabilityLayer::WriteToBitStreamFromInternalPacket -typedef uint24_t MessageNumberType; - -/// This is the counter used for holding ordered packet numbers, so we can detect out-of-order packets. It should be large enough that if the variables -/// were to wrap, the newly wrapped values would no longer be in use. Warning: Too large of a value wastes bandwidth! -typedef MessageNumberType OrderingIndexType; - -typedef RakNet::TimeUS RemoteSystemTimeType; - -struct InternalPacketFixedSizeTransmissionHeader -{ - /// A unique numerical identifier given to this user message. Used to identify reliable messages on the network - MessageNumberType reliableMessageNumber; - ///The ID used as identification for ordering messages. Also included in sequenced messages - OrderingIndexType orderingIndex; - // Used only with sequenced messages - OrderingIndexType sequencingIndex; - ///What ordering channel this packet is on, if the reliability type uses ordering channels - unsigned char orderingChannel; - ///The ID of the split packet, if we have split packets. This is the maximum number of split messages we can send simultaneously per connection. - SplitPacketIdType splitPacketId; - ///If this is a split packet, the index into the array of subsplit packets - SplitPacketIndexType splitPacketIndex; - ///The size of the array of subsplit packets - SplitPacketIndexType splitPacketCount;; - ///How many bits long the data is - BitSize_t dataBitLength; - ///What type of reliability algorithm to use with this packet - PacketReliability reliability; - // Not endian safe - // unsigned char priority : 3; - // unsigned char reliability : 5; -}; - -/// Used in InternalPacket when pointing to sharedDataBlock, rather than allocating itself -struct InternalPacketRefCountedData -{ - unsigned char *sharedDataBlock; - unsigned int refCount; -}; - -/// Holds a user message, and related information -/// Don't use a constructor or destructor, due to the memory pool I am using -struct InternalPacket : public InternalPacketFixedSizeTransmissionHeader -{ - /// Identifies the order in which this number was sent. Used locally - MessageNumberType messageInternalOrder; - /// Has this message number been assigned yet? We don't assign until the message is actually sent. - /// This fixes a bug where pre-determining message numbers and then sending a message on a different channel creates a huge gap. - /// This causes performance problems and causes those messages to timeout. - bool messageNumberAssigned; - /// Was this packet number used this update to track windowing drops or increases? Each packet number is only used once per update. -// bool allowWindowUpdate; - ///When this packet was created - RakNet::TimeUS creationTime; - ///The resendNext time to take action on this packet - RakNet::TimeUS nextActionTime; - // Size of the header when encoded into a bitstream - BitSize_t headerLength; - /// Buffer is a pointer to the actual data, assuming this packet has data at all - unsigned char *data; - /// How to alloc and delete the data member - enum AllocationScheme - { - /// Data is allocated using rakMalloc. Just free it - NORMAL, - - /// data points to a larger block of data, where the larger block is reference counted. internalPacketRefCountedData is used in this case - REF_COUNTED, - - /// If allocation scheme is STACK, data points to stackData and should not be deallocated - /// This is only used when sending. Received packets are deallocated in RakPeer - STACK - } allocationScheme; - InternalPacketRefCountedData *refCountedData; - /// How many attempts we made at sending this message -// unsigned char timesSent; - /// The priority level of this packet - PacketPriority priority; - /// If the reliability type requires a receipt, then return this number with it - uint32_t sendReceiptSerial; - - // Used for the resend queue - // Linked list implementation so I can remove from the list via a pointer, without finding it in the list - InternalPacket *resendPrev, *resendNext,*unreliablePrev,*unreliableNext; - - unsigned char stackData[128]; -}; - -} // namespace RakNet - -#endif - +/// \file +/// \brief \b [Internal] A class which stores a user message, and all information associated with sending and receiving that message. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __INTERNAL_PACKET_H +#define __INTERNAL_PACKET_H + +#include "PacketPriority.h" +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" +#include "RakNetDefines.h" +#include "NativeTypes.h" +#include "RakNetDefines.h" +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 +#include "CCRakNetUDT.h" +#else +#include "CCRakNetSlidingWindow.h" +#endif + +namespace RakNet { + +typedef uint16_t SplitPacketIdType; +typedef uint32_t SplitPacketIndexType; + +/// This is the counter used for holding packet numbers, so we can detect duplicate packets. It should be large enough that if the variables +/// Internally assumed to be 4 bytes, but written as 3 bytes in ReliabilityLayer::WriteToBitStreamFromInternalPacket +typedef uint24_t MessageNumberType; + +/// This is the counter used for holding ordered packet numbers, so we can detect out-of-order packets. It should be large enough that if the variables +/// were to wrap, the newly wrapped values would no longer be in use. Warning: Too large of a value wastes bandwidth! +typedef MessageNumberType OrderingIndexType; + +typedef RakNet::TimeUS RemoteSystemTimeType; + +struct InternalPacketFixedSizeTransmissionHeader +{ + /// A unique numerical identifier given to this user message. Used to identify reliable messages on the network + MessageNumberType reliableMessageNumber; + ///The ID used as identification for ordering messages. Also included in sequenced messages + OrderingIndexType orderingIndex; + // Used only with sequenced messages + OrderingIndexType sequencingIndex; + ///What ordering channel this packet is on, if the reliability type uses ordering channels + unsigned char orderingChannel; + ///The ID of the split packet, if we have split packets. This is the maximum number of split messages we can send simultaneously per connection. + SplitPacketIdType splitPacketId; + ///If this is a split packet, the index into the array of subsplit packets + SplitPacketIndexType splitPacketIndex; + ///The size of the array of subsplit packets + SplitPacketIndexType splitPacketCount;; + ///How many bits long the data is + BitSize_t dataBitLength; + ///What type of reliability algorithm to use with this packet + PacketReliability reliability; + // Not endian safe + // unsigned char priority : 3; + // unsigned char reliability : 5; +}; + +/// Used in InternalPacket when pointing to sharedDataBlock, rather than allocating itself +struct InternalPacketRefCountedData +{ + unsigned char *sharedDataBlock; + unsigned int refCount; +}; + +/// Holds a user message, and related information +/// Don't use a constructor or destructor, due to the memory pool I am using +struct InternalPacket : public InternalPacketFixedSizeTransmissionHeader +{ + /// Identifies the order in which this number was sent. Used locally + MessageNumberType messageInternalOrder; + /// Has this message number been assigned yet? We don't assign until the message is actually sent. + /// This fixes a bug where pre-determining message numbers and then sending a message on a different channel creates a huge gap. + /// This causes performance problems and causes those messages to timeout. + bool messageNumberAssigned; + /// Was this packet number used this update to track windowing drops or increases? Each packet number is only used once per update. +// bool allowWindowUpdate; + ///When this packet was created + RakNet::TimeUS creationTime; + ///The resendNext time to take action on this packet + RakNet::TimeUS nextActionTime; + // Size of the header when encoded into a bitstream + BitSize_t headerLength; + /// Buffer is a pointer to the actual data, assuming this packet has data at all + unsigned char *data; + /// How to alloc and delete the data member + enum AllocationScheme + { + /// Data is allocated using rakMalloc. Just free it + NORMAL, + + /// data points to a larger block of data, where the larger block is reference counted. internalPacketRefCountedData is used in this case + REF_COUNTED, + + /// If allocation scheme is STACK, data points to stackData and should not be deallocated + /// This is only used when sending. Received packets are deallocated in RakPeer + STACK + } allocationScheme; + InternalPacketRefCountedData *refCountedData; + /// How many attempts we made at sending this message +// unsigned char timesSent; + /// The priority level of this packet + PacketPriority priority; + /// If the reliability type requires a receipt, then return this number with it + uint32_t sendReceiptSerial; + + // Used for the resend queue + // Linked list implementation so I can remove from the list via a pointer, without finding it in the list + InternalPacket *resendPrev, *resendNext,*unreliablePrev,*unreliableNext; + + unsigned char stackData[128]; +}; + +} // namespace RakNet + +#endif + diff --git a/src/raknet/Itoa.cpp b/src/raknet/Itoa.cpp index c5dc1a7..0d8a7ae 100755 --- a/src/raknet/Itoa.cpp +++ b/src/raknet/Itoa.cpp @@ -1,52 +1,52 @@ -#ifdef __cplusplus -extern "C" { -#endif - -// Fast itoa from http://www.jb.man.ac.uk/~slowe/cpp/itoa.html for Linux since it seems like Linux doesn't support this function. -// I modified it to remove the std dependencies. -char* Itoa( int value, char* result, int base ) - { - // check that the base if valid - if (base < 2 || base > 16) { *result = 0; return result; } - char* out = result; - int quotient = value; - - int absQModB; - - do { - // KevinJ - get rid of this dependency - //*out = "0123456789abcdef"[ std::abs( quotient % base ) ]; - absQModB=quotient % base; - if (absQModB < 0) - absQModB=-absQModB; - *out = "0123456789abcdef"[ absQModB ]; - ++out; - quotient /= base; - } while ( quotient ); - - // Only apply negative sign for base 10 - if ( value < 0 && base == 10) *out++ = '-'; - - // KevinJ - get rid of this dependency - // std::reverse( result, out ); - *out = 0; - - // KevinJ - My own reverse code - char *start = result; - char temp; - out--; - while (start < out) - { - temp=*start; - *start=*out; - *out=temp; - start++; - out--; - } - - return result; -} - -#ifdef __cplusplus -} -#endif +#ifdef __cplusplus +extern "C" { +#endif + +// Fast itoa from http://www.jb.man.ac.uk/~slowe/cpp/itoa.html for Linux since it seems like Linux doesn't support this function. +// I modified it to remove the std dependencies. +char* Itoa( int value, char* result, int base ) + { + // check that the base if valid + if (base < 2 || base > 16) { *result = 0; return result; } + char* out = result; + int quotient = value; + + int absQModB; + + do { + // KevinJ - get rid of this dependency + //*out = "0123456789abcdef"[ std::abs( quotient % base ) ]; + absQModB=quotient % base; + if (absQModB < 0) + absQModB=-absQModB; + *out = "0123456789abcdef"[ absQModB ]; + ++out; + quotient /= base; + } while ( quotient ); + + // Only apply negative sign for base 10 + if ( value < 0 && base == 10) *out++ = '-'; + + // KevinJ - get rid of this dependency + // std::reverse( result, out ); + *out = 0; + + // KevinJ - My own reverse code + char *start = result; + char temp; + out--; + while (start < out) + { + temp=*start; + *start=*out; + *out=temp; + start++; + out--; + } + + return result; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/raknet/Itoa.h b/src/raknet/Itoa.h index 1b41ad7..d7eb6a3 100755 --- a/src/raknet/Itoa.h +++ b/src/raknet/Itoa.h @@ -1,15 +1,15 @@ -#ifndef __RAK_ITOA_H -#define __RAK_ITOA_H - -#ifdef __cplusplus -extern "C" { -#endif - -char* Itoa( int value, char* result, int base ); - -#ifdef __cplusplus -} -#endif - - -#endif +#ifndef __RAK_ITOA_H +#define __RAK_ITOA_H + +#ifdef __cplusplus +extern "C" { +#endif + +char* Itoa( int value, char* result, int base ); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/raknet/Kbhit.h b/src/raknet/Kbhit.h index c311c15..8568d35 100755 --- a/src/raknet/Kbhit.h +++ b/src/raknet/Kbhit.h @@ -1,84 +1,84 @@ -/***************************************************************************** -kbhit() and getch() for Linux/UNIX -Chris Giese http://my.execpc.com/~geezer -Release date: ? -This code is public domain (no copyright). -You can do whatever you want with it. -*****************************************************************************/ -#if defined(_WIN32) -#include /* kbhit(), getch() */ - -#else -#include /* struct timeval, select() */ -/* ICANON, ECHO, TCSANOW, struct termios */ -#include /* tcgetattr(), tcsetattr() */ -#include /* atexit(), exit() */ -#include /* read() */ -#include /* printf() */ -#include /* memcpy */ - -static struct termios g_old_kbd_mode; -/***************************************************************************** -*****************************************************************************/ -static void cooked(void) -{ - tcsetattr(0, TCSANOW, &g_old_kbd_mode); -} -/***************************************************************************** -*****************************************************************************/ -static void raw(void) -{ - static char init; -/**/ - struct termios new_kbd_mode; - - if(init) - return; -/* put keyboard (stdin, actually) in raw, unbuffered mode */ - tcgetattr(0, &g_old_kbd_mode); - memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios)); - new_kbd_mode.c_lflag &= ~(ICANON /*| ECHO */ ); - new_kbd_mode.c_cc[VTIME] = 0; - new_kbd_mode.c_cc[VMIN] = 1; - tcsetattr(0, TCSANOW, &new_kbd_mode); -/* when we exit, go back to normal, "cooked" mode */ - atexit(cooked); - - init = 1; -} -/***************************************************************************** -*****************************************************************************/ -static int kbhit(void) -{ - struct timeval timeout; - fd_set read_handles; - int status; - - raw(); -/* check stdin (fd 0) for activity */ - FD_ZERO(&read_handles); - FD_SET(0, &read_handles); - timeout.tv_sec = timeout.tv_usec = 0; - status = select(0 + 1, &read_handles, NULL, NULL, &timeout); - if(status < 0) - { - printf("select() failed in kbhit()\n"); - exit(1); - } - return status; -} -/***************************************************************************** -*****************************************************************************/ -static int getch(void) -{ - unsigned char temp; - - raw(); -/* stdin = fd 0 */ - if(read(0, &temp, 1) != 1) - return 0; - return temp; -} -#endif - - +/***************************************************************************** +kbhit() and getch() for Linux/UNIX +Chris Giese http://my.execpc.com/~geezer +Release date: ? +This code is public domain (no copyright). +You can do whatever you want with it. +*****************************************************************************/ +#if defined(_WIN32) +#include /* kbhit(), getch() */ + +#else +#include /* struct timeval, select() */ +/* ICANON, ECHO, TCSANOW, struct termios */ +#include /* tcgetattr(), tcsetattr() */ +#include /* atexit(), exit() */ +#include /* read() */ +#include /* printf() */ +#include /* memcpy */ + +static struct termios g_old_kbd_mode; +/***************************************************************************** +*****************************************************************************/ +static void cooked(void) +{ + tcsetattr(0, TCSANOW, &g_old_kbd_mode); +} +/***************************************************************************** +*****************************************************************************/ +static void raw(void) +{ + static char init; +/**/ + struct termios new_kbd_mode; + + if(init) + return; +/* put keyboard (stdin, actually) in raw, unbuffered mode */ + tcgetattr(0, &g_old_kbd_mode); + memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios)); + new_kbd_mode.c_lflag &= ~(ICANON /*| ECHO */ ); + new_kbd_mode.c_cc[VTIME] = 0; + new_kbd_mode.c_cc[VMIN] = 1; + tcsetattr(0, TCSANOW, &new_kbd_mode); +/* when we exit, go back to normal, "cooked" mode */ + atexit(cooked); + + init = 1; +} +/***************************************************************************** +*****************************************************************************/ +static int kbhit(void) +{ + struct timeval timeout; + fd_set read_handles; + int status; + + raw(); +/* check stdin (fd 0) for activity */ + FD_ZERO(&read_handles); + FD_SET(0, &read_handles); + timeout.tv_sec = timeout.tv_usec = 0; + status = select(0 + 1, &read_handles, NULL, NULL, &timeout); + if(status < 0) + { + printf("select() failed in kbhit()\n"); + exit(1); + } + return status; +} +/***************************************************************************** +*****************************************************************************/ +static int getch(void) +{ + unsigned char temp; + + raw(); +/* stdin = fd 0 */ + if(read(0, &temp, 1) != 1) + return 0; + return temp; +} +#endif + + diff --git a/src/raknet/LinuxStrings.cpp b/src/raknet/LinuxStrings.cpp index e29ed14..93c8491 100755 --- a/src/raknet/LinuxStrings.cpp +++ b/src/raknet/LinuxStrings.cpp @@ -1,26 +1,26 @@ -#if (defined(__GNUC__) || defined(__ARMCC_VERSION) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32) -#include -#ifndef _stricmp -int _stricmp(const char* s1, const char* s2) -{ - return strcasecmp(s1,s2); -} -#endif -int _strnicmp(const char* s1, const char* s2, size_t n) -{ - return strncasecmp(s1,s2,n); -} -#ifndef __APPLE__ -char *_strlwr(char * str ) -{ - if (str==0) - return 0; - for (int i=0; str[i]; i++) - { - if (str[i]>='A' && str[i]<='Z') - str[i]+='a'-'A'; - } - return str; -} -#endif -#endif +#if (defined(__GNUC__) || defined(__ARMCC_VERSION) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32) +#include +#ifndef _stricmp +int _stricmp(const char* s1, const char* s2) +{ + return strcasecmp(s1,s2); +} +#endif +int _strnicmp(const char* s1, const char* s2, size_t n) +{ + return strncasecmp(s1,s2,n); +} +#ifndef __APPLE__ +char *_strlwr(char * str ) +{ + if (str==0) + return 0; + for (int i=0; str[i]; i++) + { + if (str[i]>='A' && str[i]<='Z') + str[i]+='a'-'A'; + } + return str; +} +#endif +#endif diff --git a/src/raknet/LinuxStrings.h b/src/raknet/LinuxStrings.h index 11c8589..f92b444 100755 --- a/src/raknet/LinuxStrings.h +++ b/src/raknet/LinuxStrings.h @@ -1,26 +1,26 @@ -#ifndef _GCC_WIN_STRINGS -#define _GCC_WIN_STRINGS - - - - - - - - - - #if (defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32) - #ifndef _stricmp - int _stricmp(const char* s1, const char* s2); - #endif - int _strnicmp(const char* s1, const char* s2, size_t n); -#ifndef __APPLE__ - char *_strlwr(char * str ); //this won't compile on OSX for some reason -#endif - #ifndef _vsnprintf - #define _vsnprintf vsnprintf - #endif - #endif - - -#endif // _GCC_WIN_STRINGS +#ifndef _GCC_WIN_STRINGS +#define _GCC_WIN_STRINGS + + + + + + + + + + #if (defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32) + #ifndef _stricmp + int _stricmp(const char* s1, const char* s2); + #endif + int _strnicmp(const char* s1, const char* s2, size_t n); +#ifndef __APPLE__ + char *_strlwr(char * str ); //this won't compile on OSX for some reason +#endif + #ifndef _vsnprintf + #define _vsnprintf vsnprintf + #endif + #endif + + +#endif // _GCC_WIN_STRINGS diff --git a/src/raknet/LocklessTypes.cpp b/src/raknet/LocklessTypes.cpp index 501e33b..650320a 100755 --- a/src/raknet/LocklessTypes.cpp +++ b/src/raknet/LocklessTypes.cpp @@ -1,42 +1,42 @@ -#include "LocklessTypes.h" - -using namespace RakNet; - -LocklessUint32_t::LocklessUint32_t() -{ - value=0; -} -LocklessUint32_t::LocklessUint32_t(uint32_t initial) -{ - value=initial; -} -uint32_t LocklessUint32_t::Increment(void) -{ -#ifdef _WIN32 - return (uint32_t) InterlockedIncrement(&value); -#elif defined(ANDROID) || defined(__S3E__) - uint32_t v; - mutex.Lock(); - ++value; - v=value; - mutex.Unlock(); - return v; -#else - return __sync_fetch_and_add (&value, (uint32_t) 1); -#endif -} -uint32_t LocklessUint32_t::Decrement(void) -{ -#ifdef _WIN32 - return (uint32_t) InterlockedDecrement(&value); -#elif defined(ANDROID) || defined(__S3E__) - uint32_t v; - mutex.Lock(); - --value; - v=value; - mutex.Unlock(); - return v; -#else - return __sync_fetch_and_add (&value, (uint32_t) -1); -#endif -} +#include "LocklessTypes.h" + +using namespace RakNet; + +LocklessUint32_t::LocklessUint32_t() +{ + value=0; +} +LocklessUint32_t::LocklessUint32_t(uint32_t initial) +{ + value=initial; +} +uint32_t LocklessUint32_t::Increment(void) +{ +#ifdef _WIN32 + return (uint32_t) InterlockedIncrement(&value); +#elif defined(ANDROID) || defined(__S3E__) + uint32_t v; + mutex.Lock(); + ++value; + v=value; + mutex.Unlock(); + return v; +#else + return __sync_fetch_and_add (&value, (uint32_t) 1); +#endif +} +uint32_t LocklessUint32_t::Decrement(void) +{ +#ifdef _WIN32 + return (uint32_t) InterlockedDecrement(&value); +#elif defined(ANDROID) || defined(__S3E__) + uint32_t v; + mutex.Lock(); + --value; + v=value; + mutex.Unlock(); + return v; +#else + return __sync_fetch_and_add (&value, (uint32_t) -1); +#endif +} diff --git a/src/raknet/LocklessTypes.h b/src/raknet/LocklessTypes.h index 441e6bd..36902a6 100755 --- a/src/raknet/LocklessTypes.h +++ b/src/raknet/LocklessTypes.h @@ -1,40 +1,40 @@ -#ifndef __LOCKLESS_TYPES_H -#define __LOCKLESS_TYPES_H - -#include "Export.h" -#include "NativeTypes.h" -#include "WindowsIncludes.h" -#if defined(ANDROID) || defined(__S3E__) -// __sync_fetch_and_add not supported apparently -#include "SimpleMutex.h" -#endif - -namespace RakNet -{ - -class RAK_DLL_EXPORT LocklessUint32_t -{ -public: - LocklessUint32_t(); - explicit LocklessUint32_t(uint32_t initial); - // Returns variable value after changing it - uint32_t Increment(void); - // Returns variable value after changing it - uint32_t Decrement(void); - volatile uint32_t GetValue(void) const {return value;} - -protected: -#ifdef _WIN32 - volatile LONG value; -#elif defined(ANDROID) || defined(__S3E__) - // __sync_fetch_and_add not supported apparently - SimpleMutex mutex; - uint32_t value; -#else - volatile uint32_t value; -#endif -}; - -}; - -#endif +#ifndef __LOCKLESS_TYPES_H +#define __LOCKLESS_TYPES_H + +#include "Export.h" +#include "NativeTypes.h" +#include "WindowsIncludes.h" +#if defined(ANDROID) || defined(__S3E__) +// __sync_fetch_and_add not supported apparently +#include "SimpleMutex.h" +#endif + +namespace RakNet +{ + +class RAK_DLL_EXPORT LocklessUint32_t +{ +public: + LocklessUint32_t(); + explicit LocklessUint32_t(uint32_t initial); + // Returns variable value after changing it + uint32_t Increment(void); + // Returns variable value after changing it + uint32_t Decrement(void); + volatile uint32_t GetValue(void) const {return value;} + +protected: +#ifdef _WIN32 + volatile LONG value; +#elif defined(ANDROID) || defined(__S3E__) + // __sync_fetch_and_add not supported apparently + SimpleMutex mutex; + uint32_t value; +#else + volatile uint32_t value; +#endif +}; + +}; + +#endif diff --git a/src/raknet/LogCommandParser.cpp b/src/raknet/LogCommandParser.cpp index 50ed7fa..c6bbe77 100755 --- a/src/raknet/LogCommandParser.cpp +++ b/src/raknet/LogCommandParser.cpp @@ -1,279 +1,279 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_LogCommandParser==1 - -#include "LogCommandParser.h" -#include "TransportInterface.h" - -#include - -#include -#include -#include - -#include "LinuxStrings.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(LogCommandParser,LogCommandParser); - -LogCommandParser::LogCommandParser() -{ - RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Subscribe","[] - Subscribes to a named channel, or all channels"); - RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Unsubscribe","[] - Unsubscribes from a named channel, or all channels"); - memset(channelNames,0,sizeof(channelNames)); -} -LogCommandParser::~LogCommandParser() -{ -} -bool LogCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString) -{ - (void) originalString; - - if (strcmp(command, "Subscribe")==0) - { - unsigned channelIndex; - if (numParameters==0) - { - Subscribe(systemAddress, 0); - transport->Send(systemAddress, "Subscribed to all channels.\r\n"); - } - else if (numParameters==1) - { - if ((channelIndex=Subscribe(systemAddress, parameterList[0]))!=(unsigned)-1) - { - transport->Send(systemAddress, "You are now subscribed to channel %s.\r\n", channelNames[channelIndex]); - } - else - { - transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]); - PrintChannels(systemAddress, transport); - } - } - else - { - transport->Send(systemAddress, "Subscribe takes either 0 or 1 parameters.\r\n"); - } - } - else if (strcmp(command, "Unsubscribe")==0) - { - unsigned channelIndex; - if (numParameters==0) - { - Unsubscribe(systemAddress, 0); - transport->Send(systemAddress, "Unsubscribed from all channels.\r\n"); - } - else if (numParameters==1) - { - if ((channelIndex=Unsubscribe(systemAddress, parameterList[0]))!=(unsigned)-1) - { - transport->Send(systemAddress, "You are now unsubscribed from channel %s.\r\n", channelNames[channelIndex]); - } - else - { - transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]); - PrintChannels(systemAddress, transport); - } - } - else - { - transport->Send(systemAddress, "Unsubscribe takes either 0 or 1 parameters.\r\n"); - } - } - - return true; -} -const char *LogCommandParser::GetName(void) const -{ - return "Logger"; -} -void LogCommandParser::SendHelp(TransportInterface *transport, const SystemAddress &systemAddress) -{ - transport->Send(systemAddress, "The logger will accept user log data via the Log(...) function.\r\n"); - transport->Send(systemAddress, "Each log is associated with a named channel.\r\n"); - transport->Send(systemAddress, "You can subscribe to or unsubscribe from named channels.\r\n"); - PrintChannels(systemAddress, transport); -} -void LogCommandParser::AddChannel(const char *channelName) -{ - unsigned channelIndex=0; - channelIndex = GetChannelIndexFromName(channelName); - // Each channel can only be added once. - RakAssert(channelIndex==(unsigned)-1); - - unsigned i; - for (i=0; i < 32; i++) - { - if (channelNames[i]==0) - { - // Assuming a persistent static string. - channelNames[i]=channelName; - return; - } - } - - // No more available channels - max 32 with this implementation where I save subscribed channels with bit operations - RakAssert(0); -} -void LogCommandParser::WriteLog(const char *channelName, const char *format, ...) -{ - if (channelName==0 || format==0) - return; - - unsigned channelIndex; - channelIndex = GetChannelIndexFromName(channelName); - if (channelIndex==(unsigned)-1) - { - AddChannel(channelName); - } - - char text[REMOTE_MAX_TEXT_INPUT]; - va_list ap; - va_start(ap, format); - _vsnprintf(text, REMOTE_MAX_TEXT_INPUT, format, ap); - va_end(ap); - text[REMOTE_MAX_TEXT_INPUT-1]=0; - - // Make sure that text ends in \r\n - int textLen; - textLen=(int)strlen(text); - if (textLen==0) - return; - if (text[textLen-1]=='\n') - { - text[textLen-1]=0; - } - if (textLen < REMOTE_MAX_TEXT_INPUT-4) - strcat(text, "\r\n"); - else - { - text[textLen-3]='\r'; - text[textLen-2]='\n'; - text[textLen-1]=0; - } - - // For each user that subscribes to this channel, send to them. - unsigned i; - for (i=0; i < remoteUsers.Size(); i++) - { - if (remoteUsers[i].channels & (1 << channelIndex)) - { - trans->Send(remoteUsers[i].systemAddress, text); - } - } -} -void LogCommandParser::PrintChannels(const SystemAddress &systemAddress, TransportInterface *transport) const -{ - unsigned i; - bool anyChannels=false; - transport->Send(systemAddress, "CHANNELS:\r\n"); - for (i=0; i < 32; i++) - { - if (channelNames[i]) - { - transport->Send(systemAddress, "%i. %s\r\n", i+1,channelNames[i]); - anyChannels=true; - } - } - if (anyChannels==false) - transport->Send(systemAddress, "None.\r\n"); -} -void LogCommandParser::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport) -{ - (void) systemAddress; - (void) transport; -} -void LogCommandParser::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport) -{ - (void) transport; - Unsubscribe(systemAddress, 0); -} -unsigned LogCommandParser::Unsubscribe(const SystemAddress &systemAddress, const char *channelName) -{ - unsigned i; - for (i=0; i < remoteUsers.Size(); i++) - { - if (remoteUsers[i].systemAddress==systemAddress) - { - if (channelName==0) - { - // Unsubscribe from all and delete this user. - remoteUsers[i]=remoteUsers[remoteUsers.Size()-1]; - remoteUsers.RemoveFromEnd(); - return 0; - } - else - { - unsigned channelIndex; - channelIndex = GetChannelIndexFromName(channelName); - if (channelIndex!=(unsigned)-1) - { - remoteUsers[i].channels&=0xFFFF ^ (1< + +#include +#include +#include + +#include "LinuxStrings.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(LogCommandParser,LogCommandParser); + +LogCommandParser::LogCommandParser() +{ + RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Subscribe","[] - Subscribes to a named channel, or all channels"); + RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Unsubscribe","[] - Unsubscribes from a named channel, or all channels"); + memset(channelNames,0,sizeof(channelNames)); +} +LogCommandParser::~LogCommandParser() +{ +} +bool LogCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString) +{ + (void) originalString; + + if (strcmp(command, "Subscribe")==0) + { + unsigned channelIndex; + if (numParameters==0) + { + Subscribe(systemAddress, 0); + transport->Send(systemAddress, "Subscribed to all channels.\r\n"); + } + else if (numParameters==1) + { + if ((channelIndex=Subscribe(systemAddress, parameterList[0]))!=(unsigned)-1) + { + transport->Send(systemAddress, "You are now subscribed to channel %s.\r\n", channelNames[channelIndex]); + } + else + { + transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]); + PrintChannels(systemAddress, transport); + } + } + else + { + transport->Send(systemAddress, "Subscribe takes either 0 or 1 parameters.\r\n"); + } + } + else if (strcmp(command, "Unsubscribe")==0) + { + unsigned channelIndex; + if (numParameters==0) + { + Unsubscribe(systemAddress, 0); + transport->Send(systemAddress, "Unsubscribed from all channels.\r\n"); + } + else if (numParameters==1) + { + if ((channelIndex=Unsubscribe(systemAddress, parameterList[0]))!=(unsigned)-1) + { + transport->Send(systemAddress, "You are now unsubscribed from channel %s.\r\n", channelNames[channelIndex]); + } + else + { + transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]); + PrintChannels(systemAddress, transport); + } + } + else + { + transport->Send(systemAddress, "Unsubscribe takes either 0 or 1 parameters.\r\n"); + } + } + + return true; +} +const char *LogCommandParser::GetName(void) const +{ + return "Logger"; +} +void LogCommandParser::SendHelp(TransportInterface *transport, const SystemAddress &systemAddress) +{ + transport->Send(systemAddress, "The logger will accept user log data via the Log(...) function.\r\n"); + transport->Send(systemAddress, "Each log is associated with a named channel.\r\n"); + transport->Send(systemAddress, "You can subscribe to or unsubscribe from named channels.\r\n"); + PrintChannels(systemAddress, transport); +} +void LogCommandParser::AddChannel(const char *channelName) +{ + unsigned channelIndex=0; + channelIndex = GetChannelIndexFromName(channelName); + // Each channel can only be added once. + RakAssert(channelIndex==(unsigned)-1); + + unsigned i; + for (i=0; i < 32; i++) + { + if (channelNames[i]==0) + { + // Assuming a persistent static string. + channelNames[i]=channelName; + return; + } + } + + // No more available channels - max 32 with this implementation where I save subscribed channels with bit operations + RakAssert(0); +} +void LogCommandParser::WriteLog(const char *channelName, const char *format, ...) +{ + if (channelName==0 || format==0) + return; + + unsigned channelIndex; + channelIndex = GetChannelIndexFromName(channelName); + if (channelIndex==(unsigned)-1) + { + AddChannel(channelName); + } + + char text[REMOTE_MAX_TEXT_INPUT]; + va_list ap; + va_start(ap, format); + _vsnprintf(text, REMOTE_MAX_TEXT_INPUT, format, ap); + va_end(ap); + text[REMOTE_MAX_TEXT_INPUT-1]=0; + + // Make sure that text ends in \r\n + int textLen; + textLen=(int)strlen(text); + if (textLen==0) + return; + if (text[textLen-1]=='\n') + { + text[textLen-1]=0; + } + if (textLen < REMOTE_MAX_TEXT_INPUT-4) + strcat(text, "\r\n"); + else + { + text[textLen-3]='\r'; + text[textLen-2]='\n'; + text[textLen-1]=0; + } + + // For each user that subscribes to this channel, send to them. + unsigned i; + for (i=0; i < remoteUsers.Size(); i++) + { + if (remoteUsers[i].channels & (1 << channelIndex)) + { + trans->Send(remoteUsers[i].systemAddress, text); + } + } +} +void LogCommandParser::PrintChannels(const SystemAddress &systemAddress, TransportInterface *transport) const +{ + unsigned i; + bool anyChannels=false; + transport->Send(systemAddress, "CHANNELS:\r\n"); + for (i=0; i < 32; i++) + { + if (channelNames[i]) + { + transport->Send(systemAddress, "%i. %s\r\n", i+1,channelNames[i]); + anyChannels=true; + } + } + if (anyChannels==false) + transport->Send(systemAddress, "None.\r\n"); +} +void LogCommandParser::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport) +{ + (void) systemAddress; + (void) transport; +} +void LogCommandParser::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport) +{ + (void) transport; + Unsubscribe(systemAddress, 0); +} +unsigned LogCommandParser::Unsubscribe(const SystemAddress &systemAddress, const char *channelName) +{ + unsigned i; + for (i=0; i < remoteUsers.Size(); i++) + { + if (remoteUsers[i].systemAddress==systemAddress) + { + if (channelName==0) + { + // Unsubscribe from all and delete this user. + remoteUsers[i]=remoteUsers[remoteUsers.Size()-1]; + remoteUsers.RemoveFromEnd(); + return 0; + } + else + { + unsigned channelIndex; + channelIndex = GetChannelIndexFromName(channelName); + if (channelIndex!=(unsigned)-1) + { + remoteUsers[i].channels&=0xFFFF ^ (1< remoteUsers; - - /// Names of the channels at each bit, or 0 for an unused channel - const char *channelNames[32]; - - /// This is so I can save the current transport provider, solely so I can use it without having the user pass it to Log - TransportInterface *trans; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains LogCommandParser , Used to send logs to connected consoles +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_LogCommandParser==1 + +#ifndef __LOG_COMMAND_PARSER +#define __LOG_COMMAND_PARSER + +#include "CommandParserInterface.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief Adds the ability to send logging output to a remote console +class RAK_DLL_EXPORT LogCommandParser : public CommandParserInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(LogCommandParser) + + LogCommandParser(); + ~LogCommandParser(); + + /// Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + const char *GetName(void) const; + + /// A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress); + + /// All logs must be associated with a channel. This is a filter so that remote clients only get logs for a system they care about. + // If you call Log with a channel that is unknown, that channel will automatically be added + /// \param[in] channelName A persistent string naming the channel. Don't deallocate this string. + void AddChannel(const char *channelName); + + /// Write a log to a channel. + /// Logs are not buffered, so only remote consoles connected and subscribing at the time you write will get the output. + /// \param[in] format Same as RAKNET_DEBUG_PRINTF() + /// \param[in] ... Same as RAKNET_DEBUG_PRINTF() + void WriteLog(const char *channelName, const char *format, ...); + + /// A callback for when \a systemAddress has connected to us. + /// \param[in] systemAddress The player that has connected. + /// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players. + void OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport); + + /// A callback for when \a systemAddress has disconnected, either gracefully or forcefully + /// \param[in] systemAddress The player that has disconnected. + /// \param[in] transport The transport interface that sent us this information. + void OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport); + + /// This is called every time transport interface is registered. If you want to save a copy of the TransportInterface pointer + /// This is the place to do it + /// \param[in] transport The new TransportInterface + void OnTransportChange(TransportInterface *transport); +protected: + /// Sends the currently active channels to the user + /// \param[in] systemAddress The player to send to + /// \param[in] transport The transport interface to use to send the channels + void PrintChannels(const SystemAddress &systemAddress, TransportInterface *transport) const; + + /// Unsubscribe a user from a channel (or from all channels) + /// \param[in] systemAddress The player to unsubscribe to + /// \param[in] channelName If 0, then unsubscribe from all channels. Otherwise unsubscribe from the named channel + unsigned Unsubscribe(const SystemAddress &systemAddress, const char *channelName); + + /// Subscribe a user to a channel (or to all channels) + /// \param[in] systemAddress The player to subscribe to + /// \param[in] channelName If 0, then subscribe from all channels. Otherwise subscribe to the named channel + unsigned Subscribe(const SystemAddress &systemAddress, const char *channelName); + + /// Given the name of a channel, return the index into channelNames where it is located + /// \param[in] channelName The name of the channel + unsigned GetChannelIndexFromName(const char *channelName); + + /// One of these structures is created per player + struct SystemAddressAndChannel + { + /// The ID of the player + SystemAddress systemAddress; + + /// Bitwise representations of the channels subscribed to. If bit 0 is set, then we subscribe to channelNames[0] and so on. + unsigned channels; + }; + + /// The list of remote users. Added to when users subscribe, removed when they disconnect or unsubscribe + DataStructures::List remoteUsers; + + /// Names of the channels at each bit, or 0 for an unused channel + const char *channelNames[32]; + + /// This is so I can save the current transport provider, solely so I can use it without having the user pass it to Log + TransportInterface *trans; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/MTUSize.h b/src/raknet/MTUSize.h index 0786b4f..4a627b6 100755 --- a/src/raknet/MTUSize.h +++ b/src/raknet/MTUSize.h @@ -1,31 +1,31 @@ -/// \file -/// \brief \b [Internal] Defines the default maximum transfer unit. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef MAXIMUM_MTU_SIZE - -/// \li \em 17914 16 Mbit/Sec Token Ring -/// \li \em 4464 4 Mbits/Sec Token Ring -/// \li \em 4352 FDDI -/// \li \em 1500. The largest Ethernet packet size \b recommended. This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. -/// \li \em 1492. The size PPPoE prefers. -/// \li \em 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) -/// \li \em 1468. The size DHCP prefers. -/// \li \em 1460. Usable by AOL if you don't have large email attachments, etc. -/// \li \em 1430. The size VPN and PPTP prefer. -/// \li \em 1400. Maximum size for AOL DSL. -/// \li \em 576. Typical value to connect to dial-up ISPs. -/// The largest value for an UDP datagram - - - -#define MAXIMUM_MTU_SIZE 1492 - - -#define MINIMUM_MTU_SIZE 400 - -#endif +/// \file +/// \brief \b [Internal] Defines the default maximum transfer unit. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef MAXIMUM_MTU_SIZE + +/// \li \em 17914 16 Mbit/Sec Token Ring +/// \li \em 4464 4 Mbits/Sec Token Ring +/// \li \em 4352 FDDI +/// \li \em 1500. The largest Ethernet packet size \b recommended. This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. +/// \li \em 1492. The size PPPoE prefers. +/// \li \em 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) +/// \li \em 1468. The size DHCP prefers. +/// \li \em 1460. Usable by AOL if you don't have large email attachments, etc. +/// \li \em 1430. The size VPN and PPTP prefer. +/// \li \em 1400. Maximum size for AOL DSL. +/// \li \em 576. Typical value to connect to dial-up ISPs. +/// The largest value for an UDP datagram + + + +#define MAXIMUM_MTU_SIZE 1492 + + +#define MINIMUM_MTU_SIZE 400 + +#endif diff --git a/src/raknet/MessageFilter.cpp b/src/raknet/MessageFilter.cpp index 28fa423..c2fc37a 100755 --- a/src/raknet/MessageFilter.cpp +++ b/src/raknet/MessageFilter.cpp @@ -1,408 +1,408 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_MessageFilter==1 - -#include "MessageFilter.h" -#include "RakAssert.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "RakAssert.h" -#include "RakPeerInterface.h" -#include "PacketizedTCP.h" -#include "BitStream.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -int RakNet::MessageFilterStrComp( char *const &key,char *const &data ) -{ - return strcmp(key,data); -} - -int RakNet::FilterSetComp( const int &key, FilterSet * const &data ) -{ - if (key < data->filterSetID) - return -1; - else if (key==data->filterSetID) - return 0; - else - return 1; -} -STATIC_FACTORY_DEFINITIONS(MessageFilter,MessageFilter); - -MessageFilter::MessageFilter() -{ - whenLastTimeoutCheck=RakNet::GetTime(); -} -MessageFilter::~MessageFilter() -{ - Clear(); -} -void MessageFilter::SetAutoAddNewConnectionsToFilter(int filterSetID) -{ - autoAddNewConnectionsToFilter=filterSetID; -} -void MessageFilter::SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID) -{ - RakAssert(messageIDStart <= messageIDEnd); - FilterSet *filterSet = GetFilterSetByID(filterSetID); - int i; - for (i=messageIDStart; i <= messageIDEnd; ++i) - filterSet->allowedIDs[i]=allow; -} -void MessageFilter::SetAllowRPC4(bool allow, const char* uniqueID, int filterSetID) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - bool objectExists; - unsigned int idx = filterSet->allowedRPC4.GetIndexFromKey(uniqueID, &objectExists); - if (allow) - { - if (objectExists==false) - { - filterSet->allowedRPC4.InsertAtIndex(uniqueID, idx, _FILE_AND_LINE_); - filterSet->allowedIDs[ID_RPC_PLUGIN]=true; - } - } - else - { - if (objectExists==true) - { - filterSet->allowedRPC4.RemoveAtIndex(idx); - if (filterSet->allowedRPC4.Size()==0) - { - filterSet->allowedIDs[ID_RPC_PLUGIN]=false; - } - } - } -} -void MessageFilter::SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNet::TimeMS banTimeMS, int filterSetID) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - filterSet->kickOnDisallowedMessage=kickOnDisallowed; - filterSet->disallowedMessageBanTimeMS=banTimeMS; - filterSet->banOnDisallowedMessage=banOnDisallowed; -} -void MessageFilter::SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID)) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - filterSet->invalidMessageCallback=invalidMessageCallback; - filterSet->disallowedCallbackUserData=userData; -} -void MessageFilter::SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData)) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - filterSet->timeoutCallback=invalidMessageCallback; - filterSet->timeoutUserData=userData; -} -void MessageFilter::SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNet::TimeMS banTimeMS, int filterSetID) -{ - FilterSet *filterSet = GetFilterSetByID(filterSetID); - filterSet->maxMemberTimeMS=allowedTimeMS; - filterSet->banOnFilterTimeExceed=banOnExceed; - filterSet->timeExceedBanTimeMS=banTimeMS; -} -int MessageFilter::GetSystemFilterSet(AddressOrGUID systemAddress) -{ -// bool objectExists; -// unsigned index = systemList.GetIndexFromKey(systemAddress, &objectExists); -// if (objectExists==false) -// return -1; -// else -// return systemList[index].filter->filterSetID; - - DataStructures::HashIndex index = systemList.GetIndexOf(systemAddress); - if (index.IsInvalid()) - return -1; - else - return systemList.ItemAtIndex(index).filter->filterSetID; -} -void MessageFilter::SetSystemFilterSet(AddressOrGUID addressOrGUID, int filterSetID) -{ - // Allocate this filter set if it doesn't exist. - RakAssert(addressOrGUID.IsUndefined()==false); -// bool objectExists; -// unsigned index = systemList.GetIndexFromKey(addressOrGUID, &objectExists); -// if (objectExists==false) - DataStructures::HashIndex index = systemList.GetIndexOf(addressOrGUID); - if (index.IsInvalid()) - { - if (filterSetID<0) - return; - - FilteredSystem filteredSystem; - filteredSystem.filter = GetFilterSetByID(filterSetID); - // filteredSystem.addressOrGUID=addressOrGUID; - filteredSystem.timeEnteredThisSet=RakNet::GetTimeMS(); - // systemList.Insert(addressOrGUID, filteredSystem, true, _FILE_AND_LINE_); - systemList.Push(addressOrGUID,filteredSystem,_FILE_AND_LINE_); - } - else - { - if (filterSetID>=0) - { - FilterSet *filterSet = GetFilterSetByID(filterSetID); - systemList.ItemAtIndex(index).timeEnteredThisSet=RakNet::GetTimeMS(); - systemList.ItemAtIndex(index).filter=filterSet; - } - else - { - systemList.RemoveAtIndex(index, _FILE_AND_LINE_); - } - } -} -unsigned MessageFilter::GetSystemCount(int filterSetID) const -{ - if (filterSetID==-1) - { - return systemList.Size(); - } - else - { - unsigned i; - unsigned count=0; - DataStructures::List< FilteredSystem > itemList; - DataStructures::List< AddressOrGUID > keyList; - systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); - for (i=0; i < itemList.Size(); i++) - if (itemList[i].filter->filterSetID==filterSetID) - ++count; - return count; - } -} -unsigned MessageFilter::GetFilterSetCount(void) const -{ - return filterList.Size(); -} -int MessageFilter::GetFilterSetIDByIndex(unsigned index) -{ - return filterList[index]->filterSetID; -} -void MessageFilter::DeleteFilterSet(int filterSetID) -{ - FilterSet *filterSet; - bool objectExists; - unsigned i,index; - index = filterList.GetIndexFromKey(filterSetID, &objectExists); - if (objectExists) - { - filterSet=filterList[index]; - DeallocateFilterSet(filterSet); - filterList.RemoveAtIndex(index); - - DataStructures::List< FilteredSystem > itemList; - DataStructures::List< AddressOrGUID > keyList; - systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); - for (i=0; i < itemList.Size(); i++) - { - if (itemList[i].filter==filterSet) - { - systemList.Remove(keyList[i], _FILE_AND_LINE_); - } - } - - /* - // Don't reference this pointer any longer - i=0; - while (i < systemList.Size()) - { - if (systemList[i].filter==filterSet) - systemList.RemoveAtIndex(i); - else - ++i; - } - */ - } -} -void MessageFilter::Clear(void) -{ - unsigned i; - systemList.Clear(_FILE_AND_LINE_); - for (i=0; i < filterList.Size(); i++) - DeallocateFilterSet(filterList[i]); - filterList.Clear(false, _FILE_AND_LINE_); -} -void MessageFilter::DeallocateFilterSet(FilterSet* filterSet) -{ - RakNet::OP_DELETE(filterSet, _FILE_AND_LINE_); -} -FilterSet* MessageFilter::GetFilterSetByID(int filterSetID) -{ - RakAssert(filterSetID>=0); - bool objectExists; - unsigned index; - index = filterList.GetIndexFromKey(filterSetID, &objectExists); - if (objectExists) - return filterList[index]; - else - { - FilterSet *newFilterSet = RakNet::OP_NEW( _FILE_AND_LINE_ ); - memset(newFilterSet->allowedIDs, 0, MESSAGE_FILTER_MAX_MESSAGE_ID * sizeof(bool)); - newFilterSet->banOnFilterTimeExceed=false; - newFilterSet->kickOnDisallowedMessage=false; - newFilterSet->banOnDisallowedMessage=false; - newFilterSet->disallowedMessageBanTimeMS=0; - newFilterSet->timeExceedBanTimeMS=0; - newFilterSet->maxMemberTimeMS=0; - newFilterSet->filterSetID=filterSetID; - newFilterSet->invalidMessageCallback=0; - newFilterSet->timeoutCallback=0; - newFilterSet->timeoutUserData=0; - filterList.Insert(filterSetID, newFilterSet, true, _FILE_AND_LINE_); - return newFilterSet; - } -} -void MessageFilter::OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID) -{ - if (filterSet->invalidMessageCallback) - filterSet->invalidMessageCallback(rakPeerInterface, systemAddress, filterSet->filterSetID, filterSet->disallowedCallbackUserData, messageID); - if (filterSet->banOnDisallowedMessage && rakPeerInterface) - { - char str1[64]; - systemAddress.systemAddress.ToString(false, str1); - rakPeerInterface->AddToBanList(str1, filterSet->disallowedMessageBanTimeMS); - } - if (filterSet->kickOnDisallowedMessage) - { - if (rakPeerInterface) - rakPeerInterface->CloseConnection(systemAddress, true, 0); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->CloseConnection(systemAddress.systemAddress); -#endif - } -} -void MessageFilter::Update(void) -{ - // Update all timers for all systems. If those systems' filter sets are expired, take the appropriate action. - RakNet::Time curTime = RakNet::GetTime(); - if (GreaterThan(curTime - 1000, whenLastTimeoutCheck)) - { - DataStructures::List< FilteredSystem > itemList; - DataStructures::List< AddressOrGUID > keyList; - systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); - - unsigned int index; - for (index=0; index < itemList.Size(); index++) - { - if (itemList[index].filter && - itemList[index].filter->maxMemberTimeMS>0 && - curTime-itemList[index].timeEnteredThisSet >= itemList[index].filter->maxMemberTimeMS) - { - if (itemList[index].filter->timeoutCallback) - itemList[index].filter->timeoutCallback(rakPeerInterface, keyList[index], itemList[index].filter->filterSetID, itemList[index].filter->timeoutUserData); - - if (itemList[index].filter->banOnFilterTimeExceed && rakPeerInterface) - { - char str1[64]; - keyList[index].ToString(false, str1); - rakPeerInterface->AddToBanList(str1, itemList[index].filter->timeExceedBanTimeMS); - } - if (rakPeerInterface) - rakPeerInterface->CloseConnection(keyList[index], true, 0); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->CloseConnection(keyList[index].systemAddress); -#endif - - systemList.Remove(keyList[index], _FILE_AND_LINE_); - } - } - - whenLastTimeoutCheck=curTime+1000; - } -} -void MessageFilter::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) systemAddress; - (void) rakNetGUID; - (void) isIncoming; - - AddressOrGUID aog; - aog.rakNetGuid=rakNetGUID; - aog.systemAddress=systemAddress; - - // New system, automatically assign to filter set if appropriate - if (autoAddNewConnectionsToFilter>=0 && systemList.HasData(aog)==false) - SetSystemFilterSet(aog, autoAddNewConnectionsToFilter); -} -void MessageFilter::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) rakNetGUID; - (void) lostConnectionReason; - - AddressOrGUID aog; - aog.rakNetGuid=rakNetGUID; - aog.systemAddress=systemAddress; - - // Lost system, remove from the list - systemList.Remove(aog, _FILE_AND_LINE_); -} - PluginReceiveResult MessageFilter::OnReceive(Packet *packet) -{ - DataStructures::HashIndex index; - unsigned char messageId; - - switch (packet->data[0]) - { - case ID_NEW_INCOMING_CONNECTION: - case ID_CONNECTION_REQUEST_ACCEPTED: - case ID_CONNECTION_LOST: - case ID_DISCONNECTION_NOTIFICATION: - case ID_CONNECTION_ATTEMPT_FAILED: - case ID_NO_FREE_INCOMING_CONNECTIONS: - case ID_IP_RECENTLY_CONNECTED: - case ID_CONNECTION_BANNED: - case ID_INVALID_PASSWORD: - case ID_UNCONNECTED_PONG: - case ID_ALREADY_CONNECTED: - case ID_ADVERTISE_SYSTEM: - case ID_REMOTE_DISCONNECTION_NOTIFICATION: - case ID_REMOTE_CONNECTION_LOST: - case ID_REMOTE_NEW_INCOMING_CONNECTION: - case ID_DOWNLOAD_PROGRESS: - break; - default: - if (packet->data[0]==ID_TIMESTAMP) - { - if (packet->lengthdata[sizeof(MessageID) + sizeof(RakNet::TimeMS)]; - } - else - messageId=packet->data[0]; - // If this system is filtered, check if this message is allowed. If not allowed, return RR_STOP_PROCESSING_AND_DEALLOCATE - // index = systemList.GetIndexFromKey(packet->addressOrGUID, &objectExists); - index = systemList.GetIndexOf(packet); - if (index.IsInvalid()) - break; - if (systemList.ItemAtIndex(index).filter->allowedIDs[messageId]==false) - { - OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - if (packet->data[0]==ID_RPC_PLUGIN) - { - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - RakNet::RakString functionName; - bsIn.ReadCompressed(functionName); - if (systemList.ItemAtIndex(index).filter->allowedRPC4.HasData(functionName)==false) - { - OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - - break; - } - - return RR_CONTINUE_PROCESSING; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_MessageFilter==1 + +#include "MessageFilter.h" +#include "RakAssert.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "RakAssert.h" +#include "RakPeerInterface.h" +#include "PacketizedTCP.h" +#include "BitStream.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +int RakNet::MessageFilterStrComp( char *const &key,char *const &data ) +{ + return strcmp(key,data); +} + +int RakNet::FilterSetComp( const int &key, FilterSet * const &data ) +{ + if (key < data->filterSetID) + return -1; + else if (key==data->filterSetID) + return 0; + else + return 1; +} +STATIC_FACTORY_DEFINITIONS(MessageFilter,MessageFilter); + +MessageFilter::MessageFilter() +{ + whenLastTimeoutCheck=RakNet::GetTime(); +} +MessageFilter::~MessageFilter() +{ + Clear(); +} +void MessageFilter::SetAutoAddNewConnectionsToFilter(int filterSetID) +{ + autoAddNewConnectionsToFilter=filterSetID; +} +void MessageFilter::SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID) +{ + RakAssert(messageIDStart <= messageIDEnd); + FilterSet *filterSet = GetFilterSetByID(filterSetID); + int i; + for (i=messageIDStart; i <= messageIDEnd; ++i) + filterSet->allowedIDs[i]=allow; +} +void MessageFilter::SetAllowRPC4(bool allow, const char* uniqueID, int filterSetID) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + bool objectExists; + unsigned int idx = filterSet->allowedRPC4.GetIndexFromKey(uniqueID, &objectExists); + if (allow) + { + if (objectExists==false) + { + filterSet->allowedRPC4.InsertAtIndex(uniqueID, idx, _FILE_AND_LINE_); + filterSet->allowedIDs[ID_RPC_PLUGIN]=true; + } + } + else + { + if (objectExists==true) + { + filterSet->allowedRPC4.RemoveAtIndex(idx); + if (filterSet->allowedRPC4.Size()==0) + { + filterSet->allowedIDs[ID_RPC_PLUGIN]=false; + } + } + } +} +void MessageFilter::SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNet::TimeMS banTimeMS, int filterSetID) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + filterSet->kickOnDisallowedMessage=kickOnDisallowed; + filterSet->disallowedMessageBanTimeMS=banTimeMS; + filterSet->banOnDisallowedMessage=banOnDisallowed; +} +void MessageFilter::SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID)) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + filterSet->invalidMessageCallback=invalidMessageCallback; + filterSet->disallowedCallbackUserData=userData; +} +void MessageFilter::SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData)) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + filterSet->timeoutCallback=invalidMessageCallback; + filterSet->timeoutUserData=userData; +} +void MessageFilter::SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNet::TimeMS banTimeMS, int filterSetID) +{ + FilterSet *filterSet = GetFilterSetByID(filterSetID); + filterSet->maxMemberTimeMS=allowedTimeMS; + filterSet->banOnFilterTimeExceed=banOnExceed; + filterSet->timeExceedBanTimeMS=banTimeMS; +} +int MessageFilter::GetSystemFilterSet(AddressOrGUID systemAddress) +{ +// bool objectExists; +// unsigned index = systemList.GetIndexFromKey(systemAddress, &objectExists); +// if (objectExists==false) +// return -1; +// else +// return systemList[index].filter->filterSetID; + + DataStructures::HashIndex index = systemList.GetIndexOf(systemAddress); + if (index.IsInvalid()) + return -1; + else + return systemList.ItemAtIndex(index).filter->filterSetID; +} +void MessageFilter::SetSystemFilterSet(AddressOrGUID addressOrGUID, int filterSetID) +{ + // Allocate this filter set if it doesn't exist. + RakAssert(addressOrGUID.IsUndefined()==false); +// bool objectExists; +// unsigned index = systemList.GetIndexFromKey(addressOrGUID, &objectExists); +// if (objectExists==false) + DataStructures::HashIndex index = systemList.GetIndexOf(addressOrGUID); + if (index.IsInvalid()) + { + if (filterSetID<0) + return; + + FilteredSystem filteredSystem; + filteredSystem.filter = GetFilterSetByID(filterSetID); + // filteredSystem.addressOrGUID=addressOrGUID; + filteredSystem.timeEnteredThisSet=RakNet::GetTimeMS(); + // systemList.Insert(addressOrGUID, filteredSystem, true, _FILE_AND_LINE_); + systemList.Push(addressOrGUID,filteredSystem,_FILE_AND_LINE_); + } + else + { + if (filterSetID>=0) + { + FilterSet *filterSet = GetFilterSetByID(filterSetID); + systemList.ItemAtIndex(index).timeEnteredThisSet=RakNet::GetTimeMS(); + systemList.ItemAtIndex(index).filter=filterSet; + } + else + { + systemList.RemoveAtIndex(index, _FILE_AND_LINE_); + } + } +} +unsigned MessageFilter::GetSystemCount(int filterSetID) const +{ + if (filterSetID==-1) + { + return systemList.Size(); + } + else + { + unsigned i; + unsigned count=0; + DataStructures::List< FilteredSystem > itemList; + DataStructures::List< AddressOrGUID > keyList; + systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); + for (i=0; i < itemList.Size(); i++) + if (itemList[i].filter->filterSetID==filterSetID) + ++count; + return count; + } +} +unsigned MessageFilter::GetFilterSetCount(void) const +{ + return filterList.Size(); +} +int MessageFilter::GetFilterSetIDByIndex(unsigned index) +{ + return filterList[index]->filterSetID; +} +void MessageFilter::DeleteFilterSet(int filterSetID) +{ + FilterSet *filterSet; + bool objectExists; + unsigned i,index; + index = filterList.GetIndexFromKey(filterSetID, &objectExists); + if (objectExists) + { + filterSet=filterList[index]; + DeallocateFilterSet(filterSet); + filterList.RemoveAtIndex(index); + + DataStructures::List< FilteredSystem > itemList; + DataStructures::List< AddressOrGUID > keyList; + systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); + for (i=0; i < itemList.Size(); i++) + { + if (itemList[i].filter==filterSet) + { + systemList.Remove(keyList[i], _FILE_AND_LINE_); + } + } + + /* + // Don't reference this pointer any longer + i=0; + while (i < systemList.Size()) + { + if (systemList[i].filter==filterSet) + systemList.RemoveAtIndex(i); + else + ++i; + } + */ + } +} +void MessageFilter::Clear(void) +{ + unsigned i; + systemList.Clear(_FILE_AND_LINE_); + for (i=0; i < filterList.Size(); i++) + DeallocateFilterSet(filterList[i]); + filterList.Clear(false, _FILE_AND_LINE_); +} +void MessageFilter::DeallocateFilterSet(FilterSet* filterSet) +{ + RakNet::OP_DELETE(filterSet, _FILE_AND_LINE_); +} +FilterSet* MessageFilter::GetFilterSetByID(int filterSetID) +{ + RakAssert(filterSetID>=0); + bool objectExists; + unsigned index; + index = filterList.GetIndexFromKey(filterSetID, &objectExists); + if (objectExists) + return filterList[index]; + else + { + FilterSet *newFilterSet = RakNet::OP_NEW( _FILE_AND_LINE_ ); + memset(newFilterSet->allowedIDs, 0, MESSAGE_FILTER_MAX_MESSAGE_ID * sizeof(bool)); + newFilterSet->banOnFilterTimeExceed=false; + newFilterSet->kickOnDisallowedMessage=false; + newFilterSet->banOnDisallowedMessage=false; + newFilterSet->disallowedMessageBanTimeMS=0; + newFilterSet->timeExceedBanTimeMS=0; + newFilterSet->maxMemberTimeMS=0; + newFilterSet->filterSetID=filterSetID; + newFilterSet->invalidMessageCallback=0; + newFilterSet->timeoutCallback=0; + newFilterSet->timeoutUserData=0; + filterList.Insert(filterSetID, newFilterSet, true, _FILE_AND_LINE_); + return newFilterSet; + } +} +void MessageFilter::OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID) +{ + if (filterSet->invalidMessageCallback) + filterSet->invalidMessageCallback(rakPeerInterface, systemAddress, filterSet->filterSetID, filterSet->disallowedCallbackUserData, messageID); + if (filterSet->banOnDisallowedMessage && rakPeerInterface) + { + char str1[64]; + systemAddress.systemAddress.ToString(false, str1); + rakPeerInterface->AddToBanList(str1, filterSet->disallowedMessageBanTimeMS); + } + if (filterSet->kickOnDisallowedMessage) + { + if (rakPeerInterface) + rakPeerInterface->CloseConnection(systemAddress, true, 0); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->CloseConnection(systemAddress.systemAddress); +#endif + } +} +void MessageFilter::Update(void) +{ + // Update all timers for all systems. If those systems' filter sets are expired, take the appropriate action. + RakNet::Time curTime = RakNet::GetTime(); + if (GreaterThan(curTime - 1000, whenLastTimeoutCheck)) + { + DataStructures::List< FilteredSystem > itemList; + DataStructures::List< AddressOrGUID > keyList; + systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_); + + unsigned int index; + for (index=0; index < itemList.Size(); index++) + { + if (itemList[index].filter && + itemList[index].filter->maxMemberTimeMS>0 && + curTime-itemList[index].timeEnteredThisSet >= itemList[index].filter->maxMemberTimeMS) + { + if (itemList[index].filter->timeoutCallback) + itemList[index].filter->timeoutCallback(rakPeerInterface, keyList[index], itemList[index].filter->filterSetID, itemList[index].filter->timeoutUserData); + + if (itemList[index].filter->banOnFilterTimeExceed && rakPeerInterface) + { + char str1[64]; + keyList[index].ToString(false, str1); + rakPeerInterface->AddToBanList(str1, itemList[index].filter->timeExceedBanTimeMS); + } + if (rakPeerInterface) + rakPeerInterface->CloseConnection(keyList[index], true, 0); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->CloseConnection(keyList[index].systemAddress); +#endif + + systemList.Remove(keyList[index], _FILE_AND_LINE_); + } + } + + whenLastTimeoutCheck=curTime+1000; + } +} +void MessageFilter::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) systemAddress; + (void) rakNetGUID; + (void) isIncoming; + + AddressOrGUID aog; + aog.rakNetGuid=rakNetGUID; + aog.systemAddress=systemAddress; + + // New system, automatically assign to filter set if appropriate + if (autoAddNewConnectionsToFilter>=0 && systemList.HasData(aog)==false) + SetSystemFilterSet(aog, autoAddNewConnectionsToFilter); +} +void MessageFilter::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) rakNetGUID; + (void) lostConnectionReason; + + AddressOrGUID aog; + aog.rakNetGuid=rakNetGUID; + aog.systemAddress=systemAddress; + + // Lost system, remove from the list + systemList.Remove(aog, _FILE_AND_LINE_); +} + PluginReceiveResult MessageFilter::OnReceive(Packet *packet) +{ + DataStructures::HashIndex index; + unsigned char messageId; + + switch (packet->data[0]) + { + case ID_NEW_INCOMING_CONNECTION: + case ID_CONNECTION_REQUEST_ACCEPTED: + case ID_CONNECTION_LOST: + case ID_DISCONNECTION_NOTIFICATION: + case ID_CONNECTION_ATTEMPT_FAILED: + case ID_NO_FREE_INCOMING_CONNECTIONS: + case ID_IP_RECENTLY_CONNECTED: + case ID_CONNECTION_BANNED: + case ID_INVALID_PASSWORD: + case ID_UNCONNECTED_PONG: + case ID_ALREADY_CONNECTED: + case ID_ADVERTISE_SYSTEM: + case ID_REMOTE_DISCONNECTION_NOTIFICATION: + case ID_REMOTE_CONNECTION_LOST: + case ID_REMOTE_NEW_INCOMING_CONNECTION: + case ID_DOWNLOAD_PROGRESS: + break; + default: + if (packet->data[0]==ID_TIMESTAMP) + { + if (packet->lengthdata[sizeof(MessageID) + sizeof(RakNet::TimeMS)]; + } + else + messageId=packet->data[0]; + // If this system is filtered, check if this message is allowed. If not allowed, return RR_STOP_PROCESSING_AND_DEALLOCATE + // index = systemList.GetIndexFromKey(packet->addressOrGUID, &objectExists); + index = systemList.GetIndexOf(packet); + if (index.IsInvalid()) + break; + if (systemList.ItemAtIndex(index).filter->allowedIDs[messageId]==false) + { + OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + if (packet->data[0]==ID_RPC_PLUGIN) + { + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + RakNet::RakString functionName; + bsIn.ReadCompressed(functionName); + if (systemList.ItemAtIndex(index).filter->allowedRPC4.HasData(functionName)==false) + { + OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + + break; + } + + return RR_CONTINUE_PROCESSING; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/MessageFilter.h b/src/raknet/MessageFilter.h index 011bb36..8befe7b 100755 --- a/src/raknet/MessageFilter.h +++ b/src/raknet/MessageFilter.h @@ -1,191 +1,191 @@ -/// \file -/// \brief Message filter plugin. Assigns systems to FilterSets. Each FilterSet limits what messages are allowed. This is a security related plugin. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_MessageFilter==1 - -#ifndef __MESSAGE_FILTER_PLUGIN_H -#define __MESSAGE_FILTER_PLUGIN_H - -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "DS_OrderedList.h" -#include "DS_Hash.h" -#include "Export.h" - -/// MessageIdentifier (ID_*) values shoudln't go higher than this. Change it if you do. -#define MESSAGE_FILTER_MAX_MESSAGE_ID 256 - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \internal Has to be public so some of the shittier compilers can use it. -int RAK_DLL_EXPORT MessageFilterStrComp( char *const &key,char *const &data ); - -/// \internal Has to be public so some of the shittier compilers can use it. -struct FilterSet -{ - bool banOnFilterTimeExceed; - bool kickOnDisallowedMessage; - bool banOnDisallowedMessage; - RakNet::TimeMS disallowedMessageBanTimeMS; - RakNet::TimeMS timeExceedBanTimeMS; - RakNet::TimeMS maxMemberTimeMS; - void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID); - void *disallowedCallbackUserData; - void (*timeoutCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData); - void *timeoutUserData; - int filterSetID; - bool allowedIDs[MESSAGE_FILTER_MAX_MESSAGE_ID]; - DataStructures::OrderedList allowedRPC4; -}; - -/// \internal Has to be public so some of the shittier compilers can use it. -int RAK_DLL_EXPORT FilterSetComp( const int &key, FilterSet * const &data ); - -/// \internal Has to be public so some of the shittier compilers can use it. -struct FilteredSystem -{ - FilterSet *filter; - RakNet::TimeMS timeEnteredThisSet; -}; - -/// \defgroup MESSAGEFILTER_GROUP MessageFilter -/// \brief Remote incoming packets from unauthorized systems -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief Assigns systems to FilterSets. Each FilterSet limits what kinds of messages are allowed. -/// \details The MessageFilter plugin is used for security where you limit what systems can send what kind of messages.
      -/// You implicitly define FilterSets, and add allowed message IDs to these FilterSets.
      -/// You then add systems to these filters, such that those systems are limited to sending what the filters allows.
      -/// You can automatically assign systems to a filter.
      -/// You can automatically kick and possibly ban users that stay in a filter too long, or send the wrong message.
      -/// Each system is a member of either zero or one filters.
      -/// Add this plugin before any plugin you wish to filter (most likely just add this plugin before any other). -/// \ingroup MESSAGEFILTER_GROUP -class RAK_DLL_EXPORT MessageFilter : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(MessageFilter) - - MessageFilter(); - virtual ~MessageFilter(); - - // -------------------------------------------------------------------------------------------- - // User functions - // -------------------------------------------------------------------------------------------- - - /// Automatically add all new systems to a particular filter - /// Defaults to -1 - /// \param[in] filterSetID Which filter to add new systems to. <0 for do not add. - void SetAutoAddNewConnectionsToFilter(int filterSetID); - - /// Allow a range of message IDs - /// Always allowed by default: ID_CONNECTION_REQUEST_ACCEPTED through ID_DOWNLOAD_PROGRESS - /// Usually you specify a range to make it easier to add new enumerations without having to constantly refer back to this function. - /// \param[in] allow True to allow this message ID, false to disallow. By default, all messageIDs except the noted types are disallowed. This includes messages from other plugins! - /// \param[in] messageIDStart The first ID_* message to allow in the range. Inclusive. - /// \param[in] messageIDEnd The last ID_* message to allow in the range. Inclusive. - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - void SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID); - - /// Allow a specific RPC4 call - /// \pre MessageFilter must be attached before RPC4 - /// \param[in] uniqueID Identifier passed to RegisterFunction() - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - void SetAllowRPC4(bool allow, const char* uniqueID, int filterSetID); - - /// What action to take on a disallowed message. You can kick or not. You can add them to the ban list for some time - /// By default no action is taken. The message is simply ignored. - /// param[in] 0 for permanent ban, >0 for ban time in milliseconds. - /// \param[in] kickOnDisallowed kick the system that sent a disallowed message. - /// \param[in] banOnDisallowed ban the system that sent a disallowed message. See \a banTimeMS for the ban duration - /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - void SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNet::TimeMS banTimeMS, int filterSetID); - - /// Set a user callback to be called on an invalid message for a particular filterSet - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - /// \param[in] userData A pointer passed with the callback - /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. - void SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID addressOrGUID, int filterSetID, void *userData, unsigned char messageID)); - - /// Set a user callback to be called when a user is disconnected due to SetFilterMaxTime - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - /// \param[in] userData A pointer passed with the callback - /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. - void SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID addressOrGUID, int filterSetID, void *userData)); - - /// Limit how long a connection can stay in a particular filterSetID. After this time, the connection is kicked and possibly banned. - /// By default there is no limit to how long a connection can stay in a particular filter set. - /// \param[in] allowedTimeMS How many milliseconds to allow a connection to stay in this filter set. - /// \param[in] banOnExceed True or false to ban the system, or not, when \a allowedTimeMS is exceeded - /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. - void SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNet::TimeMS banTimeMS, int filterSetID); - - /// Get the filterSetID a system is using. Returns -1 for none. - /// \param[in] addressOrGUID The system we are referring to - int GetSystemFilterSet(AddressOrGUID addressOrGUID); - - /// Assign a system to a filter set. - /// Systems are automatically added to filter sets (or not) based on SetAutoAddNewConnectionsToFilter() - /// This function is used to change the filter set a system is using, to add it to a new filter set, or to remove it from all existin filter sets. - /// \param[in] addressOrGUID The system we are referring to - /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. If -1, the system will be removed from all filter sets. - void SetSystemFilterSet(AddressOrGUID addressOrGUID, int filterSetID); - - /// Returns the number of systems subscribed to a particular filter set - /// Using anything other than -1 for \a filterSetID is slow, so you should store the returned value. - /// \param[in] filterSetID The filter set to limit to. Use -1 for none (just returns the total number of filter systems in that case). - unsigned GetSystemCount(int filterSetID) const; - - /// Returns the total number of filter sets. - /// \return The total number of filter sets. - unsigned GetFilterSetCount(void) const; - - /// Returns the ID of a filter set, by index - /// \param[in] An index between 0 and GetFilterSetCount()-1 inclusive - int GetFilterSetIDByIndex(unsigned index); - - /// Delete a FilterSet. All systems formerly subscribed to this filter are now unrestricted. - /// \param[in] filterSetID The ID of the filter set to delete. - void DeleteFilterSet(int filterSetID); - - // -------------------------------------------------------------------------------------------- - // Packet handling functions - // -------------------------------------------------------------------------------------------- - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - -protected: - - void Clear(void); - void DeallocateFilterSet(FilterSet *filterSet); - FilterSet* GetFilterSetByID(int filterSetID); - void OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID); - - DataStructures::OrderedList filterList; - // Change to guid - DataStructures::Hash systemList; - - int autoAddNewConnectionsToFilter; - RakNet::Time whenLastTimeoutCheck; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Message filter plugin. Assigns systems to FilterSets. Each FilterSet limits what messages are allowed. This is a security related plugin. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_MessageFilter==1 + +#ifndef __MESSAGE_FILTER_PLUGIN_H +#define __MESSAGE_FILTER_PLUGIN_H + +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "DS_OrderedList.h" +#include "DS_Hash.h" +#include "Export.h" + +/// MessageIdentifier (ID_*) values shoudln't go higher than this. Change it if you do. +#define MESSAGE_FILTER_MAX_MESSAGE_ID 256 + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \internal Has to be public so some of the shittier compilers can use it. +int RAK_DLL_EXPORT MessageFilterStrComp( char *const &key,char *const &data ); + +/// \internal Has to be public so some of the shittier compilers can use it. +struct FilterSet +{ + bool banOnFilterTimeExceed; + bool kickOnDisallowedMessage; + bool banOnDisallowedMessage; + RakNet::TimeMS disallowedMessageBanTimeMS; + RakNet::TimeMS timeExceedBanTimeMS; + RakNet::TimeMS maxMemberTimeMS; + void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID); + void *disallowedCallbackUserData; + void (*timeoutCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData); + void *timeoutUserData; + int filterSetID; + bool allowedIDs[MESSAGE_FILTER_MAX_MESSAGE_ID]; + DataStructures::OrderedList allowedRPC4; +}; + +/// \internal Has to be public so some of the shittier compilers can use it. +int RAK_DLL_EXPORT FilterSetComp( const int &key, FilterSet * const &data ); + +/// \internal Has to be public so some of the shittier compilers can use it. +struct FilteredSystem +{ + FilterSet *filter; + RakNet::TimeMS timeEnteredThisSet; +}; + +/// \defgroup MESSAGEFILTER_GROUP MessageFilter +/// \brief Remote incoming packets from unauthorized systems +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief Assigns systems to FilterSets. Each FilterSet limits what kinds of messages are allowed. +/// \details The MessageFilter plugin is used for security where you limit what systems can send what kind of messages.
      +/// You implicitly define FilterSets, and add allowed message IDs to these FilterSets.
      +/// You then add systems to these filters, such that those systems are limited to sending what the filters allows.
      +/// You can automatically assign systems to a filter.
      +/// You can automatically kick and possibly ban users that stay in a filter too long, or send the wrong message.
      +/// Each system is a member of either zero or one filters.
      +/// Add this plugin before any plugin you wish to filter (most likely just add this plugin before any other). +/// \ingroup MESSAGEFILTER_GROUP +class RAK_DLL_EXPORT MessageFilter : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(MessageFilter) + + MessageFilter(); + virtual ~MessageFilter(); + + // -------------------------------------------------------------------------------------------- + // User functions + // -------------------------------------------------------------------------------------------- + + /// Automatically add all new systems to a particular filter + /// Defaults to -1 + /// \param[in] filterSetID Which filter to add new systems to. <0 for do not add. + void SetAutoAddNewConnectionsToFilter(int filterSetID); + + /// Allow a range of message IDs + /// Always allowed by default: ID_CONNECTION_REQUEST_ACCEPTED through ID_DOWNLOAD_PROGRESS + /// Usually you specify a range to make it easier to add new enumerations without having to constantly refer back to this function. + /// \param[in] allow True to allow this message ID, false to disallow. By default, all messageIDs except the noted types are disallowed. This includes messages from other plugins! + /// \param[in] messageIDStart The first ID_* message to allow in the range. Inclusive. + /// \param[in] messageIDEnd The last ID_* message to allow in the range. Inclusive. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID); + + /// Allow a specific RPC4 call + /// \pre MessageFilter must be attached before RPC4 + /// \param[in] uniqueID Identifier passed to RegisterFunction() + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetAllowRPC4(bool allow, const char* uniqueID, int filterSetID); + + /// What action to take on a disallowed message. You can kick or not. You can add them to the ban list for some time + /// By default no action is taken. The message is simply ignored. + /// param[in] 0 for permanent ban, >0 for ban time in milliseconds. + /// \param[in] kickOnDisallowed kick the system that sent a disallowed message. + /// \param[in] banOnDisallowed ban the system that sent a disallowed message. See \a banTimeMS for the ban duration + /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNet::TimeMS banTimeMS, int filterSetID); + + /// Set a user callback to be called on an invalid message for a particular filterSet + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + /// \param[in] userData A pointer passed with the callback + /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. + void SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID addressOrGUID, int filterSetID, void *userData, unsigned char messageID)); + + /// Set a user callback to be called when a user is disconnected due to SetFilterMaxTime + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + /// \param[in] userData A pointer passed with the callback + /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. + void SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID addressOrGUID, int filterSetID, void *userData)); + + /// Limit how long a connection can stay in a particular filterSetID. After this time, the connection is kicked and possibly banned. + /// By default there is no limit to how long a connection can stay in a particular filter set. + /// \param[in] allowedTimeMS How many milliseconds to allow a connection to stay in this filter set. + /// \param[in] banOnExceed True or false to ban the system, or not, when \a allowedTimeMS is exceeded + /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNet::TimeMS banTimeMS, int filterSetID); + + /// Get the filterSetID a system is using. Returns -1 for none. + /// \param[in] addressOrGUID The system we are referring to + int GetSystemFilterSet(AddressOrGUID addressOrGUID); + + /// Assign a system to a filter set. + /// Systems are automatically added to filter sets (or not) based on SetAutoAddNewConnectionsToFilter() + /// This function is used to change the filter set a system is using, to add it to a new filter set, or to remove it from all existin filter sets. + /// \param[in] addressOrGUID The system we are referring to + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. If -1, the system will be removed from all filter sets. + void SetSystemFilterSet(AddressOrGUID addressOrGUID, int filterSetID); + + /// Returns the number of systems subscribed to a particular filter set + /// Using anything other than -1 for \a filterSetID is slow, so you should store the returned value. + /// \param[in] filterSetID The filter set to limit to. Use -1 for none (just returns the total number of filter systems in that case). + unsigned GetSystemCount(int filterSetID) const; + + /// Returns the total number of filter sets. + /// \return The total number of filter sets. + unsigned GetFilterSetCount(void) const; + + /// Returns the ID of a filter set, by index + /// \param[in] An index between 0 and GetFilterSetCount()-1 inclusive + int GetFilterSetIDByIndex(unsigned index); + + /// Delete a FilterSet. All systems formerly subscribed to this filter are now unrestricted. + /// \param[in] filterSetID The ID of the filter set to delete. + void DeleteFilterSet(int filterSetID); + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + +protected: + + void Clear(void); + void DeallocateFilterSet(FilterSet *filterSet); + FilterSet* GetFilterSetByID(int filterSetID); + void OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID); + + DataStructures::OrderedList filterList; + // Change to guid + DataStructures::Hash systemList; + + int autoAddNewConnectionsToFilter; + RakNet::Time whenLastTimeoutCheck; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/MessageIdentifiers.h b/src/raknet/MessageIdentifiers.h index 65917db..6788de2 100755 --- a/src/raknet/MessageIdentifiers.h +++ b/src/raknet/MessageIdentifiers.h @@ -1,403 +1,403 @@ -/// \file -/// \brief All the message identifiers used by RakNet. Message identifiers comprise the first byte of any message. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __MESSAGE_IDENTIFIERS_H -#define __MESSAGE_IDENTIFIERS_H - -#if defined(RAKNET_USE_CUSTOM_PACKET_IDS) -#include "CustomPacketIdentifiers.h" -#else - -enum OutOfBandIdentifiers -{ - ID_NAT_ESTABLISH_UNIDIRECTIONAL, - ID_NAT_ESTABLISH_BIDIRECTIONAL, - ID_NAT_TYPE_DETECT, - ID_ROUTER_2_REPLY_TO_SENDER_PORT, - ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, - ID_ROUTER_2_MINI_PUNCH_REPLY, - ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, - ID_XBOX_360_VOICE, - ID_XBOX_360_GET_NETWORK_ROOM, - ID_XBOX_360_RETURN_NETWORK_ROOM, -}; - -/// You should not edit the file MessageIdentifiers.h as it is a part of RakNet static library -/// To define your own message id, define an enum following the code example that follows. -/// -/// \code -/// enum { -/// ID_MYPROJECT_MSG_1 = ID_USER_PACKET_ENUM, -/// ID_MYPROJECT_MSG_2, -/// ... -/// }; -/// \endcode -/// -/// \note All these enumerations should be casted to (unsigned char) before writing them to RakNet::BitStream -enum DefaultMessageIDTypes -{ - // - // RESERVED TYPES - DO NOT CHANGE THESE - // All types from RakPeer - // - /// These types are never returned to the user. - /// Ping from a connected system. Update timestamps (internal use only) - ID_CONNECTED_PING, - /// Ping from an unconnected system. Reply but do not update timestamps. (internal use only) - ID_UNCONNECTED_PING, - /// Ping from an unconnected system. Only reply if we have open connections. Do not update timestamps. (internal use only) - ID_UNCONNECTED_PING_OPEN_CONNECTIONS, - /// Pong from a connected system. Update timestamps (internal use only) - ID_CONNECTED_PONG, - /// A reliable packet to detect lost connections (internal use only) - ID_DETECT_LOST_CONNECTIONS, - /// C2S: Initial query: Header(1), OfflineMesageID(16), Protocol number(1), Pad(toMTU), sent with no fragment set. - /// If protocol fails on server, returns ID_INCOMPATIBLE_PROTOCOL_VERSION to client - ID_OPEN_CONNECTION_REQUEST_1, - /// S2C: Header(1), OfflineMesageID(16), server GUID(8), HasSecurity(1), Cookie(4, if HasSecurity) - /// , public key (if do security is true), MTU(2). If public key fails on client, returns ID_PUBLIC_KEY_MISMATCH - ID_OPEN_CONNECTION_REPLY_1, - /// C2S: Header(1), OfflineMesageID(16), Cookie(4, if HasSecurity is true on the server), clientSupportsSecurity(1 bit), - /// handshakeChallenge (if has security on both server and client), remoteBindingAddress(6), MTU(2), client GUID(8) - /// Connection slot allocated if cookie is valid, server is not full, GUID and IP not already in use. - ID_OPEN_CONNECTION_REQUEST_2, - /// S2C: Header(1), OfflineMesageID(16), server GUID(8), mtu(2), doSecurity(1 bit), handshakeAnswer (if do security is true) - ID_OPEN_CONNECTION_REPLY_2, - /// C2S: Header(1), GUID(8), Timestamp, HasSecurity(1), Proof(32) - ID_CONNECTION_REQUEST, - /// RakPeer - Remote system requires secure connections, pass a public key to RakPeerInterface::Connect() - ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY, - /// RakPeer - We passed a public key to RakPeerInterface::Connect(), but the other system did not have security turned on - ID_OUR_SYSTEM_REQUIRES_SECURITY, - /// RakPeer - Wrong public key passed to RakPeerInterface::Connect() - ID_PUBLIC_KEY_MISMATCH, - /// RakPeer - Same as ID_ADVERTISE_SYSTEM, but intended for internal use rather than being passed to the user. - /// Second byte indicates type. Used currently for NAT punchthrough for receiver port advertisement. See ID_NAT_ADVERTISE_RECIPIENT_PORT - ID_OUT_OF_BAND_INTERNAL, - /// If RakPeerInterface::Send() is called where PacketReliability contains _WITH_ACK_RECEIPT, then on a later call to - /// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long, - /// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message. - /// This number will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_ACKED means that - /// the message arrived - ID_SND_RECEIPT_ACKED, - /// If RakPeerInterface::Send() is called where PacketReliability contains UNRELIABLE_WITH_ACK_RECEIPT, then on a later call to - /// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long, - /// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message. This number - /// will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_LOSS means that an ack for the - /// message did not arrive (it may or may not have been delivered, probably not). On disconnect or shutdown, you will not get - /// ID_SND_RECEIPT_LOSS for unsent messages, you should consider those messages as all lost. - ID_SND_RECEIPT_LOSS, - - - // - // USER TYPES - DO NOT CHANGE THESE - // - - /// RakPeer - In a client/server environment, our connection request to the server has been accepted. - ID_CONNECTION_REQUEST_ACCEPTED, - /// RakPeer - Sent to the player when a connection request cannot be completed due to inability to connect. - ID_CONNECTION_ATTEMPT_FAILED, - /// RakPeer - Sent a connect request to a system we are currently connected to. - ID_ALREADY_CONNECTED, - /// RakPeer - A remote system has successfully connected. - ID_NEW_INCOMING_CONNECTION, - /// RakPeer - The system we attempted to connect to is not accepting new connections. - ID_NO_FREE_INCOMING_CONNECTIONS, - /// RakPeer - The system specified in Packet::systemAddress has disconnected from us. For the client, this would mean the - /// server has shutdown. - ID_DISCONNECTION_NOTIFICATION, - /// RakPeer - Reliable packets cannot be delivered to the system specified in Packet::systemAddress. The connection to that - /// system has been closed. - ID_CONNECTION_LOST, - /// RakPeer - We are banned from the system we attempted to connect to. - ID_CONNECTION_BANNED, - /// RakPeer - The remote system is using a password and has refused our connection because we did not set the correct password. - ID_INVALID_PASSWORD, - // RAKNET_PROTOCOL_VERSION in RakNetVersion.h does not match on the remote system what we have on our system - // This means the two systems cannot communicate. - // The 2nd byte of the message contains the value of RAKNET_PROTOCOL_VERSION for the remote system - ID_INCOMPATIBLE_PROTOCOL_VERSION, - // Means that this IP address connected recently, and can't connect again as a security measure. See - /// RakPeer::SetLimitIPConnectionFrequency() - ID_IP_RECENTLY_CONNECTED, - /// RakPeer - The sizeof(RakNetTime) bytes following this byte represent a value which is automatically modified by the difference - /// in system times between the sender and the recipient. Requires that you call SetOccasionalPing. - ID_TIMESTAMP, - /// RakPeer - Pong from an unconnected system. First byte is ID_UNCONNECTED_PONG, second sizeof(RakNet::TimeMS) bytes is the ping, - /// following bytes is system specific enumeration data. - /// Read using bitstreams - ID_UNCONNECTED_PONG, - /// RakPeer - Inform a remote system of our IP/Port. On the recipient, all data past ID_ADVERTISE_SYSTEM is whatever was passed to - /// the data parameter - ID_ADVERTISE_SYSTEM, - // RakPeer - Downloading a large message. Format is ID_DOWNLOAD_PROGRESS (MessageID), partCount (unsigned int), - /// partTotal (unsigned int), - /// partLength (unsigned int), first part data (length <= MAX_MTU_SIZE). See the three parameters partCount, partTotal - /// and partLength in OnFileProgress in FileListTransferCBInterface.h - ID_DOWNLOAD_PROGRESS, - - /// ConnectionGraph2 plugin - In a client/server environment, a client other than ourselves has disconnected gracefully. - /// Packet::systemAddress is modified to reflect the systemAddress of this client. - ID_REMOTE_DISCONNECTION_NOTIFICATION, - /// ConnectionGraph2 plugin - In a client/server environment, a client other than ourselves has been forcefully dropped. - /// Packet::systemAddress is modified to reflect the systemAddress of this client. - ID_REMOTE_CONNECTION_LOST, - /// ConnectionGraph2 plugin: Bytes 1-4 = count. for (count items) contains {SystemAddress, RakNetGUID, 2 byte ping} - ID_REMOTE_NEW_INCOMING_CONNECTION, - - /// FileListTransfer plugin - Setup data - ID_FILE_LIST_TRANSFER_HEADER, - /// FileListTransfer plugin - A file - ID_FILE_LIST_TRANSFER_FILE, - // Ack for reference push, to send more of the file - ID_FILE_LIST_REFERENCE_PUSH_ACK, - - /// DirectoryDeltaTransfer plugin - Request from a remote system for a download of a directory - ID_DDT_DOWNLOAD_REQUEST, - - /// RakNetTransport plugin - Transport provider message, used for remote console - ID_TRANSPORT_STRING, - - /// ReplicaManager plugin - Create an object - ID_REPLICA_MANAGER_CONSTRUCTION, - /// ReplicaManager plugin - Changed scope of an object - ID_REPLICA_MANAGER_SCOPE_CHANGE, - /// ReplicaManager plugin - Serialized data of an object - ID_REPLICA_MANAGER_SERIALIZE, - /// ReplicaManager plugin - New connection, about to send all world objects - ID_REPLICA_MANAGER_DOWNLOAD_STARTED, - /// ReplicaManager plugin - Finished downloading all serialized objects - ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE, - - /// RakVoice plugin - Open a communication channel - ID_RAKVOICE_OPEN_CHANNEL_REQUEST, - /// RakVoice plugin - Communication channel accepted - ID_RAKVOICE_OPEN_CHANNEL_REPLY, - /// RakVoice plugin - Close a communication channel - ID_RAKVOICE_CLOSE_CHANNEL, - /// RakVoice plugin - Voice data - ID_RAKVOICE_DATA, - - /// Autopatcher plugin - Get a list of files that have changed since a certain date - ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE, - /// Autopatcher plugin - A list of files to create - ID_AUTOPATCHER_CREATION_LIST, - /// Autopatcher plugin - A list of files to delete - ID_AUTOPATCHER_DELETION_LIST, - /// Autopatcher plugin - A list of files to get patches for - ID_AUTOPATCHER_GET_PATCH, - /// Autopatcher plugin - A list of patches for a list of files - ID_AUTOPATCHER_PATCH_LIST, - /// Autopatcher plugin - Returned to the user: An error from the database repository for the autopatcher. - ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR, - /// Autopatcher plugin - Finished getting all files from the autopatcher - ID_AUTOPATCHER_FINISHED_INTERNAL, - ID_AUTOPATCHER_FINISHED, - /// Autopatcher plugin - Returned to the user: You must restart the application to finish patching. - ID_AUTOPATCHER_RESTART_APPLICATION, - - /// NATPunchthrough plugin: internal - ID_NAT_PUNCHTHROUGH_REQUEST, - /// NATPunchthrough plugin: internal - ID_NAT_GROUP_PUNCHTHROUGH_REQUEST, - /// NATPunchthrough plugin: internal - ID_NAT_GROUP_PUNCHTHROUGH_REPLY, - /// NATPunchthrough plugin: internal - ID_NAT_CONNECT_AT_TIME, - /// NATPunchthrough plugin: internal - ID_NAT_GET_MOST_RECENT_PORT, - /// NATPunchthrough plugin: internal - ID_NAT_CLIENT_READY, - /// NATPunchthrough plugin: internal - ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION, - - /// NATPunchthrough plugin: Destination system is not connected to the server. Bytes starting at offset 1 contains the - /// RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). - ID_NAT_TARGET_NOT_CONNECTED, - /// NATPunchthrough plugin: Destination system is not responding to ID_NAT_GET_MOST_RECENT_PORT. Possibly the plugin is not installed. - /// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). - ID_NAT_TARGET_UNRESPONSIVE, - /// NATPunchthrough plugin: The server lost the connection to the destination system while setting up punchthrough. - /// Possibly the plugin is not installed. Bytes starting at offset 1 contains the RakNetGUID destination - /// field of NatPunchthroughClient::OpenNAT(). - ID_NAT_CONNECTION_TO_TARGET_LOST, - /// NATPunchthrough plugin: This punchthrough is already in progress. Possibly the plugin is not installed. - /// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). - ID_NAT_ALREADY_IN_PROGRESS, - /// NATPunchthrough plugin: This message is generated on the local system, and does not come from the network. - /// packet::guid contains the destination field of NatPunchthroughClient::OpenNAT(). Byte 1 contains 1 if you are the sender, 0 if not - ID_NAT_PUNCHTHROUGH_FAILED, - /// NATPunchthrough plugin: Punchthrough succeeded. See packet::systemAddress and packet::guid. Byte 1 contains 1 if you are the sender, - /// 0 if not. You can now use RakPeer::Connect() or other calls to communicate with this system. - ID_NAT_PUNCHTHROUGH_SUCCEEDED, - /// NATPunchthrough plugin: OpenNATGroup failed. - /// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT() - /// Data format starts at byte 1:
      - /// (char) passedSystemsCount,
      - /// (RakNetGuid, SystemAddress) (for passedSystemsCount),
      - /// (char) ignoredSystemsCount (caused by ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_TARGET_UNRESPONSIVE),
      - /// RakNetGuid (for ignoredSystemsCount),
      - /// (char) failedSystemsCount,
      - /// RakNetGuid (for failedSystemsCount)
      - ID_NAT_GROUP_PUNCH_FAILED, - /// NATPunchthrough plugin: OpenNATGroup succeeded. - /// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT() - /// See ID_NAT_GROUP_PUNCH_FAILED for data format - ID_NAT_GROUP_PUNCH_SUCCEEDED, - - /// ReadyEvent plugin - Set the ready state for a particular system - /// First 4 bytes after the message contains the id - ID_READY_EVENT_SET, - /// ReadyEvent plugin - Unset the ready state for a particular system - /// First 4 bytes after the message contains the id - ID_READY_EVENT_UNSET, - /// All systems are in state ID_READY_EVENT_SET - /// First 4 bytes after the message contains the id - ID_READY_EVENT_ALL_SET, - /// \internal, do not process in your game - /// ReadyEvent plugin - Request of ready event state - used for pulling data when newly connecting - ID_READY_EVENT_QUERY, - - /// Lobby packets. Second byte indicates type. - ID_LOBBY_GENERAL, - - // RPC3, RPC4Plugin error - ID_RPC_REMOTE_ERROR, - /// Plugin based replacement for RPC system - ID_RPC_PLUGIN, - - /// FileListTransfer transferring large files in chunks that are read only when needed, to save memory - ID_FILE_LIST_REFERENCE_PUSH, - /// Force the ready event to all set - ID_READY_EVENT_FORCE_ALL_SET, - - /// Rooms function - ID_ROOMS_EXECUTE_FUNC, - ID_ROOMS_LOGON_STATUS, - ID_ROOMS_HANDLE_CHANGE, - - /// Lobby2 message - ID_LOBBY2_SEND_MESSAGE, - ID_LOBBY2_SERVER_ERROR, - - /// Informs user of a new host GUID. Packet::Guid contains this new host RakNetGuid. The old host can be read out using BitStream->Read(RakNetGuid) starting on byte 1 - /// This is not returned until connected to a remote system - /// If the oldHost is UNASSIGNED_RAKNET_GUID, then this is the first time the host has been determined - ID_FCM2_NEW_HOST, - /// \internal For FullyConnectedMesh2 plugin - ID_FCM2_REQUEST_FCMGUID, - /// \internal For FullyConnectedMesh2 plugin - ID_FCM2_RESPOND_CONNECTION_COUNT, - /// \internal For FullyConnectedMesh2 plugin - ID_FCM2_INFORM_FCMGUID, - /// \internal For FullyConnectedMesh2 plugin - ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT, - - /// UDP proxy messages. Second byte indicates type. - ID_UDP_PROXY_GENERAL, - - /// SQLite3Plugin - execute - ID_SQLite3_EXEC, - /// SQLite3Plugin - Remote database is unknown - ID_SQLite3_UNKNOWN_DB, - /// Events happening with SQLiteClientLoggerPlugin - ID_SQLLITE_LOGGER, - - /// Sent to NatTypeDetectionServer - ID_NAT_TYPE_DETECTION_REQUEST, - /// Sent to NatTypeDetectionClient. Byte 1 contains the type of NAT detected. - ID_NAT_TYPE_DETECTION_RESULT, - - /// Used by the router2 plugin - ID_ROUTER_2_INTERNAL, - /// No path is available or can be established to the remote system - /// Packet::guid contains the endpoint guid that we were trying to reach - ID_ROUTER_2_FORWARDING_NO_PATH, - /// \brief You can now call connect, ping, or other operations to the destination system. - /// - /// Connect as follows: - /// - /// RakNet::BitStream bs(packet->data, packet->length, false); - /// bs.IgnoreBytes(sizeof(MessageID)); - /// RakNetGUID endpointGuid; - /// bs.Read(endpointGuid); - /// unsigned short sourceToDestPort; - /// bs.Read(sourceToDestPort); - /// char ipAddressString[32]; - /// packet->systemAddress.ToString(false, ipAddressString); - /// rakPeerInterface->Connect(ipAddressString, sourceToDestPort, 0,0); - ID_ROUTER_2_FORWARDING_ESTABLISHED, - /// The IP address for a forwarded connection has changed - /// Read endpointGuid and port as per ID_ROUTER_2_FORWARDING_ESTABLISHED - ID_ROUTER_2_REROUTED, - - /// \internal Used by the team balancer plugin - ID_TEAM_BALANCER_INTERNAL, - /// Cannot switch to the desired team because it is full. However, if someone on that team leaves, you will - /// get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member. - ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING, - /// Cannot switch to the desired team because all teams are locked. However, if someone on that team leaves, - /// you will get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join. - ID_TEAM_BALANCER_TEAMS_LOCKED, - /// Team balancer plugin informing you of your team. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member. - ID_TEAM_BALANCER_TEAM_ASSIGNED, - - /// Gamebryo Lightspeed integration - ID_LIGHTSPEED_INTEGRATION, - - /// XBOX integration - ID_XBOX_LOBBY, - - /// The password we used to challenge the other system passed, meaning the other system has called TwoWayAuthentication::AddPassword() with the same password we passed to TwoWayAuthentication::Challenge() - /// You can read the identifier used to challenge as follows: - /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(RakNet::MessageID)); RakNet::RakString password; bs.Read(password); - ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, - ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS, - /// A remote system sent us a challenge using TwoWayAuthentication::Challenge(), and the challenge failed. - /// If the other system must pass the challenge to stay connected, you should call RakPeer::CloseConnection() to terminate the connection to the other system. - ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE, - /// The other system did not add the password we used to TwoWayAuthentication::AddPassword() - /// You can read the identifier used to challenge as follows: - /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password); - ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE, - /// The other system did not respond within a timeout threshhold. Either the other system is not running the plugin or the other system was blocking on some operation for a long time. - /// You can read the identifier used to challenge as follows: - /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password); - ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, - /// \internal - ID_TWO_WAY_AUTHENTICATION_NEGOTIATION, - - /// CloudClient / CloudServer - ID_CLOUD_POST_REQUEST, - ID_CLOUD_RELEASE_REQUEST, - ID_CLOUD_GET_REQUEST, - ID_CLOUD_GET_RESPONSE, - ID_CLOUD_UNSUBSCRIBE_REQUEST, - ID_CLOUD_SERVER_TO_SERVER_COMMAND, - ID_CLOUD_SUBSCRIPTION_NOTIFICATION, - - // So I can add more without changing user enumerations - ID_RESERVED_1, - ID_RESERVED_2, - ID_RESERVED_3, - ID_RESERVED_4, - ID_RESERVED_5, - ID_RESERVED_6, - ID_RESERVED_7, - ID_RESERVED_8, - ID_RESERVED_9, - - // For the user to use. Start your first enumeration at this value. - ID_USER_PACKET_ENUM, - //------------------------------------------------------------------------------------------------------------- - -}; - -#endif // RAKNET_USE_CUSTOM_PACKET_IDS - -#endif +/// \file +/// \brief All the message identifiers used by RakNet. Message identifiers comprise the first byte of any message. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __MESSAGE_IDENTIFIERS_H +#define __MESSAGE_IDENTIFIERS_H + +#if defined(RAKNET_USE_CUSTOM_PACKET_IDS) +#include "CustomPacketIdentifiers.h" +#else + +enum OutOfBandIdentifiers +{ + ID_NAT_ESTABLISH_UNIDIRECTIONAL, + ID_NAT_ESTABLISH_BIDIRECTIONAL, + ID_NAT_TYPE_DETECT, + ID_ROUTER_2_REPLY_TO_SENDER_PORT, + ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, + ID_ROUTER_2_MINI_PUNCH_REPLY, + ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, + ID_XBOX_360_VOICE, + ID_XBOX_360_GET_NETWORK_ROOM, + ID_XBOX_360_RETURN_NETWORK_ROOM, +}; + +/// You should not edit the file MessageIdentifiers.h as it is a part of RakNet static library +/// To define your own message id, define an enum following the code example that follows. +/// +/// \code +/// enum { +/// ID_MYPROJECT_MSG_1 = ID_USER_PACKET_ENUM, +/// ID_MYPROJECT_MSG_2, +/// ... +/// }; +/// \endcode +/// +/// \note All these enumerations should be casted to (unsigned char) before writing them to RakNet::BitStream +enum DefaultMessageIDTypes +{ + // + // RESERVED TYPES - DO NOT CHANGE THESE + // All types from RakPeer + // + /// These types are never returned to the user. + /// Ping from a connected system. Update timestamps (internal use only) + ID_CONNECTED_PING, + /// Ping from an unconnected system. Reply but do not update timestamps. (internal use only) + ID_UNCONNECTED_PING, + /// Ping from an unconnected system. Only reply if we have open connections. Do not update timestamps. (internal use only) + ID_UNCONNECTED_PING_OPEN_CONNECTIONS, + /// Pong from a connected system. Update timestamps (internal use only) + ID_CONNECTED_PONG, + /// A reliable packet to detect lost connections (internal use only) + ID_DETECT_LOST_CONNECTIONS, + /// C2S: Initial query: Header(1), OfflineMesageID(16), Protocol number(1), Pad(toMTU), sent with no fragment set. + /// If protocol fails on server, returns ID_INCOMPATIBLE_PROTOCOL_VERSION to client + ID_OPEN_CONNECTION_REQUEST_1, + /// S2C: Header(1), OfflineMesageID(16), server GUID(8), HasSecurity(1), Cookie(4, if HasSecurity) + /// , public key (if do security is true), MTU(2). If public key fails on client, returns ID_PUBLIC_KEY_MISMATCH + ID_OPEN_CONNECTION_REPLY_1, + /// C2S: Header(1), OfflineMesageID(16), Cookie(4, if HasSecurity is true on the server), clientSupportsSecurity(1 bit), + /// handshakeChallenge (if has security on both server and client), remoteBindingAddress(6), MTU(2), client GUID(8) + /// Connection slot allocated if cookie is valid, server is not full, GUID and IP not already in use. + ID_OPEN_CONNECTION_REQUEST_2, + /// S2C: Header(1), OfflineMesageID(16), server GUID(8), mtu(2), doSecurity(1 bit), handshakeAnswer (if do security is true) + ID_OPEN_CONNECTION_REPLY_2, + /// C2S: Header(1), GUID(8), Timestamp, HasSecurity(1), Proof(32) + ID_CONNECTION_REQUEST, + /// RakPeer - Remote system requires secure connections, pass a public key to RakPeerInterface::Connect() + ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY, + /// RakPeer - We passed a public key to RakPeerInterface::Connect(), but the other system did not have security turned on + ID_OUR_SYSTEM_REQUIRES_SECURITY, + /// RakPeer - Wrong public key passed to RakPeerInterface::Connect() + ID_PUBLIC_KEY_MISMATCH, + /// RakPeer - Same as ID_ADVERTISE_SYSTEM, but intended for internal use rather than being passed to the user. + /// Second byte indicates type. Used currently for NAT punchthrough for receiver port advertisement. See ID_NAT_ADVERTISE_RECIPIENT_PORT + ID_OUT_OF_BAND_INTERNAL, + /// If RakPeerInterface::Send() is called where PacketReliability contains _WITH_ACK_RECEIPT, then on a later call to + /// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long, + /// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message. + /// This number will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_ACKED means that + /// the message arrived + ID_SND_RECEIPT_ACKED, + /// If RakPeerInterface::Send() is called where PacketReliability contains UNRELIABLE_WITH_ACK_RECEIPT, then on a later call to + /// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long, + /// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message. This number + /// will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_LOSS means that an ack for the + /// message did not arrive (it may or may not have been delivered, probably not). On disconnect or shutdown, you will not get + /// ID_SND_RECEIPT_LOSS for unsent messages, you should consider those messages as all lost. + ID_SND_RECEIPT_LOSS, + + + // + // USER TYPES - DO NOT CHANGE THESE + // + + /// RakPeer - In a client/server environment, our connection request to the server has been accepted. + ID_CONNECTION_REQUEST_ACCEPTED, + /// RakPeer - Sent to the player when a connection request cannot be completed due to inability to connect. + ID_CONNECTION_ATTEMPT_FAILED, + /// RakPeer - Sent a connect request to a system we are currently connected to. + ID_ALREADY_CONNECTED, + /// RakPeer - A remote system has successfully connected. + ID_NEW_INCOMING_CONNECTION, + /// RakPeer - The system we attempted to connect to is not accepting new connections. + ID_NO_FREE_INCOMING_CONNECTIONS, + /// RakPeer - The system specified in Packet::systemAddress has disconnected from us. For the client, this would mean the + /// server has shutdown. + ID_DISCONNECTION_NOTIFICATION, + /// RakPeer - Reliable packets cannot be delivered to the system specified in Packet::systemAddress. The connection to that + /// system has been closed. + ID_CONNECTION_LOST, + /// RakPeer - We are banned from the system we attempted to connect to. + ID_CONNECTION_BANNED, + /// RakPeer - The remote system is using a password and has refused our connection because we did not set the correct password. + ID_INVALID_PASSWORD, + // RAKNET_PROTOCOL_VERSION in RakNetVersion.h does not match on the remote system what we have on our system + // This means the two systems cannot communicate. + // The 2nd byte of the message contains the value of RAKNET_PROTOCOL_VERSION for the remote system + ID_INCOMPATIBLE_PROTOCOL_VERSION, + // Means that this IP address connected recently, and can't connect again as a security measure. See + /// RakPeer::SetLimitIPConnectionFrequency() + ID_IP_RECENTLY_CONNECTED, + /// RakPeer - The sizeof(RakNetTime) bytes following this byte represent a value which is automatically modified by the difference + /// in system times between the sender and the recipient. Requires that you call SetOccasionalPing. + ID_TIMESTAMP, + /// RakPeer - Pong from an unconnected system. First byte is ID_UNCONNECTED_PONG, second sizeof(RakNet::TimeMS) bytes is the ping, + /// following bytes is system specific enumeration data. + /// Read using bitstreams + ID_UNCONNECTED_PONG, + /// RakPeer - Inform a remote system of our IP/Port. On the recipient, all data past ID_ADVERTISE_SYSTEM is whatever was passed to + /// the data parameter + ID_ADVERTISE_SYSTEM, + // RakPeer - Downloading a large message. Format is ID_DOWNLOAD_PROGRESS (MessageID), partCount (unsigned int), + /// partTotal (unsigned int), + /// partLength (unsigned int), first part data (length <= MAX_MTU_SIZE). See the three parameters partCount, partTotal + /// and partLength in OnFileProgress in FileListTransferCBInterface.h + ID_DOWNLOAD_PROGRESS, + + /// ConnectionGraph2 plugin - In a client/server environment, a client other than ourselves has disconnected gracefully. + /// Packet::systemAddress is modified to reflect the systemAddress of this client. + ID_REMOTE_DISCONNECTION_NOTIFICATION, + /// ConnectionGraph2 plugin - In a client/server environment, a client other than ourselves has been forcefully dropped. + /// Packet::systemAddress is modified to reflect the systemAddress of this client. + ID_REMOTE_CONNECTION_LOST, + /// ConnectionGraph2 plugin: Bytes 1-4 = count. for (count items) contains {SystemAddress, RakNetGUID, 2 byte ping} + ID_REMOTE_NEW_INCOMING_CONNECTION, + + /// FileListTransfer plugin - Setup data + ID_FILE_LIST_TRANSFER_HEADER, + /// FileListTransfer plugin - A file + ID_FILE_LIST_TRANSFER_FILE, + // Ack for reference push, to send more of the file + ID_FILE_LIST_REFERENCE_PUSH_ACK, + + /// DirectoryDeltaTransfer plugin - Request from a remote system for a download of a directory + ID_DDT_DOWNLOAD_REQUEST, + + /// RakNetTransport plugin - Transport provider message, used for remote console + ID_TRANSPORT_STRING, + + /// ReplicaManager plugin - Create an object + ID_REPLICA_MANAGER_CONSTRUCTION, + /// ReplicaManager plugin - Changed scope of an object + ID_REPLICA_MANAGER_SCOPE_CHANGE, + /// ReplicaManager plugin - Serialized data of an object + ID_REPLICA_MANAGER_SERIALIZE, + /// ReplicaManager plugin - New connection, about to send all world objects + ID_REPLICA_MANAGER_DOWNLOAD_STARTED, + /// ReplicaManager plugin - Finished downloading all serialized objects + ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE, + + /// RakVoice plugin - Open a communication channel + ID_RAKVOICE_OPEN_CHANNEL_REQUEST, + /// RakVoice plugin - Communication channel accepted + ID_RAKVOICE_OPEN_CHANNEL_REPLY, + /// RakVoice plugin - Close a communication channel + ID_RAKVOICE_CLOSE_CHANNEL, + /// RakVoice plugin - Voice data + ID_RAKVOICE_DATA, + + /// Autopatcher plugin - Get a list of files that have changed since a certain date + ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE, + /// Autopatcher plugin - A list of files to create + ID_AUTOPATCHER_CREATION_LIST, + /// Autopatcher plugin - A list of files to delete + ID_AUTOPATCHER_DELETION_LIST, + /// Autopatcher plugin - A list of files to get patches for + ID_AUTOPATCHER_GET_PATCH, + /// Autopatcher plugin - A list of patches for a list of files + ID_AUTOPATCHER_PATCH_LIST, + /// Autopatcher plugin - Returned to the user: An error from the database repository for the autopatcher. + ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR, + /// Autopatcher plugin - Finished getting all files from the autopatcher + ID_AUTOPATCHER_FINISHED_INTERNAL, + ID_AUTOPATCHER_FINISHED, + /// Autopatcher plugin - Returned to the user: You must restart the application to finish patching. + ID_AUTOPATCHER_RESTART_APPLICATION, + + /// NATPunchthrough plugin: internal + ID_NAT_PUNCHTHROUGH_REQUEST, + /// NATPunchthrough plugin: internal + ID_NAT_GROUP_PUNCHTHROUGH_REQUEST, + /// NATPunchthrough plugin: internal + ID_NAT_GROUP_PUNCHTHROUGH_REPLY, + /// NATPunchthrough plugin: internal + ID_NAT_CONNECT_AT_TIME, + /// NATPunchthrough plugin: internal + ID_NAT_GET_MOST_RECENT_PORT, + /// NATPunchthrough plugin: internal + ID_NAT_CLIENT_READY, + /// NATPunchthrough plugin: internal + ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION, + + /// NATPunchthrough plugin: Destination system is not connected to the server. Bytes starting at offset 1 contains the + /// RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). + ID_NAT_TARGET_NOT_CONNECTED, + /// NATPunchthrough plugin: Destination system is not responding to ID_NAT_GET_MOST_RECENT_PORT. Possibly the plugin is not installed. + /// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). + ID_NAT_TARGET_UNRESPONSIVE, + /// NATPunchthrough plugin: The server lost the connection to the destination system while setting up punchthrough. + /// Possibly the plugin is not installed. Bytes starting at offset 1 contains the RakNetGUID destination + /// field of NatPunchthroughClient::OpenNAT(). + ID_NAT_CONNECTION_TO_TARGET_LOST, + /// NATPunchthrough plugin: This punchthrough is already in progress. Possibly the plugin is not installed. + /// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT(). + ID_NAT_ALREADY_IN_PROGRESS, + /// NATPunchthrough plugin: This message is generated on the local system, and does not come from the network. + /// packet::guid contains the destination field of NatPunchthroughClient::OpenNAT(). Byte 1 contains 1 if you are the sender, 0 if not + ID_NAT_PUNCHTHROUGH_FAILED, + /// NATPunchthrough plugin: Punchthrough succeeded. See packet::systemAddress and packet::guid. Byte 1 contains 1 if you are the sender, + /// 0 if not. You can now use RakPeer::Connect() or other calls to communicate with this system. + ID_NAT_PUNCHTHROUGH_SUCCEEDED, + /// NATPunchthrough plugin: OpenNATGroup failed. + /// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT() + /// Data format starts at byte 1:
      + /// (char) passedSystemsCount,
      + /// (RakNetGuid, SystemAddress) (for passedSystemsCount),
      + /// (char) ignoredSystemsCount (caused by ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_TARGET_UNRESPONSIVE),
      + /// RakNetGuid (for ignoredSystemsCount),
      + /// (char) failedSystemsCount,
      + /// RakNetGuid (for failedSystemsCount)
      + ID_NAT_GROUP_PUNCH_FAILED, + /// NATPunchthrough plugin: OpenNATGroup succeeded. + /// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT() + /// See ID_NAT_GROUP_PUNCH_FAILED for data format + ID_NAT_GROUP_PUNCH_SUCCEEDED, + + /// ReadyEvent plugin - Set the ready state for a particular system + /// First 4 bytes after the message contains the id + ID_READY_EVENT_SET, + /// ReadyEvent plugin - Unset the ready state for a particular system + /// First 4 bytes after the message contains the id + ID_READY_EVENT_UNSET, + /// All systems are in state ID_READY_EVENT_SET + /// First 4 bytes after the message contains the id + ID_READY_EVENT_ALL_SET, + /// \internal, do not process in your game + /// ReadyEvent plugin - Request of ready event state - used for pulling data when newly connecting + ID_READY_EVENT_QUERY, + + /// Lobby packets. Second byte indicates type. + ID_LOBBY_GENERAL, + + // RPC3, RPC4Plugin error + ID_RPC_REMOTE_ERROR, + /// Plugin based replacement for RPC system + ID_RPC_PLUGIN, + + /// FileListTransfer transferring large files in chunks that are read only when needed, to save memory + ID_FILE_LIST_REFERENCE_PUSH, + /// Force the ready event to all set + ID_READY_EVENT_FORCE_ALL_SET, + + /// Rooms function + ID_ROOMS_EXECUTE_FUNC, + ID_ROOMS_LOGON_STATUS, + ID_ROOMS_HANDLE_CHANGE, + + /// Lobby2 message + ID_LOBBY2_SEND_MESSAGE, + ID_LOBBY2_SERVER_ERROR, + + /// Informs user of a new host GUID. Packet::Guid contains this new host RakNetGuid. The old host can be read out using BitStream->Read(RakNetGuid) starting on byte 1 + /// This is not returned until connected to a remote system + /// If the oldHost is UNASSIGNED_RAKNET_GUID, then this is the first time the host has been determined + ID_FCM2_NEW_HOST, + /// \internal For FullyConnectedMesh2 plugin + ID_FCM2_REQUEST_FCMGUID, + /// \internal For FullyConnectedMesh2 plugin + ID_FCM2_RESPOND_CONNECTION_COUNT, + /// \internal For FullyConnectedMesh2 plugin + ID_FCM2_INFORM_FCMGUID, + /// \internal For FullyConnectedMesh2 plugin + ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT, + + /// UDP proxy messages. Second byte indicates type. + ID_UDP_PROXY_GENERAL, + + /// SQLite3Plugin - execute + ID_SQLite3_EXEC, + /// SQLite3Plugin - Remote database is unknown + ID_SQLite3_UNKNOWN_DB, + /// Events happening with SQLiteClientLoggerPlugin + ID_SQLLITE_LOGGER, + + /// Sent to NatTypeDetectionServer + ID_NAT_TYPE_DETECTION_REQUEST, + /// Sent to NatTypeDetectionClient. Byte 1 contains the type of NAT detected. + ID_NAT_TYPE_DETECTION_RESULT, + + /// Used by the router2 plugin + ID_ROUTER_2_INTERNAL, + /// No path is available or can be established to the remote system + /// Packet::guid contains the endpoint guid that we were trying to reach + ID_ROUTER_2_FORWARDING_NO_PATH, + /// \brief You can now call connect, ping, or other operations to the destination system. + /// + /// Connect as follows: + /// + /// RakNet::BitStream bs(packet->data, packet->length, false); + /// bs.IgnoreBytes(sizeof(MessageID)); + /// RakNetGUID endpointGuid; + /// bs.Read(endpointGuid); + /// unsigned short sourceToDestPort; + /// bs.Read(sourceToDestPort); + /// char ipAddressString[32]; + /// packet->systemAddress.ToString(false, ipAddressString); + /// rakPeerInterface->Connect(ipAddressString, sourceToDestPort, 0,0); + ID_ROUTER_2_FORWARDING_ESTABLISHED, + /// The IP address for a forwarded connection has changed + /// Read endpointGuid and port as per ID_ROUTER_2_FORWARDING_ESTABLISHED + ID_ROUTER_2_REROUTED, + + /// \internal Used by the team balancer plugin + ID_TEAM_BALANCER_INTERNAL, + /// Cannot switch to the desired team because it is full. However, if someone on that team leaves, you will + /// get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member. + ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING, + /// Cannot switch to the desired team because all teams are locked. However, if someone on that team leaves, + /// you will get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join. + ID_TEAM_BALANCER_TEAMS_LOCKED, + /// Team balancer plugin informing you of your team. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member. + ID_TEAM_BALANCER_TEAM_ASSIGNED, + + /// Gamebryo Lightspeed integration + ID_LIGHTSPEED_INTEGRATION, + + /// XBOX integration + ID_XBOX_LOBBY, + + /// The password we used to challenge the other system passed, meaning the other system has called TwoWayAuthentication::AddPassword() with the same password we passed to TwoWayAuthentication::Challenge() + /// You can read the identifier used to challenge as follows: + /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(RakNet::MessageID)); RakNet::RakString password; bs.Read(password); + ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, + ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS, + /// A remote system sent us a challenge using TwoWayAuthentication::Challenge(), and the challenge failed. + /// If the other system must pass the challenge to stay connected, you should call RakPeer::CloseConnection() to terminate the connection to the other system. + ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE, + /// The other system did not add the password we used to TwoWayAuthentication::AddPassword() + /// You can read the identifier used to challenge as follows: + /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password); + ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE, + /// The other system did not respond within a timeout threshhold. Either the other system is not running the plugin or the other system was blocking on some operation for a long time. + /// You can read the identifier used to challenge as follows: + /// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password); + ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, + /// \internal + ID_TWO_WAY_AUTHENTICATION_NEGOTIATION, + + /// CloudClient / CloudServer + ID_CLOUD_POST_REQUEST, + ID_CLOUD_RELEASE_REQUEST, + ID_CLOUD_GET_REQUEST, + ID_CLOUD_GET_RESPONSE, + ID_CLOUD_UNSUBSCRIBE_REQUEST, + ID_CLOUD_SERVER_TO_SERVER_COMMAND, + ID_CLOUD_SUBSCRIPTION_NOTIFICATION, + + // So I can add more without changing user enumerations + ID_RESERVED_1, + ID_RESERVED_2, + ID_RESERVED_3, + ID_RESERVED_4, + ID_RESERVED_5, + ID_RESERVED_6, + ID_RESERVED_7, + ID_RESERVED_8, + ID_RESERVED_9, + + // For the user to use. Start your first enumeration at this value. + ID_USER_PACKET_ENUM, + //------------------------------------------------------------------------------------------------------------- + +}; + +#endif // RAKNET_USE_CUSTOM_PACKET_IDS + +#endif diff --git a/src/raknet/NatPunchthroughClient.cpp b/src/raknet/NatPunchthroughClient.cpp index da1c948..28a9de4 100755 --- a/src/raknet/NatPunchthroughClient.cpp +++ b/src/raknet/NatPunchthroughClient.cpp @@ -1,970 +1,970 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatPunchthroughClient==1 - -#include "NatPunchthroughClient.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "GetTime.h" -#include "PacketLogger.h" -#include "Itoa.h" - -using namespace RakNet; - -void NatPunchthroughDebugInterface_Printf::OnClientMessage(const char *msg) -{ - printf("%s\n", msg); -} -#if _RAKNET_SUPPORT_PacketLogger==1 -void NatPunchthroughDebugInterface_PacketLogger::OnClientMessage(const char *msg) -{ - if (pl) - { - pl->WriteMiscellaneous("Nat", msg); - } -} -#endif - -STATIC_FACTORY_DEFINITIONS(NatPunchthroughClient,NatPunchthroughClient); - -NatPunchthroughClient::NatPunchthroughClient() -{ - natPunchthroughDebugInterface=0; - mostRecentNewExternalPort=0; - sp.nextActionTime=0; -} -NatPunchthroughClient::~NatPunchthroughClient() -{ - rakPeerInterface=0; - Clear(); -} -bool NatPunchthroughClient::OpenNAT(RakNetGUID destination, const SystemAddress &facilitator) -{ - ConnectionState cs = rakPeerInterface->GetConnectionState(facilitator); - if (cs!=IS_CONNECTED) - return false; - - SendPunchthrough(destination, facilitator); - return true; -} -bool NatPunchthroughClient::OpenNATGroup(DataStructures::List destinationSystems, const SystemAddress &facilitator) -{ - ConnectionState cs = rakPeerInterface->GetConnectionState(facilitator); - if (cs!=IS_CONNECTED) - return false; - - unsigned long i; - for (i=0; i < destinationSystems.Size(); i++) - { - SendPunchthrough(destinationSystems[i], facilitator); - } - - GroupPunchRequest *gpr = RakNet::OP_NEW(_FILE_AND_LINE_); - gpr->facilitator=facilitator; - gpr->pendingList=destinationSystems; - groupPunchRequests.Push(gpr, _FILE_AND_LINE_); - - return true; -} -void NatPunchthroughClient::SetDebugInterface(NatPunchthroughDebugInterface *i) -{ - natPunchthroughDebugInterface=i; -} -void NatPunchthroughClient::Update(void) -{ - RakNet::Time time = RakNet::GetTime(); - if (sp.nextActionTime && sp.nextActionTime < time) - { - RakNet::Time delta = time - sp.nextActionTime; - if (sp.testMode==SendPing::TESTING_INTERNAL_IPS) - { - SendOutOfBand(sp.internalIds[sp.attemptCount],ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_INTERNAL) - { - ++sp.attemptCount; - sp.retryCount=0; - } - - if (sp.attemptCount>=pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK) - { - sp.testMode=SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE; - if (pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS>0) - { - sp.nextActionTime=time+pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS-delta; - } - else - { - // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned - sp.testMode=SendPing::SEND_WITH_TTL; - // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; - sp.attemptCount=0; - } - } - else - { - sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL-delta; - } - } - else if (sp.testMode==SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE) - { - // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned - sp.testMode=SendPing::SEND_WITH_TTL; - // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; - sp.attemptCount=0; - } - else if (sp.testMode==SendPing::SEND_WITH_TTL) - { - // See http://www.jenkinssoftware.com/forum/index.php?topic=4021.0 - // For Linux 2.6.32 soft-router (ip-tables) - /* - If I understand correctly, getting a datagram on a particular address that was not previously used causes that the port for that address to not be used for when a reply would have otherwise been sent back from that address. - - PHASE 1: - 1. System 1 and 2 send to each other. - 2. Due to latency, system 1 sends first using the server port. - 3. System 2 gets the datagram and no longer uses the server port. Instead, it replies using port 1024. - 4. System 1 gets the reply. The source port is wrong, so it is rejected. - - To put it another way, if a router gets a datagram on a port that was not previously used, it will not reply on that port. However, if it doesn't reply on that port, the message will not be accepted by the remote system. - */ - - // Send to unused port. We do not want the message to arrive, just to open our router's table - SystemAddress sa=sp.targetAddress; - int ttlSendIndex; - for (ttlSendIndex=0; ttlSendIndex <= pc.MAX_PREDICTIVE_PORT_RANGE; ttlSendIndex++) - { - sa.SetPort((unsigned short) (sp.targetAddress.GetPort()+ttlSendIndex)); - SendTTL(sa); - } - - // Only do this stage once - // Wait 250 milliseconds for next stage. The delay is so that even with timing errors both systems send out the - // datagram with TTL before either sends a real one - sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; - sp.nextActionTime=time-delta+250; - } - else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT) - { - SystemAddress sa=sp.targetAddress; - sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); - SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - IncrementExternalAttemptCount(time, delta); - - if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) - { - sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; - - // Skip TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT, etc. - /* - sp.testMode=SendPing::TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT; - sp.attemptCount=0; - */ - } - } - else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT) - { - SystemAddress sa=sp.targetAddress; - if ( sp.targetGuid < rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) ) - sa.SetPort((unsigned short) (1024+sp.attemptCount)); - else - sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); - SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - IncrementExternalAttemptCount(time, delta); - - if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) - { - // From 1024 disabled, never helps as I've seen, but slows down the process by half - sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024; - sp.attemptCount=0; - } - - } - else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024) - { - SystemAddress sa=sp.targetAddress; - if ( sp.targetGuid > rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) ) - sa.SetPort((unsigned short) (1024+sp.attemptCount)); - else - sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); - SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - IncrementExternalAttemptCount(time, delta); - - if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) - { - // From 1024 disabled, never helps as I've seen, but slows down the process by half - sp.testMode=SendPing::TESTING_EXTERNAL_IPS_1024_TO_1024; - sp.attemptCount=0; - } - } - else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_1024_TO_1024) - { - SystemAddress sa=sp.targetAddress; - sa.SetPort((unsigned short) (1024+sp.attemptCount)); - SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); - - IncrementExternalAttemptCount(time, delta); - - if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely bidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString)); - } - - sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; - } - } - else if (sp.testMode==SendPing::WAITING_AFTER_ALL_ATTEMPTS) - { - // Failed. Tell the user - OnPunchthroughFailure(); - UpdateGroupPunchOnNatResult(sp.facilitator, sp.targetGuid, sp.targetAddress, 1); - } - - if (sp.testMode==SendPing::PUNCHING_FIXED_PORT) - { - SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); - if (++sp.retryCount>=sp.punchingFixedPortAttempts) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely unidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString)); - } - - sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; - } - else - { - if ((sp.retryCount%pc.UDP_SENDS_PER_PORT_EXTERNAL)==0) - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta; - else - sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta; - } - } - } - - // Remove elapsed groupRequestsInProgress - unsigned int i; - i=0; - while (i < groupRequestsInProgress.Size()) - { - if (time > groupRequestsInProgress[i].time) - groupRequestsInProgress.RemoveAtIndexFast(i); - else - i++; - } -} -void NatPunchthroughClient::PushFailure(void) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); - p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED; - p->systemAddress=sp.targetAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=sp.targetGuid; - if (sp.weAreSender) - p->data[1]=1; - else - p->data[1]=0; - p->wasGeneratedLocally=true; - rakPeerInterface->PushBackPacket(p, true); -} -void NatPunchthroughClient::OnPunchthroughFailure(void) -{ - if (pc.retryOnFailure==false) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough once. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString)); - } - - PushFailure(); - OnReadyForNextPunchthrough(); - return; - } - - unsigned int i; - for (i=0; i < failedAttemptList.Size(); i++) - { - if (failedAttemptList[i].guid==sp.targetGuid) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough twice. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString)); - } - - // Failed a second time, so return failed to user - PushFailure(); - - OnReadyForNextPunchthrough(); - - failedAttemptList.RemoveAtIndexFast(i); - return; - } - } - - if (rakPeerInterface->GetConnectionState(sp.facilitator)!=IS_CONNECTED) - { - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Not connected to facilitator, so cannot retry punchthrough after first failure. Returning failure onj guid %s, system address %s to user.", guidString, ipAddressString)); - } - - // Failed, and can't try again because no facilitator - PushFailure(); - return; - } - - if (natPunchthroughDebugInterface) - { - char ipAddressString[32]; - sp.targetAddress.ToString(true, ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("First punchthrough failure on guid %s, system address %s. Reattempting.", guidString, ipAddressString)); - } - - // Failed the first time. Add to the failure queue and try again - AddrAndGuid aag; - aag.addr=sp.targetAddress; - aag.guid=sp.targetGuid; - failedAttemptList.Push(aag, _FILE_AND_LINE_); - - // Tell the server we are ready - OnReadyForNextPunchthrough(); - - // If we are the sender, try again, immediately if possible, else added to the queue on the faciltiator - if (sp.weAreSender) - SendPunchthrough(sp.targetGuid, sp.facilitator); -} -PluginReceiveResult NatPunchthroughClient::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_NAT_GET_MOST_RECENT_PORT: - { - OnGetMostRecentPort(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_NAT_PUNCHTHROUGH_FAILED: - case ID_NAT_PUNCHTHROUGH_SUCCEEDED: - if (packet->wasGeneratedLocally==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - break; - case ID_OUT_OF_BAND_INTERNAL: - if (packet->length>=2 && - (packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL || packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL) && - sp.nextActionTime!=0) - { - RakNet::BitStream bs(packet->data,packet->length,false); - bs.IgnoreBytes(2); - uint16_t sessionId; - bs.Read(sessionId); -// RakAssert(sessionId<100); - if (sessionId!=sp.sessionId) - break; - - char ipAddressString[32]; - packet->systemAddress.ToString(true,ipAddressString); - // sp.targetGuid==packet->guid is because the internal IP addresses reported may include loopbacks not reported by RakPeer::IsLocalIP() - if (packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL && sp.targetGuid==packet->guid) - { - if (natPunchthroughDebugInterface) - { - char guidString[128]; - sp.targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Received ID_NAT_ESTABLISH_UNIDIRECTIONAL from guid %s, system address %s.", guidString, ipAddressString)); - } - if (sp.testMode!=SendPing::PUNCHING_FIXED_PORT) - { - sp.testMode=SendPing::PUNCHING_FIXED_PORT; - sp.retryCount+=sp.attemptCount*pc.UDP_SENDS_PER_PORT_EXTERNAL; - sp.targetAddress=packet->systemAddress; - // Keeps trying until the other side gives up too, in case it is unidirectional - sp.punchingFixedPortAttempts=pc.UDP_SENDS_PER_PORT_EXTERNAL*(pc.MAX_PREDICTIVE_PORT_RANGE+1); - } - - SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); - } - else if (packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL && - sp.targetGuid==packet->guid) - { - // They send back our port - bs.Read(mostRecentNewExternalPort); - - SendOutOfBand(packet->systemAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); - - // Tell the user about the success - sp.targetAddress=packet->systemAddress; - PushSuccess(); - UpdateGroupPunchOnNatResult(sp.facilitator, sp.targetGuid, sp.targetAddress, 1); - OnReadyForNextPunchthrough(); - bool removedFromFailureQueue=RemoveFromFailureQueue(); - - if (natPunchthroughDebugInterface) - { - char guidString[128]; - sp.targetGuid.ToString(guidString); - if (removedFromFailureQueue) - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 2nd attempt.", guidString, ipAddressString)); - else - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 1st attempt.", guidString, ipAddressString)); - } - } - - // mostRecentNewExternalPort=packet->systemAddress.GetPort(); - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_NAT_ALREADY_IN_PROGRESS: - { - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID targetGuid; - incomingBs.Read(targetGuid); - // Don't update group, just use later message - // UpdateGroupPunchOnNatResult(packet->systemAddress, targetGuid, UNASSIGNED_SYSTEM_ADDRESS, 2); - if (natPunchthroughDebugInterface) - { - char guidString[128]; - targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to ID_NAT_ALREADY_IN_PROGRESS. Returning failure.", guidString)); - } - - } - break; - case ID_NAT_TARGET_NOT_CONNECTED: - case ID_NAT_CONNECTION_TO_TARGET_LOST: - case ID_NAT_TARGET_UNRESPONSIVE: - { - const char *reason; - if (packet->data[0]==ID_NAT_TARGET_NOT_CONNECTED) - reason=(char *)"ID_NAT_TARGET_NOT_CONNECTED"; - else if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST) - reason=(char *)"ID_NAT_CONNECTION_TO_TARGET_LOST"; - else - reason=(char *)"ID_NAT_TARGET_UNRESPONSIVE"; - - - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - - RakNetGUID targetGuid; - incomingBs.Read(targetGuid); - UpdateGroupPunchOnNatResult(packet->systemAddress, targetGuid, UNASSIGNED_SYSTEM_ADDRESS, 2); - - if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST || - packet->data[0]==ID_NAT_TARGET_UNRESPONSIVE) - { - uint16_t sessionId; - incomingBs.Read(sessionId); - if (sessionId!=sp.sessionId) - break; - } - - unsigned int i; - for (i=0; i < failedAttemptList.Size(); i++) - { - if (failedAttemptList[i].guid==targetGuid) - { - if (natPunchthroughDebugInterface) - { - char guidString[128]; - targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to %s.", guidString, reason)); - - } - - // If the retry target is not connected, or loses connection, or is not responsive, then previous failures cannot be retried. - - // Don't need to return failed, the other messages indicate failure anyway - /* - Packet *p = AllocatePacketUnified(sizeof(MessageID)); - p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED; - p->systemAddress=failedAttemptList[i].addr; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=failedAttemptList[i].guid; - rakPeerInterface->PushBackPacket(p, false); - */ - - failedAttemptList.RemoveAtIndexFast(i); - break; - } - } - - if (natPunchthroughDebugInterface) - { - char guidString[128]; - targetGuid.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough attempt to guid %s failed due to %s.", guidString, reason)); - } - - // Stop trying punchthrough - sp.nextActionTime=0; - - /* - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID failedSystem; - bs.Read(failedSystem); - bool deletedFirst=false; - unsigned int i=0; - while (i < pendingOpenNAT.Size()) - { - if (pendingOpenNAT[i].destination==failedSystem) - { - if (i==0) - deletedFirst=true; - pendingOpenNAT.RemoveAtIndex(i); - } - else - i++; - } - // Failed while in progress. Go to next in attempt queue - if (deletedFirst && pendingOpenNAT.Size()) - { - SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator); - sp.nextActionTime=0; - } - */ - } - break; - case ID_TIMESTAMP: - if (packet->data[sizeof(MessageID)+sizeof(RakNet::Time)]==ID_NAT_CONNECT_AT_TIME) - { - OnConnectAtTime(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - break; - } - return RR_CONTINUE_PROCESSING; -} -/* -void NatPunchthroughClient::ProcessNextPunchthroughQueue(void) -{ - // Go to the next attempt - if (pendingOpenNAT.Size()) - pendingOpenNAT.RemoveAtIndex(0); - - // Do next punchthrough attempt - if (pendingOpenNAT.Size()) - SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator); - - sp.nextActionTime=0; -} -*/ -void NatPunchthroughClient::OnConnectAtTime(Packet *packet) -{ -// RakAssert(sp.nextActionTime==0); - - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.Read(sp.nextActionTime); - bs.IgnoreBytes(sizeof(MessageID)); - bs.Read(sp.sessionId); - bs.Read(sp.targetAddress); - int j; -// int k; -// k=0; - for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) - bs.Read(sp.internalIds[j]); - - // Prevents local testing - /* - for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) - { - SystemAddress id; - bs.Read(id); - char str[32]; - id.ToString(false,str); - if (rakPeerInterface->IsLocalIP(str)==false) - sp.internalIds[k++]=id; - } - */ - sp.attemptCount=0; - sp.retryCount=0; - if (pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK>0) - { - sp.testMode=SendPing::TESTING_INTERNAL_IPS; - } - else - { - // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned - sp.testMode=SendPing::SEND_WITH_TTL; - // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; - } - bs.Read(sp.targetGuid); - bs.Read(sp.weAreSender); -} -void NatPunchthroughClient::SendTTL(const SystemAddress &sa) -{ - if (sa==UNASSIGNED_SYSTEM_ADDRESS) - return; - if (sa.GetPort()==0) - return; - - char ipAddressString[32]; - sa.ToString(false, ipAddressString); - // TTL of 1 doesn't get past the router, 2 might hit the other system on a LAN - rakPeerInterface->SendTTL(ipAddressString,sa.GetPort(), 2); -} -void NatPunchthroughClient::SendOutOfBand(SystemAddress sa, MessageID oobId) -{ - if (sa==UNASSIGNED_SYSTEM_ADDRESS) - return; - if (sa.GetPort()==0) - return; - - RakNet::BitStream oob; - oob.Write(oobId); - oob.Write(sp.sessionId); -// RakAssert(sp.sessionId<100); - if (oobId==ID_NAT_ESTABLISH_BIDIRECTIONAL) - oob.Write(sa.GetPort()); - char ipAddressString[32]; - sa.ToString(false, ipAddressString); - rakPeerInterface->SendOutOfBand((const char*) ipAddressString,sa.GetPort(),(const char*) oob.GetData(),oob.GetNumberOfBytesUsed()); - - if (natPunchthroughDebugInterface) - { - sa.ToString(true,ipAddressString); - char guidString[128]; - sp.targetGuid.ToString(guidString); - - if (oobId==ID_NAT_ESTABLISH_UNIDIRECTIONAL) - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_UNIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString)); - else - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_BIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString)); - } -} -void NatPunchthroughClient::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) rakNetGUID; - (void) isIncoming; - - // Try to track new port mappings on the router. Not reliable, but better than nothing. - SystemAddress ourExternalId = rakPeerInterface->GetExternalID(systemAddress); - if (ourExternalId!=UNASSIGNED_SYSTEM_ADDRESS) - mostRecentNewExternalPort=ourExternalId.GetPort(); - - unsigned int i; - i=0; - while (i < groupRequestsInProgress.Size()) - { - if (groupRequestsInProgress[i].guid==rakNetGUID) - { - groupRequestsInProgress.RemoveAtIndexFast(i); - } - else - { - i++; - } - } -} - -void NatPunchthroughClient::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) systemAddress; - (void) rakNetGUID; - (void) lostConnectionReason; - - if (sp.facilitator==systemAddress) - { - // If we lose the connection to the facilitator, all previous failures not currently in progress are returned as such - unsigned int i=0; - while (i < failedAttemptList.Size()) - { - if (sp.nextActionTime!=0 && sp.targetGuid==failedAttemptList[i].guid) - { - i++; - continue; - } - - PushFailure(); - - failedAttemptList.RemoveAtIndexFast(i); - } - } - - unsigned int i; - i=0; - while (i < groupPunchRequests.Size()) - { - if (groupPunchRequests[i]->facilitator==systemAddress) - { - RakNet::OP_DELETE(groupPunchRequests[i],_FILE_AND_LINE_); - groupPunchRequests.RemoveAtIndexFast(i); - } - else - { - i++; - } - } - -} -void NatPunchthroughClient::GetUPNPPortMappings(char *externalPort, char *internalPort, const SystemAddress &natPunchthroughServerAddress) -{ - DataStructures::List > sockets; - rakPeerInterface->GetSockets(sockets); - Itoa(sockets[0]->boundAddress.GetPort(),internalPort,10); - if (mostRecentNewExternalPort==0) - mostRecentNewExternalPort=rakPeerInterface->GetExternalID(natPunchthroughServerAddress).GetPort(); - Itoa(mostRecentNewExternalPort,externalPort,10); -} -void NatPunchthroughClient::OnFailureNotification(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data,packet->length,false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID senderGuid; - incomingBs.Read(senderGuid); - - unsigned int i; - i=0; - while (i < groupRequestsInProgress.Size()) - { - if (groupRequestsInProgress[i].guid==senderGuid) - { - groupRequestsInProgress.RemoveAtIndexFast(i); - break; - } - else - { - i++; - } - } -} -void NatPunchthroughClient::OnGetMostRecentPort(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data,packet->length,false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - uint16_t sessionId; - incomingBs.Read(sessionId); - - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT); - outgoingBs.Write(sessionId); - if (mostRecentNewExternalPort==0) - mostRecentNewExternalPort=rakPeerInterface->GetExternalID(packet->systemAddress).GetPort(); - RakAssert(mostRecentNewExternalPort!=0); - outgoingBs.Write(mostRecentNewExternalPort); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - sp.facilitator=packet->systemAddress; -} -/* -unsigned int NatPunchthroughClient::GetPendingOpenNATIndex(RakNetGUID destination, const SystemAddress &facilitator) -{ - unsigned int i; - for (i=0; i < pendingOpenNAT.Size(); i++) - { - if (pendingOpenNAT[i].destination==destination && pendingOpenNAT[i].facilitator==facilitator) - return i; - } - return (unsigned int) -1; -} -*/ -void NatPunchthroughClient::SendPunchthrough(RakNetGUID destination, const SystemAddress &facilitator) -{ - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_PUNCHTHROUGH_REQUEST); - outgoingBs.Write(destination); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,facilitator,false); - -// RakAssert(rakPeerInterface->GetSystemAddressFromGuid(destination)==UNASSIGNED_SYSTEM_ADDRESS); - - if (natPunchthroughDebugInterface) - { - char guidString[128]; - destination.ToString(guidString); - natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Starting ID_NAT_PUNCHTHROUGH_REQUEST to guid %s.", guidString)); - } -} -void NatPunchthroughClient::OnAttach(void) -{ - Clear(); -} -void NatPunchthroughClient::OnDetach(void) -{ - Clear(); -} -void NatPunchthroughClient::OnRakPeerShutdown(void) -{ - Clear(); -} -void NatPunchthroughClient::Clear(void) -{ - OnReadyForNextPunchthrough(); - - failedAttemptList.Clear(false, _FILE_AND_LINE_); - groupRequestsInProgress.Clear(false, _FILE_AND_LINE_); - unsigned int i; - for (i=0; i < groupPunchRequests.Size(); i++) - { - RakNet::OP_DELETE(groupPunchRequests[i],_FILE_AND_LINE_); - } - groupPunchRequests.Clear(true, _FILE_AND_LINE_); -} -PunchthroughConfiguration* NatPunchthroughClient::GetPunchthroughConfiguration(void) -{ - return &pc; -} -void NatPunchthroughClient::OnReadyForNextPunchthrough(void) -{ - if (rakPeerInterface==0) - return; - - sp.nextActionTime=0; - - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_CLIENT_READY); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sp.facilitator,false); -} - -void NatPunchthroughClient::PushSuccess(void) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); - p->data[0]=ID_NAT_PUNCHTHROUGH_SUCCEEDED; - p->systemAddress=sp.targetAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=sp.targetGuid; - if (sp.weAreSender) - p->data[1]=1; - else - p->data[1]=0; - p->wasGeneratedLocally=true; - rakPeerInterface->PushBackPacket(p, true); -} -bool NatPunchthroughClient::RemoveFromFailureQueue(void) -{ - unsigned int i; - for (i=0; i < failedAttemptList.Size(); i++) - { - if (failedAttemptList[i].guid==sp.targetGuid) - { - // Remove from failure queue - failedAttemptList.RemoveAtIndexFast(i); - return true; - } - } - return false; -} - -void NatPunchthroughClient::IncrementExternalAttemptCount(RakNet::Time time, RakNet::Time delta) -{ - if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_EXTERNAL) - { - ++sp.attemptCount; - sp.retryCount=0; - sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta; - } - else - { - sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta; - } -} -// 0=failed, 1=success, 2=ignore -void NatPunchthroughClient::UpdateGroupPunchOnNatResult(SystemAddress facilitator, RakNetGUID targetSystem, SystemAddress targetSystemAddress, int result) -{ - GroupPunchRequest *gpr; - unsigned long i,j,k; - i=0; - while (i < groupPunchRequests.Size()) - { - gpr = groupPunchRequests[i]; - if (gpr->facilitator==facilitator) - { - j=0; - while (j < gpr->pendingList.Size()) - { - if (gpr->pendingList[j]==targetSystem) - { - if (result==0) - { - gpr->failedList.Push(targetSystem, _FILE_AND_LINE_); - } - else if (result==1) - { - gpr->passedListGuid.Push(targetSystem, _FILE_AND_LINE_); - gpr->passedListAddress.Push(targetSystemAddress, _FILE_AND_LINE_); - } - else - { - gpr->ignoredList.Push(targetSystem, _FILE_AND_LINE_); - } - gpr->pendingList.RemoveAtIndex(j); - } - else - j++; - } - } - if (gpr->pendingList.Size()==0) - { - RakNet::BitStream output; - if (gpr->failedList.Size()==0) - { - output.Write(ID_NAT_GROUP_PUNCH_SUCCEEDED); - } - else - { - output.Write(ID_NAT_GROUP_PUNCH_FAILED); - } - - output.WriteCasted(gpr->passedListGuid.Size()); - for (k=0; k < gpr->passedListGuid.Size(); k++) - { - output.Write(gpr->passedListGuid[k]); - output.Write(gpr->passedListAddress[k]); - } - output.WriteCasted(gpr->ignoredList.Size()); - for (k=0; k < gpr->ignoredList.Size(); k++) - { - output.Write(gpr->ignoredList[k]); - } - output.WriteCasted(gpr->failedList.Size()); - for (k=0; k < gpr->failedList.Size(); k++) - { - output.Write(gpr->failedList[k]); - } - - Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed()); - p->systemAddress=gpr->facilitator; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=rakPeerInterface->GetGuidFromSystemAddress(gpr->facilitator); - p->wasGeneratedLocally=true; - memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed()); - rakPeerInterface->PushBackPacket(p, true); - - groupPunchRequests.RemoveAtIndex(i); - RakNet::OP_DELETE(gpr, _FILE_AND_LINE_); - } - else - i++; - } -} - -#endif // _RAKNET_SUPPORT_* - +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatPunchthroughClient==1 + +#include "NatPunchthroughClient.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "GetTime.h" +#include "PacketLogger.h" +#include "Itoa.h" + +using namespace RakNet; + +void NatPunchthroughDebugInterface_Printf::OnClientMessage(const char *msg) +{ + printf("%s\n", msg); +} +#if _RAKNET_SUPPORT_PacketLogger==1 +void NatPunchthroughDebugInterface_PacketLogger::OnClientMessage(const char *msg) +{ + if (pl) + { + pl->WriteMiscellaneous("Nat", msg); + } +} +#endif + +STATIC_FACTORY_DEFINITIONS(NatPunchthroughClient,NatPunchthroughClient); + +NatPunchthroughClient::NatPunchthroughClient() +{ + natPunchthroughDebugInterface=0; + mostRecentNewExternalPort=0; + sp.nextActionTime=0; +} +NatPunchthroughClient::~NatPunchthroughClient() +{ + rakPeerInterface=0; + Clear(); +} +bool NatPunchthroughClient::OpenNAT(RakNetGUID destination, const SystemAddress &facilitator) +{ + ConnectionState cs = rakPeerInterface->GetConnectionState(facilitator); + if (cs!=IS_CONNECTED) + return false; + + SendPunchthrough(destination, facilitator); + return true; +} +bool NatPunchthroughClient::OpenNATGroup(DataStructures::List destinationSystems, const SystemAddress &facilitator) +{ + ConnectionState cs = rakPeerInterface->GetConnectionState(facilitator); + if (cs!=IS_CONNECTED) + return false; + + unsigned long i; + for (i=0; i < destinationSystems.Size(); i++) + { + SendPunchthrough(destinationSystems[i], facilitator); + } + + GroupPunchRequest *gpr = RakNet::OP_NEW(_FILE_AND_LINE_); + gpr->facilitator=facilitator; + gpr->pendingList=destinationSystems; + groupPunchRequests.Push(gpr, _FILE_AND_LINE_); + + return true; +} +void NatPunchthroughClient::SetDebugInterface(NatPunchthroughDebugInterface *i) +{ + natPunchthroughDebugInterface=i; +} +void NatPunchthroughClient::Update(void) +{ + RakNet::Time time = RakNet::GetTime(); + if (sp.nextActionTime && sp.nextActionTime < time) + { + RakNet::Time delta = time - sp.nextActionTime; + if (sp.testMode==SendPing::TESTING_INTERNAL_IPS) + { + SendOutOfBand(sp.internalIds[sp.attemptCount],ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_INTERNAL) + { + ++sp.attemptCount; + sp.retryCount=0; + } + + if (sp.attemptCount>=pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK) + { + sp.testMode=SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE; + if (pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS>0) + { + sp.nextActionTime=time+pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS-delta; + } + else + { + // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned + sp.testMode=SendPing::SEND_WITH_TTL; + // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; + sp.attemptCount=0; + } + } + else + { + sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL-delta; + } + } + else if (sp.testMode==SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE) + { + // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned + sp.testMode=SendPing::SEND_WITH_TTL; + // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; + sp.attemptCount=0; + } + else if (sp.testMode==SendPing::SEND_WITH_TTL) + { + // See http://www.jenkinssoftware.com/forum/index.php?topic=4021.0 + // For Linux 2.6.32 soft-router (ip-tables) + /* + If I understand correctly, getting a datagram on a particular address that was not previously used causes that the port for that address to not be used for when a reply would have otherwise been sent back from that address. + + PHASE 1: + 1. System 1 and 2 send to each other. + 2. Due to latency, system 1 sends first using the server port. + 3. System 2 gets the datagram and no longer uses the server port. Instead, it replies using port 1024. + 4. System 1 gets the reply. The source port is wrong, so it is rejected. + + To put it another way, if a router gets a datagram on a port that was not previously used, it will not reply on that port. However, if it doesn't reply on that port, the message will not be accepted by the remote system. + */ + + // Send to unused port. We do not want the message to arrive, just to open our router's table + SystemAddress sa=sp.targetAddress; + int ttlSendIndex; + for (ttlSendIndex=0; ttlSendIndex <= pc.MAX_PREDICTIVE_PORT_RANGE; ttlSendIndex++) + { + sa.SetPort((unsigned short) (sp.targetAddress.GetPort()+ttlSendIndex)); + SendTTL(sa); + } + + // Only do this stage once + // Wait 250 milliseconds for next stage. The delay is so that even with timing errors both systems send out the + // datagram with TTL before either sends a real one + sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; + sp.nextActionTime=time-delta+250; + } + else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT) + { + SystemAddress sa=sp.targetAddress; + sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); + SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + IncrementExternalAttemptCount(time, delta); + + if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) + { + sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; + + // Skip TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT, etc. + /* + sp.testMode=SendPing::TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT; + sp.attemptCount=0; + */ + } + } + else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT) + { + SystemAddress sa=sp.targetAddress; + if ( sp.targetGuid < rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) ) + sa.SetPort((unsigned short) (1024+sp.attemptCount)); + else + sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); + SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + IncrementExternalAttemptCount(time, delta); + + if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) + { + // From 1024 disabled, never helps as I've seen, but slows down the process by half + sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024; + sp.attemptCount=0; + } + + } + else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024) + { + SystemAddress sa=sp.targetAddress; + if ( sp.targetGuid > rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) ) + sa.SetPort((unsigned short) (1024+sp.attemptCount)); + else + sa.SetPort((unsigned short) (sa.GetPort()+sp.attemptCount)); + SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + IncrementExternalAttemptCount(time, delta); + + if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) + { + // From 1024 disabled, never helps as I've seen, but slows down the process by half + sp.testMode=SendPing::TESTING_EXTERNAL_IPS_1024_TO_1024; + sp.attemptCount=0; + } + } + else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_1024_TO_1024) + { + SystemAddress sa=sp.targetAddress; + sa.SetPort((unsigned short) (1024+sp.attemptCount)); + SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL); + + IncrementExternalAttemptCount(time, delta); + + if (sp.attemptCount>pc.MAX_PREDICTIVE_PORT_RANGE) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely bidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString)); + } + + sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; + } + } + else if (sp.testMode==SendPing::WAITING_AFTER_ALL_ATTEMPTS) + { + // Failed. Tell the user + OnPunchthroughFailure(); + UpdateGroupPunchOnNatResult(sp.facilitator, sp.targetGuid, sp.targetAddress, 1); + } + + if (sp.testMode==SendPing::PUNCHING_FIXED_PORT) + { + SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); + if (++sp.retryCount>=sp.punchingFixedPortAttempts) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely unidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString)); + } + + sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS; + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta; + } + else + { + if ((sp.retryCount%pc.UDP_SENDS_PER_PORT_EXTERNAL)==0) + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta; + else + sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta; + } + } + } + + // Remove elapsed groupRequestsInProgress + unsigned int i; + i=0; + while (i < groupRequestsInProgress.Size()) + { + if (time > groupRequestsInProgress[i].time) + groupRequestsInProgress.RemoveAtIndexFast(i); + else + i++; + } +} +void NatPunchthroughClient::PushFailure(void) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); + p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED; + p->systemAddress=sp.targetAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=sp.targetGuid; + if (sp.weAreSender) + p->data[1]=1; + else + p->data[1]=0; + p->wasGeneratedLocally=true; + rakPeerInterface->PushBackPacket(p, true); +} +void NatPunchthroughClient::OnPunchthroughFailure(void) +{ + if (pc.retryOnFailure==false) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough once. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString)); + } + + PushFailure(); + OnReadyForNextPunchthrough(); + return; + } + + unsigned int i; + for (i=0; i < failedAttemptList.Size(); i++) + { + if (failedAttemptList[i].guid==sp.targetGuid) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough twice. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString)); + } + + // Failed a second time, so return failed to user + PushFailure(); + + OnReadyForNextPunchthrough(); + + failedAttemptList.RemoveAtIndexFast(i); + return; + } + } + + if (rakPeerInterface->GetConnectionState(sp.facilitator)!=IS_CONNECTED) + { + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Not connected to facilitator, so cannot retry punchthrough after first failure. Returning failure onj guid %s, system address %s to user.", guidString, ipAddressString)); + } + + // Failed, and can't try again because no facilitator + PushFailure(); + return; + } + + if (natPunchthroughDebugInterface) + { + char ipAddressString[32]; + sp.targetAddress.ToString(true, ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("First punchthrough failure on guid %s, system address %s. Reattempting.", guidString, ipAddressString)); + } + + // Failed the first time. Add to the failure queue and try again + AddrAndGuid aag; + aag.addr=sp.targetAddress; + aag.guid=sp.targetGuid; + failedAttemptList.Push(aag, _FILE_AND_LINE_); + + // Tell the server we are ready + OnReadyForNextPunchthrough(); + + // If we are the sender, try again, immediately if possible, else added to the queue on the faciltiator + if (sp.weAreSender) + SendPunchthrough(sp.targetGuid, sp.facilitator); +} +PluginReceiveResult NatPunchthroughClient::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_NAT_GET_MOST_RECENT_PORT: + { + OnGetMostRecentPort(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_NAT_PUNCHTHROUGH_FAILED: + case ID_NAT_PUNCHTHROUGH_SUCCEEDED: + if (packet->wasGeneratedLocally==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + break; + case ID_OUT_OF_BAND_INTERNAL: + if (packet->length>=2 && + (packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL || packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL) && + sp.nextActionTime!=0) + { + RakNet::BitStream bs(packet->data,packet->length,false); + bs.IgnoreBytes(2); + uint16_t sessionId; + bs.Read(sessionId); +// RakAssert(sessionId<100); + if (sessionId!=sp.sessionId) + break; + + char ipAddressString[32]; + packet->systemAddress.ToString(true,ipAddressString); + // sp.targetGuid==packet->guid is because the internal IP addresses reported may include loopbacks not reported by RakPeer::IsLocalIP() + if (packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL && sp.targetGuid==packet->guid) + { + if (natPunchthroughDebugInterface) + { + char guidString[128]; + sp.targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Received ID_NAT_ESTABLISH_UNIDIRECTIONAL from guid %s, system address %s.", guidString, ipAddressString)); + } + if (sp.testMode!=SendPing::PUNCHING_FIXED_PORT) + { + sp.testMode=SendPing::PUNCHING_FIXED_PORT; + sp.retryCount+=sp.attemptCount*pc.UDP_SENDS_PER_PORT_EXTERNAL; + sp.targetAddress=packet->systemAddress; + // Keeps trying until the other side gives up too, in case it is unidirectional + sp.punchingFixedPortAttempts=pc.UDP_SENDS_PER_PORT_EXTERNAL*(pc.MAX_PREDICTIVE_PORT_RANGE+1); + } + + SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); + } + else if (packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL && + sp.targetGuid==packet->guid) + { + // They send back our port + bs.Read(mostRecentNewExternalPort); + + SendOutOfBand(packet->systemAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL); + + // Tell the user about the success + sp.targetAddress=packet->systemAddress; + PushSuccess(); + UpdateGroupPunchOnNatResult(sp.facilitator, sp.targetGuid, sp.targetAddress, 1); + OnReadyForNextPunchthrough(); + bool removedFromFailureQueue=RemoveFromFailureQueue(); + + if (natPunchthroughDebugInterface) + { + char guidString[128]; + sp.targetGuid.ToString(guidString); + if (removedFromFailureQueue) + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 2nd attempt.", guidString, ipAddressString)); + else + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 1st attempt.", guidString, ipAddressString)); + } + } + + // mostRecentNewExternalPort=packet->systemAddress.GetPort(); + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_NAT_ALREADY_IN_PROGRESS: + { + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID targetGuid; + incomingBs.Read(targetGuid); + // Don't update group, just use later message + // UpdateGroupPunchOnNatResult(packet->systemAddress, targetGuid, UNASSIGNED_SYSTEM_ADDRESS, 2); + if (natPunchthroughDebugInterface) + { + char guidString[128]; + targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to ID_NAT_ALREADY_IN_PROGRESS. Returning failure.", guidString)); + } + + } + break; + case ID_NAT_TARGET_NOT_CONNECTED: + case ID_NAT_CONNECTION_TO_TARGET_LOST: + case ID_NAT_TARGET_UNRESPONSIVE: + { + const char *reason; + if (packet->data[0]==ID_NAT_TARGET_NOT_CONNECTED) + reason=(char *)"ID_NAT_TARGET_NOT_CONNECTED"; + else if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST) + reason=(char *)"ID_NAT_CONNECTION_TO_TARGET_LOST"; + else + reason=(char *)"ID_NAT_TARGET_UNRESPONSIVE"; + + + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + + RakNetGUID targetGuid; + incomingBs.Read(targetGuid); + UpdateGroupPunchOnNatResult(packet->systemAddress, targetGuid, UNASSIGNED_SYSTEM_ADDRESS, 2); + + if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST || + packet->data[0]==ID_NAT_TARGET_UNRESPONSIVE) + { + uint16_t sessionId; + incomingBs.Read(sessionId); + if (sessionId!=sp.sessionId) + break; + } + + unsigned int i; + for (i=0; i < failedAttemptList.Size(); i++) + { + if (failedAttemptList[i].guid==targetGuid) + { + if (natPunchthroughDebugInterface) + { + char guidString[128]; + targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to %s.", guidString, reason)); + + } + + // If the retry target is not connected, or loses connection, or is not responsive, then previous failures cannot be retried. + + // Don't need to return failed, the other messages indicate failure anyway + /* + Packet *p = AllocatePacketUnified(sizeof(MessageID)); + p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED; + p->systemAddress=failedAttemptList[i].addr; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=failedAttemptList[i].guid; + rakPeerInterface->PushBackPacket(p, false); + */ + + failedAttemptList.RemoveAtIndexFast(i); + break; + } + } + + if (natPunchthroughDebugInterface) + { + char guidString[128]; + targetGuid.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough attempt to guid %s failed due to %s.", guidString, reason)); + } + + // Stop trying punchthrough + sp.nextActionTime=0; + + /* + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID failedSystem; + bs.Read(failedSystem); + bool deletedFirst=false; + unsigned int i=0; + while (i < pendingOpenNAT.Size()) + { + if (pendingOpenNAT[i].destination==failedSystem) + { + if (i==0) + deletedFirst=true; + pendingOpenNAT.RemoveAtIndex(i); + } + else + i++; + } + // Failed while in progress. Go to next in attempt queue + if (deletedFirst && pendingOpenNAT.Size()) + { + SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator); + sp.nextActionTime=0; + } + */ + } + break; + case ID_TIMESTAMP: + if (packet->data[sizeof(MessageID)+sizeof(RakNet::Time)]==ID_NAT_CONNECT_AT_TIME) + { + OnConnectAtTime(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + break; + } + return RR_CONTINUE_PROCESSING; +} +/* +void NatPunchthroughClient::ProcessNextPunchthroughQueue(void) +{ + // Go to the next attempt + if (pendingOpenNAT.Size()) + pendingOpenNAT.RemoveAtIndex(0); + + // Do next punchthrough attempt + if (pendingOpenNAT.Size()) + SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator); + + sp.nextActionTime=0; +} +*/ +void NatPunchthroughClient::OnConnectAtTime(Packet *packet) +{ +// RakAssert(sp.nextActionTime==0); + + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.Read(sp.nextActionTime); + bs.IgnoreBytes(sizeof(MessageID)); + bs.Read(sp.sessionId); + bs.Read(sp.targetAddress); + int j; +// int k; +// k=0; + for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) + bs.Read(sp.internalIds[j]); + + // Prevents local testing + /* + for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) + { + SystemAddress id; + bs.Read(id); + char str[32]; + id.ToString(false,str); + if (rakPeerInterface->IsLocalIP(str)==false) + sp.internalIds[k++]=id; + } + */ + sp.attemptCount=0; + sp.retryCount=0; + if (pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK>0) + { + sp.testMode=SendPing::TESTING_INTERNAL_IPS; + } + else + { + // TESTING: Try sending to unused ports on the remote system to reserve our own ports while not getting banned + sp.testMode=SendPing::SEND_WITH_TTL; + // sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT; + } + bs.Read(sp.targetGuid); + bs.Read(sp.weAreSender); +} +void NatPunchthroughClient::SendTTL(const SystemAddress &sa) +{ + if (sa==UNASSIGNED_SYSTEM_ADDRESS) + return; + if (sa.GetPort()==0) + return; + + char ipAddressString[32]; + sa.ToString(false, ipAddressString); + // TTL of 1 doesn't get past the router, 2 might hit the other system on a LAN + rakPeerInterface->SendTTL(ipAddressString,sa.GetPort(), 2); +} +void NatPunchthroughClient::SendOutOfBand(SystemAddress sa, MessageID oobId) +{ + if (sa==UNASSIGNED_SYSTEM_ADDRESS) + return; + if (sa.GetPort()==0) + return; + + RakNet::BitStream oob; + oob.Write(oobId); + oob.Write(sp.sessionId); +// RakAssert(sp.sessionId<100); + if (oobId==ID_NAT_ESTABLISH_BIDIRECTIONAL) + oob.Write(sa.GetPort()); + char ipAddressString[32]; + sa.ToString(false, ipAddressString); + rakPeerInterface->SendOutOfBand((const char*) ipAddressString,sa.GetPort(),(const char*) oob.GetData(),oob.GetNumberOfBytesUsed()); + + if (natPunchthroughDebugInterface) + { + sa.ToString(true,ipAddressString); + char guidString[128]; + sp.targetGuid.ToString(guidString); + + if (oobId==ID_NAT_ESTABLISH_UNIDIRECTIONAL) + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_UNIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString)); + else + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_BIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString)); + } +} +void NatPunchthroughClient::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) rakNetGUID; + (void) isIncoming; + + // Try to track new port mappings on the router. Not reliable, but better than nothing. + SystemAddress ourExternalId = rakPeerInterface->GetExternalID(systemAddress); + if (ourExternalId!=UNASSIGNED_SYSTEM_ADDRESS) + mostRecentNewExternalPort=ourExternalId.GetPort(); + + unsigned int i; + i=0; + while (i < groupRequestsInProgress.Size()) + { + if (groupRequestsInProgress[i].guid==rakNetGUID) + { + groupRequestsInProgress.RemoveAtIndexFast(i); + } + else + { + i++; + } + } +} + +void NatPunchthroughClient::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) systemAddress; + (void) rakNetGUID; + (void) lostConnectionReason; + + if (sp.facilitator==systemAddress) + { + // If we lose the connection to the facilitator, all previous failures not currently in progress are returned as such + unsigned int i=0; + while (i < failedAttemptList.Size()) + { + if (sp.nextActionTime!=0 && sp.targetGuid==failedAttemptList[i].guid) + { + i++; + continue; + } + + PushFailure(); + + failedAttemptList.RemoveAtIndexFast(i); + } + } + + unsigned int i; + i=0; + while (i < groupPunchRequests.Size()) + { + if (groupPunchRequests[i]->facilitator==systemAddress) + { + RakNet::OP_DELETE(groupPunchRequests[i],_FILE_AND_LINE_); + groupPunchRequests.RemoveAtIndexFast(i); + } + else + { + i++; + } + } + +} +void NatPunchthroughClient::GetUPNPPortMappings(char *externalPort, char *internalPort, const SystemAddress &natPunchthroughServerAddress) +{ + DataStructures::List > sockets; + rakPeerInterface->GetSockets(sockets); + Itoa(sockets[0]->boundAddress.GetPort(),internalPort,10); + if (mostRecentNewExternalPort==0) + mostRecentNewExternalPort=rakPeerInterface->GetExternalID(natPunchthroughServerAddress).GetPort(); + Itoa(mostRecentNewExternalPort,externalPort,10); +} +void NatPunchthroughClient::OnFailureNotification(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data,packet->length,false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID senderGuid; + incomingBs.Read(senderGuid); + + unsigned int i; + i=0; + while (i < groupRequestsInProgress.Size()) + { + if (groupRequestsInProgress[i].guid==senderGuid) + { + groupRequestsInProgress.RemoveAtIndexFast(i); + break; + } + else + { + i++; + } + } +} +void NatPunchthroughClient::OnGetMostRecentPort(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data,packet->length,false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + uint16_t sessionId; + incomingBs.Read(sessionId); + + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT); + outgoingBs.Write(sessionId); + if (mostRecentNewExternalPort==0) + mostRecentNewExternalPort=rakPeerInterface->GetExternalID(packet->systemAddress).GetPort(); + RakAssert(mostRecentNewExternalPort!=0); + outgoingBs.Write(mostRecentNewExternalPort); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + sp.facilitator=packet->systemAddress; +} +/* +unsigned int NatPunchthroughClient::GetPendingOpenNATIndex(RakNetGUID destination, const SystemAddress &facilitator) +{ + unsigned int i; + for (i=0; i < pendingOpenNAT.Size(); i++) + { + if (pendingOpenNAT[i].destination==destination && pendingOpenNAT[i].facilitator==facilitator) + return i; + } + return (unsigned int) -1; +} +*/ +void NatPunchthroughClient::SendPunchthrough(RakNetGUID destination, const SystemAddress &facilitator) +{ + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_PUNCHTHROUGH_REQUEST); + outgoingBs.Write(destination); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,facilitator,false); + +// RakAssert(rakPeerInterface->GetSystemAddressFromGuid(destination)==UNASSIGNED_SYSTEM_ADDRESS); + + if (natPunchthroughDebugInterface) + { + char guidString[128]; + destination.ToString(guidString); + natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Starting ID_NAT_PUNCHTHROUGH_REQUEST to guid %s.", guidString)); + } +} +void NatPunchthroughClient::OnAttach(void) +{ + Clear(); +} +void NatPunchthroughClient::OnDetach(void) +{ + Clear(); +} +void NatPunchthroughClient::OnRakPeerShutdown(void) +{ + Clear(); +} +void NatPunchthroughClient::Clear(void) +{ + OnReadyForNextPunchthrough(); + + failedAttemptList.Clear(false, _FILE_AND_LINE_); + groupRequestsInProgress.Clear(false, _FILE_AND_LINE_); + unsigned int i; + for (i=0; i < groupPunchRequests.Size(); i++) + { + RakNet::OP_DELETE(groupPunchRequests[i],_FILE_AND_LINE_); + } + groupPunchRequests.Clear(true, _FILE_AND_LINE_); +} +PunchthroughConfiguration* NatPunchthroughClient::GetPunchthroughConfiguration(void) +{ + return &pc; +} +void NatPunchthroughClient::OnReadyForNextPunchthrough(void) +{ + if (rakPeerInterface==0) + return; + + sp.nextActionTime=0; + + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_CLIENT_READY); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sp.facilitator,false); +} + +void NatPunchthroughClient::PushSuccess(void) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); + p->data[0]=ID_NAT_PUNCHTHROUGH_SUCCEEDED; + p->systemAddress=sp.targetAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=sp.targetGuid; + if (sp.weAreSender) + p->data[1]=1; + else + p->data[1]=0; + p->wasGeneratedLocally=true; + rakPeerInterface->PushBackPacket(p, true); +} +bool NatPunchthroughClient::RemoveFromFailureQueue(void) +{ + unsigned int i; + for (i=0; i < failedAttemptList.Size(); i++) + { + if (failedAttemptList[i].guid==sp.targetGuid) + { + // Remove from failure queue + failedAttemptList.RemoveAtIndexFast(i); + return true; + } + } + return false; +} + +void NatPunchthroughClient::IncrementExternalAttemptCount(RakNet::Time time, RakNet::Time delta) +{ + if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_EXTERNAL) + { + ++sp.attemptCount; + sp.retryCount=0; + sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta; + } + else + { + sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta; + } +} +// 0=failed, 1=success, 2=ignore +void NatPunchthroughClient::UpdateGroupPunchOnNatResult(SystemAddress facilitator, RakNetGUID targetSystem, SystemAddress targetSystemAddress, int result) +{ + GroupPunchRequest *gpr; + unsigned long i,j,k; + i=0; + while (i < groupPunchRequests.Size()) + { + gpr = groupPunchRequests[i]; + if (gpr->facilitator==facilitator) + { + j=0; + while (j < gpr->pendingList.Size()) + { + if (gpr->pendingList[j]==targetSystem) + { + if (result==0) + { + gpr->failedList.Push(targetSystem, _FILE_AND_LINE_); + } + else if (result==1) + { + gpr->passedListGuid.Push(targetSystem, _FILE_AND_LINE_); + gpr->passedListAddress.Push(targetSystemAddress, _FILE_AND_LINE_); + } + else + { + gpr->ignoredList.Push(targetSystem, _FILE_AND_LINE_); + } + gpr->pendingList.RemoveAtIndex(j); + } + else + j++; + } + } + if (gpr->pendingList.Size()==0) + { + RakNet::BitStream output; + if (gpr->failedList.Size()==0) + { + output.Write(ID_NAT_GROUP_PUNCH_SUCCEEDED); + } + else + { + output.Write(ID_NAT_GROUP_PUNCH_FAILED); + } + + output.WriteCasted(gpr->passedListGuid.Size()); + for (k=0; k < gpr->passedListGuid.Size(); k++) + { + output.Write(gpr->passedListGuid[k]); + output.Write(gpr->passedListAddress[k]); + } + output.WriteCasted(gpr->ignoredList.Size()); + for (k=0; k < gpr->ignoredList.Size(); k++) + { + output.Write(gpr->ignoredList[k]); + } + output.WriteCasted(gpr->failedList.Size()); + for (k=0; k < gpr->failedList.Size(); k++) + { + output.Write(gpr->failedList[k]); + } + + Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed()); + p->systemAddress=gpr->facilitator; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=rakPeerInterface->GetGuidFromSystemAddress(gpr->facilitator); + p->wasGeneratedLocally=true; + memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed()); + rakPeerInterface->PushBackPacket(p, true); + + groupPunchRequests.RemoveAtIndex(i); + RakNet::OP_DELETE(gpr, _FILE_AND_LINE_); + } + else + i++; + } +} + +#endif // _RAKNET_SUPPORT_* + diff --git a/src/raknet/NatPunchthroughClient.h b/src/raknet/NatPunchthroughClient.h index 827de71..c12b390 100755 --- a/src/raknet/NatPunchthroughClient.h +++ b/src/raknet/NatPunchthroughClient.h @@ -1,265 +1,265 @@ - -/// \file -/// \brief Contains the NAT-punchthrough plugin for the client. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatPunchthroughClient==1 - -#ifndef __NAT_PUNCHTHROUGH_CLIENT_H -#define __NAT_PUNCHTHROUGH_CLIENT_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "SocketIncludes.h" -#include "DS_List.h" -#include "RakString.h" - -// Trendnet TEW-632BRP sometimes starts at port 1024 and increments sequentially. -// Zonnet zsr1134we. Replies go out on the net, but are always absorbed by the remote router?? -// Dlink ebr2310 to Trendnet ok -// Trendnet TEW-652BRP to Trendnet 632BRP OK -// Trendnet TEW-632BRP to Trendnet 632BRP OK -// Buffalo WHR-HP-G54 OK -// Netgear WGR614 ok - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -struct Packet; -#if _RAKNET_SUPPORT_PacketLogger==1 -class PacketLogger; -#endif - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT PunchthroughConfiguration -{ - /// internal: (15 ms * 2 tries + 30 wait) * 5 ports * 8 players = 2.4 seconds - /// external: (50 ms * 8 sends + 100 wait) * 2 port * 8 players = 8 seconds - /// Total: 8 seconds - PunchthroughConfiguration() { - TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL=15; - TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL=50; - UDP_SENDS_PER_PORT_INTERNAL=2; - UDP_SENDS_PER_PORT_EXTERNAL=8; - INTERNAL_IP_WAIT_AFTER_ATTEMPTS=30; - MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK=5; /// set to 0 to not do lan connects - MAX_PREDICTIVE_PORT_RANGE=2; - EXTERNAL_IP_WAIT_BETWEEN_PORTS=100; - EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS=EXTERNAL_IP_WAIT_BETWEEN_PORTS; - retryOnFailure=false; - } - - /// How much time between each UDP send - RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL; - RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL; - - /// How many tries for one port before giving up and going to the next port - int UDP_SENDS_PER_PORT_INTERNAL; - int UDP_SENDS_PER_PORT_EXTERNAL; - - /// After giving up on one internal port, how long to wait before trying the next port - int INTERNAL_IP_WAIT_AFTER_ATTEMPTS; - - /// How many external ports to try past the last known starting port - int MAX_PREDICTIVE_PORT_RANGE; - - /// After giving up on one external port, how long to wait before trying the next port - int EXTERNAL_IP_WAIT_BETWEEN_PORTS; - - /// After trying all external ports, how long to wait before returning ID_NAT_PUNCHTHROUGH_FAILED - int EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS; - - /// Maximum number of internal IP address to try to connect to. - /// Cannot be greater than MAXIMUM_NUMBER_OF_INTERNAL_IDS - /// Should be high enough to try all internal IP addresses on the majority of computers - int MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK; - - /// If the first punchthrough attempt fails, try again - /// This sometimes works because the remote router was looking for an incoming message on a higher numbered port before responding to a lower numbered port from the other system - bool retryOnFailure; -}; - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughDebugInterface -{ - NatPunchthroughDebugInterface() {} - virtual ~NatPunchthroughDebugInterface() {} - virtual void OnClientMessage(const char *msg)=0; -}; - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_Printf : public NatPunchthroughDebugInterface -{ - virtual void OnClientMessage(const char *msg); -}; - -#if _RAKNET_SUPPORT_PacketLogger==1 -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_PacketLogger : public NatPunchthroughDebugInterface -{ - // Set to non-zero to write to the packetlogger! - PacketLogger *pl; - - NatPunchthroughDebugInterface_PacketLogger() {pl=0;} - ~NatPunchthroughDebugInterface_PacketLogger() {} - virtual void OnClientMessage(const char *msg); -}; -#endif - -/// \brief Client code for NATPunchthrough -/// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient
      -/// Client will send datagrams to port to estimate next port
      -/// Will simultaneously connect with another client once ports are estimated. -/// \sa NatTypeDetectionClient -/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html -/// \ingroup NAT_PUNCHTHROUGH_GROUP -class RAK_DLL_EXPORT NatPunchthroughClient : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(NatPunchthroughClient) - - NatPunchthroughClient(); - ~NatPunchthroughClient(); - - /// Punchthrough a NAT. Doesn't connect, just tries to setup the routing table - /// \param[in] destination The system to punch. Must already be connected to \a facilitator - /// \param[in] facilitator A system we are already connected to running the NatPunchthroughServer plugin - /// \sa OpenNATGroup() - /// You will get ID_NAT_PUNCHTHROUGH_SUCCEEDED on success - /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_TARGET_UNRESPONSIVE, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_PUNCHTHROUGH_FAILED on failures of various types - /// However, if you lose connection to the facilitator, you may not necessarily get above - bool OpenNAT(RakNetGUID destination, const SystemAddress &facilitator); - - /// Same as calling OpenNAT for a list of systems, but reply is delayed until all systems pass. - /// This is useful for peer to peer games where you want to connect to every system in the remote session, not just one particular system - /// \note For cloud computing, all systems in the group must be connected to the same facilitator since we're only specifying one - /// You will get ID_NAT_GROUP_PUNCH_SUCCEEDED on success - /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_GROUP_PUNCH_FAILED on failures of various types - /// However, if you lose connection to the facilitator, you may not necessarily get above - bool OpenNATGroup(DataStructures::List destinationSystems, const SystemAddress &facilitator); - - /// Modify the system configuration if desired - /// Don't modify the variables in the structure while punchthrough is in progress - PunchthroughConfiguration* GetPunchthroughConfiguration(void); - - /// Sets a callback to be called with debug messages - /// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear. - void SetDebugInterface(NatPunchthroughDebugInterface *i); - - /// Get the port mappings you should pass to UPNP (for miniupnpc-1.5, for the function UPNP_AddPortMapping) - void GetUPNPPortMappings(char *externalPort, char *internalPort, const SystemAddress &natPunchthroughServerAddress); - - /// \internal For plugin handling - virtual void Update(void); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - - /// \internal For plugin handling - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - - /// \internal For plugin handling - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - virtual void OnAttach(void); - virtual void OnDetach(void); - virtual void OnRakPeerShutdown(void); - void Clear(void); - -protected: - unsigned short mostRecentNewExternalPort; - void OnNatGroupPunchthroughRequest(Packet *packet); - void OnFailureNotification(Packet *packet); - void OnNatGroupPunchthroughReply(Packet *packet); - void OnGetMostRecentPort(Packet *packet); - void OnConnectAtTime(Packet *packet); - unsigned int GetPendingOpenNATIndex(RakNetGUID destination, const SystemAddress &facilitator); - void SendPunchthrough(RakNetGUID destination, const SystemAddress &facilitator); - void SendTTL(const SystemAddress &sa); - void SendOutOfBand(SystemAddress sa, MessageID oobId); - void OnPunchthroughFailure(void); - void OnReadyForNextPunchthrough(void); - void PushFailure(void); - bool RemoveFromFailureQueue(void); - void PushSuccess(void); - - struct SendPing - { - RakNet::Time nextActionTime; - SystemAddress targetAddress; - SystemAddress facilitator; - SystemAddress internalIds[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; - RakNetGUID targetGuid; - bool weAreSender; - int attemptCount; - int retryCount; - int punchingFixedPortAttempts; // only used for TestMode::PUNCHING_FIXED_PORT - uint16_t sessionId; - // Give priority to internal IP addresses because if we are on a LAN, we don't want to try to connect through the internet - enum TestMode - { - TESTING_INTERNAL_IPS, - WAITING_FOR_INTERNAL_IPS_RESPONSE, - SEND_WITH_TTL, - TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT, - TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT, - TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024, - TESTING_EXTERNAL_IPS_1024_TO_1024, - WAITING_AFTER_ALL_ATTEMPTS, - - // The trendnet remaps the remote port to 1024. - // If you continue punching on a different port for the same IP it bans you and the communication becomes unidirectioal - PUNCHING_FIXED_PORT, - - // try port 1024-1028 - } testMode; - } sp; - - PunchthroughConfiguration pc; - NatPunchthroughDebugInterface *natPunchthroughDebugInterface; - - // The first time we fail a NAT attempt, we add it to failedAttemptList and try again, since sometimes trying again later fixes the problem - // The second time we fail, we return ID_NAT_PUNCHTHROUGH_FAILED - struct AddrAndGuid - { - SystemAddress addr; - RakNetGUID guid; - }; - DataStructures::List failedAttemptList; - - void IncrementExternalAttemptCount(RakNet::Time time, RakNet::Time delta); - - struct TimeAndGuid - { - RakNet::Time time; - RakNetGUID guid; - }; - DataStructures::List groupRequestsInProgress; - - struct GroupPunchRequest - { - SystemAddress facilitator; - DataStructures::List pendingList; - DataStructures::List passedListGuid; - DataStructures::List passedListAddress; - DataStructures::List failedList; - DataStructures::List ignoredList; - }; - DataStructures::List groupPunchRequests; - void UpdateGroupPunchOnNatResult(SystemAddress facilitator, RakNetGUID targetSystem, SystemAddress targetSystemAddress, int result); // 0=failed, 1=success, 2=ignore -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* + +/// \file +/// \brief Contains the NAT-punchthrough plugin for the client. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatPunchthroughClient==1 + +#ifndef __NAT_PUNCHTHROUGH_CLIENT_H +#define __NAT_PUNCHTHROUGH_CLIENT_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "SocketIncludes.h" +#include "DS_List.h" +#include "RakString.h" + +// Trendnet TEW-632BRP sometimes starts at port 1024 and increments sequentially. +// Zonnet zsr1134we. Replies go out on the net, but are always absorbed by the remote router?? +// Dlink ebr2310 to Trendnet ok +// Trendnet TEW-652BRP to Trendnet 632BRP OK +// Trendnet TEW-632BRP to Trendnet 632BRP OK +// Buffalo WHR-HP-G54 OK +// Netgear WGR614 ok + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +struct Packet; +#if _RAKNET_SUPPORT_PacketLogger==1 +class PacketLogger; +#endif + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT PunchthroughConfiguration +{ + /// internal: (15 ms * 2 tries + 30 wait) * 5 ports * 8 players = 2.4 seconds + /// external: (50 ms * 8 sends + 100 wait) * 2 port * 8 players = 8 seconds + /// Total: 8 seconds + PunchthroughConfiguration() { + TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL=15; + TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL=50; + UDP_SENDS_PER_PORT_INTERNAL=2; + UDP_SENDS_PER_PORT_EXTERNAL=8; + INTERNAL_IP_WAIT_AFTER_ATTEMPTS=30; + MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK=5; /// set to 0 to not do lan connects + MAX_PREDICTIVE_PORT_RANGE=2; + EXTERNAL_IP_WAIT_BETWEEN_PORTS=100; + EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS=EXTERNAL_IP_WAIT_BETWEEN_PORTS; + retryOnFailure=false; + } + + /// How much time between each UDP send + RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL; + RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL; + + /// How many tries for one port before giving up and going to the next port + int UDP_SENDS_PER_PORT_INTERNAL; + int UDP_SENDS_PER_PORT_EXTERNAL; + + /// After giving up on one internal port, how long to wait before trying the next port + int INTERNAL_IP_WAIT_AFTER_ATTEMPTS; + + /// How many external ports to try past the last known starting port + int MAX_PREDICTIVE_PORT_RANGE; + + /// After giving up on one external port, how long to wait before trying the next port + int EXTERNAL_IP_WAIT_BETWEEN_PORTS; + + /// After trying all external ports, how long to wait before returning ID_NAT_PUNCHTHROUGH_FAILED + int EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS; + + /// Maximum number of internal IP address to try to connect to. + /// Cannot be greater than MAXIMUM_NUMBER_OF_INTERNAL_IDS + /// Should be high enough to try all internal IP addresses on the majority of computers + int MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK; + + /// If the first punchthrough attempt fails, try again + /// This sometimes works because the remote router was looking for an incoming message on a higher numbered port before responding to a lower numbered port from the other system + bool retryOnFailure; +}; + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughDebugInterface +{ + NatPunchthroughDebugInterface() {} + virtual ~NatPunchthroughDebugInterface() {} + virtual void OnClientMessage(const char *msg)=0; +}; + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_Printf : public NatPunchthroughDebugInterface +{ + virtual void OnClientMessage(const char *msg); +}; + +#if _RAKNET_SUPPORT_PacketLogger==1 +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_PacketLogger : public NatPunchthroughDebugInterface +{ + // Set to non-zero to write to the packetlogger! + PacketLogger *pl; + + NatPunchthroughDebugInterface_PacketLogger() {pl=0;} + ~NatPunchthroughDebugInterface_PacketLogger() {} + virtual void OnClientMessage(const char *msg); +}; +#endif + +/// \brief Client code for NATPunchthrough +/// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient
      +/// Client will send datagrams to port to estimate next port
      +/// Will simultaneously connect with another client once ports are estimated. +/// \sa NatTypeDetectionClient +/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html +/// \ingroup NAT_PUNCHTHROUGH_GROUP +class RAK_DLL_EXPORT NatPunchthroughClient : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(NatPunchthroughClient) + + NatPunchthroughClient(); + ~NatPunchthroughClient(); + + /// Punchthrough a NAT. Doesn't connect, just tries to setup the routing table + /// \param[in] destination The system to punch. Must already be connected to \a facilitator + /// \param[in] facilitator A system we are already connected to running the NatPunchthroughServer plugin + /// \sa OpenNATGroup() + /// You will get ID_NAT_PUNCHTHROUGH_SUCCEEDED on success + /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_TARGET_UNRESPONSIVE, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_PUNCHTHROUGH_FAILED on failures of various types + /// However, if you lose connection to the facilitator, you may not necessarily get above + bool OpenNAT(RakNetGUID destination, const SystemAddress &facilitator); + + /// Same as calling OpenNAT for a list of systems, but reply is delayed until all systems pass. + /// This is useful for peer to peer games where you want to connect to every system in the remote session, not just one particular system + /// \note For cloud computing, all systems in the group must be connected to the same facilitator since we're only specifying one + /// You will get ID_NAT_GROUP_PUNCH_SUCCEEDED on success + /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_GROUP_PUNCH_FAILED on failures of various types + /// However, if you lose connection to the facilitator, you may not necessarily get above + bool OpenNATGroup(DataStructures::List destinationSystems, const SystemAddress &facilitator); + + /// Modify the system configuration if desired + /// Don't modify the variables in the structure while punchthrough is in progress + PunchthroughConfiguration* GetPunchthroughConfiguration(void); + + /// Sets a callback to be called with debug messages + /// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear. + void SetDebugInterface(NatPunchthroughDebugInterface *i); + + /// Get the port mappings you should pass to UPNP (for miniupnpc-1.5, for the function UPNP_AddPortMapping) + void GetUPNPPortMappings(char *externalPort, char *internalPort, const SystemAddress &natPunchthroughServerAddress); + + /// \internal For plugin handling + virtual void Update(void); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + + /// \internal For plugin handling + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + + /// \internal For plugin handling + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + virtual void OnAttach(void); + virtual void OnDetach(void); + virtual void OnRakPeerShutdown(void); + void Clear(void); + +protected: + unsigned short mostRecentNewExternalPort; + void OnNatGroupPunchthroughRequest(Packet *packet); + void OnFailureNotification(Packet *packet); + void OnNatGroupPunchthroughReply(Packet *packet); + void OnGetMostRecentPort(Packet *packet); + void OnConnectAtTime(Packet *packet); + unsigned int GetPendingOpenNATIndex(RakNetGUID destination, const SystemAddress &facilitator); + void SendPunchthrough(RakNetGUID destination, const SystemAddress &facilitator); + void SendTTL(const SystemAddress &sa); + void SendOutOfBand(SystemAddress sa, MessageID oobId); + void OnPunchthroughFailure(void); + void OnReadyForNextPunchthrough(void); + void PushFailure(void); + bool RemoveFromFailureQueue(void); + void PushSuccess(void); + + struct SendPing + { + RakNet::Time nextActionTime; + SystemAddress targetAddress; + SystemAddress facilitator; + SystemAddress internalIds[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; + RakNetGUID targetGuid; + bool weAreSender; + int attemptCount; + int retryCount; + int punchingFixedPortAttempts; // only used for TestMode::PUNCHING_FIXED_PORT + uint16_t sessionId; + // Give priority to internal IP addresses because if we are on a LAN, we don't want to try to connect through the internet + enum TestMode + { + TESTING_INTERNAL_IPS, + WAITING_FOR_INTERNAL_IPS_RESPONSE, + SEND_WITH_TTL, + TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT, + TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT, + TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024, + TESTING_EXTERNAL_IPS_1024_TO_1024, + WAITING_AFTER_ALL_ATTEMPTS, + + // The trendnet remaps the remote port to 1024. + // If you continue punching on a different port for the same IP it bans you and the communication becomes unidirectioal + PUNCHING_FIXED_PORT, + + // try port 1024-1028 + } testMode; + } sp; + + PunchthroughConfiguration pc; + NatPunchthroughDebugInterface *natPunchthroughDebugInterface; + + // The first time we fail a NAT attempt, we add it to failedAttemptList and try again, since sometimes trying again later fixes the problem + // The second time we fail, we return ID_NAT_PUNCHTHROUGH_FAILED + struct AddrAndGuid + { + SystemAddress addr; + RakNetGUID guid; + }; + DataStructures::List failedAttemptList; + + void IncrementExternalAttemptCount(RakNet::Time time, RakNet::Time delta); + + struct TimeAndGuid + { + RakNet::Time time; + RakNetGUID guid; + }; + DataStructures::List groupRequestsInProgress; + + struct GroupPunchRequest + { + SystemAddress facilitator; + DataStructures::List pendingList; + DataStructures::List passedListGuid; + DataStructures::List passedListAddress; + DataStructures::List failedList; + DataStructures::List ignoredList; + }; + DataStructures::List groupPunchRequests; + void UpdateGroupPunchOnNatResult(SystemAddress facilitator, RakNetGUID targetSystem, SystemAddress targetSystemAddress, int result); // 0=failed, 1=success, 2=ignore +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/NatPunchthroughServer.cpp b/src/raknet/NatPunchthroughServer.cpp index 4130b65..fd057eb 100755 --- a/src/raknet/NatPunchthroughServer.cpp +++ b/src/raknet/NatPunchthroughServer.cpp @@ -1,563 +1,563 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatPunchthroughServer==1 - -#include "NatPunchthroughServer.h" -#include "SocketLayer.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "MTUSize.h" -#include "GetTime.h" -#include "PacketLogger.h" - -using namespace RakNet; - -void NatPunchthroughServerDebugInterface_Printf::OnServerMessage(const char *msg) -{ - printf("%s\n", msg); -} -#if _RAKNET_SUPPORT_PacketLogger==1 -void NatPunchthroughServerDebugInterface_PacketLogger::OnServerMessage(const char *msg) -{ - if (pl) - { - pl->WriteMiscellaneous("Nat", msg); - } -} -#endif - -void NatPunchthroughServer::User::DeleteConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca) -{ - unsigned int index = connectionAttempts.GetIndexOf(ca); - if ((unsigned int)index!=(unsigned int)-1) - { - RakNet::OP_DELETE(ca,_FILE_AND_LINE_); - connectionAttempts.RemoveAtIndex(index); - } -} -void NatPunchthroughServer::User::DerefConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca) -{ - unsigned int index = connectionAttempts.GetIndexOf(ca); - if ((unsigned int)index!=(unsigned int)-1) - { - connectionAttempts.RemoveAtIndex(index); - } -} -bool NatPunchthroughServer::User::HasConnectionAttemptToUser(User *user) -{ - unsigned int index; - for (index=0; index < connectionAttempts.Size(); index++) - { - if (connectionAttempts[index]->recipient->guid==user->guid || - connectionAttempts[index]->sender->guid==user->guid) - return true; - } - return false; -} -void NatPunchthroughServer::User::LogConnectionAttempts(RakNet::RakString &rs) -{ - rs.Clear(); - unsigned int index; - char guidStr[128], ipStr[128]; - guid.ToString(guidStr); - systemAddress.ToString(true,ipStr); - rs=RakNet::RakString("User systemAddress=%s guid=%s\n", ipStr, guidStr); - rs+=RakNet::RakString("%i attempts in list:\n", connectionAttempts.Size()); - for (index=0; index < connectionAttempts.Size(); index++) - { - rs+=RakNet::RakString("%i. SessionID=%i ", index+1, connectionAttempts[index]->sessionId); - if (connectionAttempts[index]->sender==this) - rs+="(We are sender) "; - else - rs+="(We are recipient) "; - if (isReady) - rs+="(READY TO START) "; - else - rs+="(NOT READY TO START) "; - if (connectionAttempts[index]->attemptPhase==NatPunchthroughServer::ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED) - rs+="(NOT_STARTED). "; - else - rs+="(GETTING_RECENT_PORTS). "; - if (connectionAttempts[index]->sender==this) - { - connectionAttempts[index]->recipient->guid.ToString(guidStr); - connectionAttempts[index]->recipient->systemAddress.ToString(true,ipStr); - } - else - { - connectionAttempts[index]->sender->guid.ToString(guidStr); - connectionAttempts[index]->sender->systemAddress.ToString(true,ipStr); - } - - rs+=RakNet::RakString("Target systemAddress=%s, guid=%s.\n", ipStr, guidStr); - } -} - -int RakNet::NatPunchthroughServer::NatPunchthroughUserComp( const RakNetGUID &key, User * const &data ) -{ - if (key < data->guid) - return -1; - if (key > data->guid) - return 1; - return 0; -} - -STATIC_FACTORY_DEFINITIONS(NatPunchthroughServer,NatPunchthroughServer); - -NatPunchthroughServer::NatPunchthroughServer() -{ - lastUpdate=0; - sessionId=0; - natPunchthroughServerDebugInterface=0; -} -NatPunchthroughServer::~NatPunchthroughServer() -{ - User *user, *otherUser; - ConnectionAttempt *connectionAttempt; - unsigned int j; - while(users.Size()) - { - user = users[0]; - for (j=0; j < user->connectionAttempts.Size(); j++) - { - connectionAttempt=user->connectionAttempts[j]; - if (connectionAttempt->sender==user) - otherUser=connectionAttempt->recipient; - else - otherUser=connectionAttempt->sender; - otherUser->DeleteConnectionAttempt(connectionAttempt); - } - RakNet::OP_DELETE(user,_FILE_AND_LINE_); - users[0]=users[users.Size()-1]; - users.RemoveAtIndex(users.Size()-1); - } -} -void NatPunchthroughServer::SetDebugInterface(NatPunchthroughServerDebugInterface *i) -{ - natPunchthroughServerDebugInterface=i; -} -void NatPunchthroughServer::Update(void) -{ - ConnectionAttempt *connectionAttempt; - User *user, *recipient; - unsigned int i,j; - RakNet::Time time = RakNet::GetTime(); - if (time > lastUpdate+250) - { - lastUpdate=time; - - for (i=0; i < users.Size(); i++) - { - user=users[i]; - for (j=0; j < user->connectionAttempts.Size(); j++) - { - connectionAttempt=user->connectionAttempts[j]; - if (connectionAttempt->sender==user) - { - if (connectionAttempt->attemptPhase!=ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED && - time > connectionAttempt->startTime && - time > 10000 + connectionAttempt->startTime ) // Formerly 5000, but sometimes false positives - { - RakNet::BitStream outgoingBs; - - // that other system might not be running the plugin - outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE); - outgoingBs.Write(connectionAttempt->recipient->guid); - outgoingBs.Write(connectionAttempt->sessionId); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->sender->systemAddress,false); - - // 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST - // However, recipient may be expecting it due to external code - // In that case, recipient would never get any response if the sender dropped - outgoingBs.Reset(); - outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE); - outgoingBs.Write(connectionAttempt->sender->guid); - outgoingBs.Write(connectionAttempt->sessionId); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->recipient->systemAddress,false); - - connectionAttempt->sender->isReady=true; - connectionAttempt->recipient->isReady=true; - recipient=connectionAttempt->recipient; - - - if (natPunchthroughServerDebugInterface) - { - char str[1024]; - char addr1[128], addr2[128]; - // 8/01/09 Fixed bug where this was after DeleteConnectionAttempt() - connectionAttempt->sender->systemAddress.ToString(true,addr1); - connectionAttempt->recipient->systemAddress.ToString(true,addr2); - sprintf(str, "Sending ID_NAT_TARGET_UNRESPONSIVE to sender %s and recipient %s.", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(str); - RakNet::RakString log; - connectionAttempt->sender->LogConnectionAttempts(log); - connectionAttempt->recipient->LogConnectionAttempts(log); - } - - - connectionAttempt->sender->DerefConnectionAttempt(connectionAttempt); - connectionAttempt->recipient->DeleteConnectionAttempt(connectionAttempt); - - StartPunchthroughForUser(user); - StartPunchthroughForUser(recipient); - - break; - } - } - } - } - } -} -PluginReceiveResult NatPunchthroughServer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_NAT_PUNCHTHROUGH_REQUEST: - OnNATPunchthroughRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_NAT_GET_MOST_RECENT_PORT: - OnGetMostRecentPort(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_NAT_CLIENT_READY: - OnClientReady(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - return RR_CONTINUE_PROCESSING; -} -void NatPunchthroughServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - - unsigned int i=0; - bool objectExists; - i = users.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists) - { - RakNet::BitStream outgoingBs; - DataStructures::List freedUpInProgressUsers; - User *user = users[i]; - User *otherUser; - unsigned int connectionAttemptIndex; - ConnectionAttempt *connectionAttempt; - for (connectionAttemptIndex=0; connectionAttemptIndex < user->connectionAttempts.Size(); connectionAttemptIndex++) - { - connectionAttempt=user->connectionAttempts[connectionAttemptIndex]; - outgoingBs.Reset(); - if (connectionAttempt->recipient==user) - { - otherUser=connectionAttempt->sender; - } - else - { - otherUser=connectionAttempt->recipient; - } - - // 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST - // However, recipient may be expecting it due to external code - // In that case, recipient would never get any response if the sender dropped - outgoingBs.Write((MessageID)ID_NAT_CONNECTION_TO_TARGET_LOST); - outgoingBs.Write(rakNetGUID); - outgoingBs.Write(connectionAttempt->sessionId); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,otherUser->systemAddress,false); - - // 4/22/09 - Bug: was checking inProgress, legacy variable not used elsewhere - if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS) - { - otherUser->isReady=true; - freedUpInProgressUsers.Insert(otherUser, _FILE_AND_LINE_ ); - } - - otherUser->DeleteConnectionAttempt(connectionAttempt); - } - - RakNet::OP_DELETE(users[i], _FILE_AND_LINE_); - users.RemoveAtIndex(i); - - for (i=0; i < freedUpInProgressUsers.Size(); i++) - { - StartPunchthroughForUser(freedUpInProgressUsers[i]); - } - } - - // Also remove from groupPunchthroughRequests - for (i=0; i < users.Size(); i++) - { - bool objectExists; - unsigned int gprIndex; - gprIndex = users[i]->groupPunchthroughRequests.GetIndexFromKey(rakNetGUID, &objectExists); - if (objectExists) - { -// printf("DEBUG %i\n", __LINE__); - - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED); - outgoingBs.Write(rakNetGUID); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,users[i]->systemAddress,false); - - users[i]->groupPunchthroughRequests.RemoveAtIndex(gprIndex); - } - } -} - -void NatPunchthroughServer::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) systemAddress; - (void) isIncoming; - - User *user = RakNet::OP_NEW(_FILE_AND_LINE_); - user->guid=rakNetGUID; - user->mostRecentPort=0; - user->systemAddress=systemAddress; - user->isReady=true; - users.Insert(rakNetGUID, user, true, _FILE_AND_LINE_); - -// printf("Adding to users %s\n", rakNetGUID.ToString()); -// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString()); -} -void NatPunchthroughServer::OnNATPunchthroughRequest(Packet *packet) -{ - RakNet::BitStream outgoingBs; - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID recipientGuid, senderGuid; - incomingBs.Read(recipientGuid); - senderGuid=packet->guid; - unsigned int i; - bool objectExists; - i = users.GetIndexFromKey(senderGuid, &objectExists); - RakAssert(objectExists); - - ConnectionAttempt *ca = RakNet::OP_NEW(_FILE_AND_LINE_); - ca->sender=users[i]; - ca->sessionId=sessionId++; - i = users.GetIndexFromKey(recipientGuid, &objectExists); - if (objectExists==false) - { -// printf("DEBUG %i\n", __LINE__); -// printf("DEBUG recipientGuid=%s\n", recipientGuid.ToString()); -// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString()); - - outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED); - outgoingBs.Write(recipientGuid); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - RakNet::OP_DELETE(ca,_FILE_AND_LINE_); - return; - } - ca->recipient=users[i]; - if (ca->recipient->HasConnectionAttemptToUser(ca->sender)) - { - outgoingBs.Write((MessageID)ID_NAT_ALREADY_IN_PROGRESS); - outgoingBs.Write(recipientGuid); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - RakNet::OP_DELETE(ca,_FILE_AND_LINE_); - return; - } - - ca->sender->connectionAttempts.Insert(ca, _FILE_AND_LINE_ ); - ca->recipient->connectionAttempts.Insert(ca, _FILE_AND_LINE_ ); - - StartPunchthroughForUser(ca->sender); -} -void NatPunchthroughServer::OnClientReady(Packet *packet) -{ - unsigned int i; - bool objectExists; - i = users.GetIndexFromKey(packet->guid, &objectExists); - if (objectExists) - { - users[i]->isReady=true; - StartPunchthroughForUser(users[i]); - } -} -void NatPunchthroughServer::OnGetMostRecentPort(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)); - uint16_t sessionId; - unsigned short mostRecentPort; - bsIn.Read(sessionId); - bsIn.Read(mostRecentPort); - - unsigned int i,j; - User *user; - ConnectionAttempt *connectionAttempt; - bool objectExists; - i = users.GetIndexFromKey(packet->guid, &objectExists); - - if (natPunchthroughServerDebugInterface) - { - RakNet::RakString log; - char addr1[128], addr2[128]; - packet->systemAddress.ToString(true,addr1); - packet->guid.ToString(addr2); - log=RakNet::RakString("Got ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s. port=%i. sessionId=%i. userFound=%i.", addr1, addr2, mostRecentPort, sessionId, objectExists); - natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); - } - - if (objectExists) - { - user=users[i]; - user->mostRecentPort=mostRecentPort; - RakNet::Time time = RakNet::GetTime(); - - for (j=0; j < user->connectionAttempts.Size(); j++) - { - connectionAttempt=user->connectionAttempts[j]; - if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS && - connectionAttempt->sender->mostRecentPort!=0 && - connectionAttempt->recipient->mostRecentPort!=0 && - // 04/29/08 add sessionId to prevent processing for other systems - connectionAttempt->sessionId==sessionId) - { - SystemAddress senderSystemAddress = connectionAttempt->sender->systemAddress; - SystemAddress recipientSystemAddress = connectionAttempt->recipient->systemAddress; - SystemAddress recipientTargetAddress = recipientSystemAddress; - SystemAddress senderTargetAddress = senderSystemAddress; - recipientTargetAddress.SetPort(connectionAttempt->recipient->mostRecentPort); - senderTargetAddress.SetPort(connectionAttempt->sender->mostRecentPort); - - // Pick a time far enough in the future that both systems will have gotten the message - int targetPing = rakPeerInterface->GetAveragePing(recipientTargetAddress); - int senderPing = rakPeerInterface->GetAveragePing(senderSystemAddress); - RakNet::Time simultaneousAttemptTime; - if (targetPing==-1 || senderPing==-1) - simultaneousAttemptTime = time + 1500; - else - { - int largerPing = targetPing > senderPing ? targetPing : senderPing; - if (largerPing * 4 < 100) - simultaneousAttemptTime = time + 100; - else - simultaneousAttemptTime = time + (largerPing * 4); - } - - if (natPunchthroughServerDebugInterface) - { - RakNet::RakString log; - char addr1[128], addr2[128]; - recipientSystemAddress.ToString(true,addr1); - connectionAttempt->recipient->guid.ToString(addr2); - log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to recipient systemAddress %s guid %s", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); - } - - // Send to recipient timestamped message to connect at time - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TIMESTAMP); - bsOut.Write(simultaneousAttemptTime); - bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME); - bsOut.Write(connectionAttempt->sessionId); - bsOut.Write(senderTargetAddress); // Public IP, using most recent port - for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP - bsOut.Write(rakPeerInterface->GetInternalID(senderSystemAddress,j)); - bsOut.Write(connectionAttempt->sender->guid); - bsOut.Write(false); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipientSystemAddress,false); - - - if (natPunchthroughServerDebugInterface) - { - RakNet::RakString log; - char addr1[128], addr2[128]; - senderSystemAddress.ToString(true,addr1); - connectionAttempt->sender->guid.ToString(addr2); - log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to sender systemAddress %s guid %s", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); - } - - - // Same for sender - bsOut.Reset(); - bsOut.Write((MessageID)ID_TIMESTAMP); - bsOut.Write(simultaneousAttemptTime); - bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME); - bsOut.Write(connectionAttempt->sessionId); - bsOut.Write(recipientTargetAddress); // Public IP, using most recent port - for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP - bsOut.Write(rakPeerInterface->GetInternalID(recipientSystemAddress,j)); - bsOut.Write(connectionAttempt->recipient->guid); - bsOut.Write(true); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,senderSystemAddress,false); - - connectionAttempt->recipient->DerefConnectionAttempt(connectionAttempt); - connectionAttempt->sender->DeleteConnectionAttempt(connectionAttempt); - - // 04/29/08 missing return - return; - } - } - } - else - { - - if (natPunchthroughServerDebugInterface) - { - RakNet::RakString log; - char addr1[128], addr2[128]; - packet->systemAddress.ToString(true,addr1); - packet->guid.ToString(addr2); - log=RakNet::RakString("Ignoring ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); - } - - } -} -void NatPunchthroughServer::StartPunchthroughForUser(User *user) -{ - if (user->isReady==false) - return; - - ConnectionAttempt *connectionAttempt; - User *sender,*recipient,*otherUser; - unsigned int i; - for (i=0; i < user->connectionAttempts.Size(); i++) - { - connectionAttempt=user->connectionAttempts[i]; - if (connectionAttempt->sender==user) - { - otherUser=connectionAttempt->recipient; - sender=user; - recipient=otherUser; - } - else - { - otherUser=connectionAttempt->sender; - recipient=user; - sender=otherUser; - } - - if (otherUser->isReady) - { - if (natPunchthroughServerDebugInterface) - { - char str[1024]; - char addr1[128], addr2[128]; - sender->systemAddress.ToString(true,addr1); - recipient->systemAddress.ToString(true,addr2); - sprintf(str, "Sending NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS to sender %s and recipient %s.", addr1, addr2); - natPunchthroughServerDebugInterface->OnServerMessage(str); - } - - sender->isReady=false; - recipient->isReady=false; - connectionAttempt->attemptPhase=ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS; - connectionAttempt->startTime=RakNet::GetTime(); - - sender->mostRecentPort=0; - recipient->mostRecentPort=0; - - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT); - // 4/29/09 Write sessionID so we don't use returned port for a system we don't want - outgoingBs.Write(connectionAttempt->sessionId); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sender->systemAddress,false); - rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipient->systemAddress,false); - - // 4/22/09 - BUG: missing break statement here - break; - } - } -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatPunchthroughServer==1 + +#include "NatPunchthroughServer.h" +#include "SocketLayer.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "MTUSize.h" +#include "GetTime.h" +#include "PacketLogger.h" + +using namespace RakNet; + +void NatPunchthroughServerDebugInterface_Printf::OnServerMessage(const char *msg) +{ + printf("%s\n", msg); +} +#if _RAKNET_SUPPORT_PacketLogger==1 +void NatPunchthroughServerDebugInterface_PacketLogger::OnServerMessage(const char *msg) +{ + if (pl) + { + pl->WriteMiscellaneous("Nat", msg); + } +} +#endif + +void NatPunchthroughServer::User::DeleteConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca) +{ + unsigned int index = connectionAttempts.GetIndexOf(ca); + if ((unsigned int)index!=(unsigned int)-1) + { + RakNet::OP_DELETE(ca,_FILE_AND_LINE_); + connectionAttempts.RemoveAtIndex(index); + } +} +void NatPunchthroughServer::User::DerefConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca) +{ + unsigned int index = connectionAttempts.GetIndexOf(ca); + if ((unsigned int)index!=(unsigned int)-1) + { + connectionAttempts.RemoveAtIndex(index); + } +} +bool NatPunchthroughServer::User::HasConnectionAttemptToUser(User *user) +{ + unsigned int index; + for (index=0; index < connectionAttempts.Size(); index++) + { + if (connectionAttempts[index]->recipient->guid==user->guid || + connectionAttempts[index]->sender->guid==user->guid) + return true; + } + return false; +} +void NatPunchthroughServer::User::LogConnectionAttempts(RakNet::RakString &rs) +{ + rs.Clear(); + unsigned int index; + char guidStr[128], ipStr[128]; + guid.ToString(guidStr); + systemAddress.ToString(true,ipStr); + rs=RakNet::RakString("User systemAddress=%s guid=%s\n", ipStr, guidStr); + rs+=RakNet::RakString("%i attempts in list:\n", connectionAttempts.Size()); + for (index=0; index < connectionAttempts.Size(); index++) + { + rs+=RakNet::RakString("%i. SessionID=%i ", index+1, connectionAttempts[index]->sessionId); + if (connectionAttempts[index]->sender==this) + rs+="(We are sender) "; + else + rs+="(We are recipient) "; + if (isReady) + rs+="(READY TO START) "; + else + rs+="(NOT READY TO START) "; + if (connectionAttempts[index]->attemptPhase==NatPunchthroughServer::ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED) + rs+="(NOT_STARTED). "; + else + rs+="(GETTING_RECENT_PORTS). "; + if (connectionAttempts[index]->sender==this) + { + connectionAttempts[index]->recipient->guid.ToString(guidStr); + connectionAttempts[index]->recipient->systemAddress.ToString(true,ipStr); + } + else + { + connectionAttempts[index]->sender->guid.ToString(guidStr); + connectionAttempts[index]->sender->systemAddress.ToString(true,ipStr); + } + + rs+=RakNet::RakString("Target systemAddress=%s, guid=%s.\n", ipStr, guidStr); + } +} + +int RakNet::NatPunchthroughServer::NatPunchthroughUserComp( const RakNetGUID &key, User * const &data ) +{ + if (key < data->guid) + return -1; + if (key > data->guid) + return 1; + return 0; +} + +STATIC_FACTORY_DEFINITIONS(NatPunchthroughServer,NatPunchthroughServer); + +NatPunchthroughServer::NatPunchthroughServer() +{ + lastUpdate=0; + sessionId=0; + natPunchthroughServerDebugInterface=0; +} +NatPunchthroughServer::~NatPunchthroughServer() +{ + User *user, *otherUser; + ConnectionAttempt *connectionAttempt; + unsigned int j; + while(users.Size()) + { + user = users[0]; + for (j=0; j < user->connectionAttempts.Size(); j++) + { + connectionAttempt=user->connectionAttempts[j]; + if (connectionAttempt->sender==user) + otherUser=connectionAttempt->recipient; + else + otherUser=connectionAttempt->sender; + otherUser->DeleteConnectionAttempt(connectionAttempt); + } + RakNet::OP_DELETE(user,_FILE_AND_LINE_); + users[0]=users[users.Size()-1]; + users.RemoveAtIndex(users.Size()-1); + } +} +void NatPunchthroughServer::SetDebugInterface(NatPunchthroughServerDebugInterface *i) +{ + natPunchthroughServerDebugInterface=i; +} +void NatPunchthroughServer::Update(void) +{ + ConnectionAttempt *connectionAttempt; + User *user, *recipient; + unsigned int i,j; + RakNet::Time time = RakNet::GetTime(); + if (time > lastUpdate+250) + { + lastUpdate=time; + + for (i=0; i < users.Size(); i++) + { + user=users[i]; + for (j=0; j < user->connectionAttempts.Size(); j++) + { + connectionAttempt=user->connectionAttempts[j]; + if (connectionAttempt->sender==user) + { + if (connectionAttempt->attemptPhase!=ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED && + time > connectionAttempt->startTime && + time > 10000 + connectionAttempt->startTime ) // Formerly 5000, but sometimes false positives + { + RakNet::BitStream outgoingBs; + + // that other system might not be running the plugin + outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE); + outgoingBs.Write(connectionAttempt->recipient->guid); + outgoingBs.Write(connectionAttempt->sessionId); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->sender->systemAddress,false); + + // 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST + // However, recipient may be expecting it due to external code + // In that case, recipient would never get any response if the sender dropped + outgoingBs.Reset(); + outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE); + outgoingBs.Write(connectionAttempt->sender->guid); + outgoingBs.Write(connectionAttempt->sessionId); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->recipient->systemAddress,false); + + connectionAttempt->sender->isReady=true; + connectionAttempt->recipient->isReady=true; + recipient=connectionAttempt->recipient; + + + if (natPunchthroughServerDebugInterface) + { + char str[1024]; + char addr1[128], addr2[128]; + // 8/01/09 Fixed bug where this was after DeleteConnectionAttempt() + connectionAttempt->sender->systemAddress.ToString(true,addr1); + connectionAttempt->recipient->systemAddress.ToString(true,addr2); + sprintf(str, "Sending ID_NAT_TARGET_UNRESPONSIVE to sender %s and recipient %s.", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(str); + RakNet::RakString log; + connectionAttempt->sender->LogConnectionAttempts(log); + connectionAttempt->recipient->LogConnectionAttempts(log); + } + + + connectionAttempt->sender->DerefConnectionAttempt(connectionAttempt); + connectionAttempt->recipient->DeleteConnectionAttempt(connectionAttempt); + + StartPunchthroughForUser(user); + StartPunchthroughForUser(recipient); + + break; + } + } + } + } + } +} +PluginReceiveResult NatPunchthroughServer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_NAT_PUNCHTHROUGH_REQUEST: + OnNATPunchthroughRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_NAT_GET_MOST_RECENT_PORT: + OnGetMostRecentPort(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_NAT_CLIENT_READY: + OnClientReady(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + return RR_CONTINUE_PROCESSING; +} +void NatPunchthroughServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + + unsigned int i=0; + bool objectExists; + i = users.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists) + { + RakNet::BitStream outgoingBs; + DataStructures::List freedUpInProgressUsers; + User *user = users[i]; + User *otherUser; + unsigned int connectionAttemptIndex; + ConnectionAttempt *connectionAttempt; + for (connectionAttemptIndex=0; connectionAttemptIndex < user->connectionAttempts.Size(); connectionAttemptIndex++) + { + connectionAttempt=user->connectionAttempts[connectionAttemptIndex]; + outgoingBs.Reset(); + if (connectionAttempt->recipient==user) + { + otherUser=connectionAttempt->sender; + } + else + { + otherUser=connectionAttempt->recipient; + } + + // 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST + // However, recipient may be expecting it due to external code + // In that case, recipient would never get any response if the sender dropped + outgoingBs.Write((MessageID)ID_NAT_CONNECTION_TO_TARGET_LOST); + outgoingBs.Write(rakNetGUID); + outgoingBs.Write(connectionAttempt->sessionId); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,otherUser->systemAddress,false); + + // 4/22/09 - Bug: was checking inProgress, legacy variable not used elsewhere + if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS) + { + otherUser->isReady=true; + freedUpInProgressUsers.Insert(otherUser, _FILE_AND_LINE_ ); + } + + otherUser->DeleteConnectionAttempt(connectionAttempt); + } + + RakNet::OP_DELETE(users[i], _FILE_AND_LINE_); + users.RemoveAtIndex(i); + + for (i=0; i < freedUpInProgressUsers.Size(); i++) + { + StartPunchthroughForUser(freedUpInProgressUsers[i]); + } + } + + // Also remove from groupPunchthroughRequests + for (i=0; i < users.Size(); i++) + { + bool objectExists; + unsigned int gprIndex; + gprIndex = users[i]->groupPunchthroughRequests.GetIndexFromKey(rakNetGUID, &objectExists); + if (objectExists) + { +// printf("DEBUG %i\n", __LINE__); + + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED); + outgoingBs.Write(rakNetGUID); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,users[i]->systemAddress,false); + + users[i]->groupPunchthroughRequests.RemoveAtIndex(gprIndex); + } + } +} + +void NatPunchthroughServer::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) systemAddress; + (void) isIncoming; + + User *user = RakNet::OP_NEW(_FILE_AND_LINE_); + user->guid=rakNetGUID; + user->mostRecentPort=0; + user->systemAddress=systemAddress; + user->isReady=true; + users.Insert(rakNetGUID, user, true, _FILE_AND_LINE_); + +// printf("Adding to users %s\n", rakNetGUID.ToString()); +// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString()); +} +void NatPunchthroughServer::OnNATPunchthroughRequest(Packet *packet) +{ + RakNet::BitStream outgoingBs; + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID recipientGuid, senderGuid; + incomingBs.Read(recipientGuid); + senderGuid=packet->guid; + unsigned int i; + bool objectExists; + i = users.GetIndexFromKey(senderGuid, &objectExists); + RakAssert(objectExists); + + ConnectionAttempt *ca = RakNet::OP_NEW(_FILE_AND_LINE_); + ca->sender=users[i]; + ca->sessionId=sessionId++; + i = users.GetIndexFromKey(recipientGuid, &objectExists); + if (objectExists==false) + { +// printf("DEBUG %i\n", __LINE__); +// printf("DEBUG recipientGuid=%s\n", recipientGuid.ToString()); +// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString()); + + outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED); + outgoingBs.Write(recipientGuid); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + RakNet::OP_DELETE(ca,_FILE_AND_LINE_); + return; + } + ca->recipient=users[i]; + if (ca->recipient->HasConnectionAttemptToUser(ca->sender)) + { + outgoingBs.Write((MessageID)ID_NAT_ALREADY_IN_PROGRESS); + outgoingBs.Write(recipientGuid); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + RakNet::OP_DELETE(ca,_FILE_AND_LINE_); + return; + } + + ca->sender->connectionAttempts.Insert(ca, _FILE_AND_LINE_ ); + ca->recipient->connectionAttempts.Insert(ca, _FILE_AND_LINE_ ); + + StartPunchthroughForUser(ca->sender); +} +void NatPunchthroughServer::OnClientReady(Packet *packet) +{ + unsigned int i; + bool objectExists; + i = users.GetIndexFromKey(packet->guid, &objectExists); + if (objectExists) + { + users[i]->isReady=true; + StartPunchthroughForUser(users[i]); + } +} +void NatPunchthroughServer::OnGetMostRecentPort(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)); + uint16_t sessionId; + unsigned short mostRecentPort; + bsIn.Read(sessionId); + bsIn.Read(mostRecentPort); + + unsigned int i,j; + User *user; + ConnectionAttempt *connectionAttempt; + bool objectExists; + i = users.GetIndexFromKey(packet->guid, &objectExists); + + if (natPunchthroughServerDebugInterface) + { + RakNet::RakString log; + char addr1[128], addr2[128]; + packet->systemAddress.ToString(true,addr1); + packet->guid.ToString(addr2); + log=RakNet::RakString("Got ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s. port=%i. sessionId=%i. userFound=%i.", addr1, addr2, mostRecentPort, sessionId, objectExists); + natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); + } + + if (objectExists) + { + user=users[i]; + user->mostRecentPort=mostRecentPort; + RakNet::Time time = RakNet::GetTime(); + + for (j=0; j < user->connectionAttempts.Size(); j++) + { + connectionAttempt=user->connectionAttempts[j]; + if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS && + connectionAttempt->sender->mostRecentPort!=0 && + connectionAttempt->recipient->mostRecentPort!=0 && + // 04/29/08 add sessionId to prevent processing for other systems + connectionAttempt->sessionId==sessionId) + { + SystemAddress senderSystemAddress = connectionAttempt->sender->systemAddress; + SystemAddress recipientSystemAddress = connectionAttempt->recipient->systemAddress; + SystemAddress recipientTargetAddress = recipientSystemAddress; + SystemAddress senderTargetAddress = senderSystemAddress; + recipientTargetAddress.SetPort(connectionAttempt->recipient->mostRecentPort); + senderTargetAddress.SetPort(connectionAttempt->sender->mostRecentPort); + + // Pick a time far enough in the future that both systems will have gotten the message + int targetPing = rakPeerInterface->GetAveragePing(recipientTargetAddress); + int senderPing = rakPeerInterface->GetAveragePing(senderSystemAddress); + RakNet::Time simultaneousAttemptTime; + if (targetPing==-1 || senderPing==-1) + simultaneousAttemptTime = time + 1500; + else + { + int largerPing = targetPing > senderPing ? targetPing : senderPing; + if (largerPing * 4 < 100) + simultaneousAttemptTime = time + 100; + else + simultaneousAttemptTime = time + (largerPing * 4); + } + + if (natPunchthroughServerDebugInterface) + { + RakNet::RakString log; + char addr1[128], addr2[128]; + recipientSystemAddress.ToString(true,addr1); + connectionAttempt->recipient->guid.ToString(addr2); + log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to recipient systemAddress %s guid %s", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); + } + + // Send to recipient timestamped message to connect at time + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TIMESTAMP); + bsOut.Write(simultaneousAttemptTime); + bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME); + bsOut.Write(connectionAttempt->sessionId); + bsOut.Write(senderTargetAddress); // Public IP, using most recent port + for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP + bsOut.Write(rakPeerInterface->GetInternalID(senderSystemAddress,j)); + bsOut.Write(connectionAttempt->sender->guid); + bsOut.Write(false); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipientSystemAddress,false); + + + if (natPunchthroughServerDebugInterface) + { + RakNet::RakString log; + char addr1[128], addr2[128]; + senderSystemAddress.ToString(true,addr1); + connectionAttempt->sender->guid.ToString(addr2); + log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to sender systemAddress %s guid %s", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); + } + + + // Same for sender + bsOut.Reset(); + bsOut.Write((MessageID)ID_TIMESTAMP); + bsOut.Write(simultaneousAttemptTime); + bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME); + bsOut.Write(connectionAttempt->sessionId); + bsOut.Write(recipientTargetAddress); // Public IP, using most recent port + for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP + bsOut.Write(rakPeerInterface->GetInternalID(recipientSystemAddress,j)); + bsOut.Write(connectionAttempt->recipient->guid); + bsOut.Write(true); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,senderSystemAddress,false); + + connectionAttempt->recipient->DerefConnectionAttempt(connectionAttempt); + connectionAttempt->sender->DeleteConnectionAttempt(connectionAttempt); + + // 04/29/08 missing return + return; + } + } + } + else + { + + if (natPunchthroughServerDebugInterface) + { + RakNet::RakString log; + char addr1[128], addr2[128]; + packet->systemAddress.ToString(true,addr1); + packet->guid.ToString(addr2); + log=RakNet::RakString("Ignoring ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(log.C_String()); + } + + } +} +void NatPunchthroughServer::StartPunchthroughForUser(User *user) +{ + if (user->isReady==false) + return; + + ConnectionAttempt *connectionAttempt; + User *sender,*recipient,*otherUser; + unsigned int i; + for (i=0; i < user->connectionAttempts.Size(); i++) + { + connectionAttempt=user->connectionAttempts[i]; + if (connectionAttempt->sender==user) + { + otherUser=connectionAttempt->recipient; + sender=user; + recipient=otherUser; + } + else + { + otherUser=connectionAttempt->sender; + recipient=user; + sender=otherUser; + } + + if (otherUser->isReady) + { + if (natPunchthroughServerDebugInterface) + { + char str[1024]; + char addr1[128], addr2[128]; + sender->systemAddress.ToString(true,addr1); + recipient->systemAddress.ToString(true,addr2); + sprintf(str, "Sending NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS to sender %s and recipient %s.", addr1, addr2); + natPunchthroughServerDebugInterface->OnServerMessage(str); + } + + sender->isReady=false; + recipient->isReady=false; + connectionAttempt->attemptPhase=ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS; + connectionAttempt->startTime=RakNet::GetTime(); + + sender->mostRecentPort=0; + recipient->mostRecentPort=0; + + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT); + // 4/29/09 Write sessionID so we don't use returned port for a system we don't want + outgoingBs.Write(connectionAttempt->sessionId); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sender->systemAddress,false); + rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipient->systemAddress,false); + + // 4/22/09 - BUG: missing break statement here + break; + } + } +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/NatPunchthroughServer.h b/src/raknet/NatPunchthroughServer.h index 3b4f6df..7a7e138 100755 --- a/src/raknet/NatPunchthroughServer.h +++ b/src/raknet/NatPunchthroughServer.h @@ -1,145 +1,145 @@ -/// \file -/// \brief Contains the NAT-punchthrough plugin for the server. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatPunchthroughServer==1 - -#ifndef __NAT_PUNCHTHROUGH_SERVER_H -#define __NAT_PUNCHTHROUGH_SERVER_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "SocketIncludes.h" -#include "DS_OrderedList.h" -#include "RakString.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -struct Packet; -#if _RAKNET_SUPPORT_PacketLogger==1 -class PacketLogger; -#endif - -/// \defgroup NAT_PUNCHTHROUGH_GROUP NatPunchthrough -/// \brief Connect systems despite both systems being behind a router -/// \details -/// \ingroup PLUGINS_GROUP - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface -{ - NatPunchthroughServerDebugInterface() {} - virtual ~NatPunchthroughServerDebugInterface() {} - virtual void OnServerMessage(const char *msg)=0; -}; - -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface_Printf : public NatPunchthroughServerDebugInterface -{ - virtual void OnServerMessage(const char *msg); -}; - -#if _RAKNET_SUPPORT_PacketLogger==1 -/// \ingroup NAT_PUNCHTHROUGH_GROUP -struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface_PacketLogger : public NatPunchthroughServerDebugInterface -{ - // Set to non-zero to write to the packetlogger! - PacketLogger *pl; - - NatPunchthroughServerDebugInterface_PacketLogger() {pl=0;} - ~NatPunchthroughServerDebugInterface_PacketLogger() {} - virtual void OnServerMessage(const char *msg); -}; -#endif - -/// \brief Server code for NATPunchthrough -/// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient
      -/// Server maintains two sockets clients can connect to so as to estimate the next port choice
      -/// Server tells other client about port estimate, current public port to the server, and a time to start connection attempts -/// \sa NatTypeDetectionClient -/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html -/// \ingroup NAT_PUNCHTHROUGH_GROUP -class RAK_DLL_EXPORT NatPunchthroughServer : public PluginInterface2 -{ -public: - - STATIC_FACTORY_DECLARATIONS(NatPunchthroughServer) - - // Constructor - NatPunchthroughServer(); - - // Destructor - virtual ~NatPunchthroughServer(); - - /// Sets a callback to be called with debug messages - /// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear. - void SetDebugInterface(NatPunchthroughServerDebugInterface *i); - - /// \internal For plugin handling - virtual void Update(void); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - - /// \internal For plugin handling - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - - // Each connected user has a ready state. Ready means ready for nat punchthrough. - struct User; - struct ConnectionAttempt - { - ConnectionAttempt() {sender=0; recipient=0; startTime=0; attemptPhase=NAT_ATTEMPT_PHASE_NOT_STARTED;} - User *sender, *recipient; - uint16_t sessionId; - RakNet::Time startTime; - enum - { - NAT_ATTEMPT_PHASE_NOT_STARTED, - NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS, - } attemptPhase; - }; - struct User - { - RakNetGUID guid; - SystemAddress systemAddress; - unsigned short mostRecentPort; - bool isReady; - DataStructures::OrderedList groupPunchthroughRequests; - - DataStructures::List connectionAttempts; - bool HasConnectionAttemptToUser(User *user); - void DerefConnectionAttempt(ConnectionAttempt *ca); - void DeleteConnectionAttempt(ConnectionAttempt *ca); - void LogConnectionAttempts(RakNet::RakString &rs); - }; - RakNet::Time lastUpdate; - static int NatPunchthroughUserComp( const RakNetGUID &key, User * const &data ); -protected: - void OnNATPunchthroughRequest(Packet *packet); - DataStructures::OrderedList users; - - void OnGetMostRecentPort(Packet *packet); - void OnClientReady(Packet *packet); - - void SendTimestamps(void); - void StartPendingPunchthrough(void); - void StartPunchthroughForUser(User*user); - uint16_t sessionId; - NatPunchthroughServerDebugInterface *natPunchthroughServerDebugInterface; - -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains the NAT-punchthrough plugin for the server. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatPunchthroughServer==1 + +#ifndef __NAT_PUNCHTHROUGH_SERVER_H +#define __NAT_PUNCHTHROUGH_SERVER_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "SocketIncludes.h" +#include "DS_OrderedList.h" +#include "RakString.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +struct Packet; +#if _RAKNET_SUPPORT_PacketLogger==1 +class PacketLogger; +#endif + +/// \defgroup NAT_PUNCHTHROUGH_GROUP NatPunchthrough +/// \brief Connect systems despite both systems being behind a router +/// \details +/// \ingroup PLUGINS_GROUP + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface +{ + NatPunchthroughServerDebugInterface() {} + virtual ~NatPunchthroughServerDebugInterface() {} + virtual void OnServerMessage(const char *msg)=0; +}; + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface_Printf : public NatPunchthroughServerDebugInterface +{ + virtual void OnServerMessage(const char *msg); +}; + +#if _RAKNET_SUPPORT_PacketLogger==1 +/// \ingroup NAT_PUNCHTHROUGH_GROUP +struct RAK_DLL_EXPORT NatPunchthroughServerDebugInterface_PacketLogger : public NatPunchthroughServerDebugInterface +{ + // Set to non-zero to write to the packetlogger! + PacketLogger *pl; + + NatPunchthroughServerDebugInterface_PacketLogger() {pl=0;} + ~NatPunchthroughServerDebugInterface_PacketLogger() {} + virtual void OnServerMessage(const char *msg); +}; +#endif + +/// \brief Server code for NATPunchthrough +/// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient
      +/// Server maintains two sockets clients can connect to so as to estimate the next port choice
      +/// Server tells other client about port estimate, current public port to the server, and a time to start connection attempts +/// \sa NatTypeDetectionClient +/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html +/// \ingroup NAT_PUNCHTHROUGH_GROUP +class RAK_DLL_EXPORT NatPunchthroughServer : public PluginInterface2 +{ +public: + + STATIC_FACTORY_DECLARATIONS(NatPunchthroughServer) + + // Constructor + NatPunchthroughServer(); + + // Destructor + virtual ~NatPunchthroughServer(); + + /// Sets a callback to be called with debug messages + /// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear. + void SetDebugInterface(NatPunchthroughServerDebugInterface *i); + + /// \internal For plugin handling + virtual void Update(void); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + + /// \internal For plugin handling + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + + // Each connected user has a ready state. Ready means ready for nat punchthrough. + struct User; + struct ConnectionAttempt + { + ConnectionAttempt() {sender=0; recipient=0; startTime=0; attemptPhase=NAT_ATTEMPT_PHASE_NOT_STARTED;} + User *sender, *recipient; + uint16_t sessionId; + RakNet::Time startTime; + enum + { + NAT_ATTEMPT_PHASE_NOT_STARTED, + NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS, + } attemptPhase; + }; + struct User + { + RakNetGUID guid; + SystemAddress systemAddress; + unsigned short mostRecentPort; + bool isReady; + DataStructures::OrderedList groupPunchthroughRequests; + + DataStructures::List connectionAttempts; + bool HasConnectionAttemptToUser(User *user); + void DerefConnectionAttempt(ConnectionAttempt *ca); + void DeleteConnectionAttempt(ConnectionAttempt *ca); + void LogConnectionAttempts(RakNet::RakString &rs); + }; + RakNet::Time lastUpdate; + static int NatPunchthroughUserComp( const RakNetGUID &key, User * const &data ); +protected: + void OnNATPunchthroughRequest(Packet *packet); + DataStructures::OrderedList users; + + void OnGetMostRecentPort(Packet *packet); + void OnClientReady(Packet *packet); + + void SendTimestamps(void); + void StartPendingPunchthrough(void); + void StartPunchthroughForUser(User*user); + uint16_t sessionId; + NatPunchthroughServerDebugInterface *natPunchthroughServerDebugInterface; + +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/NatTypeDetectionClient.cpp b/src/raknet/NatTypeDetectionClient.cpp index 89e28fd..b18ac7f 100755 --- a/src/raknet/NatTypeDetectionClient.cpp +++ b/src/raknet/NatTypeDetectionClient.cpp @@ -1,170 +1,170 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatTypeDetectionClient==1 - -#include "NatTypeDetectionClient.h" -#include "RakNetSocket.h" -#include "RakNetSmartPtr.h" -#include "BitStream.h" -#include "SocketIncludes.h" -#include "RakString.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "SocketLayer.h" -#include "SocketDefines.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(NatTypeDetectionClient,NatTypeDetectionClient); - -NatTypeDetectionClient::NatTypeDetectionClient() -{ - c2=INVALID_SOCKET; -} -NatTypeDetectionClient::~NatTypeDetectionClient() -{ - if (c2!=INVALID_SOCKET) - { - closesocket__(c2); - } -} -void NatTypeDetectionClient::DetectNATType(SystemAddress _serverAddress) -{ - if (IsInProgress()) - return; - - if (c2==INVALID_SOCKET) - { - DataStructures::List > sockets; - rakPeerInterface->GetSockets(sockets); - SystemAddress sockAddr; - SocketLayer::GetSystemAddress(sockets[0]->s, &sockAddr); - char str[64]; - sockAddr.ToString(false,str); - c2=CreateNonblockingBoundSocket(str); - c2Port=SocketLayer::GetLocalPort(c2); - } - - - serverAddress=_serverAddress; - - RakNet::BitStream bs; - bs.Write((unsigned char)ID_NAT_TYPE_DETECTION_REQUEST); - bs.Write(true); // IsRequest - bs.Write(c2Port); - rakPeerInterface->Send(&bs,MEDIUM_PRIORITY,RELIABLE,0,serverAddress,false); -} -void NatTypeDetectionClient::OnCompletion(NATTypeDetectionResult result) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)*2); - printf("Returning nat detection result to the user\n"); - p->data[0]=ID_NAT_TYPE_DETECTION_RESULT; - p->systemAddress=serverAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=rakPeerInterface->GetGuidFromSystemAddress(serverAddress); - p->data[1]=(unsigned char) result; - p->wasGeneratedLocally=true; - rakPeerInterface->PushBackPacket(p, true); - - // Symmetric and port restricted are determined by server, so no need to notify server we are done - if (result!=NAT_TYPE_PORT_RESTRICTED && result!=NAT_TYPE_SYMMETRIC) - { - // Otherwise tell the server we got this message, so it stops sending tests to us - RakNet::BitStream bs; - bs.Write((unsigned char)ID_NAT_TYPE_DETECTION_REQUEST); - bs.Write(false); // Done - rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,serverAddress,false); - } - - Shutdown(); -} -bool NatTypeDetectionClient::IsInProgress(void) const -{ - return serverAddress!=UNASSIGNED_SYSTEM_ADDRESS; -} -void NatTypeDetectionClient::Update(void) -{ - if (IsInProgress()) - { - char data[ MAXIMUM_MTU_SIZE ]; - int len; - SystemAddress sender; - len=NatTypeRecvFrom(data, c2, sender); - if (len==1 && data[0]==NAT_TYPE_NONE) - { - OnCompletion(NAT_TYPE_NONE); - RakAssert(IsInProgress()==false); - } - } -} -PluginReceiveResult NatTypeDetectionClient::OnReceive(Packet *packet) -{ - if (IsInProgress()) - { - switch (packet->data[0]) - { - case ID_OUT_OF_BAND_INTERNAL: - { - if (packet->length>=3 && packet->data[1]==ID_NAT_TYPE_DETECT) - { - OnCompletion((NATTypeDetectionResult)packet->data[2]); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - break; - case ID_NAT_TYPE_DETECTION_RESULT: - if (packet->wasGeneratedLocally==false) - { - OnCompletion((NATTypeDetectionResult)packet->data[1]); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - else - break; - case ID_NAT_TYPE_DETECTION_REQUEST: - OnTestPortRestricted(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - - return RR_CONTINUE_PROCESSING; -} -void NatTypeDetectionClient::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - if (IsInProgress() && systemAddress==serverAddress) - Shutdown(); -} -void NatTypeDetectionClient::OnTestPortRestricted(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - RakNet::RakString s3p4StrAddress; - bsIn.Read(s3p4StrAddress); - unsigned short s3p4Port; - bsIn.Read(s3p4Port); - - DataStructures::List > sockets; - rakPeerInterface->GetSockets(sockets); - SystemAddress s3p4Addr = sockets[0]->boundAddress; - s3p4Addr.FromStringExplicitPort(s3p4StrAddress.C_String(), s3p4Port); - - // Send off the RakNet socket to the specified address, message is unformatted - // Server does this twice, so don't have to unduly worry about packetloss - RakNet::BitStream bsOut; - bsOut.Write((MessageID) NAT_TYPE_PORT_RESTRICTED); - bsOut.Write(rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - SocketLayer::SendTo_PC( sockets[0]->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), s3p4Addr, __FILE__, __LINE__ ); -} -void NatTypeDetectionClient::Shutdown(void) -{ - serverAddress=UNASSIGNED_SYSTEM_ADDRESS; - if (c2!=INVALID_SOCKET) - { - closesocket__(c2); - c2=INVALID_SOCKET; - } - -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatTypeDetectionClient==1 + +#include "NatTypeDetectionClient.h" +#include "RakNetSocket.h" +#include "RakNetSmartPtr.h" +#include "BitStream.h" +#include "SocketIncludes.h" +#include "RakString.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "SocketLayer.h" +#include "SocketDefines.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(NatTypeDetectionClient,NatTypeDetectionClient); + +NatTypeDetectionClient::NatTypeDetectionClient() +{ + c2=INVALID_SOCKET; +} +NatTypeDetectionClient::~NatTypeDetectionClient() +{ + if (c2!=INVALID_SOCKET) + { + closesocket__(c2); + } +} +void NatTypeDetectionClient::DetectNATType(SystemAddress _serverAddress) +{ + if (IsInProgress()) + return; + + if (c2==INVALID_SOCKET) + { + DataStructures::List > sockets; + rakPeerInterface->GetSockets(sockets); + SystemAddress sockAddr; + SocketLayer::GetSystemAddress(sockets[0]->s, &sockAddr); + char str[64]; + sockAddr.ToString(false,str); + c2=CreateNonblockingBoundSocket(str); + c2Port=SocketLayer::GetLocalPort(c2); + } + + + serverAddress=_serverAddress; + + RakNet::BitStream bs; + bs.Write((unsigned char)ID_NAT_TYPE_DETECTION_REQUEST); + bs.Write(true); // IsRequest + bs.Write(c2Port); + rakPeerInterface->Send(&bs,MEDIUM_PRIORITY,RELIABLE,0,serverAddress,false); +} +void NatTypeDetectionClient::OnCompletion(NATTypeDetectionResult result) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)*2); + printf("Returning nat detection result to the user\n"); + p->data[0]=ID_NAT_TYPE_DETECTION_RESULT; + p->systemAddress=serverAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=rakPeerInterface->GetGuidFromSystemAddress(serverAddress); + p->data[1]=(unsigned char) result; + p->wasGeneratedLocally=true; + rakPeerInterface->PushBackPacket(p, true); + + // Symmetric and port restricted are determined by server, so no need to notify server we are done + if (result!=NAT_TYPE_PORT_RESTRICTED && result!=NAT_TYPE_SYMMETRIC) + { + // Otherwise tell the server we got this message, so it stops sending tests to us + RakNet::BitStream bs; + bs.Write((unsigned char)ID_NAT_TYPE_DETECTION_REQUEST); + bs.Write(false); // Done + rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,serverAddress,false); + } + + Shutdown(); +} +bool NatTypeDetectionClient::IsInProgress(void) const +{ + return serverAddress!=UNASSIGNED_SYSTEM_ADDRESS; +} +void NatTypeDetectionClient::Update(void) +{ + if (IsInProgress()) + { + char data[ MAXIMUM_MTU_SIZE ]; + int len; + SystemAddress sender; + len=NatTypeRecvFrom(data, c2, sender); + if (len==1 && data[0]==NAT_TYPE_NONE) + { + OnCompletion(NAT_TYPE_NONE); + RakAssert(IsInProgress()==false); + } + } +} +PluginReceiveResult NatTypeDetectionClient::OnReceive(Packet *packet) +{ + if (IsInProgress()) + { + switch (packet->data[0]) + { + case ID_OUT_OF_BAND_INTERNAL: + { + if (packet->length>=3 && packet->data[1]==ID_NAT_TYPE_DETECT) + { + OnCompletion((NATTypeDetectionResult)packet->data[2]); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + break; + case ID_NAT_TYPE_DETECTION_RESULT: + if (packet->wasGeneratedLocally==false) + { + OnCompletion((NATTypeDetectionResult)packet->data[1]); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + else + break; + case ID_NAT_TYPE_DETECTION_REQUEST: + OnTestPortRestricted(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + + return RR_CONTINUE_PROCESSING; +} +void NatTypeDetectionClient::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + if (IsInProgress() && systemAddress==serverAddress) + Shutdown(); +} +void NatTypeDetectionClient::OnTestPortRestricted(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + RakNet::RakString s3p4StrAddress; + bsIn.Read(s3p4StrAddress); + unsigned short s3p4Port; + bsIn.Read(s3p4Port); + + DataStructures::List > sockets; + rakPeerInterface->GetSockets(sockets); + SystemAddress s3p4Addr = sockets[0]->boundAddress; + s3p4Addr.FromStringExplicitPort(s3p4StrAddress.C_String(), s3p4Port); + + // Send off the RakNet socket to the specified address, message is unformatted + // Server does this twice, so don't have to unduly worry about packetloss + RakNet::BitStream bsOut; + bsOut.Write((MessageID) NAT_TYPE_PORT_RESTRICTED); + bsOut.Write(rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + SocketLayer::SendTo_PC( sockets[0]->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), s3p4Addr, __FILE__, __LINE__ ); +} +void NatTypeDetectionClient::Shutdown(void) +{ + serverAddress=UNASSIGNED_SYSTEM_ADDRESS; + if (c2!=INVALID_SOCKET) + { + closesocket__(c2); + c2=INVALID_SOCKET; + } + +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/NatTypeDetectionClient.h b/src/raknet/NatTypeDetectionClient.h index 86cc74e..09614dc 100755 --- a/src/raknet/NatTypeDetectionClient.h +++ b/src/raknet/NatTypeDetectionClient.h @@ -1,81 +1,81 @@ -/// \file -/// \brief Contains the NAT-type detection code for the client -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatTypeDetectionClient==1 - -#ifndef __NAT_TYPE_DETECTION_CLIENT_H -#define __NAT_TYPE_DETECTION_CLIENT_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "SocketIncludes.h" -#include "DS_OrderedList.h" -#include "RakString.h" -#include "NatTypeDetectionCommon.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -struct Packet; - - /// \brief Client code for NatTypeDetection - /// \details See NatTypeDetectionServer.h for algorithm - /// To use, just connect to the server, and call DetectNAT - /// You will get back ID_NAT_TYPE_DETECTION_RESULT with one of the enumerated values of NATTypeDetectionResult found in NATTypeDetectionCommon.h - /// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html - /// \sa NatPunchthroughClient - /// \sa NatTypeDetectionServer - /// \ingroup NAT_TYPE_DETECTION_GROUP - class RAK_DLL_EXPORT NatTypeDetectionClient : public PluginInterface2 - { - public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(NatTypeDetectionClient) - - // Constructor - NatTypeDetectionClient(); - - // Destructor - virtual ~NatTypeDetectionClient(); - - /// Send the message to the server to detect the nat type - /// Server must be running NatTypeDetectionServer - /// We must already be connected to the server - /// \param[in] serverAddress address of the server - void DetectNATType(SystemAddress _serverAddress); - - /// \internal For plugin handling - virtual void Update(void); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - protected: - SOCKET c2; - unsigned short c2Port; - void Shutdown(void); - void OnCompletion(NATTypeDetectionResult result); - bool IsInProgress(void) const; - - void OnTestPortRestricted(Packet *packet); - SystemAddress serverAddress; - }; - - -} - - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains the NAT-type detection code for the client +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatTypeDetectionClient==1 + +#ifndef __NAT_TYPE_DETECTION_CLIENT_H +#define __NAT_TYPE_DETECTION_CLIENT_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "SocketIncludes.h" +#include "DS_OrderedList.h" +#include "RakString.h" +#include "NatTypeDetectionCommon.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +struct Packet; + + /// \brief Client code for NatTypeDetection + /// \details See NatTypeDetectionServer.h for algorithm + /// To use, just connect to the server, and call DetectNAT + /// You will get back ID_NAT_TYPE_DETECTION_RESULT with one of the enumerated values of NATTypeDetectionResult found in NATTypeDetectionCommon.h + /// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html + /// \sa NatPunchthroughClient + /// \sa NatTypeDetectionServer + /// \ingroup NAT_TYPE_DETECTION_GROUP + class RAK_DLL_EXPORT NatTypeDetectionClient : public PluginInterface2 + { + public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(NatTypeDetectionClient) + + // Constructor + NatTypeDetectionClient(); + + // Destructor + virtual ~NatTypeDetectionClient(); + + /// Send the message to the server to detect the nat type + /// Server must be running NatTypeDetectionServer + /// We must already be connected to the server + /// \param[in] serverAddress address of the server + void DetectNATType(SystemAddress _serverAddress); + + /// \internal For plugin handling + virtual void Update(void); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + protected: + SOCKET c2; + unsigned short c2Port; + void Shutdown(void); + void OnCompletion(NATTypeDetectionResult result); + bool IsInProgress(void) const; + + void OnTestPortRestricted(Packet *packet); + SystemAddress serverAddress; + }; + + +} + + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/NatTypeDetectionCommon.cpp b/src/raknet/NatTypeDetectionCommon.cpp index ddfc5d1..89856d5 100755 --- a/src/raknet/NatTypeDetectionCommon.cpp +++ b/src/raknet/NatTypeDetectionCommon.cpp @@ -1,116 +1,116 @@ -#include "NatTypeDetectionCommon.h" -#include "SocketLayer.h" -#include "SocketIncludes.h" -#include "SocketDefines.h" - -using namespace RakNet; - -bool RakNet::CanConnect(NATTypeDetectionResult type1, NATTypeDetectionResult type2) -{ - /// If one system is NAT_TYPE_SYMMETRIC, the other must be NAT_TYPE_ADDRESS_RESTRICTED or less - /// If one system is NAT_TYPE_PORT_RESTRICTED, the other must be NAT_TYPE_PORT_RESTRICTED or less - bool connectionGraph[NAT_TYPE_COUNT][NAT_TYPE_COUNT] = - { - // None, Full Cone, Address Restricted, Port Restricted, Symmetric, Unknown, InProgress, Supports_UPNP - {true, true, true, true, true, false, false, false}, // None - {true, true, true, true, true, false, false, false}, // Full Cone - {true, true, true, true, true, false, false, false}, // Address restricted - {true, true, true, true, false, false, false, false}, // Port restricted - {true, true, true, false, false, false, false, false}, // Symmetric - {false, false, false, false, false, false, false, false}, // Unknown - {false, false, false, false, false, false, false, false}, // InProgress - {false, false, false, false, false, false, false, false} // Supports_UPNP - }; - - return connectionGraph[(int) type1][(int) type2]; -} - -const char *RakNet::NATTypeDetectionResultToString(NATTypeDetectionResult type) -{ - switch (type) - { - case NAT_TYPE_NONE: - return "None"; - case NAT_TYPE_FULL_CONE: - return "Full cone"; - case NAT_TYPE_ADDRESS_RESTRICTED: - return "Address restricted"; - case NAT_TYPE_PORT_RESTRICTED: - return "Port restricted"; - case NAT_TYPE_SYMMETRIC: - return "Symmetric"; - case NAT_TYPE_UNKNOWN: - return "Unknown"; - case NAT_TYPE_DETECTION_IN_PROGRESS: - return "In Progress"; - case NAT_TYPE_SUPPORTS_UPNP: - return "Supports UPNP"; - case NAT_TYPE_COUNT: - return "NAT_TYPE_COUNT"; - } - return "Error, unknown enum in NATTypeDetectionResult"; -} - -// None and relaxed can connect to anything -// Moderate can connect to moderate or less -// Strict can connect to relaxed or less -const char *RakNet::NATTypeDetectionResultToStringFriendly(NATTypeDetectionResult type) -{ - switch (type) - { - case NAT_TYPE_NONE: - return "Open"; - case NAT_TYPE_FULL_CONE: - return "Relaxed"; - case NAT_TYPE_ADDRESS_RESTRICTED: - return "Relaxed"; - case NAT_TYPE_PORT_RESTRICTED: - return "Moderate"; - case NAT_TYPE_SYMMETRIC: - return "Strict"; - case NAT_TYPE_UNKNOWN: - return "Unknown"; - case NAT_TYPE_DETECTION_IN_PROGRESS: - return "In Progress"; - case NAT_TYPE_SUPPORTS_UPNP: - return "Supports UPNP"; - case NAT_TYPE_COUNT: - return "NAT_TYPE_COUNT"; - } - return "Error, unknown enum in NATTypeDetectionResult"; -} - - -SOCKET RakNet::CreateNonblockingBoundSocket(const char *bindAddr ) -{ - SOCKET s = SocketLayer::CreateBoundSocket( 0, false, bindAddr, true, 0, AF_INET ); - #ifdef _WIN32 - unsigned long nonblocking = 1; - ioctlsocket__( s, FIONBIO, &nonblocking ); - - - - #else - fcntl( s, F_SETFL, O_NONBLOCK ); - #endif - return s; -} - -int RakNet::NatTypeRecvFrom(char *data, SOCKET socket, SystemAddress &sender) -{ - sockaddr_in sa; - socklen_t len2; - const int flag=0; - len2 = sizeof( sa ); - sa.sin_family = AF_INET; - sa.sin_port=0; - int len = recvfrom__( socket, data, MAXIMUM_MTU_SIZE, flag, ( sockaddr* ) & sa, ( socklen_t* ) & len2 ); - if (len>0) - { - sender.address.addr4.sin_family=AF_INET; - sender.address.addr4.sin_addr.s_addr = sa.sin_addr.s_addr; - //sender.SetPort( ntohs( sa.sin_port ) ); - sender.SetPort( ntohs( sa.sin_port ) ); - } - return len; -} +#include "NatTypeDetectionCommon.h" +#include "SocketLayer.h" +#include "SocketIncludes.h" +#include "SocketDefines.h" + +using namespace RakNet; + +bool RakNet::CanConnect(NATTypeDetectionResult type1, NATTypeDetectionResult type2) +{ + /// If one system is NAT_TYPE_SYMMETRIC, the other must be NAT_TYPE_ADDRESS_RESTRICTED or less + /// If one system is NAT_TYPE_PORT_RESTRICTED, the other must be NAT_TYPE_PORT_RESTRICTED or less + bool connectionGraph[NAT_TYPE_COUNT][NAT_TYPE_COUNT] = + { + // None, Full Cone, Address Restricted, Port Restricted, Symmetric, Unknown, InProgress, Supports_UPNP + {true, true, true, true, true, false, false, false}, // None + {true, true, true, true, true, false, false, false}, // Full Cone + {true, true, true, true, true, false, false, false}, // Address restricted + {true, true, true, true, false, false, false, false}, // Port restricted + {true, true, true, false, false, false, false, false}, // Symmetric + {false, false, false, false, false, false, false, false}, // Unknown + {false, false, false, false, false, false, false, false}, // InProgress + {false, false, false, false, false, false, false, false} // Supports_UPNP + }; + + return connectionGraph[(int) type1][(int) type2]; +} + +const char *RakNet::NATTypeDetectionResultToString(NATTypeDetectionResult type) +{ + switch (type) + { + case NAT_TYPE_NONE: + return "None"; + case NAT_TYPE_FULL_CONE: + return "Full cone"; + case NAT_TYPE_ADDRESS_RESTRICTED: + return "Address restricted"; + case NAT_TYPE_PORT_RESTRICTED: + return "Port restricted"; + case NAT_TYPE_SYMMETRIC: + return "Symmetric"; + case NAT_TYPE_UNKNOWN: + return "Unknown"; + case NAT_TYPE_DETECTION_IN_PROGRESS: + return "In Progress"; + case NAT_TYPE_SUPPORTS_UPNP: + return "Supports UPNP"; + case NAT_TYPE_COUNT: + return "NAT_TYPE_COUNT"; + } + return "Error, unknown enum in NATTypeDetectionResult"; +} + +// None and relaxed can connect to anything +// Moderate can connect to moderate or less +// Strict can connect to relaxed or less +const char *RakNet::NATTypeDetectionResultToStringFriendly(NATTypeDetectionResult type) +{ + switch (type) + { + case NAT_TYPE_NONE: + return "Open"; + case NAT_TYPE_FULL_CONE: + return "Relaxed"; + case NAT_TYPE_ADDRESS_RESTRICTED: + return "Relaxed"; + case NAT_TYPE_PORT_RESTRICTED: + return "Moderate"; + case NAT_TYPE_SYMMETRIC: + return "Strict"; + case NAT_TYPE_UNKNOWN: + return "Unknown"; + case NAT_TYPE_DETECTION_IN_PROGRESS: + return "In Progress"; + case NAT_TYPE_SUPPORTS_UPNP: + return "Supports UPNP"; + case NAT_TYPE_COUNT: + return "NAT_TYPE_COUNT"; + } + return "Error, unknown enum in NATTypeDetectionResult"; +} + + +SOCKET RakNet::CreateNonblockingBoundSocket(const char *bindAddr ) +{ + SOCKET s = SocketLayer::CreateBoundSocket( 0, false, bindAddr, true, 0, AF_INET ); + #ifdef _WIN32 + unsigned long nonblocking = 1; + ioctlsocket__( s, FIONBIO, &nonblocking ); + + + + #else + fcntl( s, F_SETFL, O_NONBLOCK ); + #endif + return s; +} + +int RakNet::NatTypeRecvFrom(char *data, SOCKET socket, SystemAddress &sender) +{ + sockaddr_in sa; + socklen_t len2; + const int flag=0; + len2 = sizeof( sa ); + sa.sin_family = AF_INET; + sa.sin_port=0; + int len = recvfrom__( socket, data, MAXIMUM_MTU_SIZE, flag, ( sockaddr* ) & sa, ( socklen_t* ) & len2 ); + if (len>0) + { + sender.address.addr4.sin_family=AF_INET; + sender.address.addr4.sin_addr.s_addr = sa.sin_addr.s_addr; + //sender.SetPort( ntohs( sa.sin_port ) ); + sender.SetPort( ntohs( sa.sin_port ) ); + } + return len; +} diff --git a/src/raknet/NatTypeDetectionCommon.h b/src/raknet/NatTypeDetectionCommon.h index 1fbdacf..6c59c03 100755 --- a/src/raknet/NatTypeDetectionCommon.h +++ b/src/raknet/NatTypeDetectionCommon.h @@ -1,56 +1,56 @@ -/// \defgroup NAT_TYPE_DETECTION_GROUP NatTypeDetection -/// \brief Use a remote server with multiple IP addresses to determine what type of NAT your router is using -/// \details -/// \ingroup PLUGINS_GROUP - -#ifndef __NAT_TYPE_DETECTION_COMMON_H -#define __NAT_TYPE_DETECTION_COMMON_H - -#include "SocketIncludes.h" -#include "RakNetTypes.h" - -namespace RakNet -{ - /// All possible types of NATs (except NAT_TYPE_COUNT, which is an internal value) - enum NATTypeDetectionResult - { - /// Works with anyone - NAT_TYPE_NONE, - /// Accepts any datagrams to a port that has been previously used. Will accept the first datagram from the remote peer. - NAT_TYPE_FULL_CONE, - /// Accepts datagrams to a port as long as the datagram source IP address is a system we have already sent to. Will accept the first datagram if both systems send simultaneously. Otherwise, will accept the first datagram after we have sent one datagram. - NAT_TYPE_ADDRESS_RESTRICTED, - /// Same as address-restricted cone NAT, but we had to send to both the correct remote IP address and correct remote port. The same source address and port to a different destination uses the same mapping. - NAT_TYPE_PORT_RESTRICTED, - /// A different port is chosen for every remote destination. The same source address and port to a different destination uses a different mapping. Since the port will be different, the first external punchthrough attempt will fail. For this to work it requires port-prediction (MAX_PREDICTIVE_PORT_RANGE>1) and that the router chooses ports sequentially. - NAT_TYPE_SYMMETRIC, - /// Hasn't been determined. NATTypeDetectionClient does not use this, but other plugins might - NAT_TYPE_UNKNOWN, - /// In progress. NATTypeDetectionClient does not use this, but other plugins might - NAT_TYPE_DETECTION_IN_PROGRESS, - /// Didn't bother figuring it out, as we support UPNP, so it is equivalent to NAT_TYPE_NONE. NATTypeDetectionClient does not use this, but other plugins might - NAT_TYPE_SUPPORTS_UPNP, - /// \internal Must be last - NAT_TYPE_COUNT - }; - - /// \return Can one system with NATTypeDetectionResult \a type1 connect to \a type2 - bool RAK_DLL_EXPORT CanConnect(NATTypeDetectionResult type1, NATTypeDetectionResult type2); - - /// Return a technical string representin the enumeration - RAK_DLL_EXPORT const char * NATTypeDetectionResultToString(NATTypeDetectionResult type); - - /// Return a friendly string representing the enumeration - /// None and relaxed can connect to anything - /// Moderate can connect to moderate or less - /// Strict can connect to relaxed or less - RAK_DLL_EXPORT const char * NATTypeDetectionResultToStringFriendly(NATTypeDetectionResult type); - - /// \internal - SOCKET RAK_DLL_EXPORT CreateNonblockingBoundSocket(const char *bindAddr); - - /// \internal - int NatTypeRecvFrom(char *data, SOCKET socket, SystemAddress &sender); -} - -#endif +/// \defgroup NAT_TYPE_DETECTION_GROUP NatTypeDetection +/// \brief Use a remote server with multiple IP addresses to determine what type of NAT your router is using +/// \details +/// \ingroup PLUGINS_GROUP + +#ifndef __NAT_TYPE_DETECTION_COMMON_H +#define __NAT_TYPE_DETECTION_COMMON_H + +#include "SocketIncludes.h" +#include "RakNetTypes.h" + +namespace RakNet +{ + /// All possible types of NATs (except NAT_TYPE_COUNT, which is an internal value) + enum NATTypeDetectionResult + { + /// Works with anyone + NAT_TYPE_NONE, + /// Accepts any datagrams to a port that has been previously used. Will accept the first datagram from the remote peer. + NAT_TYPE_FULL_CONE, + /// Accepts datagrams to a port as long as the datagram source IP address is a system we have already sent to. Will accept the first datagram if both systems send simultaneously. Otherwise, will accept the first datagram after we have sent one datagram. + NAT_TYPE_ADDRESS_RESTRICTED, + /// Same as address-restricted cone NAT, but we had to send to both the correct remote IP address and correct remote port. The same source address and port to a different destination uses the same mapping. + NAT_TYPE_PORT_RESTRICTED, + /// A different port is chosen for every remote destination. The same source address and port to a different destination uses a different mapping. Since the port will be different, the first external punchthrough attempt will fail. For this to work it requires port-prediction (MAX_PREDICTIVE_PORT_RANGE>1) and that the router chooses ports sequentially. + NAT_TYPE_SYMMETRIC, + /// Hasn't been determined. NATTypeDetectionClient does not use this, but other plugins might + NAT_TYPE_UNKNOWN, + /// In progress. NATTypeDetectionClient does not use this, but other plugins might + NAT_TYPE_DETECTION_IN_PROGRESS, + /// Didn't bother figuring it out, as we support UPNP, so it is equivalent to NAT_TYPE_NONE. NATTypeDetectionClient does not use this, but other plugins might + NAT_TYPE_SUPPORTS_UPNP, + /// \internal Must be last + NAT_TYPE_COUNT + }; + + /// \return Can one system with NATTypeDetectionResult \a type1 connect to \a type2 + bool RAK_DLL_EXPORT CanConnect(NATTypeDetectionResult type1, NATTypeDetectionResult type2); + + /// Return a technical string representin the enumeration + RAK_DLL_EXPORT const char * NATTypeDetectionResultToString(NATTypeDetectionResult type); + + /// Return a friendly string representing the enumeration + /// None and relaxed can connect to anything + /// Moderate can connect to moderate or less + /// Strict can connect to relaxed or less + RAK_DLL_EXPORT const char * NATTypeDetectionResultToStringFriendly(NATTypeDetectionResult type); + + /// \internal + SOCKET RAK_DLL_EXPORT CreateNonblockingBoundSocket(const char *bindAddr); + + /// \internal + int NatTypeRecvFrom(char *data, SOCKET socket, SystemAddress &sender); +} + +#endif diff --git a/src/raknet/NatTypeDetectionServer.cpp b/src/raknet/NatTypeDetectionServer.cpp index cf9e7be..ede3af3 100755 --- a/src/raknet/NatTypeDetectionServer.cpp +++ b/src/raknet/NatTypeDetectionServer.cpp @@ -1,263 +1,263 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatTypeDetectionServer==1 - -#include "NatTypeDetectionServer.h" -#include "SocketLayer.h" -#include "RakNetSocket.h" -#include "RakNetSmartPtr.h" -#include "SocketIncludes.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "GetTime.h" -#include "BitStream.h" -#include "SocketDefines.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(NatTypeDetectionServer,NatTypeDetectionServer); - -NatTypeDetectionServer::NatTypeDetectionServer() -{ - s1p2=s2p3=s3p4=s4p5=INVALID_SOCKET; -} -NatTypeDetectionServer::~NatTypeDetectionServer() -{ - Shutdown(); -} -void NatTypeDetectionServer::Startup( - const char *nonRakNetIP2, - const char *nonRakNetIP3, - const char *nonRakNetIP4) -{ - DataStructures::List > sockets; - rakPeerInterface->GetSockets(sockets); - char str[64]; - sockets[0]->boundAddress.ToString(false,str); - s1p2=CreateNonblockingBoundSocket(str); - s1p2Port=SocketLayer::GetLocalPort(s1p2); - s2p3=CreateNonblockingBoundSocket(nonRakNetIP2); - s2p3Port=SocketLayer::GetLocalPort(s2p3); - s3p4=CreateNonblockingBoundSocket(nonRakNetIP3); - s3p4Port=SocketLayer::GetLocalPort(s3p4); - s4p5=CreateNonblockingBoundSocket(nonRakNetIP4); - s4p5Port=SocketLayer::GetLocalPort(s4p5); - strcpy(s3p4Address, nonRakNetIP3); -} -void NatTypeDetectionServer::Shutdown() -{ - if (s1p2!=INVALID_SOCKET) - { - closesocket__(s1p2); - s1p2=INVALID_SOCKET; - } - if (s2p3!=INVALID_SOCKET) - { - closesocket__(s2p3); - s2p3=INVALID_SOCKET; - } - if (s3p4!=INVALID_SOCKET) - { - closesocket__(s3p4); - s3p4=INVALID_SOCKET; - } - if (s4p5!=INVALID_SOCKET) - { - closesocket__(s4p5); - s4p5=INVALID_SOCKET; - } -} -void NatTypeDetectionServer::Update(void) -{ - int i=0; - RakNet::TimeMS time = RakNet::GetTimeMS(); - RakNet::BitStream bs; - SystemAddress boundAddress; - - // Only socket that receives messages is s3p4, to see if the external address is different than that of the connection to rakPeerInterface - char data[ MAXIMUM_MTU_SIZE ]; - int len; - SystemAddress senderAddr; - len=NatTypeRecvFrom(data, s3p4, senderAddr); - // Client is asking us if this is port restricted. Only client requests of this type come in on s3p4 - while (len>0 && data[0]==NAT_TYPE_PORT_RESTRICTED) - { - RakNet::BitStream bsIn((unsigned char*) data,len,false); - RakNetGUID senderGuid; - bsIn.IgnoreBytes(sizeof(MessageID)); - bool readSuccess = bsIn.Read(senderGuid); - RakAssert(readSuccess); - if (readSuccess) - { - unsigned int i = GetDetectionAttemptIndex(senderGuid); - if (i!=(unsigned int)-1) - { - bs.Reset(); - bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); - // If different, then symmetric - if (senderAddr!=natDetectionAttempts[i].systemAddress) - { - printf("Determined client is symmetric\n"); - bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); - } - else - { - // else port restricted - printf("Determined client is port restricted\n"); - bs.Write((unsigned char) NAT_TYPE_PORT_RESTRICTED); - } - - rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); - - // Done - natDetectionAttempts.RemoveAtIndexFast(i); - } - else - { - // RakAssert("i==0 in Update when looking up GUID in NatTypeDetectionServer.cpp. Either a bug or a late resend" && 0); - } - } - else - { - // RakAssert("Didn't read GUID in Update in NatTypeDetectionServer.cpp. Message format error" && 0); - } - - len=NatTypeRecvFrom(data, s3p4, senderAddr); - } - - - while (i < (int) natDetectionAttempts.Size()) - { - if (time > natDetectionAttempts[i].nextStateTime) - { - natDetectionAttempts[i].detectionState=(NATDetectionState)((int)natDetectionAttempts[i].detectionState+1); - natDetectionAttempts[i].nextStateTime=time+natDetectionAttempts[i].timeBetweenAttempts; - SystemAddress saOut; - unsigned char c; - bs.Reset(); - switch (natDetectionAttempts[i].detectionState) - { - case STATE_TESTING_NONE_1: - case STATE_TESTING_NONE_2: - c = NAT_TYPE_NONE; - printf("Testing NAT_TYPE_NONE\n"); - // S4P5 sends to C2. If arrived, no NAT. Done. (Else S4P5 potentially banned, do not use again). - saOut=natDetectionAttempts[i].systemAddress; - saOut.SetPort(natDetectionAttempts[i].c2Port); - SocketLayer::SendTo_PC( s4p5, (const char*) &c, 1, saOut, __FILE__, __LINE__ ); - break; - case STATE_TESTING_FULL_CONE_1: - case STATE_TESTING_FULL_CONE_2: - printf("Testing NAT_TYPE_FULL_CONE\n"); - rakPeerInterface->WriteOutOfBandHeader(&bs); - bs.Write((unsigned char) ID_NAT_TYPE_DETECT); - bs.Write((unsigned char) NAT_TYPE_FULL_CONE); - // S2P3 sends to C1 (Different address, different port, to previously used port on client). If received, Full-cone nat. Done. (Else S2P3 potentially banned, do not use again). - saOut=natDetectionAttempts[i].systemAddress; - saOut.SetPort(natDetectionAttempts[i].systemAddress.GetPort()); - SocketLayer::SendTo_PC( s2p3, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); - break; - case STATE_TESTING_ADDRESS_RESTRICTED_1: - case STATE_TESTING_ADDRESS_RESTRICTED_2: - printf("Testing NAT_TYPE_ADDRESS_RESTRICTED\n"); - rakPeerInterface->WriteOutOfBandHeader(&bs); - bs.Write((unsigned char) ID_NAT_TYPE_DETECT); - bs.Write((unsigned char) NAT_TYPE_ADDRESS_RESTRICTED); - // S1P2 sends to C1 (Same address, different port, to previously used port on client). If received, address-restricted cone nat. Done. - saOut=natDetectionAttempts[i].systemAddress; - saOut.SetPort(natDetectionAttempts[i].systemAddress.GetPort()); - SocketLayer::SendTo_PC( s1p2, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); - break; - case STATE_TESTING_PORT_RESTRICTED_1: - case STATE_TESTING_PORT_RESTRICTED_2: - // C1 sends to S3P4. If address of C1 as seen by S3P4 is the same as the address of C1 as seen by S1P1, then port-restricted cone nat. Done - printf("Testing NAT_TYPE_PORT_RESTRICTED\n"); - bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_REQUEST); - bs.Write(RakString::NonVariadic(s3p4Address)); - bs.Write(s3p4Port); - rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); - break; - default: - printf("Warning, exceeded final check STATE_TESTING_PORT_RESTRICTED_2.\nExpected that client would have sent NAT_TYPE_PORT_RESTRICTED on s3p4.\nDefaulting to Symmetric\n"); - bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); - bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); - rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); - natDetectionAttempts.RemoveAtIndexFast(i); - i--; - break; - } - - } - i++; - } -} -PluginReceiveResult NatTypeDetectionServer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_NAT_TYPE_DETECTION_REQUEST: - OnDetectionRequest(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - return RR_CONTINUE_PROCESSING; -} -void NatTypeDetectionServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - unsigned int i = GetDetectionAttemptIndex(systemAddress); - if (i==(unsigned int)-1) - return; - natDetectionAttempts.RemoveAtIndexFast(i); -} -void NatTypeDetectionServer::OnDetectionRequest(Packet *packet) -{ - unsigned int i = GetDetectionAttemptIndex(packet->systemAddress); - - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(1); - bool isRequest=false; - bsIn.Read(isRequest); - if (isRequest) - { - if (i!=(unsigned int)-1) - return; // Already in progress - - NATDetectionAttempt nda; - nda.detectionState=STATE_NONE; - nda.systemAddress=packet->systemAddress; - nda.guid=packet->guid; - bsIn.Read(nda.c2Port); - nda.nextStateTime=0; - nda.timeBetweenAttempts=rakPeerInterface->GetLastPing(nda.systemAddress)*3+50; - natDetectionAttempts.Push(nda, _FILE_AND_LINE_); - } - else - { - if (i==(unsigned int)-1) - return; // Unknown - // They are done - natDetectionAttempts.RemoveAtIndexFast(i); - } - -} -unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(const SystemAddress &sa) -{ - for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) - { - if (natDetectionAttempts[i].systemAddress==sa) - return i; - } - return (unsigned int) -1; -} -unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(RakNetGUID guid) -{ - for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) - { - if (natDetectionAttempts[i].guid==guid) - return i; - } - return (unsigned int) -1; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatTypeDetectionServer==1 + +#include "NatTypeDetectionServer.h" +#include "SocketLayer.h" +#include "RakNetSocket.h" +#include "RakNetSmartPtr.h" +#include "SocketIncludes.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "GetTime.h" +#include "BitStream.h" +#include "SocketDefines.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(NatTypeDetectionServer,NatTypeDetectionServer); + +NatTypeDetectionServer::NatTypeDetectionServer() +{ + s1p2=s2p3=s3p4=s4p5=INVALID_SOCKET; +} +NatTypeDetectionServer::~NatTypeDetectionServer() +{ + Shutdown(); +} +void NatTypeDetectionServer::Startup( + const char *nonRakNetIP2, + const char *nonRakNetIP3, + const char *nonRakNetIP4) +{ + DataStructures::List > sockets; + rakPeerInterface->GetSockets(sockets); + char str[64]; + sockets[0]->boundAddress.ToString(false,str); + s1p2=CreateNonblockingBoundSocket(str); + s1p2Port=SocketLayer::GetLocalPort(s1p2); + s2p3=CreateNonblockingBoundSocket(nonRakNetIP2); + s2p3Port=SocketLayer::GetLocalPort(s2p3); + s3p4=CreateNonblockingBoundSocket(nonRakNetIP3); + s3p4Port=SocketLayer::GetLocalPort(s3p4); + s4p5=CreateNonblockingBoundSocket(nonRakNetIP4); + s4p5Port=SocketLayer::GetLocalPort(s4p5); + strcpy(s3p4Address, nonRakNetIP3); +} +void NatTypeDetectionServer::Shutdown() +{ + if (s1p2!=INVALID_SOCKET) + { + closesocket__(s1p2); + s1p2=INVALID_SOCKET; + } + if (s2p3!=INVALID_SOCKET) + { + closesocket__(s2p3); + s2p3=INVALID_SOCKET; + } + if (s3p4!=INVALID_SOCKET) + { + closesocket__(s3p4); + s3p4=INVALID_SOCKET; + } + if (s4p5!=INVALID_SOCKET) + { + closesocket__(s4p5); + s4p5=INVALID_SOCKET; + } +} +void NatTypeDetectionServer::Update(void) +{ + int i=0; + RakNet::TimeMS time = RakNet::GetTimeMS(); + RakNet::BitStream bs; + SystemAddress boundAddress; + + // Only socket that receives messages is s3p4, to see if the external address is different than that of the connection to rakPeerInterface + char data[ MAXIMUM_MTU_SIZE ]; + int len; + SystemAddress senderAddr; + len=NatTypeRecvFrom(data, s3p4, senderAddr); + // Client is asking us if this is port restricted. Only client requests of this type come in on s3p4 + while (len>0 && data[0]==NAT_TYPE_PORT_RESTRICTED) + { + RakNet::BitStream bsIn((unsigned char*) data,len,false); + RakNetGUID senderGuid; + bsIn.IgnoreBytes(sizeof(MessageID)); + bool readSuccess = bsIn.Read(senderGuid); + RakAssert(readSuccess); + if (readSuccess) + { + unsigned int i = GetDetectionAttemptIndex(senderGuid); + if (i!=(unsigned int)-1) + { + bs.Reset(); + bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); + // If different, then symmetric + if (senderAddr!=natDetectionAttempts[i].systemAddress) + { + printf("Determined client is symmetric\n"); + bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); + } + else + { + // else port restricted + printf("Determined client is port restricted\n"); + bs.Write((unsigned char) NAT_TYPE_PORT_RESTRICTED); + } + + rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); + + // Done + natDetectionAttempts.RemoveAtIndexFast(i); + } + else + { + // RakAssert("i==0 in Update when looking up GUID in NatTypeDetectionServer.cpp. Either a bug or a late resend" && 0); + } + } + else + { + // RakAssert("Didn't read GUID in Update in NatTypeDetectionServer.cpp. Message format error" && 0); + } + + len=NatTypeRecvFrom(data, s3p4, senderAddr); + } + + + while (i < (int) natDetectionAttempts.Size()) + { + if (time > natDetectionAttempts[i].nextStateTime) + { + natDetectionAttempts[i].detectionState=(NATDetectionState)((int)natDetectionAttempts[i].detectionState+1); + natDetectionAttempts[i].nextStateTime=time+natDetectionAttempts[i].timeBetweenAttempts; + SystemAddress saOut; + unsigned char c; + bs.Reset(); + switch (natDetectionAttempts[i].detectionState) + { + case STATE_TESTING_NONE_1: + case STATE_TESTING_NONE_2: + c = NAT_TYPE_NONE; + printf("Testing NAT_TYPE_NONE\n"); + // S4P5 sends to C2. If arrived, no NAT. Done. (Else S4P5 potentially banned, do not use again). + saOut=natDetectionAttempts[i].systemAddress; + saOut.SetPort(natDetectionAttempts[i].c2Port); + SocketLayer::SendTo_PC( s4p5, (const char*) &c, 1, saOut, __FILE__, __LINE__ ); + break; + case STATE_TESTING_FULL_CONE_1: + case STATE_TESTING_FULL_CONE_2: + printf("Testing NAT_TYPE_FULL_CONE\n"); + rakPeerInterface->WriteOutOfBandHeader(&bs); + bs.Write((unsigned char) ID_NAT_TYPE_DETECT); + bs.Write((unsigned char) NAT_TYPE_FULL_CONE); + // S2P3 sends to C1 (Different address, different port, to previously used port on client). If received, Full-cone nat. Done. (Else S2P3 potentially banned, do not use again). + saOut=natDetectionAttempts[i].systemAddress; + saOut.SetPort(natDetectionAttempts[i].systemAddress.GetPort()); + SocketLayer::SendTo_PC( s2p3, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); + break; + case STATE_TESTING_ADDRESS_RESTRICTED_1: + case STATE_TESTING_ADDRESS_RESTRICTED_2: + printf("Testing NAT_TYPE_ADDRESS_RESTRICTED\n"); + rakPeerInterface->WriteOutOfBandHeader(&bs); + bs.Write((unsigned char) ID_NAT_TYPE_DETECT); + bs.Write((unsigned char) NAT_TYPE_ADDRESS_RESTRICTED); + // S1P2 sends to C1 (Same address, different port, to previously used port on client). If received, address-restricted cone nat. Done. + saOut=natDetectionAttempts[i].systemAddress; + saOut.SetPort(natDetectionAttempts[i].systemAddress.GetPort()); + SocketLayer::SendTo_PC( s1p2, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); + break; + case STATE_TESTING_PORT_RESTRICTED_1: + case STATE_TESTING_PORT_RESTRICTED_2: + // C1 sends to S3P4. If address of C1 as seen by S3P4 is the same as the address of C1 as seen by S1P1, then port-restricted cone nat. Done + printf("Testing NAT_TYPE_PORT_RESTRICTED\n"); + bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_REQUEST); + bs.Write(RakString::NonVariadic(s3p4Address)); + bs.Write(s3p4Port); + rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); + break; + default: + printf("Warning, exceeded final check STATE_TESTING_PORT_RESTRICTED_2.\nExpected that client would have sent NAT_TYPE_PORT_RESTRICTED on s3p4.\nDefaulting to Symmetric\n"); + bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); + bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); + rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); + natDetectionAttempts.RemoveAtIndexFast(i); + i--; + break; + } + + } + i++; + } +} +PluginReceiveResult NatTypeDetectionServer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_NAT_TYPE_DETECTION_REQUEST: + OnDetectionRequest(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + return RR_CONTINUE_PROCESSING; +} +void NatTypeDetectionServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + unsigned int i = GetDetectionAttemptIndex(systemAddress); + if (i==(unsigned int)-1) + return; + natDetectionAttempts.RemoveAtIndexFast(i); +} +void NatTypeDetectionServer::OnDetectionRequest(Packet *packet) +{ + unsigned int i = GetDetectionAttemptIndex(packet->systemAddress); + + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(1); + bool isRequest=false; + bsIn.Read(isRequest); + if (isRequest) + { + if (i!=(unsigned int)-1) + return; // Already in progress + + NATDetectionAttempt nda; + nda.detectionState=STATE_NONE; + nda.systemAddress=packet->systemAddress; + nda.guid=packet->guid; + bsIn.Read(nda.c2Port); + nda.nextStateTime=0; + nda.timeBetweenAttempts=rakPeerInterface->GetLastPing(nda.systemAddress)*3+50; + natDetectionAttempts.Push(nda, _FILE_AND_LINE_); + } + else + { + if (i==(unsigned int)-1) + return; // Unknown + // They are done + natDetectionAttempts.RemoveAtIndexFast(i); + } + +} +unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(const SystemAddress &sa) +{ + for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) + { + if (natDetectionAttempts[i].systemAddress==sa) + return i; + } + return (unsigned int) -1; +} +unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(RakNetGUID guid) +{ + for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) + { + if (natDetectionAttempts[i].guid==guid) + return i; + } + return (unsigned int) -1; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/NatTypeDetectionServer.h b/src/raknet/NatTypeDetectionServer.h index 093382d..6c72559 100755 --- a/src/raknet/NatTypeDetectionServer.h +++ b/src/raknet/NatTypeDetectionServer.h @@ -1,120 +1,120 @@ -/// \file -/// \brief Contains the NAT-type detection code for the server -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_NatTypeDetectionServer==1 - -#ifndef __NAT_TYPE_DETECTION_SERVER_H -#define __NAT_TYPE_DETECTION_SERVER_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "SocketIncludes.h" -#include "DS_OrderedList.h" -#include "RakString.h" -#include "NatTypeDetectionCommon.h" - - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -struct Packet; - -/// \brief Server code for NatTypeDetection -/// \details -/// Sends to a remote system on certain ports and addresses to determine what type of router, if any, that client is behind -/// Requires that the server have 4 external IP addresses -///
        -///
      1. Server has 1 instance of RakNet. Server has four external ip addresses S1 to S4. Five ports are used in total P1 to P5. RakNet is bound to S1P1. Sockets are bound to S1P2, S2P3, S3P4, S4P5 -///
      2. Client with one port using RakNet (C1). Another port not using anything (C2). -///
      3. C1 connects to S1P1 for normal communication. -///
      4. S4P5 sends to C2. If arrived, no NAT. Done. (If didn't arrive, S4P5 potentially banned, do not use again). -///
      5. S2P3 sends to C1 (Different address, different port, to previously used port on client). If received, Full-cone nat. Done. (If didn't arrive, S2P3 potentially banned, do not use again). -///
      6. S1P2 sends to C1 (Same address, different port, to previously used port on client). If received, address-restricted cone nat. Done. -///
      7. Server via RakNet connection tells C1 to send to to S3P4. If address of C1 as seen by S3P4 is the same as the address of C1 as seen by S1P1 (RakNet connection), then port-restricted cone nat. Done -///
      8. Else symmetric nat. Done. -///
      -/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html -/// \sa NatPunchthroughServer -/// \sa NatTypeDetectionClient -/// \ingroup NAT_TYPE_DETECTION_GROUP -class RAK_DLL_EXPORT NatTypeDetectionServer : public PluginInterface2 -{ -public: - - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(NatTypeDetectionServer) - - // Constructor - NatTypeDetectionServer(); - - // Destructor - virtual ~NatTypeDetectionServer(); - - /// Start the system, binding to 3 external IPs not already in useS - /// \param[in] nonRakNetIP2 First unused external IP - /// \param[in] nonRakNetIP3 Second unused external IP - /// \param[in] nonRakNetIP4 Third unused external IP - void Startup( - const char *nonRakNetIP2, - const char *nonRakNetIP3, - const char *nonRakNetIP4); - - // Releases the sockets created in Startup(); - void Shutdown(void); - - /// \internal For plugin handling - virtual void Update(void); - - /// \internal For plugin handling - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - enum NATDetectionState - { - STATE_NONE, - STATE_TESTING_NONE_1, - STATE_TESTING_NONE_2, - STATE_TESTING_FULL_CONE_1, - STATE_TESTING_FULL_CONE_2, - STATE_TESTING_ADDRESS_RESTRICTED_1, - STATE_TESTING_ADDRESS_RESTRICTED_2, - STATE_TESTING_PORT_RESTRICTED_1, - STATE_TESTING_PORT_RESTRICTED_2, - STATE_DONE, - }; - - struct NATDetectionAttempt - { - SystemAddress systemAddress; - NATDetectionState detectionState; - RakNet::TimeMS nextStateTime; - RakNet::TimeMS timeBetweenAttempts; - unsigned short c2Port; - RakNetGUID guid; - }; - -protected: - void OnDetectionRequest(Packet *packet); - DataStructures::List natDetectionAttempts; - unsigned int GetDetectionAttemptIndex(const SystemAddress &sa); - unsigned int GetDetectionAttemptIndex(RakNetGUID guid); - - // s1p1 is rakpeer itself - SOCKET s1p2,s2p3,s3p4,s4p5; - unsigned short s1p2Port, s2p3Port, s3p4Port, s4p5Port; - char s3p4Address[64]; -}; -} - - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains the NAT-type detection code for the server +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_NatTypeDetectionServer==1 + +#ifndef __NAT_TYPE_DETECTION_SERVER_H +#define __NAT_TYPE_DETECTION_SERVER_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "SocketIncludes.h" +#include "DS_OrderedList.h" +#include "RakString.h" +#include "NatTypeDetectionCommon.h" + + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +struct Packet; + +/// \brief Server code for NatTypeDetection +/// \details +/// Sends to a remote system on certain ports and addresses to determine what type of router, if any, that client is behind +/// Requires that the server have 4 external IP addresses +///
        +///
      1. Server has 1 instance of RakNet. Server has four external ip addresses S1 to S4. Five ports are used in total P1 to P5. RakNet is bound to S1P1. Sockets are bound to S1P2, S2P3, S3P4, S4P5 +///
      2. Client with one port using RakNet (C1). Another port not using anything (C2). +///
      3. C1 connects to S1P1 for normal communication. +///
      4. S4P5 sends to C2. If arrived, no NAT. Done. (If didn't arrive, S4P5 potentially banned, do not use again). +///
      5. S2P3 sends to C1 (Different address, different port, to previously used port on client). If received, Full-cone nat. Done. (If didn't arrive, S2P3 potentially banned, do not use again). +///
      6. S1P2 sends to C1 (Same address, different port, to previously used port on client). If received, address-restricted cone nat. Done. +///
      7. Server via RakNet connection tells C1 to send to to S3P4. If address of C1 as seen by S3P4 is the same as the address of C1 as seen by S1P1 (RakNet connection), then port-restricted cone nat. Done +///
      8. Else symmetric nat. Done. +///
      +/// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html +/// \sa NatPunchthroughServer +/// \sa NatTypeDetectionClient +/// \ingroup NAT_TYPE_DETECTION_GROUP +class RAK_DLL_EXPORT NatTypeDetectionServer : public PluginInterface2 +{ +public: + + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(NatTypeDetectionServer) + + // Constructor + NatTypeDetectionServer(); + + // Destructor + virtual ~NatTypeDetectionServer(); + + /// Start the system, binding to 3 external IPs not already in useS + /// \param[in] nonRakNetIP2 First unused external IP + /// \param[in] nonRakNetIP3 Second unused external IP + /// \param[in] nonRakNetIP4 Third unused external IP + void Startup( + const char *nonRakNetIP2, + const char *nonRakNetIP3, + const char *nonRakNetIP4); + + // Releases the sockets created in Startup(); + void Shutdown(void); + + /// \internal For plugin handling + virtual void Update(void); + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + enum NATDetectionState + { + STATE_NONE, + STATE_TESTING_NONE_1, + STATE_TESTING_NONE_2, + STATE_TESTING_FULL_CONE_1, + STATE_TESTING_FULL_CONE_2, + STATE_TESTING_ADDRESS_RESTRICTED_1, + STATE_TESTING_ADDRESS_RESTRICTED_2, + STATE_TESTING_PORT_RESTRICTED_1, + STATE_TESTING_PORT_RESTRICTED_2, + STATE_DONE, + }; + + struct NATDetectionAttempt + { + SystemAddress systemAddress; + NATDetectionState detectionState; + RakNet::TimeMS nextStateTime; + RakNet::TimeMS timeBetweenAttempts; + unsigned short c2Port; + RakNetGUID guid; + }; + +protected: + void OnDetectionRequest(Packet *packet); + DataStructures::List natDetectionAttempts; + unsigned int GetDetectionAttemptIndex(const SystemAddress &sa); + unsigned int GetDetectionAttemptIndex(RakNetGUID guid); + + // s1p1 is rakpeer itself + SOCKET s1p2,s2p3,s3p4,s4p5; + unsigned short s1p2Port, s2p3Port, s3p4Port, s4p5Port; + char s3p4Address[64]; +}; +} + + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/NativeFeatureIncludes.h b/src/raknet/NativeFeatureIncludes.h index 1a8df4d..5c59be3 100755 --- a/src/raknet/NativeFeatureIncludes.h +++ b/src/raknet/NativeFeatureIncludes.h @@ -1,175 +1,175 @@ -// If you want to change these defines, put them in NativeFeatureIncludesOverrides so your changes are not lost when updating RakNet -// The user should not edit this file -#include "NativeFeatureIncludesOverrides.h" - -#ifndef __NATIVE_FEATURE_INCLDUES_H -#define __NATIVE_FEATURE_INCLDUES_H - -// Uncomment below defines, and paste to NativeFeatureIncludesOverrides.h, to exclude plugins that you do not want to build into the static library, or DLL -// These are not all the plugins, only those that are in the core library -// Other plugins are located in DependentExtensions -// #define _RAKNET_SUPPORT_ConnectionGraph2 0 -// #define _RAKNET_SUPPORT_DirectoryDeltaTransfer 0 -// #define _RAKNET_SUPPORT_FileListTransfer 0 -// #define _RAKNET_SUPPORT_FullyConnectedMesh2 0 -// #define _RAKNET_SUPPORT_MessageFilter 0 -// #define _RAKNET_SUPPORT_NatPunchthroughClient 0 -// #define _RAKNET_SUPPORT_NatPunchthroughServer 0 -// #define _RAKNET_SUPPORT_NatTypeDetectionClient 0 -// #define _RAKNET_SUPPORT_NatTypeDetectionServer 0 -// #define _RAKNET_SUPPORT_PacketLogger 0 -// #define _RAKNET_SUPPORT_ReadyEvent 0 -// #define _RAKNET_SUPPORT_ReplicaManager3 0 -// #define _RAKNET_SUPPORT_Router2 0 -// #define _RAKNET_SUPPORT_RPC4Plugin 0 -// #define _RAKNET_SUPPORT_TeamBalancer 0 -// #define _RAKNET_SUPPORT_UDPProxyClient 0 -// #define _RAKNET_SUPPORT_UDPProxyCoordinator 0 -// #define _RAKNET_SUPPORT_UDPProxyServer 0 -// #define _RAKNET_SUPPORT_ConsoleServer 0 -// #define _RAKNET_SUPPORT_RakNetTransport 0 -// #define _RAKNET_SUPPORT_TelnetTransport 0 -// #define _RAKNET_SUPPORT_TCPInterface 0 -// #define _RAKNET_SUPPORT_LogCommandParser 0 -// #define _RAKNET_SUPPORT_RakNetCommandParser 0 -// #define _RAKNET_SUPPORT_EmailSender 0 -// #define _RAKNET_SUPPORT_HTTPConnection 0 -// #define _RAKNET_SUPPORT_PacketizedTCP 0 -// #define _RAKNET_SUPPORT_TwoWayAuthentication 0 - -// SET DEFAULTS IF UNDEFINED -#ifndef LIBCAT_SECURITY -#define LIBCAT_SECURITY 0 -#endif -#ifndef _RAKNET_SUPPORT_ConnectionGraph2 -#define _RAKNET_SUPPORT_ConnectionGraph2 1 -#endif -#ifndef _RAKNET_SUPPORT_DirectoryDeltaTransfer -#define _RAKNET_SUPPORT_DirectoryDeltaTransfer 1 -#endif -#ifndef _RAKNET_SUPPORT_FileListTransfer -#define _RAKNET_SUPPORT_FileListTransfer 1 -#endif -#ifndef _RAKNET_SUPPORT_FullyConnectedMesh -#define _RAKNET_SUPPORT_FullyConnectedMesh 1 -#endif -#ifndef _RAKNET_SUPPORT_FullyConnectedMesh2 -#define _RAKNET_SUPPORT_FullyConnectedMesh2 1 -#endif -#ifndef _RAKNET_SUPPORT_MessageFilter -#define _RAKNET_SUPPORT_MessageFilter 1 -#endif -#ifndef _RAKNET_SUPPORT_NatPunchthroughClient -#define _RAKNET_SUPPORT_NatPunchthroughClient 1 -#endif -#ifndef _RAKNET_SUPPORT_NatPunchthroughServer -#define _RAKNET_SUPPORT_NatPunchthroughServer 1 -#endif -#ifndef _RAKNET_SUPPORT_NatTypeDetectionClient -#define _RAKNET_SUPPORT_NatTypeDetectionClient 1 -#endif -#ifndef _RAKNET_SUPPORT_NatTypeDetectionServer -#define _RAKNET_SUPPORT_NatTypeDetectionServer 1 -#endif -#ifndef _RAKNET_SUPPORT_PacketLogger -#define _RAKNET_SUPPORT_PacketLogger 1 -#endif -#ifndef _RAKNET_SUPPORT_ReadyEvent -#define _RAKNET_SUPPORT_ReadyEvent 1 -#endif -#ifndef _RAKNET_SUPPORT_ReplicaManager3 -#define _RAKNET_SUPPORT_ReplicaManager3 1 -#endif -#ifndef _RAKNET_SUPPORT_Router2 -#define _RAKNET_SUPPORT_Router2 1 -#endif -#ifndef _RAKNET_SUPPORT_RPC4Plugin -#define _RAKNET_SUPPORT_RPC4Plugin 1 -#endif -#ifndef _RAKNET_SUPPORT_TeamBalancer -#define _RAKNET_SUPPORT_TeamBalancer 1 -#endif -#ifndef _RAKNET_SUPPORT_UDPProxyClient -#define _RAKNET_SUPPORT_UDPProxyClient 1 -#endif -#ifndef _RAKNET_SUPPORT_UDPProxyCoordinator -#define _RAKNET_SUPPORT_UDPProxyCoordinator 1 -#endif -#ifndef _RAKNET_SUPPORT_UDPProxyServer -#define _RAKNET_SUPPORT_UDPProxyServer 1 -#endif -#ifndef _RAKNET_SUPPORT_ConsoleServer -#define _RAKNET_SUPPORT_ConsoleServer 1 -#endif -#ifndef _RAKNET_SUPPORT_RakNetTransport -#define _RAKNET_SUPPORT_RakNetTransport 1 -#endif -#ifndef _RAKNET_SUPPORT_TelnetTransport -#define _RAKNET_SUPPORT_TelnetTransport 1 -#endif -#ifndef _RAKNET_SUPPORT_TCPInterface -#define _RAKNET_SUPPORT_TCPInterface 1 -#endif -#ifndef _RAKNET_SUPPORT_LogCommandParser -#define _RAKNET_SUPPORT_LogCommandParser 1 -#endif -#ifndef _RAKNET_SUPPORT_RakNetCommandParser -#define _RAKNET_SUPPORT_RakNetCommandParser 1 -#endif -#ifndef _RAKNET_SUPPORT_EmailSender -#define _RAKNET_SUPPORT_EmailSender 1 -#endif -#ifndef _RAKNET_SUPPORT_HTTPConnection -#define _RAKNET_SUPPORT_HTTPConnection 1 -#endif -#ifndef _RAKNET_SUPPORT_PacketizedTCP -#define _RAKNET_SUPPORT_PacketizedTCP 1 -#endif -#ifndef _RAKNET_SUPPORT_TwoWayAuthentication -#define _RAKNET_SUPPORT_TwoWayAuthentication 1 -#endif -#ifndef _RAKNET_SUPPORT_CloudClient -#define _RAKNET_SUPPORT_CloudClient 1 -#endif -#ifndef _RAKNET_SUPPORT_CloudServer -#define _RAKNET_SUPPORT_CloudServer 1 -#endif -#ifndef _RAKNET_SUPPORT_DynDNS -#define _RAKNET_SUPPORT_DynDNS 1 -#endif -#ifndef _RAKNET_SUPPORT_Rackspace -#define _RAKNET_SUPPORT_Rackspace 1 -#endif -#ifndef _RAKNET_SUPPORT_FileOperations -#define _RAKNET_SUPPORT_FileOperations 1 -#endif -#ifndef _RAKNET_SUPPORT_UDPForwarder -#define _RAKNET_SUPPORT_UDPForwarder 1 -#endif - -// Take care of dependencies - -#undef _RAKNET_SUPPORT_FileListTransfer -#define _RAKNET_SUPPORT_FileListTransfer 1 - - -#undef _RAKNET_SUPPORT_ConnectionGraph2 -#define _RAKNET_SUPPORT_ConnectionGraph2 1 - - -#undef _RAKNET_SUPPORT_PacketizedTCP -#define _RAKNET_SUPPORT_PacketizedTCP 1 - - -#undef _RAKNET_SUPPORT_TCPInterface -#define _RAKNET_SUPPORT_TCPInterface 1 - - - - - - - - - -#endif // __NATIVE_FEATURE_INCLDUES_H +// If you want to change these defines, put them in NativeFeatureIncludesOverrides so your changes are not lost when updating RakNet +// The user should not edit this file +#include "NativeFeatureIncludesOverrides.h" + +#ifndef __NATIVE_FEATURE_INCLDUES_H +#define __NATIVE_FEATURE_INCLDUES_H + +// Uncomment below defines, and paste to NativeFeatureIncludesOverrides.h, to exclude plugins that you do not want to build into the static library, or DLL +// These are not all the plugins, only those that are in the core library +// Other plugins are located in DependentExtensions +// #define _RAKNET_SUPPORT_ConnectionGraph2 0 +// #define _RAKNET_SUPPORT_DirectoryDeltaTransfer 0 +// #define _RAKNET_SUPPORT_FileListTransfer 0 +// #define _RAKNET_SUPPORT_FullyConnectedMesh2 0 +// #define _RAKNET_SUPPORT_MessageFilter 0 +// #define _RAKNET_SUPPORT_NatPunchthroughClient 0 +// #define _RAKNET_SUPPORT_NatPunchthroughServer 0 +// #define _RAKNET_SUPPORT_NatTypeDetectionClient 0 +// #define _RAKNET_SUPPORT_NatTypeDetectionServer 0 +// #define _RAKNET_SUPPORT_PacketLogger 0 +// #define _RAKNET_SUPPORT_ReadyEvent 0 +// #define _RAKNET_SUPPORT_ReplicaManager3 0 +// #define _RAKNET_SUPPORT_Router2 0 +// #define _RAKNET_SUPPORT_RPC4Plugin 0 +// #define _RAKNET_SUPPORT_TeamBalancer 0 +// #define _RAKNET_SUPPORT_UDPProxyClient 0 +// #define _RAKNET_SUPPORT_UDPProxyCoordinator 0 +// #define _RAKNET_SUPPORT_UDPProxyServer 0 +// #define _RAKNET_SUPPORT_ConsoleServer 0 +// #define _RAKNET_SUPPORT_RakNetTransport 0 +// #define _RAKNET_SUPPORT_TelnetTransport 0 +// #define _RAKNET_SUPPORT_TCPInterface 0 +// #define _RAKNET_SUPPORT_LogCommandParser 0 +// #define _RAKNET_SUPPORT_RakNetCommandParser 0 +// #define _RAKNET_SUPPORT_EmailSender 0 +// #define _RAKNET_SUPPORT_HTTPConnection 0 +// #define _RAKNET_SUPPORT_PacketizedTCP 0 +// #define _RAKNET_SUPPORT_TwoWayAuthentication 0 + +// SET DEFAULTS IF UNDEFINED +#ifndef LIBCAT_SECURITY +#define LIBCAT_SECURITY 0 +#endif +#ifndef _RAKNET_SUPPORT_ConnectionGraph2 +#define _RAKNET_SUPPORT_ConnectionGraph2 1 +#endif +#ifndef _RAKNET_SUPPORT_DirectoryDeltaTransfer +#define _RAKNET_SUPPORT_DirectoryDeltaTransfer 1 +#endif +#ifndef _RAKNET_SUPPORT_FileListTransfer +#define _RAKNET_SUPPORT_FileListTransfer 1 +#endif +#ifndef _RAKNET_SUPPORT_FullyConnectedMesh +#define _RAKNET_SUPPORT_FullyConnectedMesh 1 +#endif +#ifndef _RAKNET_SUPPORT_FullyConnectedMesh2 +#define _RAKNET_SUPPORT_FullyConnectedMesh2 1 +#endif +#ifndef _RAKNET_SUPPORT_MessageFilter +#define _RAKNET_SUPPORT_MessageFilter 1 +#endif +#ifndef _RAKNET_SUPPORT_NatPunchthroughClient +#define _RAKNET_SUPPORT_NatPunchthroughClient 1 +#endif +#ifndef _RAKNET_SUPPORT_NatPunchthroughServer +#define _RAKNET_SUPPORT_NatPunchthroughServer 1 +#endif +#ifndef _RAKNET_SUPPORT_NatTypeDetectionClient +#define _RAKNET_SUPPORT_NatTypeDetectionClient 1 +#endif +#ifndef _RAKNET_SUPPORT_NatTypeDetectionServer +#define _RAKNET_SUPPORT_NatTypeDetectionServer 1 +#endif +#ifndef _RAKNET_SUPPORT_PacketLogger +#define _RAKNET_SUPPORT_PacketLogger 1 +#endif +#ifndef _RAKNET_SUPPORT_ReadyEvent +#define _RAKNET_SUPPORT_ReadyEvent 1 +#endif +#ifndef _RAKNET_SUPPORT_ReplicaManager3 +#define _RAKNET_SUPPORT_ReplicaManager3 1 +#endif +#ifndef _RAKNET_SUPPORT_Router2 +#define _RAKNET_SUPPORT_Router2 1 +#endif +#ifndef _RAKNET_SUPPORT_RPC4Plugin +#define _RAKNET_SUPPORT_RPC4Plugin 1 +#endif +#ifndef _RAKNET_SUPPORT_TeamBalancer +#define _RAKNET_SUPPORT_TeamBalancer 1 +#endif +#ifndef _RAKNET_SUPPORT_UDPProxyClient +#define _RAKNET_SUPPORT_UDPProxyClient 1 +#endif +#ifndef _RAKNET_SUPPORT_UDPProxyCoordinator +#define _RAKNET_SUPPORT_UDPProxyCoordinator 1 +#endif +#ifndef _RAKNET_SUPPORT_UDPProxyServer +#define _RAKNET_SUPPORT_UDPProxyServer 1 +#endif +#ifndef _RAKNET_SUPPORT_ConsoleServer +#define _RAKNET_SUPPORT_ConsoleServer 1 +#endif +#ifndef _RAKNET_SUPPORT_RakNetTransport +#define _RAKNET_SUPPORT_RakNetTransport 1 +#endif +#ifndef _RAKNET_SUPPORT_TelnetTransport +#define _RAKNET_SUPPORT_TelnetTransport 1 +#endif +#ifndef _RAKNET_SUPPORT_TCPInterface +#define _RAKNET_SUPPORT_TCPInterface 1 +#endif +#ifndef _RAKNET_SUPPORT_LogCommandParser +#define _RAKNET_SUPPORT_LogCommandParser 1 +#endif +#ifndef _RAKNET_SUPPORT_RakNetCommandParser +#define _RAKNET_SUPPORT_RakNetCommandParser 1 +#endif +#ifndef _RAKNET_SUPPORT_EmailSender +#define _RAKNET_SUPPORT_EmailSender 1 +#endif +#ifndef _RAKNET_SUPPORT_HTTPConnection +#define _RAKNET_SUPPORT_HTTPConnection 1 +#endif +#ifndef _RAKNET_SUPPORT_PacketizedTCP +#define _RAKNET_SUPPORT_PacketizedTCP 1 +#endif +#ifndef _RAKNET_SUPPORT_TwoWayAuthentication +#define _RAKNET_SUPPORT_TwoWayAuthentication 1 +#endif +#ifndef _RAKNET_SUPPORT_CloudClient +#define _RAKNET_SUPPORT_CloudClient 1 +#endif +#ifndef _RAKNET_SUPPORT_CloudServer +#define _RAKNET_SUPPORT_CloudServer 1 +#endif +#ifndef _RAKNET_SUPPORT_DynDNS +#define _RAKNET_SUPPORT_DynDNS 1 +#endif +#ifndef _RAKNET_SUPPORT_Rackspace +#define _RAKNET_SUPPORT_Rackspace 1 +#endif +#ifndef _RAKNET_SUPPORT_FileOperations +#define _RAKNET_SUPPORT_FileOperations 1 +#endif +#ifndef _RAKNET_SUPPORT_UDPForwarder +#define _RAKNET_SUPPORT_UDPForwarder 1 +#endif + +// Take care of dependencies + +#undef _RAKNET_SUPPORT_FileListTransfer +#define _RAKNET_SUPPORT_FileListTransfer 1 + + +#undef _RAKNET_SUPPORT_ConnectionGraph2 +#define _RAKNET_SUPPORT_ConnectionGraph2 1 + + +#undef _RAKNET_SUPPORT_PacketizedTCP +#define _RAKNET_SUPPORT_PacketizedTCP 1 + + +#undef _RAKNET_SUPPORT_TCPInterface +#define _RAKNET_SUPPORT_TCPInterface 1 + + + + + + + + + +#endif // __NATIVE_FEATURE_INCLDUES_H diff --git a/src/raknet/NativeFeatureIncludesOverrides.h b/src/raknet/NativeFeatureIncludesOverrides.h index 820f7ab..d76ef48 100755 --- a/src/raknet/NativeFeatureIncludesOverrides.h +++ b/src/raknet/NativeFeatureIncludesOverrides.h @@ -1,9 +1,9 @@ -// USER EDITABLE FILE -// See NativeFeatureIncludes.h - -#ifndef __NATIVE_FEATURE_INCLDUES_OVERRIDES_H -#define __NATIVE_FEATURE_INCLDUES_OVERRIDES_H - -//#define LIBCAT_SECURITY 1 - -#endif +// USER EDITABLE FILE +// See NativeFeatureIncludes.h + +#ifndef __NATIVE_FEATURE_INCLDUES_OVERRIDES_H +#define __NATIVE_FEATURE_INCLDUES_OVERRIDES_H + +//#define LIBCAT_SECURITY 1 + +#endif diff --git a/src/raknet/NativeTypes.h b/src/raknet/NativeTypes.h index f1ee4cf..bf3c5bb 100755 --- a/src/raknet/NativeTypes.h +++ b/src/raknet/NativeTypes.h @@ -1,23 +1,23 @@ -#ifndef __NATIVE_TYPES_H -#define __NATIVE_TYPES_H - -#if defined(__GNUC__) || defined(__GCCXML__) || defined(__SNC__) || defined(__S3E__) -#include -#elif !defined(_STDINT_H) && !defined(_SN_STDINT_H) && !defined(_SYS_STDINT_H_) && !defined(_STDINT) && !defined(_MACHTYPES_H_) && !defined(_STDINT_H_) - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned __int32 uint32_t; - typedef signed char int8_t; - typedef signed short int16_t; - typedef __int32 int32_t; - #if defined(_MSC_VER) && _MSC_VER < 1300 - typedef unsigned __int64 uint64_t; - typedef signed __int64 int64_t; - #else - typedef unsigned long long int uint64_t; - typedef signed long long int64_t; - #endif -#endif - - -#endif +#ifndef __NATIVE_TYPES_H +#define __NATIVE_TYPES_H + +#if defined(__GNUC__) || defined(__GCCXML__) || defined(__SNC__) || defined(__S3E__) +#include +#elif !defined(_STDINT_H) && !defined(_SN_STDINT_H) && !defined(_SYS_STDINT_H_) && !defined(_STDINT) && !defined(_MACHTYPES_H_) && !defined(_STDINT_H_) + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned __int32 uint32_t; + typedef signed char int8_t; + typedef signed short int16_t; + typedef __int32 int32_t; + #if defined(_MSC_VER) && _MSC_VER < 1300 + typedef unsigned __int64 uint64_t; + typedef signed __int64 int64_t; + #else + typedef unsigned long long int uint64_t; + typedef signed long long int64_t; + #endif +#endif + + +#endif diff --git a/src/raknet/NetworkIDManager.cpp b/src/raknet/NetworkIDManager.cpp index 35f429a..ed82ee2 100755 --- a/src/raknet/NetworkIDManager.cpp +++ b/src/raknet/NetworkIDManager.cpp @@ -1,108 +1,108 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NetworkIDManager.h" -#include "NetworkIDObject.h" -#include "RakAssert.h" -#include "GetTime.h" -#include "RakSleep.h" -#include "SuperFastHash.h" -#include "RakPeerInterface.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(NetworkIDManager,NetworkIDManager) - -NetworkIDManager::NetworkIDManager() -{ - startingOffset = RakPeerInterface::Get64BitUniqueRandomNumber(); - memset(networkIdHash,0,sizeof(networkIdHash)); -} -NetworkIDManager::~NetworkIDManager(void) -{ - -} -NetworkIDObject *NetworkIDManager::GET_BASE_OBJECT_FROM_ID(NetworkID x) -{ - unsigned int hashIndex=NetworkIDToHashIndex(x); - NetworkIDObject *nio=networkIdHash[hashIndex]; - while (nio) - { - if (nio->GetNetworkID()==x) - return nio; - nio=nio->nextInstanceForNetworkIDManager; - } - return 0; -} -NetworkID NetworkIDManager::GetNewNetworkID(void) -{ - while (GET_BASE_OBJECT_FROM_ID(++startingOffset)) - ; - return startingOffset; -} -unsigned int NetworkIDManager::NetworkIDToHashIndex(NetworkID networkId) -{ -// return SuperFastHash((const char*) &networkId.guid.g,sizeof(networkId.guid.g)) % NETWORK_ID_MANAGER_HASH_LENGTH; - return (unsigned int) (networkId % NETWORK_ID_MANAGER_HASH_LENGTH); -} -void NetworkIDManager::TrackNetworkIDObject(NetworkIDObject *networkIdObject) -{ - RakAssert(networkIdObject->GetNetworkID()!=UNASSIGNED_NETWORK_ID); - unsigned int hashIndex=NetworkIDToHashIndex(networkIdObject->GetNetworkID()); -// printf("TrackNetworkIDObject hashIndex=%i guid=%s\n",hashIndex, networkIdObject->GetNetworkID().guid.ToString()); // removeme - if (networkIdHash[hashIndex]==0) - { - networkIdHash[hashIndex]=networkIdObject; - return; - } - NetworkIDObject *nio=networkIdHash[hashIndex]; - // Duplicate insertion? - RakAssert(nio!=networkIdObject); - // Random GUID conflict? - RakAssert(nio->GetNetworkID()!=networkIdObject->GetNetworkID()); - - while (nio->nextInstanceForNetworkIDManager!=0) - { - nio=nio->nextInstanceForNetworkIDManager; - - // Duplicate insertion? - RakAssert(nio!=networkIdObject); - // Random GUID conflict? - RakAssert(nio->GetNetworkID()!=networkIdObject->GetNetworkID()); - } - - networkIdObject->nextInstanceForNetworkIDManager=0; - nio->nextInstanceForNetworkIDManager=networkIdObject; -} -void NetworkIDManager::StopTrackingNetworkIDObject(NetworkIDObject *networkIdObject) -{ - RakAssert(networkIdObject->GetNetworkID()!=UNASSIGNED_NETWORK_ID); - unsigned int hashIndex=NetworkIDToHashIndex(networkIdObject->GetNetworkID()); -// printf("hashIndex=%i\n",hashIndex); // removeme - NetworkIDObject *nio=networkIdHash[hashIndex]; - if (nio==0) - { - RakAssert("NetworkIDManager::StopTrackingNetworkIDObject didn't find object" && 0); - return; - } - if (nio==networkIdObject) - { - networkIdHash[hashIndex]=nio->nextInstanceForNetworkIDManager; - return; - } - - while (nio) - { - if (nio->nextInstanceForNetworkIDManager==networkIdObject) - { - nio->nextInstanceForNetworkIDManager=networkIdObject->nextInstanceForNetworkIDManager; - return; - } - nio=nio->nextInstanceForNetworkIDManager; - } - - RakAssert("NetworkIDManager::StopTrackingNetworkIDObject didn't find object" && 0); -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NetworkIDManager.h" +#include "NetworkIDObject.h" +#include "RakAssert.h" +#include "GetTime.h" +#include "RakSleep.h" +#include "SuperFastHash.h" +#include "RakPeerInterface.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(NetworkIDManager,NetworkIDManager) + +NetworkIDManager::NetworkIDManager() +{ + startingOffset = RakPeerInterface::Get64BitUniqueRandomNumber(); + memset(networkIdHash,0,sizeof(networkIdHash)); +} +NetworkIDManager::~NetworkIDManager(void) +{ + +} +NetworkIDObject *NetworkIDManager::GET_BASE_OBJECT_FROM_ID(NetworkID x) +{ + unsigned int hashIndex=NetworkIDToHashIndex(x); + NetworkIDObject *nio=networkIdHash[hashIndex]; + while (nio) + { + if (nio->GetNetworkID()==x) + return nio; + nio=nio->nextInstanceForNetworkIDManager; + } + return 0; +} +NetworkID NetworkIDManager::GetNewNetworkID(void) +{ + while (GET_BASE_OBJECT_FROM_ID(++startingOffset)) + ; + return startingOffset; +} +unsigned int NetworkIDManager::NetworkIDToHashIndex(NetworkID networkId) +{ +// return SuperFastHash((const char*) &networkId.guid.g,sizeof(networkId.guid.g)) % NETWORK_ID_MANAGER_HASH_LENGTH; + return (unsigned int) (networkId % NETWORK_ID_MANAGER_HASH_LENGTH); +} +void NetworkIDManager::TrackNetworkIDObject(NetworkIDObject *networkIdObject) +{ + RakAssert(networkIdObject->GetNetworkID()!=UNASSIGNED_NETWORK_ID); + unsigned int hashIndex=NetworkIDToHashIndex(networkIdObject->GetNetworkID()); +// printf("TrackNetworkIDObject hashIndex=%i guid=%s\n",hashIndex, networkIdObject->GetNetworkID().guid.ToString()); // removeme + if (networkIdHash[hashIndex]==0) + { + networkIdHash[hashIndex]=networkIdObject; + return; + } + NetworkIDObject *nio=networkIdHash[hashIndex]; + // Duplicate insertion? + RakAssert(nio!=networkIdObject); + // Random GUID conflict? + RakAssert(nio->GetNetworkID()!=networkIdObject->GetNetworkID()); + + while (nio->nextInstanceForNetworkIDManager!=0) + { + nio=nio->nextInstanceForNetworkIDManager; + + // Duplicate insertion? + RakAssert(nio!=networkIdObject); + // Random GUID conflict? + RakAssert(nio->GetNetworkID()!=networkIdObject->GetNetworkID()); + } + + networkIdObject->nextInstanceForNetworkIDManager=0; + nio->nextInstanceForNetworkIDManager=networkIdObject; +} +void NetworkIDManager::StopTrackingNetworkIDObject(NetworkIDObject *networkIdObject) +{ + RakAssert(networkIdObject->GetNetworkID()!=UNASSIGNED_NETWORK_ID); + unsigned int hashIndex=NetworkIDToHashIndex(networkIdObject->GetNetworkID()); +// printf("hashIndex=%i\n",hashIndex); // removeme + NetworkIDObject *nio=networkIdHash[hashIndex]; + if (nio==0) + { + RakAssert("NetworkIDManager::StopTrackingNetworkIDObject didn't find object" && 0); + return; + } + if (nio==networkIdObject) + { + networkIdHash[hashIndex]=nio->nextInstanceForNetworkIDManager; + return; + } + + while (nio) + { + if (nio->nextInstanceForNetworkIDManager==networkIdObject) + { + nio->nextInstanceForNetworkIDManager=networkIdObject->nextInstanceForNetworkIDManager; + return; + } + nio=nio->nextInstanceForNetworkIDManager; + } + + RakAssert("NetworkIDManager::StopTrackingNetworkIDObject didn't find object" && 0); +} diff --git a/src/raknet/NetworkIDManager.h b/src/raknet/NetworkIDManager.h index 0afd057..ae009be 100755 --- a/src/raknet/NetworkIDManager.h +++ b/src/raknet/NetworkIDManager.h @@ -1,70 +1,70 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __NETWORK_ID_MANAGER_H -#define __NETWORK_ID_MANAGER_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "RakMemoryOverride.h" -#include "NetworkIDObject.h" -#include "Rand.h" - -namespace RakNet -{ - -/// Increase this value if you plan to have many persistent objects -/// This value must match on all systems -#define NETWORK_ID_MANAGER_HASH_LENGTH 1024 - -/// This class is simply used to generate a unique number for a group of instances of NetworkIDObject -/// An instance of this class is required to use the ObjectID to pointer lookup system -/// You should have one instance of this class per game instance. -/// Call SetIsNetworkIDAuthority before using any functions of this class, or of NetworkIDObject -class RAK_DLL_EXPORT NetworkIDManager -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(NetworkIDManager) - - NetworkIDManager(); - virtual ~NetworkIDManager(void); - - /// Returns the parent object, or this instance if you don't use a parent. - /// Supports NetworkIDObject anywhere in the inheritance hierarchy - /// \pre You must first call SetNetworkIDManager before using this function - template - returnType GET_OBJECT_FROM_ID(NetworkID x) { - NetworkIDObject *nio = GET_BASE_OBJECT_FROM_ID(x); - if (nio==0) - return 0; - if (nio->GetParent()) - return (returnType) nio->GetParent(); - return (returnType) nio; - } - - /// \internal - NetworkIDObject *GET_BASE_OBJECT_FROM_ID(NetworkID x); - - /// \internal - void TrackNetworkIDObject(NetworkIDObject *networkIdObject); - void StopTrackingNetworkIDObject(NetworkIDObject *networkIdObject); - -protected: - friend class NetworkIDObject; - - NetworkIDObject *networkIdHash[NETWORK_ID_MANAGER_HASH_LENGTH]; - unsigned int NetworkIDToHashIndex(NetworkID networkId); - uint64_t startingOffset; - /// \internal - NetworkID GetNewNetworkID(void); - -}; - -} // namespace RakNet - -#endif +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __NETWORK_ID_MANAGER_H +#define __NETWORK_ID_MANAGER_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "RakMemoryOverride.h" +#include "NetworkIDObject.h" +#include "Rand.h" + +namespace RakNet +{ + +/// Increase this value if you plan to have many persistent objects +/// This value must match on all systems +#define NETWORK_ID_MANAGER_HASH_LENGTH 1024 + +/// This class is simply used to generate a unique number for a group of instances of NetworkIDObject +/// An instance of this class is required to use the ObjectID to pointer lookup system +/// You should have one instance of this class per game instance. +/// Call SetIsNetworkIDAuthority before using any functions of this class, or of NetworkIDObject +class RAK_DLL_EXPORT NetworkIDManager +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(NetworkIDManager) + + NetworkIDManager(); + virtual ~NetworkIDManager(void); + + /// Returns the parent object, or this instance if you don't use a parent. + /// Supports NetworkIDObject anywhere in the inheritance hierarchy + /// \pre You must first call SetNetworkIDManager before using this function + template + returnType GET_OBJECT_FROM_ID(NetworkID x) { + NetworkIDObject *nio = GET_BASE_OBJECT_FROM_ID(x); + if (nio==0) + return 0; + if (nio->GetParent()) + return (returnType) nio->GetParent(); + return (returnType) nio; + } + + /// \internal + NetworkIDObject *GET_BASE_OBJECT_FROM_ID(NetworkID x); + + /// \internal + void TrackNetworkIDObject(NetworkIDObject *networkIdObject); + void StopTrackingNetworkIDObject(NetworkIDObject *networkIdObject); + +protected: + friend class NetworkIDObject; + + NetworkIDObject *networkIdHash[NETWORK_ID_MANAGER_HASH_LENGTH]; + unsigned int NetworkIDToHashIndex(NetworkID networkId); + uint64_t startingOffset; + /// \internal + NetworkID GetNewNetworkID(void); + +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/NetworkIDObject.cpp b/src/raknet/NetworkIDObject.cpp index 89b4209..60a2345 100755 --- a/src/raknet/NetworkIDObject.cpp +++ b/src/raknet/NetworkIDObject.cpp @@ -1,62 +1,62 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "NetworkIDObject.h" -#include "NetworkIDManager.h" -#include "RakAssert.h" -#include "RakAlloca.h" - -using namespace RakNet; - -NetworkIDObject::NetworkIDObject() -{ - networkID=UNASSIGNED_NETWORK_ID; - parent=0; - networkIDManager=0; - nextInstanceForNetworkIDManager=0; -} -NetworkIDObject::~NetworkIDObject() -{ - if (networkID!=UNASSIGNED_NETWORK_ID) - networkIDManager->StopTrackingNetworkIDObject(this); -} -void NetworkIDObject::SetNetworkIDManager( NetworkIDManager *manager) -{ - networkIDManager=manager; -} -NetworkIDManager * NetworkIDObject::GetNetworkIDManager( void ) -{ - return networkIDManager; -} -NetworkID NetworkIDObject::GetNetworkID( void ) -{ - if (networkID==UNASSIGNED_NETWORK_ID) - { - RakAssert(networkIDManager); - networkID = networkIDManager->GetNewNetworkID(); - networkIDManager->TrackNetworkIDObject(this); - } - return networkID; -} -void NetworkIDObject::SetNetworkID( NetworkID id ) -{ - if (id==networkID) - return; - - if (networkID!=UNASSIGNED_NETWORK_ID) - networkIDManager->StopTrackingNetworkIDObject(this); - networkID = id; - networkIDManager->TrackNetworkIDObject(this); -} -void NetworkIDObject::SetParent( void *_parent ) -{ - parent=_parent; -} -void* NetworkIDObject::GetParent( void ) const -{ - return parent; -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "NetworkIDObject.h" +#include "NetworkIDManager.h" +#include "RakAssert.h" +#include "RakAlloca.h" + +using namespace RakNet; + +NetworkIDObject::NetworkIDObject() +{ + networkID=UNASSIGNED_NETWORK_ID; + parent=0; + networkIDManager=0; + nextInstanceForNetworkIDManager=0; +} +NetworkIDObject::~NetworkIDObject() +{ + if (networkID!=UNASSIGNED_NETWORK_ID) + networkIDManager->StopTrackingNetworkIDObject(this); +} +void NetworkIDObject::SetNetworkIDManager( NetworkIDManager *manager) +{ + networkIDManager=manager; +} +NetworkIDManager * NetworkIDObject::GetNetworkIDManager( void ) +{ + return networkIDManager; +} +NetworkID NetworkIDObject::GetNetworkID( void ) +{ + if (networkID==UNASSIGNED_NETWORK_ID) + { + RakAssert(networkIDManager); + networkID = networkIDManager->GetNewNetworkID(); + networkIDManager->TrackNetworkIDObject(this); + } + return networkID; +} +void NetworkIDObject::SetNetworkID( NetworkID id ) +{ + if (id==networkID) + return; + + if (networkID!=UNASSIGNED_NETWORK_ID) + networkIDManager->StopTrackingNetworkIDObject(this); + networkID = id; + networkIDManager->TrackNetworkIDObject(this); +} +void NetworkIDObject::SetParent( void *_parent ) +{ + parent=_parent; +} +void* NetworkIDObject::GetParent( void ) const +{ + return parent; +} diff --git a/src/raknet/NetworkIDObject.h b/src/raknet/NetworkIDObject.h index b53b702..69aaf34 100755 --- a/src/raknet/NetworkIDObject.h +++ b/src/raknet/NetworkIDObject.h @@ -1,81 +1,81 @@ -/// \file -/// \brief A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#if !defined(__NETWORK_ID_GENERATOR) -#define __NETWORK_ID_GENERATOR - -#include "RakNetTypes.h" -#include "RakMemoryOverride.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class NetworkIDManager; - -typedef uint32_t NetworkIDType; - -/// \brief Unique shared ids for each object instance -/// \details A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network. -/// One system should return true for IsNetworkIDAuthority() and the rest should return false. When an object needs to be created, have the the one system create the object. -/// Then have that system send a message to all other systems, and include the value returned from GetNetworkID() in that packet. All other systems should then create the same -/// class of object, and call SetNetworkID() on that class with the NetworkID in the packet. -/// \see the manual for more information on this. -class RAK_DLL_EXPORT NetworkIDObject -{ -public: - // Constructor. NetworkIDs, if IsNetworkIDAuthority() is true, are created here. - NetworkIDObject(); - - // Destructor. Used NetworkIDs, if any, are freed here. - virtual ~NetworkIDObject(); - - /// Sets the manager class from which to request unique network IDs - /// Unlike previous versions, the NetworkIDObject relies on a manager class to provide IDs, rather than using statics, - /// So you can have more than one set of IDs on the same system. - virtual void SetNetworkIDManager( NetworkIDManager *manager); - - /// Returns what was passed to SetNetworkIDManager - virtual NetworkIDManager * GetNetworkIDManager( void ); - - /// Returns the NetworkID that you can use to refer to this object over the network. - /// \pre You must first call SetNetworkIDManager before using this function - /// \retval UNASSIGNED_NETWORK_ID UNASSIGNED_NETWORK_ID is returned IsNetworkIDAuthority() is false and SetNetworkID() was not previously called. This is also returned if you call this function in the constructor. - /// \retval 0-65534 Any other value is a valid NetworkID. NetworkIDs start at 0 and go to 65534, wrapping at that point. - virtual NetworkID GetNetworkID( void ); - - /// Sets the NetworkID for this instance. Usually this is called by the clients and determined from the servers. However, if you save multiplayer games you would likely use - /// This on load as well. - virtual void SetNetworkID( NetworkID id ); - - /// Your class does not have to derive from NetworkIDObject, although that is the easiest way to implement this. - /// If you want this to be a member object of another class, rather than inherit, then call SetParent() with a pointer to the parent class instance. - /// GET_OBJECT_FROM_ID will then return the parent rather than this instance. - virtual void SetParent( void *_parent ); - - /// Return what was passed to SetParent - /// \return The value passed to SetParent, or 0 if it was never called. - virtual void* GetParent( void ) const; - -protected: - /// The network ID of this object - NetworkID networkID; - - NetworkIDManager *networkIDManager; - - /// The parent set by SetParent() - void *parent; - - /// \internal, used by NetworkIDManager - friend class NetworkIDManager; - NetworkIDObject *nextInstanceForNetworkIDManager; -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#if !defined(__NETWORK_ID_GENERATOR) +#define __NETWORK_ID_GENERATOR + +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class NetworkIDManager; + +typedef uint32_t NetworkIDType; + +/// \brief Unique shared ids for each object instance +/// \details A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network. +/// One system should return true for IsNetworkIDAuthority() and the rest should return false. When an object needs to be created, have the the one system create the object. +/// Then have that system send a message to all other systems, and include the value returned from GetNetworkID() in that packet. All other systems should then create the same +/// class of object, and call SetNetworkID() on that class with the NetworkID in the packet. +/// \see the manual for more information on this. +class RAK_DLL_EXPORT NetworkIDObject +{ +public: + // Constructor. NetworkIDs, if IsNetworkIDAuthority() is true, are created here. + NetworkIDObject(); + + // Destructor. Used NetworkIDs, if any, are freed here. + virtual ~NetworkIDObject(); + + /// Sets the manager class from which to request unique network IDs + /// Unlike previous versions, the NetworkIDObject relies on a manager class to provide IDs, rather than using statics, + /// So you can have more than one set of IDs on the same system. + virtual void SetNetworkIDManager( NetworkIDManager *manager); + + /// Returns what was passed to SetNetworkIDManager + virtual NetworkIDManager * GetNetworkIDManager( void ); + + /// Returns the NetworkID that you can use to refer to this object over the network. + /// \pre You must first call SetNetworkIDManager before using this function + /// \retval UNASSIGNED_NETWORK_ID UNASSIGNED_NETWORK_ID is returned IsNetworkIDAuthority() is false and SetNetworkID() was not previously called. This is also returned if you call this function in the constructor. + /// \retval 0-65534 Any other value is a valid NetworkID. NetworkIDs start at 0 and go to 65534, wrapping at that point. + virtual NetworkID GetNetworkID( void ); + + /// Sets the NetworkID for this instance. Usually this is called by the clients and determined from the servers. However, if you save multiplayer games you would likely use + /// This on load as well. + virtual void SetNetworkID( NetworkID id ); + + /// Your class does not have to derive from NetworkIDObject, although that is the easiest way to implement this. + /// If you want this to be a member object of another class, rather than inherit, then call SetParent() with a pointer to the parent class instance. + /// GET_OBJECT_FROM_ID will then return the parent rather than this instance. + virtual void SetParent( void *_parent ); + + /// Return what was passed to SetParent + /// \return The value passed to SetParent, or 0 if it was never called. + virtual void* GetParent( void ) const; + +protected: + /// The network ID of this object + NetworkID networkID; + + NetworkIDManager *networkIDManager; + + /// The parent set by SetParent() + void *parent; + + /// \internal, used by NetworkIDManager + friend class NetworkIDManager; + NetworkIDObject *nextInstanceForNetworkIDManager; +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/PS3Includes.h b/src/raknet/PS3Includes.h index 5d0e340..ff1a125 100755 --- a/src/raknet/PS3Includes.h +++ b/src/raknet/PS3Includes.h @@ -1,71 +1,71 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/raknet/PacketConsoleLogger.cpp b/src/raknet/PacketConsoleLogger.cpp index 4d69f99..4788618 100755 --- a/src/raknet/PacketConsoleLogger.cpp +++ b/src/raknet/PacketConsoleLogger.cpp @@ -1,26 +1,26 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_LogCommandParser==1 && _RAKNET_SUPPORT_PacketLogger==1 -#include "PacketConsoleLogger.h" -#include "LogCommandParser.h" -#include - -using namespace RakNet; - -PacketConsoleLogger::PacketConsoleLogger() -{ - logCommandParser=0; -} - -void PacketConsoleLogger::SetLogCommandParser(LogCommandParser *lcp) -{ - logCommandParser=lcp; - if (logCommandParser) - logCommandParser->AddChannel("PacketConsoleLogger"); -} -void PacketConsoleLogger::WriteLog(const char *str) -{ - if (logCommandParser) - logCommandParser->WriteLog("PacketConsoleLogger", str); -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_LogCommandParser==1 && _RAKNET_SUPPORT_PacketLogger==1 +#include "PacketConsoleLogger.h" +#include "LogCommandParser.h" +#include + +using namespace RakNet; + +PacketConsoleLogger::PacketConsoleLogger() +{ + logCommandParser=0; +} + +void PacketConsoleLogger::SetLogCommandParser(LogCommandParser *lcp) +{ + logCommandParser=lcp; + if (logCommandParser) + logCommandParser->AddChannel("PacketConsoleLogger"); +} +void PacketConsoleLogger::WriteLog(const char *str) +{ + if (logCommandParser) + logCommandParser->WriteLog("PacketConsoleLogger", str); +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PacketConsoleLogger.h b/src/raknet/PacketConsoleLogger.h index 28aaef9..62bc001 100755 --- a/src/raknet/PacketConsoleLogger.h +++ b/src/raknet/PacketConsoleLogger.h @@ -1,38 +1,38 @@ -/// \file -/// \brief This will write all incoming and outgoing network messages to the log command parser, which can be accessed through Telnet -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_LogCommandParser==1 && _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __PACKET_CONSOLE_LOGGER_H_ -#define __PACKET_CONSOLE_LOGGER_H_ - -#include "PacketLogger.h" - -namespace RakNet -{ -/// Forward declarations -class LogCommandParser; - -/// \ingroup PACKETLOGGER_GROUP -/// \brief Packetlogger that logs to a remote command console -class RAK_DLL_EXPORT PacketConsoleLogger : public PacketLogger -{ -public: - PacketConsoleLogger(); - // Writes to the command parser used for logging, which is accessed through a secondary communication layer (such as Telnet or RakNet) - See ConsoleServer.h - virtual void SetLogCommandParser(LogCommandParser *lcp); - virtual void WriteLog(const char *str); -protected: - LogCommandParser *logCommandParser; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief This will write all incoming and outgoing network messages to the log command parser, which can be accessed through Telnet +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_LogCommandParser==1 && _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __PACKET_CONSOLE_LOGGER_H_ +#define __PACKET_CONSOLE_LOGGER_H_ + +#include "PacketLogger.h" + +namespace RakNet +{ +/// Forward declarations +class LogCommandParser; + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Packetlogger that logs to a remote command console +class RAK_DLL_EXPORT PacketConsoleLogger : public PacketLogger +{ +public: + PacketConsoleLogger(); + // Writes to the command parser used for logging, which is accessed through a secondary communication layer (such as Telnet or RakNet) - See ConsoleServer.h + virtual void SetLogCommandParser(LogCommandParser *lcp); + virtual void WriteLog(const char *str); +protected: + LogCommandParser *logCommandParser; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PacketFileLogger.cpp b/src/raknet/PacketFileLogger.cpp index 18631c2..02aabd7 100755 --- a/src/raknet/PacketFileLogger.cpp +++ b/src/raknet/PacketFileLogger.cpp @@ -1,45 +1,45 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 -#include "PacketFileLogger.h" -#include "GetTime.h" - -using namespace RakNet; - -PacketFileLogger::PacketFileLogger() -{ - packetLogFile=0; -} -PacketFileLogger::~PacketFileLogger() -{ - if (packetLogFile) - { - fflush(packetLogFile); - fclose(packetLogFile); - } -} -void PacketFileLogger::StartLog(const char *filenamePrefix) -{ - // Open file for writing - char filename[256]; - if (filenamePrefix) - sprintf(filename, "%s_%i.csv", filenamePrefix, (int) RakNet::GetTimeMS()); - else - sprintf(filename, "PacketLog_%i.csv", (int) RakNet::GetTimeMS()); - packetLogFile = fopen(filename, "wt"); - LogHeader(); - if (packetLogFile) - { - fflush(packetLogFile); - } -} - -void PacketFileLogger::WriteLog(const char *str) -{ - if (packetLogFile) - { - fprintf(packetLogFile, "%s\n", str); - fflush(packetLogFile); - } -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 +#include "PacketFileLogger.h" +#include "GetTime.h" + +using namespace RakNet; + +PacketFileLogger::PacketFileLogger() +{ + packetLogFile=0; +} +PacketFileLogger::~PacketFileLogger() +{ + if (packetLogFile) + { + fflush(packetLogFile); + fclose(packetLogFile); + } +} +void PacketFileLogger::StartLog(const char *filenamePrefix) +{ + // Open file for writing + char filename[256]; + if (filenamePrefix) + sprintf(filename, "%s_%i.csv", filenamePrefix, (int) RakNet::GetTimeMS()); + else + sprintf(filename, "PacketLog_%i.csv", (int) RakNet::GetTimeMS()); + packetLogFile = fopen(filename, "wt"); + LogHeader(); + if (packetLogFile) + { + fflush(packetLogFile); + } +} + +void PacketFileLogger::WriteLog(const char *str) +{ + if (packetLogFile) + { + fprintf(packetLogFile, "%s\n", str); + fflush(packetLogFile); + } +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PacketFileLogger.h b/src/raknet/PacketFileLogger.h index 48f5ce2..1e08f28 100755 --- a/src/raknet/PacketFileLogger.h +++ b/src/raknet/PacketFileLogger.h @@ -1,37 +1,37 @@ -/// \file -/// \brief This will write all incoming and outgoing network messages to a file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __PACKET_FILE_LOGGER_H_ -#define __PACKET_FILE_LOGGER_H_ - -#include "PacketLogger.h" -#include - -namespace RakNet -{ - -/// \ingroup PACKETLOGGER_GROUP -/// \brief Packetlogger that outputs to a file -class RAK_DLL_EXPORT PacketFileLogger : public PacketLogger -{ -public: - PacketFileLogger(); - virtual ~PacketFileLogger(); - void StartLog(const char *filenamePrefix); - virtual void WriteLog(const char *str); -protected: - FILE *packetLogFile; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief This will write all incoming and outgoing network messages to a file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __PACKET_FILE_LOGGER_H_ +#define __PACKET_FILE_LOGGER_H_ + +#include "PacketLogger.h" +#include + +namespace RakNet +{ + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Packetlogger that outputs to a file +class RAK_DLL_EXPORT PacketFileLogger : public PacketLogger +{ +public: + PacketFileLogger(); + virtual ~PacketFileLogger(); + void StartLog(const char *filenamePrefix); + virtual void WriteLog(const char *str); +protected: + FILE *packetLogFile; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PacketLogger.cpp b/src/raknet/PacketLogger.cpp index 472991c..6d0b58c 100755 --- a/src/raknet/PacketLogger.cpp +++ b/src/raknet/PacketLogger.cpp @@ -1,456 +1,456 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#include "PacketLogger.h" -#include "BitStream.h" -#include "DS_List.h" -#include "InternalPacket.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "StringCompressor.h" -#include "GetTime.h" -#include -#include -#include -#include "Itoa.h" -#include -#include "SocketIncludes.h" -#include "gettimeofday.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(PacketLogger,PacketLogger); - -PacketLogger::PacketLogger() -{ - printId=true; - printAcks=true; - prefix[0]=0; - suffix[0]=0; - logDirectMessages=true; -} -PacketLogger::~PacketLogger() -{ -} -void PacketLogger::FormatLine( -char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame, unsigned char id -, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, -unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex) -{ - char numericID[16]; - const char* idToPrint = NULL; - if(printId) - { - if (splitPacketCount>0 && splitPacketCount!=(unsigned int)-1) - idToPrint="(SPLIT PACKET)"; - else - idToPrint = IDTOString(id); - } - // If printId is false, idToPrint will be NULL, as it will - // in the case of an unrecognized id. Testing printId for false - // would just be redundant. - if(idToPrint == NULL) - { - sprintf(numericID, "%5u", id); - idToPrint = numericID; - } - - FormatLine(into, dir, type, reliableMessageNumber, frame, idToPrint, bitLen, time, local, remote,splitPacketId,splitPacketIndex,splitPacketCount, orderingIndex); -} - -void PacketLogger::FormatLine( -char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame, const char* idToPrint -, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, -unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex) -{ - char str1[64], str2[62]; - local.ToString(true, str1); - remote.ToString(true, str2); - char localtime[128]; - GetLocalTime(localtime); - char str3[64]; - if (reliableMessageNumber==(unsigned int)-1) - { - str3[0]='N'; - str3[1]='/'; - str3[2]='A'; - str3[3]=0; - } - else - { - sprintf(str3,"%5u",reliableMessageNumber); - } - - sprintf(into, "%s,%s%s,%s,%s,%5u,%s,%u,%" PRINTF_64_BIT_MODIFIER "u,%s,%s,%i,%i,%i,%i,%s," - , localtime - , prefix - , dir - , type - , str3 - , frame - , idToPrint - , bitLen - , time - , str1 - , str2 - , splitPacketId - , splitPacketIndex - , splitPacketCount - , orderingIndex - , suffix - ); -} -void PacketLogger::OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) -{ - if (logDirectMessages==false) - return; - - char str[256]; - FormatLine(str, "Snd", "Raw", 0, 0, data[0], bitsUsed, RakNet::GetTimeMS(), rakPeerInterface->GetExternalID(remoteSystemAddress), remoteSystemAddress, (unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); - AddToLog(str); -} - -void PacketLogger::LogHeader(void) -{ - // Last 5 are splitpacket id, split packet index, split packet count, ordering index, suffix - AddToLog("Clock,S|R,Typ,Reliable#,Frm #,PktID,BitLn,Time ,Local IP:Port ,RemoteIP:Port,SPID,SPIN,SPCO,OI,Suffix,Miscellaneous\n"); -} -void PacketLogger::OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) -{ - if (logDirectMessages==false) - return; - - char str[256]; - FormatLine(str, "Rcv", "Raw", 0, 0, data[0], bitsUsed, RakNet::GetTime(), rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS), remoteSystemAddress,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); - AddToLog(str); -} -void PacketLogger::OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) -{ - char str[1024]; - FormatLine(str, "RcvErr", errorMessage, 0, 0, "", bitsUsed, RakNet::GetTime(), rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS), remoteSystemAddress,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); - AddToLog(str); -} -void PacketLogger::OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time) -{ - char str[256]; - char str1[64], str2[62]; - SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); - localSystemAddress.ToString(true, str1); - remoteSystemAddress.ToString(true, str2); - char localtime[128]; - GetLocalTime(localtime); - - sprintf(str, "%s,Rcv,Ack,%i,,,,%" PRINTF_64_BIT_MODIFIER "u,%s,%s,,,,,," - , localtime - , messageNumber - , (unsigned long long) time - , str1 - , str2 - ); - AddToLog(str); -} -void PacketLogger::OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) -{ - char str[256]; - char str1[64], str2[62]; - SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); - localSystemAddress.ToString(true, str1); - remoteSystemAddress.ToString(true, str2); - RakNet::TimeMS time = RakNet::GetTimeMS(); - char localtime[128]; - GetLocalTime(localtime); - - sprintf(str, "%s,Lcl,PBP,,,%s,%i,%" PRINTF_64_BIT_MODIFIER "u,%s,%s,,,,,," - , localtime - , BaseIDTOString(data[0]) - , bitsUsed - , (unsigned long long) time - , str1 - , str2 - ); - AddToLog(str); -} -void PacketLogger::OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend) -{ - char str[256]; - const char *sendTypes[] = - { - "Rcv", - "Snd", - "Err1", - "Err2", - "Err3", - "Err4", - "Err5", - "Err6", - }; - const char *sendType = sendTypes[isSend]; - SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); - - unsigned int reliableMessageNumber; - if (internalPacket->reliability==UNRELIABLE || internalPacket->reliability==UNRELIABLE_SEQUENCED || internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) - reliableMessageNumber=(unsigned int)-1; - else - reliableMessageNumber=internalPacket->reliableMessageNumber; - - if (internalPacket->data[0]==ID_TIMESTAMP) - { - FormatLine(str, sendType, "Tms", reliableMessageNumber, frameNumber, internalPacket->data[1+sizeof(RakNet::Time)], internalPacket->dataBitLength, (unsigned long long)time, localSystemAddress, remoteSystemAddress, internalPacket->splitPacketId, internalPacket->splitPacketIndex, internalPacket->splitPacketCount, internalPacket->orderingIndex); - } - else - { - FormatLine(str, sendType, "Nrm", reliableMessageNumber, frameNumber, internalPacket->data[0], internalPacket->dataBitLength, (unsigned long long)time, localSystemAddress, remoteSystemAddress, internalPacket->splitPacketId, internalPacket->splitPacketIndex, internalPacket->splitPacketCount, internalPacket->orderingIndex); - } - - AddToLog(str); -} -void PacketLogger::AddToLog(const char *str) -{ - WriteLog(str); -} -void PacketLogger::WriteLog(const char *str) -{ - RAKNET_DEBUG_PRINTF("%s\n", str); -} -void PacketLogger::WriteMiscellaneous(const char *type, const char *msg) -{ - char str[1024]; - char str1[64]; - SystemAddress localSystemAddress = rakPeerInterface->GetInternalID(); - localSystemAddress.ToString(true, str1); - RakNet::TimeMS time = RakNet::GetTimeMS(); - char localtime[128]; - GetLocalTime(localtime); - - sprintf(str, "%s,Lcl,%s,,,,,%" PRINTF_64_BIT_MODIFIER "u,%s,,,,,,,%s" - , localtime - , type - , (unsigned long long) time - , str1 - , msg - ); - - AddToLog(msg); -} -void PacketLogger::SetPrintID(bool print) -{ - printId=print; -} -void PacketLogger::SetPrintAcks(bool print) -{ - printAcks=print; -} -const char* PacketLogger::BaseIDTOString(unsigned char Id) -{ - if (Id >= ID_USER_PACKET_ENUM) - return 0; - - const char *IDTable[((int)ID_USER_PACKET_ENUM)+1]= - { - "ID_CONNECTED_PING", - "ID_UNCONNECTED_PING", - "ID_UNCONNECTED_PING_OPEN_CONNECTIONS", - "ID_CONNECTED_PONG", - "ID_DETECT_LOST_CONNECTIONS", - "ID_OPEN_CONNECTION_REQUEST_1", - "ID_OPEN_CONNECTION_REPLY_1", - "ID_OPEN_CONNECTION_REQUEST_2", - "ID_OPEN_CONNECTION_REPLY_2", - "ID_CONNECTION_REQUEST", - "ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY", - "ID_OUR_SYSTEM_REQUIRES_SECURITY", - "ID_PUBLIC_KEY_MISMATCH", - "ID_OUT_OF_BAND_INTERNAL", - "ID_SND_RECEIPT_ACKED", - "ID_SND_RECEIPT_LOSS", - "ID_CONNECTION_REQUEST_ACCEPTED", - "ID_CONNECTION_ATTEMPT_FAILED", - "ID_ALREADY_CONNECTED", - "ID_NEW_INCOMING_CONNECTION", - "ID_NO_FREE_INCOMING_CONNECTIONS", - "ID_DISCONNECTION_NOTIFICATION", - "ID_CONNECTION_LOST", - "ID_CONNECTION_BANNED", - "ID_INVALID_PASSWORD", - "ID_INCOMPATIBLE_PROTOCOL_VERSION", - "ID_IP_RECENTLY_CONNECTED", - "ID_TIMESTAMP", - "ID_UNCONNECTED_PONG", - "ID_ADVERTISE_SYSTEM", - "ID_DOWNLOAD_PROGRESS", - "ID_REMOTE_DISCONNECTION_NOTIFICATION", - "ID_REMOTE_CONNECTION_LOST", - "ID_REMOTE_NEW_INCOMING_CONNECTION", - "ID_FILE_LIST_TRANSFER_HEADER", - "ID_FILE_LIST_TRANSFER_FILE", - "ID_FILE_LIST_REFERENCE_PUSH_ACK", - "ID_DDT_DOWNLOAD_REQUEST", - "ID_TRANSPORT_STRING", - "ID_REPLICA_MANAGER_CONSTRUCTION", - "ID_REPLICA_MANAGER_SCOPE_CHANGE", - "ID_REPLICA_MANAGER_SERIALIZE", - "ID_REPLICA_MANAGER_DOWNLOAD_STARTED", - "ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE", - "ID_RAKVOICE_OPEN_CHANNEL_REQUEST", - "ID_RAKVOICE_OPEN_CHANNEL_REPLY", - "ID_RAKVOICE_CLOSE_CHANNEL", - "ID_RAKVOICE_DATA", - "ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE", - "ID_AUTOPATCHER_CREATION_LIST", - "ID_AUTOPATCHER_DELETION_LIST", - "ID_AUTOPATCHER_GET_PATCH", - "ID_AUTOPATCHER_PATCH_LIST", - "ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR", - "ID_AUTOPATCHER_FINISHED_INTERNAL", - "ID_AUTOPATCHER_FINISHED", - "ID_AUTOPATCHER_RESTART_APPLICATION", - "ID_NAT_PUNCHTHROUGH_REQUEST", - "ID_NAT_GROUP_PUNCHTHROUGH_REQUEST", - "ID_NAT_GROUP_PUNCHTHROUGH_REPLY", - "ID_NAT_CONNECT_AT_TIME", - "ID_NAT_GET_MOST_RECENT_PORT", - "ID_NAT_CLIENT_READY", - "ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION", - "ID_NAT_TARGET_NOT_CONNECTED", - "ID_NAT_TARGET_UNRESPONSIVE", - "ID_NAT_CONNECTION_TO_TARGET_LOST", - "ID_NAT_ALREADY_IN_PROGRESS", - "ID_NAT_PUNCHTHROUGH_FAILED", - "ID_NAT_PUNCHTHROUGH_SUCCEEDED", - "ID_NAT_GROUP_PUNCH_FAILED", - "ID_NAT_GROUP_PUNCH_SUCCEEDED", - "ID_READY_EVENT_SET", - "ID_READY_EVENT_UNSET", - "ID_READY_EVENT_ALL_SET", - "ID_READY_EVENT_QUERY", - "ID_LOBBY_GENERAL", - "ID_RPC_REMOTE_ERROR", - "ID_RPC_PLUGIN", - "ID_FILE_LIST_REFERENCE_PUSH", - "ID_READY_EVENT_FORCE_ALL_SET", - "ID_ROOMS_EXECUTE_FUNC", - "ID_ROOMS_LOGON_STATUS", - "ID_ROOMS_HANDLE_CHANGE", - "ID_LOBBY2_SEND_MESSAGE", - "ID_LOBBY2_SERVER_ERROR", - "ID_FCM2_NEW_HOST", - "ID_FCM2_REQUEST_FCMGUID", - "ID_FCM2_RESPOND_CONNECTION_COUNT", - "ID_FCM2_INFORM_FCMGUID", - "ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT", - "ID_UDP_PROXY_GENERAL", - "ID_SQLite3_EXEC", - "ID_SQLite3_UNKNOWN_DB", - "ID_SQLLITE_LOGGER", - "ID_NAT_TYPE_DETECTION_REQUEST", - "ID_NAT_TYPE_DETECTION_RESULT", - "ID_ROUTER_2_INTERNAL", - "ID_ROUTER_2_FORWARDING_NO_PATH", - "ID_ROUTER_2_FORWARDING_ESTABLISHED", - "ID_ROUTER_2_REROUTED", - "ID_TEAM_BALANCER_INTERNAL", - "ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING", - "ID_TEAM_BALANCER_TEAMS_LOCKED", - "ID_TEAM_BALANCER_TEAM_ASSIGNED", - "ID_LIGHTSPEED_INTEGRATION", - "ID_XBOX_LOBBY", - "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS", - "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS", - "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE", - "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE", - "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT", - "ID_TWO_WAY_AUTHENTICATION_NEGOTIATION", - "ID_CLOUD_POST_REQUEST", - "ID_CLOUD_RELEASE_REQUEST", - "ID_CLOUD_GET_REQUEST", - "ID_CLOUD_GET_RESPONSE", - "ID_CLOUD_UNSUBSCRIBE_REQUEST", - "ID_CLOUD_SERVER_TO_SERVER_COMMAND", - "ID_CLOUD_SUBSCRIPTION_NOTIFICATION", - "ID_RESERVED_1", - "ID_RESERVED_2", - "ID_RESERVED_3", - "ID_RESERVED_4", - "ID_RESERVED_5", - "ID_RESERVED_6", - "ID_RESERVED_7", - "ID_RESERVED_8", - "ID_RESERVED_9", - "ID_USER_PACKET_ENUM", - }; - - return (char*)IDTable[Id]; -} -const char* PacketLogger::UserIDTOString(unsigned char Id) -{ - // Users should override this - static char str[256]; - Itoa(Id, str, 10); - return (const char*) str; -} -const char* PacketLogger::IDTOString(unsigned char Id) -{ - const char *out; - out=BaseIDTOString(Id); - if (out) - return out; - return UserIDTOString(Id); -} -void PacketLogger::SetPrefix(const char *_prefix) -{ - strncpy(prefix, _prefix, 255); - prefix[255]=0; -} -void PacketLogger::SetSuffix(const char *_suffix) -{ - strncpy(suffix, _suffix, 255); - suffix[255]=0; -} -void PacketLogger::GetLocalTime(char buffer[128]) -{ -#if defined(_WIN32) && !defined(__GNUC__) && !defined(__GCCXML__) - time_t rawtime; - struct timeval tv; - // If you get an arror about an incomplete type, just delete this file - struct timezone tz; - gettimeofday(&tv, &tz); - // time ( &rawtime ); - rawtime=tv.tv_sec; - - struct tm * timeinfo; - timeinfo = localtime ( &rawtime ); - strftime (buffer,128,"%x %X",timeinfo); - char buff[32]; - sprintf(buff, ".%i", tv.tv_usec); - strcat(buffer,buff); - - // Commented version puts the time first - /* - struct tm * timeinfo; - timeinfo = localtime ( &rawtime ); - strftime (buffer,128,"%X",timeinfo); - char buff[32]; - sprintf(buff, ".%i ", tv.tv_usec); - strcat(buffer,buff); - char buff2[32]; - strftime (buff2,32,"%x",timeinfo); - strcat(buffer,buff2); - */ -#else - buffer[0]=0; -#endif -} -void PacketLogger::SetLogDirectMessages(bool send) -{ - logDirectMessages=send; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#include "PacketLogger.h" +#include "BitStream.h" +#include "DS_List.h" +#include "InternalPacket.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "StringCompressor.h" +#include "GetTime.h" +#include +#include +#include +#include "Itoa.h" +#include +#include "SocketIncludes.h" +#include "gettimeofday.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(PacketLogger,PacketLogger); + +PacketLogger::PacketLogger() +{ + printId=true; + printAcks=true; + prefix[0]=0; + suffix[0]=0; + logDirectMessages=true; +} +PacketLogger::~PacketLogger() +{ +} +void PacketLogger::FormatLine( +char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame, unsigned char id +, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, +unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex) +{ + char numericID[16]; + const char* idToPrint = NULL; + if(printId) + { + if (splitPacketCount>0 && splitPacketCount!=(unsigned int)-1) + idToPrint="(SPLIT PACKET)"; + else + idToPrint = IDTOString(id); + } + // If printId is false, idToPrint will be NULL, as it will + // in the case of an unrecognized id. Testing printId for false + // would just be redundant. + if(idToPrint == NULL) + { + sprintf(numericID, "%5u", id); + idToPrint = numericID; + } + + FormatLine(into, dir, type, reliableMessageNumber, frame, idToPrint, bitLen, time, local, remote,splitPacketId,splitPacketIndex,splitPacketCount, orderingIndex); +} + +void PacketLogger::FormatLine( +char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame, const char* idToPrint +, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, +unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex) +{ + char str1[64], str2[62]; + local.ToString(true, str1); + remote.ToString(true, str2); + char localtime[128]; + GetLocalTime(localtime); + char str3[64]; + if (reliableMessageNumber==(unsigned int)-1) + { + str3[0]='N'; + str3[1]='/'; + str3[2]='A'; + str3[3]=0; + } + else + { + sprintf(str3,"%5u",reliableMessageNumber); + } + + sprintf(into, "%s,%s%s,%s,%s,%5u,%s,%u,%" PRINTF_64_BIT_MODIFIER "u,%s,%s,%i,%i,%i,%i,%s," + , localtime + , prefix + , dir + , type + , str3 + , frame + , idToPrint + , bitLen + , time + , str1 + , str2 + , splitPacketId + , splitPacketIndex + , splitPacketCount + , orderingIndex + , suffix + ); +} +void PacketLogger::OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) +{ + if (logDirectMessages==false) + return; + + char str[256]; + FormatLine(str, "Snd", "Raw", 0, 0, data[0], bitsUsed, RakNet::GetTimeMS(), rakPeerInterface->GetExternalID(remoteSystemAddress), remoteSystemAddress, (unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); + AddToLog(str); +} + +void PacketLogger::LogHeader(void) +{ + // Last 5 are splitpacket id, split packet index, split packet count, ordering index, suffix + AddToLog("Clock,S|R,Typ,Reliable#,Frm #,PktID,BitLn,Time ,Local IP:Port ,RemoteIP:Port,SPID,SPIN,SPCO,OI,Suffix,Miscellaneous\n"); +} +void PacketLogger::OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) +{ + if (logDirectMessages==false) + return; + + char str[256]; + FormatLine(str, "Rcv", "Raw", 0, 0, data[0], bitsUsed, RakNet::GetTime(), rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS), remoteSystemAddress,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); + AddToLog(str); +} +void PacketLogger::OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) +{ + char str[1024]; + FormatLine(str, "RcvErr", errorMessage, 0, 0, "", bitsUsed, RakNet::GetTime(), rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS), remoteSystemAddress,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1,(unsigned int)-1); + AddToLog(str); +} +void PacketLogger::OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time) +{ + char str[256]; + char str1[64], str2[62]; + SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); + localSystemAddress.ToString(true, str1); + remoteSystemAddress.ToString(true, str2); + char localtime[128]; + GetLocalTime(localtime); + + sprintf(str, "%s,Rcv,Ack,%i,,,,%" PRINTF_64_BIT_MODIFIER "u,%s,%s,,,,,," + , localtime + , messageNumber + , (unsigned long long) time + , str1 + , str2 + ); + AddToLog(str); +} +void PacketLogger::OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) +{ + char str[256]; + char str1[64], str2[62]; + SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); + localSystemAddress.ToString(true, str1); + remoteSystemAddress.ToString(true, str2); + RakNet::TimeMS time = RakNet::GetTimeMS(); + char localtime[128]; + GetLocalTime(localtime); + + sprintf(str, "%s,Lcl,PBP,,,%s,%i,%" PRINTF_64_BIT_MODIFIER "u,%s,%s,,,,,," + , localtime + , BaseIDTOString(data[0]) + , bitsUsed + , (unsigned long long) time + , str1 + , str2 + ); + AddToLog(str); +} +void PacketLogger::OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend) +{ + char str[256]; + const char *sendTypes[] = + { + "Rcv", + "Snd", + "Err1", + "Err2", + "Err3", + "Err4", + "Err5", + "Err6", + }; + const char *sendType = sendTypes[isSend]; + SystemAddress localSystemAddress = rakPeerInterface->GetExternalID(remoteSystemAddress); + + unsigned int reliableMessageNumber; + if (internalPacket->reliability==UNRELIABLE || internalPacket->reliability==UNRELIABLE_SEQUENCED || internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) + reliableMessageNumber=(unsigned int)-1; + else + reliableMessageNumber=internalPacket->reliableMessageNumber; + + if (internalPacket->data[0]==ID_TIMESTAMP) + { + FormatLine(str, sendType, "Tms", reliableMessageNumber, frameNumber, internalPacket->data[1+sizeof(RakNet::Time)], internalPacket->dataBitLength, (unsigned long long)time, localSystemAddress, remoteSystemAddress, internalPacket->splitPacketId, internalPacket->splitPacketIndex, internalPacket->splitPacketCount, internalPacket->orderingIndex); + } + else + { + FormatLine(str, sendType, "Nrm", reliableMessageNumber, frameNumber, internalPacket->data[0], internalPacket->dataBitLength, (unsigned long long)time, localSystemAddress, remoteSystemAddress, internalPacket->splitPacketId, internalPacket->splitPacketIndex, internalPacket->splitPacketCount, internalPacket->orderingIndex); + } + + AddToLog(str); +} +void PacketLogger::AddToLog(const char *str) +{ + WriteLog(str); +} +void PacketLogger::WriteLog(const char *str) +{ + RAKNET_DEBUG_PRINTF("%s\n", str); +} +void PacketLogger::WriteMiscellaneous(const char *type, const char *msg) +{ + char str[1024]; + char str1[64]; + SystemAddress localSystemAddress = rakPeerInterface->GetInternalID(); + localSystemAddress.ToString(true, str1); + RakNet::TimeMS time = RakNet::GetTimeMS(); + char localtime[128]; + GetLocalTime(localtime); + + sprintf(str, "%s,Lcl,%s,,,,,%" PRINTF_64_BIT_MODIFIER "u,%s,,,,,,,%s" + , localtime + , type + , (unsigned long long) time + , str1 + , msg + ); + + AddToLog(msg); +} +void PacketLogger::SetPrintID(bool print) +{ + printId=print; +} +void PacketLogger::SetPrintAcks(bool print) +{ + printAcks=print; +} +const char* PacketLogger::BaseIDTOString(unsigned char Id) +{ + if (Id >= ID_USER_PACKET_ENUM) + return 0; + + const char *IDTable[((int)ID_USER_PACKET_ENUM)+1]= + { + "ID_CONNECTED_PING", + "ID_UNCONNECTED_PING", + "ID_UNCONNECTED_PING_OPEN_CONNECTIONS", + "ID_CONNECTED_PONG", + "ID_DETECT_LOST_CONNECTIONS", + "ID_OPEN_CONNECTION_REQUEST_1", + "ID_OPEN_CONNECTION_REPLY_1", + "ID_OPEN_CONNECTION_REQUEST_2", + "ID_OPEN_CONNECTION_REPLY_2", + "ID_CONNECTION_REQUEST", + "ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY", + "ID_OUR_SYSTEM_REQUIRES_SECURITY", + "ID_PUBLIC_KEY_MISMATCH", + "ID_OUT_OF_BAND_INTERNAL", + "ID_SND_RECEIPT_ACKED", + "ID_SND_RECEIPT_LOSS", + "ID_CONNECTION_REQUEST_ACCEPTED", + "ID_CONNECTION_ATTEMPT_FAILED", + "ID_ALREADY_CONNECTED", + "ID_NEW_INCOMING_CONNECTION", + "ID_NO_FREE_INCOMING_CONNECTIONS", + "ID_DISCONNECTION_NOTIFICATION", + "ID_CONNECTION_LOST", + "ID_CONNECTION_BANNED", + "ID_INVALID_PASSWORD", + "ID_INCOMPATIBLE_PROTOCOL_VERSION", + "ID_IP_RECENTLY_CONNECTED", + "ID_TIMESTAMP", + "ID_UNCONNECTED_PONG", + "ID_ADVERTISE_SYSTEM", + "ID_DOWNLOAD_PROGRESS", + "ID_REMOTE_DISCONNECTION_NOTIFICATION", + "ID_REMOTE_CONNECTION_LOST", + "ID_REMOTE_NEW_INCOMING_CONNECTION", + "ID_FILE_LIST_TRANSFER_HEADER", + "ID_FILE_LIST_TRANSFER_FILE", + "ID_FILE_LIST_REFERENCE_PUSH_ACK", + "ID_DDT_DOWNLOAD_REQUEST", + "ID_TRANSPORT_STRING", + "ID_REPLICA_MANAGER_CONSTRUCTION", + "ID_REPLICA_MANAGER_SCOPE_CHANGE", + "ID_REPLICA_MANAGER_SERIALIZE", + "ID_REPLICA_MANAGER_DOWNLOAD_STARTED", + "ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE", + "ID_RAKVOICE_OPEN_CHANNEL_REQUEST", + "ID_RAKVOICE_OPEN_CHANNEL_REPLY", + "ID_RAKVOICE_CLOSE_CHANNEL", + "ID_RAKVOICE_DATA", + "ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE", + "ID_AUTOPATCHER_CREATION_LIST", + "ID_AUTOPATCHER_DELETION_LIST", + "ID_AUTOPATCHER_GET_PATCH", + "ID_AUTOPATCHER_PATCH_LIST", + "ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR", + "ID_AUTOPATCHER_FINISHED_INTERNAL", + "ID_AUTOPATCHER_FINISHED", + "ID_AUTOPATCHER_RESTART_APPLICATION", + "ID_NAT_PUNCHTHROUGH_REQUEST", + "ID_NAT_GROUP_PUNCHTHROUGH_REQUEST", + "ID_NAT_GROUP_PUNCHTHROUGH_REPLY", + "ID_NAT_CONNECT_AT_TIME", + "ID_NAT_GET_MOST_RECENT_PORT", + "ID_NAT_CLIENT_READY", + "ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION", + "ID_NAT_TARGET_NOT_CONNECTED", + "ID_NAT_TARGET_UNRESPONSIVE", + "ID_NAT_CONNECTION_TO_TARGET_LOST", + "ID_NAT_ALREADY_IN_PROGRESS", + "ID_NAT_PUNCHTHROUGH_FAILED", + "ID_NAT_PUNCHTHROUGH_SUCCEEDED", + "ID_NAT_GROUP_PUNCH_FAILED", + "ID_NAT_GROUP_PUNCH_SUCCEEDED", + "ID_READY_EVENT_SET", + "ID_READY_EVENT_UNSET", + "ID_READY_EVENT_ALL_SET", + "ID_READY_EVENT_QUERY", + "ID_LOBBY_GENERAL", + "ID_RPC_REMOTE_ERROR", + "ID_RPC_PLUGIN", + "ID_FILE_LIST_REFERENCE_PUSH", + "ID_READY_EVENT_FORCE_ALL_SET", + "ID_ROOMS_EXECUTE_FUNC", + "ID_ROOMS_LOGON_STATUS", + "ID_ROOMS_HANDLE_CHANGE", + "ID_LOBBY2_SEND_MESSAGE", + "ID_LOBBY2_SERVER_ERROR", + "ID_FCM2_NEW_HOST", + "ID_FCM2_REQUEST_FCMGUID", + "ID_FCM2_RESPOND_CONNECTION_COUNT", + "ID_FCM2_INFORM_FCMGUID", + "ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT", + "ID_UDP_PROXY_GENERAL", + "ID_SQLite3_EXEC", + "ID_SQLite3_UNKNOWN_DB", + "ID_SQLLITE_LOGGER", + "ID_NAT_TYPE_DETECTION_REQUEST", + "ID_NAT_TYPE_DETECTION_RESULT", + "ID_ROUTER_2_INTERNAL", + "ID_ROUTER_2_FORWARDING_NO_PATH", + "ID_ROUTER_2_FORWARDING_ESTABLISHED", + "ID_ROUTER_2_REROUTED", + "ID_TEAM_BALANCER_INTERNAL", + "ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING", + "ID_TEAM_BALANCER_TEAMS_LOCKED", + "ID_TEAM_BALANCER_TEAM_ASSIGNED", + "ID_LIGHTSPEED_INTEGRATION", + "ID_XBOX_LOBBY", + "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS", + "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS", + "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE", + "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE", + "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT", + "ID_TWO_WAY_AUTHENTICATION_NEGOTIATION", + "ID_CLOUD_POST_REQUEST", + "ID_CLOUD_RELEASE_REQUEST", + "ID_CLOUD_GET_REQUEST", + "ID_CLOUD_GET_RESPONSE", + "ID_CLOUD_UNSUBSCRIBE_REQUEST", + "ID_CLOUD_SERVER_TO_SERVER_COMMAND", + "ID_CLOUD_SUBSCRIPTION_NOTIFICATION", + "ID_RESERVED_1", + "ID_RESERVED_2", + "ID_RESERVED_3", + "ID_RESERVED_4", + "ID_RESERVED_5", + "ID_RESERVED_6", + "ID_RESERVED_7", + "ID_RESERVED_8", + "ID_RESERVED_9", + "ID_USER_PACKET_ENUM", + }; + + return (char*)IDTable[Id]; +} +const char* PacketLogger::UserIDTOString(unsigned char Id) +{ + // Users should override this + static char str[256]; + Itoa(Id, str, 10); + return (const char*) str; +} +const char* PacketLogger::IDTOString(unsigned char Id) +{ + const char *out; + out=BaseIDTOString(Id); + if (out) + return out; + return UserIDTOString(Id); +} +void PacketLogger::SetPrefix(const char *_prefix) +{ + strncpy(prefix, _prefix, 255); + prefix[255]=0; +} +void PacketLogger::SetSuffix(const char *_suffix) +{ + strncpy(suffix, _suffix, 255); + suffix[255]=0; +} +void PacketLogger::GetLocalTime(char buffer[128]) +{ +#if defined(_WIN32) && !defined(__GNUC__) && !defined(__GCCXML__) + time_t rawtime; + struct timeval tv; + // If you get an arror about an incomplete type, just delete this file + struct timezone tz; + gettimeofday(&tv, &tz); + // time ( &rawtime ); + rawtime=tv.tv_sec; + + struct tm * timeinfo; + timeinfo = localtime ( &rawtime ); + strftime (buffer,128,"%x %X",timeinfo); + char buff[32]; + sprintf(buff, ".%i", tv.tv_usec); + strcat(buffer,buff); + + // Commented version puts the time first + /* + struct tm * timeinfo; + timeinfo = localtime ( &rawtime ); + strftime (buffer,128,"%X",timeinfo); + char buff[32]; + sprintf(buff, ".%i ", tv.tv_usec); + strcat(buffer,buff); + char buff2[32]; + strftime (buff2,32,"%x",timeinfo); + strcat(buffer,buff2); + */ +#else + buffer[0]=0; +#endif +} +void PacketLogger::SetLogDirectMessages(bool send) +{ + logDirectMessages=send; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PacketLogger.h b/src/raknet/PacketLogger.h index 740620f..3ad9988 100755 --- a/src/raknet/PacketLogger.h +++ b/src/raknet/PacketLogger.h @@ -1,101 +1,101 @@ -/// \file -/// \brief This will write all incoming and outgoing network messages to the local console screen. See derived functions for other outputs -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __PACKET_LOGGER_H -#define __PACKET_LOGGER_H - -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \defgroup PACKETLOGGER_GROUP PacketLogger -/// \brief Print out incoming messages to a target destination -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief Writes incoming and outgoing messages to the screen. -/// This will write all incoming and outgoing messages to the console window, or to a file if you override it and give it this functionality. -/// \ingroup PACKETLOGGER_GROUP -class RAK_DLL_EXPORT PacketLogger : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(PacketLogger) - - PacketLogger(); - virtual ~PacketLogger(); - - // Translate the supplied parameters into an output line - overloaded version that takes a MessageIdentifier - // and translates it into a string (numeric or textual representation based on printId); this calls the - // second version which takes a const char* argument for the messageIdentifier - virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame - , unsigned char messageIdentifier, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, - unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); - virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame - , const char* idToPrint, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, - unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); - - /// Events on low level sends and receives. These functions may be called from different threads at the same time. - virtual void OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); - virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); - virtual void OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); - virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend); - virtual void OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time); - virtual void OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); - - /// Logs out a header for all the data - virtual void LogHeader(void); - - /// Override this to log strings to wherever. Log should be threadsafe - virtual void WriteLog(const char *str); - - // Write informational messages - virtual void WriteMiscellaneous(const char *type, const char *msg); - - - // Set to true to print ID_* instead of numbers - virtual void SetPrintID(bool print); - // Print or hide acks (clears up the screen not to print them but is worse for debugging) - virtual void SetPrintAcks(bool print); - - /// Prepend this string to output logs. - virtual void SetPrefix(const char *_prefix); - - /// Append this string to output logs. (newline is useful here) - virtual void SetSuffix(const char *_suffix); - static const char* BaseIDTOString(unsigned char Id); - - /// Log the direct sends and receives or not. Default true - void SetLogDirectMessages(bool send); -protected: - - virtual bool UsesReliabilityLayer(void) const {return true;} - const char* IDTOString(unsigned char Id); - virtual void AddToLog(const char *str); - // Users should override this - virtual const char* UserIDTOString(unsigned char Id); - void GetLocalTime(char buffer[128]); - bool logDirectMessages; - - bool printId, printAcks; - char prefix[256]; - char suffix[256]; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief This will write all incoming and outgoing network messages to the local console screen. See derived functions for other outputs +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __PACKET_LOGGER_H +#define __PACKET_LOGGER_H + +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \defgroup PACKETLOGGER_GROUP PacketLogger +/// \brief Print out incoming messages to a target destination +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief Writes incoming and outgoing messages to the screen. +/// This will write all incoming and outgoing messages to the console window, or to a file if you override it and give it this functionality. +/// \ingroup PACKETLOGGER_GROUP +class RAK_DLL_EXPORT PacketLogger : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(PacketLogger) + + PacketLogger(); + virtual ~PacketLogger(); + + // Translate the supplied parameters into an output line - overloaded version that takes a MessageIdentifier + // and translates it into a string (numeric or textual representation based on printId); this calls the + // second version which takes a const char* argument for the messageIdentifier + virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame + , unsigned char messageIdentifier, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, + unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); + virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int reliableMessageNumber, unsigned int frame + , const char* idToPrint, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, + unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); + + /// Events on low level sends and receives. These functions may be called from different threads at the same time. + virtual void OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + virtual void OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend); + virtual void OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time); + virtual void OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + + /// Logs out a header for all the data + virtual void LogHeader(void); + + /// Override this to log strings to wherever. Log should be threadsafe + virtual void WriteLog(const char *str); + + // Write informational messages + virtual void WriteMiscellaneous(const char *type, const char *msg); + + + // Set to true to print ID_* instead of numbers + virtual void SetPrintID(bool print); + // Print or hide acks (clears up the screen not to print them but is worse for debugging) + virtual void SetPrintAcks(bool print); + + /// Prepend this string to output logs. + virtual void SetPrefix(const char *_prefix); + + /// Append this string to output logs. (newline is useful here) + virtual void SetSuffix(const char *_suffix); + static const char* BaseIDTOString(unsigned char Id); + + /// Log the direct sends and receives or not. Default true + void SetLogDirectMessages(bool send); +protected: + + virtual bool UsesReliabilityLayer(void) const {return true;} + const char* IDTOString(unsigned char Id); + virtual void AddToLog(const char *str); + // Users should override this + virtual const char* UserIDTOString(unsigned char Id); + void GetLocalTime(char buffer[128]); + bool logDirectMessages; + + bool printId, printAcks; + char prefix[256]; + char suffix[256]; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PacketOutputWindowLogger.cpp b/src/raknet/PacketOutputWindowLogger.cpp index b5ffb5f..7f2d3aa 100755 --- a/src/raknet/PacketOutputWindowLogger.cpp +++ b/src/raknet/PacketOutputWindowLogger.cpp @@ -1,39 +1,39 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#if defined(UNICODE) -#include "RakWString.h" -#endif - -#include "PacketOutputWindowLogger.h" -#include "RakString.h" -#if defined(_WIN32) && !defined(X360__) -#include "WindowsIncludes.h" -#endif - -using namespace RakNet; - -PacketOutputWindowLogger::PacketOutputWindowLogger() -{ -} -PacketOutputWindowLogger::~PacketOutputWindowLogger() -{ -} -void PacketOutputWindowLogger::WriteLog(const char *str) -{ -#if defined(_WIN32) && !defined(X360__) - - #if defined(UNICODE) - RakNet::RakWString str2 = str; - str2+="\n"; - OutputDebugString(str2.C_String()); - #else - RakNet::RakString str2 = str; - str2+="\n"; - OutputDebugString(str2.C_String()); - #endif - -#endif -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#if defined(UNICODE) +#include "RakWString.h" +#endif + +#include "PacketOutputWindowLogger.h" +#include "RakString.h" +#if defined(_WIN32) && !defined(X360__) +#include "WindowsIncludes.h" +#endif + +using namespace RakNet; + +PacketOutputWindowLogger::PacketOutputWindowLogger() +{ +} +PacketOutputWindowLogger::~PacketOutputWindowLogger() +{ +} +void PacketOutputWindowLogger::WriteLog(const char *str) +{ +#if defined(_WIN32) && !defined(X360__) + + #if defined(UNICODE) + RakNet::RakWString str2 = str; + str2+="\n"; + OutputDebugString(str2.C_String()); + #else + RakNet::RakString str2 = str; + str2+="\n"; + OutputDebugString(str2.C_String()); + #endif + +#endif +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PacketOutputWindowLogger.h b/src/raknet/PacketOutputWindowLogger.h index f2655a0..a1dd760 100755 --- a/src/raknet/PacketOutputWindowLogger.h +++ b/src/raknet/PacketOutputWindowLogger.h @@ -1,34 +1,34 @@ -/// \file -/// \brief This will write all incoming and outgoing network messages to a file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __PACKET_OUTPUT_WINDOW_LOGGER_H_ -#define __PACKET_OUTPUT_WINDOW_LOGGER_H_ - -#include "PacketLogger.h" - -namespace RakNet -{ - -/// \ingroup PACKETLOGGER_GROUP -/// \brief Packetlogger that outputs to the output window in the debugger. Windows only. -class RAK_DLL_EXPORT PacketOutputWindowLogger : public PacketLogger -{ -public: - PacketOutputWindowLogger(); - virtual ~PacketOutputWindowLogger(); - virtual void WriteLog(const char *str); -protected: -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief This will write all incoming and outgoing network messages to a file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __PACKET_OUTPUT_WINDOW_LOGGER_H_ +#define __PACKET_OUTPUT_WINDOW_LOGGER_H_ + +#include "PacketLogger.h" + +namespace RakNet +{ + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Packetlogger that outputs to the output window in the debugger. Windows only. +class RAK_DLL_EXPORT PacketOutputWindowLogger : public PacketLogger +{ +public: + PacketOutputWindowLogger(); + virtual ~PacketOutputWindowLogger(); + virtual void WriteLog(const char *str); +protected: +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PacketPriority.h b/src/raknet/PacketPriority.h index 83bcd57..ff84dab 100755 --- a/src/raknet/PacketPriority.h +++ b/src/raknet/PacketPriority.h @@ -1,79 +1,79 @@ -/// \file -/// \brief This file contains enumerations for packet priority and reliability enumerations. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __PACKET_PRIORITY_H -#define __PACKET_PRIORITY_H - -/// These enumerations are used to describe when packets are delivered. -enum PacketPriority -{ - /// The highest possible priority. These message trigger sends immediately, and are generally not buffered or aggregated into a single datagram. - IMMEDIATE_PRIORITY, - - /// For every 2 IMMEDIATE_PRIORITY messages, 1 HIGH_PRIORITY will be sent. - /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. - HIGH_PRIORITY, - - /// For every 2 HIGH_PRIORITY messages, 1 MEDIUM_PRIORITY will be sent. - /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. - MEDIUM_PRIORITY, - - /// For every 2 MEDIUM_PRIORITY messages, 1 LOW_PRIORITY will be sent. - /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. - LOW_PRIORITY, - - /// \internal - NUMBER_OF_PRIORITIES -}; - -/// These enumerations are used to describe how packets are delivered. -/// \note Note to self: I write this with 3 bits in the stream. If I add more remember to change that -/// \note In ReliabilityLayer::WriteToBitStreamFromInternalPacket I assume there are 5 major types -/// \note Do not reorder, I check on >= UNRELIABLE_WITH_ACK_RECEIPT -enum PacketReliability -{ - /// Same as regular UDP, except that it will also discard duplicate datagrams. RakNet adds (6 to 17) + 21 bits of overhead, 16 of which is used to detect duplicate packets and 6 to 17 of which is used for message length. - UNRELIABLE, - - /// Regular UDP with a sequence counter. Out of order messages will be discarded. - /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. - UNRELIABLE_SEQUENCED, - - /// The message is sent reliably, but not necessarily in any order. Same overhead as UNRELIABLE. - RELIABLE, - - /// This message is reliable and will arrive in the order you sent it. Messages will be delayed while waiting for out of order messages. Same overhead as UNRELIABLE_SEQUENCED. - /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. - RELIABLE_ORDERED, - - /// This message is reliable and will arrive in the sequence you sent it. Out or order messages will be dropped. Same overhead as UNRELIABLE_SEQUENCED. - /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. - RELIABLE_SEQUENCED, - - /// Same as UNRELIABLE, however the user will get either ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS based on the result of sending this message when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. - UNRELIABLE_WITH_ACK_RECEIPT, - - /// Same as UNRELIABLE_SEQUENCED, however the user will get either ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS based on the result of sending this message when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. - /// 05/04/10 You can't have sequenced and ack receipts, because you don't know if the other system discarded the message, meaning you don't know if the message was processed - // UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT, - - /// Same as RELIABLE. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). ID_SND_RECEIPT_ACKED is returned when the message arrives, not necessarily the order when it was sent. Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. This does not return ID_SND_RECEIPT_LOSS. - RELIABLE_WITH_ACK_RECEIPT, - - /// Same as RELIABLE_ORDERED_ACK_RECEIPT. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). ID_SND_RECEIPT_ACKED is returned when the message arrives, not necessarily the order when it was sent. Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. This does not return ID_SND_RECEIPT_LOSS. - RELIABLE_ORDERED_WITH_ACK_RECEIPT, - - /// Same as RELIABLE_SEQUENCED. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. - /// 05/04/10 You can't have sequenced and ack receipts, because you don't know if the other system discarded the message, meaning you don't know if the message was processed - // RELIABLE_SEQUENCED_WITH_ACK_RECEIPT, - - /// \internal - NUMBER_OF_RELIABILITIES -}; - -#endif +/// \file +/// \brief This file contains enumerations for packet priority and reliability enumerations. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __PACKET_PRIORITY_H +#define __PACKET_PRIORITY_H + +/// These enumerations are used to describe when packets are delivered. +enum PacketPriority +{ + /// The highest possible priority. These message trigger sends immediately, and are generally not buffered or aggregated into a single datagram. + IMMEDIATE_PRIORITY, + + /// For every 2 IMMEDIATE_PRIORITY messages, 1 HIGH_PRIORITY will be sent. + /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. + HIGH_PRIORITY, + + /// For every 2 HIGH_PRIORITY messages, 1 MEDIUM_PRIORITY will be sent. + /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. + MEDIUM_PRIORITY, + + /// For every 2 MEDIUM_PRIORITY messages, 1 LOW_PRIORITY will be sent. + /// Messages at this priority and lower are buffered to be sent in groups at 10 millisecond intervals to reduce UDP overhead and better measure congestion control. + LOW_PRIORITY, + + /// \internal + NUMBER_OF_PRIORITIES +}; + +/// These enumerations are used to describe how packets are delivered. +/// \note Note to self: I write this with 3 bits in the stream. If I add more remember to change that +/// \note In ReliabilityLayer::WriteToBitStreamFromInternalPacket I assume there are 5 major types +/// \note Do not reorder, I check on >= UNRELIABLE_WITH_ACK_RECEIPT +enum PacketReliability +{ + /// Same as regular UDP, except that it will also discard duplicate datagrams. RakNet adds (6 to 17) + 21 bits of overhead, 16 of which is used to detect duplicate packets and 6 to 17 of which is used for message length. + UNRELIABLE, + + /// Regular UDP with a sequence counter. Out of order messages will be discarded. + /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. + UNRELIABLE_SEQUENCED, + + /// The message is sent reliably, but not necessarily in any order. Same overhead as UNRELIABLE. + RELIABLE, + + /// This message is reliable and will arrive in the order you sent it. Messages will be delayed while waiting for out of order messages. Same overhead as UNRELIABLE_SEQUENCED. + /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. + RELIABLE_ORDERED, + + /// This message is reliable and will arrive in the sequence you sent it. Out or order messages will be dropped. Same overhead as UNRELIABLE_SEQUENCED. + /// Sequenced and ordered messages sent on the same channel will arrive in the order sent. + RELIABLE_SEQUENCED, + + /// Same as UNRELIABLE, however the user will get either ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS based on the result of sending this message when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. + UNRELIABLE_WITH_ACK_RECEIPT, + + /// Same as UNRELIABLE_SEQUENCED, however the user will get either ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS based on the result of sending this message when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. + /// 05/04/10 You can't have sequenced and ack receipts, because you don't know if the other system discarded the message, meaning you don't know if the message was processed + // UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT, + + /// Same as RELIABLE. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). ID_SND_RECEIPT_ACKED is returned when the message arrives, not necessarily the order when it was sent. Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. This does not return ID_SND_RECEIPT_LOSS. + RELIABLE_WITH_ACK_RECEIPT, + + /// Same as RELIABLE_ORDERED_ACK_RECEIPT. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). ID_SND_RECEIPT_ACKED is returned when the message arrives, not necessarily the order when it was sent. Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. This does not return ID_SND_RECEIPT_LOSS. + RELIABLE_ORDERED_WITH_ACK_RECEIPT, + + /// Same as RELIABLE_SEQUENCED. The user will also get ID_SND_RECEIPT_ACKED after the message is delivered when calling RakPeerInterface::Receive(). Bytes 1-4 will contain the number returned from the Send() function. On disconnect or shutdown, all messages not previously acked should be considered lost. + /// 05/04/10 You can't have sequenced and ack receipts, because you don't know if the other system discarded the message, meaning you don't know if the message was processed + // RELIABLE_SEQUENCED_WITH_ACK_RECEIPT, + + /// \internal + NUMBER_OF_RELIABILITIES +}; + +#endif diff --git a/src/raknet/PacketizedTCP.cpp b/src/raknet/PacketizedTCP.cpp index bffab02..b5f4fe5 100755 --- a/src/raknet/PacketizedTCP.cpp +++ b/src/raknet/PacketizedTCP.cpp @@ -1,408 +1,408 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "PacketizedTCP.h" -#include "NativeTypes.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakAlloca.h" - -using namespace RakNet; - -typedef uint32_t PTCPHeader; - -STATIC_FACTORY_DEFINITIONS(PacketizedTCP,PacketizedTCP); - -PacketizedTCP::PacketizedTCP() -{ - -} -PacketizedTCP::~PacketizedTCP() -{ - ClearAllConnections(); -} - -bool PacketizedTCP::Start(unsigned short port, unsigned short maxIncomingConnections, int threadPriority, unsigned short socketFamily) -{ - bool success = TCPInterface::Start(port, maxIncomingConnections,0,threadPriority, socketFamily); - if (success) - { - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnRakPeerStartup(); - } - return success; -} - -void PacketizedTCP::Stop(void) -{ - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnRakPeerShutdown(); - for (i=0; i < waitingPackets.Size(); i++) - DeallocatePacket(waitingPackets[i]); - TCPInterface::Stop(); - ClearAllConnections(); -} - -void PacketizedTCP::Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ) -{ - PTCPHeader dataLength; - dataLength=length; -#ifndef __BITSTREAM_NATIVE_END - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytes((unsigned char*) &length,(unsigned char*) &dataLength,sizeof(dataLength)); -#else - dataLength=length; -#endif - - unsigned int lengthsArray[2]; - const char *dataArray[2]; - dataArray[0]=(char*) &dataLength; - dataArray[1]=data; - lengthsArray[0]=sizeof(dataLength); - lengthsArray[1]=length; - TCPInterface::SendList(dataArray,lengthsArray,2,systemAddress,broadcast); -} -bool PacketizedTCP::SendList( const char **data, const int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ) -{ - if (isStarted==false) - return false; - if (data==0) - return false; - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS && broadcast==false) - return false; - PTCPHeader totalLengthOfUserData=0; - int i; - for (i=0; i < numParameters; i++) - { - if (lengths[i]>0) - totalLengthOfUserData+=lengths[i]; - } - if (totalLengthOfUserData==0) - return false; - - PTCPHeader dataLength; -#ifndef __BITSTREAM_NATIVE_END - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytes((unsigned char*) &totalLengthOfUserData,(unsigned char*) &dataLength,sizeof(dataLength)); -#else - dataLength=totalLengthOfUserData; -#endif - - - unsigned int lengthsArray[512]; - const char *dataArray[512]; - dataArray[0]=(char*) &dataLength; - lengthsArray[0]=sizeof(dataLength); - for (int i=0; i < 512 && i < numParameters; i++) - { - dataArray[i+1]=data[i]; - lengthsArray[i+1]=lengths[i]; - } - return TCPInterface::SendList(dataArray,lengthsArray,numParameters+1,systemAddress,broadcast); -} -void PacketizedTCP::PushNotificationsToQueues(void) -{ - SystemAddress sa; - sa = TCPInterface::HasNewIncomingConnection(); - if (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - _newIncomingConnections.Push(sa, _FILE_AND_LINE_ ); - AddToConnectionList(sa); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnNewConnection(sa, UNASSIGNED_RAKNET_GUID, true); - } - - sa = TCPInterface::HasFailedConnectionAttempt(); - if (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - _failedConnectionAttempts.Push(sa, _FILE_AND_LINE_ ); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - { - Packet p; - p.systemAddress=sa; - p.data=0; - p.length=0; - p.bitSize=0; - messageHandlerList[i]->OnFailedConnectionAttempt(&p, FCAR_CONNECTION_ATTEMPT_FAILED); - } - } - - sa = TCPInterface::HasLostConnection(); - if (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - _lostConnections.Push(sa, _FILE_AND_LINE_ ); - RemoveFromConnectionList(sa); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnClosedConnection(sa, UNASSIGNED_RAKNET_GUID, LCR_DISCONNECTION_NOTIFICATION); - } - - sa = TCPInterface::HasCompletedConnectionAttempt(); - if (sa!=UNASSIGNED_SYSTEM_ADDRESS) - { - _completedConnectionAttempts.Push(sa, _FILE_AND_LINE_ ); - AddToConnectionList(sa); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnNewConnection(sa, UNASSIGNED_RAKNET_GUID, true); - } -} -Packet* PacketizedTCP::Receive( void ) -{ - PushNotificationsToQueues(); - - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->Update(); - - Packet *outgoingPacket=ReturnOutgoingPacket(); - if (outgoingPacket) - return outgoingPacket; - - Packet *incomingPacket; - incomingPacket = TCPInterface::Receive(); - unsigned int index; - - while (incomingPacket) - { - if (connections.Has(incomingPacket->systemAddress)) - index = connections.GetIndexAtKey(incomingPacket->systemAddress); - else - index=(unsigned int) -1; - if ((unsigned int)index==(unsigned int)-1) - { - DeallocatePacket(incomingPacket); - incomingPacket = TCPInterface::Receive(); - continue; - } - - - if (incomingPacket->deleteData==true) - { - // Came from network - SystemAddress systemAddressFromPacket; - if (index < connections.Size()) - { - DataStructures::ByteQueue *bq = connections[index]; - // Buffer data - bq->WriteBytes((const char*) incomingPacket->data,incomingPacket->length, _FILE_AND_LINE_); - systemAddressFromPacket=incomingPacket->systemAddress; - PTCPHeader dataLength; - - // Peek the header to see if a full message is waiting - bq->ReadBytes((char*) &dataLength,sizeof(PTCPHeader),true); - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &dataLength,sizeof(dataLength)); - // Header indicates packet length. If enough data is available, read out and return one packet - if (bq->GetBytesWritten()>=dataLength+sizeof(PTCPHeader)) - { - do - { - bq->IncrementReadOffset(sizeof(PTCPHeader)); - outgoingPacket = RakNet::OP_NEW(_FILE_AND_LINE_); - outgoingPacket->length=dataLength; - outgoingPacket->bitSize=BYTES_TO_BITS(dataLength); - outgoingPacket->guid=UNASSIGNED_RAKNET_GUID; - outgoingPacket->systemAddress=systemAddressFromPacket; - outgoingPacket->deleteData=false; // Did not come from the network - outgoingPacket->data=(unsigned char*) rakMalloc_Ex(dataLength, _FILE_AND_LINE_); - if (outgoingPacket->data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - RakNet::OP_DELETE(outgoingPacket,_FILE_AND_LINE_); - return 0; - } - bq->ReadBytes((char*) outgoingPacket->data,dataLength,false); - - waitingPackets.Push(outgoingPacket, _FILE_AND_LINE_ ); - - // Peek the header to see if a full message is waiting - if (bq->ReadBytes((char*) &dataLength,sizeof(PTCPHeader),true)) - { - if (RakNet::BitStream::DoEndianSwap()) - RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &dataLength,sizeof(dataLength)); - } - else - break; - } while (bq->GetBytesWritten()>=dataLength+sizeof(PTCPHeader)); - } - else - { - - unsigned int oldWritten = bq->GetBytesWritten()-incomingPacket->length; - unsigned int newWritten = bq->GetBytesWritten(); - - // Return ID_DOWNLOAD_PROGRESS - if (newWritten/65536!=oldWritten/65536) - { - outgoingPacket = RakNet::OP_NEW(_FILE_AND_LINE_); - outgoingPacket->length=sizeof(MessageID) + - sizeof(unsigned int)*2 + - sizeof(unsigned int) + - 65536; - outgoingPacket->bitSize=BYTES_TO_BITS(incomingPacket->length); - outgoingPacket->guid=UNASSIGNED_RAKNET_GUID; - outgoingPacket->systemAddress=incomingPacket->systemAddress; - outgoingPacket->deleteData=false; - outgoingPacket->data=(unsigned char*) rakMalloc_Ex(outgoingPacket->length, _FILE_AND_LINE_); - if (outgoingPacket->data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - RakNet::OP_DELETE(outgoingPacket,_FILE_AND_LINE_); - return 0; - } - - outgoingPacket->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; - unsigned int totalParts=dataLength/65536; - unsigned int partIndex=newWritten/65536; - unsigned int oneChunkSize=65536; - memcpy(outgoingPacket->data+sizeof(MessageID), &partIndex, sizeof(unsigned int)); - memcpy(outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*1, &totalParts, sizeof(unsigned int)); - memcpy(outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*2, &oneChunkSize, sizeof(unsigned int)); - bq->IncrementReadOffset(sizeof(PTCPHeader)); - bq->ReadBytes((char*) outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*3,oneChunkSize,true); - bq->DecrementReadOffset(sizeof(PTCPHeader)); - - waitingPackets.Push(outgoingPacket, _FILE_AND_LINE_ ); - } - } - - } - - DeallocatePacket(incomingPacket); - incomingPacket=0; - } - else - waitingPackets.Push(incomingPacket, _FILE_AND_LINE_ ); - - incomingPacket = TCPInterface::Receive(); - } - - return ReturnOutgoingPacket(); -} -Packet *PacketizedTCP::ReturnOutgoingPacket(void) -{ - Packet *outgoingPacket=0; - unsigned int i; - while (outgoingPacket==0 && waitingPackets.IsEmpty()==false) - { - outgoingPacket=waitingPackets.Pop(); - PluginReceiveResult pluginResult; - for (i=0; i < messageHandlerList.Size(); i++) - { - pluginResult=messageHandlerList[i]->OnReceive(outgoingPacket); - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( outgoingPacket ); - outgoingPacket=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - outgoingPacket=0; - break; - } - } - } - - return outgoingPacket; -} - -void PacketizedTCP::AttachPlugin( PluginInterface2 *plugin ) -{ - if (messageHandlerList.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - messageHandlerList.Insert(plugin, _FILE_AND_LINE_); - plugin->SetPacketizedTCP(this); - plugin->OnAttach(); - } -} -void PacketizedTCP::DetachPlugin( PluginInterface2 *plugin ) -{ - if (plugin==0) - return; - - unsigned int index; - index = messageHandlerList.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - messageHandlerList[index]->OnDetach(); - // Unordered list so delete from end for speed - messageHandlerList[index]=messageHandlerList[messageHandlerList.Size()-1]; - messageHandlerList.RemoveFromEnd(); - plugin->SetPacketizedTCP(0); - } -} -void PacketizedTCP::CloseConnection( SystemAddress systemAddress ) -{ - RemoveFromConnectionList(systemAddress); - unsigned int i; - for (i=0; i < messageHandlerList.Size(); i++) - messageHandlerList[i]->OnClosedConnection(systemAddress, UNASSIGNED_RAKNET_GUID, LCR_CLOSED_BY_USER); - TCPInterface::CloseConnection(systemAddress); -} -void PacketizedTCP::RemoveFromConnectionList(const SystemAddress &sa) -{ - if (sa==UNASSIGNED_SYSTEM_ADDRESS) - return; - if (connections.Has(sa)) - { - unsigned int index = connections.GetIndexAtKey(sa); - if (index!=(unsigned int)-1) - { - RakNet::OP_DELETE(connections[index],_FILE_AND_LINE_); - connections.RemoveAtIndex(index); - } - } -} -void PacketizedTCP::AddToConnectionList(const SystemAddress &sa) -{ - if (sa==UNASSIGNED_SYSTEM_ADDRESS) - return; - connections.SetNew(sa, RakNet::OP_NEW(_FILE_AND_LINE_)); -} -void PacketizedTCP::ClearAllConnections(void) -{ - unsigned int i; - for (i=0; i < connections.Size(); i++) - RakNet::OP_DELETE(connections[i],_FILE_AND_LINE_); - connections.Clear(); -} -SystemAddress PacketizedTCP::HasCompletedConnectionAttempt(void) -{ - PushNotificationsToQueues(); - - if (_completedConnectionAttempts.IsEmpty()==false) - return _completedConnectionAttempts.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -SystemAddress PacketizedTCP::HasFailedConnectionAttempt(void) -{ - PushNotificationsToQueues(); - - if (_failedConnectionAttempts.IsEmpty()==false) - return _failedConnectionAttempts.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -SystemAddress PacketizedTCP::HasNewIncomingConnection(void) -{ - PushNotificationsToQueues(); - - if (_newIncomingConnections.IsEmpty()==false) - return _newIncomingConnections.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -SystemAddress PacketizedTCP::HasLostConnection(void) -{ - PushNotificationsToQueues(); - - if (_lostConnections.IsEmpty()==false) - return _lostConnections.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "PacketizedTCP.h" +#include "NativeTypes.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakAlloca.h" + +using namespace RakNet; + +typedef uint32_t PTCPHeader; + +STATIC_FACTORY_DEFINITIONS(PacketizedTCP,PacketizedTCP); + +PacketizedTCP::PacketizedTCP() +{ + +} +PacketizedTCP::~PacketizedTCP() +{ + ClearAllConnections(); +} + +bool PacketizedTCP::Start(unsigned short port, unsigned short maxIncomingConnections, int threadPriority, unsigned short socketFamily) +{ + bool success = TCPInterface::Start(port, maxIncomingConnections,0,threadPriority, socketFamily); + if (success) + { + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnRakPeerStartup(); + } + return success; +} + +void PacketizedTCP::Stop(void) +{ + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnRakPeerShutdown(); + for (i=0; i < waitingPackets.Size(); i++) + DeallocatePacket(waitingPackets[i]); + TCPInterface::Stop(); + ClearAllConnections(); +} + +void PacketizedTCP::Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ) +{ + PTCPHeader dataLength; + dataLength=length; +#ifndef __BITSTREAM_NATIVE_END + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytes((unsigned char*) &length,(unsigned char*) &dataLength,sizeof(dataLength)); +#else + dataLength=length; +#endif + + unsigned int lengthsArray[2]; + const char *dataArray[2]; + dataArray[0]=(char*) &dataLength; + dataArray[1]=data; + lengthsArray[0]=sizeof(dataLength); + lengthsArray[1]=length; + TCPInterface::SendList(dataArray,lengthsArray,2,systemAddress,broadcast); +} +bool PacketizedTCP::SendList( const char **data, const int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ) +{ + if (isStarted==false) + return false; + if (data==0) + return false; + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS && broadcast==false) + return false; + PTCPHeader totalLengthOfUserData=0; + int i; + for (i=0; i < numParameters; i++) + { + if (lengths[i]>0) + totalLengthOfUserData+=lengths[i]; + } + if (totalLengthOfUserData==0) + return false; + + PTCPHeader dataLength; +#ifndef __BITSTREAM_NATIVE_END + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytes((unsigned char*) &totalLengthOfUserData,(unsigned char*) &dataLength,sizeof(dataLength)); +#else + dataLength=totalLengthOfUserData; +#endif + + + unsigned int lengthsArray[512]; + const char *dataArray[512]; + dataArray[0]=(char*) &dataLength; + lengthsArray[0]=sizeof(dataLength); + for (int i=0; i < 512 && i < numParameters; i++) + { + dataArray[i+1]=data[i]; + lengthsArray[i+1]=lengths[i]; + } + return TCPInterface::SendList(dataArray,lengthsArray,numParameters+1,systemAddress,broadcast); +} +void PacketizedTCP::PushNotificationsToQueues(void) +{ + SystemAddress sa; + sa = TCPInterface::HasNewIncomingConnection(); + if (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + _newIncomingConnections.Push(sa, _FILE_AND_LINE_ ); + AddToConnectionList(sa); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnNewConnection(sa, UNASSIGNED_RAKNET_GUID, true); + } + + sa = TCPInterface::HasFailedConnectionAttempt(); + if (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + _failedConnectionAttempts.Push(sa, _FILE_AND_LINE_ ); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + { + Packet p; + p.systemAddress=sa; + p.data=0; + p.length=0; + p.bitSize=0; + messageHandlerList[i]->OnFailedConnectionAttempt(&p, FCAR_CONNECTION_ATTEMPT_FAILED); + } + } + + sa = TCPInterface::HasLostConnection(); + if (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + _lostConnections.Push(sa, _FILE_AND_LINE_ ); + RemoveFromConnectionList(sa); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnClosedConnection(sa, UNASSIGNED_RAKNET_GUID, LCR_DISCONNECTION_NOTIFICATION); + } + + sa = TCPInterface::HasCompletedConnectionAttempt(); + if (sa!=UNASSIGNED_SYSTEM_ADDRESS) + { + _completedConnectionAttempts.Push(sa, _FILE_AND_LINE_ ); + AddToConnectionList(sa); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnNewConnection(sa, UNASSIGNED_RAKNET_GUID, true); + } +} +Packet* PacketizedTCP::Receive( void ) +{ + PushNotificationsToQueues(); + + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->Update(); + + Packet *outgoingPacket=ReturnOutgoingPacket(); + if (outgoingPacket) + return outgoingPacket; + + Packet *incomingPacket; + incomingPacket = TCPInterface::Receive(); + unsigned int index; + + while (incomingPacket) + { + if (connections.Has(incomingPacket->systemAddress)) + index = connections.GetIndexAtKey(incomingPacket->systemAddress); + else + index=(unsigned int) -1; + if ((unsigned int)index==(unsigned int)-1) + { + DeallocatePacket(incomingPacket); + incomingPacket = TCPInterface::Receive(); + continue; + } + + + if (incomingPacket->deleteData==true) + { + // Came from network + SystemAddress systemAddressFromPacket; + if (index < connections.Size()) + { + DataStructures::ByteQueue *bq = connections[index]; + // Buffer data + bq->WriteBytes((const char*) incomingPacket->data,incomingPacket->length, _FILE_AND_LINE_); + systemAddressFromPacket=incomingPacket->systemAddress; + PTCPHeader dataLength; + + // Peek the header to see if a full message is waiting + bq->ReadBytes((char*) &dataLength,sizeof(PTCPHeader),true); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &dataLength,sizeof(dataLength)); + // Header indicates packet length. If enough data is available, read out and return one packet + if (bq->GetBytesWritten()>=dataLength+sizeof(PTCPHeader)) + { + do + { + bq->IncrementReadOffset(sizeof(PTCPHeader)); + outgoingPacket = RakNet::OP_NEW(_FILE_AND_LINE_); + outgoingPacket->length=dataLength; + outgoingPacket->bitSize=BYTES_TO_BITS(dataLength); + outgoingPacket->guid=UNASSIGNED_RAKNET_GUID; + outgoingPacket->systemAddress=systemAddressFromPacket; + outgoingPacket->deleteData=false; // Did not come from the network + outgoingPacket->data=(unsigned char*) rakMalloc_Ex(dataLength, _FILE_AND_LINE_); + if (outgoingPacket->data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + RakNet::OP_DELETE(outgoingPacket,_FILE_AND_LINE_); + return 0; + } + bq->ReadBytes((char*) outgoingPacket->data,dataLength,false); + + waitingPackets.Push(outgoingPacket, _FILE_AND_LINE_ ); + + // Peek the header to see if a full message is waiting + if (bq->ReadBytes((char*) &dataLength,sizeof(PTCPHeader),true)) + { + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &dataLength,sizeof(dataLength)); + } + else + break; + } while (bq->GetBytesWritten()>=dataLength+sizeof(PTCPHeader)); + } + else + { + + unsigned int oldWritten = bq->GetBytesWritten()-incomingPacket->length; + unsigned int newWritten = bq->GetBytesWritten(); + + // Return ID_DOWNLOAD_PROGRESS + if (newWritten/65536!=oldWritten/65536) + { + outgoingPacket = RakNet::OP_NEW(_FILE_AND_LINE_); + outgoingPacket->length=sizeof(MessageID) + + sizeof(unsigned int)*2 + + sizeof(unsigned int) + + 65536; + outgoingPacket->bitSize=BYTES_TO_BITS(incomingPacket->length); + outgoingPacket->guid=UNASSIGNED_RAKNET_GUID; + outgoingPacket->systemAddress=incomingPacket->systemAddress; + outgoingPacket->deleteData=false; + outgoingPacket->data=(unsigned char*) rakMalloc_Ex(outgoingPacket->length, _FILE_AND_LINE_); + if (outgoingPacket->data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + RakNet::OP_DELETE(outgoingPacket,_FILE_AND_LINE_); + return 0; + } + + outgoingPacket->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; + unsigned int totalParts=dataLength/65536; + unsigned int partIndex=newWritten/65536; + unsigned int oneChunkSize=65536; + memcpy(outgoingPacket->data+sizeof(MessageID), &partIndex, sizeof(unsigned int)); + memcpy(outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*1, &totalParts, sizeof(unsigned int)); + memcpy(outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*2, &oneChunkSize, sizeof(unsigned int)); + bq->IncrementReadOffset(sizeof(PTCPHeader)); + bq->ReadBytes((char*) outgoingPacket->data+sizeof(MessageID)+sizeof(unsigned int)*3,oneChunkSize,true); + bq->DecrementReadOffset(sizeof(PTCPHeader)); + + waitingPackets.Push(outgoingPacket, _FILE_AND_LINE_ ); + } + } + + } + + DeallocatePacket(incomingPacket); + incomingPacket=0; + } + else + waitingPackets.Push(incomingPacket, _FILE_AND_LINE_ ); + + incomingPacket = TCPInterface::Receive(); + } + + return ReturnOutgoingPacket(); +} +Packet *PacketizedTCP::ReturnOutgoingPacket(void) +{ + Packet *outgoingPacket=0; + unsigned int i; + while (outgoingPacket==0 && waitingPackets.IsEmpty()==false) + { + outgoingPacket=waitingPackets.Pop(); + PluginReceiveResult pluginResult; + for (i=0; i < messageHandlerList.Size(); i++) + { + pluginResult=messageHandlerList[i]->OnReceive(outgoingPacket); + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( outgoingPacket ); + outgoingPacket=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + outgoingPacket=0; + break; + } + } + } + + return outgoingPacket; +} + +void PacketizedTCP::AttachPlugin( PluginInterface2 *plugin ) +{ + if (messageHandlerList.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + messageHandlerList.Insert(plugin, _FILE_AND_LINE_); + plugin->SetPacketizedTCP(this); + plugin->OnAttach(); + } +} +void PacketizedTCP::DetachPlugin( PluginInterface2 *plugin ) +{ + if (plugin==0) + return; + + unsigned int index; + index = messageHandlerList.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + messageHandlerList[index]->OnDetach(); + // Unordered list so delete from end for speed + messageHandlerList[index]=messageHandlerList[messageHandlerList.Size()-1]; + messageHandlerList.RemoveFromEnd(); + plugin->SetPacketizedTCP(0); + } +} +void PacketizedTCP::CloseConnection( SystemAddress systemAddress ) +{ + RemoveFromConnectionList(systemAddress); + unsigned int i; + for (i=0; i < messageHandlerList.Size(); i++) + messageHandlerList[i]->OnClosedConnection(systemAddress, UNASSIGNED_RAKNET_GUID, LCR_CLOSED_BY_USER); + TCPInterface::CloseConnection(systemAddress); +} +void PacketizedTCP::RemoveFromConnectionList(const SystemAddress &sa) +{ + if (sa==UNASSIGNED_SYSTEM_ADDRESS) + return; + if (connections.Has(sa)) + { + unsigned int index = connections.GetIndexAtKey(sa); + if (index!=(unsigned int)-1) + { + RakNet::OP_DELETE(connections[index],_FILE_AND_LINE_); + connections.RemoveAtIndex(index); + } + } +} +void PacketizedTCP::AddToConnectionList(const SystemAddress &sa) +{ + if (sa==UNASSIGNED_SYSTEM_ADDRESS) + return; + connections.SetNew(sa, RakNet::OP_NEW(_FILE_AND_LINE_)); +} +void PacketizedTCP::ClearAllConnections(void) +{ + unsigned int i; + for (i=0; i < connections.Size(); i++) + RakNet::OP_DELETE(connections[i],_FILE_AND_LINE_); + connections.Clear(); +} +SystemAddress PacketizedTCP::HasCompletedConnectionAttempt(void) +{ + PushNotificationsToQueues(); + + if (_completedConnectionAttempts.IsEmpty()==false) + return _completedConnectionAttempts.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +SystemAddress PacketizedTCP::HasFailedConnectionAttempt(void) +{ + PushNotificationsToQueues(); + + if (_failedConnectionAttempts.IsEmpty()==false) + return _failedConnectionAttempts.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +SystemAddress PacketizedTCP::HasNewIncomingConnection(void) +{ + PushNotificationsToQueues(); + + if (_newIncomingConnections.IsEmpty()==false) + return _newIncomingConnections.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +SystemAddress PacketizedTCP::HasLostConnection(void) +{ + PushNotificationsToQueues(); + + if (_lostConnections.IsEmpty()==false) + return _lostConnections.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PacketizedTCP.h b/src/raknet/PacketizedTCP.h index 9a08fe3..986fc11 100755 --- a/src/raknet/PacketizedTCP.h +++ b/src/raknet/PacketizedTCP.h @@ -1,93 +1,93 @@ -/// \file -/// \brief A simple TCP based server allowing sends and receives. Can be connected by any TCP client, including telnet. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#ifndef __PACKETIZED_TCP -#define __PACKETIZED_TCP - -#include "TCPInterface.h" -#include "DS_ByteQueue.h" -#include "PluginInterface2.h" -#include "DS_Map.h" - -namespace RakNet -{ - -class RAK_DLL_EXPORT PacketizedTCP : public TCPInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(PacketizedTCP) - - PacketizedTCP(); - virtual ~PacketizedTCP(); - - /// Starts the TCP server on the indicated port - /// \param[in] port Which port to listen on. - /// \param[in] maxIncomingConnections Max incoming connections we will accept - /// \param[in] maxConnections Max total connections, which should be >= maxIncomingConnections - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - bool Start(unsigned short port, unsigned short maxIncomingConnections, int threadPriority=-99999, unsigned short socketFamily=AF_INET); - - /// Stops the TCP server - void Stop(void); - - /// Sends a byte stream - void Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ); - - // Sends a concatenated list of byte streams - bool SendList( const char **data, const int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ); - - /// Returns data received - Packet* Receive( void ); - - /// Disconnects a player/address - void CloseConnection( SystemAddress systemAddress ); - - /// Has a previous call to connect succeeded? - /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. - SystemAddress HasCompletedConnectionAttempt(void); - - /// Has a previous call to connect failed? - /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. - SystemAddress HasFailedConnectionAttempt(void); - - /// Queued events of new incoming connections - SystemAddress HasNewIncomingConnection(void); - - /// Queued events of lost connections - SystemAddress HasLostConnection(void); - - // Only currently tested with FileListTransfer! - void AttachPlugin( PluginInterface2 *plugin ); - void DetachPlugin( PluginInterface2 *plugin ); - -protected: - void ClearAllConnections(void); - void RemoveFromConnectionList(const SystemAddress &sa); - void AddToConnectionList(const SystemAddress &sa); - void PushNotificationsToQueues(void); - Packet *ReturnOutgoingPacket(void); - - // Plugins - DataStructures::List messageHandlerList; - // A single TCP recieve may generate multiple split packets. They are stored in the waitingPackets list until Receive is called - DataStructures::Queue waitingPackets; - DataStructures::Map connections; - - // Mirrors single producer / consumer, but processes them in Receive() before returning to user - DataStructures::Queue _newIncomingConnections, _lostConnections, _failedConnectionAttempts, _completedConnectionAttempts; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief A simple TCP based server allowing sends and receives. Can be connected by any TCP client, including telnet. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#ifndef __PACKETIZED_TCP +#define __PACKETIZED_TCP + +#include "TCPInterface.h" +#include "DS_ByteQueue.h" +#include "PluginInterface2.h" +#include "DS_Map.h" + +namespace RakNet +{ + +class RAK_DLL_EXPORT PacketizedTCP : public TCPInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(PacketizedTCP) + + PacketizedTCP(); + virtual ~PacketizedTCP(); + + /// Starts the TCP server on the indicated port + /// \param[in] port Which port to listen on. + /// \param[in] maxIncomingConnections Max incoming connections we will accept + /// \param[in] maxConnections Max total connections, which should be >= maxIncomingConnections + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + bool Start(unsigned short port, unsigned short maxIncomingConnections, int threadPriority=-99999, unsigned short socketFamily=AF_INET); + + /// Stops the TCP server + void Stop(void); + + /// Sends a byte stream + void Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ); + + // Sends a concatenated list of byte streams + bool SendList( const char **data, const int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ); + + /// Returns data received + Packet* Receive( void ); + + /// Disconnects a player/address + void CloseConnection( SystemAddress systemAddress ); + + /// Has a previous call to connect succeeded? + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasCompletedConnectionAttempt(void); + + /// Has a previous call to connect failed? + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasFailedConnectionAttempt(void); + + /// Queued events of new incoming connections + SystemAddress HasNewIncomingConnection(void); + + /// Queued events of lost connections + SystemAddress HasLostConnection(void); + + // Only currently tested with FileListTransfer! + void AttachPlugin( PluginInterface2 *plugin ); + void DetachPlugin( PluginInterface2 *plugin ); + +protected: + void ClearAllConnections(void); + void RemoveFromConnectionList(const SystemAddress &sa); + void AddToConnectionList(const SystemAddress &sa); + void PushNotificationsToQueues(void); + Packet *ReturnOutgoingPacket(void); + + // Plugins + DataStructures::List messageHandlerList; + // A single TCP recieve may generate multiple split packets. They are stored in the waitingPackets list until Receive is called + DataStructures::Queue waitingPackets; + DataStructures::Map connections; + + // Mirrors single producer / consumer, but processes them in Receive() before returning to user + DataStructures::Queue _newIncomingConnections, _lostConnections, _failedConnectionAttempts, _completedConnectionAttempts; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/PluginInterface2.cpp b/src/raknet/PluginInterface2.cpp index 706962d..10d9bd7 100755 --- a/src/raknet/PluginInterface2.cpp +++ b/src/raknet/PluginInterface2.cpp @@ -1,89 +1,89 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "PluginInterface2.h" -#include "PacketizedTCP.h" -#include "RakPeerInterface.h" -#include "BitStream.h" - -using namespace RakNet; - -PluginInterface2::PluginInterface2() -{ - rakPeerInterface=0; -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - packetizedTCP=0; -#endif -} -PluginInterface2::~PluginInterface2() -{ - -} -void PluginInterface2::SendUnified( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) -{ - if (rakPeerInterface) - rakPeerInterface->Send(bitStream,priority,reliability,orderingChannel,systemIdentifier,broadcast); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->Send((const char*) bitStream->GetData(), bitStream->GetNumberOfBytesUsed(), systemIdentifier.systemAddress, broadcast); -#endif -} -Packet *PluginInterface2::AllocatePacketUnified(unsigned dataSize) -{ - if (rakPeerInterface) - return rakPeerInterface->AllocatePacket(dataSize); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - return packetizedTCP->AllocatePacket(dataSize); -#else - return 0; -#endif - -} -void PluginInterface2::PushBackPacketUnified(Packet *packet, bool pushAtHead) -{ - if (rakPeerInterface) - rakPeerInterface->PushBackPacket(packet,pushAtHead); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->PushBackPacket(packet,pushAtHead); -#endif -} -void PluginInterface2::DeallocPacketUnified(Packet *packet) -{ - if (rakPeerInterface) - rakPeerInterface->DeallocatePacket(packet); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - packetizedTCP->DeallocatePacket(packet); -#endif -} -bool PluginInterface2::SendListUnified( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) -{ - if (rakPeerInterface) - { - return rakPeerInterface->SendList(data,lengths,numParameters,priority,reliability,orderingChannel,systemIdentifier,broadcast)!=0; - } -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - { - return packetizedTCP->SendList(data,lengths,numParameters,systemIdentifier.systemAddress,broadcast ); - } -#else - return false; -#endif -} -void PluginInterface2::SetRakPeerInterface( RakPeerInterface *ptr ) -{ - rakPeerInterface=ptr; -} -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 -void PluginInterface2::SetPacketizedTCP( PacketizedTCP *ptr ) -{ - packetizedTCP=ptr; -} -#endif +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "PluginInterface2.h" +#include "PacketizedTCP.h" +#include "RakPeerInterface.h" +#include "BitStream.h" + +using namespace RakNet; + +PluginInterface2::PluginInterface2() +{ + rakPeerInterface=0; +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + packetizedTCP=0; +#endif +} +PluginInterface2::~PluginInterface2() +{ + +} +void PluginInterface2::SendUnified( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) +{ + if (rakPeerInterface) + rakPeerInterface->Send(bitStream,priority,reliability,orderingChannel,systemIdentifier,broadcast); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->Send((const char*) bitStream->GetData(), bitStream->GetNumberOfBytesUsed(), systemIdentifier.systemAddress, broadcast); +#endif +} +Packet *PluginInterface2::AllocatePacketUnified(unsigned dataSize) +{ + if (rakPeerInterface) + return rakPeerInterface->AllocatePacket(dataSize); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + return packetizedTCP->AllocatePacket(dataSize); +#else + return 0; +#endif + +} +void PluginInterface2::PushBackPacketUnified(Packet *packet, bool pushAtHead) +{ + if (rakPeerInterface) + rakPeerInterface->PushBackPacket(packet,pushAtHead); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->PushBackPacket(packet,pushAtHead); +#endif +} +void PluginInterface2::DeallocPacketUnified(Packet *packet) +{ + if (rakPeerInterface) + rakPeerInterface->DeallocatePacket(packet); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + packetizedTCP->DeallocatePacket(packet); +#endif +} +bool PluginInterface2::SendListUnified( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) +{ + if (rakPeerInterface) + { + return rakPeerInterface->SendList(data,lengths,numParameters,priority,reliability,orderingChannel,systemIdentifier,broadcast)!=0; + } +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + { + return packetizedTCP->SendList(data,lengths,numParameters,systemIdentifier.systemAddress,broadcast ); + } +#else + return false; +#endif +} +void PluginInterface2::SetRakPeerInterface( RakPeerInterface *ptr ) +{ + rakPeerInterface=ptr; +} +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 +void PluginInterface2::SetPacketizedTCP( PacketizedTCP *ptr ) +{ + packetizedTCP=ptr; +} +#endif diff --git a/src/raknet/PluginInterface2.h b/src/raknet/PluginInterface2.h index 549885d..48f8284 100755 --- a/src/raknet/PluginInterface2.h +++ b/src/raknet/PluginInterface2.h @@ -1,200 +1,200 @@ -/// \file -/// \brief \b RakNet's plugin functionality system, version 2. You can derive from this to create your own plugins. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __PLUGIN_INTERFACE_2_H -#define __PLUGIN_INTERFACE_2_H - -#include "NativeFeatureIncludes.h" -#include "RakNetTypes.h" -#include "Export.h" -#include "PacketPriority.h" - -namespace RakNet { - -/// Forward declarations -class RakPeerInterface; -class PacketizedTCP; -struct Packet; -struct InternalPacket; - -/// \defgroup PLUGIN_INTERFACE_GROUP PluginInterface2 - -/// \defgroup PLUGINS_GROUP Plugins -/// \ingroup PLUGIN_INTERFACE_GROUP - -/// For each message that arrives on an instance of RakPeer, the plugins get an opportunity to process them first. This enumeration represents what to do with the message -/// \ingroup PLUGIN_INTERFACE_GROUP -enum PluginReceiveResult -{ - /// The plugin used this message and it shouldn't be given to the user. - RR_STOP_PROCESSING_AND_DEALLOCATE=0, - - /// This message will be processed by other plugins, and at last by the user. - RR_CONTINUE_PROCESSING, - - /// The plugin is going to hold on to this message. Do not deallocate it but do not pass it to other plugins either. - RR_STOP_PROCESSING, -}; - -/// Reasons why a connection was lost -/// \ingroup PLUGIN_INTERFACE_GROUP -enum PI2_LostConnectionReason -{ - /// Called RakPeer::CloseConnection() - LCR_CLOSED_BY_USER, - - /// Got ID_DISCONNECTION_NOTIFICATION - LCR_DISCONNECTION_NOTIFICATION, - - /// GOT ID_CONNECTION_LOST - LCR_CONNECTION_LOST -}; - -/// Returns why a connection attempt failed -/// \ingroup PLUGIN_INTERFACE_GROUP -enum PI2_FailedConnectionAttemptReason -{ - FCAR_CONNECTION_ATTEMPT_FAILED, - FCAR_ALREADY_CONNECTED, - FCAR_NO_FREE_INCOMING_CONNECTIONS, - FCAR_SECURITY_PUBLIC_KEY_MISMATCH, - FCAR_CONNECTION_BANNED, - FCAR_INVALID_PASSWORD, - FCAR_INCOMPATIBLE_PROTOCOL, - FCAR_IP_RECENTLY_CONNECTED, - FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY, - FCAR_OUR_SYSTEM_REQUIRES_SECURITY, - FCAR_PUBLIC_KEY_MISMATCH, -}; - -/// RakNet's plugin system. Each plugin processes the following events: -/// -Connection attempts -/// -The result of connection attempts -/// -Each incoming message -/// -Updates over time, when RakPeer::Receive() is called -/// -/// \ingroup PLUGIN_INTERFACE_GROUP -class RAK_DLL_EXPORT PluginInterface2 -{ -public: - PluginInterface2(); - virtual ~PluginInterface2(); - - /// Called when the interface is attached - virtual void OnAttach(void) {} - - /// Called when the interface is detached - virtual void OnDetach(void) {} - - /// Update is called every time a packet is checked for . - virtual void Update(void) {} - - /// OnReceive is called for every packet. - /// \param[in] packet the packet that is being returned to the user - /// \return True to allow the game and other plugins to get this message, false to absorb it - virtual PluginReceiveResult OnReceive(Packet *packet) {(void) packet; return RR_CONTINUE_PROCESSING;} - - /// Called when RakPeer is initialized - virtual void OnRakPeerStartup(void) {} - - /// Called when RakPeer is shutdown - virtual void OnRakPeerShutdown(void) {} - - /// Called when a connection is dropped because the user called RakPeer::CloseConnection() for a particular system - /// \param[in] systemAddress The system whose connection was closed - /// \param[in] rakNetGuid The guid of the specified system - /// \param[in] lostConnectionReason How the connection was closed: manually, connection lost, or notification of disconnection - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ){(void) systemAddress; (void) rakNetGUID; (void) lostConnectionReason;} - - /// Called when we got a new connection - /// \param[in] systemAddress Address of the new connection - /// \param[in] rakNetGuid The guid of the specified system - /// \param[in] isIncoming If true, this is ID_NEW_INCOMING_CONNECTION, or the equivalent - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) {(void) systemAddress; (void) rakNetGUID; (void) isIncoming;} - - /// Called when a connection attempt fails - /// \param[in] packet Packet to be returned to the user - /// \param[in] failedConnectionReason Why the connection failed - virtual void OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason) {(void) packet; (void) failedConnectionAttemptReason;} - - /// Queried when attached to RakPeer - /// Return true to call OnDirectSocketSend(), OnDirectSocketReceive(), OnReliabilityLayerPacketError(), OnInternalPacket(), and OnAck() - /// If true, then you cannot call RakPeer::AttachPlugin() or RakPeer::DetachPlugin() for this plugin, while RakPeer is active - virtual bool UsesReliabilityLayer(void) const {return false;} - - /// Called on a send to the socket, per datagram, that does not go through the reliability layer - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] data The data being sent - /// \param[in] bitsUsed How many bits long \a data is - /// \param[in] remoteSystemAddress Which system this message is being sent to - virtual void OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} - - /// Called on a receive from the socket, per datagram, that does not go through the reliability layer - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] data The data being sent - /// \param[in] bitsUsed How many bits long \a data is - /// \param[in] remoteSystemAddress Which system this message is being sent to - virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} - - /// Called when the reliability layer rejects a send or receive - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] bitsUsed How many bits long \a data is - /// \param[in] remoteSystemAddress Which system this message is being sent to - virtual void OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) errorMessage; (void) bitsUsed; (void) remoteSystemAddress;} - - /// Called on a send or receive of a message within the reliability layer - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] internalPacket The user message, along with all send data. - /// \param[in] frameNumber The number of frames sent or received so far for this player depending on \a isSend . Indicates the frame of this user message. - /// \param[in] remoteSystemAddress The player we sent or got this packet from - /// \param[in] time The current time as returned by RakNet::GetTimeMS() - /// \param[in] isSend Is this callback representing a send event or receive event? - virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend) {(void) internalPacket; (void) frameNumber; (void) remoteSystemAddress; (void) time; (void) isSend;} - - /// Called when we get an ack for a message we reliably sent - /// \pre To be called, UsesReliabilityLayer() must return true - /// \param[in] messageNumber The numerical identifier for which message this is - /// \param[in] remoteSystemAddress The player we sent or got this packet from - /// \param[in] time The current time as returned by RakNet::GetTimeMS() - virtual void OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time) {(void) messageNumber; (void) remoteSystemAddress; (void) time;} - - /// System called RakPeerInterface::PushBackPacket - /// \param[in] data The data being sent - /// \param[in] bitsUsed How many bits long \a data is - /// \param[in] remoteSystemAddress The player we sent or got this packet from - virtual void OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} - - RakPeerInterface *GetRakPeerInterface(void) const {return rakPeerInterface;} - - /// \internal - void SetRakPeerInterface( RakPeerInterface *ptr ); - -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - /// \internal - void SetPacketizedTCP( PacketizedTCP *ptr ); -#endif -protected: - // Send through either rakPeerInterface or packetizedTCP, whichever is available - void SendUnified( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); - bool SendListUnified( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); - - Packet *AllocatePacketUnified(unsigned dataSize); - void PushBackPacketUnified(Packet *packet, bool pushAtHead); - void DeallocPacketUnified(Packet *packet); - - // Filled automatically in when attached - RakPeerInterface *rakPeerInterface; -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - PacketizedTCP *packetizedTCP; -#endif -}; - -} // namespace RakNet - -#endif - +/// \file +/// \brief \b RakNet's plugin functionality system, version 2. You can derive from this to create your own plugins. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __PLUGIN_INTERFACE_2_H +#define __PLUGIN_INTERFACE_2_H + +#include "NativeFeatureIncludes.h" +#include "RakNetTypes.h" +#include "Export.h" +#include "PacketPriority.h" + +namespace RakNet { + +/// Forward declarations +class RakPeerInterface; +class PacketizedTCP; +struct Packet; +struct InternalPacket; + +/// \defgroup PLUGIN_INTERFACE_GROUP PluginInterface2 + +/// \defgroup PLUGINS_GROUP Plugins +/// \ingroup PLUGIN_INTERFACE_GROUP + +/// For each message that arrives on an instance of RakPeer, the plugins get an opportunity to process them first. This enumeration represents what to do with the message +/// \ingroup PLUGIN_INTERFACE_GROUP +enum PluginReceiveResult +{ + /// The plugin used this message and it shouldn't be given to the user. + RR_STOP_PROCESSING_AND_DEALLOCATE=0, + + /// This message will be processed by other plugins, and at last by the user. + RR_CONTINUE_PROCESSING, + + /// The plugin is going to hold on to this message. Do not deallocate it but do not pass it to other plugins either. + RR_STOP_PROCESSING, +}; + +/// Reasons why a connection was lost +/// \ingroup PLUGIN_INTERFACE_GROUP +enum PI2_LostConnectionReason +{ + /// Called RakPeer::CloseConnection() + LCR_CLOSED_BY_USER, + + /// Got ID_DISCONNECTION_NOTIFICATION + LCR_DISCONNECTION_NOTIFICATION, + + /// GOT ID_CONNECTION_LOST + LCR_CONNECTION_LOST +}; + +/// Returns why a connection attempt failed +/// \ingroup PLUGIN_INTERFACE_GROUP +enum PI2_FailedConnectionAttemptReason +{ + FCAR_CONNECTION_ATTEMPT_FAILED, + FCAR_ALREADY_CONNECTED, + FCAR_NO_FREE_INCOMING_CONNECTIONS, + FCAR_SECURITY_PUBLIC_KEY_MISMATCH, + FCAR_CONNECTION_BANNED, + FCAR_INVALID_PASSWORD, + FCAR_INCOMPATIBLE_PROTOCOL, + FCAR_IP_RECENTLY_CONNECTED, + FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY, + FCAR_OUR_SYSTEM_REQUIRES_SECURITY, + FCAR_PUBLIC_KEY_MISMATCH, +}; + +/// RakNet's plugin system. Each plugin processes the following events: +/// -Connection attempts +/// -The result of connection attempts +/// -Each incoming message +/// -Updates over time, when RakPeer::Receive() is called +/// +/// \ingroup PLUGIN_INTERFACE_GROUP +class RAK_DLL_EXPORT PluginInterface2 +{ +public: + PluginInterface2(); + virtual ~PluginInterface2(); + + /// Called when the interface is attached + virtual void OnAttach(void) {} + + /// Called when the interface is detached + virtual void OnDetach(void) {} + + /// Update is called every time a packet is checked for . + virtual void Update(void) {} + + /// OnReceive is called for every packet. + /// \param[in] packet the packet that is being returned to the user + /// \return True to allow the game and other plugins to get this message, false to absorb it + virtual PluginReceiveResult OnReceive(Packet *packet) {(void) packet; return RR_CONTINUE_PROCESSING;} + + /// Called when RakPeer is initialized + virtual void OnRakPeerStartup(void) {} + + /// Called when RakPeer is shutdown + virtual void OnRakPeerShutdown(void) {} + + /// Called when a connection is dropped because the user called RakPeer::CloseConnection() for a particular system + /// \param[in] systemAddress The system whose connection was closed + /// \param[in] rakNetGuid The guid of the specified system + /// \param[in] lostConnectionReason How the connection was closed: manually, connection lost, or notification of disconnection + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ){(void) systemAddress; (void) rakNetGUID; (void) lostConnectionReason;} + + /// Called when we got a new connection + /// \param[in] systemAddress Address of the new connection + /// \param[in] rakNetGuid The guid of the specified system + /// \param[in] isIncoming If true, this is ID_NEW_INCOMING_CONNECTION, or the equivalent + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) {(void) systemAddress; (void) rakNetGUID; (void) isIncoming;} + + /// Called when a connection attempt fails + /// \param[in] packet Packet to be returned to the user + /// \param[in] failedConnectionReason Why the connection failed + virtual void OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason) {(void) packet; (void) failedConnectionAttemptReason;} + + /// Queried when attached to RakPeer + /// Return true to call OnDirectSocketSend(), OnDirectSocketReceive(), OnReliabilityLayerPacketError(), OnInternalPacket(), and OnAck() + /// If true, then you cannot call RakPeer::AttachPlugin() or RakPeer::DetachPlugin() for this plugin, while RakPeer is active + virtual bool UsesReliabilityLayer(void) const {return false;} + + /// Called on a send to the socket, per datagram, that does not go through the reliability layer + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] data The data being sent + /// \param[in] bitsUsed How many bits long \a data is + /// \param[in] remoteSystemAddress Which system this message is being sent to + virtual void OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} + + /// Called on a receive from the socket, per datagram, that does not go through the reliability layer + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] data The data being sent + /// \param[in] bitsUsed How many bits long \a data is + /// \param[in] remoteSystemAddress Which system this message is being sent to + virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} + + /// Called when the reliability layer rejects a send or receive + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] bitsUsed How many bits long \a data is + /// \param[in] remoteSystemAddress Which system this message is being sent to + virtual void OnReliabilityLayerPacketError(const char *errorMessage, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) errorMessage; (void) bitsUsed; (void) remoteSystemAddress;} + + /// Called on a send or receive of a message within the reliability layer + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] internalPacket The user message, along with all send data. + /// \param[in] frameNumber The number of frames sent or received so far for this player depending on \a isSend . Indicates the frame of this user message. + /// \param[in] remoteSystemAddress The player we sent or got this packet from + /// \param[in] time The current time as returned by RakNet::GetTimeMS() + /// \param[in] isSend Is this callback representing a send event or receive event? + virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time, int isSend) {(void) internalPacket; (void) frameNumber; (void) remoteSystemAddress; (void) time; (void) isSend;} + + /// Called when we get an ack for a message we reliably sent + /// \pre To be called, UsesReliabilityLayer() must return true + /// \param[in] messageNumber The numerical identifier for which message this is + /// \param[in] remoteSystemAddress The player we sent or got this packet from + /// \param[in] time The current time as returned by RakNet::GetTimeMS() + virtual void OnAck(unsigned int messageNumber, SystemAddress remoteSystemAddress, RakNet::TimeMS time) {(void) messageNumber; (void) remoteSystemAddress; (void) time;} + + /// System called RakPeerInterface::PushBackPacket + /// \param[in] data The data being sent + /// \param[in] bitsUsed How many bits long \a data is + /// \param[in] remoteSystemAddress The player we sent or got this packet from + virtual void OnPushBackPacket(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress) {(void) data; (void) bitsUsed; (void) remoteSystemAddress;} + + RakPeerInterface *GetRakPeerInterface(void) const {return rakPeerInterface;} + + /// \internal + void SetRakPeerInterface( RakPeerInterface *ptr ); + +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + /// \internal + void SetPacketizedTCP( PacketizedTCP *ptr ); +#endif +protected: + // Send through either rakPeerInterface or packetizedTCP, whichever is available + void SendUnified( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); + bool SendListUnified( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); + + Packet *AllocatePacketUnified(unsigned dataSize); + void PushBackPacketUnified(Packet *packet, bool pushAtHead); + void DeallocPacketUnified(Packet *packet); + + // Filled automatically in when attached + RakPeerInterface *rakPeerInterface; +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + PacketizedTCP *packetizedTCP; +#endif +}; + +} // namespace RakNet + +#endif + diff --git a/src/raknet/RPC4Plugin.cpp b/src/raknet/RPC4Plugin.cpp index 7583d28..64303ba 100755 --- a/src/raknet/RPC4Plugin.cpp +++ b/src/raknet/RPC4Plugin.cpp @@ -1,594 +1,594 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_RPC4Plugin==1 - -#include "RPC4Plugin.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "PacketizedTCP.h" -#include "RakSleep.h" -#include "RakNetDefines.h" -#include "DS_Queue.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(RPC4,RPC4); - -struct GlobalRegistration -{ - void ( *registerFunctionPointer ) ( RakNet::BitStream *userData, Packet *packet ); - void ( *registerBlockingFunctionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet ); - char functionName[RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH]; - MessageID messageId; - int callPriority; -}; -static GlobalRegistration globalRegistrationBuffer[RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS]; -static unsigned int globalRegistrationIndex=0; - -RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) -{ - RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); - unsigned int i; - for (i=0; uniqueID[i]; i++) - { - RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); - globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; - } - globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=functionPointer; - globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; - globalRegistrationBuffer[globalRegistrationIndex].callPriority=0xFFFFFFFF; - globalRegistrationIndex++; -} -RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority) -{ - RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); - unsigned int i; - for (i=0; uniqueID[i]; i++) - { - RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); - globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; - } - globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=functionPointer; - globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; - RakAssert(callPriority!=0xFFFFFFFF); - globalRegistrationBuffer[globalRegistrationIndex].callPriority=callPriority; - globalRegistrationIndex++; -} -RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )) -{ - RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); - unsigned int i; - for (i=0; uniqueID[i]; i++) - { - RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); - globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; - } - globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=0; - globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=functionPointer; - globalRegistrationIndex++; -} -RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, MessageID messageId) -{ - RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); - unsigned int i; - for (i=0; uniqueID[i]; i++) - { - RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); - globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; - } - globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=0; - globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; - globalRegistrationBuffer[globalRegistrationIndex].messageId=messageId; - globalRegistrationIndex++; -} - -enum RPC4Identifiers -{ - ID_RPC4_CALL, - ID_RPC4_RETURN, - ID_RPC4_SIGNAL, -}; -int RPC4::LocalSlotObjectComp( const LocalSlotObject &key, const LocalSlotObject &data ) -{ - if (key.callPriority>data.callPriority) - return -1; - if (key.callPriority==data.callPriority) - { - if (key.registrationCountmessageId) - return -1; - if (key > data->messageId) - return 1; - return 0; -} - -RPC4::RPC4() -{ - gotBlockingReturnValue=false; - nextSlotRegistrationCount=0; - interruptSignal=false; -} -RPC4::~RPC4() -{ - unsigned int i; - for (i=0; i < localCallbacks.Size(); i++) - { - RakNet::OP_DELETE(localCallbacks[i],_FILE_AND_LINE_); - } - - DataStructures::List keyList; - DataStructures::List outputList; - localSlots.GetAsList(outputList,keyList,_FILE_AND_LINE_); - unsigned int j; - for (j=0; j < outputList.Size(); j++) - { - RakNet::OP_DELETE(outputList[j],_FILE_AND_LINE_); - } - localSlots.Clear(_FILE_AND_LINE_); -} -bool RPC4::RegisterFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) -{ - DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(uniqueID); - if (skhi.IsInvalid()==false) - return false; - - registeredNonblockingFunctions.Push(uniqueID,functionPointer,_FILE_AND_LINE_); - return true; -} -void RPC4::RegisterSlot(const char *sharedIdentifier, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority) -{ - LocalSlotObject lso(nextSlotRegistrationCount++, callPriority, functionPointer); - DataStructures::HashIndex idx = GetLocalSlotIndex(sharedIdentifier); - LocalSlot *localSlot; - if (idx.IsInvalid()) - { - localSlot = RakNet::OP_NEW(_FILE_AND_LINE_); - localSlots.Push(sharedIdentifier, localSlot,_FILE_AND_LINE_); - } - else - { - localSlot=localSlots.ItemAtIndex(idx); - } - localSlot->slotObjects.Insert(lso,lso,true,_FILE_AND_LINE_); -} -bool RPC4::RegisterBlockingFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )) -{ - DataStructures::HashIndex skhi = registeredBlockingFunctions.GetIndexOf(uniqueID); - if (skhi.IsInvalid()==false) - return false; - - registeredBlockingFunctions.Push(uniqueID,functionPointer,_FILE_AND_LINE_); - return true; -} -void RPC4::RegisterLocalCallback(const char* uniqueID, MessageID messageId) -{ - bool objectExists; - unsigned int index; - LocalCallback *lc; - RakNet::RakString str; - str=uniqueID; - index = localCallbacks.GetIndexFromKey(messageId,&objectExists); - if (objectExists) - { - lc = localCallbacks[index]; - index = lc->functions.GetIndexFromKey(str,&objectExists); - if (objectExists==false) - lc->functions.InsertAtIndex(str,index,_FILE_AND_LINE_); - } - else - { - lc = RakNet::OP_NEW(_FILE_AND_LINE_); - lc->messageId=messageId; - lc->functions.Insert(str,str,false,_FILE_AND_LINE_); - localCallbacks.InsertAtIndex(lc,index,_FILE_AND_LINE_); - } -} -bool RPC4::UnregisterFunction(const char* uniqueID) -{ - void ( *f ) ( RakNet::BitStream *, Packet * ); - return registeredNonblockingFunctions.Pop(f,uniqueID,_FILE_AND_LINE_); -} -bool RPC4::UnregisterBlockingFunction(const char* uniqueID) -{ - void ( *f ) ( RakNet::BitStream *, RakNet::BitStream *,Packet * ); - return registeredBlockingFunctions.Pop(f,uniqueID,_FILE_AND_LINE_); -} -bool RPC4::UnregisterLocalCallback(const char* uniqueID, MessageID messageId) -{ - bool objectExists; - unsigned int index, index2; - LocalCallback *lc; - RakNet::RakString str; - str=uniqueID; - index = localCallbacks.GetIndexFromKey(messageId,&objectExists); - if (objectExists) - { - lc = localCallbacks[index]; - index2 = lc->functions.GetIndexFromKey(str,&objectExists); - if (objectExists) - { - lc->functions.RemoveAtIndex(index2); - if (lc->functions.Size()==0) - { - RakNet::OP_DELETE(lc,_FILE_AND_LINE_); - localCallbacks.RemoveAtIndex(index); - return true; - } - } - } - return false; -} -bool RPC4::UnregisterSlot(const char* sharedIdentifier) -{ - DataStructures::HashIndex hi = localSlots.GetIndexOf(sharedIdentifier); - if (hi.IsInvalid()==false) - { - LocalSlot *ls = localSlots.ItemAtIndex(hi); - RakNet::OP_DELETE(ls, _FILE_AND_LINE_); - localSlots.RemoveAtIndex(hi, _FILE_AND_LINE_); - return true; - } - - return false; -} -void RPC4::CallLoopback( const char* uniqueID, RakNet::BitStream * bitStream ) -{ - Packet *p=0; - - DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(uniqueID); - - if (skhi.IsInvalid()==true) - { - if (rakPeerInterface) - p=AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)+(unsigned int) strlen(uniqueID)+1); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - p=packetizedTCP->AllocatePacket(sizeof(MessageID)+sizeof(unsigned char)+(unsigned int) strlen(uniqueID)+1); -#endif - - if (rakPeerInterface) - p->guid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - p->guid=UNASSIGNED_RAKNET_GUID; -#endif - - p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->data[0]=ID_RPC_REMOTE_ERROR; - p->data[1]=RPC_ERROR_FUNCTION_NOT_REGISTERED; - strcpy((char*) p->data+2, uniqueID); - - PushBackPacketUnified(p,false); - - return; - } - - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_CALL); - out.WriteCompressed(uniqueID); - out.Write(false); // nonblocking - if (bitStream) - { - bitStream->ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(bitStream); - } - if (rakPeerInterface) - p=AllocatePacketUnified(out.GetNumberOfBytesUsed()); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - p=packetizedTCP->AllocatePacket(out.GetNumberOfBytesUsed()); -#endif - - if (rakPeerInterface) - p->guid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 - else - p->guid=UNASSIGNED_RAKNET_GUID; -#endif - p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - p->systemAddress.systemIndex=(SystemIndex)-1; - memcpy(p->data,out.GetData(),out.GetNumberOfBytesUsed()); - PushBackPacketUnified(p,false); - return; -} -void RPC4::Call( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) -{ - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_CALL); - out.WriteCompressed(uniqueID); - out.Write(false); // Nonblocking - if (bitStream) - { - bitStream->ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(bitStream); - } - SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,broadcast); -} -bool RPC4::CallBlocking( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, RakNet::BitStream *returnData ) -{ - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_CALL); - out.WriteCompressed(uniqueID); - out.Write(true); // Blocking - if (bitStream) - { - bitStream->ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(bitStream); - } - RakAssert(returnData); - RakAssert(rakPeerInterface); - ConnectionState cs; - cs = rakPeerInterface->GetConnectionState(systemIdentifier); - if (cs!=IS_CONNECTED) - return false; - - SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,false); - - returnData->Reset(); - blockingReturnValue.Reset(); - gotBlockingReturnValue=false; - Packet *packet; - DataStructures::Queue packetQueue; - while (gotBlockingReturnValue==false) - { - // TODO - block, filter until gotBlockingReturnValue==true or ID_CONNECTION_LOST or ID_DISCONNECTION_NOTIFICXATION or ID_RPC_REMOTE_ERROR/RPC_ERROR_FUNCTION_NOT_REGISTERED - RakSleep(30); - - packet=rakPeerInterface->Receive(); - - if (packet) - { - if ( - (packet->data[0]==ID_CONNECTION_LOST || packet->data[0]==ID_DISCONNECTION_NOTIFICATION) && - ((systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID && packet->guid==systemIdentifier.rakNetGuid) || - (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && packet->systemAddress==systemIdentifier.systemAddress)) - ) - { - // Push back to head in reverse order - rakPeerInterface->PushBackPacket(packet,true); - while (packetQueue.Size()) - rakPeerInterface->PushBackPacket(packetQueue.Pop(),true); - return false; - } - else if (packet->data[0]==ID_RPC_REMOTE_ERROR && packet->data[1]==RPC_ERROR_FUNCTION_NOT_REGISTERED) - { - RakNet::RakString functionName; - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - bsIn.Read(functionName); - if (functionName==uniqueID) - { - // Push back to head in reverse order - rakPeerInterface->PushBackPacket(packet,true); - while (packetQueue.Size()) - rakPeerInterface->PushBackPacket(packetQueue.Pop(),true); - return false; - } - else - { - packetQueue.PushAtHead(packet,0,_FILE_AND_LINE_); - } - } - else - { - packetQueue.PushAtHead(packet,0,_FILE_AND_LINE_); - } - } - } - - returnData->Read(blockingReturnValue); - return true; -} -void RPC4::Signal(const char *sharedIdentifier, RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool invokeLocal) -{ - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_SIGNAL); - out.WriteCompressed(sharedIdentifier); - if (bitStream) - { - bitStream->ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(bitStream); - } - SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,broadcast); - - if (invokeLocal) - { - DataStructures::HashIndex functionIndex; - functionIndex = localSlots.GetIndexOf(sharedIdentifier); - if (functionIndex.IsInvalid()) - return; - - Packet p; - p.guid=rakPeerInterface->GetMyGUID(); - p.systemAddress=rakPeerInterface->GetMyBoundAddress(0); - p.wasGeneratedLocally=true; - RakNet::BitStream *bsptr, bstemp; - if (bitStream) - { - bitStream->ResetReadPointer(); - p.length=bitStream->GetNumberOfBytesUsed(); - p.bitSize=bitStream->GetNumberOfBitsUsed(); - bsptr=bitStream; - } - else - { - p.length=0; - p.bitSize=0; - bsptr=&bstemp; - } - InvokeSignal(functionIndex, bsptr, &p); - } -} -void RPC4::InvokeSignal(DataStructures::HashIndex functionIndex, RakNet::BitStream *serializedParameters, Packet *packet) -{ - if (functionIndex.IsInvalid()) - return; - - interruptSignal=false; - LocalSlot *localSlot = localSlots.ItemAtIndex(functionIndex); - unsigned int i; - i=0; - while (i < localSlot->slotObjects.Size()) - { - localSlot->slotObjects[i].functionPointer(serializedParameters, packet); - - // Not threadsafe - if (interruptSignal==true) - break; - - serializedParameters->ResetReadPointer(); - - i++; - } -} -void RPC4::InterruptSignal(void) -{ - interruptSignal=true; -} -void RPC4::OnAttach(void) -{ - unsigned int i; - for (i=0; i < globalRegistrationIndex; i++) - { - if (globalRegistrationBuffer[i].registerFunctionPointer) - { - if (globalRegistrationBuffer[i].callPriority==(int)0xFFFFFFFF) - RegisterFunction(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerFunctionPointer); - else - RegisterSlot(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerFunctionPointer, globalRegistrationBuffer[i].callPriority); - } - else if (globalRegistrationBuffer[i].registerBlockingFunctionPointer) - RegisterBlockingFunction(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerBlockingFunctionPointer); - else - RegisterLocalCallback(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].messageId); - } -} -PluginReceiveResult RPC4::OnReceive(Packet *packet) -{ - if (packet->data[0]==ID_RPC_PLUGIN) - { - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - - if (packet->data[1]==ID_RPC4_CALL) - { - RakNet::RakString functionName; - bsIn.ReadCompressed(functionName); - bool isBlocking=false; - bsIn.Read(isBlocking); - if (isBlocking==false) - { - DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(functionName.C_String()); - if (skhi.IsInvalid()) - { - RakNet::BitStream bsOut; - bsOut.Write((unsigned char) ID_RPC_REMOTE_ERROR); - bsOut.Write((unsigned char) RPC_ERROR_FUNCTION_NOT_REGISTERED); - bsOut.Write(functionName.C_String(),(unsigned int) functionName.GetLength()+1); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - void ( *fp ) ( RakNet::BitStream *, Packet * ); - fp = registeredNonblockingFunctions.ItemAtIndex(skhi); - bsIn.AlignReadToByteBoundary(); - fp(&bsIn,packet); - } - else - { - DataStructures::HashIndex skhi = registeredBlockingFunctions.GetIndexOf(functionName.C_String()); - if (skhi.IsInvalid()) - { - RakNet::BitStream bsOut; - bsOut.Write((unsigned char) ID_RPC_REMOTE_ERROR); - bsOut.Write((unsigned char) RPC_ERROR_FUNCTION_NOT_REGISTERED); - bsOut.Write(functionName.C_String(),(unsigned int) functionName.GetLength()+1); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - void ( *fp ) ( RakNet::BitStream *, RakNet::BitStream *, Packet * ); - fp = registeredBlockingFunctions.ItemAtIndex(skhi); - RakNet::BitStream returnData; - bsIn.AlignReadToByteBoundary(); - fp(&bsIn, &returnData, packet); - - RakNet::BitStream out; - out.Write((MessageID) ID_RPC_PLUGIN); - out.Write((MessageID) ID_RPC4_RETURN); - returnData.ResetReadPointer(); - out.AlignWriteToByteBoundary(); - out.Write(returnData); - SendUnified(&out,IMMEDIATE_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); - } - } - else if (packet->data[1]==ID_RPC4_SIGNAL) - { - RakNet::RakString sharedIdentifier; - bsIn.ReadCompressed(sharedIdentifier); - DataStructures::HashIndex functionIndex; - functionIndex = localSlots.GetIndexOf(sharedIdentifier); - RakNet::BitStream serializedParameters; - bsIn.AlignReadToByteBoundary(); - bsIn.Read(&serializedParameters); - InvokeSignal(functionIndex, &serializedParameters, packet); - } - else - { - RakAssert(packet->data[1]==ID_RPC4_RETURN); - blockingReturnValue.Reset(); - blockingReturnValue.Read(bsIn); - gotBlockingReturnValue=true; - } - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - bool objectExists; - unsigned int index, index2; - index = localCallbacks.GetIndexFromKey(packet->data[0],&objectExists); - if (objectExists) - { - LocalCallback *lc; - lc = localCallbacks[index]; - for (index2=0; index2 < lc->functions.Size(); index2++) - { - RakNet::BitStream bsIn(packet->data, packet->length, false); - - DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(lc->functions[index2].C_String()); - if (skhi.IsInvalid()==false) - { - void ( *fp ) ( RakNet::BitStream *, Packet * ); - fp = registeredNonblockingFunctions.ItemAtIndex(skhi); - bsIn.AlignReadToByteBoundary(); - fp(&bsIn,packet); - } - } - } - - return RR_CONTINUE_PROCESSING; -} -DataStructures::HashIndex RPC4::GetLocalSlotIndex(const char *sharedIdentifier) -{ - return localSlots.GetIndexOf(sharedIdentifier); -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_RPC4Plugin==1 + +#include "RPC4Plugin.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "PacketizedTCP.h" +#include "RakSleep.h" +#include "RakNetDefines.h" +#include "DS_Queue.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(RPC4,RPC4); + +struct GlobalRegistration +{ + void ( *registerFunctionPointer ) ( RakNet::BitStream *userData, Packet *packet ); + void ( *registerBlockingFunctionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet ); + char functionName[RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH]; + MessageID messageId; + int callPriority; +}; +static GlobalRegistration globalRegistrationBuffer[RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS]; +static unsigned int globalRegistrationIndex=0; + +RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) +{ + RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); + unsigned int i; + for (i=0; uniqueID[i]; i++) + { + RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); + globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; + } + globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=functionPointer; + globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; + globalRegistrationBuffer[globalRegistrationIndex].callPriority=0xFFFFFFFF; + globalRegistrationIndex++; +} +RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority) +{ + RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); + unsigned int i; + for (i=0; uniqueID[i]; i++) + { + RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); + globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; + } + globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=functionPointer; + globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; + RakAssert(callPriority!=0xFFFFFFFF); + globalRegistrationBuffer[globalRegistrationIndex].callPriority=callPriority; + globalRegistrationIndex++; +} +RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )) +{ + RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); + unsigned int i; + for (i=0; uniqueID[i]; i++) + { + RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); + globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; + } + globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=0; + globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=functionPointer; + globalRegistrationIndex++; +} +RPC4GlobalRegistration::RPC4GlobalRegistration(const char* uniqueID, MessageID messageId) +{ + RakAssert(globalRegistrationIndex!=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS); + unsigned int i; + for (i=0; uniqueID[i]; i++) + { + RakAssert(i<=RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH-1); + globalRegistrationBuffer[globalRegistrationIndex].functionName[i]=uniqueID[i]; + } + globalRegistrationBuffer[globalRegistrationIndex].registerFunctionPointer=0; + globalRegistrationBuffer[globalRegistrationIndex].registerBlockingFunctionPointer=0; + globalRegistrationBuffer[globalRegistrationIndex].messageId=messageId; + globalRegistrationIndex++; +} + +enum RPC4Identifiers +{ + ID_RPC4_CALL, + ID_RPC4_RETURN, + ID_RPC4_SIGNAL, +}; +int RPC4::LocalSlotObjectComp( const LocalSlotObject &key, const LocalSlotObject &data ) +{ + if (key.callPriority>data.callPriority) + return -1; + if (key.callPriority==data.callPriority) + { + if (key.registrationCountmessageId) + return -1; + if (key > data->messageId) + return 1; + return 0; +} + +RPC4::RPC4() +{ + gotBlockingReturnValue=false; + nextSlotRegistrationCount=0; + interruptSignal=false; +} +RPC4::~RPC4() +{ + unsigned int i; + for (i=0; i < localCallbacks.Size(); i++) + { + RakNet::OP_DELETE(localCallbacks[i],_FILE_AND_LINE_); + } + + DataStructures::List keyList; + DataStructures::List outputList; + localSlots.GetAsList(outputList,keyList,_FILE_AND_LINE_); + unsigned int j; + for (j=0; j < outputList.Size(); j++) + { + RakNet::OP_DELETE(outputList[j],_FILE_AND_LINE_); + } + localSlots.Clear(_FILE_AND_LINE_); +} +bool RPC4::RegisterFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) +{ + DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(uniqueID); + if (skhi.IsInvalid()==false) + return false; + + registeredNonblockingFunctions.Push(uniqueID,functionPointer,_FILE_AND_LINE_); + return true; +} +void RPC4::RegisterSlot(const char *sharedIdentifier, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority) +{ + LocalSlotObject lso(nextSlotRegistrationCount++, callPriority, functionPointer); + DataStructures::HashIndex idx = GetLocalSlotIndex(sharedIdentifier); + LocalSlot *localSlot; + if (idx.IsInvalid()) + { + localSlot = RakNet::OP_NEW(_FILE_AND_LINE_); + localSlots.Push(sharedIdentifier, localSlot,_FILE_AND_LINE_); + } + else + { + localSlot=localSlots.ItemAtIndex(idx); + } + localSlot->slotObjects.Insert(lso,lso,true,_FILE_AND_LINE_); +} +bool RPC4::RegisterBlockingFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )) +{ + DataStructures::HashIndex skhi = registeredBlockingFunctions.GetIndexOf(uniqueID); + if (skhi.IsInvalid()==false) + return false; + + registeredBlockingFunctions.Push(uniqueID,functionPointer,_FILE_AND_LINE_); + return true; +} +void RPC4::RegisterLocalCallback(const char* uniqueID, MessageID messageId) +{ + bool objectExists; + unsigned int index; + LocalCallback *lc; + RakNet::RakString str; + str=uniqueID; + index = localCallbacks.GetIndexFromKey(messageId,&objectExists); + if (objectExists) + { + lc = localCallbacks[index]; + index = lc->functions.GetIndexFromKey(str,&objectExists); + if (objectExists==false) + lc->functions.InsertAtIndex(str,index,_FILE_AND_LINE_); + } + else + { + lc = RakNet::OP_NEW(_FILE_AND_LINE_); + lc->messageId=messageId; + lc->functions.Insert(str,str,false,_FILE_AND_LINE_); + localCallbacks.InsertAtIndex(lc,index,_FILE_AND_LINE_); + } +} +bool RPC4::UnregisterFunction(const char* uniqueID) +{ + void ( *f ) ( RakNet::BitStream *, Packet * ); + return registeredNonblockingFunctions.Pop(f,uniqueID,_FILE_AND_LINE_); +} +bool RPC4::UnregisterBlockingFunction(const char* uniqueID) +{ + void ( *f ) ( RakNet::BitStream *, RakNet::BitStream *,Packet * ); + return registeredBlockingFunctions.Pop(f,uniqueID,_FILE_AND_LINE_); +} +bool RPC4::UnregisterLocalCallback(const char* uniqueID, MessageID messageId) +{ + bool objectExists; + unsigned int index, index2; + LocalCallback *lc; + RakNet::RakString str; + str=uniqueID; + index = localCallbacks.GetIndexFromKey(messageId,&objectExists); + if (objectExists) + { + lc = localCallbacks[index]; + index2 = lc->functions.GetIndexFromKey(str,&objectExists); + if (objectExists) + { + lc->functions.RemoveAtIndex(index2); + if (lc->functions.Size()==0) + { + RakNet::OP_DELETE(lc,_FILE_AND_LINE_); + localCallbacks.RemoveAtIndex(index); + return true; + } + } + } + return false; +} +bool RPC4::UnregisterSlot(const char* sharedIdentifier) +{ + DataStructures::HashIndex hi = localSlots.GetIndexOf(sharedIdentifier); + if (hi.IsInvalid()==false) + { + LocalSlot *ls = localSlots.ItemAtIndex(hi); + RakNet::OP_DELETE(ls, _FILE_AND_LINE_); + localSlots.RemoveAtIndex(hi, _FILE_AND_LINE_); + return true; + } + + return false; +} +void RPC4::CallLoopback( const char* uniqueID, RakNet::BitStream * bitStream ) +{ + Packet *p=0; + + DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(uniqueID); + + if (skhi.IsInvalid()==true) + { + if (rakPeerInterface) + p=AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)+(unsigned int) strlen(uniqueID)+1); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + p=packetizedTCP->AllocatePacket(sizeof(MessageID)+sizeof(unsigned char)+(unsigned int) strlen(uniqueID)+1); +#endif + + if (rakPeerInterface) + p->guid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + p->guid=UNASSIGNED_RAKNET_GUID; +#endif + + p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->data[0]=ID_RPC_REMOTE_ERROR; + p->data[1]=RPC_ERROR_FUNCTION_NOT_REGISTERED; + strcpy((char*) p->data+2, uniqueID); + + PushBackPacketUnified(p,false); + + return; + } + + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_CALL); + out.WriteCompressed(uniqueID); + out.Write(false); // nonblocking + if (bitStream) + { + bitStream->ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(bitStream); + } + if (rakPeerInterface) + p=AllocatePacketUnified(out.GetNumberOfBytesUsed()); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + p=packetizedTCP->AllocatePacket(out.GetNumberOfBytesUsed()); +#endif + + if (rakPeerInterface) + p->guid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1 + else + p->guid=UNASSIGNED_RAKNET_GUID; +#endif + p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + p->systemAddress.systemIndex=(SystemIndex)-1; + memcpy(p->data,out.GetData(),out.GetNumberOfBytesUsed()); + PushBackPacketUnified(p,false); + return; +} +void RPC4::Call( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ) +{ + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_CALL); + out.WriteCompressed(uniqueID); + out.Write(false); // Nonblocking + if (bitStream) + { + bitStream->ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(bitStream); + } + SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,broadcast); +} +bool RPC4::CallBlocking( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, RakNet::BitStream *returnData ) +{ + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_CALL); + out.WriteCompressed(uniqueID); + out.Write(true); // Blocking + if (bitStream) + { + bitStream->ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(bitStream); + } + RakAssert(returnData); + RakAssert(rakPeerInterface); + ConnectionState cs; + cs = rakPeerInterface->GetConnectionState(systemIdentifier); + if (cs!=IS_CONNECTED) + return false; + + SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,false); + + returnData->Reset(); + blockingReturnValue.Reset(); + gotBlockingReturnValue=false; + Packet *packet; + DataStructures::Queue packetQueue; + while (gotBlockingReturnValue==false) + { + // TODO - block, filter until gotBlockingReturnValue==true or ID_CONNECTION_LOST or ID_DISCONNECTION_NOTIFICXATION or ID_RPC_REMOTE_ERROR/RPC_ERROR_FUNCTION_NOT_REGISTERED + RakSleep(30); + + packet=rakPeerInterface->Receive(); + + if (packet) + { + if ( + (packet->data[0]==ID_CONNECTION_LOST || packet->data[0]==ID_DISCONNECTION_NOTIFICATION) && + ((systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID && packet->guid==systemIdentifier.rakNetGuid) || + (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && packet->systemAddress==systemIdentifier.systemAddress)) + ) + { + // Push back to head in reverse order + rakPeerInterface->PushBackPacket(packet,true); + while (packetQueue.Size()) + rakPeerInterface->PushBackPacket(packetQueue.Pop(),true); + return false; + } + else if (packet->data[0]==ID_RPC_REMOTE_ERROR && packet->data[1]==RPC_ERROR_FUNCTION_NOT_REGISTERED) + { + RakNet::RakString functionName; + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + bsIn.Read(functionName); + if (functionName==uniqueID) + { + // Push back to head in reverse order + rakPeerInterface->PushBackPacket(packet,true); + while (packetQueue.Size()) + rakPeerInterface->PushBackPacket(packetQueue.Pop(),true); + return false; + } + else + { + packetQueue.PushAtHead(packet,0,_FILE_AND_LINE_); + } + } + else + { + packetQueue.PushAtHead(packet,0,_FILE_AND_LINE_); + } + } + } + + returnData->Read(blockingReturnValue); + return true; +} +void RPC4::Signal(const char *sharedIdentifier, RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool invokeLocal) +{ + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_SIGNAL); + out.WriteCompressed(sharedIdentifier); + if (bitStream) + { + bitStream->ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(bitStream); + } + SendUnified(&out,priority,reliability,orderingChannel,systemIdentifier,broadcast); + + if (invokeLocal) + { + DataStructures::HashIndex functionIndex; + functionIndex = localSlots.GetIndexOf(sharedIdentifier); + if (functionIndex.IsInvalid()) + return; + + Packet p; + p.guid=rakPeerInterface->GetMyGUID(); + p.systemAddress=rakPeerInterface->GetMyBoundAddress(0); + p.wasGeneratedLocally=true; + RakNet::BitStream *bsptr, bstemp; + if (bitStream) + { + bitStream->ResetReadPointer(); + p.length=bitStream->GetNumberOfBytesUsed(); + p.bitSize=bitStream->GetNumberOfBitsUsed(); + bsptr=bitStream; + } + else + { + p.length=0; + p.bitSize=0; + bsptr=&bstemp; + } + InvokeSignal(functionIndex, bsptr, &p); + } +} +void RPC4::InvokeSignal(DataStructures::HashIndex functionIndex, RakNet::BitStream *serializedParameters, Packet *packet) +{ + if (functionIndex.IsInvalid()) + return; + + interruptSignal=false; + LocalSlot *localSlot = localSlots.ItemAtIndex(functionIndex); + unsigned int i; + i=0; + while (i < localSlot->slotObjects.Size()) + { + localSlot->slotObjects[i].functionPointer(serializedParameters, packet); + + // Not threadsafe + if (interruptSignal==true) + break; + + serializedParameters->ResetReadPointer(); + + i++; + } +} +void RPC4::InterruptSignal(void) +{ + interruptSignal=true; +} +void RPC4::OnAttach(void) +{ + unsigned int i; + for (i=0; i < globalRegistrationIndex; i++) + { + if (globalRegistrationBuffer[i].registerFunctionPointer) + { + if (globalRegistrationBuffer[i].callPriority==(int)0xFFFFFFFF) + RegisterFunction(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerFunctionPointer); + else + RegisterSlot(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerFunctionPointer, globalRegistrationBuffer[i].callPriority); + } + else if (globalRegistrationBuffer[i].registerBlockingFunctionPointer) + RegisterBlockingFunction(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].registerBlockingFunctionPointer); + else + RegisterLocalCallback(globalRegistrationBuffer[i].functionName, globalRegistrationBuffer[i].messageId); + } +} +PluginReceiveResult RPC4::OnReceive(Packet *packet) +{ + if (packet->data[0]==ID_RPC_PLUGIN) + { + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + + if (packet->data[1]==ID_RPC4_CALL) + { + RakNet::RakString functionName; + bsIn.ReadCompressed(functionName); + bool isBlocking=false; + bsIn.Read(isBlocking); + if (isBlocking==false) + { + DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(functionName.C_String()); + if (skhi.IsInvalid()) + { + RakNet::BitStream bsOut; + bsOut.Write((unsigned char) ID_RPC_REMOTE_ERROR); + bsOut.Write((unsigned char) RPC_ERROR_FUNCTION_NOT_REGISTERED); + bsOut.Write(functionName.C_String(),(unsigned int) functionName.GetLength()+1); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + void ( *fp ) ( RakNet::BitStream *, Packet * ); + fp = registeredNonblockingFunctions.ItemAtIndex(skhi); + bsIn.AlignReadToByteBoundary(); + fp(&bsIn,packet); + } + else + { + DataStructures::HashIndex skhi = registeredBlockingFunctions.GetIndexOf(functionName.C_String()); + if (skhi.IsInvalid()) + { + RakNet::BitStream bsOut; + bsOut.Write((unsigned char) ID_RPC_REMOTE_ERROR); + bsOut.Write((unsigned char) RPC_ERROR_FUNCTION_NOT_REGISTERED); + bsOut.Write(functionName.C_String(),(unsigned int) functionName.GetLength()+1); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + void ( *fp ) ( RakNet::BitStream *, RakNet::BitStream *, Packet * ); + fp = registeredBlockingFunctions.ItemAtIndex(skhi); + RakNet::BitStream returnData; + bsIn.AlignReadToByteBoundary(); + fp(&bsIn, &returnData, packet); + + RakNet::BitStream out; + out.Write((MessageID) ID_RPC_PLUGIN); + out.Write((MessageID) ID_RPC4_RETURN); + returnData.ResetReadPointer(); + out.AlignWriteToByteBoundary(); + out.Write(returnData); + SendUnified(&out,IMMEDIATE_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false); + } + } + else if (packet->data[1]==ID_RPC4_SIGNAL) + { + RakNet::RakString sharedIdentifier; + bsIn.ReadCompressed(sharedIdentifier); + DataStructures::HashIndex functionIndex; + functionIndex = localSlots.GetIndexOf(sharedIdentifier); + RakNet::BitStream serializedParameters; + bsIn.AlignReadToByteBoundary(); + bsIn.Read(&serializedParameters); + InvokeSignal(functionIndex, &serializedParameters, packet); + } + else + { + RakAssert(packet->data[1]==ID_RPC4_RETURN); + blockingReturnValue.Reset(); + blockingReturnValue.Read(bsIn); + gotBlockingReturnValue=true; + } + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + bool objectExists; + unsigned int index, index2; + index = localCallbacks.GetIndexFromKey(packet->data[0],&objectExists); + if (objectExists) + { + LocalCallback *lc; + lc = localCallbacks[index]; + for (index2=0; index2 < lc->functions.Size(); index2++) + { + RakNet::BitStream bsIn(packet->data, packet->length, false); + + DataStructures::HashIndex skhi = registeredNonblockingFunctions.GetIndexOf(lc->functions[index2].C_String()); + if (skhi.IsInvalid()==false) + { + void ( *fp ) ( RakNet::BitStream *, Packet * ); + fp = registeredNonblockingFunctions.ItemAtIndex(skhi); + bsIn.AlignReadToByteBoundary(); + fp(&bsIn,packet); + } + } + } + + return RR_CONTINUE_PROCESSING; +} +DataStructures::HashIndex RPC4::GetLocalSlotIndex(const char *sharedIdentifier) +{ + return localSlots.GetIndexOf(sharedIdentifier); +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/RPC4Plugin.h b/src/raknet/RPC4Plugin.h index d5296a6..cce5978 100755 --- a/src/raknet/RPC4Plugin.h +++ b/src/raknet/RPC4Plugin.h @@ -1,236 +1,236 @@ -/// \file -/// \brief Remote procedure call, supporting C functions only. No external dependencies required. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_RPC4Plugin==1 - -#ifndef __RPC_4_PLUGIN_H -#define __RPC_4_PLUGIN_H - -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "RakNetTypes.h" -#include "BitStream.h" -#include "RakString.h" -#include "NetworkIDObject.h" -#include "DS_Hash.h" -#include "DS_OrderedList.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -/// \defgroup RPC_PLUGIN_GROUP RPC -/// \brief Remote procedure calls, without external dependencies. -/// \details This should not be used at the same time as RPC3. This is a less functional version of RPC3, and is here for users that do not want the Boost dependency of RPC3. -/// \ingroup PLUGINS_GROUP - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; -class NetworkIDManager; - - /// \brief Error codes returned by a remote system as to why an RPC function call cannot execute - /// \details Error code follows packet ID ID_RPC_REMOTE_ERROR, that is packet->data[1]
      - /// Name of the function will be appended starting at packet->data[2] - /// \ingroup RPC_PLUGIN_GROUP - enum RPCErrorCodes - { - /// Named function was not registered with RegisterFunction(). Check your spelling. - RPC_ERROR_FUNCTION_NOT_REGISTERED, - }; - - /// \brief Instantiate this class globally if you want to register a function with RPC4 at the global space - class RAK_DLL_EXPORT RPC4GlobalRegistration - { - public: - /// \brief Queue a call to RPC4::RegisterFunction() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. - RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )); - - /// \brief Queue a call to RPC4::RegisterSlot() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. - RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority); - - /// \brief Queue a call to RPC4::RegisterBlockingFunction() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. - RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )); - - /// \brief Queue a call to RPC4::RegisterLocalCallback() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. - RPC4GlobalRegistration(const char* uniqueID, MessageID messageId); - }; - - /// \brief The RPC4 plugin is just an association between a C function pointer and a string. - /// \details It is for users that want to use RPC, but do not want to use boost. - /// You do not have the automatic serialization or other features of RPC3, and C++ member calls are not supported. - /// \note You cannot use RPC4 at the same time as RPC3Plugin - /// \ingroup RPC_PLUGIN_GROUP - class RAK_DLL_EXPORT RPC4 : public PluginInterface2 - { - public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(RPC4) - - // Constructor - RPC4(); - - // Destructor - virtual ~RPC4(); - - /// \deprecated Use RegisterSlot - /// \brief Register a function pointer to be callable from a remote system - /// \details The hash of the function name will be stored as an association with the function pointer - /// When a call is made to call this function from the \a Call() or CallLoopback() function, the function pointer will be invoked with the passed bitStream to Call() and the actual Packet that RakNet got. - /// \sa RegisterPacketCallback() - /// \param[in] uniqueID Identifier to be associated with \a functionPointer. If this identifier is already in use, the call will return false. - /// \param[in] functionPointer C function pointer to be called - /// \return True if the hash of uniqueID is not in use, false otherwise. - bool RegisterFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )); - - /// Register a slot, which is a function pointer to one or more implementations that supports this function signature - /// When a signal occurs, all slots with the same identifier are called. - /// \param[in] sharedIdentifier A string to identify the slot. Recommended to be the same as the name of the function. - /// \param[in] functionPtr Pointer to the function. For C, just pass the name of the function. For C++, use ARPC_REGISTER_CPP_FUNCTION - /// \param[in] callPriority Slots are called by order of the highest callPriority first. For slots with the same priority, they are called in the order they are registered - void RegisterSlot(const char *sharedIdentifier, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority); - - /// \brief Same as \a RegisterFunction, but is called with CallBlocking() instead of Call() and returns a value to the caller - bool RegisterBlockingFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )); - - /// \deprecated Use RegisterSlot and invoke on self only when the packet you want arrives - /// When a RakNet Packet with the specified identifier is returned, execute CallLoopback() on a function previously registered with RegisterFunction() - /// For example, you could call "OnClosedConnection" whenever you get ID_DISCONNECTION_NOTIFICATION or ID_CONNECTION_LOST - /// \param[in] uniqueID Identifier passed to RegisterFunction() - /// \param[in] messageId What RakNet packet ID to call on, for example ID_DISCONNECTION_NOTIFICATION or ID_CONNECTION_LOST - void RegisterLocalCallback(const char* uniqueID, MessageID messageId); - - /// \brief Unregister a function pointer previously registered with RegisterFunction() - /// \param[in] Identifier originally passed to RegisterFunction() - /// \return True if the hash of uniqueID was in use, and hence removed. false otherwise. - bool UnregisterFunction(const char* uniqueID); - - /// \brief Same as UnregisterFunction, except for a blocking function - bool UnregisterBlockingFunction(const char* uniqueID); - - /// Remove the association created with RegisterPacketCallback() - /// \param[in] uniqueID Identifier passed as uniqueID to RegisterLocalCallback() - /// \param[in] messageId Identifier passed as messageId to RegisterLocalCallback() - /// \return True if the combination of uniqueID and messageId was in use, and hence removed - bool UnregisterLocalCallback(const char* uniqueID, MessageID messageId); - - /// Remove the association created with RegisterSlot() - /// \param[in] sharedIdentifier Identifier passed as sharedIdentifier to RegisterSlot() - bool UnregisterSlot(const char* sharedIdentifier); - - /// \deprecated Use RegisterSlot() and Signal() with your own RakNetGUID as the send target - /// Send to the attached instance of RakPeer. See RakPeerInterface::SendLoopback() - /// \param[in] Identifier originally passed to RegisterFunction() on the local system - /// \param[in] bitStream bitStream encoded data to send to the function callback - void CallLoopback( const char* uniqueID, RakNet::BitStream * bitStream ); - - /// \deprecated, use Signal() - /// Send to the specified remote instance of RakPeer. - /// \param[in] uniqueID Identifier originally passed to RegisterFunction() on the remote system(s) - /// \param[in] bitStream bitStream encoded data to send to the function callback - /// \param[in] priority See RakPeerInterface::Send() - /// \param[in] reliability See RakPeerInterface::Send() - /// \param[in] orderingChannel See RakPeerInterface::Send() - /// \param[in] systemIdentifier See RakPeerInterface::Send() - /// \param[in] broadcast See RakPeerInterface::Send() - void Call( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); - - /// \brief Same as call, but don't return until the remote system replies. - /// Broadcasting parameter does not exist, this can only call one remote system - /// \note This function does not return until the remote system responds, disconnects, or was never connected to begin with - /// \param[in] Identifier originally passed to RegisterBlockingFunction() on the remote system(s) - /// \param[in] bitStream bitStream encoded data to send to the function callback - /// \param[in] priority See RakPeerInterface::Send() - /// \param[in] reliability See RakPeerInterface::Send() - /// \param[in] orderingChannel See RakPeerInterface::Send() - /// \param[in] systemIdentifier See RakPeerInterface::Send() - /// \param[out] returnData Written to by the function registered with RegisterBlockingFunction. - /// \return true if successfully called. False on disconnect, function not registered, or not connected to begin with - bool CallBlocking( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, RakNet::BitStream *returnData ); - - /// Calls zero or more functions identified by sharedIdentifier registered with RegisterSlot() - /// \param[in] sharedIdentifier parameter of the same name passed to RegisterSlot() on the remote system - /// \param[in] bitStream bitStream encoded data to send to the function callback - /// \param[in] priority See RakPeerInterface::Send() - /// \param[in] reliability See RakPeerInterface::Send() - /// \param[in] orderingChannel See RakPeerInterface::Send() - /// \param[in] systemIdentifier See RakPeerInterface::Send() - /// \param[in] broadcast See RakPeerInterface::Send() - /// \param[in] invokeLocal If true, also sends to self. - void Signal(const char *sharedIdentifier, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool invokeLocal); - - /// If called while processing a slot, no further slots for the currently executing signal will be executed - void InterruptSignal(void); - - /// \internal - struct LocalCallback - { - MessageID messageId; - DataStructures::OrderedList functions; - }; - static int LocalCallbackComp(const MessageID &key, LocalCallback* const &data ); - - /// \internal - // Callable object, along with priority to call relative to other objects - struct LocalSlotObject - { - LocalSlotObject() {} - LocalSlotObject(unsigned int _registrationCount,int _callPriority, void ( *_functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) - {registrationCount=_registrationCount;callPriority=_callPriority;functionPointer=_functionPointer;} - ~LocalSlotObject() {} - - // Used so slots are called in the order they are registered - unsigned int registrationCount; - int callPriority; - void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ); - }; - - static int LocalSlotObjectComp( const LocalSlotObject &key, const LocalSlotObject &data ); - - /// \internal - struct LocalSlot - { - DataStructures::OrderedList slotObjects; - }; - DataStructures::Hash localSlots; - - protected: - - // -------------------------------------------------------------------------------------------- - // Packet handling functions - // -------------------------------------------------------------------------------------------- - virtual void OnAttach(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - - DataStructures::Hash registeredNonblockingFunctions; - DataStructures::Hash registeredBlockingFunctions; - DataStructures::OrderedList localCallbacks; - - RakNet::BitStream blockingReturnValue; - bool gotBlockingReturnValue; - - DataStructures::HashIndex GetLocalSlotIndex(const char *sharedIdentifier); - - /// Used so slots are called in the order they are registered - unsigned int nextSlotRegistrationCount; - - bool interruptSignal; - - void InvokeSignal(DataStructures::HashIndex functionIndex, RakNet::BitStream *serializedParameters, Packet *packet); - }; - -} // End namespace - -#endif - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Remote procedure call, supporting C functions only. No external dependencies required. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_RPC4Plugin==1 + +#ifndef __RPC_4_PLUGIN_H +#define __RPC_4_PLUGIN_H + +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "RakNetTypes.h" +#include "BitStream.h" +#include "RakString.h" +#include "NetworkIDObject.h" +#include "DS_Hash.h" +#include "DS_OrderedList.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// \defgroup RPC_PLUGIN_GROUP RPC +/// \brief Remote procedure calls, without external dependencies. +/// \details This should not be used at the same time as RPC3. This is a less functional version of RPC3, and is here for users that do not want the Boost dependency of RPC3. +/// \ingroup PLUGINS_GROUP + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; +class NetworkIDManager; + + /// \brief Error codes returned by a remote system as to why an RPC function call cannot execute + /// \details Error code follows packet ID ID_RPC_REMOTE_ERROR, that is packet->data[1]
      + /// Name of the function will be appended starting at packet->data[2] + /// \ingroup RPC_PLUGIN_GROUP + enum RPCErrorCodes + { + /// Named function was not registered with RegisterFunction(). Check your spelling. + RPC_ERROR_FUNCTION_NOT_REGISTERED, + }; + + /// \brief Instantiate this class globally if you want to register a function with RPC4 at the global space + class RAK_DLL_EXPORT RPC4GlobalRegistration + { + public: + /// \brief Queue a call to RPC4::RegisterFunction() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. + RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )); + + /// \brief Queue a call to RPC4::RegisterSlot() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. + RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority); + + /// \brief Queue a call to RPC4::RegisterBlockingFunction() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. + RPC4GlobalRegistration(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )); + + /// \brief Queue a call to RPC4::RegisterLocalCallback() globally. Actual call occurs once RPC4 is attached to an instance of RakPeer or TCPInterface. + RPC4GlobalRegistration(const char* uniqueID, MessageID messageId); + }; + + /// \brief The RPC4 plugin is just an association between a C function pointer and a string. + /// \details It is for users that want to use RPC, but do not want to use boost. + /// You do not have the automatic serialization or other features of RPC3, and C++ member calls are not supported. + /// \note You cannot use RPC4 at the same time as RPC3Plugin + /// \ingroup RPC_PLUGIN_GROUP + class RAK_DLL_EXPORT RPC4 : public PluginInterface2 + { + public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(RPC4) + + // Constructor + RPC4(); + + // Destructor + virtual ~RPC4(); + + /// \deprecated Use RegisterSlot + /// \brief Register a function pointer to be callable from a remote system + /// \details The hash of the function name will be stored as an association with the function pointer + /// When a call is made to call this function from the \a Call() or CallLoopback() function, the function pointer will be invoked with the passed bitStream to Call() and the actual Packet that RakNet got. + /// \sa RegisterPacketCallback() + /// \param[in] uniqueID Identifier to be associated with \a functionPointer. If this identifier is already in use, the call will return false. + /// \param[in] functionPointer C function pointer to be called + /// \return True if the hash of uniqueID is not in use, false otherwise. + bool RegisterFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet )); + + /// Register a slot, which is a function pointer to one or more implementations that supports this function signature + /// When a signal occurs, all slots with the same identifier are called. + /// \param[in] sharedIdentifier A string to identify the slot. Recommended to be the same as the name of the function. + /// \param[in] functionPtr Pointer to the function. For C, just pass the name of the function. For C++, use ARPC_REGISTER_CPP_FUNCTION + /// \param[in] callPriority Slots are called by order of the highest callPriority first. For slots with the same priority, they are called in the order they are registered + void RegisterSlot(const char *sharedIdentifier, void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ), int callPriority); + + /// \brief Same as \a RegisterFunction, but is called with CallBlocking() instead of Call() and returns a value to the caller + bool RegisterBlockingFunction(const char* uniqueID, void ( *functionPointer ) ( RakNet::BitStream *userData, RakNet::BitStream *returnData, Packet *packet )); + + /// \deprecated Use RegisterSlot and invoke on self only when the packet you want arrives + /// When a RakNet Packet with the specified identifier is returned, execute CallLoopback() on a function previously registered with RegisterFunction() + /// For example, you could call "OnClosedConnection" whenever you get ID_DISCONNECTION_NOTIFICATION or ID_CONNECTION_LOST + /// \param[in] uniqueID Identifier passed to RegisterFunction() + /// \param[in] messageId What RakNet packet ID to call on, for example ID_DISCONNECTION_NOTIFICATION or ID_CONNECTION_LOST + void RegisterLocalCallback(const char* uniqueID, MessageID messageId); + + /// \brief Unregister a function pointer previously registered with RegisterFunction() + /// \param[in] Identifier originally passed to RegisterFunction() + /// \return True if the hash of uniqueID was in use, and hence removed. false otherwise. + bool UnregisterFunction(const char* uniqueID); + + /// \brief Same as UnregisterFunction, except for a blocking function + bool UnregisterBlockingFunction(const char* uniqueID); + + /// Remove the association created with RegisterPacketCallback() + /// \param[in] uniqueID Identifier passed as uniqueID to RegisterLocalCallback() + /// \param[in] messageId Identifier passed as messageId to RegisterLocalCallback() + /// \return True if the combination of uniqueID and messageId was in use, and hence removed + bool UnregisterLocalCallback(const char* uniqueID, MessageID messageId); + + /// Remove the association created with RegisterSlot() + /// \param[in] sharedIdentifier Identifier passed as sharedIdentifier to RegisterSlot() + bool UnregisterSlot(const char* sharedIdentifier); + + /// \deprecated Use RegisterSlot() and Signal() with your own RakNetGUID as the send target + /// Send to the attached instance of RakPeer. See RakPeerInterface::SendLoopback() + /// \param[in] Identifier originally passed to RegisterFunction() on the local system + /// \param[in] bitStream bitStream encoded data to send to the function callback + void CallLoopback( const char* uniqueID, RakNet::BitStream * bitStream ); + + /// \deprecated, use Signal() + /// Send to the specified remote instance of RakPeer. + /// \param[in] uniqueID Identifier originally passed to RegisterFunction() on the remote system(s) + /// \param[in] bitStream bitStream encoded data to send to the function callback + /// \param[in] priority See RakPeerInterface::Send() + /// \param[in] reliability See RakPeerInterface::Send() + /// \param[in] orderingChannel See RakPeerInterface::Send() + /// \param[in] systemIdentifier See RakPeerInterface::Send() + /// \param[in] broadcast See RakPeerInterface::Send() + void Call( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast ); + + /// \brief Same as call, but don't return until the remote system replies. + /// Broadcasting parameter does not exist, this can only call one remote system + /// \note This function does not return until the remote system responds, disconnects, or was never connected to begin with + /// \param[in] Identifier originally passed to RegisterBlockingFunction() on the remote system(s) + /// \param[in] bitStream bitStream encoded data to send to the function callback + /// \param[in] priority See RakPeerInterface::Send() + /// \param[in] reliability See RakPeerInterface::Send() + /// \param[in] orderingChannel See RakPeerInterface::Send() + /// \param[in] systemIdentifier See RakPeerInterface::Send() + /// \param[out] returnData Written to by the function registered with RegisterBlockingFunction. + /// \return true if successfully called. False on disconnect, function not registered, or not connected to begin with + bool CallBlocking( const char* uniqueID, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, RakNet::BitStream *returnData ); + + /// Calls zero or more functions identified by sharedIdentifier registered with RegisterSlot() + /// \param[in] sharedIdentifier parameter of the same name passed to RegisterSlot() on the remote system + /// \param[in] bitStream bitStream encoded data to send to the function callback + /// \param[in] priority See RakPeerInterface::Send() + /// \param[in] reliability See RakPeerInterface::Send() + /// \param[in] orderingChannel See RakPeerInterface::Send() + /// \param[in] systemIdentifier See RakPeerInterface::Send() + /// \param[in] broadcast See RakPeerInterface::Send() + /// \param[in] invokeLocal If true, also sends to self. + void Signal(const char *sharedIdentifier, RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool invokeLocal); + + /// If called while processing a slot, no further slots for the currently executing signal will be executed + void InterruptSignal(void); + + /// \internal + struct LocalCallback + { + MessageID messageId; + DataStructures::OrderedList functions; + }; + static int LocalCallbackComp(const MessageID &key, LocalCallback* const &data ); + + /// \internal + // Callable object, along with priority to call relative to other objects + struct LocalSlotObject + { + LocalSlotObject() {} + LocalSlotObject(unsigned int _registrationCount,int _callPriority, void ( *_functionPointer ) ( RakNet::BitStream *userData, Packet *packet )) + {registrationCount=_registrationCount;callPriority=_callPriority;functionPointer=_functionPointer;} + ~LocalSlotObject() {} + + // Used so slots are called in the order they are registered + unsigned int registrationCount; + int callPriority; + void ( *functionPointer ) ( RakNet::BitStream *userData, Packet *packet ); + }; + + static int LocalSlotObjectComp( const LocalSlotObject &key, const LocalSlotObject &data ); + + /// \internal + struct LocalSlot + { + DataStructures::OrderedList slotObjects; + }; + DataStructures::Hash localSlots; + + protected: + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual void OnAttach(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + + DataStructures::Hash registeredNonblockingFunctions; + DataStructures::Hash registeredBlockingFunctions; + DataStructures::OrderedList localCallbacks; + + RakNet::BitStream blockingReturnValue; + bool gotBlockingReturnValue; + + DataStructures::HashIndex GetLocalSlotIndex(const char *sharedIdentifier); + + /// Used so slots are called in the order they are registered + unsigned int nextSlotRegistrationCount; + + bool interruptSignal; + + void InvokeSignal(DataStructures::HashIndex functionIndex, RakNet::BitStream *serializedParameters, Packet *packet); + }; + +} // End namespace + +#endif + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/Rackspace.cpp b/src/raknet/Rackspace.cpp index 9d0dfe8..13541ce 100755 --- a/src/raknet/Rackspace.cpp +++ b/src/raknet/Rackspace.cpp @@ -1,655 +1,655 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_Rackspace==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "Rackspace.h" -#include "RakString.h" -#include "TCPInterface.h" - -using namespace RakNet; - -Rackspace::Rackspace() -{ - tcpInterface=0; -} - -Rackspace::~Rackspace() -{ - -} - -void Rackspace::AddEventCallback(RackspaceEventCallback *callback) -{ - unsigned int idx = eventCallbacks.GetIndexOf(callback); - if (idx == (unsigned int)-1) - eventCallbacks.Push(callback,_FILE_AND_LINE_); -} -void Rackspace::RemoveEventCallback(RackspaceEventCallback *callback) -{ - unsigned int idx = eventCallbacks.GetIndexOf(callback); - if (idx != (unsigned int)-1) - eventCallbacks.RemoveAtIndex(idx); -} -void Rackspace::ClearEventCallbacks(void) -{ - eventCallbacks.Clear(true, _FILE_AND_LINE_); -} -SystemAddress Rackspace::Authenticate(TCPInterface *_tcpInterface, const char *_authenticationURL, const char *_rackspaceCloudUsername, const char *_apiAccessKey) -{ - unsigned int index = GetOperationOfTypeIndex(RO_CONNECT_AND_AUTHENTICATE); - if (index!=(unsigned int)-1) - { - // In progress - return operations[index].connectionAddress; - } - - tcpInterface=_tcpInterface; - - rackspaceCloudUsername=_rackspaceCloudUsername; - apiAccessKey=_apiAccessKey; - - unsigned int i; - - RackspaceOperation ro; - ro.type=RO_CONNECT_AND_AUTHENTICATE; - ro.isPendingAuthentication=false; - - RakAssert(tcpInterface->WasStarted()); - ro.connectionAddress=tcpInterface->Connect(_authenticationURL,443,true); - if (ro.connectionAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnConnectionAttemptFailure(RO_CONNECT_AND_AUTHENTICATE, _authenticationURL); - - return UNASSIGNED_SYSTEM_ADDRESS; - } - -#if OPEN_SSL_CLIENT_SUPPORT==1 - tcpInterface->StartSSLClient(ro.connectionAddress); -#endif - - RakNet::RakString command( - "GET /v1.0 HTTP/1.1\n" - "Host: %s\n" - "X-Auth-User: %s\n" - "X-Auth-Key: %s\n\n" - ,_authenticationURL, _rackspaceCloudUsername, _apiAccessKey); - tcpInterface->Send(command.C_String(), (unsigned int) command.GetLength(), ro.connectionAddress, false); - - operations.Insert(ro,_FILE_AND_LINE_); - return ro.connectionAddress; -} - -const char * Rackspace::EventTypeToString(RackspaceEventType eventType) -{ - switch (eventType) - { - case RET_Success_200: - return "Success_200"; - case RET_Success_201: - return "Success_201"; - case RET_Success_202: - return "Success_202"; - case RET_Success_203: - return "Success_203"; - case RET_Success_204: - return "Success_204"; - case RET_Cloud_Servers_Fault_500: - return "Cloud_Servers_Fault_500"; - case RET_Service_Unavailable_503: - return "Service_Unavailable_503"; - case RET_Unauthorized_401: - return "Unauthorized_401"; - case RET_Bad_Request_400: - return "Bad_Request_400"; - case RET_Over_Limit_413: - return "Over_Limit_413"; - case RET_Bad_Media_Type_415: - return "Bad_Media_Type_415"; - case RET_Item_Not_Found_404: - return "Item_Not_Found_404"; - case RET_Build_In_Progress_409: - return "Build_In_Progress_409"; - case RET_Resize_Not_Allowed_403: - return "Resize_Not_Allowed_403"; - case RET_Connection_Closed_Without_Reponse: - return "Connection_Closed_Without_Reponse"; - case RET_Unknown_Failure: - return "Unknown_Failure"; - } - return "Unknown event type (bug)"; -} -void Rackspace::AddOperation(RackspaceOperationType type, RakNet::RakString httpCommand, RakNet::RakString operation, RakNet::RakString xml) -{ - RackspaceOperation ro; - ro.type=type; - ro.httpCommand=httpCommand; - ro.operation=operation; - ro.xml=xml; - ro.isPendingAuthentication=HasOperationOfType(RO_CONNECT_AND_AUTHENTICATE); - if (ro.isPendingAuthentication==false) - { - if (ExecuteOperation(ro)) - operations.Insert(ro,_FILE_AND_LINE_); - } - else - operations.Insert(ro,_FILE_AND_LINE_); -} -void Rackspace::ListServers(void) -{ - AddOperation(RO_LIST_SERVERS, "GET", "servers", ""); -} -void Rackspace::ListServersWithDetails(void) -{ - AddOperation(RO_LIST_SERVERS_WITH_DETAILS, "GET", "servers/detail", ""); -} -void Rackspace::CreateServer(RakNet::RakString name, RakNet::RakString imageId, RakNet::RakString flavorId) -{ - RakNet::RakString xml( - "" - "" - "" - ,name.C_String() ,imageId.C_String(), flavorId.C_String()); - AddOperation(RO_CREATE_SERVER, "POST", "servers", xml); -} -void Rackspace::GetServerDetails(RakNet::RakString serverId) -{ - AddOperation(RO_GET_SERVER_DETAILS, "GET", RakNet::RakString("servers/%s", serverId.C_String()), ""); -} -void Rackspace::UpdateServerNameOrPassword(RakNet::RakString serverId, RakNet::RakString newName, RakNet::RakString newPassword) -{ - if (newName.IsEmpty() && newPassword.IsEmpty()) - return; - RakNet::RakString xml( - "" - "" - "", - rebootType.C_String()); - - AddOperation(RO_REBOOT_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::RebuildServer(RakNet::RakString serverId, RakNet::RakString imageId) -{ - RakNet::RakString xml( - "" - "", - imageId.C_String()); - - AddOperation(RO_REBUILD_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::ResizeServer(RakNet::RakString serverId, RakNet::RakString flavorId) -{ - RakNet::RakString xml( - "" - "", - flavorId.C_String()); - - AddOperation(RO_RESIZE_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::ConfirmResizedServer(RakNet::RakString serverId) -{ - RakNet::RakString xml( - "" - ""); - AddOperation(RO_CONFIRM_RESIZED_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::RevertResizedServer(RakNet::RakString serverId) -{ - RakNet::RakString xml( - "" - ""); - AddOperation(RO_REVERT_RESIZED_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); -} -void Rackspace::ListFlavors(void) -{ - AddOperation(RO_LIST_FLAVORS, "GET", "flavors", ""); -} -void Rackspace::GetFlavorDetails(RakNet::RakString flavorId) -{ - AddOperation(RO_GET_FLAVOR_DETAILS, "GET", RakNet::RakString("flavors/%s", flavorId.C_String()), ""); -} -void Rackspace::ListImages(void) -{ - AddOperation(RO_LIST_IMAGES, "GET", "images", ""); -} -void Rackspace::CreateImage(RakNet::RakString serverId, RakNet::RakString imageName) -{ - RakNet::RakString xml( - "" - "", - imageName.C_String(),serverId.C_String()); - - AddOperation(RO_CREATE_IMAGE, "POST", "images", xml); -} -void Rackspace::GetImageDetails(RakNet::RakString imageId) -{ - AddOperation(RO_GET_IMAGE_DETAILS, "GET", RakNet::RakString("images/%s", imageId.C_String()), ""); -} -void Rackspace::DeleteImage(RakNet::RakString imageId) -{ - AddOperation(RO_DELETE_IMAGE, "DELETE", RakNet::RakString("images/%s", imageId.C_String()), ""); -} -void Rackspace::ListSharedIPGroups(void) -{ - AddOperation(RO_LIST_SHARED_IP_GROUPS, "GET", "shared_ip_groups", ""); -} -void Rackspace::ListSharedIPGroupsWithDetails(void) -{ - AddOperation(RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS, "GET", "shared_ip_groups/detail", ""); -} -void Rackspace::CreateSharedIPGroup(RakNet::RakString name, RakNet::RakString optionalServerId) -{ - RakNet::RakString xml( - "" - "", name.C_String()); - if (optionalServerId.IsEmpty()==false) - xml+=RakNet::RakString("", optionalServerId.C_String()); - xml+=""; - - AddOperation(RO_CREATE_SHARED_IP_GROUP, "POST", "shared_ip_groups", xml); -} -void Rackspace::GetSharedIPGroupDetails(RakNet::RakString groupId) -{ - AddOperation(RO_GET_SHARED_IP_GROUP_DETAILS, "GET", RakNet::RakString("shared_ip_groups/%s", groupId.C_String()), ""); -} -void Rackspace::DeleteSharedIPGroup(RakNet::RakString groupId) -{ - AddOperation(RO_DELETE_SHARED_IP_GROUP, "DELETE", RakNet::RakString("shared_ip_groups/%s", groupId.C_String()), ""); -} -void Rackspace::OnClosedConnection(SystemAddress systemAddress) -{ - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - return; - - unsigned int i, operationsIndex; - operationsIndex=0; - while (operationsIndex < operations.Size()) - { - if (operations[operationsIndex].isPendingAuthentication==false && operations[operationsIndex].connectionAddress==systemAddress) - { - RackspaceOperation ro = operations[operationsIndex]; - operations.RemoveAtIndex(operationsIndex); - - RakNet::RakString packetDataString = ro.incomingStream; - const char *packetData = packetDataString.C_String(); - - char resultCodeStr[32]; - int resultCodeInt; - - RackspaceEventType rackspaceEventType; - char *result; - result=strstr((char*) packetData, "HTTP/1.1 "); - if (result!=0) - { - result+=strlen("HTTP/1.1 "); - for (i=0; i < sizeof(resultCodeStr)-1 && result[i] && result[i]>='0' && result[i]<='9'; i++) - resultCodeStr[i]=result[i]; - resultCodeStr[i]=0; - resultCodeInt=atoi(resultCodeStr); - - switch (resultCodeInt) - { - case 200: rackspaceEventType=RET_Success_200; break; - case 201: rackspaceEventType=RET_Success_201; break; - case 202: rackspaceEventType=RET_Success_202; break; - case 203: rackspaceEventType=RET_Success_203; break; - case 204: rackspaceEventType=RET_Success_204; break; - case 500: rackspaceEventType=RET_Cloud_Servers_Fault_500; break; - case 503: rackspaceEventType=RET_Service_Unavailable_503; break; - case 401: rackspaceEventType=RET_Unauthorized_401; break; - case 400: rackspaceEventType=RET_Bad_Request_400; break; - case 413: rackspaceEventType=RET_Over_Limit_413; break; - case 415: rackspaceEventType=RET_Bad_Media_Type_415; break; - case 404: rackspaceEventType=RET_Item_Not_Found_404; break; - case 409: rackspaceEventType=RET_Build_In_Progress_409; break; - case 403: rackspaceEventType=RET_Resize_Not_Allowed_403; break; - default: rackspaceEventType=RET_Unknown_Failure; break; - } - } - else - { - rackspaceEventType=RET_Connection_Closed_Without_Reponse; - } - - switch (ro.type) - { - case RO_CONNECT_AND_AUTHENTICATE: - { - if (rackspaceEventType==RET_Success_204) - { - RakNet::RakString header; - ReadLine(packetData, "X-Server-Management-Url: ", serverManagementURL); - serverManagementURL.SplitURI(header, serverManagementDomain, serverManagementPath); - ReadLine(packetData, "X-Storage-Url: ", storageURL); - storageURL.SplitURI(header, storageDomain, storagePath); - ReadLine(packetData, "X-CDN-Management-Url: ", cdnManagementURL); - cdnManagementURL.SplitURI(header, cdnManagementDomain, cdnManagementPath); - ReadLine(packetData, "X-Auth-Token: ", authToken); - ReadLine(packetData, "X-Storage-Token: ", storageToken); - - operationsIndex=0; - while (operationsIndex < operations.Size()) - { - if (operations[operationsIndex].isPendingAuthentication==true) - { - operations[operationsIndex].isPendingAuthentication=false; - if (ExecuteOperation(operations[operationsIndex])==false) - { - operations.RemoveAtIndex(operationsIndex); - } - else - operationsIndex++; - } - else - operationsIndex++; - } - - // Restart in list - operationsIndex=0; - } - - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnAuthenticationResult(rackspaceEventType, (const char*) packetData); - - break; - } - case RO_LIST_SERVERS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListServersResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_SERVERS_WITH_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListServersWithDetailsResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_CREATE_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnCreateServerResult(rackspaceEventType, (const char*) packetData); - break; - } - - case RO_GET_SERVER_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnGetServerDetails(rackspaceEventType, (const char*) packetData); - break; - } - case RO_UPDATE_SERVER_NAME_OR_PASSWORD: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnUpdateServerNameOrPassword(rackspaceEventType, (const char*) packetData); - break; - } - case RO_DELETE_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnDeleteServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_SERVER_ADDRESSES: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListServerAddresses(rackspaceEventType, (const char*) packetData); - break; - } - case RO_SHARE_SERVER_ADDRESS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnShareServerAddress(rackspaceEventType, (const char*) packetData); - break; - } - case RO_DELETE_SERVER_ADDRESS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnDeleteServerAddress(rackspaceEventType, (const char*) packetData); - break; - } - case RO_REBOOT_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnRebootServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_REBUILD_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnRebuildServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_RESIZE_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnResizeServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_CONFIRM_RESIZED_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnConfirmResizedServer(rackspaceEventType, (const char*) packetData); - break; - } - case RO_REVERT_RESIZED_SERVER: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnRevertResizedServer(rackspaceEventType, (const char*) packetData); - break; - } - - - case RO_LIST_FLAVORS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListFlavorsResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_GET_FLAVOR_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnGetFlavorDetailsResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_IMAGES: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListImagesResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_CREATE_IMAGE: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnCreateImageResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_GET_IMAGE_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnGetImageDetailsResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_DELETE_IMAGE: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnDeleteImageResult(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_SHARED_IP_GROUPS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListSharedIPGroups(rackspaceEventType, (const char*) packetData); - break; - } - case RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnListSharedIPGroupsWithDetails(rackspaceEventType, (const char*) packetData); - break; - } - case RO_CREATE_SHARED_IP_GROUP: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnCreateSharedIPGroup(rackspaceEventType, (const char*) packetData); - break; - } - case RO_GET_SHARED_IP_GROUP_DETAILS: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnGetSharedIPGroupDetails(rackspaceEventType, (const char*) packetData); - break; - } - case RO_DELETE_SHARED_IP_GROUP: - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnDeleteSharedIPGroup(rackspaceEventType, (const char*) packetData); - break; - } - default: - break; - - } - } - else - { - operationsIndex++; - } - } -} -void Rackspace::OnReceive(Packet *packet) -{ - unsigned int operationsIndex; - for (operationsIndex=0; operationsIndex < operations.Size(); operationsIndex++) - { - if (operations[operationsIndex].isPendingAuthentication==false && operations[operationsIndex].connectionAddress==packet->systemAddress) - { - operations[operationsIndex].incomingStream+=packet->data; - } - } -} -bool Rackspace::ExecuteOperation(RackspaceOperation &ro) -{ - if (ConnectToServerManagementDomain(ro)==false) - return false; - - RakNet::RakString command( - "%s %s/%s HTTP/1.1\n" - "Host: %s\n" - "Content-Type: application/xml\n" - "Content-Length: %i\n" - "Accept: application/xml\n" - "X-Auth-Token: %s\n", - ro.httpCommand.C_String(), serverManagementPath.C_String(), ro.operation.C_String(), serverManagementDomain.C_String(), - ro.xml.GetLength(), - authToken.C_String()); - - if (ro.xml.IsEmpty()==false) - { - command+="\n"; - command+=ro.xml; - command+="\n"; - } - - command+="\n"; - - //printf(command.C_String()); - - tcpInterface->Send(command.C_String(), (unsigned int) command.GetLength(), ro.connectionAddress, false); - return true; -} -void Rackspace::ReadLine(const char *data, const char *stringStart, RakNet::RakString &output) -{ - output.Clear(); - - char *result, *resultEnd; - - result=strstr((char*) data, stringStart); - result+=strlen(stringStart); - if (result==0) - { - RakAssert(0); - return; - } - - output=result; - resultEnd=result; - while (*resultEnd && (*resultEnd!='\r') && (*resultEnd!='\n') ) - resultEnd++; - output.Truncate((unsigned int) (resultEnd-result)); -} - - -bool Rackspace::ConnectToServerManagementDomain(RackspaceOperation &ro) -{ - unsigned int i; - - ro.connectionAddress=tcpInterface->Connect(serverManagementDomain.C_String(),443,true); - if (ro.connectionAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < eventCallbacks.Size(); i++) - eventCallbacks[i]->OnConnectionAttemptFailure(ro.type, serverManagementURL); - return false; - } - -#if OPEN_SSL_CLIENT_SUPPORT==1 - tcpInterface->StartSSLClient(ro.connectionAddress); -#endif - - return true; -} -bool Rackspace::HasOperationOfType(RackspaceOperationType t) -{ - unsigned int i; - for (i=0; i < operations.Size(); i++) - { - if (operations[i].type==t) - return true; - } - return false; -} -unsigned int Rackspace::GetOperationOfTypeIndex(RackspaceOperationType t) -{ - unsigned int i; - for (i=0; i < operations.Size(); i++) - { - if (operations[i].type==t) - return i; - } - return (unsigned int) -1; -} - -#endif +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_Rackspace==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "Rackspace.h" +#include "RakString.h" +#include "TCPInterface.h" + +using namespace RakNet; + +Rackspace::Rackspace() +{ + tcpInterface=0; +} + +Rackspace::~Rackspace() +{ + +} + +void Rackspace::AddEventCallback(RackspaceEventCallback *callback) +{ + unsigned int idx = eventCallbacks.GetIndexOf(callback); + if (idx == (unsigned int)-1) + eventCallbacks.Push(callback,_FILE_AND_LINE_); +} +void Rackspace::RemoveEventCallback(RackspaceEventCallback *callback) +{ + unsigned int idx = eventCallbacks.GetIndexOf(callback); + if (idx != (unsigned int)-1) + eventCallbacks.RemoveAtIndex(idx); +} +void Rackspace::ClearEventCallbacks(void) +{ + eventCallbacks.Clear(true, _FILE_AND_LINE_); +} +SystemAddress Rackspace::Authenticate(TCPInterface *_tcpInterface, const char *_authenticationURL, const char *_rackspaceCloudUsername, const char *_apiAccessKey) +{ + unsigned int index = GetOperationOfTypeIndex(RO_CONNECT_AND_AUTHENTICATE); + if (index!=(unsigned int)-1) + { + // In progress + return operations[index].connectionAddress; + } + + tcpInterface=_tcpInterface; + + rackspaceCloudUsername=_rackspaceCloudUsername; + apiAccessKey=_apiAccessKey; + + unsigned int i; + + RackspaceOperation ro; + ro.type=RO_CONNECT_AND_AUTHENTICATE; + ro.isPendingAuthentication=false; + + RakAssert(tcpInterface->WasStarted()); + ro.connectionAddress=tcpInterface->Connect(_authenticationURL,443,true); + if (ro.connectionAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnConnectionAttemptFailure(RO_CONNECT_AND_AUTHENTICATE, _authenticationURL); + + return UNASSIGNED_SYSTEM_ADDRESS; + } + +#if OPEN_SSL_CLIENT_SUPPORT==1 + tcpInterface->StartSSLClient(ro.connectionAddress); +#endif + + RakNet::RakString command( + "GET /v1.0 HTTP/1.1\n" + "Host: %s\n" + "X-Auth-User: %s\n" + "X-Auth-Key: %s\n\n" + ,_authenticationURL, _rackspaceCloudUsername, _apiAccessKey); + tcpInterface->Send(command.C_String(), (unsigned int) command.GetLength(), ro.connectionAddress, false); + + operations.Insert(ro,_FILE_AND_LINE_); + return ro.connectionAddress; +} + +const char * Rackspace::EventTypeToString(RackspaceEventType eventType) +{ + switch (eventType) + { + case RET_Success_200: + return "Success_200"; + case RET_Success_201: + return "Success_201"; + case RET_Success_202: + return "Success_202"; + case RET_Success_203: + return "Success_203"; + case RET_Success_204: + return "Success_204"; + case RET_Cloud_Servers_Fault_500: + return "Cloud_Servers_Fault_500"; + case RET_Service_Unavailable_503: + return "Service_Unavailable_503"; + case RET_Unauthorized_401: + return "Unauthorized_401"; + case RET_Bad_Request_400: + return "Bad_Request_400"; + case RET_Over_Limit_413: + return "Over_Limit_413"; + case RET_Bad_Media_Type_415: + return "Bad_Media_Type_415"; + case RET_Item_Not_Found_404: + return "Item_Not_Found_404"; + case RET_Build_In_Progress_409: + return "Build_In_Progress_409"; + case RET_Resize_Not_Allowed_403: + return "Resize_Not_Allowed_403"; + case RET_Connection_Closed_Without_Reponse: + return "Connection_Closed_Without_Reponse"; + case RET_Unknown_Failure: + return "Unknown_Failure"; + } + return "Unknown event type (bug)"; +} +void Rackspace::AddOperation(RackspaceOperationType type, RakNet::RakString httpCommand, RakNet::RakString operation, RakNet::RakString xml) +{ + RackspaceOperation ro; + ro.type=type; + ro.httpCommand=httpCommand; + ro.operation=operation; + ro.xml=xml; + ro.isPendingAuthentication=HasOperationOfType(RO_CONNECT_AND_AUTHENTICATE); + if (ro.isPendingAuthentication==false) + { + if (ExecuteOperation(ro)) + operations.Insert(ro,_FILE_AND_LINE_); + } + else + operations.Insert(ro,_FILE_AND_LINE_); +} +void Rackspace::ListServers(void) +{ + AddOperation(RO_LIST_SERVERS, "GET", "servers", ""); +} +void Rackspace::ListServersWithDetails(void) +{ + AddOperation(RO_LIST_SERVERS_WITH_DETAILS, "GET", "servers/detail", ""); +} +void Rackspace::CreateServer(RakNet::RakString name, RakNet::RakString imageId, RakNet::RakString flavorId) +{ + RakNet::RakString xml( + "" + "" + "" + ,name.C_String() ,imageId.C_String(), flavorId.C_String()); + AddOperation(RO_CREATE_SERVER, "POST", "servers", xml); +} +void Rackspace::GetServerDetails(RakNet::RakString serverId) +{ + AddOperation(RO_GET_SERVER_DETAILS, "GET", RakNet::RakString("servers/%s", serverId.C_String()), ""); +} +void Rackspace::UpdateServerNameOrPassword(RakNet::RakString serverId, RakNet::RakString newName, RakNet::RakString newPassword) +{ + if (newName.IsEmpty() && newPassword.IsEmpty()) + return; + RakNet::RakString xml( + "" + "" + "", + rebootType.C_String()); + + AddOperation(RO_REBOOT_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::RebuildServer(RakNet::RakString serverId, RakNet::RakString imageId) +{ + RakNet::RakString xml( + "" + "", + imageId.C_String()); + + AddOperation(RO_REBUILD_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::ResizeServer(RakNet::RakString serverId, RakNet::RakString flavorId) +{ + RakNet::RakString xml( + "" + "", + flavorId.C_String()); + + AddOperation(RO_RESIZE_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::ConfirmResizedServer(RakNet::RakString serverId) +{ + RakNet::RakString xml( + "" + ""); + AddOperation(RO_CONFIRM_RESIZED_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::RevertResizedServer(RakNet::RakString serverId) +{ + RakNet::RakString xml( + "" + ""); + AddOperation(RO_REVERT_RESIZED_SERVER, "POST", RakNet::RakString("servers/%s/action", serverId.C_String()), xml); +} +void Rackspace::ListFlavors(void) +{ + AddOperation(RO_LIST_FLAVORS, "GET", "flavors", ""); +} +void Rackspace::GetFlavorDetails(RakNet::RakString flavorId) +{ + AddOperation(RO_GET_FLAVOR_DETAILS, "GET", RakNet::RakString("flavors/%s", flavorId.C_String()), ""); +} +void Rackspace::ListImages(void) +{ + AddOperation(RO_LIST_IMAGES, "GET", "images", ""); +} +void Rackspace::CreateImage(RakNet::RakString serverId, RakNet::RakString imageName) +{ + RakNet::RakString xml( + "" + "", + imageName.C_String(),serverId.C_String()); + + AddOperation(RO_CREATE_IMAGE, "POST", "images", xml); +} +void Rackspace::GetImageDetails(RakNet::RakString imageId) +{ + AddOperation(RO_GET_IMAGE_DETAILS, "GET", RakNet::RakString("images/%s", imageId.C_String()), ""); +} +void Rackspace::DeleteImage(RakNet::RakString imageId) +{ + AddOperation(RO_DELETE_IMAGE, "DELETE", RakNet::RakString("images/%s", imageId.C_String()), ""); +} +void Rackspace::ListSharedIPGroups(void) +{ + AddOperation(RO_LIST_SHARED_IP_GROUPS, "GET", "shared_ip_groups", ""); +} +void Rackspace::ListSharedIPGroupsWithDetails(void) +{ + AddOperation(RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS, "GET", "shared_ip_groups/detail", ""); +} +void Rackspace::CreateSharedIPGroup(RakNet::RakString name, RakNet::RakString optionalServerId) +{ + RakNet::RakString xml( + "" + "", name.C_String()); + if (optionalServerId.IsEmpty()==false) + xml+=RakNet::RakString("", optionalServerId.C_String()); + xml+=""; + + AddOperation(RO_CREATE_SHARED_IP_GROUP, "POST", "shared_ip_groups", xml); +} +void Rackspace::GetSharedIPGroupDetails(RakNet::RakString groupId) +{ + AddOperation(RO_GET_SHARED_IP_GROUP_DETAILS, "GET", RakNet::RakString("shared_ip_groups/%s", groupId.C_String()), ""); +} +void Rackspace::DeleteSharedIPGroup(RakNet::RakString groupId) +{ + AddOperation(RO_DELETE_SHARED_IP_GROUP, "DELETE", RakNet::RakString("shared_ip_groups/%s", groupId.C_String()), ""); +} +void Rackspace::OnClosedConnection(SystemAddress systemAddress) +{ + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + return; + + unsigned int i, operationsIndex; + operationsIndex=0; + while (operationsIndex < operations.Size()) + { + if (operations[operationsIndex].isPendingAuthentication==false && operations[operationsIndex].connectionAddress==systemAddress) + { + RackspaceOperation ro = operations[operationsIndex]; + operations.RemoveAtIndex(operationsIndex); + + RakNet::RakString packetDataString = ro.incomingStream; + const char *packetData = packetDataString.C_String(); + + char resultCodeStr[32]; + int resultCodeInt; + + RackspaceEventType rackspaceEventType; + char *result; + result=strstr((char*) packetData, "HTTP/1.1 "); + if (result!=0) + { + result+=strlen("HTTP/1.1 "); + for (i=0; i < sizeof(resultCodeStr)-1 && result[i] && result[i]>='0' && result[i]<='9'; i++) + resultCodeStr[i]=result[i]; + resultCodeStr[i]=0; + resultCodeInt=atoi(resultCodeStr); + + switch (resultCodeInt) + { + case 200: rackspaceEventType=RET_Success_200; break; + case 201: rackspaceEventType=RET_Success_201; break; + case 202: rackspaceEventType=RET_Success_202; break; + case 203: rackspaceEventType=RET_Success_203; break; + case 204: rackspaceEventType=RET_Success_204; break; + case 500: rackspaceEventType=RET_Cloud_Servers_Fault_500; break; + case 503: rackspaceEventType=RET_Service_Unavailable_503; break; + case 401: rackspaceEventType=RET_Unauthorized_401; break; + case 400: rackspaceEventType=RET_Bad_Request_400; break; + case 413: rackspaceEventType=RET_Over_Limit_413; break; + case 415: rackspaceEventType=RET_Bad_Media_Type_415; break; + case 404: rackspaceEventType=RET_Item_Not_Found_404; break; + case 409: rackspaceEventType=RET_Build_In_Progress_409; break; + case 403: rackspaceEventType=RET_Resize_Not_Allowed_403; break; + default: rackspaceEventType=RET_Unknown_Failure; break; + } + } + else + { + rackspaceEventType=RET_Connection_Closed_Without_Reponse; + } + + switch (ro.type) + { + case RO_CONNECT_AND_AUTHENTICATE: + { + if (rackspaceEventType==RET_Success_204) + { + RakNet::RakString header; + ReadLine(packetData, "X-Server-Management-Url: ", serverManagementURL); + serverManagementURL.SplitURI(header, serverManagementDomain, serverManagementPath); + ReadLine(packetData, "X-Storage-Url: ", storageURL); + storageURL.SplitURI(header, storageDomain, storagePath); + ReadLine(packetData, "X-CDN-Management-Url: ", cdnManagementURL); + cdnManagementURL.SplitURI(header, cdnManagementDomain, cdnManagementPath); + ReadLine(packetData, "X-Auth-Token: ", authToken); + ReadLine(packetData, "X-Storage-Token: ", storageToken); + + operationsIndex=0; + while (operationsIndex < operations.Size()) + { + if (operations[operationsIndex].isPendingAuthentication==true) + { + operations[operationsIndex].isPendingAuthentication=false; + if (ExecuteOperation(operations[operationsIndex])==false) + { + operations.RemoveAtIndex(operationsIndex); + } + else + operationsIndex++; + } + else + operationsIndex++; + } + + // Restart in list + operationsIndex=0; + } + + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnAuthenticationResult(rackspaceEventType, (const char*) packetData); + + break; + } + case RO_LIST_SERVERS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListServersResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_SERVERS_WITH_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListServersWithDetailsResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_CREATE_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnCreateServerResult(rackspaceEventType, (const char*) packetData); + break; + } + + case RO_GET_SERVER_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnGetServerDetails(rackspaceEventType, (const char*) packetData); + break; + } + case RO_UPDATE_SERVER_NAME_OR_PASSWORD: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnUpdateServerNameOrPassword(rackspaceEventType, (const char*) packetData); + break; + } + case RO_DELETE_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnDeleteServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_SERVER_ADDRESSES: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListServerAddresses(rackspaceEventType, (const char*) packetData); + break; + } + case RO_SHARE_SERVER_ADDRESS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnShareServerAddress(rackspaceEventType, (const char*) packetData); + break; + } + case RO_DELETE_SERVER_ADDRESS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnDeleteServerAddress(rackspaceEventType, (const char*) packetData); + break; + } + case RO_REBOOT_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnRebootServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_REBUILD_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnRebuildServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_RESIZE_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnResizeServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_CONFIRM_RESIZED_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnConfirmResizedServer(rackspaceEventType, (const char*) packetData); + break; + } + case RO_REVERT_RESIZED_SERVER: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnRevertResizedServer(rackspaceEventType, (const char*) packetData); + break; + } + + + case RO_LIST_FLAVORS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListFlavorsResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_GET_FLAVOR_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnGetFlavorDetailsResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_IMAGES: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListImagesResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_CREATE_IMAGE: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnCreateImageResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_GET_IMAGE_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnGetImageDetailsResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_DELETE_IMAGE: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnDeleteImageResult(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_SHARED_IP_GROUPS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListSharedIPGroups(rackspaceEventType, (const char*) packetData); + break; + } + case RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnListSharedIPGroupsWithDetails(rackspaceEventType, (const char*) packetData); + break; + } + case RO_CREATE_SHARED_IP_GROUP: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnCreateSharedIPGroup(rackspaceEventType, (const char*) packetData); + break; + } + case RO_GET_SHARED_IP_GROUP_DETAILS: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnGetSharedIPGroupDetails(rackspaceEventType, (const char*) packetData); + break; + } + case RO_DELETE_SHARED_IP_GROUP: + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnDeleteSharedIPGroup(rackspaceEventType, (const char*) packetData); + break; + } + default: + break; + + } + } + else + { + operationsIndex++; + } + } +} +void Rackspace::OnReceive(Packet *packet) +{ + unsigned int operationsIndex; + for (operationsIndex=0; operationsIndex < operations.Size(); operationsIndex++) + { + if (operations[operationsIndex].isPendingAuthentication==false && operations[operationsIndex].connectionAddress==packet->systemAddress) + { + operations[operationsIndex].incomingStream+=packet->data; + } + } +} +bool Rackspace::ExecuteOperation(RackspaceOperation &ro) +{ + if (ConnectToServerManagementDomain(ro)==false) + return false; + + RakNet::RakString command( + "%s %s/%s HTTP/1.1\n" + "Host: %s\n" + "Content-Type: application/xml\n" + "Content-Length: %i\n" + "Accept: application/xml\n" + "X-Auth-Token: %s\n", + ro.httpCommand.C_String(), serverManagementPath.C_String(), ro.operation.C_String(), serverManagementDomain.C_String(), + ro.xml.GetLength(), + authToken.C_String()); + + if (ro.xml.IsEmpty()==false) + { + command+="\n"; + command+=ro.xml; + command+="\n"; + } + + command+="\n"; + + //printf(command.C_String()); + + tcpInterface->Send(command.C_String(), (unsigned int) command.GetLength(), ro.connectionAddress, false); + return true; +} +void Rackspace::ReadLine(const char *data, const char *stringStart, RakNet::RakString &output) +{ + output.Clear(); + + char *result, *resultEnd; + + result=strstr((char*) data, stringStart); + result+=strlen(stringStart); + if (result==0) + { + RakAssert(0); + return; + } + + output=result; + resultEnd=result; + while (*resultEnd && (*resultEnd!='\r') && (*resultEnd!='\n') ) + resultEnd++; + output.Truncate((unsigned int) (resultEnd-result)); +} + + +bool Rackspace::ConnectToServerManagementDomain(RackspaceOperation &ro) +{ + unsigned int i; + + ro.connectionAddress=tcpInterface->Connect(serverManagementDomain.C_String(),443,true); + if (ro.connectionAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < eventCallbacks.Size(); i++) + eventCallbacks[i]->OnConnectionAttemptFailure(ro.type, serverManagementURL); + return false; + } + +#if OPEN_SSL_CLIENT_SUPPORT==1 + tcpInterface->StartSSLClient(ro.connectionAddress); +#endif + + return true; +} +bool Rackspace::HasOperationOfType(RackspaceOperationType t) +{ + unsigned int i; + for (i=0; i < operations.Size(); i++) + { + if (operations[i].type==t) + return true; + } + return false; +} +unsigned int Rackspace::GetOperationOfTypeIndex(RackspaceOperationType t) +{ + unsigned int i; + for (i=0; i < operations.Size(); i++) + { + if (operations[i].type==t) + return i; + } + return (unsigned int) -1; +} + +#endif diff --git a/src/raknet/Rackspace.h b/src/raknet/Rackspace.h index 145e55c..d2fc16e 100755 --- a/src/raknet/Rackspace.h +++ b/src/raknet/Rackspace.h @@ -1,405 +1,405 @@ -/// \file Rackspace.h -/// \brief Helper to class to manage Rackspace servers -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" - -#if _RAKNET_SUPPORT_Rackspace==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "Export.h" -#include "DS_List.h" -#include "RakNetTypes.h" -#include "DS_Queue.h" -#include "RakString.h" - -#ifndef __RACKSPACE_H -#define __RACKSPACE_H - -namespace RakNet -{ - - class TCPInterface; - struct Packet; - - /// \brief Result codes for Rackspace commands - /// /sa Rackspace::EventTypeToString() - enum RackspaceEventType - { - RET_Success_200, - RET_Success_201, - RET_Success_202, - RET_Success_203, - RET_Success_204, - RET_Cloud_Servers_Fault_500, - RET_Service_Unavailable_503, - RET_Unauthorized_401, - RET_Bad_Request_400, - RET_Over_Limit_413, - RET_Bad_Media_Type_415, - RET_Item_Not_Found_404, - RET_Build_In_Progress_409, - RET_Resize_Not_Allowed_403, - RET_Connection_Closed_Without_Reponse, - RET_Unknown_Failure, - }; - - /// \internal - enum RackspaceOperationType - { - RO_CONNECT_AND_AUTHENTICATE, - RO_LIST_SERVERS, - RO_LIST_SERVERS_WITH_DETAILS, - RO_CREATE_SERVER, - RO_GET_SERVER_DETAILS, - RO_UPDATE_SERVER_NAME_OR_PASSWORD, - RO_DELETE_SERVER, - RO_LIST_SERVER_ADDRESSES, - RO_SHARE_SERVER_ADDRESS, - RO_DELETE_SERVER_ADDRESS, - RO_REBOOT_SERVER, - RO_REBUILD_SERVER, - RO_RESIZE_SERVER, - RO_CONFIRM_RESIZED_SERVER, - RO_REVERT_RESIZED_SERVER, - RO_LIST_FLAVORS, - RO_GET_FLAVOR_DETAILS, - RO_LIST_IMAGES, - RO_CREATE_IMAGE, - RO_GET_IMAGE_DETAILS, - RO_DELETE_IMAGE, - RO_LIST_SHARED_IP_GROUPS, - RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS, - RO_CREATE_SHARED_IP_GROUP, - RO_GET_SHARED_IP_GROUP_DETAILS, - RO_DELETE_SHARED_IP_GROUP, - - RO_NONE, - }; - - /// \brief Callback interface to receive the results of operations - class RAK_DLL_EXPORT RackspaceEventCallback - { - public: - RackspaceEventCallback() {} - virtual ~RackspaceEventCallback() {} - virtual void OnAuthenticationResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListServersResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListServersWithDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnCreateServerResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnGetServerDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnUpdateServerNameOrPassword(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnDeleteServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListServerAddresses(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnShareServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnDeleteServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnRebootServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnRebuildServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnResizeServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnConfirmResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnRevertResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListFlavorsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnGetFlavorDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListImagesResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnCreateImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnGetImageDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnDeleteImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListSharedIPGroups(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnListSharedIPGroupsWithDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnCreateSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnGetSharedIPGroupDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - virtual void OnDeleteSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; - - virtual void OnConnectionAttemptFailure(RackspaceOperationType operationType, const char *url)=0; - }; - - /// \brief Callback interface to receive the results of operations, with a default result - class RAK_DLL_EXPORT RackspaceEventCallback_Default : public RackspaceEventCallback - { - public: - virtual void ExecuteDefault(const char *callbackName, RackspaceEventType eventType, const char *htmlAdditionalInfo) {(void) callbackName; (void) eventType; (void) htmlAdditionalInfo;} - - virtual void OnAuthenticationResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnAuthenticationResult", eventType, htmlAdditionalInfo);} - virtual void OnListServersResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServersResult", eventType, htmlAdditionalInfo);} - virtual void OnListServersWithDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServersWithDetailsResult", eventType, htmlAdditionalInfo);} - virtual void OnCreateServerResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateServerResult", eventType, htmlAdditionalInfo);} - virtual void OnGetServerDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetServerDetails", eventType, htmlAdditionalInfo);} - virtual void OnUpdateServerNameOrPassword(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnUpdateServerNameOrPassword", eventType, htmlAdditionalInfo);} - virtual void OnDeleteServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteServer", eventType, htmlAdditionalInfo);} - virtual void OnListServerAddresses(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServerAddresses", eventType, htmlAdditionalInfo);} - virtual void OnShareServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnShareServerAddress", eventType, htmlAdditionalInfo);} - virtual void OnDeleteServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteServerAddress", eventType, htmlAdditionalInfo);} - virtual void OnRebootServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRebootServer", eventType, htmlAdditionalInfo);} - virtual void OnRebuildServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRebuildServer", eventType, htmlAdditionalInfo);} - virtual void OnResizeServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnResizeServer", eventType, htmlAdditionalInfo);} - virtual void OnConfirmResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnConfirmResizedServer", eventType, htmlAdditionalInfo);} - virtual void OnRevertResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRevertResizedServer", eventType, htmlAdditionalInfo);} - virtual void OnListFlavorsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListFlavorsResult", eventType, htmlAdditionalInfo);} - virtual void OnGetFlavorDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetFlavorDetailsResult", eventType, htmlAdditionalInfo);} - virtual void OnListImagesResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListImagesResult", eventType, htmlAdditionalInfo);} - virtual void OnCreateImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateImageResult", eventType, htmlAdditionalInfo);} - virtual void OnGetImageDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetImageDetailsResult", eventType, htmlAdditionalInfo);} - virtual void OnDeleteImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteImageResult", eventType, htmlAdditionalInfo);} - virtual void OnListSharedIPGroups(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListSharedIPGroups", eventType, htmlAdditionalInfo);} - virtual void OnListSharedIPGroupsWithDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListSharedIPGroupsWithDetails", eventType, htmlAdditionalInfo);} - virtual void OnCreateSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateSharedIPGroup", eventType, htmlAdditionalInfo);} - virtual void OnGetSharedIPGroupDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetSharedIPGroupDetails", eventType, htmlAdditionalInfo);} - virtual void OnDeleteSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteSharedIPGroup", eventType, htmlAdditionalInfo);} - - virtual void OnConnectionAttemptFailure(RackspaceOperationType operationType, const char *url) {(void) operationType; (void) url;} - }; - - /// \brief Code that uses the TCPInterface class to communicate with the Rackspace API servers - /// \pre Compile RakNet with OPEN_SSL_CLIENT_SUPPORT set to 1 - /// \pre Packets returned from TCPInterface::OnReceive() must be passed to Rackspace::OnReceive() - /// \pre Packets returned from TCPInterface::HasLostConnection() must be passed to Rackspace::OnClosedConnection() - class RAK_DLL_EXPORT Rackspace - { - public: - Rackspace(); - ~Rackspace(); - - /// \brief Authenticate with Rackspace servers, required before executing any commands. - /// \details All requests to authenticate and operate against Cloud Servers are performed using SSL over HTTP (HTTPS) on TCP port 443. - /// Times out after 24 hours - if you get RET_Authenticate_Unauthorized in the RackspaceEventCallback callback, call again - /// \sa RackspaceEventCallback::OnAuthenticationResult() - /// \param[in] _tcpInterface An instance of TCPInterface, build with OPEN_SSL_CLIENT_SUPPORT 1 and already started - /// \param[in] _authenticationURL See http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf . US-based accounts authenticate through auth.api.rackspacecloud.com. UK-based accounts authenticate through lon.auth.api.rackspacecloud.com - /// \param[in] _rackspaceCloudUsername Username you registered with Rackspace on their website - /// \param[in] _apiAccessKey Obtain your API access key from the Rackspace Cloud Control Panel in the Your Account API Access section. - /// \return The address of the authentication server, or UNASSIGNED_SYSTEM_ADDRESS if the connection attempt failed - SystemAddress Authenticate(TCPInterface *_tcpInterface, const char *_authenticationURL, const char *_rackspaceCloudUsername, const char *_apiAccessKey); - - /// \brief Get a list of running servers - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListServersResult() - void ListServers(void); - - /// \brief Get a list of running servers, with extended details on each server - /// \sa GetServerDetails() - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListServersWithDetailsResult() - void ListServersWithDetails(void); - - /// \brief Create a server - /// \details Create a server with a given image (harddrive contents) and flavor (hardware configuration) - /// Get the available images with ListImages() - /// Get the available flavors with ListFlavors() - /// It is possible to configure the server in more detail. See the XML schema at http://docs.rackspacecloud.com/servers/api/v1.0 - /// You can execute such a custom command by calling AddOperation() manually. See the implementation of CreateServer for how to do so. - /// The server takes a while to build. Call GetServerDetails() to get the current build status. Server id to pass to GetServerDetails() is returned in the field - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnCreateServerResult() - /// \param[in] name Name of the server. Only alphanumeric characters, periods, and hyphens are valid. Server Name cannot start or end with a period or hyphen. - /// \param[in] imageId Which image (harddrive contents, including OS) to use - /// \param[in] flavorId Which flavor (hardware config) to use, primarily how much memory is available. - void CreateServer(RakNet::RakString name, RakNet::RakString imageId, RakNet::RakString flavorId); - - /// \brief Get details on a particular server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnGetServerDetailsResult() - /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. - void GetServerDetails(RakNet::RakString serverId); - - /// \brief Changes the name or password for a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnUpdateServerNameOrPasswordResult() - /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. - /// \param[in] newName The new server name. Leave blank to leave unchanged. Only alphanumeric characters, periods, and hyphens are valid. Server Name cannot start or end with a period or hyphen. - /// \param[in] newPassword The new server password. Leave blank to leave unchanged. - void UpdateServerNameOrPassword(RakNet::RakString serverId, RakNet::RakString newName, RakNet::RakString newPassword); - - /// \brief Deletes a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnDeleteServerResult() - /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. - void DeleteServer(RakNet::RakString serverId); - - /// \brief Lists the IP addresses available to a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListServerAddressesResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - void ListServerAddresses(RakNet::RakString serverId); - - /// \brief Shares an IP address with a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnShareServerAddressResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] ipAddress Which IP address. You can call ListServerAddresses() to get the list of addresses for the specified server - void ShareServerAddress(RakNet::RakString serverId, RakNet::RakString ipAddress); - - /// \brief Stops sharing an IP address with a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnDeleteServerAddressResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] ipAddress Which IP address. You can call ListServerAddresses() to get the list of addresses for the specified server - void DeleteServerAddress(RakNet::RakString serverId, RakNet::RakString ipAddress); - - /// \brief Reboots a server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnRebootServerResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] rebootType Should be either "HARD" or "SOFT" - void RebootServer(RakNet::RakString serverId, RakNet::RakString rebootType); - - /// \brief Rebuilds a server with a different image (harddrive contents) - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnRebuildServerResult() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] imageId Which image (harddrive contents, including OS) to use - void RebuildServer(RakNet::RakString serverId, RakNet::RakString imageId); - - /// \brief Changes the hardware configuration of a server. This does not take effect until you call ConfirmResizedServer() - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnResizeServerResult() - /// \sa RevertResizedServer() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] flavorId Which flavor (hardware config) to use, primarily how much memory is available. - void ResizeServer(RakNet::RakString serverId, RakNet::RakString flavorId); - - /// \brief Confirm a resize for the specified server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnConfirmResizedServerResult() - /// \sa ResizeServer() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - void ConfirmResizedServer(RakNet::RakString serverId); - - /// \brief Reverts a resize for the specified server - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnRevertResizedServerResult() - /// \sa ResizeServer() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - void RevertResizedServer(RakNet::RakString serverId); - - /// \brief List all flavors (hardware configs, primarily memory) - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListFlavorsResult() - void ListFlavors(void); - - /// \brief Get extended details about a specific flavor - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnGetFlavorDetailsResult() - /// \sa ListFlavors() - /// \param[in] flavorId Which flavor (hardware config) - void GetFlavorDetails(RakNet::RakString flavorId); - - /// \brief List all images (software configs, including operating systems), which includes images you create yourself - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListImagesResult() - /// \sa CreateImage() - void ListImages(void); - - /// \brief Images a running server. This essentially copies the harddrive, and lets you start a server with the same harddrive contents later - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnCreateImageResult() - /// \sa ListImages() - /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. - /// \param[in] imageName What to call this image - void CreateImage(RakNet::RakString serverId, RakNet::RakString imageName); - - /// \brief Get extended details about a particular image - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnGetImageDetailsResult() - /// \sa ListImages() - /// \param[in] imageId Which image - void GetImageDetails(RakNet::RakString imageId); - - /// \brief Delete a custom image created with CreateImage() - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnDeleteImageResult() - /// \sa ListImages() - /// \param[in] imageId Which image - void DeleteImage(RakNet::RakString imageId); - - /// \brief List IP groups - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListSharedIPGroupsResult() - void ListSharedIPGroups(void); - - /// \brief List IP groups with extended details - /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf - /// \sa RackspaceEventCallback::OnListSharedIPGroupsWithDetailsResult() - void ListSharedIPGroupsWithDetails(void); - - // I don't know what this does - void CreateSharedIPGroup(RakNet::RakString name, RakNet::RakString optionalServerId); - // I don't know what this does - void GetSharedIPGroupDetails(RakNet::RakString groupId); - // I don't know what this does - void DeleteSharedIPGroup(RakNet::RakString groupId); - - /// \brief Adds a callback to the list of callbacks to be called when any of the above functions finish executing - /// The callbacks are called in the order they are added - void AddEventCallback(RackspaceEventCallback *callback); - /// \brief Removes a callback from the list of callbacks to be called when any of the above functions finish executing - /// The callbacks are called in the order they are added - void RemoveEventCallback(RackspaceEventCallback *callback); - /// \brief Removes all callbacks - void ClearEventCallbacks(void); - - /// Call this anytime TCPInterface returns a packet - void OnReceive(Packet *packet); - - /// Call this when TCPInterface returns something other than UNASSIGNED_SYSTEM_ADDRESS from HasLostConnection() - void OnClosedConnection(SystemAddress systemAddress); - - /// String representation of each RackspaceEventType - static const char * EventTypeToString(RackspaceEventType eventType); - - /// \brief Mostly for internal use, but you can use it to execute an operation with more complex xml if desired - /// See the Rackspace.cpp on how to use it - void AddOperation(RackspaceOperationType type, RakNet::RakString httpCommand, RakNet::RakString operation, RakNet::RakString xml); - protected: - - DataStructures::List eventCallbacks; - - struct RackspaceOperation - { - RackspaceOperationType type; - // RakNet::RakString stringInfo; - SystemAddress connectionAddress; - bool isPendingAuthentication; - RakNet::RakString incomingStream; - RakNet::RakString httpCommand; - RakNet::RakString operation; - RakNet::RakString xml; - }; - - TCPInterface *tcpInterface; - - // RackspaceOperationType currentOperation; - // DataStructures::Queue nextOperationQueue; - - DataStructures::List operations; - bool HasOperationOfType(RackspaceOperationType t); - unsigned int GetOperationOfTypeIndex(RackspaceOperationType t); - - RakNet::RakString serverManagementURL; - RakNet::RakString serverManagementDomain; - RakNet::RakString serverManagementPath; - RakNet::RakString storageURL; - RakNet::RakString storageDomain; - RakNet::RakString storagePath; - RakNet::RakString cdnManagementURL; - RakNet::RakString cdnManagementDomain; - RakNet::RakString cdnManagementPath; - - RakNet::RakString storageToken; - RakNet::RakString authToken; - RakNet::RakString rackspaceCloudUsername; - RakNet::RakString apiAccessKey; - - bool ExecuteOperation(RackspaceOperation &ro); - void ReadLine(const char *data, const char *stringStart, RakNet::RakString &output); - bool ConnectToServerManagementDomain(RackspaceOperation &ro); - - - }; - -} // namespace RakNet - -#endif // __RACKSPACE_API_H - -#endif // _RAKNET_SUPPORT_Rackspace +/// \file Rackspace.h +/// \brief Helper to class to manage Rackspace servers +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" + +#if _RAKNET_SUPPORT_Rackspace==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "Export.h" +#include "DS_List.h" +#include "RakNetTypes.h" +#include "DS_Queue.h" +#include "RakString.h" + +#ifndef __RACKSPACE_H +#define __RACKSPACE_H + +namespace RakNet +{ + + class TCPInterface; + struct Packet; + + /// \brief Result codes for Rackspace commands + /// /sa Rackspace::EventTypeToString() + enum RackspaceEventType + { + RET_Success_200, + RET_Success_201, + RET_Success_202, + RET_Success_203, + RET_Success_204, + RET_Cloud_Servers_Fault_500, + RET_Service_Unavailable_503, + RET_Unauthorized_401, + RET_Bad_Request_400, + RET_Over_Limit_413, + RET_Bad_Media_Type_415, + RET_Item_Not_Found_404, + RET_Build_In_Progress_409, + RET_Resize_Not_Allowed_403, + RET_Connection_Closed_Without_Reponse, + RET_Unknown_Failure, + }; + + /// \internal + enum RackspaceOperationType + { + RO_CONNECT_AND_AUTHENTICATE, + RO_LIST_SERVERS, + RO_LIST_SERVERS_WITH_DETAILS, + RO_CREATE_SERVER, + RO_GET_SERVER_DETAILS, + RO_UPDATE_SERVER_NAME_OR_PASSWORD, + RO_DELETE_SERVER, + RO_LIST_SERVER_ADDRESSES, + RO_SHARE_SERVER_ADDRESS, + RO_DELETE_SERVER_ADDRESS, + RO_REBOOT_SERVER, + RO_REBUILD_SERVER, + RO_RESIZE_SERVER, + RO_CONFIRM_RESIZED_SERVER, + RO_REVERT_RESIZED_SERVER, + RO_LIST_FLAVORS, + RO_GET_FLAVOR_DETAILS, + RO_LIST_IMAGES, + RO_CREATE_IMAGE, + RO_GET_IMAGE_DETAILS, + RO_DELETE_IMAGE, + RO_LIST_SHARED_IP_GROUPS, + RO_LIST_SHARED_IP_GROUPS_WITH_DETAILS, + RO_CREATE_SHARED_IP_GROUP, + RO_GET_SHARED_IP_GROUP_DETAILS, + RO_DELETE_SHARED_IP_GROUP, + + RO_NONE, + }; + + /// \brief Callback interface to receive the results of operations + class RAK_DLL_EXPORT RackspaceEventCallback + { + public: + RackspaceEventCallback() {} + virtual ~RackspaceEventCallback() {} + virtual void OnAuthenticationResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListServersResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListServersWithDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnCreateServerResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnGetServerDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnUpdateServerNameOrPassword(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnDeleteServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListServerAddresses(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnShareServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnDeleteServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnRebootServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnRebuildServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnResizeServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnConfirmResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnRevertResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListFlavorsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnGetFlavorDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListImagesResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnCreateImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnGetImageDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnDeleteImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListSharedIPGroups(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnListSharedIPGroupsWithDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnCreateSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnGetSharedIPGroupDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + virtual void OnDeleteSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo)=0; + + virtual void OnConnectionAttemptFailure(RackspaceOperationType operationType, const char *url)=0; + }; + + /// \brief Callback interface to receive the results of operations, with a default result + class RAK_DLL_EXPORT RackspaceEventCallback_Default : public RackspaceEventCallback + { + public: + virtual void ExecuteDefault(const char *callbackName, RackspaceEventType eventType, const char *htmlAdditionalInfo) {(void) callbackName; (void) eventType; (void) htmlAdditionalInfo;} + + virtual void OnAuthenticationResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnAuthenticationResult", eventType, htmlAdditionalInfo);} + virtual void OnListServersResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServersResult", eventType, htmlAdditionalInfo);} + virtual void OnListServersWithDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServersWithDetailsResult", eventType, htmlAdditionalInfo);} + virtual void OnCreateServerResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateServerResult", eventType, htmlAdditionalInfo);} + virtual void OnGetServerDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetServerDetails", eventType, htmlAdditionalInfo);} + virtual void OnUpdateServerNameOrPassword(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnUpdateServerNameOrPassword", eventType, htmlAdditionalInfo);} + virtual void OnDeleteServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteServer", eventType, htmlAdditionalInfo);} + virtual void OnListServerAddresses(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListServerAddresses", eventType, htmlAdditionalInfo);} + virtual void OnShareServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnShareServerAddress", eventType, htmlAdditionalInfo);} + virtual void OnDeleteServerAddress(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteServerAddress", eventType, htmlAdditionalInfo);} + virtual void OnRebootServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRebootServer", eventType, htmlAdditionalInfo);} + virtual void OnRebuildServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRebuildServer", eventType, htmlAdditionalInfo);} + virtual void OnResizeServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnResizeServer", eventType, htmlAdditionalInfo);} + virtual void OnConfirmResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnConfirmResizedServer", eventType, htmlAdditionalInfo);} + virtual void OnRevertResizedServer(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnRevertResizedServer", eventType, htmlAdditionalInfo);} + virtual void OnListFlavorsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListFlavorsResult", eventType, htmlAdditionalInfo);} + virtual void OnGetFlavorDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetFlavorDetailsResult", eventType, htmlAdditionalInfo);} + virtual void OnListImagesResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListImagesResult", eventType, htmlAdditionalInfo);} + virtual void OnCreateImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateImageResult", eventType, htmlAdditionalInfo);} + virtual void OnGetImageDetailsResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetImageDetailsResult", eventType, htmlAdditionalInfo);} + virtual void OnDeleteImageResult(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteImageResult", eventType, htmlAdditionalInfo);} + virtual void OnListSharedIPGroups(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListSharedIPGroups", eventType, htmlAdditionalInfo);} + virtual void OnListSharedIPGroupsWithDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnListSharedIPGroupsWithDetails", eventType, htmlAdditionalInfo);} + virtual void OnCreateSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnCreateSharedIPGroup", eventType, htmlAdditionalInfo);} + virtual void OnGetSharedIPGroupDetails(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnGetSharedIPGroupDetails", eventType, htmlAdditionalInfo);} + virtual void OnDeleteSharedIPGroup(RackspaceEventType eventType, const char *htmlAdditionalInfo) {ExecuteDefault("OnDeleteSharedIPGroup", eventType, htmlAdditionalInfo);} + + virtual void OnConnectionAttemptFailure(RackspaceOperationType operationType, const char *url) {(void) operationType; (void) url;} + }; + + /// \brief Code that uses the TCPInterface class to communicate with the Rackspace API servers + /// \pre Compile RakNet with OPEN_SSL_CLIENT_SUPPORT set to 1 + /// \pre Packets returned from TCPInterface::OnReceive() must be passed to Rackspace::OnReceive() + /// \pre Packets returned from TCPInterface::HasLostConnection() must be passed to Rackspace::OnClosedConnection() + class RAK_DLL_EXPORT Rackspace + { + public: + Rackspace(); + ~Rackspace(); + + /// \brief Authenticate with Rackspace servers, required before executing any commands. + /// \details All requests to authenticate and operate against Cloud Servers are performed using SSL over HTTP (HTTPS) on TCP port 443. + /// Times out after 24 hours - if you get RET_Authenticate_Unauthorized in the RackspaceEventCallback callback, call again + /// \sa RackspaceEventCallback::OnAuthenticationResult() + /// \param[in] _tcpInterface An instance of TCPInterface, build with OPEN_SSL_CLIENT_SUPPORT 1 and already started + /// \param[in] _authenticationURL See http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf . US-based accounts authenticate through auth.api.rackspacecloud.com. UK-based accounts authenticate through lon.auth.api.rackspacecloud.com + /// \param[in] _rackspaceCloudUsername Username you registered with Rackspace on their website + /// \param[in] _apiAccessKey Obtain your API access key from the Rackspace Cloud Control Panel in the Your Account API Access section. + /// \return The address of the authentication server, or UNASSIGNED_SYSTEM_ADDRESS if the connection attempt failed + SystemAddress Authenticate(TCPInterface *_tcpInterface, const char *_authenticationURL, const char *_rackspaceCloudUsername, const char *_apiAccessKey); + + /// \brief Get a list of running servers + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListServersResult() + void ListServers(void); + + /// \brief Get a list of running servers, with extended details on each server + /// \sa GetServerDetails() + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListServersWithDetailsResult() + void ListServersWithDetails(void); + + /// \brief Create a server + /// \details Create a server with a given image (harddrive contents) and flavor (hardware configuration) + /// Get the available images with ListImages() + /// Get the available flavors with ListFlavors() + /// It is possible to configure the server in more detail. See the XML schema at http://docs.rackspacecloud.com/servers/api/v1.0 + /// You can execute such a custom command by calling AddOperation() manually. See the implementation of CreateServer for how to do so. + /// The server takes a while to build. Call GetServerDetails() to get the current build status. Server id to pass to GetServerDetails() is returned in the field + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnCreateServerResult() + /// \param[in] name Name of the server. Only alphanumeric characters, periods, and hyphens are valid. Server Name cannot start or end with a period or hyphen. + /// \param[in] imageId Which image (harddrive contents, including OS) to use + /// \param[in] flavorId Which flavor (hardware config) to use, primarily how much memory is available. + void CreateServer(RakNet::RakString name, RakNet::RakString imageId, RakNet::RakString flavorId); + + /// \brief Get details on a particular server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnGetServerDetailsResult() + /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. + void GetServerDetails(RakNet::RakString serverId); + + /// \brief Changes the name or password for a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnUpdateServerNameOrPasswordResult() + /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. + /// \param[in] newName The new server name. Leave blank to leave unchanged. Only alphanumeric characters, periods, and hyphens are valid. Server Name cannot start or end with a period or hyphen. + /// \param[in] newPassword The new server password. Leave blank to leave unchanged. + void UpdateServerNameOrPassword(RakNet::RakString serverId, RakNet::RakString newName, RakNet::RakString newPassword); + + /// \brief Deletes a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnDeleteServerResult() + /// \param[in] serverId Which server to get details on. You can call ListServers() to get the list of active servers. + void DeleteServer(RakNet::RakString serverId); + + /// \brief Lists the IP addresses available to a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListServerAddressesResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + void ListServerAddresses(RakNet::RakString serverId); + + /// \brief Shares an IP address with a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnShareServerAddressResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] ipAddress Which IP address. You can call ListServerAddresses() to get the list of addresses for the specified server + void ShareServerAddress(RakNet::RakString serverId, RakNet::RakString ipAddress); + + /// \brief Stops sharing an IP address with a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnDeleteServerAddressResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] ipAddress Which IP address. You can call ListServerAddresses() to get the list of addresses for the specified server + void DeleteServerAddress(RakNet::RakString serverId, RakNet::RakString ipAddress); + + /// \brief Reboots a server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnRebootServerResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] rebootType Should be either "HARD" or "SOFT" + void RebootServer(RakNet::RakString serverId, RakNet::RakString rebootType); + + /// \brief Rebuilds a server with a different image (harddrive contents) + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnRebuildServerResult() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] imageId Which image (harddrive contents, including OS) to use + void RebuildServer(RakNet::RakString serverId, RakNet::RakString imageId); + + /// \brief Changes the hardware configuration of a server. This does not take effect until you call ConfirmResizedServer() + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnResizeServerResult() + /// \sa RevertResizedServer() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] flavorId Which flavor (hardware config) to use, primarily how much memory is available. + void ResizeServer(RakNet::RakString serverId, RakNet::RakString flavorId); + + /// \brief Confirm a resize for the specified server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnConfirmResizedServerResult() + /// \sa ResizeServer() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + void ConfirmResizedServer(RakNet::RakString serverId); + + /// \brief Reverts a resize for the specified server + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnRevertResizedServerResult() + /// \sa ResizeServer() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + void RevertResizedServer(RakNet::RakString serverId); + + /// \brief List all flavors (hardware configs, primarily memory) + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListFlavorsResult() + void ListFlavors(void); + + /// \brief Get extended details about a specific flavor + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnGetFlavorDetailsResult() + /// \sa ListFlavors() + /// \param[in] flavorId Which flavor (hardware config) + void GetFlavorDetails(RakNet::RakString flavorId); + + /// \brief List all images (software configs, including operating systems), which includes images you create yourself + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListImagesResult() + /// \sa CreateImage() + void ListImages(void); + + /// \brief Images a running server. This essentially copies the harddrive, and lets you start a server with the same harddrive contents later + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnCreateImageResult() + /// \sa ListImages() + /// \param[in] serverId Which server to operate on. You can call ListServers() to get the list of active servers. + /// \param[in] imageName What to call this image + void CreateImage(RakNet::RakString serverId, RakNet::RakString imageName); + + /// \brief Get extended details about a particular image + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnGetImageDetailsResult() + /// \sa ListImages() + /// \param[in] imageId Which image + void GetImageDetails(RakNet::RakString imageId); + + /// \brief Delete a custom image created with CreateImage() + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnDeleteImageResult() + /// \sa ListImages() + /// \param[in] imageId Which image + void DeleteImage(RakNet::RakString imageId); + + /// \brief List IP groups + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListSharedIPGroupsResult() + void ListSharedIPGroups(void); + + /// \brief List IP groups with extended details + /// \sa http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf + /// \sa RackspaceEventCallback::OnListSharedIPGroupsWithDetailsResult() + void ListSharedIPGroupsWithDetails(void); + + // I don't know what this does + void CreateSharedIPGroup(RakNet::RakString name, RakNet::RakString optionalServerId); + // I don't know what this does + void GetSharedIPGroupDetails(RakNet::RakString groupId); + // I don't know what this does + void DeleteSharedIPGroup(RakNet::RakString groupId); + + /// \brief Adds a callback to the list of callbacks to be called when any of the above functions finish executing + /// The callbacks are called in the order they are added + void AddEventCallback(RackspaceEventCallback *callback); + /// \brief Removes a callback from the list of callbacks to be called when any of the above functions finish executing + /// The callbacks are called in the order they are added + void RemoveEventCallback(RackspaceEventCallback *callback); + /// \brief Removes all callbacks + void ClearEventCallbacks(void); + + /// Call this anytime TCPInterface returns a packet + void OnReceive(Packet *packet); + + /// Call this when TCPInterface returns something other than UNASSIGNED_SYSTEM_ADDRESS from HasLostConnection() + void OnClosedConnection(SystemAddress systemAddress); + + /// String representation of each RackspaceEventType + static const char * EventTypeToString(RackspaceEventType eventType); + + /// \brief Mostly for internal use, but you can use it to execute an operation with more complex xml if desired + /// See the Rackspace.cpp on how to use it + void AddOperation(RackspaceOperationType type, RakNet::RakString httpCommand, RakNet::RakString operation, RakNet::RakString xml); + protected: + + DataStructures::List eventCallbacks; + + struct RackspaceOperation + { + RackspaceOperationType type; + // RakNet::RakString stringInfo; + SystemAddress connectionAddress; + bool isPendingAuthentication; + RakNet::RakString incomingStream; + RakNet::RakString httpCommand; + RakNet::RakString operation; + RakNet::RakString xml; + }; + + TCPInterface *tcpInterface; + + // RackspaceOperationType currentOperation; + // DataStructures::Queue nextOperationQueue; + + DataStructures::List operations; + bool HasOperationOfType(RackspaceOperationType t); + unsigned int GetOperationOfTypeIndex(RackspaceOperationType t); + + RakNet::RakString serverManagementURL; + RakNet::RakString serverManagementDomain; + RakNet::RakString serverManagementPath; + RakNet::RakString storageURL; + RakNet::RakString storageDomain; + RakNet::RakString storagePath; + RakNet::RakString cdnManagementURL; + RakNet::RakString cdnManagementDomain; + RakNet::RakString cdnManagementPath; + + RakNet::RakString storageToken; + RakNet::RakString authToken; + RakNet::RakString rackspaceCloudUsername; + RakNet::RakString apiAccessKey; + + bool ExecuteOperation(RackspaceOperation &ro); + void ReadLine(const char *data, const char *stringStart, RakNet::RakString &output); + bool ConnectToServerManagementDomain(RackspaceOperation &ro); + + + }; + +} // namespace RakNet + +#endif // __RACKSPACE_API_H + +#endif // _RAKNET_SUPPORT_Rackspace diff --git a/src/raknet/RakAlloca.h b/src/raknet/RakAlloca.h index b25e5ba..1647f27 100755 --- a/src/raknet/RakAlloca.h +++ b/src/raknet/RakAlloca.h @@ -1,16 +1,16 @@ -#if defined(__FreeBSD__) -#include - - - - -#elif defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) -#include -#include -#elif defined(_WIN32) -#include -#else -#include -// Alloca needed on Ubuntu apparently -#include -#endif +#if defined(__FreeBSD__) +#include + + + + +#elif defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) +#include +#include +#elif defined(_WIN32) +#include +#else +#include +// Alloca needed on Ubuntu apparently +#include +#endif diff --git a/src/raknet/RakAssert.h b/src/raknet/RakAssert.h index 4521d5f..f0cc96f 100755 --- a/src/raknet/RakAssert.h +++ b/src/raknet/RakAssert.h @@ -1,2 +1,2 @@ -#include -#include "RakNetDefines.h" +#include +#include "RakNetDefines.h" diff --git a/src/raknet/RakMemoryOverride.cpp b/src/raknet/RakMemoryOverride.cpp index 8e20f8f..45d77ab 100755 --- a/src/raknet/RakMemoryOverride.cpp +++ b/src/raknet/RakMemoryOverride.cpp @@ -1,294 +1,294 @@ -#include "RakMemoryOverride.h" -#include "RakAssert.h" -#include - -#ifdef _RAKNET_SUPPORT_DL_MALLOC -#include "rdlmalloc.h" -#endif - - - - - -using namespace RakNet; - -#if _USE_RAK_MEMORY_OVERRIDE==1 - #if defined(malloc) - #pragma push_macro("malloc") - #undef malloc - #define RMO_MALLOC_UNDEF - #endif - - #if defined(realloc) - #pragma push_macro("realloc") - #undef realloc - #define RMO_REALLOC_UNDEF - #endif - - #if defined(free) - #pragma push_macro("free") - #undef free - #define RMO_FREE_UNDEF - #endif -#endif - -void DefaultOutOfMemoryHandler(const char *file, const long line) -{ - (void) file; - (void) line; - RakAssert(0); -} - -void * (*rakMalloc) (size_t size) = RakNet::_RakMalloc; -void* (*rakRealloc) (void *p, size_t size) = RakNet::_RakRealloc; -void (*rakFree) (void *p) = RakNet::_RakFree; -void* (*rakMalloc_Ex) (size_t size, const char *file, unsigned int line) = RakNet::_RakMalloc_Ex; -void* (*rakRealloc_Ex) (void *p, size_t size, const char *file, unsigned int line) = RakNet::_RakRealloc_Ex; -void (*rakFree_Ex) (void *p, const char *file, unsigned int line) = RakNet::_RakFree_Ex; -void (*notifyOutOfMemory) (const char *file, const long line)=DefaultOutOfMemoryHandler; -void * (*dlMallocMMap) (size_t size) = RakNet::_DLMallocMMap; -void * (*dlMallocDirectMMap) (size_t size) = RakNet::_DLMallocDirectMMap; -int (*dlMallocMUnmap) (void* ptr, size_t size) = RakNet::_DLMallocMUnmap; - -void SetMalloc( void* (*userFunction)(size_t size) ) -{ - rakMalloc=userFunction; -} -void SetRealloc( void* (*userFunction)(void *p, size_t size) ) -{ - rakRealloc=userFunction; -} -void SetFree( void (*userFunction)(void *p) ) -{ - rakFree=userFunction; -} -void SetMalloc_Ex( void* (*userFunction)(size_t size, const char *file, unsigned int line) ) -{ - rakMalloc_Ex=userFunction; -} -void SetRealloc_Ex( void* (*userFunction)(void *p, size_t size, const char *file, unsigned int line) ) -{ - rakRealloc_Ex=userFunction; -} -void SetFree_Ex( void (*userFunction)(void *p, const char *file, unsigned int line) ) -{ - rakFree_Ex=userFunction; -} -void SetNotifyOutOfMemory( void (*userFunction)(const char *file, const long line) ) -{ - notifyOutOfMemory=userFunction; -} -void SetDLMallocMMap( void* (*userFunction)(size_t size) ) -{ - dlMallocMMap=userFunction; -} -void SetDLMallocDirectMMap( void* (*userFunction)(size_t size) ) -{ - dlMallocDirectMMap=userFunction; -} -void SetDLMallocMUnmap( int (*userFunction)(void* ptr, size_t size) ) -{ - dlMallocMUnmap=userFunction; -} -void * (*GetMalloc()) (size_t size) -{ - return rakMalloc; -} -void * (*GetRealloc()) (void *p, size_t size) -{ - return rakRealloc; -} -void (*GetFree()) (void *p) -{ - return rakFree; -} -void * (*GetMalloc_Ex()) (size_t size, const char *file, unsigned int line) -{ - return rakMalloc_Ex; -} -void * (*GetRealloc_Ex()) (void *p, size_t size, const char *file, unsigned int line) -{ - return rakRealloc_Ex; -} -void (*GetFree_Ex()) (void *p, const char *file, unsigned int line) -{ - return rakFree_Ex; -} -void *(*GetDLMallocMMap())(size_t size) -{ - return dlMallocMMap; -} -void *(*GetDLMallocDirectMMap())(size_t size) -{ - return dlMallocDirectMMap; -} -int (*GetDLMallocMUnmap())(void* ptr, size_t size) -{ - return dlMallocMUnmap; -} -void* RakNet::_RakMalloc (size_t size) -{ - return malloc(size); -} - -void* RakNet::_RakRealloc (void *p, size_t size) -{ - return realloc(p,size); -} - -void RakNet::_RakFree (void *p) -{ - free(p); -} - -void* RakNet::_RakMalloc_Ex (size_t size, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - return malloc(size); -} - -void* RakNet::_RakRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - return realloc(p,size); -} - -void RakNet::_RakFree_Ex (void *p, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - free(p); -} -#ifdef _RAKNET_SUPPORT_DL_MALLOC -void * RakNet::_DLMallocMMap (size_t size) -{ - return RAK_MMAP_DEFAULT(size); -} -void * RakNet::_DLMallocDirectMMap (size_t size) -{ - return RAK_DIRECT_MMAP_DEFAULT(size); -} -int RakNet::_DLMallocMUnmap (void *p, size_t size) -{ - return RAK_MUNMAP_DEFAULT(p,size); -} - -static mspace rakNetFixedHeapMSpace=0; - -void* _DLMalloc(size_t size) -{ - return rak_mspace_malloc(rakNetFixedHeapMSpace,size); -} - -void* _DLRealloc(void *p, size_t size) -{ - return rak_mspace_realloc(rakNetFixedHeapMSpace,p,size); -} - -void _DLFree(void *p) -{ - if (p) - rak_mspace_free(rakNetFixedHeapMSpace,p); -} -void* _DLMalloc_Ex (size_t size, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - return rak_mspace_malloc(rakNetFixedHeapMSpace,size); -} - -void* _DLRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - return rak_mspace_realloc(rakNetFixedHeapMSpace,p,size); -} - -void _DLFree_Ex (void *p, const char *file, unsigned int line) -{ - (void) file; - (void) line; - - if (p) - rak_mspace_free(rakNetFixedHeapMSpace,p); -} - -void UseRaknetFixedHeap(size_t initialCapacity, - void * (*yourMMapFunction) (size_t size), - void * (*yourDirectMMapFunction) (size_t size), - int (*yourMUnmapFunction) (void *p, size_t size)) -{ - SetDLMallocMMap(yourMMapFunction); - SetDLMallocDirectMMap(yourDirectMMapFunction); - SetDLMallocMUnmap(yourMUnmapFunction); - SetMalloc(_DLMalloc); - SetRealloc(_DLRealloc); - SetFree(_DLFree); - SetMalloc_Ex(_DLMalloc_Ex); - SetRealloc_Ex(_DLRealloc_Ex); - SetFree_Ex(_DLFree_Ex); - - rakNetFixedHeapMSpace=rak_create_mspace(initialCapacity, 0); -} -void FreeRakNetFixedHeap(void) -{ - if (rakNetFixedHeapMSpace) - { - rak_destroy_mspace(rakNetFixedHeapMSpace); - rakNetFixedHeapMSpace=0; - } - - SetMalloc(_RakMalloc); - SetRealloc(_RakRealloc); - SetFree(_RakFree); - SetMalloc_Ex(_RakMalloc_Ex); - SetRealloc_Ex(_RakRealloc_Ex); - SetFree_Ex(_RakFree_Ex); -} -#else -void * RakNet::_DLMallocMMap (size_t size) {(void) size; return 0;} -void * RakNet::_DLMallocDirectMMap (size_t size) {(void) size; return 0;} -int RakNet::_DLMallocMUnmap (void *p, size_t size) {(void) size; (void) p; return 0;} -void* _DLMalloc(size_t size) {(void) size; return 0;} -void* _DLRealloc(void *p, size_t size) {(void) p; (void) size; return 0;} -void _DLFree(void *p) {(void) p;} -void* _DLMalloc_Ex (size_t size, const char *file, unsigned int line) {(void) size; (void) file; (void) line; return 0;} -void* _DLRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) {(void) p; (void) size; (void) file; (void) line; return 0;} -void _DLFree_Ex (void *p, const char *file, unsigned int line) {(void) p; (void) file; (void) line;} - -void UseRaknetFixedHeap(size_t initialCapacity, - void * (*yourMMapFunction) (size_t size), - void * (*yourDirectMMapFunction) (size_t size), - int (*yourMUnmapFunction) (void *p, size_t size)) -{ - (void) initialCapacity; - (void) yourMMapFunction; - (void) yourDirectMMapFunction; - (void) yourMUnmapFunction; -} -void FreeRakNetFixedHeap(void) {} -#endif - -#if _USE_RAK_MEMORY_OVERRIDE==1 - - #pragma pop_macro("malloc") - #undef RMO_MALLOC_UNDEF - - - - #pragma pop_macro("realloc") - #undef RMO_REALLOC_UNDEF - - - - #pragma pop_macro("free") - #undef RMO_FREE_UNDEF - -#endif +#include "RakMemoryOverride.h" +#include "RakAssert.h" +#include + +#ifdef _RAKNET_SUPPORT_DL_MALLOC +#include "rdlmalloc.h" +#endif + + + + + +using namespace RakNet; + +#if _USE_RAK_MEMORY_OVERRIDE==1 + #if defined(malloc) + #pragma push_macro("malloc") + #undef malloc + #define RMO_MALLOC_UNDEF + #endif + + #if defined(realloc) + #pragma push_macro("realloc") + #undef realloc + #define RMO_REALLOC_UNDEF + #endif + + #if defined(free) + #pragma push_macro("free") + #undef free + #define RMO_FREE_UNDEF + #endif +#endif + +void DefaultOutOfMemoryHandler(const char *file, const long line) +{ + (void) file; + (void) line; + RakAssert(0); +} + +void * (*rakMalloc) (size_t size) = RakNet::_RakMalloc; +void* (*rakRealloc) (void *p, size_t size) = RakNet::_RakRealloc; +void (*rakFree) (void *p) = RakNet::_RakFree; +void* (*rakMalloc_Ex) (size_t size, const char *file, unsigned int line) = RakNet::_RakMalloc_Ex; +void* (*rakRealloc_Ex) (void *p, size_t size, const char *file, unsigned int line) = RakNet::_RakRealloc_Ex; +void (*rakFree_Ex) (void *p, const char *file, unsigned int line) = RakNet::_RakFree_Ex; +void (*notifyOutOfMemory) (const char *file, const long line)=DefaultOutOfMemoryHandler; +void * (*dlMallocMMap) (size_t size) = RakNet::_DLMallocMMap; +void * (*dlMallocDirectMMap) (size_t size) = RakNet::_DLMallocDirectMMap; +int (*dlMallocMUnmap) (void* ptr, size_t size) = RakNet::_DLMallocMUnmap; + +void SetMalloc( void* (*userFunction)(size_t size) ) +{ + rakMalloc=userFunction; +} +void SetRealloc( void* (*userFunction)(void *p, size_t size) ) +{ + rakRealloc=userFunction; +} +void SetFree( void (*userFunction)(void *p) ) +{ + rakFree=userFunction; +} +void SetMalloc_Ex( void* (*userFunction)(size_t size, const char *file, unsigned int line) ) +{ + rakMalloc_Ex=userFunction; +} +void SetRealloc_Ex( void* (*userFunction)(void *p, size_t size, const char *file, unsigned int line) ) +{ + rakRealloc_Ex=userFunction; +} +void SetFree_Ex( void (*userFunction)(void *p, const char *file, unsigned int line) ) +{ + rakFree_Ex=userFunction; +} +void SetNotifyOutOfMemory( void (*userFunction)(const char *file, const long line) ) +{ + notifyOutOfMemory=userFunction; +} +void SetDLMallocMMap( void* (*userFunction)(size_t size) ) +{ + dlMallocMMap=userFunction; +} +void SetDLMallocDirectMMap( void* (*userFunction)(size_t size) ) +{ + dlMallocDirectMMap=userFunction; +} +void SetDLMallocMUnmap( int (*userFunction)(void* ptr, size_t size) ) +{ + dlMallocMUnmap=userFunction; +} +void * (*GetMalloc()) (size_t size) +{ + return rakMalloc; +} +void * (*GetRealloc()) (void *p, size_t size) +{ + return rakRealloc; +} +void (*GetFree()) (void *p) +{ + return rakFree; +} +void * (*GetMalloc_Ex()) (size_t size, const char *file, unsigned int line) +{ + return rakMalloc_Ex; +} +void * (*GetRealloc_Ex()) (void *p, size_t size, const char *file, unsigned int line) +{ + return rakRealloc_Ex; +} +void (*GetFree_Ex()) (void *p, const char *file, unsigned int line) +{ + return rakFree_Ex; +} +void *(*GetDLMallocMMap())(size_t size) +{ + return dlMallocMMap; +} +void *(*GetDLMallocDirectMMap())(size_t size) +{ + return dlMallocDirectMMap; +} +int (*GetDLMallocMUnmap())(void* ptr, size_t size) +{ + return dlMallocMUnmap; +} +void* RakNet::_RakMalloc (size_t size) +{ + return malloc(size); +} + +void* RakNet::_RakRealloc (void *p, size_t size) +{ + return realloc(p,size); +} + +void RakNet::_RakFree (void *p) +{ + free(p); +} + +void* RakNet::_RakMalloc_Ex (size_t size, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + return malloc(size); +} + +void* RakNet::_RakRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + return realloc(p,size); +} + +void RakNet::_RakFree_Ex (void *p, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + free(p); +} +#ifdef _RAKNET_SUPPORT_DL_MALLOC +void * RakNet::_DLMallocMMap (size_t size) +{ + return RAK_MMAP_DEFAULT(size); +} +void * RakNet::_DLMallocDirectMMap (size_t size) +{ + return RAK_DIRECT_MMAP_DEFAULT(size); +} +int RakNet::_DLMallocMUnmap (void *p, size_t size) +{ + return RAK_MUNMAP_DEFAULT(p,size); +} + +static mspace rakNetFixedHeapMSpace=0; + +void* _DLMalloc(size_t size) +{ + return rak_mspace_malloc(rakNetFixedHeapMSpace,size); +} + +void* _DLRealloc(void *p, size_t size) +{ + return rak_mspace_realloc(rakNetFixedHeapMSpace,p,size); +} + +void _DLFree(void *p) +{ + if (p) + rak_mspace_free(rakNetFixedHeapMSpace,p); +} +void* _DLMalloc_Ex (size_t size, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + return rak_mspace_malloc(rakNetFixedHeapMSpace,size); +} + +void* _DLRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + return rak_mspace_realloc(rakNetFixedHeapMSpace,p,size); +} + +void _DLFree_Ex (void *p, const char *file, unsigned int line) +{ + (void) file; + (void) line; + + if (p) + rak_mspace_free(rakNetFixedHeapMSpace,p); +} + +void UseRaknetFixedHeap(size_t initialCapacity, + void * (*yourMMapFunction) (size_t size), + void * (*yourDirectMMapFunction) (size_t size), + int (*yourMUnmapFunction) (void *p, size_t size)) +{ + SetDLMallocMMap(yourMMapFunction); + SetDLMallocDirectMMap(yourDirectMMapFunction); + SetDLMallocMUnmap(yourMUnmapFunction); + SetMalloc(_DLMalloc); + SetRealloc(_DLRealloc); + SetFree(_DLFree); + SetMalloc_Ex(_DLMalloc_Ex); + SetRealloc_Ex(_DLRealloc_Ex); + SetFree_Ex(_DLFree_Ex); + + rakNetFixedHeapMSpace=rak_create_mspace(initialCapacity, 0); +} +void FreeRakNetFixedHeap(void) +{ + if (rakNetFixedHeapMSpace) + { + rak_destroy_mspace(rakNetFixedHeapMSpace); + rakNetFixedHeapMSpace=0; + } + + SetMalloc(_RakMalloc); + SetRealloc(_RakRealloc); + SetFree(_RakFree); + SetMalloc_Ex(_RakMalloc_Ex); + SetRealloc_Ex(_RakRealloc_Ex); + SetFree_Ex(_RakFree_Ex); +} +#else +void * RakNet::_DLMallocMMap (size_t size) {(void) size; return 0;} +void * RakNet::_DLMallocDirectMMap (size_t size) {(void) size; return 0;} +int RakNet::_DLMallocMUnmap (void *p, size_t size) {(void) size; (void) p; return 0;} +void* _DLMalloc(size_t size) {(void) size; return 0;} +void* _DLRealloc(void *p, size_t size) {(void) p; (void) size; return 0;} +void _DLFree(void *p) {(void) p;} +void* _DLMalloc_Ex (size_t size, const char *file, unsigned int line) {(void) size; (void) file; (void) line; return 0;} +void* _DLRealloc_Ex (void *p, size_t size, const char *file, unsigned int line) {(void) p; (void) size; (void) file; (void) line; return 0;} +void _DLFree_Ex (void *p, const char *file, unsigned int line) {(void) p; (void) file; (void) line;} + +void UseRaknetFixedHeap(size_t initialCapacity, + void * (*yourMMapFunction) (size_t size), + void * (*yourDirectMMapFunction) (size_t size), + int (*yourMUnmapFunction) (void *p, size_t size)) +{ + (void) initialCapacity; + (void) yourMMapFunction; + (void) yourDirectMMapFunction; + (void) yourMUnmapFunction; +} +void FreeRakNetFixedHeap(void) {} +#endif + +#if _USE_RAK_MEMORY_OVERRIDE==1 + + #pragma pop_macro("malloc") + #undef RMO_MALLOC_UNDEF + + + + #pragma pop_macro("realloc") + #undef RMO_REALLOC_UNDEF + + + + #pragma pop_macro("free") + #undef RMO_FREE_UNDEF + +#endif diff --git a/src/raknet/RakMemoryOverride.h b/src/raknet/RakMemoryOverride.h index 57b0de6..5eb84b8 100755 --- a/src/raknet/RakMemoryOverride.h +++ b/src/raknet/RakMemoryOverride.h @@ -1,236 +1,236 @@ -/// \file -/// \brief If _USE_RAK_MEMORY_OVERRIDE is defined, memory allocations go through rakMalloc, rakRealloc, and rakFree -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAK_MEMORY_H -#define __RAK_MEMORY_H - -#include "Export.h" -#include "RakNetDefines.h" -#include - - - - - - - -#include "RakAlloca.h" - -// #if _USE_RAK_MEMORY_OVERRIDE==1 -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RMO_NEW_UNDEF -// #endif -// #endif - - -// These pointers are statically and globally defined in RakMemoryOverride.cpp -// Change them to point to your own allocators if you want. -// Use the functions for a DLL, or just reassign the variable if using source -extern RAK_DLL_EXPORT void * (*rakMalloc) (size_t size); -extern RAK_DLL_EXPORT void * (*rakRealloc) (void *p, size_t size); -extern RAK_DLL_EXPORT void (*rakFree) (void *p); -extern RAK_DLL_EXPORT void * (*rakMalloc_Ex) (size_t size, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void * (*rakRealloc_Ex) (void *p, size_t size, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void (*rakFree_Ex) (void *p, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void (*notifyOutOfMemory) (const char *file, const long line); -extern RAK_DLL_EXPORT void * (*dlMallocMMap) (size_t size); -extern RAK_DLL_EXPORT void * (*dlMallocDirectMMap) (size_t size); -extern RAK_DLL_EXPORT int (*dlMallocMUnmap) (void* ptr, size_t size); - -// Change to a user defined allocation function -void RAK_DLL_EXPORT SetMalloc( void* (*userFunction)(size_t size) ); -void RAK_DLL_EXPORT SetRealloc( void* (*userFunction)(void *p, size_t size) ); -void RAK_DLL_EXPORT SetFree( void (*userFunction)(void *p) ); -void RAK_DLL_EXPORT SetMalloc_Ex( void* (*userFunction)(size_t size, const char *file, unsigned int line) ); -void RAK_DLL_EXPORT SetRealloc_Ex( void* (*userFunction)(void *p, size_t size, const char *file, unsigned int line) ); -void RAK_DLL_EXPORT SetFree_Ex( void (*userFunction)(void *p, const char *file, unsigned int line) ); -// Change to a user defined out of memory function -void RAK_DLL_EXPORT SetNotifyOutOfMemory( void (*userFunction)(const char *file, const long line) ); -void RAK_DLL_EXPORT SetDLMallocMMap( void* (*userFunction)(size_t size) ); -void RAK_DLL_EXPORT SetDLMallocDirectMMap( void* (*userFunction)(size_t size) ); -void RAK_DLL_EXPORT SetDLMallocMUnmap( int (*userFunction)(void* ptr, size_t size) ); - -extern RAK_DLL_EXPORT void * (*GetMalloc()) (size_t size); -extern RAK_DLL_EXPORT void * (*GetRealloc()) (void *p, size_t size); -extern RAK_DLL_EXPORT void (*GetFree()) (void *p); -extern RAK_DLL_EXPORT void * (*GetMalloc_Ex()) (size_t size, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void * (*GetRealloc_Ex()) (void *p, size_t size, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void (*GetFree_Ex()) (void *p, const char *file, unsigned int line); -extern RAK_DLL_EXPORT void *(*GetDLMallocMMap())(size_t size); -extern RAK_DLL_EXPORT void *(*GetDLMallocDirectMMap())(size_t size); -extern RAK_DLL_EXPORT int (*GetDLMallocMUnmap())(void* ptr, size_t size); - -namespace RakNet -{ - - template - RAK_DLL_EXPORT Type* OP_NEW(const char *file, unsigned int line) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type; - return t; -#else - (void) file; - (void) line; - return new Type; -#endif - } - - template - RAK_DLL_EXPORT Type* OP_NEW_1(const char *file, unsigned int line, const P1 &p1) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type(p1); - return t; -#else - (void) file; - (void) line; - return new Type(p1); -#endif - } - - template - RAK_DLL_EXPORT Type* OP_NEW_2(const char *file, unsigned int line, const P1 &p1, const P2 &p2) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type(p1, p2); - return t; -#else - (void) file; - (void) line; - return new Type(p1, p2); -#endif - } - - template - RAK_DLL_EXPORT Type* OP_NEW_3(const char *file, unsigned int line, const P1 &p1, const P2 &p2, const P3 &p3) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type(p1, p2, p3); - return t; -#else - (void) file; - (void) line; - return new Type(p1, p2, p3); -#endif - } - - template - RAK_DLL_EXPORT Type* OP_NEW_4(const char *file, unsigned int line, const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); - Type *t = new (buffer) Type(p1, p2, p3, p4); - return t; -#else - (void) file; - (void) line; - return new Type(p1, p2, p3, p4); -#endif - } - - - template - RAK_DLL_EXPORT Type* OP_NEW_ARRAY(const int count, const char *file, unsigned int line) - { - if (count==0) - return 0; - -#if _USE_RAK_MEMORY_OVERRIDE==1 -// Type *t; - char *buffer = (char *) (GetMalloc_Ex())(sizeof(int)+sizeof(Type)*count, file, line); - ((int*)buffer)[0]=count; - for (int i=0; i - RAK_DLL_EXPORT void OP_DELETE(Type *buff, const char *file, unsigned int line) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - if (buff==0) return; - buff->~Type(); - (GetFree_Ex())((char*)buff, file, line ); -#else - (void) file; - (void) line; - delete buff; -#endif - - } - - template - RAK_DLL_EXPORT void OP_DELETE_ARRAY(Type *buff, const char *file, unsigned int line) - { -#if _USE_RAK_MEMORY_OVERRIDE==1 - if (buff==0) - return; - - int count = ((int*)((char*)buff-sizeof(int)))[0]; - Type *t; - for (int i=0; i~Type(); - } - (GetFree_Ex())((char*)buff-sizeof(int), file, line ); -#else - (void) file; - (void) line; - delete [] buff; -#endif - - } - - void RAK_DLL_EXPORT * _RakMalloc (size_t size); - void RAK_DLL_EXPORT * _RakRealloc (void *p, size_t size); - void RAK_DLL_EXPORT _RakFree (void *p); - void RAK_DLL_EXPORT * _RakMalloc_Ex (size_t size, const char *file, unsigned int line); - void RAK_DLL_EXPORT * _RakRealloc_Ex (void *p, size_t size, const char *file, unsigned int line); - void RAK_DLL_EXPORT _RakFree_Ex (void *p, const char *file, unsigned int line); - void RAK_DLL_EXPORT * _DLMallocMMap (size_t size); - void RAK_DLL_EXPORT * _DLMallocDirectMMap (size_t size); - int RAK_DLL_EXPORT _DLMallocMUnmap (void *p, size_t size); - -} - -// Call to make RakNet allocate a large block of memory, and do all subsequent allocations in that memory block -// Initial and reallocations will be done through whatever function is pointed to by yourMMapFunction, and yourDirectMMapFunction (default is malloc) -// Allocations will be freed through whatever function is pointed to by yourMUnmapFunction (default free) -void UseRaknetFixedHeap(size_t initialCapacity, - void * (*yourMMapFunction) (size_t size) = RakNet::_DLMallocMMap, - void * (*yourDirectMMapFunction) (size_t size) = RakNet::_DLMallocDirectMMap, - int (*yourMUnmapFunction) (void *p, size_t size) = RakNet::_DLMallocMUnmap); - -// Free memory allocated from UseRaknetFixedHeap -void FreeRakNetFixedHeap(void); - -// #if _USE_RAK_MEMORY_OVERRIDE==1 -// #if defined(RMO_NEW_UNDEF) -// #pragma pop_macro("new") -// #undef RMO_NEW_UNDEF -// #endif -// #endif - -#endif +/// \file +/// \brief If _USE_RAK_MEMORY_OVERRIDE is defined, memory allocations go through rakMalloc, rakRealloc, and rakFree +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAK_MEMORY_H +#define __RAK_MEMORY_H + +#include "Export.h" +#include "RakNetDefines.h" +#include + + + + + + + +#include "RakAlloca.h" + +// #if _USE_RAK_MEMORY_OVERRIDE==1 +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RMO_NEW_UNDEF +// #endif +// #endif + + +// These pointers are statically and globally defined in RakMemoryOverride.cpp +// Change them to point to your own allocators if you want. +// Use the functions for a DLL, or just reassign the variable if using source +extern RAK_DLL_EXPORT void * (*rakMalloc) (size_t size); +extern RAK_DLL_EXPORT void * (*rakRealloc) (void *p, size_t size); +extern RAK_DLL_EXPORT void (*rakFree) (void *p); +extern RAK_DLL_EXPORT void * (*rakMalloc_Ex) (size_t size, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void * (*rakRealloc_Ex) (void *p, size_t size, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void (*rakFree_Ex) (void *p, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void (*notifyOutOfMemory) (const char *file, const long line); +extern RAK_DLL_EXPORT void * (*dlMallocMMap) (size_t size); +extern RAK_DLL_EXPORT void * (*dlMallocDirectMMap) (size_t size); +extern RAK_DLL_EXPORT int (*dlMallocMUnmap) (void* ptr, size_t size); + +// Change to a user defined allocation function +void RAK_DLL_EXPORT SetMalloc( void* (*userFunction)(size_t size) ); +void RAK_DLL_EXPORT SetRealloc( void* (*userFunction)(void *p, size_t size) ); +void RAK_DLL_EXPORT SetFree( void (*userFunction)(void *p) ); +void RAK_DLL_EXPORT SetMalloc_Ex( void* (*userFunction)(size_t size, const char *file, unsigned int line) ); +void RAK_DLL_EXPORT SetRealloc_Ex( void* (*userFunction)(void *p, size_t size, const char *file, unsigned int line) ); +void RAK_DLL_EXPORT SetFree_Ex( void (*userFunction)(void *p, const char *file, unsigned int line) ); +// Change to a user defined out of memory function +void RAK_DLL_EXPORT SetNotifyOutOfMemory( void (*userFunction)(const char *file, const long line) ); +void RAK_DLL_EXPORT SetDLMallocMMap( void* (*userFunction)(size_t size) ); +void RAK_DLL_EXPORT SetDLMallocDirectMMap( void* (*userFunction)(size_t size) ); +void RAK_DLL_EXPORT SetDLMallocMUnmap( int (*userFunction)(void* ptr, size_t size) ); + +extern RAK_DLL_EXPORT void * (*GetMalloc()) (size_t size); +extern RAK_DLL_EXPORT void * (*GetRealloc()) (void *p, size_t size); +extern RAK_DLL_EXPORT void (*GetFree()) (void *p); +extern RAK_DLL_EXPORT void * (*GetMalloc_Ex()) (size_t size, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void * (*GetRealloc_Ex()) (void *p, size_t size, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void (*GetFree_Ex()) (void *p, const char *file, unsigned int line); +extern RAK_DLL_EXPORT void *(*GetDLMallocMMap())(size_t size); +extern RAK_DLL_EXPORT void *(*GetDLMallocDirectMMap())(size_t size); +extern RAK_DLL_EXPORT int (*GetDLMallocMUnmap())(void* ptr, size_t size); + +namespace RakNet +{ + + template + RAK_DLL_EXPORT Type* OP_NEW(const char *file, unsigned int line) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type; + return t; +#else + (void) file; + (void) line; + return new Type; +#endif + } + + template + RAK_DLL_EXPORT Type* OP_NEW_1(const char *file, unsigned int line, const P1 &p1) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type(p1); + return t; +#else + (void) file; + (void) line; + return new Type(p1); +#endif + } + + template + RAK_DLL_EXPORT Type* OP_NEW_2(const char *file, unsigned int line, const P1 &p1, const P2 &p2) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type(p1, p2); + return t; +#else + (void) file; + (void) line; + return new Type(p1, p2); +#endif + } + + template + RAK_DLL_EXPORT Type* OP_NEW_3(const char *file, unsigned int line, const P1 &p1, const P2 &p2, const P3 &p3) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type(p1, p2, p3); + return t; +#else + (void) file; + (void) line; + return new Type(p1, p2, p3); +#endif + } + + template + RAK_DLL_EXPORT Type* OP_NEW_4(const char *file, unsigned int line, const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + char *buffer = (char *) (GetMalloc_Ex())(sizeof(Type), file, line); + Type *t = new (buffer) Type(p1, p2, p3, p4); + return t; +#else + (void) file; + (void) line; + return new Type(p1, p2, p3, p4); +#endif + } + + + template + RAK_DLL_EXPORT Type* OP_NEW_ARRAY(const int count, const char *file, unsigned int line) + { + if (count==0) + return 0; + +#if _USE_RAK_MEMORY_OVERRIDE==1 +// Type *t; + char *buffer = (char *) (GetMalloc_Ex())(sizeof(int)+sizeof(Type)*count, file, line); + ((int*)buffer)[0]=count; + for (int i=0; i + RAK_DLL_EXPORT void OP_DELETE(Type *buff, const char *file, unsigned int line) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + if (buff==0) return; + buff->~Type(); + (GetFree_Ex())((char*)buff, file, line ); +#else + (void) file; + (void) line; + delete buff; +#endif + + } + + template + RAK_DLL_EXPORT void OP_DELETE_ARRAY(Type *buff, const char *file, unsigned int line) + { +#if _USE_RAK_MEMORY_OVERRIDE==1 + if (buff==0) + return; + + int count = ((int*)((char*)buff-sizeof(int)))[0]; + Type *t; + for (int i=0; i~Type(); + } + (GetFree_Ex())((char*)buff-sizeof(int), file, line ); +#else + (void) file; + (void) line; + delete [] buff; +#endif + + } + + void RAK_DLL_EXPORT * _RakMalloc (size_t size); + void RAK_DLL_EXPORT * _RakRealloc (void *p, size_t size); + void RAK_DLL_EXPORT _RakFree (void *p); + void RAK_DLL_EXPORT * _RakMalloc_Ex (size_t size, const char *file, unsigned int line); + void RAK_DLL_EXPORT * _RakRealloc_Ex (void *p, size_t size, const char *file, unsigned int line); + void RAK_DLL_EXPORT _RakFree_Ex (void *p, const char *file, unsigned int line); + void RAK_DLL_EXPORT * _DLMallocMMap (size_t size); + void RAK_DLL_EXPORT * _DLMallocDirectMMap (size_t size); + int RAK_DLL_EXPORT _DLMallocMUnmap (void *p, size_t size); + +} + +// Call to make RakNet allocate a large block of memory, and do all subsequent allocations in that memory block +// Initial and reallocations will be done through whatever function is pointed to by yourMMapFunction, and yourDirectMMapFunction (default is malloc) +// Allocations will be freed through whatever function is pointed to by yourMUnmapFunction (default free) +void UseRaknetFixedHeap(size_t initialCapacity, + void * (*yourMMapFunction) (size_t size) = RakNet::_DLMallocMMap, + void * (*yourDirectMMapFunction) (size_t size) = RakNet::_DLMallocDirectMMap, + int (*yourMUnmapFunction) (void *p, size_t size) = RakNet::_DLMallocMUnmap); + +// Free memory allocated from UseRaknetFixedHeap +void FreeRakNetFixedHeap(void); + +// #if _USE_RAK_MEMORY_OVERRIDE==1 +// #if defined(RMO_NEW_UNDEF) +// #pragma pop_macro("new") +// #undef RMO_NEW_UNDEF +// #endif +// #endif + +#endif diff --git a/src/raknet/RakNet.vcproj b/src/raknet/RakNet.vcproj index b8f5f07..78bd057 100755 --- a/src/raknet/RakNet.vcproj +++ b/src/raknet/RakNet.vcproj @@ -1,845 +1,845 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/raknet/RakNetCommandParser.cpp b/src/raknet/RakNetCommandParser.cpp index 8e156d1..f572d54 100755 --- a/src/raknet/RakNetCommandParser.cpp +++ b/src/raknet/RakNetCommandParser.cpp @@ -1,302 +1,302 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_RakNetCommandParser==1 - -#include "RakNetCommandParser.h" -#include "TransportInterface.h" -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "RakAssert.h" -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(RakNetCommandParser,RakNetCommandParser); - -RakNetCommandParser::RakNetCommandParser() -{ - RegisterCommand(4, "Startup","( unsigned short maxConnections, unsigned short localPort, const char *forceHostAddress );"); - RegisterCommand(0,"InitializeSecurity","();"); - RegisterCommand(0,"DisableSecurity","( void );"); - RegisterCommand(1,"AddToSecurityExceptionList","( const char *ip );"); - RegisterCommand(1,"RemoveFromSecurityExceptionList","( const char *ip );"); - RegisterCommand(1,"IsInSecurityExceptionList","( const char *ip );"); - RegisterCommand(1,"SetMaximumIncomingConnections","( unsigned short numberAllowed );"); - RegisterCommand(0,"GetMaximumIncomingConnections","( void ) const;"); - RegisterCommand(4,"Connect","( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength );"); - RegisterCommand(2,"Disconnect","( unsigned int blockDuration, unsigned char orderingChannel=0 );"); - RegisterCommand(0,"IsActive","( void ) const;"); - RegisterCommand(0,"GetConnectionList","() const;"); - RegisterCommand(3,"CloseConnection","( const SystemAddress target, bool sendDisconnectionNotification, unsigned char orderingChannel=0 );"); - RegisterCommand(2,"IsConnected","( );"); - RegisterCommand(1,"GetIndexFromSystemAddress","( const SystemAddress systemAddress );"); - RegisterCommand(1,"GetSystemAddressFromIndex","( int index );"); - RegisterCommand(2,"AddToBanList","( const char *IP, RakNet::TimeMS milliseconds=0 );"); - RegisterCommand(1,"RemoveFromBanList","( const char *IP );"); - RegisterCommand(0,"ClearBanList","( void );"); - RegisterCommand(1,"IsBanned","( const char *IP );"); - RegisterCommand(1,"Ping1","( const SystemAddress target );"); - RegisterCommand(3,"Ping2","( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections );"); - RegisterCommand(1,"GetAveragePing","( const SystemAddress systemAddress );"); - RegisterCommand(1,"GetLastPing","( const SystemAddress systemAddress ) const;"); - RegisterCommand(1,"GetLowestPing","( const SystemAddress systemAddress ) const;"); - RegisterCommand(1,"SetOccasionalPing","( bool doPing );"); - RegisterCommand(2,"SetOfflinePingResponse","( const char *data, const unsigned int length );"); - RegisterCommand(0,"GetInternalID","( void ) const;"); - RegisterCommand(1,"GetExternalID","( const SystemAddress target ) const;"); - RegisterCommand(2,"SetTimeoutTime","( RakNet::TimeMS timeMS, const SystemAddress target );"); -// RegisterCommand(1,"SetMTUSize","( int size );"); - RegisterCommand(0,"GetMTUSize","( void ) const;"); - RegisterCommand(0,"GetNumberOfAddresses","( void );"); - RegisterCommand(1,"GetLocalIP","( unsigned int index );"); - RegisterCommand(1,"AllowConnectionResponseIPMigration","( bool allow );"); - RegisterCommand(4,"AdvertiseSystem","( const char *host, unsigned short remotePort, const char *data, int dataLength );"); - RegisterCommand(2,"SetIncomingPassword","( const char* passwordData, int passwordDataLength );"); - RegisterCommand(0,"GetIncomingPassword","( void );"); - RegisterCommand(0,"IsNetworkSimulatorActive","( void );"); -} -RakNetCommandParser::~RakNetCommandParser() -{ -} -void RakNetCommandParser::SetRakPeerInterface(RakNet::RakPeerInterface *rakPeer) -{ - peer=rakPeer; -} -bool RakNetCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString) -{ - (void) originalString; - (void) numParameters; - - if (peer==0) - return false; - - if (strcmp(command, "Startup")==0) - { - RakNet::SocketDescriptor socketDescriptor((unsigned short)atoi(parameterList[1]), parameterList[2]); - ReturnResult(peer->Startup((unsigned short)atoi(parameterList[0]), &socketDescriptor, 1), command, transport, systemAddress); - } - else if (strcmp(command, "InitializeSecurity")==0) - { - ReturnResult(peer->InitializeSecurity(parameterList[0],parameterList[1]), command, transport, systemAddress); - } - else if (strcmp(command, "DisableSecurity")==0) - { - peer->DisableSecurity(); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "AddToSecurityExceptionList")==0) - { - peer->AddToSecurityExceptionList(parameterList[1]); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "RemoveFromSecurityExceptionList")==0) - { - peer->RemoveFromSecurityExceptionList(parameterList[1]); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "IsInSecurityExceptionList")==0) - { - ReturnResult(peer->IsInSecurityExceptionList(parameterList[1]),command, transport, systemAddress); - } - else if (strcmp(command, "SetMaximumIncomingConnections")==0) - { - peer->SetMaximumIncomingConnections((unsigned short)atoi(parameterList[0])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetMaximumIncomingConnections")==0) - { - ReturnResult(peer->GetMaximumIncomingConnections(), command, transport, systemAddress); - } - else if (strcmp(command, "Connect")==0) - { - ReturnResult(peer->Connect(parameterList[0], (unsigned short)atoi(parameterList[1]),parameterList[2],atoi(parameterList[3]))==RakNet::CONNECTION_ATTEMPT_STARTED, command, transport, systemAddress); - } - else if (strcmp(command, "Disconnect")==0) - { - peer->Shutdown(atoi(parameterList[0]), (unsigned char)atoi(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "IsActive")==0) - { - ReturnResult(peer->IsActive(), command, transport, systemAddress); - } - else if (strcmp(command, "GetConnectionList")==0) - { - SystemAddress remoteSystems[32]; - unsigned short count=32; - unsigned i; - if (peer->GetConnectionList(remoteSystems, &count)) - { - if (count==0) - { - transport->Send(systemAddress, "GetConnectionList() returned no systems connected.\r\n"); - } - else - { - transport->Send(systemAddress, "GetConnectionList() returned:\r\n"); - for (i=0; i < count; i++) - { - char str1[64]; - remoteSystems[i].ToString(true, str1); - transport->Send(systemAddress, "%i %s\r\n", i, str1); - } - } - } - else - transport->Send(systemAddress, "GetConnectionList() returned false.\r\n"); - } - else if (strcmp(command, "CloseConnection")==0) - { - peer->CloseConnection(SystemAddress(parameterList[0]), atoi(parameterList[1])!=0,(unsigned char)atoi(parameterList[2])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetConnectionState")==0) - { - ReturnResult((int) peer->GetConnectionState(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "GetIndexFromSystemAddress")==0) - { - ReturnResult(peer->GetIndexFromSystemAddress(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "GetSystemAddressFromIndex")==0) - { - ReturnResult(peer->GetSystemAddressFromIndex(atoi(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "AddToBanList")==0) - { - peer->AddToBanList(parameterList[0], atoi(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "RemoveFromBanList")==0) - { - peer->RemoveFromBanList(parameterList[0]); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "ClearBanList")==0) - { - peer->ClearBanList(); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "IsBanned")==0) - { - ReturnResult(peer->IsBanned(parameterList[0]), command, transport, systemAddress); - } - else if (strcmp(command, "Ping1")==0) - { - peer->Ping(SystemAddress(parameterList[0])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "Ping2")==0) - { - peer->Ping(parameterList[0], (unsigned short) atoi(parameterList[1]), atoi(parameterList[2])!=0); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetAveragePing")==0) - { - ReturnResult(peer->GetAveragePing(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "GetLastPing")==0) - { - ReturnResult(peer->GetLastPing(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "GetLowestPing")==0) - { - ReturnResult(peer->GetLowestPing(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "SetOccasionalPing")==0) - { - peer->SetOccasionalPing(atoi(parameterList[0])!=0); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "SetOfflinePingResponse")==0) - { - peer->SetOfflinePingResponse(parameterList[0], atoi(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetInternalID")==0) - { - ReturnResult(peer->GetInternalID(), command, transport, systemAddress); - } - else if (strcmp(command, "GetExternalID")==0) - { - ReturnResult(peer->GetExternalID(SystemAddress(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "SetTimeoutTime")==0) - { - peer->SetTimeoutTime(atoi(parameterList[0]), SystemAddress(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - /* - else if (strcmp(command, "SetMTUSize")==0) - { - ReturnResult(peer->SetMTUSize(atoi(parameterList[0]), UNASSIGNED_SYSTEM_ADDRESS), command, transport, systemAddress); - } - */ - else if (strcmp(command, "GetMTUSize")==0) - { - ReturnResult(peer->GetMTUSize(UNASSIGNED_SYSTEM_ADDRESS), command, transport, systemAddress); - } - else if (strcmp(command, "GetNumberOfAddresses")==0) - { - ReturnResult((int)peer->GetNumberOfAddresses(), command, transport, systemAddress); - } - else if (strcmp(command, "GetLocalIP")==0) - { - ReturnResult((char*) peer->GetLocalIP(atoi(parameterList[0])), command, transport, systemAddress); - } - else if (strcmp(command, "AllowConnectionResponseIPMigration")==0) - { - peer->AllowConnectionResponseIPMigration(atoi(parameterList[0])!=0); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "AdvertiseSystem")==0) - { - peer->AdvertiseSystem(parameterList[0], (unsigned short) atoi(parameterList[1]),parameterList[2],atoi(parameterList[3])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "SetIncomingPassword")==0) - { - peer->SetIncomingPassword(parameterList[0], atoi(parameterList[1])); - ReturnResult(command, transport, systemAddress); - } - else if (strcmp(command, "GetIncomingPassword")==0) - { - char password[256]; - int passwordLength; - peer->GetIncomingPassword(password, &passwordLength); - if (passwordLength) - ReturnResult((char*)password, command, transport, systemAddress); - else - ReturnResult(0, command, transport, systemAddress); - } - - return true; -} -const char *RakNetCommandParser::GetName(void) const -{ - return "RakNet"; -} -void RakNetCommandParser::SendHelp(TransportInterface *transport, const SystemAddress &systemAddress) -{ - if (peer) - { - transport->Send(systemAddress, "The RakNet parser provides mirror functions to RakPeer\r\n"); - transport->Send(systemAddress, "SystemAddresss take two parameters: send .\r\n"); - transport->Send(systemAddress, "For bool, send 1 or 0.\r\n"); - } - else - { - transport->Send(systemAddress, "Parser not active. Call SetRakPeerInterface.\r\n"); - } -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_RakNetCommandParser==1 + +#include "RakNetCommandParser.h" +#include "TransportInterface.h" +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "RakAssert.h" +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(RakNetCommandParser,RakNetCommandParser); + +RakNetCommandParser::RakNetCommandParser() +{ + RegisterCommand(4, "Startup","( unsigned short maxConnections, unsigned short localPort, const char *forceHostAddress );"); + RegisterCommand(0,"InitializeSecurity","();"); + RegisterCommand(0,"DisableSecurity","( void );"); + RegisterCommand(1,"AddToSecurityExceptionList","( const char *ip );"); + RegisterCommand(1,"RemoveFromSecurityExceptionList","( const char *ip );"); + RegisterCommand(1,"IsInSecurityExceptionList","( const char *ip );"); + RegisterCommand(1,"SetMaximumIncomingConnections","( unsigned short numberAllowed );"); + RegisterCommand(0,"GetMaximumIncomingConnections","( void ) const;"); + RegisterCommand(4,"Connect","( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength );"); + RegisterCommand(2,"Disconnect","( unsigned int blockDuration, unsigned char orderingChannel=0 );"); + RegisterCommand(0,"IsActive","( void ) const;"); + RegisterCommand(0,"GetConnectionList","() const;"); + RegisterCommand(3,"CloseConnection","( const SystemAddress target, bool sendDisconnectionNotification, unsigned char orderingChannel=0 );"); + RegisterCommand(2,"IsConnected","( );"); + RegisterCommand(1,"GetIndexFromSystemAddress","( const SystemAddress systemAddress );"); + RegisterCommand(1,"GetSystemAddressFromIndex","( int index );"); + RegisterCommand(2,"AddToBanList","( const char *IP, RakNet::TimeMS milliseconds=0 );"); + RegisterCommand(1,"RemoveFromBanList","( const char *IP );"); + RegisterCommand(0,"ClearBanList","( void );"); + RegisterCommand(1,"IsBanned","( const char *IP );"); + RegisterCommand(1,"Ping1","( const SystemAddress target );"); + RegisterCommand(3,"Ping2","( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections );"); + RegisterCommand(1,"GetAveragePing","( const SystemAddress systemAddress );"); + RegisterCommand(1,"GetLastPing","( const SystemAddress systemAddress ) const;"); + RegisterCommand(1,"GetLowestPing","( const SystemAddress systemAddress ) const;"); + RegisterCommand(1,"SetOccasionalPing","( bool doPing );"); + RegisterCommand(2,"SetOfflinePingResponse","( const char *data, const unsigned int length );"); + RegisterCommand(0,"GetInternalID","( void ) const;"); + RegisterCommand(1,"GetExternalID","( const SystemAddress target ) const;"); + RegisterCommand(2,"SetTimeoutTime","( RakNet::TimeMS timeMS, const SystemAddress target );"); +// RegisterCommand(1,"SetMTUSize","( int size );"); + RegisterCommand(0,"GetMTUSize","( void ) const;"); + RegisterCommand(0,"GetNumberOfAddresses","( void );"); + RegisterCommand(1,"GetLocalIP","( unsigned int index );"); + RegisterCommand(1,"AllowConnectionResponseIPMigration","( bool allow );"); + RegisterCommand(4,"AdvertiseSystem","( const char *host, unsigned short remotePort, const char *data, int dataLength );"); + RegisterCommand(2,"SetIncomingPassword","( const char* passwordData, int passwordDataLength );"); + RegisterCommand(0,"GetIncomingPassword","( void );"); + RegisterCommand(0,"IsNetworkSimulatorActive","( void );"); +} +RakNetCommandParser::~RakNetCommandParser() +{ +} +void RakNetCommandParser::SetRakPeerInterface(RakNet::RakPeerInterface *rakPeer) +{ + peer=rakPeer; +} +bool RakNetCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString) +{ + (void) originalString; + (void) numParameters; + + if (peer==0) + return false; + + if (strcmp(command, "Startup")==0) + { + RakNet::SocketDescriptor socketDescriptor((unsigned short)atoi(parameterList[1]), parameterList[2]); + ReturnResult(peer->Startup((unsigned short)atoi(parameterList[0]), &socketDescriptor, 1), command, transport, systemAddress); + } + else if (strcmp(command, "InitializeSecurity")==0) + { + ReturnResult(peer->InitializeSecurity(parameterList[0],parameterList[1]), command, transport, systemAddress); + } + else if (strcmp(command, "DisableSecurity")==0) + { + peer->DisableSecurity(); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "AddToSecurityExceptionList")==0) + { + peer->AddToSecurityExceptionList(parameterList[1]); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "RemoveFromSecurityExceptionList")==0) + { + peer->RemoveFromSecurityExceptionList(parameterList[1]); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "IsInSecurityExceptionList")==0) + { + ReturnResult(peer->IsInSecurityExceptionList(parameterList[1]),command, transport, systemAddress); + } + else if (strcmp(command, "SetMaximumIncomingConnections")==0) + { + peer->SetMaximumIncomingConnections((unsigned short)atoi(parameterList[0])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetMaximumIncomingConnections")==0) + { + ReturnResult(peer->GetMaximumIncomingConnections(), command, transport, systemAddress); + } + else if (strcmp(command, "Connect")==0) + { + ReturnResult(peer->Connect(parameterList[0], (unsigned short)atoi(parameterList[1]),parameterList[2],atoi(parameterList[3]))==RakNet::CONNECTION_ATTEMPT_STARTED, command, transport, systemAddress); + } + else if (strcmp(command, "Disconnect")==0) + { + peer->Shutdown(atoi(parameterList[0]), (unsigned char)atoi(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "IsActive")==0) + { + ReturnResult(peer->IsActive(), command, transport, systemAddress); + } + else if (strcmp(command, "GetConnectionList")==0) + { + SystemAddress remoteSystems[32]; + unsigned short count=32; + unsigned i; + if (peer->GetConnectionList(remoteSystems, &count)) + { + if (count==0) + { + transport->Send(systemAddress, "GetConnectionList() returned no systems connected.\r\n"); + } + else + { + transport->Send(systemAddress, "GetConnectionList() returned:\r\n"); + for (i=0; i < count; i++) + { + char str1[64]; + remoteSystems[i].ToString(true, str1); + transport->Send(systemAddress, "%i %s\r\n", i, str1); + } + } + } + else + transport->Send(systemAddress, "GetConnectionList() returned false.\r\n"); + } + else if (strcmp(command, "CloseConnection")==0) + { + peer->CloseConnection(SystemAddress(parameterList[0]), atoi(parameterList[1])!=0,(unsigned char)atoi(parameterList[2])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetConnectionState")==0) + { + ReturnResult((int) peer->GetConnectionState(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "GetIndexFromSystemAddress")==0) + { + ReturnResult(peer->GetIndexFromSystemAddress(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "GetSystemAddressFromIndex")==0) + { + ReturnResult(peer->GetSystemAddressFromIndex(atoi(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "AddToBanList")==0) + { + peer->AddToBanList(parameterList[0], atoi(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "RemoveFromBanList")==0) + { + peer->RemoveFromBanList(parameterList[0]); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "ClearBanList")==0) + { + peer->ClearBanList(); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "IsBanned")==0) + { + ReturnResult(peer->IsBanned(parameterList[0]), command, transport, systemAddress); + } + else if (strcmp(command, "Ping1")==0) + { + peer->Ping(SystemAddress(parameterList[0])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "Ping2")==0) + { + peer->Ping(parameterList[0], (unsigned short) atoi(parameterList[1]), atoi(parameterList[2])!=0); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetAveragePing")==0) + { + ReturnResult(peer->GetAveragePing(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "GetLastPing")==0) + { + ReturnResult(peer->GetLastPing(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "GetLowestPing")==0) + { + ReturnResult(peer->GetLowestPing(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "SetOccasionalPing")==0) + { + peer->SetOccasionalPing(atoi(parameterList[0])!=0); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "SetOfflinePingResponse")==0) + { + peer->SetOfflinePingResponse(parameterList[0], atoi(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetInternalID")==0) + { + ReturnResult(peer->GetInternalID(), command, transport, systemAddress); + } + else if (strcmp(command, "GetExternalID")==0) + { + ReturnResult(peer->GetExternalID(SystemAddress(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "SetTimeoutTime")==0) + { + peer->SetTimeoutTime(atoi(parameterList[0]), SystemAddress(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + /* + else if (strcmp(command, "SetMTUSize")==0) + { + ReturnResult(peer->SetMTUSize(atoi(parameterList[0]), UNASSIGNED_SYSTEM_ADDRESS), command, transport, systemAddress); + } + */ + else if (strcmp(command, "GetMTUSize")==0) + { + ReturnResult(peer->GetMTUSize(UNASSIGNED_SYSTEM_ADDRESS), command, transport, systemAddress); + } + else if (strcmp(command, "GetNumberOfAddresses")==0) + { + ReturnResult((int)peer->GetNumberOfAddresses(), command, transport, systemAddress); + } + else if (strcmp(command, "GetLocalIP")==0) + { + ReturnResult((char*) peer->GetLocalIP(atoi(parameterList[0])), command, transport, systemAddress); + } + else if (strcmp(command, "AllowConnectionResponseIPMigration")==0) + { + peer->AllowConnectionResponseIPMigration(atoi(parameterList[0])!=0); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "AdvertiseSystem")==0) + { + peer->AdvertiseSystem(parameterList[0], (unsigned short) atoi(parameterList[1]),parameterList[2],atoi(parameterList[3])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "SetIncomingPassword")==0) + { + peer->SetIncomingPassword(parameterList[0], atoi(parameterList[1])); + ReturnResult(command, transport, systemAddress); + } + else if (strcmp(command, "GetIncomingPassword")==0) + { + char password[256]; + int passwordLength; + peer->GetIncomingPassword(password, &passwordLength); + if (passwordLength) + ReturnResult((char*)password, command, transport, systemAddress); + else + ReturnResult(0, command, transport, systemAddress); + } + + return true; +} +const char *RakNetCommandParser::GetName(void) const +{ + return "RakNet"; +} +void RakNetCommandParser::SendHelp(TransportInterface *transport, const SystemAddress &systemAddress) +{ + if (peer) + { + transport->Send(systemAddress, "The RakNet parser provides mirror functions to RakPeer\r\n"); + transport->Send(systemAddress, "SystemAddresss take two parameters: send .\r\n"); + transport->Send(systemAddress, "For bool, send 1 or 0.\r\n"); + } + else + { + transport->Send(systemAddress, "Parser not active. Call SetRakPeerInterface.\r\n"); + } +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/RakNetCommandParser.h b/src/raknet/RakNetCommandParser.h index 3b684e4..4760291 100755 --- a/src/raknet/RakNetCommandParser.h +++ b/src/raknet/RakNetCommandParser.h @@ -1,63 +1,63 @@ -/// \file -/// \brief Contains RakNetCommandParser , used to send commands to an instance of RakPeer -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_RakNetCommandParser==1 - -#ifndef __RAKNET_COMMAND_PARSER -#define __RAKNET_COMMAND_PARSER - -#include "CommandParserInterface.h" -#include "Export.h" - -namespace RakNet -{ -class RakPeerInterface; - -/// \brief This allows a console client to call most of the functions in RakPeer -class RAK_DLL_EXPORT RakNetCommandParser : public CommandParserInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(RakNetCommandParser) - - RakNetCommandParser(); - ~RakNetCommandParser(); - - /// Given \a command with parameters \a parameterList , do whatever processing you wish. - /// \param[in] command The command to process - /// \param[in] numParameters How many parameters were passed along with the command - /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player that sent this command. - /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing - bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString); - - /// You are responsible for overriding this function and returning a static string, which will identifier your parser. - /// This should return a static string - /// \return The name that you return. - const char *GetName(void) const; - - /// A callback for when you are expected to send a brief description of your parser to \a systemAddress - /// \param[in] transport The transport interface we can use to write to - /// \param[in] systemAddress The player that requested help. - void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress); - - /// Records the instance of RakPeer to perform the desired commands on - /// \param[in] rakPeer The RakPeer instance, or a derived class (e.g. RakPeer or RakPeer) - void SetRakPeerInterface(RakNet::RakPeerInterface *rakPeer); -protected: - - /// Which instance of RakPeer we are working on. Set from SetRakPeerInterface() - RakPeerInterface *peer; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains RakNetCommandParser , used to send commands to an instance of RakPeer +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_RakNetCommandParser==1 + +#ifndef __RAKNET_COMMAND_PARSER +#define __RAKNET_COMMAND_PARSER + +#include "CommandParserInterface.h" +#include "Export.h" + +namespace RakNet +{ +class RakPeerInterface; + +/// \brief This allows a console client to call most of the functions in RakPeer +class RAK_DLL_EXPORT RakNetCommandParser : public CommandParserInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(RakNetCommandParser) + + RakNetCommandParser(); + ~RakNetCommandParser(); + + /// Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + const char *GetName(void) const; + + /// A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + void SendHelp(TransportInterface *transport, const SystemAddress &systemAddress); + + /// Records the instance of RakPeer to perform the desired commands on + /// \param[in] rakPeer The RakPeer instance, or a derived class (e.g. RakPeer or RakPeer) + void SetRakPeerInterface(RakNet::RakPeerInterface *rakPeer); +protected: + + /// Which instance of RakPeer we are working on. Set from SetRakPeerInterface() + RakPeerInterface *peer; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/RakNetDefines.h b/src/raknet/RakNetDefines.h index 78822f6..a0d1cfe 100755 --- a/src/raknet/RakNetDefines.h +++ b/src/raknet/RakNetDefines.h @@ -1,169 +1,169 @@ -#ifndef __RAKNET_DEFINES_H -#define __RAKNET_DEFINES_H - -// If you want to change these defines, put them in RakNetDefinesOverrides so your changes are not lost when updating RakNet -// The user should not edit this file -#include "RakNetDefinesOverrides.h" - -/// Define __GET_TIME_64BIT to have RakNet::TimeMS use a 64, rather than 32 bit value. A 32 bit value will overflow after about 5 weeks. -/// However, this doubles the bandwidth use for sending times, so don't do it unless you have a reason to. -/// Comment out if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 -/// This must be the same on all systems, or they won't connect -#ifndef __GET_TIME_64BIT -#define __GET_TIME_64BIT 1 -#endif - -// Define _FILE_AND_LINE_ to "",0 if you want to strip out file and line info for memory tracking from the EXE -#ifndef _FILE_AND_LINE_ -#define _FILE_AND_LINE_ __FILE__,__LINE__ -#endif - -/// Define __BITSTREAM_NATIVE_END to NOT support endian swapping in the BitStream class. This is faster and is what you should use -/// unless you actually plan to have different endianness systems connect to each other -/// Enabled by default. -// #define __BITSTREAM_NATIVE_END - -/// Maximum (stack) size to use with _alloca before using new and delete instead. -#ifndef MAX_ALLOCA_STACK_ALLOCATION -#define MAX_ALLOCA_STACK_ALLOCATION 1048576 -#endif - -// Use WaitForSingleObject instead of sleep. -// Defining it plays nicer with other systems, and uses less CPU, but gives worse RakNet performance -// Undefining it uses more CPU time, but is more responsive and faster. -#ifndef _WIN32_WCE -#define USE_WAIT_FOR_MULTIPLE_EVENTS -#endif - -/// Uncomment to use RakMemoryOverride for custom memory tracking -/// See RakMemoryOverride.h. -#ifndef _USE_RAK_MEMORY_OVERRIDE -#define _USE_RAK_MEMORY_OVERRIDE 0 -#endif - -/// If defined, OpenSSL is enabled for the class TCPInterface -/// This is necessary to use the SendEmail class with Google POP servers -/// Note that OpenSSL carries its own license restrictions that you should be aware of. If you don't agree, don't enable this define -/// This also requires that you enable header search paths to DependentExtensions\openssl-0.9.8g -// #define OPEN_SSL_CLIENT_SUPPORT -#ifndef OPEN_SSL_CLIENT_SUPPORT -#define OPEN_SSL_CLIENT_SUPPORT 0 -#endif - -/// Threshold at which to do a malloc / free rather than pushing data onto a fixed stack for the bitstream class -/// Arbitrary size, just picking something likely to be larger than most packets -#ifndef BITSTREAM_STACK_ALLOCATION_SIZE -#define BITSTREAM_STACK_ALLOCATION_SIZE 256 -#endif - -// Redefine if you want to disable or change the target for debug RAKNET_DEBUG_PRINTF -#ifndef RAKNET_DEBUG_PRINTF -#define RAKNET_DEBUG_PRINTF printf -#endif - -// Maximum number of local IP addresses supported -#ifndef MAXIMUM_NUMBER_OF_INTERNAL_IDS -#define MAXIMUM_NUMBER_OF_INTERNAL_IDS 10 -#endif - -#ifndef RakAssert - - - - -#if defined(_DEBUG) -#define RakAssert(x) assert(x); -#else -#define RakAssert(x) -#endif - -#endif - -/// This controls the amount of memory used per connection. -/// This many datagrams are tracked by datagramNumber. If more than this many datagrams are sent, then an ack for an older datagram would be ignored -/// This results in an unnecessary resend in that case -#ifndef DATAGRAM_MESSAGE_ID_ARRAY_LENGTH -#define DATAGRAM_MESSAGE_ID_ARRAY_LENGTH 512 -#endif - -/// This is the maximum number of reliable user messages that can be on the wire at a time -/// If this is too low, then high ping connections with a large throughput will be underutilized -/// This will be evident because RakNetStatistics::messagesInSend buffer will increase over time, yet at the same time the outgoing bandwidth per second is less than your connection supports -#ifndef RESEND_BUFFER_ARRAY_LENGTH -#define RESEND_BUFFER_ARRAY_LENGTH 512 -#define RESEND_BUFFER_ARRAY_MASK 511 -#endif - -/// Uncomment if you want to link in the DLMalloc library to use with RakMemoryOverride -// #define _LINK_DL_MALLOC - -#ifndef GET_TIME_SPIKE_LIMIT -/// Workaround for http://support.microsoft.com/kb/274323 -/// If two calls between RakNet::GetTime() happen farther apart than this time in microseconds, this delta will be returned instead -/// Note: This will cause ID_TIMESTAMP to be temporarily inaccurate if you set a breakpoint that pauses the UpdateNetworkLoop() thread in RakPeer -/// Define in RakNetDefinesOverrides.h to enable (non-zero) or disable (0) -#define GET_TIME_SPIKE_LIMIT 0 -#endif - -// Use sliding window congestion control instead of ping based congestion control -#ifndef USE_SLIDING_WINDOW_CONGESTION_CONTROL -#define USE_SLIDING_WINDOW_CONGESTION_CONTROL 1 -#endif - -// When a large message is arriving, preallocate the memory for the entire block -// This results in large messages not taking up time to reassembly with memcpy, but is vulnerable to attackers causing the host to run out of memory -#ifndef PREALLOCATE_LARGE_MESSAGES -#define PREALLOCATE_LARGE_MESSAGES 0 -#endif - -#ifndef RAKNET_SUPPORT_IPV6 -#define RAKNET_SUPPORT_IPV6 0 -#endif - - - - - - - - - - - -#ifndef RAKSTRING_TYPE -#if defined(_UNICODE) -#define RAKSTRING_TYPE RakWString -#define RAKSTRING_TYPE_IS_UNICODE 1 -#else -#define RAKSTRING_TYPE RakString -#define RAKSTRING_TYPE_IS_UNICODE 0 -#endif -#endif - -#ifndef RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS -#define RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS 32 -#endif - -#ifndef RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH -#define RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH 32 -#endif - -#ifndef XBOX_BYPASS_SECURITY -#define XBOX_BYPASS_SECURITY 1 -#endif - -// Controls how many allocations occur at once for the memory pool of incoming datagrams waiting to be transferred between the recvfrom thread and the main update thread -// Has large effect on memory usage, per instance of RakPeer. Approximately MAXIMUM_MTU_SIZE*BUFFERED_PACKETS_PAGE_SIZE bytes, once after calling RakPeer::Startup() -#ifndef BUFFERED_PACKETS_PAGE_SIZE -#define BUFFERED_PACKETS_PAGE_SIZE 8 -#endif - -// Controls how many allocations occur at once for the memory pool of incoming or outgoing datagrams. -// Has small effect on memory usage per connection. Uses about 256 bytes*INTERNAL_PACKET_PAGE_SIZE per connection -#ifndef INTERNAL_PACKET_PAGE_SIZE -#define INTERNAL_PACKET_PAGE_SIZE 8 -#endif - -//#define USE_THREADED_SEND - -#endif // __RAKNET_DEFINES_H +#ifndef __RAKNET_DEFINES_H +#define __RAKNET_DEFINES_H + +// If you want to change these defines, put them in RakNetDefinesOverrides so your changes are not lost when updating RakNet +// The user should not edit this file +#include "RakNetDefinesOverrides.h" + +/// Define __GET_TIME_64BIT to have RakNet::TimeMS use a 64, rather than 32 bit value. A 32 bit value will overflow after about 5 weeks. +/// However, this doubles the bandwidth use for sending times, so don't do it unless you have a reason to. +/// Comment out if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0 +/// This must be the same on all systems, or they won't connect +#ifndef __GET_TIME_64BIT +#define __GET_TIME_64BIT 1 +#endif + +// Define _FILE_AND_LINE_ to "",0 if you want to strip out file and line info for memory tracking from the EXE +#ifndef _FILE_AND_LINE_ +#define _FILE_AND_LINE_ __FILE__,__LINE__ +#endif + +/// Define __BITSTREAM_NATIVE_END to NOT support endian swapping in the BitStream class. This is faster and is what you should use +/// unless you actually plan to have different endianness systems connect to each other +/// Enabled by default. +// #define __BITSTREAM_NATIVE_END + +/// Maximum (stack) size to use with _alloca before using new and delete instead. +#ifndef MAX_ALLOCA_STACK_ALLOCATION +#define MAX_ALLOCA_STACK_ALLOCATION 1048576 +#endif + +// Use WaitForSingleObject instead of sleep. +// Defining it plays nicer with other systems, and uses less CPU, but gives worse RakNet performance +// Undefining it uses more CPU time, but is more responsive and faster. +#ifndef _WIN32_WCE +#define USE_WAIT_FOR_MULTIPLE_EVENTS +#endif + +/// Uncomment to use RakMemoryOverride for custom memory tracking +/// See RakMemoryOverride.h. +#ifndef _USE_RAK_MEMORY_OVERRIDE +#define _USE_RAK_MEMORY_OVERRIDE 0 +#endif + +/// If defined, OpenSSL is enabled for the class TCPInterface +/// This is necessary to use the SendEmail class with Google POP servers +/// Note that OpenSSL carries its own license restrictions that you should be aware of. If you don't agree, don't enable this define +/// This also requires that you enable header search paths to DependentExtensions\openssl-0.9.8g +// #define OPEN_SSL_CLIENT_SUPPORT +#ifndef OPEN_SSL_CLIENT_SUPPORT +#define OPEN_SSL_CLIENT_SUPPORT 0 +#endif + +/// Threshold at which to do a malloc / free rather than pushing data onto a fixed stack for the bitstream class +/// Arbitrary size, just picking something likely to be larger than most packets +#ifndef BITSTREAM_STACK_ALLOCATION_SIZE +#define BITSTREAM_STACK_ALLOCATION_SIZE 256 +#endif + +// Redefine if you want to disable or change the target for debug RAKNET_DEBUG_PRINTF +#ifndef RAKNET_DEBUG_PRINTF +#define RAKNET_DEBUG_PRINTF printf +#endif + +// Maximum number of local IP addresses supported +#ifndef MAXIMUM_NUMBER_OF_INTERNAL_IDS +#define MAXIMUM_NUMBER_OF_INTERNAL_IDS 10 +#endif + +#ifndef RakAssert + + + + +#if defined(_DEBUG) +#define RakAssert(x) assert(x); +#else +#define RakAssert(x) +#endif + +#endif + +/// This controls the amount of memory used per connection. +/// This many datagrams are tracked by datagramNumber. If more than this many datagrams are sent, then an ack for an older datagram would be ignored +/// This results in an unnecessary resend in that case +#ifndef DATAGRAM_MESSAGE_ID_ARRAY_LENGTH +#define DATAGRAM_MESSAGE_ID_ARRAY_LENGTH 512 +#endif + +/// This is the maximum number of reliable user messages that can be on the wire at a time +/// If this is too low, then high ping connections with a large throughput will be underutilized +/// This will be evident because RakNetStatistics::messagesInSend buffer will increase over time, yet at the same time the outgoing bandwidth per second is less than your connection supports +#ifndef RESEND_BUFFER_ARRAY_LENGTH +#define RESEND_BUFFER_ARRAY_LENGTH 512 +#define RESEND_BUFFER_ARRAY_MASK 511 +#endif + +/// Uncomment if you want to link in the DLMalloc library to use with RakMemoryOverride +// #define _LINK_DL_MALLOC + +#ifndef GET_TIME_SPIKE_LIMIT +/// Workaround for http://support.microsoft.com/kb/274323 +/// If two calls between RakNet::GetTime() happen farther apart than this time in microseconds, this delta will be returned instead +/// Note: This will cause ID_TIMESTAMP to be temporarily inaccurate if you set a breakpoint that pauses the UpdateNetworkLoop() thread in RakPeer +/// Define in RakNetDefinesOverrides.h to enable (non-zero) or disable (0) +#define GET_TIME_SPIKE_LIMIT 0 +#endif + +// Use sliding window congestion control instead of ping based congestion control +#ifndef USE_SLIDING_WINDOW_CONGESTION_CONTROL +#define USE_SLIDING_WINDOW_CONGESTION_CONTROL 1 +#endif + +// When a large message is arriving, preallocate the memory for the entire block +// This results in large messages not taking up time to reassembly with memcpy, but is vulnerable to attackers causing the host to run out of memory +#ifndef PREALLOCATE_LARGE_MESSAGES +#define PREALLOCATE_LARGE_MESSAGES 0 +#endif + +#ifndef RAKNET_SUPPORT_IPV6 +#define RAKNET_SUPPORT_IPV6 0 +#endif + + + + + + + + + + + +#ifndef RAKSTRING_TYPE +#if defined(_UNICODE) +#define RAKSTRING_TYPE RakWString +#define RAKSTRING_TYPE_IS_UNICODE 1 +#else +#define RAKSTRING_TYPE RakString +#define RAKSTRING_TYPE_IS_UNICODE 0 +#endif +#endif + +#ifndef RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS +#define RPC4_GLOBAL_REGISTRATION_MAX_FUNCTIONS 32 +#endif + +#ifndef RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH +#define RPC4_GLOBAL_REGISTRATION_MAX_FUNCTION_NAME_LENGTH 32 +#endif + +#ifndef XBOX_BYPASS_SECURITY +#define XBOX_BYPASS_SECURITY 1 +#endif + +// Controls how many allocations occur at once for the memory pool of incoming datagrams waiting to be transferred between the recvfrom thread and the main update thread +// Has large effect on memory usage, per instance of RakPeer. Approximately MAXIMUM_MTU_SIZE*BUFFERED_PACKETS_PAGE_SIZE bytes, once after calling RakPeer::Startup() +#ifndef BUFFERED_PACKETS_PAGE_SIZE +#define BUFFERED_PACKETS_PAGE_SIZE 8 +#endif + +// Controls how many allocations occur at once for the memory pool of incoming or outgoing datagrams. +// Has small effect on memory usage per connection. Uses about 256 bytes*INTERNAL_PACKET_PAGE_SIZE per connection +#ifndef INTERNAL_PACKET_PAGE_SIZE +#define INTERNAL_PACKET_PAGE_SIZE 8 +#endif + +//#define USE_THREADED_SEND + +#endif // __RAKNET_DEFINES_H diff --git a/src/raknet/RakNetDefinesOverrides.h b/src/raknet/RakNetDefinesOverrides.h index 7613ea2..913357a 100755 --- a/src/raknet/RakNetDefinesOverrides.h +++ b/src/raknet/RakNetDefinesOverrides.h @@ -1,2 +1,2 @@ -// USER EDITABLE FILE - +// USER EDITABLE FILE + diff --git a/src/raknet/RakNetSmartPtr.h b/src/raknet/RakNetSmartPtr.h index fb9b9d8..4e5a8d7 100755 --- a/src/raknet/RakNetSmartPtr.h +++ b/src/raknet/RakNetSmartPtr.h @@ -1,173 +1,173 @@ -#ifndef __RAKNET_SMART_PTR_H -#define __RAKNET_SMART_PTR_H - -// From http://www.codeproject.com/KB/cpp/SmartPointers.aspx -// with bugs fixed - -#include "RakMemoryOverride.h" -#include "Export.h" - -//static int allocCount=0; -//static int deallocCount=0; - -namespace RakNet -{ - -class RAK_DLL_EXPORT ReferenceCounter -{ -private: - int refCount; - -public: - ReferenceCounter() {refCount=0;} - ~ReferenceCounter() {} - void AddRef() {refCount++;} - int Release() {return --refCount;} - int GetRefCount(void) const {return refCount;} -}; - -template < typename T > class RAK_DLL_EXPORT RakNetSmartPtr -{ -private: - T* ptr; // pointer - ReferenceCounter* reference; // Reference refCount - -public: - RakNetSmartPtr() : ptr(0), reference(0) - { - // Do not allocate by default, wasteful if we just have a list of preallocated and unassigend smart pointers - } - - RakNetSmartPtr(T* pValue) : ptr(pValue) - { - reference = RakNet::OP_NEW(_FILE_AND_LINE_); - reference->AddRef(); - -// allocCount+=2; -// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); - } - - RakNetSmartPtr(const RakNetSmartPtr& sp) : ptr(sp.ptr), reference(sp.reference) - { - if (reference) - reference->AddRef(); - } - - ~RakNetSmartPtr() - { - if(reference && reference->Release() == 0) - { - RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); - RakNet::OP_DELETE(reference, _FILE_AND_LINE_); - -// deallocCount+=2; -// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); - } - } - - bool IsNull(void) const - { - return ptr==0; - } - - void SetNull(void) - { - if(reference && reference->Release() == 0) - { - RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); - RakNet::OP_DELETE(reference, _FILE_AND_LINE_); - -// deallocCount+=2; -// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); - } - ptr=0; - reference=0; - } - - bool IsUnique(void) const - { - return reference->GetRefCount()==1; - } - - // Allow you to change the values of the internal contents of the pointer, without changing what is pointed to by other instances of the smart pointer - void Clone(bool copyContents) - { - if (IsUnique()==false) - { - reference->Release(); - - reference = RakNet::OP_NEW(_FILE_AND_LINE_); - reference->AddRef(); - T* oldPtr=ptr; - ptr=RakNet::OP_NEW(_FILE_AND_LINE_); - if (copyContents) - *ptr=*oldPtr; - } - } - - int GetRefCount(void) const - { - return reference->GetRefCount(); - } - - T& operator* () - { - return *ptr; - } - - const T& operator* () const - { - return *ptr; - } - - T* operator-> () - { - return ptr; - } - - const T* operator-> () const - { - return ptr; - } - - bool operator == (const RakNetSmartPtr& sp) - { - return ptr == sp.ptr; - } - bool operator<( const RakNetSmartPtr &right ) {return ptr < right.ptr;} - bool operator>( const RakNetSmartPtr &right ) {return ptr > right.ptr;} - - bool operator != (const RakNetSmartPtr& sp) - { - return ptr != sp.ptr; - } - - RakNetSmartPtr& operator = (const RakNetSmartPtr& sp) - { - // Assignment operator - - if (this != &sp) // Avoid self assignment - { - if(reference && reference->Release() == 0) - { - RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); - RakNet::OP_DELETE(reference, _FILE_AND_LINE_); - -// deallocCount+=2; -// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); - } - - ptr = sp.ptr; - reference = sp.reference; - if (reference) - reference->AddRef(); - } - return *this; - } - - -}; - -} // namespace RakNet - -#endif +#ifndef __RAKNET_SMART_PTR_H +#define __RAKNET_SMART_PTR_H + +// From http://www.codeproject.com/KB/cpp/SmartPointers.aspx +// with bugs fixed + +#include "RakMemoryOverride.h" +#include "Export.h" + +//static int allocCount=0; +//static int deallocCount=0; + +namespace RakNet +{ + +class RAK_DLL_EXPORT ReferenceCounter +{ +private: + int refCount; + +public: + ReferenceCounter() {refCount=0;} + ~ReferenceCounter() {} + void AddRef() {refCount++;} + int Release() {return --refCount;} + int GetRefCount(void) const {return refCount;} +}; + +template < typename T > class RAK_DLL_EXPORT RakNetSmartPtr +{ +private: + T* ptr; // pointer + ReferenceCounter* reference; // Reference refCount + +public: + RakNetSmartPtr() : ptr(0), reference(0) + { + // Do not allocate by default, wasteful if we just have a list of preallocated and unassigend smart pointers + } + + RakNetSmartPtr(T* pValue) : ptr(pValue) + { + reference = RakNet::OP_NEW(_FILE_AND_LINE_); + reference->AddRef(); + +// allocCount+=2; +// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); + } + + RakNetSmartPtr(const RakNetSmartPtr& sp) : ptr(sp.ptr), reference(sp.reference) + { + if (reference) + reference->AddRef(); + } + + ~RakNetSmartPtr() + { + if(reference && reference->Release() == 0) + { + RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); + RakNet::OP_DELETE(reference, _FILE_AND_LINE_); + +// deallocCount+=2; +// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); + } + } + + bool IsNull(void) const + { + return ptr==0; + } + + void SetNull(void) + { + if(reference && reference->Release() == 0) + { + RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); + RakNet::OP_DELETE(reference, _FILE_AND_LINE_); + +// deallocCount+=2; +// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); + } + ptr=0; + reference=0; + } + + bool IsUnique(void) const + { + return reference->GetRefCount()==1; + } + + // Allow you to change the values of the internal contents of the pointer, without changing what is pointed to by other instances of the smart pointer + void Clone(bool copyContents) + { + if (IsUnique()==false) + { + reference->Release(); + + reference = RakNet::OP_NEW(_FILE_AND_LINE_); + reference->AddRef(); + T* oldPtr=ptr; + ptr=RakNet::OP_NEW(_FILE_AND_LINE_); + if (copyContents) + *ptr=*oldPtr; + } + } + + int GetRefCount(void) const + { + return reference->GetRefCount(); + } + + T& operator* () + { + return *ptr; + } + + const T& operator* () const + { + return *ptr; + } + + T* operator-> () + { + return ptr; + } + + const T* operator-> () const + { + return ptr; + } + + bool operator == (const RakNetSmartPtr& sp) + { + return ptr == sp.ptr; + } + bool operator<( const RakNetSmartPtr &right ) {return ptr < right.ptr;} + bool operator>( const RakNetSmartPtr &right ) {return ptr > right.ptr;} + + bool operator != (const RakNetSmartPtr& sp) + { + return ptr != sp.ptr; + } + + RakNetSmartPtr& operator = (const RakNetSmartPtr& sp) + { + // Assignment operator + + if (this != &sp) // Avoid self assignment + { + if(reference && reference->Release() == 0) + { + RakNet::OP_DELETE(ptr, _FILE_AND_LINE_); + RakNet::OP_DELETE(reference, _FILE_AND_LINE_); + +// deallocCount+=2; +// printf("allocCount=%i deallocCount=%i Line=%i\n",allocCount, deallocCount, __LINE__); + } + + ptr = sp.ptr; + reference = sp.reference; + if (reference) + reference->AddRef(); + } + return *this; + } + + +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/RakNetSocket.cpp b/src/raknet/RakNetSocket.cpp index a3903b1..da95fb2 100755 --- a/src/raknet/RakNetSocket.cpp +++ b/src/raknet/RakNetSocket.cpp @@ -1,25 +1,25 @@ -#include "RakNetSocket.h" -#include "SocketIncludes.h" -#include "SocketDefines.h" - -using namespace RakNet; - -RakNetSocket::RakNetSocket() { - s = (unsigned int)-1; -#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - recvEvent=INVALID_HANDLE_VALUE; -#endif -} -RakNetSocket::~RakNetSocket() -{ - if ((SOCKET)s != (SOCKET)-1) - closesocket__(s); - -#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - if (recvEvent!=INVALID_HANDLE_VALUE) - { - CloseHandle( recvEvent ); - recvEvent = INVALID_HANDLE_VALUE; - } -#endif -} +#include "RakNetSocket.h" +#include "SocketIncludes.h" +#include "SocketDefines.h" + +using namespace RakNet; + +RakNetSocket::RakNetSocket() { + s = (unsigned int)-1; +#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + recvEvent=INVALID_HANDLE_VALUE; +#endif +} +RakNetSocket::~RakNetSocket() +{ + if ((SOCKET)s != (SOCKET)-1) + closesocket__(s); + +#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + if (recvEvent!=INVALID_HANDLE_VALUE) + { + CloseHandle( recvEvent ); + recvEvent = INVALID_HANDLE_VALUE; + } +#endif +} diff --git a/src/raknet/RakNetSocket.h b/src/raknet/RakNetSocket.h index 49c9557..d55068f 100755 --- a/src/raknet/RakNetSocket.h +++ b/src/raknet/RakNetSocket.h @@ -1,38 +1,38 @@ -#ifndef __RAKNET_SOCKET_H -#define __RAKNET_SOCKET_H - -#include "RakNetTypes.h" -#include "RakNetDefines.h" -#include "Export.h" - -namespace RakNet -{ - -struct RAK_DLL_EXPORT RakNetSocket -{ - RakNetSocket(); - ~RakNetSocket(); - // SocketIncludes.h includes Windows.h, which messes up a lot of compiles - // SOCKET s; - unsigned int s; - unsigned int userConnectionSocketIndex; - SystemAddress boundAddress; - unsigned short socketFamily; - -#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - void* recvEvent; -#endif - - - - - - - unsigned short remotePortRakNetWasStartedOn_PS3_PSP2; - - unsigned int extraSocketOptions; -}; - -} // namespace RakNet - -#endif +#ifndef __RAKNET_SOCKET_H +#define __RAKNET_SOCKET_H + +#include "RakNetTypes.h" +#include "RakNetDefines.h" +#include "Export.h" + +namespace RakNet +{ + +struct RAK_DLL_EXPORT RakNetSocket +{ + RakNetSocket(); + ~RakNetSocket(); + // SocketIncludes.h includes Windows.h, which messes up a lot of compiles + // SOCKET s; + unsigned int s; + unsigned int userConnectionSocketIndex; + SystemAddress boundAddress; + unsigned short socketFamily; + +#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + void* recvEvent; +#endif + + + + + + + unsigned short remotePortRakNetWasStartedOn_PS3_PSP2; + + unsigned int extraSocketOptions; +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/RakNetStatistics.cpp b/src/raknet/RakNetStatistics.cpp index bba9354..5f882b4 100755 --- a/src/raknet/RakNetStatistics.cpp +++ b/src/raknet/RakNetStatistics.cpp @@ -1,148 +1,148 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "RakNetStatistics.h" -#include // sprintf -#include "GetTime.h" -#include "RakString.h" - -using namespace RakNet; - -// Verbosity level currently supports 0 (low), 1 (medium), 2 (high) -// Buffer must be hold enough to hold the output string. See the source to get an idea of how many bytes will be output -void RAK_DLL_EXPORT RakNet::StatisticsToString( RakNetStatistics *s, char *buffer, int verbosityLevel ) -{ - if ( s == 0 ) - { - sprintf( buffer, "stats is a NULL pointer in statsToString\n" ); - return ; - } - - if (verbosityLevel==0) - { - sprintf(buffer, - "Bytes per second sent %" PRINTF_64_BIT_MODIFIER "u\n" - "Bytes per second received %" PRINTF_64_BIT_MODIFIER "u\n" - "Current packetloss %.1f%%\n", - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], - s->packetlossLastSecond*100.0f - ); - } - else if (verbosityLevel==1) - { - sprintf(buffer, - "Actual bytes per second sent %" PRINTF_64_BIT_MODIFIER "u\n" - "Actual bytes per second received %" PRINTF_64_BIT_MODIFIER "u\n" - "Message bytes per second pushed %" PRINTF_64_BIT_MODIFIER "u\n" - "Total actual bytes sent %" PRINTF_64_BIT_MODIFIER "u\n" - "Total actual bytes received %" PRINTF_64_BIT_MODIFIER "u\n" - "Total message bytes pushed %" PRINTF_64_BIT_MODIFIER "u\n" - "Current packetloss %.1f%%\n" - "Average packetloss %.1f%%\n" - "Elapsed connection time in seconds %" PRINTF_64_BIT_MODIFIER "u\n", - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_PUSHED], - (long long unsigned int) s->runningTotal[ACTUAL_BYTES_SENT], - (long long unsigned int) s->runningTotal[ACTUAL_BYTES_RECEIVED], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_PUSHED], - s->packetlossLastSecond*100.0f, - s->packetlossTotal*100.0f, - (long long unsigned int) (uint64_t)((RakNet::GetTimeUS()-s->connectionStartTime)/1000000) - ); - - if (s->BPSLimitByCongestionControl!=0) - { - char buff2[128]; - sprintf(buff2, - "Send capacity %" PRINTF_64_BIT_MODIFIER "u bytes per second (%.0f%%)\n", - (long long unsigned int) s->BPSLimitByCongestionControl, - 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByCongestionControl - ); - strcat(buffer,buff2); - } - if (s->BPSLimitByOutgoingBandwidthLimit!=0) - { - char buff2[128]; - sprintf(buff2, - "Send limit %" PRINTF_64_BIT_MODIFIER "u (%.0f%%)\n", - (long long unsigned int) s->BPSLimitByOutgoingBandwidthLimit, - 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByOutgoingBandwidthLimit - ); - strcat(buffer,buff2); - } - } - else - { - sprintf(buffer, - "Actual bytes per second sent %" PRINTF_64_BIT_MODIFIER "u\n" - "Actual bytes per second received %" PRINTF_64_BIT_MODIFIER "u\n" - "Message bytes per second sent %" PRINTF_64_BIT_MODIFIER "u\n" - "Message bytes per second resent %" PRINTF_64_BIT_MODIFIER "u\n" - "Message bytes per second pushed %" PRINTF_64_BIT_MODIFIER "u\n" - "Message bytes per second processed %" PRINTF_64_BIT_MODIFIER "u\n" - "Message bytes per second ignored %" PRINTF_64_BIT_MODIFIER "u\n" - "Total bytes sent %" PRINTF_64_BIT_MODIFIER "u\n" - "Total bytes received %" PRINTF_64_BIT_MODIFIER "u\n" - "Total message bytes sent %" PRINTF_64_BIT_MODIFIER "u\n" - "Total message bytes resent %" PRINTF_64_BIT_MODIFIER "u\n" - "Total message bytes pushed %" PRINTF_64_BIT_MODIFIER "u\n" - "Total message bytes received %" PRINTF_64_BIT_MODIFIER "u\n" - "Total message bytes ignored %" PRINTF_64_BIT_MODIFIER "u\n" - "Messages in send buffer, by priority %i,%i,%i,%i\n" - "Bytes in send buffer, by priority %i,%i,%i,%i\n" - "Messages in resend buffer %i\n" - "Bytes in resend buffer %" PRINTF_64_BIT_MODIFIER "u\n" - "Current packetloss %.1f%%\n" - "Average packetloss %.1f%%\n" - "Elapsed connection time in seconds %" PRINTF_64_BIT_MODIFIER "u\n", - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], - (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_SENT], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_PUSHED], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RECEIVED_PROCESSED], - (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RECEIVED_IGNORED], - (long long unsigned int) s->runningTotal[ACTUAL_BYTES_SENT], - (long long unsigned int) s->runningTotal[ACTUAL_BYTES_RECEIVED], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_SENT], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RESENT], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_PUSHED], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RECEIVED_PROCESSED], - (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RECEIVED_IGNORED], - s->messageInSendBuffer[IMMEDIATE_PRIORITY],s->messageInSendBuffer[HIGH_PRIORITY],s->messageInSendBuffer[MEDIUM_PRIORITY],s->messageInSendBuffer[LOW_PRIORITY], - (unsigned int) s->bytesInSendBuffer[IMMEDIATE_PRIORITY],(unsigned int) s->bytesInSendBuffer[HIGH_PRIORITY],(unsigned int) s->bytesInSendBuffer[MEDIUM_PRIORITY],(unsigned int) s->bytesInSendBuffer[LOW_PRIORITY], - s->messagesInResendBuffer, - (long long unsigned int) s->bytesInResendBuffer, - s->packetlossLastSecond*100.0f, - s->packetlossTotal*100.0f, - (long long unsigned int) (uint64_t)((RakNet::GetTimeUS()-s->connectionStartTime)/1000000) - ); - - if (s->BPSLimitByCongestionControl!=0) - { - char buff2[128]; - sprintf(buff2, - "Send capacity %" PRINTF_64_BIT_MODIFIER "u bytes per second (%.0f%%)\n", - (long long unsigned int) s->BPSLimitByCongestionControl, - 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByCongestionControl - ); - strcat(buffer,buff2); - } - if (s->BPSLimitByOutgoingBandwidthLimit!=0) - { - char buff2[128]; - sprintf(buff2, - "Send limit %" PRINTF_64_BIT_MODIFIER "u (%.0f%%)\n", - (long long unsigned int) s->BPSLimitByOutgoingBandwidthLimit, - 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByOutgoingBandwidthLimit - ); - strcat(buffer,buff2); - } - } -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "RakNetStatistics.h" +#include // sprintf +#include "GetTime.h" +#include "RakString.h" + +using namespace RakNet; + +// Verbosity level currently supports 0 (low), 1 (medium), 2 (high) +// Buffer must be hold enough to hold the output string. See the source to get an idea of how many bytes will be output +void RAK_DLL_EXPORT RakNet::StatisticsToString( RakNetStatistics *s, char *buffer, int verbosityLevel ) +{ + if ( s == 0 ) + { + sprintf( buffer, "stats is a NULL pointer in statsToString\n" ); + return ; + } + + if (verbosityLevel==0) + { + sprintf(buffer, + "Bytes per second sent %" PRINTF_64_BIT_MODIFIER "u\n" + "Bytes per second received %" PRINTF_64_BIT_MODIFIER "u\n" + "Current packetloss %.1f%%\n", + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], + s->packetlossLastSecond*100.0f + ); + } + else if (verbosityLevel==1) + { + sprintf(buffer, + "Actual bytes per second sent %" PRINTF_64_BIT_MODIFIER "u\n" + "Actual bytes per second received %" PRINTF_64_BIT_MODIFIER "u\n" + "Message bytes per second pushed %" PRINTF_64_BIT_MODIFIER "u\n" + "Total actual bytes sent %" PRINTF_64_BIT_MODIFIER "u\n" + "Total actual bytes received %" PRINTF_64_BIT_MODIFIER "u\n" + "Total message bytes pushed %" PRINTF_64_BIT_MODIFIER "u\n" + "Current packetloss %.1f%%\n" + "Average packetloss %.1f%%\n" + "Elapsed connection time in seconds %" PRINTF_64_BIT_MODIFIER "u\n", + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_PUSHED], + (long long unsigned int) s->runningTotal[ACTUAL_BYTES_SENT], + (long long unsigned int) s->runningTotal[ACTUAL_BYTES_RECEIVED], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_PUSHED], + s->packetlossLastSecond*100.0f, + s->packetlossTotal*100.0f, + (long long unsigned int) (uint64_t)((RakNet::GetTimeUS()-s->connectionStartTime)/1000000) + ); + + if (s->BPSLimitByCongestionControl!=0) + { + char buff2[128]; + sprintf(buff2, + "Send capacity %" PRINTF_64_BIT_MODIFIER "u bytes per second (%.0f%%)\n", + (long long unsigned int) s->BPSLimitByCongestionControl, + 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByCongestionControl + ); + strcat(buffer,buff2); + } + if (s->BPSLimitByOutgoingBandwidthLimit!=0) + { + char buff2[128]; + sprintf(buff2, + "Send limit %" PRINTF_64_BIT_MODIFIER "u (%.0f%%)\n", + (long long unsigned int) s->BPSLimitByOutgoingBandwidthLimit, + 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByOutgoingBandwidthLimit + ); + strcat(buffer,buff2); + } + } + else + { + sprintf(buffer, + "Actual bytes per second sent %" PRINTF_64_BIT_MODIFIER "u\n" + "Actual bytes per second received %" PRINTF_64_BIT_MODIFIER "u\n" + "Message bytes per second sent %" PRINTF_64_BIT_MODIFIER "u\n" + "Message bytes per second resent %" PRINTF_64_BIT_MODIFIER "u\n" + "Message bytes per second pushed %" PRINTF_64_BIT_MODIFIER "u\n" + "Message bytes per second processed %" PRINTF_64_BIT_MODIFIER "u\n" + "Message bytes per second ignored %" PRINTF_64_BIT_MODIFIER "u\n" + "Total bytes sent %" PRINTF_64_BIT_MODIFIER "u\n" + "Total bytes received %" PRINTF_64_BIT_MODIFIER "u\n" + "Total message bytes sent %" PRINTF_64_BIT_MODIFIER "u\n" + "Total message bytes resent %" PRINTF_64_BIT_MODIFIER "u\n" + "Total message bytes pushed %" PRINTF_64_BIT_MODIFIER "u\n" + "Total message bytes received %" PRINTF_64_BIT_MODIFIER "u\n" + "Total message bytes ignored %" PRINTF_64_BIT_MODIFIER "u\n" + "Messages in send buffer, by priority %i,%i,%i,%i\n" + "Bytes in send buffer, by priority %i,%i,%i,%i\n" + "Messages in resend buffer %i\n" + "Bytes in resend buffer %" PRINTF_64_BIT_MODIFIER "u\n" + "Current packetloss %.1f%%\n" + "Average packetloss %.1f%%\n" + "Elapsed connection time in seconds %" PRINTF_64_BIT_MODIFIER "u\n", + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_SENT], + (long long unsigned int) s->valueOverLastSecond[ACTUAL_BYTES_RECEIVED], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_SENT], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_PUSHED], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RECEIVED_PROCESSED], + (long long unsigned int) s->valueOverLastSecond[USER_MESSAGE_BYTES_RECEIVED_IGNORED], + (long long unsigned int) s->runningTotal[ACTUAL_BYTES_SENT], + (long long unsigned int) s->runningTotal[ACTUAL_BYTES_RECEIVED], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_SENT], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RESENT], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_PUSHED], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RECEIVED_PROCESSED], + (long long unsigned int) s->runningTotal[USER_MESSAGE_BYTES_RECEIVED_IGNORED], + s->messageInSendBuffer[IMMEDIATE_PRIORITY],s->messageInSendBuffer[HIGH_PRIORITY],s->messageInSendBuffer[MEDIUM_PRIORITY],s->messageInSendBuffer[LOW_PRIORITY], + (unsigned int) s->bytesInSendBuffer[IMMEDIATE_PRIORITY],(unsigned int) s->bytesInSendBuffer[HIGH_PRIORITY],(unsigned int) s->bytesInSendBuffer[MEDIUM_PRIORITY],(unsigned int) s->bytesInSendBuffer[LOW_PRIORITY], + s->messagesInResendBuffer, + (long long unsigned int) s->bytesInResendBuffer, + s->packetlossLastSecond*100.0f, + s->packetlossTotal*100.0f, + (long long unsigned int) (uint64_t)((RakNet::GetTimeUS()-s->connectionStartTime)/1000000) + ); + + if (s->BPSLimitByCongestionControl!=0) + { + char buff2[128]; + sprintf(buff2, + "Send capacity %" PRINTF_64_BIT_MODIFIER "u bytes per second (%.0f%%)\n", + (long long unsigned int) s->BPSLimitByCongestionControl, + 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByCongestionControl + ); + strcat(buffer,buff2); + } + if (s->BPSLimitByOutgoingBandwidthLimit!=0) + { + char buff2[128]; + sprintf(buff2, + "Send limit %" PRINTF_64_BIT_MODIFIER "u (%.0f%%)\n", + (long long unsigned int) s->BPSLimitByOutgoingBandwidthLimit, + 100.0f * s->valueOverLastSecond[ACTUAL_BYTES_SENT] / s->BPSLimitByOutgoingBandwidthLimit + ); + strcat(buffer,buff2); + } + } +} diff --git a/src/raknet/RakNetStatistics.h b/src/raknet/RakNetStatistics.h index 4f9cd6b..634a010 100755 --- a/src/raknet/RakNetStatistics.h +++ b/src/raknet/RakNetStatistics.h @@ -1,126 +1,126 @@ -/// \file -/// \brief A structure that holds all statistical data returned by RakNet. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - - -#ifndef __RAK_NET_STATISTICS_H -#define __RAK_NET_STATISTICS_H - -#include "PacketPriority.h" -#include "Export.h" -#include "RakNetTypes.h" - -namespace RakNet -{ - -enum RNSPerSecondMetrics -{ - /// How many bytes per pushed via a call to RakPeerInterface::Send() - USER_MESSAGE_BYTES_PUSHED, - - /// How many user message bytes were sent via a call to RakPeerInterface::Send(). This is less than or equal to USER_MESSAGE_BYTES_PUSHED. - /// A message would be pushed, but not yet sent, due to congestion control - USER_MESSAGE_BYTES_SENT, - - /// How many user message bytes were resent. A message is resent if it is marked as reliable, and either the message didn't arrive or the message ack didn't arrive. - USER_MESSAGE_BYTES_RESENT, - - /// How many user message bytes were received, and processed successfully. - USER_MESSAGE_BYTES_RECEIVED_PROCESSED, - - /// How many user message bytes were received, but ignored due to data format errors. This will usually be 0. - USER_MESSAGE_BYTES_RECEIVED_IGNORED, - - /// How many actual bytes were sent, including per-message and per-datagram overhead, and reliable message acks - ACTUAL_BYTES_SENT, - - /// How many actual bytes were received, including overead and acks. - ACTUAL_BYTES_RECEIVED, - - /// \internal - RNS_PER_SECOND_METRICS_COUNT -}; - -/// \brief Network Statisics Usage -/// -/// Store Statistics information related to network usage -struct RAK_DLL_EXPORT RakNetStatistics -{ - /// For each type in RNSPerSecondMetrics, what is the value over the last 1 second? - uint64_t valueOverLastSecond[RNS_PER_SECOND_METRICS_COUNT]; - - /// For each type in RNSPerSecondMetrics, what is the total value over the lifetime of the connection? - uint64_t runningTotal[RNS_PER_SECOND_METRICS_COUNT]; - - /// When did the connection start? - /// \sa RakNet::GetTimeUS() - RakNet::TimeUS connectionStartTime; - - /// Is our current send rate throttled by congestion control? - /// This value should be true if you send more data per second than your bandwidth capacity - bool isLimitedByCongestionControl; - - /// If \a isLimitedByCongestionControl is true, what is the limit, in bytes per second? - uint64_t BPSLimitByCongestionControl; - - /// Is our current send rate throttled by a call to RakPeer::SetPerConnectionOutgoingBandwidthLimit()? - bool isLimitedByOutgoingBandwidthLimit; - - /// If \a isLimitedByOutgoingBandwidthLimit is true, what is the limit, in bytes per second? - uint64_t BPSLimitByOutgoingBandwidthLimit; - - /// For each priority level, how many messages are waiting to be sent out? - unsigned int messageInSendBuffer[NUMBER_OF_PRIORITIES]; - - /// For each priority level, how many bytes are waiting to be sent out? - double bytesInSendBuffer[NUMBER_OF_PRIORITIES]; - - /// How many messages are waiting in the resend buffer? This includes messages waiting for an ack, so should normally be a small value - /// If the value is rising over time, you are exceeding the bandwidth capacity. See BPSLimitByCongestionControl - unsigned int messagesInResendBuffer; - - /// How many bytes are waiting in the resend buffer. See also messagesInResendBuffer - uint64_t bytesInResendBuffer; - - /// Over the last second, what was our packetloss? This number will range from 0.0 (for none) to 1.0 (for 100%) - float packetlossLastSecond; - - /// What is the average total packetloss over the lifetime of the connection? - float packetlossTotal; - - RakNetStatistics& operator +=(const RakNetStatistics& other) - { - unsigned i; - for (i=0; i < NUMBER_OF_PRIORITIES; i++) - { - messageInSendBuffer[i]+=other.messageInSendBuffer[i]; - bytesInSendBuffer[i]+=other.bytesInSendBuffer[i]; - } - - for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) - { - valueOverLastSecond[i]+=other.valueOverLastSecond[i]; - runningTotal[i]+=other.runningTotal[i]; - } - - return *this; - } -}; - -/// Verbosity level currently supports 0 (low), 1 (medium), 2 (high) -/// \param[in] s The Statistical information to format out -/// \param[in] buffer The buffer containing a formated report -/// \param[in] verbosityLevel -/// 0 low -/// 1 medium -/// 2 high -/// 3 debugging congestion control -void RAK_DLL_EXPORT StatisticsToString( RakNetStatistics *s, char *buffer, int verbosityLevel ); - -} // namespace RakNet - -#endif +/// \file +/// \brief A structure that holds all statistical data returned by RakNet. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + + +#ifndef __RAK_NET_STATISTICS_H +#define __RAK_NET_STATISTICS_H + +#include "PacketPriority.h" +#include "Export.h" +#include "RakNetTypes.h" + +namespace RakNet +{ + +enum RNSPerSecondMetrics +{ + /// How many bytes per pushed via a call to RakPeerInterface::Send() + USER_MESSAGE_BYTES_PUSHED, + + /// How many user message bytes were sent via a call to RakPeerInterface::Send(). This is less than or equal to USER_MESSAGE_BYTES_PUSHED. + /// A message would be pushed, but not yet sent, due to congestion control + USER_MESSAGE_BYTES_SENT, + + /// How many user message bytes were resent. A message is resent if it is marked as reliable, and either the message didn't arrive or the message ack didn't arrive. + USER_MESSAGE_BYTES_RESENT, + + /// How many user message bytes were received, and processed successfully. + USER_MESSAGE_BYTES_RECEIVED_PROCESSED, + + /// How many user message bytes were received, but ignored due to data format errors. This will usually be 0. + USER_MESSAGE_BYTES_RECEIVED_IGNORED, + + /// How many actual bytes were sent, including per-message and per-datagram overhead, and reliable message acks + ACTUAL_BYTES_SENT, + + /// How many actual bytes were received, including overead and acks. + ACTUAL_BYTES_RECEIVED, + + /// \internal + RNS_PER_SECOND_METRICS_COUNT +}; + +/// \brief Network Statisics Usage +/// +/// Store Statistics information related to network usage +struct RAK_DLL_EXPORT RakNetStatistics +{ + /// For each type in RNSPerSecondMetrics, what is the value over the last 1 second? + uint64_t valueOverLastSecond[RNS_PER_SECOND_METRICS_COUNT]; + + /// For each type in RNSPerSecondMetrics, what is the total value over the lifetime of the connection? + uint64_t runningTotal[RNS_PER_SECOND_METRICS_COUNT]; + + /// When did the connection start? + /// \sa RakNet::GetTimeUS() + RakNet::TimeUS connectionStartTime; + + /// Is our current send rate throttled by congestion control? + /// This value should be true if you send more data per second than your bandwidth capacity + bool isLimitedByCongestionControl; + + /// If \a isLimitedByCongestionControl is true, what is the limit, in bytes per second? + uint64_t BPSLimitByCongestionControl; + + /// Is our current send rate throttled by a call to RakPeer::SetPerConnectionOutgoingBandwidthLimit()? + bool isLimitedByOutgoingBandwidthLimit; + + /// If \a isLimitedByOutgoingBandwidthLimit is true, what is the limit, in bytes per second? + uint64_t BPSLimitByOutgoingBandwidthLimit; + + /// For each priority level, how many messages are waiting to be sent out? + unsigned int messageInSendBuffer[NUMBER_OF_PRIORITIES]; + + /// For each priority level, how many bytes are waiting to be sent out? + double bytesInSendBuffer[NUMBER_OF_PRIORITIES]; + + /// How many messages are waiting in the resend buffer? This includes messages waiting for an ack, so should normally be a small value + /// If the value is rising over time, you are exceeding the bandwidth capacity. See BPSLimitByCongestionControl + unsigned int messagesInResendBuffer; + + /// How many bytes are waiting in the resend buffer. See also messagesInResendBuffer + uint64_t bytesInResendBuffer; + + /// Over the last second, what was our packetloss? This number will range from 0.0 (for none) to 1.0 (for 100%) + float packetlossLastSecond; + + /// What is the average total packetloss over the lifetime of the connection? + float packetlossTotal; + + RakNetStatistics& operator +=(const RakNetStatistics& other) + { + unsigned i; + for (i=0; i < NUMBER_OF_PRIORITIES; i++) + { + messageInSendBuffer[i]+=other.messageInSendBuffer[i]; + bytesInSendBuffer[i]+=other.bytesInSendBuffer[i]; + } + + for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) + { + valueOverLastSecond[i]+=other.valueOverLastSecond[i]; + runningTotal[i]+=other.runningTotal[i]; + } + + return *this; + } +}; + +/// Verbosity level currently supports 0 (low), 1 (medium), 2 (high) +/// \param[in] s The Statistical information to format out +/// \param[in] buffer The buffer containing a formated report +/// \param[in] verbosityLevel +/// 0 low +/// 1 medium +/// 2 high +/// 3 debugging congestion control +void RAK_DLL_EXPORT StatisticsToString( RakNetStatistics *s, char *buffer, int verbosityLevel ); + +} // namespace RakNet + +#endif diff --git a/src/raknet/RakNetTime.h b/src/raknet/RakNetTime.h index f9e7cd9..1c76b83 100755 --- a/src/raknet/RakNetTime.h +++ b/src/raknet/RakNetTime.h @@ -1,23 +1,23 @@ -#ifndef __RAKNET_TIME_H -#define __RAKNET_TIME_H - -#include "NativeTypes.h" -#include "RakNetDefines.h" - -namespace RakNet { - -// Define __GET_TIME_64BIT if you want to use large types for GetTime (takes more bandwidth when you transmit time though!) -// You would want to do this if your system is going to run long enough to overflow the millisecond counter (over a month) -#if __GET_TIME_64BIT==1 -typedef uint64_t Time; -typedef uint32_t TimeMS; -typedef uint64_t TimeUS; -#else -typedef uint32_t Time; -typedef uint32_t TimeMS; -typedef uint64_t TimeUS; -#endif - -}; // namespace RakNet - -#endif +#ifndef __RAKNET_TIME_H +#define __RAKNET_TIME_H + +#include "NativeTypes.h" +#include "RakNetDefines.h" + +namespace RakNet { + +// Define __GET_TIME_64BIT if you want to use large types for GetTime (takes more bandwidth when you transmit time though!) +// You would want to do this if your system is going to run long enough to overflow the millisecond counter (over a month) +#if __GET_TIME_64BIT==1 +typedef uint64_t Time; +typedef uint32_t TimeMS; +typedef uint64_t TimeUS; +#else +typedef uint32_t Time; +typedef uint32_t TimeMS; +typedef uint64_t TimeUS; +#endif + +}; // namespace RakNet + +#endif diff --git a/src/raknet/RakNetTransport2.cpp b/src/raknet/RakNetTransport2.cpp index 514318f..e03e155 100755 --- a/src/raknet/RakNetTransport2.cpp +++ b/src/raknet/RakNetTransport2.cpp @@ -1,128 +1,128 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TelnetTransport==1 - -#include "RakNetTransport2.h" - -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include -#include -#include -#include "LinuxStrings.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(RakNetTransport2,RakNetTransport2); - -RakNetTransport2::RakNetTransport2() -{ -} -RakNetTransport2::~RakNetTransport2() -{ - Stop(); -} -bool RakNetTransport2::Start(unsigned short port, bool serverMode) -{ - (void) port; - (void) serverMode; - return true; -} -void RakNetTransport2::Stop(void) -{ - newConnections.Clear(_FILE_AND_LINE_); - lostConnections.Clear(_FILE_AND_LINE_); - for (unsigned int i=0; i < packetQueue.Size(); i++) - { - rakFree_Ex(packetQueue[i]->data,_FILE_AND_LINE_); - RakNet::OP_DELETE(packetQueue[i],_FILE_AND_LINE_); - } - packetQueue.Clear(_FILE_AND_LINE_); -} -void RakNetTransport2::Send( SystemAddress systemAddress, const char *data, ... ) -{ - if (data==0 || data[0]==0) return; - - char text[REMOTE_MAX_TEXT_INPUT]; - va_list ap; - va_start(ap, data); - _vsnprintf(text, REMOTE_MAX_TEXT_INPUT, data, ap); - va_end(ap); - text[REMOTE_MAX_TEXT_INPUT-1]=0; - - RakNet::BitStream str; - str.Write((MessageID)ID_TRANSPORT_STRING); - str.Write(text, (int) strlen(text)); - str.Write((unsigned char) 0); // Null terminate the string - rakPeerInterface->Send(&str, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, (systemAddress==UNASSIGNED_SYSTEM_ADDRESS)!=0); -} -void RakNetTransport2::CloseConnection( SystemAddress systemAddress ) -{ - rakPeerInterface->CloseConnection(systemAddress, true, 0); -} -Packet* RakNetTransport2::Receive( void ) -{ - if (packetQueue.Size()==0) - return 0; - return packetQueue.Pop(); -} -SystemAddress RakNetTransport2::HasNewIncomingConnection(void) -{ - if (newConnections.Size()) - return newConnections.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -SystemAddress RakNetTransport2::HasLostConnection(void) -{ - if (lostConnections.Size()) - return lostConnections.Pop(); - return UNASSIGNED_SYSTEM_ADDRESS; -} -void RakNetTransport2::DeallocatePacket( Packet *packet ) -{ - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(packet, _FILE_AND_LINE_ ); -} -PluginReceiveResult RakNetTransport2::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_TRANSPORT_STRING: - { - if (packet->length==sizeof(MessageID)) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - Packet *p = RakNet::OP_NEW(_FILE_AND_LINE_); - *p=*packet; - p->bitSize-=8; - p->length--; - p->data=(unsigned char*) rakMalloc_Ex(p->length,_FILE_AND_LINE_); - memcpy(p->data, packet->data+1, p->length); - packetQueue.Push(p, _FILE_AND_LINE_ ); - - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - return RR_CONTINUE_PROCESSING; -} -void RakNetTransport2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) rakNetGUID; - (void) lostConnectionReason; - lostConnections.Push(systemAddress, _FILE_AND_LINE_ ); -} -void RakNetTransport2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) rakNetGUID; - (void) isIncoming; - newConnections.Push(systemAddress, _FILE_AND_LINE_ ); -} -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TelnetTransport==1 + +#include "RakNetTransport2.h" + +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include +#include +#include +#include "LinuxStrings.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(RakNetTransport2,RakNetTransport2); + +RakNetTransport2::RakNetTransport2() +{ +} +RakNetTransport2::~RakNetTransport2() +{ + Stop(); +} +bool RakNetTransport2::Start(unsigned short port, bool serverMode) +{ + (void) port; + (void) serverMode; + return true; +} +void RakNetTransport2::Stop(void) +{ + newConnections.Clear(_FILE_AND_LINE_); + lostConnections.Clear(_FILE_AND_LINE_); + for (unsigned int i=0; i < packetQueue.Size(); i++) + { + rakFree_Ex(packetQueue[i]->data,_FILE_AND_LINE_); + RakNet::OP_DELETE(packetQueue[i],_FILE_AND_LINE_); + } + packetQueue.Clear(_FILE_AND_LINE_); +} +void RakNetTransport2::Send( SystemAddress systemAddress, const char *data, ... ) +{ + if (data==0 || data[0]==0) return; + + char text[REMOTE_MAX_TEXT_INPUT]; + va_list ap; + va_start(ap, data); + _vsnprintf(text, REMOTE_MAX_TEXT_INPUT, data, ap); + va_end(ap); + text[REMOTE_MAX_TEXT_INPUT-1]=0; + + RakNet::BitStream str; + str.Write((MessageID)ID_TRANSPORT_STRING); + str.Write(text, (int) strlen(text)); + str.Write((unsigned char) 0); // Null terminate the string + rakPeerInterface->Send(&str, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, (systemAddress==UNASSIGNED_SYSTEM_ADDRESS)!=0); +} +void RakNetTransport2::CloseConnection( SystemAddress systemAddress ) +{ + rakPeerInterface->CloseConnection(systemAddress, true, 0); +} +Packet* RakNetTransport2::Receive( void ) +{ + if (packetQueue.Size()==0) + return 0; + return packetQueue.Pop(); +} +SystemAddress RakNetTransport2::HasNewIncomingConnection(void) +{ + if (newConnections.Size()) + return newConnections.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +SystemAddress RakNetTransport2::HasLostConnection(void) +{ + if (lostConnections.Size()) + return lostConnections.Pop(); + return UNASSIGNED_SYSTEM_ADDRESS; +} +void RakNetTransport2::DeallocatePacket( Packet *packet ) +{ + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(packet, _FILE_AND_LINE_ ); +} +PluginReceiveResult RakNetTransport2::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_TRANSPORT_STRING: + { + if (packet->length==sizeof(MessageID)) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + Packet *p = RakNet::OP_NEW(_FILE_AND_LINE_); + *p=*packet; + p->bitSize-=8; + p->length--; + p->data=(unsigned char*) rakMalloc_Ex(p->length,_FILE_AND_LINE_); + memcpy(p->data, packet->data+1, p->length); + packetQueue.Push(p, _FILE_AND_LINE_ ); + + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + return RR_CONTINUE_PROCESSING; +} +void RakNetTransport2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) rakNetGUID; + (void) lostConnectionReason; + lostConnections.Push(systemAddress, _FILE_AND_LINE_ ); +} +void RakNetTransport2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) rakNetGUID; + (void) isIncoming; + newConnections.Push(systemAddress, _FILE_AND_LINE_ ); +} +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/RakNetTransport2.h b/src/raknet/RakNetTransport2.h index a8b522d..1125048 100755 --- a/src/raknet/RakNetTransport2.h +++ b/src/raknet/RakNetTransport2.h @@ -1,102 +1,102 @@ -/// \file -/// \brief Contains RakNetTransportCommandParser and RakNetTransport used to provide a secure console connection. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TelnetTransport==1 - -#ifndef __RAKNET_TRANSPORT_2 -#define __RAKNET_TRANSPORT_2 - -#include "TransportInterface.h" -#include "DS_Queue.h" -#include "CommandParserInterface.h" -#include "PluginInterface2.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class BitStream; -class RakPeerInterface; -class RakNetTransport; - -/// \defgroup RAKNET_TRANSPORT_GROUP RakNetTransport -/// \brief UDP based transport implementation for the ConsoleServer -/// \details -/// \ingroup PLUGINS_GROUP - -/// \brief Use RakNetTransport if you need a secure connection between the client and the console server. -/// \details RakNetTransport automatically initializes security for the system. Use the project CommandConsoleClient to connect -/// To the ConsoleServer if you use RakNetTransport -/// \ingroup RAKNET_TRANSPORT_GROUP -class RAK_DLL_EXPORT RakNetTransport2 : public TransportInterface, public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(RakNetTransport2) - - RakNetTransport2(); - virtual ~RakNetTransport2(); - - /// Start the transport provider on the indicated port. - /// \param[in] port The port to start the transport provider on - /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) - /// \return Return true on success, false on failure. - bool Start(unsigned short port, bool serverMode); - - /// Stop the transport provider. You can clear memory and shutdown threads here. - void Stop(void); - - /// Send a null-terminated string to \a systemAddress - /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here - /// and parse it out in Receive(). - /// \param[in] systemAddress The player to send the string to - /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF - /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF - void Send( SystemAddress systemAddress, const char *data, ... ); - - /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. - /// \param[in] systemAddress The player/address to disconnect - void CloseConnection( SystemAddress systemAddress ); - - /// Return a string. The string should be allocated and written to Packet::data . - /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress - /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet - /// and thus only return a string in Packet::data - /// \return The packet structure containing the result of Receive, or 0 if no data is available - Packet* Receive( void ); - - /// Deallocate the Packet structure returned by Receive - /// \param[in] The packet to deallocate - void DeallocatePacket( Packet *packet ); - - /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. - /// \return The SystemAddress/address of the system - SystemAddress HasNewIncomingConnection(void); - - /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. - /// \return The SystemAddress/address of the system - SystemAddress HasLostConnection(void); - - virtual CommandParserInterface* GetCommandParser(void) {return 0;} - - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); -protected: - DataStructures::Queue newConnections, lostConnections; - DataStructures::Queue packetQueue; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains RakNetTransportCommandParser and RakNetTransport used to provide a secure console connection. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TelnetTransport==1 + +#ifndef __RAKNET_TRANSPORT_2 +#define __RAKNET_TRANSPORT_2 + +#include "TransportInterface.h" +#include "DS_Queue.h" +#include "CommandParserInterface.h" +#include "PluginInterface2.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class BitStream; +class RakPeerInterface; +class RakNetTransport; + +/// \defgroup RAKNET_TRANSPORT_GROUP RakNetTransport +/// \brief UDP based transport implementation for the ConsoleServer +/// \details +/// \ingroup PLUGINS_GROUP + +/// \brief Use RakNetTransport if you need a secure connection between the client and the console server. +/// \details RakNetTransport automatically initializes security for the system. Use the project CommandConsoleClient to connect +/// To the ConsoleServer if you use RakNetTransport +/// \ingroup RAKNET_TRANSPORT_GROUP +class RAK_DLL_EXPORT RakNetTransport2 : public TransportInterface, public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(RakNetTransport2) + + RakNetTransport2(); + virtual ~RakNetTransport2(); + + /// Start the transport provider on the indicated port. + /// \param[in] port The port to start the transport provider on + /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) + /// \return Return true on success, false on failure. + bool Start(unsigned short port, bool serverMode); + + /// Stop the transport provider. You can clear memory and shutdown threads here. + void Stop(void); + + /// Send a null-terminated string to \a systemAddress + /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here + /// and parse it out in Receive(). + /// \param[in] systemAddress The player to send the string to + /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF + /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF + void Send( SystemAddress systemAddress, const char *data, ... ); + + /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. + /// \param[in] systemAddress The player/address to disconnect + void CloseConnection( SystemAddress systemAddress ); + + /// Return a string. The string should be allocated and written to Packet::data . + /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress + /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet + /// and thus only return a string in Packet::data + /// \return The packet structure containing the result of Receive, or 0 if no data is available + Packet* Receive( void ); + + /// Deallocate the Packet structure returned by Receive + /// \param[in] The packet to deallocate + void DeallocatePacket( Packet *packet ); + + /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + SystemAddress HasNewIncomingConnection(void); + + /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + SystemAddress HasLostConnection(void); + + virtual CommandParserInterface* GetCommandParser(void) {return 0;} + + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); +protected: + DataStructures::Queue newConnections, lostConnections; + DataStructures::Queue packetQueue; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/RakNetTypes.cpp b/src/raknet/RakNetTypes.cpp index d39af87..2a2b109 100755 --- a/src/raknet/RakNetTypes.cpp +++ b/src/raknet/RakNetTypes.cpp @@ -1,725 +1,725 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "RakNetTypes.h" -#include "RakAssert.h" -#include -#include -#include "WindowsIncludes.h" -#include "WSAStartupSingleton.h" -#include "SocketDefines.h" - - -#if defined(_WIN32) -// extern __int64 _strtoui64(const char*, char**, int); // needed for Code::Blocks. Does not compile on Visual Studio 2010 -// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib -// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly -#include - -#else -#include -#include -#include -#endif - -#include // strncasecmp -#include "Itoa.h" -#include "SocketLayer.h" -#include "SuperFastHash.h" -#include - -using namespace RakNet; - -const char *IPV6_LOOPBACK="::1"; -const char *IPV4_LOOPBACK="127.0.0.1"; - -AddressOrGUID::AddressOrGUID( Packet *packet ) -{ - rakNetGuid=packet->guid; - systemAddress=packet->systemAddress; -} - -unsigned long AddressOrGUID::ToInteger( const AddressOrGUID &aog ) -{ - if (aog.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return RakNetGUID::ToUint32(aog.rakNetGuid); - return SystemAddress::ToInteger(aog.systemAddress); -} -const char *AddressOrGUID::ToString(bool writePort) const -{ - if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return rakNetGuid.ToString(); - return systemAddress.ToString(writePort); -} -void AddressOrGUID::ToString(bool writePort, char *dest) const -{ - if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return rakNetGuid.ToString(dest); - return systemAddress.ToString(writePort,dest); -} -bool RakNet::NonNumericHostString( const char *host ) -{ - if ( host[ 0 ] >= '0' && host[ 0 ] <= '9' ) - return false; - - if ( (host[ 0 ] == '-') && ( host[ 1 ] >= '0' && host[ 1 ] <= '9' ) ) - return false; - - if ( strstr(host,":") ) - return false; - - return true; -} - -SocketDescriptor::SocketDescriptor() {port=0; hostAddress[0]=0; remotePortRakNetWasStartedOn_PS3_PSP2=0; extraSocketOptions=0; socketFamily=AF_INET;} -SocketDescriptor::SocketDescriptor(unsigned short _port, const char *_hostAddress) -{ - remotePortRakNetWasStartedOn_PS3_PSP2=0; - port=_port; - if (_hostAddress) - strcpy(hostAddress, _hostAddress); - else - hostAddress[0]=0; - extraSocketOptions=0; - socketFamily=AF_INET; -} - -// Defaults to not in peer to peer mode for NetworkIDs. This only sends the localSystemAddress portion in the BitStream class -// This is what you want for client/server, where the server assigns all NetworkIDs and it is unnecessary to transmit the full structure. -// For peer to peer, this will transmit the systemAddress of the system that created the object in addition to localSystemAddress. This allows -// Any system to create unique ids locally. -// All systems must use the same value for this variable. -//bool RAK_DLL_EXPORT NetworkID::peerToPeerMode=false; - -SystemAddress& SystemAddress::operator = ( const SystemAddress& input ) -{ - memcpy(&address, &input.address, sizeof(address)); - systemIndex = input.systemIndex; - debugPort = input.debugPort; - return *this; -} -bool SystemAddress::EqualsExcludingPort( const SystemAddress& right ) const -{ - return (address.addr4.sin_family==AF_INET && address.addr4.sin_addr.s_addr==right.address.addr4.sin_addr.s_addr) -#if RAKNET_SUPPORT_IPV6==1 - || (address.addr4.sin_family==AF_INET6 && memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))==0) -#endif - ; -} -unsigned short SystemAddress::GetPort(void) const -{ - return ntohs(address.addr4.sin_port); -} -unsigned short SystemAddress::GetPortNetworkOrder(void) const -{ - return address.addr4.sin_port; -} -void SystemAddress::SetPort(unsigned short s) -{ - address.addr4.sin_port=htons(s); - debugPort=s; -} -void SystemAddress::SetPortNetworkOrder(unsigned short s) -{ - address.addr4.sin_port=s; - debugPort=ntohs(s); -} -bool SystemAddress::operator==( const SystemAddress& right ) const -{ - return address.addr4.sin_port == right.address.addr4.sin_port && EqualsExcludingPort(right); -} - -bool SystemAddress::operator!=( const SystemAddress& right ) const -{ - return (*this==right)==false; -} - -bool SystemAddress::operator>( const SystemAddress& right ) const -{ - if (address.addr4.sin_port == right.address.addr4.sin_port) - { -#if RAKNET_SUPPORT_IPV6==1 - if (address.addr4.sin_family==AF_INET) - return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr; - return memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))>0; -#else - return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr; -#endif - } - return address.addr4.sin_port>right.address.addr4.sin_port; -} - -bool SystemAddress::operator<( const SystemAddress& right ) const -{ - if (address.addr4.sin_port == right.address.addr4.sin_port) - { -#if RAKNET_SUPPORT_IPV6==1 - if (address.addr4.sin_family==AF_INET) - return address.addr4.sin_addr.s_addr0; -#else - return address.addr4.sin_addr.s_addr'9')) - break; - IPPart[index]=str[index]; - } - IPPart[index]=0; - portPart[0]=0; - if (str[index] && str[index+1]) - { - index++; - for (portIndex=0; portIndex<10 && str[index] && index < 22+10; index++, portIndex++) - { - if (str[index]<'0' || str[index]>'9') - break; - - portPart[portIndex]=str[index]; - } - portPart[portIndex]=0; - } - - - - - - - - - - - - - - if (IPPart[0]) - { - - - - address.addr4.sin_addr.s_addr=inet_addr__(IPPart); - - } - - - if (portPart[0]) - { - address.addr4.sin_port=htons((unsigned short) atoi(portPart)); - debugPort=ntohs(address.addr4.sin_port); - } - //#endif - } -} - -bool SystemAddress::FromString(const char *str, char portDelineator, int ipVersion) -{ -#if RAKNET_SUPPORT_IPV6!=1 - (void) ipVersion; - SetBinaryAddress(str,portDelineator); - return true; -#else - if (str==0) - { - memset(&address,0,sizeof(address)); - address.addr4.sin_family=AF_INET; - return true; - } -#if RAKNET_SUPPORT_IPV6==1 - char ipPart[INET6_ADDRSTRLEN]; -#else - char ipPart[INET_ADDRSTRLEN]; -#endif - char portPart[32]; - int i=0,j; - - // TODO - what about 255.255.255.255? - if (ipVersion==4 && strcmp(str, IPV6_LOOPBACK)==0) - { - strcpy(ipPart,IPV4_LOOPBACK); - } - else if (ipVersion==6 && strcmp(str, IPV4_LOOPBACK)==0) - { - address.addr4.sin_family=AF_INET6; - strcpy(ipPart,IPV6_LOOPBACK); - } - else if (NonNumericHostString(str)==false) - { - for (; i < sizeof(ipPart) && str[i]!=0 && str[i]!=portDelineator; i++) - { - if ((str[i]<'0' || str[i]>'9') && (str[i]<'a' || str[i]>'f') && (str[i]<'A' || str[i]>'F') && str[i]!='.' && str[i]!=':' && str[i]!='%' && str[i]!='-' && str[i]!='/') - break; - - ipPart[i]=str[i]; - } - ipPart[i]=0; - } - else - { - strncpy(ipPart,str,sizeof(ipPart)); - ipPart[sizeof(ipPart)-1]=0; - } - - j=0; - if (str[i]==portDelineator && portDelineator!=0) - { - i++; - for (; j < sizeof(portPart) && str[i]!=0; i++, j++) - { - portPart[j]=str[i]; - } - } - portPart[j]=0; - - - - - - - - - - - // needed for getaddrinfo - WSAStartupSingleton::AddRef(); - - // This could be a domain, or a printable address such as "192.0.2.1" or "2001:db8:63b3:1::3490" - // I want to convert it to its binary representation - addrinfo hints, *servinfo=0; - memset(&hints, 0, sizeof hints); - hints.ai_socktype = SOCK_DGRAM; - if (ipVersion==6) - hints.ai_family = AF_INET6; - else if (ipVersion==4) - hints.ai_family = AF_INET; - else - hints.ai_family = AF_UNSPEC; - getaddrinfo(ipPart, "", &hints, &servinfo); - if (servinfo==0) - return false; - RakAssert(servinfo); - - unsigned short oldPort = address.addr4.sin_port; -#if RAKNET_SUPPORT_IPV6==1 - if (servinfo->ai_family == AF_INET) - { -// if (ipVersion==6) -// { - address.addr4.sin_family=AF_INET6; -// memset(&address.addr6,0,sizeof(address.addr6)); -// memcpy(address.addr6.sin6_addr.s6_addr+12,&((struct sockaddr_in *)servinfo->ai_addr)->sin_addr.s_addr,sizeof(unsigned long)); -// } -// else -// { - address.addr4.sin_family=AF_INET; - memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in)); -// } - } - else - { - address.addr4.sin_family=AF_INET6; - memcpy(&address.addr6, (struct sockaddr_in6 *)servinfo->ai_addr,sizeof(struct sockaddr_in6)); - } -#else - address.addr4.sin_family=AF_INET4; - memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in)); -#endif - - freeaddrinfo(servinfo); // free the linked list - - // needed for getaddrinfo - WSAStartupSingleton::Deref(); - - // PORT - if (portPart[0]) - { - address.addr4.sin_port=htons((unsigned short) atoi(portPart)); - debugPort=ntohs(address.addr4.sin_port); - } - else - { - address.addr4.sin_port=oldPort; - } -#endif // #if RAKNET_SUPPORT_IPV6!=1 - - return true; -} -bool SystemAddress::FromStringExplicitPort(const char *str, unsigned short port, int ipVersion) -{ - bool b = FromString(str,(char) 0,ipVersion); - if (b==false) - return false; - address.addr4.sin_port=htons(port); - debugPort=ntohs(address.addr4.sin_port); - return true; -} -void SystemAddress::CopyPort( const SystemAddress& right ) -{ - address.addr4.sin_port=right.address.addr4.sin_port; - debugPort=right.debugPort; -} -RakNetGUID::RakNetGUID() -{ - systemIndex=(SystemIndex)-1; - *this=UNASSIGNED_RAKNET_GUID; -} -bool RakNetGUID::operator==( const RakNetGUID& right ) const -{ - return g==right.g; -} -bool RakNetGUID::operator!=( const RakNetGUID& right ) const -{ - return g!=right.g; -} -bool RakNetGUID::operator > ( const RakNetGUID& right ) const -{ - return g > right.g; -} -bool RakNetGUID::operator < ( const RakNetGUID& right ) const -{ - return g < right.g; -} -const char *RakNetGUID::ToString(void) const -{ - static unsigned char strIndex=0; - static char str[8][64]; - - unsigned char lastStrIndex=strIndex; - strIndex++; - ToString(str[lastStrIndex&7]); - return (char*) str[lastStrIndex&7]; -} -void RakNetGUID::ToString(char *dest) const -{ - if (*this==UNASSIGNED_RAKNET_GUID) - strcpy(dest, "UNASSIGNED_RAKNET_GUID"); - - //sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]); - sprintf(dest, "%" PRINTF_64_BIT_MODIFIER "u", (long long unsigned int) g); - // sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]); -} -bool RakNetGUID::FromString(const char *source) -{ - if (source==0) - return false; - - - -#if defined(WIN32) - g=_strtoui64(source, NULL, 10); - - -#else - // Changed from g=strtoull(source,0,10); for android - g=strtoull(source, (char **)NULL, 10); -#endif - return true; - -} -unsigned long RakNetGUID::ToUint32( const RakNetGUID &g ) -{ - return ((unsigned long) (g.g >> 32)) ^ ((unsigned long) (g.g & 0xFFFFFFFF)); -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "RakNetTypes.h" +#include "RakAssert.h" +#include +#include +#include "WindowsIncludes.h" +#include "WSAStartupSingleton.h" +#include "SocketDefines.h" + + +#if defined(_WIN32) +// extern __int64 _strtoui64(const char*, char**, int); // needed for Code::Blocks. Does not compile on Visual Studio 2010 +// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib +// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly +#include + +#else +#include +#include +#include +#endif + +#include // strncasecmp +#include "Itoa.h" +#include "SocketLayer.h" +#include "SuperFastHash.h" +#include + +using namespace RakNet; + +const char *IPV6_LOOPBACK="::1"; +const char *IPV4_LOOPBACK="127.0.0.1"; + +AddressOrGUID::AddressOrGUID( Packet *packet ) +{ + rakNetGuid=packet->guid; + systemAddress=packet->systemAddress; +} + +unsigned long AddressOrGUID::ToInteger( const AddressOrGUID &aog ) +{ + if (aog.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return RakNetGUID::ToUint32(aog.rakNetGuid); + return SystemAddress::ToInteger(aog.systemAddress); +} +const char *AddressOrGUID::ToString(bool writePort) const +{ + if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return rakNetGuid.ToString(); + return systemAddress.ToString(writePort); +} +void AddressOrGUID::ToString(bool writePort, char *dest) const +{ + if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return rakNetGuid.ToString(dest); + return systemAddress.ToString(writePort,dest); +} +bool RakNet::NonNumericHostString( const char *host ) +{ + if ( host[ 0 ] >= '0' && host[ 0 ] <= '9' ) + return false; + + if ( (host[ 0 ] == '-') && ( host[ 1 ] >= '0' && host[ 1 ] <= '9' ) ) + return false; + + if ( strstr(host,":") ) + return false; + + return true; +} + +SocketDescriptor::SocketDescriptor() {port=0; hostAddress[0]=0; remotePortRakNetWasStartedOn_PS3_PSP2=0; extraSocketOptions=0; socketFamily=AF_INET;} +SocketDescriptor::SocketDescriptor(unsigned short _port, const char *_hostAddress) +{ + remotePortRakNetWasStartedOn_PS3_PSP2=0; + port=_port; + if (_hostAddress) + strcpy(hostAddress, _hostAddress); + else + hostAddress[0]=0; + extraSocketOptions=0; + socketFamily=AF_INET; +} + +// Defaults to not in peer to peer mode for NetworkIDs. This only sends the localSystemAddress portion in the BitStream class +// This is what you want for client/server, where the server assigns all NetworkIDs and it is unnecessary to transmit the full structure. +// For peer to peer, this will transmit the systemAddress of the system that created the object in addition to localSystemAddress. This allows +// Any system to create unique ids locally. +// All systems must use the same value for this variable. +//bool RAK_DLL_EXPORT NetworkID::peerToPeerMode=false; + +SystemAddress& SystemAddress::operator = ( const SystemAddress& input ) +{ + memcpy(&address, &input.address, sizeof(address)); + systemIndex = input.systemIndex; + debugPort = input.debugPort; + return *this; +} +bool SystemAddress::EqualsExcludingPort( const SystemAddress& right ) const +{ + return (address.addr4.sin_family==AF_INET && address.addr4.sin_addr.s_addr==right.address.addr4.sin_addr.s_addr) +#if RAKNET_SUPPORT_IPV6==1 + || (address.addr4.sin_family==AF_INET6 && memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))==0) +#endif + ; +} +unsigned short SystemAddress::GetPort(void) const +{ + return ntohs(address.addr4.sin_port); +} +unsigned short SystemAddress::GetPortNetworkOrder(void) const +{ + return address.addr4.sin_port; +} +void SystemAddress::SetPort(unsigned short s) +{ + address.addr4.sin_port=htons(s); + debugPort=s; +} +void SystemAddress::SetPortNetworkOrder(unsigned short s) +{ + address.addr4.sin_port=s; + debugPort=ntohs(s); +} +bool SystemAddress::operator==( const SystemAddress& right ) const +{ + return address.addr4.sin_port == right.address.addr4.sin_port && EqualsExcludingPort(right); +} + +bool SystemAddress::operator!=( const SystemAddress& right ) const +{ + return (*this==right)==false; +} + +bool SystemAddress::operator>( const SystemAddress& right ) const +{ + if (address.addr4.sin_port == right.address.addr4.sin_port) + { +#if RAKNET_SUPPORT_IPV6==1 + if (address.addr4.sin_family==AF_INET) + return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr; + return memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))>0; +#else + return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr; +#endif + } + return address.addr4.sin_port>right.address.addr4.sin_port; +} + +bool SystemAddress::operator<( const SystemAddress& right ) const +{ + if (address.addr4.sin_port == right.address.addr4.sin_port) + { +#if RAKNET_SUPPORT_IPV6==1 + if (address.addr4.sin_family==AF_INET) + return address.addr4.sin_addr.s_addr0; +#else + return address.addr4.sin_addr.s_addr'9')) + break; + IPPart[index]=str[index]; + } + IPPart[index]=0; + portPart[0]=0; + if (str[index] && str[index+1]) + { + index++; + for (portIndex=0; portIndex<10 && str[index] && index < 22+10; index++, portIndex++) + { + if (str[index]<'0' || str[index]>'9') + break; + + portPart[portIndex]=str[index]; + } + portPart[portIndex]=0; + } + + + + + + + + + + + + + + if (IPPart[0]) + { + + + + address.addr4.sin_addr.s_addr=inet_addr__(IPPart); + + } + + + if (portPart[0]) + { + address.addr4.sin_port=htons((unsigned short) atoi(portPart)); + debugPort=ntohs(address.addr4.sin_port); + } + //#endif + } +} + +bool SystemAddress::FromString(const char *str, char portDelineator, int ipVersion) +{ +#if RAKNET_SUPPORT_IPV6!=1 + (void) ipVersion; + SetBinaryAddress(str,portDelineator); + return true; +#else + if (str==0) + { + memset(&address,0,sizeof(address)); + address.addr4.sin_family=AF_INET; + return true; + } +#if RAKNET_SUPPORT_IPV6==1 + char ipPart[INET6_ADDRSTRLEN]; +#else + char ipPart[INET_ADDRSTRLEN]; +#endif + char portPart[32]; + int i=0,j; + + // TODO - what about 255.255.255.255? + if (ipVersion==4 && strcmp(str, IPV6_LOOPBACK)==0) + { + strcpy(ipPart,IPV4_LOOPBACK); + } + else if (ipVersion==6 && strcmp(str, IPV4_LOOPBACK)==0) + { + address.addr4.sin_family=AF_INET6; + strcpy(ipPart,IPV6_LOOPBACK); + } + else if (NonNumericHostString(str)==false) + { + for (; i < sizeof(ipPart) && str[i]!=0 && str[i]!=portDelineator; i++) + { + if ((str[i]<'0' || str[i]>'9') && (str[i]<'a' || str[i]>'f') && (str[i]<'A' || str[i]>'F') && str[i]!='.' && str[i]!=':' && str[i]!='%' && str[i]!='-' && str[i]!='/') + break; + + ipPart[i]=str[i]; + } + ipPart[i]=0; + } + else + { + strncpy(ipPart,str,sizeof(ipPart)); + ipPart[sizeof(ipPart)-1]=0; + } + + j=0; + if (str[i]==portDelineator && portDelineator!=0) + { + i++; + for (; j < sizeof(portPart) && str[i]!=0; i++, j++) + { + portPart[j]=str[i]; + } + } + portPart[j]=0; + + + + + + + + + + + // needed for getaddrinfo + WSAStartupSingleton::AddRef(); + + // This could be a domain, or a printable address such as "192.0.2.1" or "2001:db8:63b3:1::3490" + // I want to convert it to its binary representation + addrinfo hints, *servinfo=0; + memset(&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_DGRAM; + if (ipVersion==6) + hints.ai_family = AF_INET6; + else if (ipVersion==4) + hints.ai_family = AF_INET; + else + hints.ai_family = AF_UNSPEC; + getaddrinfo(ipPart, "", &hints, &servinfo); + if (servinfo==0) + return false; + RakAssert(servinfo); + + unsigned short oldPort = address.addr4.sin_port; +#if RAKNET_SUPPORT_IPV6==1 + if (servinfo->ai_family == AF_INET) + { +// if (ipVersion==6) +// { + address.addr4.sin_family=AF_INET6; +// memset(&address.addr6,0,sizeof(address.addr6)); +// memcpy(address.addr6.sin6_addr.s6_addr+12,&((struct sockaddr_in *)servinfo->ai_addr)->sin_addr.s_addr,sizeof(unsigned long)); +// } +// else +// { + address.addr4.sin_family=AF_INET; + memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in)); +// } + } + else + { + address.addr4.sin_family=AF_INET6; + memcpy(&address.addr6, (struct sockaddr_in6 *)servinfo->ai_addr,sizeof(struct sockaddr_in6)); + } +#else + address.addr4.sin_family=AF_INET4; + memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in)); +#endif + + freeaddrinfo(servinfo); // free the linked list + + // needed for getaddrinfo + WSAStartupSingleton::Deref(); + + // PORT + if (portPart[0]) + { + address.addr4.sin_port=htons((unsigned short) atoi(portPart)); + debugPort=ntohs(address.addr4.sin_port); + } + else + { + address.addr4.sin_port=oldPort; + } +#endif // #if RAKNET_SUPPORT_IPV6!=1 + + return true; +} +bool SystemAddress::FromStringExplicitPort(const char *str, unsigned short port, int ipVersion) +{ + bool b = FromString(str,(char) 0,ipVersion); + if (b==false) + return false; + address.addr4.sin_port=htons(port); + debugPort=ntohs(address.addr4.sin_port); + return true; +} +void SystemAddress::CopyPort( const SystemAddress& right ) +{ + address.addr4.sin_port=right.address.addr4.sin_port; + debugPort=right.debugPort; +} +RakNetGUID::RakNetGUID() +{ + systemIndex=(SystemIndex)-1; + *this=UNASSIGNED_RAKNET_GUID; +} +bool RakNetGUID::operator==( const RakNetGUID& right ) const +{ + return g==right.g; +} +bool RakNetGUID::operator!=( const RakNetGUID& right ) const +{ + return g!=right.g; +} +bool RakNetGUID::operator > ( const RakNetGUID& right ) const +{ + return g > right.g; +} +bool RakNetGUID::operator < ( const RakNetGUID& right ) const +{ + return g < right.g; +} +const char *RakNetGUID::ToString(void) const +{ + static unsigned char strIndex=0; + static char str[8][64]; + + unsigned char lastStrIndex=strIndex; + strIndex++; + ToString(str[lastStrIndex&7]); + return (char*) str[lastStrIndex&7]; +} +void RakNetGUID::ToString(char *dest) const +{ + if (*this==UNASSIGNED_RAKNET_GUID) + strcpy(dest, "UNASSIGNED_RAKNET_GUID"); + + //sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]); + sprintf(dest, "%" PRINTF_64_BIT_MODIFIER "u", (long long unsigned int) g); + // sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]); +} +bool RakNetGUID::FromString(const char *source) +{ + if (source==0) + return false; + + + +#if defined(WIN32) + g=_strtoui64(source, NULL, 10); + + +#else + // Changed from g=strtoull(source,0,10); for android + g=strtoull(source, (char **)NULL, 10); +#endif + return true; + +} +unsigned long RakNetGUID::ToUint32( const RakNetGUID &g ) +{ + return ((unsigned long) (g.g >> 32)) ^ ((unsigned long) (g.g & 0xFFFFFFFF)); +} diff --git a/src/raknet/RakNetTypes.h b/src/raknet/RakNetTypes.h index befcfc1..063be59 100755 --- a/src/raknet/RakNetTypes.h +++ b/src/raknet/RakNetTypes.h @@ -1,482 +1,482 @@ -/// \file -/// \brief Types used by RakNet, most of which involve user code. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __NETWORK_TYPES_H -#define __NETWORK_TYPES_H - -#include "RakNetDefines.h" -#include "NativeTypes.h" -#include "RakNetTime.h" -#include "Export.h" -#include "SocketIncludes.h" -#include "WindowsIncludes.h" -#include "XBox360Includes.h" - - - - - -namespace RakNet { -/// Forward declarations -class RakPeerInterface; -class BitStream; -struct Packet; - -enum StartupResult -{ - RAKNET_STARTED, - RAKNET_ALREADY_STARTED, - INVALID_SOCKET_DESCRIPTORS, - INVALID_MAX_CONNECTIONS, - SOCKET_FAMILY_NOT_SUPPORTED, - SOCKET_PORT_ALREADY_IN_USE, - SOCKET_FAILED_TO_BIND, - SOCKET_FAILED_TEST_SEND, - PORT_CANNOT_BE_ZERO, - FAILED_TO_CREATE_NETWORK_THREAD, - STARTUP_OTHER_FAILURE, -}; - - -enum ConnectionAttemptResult -{ - CONNECTION_ATTEMPT_STARTED, - INVALID_PARAMETER, - CANNOT_RESOLVE_DOMAIN_NAME, - ALREADY_CONNECTED_TO_ENDPOINT, - CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS, - SECURITY_INITIALIZATION_FAILED -}; - -/// Returned from RakPeerInterface::GetConnectionState() -enum ConnectionState -{ - /// Connect() was called, but the process hasn't started yet - IS_PENDING, - /// Processing the connection attempt - IS_CONNECTING, - /// Is connected and able to communicate - IS_CONNECTED, - /// Was connected, but will disconnect as soon as the remaining messages are delivered - IS_DISCONNECTING, - /// A connection attempt failed and will be aborted - IS_SILENTLY_DISCONNECTING, - /// No longer connected - IS_DISCONNECTED, - /// Was never connected, or else was disconnected long enough ago that the entry has been discarded - IS_NOT_CONNECTED, -}; - -/// Given a number of bits, return how many bytes are needed to represent that. -#define BITS_TO_BYTES(x) (((x)+7)>>3) -#define BYTES_TO_BITS(x) ((x)<<3) - -/// \sa NetworkIDObject.h -typedef unsigned char UniqueIDType; -typedef unsigned short SystemIndex; -typedef unsigned char RPCIndex; -const int MAX_RPC_MAP_SIZE=((RPCIndex)-1)-1; -const int UNDEFINED_RPC_INDEX=((RPCIndex)-1); - -/// First byte of a network message -typedef unsigned char MessageID; - -typedef uint32_t BitSize_t; - -#if defined(_MSC_VER) && _MSC_VER > 0 -#define PRINTF_64_BIT_MODIFIER "I64" -#else -#define PRINTF_64_BIT_MODIFIER "ll" -#endif - -/// Used with the PublicKey structure -enum PublicKeyMode -{ - /// The connection is insecure. You can also just pass 0 for the pointer to PublicKey in RakPeerInterface::Connect() - PKM_INSECURE_CONNECTION, - - /// Accept whatever public key the server gives us. This is vulnerable to man in the middle, but does not require - /// distribution of the public key in advance of connecting. - PKM_ACCEPT_ANY_PUBLIC_KEY, - - /// Use a known remote server public key. PublicKey::remoteServerPublicKey must be non-zero. - /// This is the recommended mode for secure connections. - PKM_USE_KNOWN_PUBLIC_KEY, - - /// Use a known remote server public key AND provide a public key for the connecting client. - /// PublicKey::remoteServerPublicKey, myPublicKey and myPrivateKey must be all be non-zero. - /// The server must cooperate for this mode to work. - /// I recommend not using this mode except for server-to-server communication as it significantly increases the CPU requirements during connections for both sides. - /// Furthermore, when it is used, a connection password should be used as well to avoid DoS attacks. - PKM_USE_TWO_WAY_AUTHENTICATION -}; - -/// Passed to RakPeerInterface::Connect() -struct RAK_DLL_EXPORT PublicKey -{ - /// How to interpret the public key, see above - PublicKeyMode publicKeyMode; - - /// Pointer to a public key of length cat::EasyHandshake::PUBLIC_KEY_BYTES. See the Encryption sample. - char *remoteServerPublicKey; - - /// (Optional) Pointer to a public key of length cat::EasyHandshake::PUBLIC_KEY_BYTES - char *myPublicKey; - - /// (Optional) Pointer to a private key of length cat::EasyHandshake::PRIVATE_KEY_BYTES - char *myPrivateKey; -}; - -/// Describes the local socket to use for RakPeer::Startup -struct RAK_DLL_EXPORT SocketDescriptor -{ - SocketDescriptor(); - SocketDescriptor(unsigned short _port, const char *_hostAddress); - - /// The local port to bind to. Pass 0 to have the OS autoassign a port. - unsigned short port; - - /// The local network card address to bind to, such as "127.0.0.1". Pass an empty string to use INADDR_ANY. - char hostAddress[32]; - - /// IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - /// IPV6 is the newer internet protocol. Instead of addresses such as 94.198.81.195, you may have an address such as fe80::7c:31f7:fec4:27de%14. - /// Encoding takes 16 bytes instead of 4, so IPV6 is less efficient for bandwidth. - /// On the positive side, NAT Punchthrough is not needed and should not be used with IPV6 because there are enough addresses that routers do not need to create address mappings. - /// RakPeer::Startup() will fail if this IP version is not supported. - /// \pre RAKNET_SUPPORT_IPV6 must be set to 1 in RakNetDefines.h for AF_INET6 - short socketFamily; - - - - - - - - - - unsigned short remotePortRakNetWasStartedOn_PS3_PSP2; - - /// XBOX only: set IPPROTO_VDP if you want to use VDP. If enabled, this socket does not support broadcast to 255.255.255.255 - unsigned int extraSocketOptions; -}; - -extern bool NonNumericHostString( const char *host ); - -/// \brief Network address for a system -/// \details Corresponds to a network address
      -/// This is not necessarily a unique identifier. For example, if a system has both LAN and internet connections, the system may be identified by either one, depending on who is communicating
      -/// Therefore, you should not transmit the SystemAddress over the network and expect it to identify a system, or use it to connect to that system, except in the case where that system is not behind a NAT (such as with a dedciated server) -/// Use RakNetGUID for a unique per-instance of RakPeer to identify systems -struct RAK_DLL_EXPORT SystemAddress -{ - /// Constructors - SystemAddress(); - SystemAddress(const char *str); - SystemAddress(const char *str, unsigned short port); - - - - - - - - /// SystemAddress, with RAKNET_SUPPORT_IPV6 defined, holds both an sockaddr_in6 and a sockaddr_in - union// In6OrIn4 - { -#if RAKNET_SUPPORT_IPV6==1 - struct sockaddr_in6 addr6; -#endif - - struct sockaddr_in addr4; - } address; - - /// This is not used internally, but holds a copy of the port held in the address union, so for debugging it's easier to check what port is being held - unsigned short debugPort; - - /// \internal Return the size to write to a bitStream - static int size(void); - - /// Hash the system address - static unsigned long ToInteger( const SystemAddress &sa ); - - /// Return the IP version, either IPV4 or IPV6 - /// \return Either 4 or 6 - unsigned char GetIPVersion(void) const; - - /// \internal Returns either IPPROTO_IP or IPPROTO_IPV6 - /// \sa GetIPVersion - unsigned int GetIPPROTO(void) const; - - /// Call SetToLoopback(), with whatever IP version is currently held. Defaults to IPV4 - void SetToLoopback(void); - - /// Call SetToLoopback() with a specific IP version - /// \param[in] ipVersion Either 4 for IPV4 or 6 for IPV6 - void SetToLoopback(unsigned char ipVersion); - - /// \return If was set to 127.0.0.1 or ::1 - bool IsLoopback(void) const; - - // Return the systemAddress as a string in the format | - // Returns a static string - // NOT THREADSAFE - // portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f - const char *ToString(bool writePort=true, char portDelineator='|') const; - - // Return the systemAddress as a string in the format | - // dest must be large enough to hold the output - // portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f - // THREADSAFE - void ToString(bool writePort, char *dest, char portDelineator='|') const; - - /// Set the system address from a printable IP string, for example "192.0.2.1" or "2001:db8:63b3:1::3490" - /// You can write the port as well, using the portDelineator, for example "192.0.2.1|1234" - /// \param[in] str A printable IP string, for example "192.0.2.1" or "2001:db8:63b3:1::3490". Pass 0 for \a str to set to UNASSIGNED_SYSTEM_ADDRESS - /// \param[in] portDelineator if \a str contains a port, delineate the port with this character. portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f - /// \param[in] ipVersion Only used if str is a pre-defined address in the wrong format, such as 127.0.0.1 but you want ip version 6, so you can pass 6 here to do the conversion - /// \note The current port is unchanged if a port is not specified in \a str - /// \return True on success, false on ipVersion does not match type of passed string - bool FromString(const char *str, char portDelineator='|', int ipVersion=0); - - /// Same as FromString(), but you explicitly set a port at the same time - bool FromStringExplicitPort(const char *str, unsigned short port, int ipVersion=0); - - /// Copy the port from another SystemAddress structure - void CopyPort( const SystemAddress& right ); - - /// Returns if two system addresses have the same IP (port is not checked) - bool EqualsExcludingPort( const SystemAddress& right ) const; - - /// Returns the port in host order (this is what you normally use) - unsigned short GetPort(void) const; - - /// \internal Returns the port in network order - unsigned short GetPortNetworkOrder(void) const; - - /// Sets the port. The port value should be in host order (this is what you normally use) - void SetPort(unsigned short s); - - /// \internal Sets the port. The port value should already be in network order. - void SetPortNetworkOrder(unsigned short s); - - /// Old version, for crap platforms that don't support newer socket functions - void SetBinaryAddress(const char *str, char portDelineator=':'); - /// Old version, for crap platforms that don't support newer socket functions - void ToString_Old(bool writePort, char *dest, char portDelineator=':') const; - - /// \internal sockaddr_in6 requires extra data beyond just the IP and port. Copy that extra data from an existing SystemAddress that already has it - void FixForIPVersion(const SystemAddress &boundAddressToSocket); - - SystemAddress& operator = ( const SystemAddress& input ); - bool operator==( const SystemAddress& right ) const; - bool operator!=( const SystemAddress& right ) const; - bool operator > ( const SystemAddress& right ) const; - bool operator < ( const SystemAddress& right ) const; - - /// \internal Used internally for fast lookup. Optional (use -1 to do regular lookup). Don't transmit this. - SystemIndex systemIndex; - - private: - -#if RAKNET_SUPPORT_IPV6==1 - void ToString_New(bool writePort, char *dest, char portDelineator) const; -#endif -}; - -/// Uniquely identifies an instance of RakPeer. Use RakPeer::GetGuidFromSystemAddress() and RakPeer::GetSystemAddressFromGuid() to go between SystemAddress and RakNetGUID -/// Use RakPeer::GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) to get your own GUID -struct RAK_DLL_EXPORT RakNetGUID -{ - RakNetGUID(); - explicit RakNetGUID(uint64_t _g) {g=_g; systemIndex=(SystemIndex)-1;} -// uint32_t g[6]; - uint64_t g; - - // Return the GUID as a string - // Returns a static string - // NOT THREADSAFE - const char *ToString(void) const; - - // Return the GUID as a string - // dest must be large enough to hold the output - // THREADSAFE - void ToString(char *dest) const; - - bool FromString(const char *source); - - static unsigned long ToUint32( const RakNetGUID &g ); - - RakNetGUID& operator = ( const RakNetGUID& input ) - { - g=input.g; - systemIndex=input.systemIndex; - return *this; - } - - // Used internally for fast lookup. Optional (use -1 to do regular lookup). Don't transmit this. - SystemIndex systemIndex; - static int size() {return (int) sizeof(uint64_t);} - - bool operator==( const RakNetGUID& right ) const; - bool operator!=( const RakNetGUID& right ) const; - bool operator > ( const RakNetGUID& right ) const; - bool operator < ( const RakNetGUID& right ) const; -}; - -/// Index of an invalid SystemAddress -//const SystemAddress UNASSIGNED_SYSTEM_ADDRESS = -//{ -// 0xFFFFFFFF, 0xFFFF -//}; -#ifndef SWIG -const SystemAddress UNASSIGNED_SYSTEM_ADDRESS; -const RakNetGUID UNASSIGNED_RAKNET_GUID((uint64_t)-1); -#endif -//{ -// {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF} -// 0xFFFFFFFFFFFFFFFF -//}; - - -struct RAK_DLL_EXPORT AddressOrGUID -{ - RakNetGUID rakNetGuid; - SystemAddress systemAddress; - - SystemIndex GetSystemIndex(void) const {if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) return rakNetGuid.systemIndex; else return systemAddress.systemIndex;} - bool IsUndefined(void) const {return rakNetGuid==UNASSIGNED_RAKNET_GUID && systemAddress==UNASSIGNED_SYSTEM_ADDRESS;} - void SetUndefined(void) {rakNetGuid=UNASSIGNED_RAKNET_GUID; systemAddress=UNASSIGNED_SYSTEM_ADDRESS;} - static unsigned long ToInteger( const AddressOrGUID &aog ); - const char *ToString(bool writePort=true) const; - void ToString(bool writePort, char *dest) const; - - AddressOrGUID() {} - AddressOrGUID( const AddressOrGUID& input ) - { - rakNetGuid=input.rakNetGuid; - systemAddress=input.systemAddress; - } - AddressOrGUID( const SystemAddress& input ) - { - rakNetGuid=UNASSIGNED_RAKNET_GUID; - systemAddress=input; - } - AddressOrGUID( Packet *packet ); - AddressOrGUID( const RakNetGUID& input ) - { - rakNetGuid=input; - systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - } - AddressOrGUID& operator = ( const AddressOrGUID& input ) - { - rakNetGuid=input.rakNetGuid; - systemAddress=input.systemAddress; - return *this; - } - - AddressOrGUID& operator = ( const SystemAddress& input ) - { - rakNetGuid=UNASSIGNED_RAKNET_GUID; - systemAddress=input; - return *this; - } - - AddressOrGUID& operator = ( const RakNetGUID& input ) - { - rakNetGuid=input; - systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - return *this; - } - - inline bool operator==( const AddressOrGUID& right ) const {return (rakNetGuid!=UNASSIGNED_RAKNET_GUID && rakNetGuid==right.rakNetGuid) || (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && systemAddress==right.systemAddress);} -}; - -typedef uint64_t NetworkID; - -/// This represents a user message from another system. -struct Packet -{ - /// The system that send this packet. - SystemAddress systemAddress; - - /// A unique identifier for the system that sent this packet, regardless of IP address (internal / external / remote system) - /// Only valid once a connection has been established (ID_CONNECTION_REQUEST_ACCEPTED, or ID_NEW_INCOMING_CONNECTION) - /// Until that time, will be UNASSIGNED_RAKNET_GUID - RakNetGUID guid; - - /// The length of the data in bytes - unsigned int length; - - /// The length of the data in bits - BitSize_t bitSize; - - /// The data from the sender - unsigned char* data; - - /// @internal - /// Indicates whether to delete the data, or to simply delete the packet. - bool deleteData; - - /// @internal - /// If true, this message is meant for the user, not for the plugins, so do not process it through plugins - bool wasGeneratedLocally; -}; - -/// Index of an unassigned player -const SystemIndex UNASSIGNED_PLAYER_INDEX = 65535; - -/// Unassigned object ID -const NetworkID UNASSIGNED_NETWORK_ID = (uint64_t) -1; - -const int PING_TIMES_ARRAY_SIZE = 5; - -struct RAK_DLL_EXPORT uint24_t -{ - uint32_t val; - - uint24_t() {} - inline operator uint32_t() { return val; } - inline operator uint32_t() const { return val; } - - inline uint24_t(const uint24_t& a) {val=a.val;} - inline uint24_t operator++() {++val; val&=0x00FFFFFF; return *this;} - inline uint24_t operator--() {--val; val&=0x00FFFFFF; return *this;} - inline uint24_t operator++(int) {uint24_t temp(val); ++val; val&=0x00FFFFFF; return temp;} - inline uint24_t operator--(int) {uint24_t temp(val); --val; val&=0x00FFFFFF; return temp;} - inline uint24_t operator&(const uint24_t& a) {return uint24_t(val&a.val);} - inline uint24_t& operator=(const uint24_t& a) { val=a.val; return *this; } - inline uint24_t& operator+=(const uint24_t& a) { val+=a.val; val&=0x00FFFFFF; return *this; } - inline uint24_t& operator-=(const uint24_t& a) { val-=a.val; val&=0x00FFFFFF; return *this; } - inline bool operator==( const uint24_t& right ) const {return val==right.val;} - inline bool operator!=( const uint24_t& right ) const {return val!=right.val;} - inline bool operator > ( const uint24_t& right ) const {return val>right.val;} - inline bool operator < ( const uint24_t& right ) const {return val ( const uint32_t& right ) const {return val>(right&0x00FFFFFF);} - inline bool operator < ( const uint32_t& right ) const {return val<(right&0x00FFFFFF);} - inline const uint24_t operator+( const uint32_t &other ) const { return uint24_t(val+other); } - inline const uint24_t operator-( const uint32_t &other ) const { return uint24_t(val-other); } - inline const uint24_t operator/( const uint32_t &other ) const { return uint24_t(val/other); } - inline const uint24_t operator*( const uint32_t &other ) const { return uint24_t(val*other); } -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief Types used by RakNet, most of which involve user code. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __NETWORK_TYPES_H +#define __NETWORK_TYPES_H + +#include "RakNetDefines.h" +#include "NativeTypes.h" +#include "RakNetTime.h" +#include "Export.h" +#include "SocketIncludes.h" +#include "WindowsIncludes.h" +#include "XBox360Includes.h" + + + + + +namespace RakNet { +/// Forward declarations +class RakPeerInterface; +class BitStream; +struct Packet; + +enum StartupResult +{ + RAKNET_STARTED, + RAKNET_ALREADY_STARTED, + INVALID_SOCKET_DESCRIPTORS, + INVALID_MAX_CONNECTIONS, + SOCKET_FAMILY_NOT_SUPPORTED, + SOCKET_PORT_ALREADY_IN_USE, + SOCKET_FAILED_TO_BIND, + SOCKET_FAILED_TEST_SEND, + PORT_CANNOT_BE_ZERO, + FAILED_TO_CREATE_NETWORK_THREAD, + STARTUP_OTHER_FAILURE, +}; + + +enum ConnectionAttemptResult +{ + CONNECTION_ATTEMPT_STARTED, + INVALID_PARAMETER, + CANNOT_RESOLVE_DOMAIN_NAME, + ALREADY_CONNECTED_TO_ENDPOINT, + CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS, + SECURITY_INITIALIZATION_FAILED +}; + +/// Returned from RakPeerInterface::GetConnectionState() +enum ConnectionState +{ + /// Connect() was called, but the process hasn't started yet + IS_PENDING, + /// Processing the connection attempt + IS_CONNECTING, + /// Is connected and able to communicate + IS_CONNECTED, + /// Was connected, but will disconnect as soon as the remaining messages are delivered + IS_DISCONNECTING, + /// A connection attempt failed and will be aborted + IS_SILENTLY_DISCONNECTING, + /// No longer connected + IS_DISCONNECTED, + /// Was never connected, or else was disconnected long enough ago that the entry has been discarded + IS_NOT_CONNECTED, +}; + +/// Given a number of bits, return how many bytes are needed to represent that. +#define BITS_TO_BYTES(x) (((x)+7)>>3) +#define BYTES_TO_BITS(x) ((x)<<3) + +/// \sa NetworkIDObject.h +typedef unsigned char UniqueIDType; +typedef unsigned short SystemIndex; +typedef unsigned char RPCIndex; +const int MAX_RPC_MAP_SIZE=((RPCIndex)-1)-1; +const int UNDEFINED_RPC_INDEX=((RPCIndex)-1); + +/// First byte of a network message +typedef unsigned char MessageID; + +typedef uint32_t BitSize_t; + +#if defined(_MSC_VER) && _MSC_VER > 0 +#define PRINTF_64_BIT_MODIFIER "I64" +#else +#define PRINTF_64_BIT_MODIFIER "ll" +#endif + +/// Used with the PublicKey structure +enum PublicKeyMode +{ + /// The connection is insecure. You can also just pass 0 for the pointer to PublicKey in RakPeerInterface::Connect() + PKM_INSECURE_CONNECTION, + + /// Accept whatever public key the server gives us. This is vulnerable to man in the middle, but does not require + /// distribution of the public key in advance of connecting. + PKM_ACCEPT_ANY_PUBLIC_KEY, + + /// Use a known remote server public key. PublicKey::remoteServerPublicKey must be non-zero. + /// This is the recommended mode for secure connections. + PKM_USE_KNOWN_PUBLIC_KEY, + + /// Use a known remote server public key AND provide a public key for the connecting client. + /// PublicKey::remoteServerPublicKey, myPublicKey and myPrivateKey must be all be non-zero. + /// The server must cooperate for this mode to work. + /// I recommend not using this mode except for server-to-server communication as it significantly increases the CPU requirements during connections for both sides. + /// Furthermore, when it is used, a connection password should be used as well to avoid DoS attacks. + PKM_USE_TWO_WAY_AUTHENTICATION +}; + +/// Passed to RakPeerInterface::Connect() +struct RAK_DLL_EXPORT PublicKey +{ + /// How to interpret the public key, see above + PublicKeyMode publicKeyMode; + + /// Pointer to a public key of length cat::EasyHandshake::PUBLIC_KEY_BYTES. See the Encryption sample. + char *remoteServerPublicKey; + + /// (Optional) Pointer to a public key of length cat::EasyHandshake::PUBLIC_KEY_BYTES + char *myPublicKey; + + /// (Optional) Pointer to a private key of length cat::EasyHandshake::PRIVATE_KEY_BYTES + char *myPrivateKey; +}; + +/// Describes the local socket to use for RakPeer::Startup +struct RAK_DLL_EXPORT SocketDescriptor +{ + SocketDescriptor(); + SocketDescriptor(unsigned short _port, const char *_hostAddress); + + /// The local port to bind to. Pass 0 to have the OS autoassign a port. + unsigned short port; + + /// The local network card address to bind to, such as "127.0.0.1". Pass an empty string to use INADDR_ANY. + char hostAddress[32]; + + /// IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + /// IPV6 is the newer internet protocol. Instead of addresses such as 94.198.81.195, you may have an address such as fe80::7c:31f7:fec4:27de%14. + /// Encoding takes 16 bytes instead of 4, so IPV6 is less efficient for bandwidth. + /// On the positive side, NAT Punchthrough is not needed and should not be used with IPV6 because there are enough addresses that routers do not need to create address mappings. + /// RakPeer::Startup() will fail if this IP version is not supported. + /// \pre RAKNET_SUPPORT_IPV6 must be set to 1 in RakNetDefines.h for AF_INET6 + short socketFamily; + + + + + + + + + + unsigned short remotePortRakNetWasStartedOn_PS3_PSP2; + + /// XBOX only: set IPPROTO_VDP if you want to use VDP. If enabled, this socket does not support broadcast to 255.255.255.255 + unsigned int extraSocketOptions; +}; + +extern bool NonNumericHostString( const char *host ); + +/// \brief Network address for a system +/// \details Corresponds to a network address
      +/// This is not necessarily a unique identifier. For example, if a system has both LAN and internet connections, the system may be identified by either one, depending on who is communicating
      +/// Therefore, you should not transmit the SystemAddress over the network and expect it to identify a system, or use it to connect to that system, except in the case where that system is not behind a NAT (such as with a dedciated server) +/// Use RakNetGUID for a unique per-instance of RakPeer to identify systems +struct RAK_DLL_EXPORT SystemAddress +{ + /// Constructors + SystemAddress(); + SystemAddress(const char *str); + SystemAddress(const char *str, unsigned short port); + + + + + + + + /// SystemAddress, with RAKNET_SUPPORT_IPV6 defined, holds both an sockaddr_in6 and a sockaddr_in + union// In6OrIn4 + { +#if RAKNET_SUPPORT_IPV6==1 + struct sockaddr_in6 addr6; +#endif + + struct sockaddr_in addr4; + } address; + + /// This is not used internally, but holds a copy of the port held in the address union, so for debugging it's easier to check what port is being held + unsigned short debugPort; + + /// \internal Return the size to write to a bitStream + static int size(void); + + /// Hash the system address + static unsigned long ToInteger( const SystemAddress &sa ); + + /// Return the IP version, either IPV4 or IPV6 + /// \return Either 4 or 6 + unsigned char GetIPVersion(void) const; + + /// \internal Returns either IPPROTO_IP or IPPROTO_IPV6 + /// \sa GetIPVersion + unsigned int GetIPPROTO(void) const; + + /// Call SetToLoopback(), with whatever IP version is currently held. Defaults to IPV4 + void SetToLoopback(void); + + /// Call SetToLoopback() with a specific IP version + /// \param[in] ipVersion Either 4 for IPV4 or 6 for IPV6 + void SetToLoopback(unsigned char ipVersion); + + /// \return If was set to 127.0.0.1 or ::1 + bool IsLoopback(void) const; + + // Return the systemAddress as a string in the format | + // Returns a static string + // NOT THREADSAFE + // portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f + const char *ToString(bool writePort=true, char portDelineator='|') const; + + // Return the systemAddress as a string in the format | + // dest must be large enough to hold the output + // portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f + // THREADSAFE + void ToString(bool writePort, char *dest, char portDelineator='|') const; + + /// Set the system address from a printable IP string, for example "192.0.2.1" or "2001:db8:63b3:1::3490" + /// You can write the port as well, using the portDelineator, for example "192.0.2.1|1234" + /// \param[in] str A printable IP string, for example "192.0.2.1" or "2001:db8:63b3:1::3490". Pass 0 for \a str to set to UNASSIGNED_SYSTEM_ADDRESS + /// \param[in] portDelineator if \a str contains a port, delineate the port with this character. portDelineator should not be '.', ':', '%', '-', '/', a number, or a-f + /// \param[in] ipVersion Only used if str is a pre-defined address in the wrong format, such as 127.0.0.1 but you want ip version 6, so you can pass 6 here to do the conversion + /// \note The current port is unchanged if a port is not specified in \a str + /// \return True on success, false on ipVersion does not match type of passed string + bool FromString(const char *str, char portDelineator='|', int ipVersion=0); + + /// Same as FromString(), but you explicitly set a port at the same time + bool FromStringExplicitPort(const char *str, unsigned short port, int ipVersion=0); + + /// Copy the port from another SystemAddress structure + void CopyPort( const SystemAddress& right ); + + /// Returns if two system addresses have the same IP (port is not checked) + bool EqualsExcludingPort( const SystemAddress& right ) const; + + /// Returns the port in host order (this is what you normally use) + unsigned short GetPort(void) const; + + /// \internal Returns the port in network order + unsigned short GetPortNetworkOrder(void) const; + + /// Sets the port. The port value should be in host order (this is what you normally use) + void SetPort(unsigned short s); + + /// \internal Sets the port. The port value should already be in network order. + void SetPortNetworkOrder(unsigned short s); + + /// Old version, for crap platforms that don't support newer socket functions + void SetBinaryAddress(const char *str, char portDelineator=':'); + /// Old version, for crap platforms that don't support newer socket functions + void ToString_Old(bool writePort, char *dest, char portDelineator=':') const; + + /// \internal sockaddr_in6 requires extra data beyond just the IP and port. Copy that extra data from an existing SystemAddress that already has it + void FixForIPVersion(const SystemAddress &boundAddressToSocket); + + SystemAddress& operator = ( const SystemAddress& input ); + bool operator==( const SystemAddress& right ) const; + bool operator!=( const SystemAddress& right ) const; + bool operator > ( const SystemAddress& right ) const; + bool operator < ( const SystemAddress& right ) const; + + /// \internal Used internally for fast lookup. Optional (use -1 to do regular lookup). Don't transmit this. + SystemIndex systemIndex; + + private: + +#if RAKNET_SUPPORT_IPV6==1 + void ToString_New(bool writePort, char *dest, char portDelineator) const; +#endif +}; + +/// Uniquely identifies an instance of RakPeer. Use RakPeer::GetGuidFromSystemAddress() and RakPeer::GetSystemAddressFromGuid() to go between SystemAddress and RakNetGUID +/// Use RakPeer::GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) to get your own GUID +struct RAK_DLL_EXPORT RakNetGUID +{ + RakNetGUID(); + explicit RakNetGUID(uint64_t _g) {g=_g; systemIndex=(SystemIndex)-1;} +// uint32_t g[6]; + uint64_t g; + + // Return the GUID as a string + // Returns a static string + // NOT THREADSAFE + const char *ToString(void) const; + + // Return the GUID as a string + // dest must be large enough to hold the output + // THREADSAFE + void ToString(char *dest) const; + + bool FromString(const char *source); + + static unsigned long ToUint32( const RakNetGUID &g ); + + RakNetGUID& operator = ( const RakNetGUID& input ) + { + g=input.g; + systemIndex=input.systemIndex; + return *this; + } + + // Used internally for fast lookup. Optional (use -1 to do regular lookup). Don't transmit this. + SystemIndex systemIndex; + static int size() {return (int) sizeof(uint64_t);} + + bool operator==( const RakNetGUID& right ) const; + bool operator!=( const RakNetGUID& right ) const; + bool operator > ( const RakNetGUID& right ) const; + bool operator < ( const RakNetGUID& right ) const; +}; + +/// Index of an invalid SystemAddress +//const SystemAddress UNASSIGNED_SYSTEM_ADDRESS = +//{ +// 0xFFFFFFFF, 0xFFFF +//}; +#ifndef SWIG +const SystemAddress UNASSIGNED_SYSTEM_ADDRESS; +const RakNetGUID UNASSIGNED_RAKNET_GUID((uint64_t)-1); +#endif +//{ +// {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF} +// 0xFFFFFFFFFFFFFFFF +//}; + + +struct RAK_DLL_EXPORT AddressOrGUID +{ + RakNetGUID rakNetGuid; + SystemAddress systemAddress; + + SystemIndex GetSystemIndex(void) const {if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) return rakNetGuid.systemIndex; else return systemAddress.systemIndex;} + bool IsUndefined(void) const {return rakNetGuid==UNASSIGNED_RAKNET_GUID && systemAddress==UNASSIGNED_SYSTEM_ADDRESS;} + void SetUndefined(void) {rakNetGuid=UNASSIGNED_RAKNET_GUID; systemAddress=UNASSIGNED_SYSTEM_ADDRESS;} + static unsigned long ToInteger( const AddressOrGUID &aog ); + const char *ToString(bool writePort=true) const; + void ToString(bool writePort, char *dest) const; + + AddressOrGUID() {} + AddressOrGUID( const AddressOrGUID& input ) + { + rakNetGuid=input.rakNetGuid; + systemAddress=input.systemAddress; + } + AddressOrGUID( const SystemAddress& input ) + { + rakNetGuid=UNASSIGNED_RAKNET_GUID; + systemAddress=input; + } + AddressOrGUID( Packet *packet ); + AddressOrGUID( const RakNetGUID& input ) + { + rakNetGuid=input; + systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + } + AddressOrGUID& operator = ( const AddressOrGUID& input ) + { + rakNetGuid=input.rakNetGuid; + systemAddress=input.systemAddress; + return *this; + } + + AddressOrGUID& operator = ( const SystemAddress& input ) + { + rakNetGuid=UNASSIGNED_RAKNET_GUID; + systemAddress=input; + return *this; + } + + AddressOrGUID& operator = ( const RakNetGUID& input ) + { + rakNetGuid=input; + systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + return *this; + } + + inline bool operator==( const AddressOrGUID& right ) const {return (rakNetGuid!=UNASSIGNED_RAKNET_GUID && rakNetGuid==right.rakNetGuid) || (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && systemAddress==right.systemAddress);} +}; + +typedef uint64_t NetworkID; + +/// This represents a user message from another system. +struct Packet +{ + /// The system that send this packet. + SystemAddress systemAddress; + + /// A unique identifier for the system that sent this packet, regardless of IP address (internal / external / remote system) + /// Only valid once a connection has been established (ID_CONNECTION_REQUEST_ACCEPTED, or ID_NEW_INCOMING_CONNECTION) + /// Until that time, will be UNASSIGNED_RAKNET_GUID + RakNetGUID guid; + + /// The length of the data in bytes + unsigned int length; + + /// The length of the data in bits + BitSize_t bitSize; + + /// The data from the sender + unsigned char* data; + + /// @internal + /// Indicates whether to delete the data, or to simply delete the packet. + bool deleteData; + + /// @internal + /// If true, this message is meant for the user, not for the plugins, so do not process it through plugins + bool wasGeneratedLocally; +}; + +/// Index of an unassigned player +const SystemIndex UNASSIGNED_PLAYER_INDEX = 65535; + +/// Unassigned object ID +const NetworkID UNASSIGNED_NETWORK_ID = (uint64_t) -1; + +const int PING_TIMES_ARRAY_SIZE = 5; + +struct RAK_DLL_EXPORT uint24_t +{ + uint32_t val; + + uint24_t() {} + inline operator uint32_t() { return val; } + inline operator uint32_t() const { return val; } + + inline uint24_t(const uint24_t& a) {val=a.val;} + inline uint24_t operator++() {++val; val&=0x00FFFFFF; return *this;} + inline uint24_t operator--() {--val; val&=0x00FFFFFF; return *this;} + inline uint24_t operator++(int) {uint24_t temp(val); ++val; val&=0x00FFFFFF; return temp;} + inline uint24_t operator--(int) {uint24_t temp(val); --val; val&=0x00FFFFFF; return temp;} + inline uint24_t operator&(const uint24_t& a) {return uint24_t(val&a.val);} + inline uint24_t& operator=(const uint24_t& a) { val=a.val; return *this; } + inline uint24_t& operator+=(const uint24_t& a) { val+=a.val; val&=0x00FFFFFF; return *this; } + inline uint24_t& operator-=(const uint24_t& a) { val-=a.val; val&=0x00FFFFFF; return *this; } + inline bool operator==( const uint24_t& right ) const {return val==right.val;} + inline bool operator!=( const uint24_t& right ) const {return val!=right.val;} + inline bool operator > ( const uint24_t& right ) const {return val>right.val;} + inline bool operator < ( const uint24_t& right ) const {return val ( const uint32_t& right ) const {return val>(right&0x00FFFFFF);} + inline bool operator < ( const uint32_t& right ) const {return val<(right&0x00FFFFFF);} + inline const uint24_t operator+( const uint32_t &other ) const { return uint24_t(val+other); } + inline const uint24_t operator-( const uint32_t &other ) const { return uint24_t(val-other); } + inline const uint24_t operator/( const uint32_t &other ) const { return uint24_t(val/other); } + inline const uint24_t operator*( const uint32_t &other ) const { return uint24_t(val*other); } +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/RakNetVersion.h b/src/raknet/RakNetVersion.h index 4e61675..5ef88c8 100755 --- a/src/raknet/RakNetVersion.h +++ b/src/raknet/RakNetVersion.h @@ -1,8 +1,8 @@ -#define RAKNET_VERSION "4.036" -#define RAKNET_VERSION_NUMBER 4.036 - -#define RAKNET_DATE "2/1/2012" - -// What compatible protocol version RakNet is using. When this value changes, it indicates this version of RakNet cannot connection to an older version. -// ID_INCOMPATIBLE_PROTOCOL_VERSION will be returned on connection attempt in this case -#define RAKNET_PROTOCOL_VERSION 5 +#define RAKNET_VERSION "4.036" +#define RAKNET_VERSION_NUMBER 4.036 + +#define RAKNET_DATE "2/1/2012" + +// What compatible protocol version RakNet is using. When this value changes, it indicates this version of RakNet cannot connection to an older version. +// ID_INCOMPATIBLE_PROTOCOL_VERSION will be returned on connection attempt in this case +#define RAKNET_PROTOCOL_VERSION 5 diff --git a/src/raknet/RakNet_vc8.vcproj b/src/raknet/RakNet_vc8.vcproj index 694c818..a75edee 100755 --- a/src/raknet/RakNet_vc8.vcproj +++ b/src/raknet/RakNet_vc8.vcproj @@ -1,849 +1,849 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/raknet/RakNet_vc9.vcproj b/src/raknet/RakNet_vc9.vcproj index 694c818..a75edee 100755 --- a/src/raknet/RakNet_vc9.vcproj +++ b/src/raknet/RakNet_vc9.vcproj @@ -1,849 +1,849 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/raknet/RakPeer.cpp b/src/raknet/RakPeer.cpp index a24a2b5..7ba9ec6 100755 --- a/src/raknet/RakPeer.cpp +++ b/src/raknet/RakPeer.cpp @@ -1,5964 +1,5964 @@ -// \file -// -// This file is part of RakNet Copyright 2003 Jenkins Software LLC -// -// Usage of RakNet is subject to the appropriate license agreement. - - -#define CAT_NEUTER_EXPORT /* Neuter dllimport for libcat */ - -#include "RakNetDefines.h" -#include "RakPeer.h" -#include "RakNetTypes.h" - -#ifdef _WIN32 - -#else -#include -#endif - -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - -#include -#include // toupper -#include -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "DS_HuffmanEncodingTree.h" -#include "Rand.h" -#include "PluginInterface2.h" -#include "StringCompressor.h" -#include "StringTable.h" -#include "NetworkIDObject.h" -#include "RakNetTypes.h" -#include "SHA1.h" -#include "RakSleep.h" -#include "RakAssert.h" -#include "RakNetVersion.h" -#include "NetworkIDManager.h" -#include "gettimeofday.h" -#include "SignaledEvent.h" -#include "SuperFastHash.h" -#include "RakAlloca.h" -#include "WSAStartupSingleton.h" - -#ifdef USE_THREADED_SEND -#include "SendToThread.h" -#endif - -#ifdef CAT_AUDIT -#define CAT_AUDIT_PRINTF(...) printf(__VA_ARGS__) -#else -#define CAT_AUDIT_PRINTF(...) -#endif - -namespace RakNet -{ -RAK_THREAD_DECLARATION(UpdateNetworkLoop); -RAK_THREAD_DECLARATION(RecvFromLoop); -RAK_THREAD_DECLARATION(UDTConnect); -} -#define REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE 8 - -#if !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) -#include // malloc -#endif - - - -#if defined(_WIN32) -// -#else -/* -#include // Console 2 -#include -extern bool _extern_Console2LoadModules(void); -extern int _extern_Console2GetConnectionStatus(void); -extern int _extern_Console2GetLobbyStatus(void); -//extern bool Console2StartupFluff(unsigned int *); -extern void Console2ShutdownFluff(void); -//extern unsigned int Console2ActivateConnection(unsigned int, void *); -//extern bool Console2BlockOnEstablished(void); -extern void Console2GetIPAndPort(unsigned int, char *, unsigned short *, unsigned int ); -//extern void Console2DeactivateConnection(unsigned int, unsigned int); -*/ -#endif - - -static const int NUM_MTU_SIZES=3; - - - -static const int mtuSizes[NUM_MTU_SIZES]={MAXIMUM_MTU_SIZE, 1200, 576}; - - -// Note to self - if I change this it might affect RECIPIENT_OFFLINE_MESSAGE_INTERVAL in Natpunchthrough.cpp -//static const int MAX_OPEN_CONNECTION_REQUESTS=8; -//static const int TIME_BETWEEN_OPEN_CONNECTION_REQUESTS=500; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -static RakNetRandom rnr; - -struct RakPeerAndIndex -{ - SOCKET s; - unsigned short remotePortRakNetWasStartedOn_PS3; - RakPeer *rakPeer; - unsigned int extraSocketOptions; -}; - -static const unsigned int MAX_OFFLINE_DATA_LENGTH=400; // I set this because I limit ID_CONNECTION_REQUEST to 512 bytes, and the password is appended to that packet. - -// Used to distinguish between offline messages with data, and messages from the reliability layer -// Should be different than any message that could result from messages from the reliability layer -#if !defined(__GNUC__) -#pragma warning(disable:4309) // 'initializing' : truncation of constant value -#endif -// Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false -static const unsigned char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78}; - -struct PacketFollowedByData -{ - Packet p; - unsigned char data[1]; -}; - -Packet *RakPeer::AllocPacket(unsigned dataSize, const char *file, unsigned int line) -{ - // Crashes when dataSize is 4 bytes - not sure why -// unsigned char *data = (unsigned char *) rakMalloc_Ex(sizeof(PacketFollowedByData)+dataSize, file, line); -// Packet *p = &((PacketFollowedByData *)data)->p; -// p->data=((PacketFollowedByData *)data)->data; -// p->length=dataSize; -// p->bitSize=BYTES_TO_BITS(dataSize); -// p->deleteData=false; -// p->guid=UNASSIGNED_RAKNET_GUID; -// return p; - - RakNet::Packet *p; - packetAllocationPoolMutex.Lock(); - p = packetAllocationPool.Allocate(file,line); - packetAllocationPoolMutex.Unlock(); - p = new ((void*)p) Packet; - p->data=(unsigned char*) rakMalloc_Ex(dataSize,file,line); - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=true; - p->guid=UNASSIGNED_RAKNET_GUID; - p->wasGeneratedLocally=false; - return p; -} - -Packet *RakPeer::AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line) -{ - // Packet *p = (Packet *)rakMalloc_Ex(sizeof(Packet), file, line); - RakNet::Packet *p; - packetAllocationPoolMutex.Lock(); - p = packetAllocationPool.Allocate(file,line); - packetAllocationPoolMutex.Unlock(); - p = new ((void*)p) Packet; - RakAssert(p); - p->data=data; - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=true; - p->guid=UNASSIGNED_RAKNET_GUID; - p->wasGeneratedLocally=false; - return p; -} - -STATIC_FACTORY_DEFINITIONS(RakPeerInterface,RakPeer) - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Constructor -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RakPeer() -{ - errorState = 0; -#if LIBCAT_SECURITY==1 - // Encryption and security - CAT_AUDIT_PRINTF("AUDIT: Initializing RakPeer security flags: using_security = false, server_handshake = null, cookie_jar = null\n"); - _using_security = false; - _server_handshake = 0; - _cookie_jar = 0; -#endif - - StringCompressor::AddReference(); - RakNet::StringTable::AddReference(); - WSAStartupSingleton::AddRef(); - - defaultMTUSize = mtuSizes[NUM_MTU_SIZES-1]; - trackFrequencyTable = false; - maximumIncomingConnections = 0; - maximumNumberOfPeers = 0; - //remoteSystemListSize=0; - remoteSystemList = 0; - activeSystemList = 0; - activeSystemListSize=0; - remoteSystemLookup=0; - bytesSentPerSecond = bytesReceivedPerSecond = 0; - endThreads = true; - isMainLoopThreadActive = false; - - - - - - // isRecvfromThreadActive=false; -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 - occasionalPing = true; -#else - occasionalPing = false; -#endif - allowInternalRouting=false; - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - ipList[i]=UNASSIGNED_SYSTEM_ADDRESS; - allowConnectionResponseIPMigration = false; - //incomingPasswordLength=outgoingPasswordLength=0; - incomingPasswordLength=0; - splitMessageProgressInterval=0; - //unreliableTimeout=0; - unreliableTimeout=1000; - maxOutgoingBPS=0; - firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; - myGuid=UNASSIGNED_RAKNET_GUID; - userUpdateThreadPtr=0; - userUpdateThreadData=0; - -#ifdef _DEBUG - // Wait longer to disconnect in debug so I don't get disconnected while tracing - defaultTimeoutTime=30000; -#else - defaultTimeoutTime=10000; -#endif - -#ifdef _DEBUG - _packetloss=0.0; - _minExtraPing=0; - _extraPingVariance=0; -#endif - - bufferedCommands.SetPageSize(sizeof(BufferedCommandStruct)*16); - socketQueryOutput.SetPageSize(sizeof(SocketQueryOutput)*8); - bufferedPackets.SetPageSize(sizeof(RecvFromStruct)*BUFFERED_PACKETS_PAGE_SIZE); // This is big, keep the size down - - packetAllocationPoolMutex.Lock(); - packetAllocationPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); - packetAllocationPoolMutex.Unlock(); - - remoteSystemIndexPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GenerateGUID(); - - quitAndDataEvents.InitEvent(); - limitConnectionFrequencyFromTheSameIP=false; - ResetSendReceipt(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Destructor -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::~RakPeer() -{ - Shutdown( 0, 0 ); - - // Free the ban list. - ClearBanList(); - - StringCompressor::RemoveReference(); - RakNet::StringTable::RemoveReference(); - WSAStartupSingleton::Deref(); - - quitAndDataEvents.CloseEvent(); - -#if LIBCAT_SECURITY==1 - // Encryption and security - CAT_AUDIT_PRINTF("AUDIT: Deleting RakPeer security objects, handshake = %x, cookie jar = %x\n", _server_handshake, _cookie_jar); - if (_server_handshake) RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - if (_cookie_jar) RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); -#endif - - - - - - - - - - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// \brief Starts the network threads, opens the listen port. -// You must call this before calling Connect(). -// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). -// \note Call SetMaximumIncomingConnections if you want to accept incoming connections -// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections -// \param[in] localPort The port to listen for connections on. -// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle. With new congestion control, the best results will be obtained by passing 10. -// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass &socketDescriptor, 1SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); -// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. -// \return False on failure (can't create socket or thread), true on success. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -StartupResult RakPeer::Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority ) -{ - errorState = 0; - - if (IsActive()) - return RAKNET_ALREADY_STARTED; - - if (threadPriority==-99999) - { - - -#if defined(_WIN32) - threadPriority=0; - - -#else - threadPriority=1000; -#endif - } - - - // Fill out ipList structure - unsigned int i; - - SocketLayer::GetMyIP( ipList ); -// for (i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) -// { -// if (ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS) -// { -// char str[128]; -// ipList[i].ToString(false,str); -// printf("%s\n",str); -// } -// } - - - if (myGuid==UNASSIGNED_RAKNET_GUID) - { - rnr.SeedMT( GenerateSeedFromGuid() ); - } - - RakPeerAndIndex rpai[32]; - RakAssert(socketDescriptorCount<32); - - RakAssert(socketDescriptors && socketDescriptorCount>=1); - - if (socketDescriptors==0 || socketDescriptorCount<1) - return INVALID_SOCKET_DESCRIPTORS; - - //unsigned short localPort; - //localPort=socketDescriptors[0].port; - - RakAssert( maxConnections > 0 ); - - if ( maxConnections <= 0 ) - return INVALID_MAX_CONNECTIONS; - - DerefAllSockets(); - - - // Go through all socket descriptors and precreate sockets on the specified addresses - for (i=0; i rns(RakNet::OP_NEW(_FILE_AND_LINE_)); - if (socketDescriptors[i].remotePortRakNetWasStartedOn_PS3_PSP2==0) - { - rns->s = (unsigned int) SocketLayer::CreateBoundSocket( socketDescriptors[i].port, true, addrToBind, 100, socketDescriptors[i].extraSocketOptions, socketDescriptors[i].socketFamily ); - } - else - { - - - - - - } - - if ((SOCKET)rns->s==(SOCKET)-1) - { - DerefAllSockets(); - return SOCKET_FAILED_TO_BIND; - } - - SocketLayer::GetSystemAddress( rns->s, &rns->boundAddress ); - - rns->remotePortRakNetWasStartedOn_PS3_PSP2=socketDescriptors[i].remotePortRakNetWasStartedOn_PS3_PSP2; - rns->extraSocketOptions=socketDescriptors[i].extraSocketOptions; - rns->userConnectionSocketIndex=i; - -#if RAKNET_SUPPORT_IPV6==0 - if (addrToBind==0) - rns->boundAddress.SetToLoopback(4); -#endif - - int zero=0; - if (SocketLayer::SendTo((SOCKET)rns->s, (const char*) &zero,4,rns->boundAddress, rns->remotePortRakNetWasStartedOn_PS3_PSP2, rns->extraSocketOptions, _FILE_AND_LINE_)!=0) - { - DerefAllSockets(); - return SOCKET_FAILED_TEST_SEND; - } - - socketList.Push(rns, _FILE_AND_LINE_ ); - - } - - - if ( maximumNumberOfPeers == 0 ) - { - // Don't allow more incoming connections than we have peers. - if ( maximumIncomingConnections > maxConnections ) - maximumIncomingConnections = maxConnections; - - maximumNumberOfPeers = maxConnections; - // 04/19/2006 - Don't overallocate because I'm no longer allowing connected pings. - // The disconnects are not consistently processed and the process was sloppy and complicated. - // Allocate 10% extra to handle new connections from players trying to connect when the server is full - //remoteSystemListSize = maxConnections;// * 11 / 10 + 1; - - // remoteSystemList in Single thread - //remoteSystemList = RakNet::OP_NEW( _FILE_AND_LINE_ ); - remoteSystemList = RakNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); - - remoteSystemLookup = RakNet::OP_NEW_ARRAY((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE, _FILE_AND_LINE_ ); - - activeSystemList = RakNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); - - for ( i = 0; i < maximumNumberOfPeers; i++ ) - //for ( i = 0; i < remoteSystemListSize; i++ ) - { - // remoteSystemList in Single thread - remoteSystemList[ i ].isActive = false; - remoteSystemList[ i ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystemList[ i ].guid = UNASSIGNED_RAKNET_GUID; - remoteSystemList[ i ].myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystemList[ i ].connectMode=RemoteSystemStruct::NO_ACTION; - remoteSystemList[ i ].MTUSize = defaultMTUSize; - remoteSystemList[ i ].remoteSystemIndex = (SystemIndex) i; -#ifdef _DEBUG - remoteSystemList[ i ].reliabilityLayer.ApplyNetworkSimulator(_packetloss, _minExtraPing, _extraPingVariance); -#endif - - // All entries in activeSystemList have valid pointers all the time. - activeSystemList[ i ] = &remoteSystemList[ i ]; - } - - for (unsigned int i=0; i < (unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE; i++) - { - remoteSystemLookup[i]=0; - } - } - - // For histogram statistics - // nextReadBytesTime=0; - // lastSentBytes=lastReceivedBytes=0; - - if ( endThreads ) - { - updateCycleIsRunning = false; - endThreads = false; - - ClearBufferedCommands(); - ClearBufferedPackets(); - ClearSocketQueryOutput(); - - if ( isMainLoopThreadActive == false ) - { - int errorCode; - - - - - - - - errorCode = RakNet::RakThread::Create(UpdateNetworkLoop, this, threadPriority); - - - if ( errorCode != 0 ) - { - Shutdown( 0, 0 ); - return FAILED_TO_CREATE_NETWORK_THREAD; - } - - RakAssert(isRecvFromLoopThreadActive.GetValue()==0); - for (i=0; is; - rpai[i].rakPeer=this; - - - - - - errorCode = RakNet::RakThread::Create(RecvFromLoop, &rpai[i], threadPriority); - - - if ( errorCode != 0 ) - { - Shutdown( 0, 0 ); - return FAILED_TO_CREATE_NETWORK_THREAD; - } - } - - - while ( isRecvFromLoopThreadActive.GetValue() < (uint32_t) socketDescriptorCount ) - RakSleep(10); - } - - // Wait for the threads to activate. When they are active they will set these variables to true - - while ( isMainLoopThreadActive == false ) - RakSleep(10); - - } - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->OnRakPeerStartup(); - } - - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->OnRakPeerStartup(); - } - -#ifdef USE_THREADED_SEND - RakNet::SendToThread::AddRef(); -#endif - - return RAKNET_STARTED; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Must be called while offline -// -// If you accept connections, you must call this or else security will not be enabled for incoming connections. -// -// This feature requires more round trips, bandwidth, and CPU time for the connection handshake -// x64 builds require under 25% of the CPU time of other builds -// -// See the Encryption sample for example usage -// -// Parameters: -// publicKey = A pointer to the public key for accepting new connections -// privateKey = A pointer to the private key for accepting new connections -// If the private keys are 0, then a new key will be generated when this function is called -// bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::InitializeSecurity(const char *public_key, const char *private_key, bool bRequireClientKey) -{ -#if LIBCAT_SECURITY==1 - if ( endThreads == false ) - return false; - - // Copy client public key requirement flag - _require_client_public_key = bRequireClientKey; - - if (_server_handshake) - { - CAT_AUDIT_PRINTF("AUDIT: Deleting old server_handshake %x\n", _server_handshake); - RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - } - if (_cookie_jar) - { - CAT_AUDIT_PRINTF("AUDIT: Deleting old cookie jar %x\n", _cookie_jar); - RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - } - - _server_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); - _cookie_jar = RakNet::OP_NEW(_FILE_AND_LINE_); - - CAT_AUDIT_PRINTF("AUDIT: Created new server_handshake %x\n", _server_handshake); - CAT_AUDIT_PRINTF("AUDIT: Created new cookie jar %x\n", _cookie_jar); - CAT_AUDIT_PRINTF("AUDIT: Running _server_handshake->Initialize()\n"); - - if (_server_handshake->Initialize(public_key, private_key)) - { - CAT_AUDIT_PRINTF("AUDIT: Successfully initialized, filling cookie jar with goodies, storing public key and setting using security flag to true\n"); - - _server_handshake->FillCookieJar(_cookie_jar); - - memcpy(my_public_key, public_key, sizeof(my_public_key)); - - _using_security = true; - return true; - } - - CAT_AUDIT_PRINTF("AUDIT: Failure to initialize so deleting server handshake and cookie jar; also setting using_security flag = false\n"); - - RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - _server_handshake=0; - RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - _cookie_jar=0; - _using_security = false; - return false; -#else - (void) public_key; - (void) private_key; - (void) bRequireClientKey; - - return false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description -// Must be called while offline -// Disables security for incoming connections. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DisableSecurity( void ) -{ -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: DisableSecurity() called, so deleting _server_handshake %x and cookie_jar %x\n", _server_handshake, _cookie_jar); - RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - _server_handshake=0; - RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - _cookie_jar=0; - - _using_security = false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToSecurityExceptionList(const char *ip) -{ - securityExceptionMutex.Lock(); - securityExceptionList.Insert(RakString(ip), _FILE_AND_LINE_); - securityExceptionMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromSecurityExceptionList(const char *ip) -{ - if (securityExceptionList.Size()==0) - return; - - if (ip==0) - { - securityExceptionMutex.Lock(); - securityExceptionList.Clear(false, _FILE_AND_LINE_); - securityExceptionMutex.Unlock(); - } - else - { - unsigned i=0; - securityExceptionMutex.Lock(); - while (i < securityExceptionList.Size()) - { - if (securityExceptionList[i].IPAddressMatch(ip)) - { - securityExceptionList[i]=securityExceptionList[securityExceptionList.Size()-1]; - securityExceptionList.RemoveAtIndex(securityExceptionList.Size()-1); - } - else - i++; - } - securityExceptionMutex.Unlock(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsInSecurityExceptionList(const char *ip) -{ - if (securityExceptionList.Size()==0) - return false; - - unsigned i=0; - securityExceptionMutex.Lock(); - for (; i < securityExceptionList.Size(); i++) - { - if (securityExceptionList[i].IPAddressMatch(ip)) - { - securityExceptionMutex.Unlock(); - return true; - } - } - securityExceptionMutex.Unlock(); - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no -// more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced -// to the maximum number of peers allowed. Defaults to 0. -// -// Parameters: -// numberAllowed - Maximum number of incoming connections allowed. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetMaximumIncomingConnections( unsigned short numberAllowed ) -{ - maximumIncomingConnections = numberAllowed; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the maximum number of incoming connections, which is always <= maxConnections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::GetMaximumIncomingConnections( void ) const -{ - return maximumIncomingConnections; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns how many open connections there are at this time -// \return the number of open connections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::NumberOfConnections(void) const -{ - DataStructures::List addresses; - DataStructures::List guids; - GetSystemList(addresses, guids); - return (unsigned short) addresses.Size(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sets the password incoming connections must match in the call to Connect (defaults to none) -// Pass 0 to passwordData to specify no password -// -// Parameters: -// passwordData: A data block that incoming connections must match. This can be just a password, or can be a stream of data. -// - Specify 0 for no password data -// passwordDataLength: The length in bytes of passwordData -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetIncomingPassword( const char* passwordData, int passwordDataLength ) -{ - //if (passwordDataLength > MAX_OFFLINE_DATA_LENGTH) - // passwordDataLength=MAX_OFFLINE_DATA_LENGTH; - - if (passwordDataLength > 255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? - // It won't overflow at least because incomingPasswordLength is an unsigned char - if (passwordDataLength>0) - memcpy(incomingPassword, passwordData, passwordDataLength); - incomingPasswordLength=(unsigned char)passwordDataLength; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetIncomingPassword( char* passwordData, int *passwordDataLength ) -{ - if (passwordData==0) - { - *passwordDataLength=incomingPasswordLength; - return; - } - - if (*passwordDataLength > incomingPasswordLength) - *passwordDataLength=incomingPasswordLength; - - if (*passwordDataLength>0) - memcpy(passwordData, incomingPassword, *passwordDataLength); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Call this to connect to the specified host (ip or domain name) and server port. -// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. -// This is a non-blocking connection. You know the connection is successful when IsConnected() returns true -// or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. If the connection is not -// successful, such as rejected connection or no response then neither of these things will happen. -// Requires that you first call Initialize -// -// Parameters: -// host: Either a dotted IP address or a domain name -// remotePort: Which port to connect to on the remote machine. -// passwordData: A data block that must match the data block on the server. This can be just a password, or can be a stream of data -// passwordDataLength: The length in bytes of passwordData -// -// Returns: -// True on successful initiation. False on incorrect parameters, internal error, or too many existing peers -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -ConnectionAttemptResult RakPeer::Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ) -{ - // If endThreads is true here you didn't call Startup() first. - if ( host == 0 || endThreads || connectionSocketIndex>=socketList.Size() ) - return INVALID_PARAMETER; - - RakAssert(remotePort!=0); - - connectionSocketIndex=GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - if (passwordDataLength>255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? - // It won't overflow at least because outgoingPasswordLength is an unsigned char -// if (passwordDataLength>0) -// memcpy(outgoingPassword, passwordData, passwordDataLength); -// outgoingPasswordLength=(unsigned char) passwordDataLength; - - // 04/02/09 - Can't remember why I disabled connecting to self, but it seems to work - // Connecting to ourselves in the same instance of the program? -// if ( ( strcmp( host, "127.0.0.1" ) == 0 || strcmp( host, "0.0.0.0" ) == 0 ) && remotePort == mySystemAddress[0].port ) -// return false; - - return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, connectionSocketIndex, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ConnectionAttemptResult RakPeer::ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime) -{ - if ( host == 0 || endThreads || socket.IsNull() ) - return INVALID_PARAMETER; - - if (passwordDataLength>255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, 0, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime, socket ); - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Stops the network threads and close all connections. Multiple calls are ok. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::Shutdown( unsigned int blockDuration, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) -{ - unsigned i,j; - bool anyActive; - RakNet::TimeMS startWaitingTime; -// SystemAddress systemAddress; - RakNet::TimeMS time; - //unsigned short systemListSize = remoteSystemListSize; // This is done for threading reasons - unsigned short systemListSize = maximumNumberOfPeers; - - if ( blockDuration > 0 ) - { - for ( i = 0; i < systemListSize; i++ ) - { - // remoteSystemList in user thread - if (remoteSystemList[i].isActive) - NotifyAndFlagForShutdown(remoteSystemList[i].systemAddress, false, orderingChannel, disconnectionNotificationPriority); - } - - time = RakNet::GetTimeMS(); - startWaitingTime = time; - while ( time - startWaitingTime < blockDuration ) - { - anyActive=false; - for (j=0; j < systemListSize; j++) - { - // remoteSystemList in user thread - if (remoteSystemList[j].isActive) - { - anyActive=true; - break; - } - } - - // If this system is out of packets to send, then stop waiting - if ( anyActive==false ) - break; - - // This will probably cause the update thread to run which will probably - // send the disconnection notification - - RakSleep(15); - time = RakNet::GetTimeMS(); - } - } - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->OnRakPeerShutdown(); - } - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->OnRakPeerShutdown(); - } - - activeSystemListSize=0; - - quitAndDataEvents.SetEvent(); - - endThreads = true; - // Get recvfrom to unblock - for (i=0; i < socketList.Size(); i++) - { - SystemAddress sa2 = socketList[i]->boundAddress; - if (SocketLayer::SendTo(socketList[i]->s, (const char*) &i,1,sa2, socketList[i]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[i]->extraSocketOptions, _FILE_AND_LINE_)!=0) - break; - } - while ( isMainLoopThreadActive ) - { - endThreads = true; - RakSleep(15); - } - - RakNet::TimeMS timeout = RakNet::GetTimeMS()+1000; - while ( isRecvFromLoopThreadActive.GetValue()>0 && RakNet::GetTimeMS()boundAddress; - SocketLayer::SendTo(socketList[i]->s, (const char*) &i,1,sa2, socketList[i]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[i]->extraSocketOptions, _FILE_AND_LINE_); - } - - RakSleep(30); - } - -// char c=0; -// unsigned int socketIndex; - // remoteSystemList in Single thread - for ( i = 0; i < systemListSize; i++ ) - { - // Reserve this reliability layer for ourselves - remoteSystemList[ i ].isActive = false; - - // Remove any remaining packets - remoteSystemList[ i ].reliabilityLayer.Reset(false, remoteSystemList[ i ].MTUSize, false); - remoteSystemList[ i ].rakNetSocket.SetNull(); - - } - - - // Setting maximumNumberOfPeers to 0 allows remoteSystemList to be reallocated in Initialize. - // Setting remoteSystemListSize prevents threads from accessing the reliability layer - maximumNumberOfPeers = 0; - //remoteSystemListSize = 0; - - // Free any packets the user didn't deallocate - packetReturnMutex.Lock(); - for (i=0; i < packetReturnQueue.Size(); i++) - DeallocatePacket(packetReturnQueue[i]); - packetReturnQueue.Clear(_FILE_AND_LINE_); - packetReturnMutex.Unlock(); - packetAllocationPoolMutex.Lock(); - packetAllocationPool.Clear(_FILE_AND_LINE_); - packetAllocationPoolMutex.Unlock(); - - if (isRecvFromLoopThreadActive.GetValue()>0) - { - timeout = RakNet::GetTimeMS()+1000; - while ( isRecvFromLoopThreadActive.GetValue()>0 && RakNet::GetTimeMS() addresses; - DataStructures::List guids; - GetSystemList(addresses, guids); - if (remoteSystems) - { - unsigned short i; - for (i=0; i < *numberOfSystems && i < addresses.Size(); i++) - remoteSystems[i]=addresses[i]; - *numberOfSystems=i; - } - else - { - *numberOfSystems=(unsigned short) addresses.Size(); - } - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::GetNextSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - uint32_t retVal = sendReceiptSerial; - sendReceiptSerialMutex.Unlock(); - return retVal; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::IncrementNextSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - uint32_t returned = sendReceiptSerial; - if (++sendReceiptSerial==0) - sendReceiptSerial=1; - sendReceiptSerialMutex.Unlock(); - return returned; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sends a block of data to the specified system that you are connected to. -// This function only works while the client is connected (Use the Connect function). -// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM -// -// Parameters: -// data: The block of data to send -// length: The size in bytes of the data to send -// bitStream: The bitstream to send -// priority: What priority level to send on. -// reliability: How reliability to send this data -// orderingChannel: When using ordered or sequenced packets, what channel to order these on. -// - Packets are only ordered relative to other packets on the same stream -// systemAddress: Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none -// broadcast: True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. -// Returns: -// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( data && length > 0 ); -#endif - RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); - RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - if ( data == 0 || length < 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined()) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback(data,length); - - if (reliability>=UNRELIABLE_WITH_ACK_RECEIPT) - { - char buff[5]; - buff[0]=ID_SND_RECEIPT_ACKED; - sendReceiptSerialMutex.Lock(); - memcpy(buff+1, &sendReceiptSerial, 4); - sendReceiptSerialMutex.Unlock(); - SendLoopback( buff, 5 ); - } - - return usedSendReceipt; - } - - SendBuffered(data, length*8, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - return usedSendReceipt; -} - -void RakPeer::SendLoopback( const char *data, const int length ) -{ - if ( data == 0 || length < 0 ) - return; - - Packet *packet = AllocPacket(length, _FILE_AND_LINE_); - memcpy(packet->data, data, length); - packet->systemAddress = GetLoopbackAddress(); - packet->guid=myGuid; - PushBackPacket(packet, false); -} - -uint32_t RakPeer::Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( bitStream->GetNumberOfBytesUsed() > 0 ); -#endif - - RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); - RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - if ( bitStream->GetNumberOfBytesUsed() == 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined() ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback((const char*) bitStream->GetData(),bitStream->GetNumberOfBytesUsed()); - if (reliability>=UNRELIABLE_WITH_ACK_RECEIPT) - { - char buff[5]; - buff[0]=ID_SND_RECEIPT_ACKED; - sendReceiptSerialMutex.Lock(); - memcpy(buff+1, &sendReceiptSerial,4); - sendReceiptSerialMutex.Unlock(); - SendLoopback( buff, 5 ); - } - return usedSendReceipt; - } - - // Sends need to be buffered and processed in the update thread because the systemAddress associated with the reliability layer can change, - // from that thread, resulting in a send to the wrong player! While I could mutex the systemAddress, that is much slower than doing this - SendBuffered((const char*)bitStream->GetData(), bitStream->GetNumberOfBitsUsed(), priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - - return usedSendReceipt; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Sends multiple blocks of data, concatenating them automatically. -// -// This is equivalent to: -// RakNet::BitStream bs; -// bs.WriteAlignedBytes(block1, blockLength1); -// bs.WriteAlignedBytes(block2, blockLength2); -// bs.WriteAlignedBytes(block3, blockLength3); -// Send(&bs, ...) -// -// This function only works while the connected -// \param[in] data An array of pointers to blocks of data -// \param[in] lengths An array of integers indicating the length of each block of data -// \param[in] numParameters Length of the arrays data and lengths -// \param[in] priority What priority level to send on. See PacketPriority.h -// \param[in] reliability How reliability to send this data. See PacketPriority.h -// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream -// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none -// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. -// \return False if we are not connected to the specified recipient. True otherwise -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( data ); -#endif - - if ( data == 0 || lengths == 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if (numParameters==0) - return 0; - - if (lengths==0) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined() ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - SendBufferedList(data, lengths, numParameters, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - return usedSendReceipt; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. -// Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct -// -// Returns: -// 0 if no packets are waiting to be handled, otherwise an allocated packet -// If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected -// This also updates all memory blocks associated with synchronized memory and distributed objects -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -#ifdef _MSC_VER -#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized -#endif -Packet* RakPeer::Receive( void ) -{ - if ( !( IsActive() ) ) - return 0; - - RakNet::Packet *packet; -// Packet **threadPacket; - PluginReceiveResult pluginResult; - - int offset; - unsigned int i; - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->Update(); - } - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->Update(); - } - - do - { - packetReturnMutex.Lock(); - if (packetReturnQueue.IsEmpty()) - packet=0; - else - packet = packetReturnQueue.Pop(); - packetReturnMutex.Unlock(); - if (packet==0) - return 0; - -// unsigned char msgId; - if ( ( packet->length >= sizeof(unsigned char) + sizeof( RakNet::Time ) ) && - ( (unsigned char) packet->data[ 0 ] == ID_TIMESTAMP ) ) - { - offset = sizeof(unsigned char); - ShiftIncomingTimestamp( packet->data + offset, packet->systemAddress ); -// msgId=packet->data[sizeof(unsigned char) + sizeof( RakNet::Time )]; - } -// else - // msgId=packet->data[0]; - - // Some locally generated packets need to be processed by plugins, for example ID_FCM2_NEW_HOST - // The plugin itself should intercept these messages generated remotely -// if (packet->wasGeneratedLocally) -// return packet; - - - CallPluginCallbacks(pluginListTS, packet); - CallPluginCallbacks(pluginListNTS, packet); - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginResult=pluginListTS[i]->OnReceive(packet); - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( packet ); - packet=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - packet=0; - break; - } - } - - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginResult=pluginListNTS[i]->OnReceive(packet); - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( packet ); - packet=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - packet=0; - break; - } - } - - } while(packet==0); - -#ifdef _DEBUG - RakAssert( packet->data ); -#endif - - return packet; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Call this to deallocate a packet returned by Receive -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DeallocatePacket( Packet *packet ) -{ - if ( packet == 0 ) - return; - - if (packet->deleteData) - { - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - packet->~Packet(); - packetAllocationPoolMutex.Lock(); - packetAllocationPool.Release(packet,_FILE_AND_LINE_); - packetAllocationPoolMutex.Unlock(); - } - else - { - rakFree_Ex(packet, _FILE_AND_LINE_ ); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the total number of connections we are allowed -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::GetMaximumNumberOfPeers( void ) const -{ - return maximumNumberOfPeers; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). -// -// Parameters: -// target: Which connection to close -// sendDisconnectionNotification: True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. -// channel: If blockDuration > 0, the disconnect packet will be sent on this channel -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) -{ - /* - // This only be called from the user thread, for the user shutting down. - // From the network thread, this should occur because of ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST - unsigned j; - for (j=0; j < messageHandlerList.Size(); j++) - { - messageHandlerList[j]->OnClosedConnection( - target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress, - target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid, - LCR_CLOSED_BY_USER); - } - */ - - CloseConnectionInternal(target, sendDisconnectionNotification, false, orderingChannel, disconnectionNotificationPriority); - - // 12/14/09 Return ID_CONNECTION_LOST when calling CloseConnection with sendDisconnectionNotification==false, elsewise it is never returned - if (sendDisconnectionNotification==false && GetConnectionState(target)==IS_CONNECTED) - { - Packet *packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection - packet->guid = target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid; - packet->systemAddress = target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress; - packet->systemAddress.systemIndex = (SystemIndex) GetIndexFromSystemAddress(packet->systemAddress); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - packet->wasGeneratedLocally=true; // else processed twice - AddPacketToProducer(packet); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Cancel a pending connection attempt -// If we are already connected, the connection stays open -// \param[in] target Which system to cancel -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CancelConnectionAttempt( const SystemAddress target ) -{ - unsigned int i; - - // Cancel pending connection attempt, if there is one - i=0; - requestedConnectionQueueMutex.Lock(); - while (i < requestedConnectionQueue.Size()) - { - if (requestedConnectionQueue[i]->systemAddress==target) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Deleting requestedConnectionQueue %i client_handshake %x\n", i, requestedConnectionQueue[ i ]->client_handshake); - RakNet::OP_DELETE(requestedConnectionQueue[i]->client_handshake, _FILE_AND_LINE_ ); -#endif - RakNet::OP_DELETE(requestedConnectionQueue[i], _FILE_AND_LINE_ ); - requestedConnectionQueue.RemoveAtIndex(i); - break; - } - else - i++; - } - requestedConnectionQueueMutex.Unlock(); - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ConnectionState RakPeer::GetConnectionState(const AddressOrGUID systemIdentifier) -{ - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemIdentifier.systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - return IS_PENDING; - } - } - requestedConnectionQueueMutex.Unlock(); - } - - int index; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); - } - else - { - index = GetIndexFromGuid(systemIdentifier.rakNetGuid); - } - - if (index==-1) - return IS_NOT_CONNECTED; - - if (remoteSystemList[index].isActive==false) - return IS_DISCONNECTED; - - switch (remoteSystemList[index].connectMode) - { - case RemoteSystemStruct::DISCONNECT_ASAP: - return IS_DISCONNECTING; - case RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY: - return IS_SILENTLY_DISCONNECTING; - case RemoteSystemStruct::DISCONNECT_ON_NO_ACK: - return IS_DISCONNECTING; - case RemoteSystemStruct::REQUESTED_CONNECTION: - return IS_CONNECTING; - case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: - return IS_CONNECTING; - case RemoteSystemStruct::UNVERIFIED_SENDER: - return IS_CONNECTING; - case RemoteSystemStruct::CONNECTED: - return IS_CONNECTED; - default: - return IS_NOT_CONNECTED; - } - - return IS_NOT_CONNECTED; -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. -// -// Parameters -// systemAddress - The systemAddress to search for -// -// Returns -// An integer from 0 to the maximum number of peers -1, or -1 if that player is not found -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress ) const -{ - return GetIndexFromSystemAddress(systemAddress, false); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// This function is only useful for looping through all players. -// -// Parameters -// index - an integer between 0 and the maximum number of players allowed - 1. -// -// Returns -// A valid systemAddress or UNASSIGNED_SYSTEM_ADDRESS if no such player at that index -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetSystemAddressFromIndex( int index ) -{ - // remoteSystemList in user thread - //if ( index >= 0 && index < remoteSystemListSize ) - if ( index >= 0 && index < maximumNumberOfPeers ) - if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail - return remoteSystemList[ index ].systemAddress; - - return UNASSIGNED_SYSTEM_ADDRESS; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Same as GetSystemAddressFromIndex but returns RakNetGUID -// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. -// \return The RakNetGUID -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetGUID RakPeer::GetGUIDFromIndex( int index ) -{ - // remoteSystemList in user thread - //if ( index >= 0 && index < remoteSystemListSize ) - if ( index >= 0 && index < maximumNumberOfPeers ) - if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail - return remoteSystemList[ index ].guid; - - return UNASSIGNED_RAKNET_GUID; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient -// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system -// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. -// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const -{ - addresses.Clear(false, _FILE_AND_LINE_); - guids.Clear(false, _FILE_AND_LINE_); - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) - { - addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); - guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Bans an IP from connecting. Banned IPs persist between connections. -// -// Parameters -// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban -// All IP addresses starting with 128.0.0 -// milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToBanList( const char *IP, RakNet::TimeMS milliseconds ) -{ - unsigned index; - RakNet::TimeMS time = RakNet::GetTimeMS(); - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return ; - - // If this guy is already in the ban list, do nothing - index = 0; - - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - if ( strcmp( IP, banList[ index ]->IP ) == 0 ) - { - // Already in the ban list. Just update the time - if (milliseconds==0) - banList[ index ]->timeout=0; // Infinite - else - banList[ index ]->timeout=time+milliseconds; - banListMutex.Unlock(); - return; - } - } - - banListMutex.Unlock(); - - BanStruct *banStruct = RakNet::OP_NEW( _FILE_AND_LINE_ ); - banStruct->IP = (char*) rakMalloc_Ex( 16, _FILE_AND_LINE_ ); - if (milliseconds==0) - banStruct->timeout=0; // Infinite - else - banStruct->timeout=time+milliseconds; - strcpy( banStruct->IP, IP ); - banListMutex.Lock(); - banList.Insert( banStruct, _FILE_AND_LINE_ ); - banListMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allows a previously banned IP to connect. -// -// Parameters -// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban -// All IP addresses starting with 128.0.0 -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromBanList( const char *IP ) -{ - unsigned index; - BanStruct *temp; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return ; - - index = 0; - temp=0; - - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - if ( strcmp( IP, banList[ index ]->IP ) == 0 ) - { - temp = banList[ index ]; - banList[ index ] = banList[ banList.Size() - 1 ]; - banList.RemoveAtIndex( banList.Size() - 1 ); - break; - } - } - - banListMutex.Unlock(); - - if (temp) - { - rakFree_Ex(temp->IP, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allows all previously banned IPs to connect. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBanList( void ) -{ - unsigned index; - index = 0; - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - rakFree_Ex(banList[ index ]->IP, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(banList[ index ], _FILE_AND_LINE_); - } - - banList.Clear(false, _FILE_AND_LINE_); - - banListMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetLimitIPConnectionFrequency(bool b) -{ - limitConnectionFrequencyFromTheSameIP=b; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Determines if a particular IP is banned. -// -// Parameters -// IP - Complete dotted IP address -// -// Returns -// True if IP matches any IPs in the ban list, accounting for any wildcards. -// False otherwise. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsBanned( const char *IP ) -{ - unsigned banListIndex, characterIndex; - RakNet::TimeMS time; - BanStruct *temp; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return false; - - banListIndex = 0; - - if ( banList.Size() == 0 ) - return false; // Skip the mutex if possible - - time = RakNet::GetTimeMS(); - - banListMutex.Lock(); - - while ( banListIndex < banList.Size() ) - { - if (banList[ banListIndex ]->timeout>0 && banList[ banListIndex ]->timeoutIP, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - else - { - characterIndex = 0; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( true ) - { - if ( banList[ banListIndex ]->IP[ characterIndex ] == IP[ characterIndex ] ) - { - // Equal characters - - if ( IP[ characterIndex ] == 0 ) - { - banListMutex.Unlock(); - // End of the string and the strings match - - return true; - } - - characterIndex++; - } - - else - { - if ( banList[ banListIndex ]->IP[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) - { - // End of one of the strings - break; - } - - // Characters do not match - if ( banList[ banListIndex ]->IP[ characterIndex ] == '*' ) - { - banListMutex.Unlock(); - - // Domain is banned. - return true; - } - - // Characters do not match and it is not a * - break; - } - } - - banListIndex++; - } - } - - banListMutex.Unlock(); - - // No match found. - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Send a ping to the specified connected system. -// -// Parameters: -// target - who to ping -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::Ping( const SystemAddress target ) -{ - PingInternal(target, false, UNRELIABLE); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Send a ping to the specified unconnected system. -// The remote system, if it is Initialized, will respond with ID_UNCONNECTED_PONG. -// The final ping time will be encoded in the following sizeof(RakNet::TimeMS) bytes. (Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h -// -// Parameters: -// host: Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. -// remotePort: Which port to connect to on the remote machine. -// onlyReplyOnAcceptingConnections: Only request a reply if the remote system has open connections -// connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex ) -{ - if ( host == 0 ) - return false; - - // If this assert hits then Startup wasn't called or the call failed. - RakAssert(connectionSocketIndex < socketList.Size()); - -// if ( IsActive() == false ) -// return; - - RakNet::BitStream bitStream( sizeof(unsigned char) + sizeof(RakNet::Time) ); - if ( onlyReplyOnAcceptingConnections ) - bitStream.Write((MessageID)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); - else - bitStream.Write((MessageID)ID_UNCONNECTED_PING); - - bitStream.Write(RakNet::GetTime()); - - bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - - // No timestamp for 255.255.255.255 - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->boundAddress.GetIPVersion()); - systemAddress.FixForIPVersion(socketList[realIndex]->boundAddress); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( socketList[realIndex]->s, (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, socketList[realIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[realIndex]->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the average of all ping times read for a specified target -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetAveragePing( const AddressOrGUID systemIdentifier ) -{ - int sum, quantity; - RemoteSystemStruct *remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - - for ( sum = 0, quantity = 0; quantity < PING_TIMES_ARRAY_SIZE; quantity++ ) - { - if ( remoteSystem->pingAndClockDifferential[ quantity ].pingTime == 65535 ) - break; - else - sum += remoteSystem->pingAndClockDifferential[ quantity ].pingTime; - } - - if ( quantity > 0 ) - return sum / quantity; - else - return -1; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the last ping time read for the specific player or -1 if none read yet -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetLastPing( const AddressOrGUID systemIdentifier ) const -{ - RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - -// return (int)(remoteSystem->reliabilityLayer.GetAckPing()/(RakNet::TimeUS)1000); - - if ( remoteSystem->pingAndClockDifferentialWriteIndex == 0 ) - return remoteSystem->pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE - 1 ].pingTime; - else - return remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex - 1 ].pingTime; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the lowest ping time read or -1 if none read yet -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetLowestPing( const AddressOrGUID systemIdentifier ) const -{ - RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - - return remoteSystem->lowestPing; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Ping the remote systems every so often. This is off by default -// This will work anytime -// -// Parameters: -// doPing - True to start occasional pings. False to stop them. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetOccasionalPing( bool doPing ) -{ - occasionalPing = doPing; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Length should be under 400 bytes, as a security measure against flood attacks -// Sets the data to send with an (LAN server discovery) /(offline ping) response -// See the Ping sample project for how this is used. -// data: a block of data to store, or 0 for none -// length: The length of data in bytes, or 0 for none -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetOfflinePingResponse( const char *data, const unsigned int length ) -{ - RakAssert(length < 400); - - rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); - offlinePingResponse.Reset(); - - if ( data && length > 0 ) - offlinePingResponse.Write( data, length ); - - rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns pointers to a copy of the data passed to SetOfflinePingResponse -// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() -// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() -// \sa SetOfflinePingResponse -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetOfflinePingResponse( char **data, unsigned int *length ) -{ - rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); - *data = (char*) offlinePingResponse.GetData(); - *length = (int) offlinePingResponse.GetNumberOfBytesUsed(); - rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the unique SystemAddress that represents you on the the network -// Note that unlike in previous versions, this is a struct and is not sequential -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetInternalID( const SystemAddress systemAddress, const int index ) const -{ - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - return ipList[index]; - } - else - { - -// SystemAddress returnValue; - RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, false, true ); - if (remoteSystem==0) - return UNASSIGNED_SYSTEM_ADDRESS; - - return remoteSystem->theirInternalSystemAddress[index]; - /* - sockaddr_in sa; - socklen_t len = sizeof(sa); - if (getsockname__(connectionSockets[remoteSystem->connectionSocketIndex], (sockaddr*)&sa, &len)!=0) - return UNASSIGNED_SYSTEM_ADDRESS; - returnValue.port=ntohs(sa.sin_port); - returnValue.binaryAddress=sa.sin_addr.s_addr; - return returnValue; -*/ - - - - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the unique address identifier that represents you on the the network and is based on your external -// IP / port (the IP / port the specified player uses to communicate with you) -// Note that unlike in previous versions, this is a struct and is not sequential -// -// Parameters: -// target: Which remote system you are referring to for your external ID -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetExternalID( const SystemAddress target ) const -{ - unsigned i; - SystemAddress inactiveExternalId; - - inactiveExternalId=UNASSIGNED_SYSTEM_ADDRESS; - - if (target==UNASSIGNED_SYSTEM_ADDRESS) - return firstExternalID; - - // First check for active connection with this systemAddress - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].systemAddress == target ) - { - if ( remoteSystemList[ i ].isActive ) - return remoteSystemList[ i ].myExternalSystemAddress; - else if (remoteSystemList[ i ].myExternalSystemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - inactiveExternalId=remoteSystemList[ i ].myExternalSystemAddress; - } - } - - return inactiveExternalId; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -const RakNetGUID RakPeer::GetMyGUID(void) const -{ - return myGuid; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetMyBoundAddress(const int socketIndex) -{ - DataStructures::List > sockets; - GetSockets( sockets ); - if (sockets.Size()>0) - return sockets[socketIndex]->boundAddress; - else - return UNASSIGNED_SYSTEM_ADDRESS; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -const RakNetGUID& RakPeer::GetGuidFromSystemAddress( const SystemAddress input ) const -{ - if (input==UNASSIGNED_SYSTEM_ADDRESS) - return myGuid; - - if (input.systemIndex!=(SystemIndex)-1 && input.systemIndexreliabilityLayer.SetTimeoutTime(timeMS); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RakNet::TimeMS RakPeer::GetTimeoutTime( const SystemAddress target ) -{ - if (target==UNASSIGNED_SYSTEM_ADDRESS) - { - return defaultTimeoutTime; - } - else - { - RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( target, false, true ); - - if ( remoteSystem != 0 ) - remoteSystem->reliabilityLayer.GetTimeoutTime(); - } - return defaultTimeoutTime; -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the current MTU size -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetMTUSize( const SystemAddress target ) const -{ - if (target!=UNASSIGNED_SYSTEM_ADDRESS) - { - RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(target, false, true); - if (rss) - return rss->MTUSize; - } - return defaultMTUSize; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the number of IP addresses we have -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetNumberOfAddresses( void ) -{ - - int i = 0; - - while ( ipList[ i ]!=UNASSIGNED_SYSTEM_ADDRESS ) - i++; - - return i; - - - - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns an IP address at index 0 to GetNumberOfAddresses-1 -// \param[in] index index into the list of IP addresses -// \return The local IP address at this index -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -const char* RakPeer::GetLocalIP( unsigned int index ) -{ - if (IsActive()==false) - { - // Fill out ipList structure - - SocketLayer::GetMyIP( ipList ); - - } - - - static char str[128]; - ipList[index].ToString(false,str); - return str; - - - - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Is this a local IP? -// \param[in] An IP address to check -// \return True if this is one of the IP addresses returned by GetLocalIP -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsLocalIP( const char *ip ) -{ - if (ip==0 || ip[0]==0) - return false; - - - if (strcmp(ip, "127.0.0.1")==0 || strcmp(ip, "localhost")==0) - return true; - - int num = GetNumberOfAddresses(); - int i; - for (i=0; i < num; i++) - { - if (strcmp(ip, GetLocalIP(i))==0) - return true; - } - - - - - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary -// when connection to servers with multiple IP addresses -// -// Parameters: -// allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AllowConnectionResponseIPMigration( bool allow ) -{ - allowConnectionResponseIPMigration = allow; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sends a message ID_ADVERTISE_SYSTEM to the remote unconnected system. -// This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through -// -// Requires: -// The sender and recipient must already be started via a successful call to Initialize -// -// host: Either a dotted IP address or a domain name -// remotePort: Which port to connect to on the remote machine. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex ) -{ - RakNet::BitStream bs; - bs.Write((MessageID)ID_ADVERTISE_SYSTEM); - bs.WriteAlignedBytes((const unsigned char*) data,dataLength); - return SendOutOfBand(host, remotePort, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), connectionSocketIndex ); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. -// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived -// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. -// Defaults to 0 (never return this notification) -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSplitMessageProgressInterval(int interval) -{ - RakAssert(interval>=0); - splitMessageProgressInterval=interval; - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - remoteSystemList[ i ].reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns what was passed to SetSplitMessageProgressInterval() -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetSplitMessageProgressInterval(void) const -{ - return splitMessageProgressInterval; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Set how long to wait before giving up on sending an unreliable message -// Useful if the network is clogged up. -// Set to 0 or less to never timeout. Defaults to 0. -// timeoutMS How many ms to wait before simply not sending an unreliable message. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetUnreliableTimeout(RakNet::TimeMS timeoutMS) -{ - unreliableTimeout=timeoutMS; - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - remoteSystemList[ i ].reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Send a message to host, with the IP socket option TTL set to 3 -// This message will not reach the host, but will open the router. -// Used for NAT-Punchthrough -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex ) -{ - char fakeData[2]; - fakeData[0]=0; - fakeData[1]=1; - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - SystemAddress systemAddress = socketList[realIndex]->boundAddress; - systemAddress.FromStringExplicitPort(host,remotePort); - SocketLayer::SendToTTL( socketList[realIndex]->s, (char*)fakeData, 2, systemAddress, ttl ); -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Attatches a Plugin interface to run code automatically on message receipt in the Receive call -// -// \param messageHandler Pointer to a plugin to attach -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AttachPlugin( PluginInterface2 *plugin ) -{ - bool isNotThreadsafe = plugin->UsesReliabilityLayer(); - if (isNotThreadsafe) - { - if (pluginListNTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - plugin->SetRakPeerInterface(this); - plugin->OnAttach(); - pluginListNTS.Insert(plugin, _FILE_AND_LINE_); - } - } - else - { - if (pluginListTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - plugin->SetRakPeerInterface(this); - plugin->OnAttach(); - pluginListTS.Insert(plugin, _FILE_AND_LINE_); - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Detaches a Plugin interface to run code automatically on message receipt -// -// \param messageHandler Pointer to a plugin to detach -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DetachPlugin( PluginInterface2 *plugin ) -{ - if (plugin==0) - return; - - unsigned int index; - - bool isNotThreadsafe = plugin->UsesReliabilityLayer(); - if (isNotThreadsafe) - { - index = pluginListNTS.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - // Unordered list so delete from end for speed - pluginListNTS[index]=pluginListNTS[pluginListNTS.Size()-1]; - pluginListNTS.RemoveFromEnd(); - } - } - else - { - index = pluginListTS.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - // Unordered list so delete from end for speed - pluginListTS[index]=pluginListTS[pluginListTS.Size()-1]; - pluginListTS.RemoveFromEnd(); - } - } - plugin->OnDetach(); - plugin->SetRakPeerInterface(0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Put a packet back at the end of the receive queue in case you don't want to deal with it immediately -// -// packet The packet you want to push back. -// pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PushBackPacket( Packet *packet, bool pushAtHead) -{ - if (packet==0) - return; - - unsigned i; - for (i=0; i < pluginListTS.Size(); i++) - pluginListTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); - - packetReturnMutex.Lock(); - if (pushAtHead) - packetReturnQueue.PushAtHead(packet,0,_FILE_AND_LINE_); - else - packetReturnQueue.Push(packet,_FILE_AND_LINE_); - packetReturnMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress) -{ - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = 0; - bcs->systemIdentifier.systemAddress=systemAddress; - bcs->systemIdentifier.rakNetGuid=guid; - bcs->command=BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS; - bufferedCommands.Push(bcs); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -Packet* RakPeer::AllocatePacket(unsigned dataSize) -{ - return AllocPacket(dataSize, _FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetSmartPtr RakPeer::GetSocket( const SystemAddress target ) -{ - // Send a query to the thread to get the socket, and return when we got it - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; - bcs->systemIdentifier=target; - bcs->data=0; - bufferedCommands.Push(bcs); - - // Block up to one second to get the socket, although it should actually take virtually no time - SocketQueryOutput *sqo; - RakNet::TimeMS stopWaiting = RakNet::GetTimeMS()+1000; - DataStructures::List > output; - while (RakNet::GetTimeMS() < stopWaiting) - { - if (isMainLoopThreadActive==false) - return RakNetSmartPtr(); - - RakSleep(0); - - sqo = socketQueryOutput.Pop(); - if (sqo) - { - output=sqo->sockets; - sqo->sockets.Clear(false, _FILE_AND_LINE_); - socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); - if (output.Size()) - return output[0]; - break; - } - } - return RakNetSmartPtr(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSockets( DataStructures::List > &sockets ) -{ - sockets.Clear(false, _FILE_AND_LINE_); - - // Send a query to the thread to get the socket, and return when we got it - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; - bcs->systemIdentifier=UNASSIGNED_SYSTEM_ADDRESS; - bcs->data=0; - bufferedCommands.Push(bcs); - - // Block up to one second to get the socket, although it should actually take virtually no time - SocketQueryOutput *sqo; - RakNetSmartPtr output; - while (1) - { - if (isMainLoopThreadActive==false) - return; - - RakSleep(0); - - sqo = socketQueryOutput.Pop(); - if (sqo) - { - sockets=sqo->sockets; - sqo->sockets.Clear(false, _FILE_AND_LINE_); - socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); - return; - } - } - return; -} -void RakPeer::ReleaseSockets( DataStructures::List > &sockets ) -{ - sockets.Clear(false,_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Adds simulated ping and packet loss to the outgoing data flow. -// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance) -{ -#ifdef _DEBUG - if (remoteSystemList) - { - unsigned short i; - for (i=0; i < maximumNumberOfPeers; i++) - //for (i=0; i < remoteSystemListSize; i++) - remoteSystemList[i].reliabilityLayer.ApplyNetworkSimulator(packetloss, minExtraPing, extraPingVariance); - } - - _packetloss=packetloss; - _minExtraPing=minExtraPing; - _extraPingVariance=extraPingVariance; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void RakPeer::SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ) -{ - maxOutgoingBPS=maxBitsPerSecond; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns if you previously called ApplyNetworkSimulator -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsNetworkSimulatorActive( void ) -{ -#ifdef _DEBUG - return _packetloss>0 || _minExtraPing>0 || _extraPingVariance>0; -#else - return false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::WriteOutOfBandHeader(RakNet::BitStream *bitStream) -{ - bitStream->Write((MessageID)ID_OUT_OF_BAND_INTERNAL); - bitStream->Write(myGuid); - bitStream->WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData) -{ - userUpdateThreadPtr=_userUpdateThreadPtr; - userUpdateThreadData=_userUpdateThreadData; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex ) -{ - if ( IsActive() == false ) - return false; - - if (host==0 || host[0]==0) - return false; - - // If this assert hits then Startup wasn't called or the call failed. - RakAssert(connectionSocketIndex < socketList.Size()); - - // This is a security measure. Don't send data longer than this value - RakAssert(dataLength <= (MAX_OFFLINE_DATA_LENGTH + sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID))); - - if (host==0) - return false; - - // 34 bytes - RakNet::BitStream bitStream; - WriteOutOfBandHeader(&bitStream); - - if (dataLength>0) - { - bitStream.Write(data, dataLength); - } - - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->boundAddress.GetIPVersion()); - systemAddress.FixForIPVersion(socketList[realIndex]->boundAddress); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); - - SocketLayer::SendTo( socketList[realIndex]->s, (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, socketList[realIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[realIndex]->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetStatistics * RakPeer::GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns ) -{ - static RakNetStatistics staticStatistics; - RakNetStatistics *systemStats; - if (rns==0) - systemStats=&staticStatistics; - else - systemStats=rns; - - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - bool firstWrite=false; - // Return a crude sum - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].isActive) - { - RakNetStatistics rnsTemp; - remoteSystemList[ i ].reliabilityLayer.GetStatistics(&rnsTemp); - - if (firstWrite==false) - { - memcpy(systemStats, &rnsTemp, sizeof(RakNetStatistics)); - firstWrite=true; - } - else - (*systemStats)+=rnsTemp; - } - } - return systemStats; - } - else - { - RemoteSystemStruct * rss; - rss = GetRemoteSystemFromSystemAddress( systemAddress, false, false ); - if ( rss && endThreads==false ) - { - rss->reliabilityLayer.GetStatistics(systemStats); - return systemStats; - } - } - - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::GetStatistics( const int index, RakNetStatistics *rns ) -{ - if (index < maximumNumberOfPeers && remoteSystemList[ index ].isActive) - { - remoteSystemList[ index ].reliabilityLayer.GetStatistics(rns); - return true; - } - return false; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetReceiveBufferSize(void) -{ - unsigned int size; - packetReturnMutex.Lock(); - size=packetReturnQueue.Size(); - packetReturnMutex.Unlock(); - return size; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const -{ - unsigned i; - - if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) - return -1; - - if (systemAddress.systemIndex!=(SystemIndex)-1 && systemAddress.systemIndex < maximumNumberOfPeers && remoteSystemList[systemAddress.systemIndex].systemAddress==systemAddress && remoteSystemList[ systemAddress.systemIndex ].isActive) - return systemAddress.systemIndex; - - if (calledFromNetworkThread) - { - return GetRemoteSystemIndex(systemAddress); - } - else - { - // remoteSystemList in user and network thread - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].systemAddress == systemAddress ) - return i; - - // If no active results found, try previously active results. - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].systemAddress == systemAddress ) - return i; - } - - return -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromGuid( const RakNetGUID guid ) -{ - unsigned i; - - if ( guid == UNASSIGNED_RAKNET_GUID ) - return -1; - - if (guid.systemIndex!=(SystemIndex)-1 && guid.systemIndex < maximumNumberOfPeers && remoteSystemList[guid.systemIndex].guid==guid && remoteSystemList[ guid.systemIndex ].isActive) - return guid.systemIndex; - - // remoteSystemList in user and network thread - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].guid == guid ) - return i; - - // If no active results found, try previously active results. - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].guid == guid ) - return i; - - return -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -#if LIBCAT_SECURITY==1 -bool RakPeer::GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey) -{ - CAT_AUDIT_PRINTF("AUDIT: In GenerateConnectionRequestChallenge()\n"); - - rcs->client_handshake = 0; - rcs->publicKeyMode = PKM_INSECURE_CONNECTION; - - if (!publicKey) return true; - - switch (publicKey->publicKeyMode) - { - default: - case PKM_INSECURE_CONNECTION: - break; - - case PKM_ACCEPT_ANY_PUBLIC_KEY: - CAT_OBJCLR(rcs->remote_public_key); - rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); - - rcs->publicKeyMode = PKM_ACCEPT_ANY_PUBLIC_KEY; - break; - - case PKM_USE_TWO_WAY_AUTHENTICATION: - if (publicKey->myPublicKey == 0 || publicKey->myPrivateKey == 0 || - publicKey->remoteServerPublicKey == 0) - { - return false; - } - - rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); - memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); - - if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || - !rcs->client_handshake->SetIdentity(publicKey->myPublicKey, publicKey->myPrivateKey) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object with identity for this RequestedConnectionStruct\n"); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - return false; - } - - CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object with identity for this RequestedConnectionStruct -- pre-generated challenge\n"); - - rcs->publicKeyMode = PKM_USE_TWO_WAY_AUTHENTICATION; - break; - - case PKM_USE_KNOWN_PUBLIC_KEY: - if (publicKey->remoteServerPublicKey == 0) - return false; - - rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); - memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); - - if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object for this RequestedConnectionStruct\n"); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - return false; - } - - CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object for this RequestedConnectionStruct -- pre-generated challenge\n"); - - rcs->publicKeyMode = PKM_USE_KNOWN_PUBLIC_KEY; - break; - } - - return true; -} -#endif -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ) -{ - RakAssert(passwordDataLength <= 256); - RakAssert(remotePort!=0); - SystemAddress systemAddress; - if (!systemAddress.FromStringExplicitPort(host,remotePort,socketList[connectionSocketIndex]->boundAddress.GetIPVersion())) - return CANNOT_RESOLVE_DOMAIN_NAME; - - // Already connected? - if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) - return ALREADY_CONNECTED_TO_ENDPOINT; - - //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); - RequestedConnectionStruct *rcs = RakNet::OP_NEW(_FILE_AND_LINE_); - - rcs->systemAddress=systemAddress; - rcs->nextRequestTime=RakNet::GetTimeMS(); - rcs->requestsMade=0; - rcs->data=0; - rcs->extraData=extraData; - rcs->socketIndex=connectionSocketIndex; - rcs->actionToTake=RequestedConnectionStruct::CONNECT; - rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; - rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; - memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); - rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; - rcs->timeoutTime=timeoutTime; - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: In SendConnectionRequest()\n"); - if (!GenerateConnectionRequestChallenge(rcs,publicKey)) - return SECURITY_INITIALIZATION_FAILED; -#else - (void) publicKey; -#endif - - // Return false if already pending, else push on queue - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - // Not necessary - //RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; - } - } - requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - - return CONNECTION_ATTEMPT_STARTED; -} -ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime, RakNetSmartPtr socket ) -{ - RakAssert(passwordDataLength <= 256); - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort); - - // Already connected? - if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) - return ALREADY_CONNECTED_TO_ENDPOINT; - - //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); - RequestedConnectionStruct *rcs = RakNet::OP_NEW(_FILE_AND_LINE_); - - rcs->systemAddress=systemAddress; - rcs->nextRequestTime=RakNet::GetTimeMS(); - rcs->requestsMade=0; - rcs->data=0; - rcs->extraData=extraData; - rcs->socketIndex=connectionSocketIndex; - rcs->actionToTake=RequestedConnectionStruct::CONNECT; - rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; - rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; - memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); - rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; - rcs->timeoutTime=timeoutTime; - rcs->socket=socket; - -#if LIBCAT_SECURITY==1 - if (!GenerateConnectionRequestChallenge(rcs,publicKey)) - return SECURITY_INITIALIZATION_FAILED; -#else - (void) publicKey; -#endif - - // Return false if already pending, else push on queue - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - // Not necessary - //RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; - } - } - requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - - return CONNECTION_ATTEMPT_STARTED; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ValidateRemoteSystemLookup(void) const -{ -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const -{ - if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return GetRemoteSystemFromGUID(systemIdentifier.rakNetGuid, onlyActive); - else - return GetRemoteSystemFromSystemAddress(systemIdentifier.systemAddress, calledFromNetworkThread, onlyActive); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const -{ - unsigned i; - - if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) - return 0; - - if (calledFromNetworkThread) - { - unsigned int index = GetRemoteSystemIndex(systemAddress); - if (index!=(unsigned int) -1) - { - if (onlyActive==false || remoteSystemList[ index ].isActive==true ) - { - RakAssert(remoteSystemList[index].systemAddress==systemAddress); - return remoteSystemList + index; - } - } - } - else - { - int deadConnectionIndex=-1; - - // Active connections take priority. But if there are no active connections, return the first systemAddress match found - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].systemAddress == systemAddress) - { - if ( remoteSystemList[ i ].isActive ) - return remoteSystemList + i; - else if (deadConnectionIndex==-1) - deadConnectionIndex=i; - } - } - - if (deadConnectionIndex!=-1 && onlyActive==false) - return remoteSystemList + deadConnectionIndex; - } - - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const -{ - if (guid==UNASSIGNED_RAKNET_GUID) - return 0; - - unsigned i; - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].guid == guid && (onlyActive==false || remoteSystemList[ i ].isActive)) - { - return remoteSystemList + i; - } - } - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize ) -{ - RakNet::BitStream bs((unsigned char*) data,byteSize,false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID guid; - bs.Read(guid); - RakNet::Time incomingTimestamp; - bs.Read(incomingTimestamp); - unsigned char doSecurity; - bs.Read(doSecurity); - -#if LIBCAT_SECURITY==1 - unsigned char doClientKey; - if (_using_security) - { - // Ignore message on bad state - if (doSecurity != 1 || !remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()) - return; - - // Validate client proof of key - unsigned char proof[cat::EasyHandshake::PROOF_BYTES]; - bs.ReadAlignedBytes(proof, sizeof(proof)); - if (!remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->ValidateProof(proof, sizeof(proof))) - { - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - - CAT_OBJCLR(remoteSystem->client_public_key); - - bs.Read(doClientKey); - - // Check if client wants to prove identity - if (doClientKey == 1) - { - // Read identity proof - unsigned char ident[cat::EasyHandshake::IDENTITY_BYTES]; - bs.ReadAlignedBytes(ident, sizeof(ident)); - - // If we are listening to these proofs, - if (_require_client_public_key) - { - // Validate client identity - if (!_server_handshake->VerifyInitiatorIdentity(remoteSystem->answer, ident, remoteSystem->client_public_key)) - { - RakNet::BitStream bitStream; - bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect - bitStream.Write((unsigned char)2); // Indicate client identity is invalid - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - } - - // Otherwise ignore the client public key - } - else - { - // If no client key was provided but it is required, - if (_require_client_public_key) - { - RakNet::BitStream bitStream; - bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect - bitStream.Write((unsigned char)1); // Indicate client identity is missing - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - } - } -#endif // LIBCAT_SECURITY - - unsigned char *password = bs.GetData()+BITS_TO_BYTES(bs.GetReadOffset()); - int passwordLength = byteSize - BITS_TO_BYTES(bs.GetReadOffset()); - if ( incomingPasswordLength != passwordLength || - memcmp( password, incomingPassword, incomingPasswordLength ) != 0 ) - { - CAT_AUDIT_PRINTF("AUDIT: Invalid password\n"); - // This one we only send once since we don't care if it arrives. - RakNet::BitStream bitStream; - bitStream.Write((MessageID)ID_INVALID_PASSWORD); - bitStream.Write(GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - - // OK - remoteSystem->connectMode=RemoteSystemStruct::HANDLING_CONNECTION_REQUEST; - - OnConnectionRequest( remoteSystem, incomingTimestamp ); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, RakNet::Time incomingTimestamp ) -{ - RakNet::BitStream bitStream; - bitStream.Write((MessageID)ID_CONNECTION_REQUEST_ACCEPTED); - bitStream.Write(remoteSystem->systemAddress); - SystemIndex systemIndex = (SystemIndex) GetIndexFromSystemAddress( remoteSystem->systemAddress, true ); - RakAssert(systemIndex!=65535); - bitStream.Write(systemIndex); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - bitStream.Write(ipList[i]); - bitStream.Write(incomingTimestamp); - bitStream.Write(RakNet::GetTime()); - - SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, remoteSystem->systemAddress, false, false, RakNet::GetTimeUS(), 0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) -{ - RakNet::BitStream temp( sizeof(unsigned char) ); - temp.Write( (MessageID)ID_DISCONNECTION_NOTIFICATION ); - if (performImmediate) - { - SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, RELIABLE_ORDERED, orderingChannel, systemAddress, false, false, RakNet::GetTimeUS(), 0); - RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(systemAddress, true, true); - rss->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; - } - else - { - SendBuffered((const char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, RELIABLE_ORDERED, orderingChannel, systemAddress, false, RemoteSystemStruct::DISCONNECT_ASAP, 0); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::GetNumberOfRemoteInitiatedConnections( void ) const -{ - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - unsigned short numberOfIncomingConnections; - numberOfIncomingConnections = 0; - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED && - (activeSystemList[i])->weInitiatedTheConnection==false - ) - { - numberOfIncomingConnections++; - } - } - return numberOfIncomingConnections; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct * RakPeer::AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSmartPtr incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ) -{ - RemoteSystemStruct * remoteSystem; - unsigned i,j,assignedIndex; - RakNet::TimeMS time = RakNet::GetTimeMS(); -#ifdef _DEBUG - RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); -#endif - - if (limitConnectionFrequencyFromTheSameIP) - { - if (IsLoopbackAddress(systemAddress,false)==false) - { - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if ( remoteSystemList[ i ].isActive==true && - remoteSystemList[ i ].systemAddress.EqualsExcludingPort(systemAddress) && - time >= remoteSystemList[ i ].connectionTime && - time - remoteSystemList[ i ].connectionTime < 100 - ) - { - // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots - // Ignore connection attempts if this IP address connected within the last 100 milliseconds - *thisIPConnectedRecently=true; - ValidateRemoteSystemLookup(); - return 0; - } - } - } - } - - // Don't use a different port than what we received on - bindingAddress.CopyPort(incomingRakNetSocket->boundAddress); - - *thisIPConnectedRecently=false; - for ( assignedIndex = 0; assignedIndex < maximumNumberOfPeers; assignedIndex++ ) - { - if ( remoteSystemList[ assignedIndex ].isActive==false ) - { - remoteSystem=remoteSystemList+assignedIndex; - ReferenceRemoteSystem(systemAddress, assignedIndex); - remoteSystem->MTUSize=defaultMTUSize; - remoteSystem->guid=guid; - remoteSystem->isActive = true; // This one line causes future incoming packets to go through the reliability layer - // Reserve this reliability layer for ourselves. - if (incomingMTU > remoteSystem->MTUSize) - remoteSystem->MTUSize=incomingMTU; - remoteSystem->reliabilityLayer.Reset(true, remoteSystem->MTUSize, useSecurity); - remoteSystem->reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); - remoteSystem->reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); - remoteSystem->reliabilityLayer.SetTimeoutTime(defaultTimeoutTime); - AddToActiveSystemList(assignedIndex); - if (incomingRakNetSocket->boundAddress==bindingAddress) - { - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - else - { - char str[256]; - bindingAddress.ToString(true,str); - // See if this is an internal IP address. - // If so, force binding on it so we reply on the same IP address as they sent to. - unsigned int ipListIndex, foundIndex=(unsigned int)-1; - - for (ipListIndex=0; ipListIndex < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ipListIndex++) - { - if (ipList[ipListIndex]==UNASSIGNED_SYSTEM_ADDRESS) - break; - - if (bindingAddress.EqualsExcludingPort(ipList[ipListIndex])) - { - foundIndex=ipListIndex; - break; - } - } - - // 06/26/09 Unconfirmed report that Vista firewall blocks the reply if we force a binding - // For now use the incoming socket only - // Originally this code was to force a machine with multiple IP addresses to reply back on the IP - // that the datagram came in on - if (1 || foundIndex==(unsigned int)-1) - { - // Must not be an internal LAN address. Just use whatever socket it came in on - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - else - { - // Force binding - unsigned int socketListIndex; - for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) - { - if (socketList[socketListIndex]->boundAddress==bindingAddress) - { - // Force binding with existing socket - remoteSystem->rakNetSocket=socketList[socketListIndex]; - break; - } - } - - if (socketListIndex==socketList.Size()) - { - char ipListFoundIndexStr[128]; - ipList[foundIndex].ToString(false,str); - - // Force binding with new socket - RakNetSmartPtr rns(RakNet::OP_NEW(_FILE_AND_LINE_)); - if (incomingRakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2==0) - rns->s = (unsigned int) SocketLayer::CreateBoundSocket( bindingAddress.GetPort(), true, ipListFoundIndexStr, 0, incomingRakNetSocket->extraSocketOptions, incomingRakNetSocket->socketFamily ); - else - rns->s = (unsigned int) SocketLayer::CreateBoundSocket_PS3Lobby( bindingAddress.GetPort(), true, ipListFoundIndexStr, incomingRakNetSocket->socketFamily ); - if ((SOCKET)rns->s==(SOCKET)-1) - { - // Can't bind. Just use whatever socket it came in on - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - else - { - rns->boundAddress=bindingAddress; - rns->remotePortRakNetWasStartedOn_PS3_PSP2=incomingRakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2; - rns->extraSocketOptions=incomingRakNetSocket->extraSocketOptions; - rns->userConnectionSocketIndex=(unsigned int)-1; - socketList.Push(rns, _FILE_AND_LINE_ ); - remoteSystem->rakNetSocket=rns; - - RakPeerAndIndex rpai; - rpai.remotePortRakNetWasStartedOn_PS3=rns->remotePortRakNetWasStartedOn_PS3_PSP2; - rpai.extraSocketOptions=rns->extraSocketOptions; - rpai.s=rns->s; - rpai.rakPeer=this; -#ifdef _WIN32 - int highPriority=THREAD_PRIORITY_ABOVE_NORMAL; -#else - int highPriority=-10; -#endif - -//#if !defined(_XBOX) && !defined(X360) - highPriority=0; -//#endif - - - - - - - - RakNet::RakThread::Create(RecvFromLoop, &rpai, highPriority); - - RakSleep(10); - - - /* -#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - if (threadSleepTimer>0) - { - rns->recvEvent=CreateEvent(0,FALSE,FALSE,0); - WSAEventSelect(rns->s,rns->recvEvent,FD_READ); - } -#endif - */ - } - } - } - } - - for ( j = 0; j < (unsigned) PING_TIMES_ARRAY_SIZE; j++ ) - { - remoteSystem->pingAndClockDifferential[ j ].pingTime = 65535; - remoteSystem->pingAndClockDifferential[ j ].clockDifferential = 0; - } - - remoteSystem->connectMode=connectionMode; - remoteSystem->pingAndClockDifferentialWriteIndex = 0; - remoteSystem->lowestPing = 65535; - remoteSystem->nextPingTime = 0; // Ping immediately - remoteSystem->weInitiatedTheConnection = false; - remoteSystem->connectionTime = time; - remoteSystem->myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystem->lastReliableSend=time; - -#ifdef _DEBUG - int indexLoopupCheck=GetIndexFromSystemAddress( systemAddress, true ); - if ((int) indexLoopupCheck!=(int) assignedIndex) - { - RakAssert((int) indexLoopupCheck==(int) assignedIndex); - } -#endif - - return remoteSystem; - } - } - - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Adjust the first four bytes (treated as unsigned int) of the pointer -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const -{ -#ifdef _DEBUG - RakAssert( IsActive() ); - RakAssert( data ); -#endif - - RakNet::BitStream timeBS( data, sizeof(RakNet::Time), false); - RakNet::Time encodedTimestamp; - timeBS.Read(encodedTimestamp); - - encodedTimestamp = encodedTimestamp - GetBestClockDifferential( systemAddress ); - timeBS.SetWriteOffset(0); - timeBS.Write(encodedTimestamp); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm -RakNet::Time RakPeer::GetBestClockDifferential( const SystemAddress systemAddress ) const -{ - int counter, lowestPingSoFar; - RakNet::Time clockDifferential; - RemoteSystemStruct *remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - - if ( remoteSystem == 0 ) - return 0; - - lowestPingSoFar = 65535; - - clockDifferential = 0; - - for ( counter = 0; counter < PING_TIMES_ARRAY_SIZE; counter++ ) - { - if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime == 65535 ) - break; - - if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime < lowestPingSoFar ) - { - clockDifferential = remoteSystem->pingAndClockDifferential[ counter ].clockDifferential; - lowestPingSoFar = remoteSystem->pingAndClockDifferential[ counter ].pingTime; - } - } - - return clockDifferential; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::RemoteSystemLookupHashIndex(const SystemAddress &sa) const -{ - return SystemAddress::ToInteger(sa) % ((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex) -{ -// #ifdef _DEBUG -// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// RakAssert(hashIndex==remoteSystemIndex); -// } -// } -// #endif - - - SystemAddress oldAddress = remoteSystemList[remoteSystemListIndex].systemAddress; - if (oldAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - // The system might be active if rerouting -// RakAssert(remoteSystemList[remoteSystemListIndex].isActive==false); - - // Remove the reference if the reference is pointing to this inactive system - if (GetRemoteSystem(oldAddress)==&remoteSystemList[remoteSystemListIndex]) - DereferenceRemoteSystem(oldAddress); - } - DereferenceRemoteSystem(sa); - -// #ifdef _DEBUG -// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// if (hashIndex!=remoteSystemIndex) -// { -// RakAssert(hashIndex==remoteSystemIndex); -// } -// } -// } -// #endif - - - remoteSystemList[remoteSystemListIndex].systemAddress=sa; - - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *rsi; - rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); - if (remoteSystemLookup[hashIndex]==0) - { - rsi->next=0; - rsi->index=remoteSystemListIndex; - remoteSystemLookup[hashIndex]=rsi; - } - else - { - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - while (cur->next!=0) - { - cur=cur->next; - } - - rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); - rsi->next=0; - rsi->index=remoteSystemListIndex; - cur->next=rsi; - } - -// #ifdef _DEBUG -// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// RakAssert(hashIndex==remoteSystemIndex); -// } -// } -// #endif - - - RakAssert(GetRemoteSystemIndex(sa)==remoteSystemListIndex); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DereferenceRemoteSystem(const SystemAddress &sa) -{ - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - RemoteSystemIndex *last = 0; - while (cur!=0) - { - if (remoteSystemList[cur->index].systemAddress==sa) - { - if (last==0) - { - remoteSystemLookup[hashIndex]=cur->next; - } - else - { - last->next=cur->next; - } - remoteSystemIndexPool.Release(cur,_FILE_AND_LINE_); - break; - } - last=cur; - cur=cur->next; - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetRemoteSystemIndex(const SystemAddress &sa) const -{ - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - while (cur!=0) - { - if (remoteSystemList[cur->index].systemAddress==sa) - return cur->index; - cur=cur->next; - } - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct* RakPeer::GetRemoteSystem(const SystemAddress &sa) const -{ - unsigned int remoteSystemIndex = GetRemoteSystemIndex(sa); - if (remoteSystemIndex==(unsigned int)-1) - return 0; - return remoteSystemList + remoteSystemIndex; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearRemoteSystemLookup(void) -{ - remoteSystemIndexPool.Clear(_FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(remoteSystemLookup,_FILE_AND_LINE_); - remoteSystemLookup=0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToActiveSystemList(unsigned int remoteSystemListIndex) -{ - activeSystemList[activeSystemListSize++]=remoteSystemList+remoteSystemListIndex; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromActiveSystemList(const SystemAddress &sa) -{ - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - RemoteSystemStruct *rss=activeSystemList[i]; - if (rss->systemAddress==sa) - { - activeSystemList[i]=activeSystemList[activeSystemListSize-1]; - activeSystemListSize--; - return; - } - } - RakAssert("activeSystemList invalid, entry not found in RemoveFromActiveSystemList. Ensure that AddToActiveSystemList and RemoveFromActiveSystemList are called by the same thread." && 0); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/* -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::LookupIndexUsingHashIndex(const SystemAddress &sa) const -{ - unsigned int scanCount=0; - unsigned int index = RemoteSystemLookupHashIndex(sa); - if (remoteSystemLookup[index].index==(unsigned int)-1) - return (unsigned int) -1; - while (remoteSystemList[remoteSystemLookup[index].index].systemAddress!=sa) - { - if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - index=0; - if (++scanCount>(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - return (unsigned int) -1; - if (remoteSystemLookup[index].index==-1) - return (unsigned int) -1; - } - return index; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const -{ - unsigned int index = LookupIndexUsingHashIndex(sa); - if (index!=(unsigned int) -1) - { - return remoteSystemLookup[index].index; - } - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const -{ -// unsigned int collisionCount=0; - unsigned int index = RemoteSystemLookupHashIndex(sa); - while (remoteSystemLookup[index].index!=(unsigned int)-1) - { - if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - index=0; -// collisionCount++; - } -// printf("%i collisions. Using index %i\n", collisionCount, index); - return index; -} -*/ -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const -{ - if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return systemIdentifier.rakNetGuid==myGuid; - - for (int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS; i++) - { - if (matchPort) - { - if (ipList[i]==systemIdentifier.systemAddress) - return true; - } - else - { - if (ipList[i].EqualsExcludingPort(systemIdentifier.systemAddress)) - return true; - } - } - - return (matchPort==true && systemIdentifier.systemAddress==firstExternalID) || - (matchPort==false && systemIdentifier.systemAddress.EqualsExcludingPort(firstExternalID)); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetLoopbackAddress(void) const -{ - - return ipList[0]; - - - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AllowIncomingConnections(void) const -{ - return GetNumberOfRemoteInitiatedConnections() < GetMaximumIncomingConnections(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PingInternal( const SystemAddress target, bool performImmediate, PacketReliability reliability ) -{ - if ( IsActive() == false ) - return ; - - RakNet::BitStream bitStream(sizeof(unsigned char)+sizeof(RakNet::Time)); - bitStream.Write((MessageID)ID_CONNECTED_PING); - bitStream.Write(RakNet::GetTime()); - if (performImmediate) - SendImmediate( (char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, reliability, 0, target, false, false, RakNet::GetTimeUS(), 0 ); - else - Send( &bitStream, IMMEDIATE_PRIORITY, reliability, 0, target, false ); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) -{ -#ifdef _DEBUG - RakAssert(orderingChannel < 32); -#endif - - if (systemIdentifier.IsUndefined()) - return; - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - SystemAddress target; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - target=systemIdentifier.systemAddress; - } - else - { - target=GetSystemAddressFromGuid(systemIdentifier.rakNetGuid); - } - - if (target!=UNASSIGNED_SYSTEM_ADDRESS && performImmediate) - target.FixForIPVersion(socketList[0]->boundAddress); - - if (sendDisconnectionNotification) - { - NotifyAndFlagForShutdown(target, performImmediate, orderingChannel, disconnectionNotificationPriority); - } - else - { - if (performImmediate) - { - unsigned int index = GetRemoteSystemIndex(target); - if (index!=(unsigned int) -1) - { - if ( remoteSystemList[index].isActive ) - { - RemoveFromActiveSystemList(target); - - // Found the index to stop - remoteSystemList[index].isActive = false; - - remoteSystemList[index].guid=UNASSIGNED_RAKNET_GUID; - - // Reserve this reliability layer for ourselves - //remoteSystemList[ remoteSystemLookup[index].index ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; - - // Clear any remaining messages - remoteSystemList[index].reliabilityLayer.Reset(false, remoteSystemList[index].MTUSize, false); - - // Not using this socket - remoteSystemList[index].rakNetSocket.SetNull(); - } - } - } - else - { - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_CLOSE_CONNECTION; - bcs->systemIdentifier=target; - bcs->data=0; - bcs->orderingChannel=orderingChannel; - bcs->priority=disconnectionNotificationPriority; - bufferedCommands.Push(bcs); - } - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendBuffered( const char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) -{ - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = (char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES(numberOfBitsToSend), _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy - if (bcs->data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - return; - } - memcpy(bcs->data, data, (size_t) BITS_TO_BYTES(numberOfBitsToSend)); - bcs->numberOfBitsToSend=numberOfBitsToSend; - bcs->priority=priority; - bcs->reliability=reliability; - bcs->orderingChannel=orderingChannel; - bcs->systemIdentifier=systemIdentifier; - bcs->broadcast=broadcast; - bcs->connectionMode=connectionMode; - bcs->receipt=receipt; - bcs->command=BufferedCommandStruct::BCS_SEND; - bufferedCommands.Push(bcs); - - if (priority==IMMEDIATE_PRIORITY) - { - // Forces pending sends to go out now, rather than waiting to the next update interval - quitAndDataEvents.SetEvent(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendBufferedList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) -{ - BufferedCommandStruct *bcs; - unsigned int totalLength=0; - unsigned int lengthOffset; - int i; - for (i=0; i < numParameters; i++) - { - if (lengths[i]>0) - totalLength+=lengths[i]; - } - if (totalLength==0) - return; - - char *dataAggregate; - dataAggregate = (char*) rakMalloc_Ex( (size_t) totalLength, _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy - if (dataAggregate==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return; - } - for (i=0, lengthOffset=0; i < numParameters; i++) - { - if (lengths[i]>0) - { - memcpy(dataAggregate+lengthOffset, data[i], lengths[i]); - lengthOffset+=lengths[i]; - } - } - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback(dataAggregate,totalLength); - rakFree_Ex(dataAggregate,_FILE_AND_LINE_); - return; - } - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = dataAggregate; - bcs->numberOfBitsToSend=BYTES_TO_BITS(totalLength); - bcs->priority=priority; - bcs->reliability=reliability; - bcs->orderingChannel=orderingChannel; - bcs->systemIdentifier=systemIdentifier; - bcs->broadcast=broadcast; - bcs->connectionMode=connectionMode; - bcs->receipt=receipt; - bcs->command=BufferedCommandStruct::BCS_SEND; - bufferedCommands.Push(bcs); - - if (priority==IMMEDIATE_PRIORITY) - { - // Forces pending sends to go out now, rather than waiting to the next update interval - quitAndDataEvents.SetEvent(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::SendImmediate( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, RakNet::TimeUS currentTime, uint32_t receipt ) -{ - unsigned *sendList; - unsigned sendListSize; - bool callerDataAllocationUsed; - unsigned int remoteSystemIndex, sendListIndex; // Iterates into the list of remote systems -// unsigned numberOfBytesUsed = (unsigned) BITS_TO_BYTES(numberOfBitsToSend); - callerDataAllocationUsed=false; - - sendListSize=0; - - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - remoteSystemIndex=GetIndexFromSystemAddress( systemIdentifier.systemAddress, true ); - else if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - remoteSystemIndex=GetSystemIndexFromGuid(systemIdentifier.rakNetGuid); - else - remoteSystemIndex=(unsigned int) -1; - - // 03/06/06 - If broadcast is false, use the optimized version of GetIndexFromSystemAddress - if (broadcast==false) - { - if (remoteSystemIndex==(unsigned int) -1) - { -#ifdef _DEBUG -// int debugIndex = GetRemoteSystemIndex(systemIdentifier.systemAddress); -#endif - return false; - } - - - sendList=(unsigned *)alloca(sizeof(unsigned)); - - - - - if (remoteSystemList[remoteSystemIndex].isActive && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ON_NO_ACK) - { - sendList[0]=remoteSystemIndex; - sendListSize=1; - } - } - else - { - - //sendList=(unsigned *)alloca(sizeof(unsigned)*remoteSystemListSize); - sendList=(unsigned *)alloca(sizeof(unsigned)*maximumNumberOfPeers); - - - - - - // remoteSystemList in network thread - unsigned int idx; - for ( idx = 0; idx < maximumNumberOfPeers; idx++ ) - { - if (remoteSystemIndex!=(unsigned int) -1 && idx==remoteSystemIndex) - continue; - - if ( remoteSystemList[ idx ].isActive && remoteSystemList[ idx ].systemAddress != UNASSIGNED_SYSTEM_ADDRESS ) - sendList[sendListSize++]=idx; - } - } - - if (sendListSize==0) - { - - - - return false; - } - - for (sendListIndex=0; sendListIndex < sendListSize; sendListIndex++) - { - // Send may split the packet and thus deallocate data. Don't assume data is valid if we use the callerAllocationData - bool useData = useCallerDataAllocation && callerDataAllocationUsed==false && sendListIndex+1==sendListSize; - remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( data, numberOfBitsToSend, priority, reliability, orderingChannel, useData==false, remoteSystemList[sendList[sendListIndex]].MTUSize, currentTime, receipt ); - if (useData) - callerDataAllocationUsed=true; - - if (reliability==RELIABLE || - reliability==RELIABLE_ORDERED || - reliability==RELIABLE_SEQUENCED || - reliability==RELIABLE_WITH_ACK_RECEIPT || - reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT -// || -// reliability==RELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - remoteSystemList[sendList[sendListIndex]].lastReliableSend=(RakNet::TimeMS)(currentTime/(RakNet::TimeUS)1000); - } - - - - - - // Return value only meaningful if true was passed for useCallerDataAllocation. Means the reliability layer used that data copy, so the caller should not deallocate it - return callerDataAllocationUsed; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ResetSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - sendReceiptSerial=1; - sendReceiptSerialMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::OnConnectedPong(RakNet::Time sendPingTime, RakNet::Time sendPongTime, RemoteSystemStruct *remoteSystem) -{ - RakNet::Time ping; -// RakNet::TimeMS lastPing; - RakNet::Time time = RakNet::GetTime(); // Update the time value to be accurate - if (time > sendPingTime) - ping = time - sendPingTime; - else - ping=0; - -// lastPing = remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime; - - remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime = ( unsigned short ) ping; - // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm - // Divide each integer by 2, rather than the sum by 2, to prevent overflow - remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].clockDifferential = sendPongTime - ( time/2 + sendPingTime/2 ); - - if ( remoteSystem->lowestPing == (unsigned short)-1 || remoteSystem->lowestPing > (int) ping ) - remoteSystem->lowestPing = (unsigned short) ping; - - if ( ++( remoteSystem->pingAndClockDifferentialWriteIndex ) == (RakNet::Time) PING_TIMES_ARRAY_SIZE ) - remoteSystem->pingAndClockDifferentialWriteIndex = 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBufferedCommands(void) -{ - BufferedCommandStruct *bcs; - - while ((bcs=bufferedCommands.Pop())!=0) - { - if (bcs->data) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - } - bufferedCommands.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBufferedPackets(void) -{ - RecvFromStruct *bcs; - - while ((bcs=bufferedPackets.Pop())!=0) - { - bufferedPackets.Deallocate(bcs, _FILE_AND_LINE_); - } - bufferedPackets.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearSocketQueryOutput(void) -{ - socketQueryOutput.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearRequestedConnectionList(void) -{ - DataStructures::Queue freeQueue; - requestedConnectionQueueMutex.Lock(); - while (requestedConnectionQueue.Size()) - freeQueue.Push(requestedConnectionQueue.Pop(), _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - unsigned i; - for (i=0; i < freeQueue.Size(); i++) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: In ClearRequestedConnectionList(), Deleting freeQueue index %i client_handshake %x\n", i, freeQueue[i]->client_handshake); - RakNet::OP_DELETE(freeQueue[i]->client_handshake,_FILE_AND_LINE_); -#endif - RakNet::OP_DELETE(freeQueue[i], _FILE_AND_LINE_ ); - } -} -inline void RakPeer::AddPacketToProducer(RakNet::Packet *p) -{ - packetReturnMutex.Lock(); - packetReturnQueue.Push(p,_FILE_AND_LINE_); - packetReturnMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -union Buff6AndBuff8 -{ - unsigned char buff6[6]; - uint64_t buff8; -}; -uint64_t RakPeerInterface::Get64BitUniqueRandomNumber(void) -{ - // Mac address is a poor solution because you can't have multiple connections from the same system - - - - - - - - - - - - - -#if defined(_WIN32) - uint64_t g=RakNet::GetTimeUS(); - - RakNet::TimeUS lastTime, thisTime; - int j; - // Sleep a small random time, then use the last 4 bits as a source of randomness - for (j=0; j < 8; j++) - { - lastTime = RakNet::GetTimeUS(); - RakSleep(1); - RakSleep(0); - thisTime = RakNet::GetTimeUS(); - RakNet::TimeUS diff = thisTime-lastTime; - unsigned int diff4Bits = (unsigned int) (diff & 15); - diff4Bits <<= 32-4; - diff4Bits >>= j*4; - ((char*)&g)[j] ^= diff4Bits; - } - return g; - -#else - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_usec + tv.tv_sec * 1000000; -#endif -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GenerateGUID(void) -{ - myGuid.g=Get64BitUniqueRandomNumber(); - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// void RakNet::ProcessPortUnreachable( SystemAddress systemAddress, RakPeer *rakPeer ) -// { -// (void) binaryAddress; -// (void) port; -// (void) rakPeer; -// -// } -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -namespace RakNet { -bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, bool *isOfflineMessage, RakNet::TimeUS timeRead ) -{ - (void) timeRead; - RakPeer::RemoteSystemStruct *remoteSystem; - RakNet::Packet *packet; - unsigned i; - - - char str1[64]; - systemAddress.ToString(false, str1); - if (rakPeer->IsBanned( str1 )) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - RakNet::BitStream bs; - bs.Write((MessageID)ID_CONNECTION_BANNED); - bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - unsigned i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (char*) bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - - - - // The reason for all this is that the reliability layer has no way to tell between offline messages that arrived late for a player that is now connected, - // and a regular encoding. So I insert OFFLINE_MESSAGE_DATA_ID into the stream, the encoding of which is essentially impossible to hit by chance - if (length <=2) - { - *isOfflineMessage=true; - } - else if ( - ((unsigned char)data[0] == ID_UNCONNECTED_PING || - (unsigned char)data[0] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS) && - length == sizeof(unsigned char) + sizeof(RakNet::Time) + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(RakNet::Time), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ((unsigned char)data[0] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char) + sizeof(RakNet::TimeMS) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(RakNet::Time) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ( - (unsigned char)data[0] == ID_OUT_OF_BAND_INTERNAL && - (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ( - ( - (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_1 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_2 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_1 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_2 || - (unsigned char)data[0] == ID_CONNECTION_ATTEMPT_FAILED || - (unsigned char)data[0] == ID_NO_FREE_INCOMING_CONNECTIONS || - (unsigned char)data[0] == ID_CONNECTION_BANNED || - (unsigned char)data[0] == ID_ALREADY_CONNECTED || - (unsigned char)data[0] == ID_IP_RECENTLY_CONNECTED) && - (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if (((unsigned char)data[0] == ID_INCOMPATIBLE_PROTOCOL_VERSION&& - (size_t) length == sizeof(MessageID)*2 + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID))) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID)*2, OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else - { - *isOfflineMessage=false; - } - - if (*isOfflineMessage) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - // These are all messages from unconnected systems. Messages here can be any size, but are never processed from connected systems. - if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS - || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && length == sizeof(unsigned char)+sizeof(RakNet::Time)+sizeof(OFFLINE_MESSAGE_DATA_ID) ) - { - if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || - rakPeer->AllowIncomingConnections() ) // Open connections with players - { -// #if !defined(_XBOX) && !defined(X360) - RakNet::BitStream inBitStream( (unsigned char *) data, length, false ); - inBitStream.IgnoreBits(8); - RakNet::Time sendPingTime; - inBitStream.Read(sendPingTime); - - RakNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_UNCONNECTED_PONG); // Should be named ID_UNCONNECTED_PONG eventually - outBitStream.Write(sendPingTime); - outBitStream.Write(rakPeer->myGuid); - outBitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - - rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Lock(); - // They are connected, so append offline ping data - outBitStream.Write( (char*)rakPeer->offlinePingResponse.GetData(), rakPeer->offlinePingResponse.GetNumberOfBytesUsed() ); - rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Unlock(); - - unsigned i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*)outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), systemAddress); - - SocketLayer::SendTo( rakNetSocket->s, (const char*)outBitStream.GetData(), (unsigned int) outBitStream.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - packet=rakPeer->AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - packet->data[0]=data[0]; - packet->systemAddress = systemAddress; - packet->guid=UNASSIGNED_RAKNET_GUID; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); -// #endif - } - } - // UNCONNECTED MESSAGE Pong with no data. - else if ((unsigned char) data[ 0 ] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID) && (size_t) length < sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID)+MAX_OFFLINE_DATA_LENGTH) - { - packet=rakPeer->AllocPacket((unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(RakNet::Time)+sizeof(RakNet::TimeMS)), _FILE_AND_LINE_); - RakNet::BitStream bsIn((unsigned char*) data, length, false); - bsIn.IgnoreBytes(sizeof(unsigned char)); - RakNet::Time ping; - bsIn.Read(ping); - bsIn.Read(packet->guid); - - RakNet::BitStream bsOut((unsigned char*) packet->data, packet->length, false); - bsOut.ResetWritePointer(); - bsOut.Write((unsigned char)ID_UNCONNECTED_PONG); - RakNet::TimeMS pingMS=(RakNet::TimeMS)ping; - bsOut.Write(pingMS); - bsOut.WriteAlignedBytes( - (const unsigned char*)data+sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID), - length-sizeof(unsigned char)-sizeof(RakNet::Time)-RakNetGUID::size()-sizeof(OFFLINE_MESSAGE_DATA_ID) - ); - - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - else if ((unsigned char) data[ 0 ] == ID_OUT_OF_BAND_INTERNAL && - (size_t) length < MAX_OFFLINE_DATA_LENGTH+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size()) - { - unsigned int dataLength = (unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MessageID)); - RakAssert(dataLength<1024); - packet=rakPeer->AllocPacket(dataLength+1, _FILE_AND_LINE_); - RakAssert(packet->length<1024); - - RakNet::BitStream bs2((unsigned char*) data, length, false); - bs2.IgnoreBytes(sizeof(MessageID)); - bs2.Read(packet->guid); - - if (data[sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size()]==ID_ADVERTISE_SYSTEM) - { - packet->length--; - packet->bitSize=BYTES_TO_BITS(packet->length); - packet->data[0]=ID_ADVERTISE_SYSTEM; - memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)*2 + RakNetGUID::size(), dataLength-1); - } - else - { - packet->data[0]=ID_OUT_OF_BAND_INTERNAL; - memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size(), dataLength); - } - - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_1) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - RakNet::BitStream bsIn((unsigned char*) data,length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bsIn.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID serverGuid; - bsIn.Read(serverGuid); - unsigned char serverHasSecurity; - uint32_t cookie; - (void) cookie; - bsIn.Read(serverHasSecurity); - // Even if the server has security, it may not be required of us if we are in the security exception list - if (serverHasSecurity) - { - bsIn.Read(cookie); - } - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_2); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - if (serverHasSecurity) - bsOut.Write(cookie); - - unsigned i; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - RakPeer::RequestedConnectionStruct *rcs; - rcs=rakPeer->requestedConnectionQueue[i]; - if (rcs->systemAddress==systemAddress) - { - if (serverHasSecurity) - { -#if LIBCAT_SECURITY==1 - unsigned char public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; - bsIn.ReadAlignedBytes(public_key, sizeof(public_key)); - - if (rcs->publicKeyMode==PKM_ACCEPT_ANY_PUBLIC_KEY) - { - memcpy(rcs->remote_public_key, public_key, cat::EasyHandshake::PUBLIC_KEY_BYTES); - if (!rcs->client_handshake->Initialize(public_key) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Server passed a bad public key with PKM_ACCEPT_ANY_PUBLIC_KEY"); - return true; - } - } - - if (cat::SecureEqual(public_key, - rcs->remote_public_key, - cat::EasyHandshake::PUBLIC_KEY_BYTES)==false) - { - rakPeer->requestedConnectionQueueMutex.Unlock(); - CAT_AUDIT_PRINTF("AUDIT: Expected public key does not match what was sent by server -- Reporting back ID_PUBLIC_KEY_MISMATCH to user\n"); - - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_PUBLIC_KEY_MISMATCH; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=serverGuid; - rakPeer->AddPacketToProducer(packet); - return true; - } - - if (rcs->client_handshake==0) - { - // Message does not contain a challenge - // We might still pass if we are in the security exception list - bsOut.Write((unsigned char)0); - } - else - { - // Message contains a challenge - bsOut.Write((unsigned char)1); - // challenge - CAT_AUDIT_PRINTF("AUDIT: Sending challenge\n"); - bsOut.WriteAlignedBytes((const unsigned char*) rcs->handshakeChallenge,cat::EasyHandshake::CHALLENGE_BYTES); - } -#else // LIBCAT_SECURITY - // Message does not contain a challenge - bsOut.Write((unsigned char)0); -#endif // LIBCAT_SECURITY - } - else - { - // Server does not need security -#if LIBCAT_SECURITY==1 - if (rcs->client_handshake!=0) - { - rakPeer->requestedConnectionQueueMutex.Unlock(); - CAT_AUDIT_PRINTF("AUDIT: Security disabled by server but we expected security (indicated by client_handshake not null) so failing!\n"); - - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_OUR_SYSTEM_REQUIRES_SECURITY; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=serverGuid; - rakPeer->AddPacketToProducer(packet); - return true; - } -#endif // LIBCAT_SECURITY - - } - - uint16_t mtu; - bsIn.Read(mtu); - - // Binding address - bsOut.Write(rcs->systemAddress); - rakPeer->requestedConnectionQueueMutex.Unlock(); - // MTU - bsOut.Write(mtu); - // Our guid - bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), rcs->systemAddress); - - SocketLayer::SendTo( rakPeer->socketList[rcs->socketIndex]->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), rcs->systemAddress, rakPeer->socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, rakPeer->socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - } - rakPeer->requestedConnectionQueueMutex.Unlock(); - } - else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_2) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - RakNet::BitStream bs((unsigned char*) data,length,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID guid; - bs.Read(guid); - SystemAddress bindingAddress; - bool b = bs.Read(bindingAddress); - RakAssert(b); - uint16_t mtu; - b=bs.Read(mtu); - RakAssert(b); - bool doSecurity=false; - b=bs.Read(doSecurity); - RakAssert(b); - -#if LIBCAT_SECURITY==1 - char answer[cat::EasyHandshake::ANSWER_BYTES]; - CAT_AUDIT_PRINTF("AUDIT: Got ID_OPEN_CONNECTION_REPLY_2 and given doSecurity=%i\n", (int)doSecurity); - if (doSecurity) - { - CAT_AUDIT_PRINTF("AUDIT: Reading cookie and public key\n"); - bs.ReadAlignedBytes((unsigned char*) answer, sizeof(answer)); - } - cat::ClientEasyHandshake *client_handshake=0; -#endif // LIBCAT_SECURITY - - RakPeer::RequestedConnectionStruct *rcs; - bool unlock=true; - unsigned i; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - rcs=rakPeer->requestedConnectionQueue[i]; - - - if (rcs->systemAddress==systemAddress) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: System address matches an entry in the requestedConnectionQueue and doSecurity=%i\n", (int)doSecurity); - if (doSecurity) - { - if (rcs->client_handshake==0) - { - CAT_AUDIT_PRINTF("AUDIT: Server wants security but we didn't set a public key -- Reporting back ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY to user\n"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - - packet=rakPeer->AllocPacket(2, _FILE_AND_LINE_); - packet->data[ 0 ] = ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY; // Attempted a connection and couldn't - packet->data[ 1 ] = 0; // Indicate server public key is missing - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - return true; - } - - CAT_AUDIT_PRINTF("AUDIT: Looks good, preparing to send challenge to server! client_handshake = %x\n", client_handshake); - } - -#endif // LIBCAT_SECURITY - - rakPeer->requestedConnectionQueueMutex.Unlock(); - unlock=false; - - RakAssert(rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT); - // You might get this when already connected because of cross-connections - bool thisIPConnectedRecently=false; - remoteSystem=rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - if (remoteSystem==0) - { - if (rcs->socket.IsNull()) - { - remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); - } - else - { - remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rcs->socket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); - } - } - - // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots - // Ignore connection attempts if this IP address connected within the last 100 milliseconds - if (thisIPConnectedRecently==false) - { - // Don't check GetRemoteSystemFromGUID, server will verify - if (remoteSystem) - { - // Move pointer from RequestedConnectionStruct to RemoteSystemStruct -#if LIBCAT_SECURITY==1 - cat::u8 ident[cat::EasyHandshake::IDENTITY_BYTES]; - bool doIdentity = false; - - if (rcs->client_handshake) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer\n"); - if (rcs->publicKeyMode == PKM_USE_TWO_WAY_AUTHENTICATION) - { - if (!rcs->client_handshake->ProcessAnswerWithIdentity(answer, ident, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - - return true; - } - - doIdentity = true; - } - else - { - if (!rcs->client_handshake->ProcessAnswer(answer, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - - return true; - } - } - CAT_AUDIT_PRINTF("AUDIT: Success!\n"); - - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - } -#endif // LIBCAT_SECURITY - - remoteSystem->weInitiatedTheConnection=true; - remoteSystem->connectMode=RakPeer::RemoteSystemStruct::REQUESTED_CONNECTION; - if (rcs->timeoutTime!=0) - remoteSystem->reliabilityLayer.SetTimeoutTime(rcs->timeoutTime); - - RakNet::BitStream temp; - temp.Write( (MessageID)ID_CONNECTION_REQUEST); - temp.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - temp.Write(RakNet::GetTime()); - -#if LIBCAT_SECURITY==1 - temp.Write((unsigned char)(doSecurity ? 1 : 0)); - - if (doSecurity) - { - unsigned char proof[32]; - remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->GenerateProof(proof, sizeof(proof)); - temp.WriteAlignedBytes(proof, sizeof(proof)); - - temp.Write((unsigned char)(doIdentity ? 1 : 0)); - - if (doIdentity) - { - temp.WriteAlignedBytes(ident, sizeof(ident)); - } - } -#else - temp.Write((unsigned char)0); -#endif // LIBCAT_SECURITY - - if ( rcs->outgoingPasswordLength > 0 ) - temp.Write( ( char* ) rcs->outgoingPassword, rcs->outgoingPasswordLength ); - - rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, timeRead, 0 ); - } - else - { - // Failed, no connections available anymore - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - } - } - - rakPeer->requestedConnectionQueueMutex.Lock(); - for (unsigned int k=0; k < rakPeer->requestedConnectionQueue.Size(); k++) - { - if (rakPeer->requestedConnectionQueue[k]->systemAddress==systemAddress) - { - rakPeer->requestedConnectionQueue.RemoveAtIndex(k); - break; - } - } - rakPeer->requestedConnectionQueueMutex.Unlock(); - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Deleting client_handshake object %x and rcs->client_handshake object %x\n", client_handshake, rcs->client_handshake); - RakNet::OP_DELETE(client_handshake,_FILE_AND_LINE_); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif // LIBCAT_SECURITY - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - - break; - } - } - - if (unlock) - rakPeer->requestedConnectionQueueMutex.Unlock(); - - return true; - - } - else if ((unsigned char)(data)[0] == (MessageID)ID_CONNECTION_ATTEMPT_FAILED || - (unsigned char)(data)[0] == (MessageID)ID_NO_FREE_INCOMING_CONNECTIONS || - (unsigned char)(data)[0] == (MessageID)ID_CONNECTION_BANNED || - (unsigned char)(data)[0] == (MessageID)ID_ALREADY_CONNECTED || - (unsigned char)(data)[0] == (MessageID)ID_INVALID_PASSWORD || - (unsigned char)(data)[0] == (MessageID)ID_IP_RECENTLY_CONNECTED || - (unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) - { - - RakNet::BitStream bs((unsigned char*) data,length,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - if ((unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) - bs.IgnoreBytes(sizeof(unsigned char)); - - RakNetGUID guid; - bs.Read(guid); - - RakPeer::RequestedConnectionStruct *rcs; - bool connectionAttemptCancelled=false; - unsigned i; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - rcs=rakPeer->requestedConnectionQueue[i]; - if (rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT && rcs->systemAddress==systemAddress) - { - connectionAttemptCancelled=true; - rakPeer->requestedConnectionQueue.RemoveAtIndex(i); - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Connection attempt canceled so deleting rcs->client_handshake object %x\n", rcs->client_handshake); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif // LIBCAT_SECURITY - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - break; - } - } - - rakPeer->requestedConnectionQueueMutex.Unlock(); - - if (connectionAttemptCancelled) - { - // Tell user of connection attempt failed - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = data[0]; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - } - } - else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_1 && length > (int) (1+sizeof(OFFLINE_MESSAGE_DATA_ID))) - {/* - static int x = 0; - ++x; - - SystemAddress *addr = (SystemAddress*)&systemAddress; - addr->binaryAddress += x;*/ - - unsigned int i; - //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); - char remoteProtocol=data[1+sizeof(OFFLINE_MESSAGE_DATA_ID)]; - if (remoteProtocol!=RAKNET_PROTOCOL_VERSION) - { - RakNet::BitStream bs; - bs.Write((MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION); - bs.Write((unsigned char)RAKNET_PROTOCOL_VERSION); - bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*)bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - - SocketLayer::SendTo( rakNetSocket->s, (char*)bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - return true; - } - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_OPEN_CONNECTION_REPLY_1); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); -#if LIBCAT_SECURITY==1 - if (rakPeer->_using_security) - { - bsOut.Write((unsigned char) 1); // HasCookie Yes - // Write cookie - uint32_t cookie = rakPeer->_cookie_jar->Generate(&systemAddress.address,sizeof(systemAddress.address)); - CAT_AUDIT_PRINTF("AUDIT: Writing cookie %i to %i:%i\n", cookie, systemAddress); - bsOut.Write(cookie); - // Write my public key - bsOut.WriteAlignedBytes((const unsigned char *) rakPeer->my_public_key,sizeof(rakPeer->my_public_key)); - } - else -#endif // LIBCAT_SECURITY - bsOut.Write((unsigned char) 0); // HasCookie No - - // MTU - bsOut.Write((uint16_t) (length+UDP_HEADER_SIZE)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - } - else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_2) - { - SystemAddress bindingAddress; - RakNetGUID guid; - RakNet::BitStream bsOut; - RakNet::BitStream bs((unsigned char*) data, length, false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - - bool requiresSecurityOfThisClient=false; -#if LIBCAT_SECURITY==1 - char remoteHandshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; - - if (rakPeer->_using_security) - { - char str1[64]; - systemAddress.ToString(false, str1); - requiresSecurityOfThisClient=rakPeer->IsInSecurityExceptionList(str1)==false; - - uint32_t cookie; - bs.Read(cookie); - CAT_AUDIT_PRINTF("AUDIT: Got cookie %i from %i:%i\n", cookie, systemAddress); - if (rakPeer->_cookie_jar->Verify(&systemAddress.address,sizeof(systemAddress.address), cookie)==false) - { - return true; - } - CAT_AUDIT_PRINTF("AUDIT: Cookie good!\n"); - - unsigned char clientWroteChallenge; - bs.Read(clientWroteChallenge); - - if (requiresSecurityOfThisClient==true && clientWroteChallenge==0) - { - // Fail, client doesn't support security, and it is required - return true; - } - - if (clientWroteChallenge) - { - bs.ReadAlignedBytes((unsigned char*) remoteHandshakeChallenge, cat::EasyHandshake::CHALLENGE_BYTES); -#ifdef CAT_AUDIT - printf("AUDIT: RECV CHALLENGE "); - for (int ii = 0; ii < sizeof(remoteHandshakeChallenge); ++ii) - { - printf("%02x", (cat::u8)remoteHandshakeChallenge[ii]); - } - printf("\n"); -#endif - } - } -#endif // LIBCAT_SECURITY - - bs.Read(bindingAddress); - uint16_t mtu; - bs.Read(mtu); - bs.Read(guid); - - RakPeer::RemoteSystemStruct *rssFromSA = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - bool IPAddrInUse = rssFromSA != 0 && rssFromSA->isActive; - RakPeer::RemoteSystemStruct *rssFromGuid = rakPeer->GetRemoteSystemFromGUID(guid, true); - bool GUIDInUse = rssFromGuid != 0 && rssFromGuid->isActive; - - // IPAddrInUse, GuidInUse, outcome - // TRUE, , TRUE , ID_OPEN_CONNECTION_REPLY if they are the same, else ID_ALREADY_CONNECTED - // FALSE, , TRUE , ID_ALREADY_CONNECTED (someone else took this guid) - // TRUE, , FALSE , ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) - // FALSE , FALSE , Allow connection - - int outcome; - if (IPAddrInUse & GUIDInUse) - { - if (rssFromSA==rssFromGuid && rssFromSA->connectMode==RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER) - { - // ID_OPEN_CONNECTION_REPLY if they are the same - outcome=1; - } - else - { - // ID_ALREADY_CONNECTED (restarted raknet, connected again from same ip, plus someone else took this guid) - outcome=2; - } - } - else if (IPAddrInUse==false && GUIDInUse==true) - { - // ID_ALREADY_CONNECTED (someone else took this guid) - outcome=3; - } - else if (IPAddrInUse==true && GUIDInUse==false) - { - // ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) - outcome=4; - } - else - { - // Allow connection - outcome=0; - } - - RakNet::BitStream bsAnswer; - bsAnswer.Write((MessageID)ID_OPEN_CONNECTION_REPLY_2); - bsAnswer.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsAnswer.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - bsAnswer.Write(systemAddress); - bsAnswer.Write(mtu); - bsAnswer.Write(requiresSecurityOfThisClient); - - if (outcome==1) - { - // Duplicate connection request packet from packetloss - // Send back the same answer -#if LIBCAT_SECURITY==1 - if (requiresSecurityOfThisClient) - { - CAT_AUDIT_PRINTF("AUDIT: Resending public key and answer from packetloss. Sending ID_OPEN_CONNECTION_REPLY_2\n"); - bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); - } -#endif // LIBCAT_SECURITY - - unsigned int i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - else if (outcome!=0) - { - bsOut.Write((MessageID)ID_ALREADY_CONNECTED); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(guid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - - if (rakPeer->AllowIncomingConnections()==false) - { - bsOut.Write((MessageID)ID_NO_FREE_INCOMING_CONNECTIONS); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(guid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - - bool thisIPConnectedRecently=false; - rssFromSA = rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, requiresSecurityOfThisClient); - - if (thisIPConnectedRecently==true) - { - bsOut.Write((MessageID)ID_IP_RECENTLY_CONNECTED); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(guid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - - return true; - } - -#if LIBCAT_SECURITY==1 - if (requiresSecurityOfThisClient) - { - CAT_AUDIT_PRINTF("AUDIT: Writing public key. Sending ID_OPEN_CONNECTION_REPLY_2\n"); - if (rakPeer->_server_handshake->ProcessChallenge(remoteHandshakeChallenge, rssFromSA->answer, rssFromSA->reliabilityLayer.GetAuthenticatedEncryption() )) - { - CAT_AUDIT_PRINTF("AUDIT: Challenge good!\n"); - // Keep going to OK block - } - else - { - CAT_AUDIT_PRINTF("AUDIT: Challenge BAD!\n"); - - // Unassign this remote system - rakPeer->DereferenceRemoteSystem(systemAddress); - return true; - } - - bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); - } -#endif // LIBCAT_SECURITY - - unsigned int i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket->s, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); - } - return true; - } - - return false; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead, BitStream &updateBitStream ) -{ - ProcessNetworkPacket(systemAddress,data,length,rakPeer,rakPeer->socketList[0],timeRead, updateBitStream); -} -void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead, BitStream &updateBitStream ) -{ -#if LIBCAT_SECURITY==1 -#ifdef CAT_AUDIT - printf("AUDIT: RECV "); - for (int ii = 0; ii < length; ++ii) - { - printf("%02x", (cat::u8)data[ii]); - } - printf("\n"); -#endif -#endif // LIBCAT_SECURITY - - RakAssert(systemAddress.GetPort()); - bool isOfflineMessage; - if (ProcessOfflineNetworkPacket(systemAddress, data, length, rakPeer, rakNetSocket, &isOfflineMessage, timeRead)) - { - return; - } - -// RakNet::Packet *packet; - RakPeer::RemoteSystemStruct *remoteSystem; - - // See if this datagram came from a connected system - remoteSystem = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - if ( remoteSystem ) - { - // Handle regular incoming data - // HandleSocketReceiveFromConnectedPlayer is only safe to be called from the same thread as Update, which is this thread - if ( isOfflineMessage==false) - { - remoteSystem->reliabilityLayer.HandleSocketReceiveFromConnectedPlayer( - data, length, systemAddress, rakPeer->pluginListNTS, remoteSystem->MTUSize, - rakNetSocket->s, &rnr, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, timeRead, updateBitStream); - } - } -} - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GenerateSeedFromGuid(void) -{ - /* - // Construct a random seed based on the initial guid value, and the last digits of the difference to each subsequent number - // This assumes that only the last 3 bits of each guidId integer has a meaningful amount of randomness between it and the prior number - unsigned int t = guid.g[0]; - unsigned int i; - for (i=1; i < sizeof(guid.g) / sizeof(guid.g[0]); i++) - { - unsigned int diff = guid.g[i]-guid.g[i-1]; - unsigned int diff3Bits = diff & 0x0007; - diff3Bits <<= 29; - diff3Bits >>= (i-1)*3; - t ^= diff3Bits; - } - - return t; - */ - return (unsigned int) ((myGuid.g >> 32) ^ myGuid.g); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DerefAllSockets(void) -{ - unsigned int i; - for (i=0; i < socketList.Size(); i++) - { - socketList[i].SetNull(); - } - socketList.Clear(false, _FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const -{ - unsigned int i; - for (i=0; i < socketList.Size(); i++) - { - if (socketList[i]->userConnectionSocketIndex==userIndex) - return i; - } - RakAssert("GetRakNetSocketFromUserConnectionSocketIndex failed" && 0); - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::RunUpdateCycle( RakNet::TimeUS timeNS, RakNet::Time timeMS, BitStream &updateBitStream ) -{ - RakPeer::RemoteSystemStruct * remoteSystem; - unsigned int activeSystemListIndex; - Packet *packet; - // int currentSentBytes,currentReceivedBytes; -// unsigned numberOfBytesUsed; -// BitSize_t numberOfBitsUsed; - //SystemAddress authoritativeClientSystemAddress; - BitSize_t bitSize; - unsigned int byteSize; - unsigned char *data; - SystemAddress systemAddress; - BufferedCommandStruct *bcs; - bool callerDataAllocationUsed; - RakNetStatistics *rnss; - - // This is here so RecvFromBlocking actually gets data from the same thread - if (SocketLayer::GetSocketLayerOverride()) - { - int len; - SystemAddress sender; - char dataOut[ MAXIMUM_MTU_SIZE ]; - do { - len = SocketLayer::GetSocketLayerOverride()->RakNetRecvFrom(socketList[0]->s,this,dataOut,&sender,true); - if (len>0) - ProcessNetworkPacket( sender, dataOut, len, this, socketList[0], RakNet::GetTimeUS(), updateBitStream ); - } while (len>0); - } - - unsigned int socketListIndex; - RakPeer::RecvFromStruct *recvFromStruct; - while ((recvFromStruct=bufferedPackets.PopInaccurate())!=0) - { - for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) - { - if ((SOCKET) socketList[socketListIndex]->s==recvFromStruct->s) - break; - } - if (socketListIndex!=socketList.Size()) - ProcessNetworkPacket(recvFromStruct->systemAddress, recvFromStruct->data, recvFromStruct->bytesRead, this, socketList[socketListIndex], recvFromStruct->timeRead, updateBitStream); - bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); - } - - while ((bcs=bufferedCommands.PopInaccurate())!=0) - { - if (bcs->command==BufferedCommandStruct::BCS_SEND) - { - // GetTime is a very slow call so do it once and as late as possible - if (timeNS==0) - { - timeNS = RakNet::GetTimeUS(); - timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); - } - - callerDataAllocationUsed=SendImmediate((char*)bcs->data, bcs->numberOfBitsToSend, bcs->priority, bcs->reliability, bcs->orderingChannel, bcs->systemIdentifier, bcs->broadcast, true, timeNS, bcs->receipt); - if ( callerDataAllocationUsed==false ) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - - // Set the new connection state AFTER we call sendImmediate in case we are setting it to a disconnection state, which does not allow further sends - if (bcs->connectionMode!=RemoteSystemStruct::NO_ACTION ) - { - remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); - if (remoteSystem) - remoteSystem->connectMode=bcs->connectionMode; - } - } - else if (bcs->command==BufferedCommandStruct::BCS_CLOSE_CONNECTION) - { - CloseConnectionInternal(bcs->systemIdentifier, false, true, bcs->orderingChannel, bcs->priority); - } - else if (bcs->command==BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS) - { - // Reroute - RakPeer::RemoteSystemStruct *rssFromGuid = GetRemoteSystem(bcs->systemIdentifier.rakNetGuid,true,true); - if (rssFromGuid!=0) - { - unsigned int existingSystemIndex = GetRemoteSystemIndex(rssFromGuid->systemAddress); - ReferenceRemoteSystem(bcs->systemIdentifier.systemAddress, existingSystemIndex); - } - } - else if (bcs->command==BufferedCommandStruct::BCS_GET_SOCKET) - { - SocketQueryOutput *sqo; - if (bcs->systemIdentifier.IsUndefined()) - { - sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); - sqo->sockets=socketList; - socketQueryOutput.Push(sqo); - } - else - { - remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); - sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); - - sqo->sockets.Clear(false, _FILE_AND_LINE_); - if (remoteSystem) - { - sqo->sockets.Push(remoteSystem->rakNetSocket, _FILE_AND_LINE_ ); - } - else - { - // Leave empty smart pointer - } - socketQueryOutput.Push(sqo); - } - - } - -#ifdef _DEBUG - bcs->data=0; -#endif - - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - } - - if (requestedConnectionQueue.IsEmpty()==false) - { - if (timeNS==0) - { - timeNS = RakNet::GetTimeUS(); - timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); - } - - bool condition1, condition2; - unsigned requestedConnectionQueueIndex=0; - requestedConnectionQueueMutex.Lock(); - while (requestedConnectionQueueIndex < requestedConnectionQueue.Size()) - { - RequestedConnectionStruct *rcs; - rcs = requestedConnectionQueue[requestedConnectionQueueIndex]; - requestedConnectionQueueMutex.Unlock(); - if (rcs->nextRequestTime < timeMS) - { - condition1=rcs->requestsMade==rcs->sendConnectionAttemptCount+1; - condition2=(bool)((rcs->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)==1); - // If too many requests made or a hole then remove this if possible, otherwise invalidate it - if (condition1 || condition2) - { - if (rcs->data) - { - rakFree_Ex(rcs->data, _FILE_AND_LINE_ ); - rcs->data=0; - } - - if (condition1 && !condition2 && rcs->actionToTake==RequestedConnectionStruct::CONNECT) - { - // Tell user of connection attempt failed - packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - AddPacketToProducer(packet); - } - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Connection attempt FAILED so deleting rcs->client_handshake object %x\n", rcs->client_handshake); - RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif - RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); - - requestedConnectionQueueMutex.Lock(); - for (unsigned int k=0; k < requestedConnectionQueue.Size(); k++) - { - if (requestedConnectionQueue[k]==rcs) - { - requestedConnectionQueue.RemoveAtIndex(k); - break; - } - } - requestedConnectionQueueMutex.Unlock(); - } - else - { - - int MTUSizeIndex = rcs->requestsMade / (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES); - if (MTUSizeIndex>=NUM_MTU_SIZES) - MTUSizeIndex=NUM_MTU_SIZES-1; - rcs->requestsMade++; - rcs->nextRequestTime=timeMS+rcs->timeBetweenSendConnectionAttemptsMS; - - RakNet::BitStream bitStream; - //WriteOutOfBandHeader(&bitStream, ID_USER_PACKET_ENUM); - bitStream.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_1); - bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bitStream.Write((MessageID)RAKNET_PROTOCOL_VERSION); - bitStream.PadWithZeroToByteLength(mtuSizes[MTUSizeIndex]-UDP_HEADER_SIZE); - - char str[256]; - rcs->systemAddress.ToString(true,str); - - //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*) bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), rcs->systemAddress); - - if (rcs->socket.IsNull()) - { - rcs->systemAddress.FixForIPVersion(socketList[rcs->socketIndex]->boundAddress); - SocketLayer::SetDoNotFragment(socketList[rcs->socketIndex]->s, 1, socketList[rcs->socketIndex]->boundAddress.GetIPPROTO()); - RakNet::Time sendToStart=RakNet::GetTime(); - if (SocketLayer::SendTo( socketList[rcs->socketIndex]->s, (const char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), rcs->systemAddress, socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ )==-10040) - { - // Don't use this MTU size again - rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES)); - rcs->nextRequestTime=timeMS; - } - else - { - RakNet::Time sendToEnd=RakNet::GetTime(); - if (sendToEnd-sendToStart>100) - { - // Drop to lowest MTU - int lowestMtuIndex = rcs->sendConnectionAttemptCount/NUM_MTU_SIZES * (NUM_MTU_SIZES - 1); - if (lowestMtuIndex > rcs->requestsMade) - { - rcs->requestsMade = (unsigned char) lowestMtuIndex; - rcs->nextRequestTime=timeMS; - } - else - rcs->requestsMade=(unsigned char)(rcs->sendConnectionAttemptCount+1); - } - } - SocketLayer::SetDoNotFragment(socketList[rcs->socketIndex]->s, 0, socketList[rcs->socketIndex]->boundAddress.GetIPPROTO()); - } - else - { - rcs->systemAddress.FixForIPVersion(rcs->socket->boundAddress); - SocketLayer::SetDoNotFragment(rcs->socket->s, 1, rcs->socket->boundAddress.GetIPPROTO()); - RakNet::Time sendToStart=RakNet::GetTime(); - if (SocketLayer::SendTo( rcs->socket->s, (const char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), rcs->systemAddress, socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ )==-10040) - { - // Don't use this MTU size again - rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES)); - rcs->nextRequestTime=timeMS; - } - else - { - RakNet::Time sendToEnd=RakNet::GetTime(); - if (sendToEnd-sendToStart>100) - { - // Drop to lowest MTU - int lowestMtuIndex = rcs->sendConnectionAttemptCount/NUM_MTU_SIZES * (NUM_MTU_SIZES - 1); - if (lowestMtuIndex > rcs->requestsMade) - { - rcs->requestsMade = (unsigned char) lowestMtuIndex; - rcs->nextRequestTime=timeMS; - } - else - rcs->requestsMade= (unsigned char) rcs->sendConnectionAttemptCount+1; - } - } - SocketLayer::SetDoNotFragment(rcs->socket->s, 0, rcs->socket->boundAddress.GetIPPROTO()); - } - - requestedConnectionQueueIndex++; - } - } - else - requestedConnectionQueueIndex++; - - requestedConnectionQueueMutex.Lock(); - } - requestedConnectionQueueMutex.Unlock(); - } - - // remoteSystemList in network thread - for ( activeSystemListIndex = 0; activeSystemListIndex < activeSystemListSize; ++activeSystemListIndex ) - //for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; ++remoteSystemIndex ) - { - // I'm using systemAddress from remoteSystemList but am not locking it because this loop is called very frequently and it doesn't - // matter if we miss or do an extra update. The reliability layers themselves never care which player they are associated with - //systemAddress = remoteSystemList[ remoteSystemIndex ].systemAddress; - // Allow the systemAddress for this remote system list to change. We don't care if it changes now. - // remoteSystemList[ remoteSystemIndex ].allowSystemAddressAssigment=true; - - - // Found an active remote system - remoteSystem = activeSystemList[ activeSystemListIndex ]; - systemAddress = remoteSystem->systemAddress; - RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); - // Update is only safe to call from the same thread that calls HandleSocketReceiveFromConnectedPlayer, - // which is this thread - - if (timeNS==0) - { - timeNS = RakNet::GetTimeUS(); - timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); - //RAKNET_DEBUG_PRINTF("timeNS = %I64i timeMS=%i\n", timeNS, timeMS); - } - - - if (timeMS > remoteSystem->lastReliableSend && timeMS-remoteSystem->lastReliableSend > remoteSystem->reliabilityLayer.GetTimeoutTime()/2 && remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) - { - // If no reliable packets are waiting for an ack, do a one byte reliable send so that disconnections are noticed - RakNetStatistics rakNetStatistics; - rnss=remoteSystem->reliabilityLayer.GetStatistics(&rakNetStatistics); - if (rnss->messagesInResendBuffer==0) - { - PingInternal( systemAddress, true, RELIABLE ); - - //remoteSystem->lastReliableSend=timeMS+remoteSystem->reliabilityLayer.GetTimeoutTime(); - remoteSystem->lastReliableSend=timeMS; - } - } - - remoteSystem->reliabilityLayer.Update( remoteSystem->rakNetSocket->s, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, remoteSystem->rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, remoteSystem->rakNetSocket->extraSocketOptions, updateBitStream ); // systemAddress only used for the internet simulator test - - // Check for failure conditions - if ( remoteSystem->reliabilityLayer.IsDeadConnection() || - ((remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY) && remoteSystem->reliabilityLayer.IsOutgoingDataWaiting()==false) || - (remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && (remoteSystem->reliabilityLayer.AreAcksWaiting()==false || remoteSystem->reliabilityLayer.AckTimeout(timeMS)==true)) || - (( - (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || - remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || - remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) - && timeMS > remoteSystem->connectionTime && timeMS - remoteSystem->connectionTime > 10000)) - ) - { - // RAKNET_DEBUG_PRINTF("timeMS=%i remoteSystem->connectionTime=%i\n", timeMS, remoteSystem->connectionTime ); - - // Failed. Inform the user? - // TODO - RakNet 4.0 - Return a different message identifier for DISCONNECT_ASAP_SILENTLY and DISCONNECT_ASAP than for DISCONNECT_ON_NO_ACK - // The first two mean we called CloseConnection(), the last means the other system sent us ID_DISCONNECTION_NOTIFICATION - if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION - || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK) - { - -// RakNet::BitStream undeliveredMessages; -// remoteSystem->reliabilityLayer.GetUndeliveredMessages(&undeliveredMessages,remoteSystem->MTUSize); - -// packet=AllocPacket(sizeof( char ) + undeliveredMessages.GetNumberOfBytesUsed()); - packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - else if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) - packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection - else - packet->data[ 0 ] = ID_DISCONNECTION_NOTIFICATION; // DeadConnection - -// memcpy(packet->data+1, undeliveredMessages.GetData(), undeliveredMessages.GetNumberOfBytesUsed()); - - packet->guid = remoteSystem->guid; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - - AddPacketToProducer(packet); - } - // else connection shutting down, don't bother telling the user - -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Connection dropped for player %i:%i\n", systemAddress); -#endif - CloseConnectionInternal( systemAddress, false, true, 0, LOW_PRIORITY ); - continue; - } - - // Ping this guy if it is time to do so - if ( remoteSystem->connectMode==RemoteSystemStruct::CONNECTED && timeMS > remoteSystem->nextPingTime && ( occasionalPing || remoteSystem->lowestPing == (unsigned short)-1 ) ) - { - remoteSystem->nextPingTime = timeMS + 5000; - PingInternal( systemAddress, true, UNRELIABLE ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - } - - // Find whoever has the lowest player ID - //if (systemAddress < authoritativeClientSystemAddress) - // authoritativeClientSystemAddress=systemAddress; - - // Does the reliability layer have any packets waiting for us? - // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer - bitSize = remoteSystem->reliabilityLayer.Receive( &data ); - - while ( bitSize > 0 ) - { - // These types are for internal use and should never arrive from a network packet - if (data[0]==ID_CONNECTION_ATTEMPT_FAILED) - { - RakAssert(0); - bitSize=0; - continue; - } - - // Fast and easy - just use the data that was returned - byteSize = (unsigned int) BITS_TO_BYTES( bitSize ); - - // For unknown senders we only accept a few specific packets - if (remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) - { - if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) - { - ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else - { - CloseConnectionInternal( systemAddress, false, true, 0, LOW_PRIORITY ); -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Temporarily banning %i:%i for sending nonsense data\n", systemAddress); -#endif - - char str1[64]; - systemAddress.ToString(false, str1); - AddToBanList(str1, remoteSystem->reliabilityLayer.GetTimeoutTime()); - - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // However, if we are connected we still take a connection request in case both systems are trying to connect to each other - // at the same time - if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) - { - // 04/27/06 This is wrong. With cross connections, we can both have initiated the connection are in state REQUESTED_CONNECTION - // 04/28/06 Downgrading connections from connected will close the connection due to security at ((remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED && time > remoteSystem->connectionTime && time - remoteSystem->connectionTime > 10000)) - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - { - ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); - } - else - { - - RakNet::BitStream bs((unsigned char*) data,byteSize,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.IgnoreBytes(RakNetGUID::size()); - RakNet::Time incomingTimestamp; - bs.Read(incomingTimestamp); - - // Got a connection request message from someone we are already connected to. Just reply normally. - // This can happen due to race conditions with the fully connected mesh - OnConnectionRequest( remoteSystem, incomingTimestamp ); - } - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char) data[ 0 ] == ID_NEW_INCOMING_CONNECTION && byteSize > sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(RakNet::Time)*2 ) - { - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) - { - remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; - PingInternal( systemAddress, true, UNRELIABLE ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - - RakNet::BitStream inBitStream((unsigned char *) data, byteSize, false); - SystemAddress bsSystemAddress; - - inBitStream.IgnoreBits(8); - inBitStream.Read(bsSystemAddress); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); - - RakNet::Time sendPingTime, sendPongTime; - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); - - // Overwrite the data in the packet - // NewIncomingConnectionStruct newIncomingConnectionStruct; - // RakNet::BitStream nICS_BS( data, NewIncomingConnectionStruct_Size, false ); - // newIncomingConnectionStruct.Deserialize( nICS_BS ); - - remoteSystem->myExternalSystemAddress = bsSystemAddress; - firstExternalID=bsSystemAddress; - firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); - - // Send this info down to the game - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - } - else - { - // Send to game even if already connected. This could happen when connecting to 127.0.0.1 - // Ignore, already connected - // rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char) data[ 0 ] == ID_CONNECTED_PONG && byteSize == sizeof(unsigned char)+sizeof(RakNet::Time)*2 ) - { - RakNet::Time sendPingTime, sendPongTime; - - // Copy into the ping times array the current time - the value returned - // First extract the sent ping - RakNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); - //PingStruct ps; - //ps.Deserialize(psBS); - inBitStream.IgnoreBits(8); - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - - OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)data[0] == ID_CONNECTED_PING && byteSize == sizeof(unsigned char)+sizeof(RakNet::Time) ) - { - RakNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); - inBitStream.IgnoreBits(8); - RakNet::Time sendPingTime; - inBitStream.Read(sendPingTime); - - RakNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_CONNECTED_PONG); - outBitStream.Write(sendPingTime); - outBitStream.Write(RakNet::GetTime()); - SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, UNRELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0 ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char) data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) - { - // We shouldn't close the connection immediately because we need to ack the ID_DISCONNECTION_NOTIFICATION - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ON_NO_ACK; - rakFree_Ex(data, _FILE_AND_LINE_ ); - - // AddPacketToProducer(packet); - } - else if ( (unsigned char)(data)[0] == ID_DETECT_LOST_CONNECTIONS && byteSize == sizeof(unsigned char) ) - { - // Do nothing - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)(data)[0] == ID_INVALID_PASSWORD ) - { - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - { - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST_ACCEPTED ) - { - if (byteSize > sizeof(MessageID)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(SystemIndex)+sizeof(RakNet::Time)*2) - { - // Make sure this connection accept is from someone we wanted to connect to - bool allowConnection, alreadyConnected; - - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || - remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || - allowConnectionResponseIPMigration) - allowConnection=true; - else - allowConnection=false; - - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) - alreadyConnected=true; - else - alreadyConnected=false; - - if ( allowConnection ) - { - SystemAddress externalID; - SystemIndex systemIndex; -// SystemAddress internalID; - - RakNet::BitStream inBitStream((unsigned char *) data, byteSize, false); - inBitStream.IgnoreBits(8); - // inBitStream.Read(remotePort); - inBitStream.Read(externalID); - inBitStream.Read(systemIndex); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); - - RakNet::Time sendPingTime, sendPongTime; - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - OnConnectedPong(sendPingTime, sendPongTime, remoteSystem); - - // Find a free remote system struct to use - // RakNet::BitStream casBitS(data, byteSize, false); - // ConnectionAcceptStruct cas; - // cas.Deserialize(casBitS); - // systemAddress.GetPort() = remotePort; - - // The remote system told us our external IP, so save it - remoteSystem->myExternalSystemAddress = externalID; - remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; - - firstExternalID=externalID; - firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); - - // Send the connection request complete to the game - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = byteSize * 8; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) GetIndexFromSystemAddress( systemAddress, true ); - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - - RakNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_NEW_INCOMING_CONNECTION); - outBitStream.Write(systemAddress); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - outBitStream.Write(ipList[i]); - outBitStream.Write(sendPongTime); - outBitStream.Write(RakNet::GetTime()); - - - // We turned on encryption with SetEncryptionKey. This pads packets to up to a multiple of 16 bytes. - // As soon as a multiple of 16 byte packet arrives on the remote system, we will turn on AES. This works because all encrypted packets are multiples of 16 and the - // packets I happen to be sending before this are not a multiple of 16 bytes. Otherwise there is no way to know if a packet that arrived is - // encrypted or not so the other side won't know to turn on encryption or not. - RakAssert((outBitStream.GetNumberOfBytesUsed()&15)!=0); - SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0 ); - - if (alreadyConnected==false) - { - PingInternal( systemAddress, true, UNRELIABLE ); - } - } - else - { - // Ignore, already connected - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // Version mismatch error? - RakAssert(0); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // What do I do if I get a message from a system, before I am fully connected? - // I can either ignore it or give it to the user - // It seems like giving it to the user is a better option - if ((data[0]>=(MessageID)ID_TIMESTAMP || data[0]==ID_SND_RECEIPT_ACKED || data[0]==ID_SND_RECEIPT_LOSS) && - remoteSystem->isActive - ) - { - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - } - - // Does the reliability layer have any more packets waiting for us? - // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer - bitSize = remoteSystem->reliabilityLayer.Receive( &data ); - } - - } - - return true; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RAK_THREAD_DECLARATION(RakNet::RecvFromLoop) -{ - - - - RakPeerAndIndex *rpai = ( RakPeerAndIndex * ) arguments; - - RakPeer * rakPeer = rpai->rakPeer; - SOCKET s = rpai->s; - unsigned short remotePortRakNetWasStartedOn_PS3 = rpai->remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions = rpai->extraSocketOptions; - - rakPeer->isRecvFromLoopThreadActive.Increment(); - - RakPeer::RecvFromStruct *recvFromStruct; - while ( rakPeer->endThreads == false ) - { - recvFromStruct=rakPeer->bufferedPackets.Allocate( _FILE_AND_LINE_ ); - if (recvFromStruct != NULL) - { - recvFromStruct->s=s; - recvFromStruct->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; - recvFromStruct->extraSocketOptions=extraSocketOptions; - SocketLayer::RecvFromBlocking(s, rakPeer, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); - - if (recvFromStruct->bytesRead>0) - { - RakAssert(recvFromStruct->systemAddress.GetPort()); - rakPeer->bufferedPackets.Push(recvFromStruct); - rakPeer->quitAndDataEvents.SetEvent(); - rakPeer->errorState = 0; - } - else - { - if (recvFromStruct->bytesRead < 0) { - --rakPeer->errorState; - RakSleep(30); - } - rakPeer->bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); - } - } - else - RakSleep(30); - } - rakPeer->isRecvFromLoopThreadActive.Decrement(); - - - - - return 0; - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RAK_THREAD_DECLARATION(RakNet::UpdateNetworkLoop) -{ - - - - RakPeer * rakPeer = ( RakPeer * ) arguments; - - -/* - // 11/15/05 - this is slower than Sleep() -#ifdef _WIN32 -#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) - // Lets see if these timers give better performance than Sleep - HANDLE timerHandle; - LARGE_INTEGER dueTime; - - if ( rakPeer->threadSleepTimer <= 0 ) - rakPeer->threadSleepTimer = 1; - - // 2nd parameter of false means synchronization timer instead of manual-reset timer - timerHandle = CreateWaitableTimer( NULL, FALSE, 0 ); - - RakAssert( timerHandle ); - - dueTime.QuadPart = -10000 * rakPeer->threadSleepTimer; // 10000 is 1 ms? - - BOOL success = SetWaitableTimer( timerHandle, &dueTime, rakPeer->threadSleepTimer, NULL, NULL, FALSE ); - (void) success; - RakAssert( success ); - -#endif -#endif -*/ - - BitStream updateBitStream( MAXIMUM_MTU_SIZE -#if LIBCAT_SECURITY==1 - + cat::AuthenticatedEncryption::OVERHEAD_BYTES -#endif - ); - - RakNet::TimeUS timeNS; - RakNet::TimeMS timeMS; - - rakPeer->isMainLoopThreadActive = true; - - while ( rakPeer->endThreads == false ) - { - // Set inside RunUpdateCycle() itself, this is here for testing - timeNS=0; - timeMS=0; - -// #ifdef _DEBUG -// // Sanity check, make sure RunUpdateCycle does not block or not otherwise get called for a long time -// RakNetTime thisCall=RakNet::GetTime(); -// RakAssert(thisCall-lastCall<250); -// lastCall=thisCall; -// #endif - if (rakPeer->userUpdateThreadPtr) - rakPeer->userUpdateThreadPtr(rakPeer, rakPeer->userUpdateThreadData); - - rakPeer->RunUpdateCycle(timeNS, timeMS, updateBitStream); - - // Pending sends go out this often, unless quitAndDataEvents is set - rakPeer->quitAndDataEvents.WaitOnEvent(10); - - /* - -// #if ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && -#if defined(USE_WAIT_FOR_MULTIPLE_EVENTS) && defined(_WIN32) - - if (rakPeer->threadSleepTimer>0) - { - WSAEVENT eventArray[256]; - unsigned int i, eventArrayIndex; - for (i=0,eventArrayIndex=0; i < rakPeer->socketList.Size(); i++) - { - if (rakPeer->socketList[i]->recvEvent!=INVALID_HANDLE_VALUE) - { - eventArray[eventArrayIndex]=rakPeer->socketList[i]->recvEvent; - eventArrayIndex++; - if (eventArrayIndex==256) - break; - } - } - WSAWaitForMultipleEvents(eventArrayIndex,(const HANDLE*) &eventArray,FALSE,rakPeer->threadSleepTimer,FALSE); - } - else - { - RakSleep(0); - } - -#else // ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - #pragma message("-- RakNet: Using Sleep(). Uncomment USE_WAIT_FOR_MULTIPLE_EVENTS in RakNetDefines.h if you want to use WaitForSingleObject instead. --") - - RakSleep( rakPeer->threadSleepTimer ); -#endif - */ - } - - rakPeer->isMainLoopThreadActive = false; - - /* -#ifdef _WIN32 -#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) - CloseHandle(timerHandle); -#endif -#endif - */ - - - - - return 0; - -} - -void RakPeer::CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet) -{ - for (unsigned int i=0; i < pluginList.Size(); i++) - { - switch (packet->data[0]) - { - case ID_DISCONNECTION_NOTIFICATION: - pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_DISCONNECTION_NOTIFICATION); - break; - case ID_CONNECTION_LOST: - pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_CONNECTION_LOST); - break; - case ID_NEW_INCOMING_CONNECTION: - pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, true); - break; - case ID_CONNECTION_REQUEST_ACCEPTED: - pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, false); - break; - case ID_CONNECTION_ATTEMPT_FAILED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_ATTEMPT_FAILED); - break; - case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); - break; - case ID_OUR_SYSTEM_REQUIRES_SECURITY: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_OUR_SYSTEM_REQUIRES_SECURITY); - break; - case ID_PUBLIC_KEY_MISMATCH: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_PUBLIC_KEY_MISMATCH); - break; - case ID_ALREADY_CONNECTED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_ALREADY_CONNECTED); - break; - case ID_NO_FREE_INCOMING_CONNECTIONS: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_NO_FREE_INCOMING_CONNECTIONS); - break; - case ID_CONNECTION_BANNED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_BANNED); - break; - case ID_INVALID_PASSWORD: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INVALID_PASSWORD); - break; - case ID_INCOMPATIBLE_PROTOCOL_VERSION: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INCOMPATIBLE_PROTOCOL); - break; - case ID_IP_RECENTLY_CONNECTED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_IP_RECENTLY_CONNECTED); - break; - } - } -} - -// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) -// #pragma pop_macro("new") -// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +// \file +// +// This file is part of RakNet Copyright 2003 Jenkins Software LLC +// +// Usage of RakNet is subject to the appropriate license agreement. + + +#define CAT_NEUTER_EXPORT /* Neuter dllimport for libcat */ + +#include "RakNetDefines.h" +#include "RakPeer.h" +#include "RakNetTypes.h" + +#ifdef _WIN32 + +#else +#include +#endif + +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + +#include +#include // toupper +#include +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "DS_HuffmanEncodingTree.h" +#include "Rand.h" +#include "PluginInterface2.h" +#include "StringCompressor.h" +#include "StringTable.h" +#include "NetworkIDObject.h" +#include "RakNetTypes.h" +#include "SHA1.h" +#include "RakSleep.h" +#include "RakAssert.h" +#include "RakNetVersion.h" +#include "NetworkIDManager.h" +#include "gettimeofday.h" +#include "SignaledEvent.h" +#include "SuperFastHash.h" +#include "RakAlloca.h" +#include "WSAStartupSingleton.h" + +#ifdef USE_THREADED_SEND +#include "SendToThread.h" +#endif + +#ifdef CAT_AUDIT +#define CAT_AUDIT_PRINTF(...) printf(__VA_ARGS__) +#else +#define CAT_AUDIT_PRINTF(...) +#endif + +namespace RakNet +{ +RAK_THREAD_DECLARATION(UpdateNetworkLoop); +RAK_THREAD_DECLARATION(RecvFromLoop); +RAK_THREAD_DECLARATION(UDTConnect); +} +#define REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE 8 + +#if !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) +#include // malloc +#endif + + + +#if defined(_WIN32) +// +#else +/* +#include // Console 2 +#include +extern bool _extern_Console2LoadModules(void); +extern int _extern_Console2GetConnectionStatus(void); +extern int _extern_Console2GetLobbyStatus(void); +//extern bool Console2StartupFluff(unsigned int *); +extern void Console2ShutdownFluff(void); +//extern unsigned int Console2ActivateConnection(unsigned int, void *); +//extern bool Console2BlockOnEstablished(void); +extern void Console2GetIPAndPort(unsigned int, char *, unsigned short *, unsigned int ); +//extern void Console2DeactivateConnection(unsigned int, unsigned int); +*/ +#endif + + +static const int NUM_MTU_SIZES=3; + + + +static const int mtuSizes[NUM_MTU_SIZES]={MAXIMUM_MTU_SIZE, 1200, 576}; + + +// Note to self - if I change this it might affect RECIPIENT_OFFLINE_MESSAGE_INTERVAL in Natpunchthrough.cpp +//static const int MAX_OPEN_CONNECTION_REQUESTS=8; +//static const int TIME_BETWEEN_OPEN_CONNECTION_REQUESTS=500; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +static RakNetRandom rnr; + +struct RakPeerAndIndex +{ + SOCKET s; + unsigned short remotePortRakNetWasStartedOn_PS3; + RakPeer *rakPeer; + unsigned int extraSocketOptions; +}; + +static const unsigned int MAX_OFFLINE_DATA_LENGTH=400; // I set this because I limit ID_CONNECTION_REQUEST to 512 bytes, and the password is appended to that packet. + +// Used to distinguish between offline messages with data, and messages from the reliability layer +// Should be different than any message that could result from messages from the reliability layer +#if !defined(__GNUC__) +#pragma warning(disable:4309) // 'initializing' : truncation of constant value +#endif +// Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false +static const unsigned char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78}; + +struct PacketFollowedByData +{ + Packet p; + unsigned char data[1]; +}; + +Packet *RakPeer::AllocPacket(unsigned dataSize, const char *file, unsigned int line) +{ + // Crashes when dataSize is 4 bytes - not sure why +// unsigned char *data = (unsigned char *) rakMalloc_Ex(sizeof(PacketFollowedByData)+dataSize, file, line); +// Packet *p = &((PacketFollowedByData *)data)->p; +// p->data=((PacketFollowedByData *)data)->data; +// p->length=dataSize; +// p->bitSize=BYTES_TO_BITS(dataSize); +// p->deleteData=false; +// p->guid=UNASSIGNED_RAKNET_GUID; +// return p; + + RakNet::Packet *p; + packetAllocationPoolMutex.Lock(); + p = packetAllocationPool.Allocate(file,line); + packetAllocationPoolMutex.Unlock(); + p = new ((void*)p) Packet; + p->data=(unsigned char*) rakMalloc_Ex(dataSize,file,line); + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=true; + p->guid=UNASSIGNED_RAKNET_GUID; + p->wasGeneratedLocally=false; + return p; +} + +Packet *RakPeer::AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line) +{ + // Packet *p = (Packet *)rakMalloc_Ex(sizeof(Packet), file, line); + RakNet::Packet *p; + packetAllocationPoolMutex.Lock(); + p = packetAllocationPool.Allocate(file,line); + packetAllocationPoolMutex.Unlock(); + p = new ((void*)p) Packet; + RakAssert(p); + p->data=data; + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=true; + p->guid=UNASSIGNED_RAKNET_GUID; + p->wasGeneratedLocally=false; + return p; +} + +STATIC_FACTORY_DEFINITIONS(RakPeerInterface,RakPeer) + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Constructor +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RakPeer() +{ + errorState = 0; +#if LIBCAT_SECURITY==1 + // Encryption and security + CAT_AUDIT_PRINTF("AUDIT: Initializing RakPeer security flags: using_security = false, server_handshake = null, cookie_jar = null\n"); + _using_security = false; + _server_handshake = 0; + _cookie_jar = 0; +#endif + + StringCompressor::AddReference(); + RakNet::StringTable::AddReference(); + WSAStartupSingleton::AddRef(); + + defaultMTUSize = mtuSizes[NUM_MTU_SIZES-1]; + trackFrequencyTable = false; + maximumIncomingConnections = 0; + maximumNumberOfPeers = 0; + //remoteSystemListSize=0; + remoteSystemList = 0; + activeSystemList = 0; + activeSystemListSize=0; + remoteSystemLookup=0; + bytesSentPerSecond = bytesReceivedPerSecond = 0; + endThreads = true; + isMainLoopThreadActive = false; + + + + + + // isRecvfromThreadActive=false; +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 + occasionalPing = true; +#else + occasionalPing = false; +#endif + allowInternalRouting=false; + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + ipList[i]=UNASSIGNED_SYSTEM_ADDRESS; + allowConnectionResponseIPMigration = false; + //incomingPasswordLength=outgoingPasswordLength=0; + incomingPasswordLength=0; + splitMessageProgressInterval=0; + //unreliableTimeout=0; + unreliableTimeout=1000; + maxOutgoingBPS=0; + firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; + myGuid=UNASSIGNED_RAKNET_GUID; + userUpdateThreadPtr=0; + userUpdateThreadData=0; + +#ifdef _DEBUG + // Wait longer to disconnect in debug so I don't get disconnected while tracing + defaultTimeoutTime=30000; +#else + defaultTimeoutTime=10000; +#endif + +#ifdef _DEBUG + _packetloss=0.0; + _minExtraPing=0; + _extraPingVariance=0; +#endif + + bufferedCommands.SetPageSize(sizeof(BufferedCommandStruct)*16); + socketQueryOutput.SetPageSize(sizeof(SocketQueryOutput)*8); + bufferedPackets.SetPageSize(sizeof(RecvFromStruct)*BUFFERED_PACKETS_PAGE_SIZE); // This is big, keep the size down + + packetAllocationPoolMutex.Lock(); + packetAllocationPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); + packetAllocationPoolMutex.Unlock(); + + remoteSystemIndexPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenerateGUID(); + + quitAndDataEvents.InitEvent(); + limitConnectionFrequencyFromTheSameIP=false; + ResetSendReceipt(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Destructor +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::~RakPeer() +{ + Shutdown( 0, 0 ); + + // Free the ban list. + ClearBanList(); + + StringCompressor::RemoveReference(); + RakNet::StringTable::RemoveReference(); + WSAStartupSingleton::Deref(); + + quitAndDataEvents.CloseEvent(); + +#if LIBCAT_SECURITY==1 + // Encryption and security + CAT_AUDIT_PRINTF("AUDIT: Deleting RakPeer security objects, handshake = %x, cookie jar = %x\n", _server_handshake, _cookie_jar); + if (_server_handshake) RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + if (_cookie_jar) RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); +#endif + + + + + + + + + + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// \brief Starts the network threads, opens the listen port. +// You must call this before calling Connect(). +// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). +// \note Call SetMaximumIncomingConnections if you want to accept incoming connections +// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections +// \param[in] localPort The port to listen for connections on. +// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle. With new congestion control, the best results will be obtained by passing 10. +// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass &socketDescriptor, 1SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); +// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. +// \return False on failure (can't create socket or thread), true on success. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +StartupResult RakPeer::Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority ) +{ + errorState = 0; + + if (IsActive()) + return RAKNET_ALREADY_STARTED; + + if (threadPriority==-99999) + { + + +#if defined(_WIN32) + threadPriority=0; + + +#else + threadPriority=1000; +#endif + } + + + // Fill out ipList structure + unsigned int i; + + SocketLayer::GetMyIP( ipList ); +// for (i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) +// { +// if (ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS) +// { +// char str[128]; +// ipList[i].ToString(false,str); +// printf("%s\n",str); +// } +// } + + + if (myGuid==UNASSIGNED_RAKNET_GUID) + { + rnr.SeedMT( GenerateSeedFromGuid() ); + } + + RakPeerAndIndex rpai[32]; + RakAssert(socketDescriptorCount<32); + + RakAssert(socketDescriptors && socketDescriptorCount>=1); + + if (socketDescriptors==0 || socketDescriptorCount<1) + return INVALID_SOCKET_DESCRIPTORS; + + //unsigned short localPort; + //localPort=socketDescriptors[0].port; + + RakAssert( maxConnections > 0 ); + + if ( maxConnections <= 0 ) + return INVALID_MAX_CONNECTIONS; + + DerefAllSockets(); + + + // Go through all socket descriptors and precreate sockets on the specified addresses + for (i=0; i rns(RakNet::OP_NEW(_FILE_AND_LINE_)); + if (socketDescriptors[i].remotePortRakNetWasStartedOn_PS3_PSP2==0) + { + rns->s = (unsigned int) SocketLayer::CreateBoundSocket( socketDescriptors[i].port, true, addrToBind, 100, socketDescriptors[i].extraSocketOptions, socketDescriptors[i].socketFamily ); + } + else + { + + + + + + } + + if ((SOCKET)rns->s==(SOCKET)-1) + { + DerefAllSockets(); + return SOCKET_FAILED_TO_BIND; + } + + SocketLayer::GetSystemAddress( rns->s, &rns->boundAddress ); + + rns->remotePortRakNetWasStartedOn_PS3_PSP2=socketDescriptors[i].remotePortRakNetWasStartedOn_PS3_PSP2; + rns->extraSocketOptions=socketDescriptors[i].extraSocketOptions; + rns->userConnectionSocketIndex=i; + +#if RAKNET_SUPPORT_IPV6==0 + if (addrToBind==0) + rns->boundAddress.SetToLoopback(4); +#endif + + int zero=0; + if (SocketLayer::SendTo((SOCKET)rns->s, (const char*) &zero,4,rns->boundAddress, rns->remotePortRakNetWasStartedOn_PS3_PSP2, rns->extraSocketOptions, _FILE_AND_LINE_)!=0) + { + DerefAllSockets(); + return SOCKET_FAILED_TEST_SEND; + } + + socketList.Push(rns, _FILE_AND_LINE_ ); + + } + + + if ( maximumNumberOfPeers == 0 ) + { + // Don't allow more incoming connections than we have peers. + if ( maximumIncomingConnections > maxConnections ) + maximumIncomingConnections = maxConnections; + + maximumNumberOfPeers = maxConnections; + // 04/19/2006 - Don't overallocate because I'm no longer allowing connected pings. + // The disconnects are not consistently processed and the process was sloppy and complicated. + // Allocate 10% extra to handle new connections from players trying to connect when the server is full + //remoteSystemListSize = maxConnections;// * 11 / 10 + 1; + + // remoteSystemList in Single thread + //remoteSystemList = RakNet::OP_NEW( _FILE_AND_LINE_ ); + remoteSystemList = RakNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); + + remoteSystemLookup = RakNet::OP_NEW_ARRAY((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE, _FILE_AND_LINE_ ); + + activeSystemList = RakNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); + + for ( i = 0; i < maximumNumberOfPeers; i++ ) + //for ( i = 0; i < remoteSystemListSize; i++ ) + { + // remoteSystemList in Single thread + remoteSystemList[ i ].isActive = false; + remoteSystemList[ i ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystemList[ i ].guid = UNASSIGNED_RAKNET_GUID; + remoteSystemList[ i ].myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystemList[ i ].connectMode=RemoteSystemStruct::NO_ACTION; + remoteSystemList[ i ].MTUSize = defaultMTUSize; + remoteSystemList[ i ].remoteSystemIndex = (SystemIndex) i; +#ifdef _DEBUG + remoteSystemList[ i ].reliabilityLayer.ApplyNetworkSimulator(_packetloss, _minExtraPing, _extraPingVariance); +#endif + + // All entries in activeSystemList have valid pointers all the time. + activeSystemList[ i ] = &remoteSystemList[ i ]; + } + + for (unsigned int i=0; i < (unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE; i++) + { + remoteSystemLookup[i]=0; + } + } + + // For histogram statistics + // nextReadBytesTime=0; + // lastSentBytes=lastReceivedBytes=0; + + if ( endThreads ) + { + updateCycleIsRunning = false; + endThreads = false; + + ClearBufferedCommands(); + ClearBufferedPackets(); + ClearSocketQueryOutput(); + + if ( isMainLoopThreadActive == false ) + { + int errorCode; + + + + + + + + errorCode = RakNet::RakThread::Create(UpdateNetworkLoop, this, threadPriority); + + + if ( errorCode != 0 ) + { + Shutdown( 0, 0 ); + return FAILED_TO_CREATE_NETWORK_THREAD; + } + + RakAssert(isRecvFromLoopThreadActive.GetValue()==0); + for (i=0; is; + rpai[i].rakPeer=this; + + + + + + errorCode = RakNet::RakThread::Create(RecvFromLoop, &rpai[i], threadPriority); + + + if ( errorCode != 0 ) + { + Shutdown( 0, 0 ); + return FAILED_TO_CREATE_NETWORK_THREAD; + } + } + + + while ( isRecvFromLoopThreadActive.GetValue() < (uint32_t) socketDescriptorCount ) + RakSleep(10); + } + + // Wait for the threads to activate. When they are active they will set these variables to true + + while ( isMainLoopThreadActive == false ) + RakSleep(10); + + } + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->OnRakPeerStartup(); + } + + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->OnRakPeerStartup(); + } + +#ifdef USE_THREADED_SEND + RakNet::SendToThread::AddRef(); +#endif + + return RAKNET_STARTED; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Must be called while offline +// +// If you accept connections, you must call this or else security will not be enabled for incoming connections. +// +// This feature requires more round trips, bandwidth, and CPU time for the connection handshake +// x64 builds require under 25% of the CPU time of other builds +// +// See the Encryption sample for example usage +// +// Parameters: +// publicKey = A pointer to the public key for accepting new connections +// privateKey = A pointer to the private key for accepting new connections +// If the private keys are 0, then a new key will be generated when this function is called +// bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::InitializeSecurity(const char *public_key, const char *private_key, bool bRequireClientKey) +{ +#if LIBCAT_SECURITY==1 + if ( endThreads == false ) + return false; + + // Copy client public key requirement flag + _require_client_public_key = bRequireClientKey; + + if (_server_handshake) + { + CAT_AUDIT_PRINTF("AUDIT: Deleting old server_handshake %x\n", _server_handshake); + RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + } + if (_cookie_jar) + { + CAT_AUDIT_PRINTF("AUDIT: Deleting old cookie jar %x\n", _cookie_jar); + RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + } + + _server_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); + _cookie_jar = RakNet::OP_NEW(_FILE_AND_LINE_); + + CAT_AUDIT_PRINTF("AUDIT: Created new server_handshake %x\n", _server_handshake); + CAT_AUDIT_PRINTF("AUDIT: Created new cookie jar %x\n", _cookie_jar); + CAT_AUDIT_PRINTF("AUDIT: Running _server_handshake->Initialize()\n"); + + if (_server_handshake->Initialize(public_key, private_key)) + { + CAT_AUDIT_PRINTF("AUDIT: Successfully initialized, filling cookie jar with goodies, storing public key and setting using security flag to true\n"); + + _server_handshake->FillCookieJar(_cookie_jar); + + memcpy(my_public_key, public_key, sizeof(my_public_key)); + + _using_security = true; + return true; + } + + CAT_AUDIT_PRINTF("AUDIT: Failure to initialize so deleting server handshake and cookie jar; also setting using_security flag = false\n"); + + RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + _server_handshake=0; + RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + _cookie_jar=0; + _using_security = false; + return false; +#else + (void) public_key; + (void) private_key; + (void) bRequireClientKey; + + return false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description +// Must be called while offline +// Disables security for incoming connections. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DisableSecurity( void ) +{ +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: DisableSecurity() called, so deleting _server_handshake %x and cookie_jar %x\n", _server_handshake, _cookie_jar); + RakNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + _server_handshake=0; + RakNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + _cookie_jar=0; + + _using_security = false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToSecurityExceptionList(const char *ip) +{ + securityExceptionMutex.Lock(); + securityExceptionList.Insert(RakString(ip), _FILE_AND_LINE_); + securityExceptionMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromSecurityExceptionList(const char *ip) +{ + if (securityExceptionList.Size()==0) + return; + + if (ip==0) + { + securityExceptionMutex.Lock(); + securityExceptionList.Clear(false, _FILE_AND_LINE_); + securityExceptionMutex.Unlock(); + } + else + { + unsigned i=0; + securityExceptionMutex.Lock(); + while (i < securityExceptionList.Size()) + { + if (securityExceptionList[i].IPAddressMatch(ip)) + { + securityExceptionList[i]=securityExceptionList[securityExceptionList.Size()-1]; + securityExceptionList.RemoveAtIndex(securityExceptionList.Size()-1); + } + else + i++; + } + securityExceptionMutex.Unlock(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsInSecurityExceptionList(const char *ip) +{ + if (securityExceptionList.Size()==0) + return false; + + unsigned i=0; + securityExceptionMutex.Lock(); + for (; i < securityExceptionList.Size(); i++) + { + if (securityExceptionList[i].IPAddressMatch(ip)) + { + securityExceptionMutex.Unlock(); + return true; + } + } + securityExceptionMutex.Unlock(); + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no +// more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced +// to the maximum number of peers allowed. Defaults to 0. +// +// Parameters: +// numberAllowed - Maximum number of incoming connections allowed. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetMaximumIncomingConnections( unsigned short numberAllowed ) +{ + maximumIncomingConnections = numberAllowed; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the maximum number of incoming connections, which is always <= maxConnections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::GetMaximumIncomingConnections( void ) const +{ + return maximumIncomingConnections; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns how many open connections there are at this time +// \return the number of open connections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::NumberOfConnections(void) const +{ + DataStructures::List addresses; + DataStructures::List guids; + GetSystemList(addresses, guids); + return (unsigned short) addresses.Size(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sets the password incoming connections must match in the call to Connect (defaults to none) +// Pass 0 to passwordData to specify no password +// +// Parameters: +// passwordData: A data block that incoming connections must match. This can be just a password, or can be a stream of data. +// - Specify 0 for no password data +// passwordDataLength: The length in bytes of passwordData +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetIncomingPassword( const char* passwordData, int passwordDataLength ) +{ + //if (passwordDataLength > MAX_OFFLINE_DATA_LENGTH) + // passwordDataLength=MAX_OFFLINE_DATA_LENGTH; + + if (passwordDataLength > 255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? + // It won't overflow at least because incomingPasswordLength is an unsigned char + if (passwordDataLength>0) + memcpy(incomingPassword, passwordData, passwordDataLength); + incomingPasswordLength=(unsigned char)passwordDataLength; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetIncomingPassword( char* passwordData, int *passwordDataLength ) +{ + if (passwordData==0) + { + *passwordDataLength=incomingPasswordLength; + return; + } + + if (*passwordDataLength > incomingPasswordLength) + *passwordDataLength=incomingPasswordLength; + + if (*passwordDataLength>0) + memcpy(passwordData, incomingPassword, *passwordDataLength); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Call this to connect to the specified host (ip or domain name) and server port. +// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. +// This is a non-blocking connection. You know the connection is successful when IsConnected() returns true +// or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. If the connection is not +// successful, such as rejected connection or no response then neither of these things will happen. +// Requires that you first call Initialize +// +// Parameters: +// host: Either a dotted IP address or a domain name +// remotePort: Which port to connect to on the remote machine. +// passwordData: A data block that must match the data block on the server. This can be just a password, or can be a stream of data +// passwordDataLength: The length in bytes of passwordData +// +// Returns: +// True on successful initiation. False on incorrect parameters, internal error, or too many existing peers +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ConnectionAttemptResult RakPeer::Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ) +{ + // If endThreads is true here you didn't call Startup() first. + if ( host == 0 || endThreads || connectionSocketIndex>=socketList.Size() ) + return INVALID_PARAMETER; + + RakAssert(remotePort!=0); + + connectionSocketIndex=GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + if (passwordDataLength>255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? + // It won't overflow at least because outgoingPasswordLength is an unsigned char +// if (passwordDataLength>0) +// memcpy(outgoingPassword, passwordData, passwordDataLength); +// outgoingPasswordLength=(unsigned char) passwordDataLength; + + // 04/02/09 - Can't remember why I disabled connecting to self, but it seems to work + // Connecting to ourselves in the same instance of the program? +// if ( ( strcmp( host, "127.0.0.1" ) == 0 || strcmp( host, "0.0.0.0" ) == 0 ) && remotePort == mySystemAddress[0].port ) +// return false; + + return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, connectionSocketIndex, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ConnectionAttemptResult RakPeer::ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime) +{ + if ( host == 0 || endThreads || socket.IsNull() ) + return INVALID_PARAMETER; + + if (passwordDataLength>255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, 0, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime, socket ); + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Stops the network threads and close all connections. Multiple calls are ok. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::Shutdown( unsigned int blockDuration, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) +{ + unsigned i,j; + bool anyActive; + RakNet::TimeMS startWaitingTime; +// SystemAddress systemAddress; + RakNet::TimeMS time; + //unsigned short systemListSize = remoteSystemListSize; // This is done for threading reasons + unsigned short systemListSize = maximumNumberOfPeers; + + if ( blockDuration > 0 ) + { + for ( i = 0; i < systemListSize; i++ ) + { + // remoteSystemList in user thread + if (remoteSystemList[i].isActive) + NotifyAndFlagForShutdown(remoteSystemList[i].systemAddress, false, orderingChannel, disconnectionNotificationPriority); + } + + time = RakNet::GetTimeMS(); + startWaitingTime = time; + while ( time - startWaitingTime < blockDuration ) + { + anyActive=false; + for (j=0; j < systemListSize; j++) + { + // remoteSystemList in user thread + if (remoteSystemList[j].isActive) + { + anyActive=true; + break; + } + } + + // If this system is out of packets to send, then stop waiting + if ( anyActive==false ) + break; + + // This will probably cause the update thread to run which will probably + // send the disconnection notification + + RakSleep(15); + time = RakNet::GetTimeMS(); + } + } + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->OnRakPeerShutdown(); + } + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->OnRakPeerShutdown(); + } + + activeSystemListSize=0; + + quitAndDataEvents.SetEvent(); + + endThreads = true; + // Get recvfrom to unblock + for (i=0; i < socketList.Size(); i++) + { + SystemAddress sa2 = socketList[i]->boundAddress; + if (SocketLayer::SendTo(socketList[i]->s, (const char*) &i,1,sa2, socketList[i]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[i]->extraSocketOptions, _FILE_AND_LINE_)!=0) + break; + } + while ( isMainLoopThreadActive ) + { + endThreads = true; + RakSleep(15); + } + + RakNet::TimeMS timeout = RakNet::GetTimeMS()+1000; + while ( isRecvFromLoopThreadActive.GetValue()>0 && RakNet::GetTimeMS()boundAddress; + SocketLayer::SendTo(socketList[i]->s, (const char*) &i,1,sa2, socketList[i]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[i]->extraSocketOptions, _FILE_AND_LINE_); + } + + RakSleep(30); + } + +// char c=0; +// unsigned int socketIndex; + // remoteSystemList in Single thread + for ( i = 0; i < systemListSize; i++ ) + { + // Reserve this reliability layer for ourselves + remoteSystemList[ i ].isActive = false; + + // Remove any remaining packets + remoteSystemList[ i ].reliabilityLayer.Reset(false, remoteSystemList[ i ].MTUSize, false); + remoteSystemList[ i ].rakNetSocket.SetNull(); + + } + + + // Setting maximumNumberOfPeers to 0 allows remoteSystemList to be reallocated in Initialize. + // Setting remoteSystemListSize prevents threads from accessing the reliability layer + maximumNumberOfPeers = 0; + //remoteSystemListSize = 0; + + // Free any packets the user didn't deallocate + packetReturnMutex.Lock(); + for (i=0; i < packetReturnQueue.Size(); i++) + DeallocatePacket(packetReturnQueue[i]); + packetReturnQueue.Clear(_FILE_AND_LINE_); + packetReturnMutex.Unlock(); + packetAllocationPoolMutex.Lock(); + packetAllocationPool.Clear(_FILE_AND_LINE_); + packetAllocationPoolMutex.Unlock(); + + if (isRecvFromLoopThreadActive.GetValue()>0) + { + timeout = RakNet::GetTimeMS()+1000; + while ( isRecvFromLoopThreadActive.GetValue()>0 && RakNet::GetTimeMS() addresses; + DataStructures::List guids; + GetSystemList(addresses, guids); + if (remoteSystems) + { + unsigned short i; + for (i=0; i < *numberOfSystems && i < addresses.Size(); i++) + remoteSystems[i]=addresses[i]; + *numberOfSystems=i; + } + else + { + *numberOfSystems=(unsigned short) addresses.Size(); + } + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::GetNextSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + uint32_t retVal = sendReceiptSerial; + sendReceiptSerialMutex.Unlock(); + return retVal; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::IncrementNextSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + uint32_t returned = sendReceiptSerial; + if (++sendReceiptSerial==0) + sendReceiptSerial=1; + sendReceiptSerialMutex.Unlock(); + return returned; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sends a block of data to the specified system that you are connected to. +// This function only works while the client is connected (Use the Connect function). +// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM +// +// Parameters: +// data: The block of data to send +// length: The size in bytes of the data to send +// bitStream: The bitstream to send +// priority: What priority level to send on. +// reliability: How reliability to send this data +// orderingChannel: When using ordered or sequenced packets, what channel to order these on. +// - Packets are only ordered relative to other packets on the same stream +// systemAddress: Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none +// broadcast: True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. +// Returns: +// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( data && length > 0 ); +#endif + RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); + RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + if ( data == 0 || length < 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined()) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback(data,length); + + if (reliability>=UNRELIABLE_WITH_ACK_RECEIPT) + { + char buff[5]; + buff[0]=ID_SND_RECEIPT_ACKED; + sendReceiptSerialMutex.Lock(); + memcpy(buff+1, &sendReceiptSerial, 4); + sendReceiptSerialMutex.Unlock(); + SendLoopback( buff, 5 ); + } + + return usedSendReceipt; + } + + SendBuffered(data, length*8, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + return usedSendReceipt; +} + +void RakPeer::SendLoopback( const char *data, const int length ) +{ + if ( data == 0 || length < 0 ) + return; + + Packet *packet = AllocPacket(length, _FILE_AND_LINE_); + memcpy(packet->data, data, length); + packet->systemAddress = GetLoopbackAddress(); + packet->guid=myGuid; + PushBackPacket(packet, false); +} + +uint32_t RakPeer::Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( bitStream->GetNumberOfBytesUsed() > 0 ); +#endif + + RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); + RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + if ( bitStream->GetNumberOfBytesUsed() == 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined() ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback((const char*) bitStream->GetData(),bitStream->GetNumberOfBytesUsed()); + if (reliability>=UNRELIABLE_WITH_ACK_RECEIPT) + { + char buff[5]; + buff[0]=ID_SND_RECEIPT_ACKED; + sendReceiptSerialMutex.Lock(); + memcpy(buff+1, &sendReceiptSerial,4); + sendReceiptSerialMutex.Unlock(); + SendLoopback( buff, 5 ); + } + return usedSendReceipt; + } + + // Sends need to be buffered and processed in the update thread because the systemAddress associated with the reliability layer can change, + // from that thread, resulting in a send to the wrong player! While I could mutex the systemAddress, that is much slower than doing this + SendBuffered((const char*)bitStream->GetData(), bitStream->GetNumberOfBitsUsed(), priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + + return usedSendReceipt; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Sends multiple blocks of data, concatenating them automatically. +// +// This is equivalent to: +// RakNet::BitStream bs; +// bs.WriteAlignedBytes(block1, blockLength1); +// bs.WriteAlignedBytes(block2, blockLength2); +// bs.WriteAlignedBytes(block3, blockLength3); +// Send(&bs, ...) +// +// This function only works while the connected +// \param[in] data An array of pointers to blocks of data +// \param[in] lengths An array of integers indicating the length of each block of data +// \param[in] numParameters Length of the arrays data and lengths +// \param[in] priority What priority level to send on. See PacketPriority.h +// \param[in] reliability How reliability to send this data. See PacketPriority.h +// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream +// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none +// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. +// \return False if we are not connected to the specified recipient. True otherwise +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( data ); +#endif + + if ( data == 0 || lengths == 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if (numParameters==0) + return 0; + + if (lengths==0) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined() ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + SendBufferedList(data, lengths, numParameters, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + return usedSendReceipt; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. +// Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct +// +// Returns: +// 0 if no packets are waiting to be handled, otherwise an allocated packet +// If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected +// This also updates all memory blocks associated with synchronized memory and distributed objects +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +#ifdef _MSC_VER +#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized +#endif +Packet* RakPeer::Receive( void ) +{ + if ( !( IsActive() ) ) + return 0; + + RakNet::Packet *packet; +// Packet **threadPacket; + PluginReceiveResult pluginResult; + + int offset; + unsigned int i; + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->Update(); + } + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->Update(); + } + + do + { + packetReturnMutex.Lock(); + if (packetReturnQueue.IsEmpty()) + packet=0; + else + packet = packetReturnQueue.Pop(); + packetReturnMutex.Unlock(); + if (packet==0) + return 0; + +// unsigned char msgId; + if ( ( packet->length >= sizeof(unsigned char) + sizeof( RakNet::Time ) ) && + ( (unsigned char) packet->data[ 0 ] == ID_TIMESTAMP ) ) + { + offset = sizeof(unsigned char); + ShiftIncomingTimestamp( packet->data + offset, packet->systemAddress ); +// msgId=packet->data[sizeof(unsigned char) + sizeof( RakNet::Time )]; + } +// else + // msgId=packet->data[0]; + + // Some locally generated packets need to be processed by plugins, for example ID_FCM2_NEW_HOST + // The plugin itself should intercept these messages generated remotely +// if (packet->wasGeneratedLocally) +// return packet; + + + CallPluginCallbacks(pluginListTS, packet); + CallPluginCallbacks(pluginListNTS, packet); + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginResult=pluginListTS[i]->OnReceive(packet); + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( packet ); + packet=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + packet=0; + break; + } + } + + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginResult=pluginListNTS[i]->OnReceive(packet); + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( packet ); + packet=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + packet=0; + break; + } + } + + } while(packet==0); + +#ifdef _DEBUG + RakAssert( packet->data ); +#endif + + return packet; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Call this to deallocate a packet returned by Receive +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DeallocatePacket( Packet *packet ) +{ + if ( packet == 0 ) + return; + + if (packet->deleteData) + { + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + packet->~Packet(); + packetAllocationPoolMutex.Lock(); + packetAllocationPool.Release(packet,_FILE_AND_LINE_); + packetAllocationPoolMutex.Unlock(); + } + else + { + rakFree_Ex(packet, _FILE_AND_LINE_ ); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the total number of connections we are allowed +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::GetMaximumNumberOfPeers( void ) const +{ + return maximumNumberOfPeers; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). +// +// Parameters: +// target: Which connection to close +// sendDisconnectionNotification: True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. +// channel: If blockDuration > 0, the disconnect packet will be sent on this channel +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) +{ + /* + // This only be called from the user thread, for the user shutting down. + // From the network thread, this should occur because of ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST + unsigned j; + for (j=0; j < messageHandlerList.Size(); j++) + { + messageHandlerList[j]->OnClosedConnection( + target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress, + target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid, + LCR_CLOSED_BY_USER); + } + */ + + CloseConnectionInternal(target, sendDisconnectionNotification, false, orderingChannel, disconnectionNotificationPriority); + + // 12/14/09 Return ID_CONNECTION_LOST when calling CloseConnection with sendDisconnectionNotification==false, elsewise it is never returned + if (sendDisconnectionNotification==false && GetConnectionState(target)==IS_CONNECTED) + { + Packet *packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection + packet->guid = target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid; + packet->systemAddress = target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress; + packet->systemAddress.systemIndex = (SystemIndex) GetIndexFromSystemAddress(packet->systemAddress); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + packet->wasGeneratedLocally=true; // else processed twice + AddPacketToProducer(packet); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Cancel a pending connection attempt +// If we are already connected, the connection stays open +// \param[in] target Which system to cancel +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CancelConnectionAttempt( const SystemAddress target ) +{ + unsigned int i; + + // Cancel pending connection attempt, if there is one + i=0; + requestedConnectionQueueMutex.Lock(); + while (i < requestedConnectionQueue.Size()) + { + if (requestedConnectionQueue[i]->systemAddress==target) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Deleting requestedConnectionQueue %i client_handshake %x\n", i, requestedConnectionQueue[ i ]->client_handshake); + RakNet::OP_DELETE(requestedConnectionQueue[i]->client_handshake, _FILE_AND_LINE_ ); +#endif + RakNet::OP_DELETE(requestedConnectionQueue[i], _FILE_AND_LINE_ ); + requestedConnectionQueue.RemoveAtIndex(i); + break; + } + else + i++; + } + requestedConnectionQueueMutex.Unlock(); + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ConnectionState RakPeer::GetConnectionState(const AddressOrGUID systemIdentifier) +{ + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemIdentifier.systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + return IS_PENDING; + } + } + requestedConnectionQueueMutex.Unlock(); + } + + int index; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); + } + else + { + index = GetIndexFromGuid(systemIdentifier.rakNetGuid); + } + + if (index==-1) + return IS_NOT_CONNECTED; + + if (remoteSystemList[index].isActive==false) + return IS_DISCONNECTED; + + switch (remoteSystemList[index].connectMode) + { + case RemoteSystemStruct::DISCONNECT_ASAP: + return IS_DISCONNECTING; + case RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY: + return IS_SILENTLY_DISCONNECTING; + case RemoteSystemStruct::DISCONNECT_ON_NO_ACK: + return IS_DISCONNECTING; + case RemoteSystemStruct::REQUESTED_CONNECTION: + return IS_CONNECTING; + case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: + return IS_CONNECTING; + case RemoteSystemStruct::UNVERIFIED_SENDER: + return IS_CONNECTING; + case RemoteSystemStruct::CONNECTED: + return IS_CONNECTED; + default: + return IS_NOT_CONNECTED; + } + + return IS_NOT_CONNECTED; +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. +// +// Parameters +// systemAddress - The systemAddress to search for +// +// Returns +// An integer from 0 to the maximum number of peers -1, or -1 if that player is not found +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress ) const +{ + return GetIndexFromSystemAddress(systemAddress, false); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// This function is only useful for looping through all players. +// +// Parameters +// index - an integer between 0 and the maximum number of players allowed - 1. +// +// Returns +// A valid systemAddress or UNASSIGNED_SYSTEM_ADDRESS if no such player at that index +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetSystemAddressFromIndex( int index ) +{ + // remoteSystemList in user thread + //if ( index >= 0 && index < remoteSystemListSize ) + if ( index >= 0 && index < maximumNumberOfPeers ) + if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail + return remoteSystemList[ index ].systemAddress; + + return UNASSIGNED_SYSTEM_ADDRESS; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Same as GetSystemAddressFromIndex but returns RakNetGUID +// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. +// \return The RakNetGUID +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetGUID RakPeer::GetGUIDFromIndex( int index ) +{ + // remoteSystemList in user thread + //if ( index >= 0 && index < remoteSystemListSize ) + if ( index >= 0 && index < maximumNumberOfPeers ) + if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail + return remoteSystemList[ index ].guid; + + return UNASSIGNED_RAKNET_GUID; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient +// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system +// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. +// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const +{ + addresses.Clear(false, _FILE_AND_LINE_); + guids.Clear(false, _FILE_AND_LINE_); + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) + { + addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); + guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Bans an IP from connecting. Banned IPs persist between connections. +// +// Parameters +// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban +// All IP addresses starting with 128.0.0 +// milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToBanList( const char *IP, RakNet::TimeMS milliseconds ) +{ + unsigned index; + RakNet::TimeMS time = RakNet::GetTimeMS(); + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return ; + + // If this guy is already in the ban list, do nothing + index = 0; + + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + if ( strcmp( IP, banList[ index ]->IP ) == 0 ) + { + // Already in the ban list. Just update the time + if (milliseconds==0) + banList[ index ]->timeout=0; // Infinite + else + banList[ index ]->timeout=time+milliseconds; + banListMutex.Unlock(); + return; + } + } + + banListMutex.Unlock(); + + BanStruct *banStruct = RakNet::OP_NEW( _FILE_AND_LINE_ ); + banStruct->IP = (char*) rakMalloc_Ex( 16, _FILE_AND_LINE_ ); + if (milliseconds==0) + banStruct->timeout=0; // Infinite + else + banStruct->timeout=time+milliseconds; + strcpy( banStruct->IP, IP ); + banListMutex.Lock(); + banList.Insert( banStruct, _FILE_AND_LINE_ ); + banListMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allows a previously banned IP to connect. +// +// Parameters +// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban +// All IP addresses starting with 128.0.0 +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromBanList( const char *IP ) +{ + unsigned index; + BanStruct *temp; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return ; + + index = 0; + temp=0; + + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + if ( strcmp( IP, banList[ index ]->IP ) == 0 ) + { + temp = banList[ index ]; + banList[ index ] = banList[ banList.Size() - 1 ]; + banList.RemoveAtIndex( banList.Size() - 1 ); + break; + } + } + + banListMutex.Unlock(); + + if (temp) + { + rakFree_Ex(temp->IP, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allows all previously banned IPs to connect. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBanList( void ) +{ + unsigned index; + index = 0; + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + rakFree_Ex(banList[ index ]->IP, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(banList[ index ], _FILE_AND_LINE_); + } + + banList.Clear(false, _FILE_AND_LINE_); + + banListMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetLimitIPConnectionFrequency(bool b) +{ + limitConnectionFrequencyFromTheSameIP=b; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Determines if a particular IP is banned. +// +// Parameters +// IP - Complete dotted IP address +// +// Returns +// True if IP matches any IPs in the ban list, accounting for any wildcards. +// False otherwise. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsBanned( const char *IP ) +{ + unsigned banListIndex, characterIndex; + RakNet::TimeMS time; + BanStruct *temp; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return false; + + banListIndex = 0; + + if ( banList.Size() == 0 ) + return false; // Skip the mutex if possible + + time = RakNet::GetTimeMS(); + + banListMutex.Lock(); + + while ( banListIndex < banList.Size() ) + { + if (banList[ banListIndex ]->timeout>0 && banList[ banListIndex ]->timeoutIP, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + else + { + characterIndex = 0; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) + { + if ( banList[ banListIndex ]->IP[ characterIndex ] == IP[ characterIndex ] ) + { + // Equal characters + + if ( IP[ characterIndex ] == 0 ) + { + banListMutex.Unlock(); + // End of the string and the strings match + + return true; + } + + characterIndex++; + } + + else + { + if ( banList[ banListIndex ]->IP[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) + { + // End of one of the strings + break; + } + + // Characters do not match + if ( banList[ banListIndex ]->IP[ characterIndex ] == '*' ) + { + banListMutex.Unlock(); + + // Domain is banned. + return true; + } + + // Characters do not match and it is not a * + break; + } + } + + banListIndex++; + } + } + + banListMutex.Unlock(); + + // No match found. + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Send a ping to the specified connected system. +// +// Parameters: +// target - who to ping +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::Ping( const SystemAddress target ) +{ + PingInternal(target, false, UNRELIABLE); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Send a ping to the specified unconnected system. +// The remote system, if it is Initialized, will respond with ID_UNCONNECTED_PONG. +// The final ping time will be encoded in the following sizeof(RakNet::TimeMS) bytes. (Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h +// +// Parameters: +// host: Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. +// remotePort: Which port to connect to on the remote machine. +// onlyReplyOnAcceptingConnections: Only request a reply if the remote system has open connections +// connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex ) +{ + if ( host == 0 ) + return false; + + // If this assert hits then Startup wasn't called or the call failed. + RakAssert(connectionSocketIndex < socketList.Size()); + +// if ( IsActive() == false ) +// return; + + RakNet::BitStream bitStream( sizeof(unsigned char) + sizeof(RakNet::Time) ); + if ( onlyReplyOnAcceptingConnections ) + bitStream.Write((MessageID)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); + else + bitStream.Write((MessageID)ID_UNCONNECTED_PING); + + bitStream.Write(RakNet::GetTime()); + + bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + + // No timestamp for 255.255.255.255 + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->boundAddress.GetIPVersion()); + systemAddress.FixForIPVersion(socketList[realIndex]->boundAddress); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( socketList[realIndex]->s, (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, socketList[realIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[realIndex]->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the average of all ping times read for a specified target +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetAveragePing( const AddressOrGUID systemIdentifier ) +{ + int sum, quantity; + RemoteSystemStruct *remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + + for ( sum = 0, quantity = 0; quantity < PING_TIMES_ARRAY_SIZE; quantity++ ) + { + if ( remoteSystem->pingAndClockDifferential[ quantity ].pingTime == 65535 ) + break; + else + sum += remoteSystem->pingAndClockDifferential[ quantity ].pingTime; + } + + if ( quantity > 0 ) + return sum / quantity; + else + return -1; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the last ping time read for the specific player or -1 if none read yet +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetLastPing( const AddressOrGUID systemIdentifier ) const +{ + RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + +// return (int)(remoteSystem->reliabilityLayer.GetAckPing()/(RakNet::TimeUS)1000); + + if ( remoteSystem->pingAndClockDifferentialWriteIndex == 0 ) + return remoteSystem->pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE - 1 ].pingTime; + else + return remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex - 1 ].pingTime; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the lowest ping time read or -1 if none read yet +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetLowestPing( const AddressOrGUID systemIdentifier ) const +{ + RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + + return remoteSystem->lowestPing; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Ping the remote systems every so often. This is off by default +// This will work anytime +// +// Parameters: +// doPing - True to start occasional pings. False to stop them. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetOccasionalPing( bool doPing ) +{ + occasionalPing = doPing; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Length should be under 400 bytes, as a security measure against flood attacks +// Sets the data to send with an (LAN server discovery) /(offline ping) response +// See the Ping sample project for how this is used. +// data: a block of data to store, or 0 for none +// length: The length of data in bytes, or 0 for none +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetOfflinePingResponse( const char *data, const unsigned int length ) +{ + RakAssert(length < 400); + + rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); + offlinePingResponse.Reset(); + + if ( data && length > 0 ) + offlinePingResponse.Write( data, length ); + + rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns pointers to a copy of the data passed to SetOfflinePingResponse +// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() +// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() +// \sa SetOfflinePingResponse +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetOfflinePingResponse( char **data, unsigned int *length ) +{ + rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); + *data = (char*) offlinePingResponse.GetData(); + *length = (int) offlinePingResponse.GetNumberOfBytesUsed(); + rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the unique SystemAddress that represents you on the the network +// Note that unlike in previous versions, this is a struct and is not sequential +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetInternalID( const SystemAddress systemAddress, const int index ) const +{ + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + return ipList[index]; + } + else + { + +// SystemAddress returnValue; + RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, false, true ); + if (remoteSystem==0) + return UNASSIGNED_SYSTEM_ADDRESS; + + return remoteSystem->theirInternalSystemAddress[index]; + /* + sockaddr_in sa; + socklen_t len = sizeof(sa); + if (getsockname__(connectionSockets[remoteSystem->connectionSocketIndex], (sockaddr*)&sa, &len)!=0) + return UNASSIGNED_SYSTEM_ADDRESS; + returnValue.port=ntohs(sa.sin_port); + returnValue.binaryAddress=sa.sin_addr.s_addr; + return returnValue; +*/ + + + + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the unique address identifier that represents you on the the network and is based on your external +// IP / port (the IP / port the specified player uses to communicate with you) +// Note that unlike in previous versions, this is a struct and is not sequential +// +// Parameters: +// target: Which remote system you are referring to for your external ID +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetExternalID( const SystemAddress target ) const +{ + unsigned i; + SystemAddress inactiveExternalId; + + inactiveExternalId=UNASSIGNED_SYSTEM_ADDRESS; + + if (target==UNASSIGNED_SYSTEM_ADDRESS) + return firstExternalID; + + // First check for active connection with this systemAddress + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].systemAddress == target ) + { + if ( remoteSystemList[ i ].isActive ) + return remoteSystemList[ i ].myExternalSystemAddress; + else if (remoteSystemList[ i ].myExternalSystemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + inactiveExternalId=remoteSystemList[ i ].myExternalSystemAddress; + } + } + + return inactiveExternalId; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +const RakNetGUID RakPeer::GetMyGUID(void) const +{ + return myGuid; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetMyBoundAddress(const int socketIndex) +{ + DataStructures::List > sockets; + GetSockets( sockets ); + if (sockets.Size()>0) + return sockets[socketIndex]->boundAddress; + else + return UNASSIGNED_SYSTEM_ADDRESS; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +const RakNetGUID& RakPeer::GetGuidFromSystemAddress( const SystemAddress input ) const +{ + if (input==UNASSIGNED_SYSTEM_ADDRESS) + return myGuid; + + if (input.systemIndex!=(SystemIndex)-1 && input.systemIndexreliabilityLayer.SetTimeoutTime(timeMS); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RakNet::TimeMS RakPeer::GetTimeoutTime( const SystemAddress target ) +{ + if (target==UNASSIGNED_SYSTEM_ADDRESS) + { + return defaultTimeoutTime; + } + else + { + RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( target, false, true ); + + if ( remoteSystem != 0 ) + remoteSystem->reliabilityLayer.GetTimeoutTime(); + } + return defaultTimeoutTime; +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the current MTU size +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetMTUSize( const SystemAddress target ) const +{ + if (target!=UNASSIGNED_SYSTEM_ADDRESS) + { + RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(target, false, true); + if (rss) + return rss->MTUSize; + } + return defaultMTUSize; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the number of IP addresses we have +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetNumberOfAddresses( void ) +{ + + int i = 0; + + while ( ipList[ i ]!=UNASSIGNED_SYSTEM_ADDRESS ) + i++; + + return i; + + + + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns an IP address at index 0 to GetNumberOfAddresses-1 +// \param[in] index index into the list of IP addresses +// \return The local IP address at this index +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +const char* RakPeer::GetLocalIP( unsigned int index ) +{ + if (IsActive()==false) + { + // Fill out ipList structure + + SocketLayer::GetMyIP( ipList ); + + } + + + static char str[128]; + ipList[index].ToString(false,str); + return str; + + + + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Is this a local IP? +// \param[in] An IP address to check +// \return True if this is one of the IP addresses returned by GetLocalIP +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsLocalIP( const char *ip ) +{ + if (ip==0 || ip[0]==0) + return false; + + + if (strcmp(ip, "127.0.0.1")==0 || strcmp(ip, "localhost")==0) + return true; + + int num = GetNumberOfAddresses(); + int i; + for (i=0; i < num; i++) + { + if (strcmp(ip, GetLocalIP(i))==0) + return true; + } + + + + + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary +// when connection to servers with multiple IP addresses +// +// Parameters: +// allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AllowConnectionResponseIPMigration( bool allow ) +{ + allowConnectionResponseIPMigration = allow; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sends a message ID_ADVERTISE_SYSTEM to the remote unconnected system. +// This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through +// +// Requires: +// The sender and recipient must already be started via a successful call to Initialize +// +// host: Either a dotted IP address or a domain name +// remotePort: Which port to connect to on the remote machine. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex ) +{ + RakNet::BitStream bs; + bs.Write((MessageID)ID_ADVERTISE_SYSTEM); + bs.WriteAlignedBytes((const unsigned char*) data,dataLength); + return SendOutOfBand(host, remotePort, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), connectionSocketIndex ); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. +// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived +// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. +// Defaults to 0 (never return this notification) +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSplitMessageProgressInterval(int interval) +{ + RakAssert(interval>=0); + splitMessageProgressInterval=interval; + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + remoteSystemList[ i ].reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns what was passed to SetSplitMessageProgressInterval() +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetSplitMessageProgressInterval(void) const +{ + return splitMessageProgressInterval; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Set how long to wait before giving up on sending an unreliable message +// Useful if the network is clogged up. +// Set to 0 or less to never timeout. Defaults to 0. +// timeoutMS How many ms to wait before simply not sending an unreliable message. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetUnreliableTimeout(RakNet::TimeMS timeoutMS) +{ + unreliableTimeout=timeoutMS; + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + remoteSystemList[ i ].reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Send a message to host, with the IP socket option TTL set to 3 +// This message will not reach the host, but will open the router. +// Used for NAT-Punchthrough +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex ) +{ + char fakeData[2]; + fakeData[0]=0; + fakeData[1]=1; + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + SystemAddress systemAddress = socketList[realIndex]->boundAddress; + systemAddress.FromStringExplicitPort(host,remotePort); + SocketLayer::SendToTTL( socketList[realIndex]->s, (char*)fakeData, 2, systemAddress, ttl ); +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Attatches a Plugin interface to run code automatically on message receipt in the Receive call +// +// \param messageHandler Pointer to a plugin to attach +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AttachPlugin( PluginInterface2 *plugin ) +{ + bool isNotThreadsafe = plugin->UsesReliabilityLayer(); + if (isNotThreadsafe) + { + if (pluginListNTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + plugin->SetRakPeerInterface(this); + plugin->OnAttach(); + pluginListNTS.Insert(plugin, _FILE_AND_LINE_); + } + } + else + { + if (pluginListTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + plugin->SetRakPeerInterface(this); + plugin->OnAttach(); + pluginListTS.Insert(plugin, _FILE_AND_LINE_); + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Detaches a Plugin interface to run code automatically on message receipt +// +// \param messageHandler Pointer to a plugin to detach +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DetachPlugin( PluginInterface2 *plugin ) +{ + if (plugin==0) + return; + + unsigned int index; + + bool isNotThreadsafe = plugin->UsesReliabilityLayer(); + if (isNotThreadsafe) + { + index = pluginListNTS.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + // Unordered list so delete from end for speed + pluginListNTS[index]=pluginListNTS[pluginListNTS.Size()-1]; + pluginListNTS.RemoveFromEnd(); + } + } + else + { + index = pluginListTS.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + // Unordered list so delete from end for speed + pluginListTS[index]=pluginListTS[pluginListTS.Size()-1]; + pluginListTS.RemoveFromEnd(); + } + } + plugin->OnDetach(); + plugin->SetRakPeerInterface(0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Put a packet back at the end of the receive queue in case you don't want to deal with it immediately +// +// packet The packet you want to push back. +// pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PushBackPacket( Packet *packet, bool pushAtHead) +{ + if (packet==0) + return; + + unsigned i; + for (i=0; i < pluginListTS.Size(); i++) + pluginListTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); + + packetReturnMutex.Lock(); + if (pushAtHead) + packetReturnQueue.PushAtHead(packet,0,_FILE_AND_LINE_); + else + packetReturnQueue.Push(packet,_FILE_AND_LINE_); + packetReturnMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress) +{ + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = 0; + bcs->systemIdentifier.systemAddress=systemAddress; + bcs->systemIdentifier.rakNetGuid=guid; + bcs->command=BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS; + bufferedCommands.Push(bcs); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +Packet* RakPeer::AllocatePacket(unsigned dataSize) +{ + return AllocPacket(dataSize, _FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetSmartPtr RakPeer::GetSocket( const SystemAddress target ) +{ + // Send a query to the thread to get the socket, and return when we got it + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; + bcs->systemIdentifier=target; + bcs->data=0; + bufferedCommands.Push(bcs); + + // Block up to one second to get the socket, although it should actually take virtually no time + SocketQueryOutput *sqo; + RakNet::TimeMS stopWaiting = RakNet::GetTimeMS()+1000; + DataStructures::List > output; + while (RakNet::GetTimeMS() < stopWaiting) + { + if (isMainLoopThreadActive==false) + return RakNetSmartPtr(); + + RakSleep(0); + + sqo = socketQueryOutput.Pop(); + if (sqo) + { + output=sqo->sockets; + sqo->sockets.Clear(false, _FILE_AND_LINE_); + socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); + if (output.Size()) + return output[0]; + break; + } + } + return RakNetSmartPtr(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSockets( DataStructures::List > &sockets ) +{ + sockets.Clear(false, _FILE_AND_LINE_); + + // Send a query to the thread to get the socket, and return when we got it + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; + bcs->systemIdentifier=UNASSIGNED_SYSTEM_ADDRESS; + bcs->data=0; + bufferedCommands.Push(bcs); + + // Block up to one second to get the socket, although it should actually take virtually no time + SocketQueryOutput *sqo; + RakNetSmartPtr output; + while (1) + { + if (isMainLoopThreadActive==false) + return; + + RakSleep(0); + + sqo = socketQueryOutput.Pop(); + if (sqo) + { + sockets=sqo->sockets; + sqo->sockets.Clear(false, _FILE_AND_LINE_); + socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); + return; + } + } + return; +} +void RakPeer::ReleaseSockets( DataStructures::List > &sockets ) +{ + sockets.Clear(false,_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Adds simulated ping and packet loss to the outgoing data flow. +// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance) +{ +#ifdef _DEBUG + if (remoteSystemList) + { + unsigned short i; + for (i=0; i < maximumNumberOfPeers; i++) + //for (i=0; i < remoteSystemListSize; i++) + remoteSystemList[i].reliabilityLayer.ApplyNetworkSimulator(packetloss, minExtraPing, extraPingVariance); + } + + _packetloss=packetloss; + _minExtraPing=minExtraPing; + _extraPingVariance=extraPingVariance; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void RakPeer::SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ) +{ + maxOutgoingBPS=maxBitsPerSecond; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns if you previously called ApplyNetworkSimulator +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsNetworkSimulatorActive( void ) +{ +#ifdef _DEBUG + return _packetloss>0 || _minExtraPing>0 || _extraPingVariance>0; +#else + return false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::WriteOutOfBandHeader(RakNet::BitStream *bitStream) +{ + bitStream->Write((MessageID)ID_OUT_OF_BAND_INTERNAL); + bitStream->Write(myGuid); + bitStream->WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData) +{ + userUpdateThreadPtr=_userUpdateThreadPtr; + userUpdateThreadData=_userUpdateThreadData; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex ) +{ + if ( IsActive() == false ) + return false; + + if (host==0 || host[0]==0) + return false; + + // If this assert hits then Startup wasn't called or the call failed. + RakAssert(connectionSocketIndex < socketList.Size()); + + // This is a security measure. Don't send data longer than this value + RakAssert(dataLength <= (MAX_OFFLINE_DATA_LENGTH + sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID))); + + if (host==0) + return false; + + // 34 bytes + RakNet::BitStream bitStream; + WriteOutOfBandHeader(&bitStream); + + if (dataLength>0) + { + bitStream.Write(data, dataLength); + } + + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->boundAddress.GetIPVersion()); + systemAddress.FixForIPVersion(socketList[realIndex]->boundAddress); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); + + SocketLayer::SendTo( socketList[realIndex]->s, (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, socketList[realIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[realIndex]->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetStatistics * RakPeer::GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns ) +{ + static RakNetStatistics staticStatistics; + RakNetStatistics *systemStats; + if (rns==0) + systemStats=&staticStatistics; + else + systemStats=rns; + + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + bool firstWrite=false; + // Return a crude sum + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].isActive) + { + RakNetStatistics rnsTemp; + remoteSystemList[ i ].reliabilityLayer.GetStatistics(&rnsTemp); + + if (firstWrite==false) + { + memcpy(systemStats, &rnsTemp, sizeof(RakNetStatistics)); + firstWrite=true; + } + else + (*systemStats)+=rnsTemp; + } + } + return systemStats; + } + else + { + RemoteSystemStruct * rss; + rss = GetRemoteSystemFromSystemAddress( systemAddress, false, false ); + if ( rss && endThreads==false ) + { + rss->reliabilityLayer.GetStatistics(systemStats); + return systemStats; + } + } + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::GetStatistics( const int index, RakNetStatistics *rns ) +{ + if (index < maximumNumberOfPeers && remoteSystemList[ index ].isActive) + { + remoteSystemList[ index ].reliabilityLayer.GetStatistics(rns); + return true; + } + return false; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetReceiveBufferSize(void) +{ + unsigned int size; + packetReturnMutex.Lock(); + size=packetReturnQueue.Size(); + packetReturnMutex.Unlock(); + return size; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const +{ + unsigned i; + + if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) + return -1; + + if (systemAddress.systemIndex!=(SystemIndex)-1 && systemAddress.systemIndex < maximumNumberOfPeers && remoteSystemList[systemAddress.systemIndex].systemAddress==systemAddress && remoteSystemList[ systemAddress.systemIndex ].isActive) + return systemAddress.systemIndex; + + if (calledFromNetworkThread) + { + return GetRemoteSystemIndex(systemAddress); + } + else + { + // remoteSystemList in user and network thread + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].systemAddress == systemAddress ) + return i; + + // If no active results found, try previously active results. + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].systemAddress == systemAddress ) + return i; + } + + return -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromGuid( const RakNetGUID guid ) +{ + unsigned i; + + if ( guid == UNASSIGNED_RAKNET_GUID ) + return -1; + + if (guid.systemIndex!=(SystemIndex)-1 && guid.systemIndex < maximumNumberOfPeers && remoteSystemList[guid.systemIndex].guid==guid && remoteSystemList[ guid.systemIndex ].isActive) + return guid.systemIndex; + + // remoteSystemList in user and network thread + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].guid == guid ) + return i; + + // If no active results found, try previously active results. + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].guid == guid ) + return i; + + return -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +#if LIBCAT_SECURITY==1 +bool RakPeer::GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey) +{ + CAT_AUDIT_PRINTF("AUDIT: In GenerateConnectionRequestChallenge()\n"); + + rcs->client_handshake = 0; + rcs->publicKeyMode = PKM_INSECURE_CONNECTION; + + if (!publicKey) return true; + + switch (publicKey->publicKeyMode) + { + default: + case PKM_INSECURE_CONNECTION: + break; + + case PKM_ACCEPT_ANY_PUBLIC_KEY: + CAT_OBJCLR(rcs->remote_public_key); + rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); + + rcs->publicKeyMode = PKM_ACCEPT_ANY_PUBLIC_KEY; + break; + + case PKM_USE_TWO_WAY_AUTHENTICATION: + if (publicKey->myPublicKey == 0 || publicKey->myPrivateKey == 0 || + publicKey->remoteServerPublicKey == 0) + { + return false; + } + + rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); + memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); + + if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || + !rcs->client_handshake->SetIdentity(publicKey->myPublicKey, publicKey->myPrivateKey) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object with identity for this RequestedConnectionStruct\n"); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + return false; + } + + CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object with identity for this RequestedConnectionStruct -- pre-generated challenge\n"); + + rcs->publicKeyMode = PKM_USE_TWO_WAY_AUTHENTICATION; + break; + + case PKM_USE_KNOWN_PUBLIC_KEY: + if (publicKey->remoteServerPublicKey == 0) + return false; + + rcs->client_handshake = RakNet::OP_NEW(_FILE_AND_LINE_); + memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); + + if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object for this RequestedConnectionStruct\n"); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + return false; + } + + CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object for this RequestedConnectionStruct -- pre-generated challenge\n"); + + rcs->publicKeyMode = PKM_USE_KNOWN_PUBLIC_KEY; + break; + } + + return true; +} +#endif +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ) +{ + RakAssert(passwordDataLength <= 256); + RakAssert(remotePort!=0); + SystemAddress systemAddress; + if (!systemAddress.FromStringExplicitPort(host,remotePort,socketList[connectionSocketIndex]->boundAddress.GetIPVersion())) + return CANNOT_RESOLVE_DOMAIN_NAME; + + // Already connected? + if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) + return ALREADY_CONNECTED_TO_ENDPOINT; + + //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); + RequestedConnectionStruct *rcs = RakNet::OP_NEW(_FILE_AND_LINE_); + + rcs->systemAddress=systemAddress; + rcs->nextRequestTime=RakNet::GetTimeMS(); + rcs->requestsMade=0; + rcs->data=0; + rcs->extraData=extraData; + rcs->socketIndex=connectionSocketIndex; + rcs->actionToTake=RequestedConnectionStruct::CONNECT; + rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; + rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; + memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); + rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; + rcs->timeoutTime=timeoutTime; + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: In SendConnectionRequest()\n"); + if (!GenerateConnectionRequestChallenge(rcs,publicKey)) + return SECURITY_INITIALIZATION_FAILED; +#else + (void) publicKey; +#endif + + // Return false if already pending, else push on queue + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + // Not necessary + //RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; + } + } + requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + + return CONNECTION_ATTEMPT_STARTED; +} +ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime, RakNetSmartPtr socket ) +{ + RakAssert(passwordDataLength <= 256); + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort); + + // Already connected? + if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) + return ALREADY_CONNECTED_TO_ENDPOINT; + + //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); + RequestedConnectionStruct *rcs = RakNet::OP_NEW(_FILE_AND_LINE_); + + rcs->systemAddress=systemAddress; + rcs->nextRequestTime=RakNet::GetTimeMS(); + rcs->requestsMade=0; + rcs->data=0; + rcs->extraData=extraData; + rcs->socketIndex=connectionSocketIndex; + rcs->actionToTake=RequestedConnectionStruct::CONNECT; + rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; + rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; + memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); + rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; + rcs->timeoutTime=timeoutTime; + rcs->socket=socket; + +#if LIBCAT_SECURITY==1 + if (!GenerateConnectionRequestChallenge(rcs,publicKey)) + return SECURITY_INITIALIZATION_FAILED; +#else + (void) publicKey; +#endif + + // Return false if already pending, else push on queue + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + // Not necessary + //RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; + } + } + requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + + return CONNECTION_ATTEMPT_STARTED; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ValidateRemoteSystemLookup(void) const +{ +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const +{ + if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return GetRemoteSystemFromGUID(systemIdentifier.rakNetGuid, onlyActive); + else + return GetRemoteSystemFromSystemAddress(systemIdentifier.systemAddress, calledFromNetworkThread, onlyActive); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const +{ + unsigned i; + + if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) + return 0; + + if (calledFromNetworkThread) + { + unsigned int index = GetRemoteSystemIndex(systemAddress); + if (index!=(unsigned int) -1) + { + if (onlyActive==false || remoteSystemList[ index ].isActive==true ) + { + RakAssert(remoteSystemList[index].systemAddress==systemAddress); + return remoteSystemList + index; + } + } + } + else + { + int deadConnectionIndex=-1; + + // Active connections take priority. But if there are no active connections, return the first systemAddress match found + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].systemAddress == systemAddress) + { + if ( remoteSystemList[ i ].isActive ) + return remoteSystemList + i; + else if (deadConnectionIndex==-1) + deadConnectionIndex=i; + } + } + + if (deadConnectionIndex!=-1 && onlyActive==false) + return remoteSystemList + deadConnectionIndex; + } + + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const +{ + if (guid==UNASSIGNED_RAKNET_GUID) + return 0; + + unsigned i; + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].guid == guid && (onlyActive==false || remoteSystemList[ i ].isActive)) + { + return remoteSystemList + i; + } + } + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize ) +{ + RakNet::BitStream bs((unsigned char*) data,byteSize,false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID guid; + bs.Read(guid); + RakNet::Time incomingTimestamp; + bs.Read(incomingTimestamp); + unsigned char doSecurity; + bs.Read(doSecurity); + +#if LIBCAT_SECURITY==1 + unsigned char doClientKey; + if (_using_security) + { + // Ignore message on bad state + if (doSecurity != 1 || !remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()) + return; + + // Validate client proof of key + unsigned char proof[cat::EasyHandshake::PROOF_BYTES]; + bs.ReadAlignedBytes(proof, sizeof(proof)); + if (!remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->ValidateProof(proof, sizeof(proof))) + { + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + + CAT_OBJCLR(remoteSystem->client_public_key); + + bs.Read(doClientKey); + + // Check if client wants to prove identity + if (doClientKey == 1) + { + // Read identity proof + unsigned char ident[cat::EasyHandshake::IDENTITY_BYTES]; + bs.ReadAlignedBytes(ident, sizeof(ident)); + + // If we are listening to these proofs, + if (_require_client_public_key) + { + // Validate client identity + if (!_server_handshake->VerifyInitiatorIdentity(remoteSystem->answer, ident, remoteSystem->client_public_key)) + { + RakNet::BitStream bitStream; + bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect + bitStream.Write((unsigned char)2); // Indicate client identity is invalid + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + } + + // Otherwise ignore the client public key + } + else + { + // If no client key was provided but it is required, + if (_require_client_public_key) + { + RakNet::BitStream bitStream; + bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect + bitStream.Write((unsigned char)1); // Indicate client identity is missing + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + } + } +#endif // LIBCAT_SECURITY + + unsigned char *password = bs.GetData()+BITS_TO_BYTES(bs.GetReadOffset()); + int passwordLength = byteSize - BITS_TO_BYTES(bs.GetReadOffset()); + if ( incomingPasswordLength != passwordLength || + memcmp( password, incomingPassword, incomingPasswordLength ) != 0 ) + { + CAT_AUDIT_PRINTF("AUDIT: Invalid password\n"); + // This one we only send once since we don't care if it arrives. + RakNet::BitStream bitStream; + bitStream.Write((MessageID)ID_INVALID_PASSWORD); + bitStream.Write(GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0); + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + + // OK + remoteSystem->connectMode=RemoteSystemStruct::HANDLING_CONNECTION_REQUEST; + + OnConnectionRequest( remoteSystem, incomingTimestamp ); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, RakNet::Time incomingTimestamp ) +{ + RakNet::BitStream bitStream; + bitStream.Write((MessageID)ID_CONNECTION_REQUEST_ACCEPTED); + bitStream.Write(remoteSystem->systemAddress); + SystemIndex systemIndex = (SystemIndex) GetIndexFromSystemAddress( remoteSystem->systemAddress, true ); + RakAssert(systemIndex!=65535); + bitStream.Write(systemIndex); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + bitStream.Write(ipList[i]); + bitStream.Write(incomingTimestamp); + bitStream.Write(RakNet::GetTime()); + + SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, remoteSystem->systemAddress, false, false, RakNet::GetTimeUS(), 0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) +{ + RakNet::BitStream temp( sizeof(unsigned char) ); + temp.Write( (MessageID)ID_DISCONNECTION_NOTIFICATION ); + if (performImmediate) + { + SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, RELIABLE_ORDERED, orderingChannel, systemAddress, false, false, RakNet::GetTimeUS(), 0); + RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(systemAddress, true, true); + rss->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; + } + else + { + SendBuffered((const char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, RELIABLE_ORDERED, orderingChannel, systemAddress, false, RemoteSystemStruct::DISCONNECT_ASAP, 0); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::GetNumberOfRemoteInitiatedConnections( void ) const +{ + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + unsigned short numberOfIncomingConnections; + numberOfIncomingConnections = 0; + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED && + (activeSystemList[i])->weInitiatedTheConnection==false + ) + { + numberOfIncomingConnections++; + } + } + return numberOfIncomingConnections; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct * RakPeer::AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSmartPtr incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ) +{ + RemoteSystemStruct * remoteSystem; + unsigned i,j,assignedIndex; + RakNet::TimeMS time = RakNet::GetTimeMS(); +#ifdef _DEBUG + RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); +#endif + + if (limitConnectionFrequencyFromTheSameIP) + { + if (IsLoopbackAddress(systemAddress,false)==false) + { + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if ( remoteSystemList[ i ].isActive==true && + remoteSystemList[ i ].systemAddress.EqualsExcludingPort(systemAddress) && + time >= remoteSystemList[ i ].connectionTime && + time - remoteSystemList[ i ].connectionTime < 100 + ) + { + // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots + // Ignore connection attempts if this IP address connected within the last 100 milliseconds + *thisIPConnectedRecently=true; + ValidateRemoteSystemLookup(); + return 0; + } + } + } + } + + // Don't use a different port than what we received on + bindingAddress.CopyPort(incomingRakNetSocket->boundAddress); + + *thisIPConnectedRecently=false; + for ( assignedIndex = 0; assignedIndex < maximumNumberOfPeers; assignedIndex++ ) + { + if ( remoteSystemList[ assignedIndex ].isActive==false ) + { + remoteSystem=remoteSystemList+assignedIndex; + ReferenceRemoteSystem(systemAddress, assignedIndex); + remoteSystem->MTUSize=defaultMTUSize; + remoteSystem->guid=guid; + remoteSystem->isActive = true; // This one line causes future incoming packets to go through the reliability layer + // Reserve this reliability layer for ourselves. + if (incomingMTU > remoteSystem->MTUSize) + remoteSystem->MTUSize=incomingMTU; + remoteSystem->reliabilityLayer.Reset(true, remoteSystem->MTUSize, useSecurity); + remoteSystem->reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); + remoteSystem->reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); + remoteSystem->reliabilityLayer.SetTimeoutTime(defaultTimeoutTime); + AddToActiveSystemList(assignedIndex); + if (incomingRakNetSocket->boundAddress==bindingAddress) + { + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + else + { + char str[256]; + bindingAddress.ToString(true,str); + // See if this is an internal IP address. + // If so, force binding on it so we reply on the same IP address as they sent to. + unsigned int ipListIndex, foundIndex=(unsigned int)-1; + + for (ipListIndex=0; ipListIndex < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ipListIndex++) + { + if (ipList[ipListIndex]==UNASSIGNED_SYSTEM_ADDRESS) + break; + + if (bindingAddress.EqualsExcludingPort(ipList[ipListIndex])) + { + foundIndex=ipListIndex; + break; + } + } + + // 06/26/09 Unconfirmed report that Vista firewall blocks the reply if we force a binding + // For now use the incoming socket only + // Originally this code was to force a machine with multiple IP addresses to reply back on the IP + // that the datagram came in on + if (1 || foundIndex==(unsigned int)-1) + { + // Must not be an internal LAN address. Just use whatever socket it came in on + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + else + { + // Force binding + unsigned int socketListIndex; + for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) + { + if (socketList[socketListIndex]->boundAddress==bindingAddress) + { + // Force binding with existing socket + remoteSystem->rakNetSocket=socketList[socketListIndex]; + break; + } + } + + if (socketListIndex==socketList.Size()) + { + char ipListFoundIndexStr[128]; + ipList[foundIndex].ToString(false,str); + + // Force binding with new socket + RakNetSmartPtr rns(RakNet::OP_NEW(_FILE_AND_LINE_)); + if (incomingRakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2==0) + rns->s = (unsigned int) SocketLayer::CreateBoundSocket( bindingAddress.GetPort(), true, ipListFoundIndexStr, 0, incomingRakNetSocket->extraSocketOptions, incomingRakNetSocket->socketFamily ); + else + rns->s = (unsigned int) SocketLayer::CreateBoundSocket_PS3Lobby( bindingAddress.GetPort(), true, ipListFoundIndexStr, incomingRakNetSocket->socketFamily ); + if ((SOCKET)rns->s==(SOCKET)-1) + { + // Can't bind. Just use whatever socket it came in on + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + else + { + rns->boundAddress=bindingAddress; + rns->remotePortRakNetWasStartedOn_PS3_PSP2=incomingRakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2; + rns->extraSocketOptions=incomingRakNetSocket->extraSocketOptions; + rns->userConnectionSocketIndex=(unsigned int)-1; + socketList.Push(rns, _FILE_AND_LINE_ ); + remoteSystem->rakNetSocket=rns; + + RakPeerAndIndex rpai; + rpai.remotePortRakNetWasStartedOn_PS3=rns->remotePortRakNetWasStartedOn_PS3_PSP2; + rpai.extraSocketOptions=rns->extraSocketOptions; + rpai.s=rns->s; + rpai.rakPeer=this; +#ifdef _WIN32 + int highPriority=THREAD_PRIORITY_ABOVE_NORMAL; +#else + int highPriority=-10; +#endif + +//#if !defined(_XBOX) && !defined(X360) + highPriority=0; +//#endif + + + + + + + + RakNet::RakThread::Create(RecvFromLoop, &rpai, highPriority); + + RakSleep(10); + + + /* +#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + if (threadSleepTimer>0) + { + rns->recvEvent=CreateEvent(0,FALSE,FALSE,0); + WSAEventSelect(rns->s,rns->recvEvent,FD_READ); + } +#endif + */ + } + } + } + } + + for ( j = 0; j < (unsigned) PING_TIMES_ARRAY_SIZE; j++ ) + { + remoteSystem->pingAndClockDifferential[ j ].pingTime = 65535; + remoteSystem->pingAndClockDifferential[ j ].clockDifferential = 0; + } + + remoteSystem->connectMode=connectionMode; + remoteSystem->pingAndClockDifferentialWriteIndex = 0; + remoteSystem->lowestPing = 65535; + remoteSystem->nextPingTime = 0; // Ping immediately + remoteSystem->weInitiatedTheConnection = false; + remoteSystem->connectionTime = time; + remoteSystem->myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystem->lastReliableSend=time; + +#ifdef _DEBUG + int indexLoopupCheck=GetIndexFromSystemAddress( systemAddress, true ); + if ((int) indexLoopupCheck!=(int) assignedIndex) + { + RakAssert((int) indexLoopupCheck==(int) assignedIndex); + } +#endif + + return remoteSystem; + } + } + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Adjust the first four bytes (treated as unsigned int) of the pointer +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const +{ +#ifdef _DEBUG + RakAssert( IsActive() ); + RakAssert( data ); +#endif + + RakNet::BitStream timeBS( data, sizeof(RakNet::Time), false); + RakNet::Time encodedTimestamp; + timeBS.Read(encodedTimestamp); + + encodedTimestamp = encodedTimestamp - GetBestClockDifferential( systemAddress ); + timeBS.SetWriteOffset(0); + timeBS.Write(encodedTimestamp); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm +RakNet::Time RakPeer::GetBestClockDifferential( const SystemAddress systemAddress ) const +{ + int counter, lowestPingSoFar; + RakNet::Time clockDifferential; + RemoteSystemStruct *remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + + if ( remoteSystem == 0 ) + return 0; + + lowestPingSoFar = 65535; + + clockDifferential = 0; + + for ( counter = 0; counter < PING_TIMES_ARRAY_SIZE; counter++ ) + { + if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime == 65535 ) + break; + + if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime < lowestPingSoFar ) + { + clockDifferential = remoteSystem->pingAndClockDifferential[ counter ].clockDifferential; + lowestPingSoFar = remoteSystem->pingAndClockDifferential[ counter ].pingTime; + } + } + + return clockDifferential; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::RemoteSystemLookupHashIndex(const SystemAddress &sa) const +{ + return SystemAddress::ToInteger(sa) % ((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex) +{ +// #ifdef _DEBUG +// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// RakAssert(hashIndex==remoteSystemIndex); +// } +// } +// #endif + + + SystemAddress oldAddress = remoteSystemList[remoteSystemListIndex].systemAddress; + if (oldAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + // The system might be active if rerouting +// RakAssert(remoteSystemList[remoteSystemListIndex].isActive==false); + + // Remove the reference if the reference is pointing to this inactive system + if (GetRemoteSystem(oldAddress)==&remoteSystemList[remoteSystemListIndex]) + DereferenceRemoteSystem(oldAddress); + } + DereferenceRemoteSystem(sa); + +// #ifdef _DEBUG +// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// if (hashIndex!=remoteSystemIndex) +// { +// RakAssert(hashIndex==remoteSystemIndex); +// } +// } +// } +// #endif + + + remoteSystemList[remoteSystemListIndex].systemAddress=sa; + + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *rsi; + rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); + if (remoteSystemLookup[hashIndex]==0) + { + rsi->next=0; + rsi->index=remoteSystemListIndex; + remoteSystemLookup[hashIndex]=rsi; + } + else + { + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + while (cur->next!=0) + { + cur=cur->next; + } + + rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); + rsi->next=0; + rsi->index=remoteSystemListIndex; + cur->next=rsi; + } + +// #ifdef _DEBUG +// for ( int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int hashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// RakAssert(hashIndex==remoteSystemIndex); +// } +// } +// #endif + + + RakAssert(GetRemoteSystemIndex(sa)==remoteSystemListIndex); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DereferenceRemoteSystem(const SystemAddress &sa) +{ + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + RemoteSystemIndex *last = 0; + while (cur!=0) + { + if (remoteSystemList[cur->index].systemAddress==sa) + { + if (last==0) + { + remoteSystemLookup[hashIndex]=cur->next; + } + else + { + last->next=cur->next; + } + remoteSystemIndexPool.Release(cur,_FILE_AND_LINE_); + break; + } + last=cur; + cur=cur->next; + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetRemoteSystemIndex(const SystemAddress &sa) const +{ + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + while (cur!=0) + { + if (remoteSystemList[cur->index].systemAddress==sa) + return cur->index; + cur=cur->next; + } + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct* RakPeer::GetRemoteSystem(const SystemAddress &sa) const +{ + unsigned int remoteSystemIndex = GetRemoteSystemIndex(sa); + if (remoteSystemIndex==(unsigned int)-1) + return 0; + return remoteSystemList + remoteSystemIndex; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearRemoteSystemLookup(void) +{ + remoteSystemIndexPool.Clear(_FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(remoteSystemLookup,_FILE_AND_LINE_); + remoteSystemLookup=0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToActiveSystemList(unsigned int remoteSystemListIndex) +{ + activeSystemList[activeSystemListSize++]=remoteSystemList+remoteSystemListIndex; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromActiveSystemList(const SystemAddress &sa) +{ + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + RemoteSystemStruct *rss=activeSystemList[i]; + if (rss->systemAddress==sa) + { + activeSystemList[i]=activeSystemList[activeSystemListSize-1]; + activeSystemListSize--; + return; + } + } + RakAssert("activeSystemList invalid, entry not found in RemoveFromActiveSystemList. Ensure that AddToActiveSystemList and RemoveFromActiveSystemList are called by the same thread." && 0); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/* +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::LookupIndexUsingHashIndex(const SystemAddress &sa) const +{ + unsigned int scanCount=0; + unsigned int index = RemoteSystemLookupHashIndex(sa); + if (remoteSystemLookup[index].index==(unsigned int)-1) + return (unsigned int) -1; + while (remoteSystemList[remoteSystemLookup[index].index].systemAddress!=sa) + { + if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + index=0; + if (++scanCount>(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + return (unsigned int) -1; + if (remoteSystemLookup[index].index==-1) + return (unsigned int) -1; + } + return index; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const +{ + unsigned int index = LookupIndexUsingHashIndex(sa); + if (index!=(unsigned int) -1) + { + return remoteSystemLookup[index].index; + } + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const +{ +// unsigned int collisionCount=0; + unsigned int index = RemoteSystemLookupHashIndex(sa); + while (remoteSystemLookup[index].index!=(unsigned int)-1) + { + if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + index=0; +// collisionCount++; + } +// printf("%i collisions. Using index %i\n", collisionCount, index); + return index; +} +*/ +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const +{ + if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return systemIdentifier.rakNetGuid==myGuid; + + for (int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS; i++) + { + if (matchPort) + { + if (ipList[i]==systemIdentifier.systemAddress) + return true; + } + else + { + if (ipList[i].EqualsExcludingPort(systemIdentifier.systemAddress)) + return true; + } + } + + return (matchPort==true && systemIdentifier.systemAddress==firstExternalID) || + (matchPort==false && systemIdentifier.systemAddress.EqualsExcludingPort(firstExternalID)); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetLoopbackAddress(void) const +{ + + return ipList[0]; + + + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AllowIncomingConnections(void) const +{ + return GetNumberOfRemoteInitiatedConnections() < GetMaximumIncomingConnections(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PingInternal( const SystemAddress target, bool performImmediate, PacketReliability reliability ) +{ + if ( IsActive() == false ) + return ; + + RakNet::BitStream bitStream(sizeof(unsigned char)+sizeof(RakNet::Time)); + bitStream.Write((MessageID)ID_CONNECTED_PING); + bitStream.Write(RakNet::GetTime()); + if (performImmediate) + SendImmediate( (char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, reliability, 0, target, false, false, RakNet::GetTimeUS(), 0 ); + else + Send( &bitStream, IMMEDIATE_PRIORITY, reliability, 0, target, false ); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ) +{ +#ifdef _DEBUG + RakAssert(orderingChannel < 32); +#endif + + if (systemIdentifier.IsUndefined()) + return; + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + SystemAddress target; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + target=systemIdentifier.systemAddress; + } + else + { + target=GetSystemAddressFromGuid(systemIdentifier.rakNetGuid); + } + + if (target!=UNASSIGNED_SYSTEM_ADDRESS && performImmediate) + target.FixForIPVersion(socketList[0]->boundAddress); + + if (sendDisconnectionNotification) + { + NotifyAndFlagForShutdown(target, performImmediate, orderingChannel, disconnectionNotificationPriority); + } + else + { + if (performImmediate) + { + unsigned int index = GetRemoteSystemIndex(target); + if (index!=(unsigned int) -1) + { + if ( remoteSystemList[index].isActive ) + { + RemoveFromActiveSystemList(target); + + // Found the index to stop + remoteSystemList[index].isActive = false; + + remoteSystemList[index].guid=UNASSIGNED_RAKNET_GUID; + + // Reserve this reliability layer for ourselves + //remoteSystemList[ remoteSystemLookup[index].index ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; + + // Clear any remaining messages + remoteSystemList[index].reliabilityLayer.Reset(false, remoteSystemList[index].MTUSize, false); + + // Not using this socket + remoteSystemList[index].rakNetSocket.SetNull(); + } + } + } + else + { + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_CLOSE_CONNECTION; + bcs->systemIdentifier=target; + bcs->data=0; + bcs->orderingChannel=orderingChannel; + bcs->priority=disconnectionNotificationPriority; + bufferedCommands.Push(bcs); + } + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendBuffered( const char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) +{ + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = (char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES(numberOfBitsToSend), _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy + if (bcs->data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + return; + } + memcpy(bcs->data, data, (size_t) BITS_TO_BYTES(numberOfBitsToSend)); + bcs->numberOfBitsToSend=numberOfBitsToSend; + bcs->priority=priority; + bcs->reliability=reliability; + bcs->orderingChannel=orderingChannel; + bcs->systemIdentifier=systemIdentifier; + bcs->broadcast=broadcast; + bcs->connectionMode=connectionMode; + bcs->receipt=receipt; + bcs->command=BufferedCommandStruct::BCS_SEND; + bufferedCommands.Push(bcs); + + if (priority==IMMEDIATE_PRIORITY) + { + // Forces pending sends to go out now, rather than waiting to the next update interval + quitAndDataEvents.SetEvent(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendBufferedList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) +{ + BufferedCommandStruct *bcs; + unsigned int totalLength=0; + unsigned int lengthOffset; + int i; + for (i=0; i < numParameters; i++) + { + if (lengths[i]>0) + totalLength+=lengths[i]; + } + if (totalLength==0) + return; + + char *dataAggregate; + dataAggregate = (char*) rakMalloc_Ex( (size_t) totalLength, _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy + if (dataAggregate==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return; + } + for (i=0, lengthOffset=0; i < numParameters; i++) + { + if (lengths[i]>0) + { + memcpy(dataAggregate+lengthOffset, data[i], lengths[i]); + lengthOffset+=lengths[i]; + } + } + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback(dataAggregate,totalLength); + rakFree_Ex(dataAggregate,_FILE_AND_LINE_); + return; + } + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = dataAggregate; + bcs->numberOfBitsToSend=BYTES_TO_BITS(totalLength); + bcs->priority=priority; + bcs->reliability=reliability; + bcs->orderingChannel=orderingChannel; + bcs->systemIdentifier=systemIdentifier; + bcs->broadcast=broadcast; + bcs->connectionMode=connectionMode; + bcs->receipt=receipt; + bcs->command=BufferedCommandStruct::BCS_SEND; + bufferedCommands.Push(bcs); + + if (priority==IMMEDIATE_PRIORITY) + { + // Forces pending sends to go out now, rather than waiting to the next update interval + quitAndDataEvents.SetEvent(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::SendImmediate( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, RakNet::TimeUS currentTime, uint32_t receipt ) +{ + unsigned *sendList; + unsigned sendListSize; + bool callerDataAllocationUsed; + unsigned int remoteSystemIndex, sendListIndex; // Iterates into the list of remote systems +// unsigned numberOfBytesUsed = (unsigned) BITS_TO_BYTES(numberOfBitsToSend); + callerDataAllocationUsed=false; + + sendListSize=0; + + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + remoteSystemIndex=GetIndexFromSystemAddress( systemIdentifier.systemAddress, true ); + else if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + remoteSystemIndex=GetSystemIndexFromGuid(systemIdentifier.rakNetGuid); + else + remoteSystemIndex=(unsigned int) -1; + + // 03/06/06 - If broadcast is false, use the optimized version of GetIndexFromSystemAddress + if (broadcast==false) + { + if (remoteSystemIndex==(unsigned int) -1) + { +#ifdef _DEBUG +// int debugIndex = GetRemoteSystemIndex(systemIdentifier.systemAddress); +#endif + return false; + } + + + sendList=(unsigned *)alloca(sizeof(unsigned)); + + + + + if (remoteSystemList[remoteSystemIndex].isActive && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ON_NO_ACK) + { + sendList[0]=remoteSystemIndex; + sendListSize=1; + } + } + else + { + + //sendList=(unsigned *)alloca(sizeof(unsigned)*remoteSystemListSize); + sendList=(unsigned *)alloca(sizeof(unsigned)*maximumNumberOfPeers); + + + + + + // remoteSystemList in network thread + unsigned int idx; + for ( idx = 0; idx < maximumNumberOfPeers; idx++ ) + { + if (remoteSystemIndex!=(unsigned int) -1 && idx==remoteSystemIndex) + continue; + + if ( remoteSystemList[ idx ].isActive && remoteSystemList[ idx ].systemAddress != UNASSIGNED_SYSTEM_ADDRESS ) + sendList[sendListSize++]=idx; + } + } + + if (sendListSize==0) + { + + + + return false; + } + + for (sendListIndex=0; sendListIndex < sendListSize; sendListIndex++) + { + // Send may split the packet and thus deallocate data. Don't assume data is valid if we use the callerAllocationData + bool useData = useCallerDataAllocation && callerDataAllocationUsed==false && sendListIndex+1==sendListSize; + remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( data, numberOfBitsToSend, priority, reliability, orderingChannel, useData==false, remoteSystemList[sendList[sendListIndex]].MTUSize, currentTime, receipt ); + if (useData) + callerDataAllocationUsed=true; + + if (reliability==RELIABLE || + reliability==RELIABLE_ORDERED || + reliability==RELIABLE_SEQUENCED || + reliability==RELIABLE_WITH_ACK_RECEIPT || + reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT +// || +// reliability==RELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + remoteSystemList[sendList[sendListIndex]].lastReliableSend=(RakNet::TimeMS)(currentTime/(RakNet::TimeUS)1000); + } + + + + + + // Return value only meaningful if true was passed for useCallerDataAllocation. Means the reliability layer used that data copy, so the caller should not deallocate it + return callerDataAllocationUsed; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ResetSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + sendReceiptSerial=1; + sendReceiptSerialMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::OnConnectedPong(RakNet::Time sendPingTime, RakNet::Time sendPongTime, RemoteSystemStruct *remoteSystem) +{ + RakNet::Time ping; +// RakNet::TimeMS lastPing; + RakNet::Time time = RakNet::GetTime(); // Update the time value to be accurate + if (time > sendPingTime) + ping = time - sendPingTime; + else + ping=0; + +// lastPing = remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime; + + remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime = ( unsigned short ) ping; + // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm + // Divide each integer by 2, rather than the sum by 2, to prevent overflow + remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].clockDifferential = sendPongTime - ( time/2 + sendPingTime/2 ); + + if ( remoteSystem->lowestPing == (unsigned short)-1 || remoteSystem->lowestPing > (int) ping ) + remoteSystem->lowestPing = (unsigned short) ping; + + if ( ++( remoteSystem->pingAndClockDifferentialWriteIndex ) == (RakNet::Time) PING_TIMES_ARRAY_SIZE ) + remoteSystem->pingAndClockDifferentialWriteIndex = 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBufferedCommands(void) +{ + BufferedCommandStruct *bcs; + + while ((bcs=bufferedCommands.Pop())!=0) + { + if (bcs->data) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + } + bufferedCommands.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBufferedPackets(void) +{ + RecvFromStruct *bcs; + + while ((bcs=bufferedPackets.Pop())!=0) + { + bufferedPackets.Deallocate(bcs, _FILE_AND_LINE_); + } + bufferedPackets.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearSocketQueryOutput(void) +{ + socketQueryOutput.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearRequestedConnectionList(void) +{ + DataStructures::Queue freeQueue; + requestedConnectionQueueMutex.Lock(); + while (requestedConnectionQueue.Size()) + freeQueue.Push(requestedConnectionQueue.Pop(), _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + unsigned i; + for (i=0; i < freeQueue.Size(); i++) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: In ClearRequestedConnectionList(), Deleting freeQueue index %i client_handshake %x\n", i, freeQueue[i]->client_handshake); + RakNet::OP_DELETE(freeQueue[i]->client_handshake,_FILE_AND_LINE_); +#endif + RakNet::OP_DELETE(freeQueue[i], _FILE_AND_LINE_ ); + } +} +inline void RakPeer::AddPacketToProducer(RakNet::Packet *p) +{ + packetReturnMutex.Lock(); + packetReturnQueue.Push(p,_FILE_AND_LINE_); + packetReturnMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +union Buff6AndBuff8 +{ + unsigned char buff6[6]; + uint64_t buff8; +}; +uint64_t RakPeerInterface::Get64BitUniqueRandomNumber(void) +{ + // Mac address is a poor solution because you can't have multiple connections from the same system + + + + + + + + + + + + + +#if defined(_WIN32) + uint64_t g=RakNet::GetTimeUS(); + + RakNet::TimeUS lastTime, thisTime; + int j; + // Sleep a small random time, then use the last 4 bits as a source of randomness + for (j=0; j < 8; j++) + { + lastTime = RakNet::GetTimeUS(); + RakSleep(1); + RakSleep(0); + thisTime = RakNet::GetTimeUS(); + RakNet::TimeUS diff = thisTime-lastTime; + unsigned int diff4Bits = (unsigned int) (diff & 15); + diff4Bits <<= 32-4; + diff4Bits >>= j*4; + ((char*)&g)[j] ^= diff4Bits; + } + return g; + +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_usec + tv.tv_sec * 1000000; +#endif +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GenerateGUID(void) +{ + myGuid.g=Get64BitUniqueRandomNumber(); + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// void RakNet::ProcessPortUnreachable( SystemAddress systemAddress, RakPeer *rakPeer ) +// { +// (void) binaryAddress; +// (void) port; +// (void) rakPeer; +// +// } +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +namespace RakNet { +bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, bool *isOfflineMessage, RakNet::TimeUS timeRead ) +{ + (void) timeRead; + RakPeer::RemoteSystemStruct *remoteSystem; + RakNet::Packet *packet; + unsigned i; + + + char str1[64]; + systemAddress.ToString(false, str1); + if (rakPeer->IsBanned( str1 )) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + RakNet::BitStream bs; + bs.Write((MessageID)ID_CONNECTION_BANNED); + bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + unsigned i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (char*) bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + + + + // The reason for all this is that the reliability layer has no way to tell between offline messages that arrived late for a player that is now connected, + // and a regular encoding. So I insert OFFLINE_MESSAGE_DATA_ID into the stream, the encoding of which is essentially impossible to hit by chance + if (length <=2) + { + *isOfflineMessage=true; + } + else if ( + ((unsigned char)data[0] == ID_UNCONNECTED_PING || + (unsigned char)data[0] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS) && + length == sizeof(unsigned char) + sizeof(RakNet::Time) + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(RakNet::Time), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ((unsigned char)data[0] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char) + sizeof(RakNet::TimeMS) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(RakNet::Time) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ( + (unsigned char)data[0] == ID_OUT_OF_BAND_INTERNAL && + (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ( + ( + (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_1 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_2 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_1 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_2 || + (unsigned char)data[0] == ID_CONNECTION_ATTEMPT_FAILED || + (unsigned char)data[0] == ID_NO_FREE_INCOMING_CONNECTIONS || + (unsigned char)data[0] == ID_CONNECTION_BANNED || + (unsigned char)data[0] == ID_ALREADY_CONNECTED || + (unsigned char)data[0] == ID_IP_RECENTLY_CONNECTED) && + (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if (((unsigned char)data[0] == ID_INCOMPATIBLE_PROTOCOL_VERSION&& + (size_t) length == sizeof(MessageID)*2 + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID))) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID)*2, OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else + { + *isOfflineMessage=false; + } + + if (*isOfflineMessage) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + // These are all messages from unconnected systems. Messages here can be any size, but are never processed from connected systems. + if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS + || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && length == sizeof(unsigned char)+sizeof(RakNet::Time)+sizeof(OFFLINE_MESSAGE_DATA_ID) ) + { + if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || + rakPeer->AllowIncomingConnections() ) // Open connections with players + { +// #if !defined(_XBOX) && !defined(X360) + RakNet::BitStream inBitStream( (unsigned char *) data, length, false ); + inBitStream.IgnoreBits(8); + RakNet::Time sendPingTime; + inBitStream.Read(sendPingTime); + + RakNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_UNCONNECTED_PONG); // Should be named ID_UNCONNECTED_PONG eventually + outBitStream.Write(sendPingTime); + outBitStream.Write(rakPeer->myGuid); + outBitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + + rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Lock(); + // They are connected, so append offline ping data + outBitStream.Write( (char*)rakPeer->offlinePingResponse.GetData(), rakPeer->offlinePingResponse.GetNumberOfBytesUsed() ); + rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Unlock(); + + unsigned i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*)outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), systemAddress); + + SocketLayer::SendTo( rakNetSocket->s, (const char*)outBitStream.GetData(), (unsigned int) outBitStream.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + packet=rakPeer->AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + packet->data[0]=data[0]; + packet->systemAddress = systemAddress; + packet->guid=UNASSIGNED_RAKNET_GUID; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); +// #endif + } + } + // UNCONNECTED MESSAGE Pong with no data. + else if ((unsigned char) data[ 0 ] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID) && (size_t) length < sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID)+MAX_OFFLINE_DATA_LENGTH) + { + packet=rakPeer->AllocPacket((unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(RakNet::Time)+sizeof(RakNet::TimeMS)), _FILE_AND_LINE_); + RakNet::BitStream bsIn((unsigned char*) data, length, false); + bsIn.IgnoreBytes(sizeof(unsigned char)); + RakNet::Time ping; + bsIn.Read(ping); + bsIn.Read(packet->guid); + + RakNet::BitStream bsOut((unsigned char*) packet->data, packet->length, false); + bsOut.ResetWritePointer(); + bsOut.Write((unsigned char)ID_UNCONNECTED_PONG); + RakNet::TimeMS pingMS=(RakNet::TimeMS)ping; + bsOut.Write(pingMS); + bsOut.WriteAlignedBytes( + (const unsigned char*)data+sizeof(unsigned char)+sizeof(RakNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID), + length-sizeof(unsigned char)-sizeof(RakNet::Time)-RakNetGUID::size()-sizeof(OFFLINE_MESSAGE_DATA_ID) + ); + + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + else if ((unsigned char) data[ 0 ] == ID_OUT_OF_BAND_INTERNAL && + (size_t) length < MAX_OFFLINE_DATA_LENGTH+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size()) + { + unsigned int dataLength = (unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MessageID)); + RakAssert(dataLength<1024); + packet=rakPeer->AllocPacket(dataLength+1, _FILE_AND_LINE_); + RakAssert(packet->length<1024); + + RakNet::BitStream bs2((unsigned char*) data, length, false); + bs2.IgnoreBytes(sizeof(MessageID)); + bs2.Read(packet->guid); + + if (data[sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size()]==ID_ADVERTISE_SYSTEM) + { + packet->length--; + packet->bitSize=BYTES_TO_BITS(packet->length); + packet->data[0]=ID_ADVERTISE_SYSTEM; + memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)*2 + RakNetGUID::size(), dataLength-1); + } + else + { + packet->data[0]=ID_OUT_OF_BAND_INTERNAL; + memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size(), dataLength); + } + + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_1) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + RakNet::BitStream bsIn((unsigned char*) data,length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bsIn.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID serverGuid; + bsIn.Read(serverGuid); + unsigned char serverHasSecurity; + uint32_t cookie; + (void) cookie; + bsIn.Read(serverHasSecurity); + // Even if the server has security, it may not be required of us if we are in the security exception list + if (serverHasSecurity) + { + bsIn.Read(cookie); + } + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_2); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + if (serverHasSecurity) + bsOut.Write(cookie); + + unsigned i; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + RakPeer::RequestedConnectionStruct *rcs; + rcs=rakPeer->requestedConnectionQueue[i]; + if (rcs->systemAddress==systemAddress) + { + if (serverHasSecurity) + { +#if LIBCAT_SECURITY==1 + unsigned char public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; + bsIn.ReadAlignedBytes(public_key, sizeof(public_key)); + + if (rcs->publicKeyMode==PKM_ACCEPT_ANY_PUBLIC_KEY) + { + memcpy(rcs->remote_public_key, public_key, cat::EasyHandshake::PUBLIC_KEY_BYTES); + if (!rcs->client_handshake->Initialize(public_key) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Server passed a bad public key with PKM_ACCEPT_ANY_PUBLIC_KEY"); + return true; + } + } + + if (cat::SecureEqual(public_key, + rcs->remote_public_key, + cat::EasyHandshake::PUBLIC_KEY_BYTES)==false) + { + rakPeer->requestedConnectionQueueMutex.Unlock(); + CAT_AUDIT_PRINTF("AUDIT: Expected public key does not match what was sent by server -- Reporting back ID_PUBLIC_KEY_MISMATCH to user\n"); + + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_PUBLIC_KEY_MISMATCH; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=serverGuid; + rakPeer->AddPacketToProducer(packet); + return true; + } + + if (rcs->client_handshake==0) + { + // Message does not contain a challenge + // We might still pass if we are in the security exception list + bsOut.Write((unsigned char)0); + } + else + { + // Message contains a challenge + bsOut.Write((unsigned char)1); + // challenge + CAT_AUDIT_PRINTF("AUDIT: Sending challenge\n"); + bsOut.WriteAlignedBytes((const unsigned char*) rcs->handshakeChallenge,cat::EasyHandshake::CHALLENGE_BYTES); + } +#else // LIBCAT_SECURITY + // Message does not contain a challenge + bsOut.Write((unsigned char)0); +#endif // LIBCAT_SECURITY + } + else + { + // Server does not need security +#if LIBCAT_SECURITY==1 + if (rcs->client_handshake!=0) + { + rakPeer->requestedConnectionQueueMutex.Unlock(); + CAT_AUDIT_PRINTF("AUDIT: Security disabled by server but we expected security (indicated by client_handshake not null) so failing!\n"); + + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_OUR_SYSTEM_REQUIRES_SECURITY; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=serverGuid; + rakPeer->AddPacketToProducer(packet); + return true; + } +#endif // LIBCAT_SECURITY + + } + + uint16_t mtu; + bsIn.Read(mtu); + + // Binding address + bsOut.Write(rcs->systemAddress); + rakPeer->requestedConnectionQueueMutex.Unlock(); + // MTU + bsOut.Write(mtu); + // Our guid + bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), rcs->systemAddress); + + SocketLayer::SendTo( rakPeer->socketList[rcs->socketIndex]->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), rcs->systemAddress, rakPeer->socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, rakPeer->socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + } + rakPeer->requestedConnectionQueueMutex.Unlock(); + } + else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_2) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + RakNet::BitStream bs((unsigned char*) data,length,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID guid; + bs.Read(guid); + SystemAddress bindingAddress; + bool b = bs.Read(bindingAddress); + RakAssert(b); + uint16_t mtu; + b=bs.Read(mtu); + RakAssert(b); + bool doSecurity=false; + b=bs.Read(doSecurity); + RakAssert(b); + +#if LIBCAT_SECURITY==1 + char answer[cat::EasyHandshake::ANSWER_BYTES]; + CAT_AUDIT_PRINTF("AUDIT: Got ID_OPEN_CONNECTION_REPLY_2 and given doSecurity=%i\n", (int)doSecurity); + if (doSecurity) + { + CAT_AUDIT_PRINTF("AUDIT: Reading cookie and public key\n"); + bs.ReadAlignedBytes((unsigned char*) answer, sizeof(answer)); + } + cat::ClientEasyHandshake *client_handshake=0; +#endif // LIBCAT_SECURITY + + RakPeer::RequestedConnectionStruct *rcs; + bool unlock=true; + unsigned i; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + rcs=rakPeer->requestedConnectionQueue[i]; + + + if (rcs->systemAddress==systemAddress) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: System address matches an entry in the requestedConnectionQueue and doSecurity=%i\n", (int)doSecurity); + if (doSecurity) + { + if (rcs->client_handshake==0) + { + CAT_AUDIT_PRINTF("AUDIT: Server wants security but we didn't set a public key -- Reporting back ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY to user\n"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + + packet=rakPeer->AllocPacket(2, _FILE_AND_LINE_); + packet->data[ 0 ] = ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY; // Attempted a connection and couldn't + packet->data[ 1 ] = 0; // Indicate server public key is missing + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + return true; + } + + CAT_AUDIT_PRINTF("AUDIT: Looks good, preparing to send challenge to server! client_handshake = %x\n", client_handshake); + } + +#endif // LIBCAT_SECURITY + + rakPeer->requestedConnectionQueueMutex.Unlock(); + unlock=false; + + RakAssert(rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT); + // You might get this when already connected because of cross-connections + bool thisIPConnectedRecently=false; + remoteSystem=rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + if (remoteSystem==0) + { + if (rcs->socket.IsNull()) + { + remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); + } + else + { + remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rcs->socket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); + } + } + + // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots + // Ignore connection attempts if this IP address connected within the last 100 milliseconds + if (thisIPConnectedRecently==false) + { + // Don't check GetRemoteSystemFromGUID, server will verify + if (remoteSystem) + { + // Move pointer from RequestedConnectionStruct to RemoteSystemStruct +#if LIBCAT_SECURITY==1 + cat::u8 ident[cat::EasyHandshake::IDENTITY_BYTES]; + bool doIdentity = false; + + if (rcs->client_handshake) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer\n"); + if (rcs->publicKeyMode == PKM_USE_TWO_WAY_AUTHENTICATION) + { + if (!rcs->client_handshake->ProcessAnswerWithIdentity(answer, ident, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + + return true; + } + + doIdentity = true; + } + else + { + if (!rcs->client_handshake->ProcessAnswer(answer, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + + return true; + } + } + CAT_AUDIT_PRINTF("AUDIT: Success!\n"); + + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + } +#endif // LIBCAT_SECURITY + + remoteSystem->weInitiatedTheConnection=true; + remoteSystem->connectMode=RakPeer::RemoteSystemStruct::REQUESTED_CONNECTION; + if (rcs->timeoutTime!=0) + remoteSystem->reliabilityLayer.SetTimeoutTime(rcs->timeoutTime); + + RakNet::BitStream temp; + temp.Write( (MessageID)ID_CONNECTION_REQUEST); + temp.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + temp.Write(RakNet::GetTime()); + +#if LIBCAT_SECURITY==1 + temp.Write((unsigned char)(doSecurity ? 1 : 0)); + + if (doSecurity) + { + unsigned char proof[32]; + remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->GenerateProof(proof, sizeof(proof)); + temp.WriteAlignedBytes(proof, sizeof(proof)); + + temp.Write((unsigned char)(doIdentity ? 1 : 0)); + + if (doIdentity) + { + temp.WriteAlignedBytes(ident, sizeof(ident)); + } + } +#else + temp.Write((unsigned char)0); +#endif // LIBCAT_SECURITY + + if ( rcs->outgoingPasswordLength > 0 ) + temp.Write( ( char* ) rcs->outgoingPassword, rcs->outgoingPasswordLength ); + + rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE, 0, systemAddress, false, false, timeRead, 0 ); + } + else + { + // Failed, no connections available anymore + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + } + } + + rakPeer->requestedConnectionQueueMutex.Lock(); + for (unsigned int k=0; k < rakPeer->requestedConnectionQueue.Size(); k++) + { + if (rakPeer->requestedConnectionQueue[k]->systemAddress==systemAddress) + { + rakPeer->requestedConnectionQueue.RemoveAtIndex(k); + break; + } + } + rakPeer->requestedConnectionQueueMutex.Unlock(); + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Deleting client_handshake object %x and rcs->client_handshake object %x\n", client_handshake, rcs->client_handshake); + RakNet::OP_DELETE(client_handshake,_FILE_AND_LINE_); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif // LIBCAT_SECURITY + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + + break; + } + } + + if (unlock) + rakPeer->requestedConnectionQueueMutex.Unlock(); + + return true; + + } + else if ((unsigned char)(data)[0] == (MessageID)ID_CONNECTION_ATTEMPT_FAILED || + (unsigned char)(data)[0] == (MessageID)ID_NO_FREE_INCOMING_CONNECTIONS || + (unsigned char)(data)[0] == (MessageID)ID_CONNECTION_BANNED || + (unsigned char)(data)[0] == (MessageID)ID_ALREADY_CONNECTED || + (unsigned char)(data)[0] == (MessageID)ID_INVALID_PASSWORD || + (unsigned char)(data)[0] == (MessageID)ID_IP_RECENTLY_CONNECTED || + (unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) + { + + RakNet::BitStream bs((unsigned char*) data,length,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + if ((unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) + bs.IgnoreBytes(sizeof(unsigned char)); + + RakNetGUID guid; + bs.Read(guid); + + RakPeer::RequestedConnectionStruct *rcs; + bool connectionAttemptCancelled=false; + unsigned i; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + rcs=rakPeer->requestedConnectionQueue[i]; + if (rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT && rcs->systemAddress==systemAddress) + { + connectionAttemptCancelled=true; + rakPeer->requestedConnectionQueue.RemoveAtIndex(i); + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Connection attempt canceled so deleting rcs->client_handshake object %x\n", rcs->client_handshake); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif // LIBCAT_SECURITY + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + break; + } + } + + rakPeer->requestedConnectionQueueMutex.Unlock(); + + if (connectionAttemptCancelled) + { + // Tell user of connection attempt failed + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = data[0]; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + } + } + else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_1 && length > (int) (1+sizeof(OFFLINE_MESSAGE_DATA_ID))) + {/* + static int x = 0; + ++x; + + SystemAddress *addr = (SystemAddress*)&systemAddress; + addr->binaryAddress += x;*/ + + unsigned int i; + //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); + char remoteProtocol=data[1+sizeof(OFFLINE_MESSAGE_DATA_ID)]; + if (remoteProtocol!=RAKNET_PROTOCOL_VERSION) + { + RakNet::BitStream bs; + bs.Write((MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION); + bs.Write((unsigned char)RAKNET_PROTOCOL_VERSION); + bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*)bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + + SocketLayer::SendTo( rakNetSocket->s, (char*)bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + return true; + } + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_OPEN_CONNECTION_REPLY_1); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); +#if LIBCAT_SECURITY==1 + if (rakPeer->_using_security) + { + bsOut.Write((unsigned char) 1); // HasCookie Yes + // Write cookie + uint32_t cookie = rakPeer->_cookie_jar->Generate(&systemAddress.address,sizeof(systemAddress.address)); + CAT_AUDIT_PRINTF("AUDIT: Writing cookie %i to %i:%i\n", cookie, systemAddress); + bsOut.Write(cookie); + // Write my public key + bsOut.WriteAlignedBytes((const unsigned char *) rakPeer->my_public_key,sizeof(rakPeer->my_public_key)); + } + else +#endif // LIBCAT_SECURITY + bsOut.Write((unsigned char) 0); // HasCookie No + + // MTU + bsOut.Write((uint16_t) (length+UDP_HEADER_SIZE)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + } + else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_2) + { + SystemAddress bindingAddress; + RakNetGUID guid; + RakNet::BitStream bsOut; + RakNet::BitStream bs((unsigned char*) data, length, false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + + bool requiresSecurityOfThisClient=false; +#if LIBCAT_SECURITY==1 + char remoteHandshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; + + if (rakPeer->_using_security) + { + char str1[64]; + systemAddress.ToString(false, str1); + requiresSecurityOfThisClient=rakPeer->IsInSecurityExceptionList(str1)==false; + + uint32_t cookie; + bs.Read(cookie); + CAT_AUDIT_PRINTF("AUDIT: Got cookie %i from %i:%i\n", cookie, systemAddress); + if (rakPeer->_cookie_jar->Verify(&systemAddress.address,sizeof(systemAddress.address), cookie)==false) + { + return true; + } + CAT_AUDIT_PRINTF("AUDIT: Cookie good!\n"); + + unsigned char clientWroteChallenge; + bs.Read(clientWroteChallenge); + + if (requiresSecurityOfThisClient==true && clientWroteChallenge==0) + { + // Fail, client doesn't support security, and it is required + return true; + } + + if (clientWroteChallenge) + { + bs.ReadAlignedBytes((unsigned char*) remoteHandshakeChallenge, cat::EasyHandshake::CHALLENGE_BYTES); +#ifdef CAT_AUDIT + printf("AUDIT: RECV CHALLENGE "); + for (int ii = 0; ii < sizeof(remoteHandshakeChallenge); ++ii) + { + printf("%02x", (cat::u8)remoteHandshakeChallenge[ii]); + } + printf("\n"); +#endif + } + } +#endif // LIBCAT_SECURITY + + bs.Read(bindingAddress); + uint16_t mtu; + bs.Read(mtu); + bs.Read(guid); + + RakPeer::RemoteSystemStruct *rssFromSA = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + bool IPAddrInUse = rssFromSA != 0 && rssFromSA->isActive; + RakPeer::RemoteSystemStruct *rssFromGuid = rakPeer->GetRemoteSystemFromGUID(guid, true); + bool GUIDInUse = rssFromGuid != 0 && rssFromGuid->isActive; + + // IPAddrInUse, GuidInUse, outcome + // TRUE, , TRUE , ID_OPEN_CONNECTION_REPLY if they are the same, else ID_ALREADY_CONNECTED + // FALSE, , TRUE , ID_ALREADY_CONNECTED (someone else took this guid) + // TRUE, , FALSE , ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) + // FALSE , FALSE , Allow connection + + int outcome; + if (IPAddrInUse & GUIDInUse) + { + if (rssFromSA==rssFromGuid && rssFromSA->connectMode==RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER) + { + // ID_OPEN_CONNECTION_REPLY if they are the same + outcome=1; + } + else + { + // ID_ALREADY_CONNECTED (restarted raknet, connected again from same ip, plus someone else took this guid) + outcome=2; + } + } + else if (IPAddrInUse==false && GUIDInUse==true) + { + // ID_ALREADY_CONNECTED (someone else took this guid) + outcome=3; + } + else if (IPAddrInUse==true && GUIDInUse==false) + { + // ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) + outcome=4; + } + else + { + // Allow connection + outcome=0; + } + + RakNet::BitStream bsAnswer; + bsAnswer.Write((MessageID)ID_OPEN_CONNECTION_REPLY_2); + bsAnswer.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsAnswer.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + bsAnswer.Write(systemAddress); + bsAnswer.Write(mtu); + bsAnswer.Write(requiresSecurityOfThisClient); + + if (outcome==1) + { + // Duplicate connection request packet from packetloss + // Send back the same answer +#if LIBCAT_SECURITY==1 + if (requiresSecurityOfThisClient) + { + CAT_AUDIT_PRINTF("AUDIT: Resending public key and answer from packetloss. Sending ID_OPEN_CONNECTION_REPLY_2\n"); + bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); + } +#endif // LIBCAT_SECURITY + + unsigned int i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + else if (outcome!=0) + { + bsOut.Write((MessageID)ID_ALREADY_CONNECTED); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(guid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + + if (rakPeer->AllowIncomingConnections()==false) + { + bsOut.Write((MessageID)ID_NO_FREE_INCOMING_CONNECTIONS); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(guid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + + bool thisIPConnectedRecently=false; + rssFromSA = rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, requiresSecurityOfThisClient); + + if (thisIPConnectedRecently==true) + { + bsOut.Write((MessageID)ID_IP_RECENTLY_CONNECTED); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(guid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + + return true; + } + +#if LIBCAT_SECURITY==1 + if (requiresSecurityOfThisClient) + { + CAT_AUDIT_PRINTF("AUDIT: Writing public key. Sending ID_OPEN_CONNECTION_REPLY_2\n"); + if (rakPeer->_server_handshake->ProcessChallenge(remoteHandshakeChallenge, rssFromSA->answer, rssFromSA->reliabilityLayer.GetAuthenticatedEncryption() )) + { + CAT_AUDIT_PRINTF("AUDIT: Challenge good!\n"); + // Keep going to OK block + } + else + { + CAT_AUDIT_PRINTF("AUDIT: Challenge BAD!\n"); + + // Unassign this remote system + rakPeer->DereferenceRemoteSystem(systemAddress); + return true; + } + + bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); + } +#endif // LIBCAT_SECURITY + + unsigned int i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket->s, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, _FILE_AND_LINE_ ); + } + return true; + } + + return false; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead, BitStream &updateBitStream ) +{ + ProcessNetworkPacket(systemAddress,data,length,rakPeer,rakPeer->socketList[0],timeRead, updateBitStream); +} +void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead, BitStream &updateBitStream ) +{ +#if LIBCAT_SECURITY==1 +#ifdef CAT_AUDIT + printf("AUDIT: RECV "); + for (int ii = 0; ii < length; ++ii) + { + printf("%02x", (cat::u8)data[ii]); + } + printf("\n"); +#endif +#endif // LIBCAT_SECURITY + + RakAssert(systemAddress.GetPort()); + bool isOfflineMessage; + if (ProcessOfflineNetworkPacket(systemAddress, data, length, rakPeer, rakNetSocket, &isOfflineMessage, timeRead)) + { + return; + } + +// RakNet::Packet *packet; + RakPeer::RemoteSystemStruct *remoteSystem; + + // See if this datagram came from a connected system + remoteSystem = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + if ( remoteSystem ) + { + // Handle regular incoming data + // HandleSocketReceiveFromConnectedPlayer is only safe to be called from the same thread as Update, which is this thread + if ( isOfflineMessage==false) + { + remoteSystem->reliabilityLayer.HandleSocketReceiveFromConnectedPlayer( + data, length, systemAddress, rakPeer->pluginListNTS, remoteSystem->MTUSize, + rakNetSocket->s, &rnr, rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, rakNetSocket->extraSocketOptions, timeRead, updateBitStream); + } + } +} + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GenerateSeedFromGuid(void) +{ + /* + // Construct a random seed based on the initial guid value, and the last digits of the difference to each subsequent number + // This assumes that only the last 3 bits of each guidId integer has a meaningful amount of randomness between it and the prior number + unsigned int t = guid.g[0]; + unsigned int i; + for (i=1; i < sizeof(guid.g) / sizeof(guid.g[0]); i++) + { + unsigned int diff = guid.g[i]-guid.g[i-1]; + unsigned int diff3Bits = diff & 0x0007; + diff3Bits <<= 29; + diff3Bits >>= (i-1)*3; + t ^= diff3Bits; + } + + return t; + */ + return (unsigned int) ((myGuid.g >> 32) ^ myGuid.g); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DerefAllSockets(void) +{ + unsigned int i; + for (i=0; i < socketList.Size(); i++) + { + socketList[i].SetNull(); + } + socketList.Clear(false, _FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const +{ + unsigned int i; + for (i=0; i < socketList.Size(); i++) + { + if (socketList[i]->userConnectionSocketIndex==userIndex) + return i; + } + RakAssert("GetRakNetSocketFromUserConnectionSocketIndex failed" && 0); + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::RunUpdateCycle( RakNet::TimeUS timeNS, RakNet::Time timeMS, BitStream &updateBitStream ) +{ + RakPeer::RemoteSystemStruct * remoteSystem; + unsigned int activeSystemListIndex; + Packet *packet; + // int currentSentBytes,currentReceivedBytes; +// unsigned numberOfBytesUsed; +// BitSize_t numberOfBitsUsed; + //SystemAddress authoritativeClientSystemAddress; + BitSize_t bitSize; + unsigned int byteSize; + unsigned char *data; + SystemAddress systemAddress; + BufferedCommandStruct *bcs; + bool callerDataAllocationUsed; + RakNetStatistics *rnss; + + // This is here so RecvFromBlocking actually gets data from the same thread + if (SocketLayer::GetSocketLayerOverride()) + { + int len; + SystemAddress sender; + char dataOut[ MAXIMUM_MTU_SIZE ]; + do { + len = SocketLayer::GetSocketLayerOverride()->RakNetRecvFrom(socketList[0]->s,this,dataOut,&sender,true); + if (len>0) + ProcessNetworkPacket( sender, dataOut, len, this, socketList[0], RakNet::GetTimeUS(), updateBitStream ); + } while (len>0); + } + + unsigned int socketListIndex; + RakPeer::RecvFromStruct *recvFromStruct; + while ((recvFromStruct=bufferedPackets.PopInaccurate())!=0) + { + for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) + { + if ((SOCKET) socketList[socketListIndex]->s==recvFromStruct->s) + break; + } + if (socketListIndex!=socketList.Size()) + ProcessNetworkPacket(recvFromStruct->systemAddress, recvFromStruct->data, recvFromStruct->bytesRead, this, socketList[socketListIndex], recvFromStruct->timeRead, updateBitStream); + bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); + } + + while ((bcs=bufferedCommands.PopInaccurate())!=0) + { + if (bcs->command==BufferedCommandStruct::BCS_SEND) + { + // GetTime is a very slow call so do it once and as late as possible + if (timeNS==0) + { + timeNS = RakNet::GetTimeUS(); + timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); + } + + callerDataAllocationUsed=SendImmediate((char*)bcs->data, bcs->numberOfBitsToSend, bcs->priority, bcs->reliability, bcs->orderingChannel, bcs->systemIdentifier, bcs->broadcast, true, timeNS, bcs->receipt); + if ( callerDataAllocationUsed==false ) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + + // Set the new connection state AFTER we call sendImmediate in case we are setting it to a disconnection state, which does not allow further sends + if (bcs->connectionMode!=RemoteSystemStruct::NO_ACTION ) + { + remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); + if (remoteSystem) + remoteSystem->connectMode=bcs->connectionMode; + } + } + else if (bcs->command==BufferedCommandStruct::BCS_CLOSE_CONNECTION) + { + CloseConnectionInternal(bcs->systemIdentifier, false, true, bcs->orderingChannel, bcs->priority); + } + else if (bcs->command==BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS) + { + // Reroute + RakPeer::RemoteSystemStruct *rssFromGuid = GetRemoteSystem(bcs->systemIdentifier.rakNetGuid,true,true); + if (rssFromGuid!=0) + { + unsigned int existingSystemIndex = GetRemoteSystemIndex(rssFromGuid->systemAddress); + ReferenceRemoteSystem(bcs->systemIdentifier.systemAddress, existingSystemIndex); + } + } + else if (bcs->command==BufferedCommandStruct::BCS_GET_SOCKET) + { + SocketQueryOutput *sqo; + if (bcs->systemIdentifier.IsUndefined()) + { + sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); + sqo->sockets=socketList; + socketQueryOutput.Push(sqo); + } + else + { + remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); + sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); + + sqo->sockets.Clear(false, _FILE_AND_LINE_); + if (remoteSystem) + { + sqo->sockets.Push(remoteSystem->rakNetSocket, _FILE_AND_LINE_ ); + } + else + { + // Leave empty smart pointer + } + socketQueryOutput.Push(sqo); + } + + } + +#ifdef _DEBUG + bcs->data=0; +#endif + + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + } + + if (requestedConnectionQueue.IsEmpty()==false) + { + if (timeNS==0) + { + timeNS = RakNet::GetTimeUS(); + timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); + } + + bool condition1, condition2; + unsigned requestedConnectionQueueIndex=0; + requestedConnectionQueueMutex.Lock(); + while (requestedConnectionQueueIndex < requestedConnectionQueue.Size()) + { + RequestedConnectionStruct *rcs; + rcs = requestedConnectionQueue[requestedConnectionQueueIndex]; + requestedConnectionQueueMutex.Unlock(); + if (rcs->nextRequestTime < timeMS) + { + condition1=rcs->requestsMade==rcs->sendConnectionAttemptCount+1; + condition2=(bool)((rcs->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)==1); + // If too many requests made or a hole then remove this if possible, otherwise invalidate it + if (condition1 || condition2) + { + if (rcs->data) + { + rakFree_Ex(rcs->data, _FILE_AND_LINE_ ); + rcs->data=0; + } + + if (condition1 && !condition2 && rcs->actionToTake==RequestedConnectionStruct::CONNECT) + { + // Tell user of connection attempt failed + packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + AddPacketToProducer(packet); + } + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Connection attempt FAILED so deleting rcs->client_handshake object %x\n", rcs->client_handshake); + RakNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif + RakNet::OP_DELETE(rcs,_FILE_AND_LINE_); + + requestedConnectionQueueMutex.Lock(); + for (unsigned int k=0; k < requestedConnectionQueue.Size(); k++) + { + if (requestedConnectionQueue[k]==rcs) + { + requestedConnectionQueue.RemoveAtIndex(k); + break; + } + } + requestedConnectionQueueMutex.Unlock(); + } + else + { + + int MTUSizeIndex = rcs->requestsMade / (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES); + if (MTUSizeIndex>=NUM_MTU_SIZES) + MTUSizeIndex=NUM_MTU_SIZES-1; + rcs->requestsMade++; + rcs->nextRequestTime=timeMS+rcs->timeBetweenSendConnectionAttemptsMS; + + RakNet::BitStream bitStream; + //WriteOutOfBandHeader(&bitStream, ID_USER_PACKET_ENUM); + bitStream.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_1); + bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bitStream.Write((MessageID)RAKNET_PROTOCOL_VERSION); + bitStream.PadWithZeroToByteLength(mtuSizes[MTUSizeIndex]-UDP_HEADER_SIZE); + + char str[256]; + rcs->systemAddress.ToString(true,str); + + //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*) bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), rcs->systemAddress); + + if (rcs->socket.IsNull()) + { + rcs->systemAddress.FixForIPVersion(socketList[rcs->socketIndex]->boundAddress); + SocketLayer::SetDoNotFragment(socketList[rcs->socketIndex]->s, 1, socketList[rcs->socketIndex]->boundAddress.GetIPPROTO()); + RakNet::Time sendToStart=RakNet::GetTime(); + if (SocketLayer::SendTo( socketList[rcs->socketIndex]->s, (const char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), rcs->systemAddress, socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ )==-10040) + { + // Don't use this MTU size again + rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES)); + rcs->nextRequestTime=timeMS; + } + else + { + RakNet::Time sendToEnd=RakNet::GetTime(); + if (sendToEnd-sendToStart>100) + { + // Drop to lowest MTU + int lowestMtuIndex = rcs->sendConnectionAttemptCount/NUM_MTU_SIZES * (NUM_MTU_SIZES - 1); + if (lowestMtuIndex > rcs->requestsMade) + { + rcs->requestsMade = (unsigned char) lowestMtuIndex; + rcs->nextRequestTime=timeMS; + } + else + rcs->requestsMade=(unsigned char)(rcs->sendConnectionAttemptCount+1); + } + } + SocketLayer::SetDoNotFragment(socketList[rcs->socketIndex]->s, 0, socketList[rcs->socketIndex]->boundAddress.GetIPPROTO()); + } + else + { + rcs->systemAddress.FixForIPVersion(rcs->socket->boundAddress); + SocketLayer::SetDoNotFragment(rcs->socket->s, 1, rcs->socket->boundAddress.GetIPPROTO()); + RakNet::Time sendToStart=RakNet::GetTime(); + if (SocketLayer::SendTo( rcs->socket->s, (const char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), rcs->systemAddress, socketList[rcs->socketIndex]->remotePortRakNetWasStartedOn_PS3_PSP2, socketList[rcs->socketIndex]->extraSocketOptions, _FILE_AND_LINE_ )==-10040) + { + // Don't use this MTU size again + rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * (rcs->sendConnectionAttemptCount/NUM_MTU_SIZES)); + rcs->nextRequestTime=timeMS; + } + else + { + RakNet::Time sendToEnd=RakNet::GetTime(); + if (sendToEnd-sendToStart>100) + { + // Drop to lowest MTU + int lowestMtuIndex = rcs->sendConnectionAttemptCount/NUM_MTU_SIZES * (NUM_MTU_SIZES - 1); + if (lowestMtuIndex > rcs->requestsMade) + { + rcs->requestsMade = (unsigned char) lowestMtuIndex; + rcs->nextRequestTime=timeMS; + } + else + rcs->requestsMade= (unsigned char) rcs->sendConnectionAttemptCount+1; + } + } + SocketLayer::SetDoNotFragment(rcs->socket->s, 0, rcs->socket->boundAddress.GetIPPROTO()); + } + + requestedConnectionQueueIndex++; + } + } + else + requestedConnectionQueueIndex++; + + requestedConnectionQueueMutex.Lock(); + } + requestedConnectionQueueMutex.Unlock(); + } + + // remoteSystemList in network thread + for ( activeSystemListIndex = 0; activeSystemListIndex < activeSystemListSize; ++activeSystemListIndex ) + //for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; ++remoteSystemIndex ) + { + // I'm using systemAddress from remoteSystemList but am not locking it because this loop is called very frequently and it doesn't + // matter if we miss or do an extra update. The reliability layers themselves never care which player they are associated with + //systemAddress = remoteSystemList[ remoteSystemIndex ].systemAddress; + // Allow the systemAddress for this remote system list to change. We don't care if it changes now. + // remoteSystemList[ remoteSystemIndex ].allowSystemAddressAssigment=true; + + + // Found an active remote system + remoteSystem = activeSystemList[ activeSystemListIndex ]; + systemAddress = remoteSystem->systemAddress; + RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); + // Update is only safe to call from the same thread that calls HandleSocketReceiveFromConnectedPlayer, + // which is this thread + + if (timeNS==0) + { + timeNS = RakNet::GetTimeUS(); + timeMS = (RakNet::TimeMS)(timeNS/(RakNet::TimeUS)1000); + //RAKNET_DEBUG_PRINTF("timeNS = %I64i timeMS=%i\n", timeNS, timeMS); + } + + + if (timeMS > remoteSystem->lastReliableSend && timeMS-remoteSystem->lastReliableSend > remoteSystem->reliabilityLayer.GetTimeoutTime()/2 && remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) + { + // If no reliable packets are waiting for an ack, do a one byte reliable send so that disconnections are noticed + RakNetStatistics rakNetStatistics; + rnss=remoteSystem->reliabilityLayer.GetStatistics(&rakNetStatistics); + if (rnss->messagesInResendBuffer==0) + { + PingInternal( systemAddress, true, RELIABLE ); + + //remoteSystem->lastReliableSend=timeMS+remoteSystem->reliabilityLayer.GetTimeoutTime(); + remoteSystem->lastReliableSend=timeMS; + } + } + + remoteSystem->reliabilityLayer.Update( remoteSystem->rakNetSocket->s, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, remoteSystem->rakNetSocket->remotePortRakNetWasStartedOn_PS3_PSP2, remoteSystem->rakNetSocket->extraSocketOptions, updateBitStream ); // systemAddress only used for the internet simulator test + + // Check for failure conditions + if ( remoteSystem->reliabilityLayer.IsDeadConnection() || + ((remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY) && remoteSystem->reliabilityLayer.IsOutgoingDataWaiting()==false) || + (remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && (remoteSystem->reliabilityLayer.AreAcksWaiting()==false || remoteSystem->reliabilityLayer.AckTimeout(timeMS)==true)) || + (( + (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || + remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || + remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) + && timeMS > remoteSystem->connectionTime && timeMS - remoteSystem->connectionTime > 10000)) + ) + { + // RAKNET_DEBUG_PRINTF("timeMS=%i remoteSystem->connectionTime=%i\n", timeMS, remoteSystem->connectionTime ); + + // Failed. Inform the user? + // TODO - RakNet 4.0 - Return a different message identifier for DISCONNECT_ASAP_SILENTLY and DISCONNECT_ASAP than for DISCONNECT_ON_NO_ACK + // The first two mean we called CloseConnection(), the last means the other system sent us ID_DISCONNECTION_NOTIFICATION + if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION + || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK) + { + +// RakNet::BitStream undeliveredMessages; +// remoteSystem->reliabilityLayer.GetUndeliveredMessages(&undeliveredMessages,remoteSystem->MTUSize); + +// packet=AllocPacket(sizeof( char ) + undeliveredMessages.GetNumberOfBytesUsed()); + packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + else if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) + packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection + else + packet->data[ 0 ] = ID_DISCONNECTION_NOTIFICATION; // DeadConnection + +// memcpy(packet->data+1, undeliveredMessages.GetData(), undeliveredMessages.GetNumberOfBytesUsed()); + + packet->guid = remoteSystem->guid; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + + AddPacketToProducer(packet); + } + // else connection shutting down, don't bother telling the user + +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Connection dropped for player %i:%i\n", systemAddress); +#endif + CloseConnectionInternal( systemAddress, false, true, 0, LOW_PRIORITY ); + continue; + } + + // Ping this guy if it is time to do so + if ( remoteSystem->connectMode==RemoteSystemStruct::CONNECTED && timeMS > remoteSystem->nextPingTime && ( occasionalPing || remoteSystem->lowestPing == (unsigned short)-1 ) ) + { + remoteSystem->nextPingTime = timeMS + 5000; + PingInternal( systemAddress, true, UNRELIABLE ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + } + + // Find whoever has the lowest player ID + //if (systemAddress < authoritativeClientSystemAddress) + // authoritativeClientSystemAddress=systemAddress; + + // Does the reliability layer have any packets waiting for us? + // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer + bitSize = remoteSystem->reliabilityLayer.Receive( &data ); + + while ( bitSize > 0 ) + { + // These types are for internal use and should never arrive from a network packet + if (data[0]==ID_CONNECTION_ATTEMPT_FAILED) + { + RakAssert(0); + bitSize=0; + continue; + } + + // Fast and easy - just use the data that was returned + byteSize = (unsigned int) BITS_TO_BYTES( bitSize ); + + // For unknown senders we only accept a few specific packets + if (remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) + { + if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) + { + ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else + { + CloseConnectionInternal( systemAddress, false, true, 0, LOW_PRIORITY ); +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Temporarily banning %i:%i for sending nonsense data\n", systemAddress); +#endif + + char str1[64]; + systemAddress.ToString(false, str1); + AddToBanList(str1, remoteSystem->reliabilityLayer.GetTimeoutTime()); + + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // However, if we are connected we still take a connection request in case both systems are trying to connect to each other + // at the same time + if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) + { + // 04/27/06 This is wrong. With cross connections, we can both have initiated the connection are in state REQUESTED_CONNECTION + // 04/28/06 Downgrading connections from connected will close the connection due to security at ((remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED && time > remoteSystem->connectionTime && time - remoteSystem->connectionTime > 10000)) + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + { + ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); + } + else + { + + RakNet::BitStream bs((unsigned char*) data,byteSize,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.IgnoreBytes(RakNetGUID::size()); + RakNet::Time incomingTimestamp; + bs.Read(incomingTimestamp); + + // Got a connection request message from someone we are already connected to. Just reply normally. + // This can happen due to race conditions with the fully connected mesh + OnConnectionRequest( remoteSystem, incomingTimestamp ); + } + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char) data[ 0 ] == ID_NEW_INCOMING_CONNECTION && byteSize > sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(RakNet::Time)*2 ) + { + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) + { + remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; + PingInternal( systemAddress, true, UNRELIABLE ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + + RakNet::BitStream inBitStream((unsigned char *) data, byteSize, false); + SystemAddress bsSystemAddress; + + inBitStream.IgnoreBits(8); + inBitStream.Read(bsSystemAddress); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); + + RakNet::Time sendPingTime, sendPongTime; + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); + + // Overwrite the data in the packet + // NewIncomingConnectionStruct newIncomingConnectionStruct; + // RakNet::BitStream nICS_BS( data, NewIncomingConnectionStruct_Size, false ); + // newIncomingConnectionStruct.Deserialize( nICS_BS ); + + remoteSystem->myExternalSystemAddress = bsSystemAddress; + firstExternalID=bsSystemAddress; + firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); + + // Send this info down to the game + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + } + else + { + // Send to game even if already connected. This could happen when connecting to 127.0.0.1 + // Ignore, already connected + // rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char) data[ 0 ] == ID_CONNECTED_PONG && byteSize == sizeof(unsigned char)+sizeof(RakNet::Time)*2 ) + { + RakNet::Time sendPingTime, sendPongTime; + + // Copy into the ping times array the current time - the value returned + // First extract the sent ping + RakNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); + //PingStruct ps; + //ps.Deserialize(psBS); + inBitStream.IgnoreBits(8); + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + + OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)data[0] == ID_CONNECTED_PING && byteSize == sizeof(unsigned char)+sizeof(RakNet::Time) ) + { + RakNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); + inBitStream.IgnoreBits(8); + RakNet::Time sendPingTime; + inBitStream.Read(sendPingTime); + + RakNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_CONNECTED_PONG); + outBitStream.Write(sendPingTime); + outBitStream.Write(RakNet::GetTime()); + SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, UNRELIABLE, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0 ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char) data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) + { + // We shouldn't close the connection immediately because we need to ack the ID_DISCONNECTION_NOTIFICATION + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ON_NO_ACK; + rakFree_Ex(data, _FILE_AND_LINE_ ); + + // AddPacketToProducer(packet); + } + else if ( (unsigned char)(data)[0] == ID_DETECT_LOST_CONNECTIONS && byteSize == sizeof(unsigned char) ) + { + // Do nothing + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)(data)[0] == ID_INVALID_PASSWORD ) + { + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + { + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST_ACCEPTED ) + { + if (byteSize > sizeof(MessageID)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(SystemIndex)+sizeof(RakNet::Time)*2) + { + // Make sure this connection accept is from someone we wanted to connect to + bool allowConnection, alreadyConnected; + + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || + remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || + allowConnectionResponseIPMigration) + allowConnection=true; + else + allowConnection=false; + + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) + alreadyConnected=true; + else + alreadyConnected=false; + + if ( allowConnection ) + { + SystemAddress externalID; + SystemIndex systemIndex; +// SystemAddress internalID; + + RakNet::BitStream inBitStream((unsigned char *) data, byteSize, false); + inBitStream.IgnoreBits(8); + // inBitStream.Read(remotePort); + inBitStream.Read(externalID); + inBitStream.Read(systemIndex); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); + + RakNet::Time sendPingTime, sendPongTime; + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + OnConnectedPong(sendPingTime, sendPongTime, remoteSystem); + + // Find a free remote system struct to use + // RakNet::BitStream casBitS(data, byteSize, false); + // ConnectionAcceptStruct cas; + // cas.Deserialize(casBitS); + // systemAddress.GetPort() = remotePort; + + // The remote system told us our external IP, so save it + remoteSystem->myExternalSystemAddress = externalID; + remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; + + firstExternalID=externalID; + firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); + + // Send the connection request complete to the game + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = byteSize * 8; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) GetIndexFromSystemAddress( systemAddress, true ); + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + + RakNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_NEW_INCOMING_CONNECTION); + outBitStream.Write(systemAddress); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + outBitStream.Write(ipList[i]); + outBitStream.Write(sendPongTime); + outBitStream.Write(RakNet::GetTime()); + + + // We turned on encryption with SetEncryptionKey. This pads packets to up to a multiple of 16 bytes. + // As soon as a multiple of 16 byte packet arrives on the remote system, we will turn on AES. This works because all encrypted packets are multiples of 16 and the + // packets I happen to be sending before this are not a multiple of 16 bytes. Otherwise there is no way to know if a packet that arrived is + // encrypted or not so the other side won't know to turn on encryption or not. + RakAssert((outBitStream.GetNumberOfBytesUsed()&15)!=0); + SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false, false, RakNet::GetTimeUS(), 0 ); + + if (alreadyConnected==false) + { + PingInternal( systemAddress, true, UNRELIABLE ); + } + } + else + { + // Ignore, already connected + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // Version mismatch error? + RakAssert(0); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // What do I do if I get a message from a system, before I am fully connected? + // I can either ignore it or give it to the user + // It seems like giving it to the user is a better option + if ((data[0]>=(MessageID)ID_TIMESTAMP || data[0]==ID_SND_RECEIPT_ACKED || data[0]==ID_SND_RECEIPT_LOSS) && + remoteSystem->isActive + ) + { + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + } + + // Does the reliability layer have any more packets waiting for us? + // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer + bitSize = remoteSystem->reliabilityLayer.Receive( &data ); + } + + } + + return true; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RAK_THREAD_DECLARATION(RakNet::RecvFromLoop) +{ + + + + RakPeerAndIndex *rpai = ( RakPeerAndIndex * ) arguments; + + RakPeer * rakPeer = rpai->rakPeer; + SOCKET s = rpai->s; + unsigned short remotePortRakNetWasStartedOn_PS3 = rpai->remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions = rpai->extraSocketOptions; + + rakPeer->isRecvFromLoopThreadActive.Increment(); + + RakPeer::RecvFromStruct *recvFromStruct; + while ( rakPeer->endThreads == false ) + { + recvFromStruct=rakPeer->bufferedPackets.Allocate( _FILE_AND_LINE_ ); + if (recvFromStruct != NULL) + { + recvFromStruct->s=s; + recvFromStruct->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; + recvFromStruct->extraSocketOptions=extraSocketOptions; + SocketLayer::RecvFromBlocking(s, rakPeer, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); + + if (recvFromStruct->bytesRead>0) + { + RakAssert(recvFromStruct->systemAddress.GetPort()); + rakPeer->bufferedPackets.Push(recvFromStruct); + rakPeer->quitAndDataEvents.SetEvent(); + rakPeer->errorState = 0; + } + else + { + if (recvFromStruct->bytesRead < 0) { + --rakPeer->errorState; + RakSleep(30); + } + rakPeer->bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); + } + } + else + RakSleep(30); + } + rakPeer->isRecvFromLoopThreadActive.Decrement(); + + + + + return 0; + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RAK_THREAD_DECLARATION(RakNet::UpdateNetworkLoop) +{ + + + + RakPeer * rakPeer = ( RakPeer * ) arguments; + + +/* + // 11/15/05 - this is slower than Sleep() +#ifdef _WIN32 +#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) + // Lets see if these timers give better performance than Sleep + HANDLE timerHandle; + LARGE_INTEGER dueTime; + + if ( rakPeer->threadSleepTimer <= 0 ) + rakPeer->threadSleepTimer = 1; + + // 2nd parameter of false means synchronization timer instead of manual-reset timer + timerHandle = CreateWaitableTimer( NULL, FALSE, 0 ); + + RakAssert( timerHandle ); + + dueTime.QuadPart = -10000 * rakPeer->threadSleepTimer; // 10000 is 1 ms? + + BOOL success = SetWaitableTimer( timerHandle, &dueTime, rakPeer->threadSleepTimer, NULL, NULL, FALSE ); + (void) success; + RakAssert( success ); + +#endif +#endif +*/ + + BitStream updateBitStream( MAXIMUM_MTU_SIZE +#if LIBCAT_SECURITY==1 + + cat::AuthenticatedEncryption::OVERHEAD_BYTES +#endif + ); + + RakNet::TimeUS timeNS; + RakNet::TimeMS timeMS; + + rakPeer->isMainLoopThreadActive = true; + + while ( rakPeer->endThreads == false ) + { + // Set inside RunUpdateCycle() itself, this is here for testing + timeNS=0; + timeMS=0; + +// #ifdef _DEBUG +// // Sanity check, make sure RunUpdateCycle does not block or not otherwise get called for a long time +// RakNetTime thisCall=RakNet::GetTime(); +// RakAssert(thisCall-lastCall<250); +// lastCall=thisCall; +// #endif + if (rakPeer->userUpdateThreadPtr) + rakPeer->userUpdateThreadPtr(rakPeer, rakPeer->userUpdateThreadData); + + rakPeer->RunUpdateCycle(timeNS, timeMS, updateBitStream); + + // Pending sends go out this often, unless quitAndDataEvents is set + rakPeer->quitAndDataEvents.WaitOnEvent(10); + + /* + +// #if ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && +#if defined(USE_WAIT_FOR_MULTIPLE_EVENTS) && defined(_WIN32) + + if (rakPeer->threadSleepTimer>0) + { + WSAEVENT eventArray[256]; + unsigned int i, eventArrayIndex; + for (i=0,eventArrayIndex=0; i < rakPeer->socketList.Size(); i++) + { + if (rakPeer->socketList[i]->recvEvent!=INVALID_HANDLE_VALUE) + { + eventArray[eventArrayIndex]=rakPeer->socketList[i]->recvEvent; + eventArrayIndex++; + if (eventArrayIndex==256) + break; + } + } + WSAWaitForMultipleEvents(eventArrayIndex,(const HANDLE*) &eventArray,FALSE,rakPeer->threadSleepTimer,FALSE); + } + else + { + RakSleep(0); + } + +#else // ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + #pragma message("-- RakNet: Using Sleep(). Uncomment USE_WAIT_FOR_MULTIPLE_EVENTS in RakNetDefines.h if you want to use WaitForSingleObject instead. --") + + RakSleep( rakPeer->threadSleepTimer ); +#endif + */ + } + + rakPeer->isMainLoopThreadActive = false; + + /* +#ifdef _WIN32 +#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) + CloseHandle(timerHandle); +#endif +#endif + */ + + + + + return 0; + +} + +void RakPeer::CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet) +{ + for (unsigned int i=0; i < pluginList.Size(); i++) + { + switch (packet->data[0]) + { + case ID_DISCONNECTION_NOTIFICATION: + pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_DISCONNECTION_NOTIFICATION); + break; + case ID_CONNECTION_LOST: + pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_CONNECTION_LOST); + break; + case ID_NEW_INCOMING_CONNECTION: + pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, true); + break; + case ID_CONNECTION_REQUEST_ACCEPTED: + pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, false); + break; + case ID_CONNECTION_ATTEMPT_FAILED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_ATTEMPT_FAILED); + break; + case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); + break; + case ID_OUR_SYSTEM_REQUIRES_SECURITY: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_OUR_SYSTEM_REQUIRES_SECURITY); + break; + case ID_PUBLIC_KEY_MISMATCH: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_PUBLIC_KEY_MISMATCH); + break; + case ID_ALREADY_CONNECTED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_ALREADY_CONNECTED); + break; + case ID_NO_FREE_INCOMING_CONNECTIONS: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_NO_FREE_INCOMING_CONNECTIONS); + break; + case ID_CONNECTION_BANNED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_BANNED); + break; + case ID_INVALID_PASSWORD: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INVALID_PASSWORD); + break; + case ID_INCOMPATIBLE_PROTOCOL_VERSION: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INCOMPATIBLE_PROTOCOL); + break; + case ID_IP_RECENTLY_CONNECTED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_IP_RECENTLY_CONNECTED); + break; + } + } +} + +// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) +// #pragma pop_macro("new") +// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/src/raknet/RakPeer.h b/src/raknet/RakPeer.h index 8fff5c7..ded067c 100755 --- a/src/raknet/RakPeer.h +++ b/src/raknet/RakPeer.h @@ -1,984 +1,984 @@ -/// \file -/// \brief Declares RakPeer class. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -// TODO - RakNet 4 - Add network simulator -// TODO - RakNet 4 - Enable disabling flow control per connections - -#ifndef __RAK_PEER_H -#define __RAK_PEER_H - -#include "ReliabilityLayer.h" -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "SingleProducerConsumer.h" -#include "SimpleMutex.h" -#include "DS_OrderedList.h" -#include "Export.h" -#include "RakString.h" -#include "RakThread.h" -#include "RakNetSocket.h" -#include "RakNetSmartPtr.h" -#include "DS_ThreadsafeAllocatingQueue.h" -#include "SignaledEvent.h" -#include "NativeFeatureIncludes.h" -#include "SecureHandshake.h" -#include "LocklessTypes.h" - -namespace RakNet { -/// Forward declarations -class HuffmanEncodingTree; -class PluginInterface2; - -// Sucks but this struct has to be outside the class. Inside and DevCPP won't let you refer to the struct as RakPeer::RemoteSystemIndex while GCC -// forces you to do RakPeer::RemoteSystemIndex -struct RemoteSystemIndex{unsigned index; RemoteSystemIndex *next;}; -//int RAK_DLL_EXPORT SystemAddressAndIndexComp( const SystemAddress &key, const RemoteSystemIndex &data ); // GCC requires RakPeer::RemoteSystemIndex or it won't compile - -///\brief Main interface for network communications. -/// \details It implements most of RakNet's functionality and is the primary interface for RakNet. -/// -/// Inherits RakPeerInterface. -/// -/// See the individual functions for what the class can do. -/// -class RAK_DLL_EXPORT RakPeer : public RakPeerInterface -{ -public: - ///Constructor - RakPeer(); - - ///Destructor - virtual ~RakPeer(); - - // --------------------------------------------------------------------------------------------Major Low Level Functions - Functions needed by most users-------------------------------------------------------------------------------------------- - /// \brief Starts the network threads and opens the listen port. - /// \details You must call this before calling Connect(). - /// \pre On the PS3, call Startup() after Client_Login() - /// \note Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). - /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections. - /// \param[in] maxConnections Maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so that the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.A hybrid would set it to the sum of both types of connections. - /// \param[in] localPort The port to listen for connections on. On linux the system may be set up so thast ports under 1024 are restricted for everything but the root user. Use a higher port for maximum compatibility. - /// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); However, on the XBOX be sure to use IPPROTO_VDP - /// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - /// \return RAKNET_STARTED on success, otherwise appropriate failure enumeration. - StartupResult Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority=-99999 ); - - /// If you accept connections, you must call this or else security will not be enabled for incoming connections. - /// This feature requires more round trips, bandwidth, and CPU time for the connection handshake - /// x64 builds require under 25% of the CPU time of other builds - /// See the Encryption sample for example usage - /// \pre Must be called while offline - /// \pre LIBCAT_SECURITY must be defined to 1 in NativeFeatureIncludes.h for this function to have any effect - /// \param[in] publicKey A pointer to the public key for accepting new connections - /// \param[in] privateKey A pointer to the private key for accepting new connections - /// \param[in] bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection - bool InitializeSecurity( const char *publicKey, const char *privateKey, bool bRequireClientKey = false ); - - /// Disables security for incoming connections. - /// \note Must be called while offline - void DisableSecurity( void ); - - /// \brief This is useful if you have a fixed-address internal server behind a LAN. - /// - /// Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. - /// \note If secure connections are on, do not use secure connections for a specific IP address. - /// \param[in] ip IP address to add. * wildcards are supported. - void AddToSecurityExceptionList(const char *ip); - - /// \brief Remove a specific connection previously added via AddToSecurityExceptionList. - /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. - void RemoveFromSecurityExceptionList(const char *ip); - - /// \brief Checks to see if a given IP is in the security exception list. - /// \param[in] IP address to check. - /// \return True if the IP address is found in security exception list, else returns false. - bool IsInSecurityExceptionList(const char *ip); - - /// \brief Sets the maximum number of incoming connections allowed. - /// \details If the number of incoming connections is less than the number of players currently connected, - /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, - /// it will be reduced to the maximum number of peers allowed. - /// - /// Defaults to 0, meaning by default, nobody can connect to you - /// \param[in] numberAllowed Maximum number of incoming connections allowed. - void SetMaximumIncomingConnections( unsigned short numberAllowed ); - - /// \brief Returns the value passed to SetMaximumIncomingConnections(). - /// \return Maximum number of incoming connections, which is always <= maxConnections - unsigned short GetMaximumIncomingConnections( void ) const; - - /// \brief Returns how many open connections exist at this time. - /// \return Number of open connections. - unsigned short NumberOfConnections(void) const; - - /// \brief Sets the password for the incoming connections. - /// \details The password must match in the call to Connect (defaults to none). - /// Pass 0 to passwordData to specify no password. - /// This is a way to set a low level password for all incoming connections. To selectively reject connections, implement your own scheme using CloseConnection() to remove unwanted connections. - /// \param[in] passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. Specify 0 for no password data - /// \param[in] passwordDataLength The length in bytes of passwordData - void SetIncomingPassword( const char* passwordData, int passwordDataLength ); - - /// \brief Gets the password passed to SetIncomingPassword - /// \param[out] passwordData Should point to a block large enough to hold the password data you passed to SetIncomingPassword() - /// \param[in,out] passwordDataLength Maximum size of the passwordData array. Modified to hold the number of bytes actually written. - void GetIncomingPassword( char* passwordData, int *passwordDataLength ); - - /// \brief Connect to the specified host (ip or domain name) and server port. - /// \details Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. - /// Calling both acts as a true peer. - /// - /// This is a non-blocking connection. - /// - /// The connection is successful when GetConnectionState() returns IS_CONNECTED or Receive() gets a message with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. - /// If the connection is not successful, such as a rejected connection or no response then neither of these things will happen. - /// \pre Requires that you first call Initialize. - /// \param[in] host Either a dotted IP address or a domain name. - /// \param[in] remotePort Port to connect to on the remote machine. - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData. - /// \param[in] publicKey The public key the server is using. If 0, the server is not using security. If non-zero, the publicKeyMode member determines how to connect - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to determine the one to send on. - /// \param[in] sendConnectionAttemptCount Number of datagrams to send to the other system to try to connect. - /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. - /// \param[in] timeoutTime Time to elapse before dropping the connection if a reliable message could not be sent. 0 to use the default value from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); - /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. - /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! - /// \note It is possible to immediately get back ID_CONNECTION_ATTEMPT_FAILED if you exceed the maxConnections parameter passed to Startup(). This could happen if you call CloseConnection() with sendDisconnectionNotificaiton true, then immediately call Connect() before the connection has closed. - ConnectionAttemptResult Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey=0, unsigned connectionSocketIndex=0, unsigned sendConnectionAttemptCount=6, unsigned timeBetweenSendConnectionAttemptsMS=1000, RakNet::TimeMS timeoutTime=0 ); - - /// \brief Connect to the specified host (ip or domain name) and server port. - /// \param[in] host Either a dotted IP address or a domain name. - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData. - /// \param[in] socket A bound socket returned by another instance of RakPeerInterface. - /// \param[in] sendConnectionAttemptCount Number of datagrams to send to the other system to try to connect. - /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned.. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. - /// \param[in] timeoutTime Time to elapse before dropping the connection if a reliable message could not be sent. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); - /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. - /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! - virtual ConnectionAttemptResult ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey=0, unsigned sendConnectionAttemptCount=6, unsigned timeBetweenSendConnectionAttemptsMS=1000, RakNet::TimeMS timeoutTime=0); - - /* /// \brief Connect to the specified network ID (Platform specific console function) - /// \details Does built-in NAT traversal - /// \param[in] networkServiceId Network ID structure for the online service - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData. - //bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength );*/ - - /// \brief Stops the network threads and closes all connections. - /// \param[in] blockDuration Wait time(milli seconds) for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. - /// \param[in] orderingChannel Channel on which ID_DISCONNECTION_NOTIFICATION will be sent, if blockDuration > 0. - /// \param[in] disconnectionNotificationPriority Priority of sending ID_DISCONNECTION_NOTIFICATION. - /// If set to 0, the disconnection notification won't be sent. - void Shutdown( unsigned int blockDuration, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY ); - - /// \brief Returns true if the network thread is running. - /// \return True if the network thread is running, False otherwise - bool IsActive( void ) const; - - /// \brief Fills the array remoteSystems with the SystemAddress of all the systems we are connected to. - /// \param[out] remoteSystems An array of SystemAddress structures, to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to get the number of systems we are connected to. - /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array. - bool GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const; - - /// Returns the next uint32_t that Send() will return - /// \note If using RakPeer from multiple threads, this may not be accurate for your thread. Use IncrementNextSendReceipt() in that case. - /// \return The next uint32_t that Send() or SendList will return - virtual uint32_t GetNextSendReceipt(void); - - /// Returns the next uint32_t that Send() will return, and increments the value by one - /// \note If using RakPeer from multiple threads, pass this to forceReceipt in the send function - /// \return The next uint32_t that Send() or SendList will return - virtual uint32_t IncrementNextSendReceipt(void); - - /// \brief Sends a block of data to the specified system that you are connected to. - /// \note This function only works while the connected. - /// \note The first byte should be a message identifier starting at ID_USER_PACKET_ENUM. - /// \param[in] data Block of data to send. - /// \param[in] length Size in bytes of the data to send. - /// \param[in] priority Priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliably to send this data. See PacketPriority.h - /// \param[in] orderingChannel When using ordered or sequenced messages, the channel to order these on. Messages are only ordered relative to other messages on the same stream. - /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - uint32_t Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); - - /// \brief "Send" to yourself rather than a remote system. - /// \details The message will be processed through the plugins and returned to the game as usual. - /// This function works anytime - /// \note The first byte should be a message identifier starting at ID_USER_PACKET_ENUM - /// \param[in] data Block of data to send. - /// \param[in] length Size in bytes of the data to send. - void SendLoopback( const char *data, const int length ); - - /// \brief Sends a block of data to the specified system that you are connected to. - /// - /// Same as the above version, but takes a BitStream as input. - /// \param[in] bitStream Bitstream to send - /// \param[in] priority Priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliably to send this data. See PacketPriority.h - /// \param[in] orderingChannel Channel to order the messages on, when using ordered or sequenced messages. Messages are only ordered relative to other messages on the same stream. - /// \param[in] systemIdentifier System Address or RakNetGUID to send this packet to, or in the case of broadcasting, the address not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none. - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. - uint32_t Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); - - /// \brief Sends multiple blocks of data, concatenating them automatically. - /// - /// This is equivalent to: - /// RakNet::BitStream bs; - /// bs.WriteAlignedBytes(block1, blockLength1); - /// bs.WriteAlignedBytes(block2, blockLength2); - /// bs.WriteAlignedBytes(block3, blockLength3); - /// Send(&bs, ...) - /// - /// This function only works when connected. - /// \param[in] data An array of pointers to blocks of data - /// \param[in] lengths An array of integers indicating the length of each block of data - /// \param[in] numParameters Length of the arrays data and lengths - /// \param[in] priority Priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliably to send this data. See PacketPriority.h - /// \param[in] orderingChannel Channel to order the messages on, when using ordered or sequenced messages. Messages are only ordered relative to other messages on the same stream. - /// \param[in] systemIdentifier System Address or RakNetGUID to send this packet to, or in the case of broadcasting, the address not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none. - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - uint32_t SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); - - /// \brief Gets a message from the incoming message queue. - /// \details Use DeallocatePacket() to deallocate the message after you are done with it. - /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. - /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. - /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. - /// \sa RakNetTypes.h contains struct Packet. - Packet* Receive( void ); - - /// \brief Call this to deallocate a message returned by Receive() when you are done handling it. - /// \param[in] packet Message to deallocate. - void DeallocatePacket( Packet *packet ); - - /// \brief Return the total number of connections we are allowed. - /// \return Total number of connections allowed. - unsigned short GetMaximumNumberOfPeers( void ) const; - - // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- - /// \brief Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). - /// \details This method closes the connection irrespective of who initiated the connection. - /// \param[in] target Which system to close the connection to. - /// \param[in] sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. - /// \param[in] channel Which ordering channel to send the disconnection notification on, if any - /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. - void CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY ); - - /// \brief Cancel a pending connection attempt. - /// \details If we are already connected, the connection stays open - /// \param[in] target Target system to cancel. - void CancelConnectionAttempt( const SystemAddress target ); - /// Returns if a system is connected, disconnected, connecting in progress, or various other states - /// \param[in] systemIdentifier The system we are referring to - /// \note This locks a mutex, do not call too frequently during connection attempts or the attempt will take longer and possibly even timeout - /// \return What state the remote system is in - ConnectionState GetConnectionState(const AddressOrGUID systemIdentifier); - - /// \brief Given \a systemAddress, returns its index into remoteSystemList. - /// \details Values range from 0 to the maximum number of players allowed - 1. - /// This includes systems which were formerly connected, but are now not connected. - /// \param[in] systemAddress The SystemAddress we are referring to - /// \return The index of this SystemAddress or -1 on system not found. - int GetIndexFromSystemAddress( const SystemAddress systemAddress ) const; - - /// \brief Given \a index into remoteSystemList, will return a SystemAddress. - /// This function is only useful for looping through all systems. - /// - /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. - /// \return The SystemAddress structure corresponding to \a index in remoteSystemList. - SystemAddress GetSystemAddressFromIndex( int index ); - - /// \brief Same as GetSystemAddressFromIndex but returns RakNetGUID - /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. - /// \return The RakNetGUID - RakNetGUID GetGUIDFromIndex( int index ); - - /// \brief Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient - /// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system - /// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the \a addresses list will match the size of the \a guids list. - /// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. - void GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const; - - /// \brief Bans an IP from connecting. - /// \details Banned IPs persist between connections but are not saved on shutdown nor loaded on startup. - /// \param[in] IP Dotted IP address. You can use * for a wildcard address, such as 128.0.0. * will ban all IP addresses starting with 128.0.0. - /// \param[in] milliseconds Gives time in milli seconds for a temporary ban of the IP address. Use 0 for a permanent ban. - void AddToBanList( const char *IP, RakNet::TimeMS milliseconds=0 ); - - /// \brief Allows a previously banned IP to connect. - /// param[in] Dotted IP address. You can use * as a wildcard. An IP such as 128.0.0.* will ban all IP addresses starting with 128.0.0. - void RemoveFromBanList( const char *IP ); - - /// \brief Allows all previously banned IPs to connect. - void ClearBanList( void ); - - /// \brief Returns true or false indicating if a particular IP is banned. - /// \param[in] IP Dotted IP address. - /// \return True if IP matches any IPs in the ban list, accounting for any wildcards. False otherwise. - bool IsBanned( const char *IP ); - - /// \brief Enable or disable allowing frequent connections from the same IP adderss - /// \details This is a security measure which is disabled by default, but can be set to true to prevent attackers from using up all connection slots. - /// \param[in] b True to limit connections from the same ip to at most 1 per 100 milliseconds. - void SetLimitIPConnectionFrequency(bool b); - - // --------------------------------------------------------------------------------------------Pinging Functions - Functions dealing with the automatic ping mechanism-------------------------------------------------------------------------------------------- - /// Send a ping to the specified connected system. - /// \pre The sender and recipient must already be started via a successful call to Startup() - /// \param[in] target Which system to ping - void Ping( const SystemAddress target ); - - /// \brief Send a ping to the specified unconnected system. - /// \details The remote system, if it is Initialized, will respond with ID_PONG followed by sizeof(RakNet::TimeMS) containing the system time the ping was sent. Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h - /// System should reply with ID_PONG if it is active - /// \param[in] host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \return true on success, false on failure (unknown hostname) - bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 ); - - /// \brief Returns the average of all ping times read for the specific system or -1 if none read yet - /// \param[in] systemAddress Which system we are referring to - /// \return The ping time for this system, or -1 - int GetAveragePing( const AddressOrGUID systemIdentifier ); - - /// \brief Returns the last ping time read for the specific system or -1 if none read yet. - /// \param[in] systemAddress Which system we are referring to - /// \return The last ping time for this system, or -1. - int GetLastPing( const AddressOrGUID systemIdentifier ) const; - - /// \brief Returns the lowest ping time read or -1 if none read yet. - /// \param[in] systemAddress Which system we are referring to - /// \return The lowest ping time for this system, or -1. - int GetLowestPing( const AddressOrGUID systemIdentifier ) const; - - /// Ping the remote systems every so often, or not. Can be called anytime. - /// By default this is true. Recommended to leave on, because congestion control uses it to determine how often to resend lost packets. - /// It would be true by default to prevent timestamp drift, since in the event of a clock spike, the timestamp deltas would no longer be accurate - /// \param[in] doPing True to start occasional pings. False to stop them. - void SetOccasionalPing( bool doPing ); - - // --------------------------------------------------------------------------------------------Static Data Functions - Functions dealing with API defined synchronized memory-------------------------------------------------------------------------------------------- - /// \brief Sets the data to send along with a LAN server discovery or offline ping reply. - /// \param[in] data Block of data to send, or 0 for none - /// \param[in] length Length of the data in bytes, or 0 for none - /// \note \a length should be under 400 bytes, as a security measure against flood attacks - /// \sa Ping.cpp - void SetOfflinePingResponse( const char *data, const unsigned int length ); - - /// \brief Returns pointers to a copy of the \a data passed to SetOfflinePingResponse. - /// \param[out] data A pointer to a copy of the data passed to SetOfflinePingResponse() - /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() - /// \sa SetOfflinePingResponse - void GetOfflinePingResponse( char **data, unsigned int *length ); - - //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- - /// \brief Returns the unique address identifier that represents you or another system on the the network and is based on your local IP / port. - /// \note Not supported by the XBOX - /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address - /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) - /// \return Identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy - SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const; - - /// \brief Returns the unique address identifier that represents the target on the the network and is based on the target's external IP / port. - /// \param[in] target The SystemAddress of the remote system. Usually the same for all systems, unless you have two or more network cards. - SystemAddress GetExternalID( const SystemAddress target ) const; - - /// Return my own GUID - const RakNetGUID GetMyGUID(void) const; - - /// Return the address bound to a socket at the specified index - SystemAddress GetMyBoundAddress(const int socketIndex=0); - - /// \brief Given a connected system address, this method gives the unique GUID representing that instance of RakPeer. - /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different. - /// Complexity is O(log2(n)). - /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID - /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID - /// \param[in] input The system address of the target system we are connected to. - const RakNetGUID& GetGuidFromSystemAddress( const SystemAddress input ) const; - - /// \brief Gives the system address of a connected system, given its GUID. - /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different. - /// Currently O(log(n)), but this may be improved in the future - /// If \a input is UNASSIGNED_RAKNET_GUID, UNASSIGNED_SYSTEM_ADDRESS is returned. - /// \param[in] input The RakNetGUID of the target system. - SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const; - - /// Given the SystemAddress of a connected system, get the public key they provided as an identity - /// Returns false if system address was not found or client public key is not known - /// \param[in] input The RakNetGUID of the system - /// \param[in] client_public_key The connected client's public key is copied to this address. Buffer must be cat::EasyHandshake::PUBLIC_KEY_BYTES bytes in length. - bool GetClientPublicKeyFromSystemAddress( const SystemAddress input, char *client_public_key ) const; - - /// \brief Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. - - /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. - /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. - /// Do not set different values for different computers that are connected to each other, or you won't be able to reconnect after ID_CONNECTION_LOST - /// \param[in] timeMS Time, in MS - /// \param[in] target SystemAddress structure of the target system. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. - void SetTimeoutTime( RakNet::TimeMS timeMS, const SystemAddress target ); - - /// \brief Returns the Timeout time for the given system. - /// \param[in] target Target system to get the TimeoutTime for. Pass UNASSIGNED_SYSTEM_ADDRESS to get the default value. - /// \return Timeout time for a given system. - RakNet::TimeMS GetTimeoutTime( const SystemAddress target ); - - /// \brief Returns the current MTU size - /// \param[in] target Which system to get MTU for. UNASSIGNED_SYSTEM_ADDRESS to get the default - /// \return The current MTU size of the target system. - int GetMTUSize( const SystemAddress target ) const; - - /// \brief Returns the number of IP addresses this system has internally. - /// \details Get the actual addresses from GetLocalIP() - unsigned GetNumberOfAddresses( void ); - - /// Returns an IP address at index 0 to GetNumberOfAddresses-1 in ipList array. - /// \param[in] index index into the list of IP addresses - /// \return The local IP address at this index - const char* GetLocalIP( unsigned int index ); - - /// Is this a local IP? - /// Checks if this ip is in the ipList array. - /// \param[in] An IP address to check, excluding the port. - /// \return True if this is one of the IP addresses returned by GetLocalIP - bool IsLocalIP( const char *ip ); - - /// \brief Allow or disallow connection responses from any IP. - /// \details Normally this should be false, but may be necessary when connecting to servers with multiple IP addresses. - /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections. - void AllowConnectionResponseIPMigration( bool allow ); - - /// \brief Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. - /// This will send our external IP outside the LAN along with some user data to the remote system. - /// \pre The sender and recipient must already be started via a successful call to Initialize - /// \param[in] host Either a dotted IP address or a domain name - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] data Optional data to append to the packet. - /// \param[in] dataLength Length of data in bytes. Use 0 if no data. - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \return False if IsActive()==false or the host is unresolvable. True otherwise. - bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 ); - - /// \brief Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. - /// \details ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived. - /// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. - /// Defaults to 0 (never return this notification). - /// \param[in] interval How many messages to use as an interval before a download progress notification is returned. - void SetSplitMessageProgressInterval(int interval); - - /// \brief Returns what was passed to SetSplitMessageProgressInterval(). - /// \return Number of messages to be recieved before a download progress notification is returned. Default to 0. - int GetSplitMessageProgressInterval(void) const; - - /// \brief Set how long to wait before giving up on sending an unreliable message. - /// Useful if the network is clogged up. - /// Set to 0 or less to never timeout. Defaults to 0. - /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. - void SetUnreliableTimeout(RakNet::TimeMS timeoutMS); - - /// \brief Send a message to a host, with the IP socket option TTL set to 3. - /// \details This message will not reach the host, but will open the router. - /// \param[in] host The address of the remote host in dotted notation. - /// \param[in] remotePort The port number to send to. - /// \param[in] ttl Max hops of datagram, set to 3 - /// \param[in] connectionSocketIndex userConnectionSocketIndex. - /// \remarks Used for NAT-Punchthrough - void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 ); - - // -------------------------------------------------------------------------------------------- Plugin Functions-------------------------------------------------------------------------------------------- - /// \brief Attaches a Plugin interface to an instance of the base class (RakPeer or PacketizedTCP) to run code automatically on message receipt in the Receive call. - /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. - /// \param[in] messageHandler Pointer to the plugin to attach. - void AttachPlugin( PluginInterface2 *plugin ); - - /// \brief Detaches a Plugin interface from the instance of the base class (RakPeer or PacketizedTCP) it is attached to. - /// \details This method disables the plugin code from running automatically on base class's updates or message receipt. - /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. - /// \param[in] messageHandler Pointer to a plugin to detach. - void DetachPlugin( PluginInterface2 *messageHandler ); - - // --------------------------------------------------------------------------------------------Miscellaneous Functions-------------------------------------------------------------------------------------------- - /// \brief Puts a message back in the receive queue in case you don't want to deal with it immediately. - /// \param[in] packet The pointer to the packet you want to push back. - /// \param[in] pushAtHead True to push the packet at the start of the queue so that the next receive call returns it. False to push it at the end of the queue. - /// \note Setting pushAtHead to false end makes the packets out of order. - void PushBackPacket( Packet *packet, bool pushAtHead ); - - /// \internal - /// \brief For a given system identified by \a guid, change the SystemAddress to send to. - /// \param[in] guid The connection we are referring to - /// \param[in] systemAddress The new address to send to - void ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress); - - /// \brief Returns a packet for you to write to if you want to create a Packet for some reason. - /// You can add it to the receive buffer with PushBackPacket - /// \param[in] dataSize How many bytes to allocate for the buffer - /// \return A packet. - Packet* AllocatePacket(unsigned dataSize); - - /// \brief Get the socket used with a particular active connection. - /// The smart pointer reference counts the RakNetSocket object, so the socket will remain active as long as the smart pointer does, even if RakNet were to shutdown or close the connection. - /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. - /// \param[in] target Which system. - /// \return A smart pointer object containing the socket information about the target. Be sure to check IsNull() which is returned if the update thread is unresponsive, shutting down, or if this system is not connected. - virtual RakNetSmartPtr GetSocket( const SystemAddress target ); - - /// \brief Gets all sockets in use. - /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. - /// \param[out] sockets List of RakNetSocket structures in use. Sockets will not be closed until \a sockets goes out of scope - virtual void GetSockets( DataStructures::List > &sockets ); - virtual void ReleaseSockets( DataStructures::List > &sockets ); - - /// \internal - virtual void WriteOutOfBandHeader(RakNet::BitStream *bitStream); - - /// If you need code to run in the same thread as RakNet's update thread, this function can be used for that - /// \param[in] _userUpdateThreadPtr C callback function - /// \param[in] _userUpdateThreadData Passed to C callback function - virtual void SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData); - - // --------------------------------------------------------------------------------------------Network Simulator Functions-------------------------------------------------------------------------------------------- - /// Adds simulated ping and packet loss to the outgoing data flow. - /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and packetloss value on each. - /// You can exclude network simulator code with the _RELEASE #define to decrease code size - /// \deprecated Use http://www.jenkinssoftware.com/forum/index.php?topic=1671.0 instead. - /// \note Doesn't work past version 3.6201 - /// \param[in] packetloss Chance to lose a packet. Ranges from 0 to 1. - /// \param[in] minExtraPing The minimum time to delay sends. - /// \param[in] extraPingVariance The additional random time to delay sends. - virtual void ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance); - - /// Limits how much outgoing bandwidth can be sent per-connection. - /// This limit does not apply to the sum of all connections! - /// Exceeding the limit queues up outgoing traffic - /// \param[in] maxBitsPerSecond Maximum bits per second to send. Use 0 for unlimited (default). Once set, it takes effect immedately and persists until called again. - virtual void SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ); - - /// Returns if you previously called ApplyNetworkSimulator - /// \return If you previously called ApplyNetworkSimulator - virtual bool IsNetworkSimulatorActive( void ); - - // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- - - /// \brief Returns a structure containing a large set of network statistics for the specified system. - /// You can map this data to a string using the C style StatisticsToString() function - /// \param[in] systemAddress Which connected system to get statistics for. - /// \param[in] rns If you supply this structure,the network statistics will be written to it. Otherwise the method uses a static struct to write the data, which is not threadsafe. - /// \return 0 if the specified system can't be found. Otherwise a pointer to the struct containing the specified system's network statistics. - /// \sa RakNetStatistics.h - RakNetStatistics * GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 ); - /// \brief Returns the network statistics of the system at the given index in the remoteSystemList. - /// \return True if the index is less than the maximum number of peers allowed and the system is active. False otherwise. - bool GetStatistics( const int index, RakNetStatistics *rns ); - - /// \Returns how many messages are waiting when you call Receive() - virtual unsigned int GetReceiveBufferSize(void); - - // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- - - /// \internal - bool SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 ); - - // static Packet *AllocPacket(unsigned dataSize, const char *file, unsigned int line); - - /// \internal - /// \brief Holds the clock differences between systems, along with the ping - struct PingAndClockDifferential - { - unsigned short pingTime; - RakNet::Time clockDifferential; - }; - - /// \internal - /// \brief All the information representing a connected system - struct RemoteSystemStruct - { - bool isActive; // Is this structure in use? - SystemAddress systemAddress; /// Their external IP on the internet - SystemAddress myExternalSystemAddress; /// Your external IP on the internet, from their perspective - SystemAddress theirInternalSystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; /// Their internal IP, behind the LAN - ReliabilityLayer reliabilityLayer; /// The reliability layer associated with this player - bool weInitiatedTheConnection; /// True if we started this connection via Connect. False if someone else connected to us. - PingAndClockDifferential pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE ]; /// last x ping times and calculated clock differentials with it - RakNet::Time pingAndClockDifferentialWriteIndex; /// The index we are writing into the pingAndClockDifferential circular buffer - unsigned short lowestPing; ///The lowest ping value encountered - RakNet::Time nextPingTime; /// When to next ping this player - RakNet::Time lastReliableSend; /// When did the last reliable send occur. Reliable sends must occur at least once every timeoutTime/2 units to notice disconnects - RakNet::Time connectionTime; /// connection time, if active. -// int connectionSocketIndex; // index into connectionSockets to send back on. - RakNetGUID guid; - int MTUSize; - // Reference counted socket to send back on - RakNetSmartPtr rakNetSocket; - SystemIndex remoteSystemIndex; - -#if LIBCAT_SECURITY==1 - // Cached answer used internally by RakPeer to prevent DoS attacks based on the connexion handshake - char answer[cat::EasyHandshake::ANSWER_BYTES]; - - // If the server has bRequireClientKey = true, then this is set to the validated public key of the connected client - // Valid after connectMode reaches HANDLING_CONNECTION_REQUEST - char client_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; -#endif - - enum ConnectMode {NO_ACTION, DISCONNECT_ASAP, DISCONNECT_ASAP_SILENTLY, DISCONNECT_ON_NO_ACK, REQUESTED_CONNECTION, HANDLING_CONNECTION_REQUEST, UNVERIFIED_SENDER, CONNECTED} connectMode; - }; - -protected: - - friend RAK_THREAD_DECLARATION(UpdateNetworkLoop); - friend RAK_THREAD_DECLARATION(RecvFromLoop); - friend RAK_THREAD_DECLARATION(UDTConnect); - - friend bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, bool *isOfflineMessage, RakNet::TimeUS timeRead ); - friend void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead, BitStream &updateBitStream ); - friend void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead, BitStream &updateBitStream ); - - int GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const; - int GetIndexFromGuid( const RakNetGUID guid ); - - //void RemoveFromRequestedConnectionsList( const SystemAddress systemAddress ); - // Two versions needed because some buggy compilers strip the last parameter if unused, and crashes - ConnectionAttemptResult SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime, RakNetSmartPtr socket ); - ConnectionAttemptResult SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ); - ///Get the reliability layer associated with a systemAddress. - /// \param[in] systemAddress The player identifier - /// \return 0 if none - RemoteSystemStruct *GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const; - RakPeer::RemoteSystemStruct *GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const; - void ValidateRemoteSystemLookup(void) const; - RemoteSystemStruct *GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const; - ///Parse out a connection request packet - void ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize); - void OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, RakNet::Time incomingTimestamp ); - ///Send a reliable disconnect packet to this player and disconnect them when it is delivered - void NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ); - ///Returns how many remote systems initiated a connection to us - unsigned short GetNumberOfRemoteInitiatedConnections( void ) const; - /// \brief Get a free remote system from the list and assign our systemAddress to it. - /// \note Should only be called from the update thread - not the user thread. - /// \param[in] systemAddress systemAddress to be assigned - /// \param[in] connectionMode connection mode of the RemoteSystem. - /// \param[in] rakNetSocket - /// \param[in] thisIPConnectedRecently Is this IP connected recently? set to False; - /// \param[in] bindingAddress Address to be binded with the remote system - /// \param[in] incomingMTU MTU for the remote system - RemoteSystemStruct * AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSmartPtr incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ); - /// \brief Adjust the timestamp of the incoming packet to be relative to this system. - /// \param[in] data Data in the incoming packet. - /// \param[in] systemAddress Sender of the incoming packet. - void ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const; - /// Get the most accurate clock differential for a certain player. - /// \param[in] systemAddress The player with whose clock the time difference is calculated. - /// \returns The clock differential for a certain player. - RakNet::Time GetBestClockDifferential( const SystemAddress systemAddress ) const; - - bool IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const; - SystemAddress GetLoopbackAddress(void) const; - - ///Set this to true to terminate the Peer thread execution - volatile bool endThreads; - ///true if the peer thread is active. - volatile bool isMainLoopThreadActive; - - RakNet::LocklessUint32_t isRecvFromLoopThreadActive; - - - bool occasionalPing; /// Do we occasionally ping the other systems?*/ - ///Store the maximum number of peers allowed to connect - unsigned short maximumNumberOfPeers; - //05/02/06 Just using maximumNumberOfPeers instead - ///Store the maximum number of peers able to connect, including reserved connection slots for pings, etc. - //unsigned short remoteSystemListSize; - ///Store the maximum incoming connection allowed - unsigned short maximumIncomingConnections; - RakNet::BitStream offlinePingResponse; - ///Local Player ID - // SystemAddress mySystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; - char incomingPassword[256]; - unsigned char incomingPasswordLength; - - /// This is an array of pointers to RemoteSystemStruct - /// This allows us to preallocate the list when starting, so we don't have to allocate or delete at runtime. - /// Another benefit is that is lets us add and remove active players simply by setting systemAddress - /// and moving elements in the list by copying pointers variables without affecting running threads, even if they are in the reliability layer - RemoteSystemStruct* remoteSystemList; - /// activeSystemList holds a list of pointers and is preallocated to be the same size as remoteSystemList. It is updated only by the network thread, but read by both threads - /// When the isActive member of RemoteSystemStruct is set to true or false, that system is added to this list of pointers - /// Threadsafe because RemoteSystemStruct is preallocated, and the list is only added to, not removed from - RemoteSystemStruct** activeSystemList; - unsigned int activeSystemListSize; - - // Use a hash, with binaryAddress plus port mod length as the index - RemoteSystemIndex **remoteSystemLookup; - unsigned int RemoteSystemLookupHashIndex(const SystemAddress &sa) const; - void ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex); - void DereferenceRemoteSystem(const SystemAddress &sa); - RemoteSystemStruct* GetRemoteSystem(const SystemAddress &sa) const; - unsigned int GetRemoteSystemIndex(const SystemAddress &sa) const; - void ClearRemoteSystemLookup(void); - DataStructures::MemoryPool remoteSystemIndexPool; - - void AddToActiveSystemList(unsigned int remoteSystemListIndex); - void RemoveFromActiveSystemList(const SystemAddress &sa); - -// unsigned int LookupIndexUsingHashIndex(const SystemAddress &sa) const; -// unsigned int RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const; -// unsigned int FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const; - - enum - { - // Only put these mutexes in user thread functions! - requestedConnectionList_Mutex, - offlinePingResponse_Mutex, - NUMBER_OF_RAKPEER_MUTEXES - }; - SimpleMutex rakPeerMutexes[ NUMBER_OF_RAKPEER_MUTEXES ]; - ///RunUpdateCycle is not thread safe but we don't need to mutex calls. Just skip calls if it is running already - - bool updateCycleIsRunning; - ///The list of people we have tried to connect to recently - - //DataStructures::Queue requestedConnectionsList; - ///Data that both the client and the server needs - - unsigned int bytesSentPerSecond, bytesReceivedPerSecond; - // bool isSocketLayerBlocking; - // bool continualPing,isRecvfromThreadActive,isMainLoopThreadActive, endThreads, isSocketLayerBlocking; - unsigned int validationInteger; - SimpleMutex incomingQueueMutex, banListMutex; //,synchronizedMemoryQueueMutex, automaticVariableSynchronizationMutex; - //DataStructures::Queue incomingpacketSingleProducerConsumer; //, synchronizedMemorypacketSingleProducerConsumer; - // BitStream enumerationData; - - struct BanStruct - { - char *IP; - RakNet::TimeMS timeout; // 0 for none - }; - - struct RequestedConnectionStruct - { - SystemAddress systemAddress; - RakNet::Time nextRequestTime; - unsigned char requestsMade; - char *data; - unsigned short dataLength; - char outgoingPassword[256]; - unsigned char outgoingPasswordLength; - unsigned socketIndex; - unsigned int extraData; - unsigned sendConnectionAttemptCount; - unsigned timeBetweenSendConnectionAttemptsMS; - RakNet::TimeMS timeoutTime; - PublicKeyMode publicKeyMode; - RakNetSmartPtr socket; - enum {CONNECT=1, /*PING=2, PING_OPEN_CONNECTIONS=4,*/ /*ADVERTISE_SYSTEM=2*/} actionToTake; - -#if LIBCAT_SECURITY==1 - char handshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; - cat::ClientEasyHandshake *client_handshake; - char remote_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; -// char remote_challenge[cat::EasyHandshake::CHALLENGE_BYTES]; - // char random[16]; -#endif - }; -#if LIBCAT_SECURITY==1 - bool GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey); -#endif - - //DataStructures::List* > automaticVariableSynchronizationList; - DataStructures::List banList; - // Threadsafe, and not thread safe - DataStructures::List pluginListTS, pluginListNTS; - - DataStructures::Queue requestedConnectionQueue; - SimpleMutex requestedConnectionQueueMutex; - - bool RunUpdateCycle( RakNet::TimeUS timeNS, RakNet::Time timeMS, BitStream &updateBitStream ); - // void RunMutexedUpdateCycle(void); - - struct BufferedCommandStruct - { - BitSize_t numberOfBitsToSend; - PacketPriority priority; - PacketReliability reliability; - char orderingChannel; - AddressOrGUID systemIdentifier; - bool broadcast; - RemoteSystemStruct::ConnectMode connectionMode; - NetworkID networkID; - bool blockingCommand; // Only used for RPC - char *data; - bool haveRakNetCloseSocket; - unsigned connectionSocketIndex; - unsigned short remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions; - SOCKET socket; - unsigned short port; - uint32_t receipt; - enum {BCS_SEND, BCS_CLOSE_CONNECTION, BCS_GET_SOCKET, BCS_CHANGE_SYSTEM_ADDRESS,/* BCS_USE_USER_SOCKET, BCS_REBIND_SOCKET_ADDRESS, BCS_RPC, BCS_RPC_SHIFT,*/ BCS_DO_NOTHING} command; - }; - - // Single producer single consumer queue using a linked list - //BufferedCommandStruct* bufferedCommandReadIndex, bufferedCommandWriteIndex; - - DataStructures::ThreadsafeAllocatingQueue bufferedCommands; - - - // Constructor not called! - struct RecvFromStruct - { - - - - char data[MAXIMUM_MTU_SIZE]; - - int bytesRead; - SystemAddress systemAddress; - RakNet::TimeUS timeRead; - SOCKET s; - unsigned short remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions; - }; - - - DataStructures::ThreadsafeAllocatingQueue bufferedPackets; - - - struct SocketQueryOutput - { - SocketQueryOutput() {} - ~SocketQueryOutput() {} - DataStructures::List > sockets; - }; - - DataStructures::ThreadsafeAllocatingQueue socketQueryOutput; - - - bool AllowIncomingConnections(void) const; - - void PingInternal( const SystemAddress target, bool performImmediate, PacketReliability reliability ); - // This stores the user send calls to be handled by the update thread. This way we don't have thread contention over systemAddresss - void CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ); - void SendBuffered( const char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ); - void SendBufferedList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ); - bool SendImmediate( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, RakNet::TimeUS currentTime, uint32_t receipt ); - //bool HandleBufferedRPC(BufferedCommandStruct *bcs, RakNet::TimeMS time); - void ClearBufferedCommands(void); - void ClearBufferedPackets(void); - void ClearSocketQueryOutput(void); - void ClearRequestedConnectionList(void); - void AddPacketToProducer(RakNet::Packet *p); - unsigned int GenerateSeedFromGuid(void); - SimpleMutex securityExceptionMutex; - - //DataStructures::AVLBalancedBinarySearchTree rpcTree; - int defaultMTUSize; - bool trackFrequencyTable; - - // Smart pointer so I can return the object to the user - DataStructures::List > socketList; - void DerefAllSockets(void); - unsigned int GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const; - // Used for RPC replies - RakNet::BitStream *replyFromTargetBS; - SystemAddress replyFromTargetPlayer; - bool replyFromTargetBroadcast; - - RakNet::TimeMS defaultTimeoutTime; - - // Generate and store a unique GUID - void GenerateGUID(void); - unsigned int GetSystemIndexFromGuid( const RakNetGUID input ) const; - RakNetGUID myGuid; - - unsigned maxOutgoingBPS; - - // Nobody would use the internet simulator in a final build. -#ifdef _DEBUG - double _packetloss; - unsigned short _minExtraPing, _extraPingVariance; -#endif - - ///How long it has been since things were updated by a call to receiveUpdate thread uses this to determine how long to sleep for - //unsigned int lastUserUpdateCycle; - /// True to allow connection accepted packets from anyone. False to only allow these packets from servers we requested a connection to. - bool allowConnectionResponseIPMigration; - - SystemAddress firstExternalID; - int splitMessageProgressInterval; - RakNet::TimeMS unreliableTimeout; - - // Systems in this list will not go through the secure connection process, even when secure connections are turned on. Wildcards are accepted. - DataStructures::List securityExceptionList; - - SystemAddress ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ]; - - bool allowInternalRouting; - - void (*userUpdateThreadPtr)(RakPeerInterface *, void *); - void *userUpdateThreadData; - - - SignaledEvent quitAndDataEvents; - bool limitConnectionFrequencyFromTheSameIP; - - SimpleMutex packetAllocationPoolMutex; - DataStructures::MemoryPool packetAllocationPool; - - SimpleMutex packetReturnMutex; - DataStructures::Queue packetReturnQueue; - Packet *AllocPacket(unsigned dataSize, const char *file, unsigned int line); - Packet *AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line); - - /// This is used to return a number to the user when they call Send identifying the message - /// This number will be returned back with ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS and is only returned - /// with the reliability types that contain RECEIPT in the name - SimpleMutex sendReceiptSerialMutex; - uint32_t sendReceiptSerial; - void ResetSendReceipt(void); - void OnConnectedPong(RakNet::Time sendPingTime, RakNet::Time sendPongTime, RemoteSystemStruct *remoteSystem); - void CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet); - -#if LIBCAT_SECURITY==1 - // Encryption and security - bool _using_security, _require_client_public_key; - char my_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; - cat::ServerEasyHandshake *_server_handshake; - cat::CookieJar *_cookie_jar; - bool InitializeClientSecurity(RequestedConnectionStruct *rcs, const char *public_key); -#endif - - - - - -} -// #if defined(SN_TARGET_PSP2) -// __attribute__((aligned(8))) -// #endif -; - -} // namespace RakNet - -#endif +/// \file +/// \brief Declares RakPeer class. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +// TODO - RakNet 4 - Add network simulator +// TODO - RakNet 4 - Enable disabling flow control per connections + +#ifndef __RAK_PEER_H +#define __RAK_PEER_H + +#include "ReliabilityLayer.h" +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "SingleProducerConsumer.h" +#include "SimpleMutex.h" +#include "DS_OrderedList.h" +#include "Export.h" +#include "RakString.h" +#include "RakThread.h" +#include "RakNetSocket.h" +#include "RakNetSmartPtr.h" +#include "DS_ThreadsafeAllocatingQueue.h" +#include "SignaledEvent.h" +#include "NativeFeatureIncludes.h" +#include "SecureHandshake.h" +#include "LocklessTypes.h" + +namespace RakNet { +/// Forward declarations +class HuffmanEncodingTree; +class PluginInterface2; + +// Sucks but this struct has to be outside the class. Inside and DevCPP won't let you refer to the struct as RakPeer::RemoteSystemIndex while GCC +// forces you to do RakPeer::RemoteSystemIndex +struct RemoteSystemIndex{unsigned index; RemoteSystemIndex *next;}; +//int RAK_DLL_EXPORT SystemAddressAndIndexComp( const SystemAddress &key, const RemoteSystemIndex &data ); // GCC requires RakPeer::RemoteSystemIndex or it won't compile + +///\brief Main interface for network communications. +/// \details It implements most of RakNet's functionality and is the primary interface for RakNet. +/// +/// Inherits RakPeerInterface. +/// +/// See the individual functions for what the class can do. +/// +class RAK_DLL_EXPORT RakPeer : public RakPeerInterface +{ +public: + ///Constructor + RakPeer(); + + ///Destructor + virtual ~RakPeer(); + + // --------------------------------------------------------------------------------------------Major Low Level Functions - Functions needed by most users-------------------------------------------------------------------------------------------- + /// \brief Starts the network threads and opens the listen port. + /// \details You must call this before calling Connect(). + /// \pre On the PS3, call Startup() after Client_Login() + /// \note Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). + /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections. + /// \param[in] maxConnections Maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so that the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.A hybrid would set it to the sum of both types of connections. + /// \param[in] localPort The port to listen for connections on. On linux the system may be set up so thast ports under 1024 are restricted for everything but the root user. Use a higher port for maximum compatibility. + /// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); However, on the XBOX be sure to use IPPROTO_VDP + /// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + /// \return RAKNET_STARTED on success, otherwise appropriate failure enumeration. + StartupResult Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority=-99999 ); + + /// If you accept connections, you must call this or else security will not be enabled for incoming connections. + /// This feature requires more round trips, bandwidth, and CPU time for the connection handshake + /// x64 builds require under 25% of the CPU time of other builds + /// See the Encryption sample for example usage + /// \pre Must be called while offline + /// \pre LIBCAT_SECURITY must be defined to 1 in NativeFeatureIncludes.h for this function to have any effect + /// \param[in] publicKey A pointer to the public key for accepting new connections + /// \param[in] privateKey A pointer to the private key for accepting new connections + /// \param[in] bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection + bool InitializeSecurity( const char *publicKey, const char *privateKey, bool bRequireClientKey = false ); + + /// Disables security for incoming connections. + /// \note Must be called while offline + void DisableSecurity( void ); + + /// \brief This is useful if you have a fixed-address internal server behind a LAN. + /// + /// Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. + /// \note If secure connections are on, do not use secure connections for a specific IP address. + /// \param[in] ip IP address to add. * wildcards are supported. + void AddToSecurityExceptionList(const char *ip); + + /// \brief Remove a specific connection previously added via AddToSecurityExceptionList. + /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. + void RemoveFromSecurityExceptionList(const char *ip); + + /// \brief Checks to see if a given IP is in the security exception list. + /// \param[in] IP address to check. + /// \return True if the IP address is found in security exception list, else returns false. + bool IsInSecurityExceptionList(const char *ip); + + /// \brief Sets the maximum number of incoming connections allowed. + /// \details If the number of incoming connections is less than the number of players currently connected, + /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, + /// it will be reduced to the maximum number of peers allowed. + /// + /// Defaults to 0, meaning by default, nobody can connect to you + /// \param[in] numberAllowed Maximum number of incoming connections allowed. + void SetMaximumIncomingConnections( unsigned short numberAllowed ); + + /// \brief Returns the value passed to SetMaximumIncomingConnections(). + /// \return Maximum number of incoming connections, which is always <= maxConnections + unsigned short GetMaximumIncomingConnections( void ) const; + + /// \brief Returns how many open connections exist at this time. + /// \return Number of open connections. + unsigned short NumberOfConnections(void) const; + + /// \brief Sets the password for the incoming connections. + /// \details The password must match in the call to Connect (defaults to none). + /// Pass 0 to passwordData to specify no password. + /// This is a way to set a low level password for all incoming connections. To selectively reject connections, implement your own scheme using CloseConnection() to remove unwanted connections. + /// \param[in] passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. Specify 0 for no password data + /// \param[in] passwordDataLength The length in bytes of passwordData + void SetIncomingPassword( const char* passwordData, int passwordDataLength ); + + /// \brief Gets the password passed to SetIncomingPassword + /// \param[out] passwordData Should point to a block large enough to hold the password data you passed to SetIncomingPassword() + /// \param[in,out] passwordDataLength Maximum size of the passwordData array. Modified to hold the number of bytes actually written. + void GetIncomingPassword( char* passwordData, int *passwordDataLength ); + + /// \brief Connect to the specified host (ip or domain name) and server port. + /// \details Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. + /// Calling both acts as a true peer. + /// + /// This is a non-blocking connection. + /// + /// The connection is successful when GetConnectionState() returns IS_CONNECTED or Receive() gets a message with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. + /// If the connection is not successful, such as a rejected connection or no response then neither of these things will happen. + /// \pre Requires that you first call Initialize. + /// \param[in] host Either a dotted IP address or a domain name. + /// \param[in] remotePort Port to connect to on the remote machine. + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData. + /// \param[in] publicKey The public key the server is using. If 0, the server is not using security. If non-zero, the publicKeyMode member determines how to connect + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to determine the one to send on. + /// \param[in] sendConnectionAttemptCount Number of datagrams to send to the other system to try to connect. + /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. + /// \param[in] timeoutTime Time to elapse before dropping the connection if a reliable message could not be sent. 0 to use the default value from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); + /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. + /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! + /// \note It is possible to immediately get back ID_CONNECTION_ATTEMPT_FAILED if you exceed the maxConnections parameter passed to Startup(). This could happen if you call CloseConnection() with sendDisconnectionNotificaiton true, then immediately call Connect() before the connection has closed. + ConnectionAttemptResult Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey=0, unsigned connectionSocketIndex=0, unsigned sendConnectionAttemptCount=6, unsigned timeBetweenSendConnectionAttemptsMS=1000, RakNet::TimeMS timeoutTime=0 ); + + /// \brief Connect to the specified host (ip or domain name) and server port. + /// \param[in] host Either a dotted IP address or a domain name. + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData. + /// \param[in] socket A bound socket returned by another instance of RakPeerInterface. + /// \param[in] sendConnectionAttemptCount Number of datagrams to send to the other system to try to connect. + /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned.. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. + /// \param[in] timeoutTime Time to elapse before dropping the connection if a reliable message could not be sent. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); + /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. + /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! + virtual ConnectionAttemptResult ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey=0, unsigned sendConnectionAttemptCount=6, unsigned timeBetweenSendConnectionAttemptsMS=1000, RakNet::TimeMS timeoutTime=0); + + /* /// \brief Connect to the specified network ID (Platform specific console function) + /// \details Does built-in NAT traversal + /// \param[in] networkServiceId Network ID structure for the online service + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword(). This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData. + //bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength );*/ + + /// \brief Stops the network threads and closes all connections. + /// \param[in] blockDuration Wait time(milli seconds) for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. + /// \param[in] orderingChannel Channel on which ID_DISCONNECTION_NOTIFICATION will be sent, if blockDuration > 0. + /// \param[in] disconnectionNotificationPriority Priority of sending ID_DISCONNECTION_NOTIFICATION. + /// If set to 0, the disconnection notification won't be sent. + void Shutdown( unsigned int blockDuration, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY ); + + /// \brief Returns true if the network thread is running. + /// \return True if the network thread is running, False otherwise + bool IsActive( void ) const; + + /// \brief Fills the array remoteSystems with the SystemAddress of all the systems we are connected to. + /// \param[out] remoteSystems An array of SystemAddress structures, to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to get the number of systems we are connected to. + /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array. + bool GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const; + + /// Returns the next uint32_t that Send() will return + /// \note If using RakPeer from multiple threads, this may not be accurate for your thread. Use IncrementNextSendReceipt() in that case. + /// \return The next uint32_t that Send() or SendList will return + virtual uint32_t GetNextSendReceipt(void); + + /// Returns the next uint32_t that Send() will return, and increments the value by one + /// \note If using RakPeer from multiple threads, pass this to forceReceipt in the send function + /// \return The next uint32_t that Send() or SendList will return + virtual uint32_t IncrementNextSendReceipt(void); + + /// \brief Sends a block of data to the specified system that you are connected to. + /// \note This function only works while the connected. + /// \note The first byte should be a message identifier starting at ID_USER_PACKET_ENUM. + /// \param[in] data Block of data to send. + /// \param[in] length Size in bytes of the data to send. + /// \param[in] priority Priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliably to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, the channel to order these on. Messages are only ordered relative to other messages on the same stream. + /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + uint32_t Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); + + /// \brief "Send" to yourself rather than a remote system. + /// \details The message will be processed through the plugins and returned to the game as usual. + /// This function works anytime + /// \note The first byte should be a message identifier starting at ID_USER_PACKET_ENUM + /// \param[in] data Block of data to send. + /// \param[in] length Size in bytes of the data to send. + void SendLoopback( const char *data, const int length ); + + /// \brief Sends a block of data to the specified system that you are connected to. + /// + /// Same as the above version, but takes a BitStream as input. + /// \param[in] bitStream Bitstream to send + /// \param[in] priority Priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliably to send this data. See PacketPriority.h + /// \param[in] orderingChannel Channel to order the messages on, when using ordered or sequenced messages. Messages are only ordered relative to other messages on the same stream. + /// \param[in] systemIdentifier System Address or RakNetGUID to send this packet to, or in the case of broadcasting, the address not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none. + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. + uint32_t Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); + + /// \brief Sends multiple blocks of data, concatenating them automatically. + /// + /// This is equivalent to: + /// RakNet::BitStream bs; + /// bs.WriteAlignedBytes(block1, blockLength1); + /// bs.WriteAlignedBytes(block2, blockLength2); + /// bs.WriteAlignedBytes(block3, blockLength3); + /// Send(&bs, ...) + /// + /// This function only works when connected. + /// \param[in] data An array of pointers to blocks of data + /// \param[in] lengths An array of integers indicating the length of each block of data + /// \param[in] numParameters Length of the arrays data and lengths + /// \param[in] priority Priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliably to send this data. See PacketPriority.h + /// \param[in] orderingChannel Channel to order the messages on, when using ordered or sequenced messages. Messages are only ordered relative to other messages on the same stream. + /// \param[in] systemIdentifier System Address or RakNetGUID to send this packet to, or in the case of broadcasting, the address not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none. + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + uint32_t SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 ); + + /// \brief Gets a message from the incoming message queue. + /// \details Use DeallocatePacket() to deallocate the message after you are done with it. + /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. + /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. + /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. + /// \sa RakNetTypes.h contains struct Packet. + Packet* Receive( void ); + + /// \brief Call this to deallocate a message returned by Receive() when you are done handling it. + /// \param[in] packet Message to deallocate. + void DeallocatePacket( Packet *packet ); + + /// \brief Return the total number of connections we are allowed. + /// \return Total number of connections allowed. + unsigned short GetMaximumNumberOfPeers( void ) const; + + // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- + /// \brief Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). + /// \details This method closes the connection irrespective of who initiated the connection. + /// \param[in] target Which system to close the connection to. + /// \param[in] sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. + /// \param[in] channel Which ordering channel to send the disconnection notification on, if any + /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. + void CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY ); + + /// \brief Cancel a pending connection attempt. + /// \details If we are already connected, the connection stays open + /// \param[in] target Target system to cancel. + void CancelConnectionAttempt( const SystemAddress target ); + /// Returns if a system is connected, disconnected, connecting in progress, or various other states + /// \param[in] systemIdentifier The system we are referring to + /// \note This locks a mutex, do not call too frequently during connection attempts or the attempt will take longer and possibly even timeout + /// \return What state the remote system is in + ConnectionState GetConnectionState(const AddressOrGUID systemIdentifier); + + /// \brief Given \a systemAddress, returns its index into remoteSystemList. + /// \details Values range from 0 to the maximum number of players allowed - 1. + /// This includes systems which were formerly connected, but are now not connected. + /// \param[in] systemAddress The SystemAddress we are referring to + /// \return The index of this SystemAddress or -1 on system not found. + int GetIndexFromSystemAddress( const SystemAddress systemAddress ) const; + + /// \brief Given \a index into remoteSystemList, will return a SystemAddress. + /// This function is only useful for looping through all systems. + /// + /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. + /// \return The SystemAddress structure corresponding to \a index in remoteSystemList. + SystemAddress GetSystemAddressFromIndex( int index ); + + /// \brief Same as GetSystemAddressFromIndex but returns RakNetGUID + /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. + /// \return The RakNetGUID + RakNetGUID GetGUIDFromIndex( int index ); + + /// \brief Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient + /// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system + /// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the \a addresses list will match the size of the \a guids list. + /// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. + void GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const; + + /// \brief Bans an IP from connecting. + /// \details Banned IPs persist between connections but are not saved on shutdown nor loaded on startup. + /// \param[in] IP Dotted IP address. You can use * for a wildcard address, such as 128.0.0. * will ban all IP addresses starting with 128.0.0. + /// \param[in] milliseconds Gives time in milli seconds for a temporary ban of the IP address. Use 0 for a permanent ban. + void AddToBanList( const char *IP, RakNet::TimeMS milliseconds=0 ); + + /// \brief Allows a previously banned IP to connect. + /// param[in] Dotted IP address. You can use * as a wildcard. An IP such as 128.0.0.* will ban all IP addresses starting with 128.0.0. + void RemoveFromBanList( const char *IP ); + + /// \brief Allows all previously banned IPs to connect. + void ClearBanList( void ); + + /// \brief Returns true or false indicating if a particular IP is banned. + /// \param[in] IP Dotted IP address. + /// \return True if IP matches any IPs in the ban list, accounting for any wildcards. False otherwise. + bool IsBanned( const char *IP ); + + /// \brief Enable or disable allowing frequent connections from the same IP adderss + /// \details This is a security measure which is disabled by default, but can be set to true to prevent attackers from using up all connection slots. + /// \param[in] b True to limit connections from the same ip to at most 1 per 100 milliseconds. + void SetLimitIPConnectionFrequency(bool b); + + // --------------------------------------------------------------------------------------------Pinging Functions - Functions dealing with the automatic ping mechanism-------------------------------------------------------------------------------------------- + /// Send a ping to the specified connected system. + /// \pre The sender and recipient must already be started via a successful call to Startup() + /// \param[in] target Which system to ping + void Ping( const SystemAddress target ); + + /// \brief Send a ping to the specified unconnected system. + /// \details The remote system, if it is Initialized, will respond with ID_PONG followed by sizeof(RakNet::TimeMS) containing the system time the ping was sent. Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h + /// System should reply with ID_PONG if it is active + /// \param[in] host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return true on success, false on failure (unknown hostname) + bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 ); + + /// \brief Returns the average of all ping times read for the specific system or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The ping time for this system, or -1 + int GetAveragePing( const AddressOrGUID systemIdentifier ); + + /// \brief Returns the last ping time read for the specific system or -1 if none read yet. + /// \param[in] systemAddress Which system we are referring to + /// \return The last ping time for this system, or -1. + int GetLastPing( const AddressOrGUID systemIdentifier ) const; + + /// \brief Returns the lowest ping time read or -1 if none read yet. + /// \param[in] systemAddress Which system we are referring to + /// \return The lowest ping time for this system, or -1. + int GetLowestPing( const AddressOrGUID systemIdentifier ) const; + + /// Ping the remote systems every so often, or not. Can be called anytime. + /// By default this is true. Recommended to leave on, because congestion control uses it to determine how often to resend lost packets. + /// It would be true by default to prevent timestamp drift, since in the event of a clock spike, the timestamp deltas would no longer be accurate + /// \param[in] doPing True to start occasional pings. False to stop them. + void SetOccasionalPing( bool doPing ); + + // --------------------------------------------------------------------------------------------Static Data Functions - Functions dealing with API defined synchronized memory-------------------------------------------------------------------------------------------- + /// \brief Sets the data to send along with a LAN server discovery or offline ping reply. + /// \param[in] data Block of data to send, or 0 for none + /// \param[in] length Length of the data in bytes, or 0 for none + /// \note \a length should be under 400 bytes, as a security measure against flood attacks + /// \sa Ping.cpp + void SetOfflinePingResponse( const char *data, const unsigned int length ); + + /// \brief Returns pointers to a copy of the \a data passed to SetOfflinePingResponse. + /// \param[out] data A pointer to a copy of the data passed to SetOfflinePingResponse() + /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() + /// \sa SetOfflinePingResponse + void GetOfflinePingResponse( char **data, unsigned int *length ); + + //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- + /// \brief Returns the unique address identifier that represents you or another system on the the network and is based on your local IP / port. + /// \note Not supported by the XBOX + /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address + /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) + /// \return Identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy + SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const; + + /// \brief Returns the unique address identifier that represents the target on the the network and is based on the target's external IP / port. + /// \param[in] target The SystemAddress of the remote system. Usually the same for all systems, unless you have two or more network cards. + SystemAddress GetExternalID( const SystemAddress target ) const; + + /// Return my own GUID + const RakNetGUID GetMyGUID(void) const; + + /// Return the address bound to a socket at the specified index + SystemAddress GetMyBoundAddress(const int socketIndex=0); + + /// \brief Given a connected system address, this method gives the unique GUID representing that instance of RakPeer. + /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different. + /// Complexity is O(log2(n)). + /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID + /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID + /// \param[in] input The system address of the target system we are connected to. + const RakNetGUID& GetGuidFromSystemAddress( const SystemAddress input ) const; + + /// \brief Gives the system address of a connected system, given its GUID. + /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different. + /// Currently O(log(n)), but this may be improved in the future + /// If \a input is UNASSIGNED_RAKNET_GUID, UNASSIGNED_SYSTEM_ADDRESS is returned. + /// \param[in] input The RakNetGUID of the target system. + SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const; + + /// Given the SystemAddress of a connected system, get the public key they provided as an identity + /// Returns false if system address was not found or client public key is not known + /// \param[in] input The RakNetGUID of the system + /// \param[in] client_public_key The connected client's public key is copied to this address. Buffer must be cat::EasyHandshake::PUBLIC_KEY_BYTES bytes in length. + bool GetClientPublicKeyFromSystemAddress( const SystemAddress input, char *client_public_key ) const; + + /// \brief Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. + + /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. + /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. + /// Do not set different values for different computers that are connected to each other, or you won't be able to reconnect after ID_CONNECTION_LOST + /// \param[in] timeMS Time, in MS + /// \param[in] target SystemAddress structure of the target system. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. + void SetTimeoutTime( RakNet::TimeMS timeMS, const SystemAddress target ); + + /// \brief Returns the Timeout time for the given system. + /// \param[in] target Target system to get the TimeoutTime for. Pass UNASSIGNED_SYSTEM_ADDRESS to get the default value. + /// \return Timeout time for a given system. + RakNet::TimeMS GetTimeoutTime( const SystemAddress target ); + + /// \brief Returns the current MTU size + /// \param[in] target Which system to get MTU for. UNASSIGNED_SYSTEM_ADDRESS to get the default + /// \return The current MTU size of the target system. + int GetMTUSize( const SystemAddress target ) const; + + /// \brief Returns the number of IP addresses this system has internally. + /// \details Get the actual addresses from GetLocalIP() + unsigned GetNumberOfAddresses( void ); + + /// Returns an IP address at index 0 to GetNumberOfAddresses-1 in ipList array. + /// \param[in] index index into the list of IP addresses + /// \return The local IP address at this index + const char* GetLocalIP( unsigned int index ); + + /// Is this a local IP? + /// Checks if this ip is in the ipList array. + /// \param[in] An IP address to check, excluding the port. + /// \return True if this is one of the IP addresses returned by GetLocalIP + bool IsLocalIP( const char *ip ); + + /// \brief Allow or disallow connection responses from any IP. + /// \details Normally this should be false, but may be necessary when connecting to servers with multiple IP addresses. + /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections. + void AllowConnectionResponseIPMigration( bool allow ); + + /// \brief Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. + /// This will send our external IP outside the LAN along with some user data to the remote system. + /// \pre The sender and recipient must already be started via a successful call to Initialize + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] data Optional data to append to the packet. + /// \param[in] dataLength Length of data in bytes. Use 0 if no data. + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return False if IsActive()==false or the host is unresolvable. True otherwise. + bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 ); + + /// \brief Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. + /// \details ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived. + /// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. + /// Defaults to 0 (never return this notification). + /// \param[in] interval How many messages to use as an interval before a download progress notification is returned. + void SetSplitMessageProgressInterval(int interval); + + /// \brief Returns what was passed to SetSplitMessageProgressInterval(). + /// \return Number of messages to be recieved before a download progress notification is returned. Default to 0. + int GetSplitMessageProgressInterval(void) const; + + /// \brief Set how long to wait before giving up on sending an unreliable message. + /// Useful if the network is clogged up. + /// Set to 0 or less to never timeout. Defaults to 0. + /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. + void SetUnreliableTimeout(RakNet::TimeMS timeoutMS); + + /// \brief Send a message to a host, with the IP socket option TTL set to 3. + /// \details This message will not reach the host, but will open the router. + /// \param[in] host The address of the remote host in dotted notation. + /// \param[in] remotePort The port number to send to. + /// \param[in] ttl Max hops of datagram, set to 3 + /// \param[in] connectionSocketIndex userConnectionSocketIndex. + /// \remarks Used for NAT-Punchthrough + void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 ); + + // -------------------------------------------------------------------------------------------- Plugin Functions-------------------------------------------------------------------------------------------- + /// \brief Attaches a Plugin interface to an instance of the base class (RakPeer or PacketizedTCP) to run code automatically on message receipt in the Receive call. + /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. + /// \param[in] messageHandler Pointer to the plugin to attach. + void AttachPlugin( PluginInterface2 *plugin ); + + /// \brief Detaches a Plugin interface from the instance of the base class (RakPeer or PacketizedTCP) it is attached to. + /// \details This method disables the plugin code from running automatically on base class's updates or message receipt. + /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. + /// \param[in] messageHandler Pointer to a plugin to detach. + void DetachPlugin( PluginInterface2 *messageHandler ); + + // --------------------------------------------------------------------------------------------Miscellaneous Functions-------------------------------------------------------------------------------------------- + /// \brief Puts a message back in the receive queue in case you don't want to deal with it immediately. + /// \param[in] packet The pointer to the packet you want to push back. + /// \param[in] pushAtHead True to push the packet at the start of the queue so that the next receive call returns it. False to push it at the end of the queue. + /// \note Setting pushAtHead to false end makes the packets out of order. + void PushBackPacket( Packet *packet, bool pushAtHead ); + + /// \internal + /// \brief For a given system identified by \a guid, change the SystemAddress to send to. + /// \param[in] guid The connection we are referring to + /// \param[in] systemAddress The new address to send to + void ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress); + + /// \brief Returns a packet for you to write to if you want to create a Packet for some reason. + /// You can add it to the receive buffer with PushBackPacket + /// \param[in] dataSize How many bytes to allocate for the buffer + /// \return A packet. + Packet* AllocatePacket(unsigned dataSize); + + /// \brief Get the socket used with a particular active connection. + /// The smart pointer reference counts the RakNetSocket object, so the socket will remain active as long as the smart pointer does, even if RakNet were to shutdown or close the connection. + /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. + /// \param[in] target Which system. + /// \return A smart pointer object containing the socket information about the target. Be sure to check IsNull() which is returned if the update thread is unresponsive, shutting down, or if this system is not connected. + virtual RakNetSmartPtr GetSocket( const SystemAddress target ); + + /// \brief Gets all sockets in use. + /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. + /// \param[out] sockets List of RakNetSocket structures in use. Sockets will not be closed until \a sockets goes out of scope + virtual void GetSockets( DataStructures::List > &sockets ); + virtual void ReleaseSockets( DataStructures::List > &sockets ); + + /// \internal + virtual void WriteOutOfBandHeader(RakNet::BitStream *bitStream); + + /// If you need code to run in the same thread as RakNet's update thread, this function can be used for that + /// \param[in] _userUpdateThreadPtr C callback function + /// \param[in] _userUpdateThreadData Passed to C callback function + virtual void SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData); + + // --------------------------------------------------------------------------------------------Network Simulator Functions-------------------------------------------------------------------------------------------- + /// Adds simulated ping and packet loss to the outgoing data flow. + /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and packetloss value on each. + /// You can exclude network simulator code with the _RELEASE #define to decrease code size + /// \deprecated Use http://www.jenkinssoftware.com/forum/index.php?topic=1671.0 instead. + /// \note Doesn't work past version 3.6201 + /// \param[in] packetloss Chance to lose a packet. Ranges from 0 to 1. + /// \param[in] minExtraPing The minimum time to delay sends. + /// \param[in] extraPingVariance The additional random time to delay sends. + virtual void ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance); + + /// Limits how much outgoing bandwidth can be sent per-connection. + /// This limit does not apply to the sum of all connections! + /// Exceeding the limit queues up outgoing traffic + /// \param[in] maxBitsPerSecond Maximum bits per second to send. Use 0 for unlimited (default). Once set, it takes effect immedately and persists until called again. + virtual void SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ); + + /// Returns if you previously called ApplyNetworkSimulator + /// \return If you previously called ApplyNetworkSimulator + virtual bool IsNetworkSimulatorActive( void ); + + // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- + + /// \brief Returns a structure containing a large set of network statistics for the specified system. + /// You can map this data to a string using the C style StatisticsToString() function + /// \param[in] systemAddress Which connected system to get statistics for. + /// \param[in] rns If you supply this structure,the network statistics will be written to it. Otherwise the method uses a static struct to write the data, which is not threadsafe. + /// \return 0 if the specified system can't be found. Otherwise a pointer to the struct containing the specified system's network statistics. + /// \sa RakNetStatistics.h + RakNetStatistics * GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 ); + /// \brief Returns the network statistics of the system at the given index in the remoteSystemList. + /// \return True if the index is less than the maximum number of peers allowed and the system is active. False otherwise. + bool GetStatistics( const int index, RakNetStatistics *rns ); + + /// \Returns how many messages are waiting when you call Receive() + virtual unsigned int GetReceiveBufferSize(void); + + // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- + + /// \internal + bool SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 ); + + // static Packet *AllocPacket(unsigned dataSize, const char *file, unsigned int line); + + /// \internal + /// \brief Holds the clock differences between systems, along with the ping + struct PingAndClockDifferential + { + unsigned short pingTime; + RakNet::Time clockDifferential; + }; + + /// \internal + /// \brief All the information representing a connected system + struct RemoteSystemStruct + { + bool isActive; // Is this structure in use? + SystemAddress systemAddress; /// Their external IP on the internet + SystemAddress myExternalSystemAddress; /// Your external IP on the internet, from their perspective + SystemAddress theirInternalSystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; /// Their internal IP, behind the LAN + ReliabilityLayer reliabilityLayer; /// The reliability layer associated with this player + bool weInitiatedTheConnection; /// True if we started this connection via Connect. False if someone else connected to us. + PingAndClockDifferential pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE ]; /// last x ping times and calculated clock differentials with it + RakNet::Time pingAndClockDifferentialWriteIndex; /// The index we are writing into the pingAndClockDifferential circular buffer + unsigned short lowestPing; ///The lowest ping value encountered + RakNet::Time nextPingTime; /// When to next ping this player + RakNet::Time lastReliableSend; /// When did the last reliable send occur. Reliable sends must occur at least once every timeoutTime/2 units to notice disconnects + RakNet::Time connectionTime; /// connection time, if active. +// int connectionSocketIndex; // index into connectionSockets to send back on. + RakNetGUID guid; + int MTUSize; + // Reference counted socket to send back on + RakNetSmartPtr rakNetSocket; + SystemIndex remoteSystemIndex; + +#if LIBCAT_SECURITY==1 + // Cached answer used internally by RakPeer to prevent DoS attacks based on the connexion handshake + char answer[cat::EasyHandshake::ANSWER_BYTES]; + + // If the server has bRequireClientKey = true, then this is set to the validated public key of the connected client + // Valid after connectMode reaches HANDLING_CONNECTION_REQUEST + char client_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; +#endif + + enum ConnectMode {NO_ACTION, DISCONNECT_ASAP, DISCONNECT_ASAP_SILENTLY, DISCONNECT_ON_NO_ACK, REQUESTED_CONNECTION, HANDLING_CONNECTION_REQUEST, UNVERIFIED_SENDER, CONNECTED} connectMode; + }; + +protected: + + friend RAK_THREAD_DECLARATION(UpdateNetworkLoop); + friend RAK_THREAD_DECLARATION(RecvFromLoop); + friend RAK_THREAD_DECLARATION(UDTConnect); + + friend bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, bool *isOfflineMessage, RakNet::TimeUS timeRead ); + friend void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead, BitStream &updateBitStream ); + friend void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead, BitStream &updateBitStream ); + + int GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const; + int GetIndexFromGuid( const RakNetGUID guid ); + + //void RemoveFromRequestedConnectionsList( const SystemAddress systemAddress ); + // Two versions needed because some buggy compilers strip the last parameter if unused, and crashes + ConnectionAttemptResult SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime, RakNetSmartPtr socket ); + ConnectionAttemptResult SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, RakNet::TimeMS timeoutTime ); + ///Get the reliability layer associated with a systemAddress. + /// \param[in] systemAddress The player identifier + /// \return 0 if none + RemoteSystemStruct *GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const; + RakPeer::RemoteSystemStruct *GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const; + void ValidateRemoteSystemLookup(void) const; + RemoteSystemStruct *GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const; + ///Parse out a connection request packet + void ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize); + void OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, RakNet::Time incomingTimestamp ); + ///Send a reliable disconnect packet to this player and disconnect them when it is delivered + void NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ); + ///Returns how many remote systems initiated a connection to us + unsigned short GetNumberOfRemoteInitiatedConnections( void ) const; + /// \brief Get a free remote system from the list and assign our systemAddress to it. + /// \note Should only be called from the update thread - not the user thread. + /// \param[in] systemAddress systemAddress to be assigned + /// \param[in] connectionMode connection mode of the RemoteSystem. + /// \param[in] rakNetSocket + /// \param[in] thisIPConnectedRecently Is this IP connected recently? set to False; + /// \param[in] bindingAddress Address to be binded with the remote system + /// \param[in] incomingMTU MTU for the remote system + RemoteSystemStruct * AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSmartPtr incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ); + /// \brief Adjust the timestamp of the incoming packet to be relative to this system. + /// \param[in] data Data in the incoming packet. + /// \param[in] systemAddress Sender of the incoming packet. + void ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const; + /// Get the most accurate clock differential for a certain player. + /// \param[in] systemAddress The player with whose clock the time difference is calculated. + /// \returns The clock differential for a certain player. + RakNet::Time GetBestClockDifferential( const SystemAddress systemAddress ) const; + + bool IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const; + SystemAddress GetLoopbackAddress(void) const; + + ///Set this to true to terminate the Peer thread execution + volatile bool endThreads; + ///true if the peer thread is active. + volatile bool isMainLoopThreadActive; + + RakNet::LocklessUint32_t isRecvFromLoopThreadActive; + + + bool occasionalPing; /// Do we occasionally ping the other systems?*/ + ///Store the maximum number of peers allowed to connect + unsigned short maximumNumberOfPeers; + //05/02/06 Just using maximumNumberOfPeers instead + ///Store the maximum number of peers able to connect, including reserved connection slots for pings, etc. + //unsigned short remoteSystemListSize; + ///Store the maximum incoming connection allowed + unsigned short maximumIncomingConnections; + RakNet::BitStream offlinePingResponse; + ///Local Player ID + // SystemAddress mySystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; + char incomingPassword[256]; + unsigned char incomingPasswordLength; + + /// This is an array of pointers to RemoteSystemStruct + /// This allows us to preallocate the list when starting, so we don't have to allocate or delete at runtime. + /// Another benefit is that is lets us add and remove active players simply by setting systemAddress + /// and moving elements in the list by copying pointers variables without affecting running threads, even if they are in the reliability layer + RemoteSystemStruct* remoteSystemList; + /// activeSystemList holds a list of pointers and is preallocated to be the same size as remoteSystemList. It is updated only by the network thread, but read by both threads + /// When the isActive member of RemoteSystemStruct is set to true or false, that system is added to this list of pointers + /// Threadsafe because RemoteSystemStruct is preallocated, and the list is only added to, not removed from + RemoteSystemStruct** activeSystemList; + unsigned int activeSystemListSize; + + // Use a hash, with binaryAddress plus port mod length as the index + RemoteSystemIndex **remoteSystemLookup; + unsigned int RemoteSystemLookupHashIndex(const SystemAddress &sa) const; + void ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex); + void DereferenceRemoteSystem(const SystemAddress &sa); + RemoteSystemStruct* GetRemoteSystem(const SystemAddress &sa) const; + unsigned int GetRemoteSystemIndex(const SystemAddress &sa) const; + void ClearRemoteSystemLookup(void); + DataStructures::MemoryPool remoteSystemIndexPool; + + void AddToActiveSystemList(unsigned int remoteSystemListIndex); + void RemoveFromActiveSystemList(const SystemAddress &sa); + +// unsigned int LookupIndexUsingHashIndex(const SystemAddress &sa) const; +// unsigned int RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const; +// unsigned int FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const; + + enum + { + // Only put these mutexes in user thread functions! + requestedConnectionList_Mutex, + offlinePingResponse_Mutex, + NUMBER_OF_RAKPEER_MUTEXES + }; + SimpleMutex rakPeerMutexes[ NUMBER_OF_RAKPEER_MUTEXES ]; + ///RunUpdateCycle is not thread safe but we don't need to mutex calls. Just skip calls if it is running already + + bool updateCycleIsRunning; + ///The list of people we have tried to connect to recently + + //DataStructures::Queue requestedConnectionsList; + ///Data that both the client and the server needs + + unsigned int bytesSentPerSecond, bytesReceivedPerSecond; + // bool isSocketLayerBlocking; + // bool continualPing,isRecvfromThreadActive,isMainLoopThreadActive, endThreads, isSocketLayerBlocking; + unsigned int validationInteger; + SimpleMutex incomingQueueMutex, banListMutex; //,synchronizedMemoryQueueMutex, automaticVariableSynchronizationMutex; + //DataStructures::Queue incomingpacketSingleProducerConsumer; //, synchronizedMemorypacketSingleProducerConsumer; + // BitStream enumerationData; + + struct BanStruct + { + char *IP; + RakNet::TimeMS timeout; // 0 for none + }; + + struct RequestedConnectionStruct + { + SystemAddress systemAddress; + RakNet::Time nextRequestTime; + unsigned char requestsMade; + char *data; + unsigned short dataLength; + char outgoingPassword[256]; + unsigned char outgoingPasswordLength; + unsigned socketIndex; + unsigned int extraData; + unsigned sendConnectionAttemptCount; + unsigned timeBetweenSendConnectionAttemptsMS; + RakNet::TimeMS timeoutTime; + PublicKeyMode publicKeyMode; + RakNetSmartPtr socket; + enum {CONNECT=1, /*PING=2, PING_OPEN_CONNECTIONS=4,*/ /*ADVERTISE_SYSTEM=2*/} actionToTake; + +#if LIBCAT_SECURITY==1 + char handshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; + cat::ClientEasyHandshake *client_handshake; + char remote_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; +// char remote_challenge[cat::EasyHandshake::CHALLENGE_BYTES]; + // char random[16]; +#endif + }; +#if LIBCAT_SECURITY==1 + bool GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey); +#endif + + //DataStructures::List* > automaticVariableSynchronizationList; + DataStructures::List banList; + // Threadsafe, and not thread safe + DataStructures::List pluginListTS, pluginListNTS; + + DataStructures::Queue requestedConnectionQueue; + SimpleMutex requestedConnectionQueueMutex; + + bool RunUpdateCycle( RakNet::TimeUS timeNS, RakNet::Time timeMS, BitStream &updateBitStream ); + // void RunMutexedUpdateCycle(void); + + struct BufferedCommandStruct + { + BitSize_t numberOfBitsToSend; + PacketPriority priority; + PacketReliability reliability; + char orderingChannel; + AddressOrGUID systemIdentifier; + bool broadcast; + RemoteSystemStruct::ConnectMode connectionMode; + NetworkID networkID; + bool blockingCommand; // Only used for RPC + char *data; + bool haveRakNetCloseSocket; + unsigned connectionSocketIndex; + unsigned short remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions; + SOCKET socket; + unsigned short port; + uint32_t receipt; + enum {BCS_SEND, BCS_CLOSE_CONNECTION, BCS_GET_SOCKET, BCS_CHANGE_SYSTEM_ADDRESS,/* BCS_USE_USER_SOCKET, BCS_REBIND_SOCKET_ADDRESS, BCS_RPC, BCS_RPC_SHIFT,*/ BCS_DO_NOTHING} command; + }; + + // Single producer single consumer queue using a linked list + //BufferedCommandStruct* bufferedCommandReadIndex, bufferedCommandWriteIndex; + + DataStructures::ThreadsafeAllocatingQueue bufferedCommands; + + + // Constructor not called! + struct RecvFromStruct + { + + + + char data[MAXIMUM_MTU_SIZE]; + + int bytesRead; + SystemAddress systemAddress; + RakNet::TimeUS timeRead; + SOCKET s; + unsigned short remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions; + }; + + + DataStructures::ThreadsafeAllocatingQueue bufferedPackets; + + + struct SocketQueryOutput + { + SocketQueryOutput() {} + ~SocketQueryOutput() {} + DataStructures::List > sockets; + }; + + DataStructures::ThreadsafeAllocatingQueue socketQueryOutput; + + + bool AllowIncomingConnections(void) const; + + void PingInternal( const SystemAddress target, bool performImmediate, PacketReliability reliability ); + // This stores the user send calls to be handled by the update thread. This way we don't have thread contention over systemAddresss + void CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, PacketPriority disconnectionNotificationPriority ); + void SendBuffered( const char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ); + void SendBufferedList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ); + bool SendImmediate( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, RakNet::TimeUS currentTime, uint32_t receipt ); + //bool HandleBufferedRPC(BufferedCommandStruct *bcs, RakNet::TimeMS time); + void ClearBufferedCommands(void); + void ClearBufferedPackets(void); + void ClearSocketQueryOutput(void); + void ClearRequestedConnectionList(void); + void AddPacketToProducer(RakNet::Packet *p); + unsigned int GenerateSeedFromGuid(void); + SimpleMutex securityExceptionMutex; + + //DataStructures::AVLBalancedBinarySearchTree rpcTree; + int defaultMTUSize; + bool trackFrequencyTable; + + // Smart pointer so I can return the object to the user + DataStructures::List > socketList; + void DerefAllSockets(void); + unsigned int GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const; + // Used for RPC replies + RakNet::BitStream *replyFromTargetBS; + SystemAddress replyFromTargetPlayer; + bool replyFromTargetBroadcast; + + RakNet::TimeMS defaultTimeoutTime; + + // Generate and store a unique GUID + void GenerateGUID(void); + unsigned int GetSystemIndexFromGuid( const RakNetGUID input ) const; + RakNetGUID myGuid; + + unsigned maxOutgoingBPS; + + // Nobody would use the internet simulator in a final build. +#ifdef _DEBUG + double _packetloss; + unsigned short _minExtraPing, _extraPingVariance; +#endif + + ///How long it has been since things were updated by a call to receiveUpdate thread uses this to determine how long to sleep for + //unsigned int lastUserUpdateCycle; + /// True to allow connection accepted packets from anyone. False to only allow these packets from servers we requested a connection to. + bool allowConnectionResponseIPMigration; + + SystemAddress firstExternalID; + int splitMessageProgressInterval; + RakNet::TimeMS unreliableTimeout; + + // Systems in this list will not go through the secure connection process, even when secure connections are turned on. Wildcards are accepted. + DataStructures::List securityExceptionList; + + SystemAddress ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ]; + + bool allowInternalRouting; + + void (*userUpdateThreadPtr)(RakPeerInterface *, void *); + void *userUpdateThreadData; + + + SignaledEvent quitAndDataEvents; + bool limitConnectionFrequencyFromTheSameIP; + + SimpleMutex packetAllocationPoolMutex; + DataStructures::MemoryPool packetAllocationPool; + + SimpleMutex packetReturnMutex; + DataStructures::Queue packetReturnQueue; + Packet *AllocPacket(unsigned dataSize, const char *file, unsigned int line); + Packet *AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line); + + /// This is used to return a number to the user when they call Send identifying the message + /// This number will be returned back with ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS and is only returned + /// with the reliability types that contain RECEIPT in the name + SimpleMutex sendReceiptSerialMutex; + uint32_t sendReceiptSerial; + void ResetSendReceipt(void); + void OnConnectedPong(RakNet::Time sendPingTime, RakNet::Time sendPongTime, RemoteSystemStruct *remoteSystem); + void CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet); + +#if LIBCAT_SECURITY==1 + // Encryption and security + bool _using_security, _require_client_public_key; + char my_public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; + cat::ServerEasyHandshake *_server_handshake; + cat::CookieJar *_cookie_jar; + bool InitializeClientSecurity(RequestedConnectionStruct *rcs, const char *public_key); +#endif + + + + + +} +// #if defined(SN_TARGET_PSP2) +// __attribute__((aligned(8))) +// #endif +; + +} // namespace RakNet + +#endif diff --git a/src/raknet/RakPeerInterface.h b/src/raknet/RakPeerInterface.h index 599980d..5eed507 100755 --- a/src/raknet/RakPeerInterface.h +++ b/src/raknet/RakPeerInterface.h @@ -1,571 +1,571 @@ -/// \file -/// \brief An interface for RakPeer. Simply contains all user functions as pure virtuals. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __RAK_PEER_INTERFACE_H -#define __RAK_PEER_INTERFACE_H - -#include "PacketPriority.h" -#include "RakNetTypes.h" -#include "RakMemoryOverride.h" -#include "Export.h" -#include "DS_List.h" -#include "RakNetSmartPtr.h" -#include "RakNetSocket.h" - -namespace RakNet -{ -// Forward declarations -class BitStream; -class PluginInterface2; -struct RPCMap; -struct RakNetStatistics; -struct RakNetBandwidth; -class RouterInterface; -class NetworkIDManager; - -/// The primary interface for RakNet, RakPeer contains all major functions for the library. -/// See the individual functions for what the class can do. -/// \brief The main interface for network communications -class RAK_DLL_EXPORT RakPeerInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(RakPeerInterface) - - ///Destructor - virtual ~RakPeerInterface() {} - - // --------------------------------------------------------------------------------------------Major Low Level Functions - Functions needed by most users-------------------------------------------------------------------------------------------- - /// \brief Starts the network threads, opens the listen port. - /// \details You must call this before calling Connect(). - /// \pre On the PS3, call Startup() after Client_Login() - /// \pre On Android, add the necessary permission to your application's androidmanifest.xml: - /// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). - /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections - /// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections - /// \param[in] localPort The port to listen for connections on. On linux the system may be set up so thast ports under 1024 are restricted for everything but the root user. Use a higher port for maximum compatibility. - /// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); However, on the XBOX be sure to use IPPROTO_VDP - /// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - /// \return RAKNET_STARTED on success, otherwise appropriate failure enumeration. - virtual StartupResult Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority=-99999 )=0; - - /// If you accept connections, you must call this or else security will not be enabled for incoming connections. - /// This feature requires more round trips, bandwidth, and CPU time for the connection handshake - /// x64 builds require under 25% of the CPU time of other builds - /// See the Encryption sample for example usage - /// \pre Must be called while offline - /// \pre LIBCAT_SECURITY must be defined to 1 in NativeFeatureIncludes.h for this function to have any effect - /// \param[in] publicKey A pointer to the public key for accepting new connections - /// \param[in] privateKey A pointer to the private key for accepting new connections - /// \param[in] bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection - virtual bool InitializeSecurity( const char *publicKey, const char *privateKey, bool bRequireClientKey = false )=0; - - /// Disables security for incoming connections. - /// \note Must be called while offline - virtual void DisableSecurity( void )=0; - - /// If secure connections are on, do not use secure connections for a specific IP address. - /// This is useful if you have a fixed-address internal server behind a LAN. - /// \note Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. - /// \param[in] ip IP address to add. * wildcards are supported. - virtual void AddToSecurityExceptionList(const char *ip)=0; - - /// Remove a specific connection previously added via AddToSecurityExceptionList - /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. - virtual void RemoveFromSecurityExceptionList(const char *ip)=0; - - /// Checks to see if a given IP is in the security exception list - /// \param[in] IP address to check. - virtual bool IsInSecurityExceptionList(const char *ip)=0; - - /// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, - /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, - /// it will be reduced to the maximum number of peers allowed. - /// Defaults to 0, meaning by default, nobody can connect to you - /// \param[in] numberAllowed Maximum number of incoming connections allowed. - virtual void SetMaximumIncomingConnections( unsigned short numberAllowed )=0; - - /// Returns the value passed to SetMaximumIncomingConnections() - /// \return the maximum number of incoming connections, which is always <= maxConnections - virtual unsigned short GetMaximumIncomingConnections( void ) const=0; - - /// Returns how many open connections there are at this time - /// \return the number of open connections - virtual unsigned short NumberOfConnections(void) const=0; - - /// Sets the password incoming connections must match in the call to Connect (defaults to none). Pass 0 to passwordData to specify no password - /// This is a way to set a low level password for all incoming connections. To selectively reject connections, implement your own scheme using CloseConnection() to remove unwanted connections - /// \param[in] passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. Specify 0 for no password data - /// \param[in] passwordDataLength The length in bytes of passwordData - virtual void SetIncomingPassword( const char* passwordData, int passwordDataLength )=0; - - /// Gets the password passed to SetIncomingPassword - /// \param[out] passwordData Should point to a block large enough to hold the password data you passed to SetIncomingPassword() - /// \param[in,out] passwordDataLength Maximum size of the array passwordData. Modified to hold the number of bytes actually written - virtual void GetIncomingPassword( char* passwordData, int *passwordDataLength )=0; - - /// \brief Connect to the specified host (ip or domain name) and server port. - /// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. - /// Calling both acts as a true peer. This is a non-blocking connection. - /// You know the connection is successful when GetConnectionState() returns IS_CONNECTED or Receive() gets a message with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. - /// If the connection is not successful, such as a rejected connection or no response then neither of these things will happen. - /// \pre Requires that you first call Initialize - /// \param[in] host Either a dotted IP address or a domain name - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData - /// \param[in] publicKey The public key the server is using. If 0, the server is not using security. If non-zero, the publicKeyMode member determines how to connect - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \param[in] sendConnectionAttemptCount How many datagrams to send to the other system to try to connect. - /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. - /// \param[in] timeoutTime How long to keep the connection alive before dropping it on unable to send a reliable message. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); - /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. - /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! - /// \note It is possible to immediately get back ID_CONNECTION_ATTEMPT_FAILED if you exceed the maxConnections parameter passed to Startup(). This could happen if you call CloseConnection() with sendDisconnectionNotificaiton true, then immediately call Connect() before the connection has closed. - virtual ConnectionAttemptResult Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey=0, unsigned connectionSocketIndex=0, unsigned sendConnectionAttemptCount=12, unsigned timeBetweenSendConnectionAttemptsMS=500, RakNet::TimeMS timeoutTime=0 )=0; - - /// \brief Connect to the specified host (ip or domain name) and server port, using a shared socket from another instance of RakNet - /// \param[in] host Either a dotted IP address or a domain name - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData - /// \param[in] socket A bound socket returned by another instance of RakPeerInterface - /// \param[in] sendConnectionAttemptCount How many datagrams to send to the other system to try to connect. - /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. - /// \param[in] timeoutTime How long to keep the connection alive before dropping it on unable to send a reliable message. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); - /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. - /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! - virtual ConnectionAttemptResult ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey=0, unsigned sendConnectionAttemptCount=12, unsigned timeBetweenSendConnectionAttemptsMS=500, RakNet::TimeMS timeoutTime=0)=0; - - /// \brief Connect to the specified network ID (Platform specific console function) - /// \details Does built-in NAt traversal - /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. - /// \param[in] passwordDataLength The length in bytes of passwordData - //virtual bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength )=0; - - /// \brief Stops the network threads and closes all connections. - /// \param[in] blockDuration How long, in milliseconds, you should wait for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. - /// \param[in] orderingChannel If blockDuration > 0, ID_DISCONNECTION_NOTIFICATION will be sent on this channel - /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. - /// If you set it to 0 then the disconnection notification won't be sent - virtual void Shutdown( unsigned int blockDuration, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY )=0; - - /// Returns if the network thread is running - /// \return true if the network thread is running, false otherwise - virtual bool IsActive( void ) const=0; - - /// Fills the array remoteSystems with the SystemAddress of all the systems we are connected to - /// \param[out] remoteSystems An array of SystemAddress structures to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to only get the number of systems we are connected to - /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array - virtual bool GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const=0; - - /// Returns the next uint32_t that Send() will return - /// \note If using RakPeer from multiple threads, this may not be accurate for your thread. Use IncrementNextSendReceipt() in that case. - /// \return The next uint32_t that Send() or SendList will return - virtual uint32_t GetNextSendReceipt(void)=0; - - /// Returns the next uint32_t that Send() will return, and increments the value by one - /// \note If using RakPeer from multiple threads, pass this to forceReceipt in the send function - /// \return The next uint32_t that Send() or SendList will return - virtual uint32_t IncrementNextSendReceipt(void)=0; - - /// Sends a block of data to the specified system that you are connected to. - /// This function only works while the connected - /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM - /// \param[in] data The block of data to send - /// \param[in] length The size in bytes of the data to send - /// \param[in] priority What priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliability to send this data. See PacketPriority.h - /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream - /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - virtual uint32_t Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; - - /// "Send" to yourself rather than a remote system. The message will be processed through the plugins and returned to the game as usual - /// This function works anytime - /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM - /// \param[in] data The block of data to send - /// \param[in] length The size in bytes of the data to send - virtual void SendLoopback( const char *data, const int length )=0; - - /// Sends a block of data to the specified system that you are connected to. Same as the above version, but takes a BitStream as input. - /// \param[in] bitStream The bitstream to send - /// \param[in] priority What priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliability to send this data. See PacketPriority.h - /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream - /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. - virtual uint32_t Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; - - /// Sends multiple blocks of data, concatenating them automatically. - /// - /// This is equivalent to: - /// RakNet::BitStream bs; - /// bs.WriteAlignedBytes(block1, blockLength1); - /// bs.WriteAlignedBytes(block2, blockLength2); - /// bs.WriteAlignedBytes(block3, blockLength3); - /// Send(&bs, ...) - /// - /// This function only works while the connected - /// \param[in] data An array of pointers to blocks of data - /// \param[in] lengths An array of integers indicating the length of each block of data - /// \param[in] numParameters Length of the arrays data and lengths - /// \param[in] priority What priority level to send on. See PacketPriority.h - /// \param[in] reliability How reliability to send this data. See PacketPriority.h - /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream - /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none - /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. - /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. - /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number - virtual uint32_t SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; - - /// Gets a message from the incoming message queue. - /// Use DeallocatePacket() to deallocate the message after you are done with it. - /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. - /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. - /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. - /// sa RakNetTypes.h contains struct Packet - virtual Packet* Receive( void )=0; - - /// Call this to deallocate a message returned by Receive() when you are done handling it. - /// \param[in] packet The message to deallocate. - virtual void DeallocatePacket( Packet *packet )=0; - - /// Return the total number of connections we are allowed - virtual unsigned short GetMaximumNumberOfPeers( void ) const=0; - - // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- - /// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). - /// \param[in] target Which system to close the connection to. - /// \param[in] sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. - /// \param[in] channel Which ordering channel to send the disconnection notification on, if any - /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. - virtual void CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY )=0; - - /// Returns if a system is connected, disconnected, connecting in progress, or various other states - /// \param[in] systemIdentifier The system we are referring to - /// \note This locks a mutex, do not call too frequently during connection attempts or the attempt will take longer and possibly even timeout - /// \return What state the remote system is in - virtual ConnectionState GetConnectionState(const AddressOrGUID systemIdentifier)=0; - - /// Cancel a pending connection attempt - /// If we are already connected, the connection stays open - /// \param[in] target Which system to cancel - virtual void CancelConnectionAttempt( const SystemAddress target )=0; - - /// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. - /// \param[in] systemAddress The SystemAddress we are referring to - /// \return The index of this SystemAddress or -1 on system not found. - virtual int GetIndexFromSystemAddress( const SystemAddress systemAddress ) const=0; - - /// This function is only useful for looping through all systems - /// Given an index, will return a SystemAddress. - /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. - /// \return The SystemAddress - virtual SystemAddress GetSystemAddressFromIndex( int index )=0; - - /// Same as GetSystemAddressFromIndex but returns RakNetGUID - /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. - /// \return The RakNetGUID - virtual RakNetGUID GetGUIDFromIndex( int index )=0; - - /// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient - /// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system - /// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. - /// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. - virtual void GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const=0; - - /// Bans an IP from connecting. Banned IPs persist between connections but are not saved on shutdown nor loaded on startup. - /// param[in] IP Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban all IP addresses starting with 128.0.0 - /// \param[in] milliseconds how many ms for a temporary ban. Use 0 for a permanent ban - virtual void AddToBanList( const char *IP, RakNet::TimeMS milliseconds=0 )=0; - - /// Allows a previously banned IP to connect. - /// param[in] Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will banAll IP addresses starting with 128.0.0 - virtual void RemoveFromBanList( const char *IP )=0; - - /// Allows all previously banned IPs to connect. - virtual void ClearBanList( void )=0; - - /// Returns true or false indicating if a particular IP is banned. - /// \param[in] IP - Dotted IP address. - /// \return true if IP matches any IPs in the ban list, accounting for any wildcards. False otherwise. - virtual bool IsBanned( const char *IP )=0; - - /// Enable or disable allowing frequent connections from the same IP adderss - /// This is a security measure which is disabled by default, but can be set to true to prevent attackers from using up all connection slots - /// \param[in] b True to limit connections from the same ip to at most 1 per 100 milliseconds. - virtual void SetLimitIPConnectionFrequency(bool b)=0; - - // --------------------------------------------------------------------------------------------Pinging Functions - Functions dealing with the automatic ping mechanism-------------------------------------------------------------------------------------------- - /// Send a ping to the specified connected system. - /// \pre The sender and recipient must already be started via a successful call to Startup() - /// \param[in] target Which system to ping - virtual void Ping( const SystemAddress target )=0; - - /// Send a ping to the specified unconnected system. The remote system, if it is Initialized, will respond with ID_PONG followed by sizeof(RakNet::TimeMS) containing the system time the ping was sent.(Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h - /// System should reply with ID_PONG if it is active - /// \param[in] host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \return true on success, false on failure (unknown hostname) - virtual bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 )=0; - - /// Returns the average of all ping times read for the specific system or -1 if none read yet - /// \param[in] systemAddress Which system we are referring to - /// \return The ping time for this system, or -1 - virtual int GetAveragePing( const AddressOrGUID systemIdentifier )=0; - - /// Returns the last ping time read for the specific system or -1 if none read yet - /// \param[in] systemAddress Which system we are referring to - /// \return The last ping time for this system, or -1 - virtual int GetLastPing( const AddressOrGUID systemIdentifier ) const=0; - - /// Returns the lowest ping time read or -1 if none read yet - /// \param[in] systemAddress Which system we are referring to - /// \return The lowest ping time for this system, or -1 - virtual int GetLowestPing( const AddressOrGUID systemIdentifier ) const=0; - - /// Ping the remote systems every so often, or not. Can be called anytime. - /// By default this is true. Recommended to leave on, because congestion control uses it to determine how often to resend lost packets. - /// It would be true by default to prevent timestamp drift, since in the event of a clock spike, the timestamp deltas would no longer be accurate - /// \param[in] doPing True to start occasional pings. False to stop them. - virtual void SetOccasionalPing( bool doPing )=0; - - // --------------------------------------------------------------------------------------------Static Data Functions - Functions dealing with API defined synchronized memory-------------------------------------------------------------------------------------------- - /// Sets the data to send along with a LAN server discovery or offline ping reply. - /// \a length should be under 400 bytes, as a security measure against flood attacks - /// \param[in] data a block of data to store, or 0 for none - /// \param[in] length The length of data in bytes, or 0 for none - /// \sa Ping.cpp - virtual void SetOfflinePingResponse( const char *data, const unsigned int length )=0; - - /// Returns pointers to a copy of the data passed to SetOfflinePingResponse - /// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() - /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() - /// \sa SetOfflinePingResponse - virtual void GetOfflinePingResponse( char **data, unsigned int *length )=0; - - //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- - /// Return the unique address identifier that represents you or another system on the the network and is based on your local IP / port. - /// \note Not supported by the XBOX - /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address - /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) - /// \return the identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy - virtual SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const=0; - - /// Return the unique address identifier that represents you on the the network and is based on your externalIP / port - /// (the IP / port the specified player uses to communicate with you) - /// \param[in] target Which remote system you are referring to for your external ID. Usually the same for all systems, unless you have two or more network cards. - virtual SystemAddress GetExternalID( const SystemAddress target ) const=0; - - /// Return my own GUID - virtual const RakNetGUID GetMyGUID(void) const=0; - - /// Return the address bound to a socket at the specified index - virtual SystemAddress GetMyBoundAddress(const int socketIndex=0)=0; - - /// Get a random number (to generate a GUID) - static uint64_t Get64BitUniqueRandomNumber(void); - - /// Given a connected system, give us the unique GUID representing that instance of RakPeer. - /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different - /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. - /// Returns UNASSIGNED_RAKNET_GUID if system address can't be found. - /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID - /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID - /// \param[in] input The system address of the system we are connected to - virtual const RakNetGUID& GetGuidFromSystemAddress( const SystemAddress input ) const=0; - - /// Given the GUID of a connected system, give us the system address of that system. - /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different - /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. - /// If \a input is UNASSIGNED_RAKNET_GUID, will return UNASSIGNED_SYSTEM_ADDRESS - /// \param[in] input The RakNetGUID of the system we are checking to see if we are connected to - virtual SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const=0; - - /// Given the SystemAddress of a connected system, get the public key they provided as an identity - /// Returns false if system address was not found or client public key is not known - /// \param[in] input The RakNetGUID of the system - /// \param[in] client_public_key The connected client's public key is copied to this address. Buffer must be cat::EasyHandshake::PUBLIC_KEY_BYTES bytes in length. - virtual bool GetClientPublicKeyFromSystemAddress( const SystemAddress input, char *client_public_key ) const=0; - - /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. - /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. - /// Do not set different values for different computers that are connected to each other, or you won't be able to reconnect after ID_CONNECTION_LOST - /// \param[in] timeMS Time, in MS - /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. - virtual void SetTimeoutTime( RakNet::TimeMS timeMS, const SystemAddress target )=0; - - /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS to get the default value - /// \return timeoutTime for a given system. - virtual RakNet::TimeMS GetTimeoutTime( const SystemAddress target )=0; - - /// Returns the current MTU size - /// \param[in] target Which system to get this for. UNASSIGNED_SYSTEM_ADDRESS to get the default - /// \return The current MTU size - virtual int GetMTUSize( const SystemAddress target ) const=0; - - /// Returns the number of IP addresses this system has internally. Get the actual addresses from GetLocalIP() - virtual unsigned GetNumberOfAddresses( void )=0; - - /// Returns an IP address at index 0 to GetNumberOfAddresses-1 - /// \param[in] index index into the list of IP addresses - /// \return The local IP address at this index - virtual const char* GetLocalIP( unsigned int index )=0; - - /// Is this a local IP? - /// \param[in] An IP address to check, excluding the port - /// \return True if this is one of the IP addresses returned by GetLocalIP - virtual bool IsLocalIP( const char *ip )=0; - - /// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary - /// when connecting to servers with multiple IP addresses. - /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections - virtual void AllowConnectionResponseIPMigration( bool allow )=0; - - /// Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. - /// This will tell the remote system our external IP outside the LAN along with some user data. - /// \pre The sender and recipient must already be started via a successful call to Initialize - /// \param[in] host Either a dotted IP address or a domain name - /// \param[in] remotePort Which port to connect to on the remote machine. - /// \param[in] data Optional data to append to the packet. - /// \param[in] dataLength length of data in bytes. Use 0 if no data. - /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - /// \return false if IsActive()==false or the host is unresolvable. True otherwise - virtual bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 )=0; - - /// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. - /// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived - /// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. - /// Defaults to 0 (never return this notification) - /// \param[in] interval How many messages to use as an interval - virtual void SetSplitMessageProgressInterval(int interval)=0; - - /// Returns what was passed to SetSplitMessageProgressInterval() - /// \return What was passed to SetSplitMessageProgressInterval(). Default to 0. - virtual int GetSplitMessageProgressInterval(void) const=0; - - /// Set how long to wait before giving up on sending an unreliable message - /// Useful if the network is clogged up. - /// Set to 0 or less to never timeout. Defaults to 0. - /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. - virtual void SetUnreliableTimeout(RakNet::TimeMS timeoutMS)=0; - - /// Send a message to host, with the IP socket option TTL set to 3 - /// This message will not reach the host, but will open the router. - /// Used for NAT-Punchthrough - virtual void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 )=0; - - // -------------------------------------------------------------------------------------------- Plugin Functions-------------------------------------------------------------------------------------------- - /// \brief Attaches a Plugin interface to an instance of the base class (RakPeer or PacketizedTCP) to run code automatically on message receipt in the Receive call. - /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. - /// \param[in] messageHandler Pointer to the plugin to attach. - virtual void AttachPlugin( PluginInterface2 *plugin )=0; - - /// \brief Detaches a Plugin interface from the instance of the base class (RakPeer or PacketizedTCP) it is attached to. - /// \details This method disables the plugin code from running automatically on base class's updates or message receipt. - /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. - /// \param[in] messageHandler Pointer to a plugin to detach. - virtual void DetachPlugin( PluginInterface2 *messageHandler )=0; - - // --------------------------------------------------------------------------------------------Miscellaneous Functions-------------------------------------------------------------------------------------------- - /// Put a message back at the end of the receive queue in case you don't want to deal with it immediately - /// \param[in] packet The packet you want to push back. - /// \param[in] pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) - virtual void PushBackPacket( Packet *packet, bool pushAtHead )=0; - - /// \internal - /// \brief For a given system identified by \a guid, change the SystemAddress to send to. - /// \param[in] guid The connection we are referring to - /// \param[in] systemAddress The new address to send to - virtual void ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress)=0; - - /// \returns a packet for you to write to if you want to create a Packet for some reason. - /// You can add it to the receive buffer with PushBackPacket - /// \param[in] dataSize How many bytes to allocate for the buffer - /// \return A packet you can write to - virtual Packet* AllocatePacket(unsigned dataSize)=0; - - /// Get the socket used with a particular active connection - /// The smart pointer reference counts the RakNetSocket object, so the socket will remain active as long as the smart pointer does, even if RakNet were to shutdown or close the connection. - /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. - /// \param[in] target Which system - /// \return A smart pointer object containing the socket information about the socket. Be sure to check IsNull() which is returned if the update thread is unresponsive, shutting down, or if this system is not connected - virtual RakNetSmartPtr GetSocket( const SystemAddress target )=0; - - /// Get all sockets in use - /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. - /// \param[out] sockets List of RakNetSocket structures in use. Sockets will not be closed until \a sockets goes out of scope - virtual void GetSockets( DataStructures::List > &sockets )=0; - virtual void ReleaseSockets( DataStructures::List > &sockets )=0; - - virtual void WriteOutOfBandHeader(RakNet::BitStream *bitStream)=0; - - /// If you need code to run in the same thread as RakNet's update thread, this function can be used for that - /// \param[in] _userUpdateThreadPtr C callback function - /// \param[in] _userUpdateThreadData Passed to C callback function - virtual void SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData)=0; - - // --------------------------------------------------------------------------------------------Network Simulator Functions-------------------------------------------------------------------------------------------- - /// Adds simulated ping and packet loss to the outgoing data flow. - /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and packetloss value on each. - /// You can exclude network simulator code with the _RELEASE #define to decrease code size - /// \deprecated Use http://www.jenkinssoftware.com/forum/index.php?topic=1671.0 instead. - /// \note Doesn't work past version 3.6201 - /// \param[in] packetloss Chance to lose a packet. Ranges from 0 to 1. - /// \param[in] minExtraPing The minimum time to delay sends. - /// \param[in] extraPingVariance The additional random time to delay sends. - virtual void ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance)=0; - - /// Limits how much outgoing bandwidth can be sent per-connection. - /// This limit does not apply to the sum of all connections! - /// Exceeding the limit queues up outgoing traffic - /// \param[in] maxBitsPerSecond Maximum bits per second to send. Use 0 for unlimited (default). Once set, it takes effect immedately and persists until called again. - virtual void SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond )=0; - - /// Returns if you previously called ApplyNetworkSimulator - /// \return If you previously called ApplyNetworkSimulator - virtual bool IsNetworkSimulatorActive( void )=0; - - // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- - - /// Returns a structure containing a large set of network statistics for the specified system. - /// You can map this data to a string using the C style StatisticsToString() function - /// \param[in] systemAddress: Which connected system to get statistics for - /// \param[in] rns If you supply this structure, it will be written to it. Otherwise it will use a static struct, which is not threadsafe - /// \return 0 on can't find the specified system. A pointer to a set of data otherwise. - /// \sa RakNetStatistics.h - virtual RakNetStatistics * GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 )=0; - virtual bool GetStatistics( const int index, RakNetStatistics *rns )=0; - - /// \Returns how many messages are waiting when you call Receive() - virtual unsigned int GetReceiveBufferSize(void)=0; - - // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- - - /// \internal - virtual bool SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 )=0; - - int errorState; -} -// #if defined(SN_TARGET_PSP2) -// __attribute__((aligned(8))) -// #endif -; - -} // namespace RakNet - -#endif +/// \file +/// \brief An interface for RakPeer. Simply contains all user functions as pure virtuals. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __RAK_PEER_INTERFACE_H +#define __RAK_PEER_INTERFACE_H + +#include "PacketPriority.h" +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" +#include "Export.h" +#include "DS_List.h" +#include "RakNetSmartPtr.h" +#include "RakNetSocket.h" + +namespace RakNet +{ +// Forward declarations +class BitStream; +class PluginInterface2; +struct RPCMap; +struct RakNetStatistics; +struct RakNetBandwidth; +class RouterInterface; +class NetworkIDManager; + +/// The primary interface for RakNet, RakPeer contains all major functions for the library. +/// See the individual functions for what the class can do. +/// \brief The main interface for network communications +class RAK_DLL_EXPORT RakPeerInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(RakPeerInterface) + + ///Destructor + virtual ~RakPeerInterface() {} + + // --------------------------------------------------------------------------------------------Major Low Level Functions - Functions needed by most users-------------------------------------------------------------------------------------------- + /// \brief Starts the network threads, opens the listen port. + /// \details You must call this before calling Connect(). + /// \pre On the PS3, call Startup() after Client_Login() + /// \pre On Android, add the necessary permission to your application's androidmanifest.xml: + /// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). + /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections + /// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections + /// \param[in] localPort The port to listen for connections on. On linux the system may be set up so thast ports under 1024 are restricted for everything but the root user. Use a higher port for maximum compatibility. + /// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); However, on the XBOX be sure to use IPPROTO_VDP + /// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + /// \return RAKNET_STARTED on success, otherwise appropriate failure enumeration. + virtual StartupResult Startup( unsigned short maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority=-99999 )=0; + + /// If you accept connections, you must call this or else security will not be enabled for incoming connections. + /// This feature requires more round trips, bandwidth, and CPU time for the connection handshake + /// x64 builds require under 25% of the CPU time of other builds + /// See the Encryption sample for example usage + /// \pre Must be called while offline + /// \pre LIBCAT_SECURITY must be defined to 1 in NativeFeatureIncludes.h for this function to have any effect + /// \param[in] publicKey A pointer to the public key for accepting new connections + /// \param[in] privateKey A pointer to the private key for accepting new connections + /// \param[in] bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection + virtual bool InitializeSecurity( const char *publicKey, const char *privateKey, bool bRequireClientKey = false )=0; + + /// Disables security for incoming connections. + /// \note Must be called while offline + virtual void DisableSecurity( void )=0; + + /// If secure connections are on, do not use secure connections for a specific IP address. + /// This is useful if you have a fixed-address internal server behind a LAN. + /// \note Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. + /// \param[in] ip IP address to add. * wildcards are supported. + virtual void AddToSecurityExceptionList(const char *ip)=0; + + /// Remove a specific connection previously added via AddToSecurityExceptionList + /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. + virtual void RemoveFromSecurityExceptionList(const char *ip)=0; + + /// Checks to see if a given IP is in the security exception list + /// \param[in] IP address to check. + virtual bool IsInSecurityExceptionList(const char *ip)=0; + + /// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, + /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, + /// it will be reduced to the maximum number of peers allowed. + /// Defaults to 0, meaning by default, nobody can connect to you + /// \param[in] numberAllowed Maximum number of incoming connections allowed. + virtual void SetMaximumIncomingConnections( unsigned short numberAllowed )=0; + + /// Returns the value passed to SetMaximumIncomingConnections() + /// \return the maximum number of incoming connections, which is always <= maxConnections + virtual unsigned short GetMaximumIncomingConnections( void ) const=0; + + /// Returns how many open connections there are at this time + /// \return the number of open connections + virtual unsigned short NumberOfConnections(void) const=0; + + /// Sets the password incoming connections must match in the call to Connect (defaults to none). Pass 0 to passwordData to specify no password + /// This is a way to set a low level password for all incoming connections. To selectively reject connections, implement your own scheme using CloseConnection() to remove unwanted connections + /// \param[in] passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. Specify 0 for no password data + /// \param[in] passwordDataLength The length in bytes of passwordData + virtual void SetIncomingPassword( const char* passwordData, int passwordDataLength )=0; + + /// Gets the password passed to SetIncomingPassword + /// \param[out] passwordData Should point to a block large enough to hold the password data you passed to SetIncomingPassword() + /// \param[in,out] passwordDataLength Maximum size of the array passwordData. Modified to hold the number of bytes actually written + virtual void GetIncomingPassword( char* passwordData, int *passwordDataLength )=0; + + /// \brief Connect to the specified host (ip or domain name) and server port. + /// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. + /// Calling both acts as a true peer. This is a non-blocking connection. + /// You know the connection is successful when GetConnectionState() returns IS_CONNECTED or Receive() gets a message with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. + /// If the connection is not successful, such as a rejected connection or no response then neither of these things will happen. + /// \pre Requires that you first call Initialize + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData + /// \param[in] publicKey The public key the server is using. If 0, the server is not using security. If non-zero, the publicKeyMode member determines how to connect + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \param[in] sendConnectionAttemptCount How many datagrams to send to the other system to try to connect. + /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. + /// \param[in] timeoutTime How long to keep the connection alive before dropping it on unable to send a reliable message. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); + /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. + /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! + /// \note It is possible to immediately get back ID_CONNECTION_ATTEMPT_FAILED if you exceed the maxConnections parameter passed to Startup(). This could happen if you call CloseConnection() with sendDisconnectionNotificaiton true, then immediately call Connect() before the connection has closed. + virtual ConnectionAttemptResult Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey=0, unsigned connectionSocketIndex=0, unsigned sendConnectionAttemptCount=12, unsigned timeBetweenSendConnectionAttemptsMS=500, RakNet::TimeMS timeoutTime=0 )=0; + + /// \brief Connect to the specified host (ip or domain name) and server port, using a shared socket from another instance of RakNet + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData + /// \param[in] socket A bound socket returned by another instance of RakPeerInterface + /// \param[in] sendConnectionAttemptCount How many datagrams to send to the other system to try to connect. + /// \param[in] timeBetweenSendConnectionAttemptsMS Time to elapse before a datagram is sent to the other system to try to connect. After sendConnectionAttemptCount number of attempts, ID_CONNECTION_ATTEMPT_FAILED is returned. Under low bandwidth conditions with multiple simultaneous outgoing connections, this value should be raised to 1000 or higher, or else the MTU detection can overrun the available bandwidth. + /// \param[in] timeoutTime How long to keep the connection alive before dropping it on unable to send a reliable message. 0 to use the default from SetTimeoutTime(UNASSIGNED_SYSTEM_ADDRESS); + /// \return CONNECTION_ATTEMPT_STARTED on successful initiation. Otherwise, an appropriate enumeration indicating failure. + /// \note CONNECTION_ATTEMPT_STARTED does not mean you are already connected! + virtual ConnectionAttemptResult ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSmartPtr socket, PublicKey *publicKey=0, unsigned sendConnectionAttemptCount=12, unsigned timeBetweenSendConnectionAttemptsMS=500, RakNet::TimeMS timeoutTime=0)=0; + + /// \brief Connect to the specified network ID (Platform specific console function) + /// \details Does built-in NAt traversal + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData + //virtual bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength )=0; + + /// \brief Stops the network threads and closes all connections. + /// \param[in] blockDuration How long, in milliseconds, you should wait for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. + /// \param[in] orderingChannel If blockDuration > 0, ID_DISCONNECTION_NOTIFICATION will be sent on this channel + /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. + /// If you set it to 0 then the disconnection notification won't be sent + virtual void Shutdown( unsigned int blockDuration, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY )=0; + + /// Returns if the network thread is running + /// \return true if the network thread is running, false otherwise + virtual bool IsActive( void ) const=0; + + /// Fills the array remoteSystems with the SystemAddress of all the systems we are connected to + /// \param[out] remoteSystems An array of SystemAddress structures to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to only get the number of systems we are connected to + /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array + virtual bool GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const=0; + + /// Returns the next uint32_t that Send() will return + /// \note If using RakPeer from multiple threads, this may not be accurate for your thread. Use IncrementNextSendReceipt() in that case. + /// \return The next uint32_t that Send() or SendList will return + virtual uint32_t GetNextSendReceipt(void)=0; + + /// Returns the next uint32_t that Send() will return, and increments the value by one + /// \note If using RakPeer from multiple threads, pass this to forceReceipt in the send function + /// \return The next uint32_t that Send() or SendList will return + virtual uint32_t IncrementNextSendReceipt(void)=0; + + /// Sends a block of data to the specified system that you are connected to. + /// This function only works while the connected + /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM + /// \param[in] data The block of data to send + /// \param[in] length The size in bytes of the data to send + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + virtual uint32_t Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; + + /// "Send" to yourself rather than a remote system. The message will be processed through the plugins and returned to the game as usual + /// This function works anytime + /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM + /// \param[in] data The block of data to send + /// \param[in] length The size in bytes of the data to send + virtual void SendLoopback( const char *data, const int length )=0; + + /// Sends a block of data to the specified system that you are connected to. Same as the above version, but takes a BitStream as input. + /// \param[in] bitStream The bitstream to send + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. + virtual uint32_t Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; + + /// Sends multiple blocks of data, concatenating them automatically. + /// + /// This is equivalent to: + /// RakNet::BitStream bs; + /// bs.WriteAlignedBytes(block1, blockLength1); + /// bs.WriteAlignedBytes(block2, blockLength2); + /// bs.WriteAlignedBytes(block3, blockLength3); + /// Send(&bs, ...) + /// + /// This function only works while the connected + /// \param[in] data An array of pointers to blocks of data + /// \param[in] lengths An array of integers indicating the length of each block of data + /// \param[in] numParameters Length of the arrays data and lengths + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it. + /// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number + virtual uint32_t SendList( const char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 )=0; + + /// Gets a message from the incoming message queue. + /// Use DeallocatePacket() to deallocate the message after you are done with it. + /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. + /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. + /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. + /// sa RakNetTypes.h contains struct Packet + virtual Packet* Receive( void )=0; + + /// Call this to deallocate a message returned by Receive() when you are done handling it. + /// \param[in] packet The message to deallocate. + virtual void DeallocatePacket( Packet *packet )=0; + + /// Return the total number of connections we are allowed + virtual unsigned short GetMaximumNumberOfPeers( void ) const=0; + + // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- + /// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). + /// \param[in] target Which system to close the connection to. + /// \param[in] sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. + /// \param[in] channel Which ordering channel to send the disconnection notification on, if any + /// \param[in] disconnectionNotificationPriority Priority to send ID_DISCONNECTION_NOTIFICATION on. + virtual void CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel=0, PacketPriority disconnectionNotificationPriority=LOW_PRIORITY )=0; + + /// Returns if a system is connected, disconnected, connecting in progress, or various other states + /// \param[in] systemIdentifier The system we are referring to + /// \note This locks a mutex, do not call too frequently during connection attempts or the attempt will take longer and possibly even timeout + /// \return What state the remote system is in + virtual ConnectionState GetConnectionState(const AddressOrGUID systemIdentifier)=0; + + /// Cancel a pending connection attempt + /// If we are already connected, the connection stays open + /// \param[in] target Which system to cancel + virtual void CancelConnectionAttempt( const SystemAddress target )=0; + + /// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. + /// \param[in] systemAddress The SystemAddress we are referring to + /// \return The index of this SystemAddress or -1 on system not found. + virtual int GetIndexFromSystemAddress( const SystemAddress systemAddress ) const=0; + + /// This function is only useful for looping through all systems + /// Given an index, will return a SystemAddress. + /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. + /// \return The SystemAddress + virtual SystemAddress GetSystemAddressFromIndex( int index )=0; + + /// Same as GetSystemAddressFromIndex but returns RakNetGUID + /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. + /// \return The RakNetGUID + virtual RakNetGUID GetGUIDFromIndex( int index )=0; + + /// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient + /// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system + /// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. + /// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. + virtual void GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const=0; + + /// Bans an IP from connecting. Banned IPs persist between connections but are not saved on shutdown nor loaded on startup. + /// param[in] IP Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban all IP addresses starting with 128.0.0 + /// \param[in] milliseconds how many ms for a temporary ban. Use 0 for a permanent ban + virtual void AddToBanList( const char *IP, RakNet::TimeMS milliseconds=0 )=0; + + /// Allows a previously banned IP to connect. + /// param[in] Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will banAll IP addresses starting with 128.0.0 + virtual void RemoveFromBanList( const char *IP )=0; + + /// Allows all previously banned IPs to connect. + virtual void ClearBanList( void )=0; + + /// Returns true or false indicating if a particular IP is banned. + /// \param[in] IP - Dotted IP address. + /// \return true if IP matches any IPs in the ban list, accounting for any wildcards. False otherwise. + virtual bool IsBanned( const char *IP )=0; + + /// Enable or disable allowing frequent connections from the same IP adderss + /// This is a security measure which is disabled by default, but can be set to true to prevent attackers from using up all connection slots + /// \param[in] b True to limit connections from the same ip to at most 1 per 100 milliseconds. + virtual void SetLimitIPConnectionFrequency(bool b)=0; + + // --------------------------------------------------------------------------------------------Pinging Functions - Functions dealing with the automatic ping mechanism-------------------------------------------------------------------------------------------- + /// Send a ping to the specified connected system. + /// \pre The sender and recipient must already be started via a successful call to Startup() + /// \param[in] target Which system to ping + virtual void Ping( const SystemAddress target )=0; + + /// Send a ping to the specified unconnected system. The remote system, if it is Initialized, will respond with ID_PONG followed by sizeof(RakNet::TimeMS) containing the system time the ping was sent.(Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h + /// System should reply with ID_PONG if it is active + /// \param[in] host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return true on success, false on failure (unknown hostname) + virtual bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 )=0; + + /// Returns the average of all ping times read for the specific system or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The ping time for this system, or -1 + virtual int GetAveragePing( const AddressOrGUID systemIdentifier )=0; + + /// Returns the last ping time read for the specific system or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The last ping time for this system, or -1 + virtual int GetLastPing( const AddressOrGUID systemIdentifier ) const=0; + + /// Returns the lowest ping time read or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The lowest ping time for this system, or -1 + virtual int GetLowestPing( const AddressOrGUID systemIdentifier ) const=0; + + /// Ping the remote systems every so often, or not. Can be called anytime. + /// By default this is true. Recommended to leave on, because congestion control uses it to determine how often to resend lost packets. + /// It would be true by default to prevent timestamp drift, since in the event of a clock spike, the timestamp deltas would no longer be accurate + /// \param[in] doPing True to start occasional pings. False to stop them. + virtual void SetOccasionalPing( bool doPing )=0; + + // --------------------------------------------------------------------------------------------Static Data Functions - Functions dealing with API defined synchronized memory-------------------------------------------------------------------------------------------- + /// Sets the data to send along with a LAN server discovery or offline ping reply. + /// \a length should be under 400 bytes, as a security measure against flood attacks + /// \param[in] data a block of data to store, or 0 for none + /// \param[in] length The length of data in bytes, or 0 for none + /// \sa Ping.cpp + virtual void SetOfflinePingResponse( const char *data, const unsigned int length )=0; + + /// Returns pointers to a copy of the data passed to SetOfflinePingResponse + /// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() + /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() + /// \sa SetOfflinePingResponse + virtual void GetOfflinePingResponse( char **data, unsigned int *length )=0; + + //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- + /// Return the unique address identifier that represents you or another system on the the network and is based on your local IP / port. + /// \note Not supported by the XBOX + /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address + /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) + /// \return the identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy + virtual SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const=0; + + /// Return the unique address identifier that represents you on the the network and is based on your externalIP / port + /// (the IP / port the specified player uses to communicate with you) + /// \param[in] target Which remote system you are referring to for your external ID. Usually the same for all systems, unless you have two or more network cards. + virtual SystemAddress GetExternalID( const SystemAddress target ) const=0; + + /// Return my own GUID + virtual const RakNetGUID GetMyGUID(void) const=0; + + /// Return the address bound to a socket at the specified index + virtual SystemAddress GetMyBoundAddress(const int socketIndex=0)=0; + + /// Get a random number (to generate a GUID) + static uint64_t Get64BitUniqueRandomNumber(void); + + /// Given a connected system, give us the unique GUID representing that instance of RakPeer. + /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different + /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. + /// Returns UNASSIGNED_RAKNET_GUID if system address can't be found. + /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID + /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID + /// \param[in] input The system address of the system we are connected to + virtual const RakNetGUID& GetGuidFromSystemAddress( const SystemAddress input ) const=0; + + /// Given the GUID of a connected system, give us the system address of that system. + /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different + /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. + /// If \a input is UNASSIGNED_RAKNET_GUID, will return UNASSIGNED_SYSTEM_ADDRESS + /// \param[in] input The RakNetGUID of the system we are checking to see if we are connected to + virtual SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const=0; + + /// Given the SystemAddress of a connected system, get the public key they provided as an identity + /// Returns false if system address was not found or client public key is not known + /// \param[in] input The RakNetGUID of the system + /// \param[in] client_public_key The connected client's public key is copied to this address. Buffer must be cat::EasyHandshake::PUBLIC_KEY_BYTES bytes in length. + virtual bool GetClientPublicKeyFromSystemAddress( const SystemAddress input, char *client_public_key ) const=0; + + /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. + /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. + /// Do not set different values for different computers that are connected to each other, or you won't be able to reconnect after ID_CONNECTION_LOST + /// \param[in] timeMS Time, in MS + /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. + virtual void SetTimeoutTime( RakNet::TimeMS timeMS, const SystemAddress target )=0; + + /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS to get the default value + /// \return timeoutTime for a given system. + virtual RakNet::TimeMS GetTimeoutTime( const SystemAddress target )=0; + + /// Returns the current MTU size + /// \param[in] target Which system to get this for. UNASSIGNED_SYSTEM_ADDRESS to get the default + /// \return The current MTU size + virtual int GetMTUSize( const SystemAddress target ) const=0; + + /// Returns the number of IP addresses this system has internally. Get the actual addresses from GetLocalIP() + virtual unsigned GetNumberOfAddresses( void )=0; + + /// Returns an IP address at index 0 to GetNumberOfAddresses-1 + /// \param[in] index index into the list of IP addresses + /// \return The local IP address at this index + virtual const char* GetLocalIP( unsigned int index )=0; + + /// Is this a local IP? + /// \param[in] An IP address to check, excluding the port + /// \return True if this is one of the IP addresses returned by GetLocalIP + virtual bool IsLocalIP( const char *ip )=0; + + /// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary + /// when connecting to servers with multiple IP addresses. + /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections + virtual void AllowConnectionResponseIPMigration( bool allow )=0; + + /// Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. + /// This will tell the remote system our external IP outside the LAN along with some user data. + /// \pre The sender and recipient must already be started via a successful call to Initialize + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] data Optional data to append to the packet. + /// \param[in] dataLength length of data in bytes. Use 0 if no data. + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return false if IsActive()==false or the host is unresolvable. True otherwise + virtual bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 )=0; + + /// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. + /// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived + /// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. + /// Defaults to 0 (never return this notification) + /// \param[in] interval How many messages to use as an interval + virtual void SetSplitMessageProgressInterval(int interval)=0; + + /// Returns what was passed to SetSplitMessageProgressInterval() + /// \return What was passed to SetSplitMessageProgressInterval(). Default to 0. + virtual int GetSplitMessageProgressInterval(void) const=0; + + /// Set how long to wait before giving up on sending an unreliable message + /// Useful if the network is clogged up. + /// Set to 0 or less to never timeout. Defaults to 0. + /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. + virtual void SetUnreliableTimeout(RakNet::TimeMS timeoutMS)=0; + + /// Send a message to host, with the IP socket option TTL set to 3 + /// This message will not reach the host, but will open the router. + /// Used for NAT-Punchthrough + virtual void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 )=0; + + // -------------------------------------------------------------------------------------------- Plugin Functions-------------------------------------------------------------------------------------------- + /// \brief Attaches a Plugin interface to an instance of the base class (RakPeer or PacketizedTCP) to run code automatically on message receipt in the Receive call. + /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. + /// \param[in] messageHandler Pointer to the plugin to attach. + virtual void AttachPlugin( PluginInterface2 *plugin )=0; + + /// \brief Detaches a Plugin interface from the instance of the base class (RakPeer or PacketizedTCP) it is attached to. + /// \details This method disables the plugin code from running automatically on base class's updates or message receipt. + /// If the plugin returns false from PluginInterface::UsesReliabilityLayer(), which is the case for all plugins except PacketLogger, you can call AttachPlugin() and DetachPlugin() for this plugin while RakPeer is active. + /// \param[in] messageHandler Pointer to a plugin to detach. + virtual void DetachPlugin( PluginInterface2 *messageHandler )=0; + + // --------------------------------------------------------------------------------------------Miscellaneous Functions-------------------------------------------------------------------------------------------- + /// Put a message back at the end of the receive queue in case you don't want to deal with it immediately + /// \param[in] packet The packet you want to push back. + /// \param[in] pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) + virtual void PushBackPacket( Packet *packet, bool pushAtHead )=0; + + /// \internal + /// \brief For a given system identified by \a guid, change the SystemAddress to send to. + /// \param[in] guid The connection we are referring to + /// \param[in] systemAddress The new address to send to + virtual void ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress)=0; + + /// \returns a packet for you to write to if you want to create a Packet for some reason. + /// You can add it to the receive buffer with PushBackPacket + /// \param[in] dataSize How many bytes to allocate for the buffer + /// \return A packet you can write to + virtual Packet* AllocatePacket(unsigned dataSize)=0; + + /// Get the socket used with a particular active connection + /// The smart pointer reference counts the RakNetSocket object, so the socket will remain active as long as the smart pointer does, even if RakNet were to shutdown or close the connection. + /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. + /// \param[in] target Which system + /// \return A smart pointer object containing the socket information about the socket. Be sure to check IsNull() which is returned if the update thread is unresponsive, shutting down, or if this system is not connected + virtual RakNetSmartPtr GetSocket( const SystemAddress target )=0; + + /// Get all sockets in use + /// \note This sends a query to the thread and blocks on the return value for up to one second. In practice it should only take a millisecond or so. + /// \param[out] sockets List of RakNetSocket structures in use. Sockets will not be closed until \a sockets goes out of scope + virtual void GetSockets( DataStructures::List > &sockets )=0; + virtual void ReleaseSockets( DataStructures::List > &sockets )=0; + + virtual void WriteOutOfBandHeader(RakNet::BitStream *bitStream)=0; + + /// If you need code to run in the same thread as RakNet's update thread, this function can be used for that + /// \param[in] _userUpdateThreadPtr C callback function + /// \param[in] _userUpdateThreadData Passed to C callback function + virtual void SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData)=0; + + // --------------------------------------------------------------------------------------------Network Simulator Functions-------------------------------------------------------------------------------------------- + /// Adds simulated ping and packet loss to the outgoing data flow. + /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and packetloss value on each. + /// You can exclude network simulator code with the _RELEASE #define to decrease code size + /// \deprecated Use http://www.jenkinssoftware.com/forum/index.php?topic=1671.0 instead. + /// \note Doesn't work past version 3.6201 + /// \param[in] packetloss Chance to lose a packet. Ranges from 0 to 1. + /// \param[in] minExtraPing The minimum time to delay sends. + /// \param[in] extraPingVariance The additional random time to delay sends. + virtual void ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance)=0; + + /// Limits how much outgoing bandwidth can be sent per-connection. + /// This limit does not apply to the sum of all connections! + /// Exceeding the limit queues up outgoing traffic + /// \param[in] maxBitsPerSecond Maximum bits per second to send. Use 0 for unlimited (default). Once set, it takes effect immedately and persists until called again. + virtual void SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond )=0; + + /// Returns if you previously called ApplyNetworkSimulator + /// \return If you previously called ApplyNetworkSimulator + virtual bool IsNetworkSimulatorActive( void )=0; + + // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- + + /// Returns a structure containing a large set of network statistics for the specified system. + /// You can map this data to a string using the C style StatisticsToString() function + /// \param[in] systemAddress: Which connected system to get statistics for + /// \param[in] rns If you supply this structure, it will be written to it. Otherwise it will use a static struct, which is not threadsafe + /// \return 0 on can't find the specified system. A pointer to a set of data otherwise. + /// \sa RakNetStatistics.h + virtual RakNetStatistics * GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 )=0; + virtual bool GetStatistics( const int index, RakNetStatistics *rns )=0; + + /// \Returns how many messages are waiting when you call Receive() + virtual unsigned int GetReceiveBufferSize(void)=0; + + // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- + + /// \internal + virtual bool SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 )=0; + + int errorState; +} +// #if defined(SN_TARGET_PSP2) +// __attribute__((aligned(8))) +// #endif +; + +} // namespace RakNet + +#endif diff --git a/src/raknet/RakSleep.cpp b/src/raknet/RakSleep.cpp index 41bc904..cf43a4d 100755 --- a/src/raknet/RakSleep.cpp +++ b/src/raknet/RakSleep.cpp @@ -1,53 +1,53 @@ - - -#if defined(_WIN32) -#include "WindowsIncludes.h" // Sleep - - - - - -#else -#include -#include -#include -#endif - -#include "RakSleep.h" - -void RakSleep(unsigned int ms) -{ -#ifdef _WIN32 - Sleep(ms); - - - - - -#else - //Single thread sleep code thanks to Furquan Shaikh, http://somethingswhichidintknow.blogspot.com/2009/09/sleep-in-pthread.html - //Modified slightly from the original - pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER; - pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER; - struct timespec timeToWait; - struct timeval now; - int rt; - - gettimeofday(&now,NULL); - - long seconds = ms/1000; - long nanoseconds = (ms - seconds * 1000) * 1000000; - timeToWait.tv_sec = now.tv_sec + seconds; - timeToWait.tv_nsec = now.tv_usec*1000 + nanoseconds; - - if (timeToWait.tv_nsec >= 1000000000) - { - timeToWait.tv_nsec -= 1000000000; - timeToWait.tv_sec++; - } - - pthread_mutex_lock(&fakeMutex); - rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait); - pthread_mutex_unlock(&fakeMutex); -#endif -} + + +#if defined(_WIN32) +#include "WindowsIncludes.h" // Sleep + + + + + +#else +#include +#include +#include +#endif + +#include "RakSleep.h" + +void RakSleep(unsigned int ms) +{ +#ifdef _WIN32 + Sleep(ms); + + + + + +#else + //Single thread sleep code thanks to Furquan Shaikh, http://somethingswhichidintknow.blogspot.com/2009/09/sleep-in-pthread.html + //Modified slightly from the original + pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER; + struct timespec timeToWait; + struct timeval now; + int rt; + + gettimeofday(&now,NULL); + + long seconds = ms/1000; + long nanoseconds = (ms - seconds * 1000) * 1000000; + timeToWait.tv_sec = now.tv_sec + seconds; + timeToWait.tv_nsec = now.tv_usec*1000 + nanoseconds; + + if (timeToWait.tv_nsec >= 1000000000) + { + timeToWait.tv_nsec -= 1000000000; + timeToWait.tv_sec++; + } + + pthread_mutex_lock(&fakeMutex); + rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait); + pthread_mutex_unlock(&fakeMutex); +#endif +} diff --git a/src/raknet/RakSleep.h b/src/raknet/RakSleep.h index f1a4148..27936bf 100755 --- a/src/raknet/RakSleep.h +++ b/src/raknet/RakSleep.h @@ -1,8 +1,8 @@ -#ifndef __RAK_SLEEP_H -#define __RAK_SLEEP_H - -#include "Export.h" - -void RAK_DLL_EXPORT RakSleep(unsigned int ms); - -#endif +#ifndef __RAK_SLEEP_H +#define __RAK_SLEEP_H + +#include "Export.h" + +void RAK_DLL_EXPORT RakSleep(unsigned int ms); + +#endif diff --git a/src/raknet/RakString.cpp b/src/raknet/RakString.cpp index b51c955..9c54eca 100755 --- a/src/raknet/RakString.cpp +++ b/src/raknet/RakString.cpp @@ -1,1422 +1,1422 @@ -#include "RakString.h" -#include "RakAssert.h" -#include "RakMemoryOverride.h" -#include "BitStream.h" -#include -#include -#include "LinuxStrings.h" -#include "StringCompressor.h" -#include "SimpleMutex.h" - -using namespace RakNet; - -//DataStructures::MemoryPool RakString::pool; -RakString::SharedString RakString::emptyString={0,0,0,(char*) "",(char*) ""}; -//RakString::SharedString *RakString::sharedStringFreeList=0; -//unsigned int RakString::sharedStringFreeListAllocationCount=0; -DataStructures::List RakString::freeList; - -class RakStringCleanup -{ -public: - ~RakStringCleanup() - { - RakNet::RakString::FreeMemoryNoMutex(); - } -}; - -static RakStringCleanup cleanup; - -SimpleMutex& GetPoolMutex(void) -{ - static SimpleMutex poolMutex; - return poolMutex; -} - -int RakNet::RakString::RakStringComp( RakString const &key, RakString const &data ) -{ - return key.StrCmp(data); -} - -RakString::RakString() -{ - sharedString=&emptyString; -} -RakString::RakString( RakString::SharedString *_sharedString ) -{ - sharedString=_sharedString; -} -RakString::RakString(char input) -{ - char str[2]; - str[0]=input; - str[1]=0; - Assign(str); -} -RakString::RakString(unsigned char input) -{ - char str[2]; - str[0]=(char) input; - str[1]=0; - Assign(str); -} -RakString::RakString(const unsigned char *format, ...){ - va_list ap; - va_start(ap, format); - Assign((const char*) format,ap); -} -RakString::RakString(const char *format, ...){ - va_list ap; - va_start(ap, format); - Assign(format,ap); -} -RakString::RakString( const RakString & rhs) -{ - if (rhs.sharedString==&emptyString) - { - sharedString=&emptyString; - return; - } - - rhs.sharedString->refCountMutex->Lock(); - if (rhs.sharedString->refCount==0) - { - sharedString=&emptyString; - } - else - { - rhs.sharedString->refCount++; - sharedString=rhs.sharedString; - } - rhs.sharedString->refCountMutex->Unlock(); -} -RakString::~RakString() -{ - Free(); -} -RakString& RakString::operator = ( const RakString& rhs ) -{ - Free(); - if (rhs.sharedString==&emptyString) - return *this; - - rhs.sharedString->refCountMutex->Lock(); - if (rhs.sharedString->refCount==0) - { - sharedString=&emptyString; - } - else - { - sharedString=rhs.sharedString; - sharedString->refCount++; - } - rhs.sharedString->refCountMutex->Unlock(); - return *this; -} -RakString& RakString::operator = ( const char *str ) -{ - Free(); - Assign(str); - return *this; -} -RakString& RakString::operator = ( char *str ) -{ - return operator = ((const char*)str); -} -RakString& RakString::operator = ( const unsigned char *str ) -{ - return operator = ((const char*)str); -} -RakString& RakString::operator = ( char unsigned *str ) -{ - return operator = ((const char*)str); -} -RakString& RakString::operator = ( const char c ) -{ - char buff[2]; - buff[0]=c; - buff[1]=0; - return operator = ((const char*)buff); -} -void RakString::Realloc(SharedString *sharedString, size_t bytes) -{ - if (bytes<=sharedString->bytesUsed) - return; - RakAssert(bytes>0); - size_t oldBytes = sharedString->bytesUsed; - size_t newBytes; - const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - newBytes = GetSizeToAllocate(bytes); - if (oldBytes <=(size_t) smallStringSize && newBytes > (size_t) smallStringSize) - { - sharedString->bigString=(char*) rakMalloc_Ex(newBytes, _FILE_AND_LINE_); - strcpy(sharedString->bigString, sharedString->smallString); - sharedString->c_str=sharedString->bigString; - } - else if (oldBytes > smallStringSize) - { - sharedString->bigString=(char*) rakRealloc_Ex(sharedString->bigString,newBytes, _FILE_AND_LINE_); - sharedString->c_str=sharedString->bigString; - } - sharedString->bytesUsed=newBytes; -} -RakString& RakString::operator +=( const RakString& rhs) -{ - if (rhs.IsEmpty()) - return *this; - - if (IsEmpty()) - { - return operator=(rhs); - } - else - { - Clone(); - size_t strLen=rhs.GetLength()+GetLength()+1; - Realloc(sharedString, strLen+GetLength()); - strcat(sharedString->c_str,rhs.C_String()); - } - return *this; -} -RakString& RakString::operator +=( const char *str ) -{ - if (str==0 || str[0]==0) - return *this; - - if (IsEmpty()) - { - Assign(str); - } - else - { - Clone(); - size_t strLen=strlen(str)+GetLength()+1; - Realloc(sharedString, strLen); - strcat(sharedString->c_str,str); - } - return *this; -} -RakString& RakString::operator +=( char *str ) -{ - return operator += ((const char*)str); -} -RakString& RakString::operator +=( const unsigned char *str ) -{ - return operator += ((const char*)str); -} -RakString& RakString::operator +=( unsigned char *str ) -{ - return operator += ((const char*)str); -} -RakString& RakString::operator +=( const char c ) -{ - char buff[2]; - buff[0]=c; - buff[1]=0; - return operator += ((const char*)buff); -} -unsigned char RakString::operator[] ( const unsigned int position ) const -{ - RakAssert(positionc_str[position]; -} -bool RakString::operator==(const RakString &rhs) const -{ - return strcmp(sharedString->c_str,rhs.sharedString->c_str)==0; -} -bool RakString::operator==(const char *str) const -{ - return strcmp(sharedString->c_str,str)==0; -} -bool RakString::operator==(char *str) const -{ - return strcmp(sharedString->c_str,str)==0; -} -bool RakString::operator < ( const RakString& right ) const -{ - return strcmp(sharedString->c_str,right.C_String()) < 0; -} -bool RakString::operator <= ( const RakString& right ) const -{ - return strcmp(sharedString->c_str,right.C_String()) <= 0; -} -bool RakString::operator > ( const RakString& right ) const -{ - return strcmp(sharedString->c_str,right.C_String()) > 0; -} -bool RakString::operator >= ( const RakString& right ) const -{ - return strcmp(sharedString->c_str,right.C_String()) >= 0; -} -bool RakString::operator!=(const RakString &rhs) const -{ - return strcmp(sharedString->c_str,rhs.sharedString->c_str)!=0; -} -bool RakString::operator!=(const char *str) const -{ - return strcmp(sharedString->c_str,str)!=0; -} -bool RakString::operator!=(char *str) const -{ - return strcmp(sharedString->c_str,str)!=0; -} -const RakNet::RakString operator+(const RakNet::RakString &lhs, const RakNet::RakString &rhs) -{ - if (lhs.IsEmpty() && rhs.IsEmpty()) - { - return RakString(&RakString::emptyString); - } - if (lhs.IsEmpty()) - { - rhs.sharedString->refCountMutex->Lock(); - if (rhs.sharedString->refCount==0) - { - rhs.sharedString->refCountMutex->Unlock(); - lhs.sharedString->refCountMutex->Lock(); - lhs.sharedString->refCount++; - lhs.sharedString->refCountMutex->Unlock(); - return RakString(lhs.sharedString); - } - else - { - rhs.sharedString->refCount++; - rhs.sharedString->refCountMutex->Unlock(); - return RakString(rhs.sharedString); - } - // rhs.sharedString->refCountMutex->Unlock(); - } - if (rhs.IsEmpty()) - { - lhs.sharedString->refCountMutex->Lock(); - lhs.sharedString->refCount++; - lhs.sharedString->refCountMutex->Unlock(); - return RakString(lhs.sharedString); - } - - size_t len1 = lhs.GetLength(); - size_t len2 = rhs.GetLength(); - size_t allocatedBytes = len1 + len2 + 1; - allocatedBytes = RakString::GetSizeToAllocate(allocatedBytes); - RakString::SharedString *sharedString; - - RakString::LockMutex(); - // sharedString = RakString::pool.Allocate( _FILE_AND_LINE_ ); - if (RakString::freeList.Size()==0) - { - //RakString::sharedStringFreeList=(RakString::SharedString*) rakRealloc_Ex(RakString::sharedStringFreeList,(RakString::sharedStringFreeListAllocationCount+1024)*sizeof(RakString::SharedString), _FILE_AND_LINE_); - unsigned i; - for (i=0; i < 128; i++) - { - // RakString::freeList.Insert(RakString::sharedStringFreeList+i+RakString::sharedStringFreeListAllocationCount); - RakString::SharedString *ss; - ss = (RakString::SharedString*) rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_); - ss->refCountMutex=RakNet::OP_NEW(_FILE_AND_LINE_); - RakString::freeList.Insert(ss, _FILE_AND_LINE_); - - } - //RakString::sharedStringFreeListAllocationCount+=1024; - } - sharedString = RakString::freeList[RakString::freeList.Size()-1]; - RakString::freeList.RemoveAtIndex(RakString::freeList.Size()-1); - RakString::UnlockMutex(); - - const int smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - sharedString->bytesUsed=allocatedBytes; - sharedString->refCount=1; - if (allocatedBytes <= (size_t) smallStringSize) - { - sharedString->c_str=sharedString->smallString; - } - else - { - sharedString->bigString=(char*)rakMalloc_Ex(sharedString->bytesUsed, _FILE_AND_LINE_); - sharedString->c_str=sharedString->bigString; - } - - strcpy(sharedString->c_str, lhs); - strcat(sharedString->c_str, rhs); - - return RakString(sharedString); -} -const char * RakString::ToLower(void) -{ - Clone(); - - size_t strLen = strlen(sharedString->c_str); - unsigned i; - for (i=0; i < strLen; i++) - sharedString->c_str[i]=ToLower(sharedString->c_str[i]); - return sharedString->c_str; -} -const char * RakString::ToUpper(void) -{ - Clone(); - - size_t strLen = strlen(sharedString->c_str); - unsigned i; - for (i=0; i < strLen; i++) - sharedString->c_str[i]=ToUpper(sharedString->c_str[i]); - return sharedString->c_str; -} -void RakString::Set(const char *format, ...) -{ - va_list ap; - va_start(ap, format); - Clear(); - Assign(format,ap); -} -bool RakString::IsEmpty(void) const -{ - return sharedString==&emptyString; -} -size_t RakString::GetLength(void) const -{ - return strlen(sharedString->c_str); -} -// http://porg.es/blog/counting-characters-in-utf-8-strings-is-faster -int porges_strlen2(char *s) -{ - int i = 0; - int iBefore = 0; - int count = 0; - - while (s[i] > 0) -ascii: i++; - - count += i-iBefore; - while (s[i]) - { - if (s[i] > 0) - { - iBefore = i; - goto ascii; - } - else - switch (0xF0 & s[i]) - { - case 0xE0: i += 3; break; - case 0xF0: i += 4; break; - default: i += 2; break; - } - ++count; - } - return count; -} -size_t RakString::GetLengthUTF8(void) const -{ - return porges_strlen2(sharedString->c_str); -} -void RakString::Replace(unsigned index, unsigned count, unsigned char c) -{ - RakAssert(index+count < GetLength()); - Clone(); - unsigned countIndex=0; - while (countIndexc_str[index]=c; - index++; - countIndex++; - } -} -void RakString::SetChar( unsigned index, unsigned char c ) -{ - RakAssert(index < GetLength()); - Clone(); - sharedString->c_str[index]=c; -} -void RakString::SetChar( unsigned index, RakNet::RakString s ) -{ - RakAssert(index < GetLength()); - Clone(); - RakNet::RakString firstHalf = SubStr(0, index); - RakNet::RakString secondHalf = SubStr(index+1, (unsigned int)-1); - *this = firstHalf; - *this += s; - *this += secondHalf; -} - -#ifdef _WIN32 -WCHAR * RakString::ToWideChar(void) -{ - // - // Special case of NULL or empty input string - // - if ( (sharedString->c_str == NULL) || (*sharedString->c_str == '\0') ) - { - // Return empty string - return L""; - } - - // - // Get size of destination UTF-16 buffer, in WCHAR's - // - int cchUTF16 = ::MultiByteToWideChar( - CP_UTF8, // convert from UTF-8 - 0, // Flags - sharedString->c_str, // source UTF-8 string - GetLength()+1, // total length of source UTF-8 string, - // in CHAR's (= bytes), including end-of-string \0 - NULL, // unused - no conversion done in this step - 0 // request size of destination buffer, in WCHAR's - ); - - if ( cchUTF16 == 0 ) - { - RakAssert("RakString::ToWideChar exception from cchUTF16==0" && 0); - return 0; - } - - // - // Allocate destination buffer to store UTF-16 string - // - WCHAR * pszUTF16 = RakNet::OP_NEW_ARRAY(cchUTF16,__FILE__,__LINE__); - - // - // Do the conversion from UTF-8 to UTF-16 - // - int result = ::MultiByteToWideChar( - CP_UTF8, // convert from UTF-8 - 0, // Buffer - sharedString->c_str, // source UTF-8 string - GetLength()+1, // total length of source UTF-8 string, - // in CHAR's (= bytes), including end-of-string \0 - pszUTF16, // destination buffer - cchUTF16 // size of destination buffer, in WCHAR's - ); - - if ( result == 0 ) - { - RakAssert("RakString::ToWideChar exception from MultiByteToWideChar" && 0); - return 0; - } - - return pszUTF16; -} -void RakString::DeallocWideChar(WCHAR * w) -{ - RakNet::OP_DELETE_ARRAY(w,__FILE__,__LINE__); -} -#endif - -size_t RakString::Find(const char *stringToFind,size_t pos) -{ - size_t len=GetLength(); - if (pos>=len || stringToFind==0 || stringToFind[0]==0) - { - return (size_t) -1; - } - size_t matchLen= strlen(stringToFind); - size_t matchPos=0; - size_t iStart=0; - - for (size_t i=pos;ic_str[i]) - { - if(matchPos==0) - { - iStart=i; - } - matchPos++; - } - else - { - matchPos=0; - } - - if (matchPos>=matchLen) - { - return iStart; - } - } - - return (size_t) -1; -} - -void RakString::TruncateUTF8(unsigned int length) -{ - int i = 0; - unsigned int count = 0; - - while (sharedString->c_str[i]!=0) - { - if (count==length) - { - sharedString->c_str[i]=0; - return; - } - else if (sharedString->c_str[i]>0) - { - i++; - } - else - { - switch (0xF0 & sharedString->c_str[i]) - { - case 0xE0: i += 3; break; - case 0xF0: i += 4; break; - default: i += 2; break; - } - } - - count++; - } -} - -void RakString::Truncate(unsigned int length) -{ - if (length < GetLength()) - { - SetChar(length, 0); - } -} - -RakString RakString::SubStr(unsigned int index, unsigned int count) const -{ - size_t length = GetLength(); - if (index >= length || count==0) - return RakString(); - RakString copy; - size_t numBytes = length-index; - if (count < numBytes) - numBytes=count; - copy.Allocate(numBytes+1); - size_t i; - for (i=0; i < numBytes; i++) - copy.sharedString->c_str[i]=sharedString->c_str[index+i]; - copy.sharedString->c_str[i]=0; - return copy; -} -void RakString::Erase(unsigned int index, unsigned int count) -{ - size_t len = GetLength(); - RakAssert(index+count <= len); - - Clone(); - unsigned i; - for (i=index; i < len-count; i++) - { - sharedString->c_str[i]=sharedString->c_str[i+count]; - } - sharedString->c_str[i]=0; -} -void RakString::TerminateAtLastCharacter(char c) -{ - int i, len=(int) GetLength(); - for (i=len-1; i >= 0; i--) - { - if (sharedString->c_str[i]==c) - { - Clone(); - sharedString->c_str[i]=0; - return; - } - } -} -void RakString::TerminateAtFirstCharacter(char c) -{ - unsigned int i, len=(unsigned int) GetLength(); - for (i=0; i < len; i++) - { - if (sharedString->c_str[i]==c) - { - Clone(); - sharedString->c_str[i]=0; - return; - } - } -} -void RakString::RemoveCharacter(char c) -{ - if (c==0) - return; - - unsigned int readIndex, writeIndex=0; - for (readIndex=0; sharedString->c_str[readIndex]; readIndex++) - { - if (sharedString->c_str[readIndex]!=c) - sharedString->c_str[writeIndex++]=sharedString->c_str[readIndex]; - else - Clone(); - } - sharedString->c_str[writeIndex]=0; -} -int RakString::StrCmp(const RakString &rhs) const -{ - return strcmp(sharedString->c_str, rhs); -} -int RakString::StrICmp(const RakString &rhs) const -{ - return _stricmp(sharedString->c_str, rhs); -} -void RakString::Printf(void) -{ - RAKNET_DEBUG_PRINTF("%s", sharedString->c_str); -} -void RakString::FPrintf(FILE *fp) -{ - fprintf(fp,"%s", sharedString->c_str); -} -bool RakString::IPAddressMatch(const char *IP) -{ - unsigned characterIndex; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return false; - - characterIndex = 0; - -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - while ( true ) - { - if (sharedString->c_str[ characterIndex ] == IP[ characterIndex ] ) - { - // Equal characters - if ( IP[ characterIndex ] == 0 ) - { - // End of the string and the strings match - - return true; - } - - characterIndex++; - } - - else - { - if ( sharedString->c_str[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) - { - // End of one of the strings - break; - } - - // Characters do not match - if ( sharedString->c_str[ characterIndex ] == '*' ) - { - // Domain is banned. - return true; - } - - // Characters do not match and it is not a * - break; - } - } - - - // No match found. - return false; -} -bool RakString::ContainsNonprintableExceptSpaces(void) const -{ - size_t strLen = strlen(sharedString->c_str); - unsigned i; - for (i=0; i < strLen; i++) - { - if (sharedString->c_str[i] < ' ' || sharedString->c_str[i] >126) - return true; - } - return false; -} -bool RakString::IsEmailAddress(void) const -{ - if (IsEmpty()) - return false; - size_t strLen = strlen(sharedString->c_str); - if (strLen < 6) // a@b.de - return false; - if (sharedString->c_str[strLen-4]!='.' && sharedString->c_str[strLen-3]!='.') // .com, .net., .org, .de - return false; - unsigned i; - // Has non-printable? - for (i=0; i < strLen; i++) - { - if (sharedString->c_str[i] <= ' ' || sharedString->c_str[i] >126) - return false; - } - int atCount=0; - for (i=0; i < strLen; i++) - { - if (sharedString->c_str[i]=='@') - { - atCount++; - } - } - if (atCount!=1) - return false; - int dotCount=0; - for (i=0; i < strLen; i++) - { - if (sharedString->c_str[i]=='.') - { - dotCount++; - } - } - if (dotCount==0) - return false; - - // There's more I could check, but this is good enough - return true; -} -RakNet::RakString& RakString::URLEncode(void) -{ - RakString result; - size_t strLen = strlen(sharedString->c_str); - result.Allocate(strLen*3); - char *output=result.sharedString->c_str; - unsigned int outputIndex=0; - unsigned i; - char c; - for (i=0; i < strLen; i++) - { - c=sharedString->c_str[i]; - if ( - (c<=47) || - (c>=58 && c<=64) || - (c>=91 && c<=96) || - (c>=123) - ) - { - RakNet::RakString tmp("%2X", c); - output[outputIndex++]='%'; - output[outputIndex++]=tmp.sharedString->c_str[0]; - output[outputIndex++]=tmp.sharedString->c_str[1]; - } - else - { - output[outputIndex++]=c; - } - } - - output[outputIndex]=0; - - *this = result; - return *this; -} -RakNet::RakString& RakString::URLDecode(void) -{ - RakString result; - size_t strLen = strlen(sharedString->c_str); - result.Allocate(strLen); - char *output=result.sharedString->c_str; - unsigned int outputIndex=0; - char c; - char hexDigits[2]; - char hexValues[2]; - unsigned int i; - for (i=0; i < strLen; i++) - { - c=sharedString->c_str[i]; - if (c=='%') - { - hexDigits[0]=sharedString->c_str[++i]; - hexDigits[1]=sharedString->c_str[++i]; - if (hexDigits[0]==' ') - hexValues[0]=0; - else if (hexDigits[0]>='A') - hexValues[0]=hexDigits[0]-'A'+10; - else - hexValues[0]=hexDigits[0]-'0'; - if (hexDigits[1]>='A') - hexValues[1]=hexDigits[1]-'A'+10; - else - hexValues[1]=hexDigits[1]-'0'; - output[outputIndex++]=hexValues[0]*16+hexValues[1]; - } - else - { - output[outputIndex++]=c; - } - } - - output[outputIndex]=0; - - *this = result; - return *this; -} -void RakString::SplitURI(RakNet::RakString &header, RakNet::RakString &domain, RakNet::RakString &path) -{ - header.Clear(); - domain.Clear(); - path.Clear(); - - size_t strLen = strlen(sharedString->c_str); - - char c; - unsigned int i=0; - if (strncmp(sharedString->c_str, "http://", 7)==0) - i+=(unsigned int) strlen("http://"); - else if (strncmp(sharedString->c_str, "https://", 8)==0) - i+=(unsigned int) strlen("https://"); - - if (strncmp(sharedString->c_str, "www.", 4)==0) - i+=(unsigned int) strlen("www."); - - if (i!=0) - { - header.Allocate(i+1); - strncpy(header.sharedString->c_str, sharedString->c_str, i); - header.sharedString->c_str[i]=0; - } - - - domain.Allocate(strLen-i+1); - char *domainOutput=domain.sharedString->c_str; - unsigned int outputIndex=0; - for (; i < strLen; i++) - { - c=sharedString->c_str[i]; - if (c=='/') - { - break; - } - else - { - domainOutput[outputIndex++]=sharedString->c_str[i]; - } - } - - domainOutput[outputIndex]=0; - - path.Allocate(strLen-header.GetLength()-outputIndex+1); - outputIndex=0; - char *pathOutput=path.sharedString->c_str; - for (; i < strLen; i++) - { - pathOutput[outputIndex++]=sharedString->c_str[i]; - } - pathOutput[outputIndex]=0; -} -RakNet::RakString& RakString::SQLEscape(void) -{ - int strLen=(int)GetLength(); - int escapedCharacterCount=0; - int index; - for (index=0; index < strLen; index++) - { - if (sharedString->c_str[index]=='\'' || - sharedString->c_str[index]=='"' || - sharedString->c_str[index]=='\\') - escapedCharacterCount++; - } - if (escapedCharacterCount==0) - return *this; - - Clone(); - Realloc(sharedString, strLen+escapedCharacterCount); - int writeIndex, readIndex; - writeIndex = strLen+escapedCharacterCount; - readIndex=strLen; - while (readIndex>=0) - { - if (sharedString->c_str[readIndex]=='\'' || - sharedString->c_str[readIndex]=='"' || - sharedString->c_str[readIndex]=='\\') - { - sharedString->c_str[writeIndex--]=sharedString->c_str[readIndex--]; - sharedString->c_str[writeIndex--]='\\'; - } - else - { - sharedString->c_str[writeIndex--]=sharedString->c_str[readIndex--]; - } - } - return *this; -} -RakNet::RakString& RakString::MakeFilePath(void) -{ - if (IsEmpty()) - return *this; - - RakNet::RakString fixedString = *this; - fixedString.Clone(); - for (int i=0; fixedString.sharedString->c_str[i]; i++) - { -#ifdef _WIN32 - if (fixedString.sharedString->c_str[i]=='/') - fixedString.sharedString->c_str[i]='\\'; -#else - if (fixedString.sharedString->c_str[i]=='\\') - fixedString.sharedString->c_str[i]='/'; -#endif - } - -#ifdef _WIN32 - if (fixedString.sharedString->c_str[strlen(fixedString.sharedString->c_str)-1]!='\\') - { - fixedString+='\\'; - } -#else - if (fixedString.sharedString->c_str[strlen(fixedString.sharedString->c_str)-1]!='/') - { - fixedString+='/'; - } -#endif - - if (fixedString!=*this) - *this = fixedString; - return *this; -} -void RakString::FreeMemory(void) -{ - LockMutex(); - FreeMemoryNoMutex(); - UnlockMutex(); -} -void RakString::FreeMemoryNoMutex(void) -{ - for (unsigned int i=0; i < freeList.Size(); i++) - { - RakNet::OP_DELETE(freeList[i]->refCountMutex,_FILE_AND_LINE_); - rakFree_Ex(freeList[i], _FILE_AND_LINE_ ); - } - freeList.Clear(false, _FILE_AND_LINE_); -} -void RakString::Serialize(BitStream *bs) const -{ - Serialize(sharedString->c_str, bs); -} -void RakString::Serialize(const char *str, BitStream *bs) -{ - unsigned short l = (unsigned short) strlen(str); - bs->Write(l); - bs->WriteAlignedBytes((const unsigned char*) str, (const unsigned int) l); -} -void RakString::SerializeCompressed(BitStream *bs, uint8_t languageId, bool writeLanguageId) const -{ - SerializeCompressed(C_String(), bs, languageId, writeLanguageId); -} -void RakString::SerializeCompressed(const char *str, BitStream *bs, uint8_t languageId, bool writeLanguageId) -{ - if (writeLanguageId) - bs->WriteCompressed(languageId); - StringCompressor::Instance()->EncodeString(str,0xFFFF,bs,languageId); -} -bool RakString::Deserialize(BitStream *bs) -{ - Clear(); - - bool b; - unsigned short l; - b=bs->Read(l); - if (l>0) - { - Allocate(((unsigned int) l)+1); - b=bs->ReadAlignedBytes((unsigned char*) sharedString->c_str, l); - if (b) - sharedString->c_str[l]=0; - else - Clear(); - } - else - bs->AlignReadToByteBoundary(); - return b; -} -bool RakString::Deserialize(char *str, BitStream *bs) -{ - bool b; - unsigned short l; - b=bs->Read(l); - if (b && l>0) - b=bs->ReadAlignedBytes((unsigned char*) str, l); - - if (b==false) - str[0]=0; - - str[l]=0; - return b; -} -bool RakString::DeserializeCompressed(BitStream *bs, bool readLanguageId) -{ - uint8_t languageId; - if (readLanguageId) - bs->ReadCompressed(languageId); - else - languageId=0; - return StringCompressor::Instance()->DecodeString(this,0xFFFF,bs,languageId); -} -bool RakString::DeserializeCompressed(char *str, BitStream *bs, bool readLanguageId) -{ - uint8_t languageId; - if (readLanguageId) - bs->ReadCompressed(languageId); - else - languageId=0; - return StringCompressor::Instance()->DecodeString(str,0xFFFF,bs,languageId); -} -const char *RakString::ToString(int64_t i) -{ - static int index=0; - static char buff[64][64]; -#if defined(_WIN32) - sprintf(buff[index], "%I64d", i); -#else - sprintf(buff[index], "%lld", (long long unsigned int) i); -#endif - int lastIndex=index; - if (++index==64) - index=0; - return buff[lastIndex]; -} -const char *RakString::ToString(uint64_t i) -{ - static int index=0; - static char buff[64][64]; -#if defined(_WIN32) - sprintf(buff[index], "%I64u", i); -#else - sprintf(buff[index], "%llu", (long long unsigned int) i); -#endif - int lastIndex=index; - if (++index==64) - index=0; - return buff[lastIndex]; -} -void RakString::Clear(void) -{ - Free(); -} -void RakString::Allocate(size_t len) -{ - RakString::LockMutex(); - // sharedString = RakString::pool.Allocate( _FILE_AND_LINE_ ); - if (RakString::freeList.Size()==0) - { - //RakString::sharedStringFreeList=(RakString::SharedString*) rakRealloc_Ex(RakString::sharedStringFreeList,(RakString::sharedStringFreeListAllocationCount+1024)*sizeof(RakString::SharedString), _FILE_AND_LINE_); - unsigned i; - for (i=0; i < 128; i++) - { - // RakString::freeList.Insert(RakString::sharedStringFreeList+i+RakString::sharedStringFreeListAllocationCount); - // RakString::freeList.Insert((RakString::SharedString*)rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_), _FILE_AND_LINE_); - - RakString::SharedString *ss; - ss = (RakString::SharedString*) rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_); - ss->refCountMutex=RakNet::OP_NEW(_FILE_AND_LINE_); - RakString::freeList.Insert(ss, _FILE_AND_LINE_); - } - //RakString::sharedStringFreeListAllocationCount+=1024; - } - sharedString = RakString::freeList[RakString::freeList.Size()-1]; - RakString::freeList.RemoveAtIndex(RakString::freeList.Size()-1); - RakString::UnlockMutex(); - - const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - sharedString->refCount=1; - if (len <= smallStringSize) - { - sharedString->bytesUsed=smallStringSize; - sharedString->c_str=sharedString->smallString; - } - else - { - sharedString->bytesUsed=len<<1; - sharedString->bigString=(char*)rakMalloc_Ex(sharedString->bytesUsed, _FILE_AND_LINE_); - sharedString->c_str=sharedString->bigString; - } -} -void RakString::Assign(const char *str) -{ - if (str==0 || str[0]==0) - { - sharedString=&emptyString; - return; - } - - size_t len = strlen(str)+1; - Allocate(len); - memcpy(sharedString->c_str, str, len); -} -void RakString::Assign(const char *str, va_list ap) -{ - char stackBuff[512]; - if (_vsnprintf(stackBuff, 512, str, ap)!=-1 -#ifndef _WIN32 - // Here Windows will return -1 if the string is too long; Linux just truncates the string. - && strlen(str) <511 -#endif - ) - { - Assign(stackBuff); - return; - } - char *buff=0, *newBuff; - size_t buffSize=8096; - while (1) - { - newBuff = (char*) rakRealloc_Ex(buff, buffSize,__FILE__,__LINE__); - if (newBuff==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - if (buff!=0) - { - Assign(buff); - rakFree_Ex(buff,__FILE__,__LINE__); - } - else - { - Assign(stackBuff); - } - return; - } - buff=newBuff; - if (_vsnprintf(buff, buffSize, str, ap)!=-1) - { - Assign(buff); - rakFree_Ex(buff,__FILE__,__LINE__); - return; - } - buffSize*=2; - } -} -RakNet::RakString RakString::Assign(const char *str,size_t pos, size_t n ) -{ - size_t incomingLen=strlen(str); - - Clone(); - - if (str==0 || str[0]==0||pos>=incomingLen) - { - sharedString=&emptyString; - return (*this); - } - - if (pos+n>=incomingLen) - { - n=incomingLen-pos; - - } - const char * tmpStr=&(str[pos]); - - size_t len = n+1; - Allocate(len); - memcpy(sharedString->c_str, tmpStr, len); - sharedString->c_str[n]=0; - - return (*this); -} - -RakNet::RakString RakString::NonVariadic(const char *str) -{ - RakNet::RakString rs; - rs=str; - return rs; -} -unsigned long RakString::ToInteger(const char *str) -{ - unsigned long hash = 0; - int c; - - while ((c = *str++)) - hash = c + (hash << 6) + (hash << 16) - hash; - - return hash; -} -unsigned long RakString::ToInteger(const RakString &rs) -{ - return RakString::ToInteger(rs.C_String()); -} -void RakString::AppendBytes(const char *bytes, unsigned int count) -{ - Clone(); - Realloc(sharedString, count); - unsigned int length=(unsigned int) GetLength(); - memcpy(sharedString->c_str+length, bytes, count); - sharedString->c_str[length+count]=0; -} -void RakString::Clone(void) -{ - if (sharedString==&emptyString) - { - return; - } - - // Empty or solo then no point to cloning - sharedString->refCountMutex->Lock(); - if (sharedString->refCount==1) - { - sharedString->refCountMutex->Unlock(); - return; - } - - sharedString->refCount--; - sharedString->refCountMutex->Unlock(); - Assign(sharedString->c_str); -} -void RakString::Free(void) -{ - if (sharedString==&emptyString) - return; - sharedString->refCountMutex->Lock(); - sharedString->refCount--; - if (sharedString->refCount==0) - { - sharedString->refCountMutex->Unlock(); - const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - if (sharedString->bytesUsed>smallStringSize) - rakFree_Ex(sharedString->bigString, _FILE_AND_LINE_ ); - /* - poolMutex->Lock(); - pool.Release(sharedString); - poolMutex->Unlock(); - */ - - RakString::LockMutex(); - RakString::freeList.Insert(sharedString, _FILE_AND_LINE_); - RakString::UnlockMutex(); - - sharedString=&emptyString; - } - else - { - sharedString->refCountMutex->Unlock(); - } - sharedString=&emptyString; -} -unsigned char RakString::ToLower(unsigned char c) -{ - if (c >= 'A' && c <= 'Z') - return c-'A'+'a'; - return c; -} -unsigned char RakString::ToUpper(unsigned char c) -{ - if (c >= 'a' && c <= 'z') - return c-'a'+'A'; - return c; -} -void RakString::LockMutex(void) -{ - GetPoolMutex().Lock(); -} -void RakString::UnlockMutex(void) -{ - GetPoolMutex().Unlock(); -} - -/* -#include "RakString.h" -#include -#include "GetTime.h" - -using namespace RakNet; - -int main(void) -{ - RakString s3("Hello world"); - RakString s5=s3; - - RakString s1; - RakString s2('a'); - - RakString s4("%i %f", 5, 6.0); - - RakString s6=s3; - RakString s7=s6; - RakString s8=s6; - RakString s9; - s9=s9; - RakString s10(s3); - RakString s11=s10 + s4 + s9 + s2; - s11+=RakString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - RakString s12("Test"); - s12+=s11; - bool b1 = s12==s12; - s11=s5; - s12.ToUpper(); - s12.ToLower(); - RakString s13; - bool b3 = s13.IsEmpty(); - s13.Set("blah %s", s12.C_String()); - bool b4 = s13.IsEmpty(); - size_t i1=s13.GetLength(); - s3.Clear(_FILE_AND_LINE_); - s4.Clear(_FILE_AND_LINE_); - s5.Clear(_FILE_AND_LINE_); - s5.Clear(_FILE_AND_LINE_); - s6.Printf(); - s7.Printf(); - RAKNET_DEBUG_PRINTF("\n"); - - static const int repeatCount=750; - DataStructures::List rakStringList; - DataStructures::List stdStringList; - DataStructures::List referenceStringList; - char *c; - unsigned i; - RakNet::TimeMS beforeReferenceList, beforeRakString, beforeStdString, afterStdString; - - unsigned loop; - for (loop=0; loop<2; loop++) - { - beforeReferenceList=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - { - c = RakNet::OP_NEW_ARRAY(56,_FILE_AND_LINE_ ); - strcpy(c, "Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - referenceStringList.Insert(c); - } - beforeRakString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - rakStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - beforeStdString=RakNet::GetTimeMS(); - - for (i=0; i < repeatCount; i++) - stdStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - afterStdString=RakNet::GetTimeMS(); - RAKNET_DEBUG_PRINTF("Insertion 1 Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); - - beforeReferenceList=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - { - RakNet::OP_DELETE_ARRAY(referenceStringList[0], _FILE_AND_LINE_); - referenceStringList.RemoveAtIndex(0); - } - beforeRakString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - rakStringList.RemoveAtIndex(0); - beforeStdString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - stdStringList.RemoveAtIndex(0); - afterStdString=RakNet::GetTimeMS(); - RAKNET_DEBUG_PRINTF("RemoveHead Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); - - beforeReferenceList=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - { - c = RakNet::OP_NEW_ARRAY(56, _FILE_AND_LINE_ ); - strcpy(c, "Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - referenceStringList.Insert(0); - } - beforeRakString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - rakStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - beforeStdString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - stdStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); - afterStdString=RakNet::GetTimeMS(); - RAKNET_DEBUG_PRINTF("Insertion 2 Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); - - beforeReferenceList=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - { - RakNet::OP_DELETE_ARRAY(referenceStringList[referenceStringList.Size()-1], _FILE_AND_LINE_); - referenceStringList.RemoveAtIndex(referenceStringList.Size()-1); - } - beforeRakString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - rakStringList.RemoveAtIndex(rakStringList.Size()-1); - beforeStdString=RakNet::GetTimeMS(); - for (i=0; i < repeatCount; i++) - stdStringList.RemoveAtIndex(stdStringList.Size()-1); - afterStdString=RakNet::GetTimeMS(); - RAKNET_DEBUG_PRINTF("RemoveTail Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); - - } - - printf("Done."); - char str[128]; - Gets(str, sizeof(str)); - return 1; -} -*/ +#include "RakString.h" +#include "RakAssert.h" +#include "RakMemoryOverride.h" +#include "BitStream.h" +#include +#include +#include "LinuxStrings.h" +#include "StringCompressor.h" +#include "SimpleMutex.h" + +using namespace RakNet; + +//DataStructures::MemoryPool RakString::pool; +RakString::SharedString RakString::emptyString={0,0,0,(char*) "",(char*) ""}; +//RakString::SharedString *RakString::sharedStringFreeList=0; +//unsigned int RakString::sharedStringFreeListAllocationCount=0; +DataStructures::List RakString::freeList; + +class RakStringCleanup +{ +public: + ~RakStringCleanup() + { + RakNet::RakString::FreeMemoryNoMutex(); + } +}; + +static RakStringCleanup cleanup; + +SimpleMutex& GetPoolMutex(void) +{ + static SimpleMutex poolMutex; + return poolMutex; +} + +int RakNet::RakString::RakStringComp( RakString const &key, RakString const &data ) +{ + return key.StrCmp(data); +} + +RakString::RakString() +{ + sharedString=&emptyString; +} +RakString::RakString( RakString::SharedString *_sharedString ) +{ + sharedString=_sharedString; +} +RakString::RakString(char input) +{ + char str[2]; + str[0]=input; + str[1]=0; + Assign(str); +} +RakString::RakString(unsigned char input) +{ + char str[2]; + str[0]=(char) input; + str[1]=0; + Assign(str); +} +RakString::RakString(const unsigned char *format, ...){ + va_list ap; + va_start(ap, format); + Assign((const char*) format,ap); +} +RakString::RakString(const char *format, ...){ + va_list ap; + va_start(ap, format); + Assign(format,ap); +} +RakString::RakString( const RakString & rhs) +{ + if (rhs.sharedString==&emptyString) + { + sharedString=&emptyString; + return; + } + + rhs.sharedString->refCountMutex->Lock(); + if (rhs.sharedString->refCount==0) + { + sharedString=&emptyString; + } + else + { + rhs.sharedString->refCount++; + sharedString=rhs.sharedString; + } + rhs.sharedString->refCountMutex->Unlock(); +} +RakString::~RakString() +{ + Free(); +} +RakString& RakString::operator = ( const RakString& rhs ) +{ + Free(); + if (rhs.sharedString==&emptyString) + return *this; + + rhs.sharedString->refCountMutex->Lock(); + if (rhs.sharedString->refCount==0) + { + sharedString=&emptyString; + } + else + { + sharedString=rhs.sharedString; + sharedString->refCount++; + } + rhs.sharedString->refCountMutex->Unlock(); + return *this; +} +RakString& RakString::operator = ( const char *str ) +{ + Free(); + Assign(str); + return *this; +} +RakString& RakString::operator = ( char *str ) +{ + return operator = ((const char*)str); +} +RakString& RakString::operator = ( const unsigned char *str ) +{ + return operator = ((const char*)str); +} +RakString& RakString::operator = ( char unsigned *str ) +{ + return operator = ((const char*)str); +} +RakString& RakString::operator = ( const char c ) +{ + char buff[2]; + buff[0]=c; + buff[1]=0; + return operator = ((const char*)buff); +} +void RakString::Realloc(SharedString *sharedString, size_t bytes) +{ + if (bytes<=sharedString->bytesUsed) + return; + RakAssert(bytes>0); + size_t oldBytes = sharedString->bytesUsed; + size_t newBytes; + const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + newBytes = GetSizeToAllocate(bytes); + if (oldBytes <=(size_t) smallStringSize && newBytes > (size_t) smallStringSize) + { + sharedString->bigString=(char*) rakMalloc_Ex(newBytes, _FILE_AND_LINE_); + strcpy(sharedString->bigString, sharedString->smallString); + sharedString->c_str=sharedString->bigString; + } + else if (oldBytes > smallStringSize) + { + sharedString->bigString=(char*) rakRealloc_Ex(sharedString->bigString,newBytes, _FILE_AND_LINE_); + sharedString->c_str=sharedString->bigString; + } + sharedString->bytesUsed=newBytes; +} +RakString& RakString::operator +=( const RakString& rhs) +{ + if (rhs.IsEmpty()) + return *this; + + if (IsEmpty()) + { + return operator=(rhs); + } + else + { + Clone(); + size_t strLen=rhs.GetLength()+GetLength()+1; + Realloc(sharedString, strLen+GetLength()); + strcat(sharedString->c_str,rhs.C_String()); + } + return *this; +} +RakString& RakString::operator +=( const char *str ) +{ + if (str==0 || str[0]==0) + return *this; + + if (IsEmpty()) + { + Assign(str); + } + else + { + Clone(); + size_t strLen=strlen(str)+GetLength()+1; + Realloc(sharedString, strLen); + strcat(sharedString->c_str,str); + } + return *this; +} +RakString& RakString::operator +=( char *str ) +{ + return operator += ((const char*)str); +} +RakString& RakString::operator +=( const unsigned char *str ) +{ + return operator += ((const char*)str); +} +RakString& RakString::operator +=( unsigned char *str ) +{ + return operator += ((const char*)str); +} +RakString& RakString::operator +=( const char c ) +{ + char buff[2]; + buff[0]=c; + buff[1]=0; + return operator += ((const char*)buff); +} +unsigned char RakString::operator[] ( const unsigned int position ) const +{ + RakAssert(positionc_str[position]; +} +bool RakString::operator==(const RakString &rhs) const +{ + return strcmp(sharedString->c_str,rhs.sharedString->c_str)==0; +} +bool RakString::operator==(const char *str) const +{ + return strcmp(sharedString->c_str,str)==0; +} +bool RakString::operator==(char *str) const +{ + return strcmp(sharedString->c_str,str)==0; +} +bool RakString::operator < ( const RakString& right ) const +{ + return strcmp(sharedString->c_str,right.C_String()) < 0; +} +bool RakString::operator <= ( const RakString& right ) const +{ + return strcmp(sharedString->c_str,right.C_String()) <= 0; +} +bool RakString::operator > ( const RakString& right ) const +{ + return strcmp(sharedString->c_str,right.C_String()) > 0; +} +bool RakString::operator >= ( const RakString& right ) const +{ + return strcmp(sharedString->c_str,right.C_String()) >= 0; +} +bool RakString::operator!=(const RakString &rhs) const +{ + return strcmp(sharedString->c_str,rhs.sharedString->c_str)!=0; +} +bool RakString::operator!=(const char *str) const +{ + return strcmp(sharedString->c_str,str)!=0; +} +bool RakString::operator!=(char *str) const +{ + return strcmp(sharedString->c_str,str)!=0; +} +const RakNet::RakString operator+(const RakNet::RakString &lhs, const RakNet::RakString &rhs) +{ + if (lhs.IsEmpty() && rhs.IsEmpty()) + { + return RakString(&RakString::emptyString); + } + if (lhs.IsEmpty()) + { + rhs.sharedString->refCountMutex->Lock(); + if (rhs.sharedString->refCount==0) + { + rhs.sharedString->refCountMutex->Unlock(); + lhs.sharedString->refCountMutex->Lock(); + lhs.sharedString->refCount++; + lhs.sharedString->refCountMutex->Unlock(); + return RakString(lhs.sharedString); + } + else + { + rhs.sharedString->refCount++; + rhs.sharedString->refCountMutex->Unlock(); + return RakString(rhs.sharedString); + } + // rhs.sharedString->refCountMutex->Unlock(); + } + if (rhs.IsEmpty()) + { + lhs.sharedString->refCountMutex->Lock(); + lhs.sharedString->refCount++; + lhs.sharedString->refCountMutex->Unlock(); + return RakString(lhs.sharedString); + } + + size_t len1 = lhs.GetLength(); + size_t len2 = rhs.GetLength(); + size_t allocatedBytes = len1 + len2 + 1; + allocatedBytes = RakString::GetSizeToAllocate(allocatedBytes); + RakString::SharedString *sharedString; + + RakString::LockMutex(); + // sharedString = RakString::pool.Allocate( _FILE_AND_LINE_ ); + if (RakString::freeList.Size()==0) + { + //RakString::sharedStringFreeList=(RakString::SharedString*) rakRealloc_Ex(RakString::sharedStringFreeList,(RakString::sharedStringFreeListAllocationCount+1024)*sizeof(RakString::SharedString), _FILE_AND_LINE_); + unsigned i; + for (i=0; i < 128; i++) + { + // RakString::freeList.Insert(RakString::sharedStringFreeList+i+RakString::sharedStringFreeListAllocationCount); + RakString::SharedString *ss; + ss = (RakString::SharedString*) rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_); + ss->refCountMutex=RakNet::OP_NEW(_FILE_AND_LINE_); + RakString::freeList.Insert(ss, _FILE_AND_LINE_); + + } + //RakString::sharedStringFreeListAllocationCount+=1024; + } + sharedString = RakString::freeList[RakString::freeList.Size()-1]; + RakString::freeList.RemoveAtIndex(RakString::freeList.Size()-1); + RakString::UnlockMutex(); + + const int smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + sharedString->bytesUsed=allocatedBytes; + sharedString->refCount=1; + if (allocatedBytes <= (size_t) smallStringSize) + { + sharedString->c_str=sharedString->smallString; + } + else + { + sharedString->bigString=(char*)rakMalloc_Ex(sharedString->bytesUsed, _FILE_AND_LINE_); + sharedString->c_str=sharedString->bigString; + } + + strcpy(sharedString->c_str, lhs); + strcat(sharedString->c_str, rhs); + + return RakString(sharedString); +} +const char * RakString::ToLower(void) +{ + Clone(); + + size_t strLen = strlen(sharedString->c_str); + unsigned i; + for (i=0; i < strLen; i++) + sharedString->c_str[i]=ToLower(sharedString->c_str[i]); + return sharedString->c_str; +} +const char * RakString::ToUpper(void) +{ + Clone(); + + size_t strLen = strlen(sharedString->c_str); + unsigned i; + for (i=0; i < strLen; i++) + sharedString->c_str[i]=ToUpper(sharedString->c_str[i]); + return sharedString->c_str; +} +void RakString::Set(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + Clear(); + Assign(format,ap); +} +bool RakString::IsEmpty(void) const +{ + return sharedString==&emptyString; +} +size_t RakString::GetLength(void) const +{ + return strlen(sharedString->c_str); +} +// http://porg.es/blog/counting-characters-in-utf-8-strings-is-faster +int porges_strlen2(char *s) +{ + int i = 0; + int iBefore = 0; + int count = 0; + + while (s[i] > 0) +ascii: i++; + + count += i-iBefore; + while (s[i]) + { + if (s[i] > 0) + { + iBefore = i; + goto ascii; + } + else + switch (0xF0 & s[i]) + { + case 0xE0: i += 3; break; + case 0xF0: i += 4; break; + default: i += 2; break; + } + ++count; + } + return count; +} +size_t RakString::GetLengthUTF8(void) const +{ + return porges_strlen2(sharedString->c_str); +} +void RakString::Replace(unsigned index, unsigned count, unsigned char c) +{ + RakAssert(index+count < GetLength()); + Clone(); + unsigned countIndex=0; + while (countIndexc_str[index]=c; + index++; + countIndex++; + } +} +void RakString::SetChar( unsigned index, unsigned char c ) +{ + RakAssert(index < GetLength()); + Clone(); + sharedString->c_str[index]=c; +} +void RakString::SetChar( unsigned index, RakNet::RakString s ) +{ + RakAssert(index < GetLength()); + Clone(); + RakNet::RakString firstHalf = SubStr(0, index); + RakNet::RakString secondHalf = SubStr(index+1, (unsigned int)-1); + *this = firstHalf; + *this += s; + *this += secondHalf; +} + +#ifdef _WIN32 +WCHAR * RakString::ToWideChar(void) +{ + // + // Special case of NULL or empty input string + // + if ( (sharedString->c_str == NULL) || (*sharedString->c_str == '\0') ) + { + // Return empty string + return L""; + } + + // + // Get size of destination UTF-16 buffer, in WCHAR's + // + int cchUTF16 = ::MultiByteToWideChar( + CP_UTF8, // convert from UTF-8 + 0, // Flags + sharedString->c_str, // source UTF-8 string + GetLength()+1, // total length of source UTF-8 string, + // in CHAR's (= bytes), including end-of-string \0 + NULL, // unused - no conversion done in this step + 0 // request size of destination buffer, in WCHAR's + ); + + if ( cchUTF16 == 0 ) + { + RakAssert("RakString::ToWideChar exception from cchUTF16==0" && 0); + return 0; + } + + // + // Allocate destination buffer to store UTF-16 string + // + WCHAR * pszUTF16 = RakNet::OP_NEW_ARRAY(cchUTF16,__FILE__,__LINE__); + + // + // Do the conversion from UTF-8 to UTF-16 + // + int result = ::MultiByteToWideChar( + CP_UTF8, // convert from UTF-8 + 0, // Buffer + sharedString->c_str, // source UTF-8 string + GetLength()+1, // total length of source UTF-8 string, + // in CHAR's (= bytes), including end-of-string \0 + pszUTF16, // destination buffer + cchUTF16 // size of destination buffer, in WCHAR's + ); + + if ( result == 0 ) + { + RakAssert("RakString::ToWideChar exception from MultiByteToWideChar" && 0); + return 0; + } + + return pszUTF16; +} +void RakString::DeallocWideChar(WCHAR * w) +{ + RakNet::OP_DELETE_ARRAY(w,__FILE__,__LINE__); +} +#endif + +size_t RakString::Find(const char *stringToFind,size_t pos) +{ + size_t len=GetLength(); + if (pos>=len || stringToFind==0 || stringToFind[0]==0) + { + return (size_t) -1; + } + size_t matchLen= strlen(stringToFind); + size_t matchPos=0; + size_t iStart=0; + + for (size_t i=pos;ic_str[i]) + { + if(matchPos==0) + { + iStart=i; + } + matchPos++; + } + else + { + matchPos=0; + } + + if (matchPos>=matchLen) + { + return iStart; + } + } + + return (size_t) -1; +} + +void RakString::TruncateUTF8(unsigned int length) +{ + int i = 0; + unsigned int count = 0; + + while (sharedString->c_str[i]!=0) + { + if (count==length) + { + sharedString->c_str[i]=0; + return; + } + else if (sharedString->c_str[i]>0) + { + i++; + } + else + { + switch (0xF0 & sharedString->c_str[i]) + { + case 0xE0: i += 3; break; + case 0xF0: i += 4; break; + default: i += 2; break; + } + } + + count++; + } +} + +void RakString::Truncate(unsigned int length) +{ + if (length < GetLength()) + { + SetChar(length, 0); + } +} + +RakString RakString::SubStr(unsigned int index, unsigned int count) const +{ + size_t length = GetLength(); + if (index >= length || count==0) + return RakString(); + RakString copy; + size_t numBytes = length-index; + if (count < numBytes) + numBytes=count; + copy.Allocate(numBytes+1); + size_t i; + for (i=0; i < numBytes; i++) + copy.sharedString->c_str[i]=sharedString->c_str[index+i]; + copy.sharedString->c_str[i]=0; + return copy; +} +void RakString::Erase(unsigned int index, unsigned int count) +{ + size_t len = GetLength(); + RakAssert(index+count <= len); + + Clone(); + unsigned i; + for (i=index; i < len-count; i++) + { + sharedString->c_str[i]=sharedString->c_str[i+count]; + } + sharedString->c_str[i]=0; +} +void RakString::TerminateAtLastCharacter(char c) +{ + int i, len=(int) GetLength(); + for (i=len-1; i >= 0; i--) + { + if (sharedString->c_str[i]==c) + { + Clone(); + sharedString->c_str[i]=0; + return; + } + } +} +void RakString::TerminateAtFirstCharacter(char c) +{ + unsigned int i, len=(unsigned int) GetLength(); + for (i=0; i < len; i++) + { + if (sharedString->c_str[i]==c) + { + Clone(); + sharedString->c_str[i]=0; + return; + } + } +} +void RakString::RemoveCharacter(char c) +{ + if (c==0) + return; + + unsigned int readIndex, writeIndex=0; + for (readIndex=0; sharedString->c_str[readIndex]; readIndex++) + { + if (sharedString->c_str[readIndex]!=c) + sharedString->c_str[writeIndex++]=sharedString->c_str[readIndex]; + else + Clone(); + } + sharedString->c_str[writeIndex]=0; +} +int RakString::StrCmp(const RakString &rhs) const +{ + return strcmp(sharedString->c_str, rhs); +} +int RakString::StrICmp(const RakString &rhs) const +{ + return _stricmp(sharedString->c_str, rhs); +} +void RakString::Printf(void) +{ + RAKNET_DEBUG_PRINTF("%s", sharedString->c_str); +} +void RakString::FPrintf(FILE *fp) +{ + fprintf(fp,"%s", sharedString->c_str); +} +bool RakString::IPAddressMatch(const char *IP) +{ + unsigned characterIndex; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return false; + + characterIndex = 0; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) + { + if (sharedString->c_str[ characterIndex ] == IP[ characterIndex ] ) + { + // Equal characters + if ( IP[ characterIndex ] == 0 ) + { + // End of the string and the strings match + + return true; + } + + characterIndex++; + } + + else + { + if ( sharedString->c_str[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) + { + // End of one of the strings + break; + } + + // Characters do not match + if ( sharedString->c_str[ characterIndex ] == '*' ) + { + // Domain is banned. + return true; + } + + // Characters do not match and it is not a * + break; + } + } + + + // No match found. + return false; +} +bool RakString::ContainsNonprintableExceptSpaces(void) const +{ + size_t strLen = strlen(sharedString->c_str); + unsigned i; + for (i=0; i < strLen; i++) + { + if (sharedString->c_str[i] < ' ' || sharedString->c_str[i] >126) + return true; + } + return false; +} +bool RakString::IsEmailAddress(void) const +{ + if (IsEmpty()) + return false; + size_t strLen = strlen(sharedString->c_str); + if (strLen < 6) // a@b.de + return false; + if (sharedString->c_str[strLen-4]!='.' && sharedString->c_str[strLen-3]!='.') // .com, .net., .org, .de + return false; + unsigned i; + // Has non-printable? + for (i=0; i < strLen; i++) + { + if (sharedString->c_str[i] <= ' ' || sharedString->c_str[i] >126) + return false; + } + int atCount=0; + for (i=0; i < strLen; i++) + { + if (sharedString->c_str[i]=='@') + { + atCount++; + } + } + if (atCount!=1) + return false; + int dotCount=0; + for (i=0; i < strLen; i++) + { + if (sharedString->c_str[i]=='.') + { + dotCount++; + } + } + if (dotCount==0) + return false; + + // There's more I could check, but this is good enough + return true; +} +RakNet::RakString& RakString::URLEncode(void) +{ + RakString result; + size_t strLen = strlen(sharedString->c_str); + result.Allocate(strLen*3); + char *output=result.sharedString->c_str; + unsigned int outputIndex=0; + unsigned i; + char c; + for (i=0; i < strLen; i++) + { + c=sharedString->c_str[i]; + if ( + (c<=47) || + (c>=58 && c<=64) || + (c>=91 && c<=96) || + (c>=123) + ) + { + RakNet::RakString tmp("%2X", c); + output[outputIndex++]='%'; + output[outputIndex++]=tmp.sharedString->c_str[0]; + output[outputIndex++]=tmp.sharedString->c_str[1]; + } + else + { + output[outputIndex++]=c; + } + } + + output[outputIndex]=0; + + *this = result; + return *this; +} +RakNet::RakString& RakString::URLDecode(void) +{ + RakString result; + size_t strLen = strlen(sharedString->c_str); + result.Allocate(strLen); + char *output=result.sharedString->c_str; + unsigned int outputIndex=0; + char c; + char hexDigits[2]; + char hexValues[2]; + unsigned int i; + for (i=0; i < strLen; i++) + { + c=sharedString->c_str[i]; + if (c=='%') + { + hexDigits[0]=sharedString->c_str[++i]; + hexDigits[1]=sharedString->c_str[++i]; + if (hexDigits[0]==' ') + hexValues[0]=0; + else if (hexDigits[0]>='A') + hexValues[0]=hexDigits[0]-'A'+10; + else + hexValues[0]=hexDigits[0]-'0'; + if (hexDigits[1]>='A') + hexValues[1]=hexDigits[1]-'A'+10; + else + hexValues[1]=hexDigits[1]-'0'; + output[outputIndex++]=hexValues[0]*16+hexValues[1]; + } + else + { + output[outputIndex++]=c; + } + } + + output[outputIndex]=0; + + *this = result; + return *this; +} +void RakString::SplitURI(RakNet::RakString &header, RakNet::RakString &domain, RakNet::RakString &path) +{ + header.Clear(); + domain.Clear(); + path.Clear(); + + size_t strLen = strlen(sharedString->c_str); + + char c; + unsigned int i=0; + if (strncmp(sharedString->c_str, "http://", 7)==0) + i+=(unsigned int) strlen("http://"); + else if (strncmp(sharedString->c_str, "https://", 8)==0) + i+=(unsigned int) strlen("https://"); + + if (strncmp(sharedString->c_str, "www.", 4)==0) + i+=(unsigned int) strlen("www."); + + if (i!=0) + { + header.Allocate(i+1); + strncpy(header.sharedString->c_str, sharedString->c_str, i); + header.sharedString->c_str[i]=0; + } + + + domain.Allocate(strLen-i+1); + char *domainOutput=domain.sharedString->c_str; + unsigned int outputIndex=0; + for (; i < strLen; i++) + { + c=sharedString->c_str[i]; + if (c=='/') + { + break; + } + else + { + domainOutput[outputIndex++]=sharedString->c_str[i]; + } + } + + domainOutput[outputIndex]=0; + + path.Allocate(strLen-header.GetLength()-outputIndex+1); + outputIndex=0; + char *pathOutput=path.sharedString->c_str; + for (; i < strLen; i++) + { + pathOutput[outputIndex++]=sharedString->c_str[i]; + } + pathOutput[outputIndex]=0; +} +RakNet::RakString& RakString::SQLEscape(void) +{ + int strLen=(int)GetLength(); + int escapedCharacterCount=0; + int index; + for (index=0; index < strLen; index++) + { + if (sharedString->c_str[index]=='\'' || + sharedString->c_str[index]=='"' || + sharedString->c_str[index]=='\\') + escapedCharacterCount++; + } + if (escapedCharacterCount==0) + return *this; + + Clone(); + Realloc(sharedString, strLen+escapedCharacterCount); + int writeIndex, readIndex; + writeIndex = strLen+escapedCharacterCount; + readIndex=strLen; + while (readIndex>=0) + { + if (sharedString->c_str[readIndex]=='\'' || + sharedString->c_str[readIndex]=='"' || + sharedString->c_str[readIndex]=='\\') + { + sharedString->c_str[writeIndex--]=sharedString->c_str[readIndex--]; + sharedString->c_str[writeIndex--]='\\'; + } + else + { + sharedString->c_str[writeIndex--]=sharedString->c_str[readIndex--]; + } + } + return *this; +} +RakNet::RakString& RakString::MakeFilePath(void) +{ + if (IsEmpty()) + return *this; + + RakNet::RakString fixedString = *this; + fixedString.Clone(); + for (int i=0; fixedString.sharedString->c_str[i]; i++) + { +#ifdef _WIN32 + if (fixedString.sharedString->c_str[i]=='/') + fixedString.sharedString->c_str[i]='\\'; +#else + if (fixedString.sharedString->c_str[i]=='\\') + fixedString.sharedString->c_str[i]='/'; +#endif + } + +#ifdef _WIN32 + if (fixedString.sharedString->c_str[strlen(fixedString.sharedString->c_str)-1]!='\\') + { + fixedString+='\\'; + } +#else + if (fixedString.sharedString->c_str[strlen(fixedString.sharedString->c_str)-1]!='/') + { + fixedString+='/'; + } +#endif + + if (fixedString!=*this) + *this = fixedString; + return *this; +} +void RakString::FreeMemory(void) +{ + LockMutex(); + FreeMemoryNoMutex(); + UnlockMutex(); +} +void RakString::FreeMemoryNoMutex(void) +{ + for (unsigned int i=0; i < freeList.Size(); i++) + { + RakNet::OP_DELETE(freeList[i]->refCountMutex,_FILE_AND_LINE_); + rakFree_Ex(freeList[i], _FILE_AND_LINE_ ); + } + freeList.Clear(false, _FILE_AND_LINE_); +} +void RakString::Serialize(BitStream *bs) const +{ + Serialize(sharedString->c_str, bs); +} +void RakString::Serialize(const char *str, BitStream *bs) +{ + unsigned short l = (unsigned short) strlen(str); + bs->Write(l); + bs->WriteAlignedBytes((const unsigned char*) str, (const unsigned int) l); +} +void RakString::SerializeCompressed(BitStream *bs, uint8_t languageId, bool writeLanguageId) const +{ + SerializeCompressed(C_String(), bs, languageId, writeLanguageId); +} +void RakString::SerializeCompressed(const char *str, BitStream *bs, uint8_t languageId, bool writeLanguageId) +{ + if (writeLanguageId) + bs->WriteCompressed(languageId); + StringCompressor::Instance()->EncodeString(str,0xFFFF,bs,languageId); +} +bool RakString::Deserialize(BitStream *bs) +{ + Clear(); + + bool b; + unsigned short l; + b=bs->Read(l); + if (l>0) + { + Allocate(((unsigned int) l)+1); + b=bs->ReadAlignedBytes((unsigned char*) sharedString->c_str, l); + if (b) + sharedString->c_str[l]=0; + else + Clear(); + } + else + bs->AlignReadToByteBoundary(); + return b; +} +bool RakString::Deserialize(char *str, BitStream *bs) +{ + bool b; + unsigned short l; + b=bs->Read(l); + if (b && l>0) + b=bs->ReadAlignedBytes((unsigned char*) str, l); + + if (b==false) + str[0]=0; + + str[l]=0; + return b; +} +bool RakString::DeserializeCompressed(BitStream *bs, bool readLanguageId) +{ + uint8_t languageId; + if (readLanguageId) + bs->ReadCompressed(languageId); + else + languageId=0; + return StringCompressor::Instance()->DecodeString(this,0xFFFF,bs,languageId); +} +bool RakString::DeserializeCompressed(char *str, BitStream *bs, bool readLanguageId) +{ + uint8_t languageId; + if (readLanguageId) + bs->ReadCompressed(languageId); + else + languageId=0; + return StringCompressor::Instance()->DecodeString(str,0xFFFF,bs,languageId); +} +const char *RakString::ToString(int64_t i) +{ + static int index=0; + static char buff[64][64]; +#if defined(_WIN32) + sprintf(buff[index], "%I64d", i); +#else + sprintf(buff[index], "%lld", (long long unsigned int) i); +#endif + int lastIndex=index; + if (++index==64) + index=0; + return buff[lastIndex]; +} +const char *RakString::ToString(uint64_t i) +{ + static int index=0; + static char buff[64][64]; +#if defined(_WIN32) + sprintf(buff[index], "%I64u", i); +#else + sprintf(buff[index], "%llu", (long long unsigned int) i); +#endif + int lastIndex=index; + if (++index==64) + index=0; + return buff[lastIndex]; +} +void RakString::Clear(void) +{ + Free(); +} +void RakString::Allocate(size_t len) +{ + RakString::LockMutex(); + // sharedString = RakString::pool.Allocate( _FILE_AND_LINE_ ); + if (RakString::freeList.Size()==0) + { + //RakString::sharedStringFreeList=(RakString::SharedString*) rakRealloc_Ex(RakString::sharedStringFreeList,(RakString::sharedStringFreeListAllocationCount+1024)*sizeof(RakString::SharedString), _FILE_AND_LINE_); + unsigned i; + for (i=0; i < 128; i++) + { + // RakString::freeList.Insert(RakString::sharedStringFreeList+i+RakString::sharedStringFreeListAllocationCount); + // RakString::freeList.Insert((RakString::SharedString*)rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_), _FILE_AND_LINE_); + + RakString::SharedString *ss; + ss = (RakString::SharedString*) rakMalloc_Ex(sizeof(RakString::SharedString), _FILE_AND_LINE_); + ss->refCountMutex=RakNet::OP_NEW(_FILE_AND_LINE_); + RakString::freeList.Insert(ss, _FILE_AND_LINE_); + } + //RakString::sharedStringFreeListAllocationCount+=1024; + } + sharedString = RakString::freeList[RakString::freeList.Size()-1]; + RakString::freeList.RemoveAtIndex(RakString::freeList.Size()-1); + RakString::UnlockMutex(); + + const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + sharedString->refCount=1; + if (len <= smallStringSize) + { + sharedString->bytesUsed=smallStringSize; + sharedString->c_str=sharedString->smallString; + } + else + { + sharedString->bytesUsed=len<<1; + sharedString->bigString=(char*)rakMalloc_Ex(sharedString->bytesUsed, _FILE_AND_LINE_); + sharedString->c_str=sharedString->bigString; + } +} +void RakString::Assign(const char *str) +{ + if (str==0 || str[0]==0) + { + sharedString=&emptyString; + return; + } + + size_t len = strlen(str)+1; + Allocate(len); + memcpy(sharedString->c_str, str, len); +} +void RakString::Assign(const char *str, va_list ap) +{ + char stackBuff[512]; + if (_vsnprintf(stackBuff, 512, str, ap)!=-1 +#ifndef _WIN32 + // Here Windows will return -1 if the string is too long; Linux just truncates the string. + && strlen(str) <511 +#endif + ) + { + Assign(stackBuff); + return; + } + char *buff=0, *newBuff; + size_t buffSize=8096; + while (1) + { + newBuff = (char*) rakRealloc_Ex(buff, buffSize,__FILE__,__LINE__); + if (newBuff==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + if (buff!=0) + { + Assign(buff); + rakFree_Ex(buff,__FILE__,__LINE__); + } + else + { + Assign(stackBuff); + } + return; + } + buff=newBuff; + if (_vsnprintf(buff, buffSize, str, ap)!=-1) + { + Assign(buff); + rakFree_Ex(buff,__FILE__,__LINE__); + return; + } + buffSize*=2; + } +} +RakNet::RakString RakString::Assign(const char *str,size_t pos, size_t n ) +{ + size_t incomingLen=strlen(str); + + Clone(); + + if (str==0 || str[0]==0||pos>=incomingLen) + { + sharedString=&emptyString; + return (*this); + } + + if (pos+n>=incomingLen) + { + n=incomingLen-pos; + + } + const char * tmpStr=&(str[pos]); + + size_t len = n+1; + Allocate(len); + memcpy(sharedString->c_str, tmpStr, len); + sharedString->c_str[n]=0; + + return (*this); +} + +RakNet::RakString RakString::NonVariadic(const char *str) +{ + RakNet::RakString rs; + rs=str; + return rs; +} +unsigned long RakString::ToInteger(const char *str) +{ + unsigned long hash = 0; + int c; + + while ((c = *str++)) + hash = c + (hash << 6) + (hash << 16) - hash; + + return hash; +} +unsigned long RakString::ToInteger(const RakString &rs) +{ + return RakString::ToInteger(rs.C_String()); +} +void RakString::AppendBytes(const char *bytes, unsigned int count) +{ + Clone(); + Realloc(sharedString, count); + unsigned int length=(unsigned int) GetLength(); + memcpy(sharedString->c_str+length, bytes, count); + sharedString->c_str[length+count]=0; +} +void RakString::Clone(void) +{ + if (sharedString==&emptyString) + { + return; + } + + // Empty or solo then no point to cloning + sharedString->refCountMutex->Lock(); + if (sharedString->refCount==1) + { + sharedString->refCountMutex->Unlock(); + return; + } + + sharedString->refCount--; + sharedString->refCountMutex->Unlock(); + Assign(sharedString->c_str); +} +void RakString::Free(void) +{ + if (sharedString==&emptyString) + return; + sharedString->refCountMutex->Lock(); + sharedString->refCount--; + if (sharedString->refCount==0) + { + sharedString->refCountMutex->Unlock(); + const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + if (sharedString->bytesUsed>smallStringSize) + rakFree_Ex(sharedString->bigString, _FILE_AND_LINE_ ); + /* + poolMutex->Lock(); + pool.Release(sharedString); + poolMutex->Unlock(); + */ + + RakString::LockMutex(); + RakString::freeList.Insert(sharedString, _FILE_AND_LINE_); + RakString::UnlockMutex(); + + sharedString=&emptyString; + } + else + { + sharedString->refCountMutex->Unlock(); + } + sharedString=&emptyString; +} +unsigned char RakString::ToLower(unsigned char c) +{ + if (c >= 'A' && c <= 'Z') + return c-'A'+'a'; + return c; +} +unsigned char RakString::ToUpper(unsigned char c) +{ + if (c >= 'a' && c <= 'z') + return c-'a'+'A'; + return c; +} +void RakString::LockMutex(void) +{ + GetPoolMutex().Lock(); +} +void RakString::UnlockMutex(void) +{ + GetPoolMutex().Unlock(); +} + +/* +#include "RakString.h" +#include +#include "GetTime.h" + +using namespace RakNet; + +int main(void) +{ + RakString s3("Hello world"); + RakString s5=s3; + + RakString s1; + RakString s2('a'); + + RakString s4("%i %f", 5, 6.0); + + RakString s6=s3; + RakString s7=s6; + RakString s8=s6; + RakString s9; + s9=s9; + RakString s10(s3); + RakString s11=s10 + s4 + s9 + s2; + s11+=RakString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + RakString s12("Test"); + s12+=s11; + bool b1 = s12==s12; + s11=s5; + s12.ToUpper(); + s12.ToLower(); + RakString s13; + bool b3 = s13.IsEmpty(); + s13.Set("blah %s", s12.C_String()); + bool b4 = s13.IsEmpty(); + size_t i1=s13.GetLength(); + s3.Clear(_FILE_AND_LINE_); + s4.Clear(_FILE_AND_LINE_); + s5.Clear(_FILE_AND_LINE_); + s5.Clear(_FILE_AND_LINE_); + s6.Printf(); + s7.Printf(); + RAKNET_DEBUG_PRINTF("\n"); + + static const int repeatCount=750; + DataStructures::List rakStringList; + DataStructures::List stdStringList; + DataStructures::List referenceStringList; + char *c; + unsigned i; + RakNet::TimeMS beforeReferenceList, beforeRakString, beforeStdString, afterStdString; + + unsigned loop; + for (loop=0; loop<2; loop++) + { + beforeReferenceList=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + { + c = RakNet::OP_NEW_ARRAY(56,_FILE_AND_LINE_ ); + strcpy(c, "Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + referenceStringList.Insert(c); + } + beforeRakString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + rakStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + beforeStdString=RakNet::GetTimeMS(); + + for (i=0; i < repeatCount; i++) + stdStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + afterStdString=RakNet::GetTimeMS(); + RAKNET_DEBUG_PRINTF("Insertion 1 Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); + + beforeReferenceList=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + { + RakNet::OP_DELETE_ARRAY(referenceStringList[0], _FILE_AND_LINE_); + referenceStringList.RemoveAtIndex(0); + } + beforeRakString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + rakStringList.RemoveAtIndex(0); + beforeStdString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + stdStringList.RemoveAtIndex(0); + afterStdString=RakNet::GetTimeMS(); + RAKNET_DEBUG_PRINTF("RemoveHead Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); + + beforeReferenceList=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + { + c = RakNet::OP_NEW_ARRAY(56, _FILE_AND_LINE_ ); + strcpy(c, "Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + referenceStringList.Insert(0); + } + beforeRakString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + rakStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + beforeStdString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + stdStringList.Insert("Aalsdkj alsdjf laksdjf ;lasdfj ;lasjfd"); + afterStdString=RakNet::GetTimeMS(); + RAKNET_DEBUG_PRINTF("Insertion 2 Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); + + beforeReferenceList=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + { + RakNet::OP_DELETE_ARRAY(referenceStringList[referenceStringList.Size()-1], _FILE_AND_LINE_); + referenceStringList.RemoveAtIndex(referenceStringList.Size()-1); + } + beforeRakString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + rakStringList.RemoveAtIndex(rakStringList.Size()-1); + beforeStdString=RakNet::GetTimeMS(); + for (i=0; i < repeatCount; i++) + stdStringList.RemoveAtIndex(stdStringList.Size()-1); + afterStdString=RakNet::GetTimeMS(); + RAKNET_DEBUG_PRINTF("RemoveTail Ref=%i Rak=%i, Std=%i\n", beforeRakString-beforeReferenceList, beforeStdString-beforeRakString, afterStdString-beforeStdString); + + } + + printf("Done."); + char str[128]; + Gets(str, sizeof(str)); + return 1; +} +*/ diff --git a/src/raknet/RakString.h b/src/raknet/RakString.h index 125586e..49ac882 100755 --- a/src/raknet/RakString.h +++ b/src/raknet/RakString.h @@ -1,307 +1,307 @@ -#ifndef __RAK_STRING_H -#define __RAK_STRING_H - -#include "Export.h" -#include "DS_List.h" -#include "RakNetTypes.h" // int64_t -#include -#include "stdarg.h" - - -#ifdef _WIN32 - - - -#include "WindowsIncludes.h" -#endif - -namespace RakNet -{ -/// Forward declarations -class SimpleMutex; -class BitStream; - -/// \brief String class -/// \details Has the following improvements over std::string -/// -Reference counting: Suitable to store in lists -/// -Variadic assignment operator -/// -Doesn't cause linker errors -class RAK_DLL_EXPORT RakString -{ -public: - // Constructors - RakString(); - RakString(char input); - RakString(unsigned char input); - RakString(const unsigned char *format, ...); - RakString(const char *format, ...); - ~RakString(); - RakString( const RakString & rhs); - - /// Implicit return of const char* - operator const char* () const {return sharedString->c_str;} - - /// Same as std::string::c_str - const char *C_String(void) const {return sharedString->c_str;} - - // Lets you modify the string. Do not make the string longer - however, you can make it shorter, or change the contents. - // Pointer is only valid in the scope of RakString itself - char *C_StringUnsafe(void) {Clone(); return sharedString->c_str;} - - /// Assigment operators - RakString& operator = ( const RakString& rhs ); - RakString& operator = ( const char *str ); - RakString& operator = ( char *str ); - RakString& operator = ( const unsigned char *str ); - RakString& operator = ( char unsigned *str ); - RakString& operator = ( const char c ); - - /// Concatenation - RakString& operator +=( const RakString& rhs); - RakString& operator += ( const char *str ); - RakString& operator += ( char *str ); - RakString& operator += ( const unsigned char *str ); - RakString& operator += ( char unsigned *str ); - RakString& operator += ( const char c ); - - /// Character index. Do not use to change the string however. - unsigned char operator[] ( const unsigned int position ) const; - -#ifdef _WIN32 - // Return as Wide char - // Deallocate with DeallocWideChar - WCHAR * ToWideChar(void); - void DeallocWideChar(WCHAR * w); -#endif - - ///String class find replacement - ///Searches the string for the content specified in stringToFind and returns the position of the first occurrence in the string. - ///Search only includes characters on or after position pos, ignoring any possible occurrences in previous locations. - /// \param[in] stringToFind The string to find inside of this object's string - /// \param[in] pos The position in the string to start the search - /// \return Returns the position of the first occurrence in the string. - size_t Find(const char *stringToFind,size_t pos = 0 ); - - /// Equality - bool operator==(const RakString &rhs) const; - bool operator==(const char *str) const; - bool operator==(char *str) const; - - // Comparison - bool operator < ( const RakString& right ) const; - bool operator <= ( const RakString& right ) const; - bool operator > ( const RakString& right ) const; - bool operator >= ( const RakString& right ) const; - - /// Inequality - bool operator!=(const RakString &rhs) const; - bool operator!=(const char *str) const; - bool operator!=(char *str) const; - - /// Change all characters to lowercase - const char * ToLower(void); - - /// Change all characters to uppercase - const char * ToUpper(void); - - /// Set the value of the string - void Set(const char *format, ...); - - /// Sets a copy of a substring of str as the new content. The substring is the portion of str - /// that begins at the character position pos and takes up to n characters - /// (it takes less than n if the end of str is reached before). - /// \param[in] str The string to copy in - /// \param[in] pos The position on str to start the copy - /// \param[in] n How many chars to copy - /// \return Returns the string, note that the current string is set to that value as well - RakString Assign(const char *str,size_t pos, size_t n ); - - /// Returns if the string is empty. Also, C_String() would return "" - bool IsEmpty(void) const; - - /// Returns the length of the string - size_t GetLength(void) const; - size_t GetLengthUTF8(void) const; - - /// Replace character(s) in starting at index, for count, with c - void Replace(unsigned index, unsigned count, unsigned char c); - - /// Replace character at index with c - void SetChar( unsigned index, unsigned char c ); - - /// Replace character at index with string s - void SetChar( unsigned index, RakNet::RakString s ); - - /// Make sure string is no longer than \a length - void Truncate(unsigned int length); - void TruncateUTF8(unsigned int length); - - // Gets the substring starting at index for count characters - RakString SubStr(unsigned int index, unsigned int count) const; - - /// Erase characters out of the string at index for count - void Erase(unsigned int index, unsigned int count); - - /// Set the first instance of c with a NULL terminator - void TerminateAtFirstCharacter(char c); - /// Set the last instance of c with a NULL terminator - void TerminateAtLastCharacter(char c); - - /// Remove all instances of c - void RemoveCharacter(char c); - - /// Create a RakString with a value, without doing printf style parsing - /// Equivalent to assignment operator - static RakNet::RakString NonVariadic(const char *str); - - /// Has the string into an unsigned int - static unsigned long ToInteger(const char *str); - static unsigned long ToInteger(const RakString &rs); - - // Like strncat, but for a fixed length - void AppendBytes(const char *bytes, unsigned int count); - - /// Compare strings (case sensitive) - int StrCmp(const RakString &rhs) const; - - /// Compare strings (not case sensitive) - int StrICmp(const RakString &rhs) const; - - /// Clear the string - void Clear(void); - - /// Print the string to the screen - void Printf(void); - - /// Print the string to a file - void FPrintf(FILE *fp); - - /// Does the given IP address match the IP address encoded into this string, accounting for wildcards? - bool IPAddressMatch(const char *IP); - - /// Does the string contain non-printable characters other than spaces? - bool ContainsNonprintableExceptSpaces(void) const; - - /// Is this a valid email address? - bool IsEmailAddress(void) const; - - /// URL Encode the string. See http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4029/ - RakNet::RakString& URLEncode(void); - - /// URL decode the string - RakNet::RakString& URLDecode(void); - - /// https://servers.api.rackspacecloud.com/v1.0 to https://, servers.api.rackspacecloud.com, /v1.0 - void SplitURI(RakNet::RakString &header, RakNet::RakString &domain, RakNet::RakString &path); - - /// Scan for quote, double quote, and backslash and prepend with backslash - RakNet::RakString& SQLEscape(void); - - /// Fix to be a file path, ending with / - RakNet::RakString& MakeFilePath(void); - - /// RakString uses a freeList of old no-longer used strings - /// Call this function to clear this memory on shutdown - static void FreeMemory(void); - /// \internal - static void FreeMemoryNoMutex(void); - - /// Serialize to a bitstream, uncompressed (slightly faster) - /// \param[out] bs Bitstream to serialize to - void Serialize(BitStream *bs) const; - - /// Static version of the Serialize function - static void Serialize(const char *str, BitStream *bs); - - /// Serialize to a bitstream, compressed (better bandwidth usage) - /// \param[out] bs Bitstream to serialize to - /// \param[in] languageId languageId to pass to the StringCompressor class - /// \param[in] writeLanguageId encode the languageId variable in the stream. If false, 0 is assumed, and DeserializeCompressed will not look for this variable in the stream (saves bandwidth) - /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) - void SerializeCompressed(BitStream *bs, uint8_t languageId=0, bool writeLanguageId=false) const; - - /// Static version of the SerializeCompressed function - static void SerializeCompressed(const char *str, BitStream *bs, uint8_t languageId=0, bool writeLanguageId=false); - - /// Deserialize what was written by Serialize - /// \param[in] bs Bitstream to serialize from - /// \return true if the deserialization was successful - bool Deserialize(BitStream *bs); - - /// Static version of the Deserialize() function - static bool Deserialize(char *str, BitStream *bs); - - /// Deserialize compressed string, written by SerializeCompressed - /// \param[in] bs Bitstream to serialize from - /// \param[in] readLanguageId If true, looks for the variable langaugeId in the data stream. Must match what was passed to SerializeCompressed - /// \return true if the deserialization was successful - /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) - bool DeserializeCompressed(BitStream *bs, bool readLanguageId=false); - - /// Static version of the DeserializeCompressed() function - static bool DeserializeCompressed(char *str, BitStream *bs, bool readLanguageId=false); - - static const char *ToString(int64_t i); - static const char *ToString(uint64_t i); - - /// \internal - static size_t GetSizeToAllocate(size_t bytes) - { - const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; - if (bytes<=smallStringSize) - return smallStringSize; - else - return bytes*2; - } - - /// \internal - struct SharedString - { - SimpleMutex *refCountMutex; - unsigned int refCount; - size_t bytesUsed; - char *bigString; - char *c_str; - char smallString[128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2]; - }; - - /// \internal - RakString( SharedString *_sharedString ); - - /// \internal - SharedString *sharedString; - -// static SimpleMutex poolMutex; -// static DataStructures::MemoryPool pool; - /// \internal - static SharedString emptyString; - - //static SharedString *sharedStringFreeList; - //static unsigned int sharedStringFreeListAllocationCount; - /// \internal - /// List of free objects to reduce memory reallocations - static DataStructures::List freeList; - - static int RakStringComp( RakString const &key, RakString const &data ); - - static void LockMutex(void); - static void UnlockMutex(void); - -protected: - void Allocate(size_t len); - void Assign(const char *str); - void Assign(const char *str, va_list ap); - - void Clone(void); - void Free(void); - unsigned char ToLower(unsigned char c); - unsigned char ToUpper(unsigned char c); - void Realloc(SharedString *sharedString, size_t bytes); -}; - -} - -const RakNet::RakString RAK_DLL_EXPORT operator+(const RakNet::RakString &lhs, const RakNet::RakString &rhs); - - -#endif +#ifndef __RAK_STRING_H +#define __RAK_STRING_H + +#include "Export.h" +#include "DS_List.h" +#include "RakNetTypes.h" // int64_t +#include +#include "stdarg.h" + + +#ifdef _WIN32 + + + +#include "WindowsIncludes.h" +#endif + +namespace RakNet +{ +/// Forward declarations +class SimpleMutex; +class BitStream; + +/// \brief String class +/// \details Has the following improvements over std::string +/// -Reference counting: Suitable to store in lists +/// -Variadic assignment operator +/// -Doesn't cause linker errors +class RAK_DLL_EXPORT RakString +{ +public: + // Constructors + RakString(); + RakString(char input); + RakString(unsigned char input); + RakString(const unsigned char *format, ...); + RakString(const char *format, ...); + ~RakString(); + RakString( const RakString & rhs); + + /// Implicit return of const char* + operator const char* () const {return sharedString->c_str;} + + /// Same as std::string::c_str + const char *C_String(void) const {return sharedString->c_str;} + + // Lets you modify the string. Do not make the string longer - however, you can make it shorter, or change the contents. + // Pointer is only valid in the scope of RakString itself + char *C_StringUnsafe(void) {Clone(); return sharedString->c_str;} + + /// Assigment operators + RakString& operator = ( const RakString& rhs ); + RakString& operator = ( const char *str ); + RakString& operator = ( char *str ); + RakString& operator = ( const unsigned char *str ); + RakString& operator = ( char unsigned *str ); + RakString& operator = ( const char c ); + + /// Concatenation + RakString& operator +=( const RakString& rhs); + RakString& operator += ( const char *str ); + RakString& operator += ( char *str ); + RakString& operator += ( const unsigned char *str ); + RakString& operator += ( char unsigned *str ); + RakString& operator += ( const char c ); + + /// Character index. Do not use to change the string however. + unsigned char operator[] ( const unsigned int position ) const; + +#ifdef _WIN32 + // Return as Wide char + // Deallocate with DeallocWideChar + WCHAR * ToWideChar(void); + void DeallocWideChar(WCHAR * w); +#endif + + ///String class find replacement + ///Searches the string for the content specified in stringToFind and returns the position of the first occurrence in the string. + ///Search only includes characters on or after position pos, ignoring any possible occurrences in previous locations. + /// \param[in] stringToFind The string to find inside of this object's string + /// \param[in] pos The position in the string to start the search + /// \return Returns the position of the first occurrence in the string. + size_t Find(const char *stringToFind,size_t pos = 0 ); + + /// Equality + bool operator==(const RakString &rhs) const; + bool operator==(const char *str) const; + bool operator==(char *str) const; + + // Comparison + bool operator < ( const RakString& right ) const; + bool operator <= ( const RakString& right ) const; + bool operator > ( const RakString& right ) const; + bool operator >= ( const RakString& right ) const; + + /// Inequality + bool operator!=(const RakString &rhs) const; + bool operator!=(const char *str) const; + bool operator!=(char *str) const; + + /// Change all characters to lowercase + const char * ToLower(void); + + /// Change all characters to uppercase + const char * ToUpper(void); + + /// Set the value of the string + void Set(const char *format, ...); + + /// Sets a copy of a substring of str as the new content. The substring is the portion of str + /// that begins at the character position pos and takes up to n characters + /// (it takes less than n if the end of str is reached before). + /// \param[in] str The string to copy in + /// \param[in] pos The position on str to start the copy + /// \param[in] n How many chars to copy + /// \return Returns the string, note that the current string is set to that value as well + RakString Assign(const char *str,size_t pos, size_t n ); + + /// Returns if the string is empty. Also, C_String() would return "" + bool IsEmpty(void) const; + + /// Returns the length of the string + size_t GetLength(void) const; + size_t GetLengthUTF8(void) const; + + /// Replace character(s) in starting at index, for count, with c + void Replace(unsigned index, unsigned count, unsigned char c); + + /// Replace character at index with c + void SetChar( unsigned index, unsigned char c ); + + /// Replace character at index with string s + void SetChar( unsigned index, RakNet::RakString s ); + + /// Make sure string is no longer than \a length + void Truncate(unsigned int length); + void TruncateUTF8(unsigned int length); + + // Gets the substring starting at index for count characters + RakString SubStr(unsigned int index, unsigned int count) const; + + /// Erase characters out of the string at index for count + void Erase(unsigned int index, unsigned int count); + + /// Set the first instance of c with a NULL terminator + void TerminateAtFirstCharacter(char c); + /// Set the last instance of c with a NULL terminator + void TerminateAtLastCharacter(char c); + + /// Remove all instances of c + void RemoveCharacter(char c); + + /// Create a RakString with a value, without doing printf style parsing + /// Equivalent to assignment operator + static RakNet::RakString NonVariadic(const char *str); + + /// Has the string into an unsigned int + static unsigned long ToInteger(const char *str); + static unsigned long ToInteger(const RakString &rs); + + // Like strncat, but for a fixed length + void AppendBytes(const char *bytes, unsigned int count); + + /// Compare strings (case sensitive) + int StrCmp(const RakString &rhs) const; + + /// Compare strings (not case sensitive) + int StrICmp(const RakString &rhs) const; + + /// Clear the string + void Clear(void); + + /// Print the string to the screen + void Printf(void); + + /// Print the string to a file + void FPrintf(FILE *fp); + + /// Does the given IP address match the IP address encoded into this string, accounting for wildcards? + bool IPAddressMatch(const char *IP); + + /// Does the string contain non-printable characters other than spaces? + bool ContainsNonprintableExceptSpaces(void) const; + + /// Is this a valid email address? + bool IsEmailAddress(void) const; + + /// URL Encode the string. See http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4029/ + RakNet::RakString& URLEncode(void); + + /// URL decode the string + RakNet::RakString& URLDecode(void); + + /// https://servers.api.rackspacecloud.com/v1.0 to https://, servers.api.rackspacecloud.com, /v1.0 + void SplitURI(RakNet::RakString &header, RakNet::RakString &domain, RakNet::RakString &path); + + /// Scan for quote, double quote, and backslash and prepend with backslash + RakNet::RakString& SQLEscape(void); + + /// Fix to be a file path, ending with / + RakNet::RakString& MakeFilePath(void); + + /// RakString uses a freeList of old no-longer used strings + /// Call this function to clear this memory on shutdown + static void FreeMemory(void); + /// \internal + static void FreeMemoryNoMutex(void); + + /// Serialize to a bitstream, uncompressed (slightly faster) + /// \param[out] bs Bitstream to serialize to + void Serialize(BitStream *bs) const; + + /// Static version of the Serialize function + static void Serialize(const char *str, BitStream *bs); + + /// Serialize to a bitstream, compressed (better bandwidth usage) + /// \param[out] bs Bitstream to serialize to + /// \param[in] languageId languageId to pass to the StringCompressor class + /// \param[in] writeLanguageId encode the languageId variable in the stream. If false, 0 is assumed, and DeserializeCompressed will not look for this variable in the stream (saves bandwidth) + /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) + void SerializeCompressed(BitStream *bs, uint8_t languageId=0, bool writeLanguageId=false) const; + + /// Static version of the SerializeCompressed function + static void SerializeCompressed(const char *str, BitStream *bs, uint8_t languageId=0, bool writeLanguageId=false); + + /// Deserialize what was written by Serialize + /// \param[in] bs Bitstream to serialize from + /// \return true if the deserialization was successful + bool Deserialize(BitStream *bs); + + /// Static version of the Deserialize() function + static bool Deserialize(char *str, BitStream *bs); + + /// Deserialize compressed string, written by SerializeCompressed + /// \param[in] bs Bitstream to serialize from + /// \param[in] readLanguageId If true, looks for the variable langaugeId in the data stream. Must match what was passed to SerializeCompressed + /// \return true if the deserialization was successful + /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) + bool DeserializeCompressed(BitStream *bs, bool readLanguageId=false); + + /// Static version of the DeserializeCompressed() function + static bool DeserializeCompressed(char *str, BitStream *bs, bool readLanguageId=false); + + static const char *ToString(int64_t i); + static const char *ToString(uint64_t i); + + /// \internal + static size_t GetSizeToAllocate(size_t bytes) + { + const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + if (bytes<=smallStringSize) + return smallStringSize; + else + return bytes*2; + } + + /// \internal + struct SharedString + { + SimpleMutex *refCountMutex; + unsigned int refCount; + size_t bytesUsed; + char *bigString; + char *c_str; + char smallString[128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2]; + }; + + /// \internal + RakString( SharedString *_sharedString ); + + /// \internal + SharedString *sharedString; + +// static SimpleMutex poolMutex; +// static DataStructures::MemoryPool pool; + /// \internal + static SharedString emptyString; + + //static SharedString *sharedStringFreeList; + //static unsigned int sharedStringFreeListAllocationCount; + /// \internal + /// List of free objects to reduce memory reallocations + static DataStructures::List freeList; + + static int RakStringComp( RakString const &key, RakString const &data ); + + static void LockMutex(void); + static void UnlockMutex(void); + +protected: + void Allocate(size_t len); + void Assign(const char *str); + void Assign(const char *str, va_list ap); + + void Clone(void); + void Free(void); + unsigned char ToLower(unsigned char c); + unsigned char ToUpper(unsigned char c); + void Realloc(SharedString *sharedString, size_t bytes); +}; + +} + +const RakNet::RakString RAK_DLL_EXPORT operator+(const RakNet::RakString &lhs, const RakNet::RakString &rhs); + + +#endif diff --git a/src/raknet/RakThread.cpp b/src/raknet/RakThread.cpp index 268ee3a..fefe388 100755 --- a/src/raknet/RakThread.cpp +++ b/src/raknet/RakThread.cpp @@ -1,163 +1,163 @@ -#include "RakThread.h" -#include "RakAssert.h" -#include "RakNetDefines.h" -#include "RakSleep.h" -#include "RakMemoryOverride.h" - -using namespace RakNet; - - - - -#if defined(_WIN32) -#include "WindowsIncludes.h" -#include - #if !defined(_WIN32_WCE) - #include - #endif - - - - -#else -#include -#endif - - -#if defined(_WIN32_WCE) -int RakThread::Create( LPTHREAD_START_ROUTINE start_address, void *arglist, int priority) -#elif defined(_WIN32) -int RakThread::Create( unsigned __stdcall start_address( void* ), void *arglist, int priority) - - - -#else -int RakThread::Create( void* start_address( void* ), void *arglist, int priority) -#endif -{ -#ifdef _WIN32 - HANDLE threadHandle; - unsigned threadID = 0; - - -#if defined (_WIN32_WCE) - threadHandle = CreateThread(NULL,MAX_ALLOCA_STACK_ALLOCATION*2,start_address,arglist,0,(DWORD*)&threadID); - SetThreadPriority(threadHandle, priority); -#else - threadHandle = (HANDLE) _beginthreadex( NULL, MAX_ALLOCA_STACK_ALLOCATION*2, start_address, arglist, 0, &threadID ); -#endif - SetThreadPriority(threadHandle, priority); - - if (threadHandle==0) - { - return 1; - } - else - { - CloseHandle( threadHandle ); - return 0; - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#else - pthread_t threadHandle; - // Create thread linux - pthread_attr_t attr; - sched_param param; - param.sched_priority=priority; - pthread_attr_init( &attr ); - pthread_attr_setschedparam(&attr, ¶m); - - - - - - pthread_attr_setstacksize(&attr, MAX_ALLOCA_STACK_ALLOCATION*2); - - pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); - int res = pthread_create( &threadHandle, &attr, start_address, arglist ); - RakAssert(res==0 && "pthread_create in RakThread.cpp failed.") - return res; -#endif -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#include "RakThread.h" +#include "RakAssert.h" +#include "RakNetDefines.h" +#include "RakSleep.h" +#include "RakMemoryOverride.h" + +using namespace RakNet; + + + + +#if defined(_WIN32) +#include "WindowsIncludes.h" +#include + #if !defined(_WIN32_WCE) + #include + #endif + + + + +#else +#include +#endif + + +#if defined(_WIN32_WCE) +int RakThread::Create( LPTHREAD_START_ROUTINE start_address, void *arglist, int priority) +#elif defined(_WIN32) +int RakThread::Create( unsigned __stdcall start_address( void* ), void *arglist, int priority) + + + +#else +int RakThread::Create( void* start_address( void* ), void *arglist, int priority) +#endif +{ +#ifdef _WIN32 + HANDLE threadHandle; + unsigned threadID = 0; + + +#if defined (_WIN32_WCE) + threadHandle = CreateThread(NULL,MAX_ALLOCA_STACK_ALLOCATION*2,start_address,arglist,0,(DWORD*)&threadID); + SetThreadPriority(threadHandle, priority); +#else + threadHandle = (HANDLE) _beginthreadex( NULL, MAX_ALLOCA_STACK_ALLOCATION*2, start_address, arglist, 0, &threadID ); +#endif + SetThreadPriority(threadHandle, priority); + + if (threadHandle==0) + { + return 1; + } + else + { + CloseHandle( threadHandle ); + return 0; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#else + pthread_t threadHandle; + // Create thread linux + pthread_attr_t attr; + sched_param param; + param.sched_priority=priority; + pthread_attr_init( &attr ); + pthread_attr_setschedparam(&attr, ¶m); + + + + + + pthread_attr_setstacksize(&attr, MAX_ALLOCA_STACK_ALLOCATION*2); + + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); + int res = pthread_create( &threadHandle, &attr, start_address, arglist ); + RakAssert(res==0 && "pthread_create in RakThread.cpp failed.") + return res; +#endif +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/raknet/RakThread.h b/src/raknet/RakThread.h index e6bf005..6cacd83 100755 --- a/src/raknet/RakThread.h +++ b/src/raknet/RakThread.h @@ -1,88 +1,88 @@ -#ifndef __RAK_THREAD_H -#define __RAK_THREAD_H - -#if defined(_WIN32_WCE) -#include "WindowsIncludes.h" -#endif - -#include "Export.h" - - - - - - -namespace RakNet -{ - -/// To define a thread, use RAK_THREAD_DECLARATION(functionName); -#if defined(_WIN32_WCE) -#define RAK_THREAD_DECLARATION(functionName) DWORD WINAPI functionName(LPVOID arguments) - - -#elif defined(_WIN32) -#define RAK_THREAD_DECLARATION(functionName) unsigned __stdcall functionName( void* arguments ) - - -#else -#define RAK_THREAD_DECLARATION(functionName) void* functionName( void* arguments ) -#endif - -class RAK_DLL_EXPORT RakThread -{ -public: - - - - - /// Create a thread, simplified to be cross platform without all the extra junk - /// To then start that thread, call RakCreateThread(functionName, arguments); - /// \param[in] start_address Function you want to call - /// \param[in] arglist Arguments to pass to the function - /// \return 0=success. >0 = error code - - /* - nice value Win32 Priority - -20 to -16 THREAD_PRIORITY_HIGHEST - -15 to -6 THREAD_PRIORITY_ABOVE_NORMAL - -5 to +4 THREAD_PRIORITY_NORMAL - +5 to +14 THREAD_PRIORITY_BELOW_NORMAL - +15 to +19 THREAD_PRIORITY_LOWEST - */ -#if defined(_WIN32_WCE) - static int Create( LPTHREAD_START_ROUTINE start_address, void *arglist, int priority=0); - - -#elif defined(_WIN32) - static int Create( unsigned __stdcall start_address( void* ), void *arglist, int priority=0); - - - -#else - static int Create( void* start_address( void* ), void *arglist, int priority=0); -#endif - - - - - - - - - - - - - - - - - - - - -}; - -} - -#endif +#ifndef __RAK_THREAD_H +#define __RAK_THREAD_H + +#if defined(_WIN32_WCE) +#include "WindowsIncludes.h" +#endif + +#include "Export.h" + + + + + + +namespace RakNet +{ + +/// To define a thread, use RAK_THREAD_DECLARATION(functionName); +#if defined(_WIN32_WCE) +#define RAK_THREAD_DECLARATION(functionName) DWORD WINAPI functionName(LPVOID arguments) + + +#elif defined(_WIN32) +#define RAK_THREAD_DECLARATION(functionName) unsigned __stdcall functionName( void* arguments ) + + +#else +#define RAK_THREAD_DECLARATION(functionName) void* functionName( void* arguments ) +#endif + +class RAK_DLL_EXPORT RakThread +{ +public: + + + + + /// Create a thread, simplified to be cross platform without all the extra junk + /// To then start that thread, call RakCreateThread(functionName, arguments); + /// \param[in] start_address Function you want to call + /// \param[in] arglist Arguments to pass to the function + /// \return 0=success. >0 = error code + + /* + nice value Win32 Priority + -20 to -16 THREAD_PRIORITY_HIGHEST + -15 to -6 THREAD_PRIORITY_ABOVE_NORMAL + -5 to +4 THREAD_PRIORITY_NORMAL + +5 to +14 THREAD_PRIORITY_BELOW_NORMAL + +15 to +19 THREAD_PRIORITY_LOWEST + */ +#if defined(_WIN32_WCE) + static int Create( LPTHREAD_START_ROUTINE start_address, void *arglist, int priority=0); + + +#elif defined(_WIN32) + static int Create( unsigned __stdcall start_address( void* ), void *arglist, int priority=0); + + + +#else + static int Create( void* start_address( void* ), void *arglist, int priority=0); +#endif + + + + + + + + + + + + + + + + + + + + +}; + +} + +#endif diff --git a/src/raknet/RakWString.cpp b/src/raknet/RakWString.cpp index fbaea50..56e2a31 100755 --- a/src/raknet/RakWString.cpp +++ b/src/raknet/RakWString.cpp @@ -1,345 +1,345 @@ -#include "RakWString.h" -#include "BitStream.h" -#include -#include -#include - -using namespace RakNet; - -// From http://www.joelonsoftware.com/articles/Unicode.html -// Only code points 128 and above are stored using 2, 3, in fact, up to 6 bytes. -//#define MAX_BYTES_PER_UNICODE_CHAR 6 -#define MAX_BYTES_PER_UNICODE_CHAR 2 - -RakWString::RakWString() -{ - c_str=0; - c_strCharLength=0; -} -RakWString::RakWString( const RakString &right ) -{ - c_str=0; - c_strCharLength=0; - *this=right; -} -RakWString::RakWString( const char *input ) -{ - c_str=0; - c_strCharLength=0; - *this = input; -} -RakWString::RakWString( const wchar_t *input ) -{ - c_str=0; - c_strCharLength=0; - *this = input; -} -RakWString::RakWString( const RakWString & right) -{ - c_str=0; - c_strCharLength=0; - *this = right; -} -RakWString::~RakWString() -{ - rakFree_Ex(c_str,_FILE_AND_LINE_); -} -RakWString& RakWString::operator = ( const RakWString& right ) -{ - Clear(); - if (right.IsEmpty()) - return *this; - c_str = (wchar_t *) rakMalloc_Ex( (right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!c_str) - { - c_strCharLength=0; - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - c_strCharLength = right.GetLength(); - memcpy(c_str,right.C_String(),(right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR); - - return *this; -} -RakWString& RakWString::operator = ( const RakString& right ) -{ - return *this = right.C_String(); -} -RakWString& RakWString::operator = ( const wchar_t * const str ) -{ - Clear(); - if (str==0) - return *this; - c_strCharLength = wcslen(str); - if (c_strCharLength==0) - return *this; - c_str = (wchar_t *) rakMalloc_Ex( (c_strCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!c_str) - { - c_strCharLength=0; - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - wcscpy(c_str,str); - - return *this; -} -RakWString& RakWString::operator = ( wchar_t *str ) -{ - *this = ( const wchar_t * const) str; - return *this; -} -RakWString& RakWString::operator = ( const char * const str ) -{ - Clear(); - -// Not supported on android -#if !defined(ANDROID) - if (str==0) - return *this; - if (str[0]==0) - return *this; - - c_strCharLength = mbstowcs(NULL, str, 0); - c_str = (wchar_t *) rakMalloc_Ex( (c_strCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!c_str) - { - c_strCharLength=0; - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - - c_strCharLength = mbstowcs(c_str, str, c_strCharLength+1); - if (c_strCharLength == (size_t) (-1)) - { - RAKNET_DEBUG_PRINTF("Couldn't convert string--invalid multibyte character.\n"); - Clear(); - return *this; - } -#else - // mbstowcs not supported on android - RakAssert("mbstowcs not supported on Android" && 0); -#endif // defined(ANDROID) - - return *this; -} -RakWString& RakWString::operator = ( char *str ) -{ - *this = ( const char * const) str; - return *this; -} -RakWString& RakWString::operator +=( const RakWString& right) -{ - if (right.IsEmpty()) - return *this; - size_t newCharLength = c_strCharLength + right.GetLength(); - wchar_t *newCStr; - bool isEmpty = IsEmpty(); - if (isEmpty) - newCStr = (wchar_t *) rakMalloc_Ex( (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - else - newCStr = (wchar_t *) rakRealloc_Ex( c_str, (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!newCStr) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - c_str = newCStr; - c_strCharLength = newCharLength; - if (isEmpty) - { - memcpy(newCStr,right.C_String(),(right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR); - } - else - { - wcscat(c_str, right.C_String()); - } - - return *this; -} -RakWString& RakWString::operator += ( const wchar_t * const right ) -{ - if (right==0) - return *this; - size_t rightLength = wcslen(right); - size_t newCharLength = c_strCharLength + rightLength; - wchar_t *newCStr; - bool isEmpty = IsEmpty(); - if (isEmpty) - newCStr = (wchar_t *) rakMalloc_Ex( (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - else - newCStr = (wchar_t *) rakRealloc_Ex( c_str, (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!newCStr) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return *this; - } - c_str = newCStr; - c_strCharLength = newCharLength; - if (isEmpty) - { - memcpy(newCStr,right,(rightLength + 1) * MAX_BYTES_PER_UNICODE_CHAR); - } - else - { - wcscat(c_str, right); - } - - return *this; -} -RakWString& RakWString::operator += ( wchar_t *right ) -{ - return *this += (const wchar_t * const) right; -} -bool RakWString::operator==(const RakWString &right) const -{ - if (GetLength()!=right.GetLength()) - return false; - return wcscmp(c_str,right.C_String())==0; -} -bool RakWString::operator < ( const RakWString& right ) const -{ - return wcscmp(c_str,right.C_String())<0; -} -bool RakWString::operator <= ( const RakWString& right ) const -{ - return wcscmp(c_str,right.C_String())<=0; -} -bool RakWString::operator > ( const RakWString& right ) const -{ - return wcscmp(c_str,right.C_String())>0; -} -bool RakWString::operator >= ( const RakWString& right ) const -{ - return wcscmp(c_str,right.C_String())>=0; -} -bool RakWString::operator!=(const RakWString &right) const -{ - if (GetLength()!=right.GetLength()) - return true; - return wcscmp(c_str,right.C_String())!=0; -} -void RakWString::Set( wchar_t *str ) -{ - *this = str; -} -bool RakWString::IsEmpty(void) const -{ - return GetLength()==0; -} -size_t RakWString::GetLength(void) const -{ - return c_strCharLength; -} -unsigned long RakWString::ToInteger(const RakWString &rs) -{ - unsigned long hash = 0; - int c; - - const char *str = (const char *)rs.C_String(); - size_t i; - for (i=0; i < rs.GetLength()*MAX_BYTES_PER_UNICODE_CHAR*sizeof(wchar_t); i++) - { - c = *str++; - hash = c + (hash << 6) + (hash << 16) - hash; - } - - return hash; -} -int RakWString::StrCmp(const RakWString &right) const -{ - return wcscmp(C_String(), right.C_String()); -} -int RakWString::StrICmp(const RakWString &right) const -{ -#ifdef _WIN32 - return wcsicmp(C_String(), right.C_String()); -#else - // Not supported - return wcscmp(C_String(), right.C_String()); -#endif -} -void RakWString::Clear(void) -{ - rakFree_Ex(c_str,_FILE_AND_LINE_); - c_str=0; - c_strCharLength=0; -} -void RakWString::Printf(void) -{ - printf("%ls", C_String()); -} -void RakWString::FPrintf(FILE *fp) -{ - fprintf(fp,"%ls", C_String()); -} -void RakWString::Serialize(BitStream *bs) const -{ - bs->WriteCasted(c_strCharLength); - bs->WriteAlignedBytes((const unsigned char*) c_str,(const unsigned int) (c_strCharLength+1)*MAX_BYTES_PER_UNICODE_CHAR); -} -void RakWString::Serialize(const wchar_t * const str, BitStream *bs) -{ - size_t length = wcslen(str); - bs->WriteCasted(length); - bs->WriteAlignedBytes((const unsigned char*) str,(const unsigned int) length*MAX_BYTES_PER_UNICODE_CHAR); -} -bool RakWString::Deserialize(BitStream *bs) -{ - Clear(); - size_t length; - bs->ReadCasted(length); - if (length>0) - { - c_str = (wchar_t *) rakMalloc_Ex( (length + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); - if (!c_str) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return false; - } - c_strCharLength = (size_t) length; - return bs->ReadAlignedBytes((unsigned char*) c_str,(const unsigned int) (c_strCharLength+1)*MAX_BYTES_PER_UNICODE_CHAR); - } - else - { - return true; - } -} -bool RakWString::Deserialize(wchar_t *str, BitStream *bs) -{ - size_t length; - bs->ReadCasted(length); - if (length>0) - { - return bs->ReadAlignedBytes((unsigned char*) str,(const unsigned int) (length+1)*MAX_BYTES_PER_UNICODE_CHAR); - } - else - { - wcscpy(str,L""); - } - return true; -} - -/* -RakNet::BitStream bsTest; -RakNet::RakWString testString("cat"), testString2; -testString = "Hllo"; -testString = L"Hello"; -testString += L" world"; -testString2 += testString2; -RakNet::RakWString ts3(L" from here"); -testString2+=ts3; -RakNet::RakWString ts4(L" 222"); -testString2=ts4; -RakNet::RakString rs("rakstring"); -testString2+=rs; -testString2=rs; -bsTest.Write(L"one"); -bsTest.Write(testString2); -bsTest.SetReadOffset(0); -RakNet::RakWString ts5, ts6; -wchar_t buff[99]; -wchar_t *wptr = (wchar_t*)buff; -bsTest.Read(wptr); -bsTest.Read(ts5); -*/ +#include "RakWString.h" +#include "BitStream.h" +#include +#include +#include + +using namespace RakNet; + +// From http://www.joelonsoftware.com/articles/Unicode.html +// Only code points 128 and above are stored using 2, 3, in fact, up to 6 bytes. +//#define MAX_BYTES_PER_UNICODE_CHAR 6 +#define MAX_BYTES_PER_UNICODE_CHAR 2 + +RakWString::RakWString() +{ + c_str=0; + c_strCharLength=0; +} +RakWString::RakWString( const RakString &right ) +{ + c_str=0; + c_strCharLength=0; + *this=right; +} +RakWString::RakWString( const char *input ) +{ + c_str=0; + c_strCharLength=0; + *this = input; +} +RakWString::RakWString( const wchar_t *input ) +{ + c_str=0; + c_strCharLength=0; + *this = input; +} +RakWString::RakWString( const RakWString & right) +{ + c_str=0; + c_strCharLength=0; + *this = right; +} +RakWString::~RakWString() +{ + rakFree_Ex(c_str,_FILE_AND_LINE_); +} +RakWString& RakWString::operator = ( const RakWString& right ) +{ + Clear(); + if (right.IsEmpty()) + return *this; + c_str = (wchar_t *) rakMalloc_Ex( (right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!c_str) + { + c_strCharLength=0; + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + c_strCharLength = right.GetLength(); + memcpy(c_str,right.C_String(),(right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR); + + return *this; +} +RakWString& RakWString::operator = ( const RakString& right ) +{ + return *this = right.C_String(); +} +RakWString& RakWString::operator = ( const wchar_t * const str ) +{ + Clear(); + if (str==0) + return *this; + c_strCharLength = wcslen(str); + if (c_strCharLength==0) + return *this; + c_str = (wchar_t *) rakMalloc_Ex( (c_strCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!c_str) + { + c_strCharLength=0; + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + wcscpy(c_str,str); + + return *this; +} +RakWString& RakWString::operator = ( wchar_t *str ) +{ + *this = ( const wchar_t * const) str; + return *this; +} +RakWString& RakWString::operator = ( const char * const str ) +{ + Clear(); + +// Not supported on android +#if !defined(ANDROID) + if (str==0) + return *this; + if (str[0]==0) + return *this; + + c_strCharLength = mbstowcs(NULL, str, 0); + c_str = (wchar_t *) rakMalloc_Ex( (c_strCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!c_str) + { + c_strCharLength=0; + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + + c_strCharLength = mbstowcs(c_str, str, c_strCharLength+1); + if (c_strCharLength == (size_t) (-1)) + { + RAKNET_DEBUG_PRINTF("Couldn't convert string--invalid multibyte character.\n"); + Clear(); + return *this; + } +#else + // mbstowcs not supported on android + RakAssert("mbstowcs not supported on Android" && 0); +#endif // defined(ANDROID) + + return *this; +} +RakWString& RakWString::operator = ( char *str ) +{ + *this = ( const char * const) str; + return *this; +} +RakWString& RakWString::operator +=( const RakWString& right) +{ + if (right.IsEmpty()) + return *this; + size_t newCharLength = c_strCharLength + right.GetLength(); + wchar_t *newCStr; + bool isEmpty = IsEmpty(); + if (isEmpty) + newCStr = (wchar_t *) rakMalloc_Ex( (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + else + newCStr = (wchar_t *) rakRealloc_Ex( c_str, (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!newCStr) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + c_str = newCStr; + c_strCharLength = newCharLength; + if (isEmpty) + { + memcpy(newCStr,right.C_String(),(right.GetLength() + 1) * MAX_BYTES_PER_UNICODE_CHAR); + } + else + { + wcscat(c_str, right.C_String()); + } + + return *this; +} +RakWString& RakWString::operator += ( const wchar_t * const right ) +{ + if (right==0) + return *this; + size_t rightLength = wcslen(right); + size_t newCharLength = c_strCharLength + rightLength; + wchar_t *newCStr; + bool isEmpty = IsEmpty(); + if (isEmpty) + newCStr = (wchar_t *) rakMalloc_Ex( (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + else + newCStr = (wchar_t *) rakRealloc_Ex( c_str, (newCharLength + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!newCStr) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return *this; + } + c_str = newCStr; + c_strCharLength = newCharLength; + if (isEmpty) + { + memcpy(newCStr,right,(rightLength + 1) * MAX_BYTES_PER_UNICODE_CHAR); + } + else + { + wcscat(c_str, right); + } + + return *this; +} +RakWString& RakWString::operator += ( wchar_t *right ) +{ + return *this += (const wchar_t * const) right; +} +bool RakWString::operator==(const RakWString &right) const +{ + if (GetLength()!=right.GetLength()) + return false; + return wcscmp(c_str,right.C_String())==0; +} +bool RakWString::operator < ( const RakWString& right ) const +{ + return wcscmp(c_str,right.C_String())<0; +} +bool RakWString::operator <= ( const RakWString& right ) const +{ + return wcscmp(c_str,right.C_String())<=0; +} +bool RakWString::operator > ( const RakWString& right ) const +{ + return wcscmp(c_str,right.C_String())>0; +} +bool RakWString::operator >= ( const RakWString& right ) const +{ + return wcscmp(c_str,right.C_String())>=0; +} +bool RakWString::operator!=(const RakWString &right) const +{ + if (GetLength()!=right.GetLength()) + return true; + return wcscmp(c_str,right.C_String())!=0; +} +void RakWString::Set( wchar_t *str ) +{ + *this = str; +} +bool RakWString::IsEmpty(void) const +{ + return GetLength()==0; +} +size_t RakWString::GetLength(void) const +{ + return c_strCharLength; +} +unsigned long RakWString::ToInteger(const RakWString &rs) +{ + unsigned long hash = 0; + int c; + + const char *str = (const char *)rs.C_String(); + size_t i; + for (i=0; i < rs.GetLength()*MAX_BYTES_PER_UNICODE_CHAR*sizeof(wchar_t); i++) + { + c = *str++; + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} +int RakWString::StrCmp(const RakWString &right) const +{ + return wcscmp(C_String(), right.C_String()); +} +int RakWString::StrICmp(const RakWString &right) const +{ +#ifdef _WIN32 + return wcsicmp(C_String(), right.C_String()); +#else + // Not supported + return wcscmp(C_String(), right.C_String()); +#endif +} +void RakWString::Clear(void) +{ + rakFree_Ex(c_str,_FILE_AND_LINE_); + c_str=0; + c_strCharLength=0; +} +void RakWString::Printf(void) +{ + printf("%ls", C_String()); +} +void RakWString::FPrintf(FILE *fp) +{ + fprintf(fp,"%ls", C_String()); +} +void RakWString::Serialize(BitStream *bs) const +{ + bs->WriteCasted(c_strCharLength); + bs->WriteAlignedBytes((const unsigned char*) c_str,(const unsigned int) (c_strCharLength+1)*MAX_BYTES_PER_UNICODE_CHAR); +} +void RakWString::Serialize(const wchar_t * const str, BitStream *bs) +{ + size_t length = wcslen(str); + bs->WriteCasted(length); + bs->WriteAlignedBytes((const unsigned char*) str,(const unsigned int) length*MAX_BYTES_PER_UNICODE_CHAR); +} +bool RakWString::Deserialize(BitStream *bs) +{ + Clear(); + size_t length; + bs->ReadCasted(length); + if (length>0) + { + c_str = (wchar_t *) rakMalloc_Ex( (length + 1) * MAX_BYTES_PER_UNICODE_CHAR, _FILE_AND_LINE_); + if (!c_str) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return false; + } + c_strCharLength = (size_t) length; + return bs->ReadAlignedBytes((unsigned char*) c_str,(const unsigned int) (c_strCharLength+1)*MAX_BYTES_PER_UNICODE_CHAR); + } + else + { + return true; + } +} +bool RakWString::Deserialize(wchar_t *str, BitStream *bs) +{ + size_t length; + bs->ReadCasted(length); + if (length>0) + { + return bs->ReadAlignedBytes((unsigned char*) str,(const unsigned int) (length+1)*MAX_BYTES_PER_UNICODE_CHAR); + } + else + { + wcscpy(str,L""); + } + return true; +} + +/* +RakNet::BitStream bsTest; +RakNet::RakWString testString("cat"), testString2; +testString = "Hllo"; +testString = L"Hello"; +testString += L" world"; +testString2 += testString2; +RakNet::RakWString ts3(L" from here"); +testString2+=ts3; +RakNet::RakWString ts4(L" 222"); +testString2=ts4; +RakNet::RakString rs("rakstring"); +testString2+=rs; +testString2=rs; +bsTest.Write(L"one"); +bsTest.Write(testString2); +bsTest.SetReadOffset(0); +RakNet::RakWString ts5, ts6; +wchar_t buff[99]; +wchar_t *wptr = (wchar_t*)buff; +bsTest.Read(wptr); +bsTest.Read(ts5); +*/ diff --git a/src/raknet/RakWString.h b/src/raknet/RakWString.h index e3baa7e..f633596 100755 --- a/src/raknet/RakWString.h +++ b/src/raknet/RakWString.h @@ -1,113 +1,113 @@ -#ifndef __RAK_W_STRING_H -#define __RAK_W_STRING_H - -#include "Export.h" -#include "RakNetTypes.h" // int64_t -#include "RakString.h" - -#ifdef _WIN32 - - - -#include "WindowsIncludes.h" -#endif - -namespace RakNet -{ - /// \brief String class for Unicode - class RAK_DLL_EXPORT RakWString - { - public: - // Constructors - RakWString(); - RakWString( const RakString &right ); - RakWString( const wchar_t *input ); - RakWString( const RakWString & right); - RakWString( const char *input ); - ~RakWString(); - - /// Implicit return of wchar_t* - operator wchar_t* () const {if (c_str) return c_str; return (wchar_t*) L"";} - - /// Same as std::string::c_str - const wchar_t* C_String(void) const {if (c_str) return c_str; return (const wchar_t*) L"";} - - /// Assignment operators - RakWString& operator = ( const RakWString& right ); - RakWString& operator = ( const RakString& right ); - RakWString& operator = ( const wchar_t * const str ); - RakWString& operator = ( wchar_t *str ); - RakWString& operator = ( const char * const str ); - RakWString& operator = ( char *str ); - - /// Concatenation - RakWString& operator +=( const RakWString& right); - RakWString& operator += ( const wchar_t * const right ); - RakWString& operator += ( wchar_t *right ); - - /// Equality - bool operator==(const RakWString &right) const; - - // Comparison - bool operator < ( const RakWString& right ) const; - bool operator <= ( const RakWString& right ) const; - bool operator > ( const RakWString& right ) const; - bool operator >= ( const RakWString& right ) const; - - /// Inequality - bool operator!=(const RakWString &right) const; - - /// Set the value of the string - void Set( wchar_t *str ); - - /// Returns if the string is empty. Also, C_String() would return "" - bool IsEmpty(void) const; - - /// Returns the length of the string - size_t GetLength(void) const; - - /// Has the string into an unsigned int - static unsigned long ToInteger(const RakWString &rs); - - /// Compare strings (case sensitive) - int StrCmp(const RakWString &right) const; - - /// Compare strings (not case sensitive) - int StrICmp(const RakWString &right) const; - - /// Clear the string - void Clear(void); - - /// Print the string to the screen - void Printf(void); - - /// Print the string to a file - void FPrintf(FILE *fp); - - /// Serialize to a bitstream, uncompressed (slightly faster) - /// \param[out] bs Bitstream to serialize to - void Serialize(BitStream *bs) const; - - /// Static version of the Serialize function - static void Serialize(const wchar_t * const str, BitStream *bs); - - /// Deserialize what was written by Serialize - /// \param[in] bs Bitstream to serialize from - /// \return true if the deserialization was successful - bool Deserialize(BitStream *bs); - - /// Static version of the Deserialize() function - static bool Deserialize(wchar_t *str, BitStream *bs); - - - protected: - wchar_t* c_str; - size_t c_strCharLength; - }; - -} - -const RakNet::RakWString RAK_DLL_EXPORT operator+(const RakNet::RakWString &lhs, const RakNet::RakWString &rhs); - - -#endif +#ifndef __RAK_W_STRING_H +#define __RAK_W_STRING_H + +#include "Export.h" +#include "RakNetTypes.h" // int64_t +#include "RakString.h" + +#ifdef _WIN32 + + + +#include "WindowsIncludes.h" +#endif + +namespace RakNet +{ + /// \brief String class for Unicode + class RAK_DLL_EXPORT RakWString + { + public: + // Constructors + RakWString(); + RakWString( const RakString &right ); + RakWString( const wchar_t *input ); + RakWString( const RakWString & right); + RakWString( const char *input ); + ~RakWString(); + + /// Implicit return of wchar_t* + operator wchar_t* () const {if (c_str) return c_str; return (wchar_t*) L"";} + + /// Same as std::string::c_str + const wchar_t* C_String(void) const {if (c_str) return c_str; return (const wchar_t*) L"";} + + /// Assignment operators + RakWString& operator = ( const RakWString& right ); + RakWString& operator = ( const RakString& right ); + RakWString& operator = ( const wchar_t * const str ); + RakWString& operator = ( wchar_t *str ); + RakWString& operator = ( const char * const str ); + RakWString& operator = ( char *str ); + + /// Concatenation + RakWString& operator +=( const RakWString& right); + RakWString& operator += ( const wchar_t * const right ); + RakWString& operator += ( wchar_t *right ); + + /// Equality + bool operator==(const RakWString &right) const; + + // Comparison + bool operator < ( const RakWString& right ) const; + bool operator <= ( const RakWString& right ) const; + bool operator > ( const RakWString& right ) const; + bool operator >= ( const RakWString& right ) const; + + /// Inequality + bool operator!=(const RakWString &right) const; + + /// Set the value of the string + void Set( wchar_t *str ); + + /// Returns if the string is empty. Also, C_String() would return "" + bool IsEmpty(void) const; + + /// Returns the length of the string + size_t GetLength(void) const; + + /// Has the string into an unsigned int + static unsigned long ToInteger(const RakWString &rs); + + /// Compare strings (case sensitive) + int StrCmp(const RakWString &right) const; + + /// Compare strings (not case sensitive) + int StrICmp(const RakWString &right) const; + + /// Clear the string + void Clear(void); + + /// Print the string to the screen + void Printf(void); + + /// Print the string to a file + void FPrintf(FILE *fp); + + /// Serialize to a bitstream, uncompressed (slightly faster) + /// \param[out] bs Bitstream to serialize to + void Serialize(BitStream *bs) const; + + /// Static version of the Serialize function + static void Serialize(const wchar_t * const str, BitStream *bs); + + /// Deserialize what was written by Serialize + /// \param[in] bs Bitstream to serialize from + /// \return true if the deserialization was successful + bool Deserialize(BitStream *bs); + + /// Static version of the Deserialize() function + static bool Deserialize(wchar_t *str, BitStream *bs); + + + protected: + wchar_t* c_str; + size_t c_strCharLength; + }; + +} + +const RakNet::RakWString RAK_DLL_EXPORT operator+(const RakNet::RakWString &lhs, const RakNet::RakWString &rhs); + + +#endif diff --git a/src/raknet/Rand.cpp b/src/raknet/Rand.cpp index c3d8413..79fc25f 100755 --- a/src/raknet/Rand.cpp +++ b/src/raknet/Rand.cpp @@ -1,274 +1,274 @@ -/** -* -* Grabbed by Kevin from http://www.math.keio.ac.jp/~matumoto/cokus.c -* This is the ``Mersenne Twister'' random number generator MT19937, which -* generates pseudorandom integers uniformly distributed in 0..(2^32 - 1) -* starting from any odd seed in 0..(2^32 - 1). This version is a recode -* by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by -* Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in -* July-August 1997). -* -* Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha -* running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to -* generate 300 million random numbers; after recoding: 24.0 sec. for the same -* (i.e., 46.5% of original time), so speed is now about 12.5 million random -* number generations per second on this machine. -* -* According to the URL -* (and paraphrasing a bit in places), the Mersenne Twister is ``designed -* with consideration of the flaws of various existing generators,'' has -* a period of 2^19937 - 1, gives a sequence that is 623-dimensionally -* equidistributed, and ``has passed many stringent tests, including the -* die-hard test of G. Marsaglia and the load test of P. Hellekalek and -* S. Wegenkittl.'' It is efficient in memory usage (typically using 2506 -* to 5012 bytes of static data, depending on data type sizes, and the code -* is quite short as well). It generates random numbers in batches of 624 -* at a time, so the caching and pipelining of modern systems is exploited. -* It is also divide- and mod-free. -* -* Licensing is free http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/elicense.html -* -* The code as Shawn received it included the following notice: -* -* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. When -* you use this, send an e-mail to with -* an appropriate reference to your work. -* -* It would be nice to CC: when you write. -*/ - -#include -#include -#include -#include "Rand.h" - -// -// uint32 must be an unsigned integer type capable of holding at least 32 -// bits; exactly 32 should be fastest, but 64 is better on an Alpha with -// GCC at -O3 optimization so try your options and see what's best for you -// - -//typedef unsigned int uint32; - -#define N (624) // length of state vector -#define M (397) // a period parameter -#define K (0x9908B0DFU) // a magic constant -#define hiBit(u) ((u) & 0x80000000U) // mask all but highest bit of u -#define loBit(u) ((u) & 0x00000001U) // mask all but lowest bit of u -#define loBits(u) ((u) & 0x7FFFFFFFU) // mask the highest bit of u -#define mixBits(u, v) (hiBit(u)|loBits(v)) // move hi bit of u to hi bit of v - -static unsigned int _state[ N + 1 ]; // state vector + 1 extra to not violate ANSI C -static unsigned int *_next; // next random value is computed from here -static int _left = -1; // can *next++ this many times before reloading - -using namespace RakNet; - -void seedMT( unsigned int seed, unsigned int *state, unsigned int *&next, int &left ); -unsigned int reloadMT( unsigned int *state, unsigned int *&next, int &left ); -unsigned int randomMT( unsigned int *state, unsigned int *&next, int &left ); -void fillBufferMT( void *buffer, unsigned int bytes, unsigned int *state, unsigned int *&next, int &left ); -float frandomMT( unsigned int *state, unsigned int *&next, int &left ); - -// Uses global vars -void seedMT( unsigned int seed ) -{ - seedMT(seed, _state, _next, _left); -} -unsigned int reloadMT( void ) -{ - return reloadMT(_state, _next, _left); -} -unsigned int randomMT( void ) -{ - return randomMT(_state, _next, _left); -} -float frandomMT( void ) -{ - return frandomMT(_state, _next, _left); -} -void fillBufferMT( void *buffer, unsigned int bytes ) -{ - fillBufferMT(buffer, bytes, _state, _next, _left); -} - -void seedMT( unsigned int seed, unsigned int *state, unsigned int *&next, int &left ) // Defined in cokus_c.c -{ - (void) next; - - // - // We initialize state[0..(N-1)] via the generator - // - // x_new = (69069 * x_old) mod 2^32 - // - // from Line 15 of Table 1, p. 106, Sec. 3.3.4 of Knuth's - // _The Art of Computer Programming_, Volume 2, 3rd ed. - // - // Notes (SJC): I do not know what the initial state requirements - // of the Mersenne Twister are, but it seems this seeding generator - // could be better. It achieves the maximum period for its modulus - // (2^30) iff x_initial is odd (p. 20-21, Sec. 3.2.1.2, Knuth); if - // x_initial can be even, you have sequences like 0, 0, 0, ...; - // 2^31, 2^31, 2^31, ...; 2^30, 2^30, 2^30, ...; 2^29, 2^29 + 2^31, - // 2^29, 2^29 + 2^31, ..., etc. so I force seed to be odd below. - // - // Even if x_initial is odd, if x_initial is 1 mod 4 then - // - // the lowest bit of x is always 1, - // the next-to-lowest bit of x is always 0, - // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , - // the 3rd-from-lowest bit of x 4-cycles ... 0 1 1 0 0 1 1 0 ... , - // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 0 1 1 1 1 0 ... , - // ... - // - // and if x_initial is 3 mod 4 then - // - // the lowest bit of x is always 1, - // the next-to-lowest bit of x is always 1, - // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , - // the 3rd-from-lowest bit of x 4-cycles ... 0 0 1 1 0 0 1 1 ... , - // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 1 1 1 1 0 0 ... , - // ... - // - // The generator's potency (min. s>=0 with (69069-1)^s = 0 mod 2^32) is - // 16, which seems to be alright by p. 25, Sec. 3.2.1.3 of Knuth. It - // also does well in the dimension 2..5 spectral tests, but it could be - // better in dimension 6 (Line 15, Table 1, p. 106, Sec. 3.3.4, Knuth). - // - // Note that the random number user does not see the values generated - // here directly since reloadMT() will always munge them first, so maybe - // none of all of this matters. In fact, the seed values made here could - // even be extra-special desirable if the Mersenne Twister theory says - // so-- that's why the only change I made is to restrict to odd seeds. - // - - register unsigned int x = ( seed | 1U ) & 0xFFFFFFFFU, *s = state; - register int j; - - for ( left = 0, *s++ = x, j = N; --j; - *s++ = ( x *= 69069U ) & 0xFFFFFFFFU ) - - ; -} - - -unsigned int reloadMT( unsigned int *state, unsigned int *&next, int &left ) -{ - register unsigned int * p0 = state, *p2 = state + 2, *pM = state + M, s0, s1; - register int j; - - if ( left < -1 ) - seedMT( 4357U ); - - left = N - 1, next = state + 1; - - for ( s0 = state[ 0 ], s1 = state[ 1 ], j = N - M + 1; --j; s0 = s1, s1 = *p2++ ) - * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); - - for ( pM = state, j = M; --j; s0 = s1, s1 = *p2++ ) - * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); - - s1 = state[ 0 ], *p0 = *pM ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); - - s1 ^= ( s1 >> 11 ); - - s1 ^= ( s1 << 7 ) & 0x9D2C5680U; - - s1 ^= ( s1 << 15 ) & 0xEFC60000U; - - return ( s1 ^ ( s1 >> 18 ) ); -} - - -unsigned int randomMT( unsigned int *state, unsigned int *&next, int &left ) -{ - unsigned int y; - - if ( --left < 0 ) - return ( reloadMT(state, next, left) ); - - y = *next++; - - y ^= ( y >> 11 ); - - y ^= ( y << 7 ) & 0x9D2C5680U; - - y ^= ( y << 15 ) & 0xEFC60000U; - - return ( y ^ ( y >> 18 ) ); - - // This change made so the value returned is in the same range as what rand() returns - // return(y ^ (y >> 18)) % 32767; -} - -void fillBufferMT( void *buffer, unsigned int bytes, unsigned int *state, unsigned int *&next, int &left ) -{ - unsigned int offset=0; - unsigned int r; - while (bytes-offset>=sizeof(r)) - { - r = randomMT(state, next, left); - memcpy((char*)buffer+offset, &r, sizeof(r)); - offset+=sizeof(r); - } - - r = randomMT(state, next, left); - memcpy((char*)buffer+offset, &r, bytes-offset); -} - -float frandomMT( unsigned int *state, unsigned int *&next, int &left ) -{ - return ( float ) ( ( double ) randomMT(state, next, left) / 4294967296.0 ); -} -RakNetRandom::RakNetRandom() -{ - left=-1; -} -RakNetRandom::~RakNetRandom() -{ -} -void RakNetRandom::SeedMT( unsigned int seed ) -{ - printf("%i\n",seed); - seedMT(seed, state, next, left); -} - -unsigned int RakNetRandom::ReloadMT( void ) -{ - return reloadMT(state, next, left); -} - -unsigned int RakNetRandom::RandomMT( void ) -{ - return randomMT(state, next, left); -} - -float RakNetRandom::FrandomMT( void ) -{ - return frandomMT(state, next, left); -} - -void RakNetRandom::FillBufferMT( void *buffer, unsigned int bytes ) -{ - fillBufferMT(buffer, bytes, state, next, left); -} - -/* -int main(void) -{ -int j; - -// you can seed with any uint32, but the best are odds in 0..(2^32 - 1) - -seedMT(4357U); - -// print the first 2,002 random numbers seven to a line as an example - -for(j=0; j<2002; j++) -RAKNET_DEBUG_PRINTF(" %10lu%s", (unsigned int) randomMT(), (j%7)==6 ? "\n" : ""); - -return(EXIT_SUCCESS); -} - -*/ - +/** +* +* Grabbed by Kevin from http://www.math.keio.ac.jp/~matumoto/cokus.c +* This is the ``Mersenne Twister'' random number generator MT19937, which +* generates pseudorandom integers uniformly distributed in 0..(2^32 - 1) +* starting from any odd seed in 0..(2^32 - 1). This version is a recode +* by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by +* Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in +* July-August 1997). +* +* Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha +* running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to +* generate 300 million random numbers; after recoding: 24.0 sec. for the same +* (i.e., 46.5% of original time), so speed is now about 12.5 million random +* number generations per second on this machine. +* +* According to the URL +* (and paraphrasing a bit in places), the Mersenne Twister is ``designed +* with consideration of the flaws of various existing generators,'' has +* a period of 2^19937 - 1, gives a sequence that is 623-dimensionally +* equidistributed, and ``has passed many stringent tests, including the +* die-hard test of G. Marsaglia and the load test of P. Hellekalek and +* S. Wegenkittl.'' It is efficient in memory usage (typically using 2506 +* to 5012 bytes of static data, depending on data type sizes, and the code +* is quite short as well). It generates random numbers in batches of 624 +* at a time, so the caching and pipelining of modern systems is exploited. +* It is also divide- and mod-free. +* +* Licensing is free http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/elicense.html +* +* The code as Shawn received it included the following notice: +* +* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. When +* you use this, send an e-mail to with +* an appropriate reference to your work. +* +* It would be nice to CC: when you write. +*/ + +#include +#include +#include +#include "Rand.h" + +// +// uint32 must be an unsigned integer type capable of holding at least 32 +// bits; exactly 32 should be fastest, but 64 is better on an Alpha with +// GCC at -O3 optimization so try your options and see what's best for you +// + +//typedef unsigned int uint32; + +#define N (624) // length of state vector +#define M (397) // a period parameter +#define K (0x9908B0DFU) // a magic constant +#define hiBit(u) ((u) & 0x80000000U) // mask all but highest bit of u +#define loBit(u) ((u) & 0x00000001U) // mask all but lowest bit of u +#define loBits(u) ((u) & 0x7FFFFFFFU) // mask the highest bit of u +#define mixBits(u, v) (hiBit(u)|loBits(v)) // move hi bit of u to hi bit of v + +static unsigned int _state[ N + 1 ]; // state vector + 1 extra to not violate ANSI C +static unsigned int *_next; // next random value is computed from here +static int _left = -1; // can *next++ this many times before reloading + +using namespace RakNet; + +void seedMT( unsigned int seed, unsigned int *state, unsigned int *&next, int &left ); +unsigned int reloadMT( unsigned int *state, unsigned int *&next, int &left ); +unsigned int randomMT( unsigned int *state, unsigned int *&next, int &left ); +void fillBufferMT( void *buffer, unsigned int bytes, unsigned int *state, unsigned int *&next, int &left ); +float frandomMT( unsigned int *state, unsigned int *&next, int &left ); + +// Uses global vars +void seedMT( unsigned int seed ) +{ + seedMT(seed, _state, _next, _left); +} +unsigned int reloadMT( void ) +{ + return reloadMT(_state, _next, _left); +} +unsigned int randomMT( void ) +{ + return randomMT(_state, _next, _left); +} +float frandomMT( void ) +{ + return frandomMT(_state, _next, _left); +} +void fillBufferMT( void *buffer, unsigned int bytes ) +{ + fillBufferMT(buffer, bytes, _state, _next, _left); +} + +void seedMT( unsigned int seed, unsigned int *state, unsigned int *&next, int &left ) // Defined in cokus_c.c +{ + (void) next; + + // + // We initialize state[0..(N-1)] via the generator + // + // x_new = (69069 * x_old) mod 2^32 + // + // from Line 15 of Table 1, p. 106, Sec. 3.3.4 of Knuth's + // _The Art of Computer Programming_, Volume 2, 3rd ed. + // + // Notes (SJC): I do not know what the initial state requirements + // of the Mersenne Twister are, but it seems this seeding generator + // could be better. It achieves the maximum period for its modulus + // (2^30) iff x_initial is odd (p. 20-21, Sec. 3.2.1.2, Knuth); if + // x_initial can be even, you have sequences like 0, 0, 0, ...; + // 2^31, 2^31, 2^31, ...; 2^30, 2^30, 2^30, ...; 2^29, 2^29 + 2^31, + // 2^29, 2^29 + 2^31, ..., etc. so I force seed to be odd below. + // + // Even if x_initial is odd, if x_initial is 1 mod 4 then + // + // the lowest bit of x is always 1, + // the next-to-lowest bit of x is always 0, + // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , + // the 3rd-from-lowest bit of x 4-cycles ... 0 1 1 0 0 1 1 0 ... , + // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 0 1 1 1 1 0 ... , + // ... + // + // and if x_initial is 3 mod 4 then + // + // the lowest bit of x is always 1, + // the next-to-lowest bit of x is always 1, + // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , + // the 3rd-from-lowest bit of x 4-cycles ... 0 0 1 1 0 0 1 1 ... , + // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 1 1 1 1 0 0 ... , + // ... + // + // The generator's potency (min. s>=0 with (69069-1)^s = 0 mod 2^32) is + // 16, which seems to be alright by p. 25, Sec. 3.2.1.3 of Knuth. It + // also does well in the dimension 2..5 spectral tests, but it could be + // better in dimension 6 (Line 15, Table 1, p. 106, Sec. 3.3.4, Knuth). + // + // Note that the random number user does not see the values generated + // here directly since reloadMT() will always munge them first, so maybe + // none of all of this matters. In fact, the seed values made here could + // even be extra-special desirable if the Mersenne Twister theory says + // so-- that's why the only change I made is to restrict to odd seeds. + // + + register unsigned int x = ( seed | 1U ) & 0xFFFFFFFFU, *s = state; + register int j; + + for ( left = 0, *s++ = x, j = N; --j; + *s++ = ( x *= 69069U ) & 0xFFFFFFFFU ) + + ; +} + + +unsigned int reloadMT( unsigned int *state, unsigned int *&next, int &left ) +{ + register unsigned int * p0 = state, *p2 = state + 2, *pM = state + M, s0, s1; + register int j; + + if ( left < -1 ) + seedMT( 4357U ); + + left = N - 1, next = state + 1; + + for ( s0 = state[ 0 ], s1 = state[ 1 ], j = N - M + 1; --j; s0 = s1, s1 = *p2++ ) + * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); + + for ( pM = state, j = M; --j; s0 = s1, s1 = *p2++ ) + * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); + + s1 = state[ 0 ], *p0 = *pM ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); + + s1 ^= ( s1 >> 11 ); + + s1 ^= ( s1 << 7 ) & 0x9D2C5680U; + + s1 ^= ( s1 << 15 ) & 0xEFC60000U; + + return ( s1 ^ ( s1 >> 18 ) ); +} + + +unsigned int randomMT( unsigned int *state, unsigned int *&next, int &left ) +{ + unsigned int y; + + if ( --left < 0 ) + return ( reloadMT(state, next, left) ); + + y = *next++; + + y ^= ( y >> 11 ); + + y ^= ( y << 7 ) & 0x9D2C5680U; + + y ^= ( y << 15 ) & 0xEFC60000U; + + return ( y ^ ( y >> 18 ) ); + + // This change made so the value returned is in the same range as what rand() returns + // return(y ^ (y >> 18)) % 32767; +} + +void fillBufferMT( void *buffer, unsigned int bytes, unsigned int *state, unsigned int *&next, int &left ) +{ + unsigned int offset=0; + unsigned int r; + while (bytes-offset>=sizeof(r)) + { + r = randomMT(state, next, left); + memcpy((char*)buffer+offset, &r, sizeof(r)); + offset+=sizeof(r); + } + + r = randomMT(state, next, left); + memcpy((char*)buffer+offset, &r, bytes-offset); +} + +float frandomMT( unsigned int *state, unsigned int *&next, int &left ) +{ + return ( float ) ( ( double ) randomMT(state, next, left) / 4294967296.0 ); +} +RakNetRandom::RakNetRandom() +{ + left=-1; +} +RakNetRandom::~RakNetRandom() +{ +} +void RakNetRandom::SeedMT( unsigned int seed ) +{ + printf("%i\n",seed); + seedMT(seed, state, next, left); +} + +unsigned int RakNetRandom::ReloadMT( void ) +{ + return reloadMT(state, next, left); +} + +unsigned int RakNetRandom::RandomMT( void ) +{ + return randomMT(state, next, left); +} + +float RakNetRandom::FrandomMT( void ) +{ + return frandomMT(state, next, left); +} + +void RakNetRandom::FillBufferMT( void *buffer, unsigned int bytes ) +{ + fillBufferMT(buffer, bytes, state, next, left); +} + +/* +int main(void) +{ +int j; + +// you can seed with any uint32, but the best are odds in 0..(2^32 - 1) + +seedMT(4357U); + +// print the first 2,002 random numbers seven to a line as an example + +for(j=0; j<2002; j++) +RAKNET_DEBUG_PRINTF(" %10lu%s", (unsigned int) randomMT(), (j%7)==6 ? "\n" : ""); + +return(EXIT_SUCCESS); +} + +*/ + diff --git a/src/raknet/Rand.h b/src/raknet/Rand.h index c7efab3..508e11a 100755 --- a/src/raknet/Rand.h +++ b/src/raknet/Rand.h @@ -1,60 +1,60 @@ -/// \file -/// \brief \b [Internal] Random number generator -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - - -#ifndef __RAND_H -#define __RAND_H - -#include "Export.h" - -/// Initialise seed for Random Generator -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -/// \param[in] seed The seed value for the random number generator. -extern void RAK_DLL_EXPORT seedMT( unsigned int seed ); - -/// \internal -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -extern unsigned int RAK_DLL_EXPORT reloadMT( void ); - -/// Gets a random unsigned int -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -/// \return an integer random value. -extern unsigned int RAK_DLL_EXPORT randomMT( void ); - -/// Gets a random float -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -/// \return 0 to 1.0f, inclusive -extern float RAK_DLL_EXPORT frandomMT( void ); - -/// Randomizes a buffer -/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread -extern void RAK_DLL_EXPORT fillBufferMT( void *buffer, unsigned int bytes ); - -namespace RakNet { - -// Same thing as above functions, but not global -class RAK_DLL_EXPORT RakNetRandom -{ -public: - RakNetRandom(); - ~RakNetRandom(); - void SeedMT( unsigned int seed ); - unsigned int ReloadMT( void ); - unsigned int RandomMT( void ); - float FrandomMT( void ); - void FillBufferMT( void *buffer, unsigned int bytes ); - -protected: - unsigned int state[ 624 + 1 ]; - unsigned int *next; - int left; -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief \b [Internal] Random number generator +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + + +#ifndef __RAND_H +#define __RAND_H + +#include "Export.h" + +/// Initialise seed for Random Generator +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +/// \param[in] seed The seed value for the random number generator. +extern void RAK_DLL_EXPORT seedMT( unsigned int seed ); + +/// \internal +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +extern unsigned int RAK_DLL_EXPORT reloadMT( void ); + +/// Gets a random unsigned int +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +/// \return an integer random value. +extern unsigned int RAK_DLL_EXPORT randomMT( void ); + +/// Gets a random float +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +/// \return 0 to 1.0f, inclusive +extern float RAK_DLL_EXPORT frandomMT( void ); + +/// Randomizes a buffer +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +extern void RAK_DLL_EXPORT fillBufferMT( void *buffer, unsigned int bytes ); + +namespace RakNet { + +// Same thing as above functions, but not global +class RAK_DLL_EXPORT RakNetRandom +{ +public: + RakNetRandom(); + ~RakNetRandom(); + void SeedMT( unsigned int seed ); + unsigned int ReloadMT( void ); + unsigned int RandomMT( void ); + float FrandomMT( void ); + void FillBufferMT( void *buffer, unsigned int bytes ); + +protected: + unsigned int state[ 624 + 1 ]; + unsigned int *next; + int left; +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/ReadyEvent.cpp b/src/raknet/ReadyEvent.cpp index 277efb4..8e58542 100755 --- a/src/raknet/ReadyEvent.cpp +++ b/src/raknet/ReadyEvent.cpp @@ -1,564 +1,564 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ReadyEvent==1 - -#include "ReadyEvent.h" -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakAssert.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -int RakNet::ReadyEvent::RemoteSystemCompBySystemAddress( const SystemAddress &key, const RemoteSystem &data ) -{ - if (key < data.systemAddress) - return -1; - else if (key==data.systemAddress) - return 0; - else - return 1; -} - -int RakNet::ReadyEvent::ReadyEventNodeComp( const int &key, ReadyEvent::ReadyEventNode * const &data ) -{ - if (key < data->eventId) - return -1; - else if (key==data->eventId) - return 0; - else - return 1; -} - -STATIC_FACTORY_DEFINITIONS(ReadyEvent,ReadyEvent); - -ReadyEvent::ReadyEvent() -{ - channel=0; -} - -ReadyEvent::~ReadyEvent() -{ - Clear(); -} - - -bool ReadyEvent::SetEvent(int eventId, bool isReady) -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists==false) - { - // Totally new event - CreateNewEvent(eventId, isReady); - } - else - { - return SetEventByIndex(eventIndex, isReady); - } - return true; -} -bool ReadyEvent::ForceCompletion(int eventId) -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists==false) - { - // Totally new event - CreateNewEvent(eventId, true); - eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - } - - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - ren->eventStatus=ID_READY_EVENT_FORCE_ALL_SET; - UpdateReadyStatus(eventIndex); - - return true; -} -bool ReadyEvent::DeleteEvent(int eventId) -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - RakNet::OP_DELETE(readyEventNodeList[eventIndex], _FILE_AND_LINE_); - readyEventNodeList.RemoveAtIndex(eventIndex); - return true; - } - return false; -} -bool ReadyEvent::IsEventSet(int eventId) -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return readyEventNodeList[eventIndex]->eventStatus==ID_READY_EVENT_SET || readyEventNodeList[eventIndex]->eventStatus==ID_READY_EVENT_ALL_SET; - } - return false; -} -bool ReadyEvent::IsEventCompletionProcessing(int eventId) const -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - bool anyAllReady=false; - bool allAllReady=true; - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) - return false; - for (unsigned i=0; i < ren->systemList.Size(); i++) - { - if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_ALL_SET) - anyAllReady=true; - else - allAllReady=false; - } - return anyAllReady==true && allAllReady==false; - } - return false; -} -bool ReadyEvent::IsEventCompleted(int eventId) const -{ - bool objectExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return IsEventCompletedByIndex(eventIndex); - } - return false; -} - -bool ReadyEvent::HasEvent(int eventId) -{ - return readyEventNodeList.HasData(eventId); -} - -unsigned ReadyEvent::GetEventListSize(void) const -{ - return readyEventNodeList.Size(); -} - -int ReadyEvent::GetEventAtIndex(unsigned index) const -{ - return readyEventNodeList[index]->eventId; -} - -bool ReadyEvent::AddToWaitList(int eventId, SystemAddress address) -{ - bool eventExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &eventExists); - if (eventExists==false) - eventIndex=CreateNewEvent(eventId, false); - - // Don't do this, otherwise if we are trying to start a 3 player game, it will not allow the 3rd player to hit ready if the first two players have already done so - //if (IsLocked(eventIndex)) - // return false; // Not in the list, but event is already completed, or is starting to complete, and adding more waiters would fail this. - - unsigned i; - unsigned numAdded=0; - if (address==UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < rakPeerInterface->GetMaximumNumberOfPeers(); i++) - { - SystemAddress internalAddress = rakPeerInterface->GetSystemAddressFromIndex(i); - if (internalAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - numAdded+=AddToWaitListInternal(eventIndex, internalAddress); - } - } - } - else - { - numAdded=AddToWaitListInternal(eventIndex, address); - } - - if (numAdded>0) - UpdateReadyStatus(eventIndex); - return numAdded>0; -} -bool ReadyEvent::RemoveFromWaitList(int eventId, SystemAddress address) -{ - bool eventExists; - unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &eventExists); - if (eventExists) - { - if (address==UNASSIGNED_SYSTEM_ADDRESS) - { - // Remove all waiters - readyEventNodeList[eventIndex]->systemList.Clear(false, _FILE_AND_LINE_); - UpdateReadyStatus(eventIndex); - } - else - { - bool systemExists; - unsigned systemIndex = readyEventNodeList[eventIndex]->systemList.GetIndexFromKey(address, &systemExists); - if (systemExists) - { - bool isCompleted = IsEventCompletedByIndex(eventIndex); - readyEventNodeList[eventIndex]->systemList.RemoveAtIndex(systemIndex); - - if (isCompleted==false && IsEventCompletedByIndex(eventIndex)) - PushCompletionPacket(readyEventNodeList[eventIndex]->eventId); - - UpdateReadyStatus(eventIndex); - - return true; - } - } - } - - return false; -} -bool ReadyEvent::IsInWaitList(int eventId, SystemAddress address) -{ - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return readyEventNodeList[readyIndex]->systemList.HasData(address); - } - return false; -} - -unsigned ReadyEvent::GetRemoteWaitListSize(int eventId) const -{ - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return readyEventNodeList[readyIndex]->systemList.Size(); - } - return 0; -} - -SystemAddress ReadyEvent::GetFromWaitListAtIndex(int eventId, unsigned index) const -{ - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - return readyEventNodeList[readyIndex]->systemList[index].systemAddress; - } - return UNASSIGNED_SYSTEM_ADDRESS; -} -ReadyEventSystemStatus ReadyEvent::GetReadyStatus(int eventId, SystemAddress address) -{ - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - ReadyEventNode *ren = readyEventNodeList[readyIndex]; - unsigned systemIndex = ren->systemList.GetIndexFromKey(address, &objectExists); - if (objectExists==false) - return RES_NOT_WAITING; - if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_SET) - return RES_READY; - if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_UNSET) - return RES_WAITING; - if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_ALL_SET) - return RES_ALL_READY; - } - - return RES_UNKNOWN_EVENT; -} -void ReadyEvent::SetSendChannel(unsigned char newChannel) -{ - channel=newChannel; -} -PluginReceiveResult ReadyEvent::OnReceive(Packet *packet) -{ - unsigned char packetIdentifier; - packetIdentifier = ( unsigned char ) packet->data[ 0 ]; - -// bool doPrint = packet->systemAddress.GetPort()==60002 || rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).GetPort()==60002; - - switch (packetIdentifier) - { - case ID_READY_EVENT_UNSET: - case ID_READY_EVENT_SET: - case ID_READY_EVENT_ALL_SET: -// if (doPrint) {if (packet->systemAddress.GetPort()==60002) RAKNET_DEBUG_PRINTF("FROM 60002: "); else if (rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port==60002) RAKNET_DEBUG_PRINTF("TO 60002: "); RAKNET_DEBUG_PRINTF("ID_READY_EVENT_SET\n");} - OnReadyEventPacketUpdate(packet); - return RR_CONTINUE_PROCESSING; - case ID_READY_EVENT_FORCE_ALL_SET: - OnReadyEventForceAllSet(packet); - return RR_CONTINUE_PROCESSING; - case ID_READY_EVENT_QUERY: -// if (doPrint) {if (packet->systemAddress.GetPort()==60002) RAKNET_DEBUG_PRINTF("FROM 60002: "); else if (rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port==60002) RAKNET_DEBUG_PRINTF("TO 60002: "); RAKNET_DEBUG_PRINTF("ID_READY_EVENT_QUERY\n");} - OnReadyEventQuery(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - return RR_CONTINUE_PROCESSING; -} -bool ReadyEvent::AddToWaitListInternal(unsigned eventIndex, SystemAddress address) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - bool objectExists; - unsigned systemIndex = ren->systemList.GetIndexFromKey(address, &objectExists); - if (objectExists==false) - { - RemoteSystem rs; - rs.lastReceivedStatus=ID_READY_EVENT_UNSET; - rs.lastSentStatus=ID_READY_EVENT_UNSET; - rs.systemAddress=address; - ren->systemList.InsertAtIndex(rs,systemIndex, _FILE_AND_LINE_); - - SendReadyStateQuery(ren->eventId, address); - return true; - } - return false; -} -void ReadyEvent::OnReadyEventForceAllSet(Packet *packet) -{ - RakNet::BitStream incomingBitStream(packet->data, packet->length, false); - incomingBitStream.IgnoreBits(8); - int eventId; - incomingBitStream.Read(eventId); - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - ReadyEventNode *ren = readyEventNodeList[readyIndex]; - if (ren->eventStatus!=ID_READY_EVENT_FORCE_ALL_SET) - { - ren->eventStatus=ID_READY_EVENT_FORCE_ALL_SET; - PushCompletionPacket(ren->eventId); - } - } -} -void ReadyEvent::OnReadyEventPacketUpdate(Packet *packet) -{ - RakNet::BitStream incomingBitStream(packet->data, packet->length, false); - incomingBitStream.IgnoreBits(8); - int eventId; - incomingBitStream.Read(eventId); - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - ReadyEventNode *ren = readyEventNodeList[readyIndex]; - bool systemExists; - unsigned systemIndex = ren->systemList.GetIndexFromKey(packet->systemAddress, &systemExists); - if (systemExists) - { - // Just return if no change - if (ren->systemList[systemIndex].lastReceivedStatus==packet->data[0]) - return; - - bool wasCompleted = IsEventCompletedByIndex(readyIndex); - ren->systemList[systemIndex].lastReceivedStatus=packet->data[0]; - // If forced all set, doesn't matter what the new packet is - if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) - return; - UpdateReadyStatus(readyIndex); - if (wasCompleted==false && IsEventCompletedByIndex(readyIndex)) - PushCompletionPacket(readyIndex); - } - } -} -void ReadyEvent::OnReadyEventQuery(Packet *packet) -{ - RakNet::BitStream incomingBitStream(packet->data, packet->length, false); - incomingBitStream.IgnoreBits(8); - int eventId; - incomingBitStream.Read(eventId); - bool objectExists; - unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); - if (objectExists) - { - unsigned systemIndex = readyEventNodeList[readyIndex]->systemList.GetIndexFromKey(packet->systemAddress,&objectExists); - // Force the non-default send, because our initial send may have arrived at a system that didn't yet create the ready event - if (objectExists) - SendReadyUpdate(readyIndex, systemIndex, true); - } -} -void ReadyEvent::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) systemAddress; - (void) rakNetGUID; - (void) lostConnectionReason; - - RemoveFromAllLists(systemAddress); -} -void ReadyEvent::OnRakPeerShutdown(void) -{ - Clear(); -} - -bool ReadyEvent::SetEventByIndex(int eventIndex, bool isReady) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - if ((ren->eventStatus==ID_READY_EVENT_ALL_SET || ren->eventStatus==ID_READY_EVENT_SET) && isReady==true) - return true; // Success - no change - if (ren->eventStatus==ID_READY_EVENT_UNSET && isReady==false) - return true; // Success - no change - if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) - return true; // Can't change - - if (isReady) - ren->eventStatus=ID_READY_EVENT_SET; - else - ren->eventStatus=ID_READY_EVENT_UNSET; - - UpdateReadyStatus(eventIndex); - - // Check if now completed, and if so, tell the user about it - if (IsEventCompletedByIndex(eventIndex)) - { - PushCompletionPacket(ren->eventId); - } - - return true; -} - -bool ReadyEvent::IsEventCompletedByIndex(unsigned eventIndex) const -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - unsigned i; - if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) - return true; - if (ren->eventStatus!=ID_READY_EVENT_ALL_SET) - return false; - for (i=0; i < ren->systemList.Size(); i++) - if (ren->systemList[i].lastReceivedStatus!=ID_READY_EVENT_ALL_SET) - return false; - return true; -} - -void ReadyEvent::Clear(void) -{ - unsigned i; - for (i=0; i < readyEventNodeList.Size(); i++) - { - RakNet::OP_DELETE(readyEventNodeList[i], _FILE_AND_LINE_); - } - readyEventNodeList.Clear(false, _FILE_AND_LINE_); -} - -unsigned ReadyEvent::CreateNewEvent(int eventId, bool isReady) -{ - ReadyEventNode *ren = RakNet::OP_NEW( _FILE_AND_LINE_ ); - ren->eventId=eventId; - if (isReady==false) - ren->eventStatus=ID_READY_EVENT_UNSET; - else - ren->eventStatus=ID_READY_EVENT_SET; - return readyEventNodeList.Insert(eventId, ren, true, _FILE_AND_LINE_); -} -void ReadyEvent::UpdateReadyStatus(unsigned eventIndex) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - bool anyUnset; - unsigned i; - if (ren->eventStatus==ID_READY_EVENT_SET) - { - // If you are set, and no other systems are ID_READY_EVENT_UNSET, then change your status to ID_READY_EVENT_ALL_SET - anyUnset=false; - for (i=0; i < ren->systemList.Size(); i++) - { - if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_UNSET) - { - anyUnset=true; - break; - } - } - if (anyUnset==false) - { - ren->eventStatus=ID_READY_EVENT_ALL_SET; - } - } - else if (ren->eventStatus==ID_READY_EVENT_ALL_SET) - { - // If you are all set, and any systems are ID_READY_EVENT_UNSET, then change your status to ID_READY_EVENT_SET - anyUnset=false; - for (i=0; i < ren->systemList.Size(); i++) - { - if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_UNSET) - { - anyUnset=true; - break; - } - } - if (anyUnset==true) - { - ren->eventStatus=ID_READY_EVENT_SET; - } - } - BroadcastReadyUpdate(eventIndex, false); -} -void ReadyEvent::SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - RakNet::BitStream bs; - // I do this rather than write true or false, so users that do not use BitStreams can still read the data - if ((ren->eventStatus!=ren->systemList[systemIndex].lastSentStatus) || - (forceIfNotDefault && ren->eventStatus!=ID_READY_EVENT_UNSET)) - { - bs.Write(ren->eventStatus); - bs.Write(ren->eventId); - SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, ren->systemList[systemIndex].systemAddress, false); - - ren->systemList[systemIndex].lastSentStatus=ren->eventStatus; - } - -} -void ReadyEvent::BroadcastReadyUpdate(unsigned eventIndex, bool forceIfNotDefault) -{ - ReadyEventNode *ren = readyEventNodeList[eventIndex]; - unsigned systemIndex; - for (systemIndex=0; systemIndex < ren->systemList.Size(); systemIndex++) - { - SendReadyUpdate(eventIndex, systemIndex, forceIfNotDefault); - } -} -void ReadyEvent::SendReadyStateQuery(unsigned eventId, SystemAddress address) -{ - RakNet::BitStream bs; - bs.Write((MessageID)ID_READY_EVENT_QUERY); - bs.Write(eventId); - SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, address, false); -} -void ReadyEvent::RemoveFromAllLists(SystemAddress address) -{ - unsigned eventIndex; - for (eventIndex=0; eventIndex < readyEventNodeList.Size(); eventIndex++) - { - bool isCompleted = IsEventCompletedByIndex(eventIndex); - bool systemExists; - unsigned systemIndex; - - systemIndex = readyEventNodeList[eventIndex]->systemList.GetIndexFromKey(address, &systemExists); - if (systemExists) - readyEventNodeList[eventIndex]->systemList.RemoveAtIndex(systemIndex); - - UpdateReadyStatus(eventIndex); - - if (isCompleted==false && IsEventCompletedByIndex(eventIndex)) - PushCompletionPacket(readyEventNodeList[eventIndex]->eventId); - } -} -void ReadyEvent::PushCompletionPacket(unsigned eventId) -{ - (void) eventId; - // Not necessary - /* - // Pass a packet to the user that we are now completed, as setting ourselves to signaled was the last thing being waited on - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(int)); - RakNet::BitStream bs(p->data, sizeof(MessageID)+sizeof(int), false); - bs.SetWriteOffset(0); - bs.Write((MessageID)ID_READY_EVENT_ALL_SET); - bs.Write(eventId); - rakPeerInterface->PushBackPacket(p, false); - */ -} -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ReadyEvent==1 + +#include "ReadyEvent.h" +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakAssert.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +int RakNet::ReadyEvent::RemoteSystemCompBySystemAddress( const SystemAddress &key, const RemoteSystem &data ) +{ + if (key < data.systemAddress) + return -1; + else if (key==data.systemAddress) + return 0; + else + return 1; +} + +int RakNet::ReadyEvent::ReadyEventNodeComp( const int &key, ReadyEvent::ReadyEventNode * const &data ) +{ + if (key < data->eventId) + return -1; + else if (key==data->eventId) + return 0; + else + return 1; +} + +STATIC_FACTORY_DEFINITIONS(ReadyEvent,ReadyEvent); + +ReadyEvent::ReadyEvent() +{ + channel=0; +} + +ReadyEvent::~ReadyEvent() +{ + Clear(); +} + + +bool ReadyEvent::SetEvent(int eventId, bool isReady) +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists==false) + { + // Totally new event + CreateNewEvent(eventId, isReady); + } + else + { + return SetEventByIndex(eventIndex, isReady); + } + return true; +} +bool ReadyEvent::ForceCompletion(int eventId) +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists==false) + { + // Totally new event + CreateNewEvent(eventId, true); + eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + } + + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + ren->eventStatus=ID_READY_EVENT_FORCE_ALL_SET; + UpdateReadyStatus(eventIndex); + + return true; +} +bool ReadyEvent::DeleteEvent(int eventId) +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + RakNet::OP_DELETE(readyEventNodeList[eventIndex], _FILE_AND_LINE_); + readyEventNodeList.RemoveAtIndex(eventIndex); + return true; + } + return false; +} +bool ReadyEvent::IsEventSet(int eventId) +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return readyEventNodeList[eventIndex]->eventStatus==ID_READY_EVENT_SET || readyEventNodeList[eventIndex]->eventStatus==ID_READY_EVENT_ALL_SET; + } + return false; +} +bool ReadyEvent::IsEventCompletionProcessing(int eventId) const +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + bool anyAllReady=false; + bool allAllReady=true; + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) + return false; + for (unsigned i=0; i < ren->systemList.Size(); i++) + { + if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_ALL_SET) + anyAllReady=true; + else + allAllReady=false; + } + return anyAllReady==true && allAllReady==false; + } + return false; +} +bool ReadyEvent::IsEventCompleted(int eventId) const +{ + bool objectExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return IsEventCompletedByIndex(eventIndex); + } + return false; +} + +bool ReadyEvent::HasEvent(int eventId) +{ + return readyEventNodeList.HasData(eventId); +} + +unsigned ReadyEvent::GetEventListSize(void) const +{ + return readyEventNodeList.Size(); +} + +int ReadyEvent::GetEventAtIndex(unsigned index) const +{ + return readyEventNodeList[index]->eventId; +} + +bool ReadyEvent::AddToWaitList(int eventId, SystemAddress address) +{ + bool eventExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &eventExists); + if (eventExists==false) + eventIndex=CreateNewEvent(eventId, false); + + // Don't do this, otherwise if we are trying to start a 3 player game, it will not allow the 3rd player to hit ready if the first two players have already done so + //if (IsLocked(eventIndex)) + // return false; // Not in the list, but event is already completed, or is starting to complete, and adding more waiters would fail this. + + unsigned i; + unsigned numAdded=0; + if (address==UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < rakPeerInterface->GetMaximumNumberOfPeers(); i++) + { + SystemAddress internalAddress = rakPeerInterface->GetSystemAddressFromIndex(i); + if (internalAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + numAdded+=AddToWaitListInternal(eventIndex, internalAddress); + } + } + } + else + { + numAdded=AddToWaitListInternal(eventIndex, address); + } + + if (numAdded>0) + UpdateReadyStatus(eventIndex); + return numAdded>0; +} +bool ReadyEvent::RemoveFromWaitList(int eventId, SystemAddress address) +{ + bool eventExists; + unsigned eventIndex = readyEventNodeList.GetIndexFromKey(eventId, &eventExists); + if (eventExists) + { + if (address==UNASSIGNED_SYSTEM_ADDRESS) + { + // Remove all waiters + readyEventNodeList[eventIndex]->systemList.Clear(false, _FILE_AND_LINE_); + UpdateReadyStatus(eventIndex); + } + else + { + bool systemExists; + unsigned systemIndex = readyEventNodeList[eventIndex]->systemList.GetIndexFromKey(address, &systemExists); + if (systemExists) + { + bool isCompleted = IsEventCompletedByIndex(eventIndex); + readyEventNodeList[eventIndex]->systemList.RemoveAtIndex(systemIndex); + + if (isCompleted==false && IsEventCompletedByIndex(eventIndex)) + PushCompletionPacket(readyEventNodeList[eventIndex]->eventId); + + UpdateReadyStatus(eventIndex); + + return true; + } + } + } + + return false; +} +bool ReadyEvent::IsInWaitList(int eventId, SystemAddress address) +{ + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return readyEventNodeList[readyIndex]->systemList.HasData(address); + } + return false; +} + +unsigned ReadyEvent::GetRemoteWaitListSize(int eventId) const +{ + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return readyEventNodeList[readyIndex]->systemList.Size(); + } + return 0; +} + +SystemAddress ReadyEvent::GetFromWaitListAtIndex(int eventId, unsigned index) const +{ + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + return readyEventNodeList[readyIndex]->systemList[index].systemAddress; + } + return UNASSIGNED_SYSTEM_ADDRESS; +} +ReadyEventSystemStatus ReadyEvent::GetReadyStatus(int eventId, SystemAddress address) +{ + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + ReadyEventNode *ren = readyEventNodeList[readyIndex]; + unsigned systemIndex = ren->systemList.GetIndexFromKey(address, &objectExists); + if (objectExists==false) + return RES_NOT_WAITING; + if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_SET) + return RES_READY; + if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_UNSET) + return RES_WAITING; + if (ren->systemList[systemIndex].lastReceivedStatus==ID_READY_EVENT_ALL_SET) + return RES_ALL_READY; + } + + return RES_UNKNOWN_EVENT; +} +void ReadyEvent::SetSendChannel(unsigned char newChannel) +{ + channel=newChannel; +} +PluginReceiveResult ReadyEvent::OnReceive(Packet *packet) +{ + unsigned char packetIdentifier; + packetIdentifier = ( unsigned char ) packet->data[ 0 ]; + +// bool doPrint = packet->systemAddress.GetPort()==60002 || rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).GetPort()==60002; + + switch (packetIdentifier) + { + case ID_READY_EVENT_UNSET: + case ID_READY_EVENT_SET: + case ID_READY_EVENT_ALL_SET: +// if (doPrint) {if (packet->systemAddress.GetPort()==60002) RAKNET_DEBUG_PRINTF("FROM 60002: "); else if (rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port==60002) RAKNET_DEBUG_PRINTF("TO 60002: "); RAKNET_DEBUG_PRINTF("ID_READY_EVENT_SET\n");} + OnReadyEventPacketUpdate(packet); + return RR_CONTINUE_PROCESSING; + case ID_READY_EVENT_FORCE_ALL_SET: + OnReadyEventForceAllSet(packet); + return RR_CONTINUE_PROCESSING; + case ID_READY_EVENT_QUERY: +// if (doPrint) {if (packet->systemAddress.GetPort()==60002) RAKNET_DEBUG_PRINTF("FROM 60002: "); else if (rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port==60002) RAKNET_DEBUG_PRINTF("TO 60002: "); RAKNET_DEBUG_PRINTF("ID_READY_EVENT_QUERY\n");} + OnReadyEventQuery(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + return RR_CONTINUE_PROCESSING; +} +bool ReadyEvent::AddToWaitListInternal(unsigned eventIndex, SystemAddress address) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + bool objectExists; + unsigned systemIndex = ren->systemList.GetIndexFromKey(address, &objectExists); + if (objectExists==false) + { + RemoteSystem rs; + rs.lastReceivedStatus=ID_READY_EVENT_UNSET; + rs.lastSentStatus=ID_READY_EVENT_UNSET; + rs.systemAddress=address; + ren->systemList.InsertAtIndex(rs,systemIndex, _FILE_AND_LINE_); + + SendReadyStateQuery(ren->eventId, address); + return true; + } + return false; +} +void ReadyEvent::OnReadyEventForceAllSet(Packet *packet) +{ + RakNet::BitStream incomingBitStream(packet->data, packet->length, false); + incomingBitStream.IgnoreBits(8); + int eventId; + incomingBitStream.Read(eventId); + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + ReadyEventNode *ren = readyEventNodeList[readyIndex]; + if (ren->eventStatus!=ID_READY_EVENT_FORCE_ALL_SET) + { + ren->eventStatus=ID_READY_EVENT_FORCE_ALL_SET; + PushCompletionPacket(ren->eventId); + } + } +} +void ReadyEvent::OnReadyEventPacketUpdate(Packet *packet) +{ + RakNet::BitStream incomingBitStream(packet->data, packet->length, false); + incomingBitStream.IgnoreBits(8); + int eventId; + incomingBitStream.Read(eventId); + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + ReadyEventNode *ren = readyEventNodeList[readyIndex]; + bool systemExists; + unsigned systemIndex = ren->systemList.GetIndexFromKey(packet->systemAddress, &systemExists); + if (systemExists) + { + // Just return if no change + if (ren->systemList[systemIndex].lastReceivedStatus==packet->data[0]) + return; + + bool wasCompleted = IsEventCompletedByIndex(readyIndex); + ren->systemList[systemIndex].lastReceivedStatus=packet->data[0]; + // If forced all set, doesn't matter what the new packet is + if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) + return; + UpdateReadyStatus(readyIndex); + if (wasCompleted==false && IsEventCompletedByIndex(readyIndex)) + PushCompletionPacket(readyIndex); + } + } +} +void ReadyEvent::OnReadyEventQuery(Packet *packet) +{ + RakNet::BitStream incomingBitStream(packet->data, packet->length, false); + incomingBitStream.IgnoreBits(8); + int eventId; + incomingBitStream.Read(eventId); + bool objectExists; + unsigned readyIndex = readyEventNodeList.GetIndexFromKey(eventId, &objectExists); + if (objectExists) + { + unsigned systemIndex = readyEventNodeList[readyIndex]->systemList.GetIndexFromKey(packet->systemAddress,&objectExists); + // Force the non-default send, because our initial send may have arrived at a system that didn't yet create the ready event + if (objectExists) + SendReadyUpdate(readyIndex, systemIndex, true); + } +} +void ReadyEvent::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) systemAddress; + (void) rakNetGUID; + (void) lostConnectionReason; + + RemoveFromAllLists(systemAddress); +} +void ReadyEvent::OnRakPeerShutdown(void) +{ + Clear(); +} + +bool ReadyEvent::SetEventByIndex(int eventIndex, bool isReady) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + if ((ren->eventStatus==ID_READY_EVENT_ALL_SET || ren->eventStatus==ID_READY_EVENT_SET) && isReady==true) + return true; // Success - no change + if (ren->eventStatus==ID_READY_EVENT_UNSET && isReady==false) + return true; // Success - no change + if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) + return true; // Can't change + + if (isReady) + ren->eventStatus=ID_READY_EVENT_SET; + else + ren->eventStatus=ID_READY_EVENT_UNSET; + + UpdateReadyStatus(eventIndex); + + // Check if now completed, and if so, tell the user about it + if (IsEventCompletedByIndex(eventIndex)) + { + PushCompletionPacket(ren->eventId); + } + + return true; +} + +bool ReadyEvent::IsEventCompletedByIndex(unsigned eventIndex) const +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + unsigned i; + if (ren->eventStatus==ID_READY_EVENT_FORCE_ALL_SET) + return true; + if (ren->eventStatus!=ID_READY_EVENT_ALL_SET) + return false; + for (i=0; i < ren->systemList.Size(); i++) + if (ren->systemList[i].lastReceivedStatus!=ID_READY_EVENT_ALL_SET) + return false; + return true; +} + +void ReadyEvent::Clear(void) +{ + unsigned i; + for (i=0; i < readyEventNodeList.Size(); i++) + { + RakNet::OP_DELETE(readyEventNodeList[i], _FILE_AND_LINE_); + } + readyEventNodeList.Clear(false, _FILE_AND_LINE_); +} + +unsigned ReadyEvent::CreateNewEvent(int eventId, bool isReady) +{ + ReadyEventNode *ren = RakNet::OP_NEW( _FILE_AND_LINE_ ); + ren->eventId=eventId; + if (isReady==false) + ren->eventStatus=ID_READY_EVENT_UNSET; + else + ren->eventStatus=ID_READY_EVENT_SET; + return readyEventNodeList.Insert(eventId, ren, true, _FILE_AND_LINE_); +} +void ReadyEvent::UpdateReadyStatus(unsigned eventIndex) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + bool anyUnset; + unsigned i; + if (ren->eventStatus==ID_READY_EVENT_SET) + { + // If you are set, and no other systems are ID_READY_EVENT_UNSET, then change your status to ID_READY_EVENT_ALL_SET + anyUnset=false; + for (i=0; i < ren->systemList.Size(); i++) + { + if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_UNSET) + { + anyUnset=true; + break; + } + } + if (anyUnset==false) + { + ren->eventStatus=ID_READY_EVENT_ALL_SET; + } + } + else if (ren->eventStatus==ID_READY_EVENT_ALL_SET) + { + // If you are all set, and any systems are ID_READY_EVENT_UNSET, then change your status to ID_READY_EVENT_SET + anyUnset=false; + for (i=0; i < ren->systemList.Size(); i++) + { + if (ren->systemList[i].lastReceivedStatus==ID_READY_EVENT_UNSET) + { + anyUnset=true; + break; + } + } + if (anyUnset==true) + { + ren->eventStatus=ID_READY_EVENT_SET; + } + } + BroadcastReadyUpdate(eventIndex, false); +} +void ReadyEvent::SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + RakNet::BitStream bs; + // I do this rather than write true or false, so users that do not use BitStreams can still read the data + if ((ren->eventStatus!=ren->systemList[systemIndex].lastSentStatus) || + (forceIfNotDefault && ren->eventStatus!=ID_READY_EVENT_UNSET)) + { + bs.Write(ren->eventStatus); + bs.Write(ren->eventId); + SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, ren->systemList[systemIndex].systemAddress, false); + + ren->systemList[systemIndex].lastSentStatus=ren->eventStatus; + } + +} +void ReadyEvent::BroadcastReadyUpdate(unsigned eventIndex, bool forceIfNotDefault) +{ + ReadyEventNode *ren = readyEventNodeList[eventIndex]; + unsigned systemIndex; + for (systemIndex=0; systemIndex < ren->systemList.Size(); systemIndex++) + { + SendReadyUpdate(eventIndex, systemIndex, forceIfNotDefault); + } +} +void ReadyEvent::SendReadyStateQuery(unsigned eventId, SystemAddress address) +{ + RakNet::BitStream bs; + bs.Write((MessageID)ID_READY_EVENT_QUERY); + bs.Write(eventId); + SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, address, false); +} +void ReadyEvent::RemoveFromAllLists(SystemAddress address) +{ + unsigned eventIndex; + for (eventIndex=0; eventIndex < readyEventNodeList.Size(); eventIndex++) + { + bool isCompleted = IsEventCompletedByIndex(eventIndex); + bool systemExists; + unsigned systemIndex; + + systemIndex = readyEventNodeList[eventIndex]->systemList.GetIndexFromKey(address, &systemExists); + if (systemExists) + readyEventNodeList[eventIndex]->systemList.RemoveAtIndex(systemIndex); + + UpdateReadyStatus(eventIndex); + + if (isCompleted==false && IsEventCompletedByIndex(eventIndex)) + PushCompletionPacket(readyEventNodeList[eventIndex]->eventId); + } +} +void ReadyEvent::PushCompletionPacket(unsigned eventId) +{ + (void) eventId; + // Not necessary + /* + // Pass a packet to the user that we are now completed, as setting ourselves to signaled was the last thing being waited on + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(int)); + RakNet::BitStream bs(p->data, sizeof(MessageID)+sizeof(int), false); + bs.SetWriteOffset(0); + bs.Write((MessageID)ID_READY_EVENT_ALL_SET); + bs.Write(eventId); + rakPeerInterface->PushBackPacket(p, false); + */ +} +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/ReadyEvent.h b/src/raknet/ReadyEvent.h index a6d516d..3b08dfc 100755 --- a/src/raknet/ReadyEvent.h +++ b/src/raknet/ReadyEvent.h @@ -1,234 +1,234 @@ -/// \file -/// \brief Ready event plugin. This enables a set of systems to create a signal event, set this signal as ready or unready, and to trigger the event when all systems are ready -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ReadyEvent==1 - -#ifndef __READY_EVENT_H -#define __READY_EVENT_H - -#include "PluginInterface2.h" -#include "DS_OrderedList.h" - -namespace RakNet { - -class RakPeerInterface; - -/// \defgroup READY_EVENT_GROUP ReadyEvent -/// \brief Peer to peer synchronized ready and unready events -/// \details -/// \ingroup PLUGINS_GROUP - -/// \ingroup READY_EVENT_GROUP -/// Returns the status of a remote system when querying with ReadyEvent::GetReadyStatus -enum ReadyEventSystemStatus -{ - /// ----------- Normal states --------------- - /// The remote system is not in the wait list, and we have never gotten a ready or complete message from it. - /// This is the default state for valid events - RES_NOT_WAITING, - /// We are waiting for this remote system to call SetEvent(thisEvent,true). - RES_WAITING, - /// The remote system called SetEvent(thisEvent,true), but it still waiting for other systems before completing the ReadyEvent. - RES_READY, - /// The remote system called SetEvent(thisEvent,true), and is no longer waiting for any other systems. - /// This remote system has completed the ReadyEvent - RES_ALL_READY, - - /// Error code, we couldn't look up the system because the event was unknown - RES_UNKNOWN_EVENT, -}; - -/// \brief Peer to peer synchronized ready and unready events -/// \details For peer to peer networks in a fully connected mesh.
      -/// Solves the problem of how to tell if all peers, relative to all other peers, are in a certain ready state.
      -/// For example, if A is connected to B and C, A may see that B and C are ready, but does not know if B is ready to C, or vice-versa.
      -/// This plugin uses two stages to solve that problem, first, everyone I know about is ready. Second, everyone I know about is ready to everyone they know about.
      -/// The user will get ID_READY_EVENT_SET and ID_READY_EVENT_UNSET as the signal flag is set or unset
      -/// The user will get ID_READY_EVENT_ALL_SET when all systems are done waiting for all other systems, in which case the event is considered complete, and no longer tracked.
      -/// \sa FullyConnectedMesh2 -/// \ingroup READY_EVENT_GROUP -class ReadyEvent : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(ReadyEvent) - - // Constructor - ReadyEvent(); - - // Destructor - virtual ~ReadyEvent(); - - // -------------------------------------------------------------------------------------------- - // User functions - // -------------------------------------------------------------------------------------------- - /// Sets or updates the initial ready state for our local system. - /// If eventId is an unknown event the event is created. - /// If eventId was previously used and you want to reuse it, call DeleteEvent first, or else you will keep the same event signals from before - /// Systems previously or later added through AddToWaitList() with the same \a eventId when isReady=true will get ID_READY_EVENT_SET - /// Systems previously added through AddToWaitList with the same \a eventId will get ID_READY_EVENT_UNSET - /// For both ID_READY_EVENT_SET and ID_READY_EVENT_UNSET, eventId is encoded in bytes 1 through 1+sizeof(int) - /// \param[in] eventId A user-defined identifier to wait on. This can be a sequence counter, an event identifier, or anything else you want. - /// \param[in] isReady True to signal we are ready to proceed with this event, false to unsignal - /// \return True on success. False (failure) on unknown eventId - bool SetEvent(int eventId, bool isReady); - - /// When systems can call SetEvent() with isReady==false, it is possible for one system to return true from IsEventCompleted() while the other systems return false - /// This can occur if a system SetEvent() with isReady==false while the completion message is still being transmitted. - /// If your game has the situation where some action should be taken on all systems when IsEventCompleted() is true for any system, then call ForceCompletion() when the action begins. - /// This will force all systems to return true from IsEventCompleted(). - /// \param[in] eventId A user-defined identifier to immediately set as completed - bool ForceCompletion(int eventId); - - /// Deletes an event. We will no longer wait for this event, and any systems that we know have set the event will be forgotten. - /// Call this to clear memory when events are completed and you know you will never need them again. - /// \param[in] eventId A user-defined identifier - /// \return True on success. False (failure) on unknown eventId - bool DeleteEvent(int eventId); - - /// Returns what was passed to SetEvent() - /// \return The value of isReady passed to SetEvent(). Also returns false on unknown event. - bool IsEventSet(int eventId); - - /// Returns if the event is about to be ready and we are negotiating the final packets. - /// This will usually only be true for a very short time, after which IsEventCompleted should return true. - /// While this is true you cannot add to the wait list, or SetEvent() isReady to false anymore. - /// \param[in] eventId A user-defined identifier - /// \return True if any other system has completed processing. Will always be true if IsEventCompleted() is true - bool IsEventCompletionProcessing(int eventId) const; - - /// Returns if the wait list is a subset of the completion list. - /// Call this after all systems you want to wait for have been added with AddToWaitList - /// If you are waiting for a specific number of systems (such as players later connecting), also check GetRemoteWaitListSize(eventId) to be equal to 1 less than the total number of participants. - /// \param[in] eventId A user-defined identifier - /// \return True on completion. False (failure) on unknown eventId, or the set is not completed. - bool IsEventCompleted(int eventId) const; - - /// Returns if this is a known event. - /// Events may be known even if we never ourselves referenced them with SetEvent, because other systems created them via ID_READY_EVENT_SET. - /// \param[in] eventId A user-defined identifier - /// \return true if we have this event, false otherwise - bool HasEvent(int eventId); - - /// Returns the total number of events stored in the system. - /// \return The total number of events stored in the system. - unsigned GetEventListSize(void) const; - - /// Returns the event ID stored at a particular index. EventIDs are stored sorted from least to greatest. - /// \param[in] index Index into the array, from 0 to GetEventListSize() - /// \return The event ID stored at a particular index - int GetEventAtIndex(unsigned index) const; - - /// Adds a system to wait for to signal an event before considering the event complete and returning ID_READY_EVENT_ALL_SET. - /// As we add systems, if this event was previously set to true with SetEvent, these systems will get ID_READY_EVENT_SET. - /// As these systems disconnect (directly or indirectly through the router) they are removed. - /// \note If the event completion process has already started, you cannot add more systems, as this would cause the completion process to fail - /// \param[in] eventId A user-defined number previously passed to SetEvent that has not yet completed - /// \param[in] addressArray An address to wait for event replies from. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. Until all systems in this list have called SetEvent with this ID and true, and have this system in the list, we won't get ID_READY_EVENT_COMPLETE - /// \return True on success, false on unknown eventId (this should be considered an error), or if the completion process has already started. - bool AddToWaitList(int eventId, SystemAddress address); - - /// Removes systems from the wait list, which should have been previously added with AddToWaitList - /// \note Systems that directly or indirectly disconnect from us are automatically removed from the wait list - /// \param[in] address The system to remove from the wait list. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. - /// \return True on success, false on unknown eventId (this should be considered an error) - bool RemoveFromWaitList(int eventId, SystemAddress address); - - /// Returns if a particular system is waiting on a particular event. - /// \param[in] eventId A user-defined identifier - /// \param[in] The address of the system we are checking up on - /// \return True if this system is waiting on this event, false otherwise. - bool IsInWaitList(int eventId, SystemAddress address); - - /// Returns the total number of systems we are waiting on for this event. - /// Does not include yourself - /// \param[in] eventId A user-defined identifier - /// \return The total number of systems we are waiting on for this event. - unsigned GetRemoteWaitListSize(int eventId) const; - - /// Returns the system address of a system at a particular index, for this event. - /// \param[in] eventId A user-defined identifier - /// \param[in] index Index into the array, from 0 to GetWaitListSize() - /// \return The system address of a system at a particular index, for this event. - SystemAddress GetFromWaitListAtIndex(int eventId, unsigned index) const; - - /// For a remote system, find out what their ready status is (waiting, signaled, complete). - /// \param[in] eventId A user-defined identifier - /// \param[in] address Which system we are checking up on - /// \return The status of this system, for this particular event. \sa ReadyEventSystemStatus - ReadyEventSystemStatus GetReadyStatus(int eventId, SystemAddress address); - - /// This channel will be used for all RakPeer::Send calls - /// \param[in] newChannel The channel to use for internal RakPeer::Send calls from this system. Defaults to 0. - void SetSendChannel(unsigned char newChannel); - - // ---------------------------- ALL INTERNAL AFTER HERE ---------------------------- - /// \internal - /// Status of a remote system - struct RemoteSystem - { - MessageID lastSentStatus, lastReceivedStatus; - SystemAddress systemAddress; - }; - static int RemoteSystemCompBySystemAddress( const SystemAddress &key, const RemoteSystem &data ); - /// \internal - /// An event, with a set of systems we are waiting for, a set of systems that are signaled, and a set of systems with completed events - struct ReadyEventNode - { - int eventId; // Sorted on this - MessageID eventStatus; - DataStructures::OrderedList systemList; - }; - static int ReadyEventNodeComp( const int &key, ReadyEvent::ReadyEventNode * const &data ); - - -protected: - // -------------------------------------------------------------------------------------------- - // Packet handling functions - // -------------------------------------------------------------------------------------------- - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnRakPeerShutdown(void); - - void Clear(void); - /* - bool AnyWaitersCompleted(unsigned eventIndex) const; - bool AllWaitersCompleted(unsigned eventIndex) const; - bool AllWaitersReady(unsigned eventIndex) const; - void SendAllReady(unsigned eventId, SystemAddress address); - void BroadcastAllReady(unsigned eventIndex); - void SendReadyStateQuery(unsigned eventId, SystemAddress address); - void BroadcastReadyUpdate(unsigned eventIndex); - bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); - bool IsLocked(unsigned eventIndex) const; - bool IsAllReadyByIndex(unsigned eventIndex) const; - */ - - void SendReadyStateQuery(unsigned eventId, SystemAddress address); - void SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault); - void BroadcastReadyUpdate(unsigned eventIndex, bool forceIfNotDefault); - void RemoveFromAllLists(SystemAddress address); - void OnReadyEventQuery(Packet *packet); - void PushCompletionPacket(unsigned eventId); - bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); - void OnReadyEventForceAllSet(Packet *packet); - void OnReadyEventPacketUpdate(Packet *packet); - void UpdateReadyStatus(unsigned eventIndex); - bool IsEventCompletedByIndex(unsigned eventIndex) const; - unsigned CreateNewEvent(int eventId, bool isReady); - bool SetEventByIndex(int eventIndex, bool isReady); - - DataStructures::OrderedList readyEventNodeList; - unsigned char channel; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Ready event plugin. This enables a set of systems to create a signal event, set this signal as ready or unready, and to trigger the event when all systems are ready +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ReadyEvent==1 + +#ifndef __READY_EVENT_H +#define __READY_EVENT_H + +#include "PluginInterface2.h" +#include "DS_OrderedList.h" + +namespace RakNet { + +class RakPeerInterface; + +/// \defgroup READY_EVENT_GROUP ReadyEvent +/// \brief Peer to peer synchronized ready and unready events +/// \details +/// \ingroup PLUGINS_GROUP + +/// \ingroup READY_EVENT_GROUP +/// Returns the status of a remote system when querying with ReadyEvent::GetReadyStatus +enum ReadyEventSystemStatus +{ + /// ----------- Normal states --------------- + /// The remote system is not in the wait list, and we have never gotten a ready or complete message from it. + /// This is the default state for valid events + RES_NOT_WAITING, + /// We are waiting for this remote system to call SetEvent(thisEvent,true). + RES_WAITING, + /// The remote system called SetEvent(thisEvent,true), but it still waiting for other systems before completing the ReadyEvent. + RES_READY, + /// The remote system called SetEvent(thisEvent,true), and is no longer waiting for any other systems. + /// This remote system has completed the ReadyEvent + RES_ALL_READY, + + /// Error code, we couldn't look up the system because the event was unknown + RES_UNKNOWN_EVENT, +}; + +/// \brief Peer to peer synchronized ready and unready events +/// \details For peer to peer networks in a fully connected mesh.
      +/// Solves the problem of how to tell if all peers, relative to all other peers, are in a certain ready state.
      +/// For example, if A is connected to B and C, A may see that B and C are ready, but does not know if B is ready to C, or vice-versa.
      +/// This plugin uses two stages to solve that problem, first, everyone I know about is ready. Second, everyone I know about is ready to everyone they know about.
      +/// The user will get ID_READY_EVENT_SET and ID_READY_EVENT_UNSET as the signal flag is set or unset
      +/// The user will get ID_READY_EVENT_ALL_SET when all systems are done waiting for all other systems, in which case the event is considered complete, and no longer tracked.
      +/// \sa FullyConnectedMesh2 +/// \ingroup READY_EVENT_GROUP +class ReadyEvent : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(ReadyEvent) + + // Constructor + ReadyEvent(); + + // Destructor + virtual ~ReadyEvent(); + + // -------------------------------------------------------------------------------------------- + // User functions + // -------------------------------------------------------------------------------------------- + /// Sets or updates the initial ready state for our local system. + /// If eventId is an unknown event the event is created. + /// If eventId was previously used and you want to reuse it, call DeleteEvent first, or else you will keep the same event signals from before + /// Systems previously or later added through AddToWaitList() with the same \a eventId when isReady=true will get ID_READY_EVENT_SET + /// Systems previously added through AddToWaitList with the same \a eventId will get ID_READY_EVENT_UNSET + /// For both ID_READY_EVENT_SET and ID_READY_EVENT_UNSET, eventId is encoded in bytes 1 through 1+sizeof(int) + /// \param[in] eventId A user-defined identifier to wait on. This can be a sequence counter, an event identifier, or anything else you want. + /// \param[in] isReady True to signal we are ready to proceed with this event, false to unsignal + /// \return True on success. False (failure) on unknown eventId + bool SetEvent(int eventId, bool isReady); + + /// When systems can call SetEvent() with isReady==false, it is possible for one system to return true from IsEventCompleted() while the other systems return false + /// This can occur if a system SetEvent() with isReady==false while the completion message is still being transmitted. + /// If your game has the situation where some action should be taken on all systems when IsEventCompleted() is true for any system, then call ForceCompletion() when the action begins. + /// This will force all systems to return true from IsEventCompleted(). + /// \param[in] eventId A user-defined identifier to immediately set as completed + bool ForceCompletion(int eventId); + + /// Deletes an event. We will no longer wait for this event, and any systems that we know have set the event will be forgotten. + /// Call this to clear memory when events are completed and you know you will never need them again. + /// \param[in] eventId A user-defined identifier + /// \return True on success. False (failure) on unknown eventId + bool DeleteEvent(int eventId); + + /// Returns what was passed to SetEvent() + /// \return The value of isReady passed to SetEvent(). Also returns false on unknown event. + bool IsEventSet(int eventId); + + /// Returns if the event is about to be ready and we are negotiating the final packets. + /// This will usually only be true for a very short time, after which IsEventCompleted should return true. + /// While this is true you cannot add to the wait list, or SetEvent() isReady to false anymore. + /// \param[in] eventId A user-defined identifier + /// \return True if any other system has completed processing. Will always be true if IsEventCompleted() is true + bool IsEventCompletionProcessing(int eventId) const; + + /// Returns if the wait list is a subset of the completion list. + /// Call this after all systems you want to wait for have been added with AddToWaitList + /// If you are waiting for a specific number of systems (such as players later connecting), also check GetRemoteWaitListSize(eventId) to be equal to 1 less than the total number of participants. + /// \param[in] eventId A user-defined identifier + /// \return True on completion. False (failure) on unknown eventId, or the set is not completed. + bool IsEventCompleted(int eventId) const; + + /// Returns if this is a known event. + /// Events may be known even if we never ourselves referenced them with SetEvent, because other systems created them via ID_READY_EVENT_SET. + /// \param[in] eventId A user-defined identifier + /// \return true if we have this event, false otherwise + bool HasEvent(int eventId); + + /// Returns the total number of events stored in the system. + /// \return The total number of events stored in the system. + unsigned GetEventListSize(void) const; + + /// Returns the event ID stored at a particular index. EventIDs are stored sorted from least to greatest. + /// \param[in] index Index into the array, from 0 to GetEventListSize() + /// \return The event ID stored at a particular index + int GetEventAtIndex(unsigned index) const; + + /// Adds a system to wait for to signal an event before considering the event complete and returning ID_READY_EVENT_ALL_SET. + /// As we add systems, if this event was previously set to true with SetEvent, these systems will get ID_READY_EVENT_SET. + /// As these systems disconnect (directly or indirectly through the router) they are removed. + /// \note If the event completion process has already started, you cannot add more systems, as this would cause the completion process to fail + /// \param[in] eventId A user-defined number previously passed to SetEvent that has not yet completed + /// \param[in] addressArray An address to wait for event replies from. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. Until all systems in this list have called SetEvent with this ID and true, and have this system in the list, we won't get ID_READY_EVENT_COMPLETE + /// \return True on success, false on unknown eventId (this should be considered an error), or if the completion process has already started. + bool AddToWaitList(int eventId, SystemAddress address); + + /// Removes systems from the wait list, which should have been previously added with AddToWaitList + /// \note Systems that directly or indirectly disconnect from us are automatically removed from the wait list + /// \param[in] address The system to remove from the wait list. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. + /// \return True on success, false on unknown eventId (this should be considered an error) + bool RemoveFromWaitList(int eventId, SystemAddress address); + + /// Returns if a particular system is waiting on a particular event. + /// \param[in] eventId A user-defined identifier + /// \param[in] The address of the system we are checking up on + /// \return True if this system is waiting on this event, false otherwise. + bool IsInWaitList(int eventId, SystemAddress address); + + /// Returns the total number of systems we are waiting on for this event. + /// Does not include yourself + /// \param[in] eventId A user-defined identifier + /// \return The total number of systems we are waiting on for this event. + unsigned GetRemoteWaitListSize(int eventId) const; + + /// Returns the system address of a system at a particular index, for this event. + /// \param[in] eventId A user-defined identifier + /// \param[in] index Index into the array, from 0 to GetWaitListSize() + /// \return The system address of a system at a particular index, for this event. + SystemAddress GetFromWaitListAtIndex(int eventId, unsigned index) const; + + /// For a remote system, find out what their ready status is (waiting, signaled, complete). + /// \param[in] eventId A user-defined identifier + /// \param[in] address Which system we are checking up on + /// \return The status of this system, for this particular event. \sa ReadyEventSystemStatus + ReadyEventSystemStatus GetReadyStatus(int eventId, SystemAddress address); + + /// This channel will be used for all RakPeer::Send calls + /// \param[in] newChannel The channel to use for internal RakPeer::Send calls from this system. Defaults to 0. + void SetSendChannel(unsigned char newChannel); + + // ---------------------------- ALL INTERNAL AFTER HERE ---------------------------- + /// \internal + /// Status of a remote system + struct RemoteSystem + { + MessageID lastSentStatus, lastReceivedStatus; + SystemAddress systemAddress; + }; + static int RemoteSystemCompBySystemAddress( const SystemAddress &key, const RemoteSystem &data ); + /// \internal + /// An event, with a set of systems we are waiting for, a set of systems that are signaled, and a set of systems with completed events + struct ReadyEventNode + { + int eventId; // Sorted on this + MessageID eventStatus; + DataStructures::OrderedList systemList; + }; + static int ReadyEventNodeComp( const int &key, ReadyEvent::ReadyEventNode * const &data ); + + +protected: + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnRakPeerShutdown(void); + + void Clear(void); + /* + bool AnyWaitersCompleted(unsigned eventIndex) const; + bool AllWaitersCompleted(unsigned eventIndex) const; + bool AllWaitersReady(unsigned eventIndex) const; + void SendAllReady(unsigned eventId, SystemAddress address); + void BroadcastAllReady(unsigned eventIndex); + void SendReadyStateQuery(unsigned eventId, SystemAddress address); + void BroadcastReadyUpdate(unsigned eventIndex); + bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); + bool IsLocked(unsigned eventIndex) const; + bool IsAllReadyByIndex(unsigned eventIndex) const; + */ + + void SendReadyStateQuery(unsigned eventId, SystemAddress address); + void SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault); + void BroadcastReadyUpdate(unsigned eventIndex, bool forceIfNotDefault); + void RemoveFromAllLists(SystemAddress address); + void OnReadyEventQuery(Packet *packet); + void PushCompletionPacket(unsigned eventId); + bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); + void OnReadyEventForceAllSet(Packet *packet); + void OnReadyEventPacketUpdate(Packet *packet); + void UpdateReadyStatus(unsigned eventIndex); + bool IsEventCompletedByIndex(unsigned eventIndex) const; + unsigned CreateNewEvent(int eventId, bool isReady); + bool SetEventByIndex(int eventIndex, bool isReady); + + DataStructures::OrderedList readyEventNodeList; + unsigned char channel; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/RefCountedObj.h b/src/raknet/RefCountedObj.h index 221ec81..f0064f9 100755 --- a/src/raknet/RefCountedObj.h +++ b/src/raknet/RefCountedObj.h @@ -1,25 +1,25 @@ -/// \file -/// \brief \b Reference counted object. Very simple class for quick and dirty uses. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __REF_COUNTED_OBJ_H -#define __REF_COUNTED_OBJ_H - -#include "RakMemoryOverride.h" - -/// World's simplest class :) -class RefCountedObj -{ - public: - RefCountedObj() {refCount=1;} - virtual ~RefCountedObj() {} - void AddRef(void) {refCount++;} - void Deref(void) {if (--refCount==0) RakNet::OP_DELETE(this, _FILE_AND_LINE_);} - int refCount; -}; - -#endif +/// \file +/// \brief \b Reference counted object. Very simple class for quick and dirty uses. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __REF_COUNTED_OBJ_H +#define __REF_COUNTED_OBJ_H + +#include "RakMemoryOverride.h" + +/// World's simplest class :) +class RefCountedObj +{ + public: + RefCountedObj() {refCount=1;} + virtual ~RefCountedObj() {} + void AddRef(void) {refCount++;} + void Deref(void) {if (--refCount==0) RakNet::OP_DELETE(this, _FILE_AND_LINE_);} + int refCount; +}; + +#endif diff --git a/src/raknet/ReliabilityLayer.cpp b/src/raknet/ReliabilityLayer.cpp index d6f9f72..12b9780 100755 --- a/src/raknet/ReliabilityLayer.cpp +++ b/src/raknet/ReliabilityLayer.cpp @@ -1,3827 +1,3827 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "ReliabilityLayer.h" -#include "GetTime.h" -#include "SocketLayer.h" -#include "PluginInterface2.h" -#include "RakAssert.h" -#include "Rand.h" -#include "MessageIdentifiers.h" -#ifdef USE_THREADED_SEND -#include "SendToThread.h" -#endif -#include - -using namespace RakNet; - -// Can't figure out which library has this function on the PS3 -double Ceil(double d) {if (((double)((int)d))==d) return d; return (int) (d+1.0);} - -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RELIABILITY_LAYER_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - - -//#define _DEBUG_LOGGER - -#if CC_TIME_TYPE_BYTES==4 -static const CCTimeType MAX_TIME_BETWEEN_PACKETS= 350; // 350 milliseconds -static const CCTimeType HISTOGRAM_RESTART_CYCLE=10000; // Every 10 seconds reset the histogram -#else -static const CCTimeType MAX_TIME_BETWEEN_PACKETS= 350000; // 350 milliseconds -//static const CCTimeType HISTOGRAM_RESTART_CYCLE=10000000; // Every 10 seconds reset the histogram -#endif -static const int DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE=512; -static const CCTimeType STARTING_TIME_BETWEEN_PACKETS=MAX_TIME_BETWEEN_PACKETS; -//static const long double TIME_BETWEEN_PACKETS_INCREASE_MULTIPLIER_DEFAULT=.02; -//static const long double TIME_BETWEEN_PACKETS_DECREASE_MULTIPLIER_DEFAULT=1.0 / 9.0; - -typedef uint32_t BitstreamLengthEncoding; - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -//#define PRINT_TO_FILE_ROT -#ifdef PRINT_TO_FILE_ROT -static FILE *fp=0; -#endif - -BPSTracker::TimeAndValue2::TimeAndValue2() {} -BPSTracker::TimeAndValue2::~TimeAndValue2() {} -BPSTracker::TimeAndValue2::TimeAndValue2(RakNet::TimeUS t, uint64_t v1) : value1(v1), time(t) {} -//BPSTracker::TimeAndValue2::TimeAndValue2(RakNet::TimeUS t, uint64_t v1, uint64_t v2) : time(t), value1(v1), value2(v2) {} -BPSTracker::BPSTracker() {Reset(_FILE_AND_LINE_);} -BPSTracker::~BPSTracker() {} -//void BPSTracker::Reset(const char *file, unsigned int line) {total1=total2=lastSec1=lastSec2=0; dataQueue.Clear(file,line);} -void BPSTracker::Reset(const char *file, unsigned int line) {total1=lastSec1=0; dataQueue.Clear(file,line);} -//void BPSTracker::Push2(RakNetTimeUS time, uint64_t value1, uint64_t value2) {dataQueue.Push(TimeAndValue2(time,value1,value2),_FILE_AND_LINE_); total1+=value1; lastSec1+=value1; total2+=value2; lastSec2+=value2;} -//uint64_t BPSTracker::GetBPS2(RakNetTimeUS time) {ClearExpired2(time); return lastSec2;} -//void BPSTracker::GetBPS1And2(RakNetTimeUS time, uint64_t &out1, uint64_t &out2) {ClearExpired2(time); out1=lastSec1; out2=lastSec2;} -uint64_t BPSTracker::GetTotal1(void) const {return total1;} -//uint64_t BPSTracker::GetTotal2(void) const {return total2;} - -// void BPSTracker::ClearExpired2(RakNet::TimeUS time) { -// RakNet::TimeUS threshold=time; -// if (threshold < 1000000) -// return; -// threshold-=1000000; -// while (dataQueue.IsEmpty()==false && dataQueue.Peek().time < threshold) -// { -// lastSec1-=dataQueue.Peek().value1; -// lastSec2-=dataQueue.Peek().value2; -// dataQueue.Pop(); -// } -// } -void BPSTracker::ClearExpired1(RakNet::TimeUS time) -{ - while (dataQueue.IsEmpty()==false && -#if CC_TIME_TYPE_BYTES==8 - dataQueue.Peek().time+1000000 < time -#else - dataQueue.Peek().time+1000 < time -#endif - ) - { - lastSec1-=dataQueue.Peek().value1; - dataQueue.Pop(); - } -} - -struct DatagramHeaderFormat -{ -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - CCTimeType sourceSystemTime; -#endif - DatagramSequenceNumberType datagramNumber; - - // Use floats to save bandwidth - // float B; // Link capacity - float AS; // Data arrival rate - bool isACK; - bool isNAK; - bool isPacketPair; - bool hasBAndAS; - bool isContinuousSend; - bool needsBAndAs; - bool isValid; // To differentiate between what I serialized, and offline data - - static BitSize_t GetDataHeaderBitLength() - { - return BYTES_TO_BITS(GetDataHeaderByteLength()); - } - - static unsigned int GetDataHeaderByteLength() - { - //return 2 + 3 + sizeof(RakNet::TimeMS) + sizeof(float)*2; - return 2 + 3 + -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - sizeof(RakNetTimeMS) + -#endif - sizeof(float)*1; - } - - void Serialize(RakNet::BitStream *b) - { - // Not endian safe - // RakAssert(GetDataHeaderByteLength()==sizeof(DatagramHeaderFormat)); - // b->WriteAlignedBytes((const unsigned char*) this, sizeof(DatagramHeaderFormat)); - // return; - - b->Write(true); // IsValid - if (isACK) - { - b->Write(true); - b->Write(hasBAndAS); - b->AlignWriteToByteBoundary(); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMSLow=(RakNet::TimeMS) sourceSystemTime&0xFFFFFFFF; b->Write(timeMSLow); -#endif - if (hasBAndAS) - { - // b->Write(B); - b->Write(AS); - } - } - else if (isNAK) - { - b->Write(false); - b->Write(true); - } - else - { - b->Write(false); - b->Write(false); - b->Write(isPacketPair); - b->Write(isContinuousSend); - b->Write(needsBAndAs); - b->AlignWriteToByteBoundary(); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMSLow=(RakNet::TimeMS) sourceSystemTime&0xFFFFFFFF; b->Write(timeMSLow); -#endif - b->Write(datagramNumber); - } - } - void Deserialize(RakNet::BitStream *b) - { - // Not endian safe - // b->ReadAlignedBytes((unsigned char*) this, sizeof(DatagramHeaderFormat)); - // return; - - b->Read(isValid); - b->Read(isACK); - if (isACK) - { - isNAK=false; - isPacketPair=false; - b->Read(hasBAndAS); - b->AlignReadToByteBoundary(); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMS; b->Read(timeMS); sourceSystemTime=(CCTimeType) timeMS; -#endif - if (hasBAndAS) - { - // b->Read(B); - b->Read(AS); - } - } - else - { - b->Read(isNAK); - if (isNAK) - { - isPacketPair=false; - } - else - { - b->Read(isPacketPair); - b->Read(isContinuousSend); - b->Read(needsBAndAs); - b->AlignReadToByteBoundary(); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMS; b->Read(timeMS); sourceSystemTime=(CCTimeType) timeMS; -#endif - b->Read(datagramNumber); - } - } - } -}; - -#if !defined(__GNUC__) && !defined(__ARMCC) -#pragma warning(disable:4702) // unreachable code -#endif - -#ifdef _WIN32 -//#define _DEBUG_LOGGER -#ifdef _DEBUG_LOGGER -#include "WindowsIncludes.h" -#endif -#endif - -//#define DEBUG_SPLIT_PACKET_PROBLEMS -#if defined (DEBUG_SPLIT_PACKET_PROBLEMS) -static int waitFlag=-1; -#endif - -using namespace RakNet; - -int RakNet::SplitPacketChannelComp( SplitPacketIdType const &key, SplitPacketChannel* const &data ) -{ -#if PREALLOCATE_LARGE_MESSAGES==1 - if (key < data->returnedPacket->splitPacketId) - return -1; - if (key == data->returnedPacket->splitPacketId) - return 0; -#else - if (key < data->splitPacketList[0]->splitPacketId) - return -1; - if (key == data->splitPacketList[0]->splitPacketId) - return 0; -#endif - return 1; -} - -// DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS( InternalPacket, SplitPacketIndexType, splitPacketIndex ) -/* -bool operator<( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get() < cls->splitPacketIndex; -} -bool operator>( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get() > cls->splitPacketIndex; -} -bool operator==( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get() == cls->splitPacketIndex; -} -/// Semi-hack: This is necessary to call Sort() -bool operator<( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get()->splitPacketIndex < cls->splitPacketIndex; -} -bool operator>( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get()->splitPacketIndex > cls->splitPacketIndex; -} -bool operator==( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) -{ - return inputKey.Get()->splitPacketIndex == cls->splitPacketIndex; -} -*/ - -int SplitPacketIndexComp( SplitPacketIndexType const &key, InternalPacket* const &data ) -{ -if (key < data->splitPacketIndex) -return -1; -if (key == data->splitPacketIndex) -return 0; -return 1; -} - -//------------------------------------------------------------------------------------------------------- -// Constructor -//------------------------------------------------------------------------------------------------------- -// Add 21 to the default MTU so if we encrypt it can hold potentially 21 more bytes of extra data + padding. -ReliabilityLayer::ReliabilityLayer() -{ - -#ifdef _DEBUG - // Wait longer to disconnect in debug so I don't get disconnected while tracing - timeoutTime=30000; -#else - timeoutTime=10000; -#endif - -#ifdef _DEBUG - minExtraPing=extraPingVariance=0; - packetloss=(double) minExtraPing; -#endif - - -#ifdef PRINT_TO_FILE_ROT - if (fp==0 && 0) - { - fp = fopen("reliableorderedoutput.txt", "wt"); - } -#endif - - InitializeVariables(); -//int i = sizeof(InternalPacket); - datagramHistoryMessagePool.SetPageSize(sizeof(MessageNumberNode)*128); - internalPacketPool.SetPageSize(sizeof(InternalPacket)*INTERNAL_PACKET_PAGE_SIZE); - refCountedDataPool.SetPageSize(sizeof(InternalPacketRefCountedData)*32); -} - -//------------------------------------------------------------------------------------------------------- -// Destructor -//------------------------------------------------------------------------------------------------------- -ReliabilityLayer::~ReliabilityLayer() -{ - FreeMemory( true ); // Free all memory immediately -} -//------------------------------------------------------------------------------------------------------- -// Resets the layer for reuse -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::Reset( bool resetVariables, int MTUSize, bool _useSecurity ) -{ - - FreeMemory( true ); // true because making a memory reset pending in the update cycle causes resets after reconnects. Instead, just call Reset from a single thread - if (resetVariables) - { - InitializeVariables(); - -#if LIBCAT_SECURITY==1 - useSecurity = _useSecurity; - - if (_useSecurity) - MTUSize -= cat::AuthenticatedEncryption::OVERHEAD_BYTES; -#else - (void) _useSecurity; -#endif // LIBCAT_SECURITY - congestionManager.Init(RakNet::GetTimeUS(), MTUSize - UDP_HEADER_SIZE); - } -} - -//------------------------------------------------------------------------------------------------------- -// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SetTimeoutTime( RakNet::TimeMS time ) -{ - timeoutTime=time; -} - -//------------------------------------------------------------------------------------------------------- -// Returns the value passed to SetTimeoutTime. or the default if it was never called -//------------------------------------------------------------------------------------------------------- -RakNet::TimeMS ReliabilityLayer::GetTimeoutTime(void) -{ - return timeoutTime; -} - -//------------------------------------------------------------------------------------------------------- -// Initialize the variables -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::InitializeVariables( void ) -{ - memset( orderedWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType)); - memset( sequencedWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); - memset( orderedReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); - memset( highestSequencedReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); - memset( &statistics, 0, sizeof( statistics ) ); - memset( &heapIndexOffsets, 0, sizeof( heapIndexOffsets ) ); - - statistics.connectionStartTime = RakNet::GetTimeUS(); - splitPacketId = 0; - elapsedTimeSinceLastUpdate=0; - throughputCapCountdown=0; - sendReliableMessageNumberIndex = 0; - internalOrderIndex=0; - timeToNextUnreliableCull=0; - unreliableLinkedListHead=0; - lastUpdateTime= RakNet::GetTimeUS(); - bandwidthExceededStatistic=false; - remoteSystemTime=0; - unreliableTimeout=0; - lastBpsClear=0; - - // Disable packet pairs - countdownToNextPacketPair=15; - - nextAllowedThroughputSample=0; - deadConnection = cheater = false; - timeOfLastContinualSend=0; - - // timeResendQueueNonEmpty = 0; - timeLastDatagramArrived=RakNet::GetTimeMS(); - // packetlossThisSample=false; - // backoffThisSample=0; - // packetlossThisSampleResendCount=0; - // lastPacketlossTime=0; - statistics.messagesInResendBuffer=0; - statistics.bytesInResendBuffer=0; - - receivedPacketsBaseIndex=0; - resetReceivedPackets=true; - receivePacketCount=0; - - // SetPing( 1000 ); - - timeBetweenPackets=STARTING_TIME_BETWEEN_PACKETS; - - ackPingIndex=0; - ackPingSum=(CCTimeType)0; - - nextSendTime=lastUpdateTime; - //nextLowestPingReset=(CCTimeType)0; - // continuousSend=false; - - // histogramStart=(CCTimeType)0; - // histogramBitsSent=0; - unacknowledgedBytes=0; - resendLinkedListHead=0; - totalUserDataBytesAcked=0; - - datagramHistoryPopCount=0; - - InitHeapWeights(); - for (int i=0; i < NUMBER_OF_PRIORITIES; i++) - { - statistics.messageInSendBuffer[i]=0; - statistics.bytesInSendBuffer[i]=0.0; - } - - for (int i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) - { - bpsMetrics[i].Reset(_FILE_AND_LINE_); - } -} - -//------------------------------------------------------------------------------------------------------- -// Frees all allocated memory -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::FreeMemory( bool freeAllImmediately ) -{ - (void) freeAllImmediately; - FreeThreadSafeMemory(); -} - -void ReliabilityLayer::FreeThreadSafeMemory( void ) -{ - unsigned i,j; - InternalPacket *internalPacket; - - ClearPacketsAndDatagrams(); - - for (i=0; i < splitPacketChannelList.Size(); i++) - { - for (j=0; j < splitPacketChannelList[i]->splitPacketList.Size(); j++) - { - FreeInternalPacketData(splitPacketChannelList[i]->splitPacketList[j], _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( splitPacketChannelList[i]->splitPacketList[j] ); - } -#if PREALLOCATE_LARGE_MESSAGES==1 - if (splitPacketChannelList[i]->returnedPacket) - { - FreeInternalPacketData(splitPacketChannelList[i]->returnedPacket, __FILE__, __LINE__ ); - ReleaseToInternalPacketPool( splitPacketChannelList[i]->returnedPacket ); - } -#endif - RakNet::OP_DELETE(splitPacketChannelList[i], __FILE__, __LINE__); - } - splitPacketChannelList.Clear(false, _FILE_AND_LINE_); - - while ( outputQueue.Size() > 0 ) - { - internalPacket = outputQueue.Pop(); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - } - - outputQueue.ClearAndForceAllocation( 32, _FILE_AND_LINE_ ); - - /* - for ( i = 0; i < orderingList.Size(); i++ ) - { - if ( orderingList[ i ] ) - { - DataStructures::LinkedList* theList = orderingList[ i ]; - - if ( theList ) - { - while ( theList->Size() ) - { - internalPacket = orderingList[ i ]->Pop(); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - } - - RakNet::OP_DELETE(theList, _FILE_AND_LINE_); - } - } - } - - orderingList.Clear(false, _FILE_AND_LINE_); - */ - - for (i=0; i < NUMBER_OF_ORDERED_STREAMS; i++) - { - for (j=0; j < orderingHeaps[i].Size(); j++) - { - FreeInternalPacketData(orderingHeaps[i][j], _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( orderingHeaps[i][j] ); - } - orderingHeaps[i].Clear(true, _FILE_AND_LINE_); - } - - //resendList.ForEachData(DeleteInternalPacket); - // resendTree.Clear(_FILE_AND_LINE_); - memset(resendBuffer, 0, sizeof(resendBuffer)); - statistics.messagesInResendBuffer=0; - statistics.bytesInResendBuffer=0; - - if (resendLinkedListHead) - { - InternalPacket *prev; - InternalPacket *iter = resendLinkedListHead; - while (1) - { - if (iter->data) - FreeInternalPacketData(iter, _FILE_AND_LINE_ ); - prev=iter; - iter=iter->resendNext; - if (iter==resendLinkedListHead) - { - ReleaseToInternalPacketPool(prev); - break; - } - ReleaseToInternalPacketPool(prev); - } - resendLinkedListHead=0; - } - unacknowledgedBytes=0; - - // acknowlegements.Clear(_FILE_AND_LINE_); - - for ( j=0 ; j < outgoingPacketBuffer.Size(); j++ ) - { - if ( outgoingPacketBuffer[ j ]->data) - FreeInternalPacketData( outgoingPacketBuffer[ j ], _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( outgoingPacketBuffer[ j ] ); - } - - outgoingPacketBuffer.Clear(true, _FILE_AND_LINE_); - -#ifdef _DEBUG - for (unsigned i = 0; i < delayList.Size(); i++ ) - RakNet::OP_DELETE(delayList[ i ], __FILE__, __LINE__); - delayList.Clear(__FILE__, __LINE__); -#endif - - unreliableWithAckReceiptHistory.Clear(false, _FILE_AND_LINE_); - - packetsToSendThisUpdate.Clear(false, _FILE_AND_LINE_); - packetsToSendThisUpdate.Preallocate(512, _FILE_AND_LINE_); - packetsToDeallocThisUpdate.Clear(false, _FILE_AND_LINE_); - packetsToDeallocThisUpdate.Preallocate(512, _FILE_AND_LINE_); - packetsToSendThisUpdateDatagramBoundaries.Clear(false, _FILE_AND_LINE_); - packetsToSendThisUpdateDatagramBoundaries.Preallocate(128, _FILE_AND_LINE_); - datagramSizesInBytes.Clear(false, _FILE_AND_LINE_); - datagramSizesInBytes.Preallocate(128, _FILE_AND_LINE_); - - internalPacketPool.Clear(_FILE_AND_LINE_); - - refCountedDataPool.Clear(_FILE_AND_LINE_); - - /* - DataStructures::Page *cur = datagramMessageIDTree.GetListHead(); - while (cur) - { - int treeIndex; - for (treeIndex=0; treeIndex < cur->size; treeIndex++) - ReleaseToDatagramMessageIDPool(cur->data[treeIndex]); - cur=cur->resendNext; - } - datagramMessageIDTree.Clear(_FILE_AND_LINE_); - datagramMessageIDPool.Clear(_FILE_AND_LINE_); - */ - - while (datagramHistory.Size()) - { - RemoveFromDatagramHistory(datagramHistoryPopCount); - datagramHistory.Pop(); - datagramHistoryPopCount++; - } - datagramHistoryMessagePool.Clear(_FILE_AND_LINE_); - datagramHistoryPopCount=0; - - acknowlegements.Clear(); - NAKs.Clear(); - - unreliableLinkedListHead=0; -} - -//------------------------------------------------------------------------------------------------------- -// Packets are read directly from the socket layer and skip the reliability -//layer because unconnected players do not use the reliability layer -// This function takes packet data after a player has been confirmed as -//connected. The game should not use that data directly -// because some data is used internally, such as packet acknowledgment and -//split packets -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::HandleSocketReceiveFromConnectedPlayer( - const char *buffer, unsigned int length, SystemAddress &systemAddress, DataStructures::List &messageHandlerList, int MTUSize, - SOCKET s, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType timeRead, - BitStream &updateBitStream) -{ -#ifdef _DEBUG - RakAssert( !( buffer == 0 ) ); -#endif - -#if CC_TIME_TYPE_BYTES==4 - timeRead/=1000; -#endif - - - bpsMetrics[(int) ACTUAL_BYTES_RECEIVED].Push1(timeRead,length); - - (void) MTUSize; - - if ( length <= 2 || buffer == 0 ) // Length of 1 is a connection request resend that we just ignore - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("length <= 2 || buffer == 0", BYTES_TO_BITS(length), systemAddress); - return true; - } - - timeLastDatagramArrived=RakNet::GetTimeMS(); - - // CCTimeType time; -// bool indexFound; -// int count, size; - DatagramSequenceNumberType holeCount; - unsigned i; - -#if LIBCAT_SECURITY==1 - if (useSecurity) - { - unsigned int received = length; - - if (!auth_enc.Decrypt((cat::u8*)buffer, received)) - return false; - - length = received; - } -#endif - - RakNet::BitStream socketData( (unsigned char*) buffer, length, false ); // Convert the incoming data to a bitstream for easy parsing - // time = RakNet::GetTimeUS(); - - // Set to the current time if it is not zero, and we get incoming data - // if (timeResendQueueNonEmpty!=0) - // timeResendQueueNonEmpty=timeRead; - - DatagramHeaderFormat dhf; - dhf.Deserialize(&socketData); - if (dhf.isValid==false) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("dhf.isValid==false", BYTES_TO_BITS(length), systemAddress); - - return true; - } - if (dhf.isACK) - { - DatagramSequenceNumberType datagramNumber; - // datagramNumber=dhf.datagramNumber; - -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - RakNet::TimeMS timeMSLow=(RakNet::TimeMS) timeRead&0xFFFFFFFF; - CCTimeType rtt = timeMSLow-dhf.sourceSystemTime; -#if CC_TIME_TYPE_BYTES==4 - if (rtt > 10000) -#else - if (rtt > 10000000) -#endif - { - // Sanity check. This could happen due to type overflow, especially since I only send the low 4 bytes to reduce bandwidth - rtt=(CCTimeType) congestionManager.GetRTT(); - } - // RakAssert(rtt < 500000); - // printf("%i ", (RakNet::TimeMS)(rtt/1000)); - ackPing=rtt; -#endif - -#ifdef _DEBUG - if (dhf.hasBAndAS==false) - { - // dhf.B=0; - dhf.AS=0; - } -#endif - // congestionManager.OnAck(timeRead, rtt, dhf.hasBAndAS, dhf.B, dhf.AS, totalUserDataBytesAcked ); - - - incomingAcks.Clear(); - if (incomingAcks.Deserialize(&socketData)==false) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingAcks.Deserialize failed", BYTES_TO_BITS(length), systemAddress); - - return false; - } - for (i=0; iincomingAcks.ranges[i].maxIndex) - { - RakAssert(incomingAcks.ranges[i].minIndex<=incomingAcks.ranges[i].maxIndex); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingAcks minIndex > maxIndex", BYTES_TO_BITS(length), systemAddress); - return false; - } - for (datagramNumber=incomingAcks.ranges[i].minIndex; datagramNumber >= incomingAcks.ranges[i].minIndex && datagramNumber <= incomingAcks.ranges[i].maxIndex; datagramNumber++) - { - CCTimeType whenSent; - - if (unreliableWithAckReceiptHistory.Size()>0) - { - unsigned int k=0; - while (k < unreliableWithAckReceiptHistory.Size()) - { - if (unreliableWithAckReceiptHistory[k].datagramNumber == datagramNumber) - { - InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); - AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); - ackReceipt->dataBitLength=BYTES_TO_BITS(5); - ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_ACKED; - memcpy(ackReceipt->data+sizeof(MessageID), &unreliableWithAckReceiptHistory[k].sendReceiptSerial, sizeof(uint32_t)); - outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); - - // Remove, swap with last - unreliableWithAckReceiptHistory.RemoveAtIndex(k); - } - else - k++; - } - } - - MessageNumberNode *messageNumberNode = GetMessageNumberNodeByDatagramIndex(datagramNumber, &whenSent); - if (messageNumberNode) - { - // printf("%p Got ack for %i\n", this, datagramNumber.val); -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - congestionManager.OnAck(timeRead, rtt, dhf.hasBAndAS, 0, dhf.AS, totalUserDataBytesAcked, bandwidthExceededStatistic, datagramNumber ); -#else - CCTimeType ping; - if (timeRead>whenSent) - ping=timeRead-whenSent; - else - ping=0; - congestionManager.OnAck(timeRead, ping, dhf.hasBAndAS, 0, dhf.AS, totalUserDataBytesAcked, bandwidthExceededStatistic, datagramNumber ); -#endif - while (messageNumberNode) - { - // TESTING1 -// printf("Remove %i on ack for datagramNumber=%i.\n", messageNumberNode->messageNumber.val, datagramNumber.val); - - RemovePacketFromResendListAndDeleteOlderReliableSequenced( messageNumberNode->messageNumber, timeRead, messageHandlerList, systemAddress ); - messageNumberNode=messageNumberNode->next; - } - - RemoveFromDatagramHistory(datagramNumber); - } -// else if (isReliable) -// { -// // Previously used slot, rather than empty unreliable slot -// printf("%p Ack %i is duplicate\n", this, datagramNumber.val); -// -// congestionManager.OnDuplicateAck(timeRead, datagramNumber); -// } - } - } - } - else if (dhf.isNAK) - { - DatagramSequenceNumberType messageNumber; - DataStructures::RangeList incomingNAKs; - if (incomingNAKs.Deserialize(&socketData)==false) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingNAKs.Deserialize failed", BYTES_TO_BITS(length), systemAddress); - - return false; - } - for (i=0; iincomingNAKs.ranges[i].maxIndex) - { - RakAssert(incomingNAKs.ranges[i].minIndex<=incomingNAKs.ranges[i].maxIndex); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingNAKs minIndex>maxIndex", BYTES_TO_BITS(length), systemAddress); - - return false; - } - // Sanity check - RakAssert(incomingNAKs.ranges[i].maxIndex.val-incomingNAKs.ranges[i].minIndex.val<1000); - for (messageNumber=incomingNAKs.ranges[i].minIndex; messageNumber >= incomingNAKs.ranges[i].minIndex && messageNumber <= incomingNAKs.ranges[i].maxIndex; messageNumber++) - { - congestionManager.OnNAK(timeRead, messageNumber); - - // REMOVEME - // printf("%p NAK %i\n", this, dhf.datagramNumber.val); - - - CCTimeType timeSent; - MessageNumberNode *messageNumberNode = GetMessageNumberNodeByDatagramIndex(messageNumber, &timeSent); - while (messageNumberNode) - { - // Update timers so resends occur immediately - InternalPacket *internalPacket = resendBuffer[messageNumberNode->messageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK]; - if (internalPacket) - { - if (internalPacket->nextActionTime!=0) - { - internalPacket->nextActionTime=timeRead; - } - } - - messageNumberNode=messageNumberNode->next; - } - } - } - } - else - { - uint32_t skippedMessageCount; - if (!congestionManager.OnGotPacket(dhf.datagramNumber, dhf.isContinuousSend, timeRead, length, &skippedMessageCount)) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("congestionManager.OnGotPacket failed", BYTES_TO_BITS(length), systemAddress); - - return true; - } - if (dhf.isPacketPair) - congestionManager.OnGotPacketPair(dhf.datagramNumber, length, timeRead); - - DatagramHeaderFormat dhfNAK; - dhfNAK.isNAK=true; - uint32_t skippedMessageOffset; - for (skippedMessageOffset=skippedMessageCount; skippedMessageOffset > 0; skippedMessageOffset--) - { - NAKs.Insert(dhf.datagramNumber-skippedMessageOffset); - } - remoteSystemNeedsBAndAS=dhf.needsBAndAs; - - // Ack dhf.datagramNumber - // Ack even unreliable messages for congestion control, just don't resend them on no ack -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - SendAcknowledgementPacket( dhf.datagramNumber, dhf.sourceSystemTime); -#else - SendAcknowledgementPacket( dhf.datagramNumber, 0); -#endif - - InternalPacket* internalPacket = CreateInternalPacketFromBitStream( &socketData, timeRead ); - if (internalPacket==0) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("CreateInternalPacketFromBitStream failed", BYTES_TO_BITS(length), systemAddress); - - return true; - } - - while ( internalPacket ) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - { -#if CC_TIME_TYPE_BYTES==4 - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, receivePacketCount, systemAddress, timeRead, false); -#else - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, receivePacketCount, systemAddress, (RakNet::TimeMS)(timeRead/(CCTimeType)1000), false); -#endif - } - - { - - // resetReceivedPackets is set from a non-threadsafe function. - // We do the actual reset in this function so the data is not modified by multiple threads - if (resetReceivedPackets) - { - hasReceivedPacketQueue.ClearAndForceAllocation(DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE, _FILE_AND_LINE_); - receivedPacketsBaseIndex=0; - resetReceivedPackets=false; - } - - // Check for corrupt orderingChannel - if ( - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED - ) - { - if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) - { - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS", BYTES_TO_BITS(length), systemAddress); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - - // 8/12/09 was previously not checking if the message was reliable. However, on packetloss this would mean you'd eventually exceed the - // hole count because unreliable messages were never resent, and you'd stop getting messages - if (internalPacket->reliability == RELIABLE || internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_ORDERED ) - { - // If the following conditional is true then this either a duplicate packet - // or an older out of order packet - // The subtraction unsigned overflow is intentional - holeCount = (DatagramSequenceNumberType)(internalPacket->reliableMessageNumber-receivedPacketsBaseIndex); - const DatagramSequenceNumberType typeRange = (DatagramSequenceNumberType)(const uint32_t)-1; - - // TESTING1 -// printf("waiting on reliableMessageNumber=%i holeCount=%i datagramNumber=%i\n", receivedPacketsBaseIndex.val, holeCount.val, dhf.datagramNumber.val); - - if (holeCount==(DatagramSequenceNumberType) 0) - { - // Got what we were expecting - if (hasReceivedPacketQueue.Size()) - hasReceivedPacketQueue.Pop(); - ++receivedPacketsBaseIndex; - } - else if (holeCount > typeRange/(DatagramSequenceNumberType) 2) - { - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Duplicate packet - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - else if ((unsigned int) holeCountdataBitLength)); - - // Duplicate packet - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - else // holeCount>=receivedPackets.Size() - { - if (holeCount > (DatagramSequenceNumberType) 1000000) - { - RakAssert("Hole count too high. See ReliabilityLayer.h" && 0); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("holeCount > 1000000", BYTES_TO_BITS(length), systemAddress); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Would crash due to out of memory! - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - - // Fix - sending on a higher priority gives us a very very high received packets base index if we formerly had pre-split a lot of messages and - // used that as the message number. Because of this, a lot of time is spent in this linear loop and the timeout time expires because not - // all of the message is sent in time. - // Fixed by late assigning message IDs on the sender - - // Add 0 times to the queue until (reliableMessageNumber - baseIndex) < queue size. - while ((unsigned int)(holeCount) > hasReceivedPacketQueue.Size()) - hasReceivedPacketQueue.Push(true, _FILE_AND_LINE_ ); // time+(CCTimeType)60 * (CCTimeType)1000 * (CCTimeType)1000); // Didn't get this packet - set the time to give up waiting - hasReceivedPacketQueue.Push(false, _FILE_AND_LINE_ ); // Got the packet -#ifdef _DEBUG - // If this assert hits then DatagramSequenceNumberType has overflowed - RakAssert(hasReceivedPacketQueue.Size() < (unsigned int)((DatagramSequenceNumberType)(const uint32_t)(-1))); -#endif - } - - while ( hasReceivedPacketQueue.Size()>0 && hasReceivedPacketQueue.Peek()==false ) - { - hasReceivedPacketQueue.Pop(); - ++receivedPacketsBaseIndex; - } - } - - // If the allocated buffer is > DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE and it is 3x greater than the number of elements actually being used - if (hasReceivedPacketQueue.AllocationSize() > (unsigned int) DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE && hasReceivedPacketQueue.AllocationSize() > hasReceivedPacketQueue.Size() * 3) - hasReceivedPacketQueue.Compress(_FILE_AND_LINE_); - - - /* - if ( internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == UNRELIABLE_SEQUENCED ) - { -#ifdef _DEBUG - RakAssert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); -#endif - - if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) - { - - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS", BYTES_TO_BITS(length), systemAddress); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - if ( IsOlderOrderedPacket( internalPacket->orderingIndex, waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] ) == false ) - { - // Is this a split packet? - if ( internalPacket->splitPacketCount > 0 ) - { - // Generate the split - // Verify some parameters to make sure we don't get junk data - - - // Check for a rebuilt packet - InsertIntoSplitPacketList( internalPacket, timeRead ); - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Sequenced - internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, - s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions); - - if ( internalPacket ) - { - // Update our index to the newest packet - waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + (OrderingIndexType)1; - - // If there is a rebuilt packet, add it to the output queue - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - internalPacket = 0; - } - - // else don't have all the parts yet - } - else - { - // Update our index to the newest packet - waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + (OrderingIndexType)1; - - // Not a split packet. Add the packet to the output queue - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - internalPacket = 0; - } - } - else - { - // Older sequenced packet. Discard it - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - } - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - // Is this an unsequenced split packet? - if ( internalPacket->splitPacketCount > 0 ) - { - // Check for a rebuilt packet - if ( internalPacket->reliability != RELIABLE_ORDERED ) - internalPacket->orderingChannel = 255; // Use 255 to designate not sequenced and not ordered - - InsertIntoSplitPacketList( internalPacket, timeRead ); - - internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, - s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions); - - if ( internalPacket == 0 ) - { - - // Don't have all the parts yet - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - */ - - /* - if ( internalPacket->reliability == RELIABLE_ORDERED ) - { -#ifdef _DEBUG - RakAssert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); -#endif - - if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) - { - // Invalid packet - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - if ( waitingForOrderedPacketReadIndex[ internalPacket->orderingChannel ] == internalPacket->orderingIndex ) - { - // Get the list to hold ordered packets for this stream - DataStructures::LinkedList *orderingListAtOrderingStream; - unsigned char orderingChannelCopy = internalPacket->orderingChannel; - - // Push the packet for the user to read - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - internalPacket = 0; // Don't reference this any longer since other threads access it - - // Wait for the resendNext ordered packet in sequence - waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ++; // This wraps - - orderingListAtOrderingStream = GetOrderingListAtOrderingStream( orderingChannelCopy ); - - if ( orderingListAtOrderingStream != 0) - { - while ( orderingListAtOrderingStream->Size() > 0 ) - { - // Cycle through the list until nothing is found - orderingListAtOrderingStream->Beginning(); - indexFound=false; - size=orderingListAtOrderingStream->Size(); - count=0; - - while (count++ < size) - { - if ( orderingListAtOrderingStream->Peek()->orderingIndex == waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ) - { - outputQueue.Push( orderingListAtOrderingStream->Pop(), _FILE_AND_LINE_ ); - waitingForOrderedPacketReadIndex[ orderingChannelCopy ]++; - indexFound=true; - } - else - (*orderingListAtOrderingStream)++; - } - - if (indexFound==false) - break; - } - } - internalPacket = 0; - } - else - { - // This is a newer ordered packet than we are waiting for. Store it for future use - AddToOrderingList( internalPacket ); - } - - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - */ - - // Is this a split packet? If so then reassemble - if ( internalPacket->splitPacketCount > 0 ) - { - // Check for a rebuilt packet - if ( internalPacket->reliability != RELIABLE_ORDERED && internalPacket->reliability!=RELIABLE_SEQUENCED && internalPacket->reliability!=UNRELIABLE_SEQUENCED) - internalPacket->orderingChannel = 255; // Use 255 to designate not sequenced and not ordered - - InsertIntoSplitPacketList( internalPacket, timeRead ); - - internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, - s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); - - if ( internalPacket == 0 ) - { - // Don't have all the parts yet - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - - if (internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED) - { -#ifdef PRINT_TO_FILE_ROT - // ___________________ - BitStream bitStream(internalPacket->data, BITS_TO_BYTES(internalPacket->dataBitLength), false); - unsigned int receivedPacketNumber; - RakNet::Time receivedTime; - unsigned char streamNumber; - PacketReliability reliability; - // ___________________ - - - bitStream.IgnoreBits(8); // Ignore ID_TIMESTAMP - bitStream.Read(receivedTime); - bitStream.IgnoreBits(8); // Ignore ID_USER_ENUM+1 - bitStream.Read(receivedPacketNumber); - bitStream.Read(streamNumber); - bitStream.Read(reliability); - char *type="UNDEFINED"; - if (reliability==UNRELIABLE_SEQUENCED) - type="UNRELIABLE_SEQUENCED"; - else if (reliability==RELIABLE_ORDERED) - type="RELIABLE_ORDERED"; - // ___________________ -#endif - - - if (internalPacket->orderingIndex==orderedReadIndex[internalPacket->orderingChannel]) - { - // Has current ordering index - if (internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED) - { - // Is sequenced - if (IsOlderOrderedPacket(internalPacket->sequencingIndex,highestSequencedReadIndex[internalPacket->orderingChannel])==false) - { - // Expected or highest known value - - // Update highest sequence - highestSequencedReadIndex[internalPacket->orderingChannel] = internalPacket->sequencingIndex; - -#ifdef PRINT_TO_FILE_ROT - if (fp) - { - fprintf(fp, "Returning %i, %s by fallthrough. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); - fflush(fp); - } -#endif - - // Fallthrough, returned to user below - } - else - { -#ifdef PRINT_TO_FILE_ROT - if (fp) - { - fprintf(fp, "Discarding %i, %s late sequenced. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); - fflush(fp); - } -#endif - - // Lower than highest known value - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - else - { - // Push to output buffer immediately - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - -#ifdef PRINT_TO_FILE_ROT - if (fp) - { - fprintf(fp, "outputting immediate %i, %s. OI=%i. SI=%i.", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); - if (orderingHeaps[internalPacket->orderingChannel].Size()==0) - fprintf(fp, "heap empty\n"); - else - fprintf(fp, "heap head=%i\n", orderingHeaps[internalPacket->orderingChannel].Peek()->orderingIndex.val); - fflush(fp); - } -#endif - - orderedReadIndex[internalPacket->orderingChannel]++; - highestSequencedReadIndex[internalPacket->orderingChannel] = 0; - - // Return off heap until order lost - while (orderingHeaps[internalPacket->orderingChannel].Size()>0 && - orderingHeaps[internalPacket->orderingChannel].Peek()->orderingIndex==orderedReadIndex[internalPacket->orderingChannel]) - { - internalPacket = orderingHeaps[internalPacket->orderingChannel].Pop(0); - -#ifdef PRINT_TO_FILE_ROT - BitStream bitStream2(internalPacket->data, BITS_TO_BYTES(internalPacket->dataBitLength), false); - bitStream2.IgnoreBits(8); // Ignore ID_TIMESTAMP - bitStream2.Read(receivedTime); - bitStream2.IgnoreBits(8); // Ignore ID_USER_ENUM+1 - bitStream2.Read(receivedPacketNumber); - bitStream2.Read(streamNumber); - bitStream2.Read(reliability); - char *type="UNDEFINED"; - if (reliability==UNRELIABLE_SEQUENCED) - type="UNRELIABLE_SEQUENCED"; - else if (reliability==RELIABLE_ORDERED) - type="RELIABLE_ORDERED"; - - if (fp) - { - fprintf(fp, "Heap pop %i, %s. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); - fflush(fp); - } -#endif - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - - if (internalPacket->reliability == RELIABLE_ORDERED) - { - orderedReadIndex[internalPacket->orderingChannel]++; - } - else - { - highestSequencedReadIndex[internalPacket->orderingChannel] = internalPacket->sequencingIndex; - } - } - - // Done - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - } - else if (IsOlderOrderedPacket(internalPacket->orderingIndex,orderedReadIndex[internalPacket->orderingChannel])==false) - { - // internalPacket->_orderingIndex is greater - // If a message has a greater ordering index, and is sequenced or ordered, buffer it - // Sequenced has a lower heap weight, ordered has max sequenced weight - - // Keep orderedHoleCount count small - if (orderingHeaps[internalPacket->orderingChannel].Size()==0) - heapIndexOffsets[internalPacket->orderingChannel]=orderedReadIndex[internalPacket->orderingChannel]; - - reliabilityHeapWeightType orderedHoleCount = internalPacket->orderingIndex-heapIndexOffsets[internalPacket->orderingChannel]; - reliabilityHeapWeightType weight = orderedHoleCount*1048576; - if (internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED) - weight+=internalPacket->sequencingIndex; - else - weight+=(1048576-1); - orderingHeaps[internalPacket->orderingChannel].Push(weight, internalPacket, _FILE_AND_LINE_); - -#ifdef PRINT_TO_FILE_ROT - if (fp) - { - fprintf(fp, "Heap push %i, %s, weight=%" PRINTF_64_BIT_MODIFIER "u. OI=%i. waiting on %i. SI=%i.\n", receivedPacketNumber, type, weight, internalPacket->orderingIndex.val, orderedReadIndex[internalPacket->orderingChannel].val, internalPacket->sequencingIndex); - fflush(fp); - } -#endif - - // Buffered, nothing to do - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - else - { - // Out of order - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - // Ignored, nothing to do - goto CONTINUE_SOCKET_DATA_PARSE_LOOP; - } - - } - - bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Nothing special about this packet. Add it to the output queue - outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); - - internalPacket = 0; - } - - // Used for a goto to jump to the resendNext packet immediately - -CONTINUE_SOCKET_DATA_PARSE_LOOP: - // Parse the bitstream to create an internal packet - internalPacket = CreateInternalPacketFromBitStream( &socketData, timeRead ); - } - - } - - - receivePacketCount++; - - return true; -} - -//------------------------------------------------------------------------------------------------------- -// This gets an end-user packet already parsed out. Returns number of BITS put into the buffer -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::Receive( unsigned char **data ) -{ - InternalPacket * internalPacket; - - if ( outputQueue.Size() > 0 ) - { - // #ifdef _DEBUG - // RakAssert(bitStream->GetNumberOfBitsUsed()==0); - // #endif - internalPacket = outputQueue.Pop(); - - BitSize_t bitLength; - *data = internalPacket->data; - bitLength = internalPacket->dataBitLength; - ReleaseToInternalPacketPool( internalPacket ); - return bitLength; - } - - else - { - return 0; - } - -} - -//------------------------------------------------------------------------------------------------------- -// Puts data on the send queue -// bitStream contains the data to send -// priority is what priority to send the data at -// reliability is what reliability to use -// ordering channel is from 0 to 255 and specifies what stream to use -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::Send( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, CCTimeType currentTime, uint32_t receipt ) -{ -#ifdef _DEBUG - RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); - RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - RakAssert( numberOfBitsToSend > 0 ); -#endif - -#if CC_TIME_TYPE_BYTES==4 - currentTime/=1000; -#endif - - (void) MTUSize; - - // int a = BITS_TO_BYTES(numberOfBitsToSend); - - // Fix any bad parameters - if ( reliability > RELIABLE_ORDERED_WITH_ACK_RECEIPT || reliability < 0 ) - reliability = RELIABLE; - - if ( priority > NUMBER_OF_PRIORITIES || priority < 0 ) - priority = HIGH_PRIORITY; - - if ( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) - orderingChannel = 0; - - unsigned int numberOfBytesToSend=(unsigned int) BITS_TO_BYTES(numberOfBitsToSend); - if ( numberOfBitsToSend == 0 ) - { - return false; - } - InternalPacket * internalPacket = AllocateFromInternalPacketPool(); - if (internalPacket==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return false; // Out of memory - } - - bpsMetrics[(int) USER_MESSAGE_BYTES_PUSHED].Push1(currentTime,numberOfBytesToSend); - - internalPacket->creationTime = currentTime; - - if ( makeDataCopy ) - { - AllocInternalPacketData(internalPacket, numberOfBytesToSend, true, _FILE_AND_LINE_ ); - //internalPacket->data = (unsigned char*) rakMalloc_Ex( numberOfBytesToSend, _FILE_AND_LINE_ ); - memcpy( internalPacket->data, data, numberOfBytesToSend ); - } - else - { - // Allocated the data elsewhere, delete it in here - //internalPacket->data = ( unsigned char* ) data; - AllocInternalPacketData(internalPacket, (unsigned char*) data ); - } - - internalPacket->dataBitLength = numberOfBitsToSend; - internalPacket->messageInternalOrder = internalOrderIndex++; - internalPacket->priority = priority; - internalPacket->reliability = reliability; - internalPacket->sendReceiptSerial=receipt; - - // Calculate if I need to split the packet - // int headerLength = BITS_TO_BYTES( GetMessageHeaderLengthBits( internalPacket, true ) ); - - unsigned int maxDataSizeBytes = GetMaxDatagramSizeExcludingMessageHeaderBytes() - BITS_TO_BYTES(GetMaxMessageHeaderLengthBits()); - - bool splitPacket = numberOfBytesToSend > maxDataSizeBytes; - - // If a split packet, we might have to upgrade the reliability - if ( splitPacket ) - { - // Split packets cannot be unreliable, in case that one part doesn't arrive and the whole cannot be reassembled. - // One part could not arrive either due to packetloss or due to unreliable discard - if (internalPacket->reliability==UNRELIABLE) - internalPacket->reliability=RELIABLE; - else if (internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) - internalPacket->reliability=RELIABLE_WITH_ACK_RECEIPT; - else if (internalPacket->reliability==UNRELIABLE_SEQUENCED) - internalPacket->reliability=RELIABLE_SEQUENCED; -// else if (internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT) -// internalPacket->reliability=RELIABLE_SEQUENCED_WITH_ACK_RECEIPT; - } - - // ++sendMessageNumberIndex; - - if ( internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == UNRELIABLE_SEQUENCED -// || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || -// internalPacket->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - { - // Assign the sequence stream and index - internalPacket->orderingChannel = orderingChannel; - internalPacket->orderingIndex = orderedWriteIndex[ orderingChannel ]; - internalPacket->sequencingIndex = sequencedWriteIndex[ orderingChannel ]++; - - // This packet supersedes all other sequenced packets on the same ordering channel - // Delete all packets in all send lists that are sequenced and on the same ordering channel - // UPDATE: - // Disabled. We don't have enough info to consistently do this. Sometimes newer data does supercede - // older data such as with constantly declining health, but not in all cases. - // For example, with sequenced unreliable sound packets just because you send a newer one doesn't mean you - // don't need the older ones because the odds are they will still arrive in order - /* - for (int i=0; i < NUMBER_OF_PRIORITIES; i++) - { - DeleteSequencedPacketsInList(orderingChannel, sendQueue[i]); - } - */ - } - else if ( internalPacket->reliability == RELIABLE_ORDERED || internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT ) - { - // Assign the ordering channel and index - internalPacket->orderingChannel = orderingChannel; - internalPacket->orderingIndex = orderedWriteIndex[ orderingChannel ] ++; - sequencedWriteIndex[ orderingChannel ]=0; - } - - if ( splitPacket ) // If it uses a secure header it will be generated here - { - // Must split the packet. This will also generate the SHA1 if it is required. It also adds it to the send list. - //InternalPacket packetCopy; - //memcpy(&packetCopy, internalPacket, sizeof(InternalPacket)); - //sendPacketSet[priority].CancelWriteLock(internalPacket); - //SplitPacket( &packetCopy, MTUSize ); - SplitPacket( internalPacket ); - //RakNet::OP_DELETE_ARRAY(packetCopy.data, _FILE_AND_LINE_); - return true; - } - - RakAssert(internalPacket->dataBitLengthdataBitLengthmessageNumberAssigned==false); - outgoingPacketBuffer.Push( GetNextWeight(internalPacket->priority), internalPacket, _FILE_AND_LINE_ ); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]++; - statistics.bytesInSendBuffer[(int)internalPacket->priority]+=(double) BITS_TO_BYTES(internalPacket->dataBitLength); - - // sendPacketSet[priority].WriteUnlock(); - return true; -} -//------------------------------------------------------------------------------------------------------- -// Run this once per game cycle. Handles internal lists and actually does the send -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::Update( SOCKET s, SystemAddress &systemAddress, int MTUSize, CCTimeType time, - unsigned bitsPerSecondLimit, - DataStructures::List &messageHandlerList, - RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, - BitStream &updateBitStream) - -{ - (void) MTUSize; - - RakNet::TimeMS timeMs; -#if CC_TIME_TYPE_BYTES==4 - time/=1000; - timeMs=time; -#else - timeMs=(RakNet::TimeMS) (time/(CCTimeType)1000); -#endif - -#ifdef _DEBUG - while (delayList.Size()) - { - if (delayList.Peek()->sendTime <= timeMs) - { - DataAndTime *dat = delayList.Pop(); - SocketLayer::SendTo( dat->s, dat->data, dat->length, systemAddress, dat->remotePortRakNetWasStartedOn_PS3, dat->extraSocketOptions, __FILE__, __LINE__ ); - RakNet::OP_DELETE(dat,__FILE__,__LINE__); - } - break; - } -#endif - - // This line is necessary because the timer isn't accurate - if (time <= lastUpdateTime) - { - // Always set the last time in case of overflow - lastUpdateTime=time; - return; - } - - CCTimeType timeSinceLastTick = time - lastUpdateTime; - lastUpdateTime=time; -#if CC_TIME_TYPE_BYTES==4 - if (timeSinceLastTick>100) - timeSinceLastTick=100; -#else - if (timeSinceLastTick>100000) - timeSinceLastTick=100000; -#endif - - if (unreliableTimeout>0) - { - if (timeSinceLastTick>=timeToNextUnreliableCull) - { - if (unreliableLinkedListHead) - { - // Cull out all unreliable messages that have exceeded the timeout - InternalPacket *cur = unreliableLinkedListHead; - InternalPacket *end = unreliableLinkedListHead->unreliablePrev; - while (1) - { - if (time > cur->creationTime+(CCTimeType)unreliableTimeout) - { - // Flag invalid, and clear the memory. Still needs to be removed from the sendPacketSet later - // This fixes a problem where a remote system disconnects, but we don't know it yet, and memory consumption increases to a huge value - FreeInternalPacketData(cur, _FILE_AND_LINE_ ); - cur->data=0; - InternalPacket *next = cur->unreliableNext; - RemoveFromUnreliableLinkedList(cur); - - if (cur==end) - break; - - cur=next; - } - else - { - // if (cur==end) - // break; - // - // cur=cur->unreliableNext; - - // They should be inserted in-order, so no need to iterate past the first failure - break; - } - } - } - - timeToNextUnreliableCull=unreliableTimeout/(CCTimeType)2; - } - else - { - timeToNextUnreliableCull-=timeSinceLastTick; - } - } - - - // Due to thread vagarities and the way I store the time to avoid slow calls to RakNet::GetTime - // time may be less than lastAck -#if CC_TIME_TYPE_BYTES==4 - if ( statistics.messagesInResendBuffer!=0 && AckTimeout(time) ) -#else - if ( statistics.messagesInResendBuffer!=0 && AckTimeout(RakNet::TimeMS(time/(CCTimeType)1000)) ) -#endif - { - // SHOW - dead connection - // We've waited a very long time for a reliable packet to get an ack and it never has - deadConnection = true; - return; - } - - if (congestionManager.ShouldSendACKs(time,timeSinceLastTick)) - { - SendACKs(s, systemAddress, time, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); - } - - if (NAKs.Size()>0) - { - updateBitStream.Reset(); - DatagramHeaderFormat dhfNAK; - dhfNAK.isNAK=true; - dhfNAK.isACK=false; - dhfNAK.isPacketPair=false; - dhfNAK.Serialize(&updateBitStream); - NAKs.Serialize(&updateBitStream, GetMaxDatagramSizeExcludingMessageHeaderBits(), true); - SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); - } - - DatagramHeaderFormat dhf; - dhf.needsBAndAs=congestionManager.GetIsInSlowStart(); - dhf.isContinuousSend=bandwidthExceededStatistic; - // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || - // sendPacketSet[1].IsEmpty()==false || - // sendPacketSet[2].IsEmpty()==false || - // sendPacketSet[3].IsEmpty()==false; - bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; - - const bool hasDataToSendOrResend = IsResendQueueEmpty()==false || bandwidthExceededStatistic; - RakAssert(NUMBER_OF_PRIORITIES==4); - congestionManager.Update(time, hasDataToSendOrResend); - - statistics.BPSLimitByOutgoingBandwidthLimit = BITS_TO_BYTES(bitsPerSecondLimit); - statistics.BPSLimitByCongestionControl = congestionManager.GetBytesPerSecondLimitByCongestionControl(); - - unsigned int i; - if (time > lastBpsClear+ -#if CC_TIME_TYPE_BYTES==4 - 100 -#else - 100000 -#endif - ) - { - for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) - { - bpsMetrics[i].ClearExpired1(time); - } - - lastBpsClear=time; - } - - if (unreliableWithAckReceiptHistory.Size()>0) - { - i=0; - while (i < unreliableWithAckReceiptHistory.Size()) - { - if (unreliableWithAckReceiptHistory[i].nextActionTime < time) - { - InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); - AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); - ackReceipt->dataBitLength=BYTES_TO_BITS(5); - ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_LOSS; - memcpy(ackReceipt->data+sizeof(MessageID), &unreliableWithAckReceiptHistory[i].sendReceiptSerial, sizeof(uint32_t)); - outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); - - // Remove, swap with last - unreliableWithAckReceiptHistory.RemoveAtIndex(i); - } - else - i++; - } - } - - if (hasDataToSendOrResend==true) - { - InternalPacket *internalPacket; - // bool forceSend=false; - bool pushedAnything; - BitSize_t nextPacketBitLength; - dhf.isACK=false; - dhf.isNAK=false; - dhf.hasBAndAS=false; - ResetPacketsAndDatagrams(); - - int transmissionBandwidth = congestionManager.GetTransmissionBandwidth(time, timeSinceLastTick, unacknowledgedBytes,dhf.isContinuousSend); - int retransmissionBandwidth = congestionManager.GetRetransmissionBandwidth(time, timeSinceLastTick, unacknowledgedBytes,dhf.isContinuousSend); - if (retransmissionBandwidth>0 || transmissionBandwidth>0) - { - statistics.isLimitedByCongestionControl=false; - - allDatagramSizesSoFar=0; - - // Keep filling datagrams until we exceed retransmission bandwidth - while ((int)BITS_TO_BYTES(allDatagramSizesSoFar)messageNumberAssigned==true); - - if ( internalPacket->nextActionTime < time ) - { - nextPacketBitLength = internalPacket->headerLength + internalPacket->dataBitLength; - if ( datagramSizeSoFar + nextPacketBitLength > GetMaxDatagramSizeExcludingMessageHeaderBits() ) - { - // Gathers all PushPackets() - PushDatagram(); - break; - } - - PopListHead(false); - - CC_DEBUG_PRINTF_2("Rs %i ", internalPacket->reliableMessageNumber.val); - - bpsMetrics[(int) USER_MESSAGE_BYTES_RESENT].Push1(time,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Testing1 -// if (internalPacket->reliability==RELIABLE_ORDERED || internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) -// printf("RESEND reliableMessageNumber %i with datagram %i\n", internalPacket->reliableMessageNumber.val, congestionManager.GetNextDatagramSequenceNumber().val); - - PushPacket(time,internalPacket,true); // Affects GetNewTransmissionBandwidth() -// internalPacket->timesSent++; - internalPacket->nextActionTime = congestionManager.GetRTOForRetransmission()+time; -#if CC_TIME_TYPE_BYTES==4 - if (internalPacket->nextActionTime-time > 10000) -#else - if (internalPacket->nextActionTime-time > 10000000) -#endif - { - // int a=5; - RakAssert(0); - } - - congestionManager.OnResend(time); - - pushedAnything=true; - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - { -#if CC_TIME_TYPE_BYTES==4 - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS) time, true); -#else - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000), true); -#endif - } - - // Put the packet back into the resend list at the correct spot - // Don't make a copy since I'm reinserting an allocated struct - InsertPacketIntoResendList( internalPacket, time, false, false ); - - // Removeme - // printf("Resend:%i ", internalPacket->reliableMessageNumber); - } - else - { - // Filled one datagram. - // If the 2nd and it's time to send a datagram pair, will be marked as a pair - PushDatagram(); - break; - } - } - - if (pushedAnything==false) - break; - } - } - else - { - statistics.isLimitedByCongestionControl=true; - } - - if ((int)BITS_TO_BYTES(allDatagramSizesSoFar)messageNumberAssigned==false); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthdata==0) - { - //sendPacketSet[ i ].Pop(); - outgoingPacketBuffer.Pop(0); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]--; - statistics.bytesInSendBuffer[(int)internalPacket->priority]-=(double) BITS_TO_BYTES(internalPacket->dataBitLength); - ReleaseToInternalPacketPool( internalPacket ); - continue; - } - - internalPacket->headerLength=GetMessageHeaderLengthBits(internalPacket); - nextPacketBitLength = internalPacket->headerLength + internalPacket->dataBitLength; - if ( datagramSizeSoFar + nextPacketBitLength > GetMaxDatagramSizeExcludingMessageHeaderBits() ) - { - // Hit MTU. May still push packets if smaller ones exist at a lower priority - RakAssert(datagramSizeSoFar!=0); - RakAssert(internalPacket->dataBitLengthreliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - isReliable = true; - else - isReliable = false; - - //sendPacketSet[ i ].Pop(); - outgoingPacketBuffer.Pop(0); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthmessageNumberAssigned==false); - statistics.messageInSendBuffer[(int)internalPacket->priority]--; - statistics.bytesInSendBuffer[(int)internalPacket->priority]-=(double) BITS_TO_BYTES(internalPacket->dataBitLength); - if (isReliable - /* - I thought about this and agree that UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT and RELIABLE_SEQUENCED_WITH_ACK_RECEIPT is not useful unless you also know if the message was discarded. - - The problem is that internally, message numbers are only assigned to reliable messages, because message numbers are only used to discard duplicate message receipt and only reliable messages get sent more than once. However, without message numbers getting assigned and transmitted, there is no way to tell the sender about which messages were discarded. In fact, in looking this over I realized that UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT introduced a bug, because the remote system assumes all message numbers are used (no holes). With that send type, on packetloss, a permanent hole would have been created which eventually would cause the system to discard all further packets. - - So I have two options. Either do not support ack receipts when sending sequenced, or write complex and major new systems. UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT would need to send the message ID number on a special channel which allows for non-delivery. And both of them would need to have a special range list to indicate which message numbers were not delivered, so when acks are sent that can be indicated as well. A further problem is that the ack itself can be lost - it is possible that the message can arrive but be discarded, yet the ack is lost. On resend, the resent message would be ignored as duplicate, and you'd never get the discard message either (unless I made a special buffer for that case too). -*/ -// || - // If needs an ack receipt, keep the internal packet around in the list -// internalPacket->reliability == UNRELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - { - internalPacket->messageNumberAssigned=true; - internalPacket->reliableMessageNumber=sendReliableMessageNumberIndex; - internalPacket->nextActionTime = congestionManager.GetRTOForRetransmission()+time; -#if CC_TIME_TYPE_BYTES==4 - const CCTimeType threshhold = 10000; -#else - const CCTimeType threshhold = 10000000; -#endif - if (internalPacket->nextActionTime-time > threshhold) - { - // int a=5; - RakAssert(time-internalPacket->nextActionTime < threshhold); - } - //resendTree.Insert( internalPacket->reliableMessageNumber, internalPacket); - if (resendBuffer[internalPacket->reliableMessageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK]!=0) - { - // bool overflow = ResendBufferOverflow(); - RakAssert(0); - } - resendBuffer[internalPacket->reliableMessageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK] = internalPacket; - statistics.messagesInResendBuffer++; - statistics.bytesInResendBuffer+=BITS_TO_BYTES(internalPacket->dataBitLength); - - // printf("pre:%i ", unacknowledgedBytes); - - InsertPacketIntoResendList( internalPacket, time, true, isReliable); - - - // printf("post:%i ", unacknowledgedBytes); - sendReliableMessageNumberIndex++; - } - else if (internalPacket->reliability == UNRELIABLE_WITH_ACK_RECEIPT) - { - unreliableWithAckReceiptHistory.Push(UnreliableWithAckReceiptNode( - congestionManager.GetNextDatagramSequenceNumber() + packetsToSendThisUpdateDatagramBoundaries.Size(), - internalPacket->sendReceiptSerial, - congestionManager.GetRTOForRetransmission()+time - ), _FILE_AND_LINE_); - } - -// internalPacket->timesSent=1; - // If isReliable is false, the packet and its contents will be added to a list to be freed in ClearPacketsAndDatagrams - // However, the internalPacket structure will remain allocated and be in the resendBuffer list if it requires a receipt - bpsMetrics[(int) USER_MESSAGE_BYTES_SENT].Push1(time,BITS_TO_BYTES(internalPacket->dataBitLength)); - - // Testing1 -// if (internalPacket->reliability==RELIABLE_ORDERED || internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) -// printf("SEND reliableMessageNumber %i in datagram %i\n", internalPacket->reliableMessageNumber.val, congestionManager.GetNextDatagramSequenceNumber().val); - - PushPacket(time,internalPacket, isReliable); - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - { -#if CC_TIME_TYPE_BYTES==4 - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)time, true); -#else - messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000), true); -#endif - } - pushedAnything=true; - - if (ResendBufferOverflow()) - break; - } - // if (ResendBufferOverflow()) - // break; - // }z - - // No datagrams pushed? - if (datagramSizeSoFar==0) - break; - - // Filled one datagram. - // If the 2nd and it's time to send a datagram pair, will be marked as a pair - PushDatagram(); - } - } - - - for (unsigned int datagramIndex=0; datagramIndex < packetsToSendThisUpdateDatagramBoundaries.Size(); datagramIndex++) - { - if (datagramIndex>0) - dhf.isContinuousSend=true; - MessageNumberNode* messageNumberNode = 0; - dhf.datagramNumber=congestionManager.GetAndIncrementNextDatagramSequenceNumber(); - dhf.isPacketPair=datagramsToSendThisUpdateIsPair[datagramIndex]; - - //printf("%p pushing datagram %i\n", this, dhf.datagramNumber.val); - - bool isSecondOfPacketPair=dhf.isPacketPair && datagramIndex>0 && datagramsToSendThisUpdateIsPair[datagramIndex-1]; - unsigned int msgIndex, msgTerm; - if (datagramIndex==0) - { - msgIndex=0; - msgTerm=packetsToSendThisUpdateDatagramBoundaries[0]; - } - else - { - msgIndex=packetsToSendThisUpdateDatagramBoundaries[datagramIndex-1]; - msgTerm=packetsToSendThisUpdateDatagramBoundaries[datagramIndex]; - } - - // More accurate time to reset here -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - dhf.sourceSystemTime=RakNet::GetTimeUS(); -#endif - updateBitStream.Reset(); - dhf.Serialize(&updateBitStream); - CC_DEBUG_PRINTF_2("S%i ",dhf.datagramNumber.val); - - while (msgIndex < msgTerm) - { - // If reliable or needs receipt - if ( packetsToSendThisUpdate[msgIndex]->reliability != UNRELIABLE && - packetsToSendThisUpdate[msgIndex]->reliability != UNRELIABLE_SEQUENCED - ) - { - if (messageNumberNode==0) - { - messageNumberNode = AddFirstToDatagramHistory(dhf.datagramNumber, packetsToSendThisUpdate[msgIndex]->reliableMessageNumber, time); - } - else - { - messageNumberNode = AddSubsequentToDatagramHistory(messageNumberNode, packetsToSendThisUpdate[msgIndex]->reliableMessageNumber); - } - } - - RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - WriteToBitStreamFromInternalPacket( &updateBitStream, packetsToSendThisUpdate[msgIndex], time ); - RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - msgIndex++; - } - - if (isSecondOfPacketPair) - { - // Pad to size of first datagram - RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - updateBitStream.PadWithZeroToByteLength(datagramSizesInBytes[datagramIndex-1]); - RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - } - - if (messageNumberNode==0) - { - // Unreliable, add dummy node - AddFirstToDatagramHistory(dhf.datagramNumber, time); - } - - // Store what message ids were sent with this datagram - // datagramMessageIDTree.Insert(dhf.datagramNumber,idList); - - congestionManager.OnSendBytes(time,UDP_HEADER_SIZE+DatagramHeaderFormat::GetDataHeaderByteLength()); - - SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); - - bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; - // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || - // sendPacketSet[1].IsEmpty()==false || - // sendPacketSet[2].IsEmpty()==false || - // sendPacketSet[3].IsEmpty()==false; - - - - if (bandwidthExceededStatistic==true) - timeOfLastContinualSend=time; - else - timeOfLastContinualSend=0; - } - - ClearPacketsAndDatagrams(); - - // Any data waiting to send after attempting to send, then bandwidth is exceeded - bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; - // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || - // sendPacketSet[1].IsEmpty()==false || - // sendPacketSet[2].IsEmpty()==false || - // sendPacketSet[3].IsEmpty()==false; - } - - - // Keep on top of deleting old unreliable split packets so they don't clog the list. - //DeleteOldUnreliableSplitPackets( time ); -} - -//------------------------------------------------------------------------------------------------------- -// Writes a bitstream to the socket -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SendBitStream( SOCKET s, SystemAddress &systemAddress, RakNet::BitStream *bitStream, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType currentTime) -{ - (void) systemAddress; - (void) rnr; - - unsigned int length; - - length = (unsigned int) bitStream->GetNumberOfBytesUsed(); - - -#ifdef _DEBUG - if (packetloss > 0.0) - { - if (frandomMT() < packetloss) - return; - } - - if (minExtraPing > 0 || extraPingVariance > 0) - { - RakNet::TimeMS delay = minExtraPing; - if (extraPingVariance>0) - delay += (randomMT() % extraPingVariance); - if (delay > 0) - { - DataAndTime *dat = RakNet::OP_NEW(__FILE__,__LINE__); - memcpy(dat->data, ( char* ) bitStream->GetData(), length ); - dat->s=s; - dat->length=length; - dat->sendTime = RakNet::GetTimeMS() + delay; - dat->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; - dat->extraSocketOptions=extraSocketOptions; - for (unsigned int i=0; i < delayList.Size(); i++) - { - if (dat->sendTime < delayList[i]->sendTime) - { - delayList.PushAtHead(dat, i, __FILE__, __LINE__); - dat=0; - break; - } - } - if (dat!=0) - delayList.Push(dat,__FILE__,__LINE__); - return; - } - } -#endif - -#if LIBCAT_SECURITY==1 - if (useSecurity) - { - unsigned char *buffer = reinterpret_cast( bitStream->GetData() ); - - int buffer_size = bitStream->GetNumberOfBitsAllocated() / 8; - - // Verify there is enough room for encrypted output and encrypt - // Encrypt() will increase length - bool success = auth_enc.Encrypt(buffer, buffer_size, length); - RakAssert(success); - } -#endif - - bpsMetrics[(int) ACTUAL_BYTES_SENT].Push1(currentTime,length); - - RakAssert(length <= congestionManager.GetMTU()); - -#ifdef USE_THREADED_SEND - SendToThread::SendToThreadBlock *block = SendToThread::AllocateBlock(); - memcpy(block->data, bitStream->GetData(), length); - block->dataWriteOffset=length; - block->extraSocketOptions=extraSocketOptions; - block->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; - block->s=s; - block->systemAddress=systemAddress; - SendToThread::ProcessBlock(block); -#else - SocketLayer::SendTo( s, ( char* ) bitStream->GetData(), length, systemAddress, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, __FILE__, __LINE__ ); -#endif -} - -//------------------------------------------------------------------------------------------------------- -// Are we waiting for any data to be sent out or be processed by the player? -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsOutgoingDataWaiting(void) -{ - if (outgoingPacketBuffer.Size()>0) - return true; - - // unsigned i; - // for ( i = 0; i < NUMBER_OF_PRIORITIES; i++ ) - // { - // if (sendPacketSet[ i ].Size() > 0) - // return true; - // } - - return - //acknowlegements.Size() > 0 || - //resendTree.IsEmpty()==false;// || outputQueue.Size() > 0 || orderingList.Size() > 0 || splitPacketChannelList.Size() > 0; - statistics.messagesInResendBuffer!=0; -} -bool ReliabilityLayer::AreAcksWaiting(void) -{ - return acknowlegements.Size() > 0; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ApplyNetworkSimulator( double _packetloss, RakNet::TimeMS _minExtraPing, RakNet::TimeMS _extraPingVariance ) -{ -#ifdef _DEBUG - packetloss=_packetloss; - minExtraPing=_minExtraPing; - extraPingVariance=_extraPingVariance; - // if (ping < (unsigned int)(minExtraPing+extraPingVariance)*2) - // ping=(minExtraPing+extraPingVariance)*2; -#endif -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SetSplitMessageProgressInterval(int interval) -{ - splitMessageProgressInterval=interval; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SetUnreliableTimeout(RakNet::TimeMS timeoutMS) -{ -#if CC_TIME_TYPE_BYTES==4 - unreliableTimeout=timeoutMS; -#else - unreliableTimeout=(CCTimeType)timeoutMS*(CCTimeType)1000; -#endif -} - -//------------------------------------------------------------------------------------------------------- -// This will return true if we should not send at this time -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsSendThrottled( int MTUSize ) -{ - (void) MTUSize; - - return false; - // return resendList.Size() > windowSize; - - // Disabling this, because it can get stuck here forever - /* - unsigned packetsWaiting; - unsigned resendListDataSize=0; - unsigned i; - for (i=0; i < resendList.Size(); i++) - { - if (resendList[i]) - resendListDataSize+=resendList[i]->dataBitLength; - } - packetsWaiting = 1 + ((BITS_TO_BYTES(resendListDataSize)) / (MTUSize - UDP_HEADER_SIZE - 10)); // 10 to roughly estimate the raknet header - - return packetsWaiting >= windowSize; - */ -} - -//------------------------------------------------------------------------------------------------------- -// We lost a packet -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::UpdateWindowFromPacketloss( CCTimeType time ) -{ - (void) time; -} - -//------------------------------------------------------------------------------------------------------- -// Increase the window size -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::UpdateWindowFromAck( CCTimeType time ) -{ - (void) time; -} - -//------------------------------------------------------------------------------------------------------- -// Does what the function name says -//------------------------------------------------------------------------------------------------------- -unsigned ReliabilityLayer::RemovePacketFromResendListAndDeleteOlderReliableSequenced( const MessageNumberType messageNumber, CCTimeType time, DataStructures::List &messageHandlerList, const SystemAddress &systemAddress ) -{ - (void) time; - (void) messageNumber; - InternalPacket * internalPacket; - //InternalPacket *temp; -// PacketReliability reliability; // What type of reliability algorithm to use with this packet -// unsigned char orderingChannel; // What ordering channel this packet is on, if the reliability type uses ordering channels -// OrderingIndexType orderingIndex; // The ID used as identification for ordering channels - // unsigned j; - - for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) - { -#if CC_TIME_TYPE_BYTES==4 - messageHandlerList[messageHandlerIndex]->OnAck(messageNumber, systemAddress, time); -#else - messageHandlerList[messageHandlerIndex]->OnAck(messageNumber, systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000)); -#endif - } - - // Testing1 -// if (resendLinkedListHead) -// { -// InternalPacket *internalPacket = resendLinkedListHead; -// do -// { -// internalPacket=internalPacket->resendNext; -// printf("%i ", internalPacket->reliableMessageNumber.val); -// } while (internalPacket!=resendLinkedListHead); -// printf("\n"); -// } - - // bool deleted; - // deleted=resendTree.Delete(messageNumber, internalPacket); - internalPacket = resendBuffer[messageNumber & RESEND_BUFFER_ARRAY_MASK]; - // May ask to remove twice, for example resend twice, then second ack - if (internalPacket && internalPacket->reliableMessageNumber==messageNumber) - { - // ValidateResendList(); - resendBuffer[messageNumber & RESEND_BUFFER_ARRAY_MASK]=0; - CC_DEBUG_PRINTF_2("AckRcv %i ", messageNumber); - - statistics.messagesInResendBuffer--; - statistics.bytesInResendBuffer-=BITS_TO_BYTES(internalPacket->dataBitLength); - -// orderingIndex = internalPacket->orderingIndex; - totalUserDataBytesAcked+=(double) BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); - - // Return receipt if asked for - if (internalPacket->reliability>=RELIABLE_WITH_ACK_RECEIPT && - (internalPacket->splitPacketCount==0 || internalPacket->splitPacketIndex+1==internalPacket->splitPacketCount) - ) - { - InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); - AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); - ackReceipt->dataBitLength=BYTES_TO_BITS(5); - ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_ACKED; - memcpy(ackReceipt->data+sizeof(MessageID), &internalPacket->sendReceiptSerial, sizeof(internalPacket->sendReceiptSerial)); - outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); - } - - bool isReliable; - if ( internalPacket->reliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - isReliable = true; - else - isReliable = false; - - RemoveFromList(internalPacket, isReliable); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - - return 0; - } - else - { - - } - - return (unsigned)-1; -} - -//------------------------------------------------------------------------------------------------------- -// Acknowledge receipt of the packet with the specified messageNumber -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SendAcknowledgementPacket( const DatagramSequenceNumberType messageNumber, CCTimeType time ) -{ - - // REMOVEME - // printf("%p Send ack %i\n", this, messageNumber.val); - - nextAckTimeToSend=time; - acknowlegements.Insert(messageNumber); - - //printf("ACK_DG:%i ", messageNumber.val); - - CC_DEBUG_PRINTF_2("AckPush %i ", messageNumber); - -} - -//------------------------------------------------------------------------------------------------------- -// Parse an internalPacket and figure out how many header bits would be -// written. Returns that number -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::GetMaxMessageHeaderLengthBits( void ) -{ - InternalPacket ip; - ip.reliability=RELIABLE_SEQUENCED; - ip.splitPacketCount=1; - return GetMessageHeaderLengthBits(&ip); -} -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::GetMessageHeaderLengthBits( const InternalPacket *const internalPacket ) -{ - BitSize_t bitLength; - - // bitStream->AlignWriteToByteBoundary(); // Potentially unaligned - // tempChar=(unsigned char)internalPacket->reliability; bitStream->WriteBits( (const unsigned char *)&tempChar, 3, true ); // 3 bits to write reliability. - // bool hasSplitPacket = internalPacket->splitPacketCount>0; bitStream->Write(hasSplitPacket); // Write 1 bit to indicate if splitPacketCount>0 - bitLength = 8*1; - - // bitStream->AlignWriteToByteBoundary(); - // RakAssert(internalPacket->dataBitLength < 65535); - // unsigned short s; s = (unsigned short) internalPacket->dataBitLength; bitStream->WriteAlignedVar16((const char*)& s); - bitLength += 8*2; - - if ( internalPacket->reliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - bitLength += 8*3; // bitStream->Write(internalPacket->reliableMessageNumber); // Message sequence number - // bitStream->AlignWriteToByteBoundary(); // Potentially nothing else to write - - - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED - ) - { - bitLength += 8*3;; // bitStream->Write(internalPacket->_sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - } - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - { - bitLength += 8*3; // bitStream->Write(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - bitLength += 8*1; // tempChar=internalPacket->orderingChannel; bitStream->WriteAlignedVar8((const char*)& tempChar); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, write one byte - } - if (internalPacket->splitPacketCount>0) - { - bitLength += 8*4; // bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketCount); RakAssert(sizeof(SplitPacketIndexType)==4); // Only needed if splitPacketCount>0. 4 bytes - bitLength += 8*sizeof(SplitPacketIdType); // bitStream->WriteAlignedVar16((const char*)& internalPacket->splitPacketId); RakAssert(sizeof(SplitPacketIdType)==2); // Only needed if splitPacketCount>0. - bitLength += 8*4; // bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes - } - - return bitLength; -} - -//------------------------------------------------------------------------------------------------------- -// Parse an internalPacket and create a bitstream to represent this data -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket, CCTimeType curTime ) -{ - (void) curTime; - - BitSize_t start = bitStream->GetNumberOfBitsUsed(); - unsigned char tempChar; - - // (Incoming data may be all zeros due to padding) - bitStream->AlignWriteToByteBoundary(); // Potentially unaligned - if (internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) - tempChar=UNRELIABLE; - else if (internalPacket->reliability==RELIABLE_WITH_ACK_RECEIPT) - tempChar=RELIABLE; - else if (internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) - tempChar=RELIABLE_ORDERED; - else - tempChar=(unsigned char)internalPacket->reliability; - - bitStream->WriteBits( (const unsigned char *)&tempChar, 3, true ); // 3 bits to write reliability. - - bool hasSplitPacket = internalPacket->splitPacketCount>0; bitStream->Write(hasSplitPacket); // Write 1 bit to indicate if splitPacketCount>0 - bitStream->AlignWriteToByteBoundary(); - RakAssert(internalPacket->dataBitLength < 65535); - unsigned short s; s = (unsigned short) internalPacket->dataBitLength; bitStream->WriteAlignedVar16((const char*)& s); - if ( internalPacket->reliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - bitStream->Write(internalPacket->reliableMessageNumber); // Used for all reliable types - bitStream->AlignWriteToByteBoundary(); // Potentially nothing else to write - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED - ) - { - bitStream->Write(internalPacket->sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - } - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - { - bitStream->Write(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - tempChar=internalPacket->orderingChannel; bitStream->WriteAlignedVar8((const char*)& tempChar); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, write one byte - } - - if (internalPacket->splitPacketCount>0) - { - // printf("Write before\n"); - // bitStream->PrintBits(); - - bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketCount); RakAssert(sizeof(SplitPacketIndexType)==4); // Only needed if splitPacketCount>0. 4 bytes - bitStream->WriteAlignedVar16((const char*)& internalPacket->splitPacketId); RakAssert(sizeof(SplitPacketIdType)==2); // Only needed if splitPacketCount>0. - bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes - - // printf("Write after\n"); - // bitStream->PrintBits(); - } - - // Write the actual data. - bitStream->WriteAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); - - return bitStream->GetNumberOfBitsUsed() - start; -} - -//------------------------------------------------------------------------------------------------------- -// Parse a bitstream and create an internal packet to represent this data -//------------------------------------------------------------------------------------------------------- -InternalPacket* ReliabilityLayer::CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, CCTimeType time ) -{ - bool bitStreamSucceeded; - InternalPacket* internalPacket; - unsigned char tempChar; - bool hasSplitPacket=false; - bool readSuccess; - - if ( bitStream->GetNumberOfUnreadBits() < (int) sizeof( internalPacket->reliableMessageNumber ) * 8 ) - return 0; // leftover bits - - internalPacket = AllocateFromInternalPacketPool(); - if (internalPacket==0) - { - // Out of memory - RakAssert(0); - return 0; - } - internalPacket->creationTime = time; - - // (Incoming data may be all zeros due to padding) - bitStream->AlignReadToByteBoundary(); // Potentially unaligned - bitStream->ReadBits( ( unsigned char* ) ( &( tempChar ) ), 3 ); - internalPacket->reliability = ( const PacketReliability ) tempChar; - readSuccess=bitStream->Read(hasSplitPacket); // Read 1 bit to indicate if splitPacketCount>0 - bitStream->AlignReadToByteBoundary(); - unsigned short s; bitStream->ReadAlignedVar16((char*)&s); internalPacket->dataBitLength=s; // Length of message (2 bytes) - if ( internalPacket->reliability == RELIABLE || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED - // I don't write ACK_RECEIPT to the remote system -// || -// internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || -// internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - bitStream->Read(internalPacket->reliableMessageNumber); // Message sequence number - else - internalPacket->reliableMessageNumber=(MessageNumberType)(const uint32_t)-1; - bitStream->AlignReadToByteBoundary(); // Potentially nothing else to Read - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED - ) - { - bitStream->Read(internalPacket->sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. - } - - if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_SEQUENCED || - internalPacket->reliability == RELIABLE_ORDERED || - internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT - ) - { - bitStream->Read(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 4 bytes. - readSuccess=bitStream->ReadAlignedVar8((char*)& internalPacket->orderingChannel); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, Read one byte - } - else - internalPacket->orderingChannel=0; - - if (hasSplitPacket) - { -// printf("Read before\n"); -// bitStream->PrintBits(); - - bitStream->ReadAlignedVar32((char*)& internalPacket->splitPacketCount); // Only needed if splitPacketCount>0. 4 bytes - bitStream->ReadAlignedVar16((char*)& internalPacket->splitPacketId); // Only needed if splitPacketCount>0. - readSuccess=bitStream->ReadAlignedVar32((char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes - RakAssert(readSuccess); - -// printf("Read after\n"); -// bitStream->PrintBits(); - } - else - { - internalPacket->splitPacketCount=0; - } - - if (readSuccess==false || - internalPacket->dataBitLength==0 || - internalPacket->reliability>=NUMBER_OF_RELIABILITIES || - internalPacket->orderingChannel>=32 || - (hasSplitPacket && (internalPacket->splitPacketIndex >= internalPacket->splitPacketCount))) - { - // If this assert hits, encoding is garbage - RakAssert("Encoding is garbage" && 0); - ReleaseToInternalPacketPool( internalPacket ); - return 0; - } - - // Allocate memory to hold our data - AllocInternalPacketData(internalPacket, BITS_TO_BYTES( internalPacket->dataBitLength ), false, _FILE_AND_LINE_ ); - RakAssert(BITS_TO_BYTES( internalPacket->dataBitLength )data == 0) - { - RakAssert("Out of memory in ReliabilityLayer::CreateInternalPacketFromBitStream" && 0); - notifyOutOfMemory(_FILE_AND_LINE_); - ReleaseToInternalPacketPool( internalPacket ); - return 0; - } - - // Set the last byte to 0 so if ReadBits does not read a multiple of 8 the last bits are 0'ed out - internalPacket->data[ BITS_TO_BYTES( internalPacket->dataBitLength ) - 1 ] = 0; - - // Read the data the packet holds - bitStreamSucceeded = bitStream->ReadAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); - - if ( bitStreamSucceeded == false ) - { - // If this hits, most likely the variable buff is too small in RunUpdateCycle in RakPeer.cpp - RakAssert("Couldn't read all the data" && 0); - - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - return 0; - } - - return internalPacket; -} - - -//------------------------------------------------------------------------------------------------------- -// Get the SHA1 code -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::GetSHA1( unsigned char * const buffer, unsigned int - nbytes, char code[ SHA1_LENGTH ] ) -{ - CSHA1 sha1; - - sha1.Reset(); - sha1.Update( ( unsigned char* ) buffer, nbytes ); - sha1.Final(); - memcpy( code, sha1.GetHash(), SHA1_LENGTH ); -} - -//------------------------------------------------------------------------------------------------------- -// Check the SHA1 code -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * - const buffer, unsigned int nbytes ) -{ - char code2[ SHA1_LENGTH ]; - GetSHA1( buffer, nbytes, code2 ); - - for ( int i = 0; i < SHA1_LENGTH; i++ ) - if ( code[ i ] != code2[ i ] ) - return false; - - return true; -} - -//------------------------------------------------------------------------------------------------------- -// Search the specified list for sequenced packets on the specified ordering -// stream, optionally skipping those with splitPacketId, and delete them -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::List&theList, int splitPacketId ) -{ - unsigned i = 0; - - while ( i < theList.Size() ) - { - if ( ( - theList[ i ]->reliability == RELIABLE_SEQUENCED || - theList[ i ]->reliability == UNRELIABLE_SEQUENCED -// || -// theList[ i ]->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || -// theList[ i ]->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) && - theList[ i ]->orderingChannel == orderingChannel && ( splitPacketId == -1 || theList[ i ]->splitPacketId != (unsigned int) splitPacketId ) ) - { - InternalPacket * internalPacket = theList[ i ]; - theList.RemoveAtIndex( i ); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - } - - else - i++; - } -} - -//------------------------------------------------------------------------------------------------------- -// Search the specified list for sequenced packets with a value less than orderingIndex and delete them -// Note - I added functionality so you can use the Queue as a list (in this case for searching) but it is less efficient to do so than a regular list -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::Queue&theList ) -{ - InternalPacket * internalPacket; - int listSize = theList.Size(); - int i = 0; - - while ( i < listSize ) - { - if ( ( - theList[ i ]->reliability == RELIABLE_SEQUENCED || - theList[ i ]->reliability == UNRELIABLE_SEQUENCED -// || -// theList[ i ]->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || -// theList[ i ]->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) && theList[ i ]->orderingChannel == orderingChannel ) - { - internalPacket = theList[ i ]; - theList.RemoveAtIndex( i ); - FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - listSize--; - } - - else - i++; - } -} - -//------------------------------------------------------------------------------------------------------- -// Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ) -{ - OrderingIndexType maxRange = (OrderingIndexType) (const uint32_t)-1; - - if ( waitingForPacketOrderingIndex > maxRange/(OrderingIndexType)2 ) - { - if ( newPacketOrderingIndex >= waitingForPacketOrderingIndex - maxRange/(OrderingIndexType)2+(OrderingIndexType)1 && newPacketOrderingIndex < waitingForPacketOrderingIndex ) - { - return true; - } - } - - else - if ( newPacketOrderingIndex >= ( OrderingIndexType ) ( waitingForPacketOrderingIndex - (( OrderingIndexType ) maxRange/(OrderingIndexType)2+(OrderingIndexType)1) ) || - newPacketOrderingIndex < waitingForPacketOrderingIndex ) - { - return true; - } - - // Old packet - return false; -} - -//------------------------------------------------------------------------------------------------------- -// Split the passed packet into chunks under MTU_SIZEbytes (including headers) and save those new chunks -// Optimized version -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SplitPacket( InternalPacket *internalPacket ) -{ - // Doing all sizes in bytes in this function so I don't write partial bytes with split packets - internalPacket->splitPacketCount = 1; // This causes GetMessageHeaderLengthBits to account for the split packet header - unsigned int headerLength = (unsigned int) BITS_TO_BYTES( GetMessageHeaderLengthBits( internalPacket ) ); - unsigned int dataByteLength = (unsigned int) BITS_TO_BYTES( internalPacket->dataBitLength ); - int maximumSendBlockBytes, byteOffset, bytesToSend; - SplitPacketIndexType splitPacketIndex; - int i; - InternalPacket **internalPacketArray; - - maximumSendBlockBytes = GetMaxDatagramSizeExcludingMessageHeaderBytes() - BITS_TO_BYTES(GetMaxMessageHeaderLengthBits()); - - // Calculate how many packets we need to create - internalPacket->splitPacketCount = ( ( dataByteLength - 1 ) / ( maximumSendBlockBytes ) + 1 ); - - // Optimization - // internalPacketArray = RakNet::OP_NEW(internalPacket->splitPacketCount, _FILE_AND_LINE_ ); - bool usedAlloca=false; - - if (sizeof( InternalPacket* ) * internalPacket->splitPacketCount < MAX_ALLOCA_STACK_ALLOCATION) - { - internalPacketArray = ( InternalPacket** ) alloca( sizeof( InternalPacket* ) * internalPacket->splitPacketCount ); - usedAlloca=true; - } - else - - internalPacketArray = (InternalPacket**) rakMalloc_Ex( sizeof(InternalPacket*) * internalPacket->splitPacketCount, _FILE_AND_LINE_ ); - - for ( i = 0; i < ( int ) internalPacket->splitPacketCount; i++ ) - { - internalPacketArray[ i ] = AllocateFromInternalPacketPool(); - - //internalPacketArray[ i ] = (InternalPacket*) alloca( sizeof( InternalPacket ) ); - // internalPacketArray[ i ] = sendPacketSet[internalPacket->priority].WriteLock(); - *internalPacketArray[ i ]=*internalPacket; - internalPacketArray[ i ]->messageNumberAssigned=false; - - if (i!=0) - internalPacket->messageInternalOrder = internalOrderIndex++; - } - - // This identifies which packet this is in the set - splitPacketIndex = 0; - - InternalPacketRefCountedData *refCounter=0; - - // Do a loop to send out all the packets - do - { - byteOffset = splitPacketIndex * maximumSendBlockBytes; - bytesToSend = dataByteLength - byteOffset; - - if ( bytesToSend > maximumSendBlockBytes ) - bytesToSend = maximumSendBlockBytes; - - // Copy over our chunk of data - - AllocInternalPacketData(internalPacketArray[ splitPacketIndex ], &refCounter, internalPacket->data, internalPacket->data + byteOffset); - // internalPacketArray[ splitPacketIndex ]->data = (unsigned char*) rakMalloc_Ex( bytesToSend, _FILE_AND_LINE_ ); - // memcpy( internalPacketArray[ splitPacketIndex ]->data, internalPacket->data + byteOffset, bytesToSend ); - - if ( bytesToSend != maximumSendBlockBytes ) - internalPacketArray[ splitPacketIndex ]->dataBitLength = internalPacket->dataBitLength - splitPacketIndex * ( maximumSendBlockBytes << 3 ); - else - internalPacketArray[ splitPacketIndex ]->dataBitLength = bytesToSend << 3; - - internalPacketArray[ splitPacketIndex ]->splitPacketIndex = splitPacketIndex; - internalPacketArray[ splitPacketIndex ]->splitPacketId = splitPacketId; - internalPacketArray[ splitPacketIndex ]->splitPacketCount = internalPacket->splitPacketCount; - RakAssert(internalPacketArray[ splitPacketIndex ]->dataBitLengthsplitPacketCount ); - - splitPacketId++; // It's ok if this wraps to 0 - - // InternalPacket *workingPacket; - - // Tell the heap we are going to push a list of elements where each element in the list follows the heap order - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthsplitPacketCount; i++ ) - { - internalPacketArray[ i ]->headerLength=headerLength; - RakAssert(internalPacketArray[ i ]->dataBitLengthpriority ].Push( internalPacketArray[ i ], _FILE_AND_LINE_ ); - RakAssert(internalPacketArray[ i ]->dataBitLengthmessageNumberAssigned==false); - outgoingPacketBuffer.PushSeries(GetNextWeight(internalPacketArray[ i ]->priority), internalPacketArray[ i ], _FILE_AND_LINE_); - RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]++; - statistics.bytesInSendBuffer[(int)(int)internalPacketArray[ i ]->priority]+=(double) BITS_TO_BYTES(internalPacketArray[ i ]->dataBitLength); - // workingPacket=sendPacketSet[internalPacket->priority].WriteLock(); - // memcpy(workingPacket, internalPacketArray[ i ], sizeof(InternalPacket)); - // sendPacketSet[internalPacket->priority].WriteUnlock(); - } - - // Do not delete, original is referenced by all split packets to avoid numerous allocations. See AllocInternalPacketData above - // FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool( internalPacket ); - - if (usedAlloca==false) - rakFree_Ex(internalPacketArray, _FILE_AND_LINE_ ); -} - -//------------------------------------------------------------------------------------------------------- -// Insert a packet into the split packet list -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::InsertIntoSplitPacketList( InternalPacket * internalPacket, CCTimeType time ) -{ - bool objectExists; - unsigned index; - index=splitPacketChannelList.GetIndexFromKey(internalPacket->splitPacketId, &objectExists); - if (objectExists==false) - { - SplitPacketChannel *newChannel = RakNet::OP_NEW( __FILE__, __LINE__ ); -#if PREALLOCATE_LARGE_MESSAGES==1 - index=splitPacketChannelList.Insert(internalPacket->splitPacketId, newChannel, true, __FILE__,__LINE__); - newChannel->returnedPacket=CreateInternalPacketCopy( internalPacket, 0, 0, time ); - newChannel->gotFirstPacket=false; - newChannel->splitPacketsArrived=0; - AllocInternalPacketData(newChannel->returnedPacket, BITS_TO_BYTES( internalPacket->dataBitLength*internalPacket->splitPacketCount ), false, __FILE__, __LINE__ ); - RakAssert(newChannel->returnedPacket->data); -#else - newChannel->firstPacket=0; - index=splitPacketChannelList.Insert(internalPacket->splitPacketId, newChannel, true, __FILE__,__LINE__); - // Preallocate to the final size, to avoid runtime copies - newChannel->splitPacketList.Preallocate(internalPacket->splitPacketCount, __FILE__,__LINE__); - -#endif - } - -#if PREALLOCATE_LARGE_MESSAGES==1 - splitPacketChannelList[index]->lastUpdateTime=time; - splitPacketChannelList[index]->splitPacketsArrived++; - splitPacketChannelList[index]->returnedPacket->dataBitLength+=internalPacket->dataBitLength; - - bool dealloc; - if (internalPacket->splitPacketIndex==0) - { - splitPacketChannelList[index]->gotFirstPacket=true; - splitPacketChannelList[index]->stride=BITS_TO_BYTES(internalPacket->dataBitLength); - - for (unsigned int j=0; j < splitPacketChannelList[index]->splitPacketList.Size(); j++) - { - memcpy(splitPacketChannelList[index]->returnedPacket->data+internalPacket->splitPacketIndex*splitPacketChannelList[index]->stride, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); - FreeInternalPacketData(splitPacketChannelList[index]->splitPacketList[j], __FILE__, __LINE__ ); - ReleaseToInternalPacketPool(splitPacketChannelList[index]->splitPacketList[j]); - } - - memcpy(splitPacketChannelList[index]->returnedPacket->data, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); - splitPacketChannelList[index]->splitPacketList.Clear(true,__FILE__,__LINE__); - dealloc=true; - } - else - { - if (splitPacketChannelList[index]->gotFirstPacket==true) - { - memcpy(splitPacketChannelList[index]->returnedPacket->data+internalPacket->splitPacketIndex*splitPacketChannelList[index]->stride, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); - dealloc=true; - } - else - { - splitPacketChannelList[index]->splitPacketList.Push(internalPacket,__FILE__,__LINE__); - dealloc=false; - } - } - - if (splitPacketChannelList[index]->gotFirstPacket==true && - splitMessageProgressInterval && - // splitPacketChannelList[index]->firstPacket && - // splitPacketChannelList[index]->splitPacketList.Size()!=splitPacketChannelList[index]->firstPacket->splitPacketCount && - // (splitPacketChannelList[index]->splitPacketList.Size()%splitMessageProgressInterval)==0 - splitPacketChannelList[index]->gotFirstPacket && - splitPacketChannelList[index]->splitPacketsArrived!=splitPacketChannelList[index]->returnedPacket->splitPacketCount && - (splitPacketChannelList[index]->splitPacketsArrived%splitMessageProgressInterval)==0 - ) - { - // Return ID_DOWNLOAD_PROGRESS - // Write splitPacketIndex (SplitPacketIndexType) - // Write splitPacketCount (SplitPacketIndexType) - // Write byteLength (4) - // Write data, splitPacketChannelList[index]->splitPacketList[0]->data - InternalPacket *progressIndicator = AllocateFromInternalPacketPool(); - // unsigned int len = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + (unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); - unsigned int l = (unsigned int) splitPacketChannelList[index]->stride; - const unsigned int len = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + l; - AllocInternalPacketData(progressIndicator, len, false, __FILE__, __LINE__ ); - progressIndicator->dataBitLength=BYTES_TO_BITS(len); - progressIndicator->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; - unsigned int temp; - // temp=splitPacketChannelList[index]->splitPacketList.Size(); - temp=splitPacketChannelList[index]->splitPacketsArrived; - memcpy(progressIndicator->data+sizeof(MessageID), &temp, sizeof(unsigned int)); - temp=(unsigned int)internalPacket->splitPacketCount; - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*1, &temp, sizeof(unsigned int)); - // temp=(unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); - temp=(unsigned int) BITS_TO_BYTES(l); - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*2, &temp, sizeof(unsigned int)); - //memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->firstPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength)); - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->returnedPacket->data, (size_t) BITS_TO_BYTES(l)); - } - - if (dealloc) - { - FreeInternalPacketData(internalPacket, __FILE__, __LINE__ ); - ReleaseToInternalPacketPool(internalPacket); - } -#else - splitPacketChannelList[index]->splitPacketList.Insert(internalPacket, __FILE__, __LINE__ ); - splitPacketChannelList[index]->lastUpdateTime=time; - - if (internalPacket->splitPacketIndex==0) - splitPacketChannelList[index]->firstPacket=internalPacket; - - if (splitMessageProgressInterval && - splitPacketChannelList[index]->firstPacket && - splitPacketChannelList[index]->splitPacketList.Size()!=splitPacketChannelList[index]->firstPacket->splitPacketCount && - (splitPacketChannelList[index]->splitPacketList.Size()%splitMessageProgressInterval)==0) - { - // Return ID_DOWNLOAD_PROGRESS - // Write splitPacketIndex (SplitPacketIndexType) - // Write splitPacketCount (SplitPacketIndexType) - // Write byteLength (4) - // Write data, splitPacketChannelList[index]->splitPacketList[0]->data - InternalPacket *progressIndicator = AllocateFromInternalPacketPool(); - unsigned int length = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + (unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); - AllocInternalPacketData(progressIndicator, length, false, __FILE__, __LINE__ ); - progressIndicator->dataBitLength=BYTES_TO_BITS(length); - progressIndicator->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; - unsigned int temp; - temp=splitPacketChannelList[index]->splitPacketList.Size(); - memcpy(progressIndicator->data+sizeof(MessageID), &temp, sizeof(unsigned int)); - temp=(unsigned int)internalPacket->splitPacketCount; - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*1, &temp, sizeof(unsigned int)); - temp=(unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*2, &temp, sizeof(unsigned int)); - - memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->firstPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength)); - outputQueue.Push(progressIndicator, __FILE__, __LINE__ ); - } - -#endif -} - -//------------------------------------------------------------------------------------------------------- -// Take all split chunks with the specified splitPacketId and try to -//reconstruct a packet. If we can, allocate and return it. Otherwise return 0 -// Optimized version -//------------------------------------------------------------------------------------------------------- -InternalPacket * ReliabilityLayer::BuildPacketFromSplitPacketList( SplitPacketChannel *splitPacketChannel, CCTimeType time ) -{ -#if PREALLOCATE_LARGE_MESSAGES==1 - InternalPacket *returnedPacket=splitPacketChannel->returnedPacket; - RakNet::OP_DELETE(splitPacketChannel, __FILE__, __LINE__); - (void) time; - return returnedPacket; -#else - unsigned int j; - InternalPacket * internalPacket, *splitPacket; - int splitPacketPartLength; - - // Reconstruct - internalPacket = CreateInternalPacketCopy( splitPacketChannel->splitPacketList[0], 0, 0, time ); - internalPacket->dataBitLength=0; - for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) - internalPacket->dataBitLength+=splitPacketChannel->splitPacketList[j]->dataBitLength; - splitPacketPartLength=BITS_TO_BYTES(splitPacketChannel->firstPacket->dataBitLength); - - internalPacket->data = (unsigned char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES( internalPacket->dataBitLength ), _FILE_AND_LINE_ ); - internalPacket->allocationScheme=InternalPacket::NORMAL; - - for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) - { - splitPacket=splitPacketChannel->splitPacketList[j]; - memcpy(internalPacket->data+splitPacket->splitPacketIndex*splitPacketPartLength, splitPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannel->splitPacketList[j]->dataBitLength)); - } - - for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) - { - FreeInternalPacketData(splitPacketChannel->splitPacketList[j], _FILE_AND_LINE_ ); - ReleaseToInternalPacketPool(splitPacketChannel->splitPacketList[j]); - } - RakNet::OP_DELETE(splitPacketChannel, __FILE__, __LINE__); - - return internalPacket; -#endif -} -//------------------------------------------------------------------------------------------------------- -InternalPacket * ReliabilityLayer::BuildPacketFromSplitPacketList( SplitPacketIdType splitPacketId, CCTimeType time, - SOCKET s, SystemAddress &systemAddress, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, - BitStream &updateBitStream) -{ - unsigned int i; - bool objectExists; - SplitPacketChannel *splitPacketChannel; - InternalPacket * internalPacket; - - i=splitPacketChannelList.GetIndexFromKey(splitPacketId, &objectExists); - splitPacketChannel=splitPacketChannelList[i]; - -#if PREALLOCATE_LARGE_MESSAGES==1 - if (splitPacketChannel->splitPacketsArrived==splitPacketChannel->returnedPacket->splitPacketCount) -#else - if (splitPacketChannel->splitPacketList.Size()==splitPacketChannel->splitPacketList[0]->splitPacketCount) -#endif - { - // Ack immediately, because for large files this can take a long time - SendACKs(s, systemAddress, time, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); - internalPacket=BuildPacketFromSplitPacketList(splitPacketChannel,time); - splitPacketChannelList.RemoveAtIndex(i); - return internalPacket; - } - else - { - return 0; - } -} -/* -//------------------------------------------------------------------------------------------------------- -// Delete any unreliable split packets that have long since expired -void ReliabilityLayer::DeleteOldUnreliableSplitPackets( CCTimeType time ) -{ -unsigned i,j; -i=0; -while (i < splitPacketChannelList.Size()) -{ -#if CC_TIME_TYPE_BYTES==4 -if (time > splitPacketChannelList[i]->lastUpdateTime + timeoutTime && -#else -if (time > splitPacketChannelList[i]->lastUpdateTime + (CCTimeType)timeoutTime*(CCTimeType)1000 && -#endif -(splitPacketChannelList[i]->splitPacketList[0]->reliability==UNRELIABLE || splitPacketChannelList[i]->splitPacketList[0]->reliability==UNRELIABLE_SEQUENCED)) -{ -for (j=0; j < splitPacketChannelList[i]->splitPacketList.Size(); j++) -{ -RakNet::OP_DELETE_ARRAY(splitPacketChannelList[i]->splitPacketList[j]->data, _FILE_AND_LINE_); -ReleaseToInternalPacketPool(splitPacketChannelList[i]->splitPacketList[j]); -} -RakNet::OP_DELETE(splitPacketChannelList[i], _FILE_AND_LINE_); -splitPacketChannelList.RemoveAtIndex(i); -} -else -i++; -} -} -*/ - -//------------------------------------------------------------------------------------------------------- -// Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. -// Does not copy any split data parameters as that information is always generated does not have any reason to be copied -//------------------------------------------------------------------------------------------------------- -InternalPacket * ReliabilityLayer::CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, CCTimeType time ) -{ - InternalPacket * copy = AllocateFromInternalPacketPool(); -#ifdef _DEBUG - // Remove accessing undefined memory error - memset( copy, 255, sizeof( InternalPacket ) ); -#endif - // Copy over our chunk of data - - if ( dataByteLength > 0 ) - { - AllocInternalPacketData(copy, BITS_TO_BYTES(dataByteLength ), false, _FILE_AND_LINE_ ); - memcpy( copy->data, original->data + dataByteOffset, dataByteLength ); - } - else - copy->data = 0; - - copy->dataBitLength = dataByteLength << 3; - copy->creationTime = time; - copy->nextActionTime = 0; - copy->orderingIndex = original->orderingIndex; - copy->sequencingIndex = original->sequencingIndex; - copy->orderingChannel = original->orderingChannel; - copy->reliableMessageNumber = original->reliableMessageNumber; - copy->priority = original->priority; - copy->reliability = original->reliability; -#if PREALLOCATE_LARGE_MESSAGES==1 - copy->splitPacketCount = original->splitPacketCount; - copy->splitPacketId = original->splitPacketId; - copy->splitPacketIndex = original->splitPacketIndex; -#endif - - return copy; -} - -//------------------------------------------------------------------------------------------------------- -// Get the specified ordering list -//------------------------------------------------------------------------------------------------------- -/* -DataStructures::LinkedList *ReliabilityLayer::GetOrderingListAtOrderingStream( unsigned char orderingChannel ) -{ - if ( orderingChannel >= orderingList.Size() ) - return 0; - - return orderingList[ orderingChannel ]; -} - -//------------------------------------------------------------------------------------------------------- -// Add the internal packet to the ordering list in order based on order index -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AddToOrderingList( InternalPacket * internalPacket ) -{ - } -*/ - -//------------------------------------------------------------------------------------------------------- -// Inserts a packet into the resend list in order -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::InsertPacketIntoResendList( InternalPacket *internalPacket, CCTimeType time, bool firstResend, bool modifyUnacknowledgedBytes ) -{ - (void) firstResend; - (void) time; - (void) internalPacket; - - AddToListTail(internalPacket, modifyUnacknowledgedBytes); - RakAssert(internalPacket->nextActionTime!=0); - -} - -//------------------------------------------------------------------------------------------------------- -// Were you ever unable to deliver a packet despite retries? -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsDeadConnection( void ) const -{ - return deadConnection; -} - -//------------------------------------------------------------------------------------------------------- -// Causes IsDeadConnection to return true -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::KillConnection( void ) -{ - deadConnection=true; -} - - -//------------------------------------------------------------------------------------------------------- -// Statistics -//------------------------------------------------------------------------------------------------------- -RakNetStatistics * ReliabilityLayer::GetStatistics( RakNetStatistics *rns ) -{ - unsigned i; - RakNet::TimeUS time = RakNet::GetTimeUS(); - uint64_t uint64Denominator; - double doubleDenominator; - - for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) - { - statistics.valueOverLastSecond[i]=bpsMetrics[i].GetBPS1Threadsafe(time); - statistics.runningTotal[i]=bpsMetrics[i].GetTotal1(); - } - - memcpy(rns, &statistics, sizeof(statistics)); - - if (rns->valueOverLastSecond[USER_MESSAGE_BYTES_SENT]+rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT]>0) - rns->packetlossLastSecond=(float)((double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT]/((double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_SENT]+(double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT])); - else - rns->packetlossLastSecond=0.0f; - - rns->packetlossTotal=0.0f; - uint64Denominator=(rns->runningTotal[USER_MESSAGE_BYTES_SENT]+rns->runningTotal[USER_MESSAGE_BYTES_RESENT]); - if (uint64Denominator!=0&&rns->runningTotal[USER_MESSAGE_BYTES_SENT]/uint64Denominator>0) - { - doubleDenominator=((double) rns->runningTotal[USER_MESSAGE_BYTES_SENT]+(double) rns->runningTotal[USER_MESSAGE_BYTES_RESENT]); - if(doubleDenominator!=0) - { - rns->packetlossTotal=(float)((double) rns->runningTotal[USER_MESSAGE_BYTES_RESENT]/doubleDenominator); - } - } - - rns->isLimitedByCongestionControl=statistics.isLimitedByCongestionControl; - rns->BPSLimitByCongestionControl=statistics.BPSLimitByCongestionControl; - rns->isLimitedByOutgoingBandwidthLimit=statistics.isLimitedByOutgoingBandwidthLimit; - rns->BPSLimitByOutgoingBandwidthLimit=statistics.BPSLimitByOutgoingBandwidthLimit; - - return rns; -} - -//------------------------------------------------------------------------------------------------------- -// Returns the number of packets in the resend queue, not counting holes -//------------------------------------------------------------------------------------------------------- -unsigned int ReliabilityLayer::GetResendListDataSize(void) const -{ - // Not accurate but thread-safe. The commented version might crash if the queue is cleared while we loop through it - // return resendTree.Size(); - return statistics.messagesInResendBuffer; -} - -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::AckTimeout(RakNet::Time curTime) -{ - // I check timeLastDatagramArrived-curTime because with threading it is possible that timeLastDatagramArrived is - // slightly greater than curTime, in which case this is NOT an ack timeout - return (timeLastDatagramArrived-curTime)>10000 && curTime-timeLastDatagramArrived>timeoutTime; -} -//------------------------------------------------------------------------------------------------------- -CCTimeType ReliabilityLayer::GetNextSendTime(void) const -{ - return nextSendTime; -} -//------------------------------------------------------------------------------------------------------- -CCTimeType ReliabilityLayer::GetTimeBetweenPackets(void) const -{ - return timeBetweenPackets; -} -//------------------------------------------------------------------------------------------------------- -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 -CCTimeType ReliabilityLayer::GetAckPing(void) const -{ - return ackPing; -} -#endif -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ResetPacketsAndDatagrams(void) -{ - packetsToSendThisUpdate.Clear(true, _FILE_AND_LINE_); - packetsToDeallocThisUpdate.Clear(true, _FILE_AND_LINE_); - packetsToSendThisUpdateDatagramBoundaries.Clear(true, _FILE_AND_LINE_); - datagramsToSendThisUpdateIsPair.Clear(true, _FILE_AND_LINE_); - datagramSizesInBytes.Clear(true, _FILE_AND_LINE_); - datagramSizeSoFar=0; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::PushPacket(CCTimeType time, InternalPacket *internalPacket, bool isReliable) -{ - BitSize_t bitsForThisPacket=BYTES_TO_BITS(BITS_TO_BYTES(internalPacket->dataBitLength)+BITS_TO_BYTES(internalPacket->headerLength)); - datagramSizeSoFar+=bitsForThisPacket; - RakAssert(BITS_TO_BYTES(datagramSizeSoFar)headerLength==GetMessageHeaderLengthBits(internalPacket)); - -// This code tells me how much time elapses between when you send, and when the message actually goes out -// if (internalPacket->data[0]==0) -// { -// RakNet::TimeMS t; -// RakNet::BitStream bs(internalPacket->data+1,sizeof(t),false); -// bs.Read(t); -// RakNet::TimeMS curTime=RakNet::GetTimeMS(); -// RakNet::TimeMS diff = curTime-t; -// } - - congestionManager.OnSendBytes(time, BITS_TO_BYTES(internalPacket->dataBitLength)+BITS_TO_BYTES(internalPacket->headerLength)); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::PushDatagram(void) -{ - if (datagramSizeSoFar>0) - { - packetsToSendThisUpdateDatagramBoundaries.Push(packetsToSendThisUpdate.Size(), _FILE_AND_LINE_ ); - datagramsToSendThisUpdateIsPair.Push(false, _FILE_AND_LINE_ ); - RakAssert(BITS_TO_BYTES(datagramSizeSoFar)=2) - { - datagramsToSendThisUpdateIsPair[datagramsToSendThisUpdateIsPair.Size()-2]=true; - datagramsToSendThisUpdateIsPair[datagramsToSendThisUpdateIsPair.Size()-1]=true; - return true; - } - return false; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ClearPacketsAndDatagrams(void) -{ - unsigned int i; - for (i=0; i < packetsToDeallocThisUpdate.Size(); i++) - { - // packetsToDeallocThisUpdate holds a boolean indicating if packetsToSendThisUpdate at this index should be freed - if (packetsToDeallocThisUpdate[i]) - { - RemoveFromUnreliableLinkedList(packetsToSendThisUpdate[i]); - FreeInternalPacketData(packetsToSendThisUpdate[i], _FILE_AND_LINE_ ); - // if (keepInternalPacketIfNeedsAck==false || packetsToSendThisUpdate[i]->reliabilityresendNext=internalPacket; - internalPacket->resendPrev=internalPacket; - resendLinkedListHead=internalPacket; - return; - } - internalPacket->resendPrev->resendNext = internalPacket->resendNext; - internalPacket->resendNext->resendPrev = internalPacket->resendPrev; - internalPacket->resendNext=resendLinkedListHead; - internalPacket->resendPrev=resendLinkedListHead->resendPrev; - internalPacket->resendPrev->resendNext=internalPacket; - resendLinkedListHead->resendPrev=internalPacket; - resendLinkedListHead=internalPacket; - RakAssert(internalPacket->headerLength+internalPacket->dataBitLength>0); - - //ValidateResendList(); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::RemoveFromList(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes) -{ - InternalPacket *newPosition; - internalPacket->resendPrev->resendNext = internalPacket->resendNext; - internalPacket->resendNext->resendPrev = internalPacket->resendPrev; - newPosition = internalPacket->resendNext; - if ( internalPacket == resendLinkedListHead ) - resendLinkedListHead = newPosition; - if (resendLinkedListHead==internalPacket) - resendLinkedListHead=0; - - if (modifyUnacknowledgedBytes) - { - RakAssert(unacknowledgedBytes>=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength)); - unacknowledgedBytes-=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); - // printf("-unacknowledgedBytes:%i ", unacknowledgedBytes); - - -// ValidateResendList(); - } -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AddToListTail(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes) -{ - if (modifyUnacknowledgedBytes) - { - unacknowledgedBytes+=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); - // printf("+unacknowledgedBytes:%i ", unacknowledgedBytes); - } - - if (resendLinkedListHead==0) - { - internalPacket->resendNext=internalPacket; - internalPacket->resendPrev=internalPacket; - resendLinkedListHead=internalPacket; - return; - } - internalPacket->resendNext=resendLinkedListHead; - internalPacket->resendPrev=resendLinkedListHead->resendPrev; - internalPacket->resendPrev->resendNext=internalPacket; - resendLinkedListHead->resendPrev=internalPacket; - -// ValidateResendList(); - -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::PopListHead(bool modifyUnacknowledgedBytes) -{ - RakAssert(resendLinkedListHead!=0); - RemoveFromList(resendLinkedListHead, modifyUnacknowledgedBytes); -} -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::IsResendQueueEmpty(void) const -{ - return resendLinkedListHead==0; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::SendACKs(SOCKET s, SystemAddress &systemAddress, CCTimeType time, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream) -{ - BitSize_t maxDatagramPayload = GetMaxDatagramSizeExcludingMessageHeaderBits(); - - while (acknowlegements.Size()>0) - { - // Send acks - updateBitStream.Reset(); - DatagramHeaderFormat dhf; - dhf.isACK=true; - dhf.isNAK=false; - dhf.isPacketPair=false; -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - dhf.sourceSystemTime=time; -#endif - double B; - double AS; - bool hasBAndAS; - if (remoteSystemNeedsBAndAS) - { - congestionManager.OnSendAckGetBAndAS(time, &hasBAndAS,&B,&AS); - dhf.AS=(float)AS; - dhf.hasBAndAS=hasBAndAS; - } - else - dhf.hasBAndAS=false; -#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 - dhf.sourceSystemTime=nextAckTimeToSend; -#endif - // dhf.B=(float)B; - updateBitStream.Reset(); - dhf.Serialize(&updateBitStream); - CC_DEBUG_PRINTF_1("AckSnd "); - acknowlegements.Serialize(&updateBitStream, maxDatagramPayload, true); - SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); - congestionManager.OnSendAck(time,updateBitStream.GetNumberOfBytesUsed()); - - // I think this is causing a bug where if the estimated bandwidth is very low for the recipient, only acks ever get sent - // congestionManager.OnSendBytes(time,UDP_HEADER_SIZE+updateBitStream.GetNumberOfBytesUsed()); - } -} -/* -//------------------------------------------------------------------------------------------------------- -ReliabilityLayer::DatagramMessageIDList* ReliabilityLayer::AllocateFromDatagramMessageIDPool(void) -{ -DatagramMessageIDList*s; -s=datagramMessageIDPool.Allocate( _FILE_AND_LINE_ ); -// Call new operator, memoryPool doesn't do this -s = new ((void*)s) DatagramMessageIDList; -return s; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ReleaseToDatagramMessageIDPool(DatagramMessageIDList* d) -{ -d->~DatagramMessageIDList(); -datagramMessageIDPool.Release(d); -} -*/ -//------------------------------------------------------------------------------------------------------- -InternalPacket* ReliabilityLayer::AllocateFromInternalPacketPool(void) -{ - InternalPacket *ip = internalPacketPool.Allocate( _FILE_AND_LINE_ ); - ip->reliableMessageNumber = (MessageNumberType) (const uint32_t)-1; - ip->messageNumberAssigned=false; - ip->nextActionTime = 0; - ip->splitPacketCount = 0; - ip->allocationScheme=InternalPacket::NORMAL; - ip->data=0; - return ip; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ReleaseToInternalPacketPool(InternalPacket *ip) -{ - internalPacketPool.Release(ip, _FILE_AND_LINE_); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::RemoveFromUnreliableLinkedList(InternalPacket *internalPacket) -{ - if (internalPacket->reliability==UNRELIABLE || - internalPacket->reliability==UNRELIABLE_SEQUENCED || - internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT -// || -// internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - { - InternalPacket *newPosition; - internalPacket->unreliablePrev->unreliableNext = internalPacket->unreliableNext; - internalPacket->unreliableNext->unreliablePrev = internalPacket->unreliablePrev; - newPosition = internalPacket->unreliableNext; - if ( internalPacket == unreliableLinkedListHead ) - unreliableLinkedListHead = newPosition; - if (unreliableLinkedListHead==internalPacket) - unreliableLinkedListHead=0; - } -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AddToUnreliableLinkedList(InternalPacket *internalPacket) -{ - if (internalPacket->reliability==UNRELIABLE || - internalPacket->reliability==UNRELIABLE_SEQUENCED || - internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT -// || -// internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - { - if (unreliableLinkedListHead==0) - { - internalPacket->unreliableNext=internalPacket; - internalPacket->unreliablePrev=internalPacket; - unreliableLinkedListHead=internalPacket; - return; - } - internalPacket->unreliableNext=unreliableLinkedListHead; - internalPacket->unreliablePrev=unreliableLinkedListHead->unreliablePrev; - internalPacket->unreliablePrev->unreliableNext=internalPacket; - unreliableLinkedListHead->unreliablePrev=internalPacket; - } -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::ValidateResendList(void) const -{ -// unsigned int count1=0, count2=0; -// for (unsigned int i=0; i < RESEND_BUFFER_ARRAY_LENGTH; i++) -// if (resendBuffer[i]) -// count1++; -// -// if (resendLinkedListHead) -// { -// InternalPacket *internalPacket = resendLinkedListHead; -// do -// { -// count2++; -// internalPacket=internalPacket->resendNext; -// } while (internalPacket!=resendLinkedListHead); -// } -// RakAssert(count1==count2); -// RakAssert(count2<=RESEND_BUFFER_ARRAY_LENGTH); -} -//------------------------------------------------------------------------------------------------------- -bool ReliabilityLayer::ResendBufferOverflow(void) const -{ - int index1 = sendReliableMessageNumberIndex & (uint32_t) RESEND_BUFFER_ARRAY_MASK; - // int index2 = (sendReliableMessageNumberIndex+(uint32_t)1) & (uint32_t) RESEND_BUFFER_ARRAY_MASK; - RakAssert(index1= datagramHistory.Size()) - return 0; - - *timeSent=datagramHistory[offsetIntoList].timeSent; - return datagramHistory[offsetIntoList].head; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::RemoveFromDatagramHistory(DatagramSequenceNumberType index) -{ - DatagramSequenceNumberType offsetIntoList = index - datagramHistoryPopCount; - MessageNumberNode *mnm = datagramHistory[offsetIntoList].head; - MessageNumberNode *next; - while (mnm) - { - next=mnm->next; - datagramHistoryMessagePool.Release(mnm, _FILE_AND_LINE_); - mnm=next; - } - datagramHistory[offsetIntoList].head=0; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, CCTimeType timeSent) -{ - (void) datagramNumber; - if (datagramHistory.Size()>DATAGRAM_MESSAGE_ID_ARRAY_LENGTH) - { - RemoveFromDatagramHistory(datagramHistoryPopCount); - datagramHistory.Pop(); - datagramHistoryPopCount++; - } - - datagramHistory.Push(DatagramHistoryNode(0, timeSent), _FILE_AND_LINE_); - // printf("%p Pushed empty DatagramHistoryNode to datagram history at index %i\n", this, datagramHistory.Size()-1); -} -//------------------------------------------------------------------------------------------------------- -ReliabilityLayer::MessageNumberNode* ReliabilityLayer::AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, DatagramSequenceNumberType messageNumber, CCTimeType timeSent) -{ - (void) datagramNumber; -// RakAssert(datagramHistoryPopCount+(unsigned int) datagramHistory.Size()==datagramNumber); - if (datagramHistory.Size()>DATAGRAM_MESSAGE_ID_ARRAY_LENGTH) - { - RemoveFromDatagramHistory(datagramHistoryPopCount); - datagramHistory.Pop(); - datagramHistoryPopCount++; - } - - MessageNumberNode *mnm = datagramHistoryMessagePool.Allocate(_FILE_AND_LINE_); - mnm->next=0; - mnm->messageNumber=messageNumber; - datagramHistory.Push(DatagramHistoryNode(mnm, timeSent), _FILE_AND_LINE_); - // printf("%p Pushed message %i to DatagramHistoryNode to datagram history at index %i\n", this, messageNumber.val, datagramHistory.Size()-1); - return mnm; -} -//------------------------------------------------------------------------------------------------------- -ReliabilityLayer::MessageNumberNode* ReliabilityLayer::AddSubsequentToDatagramHistory(MessageNumberNode *messageNumberNode, DatagramSequenceNumberType messageNumber) -{ - messageNumberNode->next=datagramHistoryMessagePool.Allocate(_FILE_AND_LINE_); - messageNumberNode->next->messageNumber=messageNumber; - messageNumberNode->next->next=0; - return messageNumberNode->next; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, InternalPacketRefCountedData **refCounter, unsigned char *externallyAllocatedPtr, unsigned char *ourOffset) -{ - internalPacket->allocationScheme=InternalPacket::REF_COUNTED; - internalPacket->data=ourOffset; - if (*refCounter==0) - { - *refCounter = refCountedDataPool.Allocate(_FILE_AND_LINE_); - // *refCounter = RakNet::OP_NEW(_FILE_AND_LINE_); - (*refCounter)->refCount=1; - (*refCounter)->sharedDataBlock=externallyAllocatedPtr; - } - else - (*refCounter)->refCount++; - internalPacket->refCountedData=(*refCounter); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, unsigned char *externallyAllocatedPtr) -{ - internalPacket->allocationScheme=InternalPacket::NORMAL; - internalPacket->data=externallyAllocatedPtr; -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, unsigned int numBytes, bool allowStack, const char *file, unsigned int line) -{ - if (allowStack && numBytes <= sizeof(internalPacket->stackData)) - { - internalPacket->allocationScheme=InternalPacket::STACK; - internalPacket->data=internalPacket->stackData; - } - else - { - internalPacket->allocationScheme=InternalPacket::NORMAL; - internalPacket->data=(unsigned char*) rakMalloc_Ex(numBytes,file,line); - } -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::FreeInternalPacketData(InternalPacket *internalPacket, const char *file, unsigned int line) -{ - if (internalPacket==0) - return; - - if (internalPacket->allocationScheme==InternalPacket::REF_COUNTED) - { - if (internalPacket->refCountedData==0) - return; - - internalPacket->refCountedData->refCount--; - if (internalPacket->refCountedData->refCount==0) - { - rakFree_Ex(internalPacket->refCountedData->sharedDataBlock, file, line ); - internalPacket->refCountedData->sharedDataBlock=0; - // RakNet::OP_DELETE(internalPacket->refCountedData,file, line); - refCountedDataPool.Release(internalPacket->refCountedData,file, line); - internalPacket->refCountedData=0; - } - } - else if (internalPacket->allocationScheme==InternalPacket::NORMAL) - { - if (internalPacket->data==0) - return; - - rakFree_Ex(internalPacket->data, file, line ); - internalPacket->data=0; - } - else - { - // Data was on stack - internalPacket->data=0; - } -} -//------------------------------------------------------------------------------------------------------- -unsigned int ReliabilityLayer::GetMaxDatagramSizeExcludingMessageHeaderBytes(void) -{ - unsigned int val = congestionManager.GetMTU() - DatagramHeaderFormat::GetDataHeaderByteLength(); - -#if LIBCAT_SECURITY==1 - if (useSecurity) - val -= cat::AuthenticatedEncryption::OVERHEAD_BYTES; -#endif - - return val; -} -//------------------------------------------------------------------------------------------------------- -BitSize_t ReliabilityLayer::GetMaxDatagramSizeExcludingMessageHeaderBits(void) -{ - return BYTES_TO_BITS(GetMaxDatagramSizeExcludingMessageHeaderBytes()); -} -//------------------------------------------------------------------------------------------------------- -void ReliabilityLayer::InitHeapWeights(void) -{ - for (int priorityLevel=0; priorityLevel < NUMBER_OF_PRIORITIES; priorityLevel++) - outgoingPacketBufferNextWeights[priorityLevel]=(1<0) - { - int peekPL = outgoingPacketBuffer.Peek()->priority; - reliabilityHeapWeightType weight = outgoingPacketBuffer.PeekWeight(); - reliabilityHeapWeightType min = weight - (1< + +using namespace RakNet; + +// Can't figure out which library has this function on the PS3 +double Ceil(double d) {if (((double)((int)d))==d) return d; return (int) (d+1.0);} + +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RELIABILITY_LAYER_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + + +//#define _DEBUG_LOGGER + +#if CC_TIME_TYPE_BYTES==4 +static const CCTimeType MAX_TIME_BETWEEN_PACKETS= 350; // 350 milliseconds +static const CCTimeType HISTOGRAM_RESTART_CYCLE=10000; // Every 10 seconds reset the histogram +#else +static const CCTimeType MAX_TIME_BETWEEN_PACKETS= 350000; // 350 milliseconds +//static const CCTimeType HISTOGRAM_RESTART_CYCLE=10000000; // Every 10 seconds reset the histogram +#endif +static const int DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE=512; +static const CCTimeType STARTING_TIME_BETWEEN_PACKETS=MAX_TIME_BETWEEN_PACKETS; +//static const long double TIME_BETWEEN_PACKETS_INCREASE_MULTIPLIER_DEFAULT=.02; +//static const long double TIME_BETWEEN_PACKETS_DECREASE_MULTIPLIER_DEFAULT=1.0 / 9.0; + +typedef uint32_t BitstreamLengthEncoding; + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +//#define PRINT_TO_FILE_ROT +#ifdef PRINT_TO_FILE_ROT +static FILE *fp=0; +#endif + +BPSTracker::TimeAndValue2::TimeAndValue2() {} +BPSTracker::TimeAndValue2::~TimeAndValue2() {} +BPSTracker::TimeAndValue2::TimeAndValue2(RakNet::TimeUS t, uint64_t v1) : value1(v1), time(t) {} +//BPSTracker::TimeAndValue2::TimeAndValue2(RakNet::TimeUS t, uint64_t v1, uint64_t v2) : time(t), value1(v1), value2(v2) {} +BPSTracker::BPSTracker() {Reset(_FILE_AND_LINE_);} +BPSTracker::~BPSTracker() {} +//void BPSTracker::Reset(const char *file, unsigned int line) {total1=total2=lastSec1=lastSec2=0; dataQueue.Clear(file,line);} +void BPSTracker::Reset(const char *file, unsigned int line) {total1=lastSec1=0; dataQueue.Clear(file,line);} +//void BPSTracker::Push2(RakNetTimeUS time, uint64_t value1, uint64_t value2) {dataQueue.Push(TimeAndValue2(time,value1,value2),_FILE_AND_LINE_); total1+=value1; lastSec1+=value1; total2+=value2; lastSec2+=value2;} +//uint64_t BPSTracker::GetBPS2(RakNetTimeUS time) {ClearExpired2(time); return lastSec2;} +//void BPSTracker::GetBPS1And2(RakNetTimeUS time, uint64_t &out1, uint64_t &out2) {ClearExpired2(time); out1=lastSec1; out2=lastSec2;} +uint64_t BPSTracker::GetTotal1(void) const {return total1;} +//uint64_t BPSTracker::GetTotal2(void) const {return total2;} + +// void BPSTracker::ClearExpired2(RakNet::TimeUS time) { +// RakNet::TimeUS threshold=time; +// if (threshold < 1000000) +// return; +// threshold-=1000000; +// while (dataQueue.IsEmpty()==false && dataQueue.Peek().time < threshold) +// { +// lastSec1-=dataQueue.Peek().value1; +// lastSec2-=dataQueue.Peek().value2; +// dataQueue.Pop(); +// } +// } +void BPSTracker::ClearExpired1(RakNet::TimeUS time) +{ + while (dataQueue.IsEmpty()==false && +#if CC_TIME_TYPE_BYTES==8 + dataQueue.Peek().time+1000000 < time +#else + dataQueue.Peek().time+1000 < time +#endif + ) + { + lastSec1-=dataQueue.Peek().value1; + dataQueue.Pop(); + } +} + +struct DatagramHeaderFormat +{ +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + CCTimeType sourceSystemTime; +#endif + DatagramSequenceNumberType datagramNumber; + + // Use floats to save bandwidth + // float B; // Link capacity + float AS; // Data arrival rate + bool isACK; + bool isNAK; + bool isPacketPair; + bool hasBAndAS; + bool isContinuousSend; + bool needsBAndAs; + bool isValid; // To differentiate between what I serialized, and offline data + + static BitSize_t GetDataHeaderBitLength() + { + return BYTES_TO_BITS(GetDataHeaderByteLength()); + } + + static unsigned int GetDataHeaderByteLength() + { + //return 2 + 3 + sizeof(RakNet::TimeMS) + sizeof(float)*2; + return 2 + 3 + +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + sizeof(RakNetTimeMS) + +#endif + sizeof(float)*1; + } + + void Serialize(RakNet::BitStream *b) + { + // Not endian safe + // RakAssert(GetDataHeaderByteLength()==sizeof(DatagramHeaderFormat)); + // b->WriteAlignedBytes((const unsigned char*) this, sizeof(DatagramHeaderFormat)); + // return; + + b->Write(true); // IsValid + if (isACK) + { + b->Write(true); + b->Write(hasBAndAS); + b->AlignWriteToByteBoundary(); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMSLow=(RakNet::TimeMS) sourceSystemTime&0xFFFFFFFF; b->Write(timeMSLow); +#endif + if (hasBAndAS) + { + // b->Write(B); + b->Write(AS); + } + } + else if (isNAK) + { + b->Write(false); + b->Write(true); + } + else + { + b->Write(false); + b->Write(false); + b->Write(isPacketPair); + b->Write(isContinuousSend); + b->Write(needsBAndAs); + b->AlignWriteToByteBoundary(); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMSLow=(RakNet::TimeMS) sourceSystemTime&0xFFFFFFFF; b->Write(timeMSLow); +#endif + b->Write(datagramNumber); + } + } + void Deserialize(RakNet::BitStream *b) + { + // Not endian safe + // b->ReadAlignedBytes((unsigned char*) this, sizeof(DatagramHeaderFormat)); + // return; + + b->Read(isValid); + b->Read(isACK); + if (isACK) + { + isNAK=false; + isPacketPair=false; + b->Read(hasBAndAS); + b->AlignReadToByteBoundary(); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMS; b->Read(timeMS); sourceSystemTime=(CCTimeType) timeMS; +#endif + if (hasBAndAS) + { + // b->Read(B); + b->Read(AS); + } + } + else + { + b->Read(isNAK); + if (isNAK) + { + isPacketPair=false; + } + else + { + b->Read(isPacketPair); + b->Read(isContinuousSend); + b->Read(needsBAndAs); + b->AlignReadToByteBoundary(); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMS; b->Read(timeMS); sourceSystemTime=(CCTimeType) timeMS; +#endif + b->Read(datagramNumber); + } + } + } +}; + +#if !defined(__GNUC__) && !defined(__ARMCC) +#pragma warning(disable:4702) // unreachable code +#endif + +#ifdef _WIN32 +//#define _DEBUG_LOGGER +#ifdef _DEBUG_LOGGER +#include "WindowsIncludes.h" +#endif +#endif + +//#define DEBUG_SPLIT_PACKET_PROBLEMS +#if defined (DEBUG_SPLIT_PACKET_PROBLEMS) +static int waitFlag=-1; +#endif + +using namespace RakNet; + +int RakNet::SplitPacketChannelComp( SplitPacketIdType const &key, SplitPacketChannel* const &data ) +{ +#if PREALLOCATE_LARGE_MESSAGES==1 + if (key < data->returnedPacket->splitPacketId) + return -1; + if (key == data->returnedPacket->splitPacketId) + return 0; +#else + if (key < data->splitPacketList[0]->splitPacketId) + return -1; + if (key == data->splitPacketList[0]->splitPacketId) + return 0; +#endif + return 1; +} + +// DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS( InternalPacket, SplitPacketIndexType, splitPacketIndex ) +/* +bool operator<( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get() < cls->splitPacketIndex; +} +bool operator>( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get() > cls->splitPacketIndex; +} +bool operator==( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get() == cls->splitPacketIndex; +} +/// Semi-hack: This is necessary to call Sort() +bool operator<( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get()->splitPacketIndex < cls->splitPacketIndex; +} +bool operator>( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get()->splitPacketIndex > cls->splitPacketIndex; +} +bool operator==( const DataStructures::MLKeyRef &inputKey, const InternalPacket *cls ) +{ + return inputKey.Get()->splitPacketIndex == cls->splitPacketIndex; +} +*/ + +int SplitPacketIndexComp( SplitPacketIndexType const &key, InternalPacket* const &data ) +{ +if (key < data->splitPacketIndex) +return -1; +if (key == data->splitPacketIndex) +return 0; +return 1; +} + +//------------------------------------------------------------------------------------------------------- +// Constructor +//------------------------------------------------------------------------------------------------------- +// Add 21 to the default MTU so if we encrypt it can hold potentially 21 more bytes of extra data + padding. +ReliabilityLayer::ReliabilityLayer() +{ + +#ifdef _DEBUG + // Wait longer to disconnect in debug so I don't get disconnected while tracing + timeoutTime=30000; +#else + timeoutTime=10000; +#endif + +#ifdef _DEBUG + minExtraPing=extraPingVariance=0; + packetloss=(double) minExtraPing; +#endif + + +#ifdef PRINT_TO_FILE_ROT + if (fp==0 && 0) + { + fp = fopen("reliableorderedoutput.txt", "wt"); + } +#endif + + InitializeVariables(); +//int i = sizeof(InternalPacket); + datagramHistoryMessagePool.SetPageSize(sizeof(MessageNumberNode)*128); + internalPacketPool.SetPageSize(sizeof(InternalPacket)*INTERNAL_PACKET_PAGE_SIZE); + refCountedDataPool.SetPageSize(sizeof(InternalPacketRefCountedData)*32); +} + +//------------------------------------------------------------------------------------------------------- +// Destructor +//------------------------------------------------------------------------------------------------------- +ReliabilityLayer::~ReliabilityLayer() +{ + FreeMemory( true ); // Free all memory immediately +} +//------------------------------------------------------------------------------------------------------- +// Resets the layer for reuse +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::Reset( bool resetVariables, int MTUSize, bool _useSecurity ) +{ + + FreeMemory( true ); // true because making a memory reset pending in the update cycle causes resets after reconnects. Instead, just call Reset from a single thread + if (resetVariables) + { + InitializeVariables(); + +#if LIBCAT_SECURITY==1 + useSecurity = _useSecurity; + + if (_useSecurity) + MTUSize -= cat::AuthenticatedEncryption::OVERHEAD_BYTES; +#else + (void) _useSecurity; +#endif // LIBCAT_SECURITY + congestionManager.Init(RakNet::GetTimeUS(), MTUSize - UDP_HEADER_SIZE); + } +} + +//------------------------------------------------------------------------------------------------------- +// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SetTimeoutTime( RakNet::TimeMS time ) +{ + timeoutTime=time; +} + +//------------------------------------------------------------------------------------------------------- +// Returns the value passed to SetTimeoutTime. or the default if it was never called +//------------------------------------------------------------------------------------------------------- +RakNet::TimeMS ReliabilityLayer::GetTimeoutTime(void) +{ + return timeoutTime; +} + +//------------------------------------------------------------------------------------------------------- +// Initialize the variables +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::InitializeVariables( void ) +{ + memset( orderedWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType)); + memset( sequencedWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); + memset( orderedReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); + memset( highestSequencedReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); + memset( &statistics, 0, sizeof( statistics ) ); + memset( &heapIndexOffsets, 0, sizeof( heapIndexOffsets ) ); + + statistics.connectionStartTime = RakNet::GetTimeUS(); + splitPacketId = 0; + elapsedTimeSinceLastUpdate=0; + throughputCapCountdown=0; + sendReliableMessageNumberIndex = 0; + internalOrderIndex=0; + timeToNextUnreliableCull=0; + unreliableLinkedListHead=0; + lastUpdateTime= RakNet::GetTimeUS(); + bandwidthExceededStatistic=false; + remoteSystemTime=0; + unreliableTimeout=0; + lastBpsClear=0; + + // Disable packet pairs + countdownToNextPacketPair=15; + + nextAllowedThroughputSample=0; + deadConnection = cheater = false; + timeOfLastContinualSend=0; + + // timeResendQueueNonEmpty = 0; + timeLastDatagramArrived=RakNet::GetTimeMS(); + // packetlossThisSample=false; + // backoffThisSample=0; + // packetlossThisSampleResendCount=0; + // lastPacketlossTime=0; + statistics.messagesInResendBuffer=0; + statistics.bytesInResendBuffer=0; + + receivedPacketsBaseIndex=0; + resetReceivedPackets=true; + receivePacketCount=0; + + // SetPing( 1000 ); + + timeBetweenPackets=STARTING_TIME_BETWEEN_PACKETS; + + ackPingIndex=0; + ackPingSum=(CCTimeType)0; + + nextSendTime=lastUpdateTime; + //nextLowestPingReset=(CCTimeType)0; + // continuousSend=false; + + // histogramStart=(CCTimeType)0; + // histogramBitsSent=0; + unacknowledgedBytes=0; + resendLinkedListHead=0; + totalUserDataBytesAcked=0; + + datagramHistoryPopCount=0; + + InitHeapWeights(); + for (int i=0; i < NUMBER_OF_PRIORITIES; i++) + { + statistics.messageInSendBuffer[i]=0; + statistics.bytesInSendBuffer[i]=0.0; + } + + for (int i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) + { + bpsMetrics[i].Reset(_FILE_AND_LINE_); + } +} + +//------------------------------------------------------------------------------------------------------- +// Frees all allocated memory +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::FreeMemory( bool freeAllImmediately ) +{ + (void) freeAllImmediately; + FreeThreadSafeMemory(); +} + +void ReliabilityLayer::FreeThreadSafeMemory( void ) +{ + unsigned i,j; + InternalPacket *internalPacket; + + ClearPacketsAndDatagrams(); + + for (i=0; i < splitPacketChannelList.Size(); i++) + { + for (j=0; j < splitPacketChannelList[i]->splitPacketList.Size(); j++) + { + FreeInternalPacketData(splitPacketChannelList[i]->splitPacketList[j], _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( splitPacketChannelList[i]->splitPacketList[j] ); + } +#if PREALLOCATE_LARGE_MESSAGES==1 + if (splitPacketChannelList[i]->returnedPacket) + { + FreeInternalPacketData(splitPacketChannelList[i]->returnedPacket, __FILE__, __LINE__ ); + ReleaseToInternalPacketPool( splitPacketChannelList[i]->returnedPacket ); + } +#endif + RakNet::OP_DELETE(splitPacketChannelList[i], __FILE__, __LINE__); + } + splitPacketChannelList.Clear(false, _FILE_AND_LINE_); + + while ( outputQueue.Size() > 0 ) + { + internalPacket = outputQueue.Pop(); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + } + + outputQueue.ClearAndForceAllocation( 32, _FILE_AND_LINE_ ); + + /* + for ( i = 0; i < orderingList.Size(); i++ ) + { + if ( orderingList[ i ] ) + { + DataStructures::LinkedList* theList = orderingList[ i ]; + + if ( theList ) + { + while ( theList->Size() ) + { + internalPacket = orderingList[ i ]->Pop(); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + } + + RakNet::OP_DELETE(theList, _FILE_AND_LINE_); + } + } + } + + orderingList.Clear(false, _FILE_AND_LINE_); + */ + + for (i=0; i < NUMBER_OF_ORDERED_STREAMS; i++) + { + for (j=0; j < orderingHeaps[i].Size(); j++) + { + FreeInternalPacketData(orderingHeaps[i][j], _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( orderingHeaps[i][j] ); + } + orderingHeaps[i].Clear(true, _FILE_AND_LINE_); + } + + //resendList.ForEachData(DeleteInternalPacket); + // resendTree.Clear(_FILE_AND_LINE_); + memset(resendBuffer, 0, sizeof(resendBuffer)); + statistics.messagesInResendBuffer=0; + statistics.bytesInResendBuffer=0; + + if (resendLinkedListHead) + { + InternalPacket *prev; + InternalPacket *iter = resendLinkedListHead; + while (1) + { + if (iter->data) + FreeInternalPacketData(iter, _FILE_AND_LINE_ ); + prev=iter; + iter=iter->resendNext; + if (iter==resendLinkedListHead) + { + ReleaseToInternalPacketPool(prev); + break; + } + ReleaseToInternalPacketPool(prev); + } + resendLinkedListHead=0; + } + unacknowledgedBytes=0; + + // acknowlegements.Clear(_FILE_AND_LINE_); + + for ( j=0 ; j < outgoingPacketBuffer.Size(); j++ ) + { + if ( outgoingPacketBuffer[ j ]->data) + FreeInternalPacketData( outgoingPacketBuffer[ j ], _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( outgoingPacketBuffer[ j ] ); + } + + outgoingPacketBuffer.Clear(true, _FILE_AND_LINE_); + +#ifdef _DEBUG + for (unsigned i = 0; i < delayList.Size(); i++ ) + RakNet::OP_DELETE(delayList[ i ], __FILE__, __LINE__); + delayList.Clear(__FILE__, __LINE__); +#endif + + unreliableWithAckReceiptHistory.Clear(false, _FILE_AND_LINE_); + + packetsToSendThisUpdate.Clear(false, _FILE_AND_LINE_); + packetsToSendThisUpdate.Preallocate(512, _FILE_AND_LINE_); + packetsToDeallocThisUpdate.Clear(false, _FILE_AND_LINE_); + packetsToDeallocThisUpdate.Preallocate(512, _FILE_AND_LINE_); + packetsToSendThisUpdateDatagramBoundaries.Clear(false, _FILE_AND_LINE_); + packetsToSendThisUpdateDatagramBoundaries.Preallocate(128, _FILE_AND_LINE_); + datagramSizesInBytes.Clear(false, _FILE_AND_LINE_); + datagramSizesInBytes.Preallocate(128, _FILE_AND_LINE_); + + internalPacketPool.Clear(_FILE_AND_LINE_); + + refCountedDataPool.Clear(_FILE_AND_LINE_); + + /* + DataStructures::Page *cur = datagramMessageIDTree.GetListHead(); + while (cur) + { + int treeIndex; + for (treeIndex=0; treeIndex < cur->size; treeIndex++) + ReleaseToDatagramMessageIDPool(cur->data[treeIndex]); + cur=cur->resendNext; + } + datagramMessageIDTree.Clear(_FILE_AND_LINE_); + datagramMessageIDPool.Clear(_FILE_AND_LINE_); + */ + + while (datagramHistory.Size()) + { + RemoveFromDatagramHistory(datagramHistoryPopCount); + datagramHistory.Pop(); + datagramHistoryPopCount++; + } + datagramHistoryMessagePool.Clear(_FILE_AND_LINE_); + datagramHistoryPopCount=0; + + acknowlegements.Clear(); + NAKs.Clear(); + + unreliableLinkedListHead=0; +} + +//------------------------------------------------------------------------------------------------------- +// Packets are read directly from the socket layer and skip the reliability +//layer because unconnected players do not use the reliability layer +// This function takes packet data after a player has been confirmed as +//connected. The game should not use that data directly +// because some data is used internally, such as packet acknowledgment and +//split packets +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::HandleSocketReceiveFromConnectedPlayer( + const char *buffer, unsigned int length, SystemAddress &systemAddress, DataStructures::List &messageHandlerList, int MTUSize, + SOCKET s, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType timeRead, + BitStream &updateBitStream) +{ +#ifdef _DEBUG + RakAssert( !( buffer == 0 ) ); +#endif + +#if CC_TIME_TYPE_BYTES==4 + timeRead/=1000; +#endif + + + bpsMetrics[(int) ACTUAL_BYTES_RECEIVED].Push1(timeRead,length); + + (void) MTUSize; + + if ( length <= 2 || buffer == 0 ) // Length of 1 is a connection request resend that we just ignore + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("length <= 2 || buffer == 0", BYTES_TO_BITS(length), systemAddress); + return true; + } + + timeLastDatagramArrived=RakNet::GetTimeMS(); + + // CCTimeType time; +// bool indexFound; +// int count, size; + DatagramSequenceNumberType holeCount; + unsigned i; + +#if LIBCAT_SECURITY==1 + if (useSecurity) + { + unsigned int received = length; + + if (!auth_enc.Decrypt((cat::u8*)buffer, received)) + return false; + + length = received; + } +#endif + + RakNet::BitStream socketData( (unsigned char*) buffer, length, false ); // Convert the incoming data to a bitstream for easy parsing + // time = RakNet::GetTimeUS(); + + // Set to the current time if it is not zero, and we get incoming data + // if (timeResendQueueNonEmpty!=0) + // timeResendQueueNonEmpty=timeRead; + + DatagramHeaderFormat dhf; + dhf.Deserialize(&socketData); + if (dhf.isValid==false) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("dhf.isValid==false", BYTES_TO_BITS(length), systemAddress); + + return true; + } + if (dhf.isACK) + { + DatagramSequenceNumberType datagramNumber; + // datagramNumber=dhf.datagramNumber; + +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + RakNet::TimeMS timeMSLow=(RakNet::TimeMS) timeRead&0xFFFFFFFF; + CCTimeType rtt = timeMSLow-dhf.sourceSystemTime; +#if CC_TIME_TYPE_BYTES==4 + if (rtt > 10000) +#else + if (rtt > 10000000) +#endif + { + // Sanity check. This could happen due to type overflow, especially since I only send the low 4 bytes to reduce bandwidth + rtt=(CCTimeType) congestionManager.GetRTT(); + } + // RakAssert(rtt < 500000); + // printf("%i ", (RakNet::TimeMS)(rtt/1000)); + ackPing=rtt; +#endif + +#ifdef _DEBUG + if (dhf.hasBAndAS==false) + { + // dhf.B=0; + dhf.AS=0; + } +#endif + // congestionManager.OnAck(timeRead, rtt, dhf.hasBAndAS, dhf.B, dhf.AS, totalUserDataBytesAcked ); + + + incomingAcks.Clear(); + if (incomingAcks.Deserialize(&socketData)==false) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingAcks.Deserialize failed", BYTES_TO_BITS(length), systemAddress); + + return false; + } + for (i=0; iincomingAcks.ranges[i].maxIndex) + { + RakAssert(incomingAcks.ranges[i].minIndex<=incomingAcks.ranges[i].maxIndex); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingAcks minIndex > maxIndex", BYTES_TO_BITS(length), systemAddress); + return false; + } + for (datagramNumber=incomingAcks.ranges[i].minIndex; datagramNumber >= incomingAcks.ranges[i].minIndex && datagramNumber <= incomingAcks.ranges[i].maxIndex; datagramNumber++) + { + CCTimeType whenSent; + + if (unreliableWithAckReceiptHistory.Size()>0) + { + unsigned int k=0; + while (k < unreliableWithAckReceiptHistory.Size()) + { + if (unreliableWithAckReceiptHistory[k].datagramNumber == datagramNumber) + { + InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); + AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); + ackReceipt->dataBitLength=BYTES_TO_BITS(5); + ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_ACKED; + memcpy(ackReceipt->data+sizeof(MessageID), &unreliableWithAckReceiptHistory[k].sendReceiptSerial, sizeof(uint32_t)); + outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); + + // Remove, swap with last + unreliableWithAckReceiptHistory.RemoveAtIndex(k); + } + else + k++; + } + } + + MessageNumberNode *messageNumberNode = GetMessageNumberNodeByDatagramIndex(datagramNumber, &whenSent); + if (messageNumberNode) + { + // printf("%p Got ack for %i\n", this, datagramNumber.val); +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + congestionManager.OnAck(timeRead, rtt, dhf.hasBAndAS, 0, dhf.AS, totalUserDataBytesAcked, bandwidthExceededStatistic, datagramNumber ); +#else + CCTimeType ping; + if (timeRead>whenSent) + ping=timeRead-whenSent; + else + ping=0; + congestionManager.OnAck(timeRead, ping, dhf.hasBAndAS, 0, dhf.AS, totalUserDataBytesAcked, bandwidthExceededStatistic, datagramNumber ); +#endif + while (messageNumberNode) + { + // TESTING1 +// printf("Remove %i on ack for datagramNumber=%i.\n", messageNumberNode->messageNumber.val, datagramNumber.val); + + RemovePacketFromResendListAndDeleteOlderReliableSequenced( messageNumberNode->messageNumber, timeRead, messageHandlerList, systemAddress ); + messageNumberNode=messageNumberNode->next; + } + + RemoveFromDatagramHistory(datagramNumber); + } +// else if (isReliable) +// { +// // Previously used slot, rather than empty unreliable slot +// printf("%p Ack %i is duplicate\n", this, datagramNumber.val); +// +// congestionManager.OnDuplicateAck(timeRead, datagramNumber); +// } + } + } + } + else if (dhf.isNAK) + { + DatagramSequenceNumberType messageNumber; + DataStructures::RangeList incomingNAKs; + if (incomingNAKs.Deserialize(&socketData)==false) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingNAKs.Deserialize failed", BYTES_TO_BITS(length), systemAddress); + + return false; + } + for (i=0; iincomingNAKs.ranges[i].maxIndex) + { + RakAssert(incomingNAKs.ranges[i].minIndex<=incomingNAKs.ranges[i].maxIndex); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("incomingNAKs minIndex>maxIndex", BYTES_TO_BITS(length), systemAddress); + + return false; + } + // Sanity check + RakAssert(incomingNAKs.ranges[i].maxIndex.val-incomingNAKs.ranges[i].minIndex.val<1000); + for (messageNumber=incomingNAKs.ranges[i].minIndex; messageNumber >= incomingNAKs.ranges[i].minIndex && messageNumber <= incomingNAKs.ranges[i].maxIndex; messageNumber++) + { + congestionManager.OnNAK(timeRead, messageNumber); + + // REMOVEME + // printf("%p NAK %i\n", this, dhf.datagramNumber.val); + + + CCTimeType timeSent; + MessageNumberNode *messageNumberNode = GetMessageNumberNodeByDatagramIndex(messageNumber, &timeSent); + while (messageNumberNode) + { + // Update timers so resends occur immediately + InternalPacket *internalPacket = resendBuffer[messageNumberNode->messageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK]; + if (internalPacket) + { + if (internalPacket->nextActionTime!=0) + { + internalPacket->nextActionTime=timeRead; + } + } + + messageNumberNode=messageNumberNode->next; + } + } + } + } + else + { + uint32_t skippedMessageCount; + if (!congestionManager.OnGotPacket(dhf.datagramNumber, dhf.isContinuousSend, timeRead, length, &skippedMessageCount)) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("congestionManager.OnGotPacket failed", BYTES_TO_BITS(length), systemAddress); + + return true; + } + if (dhf.isPacketPair) + congestionManager.OnGotPacketPair(dhf.datagramNumber, length, timeRead); + + DatagramHeaderFormat dhfNAK; + dhfNAK.isNAK=true; + uint32_t skippedMessageOffset; + for (skippedMessageOffset=skippedMessageCount; skippedMessageOffset > 0; skippedMessageOffset--) + { + NAKs.Insert(dhf.datagramNumber-skippedMessageOffset); + } + remoteSystemNeedsBAndAS=dhf.needsBAndAs; + + // Ack dhf.datagramNumber + // Ack even unreliable messages for congestion control, just don't resend them on no ack +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + SendAcknowledgementPacket( dhf.datagramNumber, dhf.sourceSystemTime); +#else + SendAcknowledgementPacket( dhf.datagramNumber, 0); +#endif + + InternalPacket* internalPacket = CreateInternalPacketFromBitStream( &socketData, timeRead ); + if (internalPacket==0) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("CreateInternalPacketFromBitStream failed", BYTES_TO_BITS(length), systemAddress); + + return true; + } + + while ( internalPacket ) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + { +#if CC_TIME_TYPE_BYTES==4 + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, receivePacketCount, systemAddress, timeRead, false); +#else + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, receivePacketCount, systemAddress, (RakNet::TimeMS)(timeRead/(CCTimeType)1000), false); +#endif + } + + { + + // resetReceivedPackets is set from a non-threadsafe function. + // We do the actual reset in this function so the data is not modified by multiple threads + if (resetReceivedPackets) + { + hasReceivedPacketQueue.ClearAndForceAllocation(DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE, _FILE_AND_LINE_); + receivedPacketsBaseIndex=0; + resetReceivedPackets=false; + } + + // Check for corrupt orderingChannel + if ( + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED + ) + { + if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) + { + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS", BYTES_TO_BITS(length), systemAddress); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + + // 8/12/09 was previously not checking if the message was reliable. However, on packetloss this would mean you'd eventually exceed the + // hole count because unreliable messages were never resent, and you'd stop getting messages + if (internalPacket->reliability == RELIABLE || internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_ORDERED ) + { + // If the following conditional is true then this either a duplicate packet + // or an older out of order packet + // The subtraction unsigned overflow is intentional + holeCount = (DatagramSequenceNumberType)(internalPacket->reliableMessageNumber-receivedPacketsBaseIndex); + const DatagramSequenceNumberType typeRange = (DatagramSequenceNumberType)(const uint32_t)-1; + + // TESTING1 +// printf("waiting on reliableMessageNumber=%i holeCount=%i datagramNumber=%i\n", receivedPacketsBaseIndex.val, holeCount.val, dhf.datagramNumber.val); + + if (holeCount==(DatagramSequenceNumberType) 0) + { + // Got what we were expecting + if (hasReceivedPacketQueue.Size()) + hasReceivedPacketQueue.Pop(); + ++receivedPacketsBaseIndex; + } + else if (holeCount > typeRange/(DatagramSequenceNumberType) 2) + { + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Duplicate packet + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + else if ((unsigned int) holeCountdataBitLength)); + + // Duplicate packet + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + else // holeCount>=receivedPackets.Size() + { + if (holeCount > (DatagramSequenceNumberType) 1000000) + { + RakAssert("Hole count too high. See ReliabilityLayer.h" && 0); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("holeCount > 1000000", BYTES_TO_BITS(length), systemAddress); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Would crash due to out of memory! + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + + // Fix - sending on a higher priority gives us a very very high received packets base index if we formerly had pre-split a lot of messages and + // used that as the message number. Because of this, a lot of time is spent in this linear loop and the timeout time expires because not + // all of the message is sent in time. + // Fixed by late assigning message IDs on the sender + + // Add 0 times to the queue until (reliableMessageNumber - baseIndex) < queue size. + while ((unsigned int)(holeCount) > hasReceivedPacketQueue.Size()) + hasReceivedPacketQueue.Push(true, _FILE_AND_LINE_ ); // time+(CCTimeType)60 * (CCTimeType)1000 * (CCTimeType)1000); // Didn't get this packet - set the time to give up waiting + hasReceivedPacketQueue.Push(false, _FILE_AND_LINE_ ); // Got the packet +#ifdef _DEBUG + // If this assert hits then DatagramSequenceNumberType has overflowed + RakAssert(hasReceivedPacketQueue.Size() < (unsigned int)((DatagramSequenceNumberType)(const uint32_t)(-1))); +#endif + } + + while ( hasReceivedPacketQueue.Size()>0 && hasReceivedPacketQueue.Peek()==false ) + { + hasReceivedPacketQueue.Pop(); + ++receivedPacketsBaseIndex; + } + } + + // If the allocated buffer is > DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE and it is 3x greater than the number of elements actually being used + if (hasReceivedPacketQueue.AllocationSize() > (unsigned int) DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE && hasReceivedPacketQueue.AllocationSize() > hasReceivedPacketQueue.Size() * 3) + hasReceivedPacketQueue.Compress(_FILE_AND_LINE_); + + + /* + if ( internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == UNRELIABLE_SEQUENCED ) + { +#ifdef _DEBUG + RakAssert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); +#endif + + if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) + { + + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + messageHandlerList[messageHandlerIndex]->OnReliabilityLayerPacketError("internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS", BYTES_TO_BITS(length), systemAddress); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + if ( IsOlderOrderedPacket( internalPacket->orderingIndex, waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] ) == false ) + { + // Is this a split packet? + if ( internalPacket->splitPacketCount > 0 ) + { + // Generate the split + // Verify some parameters to make sure we don't get junk data + + + // Check for a rebuilt packet + InsertIntoSplitPacketList( internalPacket, timeRead ); + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Sequenced + internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, + s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions); + + if ( internalPacket ) + { + // Update our index to the newest packet + waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + (OrderingIndexType)1; + + // If there is a rebuilt packet, add it to the output queue + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + internalPacket = 0; + } + + // else don't have all the parts yet + } + else + { + // Update our index to the newest packet + waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + (OrderingIndexType)1; + + // Not a split packet. Add the packet to the output queue + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + internalPacket = 0; + } + } + else + { + // Older sequenced packet. Discard it + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + } + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + // Is this an unsequenced split packet? + if ( internalPacket->splitPacketCount > 0 ) + { + // Check for a rebuilt packet + if ( internalPacket->reliability != RELIABLE_ORDERED ) + internalPacket->orderingChannel = 255; // Use 255 to designate not sequenced and not ordered + + InsertIntoSplitPacketList( internalPacket, timeRead ); + + internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, + s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions); + + if ( internalPacket == 0 ) + { + + // Don't have all the parts yet + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + */ + + /* + if ( internalPacket->reliability == RELIABLE_ORDERED ) + { +#ifdef _DEBUG + RakAssert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); +#endif + + if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) + { + // Invalid packet + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_IGNORED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + if ( waitingForOrderedPacketReadIndex[ internalPacket->orderingChannel ] == internalPacket->orderingIndex ) + { + // Get the list to hold ordered packets for this stream + DataStructures::LinkedList *orderingListAtOrderingStream; + unsigned char orderingChannelCopy = internalPacket->orderingChannel; + + // Push the packet for the user to read + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + internalPacket = 0; // Don't reference this any longer since other threads access it + + // Wait for the resendNext ordered packet in sequence + waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ++; // This wraps + + orderingListAtOrderingStream = GetOrderingListAtOrderingStream( orderingChannelCopy ); + + if ( orderingListAtOrderingStream != 0) + { + while ( orderingListAtOrderingStream->Size() > 0 ) + { + // Cycle through the list until nothing is found + orderingListAtOrderingStream->Beginning(); + indexFound=false; + size=orderingListAtOrderingStream->Size(); + count=0; + + while (count++ < size) + { + if ( orderingListAtOrderingStream->Peek()->orderingIndex == waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ) + { + outputQueue.Push( orderingListAtOrderingStream->Pop(), _FILE_AND_LINE_ ); + waitingForOrderedPacketReadIndex[ orderingChannelCopy ]++; + indexFound=true; + } + else + (*orderingListAtOrderingStream)++; + } + + if (indexFound==false) + break; + } + } + internalPacket = 0; + } + else + { + // This is a newer ordered packet than we are waiting for. Store it for future use + AddToOrderingList( internalPacket ); + } + + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + */ + + // Is this a split packet? If so then reassemble + if ( internalPacket->splitPacketCount > 0 ) + { + // Check for a rebuilt packet + if ( internalPacket->reliability != RELIABLE_ORDERED && internalPacket->reliability!=RELIABLE_SEQUENCED && internalPacket->reliability!=UNRELIABLE_SEQUENCED) + internalPacket->orderingChannel = 255; // Use 255 to designate not sequenced and not ordered + + InsertIntoSplitPacketList( internalPacket, timeRead ); + + internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, timeRead, + s, systemAddress, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); + + if ( internalPacket == 0 ) + { + // Don't have all the parts yet + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + + if (internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED) + { +#ifdef PRINT_TO_FILE_ROT + // ___________________ + BitStream bitStream(internalPacket->data, BITS_TO_BYTES(internalPacket->dataBitLength), false); + unsigned int receivedPacketNumber; + RakNet::Time receivedTime; + unsigned char streamNumber; + PacketReliability reliability; + // ___________________ + + + bitStream.IgnoreBits(8); // Ignore ID_TIMESTAMP + bitStream.Read(receivedTime); + bitStream.IgnoreBits(8); // Ignore ID_USER_ENUM+1 + bitStream.Read(receivedPacketNumber); + bitStream.Read(streamNumber); + bitStream.Read(reliability); + char *type="UNDEFINED"; + if (reliability==UNRELIABLE_SEQUENCED) + type="UNRELIABLE_SEQUENCED"; + else if (reliability==RELIABLE_ORDERED) + type="RELIABLE_ORDERED"; + // ___________________ +#endif + + + if (internalPacket->orderingIndex==orderedReadIndex[internalPacket->orderingChannel]) + { + // Has current ordering index + if (internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED) + { + // Is sequenced + if (IsOlderOrderedPacket(internalPacket->sequencingIndex,highestSequencedReadIndex[internalPacket->orderingChannel])==false) + { + // Expected or highest known value + + // Update highest sequence + highestSequencedReadIndex[internalPacket->orderingChannel] = internalPacket->sequencingIndex; + +#ifdef PRINT_TO_FILE_ROT + if (fp) + { + fprintf(fp, "Returning %i, %s by fallthrough. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); + fflush(fp); + } +#endif + + // Fallthrough, returned to user below + } + else + { +#ifdef PRINT_TO_FILE_ROT + if (fp) + { + fprintf(fp, "Discarding %i, %s late sequenced. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); + fflush(fp); + } +#endif + + // Lower than highest known value + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + else + { + // Push to output buffer immediately + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + +#ifdef PRINT_TO_FILE_ROT + if (fp) + { + fprintf(fp, "outputting immediate %i, %s. OI=%i. SI=%i.", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); + if (orderingHeaps[internalPacket->orderingChannel].Size()==0) + fprintf(fp, "heap empty\n"); + else + fprintf(fp, "heap head=%i\n", orderingHeaps[internalPacket->orderingChannel].Peek()->orderingIndex.val); + fflush(fp); + } +#endif + + orderedReadIndex[internalPacket->orderingChannel]++; + highestSequencedReadIndex[internalPacket->orderingChannel] = 0; + + // Return off heap until order lost + while (orderingHeaps[internalPacket->orderingChannel].Size()>0 && + orderingHeaps[internalPacket->orderingChannel].Peek()->orderingIndex==orderedReadIndex[internalPacket->orderingChannel]) + { + internalPacket = orderingHeaps[internalPacket->orderingChannel].Pop(0); + +#ifdef PRINT_TO_FILE_ROT + BitStream bitStream2(internalPacket->data, BITS_TO_BYTES(internalPacket->dataBitLength), false); + bitStream2.IgnoreBits(8); // Ignore ID_TIMESTAMP + bitStream2.Read(receivedTime); + bitStream2.IgnoreBits(8); // Ignore ID_USER_ENUM+1 + bitStream2.Read(receivedPacketNumber); + bitStream2.Read(streamNumber); + bitStream2.Read(reliability); + char *type="UNDEFINED"; + if (reliability==UNRELIABLE_SEQUENCED) + type="UNRELIABLE_SEQUENCED"; + else if (reliability==RELIABLE_ORDERED) + type="RELIABLE_ORDERED"; + + if (fp) + { + fprintf(fp, "Heap pop %i, %s. OI=%i. SI=%i.\n", receivedPacketNumber, type, internalPacket->orderingIndex.val, internalPacket->sequencingIndex); + fflush(fp); + } +#endif + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + + if (internalPacket->reliability == RELIABLE_ORDERED) + { + orderedReadIndex[internalPacket->orderingChannel]++; + } + else + { + highestSequencedReadIndex[internalPacket->orderingChannel] = internalPacket->sequencingIndex; + } + } + + // Done + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + } + else if (IsOlderOrderedPacket(internalPacket->orderingIndex,orderedReadIndex[internalPacket->orderingChannel])==false) + { + // internalPacket->_orderingIndex is greater + // If a message has a greater ordering index, and is sequenced or ordered, buffer it + // Sequenced has a lower heap weight, ordered has max sequenced weight + + // Keep orderedHoleCount count small + if (orderingHeaps[internalPacket->orderingChannel].Size()==0) + heapIndexOffsets[internalPacket->orderingChannel]=orderedReadIndex[internalPacket->orderingChannel]; + + reliabilityHeapWeightType orderedHoleCount = internalPacket->orderingIndex-heapIndexOffsets[internalPacket->orderingChannel]; + reliabilityHeapWeightType weight = orderedHoleCount*1048576; + if (internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED) + weight+=internalPacket->sequencingIndex; + else + weight+=(1048576-1); + orderingHeaps[internalPacket->orderingChannel].Push(weight, internalPacket, _FILE_AND_LINE_); + +#ifdef PRINT_TO_FILE_ROT + if (fp) + { + fprintf(fp, "Heap push %i, %s, weight=%" PRINTF_64_BIT_MODIFIER "u. OI=%i. waiting on %i. SI=%i.\n", receivedPacketNumber, type, weight, internalPacket->orderingIndex.val, orderedReadIndex[internalPacket->orderingChannel].val, internalPacket->sequencingIndex); + fflush(fp); + } +#endif + + // Buffered, nothing to do + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + else + { + // Out of order + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + // Ignored, nothing to do + goto CONTINUE_SOCKET_DATA_PARSE_LOOP; + } + + } + + bpsMetrics[(int) USER_MESSAGE_BYTES_RECEIVED_PROCESSED].Push1(timeRead,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Nothing special about this packet. Add it to the output queue + outputQueue.Push( internalPacket, _FILE_AND_LINE_ ); + + internalPacket = 0; + } + + // Used for a goto to jump to the resendNext packet immediately + +CONTINUE_SOCKET_DATA_PARSE_LOOP: + // Parse the bitstream to create an internal packet + internalPacket = CreateInternalPacketFromBitStream( &socketData, timeRead ); + } + + } + + + receivePacketCount++; + + return true; +} + +//------------------------------------------------------------------------------------------------------- +// This gets an end-user packet already parsed out. Returns number of BITS put into the buffer +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::Receive( unsigned char **data ) +{ + InternalPacket * internalPacket; + + if ( outputQueue.Size() > 0 ) + { + // #ifdef _DEBUG + // RakAssert(bitStream->GetNumberOfBitsUsed()==0); + // #endif + internalPacket = outputQueue.Pop(); + + BitSize_t bitLength; + *data = internalPacket->data; + bitLength = internalPacket->dataBitLength; + ReleaseToInternalPacketPool( internalPacket ); + return bitLength; + } + + else + { + return 0; + } + +} + +//------------------------------------------------------------------------------------------------------- +// Puts data on the send queue +// bitStream contains the data to send +// priority is what priority to send the data at +// reliability is what reliability to use +// ordering channel is from 0 to 255 and specifies what stream to use +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::Send( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, CCTimeType currentTime, uint32_t receipt ) +{ +#ifdef _DEBUG + RakAssert( !( reliability >= NUMBER_OF_RELIABILITIES || reliability < 0 ) ); + RakAssert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + RakAssert( numberOfBitsToSend > 0 ); +#endif + +#if CC_TIME_TYPE_BYTES==4 + currentTime/=1000; +#endif + + (void) MTUSize; + + // int a = BITS_TO_BYTES(numberOfBitsToSend); + + // Fix any bad parameters + if ( reliability > RELIABLE_ORDERED_WITH_ACK_RECEIPT || reliability < 0 ) + reliability = RELIABLE; + + if ( priority > NUMBER_OF_PRIORITIES || priority < 0 ) + priority = HIGH_PRIORITY; + + if ( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) + orderingChannel = 0; + + unsigned int numberOfBytesToSend=(unsigned int) BITS_TO_BYTES(numberOfBitsToSend); + if ( numberOfBitsToSend == 0 ) + { + return false; + } + InternalPacket * internalPacket = AllocateFromInternalPacketPool(); + if (internalPacket==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return false; // Out of memory + } + + bpsMetrics[(int) USER_MESSAGE_BYTES_PUSHED].Push1(currentTime,numberOfBytesToSend); + + internalPacket->creationTime = currentTime; + + if ( makeDataCopy ) + { + AllocInternalPacketData(internalPacket, numberOfBytesToSend, true, _FILE_AND_LINE_ ); + //internalPacket->data = (unsigned char*) rakMalloc_Ex( numberOfBytesToSend, _FILE_AND_LINE_ ); + memcpy( internalPacket->data, data, numberOfBytesToSend ); + } + else + { + // Allocated the data elsewhere, delete it in here + //internalPacket->data = ( unsigned char* ) data; + AllocInternalPacketData(internalPacket, (unsigned char*) data ); + } + + internalPacket->dataBitLength = numberOfBitsToSend; + internalPacket->messageInternalOrder = internalOrderIndex++; + internalPacket->priority = priority; + internalPacket->reliability = reliability; + internalPacket->sendReceiptSerial=receipt; + + // Calculate if I need to split the packet + // int headerLength = BITS_TO_BYTES( GetMessageHeaderLengthBits( internalPacket, true ) ); + + unsigned int maxDataSizeBytes = GetMaxDatagramSizeExcludingMessageHeaderBytes() - BITS_TO_BYTES(GetMaxMessageHeaderLengthBits()); + + bool splitPacket = numberOfBytesToSend > maxDataSizeBytes; + + // If a split packet, we might have to upgrade the reliability + if ( splitPacket ) + { + // Split packets cannot be unreliable, in case that one part doesn't arrive and the whole cannot be reassembled. + // One part could not arrive either due to packetloss or due to unreliable discard + if (internalPacket->reliability==UNRELIABLE) + internalPacket->reliability=RELIABLE; + else if (internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) + internalPacket->reliability=RELIABLE_WITH_ACK_RECEIPT; + else if (internalPacket->reliability==UNRELIABLE_SEQUENCED) + internalPacket->reliability=RELIABLE_SEQUENCED; +// else if (internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT) +// internalPacket->reliability=RELIABLE_SEQUENCED_WITH_ACK_RECEIPT; + } + + // ++sendMessageNumberIndex; + + if ( internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == UNRELIABLE_SEQUENCED +// || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || +// internalPacket->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + { + // Assign the sequence stream and index + internalPacket->orderingChannel = orderingChannel; + internalPacket->orderingIndex = orderedWriteIndex[ orderingChannel ]; + internalPacket->sequencingIndex = sequencedWriteIndex[ orderingChannel ]++; + + // This packet supersedes all other sequenced packets on the same ordering channel + // Delete all packets in all send lists that are sequenced and on the same ordering channel + // UPDATE: + // Disabled. We don't have enough info to consistently do this. Sometimes newer data does supercede + // older data such as with constantly declining health, but not in all cases. + // For example, with sequenced unreliable sound packets just because you send a newer one doesn't mean you + // don't need the older ones because the odds are they will still arrive in order + /* + for (int i=0; i < NUMBER_OF_PRIORITIES; i++) + { + DeleteSequencedPacketsInList(orderingChannel, sendQueue[i]); + } + */ + } + else if ( internalPacket->reliability == RELIABLE_ORDERED || internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT ) + { + // Assign the ordering channel and index + internalPacket->orderingChannel = orderingChannel; + internalPacket->orderingIndex = orderedWriteIndex[ orderingChannel ] ++; + sequencedWriteIndex[ orderingChannel ]=0; + } + + if ( splitPacket ) // If it uses a secure header it will be generated here + { + // Must split the packet. This will also generate the SHA1 if it is required. It also adds it to the send list. + //InternalPacket packetCopy; + //memcpy(&packetCopy, internalPacket, sizeof(InternalPacket)); + //sendPacketSet[priority].CancelWriteLock(internalPacket); + //SplitPacket( &packetCopy, MTUSize ); + SplitPacket( internalPacket ); + //RakNet::OP_DELETE_ARRAY(packetCopy.data, _FILE_AND_LINE_); + return true; + } + + RakAssert(internalPacket->dataBitLengthdataBitLengthmessageNumberAssigned==false); + outgoingPacketBuffer.Push( GetNextWeight(internalPacket->priority), internalPacket, _FILE_AND_LINE_ ); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]++; + statistics.bytesInSendBuffer[(int)internalPacket->priority]+=(double) BITS_TO_BYTES(internalPacket->dataBitLength); + + // sendPacketSet[priority].WriteUnlock(); + return true; +} +//------------------------------------------------------------------------------------------------------- +// Run this once per game cycle. Handles internal lists and actually does the send +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::Update( SOCKET s, SystemAddress &systemAddress, int MTUSize, CCTimeType time, + unsigned bitsPerSecondLimit, + DataStructures::List &messageHandlerList, + RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, + BitStream &updateBitStream) + +{ + (void) MTUSize; + + RakNet::TimeMS timeMs; +#if CC_TIME_TYPE_BYTES==4 + time/=1000; + timeMs=time; +#else + timeMs=(RakNet::TimeMS) (time/(CCTimeType)1000); +#endif + +#ifdef _DEBUG + while (delayList.Size()) + { + if (delayList.Peek()->sendTime <= timeMs) + { + DataAndTime *dat = delayList.Pop(); + SocketLayer::SendTo( dat->s, dat->data, dat->length, systemAddress, dat->remotePortRakNetWasStartedOn_PS3, dat->extraSocketOptions, __FILE__, __LINE__ ); + RakNet::OP_DELETE(dat,__FILE__,__LINE__); + } + break; + } +#endif + + // This line is necessary because the timer isn't accurate + if (time <= lastUpdateTime) + { + // Always set the last time in case of overflow + lastUpdateTime=time; + return; + } + + CCTimeType timeSinceLastTick = time - lastUpdateTime; + lastUpdateTime=time; +#if CC_TIME_TYPE_BYTES==4 + if (timeSinceLastTick>100) + timeSinceLastTick=100; +#else + if (timeSinceLastTick>100000) + timeSinceLastTick=100000; +#endif + + if (unreliableTimeout>0) + { + if (timeSinceLastTick>=timeToNextUnreliableCull) + { + if (unreliableLinkedListHead) + { + // Cull out all unreliable messages that have exceeded the timeout + InternalPacket *cur = unreliableLinkedListHead; + InternalPacket *end = unreliableLinkedListHead->unreliablePrev; + while (1) + { + if (time > cur->creationTime+(CCTimeType)unreliableTimeout) + { + // Flag invalid, and clear the memory. Still needs to be removed from the sendPacketSet later + // This fixes a problem where a remote system disconnects, but we don't know it yet, and memory consumption increases to a huge value + FreeInternalPacketData(cur, _FILE_AND_LINE_ ); + cur->data=0; + InternalPacket *next = cur->unreliableNext; + RemoveFromUnreliableLinkedList(cur); + + if (cur==end) + break; + + cur=next; + } + else + { + // if (cur==end) + // break; + // + // cur=cur->unreliableNext; + + // They should be inserted in-order, so no need to iterate past the first failure + break; + } + } + } + + timeToNextUnreliableCull=unreliableTimeout/(CCTimeType)2; + } + else + { + timeToNextUnreliableCull-=timeSinceLastTick; + } + } + + + // Due to thread vagarities and the way I store the time to avoid slow calls to RakNet::GetTime + // time may be less than lastAck +#if CC_TIME_TYPE_BYTES==4 + if ( statistics.messagesInResendBuffer!=0 && AckTimeout(time) ) +#else + if ( statistics.messagesInResendBuffer!=0 && AckTimeout(RakNet::TimeMS(time/(CCTimeType)1000)) ) +#endif + { + // SHOW - dead connection + // We've waited a very long time for a reliable packet to get an ack and it never has + deadConnection = true; + return; + } + + if (congestionManager.ShouldSendACKs(time,timeSinceLastTick)) + { + SendACKs(s, systemAddress, time, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); + } + + if (NAKs.Size()>0) + { + updateBitStream.Reset(); + DatagramHeaderFormat dhfNAK; + dhfNAK.isNAK=true; + dhfNAK.isACK=false; + dhfNAK.isPacketPair=false; + dhfNAK.Serialize(&updateBitStream); + NAKs.Serialize(&updateBitStream, GetMaxDatagramSizeExcludingMessageHeaderBits(), true); + SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); + } + + DatagramHeaderFormat dhf; + dhf.needsBAndAs=congestionManager.GetIsInSlowStart(); + dhf.isContinuousSend=bandwidthExceededStatistic; + // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || + // sendPacketSet[1].IsEmpty()==false || + // sendPacketSet[2].IsEmpty()==false || + // sendPacketSet[3].IsEmpty()==false; + bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; + + const bool hasDataToSendOrResend = IsResendQueueEmpty()==false || bandwidthExceededStatistic; + RakAssert(NUMBER_OF_PRIORITIES==4); + congestionManager.Update(time, hasDataToSendOrResend); + + statistics.BPSLimitByOutgoingBandwidthLimit = BITS_TO_BYTES(bitsPerSecondLimit); + statistics.BPSLimitByCongestionControl = congestionManager.GetBytesPerSecondLimitByCongestionControl(); + + unsigned int i; + if (time > lastBpsClear+ +#if CC_TIME_TYPE_BYTES==4 + 100 +#else + 100000 +#endif + ) + { + for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) + { + bpsMetrics[i].ClearExpired1(time); + } + + lastBpsClear=time; + } + + if (unreliableWithAckReceiptHistory.Size()>0) + { + i=0; + while (i < unreliableWithAckReceiptHistory.Size()) + { + if (unreliableWithAckReceiptHistory[i].nextActionTime < time) + { + InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); + AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); + ackReceipt->dataBitLength=BYTES_TO_BITS(5); + ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_LOSS; + memcpy(ackReceipt->data+sizeof(MessageID), &unreliableWithAckReceiptHistory[i].sendReceiptSerial, sizeof(uint32_t)); + outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); + + // Remove, swap with last + unreliableWithAckReceiptHistory.RemoveAtIndex(i); + } + else + i++; + } + } + + if (hasDataToSendOrResend==true) + { + InternalPacket *internalPacket; + // bool forceSend=false; + bool pushedAnything; + BitSize_t nextPacketBitLength; + dhf.isACK=false; + dhf.isNAK=false; + dhf.hasBAndAS=false; + ResetPacketsAndDatagrams(); + + int transmissionBandwidth = congestionManager.GetTransmissionBandwidth(time, timeSinceLastTick, unacknowledgedBytes,dhf.isContinuousSend); + int retransmissionBandwidth = congestionManager.GetRetransmissionBandwidth(time, timeSinceLastTick, unacknowledgedBytes,dhf.isContinuousSend); + if (retransmissionBandwidth>0 || transmissionBandwidth>0) + { + statistics.isLimitedByCongestionControl=false; + + allDatagramSizesSoFar=0; + + // Keep filling datagrams until we exceed retransmission bandwidth + while ((int)BITS_TO_BYTES(allDatagramSizesSoFar)messageNumberAssigned==true); + + if ( internalPacket->nextActionTime < time ) + { + nextPacketBitLength = internalPacket->headerLength + internalPacket->dataBitLength; + if ( datagramSizeSoFar + nextPacketBitLength > GetMaxDatagramSizeExcludingMessageHeaderBits() ) + { + // Gathers all PushPackets() + PushDatagram(); + break; + } + + PopListHead(false); + + CC_DEBUG_PRINTF_2("Rs %i ", internalPacket->reliableMessageNumber.val); + + bpsMetrics[(int) USER_MESSAGE_BYTES_RESENT].Push1(time,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Testing1 +// if (internalPacket->reliability==RELIABLE_ORDERED || internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) +// printf("RESEND reliableMessageNumber %i with datagram %i\n", internalPacket->reliableMessageNumber.val, congestionManager.GetNextDatagramSequenceNumber().val); + + PushPacket(time,internalPacket,true); // Affects GetNewTransmissionBandwidth() +// internalPacket->timesSent++; + internalPacket->nextActionTime = congestionManager.GetRTOForRetransmission()+time; +#if CC_TIME_TYPE_BYTES==4 + if (internalPacket->nextActionTime-time > 10000) +#else + if (internalPacket->nextActionTime-time > 10000000) +#endif + { + // int a=5; + RakAssert(0); + } + + congestionManager.OnResend(time); + + pushedAnything=true; + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + { +#if CC_TIME_TYPE_BYTES==4 + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS) time, true); +#else + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000), true); +#endif + } + + // Put the packet back into the resend list at the correct spot + // Don't make a copy since I'm reinserting an allocated struct + InsertPacketIntoResendList( internalPacket, time, false, false ); + + // Removeme + // printf("Resend:%i ", internalPacket->reliableMessageNumber); + } + else + { + // Filled one datagram. + // If the 2nd and it's time to send a datagram pair, will be marked as a pair + PushDatagram(); + break; + } + } + + if (pushedAnything==false) + break; + } + } + else + { + statistics.isLimitedByCongestionControl=true; + } + + if ((int)BITS_TO_BYTES(allDatagramSizesSoFar)messageNumberAssigned==false); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthdata==0) + { + //sendPacketSet[ i ].Pop(); + outgoingPacketBuffer.Pop(0); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]--; + statistics.bytesInSendBuffer[(int)internalPacket->priority]-=(double) BITS_TO_BYTES(internalPacket->dataBitLength); + ReleaseToInternalPacketPool( internalPacket ); + continue; + } + + internalPacket->headerLength=GetMessageHeaderLengthBits(internalPacket); + nextPacketBitLength = internalPacket->headerLength + internalPacket->dataBitLength; + if ( datagramSizeSoFar + nextPacketBitLength > GetMaxDatagramSizeExcludingMessageHeaderBits() ) + { + // Hit MTU. May still push packets if smaller ones exist at a lower priority + RakAssert(datagramSizeSoFar!=0); + RakAssert(internalPacket->dataBitLengthreliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + isReliable = true; + else + isReliable = false; + + //sendPacketSet[ i ].Pop(); + outgoingPacketBuffer.Pop(0); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthmessageNumberAssigned==false); + statistics.messageInSendBuffer[(int)internalPacket->priority]--; + statistics.bytesInSendBuffer[(int)internalPacket->priority]-=(double) BITS_TO_BYTES(internalPacket->dataBitLength); + if (isReliable + /* + I thought about this and agree that UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT and RELIABLE_SEQUENCED_WITH_ACK_RECEIPT is not useful unless you also know if the message was discarded. + + The problem is that internally, message numbers are only assigned to reliable messages, because message numbers are only used to discard duplicate message receipt and only reliable messages get sent more than once. However, without message numbers getting assigned and transmitted, there is no way to tell the sender about which messages were discarded. In fact, in looking this over I realized that UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT introduced a bug, because the remote system assumes all message numbers are used (no holes). With that send type, on packetloss, a permanent hole would have been created which eventually would cause the system to discard all further packets. + + So I have two options. Either do not support ack receipts when sending sequenced, or write complex and major new systems. UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT would need to send the message ID number on a special channel which allows for non-delivery. And both of them would need to have a special range list to indicate which message numbers were not delivered, so when acks are sent that can be indicated as well. A further problem is that the ack itself can be lost - it is possible that the message can arrive but be discarded, yet the ack is lost. On resend, the resent message would be ignored as duplicate, and you'd never get the discard message either (unless I made a special buffer for that case too). +*/ +// || + // If needs an ack receipt, keep the internal packet around in the list +// internalPacket->reliability == UNRELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + { + internalPacket->messageNumberAssigned=true; + internalPacket->reliableMessageNumber=sendReliableMessageNumberIndex; + internalPacket->nextActionTime = congestionManager.GetRTOForRetransmission()+time; +#if CC_TIME_TYPE_BYTES==4 + const CCTimeType threshhold = 10000; +#else + const CCTimeType threshhold = 10000000; +#endif + if (internalPacket->nextActionTime-time > threshhold) + { + // int a=5; + RakAssert(time-internalPacket->nextActionTime < threshhold); + } + //resendTree.Insert( internalPacket->reliableMessageNumber, internalPacket); + if (resendBuffer[internalPacket->reliableMessageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK]!=0) + { + // bool overflow = ResendBufferOverflow(); + RakAssert(0); + } + resendBuffer[internalPacket->reliableMessageNumber & (uint32_t) RESEND_BUFFER_ARRAY_MASK] = internalPacket; + statistics.messagesInResendBuffer++; + statistics.bytesInResendBuffer+=BITS_TO_BYTES(internalPacket->dataBitLength); + + // printf("pre:%i ", unacknowledgedBytes); + + InsertPacketIntoResendList( internalPacket, time, true, isReliable); + + + // printf("post:%i ", unacknowledgedBytes); + sendReliableMessageNumberIndex++; + } + else if (internalPacket->reliability == UNRELIABLE_WITH_ACK_RECEIPT) + { + unreliableWithAckReceiptHistory.Push(UnreliableWithAckReceiptNode( + congestionManager.GetNextDatagramSequenceNumber() + packetsToSendThisUpdateDatagramBoundaries.Size(), + internalPacket->sendReceiptSerial, + congestionManager.GetRTOForRetransmission()+time + ), _FILE_AND_LINE_); + } + +// internalPacket->timesSent=1; + // If isReliable is false, the packet and its contents will be added to a list to be freed in ClearPacketsAndDatagrams + // However, the internalPacket structure will remain allocated and be in the resendBuffer list if it requires a receipt + bpsMetrics[(int) USER_MESSAGE_BYTES_SENT].Push1(time,BITS_TO_BYTES(internalPacket->dataBitLength)); + + // Testing1 +// if (internalPacket->reliability==RELIABLE_ORDERED || internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) +// printf("SEND reliableMessageNumber %i in datagram %i\n", internalPacket->reliableMessageNumber.val, congestionManager.GetNextDatagramSequenceNumber().val); + + PushPacket(time,internalPacket, isReliable); + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + { +#if CC_TIME_TYPE_BYTES==4 + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)time, true); +#else + messageHandlerList[messageHandlerIndex]->OnInternalPacket(internalPacket, packetsToSendThisUpdateDatagramBoundaries.Size()+congestionManager.GetNextDatagramSequenceNumber(), systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000), true); +#endif + } + pushedAnything=true; + + if (ResendBufferOverflow()) + break; + } + // if (ResendBufferOverflow()) + // break; + // }z + + // No datagrams pushed? + if (datagramSizeSoFar==0) + break; + + // Filled one datagram. + // If the 2nd and it's time to send a datagram pair, will be marked as a pair + PushDatagram(); + } + } + + + for (unsigned int datagramIndex=0; datagramIndex < packetsToSendThisUpdateDatagramBoundaries.Size(); datagramIndex++) + { + if (datagramIndex>0) + dhf.isContinuousSend=true; + MessageNumberNode* messageNumberNode = 0; + dhf.datagramNumber=congestionManager.GetAndIncrementNextDatagramSequenceNumber(); + dhf.isPacketPair=datagramsToSendThisUpdateIsPair[datagramIndex]; + + //printf("%p pushing datagram %i\n", this, dhf.datagramNumber.val); + + bool isSecondOfPacketPair=dhf.isPacketPair && datagramIndex>0 && datagramsToSendThisUpdateIsPair[datagramIndex-1]; + unsigned int msgIndex, msgTerm; + if (datagramIndex==0) + { + msgIndex=0; + msgTerm=packetsToSendThisUpdateDatagramBoundaries[0]; + } + else + { + msgIndex=packetsToSendThisUpdateDatagramBoundaries[datagramIndex-1]; + msgTerm=packetsToSendThisUpdateDatagramBoundaries[datagramIndex]; + } + + // More accurate time to reset here +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + dhf.sourceSystemTime=RakNet::GetTimeUS(); +#endif + updateBitStream.Reset(); + dhf.Serialize(&updateBitStream); + CC_DEBUG_PRINTF_2("S%i ",dhf.datagramNumber.val); + + while (msgIndex < msgTerm) + { + // If reliable or needs receipt + if ( packetsToSendThisUpdate[msgIndex]->reliability != UNRELIABLE && + packetsToSendThisUpdate[msgIndex]->reliability != UNRELIABLE_SEQUENCED + ) + { + if (messageNumberNode==0) + { + messageNumberNode = AddFirstToDatagramHistory(dhf.datagramNumber, packetsToSendThisUpdate[msgIndex]->reliableMessageNumber, time); + } + else + { + messageNumberNode = AddSubsequentToDatagramHistory(messageNumberNode, packetsToSendThisUpdate[msgIndex]->reliableMessageNumber); + } + } + + RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + WriteToBitStreamFromInternalPacket( &updateBitStream, packetsToSendThisUpdate[msgIndex], time ); + RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + msgIndex++; + } + + if (isSecondOfPacketPair) + { + // Pad to size of first datagram + RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + updateBitStream.PadWithZeroToByteLength(datagramSizesInBytes[datagramIndex-1]); + RakAssert(updateBitStream.GetNumberOfBytesUsed()<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + } + + if (messageNumberNode==0) + { + // Unreliable, add dummy node + AddFirstToDatagramHistory(dhf.datagramNumber, time); + } + + // Store what message ids were sent with this datagram + // datagramMessageIDTree.Insert(dhf.datagramNumber,idList); + + congestionManager.OnSendBytes(time,UDP_HEADER_SIZE+DatagramHeaderFormat::GetDataHeaderByteLength()); + + SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); + + bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; + // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || + // sendPacketSet[1].IsEmpty()==false || + // sendPacketSet[2].IsEmpty()==false || + // sendPacketSet[3].IsEmpty()==false; + + + + if (bandwidthExceededStatistic==true) + timeOfLastContinualSend=time; + else + timeOfLastContinualSend=0; + } + + ClearPacketsAndDatagrams(); + + // Any data waiting to send after attempting to send, then bandwidth is exceeded + bandwidthExceededStatistic=outgoingPacketBuffer.Size()>0; + // bandwidthExceededStatistic=sendPacketSet[0].IsEmpty()==false || + // sendPacketSet[1].IsEmpty()==false || + // sendPacketSet[2].IsEmpty()==false || + // sendPacketSet[3].IsEmpty()==false; + } + + + // Keep on top of deleting old unreliable split packets so they don't clog the list. + //DeleteOldUnreliableSplitPackets( time ); +} + +//------------------------------------------------------------------------------------------------------- +// Writes a bitstream to the socket +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SendBitStream( SOCKET s, SystemAddress &systemAddress, RakNet::BitStream *bitStream, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType currentTime) +{ + (void) systemAddress; + (void) rnr; + + unsigned int length; + + length = (unsigned int) bitStream->GetNumberOfBytesUsed(); + + +#ifdef _DEBUG + if (packetloss > 0.0) + { + if (frandomMT() < packetloss) + return; + } + + if (minExtraPing > 0 || extraPingVariance > 0) + { + RakNet::TimeMS delay = minExtraPing; + if (extraPingVariance>0) + delay += (randomMT() % extraPingVariance); + if (delay > 0) + { + DataAndTime *dat = RakNet::OP_NEW(__FILE__,__LINE__); + memcpy(dat->data, ( char* ) bitStream->GetData(), length ); + dat->s=s; + dat->length=length; + dat->sendTime = RakNet::GetTimeMS() + delay; + dat->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; + dat->extraSocketOptions=extraSocketOptions; + for (unsigned int i=0; i < delayList.Size(); i++) + { + if (dat->sendTime < delayList[i]->sendTime) + { + delayList.PushAtHead(dat, i, __FILE__, __LINE__); + dat=0; + break; + } + } + if (dat!=0) + delayList.Push(dat,__FILE__,__LINE__); + return; + } + } +#endif + +#if LIBCAT_SECURITY==1 + if (useSecurity) + { + unsigned char *buffer = reinterpret_cast( bitStream->GetData() ); + + int buffer_size = bitStream->GetNumberOfBitsAllocated() / 8; + + // Verify there is enough room for encrypted output and encrypt + // Encrypt() will increase length + bool success = auth_enc.Encrypt(buffer, buffer_size, length); + RakAssert(success); + } +#endif + + bpsMetrics[(int) ACTUAL_BYTES_SENT].Push1(currentTime,length); + + RakAssert(length <= congestionManager.GetMTU()); + +#ifdef USE_THREADED_SEND + SendToThread::SendToThreadBlock *block = SendToThread::AllocateBlock(); + memcpy(block->data, bitStream->GetData(), length); + block->dataWriteOffset=length; + block->extraSocketOptions=extraSocketOptions; + block->remotePortRakNetWasStartedOn_PS3=remotePortRakNetWasStartedOn_PS3; + block->s=s; + block->systemAddress=systemAddress; + SendToThread::ProcessBlock(block); +#else + SocketLayer::SendTo( s, ( char* ) bitStream->GetData(), length, systemAddress, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, __FILE__, __LINE__ ); +#endif +} + +//------------------------------------------------------------------------------------------------------- +// Are we waiting for any data to be sent out or be processed by the player? +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsOutgoingDataWaiting(void) +{ + if (outgoingPacketBuffer.Size()>0) + return true; + + // unsigned i; + // for ( i = 0; i < NUMBER_OF_PRIORITIES; i++ ) + // { + // if (sendPacketSet[ i ].Size() > 0) + // return true; + // } + + return + //acknowlegements.Size() > 0 || + //resendTree.IsEmpty()==false;// || outputQueue.Size() > 0 || orderingList.Size() > 0 || splitPacketChannelList.Size() > 0; + statistics.messagesInResendBuffer!=0; +} +bool ReliabilityLayer::AreAcksWaiting(void) +{ + return acknowlegements.Size() > 0; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ApplyNetworkSimulator( double _packetloss, RakNet::TimeMS _minExtraPing, RakNet::TimeMS _extraPingVariance ) +{ +#ifdef _DEBUG + packetloss=_packetloss; + minExtraPing=_minExtraPing; + extraPingVariance=_extraPingVariance; + // if (ping < (unsigned int)(minExtraPing+extraPingVariance)*2) + // ping=(minExtraPing+extraPingVariance)*2; +#endif +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SetSplitMessageProgressInterval(int interval) +{ + splitMessageProgressInterval=interval; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SetUnreliableTimeout(RakNet::TimeMS timeoutMS) +{ +#if CC_TIME_TYPE_BYTES==4 + unreliableTimeout=timeoutMS; +#else + unreliableTimeout=(CCTimeType)timeoutMS*(CCTimeType)1000; +#endif +} + +//------------------------------------------------------------------------------------------------------- +// This will return true if we should not send at this time +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsSendThrottled( int MTUSize ) +{ + (void) MTUSize; + + return false; + // return resendList.Size() > windowSize; + + // Disabling this, because it can get stuck here forever + /* + unsigned packetsWaiting; + unsigned resendListDataSize=0; + unsigned i; + for (i=0; i < resendList.Size(); i++) + { + if (resendList[i]) + resendListDataSize+=resendList[i]->dataBitLength; + } + packetsWaiting = 1 + ((BITS_TO_BYTES(resendListDataSize)) / (MTUSize - UDP_HEADER_SIZE - 10)); // 10 to roughly estimate the raknet header + + return packetsWaiting >= windowSize; + */ +} + +//------------------------------------------------------------------------------------------------------- +// We lost a packet +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::UpdateWindowFromPacketloss( CCTimeType time ) +{ + (void) time; +} + +//------------------------------------------------------------------------------------------------------- +// Increase the window size +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::UpdateWindowFromAck( CCTimeType time ) +{ + (void) time; +} + +//------------------------------------------------------------------------------------------------------- +// Does what the function name says +//------------------------------------------------------------------------------------------------------- +unsigned ReliabilityLayer::RemovePacketFromResendListAndDeleteOlderReliableSequenced( const MessageNumberType messageNumber, CCTimeType time, DataStructures::List &messageHandlerList, const SystemAddress &systemAddress ) +{ + (void) time; + (void) messageNumber; + InternalPacket * internalPacket; + //InternalPacket *temp; +// PacketReliability reliability; // What type of reliability algorithm to use with this packet +// unsigned char orderingChannel; // What ordering channel this packet is on, if the reliability type uses ordering channels +// OrderingIndexType orderingIndex; // The ID used as identification for ordering channels + // unsigned j; + + for (unsigned int messageHandlerIndex=0; messageHandlerIndex < messageHandlerList.Size(); messageHandlerIndex++) + { +#if CC_TIME_TYPE_BYTES==4 + messageHandlerList[messageHandlerIndex]->OnAck(messageNumber, systemAddress, time); +#else + messageHandlerList[messageHandlerIndex]->OnAck(messageNumber, systemAddress, (RakNet::TimeMS)(time/(CCTimeType)1000)); +#endif + } + + // Testing1 +// if (resendLinkedListHead) +// { +// InternalPacket *internalPacket = resendLinkedListHead; +// do +// { +// internalPacket=internalPacket->resendNext; +// printf("%i ", internalPacket->reliableMessageNumber.val); +// } while (internalPacket!=resendLinkedListHead); +// printf("\n"); +// } + + // bool deleted; + // deleted=resendTree.Delete(messageNumber, internalPacket); + internalPacket = resendBuffer[messageNumber & RESEND_BUFFER_ARRAY_MASK]; + // May ask to remove twice, for example resend twice, then second ack + if (internalPacket && internalPacket->reliableMessageNumber==messageNumber) + { + // ValidateResendList(); + resendBuffer[messageNumber & RESEND_BUFFER_ARRAY_MASK]=0; + CC_DEBUG_PRINTF_2("AckRcv %i ", messageNumber); + + statistics.messagesInResendBuffer--; + statistics.bytesInResendBuffer-=BITS_TO_BYTES(internalPacket->dataBitLength); + +// orderingIndex = internalPacket->orderingIndex; + totalUserDataBytesAcked+=(double) BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); + + // Return receipt if asked for + if (internalPacket->reliability>=RELIABLE_WITH_ACK_RECEIPT && + (internalPacket->splitPacketCount==0 || internalPacket->splitPacketIndex+1==internalPacket->splitPacketCount) + ) + { + InternalPacket *ackReceipt = AllocateFromInternalPacketPool(); + AllocInternalPacketData(ackReceipt, 5, false, _FILE_AND_LINE_ ); + ackReceipt->dataBitLength=BYTES_TO_BITS(5); + ackReceipt->data[0]=(MessageID)ID_SND_RECEIPT_ACKED; + memcpy(ackReceipt->data+sizeof(MessageID), &internalPacket->sendReceiptSerial, sizeof(internalPacket->sendReceiptSerial)); + outputQueue.Push(ackReceipt, _FILE_AND_LINE_ ); + } + + bool isReliable; + if ( internalPacket->reliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + isReliable = true; + else + isReliable = false; + + RemoveFromList(internalPacket, isReliable); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + + return 0; + } + else + { + + } + + return (unsigned)-1; +} + +//------------------------------------------------------------------------------------------------------- +// Acknowledge receipt of the packet with the specified messageNumber +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SendAcknowledgementPacket( const DatagramSequenceNumberType messageNumber, CCTimeType time ) +{ + + // REMOVEME + // printf("%p Send ack %i\n", this, messageNumber.val); + + nextAckTimeToSend=time; + acknowlegements.Insert(messageNumber); + + //printf("ACK_DG:%i ", messageNumber.val); + + CC_DEBUG_PRINTF_2("AckPush %i ", messageNumber); + +} + +//------------------------------------------------------------------------------------------------------- +// Parse an internalPacket and figure out how many header bits would be +// written. Returns that number +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::GetMaxMessageHeaderLengthBits( void ) +{ + InternalPacket ip; + ip.reliability=RELIABLE_SEQUENCED; + ip.splitPacketCount=1; + return GetMessageHeaderLengthBits(&ip); +} +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::GetMessageHeaderLengthBits( const InternalPacket *const internalPacket ) +{ + BitSize_t bitLength; + + // bitStream->AlignWriteToByteBoundary(); // Potentially unaligned + // tempChar=(unsigned char)internalPacket->reliability; bitStream->WriteBits( (const unsigned char *)&tempChar, 3, true ); // 3 bits to write reliability. + // bool hasSplitPacket = internalPacket->splitPacketCount>0; bitStream->Write(hasSplitPacket); // Write 1 bit to indicate if splitPacketCount>0 + bitLength = 8*1; + + // bitStream->AlignWriteToByteBoundary(); + // RakAssert(internalPacket->dataBitLength < 65535); + // unsigned short s; s = (unsigned short) internalPacket->dataBitLength; bitStream->WriteAlignedVar16((const char*)& s); + bitLength += 8*2; + + if ( internalPacket->reliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + bitLength += 8*3; // bitStream->Write(internalPacket->reliableMessageNumber); // Message sequence number + // bitStream->AlignWriteToByteBoundary(); // Potentially nothing else to write + + + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED + ) + { + bitLength += 8*3;; // bitStream->Write(internalPacket->_sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + } + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + { + bitLength += 8*3; // bitStream->Write(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + bitLength += 8*1; // tempChar=internalPacket->orderingChannel; bitStream->WriteAlignedVar8((const char*)& tempChar); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, write one byte + } + if (internalPacket->splitPacketCount>0) + { + bitLength += 8*4; // bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketCount); RakAssert(sizeof(SplitPacketIndexType)==4); // Only needed if splitPacketCount>0. 4 bytes + bitLength += 8*sizeof(SplitPacketIdType); // bitStream->WriteAlignedVar16((const char*)& internalPacket->splitPacketId); RakAssert(sizeof(SplitPacketIdType)==2); // Only needed if splitPacketCount>0. + bitLength += 8*4; // bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes + } + + return bitLength; +} + +//------------------------------------------------------------------------------------------------------- +// Parse an internalPacket and create a bitstream to represent this data +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket, CCTimeType curTime ) +{ + (void) curTime; + + BitSize_t start = bitStream->GetNumberOfBitsUsed(); + unsigned char tempChar; + + // (Incoming data may be all zeros due to padding) + bitStream->AlignWriteToByteBoundary(); // Potentially unaligned + if (internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT) + tempChar=UNRELIABLE; + else if (internalPacket->reliability==RELIABLE_WITH_ACK_RECEIPT) + tempChar=RELIABLE; + else if (internalPacket->reliability==RELIABLE_ORDERED_WITH_ACK_RECEIPT) + tempChar=RELIABLE_ORDERED; + else + tempChar=(unsigned char)internalPacket->reliability; + + bitStream->WriteBits( (const unsigned char *)&tempChar, 3, true ); // 3 bits to write reliability. + + bool hasSplitPacket = internalPacket->splitPacketCount>0; bitStream->Write(hasSplitPacket); // Write 1 bit to indicate if splitPacketCount>0 + bitStream->AlignWriteToByteBoundary(); + RakAssert(internalPacket->dataBitLength < 65535); + unsigned short s; s = (unsigned short) internalPacket->dataBitLength; bitStream->WriteAlignedVar16((const char*)& s); + if ( internalPacket->reliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + bitStream->Write(internalPacket->reliableMessageNumber); // Used for all reliable types + bitStream->AlignWriteToByteBoundary(); // Potentially nothing else to write + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED + ) + { + bitStream->Write(internalPacket->sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + } + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + { + bitStream->Write(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + tempChar=internalPacket->orderingChannel; bitStream->WriteAlignedVar8((const char*)& tempChar); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, write one byte + } + + if (internalPacket->splitPacketCount>0) + { + // printf("Write before\n"); + // bitStream->PrintBits(); + + bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketCount); RakAssert(sizeof(SplitPacketIndexType)==4); // Only needed if splitPacketCount>0. 4 bytes + bitStream->WriteAlignedVar16((const char*)& internalPacket->splitPacketId); RakAssert(sizeof(SplitPacketIdType)==2); // Only needed if splitPacketCount>0. + bitStream->WriteAlignedVar32((const char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes + + // printf("Write after\n"); + // bitStream->PrintBits(); + } + + // Write the actual data. + bitStream->WriteAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); + + return bitStream->GetNumberOfBitsUsed() - start; +} + +//------------------------------------------------------------------------------------------------------- +// Parse a bitstream and create an internal packet to represent this data +//------------------------------------------------------------------------------------------------------- +InternalPacket* ReliabilityLayer::CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, CCTimeType time ) +{ + bool bitStreamSucceeded; + InternalPacket* internalPacket; + unsigned char tempChar; + bool hasSplitPacket=false; + bool readSuccess; + + if ( bitStream->GetNumberOfUnreadBits() < (int) sizeof( internalPacket->reliableMessageNumber ) * 8 ) + return 0; // leftover bits + + internalPacket = AllocateFromInternalPacketPool(); + if (internalPacket==0) + { + // Out of memory + RakAssert(0); + return 0; + } + internalPacket->creationTime = time; + + // (Incoming data may be all zeros due to padding) + bitStream->AlignReadToByteBoundary(); // Potentially unaligned + bitStream->ReadBits( ( unsigned char* ) ( &( tempChar ) ), 3 ); + internalPacket->reliability = ( const PacketReliability ) tempChar; + readSuccess=bitStream->Read(hasSplitPacket); // Read 1 bit to indicate if splitPacketCount>0 + bitStream->AlignReadToByteBoundary(); + unsigned short s; bitStream->ReadAlignedVar16((char*)&s); internalPacket->dataBitLength=s; // Length of message (2 bytes) + if ( internalPacket->reliability == RELIABLE || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED + // I don't write ACK_RECEIPT to the remote system +// || +// internalPacket->reliability == RELIABLE_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || +// internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + bitStream->Read(internalPacket->reliableMessageNumber); // Message sequence number + else + internalPacket->reliableMessageNumber=(MessageNumberType)(const uint32_t)-1; + bitStream->AlignReadToByteBoundary(); // Potentially nothing else to Read + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED + ) + { + bitStream->Read(internalPacket->sequencingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. + } + + if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_SEQUENCED || + internalPacket->reliability == RELIABLE_ORDERED || + internalPacket->reliability == RELIABLE_ORDERED_WITH_ACK_RECEIPT + ) + { + bitStream->Read(internalPacket->orderingIndex); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 4 bytes. + readSuccess=bitStream->ReadAlignedVar8((char*)& internalPacket->orderingChannel); // Used for UNRELIABLE_SEQUENCED, RELIABLE_SEQUENCED, RELIABLE_ORDERED. 5 bits needed, Read one byte + } + else + internalPacket->orderingChannel=0; + + if (hasSplitPacket) + { +// printf("Read before\n"); +// bitStream->PrintBits(); + + bitStream->ReadAlignedVar32((char*)& internalPacket->splitPacketCount); // Only needed if splitPacketCount>0. 4 bytes + bitStream->ReadAlignedVar16((char*)& internalPacket->splitPacketId); // Only needed if splitPacketCount>0. + readSuccess=bitStream->ReadAlignedVar32((char*)& internalPacket->splitPacketIndex); // Only needed if splitPacketCount>0. 4 bytes + RakAssert(readSuccess); + +// printf("Read after\n"); +// bitStream->PrintBits(); + } + else + { + internalPacket->splitPacketCount=0; + } + + if (readSuccess==false || + internalPacket->dataBitLength==0 || + internalPacket->reliability>=NUMBER_OF_RELIABILITIES || + internalPacket->orderingChannel>=32 || + (hasSplitPacket && (internalPacket->splitPacketIndex >= internalPacket->splitPacketCount))) + { + // If this assert hits, encoding is garbage + RakAssert("Encoding is garbage" && 0); + ReleaseToInternalPacketPool( internalPacket ); + return 0; + } + + // Allocate memory to hold our data + AllocInternalPacketData(internalPacket, BITS_TO_BYTES( internalPacket->dataBitLength ), false, _FILE_AND_LINE_ ); + RakAssert(BITS_TO_BYTES( internalPacket->dataBitLength )data == 0) + { + RakAssert("Out of memory in ReliabilityLayer::CreateInternalPacketFromBitStream" && 0); + notifyOutOfMemory(_FILE_AND_LINE_); + ReleaseToInternalPacketPool( internalPacket ); + return 0; + } + + // Set the last byte to 0 so if ReadBits does not read a multiple of 8 the last bits are 0'ed out + internalPacket->data[ BITS_TO_BYTES( internalPacket->dataBitLength ) - 1 ] = 0; + + // Read the data the packet holds + bitStreamSucceeded = bitStream->ReadAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); + + if ( bitStreamSucceeded == false ) + { + // If this hits, most likely the variable buff is too small in RunUpdateCycle in RakPeer.cpp + RakAssert("Couldn't read all the data" && 0); + + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + return 0; + } + + return internalPacket; +} + + +//------------------------------------------------------------------------------------------------------- +// Get the SHA1 code +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::GetSHA1( unsigned char * const buffer, unsigned int + nbytes, char code[ SHA1_LENGTH ] ) +{ + CSHA1 sha1; + + sha1.Reset(); + sha1.Update( ( unsigned char* ) buffer, nbytes ); + sha1.Final(); + memcpy( code, sha1.GetHash(), SHA1_LENGTH ); +} + +//------------------------------------------------------------------------------------------------------- +// Check the SHA1 code +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * + const buffer, unsigned int nbytes ) +{ + char code2[ SHA1_LENGTH ]; + GetSHA1( buffer, nbytes, code2 ); + + for ( int i = 0; i < SHA1_LENGTH; i++ ) + if ( code[ i ] != code2[ i ] ) + return false; + + return true; +} + +//------------------------------------------------------------------------------------------------------- +// Search the specified list for sequenced packets on the specified ordering +// stream, optionally skipping those with splitPacketId, and delete them +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::List&theList, int splitPacketId ) +{ + unsigned i = 0; + + while ( i < theList.Size() ) + { + if ( ( + theList[ i ]->reliability == RELIABLE_SEQUENCED || + theList[ i ]->reliability == UNRELIABLE_SEQUENCED +// || +// theList[ i ]->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || +// theList[ i ]->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) && + theList[ i ]->orderingChannel == orderingChannel && ( splitPacketId == -1 || theList[ i ]->splitPacketId != (unsigned int) splitPacketId ) ) + { + InternalPacket * internalPacket = theList[ i ]; + theList.RemoveAtIndex( i ); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + } + + else + i++; + } +} + +//------------------------------------------------------------------------------------------------------- +// Search the specified list for sequenced packets with a value less than orderingIndex and delete them +// Note - I added functionality so you can use the Queue as a list (in this case for searching) but it is less efficient to do so than a regular list +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::Queue&theList ) +{ + InternalPacket * internalPacket; + int listSize = theList.Size(); + int i = 0; + + while ( i < listSize ) + { + if ( ( + theList[ i ]->reliability == RELIABLE_SEQUENCED || + theList[ i ]->reliability == UNRELIABLE_SEQUENCED +// || +// theList[ i ]->reliability == RELIABLE_SEQUENCED_WITH_ACK_RECEIPT || +// theList[ i ]->reliability == UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) && theList[ i ]->orderingChannel == orderingChannel ) + { + internalPacket = theList[ i ]; + theList.RemoveAtIndex( i ); + FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + listSize--; + } + + else + i++; + } +} + +//------------------------------------------------------------------------------------------------------- +// Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ) +{ + OrderingIndexType maxRange = (OrderingIndexType) (const uint32_t)-1; + + if ( waitingForPacketOrderingIndex > maxRange/(OrderingIndexType)2 ) + { + if ( newPacketOrderingIndex >= waitingForPacketOrderingIndex - maxRange/(OrderingIndexType)2+(OrderingIndexType)1 && newPacketOrderingIndex < waitingForPacketOrderingIndex ) + { + return true; + } + } + + else + if ( newPacketOrderingIndex >= ( OrderingIndexType ) ( waitingForPacketOrderingIndex - (( OrderingIndexType ) maxRange/(OrderingIndexType)2+(OrderingIndexType)1) ) || + newPacketOrderingIndex < waitingForPacketOrderingIndex ) + { + return true; + } + + // Old packet + return false; +} + +//------------------------------------------------------------------------------------------------------- +// Split the passed packet into chunks under MTU_SIZEbytes (including headers) and save those new chunks +// Optimized version +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SplitPacket( InternalPacket *internalPacket ) +{ + // Doing all sizes in bytes in this function so I don't write partial bytes with split packets + internalPacket->splitPacketCount = 1; // This causes GetMessageHeaderLengthBits to account for the split packet header + unsigned int headerLength = (unsigned int) BITS_TO_BYTES( GetMessageHeaderLengthBits( internalPacket ) ); + unsigned int dataByteLength = (unsigned int) BITS_TO_BYTES( internalPacket->dataBitLength ); + int maximumSendBlockBytes, byteOffset, bytesToSend; + SplitPacketIndexType splitPacketIndex; + int i; + InternalPacket **internalPacketArray; + + maximumSendBlockBytes = GetMaxDatagramSizeExcludingMessageHeaderBytes() - BITS_TO_BYTES(GetMaxMessageHeaderLengthBits()); + + // Calculate how many packets we need to create + internalPacket->splitPacketCount = ( ( dataByteLength - 1 ) / ( maximumSendBlockBytes ) + 1 ); + + // Optimization + // internalPacketArray = RakNet::OP_NEW(internalPacket->splitPacketCount, _FILE_AND_LINE_ ); + bool usedAlloca=false; + + if (sizeof( InternalPacket* ) * internalPacket->splitPacketCount < MAX_ALLOCA_STACK_ALLOCATION) + { + internalPacketArray = ( InternalPacket** ) alloca( sizeof( InternalPacket* ) * internalPacket->splitPacketCount ); + usedAlloca=true; + } + else + + internalPacketArray = (InternalPacket**) rakMalloc_Ex( sizeof(InternalPacket*) * internalPacket->splitPacketCount, _FILE_AND_LINE_ ); + + for ( i = 0; i < ( int ) internalPacket->splitPacketCount; i++ ) + { + internalPacketArray[ i ] = AllocateFromInternalPacketPool(); + + //internalPacketArray[ i ] = (InternalPacket*) alloca( sizeof( InternalPacket ) ); + // internalPacketArray[ i ] = sendPacketSet[internalPacket->priority].WriteLock(); + *internalPacketArray[ i ]=*internalPacket; + internalPacketArray[ i ]->messageNumberAssigned=false; + + if (i!=0) + internalPacket->messageInternalOrder = internalOrderIndex++; + } + + // This identifies which packet this is in the set + splitPacketIndex = 0; + + InternalPacketRefCountedData *refCounter=0; + + // Do a loop to send out all the packets + do + { + byteOffset = splitPacketIndex * maximumSendBlockBytes; + bytesToSend = dataByteLength - byteOffset; + + if ( bytesToSend > maximumSendBlockBytes ) + bytesToSend = maximumSendBlockBytes; + + // Copy over our chunk of data + + AllocInternalPacketData(internalPacketArray[ splitPacketIndex ], &refCounter, internalPacket->data, internalPacket->data + byteOffset); + // internalPacketArray[ splitPacketIndex ]->data = (unsigned char*) rakMalloc_Ex( bytesToSend, _FILE_AND_LINE_ ); + // memcpy( internalPacketArray[ splitPacketIndex ]->data, internalPacket->data + byteOffset, bytesToSend ); + + if ( bytesToSend != maximumSendBlockBytes ) + internalPacketArray[ splitPacketIndex ]->dataBitLength = internalPacket->dataBitLength - splitPacketIndex * ( maximumSendBlockBytes << 3 ); + else + internalPacketArray[ splitPacketIndex ]->dataBitLength = bytesToSend << 3; + + internalPacketArray[ splitPacketIndex ]->splitPacketIndex = splitPacketIndex; + internalPacketArray[ splitPacketIndex ]->splitPacketId = splitPacketId; + internalPacketArray[ splitPacketIndex ]->splitPacketCount = internalPacket->splitPacketCount; + RakAssert(internalPacketArray[ splitPacketIndex ]->dataBitLengthsplitPacketCount ); + + splitPacketId++; // It's ok if this wraps to 0 + + // InternalPacket *workingPacket; + + // Tell the heap we are going to push a list of elements where each element in the list follows the heap order + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthsplitPacketCount; i++ ) + { + internalPacketArray[ i ]->headerLength=headerLength; + RakAssert(internalPacketArray[ i ]->dataBitLengthpriority ].Push( internalPacketArray[ i ], _FILE_AND_LINE_ ); + RakAssert(internalPacketArray[ i ]->dataBitLengthmessageNumberAssigned==false); + outgoingPacketBuffer.PushSeries(GetNextWeight(internalPacketArray[ i ]->priority), internalPacketArray[ i ], _FILE_AND_LINE_); + RakAssert(outgoingPacketBuffer.Size()==0 || outgoingPacketBuffer.Peek()->dataBitLengthpriority]++; + statistics.bytesInSendBuffer[(int)(int)internalPacketArray[ i ]->priority]+=(double) BITS_TO_BYTES(internalPacketArray[ i ]->dataBitLength); + // workingPacket=sendPacketSet[internalPacket->priority].WriteLock(); + // memcpy(workingPacket, internalPacketArray[ i ], sizeof(InternalPacket)); + // sendPacketSet[internalPacket->priority].WriteUnlock(); + } + + // Do not delete, original is referenced by all split packets to avoid numerous allocations. See AllocInternalPacketData above + // FreeInternalPacketData(internalPacket, _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool( internalPacket ); + + if (usedAlloca==false) + rakFree_Ex(internalPacketArray, _FILE_AND_LINE_ ); +} + +//------------------------------------------------------------------------------------------------------- +// Insert a packet into the split packet list +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::InsertIntoSplitPacketList( InternalPacket * internalPacket, CCTimeType time ) +{ + bool objectExists; + unsigned index; + index=splitPacketChannelList.GetIndexFromKey(internalPacket->splitPacketId, &objectExists); + if (objectExists==false) + { + SplitPacketChannel *newChannel = RakNet::OP_NEW( __FILE__, __LINE__ ); +#if PREALLOCATE_LARGE_MESSAGES==1 + index=splitPacketChannelList.Insert(internalPacket->splitPacketId, newChannel, true, __FILE__,__LINE__); + newChannel->returnedPacket=CreateInternalPacketCopy( internalPacket, 0, 0, time ); + newChannel->gotFirstPacket=false; + newChannel->splitPacketsArrived=0; + AllocInternalPacketData(newChannel->returnedPacket, BITS_TO_BYTES( internalPacket->dataBitLength*internalPacket->splitPacketCount ), false, __FILE__, __LINE__ ); + RakAssert(newChannel->returnedPacket->data); +#else + newChannel->firstPacket=0; + index=splitPacketChannelList.Insert(internalPacket->splitPacketId, newChannel, true, __FILE__,__LINE__); + // Preallocate to the final size, to avoid runtime copies + newChannel->splitPacketList.Preallocate(internalPacket->splitPacketCount, __FILE__,__LINE__); + +#endif + } + +#if PREALLOCATE_LARGE_MESSAGES==1 + splitPacketChannelList[index]->lastUpdateTime=time; + splitPacketChannelList[index]->splitPacketsArrived++; + splitPacketChannelList[index]->returnedPacket->dataBitLength+=internalPacket->dataBitLength; + + bool dealloc; + if (internalPacket->splitPacketIndex==0) + { + splitPacketChannelList[index]->gotFirstPacket=true; + splitPacketChannelList[index]->stride=BITS_TO_BYTES(internalPacket->dataBitLength); + + for (unsigned int j=0; j < splitPacketChannelList[index]->splitPacketList.Size(); j++) + { + memcpy(splitPacketChannelList[index]->returnedPacket->data+internalPacket->splitPacketIndex*splitPacketChannelList[index]->stride, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); + FreeInternalPacketData(splitPacketChannelList[index]->splitPacketList[j], __FILE__, __LINE__ ); + ReleaseToInternalPacketPool(splitPacketChannelList[index]->splitPacketList[j]); + } + + memcpy(splitPacketChannelList[index]->returnedPacket->data, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); + splitPacketChannelList[index]->splitPacketList.Clear(true,__FILE__,__LINE__); + dealloc=true; + } + else + { + if (splitPacketChannelList[index]->gotFirstPacket==true) + { + memcpy(splitPacketChannelList[index]->returnedPacket->data+internalPacket->splitPacketIndex*splitPacketChannelList[index]->stride, internalPacket->data, (size_t) BITS_TO_BYTES(internalPacket->dataBitLength)); + dealloc=true; + } + else + { + splitPacketChannelList[index]->splitPacketList.Push(internalPacket,__FILE__,__LINE__); + dealloc=false; + } + } + + if (splitPacketChannelList[index]->gotFirstPacket==true && + splitMessageProgressInterval && + // splitPacketChannelList[index]->firstPacket && + // splitPacketChannelList[index]->splitPacketList.Size()!=splitPacketChannelList[index]->firstPacket->splitPacketCount && + // (splitPacketChannelList[index]->splitPacketList.Size()%splitMessageProgressInterval)==0 + splitPacketChannelList[index]->gotFirstPacket && + splitPacketChannelList[index]->splitPacketsArrived!=splitPacketChannelList[index]->returnedPacket->splitPacketCount && + (splitPacketChannelList[index]->splitPacketsArrived%splitMessageProgressInterval)==0 + ) + { + // Return ID_DOWNLOAD_PROGRESS + // Write splitPacketIndex (SplitPacketIndexType) + // Write splitPacketCount (SplitPacketIndexType) + // Write byteLength (4) + // Write data, splitPacketChannelList[index]->splitPacketList[0]->data + InternalPacket *progressIndicator = AllocateFromInternalPacketPool(); + // unsigned int len = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + (unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); + unsigned int l = (unsigned int) splitPacketChannelList[index]->stride; + const unsigned int len = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + l; + AllocInternalPacketData(progressIndicator, len, false, __FILE__, __LINE__ ); + progressIndicator->dataBitLength=BYTES_TO_BITS(len); + progressIndicator->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; + unsigned int temp; + // temp=splitPacketChannelList[index]->splitPacketList.Size(); + temp=splitPacketChannelList[index]->splitPacketsArrived; + memcpy(progressIndicator->data+sizeof(MessageID), &temp, sizeof(unsigned int)); + temp=(unsigned int)internalPacket->splitPacketCount; + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*1, &temp, sizeof(unsigned int)); + // temp=(unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); + temp=(unsigned int) BITS_TO_BYTES(l); + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*2, &temp, sizeof(unsigned int)); + //memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->firstPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength)); + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->returnedPacket->data, (size_t) BITS_TO_BYTES(l)); + } + + if (dealloc) + { + FreeInternalPacketData(internalPacket, __FILE__, __LINE__ ); + ReleaseToInternalPacketPool(internalPacket); + } +#else + splitPacketChannelList[index]->splitPacketList.Insert(internalPacket, __FILE__, __LINE__ ); + splitPacketChannelList[index]->lastUpdateTime=time; + + if (internalPacket->splitPacketIndex==0) + splitPacketChannelList[index]->firstPacket=internalPacket; + + if (splitMessageProgressInterval && + splitPacketChannelList[index]->firstPacket && + splitPacketChannelList[index]->splitPacketList.Size()!=splitPacketChannelList[index]->firstPacket->splitPacketCount && + (splitPacketChannelList[index]->splitPacketList.Size()%splitMessageProgressInterval)==0) + { + // Return ID_DOWNLOAD_PROGRESS + // Write splitPacketIndex (SplitPacketIndexType) + // Write splitPacketCount (SplitPacketIndexType) + // Write byteLength (4) + // Write data, splitPacketChannelList[index]->splitPacketList[0]->data + InternalPacket *progressIndicator = AllocateFromInternalPacketPool(); + unsigned int length = sizeof(MessageID) + sizeof(unsigned int)*2 + sizeof(unsigned int) + (unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); + AllocInternalPacketData(progressIndicator, length, false, __FILE__, __LINE__ ); + progressIndicator->dataBitLength=BYTES_TO_BITS(length); + progressIndicator->data[0]=(MessageID)ID_DOWNLOAD_PROGRESS; + unsigned int temp; + temp=splitPacketChannelList[index]->splitPacketList.Size(); + memcpy(progressIndicator->data+sizeof(MessageID), &temp, sizeof(unsigned int)); + temp=(unsigned int)internalPacket->splitPacketCount; + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*1, &temp, sizeof(unsigned int)); + temp=(unsigned int) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength); + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*2, &temp, sizeof(unsigned int)); + + memcpy(progressIndicator->data+sizeof(MessageID)+sizeof(unsigned int)*3, splitPacketChannelList[index]->firstPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannelList[index]->firstPacket->dataBitLength)); + outputQueue.Push(progressIndicator, __FILE__, __LINE__ ); + } + +#endif +} + +//------------------------------------------------------------------------------------------------------- +// Take all split chunks with the specified splitPacketId and try to +//reconstruct a packet. If we can, allocate and return it. Otherwise return 0 +// Optimized version +//------------------------------------------------------------------------------------------------------- +InternalPacket * ReliabilityLayer::BuildPacketFromSplitPacketList( SplitPacketChannel *splitPacketChannel, CCTimeType time ) +{ +#if PREALLOCATE_LARGE_MESSAGES==1 + InternalPacket *returnedPacket=splitPacketChannel->returnedPacket; + RakNet::OP_DELETE(splitPacketChannel, __FILE__, __LINE__); + (void) time; + return returnedPacket; +#else + unsigned int j; + InternalPacket * internalPacket, *splitPacket; + int splitPacketPartLength; + + // Reconstruct + internalPacket = CreateInternalPacketCopy( splitPacketChannel->splitPacketList[0], 0, 0, time ); + internalPacket->dataBitLength=0; + for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) + internalPacket->dataBitLength+=splitPacketChannel->splitPacketList[j]->dataBitLength; + splitPacketPartLength=BITS_TO_BYTES(splitPacketChannel->firstPacket->dataBitLength); + + internalPacket->data = (unsigned char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES( internalPacket->dataBitLength ), _FILE_AND_LINE_ ); + internalPacket->allocationScheme=InternalPacket::NORMAL; + + for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) + { + splitPacket=splitPacketChannel->splitPacketList[j]; + memcpy(internalPacket->data+splitPacket->splitPacketIndex*splitPacketPartLength, splitPacket->data, (size_t) BITS_TO_BYTES(splitPacketChannel->splitPacketList[j]->dataBitLength)); + } + + for (j=0; j < splitPacketChannel->splitPacketList.Size(); j++) + { + FreeInternalPacketData(splitPacketChannel->splitPacketList[j], _FILE_AND_LINE_ ); + ReleaseToInternalPacketPool(splitPacketChannel->splitPacketList[j]); + } + RakNet::OP_DELETE(splitPacketChannel, __FILE__, __LINE__); + + return internalPacket; +#endif +} +//------------------------------------------------------------------------------------------------------- +InternalPacket * ReliabilityLayer::BuildPacketFromSplitPacketList( SplitPacketIdType splitPacketId, CCTimeType time, + SOCKET s, SystemAddress &systemAddress, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, + BitStream &updateBitStream) +{ + unsigned int i; + bool objectExists; + SplitPacketChannel *splitPacketChannel; + InternalPacket * internalPacket; + + i=splitPacketChannelList.GetIndexFromKey(splitPacketId, &objectExists); + splitPacketChannel=splitPacketChannelList[i]; + +#if PREALLOCATE_LARGE_MESSAGES==1 + if (splitPacketChannel->splitPacketsArrived==splitPacketChannel->returnedPacket->splitPacketCount) +#else + if (splitPacketChannel->splitPacketList.Size()==splitPacketChannel->splitPacketList[0]->splitPacketCount) +#endif + { + // Ack immediately, because for large files this can take a long time + SendACKs(s, systemAddress, time, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, updateBitStream); + internalPacket=BuildPacketFromSplitPacketList(splitPacketChannel,time); + splitPacketChannelList.RemoveAtIndex(i); + return internalPacket; + } + else + { + return 0; + } +} +/* +//------------------------------------------------------------------------------------------------------- +// Delete any unreliable split packets that have long since expired +void ReliabilityLayer::DeleteOldUnreliableSplitPackets( CCTimeType time ) +{ +unsigned i,j; +i=0; +while (i < splitPacketChannelList.Size()) +{ +#if CC_TIME_TYPE_BYTES==4 +if (time > splitPacketChannelList[i]->lastUpdateTime + timeoutTime && +#else +if (time > splitPacketChannelList[i]->lastUpdateTime + (CCTimeType)timeoutTime*(CCTimeType)1000 && +#endif +(splitPacketChannelList[i]->splitPacketList[0]->reliability==UNRELIABLE || splitPacketChannelList[i]->splitPacketList[0]->reliability==UNRELIABLE_SEQUENCED)) +{ +for (j=0; j < splitPacketChannelList[i]->splitPacketList.Size(); j++) +{ +RakNet::OP_DELETE_ARRAY(splitPacketChannelList[i]->splitPacketList[j]->data, _FILE_AND_LINE_); +ReleaseToInternalPacketPool(splitPacketChannelList[i]->splitPacketList[j]); +} +RakNet::OP_DELETE(splitPacketChannelList[i], _FILE_AND_LINE_); +splitPacketChannelList.RemoveAtIndex(i); +} +else +i++; +} +} +*/ + +//------------------------------------------------------------------------------------------------------- +// Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. +// Does not copy any split data parameters as that information is always generated does not have any reason to be copied +//------------------------------------------------------------------------------------------------------- +InternalPacket * ReliabilityLayer::CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, CCTimeType time ) +{ + InternalPacket * copy = AllocateFromInternalPacketPool(); +#ifdef _DEBUG + // Remove accessing undefined memory error + memset( copy, 255, sizeof( InternalPacket ) ); +#endif + // Copy over our chunk of data + + if ( dataByteLength > 0 ) + { + AllocInternalPacketData(copy, BITS_TO_BYTES(dataByteLength ), false, _FILE_AND_LINE_ ); + memcpy( copy->data, original->data + dataByteOffset, dataByteLength ); + } + else + copy->data = 0; + + copy->dataBitLength = dataByteLength << 3; + copy->creationTime = time; + copy->nextActionTime = 0; + copy->orderingIndex = original->orderingIndex; + copy->sequencingIndex = original->sequencingIndex; + copy->orderingChannel = original->orderingChannel; + copy->reliableMessageNumber = original->reliableMessageNumber; + copy->priority = original->priority; + copy->reliability = original->reliability; +#if PREALLOCATE_LARGE_MESSAGES==1 + copy->splitPacketCount = original->splitPacketCount; + copy->splitPacketId = original->splitPacketId; + copy->splitPacketIndex = original->splitPacketIndex; +#endif + + return copy; +} + +//------------------------------------------------------------------------------------------------------- +// Get the specified ordering list +//------------------------------------------------------------------------------------------------------- +/* +DataStructures::LinkedList *ReliabilityLayer::GetOrderingListAtOrderingStream( unsigned char orderingChannel ) +{ + if ( orderingChannel >= orderingList.Size() ) + return 0; + + return orderingList[ orderingChannel ]; +} + +//------------------------------------------------------------------------------------------------------- +// Add the internal packet to the ordering list in order based on order index +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AddToOrderingList( InternalPacket * internalPacket ) +{ + } +*/ + +//------------------------------------------------------------------------------------------------------- +// Inserts a packet into the resend list in order +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::InsertPacketIntoResendList( InternalPacket *internalPacket, CCTimeType time, bool firstResend, bool modifyUnacknowledgedBytes ) +{ + (void) firstResend; + (void) time; + (void) internalPacket; + + AddToListTail(internalPacket, modifyUnacknowledgedBytes); + RakAssert(internalPacket->nextActionTime!=0); + +} + +//------------------------------------------------------------------------------------------------------- +// Were you ever unable to deliver a packet despite retries? +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsDeadConnection( void ) const +{ + return deadConnection; +} + +//------------------------------------------------------------------------------------------------------- +// Causes IsDeadConnection to return true +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::KillConnection( void ) +{ + deadConnection=true; +} + + +//------------------------------------------------------------------------------------------------------- +// Statistics +//------------------------------------------------------------------------------------------------------- +RakNetStatistics * ReliabilityLayer::GetStatistics( RakNetStatistics *rns ) +{ + unsigned i; + RakNet::TimeUS time = RakNet::GetTimeUS(); + uint64_t uint64Denominator; + double doubleDenominator; + + for (i=0; i < RNS_PER_SECOND_METRICS_COUNT; i++) + { + statistics.valueOverLastSecond[i]=bpsMetrics[i].GetBPS1Threadsafe(time); + statistics.runningTotal[i]=bpsMetrics[i].GetTotal1(); + } + + memcpy(rns, &statistics, sizeof(statistics)); + + if (rns->valueOverLastSecond[USER_MESSAGE_BYTES_SENT]+rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT]>0) + rns->packetlossLastSecond=(float)((double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT]/((double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_SENT]+(double) rns->valueOverLastSecond[USER_MESSAGE_BYTES_RESENT])); + else + rns->packetlossLastSecond=0.0f; + + rns->packetlossTotal=0.0f; + uint64Denominator=(rns->runningTotal[USER_MESSAGE_BYTES_SENT]+rns->runningTotal[USER_MESSAGE_BYTES_RESENT]); + if (uint64Denominator!=0&&rns->runningTotal[USER_MESSAGE_BYTES_SENT]/uint64Denominator>0) + { + doubleDenominator=((double) rns->runningTotal[USER_MESSAGE_BYTES_SENT]+(double) rns->runningTotal[USER_MESSAGE_BYTES_RESENT]); + if(doubleDenominator!=0) + { + rns->packetlossTotal=(float)((double) rns->runningTotal[USER_MESSAGE_BYTES_RESENT]/doubleDenominator); + } + } + + rns->isLimitedByCongestionControl=statistics.isLimitedByCongestionControl; + rns->BPSLimitByCongestionControl=statistics.BPSLimitByCongestionControl; + rns->isLimitedByOutgoingBandwidthLimit=statistics.isLimitedByOutgoingBandwidthLimit; + rns->BPSLimitByOutgoingBandwidthLimit=statistics.BPSLimitByOutgoingBandwidthLimit; + + return rns; +} + +//------------------------------------------------------------------------------------------------------- +// Returns the number of packets in the resend queue, not counting holes +//------------------------------------------------------------------------------------------------------- +unsigned int ReliabilityLayer::GetResendListDataSize(void) const +{ + // Not accurate but thread-safe. The commented version might crash if the queue is cleared while we loop through it + // return resendTree.Size(); + return statistics.messagesInResendBuffer; +} + +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::AckTimeout(RakNet::Time curTime) +{ + // I check timeLastDatagramArrived-curTime because with threading it is possible that timeLastDatagramArrived is + // slightly greater than curTime, in which case this is NOT an ack timeout + return (timeLastDatagramArrived-curTime)>10000 && curTime-timeLastDatagramArrived>timeoutTime; +} +//------------------------------------------------------------------------------------------------------- +CCTimeType ReliabilityLayer::GetNextSendTime(void) const +{ + return nextSendTime; +} +//------------------------------------------------------------------------------------------------------- +CCTimeType ReliabilityLayer::GetTimeBetweenPackets(void) const +{ + return timeBetweenPackets; +} +//------------------------------------------------------------------------------------------------------- +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 +CCTimeType ReliabilityLayer::GetAckPing(void) const +{ + return ackPing; +} +#endif +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ResetPacketsAndDatagrams(void) +{ + packetsToSendThisUpdate.Clear(true, _FILE_AND_LINE_); + packetsToDeallocThisUpdate.Clear(true, _FILE_AND_LINE_); + packetsToSendThisUpdateDatagramBoundaries.Clear(true, _FILE_AND_LINE_); + datagramsToSendThisUpdateIsPair.Clear(true, _FILE_AND_LINE_); + datagramSizesInBytes.Clear(true, _FILE_AND_LINE_); + datagramSizeSoFar=0; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::PushPacket(CCTimeType time, InternalPacket *internalPacket, bool isReliable) +{ + BitSize_t bitsForThisPacket=BYTES_TO_BITS(BITS_TO_BYTES(internalPacket->dataBitLength)+BITS_TO_BYTES(internalPacket->headerLength)); + datagramSizeSoFar+=bitsForThisPacket; + RakAssert(BITS_TO_BYTES(datagramSizeSoFar)headerLength==GetMessageHeaderLengthBits(internalPacket)); + +// This code tells me how much time elapses between when you send, and when the message actually goes out +// if (internalPacket->data[0]==0) +// { +// RakNet::TimeMS t; +// RakNet::BitStream bs(internalPacket->data+1,sizeof(t),false); +// bs.Read(t); +// RakNet::TimeMS curTime=RakNet::GetTimeMS(); +// RakNet::TimeMS diff = curTime-t; +// } + + congestionManager.OnSendBytes(time, BITS_TO_BYTES(internalPacket->dataBitLength)+BITS_TO_BYTES(internalPacket->headerLength)); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::PushDatagram(void) +{ + if (datagramSizeSoFar>0) + { + packetsToSendThisUpdateDatagramBoundaries.Push(packetsToSendThisUpdate.Size(), _FILE_AND_LINE_ ); + datagramsToSendThisUpdateIsPair.Push(false, _FILE_AND_LINE_ ); + RakAssert(BITS_TO_BYTES(datagramSizeSoFar)=2) + { + datagramsToSendThisUpdateIsPair[datagramsToSendThisUpdateIsPair.Size()-2]=true; + datagramsToSendThisUpdateIsPair[datagramsToSendThisUpdateIsPair.Size()-1]=true; + return true; + } + return false; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ClearPacketsAndDatagrams(void) +{ + unsigned int i; + for (i=0; i < packetsToDeallocThisUpdate.Size(); i++) + { + // packetsToDeallocThisUpdate holds a boolean indicating if packetsToSendThisUpdate at this index should be freed + if (packetsToDeallocThisUpdate[i]) + { + RemoveFromUnreliableLinkedList(packetsToSendThisUpdate[i]); + FreeInternalPacketData(packetsToSendThisUpdate[i], _FILE_AND_LINE_ ); + // if (keepInternalPacketIfNeedsAck==false || packetsToSendThisUpdate[i]->reliabilityresendNext=internalPacket; + internalPacket->resendPrev=internalPacket; + resendLinkedListHead=internalPacket; + return; + } + internalPacket->resendPrev->resendNext = internalPacket->resendNext; + internalPacket->resendNext->resendPrev = internalPacket->resendPrev; + internalPacket->resendNext=resendLinkedListHead; + internalPacket->resendPrev=resendLinkedListHead->resendPrev; + internalPacket->resendPrev->resendNext=internalPacket; + resendLinkedListHead->resendPrev=internalPacket; + resendLinkedListHead=internalPacket; + RakAssert(internalPacket->headerLength+internalPacket->dataBitLength>0); + + //ValidateResendList(); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::RemoveFromList(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes) +{ + InternalPacket *newPosition; + internalPacket->resendPrev->resendNext = internalPacket->resendNext; + internalPacket->resendNext->resendPrev = internalPacket->resendPrev; + newPosition = internalPacket->resendNext; + if ( internalPacket == resendLinkedListHead ) + resendLinkedListHead = newPosition; + if (resendLinkedListHead==internalPacket) + resendLinkedListHead=0; + + if (modifyUnacknowledgedBytes) + { + RakAssert(unacknowledgedBytes>=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength)); + unacknowledgedBytes-=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); + // printf("-unacknowledgedBytes:%i ", unacknowledgedBytes); + + +// ValidateResendList(); + } +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AddToListTail(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes) +{ + if (modifyUnacknowledgedBytes) + { + unacknowledgedBytes+=BITS_TO_BYTES(internalPacket->headerLength+internalPacket->dataBitLength); + // printf("+unacknowledgedBytes:%i ", unacknowledgedBytes); + } + + if (resendLinkedListHead==0) + { + internalPacket->resendNext=internalPacket; + internalPacket->resendPrev=internalPacket; + resendLinkedListHead=internalPacket; + return; + } + internalPacket->resendNext=resendLinkedListHead; + internalPacket->resendPrev=resendLinkedListHead->resendPrev; + internalPacket->resendPrev->resendNext=internalPacket; + resendLinkedListHead->resendPrev=internalPacket; + +// ValidateResendList(); + +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::PopListHead(bool modifyUnacknowledgedBytes) +{ + RakAssert(resendLinkedListHead!=0); + RemoveFromList(resendLinkedListHead, modifyUnacknowledgedBytes); +} +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::IsResendQueueEmpty(void) const +{ + return resendLinkedListHead==0; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::SendACKs(SOCKET s, SystemAddress &systemAddress, CCTimeType time, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream) +{ + BitSize_t maxDatagramPayload = GetMaxDatagramSizeExcludingMessageHeaderBits(); + + while (acknowlegements.Size()>0) + { + // Send acks + updateBitStream.Reset(); + DatagramHeaderFormat dhf; + dhf.isACK=true; + dhf.isNAK=false; + dhf.isPacketPair=false; +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + dhf.sourceSystemTime=time; +#endif + double B; + double AS; + bool hasBAndAS; + if (remoteSystemNeedsBAndAS) + { + congestionManager.OnSendAckGetBAndAS(time, &hasBAndAS,&B,&AS); + dhf.AS=(float)AS; + dhf.hasBAndAS=hasBAndAS; + } + else + dhf.hasBAndAS=false; +#if INCLUDE_TIMESTAMP_WITH_DATAGRAMS==1 + dhf.sourceSystemTime=nextAckTimeToSend; +#endif + // dhf.B=(float)B; + updateBitStream.Reset(); + dhf.Serialize(&updateBitStream); + CC_DEBUG_PRINTF_1("AckSnd "); + acknowlegements.Serialize(&updateBitStream, maxDatagramPayload, true); + SendBitStream( s, systemAddress, &updateBitStream, rnr, remotePortRakNetWasStartedOn_PS3, extraSocketOptions, time ); + congestionManager.OnSendAck(time,updateBitStream.GetNumberOfBytesUsed()); + + // I think this is causing a bug where if the estimated bandwidth is very low for the recipient, only acks ever get sent + // congestionManager.OnSendBytes(time,UDP_HEADER_SIZE+updateBitStream.GetNumberOfBytesUsed()); + } +} +/* +//------------------------------------------------------------------------------------------------------- +ReliabilityLayer::DatagramMessageIDList* ReliabilityLayer::AllocateFromDatagramMessageIDPool(void) +{ +DatagramMessageIDList*s; +s=datagramMessageIDPool.Allocate( _FILE_AND_LINE_ ); +// Call new operator, memoryPool doesn't do this +s = new ((void*)s) DatagramMessageIDList; +return s; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ReleaseToDatagramMessageIDPool(DatagramMessageIDList* d) +{ +d->~DatagramMessageIDList(); +datagramMessageIDPool.Release(d); +} +*/ +//------------------------------------------------------------------------------------------------------- +InternalPacket* ReliabilityLayer::AllocateFromInternalPacketPool(void) +{ + InternalPacket *ip = internalPacketPool.Allocate( _FILE_AND_LINE_ ); + ip->reliableMessageNumber = (MessageNumberType) (const uint32_t)-1; + ip->messageNumberAssigned=false; + ip->nextActionTime = 0; + ip->splitPacketCount = 0; + ip->allocationScheme=InternalPacket::NORMAL; + ip->data=0; + return ip; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ReleaseToInternalPacketPool(InternalPacket *ip) +{ + internalPacketPool.Release(ip, _FILE_AND_LINE_); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::RemoveFromUnreliableLinkedList(InternalPacket *internalPacket) +{ + if (internalPacket->reliability==UNRELIABLE || + internalPacket->reliability==UNRELIABLE_SEQUENCED || + internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT +// || +// internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + { + InternalPacket *newPosition; + internalPacket->unreliablePrev->unreliableNext = internalPacket->unreliableNext; + internalPacket->unreliableNext->unreliablePrev = internalPacket->unreliablePrev; + newPosition = internalPacket->unreliableNext; + if ( internalPacket == unreliableLinkedListHead ) + unreliableLinkedListHead = newPosition; + if (unreliableLinkedListHead==internalPacket) + unreliableLinkedListHead=0; + } +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AddToUnreliableLinkedList(InternalPacket *internalPacket) +{ + if (internalPacket->reliability==UNRELIABLE || + internalPacket->reliability==UNRELIABLE_SEQUENCED || + internalPacket->reliability==UNRELIABLE_WITH_ACK_RECEIPT +// || +// internalPacket->reliability==UNRELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + { + if (unreliableLinkedListHead==0) + { + internalPacket->unreliableNext=internalPacket; + internalPacket->unreliablePrev=internalPacket; + unreliableLinkedListHead=internalPacket; + return; + } + internalPacket->unreliableNext=unreliableLinkedListHead; + internalPacket->unreliablePrev=unreliableLinkedListHead->unreliablePrev; + internalPacket->unreliablePrev->unreliableNext=internalPacket; + unreliableLinkedListHead->unreliablePrev=internalPacket; + } +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::ValidateResendList(void) const +{ +// unsigned int count1=0, count2=0; +// for (unsigned int i=0; i < RESEND_BUFFER_ARRAY_LENGTH; i++) +// if (resendBuffer[i]) +// count1++; +// +// if (resendLinkedListHead) +// { +// InternalPacket *internalPacket = resendLinkedListHead; +// do +// { +// count2++; +// internalPacket=internalPacket->resendNext; +// } while (internalPacket!=resendLinkedListHead); +// } +// RakAssert(count1==count2); +// RakAssert(count2<=RESEND_BUFFER_ARRAY_LENGTH); +} +//------------------------------------------------------------------------------------------------------- +bool ReliabilityLayer::ResendBufferOverflow(void) const +{ + int index1 = sendReliableMessageNumberIndex & (uint32_t) RESEND_BUFFER_ARRAY_MASK; + // int index2 = (sendReliableMessageNumberIndex+(uint32_t)1) & (uint32_t) RESEND_BUFFER_ARRAY_MASK; + RakAssert(index1= datagramHistory.Size()) + return 0; + + *timeSent=datagramHistory[offsetIntoList].timeSent; + return datagramHistory[offsetIntoList].head; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::RemoveFromDatagramHistory(DatagramSequenceNumberType index) +{ + DatagramSequenceNumberType offsetIntoList = index - datagramHistoryPopCount; + MessageNumberNode *mnm = datagramHistory[offsetIntoList].head; + MessageNumberNode *next; + while (mnm) + { + next=mnm->next; + datagramHistoryMessagePool.Release(mnm, _FILE_AND_LINE_); + mnm=next; + } + datagramHistory[offsetIntoList].head=0; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, CCTimeType timeSent) +{ + (void) datagramNumber; + if (datagramHistory.Size()>DATAGRAM_MESSAGE_ID_ARRAY_LENGTH) + { + RemoveFromDatagramHistory(datagramHistoryPopCount); + datagramHistory.Pop(); + datagramHistoryPopCount++; + } + + datagramHistory.Push(DatagramHistoryNode(0, timeSent), _FILE_AND_LINE_); + // printf("%p Pushed empty DatagramHistoryNode to datagram history at index %i\n", this, datagramHistory.Size()-1); +} +//------------------------------------------------------------------------------------------------------- +ReliabilityLayer::MessageNumberNode* ReliabilityLayer::AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, DatagramSequenceNumberType messageNumber, CCTimeType timeSent) +{ + (void) datagramNumber; +// RakAssert(datagramHistoryPopCount+(unsigned int) datagramHistory.Size()==datagramNumber); + if (datagramHistory.Size()>DATAGRAM_MESSAGE_ID_ARRAY_LENGTH) + { + RemoveFromDatagramHistory(datagramHistoryPopCount); + datagramHistory.Pop(); + datagramHistoryPopCount++; + } + + MessageNumberNode *mnm = datagramHistoryMessagePool.Allocate(_FILE_AND_LINE_); + mnm->next=0; + mnm->messageNumber=messageNumber; + datagramHistory.Push(DatagramHistoryNode(mnm, timeSent), _FILE_AND_LINE_); + // printf("%p Pushed message %i to DatagramHistoryNode to datagram history at index %i\n", this, messageNumber.val, datagramHistory.Size()-1); + return mnm; +} +//------------------------------------------------------------------------------------------------------- +ReliabilityLayer::MessageNumberNode* ReliabilityLayer::AddSubsequentToDatagramHistory(MessageNumberNode *messageNumberNode, DatagramSequenceNumberType messageNumber) +{ + messageNumberNode->next=datagramHistoryMessagePool.Allocate(_FILE_AND_LINE_); + messageNumberNode->next->messageNumber=messageNumber; + messageNumberNode->next->next=0; + return messageNumberNode->next; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, InternalPacketRefCountedData **refCounter, unsigned char *externallyAllocatedPtr, unsigned char *ourOffset) +{ + internalPacket->allocationScheme=InternalPacket::REF_COUNTED; + internalPacket->data=ourOffset; + if (*refCounter==0) + { + *refCounter = refCountedDataPool.Allocate(_FILE_AND_LINE_); + // *refCounter = RakNet::OP_NEW(_FILE_AND_LINE_); + (*refCounter)->refCount=1; + (*refCounter)->sharedDataBlock=externallyAllocatedPtr; + } + else + (*refCounter)->refCount++; + internalPacket->refCountedData=(*refCounter); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, unsigned char *externallyAllocatedPtr) +{ + internalPacket->allocationScheme=InternalPacket::NORMAL; + internalPacket->data=externallyAllocatedPtr; +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::AllocInternalPacketData(InternalPacket *internalPacket, unsigned int numBytes, bool allowStack, const char *file, unsigned int line) +{ + if (allowStack && numBytes <= sizeof(internalPacket->stackData)) + { + internalPacket->allocationScheme=InternalPacket::STACK; + internalPacket->data=internalPacket->stackData; + } + else + { + internalPacket->allocationScheme=InternalPacket::NORMAL; + internalPacket->data=(unsigned char*) rakMalloc_Ex(numBytes,file,line); + } +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::FreeInternalPacketData(InternalPacket *internalPacket, const char *file, unsigned int line) +{ + if (internalPacket==0) + return; + + if (internalPacket->allocationScheme==InternalPacket::REF_COUNTED) + { + if (internalPacket->refCountedData==0) + return; + + internalPacket->refCountedData->refCount--; + if (internalPacket->refCountedData->refCount==0) + { + rakFree_Ex(internalPacket->refCountedData->sharedDataBlock, file, line ); + internalPacket->refCountedData->sharedDataBlock=0; + // RakNet::OP_DELETE(internalPacket->refCountedData,file, line); + refCountedDataPool.Release(internalPacket->refCountedData,file, line); + internalPacket->refCountedData=0; + } + } + else if (internalPacket->allocationScheme==InternalPacket::NORMAL) + { + if (internalPacket->data==0) + return; + + rakFree_Ex(internalPacket->data, file, line ); + internalPacket->data=0; + } + else + { + // Data was on stack + internalPacket->data=0; + } +} +//------------------------------------------------------------------------------------------------------- +unsigned int ReliabilityLayer::GetMaxDatagramSizeExcludingMessageHeaderBytes(void) +{ + unsigned int val = congestionManager.GetMTU() - DatagramHeaderFormat::GetDataHeaderByteLength(); + +#if LIBCAT_SECURITY==1 + if (useSecurity) + val -= cat::AuthenticatedEncryption::OVERHEAD_BYTES; +#endif + + return val; +} +//------------------------------------------------------------------------------------------------------- +BitSize_t ReliabilityLayer::GetMaxDatagramSizeExcludingMessageHeaderBits(void) +{ + return BYTES_TO_BITS(GetMaxDatagramSizeExcludingMessageHeaderBytes()); +} +//------------------------------------------------------------------------------------------------------- +void ReliabilityLayer::InitHeapWeights(void) +{ + for (int priorityLevel=0; priorityLevel < NUMBER_OF_PRIORITIES; priorityLevel++) + outgoingPacketBufferNextWeights[priorityLevel]=(1<0) + { + int peekPL = outgoingPacketBuffer.Peek()->priority; + reliabilityHeapWeightType weight = outgoingPacketBuffer.PeekWeight(); + reliabilityHeapWeightType min = weight - (1< -{ - CCTimeType lastUpdateTime; - - DataStructures::List splitPacketList; - -#if PREALLOCATE_LARGE_MESSAGES==1 - InternalPacket *returnedPacket; - bool gotFirstPacket; - unsigned int stride; - unsigned int splitPacketsArrived; -#else - // This is here for progress notifications, since progress notifications return the first packet data, if available - InternalPacket *firstPacket; -#endif - -}; -int RAK_DLL_EXPORT SplitPacketChannelComp( SplitPacketIdType const &key, SplitPacketChannel* const &data ); - -// Helper class -struct BPSTracker -{ - BPSTracker(); - ~BPSTracker(); - void Reset(const char *file, unsigned int line); - inline void Push1(CCTimeType time, uint64_t value1) {dataQueue.Push(TimeAndValue2(time,value1),_FILE_AND_LINE_); total1+=value1; lastSec1+=value1;} -// void Push2(RakNet::TimeUS time, uint64_t value1, uint64_t value2); - inline uint64_t GetBPS1(CCTimeType time) {(void) time; return lastSec1;} - inline uint64_t GetBPS1Threadsafe(CCTimeType time) {(void) time; return lastSec1;} -// uint64_t GetBPS2(RakNetTimeUS time); -// void GetBPS1And2(RakNetTimeUS time, uint64_t &out1, uint64_t &out2); - uint64_t GetTotal1(void) const; -// uint64_t GetTotal2(void) const; - - struct TimeAndValue2 - { - TimeAndValue2(); - ~TimeAndValue2(); - TimeAndValue2(CCTimeType t, uint64_t v1); - // TimeAndValue2(RakNet::TimeUS t, uint64_t v1, uint64_t v2); - // uint64_t value1, value2; - uint64_t value1; - CCTimeType time; - }; - - uint64_t total1, lastSec1; -// uint64_t total2, lastSec2; - DataStructures::Queue dataQueue; - void ClearExpired1(CCTimeType time); -// void ClearExpired2(RakNet::TimeUS time); -}; - -/// Datagram reliable, ordered, unordered and sequenced sends. Flow control. Message splitting, reassembly, and coalescence. -class ReliabilityLayer// -{ -public: - - // Constructor - ReliabilityLayer(); - - // Destructor - ~ReliabilityLayer(); - - /// Resets the layer for reuse - void Reset( bool resetVariables, int MTUSize, bool _useSecurity ); - - /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet - /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. - /// \param[in] time Time, in MS - void SetTimeoutTime( RakNet::TimeMS time ); - - /// Returns the value passed to SetTimeoutTime. or the default if it was never called - /// \param[out] the value passed to SetTimeoutTime - RakNet::TimeMS GetTimeoutTime(void); - - /// Packets are read directly from the socket layer and skip the reliability layer because unconnected players do not use the reliability layer - /// This function takes packet data after a player has been confirmed as connected. - /// \param[in] buffer The socket data - /// \param[in] length The length of the socket data - /// \param[in] systemAddress The player that this data is from - /// \param[in] messageHandlerList A list of registered plugins - /// \param[in] MTUSize maximum datagram size - /// \retval true Success - /// \retval false Modified packet - bool HandleSocketReceiveFromConnectedPlayer( - const char *buffer, unsigned int length, SystemAddress &systemAddress, DataStructures::List &messageHandlerList, int MTUSize, - SOCKET s, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType timeRead, BitStream &updateBitStream); - - /// This allocates bytes and writes a user-level message to those bytes. - /// \param[out] data The message - /// \return Returns number of BITS put into the buffer - BitSize_t Receive( unsigned char**data ); - - /// Puts data on the send queue - /// \param[in] data The data to send - /// \param[in] numberOfBitsToSend The length of \a data in bits - /// \param[in] priority The priority level for the send - /// \param[in] reliability The reliability type for the send - /// \param[in] orderingChannel 0 to 31. Specifies what channel to use, for relational ordering and sequencing of packets. - /// \param[in] makeDataCopy If true \a data will be copied. Otherwise, only a pointer will be stored. - /// \param[in] MTUSize maximum datagram size - /// \param[in] currentTime Current time, as per RakNet::GetTimeMS() - /// \param[in] receipt This number will be returned back with ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS and is only returned with the reliability types that contain RECEIPT in the name - /// \return True or false for success or failure. - bool Send( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, CCTimeType currentTime, uint32_t receipt ); - - /// Call once per game cycle. Handles internal lists and actually does the send. - /// \param[in] s the communication end point - /// \param[in] systemAddress The Unique Player Identifier who shouldhave sent some packets - /// \param[in] MTUSize maximum datagram size - /// \param[in] time current system time - /// \param[in] maxBitsPerSecond if non-zero, enforces that outgoing bandwidth does not exceed this amount - /// \param[in] messageHandlerList A list of registered plugins - void Update( SOCKET s, SystemAddress &systemAddress, int MTUSize, CCTimeType time, - unsigned bitsPerSecondLimit, - DataStructures::List &messageHandlerList, - RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); - - /// Were you ever unable to deliver a packet despite retries? - /// \return true means the connection has been lost. Otherwise not. - bool IsDeadConnection( void ) const; - - /// Causes IsDeadConnection to return true - void KillConnection(void); - - /// Get Statistics - /// \return A pointer to a static struct, filled out with current statistical information. - RakNetStatistics * GetStatistics( RakNetStatistics *rns ); - - ///Are we waiting for any data to be sent out or be processed by the player? - bool IsOutgoingDataWaiting(void); - bool AreAcksWaiting(void); - - // Set outgoing lag and packet loss properties - void ApplyNetworkSimulator( double _maxSendBPS, RakNet::TimeMS _minExtraPing, RakNet::TimeMS _extraPingVariance ); - - /// Returns if you previously called ApplyNetworkSimulator - /// \return If you previously called ApplyNetworkSimulator - bool IsNetworkSimulatorActive( void ); - - void SetSplitMessageProgressInterval(int interval); - void SetUnreliableTimeout(RakNet::TimeMS timeoutMS); - /// Has a lot of time passed since the last ack - bool AckTimeout(RakNet::Time curTime); - CCTimeType GetNextSendTime(void) const; - CCTimeType GetTimeBetweenPackets(void) const; - - - - RakNet::TimeMS GetTimeLastDatagramArrived(void) const {return timeLastDatagramArrived;} - - // If true, will update time between packets quickly based on ping calculations - //void SetDoFastThroughputReactions(bool fast); - - // Encoded as numMessages[unsigned int], message1BitLength[unsigned int], message1Data (aligned), ... - //void GetUndeliveredMessages(RakNet::BitStream *messages, int MTUSize); - -private: - /// Send the contents of a bitstream to the socket - /// \param[in] s The socket used for sending data - /// \param[in] systemAddress The address and port to send to - /// \param[in] bitStream The data to send. - void SendBitStream( SOCKET s, SystemAddress &systemAddress, RakNet::BitStream *bitStream, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType currentTime); - - ///Parse an internalPacket and create a bitstream to represent this data - /// \return Returns number of bits used - BitSize_t WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket, CCTimeType curTime ); - - - /// Parse a bitstream and create an internal packet to represent this data - InternalPacket* CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, CCTimeType time ); - - /// Does what the function name says - unsigned RemovePacketFromResendListAndDeleteOlderReliableSequenced( const MessageNumberType messageNumber, CCTimeType time, DataStructures::List &messageHandlerList, const SystemAddress &systemAddress ); - - /// Acknowledge receipt of the packet with the specified messageNumber - void SendAcknowledgementPacket( const DatagramSequenceNumberType messageNumber, CCTimeType time); - - /// This will return true if we should not send at this time - bool IsSendThrottled( int MTUSize ); - - /// We lost a packet - void UpdateWindowFromPacketloss( CCTimeType time ); - - /// Increase the window size - void UpdateWindowFromAck( CCTimeType time ); - - /// Parse an internalPacket and figure out how many header bits would be written. Returns that number - BitSize_t GetMaxMessageHeaderLengthBits( void ); - BitSize_t GetMessageHeaderLengthBits( const InternalPacket *const internalPacket ); - - /// Get the SHA1 code - void GetSHA1( unsigned char * const buffer, unsigned int nbytes, char code[ SHA1_LENGTH ] ); - - /// Check the SHA1 code - bool CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * const buffer, unsigned int nbytes ); - - /// Search the specified list for sequenced packets on the specified ordering channel, optionally skipping those with splitPacketId, and delete them - void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::List&theList, int splitPacketId = -1 ); - - /// Search the specified list for sequenced packets with a value less than orderingIndex and delete them - void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::Queue&theList ); - - /// Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex - bool IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ); - - /// Split the passed packet into chunks under MTU_SIZE bytes (including headers) and save those new chunks - void SplitPacket( InternalPacket *internalPacket ); - - /// Insert a packet into the split packet list - void InsertIntoSplitPacketList( InternalPacket * internalPacket, CCTimeType time ); - - /// Take all split chunks with the specified splitPacketId and try to reconstruct a packet. If we can, allocate and return it. Otherwise return 0 - InternalPacket * BuildPacketFromSplitPacketList( SplitPacketIdType splitPacketId, CCTimeType time, - SOCKET s, SystemAddress &systemAddress, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); - InternalPacket * BuildPacketFromSplitPacketList( SplitPacketChannel *splitPacketChannel, CCTimeType time ); - - /// Delete any unreliable split packets that have long since expired - //void DeleteOldUnreliableSplitPackets( CCTimeType time ); - - /// Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. - /// Does not copy any split data parameters as that information is always generated does not have any reason to be copied - InternalPacket * CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, CCTimeType time ); - - /// Get the specified ordering list - // DataStructures::LinkedList *GetOrderingListAtOrderingStream( unsigned char orderingChannel ); - - /// Add the internal packet to the ordering list in order based on order index - // void AddToOrderingList( InternalPacket * internalPacket ); - - /// Inserts a packet into the resend list in order - void InsertPacketIntoResendList( InternalPacket *internalPacket, CCTimeType time, bool firstResend, bool modifyUnacknowledgedBytes ); - - /// Memory handling - void FreeMemory( bool freeAllImmediately ); - - /// Memory handling - void FreeThreadSafeMemory( void ); - - // Initialize the variables - void InitializeVariables( void ); - - /// Given the current time, is this time so old that we should consider it a timeout? - bool IsExpiredTime(unsigned int input, CCTimeType currentTime) const; - - // Make it so we don't do resends within a minimum threshold of time - void UpdateNextActionTime(void); - - - /// Does this packet number represent a packet that was skipped (out of order?) - //unsigned int IsReceivedPacketHole(unsigned int input, RakNet::TimeMS currentTime) const; - - /// Skip an element in the received packets list - //unsigned int MakeReceivedPacketHole(unsigned int input) const; - - /// How many elements are waiting to be resent? - unsigned int GetResendListDataSize(void) const; - - /// Update all memory which is not threadsafe - void UpdateThreadedMemory(void); - - void CalculateHistogramAckSize(void); - - // Used ONLY for RELIABLE_ORDERED - // RELIABLE_SEQUENCED just returns the newest one - // DataStructures::List*> orderingList; - DataStructures::Queue outputQueue; - int splitMessageProgressInterval; - CCTimeType unreliableTimeout; - - struct MessageNumberNode - { - DatagramSequenceNumberType messageNumber; - MessageNumberNode *next; - }; - struct DatagramHistoryNode - { - DatagramHistoryNode() {} - DatagramHistoryNode(MessageNumberNode *_head, CCTimeType ts - ) : - head(_head), timeSent(ts) - {} - MessageNumberNode *head; - CCTimeType timeSent; - }; - // Queue length is programmatically restricted to DATAGRAM_MESSAGE_ID_ARRAY_LENGTH - // This is essentially an O(1) lookup to get a DatagramHistoryNode given an index - // datagramHistory holds a linked list of MessageNumberNode. Each MessageNumberNode refers to one element in resendList which can be cleared on an ack. - DataStructures::Queue datagramHistory; - DataStructures::MemoryPool datagramHistoryMessagePool; - - struct UnreliableWithAckReceiptNode - { - UnreliableWithAckReceiptNode() {} - UnreliableWithAckReceiptNode(DatagramSequenceNumberType _datagramNumber, uint32_t _sendReceiptSerial, RakNet::TimeUS _nextActionTime) : - datagramNumber(_datagramNumber), sendReceiptSerial(_sendReceiptSerial), nextActionTime(_nextActionTime) - {} - DatagramSequenceNumberType datagramNumber; - uint32_t sendReceiptSerial; - RakNet::TimeUS nextActionTime; - }; - DataStructures::List unreliableWithAckReceiptHistory; - - void RemoveFromDatagramHistory(DatagramSequenceNumberType index); - MessageNumberNode* GetMessageNumberNodeByDatagramIndex(DatagramSequenceNumberType index, CCTimeType *timeSent); - void AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, CCTimeType timeSent); - MessageNumberNode* AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, DatagramSequenceNumberType messageNumber, CCTimeType timeSent); - MessageNumberNode* AddSubsequentToDatagramHistory(MessageNumberNode *messageNumberNode, DatagramSequenceNumberType messageNumber); - DatagramSequenceNumberType datagramHistoryPopCount; - - DataStructures::MemoryPool internalPacketPool; - // DataStructures::BPlusTree resendTree; - InternalPacket *resendBuffer[RESEND_BUFFER_ARRAY_LENGTH]; - InternalPacket *resendLinkedListHead; - InternalPacket *unreliableLinkedListHead; - void RemoveFromUnreliableLinkedList(InternalPacket *internalPacket); - void AddToUnreliableLinkedList(InternalPacket *internalPacket); -// unsigned int numPacketsOnResendBuffer; - //unsigned int blockWindowIncreaseUntilTime; - // DataStructures::RangeList acknowlegements; - // Resend list is a tree of packets we need to resend - - // Set to the current time when the resend queue is no longer empty - // Set to zero when it becomes empty - // Set to the current time if it is not zero, and we get incoming data - // If the current time - timeResendQueueNonEmpty is greater than a threshold, we are disconnected -// CCTimeType timeResendQueueNonEmpty; - RakNet::TimeMS timeLastDatagramArrived; - - - // If we backoff due to packetloss, don't remeasure until all waiting resends have gone out or else we overcount -// bool packetlossThisSample; -// int backoffThisSample; -// unsigned packetlossThisSampleResendCount; -// CCTimeType lastPacketlossTime; - - //DataStructures::Queue sendPacketSet[ NUMBER_OF_PRIORITIES ]; - DataStructures::Heap outgoingPacketBuffer; - reliabilityHeapWeightType outgoingPacketBufferNextWeights[NUMBER_OF_PRIORITIES]; - void InitHeapWeights(void); - reliabilityHeapWeightType GetNextWeight(int priorityLevel); -// unsigned int messageInSendBuffer[NUMBER_OF_PRIORITIES]; -// double bytesInSendBuffer[NUMBER_OF_PRIORITIES]; - - - DataStructures::OrderedList splitPacketChannelList; - - MessageNumberType sendReliableMessageNumberIndex; - MessageNumberType internalOrderIndex; - //unsigned int windowSize; - //RakNet::BitStream updateBitStream; - bool deadConnection, cheater; - SplitPacketIdType splitPacketId; - RakNet::TimeMS timeoutTime; // How long to wait in MS before timing someone out - //int MAX_AVERAGE_PACKETS_PER_SECOND; // Name says it all -// int RECEIVED_PACKET_LOG_LENGTH, requestedReceivedPacketLogLength; // How big the receivedPackets array is -// unsigned int *receivedPackets; - RakNetStatistics statistics; - - // Algorithm for blending ordered and sequenced on the same channel: - // 1. Each ordered message transmits OrderingIndexType orderedWriteIndex. There are NUMBER_OF_ORDERED_STREAMS independent values of these. The value - // starts at 0. Every time an ordered message is sent, the value increments by 1 - // 2. Each sequenced message contains the current value of orderedWriteIndex for that channel, and additionally OrderingIndexType sequencedWriteIndex. - // sequencedWriteIndex resets to 0 every time orderedWriteIndex increments. It increments by 1 every time a sequenced message is sent. - // 3. The receiver maintains the next expected value for the orderedWriteIndex, stored in orderedReadIndex. - // 4. As messages arrive: - // If a message has the current ordering index, and is sequenced, and is < the current highest sequence value, discard - // If a message has the current ordering index, and is sequenced, and is >= the current highest sequence value, return immediately - // If a message has a greater ordering index, and is sequenced or ordered, buffer it - // If a message has the current ordering index, and is ordered, buffer, then push off messages from buffer - // 5. Pushing off messages from buffer: - // Messages in buffer are put in a minheap. The value of each node is calculated such that messages are returned: - // A. (lowest ordering index, lowest sequence index) - // B. (lowest ordering index, no sequence index) - // Messages are pushed off until the heap is empty, or the next message to be returned does not preserve the ordered index - // For an empty heap, the heap weight should start at the lowest value based on the next expected ordering index, to avoid variable overflow - - // Sender increments this by 1 for every ordered message sent - OrderingIndexType orderedWriteIndex[NUMBER_OF_ORDERED_STREAMS]; - // Sender increments by 1 for every sequenced message sent. Resets to 0 when an ordered message is sent - OrderingIndexType sequencedWriteIndex[NUMBER_OF_ORDERED_STREAMS]; - // Next expected index for ordered messages. - OrderingIndexType orderedReadIndex[NUMBER_OF_ORDERED_STREAMS]; - // Highest value received for sequencedWriteIndex for the current value of orderedReadIndex on the same channel. - OrderingIndexType highestSequencedReadIndex[NUMBER_OF_ORDERED_STREAMS]; - DataStructures::Heap orderingHeaps[NUMBER_OF_ORDERED_STREAMS]; - OrderingIndexType heapIndexOffsets[NUMBER_OF_ORDERED_STREAMS]; - - - - - - - -// CCTimeType histogramStart; -// unsigned histogramBitsSent; - - - /// Memory-efficient receivedPackets algorithm: - /// receivedPacketsBaseIndex is the packet number we are expecting - /// Everything under receivedPacketsBaseIndex is a packet we already got - /// Everything over receivedPacketsBaseIndex is stored in hasReceivedPacketQueue - /// It stores the time to stop waiting for a particular packet number, where the packet number is receivedPacketsBaseIndex + the index into the queue - /// If 0, we got got that packet. Otherwise, the time to give up waiting for that packet. - /// If we get a packet number where (receivedPacketsBaseIndex-packetNumber) is less than half the range of receivedPacketsBaseIndex then it is a duplicate - /// Otherwise, it is a duplicate packet (and ignore it). - // DataStructures::Queue hasReceivedPacketQueue; - DataStructures::Queue hasReceivedPacketQueue; - DatagramSequenceNumberType receivedPacketsBaseIndex; - bool resetReceivedPackets; - - CCTimeType lastUpdateTime; - CCTimeType timeBetweenPackets, nextSendTime; - - - -// CCTimeType ackPingSamples[ACK_PING_SAMPLES_SIZE]; // Must be range of unsigned char to wrap ackPingIndex properly - CCTimeType ackPingSum; - unsigned char ackPingIndex; - //CCTimeType nextLowestPingReset; - RemoteSystemTimeType remoteSystemTime; -// bool continuousSend; -// CCTimeType lastTimeBetweenPacketsIncrease,lastTimeBetweenPacketsDecrease; - // Limit changes in throughput to once per ping - otherwise even if lag starts we don't know about it - // In the meantime the connection is flooded and overrun. - CCTimeType nextAllowedThroughputSample; - bool bandwidthExceededStatistic; - - // If Update::maxBitsPerSecond > 0, then throughputCapCountdown is used as a timer to prevent sends for some amount of time after each send, depending on - // the amount of data sent - long long throughputCapCountdown; - - unsigned receivePacketCount; - -#ifdef _DEBUG - struct DataAndTime// - { - SOCKET s; - char data[ MAXIMUM_MTU_SIZE ]; - unsigned int length; - RakNet::TimeMS sendTime; - // SystemAddress systemAddress; - unsigned short remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions; - }; - DataStructures::Queue delayList; - - // Internet simulator - double packetloss; - RakNet::TimeMS minExtraPing, extraPingVariance; -#endif - - CCTimeType elapsedTimeSinceLastUpdate; - - CCTimeType nextAckTimeToSend; - - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1 - RakNet::CCRakNetSlidingWindow congestionManager; -#else - RakNet::CCRakNetUDT congestionManager; -#endif - - - uint32_t unacknowledgedBytes; - - bool ResendBufferOverflow(void) const; - void ValidateResendList(void) const; - void ResetPacketsAndDatagrams(void); - void PushPacket(CCTimeType time, InternalPacket *internalPacket, bool isReliable); - void PushDatagram(void); - bool TagMostRecentPushAsSecondOfPacketPair(void); - void ClearPacketsAndDatagrams(void); - void MoveToListHead(InternalPacket *internalPacket); - void RemoveFromList(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes); - void AddToListTail(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes); - void PopListHead(bool modifyUnacknowledgedBytes); - bool IsResendQueueEmpty(void) const; - void SortSplitPacketList(DataStructures::List &data, unsigned int leftEdge, unsigned int rightEdge) const; - void SendACKs(SOCKET s, SystemAddress &systemAddress, CCTimeType time, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); - - DataStructures::List packetsToSendThisUpdate; - DataStructures::List packetsToDeallocThisUpdate; - // boundary is in packetsToSendThisUpdate, inclusive - DataStructures::List packetsToSendThisUpdateDatagramBoundaries; - DataStructures::List datagramsToSendThisUpdateIsPair; - DataStructures::List datagramSizesInBytes; - BitSize_t datagramSizeSoFar; - BitSize_t allDatagramSizesSoFar; - double totalUserDataBytesAcked; - CCTimeType timeOfLastContinualSend; - CCTimeType timeToNextUnreliableCull; - - // This doesn't need to be a member, but I do it to avoid reallocations - DataStructures::RangeList incomingAcks; - - // Every 16 datagrams, we make sure the 17th datagram goes out the same update tick, and is the same size as the 16th - int countdownToNextPacketPair; - InternalPacket* AllocateFromInternalPacketPool(void); - void ReleaseToInternalPacketPool(InternalPacket *ip); - - DataStructures::RangeList acknowlegements; - DataStructures::RangeList NAKs; - bool remoteSystemNeedsBAndAS; - - unsigned int GetMaxDatagramSizeExcludingMessageHeaderBytes(void); - BitSize_t GetMaxDatagramSizeExcludingMessageHeaderBits(void); - - // ourOffset refers to a section within externallyAllocatedPtr. Do not deallocate externallyAllocatedPtr until all references are lost - void AllocInternalPacketData(InternalPacket *internalPacket, InternalPacketRefCountedData **refCounter, unsigned char *externallyAllocatedPtr, unsigned char *ourOffset); - // Set the data pointer to externallyAllocatedPtr, do not allocate - void AllocInternalPacketData(InternalPacket *internalPacket, unsigned char *externallyAllocatedPtr); - // Allocate new - void AllocInternalPacketData(InternalPacket *internalPacket, unsigned int numBytes, bool allowStack, const char *file, unsigned int line); - void FreeInternalPacketData(InternalPacket *internalPacket, const char *file, unsigned int line); - DataStructures::MemoryPool refCountedDataPool; - - BPSTracker bpsMetrics[RNS_PER_SECOND_METRICS_COUNT]; - CCTimeType lastBpsClear; - -#if LIBCAT_SECURITY==1 -public: - cat::AuthenticatedEncryption* GetAuthenticatedEncryption(void) { return &auth_enc; } - -protected: - cat::AuthenticatedEncryption auth_enc; - bool useSecurity; -#endif // LIBCAT_SECURITY -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief \b [Internal] Datagram reliable, ordered, unordered and sequenced sends. Flow control. Message splitting, reassembly, and coalescence. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RELIABILITY_LAYER_H +#define __RELIABILITY_LAYER_H + +#include "RakMemoryOverride.h" +#include "MTUSize.h" +#include "DS_LinkedList.h" +#include "DS_List.h" +#include "SocketLayer.h" +#include "PacketPriority.h" +#include "DS_Queue.h" +#include "BitStream.h" +#include "InternalPacket.h" +#include "RakNetStatistics.h" +#include "SHA1.h" +#include "DS_OrderedList.h" +#include "DS_RangeList.h" +#include "DS_BPlusTree.h" +#include "DS_MemoryPool.h" +#include "RakNetDefines.h" +#include "DS_Heap.h" +#include "BitStream.h" +#include "NativeFeatureIncludes.h" +#include "SecureHandshake.h" +#include "PluginInterface2.h" +#include "Rand.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 +#include "CCRakNetUDT.h" +#define INCLUDE_TIMESTAMP_WITH_DATAGRAMS 1 +#else +#include "CCRakNetSlidingWindow.h" +#define INCLUDE_TIMESTAMP_WITH_DATAGRAMS 0 +#endif + +/// Number of ordered streams available. You can use up to 32 ordered streams +#define NUMBER_OF_ORDERED_STREAMS 32 // 2^5 + +#define RESEND_TREE_ORDER 32 + +namespace RakNet { + + /// Forward declarations +class PluginInterface2; +class RakNetRandom; +typedef uint64_t reliabilityHeapWeightType; + +// int SplitPacketIndexComp( SplitPacketIndexType const &key, InternalPacket* const &data ); +struct SplitPacketChannel// +{ + CCTimeType lastUpdateTime; + + DataStructures::List splitPacketList; + +#if PREALLOCATE_LARGE_MESSAGES==1 + InternalPacket *returnedPacket; + bool gotFirstPacket; + unsigned int stride; + unsigned int splitPacketsArrived; +#else + // This is here for progress notifications, since progress notifications return the first packet data, if available + InternalPacket *firstPacket; +#endif + +}; +int RAK_DLL_EXPORT SplitPacketChannelComp( SplitPacketIdType const &key, SplitPacketChannel* const &data ); + +// Helper class +struct BPSTracker +{ + BPSTracker(); + ~BPSTracker(); + void Reset(const char *file, unsigned int line); + inline void Push1(CCTimeType time, uint64_t value1) {dataQueue.Push(TimeAndValue2(time,value1),_FILE_AND_LINE_); total1+=value1; lastSec1+=value1;} +// void Push2(RakNet::TimeUS time, uint64_t value1, uint64_t value2); + inline uint64_t GetBPS1(CCTimeType time) {(void) time; return lastSec1;} + inline uint64_t GetBPS1Threadsafe(CCTimeType time) {(void) time; return lastSec1;} +// uint64_t GetBPS2(RakNetTimeUS time); +// void GetBPS1And2(RakNetTimeUS time, uint64_t &out1, uint64_t &out2); + uint64_t GetTotal1(void) const; +// uint64_t GetTotal2(void) const; + + struct TimeAndValue2 + { + TimeAndValue2(); + ~TimeAndValue2(); + TimeAndValue2(CCTimeType t, uint64_t v1); + // TimeAndValue2(RakNet::TimeUS t, uint64_t v1, uint64_t v2); + // uint64_t value1, value2; + uint64_t value1; + CCTimeType time; + }; + + uint64_t total1, lastSec1; +// uint64_t total2, lastSec2; + DataStructures::Queue dataQueue; + void ClearExpired1(CCTimeType time); +// void ClearExpired2(RakNet::TimeUS time); +}; + +/// Datagram reliable, ordered, unordered and sequenced sends. Flow control. Message splitting, reassembly, and coalescence. +class ReliabilityLayer// +{ +public: + + // Constructor + ReliabilityLayer(); + + // Destructor + ~ReliabilityLayer(); + + /// Resets the layer for reuse + void Reset( bool resetVariables, int MTUSize, bool _useSecurity ); + + /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet + /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. + /// \param[in] time Time, in MS + void SetTimeoutTime( RakNet::TimeMS time ); + + /// Returns the value passed to SetTimeoutTime. or the default if it was never called + /// \param[out] the value passed to SetTimeoutTime + RakNet::TimeMS GetTimeoutTime(void); + + /// Packets are read directly from the socket layer and skip the reliability layer because unconnected players do not use the reliability layer + /// This function takes packet data after a player has been confirmed as connected. + /// \param[in] buffer The socket data + /// \param[in] length The length of the socket data + /// \param[in] systemAddress The player that this data is from + /// \param[in] messageHandlerList A list of registered plugins + /// \param[in] MTUSize maximum datagram size + /// \retval true Success + /// \retval false Modified packet + bool HandleSocketReceiveFromConnectedPlayer( + const char *buffer, unsigned int length, SystemAddress &systemAddress, DataStructures::List &messageHandlerList, int MTUSize, + SOCKET s, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType timeRead, BitStream &updateBitStream); + + /// This allocates bytes and writes a user-level message to those bytes. + /// \param[out] data The message + /// \return Returns number of BITS put into the buffer + BitSize_t Receive( unsigned char**data ); + + /// Puts data on the send queue + /// \param[in] data The data to send + /// \param[in] numberOfBitsToSend The length of \a data in bits + /// \param[in] priority The priority level for the send + /// \param[in] reliability The reliability type for the send + /// \param[in] orderingChannel 0 to 31. Specifies what channel to use, for relational ordering and sequencing of packets. + /// \param[in] makeDataCopy If true \a data will be copied. Otherwise, only a pointer will be stored. + /// \param[in] MTUSize maximum datagram size + /// \param[in] currentTime Current time, as per RakNet::GetTimeMS() + /// \param[in] receipt This number will be returned back with ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS and is only returned with the reliability types that contain RECEIPT in the name + /// \return True or false for success or failure. + bool Send( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, CCTimeType currentTime, uint32_t receipt ); + + /// Call once per game cycle. Handles internal lists and actually does the send. + /// \param[in] s the communication end point + /// \param[in] systemAddress The Unique Player Identifier who shouldhave sent some packets + /// \param[in] MTUSize maximum datagram size + /// \param[in] time current system time + /// \param[in] maxBitsPerSecond if non-zero, enforces that outgoing bandwidth does not exceed this amount + /// \param[in] messageHandlerList A list of registered plugins + void Update( SOCKET s, SystemAddress &systemAddress, int MTUSize, CCTimeType time, + unsigned bitsPerSecondLimit, + DataStructures::List &messageHandlerList, + RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); + + /// Were you ever unable to deliver a packet despite retries? + /// \return true means the connection has been lost. Otherwise not. + bool IsDeadConnection( void ) const; + + /// Causes IsDeadConnection to return true + void KillConnection(void); + + /// Get Statistics + /// \return A pointer to a static struct, filled out with current statistical information. + RakNetStatistics * GetStatistics( RakNetStatistics *rns ); + + ///Are we waiting for any data to be sent out or be processed by the player? + bool IsOutgoingDataWaiting(void); + bool AreAcksWaiting(void); + + // Set outgoing lag and packet loss properties + void ApplyNetworkSimulator( double _maxSendBPS, RakNet::TimeMS _minExtraPing, RakNet::TimeMS _extraPingVariance ); + + /// Returns if you previously called ApplyNetworkSimulator + /// \return If you previously called ApplyNetworkSimulator + bool IsNetworkSimulatorActive( void ); + + void SetSplitMessageProgressInterval(int interval); + void SetUnreliableTimeout(RakNet::TimeMS timeoutMS); + /// Has a lot of time passed since the last ack + bool AckTimeout(RakNet::Time curTime); + CCTimeType GetNextSendTime(void) const; + CCTimeType GetTimeBetweenPackets(void) const; + + + + RakNet::TimeMS GetTimeLastDatagramArrived(void) const {return timeLastDatagramArrived;} + + // If true, will update time between packets quickly based on ping calculations + //void SetDoFastThroughputReactions(bool fast); + + // Encoded as numMessages[unsigned int], message1BitLength[unsigned int], message1Data (aligned), ... + //void GetUndeliveredMessages(RakNet::BitStream *messages, int MTUSize); + +private: + /// Send the contents of a bitstream to the socket + /// \param[in] s The socket used for sending data + /// \param[in] systemAddress The address and port to send to + /// \param[in] bitStream The data to send. + void SendBitStream( SOCKET s, SystemAddress &systemAddress, RakNet::BitStream *bitStream, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, CCTimeType currentTime); + + ///Parse an internalPacket and create a bitstream to represent this data + /// \return Returns number of bits used + BitSize_t WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket, CCTimeType curTime ); + + + /// Parse a bitstream and create an internal packet to represent this data + InternalPacket* CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, CCTimeType time ); + + /// Does what the function name says + unsigned RemovePacketFromResendListAndDeleteOlderReliableSequenced( const MessageNumberType messageNumber, CCTimeType time, DataStructures::List &messageHandlerList, const SystemAddress &systemAddress ); + + /// Acknowledge receipt of the packet with the specified messageNumber + void SendAcknowledgementPacket( const DatagramSequenceNumberType messageNumber, CCTimeType time); + + /// This will return true if we should not send at this time + bool IsSendThrottled( int MTUSize ); + + /// We lost a packet + void UpdateWindowFromPacketloss( CCTimeType time ); + + /// Increase the window size + void UpdateWindowFromAck( CCTimeType time ); + + /// Parse an internalPacket and figure out how many header bits would be written. Returns that number + BitSize_t GetMaxMessageHeaderLengthBits( void ); + BitSize_t GetMessageHeaderLengthBits( const InternalPacket *const internalPacket ); + + /// Get the SHA1 code + void GetSHA1( unsigned char * const buffer, unsigned int nbytes, char code[ SHA1_LENGTH ] ); + + /// Check the SHA1 code + bool CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * const buffer, unsigned int nbytes ); + + /// Search the specified list for sequenced packets on the specified ordering channel, optionally skipping those with splitPacketId, and delete them + void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::List&theList, int splitPacketId = -1 ); + + /// Search the specified list for sequenced packets with a value less than orderingIndex and delete them + void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::Queue&theList ); + + /// Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex + bool IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ); + + /// Split the passed packet into chunks under MTU_SIZE bytes (including headers) and save those new chunks + void SplitPacket( InternalPacket *internalPacket ); + + /// Insert a packet into the split packet list + void InsertIntoSplitPacketList( InternalPacket * internalPacket, CCTimeType time ); + + /// Take all split chunks with the specified splitPacketId and try to reconstruct a packet. If we can, allocate and return it. Otherwise return 0 + InternalPacket * BuildPacketFromSplitPacketList( SplitPacketIdType splitPacketId, CCTimeType time, + SOCKET s, SystemAddress &systemAddress, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); + InternalPacket * BuildPacketFromSplitPacketList( SplitPacketChannel *splitPacketChannel, CCTimeType time ); + + /// Delete any unreliable split packets that have long since expired + //void DeleteOldUnreliableSplitPackets( CCTimeType time ); + + /// Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. + /// Does not copy any split data parameters as that information is always generated does not have any reason to be copied + InternalPacket * CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, CCTimeType time ); + + /// Get the specified ordering list + // DataStructures::LinkedList *GetOrderingListAtOrderingStream( unsigned char orderingChannel ); + + /// Add the internal packet to the ordering list in order based on order index + // void AddToOrderingList( InternalPacket * internalPacket ); + + /// Inserts a packet into the resend list in order + void InsertPacketIntoResendList( InternalPacket *internalPacket, CCTimeType time, bool firstResend, bool modifyUnacknowledgedBytes ); + + /// Memory handling + void FreeMemory( bool freeAllImmediately ); + + /// Memory handling + void FreeThreadSafeMemory( void ); + + // Initialize the variables + void InitializeVariables( void ); + + /// Given the current time, is this time so old that we should consider it a timeout? + bool IsExpiredTime(unsigned int input, CCTimeType currentTime) const; + + // Make it so we don't do resends within a minimum threshold of time + void UpdateNextActionTime(void); + + + /// Does this packet number represent a packet that was skipped (out of order?) + //unsigned int IsReceivedPacketHole(unsigned int input, RakNet::TimeMS currentTime) const; + + /// Skip an element in the received packets list + //unsigned int MakeReceivedPacketHole(unsigned int input) const; + + /// How many elements are waiting to be resent? + unsigned int GetResendListDataSize(void) const; + + /// Update all memory which is not threadsafe + void UpdateThreadedMemory(void); + + void CalculateHistogramAckSize(void); + + // Used ONLY for RELIABLE_ORDERED + // RELIABLE_SEQUENCED just returns the newest one + // DataStructures::List*> orderingList; + DataStructures::Queue outputQueue; + int splitMessageProgressInterval; + CCTimeType unreliableTimeout; + + struct MessageNumberNode + { + DatagramSequenceNumberType messageNumber; + MessageNumberNode *next; + }; + struct DatagramHistoryNode + { + DatagramHistoryNode() {} + DatagramHistoryNode(MessageNumberNode *_head, CCTimeType ts + ) : + head(_head), timeSent(ts) + {} + MessageNumberNode *head; + CCTimeType timeSent; + }; + // Queue length is programmatically restricted to DATAGRAM_MESSAGE_ID_ARRAY_LENGTH + // This is essentially an O(1) lookup to get a DatagramHistoryNode given an index + // datagramHistory holds a linked list of MessageNumberNode. Each MessageNumberNode refers to one element in resendList which can be cleared on an ack. + DataStructures::Queue datagramHistory; + DataStructures::MemoryPool datagramHistoryMessagePool; + + struct UnreliableWithAckReceiptNode + { + UnreliableWithAckReceiptNode() {} + UnreliableWithAckReceiptNode(DatagramSequenceNumberType _datagramNumber, uint32_t _sendReceiptSerial, RakNet::TimeUS _nextActionTime) : + datagramNumber(_datagramNumber), sendReceiptSerial(_sendReceiptSerial), nextActionTime(_nextActionTime) + {} + DatagramSequenceNumberType datagramNumber; + uint32_t sendReceiptSerial; + RakNet::TimeUS nextActionTime; + }; + DataStructures::List unreliableWithAckReceiptHistory; + + void RemoveFromDatagramHistory(DatagramSequenceNumberType index); + MessageNumberNode* GetMessageNumberNodeByDatagramIndex(DatagramSequenceNumberType index, CCTimeType *timeSent); + void AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, CCTimeType timeSent); + MessageNumberNode* AddFirstToDatagramHistory(DatagramSequenceNumberType datagramNumber, DatagramSequenceNumberType messageNumber, CCTimeType timeSent); + MessageNumberNode* AddSubsequentToDatagramHistory(MessageNumberNode *messageNumberNode, DatagramSequenceNumberType messageNumber); + DatagramSequenceNumberType datagramHistoryPopCount; + + DataStructures::MemoryPool internalPacketPool; + // DataStructures::BPlusTree resendTree; + InternalPacket *resendBuffer[RESEND_BUFFER_ARRAY_LENGTH]; + InternalPacket *resendLinkedListHead; + InternalPacket *unreliableLinkedListHead; + void RemoveFromUnreliableLinkedList(InternalPacket *internalPacket); + void AddToUnreliableLinkedList(InternalPacket *internalPacket); +// unsigned int numPacketsOnResendBuffer; + //unsigned int blockWindowIncreaseUntilTime; + // DataStructures::RangeList acknowlegements; + // Resend list is a tree of packets we need to resend + + // Set to the current time when the resend queue is no longer empty + // Set to zero when it becomes empty + // Set to the current time if it is not zero, and we get incoming data + // If the current time - timeResendQueueNonEmpty is greater than a threshold, we are disconnected +// CCTimeType timeResendQueueNonEmpty; + RakNet::TimeMS timeLastDatagramArrived; + + + // If we backoff due to packetloss, don't remeasure until all waiting resends have gone out or else we overcount +// bool packetlossThisSample; +// int backoffThisSample; +// unsigned packetlossThisSampleResendCount; +// CCTimeType lastPacketlossTime; + + //DataStructures::Queue sendPacketSet[ NUMBER_OF_PRIORITIES ]; + DataStructures::Heap outgoingPacketBuffer; + reliabilityHeapWeightType outgoingPacketBufferNextWeights[NUMBER_OF_PRIORITIES]; + void InitHeapWeights(void); + reliabilityHeapWeightType GetNextWeight(int priorityLevel); +// unsigned int messageInSendBuffer[NUMBER_OF_PRIORITIES]; +// double bytesInSendBuffer[NUMBER_OF_PRIORITIES]; + + + DataStructures::OrderedList splitPacketChannelList; + + MessageNumberType sendReliableMessageNumberIndex; + MessageNumberType internalOrderIndex; + //unsigned int windowSize; + //RakNet::BitStream updateBitStream; + bool deadConnection, cheater; + SplitPacketIdType splitPacketId; + RakNet::TimeMS timeoutTime; // How long to wait in MS before timing someone out + //int MAX_AVERAGE_PACKETS_PER_SECOND; // Name says it all +// int RECEIVED_PACKET_LOG_LENGTH, requestedReceivedPacketLogLength; // How big the receivedPackets array is +// unsigned int *receivedPackets; + RakNetStatistics statistics; + + // Algorithm for blending ordered and sequenced on the same channel: + // 1. Each ordered message transmits OrderingIndexType orderedWriteIndex. There are NUMBER_OF_ORDERED_STREAMS independent values of these. The value + // starts at 0. Every time an ordered message is sent, the value increments by 1 + // 2. Each sequenced message contains the current value of orderedWriteIndex for that channel, and additionally OrderingIndexType sequencedWriteIndex. + // sequencedWriteIndex resets to 0 every time orderedWriteIndex increments. It increments by 1 every time a sequenced message is sent. + // 3. The receiver maintains the next expected value for the orderedWriteIndex, stored in orderedReadIndex. + // 4. As messages arrive: + // If a message has the current ordering index, and is sequenced, and is < the current highest sequence value, discard + // If a message has the current ordering index, and is sequenced, and is >= the current highest sequence value, return immediately + // If a message has a greater ordering index, and is sequenced or ordered, buffer it + // If a message has the current ordering index, and is ordered, buffer, then push off messages from buffer + // 5. Pushing off messages from buffer: + // Messages in buffer are put in a minheap. The value of each node is calculated such that messages are returned: + // A. (lowest ordering index, lowest sequence index) + // B. (lowest ordering index, no sequence index) + // Messages are pushed off until the heap is empty, or the next message to be returned does not preserve the ordered index + // For an empty heap, the heap weight should start at the lowest value based on the next expected ordering index, to avoid variable overflow + + // Sender increments this by 1 for every ordered message sent + OrderingIndexType orderedWriteIndex[NUMBER_OF_ORDERED_STREAMS]; + // Sender increments by 1 for every sequenced message sent. Resets to 0 when an ordered message is sent + OrderingIndexType sequencedWriteIndex[NUMBER_OF_ORDERED_STREAMS]; + // Next expected index for ordered messages. + OrderingIndexType orderedReadIndex[NUMBER_OF_ORDERED_STREAMS]; + // Highest value received for sequencedWriteIndex for the current value of orderedReadIndex on the same channel. + OrderingIndexType highestSequencedReadIndex[NUMBER_OF_ORDERED_STREAMS]; + DataStructures::Heap orderingHeaps[NUMBER_OF_ORDERED_STREAMS]; + OrderingIndexType heapIndexOffsets[NUMBER_OF_ORDERED_STREAMS]; + + + + + + + +// CCTimeType histogramStart; +// unsigned histogramBitsSent; + + + /// Memory-efficient receivedPackets algorithm: + /// receivedPacketsBaseIndex is the packet number we are expecting + /// Everything under receivedPacketsBaseIndex is a packet we already got + /// Everything over receivedPacketsBaseIndex is stored in hasReceivedPacketQueue + /// It stores the time to stop waiting for a particular packet number, where the packet number is receivedPacketsBaseIndex + the index into the queue + /// If 0, we got got that packet. Otherwise, the time to give up waiting for that packet. + /// If we get a packet number where (receivedPacketsBaseIndex-packetNumber) is less than half the range of receivedPacketsBaseIndex then it is a duplicate + /// Otherwise, it is a duplicate packet (and ignore it). + // DataStructures::Queue hasReceivedPacketQueue; + DataStructures::Queue hasReceivedPacketQueue; + DatagramSequenceNumberType receivedPacketsBaseIndex; + bool resetReceivedPackets; + + CCTimeType lastUpdateTime; + CCTimeType timeBetweenPackets, nextSendTime; + + + +// CCTimeType ackPingSamples[ACK_PING_SAMPLES_SIZE]; // Must be range of unsigned char to wrap ackPingIndex properly + CCTimeType ackPingSum; + unsigned char ackPingIndex; + //CCTimeType nextLowestPingReset; + RemoteSystemTimeType remoteSystemTime; +// bool continuousSend; +// CCTimeType lastTimeBetweenPacketsIncrease,lastTimeBetweenPacketsDecrease; + // Limit changes in throughput to once per ping - otherwise even if lag starts we don't know about it + // In the meantime the connection is flooded and overrun. + CCTimeType nextAllowedThroughputSample; + bool bandwidthExceededStatistic; + + // If Update::maxBitsPerSecond > 0, then throughputCapCountdown is used as a timer to prevent sends for some amount of time after each send, depending on + // the amount of data sent + long long throughputCapCountdown; + + unsigned receivePacketCount; + +#ifdef _DEBUG + struct DataAndTime// + { + SOCKET s; + char data[ MAXIMUM_MTU_SIZE ]; + unsigned int length; + RakNet::TimeMS sendTime; + // SystemAddress systemAddress; + unsigned short remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions; + }; + DataStructures::Queue delayList; + + // Internet simulator + double packetloss; + RakNet::TimeMS minExtraPing, extraPingVariance; +#endif + + CCTimeType elapsedTimeSinceLastUpdate; + + CCTimeType nextAckTimeToSend; + + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1 + RakNet::CCRakNetSlidingWindow congestionManager; +#else + RakNet::CCRakNetUDT congestionManager; +#endif + + + uint32_t unacknowledgedBytes; + + bool ResendBufferOverflow(void) const; + void ValidateResendList(void) const; + void ResetPacketsAndDatagrams(void); + void PushPacket(CCTimeType time, InternalPacket *internalPacket, bool isReliable); + void PushDatagram(void); + bool TagMostRecentPushAsSecondOfPacketPair(void); + void ClearPacketsAndDatagrams(void); + void MoveToListHead(InternalPacket *internalPacket); + void RemoveFromList(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes); + void AddToListTail(InternalPacket *internalPacket, bool modifyUnacknowledgedBytes); + void PopListHead(bool modifyUnacknowledgedBytes); + bool IsResendQueueEmpty(void) const; + void SortSplitPacketList(DataStructures::List &data, unsigned int leftEdge, unsigned int rightEdge) const; + void SendACKs(SOCKET s, SystemAddress &systemAddress, CCTimeType time, RakNetRandom *rnr, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, BitStream &updateBitStream); + + DataStructures::List packetsToSendThisUpdate; + DataStructures::List packetsToDeallocThisUpdate; + // boundary is in packetsToSendThisUpdate, inclusive + DataStructures::List packetsToSendThisUpdateDatagramBoundaries; + DataStructures::List datagramsToSendThisUpdateIsPair; + DataStructures::List datagramSizesInBytes; + BitSize_t datagramSizeSoFar; + BitSize_t allDatagramSizesSoFar; + double totalUserDataBytesAcked; + CCTimeType timeOfLastContinualSend; + CCTimeType timeToNextUnreliableCull; + + // This doesn't need to be a member, but I do it to avoid reallocations + DataStructures::RangeList incomingAcks; + + // Every 16 datagrams, we make sure the 17th datagram goes out the same update tick, and is the same size as the 16th + int countdownToNextPacketPair; + InternalPacket* AllocateFromInternalPacketPool(void); + void ReleaseToInternalPacketPool(InternalPacket *ip); + + DataStructures::RangeList acknowlegements; + DataStructures::RangeList NAKs; + bool remoteSystemNeedsBAndAS; + + unsigned int GetMaxDatagramSizeExcludingMessageHeaderBytes(void); + BitSize_t GetMaxDatagramSizeExcludingMessageHeaderBits(void); + + // ourOffset refers to a section within externallyAllocatedPtr. Do not deallocate externallyAllocatedPtr until all references are lost + void AllocInternalPacketData(InternalPacket *internalPacket, InternalPacketRefCountedData **refCounter, unsigned char *externallyAllocatedPtr, unsigned char *ourOffset); + // Set the data pointer to externallyAllocatedPtr, do not allocate + void AllocInternalPacketData(InternalPacket *internalPacket, unsigned char *externallyAllocatedPtr); + // Allocate new + void AllocInternalPacketData(InternalPacket *internalPacket, unsigned int numBytes, bool allowStack, const char *file, unsigned int line); + void FreeInternalPacketData(InternalPacket *internalPacket, const char *file, unsigned int line); + DataStructures::MemoryPool refCountedDataPool; + + BPSTracker bpsMetrics[RNS_PER_SECOND_METRICS_COUNT]; + CCTimeType lastBpsClear; + +#if LIBCAT_SECURITY==1 +public: + cat::AuthenticatedEncryption* GetAuthenticatedEncryption(void) { return &auth_enc; } + +protected: + cat::AuthenticatedEncryption auth_enc; + bool useSecurity; +#endif // LIBCAT_SECURITY +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/ReplicaEnums.h b/src/raknet/ReplicaEnums.h index 93aeef8..7b0b4a0 100755 --- a/src/raknet/ReplicaEnums.h +++ b/src/raknet/ReplicaEnums.h @@ -1,43 +1,43 @@ -/// \file -/// \brief Contains enumerations used by the ReplicaManager system. This file is a lightweight header, so you can include it without worrying about linking in lots of other crap -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __REPLICA_ENUMS_H -#define __REPLICA_ENUMS_H - -/// Replica interface flags, used to enable and disable function calls on the Replica object -/// Passed to ReplicaManager::EnableReplicaInterfaces and ReplicaManager::DisableReplicaInterfaces -enum -{ - REPLICA_RECEIVE_DESTRUCTION=1<<0, - REPLICA_RECEIVE_SERIALIZE=1<<1, - REPLICA_RECEIVE_SCOPE_CHANGE=1<<2, - REPLICA_SEND_CONSTRUCTION=1<<3, - REPLICA_SEND_DESTRUCTION=1<<4, - REPLICA_SEND_SCOPE_CHANGE=1<<5, - REPLICA_SEND_SERIALIZE=1<<6, - REPLICA_SET_ALL = 0xFF // Allow all of the above -}; - -enum ReplicaReturnResult -{ - /// This means call the function again later, with the same parameters - REPLICA_PROCESS_LATER, - /// This means we are done processing (the normal result to return) - REPLICA_PROCESSING_DONE, - /// This means cancel the processing - don't send any network messages and don't change the current state. - REPLICA_CANCEL_PROCESS, - /// Same as REPLICA_PROCESSING_DONE, where a message is sent, but does not clear the send bit. - /// Useful for multi-part sends with different reliability levels. - /// Only currently used by Replica::Serialize - REPLICA_PROCESS_AGAIN, - /// Only returned from the Replica::SendConstruction interface, means act as if the other system had this object but don't actually - /// Send a construction packet. This way you will still send scope and serialize packets to that system - REPLICA_PROCESS_IMPLICIT -}; - -#endif +/// \file +/// \brief Contains enumerations used by the ReplicaManager system. This file is a lightweight header, so you can include it without worrying about linking in lots of other crap +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __REPLICA_ENUMS_H +#define __REPLICA_ENUMS_H + +/// Replica interface flags, used to enable and disable function calls on the Replica object +/// Passed to ReplicaManager::EnableReplicaInterfaces and ReplicaManager::DisableReplicaInterfaces +enum +{ + REPLICA_RECEIVE_DESTRUCTION=1<<0, + REPLICA_RECEIVE_SERIALIZE=1<<1, + REPLICA_RECEIVE_SCOPE_CHANGE=1<<2, + REPLICA_SEND_CONSTRUCTION=1<<3, + REPLICA_SEND_DESTRUCTION=1<<4, + REPLICA_SEND_SCOPE_CHANGE=1<<5, + REPLICA_SEND_SERIALIZE=1<<6, + REPLICA_SET_ALL = 0xFF // Allow all of the above +}; + +enum ReplicaReturnResult +{ + /// This means call the function again later, with the same parameters + REPLICA_PROCESS_LATER, + /// This means we are done processing (the normal result to return) + REPLICA_PROCESSING_DONE, + /// This means cancel the processing - don't send any network messages and don't change the current state. + REPLICA_CANCEL_PROCESS, + /// Same as REPLICA_PROCESSING_DONE, where a message is sent, but does not clear the send bit. + /// Useful for multi-part sends with different reliability levels. + /// Only currently used by Replica::Serialize + REPLICA_PROCESS_AGAIN, + /// Only returned from the Replica::SendConstruction interface, means act as if the other system had this object but don't actually + /// Send a construction packet. This way you will still send scope and serialize packets to that system + REPLICA_PROCESS_IMPLICIT +}; + +#endif diff --git a/src/raknet/ReplicaManager3.cpp b/src/raknet/ReplicaManager3.cpp index 0bf0958..41d40c7 100755 --- a/src/raknet/ReplicaManager3.cpp +++ b/src/raknet/ReplicaManager3.cpp @@ -1,2250 +1,2250 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ReplicaManager3==1 - -#include "ReplicaManager3.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "NetworkIDManager.h" - -using namespace RakNet; - -// DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS(LastSerializationResult,Replica3*,replica); - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool PRO::operator==( const PRO& right ) const -{ - return priority == right.priority && reliability == right.reliability && orderingChannel == right.orderingChannel && sendReceipt == right.sendReceipt; -} - -bool PRO::operator!=( const PRO& right ) const -{ - return priority != right.priority || reliability != right.reliability || orderingChannel != right.orderingChannel || sendReceipt != right.sendReceipt; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -int Connection_RM3::Replica3LSRComp( Replica3 * const &replica3, LastSerializationResult * const &data ) -{ - if (replica3->GetNetworkID() < data->replica->GetNetworkID()) - return -1; - if (replica3->GetNetworkID() > data->replica->GetNetworkID()) - return 1; - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -LastSerializationResult::LastSerializationResult() -{ - replica=0; - lastSerializationResultBS=0; -} -LastSerializationResult::~LastSerializationResult() -{ - if (lastSerializationResultBS) - RakNet::OP_DELETE(lastSerializationResultBS,_FILE_AND_LINE_); -} -void LastSerializationResult::AllocBS(void) -{ - if (lastSerializationResultBS==0) - { - lastSerializationResultBS=RakNet::OP_NEW(_FILE_AND_LINE_); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ReplicaManager3::ReplicaManager3() -{ - defaultSendParameters.orderingChannel=0; - defaultSendParameters.priority=HIGH_PRIORITY; - defaultSendParameters.reliability=RELIABLE_ORDERED; - defaultSendParameters.sendReceipt=0; - autoSerializeInterval=30; - lastAutoSerializeOccurance=0; - worldId=0; - autoCreateConnections=true; - autoDestroyConnections=true; - networkIDManager=0; - currentlyDeallocatingReplica=0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ReplicaManager3::~ReplicaManager3() -{ - if (autoDestroyConnections) - { - // Clear() calls DeallocConnection(), which is pure virtual and cannot be called from the destructor - RakAssert(connectionList.Size()==0); - } - Clear(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetAutoManageConnections(bool autoCreate, bool autoDestroy) -{ - autoCreateConnections=autoCreate; - autoDestroyConnections=autoDestroy; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool ReplicaManager3::PushConnection(RakNet::Connection_RM3 *newConnection) -{ - if (newConnection==0) - return false; - if (GetConnectionByGUID(newConnection->GetRakNetGUID())) - return false; - unsigned int index = connectionList.GetIndexOf(newConnection); - if (index==(unsigned int)-1) - { - connectionList.Push(newConnection,_FILE_AND_LINE_); - - // Send message to validate the connection - newConnection->SendValidation(rakPeerInterface, worldId); - - Connection_RM3::ConstructionMode constructionMode = newConnection->QueryConstructionMode(); - if (constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - unsigned int pushIdx; - for (pushIdx=0; pushIdx < userReplicaList.Size(); pushIdx++) - newConnection->OnLocalReference(userReplicaList[pushIdx], this); - } - } - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::DeallocReplicaNoBroadcastDestruction(RakNet::Connection_RM3 *connection, RakNet::Replica3 *replica3) -{ - currentlyDeallocatingReplica=replica3; - replica3->DeallocReplica(connection); - currentlyDeallocatingReplica=0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RakNet::Connection_RM3 * ReplicaManager3::PopConnection(unsigned int index) -{ - DataStructures::List replicaList; - DataStructures::List destructionList; - DataStructures::List broadcastList; - RakNet::Connection_RM3 *connection; - unsigned int index2; - RM3ActionOnPopConnection action; - - connection=connectionList[index]; - - // Clear out downloadGroup - connection->ClearDownloadGroup(rakPeerInterface); - - RakNetGUID guid = connection->GetRakNetGUID(); - // This might be wrong, I am relying on the variable creatingSystemGuid which is transmitted - // automatically from the first system to reference the object. However, if an object changes - // owners then it is not going to be returned here, and therefore QueryActionOnPopConnection() - // will not be called for the new owner. - GetReplicasCreatedByGuid(guid, replicaList); - - for (index2=0; index2 < replicaList.Size(); index2++) - { - action = replicaList[index2]->QueryActionOnPopConnection(connection); - replicaList[index2]->OnPoppedConnection(connection); - if (action==RM3AOPC_DELETE_REPLICA) - { - destructionList.Push( replicaList[index2], _FILE_AND_LINE_ ); - } - else if (action==RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION) - { - destructionList.Push( replicaList[index2], _FILE_AND_LINE_ ); - - broadcastList.Push( replicaList[index2], _FILE_AND_LINE_ ); - } - } - - BroadcastDestructionList(broadcastList, connection->GetSystemAddress()); - for (index2=0; index2 < destructionList.Size(); index2++) - { - destructionList[index2]->PreDestruction(connection); - destructionList[index2]->DeallocReplica(connection); - } - - connectionList.RemoveAtIndex(index); - return connection; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RakNet::Connection_RM3 * ReplicaManager3::PopConnection(RakNetGUID guid) -{ - unsigned int index; - - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->GetRakNetGUID()==guid) - { - return PopConnection(index); - } - } - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::Reference(RakNet::Replica3 *replica3) -{ - unsigned int index = ReferenceInternal(replica3); - - if (index!=(unsigned int)-1) - { - unsigned int pushIdx; - for (pushIdx=0; pushIdx < connectionList.Size(); pushIdx++) - { - Connection_RM3::ConstructionMode constructionMode = connectionList[pushIdx]->QueryConstructionMode(); - if (constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - connectionList[pushIdx]->OnLocalReference(replica3, this); - } - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -unsigned int ReplicaManager3::ReferenceInternal(RakNet::Replica3 *replica3) -{ - unsigned int index; - index = userReplicaList.GetIndexOf(replica3); - if (index==(unsigned int)-1) - { - RakAssert(networkIDManager); - replica3->SetNetworkIDManager(networkIDManager); - if (replica3->creatingSystemGUID==UNASSIGNED_RAKNET_GUID) - replica3->creatingSystemGUID=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - replica3->replicaManager=this; - userReplicaList.Push(replica3,_FILE_AND_LINE_); - index=userReplicaList.Size()-1; - } - return index; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::Dereference(RakNet::Replica3 *replica3) -{ - unsigned int index, index2; - for (index=0; index < userReplicaList.Size(); index++) - { - if (userReplicaList[index]==replica3) - { - userReplicaList.RemoveAtIndex(index); - break; - } - } - - // Remove from all connections - for (index2=0; index2 < connectionList.Size(); index2++) - { - connectionList[index2]->OnDereference(replica3, this); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::DereferenceList(DataStructures::List &replicaListIn) -{ - unsigned int index; - for (index=0; index < replicaListIn.Size(); index++) - Dereference(replicaListIn[index]); -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::GetReplicasCreatedByMe(DataStructures::List &replicaListOut) -{ - RakNetGUID myGuid = rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - GetReplicasCreatedByGuid(rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS), replicaListOut); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::GetReferencedReplicaList(DataStructures::List &replicaListOut) -{ - replicaListOut=userReplicaList; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::GetReplicasCreatedByGuid(RakNetGUID guid, DataStructures::List &replicaListOut) -{ - replicaListOut.Clear(false,_FILE_AND_LINE_); - unsigned int index; - for (index=0; index < userReplicaList.Size(); index++) - { - if (userReplicaList[index]->creatingSystemGUID==guid) - replicaListOut.Push(userReplicaList[index],_FILE_AND_LINE_); - } -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -unsigned ReplicaManager3::GetReplicaCount(void) const -{ - return userReplicaList.Size(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Replica3 *ReplicaManager3::GetReplicaAtIndex(unsigned index) -{ - return userReplicaList[index]; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -unsigned int ReplicaManager3::GetConnectionCount(void) const -{ - return connectionList.Size(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3* ReplicaManager3::GetConnectionAtIndex(unsigned index) const -{ - return connectionList[index]; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3* ReplicaManager3::GetConnectionBySystemAddress(const SystemAddress &sa) const -{ - unsigned int index; - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->GetSystemAddress()==sa) - { - return connectionList[index]; - } - } - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3* ReplicaManager3::GetConnectionByGUID(RakNetGUID guid) const -{ - unsigned int index; - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->GetRakNetGUID()==guid) - { - return connectionList[index]; - } - } - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetDefaultOrderingChannel(char def) -{ - defaultSendParameters.orderingChannel=def; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetDefaultPacketPriority(PacketPriority def) -{ - defaultSendParameters.priority=def; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetDefaultPacketReliability(PacketReliability def) -{ - defaultSendParameters.reliability=def; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetAutoSerializeInterval(RakNet::Time intervalMS) -{ - autoSerializeInterval=intervalMS; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::GetConnectionsThatHaveReplicaConstructed(Replica3 *replica, DataStructures::List &connectionsThatHaveConstructedThisReplica) -{ - connectionsThatHaveConstructedThisReplica.Clear(false,_FILE_AND_LINE_); - unsigned int index; - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->HasReplicaConstructed(replica)) - connectionsThatHaveConstructedThisReplica.Push(connectionList[index],_FILE_AND_LINE_); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::Clear(void) -{ - if (autoDestroyConnections) - { - for (unsigned int i=0; i < connectionList.Size(); i++) - DeallocConnection(connectionList[i]); - } - else - { - // Clear out downloadGroup even if not auto destroying the connection, since the packets need to go back to RakPeer - for (unsigned int i=0; i < connectionList.Size(); i++) - connectionList[i]->ClearDownloadGroup(rakPeerInterface); - } - - - - connectionList.Clear(true,_FILE_AND_LINE_); - userReplicaList.Clear(true,_FILE_AND_LINE_); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PRO ReplicaManager3::GetDefaultSendParameters(void) const -{ - return defaultSendParameters; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetWorldID(unsigned char id) -{ - worldId=id; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -unsigned char ReplicaManager3::GetWorldID(void) const -{ - return worldId; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -NetworkIDManager *ReplicaManager3::GetNetworkIDManager(void) const -{ - return networkIDManager; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::SetNetworkIDManager(NetworkIDManager *_networkIDManager) -{ - networkIDManager=_networkIDManager; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnReceive(Packet *packet) -{ - if (packet->length<2) - return RR_CONTINUE_PROCESSING; - - unsigned char incomingWorldId; - - RakNet::Time timestamp=0; - unsigned char packetIdentifier, packetDataOffset; - if ( ( unsigned char ) packet->data[ 0 ] == ID_TIMESTAMP ) - { - if ( packet->length > sizeof( unsigned char ) + sizeof( RakNet::Time ) ) - { - packetIdentifier = ( unsigned char ) packet->data[ sizeof( unsigned char ) + sizeof( RakNet::Time ) ]; - // Required for proper endian swapping - RakNet::BitStream tsBs(packet->data+sizeof(MessageID),packet->length-1,false); - tsBs.Read(timestamp); - incomingWorldId=packet->data[sizeof( unsigned char )*2 + sizeof( RakNet::Time )]; - packetDataOffset=sizeof( unsigned char )*3 + sizeof( RakNet::Time ); - } - else - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - else - { - packetIdentifier = ( unsigned char ) packet->data[ 0 ]; - incomingWorldId=packet->data[sizeof( unsigned char )]; - packetDataOffset=sizeof( unsigned char )*2; - } - - switch (packetIdentifier) - { - case ID_REPLICA_MANAGER_CONSTRUCTION: - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - return OnConstruction(packet, packet->data, packet->length, packet->guid, packetDataOffset); - case ID_REPLICA_MANAGER_SERIALIZE: - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - return OnSerialize(packet, packet->data, packet->length, packet->guid, timestamp, packetDataOffset); - case ID_REPLICA_MANAGER_DOWNLOAD_STARTED: - if (packet->wasGeneratedLocally==false) - { - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - return OnDownloadStarted(packet, packet->data, packet->length, packet->guid, packetDataOffset); - } - else - break; - case ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE: - if (packet->wasGeneratedLocally==false) - { - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - return OnDownloadComplete(packet, packet->data, packet->length, packet->guid, packetDataOffset); - } - else - break; - case ID_REPLICA_MANAGER_SCOPE_CHANGE: - { - if (incomingWorldId!=worldId) - return RR_CONTINUE_PROCESSING; - - Connection_RM3 *connection = GetConnectionByGUID(packet->guid); - if (connection && connection->isValidated==false) - { - // This connection is now confirmed bidirectional - connection->isValidated=true; - // Reply back on validation - connection->SendValidation(rakPeerInterface,worldId); - } - } - } - - return RR_CONTINUE_PROCESSING; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::AutoConstructByQuery(ReplicaManager3 *replicaManager3) -{ - ValidateLists(replicaManager3); - - ConstructionMode constructionMode = QueryConstructionMode(); - - unsigned int index; - RM3ConstructionState constructionState; - LastSerializationResult *lsr; - index=0; - - constructedReplicasCulled.Clear(false,_FILE_AND_LINE_); - destroyedReplicasCulled.Clear(false,_FILE_AND_LINE_); - - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - while (index < queryToConstructReplicaList.Size()) - { - lsr=queryToConstructReplicaList[index]; - constructionState=lsr->replica->QueryConstruction(this, replicaManager3); - if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY || constructionState==RM3CS_ALREADY_EXISTS_REMOTELY_DO_NOT_CONSTRUCT) - { - OnReplicaAlreadyExists(index, replicaManager3); - if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY) - constructedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); - - /* - if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY) - { - // Serialize construction data to this connection - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_REPLICA_MANAGER_3_SERIALIZE_CONSTRUCTION_EXISTING); - bsOut.Write(replicaManager3->GetWorldID()); - NetworkID networkId; - networkId=lsr->replica->GetNetworkID(); - bsOut.Write(networkId); - BitSize_t bitsWritten = bsOut.GetNumberOfBitsUsed(); - lsr->replica->SerializeConstructionExisting(&bsOut, this); - if (bsOut.GetNumberOfBitsUsed()!=bitsWritten) - replicaManager3->SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,GetSystemAddress(), false); - } - - // Serialize first serialization to this connection. - // This is done here, as it isn't done in PushConstruction - SerializeParameters sp; - RakNet::BitStream emptyBs; - for (index=0; index < (unsigned int) RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; index++) - { - sp.lastSentBitstream[index]=&emptyBs; - sp.pro[index]=replicaManager3->GetDefaultSendParameters(); - } - sp.bitsWrittenSoFar=0; - sp.destinationConnection=this; - sp.messageTimestamp=0; - sp.whenLastSerialized=0; - - RakNet::Replica3 *replica = lsr->replica; - - RM3SerializationResult res = replica->Serialize(&sp); - if (res!=RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION && - res!=RM3SR_DO_NOT_SERIALIZE && - res!=RM3SR_SERIALIZED_UNIQUELY) - { - bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp.bitsWrittenSoFar+=sp.outputBitstream[z].GetNumberOfBitsUsed(); - allIndices[z]=true; - } - if (SendSerialize(replica, allIndices, sp.outputBitstream, sp.messageTimestamp, sp.pro, replicaManager3->GetRakPeerInterface(), replicaManager3->GetWorldID())==SSICR_SENT_DATA) - lsr->replica->whenLastSerialized=RakNet::GetTimeMS(); - } - */ - } - else if (constructionState==RM3CS_SEND_CONSTRUCTION) - { - OnConstructToThisConnection(index, replicaManager3); - constructedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); - } - else if (constructionState==RM3CS_NEVER_CONSTRUCT) - { - OnNeverConstruct(index, replicaManager3); - } - else// if (constructionState==RM3CS_NO_ACTION) - { - // Do nothing - index++; - } - } - - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - RM3DestructionState destructionState; - index=0; - while (index < queryToDestructReplicaList.Size()) - { - lsr=queryToDestructReplicaList[index]; - destructionState=lsr->replica->QueryDestruction(this, replicaManager3); - if (destructionState==RM3DS_SEND_DESTRUCTION) - { - OnSendDestructionFromQuery(index, replicaManager3); - destroyedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); - } - else if (destructionState==RM3DS_DO_NOT_QUERY_DESTRUCTION) - { - OnDoNotQueryDestruction(index, replicaManager3); - } - else// if (destructionState==RM3CS_NO_ACTION) - { - // Do nothing - index++; - } - } - } - } - else if (constructionMode==QUERY_CONNECTION_FOR_REPLICA_LIST) - { - QueryReplicaList(constructedReplicasCulled,destroyedReplicasCulled); - - unsigned int idx1, idx2; - - // Create new - for (idx2=0; idx2 < constructedReplicasCulled.Size(); idx2++) - OnConstructToThisConnection(constructedReplicasCulled[idx2], replicaManager3); - - bool exists; - for (idx2=0; idx2 < destroyedReplicasCulled.Size(); idx2++) - { - exists=false; - bool objectExists; - idx1=constructedReplicaList.GetIndexFromKey(destroyedReplicasCulled[idx2], &objectExists); - RakAssert(objectExists); - if (objectExists) - { - OnSendDestructionFromQuery(idx1,replicaManager3); - } - - // If this assert hits, the user tried to destroy a replica that doesn't exist on the remote system - RakAssert(exists); - } - } - - SendConstruction(constructedReplicasCulled,destroyedReplicasCulled,replicaManager3->defaultSendParameters,replicaManager3->rakPeerInterface,replicaManager3->worldId,replicaManager3); -} -void ReplicaManager3::Update(void) -{ - unsigned int index,index2; - - for (index=0; index < connectionList.Size(); index++) - { - if (connectionList[index]->isValidated==false) - continue; - connectionList[index]->AutoConstructByQuery(this); - } - - RakNet::Time time = RakNet::GetTimeMS(); - - if (time - lastAutoSerializeOccurance >= autoSerializeInterval) - { - for (index=0; index < userReplicaList.Size(); index++) - { - userReplicaList[index]->forceSendUntilNextUpdate=false; - userReplicaList[index]->OnUserReplicaPreSerializeTick(); - } - - - unsigned int index; - SerializeParameters sp; - sp.curTime=time; - Connection_RM3 *connection; - SendSerializeIfChangedResult ssicr; - sp.messageTimestamp=0; - for (int i=0; i < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; i++) - sp.pro[i]=defaultSendParameters; - index2=0; - for (index=0; index < connectionList.Size(); index++) - { - connection = connectionList[index]; - sp.bitsWrittenSoFar=0; - index2=0; - while (index2 < connection->queryToSerializeReplicaList.Size()) - { - sp.destinationConnection=connection; - sp.whenLastSerialized=connection->queryToSerializeReplicaList[index2]->replica->whenLastSerialized; - ssicr=connection->SendSerializeIfChanged(index2, &sp, GetRakPeerInterface(), GetWorldID(), this); - if (ssicr==SSICR_SENT_DATA) - { - connection->queryToSerializeReplicaList[index2]->replica->whenLastSerialized=time; - index2++; - } - else if (ssicr==SSICR_NEVER_SERIALIZE) - { - // Removed from the middle of the list - } - else - index2++; - } - } - - lastAutoSerializeOccurance=time; - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - if (autoDestroyConnections) - { - Connection_RM3 *connection = PopConnection(rakNetGUID); - if (connection) - DeallocConnection(connection); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) -{ - (void) isIncoming; - if (autoCreateConnections) - { - Connection_RM3 *connection = AllocConnection(systemAddress, rakNetGUID); - if (connection) - PushConnection(connection); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::OnRakPeerShutdown(void) -{ - if (autoDestroyConnections) - { - while (connectionList.Size()) - { - Connection_RM3 *connection = PopConnection(connectionList.Size()-1); - if (connection) - DeallocConnection(connection); - } - } - - Clear(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void ReplicaManager3::OnDetach(void) -{ - OnRakPeerShutdown(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnConstruction(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) -{ - Connection_RM3 *connection = GetConnectionByGUID(senderGuid); - if (connection==0) - { - // Almost certainly a bug - RakAssert("Got OnConstruction but no connection yet" && 0); - return RR_CONTINUE_PROCESSING; - } - if (connection->groupConstructionAndSerialize) - { - connection->downloadGroup.Push(packet, __FILE__, __LINE__); - return RR_STOP_PROCESSING; - } - - RakNet::BitStream bsIn(packetData,packetDataLength,false); - bsIn.IgnoreBytes(packetDataOffset); - uint16_t constructionObjectListSize, destructionObjectListSize, index, index2; - BitSize_t streamEnd, writeAllocationIDEnd; - Replica3 *replica; - NetworkID networkId; - RakNetGUID creatingSystemGuid; - bool actuallyCreateObject=false; - - DataStructures::List actuallyCreateObjectList; - DataStructures::List constructionTickStack; - - RakAssert(networkIDManager); - - bsIn.Read(constructionObjectListSize); - for (index=0; index < constructionObjectListSize; index++) - { - bsIn.Read(streamEnd); - bsIn.Read(networkId); - Replica3* existingReplica = networkIDManager->GET_OBJECT_FROM_ID(networkId); - bsIn.Read(actuallyCreateObject); - actuallyCreateObjectList.Push(actuallyCreateObject, _FILE_AND_LINE_); - bsIn.AlignReadToByteBoundary(); - - if (actuallyCreateObject) - { - bsIn.Read(creatingSystemGuid); - bsIn.Read(writeAllocationIDEnd); - - //printf("OnConstruction: %i\n",networkId.guid.g); // Removeme - if (existingReplica) - { - existingReplica->replicaManager=this; - - // Network ID already in use - connection->OnDownloadExisting(existingReplica, this); - - constructionTickStack.Push(0, _FILE_AND_LINE_); - bsIn.SetReadOffset(streamEnd); - continue; - } - - bsIn.AlignReadToByteBoundary(); - replica = connection->AllocReplica(&bsIn, this); - if (replica==0) - { - constructionTickStack.Push(0, _FILE_AND_LINE_); - bsIn.SetReadOffset(streamEnd); - continue; - } - - // Go past the bitStream written to with WriteAllocationID(). Necessary in case the user didn't read out the bitStream the same way it was written - // bitOffset2 is already aligned - bsIn.SetReadOffset(writeAllocationIDEnd); - - replica->SetNetworkIDManager(networkIDManager); - replica->SetNetworkID(networkId); - - replica->replicaManager=this; - replica->creatingSystemGUID=creatingSystemGuid; - - if (!replica->QueryRemoteConstruction(connection) || - !replica->DeserializeConstruction(&bsIn, connection)) - { - DeallocReplicaNoBroadcastDestruction(connection, replica); - bsIn.SetReadOffset(streamEnd); - constructionTickStack.Push(0, _FILE_AND_LINE_); - continue; - } - - constructionTickStack.Push(replica, _FILE_AND_LINE_); - - // Register the replica - ReferenceInternal(replica); - } - else - { - if (existingReplica) - { - existingReplica->DeserializeConstructionExisting(&bsIn, connection); - constructionTickStack.Push(existingReplica, _FILE_AND_LINE_); - } - else - { - constructionTickStack.Push(0, _FILE_AND_LINE_); - } - } - - - bsIn.SetReadOffset(streamEnd); - bsIn.AlignReadToByteBoundary(); - } - - RakAssert(constructionTickStack.Size()==constructionObjectListSize); - RakAssert(actuallyCreateObjectList.Size()==constructionObjectListSize); - - RakNet::BitStream empty; - for (index=0; index < constructionObjectListSize; index++) - { - bool pdcWritten=false; - bsIn.Read(pdcWritten); - if (pdcWritten) - { - bsIn.AlignReadToByteBoundary(); - bsIn.Read(streamEnd); - bsIn.Read(networkId); - if (constructionTickStack[index]!=0) - { - bsIn.AlignReadToByteBoundary(); - if (actuallyCreateObjectList[index]) - constructionTickStack[index]->PostDeserializeConstruction(&bsIn, connection); - else - constructionTickStack[index]->PostDeserializeConstructionExisting(&bsIn, connection); - } - bsIn.SetReadOffset(streamEnd); - } - else - { - if (constructionTickStack[index]!=0) - constructionTickStack[index]->PostDeserializeConstruction(&empty, connection); - } - } - - for (index=0; index < constructionObjectListSize; index++) - { - if (constructionTickStack[index]!=0) - { - if (actuallyCreateObjectList[index]) - { - // Tell the connection(s) that this object exists since they just sent it to us - connection->OnDownloadFromThisSystem(constructionTickStack[index], this); - - for (index2=0; index2 < connectionList.Size(); index2++) - { - if (connectionList[index2]!=connection) - connectionList[index2]->OnDownloadFromOtherSystem(constructionTickStack[index], this); - } - } - } - } - - // Destructions - bool b = bsIn.Read(destructionObjectListSize); - (void) b; - RakAssert(b); - for (index=0; index < destructionObjectListSize; index++) - { - bsIn.Read(networkId); - bsIn.Read(streamEnd); - replica = networkIDManager->GET_OBJECT_FROM_ID(networkId); - if (replica==0) - { - // Unknown object - bsIn.SetReadOffset(streamEnd); - continue; - } - bsIn.Read(replica->deletingSystemGUID); - if (replica->DeserializeDestruction(&bsIn,connection)) - { - // Make sure it wasn't deleted in DeserializeDestruction - if (networkIDManager->GET_OBJECT_FROM_ID(networkId)) - { - replica->PreDestruction(connection); - - // Forward deletion by remote system - if (replica->QueryRelayDestruction(connection)) - BroadcastDestruction(replica,connection->GetSystemAddress()); - Dereference(replica); - DeallocReplicaNoBroadcastDestruction(connection, replica); - } - } - else - { - replica->PreDestruction(connection); - connection->OnDereference(replica, this); - } - - bsIn.AlignReadToByteBoundary(); - } - return RR_CONTINUE_PROCESSING; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnSerialize(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, RakNet::Time timestamp, unsigned char packetDataOffset) -{ - Connection_RM3 *connection = GetConnectionByGUID(senderGuid); - if (connection==0) - return RR_CONTINUE_PROCESSING; - if (connection->groupConstructionAndSerialize) - { - connection->downloadGroup.Push(packet, __FILE__, __LINE__); - return RR_STOP_PROCESSING; - } - RakAssert(networkIDManager); - RakNet::BitStream bsIn(packetData,packetDataLength,false); - bsIn.IgnoreBytes(packetDataOffset); - - struct DeserializeParameters ds; - ds.timeStamp=timestamp; - ds.sourceConnection=connection; - - Replica3 *replica; - NetworkID networkId; - BitSize_t bitsUsed; - bsIn.Read(networkId); - //printf("OnSerialize: %i\n",networkId.guid.g); // Removeme - replica = networkIDManager->GET_OBJECT_FROM_ID(networkId); - if (replica) - { - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - bsIn.Read(ds.bitstreamWrittenTo[z]); - if (ds.bitstreamWrittenTo[z]) - { - bsIn.ReadCompressed(bitsUsed); - bsIn.AlignReadToByteBoundary(); - bsIn.Read(ds.serializationBitstream[z], bitsUsed); - } - } - replica->Deserialize(&ds); - } - return RR_CONTINUE_PROCESSING; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnDownloadStarted(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) -{ - Connection_RM3 *connection = GetConnectionByGUID(senderGuid); - if (connection==0) - return RR_CONTINUE_PROCESSING; - if (connection->QueryGroupDownloadMessages() && - // ID_DOWNLOAD_STARTED will be processed twice, being processed the second time once ID_DOWNLOAD_COMPLETE arrives. - // However, the second time groupConstructionAndSerialize will be set to true so it won't be processed a third time - connection->groupConstructionAndSerialize==false - ) - { - // These messages will be held by the plugin and returned when the download is complete - connection->groupConstructionAndSerialize=true; - RakAssert(connection->downloadGroup.Size()==0); - connection->downloadGroup.Push(packet, __FILE__, __LINE__); - return RR_STOP_PROCESSING; - } - - connection->groupConstructionAndSerialize=false; - RakNet::BitStream bsIn(packetData,packetDataLength,false); - bsIn.IgnoreBytes(packetDataOffset); - connection->DeserializeOnDownloadStarted(&bsIn); - return RR_CONTINUE_PROCESSING; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -PluginReceiveResult ReplicaManager3::OnDownloadComplete(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) -{ - Connection_RM3 *connection = GetConnectionByGUID(senderGuid); - if (connection==0) - return RR_CONTINUE_PROCESSING; - - if (connection->groupConstructionAndSerialize==true && connection->downloadGroup.Size()>0) - { - // Push back buffered packets in front of this one - unsigned int i; - for (i=0; i < connection->downloadGroup.Size(); i++) - rakPeerInterface->PushBackPacket(connection->downloadGroup[i],false); - - // Push this one to be last too. It will be processed again, but the second time - // groupConstructionAndSerialize will be false and downloadGroup will be empty, so it will go past this block - connection->downloadGroup.Clear(__FILE__,__LINE__); - rakPeerInterface->PushBackPacket(packet,false); - - return RR_STOP_PROCESSING; - } - - RakNet::BitStream bsIn(packetData,packetDataLength,false); - bsIn.IgnoreBytes(packetDataOffset); - connection->DeserializeOnDownloadComplete(&bsIn); - return RR_CONTINUE_PROCESSING; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Replica3* ReplicaManager3::GetReplicaByNetworkID(NetworkID networkId) -{ - unsigned int i; - for (i=0; i < userReplicaList.Size(); i++) - { - if (userReplicaList[i]->GetNetworkID()==networkId) - return userReplicaList[i]; - } - return 0; -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - - -void ReplicaManager3::BroadcastDestructionList(DataStructures::List &replicaListSource, const SystemAddress &exclusionAddress) -{ - RakNet::BitStream bsOut; - unsigned int i,j; - - DataStructures::List replicaList; - - for (i=0; i < replicaListSource.Size(); i++) - { - if (replicaListSource[i]==currentlyDeallocatingReplica) - continue; - replicaList.Push(replicaListSource[i], __FILE__, __LINE__); - } - - if (replicaList.Size()==0) - return; - - for (i=0; i < replicaList.Size(); i++) - { - if (replicaList[i]->deletingSystemGUID==UNASSIGNED_RAKNET_GUID) - replicaList[i]->deletingSystemGUID=GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - } - - for (j=0; j < connectionList.Size(); j++) - { - if (connectionList[j]->GetSystemAddress()==exclusionAddress) - continue; - - bsOut.Reset(); - bsOut.Write((MessageID)ID_REPLICA_MANAGER_CONSTRUCTION); - bsOut.Write(worldId); - uint16_t cnt=0; - bsOut.Write(cnt); // No construction - cnt=(uint16_t) replicaList.Size(); - BitSize_t cntOffset=bsOut.GetWriteOffset();; - bsOut.Write(cnt); // Overwritten at send call - cnt=0; - - for (i=0; i < replicaList.Size(); i++) - { - if (connectionList[j]->HasReplicaConstructed(replicaList[i])==false) - continue; - cnt++; - - NetworkID networkId; - networkId=replicaList[i]->GetNetworkID(); - bsOut.Write(networkId); - BitSize_t offsetStart, offsetEnd; - offsetStart=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart); - bsOut.Write(replicaList[i]->deletingSystemGUID); - replicaList[i]->SerializeDestruction(&bsOut, connectionList[j]); - bsOut.AlignWriteToByteBoundary(); - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - } - - if (cnt>0) - { - BitSize_t curOffset=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(cntOffset); - bsOut.Write(cnt); - bsOut.SetWriteOffset(curOffset); - rakPeerInterface->Send(&bsOut,defaultSendParameters.priority,defaultSendParameters.reliability,defaultSendParameters.orderingChannel,connectionList[j]->GetSystemAddress(),false, defaultSendParameters.sendReceipt); - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - - -void ReplicaManager3::BroadcastDestruction(Replica3 *replica, const SystemAddress &exclusionAddress) -{ - DataStructures::List replicaList; - replicaList.Push(replica, _FILE_AND_LINE_ ); - BroadcastDestructionList(replicaList,exclusionAddress); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3::Connection_RM3(const SystemAddress &_systemAddress, RakNetGUID _guid) -: systemAddress(_systemAddress), guid(_guid) -{ - isValidated=false; - isFirstConstruction=true; - groupConstructionAndSerialize=false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Connection_RM3::~Connection_RM3() -{ - unsigned int i; - for (i=0; i < constructedReplicaList.Size(); i++) - RakNet::OP_DELETE(constructedReplicaList[i], _FILE_AND_LINE_); - for (i=0; i < queryToConstructReplicaList.Size(); i++) - RakNet::OP_DELETE(queryToConstructReplicaList[i], _FILE_AND_LINE_); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::GetConstructedReplicas(DataStructures::List &objectsTheyDoHave) -{ - objectsTheyDoHave.Clear(true,_FILE_AND_LINE_); - for (unsigned int idx=0; idx < constructedReplicaList.Size(); idx++) - { - objectsTheyDoHave.Push(constructedReplicaList[idx]->replica, _FILE_AND_LINE_ ); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool Connection_RM3::HasReplicaConstructed(RakNet::Replica3 *replica) -{ - bool objectExists; - constructedReplicaList.GetIndexFromKey(replica, &objectExists); - return objectExists; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void Connection_RM3::SendSerializeHeader(RakNet::Replica3 *replica, RakNet::Time timestamp, RakNet::BitStream *bs, unsigned char worldId) -{ - bs->Reset(); - - if (timestamp!=0) - { - bs->Write((MessageID)ID_TIMESTAMP); - bs->Write(timestamp); - } - bs->Write((MessageID)ID_REPLICA_MANAGER_SERIALIZE); - bs->Write(worldId); - bs->Write(replica->GetNetworkID()); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void Connection_RM3::ClearDownloadGroup(RakPeerInterface *rakPeerInterface) -{ - unsigned int i; - for (i=0; i < downloadGroup.Size(); i++) - rakPeerInterface->DeallocatePacket(downloadGroup[i]); - downloadGroup.Clear(__FILE__,__LINE__); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SendSerializeIfChangedResult Connection_RM3::SendSerialize(RakNet::Replica3 *replica, bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::BitStream serializationData[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::Time timestamp, PRO sendParameters[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakPeerInterface *rakPeer, unsigned char worldId) -{ - bool channelHasData; - BitSize_t sum=0; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - if (indicesToSend[z]) - sum+=serializationData[z].GetNumberOfBitsUsed(); - } - if (sum==0) - return SSICR_DID_NOT_SEND_DATA; - - RakAssert(replica->GetNetworkID()!=UNASSIGNED_NETWORK_ID); - - RakNet::BitStream out; - BitSize_t bitsUsed; - - int channelIndex; - PRO lastPro=sendParameters[0]; - - for (channelIndex=0; channelIndex < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex++) - { - if (channelIndex==0) - { - SendSerializeHeader(replica, timestamp, &out, worldId); - } - else if (lastPro!=sendParameters[channelIndex]) - { - // Write out remainder - for (int channelIndex2=channelIndex; channelIndex2 < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex2++) - out.Write(false); - - // Send remainder - replica->OnSerializeTransmission(&out, systemAddress); - rakPeer->Send(&out,lastPro.priority,lastPro.reliability,lastPro.orderingChannel,systemAddress,false,lastPro.sendReceipt); - - // If no data left to send, quit out - bool anyData=false; - for (int channelIndex2=channelIndex; channelIndex2 < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex2++) - { - if (serializationData[channelIndex2].GetNumberOfBitsUsed()>0) - { - anyData=true; - break; - } - } - if (anyData==false) - return SSICR_SENT_DATA; - - // Restart stream - SendSerializeHeader(replica, timestamp, &out, worldId); - - for (int channelIndex2=0; channelIndex2 < channelIndex; channelIndex2++) - out.Write(false); - lastPro=sendParameters[channelIndex]; - } - - bitsUsed=serializationData[channelIndex].GetNumberOfBitsUsed(); - channelHasData = indicesToSend[channelIndex]==true && bitsUsed>0; - out.Write(channelHasData); - if (channelHasData) - { - out.WriteCompressed(bitsUsed); - out.AlignWriteToByteBoundary(); - out.Write(serializationData[channelIndex]); - // Crap, forgot this line, was a huge bug in that I'd only send to the first 3 systems - serializationData[channelIndex].ResetReadPointer(); - } - } - replica->OnSerializeTransmission(&out, systemAddress); - rakPeer->Send(&out,lastPro.priority,lastPro.reliability,lastPro.orderingChannel,systemAddress,false,lastPro.sendReceipt); - return SSICR_SENT_DATA; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -SendSerializeIfChangedResult Connection_RM3::SendSerializeIfChanged(unsigned int queryToSerializeIndex, SerializeParameters *sp, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager) -{ - RakNet::Replica3 *replica = queryToSerializeReplicaList[queryToSerializeIndex]->replica; - - if (replica->GetNetworkID()==UNASSIGNED_NETWORK_ID) - return SSICR_DID_NOT_SEND_DATA; - - RM3QuerySerializationResult rm3qsr = replica->QuerySerialization(this); - if (rm3qsr==RM3QSR_NEVER_CALL_SERIALIZE) - { - // Never again for this connection and replica pair - OnNeverSerialize(queryToSerializeIndex, replicaManager); - return SSICR_NEVER_SERIALIZE; - } - - if (rm3qsr==RM3QSR_DO_NOT_CALL_SERIALIZE) - return SSICR_DID_NOT_SEND_DATA; - - if (replica->forceSendUntilNextUpdate) - { - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - if (replica->lastSentSerialization.indicesToSend[z]) - sp->bitsWrittenSoFar+=replica->lastSentSerialization.bitStream[z].GetNumberOfBitsUsed(); - } - return SendSerialize(replica, replica->lastSentSerialization.indicesToSend, replica->lastSentSerialization.bitStream, sp->messageTimestamp, sp->pro, rakPeer, worldId); - } - - for (int i=0; i < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; i++) - { - sp->outputBitstream[i].Reset(); - if (queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS) - sp->lastSentBitstream[i]=&queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[i]; - else - sp->lastSentBitstream[i]=&replica->lastSentSerialization.bitStream[i]; - } - - RM3SerializationResult serializationResult = replica->Serialize(sp); - - if (serializationResult==RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION) - { - // Never again for this connection and replica pair - OnNeverSerialize(queryToSerializeIndex, replicaManager); - return SSICR_NEVER_SERIALIZE; - } - - if (serializationResult==RM3SR_DO_NOT_SERIALIZE) - { - // Don't serialize this tick only - return SSICR_DID_NOT_SEND_DATA; - } - - // This is necessary in case the user in the Serialize() function for some reason read the bitstream they also wrote - // WIthout this code, the Write calls to another bitstream would not write the entire bitstream - BitSize_t sum=0; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp->outputBitstream[z].ResetReadPointer(); - sum+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - } - - if (sum==0) - { - // Don't serialize this tick only - return SSICR_DID_NOT_SEND_DATA; - } - - if (serializationResult==RM3SR_SERIALIZED_ALWAYS) - { - bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - allIndices[z]=true; - - queryToSerializeReplicaList[queryToSerializeIndex]->AllocBS(); - queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Reset(); - queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Write(&sp->outputBitstream[z]); - sp->outputBitstream[z].ResetReadPointer(); - } - return SendSerialize(replica, allIndices, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); - } - - if (serializationResult==RM3SR_SERIALIZED_ALWAYS_IDENTICALLY) - { - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - replica->lastSentSerialization.indicesToSend[z]=sp->outputBitstream[z].GetNumberOfBitsUsed()>0; - sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - replica->lastSentSerialization.bitStream[z].Reset(); - replica->lastSentSerialization.bitStream[z].Write(&sp->outputBitstream[z]); - sp->outputBitstream[z].ResetReadPointer(); - replica->forceSendUntilNextUpdate=true; - } - return SendSerialize(replica, replica->lastSentSerialization.indicesToSend, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); - } - - bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - if (serializationResult==RM3SR_BROADCAST_IDENTICALLY || serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION) - { - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - if (sp->outputBitstream[z].GetNumberOfBitsUsed() > 0 && - (serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION || - ((sp->outputBitstream[z].GetNumberOfBitsUsed()!=replica->lastSentSerialization.bitStream[z].GetNumberOfBitsUsed() || - memcmp(sp->outputBitstream[z].GetData(), replica->lastSentSerialization.bitStream[z].GetData(), sp->outputBitstream[z].GetNumberOfBytesUsed())!=0)))) - { - indicesToSend[z]=true; - replica->lastSentSerialization.indicesToSend[z]=true; - sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - replica->lastSentSerialization.bitStream[z].Reset(); - replica->lastSentSerialization.bitStream[z].Write(&sp->outputBitstream[z]); - sp->outputBitstream[z].ResetReadPointer(); - replica->forceSendUntilNextUpdate=true; - } - else - { - indicesToSend[z]=false; - replica->lastSentSerialization.indicesToSend[z]=false; - } - } - } - else - { - queryToSerializeReplicaList[queryToSerializeIndex]->AllocBS(); - - // RM3SR_SERIALIZED_UNIQUELY - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - if (sp->outputBitstream[z].GetNumberOfBitsUsed() > 0 && - (sp->outputBitstream[z].GetNumberOfBitsUsed()!=queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].GetNumberOfBitsUsed() || - memcmp(sp->outputBitstream[z].GetData(), queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].GetData(), sp->outputBitstream[z].GetNumberOfBytesUsed())!=0) - ) - { - indicesToSend[z]=true; - sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); - queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Reset(); - queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Write(&sp->outputBitstream[z]); - sp->outputBitstream[z].ResetReadPointer(); - } - else - { - indicesToSend[z]=false; - } - } - } - - - if (serializationResult==RM3SR_BROADCAST_IDENTICALLY || serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION) - replica->forceSendUntilNextUpdate=true; - - // Send out the data - return SendSerialize(replica, indicesToSend, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void Connection_RM3::OnLocalReference(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) replicaManager; - (void) constructionMode; - - LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); - lsr->replica=replica3; - queryToConstructReplicaList.Push(lsr,_FILE_AND_LINE_); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDereference(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - - LastSerializationResult* lsr=0; - unsigned int idx; - - bool objectExists; - idx=constructedReplicaList.GetIndexFromKey(replica3, &objectExists); - if (objectExists) - { - lsr=constructedReplicaList[idx]; - constructedReplicaList.RemoveAtIndex(idx); - } - - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - if (queryToConstructReplicaList[idx]->replica==replica3) - { - lsr=queryToConstructReplicaList[idx]; - queryToConstructReplicaList.RemoveAtIndex(idx); - break; - } - } - - for (idx=0; idx < queryToSerializeReplicaList.Size(); idx++) - { - if (queryToSerializeReplicaList[idx]->replica==replica3) - { - lsr=queryToSerializeReplicaList[idx]; - queryToSerializeReplicaList.RemoveAtIndex(idx); - break; - } - } - - for (idx=0; idx < queryToDestructReplicaList.Size(); idx++) - { - if (queryToDestructReplicaList[idx]->replica==replica3) - { - lsr=queryToDestructReplicaList[idx]; - queryToDestructReplicaList.RemoveAtIndex(idx); - break; - } - } - - ValidateLists(replicaManager); - - if (lsr) - RakNet::OP_DELETE(lsr,_FILE_AND_LINE_); - - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDownloadFromThisSystem(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); - lsr->replica=replica3; - - ConstructionMode constructionMode = QueryConstructionMode(); - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - unsigned int j; - for (j=0; j < queryToConstructReplicaList.Size(); j++) - { - if (queryToConstructReplicaList[j]->replica->GetNetworkID()==replica3->GetNetworkID() ) - { - queryToConstructReplicaList.RemoveAtIndex(j); - break; - } - } - - queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); - } - - constructedReplicaList.Insert(lsr->replica, lsr, true, _FILE_AND_LINE_); - //assert(queryToSerializeReplicaList.GetIndexOf(replica3)==(unsigned int)-1); - queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); - - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDownloadFromOtherSystem(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - unsigned int j; - for (j=0; j < queryToConstructReplicaList.Size(); j++) - { - if (queryToConstructReplicaList[j]->replica->GetNetworkID()==replica3->GetNetworkID() ) - { - return; - } - } - - OnLocalReference(replica3, replicaManager); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnNeverConstruct(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) constructionMode; - - ValidateLists(replicaManager); - LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; - queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); - RakNet::OP_DELETE(lsr,_FILE_AND_LINE_); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnConstructToThisConnection(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) constructionMode; - - ValidateLists(replicaManager); - LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; - queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); - //assert(constructedReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - constructedReplicaList.Insert(lsr->replica,lsr,true,_FILE_AND_LINE_); - //assert(queryToDestructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); - //assert(queryToSerializeReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnConstructToThisConnection(Replica3 *replica, ReplicaManager3 *replicaManager) -{ - RakAssert(QueryConstructionMode()==QUERY_CONNECTION_FOR_REPLICA_LIST); - (void) replicaManager; - - LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); - lsr->replica=replica; - constructedReplicaList.Insert(replica,lsr,true,_FILE_AND_LINE_); - queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnNeverSerialize(unsigned int queryToSerializeIndex, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - queryToSerializeReplicaList.RemoveAtIndex(queryToSerializeIndex); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnReplicaAlreadyExists(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) constructionMode; - - ValidateLists(replicaManager); - LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; - queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); - //assert(constructedReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - constructedReplicaList.Insert(lsr->replica,lsr,true,_FILE_AND_LINE_); - //assert(queryToDestructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); - //assert(queryToSerializeReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDownloadExisting(Replica3* replica3, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - - ConstructionMode constructionMode = QueryConstructionMode(); - if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) - { - unsigned int idx; - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - if (queryToConstructReplicaList[idx]->replica==replica3) - { - OnConstructToThisConnection(idx, replicaManager); - return; - } - } - } - else - { - OnConstructToThisConnection(replica3, replicaManager); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnSendDestructionFromQuery(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager) -{ - ConstructionMode constructionMode = QueryConstructionMode(); - RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); - (void) constructionMode; - - ValidateLists(replicaManager); - LastSerializationResult* lsr = queryToDestructReplicaList[queryToDestructIdx]; - queryToDestructReplicaList.RemoveAtIndex(queryToDestructIdx); - unsigned int j; - for (j=0; j < queryToSerializeReplicaList.Size(); j++) - { - if (queryToSerializeReplicaList[j]->replica->GetNetworkID()==lsr->replica->GetNetworkID() ) - { - queryToSerializeReplicaList.RemoveAtIndex(j); - break; - } - } - for (j=0; j < constructedReplicaList.Size(); j++) - { - if (constructedReplicaList[j]->replica->GetNetworkID()==lsr->replica->GetNetworkID() ) - { - constructedReplicaList.RemoveAtIndex(j); - break; - } - } - //assert(queryToConstructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); - queryToConstructReplicaList.Push(lsr,_FILE_AND_LINE_); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::OnDoNotQueryDestruction(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager) -{ - ValidateLists(replicaManager); - queryToDestructReplicaList.RemoveAtIndex(queryToDestructIdx); - ValidateLists(replicaManager); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::ValidateLists(ReplicaManager3 *replicaManager) const -{ - (void) replicaManager; - /* -#ifdef _DEBUG - // Each object should exist only once in either constructedReplicaList or queryToConstructReplicaList - // replicaPointer from LastSerializationResult should be same among all lists - unsigned int idx, idx2; - for (idx=0; idx < constructedReplicaList.Size(); idx++) - { - idx2=queryToConstructReplicaList.GetIndexOf(constructedReplicaList[idx]->replica); - if (idx2!=(unsigned int)-1) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - idx2=constructedReplicaList.GetIndexOf(queryToConstructReplicaList[idx]->replica); - if (idx2!=(unsigned int)-1) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - LastSerializationResult *lsr, *lsr2; - for (idx=0; idx < constructedReplicaList.Size(); idx++) - { - lsr=constructedReplicaList[idx]; - - idx2=queryToSerializeReplicaList.GetIndexOf(lsr->replica); - if (idx2!=(unsigned int)-1) - { - lsr2=queryToSerializeReplicaList[idx2]; - if (lsr2!=lsr) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - idx2=queryToDestructReplicaList.GetIndexOf(lsr->replica); - if (idx2!=(unsigned int)-1) - { - lsr2=queryToDestructReplicaList[idx2]; - if (lsr2!=lsr) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - } - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - lsr=queryToConstructReplicaList[idx]; - - idx2=queryToSerializeReplicaList.GetIndexOf(lsr->replica); - if (idx2!=(unsigned int)-1) - { - lsr2=queryToSerializeReplicaList[idx2]; - if (lsr2!=lsr) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - idx2=queryToDestructReplicaList.GetIndexOf(lsr->replica); - if (idx2!=(unsigned int)-1) - { - lsr2=queryToDestructReplicaList[idx2]; - if (lsr2!=lsr) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - } - - // Verify pointer integrity - for (idx=0; idx < constructedReplicaList.Size(); idx++) - { - if (constructedReplicaList[idx]->replica->replicaManager!=replicaManager) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } - - // Verify pointer integrity - for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) - { - if (queryToConstructReplicaList[idx]->replica->replicaManager!=replicaManager) - { - int a=5; - assert(a==0); - int *b=0; - *b=5; - } - } -#endif - */ -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::SendConstruction(DataStructures::List &newObjects, DataStructures::List &deletedObjects, PRO sendParameters, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager3) -{ - if (newObjects.Size()==0 && deletedObjects.Size()==0) - return; - - // All construction and destruction takes place in the same network message - // Otherwise, if objects rely on each other being created the same tick to be valid, this won't always be true - // DataStructures::List serializedObjects; - BitSize_t offsetStart, offsetStart2, offsetEnd; - unsigned int newListIndex, oldListIndex; - RakNet::BitStream bsOut; - NetworkID networkId; - if (isFirstConstruction) - { - bsOut.Write((MessageID)ID_REPLICA_MANAGER_DOWNLOAD_STARTED); - bsOut.Write(worldId); - SerializeOnDownloadStarted(&bsOut); - rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); - } - - // LastSerializationResult* lsr; - bsOut.Reset(); - bsOut.Write((MessageID)ID_REPLICA_MANAGER_CONSTRUCTION); - bsOut.Write(worldId); - uint16_t objectSize = (uint16_t) newObjects.Size(); - bsOut.Write(objectSize); - - // Construction - for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) - { - offsetStart=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart); // overwritten to point to the end of the stream - networkId=newObjects[newListIndex]->GetNetworkID(); - bsOut.Write(networkId); - - RM3ConstructionState cs = newObjects[newListIndex]->QueryConstruction(this, replicaManager3); - bool actuallyCreateObject = cs==RM3CS_SEND_CONSTRUCTION; - bsOut.Write(actuallyCreateObject); - bsOut.AlignWriteToByteBoundary(); - - if (actuallyCreateObject) - { - // Actually create the object - bsOut.Write(newObjects[newListIndex]->creatingSystemGUID); - offsetStart2=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart2); // overwritten to point to after the call to WriteAllocationID - bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy - newObjects[newListIndex]->WriteAllocationID(this, &bsOut); - bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart2); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - newObjects[newListIndex]->SerializeConstruction(&bsOut, this); - } - else - { - newObjects[newListIndex]->SerializeConstructionExisting(&bsOut, this); - } - - bsOut.AlignWriteToByteBoundary(); - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - } - - RakNet::BitStream bsOut2; - for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) - { - bsOut2.Reset(); - RM3ConstructionState cs = newObjects[newListIndex]->QueryConstruction(this, replicaManager3); - if (cs==RM3CS_SEND_CONSTRUCTION) - { - newObjects[newListIndex]->PostSerializeConstruction(&bsOut2, this); - } - else - { - RakAssert(cs==RM3CS_ALREADY_EXISTS_REMOTELY); - newObjects[newListIndex]->PostSerializeConstructionExisting(&bsOut2, this); - } - if (bsOut2.GetNumberOfBitsUsed()>0) - { - bsOut.Write(true); - bsOut.AlignWriteToByteBoundary(); - offsetStart=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart); // overwritten to point to the end of the stream - networkId=newObjects[newListIndex]->GetNetworkID(); - bsOut.Write(networkId); - bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy - bsOut.Write(&bsOut2); - bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - } - else - bsOut.Write(false); - } - bsOut.AlignWriteToByteBoundary(); - - // Destruction - objectSize = (uint16_t) deletedObjects.Size(); - bsOut.Write(objectSize); - for (oldListIndex=0; oldListIndex < deletedObjects.Size(); oldListIndex++) - { - networkId=deletedObjects[oldListIndex]->GetNetworkID(); - bsOut.Write(networkId); - offsetStart=bsOut.GetWriteOffset(); - bsOut.Write(offsetStart); - deletedObjects[oldListIndex]->deletingSystemGUID=rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); - bsOut.Write(deletedObjects[oldListIndex]->deletingSystemGUID); - deletedObjects[oldListIndex]->SerializeDestruction(&bsOut, this); - bsOut.AlignWriteToByteBoundary(); - offsetEnd=bsOut.GetWriteOffset(); - bsOut.SetWriteOffset(offsetStart); - bsOut.Write(offsetEnd); - bsOut.SetWriteOffset(offsetEnd); - } - rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); - - // TODO - shouldn't this be part of construction? - - // Initial Download serialize to a new system - // Immediately send serialize after construction if the replica object already has saved data - // If the object was serialized identically, and does not change later on, then the new connection never gets the data - SerializeParameters sp; - sp.whenLastSerialized=0; - RakNet::BitStream emptyBs; - for (int index=0; index < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; index++) - { - sp.lastSentBitstream[index]=&emptyBs; - sp.pro[index]=sendParameters; - sp.pro[index].reliability=RELIABLE_ORDERED; - } - - sp.bitsWrittenSoFar=0; - RakNet::Time t = RakNet::GetTimeMS(); - for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) - { - sp.destinationConnection=this; - sp.messageTimestamp=0; - RakNet::Replica3 *replica = newObjects[newListIndex]; - // 8/22/09 Forgot ResetWritePointer - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp.outputBitstream[z].ResetWritePointer(); - } - - RM3SerializationResult res = replica->Serialize(&sp); - if (res!=RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION && - res!=RM3SR_DO_NOT_SERIALIZE && - res!=RM3SR_SERIALIZED_UNIQUELY) - { - bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) - { - sp.bitsWrittenSoFar+=sp.outputBitstream[z].GetNumberOfBitsUsed(); - allIndices[z]=true; - } - SendSerialize(replica, allIndices, sp.outputBitstream, sp.messageTimestamp, sp.pro, rakPeer, worldId); - newObjects[newListIndex]->whenLastSerialized=t; - - } - // else wait for construction request accepted before serializing - } - - if (isFirstConstruction) - { - bsOut.Reset(); - bsOut.Write((MessageID)ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE); - bsOut.Write(worldId); - SerializeOnDownloadComplete(&bsOut); - rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); - } - - isFirstConstruction=false; - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Connection_RM3::SendValidation(RakNet::RakPeerInterface *rakPeer, unsigned char worldId) -{ - // Hijack to mean sendValidation - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_REPLICA_MANAGER_SCOPE_CHANGE); - bsOut.Write(worldId); - rakPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Replica3::Replica3() -{ - creatingSystemGUID=UNASSIGNED_RAKNET_GUID; - deletingSystemGUID=UNASSIGNED_RAKNET_GUID; - replicaManager=0; - forceSendUntilNextUpdate=false; - whenLastSerialized=0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Replica3::~Replica3() -{ - if (replicaManager) - { - replicaManager->Dereference(this); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void Replica3::BroadcastDestruction(void) -{ - replicaManager->BroadcastDestruction(this,UNASSIGNED_SYSTEM_ADDRESS); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RakNetGUID Replica3::GetCreatingSystemGUID(void) const -{ - return creatingSystemGUID; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ConstructionState Replica3::QueryConstruction_ClientConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) -{ - (void) destinationConnection; - if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return RM3CS_SEND_CONSTRUCTION; - // Send back to the owner client too, because they couldn't assign the network ID - if (isThisTheServer) - return RM3CS_SEND_CONSTRUCTION; - return RM3CS_NEVER_CONSTRUCT; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool Replica3::QueryRemoteConstruction_ClientConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer) -{ - (void) sourceConnection; - (void) isThisTheServer; - - // OK to create - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ConstructionState Replica3::QueryConstruction_ServerConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) -{ - (void) destinationConnection; - - if (isThisTheServer) - return RM3CS_SEND_CONSTRUCTION; - return RM3CS_NEVER_CONSTRUCT; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool Replica3::QueryRemoteConstruction_ServerConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer) -{ - (void) sourceConnection; - if (isThisTheServer) - return false; - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ConstructionState Replica3::QueryConstruction_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode) -{ - (void) destinationConnection; - - if (p2pMode==R3P2PM_SINGLE_OWNER) - { - // We send to all, others do nothing - if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return RM3CS_SEND_CONSTRUCTION; - - // RM3CS_NEVER_CONSTRUCT will not send the object, and will not Serialize() it - return RM3CS_NEVER_CONSTRUCT; - } - else if (p2pMode==R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE) - { - return RM3CS_SEND_CONSTRUCTION; - } - else - { - RakAssert(p2pMode==R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE); - - // RM3CS_ALREADY_EXISTS_REMOTELY will not send the object, but WILL call QuerySerialization() and Serialize() on it. - return RM3CS_ALREADY_EXISTS_REMOTELY; - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -bool Replica3::QueryRemoteConstruction_PeerToPeer(RakNet::Connection_RM3 *sourceConnection) -{ - (void) sourceConnection; - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3QuerySerializationResult Replica3::QuerySerialization_ClientSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) -{ - // Owner client sends to all - if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return RM3QSR_CALL_SERIALIZE; - // Server sends to all but owner client - if (isThisTheServer && destinationConnection->GetRakNetGUID()!=creatingSystemGUID) - return RM3QSR_CALL_SERIALIZE; - // Remote clients do not send - return RM3QSR_NEVER_CALL_SERIALIZE; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3QuerySerializationResult Replica3::QuerySerialization_ServerSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) -{ - (void) destinationConnection; - // Server sends to all - if (isThisTheServer) - return RM3QSR_CALL_SERIALIZE; - - // Clients do not send - return RM3QSR_NEVER_CALL_SERIALIZE; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3QuerySerializationResult Replica3::QuerySerialization_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode) -{ - (void) destinationConnection; - - if (p2pMode==R3P2PM_SINGLE_OWNER) - { - // Owner peer sends to all - if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) - return RM3QSR_CALL_SERIALIZE; - - // Remote peers do not send - return RM3QSR_NEVER_CALL_SERIALIZE; - } - else if (p2pMode==R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE) - { - return RM3QSR_CALL_SERIALIZE; - } - else - { - RakAssert(p2pMode==R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE); - return RM3QSR_DO_NOT_CALL_SERIALIZE; - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_Client(RakNet::Connection_RM3 *droppedConnection) const -{ - (void) droppedConnection; - return RM3AOPC_DELETE_REPLICA; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_Server(RakNet::Connection_RM3 *droppedConnection) const -{ - (void) droppedConnection; - return RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_PeerToPeer(RakNet::Connection_RM3 *droppedConnection) const -{ - (void) droppedConnection; - return RM3AOPC_DELETE_REPLICA; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ReplicaManager3==1 + +#include "ReplicaManager3.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "NetworkIDManager.h" + +using namespace RakNet; + +// DEFINE_MULTILIST_PTR_TO_MEMBER_COMPARISONS(LastSerializationResult,Replica3*,replica); + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool PRO::operator==( const PRO& right ) const +{ + return priority == right.priority && reliability == right.reliability && orderingChannel == right.orderingChannel && sendReceipt == right.sendReceipt; +} + +bool PRO::operator!=( const PRO& right ) const +{ + return priority != right.priority || reliability != right.reliability || orderingChannel != right.orderingChannel || sendReceipt != right.sendReceipt; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +int Connection_RM3::Replica3LSRComp( Replica3 * const &replica3, LastSerializationResult * const &data ) +{ + if (replica3->GetNetworkID() < data->replica->GetNetworkID()) + return -1; + if (replica3->GetNetworkID() > data->replica->GetNetworkID()) + return 1; + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +LastSerializationResult::LastSerializationResult() +{ + replica=0; + lastSerializationResultBS=0; +} +LastSerializationResult::~LastSerializationResult() +{ + if (lastSerializationResultBS) + RakNet::OP_DELETE(lastSerializationResultBS,_FILE_AND_LINE_); +} +void LastSerializationResult::AllocBS(void) +{ + if (lastSerializationResultBS==0) + { + lastSerializationResultBS=RakNet::OP_NEW(_FILE_AND_LINE_); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ReplicaManager3::ReplicaManager3() +{ + defaultSendParameters.orderingChannel=0; + defaultSendParameters.priority=HIGH_PRIORITY; + defaultSendParameters.reliability=RELIABLE_ORDERED; + defaultSendParameters.sendReceipt=0; + autoSerializeInterval=30; + lastAutoSerializeOccurance=0; + worldId=0; + autoCreateConnections=true; + autoDestroyConnections=true; + networkIDManager=0; + currentlyDeallocatingReplica=0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ReplicaManager3::~ReplicaManager3() +{ + if (autoDestroyConnections) + { + // Clear() calls DeallocConnection(), which is pure virtual and cannot be called from the destructor + RakAssert(connectionList.Size()==0); + } + Clear(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetAutoManageConnections(bool autoCreate, bool autoDestroy) +{ + autoCreateConnections=autoCreate; + autoDestroyConnections=autoDestroy; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool ReplicaManager3::PushConnection(RakNet::Connection_RM3 *newConnection) +{ + if (newConnection==0) + return false; + if (GetConnectionByGUID(newConnection->GetRakNetGUID())) + return false; + unsigned int index = connectionList.GetIndexOf(newConnection); + if (index==(unsigned int)-1) + { + connectionList.Push(newConnection,_FILE_AND_LINE_); + + // Send message to validate the connection + newConnection->SendValidation(rakPeerInterface, worldId); + + Connection_RM3::ConstructionMode constructionMode = newConnection->QueryConstructionMode(); + if (constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + unsigned int pushIdx; + for (pushIdx=0; pushIdx < userReplicaList.Size(); pushIdx++) + newConnection->OnLocalReference(userReplicaList[pushIdx], this); + } + } + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::DeallocReplicaNoBroadcastDestruction(RakNet::Connection_RM3 *connection, RakNet::Replica3 *replica3) +{ + currentlyDeallocatingReplica=replica3; + replica3->DeallocReplica(connection); + currentlyDeallocatingReplica=0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RakNet::Connection_RM3 * ReplicaManager3::PopConnection(unsigned int index) +{ + DataStructures::List replicaList; + DataStructures::List destructionList; + DataStructures::List broadcastList; + RakNet::Connection_RM3 *connection; + unsigned int index2; + RM3ActionOnPopConnection action; + + connection=connectionList[index]; + + // Clear out downloadGroup + connection->ClearDownloadGroup(rakPeerInterface); + + RakNetGUID guid = connection->GetRakNetGUID(); + // This might be wrong, I am relying on the variable creatingSystemGuid which is transmitted + // automatically from the first system to reference the object. However, if an object changes + // owners then it is not going to be returned here, and therefore QueryActionOnPopConnection() + // will not be called for the new owner. + GetReplicasCreatedByGuid(guid, replicaList); + + for (index2=0; index2 < replicaList.Size(); index2++) + { + action = replicaList[index2]->QueryActionOnPopConnection(connection); + replicaList[index2]->OnPoppedConnection(connection); + if (action==RM3AOPC_DELETE_REPLICA) + { + destructionList.Push( replicaList[index2], _FILE_AND_LINE_ ); + } + else if (action==RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION) + { + destructionList.Push( replicaList[index2], _FILE_AND_LINE_ ); + + broadcastList.Push( replicaList[index2], _FILE_AND_LINE_ ); + } + } + + BroadcastDestructionList(broadcastList, connection->GetSystemAddress()); + for (index2=0; index2 < destructionList.Size(); index2++) + { + destructionList[index2]->PreDestruction(connection); + destructionList[index2]->DeallocReplica(connection); + } + + connectionList.RemoveAtIndex(index); + return connection; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RakNet::Connection_RM3 * ReplicaManager3::PopConnection(RakNetGUID guid) +{ + unsigned int index; + + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->GetRakNetGUID()==guid) + { + return PopConnection(index); + } + } + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::Reference(RakNet::Replica3 *replica3) +{ + unsigned int index = ReferenceInternal(replica3); + + if (index!=(unsigned int)-1) + { + unsigned int pushIdx; + for (pushIdx=0; pushIdx < connectionList.Size(); pushIdx++) + { + Connection_RM3::ConstructionMode constructionMode = connectionList[pushIdx]->QueryConstructionMode(); + if (constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==Connection_RM3::QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + connectionList[pushIdx]->OnLocalReference(replica3, this); + } + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +unsigned int ReplicaManager3::ReferenceInternal(RakNet::Replica3 *replica3) +{ + unsigned int index; + index = userReplicaList.GetIndexOf(replica3); + if (index==(unsigned int)-1) + { + RakAssert(networkIDManager); + replica3->SetNetworkIDManager(networkIDManager); + if (replica3->creatingSystemGUID==UNASSIGNED_RAKNET_GUID) + replica3->creatingSystemGUID=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + replica3->replicaManager=this; + userReplicaList.Push(replica3,_FILE_AND_LINE_); + index=userReplicaList.Size()-1; + } + return index; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::Dereference(RakNet::Replica3 *replica3) +{ + unsigned int index, index2; + for (index=0; index < userReplicaList.Size(); index++) + { + if (userReplicaList[index]==replica3) + { + userReplicaList.RemoveAtIndex(index); + break; + } + } + + // Remove from all connections + for (index2=0; index2 < connectionList.Size(); index2++) + { + connectionList[index2]->OnDereference(replica3, this); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::DereferenceList(DataStructures::List &replicaListIn) +{ + unsigned int index; + for (index=0; index < replicaListIn.Size(); index++) + Dereference(replicaListIn[index]); +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::GetReplicasCreatedByMe(DataStructures::List &replicaListOut) +{ + RakNetGUID myGuid = rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + GetReplicasCreatedByGuid(rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS), replicaListOut); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::GetReferencedReplicaList(DataStructures::List &replicaListOut) +{ + replicaListOut=userReplicaList; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::GetReplicasCreatedByGuid(RakNetGUID guid, DataStructures::List &replicaListOut) +{ + replicaListOut.Clear(false,_FILE_AND_LINE_); + unsigned int index; + for (index=0; index < userReplicaList.Size(); index++) + { + if (userReplicaList[index]->creatingSystemGUID==guid) + replicaListOut.Push(userReplicaList[index],_FILE_AND_LINE_); + } +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +unsigned ReplicaManager3::GetReplicaCount(void) const +{ + return userReplicaList.Size(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Replica3 *ReplicaManager3::GetReplicaAtIndex(unsigned index) +{ + return userReplicaList[index]; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +unsigned int ReplicaManager3::GetConnectionCount(void) const +{ + return connectionList.Size(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3* ReplicaManager3::GetConnectionAtIndex(unsigned index) const +{ + return connectionList[index]; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3* ReplicaManager3::GetConnectionBySystemAddress(const SystemAddress &sa) const +{ + unsigned int index; + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->GetSystemAddress()==sa) + { + return connectionList[index]; + } + } + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3* ReplicaManager3::GetConnectionByGUID(RakNetGUID guid) const +{ + unsigned int index; + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->GetRakNetGUID()==guid) + { + return connectionList[index]; + } + } + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetDefaultOrderingChannel(char def) +{ + defaultSendParameters.orderingChannel=def; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetDefaultPacketPriority(PacketPriority def) +{ + defaultSendParameters.priority=def; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetDefaultPacketReliability(PacketReliability def) +{ + defaultSendParameters.reliability=def; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetAutoSerializeInterval(RakNet::Time intervalMS) +{ + autoSerializeInterval=intervalMS; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::GetConnectionsThatHaveReplicaConstructed(Replica3 *replica, DataStructures::List &connectionsThatHaveConstructedThisReplica) +{ + connectionsThatHaveConstructedThisReplica.Clear(false,_FILE_AND_LINE_); + unsigned int index; + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->HasReplicaConstructed(replica)) + connectionsThatHaveConstructedThisReplica.Push(connectionList[index],_FILE_AND_LINE_); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::Clear(void) +{ + if (autoDestroyConnections) + { + for (unsigned int i=0; i < connectionList.Size(); i++) + DeallocConnection(connectionList[i]); + } + else + { + // Clear out downloadGroup even if not auto destroying the connection, since the packets need to go back to RakPeer + for (unsigned int i=0; i < connectionList.Size(); i++) + connectionList[i]->ClearDownloadGroup(rakPeerInterface); + } + + + + connectionList.Clear(true,_FILE_AND_LINE_); + userReplicaList.Clear(true,_FILE_AND_LINE_); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PRO ReplicaManager3::GetDefaultSendParameters(void) const +{ + return defaultSendParameters; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetWorldID(unsigned char id) +{ + worldId=id; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +unsigned char ReplicaManager3::GetWorldID(void) const +{ + return worldId; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +NetworkIDManager *ReplicaManager3::GetNetworkIDManager(void) const +{ + return networkIDManager; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::SetNetworkIDManager(NetworkIDManager *_networkIDManager) +{ + networkIDManager=_networkIDManager; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnReceive(Packet *packet) +{ + if (packet->length<2) + return RR_CONTINUE_PROCESSING; + + unsigned char incomingWorldId; + + RakNet::Time timestamp=0; + unsigned char packetIdentifier, packetDataOffset; + if ( ( unsigned char ) packet->data[ 0 ] == ID_TIMESTAMP ) + { + if ( packet->length > sizeof( unsigned char ) + sizeof( RakNet::Time ) ) + { + packetIdentifier = ( unsigned char ) packet->data[ sizeof( unsigned char ) + sizeof( RakNet::Time ) ]; + // Required for proper endian swapping + RakNet::BitStream tsBs(packet->data+sizeof(MessageID),packet->length-1,false); + tsBs.Read(timestamp); + incomingWorldId=packet->data[sizeof( unsigned char )*2 + sizeof( RakNet::Time )]; + packetDataOffset=sizeof( unsigned char )*3 + sizeof( RakNet::Time ); + } + else + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + else + { + packetIdentifier = ( unsigned char ) packet->data[ 0 ]; + incomingWorldId=packet->data[sizeof( unsigned char )]; + packetDataOffset=sizeof( unsigned char )*2; + } + + switch (packetIdentifier) + { + case ID_REPLICA_MANAGER_CONSTRUCTION: + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + return OnConstruction(packet, packet->data, packet->length, packet->guid, packetDataOffset); + case ID_REPLICA_MANAGER_SERIALIZE: + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + return OnSerialize(packet, packet->data, packet->length, packet->guid, timestamp, packetDataOffset); + case ID_REPLICA_MANAGER_DOWNLOAD_STARTED: + if (packet->wasGeneratedLocally==false) + { + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + return OnDownloadStarted(packet, packet->data, packet->length, packet->guid, packetDataOffset); + } + else + break; + case ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE: + if (packet->wasGeneratedLocally==false) + { + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + return OnDownloadComplete(packet, packet->data, packet->length, packet->guid, packetDataOffset); + } + else + break; + case ID_REPLICA_MANAGER_SCOPE_CHANGE: + { + if (incomingWorldId!=worldId) + return RR_CONTINUE_PROCESSING; + + Connection_RM3 *connection = GetConnectionByGUID(packet->guid); + if (connection && connection->isValidated==false) + { + // This connection is now confirmed bidirectional + connection->isValidated=true; + // Reply back on validation + connection->SendValidation(rakPeerInterface,worldId); + } + } + } + + return RR_CONTINUE_PROCESSING; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::AutoConstructByQuery(ReplicaManager3 *replicaManager3) +{ + ValidateLists(replicaManager3); + + ConstructionMode constructionMode = QueryConstructionMode(); + + unsigned int index; + RM3ConstructionState constructionState; + LastSerializationResult *lsr; + index=0; + + constructedReplicasCulled.Clear(false,_FILE_AND_LINE_); + destroyedReplicasCulled.Clear(false,_FILE_AND_LINE_); + + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + while (index < queryToConstructReplicaList.Size()) + { + lsr=queryToConstructReplicaList[index]; + constructionState=lsr->replica->QueryConstruction(this, replicaManager3); + if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY || constructionState==RM3CS_ALREADY_EXISTS_REMOTELY_DO_NOT_CONSTRUCT) + { + OnReplicaAlreadyExists(index, replicaManager3); + if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY) + constructedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); + + /* + if (constructionState==RM3CS_ALREADY_EXISTS_REMOTELY) + { + // Serialize construction data to this connection + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_REPLICA_MANAGER_3_SERIALIZE_CONSTRUCTION_EXISTING); + bsOut.Write(replicaManager3->GetWorldID()); + NetworkID networkId; + networkId=lsr->replica->GetNetworkID(); + bsOut.Write(networkId); + BitSize_t bitsWritten = bsOut.GetNumberOfBitsUsed(); + lsr->replica->SerializeConstructionExisting(&bsOut, this); + if (bsOut.GetNumberOfBitsUsed()!=bitsWritten) + replicaManager3->SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,GetSystemAddress(), false); + } + + // Serialize first serialization to this connection. + // This is done here, as it isn't done in PushConstruction + SerializeParameters sp; + RakNet::BitStream emptyBs; + for (index=0; index < (unsigned int) RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; index++) + { + sp.lastSentBitstream[index]=&emptyBs; + sp.pro[index]=replicaManager3->GetDefaultSendParameters(); + } + sp.bitsWrittenSoFar=0; + sp.destinationConnection=this; + sp.messageTimestamp=0; + sp.whenLastSerialized=0; + + RakNet::Replica3 *replica = lsr->replica; + + RM3SerializationResult res = replica->Serialize(&sp); + if (res!=RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION && + res!=RM3SR_DO_NOT_SERIALIZE && + res!=RM3SR_SERIALIZED_UNIQUELY) + { + bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp.bitsWrittenSoFar+=sp.outputBitstream[z].GetNumberOfBitsUsed(); + allIndices[z]=true; + } + if (SendSerialize(replica, allIndices, sp.outputBitstream, sp.messageTimestamp, sp.pro, replicaManager3->GetRakPeerInterface(), replicaManager3->GetWorldID())==SSICR_SENT_DATA) + lsr->replica->whenLastSerialized=RakNet::GetTimeMS(); + } + */ + } + else if (constructionState==RM3CS_SEND_CONSTRUCTION) + { + OnConstructToThisConnection(index, replicaManager3); + constructedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); + } + else if (constructionState==RM3CS_NEVER_CONSTRUCT) + { + OnNeverConstruct(index, replicaManager3); + } + else// if (constructionState==RM3CS_NO_ACTION) + { + // Do nothing + index++; + } + } + + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + RM3DestructionState destructionState; + index=0; + while (index < queryToDestructReplicaList.Size()) + { + lsr=queryToDestructReplicaList[index]; + destructionState=lsr->replica->QueryDestruction(this, replicaManager3); + if (destructionState==RM3DS_SEND_DESTRUCTION) + { + OnSendDestructionFromQuery(index, replicaManager3); + destroyedReplicasCulled.Push(lsr->replica,_FILE_AND_LINE_); + } + else if (destructionState==RM3DS_DO_NOT_QUERY_DESTRUCTION) + { + OnDoNotQueryDestruction(index, replicaManager3); + } + else// if (destructionState==RM3CS_NO_ACTION) + { + // Do nothing + index++; + } + } + } + } + else if (constructionMode==QUERY_CONNECTION_FOR_REPLICA_LIST) + { + QueryReplicaList(constructedReplicasCulled,destroyedReplicasCulled); + + unsigned int idx1, idx2; + + // Create new + for (idx2=0; idx2 < constructedReplicasCulled.Size(); idx2++) + OnConstructToThisConnection(constructedReplicasCulled[idx2], replicaManager3); + + bool exists; + for (idx2=0; idx2 < destroyedReplicasCulled.Size(); idx2++) + { + exists=false; + bool objectExists; + idx1=constructedReplicaList.GetIndexFromKey(destroyedReplicasCulled[idx2], &objectExists); + RakAssert(objectExists); + if (objectExists) + { + OnSendDestructionFromQuery(idx1,replicaManager3); + } + + // If this assert hits, the user tried to destroy a replica that doesn't exist on the remote system + RakAssert(exists); + } + } + + SendConstruction(constructedReplicasCulled,destroyedReplicasCulled,replicaManager3->defaultSendParameters,replicaManager3->rakPeerInterface,replicaManager3->worldId,replicaManager3); +} +void ReplicaManager3::Update(void) +{ + unsigned int index,index2; + + for (index=0; index < connectionList.Size(); index++) + { + if (connectionList[index]->isValidated==false) + continue; + connectionList[index]->AutoConstructByQuery(this); + } + + RakNet::Time time = RakNet::GetTimeMS(); + + if (time - lastAutoSerializeOccurance >= autoSerializeInterval) + { + for (index=0; index < userReplicaList.Size(); index++) + { + userReplicaList[index]->forceSendUntilNextUpdate=false; + userReplicaList[index]->OnUserReplicaPreSerializeTick(); + } + + + unsigned int index; + SerializeParameters sp; + sp.curTime=time; + Connection_RM3 *connection; + SendSerializeIfChangedResult ssicr; + sp.messageTimestamp=0; + for (int i=0; i < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; i++) + sp.pro[i]=defaultSendParameters; + index2=0; + for (index=0; index < connectionList.Size(); index++) + { + connection = connectionList[index]; + sp.bitsWrittenSoFar=0; + index2=0; + while (index2 < connection->queryToSerializeReplicaList.Size()) + { + sp.destinationConnection=connection; + sp.whenLastSerialized=connection->queryToSerializeReplicaList[index2]->replica->whenLastSerialized; + ssicr=connection->SendSerializeIfChanged(index2, &sp, GetRakPeerInterface(), GetWorldID(), this); + if (ssicr==SSICR_SENT_DATA) + { + connection->queryToSerializeReplicaList[index2]->replica->whenLastSerialized=time; + index2++; + } + else if (ssicr==SSICR_NEVER_SERIALIZE) + { + // Removed from the middle of the list + } + else + index2++; + } + } + + lastAutoSerializeOccurance=time; + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + if (autoDestroyConnections) + { + Connection_RM3 *connection = PopConnection(rakNetGUID); + if (connection) + DeallocConnection(connection); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming) +{ + (void) isIncoming; + if (autoCreateConnections) + { + Connection_RM3 *connection = AllocConnection(systemAddress, rakNetGUID); + if (connection) + PushConnection(connection); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::OnRakPeerShutdown(void) +{ + if (autoDestroyConnections) + { + while (connectionList.Size()) + { + Connection_RM3 *connection = PopConnection(connectionList.Size()-1); + if (connection) + DeallocConnection(connection); + } + } + + Clear(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void ReplicaManager3::OnDetach(void) +{ + OnRakPeerShutdown(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnConstruction(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) +{ + Connection_RM3 *connection = GetConnectionByGUID(senderGuid); + if (connection==0) + { + // Almost certainly a bug + RakAssert("Got OnConstruction but no connection yet" && 0); + return RR_CONTINUE_PROCESSING; + } + if (connection->groupConstructionAndSerialize) + { + connection->downloadGroup.Push(packet, __FILE__, __LINE__); + return RR_STOP_PROCESSING; + } + + RakNet::BitStream bsIn(packetData,packetDataLength,false); + bsIn.IgnoreBytes(packetDataOffset); + uint16_t constructionObjectListSize, destructionObjectListSize, index, index2; + BitSize_t streamEnd, writeAllocationIDEnd; + Replica3 *replica; + NetworkID networkId; + RakNetGUID creatingSystemGuid; + bool actuallyCreateObject=false; + + DataStructures::List actuallyCreateObjectList; + DataStructures::List constructionTickStack; + + RakAssert(networkIDManager); + + bsIn.Read(constructionObjectListSize); + for (index=0; index < constructionObjectListSize; index++) + { + bsIn.Read(streamEnd); + bsIn.Read(networkId); + Replica3* existingReplica = networkIDManager->GET_OBJECT_FROM_ID(networkId); + bsIn.Read(actuallyCreateObject); + actuallyCreateObjectList.Push(actuallyCreateObject, _FILE_AND_LINE_); + bsIn.AlignReadToByteBoundary(); + + if (actuallyCreateObject) + { + bsIn.Read(creatingSystemGuid); + bsIn.Read(writeAllocationIDEnd); + + //printf("OnConstruction: %i\n",networkId.guid.g); // Removeme + if (existingReplica) + { + existingReplica->replicaManager=this; + + // Network ID already in use + connection->OnDownloadExisting(existingReplica, this); + + constructionTickStack.Push(0, _FILE_AND_LINE_); + bsIn.SetReadOffset(streamEnd); + continue; + } + + bsIn.AlignReadToByteBoundary(); + replica = connection->AllocReplica(&bsIn, this); + if (replica==0) + { + constructionTickStack.Push(0, _FILE_AND_LINE_); + bsIn.SetReadOffset(streamEnd); + continue; + } + + // Go past the bitStream written to with WriteAllocationID(). Necessary in case the user didn't read out the bitStream the same way it was written + // bitOffset2 is already aligned + bsIn.SetReadOffset(writeAllocationIDEnd); + + replica->SetNetworkIDManager(networkIDManager); + replica->SetNetworkID(networkId); + + replica->replicaManager=this; + replica->creatingSystemGUID=creatingSystemGuid; + + if (!replica->QueryRemoteConstruction(connection) || + !replica->DeserializeConstruction(&bsIn, connection)) + { + DeallocReplicaNoBroadcastDestruction(connection, replica); + bsIn.SetReadOffset(streamEnd); + constructionTickStack.Push(0, _FILE_AND_LINE_); + continue; + } + + constructionTickStack.Push(replica, _FILE_AND_LINE_); + + // Register the replica + ReferenceInternal(replica); + } + else + { + if (existingReplica) + { + existingReplica->DeserializeConstructionExisting(&bsIn, connection); + constructionTickStack.Push(existingReplica, _FILE_AND_LINE_); + } + else + { + constructionTickStack.Push(0, _FILE_AND_LINE_); + } + } + + + bsIn.SetReadOffset(streamEnd); + bsIn.AlignReadToByteBoundary(); + } + + RakAssert(constructionTickStack.Size()==constructionObjectListSize); + RakAssert(actuallyCreateObjectList.Size()==constructionObjectListSize); + + RakNet::BitStream empty; + for (index=0; index < constructionObjectListSize; index++) + { + bool pdcWritten=false; + bsIn.Read(pdcWritten); + if (pdcWritten) + { + bsIn.AlignReadToByteBoundary(); + bsIn.Read(streamEnd); + bsIn.Read(networkId); + if (constructionTickStack[index]!=0) + { + bsIn.AlignReadToByteBoundary(); + if (actuallyCreateObjectList[index]) + constructionTickStack[index]->PostDeserializeConstruction(&bsIn, connection); + else + constructionTickStack[index]->PostDeserializeConstructionExisting(&bsIn, connection); + } + bsIn.SetReadOffset(streamEnd); + } + else + { + if (constructionTickStack[index]!=0) + constructionTickStack[index]->PostDeserializeConstruction(&empty, connection); + } + } + + for (index=0; index < constructionObjectListSize; index++) + { + if (constructionTickStack[index]!=0) + { + if (actuallyCreateObjectList[index]) + { + // Tell the connection(s) that this object exists since they just sent it to us + connection->OnDownloadFromThisSystem(constructionTickStack[index], this); + + for (index2=0; index2 < connectionList.Size(); index2++) + { + if (connectionList[index2]!=connection) + connectionList[index2]->OnDownloadFromOtherSystem(constructionTickStack[index], this); + } + } + } + } + + // Destructions + bool b = bsIn.Read(destructionObjectListSize); + (void) b; + RakAssert(b); + for (index=0; index < destructionObjectListSize; index++) + { + bsIn.Read(networkId); + bsIn.Read(streamEnd); + replica = networkIDManager->GET_OBJECT_FROM_ID(networkId); + if (replica==0) + { + // Unknown object + bsIn.SetReadOffset(streamEnd); + continue; + } + bsIn.Read(replica->deletingSystemGUID); + if (replica->DeserializeDestruction(&bsIn,connection)) + { + // Make sure it wasn't deleted in DeserializeDestruction + if (networkIDManager->GET_OBJECT_FROM_ID(networkId)) + { + replica->PreDestruction(connection); + + // Forward deletion by remote system + if (replica->QueryRelayDestruction(connection)) + BroadcastDestruction(replica,connection->GetSystemAddress()); + Dereference(replica); + DeallocReplicaNoBroadcastDestruction(connection, replica); + } + } + else + { + replica->PreDestruction(connection); + connection->OnDereference(replica, this); + } + + bsIn.AlignReadToByteBoundary(); + } + return RR_CONTINUE_PROCESSING; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnSerialize(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, RakNet::Time timestamp, unsigned char packetDataOffset) +{ + Connection_RM3 *connection = GetConnectionByGUID(senderGuid); + if (connection==0) + return RR_CONTINUE_PROCESSING; + if (connection->groupConstructionAndSerialize) + { + connection->downloadGroup.Push(packet, __FILE__, __LINE__); + return RR_STOP_PROCESSING; + } + RakAssert(networkIDManager); + RakNet::BitStream bsIn(packetData,packetDataLength,false); + bsIn.IgnoreBytes(packetDataOffset); + + struct DeserializeParameters ds; + ds.timeStamp=timestamp; + ds.sourceConnection=connection; + + Replica3 *replica; + NetworkID networkId; + BitSize_t bitsUsed; + bsIn.Read(networkId); + //printf("OnSerialize: %i\n",networkId.guid.g); // Removeme + replica = networkIDManager->GET_OBJECT_FROM_ID(networkId); + if (replica) + { + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + bsIn.Read(ds.bitstreamWrittenTo[z]); + if (ds.bitstreamWrittenTo[z]) + { + bsIn.ReadCompressed(bitsUsed); + bsIn.AlignReadToByteBoundary(); + bsIn.Read(ds.serializationBitstream[z], bitsUsed); + } + } + replica->Deserialize(&ds); + } + return RR_CONTINUE_PROCESSING; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnDownloadStarted(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) +{ + Connection_RM3 *connection = GetConnectionByGUID(senderGuid); + if (connection==0) + return RR_CONTINUE_PROCESSING; + if (connection->QueryGroupDownloadMessages() && + // ID_DOWNLOAD_STARTED will be processed twice, being processed the second time once ID_DOWNLOAD_COMPLETE arrives. + // However, the second time groupConstructionAndSerialize will be set to true so it won't be processed a third time + connection->groupConstructionAndSerialize==false + ) + { + // These messages will be held by the plugin and returned when the download is complete + connection->groupConstructionAndSerialize=true; + RakAssert(connection->downloadGroup.Size()==0); + connection->downloadGroup.Push(packet, __FILE__, __LINE__); + return RR_STOP_PROCESSING; + } + + connection->groupConstructionAndSerialize=false; + RakNet::BitStream bsIn(packetData,packetDataLength,false); + bsIn.IgnoreBytes(packetDataOffset); + connection->DeserializeOnDownloadStarted(&bsIn); + return RR_CONTINUE_PROCESSING; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +PluginReceiveResult ReplicaManager3::OnDownloadComplete(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset) +{ + Connection_RM3 *connection = GetConnectionByGUID(senderGuid); + if (connection==0) + return RR_CONTINUE_PROCESSING; + + if (connection->groupConstructionAndSerialize==true && connection->downloadGroup.Size()>0) + { + // Push back buffered packets in front of this one + unsigned int i; + for (i=0; i < connection->downloadGroup.Size(); i++) + rakPeerInterface->PushBackPacket(connection->downloadGroup[i],false); + + // Push this one to be last too. It will be processed again, but the second time + // groupConstructionAndSerialize will be false and downloadGroup will be empty, so it will go past this block + connection->downloadGroup.Clear(__FILE__,__LINE__); + rakPeerInterface->PushBackPacket(packet,false); + + return RR_STOP_PROCESSING; + } + + RakNet::BitStream bsIn(packetData,packetDataLength,false); + bsIn.IgnoreBytes(packetDataOffset); + connection->DeserializeOnDownloadComplete(&bsIn); + return RR_CONTINUE_PROCESSING; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Replica3* ReplicaManager3::GetReplicaByNetworkID(NetworkID networkId) +{ + unsigned int i; + for (i=0; i < userReplicaList.Size(); i++) + { + if (userReplicaList[i]->GetNetworkID()==networkId) + return userReplicaList[i]; + } + return 0; +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +void ReplicaManager3::BroadcastDestructionList(DataStructures::List &replicaListSource, const SystemAddress &exclusionAddress) +{ + RakNet::BitStream bsOut; + unsigned int i,j; + + DataStructures::List replicaList; + + for (i=0; i < replicaListSource.Size(); i++) + { + if (replicaListSource[i]==currentlyDeallocatingReplica) + continue; + replicaList.Push(replicaListSource[i], __FILE__, __LINE__); + } + + if (replicaList.Size()==0) + return; + + for (i=0; i < replicaList.Size(); i++) + { + if (replicaList[i]->deletingSystemGUID==UNASSIGNED_RAKNET_GUID) + replicaList[i]->deletingSystemGUID=GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + } + + for (j=0; j < connectionList.Size(); j++) + { + if (connectionList[j]->GetSystemAddress()==exclusionAddress) + continue; + + bsOut.Reset(); + bsOut.Write((MessageID)ID_REPLICA_MANAGER_CONSTRUCTION); + bsOut.Write(worldId); + uint16_t cnt=0; + bsOut.Write(cnt); // No construction + cnt=(uint16_t) replicaList.Size(); + BitSize_t cntOffset=bsOut.GetWriteOffset();; + bsOut.Write(cnt); // Overwritten at send call + cnt=0; + + for (i=0; i < replicaList.Size(); i++) + { + if (connectionList[j]->HasReplicaConstructed(replicaList[i])==false) + continue; + cnt++; + + NetworkID networkId; + networkId=replicaList[i]->GetNetworkID(); + bsOut.Write(networkId); + BitSize_t offsetStart, offsetEnd; + offsetStart=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart); + bsOut.Write(replicaList[i]->deletingSystemGUID); + replicaList[i]->SerializeDestruction(&bsOut, connectionList[j]); + bsOut.AlignWriteToByteBoundary(); + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + } + + if (cnt>0) + { + BitSize_t curOffset=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(cntOffset); + bsOut.Write(cnt); + bsOut.SetWriteOffset(curOffset); + rakPeerInterface->Send(&bsOut,defaultSendParameters.priority,defaultSendParameters.reliability,defaultSendParameters.orderingChannel,connectionList[j]->GetSystemAddress(),false, defaultSendParameters.sendReceipt); + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +void ReplicaManager3::BroadcastDestruction(Replica3 *replica, const SystemAddress &exclusionAddress) +{ + DataStructures::List replicaList; + replicaList.Push(replica, _FILE_AND_LINE_ ); + BroadcastDestructionList(replicaList,exclusionAddress); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3::Connection_RM3(const SystemAddress &_systemAddress, RakNetGUID _guid) +: systemAddress(_systemAddress), guid(_guid) +{ + isValidated=false; + isFirstConstruction=true; + groupConstructionAndSerialize=false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Connection_RM3::~Connection_RM3() +{ + unsigned int i; + for (i=0; i < constructedReplicaList.Size(); i++) + RakNet::OP_DELETE(constructedReplicaList[i], _FILE_AND_LINE_); + for (i=0; i < queryToConstructReplicaList.Size(); i++) + RakNet::OP_DELETE(queryToConstructReplicaList[i], _FILE_AND_LINE_); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::GetConstructedReplicas(DataStructures::List &objectsTheyDoHave) +{ + objectsTheyDoHave.Clear(true,_FILE_AND_LINE_); + for (unsigned int idx=0; idx < constructedReplicaList.Size(); idx++) + { + objectsTheyDoHave.Push(constructedReplicaList[idx]->replica, _FILE_AND_LINE_ ); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool Connection_RM3::HasReplicaConstructed(RakNet::Replica3 *replica) +{ + bool objectExists; + constructedReplicaList.GetIndexFromKey(replica, &objectExists); + return objectExists; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void Connection_RM3::SendSerializeHeader(RakNet::Replica3 *replica, RakNet::Time timestamp, RakNet::BitStream *bs, unsigned char worldId) +{ + bs->Reset(); + + if (timestamp!=0) + { + bs->Write((MessageID)ID_TIMESTAMP); + bs->Write(timestamp); + } + bs->Write((MessageID)ID_REPLICA_MANAGER_SERIALIZE); + bs->Write(worldId); + bs->Write(replica->GetNetworkID()); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void Connection_RM3::ClearDownloadGroup(RakPeerInterface *rakPeerInterface) +{ + unsigned int i; + for (i=0; i < downloadGroup.Size(); i++) + rakPeerInterface->DeallocatePacket(downloadGroup[i]); + downloadGroup.Clear(__FILE__,__LINE__); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SendSerializeIfChangedResult Connection_RM3::SendSerialize(RakNet::Replica3 *replica, bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::BitStream serializationData[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::Time timestamp, PRO sendParameters[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakPeerInterface *rakPeer, unsigned char worldId) +{ + bool channelHasData; + BitSize_t sum=0; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + if (indicesToSend[z]) + sum+=serializationData[z].GetNumberOfBitsUsed(); + } + if (sum==0) + return SSICR_DID_NOT_SEND_DATA; + + RakAssert(replica->GetNetworkID()!=UNASSIGNED_NETWORK_ID); + + RakNet::BitStream out; + BitSize_t bitsUsed; + + int channelIndex; + PRO lastPro=sendParameters[0]; + + for (channelIndex=0; channelIndex < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex++) + { + if (channelIndex==0) + { + SendSerializeHeader(replica, timestamp, &out, worldId); + } + else if (lastPro!=sendParameters[channelIndex]) + { + // Write out remainder + for (int channelIndex2=channelIndex; channelIndex2 < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex2++) + out.Write(false); + + // Send remainder + replica->OnSerializeTransmission(&out, systemAddress); + rakPeer->Send(&out,lastPro.priority,lastPro.reliability,lastPro.orderingChannel,systemAddress,false,lastPro.sendReceipt); + + // If no data left to send, quit out + bool anyData=false; + for (int channelIndex2=channelIndex; channelIndex2 < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; channelIndex2++) + { + if (serializationData[channelIndex2].GetNumberOfBitsUsed()>0) + { + anyData=true; + break; + } + } + if (anyData==false) + return SSICR_SENT_DATA; + + // Restart stream + SendSerializeHeader(replica, timestamp, &out, worldId); + + for (int channelIndex2=0; channelIndex2 < channelIndex; channelIndex2++) + out.Write(false); + lastPro=sendParameters[channelIndex]; + } + + bitsUsed=serializationData[channelIndex].GetNumberOfBitsUsed(); + channelHasData = indicesToSend[channelIndex]==true && bitsUsed>0; + out.Write(channelHasData); + if (channelHasData) + { + out.WriteCompressed(bitsUsed); + out.AlignWriteToByteBoundary(); + out.Write(serializationData[channelIndex]); + // Crap, forgot this line, was a huge bug in that I'd only send to the first 3 systems + serializationData[channelIndex].ResetReadPointer(); + } + } + replica->OnSerializeTransmission(&out, systemAddress); + rakPeer->Send(&out,lastPro.priority,lastPro.reliability,lastPro.orderingChannel,systemAddress,false,lastPro.sendReceipt); + return SSICR_SENT_DATA; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +SendSerializeIfChangedResult Connection_RM3::SendSerializeIfChanged(unsigned int queryToSerializeIndex, SerializeParameters *sp, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager) +{ + RakNet::Replica3 *replica = queryToSerializeReplicaList[queryToSerializeIndex]->replica; + + if (replica->GetNetworkID()==UNASSIGNED_NETWORK_ID) + return SSICR_DID_NOT_SEND_DATA; + + RM3QuerySerializationResult rm3qsr = replica->QuerySerialization(this); + if (rm3qsr==RM3QSR_NEVER_CALL_SERIALIZE) + { + // Never again for this connection and replica pair + OnNeverSerialize(queryToSerializeIndex, replicaManager); + return SSICR_NEVER_SERIALIZE; + } + + if (rm3qsr==RM3QSR_DO_NOT_CALL_SERIALIZE) + return SSICR_DID_NOT_SEND_DATA; + + if (replica->forceSendUntilNextUpdate) + { + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + if (replica->lastSentSerialization.indicesToSend[z]) + sp->bitsWrittenSoFar+=replica->lastSentSerialization.bitStream[z].GetNumberOfBitsUsed(); + } + return SendSerialize(replica, replica->lastSentSerialization.indicesToSend, replica->lastSentSerialization.bitStream, sp->messageTimestamp, sp->pro, rakPeer, worldId); + } + + for (int i=0; i < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; i++) + { + sp->outputBitstream[i].Reset(); + if (queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS) + sp->lastSentBitstream[i]=&queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[i]; + else + sp->lastSentBitstream[i]=&replica->lastSentSerialization.bitStream[i]; + } + + RM3SerializationResult serializationResult = replica->Serialize(sp); + + if (serializationResult==RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION) + { + // Never again for this connection and replica pair + OnNeverSerialize(queryToSerializeIndex, replicaManager); + return SSICR_NEVER_SERIALIZE; + } + + if (serializationResult==RM3SR_DO_NOT_SERIALIZE) + { + // Don't serialize this tick only + return SSICR_DID_NOT_SEND_DATA; + } + + // This is necessary in case the user in the Serialize() function for some reason read the bitstream they also wrote + // WIthout this code, the Write calls to another bitstream would not write the entire bitstream + BitSize_t sum=0; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp->outputBitstream[z].ResetReadPointer(); + sum+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + } + + if (sum==0) + { + // Don't serialize this tick only + return SSICR_DID_NOT_SEND_DATA; + } + + if (serializationResult==RM3SR_SERIALIZED_ALWAYS) + { + bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + allIndices[z]=true; + + queryToSerializeReplicaList[queryToSerializeIndex]->AllocBS(); + queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Reset(); + queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Write(&sp->outputBitstream[z]); + sp->outputBitstream[z].ResetReadPointer(); + } + return SendSerialize(replica, allIndices, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); + } + + if (serializationResult==RM3SR_SERIALIZED_ALWAYS_IDENTICALLY) + { + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + replica->lastSentSerialization.indicesToSend[z]=sp->outputBitstream[z].GetNumberOfBitsUsed()>0; + sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + replica->lastSentSerialization.bitStream[z].Reset(); + replica->lastSentSerialization.bitStream[z].Write(&sp->outputBitstream[z]); + sp->outputBitstream[z].ResetReadPointer(); + replica->forceSendUntilNextUpdate=true; + } + return SendSerialize(replica, replica->lastSentSerialization.indicesToSend, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); + } + + bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + if (serializationResult==RM3SR_BROADCAST_IDENTICALLY || serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION) + { + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + if (sp->outputBitstream[z].GetNumberOfBitsUsed() > 0 && + (serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION || + ((sp->outputBitstream[z].GetNumberOfBitsUsed()!=replica->lastSentSerialization.bitStream[z].GetNumberOfBitsUsed() || + memcmp(sp->outputBitstream[z].GetData(), replica->lastSentSerialization.bitStream[z].GetData(), sp->outputBitstream[z].GetNumberOfBytesUsed())!=0)))) + { + indicesToSend[z]=true; + replica->lastSentSerialization.indicesToSend[z]=true; + sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + replica->lastSentSerialization.bitStream[z].Reset(); + replica->lastSentSerialization.bitStream[z].Write(&sp->outputBitstream[z]); + sp->outputBitstream[z].ResetReadPointer(); + replica->forceSendUntilNextUpdate=true; + } + else + { + indicesToSend[z]=false; + replica->lastSentSerialization.indicesToSend[z]=false; + } + } + } + else + { + queryToSerializeReplicaList[queryToSerializeIndex]->AllocBS(); + + // RM3SR_SERIALIZED_UNIQUELY + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + if (sp->outputBitstream[z].GetNumberOfBitsUsed() > 0 && + (sp->outputBitstream[z].GetNumberOfBitsUsed()!=queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].GetNumberOfBitsUsed() || + memcmp(sp->outputBitstream[z].GetData(), queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].GetData(), sp->outputBitstream[z].GetNumberOfBytesUsed())!=0) + ) + { + indicesToSend[z]=true; + sp->bitsWrittenSoFar+=sp->outputBitstream[z].GetNumberOfBitsUsed(); + queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Reset(); + queryToSerializeReplicaList[queryToSerializeIndex]->lastSerializationResultBS->bitStream[z].Write(&sp->outputBitstream[z]); + sp->outputBitstream[z].ResetReadPointer(); + } + else + { + indicesToSend[z]=false; + } + } + } + + + if (serializationResult==RM3SR_BROADCAST_IDENTICALLY || serializationResult==RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION) + replica->forceSendUntilNextUpdate=true; + + // Send out the data + return SendSerialize(replica, indicesToSend, sp->outputBitstream, sp->messageTimestamp, sp->pro, rakPeer, worldId); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void Connection_RM3::OnLocalReference(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) replicaManager; + (void) constructionMode; + + LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); + lsr->replica=replica3; + queryToConstructReplicaList.Push(lsr,_FILE_AND_LINE_); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDereference(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + + LastSerializationResult* lsr=0; + unsigned int idx; + + bool objectExists; + idx=constructedReplicaList.GetIndexFromKey(replica3, &objectExists); + if (objectExists) + { + lsr=constructedReplicaList[idx]; + constructedReplicaList.RemoveAtIndex(idx); + } + + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + if (queryToConstructReplicaList[idx]->replica==replica3) + { + lsr=queryToConstructReplicaList[idx]; + queryToConstructReplicaList.RemoveAtIndex(idx); + break; + } + } + + for (idx=0; idx < queryToSerializeReplicaList.Size(); idx++) + { + if (queryToSerializeReplicaList[idx]->replica==replica3) + { + lsr=queryToSerializeReplicaList[idx]; + queryToSerializeReplicaList.RemoveAtIndex(idx); + break; + } + } + + for (idx=0; idx < queryToDestructReplicaList.Size(); idx++) + { + if (queryToDestructReplicaList[idx]->replica==replica3) + { + lsr=queryToDestructReplicaList[idx]; + queryToDestructReplicaList.RemoveAtIndex(idx); + break; + } + } + + ValidateLists(replicaManager); + + if (lsr) + RakNet::OP_DELETE(lsr,_FILE_AND_LINE_); + + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDownloadFromThisSystem(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); + lsr->replica=replica3; + + ConstructionMode constructionMode = QueryConstructionMode(); + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + unsigned int j; + for (j=0; j < queryToConstructReplicaList.Size(); j++) + { + if (queryToConstructReplicaList[j]->replica->GetNetworkID()==replica3->GetNetworkID() ) + { + queryToConstructReplicaList.RemoveAtIndex(j); + break; + } + } + + queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); + } + + constructedReplicaList.Insert(lsr->replica, lsr, true, _FILE_AND_LINE_); + //assert(queryToSerializeReplicaList.GetIndexOf(replica3)==(unsigned int)-1); + queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); + + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDownloadFromOtherSystem(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + unsigned int j; + for (j=0; j < queryToConstructReplicaList.Size(); j++) + { + if (queryToConstructReplicaList[j]->replica->GetNetworkID()==replica3->GetNetworkID() ) + { + return; + } + } + + OnLocalReference(replica3, replicaManager); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnNeverConstruct(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) constructionMode; + + ValidateLists(replicaManager); + LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; + queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); + RakNet::OP_DELETE(lsr,_FILE_AND_LINE_); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnConstructToThisConnection(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) constructionMode; + + ValidateLists(replicaManager); + LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; + queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); + //assert(constructedReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + constructedReplicaList.Insert(lsr->replica,lsr,true,_FILE_AND_LINE_); + //assert(queryToDestructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); + //assert(queryToSerializeReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnConstructToThisConnection(Replica3 *replica, ReplicaManager3 *replicaManager) +{ + RakAssert(QueryConstructionMode()==QUERY_CONNECTION_FOR_REPLICA_LIST); + (void) replicaManager; + + LastSerializationResult* lsr=RakNet::OP_NEW(_FILE_AND_LINE_); + lsr->replica=replica; + constructedReplicaList.Insert(replica,lsr,true,_FILE_AND_LINE_); + queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnNeverSerialize(unsigned int queryToSerializeIndex, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + queryToSerializeReplicaList.RemoveAtIndex(queryToSerializeIndex); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnReplicaAlreadyExists(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) constructionMode; + + ValidateLists(replicaManager); + LastSerializationResult* lsr = queryToConstructReplicaList[queryToConstructIdx]; + queryToConstructReplicaList.RemoveAtIndex(queryToConstructIdx); + //assert(constructedReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + constructedReplicaList.Insert(lsr->replica,lsr,true,_FILE_AND_LINE_); + //assert(queryToDestructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToDestructReplicaList.Push(lsr,_FILE_AND_LINE_); + //assert(queryToSerializeReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToSerializeReplicaList.Push(lsr,_FILE_AND_LINE_); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDownloadExisting(Replica3* replica3, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + + ConstructionMode constructionMode = QueryConstructionMode(); + if (constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION) + { + unsigned int idx; + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + if (queryToConstructReplicaList[idx]->replica==replica3) + { + OnConstructToThisConnection(idx, replicaManager); + return; + } + } + } + else + { + OnConstructToThisConnection(replica3, replicaManager); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnSendDestructionFromQuery(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager) +{ + ConstructionMode constructionMode = QueryConstructionMode(); + RakAssert(constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION || constructionMode==QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION); + (void) constructionMode; + + ValidateLists(replicaManager); + LastSerializationResult* lsr = queryToDestructReplicaList[queryToDestructIdx]; + queryToDestructReplicaList.RemoveAtIndex(queryToDestructIdx); + unsigned int j; + for (j=0; j < queryToSerializeReplicaList.Size(); j++) + { + if (queryToSerializeReplicaList[j]->replica->GetNetworkID()==lsr->replica->GetNetworkID() ) + { + queryToSerializeReplicaList.RemoveAtIndex(j); + break; + } + } + for (j=0; j < constructedReplicaList.Size(); j++) + { + if (constructedReplicaList[j]->replica->GetNetworkID()==lsr->replica->GetNetworkID() ) + { + constructedReplicaList.RemoveAtIndex(j); + break; + } + } + //assert(queryToConstructReplicaList.GetIndexOf(lsr->replica)==(unsigned int)-1); + queryToConstructReplicaList.Push(lsr,_FILE_AND_LINE_); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::OnDoNotQueryDestruction(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager) +{ + ValidateLists(replicaManager); + queryToDestructReplicaList.RemoveAtIndex(queryToDestructIdx); + ValidateLists(replicaManager); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::ValidateLists(ReplicaManager3 *replicaManager) const +{ + (void) replicaManager; + /* +#ifdef _DEBUG + // Each object should exist only once in either constructedReplicaList or queryToConstructReplicaList + // replicaPointer from LastSerializationResult should be same among all lists + unsigned int idx, idx2; + for (idx=0; idx < constructedReplicaList.Size(); idx++) + { + idx2=queryToConstructReplicaList.GetIndexOf(constructedReplicaList[idx]->replica); + if (idx2!=(unsigned int)-1) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + idx2=constructedReplicaList.GetIndexOf(queryToConstructReplicaList[idx]->replica); + if (idx2!=(unsigned int)-1) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + LastSerializationResult *lsr, *lsr2; + for (idx=0; idx < constructedReplicaList.Size(); idx++) + { + lsr=constructedReplicaList[idx]; + + idx2=queryToSerializeReplicaList.GetIndexOf(lsr->replica); + if (idx2!=(unsigned int)-1) + { + lsr2=queryToSerializeReplicaList[idx2]; + if (lsr2!=lsr) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + idx2=queryToDestructReplicaList.GetIndexOf(lsr->replica); + if (idx2!=(unsigned int)-1) + { + lsr2=queryToDestructReplicaList[idx2]; + if (lsr2!=lsr) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + } + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + lsr=queryToConstructReplicaList[idx]; + + idx2=queryToSerializeReplicaList.GetIndexOf(lsr->replica); + if (idx2!=(unsigned int)-1) + { + lsr2=queryToSerializeReplicaList[idx2]; + if (lsr2!=lsr) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + idx2=queryToDestructReplicaList.GetIndexOf(lsr->replica); + if (idx2!=(unsigned int)-1) + { + lsr2=queryToDestructReplicaList[idx2]; + if (lsr2!=lsr) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + } + + // Verify pointer integrity + for (idx=0; idx < constructedReplicaList.Size(); idx++) + { + if (constructedReplicaList[idx]->replica->replicaManager!=replicaManager) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } + + // Verify pointer integrity + for (idx=0; idx < queryToConstructReplicaList.Size(); idx++) + { + if (queryToConstructReplicaList[idx]->replica->replicaManager!=replicaManager) + { + int a=5; + assert(a==0); + int *b=0; + *b=5; + } + } +#endif + */ +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::SendConstruction(DataStructures::List &newObjects, DataStructures::List &deletedObjects, PRO sendParameters, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager3) +{ + if (newObjects.Size()==0 && deletedObjects.Size()==0) + return; + + // All construction and destruction takes place in the same network message + // Otherwise, if objects rely on each other being created the same tick to be valid, this won't always be true + // DataStructures::List serializedObjects; + BitSize_t offsetStart, offsetStart2, offsetEnd; + unsigned int newListIndex, oldListIndex; + RakNet::BitStream bsOut; + NetworkID networkId; + if (isFirstConstruction) + { + bsOut.Write((MessageID)ID_REPLICA_MANAGER_DOWNLOAD_STARTED); + bsOut.Write(worldId); + SerializeOnDownloadStarted(&bsOut); + rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); + } + + // LastSerializationResult* lsr; + bsOut.Reset(); + bsOut.Write((MessageID)ID_REPLICA_MANAGER_CONSTRUCTION); + bsOut.Write(worldId); + uint16_t objectSize = (uint16_t) newObjects.Size(); + bsOut.Write(objectSize); + + // Construction + for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) + { + offsetStart=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart); // overwritten to point to the end of the stream + networkId=newObjects[newListIndex]->GetNetworkID(); + bsOut.Write(networkId); + + RM3ConstructionState cs = newObjects[newListIndex]->QueryConstruction(this, replicaManager3); + bool actuallyCreateObject = cs==RM3CS_SEND_CONSTRUCTION; + bsOut.Write(actuallyCreateObject); + bsOut.AlignWriteToByteBoundary(); + + if (actuallyCreateObject) + { + // Actually create the object + bsOut.Write(newObjects[newListIndex]->creatingSystemGUID); + offsetStart2=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart2); // overwritten to point to after the call to WriteAllocationID + bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy + newObjects[newListIndex]->WriteAllocationID(this, &bsOut); + bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart2); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + newObjects[newListIndex]->SerializeConstruction(&bsOut, this); + } + else + { + newObjects[newListIndex]->SerializeConstructionExisting(&bsOut, this); + } + + bsOut.AlignWriteToByteBoundary(); + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + } + + RakNet::BitStream bsOut2; + for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) + { + bsOut2.Reset(); + RM3ConstructionState cs = newObjects[newListIndex]->QueryConstruction(this, replicaManager3); + if (cs==RM3CS_SEND_CONSTRUCTION) + { + newObjects[newListIndex]->PostSerializeConstruction(&bsOut2, this); + } + else + { + RakAssert(cs==RM3CS_ALREADY_EXISTS_REMOTELY); + newObjects[newListIndex]->PostSerializeConstructionExisting(&bsOut2, this); + } + if (bsOut2.GetNumberOfBitsUsed()>0) + { + bsOut.Write(true); + bsOut.AlignWriteToByteBoundary(); + offsetStart=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart); // overwritten to point to the end of the stream + networkId=newObjects[newListIndex]->GetNetworkID(); + bsOut.Write(networkId); + bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy + bsOut.Write(&bsOut2); + bsOut.AlignWriteToByteBoundary(); // Give the user an aligned bitStream in case they use memcpy + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + } + else + bsOut.Write(false); + } + bsOut.AlignWriteToByteBoundary(); + + // Destruction + objectSize = (uint16_t) deletedObjects.Size(); + bsOut.Write(objectSize); + for (oldListIndex=0; oldListIndex < deletedObjects.Size(); oldListIndex++) + { + networkId=deletedObjects[oldListIndex]->GetNetworkID(); + bsOut.Write(networkId); + offsetStart=bsOut.GetWriteOffset(); + bsOut.Write(offsetStart); + deletedObjects[oldListIndex]->deletingSystemGUID=rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); + bsOut.Write(deletedObjects[oldListIndex]->deletingSystemGUID); + deletedObjects[oldListIndex]->SerializeDestruction(&bsOut, this); + bsOut.AlignWriteToByteBoundary(); + offsetEnd=bsOut.GetWriteOffset(); + bsOut.SetWriteOffset(offsetStart); + bsOut.Write(offsetEnd); + bsOut.SetWriteOffset(offsetEnd); + } + rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); + + // TODO - shouldn't this be part of construction? + + // Initial Download serialize to a new system + // Immediately send serialize after construction if the replica object already has saved data + // If the object was serialized identically, and does not change later on, then the new connection never gets the data + SerializeParameters sp; + sp.whenLastSerialized=0; + RakNet::BitStream emptyBs; + for (int index=0; index < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; index++) + { + sp.lastSentBitstream[index]=&emptyBs; + sp.pro[index]=sendParameters; + sp.pro[index].reliability=RELIABLE_ORDERED; + } + + sp.bitsWrittenSoFar=0; + RakNet::Time t = RakNet::GetTimeMS(); + for (newListIndex=0; newListIndex < newObjects.Size(); newListIndex++) + { + sp.destinationConnection=this; + sp.messageTimestamp=0; + RakNet::Replica3 *replica = newObjects[newListIndex]; + // 8/22/09 Forgot ResetWritePointer + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp.outputBitstream[z].ResetWritePointer(); + } + + RM3SerializationResult res = replica->Serialize(&sp); + if (res!=RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION && + res!=RM3SR_DO_NOT_SERIALIZE && + res!=RM3SR_SERIALIZED_UNIQUELY) + { + bool allIndices[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + for (int z=0; z < RM3_NUM_OUTPUT_BITSTREAM_CHANNELS; z++) + { + sp.bitsWrittenSoFar+=sp.outputBitstream[z].GetNumberOfBitsUsed(); + allIndices[z]=true; + } + SendSerialize(replica, allIndices, sp.outputBitstream, sp.messageTimestamp, sp.pro, rakPeer, worldId); + newObjects[newListIndex]->whenLastSerialized=t; + + } + // else wait for construction request accepted before serializing + } + + if (isFirstConstruction) + { + bsOut.Reset(); + bsOut.Write((MessageID)ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE); + bsOut.Write(worldId); + SerializeOnDownloadComplete(&bsOut); + rakPeer->Send(&bsOut,sendParameters.priority,RELIABLE_ORDERED,sendParameters.orderingChannel,systemAddress,false,sendParameters.sendReceipt); + } + + isFirstConstruction=false; + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Connection_RM3::SendValidation(RakNet::RakPeerInterface *rakPeer, unsigned char worldId) +{ + // Hijack to mean sendValidation + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_REPLICA_MANAGER_SCOPE_CHANGE); + bsOut.Write(worldId); + rakPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Replica3::Replica3() +{ + creatingSystemGUID=UNASSIGNED_RAKNET_GUID; + deletingSystemGUID=UNASSIGNED_RAKNET_GUID; + replicaManager=0; + forceSendUntilNextUpdate=false; + whenLastSerialized=0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Replica3::~Replica3() +{ + if (replicaManager) + { + replicaManager->Dereference(this); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void Replica3::BroadcastDestruction(void) +{ + replicaManager->BroadcastDestruction(this,UNASSIGNED_SYSTEM_ADDRESS); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RakNetGUID Replica3::GetCreatingSystemGUID(void) const +{ + return creatingSystemGUID; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ConstructionState Replica3::QueryConstruction_ClientConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) +{ + (void) destinationConnection; + if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return RM3CS_SEND_CONSTRUCTION; + // Send back to the owner client too, because they couldn't assign the network ID + if (isThisTheServer) + return RM3CS_SEND_CONSTRUCTION; + return RM3CS_NEVER_CONSTRUCT; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool Replica3::QueryRemoteConstruction_ClientConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer) +{ + (void) sourceConnection; + (void) isThisTheServer; + + // OK to create + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ConstructionState Replica3::QueryConstruction_ServerConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) +{ + (void) destinationConnection; + + if (isThisTheServer) + return RM3CS_SEND_CONSTRUCTION; + return RM3CS_NEVER_CONSTRUCT; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool Replica3::QueryRemoteConstruction_ServerConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer) +{ + (void) sourceConnection; + if (isThisTheServer) + return false; + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ConstructionState Replica3::QueryConstruction_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode) +{ + (void) destinationConnection; + + if (p2pMode==R3P2PM_SINGLE_OWNER) + { + // We send to all, others do nothing + if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return RM3CS_SEND_CONSTRUCTION; + + // RM3CS_NEVER_CONSTRUCT will not send the object, and will not Serialize() it + return RM3CS_NEVER_CONSTRUCT; + } + else if (p2pMode==R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE) + { + return RM3CS_SEND_CONSTRUCTION; + } + else + { + RakAssert(p2pMode==R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE); + + // RM3CS_ALREADY_EXISTS_REMOTELY will not send the object, but WILL call QuerySerialization() and Serialize() on it. + return RM3CS_ALREADY_EXISTS_REMOTELY; + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +bool Replica3::QueryRemoteConstruction_PeerToPeer(RakNet::Connection_RM3 *sourceConnection) +{ + (void) sourceConnection; + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3QuerySerializationResult Replica3::QuerySerialization_ClientSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) +{ + // Owner client sends to all + if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return RM3QSR_CALL_SERIALIZE; + // Server sends to all but owner client + if (isThisTheServer && destinationConnection->GetRakNetGUID()!=creatingSystemGUID) + return RM3QSR_CALL_SERIALIZE; + // Remote clients do not send + return RM3QSR_NEVER_CALL_SERIALIZE; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3QuerySerializationResult Replica3::QuerySerialization_ServerSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer) +{ + (void) destinationConnection; + // Server sends to all + if (isThisTheServer) + return RM3QSR_CALL_SERIALIZE; + + // Clients do not send + return RM3QSR_NEVER_CALL_SERIALIZE; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3QuerySerializationResult Replica3::QuerySerialization_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode) +{ + (void) destinationConnection; + + if (p2pMode==R3P2PM_SINGLE_OWNER) + { + // Owner peer sends to all + if (creatingSystemGUID==replicaManager->GetRakPeerInterface()->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) + return RM3QSR_CALL_SERIALIZE; + + // Remote peers do not send + return RM3QSR_NEVER_CALL_SERIALIZE; + } + else if (p2pMode==R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE) + { + return RM3QSR_CALL_SERIALIZE; + } + else + { + RakAssert(p2pMode==R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE); + return RM3QSR_DO_NOT_CALL_SERIALIZE; + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_Client(RakNet::Connection_RM3 *droppedConnection) const +{ + (void) droppedConnection; + return RM3AOPC_DELETE_REPLICA; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_Server(RakNet::Connection_RM3 *droppedConnection) const +{ + (void) droppedConnection; + return RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RM3ActionOnPopConnection Replica3::QueryActionOnPopConnection_PeerToPeer(RakNet::Connection_RM3 *droppedConnection) const +{ + (void) droppedConnection; + return RM3AOPC_DELETE_REPLICA; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/ReplicaManager3.h b/src/raknet/ReplicaManager3.h index d256ad9..527b8eb 100755 --- a/src/raknet/ReplicaManager3.h +++ b/src/raknet/ReplicaManager3.h @@ -1,1040 +1,1040 @@ -/// \file -/// \brief Contains the third iteration of the ReplicaManager class. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_ReplicaManager3==1 - -#ifndef __REPLICA_MANAGER_3 -#define __REPLICA_MANAGER_3 - -#include "RakNetTypes.h" -#include "RakNetTime.h" -#include "BitStream.h" -#include "PacketPriority.h" -#include "PluginInterface2.h" -#include "NetworkIDObject.h" -#include "DS_OrderedList.h" -#include "DS_Queue.h" - -/// \defgroup REPLICA_MANAGER_GROUP3 ReplicaManager3 -/// \brief Third implementation of object replication -/// \details -/// \ingroup REPLICA_MANAGER_GROUP - -namespace RakNet -{ -class Connection_RM3; -class Replica3; - - -/// \internal -/// \ingroup REPLICA_MANAGER_GROUP3 -struct PRO -{ - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketPriority(). - PacketPriority priority; - - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketReliability(). - PacketReliability reliability; - - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultOrderingChannel(). - char orderingChannel; - - /// Passed to RakPeerInterface::Send(). Defaults to 0. - uint32_t sendReceipt; - - bool operator==( const PRO& right ) const; - bool operator!=( const PRO& right ) const; -}; - - -/// \brief System to help automate game object construction, destruction, and serialization -/// \details ReplicaManager3 tracks your game objects and automates the networking for replicating them across the network
      -/// As objects are created, destroyed, or serialized differently, those changes are pushed out to other systems.
      -/// To use:
      -///
        -///
      1. Derive from Connection_RM3 and implement Connection_RM3::AllocReplica(). This is a factory function where given a user-supplied identifier for a class (such as name) return an instance of that class. Should be able to return any networked object in your game. -///
      2. Derive from ReplicaManager3 and implement AllocConnection() and DeallocConnection() to return the class you created in step 1. -///
      3. Derive your networked game objects from Replica3. All pure virtuals have to be implemented, however defaults are provided for Replica3::QueryConstruction(), Replica3::QueryRemoteConstruction(), and Replica3::QuerySerialization() depending on your network architecture. -///
      4. When a new game object is created on the local system, pass it to ReplicaManager3::Reference(). -///
      5. When a game object is destroyed on the local system, and you want other systems to know about it, call Replica3::BroadcastDestruction() -///
      -///
      -/// At this point, all new connections will automatically download, get construction messages, get destruction messages, and update serialization automatically. -/// \ingroup REPLICA_MANAGER_GROUP3 -class RAK_DLL_EXPORT ReplicaManager3 : public PluginInterface2 -{ -public: - ReplicaManager3(); - virtual ~ReplicaManager3(); - - /// \brief Implement to return a game specific derivation of Connection_RM3 - /// \details The connection object represents a remote system connected to you that is using the ReplicaManager3 system.
      - /// It has functions to perform operations per-connection.
      - /// AllocConnection() and DeallocConnection() are factory functions to create and destroy instances of the connection object.
      - /// It is used if autoCreate is true via SetAutoManageConnections() (true by default). Otherwise, the function is not called, and you will have to call PushConnection() manually
      - /// \note If you do not want a new network connection to immediately download game objects, SetAutoManageConnections() and PushConnection() are how you do this. - /// \sa SetAutoManageConnections() - /// \param[in] systemAddress Address of the system you are adding - /// \param[in] rakNetGUID GUID of the system you are adding. See Packet::rakNetGUID or RakPeerInterface::GetGUIDFromSystemAddress() - /// \return The new connection instance. - virtual Connection_RM3* AllocConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID) const=0; - - /// \brief Implement to destroy a class instanced returned by AllocConnection() - /// \details Most likely just implement as {delete connection;}
      - /// It is used if autoDestroy is true via SetAutoManageConnections() (true by default). Otherwise, the function is not called and you would then be responsible for deleting your own connection objects. - /// \param[in] connection The pointer instance to delete - virtual void DeallocConnection(Connection_RM3 *connection) const=0; - - /// \brief Enable or disable automatically assigning connections to new instances of Connection_RM3 - /// \details ReplicaManager3 can automatically create and/or destroy Connection_RM3 as systems connect or disconnect from RakPeerInterface.
      - /// By default this is on, to make the system easier to learn and setup.
      - /// If you don't want all connections to take part in the game, or you want to delay when a connection downloads the game, set \a autoCreate to false.
      - /// If you want to delay deleting a connection that has dropped, set \a autoDestroy to false. If you do this, then you must call PopConnection() to remove that connection from being internally tracked. You'll also have to delete the connection instance on your own.
      - /// \param[in] autoCreate Automatically call ReplicaManager3::AllocConnection() for each new connection. Defaults to true. - /// \param[in] autoDestroy Automatically call ReplicaManager3::DeallocConnection() for each dropped connection. Defaults to true. - void SetAutoManageConnections(bool autoCreate, bool autoDestroy); - - /// \brief Track a new Connection_RM3 instance - /// \details If \a autoCreate is false for SetAutoManageConnections(), then you need this function to add new instances of Connection_RM3 yourself.
      - /// You don't need to track this pointer yourself, you can get it with GetConnectionAtIndex(), GetConnectionByGUID(), or GetConnectionBySystemAddress().
      - /// \param[in] newConnection The new connection instance to track. - bool PushConnection(RakNet::Connection_RM3 *newConnection); - - /// \brief Stop tracking a connection - /// \details On call, for each replica returned by GetReplicasCreatedByGuid(), QueryActionOnPopConnection() will be called. Depending on the return value, this may delete the corresponding replica.
      - /// If autoDestroy is true in the call to SetAutoManageConnections() (true by default) then this is called automatically when the connection is lost. In that case, the returned connection instance is deleted.
      - /// \param[in] guid of the connection to get. Passed to ReplicaManager3::AllocConnection() originally. - RakNet::Connection_RM3 * PopConnection(RakNetGUID guid); - - /// \brief Adds a replicated object to the system. - /// \details Anytime you create a new object that derives from Replica3, and you want ReplicaManager3 to use it, pass it to Reference().
      - /// Remote systems already connected will potentially download this object the next time ReplicaManager3::Update() is called, which happens every time you call RakPeerInterface::Receive().
      - /// You can also call ReplicaManager3::Update() manually to send referenced objects right away - /// \param[in] replica3 The object to start tracking - void Reference(RakNet::Replica3 *replica3); - - /// \brief Removes a replicated object from the system. - /// \details The object is not deallocated, it is up to the caller to do so.
      - /// This is called automatically from the destructor of Replica3, so you don't need to call it manually unless you want to stop tracking an object before it is destroyed. - /// \param[in] replica3 The object to stop tracking - void Dereference(RakNet::Replica3 *replica3); - - /// \brief Removes multiple replicated objects from the system. - /// \details Same as Dereference(), but for a list of objects.
      - /// Useful with the lists returned by GetReplicasCreatedByGuid(), GetReplicasCreatedByMe(), or GetReferencedReplicaList().
      - /// \param[in] replicaListIn List of objects - void DereferenceList(DataStructures::List &replicaListIn); - - /// \brief Returns all objects originally created by a particular system - /// \details Originally created is defined as the value of Replica3::creatingSystemGUID, which is automatically assigned in ReplicaManager3::Reference().
      - /// You do not have to be directly connected to that system to get the objects originally created by that system.
      - /// \param[in] guid GUID of the system we are referring to. Originally passed as the \a guid parameter to ReplicaManager3::AllocConnection() - /// \param[out] List of Replica3 instances to be returned - void GetReplicasCreatedByGuid(RakNetGUID guid, DataStructures::List &replicaListOut); - - /// \brief Returns all objects originally created by your system - /// \details Calls GetReplicasCreatedByGuid() for your own system guid. - /// \param[out] List of Replica3 instances to be returned - void GetReplicasCreatedByMe(DataStructures::List &replicaListOut); - - /// \brief Returns the entire list of Replicas that we know about. - /// \details This is all Replica3 instances passed to Reference, as well as instances we downloaded and created via Connection_RM3::AllocReference() - /// \param[out] List of Replica3 instances to be returned - void GetReferencedReplicaList(DataStructures::List &replicaListOut); - - /// \brief Returns the number of replicas known about - /// \details Returns the size of the list that would be returned by GetReferencedReplicaList() - /// \return How many replica objects are in the list of replica objects - unsigned GetReplicaCount(void) const; - - /// \brief Returns a replica by index - /// \details Returns one of the items in the list that would be returned by GetReferencedReplicaList() - /// \param[in] index An index, from 0 to GetReplicaCount()-1. - /// \return A Replica3 instance - Replica3 *GetReplicaAtIndex(unsigned index); - - /// \brief Returns the number of connections - /// \details Returns the number of connections added with ReplicaManager3::PushConnection(), minus the number removed with ReplicaManager3::PopConnection() - /// \return The number of registered connections - unsigned int GetConnectionCount(void) const; - - /// \brief Returns a connection pointer previously added with PushConnection() - /// \param[in] index An index, from 0 to GetConnectionCount()-1. - /// \return A Connection_RM3 pointer - Connection_RM3* GetConnectionAtIndex(unsigned index) const; - - /// \brief Returns a connection pointer previously added with PushConnection() - /// \param[in] sa The system address of the connection to return - /// \return A Connection_RM3 pointer, or 0 if not found - Connection_RM3* GetConnectionBySystemAddress(const SystemAddress &sa) const; - - /// \brief Returns a connection pointer previously added with PushConnection.() - /// \param[in] guid The guid of the connection to return - /// \return A Connection_RM3 pointer, or 0 if not found - Connection_RM3* GetConnectionByGUID(RakNetGUID guid) const; - - /// \param[in] Default ordering channel to use for object creation, destruction, and serializations - void SetDefaultOrderingChannel(char def); - - /// \param[in] Default packet priority to use for object creation, destruction, and serializations - void SetDefaultPacketPriority(PacketPriority def); - - /// \param[in] Default packet reliability to use for object creation, destruction, and serializations - void SetDefaultPacketReliability(PacketReliability def); - - /// \details Every \a intervalMS milliseconds, Connection_RM3::OnAutoserializeInterval() will be called.
      - /// Defaults to 30.
      - /// Pass with <0 to disable. Pass 0 to Serialize() every time RakPeer::Recieve() is called
      - /// If you want to control the update interval with more granularity, use the return values from Replica3::Serialize().
      - /// \param[in] intervalMS How frequently to autoserialize all objects. This controls the maximum number of game object updates per second. - void SetAutoSerializeInterval(RakNet::Time intervalMS); - - /// \brief Return the connections that we think have an instance of the specified Replica3 instance - /// \details This can be wrong, for example if that system locally deleted the outside the scope of ReplicaManager3, if QueryRemoteConstruction() returned false, or if DeserializeConstruction() returned false. - /// \param[in] replica The replica to check against. - /// \param[out] connectionsThatHaveConstructedThisReplica Populated with connection instances that we believe have \a replica allocated - void GetConnectionsThatHaveReplicaConstructed(Replica3 *replica, DataStructures::List &connectionsThatHaveConstructedThisReplica); - - /// \brief Defines the unique instance of ReplicaManager3 if multiple instances are on the same instance of RakPeerInterface - /// \details ReplicaManager3 supports multiple instances of itself attached to the same instance of rakPeer, in case your game has multiple worlds.
      - /// Call SetWorldID with a different number for each instance.
      - /// The default worldID is 0.
      - /// To use multiple worlds, you will also need to call ReplicaManager3::SetNetworkIDManager() to have a different NetworkIDManager instance per world - void SetWorldID(unsigned char id); - - /// \return Whatever was passed to SetWorldID(), or 0 if it was never called. - unsigned char GetWorldID(void) const; - - /// \details Sets the networkIDManager instance that this plugin relys upon.
      - /// Uses whatever instance is attached to RakPeerInterface if unset.
      - /// To support multiple worlds, you should set it to a different manager for each instance of the plugin - /// \param[in] _networkIDManager The externally allocated NetworkIDManager instance for this plugin to use. - void SetNetworkIDManager(NetworkIDManager *_networkIDManager); - - /// Returns what was passed to SetNetworkIDManager(), or the instance on RakPeerInterface if unset. - NetworkIDManager *GetNetworkIDManager(void) const; - - /// \details Send a network command to destroy one or more Replica3 instances - /// Usually you won't need this, but use Replica3::BroadcastDestruction() instead. - /// The objects are unaffected locally - /// \param[in] replicaList List of Replica3 objects to tell other systems to destroy. - /// \param[in] exclusionAddress Which system to not send to. UNASSIGNED_SYSTEM_ADDRESS to send to all. - void BroadcastDestructionList(DataStructures::List &replicaListSource, const SystemAddress &exclusionAddress); - - /// \internal - /// \details Tell other systems that have this replica to destroy this replica.
      - /// You shouldn't need to call this, as it happens in the Replica3 destructor - void BroadcastDestruction(Replica3 *replica, const SystemAddress &exclusionAddress); - - /// \internal - /// \details Frees internal lists.
      - /// Externally allocated pointers are not deallocated - void Clear(void); - - /// \internal - PRO GetDefaultSendParameters(void) const; - - /// Call interfaces, send data - virtual void Update(void); -protected: - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); - virtual void OnRakPeerShutdown(void); - virtual void OnDetach(void); - - PluginReceiveResult OnConstruction(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); - PluginReceiveResult OnSerialize(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, RakNet::Time timestamp, unsigned char packetDataOffset); - PluginReceiveResult OnDownloadStarted(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); - PluginReceiveResult OnDownloadComplete(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); - - void DeallocReplicaNoBroadcastDestruction(RakNet::Connection_RM3 *connection, RakNet::Replica3 *replica3); - RakNet::Connection_RM3 * PopConnection(unsigned int index); - Replica3* GetReplicaByNetworkID(NetworkID networkId); - unsigned int ReferenceInternal(RakNet::Replica3 *replica3); - - DataStructures::List connectionList; - DataStructures::List userReplicaList; - - PRO defaultSendParameters; - RakNet::Time autoSerializeInterval; - RakNet::Time lastAutoSerializeOccurance; - unsigned char worldId; - NetworkIDManager *networkIDManager; - bool autoCreateConnections, autoDestroyConnections; - Replica3 *currentlyDeallocatingReplica; - - friend class Connection_RM3; -}; - -static const int RM3_NUM_OUTPUT_BITSTREAM_CHANNELS=16; - -/// \ingroup REPLICA_MANAGER_GROUP3 -struct LastSerializationResultBS -{ - RakNet::BitStream bitStream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; -}; - -/// Represents the serialized data for an object the last time it was sent. Used by Connection_RM3::OnAutoserializeInterval() and Connection_RM3::SendSerializeIfChanged() -/// \ingroup REPLICA_MANAGER_GROUP3 -struct LastSerializationResult -{ - LastSerializationResult(); - ~LastSerializationResult(); - - /// The replica instance we serialized - RakNet::Replica3 *replica; - //bool neverSerialize; -// bool isConstructed; - - void AllocBS(void); - LastSerializationResultBS* lastSerializationResultBS; -}; - -/// Parameters passed to Replica3::Serialize() -/// \ingroup REPLICA_MANAGER_GROUP3 -struct SerializeParameters -{ - /// Write your output for serialization here - /// If nothing is written, the serialization will not occur - /// Write to any or all of the NUM_OUTPUT_BITSTREAM_CHANNELS channels available. Channels can hold independent data - RakNet::BitStream outputBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - - /// Last bitstream we sent for this replica to this system. - /// Read, but DO NOT MODIFY - RakNet::BitStream* lastSentBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - - /// Set to non-zero to transmit a timestamp with this message. - /// Defaults to 0 - /// Use RakNet::GetTime() for this - RakNet::Time messageTimestamp; - - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketPriority(). - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketReliability(). - /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultOrderingChannel(). - PRO pro[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - - /// Passed to RakPeerInterface::Send(). - RakNet::Connection_RM3 *destinationConnection; - - /// For prior serializations this tick, for the same connection, how many bits have we written so far? - /// Use this to limit how many objects you send to update per-tick if desired - BitSize_t bitsWrittenSoFar; - - /// When this object was last serialized to the connection - /// 0 means never - RakNet::Time whenLastSerialized; - - /// Current time, in milliseconds. - /// curTime - whenLastSerialized is how long it has been since this object was last sent - RakNet::Time curTime; -}; - -/// \ingroup REPLICA_MANAGER_GROUP3 -struct DeserializeParameters -{ - RakNet::BitStream serializationBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - bool bitstreamWrittenTo[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; - RakNet::Time timeStamp; - RakNet::Connection_RM3 *sourceConnection; -}; - -/// \ingroup REPLICA_MANAGER_GROUP3 -enum SendSerializeIfChangedResult -{ - SSICR_SENT_DATA, - SSICR_DID_NOT_SEND_DATA, - SSICR_NEVER_SERIALIZE, -}; - -/// \brief Each remote system is represented by Connection_RM3. Used to allocate Replica3 and track which instances have been allocated -/// \details Important function: AllocReplica() - must be overridden to create an object given an identifier for that object, which you define for all objects in your game -/// \ingroup REPLICA_MANAGER_GROUP3 -class RAK_DLL_EXPORT Connection_RM3 -{ -public: - - Connection_RM3(const SystemAddress &_systemAddress, RakNetGUID _guid); - virtual ~Connection_RM3(); - - /// \brief Class factory to create a Replica3 instance, given a user-defined identifier - /// \details Identifier is returned by Replica3::WriteAllocationID() for what type of class to create.
      - /// This is called when you download a replica from another system.
      - /// See Replica3::Dealloc for the corresponding destruction message.
      - /// Return 0 if unable to create the intended object. Note, in that case the other system will still think we have the object and will try to serialize object updates to us. Generally, you should not send objects the other system cannot create.
      - /// \sa Replica3::WriteAllocationID(). - /// Sample implementation:
      - /// {RakNet::RakString typeName; allocationIdBitstream->Read(typeName); if (typeName=="Soldier") return new Soldier; return 0;}
      - /// \param[in] allocationIdBitstream user-defined bitstream uniquely identifying a game object type - /// \param[in] replicaManager3 Instance of ReplicaManager3 that controls this connection - /// \return The new replica instance - virtual Replica3 *AllocReplica(RakNet::BitStream *allocationIdBitstream, ReplicaManager3 *replicaManager3)=0; - - /// \brief Get list of all replicas that are constructed for this connection - /// \param[out] objectsTheyDoHave Destination list. Returned in sorted ascending order, sorted on the value of the Replica3 pointer. - virtual void GetConstructedReplicas(DataStructures::List &objectsTheyDoHave); - - /// Returns true if we think this remote connection has this replica constructed - /// \param[in] replica3 Which replica we are querying - /// \return True if constructed, false othewise - bool HasReplicaConstructed(RakNet::Replica3 *replica); - - /// When a new connection connects, before sending any objects, SerializeOnDownloadStarted() is called - /// \param[out] bitStream Passed to DeserializeOnDownloadStarted() - virtual void SerializeOnDownloadStarted(RakNet::BitStream *bitStream) {(void) bitStream;} - - /// Receives whatever was written in SerializeOnDownloadStarted() - /// \param[in] bitStream Written in SerializeOnDownloadStarted() - virtual void DeserializeOnDownloadStarted(RakNet::BitStream *bitStream) {(void) bitStream;} - - /// When a new connection connects, after constructing and serialization all objects, SerializeOnDownloadComplete() is called - /// \param[out] bitStream Passed to DeserializeOnDownloadComplete() - virtual void SerializeOnDownloadComplete(RakNet::BitStream *bitStream) {(void) bitStream;} - - /// Receives whatever was written in DeserializeOnDownloadComplete() - /// \param[in] bitStream Written in SerializeOnDownloadComplete() - virtual void DeserializeOnDownloadComplete(RakNet::BitStream *bitStream) {(void) bitStream;} - - /// \return The system address passed to the constructor of this object - SystemAddress GetSystemAddress(void) const {return systemAddress;} - - /// \return Returns the RakNetGUID passed to the constructor of this object - RakNetGUID GetRakNetGUID(void) const {return guid;} - - /// List of enumerations for how to get the list of valid objects for other systems - enum ConstructionMode - { - /// For every object that does not exist on the remote system, call Replica3::QueryConstruction() every tick. - /// Do not call Replica3::QueryDestruction() - /// Do not call Connection_RM3::QueryReplicaList() - QUERY_REPLICA_FOR_CONSTRUCTION, - - /// For every object that does not exist on the remote system, call Replica3::QueryConstruction() every tick. Based on the call, the object may be sent to the other system. - /// For every object that does exist on the remote system, call Replica3::QueryDestruction() every tick. Based on the call, the object may be deleted on the other system. - /// Do not call Connection_RM3::QueryReplicaList() - QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION, - - /// Do not call Replica3::QueryConstruction() or Replica3::QueryDestruction() - /// Call Connection_RM3::QueryReplicaList() to determine which objects exist on remote systems - /// This can be faster than QUERY_REPLICA_FOR_CONSTRUCTION and QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION for large worlds - /// See GridSectorizer.h under /Source for code that can help with this - QUERY_CONNECTION_FOR_REPLICA_LIST - }; - - /// \brief Return whether or not downloads to our system should all be processed the same tick (call to RakPeer::Receive() ) - /// \details Normally the system will send ID_REPLICA_MANAGER_DOWNLOAD_STARTED, ID_REPLICA_MANAGER_CONSTRUCTION for all downloaded objects, - /// ID_REPLICA_MANAGER_SERIALIZE for each downloaded object, and lastly ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE. - /// This enables the application to show a downloading splash screen on ID_REPLICA_MANAGER_DOWNLOAD_STARTED, a progress bar, and to close the splash screen and activate all objects on ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE - /// However, if the application was not set up for this then it would result in incomplete objects spread out over time, and cause problems - /// If you return true from QueryGroupDownloadMessages(), then these messages will be returned all in one tick, returned only when the download is complete - /// \note ID_REPLICA_MANAGER_DOWNLOAD_STARTED calls the callback DeserializeOnDownloadStarted() - /// \note ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE calls the callback DeserializeOnDownloadComplete() - virtual bool QueryGroupDownloadMessages(void) const {return false;} - - /// \brief Queries how to get the list of objects that exist on remote systems - /// \details The default of calling QueryConstruction for every known object is easy to use, but not efficient, especially for large worlds where many objects are outside of the player's circle of influence.
      - /// QueryDestruction is also not necessarily useful or efficient, as object destruction tends to happen in known cases, and can be accomplished by calling Replica3::BroadcastDestruction() - /// QueryConstructionMode() allows you to specify more efficient algorithms than the default when overriden. - /// \return How to get the list of objects that exist on the remote system. You should always return the same value for a given connection - virtual ConstructionMode QueryConstructionMode(void) const {return QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION;} - - /// \brief Callback used when QueryConstructionMode() returns QUERY_CONNECTION_FOR_REPLICA_LIST - /// \details This advantage of this callback is if that there are many objects that a particular connection does not have, then we do not have to iterate through those - /// objects calling QueryConstruction() for each of them.
      - ///
      - /// The following code uses a sorted merge sort to quickly find new and deleted objects, given a list of objects we know should exist.
      - ///
      - /// DataStructures::List objectsTheyShouldHave; // You have to fill in this list
      - /// DataStructures::List objectsTheyCurrentlyHave,objectsTheyStillHave,existingReplicasToDestro,newReplicasToCreatey;
      - /// GetConstructedReplicas(objectsTheyCurrentlyHave);
      - /// DataStructures::Multilist::FindIntersection(objectsTheyCurrentlyHave, objectsTheyShouldHave, objectsTheyStillHave, existingReplicasToDestroy, newReplicasToCreate);
      - ///
      - /// See GridSectorizer in the Source directory as a method to find all objects within a certain radius in a fast way.
      - ///
      - /// \param[out] newReplicasToCreate Anything in this list will be created on the remote system - /// \param[out] existingReplicasToDestroy Anything in this list will be destroyed on the remote system - virtual void QueryReplicaList( - DataStructures::List newReplicasToCreate, - DataStructures::List existingReplicasToDestroy) {} - - /// \internal This is used internally - however, you can also call it manually to send a data update for a remote replica.
      - /// \brief Sends over a serialization update for \a replica.
      - /// NetworkID::GetNetworkID() is written automatically, serializationData is the object data.
      - /// \param[in] replica Which replica to serialize - /// \param[in] serializationData Serialized object data - /// \param[in] timestamp 0 means no timestamp. Otherwise message is prepended with ID_TIMESTAMP - /// \param[in] sendParameters Parameters on how to send - /// \param[in] rakPeer Instance of RakPeerInterface to send on - /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() - virtual SendSerializeIfChangedResult SendSerialize(RakNet::Replica3 *replica, bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::BitStream serializationData[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::Time timestamp, PRO sendParameters[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::RakPeerInterface *rakPeer, unsigned char worldId); - - /// \internal - /// \details Calls Connection_RM3::SendSerialize() if Replica3::Serialize() returns a different result than what is contained in \a lastSerializationResult.
      - /// Used by autoserialization in Connection_RM3::OnAutoserializeInterval() - /// \param[in] queryToSerializeIndex Index into queryToSerializeReplicaList for whichever replica this is - /// \param[in] sp Controlling parameters over the serialization - /// \param[in] rakPeer Instance of RakPeerInterface to send on - /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() - virtual SendSerializeIfChangedResult SendSerializeIfChanged(unsigned int queryToSerializeIndex, SerializeParameters *sp, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager); - - /// \internal - /// \brief Given a list of objects that were created and destroyed, serialize and send them to another system. - /// \param[in] newObjects Objects to serialize construction - /// \param[in] deletedObjects Objects to serialize destruction - /// \param[in] sendParameters Controlling parameters over the serialization - /// \param[in] rakPeer Instance of RakPeerInterface to send on - /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() - /// \param[in] replicaManager3 ReplicaManager3 instance - virtual void SendConstruction(DataStructures::List &newObjects, DataStructures::List &deletedObjects, PRO sendParameters, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager3); - - /// \internal - /// Remove from \a newObjectsIn objects that already exist and save to \a newObjectsOut - /// Remove from \a deletedObjectsIn objects that do not exist, and save to \a deletedObjectsOut - void CullUniqueNewAndDeletedObjects(DataStructures::List &newObjectsIn, - DataStructures::List &deletedObjectsIn, - DataStructures::List &newObjectsOut, - DataStructures::List &deletedObjectsOut); - - /// \internal - void SendValidation(RakNet::RakPeerInterface *rakPeer, unsigned char worldId); - - /// \internal - void AutoConstructByQuery(ReplicaManager3 *replicaManager3); - - - // Internal - does the other system have this connection too? Validated means we can now use it - bool isValidated; - // Internal - Used to see if we should send download started - bool isFirstConstruction; - - static int Replica3LSRComp( Replica3 * const &replica3, LastSerializationResult * const &data ); - -protected: - - SystemAddress systemAddress; - RakNetGUID guid; - - /* - Operations: - - Locally reference a new replica: - Add to queryToConstructReplicaList for all objects - - Add all objects to queryToConstructReplicaList - - Download: - Add to constructedReplicaList for connection that send the object to us - Add to queryToSerializeReplicaList for connection that send the object to us - Add to queryToConstructReplicaList for all other connections - - Never construct for this connection: - Remove from queryToConstructReplicaList - - Construct to this connection - Remove from queryToConstructReplicaList - Add to constructedReplicaList for this connection - Add to queryToSerializeReplicaList for this connection - - Serialize: - Iterate through queryToSerializeReplicaList - - Never serialize for this connection - Remove from queryToSerializeReplicaList - - Reference (this system has this object already) - Remove from queryToConstructReplicaList - Add to constructedReplicaList for this connection - Add to queryToSerializeReplicaList for this connection - - Downloaded an existing object - if replica is in queryToConstructReplicaList, OnConstructToThisConnection() - else ignore - - Send destruction from query - Remove from queryToDestructReplicaList - Remove from queryToSerializeReplicaList - Remove from constructedReplicaList - Add to queryToConstructReplicaList - - Do not query destruction again - Remove from queryToDestructReplicaList - */ - void OnLocalReference(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnDereference(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnDownloadFromThisSystem(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnDownloadFromOtherSystem(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnNeverConstruct(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); - void OnConstructToThisConnection(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); - void OnConstructToThisConnection(Replica3 *replica, ReplicaManager3 *replicaManager); - void OnNeverSerialize(unsigned int queryToSerializeIndex, ReplicaManager3 *replicaManager); - void OnReplicaAlreadyExists(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); - void OnDownloadExisting(Replica3* replica3, ReplicaManager3 *replicaManager); - void OnSendDestructionFromQuery(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager); - void OnDoNotQueryDestruction(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager); - void ValidateLists(ReplicaManager3 *replicaManager) const; - void SendSerializeHeader(RakNet::Replica3 *replica, RakNet::Time timestamp, RakNet::BitStream *bs, unsigned char worldId); - - // The list of objects that our local system and this remote system both have - // Either we sent this object to them, or they sent this object to us - // A given Replica can be either in queryToConstructReplicaList or constructedReplicaList but not both at the same time - DataStructures::OrderedList constructedReplicaList; - - // Objects that we have, but this system does not, and we will query each tick to see if it should be sent to them - // If we do send it to them, the replica is moved to constructedReplicaList - // A given Replica can be either in queryToConstructReplicaList or constructedReplicaList but not both at the same time - DataStructures::List queryToConstructReplicaList; - - // Objects that this system has constructed are added at the same time to queryToSerializeReplicaList - // This list is used to serialize all objects that this system has to this connection - DataStructures::List queryToSerializeReplicaList; - - // Objects that are constructed on this system are also queried if they should be destroyed to this system - DataStructures::List queryToDestructReplicaList; - - // Working lists - DataStructures::List constructedReplicasCulled, destroyedReplicasCulled; - - // This is used if QueryGroupDownloadMessages() returns true when ID_REPLICA_MANAGER_DOWNLOAD_STARTED arrives - // Packets will be gathered and not returned until ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE arrives - bool groupConstructionAndSerialize; - DataStructures::Queue downloadGroup; - void ClearDownloadGroup(RakPeerInterface *rakPeerInterface); - - friend class ReplicaManager3; -private: - Connection_RM3() {}; - - ConstructionMode constructionMode; -}; - -/// \brief Return codes for Connection_RM3::GetConstructionState() and Replica3::QueryConstruction() -/// \details Indicates what state the object should be in for the remote system -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3ConstructionState -{ - /// This object should exist on the remote system. Send a construction message if necessary - /// If the NetworkID is already in use, it will not do anything - /// If it is not in use, it will create the object, and then call DeserializeConstruction - RM3CS_SEND_CONSTRUCTION, - - /// This object should exist on the remote system. - /// The other system already has the object, and the object will never be deleted. - /// This is true of objects that are loaded with the level, for example. - /// Treat it as if it existed, without sending a construction message. - /// Will call Serialize() and SerializeConstructionExisting() to the object on the remote system - RM3CS_ALREADY_EXISTS_REMOTELY, - - /// Same as RM3CS_ALREADY_EXISTS_REMOTELY but does not call SerializeConstructionExisting() - RM3CS_ALREADY_EXISTS_REMOTELY_DO_NOT_CONSTRUCT, - - /// This object will never be sent to the target system - /// This object will never be serialized from this system to the target system - RM3CS_NEVER_CONSTRUCT, - - /// Don't do anything this tick. Will query again next tick - RM3CS_NO_ACTION, - - /// Max enum - RM3CS_MAX, -}; - -/// If this object already exists for this system, should it be removed? -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3DestructionState -{ - /// This object should not exist on the remote system. Send a destruction message if necessary. - RM3DS_SEND_DESTRUCTION, - - /// This object will never be destroyed by a per-tick query. Don't call again - RM3DS_DO_NOT_QUERY_DESTRUCTION, - - /// Don't do anything this tick. Will query again next tick - RM3DS_NO_ACTION, - - /// Max enum - RM3DS_MAX, -}; - -/// Return codes when constructing an object -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3SerializationResult -{ - /// This object serializes identically no matter who we send to - /// We also send it to every connection (broadcast). - /// Efficient for memory, speed, and bandwidth but only if the object is always broadcast identically. - RM3SR_BROADCAST_IDENTICALLY, - - /// Same as RM3SR_BROADCAST_IDENTICALLY, but assume the object needs to be serialized, do not check with a memcmp - /// Assume the object changed, and serialize it - /// Use this if you know exactly when your object needs to change. Can be faster than RM3SR_BROADCAST_IDENTICALLY. - /// An example of this is if every member variable has an accessor, changing a member sets a flag, and you check that flag in Replica3::QuerySerialization() - /// The opposite of this is RM3SR_DO_NOT_SERIALIZE, in case the object did not change - RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION, - - /// Either this object serializes differently depending on who we send to or we send it to some systems and not others. - /// Inefficient for memory and speed, but efficient for bandwidth - /// However, if you don't know what to return, return this - RM3SR_SERIALIZED_UNIQUELY, - - /// Do not compare against last sent value. Just send even if the data is the same as the last tick - /// If the data is always changing anyway, or you want to send unreliably, this is a good method of serialization - /// Can send unique data per connection if desired. If same data is sent to all connections, use RM3SR_SERIALIZED_ALWAYS_IDENTICALLY for even better performance - /// Efficient for memory and speed, but not necessarily bandwidth - RM3SR_SERIALIZED_ALWAYS, - - /// Even faster than RM3SR_SERIALIZED_ALWAYS - /// Serialize() will only be called for the first system. The remaining systems will get the same data as the first system. - RM3SR_SERIALIZED_ALWAYS_IDENTICALLY, - - /// Do not serialize this object this tick, for this connection. Will query again next autoserialize timer - RM3SR_DO_NOT_SERIALIZE, - - /// Never serialize this object for this connection - /// Useful for objects that are downloaded, and never change again - /// Efficient - RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION, - - /// Max enum - RM3SR_MAX, -}; - -/// First pass at topology to see if an object should be serialized -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3QuerySerializationResult -{ - /// Call Serialize() to see if this object should be serializable for this connection - RM3QSR_CALL_SERIALIZE, - /// Do not call Serialize() this tick to see if this object should be serializable for this connection - RM3QSR_DO_NOT_CALL_SERIALIZE, - /// Never call Serialize() for this object and connection. This system will not serialize this object for this topology - RM3QSR_NEVER_CALL_SERIALIZE, - /// Max enum - RM3QSR_MAX, -}; - -/// \ingroup REPLICA_MANAGER_GROUP3 -enum RM3ActionOnPopConnection -{ - RM3AOPC_DO_NOTHING, - RM3AOPC_DELETE_REPLICA, - RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION, - RM3AOPC_MAX, -}; - -/// \ingroup REPLICA_MANAGER_GROUP3 -/// Used for Replica3::QueryConstruction_PeerToPeer() and Replica3::QuerySerialization_PeerToPeer() to describe how the object replicates between hosts -enum Replica3P2PMode -{ - /// The Replica3 instance is constructed and serialized by one system only. - /// Example: Your avatar. No other player serializes or can create your avatar. - R3P2PM_SINGLE_OWNER, - /// The Replica3 instance is constructed and/or serialized by different systems - /// This system is currently in charge of construction and/or serialization - /// Example: A pickup. When an avatar holds it, that avatar controls it. When it is on the ground, the host controls it. - R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE, - /// The Replica3 instance is constructed and/or serialized by different systems - /// Another system is in charge of construction and/or serialization, but this system may be in charge at a later time - /// Example: A pickup held by another player. That player sends creation of that object to new connections, and serializes it until it is dropped. - R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE, -}; - -/// \brief Base class for your replicated objects for the ReplicaManager3 system. -/// \details To use, derive your class, or a member of your class, from Replica3.
      -/// \ingroup REPLICA_MANAGER_GROUP3 -class RAK_DLL_EXPORT Replica3 : public NetworkIDObject -{ -public: - Replica3(); - - /// Before deleting a local instance of Replica3, call Replica3::BroadcastDestruction() for the deletion notification to go out on the network. - /// It is not necessary to call ReplicaManager3::Dereference(), as this happens automatically in the destructor - virtual ~Replica3(); - - /// \brief Write a unique identifer that can be read on a remote system to create an object of this same class. - /// \details The value written to \a allocationIdBitstream will be passed to Connection_RM3::AllocReplica().
      - /// Sample implementation:
      - /// {allocationIdBitstream->Write(RakNet::RakString("Soldier");}
      - /// \param[out] allocationIdBitstream Bitstream for the user to write to, to identify this class - virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const=0; - - /// \brief Ask if this object, which does not exist on \a destinationConnection should (now) be sent to that system. - /// \details If ReplicaManager3::QueryConstructionMode() returns QUERY_CONNECTION_FOR_REPLICA_LIST or QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION (default), - /// then QueyrConstruction() is called once per tick from ReplicaManager3::Update() to determine if an object should exist on a given system.
      - /// Based on the return value, a network message may be sent to the other system to create the object.
      - /// If QueryConstructionMode() is overriden to return QUERY_CONNECTION_FOR_REPLICA_LIST, this function is unused.
      - /// \note Defaults are provided: QueryConstruction_PeerToPeer(), QueryConstruction_ServerConstruction(), QueryConstruction_ClientConstruction(). Return one of these functions for a working default for the relevant topology. - /// \param[in] destinationConnection Which system we will send to - /// \param[in] replicaManager3 Plugin instance for this Replica3 - /// \return What action to take - virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3)=0; - - /// \brief Ask if this object, which does exist on \a destinationConnection should be removed from the remote system - /// \details If ReplicaManager3::QueryConstructionMode() returns QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION (default), - /// then QueryDestruction() is called once per tick from ReplicaManager3::Update() to determine if an object that exists on a remote system should be destroyed for a given system.
      - /// Based on the return value, a network message may be sent to the other system to destroy the object.
      - /// Note that you can also destroy objects with BroadcastDestruction(), so this function is not useful unless you plan to delete objects for only a particular connection.
      - /// If QueryConstructionMode() is overriden to return QUERY_CONNECTION_FOR_REPLICA_LIST, this function is unused.
      - /// \param[in] destinationConnection Which system we will send to - /// \param[in] replicaManager3 Plugin instance for this Replica3 - /// \return What action to take. Only RM3CS_SEND_DESTRUCTION does anything at this time. - virtual RM3DestructionState QueryDestruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {(void) destinationConnection; (void) replicaManager3; return RM3DS_DO_NOT_QUERY_DESTRUCTION;} - - /// \brief We're about to call DeserializeConstruction() on this Replica3. If QueryRemoteConstruction() returns false, this object is deleted instead. - /// \details By default, QueryRemoteConstruction_ServerConstruction() does not allow clients to create objects. The client will get Replica3::DeserializeConstructionRequestRejected().
      - /// If you want the client to be able to potentially create objects for client/server, override accordingly.
      - /// Other variants of QueryRemoteConstruction_* just return true. - /// \note Defaults are provided: QueryRemoteConstruction_PeerToPeer(), QueryRemoteConstruction_ServerConstruction(), QueryRemoteConstruction_ClientConstruction(). Return one of these functions for a working default for the relevant topology. - /// \param[in] sourceConnection Which system sent us the object creation request message. - /// \return True to allow the object to pass onto DeserializeConstruction() (where it may also be rejected), false to immediately reject the remote construction request - virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection)=0; - - /// \brief We got a message from a connection to destroy this replica - /// Return true to automatically relay the destruction message to all our other connections - /// For a client in client/server, it does not matter what this funtion returns - /// For a server in client/server, this should normally return true - /// For a peer in peer to peer, you can normally return false since the original destroying peer would have told all other peers about the destruction - /// If a system gets a destruction command for an object that was already destroyed, the destruction message is ignored - virtual bool QueryRelayDestruction(Connection_RM3 *sourceConnection) const {(void) sourceConnection; return true;} - - /// \brief Write data to be sent only when the object is constructed on a remote system. - /// \details SerializeConstruction is used to write out data that you need to create this object in the context of your game, such as health, score, name. Use it for data you only need to send when the object is created.
      - /// After SerializeConstruction() is called, Serialize() will be called immediately thereafter. However, they are sent in different messages, so Serialize() may arrive a later frame than SerializeConstruction() - /// For that reason, the object should be valid after a call to DeserializeConstruction() for at least a short time.
      - /// \note The object's NetworkID and allocation id are handled by the system automatically, you do not need to write these values to \a constructionBitstream - /// \param[out] constructionBitstream Destination bitstream to write your data to - /// \param[in] destinationConnection System that will receive this network message. - virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection)=0; - - /// \brief Read data written by Replica3::SerializeConstruction() - /// \details Reads whatever data was written to \a constructionBitstream in Replica3::SerializeConstruction() - /// \param[out] constructionBitstream Bitstream written to in Replica3::SerializeConstruction() - /// \param[in] sourceConnection System that sent us this network message. - /// \return true to accept construction of the object. false to reject, in which case the object will be deleted via Replica3::DeallocReplica() - virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection)=0; - - /// Same as SerializeConstruction(), but for an object that already exists on the remote system. - /// Used if you return RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction - virtual void SerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;}; - - /// Same as DeserializeConstruction(), but for an object that already exists on the remote system. - /// Used if you return RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction - virtual void DeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;}; - - /// \brief Write extra data to send with the object deletion event, if desired - /// \details Replica3::SerializeDestruction() will be called to write any object destruction specific data you want to send with this event. - /// \a destructionBitstream can be read in DeserializeDestruction() - /// \param[out] destructionBitstream Bitstream for you to write to - /// \param[in] destinationConnection System that will receive this network message. - virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection)=0; - - /// \brief Read data written by Replica3::SerializeDestruction() - /// \details Return true to delete the object. BroadcastDestruction() will be called automatically, followed by ReplicaManager3::Dereference.
      - /// Return false to not delete it. If you delete it at a later point, you are responsible for calling BroadcastDestruction() yourself. - virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection)=0; - - /// \brief The system is asking what to do with this replica when the connection is dropped - /// \details Return QueryActionOnPopConnection_Client, QueryActionOnPopConnection_Server, or QueryActionOnPopConnection_PeerToPeer - virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const=0; - - /// Notification called for each of our replicas when a connection is popped - virtual void OnPoppedConnection(RakNet::Connection_RM3 *droppedConnection) {(void) droppedConnection;} - - /// \brief Override with {delete this;} - /// \details - ///
        - ///
      1. Got a remote message to delete this object which passed DeserializeDestruction(), OR - ///
      2. ReplicaManager3::SetAutoManageConnections() was called autoDestroy true (which is the default setting), and a remote system that owns this object disconnected) OR - /// <\OL> - ///
        - /// Override with {delete this;} to actually delete the object (and any other processing you wish).
        - /// If you don't want to delete the object, just do nothing, however, the system will not know this so you are responsible for deleting it yoruself later.
        - /// destructionBitstream may be 0 if the object was deleted locally - virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection)=0; - - /// \brief Implement with QuerySerialization_ClientSerializable(), QuerySerialization_ServerSerializable(), or QuerySerialization_PeerToPeer() - /// \details QuerySerialization() is a first pass query to check if a given object should serializable to a given system. The intent is that the user implements with one of the defaults for client, server, or peer to peer.
        - /// Without this function, a careless implementation would serialize an object anytime it changed to all systems. This would give you feedback loops as the sender gets the same message back from the recipient it just sent to.
        - /// If more than one system can serialize the same object then you will need to override to return true, and control the serialization result from Replica3::Serialize(). Be careful not to send back the same data to the system that just sent to you! - /// \return True to allow calling Replica3::Serialize() for this connection, false to not call. - virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection)=0; - - /// \brief Called for each replica owned by the user, once per Serialization tick, before Serialize() is called. - /// If you want to do some kind of operation on the Replica objects that you own, just before Serialization(), then overload this function - virtual void OnUserReplicaPreSerializeTick(void) {} - - /// \brief Serialize our class to a bitstream - /// \details User should implement this function to write the contents of this class to SerializationParamters::serializationBitstream.
        - /// If data only needs to be written once, you can write it to SerializeConstruction() instead for efficiency.
        - /// Transmitted over the network if it changed from the last time we called Serialize().
        - /// Called every time the time interval to ReplicaManager3::SetAutoSerializeInterval() elapses and ReplicaManager3::Update is subsequently called. - /// \param[in/out] serializeParameters Parameters controlling the serialization, including destination bitstream to write to - /// \return Whether to serialize, and if so, how to optimize the results - virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters)=0; - - /// \brief Called when the class is actually transmitted via Serialize() - /// \details Use to track how much bandwidth this class it taking - virtual void OnSerializeTransmission(RakNet::BitStream *bitStream, const SystemAddress &systemAddress) {(void) bitStream; (void) systemAddress; } - - /// \brief Read what was written in Serialize() - /// \details Reads the contents of the class from SerializationParamters::serializationBitstream.
        - /// Called whenever Serialize() is called with different data from the last send. - /// \param[in] serializationBitstream Bitstream passed to Serialize() - /// \param[in] timeStamp 0 if unused, else contains the time the message originated on the remote system - /// \param[in] sourceConnection Which system sent to us - virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters)=0; - - /// \brief Called after SerializeConstruction completes for all objects in a given update tick.
        - /// Writes to PostDeserializeConstruction(), which is called after all objects are created for a given Construction tick(). - /// Override to send data to PostDeserializeConstruction(), such as the NetworkID of other objects to resolve pointers to - virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;} - - /// Called after DeserializeConstruction completes for all objects in a given update tick.
        - /// This is used to resolve dependency chains, where two objects would refer to each other in DeserializeConstruction, yet one had not been constructed yet - /// In PostDeserializeConstruction(), you know that all objects have already been created, so can resolve NetworkIDs to pointers safely. - /// You can also use it to trigger some sort of event when you know the object has completed deserialization. - /// \param[in] constructionBitstream BitStream written in PostSerializeConstruction() - /// \param[in] sourceConnection System that sent us this network message. - virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;} - - /// Same as PostSerializeConstruction(), but for objects that returned RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction - virtual void PostSerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;} - - /// Same as PostDeserializeConstruction(), but for objects that returned RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction - virtual void PostDeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;} - - /// Called after DeserializeDestruction completes for the object successfully, but obviously before the object is deleted.
        - /// Override to trigger some sort of event when you know the object has completed destruction. - /// \param[in] sourceConnection System that sent us this network message. - virtual void PreDestruction(RakNet::Connection_RM3 *sourceConnection) {(void) sourceConnection;} - - /// \brief Default call for QueryConstruction(). - /// \details Both the client and the server is allowed to create this object. The network topology is client/server - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual RM3ConstructionState QueryConstruction_ClientConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); - /// Default call for QueryRemoteConstruction(). - /// \details Both the client and the server is allowed to create this object. The network topology is client/server - /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual bool QueryRemoteConstruction_ClientConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer); - - /// \brief Default call for QueryConstruction(). - /// \details Only the server is allowed to create this object. The network topology is client/server - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual RM3ConstructionState QueryConstruction_ServerConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); - /// \brief Default call for QueryRemoteConstruction(). Allow the server to create this object, but not the client. - /// \details Only the server is allowed to create this object. The network topology is client/server - /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual bool QueryRemoteConstruction_ServerConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer); - - /// \brief Default call for QueryConstruction(). - /// \details All clients are allowed to create all objects. The object is not relayed when remotely created - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] p2pMode If controlled only by this system ever, pass R3P2PM_SINGLE_OWNER. Otherwise pass R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE or R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE - virtual RM3ConstructionState QueryConstruction_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode=R3P2PM_SINGLE_OWNER); - /// \brief Default call for QueryRemoteConstruction(). - /// \details All clients are allowed to create all objects. The object is not relayed when remotely created - /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() - virtual bool QueryRemoteConstruction_PeerToPeer(RakNet::Connection_RM3 *sourceConnection); - - /// \brief Default call for QuerySerialization(). - /// \details Use if the values you are serializing are generated by the client that owns the object. The serialization will be relayed through the server to the other clients. - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual RakNet::RM3QuerySerializationResult QuerySerialization_ClientSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); - /// \brief Default call for QuerySerialization(). - /// \details Use if the values you are serializing are generated only by the server. The serialization will be sent to all clients, but the clients will not send back to the server. - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] isThisTheServer True if this system is the server, false if not. - virtual RakNet::RM3QuerySerializationResult QuerySerialization_ServerSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); - /// \brief Default call for QuerySerialization(). - /// \details Use if the values you are serializing are on a peer to peer network. The peer that owns the object will send to all. Remote peers will not send. - /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() - /// \param[in] p2pMode If controlled only by this system ever, pass R3P2PM_SINGLE_OWNER. Otherwise pass R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE or R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE - virtual RakNet::RM3QuerySerializationResult QuerySerialization_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode=R3P2PM_SINGLE_OWNER); - - /// Default: If we are a client, and the connection is lost, delete the server's objects - virtual RM3ActionOnPopConnection QueryActionOnPopConnection_Client(RakNet::Connection_RM3 *droppedConnection) const; - /// Default: If we are a client, and the connection is lost, delete the client's objects and broadcast the destruction - virtual RM3ActionOnPopConnection QueryActionOnPopConnection_Server(RakNet::Connection_RM3 *droppedConnection) const; - /// Default: If we are a peer, and the connection is lost, delete the peer's objects - virtual RM3ActionOnPopConnection QueryActionOnPopConnection_PeerToPeer(RakNet::Connection_RM3 *droppedConnection) const; - - /// Call to send a network message to delete this object on other systems.
        - /// Call it before deleting the object - virtual void BroadcastDestruction(void); - - /// creatingSystemGUID is set the first time Reference() is called, or if we get the object from another system - /// \return System that originally created this object - RakNetGUID GetCreatingSystemGUID(void) const; - - /// \return If ReplicaManager3::Reference() was called on this object. - bool WasReferenced(void) const {return replicaManager!=0;} - - /// GUID of the system that first called Reference() on this object. - /// Transmitted automatically when the object is constructed - RakNetGUID creatingSystemGUID; - /// GUID of the system that caused the item to send a deletion command over the network - RakNetGUID deletingSystemGUID; - - /// \internal - /// ReplicaManager3 plugin associated with this object - ReplicaManager3 *replicaManager; - - LastSerializationResultBS lastSentSerialization; - RakNet::Time whenLastSerialized; - bool forceSendUntilNextUpdate; -}; - -/// \brief Use Replica3 through composition instead of inheritance by containing an instance of this templated class -/// Calls to parent class for all functions -/// Parent class must still define and functions though! -/// \pre Parent class must call SetParent() on this object -template -class RAK_DLL_EXPORT Replica3Composite : public Replica3 -{ -protected: - parent_type *r3CompositeOwner; -public: - void SetCompositeOwner(parent_type *p) {r3CompositeOwner=p;} - parent_type* GetCompositeOwner(void) const {return r3CompositeOwner;}; - virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const {r3CompositeOwner->WriteAllocationID(destinationConnection, allocationIdBitstream);} - virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {return r3CompositeOwner->QueryConstruction(destinationConnection, replicaManager3);} - virtual RM3DestructionState QueryDestruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {return r3CompositeOwner->QueryDestruction(destinationConnection, replicaManager3);} - virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->QueryRemoteConstruction(sourceConnection);} - virtual bool QueryRelayDestruction(Connection_RM3 *sourceConnection) const {return r3CompositeOwner->QueryRelayDestruction(sourceConnection);} - virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeConstruction(constructionBitstream, destinationConnection);} - virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->DeserializeConstruction(constructionBitstream, sourceConnection);} - virtual void SerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeConstructionExisting(constructionBitstream, destinationConnection);} - virtual void DeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->DeserializeConstructionExisting(constructionBitstream, sourceConnection);} - virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeDestruction(destructionBitstream, destinationConnection);} - virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->DeserializeDestruction(destructionBitstream, sourceConnection);} - virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {return r3CompositeOwner->QueryActionOnPopConnection(droppedConnection);} - virtual void OnPoppedConnection(RakNet::Connection_RM3 *droppedConnection) {r3CompositeOwner->OnPoppedConnection(droppedConnection);} - virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->DeallocReplica(sourceConnection);} - virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {return r3CompositeOwner->QuerySerialization(destinationConnection);} - virtual void OnUserReplicaPreSerializeTick(void) {r3CompositeOwner->OnUserReplicaPreSerializeTick();} - virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters) {return r3CompositeOwner->Serialize(serializeParameters);} - virtual void OnSerializeTransmission(RakNet::BitStream *bitStream, const SystemAddress &systemAddress) {r3CompositeOwner->OnSerializeTransmission(bitStream, systemAddress);} - virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters) {r3CompositeOwner->Deserialize(deserializeParameters);} - virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->PostSerializeConstruction(constructionBitstream, destinationConnection);} - virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PostDeserializeConstruction(constructionBitstream, sourceConnection);} - virtual void PostSerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->PostSerializeConstructionExisting(constructionBitstream, destinationConnection);} - virtual void PostDeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PostDeserializeConstructionExisting(constructionBitstream, sourceConnection);} - virtual void PreDestruction(RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PreDestruction(sourceConnection);} -}; - -} // namespace RakNet - - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains the third iteration of the ReplicaManager class. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_ReplicaManager3==1 + +#ifndef __REPLICA_MANAGER_3 +#define __REPLICA_MANAGER_3 + +#include "RakNetTypes.h" +#include "RakNetTime.h" +#include "BitStream.h" +#include "PacketPriority.h" +#include "PluginInterface2.h" +#include "NetworkIDObject.h" +#include "DS_OrderedList.h" +#include "DS_Queue.h" + +/// \defgroup REPLICA_MANAGER_GROUP3 ReplicaManager3 +/// \brief Third implementation of object replication +/// \details +/// \ingroup REPLICA_MANAGER_GROUP + +namespace RakNet +{ +class Connection_RM3; +class Replica3; + + +/// \internal +/// \ingroup REPLICA_MANAGER_GROUP3 +struct PRO +{ + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketPriority(). + PacketPriority priority; + + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketReliability(). + PacketReliability reliability; + + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultOrderingChannel(). + char orderingChannel; + + /// Passed to RakPeerInterface::Send(). Defaults to 0. + uint32_t sendReceipt; + + bool operator==( const PRO& right ) const; + bool operator!=( const PRO& right ) const; +}; + + +/// \brief System to help automate game object construction, destruction, and serialization +/// \details ReplicaManager3 tracks your game objects and automates the networking for replicating them across the network
        +/// As objects are created, destroyed, or serialized differently, those changes are pushed out to other systems.
        +/// To use:
        +///
          +///
        1. Derive from Connection_RM3 and implement Connection_RM3::AllocReplica(). This is a factory function where given a user-supplied identifier for a class (such as name) return an instance of that class. Should be able to return any networked object in your game. +///
        2. Derive from ReplicaManager3 and implement AllocConnection() and DeallocConnection() to return the class you created in step 1. +///
        3. Derive your networked game objects from Replica3. All pure virtuals have to be implemented, however defaults are provided for Replica3::QueryConstruction(), Replica3::QueryRemoteConstruction(), and Replica3::QuerySerialization() depending on your network architecture. +///
        4. When a new game object is created on the local system, pass it to ReplicaManager3::Reference(). +///
        5. When a game object is destroyed on the local system, and you want other systems to know about it, call Replica3::BroadcastDestruction() +///
        +///
        +/// At this point, all new connections will automatically download, get construction messages, get destruction messages, and update serialization automatically. +/// \ingroup REPLICA_MANAGER_GROUP3 +class RAK_DLL_EXPORT ReplicaManager3 : public PluginInterface2 +{ +public: + ReplicaManager3(); + virtual ~ReplicaManager3(); + + /// \brief Implement to return a game specific derivation of Connection_RM3 + /// \details The connection object represents a remote system connected to you that is using the ReplicaManager3 system.
        + /// It has functions to perform operations per-connection.
        + /// AllocConnection() and DeallocConnection() are factory functions to create and destroy instances of the connection object.
        + /// It is used if autoCreate is true via SetAutoManageConnections() (true by default). Otherwise, the function is not called, and you will have to call PushConnection() manually
        + /// \note If you do not want a new network connection to immediately download game objects, SetAutoManageConnections() and PushConnection() are how you do this. + /// \sa SetAutoManageConnections() + /// \param[in] systemAddress Address of the system you are adding + /// \param[in] rakNetGUID GUID of the system you are adding. See Packet::rakNetGUID or RakPeerInterface::GetGUIDFromSystemAddress() + /// \return The new connection instance. + virtual Connection_RM3* AllocConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID) const=0; + + /// \brief Implement to destroy a class instanced returned by AllocConnection() + /// \details Most likely just implement as {delete connection;}
        + /// It is used if autoDestroy is true via SetAutoManageConnections() (true by default). Otherwise, the function is not called and you would then be responsible for deleting your own connection objects. + /// \param[in] connection The pointer instance to delete + virtual void DeallocConnection(Connection_RM3 *connection) const=0; + + /// \brief Enable or disable automatically assigning connections to new instances of Connection_RM3 + /// \details ReplicaManager3 can automatically create and/or destroy Connection_RM3 as systems connect or disconnect from RakPeerInterface.
        + /// By default this is on, to make the system easier to learn and setup.
        + /// If you don't want all connections to take part in the game, or you want to delay when a connection downloads the game, set \a autoCreate to false.
        + /// If you want to delay deleting a connection that has dropped, set \a autoDestroy to false. If you do this, then you must call PopConnection() to remove that connection from being internally tracked. You'll also have to delete the connection instance on your own.
        + /// \param[in] autoCreate Automatically call ReplicaManager3::AllocConnection() for each new connection. Defaults to true. + /// \param[in] autoDestroy Automatically call ReplicaManager3::DeallocConnection() for each dropped connection. Defaults to true. + void SetAutoManageConnections(bool autoCreate, bool autoDestroy); + + /// \brief Track a new Connection_RM3 instance + /// \details If \a autoCreate is false for SetAutoManageConnections(), then you need this function to add new instances of Connection_RM3 yourself.
        + /// You don't need to track this pointer yourself, you can get it with GetConnectionAtIndex(), GetConnectionByGUID(), or GetConnectionBySystemAddress().
        + /// \param[in] newConnection The new connection instance to track. + bool PushConnection(RakNet::Connection_RM3 *newConnection); + + /// \brief Stop tracking a connection + /// \details On call, for each replica returned by GetReplicasCreatedByGuid(), QueryActionOnPopConnection() will be called. Depending on the return value, this may delete the corresponding replica.
        + /// If autoDestroy is true in the call to SetAutoManageConnections() (true by default) then this is called automatically when the connection is lost. In that case, the returned connection instance is deleted.
        + /// \param[in] guid of the connection to get. Passed to ReplicaManager3::AllocConnection() originally. + RakNet::Connection_RM3 * PopConnection(RakNetGUID guid); + + /// \brief Adds a replicated object to the system. + /// \details Anytime you create a new object that derives from Replica3, and you want ReplicaManager3 to use it, pass it to Reference().
        + /// Remote systems already connected will potentially download this object the next time ReplicaManager3::Update() is called, which happens every time you call RakPeerInterface::Receive().
        + /// You can also call ReplicaManager3::Update() manually to send referenced objects right away + /// \param[in] replica3 The object to start tracking + void Reference(RakNet::Replica3 *replica3); + + /// \brief Removes a replicated object from the system. + /// \details The object is not deallocated, it is up to the caller to do so.
        + /// This is called automatically from the destructor of Replica3, so you don't need to call it manually unless you want to stop tracking an object before it is destroyed. + /// \param[in] replica3 The object to stop tracking + void Dereference(RakNet::Replica3 *replica3); + + /// \brief Removes multiple replicated objects from the system. + /// \details Same as Dereference(), but for a list of objects.
        + /// Useful with the lists returned by GetReplicasCreatedByGuid(), GetReplicasCreatedByMe(), or GetReferencedReplicaList().
        + /// \param[in] replicaListIn List of objects + void DereferenceList(DataStructures::List &replicaListIn); + + /// \brief Returns all objects originally created by a particular system + /// \details Originally created is defined as the value of Replica3::creatingSystemGUID, which is automatically assigned in ReplicaManager3::Reference().
        + /// You do not have to be directly connected to that system to get the objects originally created by that system.
        + /// \param[in] guid GUID of the system we are referring to. Originally passed as the \a guid parameter to ReplicaManager3::AllocConnection() + /// \param[out] List of Replica3 instances to be returned + void GetReplicasCreatedByGuid(RakNetGUID guid, DataStructures::List &replicaListOut); + + /// \brief Returns all objects originally created by your system + /// \details Calls GetReplicasCreatedByGuid() for your own system guid. + /// \param[out] List of Replica3 instances to be returned + void GetReplicasCreatedByMe(DataStructures::List &replicaListOut); + + /// \brief Returns the entire list of Replicas that we know about. + /// \details This is all Replica3 instances passed to Reference, as well as instances we downloaded and created via Connection_RM3::AllocReference() + /// \param[out] List of Replica3 instances to be returned + void GetReferencedReplicaList(DataStructures::List &replicaListOut); + + /// \brief Returns the number of replicas known about + /// \details Returns the size of the list that would be returned by GetReferencedReplicaList() + /// \return How many replica objects are in the list of replica objects + unsigned GetReplicaCount(void) const; + + /// \brief Returns a replica by index + /// \details Returns one of the items in the list that would be returned by GetReferencedReplicaList() + /// \param[in] index An index, from 0 to GetReplicaCount()-1. + /// \return A Replica3 instance + Replica3 *GetReplicaAtIndex(unsigned index); + + /// \brief Returns the number of connections + /// \details Returns the number of connections added with ReplicaManager3::PushConnection(), minus the number removed with ReplicaManager3::PopConnection() + /// \return The number of registered connections + unsigned int GetConnectionCount(void) const; + + /// \brief Returns a connection pointer previously added with PushConnection() + /// \param[in] index An index, from 0 to GetConnectionCount()-1. + /// \return A Connection_RM3 pointer + Connection_RM3* GetConnectionAtIndex(unsigned index) const; + + /// \brief Returns a connection pointer previously added with PushConnection() + /// \param[in] sa The system address of the connection to return + /// \return A Connection_RM3 pointer, or 0 if not found + Connection_RM3* GetConnectionBySystemAddress(const SystemAddress &sa) const; + + /// \brief Returns a connection pointer previously added with PushConnection.() + /// \param[in] guid The guid of the connection to return + /// \return A Connection_RM3 pointer, or 0 if not found + Connection_RM3* GetConnectionByGUID(RakNetGUID guid) const; + + /// \param[in] Default ordering channel to use for object creation, destruction, and serializations + void SetDefaultOrderingChannel(char def); + + /// \param[in] Default packet priority to use for object creation, destruction, and serializations + void SetDefaultPacketPriority(PacketPriority def); + + /// \param[in] Default packet reliability to use for object creation, destruction, and serializations + void SetDefaultPacketReliability(PacketReliability def); + + /// \details Every \a intervalMS milliseconds, Connection_RM3::OnAutoserializeInterval() will be called.
        + /// Defaults to 30.
        + /// Pass with <0 to disable. Pass 0 to Serialize() every time RakPeer::Recieve() is called
        + /// If you want to control the update interval with more granularity, use the return values from Replica3::Serialize().
        + /// \param[in] intervalMS How frequently to autoserialize all objects. This controls the maximum number of game object updates per second. + void SetAutoSerializeInterval(RakNet::Time intervalMS); + + /// \brief Return the connections that we think have an instance of the specified Replica3 instance + /// \details This can be wrong, for example if that system locally deleted the outside the scope of ReplicaManager3, if QueryRemoteConstruction() returned false, or if DeserializeConstruction() returned false. + /// \param[in] replica The replica to check against. + /// \param[out] connectionsThatHaveConstructedThisReplica Populated with connection instances that we believe have \a replica allocated + void GetConnectionsThatHaveReplicaConstructed(Replica3 *replica, DataStructures::List &connectionsThatHaveConstructedThisReplica); + + /// \brief Defines the unique instance of ReplicaManager3 if multiple instances are on the same instance of RakPeerInterface + /// \details ReplicaManager3 supports multiple instances of itself attached to the same instance of rakPeer, in case your game has multiple worlds.
        + /// Call SetWorldID with a different number for each instance.
        + /// The default worldID is 0.
        + /// To use multiple worlds, you will also need to call ReplicaManager3::SetNetworkIDManager() to have a different NetworkIDManager instance per world + void SetWorldID(unsigned char id); + + /// \return Whatever was passed to SetWorldID(), or 0 if it was never called. + unsigned char GetWorldID(void) const; + + /// \details Sets the networkIDManager instance that this plugin relys upon.
        + /// Uses whatever instance is attached to RakPeerInterface if unset.
        + /// To support multiple worlds, you should set it to a different manager for each instance of the plugin + /// \param[in] _networkIDManager The externally allocated NetworkIDManager instance for this plugin to use. + void SetNetworkIDManager(NetworkIDManager *_networkIDManager); + + /// Returns what was passed to SetNetworkIDManager(), or the instance on RakPeerInterface if unset. + NetworkIDManager *GetNetworkIDManager(void) const; + + /// \details Send a network command to destroy one or more Replica3 instances + /// Usually you won't need this, but use Replica3::BroadcastDestruction() instead. + /// The objects are unaffected locally + /// \param[in] replicaList List of Replica3 objects to tell other systems to destroy. + /// \param[in] exclusionAddress Which system to not send to. UNASSIGNED_SYSTEM_ADDRESS to send to all. + void BroadcastDestructionList(DataStructures::List &replicaListSource, const SystemAddress &exclusionAddress); + + /// \internal + /// \details Tell other systems that have this replica to destroy this replica.
        + /// You shouldn't need to call this, as it happens in the Replica3 destructor + void BroadcastDestruction(Replica3 *replica, const SystemAddress &exclusionAddress); + + /// \internal + /// \details Frees internal lists.
        + /// Externally allocated pointers are not deallocated + void Clear(void); + + /// \internal + PRO GetDefaultSendParameters(void) const; + + /// Call interfaces, send data + virtual void Update(void); +protected: + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming); + virtual void OnRakPeerShutdown(void); + virtual void OnDetach(void); + + PluginReceiveResult OnConstruction(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); + PluginReceiveResult OnSerialize(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, RakNet::Time timestamp, unsigned char packetDataOffset); + PluginReceiveResult OnDownloadStarted(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); + PluginReceiveResult OnDownloadComplete(Packet *packet, unsigned char *packetData, int packetDataLength, RakNetGUID senderGuid, unsigned char packetDataOffset); + + void DeallocReplicaNoBroadcastDestruction(RakNet::Connection_RM3 *connection, RakNet::Replica3 *replica3); + RakNet::Connection_RM3 * PopConnection(unsigned int index); + Replica3* GetReplicaByNetworkID(NetworkID networkId); + unsigned int ReferenceInternal(RakNet::Replica3 *replica3); + + DataStructures::List connectionList; + DataStructures::List userReplicaList; + + PRO defaultSendParameters; + RakNet::Time autoSerializeInterval; + RakNet::Time lastAutoSerializeOccurance; + unsigned char worldId; + NetworkIDManager *networkIDManager; + bool autoCreateConnections, autoDestroyConnections; + Replica3 *currentlyDeallocatingReplica; + + friend class Connection_RM3; +}; + +static const int RM3_NUM_OUTPUT_BITSTREAM_CHANNELS=16; + +/// \ingroup REPLICA_MANAGER_GROUP3 +struct LastSerializationResultBS +{ + RakNet::BitStream bitStream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; +}; + +/// Represents the serialized data for an object the last time it was sent. Used by Connection_RM3::OnAutoserializeInterval() and Connection_RM3::SendSerializeIfChanged() +/// \ingroup REPLICA_MANAGER_GROUP3 +struct LastSerializationResult +{ + LastSerializationResult(); + ~LastSerializationResult(); + + /// The replica instance we serialized + RakNet::Replica3 *replica; + //bool neverSerialize; +// bool isConstructed; + + void AllocBS(void); + LastSerializationResultBS* lastSerializationResultBS; +}; + +/// Parameters passed to Replica3::Serialize() +/// \ingroup REPLICA_MANAGER_GROUP3 +struct SerializeParameters +{ + /// Write your output for serialization here + /// If nothing is written, the serialization will not occur + /// Write to any or all of the NUM_OUTPUT_BITSTREAM_CHANNELS channels available. Channels can hold independent data + RakNet::BitStream outputBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + + /// Last bitstream we sent for this replica to this system. + /// Read, but DO NOT MODIFY + RakNet::BitStream* lastSentBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + + /// Set to non-zero to transmit a timestamp with this message. + /// Defaults to 0 + /// Use RakNet::GetTime() for this + RakNet::Time messageTimestamp; + + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketPriority(). + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultPacketReliability(). + /// Passed to RakPeerInterface::Send(). Defaults to ReplicaManager3::SetDefaultOrderingChannel(). + PRO pro[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + + /// Passed to RakPeerInterface::Send(). + RakNet::Connection_RM3 *destinationConnection; + + /// For prior serializations this tick, for the same connection, how many bits have we written so far? + /// Use this to limit how many objects you send to update per-tick if desired + BitSize_t bitsWrittenSoFar; + + /// When this object was last serialized to the connection + /// 0 means never + RakNet::Time whenLastSerialized; + + /// Current time, in milliseconds. + /// curTime - whenLastSerialized is how long it has been since this object was last sent + RakNet::Time curTime; +}; + +/// \ingroup REPLICA_MANAGER_GROUP3 +struct DeserializeParameters +{ + RakNet::BitStream serializationBitstream[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + bool bitstreamWrittenTo[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS]; + RakNet::Time timeStamp; + RakNet::Connection_RM3 *sourceConnection; +}; + +/// \ingroup REPLICA_MANAGER_GROUP3 +enum SendSerializeIfChangedResult +{ + SSICR_SENT_DATA, + SSICR_DID_NOT_SEND_DATA, + SSICR_NEVER_SERIALIZE, +}; + +/// \brief Each remote system is represented by Connection_RM3. Used to allocate Replica3 and track which instances have been allocated +/// \details Important function: AllocReplica() - must be overridden to create an object given an identifier for that object, which you define for all objects in your game +/// \ingroup REPLICA_MANAGER_GROUP3 +class RAK_DLL_EXPORT Connection_RM3 +{ +public: + + Connection_RM3(const SystemAddress &_systemAddress, RakNetGUID _guid); + virtual ~Connection_RM3(); + + /// \brief Class factory to create a Replica3 instance, given a user-defined identifier + /// \details Identifier is returned by Replica3::WriteAllocationID() for what type of class to create.
        + /// This is called when you download a replica from another system.
        + /// See Replica3::Dealloc for the corresponding destruction message.
        + /// Return 0 if unable to create the intended object. Note, in that case the other system will still think we have the object and will try to serialize object updates to us. Generally, you should not send objects the other system cannot create.
        + /// \sa Replica3::WriteAllocationID(). + /// Sample implementation:
        + /// {RakNet::RakString typeName; allocationIdBitstream->Read(typeName); if (typeName=="Soldier") return new Soldier; return 0;}
        + /// \param[in] allocationIdBitstream user-defined bitstream uniquely identifying a game object type + /// \param[in] replicaManager3 Instance of ReplicaManager3 that controls this connection + /// \return The new replica instance + virtual Replica3 *AllocReplica(RakNet::BitStream *allocationIdBitstream, ReplicaManager3 *replicaManager3)=0; + + /// \brief Get list of all replicas that are constructed for this connection + /// \param[out] objectsTheyDoHave Destination list. Returned in sorted ascending order, sorted on the value of the Replica3 pointer. + virtual void GetConstructedReplicas(DataStructures::List &objectsTheyDoHave); + + /// Returns true if we think this remote connection has this replica constructed + /// \param[in] replica3 Which replica we are querying + /// \return True if constructed, false othewise + bool HasReplicaConstructed(RakNet::Replica3 *replica); + + /// When a new connection connects, before sending any objects, SerializeOnDownloadStarted() is called + /// \param[out] bitStream Passed to DeserializeOnDownloadStarted() + virtual void SerializeOnDownloadStarted(RakNet::BitStream *bitStream) {(void) bitStream;} + + /// Receives whatever was written in SerializeOnDownloadStarted() + /// \param[in] bitStream Written in SerializeOnDownloadStarted() + virtual void DeserializeOnDownloadStarted(RakNet::BitStream *bitStream) {(void) bitStream;} + + /// When a new connection connects, after constructing and serialization all objects, SerializeOnDownloadComplete() is called + /// \param[out] bitStream Passed to DeserializeOnDownloadComplete() + virtual void SerializeOnDownloadComplete(RakNet::BitStream *bitStream) {(void) bitStream;} + + /// Receives whatever was written in DeserializeOnDownloadComplete() + /// \param[in] bitStream Written in SerializeOnDownloadComplete() + virtual void DeserializeOnDownloadComplete(RakNet::BitStream *bitStream) {(void) bitStream;} + + /// \return The system address passed to the constructor of this object + SystemAddress GetSystemAddress(void) const {return systemAddress;} + + /// \return Returns the RakNetGUID passed to the constructor of this object + RakNetGUID GetRakNetGUID(void) const {return guid;} + + /// List of enumerations for how to get the list of valid objects for other systems + enum ConstructionMode + { + /// For every object that does not exist on the remote system, call Replica3::QueryConstruction() every tick. + /// Do not call Replica3::QueryDestruction() + /// Do not call Connection_RM3::QueryReplicaList() + QUERY_REPLICA_FOR_CONSTRUCTION, + + /// For every object that does not exist on the remote system, call Replica3::QueryConstruction() every tick. Based on the call, the object may be sent to the other system. + /// For every object that does exist on the remote system, call Replica3::QueryDestruction() every tick. Based on the call, the object may be deleted on the other system. + /// Do not call Connection_RM3::QueryReplicaList() + QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION, + + /// Do not call Replica3::QueryConstruction() or Replica3::QueryDestruction() + /// Call Connection_RM3::QueryReplicaList() to determine which objects exist on remote systems + /// This can be faster than QUERY_REPLICA_FOR_CONSTRUCTION and QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION for large worlds + /// See GridSectorizer.h under /Source for code that can help with this + QUERY_CONNECTION_FOR_REPLICA_LIST + }; + + /// \brief Return whether or not downloads to our system should all be processed the same tick (call to RakPeer::Receive() ) + /// \details Normally the system will send ID_REPLICA_MANAGER_DOWNLOAD_STARTED, ID_REPLICA_MANAGER_CONSTRUCTION for all downloaded objects, + /// ID_REPLICA_MANAGER_SERIALIZE for each downloaded object, and lastly ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE. + /// This enables the application to show a downloading splash screen on ID_REPLICA_MANAGER_DOWNLOAD_STARTED, a progress bar, and to close the splash screen and activate all objects on ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE + /// However, if the application was not set up for this then it would result in incomplete objects spread out over time, and cause problems + /// If you return true from QueryGroupDownloadMessages(), then these messages will be returned all in one tick, returned only when the download is complete + /// \note ID_REPLICA_MANAGER_DOWNLOAD_STARTED calls the callback DeserializeOnDownloadStarted() + /// \note ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE calls the callback DeserializeOnDownloadComplete() + virtual bool QueryGroupDownloadMessages(void) const {return false;} + + /// \brief Queries how to get the list of objects that exist on remote systems + /// \details The default of calling QueryConstruction for every known object is easy to use, but not efficient, especially for large worlds where many objects are outside of the player's circle of influence.
        + /// QueryDestruction is also not necessarily useful or efficient, as object destruction tends to happen in known cases, and can be accomplished by calling Replica3::BroadcastDestruction() + /// QueryConstructionMode() allows you to specify more efficient algorithms than the default when overriden. + /// \return How to get the list of objects that exist on the remote system. You should always return the same value for a given connection + virtual ConstructionMode QueryConstructionMode(void) const {return QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION;} + + /// \brief Callback used when QueryConstructionMode() returns QUERY_CONNECTION_FOR_REPLICA_LIST + /// \details This advantage of this callback is if that there are many objects that a particular connection does not have, then we do not have to iterate through those + /// objects calling QueryConstruction() for each of them.
        + ///
        + /// The following code uses a sorted merge sort to quickly find new and deleted objects, given a list of objects we know should exist.
        + ///
        + /// DataStructures::List objectsTheyShouldHave; // You have to fill in this list
        + /// DataStructures::List objectsTheyCurrentlyHave,objectsTheyStillHave,existingReplicasToDestro,newReplicasToCreatey;
        + /// GetConstructedReplicas(objectsTheyCurrentlyHave);
        + /// DataStructures::Multilist::FindIntersection(objectsTheyCurrentlyHave, objectsTheyShouldHave, objectsTheyStillHave, existingReplicasToDestroy, newReplicasToCreate);
        + ///
        + /// See GridSectorizer in the Source directory as a method to find all objects within a certain radius in a fast way.
        + ///
        + /// \param[out] newReplicasToCreate Anything in this list will be created on the remote system + /// \param[out] existingReplicasToDestroy Anything in this list will be destroyed on the remote system + virtual void QueryReplicaList( + DataStructures::List newReplicasToCreate, + DataStructures::List existingReplicasToDestroy) {} + + /// \internal This is used internally - however, you can also call it manually to send a data update for a remote replica.
        + /// \brief Sends over a serialization update for \a replica.
        + /// NetworkID::GetNetworkID() is written automatically, serializationData is the object data.
        + /// \param[in] replica Which replica to serialize + /// \param[in] serializationData Serialized object data + /// \param[in] timestamp 0 means no timestamp. Otherwise message is prepended with ID_TIMESTAMP + /// \param[in] sendParameters Parameters on how to send + /// \param[in] rakPeer Instance of RakPeerInterface to send on + /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() + virtual SendSerializeIfChangedResult SendSerialize(RakNet::Replica3 *replica, bool indicesToSend[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::BitStream serializationData[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::Time timestamp, PRO sendParameters[RM3_NUM_OUTPUT_BITSTREAM_CHANNELS], RakNet::RakPeerInterface *rakPeer, unsigned char worldId); + + /// \internal + /// \details Calls Connection_RM3::SendSerialize() if Replica3::Serialize() returns a different result than what is contained in \a lastSerializationResult.
        + /// Used by autoserialization in Connection_RM3::OnAutoserializeInterval() + /// \param[in] queryToSerializeIndex Index into queryToSerializeReplicaList for whichever replica this is + /// \param[in] sp Controlling parameters over the serialization + /// \param[in] rakPeer Instance of RakPeerInterface to send on + /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() + virtual SendSerializeIfChangedResult SendSerializeIfChanged(unsigned int queryToSerializeIndex, SerializeParameters *sp, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager); + + /// \internal + /// \brief Given a list of objects that were created and destroyed, serialize and send them to another system. + /// \param[in] newObjects Objects to serialize construction + /// \param[in] deletedObjects Objects to serialize destruction + /// \param[in] sendParameters Controlling parameters over the serialization + /// \param[in] rakPeer Instance of RakPeerInterface to send on + /// \param[in] worldId Which world, see ReplicaManager3::SetWorldID() + /// \param[in] replicaManager3 ReplicaManager3 instance + virtual void SendConstruction(DataStructures::List &newObjects, DataStructures::List &deletedObjects, PRO sendParameters, RakNet::RakPeerInterface *rakPeer, unsigned char worldId, ReplicaManager3 *replicaManager3); + + /// \internal + /// Remove from \a newObjectsIn objects that already exist and save to \a newObjectsOut + /// Remove from \a deletedObjectsIn objects that do not exist, and save to \a deletedObjectsOut + void CullUniqueNewAndDeletedObjects(DataStructures::List &newObjectsIn, + DataStructures::List &deletedObjectsIn, + DataStructures::List &newObjectsOut, + DataStructures::List &deletedObjectsOut); + + /// \internal + void SendValidation(RakNet::RakPeerInterface *rakPeer, unsigned char worldId); + + /// \internal + void AutoConstructByQuery(ReplicaManager3 *replicaManager3); + + + // Internal - does the other system have this connection too? Validated means we can now use it + bool isValidated; + // Internal - Used to see if we should send download started + bool isFirstConstruction; + + static int Replica3LSRComp( Replica3 * const &replica3, LastSerializationResult * const &data ); + +protected: + + SystemAddress systemAddress; + RakNetGUID guid; + + /* + Operations: + + Locally reference a new replica: + Add to queryToConstructReplicaList for all objects + + Add all objects to queryToConstructReplicaList + + Download: + Add to constructedReplicaList for connection that send the object to us + Add to queryToSerializeReplicaList for connection that send the object to us + Add to queryToConstructReplicaList for all other connections + + Never construct for this connection: + Remove from queryToConstructReplicaList + + Construct to this connection + Remove from queryToConstructReplicaList + Add to constructedReplicaList for this connection + Add to queryToSerializeReplicaList for this connection + + Serialize: + Iterate through queryToSerializeReplicaList + + Never serialize for this connection + Remove from queryToSerializeReplicaList + + Reference (this system has this object already) + Remove from queryToConstructReplicaList + Add to constructedReplicaList for this connection + Add to queryToSerializeReplicaList for this connection + + Downloaded an existing object + if replica is in queryToConstructReplicaList, OnConstructToThisConnection() + else ignore + + Send destruction from query + Remove from queryToDestructReplicaList + Remove from queryToSerializeReplicaList + Remove from constructedReplicaList + Add to queryToConstructReplicaList + + Do not query destruction again + Remove from queryToDestructReplicaList + */ + void OnLocalReference(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnDereference(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnDownloadFromThisSystem(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnDownloadFromOtherSystem(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnNeverConstruct(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); + void OnConstructToThisConnection(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); + void OnConstructToThisConnection(Replica3 *replica, ReplicaManager3 *replicaManager); + void OnNeverSerialize(unsigned int queryToSerializeIndex, ReplicaManager3 *replicaManager); + void OnReplicaAlreadyExists(unsigned int queryToConstructIdx, ReplicaManager3 *replicaManager); + void OnDownloadExisting(Replica3* replica3, ReplicaManager3 *replicaManager); + void OnSendDestructionFromQuery(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager); + void OnDoNotQueryDestruction(unsigned int queryToDestructIdx, ReplicaManager3 *replicaManager); + void ValidateLists(ReplicaManager3 *replicaManager) const; + void SendSerializeHeader(RakNet::Replica3 *replica, RakNet::Time timestamp, RakNet::BitStream *bs, unsigned char worldId); + + // The list of objects that our local system and this remote system both have + // Either we sent this object to them, or they sent this object to us + // A given Replica can be either in queryToConstructReplicaList or constructedReplicaList but not both at the same time + DataStructures::OrderedList constructedReplicaList; + + // Objects that we have, but this system does not, and we will query each tick to see if it should be sent to them + // If we do send it to them, the replica is moved to constructedReplicaList + // A given Replica can be either in queryToConstructReplicaList or constructedReplicaList but not both at the same time + DataStructures::List queryToConstructReplicaList; + + // Objects that this system has constructed are added at the same time to queryToSerializeReplicaList + // This list is used to serialize all objects that this system has to this connection + DataStructures::List queryToSerializeReplicaList; + + // Objects that are constructed on this system are also queried if they should be destroyed to this system + DataStructures::List queryToDestructReplicaList; + + // Working lists + DataStructures::List constructedReplicasCulled, destroyedReplicasCulled; + + // This is used if QueryGroupDownloadMessages() returns true when ID_REPLICA_MANAGER_DOWNLOAD_STARTED arrives + // Packets will be gathered and not returned until ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE arrives + bool groupConstructionAndSerialize; + DataStructures::Queue downloadGroup; + void ClearDownloadGroup(RakPeerInterface *rakPeerInterface); + + friend class ReplicaManager3; +private: + Connection_RM3() {}; + + ConstructionMode constructionMode; +}; + +/// \brief Return codes for Connection_RM3::GetConstructionState() and Replica3::QueryConstruction() +/// \details Indicates what state the object should be in for the remote system +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3ConstructionState +{ + /// This object should exist on the remote system. Send a construction message if necessary + /// If the NetworkID is already in use, it will not do anything + /// If it is not in use, it will create the object, and then call DeserializeConstruction + RM3CS_SEND_CONSTRUCTION, + + /// This object should exist on the remote system. + /// The other system already has the object, and the object will never be deleted. + /// This is true of objects that are loaded with the level, for example. + /// Treat it as if it existed, without sending a construction message. + /// Will call Serialize() and SerializeConstructionExisting() to the object on the remote system + RM3CS_ALREADY_EXISTS_REMOTELY, + + /// Same as RM3CS_ALREADY_EXISTS_REMOTELY but does not call SerializeConstructionExisting() + RM3CS_ALREADY_EXISTS_REMOTELY_DO_NOT_CONSTRUCT, + + /// This object will never be sent to the target system + /// This object will never be serialized from this system to the target system + RM3CS_NEVER_CONSTRUCT, + + /// Don't do anything this tick. Will query again next tick + RM3CS_NO_ACTION, + + /// Max enum + RM3CS_MAX, +}; + +/// If this object already exists for this system, should it be removed? +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3DestructionState +{ + /// This object should not exist on the remote system. Send a destruction message if necessary. + RM3DS_SEND_DESTRUCTION, + + /// This object will never be destroyed by a per-tick query. Don't call again + RM3DS_DO_NOT_QUERY_DESTRUCTION, + + /// Don't do anything this tick. Will query again next tick + RM3DS_NO_ACTION, + + /// Max enum + RM3DS_MAX, +}; + +/// Return codes when constructing an object +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3SerializationResult +{ + /// This object serializes identically no matter who we send to + /// We also send it to every connection (broadcast). + /// Efficient for memory, speed, and bandwidth but only if the object is always broadcast identically. + RM3SR_BROADCAST_IDENTICALLY, + + /// Same as RM3SR_BROADCAST_IDENTICALLY, but assume the object needs to be serialized, do not check with a memcmp + /// Assume the object changed, and serialize it + /// Use this if you know exactly when your object needs to change. Can be faster than RM3SR_BROADCAST_IDENTICALLY. + /// An example of this is if every member variable has an accessor, changing a member sets a flag, and you check that flag in Replica3::QuerySerialization() + /// The opposite of this is RM3SR_DO_NOT_SERIALIZE, in case the object did not change + RM3SR_BROADCAST_IDENTICALLY_FORCE_SERIALIZATION, + + /// Either this object serializes differently depending on who we send to or we send it to some systems and not others. + /// Inefficient for memory and speed, but efficient for bandwidth + /// However, if you don't know what to return, return this + RM3SR_SERIALIZED_UNIQUELY, + + /// Do not compare against last sent value. Just send even if the data is the same as the last tick + /// If the data is always changing anyway, or you want to send unreliably, this is a good method of serialization + /// Can send unique data per connection if desired. If same data is sent to all connections, use RM3SR_SERIALIZED_ALWAYS_IDENTICALLY for even better performance + /// Efficient for memory and speed, but not necessarily bandwidth + RM3SR_SERIALIZED_ALWAYS, + + /// Even faster than RM3SR_SERIALIZED_ALWAYS + /// Serialize() will only be called for the first system. The remaining systems will get the same data as the first system. + RM3SR_SERIALIZED_ALWAYS_IDENTICALLY, + + /// Do not serialize this object this tick, for this connection. Will query again next autoserialize timer + RM3SR_DO_NOT_SERIALIZE, + + /// Never serialize this object for this connection + /// Useful for objects that are downloaded, and never change again + /// Efficient + RM3SR_NEVER_SERIALIZE_FOR_THIS_CONNECTION, + + /// Max enum + RM3SR_MAX, +}; + +/// First pass at topology to see if an object should be serialized +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3QuerySerializationResult +{ + /// Call Serialize() to see if this object should be serializable for this connection + RM3QSR_CALL_SERIALIZE, + /// Do not call Serialize() this tick to see if this object should be serializable for this connection + RM3QSR_DO_NOT_CALL_SERIALIZE, + /// Never call Serialize() for this object and connection. This system will not serialize this object for this topology + RM3QSR_NEVER_CALL_SERIALIZE, + /// Max enum + RM3QSR_MAX, +}; + +/// \ingroup REPLICA_MANAGER_GROUP3 +enum RM3ActionOnPopConnection +{ + RM3AOPC_DO_NOTHING, + RM3AOPC_DELETE_REPLICA, + RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION, + RM3AOPC_MAX, +}; + +/// \ingroup REPLICA_MANAGER_GROUP3 +/// Used for Replica3::QueryConstruction_PeerToPeer() and Replica3::QuerySerialization_PeerToPeer() to describe how the object replicates between hosts +enum Replica3P2PMode +{ + /// The Replica3 instance is constructed and serialized by one system only. + /// Example: Your avatar. No other player serializes or can create your avatar. + R3P2PM_SINGLE_OWNER, + /// The Replica3 instance is constructed and/or serialized by different systems + /// This system is currently in charge of construction and/or serialization + /// Example: A pickup. When an avatar holds it, that avatar controls it. When it is on the ground, the host controls it. + R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE, + /// The Replica3 instance is constructed and/or serialized by different systems + /// Another system is in charge of construction and/or serialization, but this system may be in charge at a later time + /// Example: A pickup held by another player. That player sends creation of that object to new connections, and serializes it until it is dropped. + R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE, +}; + +/// \brief Base class for your replicated objects for the ReplicaManager3 system. +/// \details To use, derive your class, or a member of your class, from Replica3.
        +/// \ingroup REPLICA_MANAGER_GROUP3 +class RAK_DLL_EXPORT Replica3 : public NetworkIDObject +{ +public: + Replica3(); + + /// Before deleting a local instance of Replica3, call Replica3::BroadcastDestruction() for the deletion notification to go out on the network. + /// It is not necessary to call ReplicaManager3::Dereference(), as this happens automatically in the destructor + virtual ~Replica3(); + + /// \brief Write a unique identifer that can be read on a remote system to create an object of this same class. + /// \details The value written to \a allocationIdBitstream will be passed to Connection_RM3::AllocReplica().
        + /// Sample implementation:
        + /// {allocationIdBitstream->Write(RakNet::RakString("Soldier");}
        + /// \param[out] allocationIdBitstream Bitstream for the user to write to, to identify this class + virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const=0; + + /// \brief Ask if this object, which does not exist on \a destinationConnection should (now) be sent to that system. + /// \details If ReplicaManager3::QueryConstructionMode() returns QUERY_CONNECTION_FOR_REPLICA_LIST or QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION (default), + /// then QueyrConstruction() is called once per tick from ReplicaManager3::Update() to determine if an object should exist on a given system.
        + /// Based on the return value, a network message may be sent to the other system to create the object.
        + /// If QueryConstructionMode() is overriden to return QUERY_CONNECTION_FOR_REPLICA_LIST, this function is unused.
        + /// \note Defaults are provided: QueryConstruction_PeerToPeer(), QueryConstruction_ServerConstruction(), QueryConstruction_ClientConstruction(). Return one of these functions for a working default for the relevant topology. + /// \param[in] destinationConnection Which system we will send to + /// \param[in] replicaManager3 Plugin instance for this Replica3 + /// \return What action to take + virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3)=0; + + /// \brief Ask if this object, which does exist on \a destinationConnection should be removed from the remote system + /// \details If ReplicaManager3::QueryConstructionMode() returns QUERY_REPLICA_FOR_CONSTRUCTION_AND_DESTRUCTION (default), + /// then QueryDestruction() is called once per tick from ReplicaManager3::Update() to determine if an object that exists on a remote system should be destroyed for a given system.
        + /// Based on the return value, a network message may be sent to the other system to destroy the object.
        + /// Note that you can also destroy objects with BroadcastDestruction(), so this function is not useful unless you plan to delete objects for only a particular connection.
        + /// If QueryConstructionMode() is overriden to return QUERY_CONNECTION_FOR_REPLICA_LIST, this function is unused.
        + /// \param[in] destinationConnection Which system we will send to + /// \param[in] replicaManager3 Plugin instance for this Replica3 + /// \return What action to take. Only RM3CS_SEND_DESTRUCTION does anything at this time. + virtual RM3DestructionState QueryDestruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {(void) destinationConnection; (void) replicaManager3; return RM3DS_DO_NOT_QUERY_DESTRUCTION;} + + /// \brief We're about to call DeserializeConstruction() on this Replica3. If QueryRemoteConstruction() returns false, this object is deleted instead. + /// \details By default, QueryRemoteConstruction_ServerConstruction() does not allow clients to create objects. The client will get Replica3::DeserializeConstructionRequestRejected().
        + /// If you want the client to be able to potentially create objects for client/server, override accordingly.
        + /// Other variants of QueryRemoteConstruction_* just return true. + /// \note Defaults are provided: QueryRemoteConstruction_PeerToPeer(), QueryRemoteConstruction_ServerConstruction(), QueryRemoteConstruction_ClientConstruction(). Return one of these functions for a working default for the relevant topology. + /// \param[in] sourceConnection Which system sent us the object creation request message. + /// \return True to allow the object to pass onto DeserializeConstruction() (where it may also be rejected), false to immediately reject the remote construction request + virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection)=0; + + /// \brief We got a message from a connection to destroy this replica + /// Return true to automatically relay the destruction message to all our other connections + /// For a client in client/server, it does not matter what this funtion returns + /// For a server in client/server, this should normally return true + /// For a peer in peer to peer, you can normally return false since the original destroying peer would have told all other peers about the destruction + /// If a system gets a destruction command for an object that was already destroyed, the destruction message is ignored + virtual bool QueryRelayDestruction(Connection_RM3 *sourceConnection) const {(void) sourceConnection; return true;} + + /// \brief Write data to be sent only when the object is constructed on a remote system. + /// \details SerializeConstruction is used to write out data that you need to create this object in the context of your game, such as health, score, name. Use it for data you only need to send when the object is created.
        + /// After SerializeConstruction() is called, Serialize() will be called immediately thereafter. However, they are sent in different messages, so Serialize() may arrive a later frame than SerializeConstruction() + /// For that reason, the object should be valid after a call to DeserializeConstruction() for at least a short time.
        + /// \note The object's NetworkID and allocation id are handled by the system automatically, you do not need to write these values to \a constructionBitstream + /// \param[out] constructionBitstream Destination bitstream to write your data to + /// \param[in] destinationConnection System that will receive this network message. + virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection)=0; + + /// \brief Read data written by Replica3::SerializeConstruction() + /// \details Reads whatever data was written to \a constructionBitstream in Replica3::SerializeConstruction() + /// \param[out] constructionBitstream Bitstream written to in Replica3::SerializeConstruction() + /// \param[in] sourceConnection System that sent us this network message. + /// \return true to accept construction of the object. false to reject, in which case the object will be deleted via Replica3::DeallocReplica() + virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection)=0; + + /// Same as SerializeConstruction(), but for an object that already exists on the remote system. + /// Used if you return RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction + virtual void SerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;}; + + /// Same as DeserializeConstruction(), but for an object that already exists on the remote system. + /// Used if you return RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction + virtual void DeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;}; + + /// \brief Write extra data to send with the object deletion event, if desired + /// \details Replica3::SerializeDestruction() will be called to write any object destruction specific data you want to send with this event. + /// \a destructionBitstream can be read in DeserializeDestruction() + /// \param[out] destructionBitstream Bitstream for you to write to + /// \param[in] destinationConnection System that will receive this network message. + virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection)=0; + + /// \brief Read data written by Replica3::SerializeDestruction() + /// \details Return true to delete the object. BroadcastDestruction() will be called automatically, followed by ReplicaManager3::Dereference.
        + /// Return false to not delete it. If you delete it at a later point, you are responsible for calling BroadcastDestruction() yourself. + virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection)=0; + + /// \brief The system is asking what to do with this replica when the connection is dropped + /// \details Return QueryActionOnPopConnection_Client, QueryActionOnPopConnection_Server, or QueryActionOnPopConnection_PeerToPeer + virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const=0; + + /// Notification called for each of our replicas when a connection is popped + virtual void OnPoppedConnection(RakNet::Connection_RM3 *droppedConnection) {(void) droppedConnection;} + + /// \brief Override with {delete this;} + /// \details + ///
          + ///
        1. Got a remote message to delete this object which passed DeserializeDestruction(), OR + ///
        2. ReplicaManager3::SetAutoManageConnections() was called autoDestroy true (which is the default setting), and a remote system that owns this object disconnected) OR + /// <\OL> + ///
          + /// Override with {delete this;} to actually delete the object (and any other processing you wish).
          + /// If you don't want to delete the object, just do nothing, however, the system will not know this so you are responsible for deleting it yoruself later.
          + /// destructionBitstream may be 0 if the object was deleted locally + virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection)=0; + + /// \brief Implement with QuerySerialization_ClientSerializable(), QuerySerialization_ServerSerializable(), or QuerySerialization_PeerToPeer() + /// \details QuerySerialization() is a first pass query to check if a given object should serializable to a given system. The intent is that the user implements with one of the defaults for client, server, or peer to peer.
          + /// Without this function, a careless implementation would serialize an object anytime it changed to all systems. This would give you feedback loops as the sender gets the same message back from the recipient it just sent to.
          + /// If more than one system can serialize the same object then you will need to override to return true, and control the serialization result from Replica3::Serialize(). Be careful not to send back the same data to the system that just sent to you! + /// \return True to allow calling Replica3::Serialize() for this connection, false to not call. + virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection)=0; + + /// \brief Called for each replica owned by the user, once per Serialization tick, before Serialize() is called. + /// If you want to do some kind of operation on the Replica objects that you own, just before Serialization(), then overload this function + virtual void OnUserReplicaPreSerializeTick(void) {} + + /// \brief Serialize our class to a bitstream + /// \details User should implement this function to write the contents of this class to SerializationParamters::serializationBitstream.
          + /// If data only needs to be written once, you can write it to SerializeConstruction() instead for efficiency.
          + /// Transmitted over the network if it changed from the last time we called Serialize().
          + /// Called every time the time interval to ReplicaManager3::SetAutoSerializeInterval() elapses and ReplicaManager3::Update is subsequently called. + /// \param[in/out] serializeParameters Parameters controlling the serialization, including destination bitstream to write to + /// \return Whether to serialize, and if so, how to optimize the results + virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters)=0; + + /// \brief Called when the class is actually transmitted via Serialize() + /// \details Use to track how much bandwidth this class it taking + virtual void OnSerializeTransmission(RakNet::BitStream *bitStream, const SystemAddress &systemAddress) {(void) bitStream; (void) systemAddress; } + + /// \brief Read what was written in Serialize() + /// \details Reads the contents of the class from SerializationParamters::serializationBitstream.
          + /// Called whenever Serialize() is called with different data from the last send. + /// \param[in] serializationBitstream Bitstream passed to Serialize() + /// \param[in] timeStamp 0 if unused, else contains the time the message originated on the remote system + /// \param[in] sourceConnection Which system sent to us + virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters)=0; + + /// \brief Called after SerializeConstruction completes for all objects in a given update tick.
          + /// Writes to PostDeserializeConstruction(), which is called after all objects are created for a given Construction tick(). + /// Override to send data to PostDeserializeConstruction(), such as the NetworkID of other objects to resolve pointers to + virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;} + + /// Called after DeserializeConstruction completes for all objects in a given update tick.
          + /// This is used to resolve dependency chains, where two objects would refer to each other in DeserializeConstruction, yet one had not been constructed yet + /// In PostDeserializeConstruction(), you know that all objects have already been created, so can resolve NetworkIDs to pointers safely. + /// You can also use it to trigger some sort of event when you know the object has completed deserialization. + /// \param[in] constructionBitstream BitStream written in PostSerializeConstruction() + /// \param[in] sourceConnection System that sent us this network message. + virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;} + + /// Same as PostSerializeConstruction(), but for objects that returned RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction + virtual void PostSerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {(void) constructionBitstream; (void) destinationConnection;} + + /// Same as PostDeserializeConstruction(), but for objects that returned RM3CS_ALREADY_EXISTS_REMOTELY from QueryConstruction + virtual void PostDeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {(void) constructionBitstream; (void) sourceConnection;} + + /// Called after DeserializeDestruction completes for the object successfully, but obviously before the object is deleted.
          + /// Override to trigger some sort of event when you know the object has completed destruction. + /// \param[in] sourceConnection System that sent us this network message. + virtual void PreDestruction(RakNet::Connection_RM3 *sourceConnection) {(void) sourceConnection;} + + /// \brief Default call for QueryConstruction(). + /// \details Both the client and the server is allowed to create this object. The network topology is client/server + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual RM3ConstructionState QueryConstruction_ClientConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); + /// Default call for QueryRemoteConstruction(). + /// \details Both the client and the server is allowed to create this object. The network topology is client/server + /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual bool QueryRemoteConstruction_ClientConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer); + + /// \brief Default call for QueryConstruction(). + /// \details Only the server is allowed to create this object. The network topology is client/server + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual RM3ConstructionState QueryConstruction_ServerConstruction(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); + /// \brief Default call for QueryRemoteConstruction(). Allow the server to create this object, but not the client. + /// \details Only the server is allowed to create this object. The network topology is client/server + /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual bool QueryRemoteConstruction_ServerConstruction(RakNet::Connection_RM3 *sourceConnection, bool isThisTheServer); + + /// \brief Default call for QueryConstruction(). + /// \details All clients are allowed to create all objects. The object is not relayed when remotely created + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] p2pMode If controlled only by this system ever, pass R3P2PM_SINGLE_OWNER. Otherwise pass R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE or R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE + virtual RM3ConstructionState QueryConstruction_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode=R3P2PM_SINGLE_OWNER); + /// \brief Default call for QueryRemoteConstruction(). + /// \details All clients are allowed to create all objects. The object is not relayed when remotely created + /// \param[in] sourceConnection destinationConnection parameter passed to QueryConstruction() + virtual bool QueryRemoteConstruction_PeerToPeer(RakNet::Connection_RM3 *sourceConnection); + + /// \brief Default call for QuerySerialization(). + /// \details Use if the values you are serializing are generated by the client that owns the object. The serialization will be relayed through the server to the other clients. + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual RakNet::RM3QuerySerializationResult QuerySerialization_ClientSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); + /// \brief Default call for QuerySerialization(). + /// \details Use if the values you are serializing are generated only by the server. The serialization will be sent to all clients, but the clients will not send back to the server. + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] isThisTheServer True if this system is the server, false if not. + virtual RakNet::RM3QuerySerializationResult QuerySerialization_ServerSerializable(RakNet::Connection_RM3 *destinationConnection, bool isThisTheServer); + /// \brief Default call for QuerySerialization(). + /// \details Use if the values you are serializing are on a peer to peer network. The peer that owns the object will send to all. Remote peers will not send. + /// \param[in] destinationConnection destinationConnection parameter passed to QueryConstruction() + /// \param[in] p2pMode If controlled only by this system ever, pass R3P2PM_SINGLE_OWNER. Otherwise pass R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE or R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE + virtual RakNet::RM3QuerySerializationResult QuerySerialization_PeerToPeer(RakNet::Connection_RM3 *destinationConnection, Replica3P2PMode p2pMode=R3P2PM_SINGLE_OWNER); + + /// Default: If we are a client, and the connection is lost, delete the server's objects + virtual RM3ActionOnPopConnection QueryActionOnPopConnection_Client(RakNet::Connection_RM3 *droppedConnection) const; + /// Default: If we are a client, and the connection is lost, delete the client's objects and broadcast the destruction + virtual RM3ActionOnPopConnection QueryActionOnPopConnection_Server(RakNet::Connection_RM3 *droppedConnection) const; + /// Default: If we are a peer, and the connection is lost, delete the peer's objects + virtual RM3ActionOnPopConnection QueryActionOnPopConnection_PeerToPeer(RakNet::Connection_RM3 *droppedConnection) const; + + /// Call to send a network message to delete this object on other systems.
          + /// Call it before deleting the object + virtual void BroadcastDestruction(void); + + /// creatingSystemGUID is set the first time Reference() is called, or if we get the object from another system + /// \return System that originally created this object + RakNetGUID GetCreatingSystemGUID(void) const; + + /// \return If ReplicaManager3::Reference() was called on this object. + bool WasReferenced(void) const {return replicaManager!=0;} + + /// GUID of the system that first called Reference() on this object. + /// Transmitted automatically when the object is constructed + RakNetGUID creatingSystemGUID; + /// GUID of the system that caused the item to send a deletion command over the network + RakNetGUID deletingSystemGUID; + + /// \internal + /// ReplicaManager3 plugin associated with this object + ReplicaManager3 *replicaManager; + + LastSerializationResultBS lastSentSerialization; + RakNet::Time whenLastSerialized; + bool forceSendUntilNextUpdate; +}; + +/// \brief Use Replica3 through composition instead of inheritance by containing an instance of this templated class +/// Calls to parent class for all functions +/// Parent class must still define and functions though! +/// \pre Parent class must call SetParent() on this object +template +class RAK_DLL_EXPORT Replica3Composite : public Replica3 +{ +protected: + parent_type *r3CompositeOwner; +public: + void SetCompositeOwner(parent_type *p) {r3CompositeOwner=p;} + parent_type* GetCompositeOwner(void) const {return r3CompositeOwner;}; + virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const {r3CompositeOwner->WriteAllocationID(destinationConnection, allocationIdBitstream);} + virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {return r3CompositeOwner->QueryConstruction(destinationConnection, replicaManager3);} + virtual RM3DestructionState QueryDestruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {return r3CompositeOwner->QueryDestruction(destinationConnection, replicaManager3);} + virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->QueryRemoteConstruction(sourceConnection);} + virtual bool QueryRelayDestruction(Connection_RM3 *sourceConnection) const {return r3CompositeOwner->QueryRelayDestruction(sourceConnection);} + virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeConstruction(constructionBitstream, destinationConnection);} + virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->DeserializeConstruction(constructionBitstream, sourceConnection);} + virtual void SerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeConstructionExisting(constructionBitstream, destinationConnection);} + virtual void DeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->DeserializeConstructionExisting(constructionBitstream, sourceConnection);} + virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->SerializeDestruction(destructionBitstream, destinationConnection);} + virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return r3CompositeOwner->DeserializeDestruction(destructionBitstream, sourceConnection);} + virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {return r3CompositeOwner->QueryActionOnPopConnection(droppedConnection);} + virtual void OnPoppedConnection(RakNet::Connection_RM3 *droppedConnection) {r3CompositeOwner->OnPoppedConnection(droppedConnection);} + virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->DeallocReplica(sourceConnection);} + virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {return r3CompositeOwner->QuerySerialization(destinationConnection);} + virtual void OnUserReplicaPreSerializeTick(void) {r3CompositeOwner->OnUserReplicaPreSerializeTick();} + virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters) {return r3CompositeOwner->Serialize(serializeParameters);} + virtual void OnSerializeTransmission(RakNet::BitStream *bitStream, const SystemAddress &systemAddress) {r3CompositeOwner->OnSerializeTransmission(bitStream, systemAddress);} + virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters) {r3CompositeOwner->Deserialize(deserializeParameters);} + virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->PostSerializeConstruction(constructionBitstream, destinationConnection);} + virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PostDeserializeConstruction(constructionBitstream, sourceConnection);} + virtual void PostSerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {r3CompositeOwner->PostSerializeConstructionExisting(constructionBitstream, destinationConnection);} + virtual void PostDeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PostDeserializeConstructionExisting(constructionBitstream, sourceConnection);} + virtual void PreDestruction(RakNet::Connection_RM3 *sourceConnection) {r3CompositeOwner->PreDestruction(sourceConnection);} +}; + +} // namespace RakNet + + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/Router2.cpp b/src/raknet/Router2.cpp index a442fb5..1f06638 100755 --- a/src/raknet/Router2.cpp +++ b/src/raknet/Router2.cpp @@ -1,1329 +1,1329 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_Router2==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#include "Router2.h" -#include "RakPeerInterface.h" -#include "BitStream.h" -#include "RakNetTime.h" -#include "GetTime.h" -#include "DS_OrderedList.h" -#include "SocketLayer.h" -#include "FormatString.h" - -using namespace RakNet; - -/* -Algorithm: - -1. Sender calls ConnectInternal(). A ConnnectRequest structure is allocated and stored in the connectionRequests list, containing a list of every system we are connected to. ID_ROUTER_2_QUERY_FORWARDING is sent to all connected systems. - -2. Upon the router getting ID_ROUTER_2_QUERY_FORWARDING, ID_ROUTER_2_REPLY_FORWARDING is sent to the sender indicating if that router is connected to the endpoint, along with the ping from the router to the endpoint. - -3. Upon the sender getting ID_ROUTER_2_REPLY_FORWARDING, the connection request structure is looked up in Router2::UpdateForwarding. The ping is stored in that structure. Once all systems have replied, the system continues to the next state. If every system in step 1 has been exhausted, and routing has occured at least once, then ID_CONNECTION_LOST is returned. If every system in step 1 has been exhausted and routing has never occured, then ID_ROUTER_2_FORWARDING_NO_PATH is returned. Otherwise, the router with the lowest ping is chosen, and RequestForwarding() is called with that system, which sends ID_ROUTER_2_REQUEST_FORWARDING to the router. - -4. When the router gets ID_ROUTER_2_REQUEST_FORWARDING, a MiniPunchRequest structure is allocated and stored in the miniPunchesInProgress list. The function SendOOBMessages() sends ID_ROUTER_2_REPLY_TO_SENDER_PORT from the routing sockets to both the sender and endpoint. It also sends ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT through the regular RakNet connection. - -5. The sender and endpoint should get ID_ROUTER_2_REPLY_TO_SENDER_PORT and/or ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT depending on what type of router they have. If ID_ROUTER_2_REPLY_TO_SENDER_PORT arrives, then this will reply back to the routing socket directly. If ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT arrives, then the reply port is modified to be the port specified by the router system. In both cases, ID_ROUTER_2_MINI_PUNCH_REPLY is sent. As the router has already setup the forwarding, ID_ROUTER_2_MINI_PUNCH_REPLY will actually arrive to the endpoint from the sender, and from the sender to the endpoint. - -6. When ID_ROUTER_2_MINI_PUNCH_REPLY arrives, ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE will be sent to the router. This is to tell the router that the forwarding has succeeded. - -7. When ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE arrives on the router, the router will find the two systems in the miniPunchesInProgress list, which was added in step 4 (See OnMiniPunchReplyBounce()). gotReplyFromSource or gotReplyFromEndpoint will be set to true, depending on the sender. When both gotReplyFromSource and gotReplyFromEndpoint have replied, then ID_ROUTER_2_REROUTE is sent to the endpoint, and ID_ROUTER_2_FORWARDING_ESTABLISHED is sent to the sender. - -8. When the endpoint gets ID_ROUTER_2_REROUTE, the system address is updated for the guid of the sender using RakPeer::ChangeSystemAddress(). This happens in OnRerouted(). - -9. When the sender gets ID_ROUTER_2_FORWARDING_ESTABLISHED, then in OnForwardingSuccess() the endpoint is removed from the connectionRequest list and moved to the forwardedConnectionList. - -10. In OnClosedConnection(), for the sender, if the closed connection is the endpoint, then the endpoint is removed from the forwardedConnectionList (this is a graceful disconnect). If the connection was instead lost to the router, then ConnectInternal() gets called, which goes back to step 1. If instead this was a connection requset in progress, then UpdateForwarding() gets called, which goes back to step 3. - -11. When the user connects the endpoint and sender, then the sender will get ID_CONNECTION_REQUEST_ACCEPTED. The sender will look up the endpoint in the forwardedConnectionList, and send ID_ROUTER_2_INCREASE_TIMEOUT to the endpoint. This message will call SetTimeoutTime() on the endpoint, so that if the router disconnects, enough time is available for the reroute to complete. -*/ - -#define MIN_MINIPUNCH_TIMEOUT 5000 - - - - -void Router2DebugInterface::ShowFailure(const char *message) -{ - printf("%s", message); -} -void Router2DebugInterface::ShowDiagnostic(const char *message) -{ - printf("%s", message); -} - -enum Router2MessageIdentifiers -{ - ID_ROUTER_2_QUERY_FORWARDING, - ID_ROUTER_2_REPLY_FORWARDING, - ID_ROUTER_2_REQUEST_FORWARDING, - ID_ROUTER_2_INCREASE_TIMEOUT, -}; -Router2::ConnnectRequest::ConnnectRequest() -{ - -} -Router2::ConnnectRequest::~ConnnectRequest() -{ - -} - -STATIC_FACTORY_DEFINITIONS(Router2,Router2); - -Router2::Router2() -{ - udpForwarder=0; - maximumForwardingRequests=0; - debugInterface=0; - socketFamily=AF_INET; -} -Router2::~Router2() -{ - ClearAll(); - - if (udpForwarder) - { - udpForwarder->Shutdown(); - RakNet::OP_DELETE(udpForwarder,_FILE_AND_LINE_); - } -} -void Router2::ClearMinipunches(void) -{ - miniPunchesInProgressMutex.Lock(); - miniPunchesInProgress.Clear(false,_FILE_AND_LINE_); - miniPunchesInProgressMutex.Unlock(); -} -void Router2::ClearConnectionRequests(void) -{ - connectionRequestsMutex.Lock(); - for (unsigned int i=0; i < connectionRequests.Size(); i++) - { - RakNet::OP_DELETE(connectionRequests[i],_FILE_AND_LINE_); - } - connectionRequests.Clear(false,_FILE_AND_LINE_); - connectionRequestsMutex.Unlock(); -} -bool Router2::ConnectInternal(RakNetGUID endpointGuid, bool returnConnectionLostOnFailure) -{ - int largestPing = GetLargestPingAmongConnectedSystems(); - if (largestPing<0) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2: ConnectInternal(%I64d) failed at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); - - // Not connected to anyone - return false; - } - - // ALready in progress? - connectionRequestsMutex.Lock(); - if (GetConnectionRequestIndex(endpointGuid)!=(unsigned int)-1) - { - connectionRequestsMutex.Unlock(); - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2: ConnectInternal(%I64d) failed at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); - - return false; - } - connectionRequestsMutex.Unlock(); - - // StoreRequest(endpointGuid, Largest(ping*2), systemsSentTo). Set state REQUEST_STATE_QUERY_FORWARDING - Router2::ConnnectRequest *cr = RakNet::OP_NEW(_FILE_AND_LINE_); - DataStructures::List addresses; - DataStructures::List guids; - rakPeerInterface->GetSystemList(addresses, guids); - if (guids.Size()==0) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", _FILE_AND_LINE_)); - - return false; - } - cr->requestState=R2RS_REQUEST_STATE_QUERY_FORWARDING; - cr->pingTimeout=RakNet::GetTimeMS()+largestPing*2+1000; - cr->endpointGuid=endpointGuid; - cr->returnConnectionLostOnFailure=returnConnectionLostOnFailure; - for (unsigned int i=0; i < guids.Size(); i++) - { - ConnectionRequestSystem crs; - if (guids[i]!=endpointGuid) - { - crs.guid=guids[i]; - crs.pingToEndpoint=-1; - cr->connectionRequestSystemsMutex.Lock(); - cr->connectionRequestSystems.Push(crs,_FILE_AND_LINE_); - cr->connectionRequestSystemsMutex.Unlock(); - - // Broadcast(ID_ROUTER_2_QUERY_FORWARDING, endpointGuid); - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); - bsOut.Write((unsigned char) ID_ROUTER_2_QUERY_FORWARDING); - bsOut.Write(endpointGuid); - uint32_t pack_id = rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,crs.guid,false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Router2::ConnectInternal: at %s:%i, pack_id = %d", __FILE__, __LINE__,pack_id)); - } - - } - else - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Router2::ConnectInternal: at %s:%i [else ..].: %I64d==%I64d", __FILE__, __LINE__, - guids[i].g,endpointGuid.g)); - } - } - } - connectionRequestsMutex.Lock(); - connectionRequests.Push(cr,_FILE_AND_LINE_); - connectionRequestsMutex.Unlock(); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Broadcasting ID_ROUTER_2_QUERY_FORWARDING to %I64d at %s:%i\n", endpointGuid.g , __FILE__, __LINE__)); - } - - return true; -} -void Router2::SetSocketFamily(unsigned short _socketFamily) -{ - socketFamily=_socketFamily; -} -void Router2::EstablishRouting(RakNetGUID endpointGuid) -{ - ConnectionState cs = rakPeerInterface->GetConnectionState(endpointGuid); - if (cs!=IS_DISCONNECTED && cs!=IS_NOT_CONNECTED) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i " - "(already connected to the %I64d)\n", __FILE__, __LINE__, endpointGuid.g )); - return; - } - - ConnectInternal(endpointGuid,false); -} -void Router2::SetMaximumForwardingRequests(int max) -{ - if (max>0 && maximumForwardingRequests<=0) - { - udpForwarder = RakNet::OP_NEW(_FILE_AND_LINE_); - udpForwarder->Startup(); - } - else if (max<=0 && maximumForwardingRequests>0) - { - udpForwarder->Shutdown(); - RakNet::OP_DELETE(udpForwarder,_FILE_AND_LINE_); - udpForwarder=0; - } - - maximumForwardingRequests=max; -} -PluginReceiveResult Router2::OnReceive(Packet *packet) -{ - SystemAddress sa; - RakNet::BitStream bs(packet->data,packet->length,false); - if (packet->data[0]==ID_ROUTER_2_INTERNAL) - { - switch (packet->data[1]) - { - case ID_ROUTER_2_QUERY_FORWARDING: - { - OnQueryForwarding(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_REPLY_FORWARDING: - { - OnQueryForwardingReply(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_REQUEST_FORWARDING: - { - - if (debugInterface) - { - char buff[512]; - char buff2[32]; - packet->systemAddress.ToString(true,buff2); - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING on ip %s from %I64d, ", - buff2,packet->guid.g)); - } - - OnRequestForwarding(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_INCREASE_TIMEOUT: - { - /// The routed system wants more time to stay alive on no communication, in case the router drops or crashes - rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - } - else if (packet->data[0]==ID_OUT_OF_BAND_INTERNAL && packet->length>=2) - { - switch (packet->data[1]) - { - case ID_ROUTER_2_REPLY_TO_SENDER_PORT: - { - RakNet::BitStream bsOut; - bsOut.Write(packet->guid); - SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, packet->systemAddress); - - if (debugInterface) - { - char buff[512]; - char buff2[32]; - sa.ToString(false,buff2); - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT %i on address %s, replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, _FILE_AND_LINE_)); - -// packet->systemAddress.ToString(true,buff2); -// debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT on address %s (%I64d), " -// "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", buff2,packet->guid.g, __FILE__, __LINE__)); - } - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT: - { - RakNet::BitStream bsOut; - bsOut.Write(packet->guid); - bs.IgnoreBytes(2); - sa=packet->systemAddress; - unsigned short port; - bs.Read(port); - sa.SetPort(port); - RakAssert(sa.GetPort()!=0); - SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, sa); - - if (debugInterface) - { - char buff[512]; - char buff2[32]; - sa.ToString(false,buff2); - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT %i on address %s, " - "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, __FILE__, __LINE__)); - } - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_ROUTER_2_MINI_PUNCH_REPLY: - OnMiniPunchReply(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE: - OnMiniPunchReplyBounce(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - else if (packet->data[0]==ID_ROUTER_2_FORWARDING_ESTABLISHED) - { -// printf("Got ID_ROUTER_2_FORWARDING_ESTABLISHED\n"); - if (OnForwardingSuccess(packet)==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - else if (packet->data[0]==ID_ROUTER_2_REROUTED) - { - OnRerouted(packet); - } - else if (packet->data[0]==ID_CONNECTION_REQUEST_ACCEPTED) - { - unsigned int forwardingIndex; - forwardedConnectionListMutex.Lock(); - for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) - { - if (forwardedConnectionList[forwardingIndex].endpointGuid==packet->guid && forwardedConnectionList[forwardingIndex].weInitiatedForwarding) - break; - } - - if (forwardingIndexSend(&bsOut,HIGH_PRIORITY,RELIABLE,0,packet->guid,false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_CONNECTION_REQUEST_ACCEPTED, " - "sending ID_ROUTER_2_INCREASE_TIMEOUT to the %I64d at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); - } - - // Also take longer ourselves - rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress); - } - else { - // ~Gwynn: Fix for Receive hanging up problem on Windows XP - // See http://blog.delphi-jedi.net/2008/04/23/the-case-of-the-unexplained-dead-lock-in-a-single-thread/ for details - forwardedConnectionListMutex.Unlock(); - } - } - else if (packet->data[0]==ID_ROUTER_2_FORWARDING_NO_PATH) - { - if (packet->wasGeneratedLocally==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - return RR_CONTINUE_PROCESSING; -} -void Router2::Update(void) -{ - RakNet::TimeMS curTime = RakNet::GetTimeMS(); - unsigned int connectionRequestIndex=0; - connectionRequestsMutex.Lock(); - while (connectionRequestIndex < connectionRequests.Size()) - { - ConnnectRequest* connectionRequest = connectionRequests[connectionRequestIndex]; - - // pingTimeout is only used with R2RS_REQUEST_STATE_QUERY_FORWARDING - if (connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING && - connectionRequest->pingTimeout < curTime) - { - bool anyRemoved=false; - unsigned int connectionRequestGuidIndex=0; - connectionRequest->connectionRequestSystemsMutex.Lock(); - while (connectionRequestGuidIndex < connectionRequest->connectionRequestSystems.Size()) - { - if (connectionRequest->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint<0) - { - anyRemoved=true; - connectionRequest->connectionRequestSystems.RemoveAtIndexFast(connectionRequestGuidIndex); - } - else - { - connectionRequestGuidIndex++; - } - } - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - if (anyRemoved) - { - if (connectionRequestIndex!=(unsigned int)-1) - { - // connectionRequestsMutex should be locked before calling this function - if (UpdateForwarding(connectionRequest)==false) - { - RemoveConnectionRequest(connectionRequestIndex); - } - else - { - connectionRequestIndex++; - } - } - else - { - connectionRequestIndex++; - } - } - else - { - connectionRequestIndex++; - } - } - else - { - connectionRequestIndex++; - } - } - connectionRequestsMutex.Unlock(); - - unsigned int i=0; - miniPunchesInProgressMutex.Lock(); - while (i < miniPunchesInProgress.Size()) - { - if (miniPunchesInProgress[i].timeoutminiPunchesInProgress[i].nextAction) - { - miniPunchesInProgress[i].nextAction=curTime+100; - SendOOBMessages(&miniPunchesInProgress[i]); - } - else - i++; - } - miniPunchesInProgressMutex.Unlock(); - -} -void Router2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) systemAddress; - - - unsigned int forwardedConnectionIndex=0; - forwardedConnectionListMutex.Lock(); - while (forwardedConnectionIndexShowDiagnostic(FormatStringTS(buff,"Closed connection to the %I64d, removing forwarding from list at %s:%i\n", rakNetGUID.g, __FILE__, __LINE__)); - } - - // No longer need forwarding - forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); - } - else if (forwardedConnectionList[forwardedConnectionIndex].intermediaryGuid==rakNetGUID && forwardedConnectionList[forwardedConnectionIndex].weInitiatedForwarding) - { - // Lost connection to intermediary. Restart process to connect to endpoint. If failed, push ID_CONNECTION_LOST. Also remove connection request if it already is in the list, to restart it - connectionRequestsMutex.Lock(); - unsigned int pos = GetConnectionRequestIndex(forwardedConnectionList[forwardedConnectionIndex].endpointGuid); - if((unsigned int)-1 != pos) {RakNet::OP_DELETE(connectionRequests[pos], __FILE__, __LINE__); connectionRequests.RemoveAtIndexFast(pos);} - connectionRequestsMutex.Unlock(); - - ConnectInternal(forwardedConnectionList[forwardedConnectionIndex].endpointGuid, true); - - forwardedConnectionIndex++; - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Closed connection %I64d, restarting forwarding at %s:%i\n",rakNetGUID.g, __FILE__, __LINE__)); - } - - // This should not be removed - the connection is still forwarded, but perhaps through another system -// forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); - } - else - forwardedConnectionIndex++; - } - forwardedConnectionListMutex.Unlock(); - - unsigned int connectionRequestIndex=0; - connectionRequestsMutex.Lock(); - while (connectionRequestIndex < connectionRequests.Size()) - { - ConnnectRequest *cr = connectionRequests[connectionRequestIndex]; - cr->connectionRequestSystemsMutex.Lock(); - unsigned int connectionRequestGuidIndex = cr->GetGuidIndex(rakNetGUID); - if (connectionRequestGuidIndex!=(unsigned int)-1) - { - cr->connectionRequestSystems.RemoveAtIndexFast(connectionRequestGuidIndex); - cr->connectionRequestSystemsMutex.Unlock(); - if (UpdateForwarding(cr)==false) // If returns false, no connection request systems left - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Aborted connection to the %I64d, aborted forwarding at %s:%i\n", rakNetGUID.g, __FILE__, __LINE__)); - } - - RemoveConnectionRequest(connectionRequestIndex); - } - else // Else a system in the connection request list dropped. If cr->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING then we are still waiting for other systems to respond. - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Aborted connection attempt to %I64d, restarting forwarding to %I64d at %s:%i\n", rakNetGUID.g,cr->endpointGuid.g,__FILE__, __LINE__)); - } -// if(volatile bool is_my_fix_a_truth = true) { // A system in the list of potential systems to try routing to dropped. There is no need to restart the whole process. -// connectionRequestsMutex.Lock(); -// connectionRequests.RemoveAtIndexFast(connectionRequestIndex); -// connectionRequestsMutex.Unlock(); -// -// if(false == ConnectInternal(cr->endpointGuid,cr->returnConnectionLostOnFailure)) -// if (debugInterface) -// { -// char buff[512]; -// debugInterface->ShowDiagnostic(FormatStringTS(buff,"ConnectInternal(cr->endpointGuid,cr->returnConnectionLostOnFailure) is false. at %s:%i\n", __FILE__, __LINE__)); -// } -// } - - connectionRequestIndex++; - } - } - else - { - cr->connectionRequestSystemsMutex.Unlock(); - connectionRequestIndex++; - } - } - connectionRequestsMutex.Unlock(); - - - unsigned int i=0; - miniPunchesInProgressMutex.Lock(); - while (i < miniPunchesInProgress.Size()) - { - if (miniPunchesInProgress[i].sourceGuid==rakNetGUID || miniPunchesInProgress[i].endpointGuid==rakNetGUID) - { - if (miniPunchesInProgress[i].sourceGuid!=rakNetGUID) - { - SendFailureOnCannotForward(miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].endpointGuid); - } - miniPunchesInProgress.RemoveAtIndexFast(i); - } - else - i++; - } - miniPunchesInProgressMutex.Unlock(); -} -void Router2::OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason) -{ - (void) failedConnectionAttemptReason; - (void) packet; - - unsigned int forwardedConnectionIndex=0; - forwardedConnectionListMutex.Lock(); - while (forwardedConnectionIndexsystemAddress) - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Failed connection attempt to forwarded system (%I64d : %s) at %s:%i\n", - forwardedConnectionList[forwardedConnectionIndex].endpointGuid.g, packet->systemAddress.ToString(true), __FILE__, __LINE__)); - } - - packet->guid=forwardedConnectionList[forwardedConnectionIndex].endpointGuid; - forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); - } - else - forwardedConnectionIndex++; - } - forwardedConnectionListMutex.Unlock(); -} -void Router2::OnRakPeerShutdown(void) -{ - ClearAll(); -} -// connectionRequestsMutex should already be locked -bool Router2::UpdateForwarding(ConnnectRequest* connectionRequest) -{ - connectionRequest->connectionRequestSystemsMutex.Lock(); - - // RAKNET_DEBUG_PRINTF(__FUNCTION__": connectionRequest->connectionRequestSystems.Size = %d", connectionRequest->connectionRequestSystems.Size()); - - if (connectionRequest->connectionRequestSystems.Size()==0) - { - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - // printf("Router2 failed at %s:%i\n", __FILE__, __LINE__); - if (connectionRequest->returnConnectionLostOnFailure) { - ReturnToUser(ID_CONNECTION_LOST, connectionRequest->endpointGuid, UNASSIGNED_SYSTEM_ADDRESS, true); // This is a connection which was previously established. Rerouting is not possible. -// bool sendDisconnectionNotification = false; -// rakPeerInterface->CloseConnection(rakPeerInterface->GetSystemAddressFromGuid(connectionRequest->endpointGuid), sendDisconnectionNotification); -// RAKNET_DEBUG_PRINTF(__FUNCTION__": call rakPeerInterface->CloseConnection(%I64d)" , connectionRequest->endpointGuid.g); - } - else - ReturnToUser(ID_ROUTER_2_FORWARDING_NO_PATH, connectionRequest->endpointGuid, UNASSIGNED_SYSTEM_ADDRESS, false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Forwarding failed, no remaining systems at %s:%i\n", _FILE_AND_LINE_)); - } - - forwardedConnectionListMutex.Lock(); - - for (unsigned int forwardedConnectionIndex=0; forwardedConnectionIndex < forwardedConnectionList.Size(); forwardedConnectionIndex++) - { - if (forwardedConnectionList[forwardedConnectionIndex].endpointGuid==connectionRequest->endpointGuid && forwardedConnectionList[forwardedConnectionIndex].weInitiatedForwarding) - { - forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); - break; - } - } - forwardedConnectionListMutex.Unlock(); - - return false; - } - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - if (connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING) - { - connectionRequest->connectionRequestSystemsMutex.Lock(); - - for (unsigned int i=0; i < connectionRequest->connectionRequestSystems.Size(); i++) - { - if (connectionRequest->connectionRequestSystems[i].pingToEndpoint<0) - { - connectionRequest->connectionRequestSystemsMutex.Unlock(); - return true; // Forward query still in progress, just return - } - } - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - RequestForwarding(connectionRequest); - } -// else if (connectionRequest->requestState==REQUEST_STATE_REQUEST_FORWARDING) -// { -// RequestForwarding(connectionRequestIndex); -// } - - return true; -} -// connectionRequestsMutex should already be locked -void Router2::RemoveConnectionRequest(unsigned int connectionRequestIndex) -{ - RakNet::OP_DELETE(connectionRequests[connectionRequestIndex],_FILE_AND_LINE_); - connectionRequests.RemoveAtIndexFast(connectionRequestIndex); -} -int ConnectionRequestSystemComp( const Router2::ConnectionRequestSystem & key, const Router2::ConnectionRequestSystem &data ) -{ - if (key.pingToEndpoint * (key.usedForwardingEntries+1) < data.pingToEndpoint * (data.usedForwardingEntries+1)) - return -1; - if (key.pingToEndpoint * (key.usedForwardingEntries+1) == data.pingToEndpoint * (data.usedForwardingEntries+1)) - return 1; - if (key.guid < data.guid) - return -1; - if (key.guid > data.guid) - return -1; - return 0; -} -// connectionRequestsMutex should already be locked -void Router2::RequestForwarding(ConnnectRequest* connectionRequest) -{ - RakAssert(connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING); - connectionRequest->requestState=REQUEST_STATE_REQUEST_FORWARDING; - - if (connectionRequest->GetGuidIndex(connectionRequest->lastRequestedForwardingSystem)!=(unsigned int)-1) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", _FILE_AND_LINE_)); - return; - } - - // Prioritize systems to request forwarding - DataStructures::OrderedList commandList; - unsigned int connectionRequestGuidIndex; - connectionRequest->connectionRequestSystemsMutex.Lock(); - for (connectionRequestGuidIndex=0; connectionRequestGuidIndex < connectionRequest->connectionRequestSystems.Size(); connectionRequestGuidIndex++) - { - RakAssert(connectionRequest->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint>=0); - commandList.Insert(connectionRequest->connectionRequestSystems[connectionRequestGuidIndex], - connectionRequest->connectionRequestSystems[connectionRequestGuidIndex], - true, - _FILE_AND_LINE_); - } - connectionRequest->connectionRequestSystemsMutex.Unlock(); - - connectionRequest->lastRequestedForwardingSystem=commandList[0].guid; - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); - bsOut.Write((unsigned char) ID_ROUTER_2_REQUEST_FORWARDING); - bsOut.Write(connectionRequest->endpointGuid); - rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,connectionRequest->lastRequestedForwardingSystem,false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REQUEST_FORWARDING " - "(connectionRequest->lastRequestedForwardingSystem = %I64d, connectionRequest->endpointGuid = %I64d) at %s:%i\n", - connectionRequest->lastRequestedForwardingSystem.g,connectionRequest->endpointGuid.g, __FILE__, __LINE__)); - } -} -void Router2::SendFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid) -{ - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); - bsOut.Write((unsigned char) ID_ROUTER_2_REPLY_FORWARDING); - bsOut.Write(endpointGuid); - bsOut.Write(false); - rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,sourceGuid,false); -} -int Router2::ReturnFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid) -{ - // If the number of systems we are currently forwarding>=maxForwarding, return ID_ROUTER_2_REPLY_FORWARDING,endpointGuid,false - if (udpForwarder==0 || udpForwarder->GetUsedForwardEntries()/2>maximumForwardingRequests) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d -> %I64d) at %s:%i\n", - sourceGuid.g, endpointGuid.g,__FILE__, __LINE__)); - SendFailureOnCannotForward(sourceGuid,endpointGuid); - return -1; - } - - // We cannot forward connections which are themselves forwarded. Return fail in that case - forwardedConnectionListMutex.Lock(); - for (unsigned int i=0; i < forwardedConnectionList.Size(); i++) - { - if ((forwardedConnectionList[i].endpointGuid==endpointGuid) - || (forwardedConnectionList[i].endpointGuid==sourceGuid)) // sourceGuid is here so you do not respond to routing requests from systems you are already routing through. - { - forwardedConnectionListMutex.Unlock(); - - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", __FILE__, __LINE__)); - SendFailureOnCannotForward(sourceGuid,endpointGuid); - return -1; - } - } - forwardedConnectionListMutex.Unlock(); - - int pingToEndpoint; - pingToEndpoint = rakPeerInterface->GetAveragePing(endpointGuid); - if (pingToEndpoint==-1) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d -> %I64d) at %s:%i\n", - sourceGuid.g, endpointGuid.g,__FILE__, __LINE__)); - - SendFailureOnCannotForward(sourceGuid,endpointGuid); - return -1; - } - return pingToEndpoint; -} -void Router2::OnQueryForwarding(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); - RakNetGUID endpointGuid; - // Read endpointGuid - bs.Read(endpointGuid); - - int pingToEndpoint = ReturnFailureOnCannotForward(packet->guid, endpointGuid); - if (pingToEndpoint==-1) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); - return; - } - - // If we are connected to endpointGuid, reply ID_ROUTER_2_REPLY_FORWARDING,endpointGuid,true,ping,numCurrentlyForwarding - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); - bsOut.Write((unsigned char) ID_ROUTER_2_REPLY_FORWARDING); - bsOut.Write(endpointGuid); - bsOut.Write(true); - bsOut.Write((unsigned short) pingToEndpoint); - bsOut.Write((unsigned short) udpForwarder->GetUsedForwardEntries()/2); - rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,packet->guid,false); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REPLY_FORWARDING to the %I64d at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); - } -} -void Router2::OnQueryForwardingReply(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); - RakNetGUID endpointGuid; - bs.Read(endpointGuid); - // Find endpointGuid among stored requests - bool canForward=false; - bs.Read(canForward); - - - connectionRequestsMutex.Lock(); - unsigned int connectionRequestIndex = GetConnectionRequestIndex(endpointGuid); - if (connectionRequestIndex==(unsigned int)-1) - { - connectionRequestsMutex.Unlock(); - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); - return; - } - - connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Lock(); - unsigned int connectionRequestGuidIndex = connectionRequests[connectionRequestIndex]->GetGuidIndex(packet->guid); - if (connectionRequestGuidIndex==(unsigned int)-1) - { - connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Unlock(); - connectionRequestsMutex.Unlock(); - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); - return; - } - - if (debugInterface) - { - char buff[512]; - char buff2[512]; - packet->systemAddress.ToString(true,buff2); - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_FORWARDING on address %s(%I64d -> %I64d) canForward=%i at %s:%i\n", - buff2, packet->guid.g, endpointGuid.g, canForward, __FILE__, __LINE__)); - } - - if (canForward) - { - unsigned short pingToEndpoint; - unsigned short usedEntries; - bs.Read(pingToEndpoint); - bs.Read(usedEntries); - connectionRequests[connectionRequestIndex]->connectionRequestSystems[connectionRequestGuidIndex].usedForwardingEntries=usedEntries; - connectionRequests[connectionRequestIndex]->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint=rakPeerInterface->GetAveragePing(packet->guid)+pingToEndpoint; - } - else - { - connectionRequests[connectionRequestIndex]->connectionRequestSystems.RemoveAtIndex(connectionRequestGuidIndex); - } - connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Unlock(); - - if (UpdateForwarding(connectionRequests[connectionRequestIndex])==false) - { - RemoveConnectionRequest(connectionRequestIndex); - } - connectionRequestsMutex.Unlock(); -} -void Router2::SendForwardingSuccess(MessageID messageId, RakNetGUID sourceGuid, RakNetGUID endpointGuid, unsigned short sourceToDstPort) -{ - RakNet::BitStream bsOut; - bsOut.Write(messageId); - bsOut.Write(endpointGuid); - bsOut.Write(sourceToDstPort); - rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,sourceGuid,false); - - if (debugInterface) - { - char buff[512]; - if (messageId==ID_ROUTER_2_FORWARDING_ESTABLISHED) - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_FORWARDING_ESTABLISHED at %s:%i\n", _FILE_AND_LINE_ )); - else - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REROUTED at %s:%i\n", _FILE_AND_LINE_ )); - } -} -void Router2::SendOOBFromRakNetPort(OutOfBandIdentifiers oob, BitStream *extraData, SystemAddress sa) -{ - RakNet::BitStream oobBs; - oobBs.Write((unsigned char)oob); - if (extraData) - { - extraData->ResetReadPointer(); - oobBs.Write(*extraData); - } - char ipAddressString[32]; - sa.ToString(false, ipAddressString); - rakPeerInterface->SendOutOfBand((const char*) ipAddressString,sa.GetPort(),(const char*) oobBs.GetData(),oobBs.GetNumberOfBytesUsed()); -} -void Router2::SendOOBFromSpecifiedSocket(OutOfBandIdentifiers oob, SystemAddress sa, SOCKET socket) -{ - RakNet::BitStream bs; - rakPeerInterface->WriteOutOfBandHeader(&bs); - bs.Write((unsigned char) oob); - SocketLayer::SendTo_PC( socket, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), sa, __FILE__, __LINE__ ); -} -void Router2::SendOOBMessages(Router2::MiniPunchRequest *mpr) -{ - // Mini NAT punch - // Send from srcToDestPort to packet->systemAddress (source). If the message arrives, the remote system should reply. - SendOOBFromSpecifiedSocket(ID_ROUTER_2_REPLY_TO_SENDER_PORT, mpr->sourceAddress, mpr->forwardingSocket); - - // Send from destToSourcePort to endpointSystemAddress (destination). If the message arrives, the remote system should reply. - SendOOBFromSpecifiedSocket(ID_ROUTER_2_REPLY_TO_SENDER_PORT, mpr->endpointAddress, mpr->forwardingSocket); - - - if (debugInterface) { - char buff [512]; - - char buff2[128]; - - mpr->sourceAddress .ToString(true,buff2); - - debugInterface->ShowDiagnostic(FormatStringTS(buff,"call SendOOBFromSpecifiedSocket(...,%s,...)", buff2)); - - mpr->endpointAddress .ToString(true,buff2); - - debugInterface->ShowDiagnostic(FormatStringTS(buff,"call SendOOBFromSpecifiedSocket(...,%s,...)", buff2)); - } - - // Tell source to send to forwardingPort - RakNet::BitStream extraData; - extraData.Write(mpr->forwardingPort); - RakAssert(mpr->forwardingPort!=0); - SendOOBFromRakNetPort(ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, &extraData, mpr->sourceAddress); - - // Tell destination to send to forwardingPort - extraData.Reset(); - extraData.Write(mpr->forwardingPort); - RakAssert(mpr->forwardingPort); - SendOOBFromRakNetPort(ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, &extraData, mpr->endpointAddress); -} -void Router2::OnRequestForwarding(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); - RakNetGUID endpointGuid; - bs.Read(endpointGuid); - - int pingToEndpoint = ReturnFailureOnCannotForward(packet->guid, endpointGuid); - if (pingToEndpoint==-1) - { - char buff[512]; - if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (packet->guid =%I64d, endpointGuid = %I64d) at %s:%i\n", - packet->guid.g, endpointGuid.g, __FILE__, __LINE__)); - return; - } - - unsigned short forwardingPort=0; - SOCKET forwardingSocket=0; - SystemAddress endpointSystemAddress = rakPeerInterface->GetSystemAddressFromGuid(endpointGuid); - UDPForwarderResult result = udpForwarder->StartForwarding( - packet->systemAddress, endpointSystemAddress, 30000, 0, socketFamily, - &forwardingPort, &forwardingSocket); - - if (result==UDPFORWARDER_FORWARDING_ALREADY_EXISTS) - { - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING, result=UDPFORWARDER_FORWARDING_ALREADY_EXISTS " - "(packet->guid =%I64d, endpointGuid = %I64d) at %s:%i\n", - packet->guid.g, endpointGuid.g,__FILE__, __LINE__)); - } - - SendForwardingSuccess(ID_ROUTER_2_FORWARDING_ESTABLISHED, packet->guid, endpointGuid, forwardingPort); - } - else if (result==UDPFORWARDER_NO_SOCKETS) - { - char buff[512]; - char buff2[64]; - char buff3[64]; - packet->systemAddress.ToString(true,buff2); - endpointSystemAddress.ToString(true,buff3); - if (debugInterface) - debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_NO_SOCKETS, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", - __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); - SendFailureOnCannotForward(packet->guid, endpointGuid); - } - else if (result==UDPFORWARDER_INVALID_PARAMETERS) - { - char buff[512]; - char buff2[64]; - char buff3[64]; - packet->systemAddress.ToString(true,buff2); - endpointSystemAddress.ToString(true,buff3); - if (debugInterface) - debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_INVALID_PARAMETERS, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", - __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); - SendFailureOnCannotForward(packet->guid, endpointGuid); - } - else if (result==UDPFORWARDER_BIND_FAILED) - { - char buff[512]; - char buff2[64]; - char buff3[64]; - packet->systemAddress.ToString(true,buff2); - endpointSystemAddress.ToString(true,buff3); - if (debugInterface) - debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_BIND_FAILED, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", - __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); - SendFailureOnCannotForward(packet->guid, endpointGuid); - } - else - { - if (debugInterface) - { - char buff2[32]; - char buff3[32]; - endpointSystemAddress.ToString(true,buff2); - packet->systemAddress.ToString(true,buff3); - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING.\n" - "endpointAddress=%s\nsourceAddress=%s\nforwardingPort=%i\n " - "calling SendOOBMessages at %s:%i\n", buff2,buff3,forwardingPort,_FILE_AND_LINE_)); - } - - // Store the punch request - MiniPunchRequest miniPunchRequest; - miniPunchRequest.endpointAddress=endpointSystemAddress; - miniPunchRequest.endpointGuid=endpointGuid; - miniPunchRequest.gotReplyFromEndpoint=false; - miniPunchRequest.gotReplyFromSource=false; - miniPunchRequest.sourceGuid=packet->guid; - miniPunchRequest.sourceAddress=packet->systemAddress; - miniPunchRequest.forwardingPort=forwardingPort; - miniPunchRequest.forwardingSocket=forwardingSocket; - int ping1 = rakPeerInterface->GetAveragePing(packet->guid); - int ping2 = rakPeerInterface->GetAveragePing(endpointGuid); - if (ping1>ping2) - miniPunchRequest.timeout=RakNet::GetTimeMS() + ping1*8+300; - else - miniPunchRequest.timeout=RakNet::GetTimeMS() + ping2*8+300; - miniPunchRequest.nextAction=RakNet::GetTimeMS()+100; - SendOOBMessages(&miniPunchRequest); - miniPunchesInProgressMutex.Lock(); - miniPunchesInProgress.Push(miniPunchRequest,_FILE_AND_LINE_); - miniPunchesInProgressMutex.Unlock(); - } -} -void Router2::OnMiniPunchReplyBounce(Packet *packet) -{ - // Find stored punch request - unsigned int i=0; - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE from guid=%I64d (miniPunchesInProgress.Size() = %d)", - packet->guid.g, miniPunchesInProgress.Size())); - } - - miniPunchesInProgressMutex.Lock(); - while (i < miniPunchesInProgress.Size()) - { - if (miniPunchesInProgress[i].sourceGuid==packet->guid || miniPunchesInProgress[i].endpointGuid==packet->guid) - { - if (miniPunchesInProgress[i].sourceGuid==packet->guid) - miniPunchesInProgress[i].gotReplyFromSource=true; - if (miniPunchesInProgress[i].endpointGuid==packet->guid) - miniPunchesInProgress[i].gotReplyFromEndpoint=true; - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Processing ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, gotReplyFromSource=%i gotReplyFromEndpoint=%i at %s:%i\n", miniPunchesInProgress[i].gotReplyFromSource, miniPunchesInProgress[i].gotReplyFromEndpoint, __FILE__, __LINE__)); - } - - if (miniPunchesInProgress[i].gotReplyFromEndpoint==true && - miniPunchesInProgress[i].gotReplyFromSource==true) - { - SendForwardingSuccess(ID_ROUTER_2_REROUTED, miniPunchesInProgress[i].endpointGuid, miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].forwardingPort); - SendForwardingSuccess(ID_ROUTER_2_FORWARDING_ESTABLISHED, miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].endpointGuid, miniPunchesInProgress[i].forwardingPort); - miniPunchesInProgress.RemoveAtIndexFast(i); - } - else - { - i++; - } - } - else - i++; - } - miniPunchesInProgressMutex.Unlock(); -} -void Router2::OnMiniPunchReply(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); - RakNetGUID routerGuid; - bs.Read(routerGuid); - SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, 0, rakPeerInterface->GetSystemAddressFromGuid(routerGuid)); - - if (debugInterface) - { - char buff[512]; - - char buff2[512]; - - rakPeerInterface->GetSystemAddressFromGuid(routerGuid).ToString(true,buff2); - - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE (%s) at %s:%i\n", buff2, __FILE__, __LINE__)); - } -} -void Router2::OnRerouted(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID endpointGuid; - bs.Read(endpointGuid); - unsigned short sourceToDestPort; - bs.Read(sourceToDestPort); - - // Return rerouted notice - SystemAddress intermediaryAddress=packet->systemAddress; - intermediaryAddress.SetPort(sourceToDestPort); - rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); - - unsigned int forwardingIndex; - forwardedConnectionListMutex.Lock(); - for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) - { - if (forwardedConnectionList[forwardingIndex].endpointGuid==endpointGuid) - break; - } - - if (forwardingIndexsystemAddress; - ref_fc.intermediaryAddress.SetPort(sourceToDestPort); - ref_fc.intermediaryGuid = packet->guid; - - rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"FIX: Got ID_ROUTER_2_REROUTE, returning ID_ROUTER_2_REROUTED," - " Calling RakPeer::ChangeSystemAddress(%I64d, %s) at %s:%i\n",endpointGuid.g, intermediaryAddress.ToString(true), __FILE__, __LINE__)); - } - } - else - { - ForwardedConnection fc; - fc.endpointGuid=endpointGuid; - fc.intermediaryAddress=packet->systemAddress; - fc.intermediaryAddress.SetPort(sourceToDestPort); - fc.intermediaryGuid=packet->guid; - fc.weInitiatedForwarding=false; - // add to forwarding list. This is only here to avoid reporting direct connections in Router2::ReturnFailureOnCannotForward - forwardedConnectionList.Push (fc,__FILE__, __LINE__); - forwardedConnectionListMutex.Unlock(); - - rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REROUTE, returning ID_ROUTER_2_REROUTED, Calling RakPeer::ChangeSystemAddress at %s:%i\n", __FILE__, __LINE__)); - } - } - -} -bool Router2::OnForwardingSuccess(Packet *packet) -{ - RakNet::BitStream bs(packet->data, packet->length, false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID endpointGuid; - bs.Read(endpointGuid); - unsigned short sourceToDestPort; - bs.Read(sourceToDestPort); - - unsigned int forwardingIndex; - forwardedConnectionListMutex.Lock(); - for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) - { - if (forwardedConnectionList[forwardingIndex].endpointGuid==endpointGuid) - break; - } - - if (forwardingIndexsystemAddress; - intermediaryAddress.SetPort(sourceToDestPort); - rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); - - //////////////////////////////////////////////////////////////////////////// - ForwardedConnection& ref_fc = forwardedConnectionList[forwardingIndex]; - ref_fc.intermediaryAddress = packet->systemAddress; - ref_fc.intermediaryAddress.SetPort(sourceToDestPort); - ref_fc.intermediaryGuid = packet->guid; - //////////////////////////////////////////////////////////////////////////// - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_FORWARDING_ESTABLISHED, returning ID_ROUTER_2_REROUTED, Calling RakPeer::ChangeSystemAddress at %s:%i\n", _FILE_AND_LINE_)); - } - - packet->data[0]=ID_ROUTER_2_REROUTED; - - forwardedConnectionListMutex.Unlock(); - return true; // Return packet to user - } - else - { - forwardedConnectionListMutex.Unlock(); - - // removeFrom connectionRequests; - ForwardedConnection fc; - connectionRequestsMutex.Lock(); - unsigned int connectionRequestIndex = GetConnectionRequestIndex(endpointGuid); - fc.returnConnectionLostOnFailure=connectionRequests[connectionRequestIndex]->returnConnectionLostOnFailure; - connectionRequests.RemoveAtIndexFast(connectionRequestIndex); - connectionRequestsMutex.Unlock(); - fc.endpointGuid=endpointGuid; - fc.intermediaryAddress=packet->systemAddress; - fc.intermediaryAddress.SetPort(sourceToDestPort); - fc.intermediaryGuid=packet->guid; - fc.weInitiatedForwarding=true; - - // add to forwarding list - forwardedConnectionListMutex.Lock(); - forwardedConnectionList.Push (fc,_FILE_AND_LINE_); - forwardedConnectionListMutex.Unlock(); - - if (debugInterface) - { - char buff[512]; - debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got and returning to user ID_ROUTER_2_FORWARDING_ESTABLISHED at %s:%i\n", _FILE_AND_LINE_)); - } - - } - return true; // Return packet to user -} -int Router2::GetLargestPingAmongConnectedSystems(void) const -{ - int avePing; - int largestPing=-1; - unsigned short maxPeers = rakPeerInterface->GetMaximumNumberOfPeers(); - if (maxPeers==0) - return 9999; - unsigned short index; - for (index=0; index < rakPeerInterface->GetMaximumNumberOfPeers(); index++) - { - RakNetGUID g = rakPeerInterface->GetGUIDFromIndex(index); - if (g!=UNASSIGNED_RAKNET_GUID) - { - avePing=rakPeerInterface->GetAveragePing(rakPeerInterface->GetGUIDFromIndex(index)); - if (avePing>largestPing) - largestPing=avePing; - } - } - return largestPing; -} - -unsigned int Router2::GetConnectionRequestIndex(RakNetGUID endpointGuid) -{ - unsigned int i; - for (i=0; i < connectionRequests.Size(); i++) - { - if (connectionRequests[i]->endpointGuid==endpointGuid) - return i; - } - return (unsigned int) -1; -} -unsigned int Router2::ConnnectRequest::GetGuidIndex(RakNetGUID guid) -{ - unsigned int i; - for (i=0; i < connectionRequestSystems.Size(); i++) - { - if (connectionRequestSystems[i].guid==guid) - return i; - } - return (unsigned int) -1; -} -void Router2::ReturnToUser(MessageID messageId, RakNetGUID endpointGuid, const SystemAddress &systemAddress, bool wasGeneratedLocally) -{ - Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); - p->data[0]=messageId; - p->systemAddress=systemAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=endpointGuid; - p->wasGeneratedLocally=wasGeneratedLocally; - rakPeerInterface->PushBackPacket(p, true); -} -void Router2::ClearForwardedConnections(void) -{ - forwardedConnectionListMutex.Lock(); - forwardedConnectionList.Clear(false,_FILE_AND_LINE_); - forwardedConnectionListMutex.Unlock(); -} -void Router2::ClearAll(void) -{ - ClearConnectionRequests(); - ClearMinipunches(); - ClearForwardedConnections(); -} -void Router2::SetDebugInterface(Router2DebugInterface *_debugInterface) -{ - debugInterface=_debugInterface; -} -Router2DebugInterface *Router2::GetDebugInterface(void) const -{ - return debugInterface; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_Router2==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#include "Router2.h" +#include "RakPeerInterface.h" +#include "BitStream.h" +#include "RakNetTime.h" +#include "GetTime.h" +#include "DS_OrderedList.h" +#include "SocketLayer.h" +#include "FormatString.h" + +using namespace RakNet; + +/* +Algorithm: + +1. Sender calls ConnectInternal(). A ConnnectRequest structure is allocated and stored in the connectionRequests list, containing a list of every system we are connected to. ID_ROUTER_2_QUERY_FORWARDING is sent to all connected systems. + +2. Upon the router getting ID_ROUTER_2_QUERY_FORWARDING, ID_ROUTER_2_REPLY_FORWARDING is sent to the sender indicating if that router is connected to the endpoint, along with the ping from the router to the endpoint. + +3. Upon the sender getting ID_ROUTER_2_REPLY_FORWARDING, the connection request structure is looked up in Router2::UpdateForwarding. The ping is stored in that structure. Once all systems have replied, the system continues to the next state. If every system in step 1 has been exhausted, and routing has occured at least once, then ID_CONNECTION_LOST is returned. If every system in step 1 has been exhausted and routing has never occured, then ID_ROUTER_2_FORWARDING_NO_PATH is returned. Otherwise, the router with the lowest ping is chosen, and RequestForwarding() is called with that system, which sends ID_ROUTER_2_REQUEST_FORWARDING to the router. + +4. When the router gets ID_ROUTER_2_REQUEST_FORWARDING, a MiniPunchRequest structure is allocated and stored in the miniPunchesInProgress list. The function SendOOBMessages() sends ID_ROUTER_2_REPLY_TO_SENDER_PORT from the routing sockets to both the sender and endpoint. It also sends ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT through the regular RakNet connection. + +5. The sender and endpoint should get ID_ROUTER_2_REPLY_TO_SENDER_PORT and/or ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT depending on what type of router they have. If ID_ROUTER_2_REPLY_TO_SENDER_PORT arrives, then this will reply back to the routing socket directly. If ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT arrives, then the reply port is modified to be the port specified by the router system. In both cases, ID_ROUTER_2_MINI_PUNCH_REPLY is sent. As the router has already setup the forwarding, ID_ROUTER_2_MINI_PUNCH_REPLY will actually arrive to the endpoint from the sender, and from the sender to the endpoint. + +6. When ID_ROUTER_2_MINI_PUNCH_REPLY arrives, ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE will be sent to the router. This is to tell the router that the forwarding has succeeded. + +7. When ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE arrives on the router, the router will find the two systems in the miniPunchesInProgress list, which was added in step 4 (See OnMiniPunchReplyBounce()). gotReplyFromSource or gotReplyFromEndpoint will be set to true, depending on the sender. When both gotReplyFromSource and gotReplyFromEndpoint have replied, then ID_ROUTER_2_REROUTE is sent to the endpoint, and ID_ROUTER_2_FORWARDING_ESTABLISHED is sent to the sender. + +8. When the endpoint gets ID_ROUTER_2_REROUTE, the system address is updated for the guid of the sender using RakPeer::ChangeSystemAddress(). This happens in OnRerouted(). + +9. When the sender gets ID_ROUTER_2_FORWARDING_ESTABLISHED, then in OnForwardingSuccess() the endpoint is removed from the connectionRequest list and moved to the forwardedConnectionList. + +10. In OnClosedConnection(), for the sender, if the closed connection is the endpoint, then the endpoint is removed from the forwardedConnectionList (this is a graceful disconnect). If the connection was instead lost to the router, then ConnectInternal() gets called, which goes back to step 1. If instead this was a connection requset in progress, then UpdateForwarding() gets called, which goes back to step 3. + +11. When the user connects the endpoint and sender, then the sender will get ID_CONNECTION_REQUEST_ACCEPTED. The sender will look up the endpoint in the forwardedConnectionList, and send ID_ROUTER_2_INCREASE_TIMEOUT to the endpoint. This message will call SetTimeoutTime() on the endpoint, so that if the router disconnects, enough time is available for the reroute to complete. +*/ + +#define MIN_MINIPUNCH_TIMEOUT 5000 + + + + +void Router2DebugInterface::ShowFailure(const char *message) +{ + printf("%s", message); +} +void Router2DebugInterface::ShowDiagnostic(const char *message) +{ + printf("%s", message); +} + +enum Router2MessageIdentifiers +{ + ID_ROUTER_2_QUERY_FORWARDING, + ID_ROUTER_2_REPLY_FORWARDING, + ID_ROUTER_2_REQUEST_FORWARDING, + ID_ROUTER_2_INCREASE_TIMEOUT, +}; +Router2::ConnnectRequest::ConnnectRequest() +{ + +} +Router2::ConnnectRequest::~ConnnectRequest() +{ + +} + +STATIC_FACTORY_DEFINITIONS(Router2,Router2); + +Router2::Router2() +{ + udpForwarder=0; + maximumForwardingRequests=0; + debugInterface=0; + socketFamily=AF_INET; +} +Router2::~Router2() +{ + ClearAll(); + + if (udpForwarder) + { + udpForwarder->Shutdown(); + RakNet::OP_DELETE(udpForwarder,_FILE_AND_LINE_); + } +} +void Router2::ClearMinipunches(void) +{ + miniPunchesInProgressMutex.Lock(); + miniPunchesInProgress.Clear(false,_FILE_AND_LINE_); + miniPunchesInProgressMutex.Unlock(); +} +void Router2::ClearConnectionRequests(void) +{ + connectionRequestsMutex.Lock(); + for (unsigned int i=0; i < connectionRequests.Size(); i++) + { + RakNet::OP_DELETE(connectionRequests[i],_FILE_AND_LINE_); + } + connectionRequests.Clear(false,_FILE_AND_LINE_); + connectionRequestsMutex.Unlock(); +} +bool Router2::ConnectInternal(RakNetGUID endpointGuid, bool returnConnectionLostOnFailure) +{ + int largestPing = GetLargestPingAmongConnectedSystems(); + if (largestPing<0) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2: ConnectInternal(%I64d) failed at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); + + // Not connected to anyone + return false; + } + + // ALready in progress? + connectionRequestsMutex.Lock(); + if (GetConnectionRequestIndex(endpointGuid)!=(unsigned int)-1) + { + connectionRequestsMutex.Unlock(); + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2: ConnectInternal(%I64d) failed at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); + + return false; + } + connectionRequestsMutex.Unlock(); + + // StoreRequest(endpointGuid, Largest(ping*2), systemsSentTo). Set state REQUEST_STATE_QUERY_FORWARDING + Router2::ConnnectRequest *cr = RakNet::OP_NEW(_FILE_AND_LINE_); + DataStructures::List addresses; + DataStructures::List guids; + rakPeerInterface->GetSystemList(addresses, guids); + if (guids.Size()==0) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", _FILE_AND_LINE_)); + + return false; + } + cr->requestState=R2RS_REQUEST_STATE_QUERY_FORWARDING; + cr->pingTimeout=RakNet::GetTimeMS()+largestPing*2+1000; + cr->endpointGuid=endpointGuid; + cr->returnConnectionLostOnFailure=returnConnectionLostOnFailure; + for (unsigned int i=0; i < guids.Size(); i++) + { + ConnectionRequestSystem crs; + if (guids[i]!=endpointGuid) + { + crs.guid=guids[i]; + crs.pingToEndpoint=-1; + cr->connectionRequestSystemsMutex.Lock(); + cr->connectionRequestSystems.Push(crs,_FILE_AND_LINE_); + cr->connectionRequestSystemsMutex.Unlock(); + + // Broadcast(ID_ROUTER_2_QUERY_FORWARDING, endpointGuid); + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); + bsOut.Write((unsigned char) ID_ROUTER_2_QUERY_FORWARDING); + bsOut.Write(endpointGuid); + uint32_t pack_id = rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,crs.guid,false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Router2::ConnectInternal: at %s:%i, pack_id = %d", __FILE__, __LINE__,pack_id)); + } + + } + else + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Router2::ConnectInternal: at %s:%i [else ..].: %I64d==%I64d", __FILE__, __LINE__, + guids[i].g,endpointGuid.g)); + } + } + } + connectionRequestsMutex.Lock(); + connectionRequests.Push(cr,_FILE_AND_LINE_); + connectionRequestsMutex.Unlock(); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Broadcasting ID_ROUTER_2_QUERY_FORWARDING to %I64d at %s:%i\n", endpointGuid.g , __FILE__, __LINE__)); + } + + return true; +} +void Router2::SetSocketFamily(unsigned short _socketFamily) +{ + socketFamily=_socketFamily; +} +void Router2::EstablishRouting(RakNetGUID endpointGuid) +{ + ConnectionState cs = rakPeerInterface->GetConnectionState(endpointGuid); + if (cs!=IS_DISCONNECTED && cs!=IS_NOT_CONNECTED) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i " + "(already connected to the %I64d)\n", __FILE__, __LINE__, endpointGuid.g )); + return; + } + + ConnectInternal(endpointGuid,false); +} +void Router2::SetMaximumForwardingRequests(int max) +{ + if (max>0 && maximumForwardingRequests<=0) + { + udpForwarder = RakNet::OP_NEW(_FILE_AND_LINE_); + udpForwarder->Startup(); + } + else if (max<=0 && maximumForwardingRequests>0) + { + udpForwarder->Shutdown(); + RakNet::OP_DELETE(udpForwarder,_FILE_AND_LINE_); + udpForwarder=0; + } + + maximumForwardingRequests=max; +} +PluginReceiveResult Router2::OnReceive(Packet *packet) +{ + SystemAddress sa; + RakNet::BitStream bs(packet->data,packet->length,false); + if (packet->data[0]==ID_ROUTER_2_INTERNAL) + { + switch (packet->data[1]) + { + case ID_ROUTER_2_QUERY_FORWARDING: + { + OnQueryForwarding(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_REPLY_FORWARDING: + { + OnQueryForwardingReply(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_REQUEST_FORWARDING: + { + + if (debugInterface) + { + char buff[512]; + char buff2[32]; + packet->systemAddress.ToString(true,buff2); + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING on ip %s from %I64d, ", + buff2,packet->guid.g)); + } + + OnRequestForwarding(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_INCREASE_TIMEOUT: + { + /// The routed system wants more time to stay alive on no communication, in case the router drops or crashes + rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + } + else if (packet->data[0]==ID_OUT_OF_BAND_INTERNAL && packet->length>=2) + { + switch (packet->data[1]) + { + case ID_ROUTER_2_REPLY_TO_SENDER_PORT: + { + RakNet::BitStream bsOut; + bsOut.Write(packet->guid); + SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, packet->systemAddress); + + if (debugInterface) + { + char buff[512]; + char buff2[32]; + sa.ToString(false,buff2); + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT %i on address %s, replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, _FILE_AND_LINE_)); + +// packet->systemAddress.ToString(true,buff2); +// debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT on address %s (%I64d), " +// "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", buff2,packet->guid.g, __FILE__, __LINE__)); + } + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT: + { + RakNet::BitStream bsOut; + bsOut.Write(packet->guid); + bs.IgnoreBytes(2); + sa=packet->systemAddress; + unsigned short port; + bs.Read(port); + sa.SetPort(port); + RakAssert(sa.GetPort()!=0); + SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, sa); + + if (debugInterface) + { + char buff[512]; + char buff2[32]; + sa.ToString(false,buff2); + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT %i on address %s, " + "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, __FILE__, __LINE__)); + } + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_ROUTER_2_MINI_PUNCH_REPLY: + OnMiniPunchReply(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE: + OnMiniPunchReplyBounce(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + else if (packet->data[0]==ID_ROUTER_2_FORWARDING_ESTABLISHED) + { +// printf("Got ID_ROUTER_2_FORWARDING_ESTABLISHED\n"); + if (OnForwardingSuccess(packet)==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + else if (packet->data[0]==ID_ROUTER_2_REROUTED) + { + OnRerouted(packet); + } + else if (packet->data[0]==ID_CONNECTION_REQUEST_ACCEPTED) + { + unsigned int forwardingIndex; + forwardedConnectionListMutex.Lock(); + for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) + { + if (forwardedConnectionList[forwardingIndex].endpointGuid==packet->guid && forwardedConnectionList[forwardingIndex].weInitiatedForwarding) + break; + } + + if (forwardingIndexSend(&bsOut,HIGH_PRIORITY,RELIABLE,0,packet->guid,false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_CONNECTION_REQUEST_ACCEPTED, " + "sending ID_ROUTER_2_INCREASE_TIMEOUT to the %I64d at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); + } + + // Also take longer ourselves + rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress); + } + else { + // ~Gwynn: Fix for Receive hanging up problem on Windows XP + // See http://blog.delphi-jedi.net/2008/04/23/the-case-of-the-unexplained-dead-lock-in-a-single-thread/ for details + forwardedConnectionListMutex.Unlock(); + } + } + else if (packet->data[0]==ID_ROUTER_2_FORWARDING_NO_PATH) + { + if (packet->wasGeneratedLocally==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + return RR_CONTINUE_PROCESSING; +} +void Router2::Update(void) +{ + RakNet::TimeMS curTime = RakNet::GetTimeMS(); + unsigned int connectionRequestIndex=0; + connectionRequestsMutex.Lock(); + while (connectionRequestIndex < connectionRequests.Size()) + { + ConnnectRequest* connectionRequest = connectionRequests[connectionRequestIndex]; + + // pingTimeout is only used with R2RS_REQUEST_STATE_QUERY_FORWARDING + if (connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING && + connectionRequest->pingTimeout < curTime) + { + bool anyRemoved=false; + unsigned int connectionRequestGuidIndex=0; + connectionRequest->connectionRequestSystemsMutex.Lock(); + while (connectionRequestGuidIndex < connectionRequest->connectionRequestSystems.Size()) + { + if (connectionRequest->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint<0) + { + anyRemoved=true; + connectionRequest->connectionRequestSystems.RemoveAtIndexFast(connectionRequestGuidIndex); + } + else + { + connectionRequestGuidIndex++; + } + } + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + if (anyRemoved) + { + if (connectionRequestIndex!=(unsigned int)-1) + { + // connectionRequestsMutex should be locked before calling this function + if (UpdateForwarding(connectionRequest)==false) + { + RemoveConnectionRequest(connectionRequestIndex); + } + else + { + connectionRequestIndex++; + } + } + else + { + connectionRequestIndex++; + } + } + else + { + connectionRequestIndex++; + } + } + else + { + connectionRequestIndex++; + } + } + connectionRequestsMutex.Unlock(); + + unsigned int i=0; + miniPunchesInProgressMutex.Lock(); + while (i < miniPunchesInProgress.Size()) + { + if (miniPunchesInProgress[i].timeoutminiPunchesInProgress[i].nextAction) + { + miniPunchesInProgress[i].nextAction=curTime+100; + SendOOBMessages(&miniPunchesInProgress[i]); + } + else + i++; + } + miniPunchesInProgressMutex.Unlock(); + +} +void Router2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) systemAddress; + + + unsigned int forwardedConnectionIndex=0; + forwardedConnectionListMutex.Lock(); + while (forwardedConnectionIndexShowDiagnostic(FormatStringTS(buff,"Closed connection to the %I64d, removing forwarding from list at %s:%i\n", rakNetGUID.g, __FILE__, __LINE__)); + } + + // No longer need forwarding + forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); + } + else if (forwardedConnectionList[forwardedConnectionIndex].intermediaryGuid==rakNetGUID && forwardedConnectionList[forwardedConnectionIndex].weInitiatedForwarding) + { + // Lost connection to intermediary. Restart process to connect to endpoint. If failed, push ID_CONNECTION_LOST. Also remove connection request if it already is in the list, to restart it + connectionRequestsMutex.Lock(); + unsigned int pos = GetConnectionRequestIndex(forwardedConnectionList[forwardedConnectionIndex].endpointGuid); + if((unsigned int)-1 != pos) {RakNet::OP_DELETE(connectionRequests[pos], __FILE__, __LINE__); connectionRequests.RemoveAtIndexFast(pos);} + connectionRequestsMutex.Unlock(); + + ConnectInternal(forwardedConnectionList[forwardedConnectionIndex].endpointGuid, true); + + forwardedConnectionIndex++; + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Closed connection %I64d, restarting forwarding at %s:%i\n",rakNetGUID.g, __FILE__, __LINE__)); + } + + // This should not be removed - the connection is still forwarded, but perhaps through another system +// forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); + } + else + forwardedConnectionIndex++; + } + forwardedConnectionListMutex.Unlock(); + + unsigned int connectionRequestIndex=0; + connectionRequestsMutex.Lock(); + while (connectionRequestIndex < connectionRequests.Size()) + { + ConnnectRequest *cr = connectionRequests[connectionRequestIndex]; + cr->connectionRequestSystemsMutex.Lock(); + unsigned int connectionRequestGuidIndex = cr->GetGuidIndex(rakNetGUID); + if (connectionRequestGuidIndex!=(unsigned int)-1) + { + cr->connectionRequestSystems.RemoveAtIndexFast(connectionRequestGuidIndex); + cr->connectionRequestSystemsMutex.Unlock(); + if (UpdateForwarding(cr)==false) // If returns false, no connection request systems left + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Aborted connection to the %I64d, aborted forwarding at %s:%i\n", rakNetGUID.g, __FILE__, __LINE__)); + } + + RemoveConnectionRequest(connectionRequestIndex); + } + else // Else a system in the connection request list dropped. If cr->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING then we are still waiting for other systems to respond. + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Aborted connection attempt to %I64d, restarting forwarding to %I64d at %s:%i\n", rakNetGUID.g,cr->endpointGuid.g,__FILE__, __LINE__)); + } +// if(volatile bool is_my_fix_a_truth = true) { // A system in the list of potential systems to try routing to dropped. There is no need to restart the whole process. +// connectionRequestsMutex.Lock(); +// connectionRequests.RemoveAtIndexFast(connectionRequestIndex); +// connectionRequestsMutex.Unlock(); +// +// if(false == ConnectInternal(cr->endpointGuid,cr->returnConnectionLostOnFailure)) +// if (debugInterface) +// { +// char buff[512]; +// debugInterface->ShowDiagnostic(FormatStringTS(buff,"ConnectInternal(cr->endpointGuid,cr->returnConnectionLostOnFailure) is false. at %s:%i\n", __FILE__, __LINE__)); +// } +// } + + connectionRequestIndex++; + } + } + else + { + cr->connectionRequestSystemsMutex.Unlock(); + connectionRequestIndex++; + } + } + connectionRequestsMutex.Unlock(); + + + unsigned int i=0; + miniPunchesInProgressMutex.Lock(); + while (i < miniPunchesInProgress.Size()) + { + if (miniPunchesInProgress[i].sourceGuid==rakNetGUID || miniPunchesInProgress[i].endpointGuid==rakNetGUID) + { + if (miniPunchesInProgress[i].sourceGuid!=rakNetGUID) + { + SendFailureOnCannotForward(miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].endpointGuid); + } + miniPunchesInProgress.RemoveAtIndexFast(i); + } + else + i++; + } + miniPunchesInProgressMutex.Unlock(); +} +void Router2::OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason) +{ + (void) failedConnectionAttemptReason; + (void) packet; + + unsigned int forwardedConnectionIndex=0; + forwardedConnectionListMutex.Lock(); + while (forwardedConnectionIndexsystemAddress) + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Failed connection attempt to forwarded system (%I64d : %s) at %s:%i\n", + forwardedConnectionList[forwardedConnectionIndex].endpointGuid.g, packet->systemAddress.ToString(true), __FILE__, __LINE__)); + } + + packet->guid=forwardedConnectionList[forwardedConnectionIndex].endpointGuid; + forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); + } + else + forwardedConnectionIndex++; + } + forwardedConnectionListMutex.Unlock(); +} +void Router2::OnRakPeerShutdown(void) +{ + ClearAll(); +} +// connectionRequestsMutex should already be locked +bool Router2::UpdateForwarding(ConnnectRequest* connectionRequest) +{ + connectionRequest->connectionRequestSystemsMutex.Lock(); + + // RAKNET_DEBUG_PRINTF(__FUNCTION__": connectionRequest->connectionRequestSystems.Size = %d", connectionRequest->connectionRequestSystems.Size()); + + if (connectionRequest->connectionRequestSystems.Size()==0) + { + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + // printf("Router2 failed at %s:%i\n", __FILE__, __LINE__); + if (connectionRequest->returnConnectionLostOnFailure) { + ReturnToUser(ID_CONNECTION_LOST, connectionRequest->endpointGuid, UNASSIGNED_SYSTEM_ADDRESS, true); // This is a connection which was previously established. Rerouting is not possible. +// bool sendDisconnectionNotification = false; +// rakPeerInterface->CloseConnection(rakPeerInterface->GetSystemAddressFromGuid(connectionRequest->endpointGuid), sendDisconnectionNotification); +// RAKNET_DEBUG_PRINTF(__FUNCTION__": call rakPeerInterface->CloseConnection(%I64d)" , connectionRequest->endpointGuid.g); + } + else + ReturnToUser(ID_ROUTER_2_FORWARDING_NO_PATH, connectionRequest->endpointGuid, UNASSIGNED_SYSTEM_ADDRESS, false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Forwarding failed, no remaining systems at %s:%i\n", _FILE_AND_LINE_)); + } + + forwardedConnectionListMutex.Lock(); + + for (unsigned int forwardedConnectionIndex=0; forwardedConnectionIndex < forwardedConnectionList.Size(); forwardedConnectionIndex++) + { + if (forwardedConnectionList[forwardedConnectionIndex].endpointGuid==connectionRequest->endpointGuid && forwardedConnectionList[forwardedConnectionIndex].weInitiatedForwarding) + { + forwardedConnectionList.RemoveAtIndexFast(forwardedConnectionIndex); + break; + } + } + forwardedConnectionListMutex.Unlock(); + + return false; + } + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + if (connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING) + { + connectionRequest->connectionRequestSystemsMutex.Lock(); + + for (unsigned int i=0; i < connectionRequest->connectionRequestSystems.Size(); i++) + { + if (connectionRequest->connectionRequestSystems[i].pingToEndpoint<0) + { + connectionRequest->connectionRequestSystemsMutex.Unlock(); + return true; // Forward query still in progress, just return + } + } + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + RequestForwarding(connectionRequest); + } +// else if (connectionRequest->requestState==REQUEST_STATE_REQUEST_FORWARDING) +// { +// RequestForwarding(connectionRequestIndex); +// } + + return true; +} +// connectionRequestsMutex should already be locked +void Router2::RemoveConnectionRequest(unsigned int connectionRequestIndex) +{ + RakNet::OP_DELETE(connectionRequests[connectionRequestIndex],_FILE_AND_LINE_); + connectionRequests.RemoveAtIndexFast(connectionRequestIndex); +} +int ConnectionRequestSystemComp( const Router2::ConnectionRequestSystem & key, const Router2::ConnectionRequestSystem &data ) +{ + if (key.pingToEndpoint * (key.usedForwardingEntries+1) < data.pingToEndpoint * (data.usedForwardingEntries+1)) + return -1; + if (key.pingToEndpoint * (key.usedForwardingEntries+1) == data.pingToEndpoint * (data.usedForwardingEntries+1)) + return 1; + if (key.guid < data.guid) + return -1; + if (key.guid > data.guid) + return -1; + return 0; +} +// connectionRequestsMutex should already be locked +void Router2::RequestForwarding(ConnnectRequest* connectionRequest) +{ + RakAssert(connectionRequest->requestState==R2RS_REQUEST_STATE_QUERY_FORWARDING); + connectionRequest->requestState=REQUEST_STATE_REQUEST_FORWARDING; + + if (connectionRequest->GetGuidIndex(connectionRequest->lastRequestedForwardingSystem)!=(unsigned int)-1) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", _FILE_AND_LINE_)); + return; + } + + // Prioritize systems to request forwarding + DataStructures::OrderedList commandList; + unsigned int connectionRequestGuidIndex; + connectionRequest->connectionRequestSystemsMutex.Lock(); + for (connectionRequestGuidIndex=0; connectionRequestGuidIndex < connectionRequest->connectionRequestSystems.Size(); connectionRequestGuidIndex++) + { + RakAssert(connectionRequest->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint>=0); + commandList.Insert(connectionRequest->connectionRequestSystems[connectionRequestGuidIndex], + connectionRequest->connectionRequestSystems[connectionRequestGuidIndex], + true, + _FILE_AND_LINE_); + } + connectionRequest->connectionRequestSystemsMutex.Unlock(); + + connectionRequest->lastRequestedForwardingSystem=commandList[0].guid; + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); + bsOut.Write((unsigned char) ID_ROUTER_2_REQUEST_FORWARDING); + bsOut.Write(connectionRequest->endpointGuid); + rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,connectionRequest->lastRequestedForwardingSystem,false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REQUEST_FORWARDING " + "(connectionRequest->lastRequestedForwardingSystem = %I64d, connectionRequest->endpointGuid = %I64d) at %s:%i\n", + connectionRequest->lastRequestedForwardingSystem.g,connectionRequest->endpointGuid.g, __FILE__, __LINE__)); + } +} +void Router2::SendFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid) +{ + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); + bsOut.Write((unsigned char) ID_ROUTER_2_REPLY_FORWARDING); + bsOut.Write(endpointGuid); + bsOut.Write(false); + rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,sourceGuid,false); +} +int Router2::ReturnFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid) +{ + // If the number of systems we are currently forwarding>=maxForwarding, return ID_ROUTER_2_REPLY_FORWARDING,endpointGuid,false + if (udpForwarder==0 || udpForwarder->GetUsedForwardEntries()/2>maximumForwardingRequests) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d -> %I64d) at %s:%i\n", + sourceGuid.g, endpointGuid.g,__FILE__, __LINE__)); + SendFailureOnCannotForward(sourceGuid,endpointGuid); + return -1; + } + + // We cannot forward connections which are themselves forwarded. Return fail in that case + forwardedConnectionListMutex.Lock(); + for (unsigned int i=0; i < forwardedConnectionList.Size(); i++) + { + if ((forwardedConnectionList[i].endpointGuid==endpointGuid) + || (forwardedConnectionList[i].endpointGuid==sourceGuid)) // sourceGuid is here so you do not respond to routing requests from systems you are already routing through. + { + forwardedConnectionListMutex.Unlock(); + + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i\n", __FILE__, __LINE__)); + SendFailureOnCannotForward(sourceGuid,endpointGuid); + return -1; + } + } + forwardedConnectionListMutex.Unlock(); + + int pingToEndpoint; + pingToEndpoint = rakPeerInterface->GetAveragePing(endpointGuid); + if (pingToEndpoint==-1) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d -> %I64d) at %s:%i\n", + sourceGuid.g, endpointGuid.g,__FILE__, __LINE__)); + + SendFailureOnCannotForward(sourceGuid,endpointGuid); + return -1; + } + return pingToEndpoint; +} +void Router2::OnQueryForwarding(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); + RakNetGUID endpointGuid; + // Read endpointGuid + bs.Read(endpointGuid); + + int pingToEndpoint = ReturnFailureOnCannotForward(packet->guid, endpointGuid); + if (pingToEndpoint==-1) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); + return; + } + + // If we are connected to endpointGuid, reply ID_ROUTER_2_REPLY_FORWARDING,endpointGuid,true,ping,numCurrentlyForwarding + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL); + bsOut.Write((unsigned char) ID_ROUTER_2_REPLY_FORWARDING); + bsOut.Write(endpointGuid); + bsOut.Write(true); + bsOut.Write((unsigned short) pingToEndpoint); + bsOut.Write((unsigned short) udpForwarder->GetUsedForwardEntries()/2); + rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,packet->guid,false); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REPLY_FORWARDING to the %I64d at %s:%i\n", packet->guid.g, __FILE__, __LINE__)); + } +} +void Router2::OnQueryForwardingReply(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); + RakNetGUID endpointGuid; + bs.Read(endpointGuid); + // Find endpointGuid among stored requests + bool canForward=false; + bs.Read(canForward); + + + connectionRequestsMutex.Lock(); + unsigned int connectionRequestIndex = GetConnectionRequestIndex(endpointGuid); + if (connectionRequestIndex==(unsigned int)-1) + { + connectionRequestsMutex.Unlock(); + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); + return; + } + + connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Lock(); + unsigned int connectionRequestGuidIndex = connectionRequests[connectionRequestIndex]->GetGuidIndex(packet->guid); + if (connectionRequestGuidIndex==(unsigned int)-1) + { + connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Unlock(); + connectionRequestsMutex.Unlock(); + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (%I64d) at %s:%i\n", endpointGuid.g, __FILE__, __LINE__)); + return; + } + + if (debugInterface) + { + char buff[512]; + char buff2[512]; + packet->systemAddress.ToString(true,buff2); + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_FORWARDING on address %s(%I64d -> %I64d) canForward=%i at %s:%i\n", + buff2, packet->guid.g, endpointGuid.g, canForward, __FILE__, __LINE__)); + } + + if (canForward) + { + unsigned short pingToEndpoint; + unsigned short usedEntries; + bs.Read(pingToEndpoint); + bs.Read(usedEntries); + connectionRequests[connectionRequestIndex]->connectionRequestSystems[connectionRequestGuidIndex].usedForwardingEntries=usedEntries; + connectionRequests[connectionRequestIndex]->connectionRequestSystems[connectionRequestGuidIndex].pingToEndpoint=rakPeerInterface->GetAveragePing(packet->guid)+pingToEndpoint; + } + else + { + connectionRequests[connectionRequestIndex]->connectionRequestSystems.RemoveAtIndex(connectionRequestGuidIndex); + } + connectionRequests[connectionRequestIndex]->connectionRequestSystemsMutex.Unlock(); + + if (UpdateForwarding(connectionRequests[connectionRequestIndex])==false) + { + RemoveConnectionRequest(connectionRequestIndex); + } + connectionRequestsMutex.Unlock(); +} +void Router2::SendForwardingSuccess(MessageID messageId, RakNetGUID sourceGuid, RakNetGUID endpointGuid, unsigned short sourceToDstPort) +{ + RakNet::BitStream bsOut; + bsOut.Write(messageId); + bsOut.Write(endpointGuid); + bsOut.Write(sourceToDstPort); + rakPeerInterface->Send(&bsOut,MEDIUM_PRIORITY,RELIABLE_ORDERED,0,sourceGuid,false); + + if (debugInterface) + { + char buff[512]; + if (messageId==ID_ROUTER_2_FORWARDING_ESTABLISHED) + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_FORWARDING_ESTABLISHED at %s:%i\n", _FILE_AND_LINE_ )); + else + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_REROUTED at %s:%i\n", _FILE_AND_LINE_ )); + } +} +void Router2::SendOOBFromRakNetPort(OutOfBandIdentifiers oob, BitStream *extraData, SystemAddress sa) +{ + RakNet::BitStream oobBs; + oobBs.Write((unsigned char)oob); + if (extraData) + { + extraData->ResetReadPointer(); + oobBs.Write(*extraData); + } + char ipAddressString[32]; + sa.ToString(false, ipAddressString); + rakPeerInterface->SendOutOfBand((const char*) ipAddressString,sa.GetPort(),(const char*) oobBs.GetData(),oobBs.GetNumberOfBytesUsed()); +} +void Router2::SendOOBFromSpecifiedSocket(OutOfBandIdentifiers oob, SystemAddress sa, SOCKET socket) +{ + RakNet::BitStream bs; + rakPeerInterface->WriteOutOfBandHeader(&bs); + bs.Write((unsigned char) oob); + SocketLayer::SendTo_PC( socket, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), sa, __FILE__, __LINE__ ); +} +void Router2::SendOOBMessages(Router2::MiniPunchRequest *mpr) +{ + // Mini NAT punch + // Send from srcToDestPort to packet->systemAddress (source). If the message arrives, the remote system should reply. + SendOOBFromSpecifiedSocket(ID_ROUTER_2_REPLY_TO_SENDER_PORT, mpr->sourceAddress, mpr->forwardingSocket); + + // Send from destToSourcePort to endpointSystemAddress (destination). If the message arrives, the remote system should reply. + SendOOBFromSpecifiedSocket(ID_ROUTER_2_REPLY_TO_SENDER_PORT, mpr->endpointAddress, mpr->forwardingSocket); + + + if (debugInterface) { + char buff [512]; + + char buff2[128]; + + mpr->sourceAddress .ToString(true,buff2); + + debugInterface->ShowDiagnostic(FormatStringTS(buff,"call SendOOBFromSpecifiedSocket(...,%s,...)", buff2)); + + mpr->endpointAddress .ToString(true,buff2); + + debugInterface->ShowDiagnostic(FormatStringTS(buff,"call SendOOBFromSpecifiedSocket(...,%s,...)", buff2)); + } + + // Tell source to send to forwardingPort + RakNet::BitStream extraData; + extraData.Write(mpr->forwardingPort); + RakAssert(mpr->forwardingPort!=0); + SendOOBFromRakNetPort(ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, &extraData, mpr->sourceAddress); + + // Tell destination to send to forwardingPort + extraData.Reset(); + extraData.Write(mpr->forwardingPort); + RakAssert(mpr->forwardingPort); + SendOOBFromRakNetPort(ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT, &extraData, mpr->endpointAddress); +} +void Router2::OnRequestForwarding(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); + RakNetGUID endpointGuid; + bs.Read(endpointGuid); + + int pingToEndpoint = ReturnFailureOnCannotForward(packet->guid, endpointGuid); + if (pingToEndpoint==-1) + { + char buff[512]; + if (debugInterface) debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed (packet->guid =%I64d, endpointGuid = %I64d) at %s:%i\n", + packet->guid.g, endpointGuid.g, __FILE__, __LINE__)); + return; + } + + unsigned short forwardingPort=0; + SOCKET forwardingSocket=0; + SystemAddress endpointSystemAddress = rakPeerInterface->GetSystemAddressFromGuid(endpointGuid); + UDPForwarderResult result = udpForwarder->StartForwarding( + packet->systemAddress, endpointSystemAddress, 30000, 0, socketFamily, + &forwardingPort, &forwardingSocket); + + if (result==UDPFORWARDER_FORWARDING_ALREADY_EXISTS) + { + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING, result=UDPFORWARDER_FORWARDING_ALREADY_EXISTS " + "(packet->guid =%I64d, endpointGuid = %I64d) at %s:%i\n", + packet->guid.g, endpointGuid.g,__FILE__, __LINE__)); + } + + SendForwardingSuccess(ID_ROUTER_2_FORWARDING_ESTABLISHED, packet->guid, endpointGuid, forwardingPort); + } + else if (result==UDPFORWARDER_NO_SOCKETS) + { + char buff[512]; + char buff2[64]; + char buff3[64]; + packet->systemAddress.ToString(true,buff2); + endpointSystemAddress.ToString(true,buff3); + if (debugInterface) + debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_NO_SOCKETS, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", + __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); + SendFailureOnCannotForward(packet->guid, endpointGuid); + } + else if (result==UDPFORWARDER_INVALID_PARAMETERS) + { + char buff[512]; + char buff2[64]; + char buff3[64]; + packet->systemAddress.ToString(true,buff2); + endpointSystemAddress.ToString(true,buff3); + if (debugInterface) + debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_INVALID_PARAMETERS, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", + __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); + SendFailureOnCannotForward(packet->guid, endpointGuid); + } + else if (result==UDPFORWARDER_BIND_FAILED) + { + char buff[512]; + char buff2[64]; + char buff3[64]; + packet->systemAddress.ToString(true,buff2); + endpointSystemAddress.ToString(true,buff3); + if (debugInterface) + debugInterface->ShowFailure(FormatStringTS(buff,"Router2 failed at %s:%i with UDPFORWARDER_BIND_FAILED, packet->systemAddress=%s, endpointSystemAddress=%s, forwardingPort=%i, forwardingSocket=%i\n", + __FILE__, __LINE__, buff2, buff3, forwardingPort, forwardingSocket)); + SendFailureOnCannotForward(packet->guid, endpointGuid); + } + else + { + if (debugInterface) + { + char buff2[32]; + char buff3[32]; + endpointSystemAddress.ToString(true,buff2); + packet->systemAddress.ToString(true,buff3); + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING.\n" + "endpointAddress=%s\nsourceAddress=%s\nforwardingPort=%i\n " + "calling SendOOBMessages at %s:%i\n", buff2,buff3,forwardingPort,_FILE_AND_LINE_)); + } + + // Store the punch request + MiniPunchRequest miniPunchRequest; + miniPunchRequest.endpointAddress=endpointSystemAddress; + miniPunchRequest.endpointGuid=endpointGuid; + miniPunchRequest.gotReplyFromEndpoint=false; + miniPunchRequest.gotReplyFromSource=false; + miniPunchRequest.sourceGuid=packet->guid; + miniPunchRequest.sourceAddress=packet->systemAddress; + miniPunchRequest.forwardingPort=forwardingPort; + miniPunchRequest.forwardingSocket=forwardingSocket; + int ping1 = rakPeerInterface->GetAveragePing(packet->guid); + int ping2 = rakPeerInterface->GetAveragePing(endpointGuid); + if (ping1>ping2) + miniPunchRequest.timeout=RakNet::GetTimeMS() + ping1*8+300; + else + miniPunchRequest.timeout=RakNet::GetTimeMS() + ping2*8+300; + miniPunchRequest.nextAction=RakNet::GetTimeMS()+100; + SendOOBMessages(&miniPunchRequest); + miniPunchesInProgressMutex.Lock(); + miniPunchesInProgress.Push(miniPunchRequest,_FILE_AND_LINE_); + miniPunchesInProgressMutex.Unlock(); + } +} +void Router2::OnMiniPunchReplyBounce(Packet *packet) +{ + // Find stored punch request + unsigned int i=0; + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE from guid=%I64d (miniPunchesInProgress.Size() = %d)", + packet->guid.g, miniPunchesInProgress.Size())); + } + + miniPunchesInProgressMutex.Lock(); + while (i < miniPunchesInProgress.Size()) + { + if (miniPunchesInProgress[i].sourceGuid==packet->guid || miniPunchesInProgress[i].endpointGuid==packet->guid) + { + if (miniPunchesInProgress[i].sourceGuid==packet->guid) + miniPunchesInProgress[i].gotReplyFromSource=true; + if (miniPunchesInProgress[i].endpointGuid==packet->guid) + miniPunchesInProgress[i].gotReplyFromEndpoint=true; + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Processing ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, gotReplyFromSource=%i gotReplyFromEndpoint=%i at %s:%i\n", miniPunchesInProgress[i].gotReplyFromSource, miniPunchesInProgress[i].gotReplyFromEndpoint, __FILE__, __LINE__)); + } + + if (miniPunchesInProgress[i].gotReplyFromEndpoint==true && + miniPunchesInProgress[i].gotReplyFromSource==true) + { + SendForwardingSuccess(ID_ROUTER_2_REROUTED, miniPunchesInProgress[i].endpointGuid, miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].forwardingPort); + SendForwardingSuccess(ID_ROUTER_2_FORWARDING_ESTABLISHED, miniPunchesInProgress[i].sourceGuid, miniPunchesInProgress[i].endpointGuid, miniPunchesInProgress[i].forwardingPort); + miniPunchesInProgress.RemoveAtIndexFast(i); + } + else + { + i++; + } + } + else + i++; + } + miniPunchesInProgressMutex.Unlock(); +} +void Router2::OnMiniPunchReply(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID) + sizeof(unsigned char)); + RakNetGUID routerGuid; + bs.Read(routerGuid); + SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE, 0, rakPeerInterface->GetSystemAddressFromGuid(routerGuid)); + + if (debugInterface) + { + char buff[512]; + + char buff2[512]; + + rakPeerInterface->GetSystemAddressFromGuid(routerGuid).ToString(true,buff2); + + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Sending ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE (%s) at %s:%i\n", buff2, __FILE__, __LINE__)); + } +} +void Router2::OnRerouted(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID endpointGuid; + bs.Read(endpointGuid); + unsigned short sourceToDestPort; + bs.Read(sourceToDestPort); + + // Return rerouted notice + SystemAddress intermediaryAddress=packet->systemAddress; + intermediaryAddress.SetPort(sourceToDestPort); + rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); + + unsigned int forwardingIndex; + forwardedConnectionListMutex.Lock(); + for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) + { + if (forwardedConnectionList[forwardingIndex].endpointGuid==endpointGuid) + break; + } + + if (forwardingIndexsystemAddress; + ref_fc.intermediaryAddress.SetPort(sourceToDestPort); + ref_fc.intermediaryGuid = packet->guid; + + rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"FIX: Got ID_ROUTER_2_REROUTE, returning ID_ROUTER_2_REROUTED," + " Calling RakPeer::ChangeSystemAddress(%I64d, %s) at %s:%i\n",endpointGuid.g, intermediaryAddress.ToString(true), __FILE__, __LINE__)); + } + } + else + { + ForwardedConnection fc; + fc.endpointGuid=endpointGuid; + fc.intermediaryAddress=packet->systemAddress; + fc.intermediaryAddress.SetPort(sourceToDestPort); + fc.intermediaryGuid=packet->guid; + fc.weInitiatedForwarding=false; + // add to forwarding list. This is only here to avoid reporting direct connections in Router2::ReturnFailureOnCannotForward + forwardedConnectionList.Push (fc,__FILE__, __LINE__); + forwardedConnectionListMutex.Unlock(); + + rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REROUTE, returning ID_ROUTER_2_REROUTED, Calling RakPeer::ChangeSystemAddress at %s:%i\n", __FILE__, __LINE__)); + } + } + +} +bool Router2::OnForwardingSuccess(Packet *packet) +{ + RakNet::BitStream bs(packet->data, packet->length, false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID endpointGuid; + bs.Read(endpointGuid); + unsigned short sourceToDestPort; + bs.Read(sourceToDestPort); + + unsigned int forwardingIndex; + forwardedConnectionListMutex.Lock(); + for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++) + { + if (forwardedConnectionList[forwardingIndex].endpointGuid==endpointGuid) + break; + } + + if (forwardingIndexsystemAddress; + intermediaryAddress.SetPort(sourceToDestPort); + rakPeerInterface->ChangeSystemAddress(endpointGuid, intermediaryAddress); + + //////////////////////////////////////////////////////////////////////////// + ForwardedConnection& ref_fc = forwardedConnectionList[forwardingIndex]; + ref_fc.intermediaryAddress = packet->systemAddress; + ref_fc.intermediaryAddress.SetPort(sourceToDestPort); + ref_fc.intermediaryGuid = packet->guid; + //////////////////////////////////////////////////////////////////////////// + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_FORWARDING_ESTABLISHED, returning ID_ROUTER_2_REROUTED, Calling RakPeer::ChangeSystemAddress at %s:%i\n", _FILE_AND_LINE_)); + } + + packet->data[0]=ID_ROUTER_2_REROUTED; + + forwardedConnectionListMutex.Unlock(); + return true; // Return packet to user + } + else + { + forwardedConnectionListMutex.Unlock(); + + // removeFrom connectionRequests; + ForwardedConnection fc; + connectionRequestsMutex.Lock(); + unsigned int connectionRequestIndex = GetConnectionRequestIndex(endpointGuid); + fc.returnConnectionLostOnFailure=connectionRequests[connectionRequestIndex]->returnConnectionLostOnFailure; + connectionRequests.RemoveAtIndexFast(connectionRequestIndex); + connectionRequestsMutex.Unlock(); + fc.endpointGuid=endpointGuid; + fc.intermediaryAddress=packet->systemAddress; + fc.intermediaryAddress.SetPort(sourceToDestPort); + fc.intermediaryGuid=packet->guid; + fc.weInitiatedForwarding=true; + + // add to forwarding list + forwardedConnectionListMutex.Lock(); + forwardedConnectionList.Push (fc,_FILE_AND_LINE_); + forwardedConnectionListMutex.Unlock(); + + if (debugInterface) + { + char buff[512]; + debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got and returning to user ID_ROUTER_2_FORWARDING_ESTABLISHED at %s:%i\n", _FILE_AND_LINE_)); + } + + } + return true; // Return packet to user +} +int Router2::GetLargestPingAmongConnectedSystems(void) const +{ + int avePing; + int largestPing=-1; + unsigned short maxPeers = rakPeerInterface->GetMaximumNumberOfPeers(); + if (maxPeers==0) + return 9999; + unsigned short index; + for (index=0; index < rakPeerInterface->GetMaximumNumberOfPeers(); index++) + { + RakNetGUID g = rakPeerInterface->GetGUIDFromIndex(index); + if (g!=UNASSIGNED_RAKNET_GUID) + { + avePing=rakPeerInterface->GetAveragePing(rakPeerInterface->GetGUIDFromIndex(index)); + if (avePing>largestPing) + largestPing=avePing; + } + } + return largestPing; +} + +unsigned int Router2::GetConnectionRequestIndex(RakNetGUID endpointGuid) +{ + unsigned int i; + for (i=0; i < connectionRequests.Size(); i++) + { + if (connectionRequests[i]->endpointGuid==endpointGuid) + return i; + } + return (unsigned int) -1; +} +unsigned int Router2::ConnnectRequest::GetGuidIndex(RakNetGUID guid) +{ + unsigned int i; + for (i=0; i < connectionRequestSystems.Size(); i++) + { + if (connectionRequestSystems[i].guid==guid) + return i; + } + return (unsigned int) -1; +} +void Router2::ReturnToUser(MessageID messageId, RakNetGUID endpointGuid, const SystemAddress &systemAddress, bool wasGeneratedLocally) +{ + Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(unsigned char)); + p->data[0]=messageId; + p->systemAddress=systemAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=endpointGuid; + p->wasGeneratedLocally=wasGeneratedLocally; + rakPeerInterface->PushBackPacket(p, true); +} +void Router2::ClearForwardedConnections(void) +{ + forwardedConnectionListMutex.Lock(); + forwardedConnectionList.Clear(false,_FILE_AND_LINE_); + forwardedConnectionListMutex.Unlock(); +} +void Router2::ClearAll(void) +{ + ClearConnectionRequests(); + ClearMinipunches(); + ClearForwardedConnections(); +} +void Router2::SetDebugInterface(Router2DebugInterface *_debugInterface) +{ + debugInterface=_debugInterface; +} +Router2DebugInterface *Router2::GetDebugInterface(void) const +{ + return debugInterface; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/Router2.h b/src/raknet/Router2.h index b4debe0..7912da5 100755 --- a/src/raknet/Router2.h +++ b/src/raknet/Router2.h @@ -1,195 +1,195 @@ -/// \file -/// \brief Router2 plugin. Allows you to connect to a system by routing packets through another system that is connected to both you and the destination. Useful for getting around NATs. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_Router2==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#ifndef __ROUTER_2_PLUGIN_H -#define __ROUTER_2_PLUGIN_H - -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "PacketPriority.h" -#include "Export.h" -#include "UDPForwarder.h" -#include "MessageIdentifiers.h" -#include "DS_List.h" -#include "SimpleMutex.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -struct Router2DebugInterface -{ - Router2DebugInterface() {} - virtual ~Router2DebugInterface() {} - virtual void ShowFailure(const char *message); - virtual void ShowDiagnostic(const char *message); -}; - -/// \defgroup ROUTER_2_GROUP Router2 -/// \brief Part of the NAT punchthrough solution, allowing you to connect to systems by routing through a shared connection. -/// \details Router2 routes datagrams between two systems that are not directly connected by using the bandwidth of a third system, to which the other two systems were connected -/// It is of benefit when a fully connected mesh topology is desired, but could not be completely established due to routers and/or firewalls -/// As the system address of a remote system will be the system address of the intermediary, it is necessary to use the RakNetGUID object to refer to systems, including with other plugins -/// \ingroup PLUGINS_GROUP - -/// \ingroup ROUTER_2_GROUP -/// \brief Class interface for the Router2 system -/// \details -class RAK_DLL_EXPORT Router2 : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(Router2) - - Router2(); - virtual ~Router2(); - - /// Sets the socket family to use, either IPV4 or IPV6 - /// \param[in] socketFamily For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - void SetSocketFamily(unsigned short _socketFamily); - - /// \brief Query all connected systems to connect through them to a third system. - /// System will return ID_ROUTER_2_FORWARDING_NO_PATH if unable to connect. - /// Else you will get ID_ROUTER_2_FORWARDING_ESTABLISHED - /// - /// On ID_ROUTER_2_FORWARDING_ESTABLISHED, EstablishRouting as follows: - /// - /// RakNet::BitStream bs(packet->data, packet->length, false); - /// bs.IgnoreBytes(sizeof(MessageID)); - /// RakNetGUID endpointGuid; - /// bs.Read(endpointGuid); - /// unsigned short sourceToDestPort; - /// bs.Read(sourceToDestPort); - /// char ipAddressString[32]; - /// packet->systemAddress.ToString(false, ipAddressString); - /// rakPeerInterface->EstablishRouting(ipAddressString, sourceToDestPort, 0,0); - /// - /// \note The SystemAddress for a connection should not be used - always use RakNetGuid as the address can change at any time. - /// When the address changes, you will get ID_ROUTER_2_REROUTED - void EstablishRouting(RakNetGUID endpointGuid); - - /// Set the maximum number of bidirectional connections this system will support - /// Defaults to 0 - void SetMaximumForwardingRequests(int max); - - /// For testing and debugging - void SetDebugInterface(Router2DebugInterface *_debugInterface); - - /// Get the pointer passed to SetDebugInterface() - Router2DebugInterface *GetDebugInterface(void) const; - - // -------------------------------------------------------------------------------------------- - // Packet handling functions - // -------------------------------------------------------------------------------------------- - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void Update(void); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason); - virtual void OnRakPeerShutdown(void); - - - enum Router2RequestStates - { - R2RS_REQUEST_STATE_QUERY_FORWARDING, - REQUEST_STATE_REQUEST_FORWARDING, - }; - - struct ConnectionRequestSystem - { - RakNetGUID guid; - int pingToEndpoint; - unsigned short usedForwardingEntries; - }; - - struct ConnnectRequest - { - ConnnectRequest(); - ~ConnnectRequest(); - - DataStructures::List connectionRequestSystems; - SimpleMutex connectionRequestSystemsMutex; - Router2RequestStates requestState; - RakNet::TimeMS pingTimeout; - RakNetGUID endpointGuid; - RakNetGUID lastRequestedForwardingSystem; - bool returnConnectionLostOnFailure; - unsigned int GetGuidIndex(RakNetGUID guid); - }; - - unsigned int GetConnectionRequestIndex(RakNetGUID endpointGuid); - - struct MiniPunchRequest - { - RakNetGUID endpointGuid; - SystemAddress endpointAddress; - bool gotReplyFromEndpoint; - RakNetGUID sourceGuid; - SystemAddress sourceAddress; - bool gotReplyFromSource; - RakNet::TimeMS timeout; - RakNet::TimeMS nextAction; - unsigned short forwardingPort; - SOCKET forwardingSocket; - }; - - struct ForwardedConnection - { - RakNetGUID endpointGuid; - RakNetGUID intermediaryGuid; - SystemAddress intermediaryAddress; - bool returnConnectionLostOnFailure; - bool weInitiatedForwarding; - }; - -protected: - - bool UpdateForwarding(ConnnectRequest* connectionRequest); - void RemoveConnectionRequest(unsigned int connectionRequestIndex); - void RequestForwarding(ConnnectRequest* connectionRequest); - void OnQueryForwarding(Packet *packet); - void OnQueryForwardingReply(Packet *packet); - void OnRequestForwarding(Packet *packet); - void OnRerouted(Packet *packet); - void OnMiniPunchReply(Packet *packet); - void OnMiniPunchReplyBounce(Packet *packet); - bool OnForwardingSuccess(Packet *packet); - int GetLargestPingAmongConnectedSystems(void) const; - void ReturnToUser(MessageID messageId, RakNetGUID endpointGuid, const SystemAddress &systemAddress, bool wasGeneratedLocally); - bool ConnectInternal(RakNetGUID endpointGuid, bool returnConnectionLostOnFailure); - - UDPForwarder *udpForwarder; - int maximumForwardingRequests; - SimpleMutex connectionRequestsMutex, miniPunchesInProgressMutex, forwardedConnectionListMutex; - DataStructures::List connectionRequests; - DataStructures::List miniPunchesInProgress; - // Forwarding we have initiated - DataStructures::List forwardedConnectionList; - - void ClearConnectionRequests(void); - void ClearMinipunches(void); - void ClearForwardedConnections(void); - void ClearAll(void); - int ReturnFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid); - void SendFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid); - void SendForwardingSuccess(MessageID messageId, RakNetGUID sourceGuid, RakNetGUID endpointGuid, unsigned short sourceToDstPort); - void SendOOBFromRakNetPort(OutOfBandIdentifiers oob, BitStream *extraData, SystemAddress sa); - void SendOOBFromSpecifiedSocket(OutOfBandIdentifiers oob, SystemAddress sa, SOCKET socket); - void SendOOBMessages(MiniPunchRequest *mpr); - - Router2DebugInterface *debugInterface; - unsigned short socketFamily; -}; - -} - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Router2 plugin. Allows you to connect to a system by routing packets through another system that is connected to both you and the destination. Useful for getting around NATs. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_Router2==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#ifndef __ROUTER_2_PLUGIN_H +#define __ROUTER_2_PLUGIN_H + +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "PacketPriority.h" +#include "Export.h" +#include "UDPForwarder.h" +#include "MessageIdentifiers.h" +#include "DS_List.h" +#include "SimpleMutex.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +struct Router2DebugInterface +{ + Router2DebugInterface() {} + virtual ~Router2DebugInterface() {} + virtual void ShowFailure(const char *message); + virtual void ShowDiagnostic(const char *message); +}; + +/// \defgroup ROUTER_2_GROUP Router2 +/// \brief Part of the NAT punchthrough solution, allowing you to connect to systems by routing through a shared connection. +/// \details Router2 routes datagrams between two systems that are not directly connected by using the bandwidth of a third system, to which the other two systems were connected +/// It is of benefit when a fully connected mesh topology is desired, but could not be completely established due to routers and/or firewalls +/// As the system address of a remote system will be the system address of the intermediary, it is necessary to use the RakNetGUID object to refer to systems, including with other plugins +/// \ingroup PLUGINS_GROUP + +/// \ingroup ROUTER_2_GROUP +/// \brief Class interface for the Router2 system +/// \details +class RAK_DLL_EXPORT Router2 : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(Router2) + + Router2(); + virtual ~Router2(); + + /// Sets the socket family to use, either IPV4 or IPV6 + /// \param[in] socketFamily For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + void SetSocketFamily(unsigned short _socketFamily); + + /// \brief Query all connected systems to connect through them to a third system. + /// System will return ID_ROUTER_2_FORWARDING_NO_PATH if unable to connect. + /// Else you will get ID_ROUTER_2_FORWARDING_ESTABLISHED + /// + /// On ID_ROUTER_2_FORWARDING_ESTABLISHED, EstablishRouting as follows: + /// + /// RakNet::BitStream bs(packet->data, packet->length, false); + /// bs.IgnoreBytes(sizeof(MessageID)); + /// RakNetGUID endpointGuid; + /// bs.Read(endpointGuid); + /// unsigned short sourceToDestPort; + /// bs.Read(sourceToDestPort); + /// char ipAddressString[32]; + /// packet->systemAddress.ToString(false, ipAddressString); + /// rakPeerInterface->EstablishRouting(ipAddressString, sourceToDestPort, 0,0); + /// + /// \note The SystemAddress for a connection should not be used - always use RakNetGuid as the address can change at any time. + /// When the address changes, you will get ID_ROUTER_2_REROUTED + void EstablishRouting(RakNetGUID endpointGuid); + + /// Set the maximum number of bidirectional connections this system will support + /// Defaults to 0 + void SetMaximumForwardingRequests(int max); + + /// For testing and debugging + void SetDebugInterface(Router2DebugInterface *_debugInterface); + + /// Get the pointer passed to SetDebugInterface() + Router2DebugInterface *GetDebugInterface(void) const; + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void Update(void); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason); + virtual void OnRakPeerShutdown(void); + + + enum Router2RequestStates + { + R2RS_REQUEST_STATE_QUERY_FORWARDING, + REQUEST_STATE_REQUEST_FORWARDING, + }; + + struct ConnectionRequestSystem + { + RakNetGUID guid; + int pingToEndpoint; + unsigned short usedForwardingEntries; + }; + + struct ConnnectRequest + { + ConnnectRequest(); + ~ConnnectRequest(); + + DataStructures::List connectionRequestSystems; + SimpleMutex connectionRequestSystemsMutex; + Router2RequestStates requestState; + RakNet::TimeMS pingTimeout; + RakNetGUID endpointGuid; + RakNetGUID lastRequestedForwardingSystem; + bool returnConnectionLostOnFailure; + unsigned int GetGuidIndex(RakNetGUID guid); + }; + + unsigned int GetConnectionRequestIndex(RakNetGUID endpointGuid); + + struct MiniPunchRequest + { + RakNetGUID endpointGuid; + SystemAddress endpointAddress; + bool gotReplyFromEndpoint; + RakNetGUID sourceGuid; + SystemAddress sourceAddress; + bool gotReplyFromSource; + RakNet::TimeMS timeout; + RakNet::TimeMS nextAction; + unsigned short forwardingPort; + SOCKET forwardingSocket; + }; + + struct ForwardedConnection + { + RakNetGUID endpointGuid; + RakNetGUID intermediaryGuid; + SystemAddress intermediaryAddress; + bool returnConnectionLostOnFailure; + bool weInitiatedForwarding; + }; + +protected: + + bool UpdateForwarding(ConnnectRequest* connectionRequest); + void RemoveConnectionRequest(unsigned int connectionRequestIndex); + void RequestForwarding(ConnnectRequest* connectionRequest); + void OnQueryForwarding(Packet *packet); + void OnQueryForwardingReply(Packet *packet); + void OnRequestForwarding(Packet *packet); + void OnRerouted(Packet *packet); + void OnMiniPunchReply(Packet *packet); + void OnMiniPunchReplyBounce(Packet *packet); + bool OnForwardingSuccess(Packet *packet); + int GetLargestPingAmongConnectedSystems(void) const; + void ReturnToUser(MessageID messageId, RakNetGUID endpointGuid, const SystemAddress &systemAddress, bool wasGeneratedLocally); + bool ConnectInternal(RakNetGUID endpointGuid, bool returnConnectionLostOnFailure); + + UDPForwarder *udpForwarder; + int maximumForwardingRequests; + SimpleMutex connectionRequestsMutex, miniPunchesInProgressMutex, forwardedConnectionListMutex; + DataStructures::List connectionRequests; + DataStructures::List miniPunchesInProgress; + // Forwarding we have initiated + DataStructures::List forwardedConnectionList; + + void ClearConnectionRequests(void); + void ClearMinipunches(void); + void ClearForwardedConnections(void); + void ClearAll(void); + int ReturnFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid); + void SendFailureOnCannotForward(RakNetGUID sourceGuid, RakNetGUID endpointGuid); + void SendForwardingSuccess(MessageID messageId, RakNetGUID sourceGuid, RakNetGUID endpointGuid, unsigned short sourceToDstPort); + void SendOOBFromRakNetPort(OutOfBandIdentifiers oob, BitStream *extraData, SystemAddress sa); + void SendOOBFromSpecifiedSocket(OutOfBandIdentifiers oob, SystemAddress sa, SOCKET socket); + void SendOOBMessages(MiniPunchRequest *mpr); + + Router2DebugInterface *debugInterface; + unsigned short socketFamily; +}; + +} + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/SHA1.cpp b/src/raknet/SHA1.cpp index b53b72c..fbfe7d0 100755 --- a/src/raknet/SHA1.cpp +++ b/src/raknet/SHA1.cpp @@ -1,326 +1,326 @@ -/** -* @brief SHA-1 Hash key computation -* -* 100% free public domain implementation of the SHA-1 -* algorithm by Dominik Reichl -* -* -* === Test Vectors (from FIPS PUB 180-1) === -* -* "abc" -* A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -* -* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" -* 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -* -* A million repetitions of "a" -* 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - - -#include "SHA1.h" -#include - - -CSHA1::CSHA1() -{ - Reset(); -} - -CSHA1::~CSHA1() -{ - Reset(); -} - - -void CSHA1::Reset() -{ - // SHA1 initialization constants - m_state[ 0 ] = 0x67452301; - m_state[ 1 ] = 0xEFCDAB89; - m_state[ 2 ] = 0x98BADCFE; - m_state[ 3 ] = 0x10325476; - m_state[ 4 ] = 0xC3D2E1F0; - - m_count[ 0 ] = 0; - m_count[ 1 ] = 0; -} - -void CSHA1::Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ) -{ - unsigned int a = 0, b = 0, c = 0, d = 0, e = 0; - - SHA1_WORKSPACE_BLOCK* block; - // static unsigned char workspace[64]; - block = ( SHA1_WORKSPACE_BLOCK * ) workspace; - memcpy( block, buffer, 64 ); - - // Copy state[] to working vars - a = state[ 0 ]; - b = state[ 1 ]; - c = state[ 2 ]; - d = state[ 3 ]; - e = state[ 4 ]; - - // 4 rounds of 20 operations each. Loop unrolled. - R0( a, b, c, d, e, 0 ); - R0( e, a, b, c, d, 1 ); - R0( d, e, a, b, c, 2 ); - R0( c, d, e, a, b, 3 ); - R0( b, c, d, e, a, 4 ); - R0( a, b, c, d, e, 5 ); - R0( e, a, b, c, d, 6 ); - R0( d, e, a, b, c, 7 ); - R0( c, d, e, a, b, 8 ); - R0( b, c, d, e, a, 9 ); - R0( a, b, c, d, e, 10 ); - R0( e, a, b, c, d, 11 ); - R0( d, e, a, b, c, 12 ); - R0( c, d, e, a, b, 13 ); - R0( b, c, d, e, a, 14 ); - R0( a, b, c, d, e, 15 ); - R1( e, a, b, c, d, 16 ); - R1( d, e, a, b, c, 17 ); - R1( c, d, e, a, b, 18 ); - R1( b, c, d, e, a, 19 ); - R2( a, b, c, d, e, 20 ); - R2( e, a, b, c, d, 21 ); - R2( d, e, a, b, c, 22 ); - R2( c, d, e, a, b, 23 ); - R2( b, c, d, e, a, 24 ); - R2( a, b, c, d, e, 25 ); - R2( e, a, b, c, d, 26 ); - R2( d, e, a, b, c, 27 ); - R2( c, d, e, a, b, 28 ); - R2( b, c, d, e, a, 29 ); - R2( a, b, c, d, e, 30 ); - R2( e, a, b, c, d, 31 ); - R2( d, e, a, b, c, 32 ); - R2( c, d, e, a, b, 33 ); - R2( b, c, d, e, a, 34 ); - R2( a, b, c, d, e, 35 ); - R2( e, a, b, c, d, 36 ); - R2( d, e, a, b, c, 37 ); - R2( c, d, e, a, b, 38 ); - R2( b, c, d, e, a, 39 ); - R3( a, b, c, d, e, 40 ); - R3( e, a, b, c, d, 41 ); - R3( d, e, a, b, c, 42 ); - R3( c, d, e, a, b, 43 ); - R3( b, c, d, e, a, 44 ); - R3( a, b, c, d, e, 45 ); - R3( e, a, b, c, d, 46 ); - R3( d, e, a, b, c, 47 ); - R3( c, d, e, a, b, 48 ); - R3( b, c, d, e, a, 49 ); - R3( a, b, c, d, e, 50 ); - R3( e, a, b, c, d, 51 ); - R3( d, e, a, b, c, 52 ); - R3( c, d, e, a, b, 53 ); - R3( b, c, d, e, a, 54 ); - R3( a, b, c, d, e, 55 ); - R3( e, a, b, c, d, 56 ); - R3( d, e, a, b, c, 57 ); - R3( c, d, e, a, b, 58 ); - R3( b, c, d, e, a, 59 ); - R4( a, b, c, d, e, 60 ); - R4( e, a, b, c, d, 61 ); - R4( d, e, a, b, c, 62 ); - R4( c, d, e, a, b, 63 ); - R4( b, c, d, e, a, 64 ); - R4( a, b, c, d, e, 65 ); - R4( e, a, b, c, d, 66 ); - R4( d, e, a, b, c, 67 ); - R4( c, d, e, a, b, 68 ); - R4( b, c, d, e, a, 69 ); - R4( a, b, c, d, e, 70 ); - R4( e, a, b, c, d, 71 ); - R4( d, e, a, b, c, 72 ); - R4( c, d, e, a, b, 73 ); - R4( b, c, d, e, a, 74 ); - R4( a, b, c, d, e, 75 ); - R4( e, a, b, c, d, 76 ); - R4( d, e, a, b, c, 77 ); - R4( c, d, e, a, b, 78 ); - R4( b, c, d, e, a, 79 ); - - // Add the working vars back into state[] - state[ 0 ] += a; - state[ 1 ] += b; - state[ 2 ] += c; - state[ 3 ] += d; - state[ 4 ] += e; - - // Wipe variables - a = 0; - b = 0; - c = 0; - d = 0; - e = 0; -} - -// Use this function to hash in binary data and strings -void CSHA1::Update( unsigned char* data, unsigned int len ) -{ - unsigned int i = 0, j = 0; - - j = ( m_count[ 0 ] >> 3 ) & 63; - - if ( ( m_count[ 0 ] += len << 3 ) < ( len << 3 ) ) - m_count[ 1 ] ++; - - m_count[ 1 ] += ( len >> 29 ); - - if ( ( j + len ) > 63 ) - { - memcpy( &m_buffer[ j ], data, ( i = 64 - j ) ); - Transform( m_state, m_buffer ); - - for ( ; i + 63 < len; i += 64 ) - { - Transform( m_state, &data[ i ] ); - } - - j = 0; - } - - else - i = 0; - - memcpy( &m_buffer[ j ], &data[ i ], len - i ); -} - -// Hash in file contents -bool CSHA1::HashFile( char *szFileName ) -{ - unsigned long ulFileSize = 0, ulRest = 0, ulBlocks = 0; - unsigned long i = 0; - unsigned char uData[ MAX_FILE_READ_BUFFER ]; - FILE *fIn = NULL; - - if ( ( fIn = fopen( szFileName, "rb" ) ) == NULL ) - return ( false ); - - fseek( fIn, 0, SEEK_END ); - - ulFileSize = ftell( fIn ); - - fseek( fIn, 0, SEEK_SET ); - - // This is faster - div_t temp; - - temp = div( ulFileSize, MAX_FILE_READ_BUFFER ); - - ulRest = temp.rem; - - ulBlocks = temp.quot; - - // ulRest = ulFileSize % MAX_FILE_READ_BUFFER; - // ulBlocks = ulFileSize / MAX_FILE_READ_BUFFER; - - for ( i = 0; i < ulBlocks; i++ ) - { - fread( uData, 1, MAX_FILE_READ_BUFFER, fIn ); - Update( uData, MAX_FILE_READ_BUFFER ); - } - - if ( ulRest != 0 ) - { - fread( uData, 1, ulRest, fIn ); - Update( uData, ulRest ); - } - - fclose( fIn ); - fIn = NULL; - - return ( true ); -} - -void CSHA1::Final() -{ - unsigned int i = 0; - unsigned char finalcount[ 8 ] = - { - 0, 0, 0, 0, 0, 0, 0, 0 - }; - - for ( i = 0; i < 8; i++ ) - finalcount[ i ] = (unsigned char) ( ( m_count[ ( i >= 4 ? 0 : 1 ) ] - >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); // Endian independent - - Update( ( unsigned char * ) "\200", 1 ); - - while ( ( m_count[ 0 ] & 504 ) != 448 ) - Update( ( unsigned char * ) "\0", 1 ); - - Update( finalcount, 8 ); // Cause a SHA1Transform() - - for ( i = 0; i < 20; i++ ) - { - m_digest[ i ] = (unsigned char) ( ( m_state[ i >> 2 ] >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); - } - - // Wipe variables for security reasons - i = 0; - -// j = 0; - - memset( m_buffer, 0, 64 ); - - memset( m_state, 0, 20 ); - - memset( m_count, 0, 8 ); - - memset( finalcount, 0, 8 ); - - Transform( m_state, m_buffer ); -} - -// Get the final hash as a pre-formatted string -void CSHA1::ReportHash( char *szReport, unsigned char uReportType ) -{ - unsigned char i = 0; - char szTemp[ 4 ]; - - if ( uReportType == REPORT_HEX ) - { - sprintf( szTemp, "%02X", m_digest[ 0 ] ); - strcat( szReport, szTemp ); - - for ( i = 1; i < 20; i++ ) - { - sprintf( szTemp, " %02X", m_digest[ i ] ); - strcat( szReport, szTemp ); - } - } - - else - if ( uReportType == REPORT_DIGIT ) - { - sprintf( szTemp, "%u", m_digest[ 0 ] ); - strcat( szReport, szTemp ); - - for ( i = 1; i < 20; i++ ) - { - sprintf( szTemp, " %u", m_digest[ i ] ); - strcat( szReport, szTemp ); - } - } - - else - strcpy( szReport, "Error: Unknown report type!" ); -} - -// Get the raw message digest -void CSHA1::GetHash( unsigned char *uDest ) -{ - memcpy( uDest, m_digest, 20 ); -} - -// Get the raw message digest -// Added by Kevin to be quicker -unsigned char * CSHA1::GetHash( void ) const -{ - return ( unsigned char * ) m_digest; -} +/** +* @brief SHA-1 Hash key computation +* +* 100% free public domain implementation of the SHA-1 +* algorithm by Dominik Reichl +* +* +* === Test Vectors (from FIPS PUB 180-1) === +* +* "abc" +* A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +* +* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +* 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +* +* A million repetitions of "a" +* 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + + +#include "SHA1.h" +#include + + +CSHA1::CSHA1() +{ + Reset(); +} + +CSHA1::~CSHA1() +{ + Reset(); +} + + +void CSHA1::Reset() +{ + // SHA1 initialization constants + m_state[ 0 ] = 0x67452301; + m_state[ 1 ] = 0xEFCDAB89; + m_state[ 2 ] = 0x98BADCFE; + m_state[ 3 ] = 0x10325476; + m_state[ 4 ] = 0xC3D2E1F0; + + m_count[ 0 ] = 0; + m_count[ 1 ] = 0; +} + +void CSHA1::Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ) +{ + unsigned int a = 0, b = 0, c = 0, d = 0, e = 0; + + SHA1_WORKSPACE_BLOCK* block; + // static unsigned char workspace[64]; + block = ( SHA1_WORKSPACE_BLOCK * ) workspace; + memcpy( block, buffer, 64 ); + + // Copy state[] to working vars + a = state[ 0 ]; + b = state[ 1 ]; + c = state[ 2 ]; + d = state[ 3 ]; + e = state[ 4 ]; + + // 4 rounds of 20 operations each. Loop unrolled. + R0( a, b, c, d, e, 0 ); + R0( e, a, b, c, d, 1 ); + R0( d, e, a, b, c, 2 ); + R0( c, d, e, a, b, 3 ); + R0( b, c, d, e, a, 4 ); + R0( a, b, c, d, e, 5 ); + R0( e, a, b, c, d, 6 ); + R0( d, e, a, b, c, 7 ); + R0( c, d, e, a, b, 8 ); + R0( b, c, d, e, a, 9 ); + R0( a, b, c, d, e, 10 ); + R0( e, a, b, c, d, 11 ); + R0( d, e, a, b, c, 12 ); + R0( c, d, e, a, b, 13 ); + R0( b, c, d, e, a, 14 ); + R0( a, b, c, d, e, 15 ); + R1( e, a, b, c, d, 16 ); + R1( d, e, a, b, c, 17 ); + R1( c, d, e, a, b, 18 ); + R1( b, c, d, e, a, 19 ); + R2( a, b, c, d, e, 20 ); + R2( e, a, b, c, d, 21 ); + R2( d, e, a, b, c, 22 ); + R2( c, d, e, a, b, 23 ); + R2( b, c, d, e, a, 24 ); + R2( a, b, c, d, e, 25 ); + R2( e, a, b, c, d, 26 ); + R2( d, e, a, b, c, 27 ); + R2( c, d, e, a, b, 28 ); + R2( b, c, d, e, a, 29 ); + R2( a, b, c, d, e, 30 ); + R2( e, a, b, c, d, 31 ); + R2( d, e, a, b, c, 32 ); + R2( c, d, e, a, b, 33 ); + R2( b, c, d, e, a, 34 ); + R2( a, b, c, d, e, 35 ); + R2( e, a, b, c, d, 36 ); + R2( d, e, a, b, c, 37 ); + R2( c, d, e, a, b, 38 ); + R2( b, c, d, e, a, 39 ); + R3( a, b, c, d, e, 40 ); + R3( e, a, b, c, d, 41 ); + R3( d, e, a, b, c, 42 ); + R3( c, d, e, a, b, 43 ); + R3( b, c, d, e, a, 44 ); + R3( a, b, c, d, e, 45 ); + R3( e, a, b, c, d, 46 ); + R3( d, e, a, b, c, 47 ); + R3( c, d, e, a, b, 48 ); + R3( b, c, d, e, a, 49 ); + R3( a, b, c, d, e, 50 ); + R3( e, a, b, c, d, 51 ); + R3( d, e, a, b, c, 52 ); + R3( c, d, e, a, b, 53 ); + R3( b, c, d, e, a, 54 ); + R3( a, b, c, d, e, 55 ); + R3( e, a, b, c, d, 56 ); + R3( d, e, a, b, c, 57 ); + R3( c, d, e, a, b, 58 ); + R3( b, c, d, e, a, 59 ); + R4( a, b, c, d, e, 60 ); + R4( e, a, b, c, d, 61 ); + R4( d, e, a, b, c, 62 ); + R4( c, d, e, a, b, 63 ); + R4( b, c, d, e, a, 64 ); + R4( a, b, c, d, e, 65 ); + R4( e, a, b, c, d, 66 ); + R4( d, e, a, b, c, 67 ); + R4( c, d, e, a, b, 68 ); + R4( b, c, d, e, a, 69 ); + R4( a, b, c, d, e, 70 ); + R4( e, a, b, c, d, 71 ); + R4( d, e, a, b, c, 72 ); + R4( c, d, e, a, b, 73 ); + R4( b, c, d, e, a, 74 ); + R4( a, b, c, d, e, 75 ); + R4( e, a, b, c, d, 76 ); + R4( d, e, a, b, c, 77 ); + R4( c, d, e, a, b, 78 ); + R4( b, c, d, e, a, 79 ); + + // Add the working vars back into state[] + state[ 0 ] += a; + state[ 1 ] += b; + state[ 2 ] += c; + state[ 3 ] += d; + state[ 4 ] += e; + + // Wipe variables + a = 0; + b = 0; + c = 0; + d = 0; + e = 0; +} + +// Use this function to hash in binary data and strings +void CSHA1::Update( unsigned char* data, unsigned int len ) +{ + unsigned int i = 0, j = 0; + + j = ( m_count[ 0 ] >> 3 ) & 63; + + if ( ( m_count[ 0 ] += len << 3 ) < ( len << 3 ) ) + m_count[ 1 ] ++; + + m_count[ 1 ] += ( len >> 29 ); + + if ( ( j + len ) > 63 ) + { + memcpy( &m_buffer[ j ], data, ( i = 64 - j ) ); + Transform( m_state, m_buffer ); + + for ( ; i + 63 < len; i += 64 ) + { + Transform( m_state, &data[ i ] ); + } + + j = 0; + } + + else + i = 0; + + memcpy( &m_buffer[ j ], &data[ i ], len - i ); +} + +// Hash in file contents +bool CSHA1::HashFile( char *szFileName ) +{ + unsigned long ulFileSize = 0, ulRest = 0, ulBlocks = 0; + unsigned long i = 0; + unsigned char uData[ MAX_FILE_READ_BUFFER ]; + FILE *fIn = NULL; + + if ( ( fIn = fopen( szFileName, "rb" ) ) == NULL ) + return ( false ); + + fseek( fIn, 0, SEEK_END ); + + ulFileSize = ftell( fIn ); + + fseek( fIn, 0, SEEK_SET ); + + // This is faster + div_t temp; + + temp = div( ulFileSize, MAX_FILE_READ_BUFFER ); + + ulRest = temp.rem; + + ulBlocks = temp.quot; + + // ulRest = ulFileSize % MAX_FILE_READ_BUFFER; + // ulBlocks = ulFileSize / MAX_FILE_READ_BUFFER; + + for ( i = 0; i < ulBlocks; i++ ) + { + fread( uData, 1, MAX_FILE_READ_BUFFER, fIn ); + Update( uData, MAX_FILE_READ_BUFFER ); + } + + if ( ulRest != 0 ) + { + fread( uData, 1, ulRest, fIn ); + Update( uData, ulRest ); + } + + fclose( fIn ); + fIn = NULL; + + return ( true ); +} + +void CSHA1::Final() +{ + unsigned int i = 0; + unsigned char finalcount[ 8 ] = + { + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + for ( i = 0; i < 8; i++ ) + finalcount[ i ] = (unsigned char) ( ( m_count[ ( i >= 4 ? 0 : 1 ) ] + >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); // Endian independent + + Update( ( unsigned char * ) "\200", 1 ); + + while ( ( m_count[ 0 ] & 504 ) != 448 ) + Update( ( unsigned char * ) "\0", 1 ); + + Update( finalcount, 8 ); // Cause a SHA1Transform() + + for ( i = 0; i < 20; i++ ) + { + m_digest[ i ] = (unsigned char) ( ( m_state[ i >> 2 ] >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); + } + + // Wipe variables for security reasons + i = 0; + +// j = 0; + + memset( m_buffer, 0, 64 ); + + memset( m_state, 0, 20 ); + + memset( m_count, 0, 8 ); + + memset( finalcount, 0, 8 ); + + Transform( m_state, m_buffer ); +} + +// Get the final hash as a pre-formatted string +void CSHA1::ReportHash( char *szReport, unsigned char uReportType ) +{ + unsigned char i = 0; + char szTemp[ 4 ]; + + if ( uReportType == REPORT_HEX ) + { + sprintf( szTemp, "%02X", m_digest[ 0 ] ); + strcat( szReport, szTemp ); + + for ( i = 1; i < 20; i++ ) + { + sprintf( szTemp, " %02X", m_digest[ i ] ); + strcat( szReport, szTemp ); + } + } + + else + if ( uReportType == REPORT_DIGIT ) + { + sprintf( szTemp, "%u", m_digest[ 0 ] ); + strcat( szReport, szTemp ); + + for ( i = 1; i < 20; i++ ) + { + sprintf( szTemp, " %u", m_digest[ i ] ); + strcat( szReport, szTemp ); + } + } + + else + strcpy( szReport, "Error: Unknown report type!" ); +} + +// Get the raw message digest +void CSHA1::GetHash( unsigned char *uDest ) +{ + memcpy( uDest, m_digest, 20 ); +} + +// Get the raw message digest +// Added by Kevin to be quicker +unsigned char * CSHA1::GetHash( void ) const +{ + return ( unsigned char * ) m_digest; +} diff --git a/src/raknet/SHA1.h b/src/raknet/SHA1.h index 8437807..be92101 100755 --- a/src/raknet/SHA1.h +++ b/src/raknet/SHA1.h @@ -1,87 +1,87 @@ -/// \brief \b [Internal] SHA-1 computation class -/// -/// 100% free public domain implementation of the SHA-1 -/// algorithm by Dominik Reichl -/// -/// -/// === Test Vectors (from FIPS PUB 180-1) === -/// -/// "abc" -/// A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -/// -/// "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" -/// 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -/// -/// A million repetitions of "a" -/// 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F - -#ifndef ___SHA1_H___ -#define ___SHA1_H___ - -#include "RakMemoryOverride.h" -#include // Needed for file access - -#include // Needed for memset and memcpy - -#include // Needed for strcat and strcpy -#include "Export.h" - -#define MAX_FILE_READ_BUFFER 8000 - -#define SHA1_LENGTH 20 - -class RAK_DLL_EXPORT CSHA1 -{ - -public: - // Rotate x bits to the left - #define ROL32(value, bits) (((value)<<(bits))|((value)>>(32-(bits)))) - -#ifdef LITTLE_ENDIAN -#define SHABLK0(i) (block->l[i] = (ROL32(block->l[i],24) & 0xFF00FF00) \ - | (ROL32(block->l[i],8) & 0x00FF00FF)) -#else -#define SHABLK0(i) (block->l[i]) -#endif - -#define SHABLK(i) (block->l[i&15] = ROL32(block->l[(i+13)&15] ^ block->l[(i+8)&15] \ - ^ block->l[(i+2)&15] ^ block->l[i&15],1)) - - // SHA-1 rounds -#define R0(v,width,x,y,z,i) { z+=((width&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5); width=ROL32(width,30); } -#define R1(v,width,x,y,z,i) { z+=((width&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5); width=ROL32(width,30); } -#define R2(v,width,x,y,z,i) { z+=(width^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5); width=ROL32(width,30); } -#define R3(v,width,x,y,z,i) { z+=(((width|x)&y)|(width&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5); width=ROL32(width,30); } -#define R4(v,width,x,y,z,i) { z+=(width^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5); width=ROL32(width,30); } - - typedef union { - unsigned char c[ 64 ]; - unsigned int l[ 16 ]; - } SHA1_WORKSPACE_BLOCK; - /* Two different formats for ReportHash(...) - */ - enum { REPORT_HEX = 0, - REPORT_DIGIT = 1}; - - CSHA1(); - virtual ~CSHA1(); - - unsigned int m_state[ 5 ]; - unsigned int m_count[ 2 ]; - unsigned char m_buffer[ 64 ]; - unsigned char m_digest[ 20 ]; - void Reset(); - void Update( unsigned char* data, unsigned int len ); - bool HashFile( char *szFileName ); - void Final(); - void ReportHash( char *szReport, unsigned char uReportType = REPORT_HEX ); - void GetHash( unsigned char *uDest ); - unsigned char * GetHash( void ) const; - -private: - void Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ); - unsigned char workspace[ 64 ]; -}; - -#endif // ___SHA1_H___ - +/// \brief \b [Internal] SHA-1 computation class +/// +/// 100% free public domain implementation of the SHA-1 +/// algorithm by Dominik Reichl +/// +/// +/// === Test Vectors (from FIPS PUB 180-1) === +/// +/// "abc" +/// A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +/// +/// "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +/// 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +/// +/// A million repetitions of "a" +/// 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + +#ifndef ___SHA1_H___ +#define ___SHA1_H___ + +#include "RakMemoryOverride.h" +#include // Needed for file access + +#include // Needed for memset and memcpy + +#include // Needed for strcat and strcpy +#include "Export.h" + +#define MAX_FILE_READ_BUFFER 8000 + +#define SHA1_LENGTH 20 + +class RAK_DLL_EXPORT CSHA1 +{ + +public: + // Rotate x bits to the left + #define ROL32(value, bits) (((value)<<(bits))|((value)>>(32-(bits)))) + +#ifdef LITTLE_ENDIAN +#define SHABLK0(i) (block->l[i] = (ROL32(block->l[i],24) & 0xFF00FF00) \ + | (ROL32(block->l[i],8) & 0x00FF00FF)) +#else +#define SHABLK0(i) (block->l[i]) +#endif + +#define SHABLK(i) (block->l[i&15] = ROL32(block->l[(i+13)&15] ^ block->l[(i+8)&15] \ + ^ block->l[(i+2)&15] ^ block->l[i&15],1)) + + // SHA-1 rounds +#define R0(v,width,x,y,z,i) { z+=((width&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5); width=ROL32(width,30); } +#define R1(v,width,x,y,z,i) { z+=((width&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5); width=ROL32(width,30); } +#define R2(v,width,x,y,z,i) { z+=(width^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5); width=ROL32(width,30); } +#define R3(v,width,x,y,z,i) { z+=(((width|x)&y)|(width&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5); width=ROL32(width,30); } +#define R4(v,width,x,y,z,i) { z+=(width^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5); width=ROL32(width,30); } + + typedef union { + unsigned char c[ 64 ]; + unsigned int l[ 16 ]; + } SHA1_WORKSPACE_BLOCK; + /* Two different formats for ReportHash(...) + */ + enum { REPORT_HEX = 0, + REPORT_DIGIT = 1}; + + CSHA1(); + virtual ~CSHA1(); + + unsigned int m_state[ 5 ]; + unsigned int m_count[ 2 ]; + unsigned char m_buffer[ 64 ]; + unsigned char m_digest[ 20 ]; + void Reset(); + void Update( unsigned char* data, unsigned int len ); + bool HashFile( char *szFileName ); + void Final(); + void ReportHash( char *szReport, unsigned char uReportType = REPORT_HEX ); + void GetHash( unsigned char *uDest ); + unsigned char * GetHash( void ) const; + +private: + void Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ); + unsigned char workspace[ 64 ]; +}; + +#endif // ___SHA1_H___ + diff --git a/src/raknet/SecureHandshake.cpp b/src/raknet/SecureHandshake.cpp index e3b3c71..2c44168 100755 --- a/src/raknet/SecureHandshake.cpp +++ b/src/raknet/SecureHandshake.cpp @@ -1,54 +1,54 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" - -#if LIBCAT_SECURITY==1 - -// If building a RakNet DLL, be sure to tweak the CAT_EXPORT macro meaning -#if !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) -# define CAT_BUILD_DLL -#else -# define CAT_NEUTER_EXPORT -#endif - -#include "cat/src/port/EndianNeutral.cpp" -#include "cat/src/port/AlignedAlloc.cpp" -#include "cat/src/time/Clock.cpp" -#include "cat/src/threads/Mutex.cpp" -#include "cat/src/threads/Thread.cpp" -#include "cat/src/threads/WaitableFlag.cpp" -#include "cat/src/hash/MurmurHash2.cpp" -#include "cat/src/lang/Strings.cpp" - -#include "cat/src/math/BigRTL.cpp" -#include "cat/src/math/BigPseudoMersenne.cpp" -#include "cat/src/math/BigTwistedEdwards.cpp" - -#include "cat/src/crypt/SecureCompare.cpp" -#include "cat/src/crypt/cookie/CookieJar.cpp" -#include "cat/src/crypt/hash/HMAC_MD5.cpp" -#include "cat/src/crypt/privatekey/ChaCha.cpp" -#include "cat/src/crypt/hash/Skein.cpp" -#include "cat/src/crypt/hash/Skein256.cpp" -#include "cat/src/crypt/hash/Skein512.cpp" -#include "cat/src/crypt/pass/Passwords.cpp" - -#include "cat/src/crypt/rand/EntropyWindows.cpp" -#include "cat/src/crypt/rand/EntropyLinux.cpp" -#include "cat/src/crypt/rand/EntropyWindowsCE.cpp" -#include "cat/src/crypt/rand/EntropyGeneric.cpp" -#include "cat/src/crypt/rand/Fortuna.cpp" - -#include "cat/src/crypt/tunnel/KeyAgreement.cpp" -#include "cat/src/crypt/tunnel/AuthenticatedEncryption.cpp" -#include "cat/src/crypt/tunnel/KeyAgreementInitiator.cpp" -#include "cat/src/crypt/tunnel/KeyAgreementResponder.cpp" -#include "cat/src/crypt/tunnel/KeyMaker.cpp" - -#include "cat/src/crypt/tunnel/EasyHandshake.cpp" - -#endif // LIBCAT_SECURITY +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" + +#if LIBCAT_SECURITY==1 + +// If building a RakNet DLL, be sure to tweak the CAT_EXPORT macro meaning +#if !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) +# define CAT_BUILD_DLL +#else +# define CAT_NEUTER_EXPORT +#endif + +#include "cat/src/port/EndianNeutral.cpp" +#include "cat/src/port/AlignedAlloc.cpp" +#include "cat/src/time/Clock.cpp" +#include "cat/src/threads/Mutex.cpp" +#include "cat/src/threads/Thread.cpp" +#include "cat/src/threads/WaitableFlag.cpp" +#include "cat/src/hash/MurmurHash2.cpp" +#include "cat/src/lang/Strings.cpp" + +#include "cat/src/math/BigRTL.cpp" +#include "cat/src/math/BigPseudoMersenne.cpp" +#include "cat/src/math/BigTwistedEdwards.cpp" + +#include "cat/src/crypt/SecureCompare.cpp" +#include "cat/src/crypt/cookie/CookieJar.cpp" +#include "cat/src/crypt/hash/HMAC_MD5.cpp" +#include "cat/src/crypt/privatekey/ChaCha.cpp" +#include "cat/src/crypt/hash/Skein.cpp" +#include "cat/src/crypt/hash/Skein256.cpp" +#include "cat/src/crypt/hash/Skein512.cpp" +#include "cat/src/crypt/pass/Passwords.cpp" + +#include "cat/src/crypt/rand/EntropyWindows.cpp" +#include "cat/src/crypt/rand/EntropyLinux.cpp" +#include "cat/src/crypt/rand/EntropyWindowsCE.cpp" +#include "cat/src/crypt/rand/EntropyGeneric.cpp" +#include "cat/src/crypt/rand/Fortuna.cpp" + +#include "cat/src/crypt/tunnel/KeyAgreement.cpp" +#include "cat/src/crypt/tunnel/AuthenticatedEncryption.cpp" +#include "cat/src/crypt/tunnel/KeyAgreementInitiator.cpp" +#include "cat/src/crypt/tunnel/KeyAgreementResponder.cpp" +#include "cat/src/crypt/tunnel/KeyMaker.cpp" + +#include "cat/src/crypt/tunnel/EasyHandshake.cpp" + +#endif // LIBCAT_SECURITY diff --git a/src/raknet/SecureHandshake.h b/src/raknet/SecureHandshake.h index b289ff5..ce1ffe6 100755 --- a/src/raknet/SecureHandshake.h +++ b/src/raknet/SecureHandshake.h @@ -1,25 +1,25 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#ifndef SECURE_HANDSHAKE_H -#define SECURE_HANDSHAKE_H - -#include "NativeFeatureIncludes.h" - -#if LIBCAT_SECURITY==1 - -// If building a RakNet DLL, be sure to tweak the CAT_EXPORT macro meaning -#if !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) -# define CAT_BUILD_DLL -#else -# define CAT_NEUTER_EXPORT -#endif - -#include "cat/AllTunnel.hpp" - -#endif // LIBCAT_SECURITY - -#endif // SECURE_HANDSHAKE_H +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#ifndef SECURE_HANDSHAKE_H +#define SECURE_HANDSHAKE_H + +#include "NativeFeatureIncludes.h" + +#if LIBCAT_SECURITY==1 + +// If building a RakNet DLL, be sure to tweak the CAT_EXPORT macro meaning +#if !defined(_RAKNET_LIB) && defined(_RAKNET_DLL) +# define CAT_BUILD_DLL +#else +# define CAT_NEUTER_EXPORT +#endif + +#include "cat/AllTunnel.hpp" + +#endif // LIBCAT_SECURITY + +#endif // SECURE_HANDSHAKE_H diff --git a/src/raknet/SendToThread.cpp b/src/raknet/SendToThread.cpp index 0c3d313..127c7b8 100755 --- a/src/raknet/SendToThread.cpp +++ b/src/raknet/SendToThread.cpp @@ -1,77 +1,77 @@ -#include "SendToThread.h" -#ifdef USE_THREADED_SEND -#include "RakThread.h" -#include "InternalPacket.h" -#include "GetTime.h" - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 -#include "CCRakNetUDT.h" -#else -#include "CCRakNetSlidingWindow.h" -#endif - -using namespace RakNet; - -int SendToThread::refCount=0; -DataStructures::ThreadsafeAllocatingQueue SendToThread::objectQueue; -ThreadPool SendToThread::threadPool; - -SendToThread::SendToThreadBlock* SendToWorkerThread(SendToThread::SendToThreadBlock* input, bool *returnOutput, void* perThreadData) -{ - (void) perThreadData; - *returnOutput=false; -// RakNet::TimeUS *mostRecentTime=(RakNet::TimeUS *)input->data; -// *mostRecentTime=RakNet::GetTimeUS(); - SocketLayer::SendTo(input->s, input->data, input->dataWriteOffset, input->systemAddress, input->remotePortRakNetWasStartedOn_PS3, input->extraSocketOptions, _FILE_AND_LINE_); - SendToThread::objectQueue.Push(input); - return 0; -} -SendToThread::SendToThread() -{ -} -SendToThread::~SendToThread() -{ - -} -void SendToThread::AddRef(void) -{ - if (++refCount==1) - { - threadPool.StartThreads(1,0); - } -} -void SendToThread::Deref(void) -{ - if (refCount>0) - { - if (--refCount==0) - { - threadPool.StopThreads(); - RakAssert(threadPool.NumThreadsWorking()==0); - - unsigned i; - SendToThreadBlock* info; - for (i=0; i < threadPool.InputSize(); i++) - { - info = threadPool.GetInputAtIndex(i); - objectQueue.Push(info); - } - threadPool.ClearInput(); - objectQueue.Clear(_FILE_AND_LINE_); - } - } -} -SendToThread::SendToThreadBlock* SendToThread::AllocateBlock(void) -{ - SendToThread::SendToThreadBlock *b; - b=objectQueue.Pop(); - if (b==0) - b=objectQueue.Allocate(_FILE_AND_LINE_); - return b; -} -void SendToThread::ProcessBlock(SendToThread::SendToThreadBlock* threadedSend) -{ - RakAssert(threadedSend->dataWriteOffset>0 && threadedSend->dataWriteOffset<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); - threadPool.AddInput(SendToWorkerThread,threadedSend); -} -#endif +#include "SendToThread.h" +#ifdef USE_THREADED_SEND +#include "RakThread.h" +#include "InternalPacket.h" +#include "GetTime.h" + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 +#include "CCRakNetUDT.h" +#else +#include "CCRakNetSlidingWindow.h" +#endif + +using namespace RakNet; + +int SendToThread::refCount=0; +DataStructures::ThreadsafeAllocatingQueue SendToThread::objectQueue; +ThreadPool SendToThread::threadPool; + +SendToThread::SendToThreadBlock* SendToWorkerThread(SendToThread::SendToThreadBlock* input, bool *returnOutput, void* perThreadData) +{ + (void) perThreadData; + *returnOutput=false; +// RakNet::TimeUS *mostRecentTime=(RakNet::TimeUS *)input->data; +// *mostRecentTime=RakNet::GetTimeUS(); + SocketLayer::SendTo(input->s, input->data, input->dataWriteOffset, input->systemAddress, input->remotePortRakNetWasStartedOn_PS3, input->extraSocketOptions, _FILE_AND_LINE_); + SendToThread::objectQueue.Push(input); + return 0; +} +SendToThread::SendToThread() +{ +} +SendToThread::~SendToThread() +{ + +} +void SendToThread::AddRef(void) +{ + if (++refCount==1) + { + threadPool.StartThreads(1,0); + } +} +void SendToThread::Deref(void) +{ + if (refCount>0) + { + if (--refCount==0) + { + threadPool.StopThreads(); + RakAssert(threadPool.NumThreadsWorking()==0); + + unsigned i; + SendToThreadBlock* info; + for (i=0; i < threadPool.InputSize(); i++) + { + info = threadPool.GetInputAtIndex(i); + objectQueue.Push(info); + } + threadPool.ClearInput(); + objectQueue.Clear(_FILE_AND_LINE_); + } + } +} +SendToThread::SendToThreadBlock* SendToThread::AllocateBlock(void) +{ + SendToThread::SendToThreadBlock *b; + b=objectQueue.Pop(); + if (b==0) + b=objectQueue.Allocate(_FILE_AND_LINE_); + return b; +} +void SendToThread::ProcessBlock(SendToThread::SendToThreadBlock* threadedSend) +{ + RakAssert(threadedSend->dataWriteOffset>0 && threadedSend->dataWriteOffset<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); + threadPool.AddInput(SendToWorkerThread,threadedSend); +} +#endif diff --git a/src/raknet/SendToThread.h b/src/raknet/SendToThread.h index 676a5f4..df1bb51 100755 --- a/src/raknet/SendToThread.h +++ b/src/raknet/SendToThread.h @@ -1,47 +1,47 @@ -#ifndef __SENDTO_THREAD -#define __SENDTO_THREAD - -#include "RakNetDefines.h" - -#ifdef USE_THREADED_SEND - -#include "InternalPacket.h" -#include "SocketLayer.h" -#include "DS_ThreadsafeAllocatingQueue.h" -#include "ThreadPool.h" - -namespace RakNet -{ -class SendToThread -{ -public: - SendToThread(); - ~SendToThread(); - - struct SendToThreadBlock - { - SOCKET s; - SystemAddress systemAddress; - unsigned short remotePortRakNetWasStartedOn_PS3; - unsigned int extraSocketOptions; - char data[MAXIMUM_MTU_SIZE]; - unsigned short dataWriteOffset; - }; - - static SendToThreadBlock* AllocateBlock(void); - static void ProcessBlock(SendToThreadBlock* threadedSend); - - static void AddRef(void); - static void Deref(void); - static DataStructures::ThreadsafeAllocatingQueue objectQueue; -protected: - static int refCount; - static ThreadPool threadPool; - -}; -} - - -#endif - -#endif +#ifndef __SENDTO_THREAD +#define __SENDTO_THREAD + +#include "RakNetDefines.h" + +#ifdef USE_THREADED_SEND + +#include "InternalPacket.h" +#include "SocketLayer.h" +#include "DS_ThreadsafeAllocatingQueue.h" +#include "ThreadPool.h" + +namespace RakNet +{ +class SendToThread +{ +public: + SendToThread(); + ~SendToThread(); + + struct SendToThreadBlock + { + SOCKET s; + SystemAddress systemAddress; + unsigned short remotePortRakNetWasStartedOn_PS3; + unsigned int extraSocketOptions; + char data[MAXIMUM_MTU_SIZE]; + unsigned short dataWriteOffset; + }; + + static SendToThreadBlock* AllocateBlock(void); + static void ProcessBlock(SendToThreadBlock* threadedSend); + + static void AddRef(void); + static void Deref(void); + static DataStructures::ThreadsafeAllocatingQueue objectQueue; +protected: + static int refCount; + static ThreadPool threadPool; + +}; +} + + +#endif + +#endif diff --git a/src/raknet/SignaledEvent.cpp b/src/raknet/SignaledEvent.cpp index 6e0ca07..b22599f 100755 --- a/src/raknet/SignaledEvent.cpp +++ b/src/raknet/SignaledEvent.cpp @@ -1,245 +1,245 @@ -#include "SignaledEvent.h" -#include "RakAssert.h" -#include "RakSleep.h" - -#if defined(__GNUC__) -#include -#include -#endif - -using namespace RakNet; - - - - - -SignaledEvent::SignaledEvent() -{ -#ifdef _WIN32 - eventList=INVALID_HANDLE_VALUE; - - -#else - isSignaled=false; -#endif -} -SignaledEvent::~SignaledEvent() -{ - // Intentionally do not close event, so it doesn't close twice on linux -} - -void SignaledEvent::InitEvent(void) -{ -#ifdef _WIN32 - eventList=CreateEvent(0, false, false, 0); - - - - - - - - - -#else - -#if !defined(ANDROID) - pthread_condattr_init( &condAttr ); - pthread_cond_init(&eventList, &condAttr); -#else - pthread_cond_init(&eventList, 0); -#endif - pthread_mutexattr_init( &mutexAttr ); - pthread_mutex_init(&hMutex, &mutexAttr); -#endif -} - -void SignaledEvent::CloseEvent(void) -{ -#ifdef _WIN32 - if (eventList!=INVALID_HANDLE_VALUE) - { - CloseHandle(eventList); - eventList=INVALID_HANDLE_VALUE; - } - - - - - - - - - -#else - pthread_cond_destroy(&eventList); - pthread_mutex_destroy(&hMutex); -#if !defined(ANDROID) - pthread_condattr_destroy( &condAttr ); -#endif - pthread_mutexattr_destroy( &mutexAttr ); -#endif -} - -void SignaledEvent::SetEvent(void) -{ -#ifdef _WIN32 - ::SetEvent(eventList); - - - - - - - - - - -#else - // Different from SetEvent which stays signaled. - // We have to record manually that the event was signaled - isSignaledMutex.Lock(); - isSignaled=true; - isSignaledMutex.Unlock(); - - // Unblock waiting threads - pthread_cond_broadcast(&eventList); -#endif -} - -void SignaledEvent::WaitOnEvent(int timeoutMs) -{ -#ifdef _WIN32 -// WaitForMultipleObjects( -// 2, -// eventList, -// false, -// timeoutMs); - WaitForSingleObject(eventList,timeoutMs); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#else - - // If was previously set signaled, just unset and return - isSignaledMutex.Lock(); - if (isSignaled==true) - { - isSignaled=false; - isSignaledMutex.Unlock(); - return; - } - isSignaledMutex.Unlock(); - - - struct timespec ts; - - // Else wait for SetEvent to be called - - - - - - - - - - - - - - - int rc; - struct timeval tp; - rc = gettimeofday(&tp, NULL); - ts.tv_sec = tp.tv_sec; - ts.tv_nsec = tp.tv_usec * 1000; - - - while (timeoutMs > 30) - { - // Wait 30 milliseconds for the signal, then check again. - // This is in case we missed the signal between the top of this function and pthread_cond_timedwait, or after the end of the loop and pthread_cond_timedwait - ts.tv_nsec += 30*1000000; - if (ts.tv_nsec >= 1000000000) - { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - - // [SBC] added mutex lock/unlock around cond_timedwait. - // this prevents airplay from generating a whole much of errors. - // not sure how this works on other platforms since according to - // the docs you are suppost to hold the lock before you wait - // on the cond. - pthread_mutex_lock(&hMutex); - pthread_cond_timedwait(&eventList, &hMutex, &ts); - pthread_mutex_unlock(&hMutex); - - timeoutMs-=30; - - isSignaledMutex.Lock(); - if (isSignaled==true) - { - isSignaled=false; - isSignaledMutex.Unlock(); - return; - } - isSignaledMutex.Unlock(); - } - - // Wait the remaining time, and turn off the signal in case it was set - ts.tv_nsec += timeoutMs*1000000; - if (ts.tv_nsec >= 1000000000) - { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - - pthread_mutex_lock(&hMutex); - pthread_cond_timedwait(&eventList, &hMutex, &ts); - pthread_mutex_unlock(&hMutex); - - isSignaledMutex.Lock(); - isSignaled=false; - isSignaledMutex.Unlock(); -#endif -} +#include "SignaledEvent.h" +#include "RakAssert.h" +#include "RakSleep.h" + +#if defined(__GNUC__) +#include +#include +#endif + +using namespace RakNet; + + + + + +SignaledEvent::SignaledEvent() +{ +#ifdef _WIN32 + eventList=INVALID_HANDLE_VALUE; + + +#else + isSignaled=false; +#endif +} +SignaledEvent::~SignaledEvent() +{ + // Intentionally do not close event, so it doesn't close twice on linux +} + +void SignaledEvent::InitEvent(void) +{ +#ifdef _WIN32 + eventList=CreateEvent(0, false, false, 0); + + + + + + + + + +#else + +#if !defined(ANDROID) + pthread_condattr_init( &condAttr ); + pthread_cond_init(&eventList, &condAttr); +#else + pthread_cond_init(&eventList, 0); +#endif + pthread_mutexattr_init( &mutexAttr ); + pthread_mutex_init(&hMutex, &mutexAttr); +#endif +} + +void SignaledEvent::CloseEvent(void) +{ +#ifdef _WIN32 + if (eventList!=INVALID_HANDLE_VALUE) + { + CloseHandle(eventList); + eventList=INVALID_HANDLE_VALUE; + } + + + + + + + + + +#else + pthread_cond_destroy(&eventList); + pthread_mutex_destroy(&hMutex); +#if !defined(ANDROID) + pthread_condattr_destroy( &condAttr ); +#endif + pthread_mutexattr_destroy( &mutexAttr ); +#endif +} + +void SignaledEvent::SetEvent(void) +{ +#ifdef _WIN32 + ::SetEvent(eventList); + + + + + + + + + + +#else + // Different from SetEvent which stays signaled. + // We have to record manually that the event was signaled + isSignaledMutex.Lock(); + isSignaled=true; + isSignaledMutex.Unlock(); + + // Unblock waiting threads + pthread_cond_broadcast(&eventList); +#endif +} + +void SignaledEvent::WaitOnEvent(int timeoutMs) +{ +#ifdef _WIN32 +// WaitForMultipleObjects( +// 2, +// eventList, +// false, +// timeoutMs); + WaitForSingleObject(eventList,timeoutMs); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#else + + // If was previously set signaled, just unset and return + isSignaledMutex.Lock(); + if (isSignaled==true) + { + isSignaled=false; + isSignaledMutex.Unlock(); + return; + } + isSignaledMutex.Unlock(); + + + struct timespec ts; + + // Else wait for SetEvent to be called + + + + + + + + + + + + + + + int rc; + struct timeval tp; + rc = gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + + + while (timeoutMs > 30) + { + // Wait 30 milliseconds for the signal, then check again. + // This is in case we missed the signal between the top of this function and pthread_cond_timedwait, or after the end of the loop and pthread_cond_timedwait + ts.tv_nsec += 30*1000000; + if (ts.tv_nsec >= 1000000000) + { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + + // [SBC] added mutex lock/unlock around cond_timedwait. + // this prevents airplay from generating a whole much of errors. + // not sure how this works on other platforms since according to + // the docs you are suppost to hold the lock before you wait + // on the cond. + pthread_mutex_lock(&hMutex); + pthread_cond_timedwait(&eventList, &hMutex, &ts); + pthread_mutex_unlock(&hMutex); + + timeoutMs-=30; + + isSignaledMutex.Lock(); + if (isSignaled==true) + { + isSignaled=false; + isSignaledMutex.Unlock(); + return; + } + isSignaledMutex.Unlock(); + } + + // Wait the remaining time, and turn off the signal in case it was set + ts.tv_nsec += timeoutMs*1000000; + if (ts.tv_nsec >= 1000000000) + { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + + pthread_mutex_lock(&hMutex); + pthread_cond_timedwait(&eventList, &hMutex, &ts); + pthread_mutex_unlock(&hMutex); + + isSignaledMutex.Lock(); + isSignaled=false; + isSignaledMutex.Unlock(); +#endif +} diff --git a/src/raknet/SignaledEvent.h b/src/raknet/SignaledEvent.h index b744b40..3d36948 100755 --- a/src/raknet/SignaledEvent.h +++ b/src/raknet/SignaledEvent.h @@ -1,59 +1,59 @@ -#ifndef __SIGNALED_EVENT_H -#define __SIGNALED_EVENT_H - - - -#if defined(_WIN32) -#include - - - -#else - #include - #include - #include "SimpleMutex.h" - - - - -#endif - -#include "Export.h" - -namespace RakNet -{ - -class RAK_DLL_EXPORT SignaledEvent -{ -public: - SignaledEvent(); - ~SignaledEvent(); - - void InitEvent(void); - void CloseEvent(void); - void SetEvent(void); - void WaitOnEvent(int timeoutMs); - -protected: -#ifdef _WIN32 - HANDLE eventList; - - - - - -#else - SimpleMutex isSignaledMutex; - bool isSignaled; -#if !defined(ANDROID) - pthread_condattr_t condAttr; -#endif - pthread_cond_t eventList; - pthread_mutex_t hMutex; - pthread_mutexattr_t mutexAttr; -#endif -}; - -} // namespace RakNet - -#endif +#ifndef __SIGNALED_EVENT_H +#define __SIGNALED_EVENT_H + + + +#if defined(_WIN32) +#include + + + +#else + #include + #include + #include "SimpleMutex.h" + + + + +#endif + +#include "Export.h" + +namespace RakNet +{ + +class RAK_DLL_EXPORT SignaledEvent +{ +public: + SignaledEvent(); + ~SignaledEvent(); + + void InitEvent(void); + void CloseEvent(void); + void SetEvent(void); + void WaitOnEvent(int timeoutMs); + +protected: +#ifdef _WIN32 + HANDLE eventList; + + + + + +#else + SimpleMutex isSignaledMutex; + bool isSignaled; +#if !defined(ANDROID) + pthread_condattr_t condAttr; +#endif + pthread_cond_t eventList; + pthread_mutex_t hMutex; + pthread_mutexattr_t mutexAttr; +#endif +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/SimpleMutex.cpp b/src/raknet/SimpleMutex.cpp index 0f5eae7..26e90b5 100755 --- a/src/raknet/SimpleMutex.cpp +++ b/src/raknet/SimpleMutex.cpp @@ -1,181 +1,181 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "SimpleMutex.h" -#include "RakAssert.h" - -using namespace RakNet; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SimpleMutex::SimpleMutex() //: isInitialized(false) -{ - - - - - - - - // Prior implementation of Initializing in Lock() was not threadsafe - Init(); -} - -SimpleMutex::~SimpleMutex() -{ -// if (isInitialized==false) -// return; -#ifdef _WIN32 - // CloseHandle(hMutex); - DeleteCriticalSection(&criticalSection); - - - - - - -#else - pthread_mutex_destroy(&hMutex); -#endif - - - - - - - -} - -#ifdef _WIN32 -#ifdef _DEBUG -#include -#endif -#endif - -void SimpleMutex::Lock(void) -{ -// if (isInitialized==false) -// Init(); - -#ifdef _WIN32 - /* - DWORD d = WaitForSingleObject(hMutex, INFINITE); - #ifdef _DEBUG - if (d==WAIT_FAILED) - { - LPVOID messageBuffer; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPTSTR) &messageBuffer, - 0, - NULL - ); - // Process any inserts in messageBuffer. - // ... - // Display the string. - //MessageBox( NULL, (LPCTSTR)messageBuffer, "Error", MB_OK | MB_ICONINFORMATION ); - RAKNET_DEBUG_PRINTF("SimpleMutex error: %s", messageBuffer); - // Free the buffer. - LocalFree( messageBuffer ); - - } - - RakAssert(d==WAIT_OBJECT_0); - */ - EnterCriticalSection(&criticalSection); - - - - - - -#else - int error = pthread_mutex_lock(&hMutex); - (void) error; - RakAssert(error==0); -#endif -} - -void SimpleMutex::Unlock(void) -{ -// if (isInitialized==false) -// return; -#ifdef _WIN32 - // ReleaseMutex(hMutex); - LeaveCriticalSection(&criticalSection); - - - - - - -#else - int error = pthread_mutex_unlock(&hMutex); - (void) error; - RakAssert(error==0); -#endif -} - -void SimpleMutex::Init(void) -{ -#ifdef _WIN32 - // hMutex = CreateMutex(NULL, FALSE, 0); - // RakAssert(hMutex); - InitializeCriticalSection(&criticalSection); - - - - - - - - -#else - int error = pthread_mutex_init(&hMutex, 0); - (void) error; - RakAssert(error==0); -#endif -// isInitialized=true; -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "SimpleMutex.h" +#include "RakAssert.h" + +using namespace RakNet; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +SimpleMutex::SimpleMutex() //: isInitialized(false) +{ + + + + + + + + // Prior implementation of Initializing in Lock() was not threadsafe + Init(); +} + +SimpleMutex::~SimpleMutex() +{ +// if (isInitialized==false) +// return; +#ifdef _WIN32 + // CloseHandle(hMutex); + DeleteCriticalSection(&criticalSection); + + + + + + +#else + pthread_mutex_destroy(&hMutex); +#endif + + + + + + + +} + +#ifdef _WIN32 +#ifdef _DEBUG +#include +#endif +#endif + +void SimpleMutex::Lock(void) +{ +// if (isInitialized==false) +// Init(); + +#ifdef _WIN32 + /* + DWORD d = WaitForSingleObject(hMutex, INFINITE); + #ifdef _DEBUG + if (d==WAIT_FAILED) + { + LPVOID messageBuffer; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &messageBuffer, + 0, + NULL + ); + // Process any inserts in messageBuffer. + // ... + // Display the string. + //MessageBox( NULL, (LPCTSTR)messageBuffer, "Error", MB_OK | MB_ICONINFORMATION ); + RAKNET_DEBUG_PRINTF("SimpleMutex error: %s", messageBuffer); + // Free the buffer. + LocalFree( messageBuffer ); + + } + + RakAssert(d==WAIT_OBJECT_0); + */ + EnterCriticalSection(&criticalSection); + + + + + + +#else + int error = pthread_mutex_lock(&hMutex); + (void) error; + RakAssert(error==0); +#endif +} + +void SimpleMutex::Unlock(void) +{ +// if (isInitialized==false) +// return; +#ifdef _WIN32 + // ReleaseMutex(hMutex); + LeaveCriticalSection(&criticalSection); + + + + + + +#else + int error = pthread_mutex_unlock(&hMutex); + (void) error; + RakAssert(error==0); +#endif +} + +void SimpleMutex::Init(void) +{ +#ifdef _WIN32 + // hMutex = CreateMutex(NULL, FALSE, 0); + // RakAssert(hMutex); + InitializeCriticalSection(&criticalSection); + + + + + + + + +#else + int error = pthread_mutex_init(&hMutex, 0); + (void) error; + RakAssert(error==0); +#endif +// isInitialized=true; +} diff --git a/src/raknet/SimpleMutex.h b/src/raknet/SimpleMutex.h index cf28355..8868851 100755 --- a/src/raknet/SimpleMutex.h +++ b/src/raknet/SimpleMutex.h @@ -1,70 +1,70 @@ -/// \file -/// \brief \b [Internal] Encapsulates a mutex -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __SIMPLE_MUTEX_H -#define __SIMPLE_MUTEX_H - -#include "RakMemoryOverride.h" - - -#if defined(_WIN32) -#include "WindowsIncludes.h" - - -#else -#include -#include -#endif -#include "Export.h" - -namespace RakNet -{ - -/// \brief An easy to use mutex. -/// -/// I wrote this because the version that comes with Windows is too complicated and requires too much code to use. -/// @remark Previously I used this everywhere, and in fact for a year or two RakNet was totally threadsafe. While doing profiling, I saw that this function was incredibly slow compared to the blazing performance of everything else, so switched to single producer / consumer everywhere. Now the user thread of RakNet is not threadsafe, but it's 100X faster than before. -class RAK_DLL_EXPORT SimpleMutex -{ -public: - - // Constructor - SimpleMutex(); - - // Destructor - ~SimpleMutex(); - - // Locks the mutex. Slow! - void Lock(void); - - // Unlocks the mutex. - void Unlock(void); - - - - - - - -private: - void Init(void); -#ifdef _WIN32 - CRITICAL_SECTION criticalSection; /// Docs say this is faster than a mutex for single process access - - -#else - pthread_mutex_t hMutex; -#endif - // Not threadsafe - // bool isInitialized; -}; - -} // namespace RakNet - -#endif - +/// \file +/// \brief \b [Internal] Encapsulates a mutex +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __SIMPLE_MUTEX_H +#define __SIMPLE_MUTEX_H + +#include "RakMemoryOverride.h" + + +#if defined(_WIN32) +#include "WindowsIncludes.h" + + +#else +#include +#include +#endif +#include "Export.h" + +namespace RakNet +{ + +/// \brief An easy to use mutex. +/// +/// I wrote this because the version that comes with Windows is too complicated and requires too much code to use. +/// @remark Previously I used this everywhere, and in fact for a year or two RakNet was totally threadsafe. While doing profiling, I saw that this function was incredibly slow compared to the blazing performance of everything else, so switched to single producer / consumer everywhere. Now the user thread of RakNet is not threadsafe, but it's 100X faster than before. +class RAK_DLL_EXPORT SimpleMutex +{ +public: + + // Constructor + SimpleMutex(); + + // Destructor + ~SimpleMutex(); + + // Locks the mutex. Slow! + void Lock(void); + + // Unlocks the mutex. + void Unlock(void); + + + + + + + +private: + void Init(void); +#ifdef _WIN32 + CRITICAL_SECTION criticalSection; /// Docs say this is faster than a mutex for single process access + + +#else + pthread_mutex_t hMutex; +#endif + // Not threadsafe + // bool isInitialized; +}; + +} // namespace RakNet + +#endif + diff --git a/src/raknet/SingleProducerConsumer.h b/src/raknet/SingleProducerConsumer.h index 8897e40..7bffbfc 100755 --- a/src/raknet/SingleProducerConsumer.h +++ b/src/raknet/SingleProducerConsumer.h @@ -1,259 +1,259 @@ -/// \file -/// \brief \b [Internal] Passes queued data between threads using a circular buffer with read and write pointers -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __SINGLE_PRODUCER_CONSUMER_H -#define __SINGLE_PRODUCER_CONSUMER_H - -#include "RakAssert.h" - -static const int MINIMUM_LIST_SIZE=8; - -#include "RakMemoryOverride.h" -#include "Export.h" - -/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures -/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. -namespace DataStructures -{ - /// \brief A single producer consumer implementation without critical sections. - template - class RAK_DLL_EXPORT SingleProducerConsumer - { - public: - // Constructor - SingleProducerConsumer(); - - // Destructor - ~SingleProducerConsumer(); - - /// WriteLock must be immediately followed by WriteUnlock. These two functions must be called in the same thread. - /// \return A pointer to a block of data you can write to. - SingleProducerConsumerType* WriteLock(void); - - /// Call if you don't want to write to a block of data from WriteLock() after all. - /// Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored - /// \param[in] cancelToLocation Which WriteLock() to cancel. - void CancelWriteLock(SingleProducerConsumerType* cancelToLocation); - - /// Call when you are done writing to a block of memory returned by WriteLock() - void WriteUnlock(void); - - /// ReadLock must be immediately followed by ReadUnlock. These two functions must be called in the same thread. - /// \retval 0 No data is availble to read - /// \retval Non-zero The data previously written to, in another thread, by WriteLock followed by WriteUnlock. - SingleProducerConsumerType* ReadLock(void); - - // Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored - /// param[in] Which ReadLock() to cancel. - void CancelReadLock(SingleProducerConsumerType* cancelToLocation); - - /// Signals that we are done reading the the data from the least recent call of ReadLock. - /// At this point that pointer is no longer valid, and should no longer be read. - void ReadUnlock(void); - - /// Clear is not thread-safe and none of the lock or unlock functions should be called while it is running. - void Clear(void); - - /// This function will estimate how many elements are waiting to be read. It's threadsafe enough that the value returned is stable, but not threadsafe enough to give accurate results. - /// \return An ESTIMATE of how many data elements are waiting to be read - int Size(void) const; - - /// Make sure that the pointer we done reading for the call to ReadUnlock is the right pointer. - /// param[in] A previous pointer returned by ReadLock() - bool CheckReadUnlockOrder(const SingleProducerConsumerType* data) const; - - /// Returns if ReadUnlock was called before ReadLock - /// \return If the read is locked - bool ReadIsLocked(void) const; - - private: - struct DataPlusPtr - { - DataPlusPtr () {readyToRead=false;} - SingleProducerConsumerType object; - - // Ready to read is so we can use an equality boolean comparison, in case the writePointer var is trashed while context switching. - volatile bool readyToRead; - volatile DataPlusPtr *next; - }; - volatile DataPlusPtr *readAheadPointer; - volatile DataPlusPtr *writeAheadPointer; - volatile DataPlusPtr *readPointer; - volatile DataPlusPtr *writePointer; - unsigned readCount, writeCount; - }; - - template - SingleProducerConsumer::SingleProducerConsumer() - { - // Preallocate - readPointer = RakNet::OP_NEW( _FILE_AND_LINE_ ); - writePointer=readPointer; - readPointer->next = RakNet::OP_NEW( _FILE_AND_LINE_ ); - int listSize; -#ifdef _DEBUG - RakAssert(MINIMUM_LIST_SIZE>=3); -#endif - for (listSize=2; listSize < MINIMUM_LIST_SIZE; listSize++) - { - readPointer=readPointer->next; - readPointer->next = RakNet::OP_NEW( _FILE_AND_LINE_ ); - } - readPointer->next->next=writePointer; // last to next = start - readPointer=writePointer; - readAheadPointer=readPointer; - writeAheadPointer=writePointer; - readCount=writeCount=0; - } - - template - SingleProducerConsumer::~SingleProducerConsumer() - { - volatile DataPlusPtr *next; - readPointer=writeAheadPointer->next; - while (readPointer!=writeAheadPointer) - { - next=readPointer->next; - RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_); - readPointer=next; - } - RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_); - } - - template - SingleProducerConsumerType* SingleProducerConsumer::WriteLock( void ) - { - if (writeAheadPointer->next==readPointer || - writeAheadPointer->next->readyToRead==true) - { - volatile DataPlusPtr *originalNext=writeAheadPointer->next; - writeAheadPointer->next=RakNet::OP_NEW(_FILE_AND_LINE_); - RakAssert(writeAheadPointer->next); - writeAheadPointer->next->next=originalNext; - } - - volatile DataPlusPtr *last; - last=writeAheadPointer; - writeAheadPointer=writeAheadPointer->next; - - return (SingleProducerConsumerType*) last; - } - - template - void SingleProducerConsumer::CancelWriteLock( SingleProducerConsumerType* cancelToLocation ) - { - writeAheadPointer=(DataPlusPtr *)cancelToLocation; - } - - template - void SingleProducerConsumer::WriteUnlock( void ) - { - // DataPlusPtr *dataContainer = (DataPlusPtr *)structure; - -#ifdef _DEBUG - RakAssert(writePointer->next!=readPointer); - RakAssert(writePointer!=writeAheadPointer); -#endif - - writeCount++; - // User is done with the data, allow send by updating the write pointer - writePointer->readyToRead=true; - writePointer=writePointer->next; - } - - template - SingleProducerConsumerType* SingleProducerConsumer::ReadLock( void ) - { - if (readAheadPointer==writePointer || - readAheadPointer->readyToRead==false) - { - return 0; - } - - volatile DataPlusPtr *last; - last=readAheadPointer; - readAheadPointer=readAheadPointer->next; - return (SingleProducerConsumerType*)last; - } - - template - void SingleProducerConsumer::CancelReadLock( SingleProducerConsumerType* cancelToLocation ) - { -#ifdef _DEBUG - RakAssert(readPointer!=writePointer); -#endif - readAheadPointer=(DataPlusPtr *)cancelToLocation; - } - - template - void SingleProducerConsumer::ReadUnlock( void ) - { -#ifdef _DEBUG - RakAssert(readAheadPointer!=readPointer); // If hits, then called ReadUnlock before ReadLock - RakAssert(readPointer!=writePointer); // If hits, then called ReadUnlock when Read returns 0 -#endif - readCount++; - - // Allow writes to this memory block - readPointer->readyToRead=false; - readPointer=readPointer->next; - } - - template - void SingleProducerConsumer::Clear( void ) - { - // Shrink the list down to MINIMUM_LIST_SIZE elements - volatile DataPlusPtr *next; - writePointer=readPointer->next; - - int listSize=1; - next=readPointer->next; - while (next!=readPointer) - { - listSize++; - next=next->next; - } - - while (listSize-- > MINIMUM_LIST_SIZE) - { - next=writePointer->next; -#ifdef _DEBUG - RakAssert(writePointer!=readPointer); -#endif - RakNet::OP_DELETE((char*) writePointer, _FILE_AND_LINE_); - writePointer=next; - } - - readPointer->next=writePointer; - writePointer=readPointer; - readAheadPointer=readPointer; - writeAheadPointer=writePointer; - readCount=writeCount=0; - } - - template - int SingleProducerConsumer::Size( void ) const - { - return writeCount-readCount; - } - - template - bool SingleProducerConsumer::CheckReadUnlockOrder(const SingleProducerConsumerType* data) const - { - return const_cast(&readPointer->object) == data; - } - - - template - bool SingleProducerConsumer::ReadIsLocked(void) const - { - return readAheadPointer!=readPointer; - } -} - -#endif +/// \file +/// \brief \b [Internal] Passes queued data between threads using a circular buffer with read and write pointers +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __SINGLE_PRODUCER_CONSUMER_H +#define __SINGLE_PRODUCER_CONSUMER_H + +#include "RakAssert.h" + +static const int MINIMUM_LIST_SIZE=8; + +#include "RakMemoryOverride.h" +#include "Export.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief A single producer consumer implementation without critical sections. + template + class RAK_DLL_EXPORT SingleProducerConsumer + { + public: + // Constructor + SingleProducerConsumer(); + + // Destructor + ~SingleProducerConsumer(); + + /// WriteLock must be immediately followed by WriteUnlock. These two functions must be called in the same thread. + /// \return A pointer to a block of data you can write to. + SingleProducerConsumerType* WriteLock(void); + + /// Call if you don't want to write to a block of data from WriteLock() after all. + /// Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored + /// \param[in] cancelToLocation Which WriteLock() to cancel. + void CancelWriteLock(SingleProducerConsumerType* cancelToLocation); + + /// Call when you are done writing to a block of memory returned by WriteLock() + void WriteUnlock(void); + + /// ReadLock must be immediately followed by ReadUnlock. These two functions must be called in the same thread. + /// \retval 0 No data is availble to read + /// \retval Non-zero The data previously written to, in another thread, by WriteLock followed by WriteUnlock. + SingleProducerConsumerType* ReadLock(void); + + // Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored + /// param[in] Which ReadLock() to cancel. + void CancelReadLock(SingleProducerConsumerType* cancelToLocation); + + /// Signals that we are done reading the the data from the least recent call of ReadLock. + /// At this point that pointer is no longer valid, and should no longer be read. + void ReadUnlock(void); + + /// Clear is not thread-safe and none of the lock or unlock functions should be called while it is running. + void Clear(void); + + /// This function will estimate how many elements are waiting to be read. It's threadsafe enough that the value returned is stable, but not threadsafe enough to give accurate results. + /// \return An ESTIMATE of how many data elements are waiting to be read + int Size(void) const; + + /// Make sure that the pointer we done reading for the call to ReadUnlock is the right pointer. + /// param[in] A previous pointer returned by ReadLock() + bool CheckReadUnlockOrder(const SingleProducerConsumerType* data) const; + + /// Returns if ReadUnlock was called before ReadLock + /// \return If the read is locked + bool ReadIsLocked(void) const; + + private: + struct DataPlusPtr + { + DataPlusPtr () {readyToRead=false;} + SingleProducerConsumerType object; + + // Ready to read is so we can use an equality boolean comparison, in case the writePointer var is trashed while context switching. + volatile bool readyToRead; + volatile DataPlusPtr *next; + }; + volatile DataPlusPtr *readAheadPointer; + volatile DataPlusPtr *writeAheadPointer; + volatile DataPlusPtr *readPointer; + volatile DataPlusPtr *writePointer; + unsigned readCount, writeCount; + }; + + template + SingleProducerConsumer::SingleProducerConsumer() + { + // Preallocate + readPointer = RakNet::OP_NEW( _FILE_AND_LINE_ ); + writePointer=readPointer; + readPointer->next = RakNet::OP_NEW( _FILE_AND_LINE_ ); + int listSize; +#ifdef _DEBUG + RakAssert(MINIMUM_LIST_SIZE>=3); +#endif + for (listSize=2; listSize < MINIMUM_LIST_SIZE; listSize++) + { + readPointer=readPointer->next; + readPointer->next = RakNet::OP_NEW( _FILE_AND_LINE_ ); + } + readPointer->next->next=writePointer; // last to next = start + readPointer=writePointer; + readAheadPointer=readPointer; + writeAheadPointer=writePointer; + readCount=writeCount=0; + } + + template + SingleProducerConsumer::~SingleProducerConsumer() + { + volatile DataPlusPtr *next; + readPointer=writeAheadPointer->next; + while (readPointer!=writeAheadPointer) + { + next=readPointer->next; + RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_); + readPointer=next; + } + RakNet::OP_DELETE((char*) readPointer, _FILE_AND_LINE_); + } + + template + SingleProducerConsumerType* SingleProducerConsumer::WriteLock( void ) + { + if (writeAheadPointer->next==readPointer || + writeAheadPointer->next->readyToRead==true) + { + volatile DataPlusPtr *originalNext=writeAheadPointer->next; + writeAheadPointer->next=RakNet::OP_NEW(_FILE_AND_LINE_); + RakAssert(writeAheadPointer->next); + writeAheadPointer->next->next=originalNext; + } + + volatile DataPlusPtr *last; + last=writeAheadPointer; + writeAheadPointer=writeAheadPointer->next; + + return (SingleProducerConsumerType*) last; + } + + template + void SingleProducerConsumer::CancelWriteLock( SingleProducerConsumerType* cancelToLocation ) + { + writeAheadPointer=(DataPlusPtr *)cancelToLocation; + } + + template + void SingleProducerConsumer::WriteUnlock( void ) + { + // DataPlusPtr *dataContainer = (DataPlusPtr *)structure; + +#ifdef _DEBUG + RakAssert(writePointer->next!=readPointer); + RakAssert(writePointer!=writeAheadPointer); +#endif + + writeCount++; + // User is done with the data, allow send by updating the write pointer + writePointer->readyToRead=true; + writePointer=writePointer->next; + } + + template + SingleProducerConsumerType* SingleProducerConsumer::ReadLock( void ) + { + if (readAheadPointer==writePointer || + readAheadPointer->readyToRead==false) + { + return 0; + } + + volatile DataPlusPtr *last; + last=readAheadPointer; + readAheadPointer=readAheadPointer->next; + return (SingleProducerConsumerType*)last; + } + + template + void SingleProducerConsumer::CancelReadLock( SingleProducerConsumerType* cancelToLocation ) + { +#ifdef _DEBUG + RakAssert(readPointer!=writePointer); +#endif + readAheadPointer=(DataPlusPtr *)cancelToLocation; + } + + template + void SingleProducerConsumer::ReadUnlock( void ) + { +#ifdef _DEBUG + RakAssert(readAheadPointer!=readPointer); // If hits, then called ReadUnlock before ReadLock + RakAssert(readPointer!=writePointer); // If hits, then called ReadUnlock when Read returns 0 +#endif + readCount++; + + // Allow writes to this memory block + readPointer->readyToRead=false; + readPointer=readPointer->next; + } + + template + void SingleProducerConsumer::Clear( void ) + { + // Shrink the list down to MINIMUM_LIST_SIZE elements + volatile DataPlusPtr *next; + writePointer=readPointer->next; + + int listSize=1; + next=readPointer->next; + while (next!=readPointer) + { + listSize++; + next=next->next; + } + + while (listSize-- > MINIMUM_LIST_SIZE) + { + next=writePointer->next; +#ifdef _DEBUG + RakAssert(writePointer!=readPointer); +#endif + RakNet::OP_DELETE((char*) writePointer, _FILE_AND_LINE_); + writePointer=next; + } + + readPointer->next=writePointer; + writePointer=readPointer; + readAheadPointer=readPointer; + writeAheadPointer=writePointer; + readCount=writeCount=0; + } + + template + int SingleProducerConsumer::Size( void ) const + { + return writeCount-readCount; + } + + template + bool SingleProducerConsumer::CheckReadUnlockOrder(const SingleProducerConsumerType* data) const + { + return const_cast(&readPointer->object) == data; + } + + + template + bool SingleProducerConsumer::ReadIsLocked(void) const + { + return readAheadPointer!=readPointer; + } +} + +#endif diff --git a/src/raknet/SocketDefines.h b/src/raknet/SocketDefines.h index f6548ab..248cf2d 100755 --- a/src/raknet/SocketDefines.h +++ b/src/raknet/SocketDefines.h @@ -1,69 +1,69 @@ -#ifndef __SOCKET_DEFINES_H -#define __SOCKET_DEFINES_H - -/// Internal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #if defined(_WIN32) - #define closesocket__ closesocket - #define select__ select - #else - #define closesocket__ close - #define select__ select - #endif -#define accept__ accept -#define connect__ connect -#define socket__ socket -#define bind__ bind -#define getsockname__ getsockname -#define getsockopt__ getsockopt -#define inet_addr__ inet_addr -#define ioctlsocket__ ioctlsocket -#define listen__ listen -#define recv__ recv -#define recvfrom__ recvfrom -#define sendto__ sendto -#define send__ send -#define setsockopt__ setsockopt -#define shutdown__ shutdown -#define WSASendTo__ WSASendTo - - -#endif +#ifndef __SOCKET_DEFINES_H +#define __SOCKET_DEFINES_H + +/// Internal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #if defined(_WIN32) + #define closesocket__ closesocket + #define select__ select + #else + #define closesocket__ close + #define select__ select + #endif +#define accept__ accept +#define connect__ connect +#define socket__ socket +#define bind__ bind +#define getsockname__ getsockname +#define getsockopt__ getsockopt +#define inet_addr__ inet_addr +#define ioctlsocket__ ioctlsocket +#define listen__ listen +#define recv__ recv +#define recvfrom__ recvfrom +#define sendto__ sendto +#define send__ send +#define setsockopt__ setsockopt +#define shutdown__ shutdown +#define WSASendTo__ WSASendTo + + +#endif diff --git a/src/raknet/SocketIncludes.h b/src/raknet/SocketIncludes.h index 3c530c5..418e074 100755 --- a/src/raknet/SocketIncludes.h +++ b/src/raknet/SocketIncludes.h @@ -1,31 +1,31 @@ -// All this crap just to include type SOCKET - - - - - - - - - - -#if defined(_WIN32) -typedef int socklen_t; -// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib -// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly -#include -#else -#define closesocket close -#include -#include -#include -#include -#include -#include -#include -#include -#define INVALID_SOCKET -1 -//#include "RakMemoryOverride.h" -/// Unix/Linux uses ints for sockets -typedef int SOCKET; -#endif +// All this crap just to include type SOCKET + + + + + + + + + + +#if defined(_WIN32) +typedef int socklen_t; +// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib +// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly +#include +#else +#define closesocket close +#include +#include +#include +#include +#include +#include +#include +#include +#define INVALID_SOCKET -1 +//#include "RakMemoryOverride.h" +/// Unix/Linux uses ints for sockets +typedef int SOCKET; +#endif diff --git a/src/raknet/SocketLayer.cpp b/src/raknet/SocketLayer.cpp index badae95..6fb3b1e 100755 --- a/src/raknet/SocketLayer.cpp +++ b/src/raknet/SocketLayer.cpp @@ -1,1851 +1,1851 @@ -/// \file -/// \brief SocketLayer class implementation -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "SocketLayer.h" -#include "RakAssert.h" -#include "RakNetTypes.h" -#include "GetTime.h" -#include "LinuxStrings.h" -#include "SocketDefines.h" - -using namespace RakNet; - -#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 -#include "CCRakNetUDT.h" -#else -#include "CCRakNetSlidingWindow.h" -#endif - -SocketLayerOverride *SocketLayer::slo=0; - -#ifdef _WIN32 -#else -#include // memcpy -#include -#include -#include -#include // error numbers -#include // RAKNET_DEBUG_PRINTF -#if !defined(ANDROID) -#include -#endif -#include -#include -#include -#include -#include - -#endif - - - - - - - - - - - - - -#if defined(_WIN32) -#include "WSAStartupSingleton.h" -#include // 'IP_DONTFRAGMENT' 'IP_TTL' -#elif defined SN_TARGET_PSP2 -#else -#include -#endif - -#include "RakSleep.h" -#include -#include "Itoa.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -namespace RakNet -{ - extern void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead ); - extern void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead ); -} - -#ifdef _DEBUG -#include -#endif - -// http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#ip4to6 -// http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#getaddrinfo - -#if RAKNET_SUPPORT_IPV6==1 -void PrepareAddrInfoHints(addrinfo *hints) -{ - memset(hints, 0, sizeof (addrinfo)); // make sure the struct is empty - hints->ai_socktype = SOCK_DGRAM; // UDP sockets - hints->ai_flags = AI_PASSIVE; // fill in my IP for me -} -#endif - -// Frogwares: Define this -// #define DEBUG_SENDTO_SPIKES - -bool SocketLayer::IsPortInUse_Old(unsigned short port, const char *hostAddress) -{ - SOCKET listenSocket; - sockaddr_in listenerSocketAddress; - memset(&listenerSocketAddress,0,sizeof(sockaddr_in)); - // Listen on our designated Port# - listenerSocketAddress.sin_port = htons( port ); - listenSocket = socket__( AF_INET, SOCK_DGRAM, 0 ); - if ( listenSocket == (SOCKET) -1 ) - return true; - // bind our name to the socket - // Fill in the rest of the address structure - listenerSocketAddress.sin_family = AF_INET; - - if ( hostAddress && hostAddress[0] ) - { - - - - listenerSocketAddress.sin_addr.s_addr = inet_addr__( hostAddress ); - - } - else - listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; - - - - - - - - int ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); - closesocket__(listenSocket); - - - - - // #if defined(_DEBUG) - // if (ret == -1) - // perror("Failed to bind to address:"); - // #endif - return ret <= -1; - -} -bool SocketLayer::IsSocketFamilySupported(const char *hostAddress, unsigned short socketFamily) -{ - (void) hostAddress; - (void) socketFamily; - -#if RAKNET_SUPPORT_IPV6!=1 - return socketFamily==AF_INET; -#else - struct addrinfo hints; -#if RAKNET_SUPPORT_IPV6==1 - PrepareAddrInfoHints(&hints); -#endif - hints.ai_family = socketFamily; - struct addrinfo *servinfo=0; - int error; - // On Ubuntu, "" returns "No address associated with hostname" while 0 works. - if (hostAddress && - (_stricmp(hostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || hostAddress[0]==0)) - { - getaddrinfo(0, "0", &hints, &servinfo); - } - else - { - getaddrinfo(hostAddress, "0", &hints, &servinfo); - } - - - (void) error; - if (servinfo) - { - freeaddrinfo(servinfo); - return true; - } - else - { -#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_WIN32) - printf("IsSocketFamilySupported failed. hostAddress=%s. %s\n", hostAddress, gai_strerror(error)); -#endif - } - return false; -#endif -} -bool SocketLayer::IsPortInUse(unsigned short port, const char *hostAddress, unsigned short socketFamily) -{ -#if RAKNET_SUPPORT_IPV6!=1 - (void) socketFamily; - return IsPortInUse_Old(port, hostAddress); -#else - SOCKET listenSocket; - struct addrinfo hints; - struct addrinfo *servinfo=0, *aip; // will point to the results - PrepareAddrInfoHints(&hints); - hints.ai_family = socketFamily; - char portStr[32]; - Itoa(port,portStr,10); - - // On Ubuntu, "" returns "No address associated with hostname" while 0 works. - if (hostAddress && - (_stricmp(hostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || hostAddress[0]==0)) - { - getaddrinfo(0, portStr, &hints, &servinfo); - } - else - { - getaddrinfo(hostAddress, portStr, &hints, &servinfo); - } - - // Try all returned addresses until one works - for (aip = servinfo; aip != NULL; aip = aip->ai_next) - { - // Open socket. The address type depends on what - // getaddrinfo() gave us. - listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); - if (listenSocket != -1) - { - int ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); - closesocket__(listenSocket); - if (ret>=0) - { - freeaddrinfo(servinfo); // free the linked-list - return false; - } - } - - // If the user didn't specify which host address, then only apply the first - if (hostAddress==0 || hostAddress[0]==0) - break; - } - - freeaddrinfo(servinfo); // free the linked-list - return true; -#endif // #if RAKNET_SUPPORT_IPV6!=1 -} -void SocketLayer::SetDoNotFragment( SOCKET listenSocket, int opt, int IPPROTO ) -{ -#if defined(IP_DONTFRAGMENT ) - -#if defined(_WIN32) && defined(_DEBUG) - // If this assert hit you improperly linked against WSock32.h - RakAssert(IP_DONTFRAGMENT==14); -#endif - - if ( setsockopt__( listenSocket, IPPROTO, IP_DONTFRAGMENT, ( char * ) & opt, sizeof ( opt ) ) == -1 ) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // I see this hit on XP with IPV6 for some reason - RAKNET_DEBUG_PRINTF( "Warning: setsockopt__(IP_DONTFRAGMENT) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - LocalFree( messageBuffer ); -#endif - } -#endif -} - -void SocketLayer::SetNonBlocking( SOCKET listenSocket) -{ -#ifdef _WIN32 - unsigned long nonBlocking = 1; - ioctlsocket__( listenSocket, FIONBIO, &nonBlocking ); - - - -#else - int flags = fcntl(listenSocket, F_GETFL, 0); - fcntl(listenSocket, F_SETFL, flags | O_NONBLOCK); -#endif -} - -void SocketLayer::SetSocketOptions( SOCKET listenSocket) -{ - int sock_opt = 1; - // // On Vista, can get WSAEACCESS (10013) - /* - if ( setsockopt__( listenSocket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & sock_opt, sizeof ( sock_opt ) ) == -1 ) - { - #if defined(_WIN32) && !defined(_XBOX) && defined(_DEBUG) && !defined(X360) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "setsockopt__(SO_REUSEADDR) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); - #endif - } - */ - - // This doubles the max throughput rate - sock_opt=1024*256; - setsockopt__(listenSocket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - // Immediate hard close. Don't linger the socket, or recreating the socket quickly on Vista fails. - // Fail with voice and xbox - - sock_opt=0; - setsockopt__(listenSocket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - - - // This doesn't make much difference: 10% maybe - // Not supported on console 2 - sock_opt=1024*16; - setsockopt__(listenSocket, SOL_SOCKET, SO_SNDBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - -#ifdef __APPLE__ - // Preventing SEGPIPE on "broken" socket - int set = 1; - setsockopt__(listenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); -#endif - - /* - #ifdef _WIN32 - unsigned long nonblocking = 1; - ioctlsocket__( listenSocket, FIONBIO, &nonblocking ); - #elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) - sock_opt=1; - setsockopt__(listenSocket, SOL_SOCKET, SO_NBIO, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - #else - fcntl( listenSocket, F_SETFL, O_NONBLOCK ); - #endif - */ - - // TODO - teston ipv6 - - // Note: Fails with VDP but not xbox - // Set broadcast capable - sock_opt=1; - if ( setsockopt__( listenSocket, SOL_SOCKET, SO_BROADCAST, ( char * ) & sock_opt, sizeof( sock_opt ) ) == -1 ) - { -#if defined(_WIN32) && defined(_DEBUG) - - DWORD dwIOError = GetLastError(); - // On Vista, can get WSAEACCESS (10013) - // See http://support.microsoft.com/kb/819124 - // http://blogs.msdn.com/wndp/archive/2007/03/19/winsock-so-exclusiveaddruse-on-vista.aspx - // http://msdn.microsoft.com/en-us/library/ms740621(VS.85).aspx - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "setsockopt__(SO_BROADCAST) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); - -#endif - - } -} -SOCKET SocketLayer::CreateBoundSocket_PS3Lobby( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ) -{ - (void) port; - (void) blockingSocket; - (void) forceHostAddress; - (void) socketFamily; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - return 0; - -} -SOCKET SocketLayer::CreateBoundSocket_PSP2( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ) -{ - (void) port; - (void) blockingSocket; - (void) forceHostAddress; - (void) socketFamily; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - return 0; - -} -SOCKET SocketLayer::CreateBoundSocket_Old( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions ) -{ - (void) blockingSocket; - - int ret; - SOCKET listenSocket; - sockaddr_in listenerSocketAddress; - memset(&listenerSocketAddress,0,sizeof(sockaddr_in)); - // Listen on our designated Port# - listenerSocketAddress.sin_port = htons( port ); - - listenSocket = socket__( AF_INET, SOCK_DGRAM, extraSocketOptions ); - - if ( listenSocket == (SOCKET) -1 ) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "socket__(...) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - char str[512]; - strcpy(str,(const char*) messageBuffer); - //Free the buffer. - LocalFree( messageBuffer ); -#endif - - return (SOCKET) -1; - } - - SetSocketOptions(listenSocket); - - // Fill in the rest of the address structure - listenerSocketAddress.sin_family = AF_INET; - - if (forceHostAddress && forceHostAddress[0]) - { - // RAKNET_DEBUG_PRINTF("Force binding %s:%i\n", forceHostAddress, port); - - - - listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); - - } - else - { - // RAKNET_DEBUG_PRINTF("Binding any on port %i\n", port); - listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; - } - - - - - - - - - // bind our name to the socket - ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); - - if ( ret <= -1 ) - { - - - - - - - -#if defined(_WIN32) - DWORD dwIOError = GetLastError(); - if (dwIOError==10048) - { - if (sleepOn10048==0) - return (SOCKET) -1; - // Vista has a bug where it returns WSAEADDRINUSE (10048) if you create, shutdown, then rebind the socket port unless you wait a while first. - // Wait, then rebind - RakSleep(100); - - closesocket__(listenSocket); - listenerSocketAddress.sin_port = htons( port ); - listenSocket = socket__( AF_INET, SOCK_DGRAM, 0 ); - if ( listenSocket == (SOCKET) -1 ) - return false; - SetSocketOptions(listenSocket); - - // Fill in the rest of the address structure - listenerSocketAddress.sin_family = AF_INET; - if (forceHostAddress && forceHostAddress[0]) - { - - - - listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); - - } - else - listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; - - // bind our name to the socket - ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); - - if ( ret >= 0 ) - return listenSocket; - } - dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "bind__(...) failed:Error code - %d\n%s", (unsigned int) dwIOError, (char*) messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#elif (defined(__GNUC__) || defined(__GCCXML__) ) && !defined(_WIN32) - switch (ret) - { - case EBADF: - RAKNET_DEBUG_PRINTF("bind__(): sockfd is not a valid descriptor.\n"); break; - - case ENOTSOCK: - RAKNET_DEBUG_PRINTF("bind__(): Argument is a descriptor for a file, not a socket.\n"); break; - - case EINVAL: - RAKNET_DEBUG_PRINTF("bind__(): The addrlen is wrong, or the socket was not in the AF_UNIX family.\n"); break; - case EROFS: - RAKNET_DEBUG_PRINTF("bind__(): The socket inode would reside on a read-only file system.\n"); break; - case EFAULT: - RAKNET_DEBUG_PRINTF("bind__(): my_addr points outside the user's accessible address space.\n"); break; - case ENAMETOOLONG: - RAKNET_DEBUG_PRINTF("bind__(): my_addr is too long.\n"); break; - case ENOENT: - RAKNET_DEBUG_PRINTF("bind__(): The file does not exist.\n"); break; - case ENOMEM: - RAKNET_DEBUG_PRINTF("bind__(): Insufficient kernel memory was available.\n"); break; - case ENOTDIR: - RAKNET_DEBUG_PRINTF("bind__(): A component of the path prefix is not a directory.\n"); break; - case EACCES: - RAKNET_DEBUG_PRINTF("bind__(): Search permission is denied on a component of the path prefix.\n"); break; - - case ELOOP: - RAKNET_DEBUG_PRINTF("bind__(): Too many symbolic links were encountered in resolving my_addr.\n"); break; - - default: - RAKNET_DEBUG_PRINTF("Unknown bind__() error %i.\n", ret); break; - } -#endif - - return (SOCKET) -1; - } - - return listenSocket; -} -SOCKET SocketLayer::CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions, unsigned short socketFamily ) -{ - (void) blockingSocket; - (void) extraSocketOptions; - (void) socketFamily; - -#if RAKNET_SUPPORT_IPV6!=1 - return CreateBoundSocket_Old(port,blockingSocket,forceHostAddress,sleepOn10048,extraSocketOptions); -#else - -#ifdef _WIN32 - // Vista has a bug where it returns WSAEADDRINUSE (10048) if you create, shutdown, then rebind the socket port unless you wait a while first. - if (sleepOn10048==0) - RakSleep(100); -#endif - - int ret=0; - SOCKET listenSocket; - struct addrinfo hints; - struct addrinfo *servinfo=0, *aip; // will point to the results - PrepareAddrInfoHints(&hints); - hints.ai_family=socketFamily; - char portStr[32]; - Itoa(port,portStr,10); - - // On Ubuntu, "" returns "No address associated with hostname" while 0 works. - if (forceHostAddress && - (_stricmp(forceHostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || forceHostAddress[0]==0)) - { - getaddrinfo(0, portStr, &hints, &servinfo); - } - else - { - getaddrinfo(forceHostAddress, portStr, &hints, &servinfo); - } - - // Try all returned addresses until one works - for (aip = servinfo; aip != NULL; aip = aip->ai_next) - { - // Open socket. The address type depends on what - // getaddrinfo() gave us. - listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); - if (listenSocket != -1) - { - ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); - if (ret>=0) - { - // Is this valid? - sockaddr_in6 addr6; - memcpy(&addr6, aip->ai_addr, sizeof(addr6)); - - freeaddrinfo(servinfo); // free the linked-list - - SetSocketOptions(listenSocket); - return listenSocket; - } - } - } - -#if defined(_WIN32) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - - RAKNET_DEBUG_PRINTF( "bind__(...) failed:Error code - %d\n%s", (unsigned int) dwIOError, (char*) messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#elif defined(__GNUC__) || defined(__GCCXML__) && !defined(_WIN32) - switch (ret) - { - case EBADF: - RAKNET_DEBUG_PRINTF("bind__(): sockfd is not a valid descriptor.\n"); break; - - case ENOTSOCK: - RAKNET_DEBUG_PRINTF("bind__(): Argument is a descriptor for a file, not a socket.\n"); break; - - case EINVAL: - RAKNET_DEBUG_PRINTF("bind__(): The addrlen is wrong, or the socket was not in the AF_UNIX family.\n"); break; - case EROFS: - RAKNET_DEBUG_PRINTF("bind__(): The socket inode would reside on a read-only file system.\n"); break; - case EFAULT: - RAKNET_DEBUG_PRINTF("bind__(): my_addr points outside the user's accessible address space.\n"); break; - case ENAMETOOLONG: - RAKNET_DEBUG_PRINTF("bind__(): my_addr is too long.\n"); break; - case ENOENT: - RAKNET_DEBUG_PRINTF("bind__(): The file does not exist.\n"); break; - case ENOMEM: - RAKNET_DEBUG_PRINTF("bind__(): Insufficient kernel memory was available.\n"); break; - case ENOTDIR: - RAKNET_DEBUG_PRINTF("bind__(): A component of the path prefix is not a directory.\n"); break; - case EACCES: - RAKNET_DEBUG_PRINTF("bind__(): Search permission is denied on a component of the path prefix.\n"); break; - - case ELOOP: - RAKNET_DEBUG_PRINTF("bind__(): Too many symbolic links were encountered in resolving my_addr.\n"); break; - - default: - RAKNET_DEBUG_PRINTF("Unknown bind__() error %i.\n", ret); break; - } -#endif - - -#endif - - return (SOCKET) -1; -} -const char* SocketLayer::DomainNameToIP_Old( const char *domainName ) -{ - struct in_addr addr; - memset(&addr,0,sizeof(in_addr)); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Use inet_addr instead? What is the difference? - struct hostent * phe = gethostbyname( domainName ); - - if ( phe == 0 || phe->h_addr_list[ 0 ] == 0 ) - { - //cerr << "Yow! Bad host lookup." << endl; - return 0; - } - - if (phe->h_addr_list[ 0 ]==0) - return 0; - - memcpy( &addr, phe->h_addr_list[ 0 ], sizeof( struct in_addr ) ); - return inet_ntoa( addr ); - - - return ""; -} -const char* SocketLayer::DomainNameToIP( const char *domainName ) -{ -#if RAKNET_SUPPORT_IPV6!=1 - return DomainNameToIP_Old(domainName); -#else - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - struct addrinfo hints, *res, *p; - int status; - static char ipstr[INET6_ADDRSTRLEN]; - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version - hints.ai_socktype = SOCK_DGRAM; - - if ((status = getaddrinfo(domainName, NULL, &hints, &res)) != 0) { - return 0; - } - - p=res; -// for(p = res;p != NULL; p = p->ai_next) { - void *addr; -// char *ipver; - - // get the pointer to the address itself, - // different fields in IPv4 and IPv6: - if (p->ai_family == AF_INET) - { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; - addr = &(ipv4->sin_addr); - strcpy(ipstr, inet_ntoa( ipv4->sin_addr )); - } - else - { - // TODO - test - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; - addr = &(ipv6->sin6_addr); - // inet_ntop function does not exist on windows - // http://www.mail-archive.com/users@ipv6.org/msg02107.html - getnameinfo((struct sockaddr *)ipv6, sizeof(struct sockaddr_in6), ipstr, 1, NULL, 0, NI_NUMERICHOST); - } - freeaddrinfo(res); // free the linked list - return ipstr; -// } - - - - return ""; - -#endif // #if RAKNET_SUPPORT_IPV6!=1 -} - - -void SocketLayer::Write( const SOCKET writeSocket, const char* data, const int length ) -{ -#ifdef _DEBUG - RakAssert( writeSocket != (SOCKET) -1 ); -#endif - - send__( writeSocket, data, length, 0 ); -} -void SocketLayer::RecvFromBlocking_Old( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ) -{ - (void) rakPeer; - - sockaddr* sockAddrPtr; - socklen_t sockLen; - socklen_t* socketlenPtr=(socklen_t*) &sockLen; - sockaddr_in sa; - memset(&sa,0,sizeof(sockaddr_in)); - int dataOutSize; - const int flag=0; - - (void) remotePortRakNetWasStartedOn_PS3; - (void) extraSocketOptions; - - - - - - - - - - - - - - - - - - - - - { - sockLen=sizeof(sa); - sa.sin_family = AF_INET; - sa.sin_port=0; - sockAddrPtr=(sockaddr*) &sa; - } - - - - - dataOutSize=MAXIMUM_MTU_SIZE; - - - *bytesReadOut = recvfrom__( s, dataOut, dataOutSize, flag, sockAddrPtr, socketlenPtr ); - - - - - - - - - - if (*bytesReadOut<=0) - return; - *timeRead=RakNet::GetTimeUS(); - - - - - - - - - - { - systemAddressOut->SetPortNetworkOrder( sa.sin_port ); - systemAddressOut->address.addr4.sin_addr.s_addr=sa.sin_addr.s_addr; - } -} - -void SocketLayer::RecvFromBlocking( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ) -{ -#if RAKNET_SUPPORT_IPV6!=1 - RecvFromBlocking_Old(s,rakPeer,remotePortRakNetWasStartedOn_PS3,extraSocketOptions,dataOut,bytesReadOut,systemAddressOut,timeRead); -#else - (void) rakPeer; - sockaddr_storage their_addr; - sockaddr* sockAddrPtr; - socklen_t sockLen; - socklen_t* socketlenPtr=(socklen_t*) &sockLen; - memset(&their_addr,0,sizeof(their_addr)); - int dataOutSize; - const int flag=0; - - (void) remotePortRakNetWasStartedOn_PS3; - (void) extraSocketOptions; - - - - - - - - - - - - { - sockLen=sizeof(their_addr); - sockAddrPtr=(sockaddr*) &their_addr; - } - - - - - dataOutSize=MAXIMUM_MTU_SIZE; - - - *bytesReadOut = recvfrom__( s, dataOut, dataOutSize, flag, sockAddrPtr, socketlenPtr ); - - - - - - - - - - if (*bytesReadOut<=0) - return; - *timeRead=RakNet::GetTimeUS(); - - - - - - - - - - { - if (their_addr.ss_family==AF_INET) - { - memcpy(&systemAddressOut->address.addr4,(sockaddr_in *)&their_addr,sizeof(sockaddr_in)); - systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr4.sin_port); - // systemAddressOut->address.addr4.sin_port=ntohs( systemAddressOut->address.addr4.sin_port ); - } - else - { - memcpy(&systemAddressOut->address.addr6,(sockaddr_in6 *)&their_addr,sizeof(sockaddr_in6)); - systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr6.sin6_port); - // systemAddressOut->address.addr6.sin6_port=ntohs( systemAddressOut->address.addr6.sin6_port ); - } - } - -#endif // defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) -} - -int SocketLayer::SendTo_PS3Lobby( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ) -{ - (void) s; - (void) data; - (void) length; - (void) remotePortRakNetWasStartedOn_PS3; - (void) systemAddress; - - int len=0; - - - - - - - - - - - - - - - return len; -} -int SocketLayer::SendTo_PSP2( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ) -{ - (void) s; - (void) data; - (void) length; - (void) remotePortRakNetWasStartedOn_PS3; - (void) systemAddress; - - int len=0; - - - - - - - - - - - - - - return len; -} -int SocketLayer::SendTo_360( SOCKET s, const char *data, int length, const char *voiceData, int voiceLength, const SystemAddress &systemAddress, unsigned int extraSocketOptions ) -{ - (void) s; - (void) data; - (void) length; - (void) voiceData; - (void) voiceLength; - (void) extraSocketOptions; - (void) systemAddress; - - int len=0; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - return len; -} -int SocketLayer::SendTo_PC( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, const char *file, const long line ) -{ - // TODO - // http://www.linuxquestions.org/questions/linux-software-2/ipv6-linux-sendto-problems-519485/ - -// #if RAKNET_SUPPORT_IPV6==1 -// RakAssert( -// systemAddress.address.addr4.sin_family!=AF_MAX && -// (systemAddress.address.addr4.sin_family==AF_INET || (systemAddress.address.addr6.sin6_scope_id!=0)) -// ); -// #endif - - /* - sockaddr_in sa; - memset(&sa,0,sizeof(sockaddr_in)); - sa.sin_port = htons( port ); // User port - sa.sin_addr.s_addr = binaryAddress; - sa.sin_family = socketFamily; - */ - - int len=0; - do - { -#ifdef DEBUG_SENDTO_SPIKES - RakNetTime start = RakNet::GetTime(); -#else - (void) file; - (void) line; -#endif - if (systemAddress.address.addr4.sin_family==AF_INET) - { - //systemAddress.address.addr4.sin_port=htons(systemAddress.address.addr4.sin_port); - len = sendto__( s, data, length, 0, ( const sockaddr* ) & systemAddress.address.addr4, sizeof( sockaddr_in ) ); - //systemAddress.address.addr4.sin_port=ntohs(systemAddress.address.addr4.sin_port); - } - else - { -#if RAKNET_SUPPORT_IPV6==1 - // systemAddress.address.addr6.sin6_port=htons(systemAddress.address.addr6.sin6_port); - len = sendto__( s, data, length, 0, ( const sockaddr* ) & systemAddress.address.addr6, sizeof( sockaddr_in6 ) ); - //systemAddress.address.addr6.sin6_port=ntohs(systemAddress.address.addr6.sin6_port); -#endif - } - -#ifdef DEBUG_SENDTO_SPIKES - RakNetTime end = RakNet::GetTime(); - static unsigned int callCount=1; - RAKNET_DEBUG_PRINTF("%i. SendTo_PC, time=%" PRINTF_64_BIT_MODIFIER "u, elapsed=%" PRINTF_64_BIT_MODIFIER "u, length=%i, returned=%i, binaryAddress=%i, port=%i, file=%s, line=%i\n", callCount++, end, end-start, length, len, binaryAddress, port, file, line); -#endif - if (len<0) - { - RAKNET_DEBUG_PRINTF("sendto failed with code %i for char %i and length %i.\n", len, data[0], length); - } - } - while ( len == 0 ); - return len; -} - -#ifdef _MSC_VER -#pragma warning( disable : 4702 ) // warning C4702: unreachable code -#endif -int SocketLayer::SendTo( SOCKET s, const char *data, int length, SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ) -{ - (void) extraSocketOptions; - - int len=0; - RakAssert(length<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); -#if !defined(__S3E__) - RakAssert(systemAddress.address.addr4.sin_family!=AF_MAX); -#endif - - if (slo) - { - len = slo->RakNetSendTo(s,data,length,systemAddress); - if ( len != -1 ) - return 0; - return 1; - } - - if ( s == (SOCKET) -1 ) - { - return -1; - } - - - if (remotePortRakNetWasStartedOn_PS3!=0) - { - - - - - - } - else - { - - - - - - - len = SendTo_PC(s,data,length,systemAddress,file,line); - - } - - if ( len != -1 ) - return 0; - -#if defined(_WIN32) && !defined(_WIN32_WCE) - - DWORD dwIOError = WSAGetLastError(); - - if ( dwIOError == WSAECONNRESET ) - { -#if defined(_DEBUG) - RAKNET_DEBUG_PRINTF( "A previous send operation resulted in an ICMP Port Unreachable message.\n" ); -#endif - - } - else if ( dwIOError != WSAEWOULDBLOCK && dwIOError != WSAEADDRNOTAVAIL) - { -#if defined(_WIN32) && defined(_DEBUG) - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "sendto failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); -#endif - - } - - return dwIOError; -#endif - - return 1; // error -} -// Not enough info for IPV6 -// int SocketLayer::SendTo( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ) -// { -// SystemAddress systemAddress; -// systemAddress.FromStringAndPort(ip,port); -// return SendTo( s, data, length, systemAddress,remotePortRakNetWasStartedOn_PS3, extraSocketOptions, file, line ); -// } -int SocketLayer::SendToTTL( SOCKET s, const char *data, int length, SystemAddress &systemAddress, int ttl ) -{ - if (slo) - return slo->RakNetSendTo(s,data,length,systemAddress); - - - int oldTTL; - socklen_t opLen=sizeof(oldTTL); - // Get the current TTL - if (getsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & oldTTL, &opLen ) == -1) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "getsockopt__(IPPROTO_IP,IP_TTL) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#endif - } - - // Set to TTL - int newTTL=ttl; - if (setsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & newTTL, sizeof ( newTTL ) ) == -1) - { - -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "setsockopt__(IPPROTO_IP,IP_TTL) failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#endif - } - - // Send - int res = SendTo(s,data,length,systemAddress,0,0, __FILE__, __LINE__ ); - - // Restore the old TTL - setsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & oldTTL, opLen ); - - return res; - - - -} - - -RakNet::RakString SocketLayer::GetSubNetForSocketAndIp(SOCKET inSock, RakNet::RakString inIpString) -{ - RakNet::RakString netMaskString; - RakNet::RakString ipString; - - - - - -#if defined(_WIN32) - INTERFACE_INFO InterfaceList[20]; - unsigned long nBytesReturned; - if (WSAIoctl(inSock, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList, - sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR) { - return ""; - } - - int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO); - - for (int i = 0; i < nNumInterfaces; ++i) - { - sockaddr_in *pAddress; - pAddress = (sockaddr_in *) & (InterfaceList[i].iiAddress); - ipString=inet_ntoa(pAddress->sin_addr); - - if (inIpString==ipString) - { - pAddress = (sockaddr_in *) & (InterfaceList[i].iiNetmask); - netMaskString=inet_ntoa(pAddress->sin_addr); - return netMaskString; - } - } - return ""; -#else - - int fd,fd2; - fd2 = socket__(AF_INET, SOCK_DGRAM, 0); - - if(fd2 < 0) - { - return ""; - } - - struct ifconf ifc; - char buf[1999]; - ifc.ifc_len = sizeof(buf); - ifc.ifc_buf = buf; - if(ioctl(fd2, SIOCGIFCONF, &ifc) < 0) - { - return ""; - } - - struct ifreq *ifr; - ifr = ifc.ifc_req; - int intNum = ifc.ifc_len / sizeof(struct ifreq); - for(int i = 0; i < intNum; i++) - { - ipString=inet_ntoa(((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr); - - if (inIpString==ipString) - { - struct ifreq ifr2; - fd = socket__(AF_INET, SOCK_DGRAM, 0); - if(fd < 0) - { - return ""; - } - ifr2.ifr_addr.sa_family = AF_INET; - - strncpy(ifr2.ifr_name, ifr[i].ifr_name, IFNAMSIZ-1); - - ioctl(fd, SIOCGIFNETMASK, &ifr2); - - close(fd); - close(fd2); - netMaskString=inet_ntoa(((struct sockaddr_in *)&ifr2.ifr_addr)->sin_addr); - - return netMaskString; - } - } - - close(fd2); - return ""; - -#endif - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void GetMyIP_Win32( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) -{ - char ac[ 80 ]; - if ( gethostname( ac, sizeof( ac ) ) == -1 ) - { - #if defined(_WIN32) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "gethostname failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); - #endif - return ; - } - - -#if RAKNET_SUPPORT_IPV6==1 - struct addrinfo hints; - struct addrinfo *servinfo=0, *aip; // will point to the results - PrepareAddrInfoHints(&hints); - getaddrinfo(ac, "", &hints, &servinfo); - - int idx; - for (idx=0, aip = servinfo; aip != NULL && idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; aip = aip->ai_next, idx++) - { - if (aip->ai_family == AF_INET) - { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)aip->ai_addr; - memcpy(&addresses[idx].address.addr4,ipv4,sizeof(sockaddr_in)); - } - else - { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)aip->ai_addr; - memcpy(&addresses[idx].address.addr4,ipv6,sizeof(sockaddr_in6)); - } - - } - - freeaddrinfo(servinfo); // free the linked-list -#else - struct hostent *phe = gethostbyname( ac ); - - if ( phe == 0 ) - { - #ifdef _WIN32 - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "gethostbyname failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); - #endif - return ; - } - int idx; - for ( idx = 0; idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ++idx ) - { - if (phe->h_addr_list[ idx ] == 0) - break; - - memcpy(&addresses[idx].address.addr4.sin_addr,phe->h_addr_list[ idx ],sizeof(struct in_addr)); - - } -#endif - - - while (idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS) - { - addresses[idx]=UNASSIGNED_SYSTEM_ADDRESS; - idx++; - } -} -// #else -/* -void GetMyIP_Linux( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) -{ - struct ifaddrs *ifaddr, *ifa; - int family, s; - char host[NI_MAXHOST]; - struct in_addr linux_in_addr; - - if (getifaddrs(&ifaddr) == -1) { - printf( "Error getting interface list\n"); - } - - int idx = 0; - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) continue; - family = ifa->ifa_addr->sa_family; - - if (family == AF_INET) { - s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - if (s != 0) { - // printf ("getnameinfo() failed: %s\n", gai_strerror(s)); - } - else if (strcmp(host,"127.0.0.1")!=0) - { - if (inet_aton(host, &addresses[idx].address.addr4.sin_addr)!=0) - idx++; - } - } -#if RAKNET_SUPPORT_IPV6==1 - else - { - s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - if (s != 0) { - // printf ("getnameinfo() failed: %s\n", gai_strerror(s)); - } - else if (strcmp(host,"::1")!=0) - { - if (inet_pton(family, host, &addresses[idx].address.addr6.sin6_addr)!=0) - idx++; - } - } -#endif - } - - for ( ; idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ++idx ) - { - addresses[idx]=UNASSIGNED_SYSTEM_ADDRESS; - } - - freeifaddrs(ifaddr); -} -*/ - - - -void SocketLayer::GetMyIP( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) -{ - - - - -#if defined(_WIN32) - GetMyIP_Win32(addresses); -#else -// GetMyIP_Linux(addresses); - GetMyIP_Win32(addresses); -#endif -} - - -unsigned short SocketLayer::GetLocalPort(SOCKET s) -{ - SystemAddress sa; - GetSystemAddress(s,&sa); - return sa.GetPort(); -} -void SocketLayer::GetSystemAddress_Old ( SOCKET s, SystemAddress *systemAddressOut ) -{ - sockaddr_in sa; - memset(&sa,0,sizeof(sockaddr_in)); - socklen_t len = sizeof(sa); - if (getsockname__(s, (sockaddr*)&sa, &len)!=0) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "getsockname failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); -#endif - *systemAddressOut = UNASSIGNED_SYSTEM_ADDRESS; - return; - } - - systemAddressOut->SetPortNetworkOrder(sa.sin_port); - systemAddressOut->address.addr4.sin_addr.s_addr=sa.sin_addr.s_addr; -} -void SocketLayer::GetSystemAddress ( SOCKET s, SystemAddress *systemAddressOut ) -{ -#if RAKNET_SUPPORT_IPV6!=1 - GetSystemAddress_Old(s,systemAddressOut); -#else - socklen_t slen; - sockaddr_storage ss; - slen = sizeof(ss); - - if (getsockname__(s, (struct sockaddr *)&ss, &slen)!=0) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "getsockname failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); -#endif - systemAddressOut->FromString(0); - return; - } - - if (ss.ss_family==AF_INET) - { - memcpy(&systemAddressOut->address.addr4,(sockaddr_in *)&ss,sizeof(sockaddr_in)); - systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr4.sin_port); - - uint32_t zero = 0; - if (memcmp(&systemAddressOut->address.addr4.sin_addr.s_addr, &zero, sizeof(zero))==0) - systemAddressOut->SetToLoopback(4); - // systemAddressOut->address.addr4.sin_port=ntohs(systemAddressOut->address.addr4.sin_port); - } - else - { - memcpy(&systemAddressOut->address.addr6,(sockaddr_in6 *)&ss,sizeof(sockaddr_in6)); - systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr6.sin6_port); - - char zero[16]; - memset(zero,0,sizeof(zero)); - if (memcmp(&systemAddressOut->address.addr4.sin_addr.s_addr, &zero, sizeof(zero))==0) - systemAddressOut->SetToLoopback(6); - - // systemAddressOut->address.addr6.sin6_port=ntohs(systemAddressOut->address.addr6.sin6_port); - } -#endif // #if RAKNET_SUPPORT_IPV6!=1 -} - -void SocketLayer::SetSocketLayerOverride(SocketLayerOverride *_slo) -{ - slo=_slo; -} - -bool SocketLayer::GetFirstBindableIP(char firstBindable[128], int ipProto) -{ - SystemAddress ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ]; - SocketLayer::GetMyIP( ipList ); - - - if (ipProto==AF_UNSPEC) - - { - ipList[0].ToString(false,firstBindable); - return true; - } - - // Find the first valid host address - unsigned int l; - for (l=0; l < MAXIMUM_NUMBER_OF_INTERNAL_IDS; l++) - { - if (ipList[l]==UNASSIGNED_SYSTEM_ADDRESS) - break; - if (ipList[l].GetIPVersion()==4 && ipProto==AF_INET) - break; - if (ipList[l].GetIPVersion()==6 && ipProto==AF_INET6) - break; - } - - if (ipList[l]==UNASSIGNED_SYSTEM_ADDRESS || l==MAXIMUM_NUMBER_OF_INTERNAL_IDS) - return false; -// RAKNET_DEBUG_PRINTF("%i %i %i %i\n", -// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[0], -// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[1], -// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[2], -// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[3] -// ); - ipList[l].ToString(false,firstBindable); - return true; - -} - - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif +/// \file +/// \brief SocketLayer class implementation +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "SocketLayer.h" +#include "RakAssert.h" +#include "RakNetTypes.h" +#include "GetTime.h" +#include "LinuxStrings.h" +#include "SocketDefines.h" + +using namespace RakNet; + +#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1 +#include "CCRakNetUDT.h" +#else +#include "CCRakNetSlidingWindow.h" +#endif + +SocketLayerOverride *SocketLayer::slo=0; + +#ifdef _WIN32 +#else +#include // memcpy +#include +#include +#include +#include // error numbers +#include // RAKNET_DEBUG_PRINTF +#if !defined(ANDROID) +#include +#endif +#include +#include +#include +#include +#include + +#endif + + + + + + + + + + + + + +#if defined(_WIN32) +#include "WSAStartupSingleton.h" +#include // 'IP_DONTFRAGMENT' 'IP_TTL' +#elif defined SN_TARGET_PSP2 +#else +#include +#endif + +#include "RakSleep.h" +#include +#include "Itoa.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +namespace RakNet +{ + extern void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNet::TimeUS timeRead ); + extern void ProcessNetworkPacket( const SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSmartPtr rakNetSocket, RakNet::TimeUS timeRead ); +} + +#ifdef _DEBUG +#include +#endif + +// http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#ip4to6 +// http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#getaddrinfo + +#if RAKNET_SUPPORT_IPV6==1 +void PrepareAddrInfoHints(addrinfo *hints) +{ + memset(hints, 0, sizeof (addrinfo)); // make sure the struct is empty + hints->ai_socktype = SOCK_DGRAM; // UDP sockets + hints->ai_flags = AI_PASSIVE; // fill in my IP for me +} +#endif + +// Frogwares: Define this +// #define DEBUG_SENDTO_SPIKES + +bool SocketLayer::IsPortInUse_Old(unsigned short port, const char *hostAddress) +{ + SOCKET listenSocket; + sockaddr_in listenerSocketAddress; + memset(&listenerSocketAddress,0,sizeof(sockaddr_in)); + // Listen on our designated Port# + listenerSocketAddress.sin_port = htons( port ); + listenSocket = socket__( AF_INET, SOCK_DGRAM, 0 ); + if ( listenSocket == (SOCKET) -1 ) + return true; + // bind our name to the socket + // Fill in the rest of the address structure + listenerSocketAddress.sin_family = AF_INET; + + if ( hostAddress && hostAddress[0] ) + { + + + + listenerSocketAddress.sin_addr.s_addr = inet_addr__( hostAddress ); + + } + else + listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; + + + + + + + + int ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); + closesocket__(listenSocket); + + + + + // #if defined(_DEBUG) + // if (ret == -1) + // perror("Failed to bind to address:"); + // #endif + return ret <= -1; + +} +bool SocketLayer::IsSocketFamilySupported(const char *hostAddress, unsigned short socketFamily) +{ + (void) hostAddress; + (void) socketFamily; + +#if RAKNET_SUPPORT_IPV6!=1 + return socketFamily==AF_INET; +#else + struct addrinfo hints; +#if RAKNET_SUPPORT_IPV6==1 + PrepareAddrInfoHints(&hints); +#endif + hints.ai_family = socketFamily; + struct addrinfo *servinfo=0; + int error; + // On Ubuntu, "" returns "No address associated with hostname" while 0 works. + if (hostAddress && + (_stricmp(hostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || hostAddress[0]==0)) + { + getaddrinfo(0, "0", &hints, &servinfo); + } + else + { + getaddrinfo(hostAddress, "0", &hints, &servinfo); + } + + + (void) error; + if (servinfo) + { + freeaddrinfo(servinfo); + return true; + } + else + { +#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_WIN32) + printf("IsSocketFamilySupported failed. hostAddress=%s. %s\n", hostAddress, gai_strerror(error)); +#endif + } + return false; +#endif +} +bool SocketLayer::IsPortInUse(unsigned short port, const char *hostAddress, unsigned short socketFamily) +{ +#if RAKNET_SUPPORT_IPV6!=1 + (void) socketFamily; + return IsPortInUse_Old(port, hostAddress); +#else + SOCKET listenSocket; + struct addrinfo hints; + struct addrinfo *servinfo=0, *aip; // will point to the results + PrepareAddrInfoHints(&hints); + hints.ai_family = socketFamily; + char portStr[32]; + Itoa(port,portStr,10); + + // On Ubuntu, "" returns "No address associated with hostname" while 0 works. + if (hostAddress && + (_stricmp(hostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || hostAddress[0]==0)) + { + getaddrinfo(0, portStr, &hints, &servinfo); + } + else + { + getaddrinfo(hostAddress, portStr, &hints, &servinfo); + } + + // Try all returned addresses until one works + for (aip = servinfo; aip != NULL; aip = aip->ai_next) + { + // Open socket. The address type depends on what + // getaddrinfo() gave us. + listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (listenSocket != -1) + { + int ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); + closesocket__(listenSocket); + if (ret>=0) + { + freeaddrinfo(servinfo); // free the linked-list + return false; + } + } + + // If the user didn't specify which host address, then only apply the first + if (hostAddress==0 || hostAddress[0]==0) + break; + } + + freeaddrinfo(servinfo); // free the linked-list + return true; +#endif // #if RAKNET_SUPPORT_IPV6!=1 +} +void SocketLayer::SetDoNotFragment( SOCKET listenSocket, int opt, int IPPROTO ) +{ +#if defined(IP_DONTFRAGMENT ) + +#if defined(_WIN32) && defined(_DEBUG) + // If this assert hit you improperly linked against WSock32.h + RakAssert(IP_DONTFRAGMENT==14); +#endif + + if ( setsockopt__( listenSocket, IPPROTO, IP_DONTFRAGMENT, ( char * ) & opt, sizeof ( opt ) ) == -1 ) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // I see this hit on XP with IPV6 for some reason + RAKNET_DEBUG_PRINTF( "Warning: setsockopt__(IP_DONTFRAGMENT) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + LocalFree( messageBuffer ); +#endif + } +#endif +} + +void SocketLayer::SetNonBlocking( SOCKET listenSocket) +{ +#ifdef _WIN32 + unsigned long nonBlocking = 1; + ioctlsocket__( listenSocket, FIONBIO, &nonBlocking ); + + + +#else + int flags = fcntl(listenSocket, F_GETFL, 0); + fcntl(listenSocket, F_SETFL, flags | O_NONBLOCK); +#endif +} + +void SocketLayer::SetSocketOptions( SOCKET listenSocket) +{ + int sock_opt = 1; + // // On Vista, can get WSAEACCESS (10013) + /* + if ( setsockopt__( listenSocket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & sock_opt, sizeof ( sock_opt ) ) == -1 ) + { + #if defined(_WIN32) && !defined(_XBOX) && defined(_DEBUG) && !defined(X360) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "setsockopt__(SO_REUSEADDR) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); + #endif + } + */ + + // This doubles the max throughput rate + sock_opt=1024*256; + setsockopt__(listenSocket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + // Immediate hard close. Don't linger the socket, or recreating the socket quickly on Vista fails. + // Fail with voice and xbox + + sock_opt=0; + setsockopt__(listenSocket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + + + // This doesn't make much difference: 10% maybe + // Not supported on console 2 + sock_opt=1024*16; + setsockopt__(listenSocket, SOL_SOCKET, SO_SNDBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + +#ifdef __APPLE__ + // Preventing SEGPIPE on "broken" socket + int set = 1; + setsockopt__(listenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); +#endif + + /* + #ifdef _WIN32 + unsigned long nonblocking = 1; + ioctlsocket__( listenSocket, FIONBIO, &nonblocking ); + #elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) + sock_opt=1; + setsockopt__(listenSocket, SOL_SOCKET, SO_NBIO, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + #else + fcntl( listenSocket, F_SETFL, O_NONBLOCK ); + #endif + */ + + // TODO - teston ipv6 + + // Note: Fails with VDP but not xbox + // Set broadcast capable + sock_opt=1; + if ( setsockopt__( listenSocket, SOL_SOCKET, SO_BROADCAST, ( char * ) & sock_opt, sizeof( sock_opt ) ) == -1 ) + { +#if defined(_WIN32) && defined(_DEBUG) + + DWORD dwIOError = GetLastError(); + // On Vista, can get WSAEACCESS (10013) + // See http://support.microsoft.com/kb/819124 + // http://blogs.msdn.com/wndp/archive/2007/03/19/winsock-so-exclusiveaddruse-on-vista.aspx + // http://msdn.microsoft.com/en-us/library/ms740621(VS.85).aspx + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "setsockopt__(SO_BROADCAST) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); + +#endif + + } +} +SOCKET SocketLayer::CreateBoundSocket_PS3Lobby( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ) +{ + (void) port; + (void) blockingSocket; + (void) forceHostAddress; + (void) socketFamily; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + return 0; + +} +SOCKET SocketLayer::CreateBoundSocket_PSP2( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ) +{ + (void) port; + (void) blockingSocket; + (void) forceHostAddress; + (void) socketFamily; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + return 0; + +} +SOCKET SocketLayer::CreateBoundSocket_Old( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions ) +{ + (void) blockingSocket; + + int ret; + SOCKET listenSocket; + sockaddr_in listenerSocketAddress; + memset(&listenerSocketAddress,0,sizeof(sockaddr_in)); + // Listen on our designated Port# + listenerSocketAddress.sin_port = htons( port ); + + listenSocket = socket__( AF_INET, SOCK_DGRAM, extraSocketOptions ); + + if ( listenSocket == (SOCKET) -1 ) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "socket__(...) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + char str[512]; + strcpy(str,(const char*) messageBuffer); + //Free the buffer. + LocalFree( messageBuffer ); +#endif + + return (SOCKET) -1; + } + + SetSocketOptions(listenSocket); + + // Fill in the rest of the address structure + listenerSocketAddress.sin_family = AF_INET; + + if (forceHostAddress && forceHostAddress[0]) + { + // RAKNET_DEBUG_PRINTF("Force binding %s:%i\n", forceHostAddress, port); + + + + listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); + + } + else + { + // RAKNET_DEBUG_PRINTF("Binding any on port %i\n", port); + listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; + } + + + + + + + + + // bind our name to the socket + ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); + + if ( ret <= -1 ) + { + + + + + + + +#if defined(_WIN32) + DWORD dwIOError = GetLastError(); + if (dwIOError==10048) + { + if (sleepOn10048==0) + return (SOCKET) -1; + // Vista has a bug where it returns WSAEADDRINUSE (10048) if you create, shutdown, then rebind the socket port unless you wait a while first. + // Wait, then rebind + RakSleep(100); + + closesocket__(listenSocket); + listenerSocketAddress.sin_port = htons( port ); + listenSocket = socket__( AF_INET, SOCK_DGRAM, 0 ); + if ( listenSocket == (SOCKET) -1 ) + return false; + SetSocketOptions(listenSocket); + + // Fill in the rest of the address structure + listenerSocketAddress.sin_family = AF_INET; + if (forceHostAddress && forceHostAddress[0]) + { + + + + listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); + + } + else + listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; + + // bind our name to the socket + ret = bind__( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); + + if ( ret >= 0 ) + return listenSocket; + } + dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "bind__(...) failed:Error code - %d\n%s", (unsigned int) dwIOError, (char*) messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#elif (defined(__GNUC__) || defined(__GCCXML__) ) && !defined(_WIN32) + switch (ret) + { + case EBADF: + RAKNET_DEBUG_PRINTF("bind__(): sockfd is not a valid descriptor.\n"); break; + + case ENOTSOCK: + RAKNET_DEBUG_PRINTF("bind__(): Argument is a descriptor for a file, not a socket.\n"); break; + + case EINVAL: + RAKNET_DEBUG_PRINTF("bind__(): The addrlen is wrong, or the socket was not in the AF_UNIX family.\n"); break; + case EROFS: + RAKNET_DEBUG_PRINTF("bind__(): The socket inode would reside on a read-only file system.\n"); break; + case EFAULT: + RAKNET_DEBUG_PRINTF("bind__(): my_addr points outside the user's accessible address space.\n"); break; + case ENAMETOOLONG: + RAKNET_DEBUG_PRINTF("bind__(): my_addr is too long.\n"); break; + case ENOENT: + RAKNET_DEBUG_PRINTF("bind__(): The file does not exist.\n"); break; + case ENOMEM: + RAKNET_DEBUG_PRINTF("bind__(): Insufficient kernel memory was available.\n"); break; + case ENOTDIR: + RAKNET_DEBUG_PRINTF("bind__(): A component of the path prefix is not a directory.\n"); break; + case EACCES: + RAKNET_DEBUG_PRINTF("bind__(): Search permission is denied on a component of the path prefix.\n"); break; + + case ELOOP: + RAKNET_DEBUG_PRINTF("bind__(): Too many symbolic links were encountered in resolving my_addr.\n"); break; + + default: + RAKNET_DEBUG_PRINTF("Unknown bind__() error %i.\n", ret); break; + } +#endif + + return (SOCKET) -1; + } + + return listenSocket; +} +SOCKET SocketLayer::CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions, unsigned short socketFamily ) +{ + (void) blockingSocket; + (void) extraSocketOptions; + (void) socketFamily; + +#if RAKNET_SUPPORT_IPV6!=1 + return CreateBoundSocket_Old(port,blockingSocket,forceHostAddress,sleepOn10048,extraSocketOptions); +#else + +#ifdef _WIN32 + // Vista has a bug where it returns WSAEADDRINUSE (10048) if you create, shutdown, then rebind the socket port unless you wait a while first. + if (sleepOn10048==0) + RakSleep(100); +#endif + + int ret=0; + SOCKET listenSocket; + struct addrinfo hints; + struct addrinfo *servinfo=0, *aip; // will point to the results + PrepareAddrInfoHints(&hints); + hints.ai_family=socketFamily; + char portStr[32]; + Itoa(port,portStr,10); + + // On Ubuntu, "" returns "No address associated with hostname" while 0 works. + if (forceHostAddress && + (_stricmp(forceHostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0 || forceHostAddress[0]==0)) + { + getaddrinfo(0, portStr, &hints, &servinfo); + } + else + { + getaddrinfo(forceHostAddress, portStr, &hints, &servinfo); + } + + // Try all returned addresses until one works + for (aip = servinfo; aip != NULL; aip = aip->ai_next) + { + // Open socket. The address type depends on what + // getaddrinfo() gave us. + listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (listenSocket != -1) + { + ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); + if (ret>=0) + { + // Is this valid? + sockaddr_in6 addr6; + memcpy(&addr6, aip->ai_addr, sizeof(addr6)); + + freeaddrinfo(servinfo); // free the linked-list + + SetSocketOptions(listenSocket); + return listenSocket; + } + } + } + +#if defined(_WIN32) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + + RAKNET_DEBUG_PRINTF( "bind__(...) failed:Error code - %d\n%s", (unsigned int) dwIOError, (char*) messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#elif defined(__GNUC__) || defined(__GCCXML__) && !defined(_WIN32) + switch (ret) + { + case EBADF: + RAKNET_DEBUG_PRINTF("bind__(): sockfd is not a valid descriptor.\n"); break; + + case ENOTSOCK: + RAKNET_DEBUG_PRINTF("bind__(): Argument is a descriptor for a file, not a socket.\n"); break; + + case EINVAL: + RAKNET_DEBUG_PRINTF("bind__(): The addrlen is wrong, or the socket was not in the AF_UNIX family.\n"); break; + case EROFS: + RAKNET_DEBUG_PRINTF("bind__(): The socket inode would reside on a read-only file system.\n"); break; + case EFAULT: + RAKNET_DEBUG_PRINTF("bind__(): my_addr points outside the user's accessible address space.\n"); break; + case ENAMETOOLONG: + RAKNET_DEBUG_PRINTF("bind__(): my_addr is too long.\n"); break; + case ENOENT: + RAKNET_DEBUG_PRINTF("bind__(): The file does not exist.\n"); break; + case ENOMEM: + RAKNET_DEBUG_PRINTF("bind__(): Insufficient kernel memory was available.\n"); break; + case ENOTDIR: + RAKNET_DEBUG_PRINTF("bind__(): A component of the path prefix is not a directory.\n"); break; + case EACCES: + RAKNET_DEBUG_PRINTF("bind__(): Search permission is denied on a component of the path prefix.\n"); break; + + case ELOOP: + RAKNET_DEBUG_PRINTF("bind__(): Too many symbolic links were encountered in resolving my_addr.\n"); break; + + default: + RAKNET_DEBUG_PRINTF("Unknown bind__() error %i.\n", ret); break; + } +#endif + + +#endif + + return (SOCKET) -1; +} +const char* SocketLayer::DomainNameToIP_Old( const char *domainName ) +{ + struct in_addr addr; + memset(&addr,0,sizeof(in_addr)); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + // Use inet_addr instead? What is the difference? + struct hostent * phe = gethostbyname( domainName ); + + if ( phe == 0 || phe->h_addr_list[ 0 ] == 0 ) + { + //cerr << "Yow! Bad host lookup." << endl; + return 0; + } + + if (phe->h_addr_list[ 0 ]==0) + return 0; + + memcpy( &addr, phe->h_addr_list[ 0 ], sizeof( struct in_addr ) ); + return inet_ntoa( addr ); + + + return ""; +} +const char* SocketLayer::DomainNameToIP( const char *domainName ) +{ +#if RAKNET_SUPPORT_IPV6!=1 + return DomainNameToIP_Old(domainName); +#else + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + struct addrinfo hints, *res, *p; + int status; + static char ipstr[INET6_ADDRSTRLEN]; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version + hints.ai_socktype = SOCK_DGRAM; + + if ((status = getaddrinfo(domainName, NULL, &hints, &res)) != 0) { + return 0; + } + + p=res; +// for(p = res;p != NULL; p = p->ai_next) { + void *addr; +// char *ipver; + + // get the pointer to the address itself, + // different fields in IPv4 and IPv6: + if (p->ai_family == AF_INET) + { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + addr = &(ipv4->sin_addr); + strcpy(ipstr, inet_ntoa( ipv4->sin_addr )); + } + else + { + // TODO - test + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + addr = &(ipv6->sin6_addr); + // inet_ntop function does not exist on windows + // http://www.mail-archive.com/users@ipv6.org/msg02107.html + getnameinfo((struct sockaddr *)ipv6, sizeof(struct sockaddr_in6), ipstr, 1, NULL, 0, NI_NUMERICHOST); + } + freeaddrinfo(res); // free the linked list + return ipstr; +// } + + + + return ""; + +#endif // #if RAKNET_SUPPORT_IPV6!=1 +} + + +void SocketLayer::Write( const SOCKET writeSocket, const char* data, const int length ) +{ +#ifdef _DEBUG + RakAssert( writeSocket != (SOCKET) -1 ); +#endif + + send__( writeSocket, data, length, 0 ); +} +void SocketLayer::RecvFromBlocking_Old( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ) +{ + (void) rakPeer; + + sockaddr* sockAddrPtr; + socklen_t sockLen; + socklen_t* socketlenPtr=(socklen_t*) &sockLen; + sockaddr_in sa; + memset(&sa,0,sizeof(sockaddr_in)); + int dataOutSize; + const int flag=0; + + (void) remotePortRakNetWasStartedOn_PS3; + (void) extraSocketOptions; + + + + + + + + + + + + + + + + + + + + + { + sockLen=sizeof(sa); + sa.sin_family = AF_INET; + sa.sin_port=0; + sockAddrPtr=(sockaddr*) &sa; + } + + + + + dataOutSize=MAXIMUM_MTU_SIZE; + + + *bytesReadOut = recvfrom__( s, dataOut, dataOutSize, flag, sockAddrPtr, socketlenPtr ); + + + + + + + + + + if (*bytesReadOut<=0) + return; + *timeRead=RakNet::GetTimeUS(); + + + + + + + + + + { + systemAddressOut->SetPortNetworkOrder( sa.sin_port ); + systemAddressOut->address.addr4.sin_addr.s_addr=sa.sin_addr.s_addr; + } +} + +void SocketLayer::RecvFromBlocking( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ) +{ +#if RAKNET_SUPPORT_IPV6!=1 + RecvFromBlocking_Old(s,rakPeer,remotePortRakNetWasStartedOn_PS3,extraSocketOptions,dataOut,bytesReadOut,systemAddressOut,timeRead); +#else + (void) rakPeer; + sockaddr_storage their_addr; + sockaddr* sockAddrPtr; + socklen_t sockLen; + socklen_t* socketlenPtr=(socklen_t*) &sockLen; + memset(&their_addr,0,sizeof(their_addr)); + int dataOutSize; + const int flag=0; + + (void) remotePortRakNetWasStartedOn_PS3; + (void) extraSocketOptions; + + + + + + + + + + + + { + sockLen=sizeof(their_addr); + sockAddrPtr=(sockaddr*) &their_addr; + } + + + + + dataOutSize=MAXIMUM_MTU_SIZE; + + + *bytesReadOut = recvfrom__( s, dataOut, dataOutSize, flag, sockAddrPtr, socketlenPtr ); + + + + + + + + + + if (*bytesReadOut<=0) + return; + *timeRead=RakNet::GetTimeUS(); + + + + + + + + + + { + if (their_addr.ss_family==AF_INET) + { + memcpy(&systemAddressOut->address.addr4,(sockaddr_in *)&their_addr,sizeof(sockaddr_in)); + systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr4.sin_port); + // systemAddressOut->address.addr4.sin_port=ntohs( systemAddressOut->address.addr4.sin_port ); + } + else + { + memcpy(&systemAddressOut->address.addr6,(sockaddr_in6 *)&their_addr,sizeof(sockaddr_in6)); + systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr6.sin6_port); + // systemAddressOut->address.addr6.sin6_port=ntohs( systemAddressOut->address.addr6.sin6_port ); + } + } + +#endif // defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) +} + +int SocketLayer::SendTo_PS3Lobby( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ) +{ + (void) s; + (void) data; + (void) length; + (void) remotePortRakNetWasStartedOn_PS3; + (void) systemAddress; + + int len=0; + + + + + + + + + + + + + + + return len; +} +int SocketLayer::SendTo_PSP2( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ) +{ + (void) s; + (void) data; + (void) length; + (void) remotePortRakNetWasStartedOn_PS3; + (void) systemAddress; + + int len=0; + + + + + + + + + + + + + + return len; +} +int SocketLayer::SendTo_360( SOCKET s, const char *data, int length, const char *voiceData, int voiceLength, const SystemAddress &systemAddress, unsigned int extraSocketOptions ) +{ + (void) s; + (void) data; + (void) length; + (void) voiceData; + (void) voiceLength; + (void) extraSocketOptions; + (void) systemAddress; + + int len=0; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + return len; +} +int SocketLayer::SendTo_PC( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, const char *file, const long line ) +{ + // TODO + // http://www.linuxquestions.org/questions/linux-software-2/ipv6-linux-sendto-problems-519485/ + +// #if RAKNET_SUPPORT_IPV6==1 +// RakAssert( +// systemAddress.address.addr4.sin_family!=AF_MAX && +// (systemAddress.address.addr4.sin_family==AF_INET || (systemAddress.address.addr6.sin6_scope_id!=0)) +// ); +// #endif + + /* + sockaddr_in sa; + memset(&sa,0,sizeof(sockaddr_in)); + sa.sin_port = htons( port ); // User port + sa.sin_addr.s_addr = binaryAddress; + sa.sin_family = socketFamily; + */ + + int len=0; + do + { +#ifdef DEBUG_SENDTO_SPIKES + RakNetTime start = RakNet::GetTime(); +#else + (void) file; + (void) line; +#endif + if (systemAddress.address.addr4.sin_family==AF_INET) + { + //systemAddress.address.addr4.sin_port=htons(systemAddress.address.addr4.sin_port); + len = sendto__( s, data, length, 0, ( const sockaddr* ) & systemAddress.address.addr4, sizeof( sockaddr_in ) ); + //systemAddress.address.addr4.sin_port=ntohs(systemAddress.address.addr4.sin_port); + } + else + { +#if RAKNET_SUPPORT_IPV6==1 + // systemAddress.address.addr6.sin6_port=htons(systemAddress.address.addr6.sin6_port); + len = sendto__( s, data, length, 0, ( const sockaddr* ) & systemAddress.address.addr6, sizeof( sockaddr_in6 ) ); + //systemAddress.address.addr6.sin6_port=ntohs(systemAddress.address.addr6.sin6_port); +#endif + } + +#ifdef DEBUG_SENDTO_SPIKES + RakNetTime end = RakNet::GetTime(); + static unsigned int callCount=1; + RAKNET_DEBUG_PRINTF("%i. SendTo_PC, time=%" PRINTF_64_BIT_MODIFIER "u, elapsed=%" PRINTF_64_BIT_MODIFIER "u, length=%i, returned=%i, binaryAddress=%i, port=%i, file=%s, line=%i\n", callCount++, end, end-start, length, len, binaryAddress, port, file, line); +#endif + if (len<0) + { + RAKNET_DEBUG_PRINTF("sendto failed with code %i for char %i and length %i.\n", len, data[0], length); + } + } + while ( len == 0 ); + return len; +} + +#ifdef _MSC_VER +#pragma warning( disable : 4702 ) // warning C4702: unreachable code +#endif +int SocketLayer::SendTo( SOCKET s, const char *data, int length, SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ) +{ + (void) extraSocketOptions; + + int len=0; + RakAssert(length<=MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE); +#if !defined(__S3E__) + RakAssert(systemAddress.address.addr4.sin_family!=AF_MAX); +#endif + + if (slo) + { + len = slo->RakNetSendTo(s,data,length,systemAddress); + if ( len != -1 ) + return 0; + return 1; + } + + if ( s == (SOCKET) -1 ) + { + return -1; + } + + + if (remotePortRakNetWasStartedOn_PS3!=0) + { + + + + + + } + else + { + + + + + + + len = SendTo_PC(s,data,length,systemAddress,file,line); + + } + + if ( len != -1 ) + return 0; + +#if defined(_WIN32) && !defined(_WIN32_WCE) + + DWORD dwIOError = WSAGetLastError(); + + if ( dwIOError == WSAECONNRESET ) + { +#if defined(_DEBUG) + RAKNET_DEBUG_PRINTF( "A previous send operation resulted in an ICMP Port Unreachable message.\n" ); +#endif + + } + else if ( dwIOError != WSAEWOULDBLOCK && dwIOError != WSAEADDRNOTAVAIL) + { +#if defined(_WIN32) && defined(_DEBUG) + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "sendto failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); +#endif + + } + + return dwIOError; +#endif + + return 1; // error +} +// Not enough info for IPV6 +// int SocketLayer::SendTo( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ) +// { +// SystemAddress systemAddress; +// systemAddress.FromStringAndPort(ip,port); +// return SendTo( s, data, length, systemAddress,remotePortRakNetWasStartedOn_PS3, extraSocketOptions, file, line ); +// } +int SocketLayer::SendToTTL( SOCKET s, const char *data, int length, SystemAddress &systemAddress, int ttl ) +{ + if (slo) + return slo->RakNetSendTo(s,data,length,systemAddress); + + + int oldTTL; + socklen_t opLen=sizeof(oldTTL); + // Get the current TTL + if (getsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & oldTTL, &opLen ) == -1) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "getsockopt__(IPPROTO_IP,IP_TTL) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#endif + } + + // Set to TTL + int newTTL=ttl; + if (setsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & newTTL, sizeof ( newTTL ) ) == -1) + { + +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "setsockopt__(IPPROTO_IP,IP_TTL) failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#endif + } + + // Send + int res = SendTo(s,data,length,systemAddress,0,0, __FILE__, __LINE__ ); + + // Restore the old TTL + setsockopt__(s, systemAddress.GetIPPROTO(), IP_TTL, ( char * ) & oldTTL, opLen ); + + return res; + + + +} + + +RakNet::RakString SocketLayer::GetSubNetForSocketAndIp(SOCKET inSock, RakNet::RakString inIpString) +{ + RakNet::RakString netMaskString; + RakNet::RakString ipString; + + + + + +#if defined(_WIN32) + INTERFACE_INFO InterfaceList[20]; + unsigned long nBytesReturned; + if (WSAIoctl(inSock, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList, + sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR) { + return ""; + } + + int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO); + + for (int i = 0; i < nNumInterfaces; ++i) + { + sockaddr_in *pAddress; + pAddress = (sockaddr_in *) & (InterfaceList[i].iiAddress); + ipString=inet_ntoa(pAddress->sin_addr); + + if (inIpString==ipString) + { + pAddress = (sockaddr_in *) & (InterfaceList[i].iiNetmask); + netMaskString=inet_ntoa(pAddress->sin_addr); + return netMaskString; + } + } + return ""; +#else + + int fd,fd2; + fd2 = socket__(AF_INET, SOCK_DGRAM, 0); + + if(fd2 < 0) + { + return ""; + } + + struct ifconf ifc; + char buf[1999]; + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if(ioctl(fd2, SIOCGIFCONF, &ifc) < 0) + { + return ""; + } + + struct ifreq *ifr; + ifr = ifc.ifc_req; + int intNum = ifc.ifc_len / sizeof(struct ifreq); + for(int i = 0; i < intNum; i++) + { + ipString=inet_ntoa(((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr); + + if (inIpString==ipString) + { + struct ifreq ifr2; + fd = socket__(AF_INET, SOCK_DGRAM, 0); + if(fd < 0) + { + return ""; + } + ifr2.ifr_addr.sa_family = AF_INET; + + strncpy(ifr2.ifr_name, ifr[i].ifr_name, IFNAMSIZ-1); + + ioctl(fd, SIOCGIFNETMASK, &ifr2); + + close(fd); + close(fd2); + netMaskString=inet_ntoa(((struct sockaddr_in *)&ifr2.ifr_addr)->sin_addr); + + return netMaskString; + } + } + + close(fd2); + return ""; + +#endif + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void GetMyIP_Win32( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) +{ + char ac[ 80 ]; + if ( gethostname( ac, sizeof( ac ) ) == -1 ) + { + #if defined(_WIN32) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "gethostname failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); + #endif + return ; + } + + +#if RAKNET_SUPPORT_IPV6==1 + struct addrinfo hints; + struct addrinfo *servinfo=0, *aip; // will point to the results + PrepareAddrInfoHints(&hints); + getaddrinfo(ac, "", &hints, &servinfo); + + int idx; + for (idx=0, aip = servinfo; aip != NULL && idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; aip = aip->ai_next, idx++) + { + if (aip->ai_family == AF_INET) + { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)aip->ai_addr; + memcpy(&addresses[idx].address.addr4,ipv4,sizeof(sockaddr_in)); + } + else + { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)aip->ai_addr; + memcpy(&addresses[idx].address.addr4,ipv6,sizeof(sockaddr_in6)); + } + + } + + freeaddrinfo(servinfo); // free the linked-list +#else + struct hostent *phe = gethostbyname( ac ); + + if ( phe == 0 ) + { + #ifdef _WIN32 + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "gethostbyname failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); + #endif + return ; + } + int idx; + for ( idx = 0; idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ++idx ) + { + if (phe->h_addr_list[ idx ] == 0) + break; + + memcpy(&addresses[idx].address.addr4.sin_addr,phe->h_addr_list[ idx ],sizeof(struct in_addr)); + + } +#endif + + + while (idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS) + { + addresses[idx]=UNASSIGNED_SYSTEM_ADDRESS; + idx++; + } +} +// #else +/* +void GetMyIP_Linux( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) +{ + struct ifaddrs *ifaddr, *ifa; + int family, s; + char host[NI_MAXHOST]; + struct in_addr linux_in_addr; + + if (getifaddrs(&ifaddr) == -1) { + printf( "Error getting interface list\n"); + } + + int idx = 0; + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) continue; + family = ifa->ifa_addr->sa_family; + + if (family == AF_INET) { + s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (s != 0) { + // printf ("getnameinfo() failed: %s\n", gai_strerror(s)); + } + else if (strcmp(host,"127.0.0.1")!=0) + { + if (inet_aton(host, &addresses[idx].address.addr4.sin_addr)!=0) + idx++; + } + } +#if RAKNET_SUPPORT_IPV6==1 + else + { + s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (s != 0) { + // printf ("getnameinfo() failed: %s\n", gai_strerror(s)); + } + else if (strcmp(host,"::1")!=0) + { + if (inet_pton(family, host, &addresses[idx].address.addr6.sin6_addr)!=0) + idx++; + } + } +#endif + } + + for ( ; idx < MAXIMUM_NUMBER_OF_INTERNAL_IDS; ++idx ) + { + addresses[idx]=UNASSIGNED_SYSTEM_ADDRESS; + } + + freeifaddrs(ifaddr); +} +*/ + + + +void SocketLayer::GetMyIP( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ) +{ + + + + +#if defined(_WIN32) + GetMyIP_Win32(addresses); +#else +// GetMyIP_Linux(addresses); + GetMyIP_Win32(addresses); +#endif +} + + +unsigned short SocketLayer::GetLocalPort(SOCKET s) +{ + SystemAddress sa; + GetSystemAddress(s,&sa); + return sa.GetPort(); +} +void SocketLayer::GetSystemAddress_Old ( SOCKET s, SystemAddress *systemAddressOut ) +{ + sockaddr_in sa; + memset(&sa,0,sizeof(sockaddr_in)); + socklen_t len = sizeof(sa); + if (getsockname__(s, (sockaddr*)&sa, &len)!=0) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "getsockname failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); +#endif + *systemAddressOut = UNASSIGNED_SYSTEM_ADDRESS; + return; + } + + systemAddressOut->SetPortNetworkOrder(sa.sin_port); + systemAddressOut->address.addr4.sin_addr.s_addr=sa.sin_addr.s_addr; +} +void SocketLayer::GetSystemAddress ( SOCKET s, SystemAddress *systemAddressOut ) +{ +#if RAKNET_SUPPORT_IPV6!=1 + GetSystemAddress_Old(s,systemAddressOut); +#else + socklen_t slen; + sockaddr_storage ss; + slen = sizeof(ss); + + if (getsockname__(s, (struct sockaddr *)&ss, &slen)!=0) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "getsockname failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); +#endif + systemAddressOut->FromString(0); + return; + } + + if (ss.ss_family==AF_INET) + { + memcpy(&systemAddressOut->address.addr4,(sockaddr_in *)&ss,sizeof(sockaddr_in)); + systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr4.sin_port); + + uint32_t zero = 0; + if (memcmp(&systemAddressOut->address.addr4.sin_addr.s_addr, &zero, sizeof(zero))==0) + systemAddressOut->SetToLoopback(4); + // systemAddressOut->address.addr4.sin_port=ntohs(systemAddressOut->address.addr4.sin_port); + } + else + { + memcpy(&systemAddressOut->address.addr6,(sockaddr_in6 *)&ss,sizeof(sockaddr_in6)); + systemAddressOut->debugPort=ntohs(systemAddressOut->address.addr6.sin6_port); + + char zero[16]; + memset(zero,0,sizeof(zero)); + if (memcmp(&systemAddressOut->address.addr4.sin_addr.s_addr, &zero, sizeof(zero))==0) + systemAddressOut->SetToLoopback(6); + + // systemAddressOut->address.addr6.sin6_port=ntohs(systemAddressOut->address.addr6.sin6_port); + } +#endif // #if RAKNET_SUPPORT_IPV6!=1 +} + +void SocketLayer::SetSocketLayerOverride(SocketLayerOverride *_slo) +{ + slo=_slo; +} + +bool SocketLayer::GetFirstBindableIP(char firstBindable[128], int ipProto) +{ + SystemAddress ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ]; + SocketLayer::GetMyIP( ipList ); + + + if (ipProto==AF_UNSPEC) + + { + ipList[0].ToString(false,firstBindable); + return true; + } + + // Find the first valid host address + unsigned int l; + for (l=0; l < MAXIMUM_NUMBER_OF_INTERNAL_IDS; l++) + { + if (ipList[l]==UNASSIGNED_SYSTEM_ADDRESS) + break; + if (ipList[l].GetIPVersion()==4 && ipProto==AF_INET) + break; + if (ipList[l].GetIPVersion()==6 && ipProto==AF_INET6) + break; + } + + if (ipList[l]==UNASSIGNED_SYSTEM_ADDRESS || l==MAXIMUM_NUMBER_OF_INTERNAL_IDS) + return false; +// RAKNET_DEBUG_PRINTF("%i %i %i %i\n", +// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[0], +// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[1], +// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[2], +// ((char*)(&ipList[l].address.addr4.sin_addr.s_addr))[3] +// ); + ipList[l].ToString(false,firstBindable); + return true; + +} + + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/src/raknet/SocketLayer.h b/src/raknet/SocketLayer.h index 01265d5..ba089fa 100755 --- a/src/raknet/SocketLayer.h +++ b/src/raknet/SocketLayer.h @@ -1,164 +1,164 @@ -/// \file -/// \brief SocketLayer class implementation -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - - -#ifndef __SOCKET_LAYER_H -#define __SOCKET_LAYER_H - -#include "RakMemoryOverride.h" -#include "SocketIncludes.h" -#include "RakNetTypes.h" -#include "RakNetSmartPtr.h" -#include "RakNetSocket.h" -#include "Export.h" -#include "MTUSize.h" -#include "RakString.h" - -//#include "ClientContextStruct.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeer; - -class RAK_DLL_EXPORT SocketLayerOverride -{ -public: - SocketLayerOverride() {} - virtual ~SocketLayerOverride() {} - - /// Called when SendTo would otherwise occur. - virtual int RakNetSendTo( SOCKET s, const char *data, int length, const SystemAddress &systemAddress )=0; - - /// Called when RecvFrom would otherwise occur. Return number of bytes read. Write data into dataOut - // Return -1 to use RakNet's normal recvfrom, 0 to abort RakNet's normal recvfrom, and positive to return data - virtual int RakNetRecvFrom( const SOCKET sIn, RakPeer *rakPeerIn, char dataOut[ MAXIMUM_MTU_SIZE ], SystemAddress *senderOut, bool calledFromMainThread )=0; -}; - - -// A platform independent implementation of Berkeley sockets, with settings used by RakNet -class RAK_DLL_EXPORT SocketLayer -{ - -public: - - /// Default Constructor - SocketLayer(); - - // Destructor - ~SocketLayer(); - - /// Creates a bound socket to listen for incoming connections on the specified port - /// \param[in] port the port number - /// \param[in] blockingSocket - /// \return A new socket used for accepting clients - static SOCKET CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions, unsigned short socketFamily ); - static SOCKET CreateBoundSocket_Old( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions ); - static SOCKET CreateBoundSocket_PS3Lobby( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ); - static SOCKET CreateBoundSocket_PSP2( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ); - - /// Returns if this specified port is in use, for UDP - /// \param[in] port the port number - /// \return If this port is already in use - static bool IsPortInUse_Old(unsigned short port, const char *hostAddress); - static bool IsPortInUse(unsigned short port, const char *hostAddress, unsigned short socketFamily ); - static bool IsSocketFamilySupported(const char *hostAddress, unsigned short socketFamily); - - static const char* DomainNameToIP_Old( const char *domainName ); - static const char* DomainNameToIP( const char *domainName ); - - /// Write \a data of length \a length to \a writeSocket - /// \param[in] writeSocket The socket to write to - /// \param[in] data The data to write - /// \param[in] length The length of \a data - static void Write( const SOCKET writeSocket, const char* data, const int length ); - - /// Read data from a socket - /// \param[in] s the socket - /// \param[in] rakPeer The instance of rakPeer containing the recvFrom C callback - /// \param[in] errorCode An error code if an error occured . - /// \param[in] connectionSocketIndex Which of the sockets in RakPeer we are using - /// \return Returns true if you successfully read data, false on error. - static void RecvFromBlocking_Old( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ); - static void RecvFromBlocking( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ); - - /// Given a socket and IP, retrieves the subnet mask, on linux the socket is unused - /// \param[in] inSock the socket - /// \param[in] inIpString The ip of the interface you wish to retrieve the subnet mask from - /// \return Returns the ip dotted subnet mask if successful, otherwise returns empty string ("") - static RakNet::RakString GetSubNetForSocketAndIp(SOCKET inSock, RakNet::RakString inIpString); - - - /// Sets the socket flags to nonblocking - /// \param[in] listenSocket the socket to set - static void SetNonBlocking( SOCKET listenSocket); - - - /// Retrieve all local IP address in a string format. - /// \param[in] s The socket whose port we are referring to - /// \param[in] ipList An array of ip address in dotted notation. - static void GetMyIP( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ); - - - /// Call sendto (UDP obviously) - /// \param[in] s the socket - /// \param[in] data The byte buffer to send - /// \param[in] length The length of the \a data in bytes - /// \param[in] ip The address of the remote host in dotted notation. - /// \param[in] port The port number to send to. - /// \return 0 on success, nonzero on failure. -// static int SendTo( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ); - - /// Call sendto (UDP obviously) - /// It won't reach the recipient, except on a LAN - /// However, this is good for opening routers / firewalls - /// \param[in] s the socket - /// \param[in] data The byte buffer to send - /// \param[in] length The length of the \a data in bytes - /// \param[in] ip The address of the remote host in dotted notation. - /// \param[in] port The port number to send to. - /// \param[in] ttl Max hops of datagram - /// \return 0 on success, nonzero on failure. - static int SendToTTL( SOCKET s, const char *data, int length, SystemAddress &systemAddress, int ttl ); - - /// Call sendto (UDP obviously) - /// \param[in] s the socket - /// \param[in] data The byte buffer to send - /// \param[in] length The length of the \a data in bytes - /// \param[in] binaryAddress The address of the remote host in binary format. - /// \param[in] port The port number to send to. - /// \return 0 on success, nonzero on failure. - static int SendTo( SOCKET s, const char *data, int length, SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ); - - static unsigned short GetLocalPort(SOCKET s); - static void GetSystemAddress_Old ( SOCKET s, SystemAddress *systemAddressOut ); - static void GetSystemAddress ( SOCKET s, SystemAddress *systemAddressOut ); - - static void SetSocketLayerOverride(SocketLayerOverride *_slo); - static SocketLayerOverride* GetSocketLayerOverride(void) {return slo;} - - static int SendTo_PS3Lobby( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ); - static int SendTo_PSP2( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ); - static int SendTo_360( SOCKET s, const char *data, int length, const char *voiceData, int voiceLength, const SystemAddress &systemAddress, unsigned int extraSocketOptions ); - static int SendTo_PC( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, const char *file, const long line ); - - static void SetDoNotFragment( SOCKET listenSocket, int opt, int IPPROTO ); - - - // AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - static bool GetFirstBindableIP(char firstBindable[128], int ipProto); - -private: - - static void SetSocketOptions( SOCKET listenSocket); - static SocketLayerOverride *slo; -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief SocketLayer class implementation +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + + +#ifndef __SOCKET_LAYER_H +#define __SOCKET_LAYER_H + +#include "RakMemoryOverride.h" +#include "SocketIncludes.h" +#include "RakNetTypes.h" +#include "RakNetSmartPtr.h" +#include "RakNetSocket.h" +#include "Export.h" +#include "MTUSize.h" +#include "RakString.h" + +//#include "ClientContextStruct.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeer; + +class RAK_DLL_EXPORT SocketLayerOverride +{ +public: + SocketLayerOverride() {} + virtual ~SocketLayerOverride() {} + + /// Called when SendTo would otherwise occur. + virtual int RakNetSendTo( SOCKET s, const char *data, int length, const SystemAddress &systemAddress )=0; + + /// Called when RecvFrom would otherwise occur. Return number of bytes read. Write data into dataOut + // Return -1 to use RakNet's normal recvfrom, 0 to abort RakNet's normal recvfrom, and positive to return data + virtual int RakNetRecvFrom( const SOCKET sIn, RakPeer *rakPeerIn, char dataOut[ MAXIMUM_MTU_SIZE ], SystemAddress *senderOut, bool calledFromMainThread )=0; +}; + + +// A platform independent implementation of Berkeley sockets, with settings used by RakNet +class RAK_DLL_EXPORT SocketLayer +{ + +public: + + /// Default Constructor + SocketLayer(); + + // Destructor + ~SocketLayer(); + + /// Creates a bound socket to listen for incoming connections on the specified port + /// \param[in] port the port number + /// \param[in] blockingSocket + /// \return A new socket used for accepting clients + static SOCKET CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions, unsigned short socketFamily ); + static SOCKET CreateBoundSocket_Old( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned int sleepOn10048, unsigned int extraSocketOptions ); + static SOCKET CreateBoundSocket_PS3Lobby( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ); + static SOCKET CreateBoundSocket_PSP2( unsigned short port, bool blockingSocket, const char *forceHostAddress, unsigned short socketFamily ); + + /// Returns if this specified port is in use, for UDP + /// \param[in] port the port number + /// \return If this port is already in use + static bool IsPortInUse_Old(unsigned short port, const char *hostAddress); + static bool IsPortInUse(unsigned short port, const char *hostAddress, unsigned short socketFamily ); + static bool IsSocketFamilySupported(const char *hostAddress, unsigned short socketFamily); + + static const char* DomainNameToIP_Old( const char *domainName ); + static const char* DomainNameToIP( const char *domainName ); + + /// Write \a data of length \a length to \a writeSocket + /// \param[in] writeSocket The socket to write to + /// \param[in] data The data to write + /// \param[in] length The length of \a data + static void Write( const SOCKET writeSocket, const char* data, const int length ); + + /// Read data from a socket + /// \param[in] s the socket + /// \param[in] rakPeer The instance of rakPeer containing the recvFrom C callback + /// \param[in] errorCode An error code if an error occured . + /// \param[in] connectionSocketIndex Which of the sockets in RakPeer we are using + /// \return Returns true if you successfully read data, false on error. + static void RecvFromBlocking_Old( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ); + static void RecvFromBlocking( const SOCKET s, RakPeer *rakPeer, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, char *dataOut, int *bytesReadOut, SystemAddress *systemAddressOut, RakNet::TimeUS *timeRead ); + + /// Given a socket and IP, retrieves the subnet mask, on linux the socket is unused + /// \param[in] inSock the socket + /// \param[in] inIpString The ip of the interface you wish to retrieve the subnet mask from + /// \return Returns the ip dotted subnet mask if successful, otherwise returns empty string ("") + static RakNet::RakString GetSubNetForSocketAndIp(SOCKET inSock, RakNet::RakString inIpString); + + + /// Sets the socket flags to nonblocking + /// \param[in] listenSocket the socket to set + static void SetNonBlocking( SOCKET listenSocket); + + + /// Retrieve all local IP address in a string format. + /// \param[in] s The socket whose port we are referring to + /// \param[in] ipList An array of ip address in dotted notation. + static void GetMyIP( SystemAddress addresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS] ); + + + /// Call sendto (UDP obviously) + /// \param[in] s the socket + /// \param[in] data The byte buffer to send + /// \param[in] length The length of the \a data in bytes + /// \param[in] ip The address of the remote host in dotted notation. + /// \param[in] port The port number to send to. + /// \return 0 on success, nonzero on failure. +// static int SendTo( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ); + + /// Call sendto (UDP obviously) + /// It won't reach the recipient, except on a LAN + /// However, this is good for opening routers / firewalls + /// \param[in] s the socket + /// \param[in] data The byte buffer to send + /// \param[in] length The length of the \a data in bytes + /// \param[in] ip The address of the remote host in dotted notation. + /// \param[in] port The port number to send to. + /// \param[in] ttl Max hops of datagram + /// \return 0 on success, nonzero on failure. + static int SendToTTL( SOCKET s, const char *data, int length, SystemAddress &systemAddress, int ttl ); + + /// Call sendto (UDP obviously) + /// \param[in] s the socket + /// \param[in] data The byte buffer to send + /// \param[in] length The length of the \a data in bytes + /// \param[in] binaryAddress The address of the remote host in binary format. + /// \param[in] port The port number to send to. + /// \return 0 on success, nonzero on failure. + static int SendTo( SOCKET s, const char *data, int length, SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3, unsigned int extraSocketOptions, const char *file, const long line ); + + static unsigned short GetLocalPort(SOCKET s); + static void GetSystemAddress_Old ( SOCKET s, SystemAddress *systemAddressOut ); + static void GetSystemAddress ( SOCKET s, SystemAddress *systemAddressOut ); + + static void SetSocketLayerOverride(SocketLayerOverride *_slo); + static SocketLayerOverride* GetSocketLayerOverride(void) {return slo;} + + static int SendTo_PS3Lobby( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ); + static int SendTo_PSP2( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, unsigned short remotePortRakNetWasStartedOn_PS3 ); + static int SendTo_360( SOCKET s, const char *data, int length, const char *voiceData, int voiceLength, const SystemAddress &systemAddress, unsigned int extraSocketOptions ); + static int SendTo_PC( SOCKET s, const char *data, int length, const SystemAddress &systemAddress, const char *file, const long line ); + + static void SetDoNotFragment( SOCKET listenSocket, int opt, int IPPROTO ); + + + // AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + static bool GetFirstBindableIP(char firstBindable[128], int ipProto); + +private: + + static void SetSocketOptions( SOCKET listenSocket); + static SocketLayerOverride *slo; +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/StringCompressor.cpp b/src/raknet/StringCompressor.cpp index 8516a33..dd63f84 100755 --- a/src/raknet/StringCompressor.cpp +++ b/src/raknet/StringCompressor.cpp @@ -1,499 +1,499 @@ -/// \file -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "StringCompressor.h" -#include "DS_HuffmanEncodingTree.h" -#include "BitStream.h" -#include "RakString.h" -#include "RakAssert.h" -#include - -#include - - - - - -using namespace RakNet; - -StringCompressor* StringCompressor::instance=0; -int StringCompressor::referenceCount=0; - -void StringCompressor::AddReference(void) -{ - if (++referenceCount==1) - { - instance = RakNet::OP_NEW( _FILE_AND_LINE_ ); - } -} -void StringCompressor::RemoveReference(void) -{ - RakAssert(referenceCount > 0); - - if (referenceCount > 0) - { - if (--referenceCount==0) - { - RakNet::OP_DELETE(instance, _FILE_AND_LINE_); - instance=0; - } - } -} - -StringCompressor* StringCompressor::Instance(void) -{ - return instance; -} - -unsigned int englishCharacterFrequencies[ 256 ] = -{ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 722, - 0, - 0, - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 11084, - 58, - 63, - 1, - 0, - 31, - 0, - 317, - 64, - 64, - 44, - 0, - 695, - 62, - 980, - 266, - 69, - 67, - 56, - 7, - 73, - 3, - 14, - 2, - 69, - 1, - 167, - 9, - 1, - 2, - 25, - 94, - 0, - 195, - 139, - 34, - 96, - 48, - 103, - 56, - 125, - 653, - 21, - 5, - 23, - 64, - 85, - 44, - 34, - 7, - 92, - 76, - 147, - 12, - 14, - 57, - 15, - 39, - 15, - 1, - 1, - 1, - 2, - 3, - 0, - 3611, - 845, - 1077, - 1884, - 5870, - 841, - 1057, - 2501, - 3212, - 164, - 531, - 2019, - 1330, - 3056, - 4037, - 848, - 47, - 2586, - 2919, - 4771, - 1707, - 535, - 1106, - 152, - 1243, - 100, - 0, - 2, - 0, - 10, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 -}; - -StringCompressor::StringCompressor() -{ - DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON(); - - // Make a default tree immediately, since this is used for RPC possibly from multiple threads at the same time - HuffmanEncodingTree *huffmanEncodingTree = RakNet::OP_NEW( _FILE_AND_LINE_ ); - huffmanEncodingTree->GenerateFromFrequencyTable( englishCharacterFrequencies ); - - huffmanEncodingTrees.Set(0, huffmanEncodingTree); -} -void StringCompressor::GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, uint8_t languageId ) -{ - HuffmanEncodingTree *huffmanEncodingTree; - if (huffmanEncodingTrees.Has(languageId)) - { - huffmanEncodingTree = huffmanEncodingTrees.Get(languageId); - RakNet::OP_DELETE(huffmanEncodingTree, _FILE_AND_LINE_); - } - - unsigned index; - unsigned int frequencyTable[ 256 ]; - - if ( inputLength == 0 ) - return ; - - // Zero out the frequency table - memset( frequencyTable, 0, sizeof( frequencyTable ) ); - - // Generate the frequency table from the strings - for ( index = 0; index < inputLength; index++ ) - frequencyTable[ input[ index ] ] ++; - - // Build the tree - huffmanEncodingTree = RakNet::OP_NEW( _FILE_AND_LINE_ ); - huffmanEncodingTree->GenerateFromFrequencyTable( frequencyTable ); - huffmanEncodingTrees.Set(languageId, huffmanEncodingTree); -} - -StringCompressor::~StringCompressor() -{ - for (unsigned i=0; i < huffmanEncodingTrees.Size(); i++) - RakNet::OP_DELETE(huffmanEncodingTrees[i], _FILE_AND_LINE_); -} - -void StringCompressor::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) -{ - HuffmanEncodingTree *huffmanEncodingTree; - if (huffmanEncodingTrees.Has(languageId)==false) - return; - huffmanEncodingTree=huffmanEncodingTrees.Get(languageId); - - if ( input == 0 ) - { - output->WriteCompressed( (uint32_t) 0 ); - return ; - } - - RakNet::BitStream encodedBitStream; - - uint32_t stringBitLength; - - int charsToWrite; - - if ( maxCharsToWrite<=0 || ( int ) strlen( input ) < maxCharsToWrite ) - charsToWrite = ( int ) strlen( input ); - else - charsToWrite = maxCharsToWrite - 1; - - huffmanEncodingTree->EncodeArray( ( unsigned char* ) input, charsToWrite, &encodedBitStream ); - - stringBitLength = (uint32_t) encodedBitStream.GetNumberOfBitsUsed(); - - output->WriteCompressed( stringBitLength ); - - output->WriteBits( encodedBitStream.GetData(), stringBitLength ); -} - -bool StringCompressor::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) -{ - HuffmanEncodingTree *huffmanEncodingTree; - if (huffmanEncodingTrees.Has(languageId)==false) - return false; - if (maxCharsToWrite<=0) - return false; - huffmanEncodingTree=huffmanEncodingTrees.Get(languageId); - - uint32_t stringBitLength; - int bytesInStream; - - output[ 0 ] = 0; - - if ( input->ReadCompressed( stringBitLength ) == false ) - return false; - - if ( (unsigned) input->GetNumberOfUnreadBits() < stringBitLength ) - return false; - - bytesInStream = huffmanEncodingTree->DecodeArray( input, stringBitLength, maxCharsToWrite, ( unsigned char* ) output ); - - if ( bytesInStream < maxCharsToWrite ) - output[ bytesInStream ] = 0; - else - output[ maxCharsToWrite - 1 ] = 0; - - return true; -} -#ifdef _CSTRING_COMPRESSOR -void StringCompressor::EncodeString( const CString &input, int maxCharsToWrite, RakNet::BitStream *output ) -{ - LPTSTR p = input; - EncodeString(p, maxCharsToWrite*sizeof(TCHAR), output, languageID); -} -bool StringCompressor::DecodeString( CString &output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) -{ - LPSTR p = output.GetBuffer(maxCharsToWrite*sizeof(TCHAR)); - DecodeString(p,maxCharsToWrite*sizeof(TCHAR), input, languageID); - output.ReleaseBuffer(0) - -} -#endif -#ifdef _STD_STRING_COMPRESSOR -void StringCompressor::EncodeString( const std::string &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) -{ - EncodeString(input.c_str(), maxCharsToWrite, output, languageID); -} -bool StringCompressor::DecodeString( std::string *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) -{ - if (maxCharsToWrite <= 0) - { - output->clear(); - return true; - } - - char *destinationBlock; - bool out; - - - if (maxCharsToWrite < MAX_ALLOCA_STACK_ALLOCATION) - { - destinationBlock = (char*) alloca(maxCharsToWrite); - out=DecodeString(destinationBlock, maxCharsToWrite, input, languageID); - *output=destinationBlock; - } - else - - { - destinationBlock = (char*) rakMalloc_Ex( maxCharsToWrite, _FILE_AND_LINE_ ); - out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); - *output=destinationBlock; - rakFree_Ex(destinationBlock, _FILE_AND_LINE_ ); - } - - return out; -} -#endif -void StringCompressor::EncodeString( const RakString *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) -{ - EncodeString(input->C_String(), maxCharsToWrite, output, languageId); -} -bool StringCompressor::DecodeString( RakString *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) -{ - if (maxCharsToWrite <= 0) - { - output->Clear(); - return true; - } - - char *destinationBlock; - bool out; - - - if (maxCharsToWrite < MAX_ALLOCA_STACK_ALLOCATION) - { - destinationBlock = (char*) alloca(maxCharsToWrite); - out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); - *output=destinationBlock; - } - else - - { - destinationBlock = (char*) rakMalloc_Ex( maxCharsToWrite, _FILE_AND_LINE_ ); - out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); - *output=destinationBlock; - rakFree_Ex(destinationBlock, _FILE_AND_LINE_ ); - } - - return out; -} +/// \file +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "StringCompressor.h" +#include "DS_HuffmanEncodingTree.h" +#include "BitStream.h" +#include "RakString.h" +#include "RakAssert.h" +#include + +#include + + + + + +using namespace RakNet; + +StringCompressor* StringCompressor::instance=0; +int StringCompressor::referenceCount=0; + +void StringCompressor::AddReference(void) +{ + if (++referenceCount==1) + { + instance = RakNet::OP_NEW( _FILE_AND_LINE_ ); + } +} +void StringCompressor::RemoveReference(void) +{ + RakAssert(referenceCount > 0); + + if (referenceCount > 0) + { + if (--referenceCount==0) + { + RakNet::OP_DELETE(instance, _FILE_AND_LINE_); + instance=0; + } + } +} + +StringCompressor* StringCompressor::Instance(void) +{ + return instance; +} + +unsigned int englishCharacterFrequencies[ 256 ] = +{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 722, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 11084, + 58, + 63, + 1, + 0, + 31, + 0, + 317, + 64, + 64, + 44, + 0, + 695, + 62, + 980, + 266, + 69, + 67, + 56, + 7, + 73, + 3, + 14, + 2, + 69, + 1, + 167, + 9, + 1, + 2, + 25, + 94, + 0, + 195, + 139, + 34, + 96, + 48, + 103, + 56, + 125, + 653, + 21, + 5, + 23, + 64, + 85, + 44, + 34, + 7, + 92, + 76, + 147, + 12, + 14, + 57, + 15, + 39, + 15, + 1, + 1, + 1, + 2, + 3, + 0, + 3611, + 845, + 1077, + 1884, + 5870, + 841, + 1057, + 2501, + 3212, + 164, + 531, + 2019, + 1330, + 3056, + 4037, + 848, + 47, + 2586, + 2919, + 4771, + 1707, + 535, + 1106, + 152, + 1243, + 100, + 0, + 2, + 0, + 10, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +StringCompressor::StringCompressor() +{ + DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON(); + + // Make a default tree immediately, since this is used for RPC possibly from multiple threads at the same time + HuffmanEncodingTree *huffmanEncodingTree = RakNet::OP_NEW( _FILE_AND_LINE_ ); + huffmanEncodingTree->GenerateFromFrequencyTable( englishCharacterFrequencies ); + + huffmanEncodingTrees.Set(0, huffmanEncodingTree); +} +void StringCompressor::GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, uint8_t languageId ) +{ + HuffmanEncodingTree *huffmanEncodingTree; + if (huffmanEncodingTrees.Has(languageId)) + { + huffmanEncodingTree = huffmanEncodingTrees.Get(languageId); + RakNet::OP_DELETE(huffmanEncodingTree, _FILE_AND_LINE_); + } + + unsigned index; + unsigned int frequencyTable[ 256 ]; + + if ( inputLength == 0 ) + return ; + + // Zero out the frequency table + memset( frequencyTable, 0, sizeof( frequencyTable ) ); + + // Generate the frequency table from the strings + for ( index = 0; index < inputLength; index++ ) + frequencyTable[ input[ index ] ] ++; + + // Build the tree + huffmanEncodingTree = RakNet::OP_NEW( _FILE_AND_LINE_ ); + huffmanEncodingTree->GenerateFromFrequencyTable( frequencyTable ); + huffmanEncodingTrees.Set(languageId, huffmanEncodingTree); +} + +StringCompressor::~StringCompressor() +{ + for (unsigned i=0; i < huffmanEncodingTrees.Size(); i++) + RakNet::OP_DELETE(huffmanEncodingTrees[i], _FILE_AND_LINE_); +} + +void StringCompressor::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) +{ + HuffmanEncodingTree *huffmanEncodingTree; + if (huffmanEncodingTrees.Has(languageId)==false) + return; + huffmanEncodingTree=huffmanEncodingTrees.Get(languageId); + + if ( input == 0 ) + { + output->WriteCompressed( (uint32_t) 0 ); + return ; + } + + RakNet::BitStream encodedBitStream; + + uint32_t stringBitLength; + + int charsToWrite; + + if ( maxCharsToWrite<=0 || ( int ) strlen( input ) < maxCharsToWrite ) + charsToWrite = ( int ) strlen( input ); + else + charsToWrite = maxCharsToWrite - 1; + + huffmanEncodingTree->EncodeArray( ( unsigned char* ) input, charsToWrite, &encodedBitStream ); + + stringBitLength = (uint32_t) encodedBitStream.GetNumberOfBitsUsed(); + + output->WriteCompressed( stringBitLength ); + + output->WriteBits( encodedBitStream.GetData(), stringBitLength ); +} + +bool StringCompressor::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) +{ + HuffmanEncodingTree *huffmanEncodingTree; + if (huffmanEncodingTrees.Has(languageId)==false) + return false; + if (maxCharsToWrite<=0) + return false; + huffmanEncodingTree=huffmanEncodingTrees.Get(languageId); + + uint32_t stringBitLength; + int bytesInStream; + + output[ 0 ] = 0; + + if ( input->ReadCompressed( stringBitLength ) == false ) + return false; + + if ( (unsigned) input->GetNumberOfUnreadBits() < stringBitLength ) + return false; + + bytesInStream = huffmanEncodingTree->DecodeArray( input, stringBitLength, maxCharsToWrite, ( unsigned char* ) output ); + + if ( bytesInStream < maxCharsToWrite ) + output[ bytesInStream ] = 0; + else + output[ maxCharsToWrite - 1 ] = 0; + + return true; +} +#ifdef _CSTRING_COMPRESSOR +void StringCompressor::EncodeString( const CString &input, int maxCharsToWrite, RakNet::BitStream *output ) +{ + LPTSTR p = input; + EncodeString(p, maxCharsToWrite*sizeof(TCHAR), output, languageID); +} +bool StringCompressor::DecodeString( CString &output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) +{ + LPSTR p = output.GetBuffer(maxCharsToWrite*sizeof(TCHAR)); + DecodeString(p,maxCharsToWrite*sizeof(TCHAR), input, languageID); + output.ReleaseBuffer(0) + +} +#endif +#ifdef _STD_STRING_COMPRESSOR +void StringCompressor::EncodeString( const std::string &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) +{ + EncodeString(input.c_str(), maxCharsToWrite, output, languageID); +} +bool StringCompressor::DecodeString( std::string *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) +{ + if (maxCharsToWrite <= 0) + { + output->clear(); + return true; + } + + char *destinationBlock; + bool out; + + + if (maxCharsToWrite < MAX_ALLOCA_STACK_ALLOCATION) + { + destinationBlock = (char*) alloca(maxCharsToWrite); + out=DecodeString(destinationBlock, maxCharsToWrite, input, languageID); + *output=destinationBlock; + } + else + + { + destinationBlock = (char*) rakMalloc_Ex( maxCharsToWrite, _FILE_AND_LINE_ ); + out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); + *output=destinationBlock; + rakFree_Ex(destinationBlock, _FILE_AND_LINE_ ); + } + + return out; +} +#endif +void StringCompressor::EncodeString( const RakString *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId ) +{ + EncodeString(input->C_String(), maxCharsToWrite, output, languageId); +} +bool StringCompressor::DecodeString( RakString *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId ) +{ + if (maxCharsToWrite <= 0) + { + output->Clear(); + return true; + } + + char *destinationBlock; + bool out; + + + if (maxCharsToWrite < MAX_ALLOCA_STACK_ALLOCATION) + { + destinationBlock = (char*) alloca(maxCharsToWrite); + out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); + *output=destinationBlock; + } + else + + { + destinationBlock = (char*) rakMalloc_Ex( maxCharsToWrite, _FILE_AND_LINE_ ); + out=DecodeString(destinationBlock, maxCharsToWrite, input, languageId); + *output=destinationBlock; + rakFree_Ex(destinationBlock, _FILE_AND_LINE_ ); + } + + return out; +} diff --git a/src/raknet/StringCompressor.h b/src/raknet/StringCompressor.h index 6852ce8..ddb2405 100755 --- a/src/raknet/StringCompressor.h +++ b/src/raknet/StringCompressor.h @@ -1,105 +1,105 @@ -/// \file -/// \brief \b Compresses/Decompresses ASCII strings and writes/reads them to BitStream class instances. You can use this to easily serialize and deserialize your own strings. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __STRING_COMPRESSOR_H -#define __STRING_COMPRESSOR_H - -#include "Export.h" -#include "DS_Map.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" - -#ifdef _STD_STRING_COMPRESSOR -#include -#endif - -/// Forward declaration -namespace RakNet -{ - class BitStream; - class RakString; -}; - - -namespace RakNet -{ -/// Forward declarations -class HuffmanEncodingTree; - -/// \brief Writes and reads strings to and from bitstreams. -/// -/// Only works with ASCII strings. The default compression is for English. -/// You can call GenerateTreeFromStrings to compress and decompress other languages efficiently as well. -class RAK_DLL_EXPORT StringCompressor -{ -public: - - // Destructor - ~StringCompressor(); - - /// static function because only static functions can access static members - /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. - /// \return the unique instance of the StringCompressor - static StringCompressor* Instance(void); - - /// Given an array of strings, such as a chat log, generate the optimal encoding tree for it. - /// This function is optional and if it is not called a default tree will be used instead. - /// \param[in] input An array of bytes which should point to text. - /// \param[in] inputLength Length of \a input - /// \param[in] languageID An identifier for the language / string table to generate the tree for. English is automatically created with ID 0 in the constructor. - void GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, uint8_t languageId ); - - /// Writes input to output, compressed. Takes care of the null terminator for you. - /// \param[in] input Pointer to an ASCII string - /// \param[in] maxCharsToWrite The max number of bytes to write of \a input. Use 0 to mean no limit. - /// \param[out] output The bitstream to write the compressed string to - /// \param[in] languageID Which language to use - void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); - - /// Writes input to output, uncompressed. Takes care of the null terminator for you. - /// \param[out] output A block of bytes to receive the output - /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. - /// \param[in] input The bitstream containing the compressed string - /// \param[in] languageID Which language to use - bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); - -#ifdef _CSTRING_COMPRESSOR - void EncodeString( const CString &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); - bool DecodeString( CString &output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); -#endif - -#ifdef _STD_STRING_COMPRESSOR - void EncodeString( const std::string &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); - bool DecodeString( std::string *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); -#endif - - void EncodeString( const RakNet::RakString *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); - bool DecodeString( RakNet::RakString *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); - - /// Used so I can allocate and deallocate this singleton at runtime - static void AddReference(void); - - /// Used so I can allocate and deallocate this singleton at runtime - static void RemoveReference(void); - - StringCompressor(); - -private: - - /// Singleton instance - static StringCompressor *instance; - - /// Pointer to the huffman encoding trees. - DataStructures::Map huffmanEncodingTrees; - - static int referenceCount; -}; - -} // namespace RakNet - -#endif +/// \file +/// \brief \b Compresses/Decompresses ASCII strings and writes/reads them to BitStream class instances. You can use this to easily serialize and deserialize your own strings. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __STRING_COMPRESSOR_H +#define __STRING_COMPRESSOR_H + +#include "Export.h" +#include "DS_Map.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" + +#ifdef _STD_STRING_COMPRESSOR +#include +#endif + +/// Forward declaration +namespace RakNet +{ + class BitStream; + class RakString; +}; + + +namespace RakNet +{ +/// Forward declarations +class HuffmanEncodingTree; + +/// \brief Writes and reads strings to and from bitstreams. +/// +/// Only works with ASCII strings. The default compression is for English. +/// You can call GenerateTreeFromStrings to compress and decompress other languages efficiently as well. +class RAK_DLL_EXPORT StringCompressor +{ +public: + + // Destructor + ~StringCompressor(); + + /// static function because only static functions can access static members + /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. + /// \return the unique instance of the StringCompressor + static StringCompressor* Instance(void); + + /// Given an array of strings, such as a chat log, generate the optimal encoding tree for it. + /// This function is optional and if it is not called a default tree will be used instead. + /// \param[in] input An array of bytes which should point to text. + /// \param[in] inputLength Length of \a input + /// \param[in] languageID An identifier for the language / string table to generate the tree for. English is automatically created with ID 0 in the constructor. + void GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, uint8_t languageId ); + + /// Writes input to output, compressed. Takes care of the null terminator for you. + /// \param[in] input Pointer to an ASCII string + /// \param[in] maxCharsToWrite The max number of bytes to write of \a input. Use 0 to mean no limit. + /// \param[out] output The bitstream to write the compressed string to + /// \param[in] languageID Which language to use + void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); + + /// Writes input to output, uncompressed. Takes care of the null terminator for you. + /// \param[out] output A block of bytes to receive the output + /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. + /// \param[in] input The bitstream containing the compressed string + /// \param[in] languageID Which language to use + bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); + +#ifdef _CSTRING_COMPRESSOR + void EncodeString( const CString &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); + bool DecodeString( CString &output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); +#endif + +#ifdef _STD_STRING_COMPRESSOR + void EncodeString( const std::string &input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); + bool DecodeString( std::string *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); +#endif + + void EncodeString( const RakNet::RakString *input, int maxCharsToWrite, RakNet::BitStream *output, uint8_t languageId=0 ); + bool DecodeString( RakNet::RakString *output, int maxCharsToWrite, RakNet::BitStream *input, uint8_t languageId=0 ); + + /// Used so I can allocate and deallocate this singleton at runtime + static void AddReference(void); + + /// Used so I can allocate and deallocate this singleton at runtime + static void RemoveReference(void); + + StringCompressor(); + +private: + + /// Singleton instance + static StringCompressor *instance; + + /// Pointer to the huffman encoding trees. + DataStructures::Map huffmanEncodingTrees; + + static int referenceCount; +}; + +} // namespace RakNet + +#endif diff --git a/src/raknet/StringTable.cpp b/src/raknet/StringTable.cpp index 48665d8..41a18c4 100755 --- a/src/raknet/StringTable.cpp +++ b/src/raknet/StringTable.cpp @@ -1,140 +1,140 @@ -#include "StringTable.h" -#include -#include "RakAssert.h" -#include -#include "BitStream.h" -#include "StringCompressor.h" -using namespace RakNet; - -StringTable* StringTable::instance=0; -int StringTable::referenceCount=0; - - -int RakNet::StrAndBoolComp( char *const &key, const StrAndBool &data ) -{ - return strcmp(key,(const char*)data.str); -} - -StringTable::StringTable() -{ - -} - -StringTable::~StringTable() -{ - unsigned i; - for (i=0; i < orderedStringList.Size(); i++) - { - if (orderedStringList[i].b) - rakFree_Ex(orderedStringList[i].str, _FILE_AND_LINE_ ); - } -} - -void StringTable::AddReference(void) -{ - if (++referenceCount==1) - { - instance = RakNet::OP_NEW( _FILE_AND_LINE_ ); - } -} -void StringTable::RemoveReference(void) -{ - RakAssert(referenceCount > 0); - - if (referenceCount > 0) - { - if (--referenceCount==0) - { - RakNet::OP_DELETE(instance, _FILE_AND_LINE_); - instance=0; - } - } -} - -StringTable* StringTable::Instance(void) -{ - return instance; -} - -void StringTable::AddString(const char *str, bool copyString) -{ - StrAndBool sab; - sab.b=copyString; - if (copyString) - { - sab.str = (char*) rakMalloc_Ex( strlen(str)+1, _FILE_AND_LINE_ ); - strcpy(sab.str, str); - } - else - { - sab.str=(char*)str; - } - - // If it asserts inside here you are adding duplicate strings. - orderedStringList.Insert(sab.str,sab, true, _FILE_AND_LINE_); - - // If this assert hits you need to increase the range of StringTableType - RakAssert(orderedStringList.Size() < (StringTableType)-1); - -} -void StringTable::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output ) -{ - unsigned index; - bool objectExists; - // This is fast because the list is kept ordered. - index=orderedStringList.GetIndexFromKey((char*)input, &objectExists); - if (objectExists) - { - output->Write(true); - output->Write((StringTableType)index); - } - else - { - LogStringNotFound(input); - output->Write(false); - StringCompressor::Instance()->EncodeString(input, maxCharsToWrite, output); - } -} - -bool StringTable::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input ) -{ - bool hasIndex=false; - RakAssert(maxCharsToWrite>0); - - if (maxCharsToWrite==0) - return false; - if (!input->Read(hasIndex)) - return false; - if (hasIndex==false) - { - StringCompressor::Instance()->DecodeString(output, maxCharsToWrite, input); - } - else - { - StringTableType index; - if (!input->Read(index)) - return false; - if (index >= orderedStringList.Size()) - { -#ifdef _DEBUG - // Critical error - got a string index out of range, which means AddString was called more times on the remote system than on this system. - // All systems must call AddString the same number of types, with the same strings in the same order. - RakAssert(0); -#endif - return false; - } - - strncpy(output, orderedStringList[index].str, maxCharsToWrite); - output[maxCharsToWrite-1]=0; - } - - return true; -} -void StringTable::LogStringNotFound(const char *strName) -{ - (void) strName; - -#ifdef _DEBUG - RAKNET_DEBUG_PRINTF("Efficiency Warning! Unregistered String %s sent to StringTable.\n", strName); -#endif -} +#include "StringTable.h" +#include +#include "RakAssert.h" +#include +#include "BitStream.h" +#include "StringCompressor.h" +using namespace RakNet; + +StringTable* StringTable::instance=0; +int StringTable::referenceCount=0; + + +int RakNet::StrAndBoolComp( char *const &key, const StrAndBool &data ) +{ + return strcmp(key,(const char*)data.str); +} + +StringTable::StringTable() +{ + +} + +StringTable::~StringTable() +{ + unsigned i; + for (i=0; i < orderedStringList.Size(); i++) + { + if (orderedStringList[i].b) + rakFree_Ex(orderedStringList[i].str, _FILE_AND_LINE_ ); + } +} + +void StringTable::AddReference(void) +{ + if (++referenceCount==1) + { + instance = RakNet::OP_NEW( _FILE_AND_LINE_ ); + } +} +void StringTable::RemoveReference(void) +{ + RakAssert(referenceCount > 0); + + if (referenceCount > 0) + { + if (--referenceCount==0) + { + RakNet::OP_DELETE(instance, _FILE_AND_LINE_); + instance=0; + } + } +} + +StringTable* StringTable::Instance(void) +{ + return instance; +} + +void StringTable::AddString(const char *str, bool copyString) +{ + StrAndBool sab; + sab.b=copyString; + if (copyString) + { + sab.str = (char*) rakMalloc_Ex( strlen(str)+1, _FILE_AND_LINE_ ); + strcpy(sab.str, str); + } + else + { + sab.str=(char*)str; + } + + // If it asserts inside here you are adding duplicate strings. + orderedStringList.Insert(sab.str,sab, true, _FILE_AND_LINE_); + + // If this assert hits you need to increase the range of StringTableType + RakAssert(orderedStringList.Size() < (StringTableType)-1); + +} +void StringTable::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output ) +{ + unsigned index; + bool objectExists; + // This is fast because the list is kept ordered. + index=orderedStringList.GetIndexFromKey((char*)input, &objectExists); + if (objectExists) + { + output->Write(true); + output->Write((StringTableType)index); + } + else + { + LogStringNotFound(input); + output->Write(false); + StringCompressor::Instance()->EncodeString(input, maxCharsToWrite, output); + } +} + +bool StringTable::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input ) +{ + bool hasIndex=false; + RakAssert(maxCharsToWrite>0); + + if (maxCharsToWrite==0) + return false; + if (!input->Read(hasIndex)) + return false; + if (hasIndex==false) + { + StringCompressor::Instance()->DecodeString(output, maxCharsToWrite, input); + } + else + { + StringTableType index; + if (!input->Read(index)) + return false; + if (index >= orderedStringList.Size()) + { +#ifdef _DEBUG + // Critical error - got a string index out of range, which means AddString was called more times on the remote system than on this system. + // All systems must call AddString the same number of types, with the same strings in the same order. + RakAssert(0); +#endif + return false; + } + + strncpy(output, orderedStringList[index].str, maxCharsToWrite); + output[maxCharsToWrite-1]=0; + } + + return true; +} +void StringTable::LogStringNotFound(const char *strName) +{ + (void) strName; + +#ifdef _DEBUG + RAKNET_DEBUG_PRINTF("Efficiency Warning! Unregistered String %s sent to StringTable.\n", strName); +#endif +} diff --git a/src/raknet/StringTable.h b/src/raknet/StringTable.h index f886bb7..8e7b3b9 100755 --- a/src/raknet/StringTable.h +++ b/src/raknet/StringTable.h @@ -1,96 +1,96 @@ -/// \file -/// \brief A simple class to encode and decode known strings based on a lookup table. Similar to the StringCompressor class. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __STRING_TABLE_H -#define __STRING_TABLE_H - -#include "DS_OrderedList.h" -#include "Export.h" -#include "RakMemoryOverride.h" - -/// Forward declaration -namespace RakNet -{ - class BitStream; -}; - -/// StringTableType should be the smallest type possible, or else it defeats the purpose of the StringTable class, which is to save bandwidth. -typedef unsigned char StringTableType; - -/// The string plus a bool telling us if this string was copied or not. -struct StrAndBool -{ - char *str; - bool b; -}; - -namespace RakNet -{ - int RAK_DLL_EXPORT StrAndBoolComp( char *const &key, const StrAndBool &data ); - - /// \details This is an even more efficient alternative to StringCompressor in that it writes a single byte from a lookup table and only does compression.
          - /// if the string does not already exist in the table.
          - /// All string tables must match on all systems - hence you must add all the strings in the same order on all systems.
          - /// Furthermore, this must be done before sending packets that use this class, since the strings are ordered for fast lookup. Adding after that time would mess up all the indices so don't do it.
          - /// Don't use this class to write strings which were not previously registered with AddString, since you just waste bandwidth then. Use StringCompressor instead. - /// \brief Writes a string index, instead of the whole string - class RAK_DLL_EXPORT StringTable - { - public: - - // Destructor - ~StringTable(); - - /// static function because only static functions can access static members - /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. - /// \return the unique instance of the StringTable - static StringTable* Instance(void); - - /// Add a string to the string table. - /// \param[in] str The string to add to the string table - /// \param[in] copyString true to make a copy of the passed string (takes more memory), false to not do so (if your string is in static memory). - void AddString(const char *str, bool copyString); - - /// Writes input to output, compressed. Takes care of the null terminator for you. - /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. - /// \param[in] input Pointer to an ASCII string - /// \param[in] maxCharsToWrite The size of \a input - /// \param[out] output The bitstream to write the compressed string to - void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output ); - - /// Writes input to output, uncompressed. Takes care of the null terminator for you. - /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. - /// \param[out] output A block of bytes to receive the output - /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. - /// \param[in] input The bitstream containing the compressed string - bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input ); - - /// Used so I can allocate and deallocate this singleton at runtime - static void AddReference(void); - - /// Used so I can allocate and deallocate this singleton at runtime - static void RemoveReference(void); - - /// Private Constructor - StringTable(); - - protected: - /// Called when you mess up and send a string using this class that was not registered with AddString - /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. - void LogStringNotFound(const char *strName); - - /// Singleton instance - static StringTable *instance; - static int referenceCount; - - DataStructures::OrderedList orderedStringList; - }; -} - - -#endif +/// \file +/// \brief A simple class to encode and decode known strings based on a lookup table. Similar to the StringCompressor class. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __STRING_TABLE_H +#define __STRING_TABLE_H + +#include "DS_OrderedList.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +/// Forward declaration +namespace RakNet +{ + class BitStream; +}; + +/// StringTableType should be the smallest type possible, or else it defeats the purpose of the StringTable class, which is to save bandwidth. +typedef unsigned char StringTableType; + +/// The string plus a bool telling us if this string was copied or not. +struct StrAndBool +{ + char *str; + bool b; +}; + +namespace RakNet +{ + int RAK_DLL_EXPORT StrAndBoolComp( char *const &key, const StrAndBool &data ); + + /// \details This is an even more efficient alternative to StringCompressor in that it writes a single byte from a lookup table and only does compression.
          + /// if the string does not already exist in the table.
          + /// All string tables must match on all systems - hence you must add all the strings in the same order on all systems.
          + /// Furthermore, this must be done before sending packets that use this class, since the strings are ordered for fast lookup. Adding after that time would mess up all the indices so don't do it.
          + /// Don't use this class to write strings which were not previously registered with AddString, since you just waste bandwidth then. Use StringCompressor instead. + /// \brief Writes a string index, instead of the whole string + class RAK_DLL_EXPORT StringTable + { + public: + + // Destructor + ~StringTable(); + + /// static function because only static functions can access static members + /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. + /// \return the unique instance of the StringTable + static StringTable* Instance(void); + + /// Add a string to the string table. + /// \param[in] str The string to add to the string table + /// \param[in] copyString true to make a copy of the passed string (takes more memory), false to not do so (if your string is in static memory). + void AddString(const char *str, bool copyString); + + /// Writes input to output, compressed. Takes care of the null terminator for you. + /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. + /// \param[in] input Pointer to an ASCII string + /// \param[in] maxCharsToWrite The size of \a input + /// \param[out] output The bitstream to write the compressed string to + void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output ); + + /// Writes input to output, uncompressed. Takes care of the null terminator for you. + /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. + /// \param[out] output A block of bytes to receive the output + /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. + /// \param[in] input The bitstream containing the compressed string + bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input ); + + /// Used so I can allocate and deallocate this singleton at runtime + static void AddReference(void); + + /// Used so I can allocate and deallocate this singleton at runtime + static void RemoveReference(void); + + /// Private Constructor + StringTable(); + + protected: + /// Called when you mess up and send a string using this class that was not registered with AddString + /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. + void LogStringNotFound(const char *strName); + + /// Singleton instance + static StringTable *instance; + static int referenceCount; + + DataStructures::OrderedList orderedStringList; + }; +} + + +#endif diff --git a/src/raknet/SuperFastHash.cpp b/src/raknet/SuperFastHash.cpp index f5626e9..f9c22b6 100755 --- a/src/raknet/SuperFastHash.cpp +++ b/src/raknet/SuperFastHash.cpp @@ -1,119 +1,119 @@ -#include "SuperFastHash.h" -#include "NativeTypes.h" -#include - -#if !defined(_WIN32) -#include -#endif - -#undef get16bits - -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#else -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif - -static const int INCREMENTAL_READ_BLOCK=65536; - -uint32_t SuperFastHash (const char * data, int length) -{ - // All this is necessary or the hash does not match SuperFastHashIncremental - int bytesRemaining=length; - unsigned int lastHash = length; - int offset=0; - while (bytesRemaining>=INCREMENTAL_READ_BLOCK) - { - lastHash=SuperFastHashIncremental (data+offset, INCREMENTAL_READ_BLOCK, lastHash ); - bytesRemaining-=INCREMENTAL_READ_BLOCK; - offset+=INCREMENTAL_READ_BLOCK; - } - if (bytesRemaining>0) - { - lastHash=SuperFastHashIncremental (data+offset, bytesRemaining, lastHash ); - } - return lastHash; - -// return SuperFastHashIncremental(data,len,len); -} -uint32_t SuperFastHashIncremental (const char * data, int len, unsigned int lastHash ) -{ - uint32_t hash = (uint32_t) lastHash; - uint32_t tmp; - int rem; - - if (len <= 0 || data == NULL) return 0; - - rem = len & 3; - len >>= 2; - - /* Main loop */ - for (;len > 0; len--) { - hash += get16bits (data); - tmp = (get16bits (data+2) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - data += 2*sizeof (uint16_t); - hash += hash >> 11; - } - - /* Handle end cases */ - switch (rem) { - case 3: hash += get16bits (data); - hash ^= hash << 16; - hash ^= data[sizeof (uint16_t)] << 18; - hash += hash >> 11; - break; - case 2: hash += get16bits (data); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: hash += *data; - hash ^= hash << 10; - hash += hash >> 1; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return (uint32_t) hash; - -} - -uint32_t SuperFastHashFile (const char * filename) -{ - FILE *fp = fopen(filename, "rb"); - if (fp==0) - return 0; - uint32_t hash = SuperFastHashFilePtr(fp); - fclose(fp); - return hash; -} - -uint32_t SuperFastHashFilePtr (FILE *fp) -{ - fseek(fp, 0, SEEK_END); - int length = ftell(fp); - fseek(fp, 0, SEEK_SET); - int bytesRemaining=length; - unsigned int lastHash = length; - char readBlock[INCREMENTAL_READ_BLOCK]; - while (bytesRemaining>=(int) sizeof(readBlock)) - { - fread(readBlock, sizeof(readBlock), 1, fp); - lastHash=SuperFastHashIncremental (readBlock, (int) sizeof(readBlock), lastHash ); - bytesRemaining-=(int) sizeof(readBlock); - } - if (bytesRemaining>0) - { - fread(readBlock, bytesRemaining, 1, fp); - lastHash=SuperFastHashIncremental (readBlock, bytesRemaining, lastHash ); - } - return lastHash; -} +#include "SuperFastHash.h" +#include "NativeTypes.h" +#include + +#if !defined(_WIN32) +#include +#endif + +#undef get16bits + +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#else +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif + +static const int INCREMENTAL_READ_BLOCK=65536; + +uint32_t SuperFastHash (const char * data, int length) +{ + // All this is necessary or the hash does not match SuperFastHashIncremental + int bytesRemaining=length; + unsigned int lastHash = length; + int offset=0; + while (bytesRemaining>=INCREMENTAL_READ_BLOCK) + { + lastHash=SuperFastHashIncremental (data+offset, INCREMENTAL_READ_BLOCK, lastHash ); + bytesRemaining-=INCREMENTAL_READ_BLOCK; + offset+=INCREMENTAL_READ_BLOCK; + } + if (bytesRemaining>0) + { + lastHash=SuperFastHashIncremental (data+offset, bytesRemaining, lastHash ); + } + return lastHash; + +// return SuperFastHashIncremental(data,len,len); +} +uint32_t SuperFastHashIncremental (const char * data, int len, unsigned int lastHash ) +{ + uint32_t hash = (uint32_t) lastHash; + uint32_t tmp; + int rem; + + if (len <= 0 || data == NULL) return 0; + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (;len > 0; len--) { + hash += get16bits (data); + tmp = (get16bits (data+2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2*sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: hash += get16bits (data); + hash ^= hash << 16; + hash ^= data[sizeof (uint16_t)] << 18; + hash += hash >> 11; + break; + case 2: hash += get16bits (data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += *data; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return (uint32_t) hash; + +} + +uint32_t SuperFastHashFile (const char * filename) +{ + FILE *fp = fopen(filename, "rb"); + if (fp==0) + return 0; + uint32_t hash = SuperFastHashFilePtr(fp); + fclose(fp); + return hash; +} + +uint32_t SuperFastHashFilePtr (FILE *fp) +{ + fseek(fp, 0, SEEK_END); + int length = ftell(fp); + fseek(fp, 0, SEEK_SET); + int bytesRemaining=length; + unsigned int lastHash = length; + char readBlock[INCREMENTAL_READ_BLOCK]; + while (bytesRemaining>=(int) sizeof(readBlock)) + { + fread(readBlock, sizeof(readBlock), 1, fp); + lastHash=SuperFastHashIncremental (readBlock, (int) sizeof(readBlock), lastHash ); + bytesRemaining-=(int) sizeof(readBlock); + } + if (bytesRemaining>0) + { + fread(readBlock, bytesRemaining, 1, fp); + lastHash=SuperFastHashIncremental (readBlock, bytesRemaining, lastHash ); + } + return lastHash; +} diff --git a/src/raknet/SuperFastHash.h b/src/raknet/SuperFastHash.h index fd8463c..acf91c4 100755 --- a/src/raknet/SuperFastHash.h +++ b/src/raknet/SuperFastHash.h @@ -1,17 +1,17 @@ -#ifndef __SUPER_FAST_HASH_H -#define __SUPER_FAST_HASH_H - -#include -#include "NativeTypes.h" - -// From http://www.azillionmonkeys.com/qed/hash.html -// Author of main code is Paul Hsieh -// I just added some convenience functions -// Also note http://burtleburtle.net/bob/hash/doobs.html, which shows that this is 20% faster than the one on that page but has more collisions - -uint32_t SuperFastHash (const char * data, int length); -uint32_t SuperFastHashIncremental (const char * data, int len, unsigned int lastHash ); -uint32_t SuperFastHashFile (const char * filename); -uint32_t SuperFastHashFilePtr (FILE *fp); - -#endif +#ifndef __SUPER_FAST_HASH_H +#define __SUPER_FAST_HASH_H + +#include +#include "NativeTypes.h" + +// From http://www.azillionmonkeys.com/qed/hash.html +// Author of main code is Paul Hsieh +// I just added some convenience functions +// Also note http://burtleburtle.net/bob/hash/doobs.html, which shows that this is 20% faster than the one on that page but has more collisions + +uint32_t SuperFastHash (const char * data, int length); +uint32_t SuperFastHashIncremental (const char * data, int len, unsigned int lastHash ); +uint32_t SuperFastHashFile (const char * filename); +uint32_t SuperFastHashFilePtr (FILE *fp); + +#endif diff --git a/src/raknet/TCPInterface.cpp b/src/raknet/TCPInterface.cpp index 78826af..bb4a847 100755 --- a/src/raknet/TCPInterface.cpp +++ b/src/raknet/TCPInterface.cpp @@ -1,1218 +1,1218 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TCPInterface==1 - -/// \file -/// \brief A simple TCP based server allowing sends and receives. Can be connected to by a telnet client. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "TCPInterface.h" -#ifdef _WIN32 -typedef int socklen_t; - - -#else -#include -#include -#include -#endif -#include -#include "RakAssert.h" -#include -#include "RakAssert.h" -#include "RakSleep.h" -#include "StringCompressor.h" -#include "StringTable.h" -#include "Itoa.h" -#include "SocketLayer.h" -#include "SocketDefines.h" - -#ifdef _DO_PRINTF -#endif - -#ifdef _WIN32 -#include "WSAStartupSingleton.h" -#endif -namespace RakNet -{ -RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop); -RAK_THREAD_DECLARATION(ConnectionAttemptLoop); -} -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(TCPInterface,TCPInterface); - -TCPInterface::TCPInterface() -{ - isStarted=false; - threadRunning=false; - listenSocket=(SOCKET) -1; - remoteClients=0; - remoteClientsLength=0; - - StringCompressor::AddReference(); - RakNet::StringTable::AddReference(); - -#if OPEN_SSL_CLIENT_SUPPORT==1 - ctx=0; - meth=0; -#endif - -#ifdef _WIN32 - WSAStartupSingleton::AddRef(); -#endif -} -TCPInterface::~TCPInterface() -{ - Stop(); -#ifdef _WIN32 - WSAStartupSingleton::Deref(); -#endif - - RakNet::OP_DELETE_ARRAY(remoteClients,_FILE_AND_LINE_); - - StringCompressor::RemoveReference(); - RakNet::StringTable::RemoveReference(); -} -bool TCPInterface::Start(unsigned short port, unsigned short maxIncomingConnections, unsigned short maxConnections, int _threadPriority, unsigned short socketFamily) -{ - (void) socketFamily; - - if (isStarted) - return false; - - threadPriority=_threadPriority; - - if (threadPriority==-99999) - { - - -#if defined(_WIN32) - threadPriority=0; - - -#else - threadPriority=1000; -#endif - } - - isStarted=true; - if (maxConnections==0) - maxConnections=maxIncomingConnections; - if (maxConnections==0) - maxConnections=1; - remoteClientsLength=maxConnections; - remoteClients=RakNet::OP_NEW_ARRAY(maxConnections,_FILE_AND_LINE_); - - -#if RAKNET_SUPPORT_IPV6!=1 - if (maxIncomingConnections>0) - { - listenSocket = socket__(AF_INET, SOCK_STREAM, 0); - if ((int)listenSocket ==-1) - return false; - - struct sockaddr_in serverAddress; - memset(&serverAddress,0,sizeof(sockaddr_in)); - serverAddress.sin_family = AF_INET; - - serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); - - serverAddress.sin_port = htons(port); - - if (bind__(listenSocket,(struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0) - return false; - - listen__(listenSocket, maxIncomingConnections); - } -#else - listenSocket=INVALID_SOCKET; - if (maxIncomingConnections>0) - { - struct addrinfo hints; - memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty - hints.ai_family = socketFamily; // don't care IPv4 or IPv6 - hints.ai_socktype = SOCK_STREAM; // TCP sockets - hints.ai_flags = AI_PASSIVE; // fill in my IP for me - struct addrinfo *servinfo=0, *aip; // will point to the results - char portStr[32]; - Itoa(port,portStr,10); - - getaddrinfo(0, portStr, &hints, &servinfo); - for (aip = servinfo; aip != NULL; aip = aip->ai_next) - { - // Open socket. The address type depends on what - // getaddrinfo() gave us. - listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); - if (listenSocket != INVALID_SOCKET) - { - int ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); - if (ret>=0) - { - break; - } - else - { - closesocket__(listenSocket); - listenSocket=INVALID_SOCKET; - } - } - } - - if (listenSocket==INVALID_SOCKET) - return false; - - listen__(listenSocket, maxIncomingConnections); - } -#endif // #if RAKNET_SUPPORT_IPV6!=1 - - - // Start the update thread - int errorCode; - - - - - - errorCode = RakNet::RakThread::Create(UpdateTCPInterfaceLoop, this, threadPriority); - - - if (errorCode!=0) - return false; - - while (threadRunning==false) - RakSleep(0); - - return true; -} -void TCPInterface::Stop(void) -{ - if (isStarted==false) - return; - - unsigned i; -#if OPEN_SSL_CLIENT_SUPPORT==1 - for (i=0; i < remoteClientsLength; i++) - remoteClients[i].DisconnectSSL(); -#endif - - isStarted=false; - - if (listenSocket!=(SOCKET) -1) - { -#ifdef _WIN32 - shutdown__(listenSocket, SD_BOTH); - -#else - shutdown__(listenSocket, SHUT_RDWR); -#endif - closesocket__(listenSocket); - listenSocket=(SOCKET) -1; - } - - // Abort waiting connect calls - blockingSocketListMutex.Lock(); - for (i=0; i < blockingSocketList.Size(); i++) - { - closesocket__(blockingSocketList[i]); - } - blockingSocketListMutex.Unlock(); - - // Wait for the thread to stop - while ( threadRunning ) - RakSleep(15); - - RakSleep(100); - - // Stuff from here on to the end of the function is not threadsafe - for (i=0; i < (unsigned int) remoteClientsLength; i++) - { - closesocket__(remoteClients[i].socket); -#if OPEN_SSL_CLIENT_SUPPORT==1 - remoteClients[i].FreeSSL(); -#endif - } - remoteClientsLength=0; - RakNet::OP_DELETE_ARRAY(remoteClients,_FILE_AND_LINE_); - remoteClients=0; - - incomingMessages.Clear(_FILE_AND_LINE_); - newIncomingConnections.Clear(_FILE_AND_LINE_); - newRemoteClients.Clear(_FILE_AND_LINE_); - lostConnections.Clear(_FILE_AND_LINE_); - requestedCloseConnections.Clear(_FILE_AND_LINE_); - failedConnectionAttempts.Clear(_FILE_AND_LINE_); - completedConnectionAttempts.Clear(_FILE_AND_LINE_); - failedConnectionAttempts.Clear(_FILE_AND_LINE_); - for (i=0; i < headPush.Size(); i++) - DeallocatePacket(headPush[i]); - headPush.Clear(_FILE_AND_LINE_); - for (i=0; i < tailPush.Size(); i++) - DeallocatePacket(tailPush[i]); - tailPush.Clear(_FILE_AND_LINE_); - -#if OPEN_SSL_CLIENT_SUPPORT==1 - SSL_CTX_free (ctx); - startSSL.Clear(_FILE_AND_LINE_); - activeSSLConnections.Clear(false, _FILE_AND_LINE_); -#endif - - - - - -} -SystemAddress TCPInterface::Connect(const char* host, unsigned short remotePort, bool block, unsigned short socketFamily) -{ - if (threadRunning==false) - return UNASSIGNED_SYSTEM_ADDRESS; - - int newRemoteClientIndex=-1; - for (newRemoteClientIndex=0; newRemoteClientIndex < remoteClientsLength; newRemoteClientIndex++) - { - remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); - if (remoteClients[newRemoteClientIndex].isActive==false) - { - remoteClients[newRemoteClientIndex].SetActive(true); - remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - break; - } - remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - } - if (newRemoteClientIndex==-1) - return UNASSIGNED_SYSTEM_ADDRESS; - - if (block) - { - SystemAddress systemAddress; - systemAddress.FromString(host); - systemAddress.SetPort(remotePort); - systemAddress.systemIndex=(SystemIndex) newRemoteClientIndex; - char buffout[128]; - systemAddress.ToString(false,buffout); - - SOCKET sockfd = SocketConnect(buffout, remotePort, socketFamily); - if (sockfd==(SOCKET)-1) - { - remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); - remoteClients[newRemoteClientIndex].SetActive(false); - remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - - failedConnectionAttemptMutex.Lock(); - failedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); - failedConnectionAttemptMutex.Unlock(); - - return UNASSIGNED_SYSTEM_ADDRESS; - } - - remoteClients[newRemoteClientIndex].socket=sockfd; - remoteClients[newRemoteClientIndex].systemAddress=systemAddress; - - completedConnectionAttemptMutex.Lock(); - completedConnectionAttempts.Push(remoteClients[newRemoteClientIndex].systemAddress, _FILE_AND_LINE_ ); - completedConnectionAttemptMutex.Unlock(); - - return remoteClients[newRemoteClientIndex].systemAddress; - } - else - { - ThisPtrPlusSysAddr *s = RakNet::OP_NEW( _FILE_AND_LINE_ ); - s->systemAddress.FromStringExplicitPort(host,remotePort); - s->systemAddress.systemIndex=(SystemIndex) newRemoteClientIndex; - s->tcpInterface=this; - s->socketFamily=socketFamily; - - // Start the connection thread - int errorCode; - - - - - errorCode = RakNet::RakThread::Create(ConnectionAttemptLoop, s, threadPriority); - - if (errorCode!=0) - { - RakNet::OP_DELETE(s, _FILE_AND_LINE_); - failedConnectionAttempts.Push(s->systemAddress, _FILE_AND_LINE_ ); - } - return UNASSIGNED_SYSTEM_ADDRESS; - } -} -#if OPEN_SSL_CLIENT_SUPPORT==1 -void TCPInterface::StartSSLClient(SystemAddress systemAddress) -{ - if (ctx==0) - { - sharedSslMutex.Lock(); - SSLeay_add_ssl_algorithms(); - meth = (SSL_METHOD*) SSLv2_client_method(); - SSL_load_error_strings(); - ctx = SSL_CTX_new (meth); - RakAssert(ctx!=0); - sharedSslMutex.Unlock(); - } - - SystemAddress *id = startSSL.Allocate( _FILE_AND_LINE_ ); - *id=systemAddress; - startSSL.Push(id); - unsigned index = activeSSLConnections.GetIndexOf(systemAddress); - if (index==(unsigned)-1) - activeSSLConnections.Insert(systemAddress,_FILE_AND_LINE_); -} -bool TCPInterface::IsSSLActive(SystemAddress systemAddress) -{ - return activeSSLConnections.GetIndexOf(systemAddress)!=-1; -} -#endif -void TCPInterface::Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ) -{ - SendList( &data, &length, 1, systemAddress,broadcast ); -} -bool TCPInterface::SendList( const char **data, const unsigned int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ) -{ - if (isStarted==false) - return false; - if (data==0) - return false; - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS && broadcast==false) - return false; - unsigned int totalLength=0; - int i; - for (i=0; i < numParameters; i++) - { - if (lengths[i]>0) - totalLength+=lengths[i]; - } - if (totalLength==0) - return false; - - if (broadcast) - { - // Send to all, possible exception system - for (i=0; i < remoteClientsLength; i++) - { - if (remoteClients[i].systemAddress!=systemAddress) - { - remoteClients[i].SendOrBuffer(data, lengths, numParameters); - } - } - } - else - { - // Send to this player - if (systemAddress.systemIndexdeleteData) - { - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - incomingMessages.Deallocate(packet, _FILE_AND_LINE_); - } - else - { - // Came from userspace AllocatePacket - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - RakNet::OP_DELETE(packet, _FILE_AND_LINE_); - } -} -Packet* TCPInterface::AllocatePacket(unsigned dataSize) -{ - Packet*p = RakNet::OP_NEW(_FILE_AND_LINE_); - p->data=(unsigned char*) rakMalloc_Ex(dataSize,_FILE_AND_LINE_); - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=false; - p->guid=UNASSIGNED_RAKNET_GUID; - p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; - p->systemAddress.systemIndex=(SystemIndex)-1; - return p; -} -void TCPInterface::PushBackPacket( Packet *packet, bool pushAtHead ) -{ - if (pushAtHead) - headPush.Push(packet, _FILE_AND_LINE_ ); - else - tailPush.Push(packet, _FILE_AND_LINE_ ); -} -bool TCPInterface::WasStarted(void) const -{ - return threadRunning==true; -} -int TCPInterface::Base64Encoding(const char *inputData, int dataLength, char *outputData) -{ - // http://en.wikipedia.org/wiki/Base64 - - int outputOffset, charCount; - int write3Count; - outputOffset=0; - charCount=0; - int j; - - write3Count=dataLength/3; - for (j=0; j < write3Count; j++) - { - // 6 leftmost bits from first byte, shifted to bits 7,8 are 0 - outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // 4 low bits from the second byte and the two high bits from the third byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[((inputData[j*3+1] << 2) | (inputData[j*3+2] >> 6)) & 63]; // Third 6 bits - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Last 6 bits from the third byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[inputData[j*3+2] & 63]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - } - - if (dataLength % 3==1) - { - // One input byte remaining - outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Pad with two equals - outputData[outputOffset++]='='; - outputData[outputOffset++]='='; - } - else if (dataLength % 3==2) - { - // Two input bytes remaining - - // 6 leftmost bits from first byte, shifted to bits 7,8 are 0 - outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 - outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // 4 low bits from the second byte, followed by 00 - outputData[outputOffset++]=Base64Map()[(inputData[j*3+1] << 2) & 63]; // Third 6 bits - if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} - - // Pad with one equal - outputData[outputOffset++]='='; - //outputData[outputOffset++]='='; - } - - // Append \r\n - outputData[outputOffset++]='\r'; - outputData[outputOffset++]='\n'; - outputData[outputOffset]=0; - - return outputOffset; -} -SystemAddress TCPInterface::HasCompletedConnectionAttempt(void) -{ - SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS; - completedConnectionAttemptMutex.Lock(); - if (completedConnectionAttempts.IsEmpty()==false) - sysAddr=completedConnectionAttempts.Pop(); - completedConnectionAttemptMutex.Unlock(); - return sysAddr; -} -SystemAddress TCPInterface::HasFailedConnectionAttempt(void) -{ - SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS; - failedConnectionAttemptMutex.Lock(); - if (failedConnectionAttempts.IsEmpty()==false) - sysAddr=failedConnectionAttempts.Pop(); - failedConnectionAttemptMutex.Unlock(); - return sysAddr; -} -SystemAddress TCPInterface::HasNewIncomingConnection(void) -{ - SystemAddress *out, out2; - out = newIncomingConnections.PopInaccurate(); - if (out) - { - out2=*out; - newIncomingConnections.Deallocate(out, _FILE_AND_LINE_); - return *out; - } - else - { - return UNASSIGNED_SYSTEM_ADDRESS; - } -} -SystemAddress TCPInterface::HasLostConnection(void) -{ - SystemAddress *out, out2; - out = lostConnections.PopInaccurate(); - if (out) - { - out2=*out; - lostConnections.Deallocate(out, _FILE_AND_LINE_); - return *out; - } - else - { - return UNASSIGNED_SYSTEM_ADDRESS; - } -} -void TCPInterface::GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const -{ - unsigned short systemCount=0; - unsigned short maxToWrite=*numberOfSystems; - for (int i=0; i < remoteClientsLength; i++) - { - if (remoteClients[i].isActive) - { - if (systemCount < maxToWrite) - remoteSystems[systemCount]=remoteClients[i].systemAddress; - systemCount++; - } - } - *numberOfSystems=systemCount; -} -unsigned short TCPInterface::GetConnectionCount(void) const -{ - unsigned short systemCount=0; - for (int i=0; i < remoteClientsLength; i++) - { - if (remoteClients[i].isActive) - systemCount++; - } - return systemCount; -} - -unsigned int TCPInterface::GetOutgoingDataBufferSize(SystemAddress systemAddress) const -{ - unsigned bytesWritten=0; - if (systemAddress.systemIndexh_addr, server->h_length); - - - - - - - blockingSocketListMutex.Lock(); - blockingSocketList.Insert(sockfd, _FILE_AND_LINE_); - blockingSocketListMutex.Unlock(); - - // This is blocking - connectResult = connect__( sockfd, ( struct sockaddr * ) &serverAddress, sizeof( struct sockaddr ) ); - -#else - - - struct addrinfo hints, *res; - int sockfd; - memset(&hints, 0, sizeof hints); - hints.ai_family = socketFamily; - hints.ai_socktype = SOCK_STREAM; - char portStr[32]; - Itoa(remotePort,portStr,10); - getaddrinfo(host, portStr, &hints, &res); - sockfd = socket__(res->ai_family, res->ai_socktype, res->ai_protocol); - blockingSocketListMutex.Lock(); - blockingSocketList.Insert(sockfd, _FILE_AND_LINE_); - blockingSocketListMutex.Unlock(); - connectResult=connect__(sockfd, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); // free the linked-list - -#endif // #if RAKNET_SUPPORT_IPV6!=1 - - if (connectResult==-1) - { - unsigned sockfdIndex; - blockingSocketListMutex.Lock(); - sockfdIndex=blockingSocketList.GetIndexOf(sockfd); - if (sockfdIndex!=(unsigned)-1) - blockingSocketList.RemoveAtIndexFast(sockfdIndex); - blockingSocketListMutex.Unlock(); - - closesocket__(sockfd); - return (SOCKET) -1; - } - - return sockfd; -} - -RAK_THREAD_DECLARATION(RakNet::ConnectionAttemptLoop) -{ - - - - TCPInterface::ThisPtrPlusSysAddr *s = (TCPInterface::ThisPtrPlusSysAddr *) arguments; - - - - SystemAddress systemAddress = s->systemAddress; - TCPInterface *tcpInterface = s->tcpInterface; - int newRemoteClientIndex=systemAddress.systemIndex; - unsigned short socketFamily = s->socketFamily; - RakNet::OP_DELETE(s, _FILE_AND_LINE_); - - char str1[64]; - systemAddress.ToString(false, str1); - SOCKET sockfd = tcpInterface->SocketConnect(str1, systemAddress.GetPort(), socketFamily); - if (sockfd==(SOCKET)-1) - { - tcpInterface->remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); - tcpInterface->remoteClients[newRemoteClientIndex].SetActive(false); - tcpInterface->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - - tcpInterface->failedConnectionAttemptMutex.Lock(); - tcpInterface->failedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); - tcpInterface->failedConnectionAttemptMutex.Unlock(); - return 0; - } - - tcpInterface->remoteClients[newRemoteClientIndex].socket=sockfd; - tcpInterface->remoteClients[newRemoteClientIndex].systemAddress=systemAddress; - - // Notify user that the connection attempt has completed. - if (tcpInterface->threadRunning) - { - tcpInterface->completedConnectionAttemptMutex.Lock(); - tcpInterface->completedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); - tcpInterface->completedConnectionAttemptMutex.Unlock(); - } - - - - - return 0; - -} - -RAK_THREAD_DECLARATION(RakNet::UpdateTCPInterfaceLoop) -{ - - - - TCPInterface * sts = ( TCPInterface * ) arguments; - - -// const int BUFF_SIZE=8096; - const unsigned int BUFF_SIZE=1048576; - //char data[ BUFF_SIZE ]; - char * data = (char*) rakMalloc_Ex(BUFF_SIZE,_FILE_AND_LINE_); - Packet *incomingMessage; - fd_set readFD, exceptionFD, writeFD; - sts->threadRunning=true; - -#if RAKNET_SUPPORT_IPV6!=1 - sockaddr_in sockAddr; - int sockAddrSize = sizeof(sockAddr); -#else - struct sockaddr_storage sockAddr; - socklen_t sockAddrSize = sizeof(sockAddr); -#endif - - int len; - SOCKET newSock; - int selectResult; - - - timeval tv; - tv.tv_sec=0; - tv.tv_usec=30000; - - - while (sts->isStarted) - { -#if OPEN_SSL_CLIENT_SUPPORT==1 - SystemAddress *sslSystemAddress; - sslSystemAddress = sts->startSSL.PopInaccurate(); - if (sslSystemAddress) - { - if (sslSystemAddress->systemIndex>=0 && - sslSystemAddress->systemIndexremoteClientsLength && - sts->remoteClients[sslSystemAddress->systemIndex].systemAddress==*sslSystemAddress) - { - sts->remoteClients[sslSystemAddress->systemIndex].InitSSL(sts->ctx,sts->meth); - } - else - { - for (int i=0; i < sts->remoteClientsLength; i++) - { - sts->remoteClients[i].isActiveMutex.Lock(); - if (sts->remoteClients[i].isActive && sts->remoteClients[i].systemAddress==*sslSystemAddress) - { - if (sts->remoteClients[i].ssl==0) - sts->remoteClients[i].InitSSL(sts->ctx,sts->meth); - } - sts->remoteClients[i].isActiveMutex.Unlock(); - } - } - sts->startSSL.Deallocate(sslSystemAddress,_FILE_AND_LINE_); - } -#endif - - - SOCKET largestDescriptor=0; // see select__()'s first parameter's documentation under linux - - - // Linux' select__() implementation changes the timeout - - tv.tv_sec=0; - tv.tv_usec=30000; - - - while (1) - { - // Reset readFD, writeFD, and exceptionFD since select seems to clear it - FD_ZERO(&readFD); - FD_ZERO(&exceptionFD); - FD_ZERO(&writeFD); -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - largestDescriptor=0; - if (sts->listenSocket!=(SOCKET) -1) - { - FD_SET(sts->listenSocket, &readFD); - FD_SET(sts->listenSocket, &exceptionFD); - largestDescriptor = sts->listenSocket; // @see largestDescriptor def - } - - unsigned i; - for (i=0; i < (unsigned int) sts->remoteClientsLength; i++) - { - sts->remoteClients[i].isActiveMutex.Lock(); - if (sts->remoteClients[i].isActive && sts->remoteClients[i].socket!=INVALID_SOCKET) - { - FD_SET(sts->remoteClients[i].socket, &readFD); - FD_SET(sts->remoteClients[i].socket, &exceptionFD); - if (sts->remoteClients[i].outgoingData.GetBytesWritten()>0) - FD_SET(sts->remoteClients[i].socket, &writeFD); - if(sts->remoteClients[i].socket > largestDescriptor) // @see largestDescriptorDef - largestDescriptor = sts->remoteClients[i].socket; - } - sts->remoteClients[i].isActiveMutex.Unlock(); - } - -#ifdef _MSC_VER -#pragma warning( disable : 4244 ) // warning C4127: conditional expression is constant -#endif - - - selectResult=(int) select__(largestDescriptor+1, &readFD, &writeFD, &exceptionFD, &tv); - - - - - if (selectResult<=0) - break; - - if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &readFD)) - { - newSock = accept__(sts->listenSocket, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize); - - if (newSock != (SOCKET) -1) - { - int newRemoteClientIndex=-1; - for (newRemoteClientIndex=0; newRemoteClientIndex < sts->remoteClientsLength; newRemoteClientIndex++) - { - sts->remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); - if (sts->remoteClients[newRemoteClientIndex].isActive==false) - { - sts->remoteClients[newRemoteClientIndex].socket=newSock; - -#if RAKNET_SUPPORT_IPV6!=1 - sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_addr.s_addr=sockAddr.sin_addr.s_addr; - sts->remoteClients[newRemoteClientIndex].systemAddress.SetPortNetworkOrder( sockAddr.sin_port); - sts->remoteClients[newRemoteClientIndex].systemAddress.systemIndex=newRemoteClientIndex; -#else - if (sockAddr.ss_family==AF_INET) - { - memcpy(&sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4,(sockaddr_in *)&sockAddr,sizeof(sockaddr_in)); - // sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_port=ntohs( sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_port ); - } - else - { - memcpy(&sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6,(sockaddr_in6 *)&sockAddr,sizeof(sockaddr_in6)); - // sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6.sin6_port=ntohs( sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6.sin6_port ); - } - -#endif // #if RAKNET_SUPPORT_IPV6!=1 - sts->remoteClients[newRemoteClientIndex].SetActive(true); - sts->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - - - SystemAddress *newConnectionSystemAddress=sts->newIncomingConnections.Allocate( _FILE_AND_LINE_ ); - *newConnectionSystemAddress=sts->remoteClients[newRemoteClientIndex].systemAddress; - sts->newIncomingConnections.Push(newConnectionSystemAddress); - - break; - } - sts->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); - } - if (newRemoteClientIndex==-1) - { - closesocket__(sts->listenSocket); - } - } - else - { -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Error: connection failed\n"); -#endif - } - } - else if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &exceptionFD)) - { -#ifdef _DO_PRINTF - int err; - int errlen = sizeof(err); - getsockopt__(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen); - RAKNET_DEBUG_PRINTF("Socket error %s on listening socket\n", err); -#endif - } - - { - i=0; - while (i < (unsigned int) sts->remoteClientsLength) - { - if (sts->remoteClients[i].isActive==false) - { - i++; - continue; - } - - if (FD_ISSET(sts->remoteClients[i].socket, &exceptionFD)) - { -// #ifdef _DO_PRINTF -// if (sts->listenSocket!=-1) -// { -// int err; -// int errlen = sizeof(err); -// getsockopt__(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen); -// in_addr in; -// in.s_addr = sts->remoteClients[i].systemAddress.binaryAddress; -// RAKNET_DEBUG_PRINTF("Socket error %i on %s:%i\n", err,inet_ntoa( in ), sts->remoteClients[i].systemAddress.GetPort() ); -// } -// -// #endif - // Connection lost abruptly - SystemAddress *lostConnectionSystemAddress=sts->lostConnections.Allocate( _FILE_AND_LINE_ ); - *lostConnectionSystemAddress=sts->remoteClients[i].systemAddress; - sts->lostConnections.Push(lostConnectionSystemAddress); - sts->remoteClients[i].isActiveMutex.Lock(); - sts->remoteClients[i].SetActive(false); - sts->remoteClients[i].isActiveMutex.Unlock(); - } - else - { - if (FD_ISSET(sts->remoteClients[i].socket, &readFD)) - { - // if recv returns 0 this was a graceful close - len = sts->remoteClients[i].Recv(data,BUFF_SIZE); - - - // removeme -// data[len]=0; -// printf(data); - - if (len>0) - { - incomingMessage=sts->incomingMessages.Allocate( _FILE_AND_LINE_ ); - incomingMessage->data = (unsigned char*) rakMalloc_Ex( len+1, _FILE_AND_LINE_ ); - memcpy(incomingMessage->data, data, len); - incomingMessage->data[len]=0; // Null terminate this so we can print it out as regular strings. This is different from RakNet which does not do this. -// printf("RECV: %s\n",incomingMessage->data); - incomingMessage->length=len; - incomingMessage->deleteData=true; // actually means came from SPSC, rather than AllocatePacket - incomingMessage->systemAddress=sts->remoteClients[i].systemAddress; - sts->incomingMessages.Push(incomingMessage); - } - else - { - // Connection lost gracefully - SystemAddress *lostConnectionSystemAddress=sts->lostConnections.Allocate( _FILE_AND_LINE_ ); - *lostConnectionSystemAddress=sts->remoteClients[i].systemAddress; - sts->lostConnections.Push(lostConnectionSystemAddress); - sts->remoteClients[i].isActiveMutex.Lock(); - sts->remoteClients[i].SetActive(false); - sts->remoteClients[i].isActiveMutex.Unlock(); - continue; - } - } - if (FD_ISSET(sts->remoteClients[i].socket, &writeFD)) - { - RemoteClient *rc = &sts->remoteClients[i]; - unsigned int bytesInBuffer; - int bytesAvailable; - int bytesSent; - rc->outgoingDataMutex.Lock(); - bytesInBuffer=rc->outgoingData.GetBytesWritten(); - if (bytesInBuffer>0) - { - unsigned int contiguousLength; - char* contiguousBytesPointer = rc->outgoingData.PeekContiguousBytes(&contiguousLength); - if (contiguousLength < (unsigned int) BUFF_SIZE && contiguousLength BUFF_SIZE) - bytesAvailable=BUFF_SIZE; - else - bytesAvailable=bytesInBuffer; - rc->outgoingData.ReadBytes(data,bytesAvailable,true); - bytesSent=rc->Send(data,bytesAvailable); - } - else - { - bytesSent=rc->Send(contiguousBytesPointer,contiguousLength); - } - - rc->outgoingData.IncrementReadOffset(bytesSent); - bytesInBuffer=rc->outgoingData.GetBytesWritten(); - } - rc->outgoingDataMutex.Unlock(); - } - - i++; // Nothing deleted so increment the index - } - } - } - } - - // Sleep 0 on Linux monopolizes the CPU - RakSleep(30); - } - sts->threadRunning=false; - - rakFree_Ex(data,_FILE_AND_LINE_); - - - - - return 0; - -} - -void RemoteClient::SetActive(bool a) -{ - if (isActive != a) - { - isActive=a; - Reset(); - if (isActive==false && socket!=INVALID_SOCKET) - { - closesocket__(socket); - socket=INVALID_SOCKET; - } - } -} -void RemoteClient::SendOrBuffer(const char **data, const unsigned int *lengths, const int numParameters) -{ - // True can save memory and buffer copies, but gives worse performance overall - // Do not use true for the XBOX, as it just locks up - const bool ALLOW_SEND_FROM_USER_THREAD=false; - - int parameterIndex; - if (isActive==false) - return; - parameterIndex=0; - for (; parameterIndex < numParameters; parameterIndex++) - { - outgoingDataMutex.Lock(); - if (ALLOW_SEND_FROM_USER_THREAD && outgoingData.GetBytesWritten()==0) - { - outgoingDataMutex.Unlock(); - int bytesSent = Send(data[parameterIndex],lengths[parameterIndex]); - if (bytesSent<(int) lengths[parameterIndex]) - { - // Push remainder - outgoingDataMutex.Lock(); - outgoingData.WriteBytes(data[parameterIndex]+bytesSent,lengths[parameterIndex]-bytesSent,_FILE_AND_LINE_); - outgoingDataMutex.Unlock(); - } - } - else - { - outgoingData.WriteBytes(data[parameterIndex],lengths[parameterIndex],_FILE_AND_LINE_); - outgoingDataMutex.Unlock(); - } - } -} -#if OPEN_SSL_CLIENT_SUPPORT==1 -void RemoteClient::InitSSL(SSL_CTX* ctx, SSL_METHOD *meth) -{ - (void) meth; - - ssl = SSL_new (ctx); - RakAssert(ssl); - SSL_set_fd (ssl, socket); - SSL_connect (ssl); -} -void RemoteClient::DisconnectSSL(void) -{ - if (ssl) - SSL_shutdown (ssl); /* send SSL/TLS close_notify */ -} -void RemoteClient::FreeSSL(void) -{ - if (ssl) - SSL_free (ssl); -} -int RemoteClient::Send(const char *data, unsigned int length) -{ - int err; - if (ssl) - { - return SSL_write (ssl, data, length); - } - else - return send__(socket, data, length, 0); -} -int RemoteClient::Recv(char *data, const int dataSize) -{ - if (ssl) - return SSL_read (ssl, data, dataSize); - else - return recv__(socket, data, dataSize, 0); -} -#else -int RemoteClient::Send(const char *data, unsigned int length) -{ - return send__(socket, data, length, 0); -} -int RemoteClient::Recv(char *data, const int dataSize) -{ - return recv__(socket, data, dataSize, 0); -} -#endif - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TCPInterface==1 + +/// \file +/// \brief A simple TCP based server allowing sends and receives. Can be connected to by a telnet client. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "TCPInterface.h" +#ifdef _WIN32 +typedef int socklen_t; + + +#else +#include +#include +#include +#endif +#include +#include "RakAssert.h" +#include +#include "RakAssert.h" +#include "RakSleep.h" +#include "StringCompressor.h" +#include "StringTable.h" +#include "Itoa.h" +#include "SocketLayer.h" +#include "SocketDefines.h" + +#ifdef _DO_PRINTF +#endif + +#ifdef _WIN32 +#include "WSAStartupSingleton.h" +#endif +namespace RakNet +{ +RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop); +RAK_THREAD_DECLARATION(ConnectionAttemptLoop); +} +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(TCPInterface,TCPInterface); + +TCPInterface::TCPInterface() +{ + isStarted=false; + threadRunning=false; + listenSocket=(SOCKET) -1; + remoteClients=0; + remoteClientsLength=0; + + StringCompressor::AddReference(); + RakNet::StringTable::AddReference(); + +#if OPEN_SSL_CLIENT_SUPPORT==1 + ctx=0; + meth=0; +#endif + +#ifdef _WIN32 + WSAStartupSingleton::AddRef(); +#endif +} +TCPInterface::~TCPInterface() +{ + Stop(); +#ifdef _WIN32 + WSAStartupSingleton::Deref(); +#endif + + RakNet::OP_DELETE_ARRAY(remoteClients,_FILE_AND_LINE_); + + StringCompressor::RemoveReference(); + RakNet::StringTable::RemoveReference(); +} +bool TCPInterface::Start(unsigned short port, unsigned short maxIncomingConnections, unsigned short maxConnections, int _threadPriority, unsigned short socketFamily) +{ + (void) socketFamily; + + if (isStarted) + return false; + + threadPriority=_threadPriority; + + if (threadPriority==-99999) + { + + +#if defined(_WIN32) + threadPriority=0; + + +#else + threadPriority=1000; +#endif + } + + isStarted=true; + if (maxConnections==0) + maxConnections=maxIncomingConnections; + if (maxConnections==0) + maxConnections=1; + remoteClientsLength=maxConnections; + remoteClients=RakNet::OP_NEW_ARRAY(maxConnections,_FILE_AND_LINE_); + + +#if RAKNET_SUPPORT_IPV6!=1 + if (maxIncomingConnections>0) + { + listenSocket = socket__(AF_INET, SOCK_STREAM, 0); + if ((int)listenSocket ==-1) + return false; + + struct sockaddr_in serverAddress; + memset(&serverAddress,0,sizeof(sockaddr_in)); + serverAddress.sin_family = AF_INET; + + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + + serverAddress.sin_port = htons(port); + + if (bind__(listenSocket,(struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0) + return false; + + listen__(listenSocket, maxIncomingConnections); + } +#else + listenSocket=INVALID_SOCKET; + if (maxIncomingConnections>0) + { + struct addrinfo hints; + memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty + hints.ai_family = socketFamily; // don't care IPv4 or IPv6 + hints.ai_socktype = SOCK_STREAM; // TCP sockets + hints.ai_flags = AI_PASSIVE; // fill in my IP for me + struct addrinfo *servinfo=0, *aip; // will point to the results + char portStr[32]; + Itoa(port,portStr,10); + + getaddrinfo(0, portStr, &hints, &servinfo); + for (aip = servinfo; aip != NULL; aip = aip->ai_next) + { + // Open socket. The address type depends on what + // getaddrinfo() gave us. + listenSocket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (listenSocket != INVALID_SOCKET) + { + int ret = bind__( listenSocket, aip->ai_addr, (int) aip->ai_addrlen ); + if (ret>=0) + { + break; + } + else + { + closesocket__(listenSocket); + listenSocket=INVALID_SOCKET; + } + } + } + + if (listenSocket==INVALID_SOCKET) + return false; + + listen__(listenSocket, maxIncomingConnections); + } +#endif // #if RAKNET_SUPPORT_IPV6!=1 + + + // Start the update thread + int errorCode; + + + + + + errorCode = RakNet::RakThread::Create(UpdateTCPInterfaceLoop, this, threadPriority); + + + if (errorCode!=0) + return false; + + while (threadRunning==false) + RakSleep(0); + + return true; +} +void TCPInterface::Stop(void) +{ + if (isStarted==false) + return; + + unsigned i; +#if OPEN_SSL_CLIENT_SUPPORT==1 + for (i=0; i < remoteClientsLength; i++) + remoteClients[i].DisconnectSSL(); +#endif + + isStarted=false; + + if (listenSocket!=(SOCKET) -1) + { +#ifdef _WIN32 + shutdown__(listenSocket, SD_BOTH); + +#else + shutdown__(listenSocket, SHUT_RDWR); +#endif + closesocket__(listenSocket); + listenSocket=(SOCKET) -1; + } + + // Abort waiting connect calls + blockingSocketListMutex.Lock(); + for (i=0; i < blockingSocketList.Size(); i++) + { + closesocket__(blockingSocketList[i]); + } + blockingSocketListMutex.Unlock(); + + // Wait for the thread to stop + while ( threadRunning ) + RakSleep(15); + + RakSleep(100); + + // Stuff from here on to the end of the function is not threadsafe + for (i=0; i < (unsigned int) remoteClientsLength; i++) + { + closesocket__(remoteClients[i].socket); +#if OPEN_SSL_CLIENT_SUPPORT==1 + remoteClients[i].FreeSSL(); +#endif + } + remoteClientsLength=0; + RakNet::OP_DELETE_ARRAY(remoteClients,_FILE_AND_LINE_); + remoteClients=0; + + incomingMessages.Clear(_FILE_AND_LINE_); + newIncomingConnections.Clear(_FILE_AND_LINE_); + newRemoteClients.Clear(_FILE_AND_LINE_); + lostConnections.Clear(_FILE_AND_LINE_); + requestedCloseConnections.Clear(_FILE_AND_LINE_); + failedConnectionAttempts.Clear(_FILE_AND_LINE_); + completedConnectionAttempts.Clear(_FILE_AND_LINE_); + failedConnectionAttempts.Clear(_FILE_AND_LINE_); + for (i=0; i < headPush.Size(); i++) + DeallocatePacket(headPush[i]); + headPush.Clear(_FILE_AND_LINE_); + for (i=0; i < tailPush.Size(); i++) + DeallocatePacket(tailPush[i]); + tailPush.Clear(_FILE_AND_LINE_); + +#if OPEN_SSL_CLIENT_SUPPORT==1 + SSL_CTX_free (ctx); + startSSL.Clear(_FILE_AND_LINE_); + activeSSLConnections.Clear(false, _FILE_AND_LINE_); +#endif + + + + + +} +SystemAddress TCPInterface::Connect(const char* host, unsigned short remotePort, bool block, unsigned short socketFamily) +{ + if (threadRunning==false) + return UNASSIGNED_SYSTEM_ADDRESS; + + int newRemoteClientIndex=-1; + for (newRemoteClientIndex=0; newRemoteClientIndex < remoteClientsLength; newRemoteClientIndex++) + { + remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); + if (remoteClients[newRemoteClientIndex].isActive==false) + { + remoteClients[newRemoteClientIndex].SetActive(true); + remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + break; + } + remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + } + if (newRemoteClientIndex==-1) + return UNASSIGNED_SYSTEM_ADDRESS; + + if (block) + { + SystemAddress systemAddress; + systemAddress.FromString(host); + systemAddress.SetPort(remotePort); + systemAddress.systemIndex=(SystemIndex) newRemoteClientIndex; + char buffout[128]; + systemAddress.ToString(false,buffout); + + SOCKET sockfd = SocketConnect(buffout, remotePort, socketFamily); + if (sockfd==(SOCKET)-1) + { + remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); + remoteClients[newRemoteClientIndex].SetActive(false); + remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + + failedConnectionAttemptMutex.Lock(); + failedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); + failedConnectionAttemptMutex.Unlock(); + + return UNASSIGNED_SYSTEM_ADDRESS; + } + + remoteClients[newRemoteClientIndex].socket=sockfd; + remoteClients[newRemoteClientIndex].systemAddress=systemAddress; + + completedConnectionAttemptMutex.Lock(); + completedConnectionAttempts.Push(remoteClients[newRemoteClientIndex].systemAddress, _FILE_AND_LINE_ ); + completedConnectionAttemptMutex.Unlock(); + + return remoteClients[newRemoteClientIndex].systemAddress; + } + else + { + ThisPtrPlusSysAddr *s = RakNet::OP_NEW( _FILE_AND_LINE_ ); + s->systemAddress.FromStringExplicitPort(host,remotePort); + s->systemAddress.systemIndex=(SystemIndex) newRemoteClientIndex; + s->tcpInterface=this; + s->socketFamily=socketFamily; + + // Start the connection thread + int errorCode; + + + + + errorCode = RakNet::RakThread::Create(ConnectionAttemptLoop, s, threadPriority); + + if (errorCode!=0) + { + RakNet::OP_DELETE(s, _FILE_AND_LINE_); + failedConnectionAttempts.Push(s->systemAddress, _FILE_AND_LINE_ ); + } + return UNASSIGNED_SYSTEM_ADDRESS; + } +} +#if OPEN_SSL_CLIENT_SUPPORT==1 +void TCPInterface::StartSSLClient(SystemAddress systemAddress) +{ + if (ctx==0) + { + sharedSslMutex.Lock(); + SSLeay_add_ssl_algorithms(); + meth = (SSL_METHOD*) SSLv2_client_method(); + SSL_load_error_strings(); + ctx = SSL_CTX_new (meth); + RakAssert(ctx!=0); + sharedSslMutex.Unlock(); + } + + SystemAddress *id = startSSL.Allocate( _FILE_AND_LINE_ ); + *id=systemAddress; + startSSL.Push(id); + unsigned index = activeSSLConnections.GetIndexOf(systemAddress); + if (index==(unsigned)-1) + activeSSLConnections.Insert(systemAddress,_FILE_AND_LINE_); +} +bool TCPInterface::IsSSLActive(SystemAddress systemAddress) +{ + return activeSSLConnections.GetIndexOf(systemAddress)!=-1; +} +#endif +void TCPInterface::Send( const char *data, unsigned length, const SystemAddress &systemAddress, bool broadcast ) +{ + SendList( &data, &length, 1, systemAddress,broadcast ); +} +bool TCPInterface::SendList( const char **data, const unsigned int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ) +{ + if (isStarted==false) + return false; + if (data==0) + return false; + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS && broadcast==false) + return false; + unsigned int totalLength=0; + int i; + for (i=0; i < numParameters; i++) + { + if (lengths[i]>0) + totalLength+=lengths[i]; + } + if (totalLength==0) + return false; + + if (broadcast) + { + // Send to all, possible exception system + for (i=0; i < remoteClientsLength; i++) + { + if (remoteClients[i].systemAddress!=systemAddress) + { + remoteClients[i].SendOrBuffer(data, lengths, numParameters); + } + } + } + else + { + // Send to this player + if (systemAddress.systemIndexdeleteData) + { + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + incomingMessages.Deallocate(packet, _FILE_AND_LINE_); + } + else + { + // Came from userspace AllocatePacket + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + RakNet::OP_DELETE(packet, _FILE_AND_LINE_); + } +} +Packet* TCPInterface::AllocatePacket(unsigned dataSize) +{ + Packet*p = RakNet::OP_NEW(_FILE_AND_LINE_); + p->data=(unsigned char*) rakMalloc_Ex(dataSize,_FILE_AND_LINE_); + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=false; + p->guid=UNASSIGNED_RAKNET_GUID; + p->systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + p->systemAddress.systemIndex=(SystemIndex)-1; + return p; +} +void TCPInterface::PushBackPacket( Packet *packet, bool pushAtHead ) +{ + if (pushAtHead) + headPush.Push(packet, _FILE_AND_LINE_ ); + else + tailPush.Push(packet, _FILE_AND_LINE_ ); +} +bool TCPInterface::WasStarted(void) const +{ + return threadRunning==true; +} +int TCPInterface::Base64Encoding(const char *inputData, int dataLength, char *outputData) +{ + // http://en.wikipedia.org/wiki/Base64 + + int outputOffset, charCount; + int write3Count; + outputOffset=0; + charCount=0; + int j; + + write3Count=dataLength/3; + for (j=0; j < write3Count; j++) + { + // 6 leftmost bits from first byte, shifted to bits 7,8 are 0 + outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // 4 low bits from the second byte and the two high bits from the third byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[((inputData[j*3+1] << 2) | (inputData[j*3+2] >> 6)) & 63]; // Third 6 bits + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Last 6 bits from the third byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[inputData[j*3+2] & 63]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + } + + if (dataLength % 3==1) + { + // One input byte remaining + outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Pad with two equals + outputData[outputOffset++]='='; + outputData[outputOffset++]='='; + } + else if (dataLength % 3==2) + { + // Two input bytes remaining + + // 6 leftmost bits from first byte, shifted to bits 7,8 are 0 + outputData[outputOffset++]=Base64Map()[inputData[j*3+0] >> 2]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8 + outputData[outputOffset++]=Base64Map()[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63]; + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // 4 low bits from the second byte, followed by 00 + outputData[outputOffset++]=Base64Map()[(inputData[j*3+1] << 2) & 63]; // Third 6 bits + if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;} + + // Pad with one equal + outputData[outputOffset++]='='; + //outputData[outputOffset++]='='; + } + + // Append \r\n + outputData[outputOffset++]='\r'; + outputData[outputOffset++]='\n'; + outputData[outputOffset]=0; + + return outputOffset; +} +SystemAddress TCPInterface::HasCompletedConnectionAttempt(void) +{ + SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS; + completedConnectionAttemptMutex.Lock(); + if (completedConnectionAttempts.IsEmpty()==false) + sysAddr=completedConnectionAttempts.Pop(); + completedConnectionAttemptMutex.Unlock(); + return sysAddr; +} +SystemAddress TCPInterface::HasFailedConnectionAttempt(void) +{ + SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS; + failedConnectionAttemptMutex.Lock(); + if (failedConnectionAttempts.IsEmpty()==false) + sysAddr=failedConnectionAttempts.Pop(); + failedConnectionAttemptMutex.Unlock(); + return sysAddr; +} +SystemAddress TCPInterface::HasNewIncomingConnection(void) +{ + SystemAddress *out, out2; + out = newIncomingConnections.PopInaccurate(); + if (out) + { + out2=*out; + newIncomingConnections.Deallocate(out, _FILE_AND_LINE_); + return *out; + } + else + { + return UNASSIGNED_SYSTEM_ADDRESS; + } +} +SystemAddress TCPInterface::HasLostConnection(void) +{ + SystemAddress *out, out2; + out = lostConnections.PopInaccurate(); + if (out) + { + out2=*out; + lostConnections.Deallocate(out, _FILE_AND_LINE_); + return *out; + } + else + { + return UNASSIGNED_SYSTEM_ADDRESS; + } +} +void TCPInterface::GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const +{ + unsigned short systemCount=0; + unsigned short maxToWrite=*numberOfSystems; + for (int i=0; i < remoteClientsLength; i++) + { + if (remoteClients[i].isActive) + { + if (systemCount < maxToWrite) + remoteSystems[systemCount]=remoteClients[i].systemAddress; + systemCount++; + } + } + *numberOfSystems=systemCount; +} +unsigned short TCPInterface::GetConnectionCount(void) const +{ + unsigned short systemCount=0; + for (int i=0; i < remoteClientsLength; i++) + { + if (remoteClients[i].isActive) + systemCount++; + } + return systemCount; +} + +unsigned int TCPInterface::GetOutgoingDataBufferSize(SystemAddress systemAddress) const +{ + unsigned bytesWritten=0; + if (systemAddress.systemIndexh_addr, server->h_length); + + + + + + + blockingSocketListMutex.Lock(); + blockingSocketList.Insert(sockfd, _FILE_AND_LINE_); + blockingSocketListMutex.Unlock(); + + // This is blocking + connectResult = connect__( sockfd, ( struct sockaddr * ) &serverAddress, sizeof( struct sockaddr ) ); + +#else + + + struct addrinfo hints, *res; + int sockfd; + memset(&hints, 0, sizeof hints); + hints.ai_family = socketFamily; + hints.ai_socktype = SOCK_STREAM; + char portStr[32]; + Itoa(remotePort,portStr,10); + getaddrinfo(host, portStr, &hints, &res); + sockfd = socket__(res->ai_family, res->ai_socktype, res->ai_protocol); + blockingSocketListMutex.Lock(); + blockingSocketList.Insert(sockfd, _FILE_AND_LINE_); + blockingSocketListMutex.Unlock(); + connectResult=connect__(sockfd, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); // free the linked-list + +#endif // #if RAKNET_SUPPORT_IPV6!=1 + + if (connectResult==-1) + { + unsigned sockfdIndex; + blockingSocketListMutex.Lock(); + sockfdIndex=blockingSocketList.GetIndexOf(sockfd); + if (sockfdIndex!=(unsigned)-1) + blockingSocketList.RemoveAtIndexFast(sockfdIndex); + blockingSocketListMutex.Unlock(); + + closesocket__(sockfd); + return (SOCKET) -1; + } + + return sockfd; +} + +RAK_THREAD_DECLARATION(RakNet::ConnectionAttemptLoop) +{ + + + + TCPInterface::ThisPtrPlusSysAddr *s = (TCPInterface::ThisPtrPlusSysAddr *) arguments; + + + + SystemAddress systemAddress = s->systemAddress; + TCPInterface *tcpInterface = s->tcpInterface; + int newRemoteClientIndex=systemAddress.systemIndex; + unsigned short socketFamily = s->socketFamily; + RakNet::OP_DELETE(s, _FILE_AND_LINE_); + + char str1[64]; + systemAddress.ToString(false, str1); + SOCKET sockfd = tcpInterface->SocketConnect(str1, systemAddress.GetPort(), socketFamily); + if (sockfd==(SOCKET)-1) + { + tcpInterface->remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); + tcpInterface->remoteClients[newRemoteClientIndex].SetActive(false); + tcpInterface->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + + tcpInterface->failedConnectionAttemptMutex.Lock(); + tcpInterface->failedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); + tcpInterface->failedConnectionAttemptMutex.Unlock(); + return 0; + } + + tcpInterface->remoteClients[newRemoteClientIndex].socket=sockfd; + tcpInterface->remoteClients[newRemoteClientIndex].systemAddress=systemAddress; + + // Notify user that the connection attempt has completed. + if (tcpInterface->threadRunning) + { + tcpInterface->completedConnectionAttemptMutex.Lock(); + tcpInterface->completedConnectionAttempts.Push(systemAddress, _FILE_AND_LINE_ ); + tcpInterface->completedConnectionAttemptMutex.Unlock(); + } + + + + + return 0; + +} + +RAK_THREAD_DECLARATION(RakNet::UpdateTCPInterfaceLoop) +{ + + + + TCPInterface * sts = ( TCPInterface * ) arguments; + + +// const int BUFF_SIZE=8096; + const unsigned int BUFF_SIZE=1048576; + //char data[ BUFF_SIZE ]; + char * data = (char*) rakMalloc_Ex(BUFF_SIZE,_FILE_AND_LINE_); + Packet *incomingMessage; + fd_set readFD, exceptionFD, writeFD; + sts->threadRunning=true; + +#if RAKNET_SUPPORT_IPV6!=1 + sockaddr_in sockAddr; + int sockAddrSize = sizeof(sockAddr); +#else + struct sockaddr_storage sockAddr; + socklen_t sockAddrSize = sizeof(sockAddr); +#endif + + int len; + SOCKET newSock; + int selectResult; + + + timeval tv; + tv.tv_sec=0; + tv.tv_usec=30000; + + + while (sts->isStarted) + { +#if OPEN_SSL_CLIENT_SUPPORT==1 + SystemAddress *sslSystemAddress; + sslSystemAddress = sts->startSSL.PopInaccurate(); + if (sslSystemAddress) + { + if (sslSystemAddress->systemIndex>=0 && + sslSystemAddress->systemIndexremoteClientsLength && + sts->remoteClients[sslSystemAddress->systemIndex].systemAddress==*sslSystemAddress) + { + sts->remoteClients[sslSystemAddress->systemIndex].InitSSL(sts->ctx,sts->meth); + } + else + { + for (int i=0; i < sts->remoteClientsLength; i++) + { + sts->remoteClients[i].isActiveMutex.Lock(); + if (sts->remoteClients[i].isActive && sts->remoteClients[i].systemAddress==*sslSystemAddress) + { + if (sts->remoteClients[i].ssl==0) + sts->remoteClients[i].InitSSL(sts->ctx,sts->meth); + } + sts->remoteClients[i].isActiveMutex.Unlock(); + } + } + sts->startSSL.Deallocate(sslSystemAddress,_FILE_AND_LINE_); + } +#endif + + + SOCKET largestDescriptor=0; // see select__()'s first parameter's documentation under linux + + + // Linux' select__() implementation changes the timeout + + tv.tv_sec=0; + tv.tv_usec=30000; + + + while (1) + { + // Reset readFD, writeFD, and exceptionFD since select seems to clear it + FD_ZERO(&readFD); + FD_ZERO(&exceptionFD); + FD_ZERO(&writeFD); +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + largestDescriptor=0; + if (sts->listenSocket!=(SOCKET) -1) + { + FD_SET(sts->listenSocket, &readFD); + FD_SET(sts->listenSocket, &exceptionFD); + largestDescriptor = sts->listenSocket; // @see largestDescriptor def + } + + unsigned i; + for (i=0; i < (unsigned int) sts->remoteClientsLength; i++) + { + sts->remoteClients[i].isActiveMutex.Lock(); + if (sts->remoteClients[i].isActive && sts->remoteClients[i].socket!=INVALID_SOCKET) + { + FD_SET(sts->remoteClients[i].socket, &readFD); + FD_SET(sts->remoteClients[i].socket, &exceptionFD); + if (sts->remoteClients[i].outgoingData.GetBytesWritten()>0) + FD_SET(sts->remoteClients[i].socket, &writeFD); + if(sts->remoteClients[i].socket > largestDescriptor) // @see largestDescriptorDef + largestDescriptor = sts->remoteClients[i].socket; + } + sts->remoteClients[i].isActiveMutex.Unlock(); + } + +#ifdef _MSC_VER +#pragma warning( disable : 4244 ) // warning C4127: conditional expression is constant +#endif + + + selectResult=(int) select__(largestDescriptor+1, &readFD, &writeFD, &exceptionFD, &tv); + + + + + if (selectResult<=0) + break; + + if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &readFD)) + { + newSock = accept__(sts->listenSocket, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize); + + if (newSock != (SOCKET) -1) + { + int newRemoteClientIndex=-1; + for (newRemoteClientIndex=0; newRemoteClientIndex < sts->remoteClientsLength; newRemoteClientIndex++) + { + sts->remoteClients[newRemoteClientIndex].isActiveMutex.Lock(); + if (sts->remoteClients[newRemoteClientIndex].isActive==false) + { + sts->remoteClients[newRemoteClientIndex].socket=newSock; + +#if RAKNET_SUPPORT_IPV6!=1 + sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_addr.s_addr=sockAddr.sin_addr.s_addr; + sts->remoteClients[newRemoteClientIndex].systemAddress.SetPortNetworkOrder( sockAddr.sin_port); + sts->remoteClients[newRemoteClientIndex].systemAddress.systemIndex=newRemoteClientIndex; +#else + if (sockAddr.ss_family==AF_INET) + { + memcpy(&sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4,(sockaddr_in *)&sockAddr,sizeof(sockaddr_in)); + // sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_port=ntohs( sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr4.sin_port ); + } + else + { + memcpy(&sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6,(sockaddr_in6 *)&sockAddr,sizeof(sockaddr_in6)); + // sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6.sin6_port=ntohs( sts->remoteClients[newRemoteClientIndex].systemAddress.address.addr6.sin6_port ); + } + +#endif // #if RAKNET_SUPPORT_IPV6!=1 + sts->remoteClients[newRemoteClientIndex].SetActive(true); + sts->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + + + SystemAddress *newConnectionSystemAddress=sts->newIncomingConnections.Allocate( _FILE_AND_LINE_ ); + *newConnectionSystemAddress=sts->remoteClients[newRemoteClientIndex].systemAddress; + sts->newIncomingConnections.Push(newConnectionSystemAddress); + + break; + } + sts->remoteClients[newRemoteClientIndex].isActiveMutex.Unlock(); + } + if (newRemoteClientIndex==-1) + { + closesocket__(sts->listenSocket); + } + } + else + { +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Error: connection failed\n"); +#endif + } + } + else if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &exceptionFD)) + { +#ifdef _DO_PRINTF + int err; + int errlen = sizeof(err); + getsockopt__(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen); + RAKNET_DEBUG_PRINTF("Socket error %s on listening socket\n", err); +#endif + } + + { + i=0; + while (i < (unsigned int) sts->remoteClientsLength) + { + if (sts->remoteClients[i].isActive==false) + { + i++; + continue; + } + + if (FD_ISSET(sts->remoteClients[i].socket, &exceptionFD)) + { +// #ifdef _DO_PRINTF +// if (sts->listenSocket!=-1) +// { +// int err; +// int errlen = sizeof(err); +// getsockopt__(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen); +// in_addr in; +// in.s_addr = sts->remoteClients[i].systemAddress.binaryAddress; +// RAKNET_DEBUG_PRINTF("Socket error %i on %s:%i\n", err,inet_ntoa( in ), sts->remoteClients[i].systemAddress.GetPort() ); +// } +// +// #endif + // Connection lost abruptly + SystemAddress *lostConnectionSystemAddress=sts->lostConnections.Allocate( _FILE_AND_LINE_ ); + *lostConnectionSystemAddress=sts->remoteClients[i].systemAddress; + sts->lostConnections.Push(lostConnectionSystemAddress); + sts->remoteClients[i].isActiveMutex.Lock(); + sts->remoteClients[i].SetActive(false); + sts->remoteClients[i].isActiveMutex.Unlock(); + } + else + { + if (FD_ISSET(sts->remoteClients[i].socket, &readFD)) + { + // if recv returns 0 this was a graceful close + len = sts->remoteClients[i].Recv(data,BUFF_SIZE); + + + // removeme +// data[len]=0; +// printf(data); + + if (len>0) + { + incomingMessage=sts->incomingMessages.Allocate( _FILE_AND_LINE_ ); + incomingMessage->data = (unsigned char*) rakMalloc_Ex( len+1, _FILE_AND_LINE_ ); + memcpy(incomingMessage->data, data, len); + incomingMessage->data[len]=0; // Null terminate this so we can print it out as regular strings. This is different from RakNet which does not do this. +// printf("RECV: %s\n",incomingMessage->data); + incomingMessage->length=len; + incomingMessage->deleteData=true; // actually means came from SPSC, rather than AllocatePacket + incomingMessage->systemAddress=sts->remoteClients[i].systemAddress; + sts->incomingMessages.Push(incomingMessage); + } + else + { + // Connection lost gracefully + SystemAddress *lostConnectionSystemAddress=sts->lostConnections.Allocate( _FILE_AND_LINE_ ); + *lostConnectionSystemAddress=sts->remoteClients[i].systemAddress; + sts->lostConnections.Push(lostConnectionSystemAddress); + sts->remoteClients[i].isActiveMutex.Lock(); + sts->remoteClients[i].SetActive(false); + sts->remoteClients[i].isActiveMutex.Unlock(); + continue; + } + } + if (FD_ISSET(sts->remoteClients[i].socket, &writeFD)) + { + RemoteClient *rc = &sts->remoteClients[i]; + unsigned int bytesInBuffer; + int bytesAvailable; + int bytesSent; + rc->outgoingDataMutex.Lock(); + bytesInBuffer=rc->outgoingData.GetBytesWritten(); + if (bytesInBuffer>0) + { + unsigned int contiguousLength; + char* contiguousBytesPointer = rc->outgoingData.PeekContiguousBytes(&contiguousLength); + if (contiguousLength < (unsigned int) BUFF_SIZE && contiguousLength BUFF_SIZE) + bytesAvailable=BUFF_SIZE; + else + bytesAvailable=bytesInBuffer; + rc->outgoingData.ReadBytes(data,bytesAvailable,true); + bytesSent=rc->Send(data,bytesAvailable); + } + else + { + bytesSent=rc->Send(contiguousBytesPointer,contiguousLength); + } + + rc->outgoingData.IncrementReadOffset(bytesSent); + bytesInBuffer=rc->outgoingData.GetBytesWritten(); + } + rc->outgoingDataMutex.Unlock(); + } + + i++; // Nothing deleted so increment the index + } + } + } + } + + // Sleep 0 on Linux monopolizes the CPU + RakSleep(30); + } + sts->threadRunning=false; + + rakFree_Ex(data,_FILE_AND_LINE_); + + + + + return 0; + +} + +void RemoteClient::SetActive(bool a) +{ + if (isActive != a) + { + isActive=a; + Reset(); + if (isActive==false && socket!=INVALID_SOCKET) + { + closesocket__(socket); + socket=INVALID_SOCKET; + } + } +} +void RemoteClient::SendOrBuffer(const char **data, const unsigned int *lengths, const int numParameters) +{ + // True can save memory and buffer copies, but gives worse performance overall + // Do not use true for the XBOX, as it just locks up + const bool ALLOW_SEND_FROM_USER_THREAD=false; + + int parameterIndex; + if (isActive==false) + return; + parameterIndex=0; + for (; parameterIndex < numParameters; parameterIndex++) + { + outgoingDataMutex.Lock(); + if (ALLOW_SEND_FROM_USER_THREAD && outgoingData.GetBytesWritten()==0) + { + outgoingDataMutex.Unlock(); + int bytesSent = Send(data[parameterIndex],lengths[parameterIndex]); + if (bytesSent<(int) lengths[parameterIndex]) + { + // Push remainder + outgoingDataMutex.Lock(); + outgoingData.WriteBytes(data[parameterIndex]+bytesSent,lengths[parameterIndex]-bytesSent,_FILE_AND_LINE_); + outgoingDataMutex.Unlock(); + } + } + else + { + outgoingData.WriteBytes(data[parameterIndex],lengths[parameterIndex],_FILE_AND_LINE_); + outgoingDataMutex.Unlock(); + } + } +} +#if OPEN_SSL_CLIENT_SUPPORT==1 +void RemoteClient::InitSSL(SSL_CTX* ctx, SSL_METHOD *meth) +{ + (void) meth; + + ssl = SSL_new (ctx); + RakAssert(ssl); + SSL_set_fd (ssl, socket); + SSL_connect (ssl); +} +void RemoteClient::DisconnectSSL(void) +{ + if (ssl) + SSL_shutdown (ssl); /* send SSL/TLS close_notify */ +} +void RemoteClient::FreeSSL(void) +{ + if (ssl) + SSL_free (ssl); +} +int RemoteClient::Send(const char *data, unsigned int length) +{ + int err; + if (ssl) + { + return SSL_write (ssl, data, length); + } + else + return send__(socket, data, length, 0); +} +int RemoteClient::Recv(char *data, const int dataSize) +{ + if (ssl) + return SSL_read (ssl, data, dataSize); + else + return recv__(socket, data, dataSize, 0); +} +#else +int RemoteClient::Send(const char *data, unsigned int length) +{ + return send__(socket, data, length, 0); +} +int RemoteClient::Recv(char *data, const int dataSize) +{ + return recv__(socket, data, dataSize, 0); +} +#endif + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/TCPInterface.h b/src/raknet/TCPInterface.h index e7bc8a0..3d76d51 100755 --- a/src/raknet/TCPInterface.h +++ b/src/raknet/TCPInterface.h @@ -1,240 +1,240 @@ -/// \file -/// \brief A simple TCP based server allowing sends and receives. Can be connected by any TCP client, including telnet. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TCPInterface==1 - -#ifndef __SIMPLE_TCP_SERVER -#define __SIMPLE_TCP_SERVER - -#include "RakMemoryOverride.h" -#include "DS_List.h" -#include "RakNetTypes.h" -#include "Export.h" -#include "RakThread.h" -#include "DS_Queue.h" -#include "SimpleMutex.h" -#include "RakNetDefines.h" -#include "SocketIncludes.h" -#include "DS_ByteQueue.h" -#include "DS_ThreadsafeAllocatingQueue.h" - -#if OPEN_SSL_CLIENT_SUPPORT==1 -#include -#include -#include -#include -#include -#endif - -namespace RakNet -{ -/// Forward declarations -struct RemoteClient; - -/// \internal -/// \brief As the name says, a simple multithreaded TCP server. Used by TelnetTransport -class RAK_DLL_EXPORT TCPInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(TCPInterface) - - TCPInterface(); - virtual ~TCPInterface(); - - /// Starts the TCP server on the indicated port - /// \param[in] port Which port to listen on. - /// \param[in] maxIncomingConnections Max incoming connections we will accept - /// \param[in] maxConnections Max total connections, which should be >= maxIncomingConnections - /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. - /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - bool Start(unsigned short port, unsigned short maxIncomingConnections, unsigned short maxConnections=0, int _threadPriority=-99999, unsigned short socketFamily=AF_INET); - - /// Stops the TCP server - void Stop(void); - - /// Connect to the specified host on the specified port - SystemAddress Connect(const char* host, unsigned short remotePort, bool block=true, unsigned short socketFamily=AF_INET); - -#if OPEN_SSL_CLIENT_SUPPORT==1 - /// Start SSL on an existing connection, notified with HasCompletedConnectionAttempt - void StartSSLClient(SystemAddress systemAddress); - - /// Was SSL started on this socket? - bool IsSSLActive(SystemAddress systemAddress); -#endif - - /// Sends a byte stream - void Send( const char *data, unsigned int length, const SystemAddress &systemAddress, bool broadcast ); - - // Sends a concatenated list of byte streams - bool SendList( const char **data, const unsigned int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ); - - // Get how many bytes are waiting to be sent. If too many, you may want to skip sending - unsigned int GetOutgoingDataBufferSize(SystemAddress systemAddress) const; - - /// Returns if Receive() will return data - bool ReceiveHasPackets( void ); - - /// Returns data received - Packet* Receive( void ); - - /// Disconnects a player/address - void CloseConnection( SystemAddress systemAddress ); - - /// Deallocates a packet returned by Receive - void DeallocatePacket( Packet *packet ); - - /// Fills the array remoteSystems with the SystemAddress of all the systems we are connected to - /// \param[out] remoteSystems An array of SystemAddress structures to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to only get the number of systems we are connected to - /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array - void GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const; - - /// Returns just the number of connections we have - unsigned short GetConnectionCount(void) const; - - /// Has a previous call to connect succeeded? - /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. - SystemAddress HasCompletedConnectionAttempt(void); - - /// Has a previous call to connect failed? - /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. - SystemAddress HasFailedConnectionAttempt(void); - - /// Queued events of new incoming connections - SystemAddress HasNewIncomingConnection(void); - - /// Queued events of lost connections - SystemAddress HasLostConnection(void); - - /// Return an allocated but empty packet, for custom use - Packet* AllocatePacket(unsigned dataSize); - - // Push a packet back to the queue - virtual void PushBackPacket( Packet *packet, bool pushAtHead ); - - static const char *Base64Map(void) {return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";} - - /// \brief Returns how many bytes were written. - static int Base64Encoding(const char *inputData, int dataLength, char *outputData); - - /// Returns if Start() was called successfully - bool WasStarted(void) const; - -protected: - - bool isStarted, threadRunning; - SOCKET listenSocket; - - DataStructures::Queue headPush, tailPush; - RemoteClient* remoteClients; - int remoteClientsLength; - - // Assuming remoteClients is only used by one thread! - // DataStructures::List remoteClients; - // Use this thread-safe queue to add to remoteClients - // DataStructures::Queue remoteClientsInsertionQueue; - // SimpleMutex remoteClientsInsertionQueueMutex; - - /* - struct OutgoingMessage - { - unsigned char* data; - SystemAddress systemAddress; - bool broadcast; - unsigned int length; - }; - */ -// DataStructures::SingleProducerConsumer outgoingMessages; -// DataStructures::SingleProducerConsumer incomingMessages; -// DataStructures::SingleProducerConsumer newIncomingConnections, lostConnections, requestedCloseConnections; -// DataStructures::SingleProducerConsumer newRemoteClients; -// DataStructures::ThreadsafeAllocatingQueue outgoingMessages; - DataStructures::ThreadsafeAllocatingQueue incomingMessages; - DataStructures::ThreadsafeAllocatingQueue newIncomingConnections, lostConnections, requestedCloseConnections; - DataStructures::ThreadsafeAllocatingQueue newRemoteClients; - SimpleMutex completedConnectionAttemptMutex, failedConnectionAttemptMutex; - DataStructures::Queue completedConnectionAttempts, failedConnectionAttempts; - - int threadPriority; - - DataStructures::List blockingSocketList; - SimpleMutex blockingSocketListMutex; - - - - - - friend RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop); - friend RAK_THREAD_DECLARATION(ConnectionAttemptLoop); - -// void DeleteRemoteClient(RemoteClient *remoteClient, fd_set *exceptionFD); -// void InsertRemoteClient(RemoteClient* remoteClient); - SOCKET SocketConnect(const char* host, unsigned short remotePort, unsigned short socketFamily); - - struct ThisPtrPlusSysAddr - { - TCPInterface *tcpInterface; - SystemAddress systemAddress; - bool useSSL; - unsigned short socketFamily; - }; - -#if OPEN_SSL_CLIENT_SUPPORT==1 - SSL_CTX* ctx; - SSL_METHOD *meth; - DataStructures::ThreadsafeAllocatingQueue startSSL; - DataStructures::List activeSSLConnections; - SimpleMutex sharedSslMutex; -#endif -}; - -/// Stores information about a remote client. -struct RemoteClient -{ - RemoteClient() { -#if OPEN_SSL_CLIENT_SUPPORT==1 - ssl=0; -#endif - isActive=false; - socket=INVALID_SOCKET; - } - SOCKET socket; - SystemAddress systemAddress; - DataStructures::ByteQueue outgoingData; - bool isActive; - SimpleMutex outgoingDataMutex; - SimpleMutex isActiveMutex; - -#if OPEN_SSL_CLIENT_SUPPORT==1 - SSL* ssl; - void InitSSL(SSL_CTX* ctx, SSL_METHOD *meth); - void DisconnectSSL(void); - void FreeSSL(void); - int Send(const char *data, unsigned int length); - int Recv(char *data, const int dataSize); -#else - int Send(const char *data, unsigned int length); - int Recv(char *data, const int dataSize); -#endif - void Reset(void) - { - outgoingDataMutex.Lock(); - outgoingData.Clear(_FILE_AND_LINE_); - outgoingDataMutex.Unlock(); - } - void SetActive(bool a); - void SendOrBuffer(const char **data, const unsigned int *lengths, const int numParameters); -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* - +/// \file +/// \brief A simple TCP based server allowing sends and receives. Can be connected by any TCP client, including telnet. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TCPInterface==1 + +#ifndef __SIMPLE_TCP_SERVER +#define __SIMPLE_TCP_SERVER + +#include "RakMemoryOverride.h" +#include "DS_List.h" +#include "RakNetTypes.h" +#include "Export.h" +#include "RakThread.h" +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "RakNetDefines.h" +#include "SocketIncludes.h" +#include "DS_ByteQueue.h" +#include "DS_ThreadsafeAllocatingQueue.h" + +#if OPEN_SSL_CLIENT_SUPPORT==1 +#include +#include +#include +#include +#include +#endif + +namespace RakNet +{ +/// Forward declarations +struct RemoteClient; + +/// \internal +/// \brief As the name says, a simple multithreaded TCP server. Used by TelnetTransport +class RAK_DLL_EXPORT TCPInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(TCPInterface) + + TCPInterface(); + virtual ~TCPInterface(); + + /// Starts the TCP server on the indicated port + /// \param[in] port Which port to listen on. + /// \param[in] maxIncomingConnections Max incoming connections we will accept + /// \param[in] maxConnections Max total connections, which should be >= maxIncomingConnections + /// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application. + /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + bool Start(unsigned short port, unsigned short maxIncomingConnections, unsigned short maxConnections=0, int _threadPriority=-99999, unsigned short socketFamily=AF_INET); + + /// Stops the TCP server + void Stop(void); + + /// Connect to the specified host on the specified port + SystemAddress Connect(const char* host, unsigned short remotePort, bool block=true, unsigned short socketFamily=AF_INET); + +#if OPEN_SSL_CLIENT_SUPPORT==1 + /// Start SSL on an existing connection, notified with HasCompletedConnectionAttempt + void StartSSLClient(SystemAddress systemAddress); + + /// Was SSL started on this socket? + bool IsSSLActive(SystemAddress systemAddress); +#endif + + /// Sends a byte stream + void Send( const char *data, unsigned int length, const SystemAddress &systemAddress, bool broadcast ); + + // Sends a concatenated list of byte streams + bool SendList( const char **data, const unsigned int *lengths, const int numParameters, const SystemAddress &systemAddress, bool broadcast ); + + // Get how many bytes are waiting to be sent. If too many, you may want to skip sending + unsigned int GetOutgoingDataBufferSize(SystemAddress systemAddress) const; + + /// Returns if Receive() will return data + bool ReceiveHasPackets( void ); + + /// Returns data received + Packet* Receive( void ); + + /// Disconnects a player/address + void CloseConnection( SystemAddress systemAddress ); + + /// Deallocates a packet returned by Receive + void DeallocatePacket( Packet *packet ); + + /// Fills the array remoteSystems with the SystemAddress of all the systems we are connected to + /// \param[out] remoteSystems An array of SystemAddress structures to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to only get the number of systems we are connected to + /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array + void GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const; + + /// Returns just the number of connections we have + unsigned short GetConnectionCount(void) const; + + /// Has a previous call to connect succeeded? + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasCompletedConnectionAttempt(void); + + /// Has a previous call to connect failed? + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasFailedConnectionAttempt(void); + + /// Queued events of new incoming connections + SystemAddress HasNewIncomingConnection(void); + + /// Queued events of lost connections + SystemAddress HasLostConnection(void); + + /// Return an allocated but empty packet, for custom use + Packet* AllocatePacket(unsigned dataSize); + + // Push a packet back to the queue + virtual void PushBackPacket( Packet *packet, bool pushAtHead ); + + static const char *Base64Map(void) {return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";} + + /// \brief Returns how many bytes were written. + static int Base64Encoding(const char *inputData, int dataLength, char *outputData); + + /// Returns if Start() was called successfully + bool WasStarted(void) const; + +protected: + + bool isStarted, threadRunning; + SOCKET listenSocket; + + DataStructures::Queue headPush, tailPush; + RemoteClient* remoteClients; + int remoteClientsLength; + + // Assuming remoteClients is only used by one thread! + // DataStructures::List remoteClients; + // Use this thread-safe queue to add to remoteClients + // DataStructures::Queue remoteClientsInsertionQueue; + // SimpleMutex remoteClientsInsertionQueueMutex; + + /* + struct OutgoingMessage + { + unsigned char* data; + SystemAddress systemAddress; + bool broadcast; + unsigned int length; + }; + */ +// DataStructures::SingleProducerConsumer outgoingMessages; +// DataStructures::SingleProducerConsumer incomingMessages; +// DataStructures::SingleProducerConsumer newIncomingConnections, lostConnections, requestedCloseConnections; +// DataStructures::SingleProducerConsumer newRemoteClients; +// DataStructures::ThreadsafeAllocatingQueue outgoingMessages; + DataStructures::ThreadsafeAllocatingQueue incomingMessages; + DataStructures::ThreadsafeAllocatingQueue newIncomingConnections, lostConnections, requestedCloseConnections; + DataStructures::ThreadsafeAllocatingQueue newRemoteClients; + SimpleMutex completedConnectionAttemptMutex, failedConnectionAttemptMutex; + DataStructures::Queue completedConnectionAttempts, failedConnectionAttempts; + + int threadPriority; + + DataStructures::List blockingSocketList; + SimpleMutex blockingSocketListMutex; + + + + + + friend RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop); + friend RAK_THREAD_DECLARATION(ConnectionAttemptLoop); + +// void DeleteRemoteClient(RemoteClient *remoteClient, fd_set *exceptionFD); +// void InsertRemoteClient(RemoteClient* remoteClient); + SOCKET SocketConnect(const char* host, unsigned short remotePort, unsigned short socketFamily); + + struct ThisPtrPlusSysAddr + { + TCPInterface *tcpInterface; + SystemAddress systemAddress; + bool useSSL; + unsigned short socketFamily; + }; + +#if OPEN_SSL_CLIENT_SUPPORT==1 + SSL_CTX* ctx; + SSL_METHOD *meth; + DataStructures::ThreadsafeAllocatingQueue startSSL; + DataStructures::List activeSSLConnections; + SimpleMutex sharedSslMutex; +#endif +}; + +/// Stores information about a remote client. +struct RemoteClient +{ + RemoteClient() { +#if OPEN_SSL_CLIENT_SUPPORT==1 + ssl=0; +#endif + isActive=false; + socket=INVALID_SOCKET; + } + SOCKET socket; + SystemAddress systemAddress; + DataStructures::ByteQueue outgoingData; + bool isActive; + SimpleMutex outgoingDataMutex; + SimpleMutex isActiveMutex; + +#if OPEN_SSL_CLIENT_SUPPORT==1 + SSL* ssl; + void InitSSL(SSL_CTX* ctx, SSL_METHOD *meth); + void DisconnectSSL(void); + void FreeSSL(void); + int Send(const char *data, unsigned int length); + int Recv(char *data, const int dataSize); +#else + int Send(const char *data, unsigned int length); + int Recv(char *data, const int dataSize); +#endif + void Reset(void) + { + outgoingDataMutex.Lock(); + outgoingData.Clear(_FILE_AND_LINE_); + outgoingDataMutex.Unlock(); + } + void SetActive(bool a); + void SendOrBuffer(const char **data, const unsigned int *lengths, const int numParameters); +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* + diff --git a/src/raknet/TableSerializer.cpp b/src/raknet/TableSerializer.cpp index 17b5373..d639607 100755 --- a/src/raknet/TableSerializer.cpp +++ b/src/raknet/TableSerializer.cpp @@ -1,320 +1,320 @@ -#include "TableSerializer.h" -#include "DS_Table.h" -#include "BitStream.h" -#include "StringCompressor.h" -#include "RakAssert.h" - -using namespace RakNet; - -void TableSerializer::SerializeTable(DataStructures::Table *in, RakNet::BitStream *out) -{ - DataStructures::Page *cur = in->GetRows().GetListHead(); - const DataStructures::List &columns=in->GetColumns(); - SerializeColumns(in, out); - out->Write((unsigned)in->GetRows().Size()); - unsigned rowIndex; - while (cur) - { - for (rowIndex=0; rowIndex < (unsigned)cur->size; rowIndex++) - { - SerializeRow(cur->data[rowIndex], cur->keys[rowIndex], columns, out); - } - cur=cur->next; - } -} -void TableSerializer::SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out) -{ - const DataStructures::List &columns=in->GetColumns(); - out->Write((unsigned)columns.Size()); - unsigned i; - for (i=0; iEncodeString(columns[i].columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out); - out->Write((unsigned char)columns[i].columnType); - } -} -void TableSerializer::SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out, DataStructures::List &skipColumnIndices) -{ - const DataStructures::List &columns=in->GetColumns(); - out->Write((unsigned)columns.Size()-skipColumnIndices.Size()); - unsigned i; - for (i=0; iEncodeString(columns[i].columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out); - out->Write((unsigned char)columns[i].columnType); - } - } -} -bool TableSerializer::DeserializeTable(unsigned char *serializedTable, unsigned int dataLength, DataStructures::Table *out) -{ - RakNet::BitStream in((unsigned char*) serializedTable, dataLength, false); - return DeserializeTable(&in, out); -} -bool TableSerializer::DeserializeTable(RakNet::BitStream *in, DataStructures::Table *out) -{ - unsigned rowSize; - DeserializeColumns(in,out); - if (in->Read(rowSize)==false || rowSize>100000) - { - RakAssert(0); - return false; // Hacker crash prevention - } - - unsigned rowIndex; - for (rowIndex=0; rowIndex < rowSize; rowIndex++) - { - if (DeserializeRow(in, out)==false) - return false; - } - return true; -} -bool TableSerializer::DeserializeColumns(RakNet::BitStream *in, DataStructures::Table *out) -{ - unsigned columnSize; - unsigned char columnType; - char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; - if (in->Read(columnSize)==false || columnSize > 10000) - return false; // Hacker crash prevention - - out->Clear(); - unsigned i; - for (i=0; iDecodeString(columnName, 32, in); - in->Read(columnType); - out->AddColumn(columnName, (DataStructures::Table::ColumnType)columnType); - } - return true; -} -void TableSerializer::SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out) -{ - unsigned cellIndex; - out->Write(keyIn); - unsigned int columnsSize = columns.Size(); - out->Write(columnsSize); - for (cellIndex=0; cellIndexWrite(cellIndex); - SerializeCell(out, in->cells[cellIndex], columns[cellIndex].columnType); - } -} -void TableSerializer::SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out, DataStructures::List &skipColumnIndices) -{ - unsigned cellIndex; - out->Write(keyIn); - unsigned int numEntries=0; - for (cellIndex=0; cellIndexWrite(numEntries); - - for (cellIndex=0; cellIndexWrite(cellIndex); - SerializeCell(out, in->cells[cellIndex], columns[cellIndex].columnType); - } - } -} -bool TableSerializer::DeserializeRow(RakNet::BitStream *in, DataStructures::Table *out) -{ - const DataStructures::List &columns=out->GetColumns(); - unsigned numEntries; - DataStructures::Table::Row *row; - unsigned key; - if (in->Read(key)==false) - return false; - row=out->AddRow(key); - unsigned int cnt; - in->Read(numEntries); - for (cnt=0; cntRead(cellIndex); - if (DeserializeCell(in, row->cells[cellIndex], columns[cellIndex].columnType)==false) - { - out->RemoveRow(key); - return false; - } - } - return true; -} -void TableSerializer::SerializeCell(RakNet::BitStream *out, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType) -{ - out->Write(cell->isEmpty); - if (cell->isEmpty==false) - { - if (columnType==DataStructures::Table::NUMERIC) - { - out->Write(cell->i); - } - else if (columnType==DataStructures::Table::STRING) - { - StringCompressor::Instance()->EncodeString(cell->c, 65535, out); - } - else if (columnType==DataStructures::Table::POINTER) - { - out->Write(cell->ptr); - } - else - { - // Binary - RakAssert(columnType==DataStructures::Table::BINARY); - RakAssert(cell->i>0); - unsigned binaryLength; - binaryLength=(unsigned)cell->i; - out->Write(binaryLength); - out->WriteAlignedBytes((const unsigned char*) cell->c, (const unsigned int) cell->i); - } - } -} -bool TableSerializer::DeserializeCell(RakNet::BitStream *in, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType) -{ - bool isEmpty=false; - double value; - void *ptr; - char tempString[65535]; - cell->Clear(); - - if (in->Read(isEmpty)==false) - return false; - if (isEmpty==false) - { - if (columnType==DataStructures::Table::NUMERIC) - { - if (in->Read(value)==false) - return false; - cell->Set(value); - } - else if (columnType==DataStructures::Table::STRING) - { - if (StringCompressor::Instance()->DecodeString(tempString, 65535, in)==false) - return false; - cell->Set(tempString); - } - else if (columnType==DataStructures::Table::POINTER) - { - if (in->Read(ptr)==false) - return false; - cell->SetPtr(ptr); - } - else - { - unsigned binaryLength; - // Binary - RakAssert(columnType==DataStructures::Table::BINARY); - if (in->Read(binaryLength)==false || binaryLength > 10000000) - return false; // Sanity check to max binary cell of 10 megabytes - in->AlignReadToByteBoundary(); - if (BITS_TO_BYTES(in->GetNumberOfUnreadBits())<(BitSize_t)binaryLength) - return false; - cell->Set((char*) in->GetData()+BITS_TO_BYTES(in->GetReadOffset()), (int) binaryLength); - in->IgnoreBits(BYTES_TO_BITS((int) binaryLength)); - } - } - return true; -} -void TableSerializer::SerializeFilterQuery(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query) -{ - StringCompressor::Instance()->EncodeString(query->columnName,_TABLE_MAX_COLUMN_NAME_LENGTH,in,0); - in->WriteCompressed(query->columnIndex); - in->Write((unsigned char) query->operation); - in->Write(query->cellValue->isEmpty); - if (query->cellValue->isEmpty==false) - { - in->Write(query->cellValue->i); - in->WriteAlignedBytesSafe((const char*)query->cellValue->c,(const unsigned int)query->cellValue->i,10000000); // Sanity check to max binary cell of 10 megabytes - in->Write(query->cellValue->ptr); - - } -} -bool TableSerializer::DeserializeFilterQuery(RakNet::BitStream *out, DataStructures::Table::FilterQuery *query) -{ - bool b; - RakAssert(query->cellValue); - StringCompressor::Instance()->DecodeString(query->columnName,_TABLE_MAX_COLUMN_NAME_LENGTH,out,0); - out->ReadCompressed(query->columnIndex); - unsigned char op; - out->Read(op); - query->operation=(DataStructures::Table::FilterQueryType) op; - query->cellValue->Clear(); - b=out->Read(query->cellValue->isEmpty); - if (query->cellValue->isEmpty==false) - { - // HACK - cellValue->i is used for integer, character, and binary data. However, for character and binary c will be 0. So use that to determine if the data was integer or not. - out->Read(query->cellValue->i); - unsigned int inputLength; - out->ReadAlignedBytesSafeAlloc(&query->cellValue->c,inputLength,10000000); // Sanity check to max binary cell of 10 megabytes - if (query->cellValue->c) - query->cellValue->i=inputLength; - b=out->Read(query->cellValue->ptr); - } - return b; -} -void TableSerializer::SerializeFilterQueryList(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query, unsigned int numQueries, unsigned int maxQueries) -{ - (void) maxQueries; - in->Write((bool)(query && numQueries>0)); - if (query==0 || numQueries<=0) - return; - - RakAssert(numQueries<=maxQueries); - in->WriteCompressed(numQueries); - unsigned i; - for (i=0; i < numQueries; i++) - { - SerializeFilterQuery(in, query); - } -} -bool TableSerializer::DeserializeFilterQueryList(RakNet::BitStream *out, DataStructures::Table::FilterQuery **query, unsigned int *numQueries, unsigned int maxQueries, int allocateExtraQueries) -{ - bool b, anyQueries=false; - out->Read(anyQueries); - if (anyQueries==false) - { - if (allocateExtraQueries<=0) - *query=0; - else - *query=new DataStructures::Table::FilterQuery[allocateExtraQueries]; - - *numQueries=0; - return true; - } - b=out->ReadCompressed(*numQueries); - if (*numQueries>maxQueries) - { - RakAssert(0); - *numQueries=maxQueries; - } - if (*numQueries==0) - return b; - - *query=new DataStructures::Table::FilterQuery[*numQueries+allocateExtraQueries]; - DataStructures::Table::FilterQuery *queryPtr = *query; - - unsigned i; - for (i=0; i < *numQueries; i++) - { - queryPtr[i].cellValue=new DataStructures::Table::Cell; - b=DeserializeFilterQuery(out, queryPtr+i); - } - - return b; -} -void TableSerializer::DeallocateQueryList(DataStructures::Table::FilterQuery *query, unsigned int numQueries) -{ - if (query==0 || numQueries==0) - return; - - unsigned i; - for (i=0; i < numQueries; i++) - RakNet::OP_DELETE(query[i].cellValue, _FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(query, _FILE_AND_LINE_); -} +#include "TableSerializer.h" +#include "DS_Table.h" +#include "BitStream.h" +#include "StringCompressor.h" +#include "RakAssert.h" + +using namespace RakNet; + +void TableSerializer::SerializeTable(DataStructures::Table *in, RakNet::BitStream *out) +{ + DataStructures::Page *cur = in->GetRows().GetListHead(); + const DataStructures::List &columns=in->GetColumns(); + SerializeColumns(in, out); + out->Write((unsigned)in->GetRows().Size()); + unsigned rowIndex; + while (cur) + { + for (rowIndex=0; rowIndex < (unsigned)cur->size; rowIndex++) + { + SerializeRow(cur->data[rowIndex], cur->keys[rowIndex], columns, out); + } + cur=cur->next; + } +} +void TableSerializer::SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out) +{ + const DataStructures::List &columns=in->GetColumns(); + out->Write((unsigned)columns.Size()); + unsigned i; + for (i=0; iEncodeString(columns[i].columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out); + out->Write((unsigned char)columns[i].columnType); + } +} +void TableSerializer::SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out, DataStructures::List &skipColumnIndices) +{ + const DataStructures::List &columns=in->GetColumns(); + out->Write((unsigned)columns.Size()-skipColumnIndices.Size()); + unsigned i; + for (i=0; iEncodeString(columns[i].columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out); + out->Write((unsigned char)columns[i].columnType); + } + } +} +bool TableSerializer::DeserializeTable(unsigned char *serializedTable, unsigned int dataLength, DataStructures::Table *out) +{ + RakNet::BitStream in((unsigned char*) serializedTable, dataLength, false); + return DeserializeTable(&in, out); +} +bool TableSerializer::DeserializeTable(RakNet::BitStream *in, DataStructures::Table *out) +{ + unsigned rowSize; + DeserializeColumns(in,out); + if (in->Read(rowSize)==false || rowSize>100000) + { + RakAssert(0); + return false; // Hacker crash prevention + } + + unsigned rowIndex; + for (rowIndex=0; rowIndex < rowSize; rowIndex++) + { + if (DeserializeRow(in, out)==false) + return false; + } + return true; +} +bool TableSerializer::DeserializeColumns(RakNet::BitStream *in, DataStructures::Table *out) +{ + unsigned columnSize; + unsigned char columnType; + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; + if (in->Read(columnSize)==false || columnSize > 10000) + return false; // Hacker crash prevention + + out->Clear(); + unsigned i; + for (i=0; iDecodeString(columnName, 32, in); + in->Read(columnType); + out->AddColumn(columnName, (DataStructures::Table::ColumnType)columnType); + } + return true; +} +void TableSerializer::SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out) +{ + unsigned cellIndex; + out->Write(keyIn); + unsigned int columnsSize = columns.Size(); + out->Write(columnsSize); + for (cellIndex=0; cellIndexWrite(cellIndex); + SerializeCell(out, in->cells[cellIndex], columns[cellIndex].columnType); + } +} +void TableSerializer::SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out, DataStructures::List &skipColumnIndices) +{ + unsigned cellIndex; + out->Write(keyIn); + unsigned int numEntries=0; + for (cellIndex=0; cellIndexWrite(numEntries); + + for (cellIndex=0; cellIndexWrite(cellIndex); + SerializeCell(out, in->cells[cellIndex], columns[cellIndex].columnType); + } + } +} +bool TableSerializer::DeserializeRow(RakNet::BitStream *in, DataStructures::Table *out) +{ + const DataStructures::List &columns=out->GetColumns(); + unsigned numEntries; + DataStructures::Table::Row *row; + unsigned key; + if (in->Read(key)==false) + return false; + row=out->AddRow(key); + unsigned int cnt; + in->Read(numEntries); + for (cnt=0; cntRead(cellIndex); + if (DeserializeCell(in, row->cells[cellIndex], columns[cellIndex].columnType)==false) + { + out->RemoveRow(key); + return false; + } + } + return true; +} +void TableSerializer::SerializeCell(RakNet::BitStream *out, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType) +{ + out->Write(cell->isEmpty); + if (cell->isEmpty==false) + { + if (columnType==DataStructures::Table::NUMERIC) + { + out->Write(cell->i); + } + else if (columnType==DataStructures::Table::STRING) + { + StringCompressor::Instance()->EncodeString(cell->c, 65535, out); + } + else if (columnType==DataStructures::Table::POINTER) + { + out->Write(cell->ptr); + } + else + { + // Binary + RakAssert(columnType==DataStructures::Table::BINARY); + RakAssert(cell->i>0); + unsigned binaryLength; + binaryLength=(unsigned)cell->i; + out->Write(binaryLength); + out->WriteAlignedBytes((const unsigned char*) cell->c, (const unsigned int) cell->i); + } + } +} +bool TableSerializer::DeserializeCell(RakNet::BitStream *in, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType) +{ + bool isEmpty=false; + double value; + void *ptr; + char tempString[65535]; + cell->Clear(); + + if (in->Read(isEmpty)==false) + return false; + if (isEmpty==false) + { + if (columnType==DataStructures::Table::NUMERIC) + { + if (in->Read(value)==false) + return false; + cell->Set(value); + } + else if (columnType==DataStructures::Table::STRING) + { + if (StringCompressor::Instance()->DecodeString(tempString, 65535, in)==false) + return false; + cell->Set(tempString); + } + else if (columnType==DataStructures::Table::POINTER) + { + if (in->Read(ptr)==false) + return false; + cell->SetPtr(ptr); + } + else + { + unsigned binaryLength; + // Binary + RakAssert(columnType==DataStructures::Table::BINARY); + if (in->Read(binaryLength)==false || binaryLength > 10000000) + return false; // Sanity check to max binary cell of 10 megabytes + in->AlignReadToByteBoundary(); + if (BITS_TO_BYTES(in->GetNumberOfUnreadBits())<(BitSize_t)binaryLength) + return false; + cell->Set((char*) in->GetData()+BITS_TO_BYTES(in->GetReadOffset()), (int) binaryLength); + in->IgnoreBits(BYTES_TO_BITS((int) binaryLength)); + } + } + return true; +} +void TableSerializer::SerializeFilterQuery(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query) +{ + StringCompressor::Instance()->EncodeString(query->columnName,_TABLE_MAX_COLUMN_NAME_LENGTH,in,0); + in->WriteCompressed(query->columnIndex); + in->Write((unsigned char) query->operation); + in->Write(query->cellValue->isEmpty); + if (query->cellValue->isEmpty==false) + { + in->Write(query->cellValue->i); + in->WriteAlignedBytesSafe((const char*)query->cellValue->c,(const unsigned int)query->cellValue->i,10000000); // Sanity check to max binary cell of 10 megabytes + in->Write(query->cellValue->ptr); + + } +} +bool TableSerializer::DeserializeFilterQuery(RakNet::BitStream *out, DataStructures::Table::FilterQuery *query) +{ + bool b; + RakAssert(query->cellValue); + StringCompressor::Instance()->DecodeString(query->columnName,_TABLE_MAX_COLUMN_NAME_LENGTH,out,0); + out->ReadCompressed(query->columnIndex); + unsigned char op; + out->Read(op); + query->operation=(DataStructures::Table::FilterQueryType) op; + query->cellValue->Clear(); + b=out->Read(query->cellValue->isEmpty); + if (query->cellValue->isEmpty==false) + { + // HACK - cellValue->i is used for integer, character, and binary data. However, for character and binary c will be 0. So use that to determine if the data was integer or not. + out->Read(query->cellValue->i); + unsigned int inputLength; + out->ReadAlignedBytesSafeAlloc(&query->cellValue->c,inputLength,10000000); // Sanity check to max binary cell of 10 megabytes + if (query->cellValue->c) + query->cellValue->i=inputLength; + b=out->Read(query->cellValue->ptr); + } + return b; +} +void TableSerializer::SerializeFilterQueryList(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query, unsigned int numQueries, unsigned int maxQueries) +{ + (void) maxQueries; + in->Write((bool)(query && numQueries>0)); + if (query==0 || numQueries<=0) + return; + + RakAssert(numQueries<=maxQueries); + in->WriteCompressed(numQueries); + unsigned i; + for (i=0; i < numQueries; i++) + { + SerializeFilterQuery(in, query); + } +} +bool TableSerializer::DeserializeFilterQueryList(RakNet::BitStream *out, DataStructures::Table::FilterQuery **query, unsigned int *numQueries, unsigned int maxQueries, int allocateExtraQueries) +{ + bool b, anyQueries=false; + out->Read(anyQueries); + if (anyQueries==false) + { + if (allocateExtraQueries<=0) + *query=0; + else + *query=new DataStructures::Table::FilterQuery[allocateExtraQueries]; + + *numQueries=0; + return true; + } + b=out->ReadCompressed(*numQueries); + if (*numQueries>maxQueries) + { + RakAssert(0); + *numQueries=maxQueries; + } + if (*numQueries==0) + return b; + + *query=new DataStructures::Table::FilterQuery[*numQueries+allocateExtraQueries]; + DataStructures::Table::FilterQuery *queryPtr = *query; + + unsigned i; + for (i=0; i < *numQueries; i++) + { + queryPtr[i].cellValue=new DataStructures::Table::Cell; + b=DeserializeFilterQuery(out, queryPtr+i); + } + + return b; +} +void TableSerializer::DeallocateQueryList(DataStructures::Table::FilterQuery *query, unsigned int numQueries) +{ + if (query==0 || numQueries==0) + return; + + unsigned i; + for (i=0; i < numQueries; i++) + RakNet::OP_DELETE(query[i].cellValue, _FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(query, _FILE_AND_LINE_); +} diff --git a/src/raknet/TableSerializer.h b/src/raknet/TableSerializer.h index fb15bf8..77ca30b 100755 --- a/src/raknet/TableSerializer.h +++ b/src/raknet/TableSerializer.h @@ -1,208 +1,208 @@ -#ifndef __TABLE_SERIALIZER_H -#define __TABLE_SERIALIZER_H - -#include "RakMemoryOverride.h" -#include "DS_Table.h" -#include "Export.h" - -namespace RakNet -{ - class BitStream; -} - -namespace RakNet -{ - -class RAK_DLL_EXPORT TableSerializer -{ -public: - static void SerializeTable(DataStructures::Table *in, RakNet::BitStream *out); - static bool DeserializeTable(unsigned char *serializedTable, unsigned int dataLength, DataStructures::Table *out); - static bool DeserializeTable(RakNet::BitStream *in, DataStructures::Table *out); - static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out); - static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); - static bool DeserializeColumns(RakNet::BitStream *in, DataStructures::Table *out); - static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out); - static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); - static bool DeserializeRow(RakNet::BitStream *in, DataStructures::Table *out); - static void SerializeCell(RakNet::BitStream *out, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); - static bool DeserializeCell(RakNet::BitStream *in, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); - static void SerializeFilterQuery(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query); - // Note that this allocates query->cell->c! - static bool DeserializeFilterQuery(RakNet::BitStream *out, DataStructures::Table::FilterQuery *query); - static void SerializeFilterQueryList(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query, unsigned int numQueries, unsigned int maxQueries); - // Note that this allocates queries, cells, and query->cell->c!. Use DeallocateQueryList to free. - static bool DeserializeFilterQueryList(RakNet::BitStream *out, DataStructures::Table::FilterQuery **query, unsigned int *numQueries, unsigned int maxQueries, int allocateExtraQueries=0); - static void DeallocateQueryList(DataStructures::Table::FilterQuery *query, unsigned int numQueries); -}; - -} // namespace RakNet - -#endif - -// Test code for the table -/* -#include "LightweightDatabaseServer.h" -#include "LightweightDatabaseClient.h" -#include "TableSerializer.h" -#include "BitStream.h" -#include "StringCompressor.h" -#include "DS_Table.h" -void main(void) -{ - DataStructures::Table table; - DataStructures::Table::Row *row; - unsigned int dummydata=12345; - - // Add columns Name (string), IP (binary), score (int), and players (int). - table.AddColumn("Name", DataStructures::Table::STRING); - table.AddColumn("IP", DataStructures::Table::BINARY); - table.AddColumn("Score", DataStructures::Table::NUMERIC); - table.AddColumn("Players", DataStructures::Table::NUMERIC); - table.AddColumn("Empty Test Column", DataStructures::Table::STRING); - RakAssert(table.GetColumnCount()==5); - row=table.AddRow(0); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,5); - row->UpdateCell(3,10); - //row->UpdateCell(4,"should be unique"); - - row=table.AddRow(1); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,5); - row->UpdateCell(3,15); - - row=table.AddRow(2); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,5); - row->UpdateCell(3,20); - - row=table.AddRow(3); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,15); - row->UpdateCell(3,5); - row->UpdateCell(4,"col index 4"); - - row=table.AddRow(4); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - //row->UpdateCell(2,25); - row->UpdateCell(3,30); - //row->UpdateCell(4,"should be unique"); - - row=table.AddRow(5); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - //row->UpdateCell(2,25); - row->UpdateCell(3,5); - //row->UpdateCell(4,"should be unique"); - - row=table.AddRow(6); - RakAssert(row); - row->UpdateCell(0,"Kevin Jenkins"); - row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); - row->UpdateCell(2,35); - //row->UpdateCell(3,40); - //row->UpdateCell(4,"should be unique"); - - row=table.AddRow(7); - RakAssert(row); - row->UpdateCell(0,"Bob Jenkins"); - - row=table.AddRow(8); - RakAssert(row); - row->UpdateCell(0,"Zack Jenkins"); - - // Test multi-column sorting - DataStructures::Table::Row *rows[30]; - DataStructures::Table::SortQuery queries[4]; - queries[0].columnIndex=0; - queries[0].operation=DataStructures::Table::QS_INCREASING_ORDER; - queries[1].columnIndex=1; - queries[1].operation=DataStructures::Table::QS_INCREASING_ORDER; - queries[2].columnIndex=2; - queries[2].operation=DataStructures::Table::QS_INCREASING_ORDER; - queries[3].columnIndex=3; - queries[3].operation=DataStructures::Table::QS_DECREASING_ORDER; - table.SortTable(queries, 4, rows); - unsigned i; - char out[256]; - RAKNET_DEBUG_PRINTF("Sort: Ascending except for column index 3\n"); - for (i=0; i < table.GetRowCount(); i++) - { - table.PrintRow(out,256,',',true, rows[i]); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - - // Test query: - // Don't return column 3, and swap columns 0 and 2 - unsigned columnsToReturn[4]; - columnsToReturn[0]=2; - columnsToReturn[1]=1; - columnsToReturn[2]=0; - columnsToReturn[3]=4; - DataStructures::Table resultsTable; - table.QueryTable(columnsToReturn,4,0,0,&resultsTable); - RAKNET_DEBUG_PRINTF("Query: Don't return column 3, and swap columns 0 and 2:\n"); - for (i=0; i < resultsTable.GetRowCount(); i++) - { - resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - - // Test filter: - // Only return rows with column index 4 empty - DataStructures::Table::FilterQuery inclusionFilters[3]; - inclusionFilters[0].columnIndex=4; - inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; - // inclusionFilters[0].cellValue; // Unused for IS_EMPTY - table.QueryTable(0,0,inclusionFilters,1,&resultsTable); - RAKNET_DEBUG_PRINTF("Filter: Only return rows with column index 4 empty:\n"); - for (i=0; i < resultsTable.GetRowCount(); i++) - { - resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - - // Column 5 empty and column 0 == Kevin Jenkins - inclusionFilters[0].columnIndex=4; - inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; - inclusionFilters[1].columnIndex=0; - inclusionFilters[1].operation=DataStructures::Table::QF_EQUAL; - inclusionFilters[1].cellValue.Set("Kevin Jenkins"); - table.QueryTable(0,0,inclusionFilters,2,&resultsTable); - RAKNET_DEBUG_PRINTF("Filter: Column 5 empty and column 0 == Kevin Jenkins:\n"); - for (i=0; i < resultsTable.GetRowCount(); i++) - { - resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - - RakNet::BitStream bs; - RAKNET_DEBUG_PRINTF("PreSerialize:\n"); - for (i=0; i < table.GetRowCount(); i++) - { - table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - StringCompressor::AddReference(); - TableSerializer::Serialize(&table, &bs); - TableSerializer::Deserialize(&bs, &table); - StringCompressor::RemoveReference(); - RAKNET_DEBUG_PRINTF("PostDeserialize:\n"); - for (i=0; i < table.GetRowCount(); i++) - { - table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); - RAKNET_DEBUG_PRINTF("%s\n", out); - } - int a=5; -} -*/ +#ifndef __TABLE_SERIALIZER_H +#define __TABLE_SERIALIZER_H + +#include "RakMemoryOverride.h" +#include "DS_Table.h" +#include "Export.h" + +namespace RakNet +{ + class BitStream; +} + +namespace RakNet +{ + +class RAK_DLL_EXPORT TableSerializer +{ +public: + static void SerializeTable(DataStructures::Table *in, RakNet::BitStream *out); + static bool DeserializeTable(unsigned char *serializedTable, unsigned int dataLength, DataStructures::Table *out); + static bool DeserializeTable(RakNet::BitStream *in, DataStructures::Table *out); + static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out); + static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); + static bool DeserializeColumns(RakNet::BitStream *in, DataStructures::Table *out); + static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out); + static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, const DataStructures::List &columns, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); + static bool DeserializeRow(RakNet::BitStream *in, DataStructures::Table *out); + static void SerializeCell(RakNet::BitStream *out, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); + static bool DeserializeCell(RakNet::BitStream *in, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); + static void SerializeFilterQuery(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query); + // Note that this allocates query->cell->c! + static bool DeserializeFilterQuery(RakNet::BitStream *out, DataStructures::Table::FilterQuery *query); + static void SerializeFilterQueryList(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query, unsigned int numQueries, unsigned int maxQueries); + // Note that this allocates queries, cells, and query->cell->c!. Use DeallocateQueryList to free. + static bool DeserializeFilterQueryList(RakNet::BitStream *out, DataStructures::Table::FilterQuery **query, unsigned int *numQueries, unsigned int maxQueries, int allocateExtraQueries=0); + static void DeallocateQueryList(DataStructures::Table::FilterQuery *query, unsigned int numQueries); +}; + +} // namespace RakNet + +#endif + +// Test code for the table +/* +#include "LightweightDatabaseServer.h" +#include "LightweightDatabaseClient.h" +#include "TableSerializer.h" +#include "BitStream.h" +#include "StringCompressor.h" +#include "DS_Table.h" +void main(void) +{ + DataStructures::Table table; + DataStructures::Table::Row *row; + unsigned int dummydata=12345; + + // Add columns Name (string), IP (binary), score (int), and players (int). + table.AddColumn("Name", DataStructures::Table::STRING); + table.AddColumn("IP", DataStructures::Table::BINARY); + table.AddColumn("Score", DataStructures::Table::NUMERIC); + table.AddColumn("Players", DataStructures::Table::NUMERIC); + table.AddColumn("Empty Test Column", DataStructures::Table::STRING); + RakAssert(table.GetColumnCount()==5); + row=table.AddRow(0); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,5); + row->UpdateCell(3,10); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(1); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,5); + row->UpdateCell(3,15); + + row=table.AddRow(2); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,5); + row->UpdateCell(3,20); + + row=table.AddRow(3); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,15); + row->UpdateCell(3,5); + row->UpdateCell(4,"col index 4"); + + row=table.AddRow(4); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + //row->UpdateCell(2,25); + row->UpdateCell(3,30); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(5); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + //row->UpdateCell(2,25); + row->UpdateCell(3,5); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(6); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,35); + //row->UpdateCell(3,40); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(7); + RakAssert(row); + row->UpdateCell(0,"Bob Jenkins"); + + row=table.AddRow(8); + RakAssert(row); + row->UpdateCell(0,"Zack Jenkins"); + + // Test multi-column sorting + DataStructures::Table::Row *rows[30]; + DataStructures::Table::SortQuery queries[4]; + queries[0].columnIndex=0; + queries[0].operation=DataStructures::Table::QS_INCREASING_ORDER; + queries[1].columnIndex=1; + queries[1].operation=DataStructures::Table::QS_INCREASING_ORDER; + queries[2].columnIndex=2; + queries[2].operation=DataStructures::Table::QS_INCREASING_ORDER; + queries[3].columnIndex=3; + queries[3].operation=DataStructures::Table::QS_DECREASING_ORDER; + table.SortTable(queries, 4, rows); + unsigned i; + char out[256]; + RAKNET_DEBUG_PRINTF("Sort: Ascending except for column index 3\n"); + for (i=0; i < table.GetRowCount(); i++) + { + table.PrintRow(out,256,',',true, rows[i]); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + // Test query: + // Don't return column 3, and swap columns 0 and 2 + unsigned columnsToReturn[4]; + columnsToReturn[0]=2; + columnsToReturn[1]=1; + columnsToReturn[2]=0; + columnsToReturn[3]=4; + DataStructures::Table resultsTable; + table.QueryTable(columnsToReturn,4,0,0,&resultsTable); + RAKNET_DEBUG_PRINTF("Query: Don't return column 3, and swap columns 0 and 2:\n"); + for (i=0; i < resultsTable.GetRowCount(); i++) + { + resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + // Test filter: + // Only return rows with column index 4 empty + DataStructures::Table::FilterQuery inclusionFilters[3]; + inclusionFilters[0].columnIndex=4; + inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; + // inclusionFilters[0].cellValue; // Unused for IS_EMPTY + table.QueryTable(0,0,inclusionFilters,1,&resultsTable); + RAKNET_DEBUG_PRINTF("Filter: Only return rows with column index 4 empty:\n"); + for (i=0; i < resultsTable.GetRowCount(); i++) + { + resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + // Column 5 empty and column 0 == Kevin Jenkins + inclusionFilters[0].columnIndex=4; + inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; + inclusionFilters[1].columnIndex=0; + inclusionFilters[1].operation=DataStructures::Table::QF_EQUAL; + inclusionFilters[1].cellValue.Set("Kevin Jenkins"); + table.QueryTable(0,0,inclusionFilters,2,&resultsTable); + RAKNET_DEBUG_PRINTF("Filter: Column 5 empty and column 0 == Kevin Jenkins:\n"); + for (i=0; i < resultsTable.GetRowCount(); i++) + { + resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + RakNet::BitStream bs; + RAKNET_DEBUG_PRINTF("PreSerialize:\n"); + for (i=0; i < table.GetRowCount(); i++) + { + table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + StringCompressor::AddReference(); + TableSerializer::Serialize(&table, &bs); + TableSerializer::Deserialize(&bs, &table); + StringCompressor::RemoveReference(); + RAKNET_DEBUG_PRINTF("PostDeserialize:\n"); + for (i=0; i < table.GetRowCount(); i++) + { + table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + int a=5; +} +*/ diff --git a/src/raknet/TeamBalancer.cpp b/src/raknet/TeamBalancer.cpp index 1c1eb0d..90cc9e0 100755 --- a/src/raknet/TeamBalancer.cpp +++ b/src/raknet/TeamBalancer.cpp @@ -1,874 +1,874 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TeamBalancer==1 - -#include "TeamBalancer.h" -#include "BitStream.h" -#include "MessageIdentifiers.h" -#include "RakPeerInterface.h" -#include "Rand.h" - -using namespace RakNet; - -enum TeamBalancerOperations -{ - ID_STATUS_UPDATE_TO_NEW_HOST, - ID_CANCEL_TEAM_REQUEST, - ID_REQUEST_ANY_TEAM, - ID_REQUEST_SPECIFIC_TEAM -}; - -STATIC_FACTORY_DEFINITIONS(TeamBalancer,TeamBalancer); - -TeamBalancer::TeamBalancer() -{ - defaultAssigmentAlgorithm=SMALLEST_TEAM; - forceTeamsToBeEven=false; - lockTeams=false; - hostGuid=UNASSIGNED_RAKNET_GUID; -} -TeamBalancer::~TeamBalancer() -{ - -} -void TeamBalancer::SetTeamSizeLimit(TeamId team, unsigned short limit) -{ - teamLimits.Replace(limit,0,team,_FILE_AND_LINE_); - if (teamLimits.Size() > teamMemberCounts.Size()) - teamMemberCounts.Replace(0,0,teamLimits.Size()-1,_FILE_AND_LINE_); -} -void TeamBalancer::SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa) -{ - // Just update the default. Currently active teams are not affected. - defaultAssigmentAlgorithm=daa; -} -void TeamBalancer::SetForceEvenTeams(bool force) -{ - // Set flag to indicate that teams should be even. - forceTeamsToBeEven=force; - - // If teams are locked, just return. - if (lockTeams==true) - return; - - if (forceTeamsToBeEven==true) - { - // Run the even team algorithm - EvenTeams(); - } -} -void TeamBalancer::SetLockTeams(bool lock) -{ - if (lock==lockTeams) - return; - - // Set flag to indicate that teams can no longer be changed. - lockTeams=lock; - - // If lock is false, and teams were set to be forced as even, then run through the even team algorithm - if (lockTeams==false) - { - // Process even swaps - TeamId i,j; - for (i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].requestedTeam!=UNASSIGNED_TEAM_ID) - { - for (j=i+1; j < teamMembers.Size(); j++) - { - if (teamMembers[j].requestedTeam==teamMembers[i].currentTeam && - teamMembers[i].requestedTeam==teamMembers[j].currentTeam) - { - SwapTeamMembersByRequest(i,j); - NotifyTeamAssigment(i); - NotifyTeamAssigment(j); - } - } - } - } - - if (forceTeamsToBeEven==true) - { - EvenTeams(); - } - else - { - // Process requested team changes - // Process movement while not full - for (i=0; i < teamMembers.Size(); i++) - { - TeamId requestedTeam = teamMembers[i].requestedTeam; - if (requestedTeam!=UNASSIGNED_TEAM_ID) - { - if (teamMemberCounts[requestedTeam]Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); -} -void TeamBalancer::CancelRequestSpecificTeam(NetworkID memberId) -{ - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==memberId) - { - myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; - - // Send packet to the host to remove our request flag. - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); - bsOut.Write((MessageID)ID_CANCEL_TEAM_REQUEST); - bsOut.Write(memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); - - return; - } - } -} -void TeamBalancer::RequestAnyTeam(NetworkID memberId) -{ - bool foundMatch=false; - - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==memberId) - { - foundMatch=true; - if (myTeamMembers[i].currentTeam!=UNASSIGNED_TEAM_ID) - return; - else - myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; - break; - } - } - - if (foundMatch==false) - { - MyTeamMembers mtm; - mtm.currentTeam=UNASSIGNED_TEAM_ID; - mtm.memberId=memberId; - mtm.requestedTeam=UNASSIGNED_TEAM_ID; - myTeamMembers.Push(mtm, _FILE_AND_LINE_); - } - - // Else send to the current host that we need a team. - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); - bsOut.Write((MessageID)ID_REQUEST_ANY_TEAM); - bsOut.Write(memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); -} -TeamId TeamBalancer::GetMyTeam(NetworkID memberId) const -{ - // Return team returned by last ID_TEAM_BALANCER_TEAM_ASSIGNED packet - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==memberId) - { - return myTeamMembers[i].currentTeam; - } - } - - return UNASSIGNED_TEAM_ID; -} -void TeamBalancer::DeleteMember(NetworkID memberId) -{ - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==memberId) - { - myTeamMembers.RemoveAtIndexFast(i); - break; - } - } - - for (unsigned int i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].memberId==memberId) - { - RemoveTeamMember(i); - break; - } - } -} -PluginReceiveResult TeamBalancer::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_FCM2_NEW_HOST: - { - hostGuid=packet->guid; - - if (myTeamMembers.Size()>0) - { - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); - bsOut.Write((MessageID)ID_STATUS_UPDATE_TO_NEW_HOST); - - bsOut.WriteCasted(myTeamMembers.Size()); - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - bsOut.Write(myTeamMembers[i].memberId); - bsOut.Write(myTeamMembers[i].currentTeam); - bsOut.Write(myTeamMembers[i].requestedTeam); - } - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); - } - } - break; - case ID_TEAM_BALANCER_INTERNAL: - { - if (packet->length>=2) - { - switch (packet->data[1]) - { - case ID_STATUS_UPDATE_TO_NEW_HOST: - OnStatusUpdateToNewHost(packet); - break; - case ID_CANCEL_TEAM_REQUEST: - OnCancelTeamRequest(packet); - break; - case ID_REQUEST_ANY_TEAM: - OnRequestAnyTeam(packet); - break; - case ID_REQUEST_SPECIFIC_TEAM: - OnRequestSpecificTeam(packet); - break; - } - } - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - case ID_TEAM_BALANCER_TEAM_ASSIGNED: - { - return OnTeamAssigned(packet); - } - - case ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING: - { - return OnRequestedTeamChangePending(packet); - } - - case ID_TEAM_BALANCER_TEAMS_LOCKED: - { - return OnTeamsLocked(packet); - } - } - - // Got RequestSpecificTeam - // If teams are locked - // - If this user already has a team, return ID_TEAM_BALANCER_TEAMS_LOCKED - // - This user does not already have a team. Assign a team as if the user called RequestAnyTeam(), with a preference for the requested team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED once the team has been assigned. - // If teams are not locked - // - If even team balancing is on, only assign this user if this would not cause teams to be unbalanced. If teams WOULD be unbalanced, then flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING - // - If the destination team is full, flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING - // - Else, join this team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED - - // Got RequestAnyTeam - // Put user on a team following the algorithm. No team is set as preferred. - - return RR_CONTINUE_PROCESSING; -} -void TeamBalancer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) systemAddress; - (void) lostConnectionReason; - - RemoveByGuid(rakNetGUID); -} -void TeamBalancer::OnAttach(void) -{ - hostGuid = rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -} -void TeamBalancer::RemoveByGuid(RakNetGUID rakNetGUID) -{ - // If we are the host, and the closed connection has a team, and teams are not locked: - if (WeAreHost()) - { - unsigned int droppedMemberIndex=0; - while (droppedMemberIndex < teamMembers.Size()) - { - if (teamMembers[droppedMemberIndex].memberGuid==rakNetGUID) - { - TeamId droppedTeam = teamMembers[droppedMemberIndex].currentTeam; - RemoveTeamMember(droppedMemberIndex); - if (lockTeams==false) - { - if (forceTeamsToBeEven) - { - // - teams were forced to be even, then run the even team algorithm - EvenTeams(); - } - else - { - // - teams were NOT forced to be even, and the team the dropped player on was full, then move users wanting to join that team (if any) - if (teamMemberCounts[ droppedTeam ]==teamLimits[ droppedTeam ]-1) - { - MoveMemberThatWantsToJoinTeam(droppedTeam); - } - } - } - } - else - { - droppedMemberIndex++; - } - } - } -} -void TeamBalancer::OnStatusUpdateToNewHost(Packet *packet) -{ - if (WeAreHost()==false) - return; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - uint8_t requestedTeamChangeListSize; - bsIn.Read(requestedTeamChangeListSize); - TeamMember tm; - tm.memberGuid=packet->guid; - for (uint8_t i=0; i < requestedTeamChangeListSize; i++) - { - bsIn.Read(tm.memberId); - bsIn.Read(tm.currentTeam); - bsIn.Read(tm.requestedTeam); - - if (tm.currentTeam!=UNASSIGNED_TEAM_ID && tm.currentTeam>teamLimits.Size()) - { - RakAssert("Current team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0); - return; - } - - if (tm.requestedTeam!=UNASSIGNED_TEAM_ID && tm.requestedTeam>teamLimits.Size()) - { - RakAssert("Requested team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0); - return; - } - - if (tm.currentTeam==UNASSIGNED_TEAM_ID && tm.requestedTeam==UNASSIGNED_TEAM_ID) - return; - - unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid); - if (memberIndex==(unsigned int) -1) - { - // Add this system (by GUID) to the list of members if he is not already there - // Also update his requested team flag. - // Do not process balancing on requested teams, since we don't necessarily have all data from all systems yet and hopefully the state during the host migration was stable. - if (tm.currentTeam==UNASSIGNED_TEAM_ID) - { - // Assign a default team, then add team member - if (tm.requestedTeam==UNASSIGNED_TEAM_ID) - { - // Assign a default team - tm.currentTeam=GetNextDefaultTeam(); - } - else - { - // Assign to requested team if possible. Otherwise, assign to a default team - if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false) - { - tm.currentTeam=tm.requestedTeam; - } - else - { - tm.currentTeam=GetNextDefaultTeam(); - } - } - } - - if (tm.currentTeam==UNASSIGNED_TEAM_ID) - { - RakAssert("Too many members asking for teams!" && 0); - return; - } - NotifyTeamAssigment(AddTeamMember(tm)); - } - } -} -void TeamBalancer::OnCancelTeamRequest(Packet *packet) -{ - if (WeAreHost()==false) - return; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - NetworkID memberId; - bsIn.Read(memberId); - - unsigned int memberIndex = GetMemberIndex(memberId, packet->guid); - if (memberIndex!=(unsigned int)-1) - teamMembers[memberIndex].requestedTeam=UNASSIGNED_TEAM_ID; -} -void TeamBalancer::OnRequestAnyTeam(Packet *packet) -{ - if (WeAreHost()==false) - return; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - NetworkID memberId; - bsIn.Read(memberId); - - unsigned int memberIndex = GetMemberIndex(memberId, packet->guid); - if (memberIndex==(unsigned int)-1) - { - TeamMember tm; - tm.currentTeam=GetNextDefaultTeam(); - tm.requestedTeam=UNASSIGNED_TEAM_ID; - tm.memberGuid=packet->guid; - tm.memberId=memberId; - if (tm.currentTeam==UNASSIGNED_TEAM_ID) - { - RakAssert("Too many members asking for teams!" && 0); - return; - } - NotifyTeamAssigment(AddTeamMember(tm)); - } -} -void TeamBalancer::OnRequestSpecificTeam(Packet *packet) -{ - if (WeAreHost()==false) - return; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(2); - TeamMember tm; - bsIn.Read(tm.memberId); - bsIn.Read(tm.requestedTeam); - - unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid); - if (tm.requestedTeam==UNASSIGNED_TEAM_ID) - { - NotifyNoTeam(tm.memberId, packet->guid); - RemoveTeamMember(memberIndex); - return; - } - - if (tm.requestedTeam>teamLimits.Size()) - { - RakAssert("Requested team out of range in TeamBalancer::OnRequestSpecificTeam" && 0); - return; - } - if (memberIndex==(unsigned int) -1) - { - tm.memberGuid=packet->guid; - - // Assign to requested team if possible. Otherwise, assign to a default team - if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false) - { - tm.currentTeam=tm.requestedTeam; - tm.requestedTeam=UNASSIGNED_TEAM_ID; - } - else - { - tm.currentTeam=GetNextDefaultTeam(); - } - if (tm.currentTeam==UNASSIGNED_TEAM_ID) - { - RakAssert("Too many members asking for teams!" && 0); - return; - } - NotifyTeamAssigment(AddTeamMember(tm)); - } - else - { - teamMembers[memberIndex].requestedTeam=tm.requestedTeam; - TeamId oldTeamThisUserWasOn = teamMembers[memberIndex].currentTeam; - - if (lockTeams) - { - NotifyTeamsLocked(packet->guid, tm.requestedTeam); - return; - } - - // Assign to requested team if possible. Otherwise, assign to a default team - if (TeamsWouldBeEvenOnSwitch(tm.requestedTeam,oldTeamThisUserWasOn)==true) - { - SwitchMemberTeam(memberIndex,tm.requestedTeam); - NotifyTeamAssigment(memberIndex); - } - else - { - // If someone wants to join this user's old team, and we want to join their team, they can swap - unsigned int swappableMemberIndex; - for (swappableMemberIndex=0; swappableMemberIndex < teamMembers.Size(); swappableMemberIndex++) - { - if (teamMembers[swappableMemberIndex].currentTeam==tm.requestedTeam && teamMembers[swappableMemberIndex].requestedTeam==oldTeamThisUserWasOn) - break; - } - - if (swappableMemberIndex!=teamMembers.Size()) - { - SwapTeamMembersByRequest(memberIndex,swappableMemberIndex); - NotifyTeamAssigment(memberIndex); - NotifyTeamAssigment(swappableMemberIndex); - } - else - { - // Full or would not be even - NotifyTeamSwitchPending(packet->guid, tm.requestedTeam, tm.memberId); - } - } - } -} -unsigned int TeamBalancer::GetMemberIndex(NetworkID memberId, RakNetGUID guid) const -{ - for (unsigned int i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].memberGuid==guid && teamMembers[i].memberId==memberId) - return i; - } - return (unsigned int) -1; -} -unsigned int TeamBalancer::AddTeamMember(const TeamMember &tm) -{ - if (tm.currentTeam>teamLimits.Size()) - { - RakAssert("TeamBalancer::AddTeamMember team index out of bounds" && 0); - return (unsigned int) -1; - } - - RakAssert(tm.currentTeam!=UNASSIGNED_TEAM_ID); - - teamMembers.Push(tm,_FILE_AND_LINE_); - if (teamMemberCounts.Size() overpopulatedTeams; - TeamId teamMemberCountsIndex; - unsigned int memberIndexToSwitch; - for (teamMemberCountsIndex=0; teamMemberCountsIndex0); - memberIndexToSwitch=GetMemberIndexToSwitchTeams(overpopulatedTeams,teamMemberCountsIndex); - RakAssert(memberIndexToSwitch!=(unsigned int)-1); - SwitchMemberTeam(memberIndexToSwitch,teamMemberCountsIndex); - // Tell this member he switched teams - NotifyTeamAssigment(memberIndexToSwitch); - } - } -} -unsigned int TeamBalancer::GetMemberIndexToSwitchTeams(const DataStructures::List &sourceTeamNumbers, TeamId targetTeamNumber) -{ - DataStructures::List preferredSwapIndices; - DataStructures::List potentialSwapIndices; - unsigned int i,j; - for (j=0; j < sourceTeamNumbers.Size(); j++) - { - RakAssert(sourceTeamNumbers[j]!=targetTeamNumber); - for (i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].currentTeam==sourceTeamNumbers[j]) - { - if (teamMembers[i].requestedTeam==targetTeamNumber) - preferredSwapIndices.Push(i,_FILE_AND_LINE_); - else - potentialSwapIndices.Push(i,_FILE_AND_LINE_); - } - } - } - - if (preferredSwapIndices.Size()>0) - { - return preferredSwapIndices[ randomMT() % preferredSwapIndices.Size() ]; - } - else if (potentialSwapIndices.Size()>0) - { - return potentialSwapIndices[ randomMT() % potentialSwapIndices.Size() ]; - } - else - { - return (unsigned int) -1; - } -} -void TeamBalancer::SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam) -{ - teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]=teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]-1; - teamMemberCounts[ destinationTeam ]=teamMemberCounts[ destinationTeam ]+1; - teamMembers[teamMemberIndex].currentTeam=destinationTeam; - if (teamMembers[teamMemberIndex].requestedTeam==destinationTeam) - teamMembers[teamMemberIndex].requestedTeam=UNASSIGNED_TEAM_ID; -} -void TeamBalancer::GetOverpopulatedTeams(DataStructures::List &overpopulatedTeams, int maxTeamSize) -{ - overpopulatedTeams.Clear(true,_FILE_AND_LINE_); - for (TeamId i=0; i < teamMemberCounts.Size(); i++) - { - if (teamMemberCounts[i]>=maxTeamSize) - overpopulatedTeams.Push(i,_FILE_AND_LINE_); - } -} -void TeamBalancer::NotifyTeamAssigment(unsigned int teamMemberIndex) -{ - RakAssert(teamMemberIndex < teamMembers.Size()); - if (teamMemberIndex>=teamMembers.Size()) - return; - - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED); - bsOut.Write(teamMembers[teamMemberIndex].currentTeam); - bsOut.Write(teamMembers[teamMemberIndex].memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,teamMembers[teamMemberIndex].memberGuid,false); -} -bool TeamBalancer::WeAreHost(void) const -{ - return hostGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); -} -PluginReceiveResult TeamBalancer::OnTeamAssigned(Packet *packet) -{ - if (packet->guid!=hostGuid) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(1); - - MyTeamMembers mtm; - bsIn.Read(mtm.currentTeam); - bsIn.Read(mtm.memberId); - mtm.requestedTeam=UNASSIGNED_TEAM_ID; - - bool foundMatch=false; - for (unsigned int i=0; i < myTeamMembers.Size(); i++) - { - if (myTeamMembers[i].memberId==mtm.memberId) - { - foundMatch=true; - if (myTeamMembers[i].requestedTeam==mtm.currentTeam) - myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; - myTeamMembers[i].currentTeam=mtm.currentTeam; - break; - } - } - - if (foundMatch==false) - { - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - - return RR_CONTINUE_PROCESSING; -} -PluginReceiveResult TeamBalancer::OnRequestedTeamChangePending(Packet *packet) -{ - if (packet->guid!=hostGuid) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - return RR_CONTINUE_PROCESSING; -} -PluginReceiveResult TeamBalancer::OnTeamsLocked(Packet *packet) -{ - if (packet->guid!=hostGuid) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - return RR_CONTINUE_PROCESSING; -} -TeamId TeamBalancer::GetNextDefaultTeam(void) -{ - // Accounting for team balancing and team limits, get the team a player should be placed on - switch (defaultAssigmentAlgorithm) - { - case SMALLEST_TEAM: - { - return GetSmallestNonFullTeam(); - } - - case FILL_IN_ORDER: - { - return GetFirstNonFullTeam(); - } - - default: - { - RakAssert("TeamBalancer::GetNextDefaultTeam unknown algorithm enumeration" && 0); - return UNASSIGNED_TEAM_ID; - } - } -} -bool TeamBalancer::TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize) -{ - // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it? - if (teamMemberCounts[teamId]>=teamLimits[teamId]) - { - return true; - } - - if (forceTeamsToBeEven) - { - int allowedLimit = teamMemberSize/teamLimits.Size() + 1; - return teamMemberCounts[teamId]>=allowedLimit; - } - - return false; -} -bool TeamBalancer::TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize) -{ - if (forceTeamsToBeEven) - { - unsigned int minMembersOnASingleTeam = (teamMemberSize-1)/teamLimits.Size(); - return teamMemberCounts[teamId]<=minMembersOnASingleTeam; - } - return false; -} -TeamId TeamBalancer::GetSmallestNonFullTeam(void) const -{ - TeamId idx; - unsigned long smallestTeamCount=MAX_UNSIGNED_LONG; - TeamId smallestTeamIndex = UNASSIGNED_TEAM_ID; - for (idx=0; idx < teamMemberCounts.Size(); idx++) - { - if (teamMemberCounts[idx] membersThatWantToJoinTheTeam; - for (TeamId i=0; i < teamMembers.Size(); i++) - { - if (teamMembers[i].requestedTeam==teamId) - membersThatWantToJoinTheTeam.Push(i,_FILE_AND_LINE_); - } - - if (membersThatWantToJoinTheTeam.Size()>0) - { - TeamId oldTeam; - unsigned int swappedMemberIndex = membersThatWantToJoinTheTeam[ randomMT() % membersThatWantToJoinTheTeam.Size() ]; - oldTeam=teamMembers[swappedMemberIndex].currentTeam; - SwitchMemberTeam(swappedMemberIndex,teamId); - NotifyTeamAssigment(swappedMemberIndex); - return oldTeam; - } - return UNASSIGNED_TEAM_ID; -} -void TeamBalancer::NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam) -{ - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAMS_LOCKED); - bsOut.Write(requestedTeam); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); -} -void TeamBalancer::NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam, NetworkID memberId) -{ - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING); - bsOut.Write(requestedTeam); - bsOut.Write(memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); -} -void TeamBalancer::SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2) -{ - TeamId index1Team = teamMembers[memberIndex1].currentTeam; - teamMembers[memberIndex1].currentTeam=teamMembers[memberIndex2].currentTeam; - teamMembers[memberIndex2].currentTeam=index1Team; - teamMembers[memberIndex1].requestedTeam=UNASSIGNED_TEAM_ID; - teamMembers[memberIndex2].requestedTeam=UNASSIGNED_TEAM_ID; -} -void TeamBalancer::NotifyNoTeam(NetworkID memberId, RakNetGUID target) -{ - BitStream bsOut; - bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED); - bsOut.Write((unsigned char)UNASSIGNED_TEAM_ID); - bsOut.Write(memberId); - rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); -} -bool TeamBalancer::TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2) -{ - RakAssert(teamMembers.Size()!=0); - return TeamWouldBeOverpopulatedOnAddition(t1, teamMembers.Size()-1)==false && - TeamWouldBeUnderpopulatedOnLeave(t2, teamMembers.Size()-1)==false; -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TeamBalancer==1 + +#include "TeamBalancer.h" +#include "BitStream.h" +#include "MessageIdentifiers.h" +#include "RakPeerInterface.h" +#include "Rand.h" + +using namespace RakNet; + +enum TeamBalancerOperations +{ + ID_STATUS_UPDATE_TO_NEW_HOST, + ID_CANCEL_TEAM_REQUEST, + ID_REQUEST_ANY_TEAM, + ID_REQUEST_SPECIFIC_TEAM +}; + +STATIC_FACTORY_DEFINITIONS(TeamBalancer,TeamBalancer); + +TeamBalancer::TeamBalancer() +{ + defaultAssigmentAlgorithm=SMALLEST_TEAM; + forceTeamsToBeEven=false; + lockTeams=false; + hostGuid=UNASSIGNED_RAKNET_GUID; +} +TeamBalancer::~TeamBalancer() +{ + +} +void TeamBalancer::SetTeamSizeLimit(TeamId team, unsigned short limit) +{ + teamLimits.Replace(limit,0,team,_FILE_AND_LINE_); + if (teamLimits.Size() > teamMemberCounts.Size()) + teamMemberCounts.Replace(0,0,teamLimits.Size()-1,_FILE_AND_LINE_); +} +void TeamBalancer::SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa) +{ + // Just update the default. Currently active teams are not affected. + defaultAssigmentAlgorithm=daa; +} +void TeamBalancer::SetForceEvenTeams(bool force) +{ + // Set flag to indicate that teams should be even. + forceTeamsToBeEven=force; + + // If teams are locked, just return. + if (lockTeams==true) + return; + + if (forceTeamsToBeEven==true) + { + // Run the even team algorithm + EvenTeams(); + } +} +void TeamBalancer::SetLockTeams(bool lock) +{ + if (lock==lockTeams) + return; + + // Set flag to indicate that teams can no longer be changed. + lockTeams=lock; + + // If lock is false, and teams were set to be forced as even, then run through the even team algorithm + if (lockTeams==false) + { + // Process even swaps + TeamId i,j; + for (i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].requestedTeam!=UNASSIGNED_TEAM_ID) + { + for (j=i+1; j < teamMembers.Size(); j++) + { + if (teamMembers[j].requestedTeam==teamMembers[i].currentTeam && + teamMembers[i].requestedTeam==teamMembers[j].currentTeam) + { + SwapTeamMembersByRequest(i,j); + NotifyTeamAssigment(i); + NotifyTeamAssigment(j); + } + } + } + } + + if (forceTeamsToBeEven==true) + { + EvenTeams(); + } + else + { + // Process requested team changes + // Process movement while not full + for (i=0; i < teamMembers.Size(); i++) + { + TeamId requestedTeam = teamMembers[i].requestedTeam; + if (requestedTeam!=UNASSIGNED_TEAM_ID) + { + if (teamMemberCounts[requestedTeam]Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); +} +void TeamBalancer::CancelRequestSpecificTeam(NetworkID memberId) +{ + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==memberId) + { + myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; + + // Send packet to the host to remove our request flag. + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); + bsOut.Write((MessageID)ID_CANCEL_TEAM_REQUEST); + bsOut.Write(memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); + + return; + } + } +} +void TeamBalancer::RequestAnyTeam(NetworkID memberId) +{ + bool foundMatch=false; + + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==memberId) + { + foundMatch=true; + if (myTeamMembers[i].currentTeam!=UNASSIGNED_TEAM_ID) + return; + else + myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; + break; + } + } + + if (foundMatch==false) + { + MyTeamMembers mtm; + mtm.currentTeam=UNASSIGNED_TEAM_ID; + mtm.memberId=memberId; + mtm.requestedTeam=UNASSIGNED_TEAM_ID; + myTeamMembers.Push(mtm, _FILE_AND_LINE_); + } + + // Else send to the current host that we need a team. + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); + bsOut.Write((MessageID)ID_REQUEST_ANY_TEAM); + bsOut.Write(memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); +} +TeamId TeamBalancer::GetMyTeam(NetworkID memberId) const +{ + // Return team returned by last ID_TEAM_BALANCER_TEAM_ASSIGNED packet + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==memberId) + { + return myTeamMembers[i].currentTeam; + } + } + + return UNASSIGNED_TEAM_ID; +} +void TeamBalancer::DeleteMember(NetworkID memberId) +{ + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==memberId) + { + myTeamMembers.RemoveAtIndexFast(i); + break; + } + } + + for (unsigned int i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].memberId==memberId) + { + RemoveTeamMember(i); + break; + } + } +} +PluginReceiveResult TeamBalancer::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_FCM2_NEW_HOST: + { + hostGuid=packet->guid; + + if (myTeamMembers.Size()>0) + { + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_INTERNAL); + bsOut.Write((MessageID)ID_STATUS_UPDATE_TO_NEW_HOST); + + bsOut.WriteCasted(myTeamMembers.Size()); + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + bsOut.Write(myTeamMembers[i].memberId); + bsOut.Write(myTeamMembers[i].currentTeam); + bsOut.Write(myTeamMembers[i].requestedTeam); + } + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,hostGuid,false); + } + } + break; + case ID_TEAM_BALANCER_INTERNAL: + { + if (packet->length>=2) + { + switch (packet->data[1]) + { + case ID_STATUS_UPDATE_TO_NEW_HOST: + OnStatusUpdateToNewHost(packet); + break; + case ID_CANCEL_TEAM_REQUEST: + OnCancelTeamRequest(packet); + break; + case ID_REQUEST_ANY_TEAM: + OnRequestAnyTeam(packet); + break; + case ID_REQUEST_SPECIFIC_TEAM: + OnRequestSpecificTeam(packet); + break; + } + } + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + case ID_TEAM_BALANCER_TEAM_ASSIGNED: + { + return OnTeamAssigned(packet); + } + + case ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING: + { + return OnRequestedTeamChangePending(packet); + } + + case ID_TEAM_BALANCER_TEAMS_LOCKED: + { + return OnTeamsLocked(packet); + } + } + + // Got RequestSpecificTeam + // If teams are locked + // - If this user already has a team, return ID_TEAM_BALANCER_TEAMS_LOCKED + // - This user does not already have a team. Assign a team as if the user called RequestAnyTeam(), with a preference for the requested team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED once the team has been assigned. + // If teams are not locked + // - If even team balancing is on, only assign this user if this would not cause teams to be unbalanced. If teams WOULD be unbalanced, then flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING + // - If the destination team is full, flag this user as wanting to join this team. Return ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING + // - Else, join this team. Return ID_TEAM_BALANCER_TEAM_ASSIGNED + + // Got RequestAnyTeam + // Put user on a team following the algorithm. No team is set as preferred. + + return RR_CONTINUE_PROCESSING; +} +void TeamBalancer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) systemAddress; + (void) lostConnectionReason; + + RemoveByGuid(rakNetGUID); +} +void TeamBalancer::OnAttach(void) +{ + hostGuid = rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +} +void TeamBalancer::RemoveByGuid(RakNetGUID rakNetGUID) +{ + // If we are the host, and the closed connection has a team, and teams are not locked: + if (WeAreHost()) + { + unsigned int droppedMemberIndex=0; + while (droppedMemberIndex < teamMembers.Size()) + { + if (teamMembers[droppedMemberIndex].memberGuid==rakNetGUID) + { + TeamId droppedTeam = teamMembers[droppedMemberIndex].currentTeam; + RemoveTeamMember(droppedMemberIndex); + if (lockTeams==false) + { + if (forceTeamsToBeEven) + { + // - teams were forced to be even, then run the even team algorithm + EvenTeams(); + } + else + { + // - teams were NOT forced to be even, and the team the dropped player on was full, then move users wanting to join that team (if any) + if (teamMemberCounts[ droppedTeam ]==teamLimits[ droppedTeam ]-1) + { + MoveMemberThatWantsToJoinTeam(droppedTeam); + } + } + } + } + else + { + droppedMemberIndex++; + } + } + } +} +void TeamBalancer::OnStatusUpdateToNewHost(Packet *packet) +{ + if (WeAreHost()==false) + return; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + uint8_t requestedTeamChangeListSize; + bsIn.Read(requestedTeamChangeListSize); + TeamMember tm; + tm.memberGuid=packet->guid; + for (uint8_t i=0; i < requestedTeamChangeListSize; i++) + { + bsIn.Read(tm.memberId); + bsIn.Read(tm.currentTeam); + bsIn.Read(tm.requestedTeam); + + if (tm.currentTeam!=UNASSIGNED_TEAM_ID && tm.currentTeam>teamLimits.Size()) + { + RakAssert("Current team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0); + return; + } + + if (tm.requestedTeam!=UNASSIGNED_TEAM_ID && tm.requestedTeam>teamLimits.Size()) + { + RakAssert("Requested team out of range in TeamBalancer::OnStatusUpdateToNewHost" && 0); + return; + } + + if (tm.currentTeam==UNASSIGNED_TEAM_ID && tm.requestedTeam==UNASSIGNED_TEAM_ID) + return; + + unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid); + if (memberIndex==(unsigned int) -1) + { + // Add this system (by GUID) to the list of members if he is not already there + // Also update his requested team flag. + // Do not process balancing on requested teams, since we don't necessarily have all data from all systems yet and hopefully the state during the host migration was stable. + if (tm.currentTeam==UNASSIGNED_TEAM_ID) + { + // Assign a default team, then add team member + if (tm.requestedTeam==UNASSIGNED_TEAM_ID) + { + // Assign a default team + tm.currentTeam=GetNextDefaultTeam(); + } + else + { + // Assign to requested team if possible. Otherwise, assign to a default team + if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false) + { + tm.currentTeam=tm.requestedTeam; + } + else + { + tm.currentTeam=GetNextDefaultTeam(); + } + } + } + + if (tm.currentTeam==UNASSIGNED_TEAM_ID) + { + RakAssert("Too many members asking for teams!" && 0); + return; + } + NotifyTeamAssigment(AddTeamMember(tm)); + } + } +} +void TeamBalancer::OnCancelTeamRequest(Packet *packet) +{ + if (WeAreHost()==false) + return; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + NetworkID memberId; + bsIn.Read(memberId); + + unsigned int memberIndex = GetMemberIndex(memberId, packet->guid); + if (memberIndex!=(unsigned int)-1) + teamMembers[memberIndex].requestedTeam=UNASSIGNED_TEAM_ID; +} +void TeamBalancer::OnRequestAnyTeam(Packet *packet) +{ + if (WeAreHost()==false) + return; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + NetworkID memberId; + bsIn.Read(memberId); + + unsigned int memberIndex = GetMemberIndex(memberId, packet->guid); + if (memberIndex==(unsigned int)-1) + { + TeamMember tm; + tm.currentTeam=GetNextDefaultTeam(); + tm.requestedTeam=UNASSIGNED_TEAM_ID; + tm.memberGuid=packet->guid; + tm.memberId=memberId; + if (tm.currentTeam==UNASSIGNED_TEAM_ID) + { + RakAssert("Too many members asking for teams!" && 0); + return; + } + NotifyTeamAssigment(AddTeamMember(tm)); + } +} +void TeamBalancer::OnRequestSpecificTeam(Packet *packet) +{ + if (WeAreHost()==false) + return; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(2); + TeamMember tm; + bsIn.Read(tm.memberId); + bsIn.Read(tm.requestedTeam); + + unsigned int memberIndex = GetMemberIndex(tm.memberId, packet->guid); + if (tm.requestedTeam==UNASSIGNED_TEAM_ID) + { + NotifyNoTeam(tm.memberId, packet->guid); + RemoveTeamMember(memberIndex); + return; + } + + if (tm.requestedTeam>teamLimits.Size()) + { + RakAssert("Requested team out of range in TeamBalancer::OnRequestSpecificTeam" && 0); + return; + } + if (memberIndex==(unsigned int) -1) + { + tm.memberGuid=packet->guid; + + // Assign to requested team if possible. Otherwise, assign to a default team + if (TeamWouldBeOverpopulatedOnAddition(tm.requestedTeam, teamMembers.Size())==false) + { + tm.currentTeam=tm.requestedTeam; + tm.requestedTeam=UNASSIGNED_TEAM_ID; + } + else + { + tm.currentTeam=GetNextDefaultTeam(); + } + if (tm.currentTeam==UNASSIGNED_TEAM_ID) + { + RakAssert("Too many members asking for teams!" && 0); + return; + } + NotifyTeamAssigment(AddTeamMember(tm)); + } + else + { + teamMembers[memberIndex].requestedTeam=tm.requestedTeam; + TeamId oldTeamThisUserWasOn = teamMembers[memberIndex].currentTeam; + + if (lockTeams) + { + NotifyTeamsLocked(packet->guid, tm.requestedTeam); + return; + } + + // Assign to requested team if possible. Otherwise, assign to a default team + if (TeamsWouldBeEvenOnSwitch(tm.requestedTeam,oldTeamThisUserWasOn)==true) + { + SwitchMemberTeam(memberIndex,tm.requestedTeam); + NotifyTeamAssigment(memberIndex); + } + else + { + // If someone wants to join this user's old team, and we want to join their team, they can swap + unsigned int swappableMemberIndex; + for (swappableMemberIndex=0; swappableMemberIndex < teamMembers.Size(); swappableMemberIndex++) + { + if (teamMembers[swappableMemberIndex].currentTeam==tm.requestedTeam && teamMembers[swappableMemberIndex].requestedTeam==oldTeamThisUserWasOn) + break; + } + + if (swappableMemberIndex!=teamMembers.Size()) + { + SwapTeamMembersByRequest(memberIndex,swappableMemberIndex); + NotifyTeamAssigment(memberIndex); + NotifyTeamAssigment(swappableMemberIndex); + } + else + { + // Full or would not be even + NotifyTeamSwitchPending(packet->guid, tm.requestedTeam, tm.memberId); + } + } + } +} +unsigned int TeamBalancer::GetMemberIndex(NetworkID memberId, RakNetGUID guid) const +{ + for (unsigned int i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].memberGuid==guid && teamMembers[i].memberId==memberId) + return i; + } + return (unsigned int) -1; +} +unsigned int TeamBalancer::AddTeamMember(const TeamMember &tm) +{ + if (tm.currentTeam>teamLimits.Size()) + { + RakAssert("TeamBalancer::AddTeamMember team index out of bounds" && 0); + return (unsigned int) -1; + } + + RakAssert(tm.currentTeam!=UNASSIGNED_TEAM_ID); + + teamMembers.Push(tm,_FILE_AND_LINE_); + if (teamMemberCounts.Size() overpopulatedTeams; + TeamId teamMemberCountsIndex; + unsigned int memberIndexToSwitch; + for (teamMemberCountsIndex=0; teamMemberCountsIndex0); + memberIndexToSwitch=GetMemberIndexToSwitchTeams(overpopulatedTeams,teamMemberCountsIndex); + RakAssert(memberIndexToSwitch!=(unsigned int)-1); + SwitchMemberTeam(memberIndexToSwitch,teamMemberCountsIndex); + // Tell this member he switched teams + NotifyTeamAssigment(memberIndexToSwitch); + } + } +} +unsigned int TeamBalancer::GetMemberIndexToSwitchTeams(const DataStructures::List &sourceTeamNumbers, TeamId targetTeamNumber) +{ + DataStructures::List preferredSwapIndices; + DataStructures::List potentialSwapIndices; + unsigned int i,j; + for (j=0; j < sourceTeamNumbers.Size(); j++) + { + RakAssert(sourceTeamNumbers[j]!=targetTeamNumber); + for (i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].currentTeam==sourceTeamNumbers[j]) + { + if (teamMembers[i].requestedTeam==targetTeamNumber) + preferredSwapIndices.Push(i,_FILE_AND_LINE_); + else + potentialSwapIndices.Push(i,_FILE_AND_LINE_); + } + } + } + + if (preferredSwapIndices.Size()>0) + { + return preferredSwapIndices[ randomMT() % preferredSwapIndices.Size() ]; + } + else if (potentialSwapIndices.Size()>0) + { + return potentialSwapIndices[ randomMT() % potentialSwapIndices.Size() ]; + } + else + { + return (unsigned int) -1; + } +} +void TeamBalancer::SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam) +{ + teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]=teamMemberCounts[ teamMembers[teamMemberIndex].currentTeam ]-1; + teamMemberCounts[ destinationTeam ]=teamMemberCounts[ destinationTeam ]+1; + teamMembers[teamMemberIndex].currentTeam=destinationTeam; + if (teamMembers[teamMemberIndex].requestedTeam==destinationTeam) + teamMembers[teamMemberIndex].requestedTeam=UNASSIGNED_TEAM_ID; +} +void TeamBalancer::GetOverpopulatedTeams(DataStructures::List &overpopulatedTeams, int maxTeamSize) +{ + overpopulatedTeams.Clear(true,_FILE_AND_LINE_); + for (TeamId i=0; i < teamMemberCounts.Size(); i++) + { + if (teamMemberCounts[i]>=maxTeamSize) + overpopulatedTeams.Push(i,_FILE_AND_LINE_); + } +} +void TeamBalancer::NotifyTeamAssigment(unsigned int teamMemberIndex) +{ + RakAssert(teamMemberIndex < teamMembers.Size()); + if (teamMemberIndex>=teamMembers.Size()) + return; + + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED); + bsOut.Write(teamMembers[teamMemberIndex].currentTeam); + bsOut.Write(teamMembers[teamMemberIndex].memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,teamMembers[teamMemberIndex].memberGuid,false); +} +bool TeamBalancer::WeAreHost(void) const +{ + return hostGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS); +} +PluginReceiveResult TeamBalancer::OnTeamAssigned(Packet *packet) +{ + if (packet->guid!=hostGuid) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(1); + + MyTeamMembers mtm; + bsIn.Read(mtm.currentTeam); + bsIn.Read(mtm.memberId); + mtm.requestedTeam=UNASSIGNED_TEAM_ID; + + bool foundMatch=false; + for (unsigned int i=0; i < myTeamMembers.Size(); i++) + { + if (myTeamMembers[i].memberId==mtm.memberId) + { + foundMatch=true; + if (myTeamMembers[i].requestedTeam==mtm.currentTeam) + myTeamMembers[i].requestedTeam=UNASSIGNED_TEAM_ID; + myTeamMembers[i].currentTeam=mtm.currentTeam; + break; + } + } + + if (foundMatch==false) + { + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + return RR_CONTINUE_PROCESSING; +} +PluginReceiveResult TeamBalancer::OnRequestedTeamChangePending(Packet *packet) +{ + if (packet->guid!=hostGuid) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + return RR_CONTINUE_PROCESSING; +} +PluginReceiveResult TeamBalancer::OnTeamsLocked(Packet *packet) +{ + if (packet->guid!=hostGuid) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + return RR_CONTINUE_PROCESSING; +} +TeamId TeamBalancer::GetNextDefaultTeam(void) +{ + // Accounting for team balancing and team limits, get the team a player should be placed on + switch (defaultAssigmentAlgorithm) + { + case SMALLEST_TEAM: + { + return GetSmallestNonFullTeam(); + } + + case FILL_IN_ORDER: + { + return GetFirstNonFullTeam(); + } + + default: + { + RakAssert("TeamBalancer::GetNextDefaultTeam unknown algorithm enumeration" && 0); + return UNASSIGNED_TEAM_ID; + } + } +} +bool TeamBalancer::TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize) +{ + // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it? + if (teamMemberCounts[teamId]>=teamLimits[teamId]) + { + return true; + } + + if (forceTeamsToBeEven) + { + int allowedLimit = teamMemberSize/teamLimits.Size() + 1; + return teamMemberCounts[teamId]>=allowedLimit; + } + + return false; +} +bool TeamBalancer::TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize) +{ + if (forceTeamsToBeEven) + { + unsigned int minMembersOnASingleTeam = (teamMemberSize-1)/teamLimits.Size(); + return teamMemberCounts[teamId]<=minMembersOnASingleTeam; + } + return false; +} +TeamId TeamBalancer::GetSmallestNonFullTeam(void) const +{ + TeamId idx; + unsigned long smallestTeamCount=MAX_UNSIGNED_LONG; + TeamId smallestTeamIndex = UNASSIGNED_TEAM_ID; + for (idx=0; idx < teamMemberCounts.Size(); idx++) + { + if (teamMemberCounts[idx] membersThatWantToJoinTheTeam; + for (TeamId i=0; i < teamMembers.Size(); i++) + { + if (teamMembers[i].requestedTeam==teamId) + membersThatWantToJoinTheTeam.Push(i,_FILE_AND_LINE_); + } + + if (membersThatWantToJoinTheTeam.Size()>0) + { + TeamId oldTeam; + unsigned int swappedMemberIndex = membersThatWantToJoinTheTeam[ randomMT() % membersThatWantToJoinTheTeam.Size() ]; + oldTeam=teamMembers[swappedMemberIndex].currentTeam; + SwitchMemberTeam(swappedMemberIndex,teamId); + NotifyTeamAssigment(swappedMemberIndex); + return oldTeam; + } + return UNASSIGNED_TEAM_ID; +} +void TeamBalancer::NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam) +{ + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAMS_LOCKED); + bsOut.Write(requestedTeam); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); +} +void TeamBalancer::NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam, NetworkID memberId) +{ + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING); + bsOut.Write(requestedTeam); + bsOut.Write(memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); +} +void TeamBalancer::SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2) +{ + TeamId index1Team = teamMembers[memberIndex1].currentTeam; + teamMembers[memberIndex1].currentTeam=teamMembers[memberIndex2].currentTeam; + teamMembers[memberIndex2].currentTeam=index1Team; + teamMembers[memberIndex1].requestedTeam=UNASSIGNED_TEAM_ID; + teamMembers[memberIndex2].requestedTeam=UNASSIGNED_TEAM_ID; +} +void TeamBalancer::NotifyNoTeam(NetworkID memberId, RakNetGUID target) +{ + BitStream bsOut; + bsOut.Write((MessageID)ID_TEAM_BALANCER_TEAM_ASSIGNED); + bsOut.Write((unsigned char)UNASSIGNED_TEAM_ID); + bsOut.Write(memberId); + rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,target,false); +} +bool TeamBalancer::TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2) +{ + RakAssert(teamMembers.Size()!=0); + return TeamWouldBeOverpopulatedOnAddition(t1, teamMembers.Size()-1)==false && + TeamWouldBeUnderpopulatedOnLeave(t2, teamMembers.Size()-1)==false; +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/TeamBalancer.h b/src/raknet/TeamBalancer.h index 1776aca..502cb35 100755 --- a/src/raknet/TeamBalancer.h +++ b/src/raknet/TeamBalancer.h @@ -1,194 +1,194 @@ -/// \file TeamBalancer.h -/// \brief Set and network team selection (supports peer to peer or client/server) -/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TeamBalancer==1 - -#ifndef __TEAM_BALANCER_H -#define __TEAM_BALANCER_H - -#include "PluginInterface2.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" -#include "DS_List.h" -#include "RakString.h" - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \defgroup TEAM_BALANCER_GROUP TeamBalancer -/// \brief Set and network team selection (supports peer to peer or client/server) -/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing -/// \ingroup PLUGINS_GROUP - -/// 0...254 for your team number identifiers. 255 is reserved as undefined. -/// \ingroup TEAM_BALANCER_GROUP -typedef unsigned char TeamId; - -#define UNASSIGNED_TEAM_ID 255 - -/// \brief Set and network team selection (supports peer to peer or client/server) -/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing.
          -/// Usage: TODO -/// \ingroup TEAM_BALANCER_GROUP -class RAK_DLL_EXPORT TeamBalancer : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(TeamBalancer) - - TeamBalancer(); - virtual ~TeamBalancer(); - - /// \brief Set the limit to the number of players on the specified team - /// \details SetTeamSizeLimit() must be called on the host, so the host can enforce the maximum number of players on each team. - /// SetTeamSizeLimit() can be called on all systems if desired - for example, in a P2P environment you may wish to call it on all systems in advanced in case you become host. - /// \param[in] team Which team to set the limit for - /// \param[in] limit The maximum number of people on this team - void SetTeamSizeLimit(TeamId team, unsigned short limit); - - enum DefaultAssigmentAlgorithm - { - /// Among all the teams, join the team with the smallest number of players - SMALLEST_TEAM, - /// Join the team with the lowest index that has open slots. - FILL_IN_ORDER - }; - /// \brief Determine how players' teams will be set when they call RequestAnyTeam() - /// \details Based on the specified enumeration, a player will join a team automatically - /// Defaults to SMALLEST_TEAM - /// This function is only used by the host - /// \param[in] daa Enumeration describing the algorithm to use - void SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa); - - /// \brief By default, teams can be unbalanced up to the team size limit defined by SetTeamSizeLimits() - /// \details If SetForceEvenTeams(true) is called on the host, then teams cannot be unbalanced by more than 1 player - /// If teams are uneven at the time that SetForceEvenTeams(true) is called, players at randomly will be switched, and will be notified of ID_TEAM_BALANCER_TEAM_ASSIGNED - /// If players disconnect from the host such that teams would not be even, and teams are not locked, then a player from the largest team is randomly moved to even the teams. - /// Defaults to false - /// \note SetLockTeams(true) takes priority over SetForceEvenTeams(), so if teams are currently locked, this function will have no effect until teams become unlocked. - /// \param[in] force True to force even teams. False to allow teams to not be evenly matched - void SetForceEvenTeams(bool force); - - /// \brief If set, calls to RequestSpecificTeam() and RequestAnyTeam() will return the team you are currently on. - /// \details However, if those functions are called and you do not have a team, then you will be assigned to a default team according to SetDefaultAssignmentAlgorithm() and possibly SetForceEvenTeams(true) - /// If \a lock is false, and SetForceEvenTeams() was called with \a force as true, and teams are currently uneven, they will be made even, and those players randomly moved will get ID_TEAM_BALANCER_TEAM_ASSIGNED - /// Defaults to false - /// \param[in] lock True to lock teams, false to unlock - void SetLockTeams(bool lock); - - /// Set your requested team. UNASSIGNED_TEAM_ID means no team. - /// After enough time for network communication, ID_TEAM_BALANCER_SET_TEAM will be returned with your current team, or - /// If team switch is not possible, ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING or ID_TEAM_BALANCER_TEAMS_LOCKED will be returned. - /// In the case of ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING the request will stay in memory. ID_TEAM_BALANCER_SET_TEAM will be returned when someone on the desired team leaves or wants to switch to your team. - /// If SetLockTeams(true) is called while you have a request pending, you will get ID_TEAM_BALANCER_TEAMS_LOCKED - /// \pre Call SetTeamSizeLimits() on the host and call SetHostGuid() on this system. If the host is not running the TeamBalancer plugin or did not have SetTeamSizeLimits() called, then you will not get any response. - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - /// \param[in] desiredTeam An index representing your team number. The index should range from 0 to one less than the size of the list passed to SetTeamSizeLimits() on the host. You can also pass UNASSIGNED_TEAM_ID to not be on any team (such as if spectating) - void RequestSpecificTeam(NetworkID memberId, TeamId desiredTeam); - - /// If ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING is returned after a call to RequestSpecificTeam(), the request will stay in memory on the host and execute when available, or until the teams become locked. - /// You can cancel the request by calling CancelRequestSpecificTeam(), in which case you will stay on your existing team. - /// \note Due to latency, even after calling CancelRequestSpecificTeam() you may still get ID_TEAM_BALANCER_SET_TEAM if the packet was already in transmission. - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - void CancelRequestSpecificTeam(NetworkID memberId); - - /// Allow host to pick your team, based on whatever algorithm it uses for default team assignments. - /// This only has an effect if you are not currently on a team (GetMyTeam() returns UNASSIGNED_TEAM_ID) - /// \pre Call SetTeamSizeLimits() on the host and call SetHostGuid() on this system - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - void RequestAnyTeam(NetworkID memberId); - - /// Returns your team. - /// As your team changes, you are notified through the ID_TEAM_BALANCER_TEAM_ASSIGNED packet in byte 1. - /// Returns UNASSIGNED_TEAM_ID initially - /// \pre For this to return anything other than UNASSIGNED_TEAM_ID, connect to a properly initialized host and RequestSpecificTeam() or RequestAnyTeam() first - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - /// \return UNASSIGNED_TEAM_ID for no team. Otherwise, the index should range from 0 to one less than the size of the list passed to SetTeamSizeLimits() on the host - TeamId GetMyTeam(NetworkID memberId) const; - - /// If you called RequestSpecificTeam() or RequestAnyTeam() with a value for \a memberId that - /// Has since been deleted, call DeleteMember(). to notify this plugin of that event. - /// Not necessary with only one team member per system - /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. - void DeleteMember(NetworkID memberId); - - struct TeamMember - { - RakNetGUID memberGuid; - NetworkID memberId; - TeamId currentTeam; - TeamId requestedTeam; - }; - struct MyTeamMembers - { - NetworkID memberId; - TeamId currentTeam; - TeamId requestedTeam; - }; - -protected: - - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - /// \internal - void OnAttach(void); - - void OnStatusUpdateToNewHost(Packet *packet); - void OnCancelTeamRequest(Packet *packet); - void OnRequestAnyTeam(Packet *packet); - void OnRequestSpecificTeam(Packet *packet); - - RakNetGUID hostGuid; - DefaultAssigmentAlgorithm defaultAssigmentAlgorithm; - bool forceTeamsToBeEven; - bool lockTeams; - // So if we lose the connection while processing, we request the same info of the new host - DataStructures::List myTeamMembers; - - DataStructures::List teamLimits; - DataStructures::List teamMemberCounts; - DataStructures::List teamMembers; - unsigned int GetMemberIndex(NetworkID memberId, RakNetGUID guid) const; - unsigned int AddTeamMember(const TeamMember &tm); // Returns index of new member - void RemoveTeamMember(unsigned int index); - void EvenTeams(void); - unsigned int GetMemberIndexToSwitchTeams(const DataStructures::List &sourceTeamNumbers, TeamId targetTeamNumber); - void GetOverpopulatedTeams(DataStructures::List &overpopulatedTeams, int maxTeamSize); - void SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam); - void NotifyTeamAssigment(unsigned int teamMemberIndex); - bool WeAreHost(void) const; - PluginReceiveResult OnTeamAssigned(Packet *packet); - PluginReceiveResult OnRequestedTeamChangePending(Packet *packet); - PluginReceiveResult OnTeamsLocked(Packet *packet); - void GetMinMaxTeamMembers(int &minMembersOnASingleTeam, int &maxMembersOnASingleTeam); - TeamId GetNextDefaultTeam(void); // Accounting for team balancing and team limits, get the team a player should be placed on - bool TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize); // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it? - bool TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize); - TeamId GetSmallestNonFullTeam(void) const; - TeamId GetFirstNonFullTeam(void) const; - void MoveMemberThatWantsToJoinTeam(TeamId teamId); - TeamId MoveMemberThatWantsToJoinTeamInternal(TeamId teamId); - void NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam); - void NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam, NetworkID memberId); - void NotifyNoTeam(NetworkID memberId, RakNetGUID target); - void SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2); - void RemoveByGuid(RakNetGUID rakNetGUID); - bool TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2); - -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file TeamBalancer.h +/// \brief Set and network team selection (supports peer to peer or client/server) +/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TeamBalancer==1 + +#ifndef __TEAM_BALANCER_H +#define __TEAM_BALANCER_H + +#include "PluginInterface2.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" +#include "DS_List.h" +#include "RakString.h" + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \defgroup TEAM_BALANCER_GROUP TeamBalancer +/// \brief Set and network team selection (supports peer to peer or client/server) +/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing +/// \ingroup PLUGINS_GROUP + +/// 0...254 for your team number identifiers. 255 is reserved as undefined. +/// \ingroup TEAM_BALANCER_GROUP +typedef unsigned char TeamId; + +#define UNASSIGNED_TEAM_ID 255 + +/// \brief Set and network team selection (supports peer to peer or client/server) +/// \details Automatically handles transmission and resolution of team selection, including team switching and balancing.
          +/// Usage: TODO +/// \ingroup TEAM_BALANCER_GROUP +class RAK_DLL_EXPORT TeamBalancer : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(TeamBalancer) + + TeamBalancer(); + virtual ~TeamBalancer(); + + /// \brief Set the limit to the number of players on the specified team + /// \details SetTeamSizeLimit() must be called on the host, so the host can enforce the maximum number of players on each team. + /// SetTeamSizeLimit() can be called on all systems if desired - for example, in a P2P environment you may wish to call it on all systems in advanced in case you become host. + /// \param[in] team Which team to set the limit for + /// \param[in] limit The maximum number of people on this team + void SetTeamSizeLimit(TeamId team, unsigned short limit); + + enum DefaultAssigmentAlgorithm + { + /// Among all the teams, join the team with the smallest number of players + SMALLEST_TEAM, + /// Join the team with the lowest index that has open slots. + FILL_IN_ORDER + }; + /// \brief Determine how players' teams will be set when they call RequestAnyTeam() + /// \details Based on the specified enumeration, a player will join a team automatically + /// Defaults to SMALLEST_TEAM + /// This function is only used by the host + /// \param[in] daa Enumeration describing the algorithm to use + void SetDefaultAssignmentAlgorithm(DefaultAssigmentAlgorithm daa); + + /// \brief By default, teams can be unbalanced up to the team size limit defined by SetTeamSizeLimits() + /// \details If SetForceEvenTeams(true) is called on the host, then teams cannot be unbalanced by more than 1 player + /// If teams are uneven at the time that SetForceEvenTeams(true) is called, players at randomly will be switched, and will be notified of ID_TEAM_BALANCER_TEAM_ASSIGNED + /// If players disconnect from the host such that teams would not be even, and teams are not locked, then a player from the largest team is randomly moved to even the teams. + /// Defaults to false + /// \note SetLockTeams(true) takes priority over SetForceEvenTeams(), so if teams are currently locked, this function will have no effect until teams become unlocked. + /// \param[in] force True to force even teams. False to allow teams to not be evenly matched + void SetForceEvenTeams(bool force); + + /// \brief If set, calls to RequestSpecificTeam() and RequestAnyTeam() will return the team you are currently on. + /// \details However, if those functions are called and you do not have a team, then you will be assigned to a default team according to SetDefaultAssignmentAlgorithm() and possibly SetForceEvenTeams(true) + /// If \a lock is false, and SetForceEvenTeams() was called with \a force as true, and teams are currently uneven, they will be made even, and those players randomly moved will get ID_TEAM_BALANCER_TEAM_ASSIGNED + /// Defaults to false + /// \param[in] lock True to lock teams, false to unlock + void SetLockTeams(bool lock); + + /// Set your requested team. UNASSIGNED_TEAM_ID means no team. + /// After enough time for network communication, ID_TEAM_BALANCER_SET_TEAM will be returned with your current team, or + /// If team switch is not possible, ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING or ID_TEAM_BALANCER_TEAMS_LOCKED will be returned. + /// In the case of ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING the request will stay in memory. ID_TEAM_BALANCER_SET_TEAM will be returned when someone on the desired team leaves or wants to switch to your team. + /// If SetLockTeams(true) is called while you have a request pending, you will get ID_TEAM_BALANCER_TEAMS_LOCKED + /// \pre Call SetTeamSizeLimits() on the host and call SetHostGuid() on this system. If the host is not running the TeamBalancer plugin or did not have SetTeamSizeLimits() called, then you will not get any response. + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + /// \param[in] desiredTeam An index representing your team number. The index should range from 0 to one less than the size of the list passed to SetTeamSizeLimits() on the host. You can also pass UNASSIGNED_TEAM_ID to not be on any team (such as if spectating) + void RequestSpecificTeam(NetworkID memberId, TeamId desiredTeam); + + /// If ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING is returned after a call to RequestSpecificTeam(), the request will stay in memory on the host and execute when available, or until the teams become locked. + /// You can cancel the request by calling CancelRequestSpecificTeam(), in which case you will stay on your existing team. + /// \note Due to latency, even after calling CancelRequestSpecificTeam() you may still get ID_TEAM_BALANCER_SET_TEAM if the packet was already in transmission. + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + void CancelRequestSpecificTeam(NetworkID memberId); + + /// Allow host to pick your team, based on whatever algorithm it uses for default team assignments. + /// This only has an effect if you are not currently on a team (GetMyTeam() returns UNASSIGNED_TEAM_ID) + /// \pre Call SetTeamSizeLimits() on the host and call SetHostGuid() on this system + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + void RequestAnyTeam(NetworkID memberId); + + /// Returns your team. + /// As your team changes, you are notified through the ID_TEAM_BALANCER_TEAM_ASSIGNED packet in byte 1. + /// Returns UNASSIGNED_TEAM_ID initially + /// \pre For this to return anything other than UNASSIGNED_TEAM_ID, connect to a properly initialized host and RequestSpecificTeam() or RequestAnyTeam() first + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + /// \return UNASSIGNED_TEAM_ID for no team. Otherwise, the index should range from 0 to one less than the size of the list passed to SetTeamSizeLimits() on the host + TeamId GetMyTeam(NetworkID memberId) const; + + /// If you called RequestSpecificTeam() or RequestAnyTeam() with a value for \a memberId that + /// Has since been deleted, call DeleteMember(). to notify this plugin of that event. + /// Not necessary with only one team member per system + /// \param[in] memberId If there is more than one player per computer, this number identifies that player. Use any consistent value, such as UNASSINGED_NETWORK_ID if there is only one player. + void DeleteMember(NetworkID memberId); + + struct TeamMember + { + RakNetGUID memberGuid; + NetworkID memberId; + TeamId currentTeam; + TeamId requestedTeam; + }; + struct MyTeamMembers + { + NetworkID memberId; + TeamId currentTeam; + TeamId requestedTeam; + }; + +protected: + + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + /// \internal + void OnAttach(void); + + void OnStatusUpdateToNewHost(Packet *packet); + void OnCancelTeamRequest(Packet *packet); + void OnRequestAnyTeam(Packet *packet); + void OnRequestSpecificTeam(Packet *packet); + + RakNetGUID hostGuid; + DefaultAssigmentAlgorithm defaultAssigmentAlgorithm; + bool forceTeamsToBeEven; + bool lockTeams; + // So if we lose the connection while processing, we request the same info of the new host + DataStructures::List myTeamMembers; + + DataStructures::List teamLimits; + DataStructures::List teamMemberCounts; + DataStructures::List teamMembers; + unsigned int GetMemberIndex(NetworkID memberId, RakNetGUID guid) const; + unsigned int AddTeamMember(const TeamMember &tm); // Returns index of new member + void RemoveTeamMember(unsigned int index); + void EvenTeams(void); + unsigned int GetMemberIndexToSwitchTeams(const DataStructures::List &sourceTeamNumbers, TeamId targetTeamNumber); + void GetOverpopulatedTeams(DataStructures::List &overpopulatedTeams, int maxTeamSize); + void SwitchMemberTeam(unsigned int teamMemberIndex, TeamId destinationTeam); + void NotifyTeamAssigment(unsigned int teamMemberIndex); + bool WeAreHost(void) const; + PluginReceiveResult OnTeamAssigned(Packet *packet); + PluginReceiveResult OnRequestedTeamChangePending(Packet *packet); + PluginReceiveResult OnTeamsLocked(Packet *packet); + void GetMinMaxTeamMembers(int &minMembersOnASingleTeam, int &maxMembersOnASingleTeam); + TeamId GetNextDefaultTeam(void); // Accounting for team balancing and team limits, get the team a player should be placed on + bool TeamWouldBeOverpopulatedOnAddition(TeamId teamId, unsigned int teamMemberSize); // Accounting for team balancing and team limits, would this team be overpopulated if a member was added to it? + bool TeamWouldBeUnderpopulatedOnLeave(TeamId teamId, unsigned int teamMemberSize); + TeamId GetSmallestNonFullTeam(void) const; + TeamId GetFirstNonFullTeam(void) const; + void MoveMemberThatWantsToJoinTeam(TeamId teamId); + TeamId MoveMemberThatWantsToJoinTeamInternal(TeamId teamId); + void NotifyTeamsLocked(RakNetGUID target, TeamId requestedTeam); + void NotifyTeamSwitchPending(RakNetGUID target, TeamId requestedTeam, NetworkID memberId); + void NotifyNoTeam(NetworkID memberId, RakNetGUID target); + void SwapTeamMembersByRequest(unsigned int memberIndex1, unsigned int memberIndex2); + void RemoveByGuid(RakNetGUID rakNetGUID); + bool TeamsWouldBeEvenOnSwitch(TeamId t1, TeamId t2); + +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/TelnetTransport.cpp b/src/raknet/TelnetTransport.cpp index cc20897..cf77463 100755 --- a/src/raknet/TelnetTransport.cpp +++ b/src/raknet/TelnetTransport.cpp @@ -1,364 +1,364 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TelnetTransport==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#include "TelnetTransport.h" -#include "TCPInterface.h" -#include -#include -#include -#include "LinuxStrings.h" - -// #define _PRINTF_DEBUG - -#define ECHO_INPUT - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(TelnetTransport,TelnetTransport); - -TelnetTransport::TelnetTransport() -{ - tcpInterface=0; - sendSuffix=0; - sendPrefix=0; -} -TelnetTransport::~TelnetTransport() -{ - Stop(); - if (sendSuffix) - rakFree_Ex(sendSuffix, _FILE_AND_LINE_ ); - if (sendPrefix) - rakFree_Ex(sendPrefix, _FILE_AND_LINE_ ); -} -bool TelnetTransport::Start(unsigned short port, bool serverMode) -{ - (void) serverMode; - AutoAllocate(); - RakAssert(serverMode); - return tcpInterface->Start(port, 64); -} -void TelnetTransport::Stop(void) -{ - if (tcpInterface==0) return; - tcpInterface->Stop(); - unsigned i; - for (i=0; i < remoteClients.Size(); i++) - RakNet::OP_DELETE(remoteClients[i], _FILE_AND_LINE_); - remoteClients.Clear(false, _FILE_AND_LINE_); - RakNet::OP_DELETE(tcpInterface, _FILE_AND_LINE_); - tcpInterface=0; -} -void TelnetTransport::Send( SystemAddress systemAddress, const char *data,... ) -{ - if (tcpInterface==0) return; - - if (data==0 || data[0]==0) - return; - - char text[REMOTE_MAX_TEXT_INPUT]; - size_t prefixLength; - if (sendPrefix) - { - strcpy(text, sendPrefix); - prefixLength = strlen(sendPrefix); - } - else - { - text[0]=0; - prefixLength=0; - } - va_list ap; - va_start(ap, data); - _vsnprintf(text+prefixLength, REMOTE_MAX_TEXT_INPUT-prefixLength, data, ap); - va_end(ap); - text[REMOTE_MAX_TEXT_INPUT-1]=0; - - if (sendSuffix) - { - size_t length = strlen(text); - size_t availableChars = REMOTE_MAX_TEXT_INPUT-length-1; - strncat(text, sendSuffix, availableChars); - } - - tcpInterface->Send(text, (unsigned int) strlen(text), systemAddress, false); -} -void TelnetTransport::CloseConnection( SystemAddress systemAddress ) -{ - tcpInterface->CloseConnection(systemAddress); -} -Packet* TelnetTransport::Receive( void ) -{ - if (tcpInterface==0) return 0; - Packet *p = tcpInterface->Receive(); - if (p==0) - return 0; - - /* - if (p->data[0]==255) - { - unsigned i; - for (i=0; i < p->length; i++) - { - RAKNET_DEBUG_PRINTF("%i ", p->data[i]); - } - RAKNET_DEBUG_PRINTF("\n"); - tcpInterface->DeallocatePacket(p); - return 0; - } - */ - - // Get this guy's cursor buffer. This is real bullcrap that I have to do this. - unsigned i; - TelnetClient *remoteClient=0; - for (i=0; i < remoteClients.Size(); i++) - { - if (remoteClients[i]->systemAddress==p->systemAddress) - remoteClient=remoteClients[i]; - } - //RakAssert(remoteClient); - if (remoteClient==0) - { - tcpInterface->DeallocatePacket(p); - return 0; - } - - - if (p->length==3 && p->data[0]==27 && p->data[1]==91 && p->data[2]==65) - { - if (remoteClient->lastSentTextInput[0]) - { - // Up arrow, return last string - for (int i=0; remoteClient->textInput[i]; i++) - remoteClient->textInput[i]=8; - strcat(remoteClient->textInput, remoteClient->lastSentTextInput); - tcpInterface->Send((const char *)remoteClient->textInput, (unsigned int) strlen(remoteClient->textInput), p->systemAddress, false); - strcpy(remoteClient->textInput,remoteClient->lastSentTextInput); - remoteClient->cursorPosition=(unsigned int) strlen(remoteClient->textInput); - } - - return 0; - } - - - // 127 is delete - ignore that - // 9 is tab - // 27 is escape - if (p->data[0]>=127 || p->data[0]==9 || p->data[0]==27) - { - tcpInterface->DeallocatePacket(p); - return 0; - } - - // Hack - I don't know what the hell this is about but cursor keys send 3 characters at a time. I can block these - //Up=27,91,65 - //Down=27,91,66 - //Right=27,91,67 - //Left=27,91,68 - if (p->length==3 && p->data[0]==27 && p->data[1]==91 && p->data[2]>=65 && p->data[2]<=68) - { - tcpInterface->DeallocatePacket(p); - return 0; - } - - - - // Echo - - tcpInterface->Send((const char *)p->data, p->length, p->systemAddress, false); - - - bool gotLine; - // Process each character in turn - for (i=0; i < p->length; i++) - { - - - if (p->data[i]==8) - { - char spaceThenBack[2]; - spaceThenBack[0]=' '; - spaceThenBack[1]=8; - tcpInterface->Send((const char *)spaceThenBack, 2, p->systemAddress, false); - } - - - gotLine=ReassembleLine(remoteClient, p->data[i]); - if (gotLine && remoteClient->textInput[0]) - { - - Packet *reassembledLine = (Packet*) rakMalloc_Ex(sizeof(Packet), _FILE_AND_LINE_); - reassembledLine->length=(unsigned int) strlen(remoteClient->textInput); - memcpy(remoteClient->lastSentTextInput, remoteClient->textInput, reassembledLine->length+1); - RakAssert(reassembledLine->length < REMOTE_MAX_TEXT_INPUT); - reassembledLine->data= (unsigned char*) rakMalloc_Ex( reassembledLine->length+1, _FILE_AND_LINE_ ); - memcpy(reassembledLine->data, remoteClient->textInput, reassembledLine->length); -#ifdef _PRINTF_DEBUG - memset(remoteClient->textInput, 0, REMOTE_MAX_TEXT_INPUT); -#endif - reassembledLine->data[reassembledLine->length]=0; - reassembledLine->systemAddress=p->systemAddress; - tcpInterface->DeallocatePacket(p); - return reassembledLine; - } - } - - tcpInterface->DeallocatePacket(p); - return 0; -} -void TelnetTransport::DeallocatePacket( Packet *packet ) -{ - if (tcpInterface==0) return; - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - rakFree_Ex(packet, _FILE_AND_LINE_ ); -} -SystemAddress TelnetTransport::HasNewIncomingConnection(void) -{ - unsigned i; - SystemAddress newConnection; - newConnection = tcpInterface->HasNewIncomingConnection(); - // 03/16/06 Can't force the stupid windows telnet to use line mode or local echo so now I have to track all the remote players and their - // input buffer - if (newConnection != UNASSIGNED_SYSTEM_ADDRESS) - { - unsigned char command[10]; - // http://www.pcmicro.com/netfoss/RFC857.html - // IAC WON'T ECHO - command[0]=255; // IAC - //command[1]=253; // WON'T - command[1]=251; // WILL - command[2]=1; // ECHO - tcpInterface->Send((const char*)command, 3, newConnection, false); - - /* - // Tell the other side to use line mode - // http://www.faqs.org/rfcs/rfc1184.html - // IAC DO LINEMODE - // command[0]=255; // IAC - // command[1]=252; // DO - // command[2]=34; // LINEMODE - // tcpInterface->Send((const char*)command, 3, newConnection); - - */ - - TelnetClient *remoteClient=0; - for (i=0; i < remoteClients.Size(); i++) - { - if (remoteClients[i]->systemAddress==newConnection) - { - remoteClient=remoteClients[i]; - remoteClient->cursorPosition=0; - } - } - - if (remoteClient==0) - { - remoteClient=new TelnetClient; - remoteClient->lastSentTextInput[0]=0; - remoteClient->cursorPosition=0; - remoteClient->systemAddress=newConnection; -#ifdef _PRINTF_DEBUG - memset(remoteClient->textInput, 0, REMOTE_MAX_TEXT_INPUT); -#endif - } - - remoteClients.Insert(remoteClient, _FILE_AND_LINE_); - } - return newConnection; -} -SystemAddress TelnetTransport::HasLostConnection(void) -{ - SystemAddress systemAddress; - unsigned i; - systemAddress=tcpInterface->HasLostConnection(); - if (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - for (i=0; i < remoteClients.Size(); i++) - { - if (remoteClients[i]->systemAddress==systemAddress) - { - RakNet::OP_DELETE(remoteClients[i], _FILE_AND_LINE_); - remoteClients[i]=remoteClients[remoteClients.Size()-1]; - remoteClients.RemoveFromEnd(); - } - } - } - return systemAddress; -} -CommandParserInterface* TelnetTransport::GetCommandParser(void) -{ - return 0; -} -void TelnetTransport::SetSendSuffix(const char *suffix) -{ - if (sendSuffix) - { - rakFree_Ex(sendSuffix, _FILE_AND_LINE_ ); - sendSuffix=0; - } - if (suffix) - { - sendSuffix = (char*) rakMalloc_Ex(strlen(suffix)+1, _FILE_AND_LINE_); - strcpy(sendSuffix, suffix); - } -} -void TelnetTransport::SetSendPrefix(const char *prefix) -{ - if (sendPrefix) - { - rakFree_Ex(sendPrefix, _FILE_AND_LINE_ ); - sendPrefix=0; - } - if (prefix) - { - sendPrefix = (char*) rakMalloc_Ex(strlen(prefix)+1, _FILE_AND_LINE_); - strcpy(sendPrefix, prefix); - } -} -void TelnetTransport::AutoAllocate(void) -{ - if (tcpInterface==0) - tcpInterface=new TCPInterface; -} -bool TelnetTransport::ReassembleLine(TelnetTransport::TelnetClient* remoteClient, unsigned char c) -{ - if (c=='\n') - { - remoteClient->textInput[remoteClient->cursorPosition]=0; - remoteClient->cursorPosition=0; -#ifdef _PRINTF_DEBUG - RAKNET_DEBUG_PRINTF("[Done] %s\n", remoteClient->textInput); -#endif - return true; - } - else if (c==8) // backspace - { - if (remoteClient->cursorPosition>0) - { - remoteClient->textInput[--remoteClient->cursorPosition]=0; -#ifdef _PRINTF_DEBUG - RAKNET_DEBUG_PRINTF("[Back] %s\n", remoteClient->textInput); -#endif - } - } - else if (c>=32 && c <127) - { - if (remoteClient->cursorPosition < REMOTE_MAX_TEXT_INPUT) - { - remoteClient->textInput[remoteClient->cursorPosition++]=c; -#ifdef _PRINTF_DEBUG - RAKNET_DEBUG_PRINTF("[Norm] %s\n", remoteClient->textInput); -#endif - } - } - return false; -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TelnetTransport==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#include "TelnetTransport.h" +#include "TCPInterface.h" +#include +#include +#include +#include "LinuxStrings.h" + +// #define _PRINTF_DEBUG + +#define ECHO_INPUT + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(TelnetTransport,TelnetTransport); + +TelnetTransport::TelnetTransport() +{ + tcpInterface=0; + sendSuffix=0; + sendPrefix=0; +} +TelnetTransport::~TelnetTransport() +{ + Stop(); + if (sendSuffix) + rakFree_Ex(sendSuffix, _FILE_AND_LINE_ ); + if (sendPrefix) + rakFree_Ex(sendPrefix, _FILE_AND_LINE_ ); +} +bool TelnetTransport::Start(unsigned short port, bool serverMode) +{ + (void) serverMode; + AutoAllocate(); + RakAssert(serverMode); + return tcpInterface->Start(port, 64); +} +void TelnetTransport::Stop(void) +{ + if (tcpInterface==0) return; + tcpInterface->Stop(); + unsigned i; + for (i=0; i < remoteClients.Size(); i++) + RakNet::OP_DELETE(remoteClients[i], _FILE_AND_LINE_); + remoteClients.Clear(false, _FILE_AND_LINE_); + RakNet::OP_DELETE(tcpInterface, _FILE_AND_LINE_); + tcpInterface=0; +} +void TelnetTransport::Send( SystemAddress systemAddress, const char *data,... ) +{ + if (tcpInterface==0) return; + + if (data==0 || data[0]==0) + return; + + char text[REMOTE_MAX_TEXT_INPUT]; + size_t prefixLength; + if (sendPrefix) + { + strcpy(text, sendPrefix); + prefixLength = strlen(sendPrefix); + } + else + { + text[0]=0; + prefixLength=0; + } + va_list ap; + va_start(ap, data); + _vsnprintf(text+prefixLength, REMOTE_MAX_TEXT_INPUT-prefixLength, data, ap); + va_end(ap); + text[REMOTE_MAX_TEXT_INPUT-1]=0; + + if (sendSuffix) + { + size_t length = strlen(text); + size_t availableChars = REMOTE_MAX_TEXT_INPUT-length-1; + strncat(text, sendSuffix, availableChars); + } + + tcpInterface->Send(text, (unsigned int) strlen(text), systemAddress, false); +} +void TelnetTransport::CloseConnection( SystemAddress systemAddress ) +{ + tcpInterface->CloseConnection(systemAddress); +} +Packet* TelnetTransport::Receive( void ) +{ + if (tcpInterface==0) return 0; + Packet *p = tcpInterface->Receive(); + if (p==0) + return 0; + + /* + if (p->data[0]==255) + { + unsigned i; + for (i=0; i < p->length; i++) + { + RAKNET_DEBUG_PRINTF("%i ", p->data[i]); + } + RAKNET_DEBUG_PRINTF("\n"); + tcpInterface->DeallocatePacket(p); + return 0; + } + */ + + // Get this guy's cursor buffer. This is real bullcrap that I have to do this. + unsigned i; + TelnetClient *remoteClient=0; + for (i=0; i < remoteClients.Size(); i++) + { + if (remoteClients[i]->systemAddress==p->systemAddress) + remoteClient=remoteClients[i]; + } + //RakAssert(remoteClient); + if (remoteClient==0) + { + tcpInterface->DeallocatePacket(p); + return 0; + } + + + if (p->length==3 && p->data[0]==27 && p->data[1]==91 && p->data[2]==65) + { + if (remoteClient->lastSentTextInput[0]) + { + // Up arrow, return last string + for (int i=0; remoteClient->textInput[i]; i++) + remoteClient->textInput[i]=8; + strcat(remoteClient->textInput, remoteClient->lastSentTextInput); + tcpInterface->Send((const char *)remoteClient->textInput, (unsigned int) strlen(remoteClient->textInput), p->systemAddress, false); + strcpy(remoteClient->textInput,remoteClient->lastSentTextInput); + remoteClient->cursorPosition=(unsigned int) strlen(remoteClient->textInput); + } + + return 0; + } + + + // 127 is delete - ignore that + // 9 is tab + // 27 is escape + if (p->data[0]>=127 || p->data[0]==9 || p->data[0]==27) + { + tcpInterface->DeallocatePacket(p); + return 0; + } + + // Hack - I don't know what the hell this is about but cursor keys send 3 characters at a time. I can block these + //Up=27,91,65 + //Down=27,91,66 + //Right=27,91,67 + //Left=27,91,68 + if (p->length==3 && p->data[0]==27 && p->data[1]==91 && p->data[2]>=65 && p->data[2]<=68) + { + tcpInterface->DeallocatePacket(p); + return 0; + } + + + + // Echo + + tcpInterface->Send((const char *)p->data, p->length, p->systemAddress, false); + + + bool gotLine; + // Process each character in turn + for (i=0; i < p->length; i++) + { + + + if (p->data[i]==8) + { + char spaceThenBack[2]; + spaceThenBack[0]=' '; + spaceThenBack[1]=8; + tcpInterface->Send((const char *)spaceThenBack, 2, p->systemAddress, false); + } + + + gotLine=ReassembleLine(remoteClient, p->data[i]); + if (gotLine && remoteClient->textInput[0]) + { + + Packet *reassembledLine = (Packet*) rakMalloc_Ex(sizeof(Packet), _FILE_AND_LINE_); + reassembledLine->length=(unsigned int) strlen(remoteClient->textInput); + memcpy(remoteClient->lastSentTextInput, remoteClient->textInput, reassembledLine->length+1); + RakAssert(reassembledLine->length < REMOTE_MAX_TEXT_INPUT); + reassembledLine->data= (unsigned char*) rakMalloc_Ex( reassembledLine->length+1, _FILE_AND_LINE_ ); + memcpy(reassembledLine->data, remoteClient->textInput, reassembledLine->length); +#ifdef _PRINTF_DEBUG + memset(remoteClient->textInput, 0, REMOTE_MAX_TEXT_INPUT); +#endif + reassembledLine->data[reassembledLine->length]=0; + reassembledLine->systemAddress=p->systemAddress; + tcpInterface->DeallocatePacket(p); + return reassembledLine; + } + } + + tcpInterface->DeallocatePacket(p); + return 0; +} +void TelnetTransport::DeallocatePacket( Packet *packet ) +{ + if (tcpInterface==0) return; + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + rakFree_Ex(packet, _FILE_AND_LINE_ ); +} +SystemAddress TelnetTransport::HasNewIncomingConnection(void) +{ + unsigned i; + SystemAddress newConnection; + newConnection = tcpInterface->HasNewIncomingConnection(); + // 03/16/06 Can't force the stupid windows telnet to use line mode or local echo so now I have to track all the remote players and their + // input buffer + if (newConnection != UNASSIGNED_SYSTEM_ADDRESS) + { + unsigned char command[10]; + // http://www.pcmicro.com/netfoss/RFC857.html + // IAC WON'T ECHO + command[0]=255; // IAC + //command[1]=253; // WON'T + command[1]=251; // WILL + command[2]=1; // ECHO + tcpInterface->Send((const char*)command, 3, newConnection, false); + + /* + // Tell the other side to use line mode + // http://www.faqs.org/rfcs/rfc1184.html + // IAC DO LINEMODE + // command[0]=255; // IAC + // command[1]=252; // DO + // command[2]=34; // LINEMODE + // tcpInterface->Send((const char*)command, 3, newConnection); + + */ + + TelnetClient *remoteClient=0; + for (i=0; i < remoteClients.Size(); i++) + { + if (remoteClients[i]->systemAddress==newConnection) + { + remoteClient=remoteClients[i]; + remoteClient->cursorPosition=0; + } + } + + if (remoteClient==0) + { + remoteClient=new TelnetClient; + remoteClient->lastSentTextInput[0]=0; + remoteClient->cursorPosition=0; + remoteClient->systemAddress=newConnection; +#ifdef _PRINTF_DEBUG + memset(remoteClient->textInput, 0, REMOTE_MAX_TEXT_INPUT); +#endif + } + + remoteClients.Insert(remoteClient, _FILE_AND_LINE_); + } + return newConnection; +} +SystemAddress TelnetTransport::HasLostConnection(void) +{ + SystemAddress systemAddress; + unsigned i; + systemAddress=tcpInterface->HasLostConnection(); + if (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + for (i=0; i < remoteClients.Size(); i++) + { + if (remoteClients[i]->systemAddress==systemAddress) + { + RakNet::OP_DELETE(remoteClients[i], _FILE_AND_LINE_); + remoteClients[i]=remoteClients[remoteClients.Size()-1]; + remoteClients.RemoveFromEnd(); + } + } + } + return systemAddress; +} +CommandParserInterface* TelnetTransport::GetCommandParser(void) +{ + return 0; +} +void TelnetTransport::SetSendSuffix(const char *suffix) +{ + if (sendSuffix) + { + rakFree_Ex(sendSuffix, _FILE_AND_LINE_ ); + sendSuffix=0; + } + if (suffix) + { + sendSuffix = (char*) rakMalloc_Ex(strlen(suffix)+1, _FILE_AND_LINE_); + strcpy(sendSuffix, suffix); + } +} +void TelnetTransport::SetSendPrefix(const char *prefix) +{ + if (sendPrefix) + { + rakFree_Ex(sendPrefix, _FILE_AND_LINE_ ); + sendPrefix=0; + } + if (prefix) + { + sendPrefix = (char*) rakMalloc_Ex(strlen(prefix)+1, _FILE_AND_LINE_); + strcpy(sendPrefix, prefix); + } +} +void TelnetTransport::AutoAllocate(void) +{ + if (tcpInterface==0) + tcpInterface=new TCPInterface; +} +bool TelnetTransport::ReassembleLine(TelnetTransport::TelnetClient* remoteClient, unsigned char c) +{ + if (c=='\n') + { + remoteClient->textInput[remoteClient->cursorPosition]=0; + remoteClient->cursorPosition=0; +#ifdef _PRINTF_DEBUG + RAKNET_DEBUG_PRINTF("[Done] %s\n", remoteClient->textInput); +#endif + return true; + } + else if (c==8) // backspace + { + if (remoteClient->cursorPosition>0) + { + remoteClient->textInput[--remoteClient->cursorPosition]=0; +#ifdef _PRINTF_DEBUG + RAKNET_DEBUG_PRINTF("[Back] %s\n", remoteClient->textInput); +#endif + } + } + else if (c>=32 && c <127) + { + if (remoteClient->cursorPosition < REMOTE_MAX_TEXT_INPUT) + { + remoteClient->textInput[remoteClient->cursorPosition++]=c; +#ifdef _PRINTF_DEBUG + RAKNET_DEBUG_PRINTF("[Norm] %s\n", remoteClient->textInput); +#endif + } + } + return false; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/TelnetTransport.h b/src/raknet/TelnetTransport.h index e5e515b..ed2836f 100755 --- a/src/raknet/TelnetTransport.h +++ b/src/raknet/TelnetTransport.h @@ -1,72 +1,72 @@ -/// \file -/// \brief Contains TelnetTransport , used to supports the telnet transport protocol. Insecure -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TelnetTransport==1 && _RAKNET_SUPPORT_TCPInterface==1 - -#ifndef __TELNET_TRANSPORT -#define __TELNET_TRANSPORT - -#include "TransportInterface.h" -#include "DS_List.h" -#include "Export.h" - -namespace RakNet -{ -/// Forward declarations -class TCPInterface; -struct TelnetClient; - -/// \brief Use TelnetTransport to easily allow windows telnet to connect to your ConsoleServer -/// \details To run Windows telnet, go to your start menu, click run, and in the edit box type "telnet " where is the ip address.
          -/// of your ConsoleServer (most likely the same IP as your game).
          -/// This implementation always echos commands. -class RAK_DLL_EXPORT TelnetTransport : public TransportInterface -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(TelnetTransport) - - TelnetTransport(); - virtual ~TelnetTransport(); - bool Start(unsigned short port, bool serverMode); - void Stop(void); - void Send( SystemAddress systemAddress, const char *data, ... ); - void CloseConnection( SystemAddress systemAddress ); - Packet* Receive( void ); - void DeallocatePacket( Packet *packet ); - SystemAddress HasNewIncomingConnection(void); - SystemAddress HasLostConnection(void); - CommandParserInterface* GetCommandParser(void); - void SetSendSuffix(const char *suffix); - void SetSendPrefix(const char *prefix); -protected: - - struct TelnetClient - { - SystemAddress systemAddress; - char textInput[REMOTE_MAX_TEXT_INPUT]; - char lastSentTextInput[REMOTE_MAX_TEXT_INPUT]; - unsigned cursorPosition; - }; - - TCPInterface *tcpInterface; - void AutoAllocate(void); - bool ReassembleLine(TelnetTransport::TelnetClient* telnetClient, unsigned char c); - - // Crap this sucks but because windows telnet won't send line at a time, I have to reconstruct the lines at the server per player - DataStructures::List remoteClients; - - char *sendSuffix, *sendPrefix; - -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Contains TelnetTransport , used to supports the telnet transport protocol. Insecure +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TelnetTransport==1 && _RAKNET_SUPPORT_TCPInterface==1 + +#ifndef __TELNET_TRANSPORT +#define __TELNET_TRANSPORT + +#include "TransportInterface.h" +#include "DS_List.h" +#include "Export.h" + +namespace RakNet +{ +/// Forward declarations +class TCPInterface; +struct TelnetClient; + +/// \brief Use TelnetTransport to easily allow windows telnet to connect to your ConsoleServer +/// \details To run Windows telnet, go to your start menu, click run, and in the edit box type "telnet " where is the ip address.
          +/// of your ConsoleServer (most likely the same IP as your game).
          +/// This implementation always echos commands. +class RAK_DLL_EXPORT TelnetTransport : public TransportInterface +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(TelnetTransport) + + TelnetTransport(); + virtual ~TelnetTransport(); + bool Start(unsigned short port, bool serverMode); + void Stop(void); + void Send( SystemAddress systemAddress, const char *data, ... ); + void CloseConnection( SystemAddress systemAddress ); + Packet* Receive( void ); + void DeallocatePacket( Packet *packet ); + SystemAddress HasNewIncomingConnection(void); + SystemAddress HasLostConnection(void); + CommandParserInterface* GetCommandParser(void); + void SetSendSuffix(const char *suffix); + void SetSendPrefix(const char *prefix); +protected: + + struct TelnetClient + { + SystemAddress systemAddress; + char textInput[REMOTE_MAX_TEXT_INPUT]; + char lastSentTextInput[REMOTE_MAX_TEXT_INPUT]; + unsigned cursorPosition; + }; + + TCPInterface *tcpInterface; + void AutoAllocate(void); + bool ReassembleLine(TelnetTransport::TelnetClient* telnetClient, unsigned char c); + + // Crap this sucks but because windows telnet won't send line at a time, I have to reconstruct the lines at the server per player + DataStructures::List remoteClients; + + char *sendSuffix, *sendPrefix; + +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/ThreadPool.h b/src/raknet/ThreadPool.h index 087460e..29eb8e8 100755 --- a/src/raknet/ThreadPool.h +++ b/src/raknet/ThreadPool.h @@ -1,620 +1,620 @@ -#ifndef __THREAD_POOL_H -#define __THREAD_POOL_H - -#include "RakMemoryOverride.h" -#include "DS_Queue.h" -#include "SimpleMutex.h" -#include "Export.h" -#include "RakThread.h" -#include "SignaledEvent.h" - -#ifdef _MSC_VER -#pragma warning( push ) -#endif - -class ThreadDataInterface -{ -public: - ThreadDataInterface() {} - virtual ~ThreadDataInterface() {} - - virtual void* PerThreadFactory(void *context)=0; - virtual void PerThreadDestructor(void* factoryResult, void *context)=0; -}; -/// A simple class to create worker threads that processes a queue of functions with data. -/// This class does not allocate or deallocate memory. It is up to the user to handle memory management. -/// InputType and OutputType are stored directly in a queue. For large structures, if you plan to delete from the middle of the queue, -/// you might wish to store pointers rather than the structures themselves so the array can shift efficiently. -template -struct RAK_DLL_EXPORT ThreadPool -{ - ThreadPool(); - ~ThreadPool(); - - /// Start the specified number of threads. - /// \param[in] numThreads The number of threads to start - /// \param[in] stackSize 0 for default (except on consoles). - /// \param[in] _perThreadInit User callback to return data stored per thread. Pass 0 if not needed. - /// \param[in] _perThreadDeinit User callback to destroy data stored per thread, created by _perThreadInit. Pass 0 if not needed. - /// \return True on success, false on failure. - bool StartThreads(int numThreads, int stackSize, void* (*_perThreadInit)()=0, void (*_perThreadDeinit)(void*)=0); - - // Alternate form of _perThreadDataFactory, _perThreadDataDestructor - void SetThreadDataInterface(ThreadDataInterface *tdi, void *context); - - /// Stops all threads - void StopThreads(void); - - /// Adds a function to a queue with data to pass to that function. This function will be called from the thread - /// Memory management is your responsibility! This class does not allocate or deallocate memory. - /// The best way to deallocate \a inputData is in userCallback. If you call EndThreads such that callbacks were not called, you - /// can iterate through the inputQueue and deallocate all pending input data there - /// The best way to deallocate output is as it is returned to you from GetOutput. Similarly, if you end the threads such that - /// not all output was returned, you can iterate through outputQueue and deallocate it there. - /// \param[in] workerThreadCallback The function to call from the thread - /// \param[in] inputData The parameter to pass to \a userCallback - void AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData); - - /// Adds to the output queue - /// Use it if you want to inject output into the same queue that the system uses. Normally you would not use this. Consider it a convenience function. - /// \param[in] outputData The output to inject - void AddOutput(OutputType outputData); - - /// Returns true if output from GetOutput is waiting. - /// \return true if output is waiting, false otherwise - bool HasOutput(void); - - /// Inaccurate but fast version of HasOutput. If this returns true, you should still check HasOutput for the real value. - /// \return true if output is probably waiting, false otherwise - bool HasOutputFast(void); - - /// Returns true if input from GetInput is waiting. - /// \return true if input is waiting, false otherwise - bool HasInput(void); - - /// Inaccurate but fast version of HasInput. If this returns true, you should still check HasInput for the real value. - /// \return true if input is probably waiting, false otherwise - bool HasInputFast(void); - - /// Gets the output of a call to \a userCallback - /// HasOutput must return true before you call this function. Otherwise it will assert. - /// \return The output of \a userCallback. If you have different output signatures, it is up to you to encode the data to indicate this - OutputType GetOutput(void); - - /// Clears internal buffers - void Clear(void); - - /// Lock the input buffer before calling the functions InputSize, InputAtIndex, and RemoveInputAtIndex - /// It is only necessary to lock the input or output while the threads are running - void LockInput(void); - - /// Unlock the input buffer after you are done with the functions InputSize, GetInputAtIndex, and RemoveInputAtIndex - void UnlockInput(void); - - /// Length of the input queue - unsigned InputSize(void); - - /// Get the input at a specified index - InputType GetInputAtIndex(unsigned index); - - /// Remove input from a specific index. This does NOT do memory deallocation - it only removes the item from the queue - void RemoveInputAtIndex(unsigned index); - - /// Lock the output buffer before calling the functions OutputSize, OutputAtIndex, and RemoveOutputAtIndex - /// It is only necessary to lock the input or output while the threads are running - void LockOutput(void); - - /// Unlock the output buffer after you are done with the functions OutputSize, GetOutputAtIndex, and RemoveOutputAtIndex - void UnlockOutput(void); - - /// Length of the output queue - unsigned OutputSize(void); - - /// Get the output at a specified index - OutputType GetOutputAtIndex(unsigned index); - - /// Remove output from a specific index. This does NOT do memory deallocation - it only removes the item from the queue - void RemoveOutputAtIndex(unsigned index); - - /// Removes all items from the input queue - void ClearInput(void); - - /// Removes all items from the output queue - void ClearOutput(void); - - /// Are any of the threads working, or is input or output available? - bool IsWorking(void); - - /// The number of currently active threads. - int NumThreadsWorking(void); - - /// Did we call Start? - bool WasStarted(void); - - // Block until all threads are stopped. - bool Pause(void); - - // Continue running - void Resume(void); - -protected: - // It is valid to cancel input before it is processed. To do so, lock the inputQueue with inputQueueMutex, - // Scan the list, and remove the item you don't want. - RakNet::SimpleMutex inputQueueMutex, outputQueueMutex, workingThreadCountMutex, runThreadsMutex; - - void* (*perThreadDataFactory)(); - void (*perThreadDataDestructor)(void*); - - // inputFunctionQueue & inputQueue are paired arrays so if you delete from one at a particular index you must delete from the other - // at the same index - DataStructures::Queue inputFunctionQueue; - DataStructures::Queue inputQueue; - DataStructures::Queue outputQueue; - - ThreadDataInterface *threadDataInterface; - void *tdiContext; - - - template - friend RAK_THREAD_DECLARATION(WorkerThread); - - /* -#ifdef _WIN32 - friend unsigned __stdcall WorkerThread( LPVOID arguments ); -#else - friend void* WorkerThread( void* arguments ); -#endif - */ - - /// \internal - bool runThreads; - /// \internal - int numThreadsRunning; - /// \internal - int numThreadsWorking; - /// \internal - RakNet::SimpleMutex numThreadsRunningMutex; - - RakNet::SignaledEvent quitAndIncomingDataEvents; - -// #if defined(SN_TARGET_PSP2) -// RakNet::RakThread::UltUlThreadRuntime *runtime; -// #endif -}; - -#include "ThreadPool.h" -#include "RakSleep.h" -#ifdef _WIN32 - -#else -#include -#endif - -#ifdef _MSC_VER -#pragma warning(disable:4127) -#pragma warning( disable : 4701 ) // potentially uninitialized local variable 'inputData' used -#endif - -template -RAK_THREAD_DECLARATION(WorkerThread) -/* -#ifdef _WIN32 -unsigned __stdcall WorkerThread( LPVOID arguments ) -#else -void* WorkerThread( void* arguments ) -#endif -*/ -{ - - - - ThreadPool *threadPool = (ThreadPool*) arguments; - - - bool returnOutput; - ThreadOutputType (*userCallback)(ThreadInputType, bool *, void*); - ThreadInputType inputData; - ThreadOutputType callbackOutput; - - userCallback=0; - - void *perThreadData; - if (threadPool->perThreadDataFactory) - perThreadData=threadPool->perThreadDataFactory(); - else if (threadPool->threadDataInterface) - perThreadData=threadPool->threadDataInterface->PerThreadFactory(threadPool->tdiContext); - else - perThreadData=0; - - // Increase numThreadsRunning - threadPool->numThreadsRunningMutex.Lock(); - ++threadPool->numThreadsRunning; - threadPool->numThreadsRunningMutex.Unlock(); - - while (1) - { -#ifdef _WIN32 - if (userCallback==0) - { - threadPool->quitAndIncomingDataEvents.WaitOnEvent(INFINITE); - } -#endif - - threadPool->runThreadsMutex.Lock(); - if (threadPool->runThreads==false) - { - threadPool->runThreadsMutex.Unlock(); - break; - } - threadPool->runThreadsMutex.Unlock(); - - threadPool->workingThreadCountMutex.Lock(); - ++threadPool->numThreadsWorking; - threadPool->workingThreadCountMutex.Unlock(); - - // Read input data - userCallback=0; - threadPool->inputQueueMutex.Lock(); - if (threadPool->inputFunctionQueue.Size()) - { - userCallback=threadPool->inputFunctionQueue.Pop(); - inputData=threadPool->inputQueue.Pop(); - } - threadPool->inputQueueMutex.Unlock(); - - if (userCallback) - { - callbackOutput=userCallback(inputData, &returnOutput,perThreadData); - if (returnOutput) - { - threadPool->outputQueueMutex.Lock(); - threadPool->outputQueue.Push(callbackOutput, _FILE_AND_LINE_ ); - threadPool->outputQueueMutex.Unlock(); - } - } - - threadPool->workingThreadCountMutex.Lock(); - --threadPool->numThreadsWorking; - threadPool->workingThreadCountMutex.Unlock(); - } - - // Decrease numThreadsRunning - threadPool->numThreadsRunningMutex.Lock(); - --threadPool->numThreadsRunning; - threadPool->numThreadsRunningMutex.Unlock(); - - if (threadPool->perThreadDataDestructor) - threadPool->perThreadDataDestructor(perThreadData); - else if (threadPool->threadDataInterface) - threadPool->threadDataInterface->PerThreadDestructor(perThreadData, threadPool->tdiContext); - - - - - return 0; - -} -template -ThreadPool::ThreadPool() -{ - runThreads=false; - numThreadsRunning=0; - threadDataInterface=0; - tdiContext=0; - numThreadsWorking=0; - -} -template -ThreadPool::~ThreadPool() -{ - StopThreads(); - Clear(); -} -template -bool ThreadPool::StartThreads(int numThreads, int stackSize, void* (*_perThreadDataFactory)(), void (*_perThreadDataDestructor)(void *)) -{ - (void) stackSize; - -// #if defined(SN_TARGET_PSP2) -// runtime = RakNet::RakThread::AllocRuntime(numThreads); -// #endif - - runThreadsMutex.Lock(); - if (runThreads==true) - { - // Already running - runThreadsMutex.Unlock(); - return false; - } - runThreadsMutex.Unlock(); - - quitAndIncomingDataEvents.InitEvent(); - - perThreadDataFactory=_perThreadDataFactory; - perThreadDataDestructor=_perThreadDataDestructor; - - runThreadsMutex.Lock(); - runThreads=true; - runThreadsMutex.Unlock(); - - numThreadsWorking=0; - unsigned threadId = 0; - (void) threadId; - int i; - for (i=0; i < numThreads; i++) - { - int errorCode; - - - - - errorCode = RakNet::RakThread::Create(WorkerThread, this); - - if (errorCode!=0) - { - StopThreads(); - return false; - } - } - // Wait for number of threads running to increase to numThreads - bool done=false; - while (done==false) - { - RakSleep(50); - numThreadsRunningMutex.Lock(); - if (numThreadsRunning==numThreads) - done=true; - numThreadsRunningMutex.Unlock(); - } - - return true; -} -template -void ThreadPool::SetThreadDataInterface(ThreadDataInterface *tdi, void *context) -{ - threadDataInterface=tdi; - tdiContext=context; -} -template -void ThreadPool::StopThreads(void) -{ - runThreadsMutex.Lock(); - if (runThreads==false) - { - runThreadsMutex.Unlock(); - return; - } - - runThreads=false; - runThreadsMutex.Unlock(); - - // Wait for number of threads running to decrease to 0 - bool done=false; - while (done==false) - { - quitAndIncomingDataEvents.SetEvent(); - - RakSleep(50); - numThreadsRunningMutex.Lock(); - if (numThreadsRunning==0) - done=true; - numThreadsRunningMutex.Unlock(); - } - - quitAndIncomingDataEvents.CloseEvent(); - -// #if defined(SN_TARGET_PSP2) -// RakNet::RakThread::DeallocRuntime(runtime); -// runtime=0; -// #endif - -} -template -void ThreadPool::AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData) -{ - inputQueueMutex.Lock(); - inputQueue.Push(inputData, _FILE_AND_LINE_ ); - inputFunctionQueue.Push(workerThreadCallback, _FILE_AND_LINE_ ); - inputQueueMutex.Unlock(); - - quitAndIncomingDataEvents.SetEvent(); -} -template -void ThreadPool::AddOutput(OutputType outputData) -{ - outputQueueMutex.Lock(); - outputQueue.Push(outputData, _FILE_AND_LINE_ ); - outputQueueMutex.Unlock(); -} -template -bool ThreadPool::HasOutputFast(void) -{ - return outputQueue.IsEmpty()==false; -} -template -bool ThreadPool::HasOutput(void) -{ - bool res; - outputQueueMutex.Lock(); - res=outputQueue.IsEmpty()==false; - outputQueueMutex.Unlock(); - return res; -} -template -bool ThreadPool::HasInputFast(void) -{ - return inputQueue.IsEmpty()==false; -} -template -bool ThreadPool::HasInput(void) -{ - bool res; - inputQueueMutex.Lock(); - res=inputQueue.IsEmpty()==false; - inputQueueMutex.Unlock(); - return res; -} -template -OutputType ThreadPool::GetOutput(void) -{ - // Real output check - OutputType output; - outputQueueMutex.Lock(); - output=outputQueue.Pop(); - outputQueueMutex.Unlock(); - return output; -} -template -void ThreadPool::Clear(void) -{ - runThreadsMutex.Lock(); - if (runThreads) - { - runThreadsMutex.Unlock(); - inputQueueMutex.Lock(); - inputFunctionQueue.Clear(_FILE_AND_LINE_); - inputQueue.Clear(_FILE_AND_LINE_); - inputQueueMutex.Unlock(); - - outputQueueMutex.Lock(); - outputQueue.Clear(_FILE_AND_LINE_); - outputQueueMutex.Unlock(); - } - else - { - inputFunctionQueue.Clear(_FILE_AND_LINE_); - inputQueue.Clear(_FILE_AND_LINE_); - outputQueue.Clear(_FILE_AND_LINE_); - } -} -template -void ThreadPool::LockInput(void) -{ - inputQueueMutex.Lock(); -} -template -void ThreadPool::UnlockInput(void) -{ - inputQueueMutex.Unlock(); -} -template -unsigned ThreadPool::InputSize(void) -{ - return inputQueue.Size(); -} -template -InputType ThreadPool::GetInputAtIndex(unsigned index) -{ - return inputQueue[index]; -} -template -void ThreadPool::RemoveInputAtIndex(unsigned index) -{ - inputQueue.RemoveAtIndex(index); - inputFunctionQueue.RemoveAtIndex(index); -} -template -void ThreadPool::LockOutput(void) -{ - outputQueueMutex.Lock(); -} -template -void ThreadPool::UnlockOutput(void) -{ - outputQueueMutex.Unlock(); -} -template -unsigned ThreadPool::OutputSize(void) -{ - return outputQueue.Size(); -} -template -OutputType ThreadPool::GetOutputAtIndex(unsigned index) -{ - return outputQueue[index]; -} -template -void ThreadPool::RemoveOutputAtIndex(unsigned index) -{ - outputQueue.RemoveAtIndex(index); -} -template -void ThreadPool::ClearInput(void) -{ - inputQueue.Clear(_FILE_AND_LINE_); - inputFunctionQueue.Clear(_FILE_AND_LINE_); -} - -template -void ThreadPool::ClearOutput(void) -{ - outputQueue.Clear(_FILE_AND_LINE_); -} -template -bool ThreadPool::IsWorking(void) -{ - bool isWorking; -// workingThreadCountMutex.Lock(); -// isWorking=numThreadsWorking!=0; -// workingThreadCountMutex.Unlock(); - -// if (isWorking) -// return true; - - // Bug fix: Originally the order of these two was reversed. - // It's possible with the thread timing that working could have been false, then it picks up the data in the other thread, then it checks - // here and sees there is no data. So it thinks the thread is not working when it was. - if (HasOutputFast() && HasOutput()) - return true; - - if (HasInputFast() && HasInput()) - return true; - - // Need to check is working again, in case the thread was between the first and second checks - workingThreadCountMutex.Lock(); - isWorking=numThreadsWorking!=0; - workingThreadCountMutex.Unlock(); - - return isWorking; -} - -template -int ThreadPool::NumThreadsWorking(void) -{ - return numThreadsWorking; -} - -template -bool ThreadPool::WasStarted(void) -{ - bool b; - runThreadsMutex.Lock(); - b = runThreads; - runThreadsMutex.Unlock(); - return b; -} -template -bool ThreadPool::Pause(void) -{ - if (WasStarted()==false) - return false; - - workingThreadCountMutex.Lock(); - while (numThreadsWorking>0) - { - RakSleep(30); - } - return true; -} -template -void ThreadPool::Resume(void) -{ - workingThreadCountMutex.Unlock(); -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -#endif - +#ifndef __THREAD_POOL_H +#define __THREAD_POOL_H + +#include "RakMemoryOverride.h" +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "Export.h" +#include "RakThread.h" +#include "SignaledEvent.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +class ThreadDataInterface +{ +public: + ThreadDataInterface() {} + virtual ~ThreadDataInterface() {} + + virtual void* PerThreadFactory(void *context)=0; + virtual void PerThreadDestructor(void* factoryResult, void *context)=0; +}; +/// A simple class to create worker threads that processes a queue of functions with data. +/// This class does not allocate or deallocate memory. It is up to the user to handle memory management. +/// InputType and OutputType are stored directly in a queue. For large structures, if you plan to delete from the middle of the queue, +/// you might wish to store pointers rather than the structures themselves so the array can shift efficiently. +template +struct RAK_DLL_EXPORT ThreadPool +{ + ThreadPool(); + ~ThreadPool(); + + /// Start the specified number of threads. + /// \param[in] numThreads The number of threads to start + /// \param[in] stackSize 0 for default (except on consoles). + /// \param[in] _perThreadInit User callback to return data stored per thread. Pass 0 if not needed. + /// \param[in] _perThreadDeinit User callback to destroy data stored per thread, created by _perThreadInit. Pass 0 if not needed. + /// \return True on success, false on failure. + bool StartThreads(int numThreads, int stackSize, void* (*_perThreadInit)()=0, void (*_perThreadDeinit)(void*)=0); + + // Alternate form of _perThreadDataFactory, _perThreadDataDestructor + void SetThreadDataInterface(ThreadDataInterface *tdi, void *context); + + /// Stops all threads + void StopThreads(void); + + /// Adds a function to a queue with data to pass to that function. This function will be called from the thread + /// Memory management is your responsibility! This class does not allocate or deallocate memory. + /// The best way to deallocate \a inputData is in userCallback. If you call EndThreads such that callbacks were not called, you + /// can iterate through the inputQueue and deallocate all pending input data there + /// The best way to deallocate output is as it is returned to you from GetOutput. Similarly, if you end the threads such that + /// not all output was returned, you can iterate through outputQueue and deallocate it there. + /// \param[in] workerThreadCallback The function to call from the thread + /// \param[in] inputData The parameter to pass to \a userCallback + void AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData); + + /// Adds to the output queue + /// Use it if you want to inject output into the same queue that the system uses. Normally you would not use this. Consider it a convenience function. + /// \param[in] outputData The output to inject + void AddOutput(OutputType outputData); + + /// Returns true if output from GetOutput is waiting. + /// \return true if output is waiting, false otherwise + bool HasOutput(void); + + /// Inaccurate but fast version of HasOutput. If this returns true, you should still check HasOutput for the real value. + /// \return true if output is probably waiting, false otherwise + bool HasOutputFast(void); + + /// Returns true if input from GetInput is waiting. + /// \return true if input is waiting, false otherwise + bool HasInput(void); + + /// Inaccurate but fast version of HasInput. If this returns true, you should still check HasInput for the real value. + /// \return true if input is probably waiting, false otherwise + bool HasInputFast(void); + + /// Gets the output of a call to \a userCallback + /// HasOutput must return true before you call this function. Otherwise it will assert. + /// \return The output of \a userCallback. If you have different output signatures, it is up to you to encode the data to indicate this + OutputType GetOutput(void); + + /// Clears internal buffers + void Clear(void); + + /// Lock the input buffer before calling the functions InputSize, InputAtIndex, and RemoveInputAtIndex + /// It is only necessary to lock the input or output while the threads are running + void LockInput(void); + + /// Unlock the input buffer after you are done with the functions InputSize, GetInputAtIndex, and RemoveInputAtIndex + void UnlockInput(void); + + /// Length of the input queue + unsigned InputSize(void); + + /// Get the input at a specified index + InputType GetInputAtIndex(unsigned index); + + /// Remove input from a specific index. This does NOT do memory deallocation - it only removes the item from the queue + void RemoveInputAtIndex(unsigned index); + + /// Lock the output buffer before calling the functions OutputSize, OutputAtIndex, and RemoveOutputAtIndex + /// It is only necessary to lock the input or output while the threads are running + void LockOutput(void); + + /// Unlock the output buffer after you are done with the functions OutputSize, GetOutputAtIndex, and RemoveOutputAtIndex + void UnlockOutput(void); + + /// Length of the output queue + unsigned OutputSize(void); + + /// Get the output at a specified index + OutputType GetOutputAtIndex(unsigned index); + + /// Remove output from a specific index. This does NOT do memory deallocation - it only removes the item from the queue + void RemoveOutputAtIndex(unsigned index); + + /// Removes all items from the input queue + void ClearInput(void); + + /// Removes all items from the output queue + void ClearOutput(void); + + /// Are any of the threads working, or is input or output available? + bool IsWorking(void); + + /// The number of currently active threads. + int NumThreadsWorking(void); + + /// Did we call Start? + bool WasStarted(void); + + // Block until all threads are stopped. + bool Pause(void); + + // Continue running + void Resume(void); + +protected: + // It is valid to cancel input before it is processed. To do so, lock the inputQueue with inputQueueMutex, + // Scan the list, and remove the item you don't want. + RakNet::SimpleMutex inputQueueMutex, outputQueueMutex, workingThreadCountMutex, runThreadsMutex; + + void* (*perThreadDataFactory)(); + void (*perThreadDataDestructor)(void*); + + // inputFunctionQueue & inputQueue are paired arrays so if you delete from one at a particular index you must delete from the other + // at the same index + DataStructures::Queue inputFunctionQueue; + DataStructures::Queue inputQueue; + DataStructures::Queue outputQueue; + + ThreadDataInterface *threadDataInterface; + void *tdiContext; + + + template + friend RAK_THREAD_DECLARATION(WorkerThread); + + /* +#ifdef _WIN32 + friend unsigned __stdcall WorkerThread( LPVOID arguments ); +#else + friend void* WorkerThread( void* arguments ); +#endif + */ + + /// \internal + bool runThreads; + /// \internal + int numThreadsRunning; + /// \internal + int numThreadsWorking; + /// \internal + RakNet::SimpleMutex numThreadsRunningMutex; + + RakNet::SignaledEvent quitAndIncomingDataEvents; + +// #if defined(SN_TARGET_PSP2) +// RakNet::RakThread::UltUlThreadRuntime *runtime; +// #endif +}; + +#include "ThreadPool.h" +#include "RakSleep.h" +#ifdef _WIN32 + +#else +#include +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4127) +#pragma warning( disable : 4701 ) // potentially uninitialized local variable 'inputData' used +#endif + +template +RAK_THREAD_DECLARATION(WorkerThread) +/* +#ifdef _WIN32 +unsigned __stdcall WorkerThread( LPVOID arguments ) +#else +void* WorkerThread( void* arguments ) +#endif +*/ +{ + + + + ThreadPool *threadPool = (ThreadPool*) arguments; + + + bool returnOutput; + ThreadOutputType (*userCallback)(ThreadInputType, bool *, void*); + ThreadInputType inputData; + ThreadOutputType callbackOutput; + + userCallback=0; + + void *perThreadData; + if (threadPool->perThreadDataFactory) + perThreadData=threadPool->perThreadDataFactory(); + else if (threadPool->threadDataInterface) + perThreadData=threadPool->threadDataInterface->PerThreadFactory(threadPool->tdiContext); + else + perThreadData=0; + + // Increase numThreadsRunning + threadPool->numThreadsRunningMutex.Lock(); + ++threadPool->numThreadsRunning; + threadPool->numThreadsRunningMutex.Unlock(); + + while (1) + { +#ifdef _WIN32 + if (userCallback==0) + { + threadPool->quitAndIncomingDataEvents.WaitOnEvent(INFINITE); + } +#endif + + threadPool->runThreadsMutex.Lock(); + if (threadPool->runThreads==false) + { + threadPool->runThreadsMutex.Unlock(); + break; + } + threadPool->runThreadsMutex.Unlock(); + + threadPool->workingThreadCountMutex.Lock(); + ++threadPool->numThreadsWorking; + threadPool->workingThreadCountMutex.Unlock(); + + // Read input data + userCallback=0; + threadPool->inputQueueMutex.Lock(); + if (threadPool->inputFunctionQueue.Size()) + { + userCallback=threadPool->inputFunctionQueue.Pop(); + inputData=threadPool->inputQueue.Pop(); + } + threadPool->inputQueueMutex.Unlock(); + + if (userCallback) + { + callbackOutput=userCallback(inputData, &returnOutput,perThreadData); + if (returnOutput) + { + threadPool->outputQueueMutex.Lock(); + threadPool->outputQueue.Push(callbackOutput, _FILE_AND_LINE_ ); + threadPool->outputQueueMutex.Unlock(); + } + } + + threadPool->workingThreadCountMutex.Lock(); + --threadPool->numThreadsWorking; + threadPool->workingThreadCountMutex.Unlock(); + } + + // Decrease numThreadsRunning + threadPool->numThreadsRunningMutex.Lock(); + --threadPool->numThreadsRunning; + threadPool->numThreadsRunningMutex.Unlock(); + + if (threadPool->perThreadDataDestructor) + threadPool->perThreadDataDestructor(perThreadData); + else if (threadPool->threadDataInterface) + threadPool->threadDataInterface->PerThreadDestructor(perThreadData, threadPool->tdiContext); + + + + + return 0; + +} +template +ThreadPool::ThreadPool() +{ + runThreads=false; + numThreadsRunning=0; + threadDataInterface=0; + tdiContext=0; + numThreadsWorking=0; + +} +template +ThreadPool::~ThreadPool() +{ + StopThreads(); + Clear(); +} +template +bool ThreadPool::StartThreads(int numThreads, int stackSize, void* (*_perThreadDataFactory)(), void (*_perThreadDataDestructor)(void *)) +{ + (void) stackSize; + +// #if defined(SN_TARGET_PSP2) +// runtime = RakNet::RakThread::AllocRuntime(numThreads); +// #endif + + runThreadsMutex.Lock(); + if (runThreads==true) + { + // Already running + runThreadsMutex.Unlock(); + return false; + } + runThreadsMutex.Unlock(); + + quitAndIncomingDataEvents.InitEvent(); + + perThreadDataFactory=_perThreadDataFactory; + perThreadDataDestructor=_perThreadDataDestructor; + + runThreadsMutex.Lock(); + runThreads=true; + runThreadsMutex.Unlock(); + + numThreadsWorking=0; + unsigned threadId = 0; + (void) threadId; + int i; + for (i=0; i < numThreads; i++) + { + int errorCode; + + + + + errorCode = RakNet::RakThread::Create(WorkerThread, this); + + if (errorCode!=0) + { + StopThreads(); + return false; + } + } + // Wait for number of threads running to increase to numThreads + bool done=false; + while (done==false) + { + RakSleep(50); + numThreadsRunningMutex.Lock(); + if (numThreadsRunning==numThreads) + done=true; + numThreadsRunningMutex.Unlock(); + } + + return true; +} +template +void ThreadPool::SetThreadDataInterface(ThreadDataInterface *tdi, void *context) +{ + threadDataInterface=tdi; + tdiContext=context; +} +template +void ThreadPool::StopThreads(void) +{ + runThreadsMutex.Lock(); + if (runThreads==false) + { + runThreadsMutex.Unlock(); + return; + } + + runThreads=false; + runThreadsMutex.Unlock(); + + // Wait for number of threads running to decrease to 0 + bool done=false; + while (done==false) + { + quitAndIncomingDataEvents.SetEvent(); + + RakSleep(50); + numThreadsRunningMutex.Lock(); + if (numThreadsRunning==0) + done=true; + numThreadsRunningMutex.Unlock(); + } + + quitAndIncomingDataEvents.CloseEvent(); + +// #if defined(SN_TARGET_PSP2) +// RakNet::RakThread::DeallocRuntime(runtime); +// runtime=0; +// #endif + +} +template +void ThreadPool::AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData) +{ + inputQueueMutex.Lock(); + inputQueue.Push(inputData, _FILE_AND_LINE_ ); + inputFunctionQueue.Push(workerThreadCallback, _FILE_AND_LINE_ ); + inputQueueMutex.Unlock(); + + quitAndIncomingDataEvents.SetEvent(); +} +template +void ThreadPool::AddOutput(OutputType outputData) +{ + outputQueueMutex.Lock(); + outputQueue.Push(outputData, _FILE_AND_LINE_ ); + outputQueueMutex.Unlock(); +} +template +bool ThreadPool::HasOutputFast(void) +{ + return outputQueue.IsEmpty()==false; +} +template +bool ThreadPool::HasOutput(void) +{ + bool res; + outputQueueMutex.Lock(); + res=outputQueue.IsEmpty()==false; + outputQueueMutex.Unlock(); + return res; +} +template +bool ThreadPool::HasInputFast(void) +{ + return inputQueue.IsEmpty()==false; +} +template +bool ThreadPool::HasInput(void) +{ + bool res; + inputQueueMutex.Lock(); + res=inputQueue.IsEmpty()==false; + inputQueueMutex.Unlock(); + return res; +} +template +OutputType ThreadPool::GetOutput(void) +{ + // Real output check + OutputType output; + outputQueueMutex.Lock(); + output=outputQueue.Pop(); + outputQueueMutex.Unlock(); + return output; +} +template +void ThreadPool::Clear(void) +{ + runThreadsMutex.Lock(); + if (runThreads) + { + runThreadsMutex.Unlock(); + inputQueueMutex.Lock(); + inputFunctionQueue.Clear(_FILE_AND_LINE_); + inputQueue.Clear(_FILE_AND_LINE_); + inputQueueMutex.Unlock(); + + outputQueueMutex.Lock(); + outputQueue.Clear(_FILE_AND_LINE_); + outputQueueMutex.Unlock(); + } + else + { + inputFunctionQueue.Clear(_FILE_AND_LINE_); + inputQueue.Clear(_FILE_AND_LINE_); + outputQueue.Clear(_FILE_AND_LINE_); + } +} +template +void ThreadPool::LockInput(void) +{ + inputQueueMutex.Lock(); +} +template +void ThreadPool::UnlockInput(void) +{ + inputQueueMutex.Unlock(); +} +template +unsigned ThreadPool::InputSize(void) +{ + return inputQueue.Size(); +} +template +InputType ThreadPool::GetInputAtIndex(unsigned index) +{ + return inputQueue[index]; +} +template +void ThreadPool::RemoveInputAtIndex(unsigned index) +{ + inputQueue.RemoveAtIndex(index); + inputFunctionQueue.RemoveAtIndex(index); +} +template +void ThreadPool::LockOutput(void) +{ + outputQueueMutex.Lock(); +} +template +void ThreadPool::UnlockOutput(void) +{ + outputQueueMutex.Unlock(); +} +template +unsigned ThreadPool::OutputSize(void) +{ + return outputQueue.Size(); +} +template +OutputType ThreadPool::GetOutputAtIndex(unsigned index) +{ + return outputQueue[index]; +} +template +void ThreadPool::RemoveOutputAtIndex(unsigned index) +{ + outputQueue.RemoveAtIndex(index); +} +template +void ThreadPool::ClearInput(void) +{ + inputQueue.Clear(_FILE_AND_LINE_); + inputFunctionQueue.Clear(_FILE_AND_LINE_); +} + +template +void ThreadPool::ClearOutput(void) +{ + outputQueue.Clear(_FILE_AND_LINE_); +} +template +bool ThreadPool::IsWorking(void) +{ + bool isWorking; +// workingThreadCountMutex.Lock(); +// isWorking=numThreadsWorking!=0; +// workingThreadCountMutex.Unlock(); + +// if (isWorking) +// return true; + + // Bug fix: Originally the order of these two was reversed. + // It's possible with the thread timing that working could have been false, then it picks up the data in the other thread, then it checks + // here and sees there is no data. So it thinks the thread is not working when it was. + if (HasOutputFast() && HasOutput()) + return true; + + if (HasInputFast() && HasInput()) + return true; + + // Need to check is working again, in case the thread was between the first and second checks + workingThreadCountMutex.Lock(); + isWorking=numThreadsWorking!=0; + workingThreadCountMutex.Unlock(); + + return isWorking; +} + +template +int ThreadPool::NumThreadsWorking(void) +{ + return numThreadsWorking; +} + +template +bool ThreadPool::WasStarted(void) +{ + bool b; + runThreadsMutex.Lock(); + b = runThreads; + runThreadsMutex.Unlock(); + return b; +} +template +bool ThreadPool::Pause(void) +{ + if (WasStarted()==false) + return false; + + workingThreadCountMutex.Lock(); + while (numThreadsWorking>0) + { + RakSleep(30); + } + return true; +} +template +void ThreadPool::Resume(void) +{ + workingThreadCountMutex.Unlock(); +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + diff --git a/src/raknet/ThreadsafePacketLogger.cpp b/src/raknet/ThreadsafePacketLogger.cpp index a749da0..cb50fef 100755 --- a/src/raknet/ThreadsafePacketLogger.cpp +++ b/src/raknet/ThreadsafePacketLogger.cpp @@ -1,38 +1,38 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#include "ThreadsafePacketLogger.h" -#include - -using namespace RakNet; - -ThreadsafePacketLogger::ThreadsafePacketLogger() -{ - -} -ThreadsafePacketLogger::~ThreadsafePacketLogger() -{ - char **msg; - while ((msg = logMessages.ReadLock()) != 0) - { - rakFree_Ex((*msg), _FILE_AND_LINE_ ); - } -} -void ThreadsafePacketLogger::Update(void) -{ - char **msg; - while ((msg = logMessages.ReadLock()) != 0) - { - WriteLog(*msg); - rakFree_Ex((*msg), _FILE_AND_LINE_ ); - } -} -void ThreadsafePacketLogger::AddToLog(const char *str) -{ - char **msg = logMessages.WriteLock(); - *msg = (char*) rakMalloc_Ex( strlen(str)+1, _FILE_AND_LINE_ ); - strcpy(*msg, str); - logMessages.WriteUnlock(); -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#include "ThreadsafePacketLogger.h" +#include + +using namespace RakNet; + +ThreadsafePacketLogger::ThreadsafePacketLogger() +{ + +} +ThreadsafePacketLogger::~ThreadsafePacketLogger() +{ + char **msg; + while ((msg = logMessages.ReadLock()) != 0) + { + rakFree_Ex((*msg), _FILE_AND_LINE_ ); + } +} +void ThreadsafePacketLogger::Update(void) +{ + char **msg; + while ((msg = logMessages.ReadLock()) != 0) + { + WriteLog(*msg); + rakFree_Ex((*msg), _FILE_AND_LINE_ ); + } +} +void ThreadsafePacketLogger::AddToLog(const char *str) +{ + char **msg = logMessages.WriteLock(); + *msg = (char*) rakMalloc_Ex( strlen(str)+1, _FILE_AND_LINE_ ); + strcpy(*msg, str); + logMessages.WriteUnlock(); +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/ThreadsafePacketLogger.h b/src/raknet/ThreadsafePacketLogger.h index bca5cf2..542e0cc 100755 --- a/src/raknet/ThreadsafePacketLogger.h +++ b/src/raknet/ThreadsafePacketLogger.h @@ -1,40 +1,40 @@ -/// \file -/// \brief Derivation of the packet logger to defer the call to WriteLog until the user thread. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_PacketLogger==1 - -#ifndef __THREADSAFE_PACKET_LOGGER_H -#define __THREADSAFE_PACKET_LOGGER_H - -#include "PacketLogger.h" -#include "SingleProducerConsumer.h" - -namespace RakNet -{ - -/// \ingroup PACKETLOGGER_GROUP -/// \brief Same as PacketLogger, but writes output in the user thread. -class RAK_DLL_EXPORT ThreadsafePacketLogger : public PacketLogger -{ -public: - ThreadsafePacketLogger(); - virtual ~ThreadsafePacketLogger(); - - virtual void Update(void); - -protected: - virtual void AddToLog(const char *str); - - DataStructures::SingleProducerConsumer logMessages; -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Derivation of the packet logger to defer the call to WriteLog until the user thread. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_PacketLogger==1 + +#ifndef __THREADSAFE_PACKET_LOGGER_H +#define __THREADSAFE_PACKET_LOGGER_H + +#include "PacketLogger.h" +#include "SingleProducerConsumer.h" + +namespace RakNet +{ + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Same as PacketLogger, but writes output in the user thread. +class RAK_DLL_EXPORT ThreadsafePacketLogger : public PacketLogger +{ +public: + ThreadsafePacketLogger(); + virtual ~ThreadsafePacketLogger(); + + virtual void Update(void); + +protected: + virtual void AddToLog(const char *str); + + DataStructures::SingleProducerConsumer logMessages; +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/TransportInterface.h b/src/raknet/TransportInterface.h index f4f8076..fa073ee 100755 --- a/src/raknet/TransportInterface.h +++ b/src/raknet/TransportInterface.h @@ -1,83 +1,83 @@ -/// \file -/// \brief Contains TransportInterface from which you can derive custom transport providers for ConsoleServer. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#ifndef __TRANSPORT_INTERFACE_H -#define __TRANSPORT_INTERFACE_H - -#include "RakNetTypes.h" -#include "Export.h" -#include "RakMemoryOverride.h" - -#define REMOTE_MAX_TEXT_INPUT 2048 - -namespace RakNet -{ - -class CommandParserInterface; - - -/// \brief Defines an interface that is used to send and receive null-terminated strings. -/// \details In practice this is only used by the CommandParser system for for servers. -class RAK_DLL_EXPORT TransportInterface -{ -public: - TransportInterface() {} - virtual ~TransportInterface() {} - - /// Start the transport provider on the indicated port. - /// \param[in] port The port to start the transport provider on - /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) - /// \return Return true on success, false on failure. - virtual bool Start(unsigned short port, bool serverMode)=0; - - /// Stop the transport provider. You can clear memory and shutdown threads here. - virtual void Stop(void)=0; - - /// Send a null-terminated string to \a systemAddress - /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here - /// and parse it out in Receive(). - /// \param[in] systemAddress The player to send the string to - /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF - /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF - virtual void Send( SystemAddress systemAddress, const char *data, ... )=0; - - /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. - /// \param[in] systemAddress The player/address to disconnect - virtual void CloseConnection( SystemAddress systemAddress )=0; - - /// Return a string. The string should be allocated and written to Packet::data . - /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress - /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet - /// and thus only return a string in Packet::data - /// \return The packet structure containing the result of Receive, or 0 if no data is available - virtual Packet* Receive( void )=0; - - /// Deallocate the Packet structure returned by Receive - /// \param[in] The packet to deallocate - virtual void DeallocatePacket( Packet *packet )=0; - - /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. - /// \return The SystemAddress/address of the system - virtual SystemAddress HasNewIncomingConnection(void)=0; - - /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. - /// \return The SystemAddress/address of the system - virtual SystemAddress HasLostConnection(void)=0; - - /// Your transport provider can itself have command parsers if the transport layer has user-modifiable features - /// For example, your transport layer may have a password which you want remote users to be able to set or you may want - /// to allow remote users to turn on or off command echo - /// \return 0 if you do not need a command parser - otherwise the desired derivation of CommandParserInterface - virtual CommandParserInterface* GetCommandParser(void)=0; -protected: -}; - -} // namespace RakNet - -#endif - +/// \file +/// \brief Contains TransportInterface from which you can derive custom transport providers for ConsoleServer. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#ifndef __TRANSPORT_INTERFACE_H +#define __TRANSPORT_INTERFACE_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +#define REMOTE_MAX_TEXT_INPUT 2048 + +namespace RakNet +{ + +class CommandParserInterface; + + +/// \brief Defines an interface that is used to send and receive null-terminated strings. +/// \details In practice this is only used by the CommandParser system for for servers. +class RAK_DLL_EXPORT TransportInterface +{ +public: + TransportInterface() {} + virtual ~TransportInterface() {} + + /// Start the transport provider on the indicated port. + /// \param[in] port The port to start the transport provider on + /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) + /// \return Return true on success, false on failure. + virtual bool Start(unsigned short port, bool serverMode)=0; + + /// Stop the transport provider. You can clear memory and shutdown threads here. + virtual void Stop(void)=0; + + /// Send a null-terminated string to \a systemAddress + /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here + /// and parse it out in Receive(). + /// \param[in] systemAddress The player to send the string to + /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF + /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF + virtual void Send( SystemAddress systemAddress, const char *data, ... )=0; + + /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. + /// \param[in] systemAddress The player/address to disconnect + virtual void CloseConnection( SystemAddress systemAddress )=0; + + /// Return a string. The string should be allocated and written to Packet::data . + /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress + /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet + /// and thus only return a string in Packet::data + /// \return The packet structure containing the result of Receive, or 0 if no data is available + virtual Packet* Receive( void )=0; + + /// Deallocate the Packet structure returned by Receive + /// \param[in] The packet to deallocate + virtual void DeallocatePacket( Packet *packet )=0; + + /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + virtual SystemAddress HasNewIncomingConnection(void)=0; + + /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + virtual SystemAddress HasLostConnection(void)=0; + + /// Your transport provider can itself have command parsers if the transport layer has user-modifiable features + /// For example, your transport layer may have a password which you want remote users to be able to set or you may want + /// to allow remote users to turn on or off command echo + /// \return 0 if you do not need a command parser - otherwise the desired derivation of CommandParserInterface + virtual CommandParserInterface* GetCommandParser(void)=0; +protected: +}; + +} // namespace RakNet + +#endif + diff --git a/src/raknet/TwoWayAuthentication.cpp b/src/raknet/TwoWayAuthentication.cpp index 17648e7..95435ab 100755 --- a/src/raknet/TwoWayAuthentication.cpp +++ b/src/raknet/TwoWayAuthentication.cpp @@ -1,431 +1,431 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TwoWayAuthentication==1 - -#include "TwoWayAuthentication.h" -#include "Rand.h" -#include "GetTime.h" -#include "MessageIdentifiers.h" -#include "BitStream.h" -#include "RakPeerInterface.h" - -#if LIBCAT_SECURITY==1 -static const int HASH_BITS = 256; -static const int HASH_BYTES = HASH_BITS / 8; -static const int STRENGTHENING_FACTOR = 256; -#include -#endif - -using namespace RakNet; - -enum NegotiationIdentifiers -{ - ID_NONCE_REQUEST, - ID_NONCE_REPLY, - ID_HASHED_NONCE_AND_PASSWORD, -}; - -TwoWayAuthentication::NonceGenerator::NonceGenerator() {nextRequestId=0;} -TwoWayAuthentication::NonceGenerator::~NonceGenerator() -{ - Clear(); -} -void TwoWayAuthentication::NonceGenerator::GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, RakNet::AddressOrGUID remoteSystem) -{ - TwoWayAuthentication::NonceAndRemoteSystemRequest *narsr = RakNet::OP_NEW(_FILE_AND_LINE_); - narsr->remoteSystem=remoteSystem; - GenerateNonce(narsr->nonce); - narsr->requestId=nextRequestId++; - *requestId=narsr->requestId; - memcpy(nonce,narsr->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - narsr->whenGenerated=RakNet::GetTime(); - generatedNonces.Push(narsr,_FILE_AND_LINE_); -} -void TwoWayAuthentication::NonceGenerator::GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]) -{ - fillBufferMT(nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); -} -bool TwoWayAuthentication::NonceGenerator::GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, RakNet::AddressOrGUID remoteSystem, bool popIfFound) -{ - unsigned int i; - for (i=0; i < generatedNonces.Size(); i++) - { - if (generatedNonces[i]->requestId==requestId) - { - if (remoteSystem==generatedNonces[i]->remoteSystem) - { - memcpy(nonce,generatedNonces[i]->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - if (popIfFound) - { - RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); - generatedNonces.RemoveAtIndex(i); - } - return true; - } - else - { - return false; - } - } - } - return false; -} -void TwoWayAuthentication::NonceGenerator::Clear(void) -{ - unsigned int i; - for (i=0; i < generatedNonces.Size(); i++) - RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); - generatedNonces.Clear(true,_FILE_AND_LINE_); -} -void TwoWayAuthentication::NonceGenerator::ClearByAddress(RakNet::AddressOrGUID remoteSystem) -{ - unsigned int i=0; - while (i < generatedNonces.Size()) - { - if (generatedNonces[i]->remoteSystem==remoteSystem) - { - RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); - generatedNonces.RemoveAtIndex(i); - } - else - { - i++; - } - } -} -void TwoWayAuthentication::NonceGenerator::Update(RakNet::Time curTime) -{ - if (generatedNonces.Size()>0 && GreaterThan(curTime-5000, generatedNonces[0]->whenGenerated)) - { - RakNet::OP_DELETE(generatedNonces[0], _FILE_AND_LINE_); - generatedNonces.RemoveAtIndex(0); - } -} -TwoWayAuthentication::TwoWayAuthentication() -{ - whenLastTimeoutCheck=RakNet::GetTime(); - seedMT(RakNet::GetTimeMS()); -} -TwoWayAuthentication::~TwoWayAuthentication() -{ - Clear(); -} -bool TwoWayAuthentication::AddPassword(RakNet::RakString identifier, RakNet::RakString password) -{ - if (password.IsEmpty()) - return false; - - if (identifier.IsEmpty()) - return false; - - if (password==identifier) - return false; // Insecure - - if (passwords.GetIndexOf(identifier.C_String()).IsInvalid()==false) - return false; // This identifier already in use - - passwords.Push(identifier, password,_FILE_AND_LINE_); - return true; -} -bool TwoWayAuthentication::Challenge(RakNet::RakString identifier, AddressOrGUID remoteSystem) -{ - DataStructures::HashIndex skhi = passwords.GetIndexOf(identifier.C_String()); - if (skhi.IsInvalid()) - return false; - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); - bsOut.Write((MessageID)ID_NONCE_REQUEST); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,remoteSystem,false); - - PendingChallenge pc; - pc.identifier=identifier; - pc.remoteSystem=remoteSystem; - pc.time=RakNet::GetTime(); - pc.sentHash=false; - outgoingChallenges.Push(pc,_FILE_AND_LINE_); - - return true; -} -void TwoWayAuthentication::Update(void) -{ - RakNet::Time curTime = RakNet::GetTime(); - nonceGenerator.Update(curTime); - if (GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, whenLastTimeoutCheck)) - { - while (outgoingChallenges.Size() && GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, outgoingChallenges.Peek().time)) - { - PendingChallenge pc = outgoingChallenges.Pop(); - - // Tell the user about the timeout - PushToUser(ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, pc.identifier, pc.remoteSystem); - } - - whenLastTimeoutCheck=curTime+CHALLENGE_MINIMUM_TIMEOUT; - } -} -PluginReceiveResult TwoWayAuthentication::OnReceive(Packet *packet) -{ - switch (packet->data[0]) - { - case ID_TWO_WAY_AUTHENTICATION_NEGOTIATION: - { - if (packet->length>=sizeof(MessageID)*2) - { - switch (packet->data[sizeof(MessageID)]) - { - case ID_NONCE_REQUEST: - { - OnNonceRequest(packet); - } - break; - case ID_NONCE_REPLY: - { - OnNonceReply(packet); - } - break; - case ID_HASHED_NONCE_AND_PASSWORD: - { - return OnHashedNonceAndPassword(packet); - } - break; - } - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE: - case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS: - { - if (packet->wasGeneratedLocally==false) - { - OnPasswordResult(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - else - break; - } - break; - // These should only be generated locally - case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS: - case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE: - case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT: - if (packet->wasGeneratedLocally==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - break; - } - - return RR_CONTINUE_PROCESSING; -} -void TwoWayAuthentication::OnRakPeerShutdown(void) -{ - Clear(); -} -void TwoWayAuthentication::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - - // Remove from pending challenges - unsigned int i=0; - while (i < outgoingChallenges.Size()) - { - if ((rakNetGUID!=UNASSIGNED_RAKNET_GUID && outgoingChallenges[i].remoteSystem.rakNetGuid==rakNetGUID) || - (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && outgoingChallenges[i].remoteSystem.systemAddress==systemAddress)) - { - outgoingChallenges.RemoveAtIndex(i); - } - else - { - i++; - } - } - - if (rakNetGUID!=UNASSIGNED_RAKNET_GUID) - nonceGenerator.ClearByAddress(rakNetGUID); - else - nonceGenerator.ClearByAddress(systemAddress); -} -void TwoWayAuthentication::Clear(void) -{ - outgoingChallenges.Clear(_FILE_AND_LINE_); - passwords.Clear(_FILE_AND_LINE_); - nonceGenerator.Clear(); -} -void TwoWayAuthentication::PushToUser(MessageID messageId, RakNet::RakString password, RakNet::AddressOrGUID remoteSystem) -{ - RakNet::BitStream output; - output.Write(messageId); - if (password.IsEmpty()==false) - output.Write(password); - Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed()); - p->systemAddress=remoteSystem.systemAddress; - p->systemAddress.systemIndex=(SystemIndex)-1; - p->guid=remoteSystem.rakNetGuid; - p->wasGeneratedLocally=true; - memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed()); - rakPeerInterface->PushBackPacket(p, true); -} -void TwoWayAuthentication::OnNonceRequest(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - unsigned short requestId; - nonceGenerator.GetNonce(nonce,&requestId,packet); - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); - bsOut.Write((MessageID)ID_NONCE_REPLY); - bsOut.Write(requestId); - bsOut.WriteAlignedBytes((const unsigned char*) nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); -} -void TwoWayAuthentication::OnNonceReply(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - unsigned short requestId; - bsIn.Read(requestId); - bsIn.ReadAlignedBytes((unsigned char *) thierNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - - // Lookup one of the negotiations for this guid/system address - AddressOrGUID aog(packet); - unsigned int i; - for (i=0; i < outgoingChallenges.Size(); i++) - { - if (outgoingChallenges[i].remoteSystem==aog && outgoingChallenges[i].sentHash==false) - { - outgoingChallenges[i].sentHash=true; - - // Get the password for this identifier - DataStructures::HashIndex skhi = passwords.GetIndexOf(outgoingChallenges[i].identifier.C_String()); - if (skhi.IsInvalid()==false) - { - RakNet::RakString password = passwords.ItemAtIndex(skhi); - - // Hash their nonce with password and reply - char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; - Hash(thierNonce, password, hashedNonceAndPw); - - // Send - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); - bsOut.Write((MessageID)ID_HASHED_NONCE_AND_PASSWORD); - bsOut.Write(requestId); - bsOut.Write(outgoingChallenges[i].identifier); // Identifier helps the other system lookup the password quickly. - bsOut.WriteAlignedBytes((const unsigned char*) hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); - } - - return; - } - } -} -PluginReceiveResult TwoWayAuthentication::OnHashedNonceAndPassword(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*2); - - char remoteHashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; - unsigned short requestId; - bsIn.Read(requestId); - RakNet::RakString passwordIdentifier; - bsIn.Read(passwordIdentifier); - bsIn.ReadAlignedBytes((unsigned char *) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - - // Look up used nonce from requestId - char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - if (nonceGenerator.GetNonceById(usedNonce, requestId, packet, true)==false) - return RR_STOP_PROCESSING_AND_DEALLOCATE; - - DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String()); - if (skhi.IsInvalid()==false) - { - char hashedThisNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; - Hash(usedNonce, passwords.ItemAtIndex(skhi), hashedThisNonceAndPw); - if (memcmp(hashedThisNonceAndPw, remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0) - { - // Pass - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS); - bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - bsOut.Write(passwordIdentifier); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); - - // Incoming success, modify packet header to tell user - PushToUser(ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, passwordIdentifier, packet); - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - - // Incoming failure, modify arrived packet header to tell user - packet->data[0]=(MessageID) ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE; - - RakNet::BitStream bsOut; - bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE); - bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - bsOut.Write(passwordIdentifier); - SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); - - return RR_CONTINUE_PROCESSING; -} -void TwoWayAuthentication::OnPasswordResult(Packet *packet) -{ - RakNet::BitStream bsIn(packet->data, packet->length, false); - bsIn.IgnoreBytes(sizeof(MessageID)*1); - char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - bsIn.ReadAlignedBytes((unsigned char *)usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; - bsIn.ReadAlignedBytes((unsigned char *)hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); - RakNet::RakString passwordIdentifier; - bsIn.Read(passwordIdentifier); - - DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String()); - if (skhi.IsInvalid()==false) - { - RakNet::RakString password = passwords.ItemAtIndex(skhi); - char testHash[HASHED_NONCE_AND_PW_LENGTH]; - Hash(usedNonce, password, testHash); - if (memcmp(testHash,hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0) - { - // Lookup the outgoing challenge and remove it from the list - unsigned int i; - AddressOrGUID aog(packet); - for (i=0; i < outgoingChallenges.Size(); i++) - { - if (outgoingChallenges[i].identifier==passwordIdentifier && - outgoingChallenges[i].remoteSystem==aog && - outgoingChallenges[i].sentHash==true) - { - outgoingChallenges.RemoveAtIndex(i); - - PushToUser(packet->data[0], passwordIdentifier, packet); - return; - } - } - } - } -} -void TwoWayAuthentication::Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], RakNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH]) -{ -#if LIBCAT_SECURITY==1 - cat::Skein hash; - if (!hash.BeginKey(HASH_BITS)) return; - hash.Crunch(thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - hash.Crunch(password.C_String(), (int) password.GetLength()); - hash.End(); - hash.Generate(out, HASH_BYTES, STRENGTHENING_FACTOR); -#else - CSHA1 sha1; - sha1.Update((unsigned char *) thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH); - sha1.Update((unsigned char *) password.C_String(), (unsigned int) password.GetLength()); - sha1.Final(); - sha1.GetHash((unsigned char *) out); -#endif -} - -#endif +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TwoWayAuthentication==1 + +#include "TwoWayAuthentication.h" +#include "Rand.h" +#include "GetTime.h" +#include "MessageIdentifiers.h" +#include "BitStream.h" +#include "RakPeerInterface.h" + +#if LIBCAT_SECURITY==1 +static const int HASH_BITS = 256; +static const int HASH_BYTES = HASH_BITS / 8; +static const int STRENGTHENING_FACTOR = 256; +#include +#endif + +using namespace RakNet; + +enum NegotiationIdentifiers +{ + ID_NONCE_REQUEST, + ID_NONCE_REPLY, + ID_HASHED_NONCE_AND_PASSWORD, +}; + +TwoWayAuthentication::NonceGenerator::NonceGenerator() {nextRequestId=0;} +TwoWayAuthentication::NonceGenerator::~NonceGenerator() +{ + Clear(); +} +void TwoWayAuthentication::NonceGenerator::GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, RakNet::AddressOrGUID remoteSystem) +{ + TwoWayAuthentication::NonceAndRemoteSystemRequest *narsr = RakNet::OP_NEW(_FILE_AND_LINE_); + narsr->remoteSystem=remoteSystem; + GenerateNonce(narsr->nonce); + narsr->requestId=nextRequestId++; + *requestId=narsr->requestId; + memcpy(nonce,narsr->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + narsr->whenGenerated=RakNet::GetTime(); + generatedNonces.Push(narsr,_FILE_AND_LINE_); +} +void TwoWayAuthentication::NonceGenerator::GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]) +{ + fillBufferMT(nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); +} +bool TwoWayAuthentication::NonceGenerator::GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, RakNet::AddressOrGUID remoteSystem, bool popIfFound) +{ + unsigned int i; + for (i=0; i < generatedNonces.Size(); i++) + { + if (generatedNonces[i]->requestId==requestId) + { + if (remoteSystem==generatedNonces[i]->remoteSystem) + { + memcpy(nonce,generatedNonces[i]->nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + if (popIfFound) + { + RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); + generatedNonces.RemoveAtIndex(i); + } + return true; + } + else + { + return false; + } + } + } + return false; +} +void TwoWayAuthentication::NonceGenerator::Clear(void) +{ + unsigned int i; + for (i=0; i < generatedNonces.Size(); i++) + RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); + generatedNonces.Clear(true,_FILE_AND_LINE_); +} +void TwoWayAuthentication::NonceGenerator::ClearByAddress(RakNet::AddressOrGUID remoteSystem) +{ + unsigned int i=0; + while (i < generatedNonces.Size()) + { + if (generatedNonces[i]->remoteSystem==remoteSystem) + { + RakNet::OP_DELETE(generatedNonces[i],_FILE_AND_LINE_); + generatedNonces.RemoveAtIndex(i); + } + else + { + i++; + } + } +} +void TwoWayAuthentication::NonceGenerator::Update(RakNet::Time curTime) +{ + if (generatedNonces.Size()>0 && GreaterThan(curTime-5000, generatedNonces[0]->whenGenerated)) + { + RakNet::OP_DELETE(generatedNonces[0], _FILE_AND_LINE_); + generatedNonces.RemoveAtIndex(0); + } +} +TwoWayAuthentication::TwoWayAuthentication() +{ + whenLastTimeoutCheck=RakNet::GetTime(); + seedMT(RakNet::GetTimeMS()); +} +TwoWayAuthentication::~TwoWayAuthentication() +{ + Clear(); +} +bool TwoWayAuthentication::AddPassword(RakNet::RakString identifier, RakNet::RakString password) +{ + if (password.IsEmpty()) + return false; + + if (identifier.IsEmpty()) + return false; + + if (password==identifier) + return false; // Insecure + + if (passwords.GetIndexOf(identifier.C_String()).IsInvalid()==false) + return false; // This identifier already in use + + passwords.Push(identifier, password,_FILE_AND_LINE_); + return true; +} +bool TwoWayAuthentication::Challenge(RakNet::RakString identifier, AddressOrGUID remoteSystem) +{ + DataStructures::HashIndex skhi = passwords.GetIndexOf(identifier.C_String()); + if (skhi.IsInvalid()) + return false; + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); + bsOut.Write((MessageID)ID_NONCE_REQUEST); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,remoteSystem,false); + + PendingChallenge pc; + pc.identifier=identifier; + pc.remoteSystem=remoteSystem; + pc.time=RakNet::GetTime(); + pc.sentHash=false; + outgoingChallenges.Push(pc,_FILE_AND_LINE_); + + return true; +} +void TwoWayAuthentication::Update(void) +{ + RakNet::Time curTime = RakNet::GetTime(); + nonceGenerator.Update(curTime); + if (GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, whenLastTimeoutCheck)) + { + while (outgoingChallenges.Size() && GreaterThan(curTime - CHALLENGE_MINIMUM_TIMEOUT, outgoingChallenges.Peek().time)) + { + PendingChallenge pc = outgoingChallenges.Pop(); + + // Tell the user about the timeout + PushToUser(ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, pc.identifier, pc.remoteSystem); + } + + whenLastTimeoutCheck=curTime+CHALLENGE_MINIMUM_TIMEOUT; + } +} +PluginReceiveResult TwoWayAuthentication::OnReceive(Packet *packet) +{ + switch (packet->data[0]) + { + case ID_TWO_WAY_AUTHENTICATION_NEGOTIATION: + { + if (packet->length>=sizeof(MessageID)*2) + { + switch (packet->data[sizeof(MessageID)]) + { + case ID_NONCE_REQUEST: + { + OnNonceRequest(packet); + } + break; + case ID_NONCE_REPLY: + { + OnNonceReply(packet); + } + break; + case ID_HASHED_NONCE_AND_PASSWORD: + { + return OnHashedNonceAndPassword(packet); + } + break; + } + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE: + case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS: + { + if (packet->wasGeneratedLocally==false) + { + OnPasswordResult(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + else + break; + } + break; + // These should only be generated locally + case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS: + case ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE: + case ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT: + if (packet->wasGeneratedLocally==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + break; + } + + return RR_CONTINUE_PROCESSING; +} +void TwoWayAuthentication::OnRakPeerShutdown(void) +{ + Clear(); +} +void TwoWayAuthentication::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + + // Remove from pending challenges + unsigned int i=0; + while (i < outgoingChallenges.Size()) + { + if ((rakNetGUID!=UNASSIGNED_RAKNET_GUID && outgoingChallenges[i].remoteSystem.rakNetGuid==rakNetGUID) || + (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && outgoingChallenges[i].remoteSystem.systemAddress==systemAddress)) + { + outgoingChallenges.RemoveAtIndex(i); + } + else + { + i++; + } + } + + if (rakNetGUID!=UNASSIGNED_RAKNET_GUID) + nonceGenerator.ClearByAddress(rakNetGUID); + else + nonceGenerator.ClearByAddress(systemAddress); +} +void TwoWayAuthentication::Clear(void) +{ + outgoingChallenges.Clear(_FILE_AND_LINE_); + passwords.Clear(_FILE_AND_LINE_); + nonceGenerator.Clear(); +} +void TwoWayAuthentication::PushToUser(MessageID messageId, RakNet::RakString password, RakNet::AddressOrGUID remoteSystem) +{ + RakNet::BitStream output; + output.Write(messageId); + if (password.IsEmpty()==false) + output.Write(password); + Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed()); + p->systemAddress=remoteSystem.systemAddress; + p->systemAddress.systemIndex=(SystemIndex)-1; + p->guid=remoteSystem.rakNetGuid; + p->wasGeneratedLocally=true; + memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed()); + rakPeerInterface->PushBackPacket(p, true); +} +void TwoWayAuthentication::OnNonceRequest(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + unsigned short requestId; + nonceGenerator.GetNonce(nonce,&requestId,packet); + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); + bsOut.Write((MessageID)ID_NONCE_REPLY); + bsOut.Write(requestId); + bsOut.WriteAlignedBytes((const unsigned char*) nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); +} +void TwoWayAuthentication::OnNonceReply(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + unsigned short requestId; + bsIn.Read(requestId); + bsIn.ReadAlignedBytes((unsigned char *) thierNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + + // Lookup one of the negotiations for this guid/system address + AddressOrGUID aog(packet); + unsigned int i; + for (i=0; i < outgoingChallenges.Size(); i++) + { + if (outgoingChallenges[i].remoteSystem==aog && outgoingChallenges[i].sentHash==false) + { + outgoingChallenges[i].sentHash=true; + + // Get the password for this identifier + DataStructures::HashIndex skhi = passwords.GetIndexOf(outgoingChallenges[i].identifier.C_String()); + if (skhi.IsInvalid()==false) + { + RakNet::RakString password = passwords.ItemAtIndex(skhi); + + // Hash their nonce with password and reply + char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; + Hash(thierNonce, password, hashedNonceAndPw); + + // Send + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION); + bsOut.Write((MessageID)ID_HASHED_NONCE_AND_PASSWORD); + bsOut.Write(requestId); + bsOut.Write(outgoingChallenges[i].identifier); // Identifier helps the other system lookup the password quickly. + bsOut.WriteAlignedBytes((const unsigned char*) hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); + } + + return; + } + } +} +PluginReceiveResult TwoWayAuthentication::OnHashedNonceAndPassword(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*2); + + char remoteHashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; + unsigned short requestId; + bsIn.Read(requestId); + RakNet::RakString passwordIdentifier; + bsIn.Read(passwordIdentifier); + bsIn.ReadAlignedBytes((unsigned char *) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + + // Look up used nonce from requestId + char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + if (nonceGenerator.GetNonceById(usedNonce, requestId, packet, true)==false) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + + DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String()); + if (skhi.IsInvalid()==false) + { + char hashedThisNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; + Hash(usedNonce, passwords.ItemAtIndex(skhi), hashedThisNonceAndPw); + if (memcmp(hashedThisNonceAndPw, remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0) + { + // Pass + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS); + bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + bsOut.Write(passwordIdentifier); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); + + // Incoming success, modify packet header to tell user + PushToUser(ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, passwordIdentifier, packet); + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + + // Incoming failure, modify arrived packet header to tell user + packet->data[0]=(MessageID) ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE; + + RakNet::BitStream bsOut; + bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE); + bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + bsOut.Write(passwordIdentifier); + SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false); + + return RR_CONTINUE_PROCESSING; +} +void TwoWayAuthentication::OnPasswordResult(Packet *packet) +{ + RakNet::BitStream bsIn(packet->data, packet->length, false); + bsIn.IgnoreBytes(sizeof(MessageID)*1); + char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + bsIn.ReadAlignedBytes((unsigned char *)usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH]; + bsIn.ReadAlignedBytes((unsigned char *)hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH); + RakNet::RakString passwordIdentifier; + bsIn.Read(passwordIdentifier); + + DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String()); + if (skhi.IsInvalid()==false) + { + RakNet::RakString password = passwords.ItemAtIndex(skhi); + char testHash[HASHED_NONCE_AND_PW_LENGTH]; + Hash(usedNonce, password, testHash); + if (memcmp(testHash,hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0) + { + // Lookup the outgoing challenge and remove it from the list + unsigned int i; + AddressOrGUID aog(packet); + for (i=0; i < outgoingChallenges.Size(); i++) + { + if (outgoingChallenges[i].identifier==passwordIdentifier && + outgoingChallenges[i].remoteSystem==aog && + outgoingChallenges[i].sentHash==true) + { + outgoingChallenges.RemoveAtIndex(i); + + PushToUser(packet->data[0], passwordIdentifier, packet); + return; + } + } + } + } +} +void TwoWayAuthentication::Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], RakNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH]) +{ +#if LIBCAT_SECURITY==1 + cat::Skein hash; + if (!hash.BeginKey(HASH_BITS)) return; + hash.Crunch(thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + hash.Crunch(password.C_String(), (int) password.GetLength()); + hash.End(); + hash.Generate(out, HASH_BYTES, STRENGTHENING_FACTOR); +#else + CSHA1 sha1; + sha1.Update((unsigned char *) thierNonce, TWO_WAY_AUTHENTICATION_NONCE_LENGTH); + sha1.Update((unsigned char *) password.C_String(), (unsigned int) password.GetLength()); + sha1.Final(); + sha1.GetHash((unsigned char *) out); +#endif +} + +#endif diff --git a/src/raknet/TwoWayAuthentication.h b/src/raknet/TwoWayAuthentication.h index 7993f8e..3a3b850 100755 --- a/src/raknet/TwoWayAuthentication.h +++ b/src/raknet/TwoWayAuthentication.h @@ -1,146 +1,146 @@ -/// \file TwoWayAuthentication.h -/// \brief Implements two way authentication -/// \details Given two systems, each of whom known a common password, verify the password without transmitting it -/// This can be used to determine what permissions are should be allowed to the other system -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_TwoWayAuthentication==1 - -#ifndef __TWO_WAY_AUTHENTICATION_H -#define __TWO_WAY_AUTHENTICATION_H - -// How often to change the nonce. -#define NONCE_TIMEOUT_MS 10000 -// How often to check for ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, and the minimum timeout time. Maximum is double this value. -#define CHALLENGE_MINIMUM_TIMEOUT 3000 - -#if LIBCAT_SECURITY==1 -// From CPP FILE: -// static const int HASH_BITS = 256; -// static const int HASH_BYTES = HASH_BITS / 8; -// static const int STRENGTHENING_FACTOR = 1000; -#define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 32 -#define HASHED_NONCE_AND_PW_LENGTH 32 -#else -#include "SHA1.h" -#define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 20 -#define HASHED_NONCE_AND_PW_LENGTH SHA1_LENGTH -#endif - -#include "PluginInterface2.h" -#include "RakMemoryOverride.h" -#include "NativeTypes.h" -#include "RakString.h" -#include "DS_Hash.h" -#include "DS_Queue.h" - -typedef int64_t FCM2Guid; - -namespace RakNet -{ -/// Forward declarations -class RakPeerInterface; - -/// \brief Implements two way authentication -/// \details Given two systems, each of whom known a common password / identifier pair, verify the password without transmitting it -/// This can be used to determine what permissions are should be allowed to the other system -/// If the other system should not send any data until authentication passes, you can use the MessageFilter plugin for this. Call MessageFilter::SetAllowMessageID() including ID_TWO_WAY_AUTHENTICATION_NEGOTIATION when doing so. Also attach MessageFilter first in the list of plugins -/// \note If other systems challenges us, and fails, you will get ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILED. -/// \ingroup PLUGINS_GROUP -class RAK_DLL_EXPORT TwoWayAuthentication : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(TwoWayAuthentication) - - TwoWayAuthentication(); - virtual ~TwoWayAuthentication(); - - /// \brief Adds a password to the list of passwords the system will accept - /// \details Each password, which is secret and not transmitted, is identified by \a identifier. - /// \a identifier is transmitted in plaintext with the request. It is only needed because the system supports multiple password. - /// It is used to only hash against once password on the remote system, rather than having to hash against every known password. - /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure - /// \param[in] password The password to add - /// \return True on success, false on identifier==password, either identifier or password is blank, or identifier is already in use - bool AddPassword(RakNet::RakString identifier, RakNet::RakString password); - - /// \brief Challenge another system for the specified identifier - /// \details After calling Challenge, you will get back ID_TWO_WAY_AUTHENTICATION_SUCCESS, ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, or ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILED - /// ID_TWO_WAY_AUTHENTICATION_SUCCESS will be returned if and only if the other system has called AddPassword() with the same identifier\password pair as this system. - /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure - /// \return True on success, false on remote system not connected, or identifier not previously added with AddPassword() - bool Challenge(RakNet::RakString identifier, AddressOrGUID remoteSystem); - - /// \brief Free all memory - void Clear(void); - - /// \internal - virtual void Update(void); - /// \internal - virtual PluginReceiveResult OnReceive(Packet *packet); - /// \internal - virtual void OnRakPeerShutdown(void); - /// \internal - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - /// \internal - struct PendingChallenge - { - RakNet::RakString identifier; - AddressOrGUID remoteSystem; - RakNet::Time time; - bool sentHash; - }; - - DataStructures::Queue outgoingChallenges; - - /// \internal - struct NonceAndRemoteSystemRequest - { - char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; - RakNet::AddressOrGUID remoteSystem; - unsigned short requestId; - RakNet::Time whenGenerated; - }; - /// \internal - struct RAK_DLL_EXPORT NonceGenerator - { - NonceGenerator(); - ~NonceGenerator(); - void GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, RakNet::AddressOrGUID remoteSystem); - void GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]); - bool GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, RakNet::AddressOrGUID remoteSystem, bool popIfFound); - void Clear(void); - void ClearByAddress(RakNet::AddressOrGUID remoteSystem); - void Update(RakNet::Time curTime); - - DataStructures::List generatedNonces; - unsigned short nextRequestId; - }; - -protected: - void PushToUser(MessageID messageId, RakNet::RakString password, RakNet::AddressOrGUID remoteSystem); - // Key is identifier, data is password - DataStructures::Hash passwords; - - RakNet::Time whenLastTimeoutCheck; - - NonceGenerator nonceGenerator; - - void OnNonceRequest(Packet *packet); - void OnNonceReply(Packet *packet); - PluginReceiveResult OnHashedNonceAndPassword(Packet *packet); - void OnPasswordResult(Packet *packet); - void Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], RakNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH]); -}; - -} // namespace RakNet - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file TwoWayAuthentication.h +/// \brief Implements two way authentication +/// \details Given two systems, each of whom known a common password, verify the password without transmitting it +/// This can be used to determine what permissions are should be allowed to the other system +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_TwoWayAuthentication==1 + +#ifndef __TWO_WAY_AUTHENTICATION_H +#define __TWO_WAY_AUTHENTICATION_H + +// How often to change the nonce. +#define NONCE_TIMEOUT_MS 10000 +// How often to check for ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, and the minimum timeout time. Maximum is double this value. +#define CHALLENGE_MINIMUM_TIMEOUT 3000 + +#if LIBCAT_SECURITY==1 +// From CPP FILE: +// static const int HASH_BITS = 256; +// static const int HASH_BYTES = HASH_BITS / 8; +// static const int STRENGTHENING_FACTOR = 1000; +#define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 32 +#define HASHED_NONCE_AND_PW_LENGTH 32 +#else +#include "SHA1.h" +#define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 20 +#define HASHED_NONCE_AND_PW_LENGTH SHA1_LENGTH +#endif + +#include "PluginInterface2.h" +#include "RakMemoryOverride.h" +#include "NativeTypes.h" +#include "RakString.h" +#include "DS_Hash.h" +#include "DS_Queue.h" + +typedef int64_t FCM2Guid; + +namespace RakNet +{ +/// Forward declarations +class RakPeerInterface; + +/// \brief Implements two way authentication +/// \details Given two systems, each of whom known a common password / identifier pair, verify the password without transmitting it +/// This can be used to determine what permissions are should be allowed to the other system +/// If the other system should not send any data until authentication passes, you can use the MessageFilter plugin for this. Call MessageFilter::SetAllowMessageID() including ID_TWO_WAY_AUTHENTICATION_NEGOTIATION when doing so. Also attach MessageFilter first in the list of plugins +/// \note If other systems challenges us, and fails, you will get ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILED. +/// \ingroup PLUGINS_GROUP +class RAK_DLL_EXPORT TwoWayAuthentication : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(TwoWayAuthentication) + + TwoWayAuthentication(); + virtual ~TwoWayAuthentication(); + + /// \brief Adds a password to the list of passwords the system will accept + /// \details Each password, which is secret and not transmitted, is identified by \a identifier. + /// \a identifier is transmitted in plaintext with the request. It is only needed because the system supports multiple password. + /// It is used to only hash against once password on the remote system, rather than having to hash against every known password. + /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure + /// \param[in] password The password to add + /// \return True on success, false on identifier==password, either identifier or password is blank, or identifier is already in use + bool AddPassword(RakNet::RakString identifier, RakNet::RakString password); + + /// \brief Challenge another system for the specified identifier + /// \details After calling Challenge, you will get back ID_TWO_WAY_AUTHENTICATION_SUCCESS, ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, or ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILED + /// ID_TWO_WAY_AUTHENTICATION_SUCCESS will be returned if and only if the other system has called AddPassword() with the same identifier\password pair as this system. + /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure + /// \return True on success, false on remote system not connected, or identifier not previously added with AddPassword() + bool Challenge(RakNet::RakString identifier, AddressOrGUID remoteSystem); + + /// \brief Free all memory + void Clear(void); + + /// \internal + virtual void Update(void); + /// \internal + virtual PluginReceiveResult OnReceive(Packet *packet); + /// \internal + virtual void OnRakPeerShutdown(void); + /// \internal + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + /// \internal + struct PendingChallenge + { + RakNet::RakString identifier; + AddressOrGUID remoteSystem; + RakNet::Time time; + bool sentHash; + }; + + DataStructures::Queue outgoingChallenges; + + /// \internal + struct NonceAndRemoteSystemRequest + { + char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; + RakNet::AddressOrGUID remoteSystem; + unsigned short requestId; + RakNet::Time whenGenerated; + }; + /// \internal + struct RAK_DLL_EXPORT NonceGenerator + { + NonceGenerator(); + ~NonceGenerator(); + void GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, RakNet::AddressOrGUID remoteSystem); + void GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]); + bool GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, RakNet::AddressOrGUID remoteSystem, bool popIfFound); + void Clear(void); + void ClearByAddress(RakNet::AddressOrGUID remoteSystem); + void Update(RakNet::Time curTime); + + DataStructures::List generatedNonces; + unsigned short nextRequestId; + }; + +protected: + void PushToUser(MessageID messageId, RakNet::RakString password, RakNet::AddressOrGUID remoteSystem); + // Key is identifier, data is password + DataStructures::Hash passwords; + + RakNet::Time whenLastTimeoutCheck; + + NonceGenerator nonceGenerator; + + void OnNonceRequest(Packet *packet); + void OnNonceReply(Packet *packet); + PluginReceiveResult OnHashedNonceAndPassword(Packet *packet); + void OnPasswordResult(Packet *packet); + void Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], RakNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH]); +}; + +} // namespace RakNet + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/UDPForwarder.cpp b/src/raknet/UDPForwarder.cpp index c043e5e..e1038c5 100755 --- a/src/raknet/UDPForwarder.cpp +++ b/src/raknet/UDPForwarder.cpp @@ -1,816 +1,816 @@ -#include "UDPForwarder.h" - -#if _RAKNET_SUPPORT_UDPForwarder==1 - -#include "GetTime.h" -#include "MTUSize.h" -#include "SocketLayer.h" -#include "WSAStartupSingleton.h" -#include "RakSleep.h" -#include "DS_OrderedList.h" -#include "LinuxStrings.h" -#include "SocketDefines.h" -#include "VitaIncludes.h" - -using namespace RakNet; -static const unsigned short DEFAULT_MAX_FORWARD_ENTRIES=64; - -#ifdef UDP_FORWARDER_EXECUTE_THREADED -namespace RakNet -{ - RAK_THREAD_DECLARATION(UpdateUDPForwarder); -} -#endif - -int UDPForwarder::SrcAndDestForwardEntryComp( const UDPForwarder::SrcAndDest &inputKey, UDPForwarder::ForwardEntry * const &cls ) -{ - if (inputKey.source < cls->srcAndDest.source) - return -1; - - if (inputKey.source > cls->srcAndDest.source) - return 1; - - if (inputKey.dest < cls->srcAndDest.dest) - return -1; - - if (inputKey.dest > cls->srcAndDest.dest) - return 1; - - return 0; -} -/* -bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) -{ - return inputKey.Get().source < cls->srcAndDest.source || - (inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest < cls->srcAndDest.dest); -} -bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) -{ - return inputKey.Get().source > cls->srcAndDest.source || - (inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest > cls->srcAndDest.dest); -} -bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) -{ - return inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest == cls->srcAndDest.dest; -} -*/ - - -UDPForwarder::ForwardEntry::ForwardEntry() {socket=INVALID_SOCKET; timeLastDatagramForwarded=RakNet::GetTimeMS(); updatedSourcePort=false; updatedDestPort=false;} -UDPForwarder::ForwardEntry::~ForwardEntry() { - if (socket!=INVALID_SOCKET) - closesocket__(socket); -} - -UDPForwarder::UDPForwarder() -{ -#ifdef _WIN32 - WSAStartupSingleton::AddRef(); -#endif - - maxForwardEntries=DEFAULT_MAX_FORWARD_ENTRIES; - isRunning=false; - threadRunning=false; - - - - -} -UDPForwarder::~UDPForwarder() -{ - Shutdown(); - -#ifdef _WIN32 - WSAStartupSingleton::Deref(); -#endif -} -void UDPForwarder::Startup(void) -{ - if (isRunning==true) - return; - - isRunning=true; - threadRunning=false; - -#ifdef UDP_FORWARDER_EXECUTE_THREADED - - - - - - int errorCode; - - - - - errorCode = RakNet::RakThread::Create(UpdateUDPForwarder, this); - - if ( errorCode != 0 ) - { - RakAssert(0); - return; - } - - while (threadRunning==false) - RakSleep(30); -#endif -} -void UDPForwarder::Shutdown(void) -{ - if (isRunning==false) - return; - - isRunning=false; - -#ifdef UDP_FORWARDER_EXECUTE_THREADED - while (threadRunning==true) - RakSleep(30); -#endif - - unsigned int j; - for (j=0; j < forwardList.Size(); j++) - RakNet::OP_DELETE(forwardList[j],_FILE_AND_LINE_); - forwardList.Clear(false, _FILE_AND_LINE_); - - - - - - -} -void UDPForwarder::Update(void) -{ -#ifndef UDP_FORWARDER_EXECUTE_THREADED - #if RAKNET_SUPPORT_IPV6!=1 - UpdateThreaded_Old(); - #else - UpdateThreaded(); - #endif -#endif -} -void UDPForwarder::UpdateThreaded_Old(void) -{ - fd_set readFD; - //fd_set exceptionFD; - FD_ZERO(&readFD); - // FD_ZERO(&exceptionFD); - int selectResult; - - - timeval tv; - tv.tv_sec=0; - tv.tv_usec=0; - - - RakNet::TimeMS curTime = RakNet::GetTimeMS(); - - SOCKET largestDescriptor=0; - unsigned int i; - - // Remove unused entries - i=0; - while (i < forwardList.Size()) - { - if (curTime > forwardList[i]->timeLastDatagramForwarded && // Account for timestamp wrap - curTime > forwardList[i]->timeLastDatagramForwarded+forwardList[i]->timeoutOnNoDataMS) - { - RakNet::OP_DELETE(forwardList[i],_FILE_AND_LINE_); - forwardList.RemoveAtIndex(i); - } - else - i++; - } - - if (forwardList.Size()==0) - return; - - for (i=0; i < forwardList.Size(); i++) - { -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - FD_SET(forwardList[i]->socket, &readFD); - // FD_SET(forwardList[i]->readSocket, &exceptionFD); - - if (forwardList[i]->socket > largestDescriptor) - largestDescriptor = forwardList[i]->socket; - } - - - selectResult=(int) select__((int) largestDescriptor+1, &readFD, 0, 0, &tv); - - - - - char data[ MAXIMUM_MTU_SIZE ]; - sockaddr_in sa; - socklen_t len2; - - if (selectResult > 0) - { - DataStructures::Queue entriesToRead; - ForwardEntry *forwardEntry; - - for (i=0; i < forwardList.Size(); i++) - { - forwardEntry = forwardList[i]; - // I do this because I'm updating the forwardList, and don't want to lose FD_ISSET as the list is no longer in order - if (FD_ISSET(forwardEntry->socket, &readFD)) - entriesToRead.Push(forwardEntry,_FILE_AND_LINE_); - } - - while (entriesToRead.IsEmpty()==false) - { - forwardEntry=entriesToRead.Pop(); - - const int flag=0; - int receivedDataLen, len=0; - unsigned short portnum=0; - len2 = sizeof( sa ); - sa.sin_family = AF_INET; - receivedDataLen = recvfrom__( forwardEntry->socket, data, MAXIMUM_MTU_SIZE, flag, ( sockaddr* ) & sa, ( socklen_t* ) & len2 ); - - if (receivedDataLen<0) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = WSAGetLastError(); - - if (dwIOError!=WSAECONNRESET && dwIOError!=WSAEINTR && dwIOError!=WSAETIMEDOUT) - { - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "recvfrom failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); - } -#endif - continue; - } - - portnum = ntohs( sa.sin_port ); - if (forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->updatedSourcePort==false && forwardEntry->srcAndDest.dest.GetPort()!=portnum) - { - forwardEntry->updatedSourcePort=true; - - if (forwardEntry->srcAndDest.source.GetPort()!=portnum) - { - unsigned int index; - SrcAndDest srcAndDest(forwardEntry->srcAndDest.dest, forwardEntry->srcAndDest.source); - bool objectExists; - index = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - if (objectExists) - { - forwardList.RemoveAtIndex(index); - } - forwardEntry->srcAndDest.source.SetPort(portnum); - forwardList.Insert(forwardEntry->srcAndDest,forwardEntry,true,_FILE_AND_LINE_); - } - } - - if (forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->srcAndDest.source.GetPort()==portnum) - { - // Forward to dest - len=0; - sockaddr_in saOut; - saOut.sin_port = forwardEntry->srcAndDest.dest.GetPortNetworkOrder(); // User port - saOut.sin_addr.s_addr = forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr; - saOut.sin_family = AF_INET; - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & saOut, sizeof( saOut ) ); - } - while ( len == 0 ); - - // printf("1. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); - - forwardEntry->timeLastDatagramForwarded=curTime; - } - - if (forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->updatedDestPort==false && forwardEntry->srcAndDest.source.GetPort()!=portnum) - { - forwardEntry->updatedDestPort=true; - - if (forwardEntry->srcAndDest.dest.GetPort()!=portnum) - { - unsigned int index; - SrcAndDest srcAndDest(forwardEntry->srcAndDest.source, forwardEntry->srcAndDest.dest); - bool objectExists; - index = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - if (objectExists) - { - forwardList.RemoveAtIndex(index); - } - forwardEntry->srcAndDest.dest.SetPort(portnum); - forwardList.Insert(forwardEntry->srcAndDest,forwardEntry,true,_FILE_AND_LINE_); - } - } - - if (forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->srcAndDest.dest.GetPort()==portnum) - { - // Forward to source - len=0; - sockaddr_in saOut; - saOut.sin_port = forwardEntry->srcAndDest.source.GetPortNetworkOrder(); // User port - saOut.sin_addr.s_addr = forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr; - saOut.sin_family = AF_INET; - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & saOut, sizeof( saOut ) ); - } - while ( len == 0 ); - - // printf("2. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); - - forwardEntry->timeLastDatagramForwarded=curTime; - } - } - } -} -#if RAKNET_SUPPORT_IPV6==1 -void UDPForwarder::UpdateThreaded(void) -{ - fd_set readFD; - //fd_set exceptionFD; - FD_ZERO(&readFD); -// FD_ZERO(&exceptionFD); - timeval tv; - int selectResult; - tv.tv_sec=0; - tv.tv_usec=0; - - RakNet::TimeMS curTime = RakNet::GetTimeMS(); - - SOCKET largestDescriptor=0; - unsigned int i; - - // Remove unused entries - i=0; - while (i < forwardList.Size()) - { - if (curTime > forwardList[i]->timeLastDatagramForwarded && // Account for timestamp wrap - curTime > forwardList[i]->timeLastDatagramForwarded+forwardList[i]->timeoutOnNoDataMS) - { - RakNet::OP_DELETE(forwardList[i],_FILE_AND_LINE_); - forwardList.RemoveAtIndex(i); - } - else - i++; - } - - if (forwardList.Size()==0) - return; - - for (i=0; i < forwardList.Size(); i++) - { -#ifdef _MSC_VER -#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant -#endif - FD_SET(forwardList[i]->socket, &readFD); -// FD_SET(forwardList[i]->readSocket, &exceptionFD); - - if (forwardList[i]->socket > largestDescriptor) - largestDescriptor = forwardList[i]->socket; - } - - selectResult=(int) select__((int) largestDescriptor+1, &readFD, 0, 0, &tv); - - char data[ MAXIMUM_MTU_SIZE ]; - sockaddr_storage their_addr; - sockaddr* sockAddrPtr; - socklen_t sockLen; - socklen_t* socketlenPtr=(socklen_t*) &sockLen; - sockaddr_in *sockAddrIn; - sockaddr_in6 *sockAddrIn6; - SystemAddress receivedAddr; - sockLen=sizeof(their_addr); - sockAddrPtr=(sockaddr*) &their_addr; - - if (selectResult > 0) - { - DataStructures::Queue entriesToRead; - ForwardEntry *forwardEntry; - - for (i=0; i < forwardList.Size(); i++) - { - forwardEntry = forwardList[i]; - // I do this because I'm updating the forwardList, and don't want to lose FD_ISSET as the list is no longer in order - if (FD_ISSET(forwardEntry->socket, &readFD)) - entriesToRead.Push(forwardEntry,_FILE_AND_LINE_); - } - - while (entriesToRead.IsEmpty()==false) - { - forwardEntry=entriesToRead.Pop(); - - const int flag=0; - int receivedDataLen, len=0; - receivedDataLen = recvfrom__( forwardEntry->socket, data, MAXIMUM_MTU_SIZE, flag, sockAddrPtr, socketlenPtr ); - - if (receivedDataLen<0) - { -#if defined(_WIN32) && defined(_DEBUG) - DWORD dwIOError = WSAGetLastError(); - - if (dwIOError!=WSAECONNRESET && dwIOError!=WSAEINTR && dwIOError!=WSAETIMEDOUT) - { - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "recvfrom failed:Error code - %d\n%s", dwIOError, messageBuffer ); - - //Free the buffer. - LocalFree( messageBuffer ); - } -#endif - continue; - } - - if (their_addr.ss_family==AF_INET) - { - sockAddrIn=(sockaddr_in *)&their_addr; - sockAddrIn6=0; - memcpy(&receivedAddr.address.addr4,sockAddrIn,sizeof(sockaddr_in)); - // receivedAddr.address.addr4.sin_port=ntohs( sockAddrIn->sin_port ); - } - else - { - sockAddrIn=0; - sockAddrIn6=(sockaddr_in6 *)&their_addr; - memcpy(&receivedAddr.address.addr6,sockAddrIn6,sizeof(sockaddr_in6)); - // receivedAddr.address.addr6.sin6_port=ntohs( sockAddrIn6->sin6_port ); - } - - if (forwardEntry->srcAndDest.source.EqualsExcludingPort(receivedAddr) && forwardEntry->updatedSourcePort==false && forwardEntry->srcAndDest.dest.GetPort()!=receivedAddr.GetPort()) - { - forwardEntry->updatedSourcePort=true; - - if (forwardEntry->srcAndDest.source.GetPort()!=receivedAddr.GetPort()) - { - unsigned int index; - SrcAndDest srcAndDest(forwardEntry->srcAndDest.dest, forwardEntry->srcAndDest.source); - bool objectExists; - index=forwardList.GetIndexFromKey(srcAndDest, &objectExists); - forwardList.RemoveAtIndex(index); - forwardEntry->srcAndDest.source.SetPort(receivedAddr.GetPort()); - forwardList.Push(forwardEntry,forwardEntry->srcAndDest,_FILE_AND_LINE_); - } - } - - if (forwardEntry->srcAndDest.source.EqualsExcludingPort(receivedAddr) && forwardEntry->srcAndDest.source.GetPort()==receivedAddr.GetPort()) - { - // Forward to dest - len=0; - - if (forwardEntry->srcAndDest.dest.address.addr4.sin_family==AF_INET) - { - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.dest.address.addr4, sizeof( sockaddr_in ) ); - } - while ( len == 0 ); - } - else - { - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.dest.address.addr6, sizeof( sockaddr_in ) ); - } - while ( len == 0 ); - } - - - // printf("1. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); - - forwardEntry->timeLastDatagramForwarded=curTime; - } - - if (forwardEntry->srcAndDest.dest.EqualsExcludingPort(receivedAddr) && forwardEntry->updatedDestPort==false && forwardEntry->srcAndDest.source.GetPort()!=receivedAddr.GetPort()) - { - forwardEntry->updatedDestPort=true; - - if (forwardEntry->srcAndDest.dest.GetPort()!=receivedAddr.GetPort()) - { - unsigned int index; - SrcAndDest srcAndDest(forwardEntry->srcAndDest.source, forwardEntry->srcAndDest.dest); - index=forwardList.GetIndexOf(srcAndDest); - forwardList.RemoveAtIndex(index); - forwardEntry->srcAndDest.dest.SetPort(receivedAddr.GetPort()); - forwardList.Push(forwardEntry,forwardEntry->srcAndDest,_FILE_AND_LINE_); - } - } - - if (forwardEntry->srcAndDest.dest.EqualsExcludingPort(receivedAddr) && forwardEntry->srcAndDest.dest.GetPort()==receivedAddr.GetPort()) - { - // Forward to source - len=0; - if (forwardEntry->srcAndDest.source.address.addr4.sin_family==AF_INET) - { - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.source.address.addr4, sizeof( sockaddr_in ) ); - } - while ( len == 0 ); - } - else - { - do - { - len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.source.address.addr6, sizeof( sockaddr_in ) ); - } - while ( len == 0 ); - } - - // printf("2. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); - - forwardEntry->timeLastDatagramForwarded=curTime; - } - } - } -} -#endif // #if RAKNET_SUPPORT_IPV6!=1 -void UDPForwarder::SetMaxForwardEntries(unsigned short maxEntries) -{ - RakAssert(maxEntries>0 && maxEntries<65535/2); - maxForwardEntries=maxEntries; -} -int UDPForwarder::GetMaxForwardEntries(void) const -{ - return maxForwardEntries; -} -int UDPForwarder::GetUsedForwardEntries(void) const -{ - return (int) forwardList.Size(); -} -UDPForwarderResult UDPForwarder::AddForwardingEntry(SrcAndDest srcAndDest, RakNet::TimeMS timeoutOnNoDataMS, unsigned short *port, const char *forceHostAddress, short socketFamily) -{ - (void) socketFamily; - - unsigned int insertionIndex; - bool objectExists; - insertionIndex = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - if (objectExists==false) - { -#if RAKNET_SUPPORT_IPV6!=1 - int sock_opt; - sockaddr_in listenerSocketAddress; - listenerSocketAddress.sin_port = 0; - ForwardEntry *fe = RakNet::OP_NEW(_FILE_AND_LINE_); - fe->srcAndDest=srcAndDest; - fe->timeoutOnNoDataMS=timeoutOnNoDataMS; - fe->socket = socket__( AF_INET, SOCK_DGRAM, 0 ); - - //printf("Made socket %i\n", fe->readSocket); - - // This doubles the max throughput rate - sock_opt=1024*256; - setsockopt__(fe->socket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - // Immediate hard close. Don't linger the readSocket, or recreating the readSocket quickly on Vista fails. - sock_opt=0; - setsockopt__(fe->socket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - listenerSocketAddress.sin_family = AF_INET; - - if (forceHostAddress && forceHostAddress[0]) - { - - - - listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); - - } - else - { - listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; - } - - int ret = bind__( fe->socket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); - if (ret==-1) - { - RakNet::OP_DELETE(fe,_FILE_AND_LINE_); - return UDPFORWARDER_BIND_FAILED; - } -#else - ForwardEntry *fe = RakNet::OP_NEW(_FILE_AND_LINE_); - fe->srcAndDest=srcAndDest; - fe->timeoutOnNoDataMS=timeoutOnNoDataMS; - fe->socket=INVALID_SOCKET; - - struct addrinfo hints; - memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty - hints.ai_family = socketFamily; - hints.ai_socktype = SOCK_DGRAM; // UDP sockets - hints.ai_flags = AI_PASSIVE; // fill in my IP for me - struct addrinfo *servinfo=0, *aip; // will point to the results - - - RakAssert(forceHostAddress==0 || forceHostAddress[0]!=0); - if (_stricmp(forceHostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0) - { - getaddrinfo(0, "0", &hints, &servinfo); - } - else - { - getaddrinfo(forceHostAddress, "0", &hints, &servinfo); - } - - for (aip = servinfo; aip != NULL; aip = aip->ai_next) - { - // Open socket. The address type depends on what - // getaddrinfo() gave us. - fe->socket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); - if (fe->socket != INVALID_SOCKET) - { - int ret = bind__( fe->socket, aip->ai_addr, (int) aip->ai_addrlen ); - if (ret>=0) - { - break; - } - else - { - closesocket__(fe->socket); - fe->socket=INVALID_SOCKET; - } - } - } - - if (fe->socket==INVALID_SOCKET) - return UDPFORWARDER_BIND_FAILED; - - //printf("Made socket %i\n", fe->readSocket); - - // This doubles the max throughput rate - int sock_opt; - sock_opt=1024*256; - setsockopt__(fe->socket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); - - // Immediate hard close. Don't linger the readSocket, or recreating the readSocket quickly on Vista fails. - sock_opt=0; - setsockopt__(fe->socket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); -#endif // #if RAKNET_SUPPORT_IPV6!=1 - -// unsigned int oldSize = forwardList.Size(); - forwardList.InsertAtIndex(fe,insertionIndex,_FILE_AND_LINE_); - // RakAssert(forwardList.GetIndexOf(fe->srcAndDest)!=(unsigned int) -1); - *port = SocketLayer::GetLocalPort ( fe->socket ); - return UDPFORWARDER_SUCCESS; - } - - return UDPFORWARDER_FORWARDING_ALREADY_EXISTS; -} -UDPForwarderResult UDPForwarder::StartForwarding(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, - unsigned short *forwardingPort, SOCKET *forwardingSocket) -{ - // Invalid parameters? - if (timeoutOnNoDataMS == 0 || timeoutOnNoDataMS > UDP_FORWARDER_MAXIMUM_TIMEOUT || source==UNASSIGNED_SYSTEM_ADDRESS || destination==UNASSIGNED_SYSTEM_ADDRESS) - return UDPFORWARDER_INVALID_PARAMETERS; - -#ifdef UDP_FORWARDER_EXECUTE_THREADED - ThreadOperation threadOperation; - threadOperation.source=source; - threadOperation.destination=destination; - threadOperation.timeoutOnNoDataMS=timeoutOnNoDataMS; - threadOperation.forceHostAddress=forceHostAddress; - threadOperation.socketFamily=socketFamily; - threadOperation.operation=ThreadOperation::TO_START_FORWARDING; - threadOperationIncomingMutex.Lock(); - threadOperationIncomingQueue.Push(threadOperation, _FILE_AND_LINE_ ); - threadOperationIncomingMutex.Unlock(); - - while (1) - { - RakSleep(0); - threadOperationOutgoingMutex.Lock(); - if (threadOperationOutgoingQueue.Size()!=0) - { - threadOperation=threadOperationOutgoingQueue.Pop(); - threadOperationOutgoingMutex.Unlock(); - if (forwardingPort) - *forwardingPort=threadOperation.forwardingPort; - if (forwardingSocket) - *forwardingSocket=threadOperation.forwardingSocket; - return threadOperation.result; - } - threadOperationOutgoingMutex.Unlock(); - - } -#else - return StartForwardingThreaded(source, destination, timeoutOnNoDataMS, forceHostAddress, socketFamily, srcToDestPort, destToSourcePort, srcToDestSocket, destToSourceSocket); -#endif - -} -UDPForwarderResult UDPForwarder::StartForwardingThreaded(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, - unsigned short *forwardingPort, SOCKET *forwardingSocket) -{ - SrcAndDest srcAndDest(source, destination); - - UDPForwarderResult result = AddForwardingEntry(srcAndDest, timeoutOnNoDataMS, forwardingPort, forceHostAddress, socketFamily); - - if (result!=UDPFORWARDER_SUCCESS) - return result; - - if (*forwardingSocket) - { - unsigned int idx; - bool objectExists; - idx = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - RakAssert(objectExists); - *forwardingSocket=forwardList[idx]->socket; - } - - return UDPFORWARDER_SUCCESS; -} -void UDPForwarder::StopForwarding(SystemAddress source, SystemAddress destination) -{ -#ifdef UDP_FORWARDER_EXECUTE_THREADED - ThreadOperation threadOperation; - threadOperation.source=source; - threadOperation.destination=destination; - threadOperation.operation=ThreadOperation::TO_STOP_FORWARDING; - threadOperationIncomingMutex.Lock(); - threadOperationIncomingQueue.Push(threadOperation, _FILE_AND_LINE_ ); - threadOperationIncomingMutex.Unlock(); -#else - StopForwardingThreaded(source, destination); -#endif -} -void UDPForwarder::StopForwardingThreaded(SystemAddress source, SystemAddress destination) -{ - SrcAndDest srcAndDest(destination,source); - - bool objectExists; - unsigned int idx = forwardList.GetIndexFromKey(srcAndDest, &objectExists); - if (objectExists) - { - RakNet::OP_DELETE(forwardList[idx],_FILE_AND_LINE_); - forwardList.RemoveAtIndex(idx); - } -} -namespace RakNet { -#ifdef UDP_FORWARDER_EXECUTE_THREADED -RAK_THREAD_DECLARATION(UpdateUDPForwarder) -{ - - - - UDPForwarder * udpForwarder = ( UDPForwarder * ) arguments; - - - udpForwarder->threadRunning=true; - UDPForwarder::ThreadOperation threadOperation; - while (udpForwarder->isRunning) - { - udpForwarder->threadOperationIncomingMutex.Lock(); - while (udpForwarder->threadOperationIncomingQueue.Size()) - { - threadOperation=udpForwarder->threadOperationIncomingQueue.Pop(); - udpForwarder->threadOperationIncomingMutex.Unlock(); - if (threadOperation.operation==UDPForwarder::ThreadOperation::TO_START_FORWARDING) - { - threadOperation.result=udpForwarder->StartForwardingThreaded(threadOperation.source, threadOperation.destination, threadOperation.timeoutOnNoDataMS, - threadOperation.forceHostAddress, threadOperation.socketFamily, &threadOperation.forwardingPort, &threadOperation.forwardingSocket); - udpForwarder->threadOperationOutgoingMutex.Lock(); - udpForwarder->threadOperationOutgoingQueue.Push(threadOperation, _FILE_AND_LINE_ ); - udpForwarder->threadOperationOutgoingMutex.Unlock(); - } - else - { - udpForwarder->StopForwardingThreaded(threadOperation.source, threadOperation.destination); - } - - - udpForwarder->threadOperationIncomingMutex.Lock(); - } - udpForwarder->threadOperationIncomingMutex.Unlock(); - -#if RAKNET_SUPPORT_IPV6!=1 - udpForwarder->UpdateThreaded_Old(); -#else - udpForwarder->UpdateThreaded(); -#endif - - - // 12/1/2010 Do not change from 0 - // See http://www.jenkinssoftware.com/forum/index.php?topic=4033.0;topicseen - // Avoid 100% reported CPU usage - RakSleep(0); - } - udpForwarder->threadRunning=false; - - - - - return 0; - - -} -} // namespace RakNet -#endif - -#endif // #if _RAKNET_SUPPORT_FileOperations==1 +#include "UDPForwarder.h" + +#if _RAKNET_SUPPORT_UDPForwarder==1 + +#include "GetTime.h" +#include "MTUSize.h" +#include "SocketLayer.h" +#include "WSAStartupSingleton.h" +#include "RakSleep.h" +#include "DS_OrderedList.h" +#include "LinuxStrings.h" +#include "SocketDefines.h" +#include "VitaIncludes.h" + +using namespace RakNet; +static const unsigned short DEFAULT_MAX_FORWARD_ENTRIES=64; + +#ifdef UDP_FORWARDER_EXECUTE_THREADED +namespace RakNet +{ + RAK_THREAD_DECLARATION(UpdateUDPForwarder); +} +#endif + +int UDPForwarder::SrcAndDestForwardEntryComp( const UDPForwarder::SrcAndDest &inputKey, UDPForwarder::ForwardEntry * const &cls ) +{ + if (inputKey.source < cls->srcAndDest.source) + return -1; + + if (inputKey.source > cls->srcAndDest.source) + return 1; + + if (inputKey.dest < cls->srcAndDest.dest) + return -1; + + if (inputKey.dest > cls->srcAndDest.dest) + return 1; + + return 0; +} +/* +bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) +{ + return inputKey.Get().source < cls->srcAndDest.source || + (inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest < cls->srcAndDest.dest); +} +bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) +{ + return inputKey.Get().source > cls->srcAndDest.source || + (inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest > cls->srcAndDest.dest); +} +bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPForwarder::ForwardEntry *cls ) +{ + return inputKey.Get().source == cls->srcAndDest.source && inputKey.Get().dest == cls->srcAndDest.dest; +} +*/ + + +UDPForwarder::ForwardEntry::ForwardEntry() {socket=INVALID_SOCKET; timeLastDatagramForwarded=RakNet::GetTimeMS(); updatedSourcePort=false; updatedDestPort=false;} +UDPForwarder::ForwardEntry::~ForwardEntry() { + if (socket!=INVALID_SOCKET) + closesocket__(socket); +} + +UDPForwarder::UDPForwarder() +{ +#ifdef _WIN32 + WSAStartupSingleton::AddRef(); +#endif + + maxForwardEntries=DEFAULT_MAX_FORWARD_ENTRIES; + isRunning=false; + threadRunning=false; + + + + +} +UDPForwarder::~UDPForwarder() +{ + Shutdown(); + +#ifdef _WIN32 + WSAStartupSingleton::Deref(); +#endif +} +void UDPForwarder::Startup(void) +{ + if (isRunning==true) + return; + + isRunning=true; + threadRunning=false; + +#ifdef UDP_FORWARDER_EXECUTE_THREADED + + + + + + int errorCode; + + + + + errorCode = RakNet::RakThread::Create(UpdateUDPForwarder, this); + + if ( errorCode != 0 ) + { + RakAssert(0); + return; + } + + while (threadRunning==false) + RakSleep(30); +#endif +} +void UDPForwarder::Shutdown(void) +{ + if (isRunning==false) + return; + + isRunning=false; + +#ifdef UDP_FORWARDER_EXECUTE_THREADED + while (threadRunning==true) + RakSleep(30); +#endif + + unsigned int j; + for (j=0; j < forwardList.Size(); j++) + RakNet::OP_DELETE(forwardList[j],_FILE_AND_LINE_); + forwardList.Clear(false, _FILE_AND_LINE_); + + + + + + +} +void UDPForwarder::Update(void) +{ +#ifndef UDP_FORWARDER_EXECUTE_THREADED + #if RAKNET_SUPPORT_IPV6!=1 + UpdateThreaded_Old(); + #else + UpdateThreaded(); + #endif +#endif +} +void UDPForwarder::UpdateThreaded_Old(void) +{ + fd_set readFD; + //fd_set exceptionFD; + FD_ZERO(&readFD); + // FD_ZERO(&exceptionFD); + int selectResult; + + + timeval tv; + tv.tv_sec=0; + tv.tv_usec=0; + + + RakNet::TimeMS curTime = RakNet::GetTimeMS(); + + SOCKET largestDescriptor=0; + unsigned int i; + + // Remove unused entries + i=0; + while (i < forwardList.Size()) + { + if (curTime > forwardList[i]->timeLastDatagramForwarded && // Account for timestamp wrap + curTime > forwardList[i]->timeLastDatagramForwarded+forwardList[i]->timeoutOnNoDataMS) + { + RakNet::OP_DELETE(forwardList[i],_FILE_AND_LINE_); + forwardList.RemoveAtIndex(i); + } + else + i++; + } + + if (forwardList.Size()==0) + return; + + for (i=0; i < forwardList.Size(); i++) + { +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + FD_SET(forwardList[i]->socket, &readFD); + // FD_SET(forwardList[i]->readSocket, &exceptionFD); + + if (forwardList[i]->socket > largestDescriptor) + largestDescriptor = forwardList[i]->socket; + } + + + selectResult=(int) select__((int) largestDescriptor+1, &readFD, 0, 0, &tv); + + + + + char data[ MAXIMUM_MTU_SIZE ]; + sockaddr_in sa; + socklen_t len2; + + if (selectResult > 0) + { + DataStructures::Queue entriesToRead; + ForwardEntry *forwardEntry; + + for (i=0; i < forwardList.Size(); i++) + { + forwardEntry = forwardList[i]; + // I do this because I'm updating the forwardList, and don't want to lose FD_ISSET as the list is no longer in order + if (FD_ISSET(forwardEntry->socket, &readFD)) + entriesToRead.Push(forwardEntry,_FILE_AND_LINE_); + } + + while (entriesToRead.IsEmpty()==false) + { + forwardEntry=entriesToRead.Pop(); + + const int flag=0; + int receivedDataLen, len=0; + unsigned short portnum=0; + len2 = sizeof( sa ); + sa.sin_family = AF_INET; + receivedDataLen = recvfrom__( forwardEntry->socket, data, MAXIMUM_MTU_SIZE, flag, ( sockaddr* ) & sa, ( socklen_t* ) & len2 ); + + if (receivedDataLen<0) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = WSAGetLastError(); + + if (dwIOError!=WSAECONNRESET && dwIOError!=WSAEINTR && dwIOError!=WSAETIMEDOUT) + { + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "recvfrom failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); + } +#endif + continue; + } + + portnum = ntohs( sa.sin_port ); + if (forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->updatedSourcePort==false && forwardEntry->srcAndDest.dest.GetPort()!=portnum) + { + forwardEntry->updatedSourcePort=true; + + if (forwardEntry->srcAndDest.source.GetPort()!=portnum) + { + unsigned int index; + SrcAndDest srcAndDest(forwardEntry->srcAndDest.dest, forwardEntry->srcAndDest.source); + bool objectExists; + index = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + if (objectExists) + { + forwardList.RemoveAtIndex(index); + } + forwardEntry->srcAndDest.source.SetPort(portnum); + forwardList.Insert(forwardEntry->srcAndDest,forwardEntry,true,_FILE_AND_LINE_); + } + } + + if (forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->srcAndDest.source.GetPort()==portnum) + { + // Forward to dest + len=0; + sockaddr_in saOut; + saOut.sin_port = forwardEntry->srcAndDest.dest.GetPortNetworkOrder(); // User port + saOut.sin_addr.s_addr = forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr; + saOut.sin_family = AF_INET; + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & saOut, sizeof( saOut ) ); + } + while ( len == 0 ); + + // printf("1. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); + + forwardEntry->timeLastDatagramForwarded=curTime; + } + + if (forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->updatedDestPort==false && forwardEntry->srcAndDest.source.GetPort()!=portnum) + { + forwardEntry->updatedDestPort=true; + + if (forwardEntry->srcAndDest.dest.GetPort()!=portnum) + { + unsigned int index; + SrcAndDest srcAndDest(forwardEntry->srcAndDest.source, forwardEntry->srcAndDest.dest); + bool objectExists; + index = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + if (objectExists) + { + forwardList.RemoveAtIndex(index); + } + forwardEntry->srcAndDest.dest.SetPort(portnum); + forwardList.Insert(forwardEntry->srcAndDest,forwardEntry,true,_FILE_AND_LINE_); + } + } + + if (forwardEntry->srcAndDest.dest.address.addr4.sin_addr.s_addr==sa.sin_addr.s_addr && forwardEntry->srcAndDest.dest.GetPort()==portnum) + { + // Forward to source + len=0; + sockaddr_in saOut; + saOut.sin_port = forwardEntry->srcAndDest.source.GetPortNetworkOrder(); // User port + saOut.sin_addr.s_addr = forwardEntry->srcAndDest.source.address.addr4.sin_addr.s_addr; + saOut.sin_family = AF_INET; + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & saOut, sizeof( saOut ) ); + } + while ( len == 0 ); + + // printf("2. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); + + forwardEntry->timeLastDatagramForwarded=curTime; + } + } + } +} +#if RAKNET_SUPPORT_IPV6==1 +void UDPForwarder::UpdateThreaded(void) +{ + fd_set readFD; + //fd_set exceptionFD; + FD_ZERO(&readFD); +// FD_ZERO(&exceptionFD); + timeval tv; + int selectResult; + tv.tv_sec=0; + tv.tv_usec=0; + + RakNet::TimeMS curTime = RakNet::GetTimeMS(); + + SOCKET largestDescriptor=0; + unsigned int i; + + // Remove unused entries + i=0; + while (i < forwardList.Size()) + { + if (curTime > forwardList[i]->timeLastDatagramForwarded && // Account for timestamp wrap + curTime > forwardList[i]->timeLastDatagramForwarded+forwardList[i]->timeoutOnNoDataMS) + { + RakNet::OP_DELETE(forwardList[i],_FILE_AND_LINE_); + forwardList.RemoveAtIndex(i); + } + else + i++; + } + + if (forwardList.Size()==0) + return; + + for (i=0; i < forwardList.Size(); i++) + { +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + FD_SET(forwardList[i]->socket, &readFD); +// FD_SET(forwardList[i]->readSocket, &exceptionFD); + + if (forwardList[i]->socket > largestDescriptor) + largestDescriptor = forwardList[i]->socket; + } + + selectResult=(int) select__((int) largestDescriptor+1, &readFD, 0, 0, &tv); + + char data[ MAXIMUM_MTU_SIZE ]; + sockaddr_storage their_addr; + sockaddr* sockAddrPtr; + socklen_t sockLen; + socklen_t* socketlenPtr=(socklen_t*) &sockLen; + sockaddr_in *sockAddrIn; + sockaddr_in6 *sockAddrIn6; + SystemAddress receivedAddr; + sockLen=sizeof(their_addr); + sockAddrPtr=(sockaddr*) &their_addr; + + if (selectResult > 0) + { + DataStructures::Queue entriesToRead; + ForwardEntry *forwardEntry; + + for (i=0; i < forwardList.Size(); i++) + { + forwardEntry = forwardList[i]; + // I do this because I'm updating the forwardList, and don't want to lose FD_ISSET as the list is no longer in order + if (FD_ISSET(forwardEntry->socket, &readFD)) + entriesToRead.Push(forwardEntry,_FILE_AND_LINE_); + } + + while (entriesToRead.IsEmpty()==false) + { + forwardEntry=entriesToRead.Pop(); + + const int flag=0; + int receivedDataLen, len=0; + receivedDataLen = recvfrom__( forwardEntry->socket, data, MAXIMUM_MTU_SIZE, flag, sockAddrPtr, socketlenPtr ); + + if (receivedDataLen<0) + { +#if defined(_WIN32) && defined(_DEBUG) + DWORD dwIOError = WSAGetLastError(); + + if (dwIOError!=WSAECONNRESET && dwIOError!=WSAEINTR && dwIOError!=WSAETIMEDOUT) + { + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "recvfrom failed:Error code - %d\n%s", dwIOError, messageBuffer ); + + //Free the buffer. + LocalFree( messageBuffer ); + } +#endif + continue; + } + + if (their_addr.ss_family==AF_INET) + { + sockAddrIn=(sockaddr_in *)&their_addr; + sockAddrIn6=0; + memcpy(&receivedAddr.address.addr4,sockAddrIn,sizeof(sockaddr_in)); + // receivedAddr.address.addr4.sin_port=ntohs( sockAddrIn->sin_port ); + } + else + { + sockAddrIn=0; + sockAddrIn6=(sockaddr_in6 *)&their_addr; + memcpy(&receivedAddr.address.addr6,sockAddrIn6,sizeof(sockaddr_in6)); + // receivedAddr.address.addr6.sin6_port=ntohs( sockAddrIn6->sin6_port ); + } + + if (forwardEntry->srcAndDest.source.EqualsExcludingPort(receivedAddr) && forwardEntry->updatedSourcePort==false && forwardEntry->srcAndDest.dest.GetPort()!=receivedAddr.GetPort()) + { + forwardEntry->updatedSourcePort=true; + + if (forwardEntry->srcAndDest.source.GetPort()!=receivedAddr.GetPort()) + { + unsigned int index; + SrcAndDest srcAndDest(forwardEntry->srcAndDest.dest, forwardEntry->srcAndDest.source); + bool objectExists; + index=forwardList.GetIndexFromKey(srcAndDest, &objectExists); + forwardList.RemoveAtIndex(index); + forwardEntry->srcAndDest.source.SetPort(receivedAddr.GetPort()); + forwardList.Push(forwardEntry,forwardEntry->srcAndDest,_FILE_AND_LINE_); + } + } + + if (forwardEntry->srcAndDest.source.EqualsExcludingPort(receivedAddr) && forwardEntry->srcAndDest.source.GetPort()==receivedAddr.GetPort()) + { + // Forward to dest + len=0; + + if (forwardEntry->srcAndDest.dest.address.addr4.sin_family==AF_INET) + { + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.dest.address.addr4, sizeof( sockaddr_in ) ); + } + while ( len == 0 ); + } + else + { + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.dest.address.addr6, sizeof( sockaddr_in ) ); + } + while ( len == 0 ); + } + + + // printf("1. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); + + forwardEntry->timeLastDatagramForwarded=curTime; + } + + if (forwardEntry->srcAndDest.dest.EqualsExcludingPort(receivedAddr) && forwardEntry->updatedDestPort==false && forwardEntry->srcAndDest.source.GetPort()!=receivedAddr.GetPort()) + { + forwardEntry->updatedDestPort=true; + + if (forwardEntry->srcAndDest.dest.GetPort()!=receivedAddr.GetPort()) + { + unsigned int index; + SrcAndDest srcAndDest(forwardEntry->srcAndDest.source, forwardEntry->srcAndDest.dest); + index=forwardList.GetIndexOf(srcAndDest); + forwardList.RemoveAtIndex(index); + forwardEntry->srcAndDest.dest.SetPort(receivedAddr.GetPort()); + forwardList.Push(forwardEntry,forwardEntry->srcAndDest,_FILE_AND_LINE_); + } + } + + if (forwardEntry->srcAndDest.dest.EqualsExcludingPort(receivedAddr) && forwardEntry->srcAndDest.dest.GetPort()==receivedAddr.GetPort()) + { + // Forward to source + len=0; + if (forwardEntry->srcAndDest.source.address.addr4.sin_family==AF_INET) + { + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.source.address.addr4, sizeof( sockaddr_in ) ); + } + while ( len == 0 ); + } + else + { + do + { + len = sendto__( forwardEntry->socket, data, receivedDataLen, 0, ( const sockaddr* ) & forwardEntry->srcAndDest.source.address.addr6, sizeof( sockaddr_in ) ); + } + while ( len == 0 ); + } + + // printf("2. Forwarding after %i ms\n", curTime-forwardEntry->timeLastDatagramForwarded); + + forwardEntry->timeLastDatagramForwarded=curTime; + } + } + } +} +#endif // #if RAKNET_SUPPORT_IPV6!=1 +void UDPForwarder::SetMaxForwardEntries(unsigned short maxEntries) +{ + RakAssert(maxEntries>0 && maxEntries<65535/2); + maxForwardEntries=maxEntries; +} +int UDPForwarder::GetMaxForwardEntries(void) const +{ + return maxForwardEntries; +} +int UDPForwarder::GetUsedForwardEntries(void) const +{ + return (int) forwardList.Size(); +} +UDPForwarderResult UDPForwarder::AddForwardingEntry(SrcAndDest srcAndDest, RakNet::TimeMS timeoutOnNoDataMS, unsigned short *port, const char *forceHostAddress, short socketFamily) +{ + (void) socketFamily; + + unsigned int insertionIndex; + bool objectExists; + insertionIndex = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + if (objectExists==false) + { +#if RAKNET_SUPPORT_IPV6!=1 + int sock_opt; + sockaddr_in listenerSocketAddress; + listenerSocketAddress.sin_port = 0; + ForwardEntry *fe = RakNet::OP_NEW(_FILE_AND_LINE_); + fe->srcAndDest=srcAndDest; + fe->timeoutOnNoDataMS=timeoutOnNoDataMS; + fe->socket = socket__( AF_INET, SOCK_DGRAM, 0 ); + + //printf("Made socket %i\n", fe->readSocket); + + // This doubles the max throughput rate + sock_opt=1024*256; + setsockopt__(fe->socket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + // Immediate hard close. Don't linger the readSocket, or recreating the readSocket quickly on Vista fails. + sock_opt=0; + setsockopt__(fe->socket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + listenerSocketAddress.sin_family = AF_INET; + + if (forceHostAddress && forceHostAddress[0]) + { + + + + listenerSocketAddress.sin_addr.s_addr = inet_addr__( forceHostAddress ); + + } + else + { + listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; + } + + int ret = bind__( fe->socket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( listenerSocketAddress ) ); + if (ret==-1) + { + RakNet::OP_DELETE(fe,_FILE_AND_LINE_); + return UDPFORWARDER_BIND_FAILED; + } +#else + ForwardEntry *fe = RakNet::OP_NEW(_FILE_AND_LINE_); + fe->srcAndDest=srcAndDest; + fe->timeoutOnNoDataMS=timeoutOnNoDataMS; + fe->socket=INVALID_SOCKET; + + struct addrinfo hints; + memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty + hints.ai_family = socketFamily; + hints.ai_socktype = SOCK_DGRAM; // UDP sockets + hints.ai_flags = AI_PASSIVE; // fill in my IP for me + struct addrinfo *servinfo=0, *aip; // will point to the results + + + RakAssert(forceHostAddress==0 || forceHostAddress[0]!=0); + if (_stricmp(forceHostAddress,"UNASSIGNED_SYSTEM_ADDRESS")==0) + { + getaddrinfo(0, "0", &hints, &servinfo); + } + else + { + getaddrinfo(forceHostAddress, "0", &hints, &servinfo); + } + + for (aip = servinfo; aip != NULL; aip = aip->ai_next) + { + // Open socket. The address type depends on what + // getaddrinfo() gave us. + fe->socket = socket__(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (fe->socket != INVALID_SOCKET) + { + int ret = bind__( fe->socket, aip->ai_addr, (int) aip->ai_addrlen ); + if (ret>=0) + { + break; + } + else + { + closesocket__(fe->socket); + fe->socket=INVALID_SOCKET; + } + } + } + + if (fe->socket==INVALID_SOCKET) + return UDPFORWARDER_BIND_FAILED; + + //printf("Made socket %i\n", fe->readSocket); + + // This doubles the max throughput rate + int sock_opt; + sock_opt=1024*256; + setsockopt__(fe->socket, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); + + // Immediate hard close. Don't linger the readSocket, or recreating the readSocket quickly on Vista fails. + sock_opt=0; + setsockopt__(fe->socket, SOL_SOCKET, SO_LINGER, ( char * ) & sock_opt, sizeof ( sock_opt ) ); +#endif // #if RAKNET_SUPPORT_IPV6!=1 + +// unsigned int oldSize = forwardList.Size(); + forwardList.InsertAtIndex(fe,insertionIndex,_FILE_AND_LINE_); + // RakAssert(forwardList.GetIndexOf(fe->srcAndDest)!=(unsigned int) -1); + *port = SocketLayer::GetLocalPort ( fe->socket ); + return UDPFORWARDER_SUCCESS; + } + + return UDPFORWARDER_FORWARDING_ALREADY_EXISTS; +} +UDPForwarderResult UDPForwarder::StartForwarding(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, + unsigned short *forwardingPort, SOCKET *forwardingSocket) +{ + // Invalid parameters? + if (timeoutOnNoDataMS == 0 || timeoutOnNoDataMS > UDP_FORWARDER_MAXIMUM_TIMEOUT || source==UNASSIGNED_SYSTEM_ADDRESS || destination==UNASSIGNED_SYSTEM_ADDRESS) + return UDPFORWARDER_INVALID_PARAMETERS; + +#ifdef UDP_FORWARDER_EXECUTE_THREADED + ThreadOperation threadOperation; + threadOperation.source=source; + threadOperation.destination=destination; + threadOperation.timeoutOnNoDataMS=timeoutOnNoDataMS; + threadOperation.forceHostAddress=forceHostAddress; + threadOperation.socketFamily=socketFamily; + threadOperation.operation=ThreadOperation::TO_START_FORWARDING; + threadOperationIncomingMutex.Lock(); + threadOperationIncomingQueue.Push(threadOperation, _FILE_AND_LINE_ ); + threadOperationIncomingMutex.Unlock(); + + while (1) + { + RakSleep(0); + threadOperationOutgoingMutex.Lock(); + if (threadOperationOutgoingQueue.Size()!=0) + { + threadOperation=threadOperationOutgoingQueue.Pop(); + threadOperationOutgoingMutex.Unlock(); + if (forwardingPort) + *forwardingPort=threadOperation.forwardingPort; + if (forwardingSocket) + *forwardingSocket=threadOperation.forwardingSocket; + return threadOperation.result; + } + threadOperationOutgoingMutex.Unlock(); + + } +#else + return StartForwardingThreaded(source, destination, timeoutOnNoDataMS, forceHostAddress, socketFamily, srcToDestPort, destToSourcePort, srcToDestSocket, destToSourceSocket); +#endif + +} +UDPForwarderResult UDPForwarder::StartForwardingThreaded(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, + unsigned short *forwardingPort, SOCKET *forwardingSocket) +{ + SrcAndDest srcAndDest(source, destination); + + UDPForwarderResult result = AddForwardingEntry(srcAndDest, timeoutOnNoDataMS, forwardingPort, forceHostAddress, socketFamily); + + if (result!=UDPFORWARDER_SUCCESS) + return result; + + if (*forwardingSocket) + { + unsigned int idx; + bool objectExists; + idx = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + RakAssert(objectExists); + *forwardingSocket=forwardList[idx]->socket; + } + + return UDPFORWARDER_SUCCESS; +} +void UDPForwarder::StopForwarding(SystemAddress source, SystemAddress destination) +{ +#ifdef UDP_FORWARDER_EXECUTE_THREADED + ThreadOperation threadOperation; + threadOperation.source=source; + threadOperation.destination=destination; + threadOperation.operation=ThreadOperation::TO_STOP_FORWARDING; + threadOperationIncomingMutex.Lock(); + threadOperationIncomingQueue.Push(threadOperation, _FILE_AND_LINE_ ); + threadOperationIncomingMutex.Unlock(); +#else + StopForwardingThreaded(source, destination); +#endif +} +void UDPForwarder::StopForwardingThreaded(SystemAddress source, SystemAddress destination) +{ + SrcAndDest srcAndDest(destination,source); + + bool objectExists; + unsigned int idx = forwardList.GetIndexFromKey(srcAndDest, &objectExists); + if (objectExists) + { + RakNet::OP_DELETE(forwardList[idx],_FILE_AND_LINE_); + forwardList.RemoveAtIndex(idx); + } +} +namespace RakNet { +#ifdef UDP_FORWARDER_EXECUTE_THREADED +RAK_THREAD_DECLARATION(UpdateUDPForwarder) +{ + + + + UDPForwarder * udpForwarder = ( UDPForwarder * ) arguments; + + + udpForwarder->threadRunning=true; + UDPForwarder::ThreadOperation threadOperation; + while (udpForwarder->isRunning) + { + udpForwarder->threadOperationIncomingMutex.Lock(); + while (udpForwarder->threadOperationIncomingQueue.Size()) + { + threadOperation=udpForwarder->threadOperationIncomingQueue.Pop(); + udpForwarder->threadOperationIncomingMutex.Unlock(); + if (threadOperation.operation==UDPForwarder::ThreadOperation::TO_START_FORWARDING) + { + threadOperation.result=udpForwarder->StartForwardingThreaded(threadOperation.source, threadOperation.destination, threadOperation.timeoutOnNoDataMS, + threadOperation.forceHostAddress, threadOperation.socketFamily, &threadOperation.forwardingPort, &threadOperation.forwardingSocket); + udpForwarder->threadOperationOutgoingMutex.Lock(); + udpForwarder->threadOperationOutgoingQueue.Push(threadOperation, _FILE_AND_LINE_ ); + udpForwarder->threadOperationOutgoingMutex.Unlock(); + } + else + { + udpForwarder->StopForwardingThreaded(threadOperation.source, threadOperation.destination); + } + + + udpForwarder->threadOperationIncomingMutex.Lock(); + } + udpForwarder->threadOperationIncomingMutex.Unlock(); + +#if RAKNET_SUPPORT_IPV6!=1 + udpForwarder->UpdateThreaded_Old(); +#else + udpForwarder->UpdateThreaded(); +#endif + + + // 12/1/2010 Do not change from 0 + // See http://www.jenkinssoftware.com/forum/index.php?topic=4033.0;topicseen + // Avoid 100% reported CPU usage + RakSleep(0); + } + udpForwarder->threadRunning=false; + + + + + return 0; + + +} +} // namespace RakNet +#endif + +#endif // #if _RAKNET_SUPPORT_FileOperations==1 diff --git a/src/raknet/UDPForwarder.h b/src/raknet/UDPForwarder.h index 372352e..2b22373 100755 --- a/src/raknet/UDPForwarder.h +++ b/src/raknet/UDPForwarder.h @@ -1,177 +1,177 @@ -/// \file -/// \brief Forwards UDP datagrams. Independent of RakNet's protocol. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. - - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPForwarder==1 - -#ifndef __UDP_FORWARDER_H -#define __UDP_FORWARDER_H - -#include "Export.h" -#include "RakNetTypes.h" -#include "SocketIncludes.h" -#include "UDPProxyCommon.h" -#include "SimpleMutex.h" -#include "RakString.h" -#include "RakThread.h" -#include "DS_Queue.h" -#include "DS_OrderedList.h" - -#define UDP_FORWARDER_EXECUTE_THREADED - -namespace RakNet -{ - -enum UDPForwarderResult -{ - UDPFORWARDER_FORWARDING_ALREADY_EXISTS, - UDPFORWARDER_NO_SOCKETS, - UDPFORWARDER_BIND_FAILED, - UDPFORWARDER_INVALID_PARAMETERS, - UDPFORWARDER_SUCCESS, - -}; - -/// \brief Forwards UDP datagrams. Independent of RakNet's protocol. -/// \ingroup NAT_PUNCHTHROUGH_GROUP -class RAK_DLL_EXPORT UDPForwarder -{ -public: - UDPForwarder(); - ~UDPForwarder(); - - /// Starts the system. - /// Required to call before StartForwarding - void Startup(void); - - /// Stops the system, and frees all sockets - void Shutdown(void); - - /// Call on a regular basis, unless using UDP_FORWARDER_EXECUTE_THREADED. - /// Will call select__() on all sockets and forward messages. - void Update(void); - - /// Sets the maximum number of forwarding entries allowed - /// Set according to your available bandwidth and the estimated average bandwidth per forwarded address. - /// A single connection requires 2 entries, as connections are bi-directional. - /// \param[in] maxEntries The maximum number of simultaneous forwarding entries. Defaults to 64 (32 connections) - void SetMaxForwardEntries(unsigned short maxEntries); - - /// \return The \a maxEntries parameter passed to SetMaxForwardEntries(), or the default if it was never called - int GetMaxForwardEntries(void) const; - - /// \note Each call to StartForwarding uses up two forwarding entries, since communications are bidirectional - /// \return How many entries have been used - int GetUsedForwardEntries(void) const; - - /// Forwards datagrams from source to destination, and vice-versa - /// Does nothing if this forward entry already exists via a previous call - /// \pre Call Startup() - /// \note RakNet's protocol will ensure a message is sent at least every 15 seconds, so if routing RakNet messages, it is a reasonable value for timeoutOnNoDataMS, plus an some extra seconds for latency - /// \param[in] source The source IP and port - /// \param[in] destination Where to forward to (and vice-versa) - /// \param[in] timeoutOnNoDataMS If no messages are forwarded for this many MS, then automatically remove this entry. Currently hardcoded to UDP_FORWARDER_MAXIMUM_TIMEOUT (else the call fails) - /// \param[in] forceHostAddress Force binding on a particular address. 0 to use any. - /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - short socketFamily; - /// \param[out] forwardingPort New opened port for forwarding - /// \param[out] forwardingSocket New opened socket for forwarding - /// \return UDPForwarderResult - UDPForwarderResult StartForwarding(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, - unsigned short *forwardingPort, SOCKET *forwardingSocket); - - /// No longer forward datagrams from source to destination - /// \param[in] source The source IP and port - /// \param[in] destination Where to forward to - void StopForwarding(SystemAddress source, SystemAddress destination); - - struct SrcAndDest - { - SrcAndDest() {} - SrcAndDest(SystemAddress sa1, SystemAddress sa2) - { - if (sa1 < sa2) - { - source=sa1; - dest=sa2; - } - else - { - source=sa2; - dest=sa1; - } - } - SystemAddress source; - SystemAddress dest; - }; - - struct ForwardEntry - { - ForwardEntry(); - ~ForwardEntry(); - SrcAndDest srcAndDest; - RakNet::TimeMS timeLastDatagramForwarded; - SOCKET socket; - RakNet::TimeMS timeoutOnNoDataMS; - bool updatedSourcePort, updatedDestPort; - short socketFamily; - }; - - -protected: - - static int SrcAndDestForwardEntryComp( const SrcAndDest &inputKey, ForwardEntry * const &cls ); - - - friend RAK_THREAD_DECLARATION(UpdateUDPForwarder); - struct ThreadOperation - { - enum { - TO_NONE, - TO_START_FORWARDING, - TO_STOP_FORWARDING, - } operation; - - SystemAddress source; - SystemAddress destination; - RakNet::TimeMS timeoutOnNoDataMS; - RakNet::RakString forceHostAddress; - unsigned short forwardingPort; - SOCKET forwardingSocket; - UDPForwarderResult result; - unsigned short socketFamily; - }; - SimpleMutex threadOperationIncomingMutex,threadOperationOutgoingMutex; - DataStructures::Queue threadOperationIncomingQueue; - DataStructures::Queue threadOperationOutgoingQueue; - -#if RAKNET_SUPPORT_IPV6==1 - void UpdateThreaded(void); -#endif - void UpdateThreaded_Old(void); - UDPForwarderResult StartForwardingThreaded(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, - unsigned short *forwardingPort, SOCKET *forwardingSocket); - void StopForwardingThreaded(SystemAddress source, SystemAddress destination); - - DataStructures::OrderedList forwardList; - unsigned short maxForwardEntries; - - UDPForwarderResult AddForwardingEntry(SrcAndDest srcAndDest, RakNet::TimeMS timeoutOnNoDataMS, unsigned short *port, const char *forceHostAddress, short socketFamily); - - - bool isRunning, threadRunning; - - - -}; - -} // End namespace - -#endif - -#endif // #if _RAKNET_SUPPORT_UDPForwarder==1 +/// \file +/// \brief Forwards UDP datagrams. Independent of RakNet's protocol. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. + + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPForwarder==1 + +#ifndef __UDP_FORWARDER_H +#define __UDP_FORWARDER_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "SocketIncludes.h" +#include "UDPProxyCommon.h" +#include "SimpleMutex.h" +#include "RakString.h" +#include "RakThread.h" +#include "DS_Queue.h" +#include "DS_OrderedList.h" + +#define UDP_FORWARDER_EXECUTE_THREADED + +namespace RakNet +{ + +enum UDPForwarderResult +{ + UDPFORWARDER_FORWARDING_ALREADY_EXISTS, + UDPFORWARDER_NO_SOCKETS, + UDPFORWARDER_BIND_FAILED, + UDPFORWARDER_INVALID_PARAMETERS, + UDPFORWARDER_SUCCESS, + +}; + +/// \brief Forwards UDP datagrams. Independent of RakNet's protocol. +/// \ingroup NAT_PUNCHTHROUGH_GROUP +class RAK_DLL_EXPORT UDPForwarder +{ +public: + UDPForwarder(); + ~UDPForwarder(); + + /// Starts the system. + /// Required to call before StartForwarding + void Startup(void); + + /// Stops the system, and frees all sockets + void Shutdown(void); + + /// Call on a regular basis, unless using UDP_FORWARDER_EXECUTE_THREADED. + /// Will call select__() on all sockets and forward messages. + void Update(void); + + /// Sets the maximum number of forwarding entries allowed + /// Set according to your available bandwidth and the estimated average bandwidth per forwarded address. + /// A single connection requires 2 entries, as connections are bi-directional. + /// \param[in] maxEntries The maximum number of simultaneous forwarding entries. Defaults to 64 (32 connections) + void SetMaxForwardEntries(unsigned short maxEntries); + + /// \return The \a maxEntries parameter passed to SetMaxForwardEntries(), or the default if it was never called + int GetMaxForwardEntries(void) const; + + /// \note Each call to StartForwarding uses up two forwarding entries, since communications are bidirectional + /// \return How many entries have been used + int GetUsedForwardEntries(void) const; + + /// Forwards datagrams from source to destination, and vice-versa + /// Does nothing if this forward entry already exists via a previous call + /// \pre Call Startup() + /// \note RakNet's protocol will ensure a message is sent at least every 15 seconds, so if routing RakNet messages, it is a reasonable value for timeoutOnNoDataMS, plus an some extra seconds for latency + /// \param[in] source The source IP and port + /// \param[in] destination Where to forward to (and vice-versa) + /// \param[in] timeoutOnNoDataMS If no messages are forwarded for this many MS, then automatically remove this entry. Currently hardcoded to UDP_FORWARDER_MAXIMUM_TIMEOUT (else the call fails) + /// \param[in] forceHostAddress Force binding on a particular address. 0 to use any. + /// \param[in] socketFamily IP version: For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + short socketFamily; + /// \param[out] forwardingPort New opened port for forwarding + /// \param[out] forwardingSocket New opened socket for forwarding + /// \return UDPForwarderResult + UDPForwarderResult StartForwarding(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, + unsigned short *forwardingPort, SOCKET *forwardingSocket); + + /// No longer forward datagrams from source to destination + /// \param[in] source The source IP and port + /// \param[in] destination Where to forward to + void StopForwarding(SystemAddress source, SystemAddress destination); + + struct SrcAndDest + { + SrcAndDest() {} + SrcAndDest(SystemAddress sa1, SystemAddress sa2) + { + if (sa1 < sa2) + { + source=sa1; + dest=sa2; + } + else + { + source=sa2; + dest=sa1; + } + } + SystemAddress source; + SystemAddress dest; + }; + + struct ForwardEntry + { + ForwardEntry(); + ~ForwardEntry(); + SrcAndDest srcAndDest; + RakNet::TimeMS timeLastDatagramForwarded; + SOCKET socket; + RakNet::TimeMS timeoutOnNoDataMS; + bool updatedSourcePort, updatedDestPort; + short socketFamily; + }; + + +protected: + + static int SrcAndDestForwardEntryComp( const SrcAndDest &inputKey, ForwardEntry * const &cls ); + + + friend RAK_THREAD_DECLARATION(UpdateUDPForwarder); + struct ThreadOperation + { + enum { + TO_NONE, + TO_START_FORWARDING, + TO_STOP_FORWARDING, + } operation; + + SystemAddress source; + SystemAddress destination; + RakNet::TimeMS timeoutOnNoDataMS; + RakNet::RakString forceHostAddress; + unsigned short forwardingPort; + SOCKET forwardingSocket; + UDPForwarderResult result; + unsigned short socketFamily; + }; + SimpleMutex threadOperationIncomingMutex,threadOperationOutgoingMutex; + DataStructures::Queue threadOperationIncomingQueue; + DataStructures::Queue threadOperationOutgoingQueue; + +#if RAKNET_SUPPORT_IPV6==1 + void UpdateThreaded(void); +#endif + void UpdateThreaded_Old(void); + UDPForwarderResult StartForwardingThreaded(SystemAddress source, SystemAddress destination, RakNet::TimeMS timeoutOnNoDataMS, const char *forceHostAddress, unsigned short socketFamily, + unsigned short *forwardingPort, SOCKET *forwardingSocket); + void StopForwardingThreaded(SystemAddress source, SystemAddress destination); + + DataStructures::OrderedList forwardList; + unsigned short maxForwardEntries; + + UDPForwarderResult AddForwardingEntry(SrcAndDest srcAndDest, RakNet::TimeMS timeoutOnNoDataMS, unsigned short *port, const char *forceHostAddress, short socketFamily); + + + bool isRunning, threadRunning; + + + +}; + +} // End namespace + +#endif + +#endif // #if _RAKNET_SUPPORT_UDPForwarder==1 diff --git a/src/raknet/UDPProxyClient.cpp b/src/raknet/UDPProxyClient.cpp index 3ce9a2d..bbe5bb9 100755 --- a/src/raknet/UDPProxyClient.cpp +++ b/src/raknet/UDPProxyClient.cpp @@ -1,301 +1,301 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyClient==1 - -#include "UDPProxyClient.h" -#include "BitStream.h" -#include "UDPProxyCommon.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "GetTime.h" - -using namespace RakNet; -static const int DEFAULT_UNRESPONSIVE_PING_TIME=1000; - -// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress < cls.serverAddress;} -// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress > cls.serverAddress;} -// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress == cls.serverAddress;} - -STATIC_FACTORY_DEFINITIONS(UDPProxyClient,UDPProxyClient); - -UDPProxyClient::UDPProxyClient() -{ - resultHandler=0; -} -UDPProxyClient::~UDPProxyClient() -{ - Clear(); -} -void UDPProxyClient::SetResultHandler(UDPProxyClientResultHandler *rh) -{ - resultHandler=rh; -} -bool UDPProxyClient::RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, RakNetGUID targetGuid, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream) -{ - // Return false if not connected - ConnectionState cs = rakPeerInterface->GetConnectionState(proxyCoordinator); - if (cs!=IS_CONNECTED) - return false; - - // Pretty much a bug not to set the result handler, as otherwise you won't know if the operation succeeed or not - RakAssert(resultHandler!=0); - if (resultHandler==0) - return false; - - BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(false); - outgoingBs.Write(targetGuid); - outgoingBs.Write(timeoutOnNoDataMS); - if (serverSelectionBitstream && serverSelectionBitstream->GetNumberOfBitsUsed()>0) - { - outgoingBs.Write(true); - outgoingBs.Write(serverSelectionBitstream); - } - else - { - outgoingBs.Write(false); - } - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, proxyCoordinator, false); - - return true; -} -bool UDPProxyClient::RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddressAsSeenFromCoordinator, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream) -{ - // Return false if not connected - ConnectionState cs = rakPeerInterface->GetConnectionState(proxyCoordinator); - if (cs!=IS_CONNECTED) - return false; - - // Pretty much a bug not to set the result handler, as otherwise you won't know if the operation succeeed or not - RakAssert(resultHandler!=0); - if (resultHandler==0) - return false; - - BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(true); - outgoingBs.Write(targetAddressAsSeenFromCoordinator); - outgoingBs.Write(timeoutOnNoDataMS); - if (serverSelectionBitstream && serverSelectionBitstream->GetNumberOfBitsUsed()>0) - { - outgoingBs.Write(true); - outgoingBs.Write(serverSelectionBitstream); - } - else - { - outgoingBs.Write(false); - } - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, proxyCoordinator, false); - - return true; -} -void UDPProxyClient::Update(void) -{ - unsigned int idx1=0; - while (idx1 < pingServerGroups.Size()) - { - PingServerGroup *psg = pingServerGroups[idx1]; - - if (psg->serversToPing.Size() > 0 && - RakNet::GetTimeMS() > psg->startPingTime+DEFAULT_UNRESPONSIVE_PING_TIME) - { - // If they didn't reply within DEFAULT_UNRESPONSIVE_PING_TIME, just give up on them - psg->SendPingedServersToCoordinator(rakPeerInterface); - - RakNet::OP_DELETE(psg,_FILE_AND_LINE_); - pingServerGroups.RemoveAtIndex(idx1); - } - else - idx1++; - } - -} -PluginReceiveResult UDPProxyClient::OnReceive(Packet *packet) -{ - if (packet->data[0]==ID_UNCONNECTED_PONG) - { - unsigned int idx1, idx2; - PingServerGroup *psg; - for (idx1=0; idx1 < pingServerGroups.Size(); idx1++) - { - psg = pingServerGroups[idx1]; - for (idx2=0; idx2 < psg->serversToPing.Size(); idx2++) - { - if (psg->serversToPing[idx2].serverAddress==packet->systemAddress) - { - RakNet::BitStream bsIn(packet->data,packet->length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - RakNet::TimeMS sentTime; - bsIn.Read(sentTime); - RakNet::TimeMS curTime=RakNet::GetTimeMS(); - int ping; - if (curTime>sentTime) - ping=(int) (curTime-sentTime); - else - ping=0; - psg->serversToPing[idx2].ping=(unsigned short) ping; - - // If all servers to ping are now pinged, reply to coordinator - if (psg->AreAllServersPinged()) - { - psg->SendPingedServersToCoordinator(rakPeerInterface); - RakNet::OP_DELETE(psg,_FILE_AND_LINE_); - pingServerGroups.RemoveAtIndex(idx1); - } - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - - } - } - else if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) - { - switch (packet->data[1]) - { - case ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT: - { - OnPingServers(packet); - } - break; - case ID_UDP_PROXY_FORWARDING_SUCCEEDED: - case ID_UDP_PROXY_ALL_SERVERS_BUSY: - case ID_UDP_PROXY_IN_PROGRESS: - case ID_UDP_PROXY_NO_SERVERS_ONLINE: - case ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR: - case ID_UDP_PROXY_FORWARDING_NOTIFICATION: - { - RakNetGUID targetGuid; - SystemAddress senderAddress, targetAddress; - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(sizeof(MessageID)*2); - incomingBs.Read(senderAddress); - incomingBs.Read(targetAddress); - incomingBs.Read(targetGuid); - - switch (packet->data[1]) - { - case ID_UDP_PROXY_FORWARDING_NOTIFICATION: - case ID_UDP_PROXY_FORWARDING_SUCCEEDED: - { - unsigned short forwardingPort; - RakNet::RakString serverIP; - incomingBs.Read(serverIP); - incomingBs.Read(forwardingPort); - if (packet->data[1]==ID_UDP_PROXY_FORWARDING_SUCCEEDED) - { - if (resultHandler) - resultHandler->OnForwardingSuccess(serverIP.C_String(), forwardingPort, packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - } - else - { - // Send a datagram to the proxy, so if we are behind a router, that router adds an entry to the routing table. - // Otherwise the router would block the incoming datagrams from source - // It doesn't matter if the message actually arrives as long as it goes through the router - rakPeerInterface->Ping(serverIP.C_String(), forwardingPort, false); - - if (resultHandler) - resultHandler->OnForwardingNotification(serverIP.C_String(), forwardingPort, packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - } - } - break; - case ID_UDP_PROXY_ALL_SERVERS_BUSY: - if (resultHandler) - resultHandler->OnAllServersBusy(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - break; - case ID_UDP_PROXY_IN_PROGRESS: - if (resultHandler) - resultHandler->OnForwardingInProgress(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - break; - case ID_UDP_PROXY_NO_SERVERS_ONLINE: - if (resultHandler) - resultHandler->OnNoServersOnline(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - break; - case ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR: - { - if (resultHandler) - resultHandler->OnRecipientNotConnected(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); - break; - } - } - - } - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - return RR_CONTINUE_PROCESSING; -} -void UDPProxyClient::OnRakPeerShutdown(void) -{ - Clear(); -} -void UDPProxyClient::OnPingServers(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - - PingServerGroup *psg = RakNet::OP_NEW(_FILE_AND_LINE_); - - ServerWithPing swp; - incomingBs.Read(psg->sata.senderClientAddress); - incomingBs.Read(psg->sata.targetClientAddress); - psg->startPingTime=RakNet::GetTimeMS(); - psg->coordinatorAddressForPings=packet->systemAddress; - unsigned short serverListSize; - incomingBs.Read(serverListSize); - SystemAddress serverAddress; - unsigned short serverListIndex; - char ipStr[64]; - for (serverListIndex=0; serverListIndexserversToPing.Push(swp, _FILE_AND_LINE_ ); - swp.serverAddress.ToString(false,ipStr); - rakPeerInterface->Ping(ipStr,swp.serverAddress.GetPort(),false,0); - } - pingServerGroups.Push(psg,_FILE_AND_LINE_); -} - -bool UDPProxyClient::PingServerGroup::AreAllServersPinged(void) const -{ - unsigned int serversToPingIndex; - for (serversToPingIndex=0; serversToPingIndex < serversToPing.Size(); serversToPingIndex++) - { - if (serversToPing[serversToPingIndex].ping==DEFAULT_UNRESPONSIVE_PING_TIME) - return false; - } - return true; -} - -void UDPProxyClient::PingServerGroup::SendPingedServersToCoordinator(RakPeerInterface *rakPeerInterface) -{ - BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(sata.targetClientAddress); - unsigned short serversToPingSize = (unsigned short) serversToPing.Size(); - outgoingBs.Write(serversToPingSize); - unsigned int serversToPingIndex; - for (serversToPingIndex=0; serversToPingIndex < serversToPingSize; serversToPingIndex++) - { - outgoingBs.Write(serversToPing[serversToPingIndex].serverAddress); - outgoingBs.Write(serversToPing[serversToPingIndex].ping); - } - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, coordinatorAddressForPings, false); -} -void UDPProxyClient::Clear(void) -{ - for (unsigned int i=0; i < pingServerGroups.Size(); i++) - RakNet::OP_DELETE(pingServerGroups[i],_FILE_AND_LINE_); - pingServerGroups.Clear(false, _FILE_AND_LINE_); -} - - -#endif // _RAKNET_SUPPORT_* - +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyClient==1 + +#include "UDPProxyClient.h" +#include "BitStream.h" +#include "UDPProxyCommon.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "GetTime.h" + +using namespace RakNet; +static const int DEFAULT_UNRESPONSIVE_PING_TIME=1000; + +// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress < cls.serverAddress;} +// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress > cls.serverAddress;} +// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyClient::ServerWithPing &cls ) {return inputKey.Get().serverAddress == cls.serverAddress;} + +STATIC_FACTORY_DEFINITIONS(UDPProxyClient,UDPProxyClient); + +UDPProxyClient::UDPProxyClient() +{ + resultHandler=0; +} +UDPProxyClient::~UDPProxyClient() +{ + Clear(); +} +void UDPProxyClient::SetResultHandler(UDPProxyClientResultHandler *rh) +{ + resultHandler=rh; +} +bool UDPProxyClient::RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, RakNetGUID targetGuid, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream) +{ + // Return false if not connected + ConnectionState cs = rakPeerInterface->GetConnectionState(proxyCoordinator); + if (cs!=IS_CONNECTED) + return false; + + // Pretty much a bug not to set the result handler, as otherwise you won't know if the operation succeeed or not + RakAssert(resultHandler!=0); + if (resultHandler==0) + return false; + + BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(false); + outgoingBs.Write(targetGuid); + outgoingBs.Write(timeoutOnNoDataMS); + if (serverSelectionBitstream && serverSelectionBitstream->GetNumberOfBitsUsed()>0) + { + outgoingBs.Write(true); + outgoingBs.Write(serverSelectionBitstream); + } + else + { + outgoingBs.Write(false); + } + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, proxyCoordinator, false); + + return true; +} +bool UDPProxyClient::RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddressAsSeenFromCoordinator, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream) +{ + // Return false if not connected + ConnectionState cs = rakPeerInterface->GetConnectionState(proxyCoordinator); + if (cs!=IS_CONNECTED) + return false; + + // Pretty much a bug not to set the result handler, as otherwise you won't know if the operation succeeed or not + RakAssert(resultHandler!=0); + if (resultHandler==0) + return false; + + BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(true); + outgoingBs.Write(targetAddressAsSeenFromCoordinator); + outgoingBs.Write(timeoutOnNoDataMS); + if (serverSelectionBitstream && serverSelectionBitstream->GetNumberOfBitsUsed()>0) + { + outgoingBs.Write(true); + outgoingBs.Write(serverSelectionBitstream); + } + else + { + outgoingBs.Write(false); + } + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, proxyCoordinator, false); + + return true; +} +void UDPProxyClient::Update(void) +{ + unsigned int idx1=0; + while (idx1 < pingServerGroups.Size()) + { + PingServerGroup *psg = pingServerGroups[idx1]; + + if (psg->serversToPing.Size() > 0 && + RakNet::GetTimeMS() > psg->startPingTime+DEFAULT_UNRESPONSIVE_PING_TIME) + { + // If they didn't reply within DEFAULT_UNRESPONSIVE_PING_TIME, just give up on them + psg->SendPingedServersToCoordinator(rakPeerInterface); + + RakNet::OP_DELETE(psg,_FILE_AND_LINE_); + pingServerGroups.RemoveAtIndex(idx1); + } + else + idx1++; + } + +} +PluginReceiveResult UDPProxyClient::OnReceive(Packet *packet) +{ + if (packet->data[0]==ID_UNCONNECTED_PONG) + { + unsigned int idx1, idx2; + PingServerGroup *psg; + for (idx1=0; idx1 < pingServerGroups.Size(); idx1++) + { + psg = pingServerGroups[idx1]; + for (idx2=0; idx2 < psg->serversToPing.Size(); idx2++) + { + if (psg->serversToPing[idx2].serverAddress==packet->systemAddress) + { + RakNet::BitStream bsIn(packet->data,packet->length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + RakNet::TimeMS sentTime; + bsIn.Read(sentTime); + RakNet::TimeMS curTime=RakNet::GetTimeMS(); + int ping; + if (curTime>sentTime) + ping=(int) (curTime-sentTime); + else + ping=0; + psg->serversToPing[idx2].ping=(unsigned short) ping; + + // If all servers to ping are now pinged, reply to coordinator + if (psg->AreAllServersPinged()) + { + psg->SendPingedServersToCoordinator(rakPeerInterface); + RakNet::OP_DELETE(psg,_FILE_AND_LINE_); + pingServerGroups.RemoveAtIndex(idx1); + } + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + + } + } + else if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) + { + switch (packet->data[1]) + { + case ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT: + { + OnPingServers(packet); + } + break; + case ID_UDP_PROXY_FORWARDING_SUCCEEDED: + case ID_UDP_PROXY_ALL_SERVERS_BUSY: + case ID_UDP_PROXY_IN_PROGRESS: + case ID_UDP_PROXY_NO_SERVERS_ONLINE: + case ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR: + case ID_UDP_PROXY_FORWARDING_NOTIFICATION: + { + RakNetGUID targetGuid; + SystemAddress senderAddress, targetAddress; + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(sizeof(MessageID)*2); + incomingBs.Read(senderAddress); + incomingBs.Read(targetAddress); + incomingBs.Read(targetGuid); + + switch (packet->data[1]) + { + case ID_UDP_PROXY_FORWARDING_NOTIFICATION: + case ID_UDP_PROXY_FORWARDING_SUCCEEDED: + { + unsigned short forwardingPort; + RakNet::RakString serverIP; + incomingBs.Read(serverIP); + incomingBs.Read(forwardingPort); + if (packet->data[1]==ID_UDP_PROXY_FORWARDING_SUCCEEDED) + { + if (resultHandler) + resultHandler->OnForwardingSuccess(serverIP.C_String(), forwardingPort, packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + } + else + { + // Send a datagram to the proxy, so if we are behind a router, that router adds an entry to the routing table. + // Otherwise the router would block the incoming datagrams from source + // It doesn't matter if the message actually arrives as long as it goes through the router + rakPeerInterface->Ping(serverIP.C_String(), forwardingPort, false); + + if (resultHandler) + resultHandler->OnForwardingNotification(serverIP.C_String(), forwardingPort, packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + } + } + break; + case ID_UDP_PROXY_ALL_SERVERS_BUSY: + if (resultHandler) + resultHandler->OnAllServersBusy(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + break; + case ID_UDP_PROXY_IN_PROGRESS: + if (resultHandler) + resultHandler->OnForwardingInProgress(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + break; + case ID_UDP_PROXY_NO_SERVERS_ONLINE: + if (resultHandler) + resultHandler->OnNoServersOnline(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + break; + case ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR: + { + if (resultHandler) + resultHandler->OnRecipientNotConnected(packet->systemAddress, senderAddress, targetAddress, targetGuid, this); + break; + } + } + + } + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + return RR_CONTINUE_PROCESSING; +} +void UDPProxyClient::OnRakPeerShutdown(void) +{ + Clear(); +} +void UDPProxyClient::OnPingServers(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + + PingServerGroup *psg = RakNet::OP_NEW(_FILE_AND_LINE_); + + ServerWithPing swp; + incomingBs.Read(psg->sata.senderClientAddress); + incomingBs.Read(psg->sata.targetClientAddress); + psg->startPingTime=RakNet::GetTimeMS(); + psg->coordinatorAddressForPings=packet->systemAddress; + unsigned short serverListSize; + incomingBs.Read(serverListSize); + SystemAddress serverAddress; + unsigned short serverListIndex; + char ipStr[64]; + for (serverListIndex=0; serverListIndexserversToPing.Push(swp, _FILE_AND_LINE_ ); + swp.serverAddress.ToString(false,ipStr); + rakPeerInterface->Ping(ipStr,swp.serverAddress.GetPort(),false,0); + } + pingServerGroups.Push(psg,_FILE_AND_LINE_); +} + +bool UDPProxyClient::PingServerGroup::AreAllServersPinged(void) const +{ + unsigned int serversToPingIndex; + for (serversToPingIndex=0; serversToPingIndex < serversToPing.Size(); serversToPingIndex++) + { + if (serversToPing[serversToPingIndex].ping==DEFAULT_UNRESPONSIVE_PING_TIME) + return false; + } + return true; +} + +void UDPProxyClient::PingServerGroup::SendPingedServersToCoordinator(RakPeerInterface *rakPeerInterface) +{ + BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(sata.targetClientAddress); + unsigned short serversToPingSize = (unsigned short) serversToPing.Size(); + outgoingBs.Write(serversToPingSize); + unsigned int serversToPingIndex; + for (serversToPingIndex=0; serversToPingIndex < serversToPingSize; serversToPingIndex++) + { + outgoingBs.Write(serversToPing[serversToPingIndex].serverAddress); + outgoingBs.Write(serversToPing[serversToPingIndex].ping); + } + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, coordinatorAddressForPings, false); +} +void UDPProxyClient::Clear(void) +{ + for (unsigned int i=0; i < pingServerGroups.Size(); i++) + RakNet::OP_DELETE(pingServerGroups[i],_FILE_AND_LINE_); + pingServerGroups.Clear(false, _FILE_AND_LINE_); +} + + +#endif // _RAKNET_SUPPORT_* + diff --git a/src/raknet/UDPProxyClient.h b/src/raknet/UDPProxyClient.h index fe49847..eb3fb68 100755 --- a/src/raknet/UDPProxyClient.h +++ b/src/raknet/UDPProxyClient.h @@ -1,178 +1,178 @@ -/// \file -/// \brief A RakNet plugin performing networking to communicate with UDPProxyCoordinator. Ultimately used to tell UDPProxyServer to forward UDP packets. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyClient==1 - -#ifndef __UDP_PROXY_CLIENT_H -#define __UDP_PROXY_CLIENT_H - -#include "Export.h" -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "DS_List.h" - -/// \defgroup UDP_PROXY_GROUP UDPProxy -/// \brief Forwards UDP datagrams from one system to another. Protocol independent -/// \details Used when NatPunchthroughClient fails -/// \ingroup PLUGINS_GROUP - -namespace RakNet -{ -class UDPProxyClient; - -/// Callback to handle results of calling UDPProxyClient::RequestForwarding() -/// \ingroup UDP_PROXY_GROUP -struct UDPProxyClientResultHandler -{ - UDPProxyClientResultHandler() {} - virtual ~UDPProxyClientResultHandler() {} - - /// Called when our forwarding request was completed. We can now connect to \a targetAddress by using \a proxyAddress instead - /// \param[out] proxyIPAddress IP Address of the proxy server, which will forward messages to targetAddress - /// \param[out] proxyPort Remote port to use on the proxy server, which will forward messages to targetAddress - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnForwardingSuccess(const char *proxyIPAddress, unsigned short proxyPort, - SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when another system has setup forwarding, with our system as the target address. - /// Plugin automatically sends a datagram to proxyIPAddress before this callback, to open our router if necessary. - /// \param[out] proxyIPAddress IP Address of the proxy server, which will forward messages to targetAddress - /// \param[out] proxyPort Remote port to use on the proxy server, which will forward messages to targetAddress - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. This is originating source IP address of the remote system that will be sending to us. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding. This is our external IP address. - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnForwardingNotification(const char *proxyIPAddress, unsigned short proxyPort, - SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when our forwarding request failed, because no UDPProxyServers are connected to UDPProxyCoordinator - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnNoServersOnline(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when our forwarding request failed, because no UDPProxyServers are connected to UDPProxyCoordinator - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnRecipientNotConnected(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when our forwarding request failed, because all UDPProxyServers that are connected to UDPProxyCoordinator are at their capacity - /// Either add more servers, or increase capacity via UDPForwarder::SetMaxForwardEntries() - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnAllServersBusy(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; - - /// Called when our forwarding request is already in progress on the \a proxyCoordinator. - /// This can be ignored, but indicates an unneeded second request - /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. - /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding - /// \param[out] proxyClient The plugin that is calling this callback - virtual void OnForwardingInProgress(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; -}; - - -/// \brief Communicates with UDPProxyCoordinator, in order to find a UDPProxyServer to forward our datagrams. -/// \details When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa.
          -/// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin.
          -/// The UDPProxyClient connects to UDPProxyCoordinator to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request -/// \sa NatPunchthroughServer -/// \sa NatPunchthroughClient -/// \ingroup UDP_PROXY_GROUP -class RAK_DLL_EXPORT UDPProxyClient : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(UDPProxyClient) - - UDPProxyClient(); - ~UDPProxyClient(); - - /// Receives the results of calling RequestForwarding() - /// Set before calling RequestForwarding or you won't know what happened - /// \param[in] resultHandler - void SetResultHandler(UDPProxyClientResultHandler *rh); - - /// Sends a request to proxyCoordinator to find a server and have that server setup UDPForwarder::StartForwarding() on our address to \a targetAddressAsSeenFromCoordinator - /// The forwarded datagrams can be from any UDP source, not just RakNet - /// \pre Must be connected to \a proxyCoordinator - /// \pre Systems running UDPProxyServer must be connected to \a proxyCoordinator and logged in via UDPProxyCoordinator::LoginServer() or UDPProxyServer::LoginToCoordinator() - /// \note May still fail, if all proxy servers have no open connections. - /// \note RakNet's protocol will ensure a message is sent at least every 5 seconds, so if routing RakNet messages, it is a reasonable value for timeoutOnNoDataMS, plus an extra few seconds for latency. - /// \param[in] proxyCoordinator System we are connected to that is running the UDPProxyCoordinator plugin - /// \param[in] sourceAddress External IP address of the system we want to forward messages from. This does not have to be our own system. To specify our own system, you can pass UNASSIGNED_SYSTEM_ADDRESS which the coordinator will treat as our external IP address. - /// \param[in] targetAddressAsSeenFromCoordinator External IP address of the system we want to forward messages to. If this system is connected to UDPProxyCoordinator at this address using RakNet, that system will ping the server and thus open the router for incoming communication. In any other case, you are responsible for doing your own network communication to have that system ping the server. See also targetGuid in the other version of RequestForwarding(), to avoid the need to know the IP address to the coordinator of the destination. - /// \param[in] timeoutOnNoData If no data is sent by the forwarded systems, how long before removing the forward entry from UDPForwarder? UDP_FORWARDER_MAXIMUM_TIMEOUT is the maximum value. Recommended high enough to not drop valid connections, but low enough to not waste forwarding slots on the proxy server. - /// \param[in] serverSelectionBitstream If you want to send data to UDPProxyCoordinator::GetBestServer(), write it here - /// \return true if the request was sent, false if we are not connected to proxyCoordinator - bool RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddressAsSeenFromCoordinator, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream=0); - - /// Same as above, but specify the target with a GUID, in case you don't know what its address is to the coordinator - /// If requesting forwarding to a RakNet enabled system, then it is easier to use targetGuid instead of targetAddressAsSeenFromCoordinator - bool RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, RakNetGUID targetGuid, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream=0); - - /// \internal - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnRakPeerShutdown(void); - - struct ServerWithPing - { - unsigned short ping; - SystemAddress serverAddress; - }; - struct SenderAndTargetAddress - { - SystemAddress senderClientAddress; - SystemAddress targetClientAddress; - }; - struct PingServerGroup - { - SenderAndTargetAddress sata; - RakNet::TimeMS startPingTime; - SystemAddress coordinatorAddressForPings; - //DataStructures::Multilist serversToPing; - DataStructures::List serversToPing; - bool AreAllServersPinged(void) const; - void SendPingedServersToCoordinator(RakPeerInterface *rakPeerInterface); - }; - //DataStructures::Multilist pingServerGroups; - DataStructures::List pingServerGroups; -protected: - - void OnPingServers(Packet *packet); - void Clear(void); - UDPProxyClientResultHandler *resultHandler; - -}; - -} // End namespace - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief A RakNet plugin performing networking to communicate with UDPProxyCoordinator. Ultimately used to tell UDPProxyServer to forward UDP packets. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyClient==1 + +#ifndef __UDP_PROXY_CLIENT_H +#define __UDP_PROXY_CLIENT_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "DS_List.h" + +/// \defgroup UDP_PROXY_GROUP UDPProxy +/// \brief Forwards UDP datagrams from one system to another. Protocol independent +/// \details Used when NatPunchthroughClient fails +/// \ingroup PLUGINS_GROUP + +namespace RakNet +{ +class UDPProxyClient; + +/// Callback to handle results of calling UDPProxyClient::RequestForwarding() +/// \ingroup UDP_PROXY_GROUP +struct UDPProxyClientResultHandler +{ + UDPProxyClientResultHandler() {} + virtual ~UDPProxyClientResultHandler() {} + + /// Called when our forwarding request was completed. We can now connect to \a targetAddress by using \a proxyAddress instead + /// \param[out] proxyIPAddress IP Address of the proxy server, which will forward messages to targetAddress + /// \param[out] proxyPort Remote port to use on the proxy server, which will forward messages to targetAddress + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnForwardingSuccess(const char *proxyIPAddress, unsigned short proxyPort, + SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when another system has setup forwarding, with our system as the target address. + /// Plugin automatically sends a datagram to proxyIPAddress before this callback, to open our router if necessary. + /// \param[out] proxyIPAddress IP Address of the proxy server, which will forward messages to targetAddress + /// \param[out] proxyPort Remote port to use on the proxy server, which will forward messages to targetAddress + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. This is originating source IP address of the remote system that will be sending to us. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding. This is our external IP address. + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnForwardingNotification(const char *proxyIPAddress, unsigned short proxyPort, + SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when our forwarding request failed, because no UDPProxyServers are connected to UDPProxyCoordinator + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnNoServersOnline(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when our forwarding request failed, because no UDPProxyServers are connected to UDPProxyCoordinator + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnRecipientNotConnected(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when our forwarding request failed, because all UDPProxyServers that are connected to UDPProxyCoordinator are at their capacity + /// Either add more servers, or increase capacity via UDPForwarder::SetMaxForwardEntries() + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnAllServersBusy(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; + + /// Called when our forwarding request is already in progress on the \a proxyCoordinator. + /// This can be ignored, but indicates an unneeded second request + /// \param[out] proxyCoordinator \a proxyCoordinator parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] sourceAddress \a sourceAddress parameter passed to UDPProxyClient::RequestForwarding. If it was UNASSIGNED_SYSTEM_ADDRESS, it is now our external IP address. + /// \param[out] targetAddress \a targetAddress parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] targetGuid \a targetGuid parameter originally passed to UDPProxyClient::RequestForwarding + /// \param[out] proxyClient The plugin that is calling this callback + virtual void OnForwardingInProgress(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin)=0; +}; + + +/// \brief Communicates with UDPProxyCoordinator, in order to find a UDPProxyServer to forward our datagrams. +/// \details When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa.
          +/// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin.
          +/// The UDPProxyClient connects to UDPProxyCoordinator to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request +/// \sa NatPunchthroughServer +/// \sa NatPunchthroughClient +/// \ingroup UDP_PROXY_GROUP +class RAK_DLL_EXPORT UDPProxyClient : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(UDPProxyClient) + + UDPProxyClient(); + ~UDPProxyClient(); + + /// Receives the results of calling RequestForwarding() + /// Set before calling RequestForwarding or you won't know what happened + /// \param[in] resultHandler + void SetResultHandler(UDPProxyClientResultHandler *rh); + + /// Sends a request to proxyCoordinator to find a server and have that server setup UDPForwarder::StartForwarding() on our address to \a targetAddressAsSeenFromCoordinator + /// The forwarded datagrams can be from any UDP source, not just RakNet + /// \pre Must be connected to \a proxyCoordinator + /// \pre Systems running UDPProxyServer must be connected to \a proxyCoordinator and logged in via UDPProxyCoordinator::LoginServer() or UDPProxyServer::LoginToCoordinator() + /// \note May still fail, if all proxy servers have no open connections. + /// \note RakNet's protocol will ensure a message is sent at least every 5 seconds, so if routing RakNet messages, it is a reasonable value for timeoutOnNoDataMS, plus an extra few seconds for latency. + /// \param[in] proxyCoordinator System we are connected to that is running the UDPProxyCoordinator plugin + /// \param[in] sourceAddress External IP address of the system we want to forward messages from. This does not have to be our own system. To specify our own system, you can pass UNASSIGNED_SYSTEM_ADDRESS which the coordinator will treat as our external IP address. + /// \param[in] targetAddressAsSeenFromCoordinator External IP address of the system we want to forward messages to. If this system is connected to UDPProxyCoordinator at this address using RakNet, that system will ping the server and thus open the router for incoming communication. In any other case, you are responsible for doing your own network communication to have that system ping the server. See also targetGuid in the other version of RequestForwarding(), to avoid the need to know the IP address to the coordinator of the destination. + /// \param[in] timeoutOnNoData If no data is sent by the forwarded systems, how long before removing the forward entry from UDPForwarder? UDP_FORWARDER_MAXIMUM_TIMEOUT is the maximum value. Recommended high enough to not drop valid connections, but low enough to not waste forwarding slots on the proxy server. + /// \param[in] serverSelectionBitstream If you want to send data to UDPProxyCoordinator::GetBestServer(), write it here + /// \return true if the request was sent, false if we are not connected to proxyCoordinator + bool RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddressAsSeenFromCoordinator, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream=0); + + /// Same as above, but specify the target with a GUID, in case you don't know what its address is to the coordinator + /// If requesting forwarding to a RakNet enabled system, then it is easier to use targetGuid instead of targetAddressAsSeenFromCoordinator + bool RequestForwarding(SystemAddress proxyCoordinator, SystemAddress sourceAddress, RakNetGUID targetGuid, RakNet::TimeMS timeoutOnNoDataMS, RakNet::BitStream *serverSelectionBitstream=0); + + /// \internal + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnRakPeerShutdown(void); + + struct ServerWithPing + { + unsigned short ping; + SystemAddress serverAddress; + }; + struct SenderAndTargetAddress + { + SystemAddress senderClientAddress; + SystemAddress targetClientAddress; + }; + struct PingServerGroup + { + SenderAndTargetAddress sata; + RakNet::TimeMS startPingTime; + SystemAddress coordinatorAddressForPings; + //DataStructures::Multilist serversToPing; + DataStructures::List serversToPing; + bool AreAllServersPinged(void) const; + void SendPingedServersToCoordinator(RakPeerInterface *rakPeerInterface); + }; + //DataStructures::Multilist pingServerGroups; + DataStructures::List pingServerGroups; +protected: + + void OnPingServers(Packet *packet); + void Clear(void); + UDPProxyClientResultHandler *resultHandler; + +}; + +} // End namespace + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/UDPProxyCommon.h b/src/raknet/UDPProxyCommon.h index 29699f3..14593e3 100755 --- a/src/raknet/UDPProxyCommon.h +++ b/src/raknet/UDPProxyCommon.h @@ -1,57 +1,57 @@ -#ifndef __UDP_PROXY_COMMON_H -#define __UDP_PROXY_COMMON_H - -// System flow: -/* -UDPProxyClient: End user -UDPProxyServer: open server, to route messages from end users that can't connect to each other using UDPForwarder class. -UDPProxyCoordinator: Server somewhere, connected to by RakNet, to maintain a list of UDPProxyServer - -UDPProxyServer - On startup, log into UDPProxyCoordinator and register self - -UDPProxyClient - Wish to open route to X - Send message to UDPProxyCoordinator containing X, desired timeout - Wait for success or failure - -UDPProxyCoordinator: -* Get openRouteRequest - If no servers registered, return failure - Add entry to memory - chooseBestUDPProxyServer() (overridable, chooses at random by default) - Query this server to StartForwarding(). Return success or failure - If failure, choose another server from the remaining list. If none remaining, return failure. Else return success. -* Disconnect: - If disconnected system is pending client on openRouteRequest, delete that request - If disconnected system is UDPProxyServer, remove from list. For each pending client for this server, choose from remaining servers. -* Login: - Add to UDPProxyServer list, validating password if set -*/ - -// Stored in the second byte after ID_UDP_PROXY_GENERAL -// Otherwise MessageIdentifiers.h is too cluttered and will hit the limit on enumerations in a single byte -enum UDPProxyMessages -{ - ID_UDP_PROXY_FORWARDING_SUCCEEDED, - ID_UDP_PROXY_FORWARDING_NOTIFICATION, - ID_UDP_PROXY_NO_SERVERS_ONLINE, - ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR, - ID_UDP_PROXY_ALL_SERVERS_BUSY, - ID_UDP_PROXY_IN_PROGRESS, - ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR, - ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT, - ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR, - ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER, - ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR, - ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR, - ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER, - ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER, - ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER, - ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER -}; - - -#define UDP_FORWARDER_MAXIMUM_TIMEOUT (60000 * 10) - -#endif +#ifndef __UDP_PROXY_COMMON_H +#define __UDP_PROXY_COMMON_H + +// System flow: +/* +UDPProxyClient: End user +UDPProxyServer: open server, to route messages from end users that can't connect to each other using UDPForwarder class. +UDPProxyCoordinator: Server somewhere, connected to by RakNet, to maintain a list of UDPProxyServer + +UDPProxyServer + On startup, log into UDPProxyCoordinator and register self + +UDPProxyClient + Wish to open route to X + Send message to UDPProxyCoordinator containing X, desired timeout + Wait for success or failure + +UDPProxyCoordinator: +* Get openRouteRequest + If no servers registered, return failure + Add entry to memory + chooseBestUDPProxyServer() (overridable, chooses at random by default) + Query this server to StartForwarding(). Return success or failure + If failure, choose another server from the remaining list. If none remaining, return failure. Else return success. +* Disconnect: + If disconnected system is pending client on openRouteRequest, delete that request + If disconnected system is UDPProxyServer, remove from list. For each pending client for this server, choose from remaining servers. +* Login: + Add to UDPProxyServer list, validating password if set +*/ + +// Stored in the second byte after ID_UDP_PROXY_GENERAL +// Otherwise MessageIdentifiers.h is too cluttered and will hit the limit on enumerations in a single byte +enum UDPProxyMessages +{ + ID_UDP_PROXY_FORWARDING_SUCCEEDED, + ID_UDP_PROXY_FORWARDING_NOTIFICATION, + ID_UDP_PROXY_NO_SERVERS_ONLINE, + ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR, + ID_UDP_PROXY_ALL_SERVERS_BUSY, + ID_UDP_PROXY_IN_PROGRESS, + ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR, + ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT, + ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR, + ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER, + ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR, + ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR, + ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER, + ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER, + ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER, + ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER +}; + + +#define UDP_FORWARDER_MAXIMUM_TIMEOUT (60000 * 10) + +#endif diff --git a/src/raknet/UDPProxyCoordinator.cpp b/src/raknet/UDPProxyCoordinator.cpp index 9e063cd..f2ff1a0 100755 --- a/src/raknet/UDPProxyCoordinator.cpp +++ b/src/raknet/UDPProxyCoordinator.cpp @@ -1,543 +1,543 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyCoordinator==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#include "UDPProxyCoordinator.h" -#include "BitStream.h" -#include "UDPProxyCommon.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" -#include "Rand.h" -#include "GetTime.h" -#include "UDPForwarder.h" - -// Larger than the client version -static const int DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME=2000; -static const int DEFAULT_UNRESPONSIVE_PING_TIME=DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME+1000; - -using namespace RakNet; - -// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() < cls.ping;} -// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() > cls.ping;} -// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() == cls.ping;} - -int UDPProxyCoordinator::ServerWithPingComp( const unsigned short &key, const UDPProxyCoordinator::ServerWithPing &data ) -{ - if (key < data.ping) - return -1; - if (key > data.ping) - return 1; - return 0; -} - -int UDPProxyCoordinator::ForwardingRequestComp( const SenderAndTargetAddress &key, ForwardingRequest* const &data) -{ - if (key.senderClientAddress < data->sata.senderClientAddress ) - return -1; - if (key.senderClientAddress > data->sata.senderClientAddress ) - return -1; - if (key.targetClientAddress < data->sata.targetClientAddress ) - return -1; - if (key.targetClientAddress > data->sata.targetClientAddress ) - return 1; - return 0; -} -// -// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) -// { -// return inputKey.Get().senderClientAddress < cls->sata.senderClientAddress || -// (inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress < cls->sata.targetClientAddress); -// } -// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) -// { -// return inputKey.Get().senderClientAddress > cls->sata.senderClientAddress || -// (inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress > cls->sata.targetClientAddress); -// } -// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) -// { -// return inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress == cls->sata.targetClientAddress; -// } - -STATIC_FACTORY_DEFINITIONS(UDPProxyCoordinator,UDPProxyCoordinator); - -UDPProxyCoordinator::UDPProxyCoordinator() -{ - -} -UDPProxyCoordinator::~UDPProxyCoordinator() -{ - Clear(); -} -void UDPProxyCoordinator::SetRemoteLoginPassword(RakNet::RakString password) -{ - remoteLoginPassword=password; -} -void UDPProxyCoordinator::Update(void) -{ - unsigned int idx; - RakNet::TimeMS curTime = RakNet::GetTimeMS(); - ForwardingRequest *fw; - idx=0; - while (idx < forwardingRequestList.Size()) - { - fw=forwardingRequestList[idx]; - if (fw->timeRequestedPings!=0 && - curTime > fw->timeRequestedPings + DEFAULT_UNRESPONSIVE_PING_TIME) - { - fw->OrderRemainingServersToTry(); - fw->timeRequestedPings=0; - TryNextServer(fw->sata, fw); - idx++; - } - else if (fw->timeoutAfterSuccess!=0 && - curTime > fw->timeoutAfterSuccess) - { - // Forwarding request succeeded, we waited a bit to prevent duplicates. Can forget about the entry now. - RakNet::OP_DELETE(fw,_FILE_AND_LINE_); - forwardingRequestList.RemoveAtIndex(idx); - } - else - idx++; - } -} -PluginReceiveResult UDPProxyCoordinator::OnReceive(Packet *packet) -{ - if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) - { - switch (packet->data[1]) - { - case ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR: - OnForwardingRequestFromClientToCoordinator(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR: - OnLoginRequestFromServerToCoordinator(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR: - OnForwardingReplyFromServerToCoordinator(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - case ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR: - OnPingServersReplyFromClientToCoordinator(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - return RR_CONTINUE_PROCESSING; -} -void UDPProxyCoordinator::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - unsigned int idx, idx2; - - idx=0; - while (idx < forwardingRequestList.Size()) - { - if (forwardingRequestList[idx]->requestingAddress==systemAddress) - { - // Guy disconnected before the attempt completed - RakNet::OP_DELETE(forwardingRequestList[idx], _FILE_AND_LINE_); - forwardingRequestList.RemoveAtIndex(idx ); - } - else - idx++; - } - - idx = serverList.GetIndexOf(systemAddress); - if (idx!=(unsigned int)-1) - { - ForwardingRequest *fw; - // For each pending client for this server, choose from remaining servers. - for (idx2=0; idx2 < forwardingRequestList.Size(); idx2++) - { - fw = forwardingRequestList[idx2]; - if (fw->currentlyAttemptedServerAddress==systemAddress) - { - // Try the next server - TryNextServer(fw->sata, fw); - } - } - - // Remove dead server - serverList.RemoveAtIndexFast(idx); - } -} -void UDPProxyCoordinator::OnForwardingRequestFromClientToCoordinator(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - SystemAddress sourceAddress; - incomingBs.Read(sourceAddress); - if (sourceAddress==UNASSIGNED_SYSTEM_ADDRESS) - sourceAddress=packet->systemAddress; - SystemAddress targetAddress; - RakNetGUID targetGuid; - bool usesAddress=false; - incomingBs.Read(usesAddress); - if (usesAddress) - { - incomingBs.Read(targetAddress); - targetGuid=rakPeerInterface->GetGuidFromSystemAddress(targetAddress); - } - else - { - incomingBs.Read(targetGuid); - targetAddress=rakPeerInterface->GetSystemAddressFromGuid(targetGuid); - } - ForwardingRequest *fw = RakNet::OP_NEW(_FILE_AND_LINE_); - fw->timeoutAfterSuccess=0; - incomingBs.Read(fw->timeoutOnNoDataMS); - bool hasServerSelectionBitstream=false; - incomingBs.Read(hasServerSelectionBitstream); - if (hasServerSelectionBitstream) - incomingBs.Read(&(fw->serverSelectionBitstream)); - - RakNet::BitStream outgoingBs; - SenderAndTargetAddress sata; - sata.senderClientAddress=sourceAddress; - sata.targetClientAddress=targetAddress; - sata.targetClientGuid=targetGuid; - sata.senderClientGuid=rakPeerInterface->GetGuidFromSystemAddress(sourceAddress); - SenderAndTargetAddress sataReversed; - sataReversed.senderClientAddress=targetAddress; - sataReversed.targetClientAddress=sourceAddress; - sataReversed.senderClientGuid=sata.targetClientGuid; - sataReversed.targetClientGuid=sata.senderClientGuid; - - unsigned int insertionIndex; - bool objectExists1, objectExists2; - insertionIndex=forwardingRequestList.GetIndexFromKey(sata, &objectExists1); - forwardingRequestList.GetIndexFromKey(sataReversed, &objectExists2); - - if (objectExists1 || objectExists2) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_IN_PROGRESS); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(targetGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - RakNet::OP_DELETE(fw, _FILE_AND_LINE_); - return; - } - - if (serverList.Size()==0) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_NO_SERVERS_ONLINE); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(targetGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - RakNet::OP_DELETE(fw, _FILE_AND_LINE_); - return; - } - - if (rakPeerInterface->GetConnectionState(targetAddress)!=IS_CONNECTED && usesAddress==false) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(targetGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - RakNet::OP_DELETE(fw, _FILE_AND_LINE_); - return; - } - - fw->sata=sata; - fw->requestingAddress=packet->systemAddress; - - if (serverList.Size()>1) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(targetGuid); - unsigned short serverListSize = (unsigned short) serverList.Size(); - outgoingBs.Write(serverListSize); - unsigned int idx; - for (idx=0; idx < serverList.Size(); idx++) - outgoingBs.Write(serverList[idx]); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, sourceAddress, false); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, targetAddress, false); - fw->timeRequestedPings=RakNet::GetTimeMS(); - unsigned int copyIndex; - for (copyIndex=0; copyIndex < serverList.Size(); copyIndex++) - fw->remainingServersToTry.Push(serverList[copyIndex], _FILE_AND_LINE_ ); - forwardingRequestList.InsertAtIndex(fw, insertionIndex, _FILE_AND_LINE_ ); - } - else - { - fw->timeRequestedPings=0; - fw->currentlyAttemptedServerAddress=serverList[0]; - forwardingRequestList.InsertAtIndex(fw, insertionIndex, _FILE_AND_LINE_ ); - SendForwardingRequest(sourceAddress, targetAddress, fw->currentlyAttemptedServerAddress, fw->timeoutOnNoDataMS); - } -} - -void UDPProxyCoordinator::SendForwardingRequest(SystemAddress sourceAddress, SystemAddress targetAddress, SystemAddress serverAddress, RakNet::TimeMS timeoutOnNoDataMS) -{ - RakNet::BitStream outgoingBs; - // Send request to desired server - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write(timeoutOnNoDataMS); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, serverAddress, false); -} -void UDPProxyCoordinator::OnLoginRequestFromServerToCoordinator(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - RakNet::RakString password; - incomingBs.Read(password); - RakNet::BitStream outgoingBs; - - if (remoteLoginPassword.IsEmpty()) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - return; - } - - if (remoteLoginPassword!=password) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - return; - } - - unsigned int insertionIndex; - insertionIndex=serverList.GetIndexOf(packet->systemAddress); - if (insertionIndex!=(unsigned int)-1) - { - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); - return; - } - serverList.Push(packet->systemAddress, _FILE_AND_LINE_ ); - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); -} -void UDPProxyCoordinator::OnForwardingReplyFromServerToCoordinator(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - SenderAndTargetAddress sata; - incomingBs.Read(sata.senderClientAddress); - incomingBs.Read(sata.targetClientAddress); - bool objectExists; - unsigned int index = forwardingRequestList.GetIndexFromKey(sata, &objectExists); - if (objectExists==false) - { - // The guy disconnected before the request finished - return; - } - ForwardingRequest *fw = forwardingRequestList[index]; - sata.senderClientGuid = fw->sata.senderClientGuid; - sata.targetClientGuid = fw->sata.targetClientGuid; - - UDPForwarderResult success; - unsigned char c; - incomingBs.Read(c); - success=(UDPForwarderResult)c; - - RakNet::BitStream outgoingBs; - if (success==UDPFORWARDER_SUCCESS) - { - char serverIP[64]; - packet->systemAddress.ToString(false,serverIP); - unsigned short forwardingPort; - incomingBs.Read(forwardingPort); - - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_SUCCEEDED); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(sata.targetClientAddress); - outgoingBs.Write(sata.targetClientGuid); - outgoingBs.Write(RakNet::RakString(serverIP)); - outgoingBs.Write(forwardingPort); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, fw->requestingAddress, false); - - outgoingBs.Reset(); - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_NOTIFICATION); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(sata.targetClientAddress); - outgoingBs.Write(sata.targetClientGuid); - outgoingBs.Write(RakNet::RakString(serverIP)); - outgoingBs.Write(forwardingPort); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, sata.targetClientAddress, false); - - // 05/18/09 Keep the entry around for some time after success, so duplicates are reported if attempting forwarding from the target system before notification of success - fw->timeoutAfterSuccess=RakNet::GetTimeMS()+fw->timeoutOnNoDataMS; - // forwardingRequestList.RemoveAtIndex(index); - // RakNet::OP_DELETE(fw,_FILE_AND_LINE_); - - return; - } - else if (success==UDPFORWARDER_NO_SOCKETS) - { - // Try next server - TryNextServer(sata, fw); - } - else - { - RakAssert(success==UDPFORWARDER_FORWARDING_ALREADY_EXISTS); - - // Return in progress - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_IN_PROGRESS); - outgoingBs.Write(sata.senderClientAddress); - outgoingBs.Write(sata.targetClientAddress); - outgoingBs.Write(sata.targetClientGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, fw->requestingAddress, false); - forwardingRequestList.RemoveAtIndex(index); - RakNet::OP_DELETE(fw,_FILE_AND_LINE_); - } -} -void UDPProxyCoordinator::OnPingServersReplyFromClientToCoordinator(Packet *packet) -{ - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - unsigned short serversToPingSize; - SystemAddress serverAddress; - SenderAndTargetAddress sata; - incomingBs.Read(sata.senderClientAddress); - incomingBs.Read(sata.targetClientAddress); - bool objectExists; - unsigned int index = forwardingRequestList.GetIndexFromKey(sata, &objectExists); - if (objectExists==false) - return; - unsigned short idx; - ServerWithPing swp; - ForwardingRequest *fw = forwardingRequestList[index]; - if (fw->timeRequestedPings==0) - return; - - incomingBs.Read(serversToPingSize); - if (packet->systemAddress==sata.senderClientAddress) - { - for (idx=0; idx < serversToPingSize; idx++) - { - incomingBs.Read(swp.serverAddress); - incomingBs.Read(swp.ping); - unsigned int index2; - for (index2=0; index2 < fw->sourceServerPings.Size(); index2++) - { - if (fw->sourceServerPings[index2].ping >= swp.ping ) - break; - } - fw->sourceServerPings.Insert(swp, index2, _FILE_AND_LINE_); - } - } - else - { - for (idx=0; idx < serversToPingSize; idx++) - { - incomingBs.Read(swp.serverAddress); - incomingBs.Read(swp.ping); - - unsigned int index2; - for (index2=0; index2 < fw->targetServerPings.Size(); index2++) - { - if (fw->targetServerPings[index2].ping >= swp.ping ) - break; - } - fw->sourceServerPings.Insert(swp, index2, _FILE_AND_LINE_); - } - } - - // Both systems have to give us pings to progress here. Otherwise will timeout in Update() - if (fw->sourceServerPings.Size()>0 && - fw->targetServerPings.Size()>0) - { - fw->OrderRemainingServersToTry(); - fw->timeRequestedPings=0; - TryNextServer(fw->sata, fw); - } -} -void UDPProxyCoordinator::TryNextServer(SenderAndTargetAddress sata, ForwardingRequest *fw) -{ - bool pickedGoodServer=false; - while(fw->remainingServersToTry.Size()>0) - { - fw->currentlyAttemptedServerAddress=fw->remainingServersToTry.Pop(); - if (serverList.GetIndexOf(fw->currentlyAttemptedServerAddress)!=(unsigned int)-1) - { - pickedGoodServer=true; - break; - } - } - - if (pickedGoodServer==false) - { - SendAllBusy(sata.senderClientAddress, sata.targetClientAddress, sata.targetClientGuid, fw->requestingAddress); - forwardingRequestList.Remove(sata); - RakNet::OP_DELETE(fw,_FILE_AND_LINE_); - return; - } - - SendForwardingRequest(sata.senderClientAddress, sata.targetClientAddress, fw->currentlyAttemptedServerAddress, fw->timeoutOnNoDataMS); -} -void UDPProxyCoordinator::SendAllBusy(SystemAddress senderClientAddress, SystemAddress targetClientAddress, RakNetGUID targetClientGuid, SystemAddress requestingAddress) -{ - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_ALL_SERVERS_BUSY); - outgoingBs.Write(senderClientAddress); - outgoingBs.Write(targetClientAddress); - outgoingBs.Write(targetClientGuid); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, requestingAddress, false); -} -void UDPProxyCoordinator::Clear(void) -{ - serverList.Clear(true, _FILE_AND_LINE_); - for (unsigned int i=0; i < forwardingRequestList.Size(); i++) - { - RakNet::OP_DELETE(forwardingRequestList[i],_FILE_AND_LINE_); - } - forwardingRequestList.Clear(false, _FILE_AND_LINE_); -} -void UDPProxyCoordinator::ForwardingRequest::OrderRemainingServersToTry(void) -{ - //DataStructures::Multilist swpList; - DataStructures::OrderedList swpList; - // swpList.SetSortOrder(true); - - if (sourceServerPings.Size()==0 && targetServerPings.Size()==0) - return; - - unsigned int idx; - UDPProxyCoordinator::ServerWithPing swp; - for (idx=0; idx < remainingServersToTry.Size(); idx++) - { - swp.serverAddress=remainingServersToTry[idx]; - swp.ping=0; - if (sourceServerPings.Size()) - swp.ping+=(unsigned short) (sourceServerPings[idx].ping); - else - swp.ping+=(unsigned short) (DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME); - if (targetServerPings.Size()) - swp.ping+=(unsigned short) (targetServerPings[idx].ping); - else - swp.ping+=(unsigned short) (DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME); - swpList.Insert(swp.ping, swp, false, _FILE_AND_LINE_); - } - remainingServersToTry.Clear(_FILE_AND_LINE_ ); - for (idx=0; idx < swpList.Size(); idx++) - { - remainingServersToTry.Push(swpList[idx].serverAddress, _FILE_AND_LINE_ ); - } -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyCoordinator==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#include "UDPProxyCoordinator.h" +#include "BitStream.h" +#include "UDPProxyCommon.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" +#include "Rand.h" +#include "GetTime.h" +#include "UDPForwarder.h" + +// Larger than the client version +static const int DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME=2000; +static const int DEFAULT_UNRESPONSIVE_PING_TIME=DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME+1000; + +using namespace RakNet; + +// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() < cls.ping;} +// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() > cls.ping;} +// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ServerWithPing &cls ) {return inputKey.Get() == cls.ping;} + +int UDPProxyCoordinator::ServerWithPingComp( const unsigned short &key, const UDPProxyCoordinator::ServerWithPing &data ) +{ + if (key < data.ping) + return -1; + if (key > data.ping) + return 1; + return 0; +} + +int UDPProxyCoordinator::ForwardingRequestComp( const SenderAndTargetAddress &key, ForwardingRequest* const &data) +{ + if (key.senderClientAddress < data->sata.senderClientAddress ) + return -1; + if (key.senderClientAddress > data->sata.senderClientAddress ) + return -1; + if (key.targetClientAddress < data->sata.targetClientAddress ) + return -1; + if (key.targetClientAddress > data->sata.targetClientAddress ) + return 1; + return 0; +} +// +// bool operator<( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) +// { +// return inputKey.Get().senderClientAddress < cls->sata.senderClientAddress || +// (inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress < cls->sata.targetClientAddress); +// } +// bool operator>( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) +// { +// return inputKey.Get().senderClientAddress > cls->sata.senderClientAddress || +// (inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress > cls->sata.targetClientAddress); +// } +// bool operator==( const DataStructures::MLKeyRef &inputKey, const UDPProxyCoordinator::ForwardingRequest *cls ) +// { +// return inputKey.Get().senderClientAddress == cls->sata.senderClientAddress && inputKey.Get().targetClientAddress == cls->sata.targetClientAddress; +// } + +STATIC_FACTORY_DEFINITIONS(UDPProxyCoordinator,UDPProxyCoordinator); + +UDPProxyCoordinator::UDPProxyCoordinator() +{ + +} +UDPProxyCoordinator::~UDPProxyCoordinator() +{ + Clear(); +} +void UDPProxyCoordinator::SetRemoteLoginPassword(RakNet::RakString password) +{ + remoteLoginPassword=password; +} +void UDPProxyCoordinator::Update(void) +{ + unsigned int idx; + RakNet::TimeMS curTime = RakNet::GetTimeMS(); + ForwardingRequest *fw; + idx=0; + while (idx < forwardingRequestList.Size()) + { + fw=forwardingRequestList[idx]; + if (fw->timeRequestedPings!=0 && + curTime > fw->timeRequestedPings + DEFAULT_UNRESPONSIVE_PING_TIME) + { + fw->OrderRemainingServersToTry(); + fw->timeRequestedPings=0; + TryNextServer(fw->sata, fw); + idx++; + } + else if (fw->timeoutAfterSuccess!=0 && + curTime > fw->timeoutAfterSuccess) + { + // Forwarding request succeeded, we waited a bit to prevent duplicates. Can forget about the entry now. + RakNet::OP_DELETE(fw,_FILE_AND_LINE_); + forwardingRequestList.RemoveAtIndex(idx); + } + else + idx++; + } +} +PluginReceiveResult UDPProxyCoordinator::OnReceive(Packet *packet) +{ + if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) + { + switch (packet->data[1]) + { + case ID_UDP_PROXY_FORWARDING_REQUEST_FROM_CLIENT_TO_COORDINATOR: + OnForwardingRequestFromClientToCoordinator(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR: + OnLoginRequestFromServerToCoordinator(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR: + OnForwardingReplyFromServerToCoordinator(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_UDP_PROXY_PING_SERVERS_REPLY_FROM_CLIENT_TO_COORDINATOR: + OnPingServersReplyFromClientToCoordinator(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + return RR_CONTINUE_PROCESSING; +} +void UDPProxyCoordinator::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + unsigned int idx, idx2; + + idx=0; + while (idx < forwardingRequestList.Size()) + { + if (forwardingRequestList[idx]->requestingAddress==systemAddress) + { + // Guy disconnected before the attempt completed + RakNet::OP_DELETE(forwardingRequestList[idx], _FILE_AND_LINE_); + forwardingRequestList.RemoveAtIndex(idx ); + } + else + idx++; + } + + idx = serverList.GetIndexOf(systemAddress); + if (idx!=(unsigned int)-1) + { + ForwardingRequest *fw; + // For each pending client for this server, choose from remaining servers. + for (idx2=0; idx2 < forwardingRequestList.Size(); idx2++) + { + fw = forwardingRequestList[idx2]; + if (fw->currentlyAttemptedServerAddress==systemAddress) + { + // Try the next server + TryNextServer(fw->sata, fw); + } + } + + // Remove dead server + serverList.RemoveAtIndexFast(idx); + } +} +void UDPProxyCoordinator::OnForwardingRequestFromClientToCoordinator(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + SystemAddress sourceAddress; + incomingBs.Read(sourceAddress); + if (sourceAddress==UNASSIGNED_SYSTEM_ADDRESS) + sourceAddress=packet->systemAddress; + SystemAddress targetAddress; + RakNetGUID targetGuid; + bool usesAddress=false; + incomingBs.Read(usesAddress); + if (usesAddress) + { + incomingBs.Read(targetAddress); + targetGuid=rakPeerInterface->GetGuidFromSystemAddress(targetAddress); + } + else + { + incomingBs.Read(targetGuid); + targetAddress=rakPeerInterface->GetSystemAddressFromGuid(targetGuid); + } + ForwardingRequest *fw = RakNet::OP_NEW(_FILE_AND_LINE_); + fw->timeoutAfterSuccess=0; + incomingBs.Read(fw->timeoutOnNoDataMS); + bool hasServerSelectionBitstream=false; + incomingBs.Read(hasServerSelectionBitstream); + if (hasServerSelectionBitstream) + incomingBs.Read(&(fw->serverSelectionBitstream)); + + RakNet::BitStream outgoingBs; + SenderAndTargetAddress sata; + sata.senderClientAddress=sourceAddress; + sata.targetClientAddress=targetAddress; + sata.targetClientGuid=targetGuid; + sata.senderClientGuid=rakPeerInterface->GetGuidFromSystemAddress(sourceAddress); + SenderAndTargetAddress sataReversed; + sataReversed.senderClientAddress=targetAddress; + sataReversed.targetClientAddress=sourceAddress; + sataReversed.senderClientGuid=sata.targetClientGuid; + sataReversed.targetClientGuid=sata.senderClientGuid; + + unsigned int insertionIndex; + bool objectExists1, objectExists2; + insertionIndex=forwardingRequestList.GetIndexFromKey(sata, &objectExists1); + forwardingRequestList.GetIndexFromKey(sataReversed, &objectExists2); + + if (objectExists1 || objectExists2) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_IN_PROGRESS); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(targetGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + RakNet::OP_DELETE(fw, _FILE_AND_LINE_); + return; + } + + if (serverList.Size()==0) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_NO_SERVERS_ONLINE); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(targetGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + RakNet::OP_DELETE(fw, _FILE_AND_LINE_); + return; + } + + if (rakPeerInterface->GetConnectionState(targetAddress)!=IS_CONNECTED && usesAddress==false) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_RECIPIENT_GUID_NOT_CONNECTED_TO_COORDINATOR); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(targetGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + RakNet::OP_DELETE(fw, _FILE_AND_LINE_); + return; + } + + fw->sata=sata; + fw->requestingAddress=packet->systemAddress; + + if (serverList.Size()>1) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_PING_SERVERS_FROM_COORDINATOR_TO_CLIENT); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(targetGuid); + unsigned short serverListSize = (unsigned short) serverList.Size(); + outgoingBs.Write(serverListSize); + unsigned int idx; + for (idx=0; idx < serverList.Size(); idx++) + outgoingBs.Write(serverList[idx]); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, sourceAddress, false); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, targetAddress, false); + fw->timeRequestedPings=RakNet::GetTimeMS(); + unsigned int copyIndex; + for (copyIndex=0; copyIndex < serverList.Size(); copyIndex++) + fw->remainingServersToTry.Push(serverList[copyIndex], _FILE_AND_LINE_ ); + forwardingRequestList.InsertAtIndex(fw, insertionIndex, _FILE_AND_LINE_ ); + } + else + { + fw->timeRequestedPings=0; + fw->currentlyAttemptedServerAddress=serverList[0]; + forwardingRequestList.InsertAtIndex(fw, insertionIndex, _FILE_AND_LINE_ ); + SendForwardingRequest(sourceAddress, targetAddress, fw->currentlyAttemptedServerAddress, fw->timeoutOnNoDataMS); + } +} + +void UDPProxyCoordinator::SendForwardingRequest(SystemAddress sourceAddress, SystemAddress targetAddress, SystemAddress serverAddress, RakNet::TimeMS timeoutOnNoDataMS) +{ + RakNet::BitStream outgoingBs; + // Send request to desired server + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write(timeoutOnNoDataMS); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, serverAddress, false); +} +void UDPProxyCoordinator::OnLoginRequestFromServerToCoordinator(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + RakNet::RakString password; + incomingBs.Read(password); + RakNet::BitStream outgoingBs; + + if (remoteLoginPassword.IsEmpty()) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + return; + } + + if (remoteLoginPassword!=password) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + return; + } + + unsigned int insertionIndex; + insertionIndex=serverList.GetIndexOf(packet->systemAddress); + if (insertionIndex!=(unsigned int)-1) + { + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); + return; + } + serverList.Push(packet->systemAddress, _FILE_AND_LINE_ ); + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); +} +void UDPProxyCoordinator::OnForwardingReplyFromServerToCoordinator(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + SenderAndTargetAddress sata; + incomingBs.Read(sata.senderClientAddress); + incomingBs.Read(sata.targetClientAddress); + bool objectExists; + unsigned int index = forwardingRequestList.GetIndexFromKey(sata, &objectExists); + if (objectExists==false) + { + // The guy disconnected before the request finished + return; + } + ForwardingRequest *fw = forwardingRequestList[index]; + sata.senderClientGuid = fw->sata.senderClientGuid; + sata.targetClientGuid = fw->sata.targetClientGuid; + + UDPForwarderResult success; + unsigned char c; + incomingBs.Read(c); + success=(UDPForwarderResult)c; + + RakNet::BitStream outgoingBs; + if (success==UDPFORWARDER_SUCCESS) + { + char serverIP[64]; + packet->systemAddress.ToString(false,serverIP); + unsigned short forwardingPort; + incomingBs.Read(forwardingPort); + + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_SUCCEEDED); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(sata.targetClientAddress); + outgoingBs.Write(sata.targetClientGuid); + outgoingBs.Write(RakNet::RakString(serverIP)); + outgoingBs.Write(forwardingPort); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, fw->requestingAddress, false); + + outgoingBs.Reset(); + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_NOTIFICATION); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(sata.targetClientAddress); + outgoingBs.Write(sata.targetClientGuid); + outgoingBs.Write(RakNet::RakString(serverIP)); + outgoingBs.Write(forwardingPort); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, sata.targetClientAddress, false); + + // 05/18/09 Keep the entry around for some time after success, so duplicates are reported if attempting forwarding from the target system before notification of success + fw->timeoutAfterSuccess=RakNet::GetTimeMS()+fw->timeoutOnNoDataMS; + // forwardingRequestList.RemoveAtIndex(index); + // RakNet::OP_DELETE(fw,_FILE_AND_LINE_); + + return; + } + else if (success==UDPFORWARDER_NO_SOCKETS) + { + // Try next server + TryNextServer(sata, fw); + } + else + { + RakAssert(success==UDPFORWARDER_FORWARDING_ALREADY_EXISTS); + + // Return in progress + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_IN_PROGRESS); + outgoingBs.Write(sata.senderClientAddress); + outgoingBs.Write(sata.targetClientAddress); + outgoingBs.Write(sata.targetClientGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, fw->requestingAddress, false); + forwardingRequestList.RemoveAtIndex(index); + RakNet::OP_DELETE(fw,_FILE_AND_LINE_); + } +} +void UDPProxyCoordinator::OnPingServersReplyFromClientToCoordinator(Packet *packet) +{ + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + unsigned short serversToPingSize; + SystemAddress serverAddress; + SenderAndTargetAddress sata; + incomingBs.Read(sata.senderClientAddress); + incomingBs.Read(sata.targetClientAddress); + bool objectExists; + unsigned int index = forwardingRequestList.GetIndexFromKey(sata, &objectExists); + if (objectExists==false) + return; + unsigned short idx; + ServerWithPing swp; + ForwardingRequest *fw = forwardingRequestList[index]; + if (fw->timeRequestedPings==0) + return; + + incomingBs.Read(serversToPingSize); + if (packet->systemAddress==sata.senderClientAddress) + { + for (idx=0; idx < serversToPingSize; idx++) + { + incomingBs.Read(swp.serverAddress); + incomingBs.Read(swp.ping); + unsigned int index2; + for (index2=0; index2 < fw->sourceServerPings.Size(); index2++) + { + if (fw->sourceServerPings[index2].ping >= swp.ping ) + break; + } + fw->sourceServerPings.Insert(swp, index2, _FILE_AND_LINE_); + } + } + else + { + for (idx=0; idx < serversToPingSize; idx++) + { + incomingBs.Read(swp.serverAddress); + incomingBs.Read(swp.ping); + + unsigned int index2; + for (index2=0; index2 < fw->targetServerPings.Size(); index2++) + { + if (fw->targetServerPings[index2].ping >= swp.ping ) + break; + } + fw->sourceServerPings.Insert(swp, index2, _FILE_AND_LINE_); + } + } + + // Both systems have to give us pings to progress here. Otherwise will timeout in Update() + if (fw->sourceServerPings.Size()>0 && + fw->targetServerPings.Size()>0) + { + fw->OrderRemainingServersToTry(); + fw->timeRequestedPings=0; + TryNextServer(fw->sata, fw); + } +} +void UDPProxyCoordinator::TryNextServer(SenderAndTargetAddress sata, ForwardingRequest *fw) +{ + bool pickedGoodServer=false; + while(fw->remainingServersToTry.Size()>0) + { + fw->currentlyAttemptedServerAddress=fw->remainingServersToTry.Pop(); + if (serverList.GetIndexOf(fw->currentlyAttemptedServerAddress)!=(unsigned int)-1) + { + pickedGoodServer=true; + break; + } + } + + if (pickedGoodServer==false) + { + SendAllBusy(sata.senderClientAddress, sata.targetClientAddress, sata.targetClientGuid, fw->requestingAddress); + forwardingRequestList.Remove(sata); + RakNet::OP_DELETE(fw,_FILE_AND_LINE_); + return; + } + + SendForwardingRequest(sata.senderClientAddress, sata.targetClientAddress, fw->currentlyAttemptedServerAddress, fw->timeoutOnNoDataMS); +} +void UDPProxyCoordinator::SendAllBusy(SystemAddress senderClientAddress, SystemAddress targetClientAddress, RakNetGUID targetClientGuid, SystemAddress requestingAddress) +{ + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_ALL_SERVERS_BUSY); + outgoingBs.Write(senderClientAddress); + outgoingBs.Write(targetClientAddress); + outgoingBs.Write(targetClientGuid); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, requestingAddress, false); +} +void UDPProxyCoordinator::Clear(void) +{ + serverList.Clear(true, _FILE_AND_LINE_); + for (unsigned int i=0; i < forwardingRequestList.Size(); i++) + { + RakNet::OP_DELETE(forwardingRequestList[i],_FILE_AND_LINE_); + } + forwardingRequestList.Clear(false, _FILE_AND_LINE_); +} +void UDPProxyCoordinator::ForwardingRequest::OrderRemainingServersToTry(void) +{ + //DataStructures::Multilist swpList; + DataStructures::OrderedList swpList; + // swpList.SetSortOrder(true); + + if (sourceServerPings.Size()==0 && targetServerPings.Size()==0) + return; + + unsigned int idx; + UDPProxyCoordinator::ServerWithPing swp; + for (idx=0; idx < remainingServersToTry.Size(); idx++) + { + swp.serverAddress=remainingServersToTry[idx]; + swp.ping=0; + if (sourceServerPings.Size()) + swp.ping+=(unsigned short) (sourceServerPings[idx].ping); + else + swp.ping+=(unsigned short) (DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME); + if (targetServerPings.Size()) + swp.ping+=(unsigned short) (targetServerPings[idx].ping); + else + swp.ping+=(unsigned short) (DEFAULT_CLIENT_UNRESPONSIVE_PING_TIME); + swpList.Insert(swp.ping, swp, false, _FILE_AND_LINE_); + } + remainingServersToTry.Clear(_FILE_AND_LINE_ ); + for (idx=0; idx < swpList.Size(); idx++) + { + remainingServersToTry.Push(swpList[idx].serverAddress, _FILE_AND_LINE_ ); + } +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/UDPProxyCoordinator.h b/src/raknet/UDPProxyCoordinator.h index 783e061..0107346 100755 --- a/src/raknet/UDPProxyCoordinator.h +++ b/src/raknet/UDPProxyCoordinator.h @@ -1,115 +1,115 @@ -/// \file -/// \brief Essentially maintains a list of servers running UDPProxyServer, and some state management for UDPProxyClient to find a free server to forward datagrams -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyCoordinator==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#ifndef __UDP_PROXY_COORDINATOR_H -#define __UDP_PROXY_COORDINATOR_H - -#include "Export.h" -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "RakString.h" -#include "BitStream.h" -#include "DS_Queue.h" -#include "DS_OrderedList.h" - -namespace RakNet -{ - /// When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa - /// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin. - /// The UDPProxyClient connects to UDPProxyCoordinator to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request - /// \brief Middleman between UDPProxyServer and UDPProxyClient, maintaining a list of UDPProxyServer, and managing state for clients to find an available forwarding server. - /// \ingroup NAT_PUNCHTHROUGH_GROUP - class RAK_DLL_EXPORT UDPProxyCoordinator : public PluginInterface2 - { - public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(UDPProxyCoordinator) - - UDPProxyCoordinator(); - virtual ~UDPProxyCoordinator(); - - /// For UDPProxyServers logging in remotely, they must pass a password to UDPProxyServer::LoginToCoordinator(). It must match the password set here. - /// If no password is set, they cannot login remotely. - /// By default, no password is set - void SetRemoteLoginPassword(RakNet::RakString password); - - /// \internal - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - - struct SenderAndTargetAddress - { - SystemAddress senderClientAddress; - RakNetGUID senderClientGuid; - SystemAddress targetClientAddress; - RakNetGUID targetClientGuid; - }; - - struct ServerWithPing - { - unsigned short ping; - SystemAddress serverAddress; - }; - - struct ForwardingRequest - { - RakNet::TimeMS timeoutOnNoDataMS; - RakNet::TimeMS timeoutAfterSuccess; - SenderAndTargetAddress sata; - SystemAddress requestingAddress; // Which system originally sent the network message to start forwarding - SystemAddress currentlyAttemptedServerAddress; - DataStructures::Queue remainingServersToTry; - RakNet::BitStream serverSelectionBitstream; - - DataStructures::List sourceServerPings, targetServerPings; - RakNet::TimeMS timeRequestedPings; - // Order based on sourceServerPings and targetServerPings - void OrderRemainingServersToTry(void); - - }; - protected: - - static int ServerWithPingComp( const unsigned short &key, const UDPProxyCoordinator::ServerWithPing &data ); - static int ForwardingRequestComp( const SenderAndTargetAddress &key, ForwardingRequest* const &data); - - void OnForwardingRequestFromClientToCoordinator(Packet *packet); - void OnLoginRequestFromServerToCoordinator(Packet *packet); - void OnForwardingReplyFromServerToCoordinator(Packet *packet); - void OnPingServersReplyFromClientToCoordinator(Packet *packet); - void TryNextServer(SenderAndTargetAddress sata, ForwardingRequest *fw); - void SendAllBusy(SystemAddress senderClientAddress, SystemAddress targetClientAddress, RakNetGUID targetClientGuid, SystemAddress requestingAddress); - void Clear(void); - - void SendForwardingRequest(SystemAddress sourceAddress, SystemAddress targetAddress, SystemAddress serverAddress, RakNet::TimeMS timeoutOnNoDataMS); - - // Logged in servers - //DataStructures::Multilist serverList; - DataStructures::List serverList; - - // Forwarding requests in progress - //DataStructures::Multilist forwardingRequestList; - DataStructures::OrderedList forwardingRequestList; - - RakNet::RakString remoteLoginPassword; - - }; - -} // End namespace - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief Essentially maintains a list of servers running UDPProxyServer, and some state management for UDPProxyClient to find a free server to forward datagrams +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyCoordinator==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#ifndef __UDP_PROXY_COORDINATOR_H +#define __UDP_PROXY_COORDINATOR_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "RakString.h" +#include "BitStream.h" +#include "DS_Queue.h" +#include "DS_OrderedList.h" + +namespace RakNet +{ + /// When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa + /// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin. + /// The UDPProxyClient connects to UDPProxyCoordinator to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request + /// \brief Middleman between UDPProxyServer and UDPProxyClient, maintaining a list of UDPProxyServer, and managing state for clients to find an available forwarding server. + /// \ingroup NAT_PUNCHTHROUGH_GROUP + class RAK_DLL_EXPORT UDPProxyCoordinator : public PluginInterface2 + { + public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(UDPProxyCoordinator) + + UDPProxyCoordinator(); + virtual ~UDPProxyCoordinator(); + + /// For UDPProxyServers logging in remotely, they must pass a password to UDPProxyServer::LoginToCoordinator(). It must match the password set here. + /// If no password is set, they cannot login remotely. + /// By default, no password is set + void SetRemoteLoginPassword(RakNet::RakString password); + + /// \internal + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + + struct SenderAndTargetAddress + { + SystemAddress senderClientAddress; + RakNetGUID senderClientGuid; + SystemAddress targetClientAddress; + RakNetGUID targetClientGuid; + }; + + struct ServerWithPing + { + unsigned short ping; + SystemAddress serverAddress; + }; + + struct ForwardingRequest + { + RakNet::TimeMS timeoutOnNoDataMS; + RakNet::TimeMS timeoutAfterSuccess; + SenderAndTargetAddress sata; + SystemAddress requestingAddress; // Which system originally sent the network message to start forwarding + SystemAddress currentlyAttemptedServerAddress; + DataStructures::Queue remainingServersToTry; + RakNet::BitStream serverSelectionBitstream; + + DataStructures::List sourceServerPings, targetServerPings; + RakNet::TimeMS timeRequestedPings; + // Order based on sourceServerPings and targetServerPings + void OrderRemainingServersToTry(void); + + }; + protected: + + static int ServerWithPingComp( const unsigned short &key, const UDPProxyCoordinator::ServerWithPing &data ); + static int ForwardingRequestComp( const SenderAndTargetAddress &key, ForwardingRequest* const &data); + + void OnForwardingRequestFromClientToCoordinator(Packet *packet); + void OnLoginRequestFromServerToCoordinator(Packet *packet); + void OnForwardingReplyFromServerToCoordinator(Packet *packet); + void OnPingServersReplyFromClientToCoordinator(Packet *packet); + void TryNextServer(SenderAndTargetAddress sata, ForwardingRequest *fw); + void SendAllBusy(SystemAddress senderClientAddress, SystemAddress targetClientAddress, RakNetGUID targetClientGuid, SystemAddress requestingAddress); + void Clear(void); + + void SendForwardingRequest(SystemAddress sourceAddress, SystemAddress targetAddress, SystemAddress serverAddress, RakNet::TimeMS timeoutOnNoDataMS); + + // Logged in servers + //DataStructures::Multilist serverList; + DataStructures::List serverList; + + // Forwarding requests in progress + //DataStructures::Multilist forwardingRequestList; + DataStructures::OrderedList forwardingRequestList; + + RakNet::RakString remoteLoginPassword; + + }; + +} // End namespace + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/UDPProxyServer.cpp b/src/raknet/UDPProxyServer.cpp index f999627..ce65db9 100755 --- a/src/raknet/UDPProxyServer.cpp +++ b/src/raknet/UDPProxyServer.cpp @@ -1,167 +1,167 @@ -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyServer==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#include "UDPProxyServer.h" -#include "BitStream.h" -#include "UDPProxyCommon.h" -#include "RakPeerInterface.h" -#include "MessageIdentifiers.h" - -using namespace RakNet; - -STATIC_FACTORY_DEFINITIONS(UDPProxyServer,UDPProxyServer); - -UDPProxyServer::UDPProxyServer() -{ - resultHandler=0; - socketFamily=AF_INET; -} -UDPProxyServer::~UDPProxyServer() -{ - -} -void UDPProxyServer::SetSocketFamily(unsigned short _socketFamily) -{ - socketFamily=_socketFamily; -} -void UDPProxyServer::SetResultHandler(UDPProxyServerResultHandler *rh) -{ - resultHandler=rh; -} -bool UDPProxyServer::LoginToCoordinator(RakNet::RakString password, SystemAddress coordinatorAddress) -{ - unsigned int insertionIndex; - bool objectExists; - insertionIndex=loggingInCoordinators.GetIndexFromKey(coordinatorAddress,&objectExists); - if (objectExists==true) - return false; - loggedInCoordinators.GetIndexFromKey(coordinatorAddress,&objectExists); - if (objectExists==true) - return false; - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR); - outgoingBs.Write(password); - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, coordinatorAddress, false); - loggingInCoordinators.InsertAtIndex(coordinatorAddress, insertionIndex, _FILE_AND_LINE_ ); - return true; -} -void UDPProxyServer::Update(void) -{ - udpForwarder.Update(); -} -PluginReceiveResult UDPProxyServer::OnReceive(Packet *packet) -{ - // Make sure incoming messages from from UDPProxyCoordinator - - if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) - { - bool objectExists; - - switch (packet->data[1]) - { - case ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER: - if (loggedInCoordinators.GetIndexFromKey(packet->systemAddress, &objectExists)!=(unsigned int)-1) - { - OnForwardingRequestFromCoordinatorToServer(packet); - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - break; - case ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER: - case ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER: - case ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER: - case ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER: - { - unsigned int removalIndex = loggingInCoordinators.GetIndexFromKey(packet->systemAddress, &objectExists); - if (objectExists) - { - loggingInCoordinators.RemoveAtIndex(removalIndex); - - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - RakNet::RakString password; - incomingBs.Read(password); - switch (packet->data[1]) - { - case ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER: - if (resultHandler) - resultHandler->OnNoPasswordSet(password, this); - break; - case ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER: - if (resultHandler) - resultHandler->OnWrongPassword(password, this); - break; - case ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER: - if (resultHandler) - resultHandler->OnAlreadyLoggedIn(password, this); - break; - case ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER: - // RakAssert(loggedInCoordinators.GetIndexOf(packet->systemAddress)==(unsigned int)-1); - loggedInCoordinators.Insert(packet->systemAddress, packet->systemAddress, true, _FILE_AND_LINE_); - if (resultHandler) - resultHandler->OnLoginSuccess(password, this); - break; - } - } - - - return RR_STOP_PROCESSING_AND_DEALLOCATE; - } - } - } - return RR_CONTINUE_PROCESSING; -} -void UDPProxyServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) -{ - (void) lostConnectionReason; - (void) rakNetGUID; - - loggingInCoordinators.RemoveIfExists(systemAddress); - loggedInCoordinators.RemoveIfExists(systemAddress); -} -void UDPProxyServer::OnRakPeerStartup(void) -{ - udpForwarder.Startup(); -} -void UDPProxyServer::OnRakPeerShutdown(void) -{ - udpForwarder.Shutdown(); - loggingInCoordinators.Clear(true,_FILE_AND_LINE_); - loggedInCoordinators.Clear(true,_FILE_AND_LINE_); -} -void UDPProxyServer::OnAttach(void) -{ - if (rakPeerInterface->IsActive()) - OnRakPeerStartup(); -} -void UDPProxyServer::OnDetach(void) -{ - OnRakPeerShutdown(); -} -void UDPProxyServer::OnForwardingRequestFromCoordinatorToServer(Packet *packet) -{ - SystemAddress sourceAddress, targetAddress; - RakNet::BitStream incomingBs(packet->data, packet->length, false); - incomingBs.IgnoreBytes(2); - incomingBs.Read(sourceAddress); - incomingBs.Read(targetAddress); - RakNet::TimeMS timeoutOnNoDataMS; - incomingBs.Read(timeoutOnNoDataMS); - RakAssert(timeoutOnNoDataMS > 0 && timeoutOnNoDataMS <= UDP_FORWARDER_MAXIMUM_TIMEOUT); - - unsigned short forwardingPort; - UDPForwarderResult success = udpForwarder.StartForwarding(sourceAddress, targetAddress, timeoutOnNoDataMS, 0, socketFamily, &forwardingPort, 0); - RakNet::BitStream outgoingBs; - outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); - outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR); - outgoingBs.Write(sourceAddress); - outgoingBs.Write(targetAddress); - outgoingBs.Write((unsigned char) success); - if (success==UDPFORWARDER_SUCCESS) - { - outgoingBs.Write(forwardingPort); - } - rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); -} - -#endif // _RAKNET_SUPPORT_* +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyServer==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#include "UDPProxyServer.h" +#include "BitStream.h" +#include "UDPProxyCommon.h" +#include "RakPeerInterface.h" +#include "MessageIdentifiers.h" + +using namespace RakNet; + +STATIC_FACTORY_DEFINITIONS(UDPProxyServer,UDPProxyServer); + +UDPProxyServer::UDPProxyServer() +{ + resultHandler=0; + socketFamily=AF_INET; +} +UDPProxyServer::~UDPProxyServer() +{ + +} +void UDPProxyServer::SetSocketFamily(unsigned short _socketFamily) +{ + socketFamily=_socketFamily; +} +void UDPProxyServer::SetResultHandler(UDPProxyServerResultHandler *rh) +{ + resultHandler=rh; +} +bool UDPProxyServer::LoginToCoordinator(RakNet::RakString password, SystemAddress coordinatorAddress) +{ + unsigned int insertionIndex; + bool objectExists; + insertionIndex=loggingInCoordinators.GetIndexFromKey(coordinatorAddress,&objectExists); + if (objectExists==true) + return false; + loggedInCoordinators.GetIndexFromKey(coordinatorAddress,&objectExists); + if (objectExists==true) + return false; + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_LOGIN_REQUEST_FROM_SERVER_TO_COORDINATOR); + outgoingBs.Write(password); + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, coordinatorAddress, false); + loggingInCoordinators.InsertAtIndex(coordinatorAddress, insertionIndex, _FILE_AND_LINE_ ); + return true; +} +void UDPProxyServer::Update(void) +{ + udpForwarder.Update(); +} +PluginReceiveResult UDPProxyServer::OnReceive(Packet *packet) +{ + // Make sure incoming messages from from UDPProxyCoordinator + + if (packet->data[0]==ID_UDP_PROXY_GENERAL && packet->length>1) + { + bool objectExists; + + switch (packet->data[1]) + { + case ID_UDP_PROXY_FORWARDING_REQUEST_FROM_COORDINATOR_TO_SERVER: + if (loggedInCoordinators.GetIndexFromKey(packet->systemAddress, &objectExists)!=(unsigned int)-1) + { + OnForwardingRequestFromCoordinatorToServer(packet); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + break; + case ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER: + case ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER: + case ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER: + case ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER: + { + unsigned int removalIndex = loggingInCoordinators.GetIndexFromKey(packet->systemAddress, &objectExists); + if (objectExists) + { + loggingInCoordinators.RemoveAtIndex(removalIndex); + + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + RakNet::RakString password; + incomingBs.Read(password); + switch (packet->data[1]) + { + case ID_UDP_PROXY_NO_PASSWORD_SET_FROM_COORDINATOR_TO_SERVER: + if (resultHandler) + resultHandler->OnNoPasswordSet(password, this); + break; + case ID_UDP_PROXY_WRONG_PASSWORD_FROM_COORDINATOR_TO_SERVER: + if (resultHandler) + resultHandler->OnWrongPassword(password, this); + break; + case ID_UDP_PROXY_ALREADY_LOGGED_IN_FROM_COORDINATOR_TO_SERVER: + if (resultHandler) + resultHandler->OnAlreadyLoggedIn(password, this); + break; + case ID_UDP_PROXY_LOGIN_SUCCESS_FROM_COORDINATOR_TO_SERVER: + // RakAssert(loggedInCoordinators.GetIndexOf(packet->systemAddress)==(unsigned int)-1); + loggedInCoordinators.Insert(packet->systemAddress, packet->systemAddress, true, _FILE_AND_LINE_); + if (resultHandler) + resultHandler->OnLoginSuccess(password, this); + break; + } + } + + + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + } + } + return RR_CONTINUE_PROCESSING; +} +void UDPProxyServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) +{ + (void) lostConnectionReason; + (void) rakNetGUID; + + loggingInCoordinators.RemoveIfExists(systemAddress); + loggedInCoordinators.RemoveIfExists(systemAddress); +} +void UDPProxyServer::OnRakPeerStartup(void) +{ + udpForwarder.Startup(); +} +void UDPProxyServer::OnRakPeerShutdown(void) +{ + udpForwarder.Shutdown(); + loggingInCoordinators.Clear(true,_FILE_AND_LINE_); + loggedInCoordinators.Clear(true,_FILE_AND_LINE_); +} +void UDPProxyServer::OnAttach(void) +{ + if (rakPeerInterface->IsActive()) + OnRakPeerStartup(); +} +void UDPProxyServer::OnDetach(void) +{ + OnRakPeerShutdown(); +} +void UDPProxyServer::OnForwardingRequestFromCoordinatorToServer(Packet *packet) +{ + SystemAddress sourceAddress, targetAddress; + RakNet::BitStream incomingBs(packet->data, packet->length, false); + incomingBs.IgnoreBytes(2); + incomingBs.Read(sourceAddress); + incomingBs.Read(targetAddress); + RakNet::TimeMS timeoutOnNoDataMS; + incomingBs.Read(timeoutOnNoDataMS); + RakAssert(timeoutOnNoDataMS > 0 && timeoutOnNoDataMS <= UDP_FORWARDER_MAXIMUM_TIMEOUT); + + unsigned short forwardingPort; + UDPForwarderResult success = udpForwarder.StartForwarding(sourceAddress, targetAddress, timeoutOnNoDataMS, 0, socketFamily, &forwardingPort, 0); + RakNet::BitStream outgoingBs; + outgoingBs.Write((MessageID)ID_UDP_PROXY_GENERAL); + outgoingBs.Write((MessageID)ID_UDP_PROXY_FORWARDING_REPLY_FROM_SERVER_TO_COORDINATOR); + outgoingBs.Write(sourceAddress); + outgoingBs.Write(targetAddress); + outgoingBs.Write((unsigned char) success); + if (success==UDPFORWARDER_SUCCESS) + { + outgoingBs.Write(forwardingPort); + } + rakPeerInterface->Send(&outgoingBs, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); +} + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/UDPProxyServer.h b/src/raknet/UDPProxyServer.h index d3b776e..1678e6c 100755 --- a/src/raknet/UDPProxyServer.h +++ b/src/raknet/UDPProxyServer.h @@ -1,118 +1,118 @@ -/// \file -/// \brief A RakNet plugin performing networking to communicate with UDPProxyServer. It allows UDPProxyServer to control our instance of UDPForwarder. -/// -/// This file is part of RakNet Copyright 2003 Jenkins Software LLC -/// -/// Usage of RakNet is subject to the appropriate license agreement. -/// Creative Commons Licensees are subject to the -/// license found at -/// http://creativecommons.org/licenses/by-nc/2.5/ -/// Single application licensees are subject to the license found at -/// http://www.jenkinssoftware.com/SingleApplicationLicense.html -/// Custom license users are subject to the terms therein. - -#include "NativeFeatureIncludes.h" -#if _RAKNET_SUPPORT_UDPProxyServer==1 && _RAKNET_SUPPORT_UDPForwarder==1 - -#ifndef __UDP_PROXY_SERVER_H -#define __UDP_PROXY_SERVER_H - -#include "Export.h" -#include "RakNetTypes.h" -#include "PluginInterface2.h" -#include "UDPForwarder.h" -#include "RakString.h" - -namespace RakNet -{ -class UDPProxyServer; - -/// Callback to handle results of calling UDPProxyServer::LoginToCoordinator() -/// \ingroup UDP_PROXY_GROUP -struct UDPProxyServerResultHandler -{ - UDPProxyServerResultHandler() {} - virtual ~UDPProxyServerResultHandler() {} - - /// Called when our login succeeds - /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() - /// \param[out] proxyServer The plugin calling this callback - virtual void OnLoginSuccess(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; - - /// We are already logged in. - /// This login failed, but the system is operational as if it succeeded - /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() - /// \param[out] proxyServer The plugin calling this callback - virtual void OnAlreadyLoggedIn(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; - - /// The coordinator operator forgot to call UDPProxyCoordinator::SetRemoteLoginPassword() - /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() - /// \param[out] proxyServer The plugin calling this callback - virtual void OnNoPasswordSet(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; - - /// The coordinator operator set a different password in UDPProxyCoordinator::SetRemoteLoginPassword() than what we passed - /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() - /// \param[out] proxyServer The plugin calling this callback - virtual void OnWrongPassword(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; -}; - -/// \brief UDPProxyServer to control our instance of UDPForwarder -/// \details When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa.
          -/// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin.
          -/// The UDPProxyServer connects to UDPProxyServer to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request. -/// \ingroup UDP_PROXY_GROUP -class RAK_DLL_EXPORT UDPProxyServer : public PluginInterface2 -{ -public: - // GetInstance() and DestroyInstance(instance*) - STATIC_FACTORY_DECLARATIONS(UDPProxyServer) - - UDPProxyServer(); - ~UDPProxyServer(); - - /// Sets the socket family to use, either IPV4 or IPV6 - /// \param[in] socketFamily For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. - void SetSocketFamily(unsigned short _socketFamily); - - /// Receives the results of calling LoginToCoordinator() - /// Set before calling LoginToCoordinator or you won't know what happened - /// \param[in] resultHandler - void SetResultHandler(UDPProxyServerResultHandler *rh); - - /// Before the coordinator will register the UDPProxyServer, you must login - /// \pre Must be connected to the coordinator - /// \pre Coordinator must have set a password with UDPProxyCoordinator::SetRemoteLoginPassword() - /// \returns false if already logged in, or logging in. Returns true otherwise - bool LoginToCoordinator(RakNet::RakString password, SystemAddress coordinatorAddress); - - /// Operative class that performs the forwarding - /// Exposed so you can call UDPForwarder::SetMaxForwardEntries() if you want to change away from the default - /// UDPForwarder::Startup(), UDPForwarder::Shutdown(), and UDPForwarder::Update() are called automatically by the plugin - UDPForwarder udpForwarder; - - virtual void OnAttach(void); - virtual void OnDetach(void); - - /// \internal - virtual void Update(void); - virtual PluginReceiveResult OnReceive(Packet *packet); - virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); - virtual void OnRakPeerStartup(void); - virtual void OnRakPeerShutdown(void); - -protected: - void OnForwardingRequestFromCoordinatorToServer(Packet *packet); - - DataStructures::OrderedList loggingInCoordinators; - DataStructures::OrderedList loggedInCoordinators; - - UDPProxyServerResultHandler *resultHandler; - unsigned short socketFamily; - -}; - -} // End namespace - -#endif - -#endif // _RAKNET_SUPPORT_* +/// \file +/// \brief A RakNet plugin performing networking to communicate with UDPProxyServer. It allows UDPProxyServer to control our instance of UDPForwarder. +/// +/// This file is part of RakNet Copyright 2003 Jenkins Software LLC +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. + +#include "NativeFeatureIncludes.h" +#if _RAKNET_SUPPORT_UDPProxyServer==1 && _RAKNET_SUPPORT_UDPForwarder==1 + +#ifndef __UDP_PROXY_SERVER_H +#define __UDP_PROXY_SERVER_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "PluginInterface2.h" +#include "UDPForwarder.h" +#include "RakString.h" + +namespace RakNet +{ +class UDPProxyServer; + +/// Callback to handle results of calling UDPProxyServer::LoginToCoordinator() +/// \ingroup UDP_PROXY_GROUP +struct UDPProxyServerResultHandler +{ + UDPProxyServerResultHandler() {} + virtual ~UDPProxyServerResultHandler() {} + + /// Called when our login succeeds + /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() + /// \param[out] proxyServer The plugin calling this callback + virtual void OnLoginSuccess(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; + + /// We are already logged in. + /// This login failed, but the system is operational as if it succeeded + /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() + /// \param[out] proxyServer The plugin calling this callback + virtual void OnAlreadyLoggedIn(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; + + /// The coordinator operator forgot to call UDPProxyCoordinator::SetRemoteLoginPassword() + /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() + /// \param[out] proxyServer The plugin calling this callback + virtual void OnNoPasswordSet(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; + + /// The coordinator operator set a different password in UDPProxyCoordinator::SetRemoteLoginPassword() than what we passed + /// \param[out] usedPassword The password we passed to UDPProxyServer::LoginToCoordinator() + /// \param[out] proxyServer The plugin calling this callback + virtual void OnWrongPassword(RakNet::RakString usedPassword, RakNet::UDPProxyServer *proxyServerPlugin)=0; +}; + +/// \brief UDPProxyServer to control our instance of UDPForwarder +/// \details When NAT Punchthrough fails, it is possible to use a non-NAT system to forward messages from us to the recipient, and vice-versa.
          +/// The class to forward messages is UDPForwarder, and it is triggered over the network via the UDPProxyServer plugin.
          +/// The UDPProxyServer connects to UDPProxyServer to get a list of servers running UDPProxyServer, and the coordinator will relay our forwarding request. +/// \ingroup UDP_PROXY_GROUP +class RAK_DLL_EXPORT UDPProxyServer : public PluginInterface2 +{ +public: + // GetInstance() and DestroyInstance(instance*) + STATIC_FACTORY_DECLARATIONS(UDPProxyServer) + + UDPProxyServer(); + ~UDPProxyServer(); + + /// Sets the socket family to use, either IPV4 or IPV6 + /// \param[in] socketFamily For IPV4, use AF_INET (default). For IPV6, use AF_INET6. To autoselect, use AF_UNSPEC. + void SetSocketFamily(unsigned short _socketFamily); + + /// Receives the results of calling LoginToCoordinator() + /// Set before calling LoginToCoordinator or you won't know what happened + /// \param[in] resultHandler + void SetResultHandler(UDPProxyServerResultHandler *rh); + + /// Before the coordinator will register the UDPProxyServer, you must login + /// \pre Must be connected to the coordinator + /// \pre Coordinator must have set a password with UDPProxyCoordinator::SetRemoteLoginPassword() + /// \returns false if already logged in, or logging in. Returns true otherwise + bool LoginToCoordinator(RakNet::RakString password, SystemAddress coordinatorAddress); + + /// Operative class that performs the forwarding + /// Exposed so you can call UDPForwarder::SetMaxForwardEntries() if you want to change away from the default + /// UDPForwarder::Startup(), UDPForwarder::Shutdown(), and UDPForwarder::Update() are called automatically by the plugin + UDPForwarder udpForwarder; + + virtual void OnAttach(void); + virtual void OnDetach(void); + + /// \internal + virtual void Update(void); + virtual PluginReceiveResult OnReceive(Packet *packet); + virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); + virtual void OnRakPeerStartup(void); + virtual void OnRakPeerShutdown(void); + +protected: + void OnForwardingRequestFromCoordinatorToServer(Packet *packet); + + DataStructures::OrderedList loggingInCoordinators; + DataStructures::OrderedList loggedInCoordinators; + + UDPProxyServerResultHandler *resultHandler; + unsigned short socketFamily; + +}; + +} // End namespace + +#endif + +#endif // _RAKNET_SUPPORT_* diff --git a/src/raknet/VariableDeltaSerializer.cpp b/src/raknet/VariableDeltaSerializer.cpp index 92ff24c..044819f 100755 --- a/src/raknet/VariableDeltaSerializer.cpp +++ b/src/raknet/VariableDeltaSerializer.cpp @@ -1,275 +1,275 @@ -#include "VariableDeltaSerializer.h" - -using namespace RakNet; - -VariableDeltaSerializer::VariableDeltaSerializer() {didComparisonThisTick=false;} -VariableDeltaSerializer::~VariableDeltaSerializer() {RemoveRemoteSystemVariableHistory();} - -VariableDeltaSerializer::SerializationContext::SerializationContext() {variableHistoryIdentical=0; variableHistoryUnique=0;} -VariableDeltaSerializer::SerializationContext::~SerializationContext() {} - -void VariableDeltaSerializer::OnMessageReceipt(RakNetGUID guid, uint32_t receiptId, bool messageArrived) -{ - // Module? - if (messageArrived) - FreeVarsAssociatedWithReceipt(guid, receiptId); - else - DirtyAndFreeVarsAssociatedWithReceipt(guid, receiptId); - -} - -void VariableDeltaSerializer::BeginUnreliableAckedSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream, uint32_t _sendReceipt) -{ - RakAssert(_guid!=UNASSIGNED_RAKNET_GUID); - context->anyVariablesWritten=false; - context->guid=_guid; - context->bitStream=_bitStream; - if (context->variableHistoryUnique==0) - context->variableHistoryUnique=StartVariableHistoryWrite(_guid); - context->variableHistory=context->variableHistoryUnique; - context->sendReceipt=_sendReceipt; - context->changedVariables = AllocChangedVariablesList(); - context->newSystemSend=false; - context->serializationMode=UNRELIABLE_WITH_ACK_RECEIPT; -} - -void VariableDeltaSerializer::BeginUniqueSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream) -{ - RakAssert(_guid!=UNASSIGNED_RAKNET_GUID); - context->anyVariablesWritten=false; - context->guid=_guid; - context->bitStream=_bitStream; - if (context->variableHistoryUnique==0) - context->variableHistoryUnique=StartVariableHistoryWrite(_guid); - context->variableHistory=context->variableHistoryUnique; - context->newSystemSend=false; - - context->serializationMode=RELIABLE; -} - - -void VariableDeltaSerializer::BeginIdenticalSerialize(SerializationContext *context, bool _isFirstSendToRemoteSystem, BitStream *_bitStream) -{ - context->anyVariablesWritten=false; - context->guid=UNASSIGNED_RAKNET_GUID; - context->bitStream=_bitStream; - context->serializationMode=RELIABLE; - if (context->variableHistoryIdentical==0) - context->variableHistoryIdentical=StartVariableHistoryWrite(UNASSIGNED_RAKNET_GUID); - context->variableHistory=context->variableHistoryIdentical; - context->newSystemSend=_isFirstSendToRemoteSystem; -} - -void VariableDeltaSerializer::EndSerialize(SerializationContext *context) -{ - if (context->serializationMode==UNRELIABLE_WITH_ACK_RECEIPT) - { - if (context->anyVariablesWritten==false) - { - context->bitStream->Reset(); - FreeChangedVariablesList(context->changedVariables); - return; - } - - StoreChangedVariablesList(context->variableHistory, context->changedVariables, context->sendReceipt); - } - else - { - if (context->variableHistoryIdentical) - { - if (didComparisonThisTick==false) - { - didComparisonThisTick=true; - identicalSerializationBs.Reset(); - - if (context->anyVariablesWritten==false) - { - context->bitStream->Reset(); - return; - } - - identicalSerializationBs.Write(context->bitStream); - context->bitStream->ResetReadPointer(); - } - else - { - context->bitStream->Write(&identicalSerializationBs); - identicalSerializationBs.ResetReadPointer(); - } - } - else if (context->anyVariablesWritten==false) - { - context->bitStream->Reset(); - return; - } - } -} - -void VariableDeltaSerializer::BeginDeserialize(DeserializationContext *context, BitStream *_bitStream) -{ - context->bitStream=_bitStream; -} - -void VariableDeltaSerializer::EndDeserialize(DeserializationContext *context) -{ - (void) context; -} - -void VariableDeltaSerializer::AddRemoteSystemVariableHistory(RakNetGUID guid) -{ - (void) guid; -} - -void VariableDeltaSerializer::RemoveRemoteSystemVariableHistory(RakNetGUID guid) -{ - unsigned int idx,idx2; - idx = GetVarsWrittenPerRemoteSystemListIndex(guid); - if (idx==(unsigned int)-1) - return; - - if (remoteSystemVariableHistoryList[idx]->guid==guid) - { - // Memory pool doesn't call destructor - for (idx2=0; idx2 < remoteSystemVariableHistoryList[idx]->updatedVariablesHistory.Size(); idx2++) - { - FreeChangedVariablesList(remoteSystemVariableHistoryList[idx]->updatedVariablesHistory[idx2]); - } - - delete remoteSystemVariableHistoryList[idx]; - remoteSystemVariableHistoryList.RemoveAtIndexFast(idx); - return; - } -} - -int RakNet::VariableDeltaSerializer::UpdatedVariablesListPtrComp( const uint32_t &key, ChangedVariablesList* const &data ) -{ - if (keysendReceipt) - return -1; - if (key==data->sendReceipt) - return 0; - return 1; -} - -void VariableDeltaSerializer::FreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId) -{ - unsigned int idx, idx2; - idx = GetVarsWrittenPerRemoteSystemListIndex(guid); - if (idx==(unsigned int)-1) - return; - - RemoteSystemVariableHistory* vprs = remoteSystemVariableHistoryList[idx]; - bool objectExists; - idx2=vprs->updatedVariablesHistory.GetIndexFromKey(receiptId,&objectExists); - if (objectExists) - { - // Free this history node - FreeChangedVariablesList(vprs->updatedVariablesHistory[idx2]); - vprs->updatedVariablesHistory.RemoveAtIndex(idx2); - } -} - -void VariableDeltaSerializer::DirtyAndFreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId) -{ - unsigned int idx, idx2; - idx = GetVarsWrittenPerRemoteSystemListIndex(guid); - if (idx==(unsigned int)-1) - return; - - RemoteSystemVariableHistory* vprs = remoteSystemVariableHistoryList[idx]; - bool objectExists; - idx2=vprs->updatedVariablesHistory.GetIndexFromKey(receiptId,&objectExists); - if (objectExists) - { - // 'Dirty' all variables sent this update, meaning they will be resent the next time Serialize() is called - vprs->variableListDeltaTracker.FlagDirtyFromBitArray(vprs->updatedVariablesHistory[idx2]->bitField); - - // Free this history node - FreeChangedVariablesList(vprs->updatedVariablesHistory[idx2]); - vprs->updatedVariablesHistory.RemoveAtIndex(idx2); - } -} -unsigned int VariableDeltaSerializer::GetVarsWrittenPerRemoteSystemListIndex(RakNetGUID guid) -{ - unsigned int idx; - for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) - { - if (remoteSystemVariableHistoryList[idx]->guid==guid) - return idx; - } - return (unsigned int) -1; -} -void VariableDeltaSerializer::RemoveRemoteSystemVariableHistory(void) -{ - unsigned int idx,idx2; - for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) - { - for (idx2=0; idx2 < remoteSystemVariableHistoryList[idx]->updatedVariablesHistory.Size(); idx2++) - { - FreeChangedVariablesList(remoteSystemVariableHistoryList[idx]->updatedVariablesHistory[idx2]); - } - - delete remoteSystemVariableHistoryList[idx]; - } - remoteSystemVariableHistoryList.Clear(false,_FILE_AND_LINE_); -} - -VariableDeltaSerializer::RemoteSystemVariableHistory* VariableDeltaSerializer::GetRemoteSystemVariableHistory(RakNetGUID guid) -{ - unsigned int rshli = GetRemoteSystemHistoryListIndex(guid); - return remoteSystemVariableHistoryList[rshli]; -} - -VariableDeltaSerializer::ChangedVariablesList *VariableDeltaSerializer::AllocChangedVariablesList(void) -{ - VariableDeltaSerializer::ChangedVariablesList *p = updatedVariablesMemoryPool.Allocate(_FILE_AND_LINE_); - p->bitWriteIndex=0; - p->bitField[0]=0; - return p; -} -void VariableDeltaSerializer::FreeChangedVariablesList(ChangedVariablesList *changedVariables) -{ - updatedVariablesMemoryPool.Release(changedVariables, _FILE_AND_LINE_); -} -void VariableDeltaSerializer::StoreChangedVariablesList(RemoteSystemVariableHistory *variableHistory, ChangedVariablesList *changedVariables, uint32_t sendReceipt) -{ - changedVariables->sendReceipt=sendReceipt; - variableHistory->updatedVariablesHistory.Insert(changedVariables->sendReceipt,changedVariables,true,_FILE_AND_LINE_); -} - -VariableDeltaSerializer::RemoteSystemVariableHistory *VariableDeltaSerializer::StartVariableHistoryWrite(RakNetGUID guid) -{ - RemoteSystemVariableHistory *variableHistory; - - unsigned int rshli = GetRemoteSystemHistoryListIndex(guid); - if (rshli==(unsigned int) -1) - { - variableHistory = new RemoteSystemVariableHistory; - variableHistory->guid=guid; - remoteSystemVariableHistoryList.Push(variableHistory,_FILE_AND_LINE_); - } - else - { - variableHistory=remoteSystemVariableHistoryList[rshli]; - } - - variableHistory->variableListDeltaTracker.StartWrite(); - return variableHistory; -} -unsigned int VariableDeltaSerializer::GetRemoteSystemHistoryListIndex(RakNetGUID guid) -{ - // Find the variable tracker for the target system - unsigned int idx; - for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) - { - if (remoteSystemVariableHistoryList[idx]->guid==guid) - { - return idx; - } - } - return (unsigned int) -1; -} - -void VariableDeltaSerializer::OnPreSerializeTick(void) -{ - didComparisonThisTick=false; -} +#include "VariableDeltaSerializer.h" + +using namespace RakNet; + +VariableDeltaSerializer::VariableDeltaSerializer() {didComparisonThisTick=false;} +VariableDeltaSerializer::~VariableDeltaSerializer() {RemoveRemoteSystemVariableHistory();} + +VariableDeltaSerializer::SerializationContext::SerializationContext() {variableHistoryIdentical=0; variableHistoryUnique=0;} +VariableDeltaSerializer::SerializationContext::~SerializationContext() {} + +void VariableDeltaSerializer::OnMessageReceipt(RakNetGUID guid, uint32_t receiptId, bool messageArrived) +{ + // Module? + if (messageArrived) + FreeVarsAssociatedWithReceipt(guid, receiptId); + else + DirtyAndFreeVarsAssociatedWithReceipt(guid, receiptId); + +} + +void VariableDeltaSerializer::BeginUnreliableAckedSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream, uint32_t _sendReceipt) +{ + RakAssert(_guid!=UNASSIGNED_RAKNET_GUID); + context->anyVariablesWritten=false; + context->guid=_guid; + context->bitStream=_bitStream; + if (context->variableHistoryUnique==0) + context->variableHistoryUnique=StartVariableHistoryWrite(_guid); + context->variableHistory=context->variableHistoryUnique; + context->sendReceipt=_sendReceipt; + context->changedVariables = AllocChangedVariablesList(); + context->newSystemSend=false; + context->serializationMode=UNRELIABLE_WITH_ACK_RECEIPT; +} + +void VariableDeltaSerializer::BeginUniqueSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream) +{ + RakAssert(_guid!=UNASSIGNED_RAKNET_GUID); + context->anyVariablesWritten=false; + context->guid=_guid; + context->bitStream=_bitStream; + if (context->variableHistoryUnique==0) + context->variableHistoryUnique=StartVariableHistoryWrite(_guid); + context->variableHistory=context->variableHistoryUnique; + context->newSystemSend=false; + + context->serializationMode=RELIABLE; +} + + +void VariableDeltaSerializer::BeginIdenticalSerialize(SerializationContext *context, bool _isFirstSendToRemoteSystem, BitStream *_bitStream) +{ + context->anyVariablesWritten=false; + context->guid=UNASSIGNED_RAKNET_GUID; + context->bitStream=_bitStream; + context->serializationMode=RELIABLE; + if (context->variableHistoryIdentical==0) + context->variableHistoryIdentical=StartVariableHistoryWrite(UNASSIGNED_RAKNET_GUID); + context->variableHistory=context->variableHistoryIdentical; + context->newSystemSend=_isFirstSendToRemoteSystem; +} + +void VariableDeltaSerializer::EndSerialize(SerializationContext *context) +{ + if (context->serializationMode==UNRELIABLE_WITH_ACK_RECEIPT) + { + if (context->anyVariablesWritten==false) + { + context->bitStream->Reset(); + FreeChangedVariablesList(context->changedVariables); + return; + } + + StoreChangedVariablesList(context->variableHistory, context->changedVariables, context->sendReceipt); + } + else + { + if (context->variableHistoryIdentical) + { + if (didComparisonThisTick==false) + { + didComparisonThisTick=true; + identicalSerializationBs.Reset(); + + if (context->anyVariablesWritten==false) + { + context->bitStream->Reset(); + return; + } + + identicalSerializationBs.Write(context->bitStream); + context->bitStream->ResetReadPointer(); + } + else + { + context->bitStream->Write(&identicalSerializationBs); + identicalSerializationBs.ResetReadPointer(); + } + } + else if (context->anyVariablesWritten==false) + { + context->bitStream->Reset(); + return; + } + } +} + +void VariableDeltaSerializer::BeginDeserialize(DeserializationContext *context, BitStream *_bitStream) +{ + context->bitStream=_bitStream; +} + +void VariableDeltaSerializer::EndDeserialize(DeserializationContext *context) +{ + (void) context; +} + +void VariableDeltaSerializer::AddRemoteSystemVariableHistory(RakNetGUID guid) +{ + (void) guid; +} + +void VariableDeltaSerializer::RemoveRemoteSystemVariableHistory(RakNetGUID guid) +{ + unsigned int idx,idx2; + idx = GetVarsWrittenPerRemoteSystemListIndex(guid); + if (idx==(unsigned int)-1) + return; + + if (remoteSystemVariableHistoryList[idx]->guid==guid) + { + // Memory pool doesn't call destructor + for (idx2=0; idx2 < remoteSystemVariableHistoryList[idx]->updatedVariablesHistory.Size(); idx2++) + { + FreeChangedVariablesList(remoteSystemVariableHistoryList[idx]->updatedVariablesHistory[idx2]); + } + + delete remoteSystemVariableHistoryList[idx]; + remoteSystemVariableHistoryList.RemoveAtIndexFast(idx); + return; + } +} + +int RakNet::VariableDeltaSerializer::UpdatedVariablesListPtrComp( const uint32_t &key, ChangedVariablesList* const &data ) +{ + if (keysendReceipt) + return -1; + if (key==data->sendReceipt) + return 0; + return 1; +} + +void VariableDeltaSerializer::FreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId) +{ + unsigned int idx, idx2; + idx = GetVarsWrittenPerRemoteSystemListIndex(guid); + if (idx==(unsigned int)-1) + return; + + RemoteSystemVariableHistory* vprs = remoteSystemVariableHistoryList[idx]; + bool objectExists; + idx2=vprs->updatedVariablesHistory.GetIndexFromKey(receiptId,&objectExists); + if (objectExists) + { + // Free this history node + FreeChangedVariablesList(vprs->updatedVariablesHistory[idx2]); + vprs->updatedVariablesHistory.RemoveAtIndex(idx2); + } +} + +void VariableDeltaSerializer::DirtyAndFreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId) +{ + unsigned int idx, idx2; + idx = GetVarsWrittenPerRemoteSystemListIndex(guid); + if (idx==(unsigned int)-1) + return; + + RemoteSystemVariableHistory* vprs = remoteSystemVariableHistoryList[idx]; + bool objectExists; + idx2=vprs->updatedVariablesHistory.GetIndexFromKey(receiptId,&objectExists); + if (objectExists) + { + // 'Dirty' all variables sent this update, meaning they will be resent the next time Serialize() is called + vprs->variableListDeltaTracker.FlagDirtyFromBitArray(vprs->updatedVariablesHistory[idx2]->bitField); + + // Free this history node + FreeChangedVariablesList(vprs->updatedVariablesHistory[idx2]); + vprs->updatedVariablesHistory.RemoveAtIndex(idx2); + } +} +unsigned int VariableDeltaSerializer::GetVarsWrittenPerRemoteSystemListIndex(RakNetGUID guid) +{ + unsigned int idx; + for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) + { + if (remoteSystemVariableHistoryList[idx]->guid==guid) + return idx; + } + return (unsigned int) -1; +} +void VariableDeltaSerializer::RemoveRemoteSystemVariableHistory(void) +{ + unsigned int idx,idx2; + for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) + { + for (idx2=0; idx2 < remoteSystemVariableHistoryList[idx]->updatedVariablesHistory.Size(); idx2++) + { + FreeChangedVariablesList(remoteSystemVariableHistoryList[idx]->updatedVariablesHistory[idx2]); + } + + delete remoteSystemVariableHistoryList[idx]; + } + remoteSystemVariableHistoryList.Clear(false,_FILE_AND_LINE_); +} + +VariableDeltaSerializer::RemoteSystemVariableHistory* VariableDeltaSerializer::GetRemoteSystemVariableHistory(RakNetGUID guid) +{ + unsigned int rshli = GetRemoteSystemHistoryListIndex(guid); + return remoteSystemVariableHistoryList[rshli]; +} + +VariableDeltaSerializer::ChangedVariablesList *VariableDeltaSerializer::AllocChangedVariablesList(void) +{ + VariableDeltaSerializer::ChangedVariablesList *p = updatedVariablesMemoryPool.Allocate(_FILE_AND_LINE_); + p->bitWriteIndex=0; + p->bitField[0]=0; + return p; +} +void VariableDeltaSerializer::FreeChangedVariablesList(ChangedVariablesList *changedVariables) +{ + updatedVariablesMemoryPool.Release(changedVariables, _FILE_AND_LINE_); +} +void VariableDeltaSerializer::StoreChangedVariablesList(RemoteSystemVariableHistory *variableHistory, ChangedVariablesList *changedVariables, uint32_t sendReceipt) +{ + changedVariables->sendReceipt=sendReceipt; + variableHistory->updatedVariablesHistory.Insert(changedVariables->sendReceipt,changedVariables,true,_FILE_AND_LINE_); +} + +VariableDeltaSerializer::RemoteSystemVariableHistory *VariableDeltaSerializer::StartVariableHistoryWrite(RakNetGUID guid) +{ + RemoteSystemVariableHistory *variableHistory; + + unsigned int rshli = GetRemoteSystemHistoryListIndex(guid); + if (rshli==(unsigned int) -1) + { + variableHistory = new RemoteSystemVariableHistory; + variableHistory->guid=guid; + remoteSystemVariableHistoryList.Push(variableHistory,_FILE_AND_LINE_); + } + else + { + variableHistory=remoteSystemVariableHistoryList[rshli]; + } + + variableHistory->variableListDeltaTracker.StartWrite(); + return variableHistory; +} +unsigned int VariableDeltaSerializer::GetRemoteSystemHistoryListIndex(RakNetGUID guid) +{ + // Find the variable tracker for the target system + unsigned int idx; + for (idx=0; idx < remoteSystemVariableHistoryList.Size(); idx++) + { + if (remoteSystemVariableHistoryList[idx]->guid==guid) + { + return idx; + } + } + return (unsigned int) -1; +} + +void VariableDeltaSerializer::OnPreSerializeTick(void) +{ + didComparisonThisTick=false; +} diff --git a/src/raknet/VariableDeltaSerializer.h b/src/raknet/VariableDeltaSerializer.h index 257426d..a05653a 100755 --- a/src/raknet/VariableDeltaSerializer.h +++ b/src/raknet/VariableDeltaSerializer.h @@ -1,257 +1,257 @@ -#ifndef __VARIABLE_DELTA_SERIALIZER_H -#define __VARIABLE_DELTA_SERIALIZER_H - -#include "VariableListDeltaTracker.h" -#include "DS_MemoryPool.h" -#include "NativeTypes.h" -#include "BitStream.h" -#include "PacketPriority.h" -#include "DS_OrderedList.h" - -namespace RakNet -{ - -/// \brief Class to compare memory values of variables in a current state to a prior state -/// Results of the comparisons will be written to a bitStream, such that only changed variables get written
          -/// Can be used with ReplicaManager3 to Serialize a Replica3 per-variable, rather than comparing the entire object against itself
          -/// Usage:
          -///
          -/// 1. Call BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(). In the case of Replica3, this would be in the Serialize() call
          -/// 2. For each variable of the type in step 1, call Serialize(). The same variables must be serialized every tick()
          -/// 3. Call EndSerialize()
          -/// 4. Repeat step 1 for each of the other categories of how to send varaibles -/// -/// On the receiver: -/// -/// 1. Call BeginDeserialize(). In the case of Replica3, this would be in the Deserialize() call -/// 2. Call DeserializeVariable() for each variable, in the same order as was Serialized() -/// 3. Call EndSerialize() -/// \sa The ReplicaManager3 sample -class VariableDeltaSerializer -{ -protected: - struct RemoteSystemVariableHistory; - struct ChangedVariablesList; - -public: - VariableDeltaSerializer(); - ~VariableDeltaSerializer(); - - struct SerializationContext - { - SerializationContext(); - ~SerializationContext(); - - RakNetGUID guid; - BitStream *bitStream; - uint32_t rakPeerSendReceipt; - RemoteSystemVariableHistory *variableHistory; - RemoteSystemVariableHistory *variableHistoryIdentical; - RemoteSystemVariableHistory *variableHistoryUnique; - ChangedVariablesList *changedVariables; - uint32_t sendReceipt; - PacketReliability serializationMode; - bool anyVariablesWritten; - bool newSystemSend; // Force send all, do not record - }; - - struct DeserializationContext - { - BitStream *bitStream; - }; - - /// \brief Call before doing one or more SerializeVariable calls when the data will be sent UNRELIABLE_WITH_ACK_RECEIPT - /// The last value of each variable will be saved per remote system. Additionally, a history of \a _sendReceipts is stored to determine what to resend on packetloss. - /// When variables are lost, they will be flagged dirty and always resent to the system that lost it - /// Disadvantages: Every variable for every remote system is copied internally, in addition to a history list of what variables changed for which \a _sendReceipt. Very memory and CPU intensive for multiple connections. - /// Advantages: When data needs to be resent by RakNet, RakNet can only resend the value it currently has. This allows the application to control the resend, sending the most recent value of the variable. The end result is that bandwidth is used more efficiently because old data is never sent. - /// \pre Upon getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED call OnMessageReceipt() - /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections - /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. - /// \param[in] _guid Which system we are sending to - /// \param[in] _bitSteam Which bitStream to write to - /// \param[in] _sendReceipt Returned from RakPeer::IncrementNextSendReceipt() and passed to the Send() or SendLists() function. Identifies this update for ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED - void BeginUnreliableAckedSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream, uint32_t _sendReceipt); - - /// \brief Call before doing one or more SerializeVariable calls for data that may be sent differently to every remote system (such as an invisibility flag that only teammates can see) - /// The last value of each variable will be saved per remote system. - /// Unlike BeginUnreliableAckedSerialize(), send receipts are not necessary - /// Disadvantages: Every variable for every remote system is copied internally. Very memory and CPU intensive for multiple connections. - /// Advantages: When data is sent differently depending on the recipient, this system can make things easier to use and is as efficient as it can be. - /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections - /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. - /// \param[in] _guid Which system we are sending to - /// \param[in] _bitSteam Which bitStream to write to - void BeginUniqueSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream); - - /// \brief Call before doing one or more SerializeVariable calls for data that is sent with the same value to every remote system (such as health, position, etc.) - /// This is the most common type of serialization, and also the most efficient - /// Disadvantages: A copy of every variable still needs to be held, although only once - /// Advantages: After the first serialization, the last serialized bitStream will be used for subsequent sends - /// \pre Call OnPreSerializeTick() before doing any calls to BeginIdenticalSerialize() for each of your objects, once per game tick - /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. - /// \param[in] _isFirstSerializeToThisSystem Pass true if this is the first time ever serializing to this system (the initial download). This way all variables will be written, rather than checking against prior sent values. - /// \param[in] _bitSteam Which bitStream to write to - void BeginIdenticalSerialize(SerializationContext *context, bool _isFirstSerializeToThisSystem, BitStream *_bitStream); - - /// \brief Call after BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(), then after calling SerializeVariable() one or more times - /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() - void EndSerialize(SerializationContext *context); - - /// \brief Call when you receive the BitStream written by SerializeVariable(), before calling DeserializeVariable() - /// \param[in] context Holds the context of this group of deserialize calls. This can be a stack object just passed to the function. - /// \param[in] _bitStream Pass the bitStream originally passed to and written to by serialize calls - void BeginDeserialize(DeserializationContext *context, BitStream *_bitStream); - - /// \param[in] context Same context pointer passed to BeginDeserialize() - void EndDeserialize(DeserializationContext *context); - - /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped - /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events - /// \param[in] _guid Which system we are sending to - void AddRemoteSystemVariableHistory(RakNetGUID guid); - - /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped - /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events - /// \param[in] _guid Which system we are sending to - void RemoveRemoteSystemVariableHistory(RakNetGUID guid); - - /// BeginIdenticalSerialize() requires knowledge of when serialization has started for an object across multiple systems - /// This way it can setup the flag to do new comparisons against the last sent values, rather than just resending the last sent bitStream - /// For Replica3, overload and call this from Replica3::OnUserReplicaPreSerializeTick() - void OnPreSerializeTick(void); - - /// Call when getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED for a particular system - /// Example: - /// - /// uint32_t msgNumber; - /// memcpy(&msgNumber, packet->data+1, 4); - /// DataStructures::List replicaListOut; - /// replicaManager.GetReplicasCreatedByMe(replicaListOut); - /// unsigned int idx; - /// for (idx=0; idx < replicaListOut.GetSize(); idx++) - /// { - /// ((SampleReplica*)replicaListOut[idx])->NotifyReplicaOfMessageDeliveryStatus(packet->guid,msgNumber, packet->data[0]==ID_SND_RECEIPT_ACKED); - /// } - /// - /// \param[in] guid Which system we are sending to - /// \param[in] receiptId Encoded in bytes 1-4 inclusive of ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED - /// \param[in] messageArrived True for ID_SND_RECEIPT_ACKED, false otherwise - void OnMessageReceipt(RakNetGUID guid, uint32_t receiptId, bool messageArrived); - - /// Call to Serialize a variable - /// Will write to the bitSteam passed to \a context true, variableValue if the variable has changed or has never been written. Otherwise will write false. - /// \pre You have called BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() - /// \pre Will also require calling OnPreSerializeTick() if using BeginIdenticalSerialize() - /// \note Be sure to call EndSerialize() after finishing all serializations - /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() - /// \param[in] variable A variable to write to the bitStream passed to \a context - template - void SerializeVariable(SerializationContext *context, const VarType &variable) - { - if (context->newSystemSend) - { - if (context->variableHistory->variableListDeltaTracker.IsPastEndOfList()==false) - { - // previously sent data to another system - context->bitStream->Write(true); - context->bitStream->Write(variable); - context->anyVariablesWritten=true; - } - else - { - // never sent data to another system - context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); - context->anyVariablesWritten=true; - } - } - else if (context->serializationMode==UNRELIABLE_WITH_ACK_RECEIPT) - { - context->anyVariablesWritten|= - context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream, context->changedVariables->bitField, context->changedVariables->bitWriteIndex++); - } - else - { - if (context->variableHistoryIdentical) - { - // Identical serialization to a number of systems - if (didComparisonThisTick==false) - context->anyVariablesWritten|= - context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); - // Else bitstream is written to at the end - } - else - { - // Per-system serialization - context->anyVariablesWritten|= - context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); - } - } - } - - /// Call to deserialize into a variable - /// \pre You have called BeginDeserialize() - /// \note Be sure to call EndDeserialize() after finishing all deserializations - /// \param[in] context Same context pointer passed to BeginDeserialize() - /// \param[in] variable A variable to write to the bitStream passed to \a context - template - bool DeserializeVariable(DeserializationContext *context, VarType &variable) - { - return VariableListDeltaTracker::ReadVarFromBitstream(variable, context->bitStream); - } - - - -protected: - - // For a given send receipt from RakPeer::Send() track which variables we updated - // That way if that send does not arrive (ID_SND_RECEIPT_LOSS) we can mark those variables as dirty to resend them with current values - struct ChangedVariablesList - { - uint32_t sendReceipt; - unsigned short bitWriteIndex; - unsigned char bitField[56]; - }; - - // static int Replica2ObjectComp( const uint32_t &key, ChangedVariablesList* const &data ); - - static int UpdatedVariablesListPtrComp( const uint32_t &key, ChangedVariablesList* const &data ); - - // For each remote system, track the last values of variables we sent to them, and the history of what values changed per call to Send() - // Every serialize if a variable changes from its last value, send it out again - // Also if a send does not arrive (ID_SND_RECEIPT_LOSS) we use updatedVariablesHistory to mark those variables as dirty, to resend them unreliably with the current values - struct RemoteSystemVariableHistory - { - RakNetGUID guid; - VariableListDeltaTracker variableListDeltaTracker; - DataStructures::OrderedList updatedVariablesHistory; - }; - /// A list of RemoteSystemVariableHistory indexed by guid, one per connection that we serialize to - /// List is added to when SerializeConstruction is called, and removed from when SerializeDestruction is called, or when a given connection is dropped - DataStructures::List remoteSystemVariableHistoryList; - - // Because the ChangedVariablesList is created every serialize and destroyed every receipt I use a pool to avoid fragmentation - DataStructures::MemoryPool updatedVariablesMemoryPool; - - bool didComparisonThisTick; - RakNet::BitStream identicalSerializationBs; - - void FreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId); - void DirtyAndFreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId); - unsigned int GetVarsWrittenPerRemoteSystemListIndex(RakNetGUID guid); - void RemoveRemoteSystemVariableHistory(void); - - RemoteSystemVariableHistory* GetRemoteSystemVariableHistory(RakNetGUID guid); - - ChangedVariablesList *AllocChangedVariablesList(void); - void FreeChangedVariablesList(ChangedVariablesList *changedVariables); - void StoreChangedVariablesList(RemoteSystemVariableHistory *variableHistory, ChangedVariablesList *changedVariables, uint32_t sendReceipt); - - RemoteSystemVariableHistory *StartVariableHistoryWrite(RakNetGUID guid); - unsigned int GetRemoteSystemHistoryListIndex(RakNetGUID guid); - -}; - -} - -#endif +#ifndef __VARIABLE_DELTA_SERIALIZER_H +#define __VARIABLE_DELTA_SERIALIZER_H + +#include "VariableListDeltaTracker.h" +#include "DS_MemoryPool.h" +#include "NativeTypes.h" +#include "BitStream.h" +#include "PacketPriority.h" +#include "DS_OrderedList.h" + +namespace RakNet +{ + +/// \brief Class to compare memory values of variables in a current state to a prior state +/// Results of the comparisons will be written to a bitStream, such that only changed variables get written
          +/// Can be used with ReplicaManager3 to Serialize a Replica3 per-variable, rather than comparing the entire object against itself
          +/// Usage:
          +///
          +/// 1. Call BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(). In the case of Replica3, this would be in the Serialize() call
          +/// 2. For each variable of the type in step 1, call Serialize(). The same variables must be serialized every tick()
          +/// 3. Call EndSerialize()
          +/// 4. Repeat step 1 for each of the other categories of how to send varaibles +/// +/// On the receiver: +/// +/// 1. Call BeginDeserialize(). In the case of Replica3, this would be in the Deserialize() call +/// 2. Call DeserializeVariable() for each variable, in the same order as was Serialized() +/// 3. Call EndSerialize() +/// \sa The ReplicaManager3 sample +class VariableDeltaSerializer +{ +protected: + struct RemoteSystemVariableHistory; + struct ChangedVariablesList; + +public: + VariableDeltaSerializer(); + ~VariableDeltaSerializer(); + + struct SerializationContext + { + SerializationContext(); + ~SerializationContext(); + + RakNetGUID guid; + BitStream *bitStream; + uint32_t rakPeerSendReceipt; + RemoteSystemVariableHistory *variableHistory; + RemoteSystemVariableHistory *variableHistoryIdentical; + RemoteSystemVariableHistory *variableHistoryUnique; + ChangedVariablesList *changedVariables; + uint32_t sendReceipt; + PacketReliability serializationMode; + bool anyVariablesWritten; + bool newSystemSend; // Force send all, do not record + }; + + struct DeserializationContext + { + BitStream *bitStream; + }; + + /// \brief Call before doing one or more SerializeVariable calls when the data will be sent UNRELIABLE_WITH_ACK_RECEIPT + /// The last value of each variable will be saved per remote system. Additionally, a history of \a _sendReceipts is stored to determine what to resend on packetloss. + /// When variables are lost, they will be flagged dirty and always resent to the system that lost it + /// Disadvantages: Every variable for every remote system is copied internally, in addition to a history list of what variables changed for which \a _sendReceipt. Very memory and CPU intensive for multiple connections. + /// Advantages: When data needs to be resent by RakNet, RakNet can only resend the value it currently has. This allows the application to control the resend, sending the most recent value of the variable. The end result is that bandwidth is used more efficiently because old data is never sent. + /// \pre Upon getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED call OnMessageReceipt() + /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections + /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. + /// \param[in] _guid Which system we are sending to + /// \param[in] _bitSteam Which bitStream to write to + /// \param[in] _sendReceipt Returned from RakPeer::IncrementNextSendReceipt() and passed to the Send() or SendLists() function. Identifies this update for ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED + void BeginUnreliableAckedSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream, uint32_t _sendReceipt); + + /// \brief Call before doing one or more SerializeVariable calls for data that may be sent differently to every remote system (such as an invisibility flag that only teammates can see) + /// The last value of each variable will be saved per remote system. + /// Unlike BeginUnreliableAckedSerialize(), send receipts are not necessary + /// Disadvantages: Every variable for every remote system is copied internally. Very memory and CPU intensive for multiple connections. + /// Advantages: When data is sent differently depending on the recipient, this system can make things easier to use and is as efficient as it can be. + /// \pre AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() must be called for new and lost connections + /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. + /// \param[in] _guid Which system we are sending to + /// \param[in] _bitSteam Which bitStream to write to + void BeginUniqueSerialize(SerializationContext *context, RakNetGUID _guid, BitStream *_bitStream); + + /// \brief Call before doing one or more SerializeVariable calls for data that is sent with the same value to every remote system (such as health, position, etc.) + /// This is the most common type of serialization, and also the most efficient + /// Disadvantages: A copy of every variable still needs to be held, although only once + /// Advantages: After the first serialization, the last serialized bitStream will be used for subsequent sends + /// \pre Call OnPreSerializeTick() before doing any calls to BeginIdenticalSerialize() for each of your objects, once per game tick + /// \param[in] context Holds the context of this group of serialize calls. This can be a stack object just passed to the function. + /// \param[in] _isFirstSerializeToThisSystem Pass true if this is the first time ever serializing to this system (the initial download). This way all variables will be written, rather than checking against prior sent values. + /// \param[in] _bitSteam Which bitStream to write to + void BeginIdenticalSerialize(SerializationContext *context, bool _isFirstSerializeToThisSystem, BitStream *_bitStream); + + /// \brief Call after BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize(), then after calling SerializeVariable() one or more times + /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() + void EndSerialize(SerializationContext *context); + + /// \brief Call when you receive the BitStream written by SerializeVariable(), before calling DeserializeVariable() + /// \param[in] context Holds the context of this group of deserialize calls. This can be a stack object just passed to the function. + /// \param[in] _bitStream Pass the bitStream originally passed to and written to by serialize calls + void BeginDeserialize(DeserializationContext *context, BitStream *_bitStream); + + /// \param[in] context Same context pointer passed to BeginDeserialize() + void EndDeserialize(DeserializationContext *context); + + /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped + /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events + /// \param[in] _guid Which system we are sending to + void AddRemoteSystemVariableHistory(RakNetGUID guid); + + /// BeginUnreliableAckedSerialize() and BeginUniqueSerialize() require knowledge of when connections are added and dropped + /// Call AddRemoteSystemVariableHistory() and RemoveRemoteSystemVariableHistory() to notify the system of these events + /// \param[in] _guid Which system we are sending to + void RemoveRemoteSystemVariableHistory(RakNetGUID guid); + + /// BeginIdenticalSerialize() requires knowledge of when serialization has started for an object across multiple systems + /// This way it can setup the flag to do new comparisons against the last sent values, rather than just resending the last sent bitStream + /// For Replica3, overload and call this from Replica3::OnUserReplicaPreSerializeTick() + void OnPreSerializeTick(void); + + /// Call when getting ID_SND_RECEIPT_LOSS or ID_SND_RECEIPT_ACKED for a particular system + /// Example: + /// + /// uint32_t msgNumber; + /// memcpy(&msgNumber, packet->data+1, 4); + /// DataStructures::List replicaListOut; + /// replicaManager.GetReplicasCreatedByMe(replicaListOut); + /// unsigned int idx; + /// for (idx=0; idx < replicaListOut.GetSize(); idx++) + /// { + /// ((SampleReplica*)replicaListOut[idx])->NotifyReplicaOfMessageDeliveryStatus(packet->guid,msgNumber, packet->data[0]==ID_SND_RECEIPT_ACKED); + /// } + /// + /// \param[in] guid Which system we are sending to + /// \param[in] receiptId Encoded in bytes 1-4 inclusive of ID_SND_RECEIPT_LOSS and ID_SND_RECEIPT_ACKED + /// \param[in] messageArrived True for ID_SND_RECEIPT_ACKED, false otherwise + void OnMessageReceipt(RakNetGUID guid, uint32_t receiptId, bool messageArrived); + + /// Call to Serialize a variable + /// Will write to the bitSteam passed to \a context true, variableValue if the variable has changed or has never been written. Otherwise will write false. + /// \pre You have called BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() + /// \pre Will also require calling OnPreSerializeTick() if using BeginIdenticalSerialize() + /// \note Be sure to call EndSerialize() after finishing all serializations + /// \param[in] context Same context pointer passed to BeginUnreliableAckedSerialize(), BeginUniqueSerialize(), or BeginIdenticalSerialize() + /// \param[in] variable A variable to write to the bitStream passed to \a context + template + void SerializeVariable(SerializationContext *context, const VarType &variable) + { + if (context->newSystemSend) + { + if (context->variableHistory->variableListDeltaTracker.IsPastEndOfList()==false) + { + // previously sent data to another system + context->bitStream->Write(true); + context->bitStream->Write(variable); + context->anyVariablesWritten=true; + } + else + { + // never sent data to another system + context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); + context->anyVariablesWritten=true; + } + } + else if (context->serializationMode==UNRELIABLE_WITH_ACK_RECEIPT) + { + context->anyVariablesWritten|= + context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream, context->changedVariables->bitField, context->changedVariables->bitWriteIndex++); + } + else + { + if (context->variableHistoryIdentical) + { + // Identical serialization to a number of systems + if (didComparisonThisTick==false) + context->anyVariablesWritten|= + context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); + // Else bitstream is written to at the end + } + else + { + // Per-system serialization + context->anyVariablesWritten|= + context->variableHistory->variableListDeltaTracker.WriteVarToBitstream(variable, context->bitStream); + } + } + } + + /// Call to deserialize into a variable + /// \pre You have called BeginDeserialize() + /// \note Be sure to call EndDeserialize() after finishing all deserializations + /// \param[in] context Same context pointer passed to BeginDeserialize() + /// \param[in] variable A variable to write to the bitStream passed to \a context + template + bool DeserializeVariable(DeserializationContext *context, VarType &variable) + { + return VariableListDeltaTracker::ReadVarFromBitstream(variable, context->bitStream); + } + + + +protected: + + // For a given send receipt from RakPeer::Send() track which variables we updated + // That way if that send does not arrive (ID_SND_RECEIPT_LOSS) we can mark those variables as dirty to resend them with current values + struct ChangedVariablesList + { + uint32_t sendReceipt; + unsigned short bitWriteIndex; + unsigned char bitField[56]; + }; + + // static int Replica2ObjectComp( const uint32_t &key, ChangedVariablesList* const &data ); + + static int UpdatedVariablesListPtrComp( const uint32_t &key, ChangedVariablesList* const &data ); + + // For each remote system, track the last values of variables we sent to them, and the history of what values changed per call to Send() + // Every serialize if a variable changes from its last value, send it out again + // Also if a send does not arrive (ID_SND_RECEIPT_LOSS) we use updatedVariablesHistory to mark those variables as dirty, to resend them unreliably with the current values + struct RemoteSystemVariableHistory + { + RakNetGUID guid; + VariableListDeltaTracker variableListDeltaTracker; + DataStructures::OrderedList updatedVariablesHistory; + }; + /// A list of RemoteSystemVariableHistory indexed by guid, one per connection that we serialize to + /// List is added to when SerializeConstruction is called, and removed from when SerializeDestruction is called, or when a given connection is dropped + DataStructures::List remoteSystemVariableHistoryList; + + // Because the ChangedVariablesList is created every serialize and destroyed every receipt I use a pool to avoid fragmentation + DataStructures::MemoryPool updatedVariablesMemoryPool; + + bool didComparisonThisTick; + RakNet::BitStream identicalSerializationBs; + + void FreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId); + void DirtyAndFreeVarsAssociatedWithReceipt(RakNetGUID guid, uint32_t receiptId); + unsigned int GetVarsWrittenPerRemoteSystemListIndex(RakNetGUID guid); + void RemoveRemoteSystemVariableHistory(void); + + RemoteSystemVariableHistory* GetRemoteSystemVariableHistory(RakNetGUID guid); + + ChangedVariablesList *AllocChangedVariablesList(void); + void FreeChangedVariablesList(ChangedVariablesList *changedVariables); + void StoreChangedVariablesList(RemoteSystemVariableHistory *variableHistory, ChangedVariablesList *changedVariables, uint32_t sendReceipt); + + RemoteSystemVariableHistory *StartVariableHistoryWrite(RakNetGUID guid); + unsigned int GetRemoteSystemHistoryListIndex(RakNetGUID guid); + +}; + +} + +#endif diff --git a/src/raknet/VariableListDeltaTracker.cpp b/src/raknet/VariableListDeltaTracker.cpp index 5523a99..cbbcb2f 100755 --- a/src/raknet/VariableListDeltaTracker.cpp +++ b/src/raknet/VariableListDeltaTracker.cpp @@ -1,40 +1,40 @@ -#include "VariableListDeltaTracker.h" - -using namespace RakNet; - -VariableListDeltaTracker::VariableListDeltaTracker() {nextWriteIndex=0;} -VariableListDeltaTracker::~VariableListDeltaTracker() -{ - unsigned int i; - for (i=0; i < variableList.Size(); i++) - rakFree_Ex(variableList[i].lastData,_FILE_AND_LINE_); -} - -// Call before using a series of WriteVar -void VariableListDeltaTracker::StartWrite(void) {nextWriteIndex=0;} - -void VariableListDeltaTracker::FlagDirtyFromBitArray(unsigned char *bArray) -{ - unsigned short readOffset=0; - for (readOffset=0; readOffset < variableList.Size(); readOffset++) - { - bool result = ( bArray[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0; - - if (result==true) - variableList[readOffset].isDirty=true; - } -} -VariableListDeltaTracker::VariableLastValueNode::VariableLastValueNode() -{ - lastData=0; -} -VariableListDeltaTracker::VariableLastValueNode::VariableLastValueNode(const unsigned char *data, int _byteLength) -{ - lastData=(char*) rakMalloc_Ex(_byteLength,_FILE_AND_LINE_); - memcpy(lastData,data,_byteLength); - byteLength=_byteLength; - isDirty=false; -} -VariableListDeltaTracker::VariableLastValueNode::~VariableLastValueNode() -{ -} +#include "VariableListDeltaTracker.h" + +using namespace RakNet; + +VariableListDeltaTracker::VariableListDeltaTracker() {nextWriteIndex=0;} +VariableListDeltaTracker::~VariableListDeltaTracker() +{ + unsigned int i; + for (i=0; i < variableList.Size(); i++) + rakFree_Ex(variableList[i].lastData,_FILE_AND_LINE_); +} + +// Call before using a series of WriteVar +void VariableListDeltaTracker::StartWrite(void) {nextWriteIndex=0;} + +void VariableListDeltaTracker::FlagDirtyFromBitArray(unsigned char *bArray) +{ + unsigned short readOffset=0; + for (readOffset=0; readOffset < variableList.Size(); readOffset++) + { + bool result = ( bArray[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0; + + if (result==true) + variableList[readOffset].isDirty=true; + } +} +VariableListDeltaTracker::VariableLastValueNode::VariableLastValueNode() +{ + lastData=0; +} +VariableListDeltaTracker::VariableLastValueNode::VariableLastValueNode(const unsigned char *data, int _byteLength) +{ + lastData=(char*) rakMalloc_Ex(_byteLength,_FILE_AND_LINE_); + memcpy(lastData,data,_byteLength); + byteLength=_byteLength; + isDirty=false; +} +VariableListDeltaTracker::VariableLastValueNode::~VariableLastValueNode() +{ +} diff --git a/src/raknet/VariableListDeltaTracker.h b/src/raknet/VariableListDeltaTracker.h index 5b55d3e..e449cd7 100755 --- a/src/raknet/VariableListDeltaTracker.h +++ b/src/raknet/VariableListDeltaTracker.h @@ -1,131 +1,131 @@ -#include "NativeTypes.h" -#include "DS_List.h" -#include "RakMemoryOverride.h" -#include "BitStream.h" - -namespace RakNet -{ -/// Class to write a series of variables, copy the contents to memory, and return if the newly written value is different than what was last written -/// Can also encode the reads, writes, and results directly to/from a bitstream -class VariableListDeltaTracker -{ -public: - VariableListDeltaTracker(); - ~VariableListDeltaTracker(); - - // Call before using a series of WriteVar - void StartWrite(void); - - bool IsPastEndOfList(void) const {return nextWriteIndex>=variableList.Size();} - - /// Records the passed value of the variable to memory, and returns true if the value is different from the write before that (or if it is the first write) - /// \pre Call StartWrite() before doing the first of a series of calls to WriteVar or other functions that call WriteVar - /// \note Variables must be of the same type, written in the same order, each time - template - bool WriteVar(const VarType &varData) - { - RakNet::BitStream temp; - temp.Write(varData); - if (nextWriteIndex>=variableList.Size()) - { - variableList.Push(VariableLastValueNode(temp.GetData(),temp.GetNumberOfBytesUsed()),_FILE_AND_LINE_); - nextWriteIndex++; - return true; // Different because it's new - } - - if (temp.GetNumberOfBytesUsed()!=variableList[nextWriteIndex].byteLength) - { - variableList[nextWriteIndex].lastData=(char*) rakRealloc_Ex(variableList[nextWriteIndex].lastData, temp.GetNumberOfBytesUsed(),_FILE_AND_LINE_); - variableList[nextWriteIndex].byteLength=temp.GetNumberOfBytesUsed(); - memcpy(variableList[nextWriteIndex].lastData,temp.GetData(),temp.GetNumberOfBytesUsed()); - nextWriteIndex++; - variableList[nextWriteIndex].isDirty=false; - return true; // Different because the serialized size is different - } - if (variableList[nextWriteIndex].isDirty==false && memcmp(temp.GetData(),variableList[nextWriteIndex].lastData, variableList[nextWriteIndex].byteLength)==0) - { - nextWriteIndex++; - return false; // Same because not dirty and memcmp is the same - } - - variableList[nextWriteIndex].isDirty=false; - memcpy(variableList[nextWriteIndex].lastData,temp.GetData(),temp.GetNumberOfBytesUsed()); - nextWriteIndex++; - return true; // Different because dirty or memcmp was different - } - /// Calls WriteVar. If the variable has changed, writes true, and writes the variable. Otherwise writes false. - template - bool WriteVarToBitstream(const VarType &varData, RakNet::BitStream *bitStream) - { - bool wasDifferent = WriteVar(varData); - bitStream->Write(wasDifferent); - if (wasDifferent) - { - bitStream->Write(varData); - return true; - } - return false; - } - /// Calls WriteVarToBitstream(). Additionally, adds the boolean result of WriteVar() to boolean bit array - template - bool WriteVarToBitstream(const VarType &varData, RakNet::BitStream *bitStream, unsigned char *bArray, unsigned short writeOffset) - { - if (WriteVarToBitstream(varData,bitStream)==true) - { - BitSize_t numberOfBitsMod8 = writeOffset & 7; - - if ( numberOfBitsMod8 == 0 ) - bArray[ writeOffset >> 3 ] = 0x80; - else - bArray[ writeOffset >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 - - return true; - } - else - { - if ( ( writeOffset & 7 ) == 0 ) - bArray[ writeOffset >> 3 ] = 0; - - return false; - } - } - - /// Paired with a call to WriteVarToBitstream(), will read a variable if it had changed. Otherwise the values remains the same. - template - static bool ReadVarFromBitstream(const VarType &varData, RakNet::BitStream *bitStream) - { - bool wasWritten; - if (bitStream->Read(wasWritten)==false) - return false; - if (wasWritten) - { - if (bitStream->Read(varData)==false) - return false; - } - return wasWritten; - } - - /// Variables flagged dirty will cause WriteVar() to return true, even if the variable had not otherwise changed - /// This updates all the variables in the list, where in each index \a varsWritten is true, so will the variable at the corresponding index be flagged dirty - void FlagDirtyFromBitArray(unsigned char *bArray); - - /// \internal - struct VariableLastValueNode - { - VariableLastValueNode(); - VariableLastValueNode(const unsigned char *data, int _byteLength); - ~VariableLastValueNode(); - char *lastData; - unsigned int byteLength; - bool isDirty; - }; - -protected: - /// \internal - DataStructures::List variableList; - /// \internal - unsigned int nextWriteIndex; -}; - - -} +#include "NativeTypes.h" +#include "DS_List.h" +#include "RakMemoryOverride.h" +#include "BitStream.h" + +namespace RakNet +{ +/// Class to write a series of variables, copy the contents to memory, and return if the newly written value is different than what was last written +/// Can also encode the reads, writes, and results directly to/from a bitstream +class VariableListDeltaTracker +{ +public: + VariableListDeltaTracker(); + ~VariableListDeltaTracker(); + + // Call before using a series of WriteVar + void StartWrite(void); + + bool IsPastEndOfList(void) const {return nextWriteIndex>=variableList.Size();} + + /// Records the passed value of the variable to memory, and returns true if the value is different from the write before that (or if it is the first write) + /// \pre Call StartWrite() before doing the first of a series of calls to WriteVar or other functions that call WriteVar + /// \note Variables must be of the same type, written in the same order, each time + template + bool WriteVar(const VarType &varData) + { + RakNet::BitStream temp; + temp.Write(varData); + if (nextWriteIndex>=variableList.Size()) + { + variableList.Push(VariableLastValueNode(temp.GetData(),temp.GetNumberOfBytesUsed()),_FILE_AND_LINE_); + nextWriteIndex++; + return true; // Different because it's new + } + + if (temp.GetNumberOfBytesUsed()!=variableList[nextWriteIndex].byteLength) + { + variableList[nextWriteIndex].lastData=(char*) rakRealloc_Ex(variableList[nextWriteIndex].lastData, temp.GetNumberOfBytesUsed(),_FILE_AND_LINE_); + variableList[nextWriteIndex].byteLength=temp.GetNumberOfBytesUsed(); + memcpy(variableList[nextWriteIndex].lastData,temp.GetData(),temp.GetNumberOfBytesUsed()); + nextWriteIndex++; + variableList[nextWriteIndex].isDirty=false; + return true; // Different because the serialized size is different + } + if (variableList[nextWriteIndex].isDirty==false && memcmp(temp.GetData(),variableList[nextWriteIndex].lastData, variableList[nextWriteIndex].byteLength)==0) + { + nextWriteIndex++; + return false; // Same because not dirty and memcmp is the same + } + + variableList[nextWriteIndex].isDirty=false; + memcpy(variableList[nextWriteIndex].lastData,temp.GetData(),temp.GetNumberOfBytesUsed()); + nextWriteIndex++; + return true; // Different because dirty or memcmp was different + } + /// Calls WriteVar. If the variable has changed, writes true, and writes the variable. Otherwise writes false. + template + bool WriteVarToBitstream(const VarType &varData, RakNet::BitStream *bitStream) + { + bool wasDifferent = WriteVar(varData); + bitStream->Write(wasDifferent); + if (wasDifferent) + { + bitStream->Write(varData); + return true; + } + return false; + } + /// Calls WriteVarToBitstream(). Additionally, adds the boolean result of WriteVar() to boolean bit array + template + bool WriteVarToBitstream(const VarType &varData, RakNet::BitStream *bitStream, unsigned char *bArray, unsigned short writeOffset) + { + if (WriteVarToBitstream(varData,bitStream)==true) + { + BitSize_t numberOfBitsMod8 = writeOffset & 7; + + if ( numberOfBitsMod8 == 0 ) + bArray[ writeOffset >> 3 ] = 0x80; + else + bArray[ writeOffset >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 + + return true; + } + else + { + if ( ( writeOffset & 7 ) == 0 ) + bArray[ writeOffset >> 3 ] = 0; + + return false; + } + } + + /// Paired with a call to WriteVarToBitstream(), will read a variable if it had changed. Otherwise the values remains the same. + template + static bool ReadVarFromBitstream(const VarType &varData, RakNet::BitStream *bitStream) + { + bool wasWritten; + if (bitStream->Read(wasWritten)==false) + return false; + if (wasWritten) + { + if (bitStream->Read(varData)==false) + return false; + } + return wasWritten; + } + + /// Variables flagged dirty will cause WriteVar() to return true, even if the variable had not otherwise changed + /// This updates all the variables in the list, where in each index \a varsWritten is true, so will the variable at the corresponding index be flagged dirty + void FlagDirtyFromBitArray(unsigned char *bArray); + + /// \internal + struct VariableLastValueNode + { + VariableLastValueNode(); + VariableLastValueNode(const unsigned char *data, int _byteLength); + ~VariableLastValueNode(); + char *lastData; + unsigned int byteLength; + bool isDirty; + }; + +protected: + /// \internal + DataStructures::List variableList; + /// \internal + unsigned int nextWriteIndex; +}; + + +} diff --git a/src/raknet/VariadicSQLParser.cpp b/src/raknet/VariadicSQLParser.cpp index bc8e3fc..c0b9204 100755 --- a/src/raknet/VariadicSQLParser.cpp +++ b/src/raknet/VariadicSQLParser.cpp @@ -1,150 +1,150 @@ -#include "VariadicSQLParser.h" -#include "BitStream.h" -#include - -using namespace VariadicSQLParser; - -struct TypeMapping -{ - char inputType; - const char *type; -}; -const int NUM_TYPE_MAPPINGS=7; -TypeMapping typeMappings[NUM_TYPE_MAPPINGS] = -{ - {'i', "int"}, - {'d', "int"}, - {'s', "text"}, - {'b', "bool"}, - {'f', "numeric"}, - {'g', "double precision"}, - {'a', "bytea"}, -}; -unsigned int GetTypeMappingIndex(char c) -{ - unsigned int i; - for (i=0; i < (unsigned int) NUM_TYPE_MAPPINGS; i++ ) - if (typeMappings[i].inputType==c) - return i; - return (unsigned int)-1; -} -const char* VariadicSQLParser::GetTypeMappingAtIndex(int i) -{ - return typeMappings[i].type; -} -void VariadicSQLParser::GetTypeMappingIndices( const char *format, DataStructures::List &indices ) -{ - bool previousCharWasPercentSign; - unsigned int i; - unsigned int typeMappingIndex; - indices.Clear(false, _FILE_AND_LINE_); - unsigned int len = (unsigned int) strlen(format); - previousCharWasPercentSign=false; - for (i=0; i < len; i++) - { - if (previousCharWasPercentSign==true ) - { - typeMappingIndex = GetTypeMappingIndex(format[i]); - if (typeMappingIndex!=(unsigned int) -1) - { - IndexAndType iat; - iat.strIndex=i-1; - iat.typeMappingIndex=typeMappingIndex; - indices.Insert(iat, _FILE_AND_LINE_ ); - } - } - - previousCharWasPercentSign=format[i]=='%'; - } -} -void VariadicSQLParser::ExtractArguments( va_list argptr, const DataStructures::List &indices, char ***argumentBinary, int **argumentLengths ) -{ - if (indices.Size()==0) - return; - - unsigned int i; - *argumentBinary=RakNet::OP_NEW_ARRAY(indices.Size(), _FILE_AND_LINE_); - *argumentLengths=RakNet::OP_NEW_ARRAY(indices.Size(), _FILE_AND_LINE_); - - char **paramData=*argumentBinary; - int *paramLength=*argumentLengths; - - int variadicArgIndex; - for (variadicArgIndex=0, i=0; i < indices.Size(); i++, variadicArgIndex++) - { - switch (typeMappings[indices[i].typeMappingIndex].inputType) - { - case 'i': - case 'd': - { - int val = va_arg( argptr, int ); - paramLength[i]=sizeof(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], &val, paramLength[i]); - if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); - } - break; - case 's': - { - char* val = va_arg( argptr, char* ); - paramLength[i]=(int) strlen(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i]+1, _FILE_AND_LINE_); - memcpy(paramData[i], val, paramLength[i]+1); - } - break; - case 'b': - { - bool val = (va_arg( argptr, int )!=0); - paramLength[i]=sizeof(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], &val, paramLength[i]); - if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); - } - break; - /* - case 'f': - { - // On MSVC at least, this only works with double as the 2nd param - float val = (float) va_arg( argptr, double ); - //float val = va_arg( argptr, float ); - paramLength[i]=sizeof(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], &val, paramLength[i]); - if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); - } - break; - */ - // On MSVC at least, this only works with double as the 2nd param - case 'f': - case 'g': - { - double val = va_arg( argptr, double ); - paramLength[i]=sizeof(val); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], &val, paramLength[i]); - if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); - } - break; - case 'a': - { - char* val = va_arg( argptr, char* ); - paramLength[i]=va_arg( argptr, unsigned int ); - paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); - memcpy(paramData[i], val, paramLength[i]); - } - break; - } - } - -} -void VariadicSQLParser::FreeArguments(const DataStructures::List &indices, char **argumentBinary, int *argumentLengths) -{ - if (indices.Size()==0) - return; - - unsigned int i; - for (i=0; i < indices.Size(); i++) - rakFree_Ex(argumentBinary[i],_FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(argumentBinary,_FILE_AND_LINE_); - RakNet::OP_DELETE_ARRAY(argumentLengths,_FILE_AND_LINE_); -} +#include "VariadicSQLParser.h" +#include "BitStream.h" +#include + +using namespace VariadicSQLParser; + +struct TypeMapping +{ + char inputType; + const char *type; +}; +const int NUM_TYPE_MAPPINGS=7; +TypeMapping typeMappings[NUM_TYPE_MAPPINGS] = +{ + {'i', "int"}, + {'d', "int"}, + {'s', "text"}, + {'b', "bool"}, + {'f', "numeric"}, + {'g', "double precision"}, + {'a', "bytea"}, +}; +unsigned int GetTypeMappingIndex(char c) +{ + unsigned int i; + for (i=0; i < (unsigned int) NUM_TYPE_MAPPINGS; i++ ) + if (typeMappings[i].inputType==c) + return i; + return (unsigned int)-1; +} +const char* VariadicSQLParser::GetTypeMappingAtIndex(int i) +{ + return typeMappings[i].type; +} +void VariadicSQLParser::GetTypeMappingIndices( const char *format, DataStructures::List &indices ) +{ + bool previousCharWasPercentSign; + unsigned int i; + unsigned int typeMappingIndex; + indices.Clear(false, _FILE_AND_LINE_); + unsigned int len = (unsigned int) strlen(format); + previousCharWasPercentSign=false; + for (i=0; i < len; i++) + { + if (previousCharWasPercentSign==true ) + { + typeMappingIndex = GetTypeMappingIndex(format[i]); + if (typeMappingIndex!=(unsigned int) -1) + { + IndexAndType iat; + iat.strIndex=i-1; + iat.typeMappingIndex=typeMappingIndex; + indices.Insert(iat, _FILE_AND_LINE_ ); + } + } + + previousCharWasPercentSign=format[i]=='%'; + } +} +void VariadicSQLParser::ExtractArguments( va_list argptr, const DataStructures::List &indices, char ***argumentBinary, int **argumentLengths ) +{ + if (indices.Size()==0) + return; + + unsigned int i; + *argumentBinary=RakNet::OP_NEW_ARRAY(indices.Size(), _FILE_AND_LINE_); + *argumentLengths=RakNet::OP_NEW_ARRAY(indices.Size(), _FILE_AND_LINE_); + + char **paramData=*argumentBinary; + int *paramLength=*argumentLengths; + + int variadicArgIndex; + for (variadicArgIndex=0, i=0; i < indices.Size(); i++, variadicArgIndex++) + { + switch (typeMappings[indices[i].typeMappingIndex].inputType) + { + case 'i': + case 'd': + { + int val = va_arg( argptr, int ); + paramLength[i]=sizeof(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], &val, paramLength[i]); + if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); + } + break; + case 's': + { + char* val = va_arg( argptr, char* ); + paramLength[i]=(int) strlen(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i]+1, _FILE_AND_LINE_); + memcpy(paramData[i], val, paramLength[i]+1); + } + break; + case 'b': + { + bool val = (va_arg( argptr, int )!=0); + paramLength[i]=sizeof(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], &val, paramLength[i]); + if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); + } + break; + /* + case 'f': + { + // On MSVC at least, this only works with double as the 2nd param + float val = (float) va_arg( argptr, double ); + //float val = va_arg( argptr, float ); + paramLength[i]=sizeof(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], &val, paramLength[i]); + if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); + } + break; + */ + // On MSVC at least, this only works with double as the 2nd param + case 'f': + case 'g': + { + double val = va_arg( argptr, double ); + paramLength[i]=sizeof(val); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], &val, paramLength[i]); + if (RakNet::BitStream::IsNetworkOrder()==false) RakNet::BitStream::ReverseBytesInPlace((unsigned char*) paramData[i], paramLength[i]); + } + break; + case 'a': + { + char* val = va_arg( argptr, char* ); + paramLength[i]=va_arg( argptr, unsigned int ); + paramData[i]=(char*) rakMalloc_Ex(paramLength[i], _FILE_AND_LINE_); + memcpy(paramData[i], val, paramLength[i]); + } + break; + } + } + +} +void VariadicSQLParser::FreeArguments(const DataStructures::List &indices, char **argumentBinary, int *argumentLengths) +{ + if (indices.Size()==0) + return; + + unsigned int i; + for (i=0; i < indices.Size(); i++) + rakFree_Ex(argumentBinary[i],_FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(argumentBinary,_FILE_AND_LINE_); + RakNet::OP_DELETE_ARRAY(argumentLengths,_FILE_AND_LINE_); +} diff --git a/src/raknet/VariadicSQLParser.h b/src/raknet/VariadicSQLParser.h index a176751..96b1c36 100755 --- a/src/raknet/VariadicSQLParser.h +++ b/src/raknet/VariadicSQLParser.h @@ -1,24 +1,24 @@ -#ifndef __VARIADIC_SQL_PARSER_H -#define __VARIADIC_SQL_PARSER_H - -#include "DS_List.h" - -#include - -namespace VariadicSQLParser -{ - struct IndexAndType - { - unsigned int strIndex; - unsigned int typeMappingIndex; - }; - const char* GetTypeMappingAtIndex(int i); - void GetTypeMappingIndices( const char *format, DataStructures::List &indices ); - // Given an SQL string with variadic arguments, allocate argumentBinary and argumentLengths, and hold the parameters in binary format - // Last 2 parameters are out parameters - void ExtractArguments( va_list argptr, const DataStructures::List &indices, char ***argumentBinary, int **argumentLengths ); - void FreeArguments(const DataStructures::List &indices, char **argumentBinary, int *argumentLengths); -} - - -#endif +#ifndef __VARIADIC_SQL_PARSER_H +#define __VARIADIC_SQL_PARSER_H + +#include "DS_List.h" + +#include + +namespace VariadicSQLParser +{ + struct IndexAndType + { + unsigned int strIndex; + unsigned int typeMappingIndex; + }; + const char* GetTypeMappingAtIndex(int i); + void GetTypeMappingIndices( const char *format, DataStructures::List &indices ); + // Given an SQL string with variadic arguments, allocate argumentBinary and argumentLengths, and hold the parameters in binary format + // Last 2 parameters are out parameters + void ExtractArguments( va_list argptr, const DataStructures::List &indices, char ***argumentBinary, int **argumentLengths ); + void FreeArguments(const DataStructures::List &indices, char **argumentBinary, int *argumentLengths); +} + + +#endif diff --git a/src/raknet/VitaIncludes.cpp b/src/raknet/VitaIncludes.cpp index fa0e62d..5e567bf 100755 --- a/src/raknet/VitaIncludes.cpp +++ b/src/raknet/VitaIncludes.cpp @@ -1,163 +1,163 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/raknet/VitaIncludes.h b/src/raknet/VitaIncludes.h index 062dad9..bd70cd1 100755 --- a/src/raknet/VitaIncludes.h +++ b/src/raknet/VitaIncludes.h @@ -1,57 +1,57 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/raknet/WSAStartupSingleton.cpp b/src/raknet/WSAStartupSingleton.cpp index 17e848b..f808214 100755 --- a/src/raknet/WSAStartupSingleton.cpp +++ b/src/raknet/WSAStartupSingleton.cpp @@ -1,75 +1,75 @@ -#include "WSAStartupSingleton.h" - - - - - -#if defined(_WIN32) -#include -#include - - - - - -#endif -#include "RakNetDefines.h" -#include - -int WSAStartupSingleton::refCount=0; - -WSAStartupSingleton::WSAStartupSingleton() {} -WSAStartupSingleton::~WSAStartupSingleton() {} -void WSAStartupSingleton::AddRef(void) -{ -#ifdef _WIN32 - - refCount++; - - if (refCount!=1) - return; - - - - - - WSADATA winsockInfo; - if ( WSAStartup( MAKEWORD( 2, 2 ), &winsockInfo ) != 0 ) - { -#if defined(_DEBUG) - DWORD dwIOError = GetLastError(); - LPVOID messageBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language - ( LPTSTR ) & messageBuffer, 0, NULL ); - // something has gone wrong here... - RAKNET_DEBUG_PRINTF( "WSAStartup failed:Error code - %d\n%s", dwIOError, messageBuffer ); - //Free the buffer. - LocalFree( messageBuffer ); -#endif - } - -#endif -} -void WSAStartupSingleton::Deref(void) -{ -#ifdef _WIN32 - if (refCount==0) - return; - - if (refCount>1) - { - refCount--; - return; - } - - WSACleanup(); - - - - - - - refCount=0; -#endif -} +#include "WSAStartupSingleton.h" + + + + + +#if defined(_WIN32) +#include +#include + + + + + +#endif +#include "RakNetDefines.h" +#include + +int WSAStartupSingleton::refCount=0; + +WSAStartupSingleton::WSAStartupSingleton() {} +WSAStartupSingleton::~WSAStartupSingleton() {} +void WSAStartupSingleton::AddRef(void) +{ +#ifdef _WIN32 + + refCount++; + + if (refCount!=1) + return; + + + + + + WSADATA winsockInfo; + if ( WSAStartup( MAKEWORD( 2, 2 ), &winsockInfo ) != 0 ) + { +#if defined(_DEBUG) + DWORD dwIOError = GetLastError(); + LPVOID messageBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language + ( LPTSTR ) & messageBuffer, 0, NULL ); + // something has gone wrong here... + RAKNET_DEBUG_PRINTF( "WSAStartup failed:Error code - %d\n%s", dwIOError, messageBuffer ); + //Free the buffer. + LocalFree( messageBuffer ); +#endif + } + +#endif +} +void WSAStartupSingleton::Deref(void) +{ +#ifdef _WIN32 + if (refCount==0) + return; + + if (refCount>1) + { + refCount--; + return; + } + + WSACleanup(); + + + + + + + refCount=0; +#endif +} diff --git a/src/raknet/WSAStartupSingleton.h b/src/raknet/WSAStartupSingleton.h index d2930fe..2ac9042 100755 --- a/src/raknet/WSAStartupSingleton.h +++ b/src/raknet/WSAStartupSingleton.h @@ -1,16 +1,16 @@ -#ifndef __WSA_STARTUP_SINGLETON_H -#define __WSA_STARTUP_SINGLETON_H - -class WSAStartupSingleton -{ -public: - WSAStartupSingleton(); - ~WSAStartupSingleton(); - static void AddRef(void); - static void Deref(void); - -protected: - static int refCount; -}; - -#endif +#ifndef __WSA_STARTUP_SINGLETON_H +#define __WSA_STARTUP_SINGLETON_H + +class WSAStartupSingleton +{ +public: + WSAStartupSingleton(); + ~WSAStartupSingleton(); + static void AddRef(void); + static void Deref(void); + +protected: + static int refCount; +}; + +#endif diff --git a/src/raknet/WindowsIncludes.h b/src/raknet/WindowsIncludes.h index 828614c..9bec7bf 100755 --- a/src/raknet/WindowsIncludes.h +++ b/src/raknet/WindowsIncludes.h @@ -1,17 +1,17 @@ -#if defined(X360__) -#elif defined (_WIN32) -#include -#include -#include - -// Must always include Winsock2.h before windows.h -// or else: -// winsock2.h(99) : error C2011: 'fd_set' : 'struct' type redefinition -// winsock2.h(134) : warning C4005: 'FD_SET' : macro redefinition -// winsock.h(83) : see previous definition of 'FD_SET' -// winsock2.h(143) : error C2011: 'timeval' : 'struct' type redefinition -// winsock2.h(199) : error C2011: 'hostent' : 'struct' type redefinition -// winsock2.h(212) : error C2011: 'netent' : 'struct' type redefinition -// winsock2.h(219) : error C2011: 'servent' : 'struct' type redefinition - -#endif +#if defined(X360__) +#elif defined (_WIN32) +#include +#include +#include + +// Must always include Winsock2.h before windows.h +// or else: +// winsock2.h(99) : error C2011: 'fd_set' : 'struct' type redefinition +// winsock2.h(134) : warning C4005: 'FD_SET' : macro redefinition +// winsock.h(83) : see previous definition of 'FD_SET' +// winsock2.h(143) : error C2011: 'timeval' : 'struct' type redefinition +// winsock2.h(199) : error C2011: 'hostent' : 'struct' type redefinition +// winsock2.h(212) : error C2011: 'netent' : 'struct' type redefinition +// winsock2.h(219) : error C2011: 'servent' : 'struct' type redefinition + +#endif diff --git a/src/raknet/XBox360Includes.h b/src/raknet/XBox360Includes.h index 7896bec..ad3f39d 100755 --- a/src/raknet/XBox360Includes.h +++ b/src/raknet/XBox360Includes.h @@ -1,55 +1,55 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/raknet/_FindFirst.cpp b/src/raknet/_FindFirst.cpp index 6db92ac..8a697c1 100755 --- a/src/raknet/_FindFirst.cpp +++ b/src/raknet/_FindFirst.cpp @@ -1,159 +1,159 @@ -/** -* Original file by the_viking, fixed by R√¥mulo Fernandes, fixed by Emmanuel Nars -* Should emulate windows finddata structure -*/ -#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_WIN32) -#include "_FindFirst.h" -#include "DS_List.h" - -#include - -#include - - -static DataStructures::List< _findinfo_t* > fileInfo; - -#include "RakMemoryOverride.h" -#include "RakAssert.h" - -/** -* _findfirst - equivalent -*/ -long _findfirst(const char *name, _finddata_t *f) -{ - RakNet::RakString nameCopy = name; - RakNet::RakString filter; - - // This is linux only, so don't bother with '\' - const char* lastSep = strrchr(name,'/'); - if(!lastSep) - { - // filter pattern only is given, search current directory. - filter = nameCopy; - nameCopy = "."; - } else - { - // strip filter pattern from directory name, leave - // trailing '/' intact. - filter = lastSep+1; - unsigned sepIndex = lastSep - name; - nameCopy.Erase(sepIndex+1, nameCopy.GetLength() - sepIndex-1); - } - - DIR* dir = opendir(nameCopy); - - if(!dir) return -1; - - _findinfo_t* fi = RakNet::OP_NEW<_findinfo_t>( _FILE_AND_LINE_ ); - fi->filter = filter; - fi->dirName = nameCopy; // we need to remember this for stat() - fi->openedDir = dir; - fileInfo.Insert(fi, _FILE_AND_LINE_); - - long ret = fileInfo.Size()-1; - - // Retrieve the first file. We cannot rely on the first item - // being '.' - if (_findnext(ret, f) == -1) return -1; - else return ret; -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int _findnext(long h, _finddata_t *f) -{ - RakAssert(h >= 0 && h < (long)fileInfo.Size()); - if (h < 0 || h >= (long)fileInfo.Size()) return -1; - - _findinfo_t* fi = fileInfo[h]; - - while(true) - { - dirent* entry = readdir(fi->openedDir); - if(entry == 0) return -1; - - // Only report stuff matching our filter - if (fnmatch(fi->filter, entry->d_name, FNM_PATHNAME) != 0) continue; - - // To reliably determine the entry's type, we must do - // a stat... don't rely on entry->d_type, as this - // might be unavailable! - struct stat filestat; - RakNet::RakString fullPath = fi->dirName + entry->d_name; - if (stat(fullPath, &filestat) != 0) - { - RAKNET_DEBUG_PRINTF("Cannot stat %s\n", fullPath.C_String()); - continue; - } - - if (S_ISREG(filestat.st_mode)) - { - f->attrib = _A_NORMAL; - } else if (S_ISDIR(filestat.st_mode)) - { - f->attrib = _A_SUBDIR; - } else continue; // We are interested in files and - // directories only. Links currently - // are not supported. - - f->size = filestat.st_size; - strncpy(f->name, entry->d_name, STRING_BUFFER_SIZE); - - return 0; - } - - return -1; -} - - - - - -/** - * _findclose - equivalent - */ -int _findclose(long h) -{ - if (h==-1) return 0; - - if (h < 0 || h >= (long)fileInfo.Size()) - { - RakAssert(false); - return -1; - } - - _findinfo_t* fi = fileInfo[h]; - closedir(fi->openedDir); - fileInfo.RemoveAtIndex(h); - RakNet::OP_DELETE(fi, _FILE_AND_LINE_); - return 0; -} -#endif +/** +* Original file by the_viking, fixed by R√¥mulo Fernandes, fixed by Emmanuel Nars +* Should emulate windows finddata structure +*/ +#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_WIN32) +#include "_FindFirst.h" +#include "DS_List.h" + +#include + +#include + + +static DataStructures::List< _findinfo_t* > fileInfo; + +#include "RakMemoryOverride.h" +#include "RakAssert.h" + +/** +* _findfirst - equivalent +*/ +long _findfirst(const char *name, _finddata_t *f) +{ + RakNet::RakString nameCopy = name; + RakNet::RakString filter; + + // This is linux only, so don't bother with '\' + const char* lastSep = strrchr(name,'/'); + if(!lastSep) + { + // filter pattern only is given, search current directory. + filter = nameCopy; + nameCopy = "."; + } else + { + // strip filter pattern from directory name, leave + // trailing '/' intact. + filter = lastSep+1; + unsigned sepIndex = lastSep - name; + nameCopy.Erase(sepIndex+1, nameCopy.GetLength() - sepIndex-1); + } + + DIR* dir = opendir(nameCopy); + + if(!dir) return -1; + + _findinfo_t* fi = RakNet::OP_NEW<_findinfo_t>( _FILE_AND_LINE_ ); + fi->filter = filter; + fi->dirName = nameCopy; // we need to remember this for stat() + fi->openedDir = dir; + fileInfo.Insert(fi, _FILE_AND_LINE_); + + long ret = fileInfo.Size()-1; + + // Retrieve the first file. We cannot rely on the first item + // being '.' + if (_findnext(ret, f) == -1) return -1; + else return ret; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +int _findnext(long h, _finddata_t *f) +{ + RakAssert(h >= 0 && h < (long)fileInfo.Size()); + if (h < 0 || h >= (long)fileInfo.Size()) return -1; + + _findinfo_t* fi = fileInfo[h]; + + while(true) + { + dirent* entry = readdir(fi->openedDir); + if(entry == 0) return -1; + + // Only report stuff matching our filter + if (fnmatch(fi->filter, entry->d_name, FNM_PATHNAME) != 0) continue; + + // To reliably determine the entry's type, we must do + // a stat... don't rely on entry->d_type, as this + // might be unavailable! + struct stat filestat; + RakNet::RakString fullPath = fi->dirName + entry->d_name; + if (stat(fullPath, &filestat) != 0) + { + RAKNET_DEBUG_PRINTF("Cannot stat %s\n", fullPath.C_String()); + continue; + } + + if (S_ISREG(filestat.st_mode)) + { + f->attrib = _A_NORMAL; + } else if (S_ISDIR(filestat.st_mode)) + { + f->attrib = _A_SUBDIR; + } else continue; // We are interested in files and + // directories only. Links currently + // are not supported. + + f->size = filestat.st_size; + strncpy(f->name, entry->d_name, STRING_BUFFER_SIZE); + + return 0; + } + + return -1; +} + + + + + +/** + * _findclose - equivalent + */ +int _findclose(long h) +{ + if (h==-1) return 0; + + if (h < 0 || h >= (long)fileInfo.Size()) + { + RakAssert(false); + return -1; + } + + _findinfo_t* fi = fileInfo[h]; + closedir(fi->openedDir); + fileInfo.RemoveAtIndex(h); + RakNet::OP_DELETE(fi, _FILE_AND_LINE_); + return 0; +} +#endif diff --git a/src/raknet/_FindFirst.h b/src/raknet/_FindFirst.h index a664222..6305af7 100755 --- a/src/raknet/_FindFirst.h +++ b/src/raknet/_FindFirst.h @@ -1,56 +1,56 @@ -/// -/// Original file by the_viking, fixed by Rômulo Fernandes -/// Should emulate windows finddata structure -/// - -#ifndef GCC_FINDFIRST_H -#define GCC_FINDFIRST_H - -#if (defined(__GNUC__) || defined(__ARMCC_VERSION) || defined(__GCCXML__) || defined(__S3E__) ) && !defined(__WIN32) - -#include - -#include "RakString.h" - -#define _A_NORMAL 0x00 // Normal file -#define _A_RDONLY 0x01 // Read-only file -#define _A_HIDDEN 0x02 // Hidden file -#define _A_SYSTEM 0x04 // System file -#define _A_VOLID 0x08 // Volume ID -#define _A_SUBDIR 0x10 // Subdirectory -#define _A_ARCH 0x20 // File changed since last archive -#define FA_NORMAL 0x00 // Synonym of _A_NORMAL -#define FA_RDONLY 0x01 // Synonym of _A_RDONLY -#define FA_HIDDEN 0x02 // Synonym of _A_HIDDEN -#define FA_SYSTEM 0x04 // Synonym of _A_SYSTEM -#define FA_LABEL 0x08 // Synonym of _A_VOLID -#define FA_DIREC 0x10 // Synonym of _A_SUBDIR -#define FA_ARCH 0x20 // Synonym of _A_ARCH - - -const unsigned STRING_BUFFER_SIZE = 512; - -typedef struct _finddata_t -{ - char name[STRING_BUFFER_SIZE]; - int attrib; - unsigned long size; -} _finddata; - -/** - * Hold information about the current search - */ -typedef struct _findinfo_t -{ - DIR* openedDir; - RakNet::RakString filter; - RakNet::RakString dirName; -} _findinfo; - -long _findfirst(const char *name, _finddata_t *f); -int _findnext(long h, _finddata_t *f); -int _findclose(long h); - -#endif -#endif - +/// +/// Original file by the_viking, fixed by Rômulo Fernandes +/// Should emulate windows finddata structure +/// + +#ifndef GCC_FINDFIRST_H +#define GCC_FINDFIRST_H + +#if (defined(__GNUC__) || defined(__ARMCC_VERSION) || defined(__GCCXML__) || defined(__S3E__) ) && !defined(__WIN32) + +#include + +#include "RakString.h" + +#define _A_NORMAL 0x00 // Normal file +#define _A_RDONLY 0x01 // Read-only file +#define _A_HIDDEN 0x02 // Hidden file +#define _A_SYSTEM 0x04 // System file +#define _A_VOLID 0x08 // Volume ID +#define _A_SUBDIR 0x10 // Subdirectory +#define _A_ARCH 0x20 // File changed since last archive +#define FA_NORMAL 0x00 // Synonym of _A_NORMAL +#define FA_RDONLY 0x01 // Synonym of _A_RDONLY +#define FA_HIDDEN 0x02 // Synonym of _A_HIDDEN +#define FA_SYSTEM 0x04 // Synonym of _A_SYSTEM +#define FA_LABEL 0x08 // Synonym of _A_VOLID +#define FA_DIREC 0x10 // Synonym of _A_SUBDIR +#define FA_ARCH 0x20 // Synonym of _A_ARCH + + +const unsigned STRING_BUFFER_SIZE = 512; + +typedef struct _finddata_t +{ + char name[STRING_BUFFER_SIZE]; + int attrib; + unsigned long size; +} _finddata; + +/** + * Hold information about the current search + */ +typedef struct _findinfo_t +{ + DIR* openedDir; + RakNet::RakString filter; + RakNet::RakString dirName; +} _findinfo; + +long _findfirst(const char *name, _finddata_t *f); +int _findnext(long h, _finddata_t *f); +int _findclose(long h); + +#endif +#endif + diff --git a/src/raknet/gettimeofday.cpp b/src/raknet/gettimeofday.cpp index 6b2e1c0..917330a 100755 --- a/src/raknet/gettimeofday.cpp +++ b/src/raknet/gettimeofday.cpp @@ -1,55 +1,55 @@ - - - - -#if defined(_WIN32) && !defined(__GNUC__) &&!defined(__GCCXML__) - -#include "gettimeofday.h" - -// From http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/ - -#include "WindowsIncludes.h" - -#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) - #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 -#else - #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL -#endif - -int gettimeofday(struct timeval *tv, struct timezone *tz) -{ - FILETIME ft; - unsigned __int64 tmpres = 0; - static int tzflag; - - if (NULL != tv) - { - GetSystemTimeAsFileTime(&ft); - - tmpres |= ft.dwHighDateTime; - tmpres <<= 32; - tmpres |= ft.dwLowDateTime; - - /*converting file time to unix epoch*/ - tmpres /= 10; /*convert into microseconds*/ - tmpres -= DELTA_EPOCH_IN_MICROSECS; - tv->tv_sec = (long)(tmpres / 1000000UL); - tv->tv_usec = (long)(tmpres % 1000000UL); - } - - if (NULL != tz) - { - if (!tzflag) - { - _tzset(); - tzflag++; - } - tz->tz_minuteswest = _timezone / 60; - tz->tz_dsttime = _daylight; - } - - return 0; -} - -#endif - + + + + +#if defined(_WIN32) && !defined(__GNUC__) &&!defined(__GCCXML__) + +#include "gettimeofday.h" + +// From http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/ + +#include "WindowsIncludes.h" + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch*/ + tmpres /= 10; /*convert into microseconds*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) + { + if (!tzflag) + { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} + +#endif + diff --git a/src/raknet/gettimeofday.h b/src/raknet/gettimeofday.h index 52c4207..5e3b639 100755 --- a/src/raknet/gettimeofday.h +++ b/src/raknet/gettimeofday.h @@ -1,56 +1,56 @@ -#ifndef __GET_TIME_OF_DAY_H -#define __GET_TIME_OF_DAY_H - -#if defined(_WIN32) && !defined(__GNUC__) &&!defined(__GCCXML__) -#include < time.h > -struct timezone -{ - int tz_minuteswest; /* minutes W of Greenwich */ - int tz_dsttime; /* type of dst correction */ -}; -int gettimeofday(struct timeval *tv, struct timezone *tz); - - -#else - - - - -#include - -#include - -// Uncomment this if you need to -/* -// http://www.halcode.com/archives/2008/08/26/retrieving-system-time-gettimeofday/ -struct timezone -{ - int tz_minuteswest; - int tz_dsttime; -}; - -#ifdef __cplusplus - -void GetSystemTimeAsFileTime(FILETIME*); - -inline int gettimeofday(struct timeval* p, void* tz ) -{ - union { - long long ns100; // time since 1 Jan 1601 in 100ns units - FILETIME ft; - } now; - - GetSystemTimeAsFileTime( &(now.ft) ); - p->tv_usec=(long)((now.ns100 / 10LL) % 1000000LL ); - p->tv_sec= (long)((now.ns100-(116444736000000000LL))/10000000LL); - return 0; -} - -#else - int gettimeofday(struct timeval* p, void* tz ); -#endif -*/ - -#endif - -#endif +#ifndef __GET_TIME_OF_DAY_H +#define __GET_TIME_OF_DAY_H + +#if defined(_WIN32) && !defined(__GNUC__) &&!defined(__GCCXML__) +#include < time.h > +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; +int gettimeofday(struct timeval *tv, struct timezone *tz); + + +#else + + + + +#include + +#include + +// Uncomment this if you need to +/* +// http://www.halcode.com/archives/2008/08/26/retrieving-system-time-gettimeofday/ +struct timezone +{ + int tz_minuteswest; + int tz_dsttime; +}; + +#ifdef __cplusplus + +void GetSystemTimeAsFileTime(FILETIME*); + +inline int gettimeofday(struct timeval* p, void* tz ) +{ + union { + long long ns100; // time since 1 Jan 1601 in 100ns units + FILETIME ft; + } now; + + GetSystemTimeAsFileTime( &(now.ft) ); + p->tv_usec=(long)((now.ns100 / 10LL) % 1000000LL ); + p->tv_sec= (long)((now.ns100-(116444736000000000LL))/10000000LL); + return 0; +} + +#else + int gettimeofday(struct timeval* p, void* tz ); +#endif +*/ + +#endif + +#endif diff --git a/src/raknet/rdlmalloc-options.h b/src/raknet/rdlmalloc-options.h index 37e9887..9f2e091 100755 --- a/src/raknet/rdlmalloc-options.h +++ b/src/raknet/rdlmalloc-options.h @@ -1,28 +1,28 @@ -#ifdef _RAKNET_SUPPORT_DL_MALLOC - -#ifndef __DLMALLOC_OPTIONS_H -#define __DLMALLOC_OPTIONS_H - -#include "NativeTypes.h" -#include "Export.h" -#include -#include - -#define MSPACES 1 -#define ONLY_MSPACES 1 -#define USE_DL_PREFIX 1 -#define NO_MALLINFO 1 -// Make it threadsafe -#define USE_LOCKS 1 - -extern RAK_DLL_EXPORT void * (*dlMallocMMap) (size_t size); -extern RAK_DLL_EXPORT void * (*dlMallocDirectMMap) (size_t size); -extern RAK_DLL_EXPORT int (*dlMallocMUnmap) (void* ptr, size_t size); - -#define MMAP(s) dlMallocMMap(s) -#define MUNMAP(a, s) dlMallocMUnmap((a), (s)) -#define DIRECT_MMAP(s) dlMallocDirectMMap(s) - -#endif - +#ifdef _RAKNET_SUPPORT_DL_MALLOC + +#ifndef __DLMALLOC_OPTIONS_H +#define __DLMALLOC_OPTIONS_H + +#include "NativeTypes.h" +#include "Export.h" +#include +#include + +#define MSPACES 1 +#define ONLY_MSPACES 1 +#define USE_DL_PREFIX 1 +#define NO_MALLINFO 1 +// Make it threadsafe +#define USE_LOCKS 1 + +extern RAK_DLL_EXPORT void * (*dlMallocMMap) (size_t size); +extern RAK_DLL_EXPORT void * (*dlMallocDirectMMap) (size_t size); +extern RAK_DLL_EXPORT int (*dlMallocMUnmap) (void* ptr, size_t size); + +#define MMAP(s) dlMallocMMap(s) +#define MUNMAP(a, s) dlMallocMUnmap((a), (s)) +#define DIRECT_MMAP(s) dlMallocDirectMMap(s) + +#endif + #endif // _RAKNET_SUPPORT_DL_MALLOC \ No newline at end of file diff --git a/src/raknet/rdlmalloc.cpp b/src/raknet/rdlmalloc.cpp index 2eb71f0..420ba6f 100755 --- a/src/raknet/rdlmalloc.cpp +++ b/src/raknet/rdlmalloc.cpp @@ -1,4161 +1,4161 @@ -#ifdef _RAKNET_SUPPORT_DL_MALLOC - -#include "rdlmalloc.h" - -/* --------------------------- Lock preliminaries ------------------------ */ - -/* -When locks are defined, there is one global lock, plus -one per-mspace lock. - -The global lock_ensures that mparams.magic and other unique -mparams values are initialized only once. It also protects -sequences of calls to MORECORE. In many cases sys_alloc requires -two calls, that should not be interleaved with calls by other -threads. This does not protect against direct calls to MORECORE -by other threads not using this lock, so there is still code to -cope the best we can on interference. - -Per-mspace locks surround calls to malloc, free, etc. To enable use -in layered extensions, per-mspace locks are reentrant. - -Because lock-protected regions generally have bounded times, it is -OK to use the supplied simple spinlocks in the custom versions for -x86. Spinlocks are likely to improve performance for lightly -contended applications, but worsen performance under heavy -contention. - -If USE_LOCKS is > 1, the definitions of lock routines here are -bypassed, in which case you will need to define the type MLOCK_T, -and at least INITIAL_LOCK, ACQUIRE_LOCK, RELEASE_LOCK and possibly -TRY_LOCK (which is not used in this malloc, but commonly needed in -extensions.) You must also declare a -static MLOCK_T malloc_global_mutex = { initialization values };. - -*/ - -#if USE_LOCKS == 1 - -#if USE_SPIN_LOCKS && SPIN_LOCKS_AVAILABLE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#if !defined(DL_PLATFORM_WIN32) - -/* Custom pthread-style spin locks on x86 and x64 for gcc */ -struct pthread_mlock_t { - volatile unsigned int l; - unsigned int c; - pthread_t threadid; -}; -#define MLOCK_T struct pthread_mlock_t -#define CURRENT_THREAD pthread_self() -#define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) -#define ACQUIRE_LOCK(sl) pthread_acquire_lock(sl) -#define RELEASE_LOCK(sl) pthread_release_lock(sl) -#define TRY_LOCK(sl) pthread_try_lock(sl) -#define SPINS_PER_YIELD 63 - -static MLOCK_T malloc_global_mutex = { 0, 0, 0}; - -static FORCEINLINE int pthread_acquire_lock (MLOCK_T *sl) { - int spins = 0; - volatile unsigned int* lp = &sl->l; - for (;;) { - if (*lp != 0) { - if (sl->threadid == CURRENT_THREAD) { - ++sl->c; - return 0; - } - } - else { - /* place args to cmpxchgl in locals to evade oddities in some gccs */ - int cmp = 0; - int val = 1; - int ret; - __asm__ __volatile__ ("lock; cmpxchgl %1, %2" - : "=a" (ret) - : "r" (val), "m" (*(lp)), "0"(cmp) - : "memory", "cc"); - if (!ret) { - assert(!sl->threadid); - sl->threadid = CURRENT_THREAD; - sl->c = 1; - return 0; - } - } - if ((++spins & SPINS_PER_YIELD) == 0) { -#if defined (__SVR4) && defined (__sun) /* solaris */ - thr_yield(); -#else -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) - sched_yield(); -#else /* no-op yield on unknown systems */ - ; -#endif /* __linux__ || __FreeBSD__ || __APPLE__ */ -#endif /* solaris */ - } - } -} - -static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) { - volatile unsigned int* lp = &sl->l; - assert(*lp != 0); - assert(sl->threadid == CURRENT_THREAD); - if (--sl->c == 0) { - sl->threadid = 0; - int prev = 0; - int ret; - __asm__ __volatile__ ("lock; xchgl %0, %1" - : "=r" (ret) - : "m" (*(lp)), "0"(prev) - : "memory"); - } -} - -static FORCEINLINE int pthread_try_lock (MLOCK_T *sl) { - volatile unsigned int* lp = &sl->l; - if (*lp != 0) { - if (sl->threadid == CURRENT_THREAD) { - ++sl->c; - return 1; - } - } - else { - int cmp = 0; - int val = 1; - int ret; - __asm__ __volatile__ ("lock; cmpxchgl %1, %2" - : "=a" (ret) - : "r" (val), "m" (*(lp)), "0"(cmp) - : "memory", "cc"); - if (!ret) { - assert(!sl->threadid); - sl->threadid = CURRENT_THREAD; - sl->c = 1; - return 1; - } - } - return 0; -} - - -#else /* DL_PLATFORM_WIN32 */ -/* Custom win32-style spin locks on x86 and x64 for MSC */ -struct win32_mlock_t { - volatile long l; - unsigned int c; - long threadid; -}; - -#define MLOCK_T struct win32_mlock_t -#define CURRENT_THREAD GetCurrentThreadId() -#define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) -#define ACQUIRE_LOCK(sl) win32_acquire_lock(sl) -#define RELEASE_LOCK(sl) win32_release_lock(sl) -#define TRY_LOCK(sl) win32_try_lock(sl) -#define SPINS_PER_YIELD 63 - -static MLOCK_T malloc_global_mutex = { 0, 0, 0}; - -static FORCEINLINE int win32_acquire_lock (MLOCK_T *sl) { - int spins = 0; - for (;;) { - if (sl->l != 0) { - if (sl->threadid == CURRENT_THREAD) { - ++sl->c; - return 0; - } - } - else { - if (!interlockedexchange(&sl->l, 1)) { - assert(!sl->threadid); - sl->threadid = CURRENT_THREAD; - sl->c = 1; - return 0; - } - } - if ((++spins & SPINS_PER_YIELD) == 0) - SleepEx(0, FALSE); - } -} - -static FORCEINLINE void win32_release_lock (MLOCK_T *sl) { - assert(sl->threadid == CURRENT_THREAD); - assert(sl->l != 0); - if (--sl->c == 0) { - sl->threadid = 0; - interlockedexchange (&sl->l, 0); - } -} - -static FORCEINLINE int win32_try_lock (MLOCK_T *sl) { - if (sl->l != 0) { - if (sl->threadid == CURRENT_THREAD) { - ++sl->c; - return 1; - } - } - else { - if (!interlockedexchange(&sl->l, 1)){ - assert(!sl->threadid); - sl->threadid = CURRENT_THREAD; - sl->c = 1; - return 1; - } - } - return 0; -} - -#endif /* DL_PLATFORM_WIN32 */ -#else /* USE_SPIN_LOCKS */ - -#ifndef DL_PLATFORM_WIN32 -/* pthreads-based locks */ - -#define MLOCK_T pthread_mutex_t -#define CURRENT_THREAD pthread_self() -#define INITIAL_LOCK(sl) pthread_init_lock(sl) -#define ACQUIRE_LOCK(sl) pthread_mutex_lock(sl) -#define RELEASE_LOCK(sl) pthread_mutex_unlock(sl) -#define TRY_LOCK(sl) (!pthread_mutex_trylock(sl)) - -static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; - -/* Cope with old-style linux recursive lock initialization by adding */ -/* skipped internal declaration from pthread.h */ -#ifdef linux -#ifndef PTHREAD_MUTEX_RECURSIVE -extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, - int __kind)); -#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP -#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) -#endif -#endif - -static int pthread_init_lock (MLOCK_T *sl) { - pthread_mutexattr_t attr; - if (pthread_mutexattr_init(&attr)) return 1; - if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; - if (pthread_mutex_init(sl, &attr)) return 1; - if (pthread_mutexattr_destroy(&attr)) return 1; - return 0; -} - -#else /* DL_PLATFORM_WIN32 */ -/* Win32 critical sections */ -#define MLOCK_T CRITICAL_SECTION -#define CURRENT_THREAD GetCurrentThreadId() -#define INITIAL_LOCK(s) (!InitializeCriticalSectionAndSpinCount((s), 0x80000000|4000)) -#define ACQUIRE_LOCK(s) (EnterCriticalSection(sl), 0) -#define RELEASE_LOCK(s) LeaveCriticalSection(sl) -#define TRY_LOCK(s) TryEnterCriticalSection(sl) -#define NEED_GLOBAL_LOCK_INIT - -static MLOCK_T malloc_global_mutex; -static volatile long malloc_global_mutex_status; - -/* Use spin loop to initialize global lock */ -static void init_malloc_global_mutex() { - for (;;) { - long stat = malloc_global_mutex_status; - if (stat > 0) - return; - /* transition to < 0 while initializing, then to > 0) */ - if (stat == 0 && - interlockedcompareexchange(&malloc_global_mutex_status, -1, 0) == 0) { - InitializeCriticalSection(&malloc_global_mutex); - interlockedexchange(&malloc_global_mutex_status,1); - return; - } - SleepEx(0, FALSE); - } -} - -#endif /* DL_PLATFORM_WIN32 */ -#endif /* USE_SPIN_LOCKS */ -#endif /* USE_LOCKS == 1 */ - -/* ----------------------- User-defined locks ------------------------ */ - -#if USE_LOCKS > 1 -/* Define your own lock implementation here */ -/* #define INITIAL_LOCK(sl) ... */ -/* #define ACQUIRE_LOCK(sl) ... */ -/* #define RELEASE_LOCK(sl) ... */ -/* #define TRY_LOCK(sl) ... */ -/* static MLOCK_T malloc_global_mutex = ... */ -#endif /* USE_LOCKS > 1 */ - -/* ----------------------- Lock-based state ------------------------ */ - -#if USE_LOCKS -#define USE_LOCK_BIT (2U) -#else /* USE_LOCKS */ -#define USE_LOCK_BIT (0U) -#define INITIAL_LOCK(l) -#endif /* USE_LOCKS */ - -#if USE_LOCKS -#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK -#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); -#endif -#ifndef RELEASE_MALLOC_GLOBAL_LOCK -#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); -#endif -#else /* USE_LOCKS */ -#define ACQUIRE_MALLOC_GLOBAL_LOCK() -#define RELEASE_MALLOC_GLOBAL_LOCK() -#endif /* USE_LOCKS */ - - -/* ----------------------- Chunk representations ------------------------ */ - -/* -(The following includes lightly edited explanations by Colin Plumb.) - -The malloc_chunk declaration below is misleading (but accurate and -necessary). It declares a "view" into memory allowing access to -necessary fields at known offsets from a given base. - -Chunks of memory are maintained using a `boundary tag' method as -originally described by Knuth. (See the paper by Paul Wilson -ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such -techniques.) Sizes of free chunks are stored both in the front of -each chunk and at the end. This makes consolidating fragmented -chunks into bigger chunks fast. The head fields also hold bits -representing whether chunks are free or in use. - -Here are some pictures to make it clearer. They are "exploded" to -show that the state of a chunk can be thought of as extending from -the high 31 bits of the head field of its header through the -prev_foot and PINUSE_BIT bit of the following chunk header. - -A chunk that's in use looks like: - -chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size of previous chunk (if P = 0) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| -| Size of this chunk 1| +-+ -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -+- -+ -| | -+- -+ -| : -+- size - sizeof(size_t) available payload bytes -+ -: | -chunk-> +- -+ -| | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| -| Size of next chunk (may or may not be in use) | +-+ -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -And if it's free, it looks like this: - -chunk-> +- -+ -| User payload (must be in use, or we would have merged!) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| -| Size of this chunk 0| +-+ -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Next pointer | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Prev pointer | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| : -+- size - sizeof(struct chunk) unused bytes -+ -: | -chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size of this chunk | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| -| Size of next chunk (must be in use, or we would have merged)| +-+ -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| : -+- User payload -+ -: | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -|0| -+-+ -Note that since we always merge adjacent free chunks, the chunks -adjacent to a free chunk must be in use. - -Given a pointer to a chunk (which can be derived trivially from the -payload pointer) we can, in O(1) time, find out whether the adjacent -chunks are free, and if so, unlink them from the lists that they -are on and merge them with the current chunk. - -Chunks always begin on even word boundaries, so the mem portion -(which is returned to the user) is also on an even word boundary, and -thus at least double-word aligned. - -The P (PINUSE_BIT) bit, stored in the unused low-order bit of the -chunk size (which is always a multiple of two words), is an in-use -bit for the *previous* chunk. If that bit is *clear*, then the -word before the current chunk size contains the previous chunk -size, and can be used to find the front of the previous chunk. -The very first chunk allocated always has this bit set, preventing -access to non-existent (or non-owned) memory. If pinuse is set for -any given chunk, then you CANNOT determine the size of the -previous chunk, and might even get a memory addressing fault when -trying to do so. - -The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of -the chunk size redundantly records whether the current chunk is -inuse (unless the chunk is mmapped). This redundancy enables usage -checks within free and realloc, and reduces indirection when freeing -and consolidating chunks. - -Each freshly allocated chunk must have both cinuse and pinuse set. -That is, each allocated chunk borders either a previously allocated -and still in-use chunk, or the base of its memory arena. This is -ensured by making all allocations from the the `lowest' part of any -found chunk. Further, no free chunk physically borders another one, -so each free chunk is known to be preceded and followed by either -inuse chunks or the ends of memory. - -Note that the `foot' of the current chunk is actually represented -as the prev_foot of the NEXT chunk. This makes it easier to -deal with alignments etc but can be very confusing when trying -to extend or adapt this code. - -The exceptions to all this are - -1. The special chunk `top' is the top-most available chunk (i.e., -the one bordering the end of available memory). It is treated -specially. Top is never included in any bin, is used only if -no other chunk is available, and is released back to the -system if it is very large (see M_TRIM_THRESHOLD). In effect, -the top chunk is treated as larger (and thus less well -fitting) than any other available chunk. The top chunk -doesn't update its trailing size field since there is no next -contiguous chunk that would have to index off it. However, -space is still allocated for it (TOP_FOOT_SIZE) to enable -separation or merging when space is extended. - -3. Chunks allocated via mmap, have both cinuse and pinuse bits -cleared in their head fields. Because they are allocated -one-by-one, each must carry its own prev_foot field, which is -also used to hold the offset this chunk has within its mmapped -region, which is needed to preserve alignment. Each mmapped -chunk is trailed by the first two fields of a fake next-chunk -for sake of usage checks. - -*/ - -struct malloc_chunk { - size_t prev_foot; /* Size of previous chunk (if free). */ - size_t head; /* Size and inuse bits. */ - struct malloc_chunk* fd; /* double links -- used only if free. */ - struct malloc_chunk* bk; -}; - -typedef struct malloc_chunk mchunk; -typedef struct malloc_chunk* mchunkptr; -typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ -typedef unsigned int bindex_t; /* Described below */ -typedef unsigned int binmap_t; /* Described below */ -typedef unsigned int flag_t; /* The type of various bit flag sets */ - -/* ------------------- Chunks sizes and alignments ----------------------- */ - -#define MCHUNK_SIZE (sizeof(mchunk)) - -#if FOOTERS -#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) -#else /* FOOTERS */ -#define CHUNK_OVERHEAD (SIZE_T_SIZE) -#endif /* FOOTERS */ - -/* MMapped chunks need a second word of overhead ... */ -#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) -/* ... and additional padding for fake next-chunk at foot */ -#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) - -/* The smallest size we can malloc is an aligned minimal chunk */ -#define MIN_CHUNK_SIZE\ - ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) - -/* conversion from malloc headers to user pointers, and back */ -#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) -#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) -/* chunk associated with aligned address A */ -#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) - -/* Bounds on request (not chunk) sizes. */ -#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) -#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) - -/* pad request bytes into a usable size */ -#define pad_request(req) \ - (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) - -/* pad request, checking for minimum (but not maximum) */ -#define request2size(req) \ - (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) - - -/* ------------------ Operations on head and foot fields ----------------- */ - -/* -The head field of a chunk is or'ed with PINUSE_BIT when previous -adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in -use, unless mmapped, in which case both bits are cleared. - -FLAG4_BIT is not used by this malloc, but might be useful in extensions. -*/ - -#define PINUSE_BIT (SIZE_T_ONE) -#define CINUSE_BIT (SIZE_T_TWO) -#define FLAG4_BIT (SIZE_T_FOUR) -#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) -#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) - -/* Head value for fenceposts */ -#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) - -/* extraction of fields from head words */ -#define cinuse(p) ((p)->head & CINUSE_BIT) -#define pinuse(p) ((p)->head & PINUSE_BIT) -#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) -#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) - -#define chunksize(p) ((p)->head & ~(FLAG_BITS)) - -#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) - -/* Treat space at ptr +/- offset as a chunk */ -#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) -#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) - -/* Ptr to next or previous physical malloc_chunk. */ -#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) -#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) - -/* extract next chunk's pinuse bit */ -#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) - -/* Get/set size at footer */ -#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) -#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) - -/* Set size, pinuse bit, and foot */ -#define set_size_and_pinuse_of_free_chunk(p, s)\ - ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) - -/* Set size, pinuse bit, foot, and clear next pinuse */ -#define set_free_with_pinuse(p, s, n)\ - (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) - -/* Get the internal overhead associated with chunk p */ -#define overhead_for(p)\ - (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) - -/* Return true if malloced space is not necessarily cleared */ -#if MMAP_CLEARS -#define calloc_must_clear(p) (!is_mmapped(p)) -#else /* MMAP_CLEARS */ -#define calloc_must_clear(p) (1) -#endif /* MMAP_CLEARS */ - -/* ---------------------- Overlaid data structures ----------------------- */ - -/* -When chunks are not in use, they are treated as nodes of either -lists or trees. - -"Small" chunks are stored in circular doubly-linked lists, and look -like this: - -chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size of previous chunk | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -`head:' | Size of chunk, in bytes |P| -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Forward pointer to next chunk in list | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Back pointer to previous chunk in list | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Unused space (may be 0 bytes long) . -. . -. | -nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -`foot:' | Size of chunk, in bytes | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -Larger chunks are kept in a form of bitwise digital trees (aka -tries) keyed on chunksizes. Because malloc_tree_chunks are only for -free chunks greater than 256 bytes, their size doesn't impose any -constraints on user chunk sizes. Each node looks like: - -chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size of previous chunk | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -`head:' | Size of chunk, in bytes |P| -mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Forward pointer to next chunk of same size | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Back pointer to previous chunk of same size | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Pointer to left child (child[0]) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Pointer to right child (child[1]) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Pointer to parent | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| bin index of this chunk | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Unused space . -. | -nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -`foot:' | Size of chunk, in bytes | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -Each tree holding treenodes is a tree of unique chunk sizes. Chunks -of the same size are arranged in a circularly-linked list, with only -the oldest chunk (the next to be used, in our FIFO ordering) -actually in the tree. (Tree members are distinguished by a non-null -parent pointer.) If a chunk with the same size an an existing node -is inserted, it is linked off the existing node using pointers that -work in the same way as fd/bk pointers of small chunks. - -Each tree contains a power of 2 sized range of chunk sizes (the -smallest is 0x100 <= x < 0x180), which is is divided in half at each -tree level, with the chunks in the smaller half of the range (0x100 -<= x < 0x140 for the top nose) in the left subtree and the larger -half (0x140 <= x < 0x180) in the right subtree. This is, of course, -done by inspecting individual bits. - -Using these rules, each node's left subtree contains all smaller -sizes than its right subtree. However, the node at the root of each -subtree has no particular ordering relationship to either. (The -dividing line between the subtree sizes is based on trie relation.) -If we remove the last chunk of a given size from the interior of the -tree, we need to replace it with a leaf node. The tree ordering -rules permit a node to be replaced by any leaf below it. - -The smallest chunk in a tree (a common operation in a best-fit -allocator) can be found by walking a path to the leftmost leaf in -the tree. Unlike a usual binary tree, where we follow left child -pointers until we reach a null, here we follow the right child -pointer any time the left one is null, until we reach a leaf with -both child pointers null. The smallest chunk in the tree will be -somewhere along that path. - -The worst case number of steps to add, find, or remove a node is -bounded by the number of bits differentiating chunks within -bins. Under current bin calculations, this ranges from 6 up to 21 -(for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case -is of course much better. -*/ - -struct malloc_tree_chunk { - /* The first four fields must be compatible with malloc_chunk */ - size_t prev_foot; - size_t head; - struct malloc_tree_chunk* fd; - struct malloc_tree_chunk* bk; - - struct malloc_tree_chunk* child[2]; - struct malloc_tree_chunk* parent; - bindex_t index; -}; - -typedef struct malloc_tree_chunk tchunk; -typedef struct malloc_tree_chunk* tchunkptr; -typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ - -/* A little helper macro for trees */ -#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) - -/* ----------------------------- Segments -------------------------------- */ - -/* -Each malloc space may include non-contiguous segments, held in a -list headed by an embedded malloc_segment record representing the -top-most space. Segments also include flags holding properties of -the space. Large chunks that are directly allocated by mmap are not -included in this list. They are instead independently created and -destroyed without otherwise keeping track of them. - -Segment management mainly comes into play for spaces allocated by -MMAP. Any call to MMAP might or might not return memory that is -adjacent to an existing segment. MORECORE normally contiguously -extends the current space, so this space is almost always adjacent, -which is simpler and faster to deal with. (This is why MORECORE is -used preferentially to MMAP when both are available -- see -sys_alloc.) When allocating using MMAP, we don't use any of the -hinting mechanisms (inconsistently) supported in various -implementations of unix mmap, or distinguish reserving from -committing memory. Instead, we just ask for space, and exploit -contiguity when we get it. It is probably possible to do -better than this on some systems, but no general scheme seems -to be significantly better. - -Management entails a simpler variant of the consolidation scheme -used for chunks to reduce fragmentation -- new adjacent memory is -normally prepended or appended to an existing segment. However, -there are limitations compared to chunk consolidation that mostly -reflect the fact that segment processing is relatively infrequent -(occurring only when getting memory from system) and that we -don't expect to have huge numbers of segments: - -* Segments are not indexed, so traversal requires linear scans. (It -would be possible to index these, but is not worth the extra -overhead and complexity for most programs on most platforms.) -* New segments are only appended to old ones when holding top-most -memory; if they cannot be prepended to others, they are held in -different segments. - -Except for the top-most segment of an mstate, each segment record -is kept at the tail of its segment. Segments are added by pushing -segment records onto the list headed by &mstate.seg for the -containing mstate. - -Segment flags control allocation/merge/deallocation policies: -* If EXTERN_BIT set, then we did not allocate this segment, -and so should not try to deallocate or merge with others. -(This currently holds only for the initial segment passed -into rak_create_mspace_with_base.) -* If USE_MMAP_BIT set, the segment may be merged with -other surrounding mmapped segments and trimmed/de-allocated -using munmap. -* If neither bit is set, then the segment was obtained using -MORECORE so can be merged with surrounding MORECORE'd segments -and deallocated/trimmed using MORECORE with negative arguments. -*/ - -struct malloc_segment { - char* base; /* base address */ - size_t size; /* allocated size */ - struct malloc_segment* next; /* ptr to next segment */ - flag_t sflags; /* mmap and extern flag */ -}; - -#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) -#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) - -typedef struct malloc_segment msegment; -typedef struct malloc_segment* msegmentptr; - -/* ---------------------------- malloc_state ----------------------------- */ - -/* -A malloc_state holds all of the bookkeeping for a space. -The main fields are: - -Top -The topmost chunk of the currently active segment. Its size is -cached in topsize. The actual size of topmost space is -topsize+TOP_FOOT_SIZE, which includes space reserved for adding -fenceposts and segment records if necessary when getting more -space from the system. The size at which to autotrim top is -cached from mparams in trim_check, except that it is disabled if -an autotrim fails. - -Designated victim (dv) -This is the preferred chunk for servicing small requests that -don't have exact fits. It is normally the chunk split off most -recently to service another small request. Its size is cached in -dvsize. The link fields of this chunk are not maintained since it -is not kept in a bin. - -SmallBins -An array of bin headers for free chunks. These bins hold chunks -with sizes less than MIN_LARGE_SIZE bytes. Each bin contains -chunks of all the same size, spaced 8 bytes apart. To simplify -use in double-linked lists, each bin header acts as a malloc_chunk -pointing to the real first node, if it exists (else pointing to -itself). This avoids special-casing for headers. But to avoid -waste, we allocate only the fd/bk pointers of bins, and then use -repositioning tricks to treat these as the fields of a chunk. - -TreeBins -Treebins are pointers to the roots of trees holding a range of -sizes. There are 2 equally spaced treebins for each power of two -from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything -larger. - -Bin maps -There is one bit map for small bins ("smallmap") and one for -treebins ("treemap). Each bin sets its bit when non-empty, and -clears the bit when empty. Bit operations are then used to avoid -bin-by-bin searching -- nearly all "search" is done without ever -looking at bins that won't be selected. The bit maps -conservatively use 32 bits per map word, even if on 64bit system. -For a good description of some of the bit-based techniques used -here, see Henry S. Warren Jr's book "Hacker's Delight" (and -supplement at http://hackersdelight.org/). Many of these are -intended to reduce the branchiness of paths through malloc etc, as -well as to reduce the number of memory locations read or written. - -Segments -A list of segments headed by an embedded malloc_segment record -representing the initial space. - -Address check support -The least_addr field is the least address ever obtained from -MORECORE or MMAP. Attempted frees and reallocs of any address less -than this are trapped (unless INSECURE is defined). - -Magic tag -A cross-check field that should always hold same value as mparams.magic. - -Flags -Bits recording whether to use MMAP, locks, or contiguous MORECORE - -Statistics -Each space keeps track of current and maximum system memory -obtained via MORECORE or MMAP. - -Trim support -Fields holding the amount of unused topmost memory that should trigger -timming, and a counter to force periodic scanning to release unused -non-topmost segments. - -Locking -If USE_LOCKS is defined, the "mutex" lock is acquired and released -around every public call using this mspace. - -Extension support -A void* pointer and a size_t field that can be used to help implement -extensions to this malloc. -*/ - -/* Bin types, widths and sizes */ -#define NSMALLBINS (32U) -#define NTREEBINS (32U) -#define SMALLBIN_SHIFT (3U) -#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) -#define TREEBIN_SHIFT (8U) -#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) -#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) -#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) - -struct malloc_state { - binmap_t smallmap; - binmap_t treemap; - size_t dvsize; - size_t topsize; - char* least_addr; - mchunkptr dv; - mchunkptr top; - size_t trim_check; - size_t release_checks; - size_t magic; - mchunkptr smallbins[(NSMALLBINS+1)*2]; - tbinptr treebins[NTREEBINS]; - size_t footprint; - size_t max_footprint; - flag_t mflags; -#if USE_LOCKS - MLOCK_T mutex; /* locate lock among fields that rarely change */ -#endif /* USE_LOCKS */ - msegment seg; - void* extp; /* Unused but available for extensions */ - size_t exts; -}; - -typedef struct malloc_state* mstate; - -/* ------------- Global malloc_state and malloc_params ------------------- */ - -/* -malloc_params holds global properties, including those that can be -dynamically set using mallopt. There is a single instance, mparams, -initialized in init_mparams. Note that the non-zeroness of "magic" -also serves as an initialization flag. -*/ - -struct malloc_params { - volatile size_t magic; - size_t page_size; - size_t granularity; - size_t mmap_threshold; - size_t trim_threshold; - flag_t default_mflags; -}; - -static struct malloc_params mparams; - -/* Ensure mparams initialized */ -#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) - -#if !ONLY_MSPACES - -/* The global malloc_state used for all non-"mspace" calls */ -static struct malloc_state _gm_; -#define gm (&_gm_) -#define is_global(M) ((M) == &_gm_) - -#endif /* !ONLY_MSPACES */ - -#define is_initialized(M) ((M)->top != 0) - -/* -------------------------- system alloc setup ------------------------- */ - -/* Operations on mflags */ - -#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) -#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) -#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) - -#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) -#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) -#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) - -#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) -#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) - -#define set_lock(M,L)\ - ((M)->mflags = (L)?\ - ((M)->mflags | USE_LOCK_BIT) :\ - ((M)->mflags & ~USE_LOCK_BIT)) - -/* page-align a size */ -#define page_align(S)\ - (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) - -/* granularity-align a size */ -#define granularity_align(S)\ - (((S) + (mparams.granularity - SIZE_T_ONE))\ - & ~(mparams.granularity - SIZE_T_ONE)) - - -/* For mmap, use granularity alignment on windows, else page-align */ -#ifdef DL_PLATFORM_WIN32 -#define mmap_align(S) granularity_align(S) -#else -#define mmap_align(S) page_align(S) -#endif - -/* For sys_alloc, enough padding to ensure can malloc request on success */ -#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) - -#define is_page_aligned(S)\ - (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) -#define is_granularity_aligned(S)\ - (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) - -/* True if segment S holds address A */ -#define segment_holds(S, A)\ - ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) - -/* Return segment holding given address */ -static msegmentptr segment_holding(mstate m, char* addr) { - msegmentptr sp = &m->seg; - for (;;) { - if (addr >= sp->base && addr < sp->base + sp->size) - return sp; - if ((sp = sp->next) == 0) - return 0; - } -} - -/* Return true if segment contains a segment link */ -static int has_segment_link(mstate m, msegmentptr ss) { - msegmentptr sp = &m->seg; - for (;;) { - if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) - return 1; - if ((sp = sp->next) == 0) - return 0; - } -} - -#ifndef MORECORE_CANNOT_TRIM -#define should_trim(M,s) ((s) > (M)->trim_check) -#else /* MORECORE_CANNOT_TRIM */ -#define should_trim(M,s) (0) -#endif /* MORECORE_CANNOT_TRIM */ - -/* -TOP_FOOT_SIZE is padding at the end of a segment, including space -that may be needed to place segment records and fenceposts when new -noncontiguous segments are added. -*/ -#define TOP_FOOT_SIZE\ - (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) - - -/* ------------------------------- Hooks -------------------------------- */ - -/* -PREACTION should be defined to return 0 on success, and nonzero on -failure. If you are not using locking, you can redefine these to do -anything you like. -*/ - -#if USE_LOCKS - -#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) -#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } -#else /* USE_LOCKS */ - -#ifndef PREACTION -#define PREACTION(M) (0) -#endif /* PREACTION */ - -#ifndef POSTACTION -#define POSTACTION(M) -#endif /* POSTACTION */ - -#endif /* USE_LOCKS */ - -/* -CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. -USAGE_ERROR_ACTION is triggered on detected bad frees and -reallocs. The argument p is an address that might have triggered the -fault. It is ignored by the two predefined actions, but might be -useful in custom actions that try to help diagnose errors. -*/ - -#if PROCEED_ON_ERROR - -/* A count of the number of corruption errors causing resets */ -int malloc_corruption_error_count; - -/* default corruption action */ -static void reset_on_error(mstate m); - -#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) -#define USAGE_ERROR_ACTION(m, p) - -#else /* PROCEED_ON_ERROR */ - -#ifndef CORRUPTION_ERROR_ACTION -#define CORRUPTION_ERROR_ACTION(m) ABORT -#endif /* CORRUPTION_ERROR_ACTION */ - -#ifndef USAGE_ERROR_ACTION -#define USAGE_ERROR_ACTION(m,p) ABORT -#endif /* USAGE_ERROR_ACTION */ - -#endif /* PROCEED_ON_ERROR */ - -/* -------------------------- Debugging setup ---------------------------- */ - -#if ! DEBUG - -#define check_free_chunk(M,P) -#define check_inuse_chunk(M,P) -#define check_malloced_chunk(M,P,N) -#define check_mmapped_chunk(M,P) -#define check_malloc_state(M) -#define check_top_chunk(M,P) - -#else /* DEBUG */ -#define check_free_chunk(M,P) do_check_free_chunk(M,P) -#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) -#define check_top_chunk(M,P) do_check_top_chunk(M,P) -#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) -#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) -#define check_malloc_state(M) do_check_malloc_state(M) - -static void do_check_any_chunk(mstate m, mchunkptr p); -static void do_check_top_chunk(mstate m, mchunkptr p); -static void do_check_mmapped_chunk(mstate m, mchunkptr p); -static void do_check_inuse_chunk(mstate m, mchunkptr p); -static void do_check_free_chunk(mstate m, mchunkptr p); -static void do_check_malloced_chunk(mstate m, void* mem, size_t s); -static void do_check_tree(mstate m, tchunkptr t); -static void do_check_treebin(mstate m, bindex_t i); -static void do_check_smallbin(mstate m, bindex_t i); -static void do_check_malloc_state(mstate m); -static int bin_find(mstate m, mchunkptr x); -static size_t traverse_and_check(mstate m); -#endif /* DEBUG */ - -/* ---------------------------- Indexing Bins ---------------------------- */ - -#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) -#define small_index(s) ((s) >> SMALLBIN_SHIFT) -#define small_index2size(i) ((i) << SMALLBIN_SHIFT) -#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) - -/* addressing by index. See above about smallbin repositioning */ -#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) -#define treebin_at(M,i) (&((M)->treebins[i])) - -/* assign tree index for size S to variable I. Use x86 asm if possible */ -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define compute_tree_index(S, I)\ -{\ - unsigned int X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K;\ - __asm__("bsrl\t%1, %0\n\t" : "=r" (K) : "g" (X));\ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ -}\ -} - -#elif defined (__INTEL_COMPILER) -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K = _bit_scan_reverse (X); \ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ -}\ -} - -#elif defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K;\ - _BitScanReverse((DWORD *) &K, X);\ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ -}\ -} - -#else /* GNUC */ -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int Y = (unsigned int)X;\ - unsigned int N = ((Y - 0x100) >> 16) & 8;\ - unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ - N += K;\ - N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ - K = 14 - N + ((Y <<= K) >> 15);\ - I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ -}\ -} -#endif /* GNUC */ - -/* Bit representing maximum resolved size in a treebin at i */ -#define bit_for_tree_index(i) \ - (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) - -/* Shift placing maximum resolved bit in a treebin at i as sign bit */ -#define leftshift_for_tree_index(i) \ - ((i == NTREEBINS-1)? 0 : \ - ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) - -/* The size of the smallest chunk held in bin with index i */ -#define minsize_for_tree_index(i) \ - ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ - (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) - - -/* ------------------------ Operations on bin maps ----------------------- */ - -/* bit corresponding to given index */ -#define idx2bit(i) ((binmap_t)(1) << (i)) - -/* Mark/Clear bits with given index */ -#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) -#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) -#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) - -#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) -#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) -#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) - -/* isolate the least set bit of a bitmap */ -#define least_bit(x) ((x) & -(x)) - -/* mask with all bits to left of least bit of x on */ -#define left_bits(x) ((x<<1) | -(x<<1)) - -/* mask with all bits to left of or equal to least bit of x on */ -#define same_or_left_bits(x) ((x) | -(x)) - -/* index corresponding to given bit. Use x86 asm if possible */ - -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - __asm__("bsfl\t%1, %0\n\t" : "=r" (J) : "g" (X));\ - I = (bindex_t)J;\ -} - -#elif defined (__INTEL_COMPILER) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - J = _bit_scan_forward (X); \ - I = (bindex_t)J;\ -} - -#elif defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - _BitScanForward((DWORD *) &J, X);\ - I = (bindex_t)J;\ -} - -#elif USE_BUILTIN_FFS -#define compute_bit2idx(X, I) I = ffs(X)-1 - -#else -#define compute_bit2idx(X, I)\ -{\ - unsigned int Y = X - 1;\ - unsigned int K = Y >> (16-4) & 16;\ - unsigned int N = K; Y >>= K;\ - N += K = Y >> (8-3) & 8; Y >>= K;\ - N += K = Y >> (4-2) & 4; Y >>= K;\ - N += K = Y >> (2-1) & 2; Y >>= K;\ - N += K = Y >> (1-0) & 1; Y >>= K;\ - I = (bindex_t)(N + Y);\ -} -#endif /* GNUC */ - - -/* ----------------------- Runtime Check Support ------------------------- */ - -/* -For security, the main invariant is that malloc/free/etc never -writes to a static address other than malloc_state, unless static -malloc_state itself has been corrupted, which cannot occur via -malloc (because of these checks). In essence this means that we -believe all pointers, sizes, maps etc held in malloc_state, but -check all of those linked or offsetted from other embedded data -structures. These checks are interspersed with main code in a way -that tends to minimize their run-time cost. - -When FOOTERS is defined, in addition to range checking, we also -verify footer fields of inuse chunks, which can be used guarantee -that the mstate controlling malloc/free is intact. This is a -streamlined version of the approach described by William Robertson -et al in "Run-time Detection of Heap-based Overflows" LISA'03 -http://www.usenix.org/events/lisa03/tech/robertson.html The footer -of an inuse chunk holds the xor of its mstate and a random seed, -that is checked upon calls to free() and realloc(). This is -(probablistically) unguessable from outside the program, but can be -computed by any code successfully malloc'ing any chunk, so does not -itself provide protection against code that has already broken -security through some other means. Unlike Robertson et al, we -always dynamically check addresses of all offset chunks (previous, -next, etc). This turns out to be cheaper than relying on hashes. -*/ - -#if !INSECURE -/* Check if address a is at least as high as any from MORECORE or MMAP */ -#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) -/* Check if address of next chunk n is higher than base chunk p */ -#define ok_next(p, n) ((char*)(p) < (char*)(n)) -/* Check if p has inuse status */ -#define ok_inuse(p) is_inuse(p) -/* Check if p has its pinuse bit on */ -#define ok_pinuse(p) pinuse(p) - -#else /* !INSECURE */ -#define ok_address(M, a) (1) -#define ok_next(b, n) (1) -#define ok_inuse(p) (1) -#define ok_pinuse(p) (1) -#endif /* !INSECURE */ - -#if (FOOTERS && !INSECURE) -/* Check if (alleged) mstate m has expected magic field */ -#define ok_magic(M) ((M)->magic == mparams.magic) -#else /* (FOOTERS && !INSECURE) */ -#define ok_magic(M) (1) -#endif /* (FOOTERS && !INSECURE) */ - - -/* In gcc, use __builtin_expect to minimize impact of checks */ -#if !INSECURE -#if defined(__GNUC__) && __GNUC__ >= 3 -#define RTCHECK(e) __builtin_expect(e, 1) -#else /* GNUC */ -#define RTCHECK(e) (e) -#endif /* GNUC */ -#else /* !INSECURE */ -#define RTCHECK(e) (1) -#endif /* !INSECURE */ - -/* macros to set up inuse chunks with or without footers */ - -#if !FOOTERS - -#define mark_inuse_foot(M,p,s) - -/* Macros for setting head/foot of non-mmapped chunks */ - -/* Set cinuse bit and pinuse bit of next chunk */ -#define set_inuse(M,p,s)\ - ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ - ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) - -/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ -#define set_inuse_and_pinuse(M,p,s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) - -/* Set size, cinuse and pinuse bit of this chunk */ -#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) - -#else /* FOOTERS */ - -/* Set foot of inuse chunk to be xor of mstate and seed */ -#define mark_inuse_foot(M,p,s)\ - (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) - -#define get_mstate_for(p)\ - ((mstate)(((mchunkptr)((char*)(p) +\ - (chunksize(p))))->prev_foot ^ mparams.magic)) - -#define set_inuse(M,p,s)\ - ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ - (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ - mark_inuse_foot(M,p,s)) - -#define set_inuse_and_pinuse(M,p,s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ - mark_inuse_foot(M,p,s)) - -#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - mark_inuse_foot(M, p, s)) - -#endif /* !FOOTERS */ - -/* ---------------------------- setting mparams -------------------------- */ - -/* Initialize mparams */ -static int init_mparams(void) { - - if (malloc_global_mutex_status <= 0) - init_malloc_global_mutex(); - - - ACQUIRE_MALLOC_GLOBAL_LOCK(); - if (mparams.magic == 0) { - size_t magic; - size_t psize; - size_t gsize; - -#ifndef DL_PLATFORM_WIN32 - psize = malloc_getpagesize; - gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); -#else /* DL_PLATFORM_WIN32 */ - { - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - psize = system_info.dwPageSize; - gsize = ((DEFAULT_GRANULARITY != 0)? -DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); - } -#endif /* DL_PLATFORM_WIN32 */ - - /* Sanity-check configuration: - size_t must be unsigned and as wide as pointer type. - ints must be at least 4 bytes. - alignment must be at least 8. - Alignment, min chunk size, and page size must all be powers of 2. - */ - if ((sizeof(size_t) != sizeof(char*)) || - (MAX_SIZE_T < MIN_CHUNK_SIZE) || - (sizeof(int) < 4) || - (MALLOC_ALIGNMENT < (size_t)8U) || - ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || - ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || - ((gsize & (gsize-SIZE_T_ONE)) != 0) || - ((psize & (psize-SIZE_T_ONE)) != 0)) - ABORT; - - mparams.granularity = gsize; - mparams.page_size = psize; - mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; - mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; -#if MORECORE_CONTIGUOUS - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; -#else /* MORECORE_CONTIGUOUS */ - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; -#endif /* MORECORE_CONTIGUOUS */ - -#if !ONLY_MSPACES - /* Set up lock for main malloc area */ - gm->mflags = mparams.default_mflags; - INITIAL_LOCK(&gm->mutex); -#endif - - { -#if USE_DEV_RANDOM - int fd; - unsigned char buf[sizeof(size_t)]; - /* Try to use /dev/urandom, else fall back on using time */ - if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && - read(fd, buf, sizeof(buf)) == sizeof(buf)) { - magic = *((size_t *) buf); - close(fd); - } - else -#endif /* USE_DEV_RANDOM */ - - - -#if defined(DL_PLATFORM_WIN32) - magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); -#else - magic = (size_t)(time(0) ^ (size_t)0x55555555U); -#endif - magic |= (size_t)8U; /* ensure nonzero */ - magic &= ~(size_t)7U; /* improve chances of fault for bad values */ - mparams.magic = magic; - } - } - - RELEASE_MALLOC_GLOBAL_LOCK(); - return 1; -} - -/* support for mallopt */ -static int change_mparam(int param_number, int value) { - size_t val; - ensure_initialization(); - val = (value == -1)? MAX_SIZE_T : (size_t)value; - switch(param_number) { - case M_TRIM_THRESHOLD: - mparams.trim_threshold = val; - return 1; - case M_GRANULARITY: - if (val >= mparams.page_size && ((val & (val-1)) == 0)) { - mparams.granularity = val; - return 1; - } - else - return 0; - case M_MMAP_THRESHOLD: - mparams.mmap_threshold = val; - return 1; - default: - return 0; - } -} - -#if DEBUG -/* ------------------------- Debugging Support --------------------------- */ - -/* Check properties of any chunk, whether free, inuse, mmapped etc */ -static void do_check_any_chunk(mstate m, mchunkptr p) { - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); - assert(ok_address(m, p)); -} - -/* Check properties of top chunk */ -static void do_check_top_chunk(mstate m, mchunkptr p) { - msegmentptr sp = segment_holding(m, (char*)p); - size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ - assert(sp != 0); - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); - assert(ok_address(m, p)); - assert(sz == m->topsize); - assert(sz > 0); - assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); - assert(pinuse(p)); - assert(!pinuse(chunk_plus_offset(p, sz))); -} - -/* Check properties of (inuse) mmapped chunks */ -static void do_check_mmapped_chunk(mstate m, mchunkptr p) { - size_t sz = chunksize(p); - size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); - assert(is_mmapped(p)); - assert(use_mmap(m)); - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); - assert(ok_address(m, p)); - assert(!is_small(sz)); - assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); - assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); - assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); -} - -/* Check properties of inuse chunks */ -static void do_check_inuse_chunk(mstate m, mchunkptr p) { - do_check_any_chunk(m, p); - assert(is_inuse(p)); - assert(next_pinuse(p)); - /* If not pinuse and not mmapped, previous chunk has OK offset */ - assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); - if (is_mmapped(p)) - do_check_mmapped_chunk(m, p); -} - -/* Check properties of free chunks */ -static void do_check_free_chunk(mstate m, mchunkptr p) { - size_t sz = chunksize(p); - mchunkptr next = chunk_plus_offset(p, sz); - do_check_any_chunk(m, p); - assert(!is_inuse(p)); - assert(!next_pinuse(p)); - assert (!is_mmapped(p)); - if (p != m->dv && p != m->top) { - if (sz >= MIN_CHUNK_SIZE) { - assert((sz & CHUNK_ALIGN_MASK) == 0); - assert(is_aligned(chunk2mem(p))); - assert(next->prev_foot == sz); - assert(pinuse(p)); - assert (next == m->top || is_inuse(next)); - assert(p->fd->bk == p); - assert(p->bk->fd == p); - } - else /* markers are always of size SIZE_T_SIZE */ - assert(sz == SIZE_T_SIZE); - } -} - -/* Check properties of malloced chunks at the point they are malloced */ -static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - size_t sz = p->head & ~INUSE_BITS; - do_check_inuse_chunk(m, p); - assert((sz & CHUNK_ALIGN_MASK) == 0); - assert(sz >= MIN_CHUNK_SIZE); - assert(sz >= s); - /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ - assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); - } -} - -/* Check a tree and its subtrees. */ -static void do_check_tree(mstate m, tchunkptr t) { - tchunkptr head = 0; - tchunkptr u = t; - bindex_t tindex = t->index; - size_t tsize = chunksize(t); - bindex_t idx; - compute_tree_index(tsize, idx); - assert(tindex == idx); - assert(tsize >= MIN_LARGE_SIZE); - assert(tsize >= minsize_for_tree_index(idx)); - assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); - - do { /* traverse through chain of same-sized nodes */ - do_check_any_chunk(m, ((mchunkptr)u)); - assert(u->index == tindex); - assert(chunksize(u) == tsize); - assert(!is_inuse(u)); - assert(!next_pinuse(u)); - assert(u->fd->bk == u); - assert(u->bk->fd == u); - if (u->parent == 0) { - assert(u->child[0] == 0); - assert(u->child[1] == 0); - } - else { - assert(head == 0); /* only one node on chain has parent */ - head = u; - assert(u->parent != u); - assert (u->parent->child[0] == u || - u->parent->child[1] == u || - *((tbinptr*)(u->parent)) == u); - if (u->child[0] != 0) { - assert(u->child[0]->parent == u); - assert(u->child[0] != u); - do_check_tree(m, u->child[0]); - } - if (u->child[1] != 0) { - assert(u->child[1]->parent == u); - assert(u->child[1] != u); - do_check_tree(m, u->child[1]); - } - if (u->child[0] != 0 && u->child[1] != 0) { - assert(chunksize(u->child[0]) < chunksize(u->child[1])); - } - } - u = u->fd; - } while (u != t); - assert(head != 0); -} - -/* Check all the chunks in a treebin. */ -static void do_check_treebin(mstate m, bindex_t i) { - tbinptr* tb = treebin_at(m, i); - tchunkptr t = *tb; - int empty = (m->treemap & (1U << i)) == 0; - if (t == 0) - assert(empty); - if (!empty) - do_check_tree(m, t); -} - -/* Check all the chunks in a smallbin. */ -static void do_check_smallbin(mstate m, bindex_t i) { - sbinptr b = smallbin_at(m, i); - mchunkptr p = b->bk; - unsigned int empty = (m->smallmap & (1U << i)) == 0; - if (p == b) - assert(empty); - if (!empty) { - for (; p != b; p = p->bk) { - size_t size = chunksize(p); - mchunkptr q; - /* each chunk claims to be free */ - do_check_free_chunk(m, p); - /* chunk belongs in bin */ - assert(small_index(size) == i); - assert(p->bk == b || chunksize(p->bk) == chunksize(p)); - /* chunk is followed by an inuse chunk */ - q = next_chunk(p); - if (q->head != FENCEPOST_HEAD) - do_check_inuse_chunk(m, q); - } - } -} - -/* Find x in a bin. Used in other check functions. */ -static int bin_find(mstate m, mchunkptr x) { - size_t size = chunksize(x); - if (is_small(size)) { - bindex_t sidx = small_index(size); - sbinptr b = smallbin_at(m, sidx); - if (smallmap_is_marked(m, sidx)) { - mchunkptr p = b; - do { - if (p == x) - return 1; - } while ((p = p->fd) != b); - } - } - else { - bindex_t tidx; - compute_tree_index(size, tidx); - if (treemap_is_marked(m, tidx)) { - tchunkptr t = *treebin_at(m, tidx); - size_t sizebits = size << leftshift_for_tree_index(tidx); - while (t != 0 && chunksize(t) != size) { - t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; - sizebits <<= 1; - } - if (t != 0) { - tchunkptr u = t; - do { - if (u == (tchunkptr)x) - return 1; - } while ((u = u->fd) != t); - } - } - } - return 0; -} - -/* Traverse each chunk and check it; return total */ -static size_t traverse_and_check(mstate m) { - size_t sum = 0; - if (is_initialized(m)) { - msegmentptr s = &m->seg; - sum += m->topsize + TOP_FOOT_SIZE; - while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - mchunkptr lastq = 0; - assert(pinuse(q)); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - sum += chunksize(q); - if (is_inuse(q)) { - assert(!bin_find(m, q)); - do_check_inuse_chunk(m, q); - } - else { - assert(q == m->dv || bin_find(m, q)); - assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ - do_check_free_chunk(m, q); - } - lastq = q; - q = next_chunk(q); - } - s = s->next; - } - } - return sum; -} - -/* Check all properties of malloc_state. */ -static void do_check_malloc_state(mstate m) { - bindex_t i; - size_t total; - /* check bins */ - for (i = 0; i < NSMALLBINS; ++i) - do_check_smallbin(m, i); - for (i = 0; i < NTREEBINS; ++i) - do_check_treebin(m, i); - - if (m->dvsize != 0) { /* check dv chunk */ - do_check_any_chunk(m, m->dv); - assert(m->dvsize == chunksize(m->dv)); - assert(m->dvsize >= MIN_CHUNK_SIZE); - assert(bin_find(m, m->dv) == 0); - } - - if (m->top != 0) { /* check top chunk */ - do_check_top_chunk(m, m->top); - /*assert(m->topsize == chunksize(m->top)); redundant */ - assert(m->topsize > 0); - assert(bin_find(m, m->top) == 0); - } - - total = traverse_and_check(m); - assert(total <= m->footprint); - assert(m->footprint <= m->max_footprint); -} -#endif /* DEBUG */ - -/* ----------------------------- statistics ------------------------------ */ - -#if !NO_MALLINFO -static struct mallinfo internal_mallinfo(mstate m) { - struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - ensure_initialization(); - if (!PREACTION(m)) { - check_malloc_state(m); - if (is_initialized(m)) { - size_t nfree = SIZE_T_ONE; /* top always free */ - size_t mfree = m->topsize + TOP_FOOT_SIZE; - size_t sum = mfree; - msegmentptr s = &m->seg; - while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - size_t sz = chunksize(q); - sum += sz; - if (!is_inuse(q)) { - mfree += sz; - ++nfree; - } - q = next_chunk(q); - } - s = s->next; - } - - nm.arena = sum; - nm.ordblks = nfree; - nm.hblkhd = m->footprint - sum; - nm.usmblks = m->max_footprint; - nm.uordblks = m->footprint - mfree; - nm.fordblks = mfree; - nm.keepcost = m->topsize; - } - - POSTACTION(m); - } - return nm; -} -#endif /* !NO_MALLINFO */ - -static void internal_malloc_stats(mstate m) { - ensure_initialization(); - if (!PREACTION(m)) { - size_t maxfp = 0; - size_t fp = 0; - size_t used = 0; - check_malloc_state(m); - if (is_initialized(m)) { - msegmentptr s = &m->seg; - maxfp = m->max_footprint; - fp = m->footprint; - used = fp - (m->topsize + TOP_FOOT_SIZE); - - while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - if (!is_inuse(q)) - used -= chunksize(q); - q = next_chunk(q); - } - s = s->next; - } - } - - fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); - fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); - fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); - - POSTACTION(m); - } -} - -/* ----------------------- Operations on smallbins ----------------------- */ - -/* -Various forms of linking and unlinking are defined as macros. Even -the ones for trees, which are very long but have very short typical -paths. This is ugly but reduces reliance on inlining support of -compilers. -*/ - -/* Link a free chunk into a smallbin */ -#define insert_small_chunk(M, P, S) {\ - bindex_t I = small_index(S);\ - mchunkptr B = smallbin_at(M, I);\ - mchunkptr F = B;\ - assert(S >= MIN_CHUNK_SIZE);\ - if (!smallmap_is_marked(M, I))\ - mark_smallmap(M, I);\ - else if (RTCHECK(ok_address(M, B->fd)))\ - F = B->fd;\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ - B->fd = P;\ - F->bk = P;\ - P->fd = F;\ - P->bk = B;\ -} - -/* Unlink a chunk from a smallbin */ -#define unlink_small_chunk(M, P, S) {\ - mchunkptr F = P->fd;\ - mchunkptr B = P->bk;\ - bindex_t I = small_index(S);\ - assert(P != B);\ - assert(P != F);\ - assert(chunksize(P) == small_index2size(I));\ - if (F == B)\ - clear_smallmap(M, I);\ - else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ - (B == smallbin_at(M,I) || ok_address(M, B)))) {\ - F->bk = B;\ - B->fd = F;\ -}\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ -} - -/* Unlink the first chunk from a smallbin */ -#define unlink_first_small_chunk(M, B, P, I) {\ - mchunkptr F = P->fd;\ - assert(P != B);\ - assert(P != F);\ - assert(chunksize(P) == small_index2size(I));\ - if (B == F)\ - clear_smallmap(M, I);\ - else if (RTCHECK(ok_address(M, F))) {\ - B->fd = F;\ - F->bk = B;\ -}\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ -} - - - -/* Replace dv node, binning the old one */ -/* Used only when dvsize known to be small */ -#define replace_dv(M, P, S) {\ - size_t DVS = M->dvsize;\ - if (DVS != 0) {\ - mchunkptr DV = M->dv;\ - assert(is_small(DVS));\ - insert_small_chunk(M, DV, DVS);\ - }\ - M->dvsize = S;\ - M->dv = P;\ -} - -/* ------------------------- Operations on trees ------------------------- */ - -/* Insert chunk into tree */ -#define insert_large_chunk(M, X, S) {\ - tbinptr* H;\ - bindex_t I;\ - compute_tree_index(S, I);\ - H = treebin_at(M, I);\ - X->index = I;\ - X->child[0] = X->child[1] = 0;\ - if (!treemap_is_marked(M, I)) {\ - mark_treemap(M, I);\ - *H = X;\ - X->parent = (tchunkptr)H;\ - X->fd = X->bk = X;\ - }\ - else {\ - tchunkptr T = *H;\ - size_t K = S << leftshift_for_tree_index(I);\ - for (;;) {\ - if (chunksize(T) != S) {\ - tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ - K <<= 1;\ - if (*C != 0)\ - T = *C;\ - else if (RTCHECK(ok_address(M, C))) {\ - *C = X;\ - X->parent = T;\ - X->fd = X->bk = X;\ - break;\ -}\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ -}\ - }\ - else {\ - tchunkptr F = T->fd;\ - if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ - T->fd = F->bk = X;\ - X->fd = F;\ - X->bk = T;\ - X->parent = 0;\ - break;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ -}\ -}\ - }\ -}\ -} - -/* -Unlink steps: - -1. If x is a chained node, unlink it from its same-sized fd/bk links -and choose its bk node as its replacement. -2. If x was the last node of its size, but not a leaf node, it must -be replaced with a leaf node (not merely one with an open left or -right), to make sure that lefts and rights of descendents -correspond properly to bit masks. We use the rightmost descendent -of x. We could use any other leaf, but this is easy to locate and -tends to counteract removal of leftmosts elsewhere, and so keeps -paths shorter than minimally guaranteed. This doesn't loop much -because on average a node in a tree is near the bottom. -3. If x is the base of a chain (i.e., has parent links) relink -x's parent and children to x's replacement (or null if none). -*/ - -#define unlink_large_chunk(M, X) {\ - tchunkptr XP = X->parent;\ - tchunkptr R;\ - if (X->bk != X) {\ - tchunkptr F = X->fd;\ - R = X->bk;\ - if (RTCHECK(ok_address(M, F))) {\ - F->bk = R;\ - R->fd = F;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ - }\ - else {\ - tchunkptr* RP;\ - if (((R = *(RP = &(X->child[1]))) != 0) ||\ - ((R = *(RP = &(X->child[0]))) != 0)) {\ - tchunkptr* CP;\ - while ((*(CP = &(R->child[1])) != 0) ||\ - (*(CP = &(R->child[0])) != 0)) {\ - R = *(RP = CP);\ -}\ - if (RTCHECK(ok_address(M, RP)))\ - *RP = 0;\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ -}\ -}\ -}\ - if (XP != 0) {\ - tbinptr* H = treebin_at(M, X->index);\ - if (X == *H) {\ - if ((*H = R) == 0) \ - clear_treemap(M, X->index);\ - }\ - else if (RTCHECK(ok_address(M, XP))) {\ - if (XP->child[0] == X) \ - XP->child[0] = R;\ - else \ - XP->child[1] = R;\ -}\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - if (R != 0) {\ - if (RTCHECK(ok_address(M, R))) {\ - tchunkptr C0, C1;\ - R->parent = XP;\ - if ((C0 = X->child[0]) != 0) {\ - if (RTCHECK(ok_address(M, C0))) {\ - R->child[0] = C0;\ - C0->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - if ((C1 = X->child[1]) != 0) {\ - if (RTCHECK(ok_address(M, C1))) {\ - R->child[1] = C1;\ - C1->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ -} - -/* Relays to large vs small bin operations */ - -#define insert_chunk(M, P, S)\ - if (is_small(S)) insert_small_chunk(M, P, S)\ - else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } - -#define unlink_chunk(M, P, S)\ - if (is_small(S)) unlink_small_chunk(M, P, S)\ - else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } - - -/* Relays to internal calls to malloc/free from realloc, memalign etc */ - -#if ONLY_MSPACES -#define internal_malloc(m, b) rak_mspace_malloc(m, b) -#define internal_free(m, mem) rak_mspace_free(m,mem); -#else /* ONLY_MSPACES */ -#if MSPACES -#define internal_malloc(m, b)\ - (m == gm)? rdlmalloc(b) : rak_mspace_malloc(m, b) -#define internal_free(m, mem)\ - if (m == gm) rdlfree(mem); else rak_mspace_free(m,mem); -#else /* MSPACES */ -#define internal_malloc(m, b) rdlmalloc(b) -#define internal_free(m, mem) rdlfree(mem) -#endif /* MSPACES */ -#endif /* ONLY_MSPACES */ - -/* ----------------------- Direct-mmapping chunks ----------------------- */ - -/* -Directly mmapped chunks are set up with an offset to the start of -the mmapped region stored in the prev_foot field of the chunk. This -allows reconstruction of the required argument to MUNMAP when freed, -and also allows adjustment of the returned chunk to meet alignment -requirements (especially in memalign). -*/ - -/* Malloc using mmap */ -static void* mmap_alloc(mstate m, size_t nb) { - size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - if (mmsize > nb) { /* Check for wrap around 0 */ - char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); - if (mm != CMFAIL) { - size_t offset = align_offset(chunk2mem(mm)); - size_t psize = mmsize - offset - MMAP_FOOT_PAD; - mchunkptr p = (mchunkptr)(mm + offset); - p->prev_foot = offset; - p->head = psize; - mark_inuse_foot(m, p, psize); - chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; - - if (m->least_addr == 0 || mm < m->least_addr) - m->least_addr = mm; - if ((m->footprint += mmsize) > m->max_footprint) - m->max_footprint = m->footprint; - assert(is_aligned(chunk2mem(p))); - check_mmapped_chunk(m, p); - return chunk2mem(p); - } - } - return 0; -} - -/* Realloc using mmap */ -static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { - size_t oldsize = chunksize(oldp); - if (is_small(nb)) /* Can't shrink mmap regions below small size */ - return 0; - /* Keep old chunk if big enough but not too big */ - if (oldsize >= nb + SIZE_T_SIZE && - (oldsize - nb) <= (mparams.granularity << 1)) - return oldp; - else { - size_t offset = oldp->prev_foot; - size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; - size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - char* cp = (char*)CALL_MREMAP((char*)oldp - offset, - oldmmsize, newmmsize, 1); - if (cp != CMFAIL) { - mchunkptr newp = (mchunkptr)(cp + offset); - size_t psize = newmmsize - offset - MMAP_FOOT_PAD; - newp->head = psize; - mark_inuse_foot(m, newp, psize); - chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; - - if (cp < m->least_addr) - m->least_addr = cp; - if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) - m->max_footprint = m->footprint; - check_mmapped_chunk(m, newp); - return newp; - } - } - return 0; -} - -/* -------------------------- mspace management -------------------------- */ - -/* Initialize top chunk and its size */ -static void init_top(mstate m, mchunkptr p, size_t psize) { - /* Ensure alignment */ - size_t offset = align_offset(chunk2mem(p)); - p = (mchunkptr)((char*)p + offset); - psize -= offset; - - m->top = p; - m->topsize = psize; - p->head = psize | PINUSE_BIT; - /* set size of fake trailing chunk holding overhead space only once */ - chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; - m->trim_check = mparams.trim_threshold; /* reset on each update */ -} - -/* Initialize bins for a new mstate that is otherwise zeroed out */ -static void init_bins(mstate m) { - /* Establish circular links for smallbins */ - bindex_t i; - for (i = 0; i < NSMALLBINS; ++i) { - sbinptr bin = smallbin_at(m,i); - bin->fd = bin->bk = bin; - } -} - -#if PROCEED_ON_ERROR - -/* default corruption action */ -static void reset_on_error(mstate m) { - int i; - ++malloc_corruption_error_count; - /* Reinitialize fields to forget about all memory */ - m->smallbins = m->treebins = 0; - m->dvsize = m->topsize = 0; - m->seg.base = 0; - m->seg.size = 0; - m->seg.next = 0; - m->top = m->dv = 0; - for (i = 0; i < NTREEBINS; ++i) - *treebin_at(m, i) = 0; - init_bins(m); -} -#endif /* PROCEED_ON_ERROR */ - -/* Allocate chunk and prepend remainder with chunk in successor base. */ -static void* prepend_alloc(mstate m, char* newbase, char* oldbase, - size_t nb) { - mchunkptr p = align_as_chunk(newbase); - mchunkptr oldfirst = align_as_chunk(oldbase); - size_t psize = (char*)oldfirst - (char*)p; - mchunkptr q = chunk_plus_offset(p, nb); - size_t qsize = psize - nb; - set_size_and_pinuse_of_inuse_chunk(m, p, nb); - - assert((char*)oldfirst > (char*)q); - assert(pinuse(oldfirst)); - assert(qsize >= MIN_CHUNK_SIZE); - - /* consolidate remainder with first chunk of old base */ - if (oldfirst == m->top) { - size_t tsize = m->topsize += qsize; - m->top = q; - q->head = tsize | PINUSE_BIT; - check_top_chunk(m, q); - } - else if (oldfirst == m->dv) { - size_t dsize = m->dvsize += qsize; - m->dv = q; - set_size_and_pinuse_of_free_chunk(q, dsize); - } - else { - if (!is_inuse(oldfirst)) { - size_t nsize = chunksize(oldfirst); - unlink_chunk(m, oldfirst, nsize); - oldfirst = chunk_plus_offset(oldfirst, nsize); - qsize += nsize; - } - set_free_with_pinuse(q, qsize, oldfirst); - insert_chunk(m, q, qsize); - check_free_chunk(m, q); - } - - check_malloced_chunk(m, chunk2mem(p), nb); - return chunk2mem(p); -} - -/* Add a segment to hold a new noncontiguous region */ -static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { - /* Determine locations and sizes of segment, fenceposts, old top */ - char* old_top = (char*)m->top; - msegmentptr oldsp = segment_holding(m, old_top); - char* old_end = oldsp->base + oldsp->size; - size_t ssize = pad_request(sizeof(struct malloc_segment)); - char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - size_t offset = align_offset(chunk2mem(rawsp)); - char* asp = rawsp + offset; - char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; - mchunkptr sp = (mchunkptr)csp; - msegmentptr ss = (msegmentptr)(chunk2mem(sp)); - mchunkptr tnext = chunk_plus_offset(sp, ssize); - mchunkptr p = tnext; - int nfences = 0; - - /* reset top to new space */ - init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); - - /* Set up segment record */ - assert(is_aligned(ss)); - set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); - *ss = m->seg; /* Push current record */ - m->seg.base = tbase; - m->seg.size = tsize; - m->seg.sflags = mmapped; - m->seg.next = ss; - - /* Insert trailing fenceposts */ - for (;;) { - mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); - p->head = FENCEPOST_HEAD; - ++nfences; - if ((char*)(&(nextp->head)) < old_end) - p = nextp; - else - break; - } - assert(nfences >= 2); - - /* Insert the rest of old top into a bin as an ordinary free chunk */ - if (csp != old_top) { - mchunkptr q = (mchunkptr)old_top; - size_t psize = csp - old_top; - mchunkptr tn = chunk_plus_offset(q, psize); - set_free_with_pinuse(q, psize, tn); - insert_chunk(m, q, psize); - } - - check_top_chunk(m, m->top); -} - -/* -------------------------- System allocation -------------------------- */ - -/* Get memory from system using MORECORE or MMAP */ -static void* sys_alloc(mstate m, size_t nb) { - char* tbase = CMFAIL; - size_t tsize = 0; - flag_t mmap_flag = 0; - - ensure_initialization(); - - /* Directly map large chunks, but only if already initialized */ - if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { - void* mem = mmap_alloc(m, nb); - if (mem != 0) - return mem; - } - - /* - Try getting memory in any of three ways (in most-preferred to - least-preferred order): - 1. A call to MORECORE that can normally contiguously extend memory. - (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or - or main space is mmapped or a previous contiguous call failed) - 2. A call to MMAP new space (disabled if not HAVE_MMAP). - Note that under the default settings, if MORECORE is unable to - fulfill a request, and HAVE_MMAP is true, then mmap is - used as a noncontiguous system allocator. This is a useful backup - strategy for systems with holes in address spaces -- in this case - sbrk cannot contiguously expand the heap, but mmap may be able to - find space. - 3. A call to MORECORE that cannot usually contiguously extend memory. - (disabled if not HAVE_MORECORE) - - In all cases, we need to request enough bytes from system to ensure - we can malloc nb bytes upon success, so pad with enough space for - top_foot, plus alignment-pad to make sure we don't lose bytes if - not on boundary, and round this up to a granularity unit. - */ - - if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { - char* br = CMFAIL; - msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); - size_t asize = 0; - ACQUIRE_MALLOC_GLOBAL_LOCK(); - - if (ss == 0) { /* First time through or recovery */ - char* base = (char*)CALL_MORECORE(0); - if (base != CMFAIL) { - asize = granularity_align(nb + SYS_ALLOC_PADDING); - /* Adjust to end on a page boundary */ - if (!is_page_aligned(base)) - asize += (page_align((size_t)base) - (size_t)base); - /* Can't call MORECORE if size is negative when treated as signed */ - if (asize < HALF_MAX_SIZE_T && - (br = (char*)(CALL_MORECORE(asize))) == base) { - tbase = base; - tsize = asize; - } - } - } - else { - /* Subtract out existing available top space from MORECORE request. */ - asize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); - /* Use mem here only if it did continuously extend old space */ - if (asize < HALF_MAX_SIZE_T && - (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) { - tbase = br; - tsize = asize; - } - } - - if (tbase == CMFAIL) { /* Cope with partial failure */ - if (br != CMFAIL) { /* Try to use/extend the space we did get */ - if (asize < HALF_MAX_SIZE_T && - asize < nb + SYS_ALLOC_PADDING) { - size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - asize); - if (esize < HALF_MAX_SIZE_T) { - char* end = (char*)CALL_MORECORE(esize); - if (end != CMFAIL) - asize += esize; - else { /* Can't use; try to release */ - (void) CALL_MORECORE(-asize); - br = CMFAIL; - } - } - } - } - if (br != CMFAIL) { /* Use the space we did get */ - tbase = br; - tsize = asize; - } - else - disable_contiguous(m); /* Don't try contiguous path in the future */ - } - - RELEASE_MALLOC_GLOBAL_LOCK(); - } - - if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ - size_t rsize = granularity_align(nb + SYS_ALLOC_PADDING); - if (rsize > nb) { /* Fail if wraps around zero */ - char* mp = (char*)(CALL_MMAP(rsize)); - if (mp != CMFAIL) { - tbase = mp; - tsize = rsize; - mmap_flag = USE_MMAP_BIT; - } - } - } - - if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ - size_t asize = granularity_align(nb + SYS_ALLOC_PADDING); - if (asize < HALF_MAX_SIZE_T) { - char* br = CMFAIL; - char* end = CMFAIL; - ACQUIRE_MALLOC_GLOBAL_LOCK(); - br = (char*)(CALL_MORECORE(asize)); - end = (char*)(CALL_MORECORE(0)); - RELEASE_MALLOC_GLOBAL_LOCK(); - if (br != CMFAIL && end != CMFAIL && br < end) { - size_t ssize = end - br; - if (ssize > nb + TOP_FOOT_SIZE) { - tbase = br; - tsize = ssize; - } - } - } - } - - if (tbase != CMFAIL) { - - if ((m->footprint += tsize) > m->max_footprint) - m->max_footprint = m->footprint; - - if (!is_initialized(m)) { /* first-time initialization */ - if (m->least_addr == 0 || tbase < m->least_addr) - m->least_addr = tbase; - m->seg.base = tbase; - m->seg.size = tsize; - m->seg.sflags = mmap_flag; - m->magic = mparams.magic; - m->release_checks = MAX_RELEASE_CHECK_RATE; - init_bins(m); -#if !ONLY_MSPACES - if (is_global(m)) - init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); - else -#endif - { - /* Offset top by embedded malloc_state */ - mchunkptr mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); - } - } - - else { - /* Try to merge with an existing segment */ - msegmentptr sp = &m->seg; - /* Only consider most recent segment if traversal suppressed */ - while (sp != 0 && tbase != sp->base + sp->size) - sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && - !is_extern_segment(sp) && - (sp->sflags & USE_MMAP_BIT) == mmap_flag && - segment_holds(sp, m->top)) { /* append */ - sp->size += tsize; - init_top(m, m->top, m->topsize + tsize); - } - else { - if (tbase < m->least_addr) - m->least_addr = tbase; - sp = &m->seg; - while (sp != 0 && sp->base != tbase + tsize) - sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && - !is_extern_segment(sp) && - (sp->sflags & USE_MMAP_BIT) == mmap_flag) { - char* oldbase = sp->base; - sp->base = tbase; - sp->size += tsize; - return prepend_alloc(m, tbase, oldbase, nb); - } - else - add_segment(m, tbase, tsize, mmap_flag); - } - } - - if (nb < m->topsize) { /* Allocate from new or extended top space */ - size_t rsize = m->topsize -= nb; - mchunkptr p = m->top; - mchunkptr r = m->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(m, p, nb); - check_top_chunk(m, m->top); - check_malloced_chunk(m, chunk2mem(p), nb); - return chunk2mem(p); - } - } - - MALLOC_FAILURE_ACTION; - return 0; -} - -/* ----------------------- system deallocation -------------------------- */ - -/* Unmap and unlink any mmapped segments that don't contain used chunks */ -static size_t release_unused_segments(mstate m) { - size_t released = 0; - int nsegs = 0; - msegmentptr pred = &m->seg; - msegmentptr sp = pred->next; - while (sp != 0) { - char* base = sp->base; - size_t size = sp->size; - msegmentptr next = sp->next; - ++nsegs; - if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { - mchunkptr p = align_as_chunk(base); - size_t psize = chunksize(p); - /* Can unmap if first chunk holds entire segment and not pinned */ - if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { - tchunkptr tp = (tchunkptr)p; - assert(segment_holds(sp, (char*)sp)); - if (p == m->dv) { - m->dv = 0; - m->dvsize = 0; - } - else { - unlink_large_chunk(m, tp); - } - if (CALL_MUNMAP(base, size) == 0) { - released += size; - m->footprint -= size; - /* unlink obsoleted record */ - sp = pred; - sp->next = next; - } - else { /* back out if cannot unmap */ - insert_large_chunk(m, tp, psize); - } - } - } - if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ - break; - pred = sp; - sp = next; - } - /* Reset check counter */ - m->release_checks = ((nsegs > MAX_RELEASE_CHECK_RATE)? -nsegs : MAX_RELEASE_CHECK_RATE); - return released; -} - -static int sys_trim(mstate m, size_t pad) { - size_t released = 0; - ensure_initialization(); - if (pad < MAX_REQUEST && is_initialized(m)) { - pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ - - if (m->topsize > pad) { - /* Shrink top space in granularity-size units, keeping at least one */ - size_t unit = mparams.granularity; - size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - - SIZE_T_ONE) * unit; - msegmentptr sp = segment_holding(m, (char*)m->top); - - if (!is_extern_segment(sp)) { - if (is_mmapped_segment(sp)) { - if (HAVE_MMAP && - sp->size >= extra && - !has_segment_link(m, sp)) { /* can't shrink if pinned */ - size_t newsize = sp->size - extra; - /* Prefer mremap, fall back to munmap */ - if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || - (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { - released = extra; - } - } - } - else if (HAVE_MORECORE) { - if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ - extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; - ACQUIRE_MALLOC_GLOBAL_LOCK(); - { - /* Make sure end of memory is where we last set it. */ - char* old_br = (char*)(CALL_MORECORE(0)); - if (old_br == sp->base + sp->size) { - char* rel_br = (char*)(CALL_MORECORE(-extra)); - char* new_br = (char*)(CALL_MORECORE(0)); - if (rel_br != CMFAIL && new_br < old_br) - released = old_br - new_br; - } - } - RELEASE_MALLOC_GLOBAL_LOCK(); - } - } - - if (released != 0) { - sp->size -= released; - m->footprint -= released; - init_top(m, m->top, m->topsize - released); - check_top_chunk(m, m->top); - } - } - - /* Unmap any unused mmapped segments */ - if (HAVE_MMAP) - released += release_unused_segments(m); - - /* On failure, disable autotrim to avoid repeated failed future calls */ - if (released == 0 && m->topsize > m->trim_check) - m->trim_check = MAX_SIZE_T; - } - - return (released != 0)? 1 : 0; -} - - -/* ---------------------------- malloc support --------------------------- */ - -/* allocate a large request from the best fitting chunk in a treebin */ -static void* tmalloc_large(mstate m, size_t nb) { - tchunkptr v = 0; - size_t rsize = -nb; /* Unsigned negation */ - tchunkptr t; - bindex_t idx; - compute_tree_index(nb, idx); - if ((t = *treebin_at(m, idx)) != 0) { - /* Traverse tree for this bin looking for node with size == nb */ - size_t sizebits = nb << leftshift_for_tree_index(idx); - tchunkptr rst = 0; /* The deepest untaken right subtree */ - for (;;) { - tchunkptr rt; - size_t trem = chunksize(t) - nb; - if (trem < rsize) { - v = t; - if ((rsize = trem) == 0) - break; - } - rt = t->child[1]; - t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; - if (rt != 0 && rt != t) - rst = rt; - if (t == 0) { - t = rst; /* set t to least subtree holding sizes > nb */ - break; - } - sizebits <<= 1; - } - } - if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ - binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; - if (leftbits != 0) { - bindex_t i; - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - t = *treebin_at(m, i); - } - } - - while (t != 0) { /* find smallest of tree or subtree */ - size_t trem = chunksize(t) - nb; - if (trem < rsize) { - rsize = trem; - v = t; - } - t = leftmost_child(t); - } - - /* If dv is a better fit, return 0 so malloc will use it */ - if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { - if (RTCHECK(ok_address(m, v))) { /* split */ - mchunkptr r = chunk_plus_offset(v, nb); - assert(chunksize(v) == rsize + nb); - if (RTCHECK(ok_next(v, r))) { - unlink_large_chunk(m, v); - if (rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(m, v, (rsize + nb)); - else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - insert_chunk(m, r, rsize); - } - return chunk2mem(v); - } - } - CORRUPTION_ERROR_ACTION(m); - } - return 0; -} - -/* allocate a small request from the best fitting chunk in a treebin */ -static void* tmalloc_small(mstate m, size_t nb) { - tchunkptr t, v; - size_t rsize; - bindex_t i; - binmap_t leastbit = least_bit(m->treemap); - compute_bit2idx(leastbit, i); - v = t = *treebin_at(m, i); - rsize = chunksize(t) - nb; - - while ((t = leftmost_child(t)) != 0) { - size_t trem = chunksize(t) - nb; - if (trem < rsize) { - rsize = trem; - v = t; - } - } - - if (RTCHECK(ok_address(m, v))) { - mchunkptr r = chunk_plus_offset(v, nb); - assert(chunksize(v) == rsize + nb); - if (RTCHECK(ok_next(v, r))) { - unlink_large_chunk(m, v); - if (rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(m, v, (rsize + nb)); - else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(m, r, rsize); - } - return chunk2mem(v); - } - } - - CORRUPTION_ERROR_ACTION(m); - return 0; -} - -/* --------------------------- realloc support --------------------------- */ - -static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { - if (bytes >= MAX_REQUEST) { - MALLOC_FAILURE_ACTION; - return 0; - } - if (!PREACTION(m)) { - mchunkptr oldp = mem2chunk(oldmem); - size_t oldsize = chunksize(oldp); - mchunkptr next = chunk_plus_offset(oldp, oldsize); - mchunkptr newp = 0; - void* extra = 0; - - /* Try to either shrink or extend into top. Else malloc-copy-free */ - - if (RTCHECK(ok_address(m, oldp) && ok_inuse(oldp) && - ok_next(oldp, next) && ok_pinuse(next))) { - size_t nb = request2size(bytes); - if (is_mmapped(oldp)) - newp = mmap_resize(m, oldp, nb); - else if (oldsize >= nb) { /* already big enough */ - size_t rsize = oldsize - nb; - newp = oldp; - if (rsize >= MIN_CHUNK_SIZE) { - mchunkptr remainder = chunk_plus_offset(newp, nb); - set_inuse(m, newp, nb); - set_inuse_and_pinuse(m, remainder, rsize); - extra = chunk2mem(remainder); - } - } - else if (next == m->top && oldsize + m->topsize > nb) { - /* Expand into top */ - size_t newsize = oldsize + m->topsize; - size_t newtopsize = newsize - nb; - mchunkptr newtop = chunk_plus_offset(oldp, nb); - set_inuse(m, oldp, nb); - newtop->head = newtopsize |PINUSE_BIT; - m->top = newtop; - m->topsize = newtopsize; - newp = oldp; - } - } - else { - USAGE_ERROR_ACTION(m, oldmem); - POSTACTION(m); - return 0; - } -#if DEBUG - if (newp != 0) { - check_inuse_chunk(m, newp); /* Check requires lock */ - } -#endif - - POSTACTION(m); - - if (newp != 0) { - if (extra != 0) { - internal_free(m, extra); - } - return chunk2mem(newp); - } - else { - void* newmem = internal_malloc(m, bytes); - if (newmem != 0) { - size_t oc = oldsize - overhead_for(oldp); - memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); - internal_free(m, oldmem); - } - return newmem; - } - } - return 0; -} - -/* --------------------------- memalign support -------------------------- */ - -static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { - if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ - return internal_malloc(m, bytes); - if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ - alignment = MIN_CHUNK_SIZE; - if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ - size_t a = MALLOC_ALIGNMENT << 1; - while (a < alignment) a <<= 1; - alignment = a; - } - - if (bytes >= MAX_REQUEST - alignment) { - if (m != 0) { /* Test isn't needed but avoids compiler warning */ - MALLOC_FAILURE_ACTION; - } - } - else { - size_t nb = request2size(bytes); - size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; - char* mem = (char*)internal_malloc(m, req); - if (mem != 0) { - void* leader = 0; - void* trailer = 0; - mchunkptr p = mem2chunk(mem); - - if (PREACTION(m)) return 0; - if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ - /* - Find an aligned spot inside chunk. Since we need to give - back leading space in a chunk of at least MIN_CHUNK_SIZE, if - the first calculation places us at a spot with less than - MIN_CHUNK_SIZE leader, we can move to the next aligned spot. - We've allocated enough total room so that this is always - possible. - */ - char* br = (char*)mem2chunk((size_t)(((size_t)(mem + - alignment - - SIZE_T_ONE)) & - -alignment)); - char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? -br : br+alignment; - mchunkptr newp = (mchunkptr)pos; - size_t leadsize = pos - (char*)(p); - size_t newsize = chunksize(p) - leadsize; - - if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ - newp->prev_foot = p->prev_foot + leadsize; - newp->head = newsize; - } - else { /* Otherwise, give back leader, use the rest */ - set_inuse(m, newp, newsize); - set_inuse(m, p, leadsize); - leader = chunk2mem(p); - } - p = newp; - } - - /* Give back spare room at the end */ - if (!is_mmapped(p)) { - size_t size = chunksize(p); - if (size > nb + MIN_CHUNK_SIZE) { - size_t remainder_size = size - nb; - mchunkptr remainder = chunk_plus_offset(p, nb); - set_inuse(m, p, nb); - set_inuse(m, remainder, remainder_size); - trailer = chunk2mem(remainder); - } - } - - assert (chunksize(p) >= nb); - assert((((size_t)(chunk2mem(p))) % alignment) == 0); - check_inuse_chunk(m, p); - POSTACTION(m); - if (leader != 0) { - internal_free(m, leader); - } - if (trailer != 0) { - internal_free(m, trailer); - } - return chunk2mem(p); - } - } - return 0; -} - -/* ------------------------ comalloc/coalloc support --------------------- */ - -static void** ialloc(mstate m, - size_t n_elements, - size_t* sizes, - int opts, - void* chunks[]) { - /* - This provides common support for independent_X routines, handling - all of the combinations that can result. - - The opts arg has: - bit 0 set if all elements are same size (using sizes[0]) - bit 1 set if elements should be zeroed - */ - - size_t element_size; /* chunksize of each element, if all same */ - size_t contents_size; /* total size of elements */ - size_t array_size; /* request size of pointer array */ - void* mem; /* malloced aggregate space */ - mchunkptr p; /* corresponding chunk */ - size_t remainder_size; /* remaining bytes while splitting */ - void** marray; /* either "chunks" or malloced ptr array */ - mchunkptr array_chunk; /* chunk for malloced ptr array */ - flag_t was_enabled; /* to disable mmap */ - size_t size; - size_t i; - - ensure_initialization(); - /* compute array length, if needed */ - if (chunks != 0) { - if (n_elements == 0) - return chunks; /* nothing to do */ - marray = chunks; - array_size = 0; - } - else { - /* if empty req, must still return chunk representing empty array */ - if (n_elements == 0) - return (void**)internal_malloc(m, 0); - marray = 0; - array_size = request2size(n_elements * (sizeof(void*))); - } - - /* compute total element size */ - if (opts & 0x1) { /* all-same-size */ - element_size = request2size(*sizes); - contents_size = n_elements * element_size; - } - else { /* add up all the sizes */ - element_size = 0; - contents_size = 0; - for (i = 0; i != n_elements; ++i) - contents_size += request2size(sizes[i]); - } - - size = contents_size + array_size; - - /* - Allocate the aggregate chunk. First disable direct-mmapping so - malloc won't use it, since we would not be able to later - free/realloc space internal to a segregated mmap region. - */ - was_enabled = use_mmap(m); - disable_mmap(m); - mem = internal_malloc(m, size - CHUNK_OVERHEAD); - if (was_enabled) - enable_mmap(m); - if (mem == 0) - return 0; - - if (PREACTION(m)) return 0; - p = mem2chunk(mem); - remainder_size = chunksize(p); - - assert(!is_mmapped(p)); - - if (opts & 0x2) { /* optionally clear the elements */ - memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); - } - - /* If not provided, allocate the pointer array as final part of chunk */ - if (marray == 0) { - size_t array_chunk_size; - array_chunk = chunk_plus_offset(p, contents_size); - array_chunk_size = remainder_size - contents_size; - marray = (void**) (chunk2mem(array_chunk)); - set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); - remainder_size = contents_size; - } - - /* split out elements */ - for (i = 0; ; ++i) { - marray[i] = chunk2mem(p); - if (i != n_elements-1) { - if (element_size != 0) - size = element_size; - else - size = request2size(sizes[i]); - remainder_size -= size; - set_size_and_pinuse_of_inuse_chunk(m, p, size); - p = chunk_plus_offset(p, size); - } - else { /* the final element absorbs any overallocation slop */ - set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); - break; - } - } - -#if DEBUG - if (marray != chunks) { - /* final element must have exactly exhausted chunk */ - if (element_size != 0) { - assert(remainder_size == element_size); - } - else { - assert(remainder_size == request2size(sizes[i])); - } - check_inuse_chunk(m, mem2chunk(marray)); - } - for (i = 0; i != n_elements; ++i) - check_inuse_chunk(m, mem2chunk(marray[i])); - -#endif /* DEBUG */ - - POSTACTION(m); - return marray; -} - - -/* -------------------------- public routines ---------------------------- */ - -#if !ONLY_MSPACES - -void* rdlmalloc(size_t bytes) { - /* - Basic algorithm: - If a small request (< 256 bytes minus per-chunk overhead): - 1. If one exists, use a remainderless chunk in associated smallbin. - (Remainderless means that there are too few excess bytes to - represent as a chunk.) - 2. If it is big enough, use the dv chunk, which is normally the - chunk adjacent to the one used for the most recent small request. - 3. If one exists, split the smallest available chunk in a bin, - saving remainder in dv. - 4. If it is big enough, use the top chunk. - 5. If available, get memory from system and use it - Otherwise, for a large request: - 1. Find the smallest available binned chunk that fits, and use it - if it is better fitting than dv chunk, splitting if necessary. - 2. If better fitting than any binned chunk, use the dv chunk. - 3. If it is big enough, use the top chunk. - 4. If request size >= mmap threshold, try to directly mmap this chunk. - 5. If available, get memory from system and use it - - The ugly goto's here ensure that postaction occurs along all paths. - */ - -#if USE_LOCKS - ensure_initialization(); /* initialize in sys_alloc if not using locks */ -#endif - - if (!PREACTION(gm)) { - void* mem; - size_t nb; - if (bytes <= MAX_SMALL_REQUEST) { - bindex_t idx; - binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); - idx = small_index(nb); - smallbits = gm->smallmap >> idx; - - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ - mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ - b = smallbin_at(gm, idx); - p = b->fd; - assert(chunksize(p) == small_index2size(idx)); - unlink_first_small_chunk(gm, b, p, idx); - set_inuse_and_pinuse(gm, p, small_index2size(idx)); - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (nb > gm->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ - mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - b = smallbin_at(gm, i); - p = b->fd; - assert(chunksize(p) == small_index2size(i)); - unlink_first_small_chunk(gm, b, p, i); - rsize = small_index2size(i) - nb; - /* Fit here cannot be remainderless if 4byte sizes */ - if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(gm, p, small_index2size(i)); - else { - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - r = chunk_plus_offset(p, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(gm, r, rsize); - } - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - } - } - else if (bytes >= MAX_REQUEST) - nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ - else { - nb = pad_request(bytes); - if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - } - - if (nb <= gm->dvsize) { - size_t rsize = gm->dvsize - nb; - mchunkptr p = gm->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ - mchunkptr r = gm->dv = chunk_plus_offset(p, nb); - gm->dvsize = rsize; - set_size_and_pinuse_of_free_chunk(r, rsize); - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - } - else { /* exhaust dv */ - size_t dvs = gm->dvsize; - gm->dvsize = 0; - gm->dv = 0; - set_inuse_and_pinuse(gm, p, dvs); - } - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (nb < gm->topsize) { /* Split top */ - size_t rsize = gm->topsize -= nb; - mchunkptr p = gm->top; - mchunkptr r = gm->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - mem = chunk2mem(p); - check_top_chunk(gm, gm->top); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - mem = sys_alloc(gm, nb); - -postaction: - POSTACTION(gm); - return mem; - } - - return 0; -} - -void rdlfree(void* mem) { - /* - Consolidate freed chunks with preceeding or succeeding bordering - free chunks, if they exist, and then place in a bin. Intermixed - with special cases for top, dv, mmapped chunks, and usage errors. - */ - - if (mem != 0) { - mchunkptr p = mem2chunk(mem); -#if FOOTERS - mstate fm = get_mstate_for(p); - if (!ok_magic(fm)) { - USAGE_ERROR_ACTION(fm, p); - return; - } -#else /* FOOTERS */ -#define fm gm -#endif /* FOOTERS */ - if (!PREACTION(fm)) { - check_inuse_chunk(fm, p); - if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - size_t psize = chunksize(p); - mchunkptr next = chunk_plus_offset(p, psize); - if (!pinuse(p)) { - size_t prevsize = p->prev_foot; - if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - fm->footprint -= psize; - goto postaction; - } - else { - mchunkptr prev = chunk_minus_offset(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ - if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { - fm->dvsize = psize; - set_free_with_pinuse(p, psize, next); - goto postaction; - } - } - else - goto erroraction; - } - } - - if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ - if (next == fm->top) { - size_t tsize = fm->topsize += psize; - fm->top = p; - p->head = tsize | PINUSE_BIT; - if (p == fm->dv) { - fm->dv = 0; - fm->dvsize = 0; - } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); - goto postaction; - } - else if (next == fm->dv) { - size_t dsize = fm->dvsize += psize; - fm->dv = p; - set_size_and_pinuse_of_free_chunk(p, dsize); - goto postaction; - } - else { - size_t nsize = chunksize(next); - psize += nsize; - unlink_chunk(fm, next, nsize); - set_size_and_pinuse_of_free_chunk(p, psize); - if (p == fm->dv) { - fm->dvsize = psize; - goto postaction; - } - } - } - else - set_free_with_pinuse(p, psize, next); - - if (is_small(psize)) { - insert_small_chunk(fm, p, psize); - check_free_chunk(fm, p); - } - else { - tchunkptr tp = (tchunkptr)p; - insert_large_chunk(fm, tp, psize); - check_free_chunk(fm, p); - if (--fm->release_checks == 0) - release_unused_segments(fm); - } - goto postaction; - } - } -erroraction: - USAGE_ERROR_ACTION(fm, p); -postaction: - POSTACTION(fm); - } - } -#if !FOOTERS -#undef fm -#endif /* FOOTERS */ -} - -void* rdlcalloc(size_t n_elements, size_t elem_size) { - void* mem; - size_t req = 0; - if (n_elements != 0) { - req = n_elements * elem_size; - if (((n_elements | elem_size) & ~(size_t)0xffff) && - (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ - } - mem = rdlmalloc(req); - if (mem != 0 && calloc_must_clear(mem2chunk(mem))) - memset(mem, 0, req); - return mem; -} - -void* rdlrealloc(void* oldmem, size_t bytes) { - if (oldmem == 0) - return rdlmalloc(bytes); -#ifdef REALLOC_ZERO_BYTES_FREES - if (bytes == 0) { - rdlfree(oldmem); - return 0; - } -#endif /* REALLOC_ZERO_BYTES_FREES */ - else { -#if ! FOOTERS - mstate m = gm; -#else /* FOOTERS */ - mstate m = get_mstate_for(mem2chunk(oldmem)); - if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); - return 0; - } -#endif /* FOOTERS */ - return internal_realloc(m, oldmem, bytes); - } -} - -void* rdlmemalign(size_t alignment, size_t bytes) { - return internal_memalign(gm, alignment, bytes); -} - -void** rdlindependent_calloc(size_t n_elements, size_t elem_size, - void* chunks[]) { - size_t sz = elem_size; /* serves as 1-element array */ - return ialloc(gm, n_elements, &sz, 3, chunks); -} - -void** rdlindependent_comalloc(size_t n_elements, size_t sizes[], - void* chunks[]) { - return ialloc(gm, n_elements, sizes, 0, chunks); -} - -void* rdlvalloc(size_t bytes) { - size_t pagesz; - ensure_initialization(); - pagesz = mparams.page_size; - return rdlmemalign(pagesz, bytes); -} - -void* rdlpvalloc(size_t bytes) { - size_t pagesz; - ensure_initialization(); - pagesz = mparams.page_size; - return rdlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); -} - -int rdlmalloc_trim(size_t pad) { - int result = 0; - ensure_initialization(); - if (!PREACTION(gm)) { - result = sys_trim(gm, pad); - POSTACTION(gm); - } - return result; -} - -size_t rdlmalloc_footprint(void) { - return gm->footprint; -} - -size_t dlmalloc_max_footprint(void) { - return gm->max_footprint; -} - -#if !NO_MALLINFO -struct mallinfo rdlmallinfo(void) { - return internal_mallinfo(gm); -} -#endif /* NO_MALLINFO */ - -void rdlmalloc_stats() { - internal_malloc_stats(gm); -} - -int rdlmallopt(int param_number, int value) { - return change_mparam(param_number, value); -} - -#endif /* !ONLY_MSPACES */ - -size_t rdlmalloc_usable_size(void* mem) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) - return chunksize(p) - overhead_for(p); - } - return 0; -} - -/* ----------------------------- user mspaces ---------------------------- */ - -#if MSPACES - -static mstate init_user_mstate(char* tbase, size_t tsize) { - size_t msize = pad_request(sizeof(struct malloc_state)); - mchunkptr mn; - mchunkptr msp = align_as_chunk(tbase); - mstate m = (mstate)(chunk2mem(msp)); - memset(m, 0, msize); - INITIAL_LOCK(&m->mutex); - msp->head = (msize|INUSE_BITS); - m->seg.base = m->least_addr = tbase; - m->seg.size = m->footprint = m->max_footprint = tsize; - m->magic = mparams.magic; - m->release_checks = MAX_RELEASE_CHECK_RATE; - m->mflags = mparams.default_mflags; - m->extp = 0; - m->exts = 0; - disable_contiguous(m); - init_bins(m); - mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); - check_top_chunk(m, m->top); - return m; -} - -mspace rak_create_mspace(size_t capacity, int locked) { - mstate m = 0; - size_t msize; - ensure_initialization(); - msize = pad_request(sizeof(struct malloc_state)); - if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { - size_t rs = ((capacity == 0)? mparams.granularity : - (capacity + TOP_FOOT_SIZE + msize)); - size_t tsize = granularity_align(rs); - char* tbase = (char*)(CALL_MMAP(tsize)); - if (tbase != CMFAIL) { - m = init_user_mstate(tbase, tsize); - m->seg.sflags = USE_MMAP_BIT; - set_lock(m, locked); - } - } - return (mspace)m; -} - -mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked) { - mstate m = 0; - size_t msize; - ensure_initialization(); - msize = pad_request(sizeof(struct malloc_state)); - if (capacity > msize + TOP_FOOT_SIZE && - capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { - m = init_user_mstate((char*)base, capacity); - m->seg.sflags = EXTERN_BIT; - set_lock(m, locked); - } - return (mspace)m; -} - -int rak_mspace_track_large_chunks(mspace msp, int enable) { - int ret = 0; - mstate ms = (mstate)msp; - if (!PREACTION(ms)) { - if (!use_mmap(ms)) - ret = 1; - if (!enable) - enable_mmap(ms); - else - disable_mmap(ms); - POSTACTION(ms); - } - return ret; -} - -size_t rak_destroy_mspace(mspace msp) { - size_t freed = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - msegmentptr sp = &ms->seg; - while (sp != 0) { - char* base = sp->base; - size_t size = sp->size; - flag_t flag = sp->sflags; - sp = sp->next; - if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && - CALL_MUNMAP(base, size) == 0) - freed += size; - } - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return freed; -} - -/* -mspace versions of routines are near-clones of the global -versions. This is not so nice but better than the alternatives. -*/ - - -void* rak_mspace_malloc(mspace msp, size_t bytes) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - if (!PREACTION(ms)) { - void* mem; - size_t nb; - if (bytes <= MAX_SMALL_REQUEST) { - bindex_t idx; - binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); - idx = small_index(nb); - smallbits = ms->smallmap >> idx; - - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ - mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ - b = smallbin_at(ms, idx); - p = b->fd; - assert(chunksize(p) == small_index2size(idx)); - unlink_first_small_chunk(ms, b, p, idx); - set_inuse_and_pinuse(ms, p, small_index2size(idx)); - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - else if (nb > ms->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ - mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - b = smallbin_at(ms, i); - p = b->fd; - assert(chunksize(p) == small_index2size(i)); - unlink_first_small_chunk(ms, b, p, i); - rsize = small_index2size(i) - nb; - /* Fit here cannot be remainderless if 4byte sizes */ - if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(ms, p, small_index2size(i)); - else { - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - r = chunk_plus_offset(p, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(ms, r, rsize); - } - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - } - } - else if (bytes >= MAX_REQUEST) - nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ - else { - nb = pad_request(bytes); - if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - } - - if (nb <= ms->dvsize) { - size_t rsize = ms->dvsize - nb; - mchunkptr p = ms->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ - mchunkptr r = ms->dv = chunk_plus_offset(p, nb); - ms->dvsize = rsize; - set_size_and_pinuse_of_free_chunk(r, rsize); - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - } - else { /* exhaust dv */ - size_t dvs = ms->dvsize; - ms->dvsize = 0; - ms->dv = 0; - set_inuse_and_pinuse(ms, p, dvs); - } - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - else if (nb < ms->topsize) { /* Split top */ - size_t rsize = ms->topsize -= nb; - mchunkptr p = ms->top; - mchunkptr r = ms->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - mem = chunk2mem(p); - check_top_chunk(ms, ms->top); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - mem = sys_alloc(ms, nb); - -postaction: - POSTACTION(ms); - return mem; - } - - return 0; -} - -void rak_mspace_free(mspace msp, void* mem) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); -#if FOOTERS - mstate fm = get_mstate_for(p); - msp = msp; /* placate people compiling -Wunused */ -#else /* FOOTERS */ - mstate fm = (mstate)msp; -#endif /* FOOTERS */ - if (!ok_magic(fm)) { - USAGE_ERROR_ACTION(fm, p); - return; - } - if (!PREACTION(fm)) { - check_inuse_chunk(fm, p); - if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - size_t psize = chunksize(p); - mchunkptr next = chunk_plus_offset(p, psize); - if (!pinuse(p)) { - size_t prevsize = p->prev_foot; - if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - fm->footprint -= psize; - goto postaction; - } - else { - mchunkptr prev = chunk_minus_offset(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ - if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { - fm->dvsize = psize; - set_free_with_pinuse(p, psize, next); - goto postaction; - } - } - else - goto erroraction; - } - } - - if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ - if (next == fm->top) { - size_t tsize = fm->topsize += psize; - fm->top = p; - p->head = tsize | PINUSE_BIT; - if (p == fm->dv) { - fm->dv = 0; - fm->dvsize = 0; - } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); - goto postaction; - } - else if (next == fm->dv) { - size_t dsize = fm->dvsize += psize; - fm->dv = p; - set_size_and_pinuse_of_free_chunk(p, dsize); - goto postaction; - } - else { - size_t nsize = chunksize(next); - psize += nsize; - unlink_chunk(fm, next, nsize); - set_size_and_pinuse_of_free_chunk(p, psize); - if (p == fm->dv) { - fm->dvsize = psize; - goto postaction; - } - } - } - else - set_free_with_pinuse(p, psize, next); - - if (is_small(psize)) { - insert_small_chunk(fm, p, psize); - check_free_chunk(fm, p); - } - else { - tchunkptr tp = (tchunkptr)p; - insert_large_chunk(fm, tp, psize); - check_free_chunk(fm, p); - if (--fm->release_checks == 0) - release_unused_segments(fm); - } - goto postaction; - } - } -erroraction: - USAGE_ERROR_ACTION(fm, p); -postaction: - POSTACTION(fm); - } - } -} - -void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { - void* mem; - size_t req = 0; - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - if (n_elements != 0) { - req = n_elements * elem_size; - if (((n_elements | elem_size) & ~(size_t)0xffff) && - (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ - } - mem = internal_malloc(ms, req); - if (mem != 0 && calloc_must_clear(mem2chunk(mem))) - memset(mem, 0, req); - return mem; -} - -void* rak_mspace_realloc(mspace msp, void* oldmem, size_t bytes) { - if (oldmem == 0) - return rak_mspace_malloc(msp, bytes); -#ifdef REALLOC_ZERO_BYTES_FREES - if (bytes == 0) { - rak_mspace_free(msp, oldmem); - return 0; - } -#endif /* REALLOC_ZERO_BYTES_FREES */ - else { -#if FOOTERS - mchunkptr p = mem2chunk(oldmem); - mstate ms = get_mstate_for(p); -#else /* FOOTERS */ - mstate ms = (mstate)msp; -#endif /* FOOTERS */ - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return internal_realloc(ms, oldmem, bytes); - } -} - -void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return internal_memalign(ms, alignment, bytes); -} - -void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]) { - size_t sz = elem_size; /* serves as 1-element array */ - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return ialloc(ms, n_elements, &sz, 3, chunks); -} - -void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return ialloc(ms, n_elements, sizes, 0, chunks); -} - -int rak_mspace_trim(mspace msp, size_t pad) { - int result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - if (!PREACTION(ms)) { - result = sys_trim(ms, pad); - POSTACTION(ms); - } - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - -void rak_mspace_malloc_stats(mspace msp) { - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - internal_malloc_stats(ms); - } - else { - USAGE_ERROR_ACTION(ms,ms); - } -} - -size_t rak_mspace_footprint(mspace msp) { - size_t result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - result = ms->footprint; - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - - -size_t mspace_max_footprint(mspace msp) { - size_t result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - result = ms->max_footprint; - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - - -#if !NO_MALLINFO -struct mallinfo rak_mspace_mallinfo(mspace msp) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - } - return internal_mallinfo(ms); -} -#endif /* NO_MALLINFO */ - -size_t rak_mspace_usable_size(void* mem) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) - return chunksize(p) - overhead_for(p); - } - return 0; -} - -int rak_mspace_mallopt(int param_number, int value) { - return change_mparam(param_number, value); -} - -#endif /* MSPACES */ - - -/* -------------------- Alternative MORECORE functions ------------------- */ - -/* -Guidelines for creating a custom version of MORECORE: - -* For best performance, MORECORE should allocate in multiples of pagesize. -* MORECORE may allocate more memory than requested. (Or even less, -but this will usually result in a malloc failure.) -* MORECORE must not allocate memory when given argument zero, but -instead return one past the end address of memory from previous -nonzero call. -* For best performance, consecutive calls to MORECORE with positive -arguments should return increasing addresses, indicating that -space has been contiguously extended. -* Even though consecutive calls to MORECORE need not return contiguous -addresses, it must be OK for malloc'ed chunks to span multiple -regions in those cases where they do happen to be contiguous. -* MORECORE need not handle negative arguments -- it may instead -just return MFAIL when given negative arguments. -Negative arguments are always multiples of pagesize. MORECORE -must not misinterpret negative args as large positive unsigned -args. You can suppress all such calls from even occurring by defining -MORECORE_CANNOT_TRIM, - -As an example alternative MORECORE, here is a custom allocator -kindly contributed for pre-OSX macOS. It uses virtually but not -necessarily physically contiguous non-paged memory (locked in, -present and won't get swapped out). You can use it by uncommenting -this section, adding some #includes, and setting up the appropriate -defines above: - -#define MORECORE osMoreCore - -There is also a shutdown routine that should somehow be called for -cleanup upon program exit. - -#define MAX_POOL_ENTRIES 100 -#define MINIMUM_MORECORE_SIZE (64 * 1024U) -static int next_os_pool; -void *our_os_pools[MAX_POOL_ENTRIES]; - -void *osMoreCore(int size) -{ -void *ptr = 0; -static void *sbrk_top = 0; - -if (size > 0) -{ -if (size < MINIMUM_MORECORE_SIZE) -size = MINIMUM_MORECORE_SIZE; -if (CurrentExecutionLevel() == kTaskLevel) -ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); -if (ptr == 0) -{ -return (void *) MFAIL; -} -// save ptrs so they can be freed during cleanup -our_os_pools[next_os_pool] = ptr; -next_os_pool++; -ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); -sbrk_top = (char *) ptr + size; -return ptr; -} -else if (size < 0) -{ -// we don't currently support shrink behavior -return (void *) MFAIL; -} -else -{ -return sbrk_top; -} -} - -// cleanup any allocated memory pools -// called as last thing before shutting down driver - -void osCleanupMem(void) -{ -void **ptr; - -for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) -if (*ptr) -{ -PoolDeallocate(*ptr); -*ptr = 0; -} -} - -*/ - - -/* ----------------------------------------------------------------------- -History: -V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) -* Use zeros instead of prev foot for is_mmapped -* Add rak_mspace_track_large_chunks; thanks to Jean Brouwers -* Fix set_inuse in internal_realloc; thanks to Jean Brouwers -* Fix insufficient sys_alloc padding when using 16byte alignment -* Fix bad error check in rak_mspace_footprint -* Adaptations for ptmalloc; thanks to Wolfram Gloger. -* Reentrant spin locks; thanks to Earl Chew and others -* Win32 improvements; thanks to Niall Douglas and Earl Chew -* Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options -* Extension hook in malloc_state -* Various small adjustments to reduce warnings on some compilers -* Various configuration extensions/changes for more platforms. Thanks -to all who contributed these. - -V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) -* Add max_footprint functions -* Ensure all appropriate literals are size_t -* Fix conditional compilation problem for some #define settings -* Avoid concatenating segments with the one provided -in rak_create_mspace_with_base -* Rename some variables to avoid compiler shadowing warnings -* Use explicit lock initialization. -* Better handling of sbrk interference. -* Simplify and fix segment insertion, trimming and mspace_destroy -* Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x -* Thanks especially to Dennis Flanagan for help on these. - -V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) -* Fix memalign brace error. - -V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) -* Fix improper #endif nesting in C++ -* Add explicit casts needed for C++ - -V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) -* Use trees for large bins -* Support mspaces -* Use segments to unify sbrk-based and mmap-based system allocation, -removing need for emulation on most platforms without sbrk. -* Default safety checks -* Optional footer checks. Thanks to William Robertson for the idea. -* Internal code refactoring -* Incorporate suggestions and platform-specific changes. -Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, -Aaron Bachmann, Emery Berger, and others. -* Speed up non-fastbin processing enough to remove fastbins. -* Remove useless cfree() to avoid conflicts with other apps. -* Remove internal memcpy, memset. Compilers handle builtins better. -* Remove some options that no one ever used and rename others. - -V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) -* Fix malloc_state bitmap array misdeclaration - -V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) -* Allow tuning of FIRST_SORTED_BIN_SIZE -* Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. -* Better detection and support for non-contiguousness of MORECORE. -Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger -* Bypass most of malloc if no frees. Thanks To Emery Berger. -* Fix freeing of old top non-contiguous chunk im sysmalloc. -* Raised default trim and map thresholds to 256K. -* Fix mmap-related #defines. Thanks to Lubos Lunak. -* Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. -* Branch-free bin calculation -* Default trim and mmap thresholds now 256K. - -V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) -* Introduce independent_comalloc and independent_calloc. -Thanks to Michael Pachos for motivation and help. -* Make optional .h file available -* Allow > 2GB requests on 32bit systems. -* new DL_PLATFORM_WIN32 sbrk, mmap, munmap, lock code from . -Thanks also to Andreas Mueller , -and Anonymous. -* Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for -helping test this.) -* memalign: check alignment arg -* realloc: don't try to shift chunks backwards, since this -leads to more fragmentation in some programs and doesn't -seem to help in any others. -* Collect all cases in malloc requiring system memory into sysmalloc -* Use mmap as backup to sbrk -* Place all internal state in malloc_state -* Introduce fastbins (although similar to 2.5.1) -* Many minor tunings and cosmetic improvements -* Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK -* Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS -Thanks to Tony E. Bennett and others. -* Include errno.h to support default failure action. - -V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) -* return null for negative arguments -* Added Several DL_PLATFORM_WIN32 cleanups from Martin C. Fong -* Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' -(e.g. DL_PLATFORM_WIN32 platforms) -* Cleanup header file inclusion for DL_PLATFORM_WIN32 platforms -* Cleanup code to avoid Microsoft Visual C++ compiler complaints -* Add 'USE_DL_PREFIX' to quickly allow co-existence with existing -memory allocation routines -* Set 'malloc_getpagesize' for DL_PLATFORM_WIN32 platforms (needs more work) -* Use 'assert' rather than 'ASSERT' in DL_PLATFORM_WIN32 code to conform to -usage of 'assert' in non-DL_PLATFORM_WIN32 code -* Improve DL_PLATFORM_WIN32 'sbrk()' emulation's 'findRegion()' routine to -avoid infinite loop -* Always call 'fREe()' rather than 'free()' - -V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) -* Fixed ordering problem with boundary-stamping - -V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) -* Added pvalloc, as recommended by H.J. Liu -* Added 64bit pointer support mainly from Wolfram Gloger -* Added anonymously donated DL_PLATFORM_WIN32 sbrk emulation -* Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen -* malloc_extend_top: fix mask error that caused wastage after -foreign sbrks -* Add linux mremap support code from HJ Liu - -V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) -* Integrated most documentation with the code. -* Add support for mmap, with help from -Wolfram Gloger (Gloger@lrz.uni-muenchen.de). -* Use last_remainder in more cases. -* Pack bins using idea from colin@nyx10.cs.du.edu -* Use ordered bins instead of best-fit threshhold -* Eliminate block-local decls to simplify tracing and debugging. -* Support another case of realloc via move into top -* Fix error occuring when initial sbrk_base not word-aligned. -* Rely on page size for units instead of SBRK_UNIT to -avoid surprises about sbrk alignment conventions. -* Add mallinfo, mallopt. Thanks to Raymond Nijssen -(raymond@es.ele.tue.nl) for the suggestion. -* Add `pad' argument to malloc_trim and top_pad mallopt parameter. -* More precautions for cases where other routines call sbrk, -courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). -* Added macros etc., allowing use in linux libc from -H.J. Lu (hjl@gnu.ai.mit.edu) -* Inverted this history list - -V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) -* Re-tuned and fixed to behave more nicely with V2.6.0 changes. -* Removed all preallocation code since under current scheme -the work required to undo bad preallocations exceeds -the work saved in good cases for most test programs. -* No longer use return list or unconsolidated bins since -no scheme using them consistently outperforms those that don't -given above changes. -* Use best fit for very large chunks to prevent some worst-cases. -* Added some support for debugging - -V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) -* Removed footers when chunks are in use. Thanks to -Paul Wilson (wilson@cs.texas.edu) for the suggestion. - -V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) -* Added malloc_trim, with help from Wolfram Gloger -(wmglo@Dent.MED.Uni-Muenchen.DE). - -V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) - -V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) -* realloc: try to expand in both directions -* malloc: swap order of clean-bin strategy; -* realloc: only conditionally expand backwards -* Try not to scavenge used bins -* Use bin counts as a guide to preallocation -* Occasionally bin return list chunks in first scan -* Add a few optimizations from colin@nyx10.cs.du.edu - -V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) -* faster bin computation & slightly different binning -* merged all consolidations to one part of malloc proper -(eliminating old malloc_find_space & malloc_clean_bin) -* Scan 2 returns chunks (not just 1) -* Propagate failure in realloc if malloc returns 0 -* Add stuff to allow compilation on non-ANSI compilers -from kpv@research.att.com - -V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) -* removed potential for odd address access in prev_chunk -* removed dependency on getpagesize.h -* misc cosmetics and a bit more internal documentation -* anticosmetics: mangled names in macros to evade debugger strangeness -* tested on sparc, hp-700, dec-mips, rs6000 -with gcc & native cc (hp, dec only) allowing -Detlefs & Zorn comparison study (in SIGPLAN Notices.) - -Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) -* Based loosely on libg++-1.2X malloc. (It retains some of the overall -structure of old version, but most details differ.) - -*/ - -#endif // _RAKNET_SUPPORT_DL_MALLOC +#ifdef _RAKNET_SUPPORT_DL_MALLOC + +#include "rdlmalloc.h" + +/* --------------------------- Lock preliminaries ------------------------ */ + +/* +When locks are defined, there is one global lock, plus +one per-mspace lock. + +The global lock_ensures that mparams.magic and other unique +mparams values are initialized only once. It also protects +sequences of calls to MORECORE. In many cases sys_alloc requires +two calls, that should not be interleaved with calls by other +threads. This does not protect against direct calls to MORECORE +by other threads not using this lock, so there is still code to +cope the best we can on interference. + +Per-mspace locks surround calls to malloc, free, etc. To enable use +in layered extensions, per-mspace locks are reentrant. + +Because lock-protected regions generally have bounded times, it is +OK to use the supplied simple spinlocks in the custom versions for +x86. Spinlocks are likely to improve performance for lightly +contended applications, but worsen performance under heavy +contention. + +If USE_LOCKS is > 1, the definitions of lock routines here are +bypassed, in which case you will need to define the type MLOCK_T, +and at least INITIAL_LOCK, ACQUIRE_LOCK, RELEASE_LOCK and possibly +TRY_LOCK (which is not used in this malloc, but commonly needed in +extensions.) You must also declare a +static MLOCK_T malloc_global_mutex = { initialization values };. + +*/ + +#if USE_LOCKS == 1 + +#if USE_SPIN_LOCKS && SPIN_LOCKS_AVAILABLE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if !defined(DL_PLATFORM_WIN32) + +/* Custom pthread-style spin locks on x86 and x64 for gcc */ +struct pthread_mlock_t { + volatile unsigned int l; + unsigned int c; + pthread_t threadid; +}; +#define MLOCK_T struct pthread_mlock_t +#define CURRENT_THREAD pthread_self() +#define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) +#define ACQUIRE_LOCK(sl) pthread_acquire_lock(sl) +#define RELEASE_LOCK(sl) pthread_release_lock(sl) +#define TRY_LOCK(sl) pthread_try_lock(sl) +#define SPINS_PER_YIELD 63 + +static MLOCK_T malloc_global_mutex = { 0, 0, 0}; + +static FORCEINLINE int pthread_acquire_lock (MLOCK_T *sl) { + int spins = 0; + volatile unsigned int* lp = &sl->l; + for (;;) { + if (*lp != 0) { + if (sl->threadid == CURRENT_THREAD) { + ++sl->c; + return 0; + } + } + else { + /* place args to cmpxchgl in locals to evade oddities in some gccs */ + int cmp = 0; + int val = 1; + int ret; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(lp)), "0"(cmp) + : "memory", "cc"); + if (!ret) { + assert(!sl->threadid); + sl->threadid = CURRENT_THREAD; + sl->c = 1; + return 0; + } + } + if ((++spins & SPINS_PER_YIELD) == 0) { +#if defined (__SVR4) && defined (__sun) /* solaris */ + thr_yield(); +#else +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) + sched_yield(); +#else /* no-op yield on unknown systems */ + ; +#endif /* __linux__ || __FreeBSD__ || __APPLE__ */ +#endif /* solaris */ + } + } +} + +static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) { + volatile unsigned int* lp = &sl->l; + assert(*lp != 0); + assert(sl->threadid == CURRENT_THREAD); + if (--sl->c == 0) { + sl->threadid = 0; + int prev = 0; + int ret; + __asm__ __volatile__ ("lock; xchgl %0, %1" + : "=r" (ret) + : "m" (*(lp)), "0"(prev) + : "memory"); + } +} + +static FORCEINLINE int pthread_try_lock (MLOCK_T *sl) { + volatile unsigned int* lp = &sl->l; + if (*lp != 0) { + if (sl->threadid == CURRENT_THREAD) { + ++sl->c; + return 1; + } + } + else { + int cmp = 0; + int val = 1; + int ret; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(lp)), "0"(cmp) + : "memory", "cc"); + if (!ret) { + assert(!sl->threadid); + sl->threadid = CURRENT_THREAD; + sl->c = 1; + return 1; + } + } + return 0; +} + + +#else /* DL_PLATFORM_WIN32 */ +/* Custom win32-style spin locks on x86 and x64 for MSC */ +struct win32_mlock_t { + volatile long l; + unsigned int c; + long threadid; +}; + +#define MLOCK_T struct win32_mlock_t +#define CURRENT_THREAD GetCurrentThreadId() +#define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) +#define ACQUIRE_LOCK(sl) win32_acquire_lock(sl) +#define RELEASE_LOCK(sl) win32_release_lock(sl) +#define TRY_LOCK(sl) win32_try_lock(sl) +#define SPINS_PER_YIELD 63 + +static MLOCK_T malloc_global_mutex = { 0, 0, 0}; + +static FORCEINLINE int win32_acquire_lock (MLOCK_T *sl) { + int spins = 0; + for (;;) { + if (sl->l != 0) { + if (sl->threadid == CURRENT_THREAD) { + ++sl->c; + return 0; + } + } + else { + if (!interlockedexchange(&sl->l, 1)) { + assert(!sl->threadid); + sl->threadid = CURRENT_THREAD; + sl->c = 1; + return 0; + } + } + if ((++spins & SPINS_PER_YIELD) == 0) + SleepEx(0, FALSE); + } +} + +static FORCEINLINE void win32_release_lock (MLOCK_T *sl) { + assert(sl->threadid == CURRENT_THREAD); + assert(sl->l != 0); + if (--sl->c == 0) { + sl->threadid = 0; + interlockedexchange (&sl->l, 0); + } +} + +static FORCEINLINE int win32_try_lock (MLOCK_T *sl) { + if (sl->l != 0) { + if (sl->threadid == CURRENT_THREAD) { + ++sl->c; + return 1; + } + } + else { + if (!interlockedexchange(&sl->l, 1)){ + assert(!sl->threadid); + sl->threadid = CURRENT_THREAD; + sl->c = 1; + return 1; + } + } + return 0; +} + +#endif /* DL_PLATFORM_WIN32 */ +#else /* USE_SPIN_LOCKS */ + +#ifndef DL_PLATFORM_WIN32 +/* pthreads-based locks */ + +#define MLOCK_T pthread_mutex_t +#define CURRENT_THREAD pthread_self() +#define INITIAL_LOCK(sl) pthread_init_lock(sl) +#define ACQUIRE_LOCK(sl) pthread_mutex_lock(sl) +#define RELEASE_LOCK(sl) pthread_mutex_unlock(sl) +#define TRY_LOCK(sl) (!pthread_mutex_trylock(sl)) + +static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Cope with old-style linux recursive lock initialization by adding */ +/* skipped internal declaration from pthread.h */ +#ifdef linux +#ifndef PTHREAD_MUTEX_RECURSIVE +extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, + int __kind)); +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) +#endif +#endif + +static int pthread_init_lock (MLOCK_T *sl) { + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr)) return 1; + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; + if (pthread_mutex_init(sl, &attr)) return 1; + if (pthread_mutexattr_destroy(&attr)) return 1; + return 0; +} + +#else /* DL_PLATFORM_WIN32 */ +/* Win32 critical sections */ +#define MLOCK_T CRITICAL_SECTION +#define CURRENT_THREAD GetCurrentThreadId() +#define INITIAL_LOCK(s) (!InitializeCriticalSectionAndSpinCount((s), 0x80000000|4000)) +#define ACQUIRE_LOCK(s) (EnterCriticalSection(sl), 0) +#define RELEASE_LOCK(s) LeaveCriticalSection(sl) +#define TRY_LOCK(s) TryEnterCriticalSection(sl) +#define NEED_GLOBAL_LOCK_INIT + +static MLOCK_T malloc_global_mutex; +static volatile long malloc_global_mutex_status; + +/* Use spin loop to initialize global lock */ +static void init_malloc_global_mutex() { + for (;;) { + long stat = malloc_global_mutex_status; + if (stat > 0) + return; + /* transition to < 0 while initializing, then to > 0) */ + if (stat == 0 && + interlockedcompareexchange(&malloc_global_mutex_status, -1, 0) == 0) { + InitializeCriticalSection(&malloc_global_mutex); + interlockedexchange(&malloc_global_mutex_status,1); + return; + } + SleepEx(0, FALSE); + } +} + +#endif /* DL_PLATFORM_WIN32 */ +#endif /* USE_SPIN_LOCKS */ +#endif /* USE_LOCKS == 1 */ + +/* ----------------------- User-defined locks ------------------------ */ + +#if USE_LOCKS > 1 +/* Define your own lock implementation here */ +/* #define INITIAL_LOCK(sl) ... */ +/* #define ACQUIRE_LOCK(sl) ... */ +/* #define RELEASE_LOCK(sl) ... */ +/* #define TRY_LOCK(sl) ... */ +/* static MLOCK_T malloc_global_mutex = ... */ +#endif /* USE_LOCKS > 1 */ + +/* ----------------------- Lock-based state ------------------------ */ + +#if USE_LOCKS +#define USE_LOCK_BIT (2U) +#else /* USE_LOCKS */ +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) +#endif /* USE_LOCKS */ + +#if USE_LOCKS +#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK +#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); +#endif +#ifndef RELEASE_MALLOC_GLOBAL_LOCK +#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); +#endif +#else /* USE_LOCKS */ +#define ACQUIRE_MALLOC_GLOBAL_LOCK() +#define RELEASE_MALLOC_GLOBAL_LOCK() +#endif /* USE_LOCKS */ + + +/* ----------------------- Chunk representations ------------------------ */ + +/* +(The following includes lightly edited explanations by Colin Plumb.) + +The malloc_chunk declaration below is misleading (but accurate and +necessary). It declares a "view" into memory allowing access to +necessary fields at known offsets from a given base. + +Chunks of memory are maintained using a `boundary tag' method as +originally described by Knuth. (See the paper by Paul Wilson +ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such +techniques.) Sizes of free chunks are stored both in the front of +each chunk and at the end. This makes consolidating fragmented +chunks into bigger chunks fast. The head fields also hold bits +representing whether chunks are free or in use. + +Here are some pictures to make it clearer. They are "exploded" to +show that the state of a chunk can be thought of as extending from +the high 31 bits of the head field of its header through the +prev_foot and PINUSE_BIT bit of the following chunk header. + +A chunk that's in use looks like: + +chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size of previous chunk (if P = 0) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| +| Size of this chunk 1| +-+ +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++- -+ +| | ++- -+ +| : ++- size - sizeof(size_t) available payload bytes -+ +: | +chunk-> +- -+ +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| +| Size of next chunk (may or may not be in use) | +-+ +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +And if it's free, it looks like this: + +chunk-> +- -+ +| User payload (must be in use, or we would have merged!) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| +| Size of this chunk 0| +-+ +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Next pointer | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Prev pointer | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| : ++- size - sizeof(struct chunk) unused bytes -+ +: | +chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size of this chunk | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| +| Size of next chunk (must be in use, or we would have merged)| +-+ +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| : ++- User payload -+ +: | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +|0| ++-+ +Note that since we always merge adjacent free chunks, the chunks +adjacent to a free chunk must be in use. + +Given a pointer to a chunk (which can be derived trivially from the +payload pointer) we can, in O(1) time, find out whether the adjacent +chunks are free, and if so, unlink them from the lists that they +are on and merge them with the current chunk. + +Chunks always begin on even word boundaries, so the mem portion +(which is returned to the user) is also on an even word boundary, and +thus at least double-word aligned. + +The P (PINUSE_BIT) bit, stored in the unused low-order bit of the +chunk size (which is always a multiple of two words), is an in-use +bit for the *previous* chunk. If that bit is *clear*, then the +word before the current chunk size contains the previous chunk +size, and can be used to find the front of the previous chunk. +The very first chunk allocated always has this bit set, preventing +access to non-existent (or non-owned) memory. If pinuse is set for +any given chunk, then you CANNOT determine the size of the +previous chunk, and might even get a memory addressing fault when +trying to do so. + +The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of +the chunk size redundantly records whether the current chunk is +inuse (unless the chunk is mmapped). This redundancy enables usage +checks within free and realloc, and reduces indirection when freeing +and consolidating chunks. + +Each freshly allocated chunk must have both cinuse and pinuse set. +That is, each allocated chunk borders either a previously allocated +and still in-use chunk, or the base of its memory arena. This is +ensured by making all allocations from the the `lowest' part of any +found chunk. Further, no free chunk physically borders another one, +so each free chunk is known to be preceded and followed by either +inuse chunks or the ends of memory. + +Note that the `foot' of the current chunk is actually represented +as the prev_foot of the NEXT chunk. This makes it easier to +deal with alignments etc but can be very confusing when trying +to extend or adapt this code. + +The exceptions to all this are + +1. The special chunk `top' is the top-most available chunk (i.e., +the one bordering the end of available memory). It is treated +specially. Top is never included in any bin, is used only if +no other chunk is available, and is released back to the +system if it is very large (see M_TRIM_THRESHOLD). In effect, +the top chunk is treated as larger (and thus less well +fitting) than any other available chunk. The top chunk +doesn't update its trailing size field since there is no next +contiguous chunk that would have to index off it. However, +space is still allocated for it (TOP_FOOT_SIZE) to enable +separation or merging when space is extended. + +3. Chunks allocated via mmap, have both cinuse and pinuse bits +cleared in their head fields. Because they are allocated +one-by-one, each must carry its own prev_foot field, which is +also used to hold the offset this chunk has within its mmapped +region, which is needed to preserve alignment. Each mmapped +chunk is trailed by the first two fields of a fake next-chunk +for sake of usage checks. + +*/ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk* mchunkptr; +typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#if FOOTERS +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE) +#endif /* FOOTERS */ + +/* MMapped chunks need a second word of overhead ... */ +#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +/* ... and additional padding for fake next-chunk at foot */ +#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + + +/* ------------------ Operations on head and foot fields ----------------- */ + +/* +The head field of a chunk is or'ed with PINUSE_BIT when previous +adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in +use, unless mmapped, in which case both bits are cleared. + +FLAG4_BIT is not used by this malloc, but might be useful in extensions. +*/ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define FLAG4_BIT (SIZE_T_FOUR) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) +#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) +#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) + +#define chunksize(p) ((p)->head & ~(FLAG_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) +#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p)\ + (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +/* Return true if malloced space is not necessarily cleared */ +#if MMAP_CLEARS +#define calloc_must_clear(p) (!is_mmapped(p)) +#else /* MMAP_CLEARS */ +#define calloc_must_clear(p) (1) +#endif /* MMAP_CLEARS */ + +/* ---------------------- Overlaid data structures ----------------------- */ + +/* +When chunks are not in use, they are treated as nodes of either +lists or trees. + +"Small" chunks are stored in circular doubly-linked lists, and look +like this: + +chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size of previous chunk | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +`head:' | Size of chunk, in bytes |P| +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Forward pointer to next chunk in list | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Back pointer to previous chunk in list | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Unused space (may be 0 bytes long) . +. . +. | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +`foot:' | Size of chunk, in bytes | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Larger chunks are kept in a form of bitwise digital trees (aka +tries) keyed on chunksizes. Because malloc_tree_chunks are only for +free chunks greater than 256 bytes, their size doesn't impose any +constraints on user chunk sizes. Each node looks like: + +chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size of previous chunk | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +`head:' | Size of chunk, in bytes |P| +mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Forward pointer to next chunk of same size | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Back pointer to previous chunk of same size | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Pointer to left child (child[0]) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Pointer to right child (child[1]) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Pointer to parent | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| bin index of this chunk | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Unused space . +. | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +`foot:' | Size of chunk, in bytes | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Each tree holding treenodes is a tree of unique chunk sizes. Chunks +of the same size are arranged in a circularly-linked list, with only +the oldest chunk (the next to be used, in our FIFO ordering) +actually in the tree. (Tree members are distinguished by a non-null +parent pointer.) If a chunk with the same size an an existing node +is inserted, it is linked off the existing node using pointers that +work in the same way as fd/bk pointers of small chunks. + +Each tree contains a power of 2 sized range of chunk sizes (the +smallest is 0x100 <= x < 0x180), which is is divided in half at each +tree level, with the chunks in the smaller half of the range (0x100 +<= x < 0x140 for the top nose) in the left subtree and the larger +half (0x140 <= x < 0x180) in the right subtree. This is, of course, +done by inspecting individual bits. + +Using these rules, each node's left subtree contains all smaller +sizes than its right subtree. However, the node at the root of each +subtree has no particular ordering relationship to either. (The +dividing line between the subtree sizes is based on trie relation.) +If we remove the last chunk of a given size from the interior of the +tree, we need to replace it with a leaf node. The tree ordering +rules permit a node to be replaced by any leaf below it. + +The smallest chunk in a tree (a common operation in a best-fit +allocator) can be found by walking a path to the leftmost leaf in +the tree. Unlike a usual binary tree, where we follow left child +pointers until we reach a null, here we follow the right child +pointer any time the left one is null, until we reach a leaf with +both child pointers null. The smallest chunk in the tree will be +somewhere along that path. + +The worst case number of steps to add, find, or remove a node is +bounded by the number of bits differentiating chunks within +bins. Under current bin calculations, this ranges from 6 up to 21 +(for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case +is of course much better. +*/ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; + + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk* tchunkptr; +typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +/* +Each malloc space may include non-contiguous segments, held in a +list headed by an embedded malloc_segment record representing the +top-most space. Segments also include flags holding properties of +the space. Large chunks that are directly allocated by mmap are not +included in this list. They are instead independently created and +destroyed without otherwise keeping track of them. + +Segment management mainly comes into play for spaces allocated by +MMAP. Any call to MMAP might or might not return memory that is +adjacent to an existing segment. MORECORE normally contiguously +extends the current space, so this space is almost always adjacent, +which is simpler and faster to deal with. (This is why MORECORE is +used preferentially to MMAP when both are available -- see +sys_alloc.) When allocating using MMAP, we don't use any of the +hinting mechanisms (inconsistently) supported in various +implementations of unix mmap, or distinguish reserving from +committing memory. Instead, we just ask for space, and exploit +contiguity when we get it. It is probably possible to do +better than this on some systems, but no general scheme seems +to be significantly better. + +Management entails a simpler variant of the consolidation scheme +used for chunks to reduce fragmentation -- new adjacent memory is +normally prepended or appended to an existing segment. However, +there are limitations compared to chunk consolidation that mostly +reflect the fact that segment processing is relatively infrequent +(occurring only when getting memory from system) and that we +don't expect to have huge numbers of segments: + +* Segments are not indexed, so traversal requires linear scans. (It +would be possible to index these, but is not worth the extra +overhead and complexity for most programs on most platforms.) +* New segments are only appended to old ones when holding top-most +memory; if they cannot be prepended to others, they are held in +different segments. + +Except for the top-most segment of an mstate, each segment record +is kept at the tail of its segment. Segments are added by pushing +segment records onto the list headed by &mstate.seg for the +containing mstate. + +Segment flags control allocation/merge/deallocation policies: +* If EXTERN_BIT set, then we did not allocate this segment, +and so should not try to deallocate or merge with others. +(This currently holds only for the initial segment passed +into rak_create_mspace_with_base.) +* If USE_MMAP_BIT set, the segment may be merged with +other surrounding mmapped segments and trimmed/de-allocated +using munmap. +* If neither bit is set, then the segment was obtained using +MORECORE so can be merged with surrounding MORECORE'd segments +and deallocated/trimmed using MORECORE with negative arguments. +*/ + +struct malloc_segment { + char* base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment* next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ +}; + +#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) +#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) + +typedef struct malloc_segment msegment; +typedef struct malloc_segment* msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* +A malloc_state holds all of the bookkeeping for a space. +The main fields are: + +Top +The topmost chunk of the currently active segment. Its size is +cached in topsize. The actual size of topmost space is +topsize+TOP_FOOT_SIZE, which includes space reserved for adding +fenceposts and segment records if necessary when getting more +space from the system. The size at which to autotrim top is +cached from mparams in trim_check, except that it is disabled if +an autotrim fails. + +Designated victim (dv) +This is the preferred chunk for servicing small requests that +don't have exact fits. It is normally the chunk split off most +recently to service another small request. Its size is cached in +dvsize. The link fields of this chunk are not maintained since it +is not kept in a bin. + +SmallBins +An array of bin headers for free chunks. These bins hold chunks +with sizes less than MIN_LARGE_SIZE bytes. Each bin contains +chunks of all the same size, spaced 8 bytes apart. To simplify +use in double-linked lists, each bin header acts as a malloc_chunk +pointing to the real first node, if it exists (else pointing to +itself). This avoids special-casing for headers. But to avoid +waste, we allocate only the fd/bk pointers of bins, and then use +repositioning tricks to treat these as the fields of a chunk. + +TreeBins +Treebins are pointers to the roots of trees holding a range of +sizes. There are 2 equally spaced treebins for each power of two +from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything +larger. + +Bin maps +There is one bit map for small bins ("smallmap") and one for +treebins ("treemap). Each bin sets its bit when non-empty, and +clears the bit when empty. Bit operations are then used to avoid +bin-by-bin searching -- nearly all "search" is done without ever +looking at bins that won't be selected. The bit maps +conservatively use 32 bits per map word, even if on 64bit system. +For a good description of some of the bit-based techniques used +here, see Henry S. Warren Jr's book "Hacker's Delight" (and +supplement at http://hackersdelight.org/). Many of these are +intended to reduce the branchiness of paths through malloc etc, as +well as to reduce the number of memory locations read or written. + +Segments +A list of segments headed by an embedded malloc_segment record +representing the initial space. + +Address check support +The least_addr field is the least address ever obtained from +MORECORE or MMAP. Attempted frees and reallocs of any address less +than this are trapped (unless INSECURE is defined). + +Magic tag +A cross-check field that should always hold same value as mparams.magic. + +Flags +Bits recording whether to use MMAP, locks, or contiguous MORECORE + +Statistics +Each space keeps track of current and maximum system memory +obtained via MORECORE or MMAP. + +Trim support +Fields holding the amount of unused topmost memory that should trigger +timming, and a counter to force periodic scanning to release unused +non-topmost segments. + +Locking +If USE_LOCKS is defined, the "mutex" lock is acquired and released +around every public call using this mspace. + +Extension support +A void* pointer and a size_t field that can be used to help implement +extensions to this malloc. +*/ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t release_checks; + size_t magic; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + flag_t mflags; +#if USE_LOCKS + MLOCK_T mutex; /* locate lock among fields that rarely change */ +#endif /* USE_LOCKS */ + msegment seg; + void* extp; /* Unused but available for extensions */ + size_t exts; +}; + +typedef struct malloc_state* mstate; + +/* ------------- Global malloc_state and malloc_params ------------------- */ + +/* +malloc_params holds global properties, including those that can be +dynamically set using mallopt. There is a single instance, mparams, +initialized in init_mparams. Note that the non-zeroness of "magic" +also serves as an initialization flag. +*/ + +struct malloc_params { + volatile size_t magic; + size_t page_size; + size_t granularity; + size_t mmap_threshold; + size_t trim_threshold; + flag_t default_mflags; +}; + +static struct malloc_params mparams; + +/* Ensure mparams initialized */ +#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) + +#if !ONLY_MSPACES + +/* The global malloc_state used for all non-"mspace" calls */ +static struct malloc_state _gm_; +#define gm (&_gm_) +#define is_global(M) ((M) == &_gm_) + +#endif /* !ONLY_MSPACES */ + +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* Operations on mflags */ + +#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) +#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) + +#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) +#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) +#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) + +#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) +#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) + +#define set_lock(M,L)\ + ((M)->mflags = (L)?\ + ((M)->mflags | USE_LOCK_BIT) :\ + ((M)->mflags & ~USE_LOCK_BIT)) + +/* page-align a size */ +#define page_align(S)\ + (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (mparams.granularity - SIZE_T_ONE))\ + & ~(mparams.granularity - SIZE_T_ONE)) + + +/* For mmap, use granularity alignment on windows, else page-align */ +#ifdef DL_PLATFORM_WIN32 +#define mmap_align(S) granularity_align(S) +#else +#define mmap_align(S) page_align(S) +#endif + +/* For sys_alloc, enough padding to ensure can malloc request on success */ +#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) + +#define is_page_aligned(S)\ + (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) +#define is_granularity_aligned(S)\ + (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char* addr) { + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) { + msegmentptr sp = &m->seg; + for (;;) { + if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + +#ifndef MORECORE_CANNOT_TRIM +#define should_trim(M,s) ((s) > (M)->trim_check) +#else /* MORECORE_CANNOT_TRIM */ +#define should_trim(M,s) (0) +#endif /* MORECORE_CANNOT_TRIM */ + +/* +TOP_FOOT_SIZE is padding at the end of a segment, including space +that may be needed to place segment records and fenceposts when new +noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + + +/* ------------------------------- Hooks -------------------------------- */ + +/* +PREACTION should be defined to return 0 on success, and nonzero on +failure. If you are not using locking, you can redefine these to do +anything you like. +*/ + +#if USE_LOCKS + +#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) +#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } +#else /* USE_LOCKS */ + +#ifndef PREACTION +#define PREACTION(M) (0) +#endif /* PREACTION */ + +#ifndef POSTACTION +#define POSTACTION(M) +#endif /* POSTACTION */ + +#endif /* USE_LOCKS */ + +/* +CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. +USAGE_ERROR_ACTION is triggered on detected bad frees and +reallocs. The argument p is an address that might have triggered the +fault. It is ignored by the two predefined actions, but might be +useful in custom actions that try to help diagnose errors. +*/ + +#if PROCEED_ON_ERROR + +/* A count of the number of corruption errors causing resets */ +int malloc_corruption_error_count; + +/* default corruption action */ +static void reset_on_error(mstate m); + +#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) +#define USAGE_ERROR_ACTION(m, p) + +#else /* PROCEED_ON_ERROR */ + +#ifndef CORRUPTION_ERROR_ACTION +#define CORRUPTION_ERROR_ACTION(m) ABORT +#endif /* CORRUPTION_ERROR_ACTION */ + +#ifndef USAGE_ERROR_ACTION +#define USAGE_ERROR_ACTION(m,p) ABORT +#endif /* USAGE_ERROR_ACTION */ + +#endif /* PROCEED_ON_ERROR */ + +/* -------------------------- Debugging setup ---------------------------- */ + +#if ! DEBUG + +#define check_free_chunk(M,P) +#define check_inuse_chunk(M,P) +#define check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) +#define check_malloc_state(M) +#define check_top_chunk(M,P) + +#else /* DEBUG */ +#define check_free_chunk(M,P) do_check_free_chunk(M,P) +#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) +#define check_top_chunk(M,P) do_check_top_chunk(M,P) +#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) +#define check_malloc_state(M) do_check_malloc_state(M) + +static void do_check_any_chunk(mstate m, mchunkptr p); +static void do_check_top_chunk(mstate m, mchunkptr p); +static void do_check_mmapped_chunk(mstate m, mchunkptr p); +static void do_check_inuse_chunk(mstate m, mchunkptr p); +static void do_check_free_chunk(mstate m, mchunkptr p); +static void do_check_malloced_chunk(mstate m, void* mem, size_t s); +static void do_check_tree(mstate m, tchunkptr t); +static void do_check_treebin(mstate m, bindex_t i); +static void do_check_smallbin(mstate m, bindex_t i); +static void do_check_malloc_state(mstate m); +static int bin_find(mstate m, mchunkptr x); +static size_t traverse_and_check(mstate m); +#endif /* DEBUG */ + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) ((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I. Use x86 asm if possible */ +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_tree_index(S, I)\ +{\ + unsigned int X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + __asm__("bsrl\t%1, %0\n\t" : "=r" (K) : "g" (X));\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ +}\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = _bit_scan_reverse (X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ +}\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + _BitScanReverse((DWORD *) &K, X);\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ +}\ +} + +#else /* GNUC */ +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int Y = (unsigned int)X;\ + unsigned int N = ((Y - 0x100) >> 16) & 8;\ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ + N += K;\ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ + K = 14 - N + ((Y <<= K) >> 15);\ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ +}\ +} +#endif /* GNUC */ + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* isolate the least set bit of a bitmap */ +#define least_bit(x) ((x) & -(x)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | -(x<<1)) + +/* mask with all bits to left of or equal to least bit of x on */ +#define same_or_left_bits(x) ((x) | -(x)) + +/* index corresponding to given bit. Use x86 asm if possible */ + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + __asm__("bsfl\t%1, %0\n\t" : "=r" (J) : "g" (X));\ + I = (bindex_t)J;\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = _bit_scan_forward (X); \ + I = (bindex_t)J;\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + _BitScanForward((DWORD *) &J, X);\ + I = (bindex_t)J;\ +} + +#elif USE_BUILTIN_FFS +#define compute_bit2idx(X, I) I = ffs(X)-1 + +#else +#define compute_bit2idx(X, I)\ +{\ + unsigned int Y = X - 1;\ + unsigned int K = Y >> (16-4) & 16;\ + unsigned int N = K; Y >>= K;\ + N += K = Y >> (8-3) & 8; Y >>= K;\ + N += K = Y >> (4-2) & 4; Y >>= K;\ + N += K = Y >> (2-1) & 2; Y >>= K;\ + N += K = Y >> (1-0) & 1; Y >>= K;\ + I = (bindex_t)(N + Y);\ +} +#endif /* GNUC */ + + +/* ----------------------- Runtime Check Support ------------------------- */ + +/* +For security, the main invariant is that malloc/free/etc never +writes to a static address other than malloc_state, unless static +malloc_state itself has been corrupted, which cannot occur via +malloc (because of these checks). In essence this means that we +believe all pointers, sizes, maps etc held in malloc_state, but +check all of those linked or offsetted from other embedded data +structures. These checks are interspersed with main code in a way +that tends to minimize their run-time cost. + +When FOOTERS is defined, in addition to range checking, we also +verify footer fields of inuse chunks, which can be used guarantee +that the mstate controlling malloc/free is intact. This is a +streamlined version of the approach described by William Robertson +et al in "Run-time Detection of Heap-based Overflows" LISA'03 +http://www.usenix.org/events/lisa03/tech/robertson.html The footer +of an inuse chunk holds the xor of its mstate and a random seed, +that is checked upon calls to free() and realloc(). This is +(probablistically) unguessable from outside the program, but can be +computed by any code successfully malloc'ing any chunk, so does not +itself provide protection against code that has already broken +security through some other means. Unlike Robertson et al, we +always dynamically check addresses of all offset chunks (previous, +next, etc). This turns out to be cheaper than relying on hashes. +*/ + +#if !INSECURE +/* Check if address a is at least as high as any from MORECORE or MMAP */ +#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) +/* Check if address of next chunk n is higher than base chunk p */ +#define ok_next(p, n) ((char*)(p) < (char*)(n)) +/* Check if p has inuse status */ +#define ok_inuse(p) is_inuse(p) +/* Check if p has its pinuse bit on */ +#define ok_pinuse(p) pinuse(p) + +#else /* !INSECURE */ +#define ok_address(M, a) (1) +#define ok_next(b, n) (1) +#define ok_inuse(p) (1) +#define ok_pinuse(p) (1) +#endif /* !INSECURE */ + +#if (FOOTERS && !INSECURE) +/* Check if (alleged) mstate m has expected magic field */ +#define ok_magic(M) ((M)->magic == mparams.magic) +#else /* (FOOTERS && !INSECURE) */ +#define ok_magic(M) (1) +#endif /* (FOOTERS && !INSECURE) */ + + +/* In gcc, use __builtin_expect to minimize impact of checks */ +#if !INSECURE +#if defined(__GNUC__) && __GNUC__ >= 3 +#define RTCHECK(e) __builtin_expect(e, 1) +#else /* GNUC */ +#define RTCHECK(e) (e) +#endif /* GNUC */ +#else /* !INSECURE */ +#define RTCHECK(e) (1) +#endif /* !INSECURE */ + +/* macros to set up inuse chunks with or without footers */ + +#if !FOOTERS + +#define mark_inuse_foot(M,p,s) + +/* Macros for setting head/foot of non-mmapped chunks */ + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +#else /* FOOTERS */ + +/* Set foot of inuse chunk to be xor of mstate and seed */ +#define mark_inuse_foot(M,p,s)\ + (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) + +#define get_mstate_for(p)\ + ((mstate)(((mchunkptr)((char*)(p) +\ + (chunksize(p))))->prev_foot ^ mparams.magic)) + +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M,p,s)) + +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ + mark_inuse_foot(M,p,s)) + +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + mark_inuse_foot(M, p, s)) + +#endif /* !FOOTERS */ + +/* ---------------------------- setting mparams -------------------------- */ + +/* Initialize mparams */ +static int init_mparams(void) { + + if (malloc_global_mutex_status <= 0) + init_malloc_global_mutex(); + + + ACQUIRE_MALLOC_GLOBAL_LOCK(); + if (mparams.magic == 0) { + size_t magic; + size_t psize; + size_t gsize; + +#ifndef DL_PLATFORM_WIN32 + psize = malloc_getpagesize; + gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); +#else /* DL_PLATFORM_WIN32 */ + { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + psize = system_info.dwPageSize; + gsize = ((DEFAULT_GRANULARITY != 0)? +DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); + } +#endif /* DL_PLATFORM_WIN32 */ + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + if ((sizeof(size_t) != sizeof(char*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((gsize & (gsize-SIZE_T_ONE)) != 0) || + ((psize & (psize-SIZE_T_ONE)) != 0)) + ABORT; + + mparams.granularity = gsize; + mparams.page_size = psize; + mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; + mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; +#if MORECORE_CONTIGUOUS + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; +#else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; +#endif /* MORECORE_CONTIGUOUS */ + +#if !ONLY_MSPACES + /* Set up lock for main malloc area */ + gm->mflags = mparams.default_mflags; + INITIAL_LOCK(&gm->mutex); +#endif + + { +#if USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + /* Try to use /dev/urandom, else fall back on using time */ + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) { + magic = *((size_t *) buf); + close(fd); + } + else +#endif /* USE_DEV_RANDOM */ + + + +#if defined(DL_PLATFORM_WIN32) + magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); +#else + magic = (size_t)(time(0) ^ (size_t)0x55555555U); +#endif + magic |= (size_t)8U; /* ensure nonzero */ + magic &= ~(size_t)7U; /* improve chances of fault for bad values */ + mparams.magic = magic; + } + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + return 1; +} + +/* support for mallopt */ +static int change_mparam(int param_number, int value) { + size_t val; + ensure_initialization(); + val = (value == -1)? MAX_SIZE_T : (size_t)value; + switch(param_number) { + case M_TRIM_THRESHOLD: + mparams.trim_threshold = val; + return 1; + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val-1)) == 0)) { + mparams.granularity = val; + return 1; + } + else + return 0; + case M_MMAP_THRESHOLD: + mparams.mmap_threshold = val; + return 1; + default: + return 0; + } +} + +#if DEBUG +/* ------------------------- Debugging Support --------------------------- */ + +/* Check properties of any chunk, whether free, inuse, mmapped etc */ +static void do_check_any_chunk(mstate m, mchunkptr p) { + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); +} + +/* Check properties of top chunk */ +static void do_check_top_chunk(mstate m, mchunkptr p) { + msegmentptr sp = segment_holding(m, (char*)p); + size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ + assert(sp != 0); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(sz == m->topsize); + assert(sz > 0); + assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); + assert(pinuse(p)); + assert(!pinuse(chunk_plus_offset(p, sz))); +} + +/* Check properties of (inuse) mmapped chunks */ +static void do_check_mmapped_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); + assert(is_mmapped(p)); + assert(use_mmap(m)); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(!is_small(sz)); + assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); + assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); + assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); +} + +/* Check properties of inuse chunks */ +static void do_check_inuse_chunk(mstate m, mchunkptr p) { + do_check_any_chunk(m, p); + assert(is_inuse(p)); + assert(next_pinuse(p)); + /* If not pinuse and not mmapped, previous chunk has OK offset */ + assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); + if (is_mmapped(p)) + do_check_mmapped_chunk(m, p); +} + +/* Check properties of free chunks */ +static void do_check_free_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + mchunkptr next = chunk_plus_offset(p, sz); + do_check_any_chunk(m, p); + assert(!is_inuse(p)); + assert(!next_pinuse(p)); + assert (!is_mmapped(p)); + if (p != m->dv && p != m->top) { + if (sz >= MIN_CHUNK_SIZE) { + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(is_aligned(chunk2mem(p))); + assert(next->prev_foot == sz); + assert(pinuse(p)); + assert (next == m->top || is_inuse(next)); + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_T_SIZE */ + assert(sz == SIZE_T_SIZE); + } +} + +/* Check properties of malloced chunks at the point they are malloced */ +static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t sz = p->head & ~INUSE_BITS; + do_check_inuse_chunk(m, p); + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(sz >= MIN_CHUNK_SIZE); + assert(sz >= s); + /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ + assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); + } +} + +/* Check a tree and its subtrees. */ +static void do_check_tree(mstate m, tchunkptr t) { + tchunkptr head = 0; + tchunkptr u = t; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; + compute_tree_index(tsize, idx); + assert(tindex == idx); + assert(tsize >= MIN_LARGE_SIZE); + assert(tsize >= minsize_for_tree_index(idx)); + assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); + + do { /* traverse through chain of same-sized nodes */ + do_check_any_chunk(m, ((mchunkptr)u)); + assert(u->index == tindex); + assert(chunksize(u) == tsize); + assert(!is_inuse(u)); + assert(!next_pinuse(u)); + assert(u->fd->bk == u); + assert(u->bk->fd == u); + if (u->parent == 0) { + assert(u->child[0] == 0); + assert(u->child[1] == 0); + } + else { + assert(head == 0); /* only one node on chain has parent */ + head = u; + assert(u->parent != u); + assert (u->parent->child[0] == u || + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); + if (u->child[0] != 0) { + assert(u->child[0]->parent == u); + assert(u->child[0] != u); + do_check_tree(m, u->child[0]); + } + if (u->child[1] != 0) { + assert(u->child[1]->parent == u); + assert(u->child[1] != u); + do_check_tree(m, u->child[1]); + } + if (u->child[0] != 0 && u->child[1] != 0) { + assert(chunksize(u->child[0]) < chunksize(u->child[1])); + } + } + u = u->fd; + } while (u != t); + assert(head != 0); +} + +/* Check all the chunks in a treebin. */ +static void do_check_treebin(mstate m, bindex_t i) { + tbinptr* tb = treebin_at(m, i); + tchunkptr t = *tb; + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) + assert(empty); + if (!empty) + do_check_tree(m, t); +} + +/* Check all the chunks in a smallbin. */ +static void do_check_smallbin(mstate m, bindex_t i) { + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; + unsigned int empty = (m->smallmap & (1U << i)) == 0; + if (p == b) + assert(empty); + if (!empty) { + for (; p != b; p = p->bk) { + size_t size = chunksize(p); + mchunkptr q; + /* each chunk claims to be free */ + do_check_free_chunk(m, p); + /* chunk belongs in bin */ + assert(small_index(size) == i); + assert(p->bk == b || chunksize(p->bk) == chunksize(p)); + /* chunk is followed by an inuse chunk */ + q = next_chunk(p); + if (q->head != FENCEPOST_HEAD) + do_check_inuse_chunk(m, q); + } + } +} + +/* Find x in a bin. Used in other check functions. */ +static int bin_find(mstate m, mchunkptr x) { + size_t size = chunksize(x); + if (is_small(size)) { + bindex_t sidx = small_index(size); + sbinptr b = smallbin_at(m, sidx); + if (smallmap_is_marked(m, sidx)) { + mchunkptr p = b; + do { + if (p == x) + return 1; + } while ((p = p->fd) != b); + } + } + else { + bindex_t tidx; + compute_tree_index(size, tidx); + if (treemap_is_marked(m, tidx)) { + tchunkptr t = *treebin_at(m, tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); + while (t != 0 && chunksize(t) != size) { + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + sizebits <<= 1; + } + if (t != 0) { + tchunkptr u = t; + do { + if (u == (tchunkptr)x) + return 1; + } while ((u = u->fd) != t); + } + } + } + return 0; +} + +/* Traverse each chunk and check it; return total */ +static size_t traverse_and_check(mstate m) { + size_t sum = 0; + if (is_initialized(m)) { + msegmentptr s = &m->seg; + sum += m->topsize + TOP_FOOT_SIZE; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + mchunkptr lastq = 0; + assert(pinuse(q)); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); + if (is_inuse(q)) { + assert(!bin_find(m, q)); + do_check_inuse_chunk(m, q); + } + else { + assert(q == m->dv || bin_find(m, q)); + assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ + do_check_free_chunk(m, q); + } + lastq = q; + q = next_chunk(q); + } + s = s->next; + } + } + return sum; +} + +/* Check all properties of malloc_state. */ +static void do_check_malloc_state(mstate m) { + bindex_t i; + size_t total; + /* check bins */ + for (i = 0; i < NSMALLBINS; ++i) + do_check_smallbin(m, i); + for (i = 0; i < NTREEBINS; ++i) + do_check_treebin(m, i); + + if (m->dvsize != 0) { /* check dv chunk */ + do_check_any_chunk(m, m->dv); + assert(m->dvsize == chunksize(m->dv)); + assert(m->dvsize >= MIN_CHUNK_SIZE); + assert(bin_find(m, m->dv) == 0); + } + + if (m->top != 0) { /* check top chunk */ + do_check_top_chunk(m, m->top); + /*assert(m->topsize == chunksize(m->top)); redundant */ + assert(m->topsize > 0); + assert(bin_find(m, m->top) == 0); + } + + total = traverse_and_check(m); + assert(total <= m->footprint); + assert(m->footprint <= m->max_footprint); +} +#endif /* DEBUG */ + +/* ----------------------------- statistics ------------------------------ */ + +#if !NO_MALLINFO +static struct mallinfo internal_mallinfo(mstate m) { + struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ensure_initialization(); + if (!PREACTION(m)) { + check_malloc_state(m); + if (is_initialized(m)) { + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; + msegmentptr s = &m->seg; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!is_inuse(q)) { + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } + + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; + nm.uordblks = m->footprint - mfree; + nm.fordblks = mfree; + nm.keepcost = m->topsize; + } + + POSTACTION(m); + } + return nm; +} +#endif /* !NO_MALLINFO */ + +static void internal_malloc_stats(mstate m) { + ensure_initialization(); + if (!PREACTION(m)) { + size_t maxfp = 0; + size_t fp = 0; + size_t used = 0; + check_malloc_state(m); + if (is_initialized(m)) { + msegmentptr s = &m->seg; + maxfp = m->max_footprint; + fp = m->footprint; + used = fp - (m->topsize + TOP_FOOT_SIZE); + + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!is_inuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; + } + } + + fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); + fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); + fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); + + POSTACTION(m); + } +} + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* +Various forms of linking and unlinking are defined as macros. Even +the ones for trees, which are very long but have very short typical +paths. This is ugly but reduces reliance on inlining support of +compilers. +*/ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + assert(S >= MIN_CHUNK_SIZE);\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, B->fd)))\ + F = B->fd;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (F == B)\ + clear_smallmap(M, I);\ + else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ + (B == smallbin_at(M,I) || ok_address(M, B)))) {\ + F->bk = B;\ + B->fd = F;\ +}\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (B == F)\ + clear_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, F))) {\ + B->fd = F;\ + F->bk = B;\ +}\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ +} + + + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + assert(is_small(DVS));\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr* H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + }\ + else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ +}\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ +}\ + }\ + else {\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ +}\ +}\ + }\ +}\ +} + +/* +Unlink steps: + +1. If x is a chained node, unlink it from its same-sized fd/bk links +and choose its bk node as its replacement. +2. If x was the last node of its size, but not a leaf node, it must +be replaced with a leaf node (not merely one with an open left or +right), to make sure that lefts and rights of descendents +correspond properly to bit masks. We use the rightmost descendent +of x. We could use any other leaf, but this is easy to locate and +tends to counteract removal of leftmosts elsewhere, and so keeps +paths shorter than minimally guaranteed. This doesn't loop much +because on average a node in a tree is near the bottom. +3. If x is the base of a chain (i.e., has parent links) relink +x's parent and children to x's replacement (or null if none). +*/ + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + if (RTCHECK(ok_address(M, F))) {\ + F->bk = R;\ + R->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ + }\ + else {\ + tchunkptr* RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr* CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ +}\ + if (RTCHECK(ok_address(M, RP)))\ + *RP = 0;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ +}\ +}\ +}\ + if (XP != 0) {\ + tbinptr* H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + }\ + else if (RTCHECK(ok_address(M, XP))) {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ +}\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + if (R != 0) {\ + if (RTCHECK(ok_address(M, R))) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) insert_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) unlink_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + + +/* Relays to internal calls to malloc/free from realloc, memalign etc */ + +#if ONLY_MSPACES +#define internal_malloc(m, b) rak_mspace_malloc(m, b) +#define internal_free(m, mem) rak_mspace_free(m,mem); +#else /* ONLY_MSPACES */ +#if MSPACES +#define internal_malloc(m, b)\ + (m == gm)? rdlmalloc(b) : rak_mspace_malloc(m, b) +#define internal_free(m, mem)\ + if (m == gm) rdlfree(mem); else rak_mspace_free(m,mem); +#else /* MSPACES */ +#define internal_malloc(m, b) rdlmalloc(b) +#define internal_free(m, mem) rdlfree(mem) +#endif /* MSPACES */ +#endif /* ONLY_MSPACES */ + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +/* +Directly mmapped chunks are set up with an offset to the start of +the mmapped region stored in the prev_foot field of the chunk. This +allows reconstruction of the required argument to MUNMAP when freed, +and also allows adjustment of the returned chunk to meet alignment +requirements (especially in memalign). +*/ + +/* Malloc using mmap */ +static void* mmap_alloc(mstate m, size_t nb) { + size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (mmsize > nb) { /* Check for wrap around 0 */ + char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset; + p->head = psize; + mark_inuse_foot(m, p, psize); + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + + if (m->least_addr == 0 || mm < m->least_addr) + m->least_addr = mm; + if ((m->footprint += mmsize) > m->max_footprint) + m->max_footprint = m->footprint; + assert(is_aligned(chunk2mem(p))); + check_mmapped_chunk(m, p); + return chunk2mem(p); + } + } + return 0; +} + +/* Realloc using mmap */ +static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { + size_t oldsize = chunksize(oldp); + if (is_small(nb)) /* Can't shrink mmap regions below small size */ + return 0; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (mparams.granularity << 1)) + return oldp; + else { + size_t offset = oldp->prev_foot; + size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; + size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + char* cp = (char*)CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, 1); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + newp->head = psize; + mark_inuse_foot(m, newp, psize); + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + + if (cp < m->least_addr) + m->least_addr = cp; + if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) + m->max_footprint = m->footprint; + check_mmapped_chunk(m, newp); + return newp; + } + } + return 0; +} + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) { + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char*)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; + m->trim_check = mparams.trim_threshold; /* reset on each update */ +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) { + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +#if PROCEED_ON_ERROR + +/* default corruption action */ +static void reset_on_error(mstate m) { + int i; + ++malloc_corruption_error_count; + /* Reinitialize fields to forget about all memory */ + m->smallbins = m->treebins = 0; + m->dvsize = m->topsize = 0; + m->seg.base = 0; + m->seg.size = 0; + m->seg.next = 0; + m->top = m->dv = 0; + for (i = 0; i < NTREEBINS; ++i) + *treebin_at(m, i) = 0; + init_bins(m); +} +#endif /* PROCEED_ON_ERROR */ + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void* prepend_alloc(mstate m, char* newbase, char* oldbase, + size_t nb) { + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (char*)oldfirst - (char*)p; + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + assert((char*)oldfirst > (char*)q); + assert(pinuse(oldfirst)); + assert(qsize >= MIN_CHUNK_SIZE); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + check_top_chunk(m, q); + } + else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } + else { + if (!is_inuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + check_free_chunk(m, q); + } + + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); +} + +/* Add a segment to hold a new noncontiguous region */ +static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { + /* Determine locations and sizes of segment, fenceposts, old top */ + char* old_top = (char*)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + char* old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char* asp = rawsp + offset; + char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + int nfences = 0; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + assert(is_aligned(ss)); + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmapped; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; + ++nfences; + if ((char*)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } + assert(nfences >= 2); + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = csp - old_top; + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize); + } + + check_top_chunk(m, m->top); +} + +/* -------------------------- System allocation -------------------------- */ + +/* Get memory from system using MORECORE or MMAP */ +static void* sys_alloc(mstate m, size_t nb) { + char* tbase = CMFAIL; + size_t tsize = 0; + flag_t mmap_flag = 0; + + ensure_initialization(); + + /* Directly map large chunks, but only if already initialized */ + if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { + void* mem = mmap_alloc(m, nb); + if (mem != 0) + return mem; + } + + /* + Try getting memory in any of three ways (in most-preferred to + least-preferred order): + 1. A call to MORECORE that can normally contiguously extend memory. + (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or + or main space is mmapped or a previous contiguous call failed) + 2. A call to MMAP new space (disabled if not HAVE_MMAP). + Note that under the default settings, if MORECORE is unable to + fulfill a request, and HAVE_MMAP is true, then mmap is + used as a noncontiguous system allocator. This is a useful backup + strategy for systems with holes in address spaces -- in this case + sbrk cannot contiguously expand the heap, but mmap may be able to + find space. + 3. A call to MORECORE that cannot usually contiguously extend memory. + (disabled if not HAVE_MORECORE) + + In all cases, we need to request enough bytes from system to ensure + we can malloc nb bytes upon success, so pad with enough space for + top_foot, plus alignment-pad to make sure we don't lose bytes if + not on boundary, and round this up to a granularity unit. + */ + + if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { + char* br = CMFAIL; + msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); + size_t asize = 0; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + + if (ss == 0) { /* First time through or recovery */ + char* base = (char*)CALL_MORECORE(0); + if (base != CMFAIL) { + asize = granularity_align(nb + SYS_ALLOC_PADDING); + /* Adjust to end on a page boundary */ + if (!is_page_aligned(base)) + asize += (page_align((size_t)base) - (size_t)base); + /* Can't call MORECORE if size is negative when treated as signed */ + if (asize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(asize))) == base) { + tbase = base; + tsize = asize; + } + } + } + else { + /* Subtract out existing available top space from MORECORE request. */ + asize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); + /* Use mem here only if it did continuously extend old space */ + if (asize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) { + tbase = br; + tsize = asize; + } + } + + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (asize < HALF_MAX_SIZE_T && + asize < nb + SYS_ALLOC_PADDING) { + size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - asize); + if (esize < HALF_MAX_SIZE_T) { + char* end = (char*)CALL_MORECORE(esize); + if (end != CMFAIL) + asize += esize; + else { /* Can't use; try to release */ + (void) CALL_MORECORE(-asize); + br = CMFAIL; + } + } + } + } + if (br != CMFAIL) { /* Use the space we did get */ + tbase = br; + tsize = asize; + } + else + disable_contiguous(m); /* Don't try contiguous path in the future */ + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + } + + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + size_t rsize = granularity_align(nb + SYS_ALLOC_PADDING); + if (rsize > nb) { /* Fail if wraps around zero */ + char* mp = (char*)(CALL_MMAP(rsize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = rsize; + mmap_flag = USE_MMAP_BIT; + } + } + } + + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + size_t asize = granularity_align(nb + SYS_ALLOC_PADDING); + if (asize < HALF_MAX_SIZE_T) { + char* br = CMFAIL; + char* end = CMFAIL; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + br = (char*)(CALL_MORECORE(asize)); + end = (char*)(CALL_MORECORE(0)); + RELEASE_MALLOC_GLOBAL_LOCK(); + if (br != CMFAIL && end != CMFAIL && br < end) { + size_t ssize = end - br; + if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; + tsize = ssize; + } + } + } + } + + if (tbase != CMFAIL) { + + if ((m->footprint += tsize) > m->max_footprint) + m->max_footprint = m->footprint; + + if (!is_initialized(m)) { /* first-time initialization */ + if (m->least_addr == 0 || tbase < m->least_addr) + m->least_addr = tbase; + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmap_flag; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + init_bins(m); +#if !ONLY_MSPACES + if (is_global(m)) + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + else +#endif + { + /* Offset top by embedded malloc_state */ + mchunkptr mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); + } + } + + else { + /* Try to merge with an existing segment */ + msegmentptr sp = &m->seg; + /* Only consider most recent segment if traversal suppressed */ + while (sp != 0 && tbase != sp->base + sp->size) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag && + segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } + else { + if (tbase < m->least_addr) + m->least_addr = tbase; + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag) { + char* oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } + else + add_segment(m, tbase, tsize, mmap_flag); + } + } + + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + check_top_chunk(m, m->top); + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); + } + } + + MALLOC_FAILURE_ACTION; + return 0; +} + +/* ----------------------- system deallocation -------------------------- */ + +/* Unmap and unlink any mmapped segments that don't contain used chunks */ +static size_t release_unused_segments(mstate m) { + size_t released = 0; + int nsegs = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + ++nsegs; + if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + assert(segment_holds(sp, (char*)sp)); + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + m->footprint -= size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } + else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } + if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ + break; + pred = sp; + sp = next; + } + /* Reset check counter */ + m->release_checks = ((nsegs > MAX_RELEASE_CHECK_RATE)? +nsegs : MAX_RELEASE_CHECK_RATE); + return released; +} + +static int sys_trim(mstate m, size_t pad) { + size_t released = 0; + ensure_initialization(); + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = mparams.granularity; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char*)m->top); + + if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + size_t newsize = sp->size - extra; + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + { + /* Make sure end of memory is where we last set it. */ + char* old_br = (char*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + char* rel_br = (char*)(CALL_MORECORE(-extra)); + char* new_br = (char*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MALLOC_GLOBAL_LOCK(); + } + } + + if (released != 0) { + sp->size -= released; + m->footprint -= released; + init_top(m, m->top, m->topsize - released); + check_top_chunk(m, m->top); + } + } + + /* Unmap any unused mmapped segments */ + if (HAVE_MMAP) + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0 && m->topsize > m->trim_check) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; +} + + +/* ---------------------------- malloc support --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void* tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; + size_t rsize = -nb; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) { + bindex_t i; + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + t = *treebin_at(m, i); + } + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return 0 so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + if (RTCHECK(ok_address(m, v))) { /* split */ + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + } + CORRUPTION_ERROR_ACTION(m); + } + return 0; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void* tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); + compute_bit2idx(leastbit, i); + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); + } + } + + CORRUPTION_ERROR_ACTION(m); + return 0; +} + +/* --------------------------- realloc support --------------------------- */ + +static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + return 0; + } + if (!PREACTION(m)) { + mchunkptr oldp = mem2chunk(oldmem); + size_t oldsize = chunksize(oldp); + mchunkptr next = chunk_plus_offset(oldp, oldsize); + mchunkptr newp = 0; + void* extra = 0; + + /* Try to either shrink or extend into top. Else malloc-copy-free */ + + if (RTCHECK(ok_address(m, oldp) && ok_inuse(oldp) && + ok_next(oldp, next) && ok_pinuse(next))) { + size_t nb = request2size(bytes); + if (is_mmapped(oldp)) + newp = mmap_resize(m, oldp, nb); + else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + newp = oldp; + if (rsize >= MIN_CHUNK_SIZE) { + mchunkptr remainder = chunk_plus_offset(newp, nb); + set_inuse(m, newp, nb); + set_inuse_and_pinuse(m, remainder, rsize); + extra = chunk2mem(remainder); + } + } + else if (next == m->top && oldsize + m->topsize > nb) { + /* Expand into top */ + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(oldp, nb); + set_inuse(m, oldp, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = oldp; + } + } + else { + USAGE_ERROR_ACTION(m, oldmem); + POSTACTION(m); + return 0; + } +#if DEBUG + if (newp != 0) { + check_inuse_chunk(m, newp); /* Check requires lock */ + } +#endif + + POSTACTION(m); + + if (newp != 0) { + if (extra != 0) { + internal_free(m, extra); + } + return chunk2mem(newp); + } + else { + void* newmem = internal_malloc(m, bytes); + if (newmem != 0) { + size_t oc = oldsize - overhead_for(oldp); + memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + return newmem; + } + } + return 0; +} + +/* --------------------------- memalign support -------------------------- */ + +static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ + return internal_malloc(m, bytes); + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ + alignment = MIN_CHUNK_SIZE; + if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ + size_t a = MALLOC_ALIGNMENT << 1; + while (a < alignment) a <<= 1; + alignment = a; + } + + if (bytes >= MAX_REQUEST - alignment) { + if (m != 0) { /* Test isn't needed but avoids compiler warning */ + MALLOC_FAILURE_ACTION; + } + } + else { + size_t nb = request2size(bytes); + size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + char* mem = (char*)internal_malloc(m, req); + if (mem != 0) { + void* leader = 0; + void* trailer = 0; + mchunkptr p = mem2chunk(mem); + + if (PREACTION(m)) return 0; + if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((size_t)(((size_t)(mem + + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? +br : br+alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; + + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + newp->prev_foot = p->prev_foot + leadsize; + newp->head = newsize; + } + else { /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); + set_inuse(m, p, leadsize); + leader = chunk2mem(p); + } + p = newp; + } + + /* Give back spare room at the end */ + if (!is_mmapped(p)) { + size_t size = chunksize(p); + if (size > nb + MIN_CHUNK_SIZE) { + size_t remainder_size = size - nb; + mchunkptr remainder = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, remainder, remainder_size); + trailer = chunk2mem(remainder); + } + } + + assert (chunksize(p) >= nb); + assert((((size_t)(chunk2mem(p))) % alignment) == 0); + check_inuse_chunk(m, p); + POSTACTION(m); + if (leader != 0) { + internal_free(m, leader); + } + if (trailer != 0) { + internal_free(m, trailer); + } + return chunk2mem(p); + } + } + return 0; +} + +/* ------------------------ comalloc/coalloc support --------------------- */ + +static void** ialloc(mstate m, + size_t n_elements, + size_t* sizes, + int opts, + void* chunks[]) { + /* + This provides common support for independent_X routines, handling + all of the combinations that can result. + + The opts arg has: + bit 0 set if all elements are same size (using sizes[0]) + bit 1 set if elements should be zeroed + */ + + size_t element_size; /* chunksize of each element, if all same */ + size_t contents_size; /* total size of elements */ + size_t array_size; /* request size of pointer array */ + void* mem; /* malloced aggregate space */ + mchunkptr p; /* corresponding chunk */ + size_t remainder_size; /* remaining bytes while splitting */ + void** marray; /* either "chunks" or malloced ptr array */ + mchunkptr array_chunk; /* chunk for malloced ptr array */ + flag_t was_enabled; /* to disable mmap */ + size_t size; + size_t i; + + ensure_initialization(); + /* compute array length, if needed */ + if (chunks != 0) { + if (n_elements == 0) + return chunks; /* nothing to do */ + marray = chunks; + array_size = 0; + } + else { + /* if empty req, must still return chunk representing empty array */ + if (n_elements == 0) + return (void**)internal_malloc(m, 0); + marray = 0; + array_size = request2size(n_elements * (sizeof(void*))); + } + + /* compute total element size */ + if (opts & 0x1) { /* all-same-size */ + element_size = request2size(*sizes); + contents_size = n_elements * element_size; + } + else { /* add up all the sizes */ + element_size = 0; + contents_size = 0; + for (i = 0; i != n_elements; ++i) + contents_size += request2size(sizes[i]); + } + + size = contents_size + array_size; + + /* + Allocate the aggregate chunk. First disable direct-mmapping so + malloc won't use it, since we would not be able to later + free/realloc space internal to a segregated mmap region. + */ + was_enabled = use_mmap(m); + disable_mmap(m); + mem = internal_malloc(m, size - CHUNK_OVERHEAD); + if (was_enabled) + enable_mmap(m); + if (mem == 0) + return 0; + + if (PREACTION(m)) return 0; + p = mem2chunk(mem); + remainder_size = chunksize(p); + + assert(!is_mmapped(p)); + + if (opts & 0x2) { /* optionally clear the elements */ + memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); + } + + /* If not provided, allocate the pointer array as final part of chunk */ + if (marray == 0) { + size_t array_chunk_size; + array_chunk = chunk_plus_offset(p, contents_size); + array_chunk_size = remainder_size - contents_size; + marray = (void**) (chunk2mem(array_chunk)); + set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); + remainder_size = contents_size; + } + + /* split out elements */ + for (i = 0; ; ++i) { + marray[i] = chunk2mem(p); + if (i != n_elements-1) { + if (element_size != 0) + size = element_size; + else + size = request2size(sizes[i]); + remainder_size -= size; + set_size_and_pinuse_of_inuse_chunk(m, p, size); + p = chunk_plus_offset(p, size); + } + else { /* the final element absorbs any overallocation slop */ + set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); + break; + } + } + +#if DEBUG + if (marray != chunks) { + /* final element must have exactly exhausted chunk */ + if (element_size != 0) { + assert(remainder_size == element_size); + } + else { + assert(remainder_size == request2size(sizes[i])); + } + check_inuse_chunk(m, mem2chunk(marray)); + } + for (i = 0; i != n_elements; ++i) + check_inuse_chunk(m, mem2chunk(marray[i])); + +#endif /* DEBUG */ + + POSTACTION(m); + return marray; +} + + +/* -------------------------- public routines ---------------------------- */ + +#if !ONLY_MSPACES + +void* rdlmalloc(size_t bytes) { + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to + represent as a chunk.) + 2. If it is big enough, use the dv chunk, which is normally the + chunk adjacent to the one used for the most recent small request. + 3. If one exists, split the smallest available chunk in a bin, + saving remainder in dv. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, and use it + if it is better fitting than dv chunk, splitting if necessary. + 2. If better fitting than any binned chunk, use the dv chunk. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it + + The ugly goto's here ensure that postaction occurs along all paths. + */ + +#if USE_LOCKS + ensure_initialization(); /* initialize in sys_alloc if not using locks */ +#endif + + if (!PREACTION(gm)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = gm->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(gm, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(gm, b, p, idx); + set_inuse_and_pinuse(gm, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb > gm->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(gm, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(gm, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(gm, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(gm, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + + if (nb <= gm->dvsize) { + size_t rsize = gm->dvsize - nb; + mchunkptr p = gm->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = gm->dv = chunk_plus_offset(p, nb); + gm->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + } + else { /* exhaust dv */ + size_t dvs = gm->dvsize; + gm->dvsize = 0; + gm->dv = 0; + set_inuse_and_pinuse(gm, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; + mchunkptr p = gm->top; + mchunkptr r = gm->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + mem = chunk2mem(p); + check_top_chunk(gm, gm->top); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + mem = sys_alloc(gm, nb); + +postaction: + POSTACTION(gm); + return mem; + } + + return 0; +} + +void rdlfree(void* mem) { + /* + Consolidate freed chunks with preceeding or succeeding bordering + free chunks, if they exist, and then place in a bin. Intermixed + with special cases for top, dv, mmapped chunks, and usage errors. + */ + + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } +erroraction: + USAGE_ERROR_ACTION(fm, p); +postaction: + POSTACTION(fm); + } + } +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ +} + +void* rdlcalloc(size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = rdlmalloc(req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +void* rdlrealloc(void* oldmem, size_t bytes) { + if (oldmem == 0) + return rdlmalloc(bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + rdlfree(oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(mem2chunk(oldmem)); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + return internal_realloc(m, oldmem, bytes); + } +} + +void* rdlmemalign(size_t alignment, size_t bytes) { + return internal_memalign(gm, alignment, bytes); +} + +void** rdlindependent_calloc(size_t n_elements, size_t elem_size, + void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + return ialloc(gm, n_elements, &sz, 3, chunks); +} + +void** rdlindependent_comalloc(size_t n_elements, size_t sizes[], + void* chunks[]) { + return ialloc(gm, n_elements, sizes, 0, chunks); +} + +void* rdlvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return rdlmemalign(pagesz, bytes); +} + +void* rdlpvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return rdlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); +} + +int rdlmalloc_trim(size_t pad) { + int result = 0; + ensure_initialization(); + if (!PREACTION(gm)) { + result = sys_trim(gm, pad); + POSTACTION(gm); + } + return result; +} + +size_t rdlmalloc_footprint(void) { + return gm->footprint; +} + +size_t dlmalloc_max_footprint(void) { + return gm->max_footprint; +} + +#if !NO_MALLINFO +struct mallinfo rdlmallinfo(void) { + return internal_mallinfo(gm); +} +#endif /* NO_MALLINFO */ + +void rdlmalloc_stats() { + internal_malloc_stats(gm); +} + +int rdlmallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* !ONLY_MSPACES */ + +size_t rdlmalloc_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +/* ----------------------------- user mspaces ---------------------------- */ + +#if MSPACES + +static mstate init_user_mstate(char* tbase, size_t tsize) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + memset(m, 0, msize); + INITIAL_LOCK(&m->mutex); + msp->head = (msize|INUSE_BITS); + m->seg.base = m->least_addr = tbase; + m->seg.size = m->footprint = m->max_footprint = tsize; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + m->mflags = mparams.default_mflags; + m->extp = 0; + m->exts = 0; + disable_contiguous(m); + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); + check_top_chunk(m, m->top); + return m; +} + +mspace rak_create_mspace(size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + size_t rs = ((capacity == 0)? mparams.granularity : + (capacity + TOP_FOOT_SIZE + msize)); + size_t tsize = granularity_align(rs); + char* tbase = (char*)(CALL_MMAP(tsize)); + if (tbase != CMFAIL) { + m = init_user_mstate(tbase, tsize); + m->seg.sflags = USE_MMAP_BIT; + set_lock(m, locked); + } + } + return (mspace)m; +} + +mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity > msize + TOP_FOOT_SIZE && + capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + m = init_user_mstate((char*)base, capacity); + m->seg.sflags = EXTERN_BIT; + set_lock(m, locked); + } + return (mspace)m; +} + +int rak_mspace_track_large_chunks(mspace msp, int enable) { + int ret = 0; + mstate ms = (mstate)msp; + if (!PREACTION(ms)) { + if (!use_mmap(ms)) + ret = 1; + if (!enable) + enable_mmap(ms); + else + disable_mmap(ms); + POSTACTION(ms); + } + return ret; +} + +size_t rak_destroy_mspace(mspace msp) { + size_t freed = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + msegmentptr sp = &ms->seg; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + flag_t flag = sp->sflags; + sp = sp->next; + if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && + CALL_MUNMAP(base, size) == 0) + freed += size; + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return freed; +} + +/* +mspace versions of routines are near-clones of the global +versions. This is not so nice but better than the alternatives. +*/ + + +void* rak_mspace_malloc(mspace msp, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (!PREACTION(ms)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(ms, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(ms, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } + else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + check_top_chunk(ms, ms->top); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + mem = sys_alloc(ms, nb); + +postaction: + POSTACTION(ms); + return mem; + } + + return 0; +} + +void rak_mspace_free(mspace msp, void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + msp = msp; /* placate people compiling -Wunused */ +#else /* FOOTERS */ + mstate fm = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } +erroraction: + USAGE_ERROR_ACTION(fm, p); +postaction: + POSTACTION(fm); + } + } +} + +void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = internal_malloc(ms, req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +void* rak_mspace_realloc(mspace msp, void* oldmem, size_t bytes) { + if (oldmem == 0) + return rak_mspace_malloc(msp, bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + rak_mspace_free(msp, oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if FOOTERS + mchunkptr p = mem2chunk(oldmem); + mstate ms = get_mstate_for(p); +#else /* FOOTERS */ + mstate ms = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_realloc(ms, oldmem, bytes); + } +} + +void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_memalign(ms, alignment, bytes); +} + +void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, &sz, 3, chunks); +} + +void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, sizes, 0, chunks); +} + +int rak_mspace_trim(mspace msp, size_t pad) { + int result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + result = sys_trim(ms, pad); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +void rak_mspace_malloc_stats(mspace msp) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + internal_malloc_stats(ms); + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} + +size_t rak_mspace_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + + +size_t mspace_max_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->max_footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + + +#if !NO_MALLINFO +struct mallinfo rak_mspace_mallinfo(mspace msp) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + } + return internal_mallinfo(ms); +} +#endif /* NO_MALLINFO */ + +size_t rak_mspace_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +int rak_mspace_mallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* MSPACES */ + + +/* -------------------- Alternative MORECORE functions ------------------- */ + +/* +Guidelines for creating a custom version of MORECORE: + +* For best performance, MORECORE should allocate in multiples of pagesize. +* MORECORE may allocate more memory than requested. (Or even less, +but this will usually result in a malloc failure.) +* MORECORE must not allocate memory when given argument zero, but +instead return one past the end address of memory from previous +nonzero call. +* For best performance, consecutive calls to MORECORE with positive +arguments should return increasing addresses, indicating that +space has been contiguously extended. +* Even though consecutive calls to MORECORE need not return contiguous +addresses, it must be OK for malloc'ed chunks to span multiple +regions in those cases where they do happen to be contiguous. +* MORECORE need not handle negative arguments -- it may instead +just return MFAIL when given negative arguments. +Negative arguments are always multiples of pagesize. MORECORE +must not misinterpret negative args as large positive unsigned +args. You can suppress all such calls from even occurring by defining +MORECORE_CANNOT_TRIM, + +As an example alternative MORECORE, here is a custom allocator +kindly contributed for pre-OSX macOS. It uses virtually but not +necessarily physically contiguous non-paged memory (locked in, +present and won't get swapped out). You can use it by uncommenting +this section, adding some #includes, and setting up the appropriate +defines above: + +#define MORECORE osMoreCore + +There is also a shutdown routine that should somehow be called for +cleanup upon program exit. + +#define MAX_POOL_ENTRIES 100 +#define MINIMUM_MORECORE_SIZE (64 * 1024U) +static int next_os_pool; +void *our_os_pools[MAX_POOL_ENTRIES]; + +void *osMoreCore(int size) +{ +void *ptr = 0; +static void *sbrk_top = 0; + +if (size > 0) +{ +if (size < MINIMUM_MORECORE_SIZE) +size = MINIMUM_MORECORE_SIZE; +if (CurrentExecutionLevel() == kTaskLevel) +ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); +if (ptr == 0) +{ +return (void *) MFAIL; +} +// save ptrs so they can be freed during cleanup +our_os_pools[next_os_pool] = ptr; +next_os_pool++; +ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); +sbrk_top = (char *) ptr + size; +return ptr; +} +else if (size < 0) +{ +// we don't currently support shrink behavior +return (void *) MFAIL; +} +else +{ +return sbrk_top; +} +} + +// cleanup any allocated memory pools +// called as last thing before shutting down driver + +void osCleanupMem(void) +{ +void **ptr; + +for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) +if (*ptr) +{ +PoolDeallocate(*ptr); +*ptr = 0; +} +} + +*/ + + +/* ----------------------------------------------------------------------- +History: +V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) +* Use zeros instead of prev foot for is_mmapped +* Add rak_mspace_track_large_chunks; thanks to Jean Brouwers +* Fix set_inuse in internal_realloc; thanks to Jean Brouwers +* Fix insufficient sys_alloc padding when using 16byte alignment +* Fix bad error check in rak_mspace_footprint +* Adaptations for ptmalloc; thanks to Wolfram Gloger. +* Reentrant spin locks; thanks to Earl Chew and others +* Win32 improvements; thanks to Niall Douglas and Earl Chew +* Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options +* Extension hook in malloc_state +* Various small adjustments to reduce warnings on some compilers +* Various configuration extensions/changes for more platforms. Thanks +to all who contributed these. + +V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) +* Add max_footprint functions +* Ensure all appropriate literals are size_t +* Fix conditional compilation problem for some #define settings +* Avoid concatenating segments with the one provided +in rak_create_mspace_with_base +* Rename some variables to avoid compiler shadowing warnings +* Use explicit lock initialization. +* Better handling of sbrk interference. +* Simplify and fix segment insertion, trimming and mspace_destroy +* Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x +* Thanks especially to Dennis Flanagan for help on these. + +V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) +* Fix memalign brace error. + +V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) +* Fix improper #endif nesting in C++ +* Add explicit casts needed for C++ + +V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) +* Use trees for large bins +* Support mspaces +* Use segments to unify sbrk-based and mmap-based system allocation, +removing need for emulation on most platforms without sbrk. +* Default safety checks +* Optional footer checks. Thanks to William Robertson for the idea. +* Internal code refactoring +* Incorporate suggestions and platform-specific changes. +Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, +Aaron Bachmann, Emery Berger, and others. +* Speed up non-fastbin processing enough to remove fastbins. +* Remove useless cfree() to avoid conflicts with other apps. +* Remove internal memcpy, memset. Compilers handle builtins better. +* Remove some options that no one ever used and rename others. + +V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) +* Fix malloc_state bitmap array misdeclaration + +V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) +* Allow tuning of FIRST_SORTED_BIN_SIZE +* Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. +* Better detection and support for non-contiguousness of MORECORE. +Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger +* Bypass most of malloc if no frees. Thanks To Emery Berger. +* Fix freeing of old top non-contiguous chunk im sysmalloc. +* Raised default trim and map thresholds to 256K. +* Fix mmap-related #defines. Thanks to Lubos Lunak. +* Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. +* Branch-free bin calculation +* Default trim and mmap thresholds now 256K. + +V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) +* Introduce independent_comalloc and independent_calloc. +Thanks to Michael Pachos for motivation and help. +* Make optional .h file available +* Allow > 2GB requests on 32bit systems. +* new DL_PLATFORM_WIN32 sbrk, mmap, munmap, lock code from . +Thanks also to Andreas Mueller , +and Anonymous. +* Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for +helping test this.) +* memalign: check alignment arg +* realloc: don't try to shift chunks backwards, since this +leads to more fragmentation in some programs and doesn't +seem to help in any others. +* Collect all cases in malloc requiring system memory into sysmalloc +* Use mmap as backup to sbrk +* Place all internal state in malloc_state +* Introduce fastbins (although similar to 2.5.1) +* Many minor tunings and cosmetic improvements +* Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK +* Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS +Thanks to Tony E. Bennett and others. +* Include errno.h to support default failure action. + +V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) +* return null for negative arguments +* Added Several DL_PLATFORM_WIN32 cleanups from Martin C. Fong +* Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' +(e.g. DL_PLATFORM_WIN32 platforms) +* Cleanup header file inclusion for DL_PLATFORM_WIN32 platforms +* Cleanup code to avoid Microsoft Visual C++ compiler complaints +* Add 'USE_DL_PREFIX' to quickly allow co-existence with existing +memory allocation routines +* Set 'malloc_getpagesize' for DL_PLATFORM_WIN32 platforms (needs more work) +* Use 'assert' rather than 'ASSERT' in DL_PLATFORM_WIN32 code to conform to +usage of 'assert' in non-DL_PLATFORM_WIN32 code +* Improve DL_PLATFORM_WIN32 'sbrk()' emulation's 'findRegion()' routine to +avoid infinite loop +* Always call 'fREe()' rather than 'free()' + +V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) +* Fixed ordering problem with boundary-stamping + +V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) +* Added pvalloc, as recommended by H.J. Liu +* Added 64bit pointer support mainly from Wolfram Gloger +* Added anonymously donated DL_PLATFORM_WIN32 sbrk emulation +* Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen +* malloc_extend_top: fix mask error that caused wastage after +foreign sbrks +* Add linux mremap support code from HJ Liu + +V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) +* Integrated most documentation with the code. +* Add support for mmap, with help from +Wolfram Gloger (Gloger@lrz.uni-muenchen.de). +* Use last_remainder in more cases. +* Pack bins using idea from colin@nyx10.cs.du.edu +* Use ordered bins instead of best-fit threshhold +* Eliminate block-local decls to simplify tracing and debugging. +* Support another case of realloc via move into top +* Fix error occuring when initial sbrk_base not word-aligned. +* Rely on page size for units instead of SBRK_UNIT to +avoid surprises about sbrk alignment conventions. +* Add mallinfo, mallopt. Thanks to Raymond Nijssen +(raymond@es.ele.tue.nl) for the suggestion. +* Add `pad' argument to malloc_trim and top_pad mallopt parameter. +* More precautions for cases where other routines call sbrk, +courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). +* Added macros etc., allowing use in linux libc from +H.J. Lu (hjl@gnu.ai.mit.edu) +* Inverted this history list + +V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) +* Re-tuned and fixed to behave more nicely with V2.6.0 changes. +* Removed all preallocation code since under current scheme +the work required to undo bad preallocations exceeds +the work saved in good cases for most test programs. +* No longer use return list or unconsolidated bins since +no scheme using them consistently outperforms those that don't +given above changes. +* Use best fit for very large chunks to prevent some worst-cases. +* Added some support for debugging + +V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) +* Removed footers when chunks are in use. Thanks to +Paul Wilson (wilson@cs.texas.edu) for the suggestion. + +V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) +* Added malloc_trim, with help from Wolfram Gloger +(wmglo@Dent.MED.Uni-Muenchen.DE). + +V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + +V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) +* realloc: try to expand in both directions +* malloc: swap order of clean-bin strategy; +* realloc: only conditionally expand backwards +* Try not to scavenge used bins +* Use bin counts as a guide to preallocation +* Occasionally bin return list chunks in first scan +* Add a few optimizations from colin@nyx10.cs.du.edu + +V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) +* faster bin computation & slightly different binning +* merged all consolidations to one part of malloc proper +(eliminating old malloc_find_space & malloc_clean_bin) +* Scan 2 returns chunks (not just 1) +* Propagate failure in realloc if malloc returns 0 +* Add stuff to allow compilation on non-ANSI compilers +from kpv@research.att.com + +V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) +* removed potential for odd address access in prev_chunk +* removed dependency on getpagesize.h +* misc cosmetics and a bit more internal documentation +* anticosmetics: mangled names in macros to evade debugger strangeness +* tested on sparc, hp-700, dec-mips, rs6000 +with gcc & native cc (hp, dec only) allowing +Detlefs & Zorn comparison study (in SIGPLAN Notices.) + +Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) +* Based loosely on libg++-1.2X malloc. (It retains some of the overall +structure of old version, but most details differ.) + +*/ + +#endif // _RAKNET_SUPPORT_DL_MALLOC diff --git a/src/raknet/rdlmalloc.h b/src/raknet/rdlmalloc.h index 1458c02..06c8594 100755 --- a/src/raknet/rdlmalloc.h +++ b/src/raknet/rdlmalloc.h @@ -1,2261 +1,2261 @@ -#ifdef _RAKNET_SUPPORT_DL_MALLOC - -/* -Default header file for malloc-2.8.x, written by Doug Lea -and released to the public domain, as explained at -http://creativecommons.org/licenses/publicdomain. - -last update: Wed May 27 14:25:17 2009 Doug Lea (dl at gee) - -This header is for ANSI C/C++ only. You can set any of -the following #defines before including: - -* If USE_DL_PREFIX is defined, it is assumed that malloc.c -was also compiled with this option, so all routines -have names starting with "dl". - -* If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this -file will be #included AFTER . This is needed only if -your system defines a struct mallinfo that is incompatible with the -standard one declared here. Otherwise, you can include this file -INSTEAD of your system system . At least on ANSI, all -declarations should be compatible with system versions - -* If MSPACES is defined, declarations for mspace versions are included. -*/ - -#ifndef MALLOC_280_H -#define MALLOC_280_H - -#include "rdlmalloc-options.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#include /* for size_t */ - -#ifndef ONLY_MSPACES -#define ONLY_MSPACES 0 /* define to a value */ -#endif /* ONLY_MSPACES */ -#ifndef NO_MALLINFO -#define NO_MALLINFO 0 -#endif /* NO_MALLINFO */ - - -#if !ONLY_MSPACES - -#ifndef USE_DL_PREFIX -#define rdlcalloc calloc -#define rdlfree free -#define rdlmalloc malloc -#define rdlmemalign memalign -#define rdlrealloc realloc -#define rdlvalloc valloc -#define rdlpvalloc pvalloc -#define rdlmallinfo mallinfo -#define rdlmallopt mallopt -#define rdlmalloc_trim malloc_trim -#define rdlmalloc_stats malloc_stats -#define rdlmalloc_usable_size malloc_usable_size -#define rdlmalloc_footprint malloc_footprint -#define rdlindependent_calloc independent_calloc -#define rdlindependent_comalloc independent_comalloc -#endif /* USE_DL_PREFIX */ -#if !NO_MALLINFO -#ifndef HAVE_USR_INCLUDE_MALLOC_H -#ifndef _MALLOC_H -#ifndef MALLINFO_FIELD_TYPE -#define MALLINFO_FIELD_TYPE size_t -#endif /* MALLINFO_FIELD_TYPE */ -#ifndef STRUCT_MALLINFO_DECLARED -#define STRUCT_MALLINFO_DECLARED 1 - struct mallinfo { - MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ - MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ - MALLINFO_FIELD_TYPE smblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ - MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ - MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ - MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ - MALLINFO_FIELD_TYPE fordblks; /* total free space */ - MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ - }; -#endif /* STRUCT_MALLINFO_DECLARED */ -#endif /* _MALLOC_H */ -#endif /* HAVE_USR_INCLUDE_MALLOC_H */ -#endif /* !NO_MALLINFO */ - - /* - malloc(size_t n) - Returns a pointer to a newly allocated chunk of at least n bytes, or - null if no space is available, in which case errno is set to ENOMEM - on ANSI C systems. - - If n is zero, malloc returns a minimum-sized chunk. (The minimum - size is 16 bytes on most 32bit systems, and 32 bytes on 64bit - systems.) Note that size_t is an unsigned type, so calls with - arguments that would be negative if signed are interpreted as - requests for huge amounts of space, which will often fail. The - maximum supported value of n differs across systems, but is in all - cases less than the maximum representable value of a size_t. - */ - void* rdlmalloc(size_t); - - /* - free(void* p) - Releases the chunk of memory pointed to by p, that had been previously - allocated using malloc or a related routine such as realloc. - It has no effect if p is null. If p was not malloced or already - freed, free(p) will by default cuase the current program to abort. - */ - void rdlfree(void*); - - /* - calloc(size_t n_elements, size_t element_size); - Returns a pointer to n_elements * element_size bytes, with all locations - set to zero. - */ - void* rdlcalloc(size_t, size_t); - - /* - realloc(void* p, size_t n) - Returns a pointer to a chunk of size n that contains the same data - as does chunk p up to the minimum of (n, p's size) bytes, or null - if no space is available. - - The returned pointer may or may not be the same as p. The algorithm - prefers extending p in most cases when possible, otherwise it - employs the equivalent of a malloc-copy-free sequence. - - If p is null, realloc is equivalent to malloc. - - If space is not available, realloc returns null, errno is set (if on - ANSI) and p is NOT freed. - - if n is for fewer bytes than already held by p, the newly unused - space is lopped off and freed if possible. realloc with a size - argument of zero (re)allocates a minimum-sized chunk. - - The old unix realloc convention of allowing the last-free'd chunk - to be used as an argument to realloc is not supported. - */ - - void* rdlrealloc(void*, size_t); - - /* - memalign(size_t alignment, size_t n); - Returns a pointer to a newly allocated chunk of n bytes, aligned - in accord with the alignment argument. - - The alignment argument should be a power of two. If the argument is - not a power of two, the nearest greater power is used. - 8-byte alignment is guaranteed by normal malloc calls, so don't - bother calling memalign with an argument of 8 or less. - - Overreliance on memalign is a sure way to fragment space. - */ - void* rdlmemalign(size_t, size_t); - - /* - valloc(size_t n); - Equivalent to memalign(pagesize, n), where pagesize is the page - size of the system. If the pagesize is unknown, 4096 is used. - */ - void* rdlvalloc(size_t); - - /* - mallopt(int parameter_number, int parameter_value) - Sets tunable parameters The format is to provide a - (parameter-number, parameter-value) pair. mallopt then sets the - corresponding parameter to the argument value if it can (i.e., so - long as the value is meaningful), and returns 1 if successful else - 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, - normally defined in malloc.h. None of these are use in this malloc, - so setting them has no effect. But this malloc also supports other - options in mallopt: - - Symbol param # default allowed param values - M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) - M_GRANULARITY -2 page size any power of 2 >= page size - M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) - */ - int rdlmallopt(int, int); - -#define M_TRIM_THRESHOLD (-1) -#define M_GRANULARITY (-2) -#define M_MMAP_THRESHOLD (-3) - - - /* - malloc_footprint(); - Returns the number of bytes obtained from the system. The total - number of bytes allocated by malloc, realloc etc., is less than this - value. Unlike mallinfo, this function returns only a precomputed - result, so can be called frequently to monitor memory consumption. - Even if locks are otherwise defined, this function does not use them, - so results might not be up to date. - */ - size_t rdlmalloc_footprint(); - -#if !NO_MALLINFO - /* - mallinfo() - Returns (by copy) a struct containing various summary statistics: - - arena: current total non-mmapped bytes allocated from system - ordblks: the number of free chunks - smblks: always zero. - hblks: current number of mmapped regions - hblkhd: total bytes held in mmapped regions - usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. - fsmblks: always zero - uordblks: current total allocated space (normal or mmapped) - fordblks: total free space - keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) - - Because these fields are ints, but internal bookkeeping may - be kept as longs, the reported values may wrap around zero and - thus be inaccurate. - */ - - struct mallinfo rdlmallinfo(void); -#endif /* NO_MALLINFO */ - - /* - independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); - - independent_calloc is similar to calloc, but instead of returning a - single cleared space, it returns an array of pointers to n_elements - independent elements that can hold contents of size elem_size, each - of which starts out cleared, and can be independently freed, - realloc'ed etc. The elements are guaranteed to be adjacently - allocated (this is not guaranteed to occur with multiple callocs or - mallocs), which may also improve cache locality in some - applications. - - The "chunks" argument is optional (i.e., may be null, which is - probably the most typical usage). If it is null, the returned array - is itself dynamically allocated and should also be freed when it is - no longer needed. Otherwise, the chunks array must be of at least - n_elements in length. It is filled in with the pointers to the - chunks. - - In either case, independent_calloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and "chunks" - is null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use regular calloc and assign pointers into this - space to represent elements. (In this case though, you cannot - independently free elements.) - - independent_calloc simplifies and speeds up implementations of many - kinds of pools. It may also be useful when constructing large data - structures that initially have a fixed number of fixed-sized nodes, - but the number is not known at compile time, and some of the nodes - may later need to be freed. For example: - - struct Node { int item; struct Node* next; }; - - struct Node* build_list() { - struct Node** pool; - int n = read_number_of_nodes_needed(); - if (n <= 0) return 0; - pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); - if (pool == 0) die(); - // organize into a linked list... - struct Node* first = pool[0]; - for (i = 0; i < n-1; ++i) - pool[i]->next = pool[i+1]; - free(pool); // Can now free the array (or not, if it is needed later) - return first; - } - */ - void** rdlindependent_calloc(size_t, size_t, void**); - - /* - independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); - - independent_comalloc allocates, all at once, a set of n_elements - chunks with sizes indicated in the "sizes" array. It returns - an array of pointers to these elements, each of which can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null). If it is null - the returned array is itself dynamically allocated and should also - be freed when it is no longer needed. Otherwise, the chunks array - must be of at least n_elements in length. It is filled in with the - pointers to the chunks. - - In either case, independent_comalloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and chunks is - null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use a single regular malloc, and assign pointers at - particular offsets in the aggregate space. (In this case though, you - cannot independently free elements.) - - independent_comallac differs from independent_calloc in that each - element may have a different size, and also that it does not - automatically clear elements. - - independent_comalloc can be used to speed up allocation in cases - where several structs or objects must always be allocated at the - same time. For example: - - struct Head { ... } - struct Foot { ... } - - void send_message(char* msg) { - int msglen = strlen(msg); - size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; - void* chunks[3]; - if (independent_comalloc(3, sizes, chunks) == 0) - die(); - struct Head* head = (struct Head*)(chunks[0]); - char* body = (char*)(chunks[1]); - struct Foot* foot = (struct Foot*)(chunks[2]); - // ... - } - - In general though, independent_comalloc is worth using only for - larger values of n_elements. For small values, you probably won't - detect enough difference from series of malloc calls to bother. - - Overuse of independent_comalloc can increase overall memory usage, - since it cannot reuse existing noncontiguous small chunks that - might be available for some of the elements. - */ - void** rdlindependent_comalloc(size_t, size_t*, void**); - - - /* - pvalloc(size_t n); - Equivalent to valloc(minimum-page-that-holds(n)), that is, - round up n to nearest pagesize. - */ - void* rdlpvalloc(size_t); - - /* - malloc_trim(size_t pad); - - If possible, gives memory back to the system (via negative arguments - to sbrk) if there is unused memory at the `high' end of the malloc - pool or in unused MMAP segments. You can call this after freeing - large blocks of memory to potentially reduce the system-level memory - requirements of a program. However, it cannot guarantee to reduce - memory. Under some allocation patterns, some large free blocks of - memory will be locked between two used chunks, so they cannot be - given back to the system. - - The `pad' argument to malloc_trim represents the amount of free - trailing space to leave untrimmed. If this argument is zero, only - the minimum amount of memory to maintain internal data structures - will be left. Non-zero arguments can be supplied to maintain enough - trailing space to service future expected allocations without having - to re-obtain memory from the system. - - Malloc_trim returns 1 if it actually released any memory, else 0. - */ - int rdlmalloc_trim(size_t); - - /* - malloc_stats(); - Prints on stderr the amount of space obtained from the system (both - via sbrk and mmap), the maximum amount (which may be more than - current if malloc_trim and/or munmap got called), and the current - number of bytes allocated via malloc (or realloc, etc) but not yet - freed. Note that this is the number of bytes allocated, not the - number requested. It will be larger than the number requested - because of alignment and bookkeeping overhead. Because it includes - alignment wastage as being in use, this figure may be greater than - zero even when no user-level chunks are allocated. - - The reported current and maximum system memory can be inaccurate if - a program makes other calls to system memory allocation functions - (normally sbrk) outside of malloc. - - malloc_stats prints only the most commonly interesting statistics. - More information can be obtained by calling mallinfo. - */ - void rdlmalloc_stats(); - -#endif /* !ONLY_MSPACES */ - - /* - malloc_usable_size(void* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. malloc_usable_size can be more useful in - debugging and assertions, for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); - */ - size_t rdlmalloc_usable_size(void*); - - -#if MSPACES - - /* - mspace is an opaque type representing an independent - region of space that supports rak_mspace_malloc, etc. - */ - typedef void* mspace; - - /* - rak_create_mspace creates and returns a new independent space with the - given initial capacity, or, if 0, the default granularity size. It - returns null if there is no system memory available to create the - space. If argument locked is non-zero, the space uses a separate - lock to control access. The capacity of the space will grow - dynamically as needed to service rak_mspace_malloc requests. You can - control the sizes of incremental increases of this space by - compiling with a different DEFAULT_GRANULARITY or dynamically - setting with mallopt(M_GRANULARITY, value). - */ - mspace rak_create_mspace(size_t capacity, int locked); - - /* - rak_destroy_mspace destroys the given space, and attempts to return all - of its memory back to the system, returning the total number of - bytes freed. After destruction, the results of access to all memory - used by the space become undefined. - */ - size_t rak_destroy_mspace(mspace msp); - - /* - rak_create_mspace_with_base uses the memory supplied as the initial base - of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this - space is used for bookkeeping, so the capacity must be at least this - large. (Otherwise 0 is returned.) When this initial space is - exhausted, additional memory will be obtained from the system. - Destroying this space will deallocate all additionally allocated - space (if possible) but not the initial base. - */ - mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked); - - /* - rak_mspace_track_large_chunks controls whether requests for large chunks - are allocated in their own untracked mmapped regions, separate from - others in this mspace. By default large chunks are not tracked, - which reduces fragmentation. However, such chunks are not - necessarily released to the system upon rak_destroy_mspace. Enabling - tracking by setting to true may increase fragmentation, but avoids - leakage when relying on rak_destroy_mspace to release all memory - allocated using this space. The function returns the previous - setting. - */ - int rak_mspace_track_large_chunks(mspace msp, int enable); - - /* - rak_mspace_malloc behaves as malloc, but operates within - the given space. - */ - void* rak_mspace_malloc(mspace msp, size_t bytes); - - /* - rak_mspace_free behaves as free, but operates within - the given space. - - If compiled with FOOTERS==1, rak_mspace_free is not actually needed. - free may be called instead of rak_mspace_free because freed chunks from - any space are handled by their originating spaces. - */ - void rak_mspace_free(mspace msp, void* mem); - - /* - rak_mspace_realloc behaves as realloc, but operates within - the given space. - - If compiled with FOOTERS==1, rak_mspace_realloc is not actually - needed. realloc may be called instead of rak_mspace_realloc because - realloced chunks from any space are handled by their originating - spaces. - */ - void* rak_mspace_realloc(mspace msp, void* mem, size_t newsize); - - /* - rak_mspace_calloc behaves as calloc, but operates within - the given space. - */ - void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); - - /* - rak_mspace_memalign behaves as memalign, but operates within - the given space. - */ - void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes); - - /* - rak_mspace_independent_calloc behaves as independent_calloc, but - operates within the given space. - */ - void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]); - - /* - rak_mspace_independent_comalloc behaves as independent_comalloc, but - operates within the given space. - */ - void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]); - - /* - rak_mspace_footprint() returns the number of bytes obtained from the - system for this space. - */ - size_t rak_mspace_footprint(mspace msp); - - -#if !NO_MALLINFO - /* - rak_mspace_mallinfo behaves as mallinfo, but reports properties of - the given space. - */ - struct mallinfo rak_mspace_mallinfo(mspace msp); -#endif /* NO_MALLINFO */ - - /* - malloc_usable_size(void* p) behaves the same as malloc_usable_size; - */ - size_t rak_mspace_usable_size(void* mem); - - /* - rak_mspace_malloc_stats behaves as malloc_stats, but reports - properties of the given space. - */ - void rak_mspace_malloc_stats(mspace msp); - - /* - rak_mspace_trim behaves as malloc_trim, but - operates within the given space. - */ - int rak_mspace_trim(mspace msp, size_t pad); - - /* - An alias for mallopt. - */ - int rak_mspace_mallopt(int, int); - -#endif /* MSPACES */ - -#ifdef __cplusplus -}; /* end of extern "C" */ -#endif - -/* -This is a version (aka rdlmalloc) of malloc/free/realloc written by -Doug Lea and released to the public domain, as explained at -http://creativecommons.org/licenses/publicdomain. Send questions, -comments, complaints, performance data, etc to dl@cs.oswego.edu - -* Version 2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) - -Note: There may be an updated version of this malloc obtainable at -ftp://gee.cs.oswego.edu/pub/misc/malloc.c -Check before installing! - -* Quickstart - -This library is all in one file to simplify the most common usage: -ftp it, compile it (-O3), and link it into another program. All of -the compile-time options default to reasonable values for use on -most platforms. You might later want to step through various -compile-time and dynamic tuning options. - -For convenience, an include file for code using this malloc is at: -ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.4.h -You don't really need this .h file unless you call functions not -defined in your system include files. The .h file contains only the -excerpts from this file needed for using this malloc on ANSI C/C++ -systems, so long as you haven't changed compile-time options about -naming and tuning parameters. If you do, then you can create your -own malloc.h that does include all settings by cutting at the point -indicated below. Note that you may already by default be using a C -library containing a malloc that is based on some version of this -malloc (for example in linux). You might still want to use the one -in this file to customize settings or to avoid overheads associated -with library versions. - -* Vital statistics: - -Supported pointer/size_t representation: 4 or 8 bytes -size_t MUST be an unsigned type of the same width as -pointers. (If you are using an ancient system that declares -size_t as a signed type, or need it to be a different width -than pointers, you can use a previous release of this malloc -(e.g. 2.7.2) supporting these.) - -Alignment: 8 bytes (default) -This suffices for nearly all current machines and C compilers. -However, you can define MALLOC_ALIGNMENT to be wider than this -if necessary (up to 128bytes), at the expense of using more space. - -Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) -8 or 16 bytes (if 8byte sizes) -Each malloced chunk has a hidden word of overhead holding size -and status information, and additional cross-check word -if FOOTERS is defined. - -Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) -8-byte ptrs: 32 bytes (including overhead) - -Even a request for zero bytes (i.e., malloc(0)) returns a -pointer to something of the minimum allocatable size. -The maximum overhead wastage (i.e., number of extra bytes -allocated than were requested in malloc) is less than or equal -to the minimum size, except for requests >= mmap_threshold that -are serviced via mmap(), where the worst case wastage is about -32 bytes plus the remainder from a system page (the minimal -mmap unit); typically 4096 or 8192 bytes. - -Security: static-safe; optionally more or less -The "security" of malloc refers to the ability of malicious -code to accentuate the effects of errors (for example, freeing -space that is not currently malloc'ed or overwriting past the -ends of chunks) in code that calls malloc. This malloc -guarantees not to modify any memory locations below the base of -heap, i.e., static variables, even in the presence of usage -errors. The routines additionally detect most improper frees -and reallocs. All this holds as long as the static bookkeeping -for malloc itself is not corrupted by some other means. This -is only one aspect of security -- these checks do not, and -cannot, detect all possible programming errors. - -If FOOTERS is defined nonzero, then each allocated chunk -carries an additional check word to verify that it was malloced -from its space. These check words are the same within each -execution of a program using malloc, but differ across -executions, so externally crafted fake chunks cannot be -freed. This improves security by rejecting frees/reallocs that -could corrupt heap memory, in addition to the checks preventing -writes to statics that are always on. This may further improve -security at the expense of time and space overhead. (Note that -FOOTERS may also be worth using with MSPACES.) - -By default detected errors cause the program to abort (calling -"abort()"). You can override this to instead proceed past -errors by defining PROCEED_ON_ERROR. In this case, a bad free -has no effect, and a malloc that encounters a bad address -caused by user overwrites will ignore the bad address by -dropping pointers and indices to all known memory. This may -be appropriate for programs that should continue if at all -possible in the face of programming errors, although they may -run out of memory because dropped memory is never reclaimed. - -If you don't like either of these options, you can define -CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything -else. And if if you are sure that your program using malloc has -no errors or vulnerabilities, you can define INSECURE to 1, -which might (or might not) provide a small performance improvement. - -Thread-safety: NOT thread-safe unless USE_LOCKS defined -When USE_LOCKS is defined, each public call to malloc, free, -etc is surrounded with either a pthread mutex or a win32 -spinlock (depending on DL_PLATFORM_WIN32). This is not especially fast, and -can be a major bottleneck. It is designed only to provide -minimal protection in concurrent environments, and to provide a -basis for extensions. If you are using malloc in a concurrent -program, consider instead using nedmalloc -(http://www.nedprod.com/programs/portable/nedmalloc/) or -ptmalloc (See http://www.malloc.de), which are derived -from versions of this malloc. - -System requirements: Any combination of MORECORE and/or MMAP/MUNMAP -This malloc can use unix sbrk or any emulation (invoked using -the CALL_MORECORE macro) and/or mmap/munmap or any emulation -(invoked using CALL_MMAP/CALL_MUNMAP) to get and release system -memory. On most unix systems, it tends to work best if both -MORECORE and MMAP are enabled. On Win32, it uses emulations -based on VirtualAlloc. It also uses common C library functions -like memset. - -Compliance: I believe it is compliant with the Single Unix Specification -(See http://www.unix.org). Also SVID/XPG, ANSI C, and probably -others as well. - -* Overview of algorithms - -This is not the fastest, most space-conserving, most portable, or -most tunable malloc ever written. However it is among the fastest -while also being among the most space-conserving, portable and -tunable. Consistent balance across these factors results in a good -general-purpose allocator for malloc-intensive programs. - -In most ways, this malloc is a best-fit allocator. Generally, it -chooses the best-fitting existing chunk for a request, with ties -broken in approximately least-recently-used order. (This strategy -normally maintains low fragmentation.) However, for requests less -than 256bytes, it deviates from best-fit when there is not an -exactly fitting available chunk by preferring to use space adjacent -to that used for the previous small request, as well as by breaking -ties in approximately most-recently-used order. (These enhance -locality of series of small allocations.) And for very large requests -(>= 256Kb by default), it relies on system memory mapping -facilities, if supported. (This helps avoid carrying around and -possibly fragmenting memory used only for large chunks.) - -All operations (except malloc_stats and mallinfo) have execution -times that are bounded by a constant factor of the number of bits in -a size_t, not counting any clearing in calloc or copying in realloc, -or actions surrounding MORECORE and MMAP that have times -proportional to the number of non-contiguous regions returned by -system allocation routines, which is often just 1. In real-time -applications, you can optionally suppress segment traversals using -NO_SEGMENT_TRAVERSAL, which assures bounded execution even when -system allocators return non-contiguous spaces, at the typical -expense of carrying around more memory and increased fragmentation. - -The implementation is not very modular and seriously overuses -macros. Perhaps someday all C compilers will do as good a job -inlining modular code as can now be done by brute-force expansion, -but now, enough of them seem not to. - -Some compilers issue a lot of warnings about code that is -dead/unreachable only on some platforms, and also about intentional -uses of negation on unsigned types. All known cases of each can be -ignored. - -For a longer but out of date high-level description, see -http://gee.cs.oswego.edu/dl/html/malloc.html - -* MSPACES -If MSPACES is defined, then in addition to malloc, free, etc., -this file also defines rak_mspace_malloc, rak_mspace_free, etc. These -are versions of malloc routines that take an "mspace" argument -obtained using rak_create_mspace, to control all internal bookkeeping. -If ONLY_MSPACES is defined, only these versions are compiled. -So if you would like to use this allocator for only some allocations, -and your system malloc for others, you can compile with -ONLY_MSPACES and then do something like... -static mspace mymspace = rak_create_mspace(0,0); // for example -#define mymalloc(bytes) rak_mspace_malloc(mymspace, bytes) - -(Note: If you only need one instance of an mspace, you can instead -use "USE_DL_PREFIX" to relabel the global malloc.) - -You can similarly create thread-local allocators by storing -mspaces as thread-locals. For example: -static __thread mspace tlms = 0; -void* tlmalloc(size_t bytes) { -if (tlms == 0) tlms = rak_create_mspace(0, 0); -return rak_mspace_malloc(tlms, bytes); -} -void tlfree(void* mem) { rak_mspace_free(tlms, mem); } - -Unless FOOTERS is defined, each mspace is completely independent. -You cannot allocate from one and free to another (although -conformance is only weakly checked, so usage errors are not always -caught). If FOOTERS is defined, then each chunk carries around a tag -indicating its originating mspace, and frees are directed to their -originating spaces. - -------------------------- Compile-time options --------------------------- - -Be careful in setting #define values for numerical constants of type -size_t. On some systems, literal values are not automatically extended -to size_t precision unless they are explicitly casted. You can also -use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. - -DL_PLATFORM_WIN32 default: defined if _WIN32 defined -Defining DL_PLATFORM_WIN32 sets up defaults for MS environment and compilers. -Otherwise defaults are for unix. Beware that there seem to be some -cases where this malloc might not be a pure drop-in replacement for -Win32 malloc: Random-looking failures from Win32 GDI API's (eg; -SetDIBits()) may be due to bugs in some video driver implementations -when pixel buffers are malloc()ed, and the region spans more than -one VirtualAlloc()ed region. Because rdlmalloc uses a small (64Kb) -default granularity, pixel buffers may straddle virtual allocation -regions more often than when using the Microsoft allocator. You can -avoid this by using VirtualAlloc() and VirtualFree() for all pixel -buffers rather than using malloc(). If this is not possible, -recompile this malloc with a larger DEFAULT_GRANULARITY. - -MALLOC_ALIGNMENT default: (size_t)8 -Controls the minimum alignment for malloc'ed chunks. It must be a -power of two and at least 8, even on machines for which smaller -alignments would suffice. It may be defined as larger than this -though. Note however that code and data structures are optimized for -the case of 8-byte alignment. - -MSPACES default: 0 (false) -If true, compile in support for independent allocation spaces. -This is only supported if HAVE_MMAP is true. - -ONLY_MSPACES default: 0 (false) -If true, only compile in mspace versions, not regular versions. - -USE_LOCKS default: 0 (false) -Causes each call to each public routine to be surrounded with -pthread or DL_PLATFORM_WIN32 mutex lock/unlock. (If set true, this can be -overridden on a per-mspace basis for mspace versions.) If set to a -non-zero value other than 1, locks are used, but their -implementation is left out, so lock functions must be supplied manually, -as described below. - -USE_SPIN_LOCKS default: 1 iff USE_LOCKS and on x86 using gcc or MSC -If true, uses custom spin locks for locking. This is currently -supported only for x86 platforms using gcc or recent MS compilers. -Otherwise, posix locks or win32 critical sections are used. - -FOOTERS default: 0 -If true, provide extra checking and dispatching by placing -information in the footers of allocated chunks. This adds -space and time overhead. - -INSECURE default: 0 -If true, omit checks for usage errors and heap space overwrites. - -USE_DL_PREFIX default: NOT defined -Causes compiler to prefix all public routines with the string 'dl'. -This can be useful when you only want to use this malloc in one part -of a program, using your regular system malloc elsewhere. - -ABORT default: defined as abort() -Defines how to abort on failed checks. On most systems, a failed -check cannot die with an "assert" or even print an informative -message, because the underlying print routines in turn call malloc, -which will fail again. Generally, the best policy is to simply call -abort(). It's not very useful to do more than this because many -errors due to overwriting will show up as address faults (null, odd -addresses etc) rather than malloc-triggered checks, so will also -abort. Also, most compilers know that abort() does not return, so -can better optimize code conditionally calling it. - -PROCEED_ON_ERROR default: defined as 0 (false) -Controls whether detected bad addresses cause them to bypassed -rather than aborting. If set, detected bad arguments to free and -realloc are ignored. And all bookkeeping information is zeroed out -upon a detected overwrite of freed heap space, thus losing the -ability to ever return it from malloc again, but enabling the -application to proceed. If PROCEED_ON_ERROR is defined, the -static variable malloc_corruption_error_count is compiled in -and can be examined to see if errors have occurred. This option -generates slower code than the default abort policy. - -DEBUG default: NOT defined -The DEBUG setting is mainly intended for people trying to modify -this code or diagnose problems when porting to new platforms. -However, it may also be able to better isolate user errors than just -using runtime checks. The assertions in the check routines spell -out in more detail the assumptions and invariants underlying the -algorithms. The checking is fairly extensive, and will slow down -execution noticeably. Calling malloc_stats or mallinfo with DEBUG -set will attempt to check every non-mmapped allocated and free chunk -in the course of computing the summaries. - -ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) -Debugging assertion failures can be nearly impossible if your -version of the assert macro causes malloc to be called, which will -lead to a cascade of further failures, blowing the runtime stack. -ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), -which will usually make debugging easier. - -MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 -The action to take before "return 0" when malloc fails to be able to -return memory because there is none available. - -HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES -True if this system supports sbrk or an emulation of it. - -MORECORE default: sbrk -The name of the sbrk-style system routine to call to obtain more -memory. See below for guidance on writing custom MORECORE -functions. The type of the argument to sbrk/MORECORE varies across -systems. It cannot be size_t, because it supports negative -arguments, so it is normally the signed type of the same width as -size_t (sometimes declared as "intptr_t"). It doesn't much matter -though. Internally, we only call it with arguments less than half -the max value of a size_t, which should work across all reasonable -possibilities, although sometimes generating compiler warnings. - -MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE -If true, take advantage of fact that consecutive calls to MORECORE -with positive arguments always return contiguous increasing -addresses. This is true of unix sbrk. It does not hurt too much to -set it true anyway, since malloc copes with non-contiguities. -Setting it false when definitely non-contiguous saves time -and possibly wasted space it would take to discover this though. - -MORECORE_CANNOT_TRIM default: NOT defined -True if MORECORE cannot release space back to the system when given -negative arguments. This is generally necessary only if you are -using a hand-crafted MORECORE function that cannot handle negative -arguments. - -NO_SEGMENT_TRAVERSAL default: 0 -If non-zero, suppresses traversals of memory segments -returned by either MORECORE or CALL_MMAP. This disables -merging of segments that are contiguous, and selectively -releasing them to the OS if unused, but bounds execution times. - -HAVE_MMAP default: 1 (true) -True if this system supports mmap or an emulation of it. If so, and -HAVE_MORECORE is not true, MMAP is used for all system -allocation. If set and HAVE_MORECORE is true as well, MMAP is -primarily used to directly allocate very large blocks. It is also -used as a backup strategy in cases where MORECORE fails to provide -space from system. Note: A single call to MUNMAP is assumed to be -able to unmap memory that may have be allocated using multiple calls -to MMAP, so long as they are adjacent. - -HAVE_MREMAP default: 1 on linux, else 0 -If true realloc() uses mremap() to re-allocate large blocks and -extend or shrink allocation spaces. - -MMAP_CLEARS default: 1 except on WINCE. -True if mmap clears memory so calloc doesn't need to. This is true -for standard unix mmap using /dev/zero and on DL_PLATFORM_WIN32 except for WINCE. - -USE_BUILTIN_FFS default: 0 (i.e., not used) -Causes malloc to use the builtin ffs() function to compute indices. -Some compilers may recognize and intrinsify ffs to be faster than the -supplied C version. Also, the case of x86 using gcc is special-cased -to an asm instruction, so is already as fast as it can be, and so -this setting has no effect. Similarly for Win32 under recent MS compilers. -(On most x86s, the asm version is only slightly faster than the C version.) - -malloc_getpagesize default: derive from system includes, or 4096. -The system page size. To the extent possible, this malloc manages -memory from the system in page-size units. This may be (and -usually is) a function rather than a constant. This is ignored -if DL_PLATFORM_WIN32, where page size is determined using getSystemInfo during -initialization. - -USE_DEV_RANDOM default: 0 (i.e., not used) -Causes malloc to use /dev/random to initialize secure magic seed for -stamping footers. Otherwise, the current time is used. - -NO_MALLINFO default: 0 -If defined, don't compile "mallinfo". This can be a simple way -of dealing with mismatches between system declarations and -those in this file. - -MALLINFO_FIELD_TYPE default: size_t -The type of the fields in the mallinfo struct. This was originally -defined as "int" in SVID etc, but is more usefully defined as -size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set - -REALLOC_ZERO_BYTES_FREES default: not defined -This should be set if a call to realloc with zero bytes should -be the same as a call to free. Some people think it should. Otherwise, -since this malloc returns a unique pointer for malloc(0), so does -realloc(p, 0). - -LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H -LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H -LACKS_STDLIB_H default: NOT defined unless on DL_PLATFORM_WIN32 -Define these if your system does not have these header files. -You might need to manually insert some of the declarations they provide. - -DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, -system_info.dwAllocationGranularity in DL_PLATFORM_WIN32, -otherwise 64K. -Also settable using mallopt(M_GRANULARITY, x) -The unit for allocating and deallocating memory from the system. On -most systems with contiguous MORECORE, there is no reason to -make this more than a page. However, systems with MMAP tend to -either require or encourage larger granularities. You can increase -this value to prevent system allocation functions to be called so -often, especially if they are slow. The value must be at least one -page and must be a power of two. Setting to 0 causes initialization -to either page size or win32 region size. (Note: In previous -versions of malloc, the equivalent of this option was called -"TOP_PAD") - -DEFAULT_TRIM_THRESHOLD default: 2MB -Also settable using mallopt(M_TRIM_THRESHOLD, x) -The maximum amount of unused top-most memory to keep before -releasing via malloc_trim in free(). Automatic trimming is mainly -useful in long-lived programs using contiguous MORECORE. Because -trimming via sbrk can be slow on some systems, and can sometimes be -wasteful (in cases where programs immediately afterward allocate -more large chunks) the value should be high enough so that your -overall system performance would improve by releasing this much -memory. As a rough guide, you might set to a value close to the -average size of a process (program) running on your system. -Releasing this much memory would allow such a process to run in -memory. Generally, it is worth tuning trim thresholds when a -program undergoes phases where several large chunks are allocated -and released in ways that can reuse each other's storage, perhaps -mixed with phases where there are no such chunks at all. The trim -value must be greater than page size to have any useful effect. To -disable trimming completely, you can set to MAX_SIZE_T. Note that the trick -some people use of mallocing a huge space and then freeing it at -program startup, in an attempt to reserve system memory, doesn't -have the intended effect under automatic trimming, since that memory -will immediately be returned to the system. - -DEFAULT_MMAP_THRESHOLD default: 256K -Also settable using mallopt(M_MMAP_THRESHOLD, x) -The request size threshold for using MMAP to directly service a -request. Requests of at least this size that cannot be allocated -using already-existing space will be serviced via mmap. (If enough -normal freed space already exists it is used instead.) Using mmap -segregates relatively large chunks of memory so that they can be -individually obtained and released from the host system. A request -serviced through mmap is never reused by any other request (at least -not directly; the system may just so happen to remap successive -requests to the same locations). Segregating space in this way has -the benefits that: Mmapped space can always be individually released -back to the system, which helps keep the system level memory demands -of a long-lived program low. Also, mapped memory doesn't become -`locked' between other chunks, as can happen with normally allocated -chunks, which means that even trimming via malloc_trim would not -release them. However, it has the disadvantage that the space -cannot be reclaimed, consolidated, and then used to service later -requests, as happens with normal chunks. The advantages of mmap -nearly always outweigh disadvantages for "large" chunks, but the -value of "large" may vary across systems. The default is an -empirically derived value that works well in most systems. You can -disable mmap by setting to MAX_SIZE_T. - -MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP -The number of consolidated frees between checks to release -unused segments when freeing. When using non-contiguous segments, -especially with multiple mspaces, checking only for topmost space -doesn't always suffice to trigger trimming. To compensate for this, -free() will, with a period of MAX_RELEASE_CHECK_RATE (or the -current number of segments, if greater) try to release unused -segments to the OS when freeing chunks that result in -consolidation. The best value for this parameter is a compromise -between slowing down frees with relatively costly checks that -rarely trigger versus holding on to unused memory. To effectively -disable, set to MAX_SIZE_T. This may lead to a very slight speed -improvement at the expense of carrying around more memory. -*/ - -/* Version identifier to allow people to support multiple versions */ -#ifndef DLMALLOC_VERSION -#define DLMALLOC_VERSION 20804 -#endif /* DLMALLOC_VERSION */ - -#include "rdlmalloc-options.h" - -#ifndef WIN32 -#if defined(_XBOX) || defined(X360) -#else -#if defined(_WIN32) -#define DL_PLATFORM_WIN32 1 -#endif /* _WIN32 */ -#ifdef _WIN32_WCE -#define LACKS_FCNTL_H -#define DL_PLATFORM_WIN32 1 -#endif /* _WIN32_WCE */ -#endif -#else -#define DL_PLATFORM_WIN32 1 -#endif /* DL_PLATFORM_WIN32 */ - -#if defined(_XBOX) || defined(X360) -#define HAVE_MMAP 1 -#define HAVE_MORECORE 0 -#define LACKS_UNISTD_H -#define LACKS_SYS_PARAM_H -#define LACKS_SYS_MMAN_H -#define LACKS_STRING_H -#define LACKS_STRINGS_H -#define LACKS_SYS_TYPES_H -#define LACKS_ERRNO_H -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION -#endif -#define MMAP_CLEARS 1 -#endif - -#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) -#define LACKS_SYS_PARAM_H -#include "sysutil\sysutil_sysparam.h" -#define LACKS_SYS_MMAN_H -#endif - - -#ifdef DL_PLATFORM_WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#define HAVE_MMAP 1 -#define HAVE_MORECORE 0 -#define LACKS_UNISTD_H -#define LACKS_SYS_PARAM_H -#define LACKS_SYS_MMAN_H -#define LACKS_STRING_H -#define LACKS_STRINGS_H -#define LACKS_SYS_TYPES_H -#define LACKS_ERRNO_H -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION -#endif /* MALLOC_FAILURE_ACTION */ -#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ -#define MMAP_CLEARS 0 -#else -#define MMAP_CLEARS 1 -#endif /* _WIN32_WCE */ -#endif /* DL_PLATFORM_WIN32 */ - -#if defined(DARWIN) || defined(_DARWIN) -/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ -#ifndef HAVE_MORECORE -#define HAVE_MORECORE 0 -#define HAVE_MMAP 1 -/* OSX allocators provide 16 byte alignment */ -#ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT ((size_t)16U) -#endif -#endif /* HAVE_MORECORE */ -#endif /* DARWIN */ - -#ifndef LACKS_SYS_TYPES_H -#include /* For size_t */ -#endif /* LACKS_SYS_TYPES_H */ - -#if (defined(__GNUC__) && ((defined(__i386__) || defined(__x86_64__)))) || (defined(_MSC_VER) && _MSC_VER>=1310) -#define SPIN_LOCKS_AVAILABLE 1 -#else -#define SPIN_LOCKS_AVAILABLE 0 -#endif - -/* The maximum possible size_t value has all bits set */ -#define MAX_SIZE_T (~(size_t)0) - -#ifndef ONLY_MSPACES -#define ONLY_MSPACES 0 /* define to a value */ -#else -#define ONLY_MSPACES 1 -#endif /* ONLY_MSPACES */ -#ifndef MSPACES -#if ONLY_MSPACES -#define MSPACES 1 -#else /* ONLY_MSPACES */ -#define MSPACES 0 -#endif /* ONLY_MSPACES */ -#endif /* MSPACES */ -#ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT ((size_t)8U) -#endif /* MALLOC_ALIGNMENT */ -#ifndef FOOTERS -#define FOOTERS 0 -#endif /* FOOTERS */ -#ifndef ABORT -#define ABORT abort() -#endif /* ABORT */ -#ifndef ABORT_ON_ASSERT_FAILURE -#define ABORT_ON_ASSERT_FAILURE 1 -#endif /* ABORT_ON_ASSERT_FAILURE */ -#ifndef PROCEED_ON_ERROR -#define PROCEED_ON_ERROR 0 -#endif /* PROCEED_ON_ERROR */ -#ifndef USE_LOCKS -#define USE_LOCKS 0 -#endif /* USE_LOCKS */ -#ifndef USE_SPIN_LOCKS -#if USE_LOCKS && SPIN_LOCKS_AVAILABLE -#define USE_SPIN_LOCKS 1 -#else -#define USE_SPIN_LOCKS 0 -#endif /* USE_LOCKS && SPIN_LOCKS_AVAILABLE. */ -#endif /* USE_SPIN_LOCKS */ -#ifndef INSECURE -#define INSECURE 0 -#endif /* INSECURE */ -#ifndef HAVE_MMAP -#define HAVE_MMAP 1 -#endif /* HAVE_MMAP */ -#ifndef MMAP_CLEARS -#define MMAP_CLEARS 1 -#endif /* MMAP_CLEARS */ -#ifndef HAVE_MREMAP -#ifdef linux -#define HAVE_MREMAP 1 -#else /* linux */ -#define HAVE_MREMAP 0 -#endif /* linux */ -#endif /* HAVE_MREMAP */ -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION errno = ENOMEM; -#endif /* MALLOC_FAILURE_ACTION */ -#ifndef HAVE_MORECORE -#if ONLY_MSPACES -#define HAVE_MORECORE 0 -#else /* ONLY_MSPACES */ -#define HAVE_MORECORE 1 -#endif /* ONLY_MSPACES */ -#endif /* HAVE_MORECORE */ -#if !HAVE_MORECORE -#define MORECORE_CONTIGUOUS 0 -#else /* !HAVE_MORECORE */ -#define MORECORE_DEFAULT sbrk -#ifndef MORECORE_CONTIGUOUS -#define MORECORE_CONTIGUOUS 1 -#endif /* MORECORE_CONTIGUOUS */ -#endif /* HAVE_MORECORE */ -#ifndef DEFAULT_GRANULARITY -#if (MORECORE_CONTIGUOUS || defined(DL_PLATFORM_WIN32)) -#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ -#else /* MORECORE_CONTIGUOUS */ -#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) -#endif /* MORECORE_CONTIGUOUS */ -#endif /* DEFAULT_GRANULARITY */ -#ifndef DEFAULT_TRIM_THRESHOLD -#ifndef MORECORE_CANNOT_TRIM -#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) -#else /* MORECORE_CANNOT_TRIM */ -#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T -#endif /* MORECORE_CANNOT_TRIM */ -#endif /* DEFAULT_TRIM_THRESHOLD */ -#ifndef DEFAULT_MMAP_THRESHOLD -#if HAVE_MMAP -#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) -#else /* HAVE_MMAP */ -#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T -#endif /* HAVE_MMAP */ -#endif /* DEFAULT_MMAP_THRESHOLD */ -#ifndef MAX_RELEASE_CHECK_RATE -#if HAVE_MMAP -#define MAX_RELEASE_CHECK_RATE 4095 -#else -#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T -#endif /* HAVE_MMAP */ -#endif /* MAX_RELEASE_CHECK_RATE */ -#ifndef USE_BUILTIN_FFS -#define USE_BUILTIN_FFS 0 -#endif /* USE_BUILTIN_FFS */ -#ifndef USE_DEV_RANDOM -#define USE_DEV_RANDOM 0 -#endif /* USE_DEV_RANDOM */ -#ifndef NO_MALLINFO -#define NO_MALLINFO 0 -#endif /* NO_MALLINFO */ -#ifndef MALLINFO_FIELD_TYPE -#define MALLINFO_FIELD_TYPE size_t -#endif /* MALLINFO_FIELD_TYPE */ -#ifndef NO_SEGMENT_TRAVERSAL -#define NO_SEGMENT_TRAVERSAL 0 -#endif /* NO_SEGMENT_TRAVERSAL */ - -/* -mallopt tuning options. SVID/XPG defines four standard parameter -numbers for mallopt, normally defined in malloc.h. None of these -are used in this malloc, so setting them has no effect. But this -malloc does support the following options. -*/ - -#define M_TRIM_THRESHOLD (-1) -#define M_GRANULARITY (-2) -#define M_MMAP_THRESHOLD (-3) - -/* ------------------------ Mallinfo declarations ------------------------ */ - -#if !NO_MALLINFO -/* -This version of malloc supports the standard SVID/XPG mallinfo -routine that returns a struct containing usage properties and -statistics. It should work on any system that has a -/usr/include/malloc.h defining struct mallinfo. The main -declaration needed is the mallinfo struct that is returned (by-copy) -by mallinfo(). The malloinfo struct contains a bunch of fields that -are not even meaningful in this version of malloc. These fields are -are instead filled by mallinfo() with other numbers that might be of -interest. - -HAVE_USR_INCLUDE_MALLOC_H should be set if you have a -/usr/include/malloc.h file that includes a declaration of struct -mallinfo. If so, it is included; else a compliant version is -declared below. These must be precisely the same for mallinfo() to -work. The original SVID version of this struct, defined on most -systems with mallinfo, declares all fields as ints. But some others -define as unsigned long. If your system defines the fields using a -type of different width than listed here, you MUST #include your -system version and #define HAVE_USR_INCLUDE_MALLOC_H. -*/ - -/* #define HAVE_USR_INCLUDE_MALLOC_H */ - -#ifdef HAVE_USR_INCLUDE_MALLOC_H -#include "/usr/include/malloc.h" -#else /* HAVE_USR_INCLUDE_MALLOC_H */ -#ifndef STRUCT_MALLINFO_DECLARED -#define STRUCT_MALLINFO_DECLARED 1 -struct mallinfo { - MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ - MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ - MALLINFO_FIELD_TYPE smblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ - MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ - MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ - MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ - MALLINFO_FIELD_TYPE fordblks; /* total free space */ - MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ -}; -#endif /* STRUCT_MALLINFO_DECLARED */ -#endif /* HAVE_USR_INCLUDE_MALLOC_H */ -#endif /* NO_MALLINFO */ - -/* -Try to persuade compilers to inline. The most critical functions for -inlining are defined as macros, so these aren't used for them. -*/ - -#ifndef FORCEINLINE -#if defined(__GNUC__) -#define FORCEINLINE __inline __attribute__ ((always_inline)) -#elif defined(_MSC_VER) -#define FORCEINLINE __forceinline -#endif -#endif -#ifndef NOINLINE -#if defined(__GNUC__) -#define NOINLINE __attribute__ ((noinline)) -#elif defined(_MSC_VER) -#define NOINLINE __declspec(noinline) -#else -#define NOINLINE -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#ifndef FORCEINLINE -#define FORCEINLINE inline -#endif -#endif /* __cplusplus */ -#ifndef FORCEINLINE -#define FORCEINLINE -#endif - -#if !ONLY_MSPACES - - /* ------------------- Declarations of public routines ------------------- */ - -#ifndef USE_DL_PREFIX -#define rdlcalloc calloc -#define rdlfree free -#define rdlmalloc malloc -#define rdlmemalign memalign -#define rdlrealloc realloc -#define rdlvalloc valloc -#define rdlpvalloc pvalloc -#define rdlmallinfo mallinfo -#define rdlmallopt mallopt -#define rdlmalloc_trim malloc_trim -#define rdlmalloc_stats malloc_stats -#define rdlmalloc_usable_size malloc_usable_size -#define rdlmalloc_footprint malloc_footprint -#define dlmalloc_max_footprint malloc_max_footprint -#define rdlindependent_calloc independent_calloc -#define rdlindependent_comalloc independent_comalloc -#endif /* USE_DL_PREFIX */ - - - /* - malloc(size_t n) - Returns a pointer to a newly allocated chunk of at least n bytes, or - null if no space is available, in which case errno is set to ENOMEM - on ANSI C systems. - - If n is zero, malloc returns a minimum-sized chunk. (The minimum - size is 16 bytes on most 32bit systems, and 32 bytes on 64bit - systems.) Note that size_t is an unsigned type, so calls with - arguments that would be negative if signed are interpreted as - requests for huge amounts of space, which will often fail. The - maximum supported value of n differs across systems, but is in all - cases less than the maximum representable value of a size_t. - */ - void* rdlmalloc(size_t); - - /* - free(void* p) - Releases the chunk of memory pointed to by p, that had been previously - allocated using malloc or a related routine such as realloc. - It has no effect if p is null. If p was not malloced or already - freed, free(p) will by default cause the current program to abort. - */ - void rdlfree(void*); - - /* - calloc(size_t n_elements, size_t element_size); - Returns a pointer to n_elements * element_size bytes, with all locations - set to zero. - */ - void* rdlcalloc(size_t, size_t); - - /* - realloc(void* p, size_t n) - Returns a pointer to a chunk of size n that contains the same data - as does chunk p up to the minimum of (n, p's size) bytes, or null - if no space is available. - - The returned pointer may or may not be the same as p. The algorithm - prefers extending p in most cases when possible, otherwise it - employs the equivalent of a malloc-copy-free sequence. - - If p is null, realloc is equivalent to malloc. - - If space is not available, realloc returns null, errno is set (if on - ANSI) and p is NOT freed. - - if n is for fewer bytes than already held by p, the newly unused - space is lopped off and freed if possible. realloc with a size - argument of zero (re)allocates a minimum-sized chunk. - - The old unix realloc convention of allowing the last-free'd chunk - to be used as an argument to realloc is not supported. - */ - - void* rdlrealloc(void*, size_t); - - /* - memalign(size_t alignment, size_t n); - Returns a pointer to a newly allocated chunk of n bytes, aligned - in accord with the alignment argument. - - The alignment argument should be a power of two. If the argument is - not a power of two, the nearest greater power is used. - 8-byte alignment is guaranteed by normal malloc calls, so don't - bother calling memalign with an argument of 8 or less. - - Overreliance on memalign is a sure way to fragment space. - */ - void* rdlmemalign(size_t, size_t); - - /* - valloc(size_t n); - Equivalent to memalign(pagesize, n), where pagesize is the page - size of the system. If the pagesize is unknown, 4096 is used. - */ - void* rdlvalloc(size_t); - - /* - mallopt(int parameter_number, int parameter_value) - Sets tunable parameters The format is to provide a - (parameter-number, parameter-value) pair. mallopt then sets the - corresponding parameter to the argument value if it can (i.e., so - long as the value is meaningful), and returns 1 if successful else - 0. To workaround the fact that mallopt is specified to use int, - not size_t parameters, the value -1 is specially treated as the - maximum unsigned size_t value. - - SVID/XPG/ANSI defines four standard param numbers for mallopt, - normally defined in malloc.h. None of these are use in this malloc, - so setting them has no effect. But this malloc also supports other - options in mallopt. See below for details. Briefly, supported - parameters are as follows (listed defaults are for "typical" - configurations). - - Symbol param # default allowed param values - M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) - M_GRANULARITY -2 page size any power of 2 >= page size - M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) - */ - int rdlmallopt(int, int); - - /* - malloc_footprint(); - Returns the number of bytes obtained from the system. The total - number of bytes allocated by malloc, realloc etc., is less than this - value. Unlike mallinfo, this function returns only a precomputed - result, so can be called frequently to monitor memory consumption. - Even if locks are otherwise defined, this function does not use them, - so results might not be up to date. - */ - size_t rdlmalloc_footprint(void); - - /* - malloc_max_footprint(); - Returns the maximum number of bytes obtained from the system. This - value will be greater than current footprint if deallocated space - has been reclaimed by the system. The peak number of bytes allocated - by malloc, realloc etc., is less than this value. Unlike mallinfo, - this function returns only a precomputed result, so can be called - frequently to monitor memory consumption. Even if locks are - otherwise defined, this function does not use them, so results might - not be up to date. - */ - size_t dlmalloc_max_footprint(void); - -#if !NO_MALLINFO - /* - mallinfo() - Returns (by copy) a struct containing various summary statistics: - - arena: current total non-mmapped bytes allocated from system - ordblks: the number of free chunks - smblks: always zero. - hblks: current number of mmapped regions - hblkhd: total bytes held in mmapped regions - usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. - fsmblks: always zero - uordblks: current total allocated space (normal or mmapped) - fordblks: total free space - keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) - - Because these fields are ints, but internal bookkeeping may - be kept as longs, the reported values may wrap around zero and - thus be inaccurate. - */ - struct mallinfo rdlmallinfo(void); -#endif /* NO_MALLINFO */ - - /* - independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); - - independent_calloc is similar to calloc, but instead of returning a - single cleared space, it returns an array of pointers to n_elements - independent elements that can hold contents of size elem_size, each - of which starts out cleared, and can be independently freed, - realloc'ed etc. The elements are guaranteed to be adjacently - allocated (this is not guaranteed to occur with multiple callocs or - mallocs), which may also improve cache locality in some - applications. - - The "chunks" argument is optional (i.e., may be null, which is - probably the most typical usage). If it is null, the returned array - is itself dynamically allocated and should also be freed when it is - no longer needed. Otherwise, the chunks array must be of at least - n_elements in length. It is filled in with the pointers to the - chunks. - - In either case, independent_calloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and "chunks" - is null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use regular calloc and assign pointers into this - space to represent elements. (In this case though, you cannot - independently free elements.) - - independent_calloc simplifies and speeds up implementations of many - kinds of pools. It may also be useful when constructing large data - structures that initially have a fixed number of fixed-sized nodes, - but the number is not known at compile time, and some of the nodes - may later need to be freed. For example: - - struct Node { int item; struct Node* next; }; - - struct Node* build_list() { - struct Node** pool; - int n = read_number_of_nodes_needed(); - if (n <= 0) return 0; - pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); - if (pool == 0) die(); - // organize into a linked list... - struct Node* first = pool[0]; - for (i = 0; i < n-1; ++i) - pool[i]->next = pool[i+1]; - free(pool); // Can now free the array (or not, if it is needed later) - return first; - } - */ - void** rdlindependent_calloc(size_t, size_t, void**); - - /* - independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); - - independent_comalloc allocates, all at once, a set of n_elements - chunks with sizes indicated in the "sizes" array. It returns - an array of pointers to these elements, each of which can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null). If it is null - the returned array is itself dynamically allocated and should also - be freed when it is no longer needed. Otherwise, the chunks array - must be of at least n_elements in length. It is filled in with the - pointers to the chunks. - - In either case, independent_comalloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and chunks is - null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use a single regular malloc, and assign pointers at - particular offsets in the aggregate space. (In this case though, you - cannot independently free elements.) - - independent_comallac differs from independent_calloc in that each - element may have a different size, and also that it does not - automatically clear elements. - - independent_comalloc can be used to speed up allocation in cases - where several structs or objects must always be allocated at the - same time. For example: - - struct Head { ... } - struct Foot { ... } - - void send_message(char* msg) { - int msglen = strlen(msg); - size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; - void* chunks[3]; - if (independent_comalloc(3, sizes, chunks) == 0) - die(); - struct Head* head = (struct Head*)(chunks[0]); - char* body = (char*)(chunks[1]); - struct Foot* foot = (struct Foot*)(chunks[2]); - // ... - } - - In general though, independent_comalloc is worth using only for - larger values of n_elements. For small values, you probably won't - detect enough difference from series of malloc calls to bother. - - Overuse of independent_comalloc can increase overall memory usage, - since it cannot reuse existing noncontiguous small chunks that - might be available for some of the elements. - */ - void** rdlindependent_comalloc(size_t, size_t*, void**); - - - /* - pvalloc(size_t n); - Equivalent to valloc(minimum-page-that-holds(n)), that is, - round up n to nearest pagesize. - */ - void* rdlpvalloc(size_t); - - /* - malloc_trim(size_t pad); - - If possible, gives memory back to the system (via negative arguments - to sbrk) if there is unused memory at the `high' end of the malloc - pool or in unused MMAP segments. You can call this after freeing - large blocks of memory to potentially reduce the system-level memory - requirements of a program. However, it cannot guarantee to reduce - memory. Under some allocation patterns, some large free blocks of - memory will be locked between two used chunks, so they cannot be - given back to the system. - - The `pad' argument to malloc_trim represents the amount of free - trailing space to leave untrimmed. If this argument is zero, only - the minimum amount of memory to maintain internal data structures - will be left. Non-zero arguments can be supplied to maintain enough - trailing space to service future expected allocations without having - to re-obtain memory from the system. - - Malloc_trim returns 1 if it actually released any memory, else 0. - */ - int rdlmalloc_trim(size_t); - - /* - malloc_stats(); - Prints on stderr the amount of space obtained from the system (both - via sbrk and mmap), the maximum amount (which may be more than - current if malloc_trim and/or munmap got called), and the current - number of bytes allocated via malloc (or realloc, etc) but not yet - freed. Note that this is the number of bytes allocated, not the - number requested. It will be larger than the number requested - because of alignment and bookkeeping overhead. Because it includes - alignment wastage as being in use, this figure may be greater than - zero even when no user-level chunks are allocated. - - The reported current and maximum system memory can be inaccurate if - a program makes other calls to system memory allocation functions - (normally sbrk) outside of malloc. - - malloc_stats prints only the most commonly interesting statistics. - More information can be obtained by calling mallinfo. - */ - void rdlmalloc_stats(void); - -#endif /* ONLY_MSPACES */ - - /* - malloc_usable_size(void* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. malloc_usable_size can be more useful in - debugging and assertions, for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); - */ - size_t rdlmalloc_usable_size(void*); - - -#if MSPACES - - /* - mspace is an opaque type representing an independent - region of space that supports rak_mspace_malloc, etc. - */ - typedef void* mspace; - - /* - rak_create_mspace creates and returns a new independent space with the - given initial capacity, or, if 0, the default granularity size. It - returns null if there is no system memory available to create the - space. If argument locked is non-zero, the space uses a separate - lock to control access. The capacity of the space will grow - dynamically as needed to service rak_mspace_malloc requests. You can - control the sizes of incremental increases of this space by - compiling with a different DEFAULT_GRANULARITY or dynamically - setting with mallopt(M_GRANULARITY, value). - */ - mspace rak_create_mspace(size_t capacity, int locked); - - /* - rak_destroy_mspace destroys the given space, and attempts to return all - of its memory back to the system, returning the total number of - bytes freed. After destruction, the results of access to all memory - used by the space become undefined. - */ - size_t rak_destroy_mspace(mspace msp); - - /* - rak_create_mspace_with_base uses the memory supplied as the initial base - of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this - space is used for bookkeeping, so the capacity must be at least this - large. (Otherwise 0 is returned.) When this initial space is - exhausted, additional memory will be obtained from the system. - Destroying this space will deallocate all additionally allocated - space (if possible) but not the initial base. - */ - mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked); - - /* - rak_mspace_track_large_chunks controls whether requests for large chunks - are allocated in their own untracked mmapped regions, separate from - others in this mspace. By default large chunks are not tracked, - which reduces fragmentation. However, such chunks are not - necessarily released to the system upon rak_destroy_mspace. Enabling - tracking by setting to true may increase fragmentation, but avoids - leakage when relying on rak_destroy_mspace to release all memory - allocated using this space. The function returns the previous - setting. - */ - int rak_mspace_track_large_chunks(mspace msp, int enable); - - - /* - rak_mspace_malloc behaves as malloc, but operates within - the given space. - */ - void* rak_mspace_malloc(mspace msp, size_t bytes); - - /* - rak_mspace_free behaves as free, but operates within - the given space. - - If compiled with FOOTERS==1, rak_mspace_free is not actually needed. - free may be called instead of rak_mspace_free because freed chunks from - any space are handled by their originating spaces. - */ - void rak_mspace_free(mspace msp, void* mem); - - /* - rak_mspace_realloc behaves as realloc, but operates within - the given space. - - If compiled with FOOTERS==1, rak_mspace_realloc is not actually - needed. realloc may be called instead of rak_mspace_realloc because - realloced chunks from any space are handled by their originating - spaces. - */ - void* rak_mspace_realloc(mspace msp, void* mem, size_t newsize); - - /* - rak_mspace_calloc behaves as calloc, but operates within - the given space. - */ - void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); - - /* - rak_mspace_memalign behaves as memalign, but operates within - the given space. - */ - void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes); - - /* - rak_mspace_independent_calloc behaves as independent_calloc, but - operates within the given space. - */ - void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]); - - /* - rak_mspace_independent_comalloc behaves as independent_comalloc, but - operates within the given space. - */ - void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]); - - /* - rak_mspace_footprint() returns the number of bytes obtained from the - system for this space. - */ - size_t rak_mspace_footprint(mspace msp); - - /* - mspace_max_footprint() returns the peak number of bytes obtained from the - system for this space. - */ - size_t mspace_max_footprint(mspace msp); - - -#if !NO_MALLINFO - /* - rak_mspace_mallinfo behaves as mallinfo, but reports properties of - the given space. - */ - struct mallinfo rak_mspace_mallinfo(mspace msp); -#endif /* NO_MALLINFO */ - - /* - malloc_usable_size(void* p) behaves the same as malloc_usable_size; - */ - size_t rak_mspace_usable_size(void* mem); - - /* - rak_mspace_malloc_stats behaves as malloc_stats, but reports - properties of the given space. - */ - void rak_mspace_malloc_stats(mspace msp); - - /* - rak_mspace_trim behaves as malloc_trim, but - operates within the given space. - */ - int rak_mspace_trim(mspace msp, size_t pad); - - /* - An alias for mallopt. - */ - int rak_mspace_mallopt(int, int); - -#endif /* MSPACES */ - -#ifdef __cplusplus -}; /* end of extern "C" */ -#endif /* __cplusplus */ - -/* -======================================================================== -To make a fully customizable malloc.h header file, cut everything -above this line, put into file malloc.h, edit to suit, and #include it -on the next line, as well as in programs that use this malloc. -======================================================================== -*/ - -/* #include "malloc.h" */ - -/*------------------------------ internal #includes ---------------------- */ - -#ifdef DL_PLATFORM_WIN32 -#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ -#endif /* DL_PLATFORM_WIN32 */ - -#include /* for printing in malloc_stats */ - -#ifndef LACKS_ERRNO_H -#include /* for MALLOC_FAILURE_ACTION */ -#endif /* LACKS_ERRNO_H */ - -#if FOOTERS || DEBUG -#include /* for magic initialization */ -#endif /* FOOTERS */ - -#ifndef LACKS_STDLIB_H -#include /* for abort() */ -#endif /* LACKS_STDLIB_H */ - -#ifdef DEBUG -#if ABORT_ON_ASSERT_FAILURE -#undef assert -#define assert(x) if(!(x)) ABORT -#else /* ABORT_ON_ASSERT_FAILURE */ -#include -#endif /* ABORT_ON_ASSERT_FAILURE */ -#else /* DEBUG */ -#ifndef assert -#define assert(x) -#endif -#define DEBUG 0 -#endif /* DEBUG */ - -#ifndef LACKS_STRING_H -#include /* for memset etc */ -#endif /* LACKS_STRING_H */ - -#if USE_BUILTIN_FFS -#ifndef LACKS_STRINGS_H -#include /* for ffs */ -#endif /* LACKS_STRINGS_H */ -#endif /* USE_BUILTIN_FFS */ - -#if HAVE_MMAP -#ifndef LACKS_SYS_MMAN_H -/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ -#if (defined(linux) && !defined(__USE_GNU)) -#define __USE_GNU 1 -#include /* for mmap */ -#undef __USE_GNU -#else -#include /* for mmap */ -#endif /* linux */ -#endif /* LACKS_SYS_MMAN_H */ -#ifndef LACKS_FCNTL_H -#include -#endif /* LACKS_FCNTL_H */ -#endif /* HAVE_MMAP */ - -#ifndef LACKS_UNISTD_H -#include /* for sbrk, sysconf */ -#else /* LACKS_UNISTD_H */ -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) -extern void* sbrk(ptrdiff_t); -#endif /* FreeBSD etc */ -#endif /* LACKS_UNISTD_H */ - -/* Declarations for locking */ -#if USE_LOCKS -#if defined(_XBOX) || defined(X360) -#pragma intrinsic (_InterlockedCompareExchange) -#pragma intrinsic (_InterlockedExchange) -#define interlockedcompareexchange _InterlockedCompareExchange -#define interlockedexchange _InterlockedExchange -#elif !defined(DL_PLATFORM_WIN32) -#include -#if defined (__SVR4) && defined (__sun) /* solaris */ -#include -#endif /* solaris */ -#else -#ifndef _M_AMD64 -/* These are already defined on AMD64 builds */ -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); - LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* _M_AMD64 */ -#pragma intrinsic (_InterlockedCompareExchange) -#pragma intrinsic (_InterlockedExchange) -#define interlockedcompareexchange _InterlockedCompareExchange -#define interlockedexchange _InterlockedExchange -#endif /* Win32 */ -#endif /* USE_LOCKS */ - -/* Declarations for bit scanning on win32 */ -#if defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) -#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - unsigned char _BitScanForward(unsigned long *index, unsigned long mask); - unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#define BitScanForward _BitScanForward -#define BitScanReverse _BitScanReverse -#pragma intrinsic(_BitScanForward) -#pragma intrinsic(_BitScanReverse) -#endif /* BitScanForward */ -#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ - -#ifndef DL_PLATFORM_WIN32 -#ifndef malloc_getpagesize -# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ -# ifndef _SC_PAGE_SIZE -# define _SC_PAGE_SIZE _SC_PAGESIZE -# endif -# endif -# ifdef _SC_PAGE_SIZE -# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) -# else -# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) -extern size_t getpagesize(); -# define malloc_getpagesize getpagesize() -# else -# ifdef DL_PLATFORM_WIN32 /* use supplied emulation of getpagesize */ -# define malloc_getpagesize getpagesize() -# else -# ifndef LACKS_SYS_PARAM_H -# include -# endif -# ifdef EXEC_PAGESIZE -# define malloc_getpagesize EXEC_PAGESIZE -# else -# ifdef NBPG -# ifndef CLSIZE -# define malloc_getpagesize NBPG -# else -# define malloc_getpagesize (NBPG * CLSIZE) -# endif -# else -# ifdef NBPC -# define malloc_getpagesize NBPC -# else -# ifdef PAGESIZE -# define malloc_getpagesize PAGESIZE -# else /* just guess */ -# define malloc_getpagesize ((size_t)4096U) -# endif -# endif -# endif -# endif -# endif -# endif -# endif -#endif -#endif - - - -/* ------------------- size_t and alignment properties -------------------- */ - -/* The byte and bit size of a size_t */ -#define SIZE_T_SIZE (sizeof(size_t)) -#define SIZE_T_BITSIZE (sizeof(size_t) << 3) - -/* Some constants coerced to size_t */ -/* Annoying but necessary to avoid errors on some platforms */ -#define SIZE_T_ZERO ((size_t)0) -#define SIZE_T_ONE ((size_t)1) -#define SIZE_T_TWO ((size_t)2) -#define SIZE_T_FOUR ((size_t)4) -#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) -#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) -#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) -#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) - -/* The bit mask value corresponding to MALLOC_ALIGNMENT */ -#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) - -/* True if address a has acceptable alignment */ -#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) - -/* the number of bytes to offset an address to align it */ -#define align_offset(A)\ - ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ - ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) - -/* -------------------------- MMAP preliminaries ------------------------- */ - -/* -If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and -checks to fail so compiler optimizer can delete code rather than -using so many "#if"s. -*/ - - -/* MORECORE and MMAP must return MFAIL on failure */ -#define MFAIL ((void*)(MAX_SIZE_T)) -#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ - -#if HAVE_MMAP - -#if defined(_XBOX) || defined(X360) - /* Win32 MMAP via VirtualAlloc */ - static void* win32mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; - } - - /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ - static void* win32direct_mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, - PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; - } - - /* This function supports releasing coalesed segments */ - static int win32munmap(void* ptr, size_t size) { - MEMORY_BASIC_INFORMATION minfo; - char* cptr = (char*)ptr; - while (size) { - if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) - return -1; - if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || - minfo.State != MEM_COMMIT || minfo.RegionSize > size) - return -1; - if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) - return -1; - cptr += minfo.RegionSize; - size -= minfo.RegionSize; - } - return 0; - } - - #define RAK_MMAP_DEFAULT(s) win32mmap(s) - #define RAK_MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) - #define RAK_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) -#elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) - -inline int ___freeit_dlmalloc_default__(void* s) {free(s); return 0;} -#define RAK_MMAP_DEFAULT(s) malloc(s); -#define RAK_MUNMAP_DEFAULT(a, s) ___freeit_dlmalloc_default__(a); -#define RAK_DIRECT_MMAP_DEFAULT(s) malloc(s); - -#elif !defined(DL_PLATFORM_WIN32) - #define RAK_MUNMAP_DEFAULT(a, s) munmap((a), (s)) - #define MMAP_PROT (PROT_READ|PROT_WRITE) - #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) - #define MAP_ANONYMOUS MAP_ANON - #endif /* MAP_ANON */ - #ifdef MAP_ANONYMOUS - #define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) - #define RAK_MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) - #else /* MAP_ANONYMOUS */ - /* - Nearly all versions of mmap support MAP_ANONYMOUS, so the following - is unlikely to be needed, but is supplied just in case. - */ - #define MMAP_FLAGS (MAP_PRIVATE) - static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ - #define RAK_MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ - (dev_zero_fd = open("/dev/zero", O_RDWR), \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) - #endif /* MAP_ANONYMOUS */ - - #define RAK_DIRECT_MMAP_DEFAULT(s) RAK_MMAP_DEFAULT(s) - -#else /* DL_PLATFORM_WIN32 */ - - /* Win32 MMAP via VirtualAlloc */ - static FORCEINLINE void* win32mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; - } - - /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ - static FORCEINLINE void* win32direct_mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, - PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; - } - - /* This function supports releasing coalesed segments */ - static FORCEINLINE int win32munmap(void* ptr, size_t size) { - MEMORY_BASIC_INFORMATION minfo; - char* cptr = (char*)ptr; - while (size) { - if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) - return -1; - if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || - minfo.State != MEM_COMMIT || minfo.RegionSize > size) - return -1; - if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) - return -1; - cptr += minfo.RegionSize; - size -= minfo.RegionSize; - } - return 0; - } - - #define RAK_MMAP_DEFAULT(s) win32mmap(s) - #define RAK_MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) - #define RAK_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) -#endif /* DL_PLATFORM_WIN32 */ -#endif /* HAVE_MMAP */ - -#if HAVE_MREMAP -#ifndef DL_PLATFORM_WIN32 -#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) -#endif /* DL_PLATFORM_WIN32 */ -#endif /* HAVE_MREMAP */ - - -/** -* Define CALL_MORECORE -*/ -#if HAVE_MORECORE -#ifdef MORECORE -#define CALL_MORECORE(S) MORECORE(S) -#else /* MORECORE */ -#define CALL_MORECORE(S) MORECORE_DEFAULT(S) -#endif /* MORECORE */ -#else /* HAVE_MORECORE */ -#define CALL_MORECORE(S) MFAIL -#endif /* HAVE_MORECORE */ - -/** -* Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP -*/ -#if HAVE_MMAP -#define USE_MMAP_BIT (SIZE_T_ONE) - -#ifdef MMAP -#define CALL_MMAP(s) MMAP(s) -#else /* MMAP */ -#define CALL_MMAP(s) RAK_MMAP_DEFAULT(s) -#endif /* MMAP */ -#ifdef MUNMAP -#define CALL_MUNMAP(a, s) MUNMAP((a), (s)) -#else /* MUNMAP */ -#define CALL_MUNMAP(a, s) RAK_MUNMAP_DEFAULT((a), (s)) -#endif /* MUNMAP */ -#ifdef DIRECT_MMAP -#define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) -#else /* DIRECT_MMAP */ -#define CALL_DIRECT_MMAP(s) RAK_DIRECT_MMAP_DEFAULT(s) -#endif /* DIRECT_MMAP */ -#else /* HAVE_MMAP */ -#define USE_MMAP_BIT (SIZE_T_ZERO) - -#define MMAP(s) MFAIL -#define MUNMAP(a, s) (-1) -#define DIRECT_MMAP(s) MFAIL -#define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) -#define CALL_MMAP(s) MMAP(s) -#define CALL_MUNMAP(a, s) MUNMAP((a), (s)) -#endif /* HAVE_MMAP */ - -/** -* Define CALL_MREMAP -*/ -#if HAVE_MMAP && HAVE_MREMAP -#ifdef MREMAP -#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) -#else /* MREMAP */ -#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) -#endif /* MREMAP */ -#else /* HAVE_MMAP && HAVE_MREMAP */ -#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL -#endif /* HAVE_MMAP && HAVE_MREMAP */ - -/* mstate bit set if continguous morecore disabled or failed */ -#define USE_NONCONTIGUOUS_BIT (4U) - -/* segment bit set in rak_create_mspace_with_base */ -#define EXTERN_BIT (8U) - - -#endif /* MALLOC_280_H */ - +#ifdef _RAKNET_SUPPORT_DL_MALLOC + +/* +Default header file for malloc-2.8.x, written by Doug Lea +and released to the public domain, as explained at +http://creativecommons.org/licenses/publicdomain. + +last update: Wed May 27 14:25:17 2009 Doug Lea (dl at gee) + +This header is for ANSI C/C++ only. You can set any of +the following #defines before including: + +* If USE_DL_PREFIX is defined, it is assumed that malloc.c +was also compiled with this option, so all routines +have names starting with "dl". + +* If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this +file will be #included AFTER . This is needed only if +your system defines a struct mallinfo that is incompatible with the +standard one declared here. Otherwise, you can include this file +INSTEAD of your system system . At least on ANSI, all +declarations should be compatible with system versions + +* If MSPACES is defined, declarations for mspace versions are included. +*/ + +#ifndef MALLOC_280_H +#define MALLOC_280_H + +#include "rdlmalloc-options.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* for size_t */ + +#ifndef ONLY_MSPACES +#define ONLY_MSPACES 0 /* define to a value */ +#endif /* ONLY_MSPACES */ +#ifndef NO_MALLINFO +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ + + +#if !ONLY_MSPACES + +#ifndef USE_DL_PREFIX +#define rdlcalloc calloc +#define rdlfree free +#define rdlmalloc malloc +#define rdlmemalign memalign +#define rdlrealloc realloc +#define rdlvalloc valloc +#define rdlpvalloc pvalloc +#define rdlmallinfo mallinfo +#define rdlmallopt mallopt +#define rdlmalloc_trim malloc_trim +#define rdlmalloc_stats malloc_stats +#define rdlmalloc_usable_size malloc_usable_size +#define rdlmalloc_footprint malloc_footprint +#define rdlindependent_calloc independent_calloc +#define rdlindependent_comalloc independent_comalloc +#endif /* USE_DL_PREFIX */ +#if !NO_MALLINFO +#ifndef HAVE_USR_INCLUDE_MALLOC_H +#ifndef _MALLOC_H +#ifndef MALLINFO_FIELD_TYPE +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ +#ifndef STRUCT_MALLINFO_DECLARED +#define STRUCT_MALLINFO_DECLARED 1 + struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ + }; +#endif /* STRUCT_MALLINFO_DECLARED */ +#endif /* _MALLOC_H */ +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* !NO_MALLINFO */ + + /* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or + null if no space is available, in which case errno is set to ENOMEM + on ANSI C systems. + + If n is zero, malloc returns a minimum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 32 bytes on 64bit + systems.) Note that size_t is an unsigned type, so calls with + arguments that would be negative if signed are interpreted as + requests for huge amounts of space, which will often fail. The + maximum supported value of n differs across systems, but is in all + cases less than the maximum representable value of a size_t. + */ + void* rdlmalloc(size_t); + + /* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. If p was not malloced or already + freed, free(p) will by default cuase the current program to abort. + */ + void rdlfree(void*); + + /* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. + */ + void* rdlcalloc(size_t, size_t); + + /* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p in most cases when possible, otherwise it + employs the equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. realloc with a size + argument of zero (re)allocates a minimum-sized chunk. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. + */ + + void* rdlrealloc(void*, size_t); + + /* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + */ + void* rdlmemalign(size_t, size_t); + + /* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. + */ + void* rdlvalloc(size_t); + + /* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. None of these are use in this malloc, + so setting them has no effect. But this malloc also supports other + options in mallopt: + + Symbol param # default allowed param values + M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) + M_GRANULARITY -2 page size any power of 2 >= page size + M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) + */ + int rdlmallopt(int, int); + +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) + + + /* + malloc_footprint(); + Returns the number of bytes obtained from the system. The total + number of bytes allocated by malloc, realloc etc., is less than this + value. Unlike mallinfo, this function returns only a precomputed + result, so can be called frequently to monitor memory consumption. + Even if locks are otherwise defined, this function does not use them, + so results might not be up to date. + */ + size_t rdlmalloc_footprint(); + +#if !NO_MALLINFO + /* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: always zero. + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: the maximum total allocated space. This will be greater + than current total if trimming has occurred. + fsmblks: always zero + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. + */ + + struct mallinfo rdlmallinfo(void); +#endif /* NO_MALLINFO */ + + /* + independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); + + independent_calloc is similar to calloc, but instead of returning a + single cleared space, it returns an array of pointers to n_elements + independent elements that can hold contents of size elem_size, each + of which starts out cleared, and can be independently freed, + realloc'ed etc. The elements are guaranteed to be adjacently + allocated (this is not guaranteed to occur with multiple callocs or + mallocs), which may also improve cache locality in some + applications. + + The "chunks" argument is optional (i.e., may be null, which is + probably the most typical usage). If it is null, the returned array + is itself dynamically allocated and should also be freed when it is + no longer needed. Otherwise, the chunks array must be of at least + n_elements in length. It is filled in with the pointers to the + chunks. + + In either case, independent_calloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and "chunks" + is null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use regular calloc and assign pointers into this + space to represent elements. (In this case though, you cannot + independently free elements.) + + independent_calloc simplifies and speeds up implementations of many + kinds of pools. It may also be useful when constructing large data + structures that initially have a fixed number of fixed-sized nodes, + but the number is not known at compile time, and some of the nodes + may later need to be freed. For example: + + struct Node { int item; struct Node* next; }; + + struct Node* build_list() { + struct Node** pool; + int n = read_number_of_nodes_needed(); + if (n <= 0) return 0; + pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); + if (pool == 0) die(); + // organize into a linked list... + struct Node* first = pool[0]; + for (i = 0; i < n-1; ++i) + pool[i]->next = pool[i+1]; + free(pool); // Can now free the array (or not, if it is needed later) + return first; + } + */ + void** rdlindependent_calloc(size_t, size_t, void**); + + /* + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + + independent_comalloc allocates, all at once, a set of n_elements + chunks with sizes indicated in the "sizes" array. It returns + an array of pointers to these elements, each of which can be + independently freed, realloc'ed etc. The elements are guaranteed to + be adjacently allocated (this is not guaranteed to occur with + multiple callocs or mallocs), which may also improve cache locality + in some applications. + + The "chunks" argument is optional (i.e., may be null). If it is null + the returned array is itself dynamically allocated and should also + be freed when it is no longer needed. Otherwise, the chunks array + must be of at least n_elements in length. It is filled in with the + pointers to the chunks. + + In either case, independent_comalloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and chunks is + null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use a single regular malloc, and assign pointers at + particular offsets in the aggregate space. (In this case though, you + cannot independently free elements.) + + independent_comallac differs from independent_calloc in that each + element may have a different size, and also that it does not + automatically clear elements. + + independent_comalloc can be used to speed up allocation in cases + where several structs or objects must always be allocated at the + same time. For example: + + struct Head { ... } + struct Foot { ... } + + void send_message(char* msg) { + int msglen = strlen(msg); + size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; + void* chunks[3]; + if (independent_comalloc(3, sizes, chunks) == 0) + die(); + struct Head* head = (struct Head*)(chunks[0]); + char* body = (char*)(chunks[1]); + struct Foot* foot = (struct Foot*)(chunks[2]); + // ... + } + + In general though, independent_comalloc is worth using only for + larger values of n_elements. For small values, you probably won't + detect enough difference from series of malloc calls to bother. + + Overuse of independent_comalloc can increase overall memory usage, + since it cannot reuse existing noncontiguous small chunks that + might be available for some of the elements. + */ + void** rdlindependent_comalloc(size_t, size_t*, void**); + + + /* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ + void* rdlpvalloc(size_t); + + /* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative arguments + to sbrk) if there is unused memory at the `high' end of the malloc + pool or in unused MMAP segments. You can call this after freeing + large blocks of memory to potentially reduce the system-level memory + requirements of a program. However, it cannot guarantee to reduce + memory. Under some allocation patterns, some large free blocks of + memory will be locked between two used chunks, so they cannot be + given back to the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, only + the minimum amount of memory to maintain internal data structures + will be left. Non-zero arguments can be supplied to maintain enough + trailing space to service future expected allocations without having + to re-obtain memory from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + */ + int rdlmalloc_trim(size_t); + + /* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. + */ + void rdlmalloc_stats(); + +#endif /* !ONLY_MSPACES */ + + /* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); + */ + size_t rdlmalloc_usable_size(void*); + + +#if MSPACES + + /* + mspace is an opaque type representing an independent + region of space that supports rak_mspace_malloc, etc. + */ + typedef void* mspace; + + /* + rak_create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service rak_mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). + */ + mspace rak_create_mspace(size_t capacity, int locked); + + /* + rak_destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. + */ + size_t rak_destroy_mspace(mspace msp); + + /* + rak_create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. + */ + mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked); + + /* + rak_mspace_track_large_chunks controls whether requests for large chunks + are allocated in their own untracked mmapped regions, separate from + others in this mspace. By default large chunks are not tracked, + which reduces fragmentation. However, such chunks are not + necessarily released to the system upon rak_destroy_mspace. Enabling + tracking by setting to true may increase fragmentation, but avoids + leakage when relying on rak_destroy_mspace to release all memory + allocated using this space. The function returns the previous + setting. + */ + int rak_mspace_track_large_chunks(mspace msp, int enable); + + /* + rak_mspace_malloc behaves as malloc, but operates within + the given space. + */ + void* rak_mspace_malloc(mspace msp, size_t bytes); + + /* + rak_mspace_free behaves as free, but operates within + the given space. + + If compiled with FOOTERS==1, rak_mspace_free is not actually needed. + free may be called instead of rak_mspace_free because freed chunks from + any space are handled by their originating spaces. + */ + void rak_mspace_free(mspace msp, void* mem); + + /* + rak_mspace_realloc behaves as realloc, but operates within + the given space. + + If compiled with FOOTERS==1, rak_mspace_realloc is not actually + needed. realloc may be called instead of rak_mspace_realloc because + realloced chunks from any space are handled by their originating + spaces. + */ + void* rak_mspace_realloc(mspace msp, void* mem, size_t newsize); + + /* + rak_mspace_calloc behaves as calloc, but operates within + the given space. + */ + void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + + /* + rak_mspace_memalign behaves as memalign, but operates within + the given space. + */ + void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes); + + /* + rak_mspace_independent_calloc behaves as independent_calloc, but + operates within the given space. + */ + void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); + + /* + rak_mspace_independent_comalloc behaves as independent_comalloc, but + operates within the given space. + */ + void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); + + /* + rak_mspace_footprint() returns the number of bytes obtained from the + system for this space. + */ + size_t rak_mspace_footprint(mspace msp); + + +#if !NO_MALLINFO + /* + rak_mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. + */ + struct mallinfo rak_mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + + /* + malloc_usable_size(void* p) behaves the same as malloc_usable_size; + */ + size_t rak_mspace_usable_size(void* mem); + + /* + rak_mspace_malloc_stats behaves as malloc_stats, but reports + properties of the given space. + */ + void rak_mspace_malloc_stats(mspace msp); + + /* + rak_mspace_trim behaves as malloc_trim, but + operates within the given space. + */ + int rak_mspace_trim(mspace msp, size_t pad); + + /* + An alias for mallopt. + */ + int rak_mspace_mallopt(int, int); + +#endif /* MSPACES */ + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +/* +This is a version (aka rdlmalloc) of malloc/free/realloc written by +Doug Lea and released to the public domain, as explained at +http://creativecommons.org/licenses/publicdomain. Send questions, +comments, complaints, performance data, etc to dl@cs.oswego.edu + +* Version 2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) + +Note: There may be an updated version of this malloc obtainable at +ftp://gee.cs.oswego.edu/pub/misc/malloc.c +Check before installing! + +* Quickstart + +This library is all in one file to simplify the most common usage: +ftp it, compile it (-O3), and link it into another program. All of +the compile-time options default to reasonable values for use on +most platforms. You might later want to step through various +compile-time and dynamic tuning options. + +For convenience, an include file for code using this malloc is at: +ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.4.h +You don't really need this .h file unless you call functions not +defined in your system include files. The .h file contains only the +excerpts from this file needed for using this malloc on ANSI C/C++ +systems, so long as you haven't changed compile-time options about +naming and tuning parameters. If you do, then you can create your +own malloc.h that does include all settings by cutting at the point +indicated below. Note that you may already by default be using a C +library containing a malloc that is based on some version of this +malloc (for example in linux). You might still want to use the one +in this file to customize settings or to avoid overheads associated +with library versions. + +* Vital statistics: + +Supported pointer/size_t representation: 4 or 8 bytes +size_t MUST be an unsigned type of the same width as +pointers. (If you are using an ancient system that declares +size_t as a signed type, or need it to be a different width +than pointers, you can use a previous release of this malloc +(e.g. 2.7.2) supporting these.) + +Alignment: 8 bytes (default) +This suffices for nearly all current machines and C compilers. +However, you can define MALLOC_ALIGNMENT to be wider than this +if necessary (up to 128bytes), at the expense of using more space. + +Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) +8 or 16 bytes (if 8byte sizes) +Each malloced chunk has a hidden word of overhead holding size +and status information, and additional cross-check word +if FOOTERS is defined. + +Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) +8-byte ptrs: 32 bytes (including overhead) + +Even a request for zero bytes (i.e., malloc(0)) returns a +pointer to something of the minimum allocatable size. +The maximum overhead wastage (i.e., number of extra bytes +allocated than were requested in malloc) is less than or equal +to the minimum size, except for requests >= mmap_threshold that +are serviced via mmap(), where the worst case wastage is about +32 bytes plus the remainder from a system page (the minimal +mmap unit); typically 4096 or 8192 bytes. + +Security: static-safe; optionally more or less +The "security" of malloc refers to the ability of malicious +code to accentuate the effects of errors (for example, freeing +space that is not currently malloc'ed or overwriting past the +ends of chunks) in code that calls malloc. This malloc +guarantees not to modify any memory locations below the base of +heap, i.e., static variables, even in the presence of usage +errors. The routines additionally detect most improper frees +and reallocs. All this holds as long as the static bookkeeping +for malloc itself is not corrupted by some other means. This +is only one aspect of security -- these checks do not, and +cannot, detect all possible programming errors. + +If FOOTERS is defined nonzero, then each allocated chunk +carries an additional check word to verify that it was malloced +from its space. These check words are the same within each +execution of a program using malloc, but differ across +executions, so externally crafted fake chunks cannot be +freed. This improves security by rejecting frees/reallocs that +could corrupt heap memory, in addition to the checks preventing +writes to statics that are always on. This may further improve +security at the expense of time and space overhead. (Note that +FOOTERS may also be worth using with MSPACES.) + +By default detected errors cause the program to abort (calling +"abort()"). You can override this to instead proceed past +errors by defining PROCEED_ON_ERROR. In this case, a bad free +has no effect, and a malloc that encounters a bad address +caused by user overwrites will ignore the bad address by +dropping pointers and indices to all known memory. This may +be appropriate for programs that should continue if at all +possible in the face of programming errors, although they may +run out of memory because dropped memory is never reclaimed. + +If you don't like either of these options, you can define +CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything +else. And if if you are sure that your program using malloc has +no errors or vulnerabilities, you can define INSECURE to 1, +which might (or might not) provide a small performance improvement. + +Thread-safety: NOT thread-safe unless USE_LOCKS defined +When USE_LOCKS is defined, each public call to malloc, free, +etc is surrounded with either a pthread mutex or a win32 +spinlock (depending on DL_PLATFORM_WIN32). This is not especially fast, and +can be a major bottleneck. It is designed only to provide +minimal protection in concurrent environments, and to provide a +basis for extensions. If you are using malloc in a concurrent +program, consider instead using nedmalloc +(http://www.nedprod.com/programs/portable/nedmalloc/) or +ptmalloc (See http://www.malloc.de), which are derived +from versions of this malloc. + +System requirements: Any combination of MORECORE and/or MMAP/MUNMAP +This malloc can use unix sbrk or any emulation (invoked using +the CALL_MORECORE macro) and/or mmap/munmap or any emulation +(invoked using CALL_MMAP/CALL_MUNMAP) to get and release system +memory. On most unix systems, it tends to work best if both +MORECORE and MMAP are enabled. On Win32, it uses emulations +based on VirtualAlloc. It also uses common C library functions +like memset. + +Compliance: I believe it is compliant with the Single Unix Specification +(See http://www.unix.org). Also SVID/XPG, ANSI C, and probably +others as well. + +* Overview of algorithms + +This is not the fastest, most space-conserving, most portable, or +most tunable malloc ever written. However it is among the fastest +while also being among the most space-conserving, portable and +tunable. Consistent balance across these factors results in a good +general-purpose allocator for malloc-intensive programs. + +In most ways, this malloc is a best-fit allocator. Generally, it +chooses the best-fitting existing chunk for a request, with ties +broken in approximately least-recently-used order. (This strategy +normally maintains low fragmentation.) However, for requests less +than 256bytes, it deviates from best-fit when there is not an +exactly fitting available chunk by preferring to use space adjacent +to that used for the previous small request, as well as by breaking +ties in approximately most-recently-used order. (These enhance +locality of series of small allocations.) And for very large requests +(>= 256Kb by default), it relies on system memory mapping +facilities, if supported. (This helps avoid carrying around and +possibly fragmenting memory used only for large chunks.) + +All operations (except malloc_stats and mallinfo) have execution +times that are bounded by a constant factor of the number of bits in +a size_t, not counting any clearing in calloc or copying in realloc, +or actions surrounding MORECORE and MMAP that have times +proportional to the number of non-contiguous regions returned by +system allocation routines, which is often just 1. In real-time +applications, you can optionally suppress segment traversals using +NO_SEGMENT_TRAVERSAL, which assures bounded execution even when +system allocators return non-contiguous spaces, at the typical +expense of carrying around more memory and increased fragmentation. + +The implementation is not very modular and seriously overuses +macros. Perhaps someday all C compilers will do as good a job +inlining modular code as can now be done by brute-force expansion, +but now, enough of them seem not to. + +Some compilers issue a lot of warnings about code that is +dead/unreachable only on some platforms, and also about intentional +uses of negation on unsigned types. All known cases of each can be +ignored. + +For a longer but out of date high-level description, see +http://gee.cs.oswego.edu/dl/html/malloc.html + +* MSPACES +If MSPACES is defined, then in addition to malloc, free, etc., +this file also defines rak_mspace_malloc, rak_mspace_free, etc. These +are versions of malloc routines that take an "mspace" argument +obtained using rak_create_mspace, to control all internal bookkeeping. +If ONLY_MSPACES is defined, only these versions are compiled. +So if you would like to use this allocator for only some allocations, +and your system malloc for others, you can compile with +ONLY_MSPACES and then do something like... +static mspace mymspace = rak_create_mspace(0,0); // for example +#define mymalloc(bytes) rak_mspace_malloc(mymspace, bytes) + +(Note: If you only need one instance of an mspace, you can instead +use "USE_DL_PREFIX" to relabel the global malloc.) + +You can similarly create thread-local allocators by storing +mspaces as thread-locals. For example: +static __thread mspace tlms = 0; +void* tlmalloc(size_t bytes) { +if (tlms == 0) tlms = rak_create_mspace(0, 0); +return rak_mspace_malloc(tlms, bytes); +} +void tlfree(void* mem) { rak_mspace_free(tlms, mem); } + +Unless FOOTERS is defined, each mspace is completely independent. +You cannot allocate from one and free to another (although +conformance is only weakly checked, so usage errors are not always +caught). If FOOTERS is defined, then each chunk carries around a tag +indicating its originating mspace, and frees are directed to their +originating spaces. + +------------------------- Compile-time options --------------------------- + +Be careful in setting #define values for numerical constants of type +size_t. On some systems, literal values are not automatically extended +to size_t precision unless they are explicitly casted. You can also +use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. + +DL_PLATFORM_WIN32 default: defined if _WIN32 defined +Defining DL_PLATFORM_WIN32 sets up defaults for MS environment and compilers. +Otherwise defaults are for unix. Beware that there seem to be some +cases where this malloc might not be a pure drop-in replacement for +Win32 malloc: Random-looking failures from Win32 GDI API's (eg; +SetDIBits()) may be due to bugs in some video driver implementations +when pixel buffers are malloc()ed, and the region spans more than +one VirtualAlloc()ed region. Because rdlmalloc uses a small (64Kb) +default granularity, pixel buffers may straddle virtual allocation +regions more often than when using the Microsoft allocator. You can +avoid this by using VirtualAlloc() and VirtualFree() for all pixel +buffers rather than using malloc(). If this is not possible, +recompile this malloc with a larger DEFAULT_GRANULARITY. + +MALLOC_ALIGNMENT default: (size_t)8 +Controls the minimum alignment for malloc'ed chunks. It must be a +power of two and at least 8, even on machines for which smaller +alignments would suffice. It may be defined as larger than this +though. Note however that code and data structures are optimized for +the case of 8-byte alignment. + +MSPACES default: 0 (false) +If true, compile in support for independent allocation spaces. +This is only supported if HAVE_MMAP is true. + +ONLY_MSPACES default: 0 (false) +If true, only compile in mspace versions, not regular versions. + +USE_LOCKS default: 0 (false) +Causes each call to each public routine to be surrounded with +pthread or DL_PLATFORM_WIN32 mutex lock/unlock. (If set true, this can be +overridden on a per-mspace basis for mspace versions.) If set to a +non-zero value other than 1, locks are used, but their +implementation is left out, so lock functions must be supplied manually, +as described below. + +USE_SPIN_LOCKS default: 1 iff USE_LOCKS and on x86 using gcc or MSC +If true, uses custom spin locks for locking. This is currently +supported only for x86 platforms using gcc or recent MS compilers. +Otherwise, posix locks or win32 critical sections are used. + +FOOTERS default: 0 +If true, provide extra checking and dispatching by placing +information in the footers of allocated chunks. This adds +space and time overhead. + +INSECURE default: 0 +If true, omit checks for usage errors and heap space overwrites. + +USE_DL_PREFIX default: NOT defined +Causes compiler to prefix all public routines with the string 'dl'. +This can be useful when you only want to use this malloc in one part +of a program, using your regular system malloc elsewhere. + +ABORT default: defined as abort() +Defines how to abort on failed checks. On most systems, a failed +check cannot die with an "assert" or even print an informative +message, because the underlying print routines in turn call malloc, +which will fail again. Generally, the best policy is to simply call +abort(). It's not very useful to do more than this because many +errors due to overwriting will show up as address faults (null, odd +addresses etc) rather than malloc-triggered checks, so will also +abort. Also, most compilers know that abort() does not return, so +can better optimize code conditionally calling it. + +PROCEED_ON_ERROR default: defined as 0 (false) +Controls whether detected bad addresses cause them to bypassed +rather than aborting. If set, detected bad arguments to free and +realloc are ignored. And all bookkeeping information is zeroed out +upon a detected overwrite of freed heap space, thus losing the +ability to ever return it from malloc again, but enabling the +application to proceed. If PROCEED_ON_ERROR is defined, the +static variable malloc_corruption_error_count is compiled in +and can be examined to see if errors have occurred. This option +generates slower code than the default abort policy. + +DEBUG default: NOT defined +The DEBUG setting is mainly intended for people trying to modify +this code or diagnose problems when porting to new platforms. +However, it may also be able to better isolate user errors than just +using runtime checks. The assertions in the check routines spell +out in more detail the assumptions and invariants underlying the +algorithms. The checking is fairly extensive, and will slow down +execution noticeably. Calling malloc_stats or mallinfo with DEBUG +set will attempt to check every non-mmapped allocated and free chunk +in the course of computing the summaries. + +ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) +Debugging assertion failures can be nearly impossible if your +version of the assert macro causes malloc to be called, which will +lead to a cascade of further failures, blowing the runtime stack. +ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), +which will usually make debugging easier. + +MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 +The action to take before "return 0" when malloc fails to be able to +return memory because there is none available. + +HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES +True if this system supports sbrk or an emulation of it. + +MORECORE default: sbrk +The name of the sbrk-style system routine to call to obtain more +memory. See below for guidance on writing custom MORECORE +functions. The type of the argument to sbrk/MORECORE varies across +systems. It cannot be size_t, because it supports negative +arguments, so it is normally the signed type of the same width as +size_t (sometimes declared as "intptr_t"). It doesn't much matter +though. Internally, we only call it with arguments less than half +the max value of a size_t, which should work across all reasonable +possibilities, although sometimes generating compiler warnings. + +MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE +If true, take advantage of fact that consecutive calls to MORECORE +with positive arguments always return contiguous increasing +addresses. This is true of unix sbrk. It does not hurt too much to +set it true anyway, since malloc copes with non-contiguities. +Setting it false when definitely non-contiguous saves time +and possibly wasted space it would take to discover this though. + +MORECORE_CANNOT_TRIM default: NOT defined +True if MORECORE cannot release space back to the system when given +negative arguments. This is generally necessary only if you are +using a hand-crafted MORECORE function that cannot handle negative +arguments. + +NO_SEGMENT_TRAVERSAL default: 0 +If non-zero, suppresses traversals of memory segments +returned by either MORECORE or CALL_MMAP. This disables +merging of segments that are contiguous, and selectively +releasing them to the OS if unused, but bounds execution times. + +HAVE_MMAP default: 1 (true) +True if this system supports mmap or an emulation of it. If so, and +HAVE_MORECORE is not true, MMAP is used for all system +allocation. If set and HAVE_MORECORE is true as well, MMAP is +primarily used to directly allocate very large blocks. It is also +used as a backup strategy in cases where MORECORE fails to provide +space from system. Note: A single call to MUNMAP is assumed to be +able to unmap memory that may have be allocated using multiple calls +to MMAP, so long as they are adjacent. + +HAVE_MREMAP default: 1 on linux, else 0 +If true realloc() uses mremap() to re-allocate large blocks and +extend or shrink allocation spaces. + +MMAP_CLEARS default: 1 except on WINCE. +True if mmap clears memory so calloc doesn't need to. This is true +for standard unix mmap using /dev/zero and on DL_PLATFORM_WIN32 except for WINCE. + +USE_BUILTIN_FFS default: 0 (i.e., not used) +Causes malloc to use the builtin ffs() function to compute indices. +Some compilers may recognize and intrinsify ffs to be faster than the +supplied C version. Also, the case of x86 using gcc is special-cased +to an asm instruction, so is already as fast as it can be, and so +this setting has no effect. Similarly for Win32 under recent MS compilers. +(On most x86s, the asm version is only slightly faster than the C version.) + +malloc_getpagesize default: derive from system includes, or 4096. +The system page size. To the extent possible, this malloc manages +memory from the system in page-size units. This may be (and +usually is) a function rather than a constant. This is ignored +if DL_PLATFORM_WIN32, where page size is determined using getSystemInfo during +initialization. + +USE_DEV_RANDOM default: 0 (i.e., not used) +Causes malloc to use /dev/random to initialize secure magic seed for +stamping footers. Otherwise, the current time is used. + +NO_MALLINFO default: 0 +If defined, don't compile "mallinfo". This can be a simple way +of dealing with mismatches between system declarations and +those in this file. + +MALLINFO_FIELD_TYPE default: size_t +The type of the fields in the mallinfo struct. This was originally +defined as "int" in SVID etc, but is more usefully defined as +size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set + +REALLOC_ZERO_BYTES_FREES default: not defined +This should be set if a call to realloc with zero bytes should +be the same as a call to free. Some people think it should. Otherwise, +since this malloc returns a unique pointer for malloc(0), so does +realloc(p, 0). + +LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H +LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H +LACKS_STDLIB_H default: NOT defined unless on DL_PLATFORM_WIN32 +Define these if your system does not have these header files. +You might need to manually insert some of the declarations they provide. + +DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, +system_info.dwAllocationGranularity in DL_PLATFORM_WIN32, +otherwise 64K. +Also settable using mallopt(M_GRANULARITY, x) +The unit for allocating and deallocating memory from the system. On +most systems with contiguous MORECORE, there is no reason to +make this more than a page. However, systems with MMAP tend to +either require or encourage larger granularities. You can increase +this value to prevent system allocation functions to be called so +often, especially if they are slow. The value must be at least one +page and must be a power of two. Setting to 0 causes initialization +to either page size or win32 region size. (Note: In previous +versions of malloc, the equivalent of this option was called +"TOP_PAD") + +DEFAULT_TRIM_THRESHOLD default: 2MB +Also settable using mallopt(M_TRIM_THRESHOLD, x) +The maximum amount of unused top-most memory to keep before +releasing via malloc_trim in free(). Automatic trimming is mainly +useful in long-lived programs using contiguous MORECORE. Because +trimming via sbrk can be slow on some systems, and can sometimes be +wasteful (in cases where programs immediately afterward allocate +more large chunks) the value should be high enough so that your +overall system performance would improve by releasing this much +memory. As a rough guide, you might set to a value close to the +average size of a process (program) running on your system. +Releasing this much memory would allow such a process to run in +memory. Generally, it is worth tuning trim thresholds when a +program undergoes phases where several large chunks are allocated +and released in ways that can reuse each other's storage, perhaps +mixed with phases where there are no such chunks at all. The trim +value must be greater than page size to have any useful effect. To +disable trimming completely, you can set to MAX_SIZE_T. Note that the trick +some people use of mallocing a huge space and then freeing it at +program startup, in an attempt to reserve system memory, doesn't +have the intended effect under automatic trimming, since that memory +will immediately be returned to the system. + +DEFAULT_MMAP_THRESHOLD default: 256K +Also settable using mallopt(M_MMAP_THRESHOLD, x) +The request size threshold for using MMAP to directly service a +request. Requests of at least this size that cannot be allocated +using already-existing space will be serviced via mmap. (If enough +normal freed space already exists it is used instead.) Using mmap +segregates relatively large chunks of memory so that they can be +individually obtained and released from the host system. A request +serviced through mmap is never reused by any other request (at least +not directly; the system may just so happen to remap successive +requests to the same locations). Segregating space in this way has +the benefits that: Mmapped space can always be individually released +back to the system, which helps keep the system level memory demands +of a long-lived program low. Also, mapped memory doesn't become +`locked' between other chunks, as can happen with normally allocated +chunks, which means that even trimming via malloc_trim would not +release them. However, it has the disadvantage that the space +cannot be reclaimed, consolidated, and then used to service later +requests, as happens with normal chunks. The advantages of mmap +nearly always outweigh disadvantages for "large" chunks, but the +value of "large" may vary across systems. The default is an +empirically derived value that works well in most systems. You can +disable mmap by setting to MAX_SIZE_T. + +MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP +The number of consolidated frees between checks to release +unused segments when freeing. When using non-contiguous segments, +especially with multiple mspaces, checking only for topmost space +doesn't always suffice to trigger trimming. To compensate for this, +free() will, with a period of MAX_RELEASE_CHECK_RATE (or the +current number of segments, if greater) try to release unused +segments to the OS when freeing chunks that result in +consolidation. The best value for this parameter is a compromise +between slowing down frees with relatively costly checks that +rarely trigger versus holding on to unused memory. To effectively +disable, set to MAX_SIZE_T. This may lead to a very slight speed +improvement at the expense of carrying around more memory. +*/ + +/* Version identifier to allow people to support multiple versions */ +#ifndef DLMALLOC_VERSION +#define DLMALLOC_VERSION 20804 +#endif /* DLMALLOC_VERSION */ + +#include "rdlmalloc-options.h" + +#ifndef WIN32 +#if defined(_XBOX) || defined(X360) +#else +#if defined(_WIN32) +#define DL_PLATFORM_WIN32 1 +#endif /* _WIN32 */ +#ifdef _WIN32_WCE +#define LACKS_FCNTL_H +#define DL_PLATFORM_WIN32 1 +#endif /* _WIN32_WCE */ +#endif +#else +#define DL_PLATFORM_WIN32 1 +#endif /* DL_PLATFORM_WIN32 */ + +#if defined(_XBOX) || defined(X360) +#define HAVE_MMAP 1 +#define HAVE_MORECORE 0 +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H +#define LACKS_SYS_MMAN_H +#define LACKS_STRING_H +#define LACKS_STRINGS_H +#define LACKS_SYS_TYPES_H +#define LACKS_ERRNO_H +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION +#endif +#define MMAP_CLEARS 1 +#endif + +#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) +#define LACKS_SYS_PARAM_H +#include "sysutil\sysutil_sysparam.h" +#define LACKS_SYS_MMAN_H +#endif + + +#ifdef DL_PLATFORM_WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define HAVE_MMAP 1 +#define HAVE_MORECORE 0 +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H +#define LACKS_SYS_MMAN_H +#define LACKS_STRING_H +#define LACKS_STRINGS_H +#define LACKS_SYS_TYPES_H +#define LACKS_ERRNO_H +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION +#endif /* MALLOC_FAILURE_ACTION */ +#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ +#define MMAP_CLEARS 0 +#else +#define MMAP_CLEARS 1 +#endif /* _WIN32_WCE */ +#endif /* DL_PLATFORM_WIN32 */ + +#if defined(DARWIN) || defined(_DARWIN) +/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ +#ifndef HAVE_MORECORE +#define HAVE_MORECORE 0 +#define HAVE_MMAP 1 +/* OSX allocators provide 16 byte alignment */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)16U) +#endif +#endif /* HAVE_MORECORE */ +#endif /* DARWIN */ + +#ifndef LACKS_SYS_TYPES_H +#include /* For size_t */ +#endif /* LACKS_SYS_TYPES_H */ + +#if (defined(__GNUC__) && ((defined(__i386__) || defined(__x86_64__)))) || (defined(_MSC_VER) && _MSC_VER>=1310) +#define SPIN_LOCKS_AVAILABLE 1 +#else +#define SPIN_LOCKS_AVAILABLE 0 +#endif + +/* The maximum possible size_t value has all bits set */ +#define MAX_SIZE_T (~(size_t)0) + +#ifndef ONLY_MSPACES +#define ONLY_MSPACES 0 /* define to a value */ +#else +#define ONLY_MSPACES 1 +#endif /* ONLY_MSPACES */ +#ifndef MSPACES +#if ONLY_MSPACES +#define MSPACES 1 +#else /* ONLY_MSPACES */ +#define MSPACES 0 +#endif /* ONLY_MSPACES */ +#endif /* MSPACES */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)8U) +#endif /* MALLOC_ALIGNMENT */ +#ifndef FOOTERS +#define FOOTERS 0 +#endif /* FOOTERS */ +#ifndef ABORT +#define ABORT abort() +#endif /* ABORT */ +#ifndef ABORT_ON_ASSERT_FAILURE +#define ABORT_ON_ASSERT_FAILURE 1 +#endif /* ABORT_ON_ASSERT_FAILURE */ +#ifndef PROCEED_ON_ERROR +#define PROCEED_ON_ERROR 0 +#endif /* PROCEED_ON_ERROR */ +#ifndef USE_LOCKS +#define USE_LOCKS 0 +#endif /* USE_LOCKS */ +#ifndef USE_SPIN_LOCKS +#if USE_LOCKS && SPIN_LOCKS_AVAILABLE +#define USE_SPIN_LOCKS 1 +#else +#define USE_SPIN_LOCKS 0 +#endif /* USE_LOCKS && SPIN_LOCKS_AVAILABLE. */ +#endif /* USE_SPIN_LOCKS */ +#ifndef INSECURE +#define INSECURE 0 +#endif /* INSECURE */ +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif /* HAVE_MMAP */ +#ifndef MMAP_CLEARS +#define MMAP_CLEARS 1 +#endif /* MMAP_CLEARS */ +#ifndef HAVE_MREMAP +#ifdef linux +#define HAVE_MREMAP 1 +#else /* linux */ +#define HAVE_MREMAP 0 +#endif /* linux */ +#endif /* HAVE_MREMAP */ +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION errno = ENOMEM; +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef HAVE_MORECORE +#if ONLY_MSPACES +#define HAVE_MORECORE 0 +#else /* ONLY_MSPACES */ +#define HAVE_MORECORE 1 +#endif /* ONLY_MSPACES */ +#endif /* HAVE_MORECORE */ +#if !HAVE_MORECORE +#define MORECORE_CONTIGUOUS 0 +#else /* !HAVE_MORECORE */ +#define MORECORE_DEFAULT sbrk +#ifndef MORECORE_CONTIGUOUS +#define MORECORE_CONTIGUOUS 1 +#endif /* MORECORE_CONTIGUOUS */ +#endif /* HAVE_MORECORE */ +#ifndef DEFAULT_GRANULARITY +#if (MORECORE_CONTIGUOUS || defined(DL_PLATFORM_WIN32)) +#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ +#else /* MORECORE_CONTIGUOUS */ +#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) +#endif /* MORECORE_CONTIGUOUS */ +#endif /* DEFAULT_GRANULARITY */ +#ifndef DEFAULT_TRIM_THRESHOLD +#ifndef MORECORE_CANNOT_TRIM +#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#else /* MORECORE_CANNOT_TRIM */ +#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T +#endif /* MORECORE_CANNOT_TRIM */ +#endif /* DEFAULT_TRIM_THRESHOLD */ +#ifndef DEFAULT_MMAP_THRESHOLD +#if HAVE_MMAP +#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) +#else /* HAVE_MMAP */ +#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* DEFAULT_MMAP_THRESHOLD */ +#ifndef MAX_RELEASE_CHECK_RATE +#if HAVE_MMAP +#define MAX_RELEASE_CHECK_RATE 4095 +#else +#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* MAX_RELEASE_CHECK_RATE */ +#ifndef USE_BUILTIN_FFS +#define USE_BUILTIN_FFS 0 +#endif /* USE_BUILTIN_FFS */ +#ifndef USE_DEV_RANDOM +#define USE_DEV_RANDOM 0 +#endif /* USE_DEV_RANDOM */ +#ifndef NO_MALLINFO +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ +#ifndef MALLINFO_FIELD_TYPE +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ +#ifndef NO_SEGMENT_TRAVERSAL +#define NO_SEGMENT_TRAVERSAL 0 +#endif /* NO_SEGMENT_TRAVERSAL */ + +/* +mallopt tuning options. SVID/XPG defines four standard parameter +numbers for mallopt, normally defined in malloc.h. None of these +are used in this malloc, so setting them has no effect. But this +malloc does support the following options. +*/ + +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) + +/* ------------------------ Mallinfo declarations ------------------------ */ + +#if !NO_MALLINFO +/* +This version of malloc supports the standard SVID/XPG mallinfo +routine that returns a struct containing usage properties and +statistics. It should work on any system that has a +/usr/include/malloc.h defining struct mallinfo. The main +declaration needed is the mallinfo struct that is returned (by-copy) +by mallinfo(). The malloinfo struct contains a bunch of fields that +are not even meaningful in this version of malloc. These fields are +are instead filled by mallinfo() with other numbers that might be of +interest. + +HAVE_USR_INCLUDE_MALLOC_H should be set if you have a +/usr/include/malloc.h file that includes a declaration of struct +mallinfo. If so, it is included; else a compliant version is +declared below. These must be precisely the same for mallinfo() to +work. The original SVID version of this struct, defined on most +systems with mallinfo, declares all fields as ints. But some others +define as unsigned long. If your system defines the fields using a +type of different width than listed here, you MUST #include your +system version and #define HAVE_USR_INCLUDE_MALLOC_H. +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#ifdef HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else /* HAVE_USR_INCLUDE_MALLOC_H */ +#ifndef STRUCT_MALLINFO_DECLARED +#define STRUCT_MALLINFO_DECLARED 1 +struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ +}; +#endif /* STRUCT_MALLINFO_DECLARED */ +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* NO_MALLINFO */ + +/* +Try to persuade compilers to inline. The most critical functions for +inlining are defined as macros, so these aren't used for them. +*/ + +#ifndef FORCEINLINE +#if defined(__GNUC__) +#define FORCEINLINE __inline __attribute__ ((always_inline)) +#elif defined(_MSC_VER) +#define FORCEINLINE __forceinline +#endif +#endif +#ifndef NOINLINE +#if defined(__GNUC__) +#define NOINLINE __attribute__ ((noinline)) +#elif defined(_MSC_VER) +#define NOINLINE __declspec(noinline) +#else +#define NOINLINE +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#ifndef FORCEINLINE +#define FORCEINLINE inline +#endif +#endif /* __cplusplus */ +#ifndef FORCEINLINE +#define FORCEINLINE +#endif + +#if !ONLY_MSPACES + + /* ------------------- Declarations of public routines ------------------- */ + +#ifndef USE_DL_PREFIX +#define rdlcalloc calloc +#define rdlfree free +#define rdlmalloc malloc +#define rdlmemalign memalign +#define rdlrealloc realloc +#define rdlvalloc valloc +#define rdlpvalloc pvalloc +#define rdlmallinfo mallinfo +#define rdlmallopt mallopt +#define rdlmalloc_trim malloc_trim +#define rdlmalloc_stats malloc_stats +#define rdlmalloc_usable_size malloc_usable_size +#define rdlmalloc_footprint malloc_footprint +#define dlmalloc_max_footprint malloc_max_footprint +#define rdlindependent_calloc independent_calloc +#define rdlindependent_comalloc independent_comalloc +#endif /* USE_DL_PREFIX */ + + + /* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or + null if no space is available, in which case errno is set to ENOMEM + on ANSI C systems. + + If n is zero, malloc returns a minimum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 32 bytes on 64bit + systems.) Note that size_t is an unsigned type, so calls with + arguments that would be negative if signed are interpreted as + requests for huge amounts of space, which will often fail. The + maximum supported value of n differs across systems, but is in all + cases less than the maximum representable value of a size_t. + */ + void* rdlmalloc(size_t); + + /* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. If p was not malloced or already + freed, free(p) will by default cause the current program to abort. + */ + void rdlfree(void*); + + /* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. + */ + void* rdlcalloc(size_t, size_t); + + /* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p in most cases when possible, otherwise it + employs the equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. realloc with a size + argument of zero (re)allocates a minimum-sized chunk. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. + */ + + void* rdlrealloc(void*, size_t); + + /* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + */ + void* rdlmemalign(size_t, size_t); + + /* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. + */ + void* rdlvalloc(size_t); + + /* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. To workaround the fact that mallopt is specified to use int, + not size_t parameters, the value -1 is specially treated as the + maximum unsigned size_t value. + + SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. None of these are use in this malloc, + so setting them has no effect. But this malloc also supports other + options in mallopt. See below for details. Briefly, supported + parameters are as follows (listed defaults are for "typical" + configurations). + + Symbol param # default allowed param values + M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) + M_GRANULARITY -2 page size any power of 2 >= page size + M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) + */ + int rdlmallopt(int, int); + + /* + malloc_footprint(); + Returns the number of bytes obtained from the system. The total + number of bytes allocated by malloc, realloc etc., is less than this + value. Unlike mallinfo, this function returns only a precomputed + result, so can be called frequently to monitor memory consumption. + Even if locks are otherwise defined, this function does not use them, + so results might not be up to date. + */ + size_t rdlmalloc_footprint(void); + + /* + malloc_max_footprint(); + Returns the maximum number of bytes obtained from the system. This + value will be greater than current footprint if deallocated space + has been reclaimed by the system. The peak number of bytes allocated + by malloc, realloc etc., is less than this value. Unlike mallinfo, + this function returns only a precomputed result, so can be called + frequently to monitor memory consumption. Even if locks are + otherwise defined, this function does not use them, so results might + not be up to date. + */ + size_t dlmalloc_max_footprint(void); + +#if !NO_MALLINFO + /* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: always zero. + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: the maximum total allocated space. This will be greater + than current total if trimming has occurred. + fsmblks: always zero + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. + */ + struct mallinfo rdlmallinfo(void); +#endif /* NO_MALLINFO */ + + /* + independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); + + independent_calloc is similar to calloc, but instead of returning a + single cleared space, it returns an array of pointers to n_elements + independent elements that can hold contents of size elem_size, each + of which starts out cleared, and can be independently freed, + realloc'ed etc. The elements are guaranteed to be adjacently + allocated (this is not guaranteed to occur with multiple callocs or + mallocs), which may also improve cache locality in some + applications. + + The "chunks" argument is optional (i.e., may be null, which is + probably the most typical usage). If it is null, the returned array + is itself dynamically allocated and should also be freed when it is + no longer needed. Otherwise, the chunks array must be of at least + n_elements in length. It is filled in with the pointers to the + chunks. + + In either case, independent_calloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and "chunks" + is null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use regular calloc and assign pointers into this + space to represent elements. (In this case though, you cannot + independently free elements.) + + independent_calloc simplifies and speeds up implementations of many + kinds of pools. It may also be useful when constructing large data + structures that initially have a fixed number of fixed-sized nodes, + but the number is not known at compile time, and some of the nodes + may later need to be freed. For example: + + struct Node { int item; struct Node* next; }; + + struct Node* build_list() { + struct Node** pool; + int n = read_number_of_nodes_needed(); + if (n <= 0) return 0; + pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); + if (pool == 0) die(); + // organize into a linked list... + struct Node* first = pool[0]; + for (i = 0; i < n-1; ++i) + pool[i]->next = pool[i+1]; + free(pool); // Can now free the array (or not, if it is needed later) + return first; + } + */ + void** rdlindependent_calloc(size_t, size_t, void**); + + /* + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + + independent_comalloc allocates, all at once, a set of n_elements + chunks with sizes indicated in the "sizes" array. It returns + an array of pointers to these elements, each of which can be + independently freed, realloc'ed etc. The elements are guaranteed to + be adjacently allocated (this is not guaranteed to occur with + multiple callocs or mallocs), which may also improve cache locality + in some applications. + + The "chunks" argument is optional (i.e., may be null). If it is null + the returned array is itself dynamically allocated and should also + be freed when it is no longer needed. Otherwise, the chunks array + must be of at least n_elements in length. It is filled in with the + pointers to the chunks. + + In either case, independent_comalloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and chunks is + null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use a single regular malloc, and assign pointers at + particular offsets in the aggregate space. (In this case though, you + cannot independently free elements.) + + independent_comallac differs from independent_calloc in that each + element may have a different size, and also that it does not + automatically clear elements. + + independent_comalloc can be used to speed up allocation in cases + where several structs or objects must always be allocated at the + same time. For example: + + struct Head { ... } + struct Foot { ... } + + void send_message(char* msg) { + int msglen = strlen(msg); + size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; + void* chunks[3]; + if (independent_comalloc(3, sizes, chunks) == 0) + die(); + struct Head* head = (struct Head*)(chunks[0]); + char* body = (char*)(chunks[1]); + struct Foot* foot = (struct Foot*)(chunks[2]); + // ... + } + + In general though, independent_comalloc is worth using only for + larger values of n_elements. For small values, you probably won't + detect enough difference from series of malloc calls to bother. + + Overuse of independent_comalloc can increase overall memory usage, + since it cannot reuse existing noncontiguous small chunks that + might be available for some of the elements. + */ + void** rdlindependent_comalloc(size_t, size_t*, void**); + + + /* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ + void* rdlpvalloc(size_t); + + /* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative arguments + to sbrk) if there is unused memory at the `high' end of the malloc + pool or in unused MMAP segments. You can call this after freeing + large blocks of memory to potentially reduce the system-level memory + requirements of a program. However, it cannot guarantee to reduce + memory. Under some allocation patterns, some large free blocks of + memory will be locked between two used chunks, so they cannot be + given back to the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, only + the minimum amount of memory to maintain internal data structures + will be left. Non-zero arguments can be supplied to maintain enough + trailing space to service future expected allocations without having + to re-obtain memory from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + */ + int rdlmalloc_trim(size_t); + + /* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. + */ + void rdlmalloc_stats(void); + +#endif /* ONLY_MSPACES */ + + /* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); + */ + size_t rdlmalloc_usable_size(void*); + + +#if MSPACES + + /* + mspace is an opaque type representing an independent + region of space that supports rak_mspace_malloc, etc. + */ + typedef void* mspace; + + /* + rak_create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service rak_mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). + */ + mspace rak_create_mspace(size_t capacity, int locked); + + /* + rak_destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. + */ + size_t rak_destroy_mspace(mspace msp); + + /* + rak_create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. + */ + mspace rak_create_mspace_with_base(void* base, size_t capacity, int locked); + + /* + rak_mspace_track_large_chunks controls whether requests for large chunks + are allocated in their own untracked mmapped regions, separate from + others in this mspace. By default large chunks are not tracked, + which reduces fragmentation. However, such chunks are not + necessarily released to the system upon rak_destroy_mspace. Enabling + tracking by setting to true may increase fragmentation, but avoids + leakage when relying on rak_destroy_mspace to release all memory + allocated using this space. The function returns the previous + setting. + */ + int rak_mspace_track_large_chunks(mspace msp, int enable); + + + /* + rak_mspace_malloc behaves as malloc, but operates within + the given space. + */ + void* rak_mspace_malloc(mspace msp, size_t bytes); + + /* + rak_mspace_free behaves as free, but operates within + the given space. + + If compiled with FOOTERS==1, rak_mspace_free is not actually needed. + free may be called instead of rak_mspace_free because freed chunks from + any space are handled by their originating spaces. + */ + void rak_mspace_free(mspace msp, void* mem); + + /* + rak_mspace_realloc behaves as realloc, but operates within + the given space. + + If compiled with FOOTERS==1, rak_mspace_realloc is not actually + needed. realloc may be called instead of rak_mspace_realloc because + realloced chunks from any space are handled by their originating + spaces. + */ + void* rak_mspace_realloc(mspace msp, void* mem, size_t newsize); + + /* + rak_mspace_calloc behaves as calloc, but operates within + the given space. + */ + void* rak_mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + + /* + rak_mspace_memalign behaves as memalign, but operates within + the given space. + */ + void* rak_mspace_memalign(mspace msp, size_t alignment, size_t bytes); + + /* + rak_mspace_independent_calloc behaves as independent_calloc, but + operates within the given space. + */ + void** rak_mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); + + /* + rak_mspace_independent_comalloc behaves as independent_comalloc, but + operates within the given space. + */ + void** rak_mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); + + /* + rak_mspace_footprint() returns the number of bytes obtained from the + system for this space. + */ + size_t rak_mspace_footprint(mspace msp); + + /* + mspace_max_footprint() returns the peak number of bytes obtained from the + system for this space. + */ + size_t mspace_max_footprint(mspace msp); + + +#if !NO_MALLINFO + /* + rak_mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. + */ + struct mallinfo rak_mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + + /* + malloc_usable_size(void* p) behaves the same as malloc_usable_size; + */ + size_t rak_mspace_usable_size(void* mem); + + /* + rak_mspace_malloc_stats behaves as malloc_stats, but reports + properties of the given space. + */ + void rak_mspace_malloc_stats(mspace msp); + + /* + rak_mspace_trim behaves as malloc_trim, but + operates within the given space. + */ + int rak_mspace_trim(mspace msp, size_t pad); + + /* + An alias for mallopt. + */ + int rak_mspace_mallopt(int, int); + +#endif /* MSPACES */ + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif /* __cplusplus */ + +/* +======================================================================== +To make a fully customizable malloc.h header file, cut everything +above this line, put into file malloc.h, edit to suit, and #include it +on the next line, as well as in programs that use this malloc. +======================================================================== +*/ + +/* #include "malloc.h" */ + +/*------------------------------ internal #includes ---------------------- */ + +#ifdef DL_PLATFORM_WIN32 +#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ +#endif /* DL_PLATFORM_WIN32 */ + +#include /* for printing in malloc_stats */ + +#ifndef LACKS_ERRNO_H +#include /* for MALLOC_FAILURE_ACTION */ +#endif /* LACKS_ERRNO_H */ + +#if FOOTERS || DEBUG +#include /* for magic initialization */ +#endif /* FOOTERS */ + +#ifndef LACKS_STDLIB_H +#include /* for abort() */ +#endif /* LACKS_STDLIB_H */ + +#ifdef DEBUG +#if ABORT_ON_ASSERT_FAILURE +#undef assert +#define assert(x) if(!(x)) ABORT +#else /* ABORT_ON_ASSERT_FAILURE */ +#include +#endif /* ABORT_ON_ASSERT_FAILURE */ +#else /* DEBUG */ +#ifndef assert +#define assert(x) +#endif +#define DEBUG 0 +#endif /* DEBUG */ + +#ifndef LACKS_STRING_H +#include /* for memset etc */ +#endif /* LACKS_STRING_H */ + +#if USE_BUILTIN_FFS +#ifndef LACKS_STRINGS_H +#include /* for ffs */ +#endif /* LACKS_STRINGS_H */ +#endif /* USE_BUILTIN_FFS */ + +#if HAVE_MMAP +#ifndef LACKS_SYS_MMAN_H +/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ +#if (defined(linux) && !defined(__USE_GNU)) +#define __USE_GNU 1 +#include /* for mmap */ +#undef __USE_GNU +#else +#include /* for mmap */ +#endif /* linux */ +#endif /* LACKS_SYS_MMAN_H */ +#ifndef LACKS_FCNTL_H +#include +#endif /* LACKS_FCNTL_H */ +#endif /* HAVE_MMAP */ + +#ifndef LACKS_UNISTD_H +#include /* for sbrk, sysconf */ +#else /* LACKS_UNISTD_H */ +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +extern void* sbrk(ptrdiff_t); +#endif /* FreeBSD etc */ +#endif /* LACKS_UNISTD_H */ + +/* Declarations for locking */ +#if USE_LOCKS +#if defined(_XBOX) || defined(X360) +#pragma intrinsic (_InterlockedCompareExchange) +#pragma intrinsic (_InterlockedExchange) +#define interlockedcompareexchange _InterlockedCompareExchange +#define interlockedexchange _InterlockedExchange +#elif !defined(DL_PLATFORM_WIN32) +#include +#if defined (__SVR4) && defined (__sun) /* solaris */ +#include +#endif /* solaris */ +#else +#ifndef _M_AMD64 +/* These are already defined on AMD64 builds */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); + LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _M_AMD64 */ +#pragma intrinsic (_InterlockedCompareExchange) +#pragma intrinsic (_InterlockedExchange) +#define interlockedcompareexchange _InterlockedCompareExchange +#define interlockedexchange _InterlockedExchange +#endif /* Win32 */ +#endif /* USE_LOCKS */ + +/* Declarations for bit scanning on win32 */ +#if defined(_MSC_VER) && _MSC_VER>=1300 && defined(DL_PLATFORM_WIN32) +#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + unsigned char _BitScanForward(unsigned long *index, unsigned long mask); + unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define BitScanForward _BitScanForward +#define BitScanReverse _BitScanReverse +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) +#endif /* BitScanForward */ +#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ + +#ifndef DL_PLATFORM_WIN32 +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) +extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef DL_PLATFORM_WIN32 /* use supplied emulation of getpagesize */ +# define malloc_getpagesize getpagesize() +# else +# ifndef LACKS_SYS_PARAM_H +# include +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else /* just guess */ +# define malloc_getpagesize ((size_t)4096U) +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif +#endif + + + +/* ------------------- size_t and alignment properties -------------------- */ + +/* The byte and bit size of a size_t */ +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +/* Some constants coerced to size_t */ +/* Annoying but necessary to avoid errors on some platforms */ +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define SIZE_T_FOUR ((size_t)4) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* True if address a has acceptable alignment */ +#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) + +/* the number of bytes to offset an address to align it */ +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) + +/* -------------------------- MMAP preliminaries ------------------------- */ + +/* +If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and +checks to fail so compiler optimizer can delete code rather than +using so many "#if"s. +*/ + + +/* MORECORE and MMAP must return MFAIL on failure */ +#define MFAIL ((void*)(MAX_SIZE_T)) +#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ + +#if HAVE_MMAP + +#if defined(_XBOX) || defined(X360) + /* Win32 MMAP via VirtualAlloc */ + static void* win32mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; + } + + /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ + static void* win32direct_mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; + } + + /* This function supports releasing coalesed segments */ + static int win32munmap(void* ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = (char*)ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; + } + + #define RAK_MMAP_DEFAULT(s) win32mmap(s) + #define RAK_MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) + #define RAK_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) +#elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2) + +inline int ___freeit_dlmalloc_default__(void* s) {free(s); return 0;} +#define RAK_MMAP_DEFAULT(s) malloc(s); +#define RAK_MUNMAP_DEFAULT(a, s) ___freeit_dlmalloc_default__(a); +#define RAK_DIRECT_MMAP_DEFAULT(s) malloc(s); + +#elif !defined(DL_PLATFORM_WIN32) + #define RAK_MUNMAP_DEFAULT(a, s) munmap((a), (s)) + #define MMAP_PROT (PROT_READ|PROT_WRITE) + #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) + #define MAP_ANONYMOUS MAP_ANON + #endif /* MAP_ANON */ + #ifdef MAP_ANONYMOUS + #define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) + #define RAK_MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) + #else /* MAP_ANONYMOUS */ + /* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. + */ + #define MMAP_FLAGS (MAP_PRIVATE) + static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ + #define RAK_MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ + (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) + #endif /* MAP_ANONYMOUS */ + + #define RAK_DIRECT_MMAP_DEFAULT(s) RAK_MMAP_DEFAULT(s) + +#else /* DL_PLATFORM_WIN32 */ + + /* Win32 MMAP via VirtualAlloc */ + static FORCEINLINE void* win32mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; + } + + /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ + static FORCEINLINE void* win32direct_mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; + } + + /* This function supports releasing coalesed segments */ + static FORCEINLINE int win32munmap(void* ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = (char*)ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; + } + + #define RAK_MMAP_DEFAULT(s) win32mmap(s) + #define RAK_MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) + #define RAK_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) +#endif /* DL_PLATFORM_WIN32 */ +#endif /* HAVE_MMAP */ + +#if HAVE_MREMAP +#ifndef DL_PLATFORM_WIN32 +#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) +#endif /* DL_PLATFORM_WIN32 */ +#endif /* HAVE_MREMAP */ + + +/** +* Define CALL_MORECORE +*/ +#if HAVE_MORECORE +#ifdef MORECORE +#define CALL_MORECORE(S) MORECORE(S) +#else /* MORECORE */ +#define CALL_MORECORE(S) MORECORE_DEFAULT(S) +#endif /* MORECORE */ +#else /* HAVE_MORECORE */ +#define CALL_MORECORE(S) MFAIL +#endif /* HAVE_MORECORE */ + +/** +* Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP +*/ +#if HAVE_MMAP +#define USE_MMAP_BIT (SIZE_T_ONE) + +#ifdef MMAP +#define CALL_MMAP(s) MMAP(s) +#else /* MMAP */ +#define CALL_MMAP(s) RAK_MMAP_DEFAULT(s) +#endif /* MMAP */ +#ifdef MUNMAP +#define CALL_MUNMAP(a, s) MUNMAP((a), (s)) +#else /* MUNMAP */ +#define CALL_MUNMAP(a, s) RAK_MUNMAP_DEFAULT((a), (s)) +#endif /* MUNMAP */ +#ifdef DIRECT_MMAP +#define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) +#else /* DIRECT_MMAP */ +#define CALL_DIRECT_MMAP(s) RAK_DIRECT_MMAP_DEFAULT(s) +#endif /* DIRECT_MMAP */ +#else /* HAVE_MMAP */ +#define USE_MMAP_BIT (SIZE_T_ZERO) + +#define MMAP(s) MFAIL +#define MUNMAP(a, s) (-1) +#define DIRECT_MMAP(s) MFAIL +#define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) +#define CALL_MMAP(s) MMAP(s) +#define CALL_MUNMAP(a, s) MUNMAP((a), (s)) +#endif /* HAVE_MMAP */ + +/** +* Define CALL_MREMAP +*/ +#if HAVE_MMAP && HAVE_MREMAP +#ifdef MREMAP +#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) +#else /* MREMAP */ +#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) +#endif /* MREMAP */ +#else /* HAVE_MMAP && HAVE_MREMAP */ +#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL +#endif /* HAVE_MMAP && HAVE_MREMAP */ + +/* mstate bit set if continguous morecore disabled or failed */ +#define USE_NONCONTIGUOUS_BIT (4U) + +/* segment bit set in rak_create_mspace_with_base */ +#define EXTERN_BIT (8U) + + +#endif /* MALLOC_280_H */ + #endif // _RAKNET_SUPPORT_DL_MALLOC \ No newline at end of file diff --git a/src/server/CreatorLevel.h b/src/server/CreatorLevel.h index e375d18..8d13666 100755 --- a/src/server/CreatorLevel.h +++ b/src/server/CreatorLevel.h @@ -1,15 +1,15 @@ -#ifndef NET_MINECRAFT_WORLD_LEVEL__CreatorLevel_H__ -#define NET_MINECRAFT_WORLD_LEVEL__CreatorLevel_H__ - -#include "../world/level/Level.h" - -class CreatorLevel: public Level -{ - typedef Level super; -public: - CreatorLevel(LevelStorage* levelStorage, const std::string& levelName, const LevelSettings& settings, int generatorVersion, Dimension* fixedDimension = NULL); - - void tick(); -}; - -#endif /* NET_MINECRAFT_WORLD_LEVEL__CreatorLevel_H__ */ +#ifndef NET_MINECRAFT_WORLD_LEVEL__CreatorLevel_H__ +#define NET_MINECRAFT_WORLD_LEVEL__CreatorLevel_H__ + +#include "../world/level/Level.h" + +class CreatorLevel: public Level +{ + typedef Level super; +public: + CreatorLevel(LevelStorage* levelStorage, const std::string& levelName, const LevelSettings& settings, int generatorVersion, Dimension* fixedDimension = NULL); + + void tick(); +}; + +#endif /* NET_MINECRAFT_WORLD_LEVEL__CreatorLevel_H__ */ diff --git a/src/server/ServerLevel.cpp b/src/server/ServerLevel.cpp index e57d6b8..cc76f29 100755 --- a/src/server/ServerLevel.cpp +++ b/src/server/ServerLevel.cpp @@ -1,67 +1,67 @@ -#include "ServerLevel.h" -#include "../network/RakNetInstance.h" -#include "../network/packet/SetTimePacket.h" -#include "../network/packet/SetHealthPacket.h" -#include "../world/level/tile/LevelEvent.h" -ServerLevel::ServerLevel(LevelStorage* levelStorage, const std::string& levelName, const LevelSettings& settings, int generatorVersion, Dimension* fixedDimension /* = NULL */) - : super(levelStorage, levelName, settings, generatorVersion, fixedDimension), - allPlayersAreSleeping(false){ - -} - -void ServerLevel::updateSleepingPlayerList() { - bool allPlayersWasSleeping = allPlayersAreSleeping; - allPlayersAreSleeping = !players.empty(); - for(PlayerList::iterator it = players.begin(); it != players.end(); ++it) { - Player* player = *it; - if(!player->isSleeping()) { - allPlayersAreSleeping = false; - break; - } - } - if(!allPlayersWasSleeping && allPlayersAreSleeping) { - levelEvent(NULL, LevelEvent::ALL_PLAYERS_SLEEPING, 0, 0, 0, 0); - for(PlayerList::iterator it = players.begin(); it != players.end(); ++it) { - (*it)->setAllPlayersSleeping(); - } - } -} - -void ServerLevel::awakenAllPlayers() { - allPlayersAreSleeping = false; - for(PlayerList::iterator it = players.begin(); it != players.end(); ++it) { - Player* player = *it; - if(player->isSleeping()) { - player->stopSleepInBed(false, false, true); - player->health = Player::MAX_HEALTH; - player->lastHealth = Player::MAX_HEALTH; - } - } - SetHealthPacket packet(Player::MAX_HEALTH); - raknetInstance->send(packet); -} - -bool ServerLevel::allPlayersSleeping() { - if(allPlayersAreSleeping && !isClientSide) { - // all players are sleeping, but have they slept long enough? - for(PlayerList::iterator it = players.begin(); it != players.end(); ++it) { - if(!(*it)->isSleepingLongEnough()) { - return false; - } - } - // yep - return true; - } - return false; -} - -void ServerLevel::tick(){ - super::tick(); - if(allPlayersSleeping()) { - long newTime = levelData.getTime() + TICKS_PER_DAY; - levelData.setTime(newTime - (newTime % TICKS_PER_DAY)); - SetTimePacket packet(levelData.getTime()); - raknetInstance->send(packet); - awakenAllPlayers(); - } -} +#include "ServerLevel.h" +#include "../network/RakNetInstance.h" +#include "../network/packet/SetTimePacket.h" +#include "../network/packet/SetHealthPacket.h" +#include "../world/level/tile/LevelEvent.h" +ServerLevel::ServerLevel(LevelStorage* levelStorage, const std::string& levelName, const LevelSettings& settings, int generatorVersion, Dimension* fixedDimension /* = NULL */) + : super(levelStorage, levelName, settings, generatorVersion, fixedDimension), + allPlayersAreSleeping(false){ + +} + +void ServerLevel::updateSleepingPlayerList() { + bool allPlayersWasSleeping = allPlayersAreSleeping; + allPlayersAreSleeping = !players.empty(); + for(PlayerList::iterator it = players.begin(); it != players.end(); ++it) { + Player* player = *it; + if(!player->isSleeping()) { + allPlayersAreSleeping = false; + break; + } + } + if(!allPlayersWasSleeping && allPlayersAreSleeping) { + levelEvent(NULL, LevelEvent::ALL_PLAYERS_SLEEPING, 0, 0, 0, 0); + for(PlayerList::iterator it = players.begin(); it != players.end(); ++it) { + (*it)->setAllPlayersSleeping(); + } + } +} + +void ServerLevel::awakenAllPlayers() { + allPlayersAreSleeping = false; + for(PlayerList::iterator it = players.begin(); it != players.end(); ++it) { + Player* player = *it; + if(player->isSleeping()) { + player->stopSleepInBed(false, false, true); + player->health = Player::MAX_HEALTH; + player->lastHealth = Player::MAX_HEALTH; + } + } + SetHealthPacket packet(Player::MAX_HEALTH); + raknetInstance->send(packet); +} + +bool ServerLevel::allPlayersSleeping() { + if(allPlayersAreSleeping && !isClientSide) { + // all players are sleeping, but have they slept long enough? + for(PlayerList::iterator it = players.begin(); it != players.end(); ++it) { + if(!(*it)->isSleepingLongEnough()) { + return false; + } + } + // yep + return true; + } + return false; +} + +void ServerLevel::tick(){ + super::tick(); + if(allPlayersSleeping()) { + long newTime = levelData.getTime() + TICKS_PER_DAY; + levelData.setTime(newTime - (newTime % TICKS_PER_DAY)); + SetTimePacket packet(levelData.getTime()); + raknetInstance->send(packet); + awakenAllPlayers(); + } +} diff --git a/src/server/ServerLevel.h b/src/server/ServerLevel.h index 688c40d..ce9719d 100755 --- a/src/server/ServerLevel.h +++ b/src/server/ServerLevel.h @@ -1,20 +1,20 @@ -#ifndef NET_MINECRAFT_WORLD_LEVEL__ServerLevel_H__ -#define NET_MINECRAFT_WORLD_LEVEL__ServerLevel_H__ - -#include "../world/level/Level.h" - -class ServerLevel: public Level -{ - typedef Level super; -public: - ServerLevel(LevelStorage* levelStorage, const std::string& levelName, const LevelSettings& settings, int generatorVersion, Dimension* fixedDimension = NULL); - - void updateSleepingPlayerList(); - void awakenAllPlayers(); - bool allPlayersSleeping(); - void tick(); -protected: - bool allPlayersAreSleeping; -}; - -#endif /* NET_MINECRAFT_WORLD_LEVEL__ServerLevel_H__ */ +#ifndef NET_MINECRAFT_WORLD_LEVEL__ServerLevel_H__ +#define NET_MINECRAFT_WORLD_LEVEL__ServerLevel_H__ + +#include "../world/level/Level.h" + +class ServerLevel: public Level +{ + typedef Level super; +public: + ServerLevel(LevelStorage* levelStorage, const std::string& levelName, const LevelSettings& settings, int generatorVersion, Dimension* fixedDimension = NULL); + + void updateSleepingPlayerList(); + void awakenAllPlayers(); + bool allPlayersSleeping(); + void tick(); +protected: + bool allPlayersAreSleeping; +}; + +#endif /* NET_MINECRAFT_WORLD_LEVEL__ServerLevel_H__ */ diff --git a/src/server/ServerPlayer.cpp b/src/server/ServerPlayer.cpp index ad3b4a9..4ecc38a 100755 --- a/src/server/ServerPlayer.cpp +++ b/src/server/ServerPlayer.cpp @@ -1,165 +1,165 @@ -#include "ServerPlayer.h" - -#include "../network/RakNetInstance.h" -#include "../network/packet/ContainerOpenPacket.h" -#include "../network/packet/ContainerClosePacket.h" -#include "../network/packet/ContainerSetDataPacket.h" -#include "../network/packet/ContainerSetSlotPacket.h" -#include "../network/packet/ContainerSetContentPacket.h" -#include "../network/packet/ChatPacket.h" -#include "../network/packet/EntityEventPacket.h" -#include "../network/packet/SetHealthPacket.h" -#include "../network/packet/TakeItemEntityPacket.h" -#include "../client/Minecraft.h" -#include "../world/level/tile/entity/FurnaceTileEntity.h" -#include "../world/inventory/FurnaceMenu.h" -#include "../world/inventory/FillingContainer.h" -#include "../world/inventory/ContainerMenu.h" -#include "../world/entity/EntityEvent.h" -#include "../network/packet/AnimatePacket.h" -#include "../world/level/tile/entity/ChestTileEntity.h" -#include "../network/packet/HurtArmorPacket.h" - -ServerPlayer::ServerPlayer( Minecraft* minecraft, Level* level ) -: super(level, minecraft->isCreativeMode()), - _mc(minecraft), - _prevHealth(-999), - _containerCounter(0) -{ - hasFakeInventory = true; - footSize = 0; -} - -ServerPlayer::~ServerPlayer() { - setContainerMenu(NULL); -} - -void ServerPlayer::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint) { - if(isSleeping()) { - AnimatePacket packet(AnimatePacket::WAKE_UP, this); - _mc->raknetInstance->send(owner, packet); - } - super::stopSleepInBed(forcefulWakeUp, updateLevelList, saveRespawnPoint); -} - - -void ServerPlayer::aiStep() { - updateAttackAnim(); - super::aiStep(); -} - -void ServerPlayer::tick() { - super::tick(); - - if(!useItem.isNull()) - useItemDuration--; - - //LOGI("Server:tick. Cmenu: %p\n", containerMenu); - if (containerMenu) - containerMenu->broadcastChanges(); - - if (health != _prevHealth) { - _prevHealth = health; - SetHealthPacket packet(health); - _mc->raknetInstance->send(owner, packet); - } -} - -void ServerPlayer::take( Entity* e, int orgCount ) { - TakeItemEntityPacket packet(e->entityId, entityId); - _mc->raknetInstance->send(packet); - - super::take(e, orgCount); -} - -void ServerPlayer::hurtArmor(int dmg) { - super::hurtArmor(dmg); - HurtArmorPacket packet(dmg); - _mc->raknetInstance->send(owner, packet); -} - -void ServerPlayer::openContainer( ChestTileEntity* container) { - LOGI("Client is opening a container\n"); - nextContainerCounter(); - ContainerOpenPacket packet(_containerCounter, ContainerType::CONTAINER, container->getName(), container->getContainerSize()); - _mc->raknetInstance->send(owner, packet); - setContainerMenu(new ContainerMenu(container, container->runningId)); -} - -void ServerPlayer::openFurnace( FurnaceTileEntity* furnace ) { - LOGI("Client is opening a furnace\n"); - nextContainerCounter(); - ContainerOpenPacket packet(_containerCounter, ContainerType::FURNACE, furnace->getName(), furnace->getContainerSize()); - _mc->raknetInstance->send(owner, packet); - setContainerMenu(new FurnaceMenu(furnace)); -} - -void ServerPlayer::closeContainer() { - LOGI("Client is closing a container\n"); - ContainerClosePacket packet(containerMenu->containerId); - _mc->raknetInstance->send(owner, packet); - doCloseContainer(); -} - -void ServerPlayer::doCloseContainer() { - if (!containerMenu) { - LOGE("Container is missing @ doCloseContainer!\n"); - } - setContainerMenu(NULL); -} - -bool ServerPlayer::hasResource( int id ) { - return true; -} - -// -// IContainerListener -// -void ServerPlayer::setContainerData( BaseContainerMenu* menu, int id, int value ) { - ContainerSetDataPacket p(menu->containerId, id, value); - _mc->raknetInstance->send(owner, p); - //LOGI("Setting container data for id %d: %d\n", id, value); -} - -void ServerPlayer::slotChanged( BaseContainerMenu* menu, int slot, const ItemInstance& item, bool isResultSlot ) { - if (isResultSlot) return; - ContainerSetSlotPacket p(menu->containerId, slot, item); - _mc->raknetInstance->send(owner, p); - //LOGI("Slot %d changed\n", slot); -} - -void ServerPlayer::refreshContainer( BaseContainerMenu* menu, const std::vector& items ) { - ContainerSetContentPacket p(menu->containerId, menu->getItems()); - _mc->raknetInstance->send(owner, p); - //LOGI("Refreshing container with %d items\n", items.size()); -} - -void ServerPlayer::nextContainerCounter() { - if (++_containerCounter >= 100) - _containerCounter = 0; -} - -void ServerPlayer::setContainerMenu( BaseContainerMenu* menu ) { - if (containerMenu == menu) - return; - - if (containerMenu) - delete containerMenu; - - containerMenu = menu; - if (containerMenu) { - containerMenu->containerId = _containerCounter; - containerMenu->setListener(this); - } -} - -void ServerPlayer::completeUsingItem() { - EntityEventPacket p(entityId, EntityEvent::USE_ITEM_COMPLETE); - _mc->raknetInstance->send(owner, p); - super::completeUsingItem(); -} - -void ServerPlayer::displayClientMessage( const std::string& messageId ) { - ChatPacket package(messageId); - _mc->raknetInstance->send(owner, package); -} +#include "ServerPlayer.h" + +#include "../network/RakNetInstance.h" +#include "../network/packet/ContainerOpenPacket.h" +#include "../network/packet/ContainerClosePacket.h" +#include "../network/packet/ContainerSetDataPacket.h" +#include "../network/packet/ContainerSetSlotPacket.h" +#include "../network/packet/ContainerSetContentPacket.h" +#include "../network/packet/ChatPacket.h" +#include "../network/packet/EntityEventPacket.h" +#include "../network/packet/SetHealthPacket.h" +#include "../network/packet/TakeItemEntityPacket.h" +#include "../client/Minecraft.h" +#include "../world/level/tile/entity/FurnaceTileEntity.h" +#include "../world/inventory/FurnaceMenu.h" +#include "../world/inventory/FillingContainer.h" +#include "../world/inventory/ContainerMenu.h" +#include "../world/entity/EntityEvent.h" +#include "../network/packet/AnimatePacket.h" +#include "../world/level/tile/entity/ChestTileEntity.h" +#include "../network/packet/HurtArmorPacket.h" + +ServerPlayer::ServerPlayer( Minecraft* minecraft, Level* level ) +: super(level, minecraft->isCreativeMode()), + _mc(minecraft), + _prevHealth(-999), + _containerCounter(0) +{ + hasFakeInventory = true; + footSize = 0; +} + +ServerPlayer::~ServerPlayer() { + setContainerMenu(NULL); +} + +void ServerPlayer::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint) { + if(isSleeping()) { + AnimatePacket packet(AnimatePacket::WAKE_UP, this); + _mc->raknetInstance->send(owner, packet); + } + super::stopSleepInBed(forcefulWakeUp, updateLevelList, saveRespawnPoint); +} + + +void ServerPlayer::aiStep() { + updateAttackAnim(); + super::aiStep(); +} + +void ServerPlayer::tick() { + super::tick(); + + if(!useItem.isNull()) + useItemDuration--; + + //LOGI("Server:tick. Cmenu: %p\n", containerMenu); + if (containerMenu) + containerMenu->broadcastChanges(); + + if (health != _prevHealth) { + _prevHealth = health; + SetHealthPacket packet(health); + _mc->raknetInstance->send(owner, packet); + } +} + +void ServerPlayer::take( Entity* e, int orgCount ) { + TakeItemEntityPacket packet(e->entityId, entityId); + _mc->raknetInstance->send(packet); + + super::take(e, orgCount); +} + +void ServerPlayer::hurtArmor(int dmg) { + super::hurtArmor(dmg); + HurtArmorPacket packet(dmg); + _mc->raknetInstance->send(owner, packet); +} + +void ServerPlayer::openContainer( ChestTileEntity* container) { + LOGI("Client is opening a container\n"); + nextContainerCounter(); + ContainerOpenPacket packet(_containerCounter, ContainerType::CONTAINER, container->getName(), container->getContainerSize()); + _mc->raknetInstance->send(owner, packet); + setContainerMenu(new ContainerMenu(container, container->runningId)); +} + +void ServerPlayer::openFurnace( FurnaceTileEntity* furnace ) { + LOGI("Client is opening a furnace\n"); + nextContainerCounter(); + ContainerOpenPacket packet(_containerCounter, ContainerType::FURNACE, furnace->getName(), furnace->getContainerSize()); + _mc->raknetInstance->send(owner, packet); + setContainerMenu(new FurnaceMenu(furnace)); +} + +void ServerPlayer::closeContainer() { + LOGI("Client is closing a container\n"); + ContainerClosePacket packet(containerMenu->containerId); + _mc->raknetInstance->send(owner, packet); + doCloseContainer(); +} + +void ServerPlayer::doCloseContainer() { + if (!containerMenu) { + LOGE("Container is missing @ doCloseContainer!\n"); + } + setContainerMenu(NULL); +} + +bool ServerPlayer::hasResource( int id ) { + return true; +} + +// +// IContainerListener +// +void ServerPlayer::setContainerData( BaseContainerMenu* menu, int id, int value ) { + ContainerSetDataPacket p(menu->containerId, id, value); + _mc->raknetInstance->send(owner, p); + //LOGI("Setting container data for id %d: %d\n", id, value); +} + +void ServerPlayer::slotChanged( BaseContainerMenu* menu, int slot, const ItemInstance& item, bool isResultSlot ) { + if (isResultSlot) return; + ContainerSetSlotPacket p(menu->containerId, slot, item); + _mc->raknetInstance->send(owner, p); + //LOGI("Slot %d changed\n", slot); +} + +void ServerPlayer::refreshContainer( BaseContainerMenu* menu, const std::vector& items ) { + ContainerSetContentPacket p(menu->containerId, menu->getItems()); + _mc->raknetInstance->send(owner, p); + //LOGI("Refreshing container with %d items\n", items.size()); +} + +void ServerPlayer::nextContainerCounter() { + if (++_containerCounter >= 100) + _containerCounter = 0; +} + +void ServerPlayer::setContainerMenu( BaseContainerMenu* menu ) { + if (containerMenu == menu) + return; + + if (containerMenu) + delete containerMenu; + + containerMenu = menu; + if (containerMenu) { + containerMenu->containerId = _containerCounter; + containerMenu->setListener(this); + } +} + +void ServerPlayer::completeUsingItem() { + EntityEventPacket p(entityId, EntityEvent::USE_ITEM_COMPLETE); + _mc->raknetInstance->send(owner, p); + super::completeUsingItem(); +} + +void ServerPlayer::displayClientMessage( const std::string& messageId ) { + ChatPacket package(messageId); + _mc->raknetInstance->send(owner, package); +} diff --git a/src/server/ServerPlayer.h b/src/server/ServerPlayer.h index de20661..4f77372 100755 --- a/src/server/ServerPlayer.h +++ b/src/server/ServerPlayer.h @@ -1,55 +1,55 @@ -#ifndef ServerPlayer_H__ -#define ServerPlayer_H__ - -#include "../world/entity/player/Player.h" -#include "../world/inventory/BaseContainerMenu.h" - -class Minecraft; -class FurnaceTileEntity; -class ItemInstance; -class FillingContainer; -class ChestTileEntity; - -class ServerPlayer: public Player, - public IContainerListener -{ - typedef Player super; -public: - ServerPlayer(Minecraft* minecraft, Level* level); - - ~ServerPlayer(); - - void aiStep(); - void tick(); - void take(Entity* e, int orgCount); - - void hurtArmor(int dmg); - - void displayClientMessage(const std::string& messageId); - - void openContainer(ChestTileEntity* furnace); - void openFurnace(FurnaceTileEntity* furnace); - void closeContainer(); - void doCloseContainer(); - - bool hasResource( int id ); - // - // IContainerListener - // - void setContainerData(BaseContainerMenu* menu, int id, int value); - void slotChanged(BaseContainerMenu* menu, int slot, const ItemInstance& item, bool isResultSlot); - void refreshContainer(BaseContainerMenu* menu, const std::vector& items); - - virtual void stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint); - - void completeUsingItem(); -private: - void nextContainerCounter(); - void setContainerMenu( BaseContainerMenu* menu ); - - Minecraft* _mc; - int _prevHealth; - int _containerCounter; -}; - -#endif /*ServerPlayer_H__*/ +#ifndef ServerPlayer_H__ +#define ServerPlayer_H__ + +#include "../world/entity/player/Player.h" +#include "../world/inventory/BaseContainerMenu.h" + +class Minecraft; +class FurnaceTileEntity; +class ItemInstance; +class FillingContainer; +class ChestTileEntity; + +class ServerPlayer: public Player, + public IContainerListener +{ + typedef Player super; +public: + ServerPlayer(Minecraft* minecraft, Level* level); + + ~ServerPlayer(); + + void aiStep(); + void tick(); + void take(Entity* e, int orgCount); + + void hurtArmor(int dmg); + + void displayClientMessage(const std::string& messageId); + + void openContainer(ChestTileEntity* furnace); + void openFurnace(FurnaceTileEntity* furnace); + void closeContainer(); + void doCloseContainer(); + + bool hasResource( int id ); + // + // IContainerListener + // + void setContainerData(BaseContainerMenu* menu, int id, int value); + void slotChanged(BaseContainerMenu* menu, int slot, const ItemInstance& item, bool isResultSlot); + void refreshContainer(BaseContainerMenu* menu, const std::vector& items); + + virtual void stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint); + + void completeUsingItem(); +private: + void nextContainerCounter(); + void setContainerMenu( BaseContainerMenu* menu ); + + Minecraft* _mc; + int _prevHealth; + int _containerCounter; +}; + +#endif /*ServerPlayer_H__*/ diff --git a/src/util/CollectionUtils.h b/src/util/CollectionUtils.h index db89e6f..793528d 100755 --- a/src/util/CollectionUtils.h +++ b/src/util/CollectionUtils.h @@ -1,17 +1,17 @@ -#ifndef COLLECTIONUTILS_H__ -#define COLLECTIONUTILS_H__ - -struct PairKeyFunctor { - template - typename T::first_type operator()(T& pair) const { - return pair.first; - } -}; -struct PairValueFunctor { - template - typename T::second_type operator()(T& pair) const { - return pair.second; - } -}; - -#endif /*COLLECTIONUTILS_H__*/ +#ifndef COLLECTIONUTILS_H__ +#define COLLECTIONUTILS_H__ + +struct PairKeyFunctor { + template + typename T::first_type operator()(T& pair) const { + return pair.first; + } +}; +struct PairValueFunctor { + template + typename T::second_type operator()(T& pair) const { + return pair.second; + } +}; + +#endif /*COLLECTIONUTILS_H__*/ diff --git a/src/util/DataIO.cpp b/src/util/DataIO.cpp index b535935..55bbe6a 100755 --- a/src/util/DataIO.cpp +++ b/src/util/DataIO.cpp @@ -1,29 +1,29 @@ -#include "DataIO.h" - -// -// BytesDataOutput -// - -void BytesDataOutput::writeString( const std::string& v ) -{ - int length = v.length() & 0x7fff; - writeShort(length); - writeBytes(v.c_str(), length); - //LOGI("Writing: %d bytes as String: %s\n", v.length(), v.c_str()); -} - -// -// BytesDataInput -// -std::string BytesDataInput::readString() { - int len = readShort(); - if (len > MAX_STRING_LENGTH - 1) - len = MAX_STRING_LENGTH - 1; - char* buffer = new char[len + 1]; - readBytes(buffer, len); - buffer[len] = 0; - - std::string out(buffer); - delete[] buffer; - return out; -} +#include "DataIO.h" + +// +// BytesDataOutput +// + +void BytesDataOutput::writeString( const std::string& v ) +{ + int length = v.length() & 0x7fff; + writeShort(length); + writeBytes(v.c_str(), length); + //LOGI("Writing: %d bytes as String: %s\n", v.length(), v.c_str()); +} + +// +// BytesDataInput +// +std::string BytesDataInput::readString() { + int len = readShort(); + if (len > MAX_STRING_LENGTH - 1) + len = MAX_STRING_LENGTH - 1; + char* buffer = new char[len + 1]; + readBytes(buffer, len); + buffer[len] = 0; + + std::string out(buffer); + delete[] buffer; + return out; +} diff --git a/src/util/DataIO.h b/src/util/DataIO.h index 189013c..a18ca8f 100755 --- a/src/util/DataIO.h +++ b/src/util/DataIO.h @@ -1,320 +1,320 @@ -#ifndef DATAIO_H__ -#define DATAIO_H__ - -#include -#include -#include - -#include "../platform/log.h" - -// Interface for writing primitives to a stream -class IDataOutput { -public: - virtual ~IDataOutput() {} - - // Write a "Pascal" string [Len(Short)][Characters, len={strlen()-1,s.length()}] - //virtual void writePStr(const char* v, int len = -1) = 0; - //virtual void writeCStr(const char* v, int len = -1) = 0; - virtual void writeString(const std::string& v) = 0; - virtual void writeFloat(float v) = 0; - virtual void writeDouble(double v) = 0; - - virtual void writeByte(char v) = 0; - virtual void writeShort(short v) = 0; - virtual void writeInt(int v) = 0; - virtual void writeLongLong(long long v) = 0; - - virtual void writeBytes(const void* data, int bytes) = 0; -}; - - -// Interface for reading primitives from a stream -class IDataInput { -public: - virtual ~IDataInput() {} - - virtual std::string readString() = 0; - //virtual void readPStr(char**) = 0; - //virtual void readCStr(char** s, int len = -1) = 0; - virtual float readFloat() = 0; - virtual double readDouble() = 0; - - virtual char readByte() = 0; - virtual short readShort() = 0; - virtual int readInt() = 0; - virtual long long readLongLong() = 0; - - virtual void readBytes(void* data, int bytes) = 0; -}; - -/** - * Redirects all calls to writeBytes - */ -class BytesDataOutput: public IDataOutput { -public: - //virtual void writePStr(const char* v, int len = -1); - //virtual void writeCStr(const char* v, int len = -1); - virtual void writeString(const std::string& v); - virtual void writeFloat(float v) { - writeBytes(&v, 4); - } - virtual void writeDouble(double v) { - writeBytes(&v, 8); - } - virtual void writeByte(char v) { - writeBytes(&v, 1); - } - virtual void writeShort(short v) { - writeBytes(&v, 2); - } - virtual void writeInt(int v) { - writeBytes(&v, 4); - } - virtual void writeLongLong(long long v) { - writeBytes(&v, 8); - } - virtual void writeBytes(const void* data, int bytes) = 0; -}; - -/** - * Redirects all calls to readBytes - */ -class BytesDataInput: public IDataInput { -public: - //virtual void readPStr(char** s); - //virtual void readCStr(char* s, int len = -1); - virtual std::string readString(); - virtual float readFloat() { - float o; - readBytes(&o, 4); - return o; - } - virtual double readDouble() { - double o; - readBytes(&o, 8); - return o; - } - virtual char readByte() { - char o; - readBytes(&o, 1); - return o; - } - virtual short readShort() { - short o; - readBytes(&o, 2); - return o; - } - virtual int readInt() { - int o; - readBytes(&o, 4); - return o; - } - virtual long long readLongLong() { - long long o; - readBytes(&o, 8); - return o; - } - virtual void readBytes(void* data, int bytes) = 0; - -private: - static const int MAX_STRING_LENGTH = SHRT_MAX; - static char _charBuffer[MAX_STRING_LENGTH]; -}; - -//class MemoryDataInput: public BytesDataInput { -//public: -// MemoryDataInput(const char* data, int size) -// : _buffer(data), -// _size(size), -// _index(0) -// {} -// MemoryDataInput(const unsigned char* data, int size) -// : _buffer((const char*)data), -// _size(size), -// _index(0) -// {} -// -// void readBytes(void* data, int bytes) { -// if (bytes <= 0) { -// if (bytes < 0) LOGE("Error: %d bytes NOT read @ MemoryDataInput::readBytes!\n", bytes); -// return; -// } -// int left = _size - _index; -// if (bytes > left) { -// LOGE("ERROR: Not enough bytes left in buffer @ MemoryDataInput::readBytes (%d/%d).\n", bytes, left); -// _index = _size; -// return; -// } -// memcpy((char*)data, &_buffer[_index], bytes); -// _index += bytes; -// } -// -//private: -// int _index; -// int _size; -// const char* _buffer; -//}; -// -// -//class MemoryDataOutput: public BytesDataOutput { -//public: -// MemoryDataOutput(char* data, int size) -// : _buffer(data), -// _size(size), -// _index(0) -// {} -// MemoryDataOutput(unsigned char* data, int size) -// : _buffer((char*)data), -// _size(size), -// _index(0) -// {} -// -// void writeBytes(const void* data, int bytes) { -// if (bytes <= 0) { -// LOGW("Warning: %d bytes read @ MemoryDataOutput::writeBytes!\n", bytes); -// return; -// } -// int left = _size - _index; -// if (bytes > left) { -// LOGE("ERROR: Not enough bytes left in buffer @ MemoryDataOutput::writeBytes (%d/%d).\n", bytes, left); -// _index = _size; -// return; -// } -// memcpy(&_buffer[_index], (const char*)data, bytes); -// _index += bytes; -// } -// -//private: -// int _index; -// int _size; -// char* _buffer; -//}; - - -class PrintStream { -public: - void print(const std::string& s) { - } - void println(const std::string& s) { - print(s); print("\n"); - } -}; - -class FileError { -public: - static const int NOT_OPENED = 1; - static const int NOT_FULLY_HANDLED = 2; -}; -// -//class FileIO { -//public: -// FileIO(const std::string& filePath, const char* mode) -// : _filePath(filePath), -// _opened(false), -// _errCode(0), -// _fp(0), -// _doClose(true) -// { -// if (mode) -// openWithMode(mode); -// } -// FileIO(FILE* fp) -// : _filePath(""), -// _opened(fp != NULL), -// _errCode(0), -// _fp(fp), -// _doClose(false) -// {} -// -// ~FileIO() { -// if (_doClose) -// close(); -// } -// -// bool isValid() { -// return _opened; -// } -// -// int getError() { -// int err = _errCode; -// _errCode = 0; -// return err; -// } -// -// bool close() { -// if (!_fp) -// return false; -// -// fclose(_fp); -// _fp = NULL; -// _opened = false; -// return true; -// } -// -//protected: -// void openWithMode(const char* mode) { -// if (_fp) return; -// _fp = fopen(_filePath.c_str(), mode); -// _opened = (_fp != NULL); -// } -// -// std::string _filePath; -// int _errCode; -// bool _opened; -// bool _doClose; -// -// FILE* _fp; -//}; -// -//class FileDataOutput: public BytesDataOutput, public FileIO -//{ -//public: -// FileDataOutput(const std::string& filePath) -// : FileIO(filePath, "wb"), -// bytesWritten(0) -// { -// } -// FileDataOutput(FILE* fp) -// : FileIO(fp), -// bytesWritten(0) -// {} -// -// virtual void writeBytes(const void* data, int bytes) { -// if (!_fp) { -// _errCode |= FileError::NOT_OPENED; -// return; -// } -// -// int bytesWritten = fwrite(data, 1, bytes, _fp); -// if (bytesWritten != bytes) -// _errCode |= FileError::NOT_FULLY_HANDLED; -// -// this->bytesWritten += bytesWritten; -// } -// -// int bytesWritten; -//}; -// -//class FileDataInput: public BytesDataInput, public FileIO -//{ -//public: -// FileDataInput(const std::string& filePath) -// : FileIO(filePath, "rb") -// { -// } -// FileDataInput(FILE* fp) -// : FileIO(fp) -// {} -// -// virtual void readBytes(void* data, int bytes) { -// if (!_fp) { -// _errCode |= FileError::NOT_OPENED; -// return; -// } -// -// if (fread(data, 1, bytes, _fp) != bytes) -// _errCode |= FileError::NOT_FULLY_HANDLED; -// } -//}; - -#endif /*DATAIO_H__*/ +#ifndef DATAIO_H__ +#define DATAIO_H__ + +#include +#include +#include + +#include "../platform/log.h" + +// Interface for writing primitives to a stream +class IDataOutput { +public: + virtual ~IDataOutput() {} + + // Write a "Pascal" string [Len(Short)][Characters, len={strlen()-1,s.length()}] + //virtual void writePStr(const char* v, int len = -1) = 0; + //virtual void writeCStr(const char* v, int len = -1) = 0; + virtual void writeString(const std::string& v) = 0; + virtual void writeFloat(float v) = 0; + virtual void writeDouble(double v) = 0; + + virtual void writeByte(char v) = 0; + virtual void writeShort(short v) = 0; + virtual void writeInt(int v) = 0; + virtual void writeLongLong(long long v) = 0; + + virtual void writeBytes(const void* data, int bytes) = 0; +}; + + +// Interface for reading primitives from a stream +class IDataInput { +public: + virtual ~IDataInput() {} + + virtual std::string readString() = 0; + //virtual void readPStr(char**) = 0; + //virtual void readCStr(char** s, int len = -1) = 0; + virtual float readFloat() = 0; + virtual double readDouble() = 0; + + virtual char readByte() = 0; + virtual short readShort() = 0; + virtual int readInt() = 0; + virtual long long readLongLong() = 0; + + virtual void readBytes(void* data, int bytes) = 0; +}; + +/** + * Redirects all calls to writeBytes + */ +class BytesDataOutput: public IDataOutput { +public: + //virtual void writePStr(const char* v, int len = -1); + //virtual void writeCStr(const char* v, int len = -1); + virtual void writeString(const std::string& v); + virtual void writeFloat(float v) { + writeBytes(&v, 4); + } + virtual void writeDouble(double v) { + writeBytes(&v, 8); + } + virtual void writeByte(char v) { + writeBytes(&v, 1); + } + virtual void writeShort(short v) { + writeBytes(&v, 2); + } + virtual void writeInt(int v) { + writeBytes(&v, 4); + } + virtual void writeLongLong(long long v) { + writeBytes(&v, 8); + } + virtual void writeBytes(const void* data, int bytes) = 0; +}; + +/** + * Redirects all calls to readBytes + */ +class BytesDataInput: public IDataInput { +public: + //virtual void readPStr(char** s); + //virtual void readCStr(char* s, int len = -1); + virtual std::string readString(); + virtual float readFloat() { + float o; + readBytes(&o, 4); + return o; + } + virtual double readDouble() { + double o; + readBytes(&o, 8); + return o; + } + virtual char readByte() { + char o; + readBytes(&o, 1); + return o; + } + virtual short readShort() { + short o; + readBytes(&o, 2); + return o; + } + virtual int readInt() { + int o; + readBytes(&o, 4); + return o; + } + virtual long long readLongLong() { + long long o; + readBytes(&o, 8); + return o; + } + virtual void readBytes(void* data, int bytes) = 0; + +private: + static const int MAX_STRING_LENGTH = SHRT_MAX; + static char _charBuffer[MAX_STRING_LENGTH]; +}; + +//class MemoryDataInput: public BytesDataInput { +//public: +// MemoryDataInput(const char* data, int size) +// : _buffer(data), +// _size(size), +// _index(0) +// {} +// MemoryDataInput(const unsigned char* data, int size) +// : _buffer((const char*)data), +// _size(size), +// _index(0) +// {} +// +// void readBytes(void* data, int bytes) { +// if (bytes <= 0) { +// if (bytes < 0) LOGE("Error: %d bytes NOT read @ MemoryDataInput::readBytes!\n", bytes); +// return; +// } +// int left = _size - _index; +// if (bytes > left) { +// LOGE("ERROR: Not enough bytes left in buffer @ MemoryDataInput::readBytes (%d/%d).\n", bytes, left); +// _index = _size; +// return; +// } +// memcpy((char*)data, &_buffer[_index], bytes); +// _index += bytes; +// } +// +//private: +// int _index; +// int _size; +// const char* _buffer; +//}; +// +// +//class MemoryDataOutput: public BytesDataOutput { +//public: +// MemoryDataOutput(char* data, int size) +// : _buffer(data), +// _size(size), +// _index(0) +// {} +// MemoryDataOutput(unsigned char* data, int size) +// : _buffer((char*)data), +// _size(size), +// _index(0) +// {} +// +// void writeBytes(const void* data, int bytes) { +// if (bytes <= 0) { +// LOGW("Warning: %d bytes read @ MemoryDataOutput::writeBytes!\n", bytes); +// return; +// } +// int left = _size - _index; +// if (bytes > left) { +// LOGE("ERROR: Not enough bytes left in buffer @ MemoryDataOutput::writeBytes (%d/%d).\n", bytes, left); +// _index = _size; +// return; +// } +// memcpy(&_buffer[_index], (const char*)data, bytes); +// _index += bytes; +// } +// +//private: +// int _index; +// int _size; +// char* _buffer; +//}; + + +class PrintStream { +public: + void print(const std::string& s) { + } + void println(const std::string& s) { + print(s); print("\n"); + } +}; + +class FileError { +public: + static const int NOT_OPENED = 1; + static const int NOT_FULLY_HANDLED = 2; +}; +// +//class FileIO { +//public: +// FileIO(const std::string& filePath, const char* mode) +// : _filePath(filePath), +// _opened(false), +// _errCode(0), +// _fp(0), +// _doClose(true) +// { +// if (mode) +// openWithMode(mode); +// } +// FileIO(FILE* fp) +// : _filePath(""), +// _opened(fp != NULL), +// _errCode(0), +// _fp(fp), +// _doClose(false) +// {} +// +// ~FileIO() { +// if (_doClose) +// close(); +// } +// +// bool isValid() { +// return _opened; +// } +// +// int getError() { +// int err = _errCode; +// _errCode = 0; +// return err; +// } +// +// bool close() { +// if (!_fp) +// return false; +// +// fclose(_fp); +// _fp = NULL; +// _opened = false; +// return true; +// } +// +//protected: +// void openWithMode(const char* mode) { +// if (_fp) return; +// _fp = fopen(_filePath.c_str(), mode); +// _opened = (_fp != NULL); +// } +// +// std::string _filePath; +// int _errCode; +// bool _opened; +// bool _doClose; +// +// FILE* _fp; +//}; +// +//class FileDataOutput: public BytesDataOutput, public FileIO +//{ +//public: +// FileDataOutput(const std::string& filePath) +// : FileIO(filePath, "wb"), +// bytesWritten(0) +// { +// } +// FileDataOutput(FILE* fp) +// : FileIO(fp), +// bytesWritten(0) +// {} +// +// virtual void writeBytes(const void* data, int bytes) { +// if (!_fp) { +// _errCode |= FileError::NOT_OPENED; +// return; +// } +// +// int bytesWritten = fwrite(data, 1, bytes, _fp); +// if (bytesWritten != bytes) +// _errCode |= FileError::NOT_FULLY_HANDLED; +// +// this->bytesWritten += bytesWritten; +// } +// +// int bytesWritten; +//}; +// +//class FileDataInput: public BytesDataInput, public FileIO +//{ +//public: +// FileDataInput(const std::string& filePath) +// : FileIO(filePath, "rb") +// { +// } +// FileDataInput(FILE* fp) +// : FileIO(fp) +// {} +// +// virtual void readBytes(void* data, int bytes) { +// if (!_fp) { +// _errCode |= FileError::NOT_OPENED; +// return; +// } +// +// if (fread(data, 1, bytes, _fp) != bytes) +// _errCode |= FileError::NOT_FULLY_HANDLED; +// } +//}; + +#endif /*DATAIO_H__*/ diff --git a/src/util/IntHashMap.h b/src/util/IntHashMap.h index f76d382..37a8484 100755 --- a/src/util/IntHashMap.h +++ b/src/util/IntHashMap.h @@ -1,226 +1,226 @@ -#ifndef UTIL__IntHashMap_H__ -#define UTIL__IntHashMap_H__ - -template -class IntHashMap -{ - static const int DEFAULT_INITIAL_CAPACITY = 16; - static const int MAXIMUM_CAPACITY = 1 << 30; - //static const float DEFAULT_LOAD_FACTOR = 0.75f; - - Entry** table; - int tableCapacity; - int size; - - int threshold; - const float loadFactor; - - volatile int modCount; - -public: - IntHashMap() - : loadFactor(0.75f) - { - threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); - table = new Entry[DEFAULT_INITIAL_CAPACITY]; - } - - ~IntHashMap() { - delete[] table; - } - - /*public*/ int size() { - return size; - } - - /*public*/ bool isEmpty() { - return size == 0; - } - - /*public*/ V get(int key) { - int hash = hash(key); - for (Entry e = table[indexFor(hash, table.length)]; e != NULL; e = e.next) { - if (e.key == key) return e.value; - } - return NULL; - } - - /*public*/ bool containsKey(int key) { - return getEntry(key) != NULL; - } - - const Entry getEntry(int key) { - int hash = hash(key); - for (Entry e = table[indexFor(hash, table.length)]; e != NULL; e = e.next) { - if (e.key == key) return e; - } - return NULL; - } - - /*public*/ void put(int key, V value) { - int hash = hash(key); - int i = indexFor(hash, table.length); - for (Entry e = table[i]; e != NULL; e = e.next) { - if (e.key == key) { - e.value = value; - } - } - - modCount++; - addEntry(hash, key, value, i); - } - - //@SuppressWarnings("unchecked") - /*private*/ void resize(int newCapacity) { - Entry[] oldTable = table; - int oldCapacity = oldTable.length; - if (oldCapacity == MAXIMUM_CAPACITY) { - threshold = INT_MAX;//Integer.MAX_VALUE; - return; - } - - Entry[] newTable = /*new*/ Entry[newCapacity]; - transfer(newTable); - table = newTable; - threshold = (int) (newCapacity * loadFactor); - } - - /*private*/ void transfer(Entry[] newTable) { - Entry[] src = table; - int newCapacity = newTable.length; - for (int j = 0; j < src.length; j++) { - Entry e = src[j]; - if (e != NULL) { - src[j] = NULL; - do { - Entry next = e.next; - int i = indexFor(e.hash, newCapacity); - e.next = newTable[i]; - newTable[i] = e; - e = next; - } while (e != NULL); - } - } - } - - /*public*/ V remove(int key) { - Entry e = removeEntryForKey(key); - return (e == NULL ? NULL : e.value); - } - - const Entry removeEntryForKey(int key) { - int hash = hash(key); - int i = indexFor(hash, table.length); - Entry prev = table[i]; - Entry e = prev; - - while (e != NULL) { - Entry next = e.next; - if (e.key == key) { - modCount++; - size--; - if (prev == e) table[i] = next; - else prev.next = next; - return e; - } - prev = e; - e = next; - } - - return e; - } - - /*public*/ void clear() { - modCount++; - Entry[] tab = table; - for (int i = 0; i < tab.length; i++) - tab[i] = NULL; - size = 0; - } - - /** - * Returns true if this map maps one or more keys to the specified - * value. - * - * @param value value whose presence in this map is to be tested - * @return true if this map maps one or more keys to the specified - * value - */ - /*public*/ bool containsValue(Object value) { - if (value == NULL) return containsNullValue(); - - Entry[] tab = table; - for (int i = 0; i < tab.length; i++) - for (Entry e = tab[i]; e != NULL; e = e.next) - if (value.equals(e.value)) return true; - return false; - } - - /*private*/ bool containsNullValue() { - Entry[] tab = table; - for (int i = 0; i < tab.length; i++) - for (Entry e = tab[i]; e != NULL; e = e.next) - if (e.value == NULL) return true; - return false; - } - - /*private*/ - template - class Entry { - const int key; - V value; - Entry* next; - const int hash; - - /** - * Creates new entry. - */ - Entry(int h, int k, V v, Entry* n) - : value(v), - next(n), - key(k), - hash(h) - { - } - - const int getKey() { - return key; - } - - const V getValue() { - return value; - } - - //@SuppressWarnings("unchecked") - bool operator==(const Entry& rhs) { - return key == rhs.key && value == rhs.value; - } - - const int hashCode() { - return hash(key); - } - - const std::string toString() { - std::stringstream ss; - ss << getKey() << "=" << getValue(); - return ss.str(); - } - } -private: - static int hash(int h) { - h ^= (h >>> 20) ^ (h >>> 12); - return h ^ (h >>> 7) ^ (h >>> 4); - } - - static int indexFor(int h, int length) { - return h & (length - 1); - } - - void addEntry(int hash, int key, V value, int bucketIndex) { - Entry e = table[bucketIndex]; - table[bucketIndex] = /*new*/ Entry(hash, key, value, e); - if (size++ >= threshold) resize(2 * table.length); - } -}; - -#endif /*UTIL__IntHashMap_H__*/ +#ifndef UTIL__IntHashMap_H__ +#define UTIL__IntHashMap_H__ + +template +class IntHashMap +{ + static const int DEFAULT_INITIAL_CAPACITY = 16; + static const int MAXIMUM_CAPACITY = 1 << 30; + //static const float DEFAULT_LOAD_FACTOR = 0.75f; + + Entry** table; + int tableCapacity; + int size; + + int threshold; + const float loadFactor; + + volatile int modCount; + +public: + IntHashMap() + : loadFactor(0.75f) + { + threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); + table = new Entry[DEFAULT_INITIAL_CAPACITY]; + } + + ~IntHashMap() { + delete[] table; + } + + /*public*/ int size() { + return size; + } + + /*public*/ bool isEmpty() { + return size == 0; + } + + /*public*/ V get(int key) { + int hash = hash(key); + for (Entry e = table[indexFor(hash, table.length)]; e != NULL; e = e.next) { + if (e.key == key) return e.value; + } + return NULL; + } + + /*public*/ bool containsKey(int key) { + return getEntry(key) != NULL; + } + + const Entry getEntry(int key) { + int hash = hash(key); + for (Entry e = table[indexFor(hash, table.length)]; e != NULL; e = e.next) { + if (e.key == key) return e; + } + return NULL; + } + + /*public*/ void put(int key, V value) { + int hash = hash(key); + int i = indexFor(hash, table.length); + for (Entry e = table[i]; e != NULL; e = e.next) { + if (e.key == key) { + e.value = value; + } + } + + modCount++; + addEntry(hash, key, value, i); + } + + //@SuppressWarnings("unchecked") + /*private*/ void resize(int newCapacity) { + Entry[] oldTable = table; + int oldCapacity = oldTable.length; + if (oldCapacity == MAXIMUM_CAPACITY) { + threshold = INT_MAX;//Integer.MAX_VALUE; + return; + } + + Entry[] newTable = /*new*/ Entry[newCapacity]; + transfer(newTable); + table = newTable; + threshold = (int) (newCapacity * loadFactor); + } + + /*private*/ void transfer(Entry[] newTable) { + Entry[] src = table; + int newCapacity = newTable.length; + for (int j = 0; j < src.length; j++) { + Entry e = src[j]; + if (e != NULL) { + src[j] = NULL; + do { + Entry next = e.next; + int i = indexFor(e.hash, newCapacity); + e.next = newTable[i]; + newTable[i] = e; + e = next; + } while (e != NULL); + } + } + } + + /*public*/ V remove(int key) { + Entry e = removeEntryForKey(key); + return (e == NULL ? NULL : e.value); + } + + const Entry removeEntryForKey(int key) { + int hash = hash(key); + int i = indexFor(hash, table.length); + Entry prev = table[i]; + Entry e = prev; + + while (e != NULL) { + Entry next = e.next; + if (e.key == key) { + modCount++; + size--; + if (prev == e) table[i] = next; + else prev.next = next; + return e; + } + prev = e; + e = next; + } + + return e; + } + + /*public*/ void clear() { + modCount++; + Entry[] tab = table; + for (int i = 0; i < tab.length; i++) + tab[i] = NULL; + size = 0; + } + + /** + * Returns true if this map maps one or more keys to the specified + * value. + * + * @param value value whose presence in this map is to be tested + * @return true if this map maps one or more keys to the specified + * value + */ + /*public*/ bool containsValue(Object value) { + if (value == NULL) return containsNullValue(); + + Entry[] tab = table; + for (int i = 0; i < tab.length; i++) + for (Entry e = tab[i]; e != NULL; e = e.next) + if (value.equals(e.value)) return true; + return false; + } + + /*private*/ bool containsNullValue() { + Entry[] tab = table; + for (int i = 0; i < tab.length; i++) + for (Entry e = tab[i]; e != NULL; e = e.next) + if (e.value == NULL) return true; + return false; + } + + /*private*/ + template + class Entry { + const int key; + V value; + Entry* next; + const int hash; + + /** + * Creates new entry. + */ + Entry(int h, int k, V v, Entry* n) + : value(v), + next(n), + key(k), + hash(h) + { + } + + const int getKey() { + return key; + } + + const V getValue() { + return value; + } + + //@SuppressWarnings("unchecked") + bool operator==(const Entry& rhs) { + return key == rhs.key && value == rhs.value; + } + + const int hashCode() { + return hash(key); + } + + const std::string toString() { + std::stringstream ss; + ss << getKey() << "=" << getValue(); + return ss.str(); + } + } +private: + static int hash(int h) { + h ^= (h >>> 20) ^ (h >>> 12); + return h ^ (h >>> 7) ^ (h >>> 4); + } + + static int indexFor(int h, int length) { + return h & (length - 1); + } + + void addEntry(int hash, int key, V value, int bucketIndex) { + Entry e = table[bucketIndex]; + table[bucketIndex] = /*new*/ Entry(hash, key, value, e); + if (size++ >= threshold) resize(2 * table.length); + } +}; + +#endif /*UTIL__IntHashMap_H__*/ diff --git a/src/util/MemUtils.h b/src/util/MemUtils.h index 8226e34..240d357 100755 --- a/src/util/MemUtils.h +++ b/src/util/MemUtils.h @@ -1,35 +1,35 @@ -#ifndef MEMUTILS_H__ -#define MEMUTILS_H__ - -template -class Ref { -public: - //~Ref() { //@todo: add this if the pointer is created externally - // dec(); - //} - void inc() { ++_count; } - void dec() { if (--_count == 0 && _obj) delete _obj; } - - __inline short refCount() { return _count; } - __inline bool isUnique() { return _count == 1; } - - __inline T* obj() { return _obj; } - - T& operator->() { return *_obj; } - void operator++() { inc(); } - void operator--() { dec(); } - - static Ref* create(T* object) { return new Ref(object); } -private: - Ref(T* object) - : _obj(object), - _count(1) {} - - Ref(const Ref& rhs); - Ref& operator=(const Ref& rhs); - - T* _obj; - short _count; -}; - -#endif /*MEMUTILS_H__*/ +#ifndef MEMUTILS_H__ +#define MEMUTILS_H__ + +template +class Ref { +public: + //~Ref() { //@todo: add this if the pointer is created externally + // dec(); + //} + void inc() { ++_count; } + void dec() { if (--_count == 0 && _obj) delete _obj; } + + __inline short refCount() { return _count; } + __inline bool isUnique() { return _count == 1; } + + __inline T* obj() { return _obj; } + + T& operator->() { return *_obj; } + void operator++() { inc(); } + void operator--() { dec(); } + + static Ref* create(T* object) { return new Ref(object); } +private: + Ref(T* object) + : _obj(object), + _count(1) {} + + Ref(const Ref& rhs); + Ref& operator=(const Ref& rhs); + + T* _obj; + short _count; +}; + +#endif /*MEMUTILS_H__*/ diff --git a/src/util/Mth.cpp b/src/util/Mth.cpp index 566f3cf..163af4a 100755 --- a/src/util/Mth.cpp +++ b/src/util/Mth.cpp @@ -1,129 +1,129 @@ -#include "Mth.h" -#include "Random.h" -#include -#include - -#include "../Performance.h" - -static Random _rand; - -namespace Mth -{ - static float _sin[65536]; - static const float _sinScale = 65536.0f / (2.0f * PI); - - void initMth() { - for (int i = 0; i < 65536; ++i) - _sin[i] = ::sin(i / _sinScale); - } - - float sqrt(float x) { - //Stopwatch& w = Performance::watches.get("sqrt"); - //w.start(); - float ret = ::sqrt(x); - //w.stop(); - return ret; - } - - static __inline float fastInvSqrt(float x) { - float xhalf = 0.5f*x; - int i = *(int*)&x; - i = 0x5f3759df - (i>>1); - x = *(float*)&i; - x = x*(1.5f - xhalf*x*x); - return x; - } - float invSqrt(float x) { - //Stopwatch& w = Performance::watches.get("invSqrt"); - //w.start(); - float ret = fastInvSqrt(x);//1.0f / sqrt(x); - //w.stop(); - return ret; - } - - int floor(float v) { - int i = (int) v; - return v < i ? i - 1 : i; - } - - float cos(float x) { - //Performance::watches.get("cos").start(); - //float ret = ::cos(x); - float ret = _sin[(int) (x * _sinScale + 65536 / 4) & 65535]; - //Performance::watches.get("cos").stop(); - return ret; - } - - float sin(float x) { - //Performance::watches.get("sin").start(); - //float ret = ::sin(x); - float ret = _sin[(int) (x * _sinScale) & 65535]; - //Performance::watches.get("sin").stop(); - return ret; - } - - float atan(float x) { - //Performance::watches.get("atan").start(); - float ret = ::atan(x); - //Performance::watches.get("atan").stop(); - return ret; - } - - float atan2(float dy, float dx) { - //Performance::watches.get("atan2").start(); - float ret = ::atan2(dy, dx); - //Performance::watches.get("atan2").stop(); - return ret; - } - - float random(){ - return _rand.nextFloat(); - } - int random(int n){ - return _rand.nextInt(n); - } - - int intFloorDiv(int a, int b) { - if (a < 0) return -((-a - 1) / b) - 1; - return a / b; - } - - float abs(float a) { return a>=0? a : -a; } - float Min(float a, float b) { return a<=b? a : b; } - float Max(float a, float b) { return a>=b? a : b; } - - int abs(int a) { return a>=0? a : -a; } - int Min(int a, int b) { return a<=b? a : b; } - int Max(int a, int b) { return a>=b? a : b; } - - float absDecrease(float value, float with, float min) { - if (value > 0) return Max(min, value - with); - return Min(value + with, -min); - } - //float absIncrease(float value, float with, float max); - - int clamp(int v, int low, int high) { - if (v > high) return high; - return v>low? v : low; - } - float clamp(float v, float low, float high) { - if (v > high) return high; - return v>low? v : low; - } - float lerp(float src, float dst, float alpha) { - return src + (dst - src) * alpha; - } - int lerp(int src, int dst, float alpha) { - return src + (int)((dst - src) * alpha); - } - - float absMax(float a, float b) { - if (a < 0) a = -a; - if (b < 0) b = -b; - return a > b ? a : b; - } - - float absMaxSigned(float a, float b) { - return abs(a) > abs(b)? a : b; - } -}; +#include "Mth.h" +#include "Random.h" +#include +#include + +#include "../Performance.h" + +static Random _rand; + +namespace Mth +{ + static float _sin[65536]; + static const float _sinScale = 65536.0f / (2.0f * PI); + + void initMth() { + for (int i = 0; i < 65536; ++i) + _sin[i] = ::sin(i / _sinScale); + } + + float sqrt(float x) { + //Stopwatch& w = Performance::watches.get("sqrt"); + //w.start(); + float ret = ::sqrt(x); + //w.stop(); + return ret; + } + + static __inline float fastInvSqrt(float x) { + float xhalf = 0.5f*x; + int i = *(int*)&x; + i = 0x5f3759df - (i>>1); + x = *(float*)&i; + x = x*(1.5f - xhalf*x*x); + return x; + } + float invSqrt(float x) { + //Stopwatch& w = Performance::watches.get("invSqrt"); + //w.start(); + float ret = fastInvSqrt(x);//1.0f / sqrt(x); + //w.stop(); + return ret; + } + + int floor(float v) { + int i = (int) v; + return v < i ? i - 1 : i; + } + + float cos(float x) { + //Performance::watches.get("cos").start(); + //float ret = ::cos(x); + float ret = _sin[(int) (x * _sinScale + 65536 / 4) & 65535]; + //Performance::watches.get("cos").stop(); + return ret; + } + + float sin(float x) { + //Performance::watches.get("sin").start(); + //float ret = ::sin(x); + float ret = _sin[(int) (x * _sinScale) & 65535]; + //Performance::watches.get("sin").stop(); + return ret; + } + + float atan(float x) { + //Performance::watches.get("atan").start(); + float ret = ::atan(x); + //Performance::watches.get("atan").stop(); + return ret; + } + + float atan2(float dy, float dx) { + //Performance::watches.get("atan2").start(); + float ret = ::atan2(dy, dx); + //Performance::watches.get("atan2").stop(); + return ret; + } + + float random(){ + return _rand.nextFloat(); + } + int random(int n){ + return _rand.nextInt(n); + } + + int intFloorDiv(int a, int b) { + if (a < 0) return -((-a - 1) / b) - 1; + return a / b; + } + + float abs(float a) { return a>=0? a : -a; } + float Min(float a, float b) { return a<=b? a : b; } + float Max(float a, float b) { return a>=b? a : b; } + + int abs(int a) { return a>=0? a : -a; } + int Min(int a, int b) { return a<=b? a : b; } + int Max(int a, int b) { return a>=b? a : b; } + + float absDecrease(float value, float with, float min) { + if (value > 0) return Max(min, value - with); + return Min(value + with, -min); + } + //float absIncrease(float value, float with, float max); + + int clamp(int v, int low, int high) { + if (v > high) return high; + return v>low? v : low; + } + float clamp(float v, float low, float high) { + if (v > high) return high; + return v>low? v : low; + } + float lerp(float src, float dst, float alpha) { + return src + (dst - src) * alpha; + } + int lerp(int src, int dst, float alpha) { + return src + (int)((dst - src) * alpha); + } + + float absMax(float a, float b) { + if (a < 0) a = -a; + if (b < 0) b = -b; + return a > b ? a : b; + } + + float absMaxSigned(float a, float b) { + return abs(a) > abs(b)? a : b; + } +}; diff --git a/src/util/Mth.h b/src/util/Mth.h index 55ce7d6..273279d 100755 --- a/src/util/Mth.h +++ b/src/util/Mth.h @@ -1,97 +1,97 @@ -#ifndef MTH_H__ -#define MTH_H__ - -#include -#include -#include - -namespace Mth { - constexpr float PI = 3.1415926535897932384626433832795028841971f; // exactly! - constexpr float TWO_PI = 2.0f * PI; // exactly! - constexpr float DEGRAD = PI / 180.0f; - const float RADDEG = 180.0f / PI; - - void initMth(); - - float sqrt(float x); - float invSqrt(float x); - - int floor(float x); - - float sin(float x); - float cos(float x); - - float atan(float x); - float atan2(float dy, float dx); - - float random(); - int random(int n); - - float abs(float a); - float Min(float a, float b); - float Max(float a, float b); - int abs(int a); - int Min(int a, int b); - int Max(int a, int b); - - int clamp(int v, int low, int high); - float clamp(float v, float low, float high); - float lerp(float src, float dst, float alpha); - int lerp(int src, int dst, float alpha); - - ///@param value The original signed value - ///@param with The (possibly signed) value to "abs-decrease" with - ///@param min The minimum value - float absDecrease(float value, float with, float min); - //float absIncrease(float value, float with, float max); - - float absMax(float a, float b); - float absMaxSigned(float a, float b); - - int intFloorDiv(int a, int b); -}; - -namespace Util -{ - template - int removeAll(std::vector& superset, const std::vector& toRemove) { - int subSize = (int)toRemove.size(); - int removed = 0; - - for (int i = 0; i < subSize; ++i) { - T elem = toRemove[i]; - int size = (int)superset.size(); - for (int j = 0; j < size; ++j) { - if (elem == superset[j]) { - superset.erase( superset.begin() + j, superset.begin() + j + 1); - ++removed; - break; - } - } - } - return removed; - } - - template - bool remove(std::vector& list, const T& instance) { - typename std::vector::iterator it = std::find(list.begin(), list.end(), instance); - if (it == list.end()) - return false; - - list.erase(it); - return true; - } - - // Could perhaps do a template